From 16d7ce60c612c7f2f09c4481d21ea5dbead9d562 Mon Sep 17 00:00:00 2001 From: benk10 Date: Mon, 24 Jun 2024 13:14:44 -0500 Subject: [PATCH 1/2] Add timelock manager role --- config.js | 14 +++ contracts/HATTimelockController.sol | 17 ++- deploy/001_deploy_hattimelockcontroller.js | 12 +- deploy/013_verify_deployment.js | 31 ++++- docs/deployment.md | 1 + docs/develop.md | 14 --- docs/dodoc/HATTimelockController.md | 17 +++ docs/roles.md | 5 + scripts/deployments/addresses.json | 46 -------- scripts/deployments/config.json | 108 ----------------- scripts/deployments/hats-deploy.js | 111 ------------------ scripts/deployments/hats-verify.js | 100 ---------------- .../hattimelockcontroller-deploy.js | 72 ------------ scripts/deployments/hattoken-deploy.js | 41 ------- scripts/deployments/hatvaultsnft-deploy.js | 75 ------------ .../deployments/tokenlockfactory-deploy.js | 50 -------- test/hattimelockcontroller.js | 26 ++-- 17 files changed, 107 insertions(+), 633 deletions(-) delete mode 100644 docs/develop.md delete mode 100644 scripts/deployments/addresses.json delete mode 100644 scripts/deployments/hats-deploy.js delete mode 100644 scripts/deployments/hats-verify.js delete mode 100644 scripts/deployments/hattimelockcontroller-deploy.js delete mode 100644 scripts/deployments/hattoken-deploy.js delete mode 100644 scripts/deployments/hatvaultsnft-deploy.js delete mode 100644 scripts/deployments/tokenlockfactory-deploy.js diff --git a/config.js b/config.js index f6e67efd..0d7aceb5 100644 --- a/config.js +++ b/config.js @@ -11,6 +11,7 @@ module.exports = { "0xF6aEF099e4473E08bed75E0BB1252C4cdAd96416", "0x42eefBC05794e71a0f7e7B63E5EcB52320345eBE" ], + "managers": [], "rewardControllersConf": [], "hatToken": "0x07865c6E87B9F70255377e024ace6630C1Eaa37F", // USDC "hatVaultsRegistryConf": { @@ -36,6 +37,7 @@ module.exports = { "0xF6aEF099e4473E08bed75E0BB1252C4cdAd96416", "0x42eefBC05794e71a0f7e7B63E5EcB52320345eBE" ], + "managers": [], "rewardControllersConf": [], "hatToken": "0x07865c6E87B9F70255377e024ace6630C1Eaa37F", // USDC "hatVaultsRegistryConf": { @@ -55,6 +57,7 @@ module.exports = { "executors": [ "0x0B7602011EC2B862Bc157fF08d27b1018aEb18d5" ], + "managers": [], "rewardControllersConf": [{ "startBlock": null, "epochLength": "195200", @@ -163,6 +166,7 @@ module.exports = { "0x56E889664F5961452E5f4183AA13AF568198eaD2", "0x1885B7c7a3AE1F35BA71C0392C13153A95c4914f" ], // proposal executors - if this empty, governance will be an executor + "managers": [], "hatVaultsRegistryConf": { "bountyGovernanceHAT": "1000", "bountyHackerHATVested": "0", @@ -217,6 +221,7 @@ module.exports = { "0xF6aEF099e4473E08bed75E0BB1252C4cdAd96416", "0x42eefBC05794e71a0f7e7B63E5EcB52320345eBE" ], + "managers": [], "rewardControllersConf": [], "hatToken": "0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174", // USDC "hatVaultsRegistryConf": { @@ -245,6 +250,7 @@ module.exports = { "0xF6aEF099e4473E08bed75E0BB1252C4cdAd96416", "0x42eefBC05794e71a0f7e7B63E5EcB52320345eBE" ], + "managers": [], "rewardControllersConf": [], "hatToken": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", // USDC "hatVaultsRegistryConf": { @@ -272,6 +278,7 @@ module.exports = { "0xF6aEF099e4473E08bed75E0BB1252C4cdAd96416", "0x42eefBC05794e71a0f7e7B63E5EcB52320345eBE" ], + "managers": [], "rewardControllersConf": [], "hatToken": "0x7F5c764cBc14f9669B88837ca1490cCa17c31607", // USDC "hatVaultsRegistryConf": { @@ -299,6 +306,7 @@ module.exports = { "0xF6aEF099e4473E08bed75E0BB1252C4cdAd96416", "0x42eefBC05794e71a0f7e7B63E5EcB52320345eBE" ], + "managers": [], "rewardControllersConf": [], "hatToken": "0xFF970A61A04b1cA14834A43f5dE4533eBDDB5CC8", // USDC "hatVaultsRegistryConf": { @@ -326,6 +334,7 @@ module.exports = { "0xF6aEF099e4473E08bed75E0BB1252C4cdAd96416", "0x42eefBC05794e71a0f7e7B63E5EcB52320345eBE" ], + "managers": [], "rewardControllersConf": [], "hatToken": "0x8ac76a51cc950d9822d68b83fe1ad97b32cd580d", // USDC "hatVaultsRegistryConf": { @@ -353,6 +362,7 @@ module.exports = { "0xF6aEF099e4473E08bed75E0BB1252C4cdAd96416", "0x42eefBC05794e71a0f7e7B63E5EcB52320345eBE" ], + "managers": [], "rewardControllersConf": [], "hatToken": "0x4200000000000000000000000000000000000006", // WETH "hatVaultsRegistryConf": { @@ -378,6 +388,7 @@ module.exports = { "0xF6aEF099e4473E08bed75E0BB1252C4cdAd96416", "0x42eefBC05794e71a0f7e7B63E5EcB52320345eBE" ], + "managers": [], "rewardControllersConf": [], "hatToken": "NEED ADDRESS", // USDC "hatVaultsRegistryConf": { @@ -403,6 +414,7 @@ module.exports = { "0xF6aEF099e4473E08bed75E0BB1252C4cdAd96416", "0x42eefBC05794e71a0f7e7B63E5EcB52320345eBE" ], + "managers": [], "rewardControllersConf": [], "hatToken": "0xd86e243fc0007e6226b07c9a50c9d70d78299eb5", // USDC "hatVaultsRegistryConf": { @@ -428,6 +440,7 @@ module.exports = { "0xF6aEF099e4473E08bed75E0BB1252C4cdAd96416", "0x42eefBC05794e71a0f7e7B63E5EcB52320345eBE" ], + "managers": [], "rewardControllersConf": [], "hatToken": "0xDDAfbb505ad214D7b80b1f830fcCc89B60fb7A83", // USDC "hatVaultsRegistryConf": { @@ -453,6 +466,7 @@ module.exports = { "0xF6aEF099e4473E08bed75E0BB1252C4cdAd96416", "0x42eefBC05794e71a0f7e7B63E5EcB52320345eBE" ], + "managers": [], "rewardControllersConf": [], "hatToken": "0x0000000000000000000000000000000000000000", // USDC "hatVaultsRegistryConf": { diff --git a/contracts/HATTimelockController.sol b/contracts/HATTimelockController.sol index 0b9ffa5f..363f19eb 100644 --- a/contracts/HATTimelockController.sol +++ b/contracts/HATTimelockController.sol @@ -7,13 +7,22 @@ import "@openzeppelin/contracts/governance/TimelockController.sol"; import "./HATGovernanceArbitrator.sol"; contract HATTimelockController is TimelockController { + bytes32 public constant MANAGER_ROLE = keccak256("MANAGER_ROLE"); constructor( uint256 _minDelay, address[] memory _proposers, - address[] memory _executors + address[] memory _executors, + address[] memory _managers // solhint-disable-next-line no-empty-blocks - ) TimelockController(_minDelay, _proposers, _executors, address(0)) {} + ) TimelockController(_minDelay, _proposers, _executors, address(0)) { + _setRoleAdmin(MANAGER_ROLE, TIMELOCK_ADMIN_ROLE); + + // register managers + for (uint256 i = 0; i < _managers.length; ++i) { + _setupRole(MANAGER_ROLE, _managers[i]); + } + } // The following functions are not subject to the timelock @@ -29,7 +38,7 @@ contract HATTimelockController is TimelockController { _claimsManager.setCommittee(_committee); } - function setVaultDescription(IHATVault _vault, string memory _descriptionHash) external onlyRole(PROPOSER_ROLE) { + function setVaultDescription(IHATVault _vault, string memory _descriptionHash) external onlyRole(MANAGER_ROLE) { _vault.setVaultDescription(_descriptionHash); } @@ -37,7 +46,7 @@ contract HATTimelockController is TimelockController { _vault.setDepositPause(_depositPause); } - function setVaultVisibility(IHATVault _vault, bool _visible) external onlyRole(PROPOSER_ROLE) { + function setVaultVisibility(IHATVault _vault, bool _visible) external onlyRole(MANAGER_ROLE) { _vault.registry().setVaultVisibility(address(_vault), _visible); } diff --git a/deploy/001_deploy_hattimelockcontroller.js b/deploy/001_deploy_hattimelockcontroller.js index 3a322338..73912a98 100644 --- a/deploy/001_deploy_hattimelockcontroller.js +++ b/deploy/001_deploy_hattimelockcontroller.js @@ -25,12 +25,22 @@ const func = async function (hre) { executors.push(governance); } + let managers = config.managers; + if (!managers && network.name === "hardhat") { + managers = [governance]; + } + + if (managers.indexOf(governance) === -1) { + managers.push(governance); + } + await deploy('HATTimelockController', { from: deployer, args: [ hatGovernanceDelay, // minDelay [governance], // proposers - executors // executors + executors, // executors + managers // managers ], log: true, }); diff --git a/deploy/013_verify_deployment.js b/deploy/013_verify_deployment.js index a8f0c351..e81dbb73 100644 --- a/deploy/013_verify_deployment.js +++ b/deploy/013_verify_deployment.js @@ -27,23 +27,35 @@ const func = async function (hre) { executors.push(governance); } + let managers = config["managers"]; + if (!managers || managers.length === 0) { + managers = [governance]; + } + + if (managers.indexOf(governance) === -1) { + managers.push(governance); + } + let hatGovernanceDelay = config["timelockDelay"]; const TIMELOCK_ADMIN_ROLE = await read('HATTimelockController', {}, 'TIMELOCK_ADMIN_ROLE'); const PROPOSER_ROLE = await read('HATTimelockController', {}, 'PROPOSER_ROLE'); const CANCELLER_ROLE = await read('HATTimelockController', {}, 'CANCELLER_ROLE'); const EXECUTOR_ROLE = await read('HATTimelockController', {}, 'EXECUTOR_ROLE'); + const MANAGER_ROLE = await read('HATTimelockController', {}, 'MANAGER_ROLE'); // print some general info before diagnosing console.log("************************************************"); console.log("deployer: ", deployer); console.log("governance: ", governance); console.log("executors: ", executors); + console.log("managers: ", managers); console.log("************************************************"); console.log("TIMELOCK_ADMIN_ROLE", TIMELOCK_ADMIN_ROLE); console.log("PROPOSER_ROLE", PROPOSER_ROLE); console.log("CANCELLER_ROLE", CANCELLER_ROLE); console.log("EXECUTOR_ROLE", EXECUTOR_ROLE); + console.log("MANAGER_ROLE", MANAGER_ROLE); console.log("************************************************"); console.log("HATTimelockController", (await deployments.get('HATTimelockController')).address); console.log("************************************************"); @@ -81,6 +93,14 @@ const func = async function (hre) { ); } + for (manager of manager) { + // Each executor has the execute role + verify( + await read('HATTimelockController', {}, 'hasRole', MANAGER_ROLE, manager), + "Manager " + manager + " has the manager role" + ); + } + // Min delay is correct verify( (await read('HATTimelockController', {}, 'getMinDelay')).toString() === hatGovernanceDelay.toString(), @@ -124,8 +144,8 @@ const func = async function (hre) { `TIMELOCK_ADMIN_ROLE should NOT be the admin role of the deployer ${deployer}` ); // Roles granted should be the 4 + number of executors - // (renounced deployer role, timelock admin of itself, governance proposer and canceller roles, and executor role to the executors) - const roleGrantEventsCount = 3 + executors.length; + // (renounced deployer role, timelock admin of itself, governance proposer and canceller roles, executor role to the executors, and manager role to the managers) + const roleGrantEventsCount = 3 + executors.length + managers.length; verify( logs.length === roleGrantEventsCount, `No unexpected roles were granted (expected ${roleGrantEventsCount}, got ${logs.length})` @@ -142,6 +162,13 @@ const func = async function (hre) { for (executor of executors) { EXPECTED_ROLES[executor] = [EXECUTOR_ROLE]; } + for (manager of managers) { + if (EXPECTED_ROLES[manager]) { + EXPECTED_ROLES[manager].push(MANAGER_ROLE); + } else { + EXPECTED_ROLES[manager] = [MANAGER_ROLE]; + } + } for (log of logs) { const role = log.args.role; const account = log.args.account; diff --git a/docs/deployment.md b/docs/deployment.md index cac50c21..909331ac 100644 --- a/docs/deployment.md +++ b/docs/deployment.md @@ -31,6 +31,7 @@ In the file [config.js](../config.js), we add a section for the sepolia network: "governance": "0xFc9F1d127f8047B0F41e9eAC2Adc2e5279C568B7", "timelockDelay": 300, "executors": [], // proposal executors - if this empty, governance will be an executor + "managers": [], // system managers - if this empty, governance will be a manager "rewardControllersConf": [], // no reward controllers "hatToken": "", // deploy a fresh HATToken contract "hatVaultsRegistryConf": { diff --git a/docs/develop.md b/docs/develop.md deleted file mode 100644 index d2f39fd9..00000000 --- a/docs/develop.md +++ /dev/null @@ -1,14 +0,0 @@ -# For developers - -## Deployment - -Deployment to a particular network of the contract stack is done with the following command: -`npx hardhat run hats-deploy --network {NETWORKNAME}` - -## Verify a deployment on Etherscan - -The deployment process will write the addresses of the deployed contracts to deployment/addresses.json. - -The following command will verify the deployed contracts on etherscan: - -`npx hardhat run hats-verify --network {NETWORKNAME}` \ No newline at end of file diff --git a/docs/dodoc/HATTimelockController.md b/docs/dodoc/HATTimelockController.md index 475c101c..c1d14d0b 100644 --- a/docs/dodoc/HATTimelockController.md +++ b/docs/dodoc/HATTimelockController.md @@ -55,6 +55,23 @@ function EXECUTOR_ROLE() external view returns (bytes32) +#### Returns + +| Name | Type | Description | +|---|---|---| +| _0 | bytes32 | undefined | + +### MANAGER_ROLE + +```solidity +function MANAGER_ROLE() external view returns (bytes32) +``` + + + + + + #### Returns | Name | Type | Description | diff --git a/docs/roles.md b/docs/roles.md index 5dc18063..afd82464 100644 --- a/docs/roles.md +++ b/docs/roles.md @@ -81,6 +81,11 @@ See [parameters](./parameters.md) for the list of parameters managed by the owne - set to governance multisig - can call `cancel` and cancel any pending operation +## `HATTimelockController.MANAGER_ROLE` + +- set to "anyone" +- can call `setVaultDescription` and `setVaultVisibility` + ## The following functions in HATVaults are **not** subject to a timelock: - `approveClaim` diff --git a/scripts/deployments/addresses.json b/scripts/deployments/addresses.json deleted file mode 100644 index 61b546fa..00000000 --- a/scripts/deployments/addresses.json +++ /dev/null @@ -1,46 +0,0 @@ -{ - "goerli": { - "governance": "0xFc9F1d127f8047B0F41e9eAC2Adc2e5279C568B7", - "arbitrator": "0x8fD27B5993E203fd27B15330f9b6D67b637639B9", - "hatTimelockController": "0xea8b945DAb3c11d374234Fc3DB8E1a9a5936A335", - "hatToken": "0x273FD7905527479252340ea411d20a987219a0a8", - "hatTokenLock": "0xEdff33E7cd333b2603c4E91Ac5c2b24694777299", - "tokenLockFactory": "0xEB8287ad99961Fa416EB94112a6Aa4312739f899", - "hatVaultsRegistry": "0xcdC4119ea3a4ee066e0C63790059Af854395b96D", - "rewardControllers": [ - "0x8719d270A389FE3c07149EEDA074404ce454a1F4" - ], - "rewardControllerImplementations": [], - "hatVaultImplementation": "0xE5793200BaE21Ef64C528336B482A322cbA90220", - "hatVaultsV2Data": "0xF1bbe158015958dDA6A3c80054FBF0D49df94D5C", - "hatVaultsNFT": "0x87b6DdCABdAA993D5FE913E6AF69A2d3E118d2a6" - }, - "hardhat": { - "governance": "0xc783df8a850f42e7F7e57013759C285caa701eB6", - "arbitrator": "0x3619DbE27d7c1e7E91aA738697Ae7Bc5FC3eACA5", - "hatTimelockController": "0x7c2C195CD6D34B8F845992d380aADB2730bB9C6F", - "hatToken": "0x0078371BDeDE8aAc7DeBfFf451B74c5EDB385Af7", - "hatTokenLock": "0xf4e77E5Da47AC3125140c470c71cBca77B5c638c", - "tokenLockFactory": "0xf784709d2317D872237C4bC22f867d1BAe2913AB", - "hatVaultsRegistry": "0x8B5B7a6055E54a36fF574bbE40cf2eA68d5554b3", - "rewardControllers": [ - "0xc4905364b78a742ccce7B890A89514061E47068D" - ], - "rewardControllerImplementations": [], - "hatVaultImplementation": "0xD6C850aeBFDC46D7F4c207e445cC0d6B0919BDBe", - "hatVaultsV2Data": "0xEcc0a6dbC0bb4D51E4F84A315a9e5B0438cAD4f0", - "hatVaultsNFT": "0x20Ce94F404343aD2752A2D01b43fa407db9E0D00" - }, - "optimism_goerli": { - "governance": "0x0B7602011EC2B862Bc157fF08d27b1018aEb18d5", - "arbitrator": "0x34782E01dB347Eb5449ab9329Bd6E92eA493b160", - "hatTimelockController": "0x878Cab06E6f4a85D90E5f236d326a41Ef6f44F9f", - "hatToken": "0xC570c434ba30a2fa5C07E590833246E18aa6B0a3", - "hatTokenLock": "0xadd155731473A9501881234A865FF79668F1B6cF", - "tokenLockFactory": "0x312917812e76d78C5B1139C28d5C1D3A272d171d", - "hatVaultsRegistry": "0x1065A9e3AC47f2624293727F4b8319A0a10F6015", - "rewardController": "0xf972D95812aCda82502Fb2e9072736C6d6EE41be", - "rewardControllerImplementation": "0xa80d0a371f4d37AFCc55188233BB4Ad463aF9E48", - "hatVaultImplementation": "0x2Ff0509D0e9a78Bf58815D768f4487f0645824F0" - } -} \ No newline at end of file diff --git a/scripts/deployments/config.json b/scripts/deployments/config.json index 6ec6d368..ea22daf3 100644 --- a/scripts/deployments/config.json +++ b/scripts/deployments/config.json @@ -1,96 +1,4 @@ { - "goerli": { - "governance": "0xFc9F1d127f8047B0F41e9eAC2Adc2e5279C568B7", - "timelockDelay": 300, - "executors": [ - "0x2bc1fed4c65c9b1dc2baaff2f3198acc42c41778" - ], - "rewardControllersConf": [{ - "startBlock": null, - "epochLength": "195200", - "epochRewardPerBlock": [ - "441300000000000000000", - "441300000000000000000", - "882500000000000000000", - "778800000000000000000", - "687300000000000000000", - "606500000000000000000", - "535300000000000000000", - "472400000000000000000", - "416900000000000000000", - "367900000000000000000", - "324700000000000000000", - "286500000000000000000", - "252800000000000000000", - "223100000000000000000", - "196900000000000000000", - "173800000000000000000", - "153400000000000000000", - "135300000000000000000", - "119400000000000000000", - "105400000000000000000", - "93000000000000000000", - "82100000000000000000", - "72400000000000000000", - "63900000000000000000" - ] - }], - "hatVaultsRegistryConf": { - "bountyGovernanceHAT": "1000", - "bountyHackerHATVested": "500" - }, - "hatVaultsNFTConf": { - "merkleTreeIPFSRef": "", - "root": null, - "deadline": null - } - }, - "optimism_goerli": { - "governance": "0x0B7602011EC2B862Bc157fF08d27b1018aEb18d5", - "timelockDelay": 300, - "executors": [ - "0x0B7602011EC2B862Bc157fF08d27b1018aEb18d5" - ], - "rewardControllersConf": [{ - "startBlock": null, - "epochLength": "195200", - "epochRewardPerBlock": [ - "441300000000000000000", - "441300000000000000000", - "882500000000000000000", - "778800000000000000000", - "687300000000000000000", - "606500000000000000000", - "535300000000000000000", - "472400000000000000000", - "416900000000000000000", - "367900000000000000000", - "324700000000000000000", - "286500000000000000000", - "252800000000000000000", - "223100000000000000000", - "196900000000000000000", - "173800000000000000000", - "153400000000000000000", - "135300000000000000000", - "119400000000000000000", - "105400000000000000000", - "93000000000000000000", - "82100000000000000000", - "72400000000000000000", - "63900000000000000000" - ] - }], - "hatVaultsRegistryConf": { - "bountyGovernanceHAT": "1000", - "bountyHackerHATVested": "500" - }, - "hatVaultsNFTConf": { - "merkleTreeIPFSRef": "", - "root": null, - "deadline": null - } - }, "hardhat": { "rewardControllersConf": [{ "startBlock": null, @@ -131,21 +39,5 @@ "root": null, "deadline": null } - }, - "polygon": { - "governance": "0xFc9F1d127f8047B0F41e9eAC2Adc2e5279C568B7", - "timelockDelay": 300, - "executors": [ ], - "rewardControllersConf": [], - "hatToken": "0x2791bca1f2de4661ed88a30c99a7a9449aa84174", - "hatVaultsRegistryConf": { - "bountyGovernanceHAT": "0", - "bountyHackerHATVested": "0" - }, - "hatVaultsNFTConf": { - "merkleTreeIPFSRef": "", - "root": null, - "deadline": null - } } } diff --git a/scripts/deployments/hats-deploy.js b/scripts/deployments/hats-deploy.js deleted file mode 100644 index 207f0758..00000000 --- a/scripts/deployments/hats-deploy.js +++ /dev/null @@ -1,111 +0,0 @@ -let CONFIG = require("./config.json"); -const ADDRESSES = require("./addresses.json"); -const { deployHATToken } = require("./hattoken-deploy.js"); -const { deployHATTimelockController } = require("./hattimelockcontroller-deploy.js"); -const { deployTokenLockFactory } = require("./tokenlockfactory-deploy.js"); -const { deployHATVaults } = require("./hatvaultsregistry-deploy.js"); -const { deployHATVaultsNFT } = require("./hatvaultsnft-deploy.js"); -const { hatsVerify } = require("./hats-verify.js"); -const fs = require("fs"); -const { network } = require("hardhat"); - -async function main() { - const config = CONFIG[network.name]; - const [deployer] = await ethers.getSigners(); - const deployerAddress = await deployer.getAddress(); - console.log("Deploying the contracts with the account:", deployerAddress); - console.log("Account balance:", (await deployer.getBalance()).toString()); - - if (!config["governance"] && network.name === "hardhat") { - config["governance"] = deployerAddress; - } - - let hatTimelockController = config["hatTimelockController"]; - if (!hatTimelockController) { - console.log("Deploying HATTimelockController"); - hatTimelockController = (await deployHATTimelockController(config)).address; - } - - config["hatTimelockController"] = hatTimelockController; - - let hatToken = config["hatToken"]; - if (!hatToken) { - console.log("Deploying HATtoken"); - hatToken = (await deployHATToken(config)).address; - } - - config["hatToken"] = hatToken; - - let hatTokenLock = config["hatTokenLock"]; - let tokenLockFactory = config["tokenLockFactory"]; - if (!tokenLockFactory) { - console.log("Deploying TokenLockFactory"); - let deployment = (await deployTokenLockFactory(config)); - hatTokenLock = deployment.hatTokenLock.address; - tokenLockFactory = deployment.tokenLockFactory.address; - } - - config["hatTokenLock"] = hatTokenLock; - config["tokenLockFactory"] = tokenLockFactory; - - console.log("Deploying HATVaults"); - let { - hatVaultsRegistry, - rewardControllers, - rewardControllerImplementations, - hatVaultImplementation, - arbitrator - } = (await deployHATVaults(config)); - - config["arbitrator"] = arbitrator; - config["hatVaultsRegistry"] = hatVaultsRegistry.address; - config["rewardControllers"] = rewardControllers.map((x) => x.address); - config["rewardControllerImplementation"] = rewardControllerImplementations.map((x) => x.address); - config["hatVaultImplementation"] = hatVaultImplementation.address; - - console.log("Deploying HATVaultsNFT"); - let { hatVaultsV2Data, hatVaultsNFT, merkleTreeIPFSRef, root, deadline } = (await deployHATVaultsNFT(config)); - - config["hatVaultsV2Data"] = hatVaultsV2Data.address; - config["hatVaultsNFT"] = hatVaultsNFT.address; - - config["hatVaultsNFTConf"]["merkleTreeIPFSRef"] = merkleTreeIPFSRef; - config["hatVaultsNFTConf"]["root"] = root; - config["hatVaultsNFTConf"]["deadline"] = deadline; - - ADDRESSES[network.name] = { - governance: config["governance"], - arbitrator: config["arbitrator"], - hatTimelockController: config["hatTimelockController"], - hatToken: config["hatToken"], - hatTokenLock: config["hatTokenLock"], - tokenLockFactory: config["tokenLockFactory"], - hatVaultsRegistry: config["hatVaultsRegistry"], - rewardControllers: config["rewardControllers"], - rewardControllerImplementations: config["rewardControllerImplementations"] || [], - hatVaultImplementation: config["hatVaultImplementation"], - hatVaultsV2Data: config["hatVaultsV2Data"], - hatVaultsNFT: config["hatVaultsNFT"] - }; - const outputFile = __dirname + '/addresses.json'; - fs.writeFileSync(outputFile, JSON.stringify(ADDRESSES, null, 2)); - - console.log(`Output written to ${outputFile}`); - console.log(ADDRESSES[network.name]); - if (network.name !== "hardhat") { - console.log("Verifying contracts"); - await hatsVerify(ADDRESSES[network.name], config).catch((error) => { - console.error("verification failed"); - console.error(error); - }); - } -} - -if (require.main === module) { - main() - .then(() => process.exit(0)) - .catch((error) => { - console.error(error); - process.exit(1); - }); -} diff --git a/scripts/deployments/hats-verify.js b/scripts/deployments/hats-verify.js deleted file mode 100644 index 195c2e97..00000000 --- a/scripts/deployments/hats-verify.js +++ /dev/null @@ -1,100 +0,0 @@ -const CONFIG = require("./config.json"); -const ADDRESSES = require("./addresses.json"); - -async function main(addresses, config) { - if (!config) { - config = CONFIG[network.name]; - } - - if (!addresses) { - addresses = ADDRESSES[network.name]; - } - - let hatTimelockController = addresses["hatTimelockController"]; - let governance = config["governance"]; - let hatGovernanceDelay; - if (config["timelockDelay"]) { - hatGovernanceDelay = config["timelockDelay"]; - } else { - hatGovernanceDelay = network.name === "mainnet" ? 60 * 60 * 24 * 7 : 60 * 5; // 7 days for mainnet or 5 minutes for testnets - } - let executors = config["executors"]; - if (!executors) { - executors = [governance]; - } - - if (executors.indexOf(governance) === -1) { - executors.push(governance); - } - - await verifyContract(hatTimelockController, [ - hatGovernanceDelay, - [governance], - executors, - ]); - - let hatToken = addresses["hatToken"]; - await verifyContract(hatToken, [hatTimelockController]); - - let hatTokenLock = addresses["hatTokenLock"]; - let tokenLockFactory = addresses["tokenLockFactory"]; - - await verifyContract(hatTokenLock, []); - await verifyContract(tokenLockFactory, [hatTokenLock, hatTimelockController]); - - let arbitrator = addresses["arbitrator"]; - let hatVaultsRegistry = addresses["hatVaultsRegistry"]; - let rewardControllerImplementations = addresses["rewardControllerImplementations"]; - let hatVaultImplementation = addresses["hatVaultImplementation"]; - - let bountyGovernanceHAT = config["hatVaultsRegistryConf"]["bountyGovernanceHAT"]; - let bountyHackerHATVested = config["hatVaultsRegistryConf"]["bountyHackerHATVested"]; - - await verifyContract(arbitrator, []); - for (const rewardControllerImplementation of rewardControllerImplementations ) { - await verifyContract(rewardControllerImplementation, []); - } - await verifyContract(hatVaultImplementation, []); - await verifyContract(hatVaultsRegistry, [ - hatVaultImplementation, - hatTimelockController, - arbitrator, - hatToken, - bountyGovernanceHAT, - bountyHackerHATVested, - tokenLockFactory - ]); - - let hatVaultsV2Data = addresses["hatVaultsV2Data"]; - await verifyContract(hatVaultsV2Data, [hatVaultsRegistry]); - - let hatVaultsNFT = addresses["hatVaultsNFT"]; - let merkleTreeIPFSRef = config["hatVaultsNFTConf"]["merkleTreeIPFSRef"]; - let root = config["hatVaultsNFTConf"]["root"]; - let deadline = config["hatVaultsNFTConf"]["deadline"]; - - await verifyContract(hatVaultsNFT, [merkleTreeIPFSRef, root, deadline]); -} - - -async function verifyContract(address, args) { - try { - await hre.run("verify:verify", { - address, - constructorArguments: args, - }); - } catch (error) { - console.log("Verification failed with error: " + error); - } -} - -if (require.main === module) { - main() - .then(() => process.exit(0)) - .catch((error) => { - console.error(error); - process.exit(1); - }); -} - -module.exports = { hatsVerify: main }; diff --git a/scripts/deployments/hattimelockcontroller-deploy.js b/scripts/deployments/hattimelockcontroller-deploy.js deleted file mode 100644 index 4a24e88d..00000000 --- a/scripts/deployments/hattimelockcontroller-deploy.js +++ /dev/null @@ -1,72 +0,0 @@ -const { network } = require("hardhat"); -const CONFIG = require("./config.json"); - -const TIMELOCK_ADMIN_ROLE = "0x5f58e3a2316349923ce3780f8d587db2d72378aed66a8261c916544fa6846ca5"; - -async function main(config) { - if (!config) { - config = CONFIG[network.name]; - } - - const silent = config["silent"] === true; - - const [deployer] = await ethers.getSigners(); - const deployerAddress = await deployer.getAddress(); - - let governance = config["governance"]; - if (!governance && network.name === "hardhat") { - governance = deployerAddress; - } - - let hatGovernanceDelay; - if (config["timelockDelay"]) { - hatGovernanceDelay = config["timelockDelay"]; - } else { - hatGovernanceDelay = network.name === "mainnet" ? 60 * 60 * 24 * 7 : 60 * 5; // 7 days for mainnet or 5 minutes for testnets - } - - let executors = config["executors"]; - if (!executors) { - executors = [governance]; - } - - if (executors.indexOf(governance) === -1) { - executors.push(governance); - } - - const HATTimelockController = await ethers.getContractFactory( - "HATTimelockController" - ); - - let hatTimelockController = await HATTimelockController.deploy( - hatGovernanceDelay, // minDelay - [governance], // proposers - executors // executors - ); - await hatTimelockController.deployed(); - - if (!silent) { - console.log("HATTimelockController address: " + hatTimelockController.address); - } - - // the deployer automatically gets the admin role, which we renounce (so all admin has to go through the proposer) - // TODO: Remove this with OpenZepellin 4.8.0 - await hatTimelockController.renounceRole(TIMELOCK_ADMIN_ROLE, deployerAddress); - - if (!silent) { - console.log(`Deployer renounced admin role`); - } - - return hatTimelockController; -} - -if (require.main === module) { - main() - .then(() => process.exit(0)) - .catch((error) => { - console.error(error); - process.exit(1); - }); -} - -module.exports = { deployHATTimelockController: main }; diff --git a/scripts/deployments/hattoken-deploy.js b/scripts/deployments/hattoken-deploy.js deleted file mode 100644 index 926c83a9..00000000 --- a/scripts/deployments/hattoken-deploy.js +++ /dev/null @@ -1,41 +0,0 @@ -const { network } = require("hardhat"); -const CONFIG = require("./config.json"); - -async function main(config) { - if (!config) { - config = CONFIG[network.name]; - } - - const silent = config["silent"] === true; - - const [deployer] = await ethers.getSigners(); - const deployerAddress = await deployer.getAddress(); - - let governance = config["governance"]; - - if (config["hatTimelockController"]) { - governance = config["hatTimelockController"]; - } else if (!governance && network.name === "hardhat") { - governance = deployerAddress; - } - - const HATToken = await ethers.getContractFactory("HATToken"); - const hatToken = await HATToken.deploy(governance); - await hatToken.deployed(); - - if (!silent) { - console.log("HATToken address: " + hatToken.address); - } - return hatToken; -} - -if (require.main === module) { - main() - .then(() => process.exit(0)) - .catch((error) => { - console.error(error); - process.exit(1); - }); -} - -module.exports = { deployHATToken: main }; diff --git a/scripts/deployments/hatvaultsnft-deploy.js b/scripts/deployments/hatvaultsnft-deploy.js deleted file mode 100644 index 20ad8386..00000000 --- a/scripts/deployments/hatvaultsnft-deploy.js +++ /dev/null @@ -1,75 +0,0 @@ -const { network } = require("hardhat"); -const CONFIG = require("./config.json"); - -async function main(config) { - if (!config) { - config = CONFIG[network.name]; - } - - const silent = config["silent"] === true; - - const [deployer] = await ethers.getSigners(); - const deployerAddress = await deployer.getAddress(); - - let hatVaultsRegistry = config["hatVaultsRegistry"]; - if (!hatVaultsRegistry && network.name === "hardhat") { - hatVaultsRegistry = "0x8B5B7a6055E54a36fF574bbE40cf2eA68d5554b3"; - } - - const HATVaultsV2Data = await ethers.getContractFactory("HATVaultsV2Data"); - const hatVaultsV2Data = await HATVaultsV2Data.deploy(hatVaultsRegistry); - await hatVaultsV2Data.deployed(); - - if (!silent) { - console.log("HATVaultsV2Data address: " + hatVaultsV2Data.address); - } - - let merkleTreeIPFSRef = config["hatVaultsNFTConf"]["merkleTreeIPFSRef"]; - if (!merkleTreeIPFSRef) { - merkleTreeIPFSRef = ""; - } - - let root = config["hatVaultsNFTConf"]["root"]; - if (!root) { - root = ethers.constants.HashZero; - } - - let deadline = config["hatVaultsNFTConf"]["deadline"]; - if (!deadline) { - const now = (await ethers.provider.getBlock(await ethers.provider.getBlockNumber())).timestamp; - deadline = now + (5 * 60); - } - - const HATVaultsNFT = await ethers.getContractFactory("HATVaultsNFT"); - const hatVaultsNFT = await HATVaultsNFT.deploy(merkleTreeIPFSRef, root, deadline); - await hatVaultsNFT.deployed(); - - if (!silent) { - console.log("HATVaultsNFT address: " + hatVaultsNFT.address); - } - - let governance = config["governance"]; - if (!governance && network.name === "hardhat") { - governance = deployerAddress; - } - - if (governance !== deployerAddress) { - await hatVaultsNFT.transferOwnership(governance); - if (!silent) { - console.log("HATVaultsNFT ownership transffered to governance"); - } - } - - return { hatVaultsV2Data, hatVaultsNFT, merkleTreeIPFSRef, root, deadline }; -} - -if (require.main === module) { - main() - .then(() => process.exit(0)) - .catch((error) => { - console.error(error); - process.exit(1); - }); -} - -module.exports = { deployHATVaultsNFT: main }; diff --git a/scripts/deployments/tokenlockfactory-deploy.js b/scripts/deployments/tokenlockfactory-deploy.js deleted file mode 100644 index a6675040..00000000 --- a/scripts/deployments/tokenlockfactory-deploy.js +++ /dev/null @@ -1,50 +0,0 @@ - -const CONFIG = require("./config.json"); - -async function main(config) { - if (!config) { - config = CONFIG[network.name]; - } - - const silent = config["silent"] === true; - - const [deployer] = await ethers.getSigners(); - const deployerAddress = await deployer.getAddress(); - - let governance = config["governance"]; - - if (config["hatTimelockController"]) { - governance = config["hatTimelockController"]; - } else if (!governance && network.name === "hardhat") { - governance = deployerAddress; - } - - const HATTokenLock = await ethers.getContractFactory("HATTokenLock"); - const hatTokenLock = await HATTokenLock.deploy(); - await hatTokenLock.deployed(); - - if (!silent) { - console.log("HATTokenLock address: " + hatTokenLock.address); - } - - const TokenLockFactory = await ethers.getContractFactory("TokenLockFactory"); - const tokenLockFactory = await TokenLockFactory.deploy(hatTokenLock.address, governance); - await tokenLockFactory.deployed(); - - if (!silent) { - console.log("TokenLockFactory address: " + tokenLockFactory.address); - } - - return { hatTokenLock, tokenLockFactory }; -} - -if (require.main === module) { - main() - .then(() => process.exit(0)) - .catch((error) => { - console.error(error); - process.exit(1); - }); -} - -module.exports = { deployTokenLockFactory: main }; diff --git a/test/hattimelockcontroller.js b/test/hattimelockcontroller.js index 1f37a745..f9c35a28 100644 --- a/test/hattimelockcontroller.js +++ b/test/hattimelockcontroller.js @@ -80,7 +80,8 @@ const setup = async function( hatTimelockController = await HATTimelockController.new( hatGovernanceDelay, [accounts[0]], - [accounts[0]] + [accounts[0]], + [accounts[1]] ); await hatToken.setMinter( @@ -189,15 +190,22 @@ contract("HatTimelockController", (accounts) => { ), true ); + assert.equal( + await hatTimelockController.hasRole( + await hatTimelockController.MANAGER_ROLE(), + accounts[1] + ), + true + ); }); it("Update vault visibility", async () => { await setup(accounts); try { await hatTimelockController.setVaultVisibility(vault.address, true, { - from: accounts[1], + from: accounts[0], }); - assert(false, "only governance"); + assert(false, "only manager"); } catch (ex) { assertVMException(ex); } @@ -209,7 +217,7 @@ contract("HatTimelockController", (accounts) => { assertVMException(ex); } assert.equal(await hatVaultsRegistry.isVaultVisible(vault.address), false); - await hatTimelockController.setVaultVisibility(vault.address, true); + await hatTimelockController.setVaultVisibility(vault.address, true, { from: accounts[1] }); assert.equal(await hatVaultsRegistry.isVaultVisible(vault.address), true); await hatTimelockController.setAllocPoint(vault.address, rewardController.address, 200); @@ -220,8 +228,8 @@ contract("HatTimelockController", (accounts) => { await stakingToken.mint(staker, web3.utils.toWei("1")); await vault.deposit(web3.utils.toWei("1"), staker, { from: staker }); assert.equal(await hatToken.balanceOf(staker), 0); - await hatTimelockController.setVaultVisibility(vault.address, true); - await hatTimelockController.setVaultVisibility(vault.address, true); + await hatTimelockController.setVaultVisibility(vault.address, true, { from: accounts[1] }); + await hatTimelockController.setVaultVisibility(vault.address, true, { from: accounts[1] }); await hatTimelockController.setAllocPoint(vault.address, rewardController.address, 200); let expectedReward = await calculateExpectedReward(staker); assert.equal(await stakingToken.balanceOf(staker), 0); @@ -241,9 +249,9 @@ contract("HatTimelockController", (accounts) => { await setup(accounts); try { await hatTimelockController.setVaultDescription(vault.address, "descHash", { - from: accounts[1], + from: accounts[0], }); - assert(false, "only governance"); + assert(false, "only manager"); } catch (ex) { assertVMException(ex); } @@ -254,7 +262,7 @@ contract("HatTimelockController", (accounts) => { } catch (ex) { assertVMException(ex); } - await hatTimelockController.setVaultDescription(vault.address, "descHash"); + await hatTimelockController.setVaultDescription(vault.address, "descHash", { from: accounts[1] }); }); it("Pause vault deposits", async () => { From 00fde3e6a96c1eec5f8df639278e1ba2d90699a8 Mon Sep 17 00:00:00 2001 From: benk10 Date: Mon, 24 Jun 2024 13:32:14 -0500 Subject: [PATCH 2/2] Fix script --- deploy/013_verify_deployment.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deploy/013_verify_deployment.js b/deploy/013_verify_deployment.js index e81dbb73..10d01f89 100644 --- a/deploy/013_verify_deployment.js +++ b/deploy/013_verify_deployment.js @@ -93,7 +93,7 @@ const func = async function (hre) { ); } - for (manager of manager) { + for (manager of managers) { // Each executor has the execute role verify( await read('HATTimelockController', {}, 'hasRole', MANAGER_ROLE, manager),