From 815933c0c85207e02ca6e163d2aa80170d946b17 Mon Sep 17 00:00:00 2001 From: Roman Kolpakov Date: Thu, 16 Jan 2025 14:14:02 +0300 Subject: [PATCH 01/20] feat: add helpers to create permission and set permission manager --- utils/permissions.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/utils/permissions.py b/utils/permissions.py index 4089f05ed..c6a5d1856 100644 --- a/utils/permissions.py +++ b/utils/permissions.py @@ -24,6 +24,18 @@ def encode_permission_revoke(target_app, permission_name, revoke_from) -> Tuple[ return acl.address, acl.revokePermission.encode_input(revoke_from, target_app, permission_id) +def encode_permission_set_manager(target_app, permission_name: str, grant_to: str) -> Tuple[str, str]: + acl = contracts.acl + permission_id = convert.to_uint(Web3.keccak(text=permission_name)) + return acl.address, acl.setPermissionManager.encode_input(grant_to, target_app, permission_id) + + +def encode_permission_create(target_app, permission_name: str, grant_to, manager) -> Tuple[str, str]: + acl = contracts.acl + permission_id = convert.to_uint(Web3.keccak(text=permission_name)) + return acl.address, acl.createPermission.encode_input(grant_to, target_app, permission_id, manager) + + def encode_permission_grant_p( target_app, permission_name: str, From 4f25ff70b6573bb6a1a6666d4fdb2f63af0d2832 Mon Sep 17 00:00:00 2001 From: Roman Kolpakov Date: Thu, 16 Jan 2025 14:14:41 +0300 Subject: [PATCH 02/20] feat: add allowed tokens registry to config --- configs/config_mainnet.py | 2 ++ utils/config.py | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/configs/config_mainnet.py b/configs/config_mainnet.py index 1c784fe60..4c66c5cd3 100644 --- a/configs/config_mainnet.py +++ b/configs/config_mainnet.py @@ -70,6 +70,8 @@ EASYTRACK_SIMPLE_DVT_CHANGE_NODE_OPERATOR_MANAGERS_FACTORY = "0xE31A0599A6772BCf9b2bFc9e25cf941e793c9a7D" EASYTRACK_CSM_SETTLE_EL_REWARDS_STEALING_PENALTY_FACTORY = "0xF6B6E7997338C48Ea3a8BCfa4BB64a315fDa76f4" +EASYTRACK_ALLOWED_TOKENS_REGISTRY = "0x4AC40c34f8992bb1e5E856A448792158022551ca" + # Multisigs FINANCE_MULTISIG = "0x48F300bD3C52c7dA6aAbDE4B683dEB27d38B9ABb" L1_EMERGENCY_BRAKES_MULTISIG = "0x73b047fe6337183A454c5217241D780a932777bD" diff --git a/utils/config.py b/utils/config.py index 767e86afc..c5c80115c 100644 --- a/utils/config.py +++ b/utils/config.py @@ -390,6 +390,10 @@ def trp_escrow_factory(self) -> interface.VestingEscrowFactory: @property def token_rate_notifier(self) -> interface.TokenRateNotifier: return interface.TokenRateNotifier(L1_TOKEN_RATE_NOTIFIER) + + @property + def allowed_tokens_registry(self) -> interface.AllowedTokensRegistry: + return interface.AllowedTokensRegistry(EASYTRACK_ALLOWED_TOKENS_REGISTRY) def __getattr__(name: str) -> Any: if name == "contracts": From 6bc0b07b1d9d02741557e4d55956822faa2cdf26 Mon Sep 17 00:00:00 2001 From: Roman Kolpakov Date: Thu, 16 Jan 2025 14:19:24 +0300 Subject: [PATCH 03/20] feat: add inteface for dg verifier --- interfaces/DualGovernanceVerifier.json | 611 +++++++++++++++++++++++++ 1 file changed, 611 insertions(+) create mode 100644 interfaces/DualGovernanceVerifier.json diff --git a/interfaces/DualGovernanceVerifier.json b/interfaces/DualGovernanceVerifier.json new file mode 100644 index 000000000..4a6b56628 --- /dev/null +++ b/interfaces/DualGovernanceVerifier.json @@ -0,0 +1,611 @@ +[ + { + "inputs": [ + { + "components": [ + { + "internalType": "Duration", + "name": "MIN_EXECUTION_DELAY", + "type": "uint32" + }, + { + "internalType": "Duration", + "name": "AFTER_SUBMIT_DELAY", + "type": "uint32" + }, + { + "internalType": "Duration", + "name": "MAX_AFTER_SUBMIT_DELAY", + "type": "uint32" + }, + { + "internalType": "Duration", + "name": "AFTER_SCHEDULE_DELAY", + "type": "uint32" + }, + { + "internalType": "Duration", + "name": "MAX_AFTER_SCHEDULE_DELAY", + "type": "uint32" + }, + { + "internalType": "Duration", + "name": "EMERGENCY_MODE_DURATION", + "type": "uint32" + }, + { + "internalType": "Duration", + "name": "MAX_EMERGENCY_MODE_DURATION", + "type": "uint32" + }, + { + "internalType": "Duration", + "name": "EMERGENCY_PROTECTION_DURATION", + "type": "uint32" + }, + { + "internalType": "Duration", + "name": "MAX_EMERGENCY_PROTECTION_DURATION", + "type": "uint32" + }, + { + "internalType": "address", + "name": "EMERGENCY_ACTIVATION_COMMITTEE", + "type": "address" + }, + { + "internalType": "address", + "name": "EMERGENCY_EXECUTION_COMMITTEE", + "type": "address" + }, + { + "internalType": "address", + "name": "RESEAL_COMMITTEE", + "type": "address" + }, + { + "internalType": "uint256", + "name": "MIN_WITHDRAWALS_BATCH_SIZE", + "type": "uint256" + }, + { + "components": [ + { + "internalType": "Duration", + "name": "activationTimeout", + "type": "uint32" + }, + { + "internalType": "Duration", + "name": "minActivationTimeout", + "type": "uint32" + }, + { + "internalType": "Duration", + "name": "maxActivationTimeout", + "type": "uint32" + }, + { + "internalType": "Duration", + "name": "executionDelay", + "type": "uint32" + }, + { + "components": [ + { + "internalType": "address[]", + "name": "members", + "type": "address[]" + }, + { + "internalType": "uint256", + "name": "quorum", + "type": "uint256" + } + ], + "internalType": "struct TiebreakerSubCommitteeDeployConfig", + "name": "influencers", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "address[]", + "name": "members", + "type": "address[]" + }, + { + "internalType": "uint256", + "name": "quorum", + "type": "uint256" + } + ], + "internalType": "struct TiebreakerSubCommitteeDeployConfig", + "name": "nodeOperators", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "address[]", + "name": "members", + "type": "address[]" + }, + { + "internalType": "uint256", + "name": "quorum", + "type": "uint256" + } + ], + "internalType": "struct TiebreakerSubCommitteeDeployConfig", + "name": "protocols", + "type": "tuple" + }, + { + "internalType": "uint256", + "name": "quorum", + "type": "uint256" + }, + { + "internalType": "address[]", + "name": "sealableWithdrawalBlockers", + "type": "address[]" + } + ], + "internalType": "struct TiebreakerDeployConfig", + "name": "tiebreakerConfig", + "type": "tuple" + }, + { + "internalType": "uint256", + "name": "MAX_SEALABLE_WITHDRAWAL_BLOCKERS_COUNT", + "type": "uint256" + }, + { + "internalType": "PercentD16", + "name": "FIRST_SEAL_RAGE_QUIT_SUPPORT", + "type": "uint128" + }, + { + "internalType": "PercentD16", + "name": "SECOND_SEAL_RAGE_QUIT_SUPPORT", + "type": "uint128" + }, + { + "internalType": "Duration", + "name": "MIN_ASSETS_LOCK_DURATION", + "type": "uint32" + }, + { + "internalType": "Duration", + "name": "MAX_MIN_ASSETS_LOCK_DURATION", + "type": "uint32" + }, + { + "internalType": "Duration", + "name": "VETO_SIGNALLING_MIN_DURATION", + "type": "uint32" + }, + { + "internalType": "Duration", + "name": "VETO_SIGNALLING_MAX_DURATION", + "type": "uint32" + }, + { + "internalType": "Duration", + "name": "VETO_SIGNALLING_MIN_ACTIVE_DURATION", + "type": "uint32" + }, + { + "internalType": "Duration", + "name": "VETO_SIGNALLING_DEACTIVATION_MAX_DURATION", + "type": "uint32" + }, + { + "internalType": "Duration", + "name": "VETO_COOLDOWN_DURATION", + "type": "uint32" + }, + { + "internalType": "Duration", + "name": "RAGE_QUIT_EXTENSION_PERIOD_DURATION", + "type": "uint32" + }, + { + "internalType": "Duration", + "name": "RAGE_QUIT_ETH_WITHDRAWALS_MIN_DELAY", + "type": "uint32" + }, + { + "internalType": "Duration", + "name": "RAGE_QUIT_ETH_WITHDRAWALS_MAX_DELAY", + "type": "uint32" + }, + { + "internalType": "Duration", + "name": "RAGE_QUIT_ETH_WITHDRAWALS_DELAY_GROWTH", + "type": "uint32" + }, + { + "internalType": "address", + "name": "TEMPORARY_EMERGENCY_GOVERNANCE_PROPOSER", + "type": "address" + } + ], + "internalType": "struct DeployConfig", + "name": "config", + "type": "tuple" + }, + { + "components": [ + { "internalType": "uint256", "name": "chainId", "type": "uint256" }, + { + "internalType": "contract IStETH", + "name": "stETH", + "type": "address" + }, + { + "internalType": "contract IWstETH", + "name": "wstETH", + "type": "address" + }, + { + "internalType": "contract IWithdrawalQueue", + "name": "withdrawalQueue", + "type": "address" + }, + { "internalType": "address", "name": "voting", "type": "address" } + ], + "internalType": "struct LidoContracts", + "name": "lidoAddresses", + "type": "tuple" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { "inputs": [], "name": "DurationOverflow", "type": "error" }, + { "inputs": [], "name": "TimestampOverflow", "type": "error" }, + { "anonymous": false, "inputs": [], "name": "Verified", "type": "event" }, + { + "inputs": [], + "name": "getConfig", + "outputs": [ + { + "components": [ + { + "internalType": "Duration", + "name": "MIN_EXECUTION_DELAY", + "type": "uint32" + }, + { + "internalType": "Duration", + "name": "AFTER_SUBMIT_DELAY", + "type": "uint32" + }, + { + "internalType": "Duration", + "name": "MAX_AFTER_SUBMIT_DELAY", + "type": "uint32" + }, + { + "internalType": "Duration", + "name": "AFTER_SCHEDULE_DELAY", + "type": "uint32" + }, + { + "internalType": "Duration", + "name": "MAX_AFTER_SCHEDULE_DELAY", + "type": "uint32" + }, + { + "internalType": "Duration", + "name": "EMERGENCY_MODE_DURATION", + "type": "uint32" + }, + { + "internalType": "Duration", + "name": "MAX_EMERGENCY_MODE_DURATION", + "type": "uint32" + }, + { + "internalType": "Duration", + "name": "EMERGENCY_PROTECTION_DURATION", + "type": "uint32" + }, + { + "internalType": "Duration", + "name": "MAX_EMERGENCY_PROTECTION_DURATION", + "type": "uint32" + }, + { + "internalType": "address", + "name": "EMERGENCY_ACTIVATION_COMMITTEE", + "type": "address" + }, + { + "internalType": "address", + "name": "EMERGENCY_EXECUTION_COMMITTEE", + "type": "address" + }, + { + "internalType": "address", + "name": "RESEAL_COMMITTEE", + "type": "address" + }, + { + "internalType": "uint256", + "name": "MIN_WITHDRAWALS_BATCH_SIZE", + "type": "uint256" + }, + { + "components": [ + { + "internalType": "Duration", + "name": "activationTimeout", + "type": "uint32" + }, + { + "internalType": "Duration", + "name": "minActivationTimeout", + "type": "uint32" + }, + { + "internalType": "Duration", + "name": "maxActivationTimeout", + "type": "uint32" + }, + { + "internalType": "Duration", + "name": "executionDelay", + "type": "uint32" + }, + { + "components": [ + { + "internalType": "address[]", + "name": "members", + "type": "address[]" + }, + { + "internalType": "uint256", + "name": "quorum", + "type": "uint256" + } + ], + "internalType": "struct TiebreakerSubCommitteeDeployConfig", + "name": "influencers", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "address[]", + "name": "members", + "type": "address[]" + }, + { + "internalType": "uint256", + "name": "quorum", + "type": "uint256" + } + ], + "internalType": "struct TiebreakerSubCommitteeDeployConfig", + "name": "nodeOperators", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "address[]", + "name": "members", + "type": "address[]" + }, + { + "internalType": "uint256", + "name": "quorum", + "type": "uint256" + } + ], + "internalType": "struct TiebreakerSubCommitteeDeployConfig", + "name": "protocols", + "type": "tuple" + }, + { + "internalType": "uint256", + "name": "quorum", + "type": "uint256" + }, + { + "internalType": "address[]", + "name": "sealableWithdrawalBlockers", + "type": "address[]" + } + ], + "internalType": "struct TiebreakerDeployConfig", + "name": "tiebreakerConfig", + "type": "tuple" + }, + { + "internalType": "uint256", + "name": "MAX_SEALABLE_WITHDRAWAL_BLOCKERS_COUNT", + "type": "uint256" + }, + { + "internalType": "PercentD16", + "name": "FIRST_SEAL_RAGE_QUIT_SUPPORT", + "type": "uint128" + }, + { + "internalType": "PercentD16", + "name": "SECOND_SEAL_RAGE_QUIT_SUPPORT", + "type": "uint128" + }, + { + "internalType": "Duration", + "name": "MIN_ASSETS_LOCK_DURATION", + "type": "uint32" + }, + { + "internalType": "Duration", + "name": "MAX_MIN_ASSETS_LOCK_DURATION", + "type": "uint32" + }, + { + "internalType": "Duration", + "name": "VETO_SIGNALLING_MIN_DURATION", + "type": "uint32" + }, + { + "internalType": "Duration", + "name": "VETO_SIGNALLING_MAX_DURATION", + "type": "uint32" + }, + { + "internalType": "Duration", + "name": "VETO_SIGNALLING_MIN_ACTIVE_DURATION", + "type": "uint32" + }, + { + "internalType": "Duration", + "name": "VETO_SIGNALLING_DEACTIVATION_MAX_DURATION", + "type": "uint32" + }, + { + "internalType": "Duration", + "name": "VETO_COOLDOWN_DURATION", + "type": "uint32" + }, + { + "internalType": "Duration", + "name": "RAGE_QUIT_EXTENSION_PERIOD_DURATION", + "type": "uint32" + }, + { + "internalType": "Duration", + "name": "RAGE_QUIT_ETH_WITHDRAWALS_MIN_DELAY", + "type": "uint32" + }, + { + "internalType": "Duration", + "name": "RAGE_QUIT_ETH_WITHDRAWALS_MAX_DELAY", + "type": "uint32" + }, + { + "internalType": "Duration", + "name": "RAGE_QUIT_ETH_WITHDRAWALS_DELAY_GROWTH", + "type": "uint32" + }, + { + "internalType": "address", + "name": "TEMPORARY_EMERGENCY_GOVERNANCE_PROPOSER", + "type": "address" + } + ], + "internalType": "struct DeployConfig", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getLidoAddresses", + "outputs": [ + { + "components": [ + { "internalType": "uint256", "name": "chainId", "type": "uint256" }, + { + "internalType": "contract IStETH", + "name": "stETH", + "type": "address" + }, + { + "internalType": "contract IWstETH", + "name": "wstETH", + "type": "address" + }, + { + "internalType": "contract IWithdrawalQueue", + "name": "withdrawalQueue", + "type": "address" + }, + { "internalType": "address", "name": "voting", "type": "address" } + ], + "internalType": "struct LidoContracts", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "contract Executor", + "name": "adminExecutor", + "type": "address" + }, + { + "internalType": "contract IEmergencyProtectedTimelock", + "name": "timelock", + "type": "address" + }, + { + "internalType": "contract TimelockedGovernance", + "name": "emergencyGovernance", + "type": "address" + }, + { + "internalType": "contract ResealManager", + "name": "resealManager", + "type": "address" + }, + { + "internalType": "contract DualGovernance", + "name": "dualGovernance", + "type": "address" + }, + { + "internalType": "contract TiebreakerCoreCommittee", + "name": "tiebreakerCoreCommittee", + "type": "address" + }, + { + "internalType": "contract TiebreakerSubCommittee", + "name": "tiebreakerSubCommitteeInfluencers", + "type": "address" + }, + { + "internalType": "contract TiebreakerSubCommittee", + "name": "tiebreakerSubCommitteeNodeOperators", + "type": "address" + }, + { + "internalType": "contract TiebreakerSubCommittee", + "name": "tiebreakerSubCommitteeProtocols", + "type": "address" + }, + { + "internalType": "contract TimelockedGovernance", + "name": "temporaryEmergencyGovernance", + "type": "address" + } + ], + "internalType": "struct DeployedContracts", + "name": "dgContracts", + "type": "tuple" + }, + { "internalType": "bool", "name": "onchainVotingCheck", "type": "bool" } + ], + "name": "verify", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] From cb168cf2564a54f32cd0c3a7b6406661a29354d2 Mon Sep 17 00:00:00 2001 From: Roman Kolpakov Date: Thu, 16 Jan 2025 14:50:57 +0300 Subject: [PATCH 04/20] feat: add dual governance contracts to config --- configs/config_holesky.py | 5 + configs/config_mainnet.py | 5 + interfaces/DualGovernance.json | 1117 +++++++++++++++++++ interfaces/DualGovernanceAdminExecutor.json | 142 +++ utils/config.py | 12 + 5 files changed, 1281 insertions(+) create mode 100644 interfaces/DualGovernance.json create mode 100644 interfaces/DualGovernanceAdminExecutor.json diff --git a/configs/config_holesky.py b/configs/config_holesky.py index c9b22258d..3843d2113 100644 --- a/configs/config_holesky.py +++ b/configs/config_holesky.py @@ -48,6 +48,11 @@ WITHDRAWAL_VAULT = "0xF0179dEC45a37423EAD4FaD5fCb136197872EAd9" +# Dual Governance +DUAL_GOVERNANCE = "0x9F14118Fc548658660a40B351C782a22e9937b42" +DUAL_GOVERNANCE_ADMIN_EXECUTOR = "0x936C1dC7d5fAD05E5aD9aBc48b4ab09B88850f04" +DUAL_GOVERNANCE_VERIFIER = "0xd67DF125fDC3360DeCB880804D1FA3Ae3fC6FFF1" + # EasyTracks EASYTRACK = "0x1763b9ED3586B08AE796c7787811a2E1bc16163a" diff --git a/configs/config_mainnet.py b/configs/config_mainnet.py index 4c66c5cd3..ae9927095 100644 --- a/configs/config_mainnet.py +++ b/configs/config_mainnet.py @@ -54,6 +54,11 @@ DEPOSIT_SECURITY_MODULE_V1 = "0x710B3303fB508a84F10793c1106e32bE873C24cd" WITHDRAWAL_VAULT = "0xB9D7934878B5FB9610B3fE8A5e441e8fad7E293f" +# Dual Governance +DUAL_GOVERNANCE = "0x0000000000000000000000000000000000000000" +DUAL_GOVERNANCE_ADMIN_EXECUTOR = "0x0000000000000000000000000000000000000000" +DUAL_GOVERNANCE_VERIFIER = "0x0000000000000000000000000000000000000000" + # EasyTracks EASYTRACK = "0xF0211b7660680B49De1A7E9f25C65660F0a13Fea" diff --git a/interfaces/DualGovernance.json b/interfaces/DualGovernance.json new file mode 100644 index 000000000..9f9c30d40 --- /dev/null +++ b/interfaces/DualGovernance.json @@ -0,0 +1,1117 @@ +[ + { + "inputs": [ + { + "components": [ + { + "internalType": "contract ITimelock", + "name": "timelock", + "type": "address" + }, + { + "internalType": "contract IResealManager", + "name": "resealManager", + "type": "address" + }, + { + "internalType": "contract IDualGovernanceConfigProvider", + "name": "configProvider", + "type": "address" + } + ], + "internalType": "struct DualGovernance.DualGovernanceComponents", + "name": "components", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "contract IStETH", + "name": "stETH", + "type": "address" + }, + { + "internalType": "contract IWstETH", + "name": "wstETH", + "type": "address" + }, + { + "internalType": "contract IWithdrawalQueue", + "name": "withdrawalQueue", + "type": "address" + } + ], + "internalType": "struct DualGovernance.SignallingTokens", + "name": "signallingTokens", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "minWithdrawalsBatchSize", + "type": "uint256" + }, + { + "internalType": "Duration", + "name": "minTiebreakerActivationTimeout", + "type": "uint32" + }, + { + "internalType": "Duration", + "name": "maxTiebreakerActivationTimeout", + "type": "uint32" + }, + { + "internalType": "uint256", + "name": "maxSealableWithdrawalBlockersCount", + "type": "uint256" + }, + { + "internalType": "Duration", + "name": "maxMinAssetsLockDuration", + "type": "uint32" + } + ], + "internalType": "struct DualGovernance.SanityCheckParams", + "name": "sanityCheckParams", + "type": "tuple" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { "inputs": [], "name": "AlreadyInitialized", "type": "error" }, + { + "inputs": [ + { "internalType": "address", "name": "caller", "type": "address" } + ], + "name": "CallerIsNotAdminExecutor", + "type": "error" + }, + { + "inputs": [ + { "internalType": "address", "name": "caller", "type": "address" } + ], + "name": "CallerIsNotProposalsCanceller", + "type": "error" + }, + { + "inputs": [ + { "internalType": "address", "name": "caller", "type": "address" } + ], + "name": "CallerIsNotResealCommittee", + "type": "error" + }, + { + "inputs": [ + { "internalType": "address", "name": "caller", "type": "address" } + ], + "name": "CallerIsNotTiebreakerCommittee", + "type": "error" + }, + { "inputs": [], "name": "DurationOverflow", "type": "error" }, + { "inputs": [], "name": "DurationUnderflow", "type": "error" }, + { "inputs": [], "name": "ERC1167FailedCreateClone", "type": "error" }, + { + "inputs": [ + { "internalType": "address", "name": "executor", "type": "address" } + ], + "name": "ExecutorNotRegistered", + "type": "error" + }, + { "inputs": [], "name": "IndexOneBasedOverflow", "type": "error" }, + { "inputs": [], "name": "IndexOneBasedUnderflow", "type": "error" }, + { + "inputs": [ + { + "internalType": "contract IDualGovernanceConfigProvider", + "name": "configProvider", + "type": "address" + } + ], + "name": "InvalidConfigProvider", + "type": "error" + }, + { + "inputs": [ + { "internalType": "address", "name": "executor", "type": "address" } + ], + "name": "InvalidExecutor", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "Duration", + "name": "minAssetsLockDuration", + "type": "uint32" + } + ], + "name": "InvalidMinAssetsLockDuration", + "type": "error" + }, + { + "inputs": [ + { "internalType": "address", "name": "canceller", "type": "address" } + ], + "name": "InvalidProposalsCanceller", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "proposerAccount", + "type": "address" + } + ], + "name": "InvalidProposerAccount", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "Duration", + "name": "rageQuitEthWithdrawalsMinDelay", + "type": "uint32" + }, + { + "internalType": "Duration", + "name": "rageQuitEthWithdrawalsMaxDelay", + "type": "uint32" + } + ], + "name": "InvalidRageQuitEthWithdrawalsDelayRange", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "PercentD16", + "name": "firstSealRageQuitSupport", + "type": "uint128" + }, + { + "internalType": "PercentD16", + "name": "secondSealRageQuitSupport", + "type": "uint128" + } + ], + "name": "InvalidRageQuitSupportRange", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "resealCommittee", + "type": "address" + } + ], + "name": "InvalidResealCommittee", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "contract IResealManager", + "name": "resealManager", + "type": "address" + } + ], + "name": "InvalidResealManager", + "type": "error" + }, + { + "inputs": [ + { "internalType": "address", "name": "sealable", "type": "address" } + ], + "name": "InvalidSealable", + "type": "error" + }, + { + "inputs": [ + { "internalType": "Duration", "name": "timeout", "type": "uint32" } + ], + "name": "InvalidTiebreakerActivationTimeout", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "Duration", + "name": "minTiebreakerActivationTimeout", + "type": "uint32" + }, + { + "internalType": "Duration", + "name": "maxTiebreakerActivationTimeout", + "type": "uint32" + } + ], + "name": "InvalidTiebreakerActivationTimeoutBounds", + "type": "error" + }, + { + "inputs": [ + { "internalType": "address", "name": "account", "type": "address" } + ], + "name": "InvalidTiebreakerCommittee", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "Duration", + "name": "vetoSignallingMinDuration", + "type": "uint32" + }, + { + "internalType": "Duration", + "name": "vetoSignallingMaxDuration", + "type": "uint32" + } + ], + "name": "InvalidVetoSignallingDurationRange", + "type": "error" + }, + { "inputs": [], "name": "PercentD16Underflow", "type": "error" }, + { + "inputs": [ + { "internalType": "uint256", "name": "proposalId", "type": "uint256" } + ], + "name": "ProposalSchedulingBlocked", + "type": "error" + }, + { "inputs": [], "name": "ProposalSubmissionBlocked", "type": "error" }, + { + "inputs": [ + { + "internalType": "address", + "name": "proposerAccount", + "type": "address" + } + ], + "name": "ProposerAlreadyRegistered", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "proposerAccount", + "type": "address" + } + ], + "name": "ProposerNotRegistered", + "type": "error" + }, + { "inputs": [], "name": "ResealIsNotAllowedInNormalState", "type": "error" }, + { + "inputs": [ + { "internalType": "address", "name": "sealable", "type": "address" } + ], + "name": "SealableWithdrawalBlockerAlreadyAdded", + "type": "error" + }, + { + "inputs": [ + { "internalType": "address", "name": "sealable", "type": "address" } + ], + "name": "SealableWithdrawalBlockerNotFound", + "type": "error" + }, + { + "inputs": [], + "name": "SealableWithdrawalBlockersLimitReached", + "type": "error" + }, + { "inputs": [], "name": "TiebreakNotAllowed", "type": "error" }, + { "inputs": [], "name": "TimestampOverflow", "type": "error" }, + { + "anonymous": false, + "inputs": [], + "name": "CancelAllPendingProposalsExecuted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [], + "name": "CancelAllPendingProposalsSkipped", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "contract IDualGovernanceConfigProvider", + "name": "newConfigProvider", + "type": "address" + } + ], + "name": "ConfigProviderSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "enum State", + "name": "from", + "type": "uint8" + }, + { + "indexed": true, + "internalType": "enum State", + "name": "to", + "type": "uint8" + }, + { + "components": [ + { "internalType": "enum State", "name": "state", "type": "uint8" }, + { + "internalType": "Timestamp", + "name": "enteredAt", + "type": "uint40" + }, + { + "internalType": "Timestamp", + "name": "vetoSignallingActivatedAt", + "type": "uint40" + }, + { + "internalType": "contract ISignallingEscrow", + "name": "signallingEscrow", + "type": "address" + }, + { "internalType": "uint8", "name": "rageQuitRound", "type": "uint8" }, + { + "internalType": "Timestamp", + "name": "vetoSignallingReactivationTime", + "type": "uint40" + }, + { + "internalType": "Timestamp", + "name": "normalOrVetoCooldownExitedAt", + "type": "uint40" + }, + { + "internalType": "contract IRageQuitEscrow", + "name": "rageQuitEscrow", + "type": "address" + }, + { + "internalType": "contract IDualGovernanceConfigProvider", + "name": "configProvider", + "type": "address" + } + ], + "indexed": false, + "internalType": "struct DualGovernanceStateMachine.Context", + "name": "state", + "type": "tuple" + } + ], + "name": "DualGovernanceStateChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "contract IEscrowBase", + "name": "escrowMasterCopy", + "type": "address" + } + ], + "name": "EscrowMasterCopyDeployed", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "contract ISignallingEscrow", + "name": "escrow", + "type": "address" + } + ], + "name": "NewSignallingEscrowDeployed", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "proposerAccount", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "proposalId", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "string", + "name": "metadata", + "type": "string" + } + ], + "name": "ProposalSubmitted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "proposalsCanceller", + "type": "address" + } + ], + "name": "ProposalsCancellerSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "proposerAccount", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "executor", + "type": "address" + } + ], + "name": "ProposerExecutorSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "proposerAccount", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "executor", + "type": "address" + } + ], + "name": "ProposerRegistered", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "proposerAccount", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "executor", + "type": "address" + } + ], + "name": "ProposerUnregistered", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "resealCommittee", + "type": "address" + } + ], + "name": "ResealCommitteeSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "contract IResealManager", + "name": "resealManager", + "type": "address" + } + ], + "name": "ResealManagerSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "sealable", + "type": "address" + } + ], + "name": "SealableWithdrawalBlockerAdded", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "sealable", + "type": "address" + } + ], + "name": "SealableWithdrawalBlockerRemoved", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "Duration", + "name": "newTiebreakerActivationTimeout", + "type": "uint32" + } + ], + "name": "TiebreakerActivationTimeoutSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "newTiebreakerCommittee", + "type": "address" + } + ], + "name": "TiebreakerCommitteeSet", + "type": "event" + }, + { + "inputs": [], + "name": "MAX_SEALABLE_WITHDRAWAL_BLOCKERS_COUNT", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "MAX_TIEBREAKER_ACTIVATION_TIMEOUT", + "outputs": [{ "internalType": "Duration", "name": "", "type": "uint32" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "MIN_TIEBREAKER_ACTIVATION_TIMEOUT", + "outputs": [{ "internalType": "Duration", "name": "", "type": "uint32" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "TIMELOCK", + "outputs": [ + { "internalType": "contract ITimelock", "name": "", "type": "address" } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "activateNextState", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "sealableWithdrawalBlocker", + "type": "address" + } + ], + "name": "addTiebreakerSealableWithdrawalBlocker", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "canCancelAllPendingProposals", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "proposalId", "type": "uint256" } + ], + "name": "canScheduleProposal", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "canSubmitProposal", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "cancelAllPendingProposals", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "getConfigProvider", + "outputs": [ + { + "internalType": "contract IDualGovernanceConfigProvider", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getEffectiveState", + "outputs": [ + { + "internalType": "enum State", + "name": "effectiveState", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getPersistedState", + "outputs": [ + { + "internalType": "enum State", + "name": "persistedState", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getProposalsCanceller", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "proposerAccount", + "type": "address" + } + ], + "name": "getProposer", + "outputs": [ + { + "components": [ + { "internalType": "address", "name": "account", "type": "address" }, + { "internalType": "address", "name": "executor", "type": "address" } + ], + "internalType": "struct Proposers.Proposer", + "name": "proposer", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getProposers", + "outputs": [ + { + "components": [ + { "internalType": "address", "name": "account", "type": "address" }, + { "internalType": "address", "name": "executor", "type": "address" } + ], + "internalType": "struct Proposers.Proposer[]", + "name": "proposers", + "type": "tuple[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getRageQuitEscrow", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getResealCommittee", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getResealManager", + "outputs": [ + { + "internalType": "contract IResealManager", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getStateDetails", + "outputs": [ + { + "components": [ + { + "internalType": "enum State", + "name": "effectiveState", + "type": "uint8" + }, + { + "internalType": "enum State", + "name": "persistedState", + "type": "uint8" + }, + { + "internalType": "Timestamp", + "name": "persistedStateEnteredAt", + "type": "uint40" + }, + { + "internalType": "Timestamp", + "name": "vetoSignallingActivatedAt", + "type": "uint40" + }, + { + "internalType": "Timestamp", + "name": "vetoSignallingReactivationTime", + "type": "uint40" + }, + { + "internalType": "Timestamp", + "name": "normalOrVetoCooldownExitedAt", + "type": "uint40" + }, + { + "internalType": "uint256", + "name": "rageQuitRound", + "type": "uint256" + }, + { + "internalType": "Duration", + "name": "vetoSignallingDuration", + "type": "uint32" + } + ], + "internalType": "struct IDualGovernance.StateDetails", + "name": "stateDetails", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getTiebreakerDetails", + "outputs": [ + { + "components": [ + { "internalType": "bool", "name": "isTie", "type": "bool" }, + { + "internalType": "address", + "name": "tiebreakerCommittee", + "type": "address" + }, + { + "internalType": "Duration", + "name": "tiebreakerActivationTimeout", + "type": "uint32" + }, + { + "internalType": "address[]", + "name": "sealableWithdrawalBlockers", + "type": "address[]" + } + ], + "internalType": "struct ITiebreaker.TiebreakerDetails", + "name": "tiebreakerState", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getVetoSignallingEscrow", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "executor", "type": "address" } + ], + "name": "isExecutor", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "proposerAccount", + "type": "address" + } + ], + "name": "isProposer", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "proposerAccount", + "type": "address" + }, + { "internalType": "address", "name": "executor", "type": "address" } + ], + "name": "registerProposer", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "sealableWithdrawalBlocker", + "type": "address" + } + ], + "name": "removeTiebreakerSealableWithdrawalBlocker", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "sealable", "type": "address" } + ], + "name": "resealSealable", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "proposalId", "type": "uint256" } + ], + "name": "scheduleProposal", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract IDualGovernanceConfigProvider", + "name": "newConfigProvider", + "type": "address" + } + ], + "name": "setConfigProvider", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newProposalsCanceller", + "type": "address" + } + ], + "name": "setProposalsCanceller", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "proposerAccount", + "type": "address" + }, + { "internalType": "address", "name": "newExecutor", "type": "address" } + ], + "name": "setProposerExecutor", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newResealCommittee", + "type": "address" + } + ], + "name": "setResealCommittee", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract IResealManager", + "name": "newResealManager", + "type": "address" + } + ], + "name": "setResealManager", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "Duration", + "name": "newTiebreakerActivationTimeout", + "type": "uint32" + } + ], + "name": "setTiebreakerActivationTimeout", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newTiebreakerCommittee", + "type": "address" + } + ], + "name": "setTiebreakerCommittee", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { "internalType": "address", "name": "target", "type": "address" }, + { "internalType": "uint96", "name": "value", "type": "uint96" }, + { "internalType": "bytes", "name": "payload", "type": "bytes" } + ], + "internalType": "struct ExternalCall[]", + "name": "calls", + "type": "tuple[]" + }, + { "internalType": "string", "name": "metadata", "type": "string" } + ], + "name": "submitProposal", + "outputs": [ + { "internalType": "uint256", "name": "proposalId", "type": "uint256" } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "sealable", "type": "address" } + ], + "name": "tiebreakerResumeSealable", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "proposalId", "type": "uint256" } + ], + "name": "tiebreakerScheduleProposal", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "proposerAccount", + "type": "address" + } + ], + "name": "unregisterProposer", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/interfaces/DualGovernanceAdminExecutor.json b/interfaces/DualGovernanceAdminExecutor.json new file mode 100644 index 000000000..ccf0c519b --- /dev/null +++ b/interfaces/DualGovernanceAdminExecutor.json @@ -0,0 +1,142 @@ +[ + { + "inputs": [ + { "internalType": "address", "name": "owner", "type": "address" } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [ + { "internalType": "address", "name": "target", "type": "address" } + ], + "name": "AddressEmptyCode", + "type": "error" + }, + { + "inputs": [ + { "internalType": "address", "name": "account", "type": "address" } + ], + "name": "AddressInsufficientBalance", + "type": "error" + }, + { "inputs": [], "name": "FailedInnerCall", "type": "error" }, + { + "inputs": [ + { "internalType": "address", "name": "owner", "type": "address" } + ], + "name": "OwnableInvalidOwner", + "type": "error" + }, + { + "inputs": [ + { "internalType": "address", "name": "account", "type": "address" } + ], + "name": "OwnableUnauthorizedAccount", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "ETHReceived", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "target", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "ethValue", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "bytes", + "name": "data", + "type": "bytes" + }, + { + "indexed": false, + "internalType": "bytes", + "name": "returndata", + "type": "bytes" + } + ], + "name": "Executed", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "inputs": [ + { "internalType": "address", "name": "target", "type": "address" }, + { "internalType": "uint256", "name": "value", "type": "uint256" }, + { "internalType": "bytes", "name": "payload", "type": "bytes" } + ], + "name": "execute", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "newOwner", "type": "address" } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { "stateMutability": "payable", "type": "receive" } +] diff --git a/utils/config.py b/utils/config.py index c5c80115c..3992b8ef7 100644 --- a/utils/config.py +++ b/utils/config.py @@ -395,6 +395,18 @@ def token_rate_notifier(self) -> interface.TokenRateNotifier: def allowed_tokens_registry(self) -> interface.AllowedTokensRegistry: return interface.AllowedTokensRegistry(EASYTRACK_ALLOWED_TOKENS_REGISTRY) + @property + def dual_governance(self) -> interface.DualGovernance: + return interface.DualGovernance(DUAL_GOVERNANCE) + + @property + def dual_governance_admin_executor(self) -> interface.DualGovernanceAdminExecutor: + return interface.DualGovernanceAdminExecutor(DUAL_GOVERNANCE_ADMIN_EXECUTOR) + + @property + def dual_governance_verifier(self) -> interface.DualGovernanceVerifier: + return interface.DualGovernanceVerifier(DUAL_GOVERNANCE_VERIFIER) + def __getattr__(name: str) -> Any: if name == "contracts": return ContractsLazyLoader() From ede3fa263b0a8f933b6c0d36ff7594fae49027e9 Mon Sep 17 00:00:00 2001 From: Roman Kolpakov Date: Thu, 16 Jan 2025 17:44:50 +0300 Subject: [PATCH 05/20] feat: add allowed tokens registry for holesky --- configs/config_holesky.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/configs/config_holesky.py b/configs/config_holesky.py index 3843d2113..f858f8c81 100644 --- a/configs/config_holesky.py +++ b/configs/config_holesky.py @@ -68,6 +68,8 @@ EASYTRACK_SIMPLE_DVT_UPDATE_TARGET_VALIDATOR_LIMITS_FACTORY = "0xC91a676A69Eb49be9ECa1954fE6fc861AE07A9A2" EASYTRACK_SIMPLE_DVT_CHANGE_NODE_OPERATOR_MANAGERS_FACTORY = "0xb8C4728bc0826bA5864D02FA53148de7A44C2f7E" +EASYTRACK_ALLOWED_TOKENS_REGISTRY = "0x091C0eC8B4D54a9fcB36269B5D5E5AF43309e666" + # Multisigs FINANCE_MULTISIG = "" From 7917d33eae6f6c7130e923d4c3fbdc5ee95e5166 Mon Sep 17 00:00:00 2001 From: Roman Kolpakov Date: Thu, 16 Jan 2025 17:45:15 +0300 Subject: [PATCH 06/20] feat: add upgrade script for holesky dry-run --- interfaces/Foo.json | 16 ++ scripts/dual_governance_upgrade_holesky.py | 170 ++++++++++++++++++ tests/dual_governance_upgrade_holesky_test.py | 63 +++++++ 3 files changed, 249 insertions(+) create mode 100644 interfaces/Foo.json create mode 100644 scripts/dual_governance_upgrade_holesky.py create mode 100644 tests/dual_governance_upgrade_holesky_test.py diff --git a/interfaces/Foo.json b/interfaces/Foo.json new file mode 100644 index 000000000..cf096dafb --- /dev/null +++ b/interfaces/Foo.json @@ -0,0 +1,16 @@ +[ + { + "inputs": [], + "name": "bar", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "callCount", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + } +] diff --git a/scripts/dual_governance_upgrade_holesky.py b/scripts/dual_governance_upgrade_holesky.py new file mode 100644 index 000000000..b3520fcb8 --- /dev/null +++ b/scripts/dual_governance_upgrade_holesky.py @@ -0,0 +1,170 @@ +import time + +from typing import Dict, Tuple, Optional +from brownie.network.transaction import TransactionReceipt +from brownie.network.account import Account, LocalAccount + +from web3 import Web3 +from utils.agent import agent_forward +from utils.voting import bake_vote_items, confirm_vote_script, create_vote +from utils.ipfs import upload_vote_ipfs_description, calculate_vote_ipfs_description +from utils.config import ( + contracts, + get_deployer_account, + get_is_live, + get_priority_fee, +) +from utils.permissions import ( + encode_permission_set_manager, + encode_permission_create, + encode_permission_revoke, + encode_permission_grant, +) + +try: + from brownie import interface +except ImportError: + print( + "You're probably running inside Brownie console. " "Please call:\n" "set_console_globals(interface=interface)" + ) + + +description = "Holesky dual governance upgrade dry-run" + +dual_governance_contracts = { + "adminExecutor": "0x936C1dC7d5fAD05E5aD9aBc48b4ab09B88850f04", + "timelock": "0x388AB7b65605e21a75Bb50E24a1eA43DD0091fa5", + "emergencyGovernance": "0xd67d96C6C4DF1eF12c2fb4C908a9484333cEfE60", + "resealManager": "0x632c29848A379a7B30Ee6461ea5e7e1e92d264d0", + "dualGovernance": "0x9F14118Fc548658660a40B351C782a22e9937b42", + "tiebreakerCoreCommittee": "0x6093B9b951C72498EE799639D74dC701Ead3f07B", + "tiebreakerSubCommitteeInfluencers": "0x8F4b730099BFcA35fa4bbFD84f790eD34CAa246f", + "tiebreakerSubCommitteeNodeOperators": "0xBB259276147Af98c0e9186e783D4dbC26e82652F", + "tiebreakerSubCommitteeProtocols": "0x485349eBc3241e0bE8eDf7149C535c0b42Fa9504", + "temporaryEmergencyGovernance": "0xc7467FeFF717C18db08BAEF252f11A84F48e8fF7", + # "EMERGENCY_ACTIVATION_COMMITTEE": "0x526d46eCa1d7969924e981ecDbcAa74e9f0EE566", + # "EMERGENCY_EXECUTION_COMMITTEE": "0x526d46eCa1d7969924e981ecDbcAa74e9f0EE566", +} + +def start_vote(tx_params: Dict[str, str], silent: bool = False): + foo_contract = interface.Foo("0x258C151254bB8C6673dEF05fB965D0dD8cB7eA89") + + vote_desc_items, call_script_items = zip( + ( + "Revoke permission for STAKING_CONTROL_ROLE from Voting contract.", + encode_permission_revoke( + target_app=contracts.lido, permission_name="STAKING_CONTROL_ROLE", revoke_from=contracts.voting + ), + ), + ( + "Grant permission for STAKING_CONTROL_ROLE to Agent contract.", + encode_permission_grant( + target_app=contracts.lido, permission_name="STAKING_CONTROL_ROLE", grant_to=contracts.agent + ), + ), + ( + "Change permission manager for Lido STAKING_CONTROL_ROLE.", + encode_permission_set_manager(contracts.lido, "STAKING_CONTROL_ROLE", contracts.agent), + ), + ( + "Grant WithdrawalQueue PAUSE_ROLE to Reseal Manager.", + ( + agent_forward( + [ + ( + contracts.withdrawal_queue.address, + contracts.withdrawal_queue.grantRole.encode_input( + contracts.withdrawal_queue.PAUSE_ROLE(), dual_governance_contracts["resealManager"] + ), + ) + ] + ) + ), + ), + ( + "Grant WithdrawalQueue RESUME_ROLE to Reseal Manager.", + ( + agent_forward( + [ + ( + contracts.withdrawal_queue.address, + contracts.withdrawal_queue.grantRole.encode_input( + contracts.withdrawal_queue.RESUME_ROLE(), dual_governance_contracts["resealManager"] + ), + ) + ] + ) + ), + ), + ( + "Grant DEFAULT_ADMIN_ROLE to Voting.", + ( + agent_forward( + [ + ( + contracts.allowed_tokens_registry.address, + contracts.allowed_tokens_registry.grantRole.encode_input( + contracts.allowed_tokens_registry.DEFAULT_ADMIN_ROLE(), contracts.voting + ), + ) + ] + ) + ), + ), + ( + "Revoke DEFAULT_ADMIN_ROLE from Agent.", + ( + contracts.allowed_tokens_registry.address, + contracts.allowed_tokens_registry.revokeRole.encode_input( + contracts.allowed_tokens_registry.DEFAULT_ADMIN_ROLE(), contracts.agent + ), + ), + ), + ( + "Grant permission for EXECUTE_ROLE to DG Executor contract.", + encode_permission_grant( + target_app=contracts.agent, + permission_name="EXECUTE_ROLE", + grant_to=contracts.dual_governance_admin_executor, + ), + ), + ( + "Verify dual governance deployment", + ( + contracts.dual_governance_verifier.address, + contracts.dual_governance_verifier.verify.encode_input(tuple(dual_governance_contracts.values()), False), + ), + ), + ( + "Submit first dual governance proposal", + ( + contracts.dual_governance.address, + contracts.dual_governance.submitProposal.encode_input( + [(foo_contract.address, 0, foo_contract.bar.encode_input())], "Test proposal" + ) + ) + ) + ) + + vote_items = bake_vote_items(list(vote_desc_items), list(call_script_items)) + + if silent: + desc_ipfs = calculate_vote_ipfs_description(description) + else: + desc_ipfs = upload_vote_ipfs_description(description) + + return confirm_vote_script(vote_items, silent, desc_ipfs) and list( + create_vote(vote_items, tx_params, desc_ipfs=desc_ipfs) + ) + + +def main(): + tx_params: Dict[str, str] = {"from": get_deployer_account().address} + if get_is_live(): + tx_params["priority_fee"] = get_priority_fee() + + vote_id, _ = start_vote(tx_params=tx_params, silent=False) + + vote_id >= 0 and print(f"Vote created: {vote_id}.") + + time.sleep(5) # hack for waiting thread #2. diff --git a/tests/dual_governance_upgrade_holesky_test.py b/tests/dual_governance_upgrade_holesky_test.py new file mode 100644 index 000000000..4e0f58efd --- /dev/null +++ b/tests/dual_governance_upgrade_holesky_test.py @@ -0,0 +1,63 @@ +""" +Tests for voting 23/07/2024. +""" + +from scripts.dual_governance_upgrade_holesky import start_vote, dual_governance_contracts +from utils.config import contracts +from utils.test.tx_tracing_helpers import * +from brownie.network.transaction import TransactionReceipt +from utils.config import contracts + +try: + from brownie import interface +except ImportError: + print( + "You're probably running inside Brownie console. " "Please call:\n" "set_console_globals(interface=interface)" + ) + +def test_vote(helpers, accounts, ldo_holder, vote_ids_from_env, bypass_events_decoding): + dao_voting = contracts.voting + + # Lido + assert contracts.acl.getPermissionManager(contracts.lido, contracts.lido.STAKING_CONTROL_ROLE()) == contracts.voting + assert contracts.acl.hasPermission(contracts.voting, contracts.lido, contracts.lido.STAKING_CONTROL_ROLE()) + + # Allowed tokens registry + assert contracts.allowed_tokens_registry.hasRole(contracts.allowed_tokens_registry.DEFAULT_ADMIN_ROLE(), contracts.agent) + assert not contracts.allowed_tokens_registry.hasRole(contracts.allowed_tokens_registry.DEFAULT_ADMIN_ROLE(), contracts.voting) + + # Agent + assert not contracts.acl.hasPermission(contracts.dual_governance_admin_executor, contracts.agent, contracts.agent.EXECUTE_ROLE()) + assert contracts.acl.hasPermission(contracts.voting, contracts.agent, contracts.agent.EXECUTE_ROLE()) + + # Reseal manager + assert not contracts.withdrawal_queue.hasRole(contracts.withdrawal_queue.PAUSE_ROLE(), dual_governance_contracts["resealManager"]) + assert not contracts.withdrawal_queue.hasRole(contracts.withdrawal_queue.RESUME_ROLE(), dual_governance_contracts["resealManager"]) + + # START VOTE + vote_id = vote_ids_from_env[0] if vote_ids_from_env else start_vote({"from": ldo_holder}, silent=True)[0] + + tx: TransactionReceipt = helpers.execute_vote( + vote_id=vote_id, accounts=accounts, dao_voting=dao_voting, skip_time=3 * 60 * 60 * 24 + ) + + # Lido + assert contracts.acl.getPermissionManager(contracts.lido, contracts.lido.STAKING_CONTROL_ROLE()) == contracts.agent + assert not contracts.acl.hasPermission(contracts.voting, contracts.lido, contracts.lido.STAKING_CONTROL_ROLE()) + assert contracts.acl.hasPermission(contracts.agent, contracts.lido, contracts.lido.STAKING_CONTROL_ROLE()) + + # # Allowed tokens registry + assert contracts.allowed_tokens_registry.hasRole(contracts.allowed_tokens_registry.DEFAULT_ADMIN_ROLE(), contracts.voting) + assert not contracts.allowed_tokens_registry.hasRole(contracts.allowed_tokens_registry.DEFAULT_ADMIN_ROLE(), contracts.agent) + + # Agent + assert contracts.acl.hasPermission(contracts.dual_governance_admin_executor, contracts.agent, contracts.agent.EXECUTE_ROLE()) + assert contracts.acl.hasPermission(contracts.voting, contracts.agent, contracts.agent.EXECUTE_ROLE()) + + # Reseal manager + assert contracts.withdrawal_queue.hasRole(contracts.withdrawal_queue.PAUSE_ROLE(), dual_governance_contracts["resealManager"]) + assert contracts.withdrawal_queue.hasRole(contracts.withdrawal_queue.RESUME_ROLE(), dual_governance_contracts["resealManager"]) + + # # Validate vote events + # if not bypass_events_decoding: + # assert count_vote_items_by_events(tx, dao_voting) == 2, "Incorrect voting items count" From c2aa4fda008cc6bd6860eade5c58110002d05650 Mon Sep 17 00:00:00 2001 From: Roman Kolpakov Date: Fri, 17 Jan 2025 13:38:51 +0300 Subject: [PATCH 07/20] feat: add fork helpers --- scripts/dual_governance_upgrade_holesky.py | 20 ++++++++++++++++---- utils/mainnet_fork.py | 18 +++++++++++------- 2 files changed, 27 insertions(+), 11 deletions(-) diff --git a/scripts/dual_governance_upgrade_holesky.py b/scripts/dual_governance_upgrade_holesky.py index b3520fcb8..dbc332722 100644 --- a/scripts/dual_governance_upgrade_holesky.py +++ b/scripts/dual_governance_upgrade_holesky.py @@ -1,10 +1,7 @@ import time -from typing import Dict, Tuple, Optional -from brownie.network.transaction import TransactionReceipt -from brownie.network.account import Account, LocalAccount +from typing import Dict -from web3 import Web3 from utils.agent import agent_forward from utils.voting import bake_vote_items, confirm_vote_script, create_vote from utils.ipfs import upload_vote_ipfs_description, calculate_vote_ipfs_description @@ -20,6 +17,8 @@ encode_permission_revoke, encode_permission_grant, ) +from utils.mainnet_fork import pass_and_exec_dao_vote + try: from brownie import interface @@ -168,3 +167,16 @@ def main(): vote_id >= 0 and print(f"Vote created: {vote_id}.") time.sleep(5) # hack for waiting thread #2. + + +def start_and_execute_vote_on_fork(): + if get_is_live(): + raise Exception("This script is for local testing only.") + + tx_params = {"from": get_deployer_account()} + vote_id, _ = start_vote(tx_params=tx_params, silent=True) + + time.sleep(5) # hack for waiting thread #2. + + print(f"Vote created: {vote_id}.") + pass_and_exec_dao_vote(int(vote_id)) diff --git a/utils/mainnet_fork.py b/utils/mainnet_fork.py index 3c03ee6ac..7db63b5dc 100644 --- a/utils/mainnet_fork.py +++ b/utils/mainnet_fork.py @@ -1,7 +1,7 @@ from contextlib import contextmanager from brownie import chain, accounts, interface -from utils.config import VOTING +from utils.config import VOTING, network_name @contextmanager @@ -27,12 +27,16 @@ def pass_and_exec_dao_vote(vote_id): if not dao_voting.canExecute(vote_id): print(f"Passing vote {vote_id}") - # together these accounts hold 15% of LDO total supply - ldo_holders = [ - "0x3e40d73eb977dc6a537af587d48316fee66e9c8c", - "0xb8d83908aab38a159f3da47a59d84db8e1838712", - "0xa2dfc431297aee387c05beef507e5335e684fbcd", - ] + + if network_name() in ("holesky", "holesky-fork"): + ldo_holders = ["0xc807d4036B400dE8f6cD2aDbd8d9cf9a3a01CC30"] + else: + # together these accounts hold 15% of LDO total supply + ldo_holders = [ + "0x3e40d73eb977dc6a537af587d48316fee66e9c8c", + "0xb8d83908aab38a159f3da47a59d84db8e1838712", + "0xa2dfc431297aee387c05beef507e5335e684fbcd", + ] for holder_addr in ldo_holders: print(f" voting from {holder_addr}") From 3fc8b650fc78097bdddf9bcd2015a67b5ea46acb Mon Sep 17 00:00:00 2001 From: Roman Kolpakov Date: Mon, 20 Jan 2025 11:39:53 +0300 Subject: [PATCH 08/20] fix: change verifier parameter --- scripts/dual_governance_upgrade_holesky.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/dual_governance_upgrade_holesky.py b/scripts/dual_governance_upgrade_holesky.py index dbc332722..04fa47731 100644 --- a/scripts/dual_governance_upgrade_holesky.py +++ b/scripts/dual_governance_upgrade_holesky.py @@ -131,7 +131,7 @@ def start_vote(tx_params: Dict[str, str], silent: bool = False): "Verify dual governance deployment", ( contracts.dual_governance_verifier.address, - contracts.dual_governance_verifier.verify.encode_input(tuple(dual_governance_contracts.values()), False), + contracts.dual_governance_verifier.verify.encode_input(tuple(dual_governance_contracts.values()), True), ), ), ( From 8c0dbc0919c5677510f8e282379f8cbb130e8da1 Mon Sep 17 00:00:00 2001 From: Roman Kolpakov Date: Tue, 21 Jan 2025 16:14:13 +0300 Subject: [PATCH 09/20] feat: add time constraints interface --- configs/config_holesky.py | 1 + configs/config_mainnet.py | 1 + interfaces/TimeConstraints.json | 69 +++++++++++++++++++++++++++++++++ utils/config.py | 4 ++ 4 files changed, 75 insertions(+) create mode 100644 interfaces/TimeConstraints.json diff --git a/configs/config_holesky.py b/configs/config_holesky.py index f858f8c81..65abffcd9 100644 --- a/configs/config_holesky.py +++ b/configs/config_holesky.py @@ -52,6 +52,7 @@ DUAL_GOVERNANCE = "0x9F14118Fc548658660a40B351C782a22e9937b42" DUAL_GOVERNANCE_ADMIN_EXECUTOR = "0x936C1dC7d5fAD05E5aD9aBc48b4ab09B88850f04" DUAL_GOVERNANCE_VERIFIER = "0xd67DF125fDC3360DeCB880804D1FA3Ae3fC6FFF1" +TIME_CONSTRAINTS = "0x3db5ABA48123bb8789f6f09ec714e7082Bc26747" # EasyTracks EASYTRACK = "0x1763b9ED3586B08AE796c7787811a2E1bc16163a" diff --git a/configs/config_mainnet.py b/configs/config_mainnet.py index ae9927095..29fdeeccf 100644 --- a/configs/config_mainnet.py +++ b/configs/config_mainnet.py @@ -58,6 +58,7 @@ DUAL_GOVERNANCE = "0x0000000000000000000000000000000000000000" DUAL_GOVERNANCE_ADMIN_EXECUTOR = "0x0000000000000000000000000000000000000000" DUAL_GOVERNANCE_VERIFIER = "0x0000000000000000000000000000000000000000" +TIME_CONSTRAINTS = "0x0000000000000000000000000000000000000000" # EasyTracks EASYTRACK = "0xF0211b7660680B49De1A7E9f25C65660F0a13Fea" diff --git a/interfaces/TimeConstraints.json b/interfaces/TimeConstraints.json new file mode 100644 index 000000000..f46fb65db --- /dev/null +++ b/interfaces/TimeConstraints.json @@ -0,0 +1,69 @@ +[ + { + "inputs": [ + { + "internalType": "Duration", + "name": "currentDayTime", + "type": "uint32" + }, + { "internalType": "Duration", "name": "startDayTime", "type": "uint32" }, + { "internalType": "Duration", "name": "endDayTime", "type": "uint32" } + ], + "name": "DayTimeOutOfRange", + "type": "error" + }, + { "inputs": [], "name": "DayTimeOverflow", "type": "error" }, + { "inputs": [], "name": "DurationOverflow", "type": "error" }, + { + "inputs": [ + { "internalType": "Duration", "name": "startDayTime", "type": "uint32" }, + { "internalType": "Duration", "name": "endDayTime", "type": "uint32" } + ], + "name": "InvalidDayTimeRange", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "Timestamp", + "name": "requiredTimestamp", + "type": "uint40" + } + ], + "name": "TimestampNotReached", + "type": "error" + }, + { + "inputs": [], + "name": "DAY_DURATION", + "outputs": [{ "internalType": "Duration", "name": "", "type": "uint32" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "Timestamp", "name": "timestamp", "type": "uint40" } + ], + "name": "checkExecuteAfterTimestamp", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "Duration", "name": "startDayTime", "type": "uint32" }, + { "internalType": "Duration", "name": "endDayTime", "type": "uint32" } + ], + "name": "checkExecuteWithinDayTime", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getCurrentDayTime", + "outputs": [{ "internalType": "Duration", "name": "", "type": "uint32" }], + "stateMutability": "view", + "type": "function" + } +] diff --git a/utils/config.py b/utils/config.py index 3992b8ef7..d50190630 100644 --- a/utils/config.py +++ b/utils/config.py @@ -407,6 +407,10 @@ def dual_governance_admin_executor(self) -> interface.DualGovernanceAdminExecuto def dual_governance_verifier(self) -> interface.DualGovernanceVerifier: return interface.DualGovernanceVerifier(DUAL_GOVERNANCE_VERIFIER) + @property + def time_constraints(self) -> interface.TimeConstraints: + return interface.TimeConstraints(TIME_CONSTRAINTS) + def __getattr__(name: str) -> Any: if name == "contracts": return ContractsLazyLoader() From bc2d2995228f6927f555252f37128a18af7f3dc1 Mon Sep 17 00:00:00 2001 From: Roman Kolpakov Date: Tue, 21 Jan 2025 16:14:42 +0300 Subject: [PATCH 10/20] feat: add emergency protected timelock interface --- interfaces/EmergencyProtectedTimelock.json | 836 +++++++++++++++++++++ 1 file changed, 836 insertions(+) create mode 100644 interfaces/EmergencyProtectedTimelock.json diff --git a/interfaces/EmergencyProtectedTimelock.json b/interfaces/EmergencyProtectedTimelock.json new file mode 100644 index 000000000..9d294c377 --- /dev/null +++ b/interfaces/EmergencyProtectedTimelock.json @@ -0,0 +1,836 @@ +[ + { + "inputs": [ + { + "components": [ + { + "internalType": "Duration", + "name": "minExecutionDelay", + "type": "uint32" + }, + { + "internalType": "Duration", + "name": "maxAfterSubmitDelay", + "type": "uint32" + }, + { + "internalType": "Duration", + "name": "maxAfterScheduleDelay", + "type": "uint32" + }, + { + "internalType": "Duration", + "name": "maxEmergencyModeDuration", + "type": "uint32" + }, + { + "internalType": "Duration", + "name": "maxEmergencyProtectionDuration", + "type": "uint32" + } + ], + "internalType": "struct EmergencyProtectedTimelock.SanityCheckParams", + "name": "sanityCheckParams", + "type": "tuple" + }, + { "internalType": "address", "name": "adminExecutor", "type": "address" }, + { + "internalType": "Duration", + "name": "afterSubmitDelay", + "type": "uint32" + }, + { + "internalType": "Duration", + "name": "afterScheduleDelay", + "type": "uint32" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "proposalId", "type": "uint256" } + ], + "name": "AfterScheduleDelayNotPassed", + "type": "error" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "proposalId", "type": "uint256" } + ], + "name": "AfterSubmitDelayNotPassed", + "type": "error" + }, + { + "inputs": [ + { "internalType": "address", "name": "caller", "type": "address" } + ], + "name": "CallerIsNotAdminExecutor", + "type": "error" + }, + { + "inputs": [ + { "internalType": "address", "name": "caller", "type": "address" } + ], + "name": "CallerIsNotEmergencyActivationCommittee", + "type": "error" + }, + { + "inputs": [ + { "internalType": "address", "name": "caller", "type": "address" } + ], + "name": "CallerIsNotEmergencyExecutionCommittee", + "type": "error" + }, + { + "inputs": [ + { "internalType": "address", "name": "caller", "type": "address" } + ], + "name": "CallerIsNotGovernance", + "type": "error" + }, + { "inputs": [], "name": "DurationOverflow", "type": "error" }, + { + "inputs": [ + { "internalType": "Timestamp", "name": "protectedTill", "type": "uint40" } + ], + "name": "EmergencyProtectionExpired", + "type": "error" + }, + { "inputs": [], "name": "EmptyCalls", "type": "error" }, + { + "inputs": [ + { "internalType": "address", "name": "adminExecutor", "type": "address" } + ], + "name": "InvalidAdminExecutor", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "Duration", + "name": "afterScheduleDelay", + "type": "uint32" + } + ], + "name": "InvalidAfterScheduleDelay", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "Duration", + "name": "afterSubmitDelay", + "type": "uint32" + } + ], + "name": "InvalidAfterSubmitDelay", + "type": "error" + }, + { + "inputs": [ + { "internalType": "address", "name": "committee", "type": "address" } + ], + "name": "InvalidEmergencyActivationCommittee", + "type": "error" + }, + { + "inputs": [ + { "internalType": "address", "name": "committee", "type": "address" } + ], + "name": "InvalidEmergencyExecutionCommittee", + "type": "error" + }, + { + "inputs": [ + { "internalType": "address", "name": "governance", "type": "address" } + ], + "name": "InvalidEmergencyGovernance", + "type": "error" + }, + { + "inputs": [ + { "internalType": "Duration", "name": "value", "type": "uint32" } + ], + "name": "InvalidEmergencyModeDuration", + "type": "error" + }, + { + "inputs": [ + { "internalType": "Timestamp", "name": "value", "type": "uint40" } + ], + "name": "InvalidEmergencyProtectionEndDate", + "type": "error" + }, + { + "inputs": [ + { "internalType": "Duration", "name": "executionDelay", "type": "uint32" } + ], + "name": "InvalidExecutionDelay", + "type": "error" + }, + { + "inputs": [ + { "internalType": "address", "name": "governance", "type": "address" } + ], + "name": "InvalidGovernance", + "type": "error" + }, + { "inputs": [], "name": "TimestampOverflow", "type": "error" }, + { + "inputs": [{ "internalType": "bool", "name": "state", "type": "bool" }], + "name": "UnexpectedEmergencyModeState", + "type": "error" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "proposalId", "type": "uint256" }, + { "internalType": "enum Status", "name": "status", "type": "uint8" } + ], + "name": "UnexpectedProposalStatus", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "newAdminExecutor", + "type": "address" + } + ], + "name": "AdminExecutorSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "Duration", + "name": "newAfterScheduleDelay", + "type": "uint32" + } + ], + "name": "AfterScheduleDelaySet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "Duration", + "name": "newAfterSubmitDelay", + "type": "uint32" + } + ], + "name": "AfterSubmitDelaySet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "newActivationCommittee", + "type": "address" + } + ], + "name": "EmergencyActivationCommitteeSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "newExecutionCommittee", + "type": "address" + } + ], + "name": "EmergencyExecutionCommitteeSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "newEmergencyGovernance", + "type": "address" + } + ], + "name": "EmergencyGovernanceSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [], + "name": "EmergencyModeActivated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [], + "name": "EmergencyModeDeactivated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "Duration", + "name": "newEmergencyModeDuration", + "type": "uint32" + } + ], + "name": "EmergencyModeDurationSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "Timestamp", + "name": "newEmergencyProtectionEndDate", + "type": "uint40" + } + ], + "name": "EmergencyProtectionEndDateSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "newGovernance", + "type": "address" + } + ], + "name": "GovernanceSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "name": "ProposalExecuted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "name": "ProposalScheduled", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "executor", + "type": "address" + }, + { + "components": [ + { "internalType": "address", "name": "target", "type": "address" }, + { "internalType": "uint96", "name": "value", "type": "uint96" }, + { "internalType": "bytes", "name": "payload", "type": "bytes" } + ], + "indexed": false, + "internalType": "struct ExternalCall[]", + "name": "calls", + "type": "tuple[]" + } + ], + "name": "ProposalSubmitted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "proposalId", + "type": "uint256" + } + ], + "name": "ProposalsCancelledTill", + "type": "event" + }, + { + "inputs": [], + "name": "MAX_AFTER_SCHEDULE_DELAY", + "outputs": [{ "internalType": "Duration", "name": "", "type": "uint32" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "MAX_AFTER_SUBMIT_DELAY", + "outputs": [{ "internalType": "Duration", "name": "", "type": "uint32" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "MAX_EMERGENCY_MODE_DURATION", + "outputs": [{ "internalType": "Duration", "name": "", "type": "uint32" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "MAX_EMERGENCY_PROTECTION_DURATION", + "outputs": [{ "internalType": "Duration", "name": "", "type": "uint32" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "MIN_EXECUTION_DELAY", + "outputs": [{ "internalType": "Duration", "name": "", "type": "uint32" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "activateEmergencyMode", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "proposalId", "type": "uint256" } + ], + "name": "canExecute", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "proposalId", "type": "uint256" } + ], + "name": "canSchedule", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "cancelAllNonExecutedProposals", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "deactivateEmergencyMode", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "proposalId", "type": "uint256" } + ], + "name": "emergencyExecute", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "emergencyReset", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "proposalId", "type": "uint256" } + ], + "name": "execute", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "getAdminExecutor", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getAfterScheduleDelay", + "outputs": [{ "internalType": "Duration", "name": "", "type": "uint32" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getAfterSubmitDelay", + "outputs": [{ "internalType": "Duration", "name": "", "type": "uint32" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getEmergencyActivationCommittee", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getEmergencyExecutionCommittee", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getEmergencyGovernance", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getEmergencyProtectionDetails", + "outputs": [ + { + "components": [ + { + "internalType": "Duration", + "name": "emergencyModeDuration", + "type": "uint32" + }, + { + "internalType": "Timestamp", + "name": "emergencyModeEndsAfter", + "type": "uint40" + }, + { + "internalType": "Timestamp", + "name": "emergencyProtectionEndsAfter", + "type": "uint40" + } + ], + "internalType": "struct IEmergencyProtectedTimelock.EmergencyProtectionDetails", + "name": "details", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getGovernance", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "proposalId", "type": "uint256" } + ], + "name": "getProposal", + "outputs": [ + { + "components": [ + { "internalType": "uint256", "name": "id", "type": "uint256" }, + { "internalType": "address", "name": "executor", "type": "address" }, + { + "internalType": "Timestamp", + "name": "submittedAt", + "type": "uint40" + }, + { + "internalType": "Timestamp", + "name": "scheduledAt", + "type": "uint40" + }, + { "internalType": "enum Status", "name": "status", "type": "uint8" } + ], + "internalType": "struct ITimelock.ProposalDetails", + "name": "proposalDetails", + "type": "tuple" + }, + { + "components": [ + { "internalType": "address", "name": "target", "type": "address" }, + { "internalType": "uint96", "name": "value", "type": "uint96" }, + { "internalType": "bytes", "name": "payload", "type": "bytes" } + ], + "internalType": "struct ExternalCall[]", + "name": "calls", + "type": "tuple[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "proposalId", "type": "uint256" } + ], + "name": "getProposalCalls", + "outputs": [ + { + "components": [ + { "internalType": "address", "name": "target", "type": "address" }, + { "internalType": "uint96", "name": "value", "type": "uint96" }, + { "internalType": "bytes", "name": "payload", "type": "bytes" } + ], + "internalType": "struct ExternalCall[]", + "name": "calls", + "type": "tuple[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "proposalId", "type": "uint256" } + ], + "name": "getProposalDetails", + "outputs": [ + { + "components": [ + { "internalType": "uint256", "name": "id", "type": "uint256" }, + { "internalType": "address", "name": "executor", "type": "address" }, + { + "internalType": "Timestamp", + "name": "submittedAt", + "type": "uint40" + }, + { + "internalType": "Timestamp", + "name": "scheduledAt", + "type": "uint40" + }, + { "internalType": "enum Status", "name": "status", "type": "uint8" } + ], + "internalType": "struct ITimelock.ProposalDetails", + "name": "proposalDetails", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getProposalsCount", + "outputs": [ + { "internalType": "uint256", "name": "count", "type": "uint256" } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "isEmergencyModeActive", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "isEmergencyProtectionEnabled", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "proposalId", "type": "uint256" } + ], + "name": "schedule", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newAdminExecutor", + "type": "address" + } + ], + "name": "setAdminExecutor", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "Duration", + "name": "newAfterScheduleDelay", + "type": "uint32" + } + ], + "name": "setAfterScheduleDelay", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "Duration", + "name": "newAfterSubmitDelay", + "type": "uint32" + } + ], + "name": "setAfterSubmitDelay", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newEmergencyGovernance", + "type": "address" + } + ], + "name": "setEmergencyGovernance", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "Duration", + "name": "newEmergencyModeDuration", + "type": "uint32" + } + ], + "name": "setEmergencyModeDuration", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newEmergencyActivationCommittee", + "type": "address" + } + ], + "name": "setEmergencyProtectionActivationCommittee", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "Timestamp", + "name": "newEmergencyProtectionEndDate", + "type": "uint40" + } + ], + "name": "setEmergencyProtectionEndDate", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newEmergencyExecutionCommittee", + "type": "address" + } + ], + "name": "setEmergencyProtectionExecutionCommittee", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "newGovernance", "type": "address" } + ], + "name": "setGovernance", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "executor", "type": "address" }, + { + "components": [ + { "internalType": "address", "name": "target", "type": "address" }, + { "internalType": "uint96", "name": "value", "type": "uint96" }, + { "internalType": "bytes", "name": "payload", "type": "bytes" } + ], + "internalType": "struct ExternalCall[]", + "name": "calls", + "type": "tuple[]" + } + ], + "name": "submit", + "outputs": [ + { "internalType": "uint256", "name": "newProposalId", "type": "uint256" } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "executor", "type": "address" }, + { "internalType": "address", "name": "owner", "type": "address" } + ], + "name": "transferExecutorOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] From dc6f5d463b77198689b59d33f4f22568ac844216 Mon Sep 17 00:00:00 2001 From: Roman Kolpakov Date: Tue, 21 Jan 2025 16:14:59 +0300 Subject: [PATCH 11/20] fix: update foo contract interface --- interfaces/Foo.json | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/interfaces/Foo.json b/interfaces/Foo.json index cf096dafb..3aa189434 100644 --- a/interfaces/Foo.json +++ b/interfaces/Foo.json @@ -1,4 +1,17 @@ [ + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" + } + ], + "name": "BarCall", + "type": "event" + }, { "inputs": [], "name": "bar", From 854790dd18be29ac5071ef9dec6df270fb6c0d7b Mon Sep 17 00:00:00 2001 From: Roman Kolpakov Date: Tue, 21 Jan 2025 16:16:08 +0300 Subject: [PATCH 12/20] feat: add option to agent_forward through dual governance --- utils/agent.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/utils/agent.py b/utils/agent.py index 4538e4b81..f89ec9262 100644 --- a/utils/agent.py +++ b/utils/agent.py @@ -1,10 +1,11 @@ from utils.config import contracts -from utils.config import AGENT +from utils.config import AGENT, DUAL_GOVERNANCE from utils.evm_script import ( encode_call_script, ) from typing import ( Tuple, + Optional, Sequence, ) @@ -18,3 +19,13 @@ def agent_forward(call_script: Sequence[Tuple[str, str]]) -> Tuple[str, str]: def agent_execute(target: str, value: str, data: str) -> Tuple[str, str]: agent = contracts.agent return (AGENT, agent.execute.encode_input(target, value, data)) + + +def dual_governance_agent_forward( + call_script: Sequence[Tuple[str, str]], + description: Optional[str] = "", +) -> Tuple[str, str]: + dual_governance = contracts.dual_governance + (agent_address, agent_calldata) = agent_forward(call_script) + + return (DUAL_GOVERNANCE, dual_governance.submitProposal.encode_input([(agent_address, 0, agent_calldata)], description)) From f3514f8f87c27794806de66bb703dd39bbe55c8c Mon Sep 17 00:00:00 2001 From: Roman Kolpakov Date: Tue, 21 Jan 2025 16:16:21 +0300 Subject: [PATCH 13/20] feat: iterate upgrade scripts --- scripts/dual_governance_downgrade_holesky.py | 147 ++++++++++++++++++ scripts/dual_governance_upgrade_holesky.py | 24 +-- tests/dual_governance_upgrade_holesky_test.py | 28 ++-- 3 files changed, 181 insertions(+), 18 deletions(-) create mode 100644 scripts/dual_governance_downgrade_holesky.py diff --git a/scripts/dual_governance_downgrade_holesky.py b/scripts/dual_governance_downgrade_holesky.py new file mode 100644 index 000000000..4220dbfea --- /dev/null +++ b/scripts/dual_governance_downgrade_holesky.py @@ -0,0 +1,147 @@ +import time + +from typing import Dict + +from utils.agent import agent_forward, dual_governance_agent_forward +from utils.voting import bake_vote_items, confirm_vote_script, create_vote +from utils.ipfs import upload_vote_ipfs_description, calculate_vote_ipfs_description +from utils.config import ( + contracts, + get_deployer_account, + get_is_live, + get_priority_fee, +) +from utils.permissions import ( + encode_permission_set_manager, + encode_permission_create, + encode_permission_revoke, + encode_permission_grant, +) +from utils.mainnet_fork import pass_and_exec_dao_vote +from dual_governance_upgrade_holesky import dual_governance_contracts + + +try: + from brownie import interface +except ImportError: + print( + "You're probably running inside Brownie console. " "Please call:\n" "set_console_globals(interface=interface)" + ) + +description = "Holesky dual governance downgrade dry-run" + +def start_vote(tx_params: Dict[str, str], silent: bool = False): + vote_desc_items, call_script_items = zip( + ( + "Change permission manager for Lido STAKING_CONTROL_ROLE.", + agent_forward( + [ + encode_permission_set_manager(contracts.lido, "STAKING_CONTROL_ROLE", contracts.voting) + ] + ) + ), + ( + "Revoke permission for STAKING_CONTROL_ROLE from Agent contract.", + encode_permission_revoke( + target_app=contracts.lido, permission_name="STAKING_CONTROL_ROLE", revoke_from=contracts.agent + ), + ), + ( + "Grant permission for STAKING_CONTROL_ROLE to Voting contract.", + encode_permission_grant( + target_app=contracts.lido, permission_name="STAKING_CONTROL_ROLE", grant_to=contracts.voting + ), + ), + ( + "Revoke WithdrawalQueue PAUSE_ROLE from Reseal Manager.", + ( + agent_forward( + [ + ( + contracts.withdrawal_queue.address, + contracts.withdrawal_queue.revokeRole.encode_input( + contracts.withdrawal_queue.PAUSE_ROLE(), dual_governance_contracts["resealManager"] + ), + ) + ] + ) + ), + ), + ( + "Revoke WithdrawalQueue RESUME_ROLE from Reseal Manager.", + ( + agent_forward( + [ + ( + contracts.withdrawal_queue.address, + contracts.withdrawal_queue.revokeRole.encode_input( + contracts.withdrawal_queue.RESUME_ROLE(), dual_governance_contracts["resealManager"] + ), + ) + ] + ) + ), + ), + ( + "Grant AllowedTokensRegistry DEFAULT_ADMIN_ROLE to Agent.", + ( + contracts.allowed_tokens_registry.address, + contracts.allowed_tokens_registry.grantRole.encode_input( + contracts.allowed_tokens_registry.DEFAULT_ADMIN_ROLE(), contracts.agent + ), + ), + ), + ( + "Revoke AllowedTokensRegistry DEFAULT_ADMIN_ROLE from Voting.", + ( + contracts.allowed_tokens_registry.address, + contracts.allowed_tokens_registry.revokeRole.encode_input( + contracts.allowed_tokens_registry.DEFAULT_ADMIN_ROLE(), contracts.voting + ), + ), + ), + ( + "Revoke permission for RUN_SCRIPT_ROLE from DG Executor contract.", + encode_permission_revoke( + target_app=contracts.agent, + permission_name="RUN_SCRIPT_ROLE", + revoke_from=contracts.dual_governance_admin_executor, + ), + ), + ) + + vote_items = bake_vote_items(list(vote_desc_items), list(call_script_items)) + + if silent: + desc_ipfs = calculate_vote_ipfs_description(description) + else: + desc_ipfs = upload_vote_ipfs_description(description) + + return confirm_vote_script(vote_items, silent, desc_ipfs) and list( + create_vote(vote_items, tx_params, desc_ipfs=desc_ipfs) + ) + + +def main(): + tx_params: Dict[str, str] = {"from": get_deployer_account().address} + if get_is_live(): + tx_params["priority_fee"] = get_priority_fee() + + vote_id, _ = start_vote(tx_params=tx_params, silent=False) + + vote_id >= 0 and print(f"Vote created: {vote_id}.") + + time.sleep(5) # hack for waiting thread #2. + + +def start_and_execute_vote_on_fork(): + if get_is_live(): + raise Exception("This script is for local testing only.") + + tx_params = {"from": get_deployer_account()} + vote_id, _ = start_vote(tx_params=tx_params, silent=True) + + time.sleep(5) # hack for waiting thread #2. + + print(f"Vote created: {vote_id}.") + pass_and_exec_dao_vote(int(vote_id)) diff --git a/scripts/dual_governance_upgrade_holesky.py b/scripts/dual_governance_upgrade_holesky.py index 04fa47731..bc347ee33 100644 --- a/scripts/dual_governance_upgrade_holesky.py +++ b/scripts/dual_governance_upgrade_holesky.py @@ -2,7 +2,7 @@ from typing import Dict -from utils.agent import agent_forward +from utils.agent import agent_forward, dual_governance_agent_forward from utils.voting import bake_vote_items, confirm_vote_script, create_vote from utils.ipfs import upload_vote_ipfs_description, calculate_vote_ipfs_description from utils.config import ( @@ -46,7 +46,7 @@ } def start_vote(tx_params: Dict[str, str], silent: bool = False): - foo_contract = interface.Foo("0x258C151254bB8C6673dEF05fB965D0dD8cB7eA89") + foo_contract = interface.Foo("0xC3fc22C7e0d20247B797fb6dc743BD3879217c81") vote_desc_items, call_script_items = zip( ( @@ -96,7 +96,7 @@ def start_vote(tx_params: Dict[str, str], silent: bool = False): ), ), ( - "Grant DEFAULT_ADMIN_ROLE to Voting.", + "Grant AllowedTokensRegistry DEFAULT_ADMIN_ROLE to Voting.", ( agent_forward( [ @@ -111,7 +111,7 @@ def start_vote(tx_params: Dict[str, str], silent: bool = False): ), ), ( - "Revoke DEFAULT_ADMIN_ROLE from Agent.", + "Revoke AllowedTokensRegistry DEFAULT_ADMIN_ROLE from Agent.", ( contracts.allowed_tokens_registry.address, contracts.allowed_tokens_registry.revokeRole.encode_input( @@ -120,10 +120,10 @@ def start_vote(tx_params: Dict[str, str], silent: bool = False): ), ), ( - "Grant permission for EXECUTE_ROLE to DG Executor contract.", + "Grant permission for RUN_SCRIPT_ROLE to DG Executor contract.", encode_permission_grant( target_app=contracts.agent, - permission_name="EXECUTE_ROLE", + permission_name="RUN_SCRIPT_ROLE", grant_to=contracts.dual_governance_admin_executor, ), ), @@ -137,9 +137,15 @@ def start_vote(tx_params: Dict[str, str], silent: bool = False): ( "Submit first dual governance proposal", ( - contracts.dual_governance.address, - contracts.dual_governance.submitProposal.encode_input( - [(foo_contract.address, 0, foo_contract.bar.encode_input())], "Test proposal" + dual_governance_agent_forward( + [( + foo_contract.address, + foo_contract.bar.encode_input() + ), + ( + contracts.time_constraints.address, + contracts.time_constraints.checkExecuteWithinDayTime.encode_input(28800, 72000) + )] ) ) ) diff --git a/tests/dual_governance_upgrade_holesky_test.py b/tests/dual_governance_upgrade_holesky_test.py index 4e0f58efd..db5579023 100644 --- a/tests/dual_governance_upgrade_holesky_test.py +++ b/tests/dual_governance_upgrade_holesky_test.py @@ -7,16 +7,18 @@ from utils.test.tx_tracing_helpers import * from brownie.network.transaction import TransactionReceipt from utils.config import contracts +from brownie.network.account import Account try: - from brownie import interface + from brownie import interface, chain except ImportError: print( "You're probably running inside Brownie console. " "Please call:\n" "set_console_globals(interface=interface)" ) -def test_vote(helpers, accounts, ldo_holder, vote_ids_from_env, bypass_events_decoding): +def test_vote(helpers, accounts, ldo_holder, vote_ids_from_env, bypass_events_decoding, stranger: Account): dao_voting = contracts.voting + timelock = interface.EmergencyProtectedTimelock(contracts.dual_governance.TIMELOCK()) # Lido assert contracts.acl.getPermissionManager(contracts.lido, contracts.lido.STAKING_CONTROL_ROLE()) == contracts.voting @@ -27,8 +29,8 @@ def test_vote(helpers, accounts, ldo_holder, vote_ids_from_env, bypass_events_de assert not contracts.allowed_tokens_registry.hasRole(contracts.allowed_tokens_registry.DEFAULT_ADMIN_ROLE(), contracts.voting) # Agent - assert not contracts.acl.hasPermission(contracts.dual_governance_admin_executor, contracts.agent, contracts.agent.EXECUTE_ROLE()) - assert contracts.acl.hasPermission(contracts.voting, contracts.agent, contracts.agent.EXECUTE_ROLE()) + assert not contracts.acl.hasPermission(contracts.dual_governance_admin_executor, contracts.agent, contracts.agent.RUN_SCRIPT_ROLE()) + assert contracts.acl.hasPermission(contracts.voting, contracts.agent, contracts.agent.RUN_SCRIPT_ROLE()) # Reseal manager assert not contracts.withdrawal_queue.hasRole(contracts.withdrawal_queue.PAUSE_ROLE(), dual_governance_contracts["resealManager"]) @@ -51,13 +53,21 @@ def test_vote(helpers, accounts, ldo_holder, vote_ids_from_env, bypass_events_de assert not contracts.allowed_tokens_registry.hasRole(contracts.allowed_tokens_registry.DEFAULT_ADMIN_ROLE(), contracts.agent) # Agent - assert contracts.acl.hasPermission(contracts.dual_governance_admin_executor, contracts.agent, contracts.agent.EXECUTE_ROLE()) - assert contracts.acl.hasPermission(contracts.voting, contracts.agent, contracts.agent.EXECUTE_ROLE()) + assert contracts.acl.hasPermission(contracts.dual_governance_admin_executor, contracts.agent, contracts.agent.RUN_SCRIPT_ROLE()) + assert contracts.acl.hasPermission(contracts.voting, contracts.agent, contracts.agent.RUN_SCRIPT_ROLE()) # Reseal manager assert contracts.withdrawal_queue.hasRole(contracts.withdrawal_queue.PAUSE_ROLE(), dual_governance_contracts["resealManager"]) assert contracts.withdrawal_queue.hasRole(contracts.withdrawal_queue.RESUME_ROLE(), dual_governance_contracts["resealManager"]) - # # Validate vote events - # if not bypass_events_decoding: - # assert count_vote_items_by_events(tx, dao_voting) == 2, "Incorrect voting items count" + proposal_id = timelock.getProposalsCount() + + # while not contracts.dual_governance.canScheduleProposal(proposal_id): + chain.sleep(60 * 24) + + contracts.dual_governance.scheduleProposal(proposal_id, {"from": stranger}) + + # while not timelock.canExecute(proposal_id): + chain.sleep(60 * 24) + + timelock.execute(proposal_id, {"from": stranger}) From beb58786c39460f71f10470d83f844f12f24dd68 Mon Sep 17 00:00:00 2001 From: Roman Kolpakov Date: Tue, 21 Jan 2025 19:02:10 +0300 Subject: [PATCH 14/20] feat: add initial role verifier --- interfaces/RolesVerifier.json | 45 ++++++++++++++++++++++ scripts/dual_governance_upgrade_holesky.py | 8 ++++ 2 files changed, 53 insertions(+) create mode 100644 interfaces/RolesVerifier.json diff --git a/interfaces/RolesVerifier.json b/interfaces/RolesVerifier.json new file mode 100644 index 000000000..5a0b85810 --- /dev/null +++ b/interfaces/RolesVerifier.json @@ -0,0 +1,45 @@ +[ + { + "inputs": [ + { + "components": [ + { "internalType": "address", "name": "who", "type": "address" }, + { "internalType": "bytes32", "name": "what", "type": "bytes32" }, + { "internalType": "address", "name": "where", "type": "address" }, + { "internalType": "bool", "name": "granted", "type": "bool" } + ], + "internalType": "struct AragonRolesVerifier.RoleToVerify[]", + "name": "_rolesToVerify", + "type": "tuple[]" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [], + "name": "ACL_ADDRESS", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "name": "rolesToVerify", + "outputs": [ + { "internalType": "address", "name": "who", "type": "address" }, + { "internalType": "bytes32", "name": "what", "type": "bytes32" }, + { "internalType": "address", "name": "where", "type": "address" }, + { "internalType": "bool", "name": "granted", "type": "bool" } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "verify", + "outputs": [], + "stateMutability": "view", + "type": "function" + } +] diff --git a/scripts/dual_governance_upgrade_holesky.py b/scripts/dual_governance_upgrade_holesky.py index bc347ee33..c28ebe818 100644 --- a/scripts/dual_governance_upgrade_holesky.py +++ b/scripts/dual_governance_upgrade_holesky.py @@ -47,6 +47,7 @@ def start_vote(tx_params: Dict[str, str], silent: bool = False): foo_contract = interface.Foo("0xC3fc22C7e0d20247B797fb6dc743BD3879217c81") + roles_verifier = interface.RolesVerifier("0xe0144de0e89390dc469425f471527d9d6bc98b05") vote_desc_items, call_script_items = zip( ( @@ -134,6 +135,13 @@ def start_vote(tx_params: Dict[str, str], silent: bool = False): contracts.dual_governance_verifier.verify.encode_input(tuple(dual_governance_contracts.values()), True), ), ), + ( + "Verifiy transferred roles", + ( + roles_verifier.address, + roles_verifier.verify.encode_input() + ) + ), ( "Submit first dual governance proposal", ( From 0d139ebbb8cefe15957ec14a570a323026e8e005 Mon Sep 17 00:00:00 2001 From: Roman Kolpakov Date: Tue, 28 Jan 2025 16:49:07 +0300 Subject: [PATCH 15/20] Revert "feat: add inteface for dg verifier" This reverts commit 6bc0b07b1d9d02741557e4d55956822faa2cdf26. --- interfaces/DualGovernanceVerifier.json | 611 ------------------------- 1 file changed, 611 deletions(-) delete mode 100644 interfaces/DualGovernanceVerifier.json diff --git a/interfaces/DualGovernanceVerifier.json b/interfaces/DualGovernanceVerifier.json deleted file mode 100644 index 4a6b56628..000000000 --- a/interfaces/DualGovernanceVerifier.json +++ /dev/null @@ -1,611 +0,0 @@ -[ - { - "inputs": [ - { - "components": [ - { - "internalType": "Duration", - "name": "MIN_EXECUTION_DELAY", - "type": "uint32" - }, - { - "internalType": "Duration", - "name": "AFTER_SUBMIT_DELAY", - "type": "uint32" - }, - { - "internalType": "Duration", - "name": "MAX_AFTER_SUBMIT_DELAY", - "type": "uint32" - }, - { - "internalType": "Duration", - "name": "AFTER_SCHEDULE_DELAY", - "type": "uint32" - }, - { - "internalType": "Duration", - "name": "MAX_AFTER_SCHEDULE_DELAY", - "type": "uint32" - }, - { - "internalType": "Duration", - "name": "EMERGENCY_MODE_DURATION", - "type": "uint32" - }, - { - "internalType": "Duration", - "name": "MAX_EMERGENCY_MODE_DURATION", - "type": "uint32" - }, - { - "internalType": "Duration", - "name": "EMERGENCY_PROTECTION_DURATION", - "type": "uint32" - }, - { - "internalType": "Duration", - "name": "MAX_EMERGENCY_PROTECTION_DURATION", - "type": "uint32" - }, - { - "internalType": "address", - "name": "EMERGENCY_ACTIVATION_COMMITTEE", - "type": "address" - }, - { - "internalType": "address", - "name": "EMERGENCY_EXECUTION_COMMITTEE", - "type": "address" - }, - { - "internalType": "address", - "name": "RESEAL_COMMITTEE", - "type": "address" - }, - { - "internalType": "uint256", - "name": "MIN_WITHDRAWALS_BATCH_SIZE", - "type": "uint256" - }, - { - "components": [ - { - "internalType": "Duration", - "name": "activationTimeout", - "type": "uint32" - }, - { - "internalType": "Duration", - "name": "minActivationTimeout", - "type": "uint32" - }, - { - "internalType": "Duration", - "name": "maxActivationTimeout", - "type": "uint32" - }, - { - "internalType": "Duration", - "name": "executionDelay", - "type": "uint32" - }, - { - "components": [ - { - "internalType": "address[]", - "name": "members", - "type": "address[]" - }, - { - "internalType": "uint256", - "name": "quorum", - "type": "uint256" - } - ], - "internalType": "struct TiebreakerSubCommitteeDeployConfig", - "name": "influencers", - "type": "tuple" - }, - { - "components": [ - { - "internalType": "address[]", - "name": "members", - "type": "address[]" - }, - { - "internalType": "uint256", - "name": "quorum", - "type": "uint256" - } - ], - "internalType": "struct TiebreakerSubCommitteeDeployConfig", - "name": "nodeOperators", - "type": "tuple" - }, - { - "components": [ - { - "internalType": "address[]", - "name": "members", - "type": "address[]" - }, - { - "internalType": "uint256", - "name": "quorum", - "type": "uint256" - } - ], - "internalType": "struct TiebreakerSubCommitteeDeployConfig", - "name": "protocols", - "type": "tuple" - }, - { - "internalType": "uint256", - "name": "quorum", - "type": "uint256" - }, - { - "internalType": "address[]", - "name": "sealableWithdrawalBlockers", - "type": "address[]" - } - ], - "internalType": "struct TiebreakerDeployConfig", - "name": "tiebreakerConfig", - "type": "tuple" - }, - { - "internalType": "uint256", - "name": "MAX_SEALABLE_WITHDRAWAL_BLOCKERS_COUNT", - "type": "uint256" - }, - { - "internalType": "PercentD16", - "name": "FIRST_SEAL_RAGE_QUIT_SUPPORT", - "type": "uint128" - }, - { - "internalType": "PercentD16", - "name": "SECOND_SEAL_RAGE_QUIT_SUPPORT", - "type": "uint128" - }, - { - "internalType": "Duration", - "name": "MIN_ASSETS_LOCK_DURATION", - "type": "uint32" - }, - { - "internalType": "Duration", - "name": "MAX_MIN_ASSETS_LOCK_DURATION", - "type": "uint32" - }, - { - "internalType": "Duration", - "name": "VETO_SIGNALLING_MIN_DURATION", - "type": "uint32" - }, - { - "internalType": "Duration", - "name": "VETO_SIGNALLING_MAX_DURATION", - "type": "uint32" - }, - { - "internalType": "Duration", - "name": "VETO_SIGNALLING_MIN_ACTIVE_DURATION", - "type": "uint32" - }, - { - "internalType": "Duration", - "name": "VETO_SIGNALLING_DEACTIVATION_MAX_DURATION", - "type": "uint32" - }, - { - "internalType": "Duration", - "name": "VETO_COOLDOWN_DURATION", - "type": "uint32" - }, - { - "internalType": "Duration", - "name": "RAGE_QUIT_EXTENSION_PERIOD_DURATION", - "type": "uint32" - }, - { - "internalType": "Duration", - "name": "RAGE_QUIT_ETH_WITHDRAWALS_MIN_DELAY", - "type": "uint32" - }, - { - "internalType": "Duration", - "name": "RAGE_QUIT_ETH_WITHDRAWALS_MAX_DELAY", - "type": "uint32" - }, - { - "internalType": "Duration", - "name": "RAGE_QUIT_ETH_WITHDRAWALS_DELAY_GROWTH", - "type": "uint32" - }, - { - "internalType": "address", - "name": "TEMPORARY_EMERGENCY_GOVERNANCE_PROPOSER", - "type": "address" - } - ], - "internalType": "struct DeployConfig", - "name": "config", - "type": "tuple" - }, - { - "components": [ - { "internalType": "uint256", "name": "chainId", "type": "uint256" }, - { - "internalType": "contract IStETH", - "name": "stETH", - "type": "address" - }, - { - "internalType": "contract IWstETH", - "name": "wstETH", - "type": "address" - }, - { - "internalType": "contract IWithdrawalQueue", - "name": "withdrawalQueue", - "type": "address" - }, - { "internalType": "address", "name": "voting", "type": "address" } - ], - "internalType": "struct LidoContracts", - "name": "lidoAddresses", - "type": "tuple" - } - ], - "stateMutability": "nonpayable", - "type": "constructor" - }, - { "inputs": [], "name": "DurationOverflow", "type": "error" }, - { "inputs": [], "name": "TimestampOverflow", "type": "error" }, - { "anonymous": false, "inputs": [], "name": "Verified", "type": "event" }, - { - "inputs": [], - "name": "getConfig", - "outputs": [ - { - "components": [ - { - "internalType": "Duration", - "name": "MIN_EXECUTION_DELAY", - "type": "uint32" - }, - { - "internalType": "Duration", - "name": "AFTER_SUBMIT_DELAY", - "type": "uint32" - }, - { - "internalType": "Duration", - "name": "MAX_AFTER_SUBMIT_DELAY", - "type": "uint32" - }, - { - "internalType": "Duration", - "name": "AFTER_SCHEDULE_DELAY", - "type": "uint32" - }, - { - "internalType": "Duration", - "name": "MAX_AFTER_SCHEDULE_DELAY", - "type": "uint32" - }, - { - "internalType": "Duration", - "name": "EMERGENCY_MODE_DURATION", - "type": "uint32" - }, - { - "internalType": "Duration", - "name": "MAX_EMERGENCY_MODE_DURATION", - "type": "uint32" - }, - { - "internalType": "Duration", - "name": "EMERGENCY_PROTECTION_DURATION", - "type": "uint32" - }, - { - "internalType": "Duration", - "name": "MAX_EMERGENCY_PROTECTION_DURATION", - "type": "uint32" - }, - { - "internalType": "address", - "name": "EMERGENCY_ACTIVATION_COMMITTEE", - "type": "address" - }, - { - "internalType": "address", - "name": "EMERGENCY_EXECUTION_COMMITTEE", - "type": "address" - }, - { - "internalType": "address", - "name": "RESEAL_COMMITTEE", - "type": "address" - }, - { - "internalType": "uint256", - "name": "MIN_WITHDRAWALS_BATCH_SIZE", - "type": "uint256" - }, - { - "components": [ - { - "internalType": "Duration", - "name": "activationTimeout", - "type": "uint32" - }, - { - "internalType": "Duration", - "name": "minActivationTimeout", - "type": "uint32" - }, - { - "internalType": "Duration", - "name": "maxActivationTimeout", - "type": "uint32" - }, - { - "internalType": "Duration", - "name": "executionDelay", - "type": "uint32" - }, - { - "components": [ - { - "internalType": "address[]", - "name": "members", - "type": "address[]" - }, - { - "internalType": "uint256", - "name": "quorum", - "type": "uint256" - } - ], - "internalType": "struct TiebreakerSubCommitteeDeployConfig", - "name": "influencers", - "type": "tuple" - }, - { - "components": [ - { - "internalType": "address[]", - "name": "members", - "type": "address[]" - }, - { - "internalType": "uint256", - "name": "quorum", - "type": "uint256" - } - ], - "internalType": "struct TiebreakerSubCommitteeDeployConfig", - "name": "nodeOperators", - "type": "tuple" - }, - { - "components": [ - { - "internalType": "address[]", - "name": "members", - "type": "address[]" - }, - { - "internalType": "uint256", - "name": "quorum", - "type": "uint256" - } - ], - "internalType": "struct TiebreakerSubCommitteeDeployConfig", - "name": "protocols", - "type": "tuple" - }, - { - "internalType": "uint256", - "name": "quorum", - "type": "uint256" - }, - { - "internalType": "address[]", - "name": "sealableWithdrawalBlockers", - "type": "address[]" - } - ], - "internalType": "struct TiebreakerDeployConfig", - "name": "tiebreakerConfig", - "type": "tuple" - }, - { - "internalType": "uint256", - "name": "MAX_SEALABLE_WITHDRAWAL_BLOCKERS_COUNT", - "type": "uint256" - }, - { - "internalType": "PercentD16", - "name": "FIRST_SEAL_RAGE_QUIT_SUPPORT", - "type": "uint128" - }, - { - "internalType": "PercentD16", - "name": "SECOND_SEAL_RAGE_QUIT_SUPPORT", - "type": "uint128" - }, - { - "internalType": "Duration", - "name": "MIN_ASSETS_LOCK_DURATION", - "type": "uint32" - }, - { - "internalType": "Duration", - "name": "MAX_MIN_ASSETS_LOCK_DURATION", - "type": "uint32" - }, - { - "internalType": "Duration", - "name": "VETO_SIGNALLING_MIN_DURATION", - "type": "uint32" - }, - { - "internalType": "Duration", - "name": "VETO_SIGNALLING_MAX_DURATION", - "type": "uint32" - }, - { - "internalType": "Duration", - "name": "VETO_SIGNALLING_MIN_ACTIVE_DURATION", - "type": "uint32" - }, - { - "internalType": "Duration", - "name": "VETO_SIGNALLING_DEACTIVATION_MAX_DURATION", - "type": "uint32" - }, - { - "internalType": "Duration", - "name": "VETO_COOLDOWN_DURATION", - "type": "uint32" - }, - { - "internalType": "Duration", - "name": "RAGE_QUIT_EXTENSION_PERIOD_DURATION", - "type": "uint32" - }, - { - "internalType": "Duration", - "name": "RAGE_QUIT_ETH_WITHDRAWALS_MIN_DELAY", - "type": "uint32" - }, - { - "internalType": "Duration", - "name": "RAGE_QUIT_ETH_WITHDRAWALS_MAX_DELAY", - "type": "uint32" - }, - { - "internalType": "Duration", - "name": "RAGE_QUIT_ETH_WITHDRAWALS_DELAY_GROWTH", - "type": "uint32" - }, - { - "internalType": "address", - "name": "TEMPORARY_EMERGENCY_GOVERNANCE_PROPOSER", - "type": "address" - } - ], - "internalType": "struct DeployConfig", - "name": "", - "type": "tuple" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "getLidoAddresses", - "outputs": [ - { - "components": [ - { "internalType": "uint256", "name": "chainId", "type": "uint256" }, - { - "internalType": "contract IStETH", - "name": "stETH", - "type": "address" - }, - { - "internalType": "contract IWstETH", - "name": "wstETH", - "type": "address" - }, - { - "internalType": "contract IWithdrawalQueue", - "name": "withdrawalQueue", - "type": "address" - }, - { "internalType": "address", "name": "voting", "type": "address" } - ], - "internalType": "struct LidoContracts", - "name": "", - "type": "tuple" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "components": [ - { - "internalType": "contract Executor", - "name": "adminExecutor", - "type": "address" - }, - { - "internalType": "contract IEmergencyProtectedTimelock", - "name": "timelock", - "type": "address" - }, - { - "internalType": "contract TimelockedGovernance", - "name": "emergencyGovernance", - "type": "address" - }, - { - "internalType": "contract ResealManager", - "name": "resealManager", - "type": "address" - }, - { - "internalType": "contract DualGovernance", - "name": "dualGovernance", - "type": "address" - }, - { - "internalType": "contract TiebreakerCoreCommittee", - "name": "tiebreakerCoreCommittee", - "type": "address" - }, - { - "internalType": "contract TiebreakerSubCommittee", - "name": "tiebreakerSubCommitteeInfluencers", - "type": "address" - }, - { - "internalType": "contract TiebreakerSubCommittee", - "name": "tiebreakerSubCommitteeNodeOperators", - "type": "address" - }, - { - "internalType": "contract TiebreakerSubCommittee", - "name": "tiebreakerSubCommitteeProtocols", - "type": "address" - }, - { - "internalType": "contract TimelockedGovernance", - "name": "temporaryEmergencyGovernance", - "type": "address" - } - ], - "internalType": "struct DeployedContracts", - "name": "dgContracts", - "type": "tuple" - }, - { "internalType": "bool", "name": "onchainVotingCheck", "type": "bool" } - ], - "name": "verify", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - } -] From 03eb26f60932497990f9a510527bbccfcd0b2fdb Mon Sep 17 00:00:00 2001 From: Roman Kolpakov Date: Tue, 28 Jan 2025 16:53:15 +0300 Subject: [PATCH 16/20] fix: remove dual governance verifier --- configs/config_holesky.py | 1 - configs/config_mainnet.py | 1 - utils/config.py | 6 +----- 3 files changed, 1 insertion(+), 7 deletions(-) diff --git a/configs/config_holesky.py b/configs/config_holesky.py index 65abffcd9..fb7ee6ce1 100644 --- a/configs/config_holesky.py +++ b/configs/config_holesky.py @@ -51,7 +51,6 @@ # Dual Governance DUAL_GOVERNANCE = "0x9F14118Fc548658660a40B351C782a22e9937b42" DUAL_GOVERNANCE_ADMIN_EXECUTOR = "0x936C1dC7d5fAD05E5aD9aBc48b4ab09B88850f04" -DUAL_GOVERNANCE_VERIFIER = "0xd67DF125fDC3360DeCB880804D1FA3Ae3fC6FFF1" TIME_CONSTRAINTS = "0x3db5ABA48123bb8789f6f09ec714e7082Bc26747" # EasyTracks diff --git a/configs/config_mainnet.py b/configs/config_mainnet.py index 29fdeeccf..9cead77e4 100644 --- a/configs/config_mainnet.py +++ b/configs/config_mainnet.py @@ -57,7 +57,6 @@ # Dual Governance DUAL_GOVERNANCE = "0x0000000000000000000000000000000000000000" DUAL_GOVERNANCE_ADMIN_EXECUTOR = "0x0000000000000000000000000000000000000000" -DUAL_GOVERNANCE_VERIFIER = "0x0000000000000000000000000000000000000000" TIME_CONSTRAINTS = "0x0000000000000000000000000000000000000000" # EasyTracks diff --git a/utils/config.py b/utils/config.py index d50190630..aa7007d0a 100644 --- a/utils/config.py +++ b/utils/config.py @@ -390,7 +390,7 @@ def trp_escrow_factory(self) -> interface.VestingEscrowFactory: @property def token_rate_notifier(self) -> interface.TokenRateNotifier: return interface.TokenRateNotifier(L1_TOKEN_RATE_NOTIFIER) - + @property def allowed_tokens_registry(self) -> interface.AllowedTokensRegistry: return interface.AllowedTokensRegistry(EASYTRACK_ALLOWED_TOKENS_REGISTRY) @@ -403,10 +403,6 @@ def dual_governance(self) -> interface.DualGovernance: def dual_governance_admin_executor(self) -> interface.DualGovernanceAdminExecutor: return interface.DualGovernanceAdminExecutor(DUAL_GOVERNANCE_ADMIN_EXECUTOR) - @property - def dual_governance_verifier(self) -> interface.DualGovernanceVerifier: - return interface.DualGovernanceVerifier(DUAL_GOVERNANCE_VERIFIER) - @property def time_constraints(self) -> interface.TimeConstraints: return interface.TimeConstraints(TIME_CONSTRAINTS) From df047f817d788fd8ca033d087b18b182a12d1e39 Mon Sep 17 00:00:00 2001 From: Roman Kolpakov Date: Tue, 28 Jan 2025 16:53:28 +0300 Subject: [PATCH 17/20] fix: remove dg verifier from voting script --- scripts/dual_governance_upgrade_holesky.py | 7 ------- 1 file changed, 7 deletions(-) diff --git a/scripts/dual_governance_upgrade_holesky.py b/scripts/dual_governance_upgrade_holesky.py index c28ebe818..2e6bd8537 100644 --- a/scripts/dual_governance_upgrade_holesky.py +++ b/scripts/dual_governance_upgrade_holesky.py @@ -128,13 +128,6 @@ def start_vote(tx_params: Dict[str, str], silent: bool = False): grant_to=contracts.dual_governance_admin_executor, ), ), - ( - "Verify dual governance deployment", - ( - contracts.dual_governance_verifier.address, - contracts.dual_governance_verifier.verify.encode_input(tuple(dual_governance_contracts.values()), True), - ), - ), ( "Verifiy transferred roles", ( From b8c003050dcb62881fe60bbce51de577546fd149 Mon Sep 17 00:00:00 2001 From: Roman Kolpakov Date: Tue, 28 Jan 2025 20:47:36 +0300 Subject: [PATCH 18/20] fix: script iteration --- interfaces/RolesValidator.json | 132 +++++++++++++++++++ interfaces/RolesVerifier.json | 45 ------- scripts/dual_governance_downgrade_holesky.py | 2 +- scripts/dual_governance_upgrade_holesky.py | 23 ++-- 4 files changed, 141 insertions(+), 61 deletions(-) create mode 100644 interfaces/RolesValidator.json delete mode 100644 interfaces/RolesVerifier.json diff --git a/interfaces/RolesValidator.json b/interfaces/RolesValidator.json new file mode 100644 index 000000000..f963945f1 --- /dev/null +++ b/interfaces/RolesValidator.json @@ -0,0 +1,132 @@ +[ + { "inputs": [], "stateMutability": "nonpayable", "type": "constructor" }, + { + "inputs": [ + { "internalType": "address", "name": "entity", "type": "address" }, + { "internalType": "string", "name": "roleName", "type": "string" }, + { "internalType": "address", "name": "app", "type": "address" } + ], + "name": "AragonPermissionGranted", + "type": "error" + }, + { + "inputs": [ + { "internalType": "address", "name": "entity", "type": "address" }, + { "internalType": "string", "name": "roleName", "type": "string" }, + { + "internalType": "address", + "name": "expectedManager", + "type": "address" + }, + { "internalType": "address", "name": "actualManager", "type": "address" } + ], + "name": "AragonPermissionInvalidManager", + "type": "error" + }, + { + "inputs": [ + { "internalType": "address", "name": "entity", "type": "address" }, + { "internalType": "string", "name": "roleName", "type": "string" }, + { "internalType": "address", "name": "app", "type": "address" } + ], + "name": "AragonPermissionNotGranted", + "type": "error" + }, + { + "inputs": [ + { "internalType": "address", "name": "entity", "type": "address" }, + { "internalType": "string", "name": "roleName", "type": "string" }, + { "internalType": "address", "name": "app", "type": "address" } + ], + "name": "OZRoleNotGranted", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "entity", + "type": "address" + }, + { + "indexed": false, + "internalType": "string", + "name": "roleName", + "type": "string" + } + ], + "name": "RoleValidated", + "type": "event" + }, + { + "inputs": [], + "name": "ACL", + "outputs": [ + { "internalType": "contract IACL", "name": "", "type": "address" } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "ACL_ADDRESS", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "AGENT", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "EASYTRACK_ALLOWED_TOKENS_REGISTRY", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "LIDO", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "VOTING", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "WITHDRAWAL_QUEUE", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "dgAdminExecutor", + "type": "address" + }, + { + "internalType": "address", + "name": "dgResealManager", + "type": "address" + } + ], + "name": "validate", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/interfaces/RolesVerifier.json b/interfaces/RolesVerifier.json deleted file mode 100644 index 5a0b85810..000000000 --- a/interfaces/RolesVerifier.json +++ /dev/null @@ -1,45 +0,0 @@ -[ - { - "inputs": [ - { - "components": [ - { "internalType": "address", "name": "who", "type": "address" }, - { "internalType": "bytes32", "name": "what", "type": "bytes32" }, - { "internalType": "address", "name": "where", "type": "address" }, - { "internalType": "bool", "name": "granted", "type": "bool" } - ], - "internalType": "struct AragonRolesVerifier.RoleToVerify[]", - "name": "_rolesToVerify", - "type": "tuple[]" - } - ], - "stateMutability": "nonpayable", - "type": "constructor" - }, - { - "inputs": [], - "name": "ACL_ADDRESS", - "outputs": [{ "internalType": "address", "name": "", "type": "address" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], - "name": "rolesToVerify", - "outputs": [ - { "internalType": "address", "name": "who", "type": "address" }, - { "internalType": "bytes32", "name": "what", "type": "bytes32" }, - { "internalType": "address", "name": "where", "type": "address" }, - { "internalType": "bool", "name": "granted", "type": "bool" } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "verify", - "outputs": [], - "stateMutability": "view", - "type": "function" - } -] diff --git a/scripts/dual_governance_downgrade_holesky.py b/scripts/dual_governance_downgrade_holesky.py index 4220dbfea..114962163 100644 --- a/scripts/dual_governance_downgrade_holesky.py +++ b/scripts/dual_governance_downgrade_holesky.py @@ -18,7 +18,7 @@ encode_permission_grant, ) from utils.mainnet_fork import pass_and_exec_dao_vote -from dual_governance_upgrade_holesky import dual_governance_contracts +from scripts.dual_governance_upgrade_holesky import dual_governance_contracts try: diff --git a/scripts/dual_governance_upgrade_holesky.py b/scripts/dual_governance_upgrade_holesky.py index 2e6bd8537..d75177795 100644 --- a/scripts/dual_governance_upgrade_holesky.py +++ b/scripts/dual_governance_upgrade_holesky.py @@ -32,22 +32,13 @@ dual_governance_contracts = { "adminExecutor": "0x936C1dC7d5fAD05E5aD9aBc48b4ab09B88850f04", - "timelock": "0x388AB7b65605e21a75Bb50E24a1eA43DD0091fa5", - "emergencyGovernance": "0xd67d96C6C4DF1eF12c2fb4C908a9484333cEfE60", - "resealManager": "0x632c29848A379a7B30Ee6461ea5e7e1e92d264d0", "dualGovernance": "0x9F14118Fc548658660a40B351C782a22e9937b42", - "tiebreakerCoreCommittee": "0x6093B9b951C72498EE799639D74dC701Ead3f07B", - "tiebreakerSubCommitteeInfluencers": "0x8F4b730099BFcA35fa4bbFD84f790eD34CAa246f", - "tiebreakerSubCommitteeNodeOperators": "0xBB259276147Af98c0e9186e783D4dbC26e82652F", - "tiebreakerSubCommitteeProtocols": "0x485349eBc3241e0bE8eDf7149C535c0b42Fa9504", - "temporaryEmergencyGovernance": "0xc7467FeFF717C18db08BAEF252f11A84F48e8fF7", - # "EMERGENCY_ACTIVATION_COMMITTEE": "0x526d46eCa1d7969924e981ecDbcAa74e9f0EE566", - # "EMERGENCY_EXECUTION_COMMITTEE": "0x526d46eCa1d7969924e981ecDbcAa74e9f0EE566", + "resealManager": "0x632c29848A379a7B30Ee6461ea5e7e1e92d264d0", } def start_vote(tx_params: Dict[str, str], silent: bool = False): foo_contract = interface.Foo("0xC3fc22C7e0d20247B797fb6dc743BD3879217c81") - roles_verifier = interface.RolesVerifier("0xe0144de0e89390dc469425f471527d9d6bc98b05") + roles_validator = interface.RolesValidator("0x0F8826a574BCFDC4997939076f6D82877971feB3") vote_desc_items, call_script_items = zip( ( @@ -125,14 +116,16 @@ def start_vote(tx_params: Dict[str, str], silent: bool = False): encode_permission_grant( target_app=contracts.agent, permission_name="RUN_SCRIPT_ROLE", - grant_to=contracts.dual_governance_admin_executor, + grant_to=dual_governance_contracts["adminExecutor"], ), ), ( - "Verifiy transferred roles", + "Validate transferred roles", ( - roles_verifier.address, - roles_verifier.verify.encode_input() + roles_validator.address, + roles_validator.validate.encode_input( + dual_governance_contracts['adminExecutor'], dual_governance_contracts['resealManager'] + ), ) ), ( From cf3ddee672d1fd59ce20492aee0fbe2de61421f6 Mon Sep 17 00:00:00 2001 From: Roman Kolpakov Date: Wed, 29 Jan 2025 13:28:08 +0300 Subject: [PATCH 19/20] feat: update addresses --- configs/config_holesky.py | 4 ++-- scripts/dual_governance_upgrade_holesky.py | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/configs/config_holesky.py b/configs/config_holesky.py index fb7ee6ce1..ed7527b98 100644 --- a/configs/config_holesky.py +++ b/configs/config_holesky.py @@ -49,8 +49,8 @@ WITHDRAWAL_VAULT = "0xF0179dEC45a37423EAD4FaD5fCb136197872EAd9" # Dual Governance -DUAL_GOVERNANCE = "0x9F14118Fc548658660a40B351C782a22e9937b42" -DUAL_GOVERNANCE_ADMIN_EXECUTOR = "0x936C1dC7d5fAD05E5aD9aBc48b4ab09B88850f04" +DUAL_GOVERNANCE = "0xb291a7f092D5cCE0A3C93eA21Bda3431129dB202" +DUAL_GOVERNANCE_ADMIN_EXECUTOR = "0xD5EE9991f44b36E186A658dc2A0357EcCf11b69B" TIME_CONSTRAINTS = "0x3db5ABA48123bb8789f6f09ec714e7082Bc26747" # EasyTracks diff --git a/scripts/dual_governance_upgrade_holesky.py b/scripts/dual_governance_upgrade_holesky.py index d75177795..0411298b7 100644 --- a/scripts/dual_governance_upgrade_holesky.py +++ b/scripts/dual_governance_upgrade_holesky.py @@ -31,9 +31,9 @@ description = "Holesky dual governance upgrade dry-run" dual_governance_contracts = { - "adminExecutor": "0x936C1dC7d5fAD05E5aD9aBc48b4ab09B88850f04", - "dualGovernance": "0x9F14118Fc548658660a40B351C782a22e9937b42", - "resealManager": "0x632c29848A379a7B30Ee6461ea5e7e1e92d264d0", + "dualGovernance": "0xb291a7f092D5cCE0A3C93eA21Bda3431129dB202", + "adminExecutor": "0xD5EE9991f44b36E186A658dc2A0357EcCf11b69B", + "resealManager": "0xc2764655e3fe0bd2D3C710D74Fa5a89162099FD8", } def start_vote(tx_params: Dict[str, str], silent: bool = False): From e960309f9847fbc7bcab324495a9c682a0862869 Mon Sep 17 00:00:00 2001 From: Roman Kolpakov Date: Mon, 3 Feb 2025 16:58:17 +0300 Subject: [PATCH 20/20] feat: add test for downgrade --- .../dual_governance_downgrade_holesky_test.py | 56 +++++++++++++++++++ tests/dual_governance_upgrade_holesky_test.py | 4 -- 2 files changed, 56 insertions(+), 4 deletions(-) create mode 100644 tests/dual_governance_downgrade_holesky_test.py diff --git a/tests/dual_governance_downgrade_holesky_test.py b/tests/dual_governance_downgrade_holesky_test.py new file mode 100644 index 000000000..897a61705 --- /dev/null +++ b/tests/dual_governance_downgrade_holesky_test.py @@ -0,0 +1,56 @@ +from scripts.dual_governance_downgrade_holesky import start_vote, dual_governance_contracts +from utils.config import contracts +from utils.test.tx_tracing_helpers import * +from brownie.network.transaction import TransactionReceipt +from utils.config import contracts +from brownie.network.account import Account + +try: + from brownie import interface, chain +except ImportError: + print( + "You're probably running inside Brownie console. " "Please call:\n" "set_console_globals(interface=interface)" + ) + +def test_vote(helpers, accounts, ldo_holder, vote_ids_from_env, bypass_events_decoding, stranger: Account): + dao_voting = contracts.voting + + # Lido + assert contracts.acl.getPermissionManager(contracts.lido, contracts.lido.STAKING_CONTROL_ROLE()) == contracts.agent + assert not contracts.acl.hasPermission(contracts.voting, contracts.lido, contracts.lido.STAKING_CONTROL_ROLE()) + assert contracts.acl.hasPermission(contracts.agent, contracts.lido, contracts.lido.STAKING_CONTROL_ROLE()) + + # Reseal manager + assert contracts.withdrawal_queue.hasRole(contracts.withdrawal_queue.PAUSE_ROLE(), dual_governance_contracts["resealManager"]) + assert contracts.withdrawal_queue.hasRole(contracts.withdrawal_queue.RESUME_ROLE(), dual_governance_contracts["resealManager"]) + + # Allowed tokens registry + assert contracts.allowed_tokens_registry.hasRole(contracts.allowed_tokens_registry.DEFAULT_ADMIN_ROLE(), contracts.voting) + assert not contracts.allowed_tokens_registry.hasRole(contracts.allowed_tokens_registry.DEFAULT_ADMIN_ROLE(), contracts.agent) + + # Agent + assert contracts.acl.hasPermission(contracts.dual_governance_admin_executor, contracts.agent, contracts.agent.RUN_SCRIPT_ROLE()) + assert contracts.acl.hasPermission(contracts.voting, contracts.agent, contracts.agent.RUN_SCRIPT_ROLE()) + + # START VOTE + vote_id = vote_ids_from_env[0] if vote_ids_from_env else start_vote({"from": ldo_holder}, silent=True)[0] + + tx: TransactionReceipt = helpers.execute_vote( + vote_id=vote_id, accounts=accounts, dao_voting=dao_voting, skip_time=3 * 60 * 60 * 24 + ) + + # Lido + assert contracts.acl.getPermissionManager(contracts.lido, contracts.lido.STAKING_CONTROL_ROLE()) == contracts.voting + assert contracts.acl.hasPermission(contracts.voting, contracts.lido, contracts.lido.STAKING_CONTROL_ROLE()) + + # Reseal manager + assert not contracts.withdrawal_queue.hasRole(contracts.withdrawal_queue.PAUSE_ROLE(), dual_governance_contracts["resealManager"]) + assert not contracts.withdrawal_queue.hasRole(contracts.withdrawal_queue.RESUME_ROLE(), dual_governance_contracts["resealManager"]) + + # Allowed tokens registry + assert contracts.allowed_tokens_registry.hasRole(contracts.allowed_tokens_registry.DEFAULT_ADMIN_ROLE(), contracts.agent) + assert not contracts.allowed_tokens_registry.hasRole(contracts.allowed_tokens_registry.DEFAULT_ADMIN_ROLE(), contracts.voting) + + # Agent + assert not contracts.acl.hasPermission(contracts.dual_governance_admin_executor, contracts.agent, contracts.agent.RUN_SCRIPT_ROLE()) + assert contracts.acl.hasPermission(contracts.voting, contracts.agent, contracts.agent.RUN_SCRIPT_ROLE()) diff --git a/tests/dual_governance_upgrade_holesky_test.py b/tests/dual_governance_upgrade_holesky_test.py index db5579023..eefa0b120 100644 --- a/tests/dual_governance_upgrade_holesky_test.py +++ b/tests/dual_governance_upgrade_holesky_test.py @@ -1,7 +1,3 @@ -""" -Tests for voting 23/07/2024. -""" - from scripts.dual_governance_upgrade_holesky import start_vote, dual_governance_contracts from utils.config import contracts from utils.test.tx_tracing_helpers import *