From cda88479521bf72b17f01e71680c98a23c4d6a61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Francisco=20L=C3=B3pez?= Date: Thu, 25 Sep 2025 17:04:22 +0200 Subject: [PATCH 1/2] - Refactored MetaHumanGovernor to inherit from GovernorUpgradeable and related upgradeable contracts. - Added withdraw function for magistrate to withdraw contracts balance. - Introduced error handling for various scenarios in governance functions. - Modified deployment scripts to deploy MetaHumanGovernor as a proxy and created a new one for upgrading a new version. - Updated tests to accommodate changes in governance contract structure and error handling. --- packages/core/.openzeppelin/sepolia.json | 2056 +++++++++++++++++ .../CrossChainGovernorCountingSimple.sol | 22 +- .../contracts/governance/DAOSpokeContract.sol | 178 +- .../governance/MetaHumanGovernor.sol | 223 +- .../governance/magistrate/Magistrate.sol | 16 +- .../vendor/TimelockController.import.sol | 6 + packages/core/hardhat.config.ts | 18 +- packages/core/package.json | 1 + packages/core/scripts/create-proposal.ts | 2 +- packages/core/scripts/deploy-hub.ts | 40 +- packages/core/scripts/upgrade-governor.ts | 43 + packages/core/test/DAOSpokeContract.ts | 70 +- packages/core/test/GovernanceUtils.ts | 30 +- packages/core/test/MetaHumanGovernor.ts | 138 +- .../core/test/MetaHumanGovernorHubOnly.ts | 114 +- 15 files changed, 2537 insertions(+), 420 deletions(-) create mode 100644 packages/core/contracts/vendor/TimelockController.import.sol create mode 100644 packages/core/scripts/upgrade-governor.ts diff --git a/packages/core/.openzeppelin/sepolia.json b/packages/core/.openzeppelin/sepolia.json index 345bff1701..9db9f24c42 100644 --- a/packages/core/.openzeppelin/sepolia.json +++ b/packages/core/.openzeppelin/sepolia.json @@ -15,6 +15,11 @@ "address": "0xaEf023AdF6D48239E520F69080bC14eCE4fCFBdd", "txHash": "0x6fe2bccee60cd77036ad31d957b1862ee1d9d6726967202a47e3a34facbf5913", "kind": "uups" + }, + { + "address": "0xF95541CC8548B9329e6122aeBD006603eB9F3d26", + "txHash": "0x026b9c522a535f4da3359265d2d268e038b1f20f0a45715d2ea1fe9a49270e85", + "kind": "transparent" } ], "impls": { @@ -1025,6 +1030,2057 @@ }, "namespaces": {} } + }, + "997c4276ebd1068cf2fca3ea27c2437e7b4653172694493e47a1b4e2d25bae8e": { + "address": "0x4be4c98F0AB61D0248a09398827aD9Bb693Ee609", + "txHash": "0xa793af2a4e929cb34ab18cc12987d817a206ea7e99417664bf54aa367e5cac8b", + "layout": { + "solcVersion": "0.8.23", + "storage": [ + { + "label": "_initialized", + "offset": 0, + "slot": "0", + "type": "t_uint8", + "contract": "Initializable", + "src": "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol:63", + "retypedFrom": "bool" + }, + { + "label": "_initializing", + "offset": 1, + "slot": "0", + "type": "t_bool", + "contract": "Initializable", + "src": "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol:68" + }, + { + "label": "__gap", + "offset": 0, + "slot": "1", + "type": "t_array(t_uint256)50_storage", + "contract": "ContextUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol:40" + }, + { + "label": "__gap", + "offset": 0, + "slot": "51", + "type": "t_array(t_uint256)50_storage", + "contract": "ERC165Upgradeable", + "src": "@openzeppelin/contracts-upgradeable/utils/introspection/ERC165Upgradeable.sol:41" + }, + { + "label": "_hashedName", + "offset": 0, + "slot": "101", + "type": "t_bytes32", + "contract": "EIP712Upgradeable", + "src": "@openzeppelin/contracts-upgradeable/utils/cryptography/EIP712Upgradeable.sol:40", + "renamedFrom": "_HASHED_NAME" + }, + { + "label": "_hashedVersion", + "offset": 0, + "slot": "102", + "type": "t_bytes32", + "contract": "EIP712Upgradeable", + "src": "@openzeppelin/contracts-upgradeable/utils/cryptography/EIP712Upgradeable.sol:42", + "renamedFrom": "_HASHED_VERSION" + }, + { + "label": "_name", + "offset": 0, + "slot": "103", + "type": "t_string_storage", + "contract": "EIP712Upgradeable", + "src": "@openzeppelin/contracts-upgradeable/utils/cryptography/EIP712Upgradeable.sol:44" + }, + { + "label": "_version", + "offset": 0, + "slot": "104", + "type": "t_string_storage", + "contract": "EIP712Upgradeable", + "src": "@openzeppelin/contracts-upgradeable/utils/cryptography/EIP712Upgradeable.sol:45" + }, + { + "label": "__gap", + "offset": 0, + "slot": "105", + "type": "t_array(t_uint256)48_storage", + "contract": "EIP712Upgradeable", + "src": "@openzeppelin/contracts-upgradeable/utils/cryptography/EIP712Upgradeable.sol:204" + }, + { + "label": "__gap", + "offset": 0, + "slot": "153", + "type": "t_array(t_uint256)50_storage", + "contract": "IGovernorUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/governance/IGovernorUpgradeable.sol:325" + }, + { + "label": "__gap", + "offset": 0, + "slot": "203", + "type": "t_array(t_uint256)50_storage", + "contract": "IGovernorTimelockUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/governance/extensions/IGovernorTimelockUpgradeable.sol:38" + }, + { + "label": "_name", + "offset": 0, + "slot": "253", + "type": "t_string_storage", + "contract": "GovernorUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/governance/GovernorUpgradeable.sol:51" + }, + { + "label": "_proposals", + "offset": 0, + "slot": "254", + "type": "t_mapping(t_uint256,t_struct(ProposalCore)600_storage)", + "contract": "GovernorUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/governance/GovernorUpgradeable.sol:54", + "retypedFrom": "mapping(uint256 => Governor.ProposalCore)" + }, + { + "label": "_governanceCall", + "offset": 0, + "slot": "255", + "type": "t_struct(Bytes32Deque)10266_storage", + "contract": "GovernorUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/governance/GovernorUpgradeable.sol:60" + }, + { + "label": "__gap", + "offset": 0, + "slot": "257", + "type": "t_array(t_uint256)46_storage", + "contract": "GovernorUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/governance/GovernorUpgradeable.sol:735" + }, + { + "label": "_votingDelay", + "offset": 0, + "slot": "303", + "type": "t_uint256", + "contract": "GovernorSettingsUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/governance/extensions/GovernorSettingsUpgradeable.sol:15" + }, + { + "label": "_votingPeriod", + "offset": 0, + "slot": "304", + "type": "t_uint256", + "contract": "GovernorSettingsUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/governance/extensions/GovernorSettingsUpgradeable.sol:16" + }, + { + "label": "_proposalThreshold", + "offset": 0, + "slot": "305", + "type": "t_uint256", + "contract": "GovernorSettingsUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/governance/extensions/GovernorSettingsUpgradeable.sol:17" + }, + { + "label": "__gap", + "offset": 0, + "slot": "306", + "type": "t_array(t_uint256)47_storage", + "contract": "GovernorSettingsUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/governance/extensions/GovernorSettingsUpgradeable.sol:121" + }, + { + "label": "_owner", + "offset": 0, + "slot": "353", + "type": "t_address", + "contract": "OwnableUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol:22" + }, + { + "label": "__gap", + "offset": 0, + "slot": "354", + "type": "t_array(t_uint256)49_storage", + "contract": "OwnableUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol:94" + }, + { + "label": "spokeContractsMapping", + "offset": 0, + "slot": "403", + "type": "t_mapping(t_bytes32,t_mapping(t_uint16,t_bool))", + "contract": "CrossChainGovernorCountingSimple", + "src": "contracts/governance/CrossChainGovernorCountingSimple.sol:18" + }, + { + "label": "spokeContracts", + "offset": 0, + "slot": "404", + "type": "t_array(t_struct(CrossChainAddress)13605_storage)dyn_storage", + "contract": "CrossChainGovernorCountingSimple", + "src": "contracts/governance/CrossChainGovernorCountingSimple.sol:19" + }, + { + "label": "spokeContractsMappingSnapshots", + "offset": 0, + "slot": "405", + "type": "t_mapping(t_uint256,t_mapping(t_bytes32,t_mapping(t_uint16,t_bool)))", + "contract": "CrossChainGovernorCountingSimple", + "src": "contracts/governance/CrossChainGovernorCountingSimple.sol:20" + }, + { + "label": "spokeContractsSnapshots", + "offset": 0, + "slot": "406", + "type": "t_mapping(t_uint256,t_array(t_struct(CrossChainAddress)13605_storage)dyn_storage)", + "contract": "CrossChainGovernorCountingSimple", + "src": "contracts/governance/CrossChainGovernorCountingSimple.sol:22" + }, + { + "label": "spokeVotes", + "offset": 0, + "slot": "407", + "type": "t_mapping(t_uint256,t_mapping(t_bytes32,t_mapping(t_uint16,t_struct(SpokeProposalVote)13614_storage)))", + "contract": "CrossChainGovernorCountingSimple", + "src": "contracts/governance/CrossChainGovernorCountingSimple.sol:23" + }, + { + "label": "_proposalVotes", + "offset": 0, + "slot": "408", + "type": "t_mapping(t_uint256,t_struct(ProposalVote)13630_storage)", + "contract": "CrossChainGovernorCountingSimple", + "src": "contracts/governance/CrossChainGovernorCountingSimple.sol:25" + }, + { + "label": "token", + "offset": 0, + "slot": "409", + "type": "t_contract(IERC5805Upgradeable)4632", + "contract": "GovernorVotesUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/governance/extensions/GovernorVotesUpgradeable.sol:18" + }, + { + "label": "__gap", + "offset": 0, + "slot": "410", + "type": "t_array(t_uint256)50_storage", + "contract": "GovernorVotesUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/governance/extensions/GovernorVotesUpgradeable.sol:68" + }, + { + "label": "_quorumNumerator", + "offset": 0, + "slot": "460", + "type": "t_uint256", + "contract": "GovernorVotesQuorumFractionUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/governance/extensions/GovernorVotesQuorumFractionUpgradeable.sol:20" + }, + { + "label": "_quorumNumeratorHistory", + "offset": 0, + "slot": "461", + "type": "t_struct(Trace224)5746_storage", + "contract": "GovernorVotesQuorumFractionUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/governance/extensions/GovernorVotesQuorumFractionUpgradeable.sol:23", + "retypedFrom": "Checkpoints.History" + }, + { + "label": "__gap", + "offset": 0, + "slot": "462", + "type": "t_array(t_uint256)48_storage", + "contract": "GovernorVotesQuorumFractionUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/governance/extensions/GovernorVotesQuorumFractionUpgradeable.sol:132" + }, + { + "label": "_timelock", + "offset": 0, + "slot": "510", + "type": "t_contract(TimelockControllerUpgradeable)3488", + "contract": "GovernorTimelockControlUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/governance/extensions/GovernorTimelockControlUpgradeable.sol:28" + }, + { + "label": "_timelockIds", + "offset": 0, + "slot": "511", + "type": "t_mapping(t_uint256,t_bytes32)", + "contract": "GovernorTimelockControlUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/governance/extensions/GovernorTimelockControlUpgradeable.sol:29" + }, + { + "label": "__gap", + "offset": 0, + "slot": "512", + "type": "t_array(t_uint256)48_storage", + "contract": "GovernorTimelockControlUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/governance/extensions/GovernorTimelockControlUpgradeable.sol:177" + }, + { + "label": "_magistrate", + "offset": 0, + "slot": "560", + "type": "t_address", + "contract": "Magistrate", + "src": "contracts/governance/magistrate/Magistrate.sol:13" + }, + { + "label": "wormholeRelayer", + "offset": 0, + "slot": "561", + "type": "t_contract(IWormholeRelayer)16471", + "contract": "MetaHumanGovernor", + "src": "contracts/governance/MetaHumanGovernor.sol:48" + }, + { + "label": "secondsPerBlock", + "offset": 0, + "slot": "562", + "type": "t_uint256", + "contract": "MetaHumanGovernor", + "src": "contracts/governance/MetaHumanGovernor.sol:50" + }, + { + "label": "chainId", + "offset": 0, + "slot": "563", + "type": "t_uint16", + "contract": "MetaHumanGovernor", + "src": "contracts/governance/MetaHumanGovernor.sol:51" + }, + { + "label": "processedMessages", + "offset": 0, + "slot": "564", + "type": "t_mapping(t_bytes32,t_bool)", + "contract": "MetaHumanGovernor", + "src": "contracts/governance/MetaHumanGovernor.sol:53" + }, + { + "label": "collectionStarted", + "offset": 0, + "slot": "565", + "type": "t_mapping(t_uint256,t_bool)", + "contract": "MetaHumanGovernor", + "src": "contracts/governance/MetaHumanGovernor.sol:54" + }, + { + "label": "collectionFinished", + "offset": 0, + "slot": "566", + "type": "t_mapping(t_uint256,t_bool)", + "contract": "MetaHumanGovernor", + "src": "contracts/governance/MetaHumanGovernor.sol:55" + } + ], + "types": { + "t_address": { + "label": "address", + "numberOfBytes": "20" + }, + "t_array(t_struct(Checkpoint224)5751_storage)dyn_storage": { + "label": "struct CheckpointsUpgradeable.Checkpoint224[]", + "numberOfBytes": "32" + }, + "t_array(t_struct(CrossChainAddress)13605_storage)dyn_storage": { + "label": "struct CrossChainGovernorCountingSimple.CrossChainAddress[]", + "numberOfBytes": "32" + }, + "t_array(t_uint256)46_storage": { + "label": "uint256[46]", + "numberOfBytes": "1472" + }, + "t_array(t_uint256)47_storage": { + "label": "uint256[47]", + "numberOfBytes": "1504" + }, + "t_array(t_uint256)48_storage": { + "label": "uint256[48]", + "numberOfBytes": "1536" + }, + "t_array(t_uint256)49_storage": { + "label": "uint256[49]", + "numberOfBytes": "1568" + }, + "t_array(t_uint256)50_storage": { + "label": "uint256[50]", + "numberOfBytes": "1600" + }, + "t_bool": { + "label": "bool", + "numberOfBytes": "1" + }, + "t_bytes24": { + "label": "bytes24", + "numberOfBytes": "24" + }, + "t_bytes32": { + "label": "bytes32", + "numberOfBytes": "32" + }, + "t_bytes4": { + "label": "bytes4", + "numberOfBytes": "4" + }, + "t_contract(IERC5805Upgradeable)4632": { + "label": "contract IERC5805Upgradeable", + "numberOfBytes": "20" + }, + "t_contract(IWormholeRelayer)16471": { + "label": "contract IWormholeRelayer", + "numberOfBytes": "20" + }, + "t_contract(TimelockControllerUpgradeable)3488": { + "label": "contract TimelockControllerUpgradeable", + "numberOfBytes": "20" + }, + "t_int128": { + "label": "int128", + "numberOfBytes": "16" + }, + "t_mapping(t_address,t_bool)": { + "label": "mapping(address => bool)", + "numberOfBytes": "32" + }, + "t_mapping(t_bytes32,t_bool)": { + "label": "mapping(bytes32 => bool)", + "numberOfBytes": "32" + }, + "t_mapping(t_bytes32,t_mapping(t_uint16,t_bool))": { + "label": "mapping(bytes32 => mapping(uint16 => bool))", + "numberOfBytes": "32" + }, + "t_mapping(t_bytes32,t_mapping(t_uint16,t_struct(SpokeProposalVote)13614_storage))": { + "label": "mapping(bytes32 => mapping(uint16 => struct CrossChainGovernorCountingSimple.SpokeProposalVote))", + "numberOfBytes": "32" + }, + "t_mapping(t_int128,t_bytes32)": { + "label": "mapping(int128 => bytes32)", + "numberOfBytes": "32" + }, + "t_mapping(t_uint16,t_bool)": { + "label": "mapping(uint16 => bool)", + "numberOfBytes": "32" + }, + "t_mapping(t_uint16,t_struct(SpokeProposalVote)13614_storage)": { + "label": "mapping(uint16 => struct CrossChainGovernorCountingSimple.SpokeProposalVote)", + "numberOfBytes": "32" + }, + "t_mapping(t_uint256,t_array(t_struct(CrossChainAddress)13605_storage)dyn_storage)": { + "label": "mapping(uint256 => struct CrossChainGovernorCountingSimple.CrossChainAddress[])", + "numberOfBytes": "32" + }, + "t_mapping(t_uint256,t_bool)": { + "label": "mapping(uint256 => bool)", + "numberOfBytes": "32" + }, + "t_mapping(t_uint256,t_bytes32)": { + "label": "mapping(uint256 => bytes32)", + "numberOfBytes": "32" + }, + "t_mapping(t_uint256,t_mapping(t_bytes32,t_mapping(t_uint16,t_bool)))": { + "label": "mapping(uint256 => mapping(bytes32 => mapping(uint16 => bool)))", + "numberOfBytes": "32" + }, + "t_mapping(t_uint256,t_mapping(t_bytes32,t_mapping(t_uint16,t_struct(SpokeProposalVote)13614_storage)))": { + "label": "mapping(uint256 => mapping(bytes32 => mapping(uint16 => struct CrossChainGovernorCountingSimple.SpokeProposalVote)))", + "numberOfBytes": "32" + }, + "t_mapping(t_uint256,t_struct(ProposalCore)600_storage)": { + "label": "mapping(uint256 => struct GovernorUpgradeable.ProposalCore)", + "numberOfBytes": "32" + }, + "t_mapping(t_uint256,t_struct(ProposalVote)13630_storage)": { + "label": "mapping(uint256 => struct CrossChainGovernorCountingSimple.ProposalVote)", + "numberOfBytes": "32" + }, + "t_string_storage": { + "label": "string", + "numberOfBytes": "32" + }, + "t_struct(Bytes32Deque)10266_storage": { + "label": "struct DoubleEndedQueueUpgradeable.Bytes32Deque", + "members": [ + { + "label": "_begin", + "type": "t_int128", + "offset": 0, + "slot": "0" + }, + { + "label": "_end", + "type": "t_int128", + "offset": 16, + "slot": "0" + }, + { + "label": "_data", + "type": "t_mapping(t_int128,t_bytes32)", + "offset": 0, + "slot": "1" + } + ], + "numberOfBytes": "64" + }, + "t_struct(Checkpoint224)5751_storage": { + "label": "struct CheckpointsUpgradeable.Checkpoint224", + "members": [ + { + "label": "_key", + "type": "t_uint32", + "offset": 0, + "slot": "0" + }, + { + "label": "_value", + "type": "t_uint224", + "offset": 4, + "slot": "0" + } + ], + "numberOfBytes": "32" + }, + "t_struct(CrossChainAddress)13605_storage": { + "label": "struct CrossChainGovernorCountingSimple.CrossChainAddress", + "members": [ + { + "label": "contractAddress", + "type": "t_bytes32", + "offset": 0, + "slot": "0" + }, + { + "label": "chainId", + "type": "t_uint16", + "offset": 0, + "slot": "1" + } + ], + "numberOfBytes": "64" + }, + "t_struct(ProposalCore)600_storage": { + "label": "struct GovernorUpgradeable.ProposalCore", + "members": [ + { + "label": "voteStart", + "type": "t_uint64", + "offset": 0, + "slot": "0" + }, + { + "label": "proposer", + "type": "t_address", + "offset": 8, + "slot": "0" + }, + { + "label": "__gap_unused0", + "type": "t_bytes4", + "offset": 28, + "slot": "0" + }, + { + "label": "voteEnd", + "type": "t_uint64", + "offset": 0, + "slot": "1" + }, + { + "label": "__gap_unused1", + "type": "t_bytes24", + "offset": 8, + "slot": "1" + }, + { + "label": "executed", + "type": "t_bool", + "offset": 0, + "slot": "2" + }, + { + "label": "canceled", + "type": "t_bool", + "offset": 1, + "slot": "2" + } + ], + "numberOfBytes": "96" + }, + "t_struct(ProposalVote)13630_storage": { + "label": "struct CrossChainGovernorCountingSimple.ProposalVote", + "members": [ + { + "label": "againstVotes", + "type": "t_uint256", + "offset": 0, + "slot": "0" + }, + { + "label": "forVotes", + "type": "t_uint256", + "offset": 0, + "slot": "1" + }, + { + "label": "abstainVotes", + "type": "t_uint256", + "offset": 0, + "slot": "2" + }, + { + "label": "hasVoted", + "type": "t_mapping(t_address,t_bool)", + "offset": 0, + "slot": "3" + } + ], + "numberOfBytes": "128" + }, + "t_struct(SpokeProposalVote)13614_storage": { + "label": "struct CrossChainGovernorCountingSimple.SpokeProposalVote", + "members": [ + { + "label": "forVotes", + "type": "t_uint256", + "offset": 0, + "slot": "0" + }, + { + "label": "againstVotes", + "type": "t_uint256", + "offset": 0, + "slot": "1" + }, + { + "label": "abstainVotes", + "type": "t_uint256", + "offset": 0, + "slot": "2" + }, + { + "label": "initialized", + "type": "t_bool", + "offset": 0, + "slot": "3" + } + ], + "numberOfBytes": "128" + }, + "t_struct(Trace224)5746_storage": { + "label": "struct CheckpointsUpgradeable.Trace224", + "members": [ + { + "label": "_checkpoints", + "type": "t_array(t_struct(Checkpoint224)5751_storage)dyn_storage", + "offset": 0, + "slot": "0" + } + ], + "numberOfBytes": "32" + }, + "t_uint16": { + "label": "uint16", + "numberOfBytes": "2" + }, + "t_uint224": { + "label": "uint224", + "numberOfBytes": "28" + }, + "t_uint256": { + "label": "uint256", + "numberOfBytes": "32" + }, + "t_uint32": { + "label": "uint32", + "numberOfBytes": "4" + }, + "t_uint64": { + "label": "uint64", + "numberOfBytes": "8" + }, + "t_uint8": { + "label": "uint8", + "numberOfBytes": "1" + } + }, + "namespaces": {} + } + }, + "fd661d18c38477f9c943739b80bab1bbfa30389ba9656eee838e7a47a12c63db": { + "address": "0x6325FaaB4E829236843373Bb7Ced43ed8F5Ce6ed", + "txHash": "0x19362ac1e0dfff1066092ef5ee7268d8196b1c8df6bde72f97bcf9e59552f8c6", + "layout": { + "solcVersion": "0.8.23", + "storage": [ + { + "label": "_initialized", + "offset": 0, + "slot": "0", + "type": "t_uint8", + "contract": "Initializable", + "src": "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol:63", + "retypedFrom": "bool" + }, + { + "label": "_initializing", + "offset": 1, + "slot": "0", + "type": "t_bool", + "contract": "Initializable", + "src": "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol:68" + }, + { + "label": "__gap", + "offset": 0, + "slot": "1", + "type": "t_array(t_uint256)50_storage", + "contract": "ContextUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol:40" + }, + { + "label": "__gap", + "offset": 0, + "slot": "51", + "type": "t_array(t_uint256)50_storage", + "contract": "ERC165Upgradeable", + "src": "@openzeppelin/contracts-upgradeable/utils/introspection/ERC165Upgradeable.sol:41" + }, + { + "label": "_hashedName", + "offset": 0, + "slot": "101", + "type": "t_bytes32", + "contract": "EIP712Upgradeable", + "src": "@openzeppelin/contracts-upgradeable/utils/cryptography/EIP712Upgradeable.sol:40", + "renamedFrom": "_HASHED_NAME" + }, + { + "label": "_hashedVersion", + "offset": 0, + "slot": "102", + "type": "t_bytes32", + "contract": "EIP712Upgradeable", + "src": "@openzeppelin/contracts-upgradeable/utils/cryptography/EIP712Upgradeable.sol:42", + "renamedFrom": "_HASHED_VERSION" + }, + { + "label": "_name", + "offset": 0, + "slot": "103", + "type": "t_string_storage", + "contract": "EIP712Upgradeable", + "src": "@openzeppelin/contracts-upgradeable/utils/cryptography/EIP712Upgradeable.sol:44" + }, + { + "label": "_version", + "offset": 0, + "slot": "104", + "type": "t_string_storage", + "contract": "EIP712Upgradeable", + "src": "@openzeppelin/contracts-upgradeable/utils/cryptography/EIP712Upgradeable.sol:45" + }, + { + "label": "__gap", + "offset": 0, + "slot": "105", + "type": "t_array(t_uint256)48_storage", + "contract": "EIP712Upgradeable", + "src": "@openzeppelin/contracts-upgradeable/utils/cryptography/EIP712Upgradeable.sol:204" + }, + { + "label": "__gap", + "offset": 0, + "slot": "153", + "type": "t_array(t_uint256)50_storage", + "contract": "IGovernorUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/governance/IGovernorUpgradeable.sol:325" + }, + { + "label": "__gap", + "offset": 0, + "slot": "203", + "type": "t_array(t_uint256)50_storage", + "contract": "IGovernorTimelockUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/governance/extensions/IGovernorTimelockUpgradeable.sol:38" + }, + { + "label": "_name", + "offset": 0, + "slot": "253", + "type": "t_string_storage", + "contract": "GovernorUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/governance/GovernorUpgradeable.sol:51" + }, + { + "label": "_proposals", + "offset": 0, + "slot": "254", + "type": "t_mapping(t_uint256,t_struct(ProposalCore)600_storage)", + "contract": "GovernorUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/governance/GovernorUpgradeable.sol:54", + "retypedFrom": "mapping(uint256 => Governor.ProposalCore)" + }, + { + "label": "_governanceCall", + "offset": 0, + "slot": "255", + "type": "t_struct(Bytes32Deque)10266_storage", + "contract": "GovernorUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/governance/GovernorUpgradeable.sol:60" + }, + { + "label": "__gap", + "offset": 0, + "slot": "257", + "type": "t_array(t_uint256)46_storage", + "contract": "GovernorUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/governance/GovernorUpgradeable.sol:735" + }, + { + "label": "_votingDelay", + "offset": 0, + "slot": "303", + "type": "t_uint256", + "contract": "GovernorSettingsUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/governance/extensions/GovernorSettingsUpgradeable.sol:15" + }, + { + "label": "_votingPeriod", + "offset": 0, + "slot": "304", + "type": "t_uint256", + "contract": "GovernorSettingsUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/governance/extensions/GovernorSettingsUpgradeable.sol:16" + }, + { + "label": "_proposalThreshold", + "offset": 0, + "slot": "305", + "type": "t_uint256", + "contract": "GovernorSettingsUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/governance/extensions/GovernorSettingsUpgradeable.sol:17" + }, + { + "label": "__gap", + "offset": 0, + "slot": "306", + "type": "t_array(t_uint256)47_storage", + "contract": "GovernorSettingsUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/governance/extensions/GovernorSettingsUpgradeable.sol:121" + }, + { + "label": "_owner", + "offset": 0, + "slot": "353", + "type": "t_address", + "contract": "OwnableUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol:22" + }, + { + "label": "__gap", + "offset": 0, + "slot": "354", + "type": "t_array(t_uint256)49_storage", + "contract": "OwnableUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol:94" + }, + { + "label": "spokeContractsMapping", + "offset": 0, + "slot": "403", + "type": "t_mapping(t_bytes32,t_mapping(t_uint16,t_bool))", + "contract": "CrossChainGovernorCountingSimple", + "src": "contracts/governance/CrossChainGovernorCountingSimple.sol:18" + }, + { + "label": "spokeContracts", + "offset": 0, + "slot": "404", + "type": "t_array(t_struct(CrossChainAddress)13605_storage)dyn_storage", + "contract": "CrossChainGovernorCountingSimple", + "src": "contracts/governance/CrossChainGovernorCountingSimple.sol:19" + }, + { + "label": "spokeContractsMappingSnapshots", + "offset": 0, + "slot": "405", + "type": "t_mapping(t_uint256,t_mapping(t_bytes32,t_mapping(t_uint16,t_bool)))", + "contract": "CrossChainGovernorCountingSimple", + "src": "contracts/governance/CrossChainGovernorCountingSimple.sol:20" + }, + { + "label": "spokeContractsSnapshots", + "offset": 0, + "slot": "406", + "type": "t_mapping(t_uint256,t_array(t_struct(CrossChainAddress)13605_storage)dyn_storage)", + "contract": "CrossChainGovernorCountingSimple", + "src": "contracts/governance/CrossChainGovernorCountingSimple.sol:22" + }, + { + "label": "spokeVotes", + "offset": 0, + "slot": "407", + "type": "t_mapping(t_uint256,t_mapping(t_bytes32,t_mapping(t_uint16,t_struct(SpokeProposalVote)13614_storage)))", + "contract": "CrossChainGovernorCountingSimple", + "src": "contracts/governance/CrossChainGovernorCountingSimple.sol:23" + }, + { + "label": "_proposalVotes", + "offset": 0, + "slot": "408", + "type": "t_mapping(t_uint256,t_struct(ProposalVote)13630_storage)", + "contract": "CrossChainGovernorCountingSimple", + "src": "contracts/governance/CrossChainGovernorCountingSimple.sol:25" + }, + { + "label": "token", + "offset": 0, + "slot": "409", + "type": "t_contract(IERC5805Upgradeable)4632", + "contract": "GovernorVotesUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/governance/extensions/GovernorVotesUpgradeable.sol:18" + }, + { + "label": "__gap", + "offset": 0, + "slot": "410", + "type": "t_array(t_uint256)50_storage", + "contract": "GovernorVotesUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/governance/extensions/GovernorVotesUpgradeable.sol:68" + }, + { + "label": "_quorumNumerator", + "offset": 0, + "slot": "460", + "type": "t_uint256", + "contract": "GovernorVotesQuorumFractionUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/governance/extensions/GovernorVotesQuorumFractionUpgradeable.sol:20" + }, + { + "label": "_quorumNumeratorHistory", + "offset": 0, + "slot": "461", + "type": "t_struct(Trace224)5746_storage", + "contract": "GovernorVotesQuorumFractionUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/governance/extensions/GovernorVotesQuorumFractionUpgradeable.sol:23", + "retypedFrom": "Checkpoints.History" + }, + { + "label": "__gap", + "offset": 0, + "slot": "462", + "type": "t_array(t_uint256)48_storage", + "contract": "GovernorVotesQuorumFractionUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/governance/extensions/GovernorVotesQuorumFractionUpgradeable.sol:132" + }, + { + "label": "_timelock", + "offset": 0, + "slot": "510", + "type": "t_contract(TimelockControllerUpgradeable)3488", + "contract": "GovernorTimelockControlUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/governance/extensions/GovernorTimelockControlUpgradeable.sol:28" + }, + { + "label": "_timelockIds", + "offset": 0, + "slot": "511", + "type": "t_mapping(t_uint256,t_bytes32)", + "contract": "GovernorTimelockControlUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/governance/extensions/GovernorTimelockControlUpgradeable.sol:29" + }, + { + "label": "__gap", + "offset": 0, + "slot": "512", + "type": "t_array(t_uint256)48_storage", + "contract": "GovernorTimelockControlUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/governance/extensions/GovernorTimelockControlUpgradeable.sol:177" + }, + { + "label": "_magistrate", + "offset": 0, + "slot": "560", + "type": "t_address", + "contract": "Magistrate", + "src": "contracts/governance/magistrate/Magistrate.sol:13" + }, + { + "label": "wormholeRelayer", + "offset": 0, + "slot": "561", + "type": "t_contract(IWormholeRelayer)16479", + "contract": "MetaHumanGovernor", + "src": "contracts/governance/MetaHumanGovernor.sol:48" + }, + { + "label": "secondsPerBlock", + "offset": 0, + "slot": "562", + "type": "t_uint256", + "contract": "MetaHumanGovernor", + "src": "contracts/governance/MetaHumanGovernor.sol:50" + }, + { + "label": "chainId", + "offset": 0, + "slot": "563", + "type": "t_uint16", + "contract": "MetaHumanGovernor", + "src": "contracts/governance/MetaHumanGovernor.sol:51" + }, + { + "label": "processedMessages", + "offset": 0, + "slot": "564", + "type": "t_mapping(t_bytes32,t_bool)", + "contract": "MetaHumanGovernor", + "src": "contracts/governance/MetaHumanGovernor.sol:53" + }, + { + "label": "collectionStarted", + "offset": 0, + "slot": "565", + "type": "t_mapping(t_uint256,t_bool)", + "contract": "MetaHumanGovernor", + "src": "contracts/governance/MetaHumanGovernor.sol:54" + }, + { + "label": "collectionFinished", + "offset": 0, + "slot": "566", + "type": "t_mapping(t_uint256,t_bool)", + "contract": "MetaHumanGovernor", + "src": "contracts/governance/MetaHumanGovernor.sol:55" + } + ], + "types": { + "t_address": { + "label": "address", + "numberOfBytes": "20" + }, + "t_array(t_struct(Checkpoint224)5751_storage)dyn_storage": { + "label": "struct CheckpointsUpgradeable.Checkpoint224[]", + "numberOfBytes": "32" + }, + "t_array(t_struct(CrossChainAddress)13605_storage)dyn_storage": { + "label": "struct CrossChainGovernorCountingSimple.CrossChainAddress[]", + "numberOfBytes": "32" + }, + "t_array(t_uint256)46_storage": { + "label": "uint256[46]", + "numberOfBytes": "1472" + }, + "t_array(t_uint256)47_storage": { + "label": "uint256[47]", + "numberOfBytes": "1504" + }, + "t_array(t_uint256)48_storage": { + "label": "uint256[48]", + "numberOfBytes": "1536" + }, + "t_array(t_uint256)49_storage": { + "label": "uint256[49]", + "numberOfBytes": "1568" + }, + "t_array(t_uint256)50_storage": { + "label": "uint256[50]", + "numberOfBytes": "1600" + }, + "t_bool": { + "label": "bool", + "numberOfBytes": "1" + }, + "t_bytes24": { + "label": "bytes24", + "numberOfBytes": "24" + }, + "t_bytes32": { + "label": "bytes32", + "numberOfBytes": "32" + }, + "t_bytes4": { + "label": "bytes4", + "numberOfBytes": "4" + }, + "t_contract(IERC5805Upgradeable)4632": { + "label": "contract IERC5805Upgradeable", + "numberOfBytes": "20" + }, + "t_contract(IWormholeRelayer)16479": { + "label": "contract IWormholeRelayer", + "numberOfBytes": "20" + }, + "t_contract(TimelockControllerUpgradeable)3488": { + "label": "contract TimelockControllerUpgradeable", + "numberOfBytes": "20" + }, + "t_int128": { + "label": "int128", + "numberOfBytes": "16" + }, + "t_mapping(t_address,t_bool)": { + "label": "mapping(address => bool)", + "numberOfBytes": "32" + }, + "t_mapping(t_bytes32,t_bool)": { + "label": "mapping(bytes32 => bool)", + "numberOfBytes": "32" + }, + "t_mapping(t_bytes32,t_mapping(t_uint16,t_bool))": { + "label": "mapping(bytes32 => mapping(uint16 => bool))", + "numberOfBytes": "32" + }, + "t_mapping(t_bytes32,t_mapping(t_uint16,t_struct(SpokeProposalVote)13614_storage))": { + "label": "mapping(bytes32 => mapping(uint16 => struct CrossChainGovernorCountingSimple.SpokeProposalVote))", + "numberOfBytes": "32" + }, + "t_mapping(t_int128,t_bytes32)": { + "label": "mapping(int128 => bytes32)", + "numberOfBytes": "32" + }, + "t_mapping(t_uint16,t_bool)": { + "label": "mapping(uint16 => bool)", + "numberOfBytes": "32" + }, + "t_mapping(t_uint16,t_struct(SpokeProposalVote)13614_storage)": { + "label": "mapping(uint16 => struct CrossChainGovernorCountingSimple.SpokeProposalVote)", + "numberOfBytes": "32" + }, + "t_mapping(t_uint256,t_array(t_struct(CrossChainAddress)13605_storage)dyn_storage)": { + "label": "mapping(uint256 => struct CrossChainGovernorCountingSimple.CrossChainAddress[])", + "numberOfBytes": "32" + }, + "t_mapping(t_uint256,t_bool)": { + "label": "mapping(uint256 => bool)", + "numberOfBytes": "32" + }, + "t_mapping(t_uint256,t_bytes32)": { + "label": "mapping(uint256 => bytes32)", + "numberOfBytes": "32" + }, + "t_mapping(t_uint256,t_mapping(t_bytes32,t_mapping(t_uint16,t_bool)))": { + "label": "mapping(uint256 => mapping(bytes32 => mapping(uint16 => bool)))", + "numberOfBytes": "32" + }, + "t_mapping(t_uint256,t_mapping(t_bytes32,t_mapping(t_uint16,t_struct(SpokeProposalVote)13614_storage)))": { + "label": "mapping(uint256 => mapping(bytes32 => mapping(uint16 => struct CrossChainGovernorCountingSimple.SpokeProposalVote)))", + "numberOfBytes": "32" + }, + "t_mapping(t_uint256,t_struct(ProposalCore)600_storage)": { + "label": "mapping(uint256 => struct GovernorUpgradeable.ProposalCore)", + "numberOfBytes": "32" + }, + "t_mapping(t_uint256,t_struct(ProposalVote)13630_storage)": { + "label": "mapping(uint256 => struct CrossChainGovernorCountingSimple.ProposalVote)", + "numberOfBytes": "32" + }, + "t_string_storage": { + "label": "string", + "numberOfBytes": "32" + }, + "t_struct(Bytes32Deque)10266_storage": { + "label": "struct DoubleEndedQueueUpgradeable.Bytes32Deque", + "members": [ + { + "label": "_begin", + "type": "t_int128", + "offset": 0, + "slot": "0" + }, + { + "label": "_end", + "type": "t_int128", + "offset": 16, + "slot": "0" + }, + { + "label": "_data", + "type": "t_mapping(t_int128,t_bytes32)", + "offset": 0, + "slot": "1" + } + ], + "numberOfBytes": "64" + }, + "t_struct(Checkpoint224)5751_storage": { + "label": "struct CheckpointsUpgradeable.Checkpoint224", + "members": [ + { + "label": "_key", + "type": "t_uint32", + "offset": 0, + "slot": "0" + }, + { + "label": "_value", + "type": "t_uint224", + "offset": 4, + "slot": "0" + } + ], + "numberOfBytes": "32" + }, + "t_struct(CrossChainAddress)13605_storage": { + "label": "struct CrossChainGovernorCountingSimple.CrossChainAddress", + "members": [ + { + "label": "contractAddress", + "type": "t_bytes32", + "offset": 0, + "slot": "0" + }, + { + "label": "chainId", + "type": "t_uint16", + "offset": 0, + "slot": "1" + } + ], + "numberOfBytes": "64" + }, + "t_struct(ProposalCore)600_storage": { + "label": "struct GovernorUpgradeable.ProposalCore", + "members": [ + { + "label": "voteStart", + "type": "t_uint64", + "offset": 0, + "slot": "0" + }, + { + "label": "proposer", + "type": "t_address", + "offset": 8, + "slot": "0" + }, + { + "label": "__gap_unused0", + "type": "t_bytes4", + "offset": 28, + "slot": "0" + }, + { + "label": "voteEnd", + "type": "t_uint64", + "offset": 0, + "slot": "1" + }, + { + "label": "__gap_unused1", + "type": "t_bytes24", + "offset": 8, + "slot": "1" + }, + { + "label": "executed", + "type": "t_bool", + "offset": 0, + "slot": "2" + }, + { + "label": "canceled", + "type": "t_bool", + "offset": 1, + "slot": "2" + } + ], + "numberOfBytes": "96" + }, + "t_struct(ProposalVote)13630_storage": { + "label": "struct CrossChainGovernorCountingSimple.ProposalVote", + "members": [ + { + "label": "againstVotes", + "type": "t_uint256", + "offset": 0, + "slot": "0" + }, + { + "label": "forVotes", + "type": "t_uint256", + "offset": 0, + "slot": "1" + }, + { + "label": "abstainVotes", + "type": "t_uint256", + "offset": 0, + "slot": "2" + }, + { + "label": "hasVoted", + "type": "t_mapping(t_address,t_bool)", + "offset": 0, + "slot": "3" + } + ], + "numberOfBytes": "128" + }, + "t_struct(SpokeProposalVote)13614_storage": { + "label": "struct CrossChainGovernorCountingSimple.SpokeProposalVote", + "members": [ + { + "label": "forVotes", + "type": "t_uint256", + "offset": 0, + "slot": "0" + }, + { + "label": "againstVotes", + "type": "t_uint256", + "offset": 0, + "slot": "1" + }, + { + "label": "abstainVotes", + "type": "t_uint256", + "offset": 0, + "slot": "2" + }, + { + "label": "initialized", + "type": "t_bool", + "offset": 0, + "slot": "3" + } + ], + "numberOfBytes": "128" + }, + "t_struct(Trace224)5746_storage": { + "label": "struct CheckpointsUpgradeable.Trace224", + "members": [ + { + "label": "_checkpoints", + "type": "t_array(t_struct(Checkpoint224)5751_storage)dyn_storage", + "offset": 0, + "slot": "0" + } + ], + "numberOfBytes": "32" + }, + "t_uint16": { + "label": "uint16", + "numberOfBytes": "2" + }, + "t_uint224": { + "label": "uint224", + "numberOfBytes": "28" + }, + "t_uint256": { + "label": "uint256", + "numberOfBytes": "32" + }, + "t_uint32": { + "label": "uint32", + "numberOfBytes": "4" + }, + "t_uint64": { + "label": "uint64", + "numberOfBytes": "8" + }, + "t_uint8": { + "label": "uint8", + "numberOfBytes": "1" + } + }, + "namespaces": {} + } + }, + "4ce58128162dd4657e8191cb81f01bcd8d412ac24cacecafa126c9609d51b344": { + "address": "0x855eed71B17052d0DF1786cc47EA35BDFF5Cad43", + "txHash": "0x0389b877ad9087d81bd9604eee14833e2f311c9c24bf2d37f22dd5d1d59a99d1", + "layout": { + "solcVersion": "0.8.23", + "storage": [ + { + "label": "_initialized", + "offset": 0, + "slot": "0", + "type": "t_uint8", + "contract": "Initializable", + "src": "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol:63", + "retypedFrom": "bool" + }, + { + "label": "_initializing", + "offset": 1, + "slot": "0", + "type": "t_bool", + "contract": "Initializable", + "src": "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol:68" + }, + { + "label": "__gap", + "offset": 0, + "slot": "1", + "type": "t_array(t_uint256)50_storage", + "contract": "ContextUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol:40" + }, + { + "label": "__gap", + "offset": 0, + "slot": "51", + "type": "t_array(t_uint256)50_storage", + "contract": "ERC165Upgradeable", + "src": "@openzeppelin/contracts-upgradeable/utils/introspection/ERC165Upgradeable.sol:41" + }, + { + "label": "_hashedName", + "offset": 0, + "slot": "101", + "type": "t_bytes32", + "contract": "EIP712Upgradeable", + "src": "@openzeppelin/contracts-upgradeable/utils/cryptography/EIP712Upgradeable.sol:40", + "renamedFrom": "_HASHED_NAME" + }, + { + "label": "_hashedVersion", + "offset": 0, + "slot": "102", + "type": "t_bytes32", + "contract": "EIP712Upgradeable", + "src": "@openzeppelin/contracts-upgradeable/utils/cryptography/EIP712Upgradeable.sol:42", + "renamedFrom": "_HASHED_VERSION" + }, + { + "label": "_name", + "offset": 0, + "slot": "103", + "type": "t_string_storage", + "contract": "EIP712Upgradeable", + "src": "@openzeppelin/contracts-upgradeable/utils/cryptography/EIP712Upgradeable.sol:44" + }, + { + "label": "_version", + "offset": 0, + "slot": "104", + "type": "t_string_storage", + "contract": "EIP712Upgradeable", + "src": "@openzeppelin/contracts-upgradeable/utils/cryptography/EIP712Upgradeable.sol:45" + }, + { + "label": "__gap", + "offset": 0, + "slot": "105", + "type": "t_array(t_uint256)48_storage", + "contract": "EIP712Upgradeable", + "src": "@openzeppelin/contracts-upgradeable/utils/cryptography/EIP712Upgradeable.sol:204" + }, + { + "label": "__gap", + "offset": 0, + "slot": "153", + "type": "t_array(t_uint256)50_storage", + "contract": "IGovernorUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/governance/IGovernorUpgradeable.sol:325" + }, + { + "label": "__gap", + "offset": 0, + "slot": "203", + "type": "t_array(t_uint256)50_storage", + "contract": "IGovernorTimelockUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/governance/extensions/IGovernorTimelockUpgradeable.sol:38" + }, + { + "label": "_name", + "offset": 0, + "slot": "253", + "type": "t_string_storage", + "contract": "GovernorUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/governance/GovernorUpgradeable.sol:51" + }, + { + "label": "_proposals", + "offset": 0, + "slot": "254", + "type": "t_mapping(t_uint256,t_struct(ProposalCore)600_storage)", + "contract": "GovernorUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/governance/GovernorUpgradeable.sol:54", + "retypedFrom": "mapping(uint256 => Governor.ProposalCore)" + }, + { + "label": "_governanceCall", + "offset": 0, + "slot": "255", + "type": "t_struct(Bytes32Deque)10879_storage", + "contract": "GovernorUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/governance/GovernorUpgradeable.sol:60" + }, + { + "label": "__gap", + "offset": 0, + "slot": "257", + "type": "t_array(t_uint256)46_storage", + "contract": "GovernorUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/governance/GovernorUpgradeable.sol:735" + }, + { + "label": "_votingDelay", + "offset": 0, + "slot": "303", + "type": "t_uint256", + "contract": "GovernorSettingsUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/governance/extensions/GovernorSettingsUpgradeable.sol:15" + }, + { + "label": "_votingPeriod", + "offset": 0, + "slot": "304", + "type": "t_uint256", + "contract": "GovernorSettingsUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/governance/extensions/GovernorSettingsUpgradeable.sol:16" + }, + { + "label": "_proposalThreshold", + "offset": 0, + "slot": "305", + "type": "t_uint256", + "contract": "GovernorSettingsUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/governance/extensions/GovernorSettingsUpgradeable.sol:17" + }, + { + "label": "__gap", + "offset": 0, + "slot": "306", + "type": "t_array(t_uint256)47_storage", + "contract": "GovernorSettingsUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/governance/extensions/GovernorSettingsUpgradeable.sol:121" + }, + { + "label": "_owner", + "offset": 0, + "slot": "353", + "type": "t_address", + "contract": "OwnableUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol:22" + }, + { + "label": "__gap", + "offset": 0, + "slot": "354", + "type": "t_array(t_uint256)49_storage", + "contract": "OwnableUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol:94" + }, + { + "label": "spokeContractsMapping", + "offset": 0, + "slot": "403", + "type": "t_mapping(t_bytes32,t_mapping(t_uint16,t_bool))", + "contract": "CrossChainGovernorCountingSimple", + "src": "contracts/governance/CrossChainGovernorCountingSimple.sol:18" + }, + { + "label": "spokeContracts", + "offset": 0, + "slot": "404", + "type": "t_array(t_struct(CrossChainAddress)23540_storage)dyn_storage", + "contract": "CrossChainGovernorCountingSimple", + "src": "contracts/governance/CrossChainGovernorCountingSimple.sol:19" + }, + { + "label": "spokeContractsMappingSnapshots", + "offset": 0, + "slot": "405", + "type": "t_mapping(t_uint256,t_mapping(t_bytes32,t_mapping(t_uint16,t_bool)))", + "contract": "CrossChainGovernorCountingSimple", + "src": "contracts/governance/CrossChainGovernorCountingSimple.sol:20" + }, + { + "label": "spokeContractsSnapshots", + "offset": 0, + "slot": "406", + "type": "t_mapping(t_uint256,t_array(t_struct(CrossChainAddress)23540_storage)dyn_storage)", + "contract": "CrossChainGovernorCountingSimple", + "src": "contracts/governance/CrossChainGovernorCountingSimple.sol:22" + }, + { + "label": "spokeVotes", + "offset": 0, + "slot": "407", + "type": "t_mapping(t_uint256,t_mapping(t_bytes32,t_mapping(t_uint16,t_struct(SpokeProposalVote)23549_storage)))", + "contract": "CrossChainGovernorCountingSimple", + "src": "contracts/governance/CrossChainGovernorCountingSimple.sol:23" + }, + { + "label": "_proposalVotes", + "offset": 0, + "slot": "408", + "type": "t_mapping(t_uint256,t_struct(ProposalVote)23565_storage)", + "contract": "CrossChainGovernorCountingSimple", + "src": "contracts/governance/CrossChainGovernorCountingSimple.sol:25" + }, + { + "label": "token", + "offset": 0, + "slot": "409", + "type": "t_contract(IERC5805Upgradeable)4653", + "contract": "GovernorVotesUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/governance/extensions/GovernorVotesUpgradeable.sol:18" + }, + { + "label": "__gap", + "offset": 0, + "slot": "410", + "type": "t_array(t_uint256)50_storage", + "contract": "GovernorVotesUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/governance/extensions/GovernorVotesUpgradeable.sol:68" + }, + { + "label": "_quorumNumerator", + "offset": 0, + "slot": "460", + "type": "t_uint256", + "contract": "GovernorVotesQuorumFractionUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/governance/extensions/GovernorVotesQuorumFractionUpgradeable.sol:20" + }, + { + "label": "_quorumNumeratorHistory", + "offset": 0, + "slot": "461", + "type": "t_struct(Trace224)6249_storage", + "contract": "GovernorVotesQuorumFractionUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/governance/extensions/GovernorVotesQuorumFractionUpgradeable.sol:23", + "retypedFrom": "Checkpoints.History" + }, + { + "label": "__gap", + "offset": 0, + "slot": "462", + "type": "t_array(t_uint256)48_storage", + "contract": "GovernorVotesQuorumFractionUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/governance/extensions/GovernorVotesQuorumFractionUpgradeable.sol:132" + }, + { + "label": "_timelock", + "offset": 0, + "slot": "510", + "type": "t_contract(TimelockControllerUpgradeable)3488", + "contract": "GovernorTimelockControlUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/governance/extensions/GovernorTimelockControlUpgradeable.sol:28" + }, + { + "label": "_timelockIds", + "offset": 0, + "slot": "511", + "type": "t_mapping(t_uint256,t_bytes32)", + "contract": "GovernorTimelockControlUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/governance/extensions/GovernorTimelockControlUpgradeable.sol:29" + }, + { + "label": "__gap", + "offset": 0, + "slot": "512", + "type": "t_array(t_uint256)48_storage", + "contract": "GovernorTimelockControlUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/governance/extensions/GovernorTimelockControlUpgradeable.sol:177" + }, + { + "label": "_magistrate", + "offset": 0, + "slot": "560", + "type": "t_address", + "contract": "Magistrate", + "src": "contracts/governance/magistrate/Magistrate.sol:13" + }, + { + "label": "wormholeRelayer", + "offset": 0, + "slot": "561", + "type": "t_contract(IWormholeRelayer)26845", + "contract": "MetaHumanGovernor", + "src": "contracts/governance/MetaHumanGovernor.sol:48" + }, + { + "label": "secondsPerBlock", + "offset": 0, + "slot": "562", + "type": "t_uint256", + "contract": "MetaHumanGovernor", + "src": "contracts/governance/MetaHumanGovernor.sol:50" + }, + { + "label": "chainId", + "offset": 0, + "slot": "563", + "type": "t_uint16", + "contract": "MetaHumanGovernor", + "src": "contracts/governance/MetaHumanGovernor.sol:51" + }, + { + "label": "processedMessages", + "offset": 0, + "slot": "564", + "type": "t_mapping(t_bytes32,t_bool)", + "contract": "MetaHumanGovernor", + "src": "contracts/governance/MetaHumanGovernor.sol:53" + }, + { + "label": "collectionStarted", + "offset": 0, + "slot": "565", + "type": "t_mapping(t_uint256,t_bool)", + "contract": "MetaHumanGovernor", + "src": "contracts/governance/MetaHumanGovernor.sol:54" + }, + { + "label": "collectionFinished", + "offset": 0, + "slot": "566", + "type": "t_mapping(t_uint256,t_bool)", + "contract": "MetaHumanGovernor", + "src": "contracts/governance/MetaHumanGovernor.sol:55" + }, + { + "label": "__gap", + "offset": 0, + "slot": "567", + "type": "t_array(t_uint256)50_storage", + "contract": "MetaHumanGovernor", + "src": "contracts/governance/MetaHumanGovernor.sol:57" + } + ], + "types": { + "t_address": { + "label": "address", + "numberOfBytes": "20" + }, + "t_array(t_struct(Checkpoint224)6254_storage)dyn_storage": { + "label": "struct CheckpointsUpgradeable.Checkpoint224[]", + "numberOfBytes": "32" + }, + "t_array(t_struct(CrossChainAddress)23540_storage)dyn_storage": { + "label": "struct CrossChainGovernorCountingSimple.CrossChainAddress[]", + "numberOfBytes": "32" + }, + "t_array(t_uint256)46_storage": { + "label": "uint256[46]", + "numberOfBytes": "1472" + }, + "t_array(t_uint256)47_storage": { + "label": "uint256[47]", + "numberOfBytes": "1504" + }, + "t_array(t_uint256)48_storage": { + "label": "uint256[48]", + "numberOfBytes": "1536" + }, + "t_array(t_uint256)49_storage": { + "label": "uint256[49]", + "numberOfBytes": "1568" + }, + "t_array(t_uint256)50_storage": { + "label": "uint256[50]", + "numberOfBytes": "1600" + }, + "t_bool": { + "label": "bool", + "numberOfBytes": "1" + }, + "t_bytes24": { + "label": "bytes24", + "numberOfBytes": "24" + }, + "t_bytes32": { + "label": "bytes32", + "numberOfBytes": "32" + }, + "t_bytes4": { + "label": "bytes4", + "numberOfBytes": "4" + }, + "t_contract(IERC5805Upgradeable)4653": { + "label": "contract IERC5805Upgradeable", + "numberOfBytes": "20" + }, + "t_contract(IWormholeRelayer)26845": { + "label": "contract IWormholeRelayer", + "numberOfBytes": "20" + }, + "t_contract(TimelockControllerUpgradeable)3488": { + "label": "contract TimelockControllerUpgradeable", + "numberOfBytes": "20" + }, + "t_int128": { + "label": "int128", + "numberOfBytes": "16" + }, + "t_mapping(t_address,t_bool)": { + "label": "mapping(address => bool)", + "numberOfBytes": "32" + }, + "t_mapping(t_bytes32,t_bool)": { + "label": "mapping(bytes32 => bool)", + "numberOfBytes": "32" + }, + "t_mapping(t_bytes32,t_mapping(t_uint16,t_bool))": { + "label": "mapping(bytes32 => mapping(uint16 => bool))", + "numberOfBytes": "32" + }, + "t_mapping(t_bytes32,t_mapping(t_uint16,t_struct(SpokeProposalVote)23549_storage))": { + "label": "mapping(bytes32 => mapping(uint16 => struct CrossChainGovernorCountingSimple.SpokeProposalVote))", + "numberOfBytes": "32" + }, + "t_mapping(t_int128,t_bytes32)": { + "label": "mapping(int128 => bytes32)", + "numberOfBytes": "32" + }, + "t_mapping(t_uint16,t_bool)": { + "label": "mapping(uint16 => bool)", + "numberOfBytes": "32" + }, + "t_mapping(t_uint16,t_struct(SpokeProposalVote)23549_storage)": { + "label": "mapping(uint16 => struct CrossChainGovernorCountingSimple.SpokeProposalVote)", + "numberOfBytes": "32" + }, + "t_mapping(t_uint256,t_array(t_struct(CrossChainAddress)23540_storage)dyn_storage)": { + "label": "mapping(uint256 => struct CrossChainGovernorCountingSimple.CrossChainAddress[])", + "numberOfBytes": "32" + }, + "t_mapping(t_uint256,t_bool)": { + "label": "mapping(uint256 => bool)", + "numberOfBytes": "32" + }, + "t_mapping(t_uint256,t_bytes32)": { + "label": "mapping(uint256 => bytes32)", + "numberOfBytes": "32" + }, + "t_mapping(t_uint256,t_mapping(t_bytes32,t_mapping(t_uint16,t_bool)))": { + "label": "mapping(uint256 => mapping(bytes32 => mapping(uint16 => bool)))", + "numberOfBytes": "32" + }, + "t_mapping(t_uint256,t_mapping(t_bytes32,t_mapping(t_uint16,t_struct(SpokeProposalVote)23549_storage)))": { + "label": "mapping(uint256 => mapping(bytes32 => mapping(uint16 => struct CrossChainGovernorCountingSimple.SpokeProposalVote)))", + "numberOfBytes": "32" + }, + "t_mapping(t_uint256,t_struct(ProposalCore)600_storage)": { + "label": "mapping(uint256 => struct GovernorUpgradeable.ProposalCore)", + "numberOfBytes": "32" + }, + "t_mapping(t_uint256,t_struct(ProposalVote)23565_storage)": { + "label": "mapping(uint256 => struct CrossChainGovernorCountingSimple.ProposalVote)", + "numberOfBytes": "32" + }, + "t_string_storage": { + "label": "string", + "numberOfBytes": "32" + }, + "t_struct(Bytes32Deque)10879_storage": { + "label": "struct DoubleEndedQueueUpgradeable.Bytes32Deque", + "members": [ + { + "label": "_begin", + "type": "t_int128", + "offset": 0, + "slot": "0" + }, + { + "label": "_end", + "type": "t_int128", + "offset": 16, + "slot": "0" + }, + { + "label": "_data", + "type": "t_mapping(t_int128,t_bytes32)", + "offset": 0, + "slot": "1" + } + ], + "numberOfBytes": "64" + }, + "t_struct(Checkpoint224)6254_storage": { + "label": "struct CheckpointsUpgradeable.Checkpoint224", + "members": [ + { + "label": "_key", + "type": "t_uint32", + "offset": 0, + "slot": "0" + }, + { + "label": "_value", + "type": "t_uint224", + "offset": 4, + "slot": "0" + } + ], + "numberOfBytes": "32" + }, + "t_struct(CrossChainAddress)23540_storage": { + "label": "struct CrossChainGovernorCountingSimple.CrossChainAddress", + "members": [ + { + "label": "contractAddress", + "type": "t_bytes32", + "offset": 0, + "slot": "0" + }, + { + "label": "chainId", + "type": "t_uint16", + "offset": 0, + "slot": "1" + } + ], + "numberOfBytes": "64" + }, + "t_struct(ProposalCore)600_storage": { + "label": "struct GovernorUpgradeable.ProposalCore", + "members": [ + { + "label": "voteStart", + "type": "t_uint64", + "offset": 0, + "slot": "0" + }, + { + "label": "proposer", + "type": "t_address", + "offset": 8, + "slot": "0" + }, + { + "label": "__gap_unused0", + "type": "t_bytes4", + "offset": 28, + "slot": "0" + }, + { + "label": "voteEnd", + "type": "t_uint64", + "offset": 0, + "slot": "1" + }, + { + "label": "__gap_unused1", + "type": "t_bytes24", + "offset": 8, + "slot": "1" + }, + { + "label": "executed", + "type": "t_bool", + "offset": 0, + "slot": "2" + }, + { + "label": "canceled", + "type": "t_bool", + "offset": 1, + "slot": "2" + } + ], + "numberOfBytes": "96" + }, + "t_struct(ProposalVote)23565_storage": { + "label": "struct CrossChainGovernorCountingSimple.ProposalVote", + "members": [ + { + "label": "againstVotes", + "type": "t_uint256", + "offset": 0, + "slot": "0" + }, + { + "label": "forVotes", + "type": "t_uint256", + "offset": 0, + "slot": "1" + }, + { + "label": "abstainVotes", + "type": "t_uint256", + "offset": 0, + "slot": "2" + }, + { + "label": "hasVoted", + "type": "t_mapping(t_address,t_bool)", + "offset": 0, + "slot": "3" + } + ], + "numberOfBytes": "128" + }, + "t_struct(SpokeProposalVote)23549_storage": { + "label": "struct CrossChainGovernorCountingSimple.SpokeProposalVote", + "members": [ + { + "label": "forVotes", + "type": "t_uint256", + "offset": 0, + "slot": "0" + }, + { + "label": "againstVotes", + "type": "t_uint256", + "offset": 0, + "slot": "1" + }, + { + "label": "abstainVotes", + "type": "t_uint256", + "offset": 0, + "slot": "2" + }, + { + "label": "initialized", + "type": "t_bool", + "offset": 0, + "slot": "3" + } + ], + "numberOfBytes": "128" + }, + "t_struct(Trace224)6249_storage": { + "label": "struct CheckpointsUpgradeable.Trace224", + "members": [ + { + "label": "_checkpoints", + "type": "t_array(t_struct(Checkpoint224)6254_storage)dyn_storage", + "offset": 0, + "slot": "0" + } + ], + "numberOfBytes": "32" + }, + "t_uint16": { + "label": "uint16", + "numberOfBytes": "2" + }, + "t_uint224": { + "label": "uint224", + "numberOfBytes": "28" + }, + "t_uint256": { + "label": "uint256", + "numberOfBytes": "32" + }, + "t_uint32": { + "label": "uint32", + "numberOfBytes": "4" + }, + "t_uint64": { + "label": "uint64", + "numberOfBytes": "8" + }, + "t_uint8": { + "label": "uint8", + "numberOfBytes": "1" + } + }, + "namespaces": {} + } } } } diff --git a/packages/core/contracts/governance/CrossChainGovernorCountingSimple.sol b/packages/core/contracts/governance/CrossChainGovernorCountingSimple.sol index d251222f05..214a7de3ed 100644 --- a/packages/core/contracts/governance/CrossChainGovernorCountingSimple.sol +++ b/packages/core/contracts/governance/CrossChainGovernorCountingSimple.sol @@ -1,15 +1,20 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.9; -import '@openzeppelin/contracts/governance/Governor.sol'; -import '@openzeppelin/contracts/access/Ownable.sol'; +import '@openzeppelin/contracts-upgradeable/governance/GovernorUpgradeable.sol'; +import '@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol'; +import '@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol'; /** * @title CrossChainGovernorCountingSimple * @dev CrossChainGovernorCountingSimple is an abstract contract that provides counting and vote functionality for a cross-chain governor. * It extends the Governor and Ownable contracts. */ -abstract contract CrossChainGovernorCountingSimple is Governor, Ownable { +abstract contract CrossChainGovernorCountingSimple is + Initializable, + GovernorUpgradeable, + OwnableUpgradeable +{ mapping(bytes32 => mapping(uint16 => bool)) public spokeContractsMapping; CrossChainAddress[] public spokeContracts; mapping(uint256 => mapping(bytes32 => mapping(uint16 => bool))) @@ -49,9 +54,16 @@ abstract contract CrossChainGovernorCountingSimple is Governor, Ownable { event SpokesUpdated(CrossChainAddress[] indexed spokes); - constructor( + function __CrossChainGovernorCountingSimple_init( CrossChainAddress[] memory _spokeContracts - ) Ownable(msg.sender) { + ) internal onlyInitializing { + __Ownable_init(); + __CrossChainGovernorCountingSimple_init_unchained(_spokeContracts); + } + + function __CrossChainGovernorCountingSimple_init_unchained( + CrossChainAddress[] memory _spokeContracts + ) internal onlyInitializing { updateSpokeContracts(_spokeContracts); } diff --git a/packages/core/contracts/governance/DAOSpokeContract.sol b/packages/core/contracts/governance/DAOSpokeContract.sol index de9f6601cb..54d33ecd45 100644 --- a/packages/core/contracts/governance/DAOSpokeContract.sol +++ b/packages/core/contracts/governance/DAOSpokeContract.sol @@ -17,6 +17,20 @@ import './magistrate/Magistrate.sol'; */ contract DAOSpokeContract is IWormholeReceiver, Magistrate { using Address for address payable; + error NotStartedVote(); + error VoteFinished(); + error VoteNotActive(); + error VoteAlreadyCast(); + error InvalidVoteType(uint8 support); + error OnlyRelayerAllowed(); + error OnlyMessagesFromHub(); + error MessageAlreadyProcessed(); + error InvalidIntendedRecipient(); + error ProposalIdMustBeUnique(); + error VoteNotFinished(); + error RelaySendFailed(); + error ZeroBalance(); + bytes32 public immutable hubContractAddress; uint16 public immutable hubContractChainId; IVotes public immutable token; @@ -67,6 +81,7 @@ contract DAOSpokeContract is IWormholeReceiver, Magistrate { * @param _targetSecondsPerBlock The target number of seconds per block for block estimation. * @param _chainId The chain ID of the current contract. * @param _wormholeRelayerAddress The address of the wormhole automatic relayer contract used for cross-chain communication. + * @param _magistrateAddress The initial magistrate address. */ constructor( bytes32 _hubContractAddress, @@ -76,7 +91,8 @@ contract DAOSpokeContract is IWormholeReceiver, Magistrate { uint16 _chainId, address _wormholeRelayerAddress, address _magistrateAddress - ) Magistrate(_magistrateAddress) { + ) { + _transferMagistrate(_magistrateAddress); token = _token; targetSecondsPerBlock = _targetSecondsPerBlock; chainId = _chainId; @@ -112,13 +128,12 @@ contract DAOSpokeContract is IWormholeReceiver, Magistrate { uint8 support ) public virtual returns (uint256) { RemoteProposal storage proposal = proposals[proposalId]; - require(isProposal(proposalId), 'DAOSpokeContract: not a started vote'); - require(!proposal.voteFinished, 'DAOSpokeContract: vote finished'); - require( - block.timestamp >= proposal.localVoteStart && - block.timestamp < proposal.localVoteEnd, - 'DAOSpokeContract: vote not currently active' - ); + if (!isProposal(proposalId)) revert NotStartedVote(); + if (proposal.voteFinished) revert VoteFinished(); + if ( + !(block.timestamp >= proposal.localVoteStart && + block.timestamp < proposal.localVoteEnd) + ) revert VoteNotActive(); uint256 weight = token.getPastVotes( msg.sender, @@ -146,10 +161,7 @@ contract DAOSpokeContract is IWormholeReceiver, Magistrate { ) internal virtual { ProposalVote storage proposalVote = proposalVotes[proposalId]; - require( - !proposalVote.hasVoted[account], - 'DAOSpokeContract: vote already cast' - ); + if (proposalVote.hasVoted[account]) revert VoteAlreadyCast(); proposalVote.hasVoted[account] = true; if (support == uint8(VoteType.Against)) { @@ -159,7 +171,7 @@ contract DAOSpokeContract is IWormholeReceiver, Magistrate { } else if (support == uint8(VoteType.Abstain)) { proposalVote.abstainVotes += weight; } else { - revert('DAOSpokeContract: invalid value for enum VoteType'); + revert InvalidVoteType(support); } } @@ -188,6 +200,45 @@ contract DAOSpokeContract is IWormholeReceiver, Magistrate { return estimatedBlock; } + /** + * @dev Sends the vote result of a proposal to the hub contract. + * @param proposalId The ID of the proposal. + * @return A boolean indicating whether the message was sent successfully. + */ + function _sendVoteResultToHub(uint256 proposalId) internal returns (bool) { + ProposalVote storage votes = proposalVotes[proposalId]; + bytes memory messageToSend = abi.encode( + 0, + proposalId, + votes.forVotes, + votes.againstVotes, + votes.abstainVotes + ); + bytes memory payloadToSend = abi.encode( + hubContractAddress, + hubContractChainId, + msg.sender, + messageToSend + ); + + uint256 cost = quoteCrossChainMessage(hubContractChainId); + try + wormholeRelayer.sendPayloadToEvm{value: cost}( + hubContractChainId, + address(uint160(uint256(hubContractAddress))), + payloadToSend, + 0, + GAS_LIMIT, + hubContractChainId, + magistrate() + ) + { + return true; + } catch { + return false; + } + } + /** * @dev Receives messages from the Wormhole protocol's relay mechanism and processes them accordingly. * This function is intended to be called only by the designated Wormhole relayer. @@ -199,19 +250,18 @@ contract DAOSpokeContract is IWormholeReceiver, Magistrate { function receiveWormholeMessages( bytes memory payload, bytes[] memory, // additionalVaas - bytes32 sourceAddress, // address that called 'sendPayloadToEvm' (Hub contract address) + bytes32 sourceAddress, uint16 sourceChain, - bytes32 deliveryHash // this can be stored in a mapping deliveryHash => bool to prevent duplicate deliveries + bytes32 deliveryHash ) public payable override { - require(msg.sender == address(wormholeRelayer), 'Only relayer allowed'); + if (msg.sender != address(wormholeRelayer)) revert OnlyRelayerAllowed(); - require( - hubContractAddress == sourceAddress && - hubContractChainId == sourceChain, - 'Only messages from the hub contract can be received!' - ); + if ( + !(hubContractAddress == sourceAddress && + hubContractChainId == sourceChain) + ) revert OnlyMessagesFromHub(); - require(!processedMessages[deliveryHash], 'Message already processed'); + if (processedMessages[deliveryHash]) revert MessageAlreadyProcessed(); ( address intendedRecipient, //chainId //sender @@ -220,10 +270,8 @@ contract DAOSpokeContract is IWormholeReceiver, Magistrate { bytes memory decodedMessage ) = abi.decode(payload, (address, uint16, address, bytes)); - require( - intendedRecipient == address(this), - 'Message is not addressed for this contract' - ); + if (intendedRecipient != address(this)) + revert InvalidIntendedRecipient(); processedMessages[deliveryHash] = true; @@ -245,7 +293,7 @@ contract DAOSpokeContract is IWormholeReceiver, Magistrate { decodedMessage, (uint16, uint256, uint256, uint256, uint256) ); - require(!isProposal(proposalId), 'Proposal ID must be unique.'); + if (isProposal(proposalId)) revert ProposalIdMustBeUnique(); proposals[proposalId] = RemoteProposal( proposalCreationTimestamp, @@ -255,82 +303,34 @@ contract DAOSpokeContract is IWormholeReceiver, Magistrate { false ); } else if (option == 1) { - // Send vote results back to the hub chain (, uint256 proposalId) = abi.decode( decodedMessage, (uint16, uint256) ); - ProposalVote storage votes = proposalVotes[proposalId]; - bytes memory messageToSend = abi.encode( - 0, - proposalId, - votes.forVotes, - votes.againstVotes, - votes.abstainVotes - ); - bytes memory payloadToSend = abi.encode( - hubContractAddress, - hubContractChainId, - msg.sender, - messageToSend - ); - - // Send a message to other contracts - // Cost of requesting a message to be sent to - // chain 'targetChain' with a gasLimit of 'GAS_LIMIT' - uint256 cost = quoteCrossChainMessage(hubContractChainId); proposals[proposalId].voteFinished = true; - wormholeRelayer.sendPayloadToEvm{value: cost}( - hubContractChainId, - address(uint160(uint256(hubContractAddress))), - payloadToSend, - 0, // no receiver value needed - GAS_LIMIT, - hubContractChainId, - magistrate() - ); + _sendVoteResultToHub(proposalId); } } function sendVoteResultToHub( uint256 proposalId ) public payable onlyMagistrate { - require( - proposals[proposalId].voteFinished, - 'DAOSpokeContract: vote is not finished' - ); - - ProposalVote storage votes = proposalVotes[proposalId]; - bytes memory messageToSend = abi.encode( - 0, - proposalId, - votes.forVotes, - votes.againstVotes, - votes.abstainVotes - ); - bytes memory payloadToSend = abi.encode( - hubContractAddress, - hubContractChainId, - msg.sender, - messageToSend - ); + if (!proposals[proposalId].voteFinished) revert VoteNotFinished(); - // Send a message to other contracts - // Cost of requesting a message to be sent to - // chain 'targetChain' with a gasLimit of 'GAS_LIMIT' - uint256 cost = quoteCrossChainMessage(hubContractChainId); + bool ok = _sendVoteResultToHub(proposalId); + if (!ok) revert RelaySendFailed(); + } - wormholeRelayer.sendPayloadToEvm{value: cost}( - hubContractChainId, - address(uint160(uint256(hubContractAddress))), - payloadToSend, - 0, // no receiver value needed - GAS_LIMIT, - hubContractChainId, - magistrate() - ); + /** + * @dev Withdraws the contract's balance to the magistrate address. + * Can only be called by the magistrate. + */ + function withdraw() external onlyMagistrate { + uint256 balance = address(this).balance; + if (balance == 0) revert ZeroBalance(); + payable(msg.sender).sendValue(balance); } /** @@ -346,4 +346,6 @@ contract DAOSpokeContract is IWormholeReceiver, Magistrate { GAS_LIMIT ); } + + receive() external payable {} } diff --git a/packages/core/contracts/governance/MetaHumanGovernor.sol b/packages/core/contracts/governance/MetaHumanGovernor.sol index 43fd1ee495..35dd58de45 100644 --- a/packages/core/contracts/governance/MetaHumanGovernor.sol +++ b/packages/core/contracts/governance/MetaHumanGovernor.sol @@ -1,14 +1,13 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.9; -import '@openzeppelin/contracts/governance/Governor.sol'; -import '@openzeppelin/contracts/governance/extensions/GovernorSettings.sol'; -import '@openzeppelin/contracts/governance/extensions/GovernorVotes.sol'; -import '@openzeppelin/contracts/governance/extensions/GovernorVotesQuorumFraction.sol'; -import '@openzeppelin/contracts/governance/extensions/GovernorTimelockControl.sol'; -import '@openzeppelin/contracts/utils/Address.sol'; +import '@openzeppelin/contracts-upgradeable/governance/GovernorUpgradeable.sol'; +import '@openzeppelin/contracts-upgradeable/governance/IGovernorUpgradeable.sol'; +import '@openzeppelin/contracts-upgradeable/governance/extensions/GovernorSettingsUpgradeable.sol'; +import '@openzeppelin/contracts-upgradeable/governance/extensions/GovernorVotesUpgradeable.sol'; +import '@openzeppelin/contracts-upgradeable/governance/extensions/GovernorVotesQuorumFractionUpgradeable.sol'; +import '@openzeppelin/contracts-upgradeable/governance/extensions/GovernorTimelockControlUpgradeable.sol'; import './CrossChainGovernorCountingSimple.sol'; -import './DAOSpokeContract.sol'; import './wormhole/IWormholeRelayer.sol'; import './wormhole/IWormholeReceiver.sol'; import './magistrate/Magistrate.sol'; @@ -22,17 +21,15 @@ import './magistrate/Magistrate.sol'; * For more details check out [OpenZeppelin's documentation](https://docs.openzeppelin.com/contracts/4.x/api/governance#governor). */ contract MetaHumanGovernor is - Governor, - GovernorSettings, + GovernorUpgradeable, + GovernorSettingsUpgradeable, CrossChainGovernorCountingSimple, - GovernorVotes, - GovernorVotesQuorumFraction, - GovernorTimelockControl, + GovernorVotesUpgradeable, + GovernorVotesQuorumFractionUpgradeable, + GovernorTimelockControlUpgradeable, Magistrate, IWormholeReceiver { - using Address for address payable; - error MessageAlreadyProcessed(); error OnlyRelayerAllowed(); error InvalidIntendedRecipient(); @@ -41,16 +38,22 @@ contract MetaHumanGovernor is error RequestAfterVotePeriodOver(); error CollectionPhaseAlreadyStarted(); error OnlyMessagesFromSpokeReceived(); + error UseCrossChainCancel(); + error UseCrossChainPropose(); + error ProposalNotSuccessful(); + error ZeroBalance(); - IWormholeRelayer public immutable wormholeRelayer; + IWormholeRelayer public wormholeRelayer; uint256 internal constant GAS_LIMIT = 500_000; - uint256 public immutable secondsPerBlock; - uint16 public immutable chainId; + uint256 public secondsPerBlock; + uint16 public chainId; mapping(bytes32 => bool) public processedMessages; mapping(uint256 => bool) public collectionStarted; mapping(uint256 => bool) public collectionFinished; + uint256[50] private __gap; + /** * @dev Contract constructor. * @param _token The address of the token contract used for voting. @@ -59,9 +62,9 @@ contract MetaHumanGovernor is * @param _chainId The chain ID of the current contract. * @param _wormholeRelayerAddress The address of the wormhole automatic relayer contract used for cross-chain communication */ - constructor( - IVotes _token, - TimelockController _timelock, + function initialize( + IVotesUpgradeable _token, + TimelockControllerUpgradeable _timelock, CrossChainAddress[] memory _spokeContracts, uint16 _chainId, address _wormholeRelayerAddress, @@ -71,19 +74,19 @@ contract MetaHumanGovernor is uint32 _votingPeriodInSeconds, uint256 _proposalThreshold, uint256 _quorumFraction - ) - Governor('MetaHumanGovernor') - GovernorSettings( + ) public initializer { + __Governor_init('MetaHumanGovernor'); + __GovernorSettings_init( _votingDelayInSeconds, _votingPeriodInSeconds, _proposalThreshold - ) - GovernorVotes(_token) - GovernorVotesQuorumFraction(_quorumFraction) - GovernorTimelockControl(_timelock) - CrossChainGovernorCountingSimple(_spokeContracts) - Magistrate(_magistrateAddress) - { + ); + __CrossChainGovernorCountingSimple_init(_spokeContracts); + __GovernorVotes_init(_token); + __GovernorVotesQuorumFraction_init(_quorumFraction); + __GovernorTimelockControl_init(_timelock); + __Magistrate_init(_magistrateAddress); + chainId = _chainId; wormholeRelayer = IWormholeRelayer(_wormholeRelayerAddress); secondsPerBlock = _secondsPerBlock; @@ -94,7 +97,11 @@ contract MetaHumanGovernor is uint256[] memory values, bytes[] memory calldatas, bytes32 descriptionHash - ) public override(Governor) returns (uint256) { + ) + public + override(GovernorUpgradeable, IGovernorUpgradeable) + returns (uint256) + { uint256 proposalId = hashProposal( targets, values, @@ -103,7 +110,7 @@ contract MetaHumanGovernor is ); if (spokeContractsSnapshots[proposalId].length > 0) { - revert('Please use crossChainCancel for proposals with spokes.'); + revert UseCrossChainCancel(); } return super.cancel(targets, values, calldatas, descriptionHash); @@ -122,7 +129,7 @@ contract MetaHumanGovernor is descriptionHash ); bytes memory message = abi.encode(uint16(2), proposalId); // selector 2 = cancel - _sendCrossChainMessageToSpokes(proposalId, message, address(this), 2); + _sendCrossChainMessageToSpokes(proposalId, message, address(this)); return proposalId; } @@ -136,7 +143,7 @@ contract MetaHumanGovernor is */ function receiveWormholeMessages( bytes calldata payload, - bytes[] calldata additionalVaas, // additionalVaas + bytes[] calldata, // additionalVaas bytes32 sourceAddress, // address that called 'sendPayloadToEvm' (HelloWormhole contract address) uint16 sourceChain, bytes32 deliveryHash // this can be stored in a mapping deliveryHash => bool to prevent duplicate deliveries @@ -204,13 +211,6 @@ contract MetaHumanGovernor is revert OnlyMessagesFromSpokeReceived(); } - require( - spokeContractsMappingSnapshots[_proposalId][emitterAddress][ - emitterChainId - ], - 'Only messages from the spoke contracts can be received!' - ); - // As long as the received data isn't already initialized... if ( spokeVotes[_proposalId][emitterAddress][emitterChainId].initialized @@ -234,13 +234,12 @@ contract MetaHumanGovernor is bool phaseFinished = true; uint256 spokeContractsLength = spokeContractsSnapshots[proposalId] .length; - for (uint16 i = 1; i <= spokeContractsLength && phaseFinished; ++i) { + for (uint16 i = 0; i < spokeContractsLength && phaseFinished; ++i) { phaseFinished = phaseFinished && spokeVotes[proposalId][ - spokeContractsSnapshots[proposalId][i - 1].contractAddress - ][spokeContractsSnapshots[proposalId][i - 1].chainId] - .initialized; + spokeContractsSnapshots[proposalId][i].contractAddress + ][spokeContractsSnapshots[proposalId][i].chainId].initialized; } collectionFinished[proposalId] = phaseFinished; @@ -250,7 +249,7 @@ contract MetaHumanGovernor is * @dev Requests the voting data from all of the spoke chains. * @param proposalId The ID of the proposal. */ - function requestCollections(uint256 proposalId) public payable { + function requestCollections(uint256 proposalId) external payable { if (block.timestamp < proposalDeadline(proposalId)) { revert RequestAfterVotePeriodOver(); } @@ -270,7 +269,7 @@ contract MetaHumanGovernor is } bytes memory message = abi.encode(uint16(1), proposalId); // selector 1 = requestCollections - _sendCrossChainMessageToSpokes(proposalId, message, msg.sender, 1); + _sendCrossChainMessageToSpokes(proposalId, message, msg.sender); } /** @@ -308,7 +307,7 @@ contract MetaHumanGovernor is voteStartTimestamp, voteEndTimestamp ); - _sendCrossChainMessageToSpokes(proposalId, message, address(this), 0); + _sendCrossChainMessageToSpokes(proposalId, message, address(this)); return proposalId; } @@ -317,26 +316,21 @@ contract MetaHumanGovernor is * @param proposalId The ID of the proposal. * @param message The encoded message to send. * @param sender The address to set as msg.sender in the payload (for requestCollections, otherwise address(this)). - * @param selector The function selector (0: propose, 1: requestCollections, 2: cancel). */ function _sendCrossChainMessageToSpokes( uint256 proposalId, bytes memory message, - address sender, - uint16 selector + address sender ) internal { uint256 spokeContractsLength = spokeContractsSnapshots[proposalId] .length; - uint256 sendMessageToHubCost = _quoteCrossChainMessage(chainId, 0); - bool isRequestCollectionsMessage = (selector == 1); - for (uint16 i = 1; i <= spokeContractsLength; ++i) { - uint16 spokeChainId = spokeContractsSnapshots[proposalId][i - 1] + for (uint16 i = 0; i < spokeContractsLength; ++i) { + uint16 spokeChainId = spokeContractsSnapshots[proposalId][i] .chainId; address spokeAddress = address( uint160( uint256( - spokeContractsSnapshots[proposalId][i - 1] - .contractAddress + spokeContractsSnapshots[proposalId][i].contractAddress ) ) ); @@ -346,15 +340,12 @@ contract MetaHumanGovernor is sender, message ); - uint256 cost = _quoteCrossChainMessage( - spokeChainId, - isRequestCollectionsMessage ? sendMessageToHubCost : 0 - ); + uint256 cost = _quoteCrossChainMessage(spokeChainId, 0); wormholeRelayer.sendPayloadToEvm{value: cost}( spokeChainId, spokeAddress, payload, - isRequestCollectionsMessage ? sendMessageToHubCost : 0, + 0, GAS_LIMIT, spokeChainId, magistrate() @@ -386,7 +377,7 @@ contract MetaHumanGovernor is function votingDelay() public view - override(Governor, GovernorSettings) + override(IGovernorUpgradeable, GovernorSettingsUpgradeable) returns (uint256) { return super.votingDelay(); // Ensure this returns time in seconds @@ -399,7 +390,7 @@ contract MetaHumanGovernor is function votingPeriod() public view - override(Governor, GovernorSettings) + override(IGovernorUpgradeable, GovernorSettingsUpgradeable) returns (uint256) { return super.votingPeriod(); // Ensure this returns time in seconds @@ -415,7 +406,7 @@ contract MetaHumanGovernor is ) public view - override(Governor, GovernorVotesQuorumFraction) + override(IGovernorUpgradeable, GovernorVotesQuorumFractionUpgradeable) returns (uint256) { return super.quorum(snapshotTime); @@ -432,7 +423,7 @@ contract MetaHumanGovernor is ) public view - override(Governor, GovernorTimelockControl) + override(GovernorUpgradeable, GovernorTimelockControlUpgradeable) returns (ProposalState) { return super.state(proposalId); @@ -446,8 +437,13 @@ contract MetaHumanGovernor is uint256[] memory, bytes[] memory, string memory - ) public pure override(Governor) returns (uint256) { - revert('Please use crossChainPropose instead.'); + ) + public + pure + override(GovernorUpgradeable, IGovernorUpgradeable) + returns (uint256) + { + revert UseCrossChainPropose(); } /** @@ -457,36 +453,12 @@ contract MetaHumanGovernor is function proposalThreshold() public view - override(Governor, GovernorSettings) + override(GovernorUpgradeable, GovernorSettingsUpgradeable) returns (uint256) { return super.proposalThreshold(); } - /** - * @dev Function to queue a proposal to the timelock. - */ - function queue( - address[] memory targets, - uint256[] memory values, - bytes[] memory calldatas, - bytes32 descriptionHash - ) public virtual override returns (uint256) { - uint256 proposalId = hashProposal( - targets, - values, - calldatas, - descriptionHash - ); - - require( - state(proposalId) == ProposalState.Succeeded, - 'Governor: proposal not successful' - ); - - return super.queue(targets, values, calldatas, descriptionHash); - } - /** * @dev Cancels a proposal. * @param targets The array of target addresses. @@ -500,7 +472,11 @@ contract MetaHumanGovernor is uint256[] memory values, bytes[] memory calldatas, bytes32 descriptionHash - ) internal override(Governor, GovernorTimelockControl) returns (uint256) { + ) + internal + override(GovernorUpgradeable, GovernorTimelockControlUpgradeable) + returns (uint256) + { return super._cancel(targets, values, calldatas, descriptionHash); } @@ -511,49 +487,29 @@ contract MetaHumanGovernor is function _executor() internal view - override(Governor, GovernorTimelockControl) + override(GovernorUpgradeable, GovernorTimelockControlUpgradeable) returns (address) { return super._executor(); } - function _queueOperations( + function _execute( uint256 proposalId, address[] memory targets, uint256[] memory values, bytes[] memory calldatas, bytes32 descriptionHash - ) internal override(Governor, GovernorTimelockControl) returns (uint48) { - return - super._queueOperations( - proposalId, - targets, - values, - calldatas, - descriptionHash - ); - } - - function _executeOperations( - uint256 proposalId, - address[] memory targets, - uint256[] memory values, - bytes[] memory calldatas, - bytes32 descriptionHash - ) internal override(Governor, GovernorTimelockControl) { + ) + internal + override(GovernorUpgradeable, GovernorTimelockControlUpgradeable) + { _finishCollectionPhase(proposalId); if (!collectionFinished[proposalId]) { revert CollectionPhaseUnfinished(); } - super._executeOperations( - proposalId, - targets, - values, - calldatas, - descriptionHash - ); + super._execute(proposalId, targets, values, calldatas, descriptionHash); } /** @@ -563,19 +519,26 @@ contract MetaHumanGovernor is */ function supportsInterface( bytes4 interfaceId - ) public view override(Governor) returns (bool) { - return super.supportsInterface(interfaceId); - } - - function proposalNeedsQueuing( - uint256 proposalId ) public view - virtual - override(Governor, GovernorTimelockControl) + override(GovernorUpgradeable, GovernorTimelockControlUpgradeable) returns (bool) { - return super.proposalNeedsQueuing(proposalId); + return super.supportsInterface(interfaceId); + } + + /** + * @dev Withdraws the contract's balance to the magistrate address. + * Can only be called by the magistrate. + */ + function withdraw() external onlyMagistrate { + uint256 balance = address(this).balance; + if (balance == 0) { + revert ZeroBalance(); + } + payable(msg.sender).transfer(balance); } + + receive() external payable override(GovernorUpgradeable) {} } diff --git a/packages/core/contracts/governance/magistrate/Magistrate.sol b/packages/core/contracts/governance/magistrate/Magistrate.sol index 06f21dd9f2..7f25bb3d29 100644 --- a/packages/core/contracts/governance/magistrate/Magistrate.sol +++ b/packages/core/contracts/governance/magistrate/Magistrate.sol @@ -2,13 +2,14 @@ pragma solidity ^0.8.0; -import '@openzeppelin/contracts/utils/Context.sol'; +import '@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol'; +import '@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol'; /** * @dev This contract is based on OpenZeppelin's access/Ownable.sol contract. * The only thing changed is the removal of `renounceOwnership()` function and name of the variables. */ -abstract contract Magistrate is Context { +abstract contract Magistrate is Initializable, ContextUpgradeable { address private _magistrate; event MagistrateChanged( @@ -17,9 +18,16 @@ abstract contract Magistrate is Context { ); /** - * @dev Initializes the contract setting the provided address as the initial magistrate. + * @dev Initializer to set the initial magistrate. Must be called during proxy initialization. */ - constructor(address magistrate_) { + function __Magistrate_init(address magistrate_) internal onlyInitializing { + __Context_init(); + __Magistrate_init_unchained(magistrate_); + } + + function __Magistrate_init_unchained( + address magistrate_ + ) internal onlyInitializing { _transferMagistrate(magistrate_); } diff --git a/packages/core/contracts/vendor/TimelockController.import.sol b/packages/core/contracts/vendor/TimelockController.import.sol new file mode 100644 index 0000000000..145615c5eb --- /dev/null +++ b/packages/core/contracts/vendor/TimelockController.import.sol @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.23; + +// This file intentionally imports the non-upgradeable OZ Timelock so Hardhat compiles it +// and TypeChain generates typings + artifacts for tests. +import '@openzeppelin/contracts/governance/TimelockController.sol'; diff --git a/packages/core/hardhat.config.ts b/packages/core/hardhat.config.ts index 045a329124..8110bea27c 100644 --- a/packages/core/hardhat.config.ts +++ b/packages/core/hardhat.config.ts @@ -52,8 +52,12 @@ const config: HardhatUserConfig = { viaIR: true, optimizer: { enabled: true, - runs: 10, + runs: 1, }, + metadata: { + bytecodeHash: 'none', + }, + evmVersion: 'paris', }, }, ], @@ -130,7 +134,7 @@ const config: HardhatUserConfig = { alphaSort: true, runOnCompile: true, disambiguatePaths: false, - strict: true, + strict: false, only: [], except: [], }, @@ -164,15 +168,7 @@ const config: HardhatUserConfig = { }, ], etherscan: { - apiKey: { - mainnet: process.env.ETHERSCAN_API_KEY || '', - sepolia: process.env.ETHERSCAN_API_KEY || '', - polygon: process.env.POLYGONSCAN_API_KEY || '', - polygonAmoy: process.env.POLYGONSCAN_API_KEY || '', - bsc: process.env.BSC_API_KEY || '', - bscTestnet: process.env.BSC_API_KEY || '', - auroraTestnet: 'empty', - }, + apiKey: process.env.ETHERSCAN_API_KEY || '', customChains: [ { network: 'auroraTestnet', diff --git a/packages/core/package.json b/packages/core/package.json index 05bf21cc2a..486b7df2b8 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -24,6 +24,7 @@ "deploy:local": "yarn deploy --network localhost", "deploy:proxy": "hardhat run scripts/deploy-proxies.ts", "upgrade:proxy": "hardhat run scripts/upgrade-proxies.ts", + "upgrade:governor": "hardhat run scripts/upgrade-governor.ts", "deploy:hub": "hardhat run scripts/deploy-hub.ts", "deploy:spokes": "hardhat run scripts/deploy-spokes.ts", "update:spokes": "hardhat run scripts/update-spokes.ts", diff --git a/packages/core/scripts/create-proposal.ts b/packages/core/scripts/create-proposal.ts index 64aa57721b..932e772c76 100644 --- a/packages/core/scripts/create-proposal.ts +++ b/packages/core/scripts/create-proposal.ts @@ -26,7 +26,7 @@ async function main() { proposal.values, proposal.calldatas, proposal.description, - { value: ethers.parseEther('0.025') } + { value: ethers.parseEther('0.01') } ); await transactionResponse.wait(); diff --git a/packages/core/scripts/deploy-hub.ts b/packages/core/scripts/deploy-hub.ts index 68020a2563..3813cf9bd6 100644 --- a/packages/core/scripts/deploy-hub.ts +++ b/packages/core/scripts/deploy-hub.ts @@ -1,5 +1,5 @@ /* eslint-disable no-console */ -import { ethers } from 'hardhat'; +import { ethers, upgrades } from 'hardhat'; import dotenv from 'dotenv'; dotenv.config(); @@ -71,25 +71,29 @@ async function main() { const MetaHumanGovernor = await ethers.getContractFactory( 'contracts/governance/MetaHumanGovernor.sol:MetaHumanGovernor' ); - const metaHumanGovernorContract = await MetaHumanGovernor.deploy( - vhmTokenAddress, - TimelockControllerContract.getAddress(), - [], - chainId, - hubAutomaticRelayerAddress, - magistrateAddress, - hubSecondsPerBlock, - votingDelay, - votingPeriod, - proposalThreshold, - quorumFraction + const metaHumanGovernorContract = await upgrades.deployProxy( + MetaHumanGovernor, + [ + vhmTokenAddress, + await TimelockControllerContract.getAddress(), + [], + chainId, + hubAutomaticRelayerAddress, + magistrateAddress, + hubSecondsPerBlock, + votingDelay, + votingPeriod, + proposalThreshold, + quorumFraction, + ], + { initializer: 'initialize' } ); - await metaHumanGovernorContract.waitForDeployment(); - console.log( - 'Governor deployed to:', - await metaHumanGovernorContract.getAddress() - ); + const proxyAddress = await metaHumanGovernorContract.getAddress(); + const implementationAddress = + await upgrades.erc1967.getImplementationAddress(proxyAddress); + console.log('Governor Proxy deployed at:', proxyAddress); + console.log('Governor Implementation at:', implementationAddress); } main().catch((error) => { diff --git a/packages/core/scripts/upgrade-governor.ts b/packages/core/scripts/upgrade-governor.ts new file mode 100644 index 0000000000..82ae82b7d0 --- /dev/null +++ b/packages/core/scripts/upgrade-governor.ts @@ -0,0 +1,43 @@ +/* eslint-disable no-console */ +import 'dotenv/config'; +import { ethers, upgrades } from 'hardhat'; + +async function main() { + const governorProxy = process.env.GOVERNOR_ADDRESS; + const fqName = + process.env.GOVERNOR_FQN || + 'contracts/governance/MetaHumanGovernor.sol:MetaHumanGovernor'; + + if (!governorProxy) { + console.error('Missing env: GOVERNOR_ADDRESS'); + process.exitCode = 1; + return; + } + + console.log('Governor Proxy Address: ', governorProxy); + console.log('Using implementation FQN: ', fqName); + + const beforeImpl = + await upgrades.erc1967.getImplementationAddress(governorProxy); + console.log('Current Governor Implementation: ', beforeImpl); + + const GovernorFactory = await ethers.getContractFactory(fqName); + + console.log('Preparing new Governor implementation...'); + const newImpl = await upgrades.prepareUpgrade(governorProxy, GovernorFactory); + console.log('Prepared Governor Implementation: ', newImpl); + + console.log('Upgrading Governor proxy...'); + const upgraded = await upgrades.upgradeProxy(governorProxy, GovernorFactory); + const contract = await upgraded.waitForDeployment(); + const txHash = contract.deploymentTransaction()?.hash; + if (txHash) { + await ethers.provider.getTransactionReceipt(txHash); + } + console.log('Governor proxy upgraded successfully!'); +} + +main().catch((error) => { + console.error(error); + process.exitCode = 1; +}); diff --git a/packages/core/test/DAOSpokeContract.ts b/packages/core/test/DAOSpokeContract.ts index 663e3e2c97..baff980948 100644 --- a/packages/core/test/DAOSpokeContract.ts +++ b/packages/core/test/DAOSpokeContract.ts @@ -1,5 +1,5 @@ import { expect } from 'chai'; -import { ethers } from 'hardhat'; +import { ethers, upgrades } from 'hardhat'; import { Signer } from 'ethers'; import { MetaHumanGovernor, @@ -64,12 +64,12 @@ describe('DAOSpokeContract', function () { proposers = [await owner.getAddress()]; const TimelockController = await ethers.getContractFactory('TimelockController'); - timelockController = await TimelockController.deploy( + timelockController = (await TimelockController.deploy( 1, proposers, executors, - owner.getAddress() - ); + await owner.getAddress() + )) as TimelockController; await timelockController.waitForDeployment(); const WormholeMock = await ethers.getContractFactory('WormholeMock'); @@ -87,18 +87,22 @@ describe('DAOSpokeContract', function () { const MetaHumanContract = await ethers.getContractFactory( 'contracts/governance/MetaHumanGovernor.sol:MetaHumanGovernor' ); - governor = (await MetaHumanContract.deploy( - voteToken.getAddress(), - timelockController.getAddress(), - [], - 0, - await wormholeMockForGovernor.getAddress(), - owner.getAddress(), - SECONDS_PER_BLOCK, - SECONDS_PER_BLOCK * 1, - SECONDS_PER_BLOCK * 20 * 15, - 0, - 4 + governor = (await upgrades.deployProxy( + MetaHumanContract, + [ + await voteToken.getAddress(), + await timelockController.getAddress(), + [], + 0, + await wormholeMockForGovernor.getAddress(), + await owner.getAddress(), + SECONDS_PER_BLOCK, + SECONDS_PER_BLOCK * 1, + SECONDS_PER_BLOCK * 20 * 15, + 0, + 4, + ], + { initializer: 'initialize' } )) as MetaHumanGovernor; // Set Governor on worm hole mock @@ -120,11 +124,11 @@ describe('DAOSpokeContract', function () { daoSpoke = (await DAOSpokeContract.deploy( ethers.zeroPadBytes(await governor.getAddress(), 32), 5, // hubChainId - voteToken.getAddress(), + await voteToken.getAddress(), SECONDS_PER_BLOCK, // voting period 6, // spokeChainId await wormholeMockForDaoSpoke.getAddress(), - owner.getAddress() // admin address + await owner.getAddress() // admin address )) as DAOSpokeContract; // Set DAOSpokeContract on worm hole mock @@ -270,9 +274,9 @@ describe('DAOSpokeContract', function () { await mineNBlocks(3); - await expect( - daoSpoke.connect(user1).castVote(proposalId, 3) - ).to.be.revertedWith('DAOSpokeContract: invalid value for enum VoteType'); + await expect(daoSpoke.connect(user1).castVote(proposalId, 3)) + .to.be.revertedWithCustomError(daoSpoke, 'InvalidVoteType') + .withArgs(3); }); it('should revert when vote is finished', async () => { @@ -294,15 +298,15 @@ describe('DAOSpokeContract', function () { await expect( daoSpoke.connect(user1).castVote(proposalId, 0) - ).to.be.revertedWith('DAOSpokeContract: vote finished'); + ).to.be.revertedWithCustomError(daoSpoke, 'VoteFinished'); }); it('should revert when proposal does not exist', async () => { await createMockUserWithVotingPower(voteToken, user1); - await expect(daoSpoke.connect(user1).castVote(1, 0)).to.be.revertedWith( - 'DAOSpokeContract: not a started vote' - ); + await expect( + daoSpoke.connect(user1).castVote(1, 0) + ).to.be.revertedWithCustomError(daoSpoke, 'NotStartedVote'); }); it('should revert when the vote was already cast', async () => { @@ -321,7 +325,7 @@ describe('DAOSpokeContract', function () { await expect( daoSpoke.connect(user1).castVote(proposalId, 0) - ).to.be.revertedWith('DAOSpokeContract: vote already cast'); + ).to.be.revertedWithCustomError(daoSpoke, 'VoteAlreadyCast'); }); }); @@ -337,9 +341,7 @@ describe('DAOSpokeContract', function () { await expect( callReceiveMessageWithWormholeMock(wormholeMockForDaoSpoke, mockPayload) - ).to.be.revertedWith( - 'Only messages from the hub contract can be received!' - ); + ).to.be.revertedWithCustomError(daoSpoke, 'OnlyMessagesFromHub'); }); it('should revert when contract is not intended recipient', async () => { @@ -351,7 +353,7 @@ describe('DAOSpokeContract', function () { await expect( callReceiveMessageWithWormholeMock(wormholeMockForDaoSpoke, mockPayload) - ).to.be.revertedWith('Message is not addressed for this contract'); + ).to.be.revertedWithCustomError(daoSpoke, 'InvalidIntendedRecipient'); }); it('should revert when proposal id is not unique', async () => { @@ -367,7 +369,7 @@ describe('DAOSpokeContract', function () { ); await expect( callReceiveMessageWithWormholeMock(wormholeMockForDaoSpoke, mockPayload) - ).to.be.revertedWith('Message already processed'); + ).to.be.revertedWithCustomError(daoSpoke, 'MessageAlreadyProcessed'); }); it('should process message when proposal start before block timestamp', async () => { @@ -535,9 +537,9 @@ describe('DAOSpokeContract', function () { await governor.getAddress() ); - await expect(daoSpoke.sendVoteResultToHub(proposalId)).to.be.revertedWith( - 'DAOSpokeContract: vote is not finished' - ); + await expect( + daoSpoke.sendVoteResultToHub(proposalId) + ).to.be.revertedWithCustomError(daoSpoke, 'VoteNotFinished'); }); it('should revert when the caller is not the magistrate', async () => { diff --git a/packages/core/test/GovernanceUtils.ts b/packages/core/test/GovernanceUtils.ts index ade3a39739..ee813dc226 100644 --- a/packages/core/test/GovernanceUtils.ts +++ b/packages/core/test/GovernanceUtils.ts @@ -257,8 +257,6 @@ export async function signProposalWithReasonAndParams( proposalId: string, governor: MetaHumanGovernor, support: number, - voter: string, - nonce: number, reason: string, params: string, signer: Signer @@ -271,7 +269,7 @@ export async function signProposalWithReasonAndParams( verifyingContract: await governor.getAddress(), }, { - Ballot: [ + ExtendedBallot: [ { name: 'proposalId', type: 'uint256', @@ -280,29 +278,19 @@ export async function signProposalWithReasonAndParams( name: 'support', type: 'uint8', }, - { - name: 'voter', - type: 'address', - }, - { - name: 'nonce', - type: 'uint256', - }, { name: 'reason', - type: 'bytes32', + type: 'string', }, { name: 'params', - type: 'bytes32', + type: 'bytes', }, ], }, { proposalId, support, - voter, - nonce, reason, params, } @@ -313,8 +301,6 @@ export async function signProposal( proposalId: string, governor: MetaHumanGovernor, support: number, - voter: string, - nonce: number, signer: Signer ): Promise { return await signer.signTypedData( @@ -334,21 +320,11 @@ export async function signProposal( name: 'support', type: 'uint8', }, - { - name: 'voter', - type: 'address', - }, - { - name: 'nonce', - type: 'uint256', - }, ], }, { proposalId, support, - voter, - nonce, } ); } diff --git a/packages/core/test/MetaHumanGovernor.ts b/packages/core/test/MetaHumanGovernor.ts index 3aa35ccaca..a5f4e6dd7d 100644 --- a/packages/core/test/MetaHumanGovernor.ts +++ b/packages/core/test/MetaHumanGovernor.ts @@ -1,5 +1,5 @@ import { expect } from 'chai'; -import { ethers } from 'hardhat'; +import { ethers, upgrades } from 'hardhat'; import { Signer, BigNumberish } from 'ethers'; import { MetaHumanGovernor, @@ -89,20 +89,23 @@ describe('MetaHumanGovernor', function () { const MetaHumanContract = await ethers.getContractFactory( 'contracts/governance/MetaHumanGovernor.sol:MetaHumanGovernor' ); - governor = (await MetaHumanContract.deploy( - voteToken.getAddress(), - timelockController.getAddress(), - [], - 0, - await wormholeMockForGovernor.getAddress(), - owner.getAddress(), - SECONDS_PER_BLOCK, - SECONDS_PER_BLOCK * 1, - SECONDS_PER_BLOCK * 300, - 0, - 4 + governor = (await upgrades.deployProxy( + MetaHumanContract, + [ + await voteToken.getAddress(), + await timelockController.getAddress(), + [], + 0, + await wormholeMockForGovernor.getAddress(), + await owner.getAddress(), + SECONDS_PER_BLOCK, + SECONDS_PER_BLOCK * 1, + SECONDS_PER_BLOCK * 300, + 0, + 4, + ], + { initializer: 'initialize' } )) as MetaHumanGovernor; - // Set Governor on worm hole mock await wormholeMockForGovernor.setReceiver(await governor.getAddress()); @@ -121,14 +124,13 @@ describe('MetaHumanGovernor', function () { ); daoSpoke = (await DAOSpokeContract.deploy( ethers.zeroPadBytes(await governor.getAddress(), 32), - hubChainId, // hubChainId - voteToken.getAddress(), - SECONDS_PER_BLOCK, // voting period - spokeChainId, // spokeChainId + hubChainId, + await voteToken.getAddress(), + SECONDS_PER_BLOCK, + spokeChainId, await wormholeMockForDaoSpoke.getAddress(), - owner.getAddress() // admin address + await owner.getAddress() )) as DAOSpokeContract; - // Set DAOSpokeContract on worm hole mock await wormholeMockForDaoSpoke.setReceiver(await daoSpoke.getAddress()); @@ -166,7 +168,7 @@ describe('MetaHumanGovernor', function () { 12, 6, '0x0591C25ebd0580E0d4F27A82Fc2e24E7489CB5e0', - await owner.getAddress() // admin address + await owner.getAddress() )) as DAOSpokeContract; const spokeContractsX = [ @@ -305,7 +307,7 @@ describe('MetaHumanGovernor', function () { await expect( governor.connect(owner).updateSpokeContracts(spokeContracts) - ).to.be.revertedWithCustomError(governor, 'OwnableUnauthorizedAccount'); + ).to.be.revertedWith('Ownable: caller is not the owner'); }); it('Should create a grant proposal', async function () { @@ -431,10 +433,7 @@ describe('MetaHumanGovernor', function () { await expect( governor.connect(user1).castVote(proposalId, 1) - ).to.be.revertedWithCustomError( - governor, - 'GovernorUnexpectedProposalState' - ); + ).to.be.revertedWith('Governor: vote not currently active'); }); it('Should allow voting against', async function () { @@ -1246,10 +1245,7 @@ describe('MetaHumanGovernor', function () { governor .connect(user1) .castVoteWithReasonAndParams(proposalId, 1, 'test reason', params) - ).to.be.revertedWithCustomError( - governor, - 'GovernorUnexpectedProposalState' - ); + ).to.be.revertedWith('Governor: vote not currently active'); }); it('Should vote on proposal by signature', async function () { @@ -1265,22 +1261,15 @@ describe('MetaHumanGovernor', function () { // create signature const support = 1; - const user1Address = await user1.getAddress(); - const signature = await signProposal( - proposalId, - governor, - support, - user1Address, - 0, - user1 - ); + const signature = await signProposal(proposalId, governor, support, user1); // wait for next block await mineNBlocks(2); // cast vote with sig + const sig1 = ethers.Signature.from(signature); await governor .connect(user1) - .castVoteBySig(proposalId, support, user1Address, signature); + .castVoteBySig(proposalId, support, sig1.v, sig1.r, sig1.s); //assert votes const { againstVotes, forVotes, abstainVotes } = @@ -1306,25 +1295,15 @@ describe('MetaHumanGovernor', function () { // create signature const support = 1; - const user1Address = await user1.getAddress(); - const signature = await signProposal( - proposalId, - governor, - support, - user1Address, - 0, - user1 - ); + const signature = await signProposal(proposalId, governor, support, user1); // cast vote with sig + const sig2 = ethers.Signature.from(signature); await expect( governor .connect(user1) - .castVoteBySig(proposalId, support, await user1.getAddress(), signature) - ).to.be.revertedWithCustomError( - governor, - 'GovernorUnexpectedProposalState' - ); + .castVoteBySig(proposalId, support, sig2.v, sig2.r, sig2.s) + ).to.be.revertedWith('Governor: vote not currently active'); }); it('Should revert when creating proposal with propose', async function () { @@ -1339,7 +1318,7 @@ describe('MetaHumanGovernor', function () { await expect( governor.propose(targets, values, calldatas, 'test') - ).to.be.revertedWith('Please use crossChainPropose instead.'); + ).to.be.revertedWithCustomError(governor, 'UseCrossChainPropose'); }); it('Should return magistrate', async function () { @@ -1359,4 +1338,53 @@ describe('MetaHumanGovernor', function () { governor.transferMagistrate(ethers.ZeroAddress) ).to.be.revertedWith('Magistrate: new magistrate is the zero address'); }); + + it('Should revert withdraw with ZeroBalance when empty', async function () { + await expect(governor.withdraw()).to.be.revertedWithCustomError( + governor, + 'ZeroBalance' + ); + }); + + it('Should revert withdraw when caller is not magistrate', async function () { + await owner.sendTransaction({ + to: await governor.getAddress(), + value: ethers.parseEther('0.1'), + }); + await expect(governor.connect(user1).withdraw()).to.be.revertedWith( + 'Magistrate: caller is not the magistrate' + ); + }); + + it('Should allow magistrate to withdraw full balance', async function () { + const amount = ethers.parseEther('0.2'); + await owner.sendTransaction({ + to: await governor.getAddress(), + value: amount, + }); + + const balanceBefore = await ethers.provider.getBalance( + await owner.getAddress() + ); + const contractBefore = await ethers.provider.getBalance( + await governor.getAddress() + ); + expect(contractBefore).to.equal(amount); + + const tx = await governor.withdraw(); + const receipt = await tx.wait(); + expect(receipt?.status).to.equal(1); + + const contractAfter = await ethers.provider.getBalance( + await governor.getAddress() + ); + expect(contractAfter).to.equal(0n); + + const balanceAfter = await ethers.provider.getBalance( + await owner.getAddress() + ); + expect(balanceAfter).to.be.greaterThan( + balanceBefore - ethers.parseEther('0.001') + ); + }); }); diff --git a/packages/core/test/MetaHumanGovernorHubOnly.ts b/packages/core/test/MetaHumanGovernorHubOnly.ts index 09e0353316..97bef56ad0 100644 --- a/packages/core/test/MetaHumanGovernorHubOnly.ts +++ b/packages/core/test/MetaHumanGovernorHubOnly.ts @@ -1,5 +1,5 @@ import { expect } from 'chai'; -import { ethers } from 'hardhat'; +import { ethers, upgrades } from 'hardhat'; import { Signer, BigNumberish } from 'ethers'; import { MetaHumanGovernor, @@ -75,22 +75,26 @@ describe('MetaHumanGovernorHubOnly', function () { value: ethers.parseEther('1'), }); - // Deploy MetaHumanGovernor + // Deploy MetaHumanGovernor (proxy) const MetaHumanContract = await ethers.getContractFactory( 'contracts/governance/MetaHumanGovernor.sol:MetaHumanGovernor' ); - governor = (await MetaHumanContract.deploy( - voteToken.getAddress(), - timelockController.getAddress(), - [], - 0, - await wormholeMockForGovernor.getAddress(), - owner.getAddress(), - SECONDS_PER_BLOCK, - SECONDS_PER_BLOCK * 1, - SECONDS_PER_BLOCK * 300, - 0, - 4 + governor = (await upgrades.deployProxy( + MetaHumanContract, + [ + await voteToken.getAddress(), + await timelockController.getAddress(), + [], + 0, + await wormholeMockForGovernor.getAddress(), + await owner.getAddress(), + SECONDS_PER_BLOCK, + SECONDS_PER_BLOCK * 1, + SECONDS_PER_BLOCK * 300, + 0, + 4, + ], + { initializer: 'initialize' } )) as MetaHumanGovernor; // Grant proposer role on timelock controller @@ -171,10 +175,7 @@ describe('MetaHumanGovernorHubOnly', function () { await expect( governor.connect(user1).castVote(proposalId, 1) - ).to.be.revertedWithCustomError( - governor, - 'GovernorUnexpectedProposalState' - ); + ).to.be.revertedWith('Governor: vote not currently active'); }); it('Should allow voting against', async function () { @@ -700,10 +701,7 @@ describe('MetaHumanGovernorHubOnly', function () { governor .connect(user1) .castVoteWithReasonAndParams(proposalId, 1, 'test reason', params) - ).to.be.revertedWithCustomError( - governor, - 'GovernorUnexpectedProposalState' - ); + ).to.be.revertedWith('Governor: vote not currently active'); }); it('Should vote on proposal by signature', async function () { @@ -717,22 +715,15 @@ describe('MetaHumanGovernorHubOnly', function () { // create signature const support = 1; - const user1Address = await user1.getAddress(); - const signature = await signProposal( - proposalId, - governor, - support, - user1Address, - 0, - user1 - ); + const signature = await signProposal(proposalId, governor, support, user1); // wait for next block await mineNBlocks(2); // cast vote with sig + const sig1 = ethers.Signature.from(signature); await governor .connect(user1) - .castVoteBySig(proposalId, support, user1Address, signature); + .castVoteBySig(proposalId, support, sig1.v, sig1.r, sig1.s); //assert votes const { againstVotes, forVotes, abstainVotes } = @@ -754,25 +745,15 @@ describe('MetaHumanGovernorHubOnly', function () { // create signature const support = 1; - const user1Address = await user1.getAddress(); - const signature = await signProposal( - proposalId, - governor, - support, - user1Address, - 0, - user1 - ); + const signature = await signProposal(proposalId, governor, support, user1); // cast vote with sig + const sig2 = ethers.Signature.from(signature); await expect( governor .connect(user1) - .castVoteBySig(proposalId, support, await user1.getAddress(), signature) - ).to.be.revertedWithCustomError( - governor, - 'GovernorUnexpectedProposalState' - ); + .castVoteBySig(proposalId, support, sig2.v, sig2.r, sig2.s) + ).to.be.revertedWith('Governor: vote not currently active'); }); it('Should revert when creating proposal with propose', async function () { @@ -787,7 +768,7 @@ describe('MetaHumanGovernorHubOnly', function () { await expect( governor.propose(targets, values, calldatas, 'test') - ).to.be.revertedWith('Please use crossChainPropose instead.'); + ).to.be.revertedWithCustomError(governor, 'UseCrossChainPropose'); }); it('Should return magistrate', async function () { @@ -807,4 +788,43 @@ describe('MetaHumanGovernorHubOnly', function () { governor.transferMagistrate(ethers.ZeroAddress) ).to.be.revertedWith('Magistrate: new magistrate is the zero address'); }); + + it('Should revert withdraw with ZeroBalance when empty', async function () { + await expect(governor.withdraw()).to.be.revertedWithCustomError( + governor, + 'ZeroBalance' + ); + }); + + it('Should revert withdraw when caller is not magistrate', async function () { + await owner.sendTransaction({ + to: await governor.getAddress(), + value: ethers.parseEther('0.05'), + }); + await expect(governor.connect(user1).withdraw()).to.be.revertedWith( + 'Magistrate: caller is not the magistrate' + ); + }); + + it('Should allow magistrate to withdraw full balance', async function () { + const amount = ethers.parseEther('0.05'); + await owner.sendTransaction({ + to: await governor.getAddress(), + value: amount, + }); + + const contractBefore = await ethers.provider.getBalance( + await governor.getAddress() + ); + expect(contractBefore).to.equal(amount); + + const tx = await governor.withdraw(); + const receipt = await tx.wait(); + expect(receipt?.status).to.equal(1); + + const contractAfter = await ethers.provider.getBalance( + await governor.getAddress() + ); + expect(contractAfter).to.equal(0n); + }); }); From 2666614915f8ddf3030b70dcf5c69247f53bb4db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Francisco=20L=C3=B3pez?= Date: Fri, 26 Sep 2025 10:15:03 +0200 Subject: [PATCH 2/2] - use existing TimelockController address if available for deploying new hub - Upload new polygon deployment --- packages/core/.openzeppelin/polygon.json | 694 +++++++++++++++++++++++ packages/core/scripts/deploy-hub.ts | 34 +- 2 files changed, 714 insertions(+), 14 deletions(-) diff --git a/packages/core/.openzeppelin/polygon.json b/packages/core/.openzeppelin/polygon.json index 9dd6f83030..3e7e80a6bc 100644 --- a/packages/core/.openzeppelin/polygon.json +++ b/packages/core/.openzeppelin/polygon.json @@ -12,6 +12,11 @@ { "address": "0x1371057BAec59944B924A7963F2EeCF43ff94CE4", "kind": "uups" + }, + { + "address": "0x98ca1cbCf337e500c7557F28b3B0770602f4Bb81", + "txHash": "0x568b30c00c08f20af291b8d517c5c51c775d1b18a9fb6b9d8ed78869a77e8747", + "kind": "transparent" } ], "impls": { @@ -718,6 +723,695 @@ }, "namespaces": {} } + }, + "4ce58128162dd4657e8191cb81f01bcd8d412ac24cacecafa126c9609d51b344": { + "address": "0x3280F7005271F583312275cf17961b0CAa97D8Ca", + "txHash": "0x53965d6153e226a4b96dcbbbbf9b6177fce7f15d42cb008a9707c30e079ca5c0", + "layout": { + "solcVersion": "0.8.23", + "storage": [ + { + "label": "_initialized", + "offset": 0, + "slot": "0", + "type": "t_uint8", + "contract": "Initializable", + "src": "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol:63", + "retypedFrom": "bool" + }, + { + "label": "_initializing", + "offset": 1, + "slot": "0", + "type": "t_bool", + "contract": "Initializable", + "src": "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol:68" + }, + { + "label": "__gap", + "offset": 0, + "slot": "1", + "type": "t_array(t_uint256)50_storage", + "contract": "ContextUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol:40" + }, + { + "label": "__gap", + "offset": 0, + "slot": "51", + "type": "t_array(t_uint256)50_storage", + "contract": "ERC165Upgradeable", + "src": "@openzeppelin/contracts-upgradeable/utils/introspection/ERC165Upgradeable.sol:41" + }, + { + "label": "_hashedName", + "offset": 0, + "slot": "101", + "type": "t_bytes32", + "contract": "EIP712Upgradeable", + "src": "@openzeppelin/contracts-upgradeable/utils/cryptography/EIP712Upgradeable.sol:40", + "renamedFrom": "_HASHED_NAME" + }, + { + "label": "_hashedVersion", + "offset": 0, + "slot": "102", + "type": "t_bytes32", + "contract": "EIP712Upgradeable", + "src": "@openzeppelin/contracts-upgradeable/utils/cryptography/EIP712Upgradeable.sol:42", + "renamedFrom": "_HASHED_VERSION" + }, + { + "label": "_name", + "offset": 0, + "slot": "103", + "type": "t_string_storage", + "contract": "EIP712Upgradeable", + "src": "@openzeppelin/contracts-upgradeable/utils/cryptography/EIP712Upgradeable.sol:44" + }, + { + "label": "_version", + "offset": 0, + "slot": "104", + "type": "t_string_storage", + "contract": "EIP712Upgradeable", + "src": "@openzeppelin/contracts-upgradeable/utils/cryptography/EIP712Upgradeable.sol:45" + }, + { + "label": "__gap", + "offset": 0, + "slot": "105", + "type": "t_array(t_uint256)48_storage", + "contract": "EIP712Upgradeable", + "src": "@openzeppelin/contracts-upgradeable/utils/cryptography/EIP712Upgradeable.sol:204" + }, + { + "label": "__gap", + "offset": 0, + "slot": "153", + "type": "t_array(t_uint256)50_storage", + "contract": "IGovernorUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/governance/IGovernorUpgradeable.sol:325" + }, + { + "label": "__gap", + "offset": 0, + "slot": "203", + "type": "t_array(t_uint256)50_storage", + "contract": "IGovernorTimelockUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/governance/extensions/IGovernorTimelockUpgradeable.sol:38" + }, + { + "label": "_name", + "offset": 0, + "slot": "253", + "type": "t_string_storage", + "contract": "GovernorUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/governance/GovernorUpgradeable.sol:51" + }, + { + "label": "_proposals", + "offset": 0, + "slot": "254", + "type": "t_mapping(t_uint256,t_struct(ProposalCore)600_storage)", + "contract": "GovernorUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/governance/GovernorUpgradeable.sol:54", + "retypedFrom": "mapping(uint256 => Governor.ProposalCore)" + }, + { + "label": "_governanceCall", + "offset": 0, + "slot": "255", + "type": "t_struct(Bytes32Deque)10879_storage", + "contract": "GovernorUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/governance/GovernorUpgradeable.sol:60" + }, + { + "label": "__gap", + "offset": 0, + "slot": "257", + "type": "t_array(t_uint256)46_storage", + "contract": "GovernorUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/governance/GovernorUpgradeable.sol:735" + }, + { + "label": "_votingDelay", + "offset": 0, + "slot": "303", + "type": "t_uint256", + "contract": "GovernorSettingsUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/governance/extensions/GovernorSettingsUpgradeable.sol:15" + }, + { + "label": "_votingPeriod", + "offset": 0, + "slot": "304", + "type": "t_uint256", + "contract": "GovernorSettingsUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/governance/extensions/GovernorSettingsUpgradeable.sol:16" + }, + { + "label": "_proposalThreshold", + "offset": 0, + "slot": "305", + "type": "t_uint256", + "contract": "GovernorSettingsUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/governance/extensions/GovernorSettingsUpgradeable.sol:17" + }, + { + "label": "__gap", + "offset": 0, + "slot": "306", + "type": "t_array(t_uint256)47_storage", + "contract": "GovernorSettingsUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/governance/extensions/GovernorSettingsUpgradeable.sol:121" + }, + { + "label": "_owner", + "offset": 0, + "slot": "353", + "type": "t_address", + "contract": "OwnableUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol:22" + }, + { + "label": "__gap", + "offset": 0, + "slot": "354", + "type": "t_array(t_uint256)49_storage", + "contract": "OwnableUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol:94" + }, + { + "label": "spokeContractsMapping", + "offset": 0, + "slot": "403", + "type": "t_mapping(t_bytes32,t_mapping(t_uint16,t_bool))", + "contract": "CrossChainGovernorCountingSimple", + "src": "contracts/governance/CrossChainGovernorCountingSimple.sol:18" + }, + { + "label": "spokeContracts", + "offset": 0, + "slot": "404", + "type": "t_array(t_struct(CrossChainAddress)23540_storage)dyn_storage", + "contract": "CrossChainGovernorCountingSimple", + "src": "contracts/governance/CrossChainGovernorCountingSimple.sol:19" + }, + { + "label": "spokeContractsMappingSnapshots", + "offset": 0, + "slot": "405", + "type": "t_mapping(t_uint256,t_mapping(t_bytes32,t_mapping(t_uint16,t_bool)))", + "contract": "CrossChainGovernorCountingSimple", + "src": "contracts/governance/CrossChainGovernorCountingSimple.sol:20" + }, + { + "label": "spokeContractsSnapshots", + "offset": 0, + "slot": "406", + "type": "t_mapping(t_uint256,t_array(t_struct(CrossChainAddress)23540_storage)dyn_storage)", + "contract": "CrossChainGovernorCountingSimple", + "src": "contracts/governance/CrossChainGovernorCountingSimple.sol:22" + }, + { + "label": "spokeVotes", + "offset": 0, + "slot": "407", + "type": "t_mapping(t_uint256,t_mapping(t_bytes32,t_mapping(t_uint16,t_struct(SpokeProposalVote)23549_storage)))", + "contract": "CrossChainGovernorCountingSimple", + "src": "contracts/governance/CrossChainGovernorCountingSimple.sol:23" + }, + { + "label": "_proposalVotes", + "offset": 0, + "slot": "408", + "type": "t_mapping(t_uint256,t_struct(ProposalVote)23565_storage)", + "contract": "CrossChainGovernorCountingSimple", + "src": "contracts/governance/CrossChainGovernorCountingSimple.sol:25" + }, + { + "label": "token", + "offset": 0, + "slot": "409", + "type": "t_contract(IERC5805Upgradeable)4653", + "contract": "GovernorVotesUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/governance/extensions/GovernorVotesUpgradeable.sol:18" + }, + { + "label": "__gap", + "offset": 0, + "slot": "410", + "type": "t_array(t_uint256)50_storage", + "contract": "GovernorVotesUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/governance/extensions/GovernorVotesUpgradeable.sol:68" + }, + { + "label": "_quorumNumerator", + "offset": 0, + "slot": "460", + "type": "t_uint256", + "contract": "GovernorVotesQuorumFractionUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/governance/extensions/GovernorVotesQuorumFractionUpgradeable.sol:20" + }, + { + "label": "_quorumNumeratorHistory", + "offset": 0, + "slot": "461", + "type": "t_struct(Trace224)6249_storage", + "contract": "GovernorVotesQuorumFractionUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/governance/extensions/GovernorVotesQuorumFractionUpgradeable.sol:23", + "retypedFrom": "Checkpoints.History" + }, + { + "label": "__gap", + "offset": 0, + "slot": "462", + "type": "t_array(t_uint256)48_storage", + "contract": "GovernorVotesQuorumFractionUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/governance/extensions/GovernorVotesQuorumFractionUpgradeable.sol:132" + }, + { + "label": "_timelock", + "offset": 0, + "slot": "510", + "type": "t_contract(TimelockControllerUpgradeable)3488", + "contract": "GovernorTimelockControlUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/governance/extensions/GovernorTimelockControlUpgradeable.sol:28" + }, + { + "label": "_timelockIds", + "offset": 0, + "slot": "511", + "type": "t_mapping(t_uint256,t_bytes32)", + "contract": "GovernorTimelockControlUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/governance/extensions/GovernorTimelockControlUpgradeable.sol:29" + }, + { + "label": "__gap", + "offset": 0, + "slot": "512", + "type": "t_array(t_uint256)48_storage", + "contract": "GovernorTimelockControlUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/governance/extensions/GovernorTimelockControlUpgradeable.sol:177" + }, + { + "label": "_magistrate", + "offset": 0, + "slot": "560", + "type": "t_address", + "contract": "Magistrate", + "src": "contracts/governance/magistrate/Magistrate.sol:13" + }, + { + "label": "wormholeRelayer", + "offset": 0, + "slot": "561", + "type": "t_contract(IWormholeRelayer)26843", + "contract": "MetaHumanGovernor", + "src": "contracts/governance/MetaHumanGovernor.sol:46" + }, + { + "label": "secondsPerBlock", + "offset": 0, + "slot": "562", + "type": "t_uint256", + "contract": "MetaHumanGovernor", + "src": "contracts/governance/MetaHumanGovernor.sol:48" + }, + { + "label": "chainId", + "offset": 0, + "slot": "563", + "type": "t_uint16", + "contract": "MetaHumanGovernor", + "src": "contracts/governance/MetaHumanGovernor.sol:49" + }, + { + "label": "processedMessages", + "offset": 0, + "slot": "564", + "type": "t_mapping(t_bytes32,t_bool)", + "contract": "MetaHumanGovernor", + "src": "contracts/governance/MetaHumanGovernor.sol:51" + }, + { + "label": "collectionStarted", + "offset": 0, + "slot": "565", + "type": "t_mapping(t_uint256,t_bool)", + "contract": "MetaHumanGovernor", + "src": "contracts/governance/MetaHumanGovernor.sol:52" + }, + { + "label": "collectionFinished", + "offset": 0, + "slot": "566", + "type": "t_mapping(t_uint256,t_bool)", + "contract": "MetaHumanGovernor", + "src": "contracts/governance/MetaHumanGovernor.sol:53" + }, + { + "label": "__gap", + "offset": 0, + "slot": "567", + "type": "t_array(t_uint256)50_storage", + "contract": "MetaHumanGovernor", + "src": "contracts/governance/MetaHumanGovernor.sol:55" + } + ], + "types": { + "t_address": { + "label": "address", + "numberOfBytes": "20" + }, + "t_array(t_struct(Checkpoint224)6254_storage)dyn_storage": { + "label": "struct CheckpointsUpgradeable.Checkpoint224[]", + "numberOfBytes": "32" + }, + "t_array(t_struct(CrossChainAddress)23540_storage)dyn_storage": { + "label": "struct CrossChainGovernorCountingSimple.CrossChainAddress[]", + "numberOfBytes": "32" + }, + "t_array(t_uint256)46_storage": { + "label": "uint256[46]", + "numberOfBytes": "1472" + }, + "t_array(t_uint256)47_storage": { + "label": "uint256[47]", + "numberOfBytes": "1504" + }, + "t_array(t_uint256)48_storage": { + "label": "uint256[48]", + "numberOfBytes": "1536" + }, + "t_array(t_uint256)49_storage": { + "label": "uint256[49]", + "numberOfBytes": "1568" + }, + "t_array(t_uint256)50_storage": { + "label": "uint256[50]", + "numberOfBytes": "1600" + }, + "t_bool": { + "label": "bool", + "numberOfBytes": "1" + }, + "t_bytes24": { + "label": "bytes24", + "numberOfBytes": "24" + }, + "t_bytes32": { + "label": "bytes32", + "numberOfBytes": "32" + }, + "t_bytes4": { + "label": "bytes4", + "numberOfBytes": "4" + }, + "t_contract(IERC5805Upgradeable)4653": { + "label": "contract IERC5805Upgradeable", + "numberOfBytes": "20" + }, + "t_contract(IWormholeRelayer)26843": { + "label": "contract IWormholeRelayer", + "numberOfBytes": "20" + }, + "t_contract(TimelockControllerUpgradeable)3488": { + "label": "contract TimelockControllerUpgradeable", + "numberOfBytes": "20" + }, + "t_int128": { + "label": "int128", + "numberOfBytes": "16" + }, + "t_mapping(t_address,t_bool)": { + "label": "mapping(address => bool)", + "numberOfBytes": "32" + }, + "t_mapping(t_bytes32,t_bool)": { + "label": "mapping(bytes32 => bool)", + "numberOfBytes": "32" + }, + "t_mapping(t_bytes32,t_mapping(t_uint16,t_bool))": { + "label": "mapping(bytes32 => mapping(uint16 => bool))", + "numberOfBytes": "32" + }, + "t_mapping(t_bytes32,t_mapping(t_uint16,t_struct(SpokeProposalVote)23549_storage))": { + "label": "mapping(bytes32 => mapping(uint16 => struct CrossChainGovernorCountingSimple.SpokeProposalVote))", + "numberOfBytes": "32" + }, + "t_mapping(t_int128,t_bytes32)": { + "label": "mapping(int128 => bytes32)", + "numberOfBytes": "32" + }, + "t_mapping(t_uint16,t_bool)": { + "label": "mapping(uint16 => bool)", + "numberOfBytes": "32" + }, + "t_mapping(t_uint16,t_struct(SpokeProposalVote)23549_storage)": { + "label": "mapping(uint16 => struct CrossChainGovernorCountingSimple.SpokeProposalVote)", + "numberOfBytes": "32" + }, + "t_mapping(t_uint256,t_array(t_struct(CrossChainAddress)23540_storage)dyn_storage)": { + "label": "mapping(uint256 => struct CrossChainGovernorCountingSimple.CrossChainAddress[])", + "numberOfBytes": "32" + }, + "t_mapping(t_uint256,t_bool)": { + "label": "mapping(uint256 => bool)", + "numberOfBytes": "32" + }, + "t_mapping(t_uint256,t_bytes32)": { + "label": "mapping(uint256 => bytes32)", + "numberOfBytes": "32" + }, + "t_mapping(t_uint256,t_mapping(t_bytes32,t_mapping(t_uint16,t_bool)))": { + "label": "mapping(uint256 => mapping(bytes32 => mapping(uint16 => bool)))", + "numberOfBytes": "32" + }, + "t_mapping(t_uint256,t_mapping(t_bytes32,t_mapping(t_uint16,t_struct(SpokeProposalVote)23549_storage)))": { + "label": "mapping(uint256 => mapping(bytes32 => mapping(uint16 => struct CrossChainGovernorCountingSimple.SpokeProposalVote)))", + "numberOfBytes": "32" + }, + "t_mapping(t_uint256,t_struct(ProposalCore)600_storage)": { + "label": "mapping(uint256 => struct GovernorUpgradeable.ProposalCore)", + "numberOfBytes": "32" + }, + "t_mapping(t_uint256,t_struct(ProposalVote)23565_storage)": { + "label": "mapping(uint256 => struct CrossChainGovernorCountingSimple.ProposalVote)", + "numberOfBytes": "32" + }, + "t_string_storage": { + "label": "string", + "numberOfBytes": "32" + }, + "t_struct(Bytes32Deque)10879_storage": { + "label": "struct DoubleEndedQueueUpgradeable.Bytes32Deque", + "members": [ + { + "label": "_begin", + "type": "t_int128", + "offset": 0, + "slot": "0" + }, + { + "label": "_end", + "type": "t_int128", + "offset": 16, + "slot": "0" + }, + { + "label": "_data", + "type": "t_mapping(t_int128,t_bytes32)", + "offset": 0, + "slot": "1" + } + ], + "numberOfBytes": "64" + }, + "t_struct(Checkpoint224)6254_storage": { + "label": "struct CheckpointsUpgradeable.Checkpoint224", + "members": [ + { + "label": "_key", + "type": "t_uint32", + "offset": 0, + "slot": "0" + }, + { + "label": "_value", + "type": "t_uint224", + "offset": 4, + "slot": "0" + } + ], + "numberOfBytes": "32" + }, + "t_struct(CrossChainAddress)23540_storage": { + "label": "struct CrossChainGovernorCountingSimple.CrossChainAddress", + "members": [ + { + "label": "contractAddress", + "type": "t_bytes32", + "offset": 0, + "slot": "0" + }, + { + "label": "chainId", + "type": "t_uint16", + "offset": 0, + "slot": "1" + } + ], + "numberOfBytes": "64" + }, + "t_struct(ProposalCore)600_storage": { + "label": "struct GovernorUpgradeable.ProposalCore", + "members": [ + { + "label": "voteStart", + "type": "t_uint64", + "offset": 0, + "slot": "0" + }, + { + "label": "proposer", + "type": "t_address", + "offset": 8, + "slot": "0" + }, + { + "label": "__gap_unused0", + "type": "t_bytes4", + "offset": 28, + "slot": "0" + }, + { + "label": "voteEnd", + "type": "t_uint64", + "offset": 0, + "slot": "1" + }, + { + "label": "__gap_unused1", + "type": "t_bytes24", + "offset": 8, + "slot": "1" + }, + { + "label": "executed", + "type": "t_bool", + "offset": 0, + "slot": "2" + }, + { + "label": "canceled", + "type": "t_bool", + "offset": 1, + "slot": "2" + } + ], + "numberOfBytes": "96" + }, + "t_struct(ProposalVote)23565_storage": { + "label": "struct CrossChainGovernorCountingSimple.ProposalVote", + "members": [ + { + "label": "againstVotes", + "type": "t_uint256", + "offset": 0, + "slot": "0" + }, + { + "label": "forVotes", + "type": "t_uint256", + "offset": 0, + "slot": "1" + }, + { + "label": "abstainVotes", + "type": "t_uint256", + "offset": 0, + "slot": "2" + }, + { + "label": "hasVoted", + "type": "t_mapping(t_address,t_bool)", + "offset": 0, + "slot": "3" + } + ], + "numberOfBytes": "128" + }, + "t_struct(SpokeProposalVote)23549_storage": { + "label": "struct CrossChainGovernorCountingSimple.SpokeProposalVote", + "members": [ + { + "label": "forVotes", + "type": "t_uint256", + "offset": 0, + "slot": "0" + }, + { + "label": "againstVotes", + "type": "t_uint256", + "offset": 0, + "slot": "1" + }, + { + "label": "abstainVotes", + "type": "t_uint256", + "offset": 0, + "slot": "2" + }, + { + "label": "initialized", + "type": "t_bool", + "offset": 0, + "slot": "3" + } + ], + "numberOfBytes": "128" + }, + "t_struct(Trace224)6249_storage": { + "label": "struct CheckpointsUpgradeable.Trace224", + "members": [ + { + "label": "_checkpoints", + "type": "t_array(t_struct(Checkpoint224)6254_storage)dyn_storage", + "offset": 0, + "slot": "0" + } + ], + "numberOfBytes": "32" + }, + "t_uint16": { + "label": "uint16", + "numberOfBytes": "2" + }, + "t_uint224": { + "label": "uint224", + "numberOfBytes": "28" + }, + "t_uint256": { + "label": "uint256", + "numberOfBytes": "32" + }, + "t_uint32": { + "label": "uint32", + "numberOfBytes": "4" + }, + "t_uint64": { + "label": "uint64", + "numberOfBytes": "8" + }, + "t_uint8": { + "label": "uint8", + "numberOfBytes": "1" + } + }, + "namespaces": {} + } } } } diff --git a/packages/core/scripts/deploy-hub.ts b/packages/core/scripts/deploy-hub.ts index 3813cf9bd6..acfd1e30d4 100644 --- a/packages/core/scripts/deploy-hub.ts +++ b/packages/core/scripts/deploy-hub.ts @@ -55,19 +55,25 @@ async function main() { const quorumFraction = process.env.QUORUM_FRACTION ? parseInt(process.env.QUORUM_FRACTION) : 0; - const TimelockController = - await ethers.getContractFactory('TimelockController'); - const TimelockControllerContract = await TimelockController.deploy( - 1, - [], - [], - await deployer.getAddress() - ); - await TimelockControllerContract.waitForDeployment(); - console.log( - 'TimelockController Address:', - await TimelockControllerContract.getAddress() - ); + + const existingTimelockAddress = + process.env.HUB_TIMELOCK_CONTROLLER_ADDRESS || ''; + let timelockAddress = existingTimelockAddress; + if (timelockAddress) { + console.log('Using existing TimelockController Address:', timelockAddress); + } else { + const TimelockController = + await ethers.getContractFactory('TimelockController'); + const TimelockControllerContract = await TimelockController.deploy( + 1, + [], + [], + await deployer.getAddress() + ); + await TimelockControllerContract.waitForDeployment(); + timelockAddress = await TimelockControllerContract.getAddress(); + console.log('TimelockController Address:', timelockAddress); + } const MetaHumanGovernor = await ethers.getContractFactory( 'contracts/governance/MetaHumanGovernor.sol:MetaHumanGovernor' ); @@ -75,7 +81,7 @@ async function main() { MetaHumanGovernor, [ vhmTokenAddress, - await TimelockControllerContract.getAddress(), + timelockAddress, [], chainId, hubAutomaticRelayerAddress,