From 4e138a4c8005bc25a11331bd5cbc6019ddb3a6e7 Mon Sep 17 00:00:00 2001 From: Bogdan Kovtun Date: Wed, 4 Dec 2024 15:03:42 +0400 Subject: [PATCH 1/4] Add script to generate permissions migration tables --- .../agent-transfer-permissions-config.ts | 626 ++++++++++++++++++ .../config/lido-contracts.ts | 144 ++++ scripts/permissions-migration/index.ts | 46 ++ .../src/aragon-permissions.ts | 354 ++++++++++ scripts/permissions-migration/src/bytes.ts | 29 + .../src/managed-contracts.ts | 123 ++++ scripts/permissions-migration/src/markdown.ts | 30 + scripts/permissions-migration/src/oz-roles.ts | 295 +++++++++ scripts/permissions-migration/src/utils.ts | 62 ++ 9 files changed, 1709 insertions(+) create mode 100644 scripts/permissions-migration/config/agent-transfer-permissions-config.ts create mode 100644 scripts/permissions-migration/config/lido-contracts.ts create mode 100644 scripts/permissions-migration/index.ts create mode 100644 scripts/permissions-migration/src/aragon-permissions.ts create mode 100644 scripts/permissions-migration/src/bytes.ts create mode 100644 scripts/permissions-migration/src/managed-contracts.ts create mode 100644 scripts/permissions-migration/src/markdown.ts create mode 100644 scripts/permissions-migration/src/oz-roles.ts create mode 100644 scripts/permissions-migration/src/utils.ts diff --git a/scripts/permissions-migration/config/agent-transfer-permissions-config.ts b/scripts/permissions-migration/config/agent-transfer-permissions-config.ts new file mode 100644 index 00000000..ef7f635c --- /dev/null +++ b/scripts/permissions-migration/config/agent-transfer-permissions-config.ts @@ -0,0 +1,626 @@ +import { AragonContractPermissionConfigs } from "../src/aragon-permissions"; +import { ManagedContractsConfig } from "../src/managed-contracts"; +import { OZContractRolesConfig } from "../src/oz-roles"; +import { LIDO_CONTRACTS } from "./lido-contracts"; + +// TODO: Check the completeness of the configs + +export const ARAGON_CONTRACT_ROLES_CONFIG: AragonContractPermissionConfigs = { + // Core protocol + Lido: { + address: LIDO_CONTRACTS.Lido, + permissions: { + STAKING_CONTROL_ROLE: { manager: "None" }, + RESUME_ROLE: { manager: "None" }, + PAUSE_ROLE: { manager: "None" }, + UNSAFE_CHANGE_DEPOSITED_VALIDATORS_ROLE: { manager: "None" }, + STAKING_PAUSE_ROLE: { manager: "None" }, + }, + }, + + // Oracle Contracts + LegacyOracle: { + address: LIDO_CONTRACTS.LegacyOracle, + permissions: { + // Has no declared permissions + }, + }, + + // DAO Contracts + DAOKernel: { + address: LIDO_CONTRACTS.DAOKernel, + permissions: { + APP_MANAGER_ROLE: { manager: "Agent" }, + }, + }, + Voting: { + address: LIDO_CONTRACTS.Voting, + permissions: { + UNSAFELY_MODIFY_VOTE_TIME_ROLE: { manager: "Voting" }, + MODIFY_QUORUM_ROLE: { manager: "Voting", grantedTo: ["Voting"] }, + MODIFY_SUPPORT_ROLE: { manager: "Voting", grantedTo: ["Voting"] }, + CREATE_VOTES_ROLE: { manager: "Voting", grantedTo: ["TokenManager"] }, + }, + }, + TokenManager: { + address: LIDO_CONTRACTS.TokenManager, + permissions: { + ISSUE_ROLE: { manager: "Voting" }, + ASSIGN_ROLE: { manager: "Voting" }, + BURN_ROLE: { manager: "Voting" }, + MINT_ROLE: { manager: "Voting" }, + REVOKE_VESTINGS_ROLE: { manager: "Voting" }, + }, + }, + Finance: { + address: LIDO_CONTRACTS.Finance, + permissions: { + CREATE_PAYMENTS_ROLE: { + manager: "Voting", + grantedTo: ["Voting", "EasyTrackEvmScriptExecutor"], + }, + CHANGE_PERIOD_ROLE: { manager: "Voting" }, + CHANGE_BUDGETS_ROLE: { manager: "Voting" }, + EXECUTE_PAYMENTS_ROLE: { manager: "Voting", grantedTo: ["Voting"] }, + MANAGE_PAYMENTS_ROLE: { manager: "Voting", grantedTo: ["Voting"] }, + }, + }, + Agent: { + address: LIDO_CONTRACTS.Agent, + permissions: { + ADD_PROTECTED_TOKEN_ROLE: { manager: "Voting" }, + TRANSFER_ROLE: { manager: "Voting", grantedTo: ["Finance"] }, + RUN_SCRIPT_ROLE: { + manager: "DualGovernance", + grantedTo: ["DualGovernance"], + }, + SAFE_EXECUTE_ROLE: { manager: "None" }, + REMOVE_PROTECTED_TOKEN_ROLE: { manager: "Voting" }, + DESIGNATE_SIGNER_ROLE: { manager: "Voting" }, + EXECUTE_ROLE: { + manager: "DualGovernance", + grantedTo: ["DualGovernance"], + }, + ADD_PRESIGNED_HASH_ROLE: { manager: "Voting" }, + }, + }, + ACL: { + address: LIDO_CONTRACTS.ACL, + permissions: { CREATE_PERMISSIONS_ROLE: { manager: "DualGovernance" } }, + }, + AragonPM: { + address: LIDO_CONTRACTS.AragonPM, + permissions: { + CREATE_REPO_ROLE: { manager: "Agent" }, + }, + }, + VotingRepo: { + address: LIDO_CONTRACTS.VotingRepo, + permissions: { + CREATE_VERSION_ROLE: { manager: "Agent" }, + }, + }, + LidoRepo: { + address: LIDO_CONTRACTS.LidoRepo, + permissions: { + CREATE_VERSION_ROLE: { manager: "Agent" }, + }, + }, + LegacyOracleRepo: { + address: LIDO_CONTRACTS.LegacyOracleRepo, + permissions: { + CREATE_VERSION_ROLE: { manager: "Agent" }, + }, + }, + CuratedModuleRepo: { + address: LIDO_CONTRACTS.CuratedModuleRepo, + permissions: { + CREATE_VERSION_ROLE: { manager: "Agent" }, + }, + }, + SimpleDVTRepo: { + address: LIDO_CONTRACTS.SimpleDVTRepo, + permissions: { + CREATE_VERSION_ROLE: { manager: "Agent" }, + }, + }, + // Staking Modules + CuratedModule: { + address: LIDO_CONTRACTS.CuratedModule, + permissions: { + STAKING_ROUTER_ROLE: { manager: "Agent" }, + MANAGE_NODE_OPERATOR_ROLE: { manager: "Agent" }, + SET_NODE_OPERATOR_LIMIT_ROLE: { manager: "Agent" }, + MANAGE_SIGNING_KEYS: { + manager: "Agent", + grantedTo: ["EasyTrackEvmScriptExecutor"], + }, + }, + }, + SimpleDVT: { + address: LIDO_CONTRACTS.SimpleDVT, + permissions: { + STAKING_ROUTER_ROLE: { manager: "Agent" }, + MANAGE_NODE_OPERATOR_ROLE: { manager: "Agent" }, + SET_NODE_OPERATOR_LIMIT_ROLE: { manager: "Agent" }, + MANAGE_SIGNING_KEYS: { + manager: "EasyTrackEvmScriptExecutor", + grantedTo: ["EasyTrackEvmScriptExecutor"], + }, + }, + }, +}; + +export const OZ_CONTRACT_ROLES_CONFIG: OZContractRolesConfig = { + // Core Protocol + StakingRouter: { + address: LIDO_CONTRACTS.StakingRouter, + roles: { + DEFAULT_ADMIN_ROLE: ["Agent"], + MANAGE_WITHDRAWAL_CREDENTIALS_ROLE: [], + REPORT_EXITED_VALIDATORS_ROLE: ["AccountingOracle"], + REPORT_REWARDS_MINTED_ROLE: ["Lido"], + STAKING_MODULE_MANAGE_ROLE: ["Agent"], + STAKING_MODULE_UNVETTING_ROLE: ["DepositSecurityModule"], + UNSAFE_SET_EXITED_VALIDATORS_ROLE: [], + }, + }, + WithdrawalQueueERC721: { + address: LIDO_CONTRACTS.WithdrawalQueueERC721, + roles: { + DEFAULT_ADMIN_ROLE: ["Agent"], + FINALIZE_ROLE: ["Lido"], + MANAGE_TOKEN_URI_ROLE: [], + ORACLE_ROLE: ["AccountingOracle"], + PAUSE_ROLE: ["OraclesGateSeal"], + RESUME_ROLE: [], + }, + }, + Burner: { + address: LIDO_CONTRACTS.Burner, + roles: { + DEFAULT_ADMIN_ROLE: ["Agent"], + REQUEST_BURN_MY_STETH_ROLE: ["Agent"], + REQUEST_BURN_SHARES_ROLE: [ + "Lido", + "CuratedModule", + "SimpleDVT", + "CSAccounting", + ], + }, + }, + + // Oracle Contracts + AccountingOracle: { + address: LIDO_CONTRACTS.AccountingOracle, + roles: { + DEFAULT_ADMIN_ROLE: ["Agent"], + MANAGE_CONSENSUS_CONTRACT_ROLE: [], + MANAGE_CONSENSUS_VERSION_ROLE: [], + SUBMIT_DATA_ROLE: [], + }, + }, + AccountingOracleHashConsensus: { + address: LIDO_CONTRACTS.AccountingOracleHashConsensus, + roles: { + DEFAULT_ADMIN_ROLE: ["Agent"], + DISABLE_CONSENSUS_ROLE: [], + MANAGE_FAST_LANE_CONFIG_ROLE: [], + MANAGE_FRAME_CONFIG_ROLE: [], + MANAGE_MEMBERS_AND_QUORUM_ROLE: ["Agent"], + MANAGE_REPORT_PROCESSOR_ROLE: [], + }, + }, + ValidatorExitBusOracle: { + address: LIDO_CONTRACTS.ValidatorExitBusOracle, + roles: { + DEFAULT_ADMIN_ROLE: ["Agent"], + MANAGE_CONSENSUS_CONTRACT_ROLE: [], + MANAGE_CONSENSUS_VERSION_ROLE: [], + PAUSE_ROLE: ["OraclesGateSeal"], + RESUME_ROLE: [], + SUBMIT_DATA_ROLE: [], + }, + }, + ValidatorExitBusHashConsensus: { + address: LIDO_CONTRACTS.ValidatorExitBusHashConsensus, + roles: { + DEFAULT_ADMIN_ROLE: ["Agent"], + DISABLE_CONSENSUS_ROLE: [], + MANAGE_FAST_LANE_CONFIG_ROLE: [], + MANAGE_FRAME_CONFIG_ROLE: [], + MANAGE_MEMBERS_AND_QUORUM_ROLE: ["Agent"], + MANAGE_REPORT_PROCESSOR_ROLE: [], + }, + }, + OracleReportSanityChecker: { + address: LIDO_CONTRACTS.OracleReportSanityChecker, + roles: { + DEFAULT_ADMIN_ROLE: ["Agent"], + ALL_LIMITS_MANAGER_ROLE: [], + ANNUAL_BALANCE_INCREASE_LIMIT_MANAGER_ROLE: [], + APPEARED_VALIDATORS_PER_DAY_LIMIT_MANAGER_ROLE: [], + EXITED_VALIDATORS_PER_DAY_LIMIT_MANAGER_ROLE: [], + INITIAL_SLASHING_AND_PENALTIES_MANAGER_ROLE: [], + MAX_ITEMS_PER_EXTRA_DATA_TRANSACTION_ROLE: [], + MAX_NODE_OPERATORS_PER_EXTRA_DATA_ITEM_ROLE: [], + MAX_POSITIVE_TOKEN_REBASE_MANAGER_ROLE: [], + MAX_VALIDATOR_EXIT_REQUESTS_PER_REPORT_ROLE: [], + REQUEST_TIMESTAMP_MARGIN_MANAGER_ROLE: [], + SECOND_OPINION_MANAGER_ROLE: [], + SHARE_RATE_DEVIATION_LIMIT_MANAGER_ROLE: [], + }, + }, + OracleDaemonConfig: { + address: LIDO_CONTRACTS.OracleDaemonConfig, + roles: { + DEFAULT_ADMIN_ROLE: ["Agent"], + CONFIG_MANAGER_ROLE: [], + }, + }, + // Staking Modules + CSModule: { + address: LIDO_CONTRACTS.CSModule, + roles: { + DEFAULT_ADMIN_ROLE: ["Agent"], + MODULE_MANAGER_ROLE: [], + PAUSE_ROLE: ["CSGateSeal"], + RECOVERER_ROLE: [], + REPORT_EL_REWARDS_STEALING_PENALTY_ROLE: [], + RESUME_ROLE: [], + SETTLE_EL_REWARDS_STEALING_PENALTY_ROLE: [], + STAKING_ROUTER_ROLE: [], + VERIFIER_ROLE: [], + }, + }, + CSAccounting: { + address: LIDO_CONTRACTS.CSAccounting, + roles: { + DEFAULT_ADMIN_ROLE: ["Agent"], + ACCOUNTING_MANAGER_ROLE: ["Agent"], + MANAGE_BOND_CURVES_ROLE: [], + PAUSE_ROLE: [], + RECOVERER_ROLE: [], + RESET_BOND_CURVE_ROLE: [], + RESUME_ROLE: [], + SET_BOND_CURVE_ROLE: [], + }, + }, + CSFeeDistributor: { + address: LIDO_CONTRACTS.CSFeeDistributor, + roles: { + DEFAULT_ADMIN_ROLE: ["Agent"], + RECOVERER_ROLE: [], + }, + }, + CSFeeOracle: { + address: LIDO_CONTRACTS.CSFeeOracle, + roles: { + DEFAULT_ADMIN_ROLE: ["Agent"], + CONTRACT_MANAGER_ROLE: [], + MANAGE_CONSENSUS_CONTRACT_ROLE: [], + MANAGE_CONSENSUS_VERSION_ROLE: [], + PAUSE_ROLE: [], + RECOVERER_ROLE: [], + RESUME_ROLE: [], + SUBMIT_DATA_ROLE: [], + }, + }, + CSHashConsensus: { + address: LIDO_CONTRACTS.CSHashConsensus, + roles: { + DEFAULT_ADMIN_ROLE: ["Agent"], + DISABLE_CONSENSUS_ROLE: [], + MANAGE_FAST_LANE_CONFIG_ROLE: [], + MANAGE_FRAME_CONFIG_ROLE: [], + MANAGE_MEMBERS_AND_QUORUM_ROLE: [], + MANAGE_REPORT_PROCESSOR_ROLE: [], + }, + }, + // Easy Track + EasyTrack: { + address: LIDO_CONTRACTS.EasyTrack, + roles: { + DEFAULT_ADMIN_ROLE: ["Voting"], + CANCEL_ROLE: [], + PAUSE_ROLE: [], + UNPAUSE_ROLE: [], + }, + }, + // Easy Track Factories for token transfers + LOLStETH_AllowedRecipientsRegistry: { + address: LIDO_CONTRACTS.LOLStETH_AllowedRecipientsRegistry, + roles: { + DEFAULT_ADMIN_ROLE: ["Voting"], + ADD_RECIPIENT_TO_ALLOWED_LIST_ROLE: ["Voting"], + REMOVE_RECIPIENT_FROM_ALLOWED_LIST_ROLE: [], + SET_PARAMETERS_ROLE: [], + UPDATE_SPENT_AMOUNT_ROLE: [], + }, + }, + RewardsShareStETH_AllowedRecipientsRegistry: { + address: LIDO_CONTRACTS.RewardsShareStETH_AllowedRecipientsRegistry, + roles: { + DEFAULT_ADMIN_ROLE: ["Voting"], + ADD_RECIPIENT_TO_ALLOWED_LIST_ROLE: [], + REMOVE_RECIPIENT_FROM_ALLOWED_LIST_ROLE: [], + SET_PARAMETERS_ROLE: [], + UPDATE_SPENT_AMOUNT_ROLE: [], + }, + }, + LegoLDO_AllowedRecipientsRegistry: { + address: LIDO_CONTRACTS.LegoLDO_AllowedRecipientsRegistry, + roles: { + DEFAULT_ADMIN_ROLE: ["Voting"], + ADD_RECIPIENT_TO_ALLOWED_LIST_ROLE: [], + REMOVE_RECIPIENT_FROM_ALLOWED_LIST_ROLE: [], + SET_PARAMETERS_ROLE: [], + UPDATE_SPENT_AMOUNT_ROLE: [], + }, + }, + LegoStablecoins_AllowedRecipientsRegistry: { + address: LIDO_CONTRACTS.LegoStablecoins_AllowedRecipientsRegistry, + roles: { + DEFAULT_ADMIN_ROLE: ["Voting"], + ADD_RECIPIENT_TO_ALLOWED_LIST_ROLE: [], + REMOVE_RECIPIENT_FROM_ALLOWED_LIST_ROLE: [], + SET_PARAMETERS_ROLE: [], + UPDATE_SPENT_AMOUNT_ROLE: [], + }, + }, + RCCStableCoins_AllowedRecipientsRegistry: { + address: LIDO_CONTRACTS.RCCStableCoins_AllowedRecipientsRegistry, + roles: { + DEFAULT_ADMIN_ROLE: ["Voting"], + ADD_RECIPIENT_TO_ALLOWED_LIST_ROLE: [], + REMOVE_RECIPIENT_FROM_ALLOWED_LIST_ROLE: [], + SET_PARAMETERS_ROLE: [], + UPDATE_SPENT_AMOUNT_ROLE: [], + }, + }, + RCCStETH_AllowedRecipientsRegistry: { + address: LIDO_CONTRACTS.RCCStETH_AllowedRecipientsRegistry, + roles: { + DEFAULT_ADMIN_ROLE: ["Voting"], + ADD_RECIPIENT_TO_ALLOWED_LIST_ROLE: [], + REMOVE_RECIPIENT_FROM_ALLOWED_LIST_ROLE: [], + SET_PARAMETERS_ROLE: [], + UPDATE_SPENT_AMOUNT_ROLE: [], + }, + }, + PMLStablecoins_AllowedRecipientsRegistry: { + address: LIDO_CONTRACTS.PMLStablecoins_AllowedRecipientsRegistry, + roles: { + DEFAULT_ADMIN_ROLE: ["Voting"], + ADD_RECIPIENT_TO_ALLOWED_LIST_ROLE: [], + REMOVE_RECIPIENT_FROM_ALLOWED_LIST_ROLE: [], + SET_PARAMETERS_ROLE: [], + UPDATE_SPENT_AMOUNT_ROLE: [], + }, + }, + PMLStETH_AllowedRecipientsRegistry: { + address: LIDO_CONTRACTS.PMLStETH_AllowedRecipientsRegistry, + roles: { + DEFAULT_ADMIN_ROLE: ["Voting"], + ADD_RECIPIENT_TO_ALLOWED_LIST_ROLE: [], + REMOVE_RECIPIENT_FROM_ALLOWED_LIST_ROLE: [], + SET_PARAMETERS_ROLE: [], + UPDATE_SPENT_AMOUNT_ROLE: [], + }, + }, + ATCStablecoins_AllowedRecipientsRegistry: { + address: LIDO_CONTRACTS.ATCStablecoins_AllowedRecipientsRegistry, + roles: { + DEFAULT_ADMIN_ROLE: ["Voting"], + ADD_RECIPIENT_TO_ALLOWED_LIST_ROLE: [], + REMOVE_RECIPIENT_FROM_ALLOWED_LIST_ROLE: [], + SET_PARAMETERS_ROLE: [], + UPDATE_SPENT_AMOUNT_ROLE: [], + }, + }, + ATCStETH_AllowedRecipientsRegistry: { + address: LIDO_CONTRACTS.ATCStETH_AllowedRecipientsRegistry, + roles: { + DEFAULT_ADMIN_ROLE: ["Voting"], + ADD_RECIPIENT_TO_ALLOWED_LIST_ROLE: [], + REMOVE_RECIPIENT_FROM_ALLOWED_LIST_ROLE: [], + SET_PARAMETERS_ROLE: [], + UPDATE_SPENT_AMOUNT_ROLE: [], + }, + }, + TRPLDO_AllowedRecipientsRegistry: { + address: LIDO_CONTRACTS.TRPLDO_AllowedRecipientsRegistry, + roles: { + DEFAULT_ADMIN_ROLE: ["Voting"], + ADD_RECIPIENT_TO_ALLOWED_LIST_ROLE: [], + REMOVE_RECIPIENT_FROM_ALLOWED_LIST_ROLE: [], + SET_PARAMETERS_ROLE: [], + UPDATE_SPENT_AMOUNT_ROLE: [], + }, + }, + GasSupplyStETH_AllowedRecipientsRegistry: { + address: LIDO_CONTRACTS.GasSupplyStETH_AllowedRecipientsRegistry, + roles: { + DEFAULT_ADMIN_ROLE: ["Voting"], + ADD_RECIPIENT_TO_ALLOWED_LIST_ROLE: [], + REMOVE_RECIPIENT_FROM_ALLOWED_LIST_ROLE: [], + SET_PARAMETERS_ROLE: [], + UPDATE_SPENT_AMOUNT_ROLE: [], + }, + }, + AllianceOpsStablecoins_AllowedRecipientsRegistry: { + address: LIDO_CONTRACTS.AllianceOpsStablecoins_AllowedRecipientsRegistry, + roles: { + DEFAULT_ADMIN_ROLE: ["Voting"], + ADD_RECIPIENT_TO_ALLOWED_LIST_ROLE: [], + REMOVE_RECIPIENT_FROM_ALLOWED_LIST_ROLE: [], + SET_PARAMETERS_ROLE: [], + UPDATE_SPENT_AMOUNT_ROLE: [], + }, + }, + StonksStETH_AllowedRecipientsRegistry: { + address: LIDO_CONTRACTS.StonksStETH_AllowedRecipientsRegistry, + roles: { + DEFAULT_ADMIN_ROLE: ["Voting"], + ADD_RECIPIENT_TO_ALLOWED_LIST_ROLE: [], + REMOVE_RECIPIENT_FROM_ALLOWED_LIST_ROLE: [], + SET_PARAMETERS_ROLE: [], + UPDATE_SPENT_AMOUNT_ROLE: [], + }, + }, + StonksStablecoins_AllowedRecipientsRegistry: { + address: LIDO_CONTRACTS.StonksStablecoins_AllowedRecipientsRegistry, + roles: { + DEFAULT_ADMIN_ROLE: ["Voting"], + ADD_RECIPIENT_TO_ALLOWED_LIST_ROLE: [], + REMOVE_RECIPIENT_FROM_ALLOWED_LIST_ROLE: [], + SET_PARAMETERS_ROLE: [], + UPDATE_SPENT_AMOUNT_ROLE: [], + }, + }, + AllowedTokensRegistry: { + address: LIDO_CONTRACTS.AllowedTokensRegistry, + roles: { + DEFAULT_ADMIN_ROLE: ["Voting"], + ADD_TOKEN_TO_ALLOWED_LIST_ROLE: [], + REMOVE_TOKEN_FROM_ALLOWED_LIST_ROLE: [], + }, + }, + // Arbitrum + L1ERC20TokenGateway_Arbitrum: { + address: LIDO_CONTRACTS.L1ERC20TokenGateway_Arbitrum, + roles: { + DEFAULT_ADMIN_ROLE: ["DualGovernance"], + DEPOSITS_DISABLER_ROLE: [], + DEPOSITS_ENABLER_ROLE: [], + WITHDRAWALS_DISABLER_ROLE: [], + WITHDRAWALS_ENABLER_ROLE: [], + }, + }, + // Optimism + L1TokensBridge_Optimism: { + address: LIDO_CONTRACTS.L1TokensBridge_Optimism, + roles: { + DEFAULT_ADMIN_ROLE: ["DualGovernance"], + DEPOSITS_DISABLER_ROLE: [], + DEPOSITS_ENABLER_ROLE: [], + WITHDRAWALS_DISABLER_ROLE: [], + WITHDRAWALS_ENABLER_ROLE: [], + }, + }, + // Polygon + ERC20Predicate_Polygon: { + address: LIDO_CONTRACTS.ERC20Predicate_Polygon, + roles: { + DEFAULT_ADMIN_ROLE: ["DualGovernance"], + MANAGER_ROLE: [], + }, + }, + // Base + L1ERC20TokenBridge_Base: { + address: LIDO_CONTRACTS.L1ERC20TokenBridge_Base, + roles: { + DEFAULT_ADMIN_ROLE: ["DualGovernance"], + DEPOSITS_DISABLER_ROLE: [], + DEPOSITS_ENABLER_ROLE: [], + WITHDRAWALS_DISABLER_ROLE: [], + WITHDRAWALS_ENABLER_ROLE: [], + }, + }, + L1ERC20Bridge_zkSync: { + address: LIDO_CONTRACTS.L1ERC20Bridge_zkSync, + roles: { + DEFAULT_ADMIN_ROLE: ["DualGovernance"], + DEPOSITS_DISABLER_ROLE: [], + DEPOSITS_ENABLER_ROLE: [], + WITHDRAWALS_DISABLER_ROLE: [], + WITHDRAWALS_ENABLER_ROLE: [], + }, + }, + // Mantle + L1ERC20TokenBridge_Mantle: { + address: LIDO_CONTRACTS.L1ERC20TokenBridge_Mantle, + roles: { + DEFAULT_ADMIN_ROLE: ["DualGovernance"], + DEPOSITS_DISABLER_ROLE: [], + DEPOSITS_ENABLER_ROLE: [], + WITHDRAWALS_DISABLER_ROLE: [], + WITHDRAWALS_ENABLER_ROLE: [], + }, + }, + + // Scroll + // Note: Scroll uses modified version of AccessControl from OZ, which allows grant roles only to + // standalone owner contract + // L1LidoGateway_Scroll: { + // address: LIDO_CONTRACTS.L1LidoGateway_Scroll, + // roles: { + // DEPOSITS_DISABLER_ROLE: [], + // DEPOSITS_ENABLER_ROLE: [], + // WITHDRAWALS_DISABLER_ROLE: [], + // WITHDRAWALS_ENABLER_ROLE: [], + // }, + // }, + + // Mode + L1ERC20TokenBridge_Mode: { + address: LIDO_CONTRACTS.L1ERC20TokenBridge_Mode, + roles: { + DEFAULT_ADMIN_ROLE: ["DualGovernance"], + DEPOSITS_DISABLER_ROLE: [], + DEPOSITS_ENABLER_ROLE: [], + WITHDRAWALS_DISABLER_ROLE: [], + WITHDRAWALS_ENABLER_ROLE: [], + }, + }, + + //Zircuit + L1ERC20TokenBridge_Zircuit: { + address: LIDO_CONTRACTS.L1ERC20TokenBridge_Zircuit, + roles: { + DEFAULT_ADMIN_ROLE: ["DualGovernance"], + DEPOSITS_DISABLER_ROLE: [], + DEPOSITS_ENABLER_ROLE: [], + WITHDRAWALS_DISABLER_ROLE: [], + WITHDRAWALS_ENABLER_ROLE: [], + }, + }, +} as const; + +export const MANAGED_CONTRACTS: ManagedContractsConfig = { + DSM: { + address: "0xfFA96D84dEF2EA035c7AB153D8B991128e3d72fD", + properties: { + owner: { property: "getOwner", managedBy: "Agent" }, + }, + }, + "LidoLocator :: Proxy": { + address: "0xC1d0b3DE6792Bf6b4b37EccdcC24e45978Cfd2Eb", + properties: { admin: { property: "proxy__getAdmin", managedBy: "Agent" } }, + }, + "StakingRouter :: Proxy": { + address: "0xFdDf38947aFB03C621C71b06C9C70bce73f12999", + properties: { admin: { property: "proxy__getAdmin", managedBy: "Agent" } }, + }, + "WithdrawalQueue :: Proxy": { + address: "0x889edC2eDab5f40e902b864aD4d7AdE8E412F9B1", + properties: { admin: { property: "proxy__getAdmin", managedBy: "Agent" } }, + }, + "WithdrawalVault :: Proxy": { + address: "0xB9D7934878B5FB9610B3fE8A5e441e8fad7E293f", + properties: { admin: { property: "proxy_getAdmin", managedBy: "Agent" } }, + }, + "AccountingOracle :: Proxy": { + address: "0x852deD011285fe67063a08005c71a85690503Cee", + properties: { admin: { property: "proxy__getAdmin", managedBy: "Agent" } }, + }, + "ValidatorsExitBusOracle :: Proxy": { + address: "0x0De4Ea0184c2ad0BacA7183356Aea5B8d5Bf5c6e", + properties: { admin: { property: "proxy__getAdmin", managedBy: "Agent" } }, + }, + InsuranceFund: { + address: "0x8B3f33234ABD88493c0Cd28De33D583B70beDe35", + properties: { owner: { property: "owner", managedBy: "Voting" } }, // ?? + }, + // TODO: Add missing contracts +}; diff --git a/scripts/permissions-migration/config/lido-contracts.ts b/scripts/permissions-migration/config/lido-contracts.ts new file mode 100644 index 00000000..5e74e1a9 --- /dev/null +++ b/scripts/permissions-migration/config/lido-contracts.ts @@ -0,0 +1,144 @@ +import bytes, { Address } from "../src/bytes"; + +export const LIDO_GENESIS_BLOCK = 11473216; + +export type LidoContractName = + | `Unknown(${Address})` + | "None" + | "DualGovernance" + | keyof typeof LIDO_CONTRACTS; + +export const LIDO_CONTRACTS = { + // Core Protocol + LidoLocator: "0xC1d0b3DE6792Bf6b4b37EccdcC24e45978Cfd2Eb", + Lido: "0xae7ab96520DE3A18E5e111B5EaAb095312D7fE84", + StakingRouter: "0xFdDf38947aFB03C621C71b06C9C70bce73f12999", + DepositSecurityModule: "0xffa96d84def2ea035c7ab153d8b991128e3d72fd", + ExecutionLayerRewardsVault: "0x388C818CA8B9251b393131C08a736A67ccB19297", + WithdrawalQueueERC721: "0x889edC2eDab5f40e902b864aD4d7AdE8E412F9B1", + WithdrawalVault: "0xb9d7934878b5fb9610b3fe8a5e441e8fad7e293f", + Burner: "0xD15a672319Cf0352560eE76d9e89eAB0889046D3", + MEVBoostRelayAllowedList: "0xF95f069F9AD107938F6ba802a3da87892298610E", + + // Oracle Contracts + AccountingOracle: "0x852deD011285fe67063a08005c71a85690503Cee", + AccountingOracleHashConsensus: "0xD624B08C83bAECF0807Dd2c6880C3154a5F0B288", + ValidatorExitBusOracle: "0x0De4Ea0184c2ad0BacA7183356Aea5B8d5Bf5c6e", + ValidatorExitBusHashConsensus: "0x7FaDB6358950c5fAA66Cb5EB8eE5147De3df355a", + OracleReportSanityChecker: "0x6232397ebac4f5772e53285B26c47914E9461E75", + OracleDaemonConfig: "0xbf05A929c3D7885a6aeAd833a992dA6E5ac23b09", + LegacyOracle: "0x442af784A788A5bd6F42A01Ebe9F287a871243fb", + + // DAO Contracts + DAOKernel: "0xb8FFC3Cd6e7Cf5a098A1c92F48009765B24088Dc", + Voting: "0x2e59A20f205bB85a89C53f1936454680651E618e", + Agent: "0x3e40D73EB977Dc6a537aF587D48316feE66E9C8c", + TokenManager: "0xf73a1260d222f447210581DDf212D915c09a3249", + Finance: "0xB9E5CBB9CA5b0d659238807E84D0176930753d86", + ACL: "0x9895f0f17cc1d1891b6f18ee0b483b6f221b37bb", + AragonPM: "0x0cb113890b04b49455dfe06554e2d784598a29c9", + VotingRepo: "0x4ee3118e3858e8d7164a634825bfe0f73d99c792", + LidoRepo: "0xF5Dc67E54FC96F993CD06073f71ca732C1E654B1", + LegacyOracleRepo: "0xF9339DE629973c60c4d2b76749c81E6F40960E3A", + CuratedModuleRepo: "0x0D97E876ad14DB2b183CFeEB8aa1A5C788eB1831", + SimpleDVTRepo: "0x2325b0a607808dE42D918DB07F925FFcCfBb2968", + OraclesGateSeal: "0x79243345edbe01a7e42edff5900156700d22611c", + + // Staking Modules + CuratedModule: "0x55032650b14df07b85bF18A3a3eC8E0Af2e028d5", + SimpleDVT: "0xaE7B191A31f627b4eB1d4DaC64eaB9976995b433", + CSModule: "0xdA7dE2ECdDfccC6c3AF10108Db212ACBBf9EA83F", + CSAccounting: "0x4d72BFF1BeaC69925F8Bd12526a39BAAb069e5Da", + CSFeeDistributor: "0xD99CC66fEC647E68294C6477B40fC7E0F6F618D0", + CSGateSeal: "0x5cFCa30450B1e5548F140C24A47E36c10CE306F0", + CSFeeOracle: "0x4D4074628678Bd302921c20573EEa1ed38DdF7FB", + CSHashConsensus: "0x71093efF8D8599b5fA340D665Ad60fA7C80688e4", + CSCommitteeMultisig: "0xc52fc3081123073078698f1eac2f1dc7bd71880f", + CSVerifier: "0x3dfc50f22aca652a0a6f28a0f892ab62074b5583", + + // Anchor Integration + AnchorVault: "0xA2F987A546D4CD1c607Ee8141276876C26b72Bdf", + bETH: "0x707f9118e33a9b8998bea41dd0d46f38bb963fc8", + + // EasyTrack + EasyTrack: "0xF0211b7660680B49De1A7E9f25C65660F0a13Fea", + EasyTrackEvmScriptExecutor: "0xFE5986E06210aC1eCC1aDCafc0cc7f8D63B3F977", + + // Easy Track Factories for token transfers + LOLStETH_AllowedRecipientsRegistry: + "0x48c4929630099b217136b64089E8543dB0E5163a", + RewardsShareStETH_AllowedRecipientsRegistry: + "0xdc7300622948a7AdaF339783F6991F9cdDD79776", + LegoLDO_AllowedRecipientsRegistry: + "0x97615f72c3428A393d65A84A3ea6BBD9ad6C0D74", + LegoStablecoins_AllowedRecipientsRegistry: + "0xb0FE4D300334461523D9d61AaD90D0494e1Abb43", + RCCStableCoins_AllowedRecipientsRegistry: + "0xDc1A0C7849150f466F07d48b38eAA6cE99079f80", + RCCStETH_AllowedRecipientsRegistry: + "0xAAC4FcE2c5d55D1152512fe5FAA94DB267EE4863", + PMLStablecoins_AllowedRecipientsRegistry: + "0xDFfCD3BF14796a62a804c1B16F877Cf7120379dB", + PMLStETH_AllowedRecipientsRegistry: + "0x7b9B8d00f807663d46Fb07F87d61B79884BC335B", + ATCStablecoins_AllowedRecipientsRegistry: + "0xe07305F43B11F230EaA951002F6a55a16419B707", + ATCStETH_AllowedRecipientsRegistry: + "0xd3950eB3d7A9B0aBf8515922c0d35D13e85a2c91", + TRPLDO_AllowedRecipientsRegistry: + "0x231Ac69A1A37649C6B06a71Ab32DdD92158C80b8", + GasSupplyStETH_AllowedRecipientsRegistry: + "0x49d1363016aA899bba09ae972a1BF200dDf8C55F", + AllianceOpsStablecoins_AllowedRecipientsRegistry: + "0x3B525F4c059F246Ca4aa995D21087204F30c9E2F", + StonksStETH_AllowedRecipientsRegistry: + "0x1a7cFA9EFB4D5BfFDE87B0FaEb1fC65d653868C0", + StonksStablecoins_AllowedRecipientsRegistry: + "0x3f0534CCcFb952470775C516DC2eff8396B8A368", + AllowedTokensRegistry: "0x4AC40c34f8992bb1e5E856A448792158022551ca", + + // Arbitrum + L1ERC20TokenGateway_Arbitrum: "0x0F25c1DC2a9922304f2eac71DCa9B07E310e8E5a", + + // Optimism + TokenRateNotifier_Optimism: "0xe6793B9e4FbA7DE0ee833F9D02bba7DB5EB27823", + L1TokensBridge_Optimism: "0x76943C0D61395d8F2edF9060e1533529cAe05dE6", + + // Polygon + ERC20Predicate_Polygon: "0x40ec5B33f54e0E8A33A975908C5BA1c14e5BbbDf", + + // Base + L1ERC20TokenBridge_Base: "0x9de443AdC5A411E83F1878Ef24C3F52C61571e72", + + // zkSync + L1Executor_zkSync: "0xFf7F4d05e3247374e86A3f7231A2Ed1CA63647F2", + L1ERC20Bridge_zkSync: "0x41527B2d03844dB6b0945f25702cB958b6d55989", + + // Mantle + L1ERC20TokenBridge_Mantle: "0x2D001d79E5aF5F65a939781FE228B267a8Ed468B", + + // Linea + L1TokenBridge_Linea: "0x051f1d88f0af5763fb888ec4378b4d8b29ea3319", + + // Scroll + L1LidoGateway_Scroll: "0x6625c6332c9f91f2d27c304e729b86db87a3f504", + + // Mode + L1ERC20TokenBridge_Mode: "0xD0DeA0a3bd8E4D55170943129c025d3fe0493F2A", + + //Zircuit + L1ERC20TokenBridge_Zircuit: "0x912C7271a6A3622dfb8B218eb46a6122aB046C79", + + // Emergency Brakes + EmergencyBrakesMultisig: "0x73b047fe6337183A454c5217241D780a932777bD", +} as const; + +export const LIDO_CONTRACTS_NAMES: Record = {}; + +for (const [name, address] of Object.entries(LIDO_CONTRACTS)) { + LIDO_CONTRACTS_NAMES[bytes.normalize(address)] = name as LidoContractName; +} + +export const CONTRACT_LABELS: Partial> = { + EasyTrackEvmScriptExecutor: "ET :: EVMScriptExecutor", +}; diff --git a/scripts/permissions-migration/index.ts b/scripts/permissions-migration/index.ts new file mode 100644 index 00000000..ff1bb98b --- /dev/null +++ b/scripts/permissions-migration/index.ts @@ -0,0 +1,46 @@ +import "dotenv/config"; +import { JsonRpcProvider } from "ethers"; + +import oz from "./src/oz-roles"; +import aragon from "./src/aragon-permissions"; +import managed from "./src/managed-contracts"; + +import { + ARAGON_CONTRACT_ROLES_CONFIG, + MANAGED_CONTRACTS, + OZ_CONTRACT_ROLES_CONFIG, +} from "./config/agent-transfer-permissions-config"; + +const RPC_URL = process.env.MAINNET_RPC_URL; + +if (!RPC_URL) { + throw new Error("RPC_URL env variable not set"); +} + +async function main() { + const provider = new JsonRpcProvider(RPC_URL); + console.log(`## Lido Permissions Transitions`); + console.log( + aragon.formatContractPermissionsSection( + await aragon.collectPermissionsData( + provider, + ARAGON_CONTRACT_ROLES_CONFIG + ) + ) + ); + console.log( + oz.formatContractRolesSection( + await oz.collectRolesInfo(provider, OZ_CONTRACT_ROLES_CONFIG) + ) + ); + console.log( + managed.formatControlledContractsSection( + await managed.collectManagedContractsInfo(provider, MANAGED_CONTRACTS) + ) + ); +} + +main().catch((error) => { + console.error(error); + process.exitCode = 1; +}); diff --git a/scripts/permissions-migration/src/aragon-permissions.ts b/scripts/permissions-migration/src/aragon-permissions.ts new file mode 100644 index 00000000..b5aec105 --- /dev/null +++ b/scripts/permissions-migration/src/aragon-permissions.ts @@ -0,0 +1,354 @@ +import { id, JsonRpcProvider } from "ethers"; + +import md from "./markdown"; +import bytes, { Address, HexStrPrefixed } from "./bytes"; +import { + decodeAddress, + decodeBool, + decodeBytes32, + makeContractCall, + sortLogs, +} from "./utils"; + +import { + CONTRACT_LABELS, + LIDO_CONTRACTS, + LIDO_CONTRACTS_NAMES, + LIDO_GENESIS_BLOCK, + LidoContractName, +} from "../config/lido-contracts"; + +export interface AragonPermissionConfig { + manager: LidoContractName; + grantedTo?: LidoContractName[]; +} + +export interface AragonContractPermissionConfig { + address: Address; + permissions: Record; +} + +export type AragonContractPermissionConfigs = Record< + string, + AragonContractPermissionConfig +>; + +interface AragonPermissionInfo { + name: string; + isModified: boolean; + oldManager: LidoContractName; + newManager: LidoContractName; + holdersToGrantRole: LidoContractName[]; + holdersToRevokeRole: LidoContractName[]; + holderAlreadyGrantedWithRole: LidoContractName[]; +} + +interface AragonPermissionsInfo { + [contractName: string]: AragonPermissionInfo[]; +} + +interface AragonPermissionHolders { + managers: { + [app: Address]: { + [role: HexStrPrefixed]: Address; + }; + }; + permissions: { + [app: Address]: { + [role: HexStrPrefixed]: Address[]; + }; + }; +} + +function formatContractPermissionsSection( + aragonContractsInfo: AragonPermissionsInfo +) { + const resSectionLines: string[] = ["### Aragon Roles Transition \n"]; + + let totalModifiedRoles = 0; + + for (const [contractName, roles] of Object.entries(aragonContractsInfo)) { + if (roles.length === 0) continue; + resSectionLines.push(`#### ${contractName}\n`); + const [modifiedRolesCount, rowsText] = formatPermissionsInfoTable(roles); + resSectionLines.push(rowsText); + resSectionLines.push("\n"); + totalModifiedRoles += modifiedRolesCount; + } + + resSectionLines.push(`\n **Total Roles Modified: ${totalModifiedRoles}** \n`); + + return resSectionLines.join("\n"); +} + +function formatPermissionsInfoTable(aragonRolesInfo: AragonPermissionInfo[]) { + const columnHeaders = ["Role", "Manager", "Revoked", "Granted"]; + const rows: string[][] = []; + + let modifiedRolesCount = 0; + + for (const role of aragonRolesInfo.sort( + (a, b) => Number(!a.isModified) - Number(!b.isModified) + )) { + if (role.isModified) { + modifiedRolesCount += 1; + } + + const oldManagerLabel = md.label( + role.oldManager === "None" ? md.empty() : role.oldManager + ); + const newManagerLabel = md.label( + role.newManager === "None" ? md.empty() : role.newManager + ); + + const managerTransition = + role.oldManager != role.newManager + ? md.bold(`${oldManagerLabel} -> ${newManagerLabel}`) + : md.label(oldManagerLabel); + + const unknownRoleHolders = role.holdersToRevokeRole.filter((holderName) => + holderName.startsWith("Unknown") + ); + const knownRoleHolders = role.holdersToRevokeRole.filter( + (holderName) => !holderName.startsWith("Unknown") + ); + + const revokedFromItems = + role.holdersToRevokeRole.length === 0 + ? [md.empty()] + : knownRoleHolders.map((roleHolder) => + md.bold(md.label(CONTRACT_LABELS[roleHolder] ?? roleHolder)) + ); + + if (unknownRoleHolders.length > 0) { + revokedFromItems.push( + md.label(`+${unknownRoleHolders.length} **UNKNOWN** holders`) + ); + } + + const grantedToItems: string[] = [ + ...role.holderAlreadyGrantedWithRole.map((roleHolder) => + md.label(CONTRACT_LABELS[roleHolder] ?? roleHolder) + ), + ...role.holdersToGrantRole.map((roleHolder) => + md.bold(CONTRACT_LABELS[roleHolder] ?? md.label(roleHolder)) + ), + ]; + + if (grantedToItems.length === 0) { + grantedToItems.push(md.empty()); + } + + const roleNameText = role.isModified + ? md.modified(md.roleName(role.name)) + : md.unchanged(md.roleName(role.name)); + + rows.push([ + roleNameText, + managerTransition, + revokedFromItems.join(", "), + grantedToItems.join(", "), + ]); + } + return [ + modifiedRolesCount, + [md.header(columnHeaders), ...rows.map((row) => md.row(row))].join("\n"), + ] as const; +} + +async function collectPermissionsData( + provider: JsonRpcProvider, + config: AragonContractPermissionConfigs +) { + const { managers, permissions } = await fetchACLPermissionsInfo(provider); + const aragonContractsInfo: AragonPermissionsInfo = {}; + + for (const [contractName, contractInfo] of Object.entries(config)) { + const address = bytes.normalize(contractInfo.address); + aragonContractsInfo[contractName] = []; + + for (const [permissionName, { manager, grantedTo }] of Object.entries( + contractInfo.permissions + )) { + const permissionHash = await getPermissionHash( + provider, + address, + permissionName + ); + + const newManager = manager; + const oldManagerAddress = managers[address]?.[permissionHash]; + const oldManager = oldManagerAddress + ? LIDO_CONTRACTS_NAMES[oldManagerAddress] + : "None"; + + const currentlyGrantedTo = ( + permissions[address]?.[permissionHash] ?? [] + ).map((roleHolderAddress) => { + return LIDO_CONTRACTS_NAMES[roleHolderAddress] === undefined + ? (`Unknown(${roleHolderAddress})` as LidoContractName) + : LIDO_CONTRACTS_NAMES[roleHolderAddress]; + }); + + const holdersToGrantRole = (grantedTo ?? []).filter( + (roleHolder) => !currentlyGrantedTo.includes(roleHolder) + ); + const holdersToRevokeRole = currentlyGrantedTo.filter( + (roleHolderName) => !(grantedTo ?? []).includes(roleHolderName) + ); + const holderAlreadyGrantedWithRole = (grantedTo ?? []).filter( + (roleHolder) => currentlyGrantedTo.includes(roleHolder) + ); + + aragonContractsInfo[contractName].push({ + name: permissionName, + oldManager, + newManager, + isModified: + newManager !== oldManager || + holdersToGrantRole.length > 0 || + holdersToRevokeRole.length > 0, + holdersToGrantRole, + holdersToRevokeRole, + holderAlreadyGrantedWithRole, + }); + } + } + return aragonContractsInfo; +} + +async function fetchACLPermissionsInfo(provider: JsonRpcProvider) { + const aclPermissionsEvents = await getACLPermissionEvents(provider, { + fromBlock: LIDO_GENESIS_BLOCK, + }); + + const result: AragonPermissionHolders = { + managers: {}, + permissions: {}, + }; + + for (const event of aclPermissionsEvents) { + if (event.name === "ChangePermissionManager") { + const { app, role, manager } = event.args; + if (!result.managers[app]) { + result.managers[app] = {}; + } + result.managers[app][role] = manager!; + } else if (event.name === "SetPermission") { + const { entity, app, role, allowed } = event.args; + + if (!result.permissions[app]) { + result.permissions[app] = {}; + } + + if (!result.permissions[app][role]) { + result.permissions[app][role] = []; + } + + if (allowed && !result.permissions[app][role].includes(entity!)) { + if (result.permissions[app][role].includes(entity!)) { + } + result.permissions[app][role].push(entity!); + } else { + if (!result.permissions[app][role].includes(entity!)) { + } + result.permissions[app][role] = result.permissions[app][role].filter( + (e) => e !== entity + ); + } + } + // TODO: Check that SetPermissionParams events doesn't impact the granted permissions + } + + return result; +} + +async function getACLPermissionEvents( + provider: JsonRpcProvider, + filterRange?: { fromBlock: number; toBlock?: number } +) { + const setPermissionTopic = id("SetPermission(address,address,bytes32,bool)"); + const setPermissionParamsTopic = id( + "SetPermissionParams(address,address,bytes32,bytes32)" + ); + const changePermissionManagerTopic = id( + "ChangePermissionManager(address,bytes32,address)" + ); + + const filterParams = { + address: LIDO_CONTRACTS.ACL, + fromBlock: filterRange?.fromBlock, + toBlock: filterRange?.toBlock, + }; + const [ + setPermissionLogs, + setPermissionParamsLogs, + changePermissionManagerLogs, + ] = await Promise.all([ + provider.getLogs({ ...filterParams, topics: [setPermissionTopic] }), + provider.getLogs({ ...filterParams, topics: [setPermissionParamsTopic] }), + provider.getLogs({ + ...filterParams, + topics: [changePermissionManagerTopic], + }), + ]); + return sortLogs([ + ...setPermissionLogs, + ...setPermissionParamsLogs, + ...changePermissionManagerLogs, + ]).map((log) => { + const commonEventInfo = { + blockNumber: log.blockNumber, + index: log.index, + transactionIndex: log.transactionIndex, + }; + if (log.topics[0] === setPermissionTopic) { + return { + ...commonEventInfo, + name: "SetPermission", + args: { + entity: decodeAddress(log.topics[1]), + app: decodeAddress(log.topics[2]), + role: decodeBytes32(log.topics[3]), + allowed: decodeBool(log.data), + }, + } as const; + } else if (log.topics[0] === setPermissionParamsTopic) { + return { + ...commonEventInfo, + name: "SetPermissionParams", + args: { + entity: decodeAddress(log.topics[1]), + app: decodeAddress(log.topics[2]), + role: decodeBytes32(log.topics[3]), + paramsHash: decodeBytes32(log.data), + } as const, + }; + } else if (log.topics[0] === changePermissionManagerTopic) { + return { + ...commonEventInfo, + name: "ChangePermissionManager", + args: { + app: decodeAddress(log.topics[1]), + role: decodeBytes32(log.topics[2]), + manager: decodeAddress(log.topics[3]), + } as const, + }; + } else { + throw new Error(`Unexpected event topic ${log.topics[0]}`); + } + }); +} + +async function getPermissionHash( + provider: JsonRpcProvider, + address: Address, + permissionName: string +) { + return makeContractCall(provider, address, permissionName); +} + +export default { + formatContractPermissionsSection, + collectPermissionsData, +}; diff --git a/scripts/permissions-migration/src/bytes.ts b/scripts/permissions-migration/src/bytes.ts new file mode 100644 index 00000000..cb29f937 --- /dev/null +++ b/scripts/permissions-migration/src/bytes.ts @@ -0,0 +1,29 @@ +export type Address = `0x${string}`; +export type HexStrNonPrefixed = string; +export type HexStrPrefixed = `0x${HexStrNonPrefixed}`; +export type HexStr = HexStrPrefixed | HexStrNonPrefixed; + +function normalize(bytes: T): HexStrPrefixed { + return prefix0x(bytes.toLowerCase() as T); +} + +function prefix0x(bytes: T): HexStrPrefixed { + return is0xPrefixed(bytes) ? bytes : (("0x" + bytes) as HexStrPrefixed); +} + +function strip0x(bytes: HexStr): HexStrNonPrefixed { + return bytes.startsWith("0x") ? bytes.slice(2) : bytes; +} + +function join(...bytes: HexStr[]): HexStrPrefixed { + return prefix0x(bytes.reduce((res, b) => res + strip0x(b), "")); +} + +function is0xPrefixed(bytes: HexStr): bytes is HexStrPrefixed { + return bytes.startsWith("0x"); +} + +export default { + join, + normalize, +}; diff --git a/scripts/permissions-migration/src/managed-contracts.ts b/scripts/permissions-migration/src/managed-contracts.ts new file mode 100644 index 00000000..975c9a12 --- /dev/null +++ b/scripts/permissions-migration/src/managed-contracts.ts @@ -0,0 +1,123 @@ +import { JsonRpcProvider } from "ethers"; + +import md from "./markdown"; +import { Address } from "./bytes"; +import { decodeAddress, makeContractCall } from "./utils"; + +import { + CONTRACT_LABELS, + LIDO_CONTRACTS_NAMES, + LidoContractName, +} from "../config/lido-contracts"; + +export type ManagedContractsConfig = Record; + +interface ManagedContractConfig { + address: Address; + properties: Record; +} + +interface ManagedContractProperty { + property: string; + managedBy: LidoContractName; +} + +interface ManagedContractPropertyInfo { + address: Address; + isModified: boolean; + contractName: string; + propertyName: string; + propertyGetter: string; + oldManagedBy: LidoContractName; + newManagedBy: LidoContractName; +} + +function formatControlledContractsSection( + managedContractsPropertiesInfo: ManagedContractPropertyInfo[] +) { + const resSectionLines: string[] = ["### Managed Contracts Updates \n"]; + const [totalModifiedRoles, tableText] = formatControlledContractsTable( + managedContractsPropertiesInfo + ); + + resSectionLines.push(tableText); + resSectionLines.push(`\n **Total Roles Modified: ${totalModifiedRoles}** \n`); + + return resSectionLines.join("\n"); +} + +function formatControlledContractsTable( + managedContractsPropertiesInfo: ManagedContractPropertyInfo[] +) { + const columnHeaders = ["Contract", "Property", "Old Manager", "New Manager"]; + const rows: string[][] = []; + + let modifiedPropertiesCount = 0; + for (const info of managedContractsPropertiesInfo) { + if (info.isModified) { + modifiedPropertiesCount += 1; + } + const contractNameText = info.isModified + ? md.bold(md.modified(info.contractName)) + : md.unchanged(info.contractName); + const newManagedBy = info.isModified + ? md.bold( + md.label(CONTRACT_LABELS[info.newManagedBy] ?? info.newManagedBy) + ) + : md.label(CONTRACT_LABELS[info.newManagedBy] ?? info.newManagedBy); + rows.push([ + contractNameText, + md.label(info.propertyGetter + "()"), + md.label(CONTRACT_LABELS[info.oldManagedBy] ?? info.oldManagedBy), + CONTRACT_LABELS[info.oldManagedBy] ?? newManagedBy, + ]); + } + + return [ + modifiedPropertiesCount, + [md.header(columnHeaders), ...rows.map((row) => md.row(row))].join("\n"), + ] as const; +} + +async function collectManagedContractsInfo( + provider: JsonRpcProvider, + config: ManagedContractsConfig +) { + const controlledContractsInfo: ManagedContractPropertyInfo[] = []; + + for (const [contractName, { address, properties }] of Object.entries( + config + )) { + for (const [propertyName, { property, managedBy }] of Object.entries( + properties + )) { + const currentlyManagedByAddress = decodeAddress( + await makeContractCall(provider, address, property) + ); + if (!LIDO_CONTRACTS_NAMES[currentlyManagedByAddress]) { + throw new Error( + `Unknown lido contract address ${currentlyManagedByAddress}` + ); + } + const currentlyManagedBy = + LIDO_CONTRACTS_NAMES[currentlyManagedByAddress]; + + controlledContractsInfo.push({ + address, + isModified: managedBy !== currentlyManagedBy, + contractName, + propertyName, + propertyGetter: property, + newManagedBy: managedBy, + oldManagedBy: currentlyManagedBy, + }); + } + } + + return controlledContractsInfo; +} + +export default { + formatControlledContractsSection, + collectManagedContractsInfo, +}; diff --git a/scripts/permissions-migration/src/markdown.ts b/scripts/permissions-migration/src/markdown.ts new file mode 100644 index 00000000..8d05f8ca --- /dev/null +++ b/scripts/permissions-migration/src/markdown.ts @@ -0,0 +1,30 @@ +export default { + row(items: string[]) { + return `| ${items.join(" | ")} |`; + }, + header(columns: string[]) { + return [ + this.row(columns), + this.row(Array(columns.length).fill("---")), + ].join("\n"); + }, + bold(text: string) { + return `**${text}**`; + }, + label(text: string) { + return `\`${text}\``; + }, + empty() { + return "∅"; + }, + roleName(rawRoleName: string) { + const roleNameParts = rawRoleName.split("_"); + return roleNameParts.join(" "); + }, + modified(text: string) { + return `⚠️ **${text}**`; + }, + unchanged(text: string) { + return `${text}`; + }, +}; diff --git a/scripts/permissions-migration/src/oz-roles.ts b/scripts/permissions-migration/src/oz-roles.ts new file mode 100644 index 00000000..1c02f4a6 --- /dev/null +++ b/scripts/permissions-migration/src/oz-roles.ts @@ -0,0 +1,295 @@ +import { AbiCoder, ethers, id, JsonRpcProvider } from "ethers"; + +import md from "./markdown"; +import { makeContractCall, sortLogs } from "./utils"; +import bytes, { Address, HexStrPrefixed } from "./bytes"; + +import { + LidoContractName, + LIDO_CONTRACTS_NAMES, + LIDO_GENESIS_BLOCK, + CONTRACT_LABELS, +} from "../config/lido-contracts"; + +export type OZContractRolesConfig = Record; + +interface OZContractConfig { + address: Address; + roles: Record; +} + +interface OZRoleInfo { + roleName: string; + isModified: boolean; + holdersToGrantRole: LidoContractName[]; + holdersToRevokeRole: LidoContractName[]; + holderAlreadyGrantedWithRole: LidoContractName[]; +} + +interface OZRolesInfo { + [contractName: string]: OZRoleInfo[]; +} + +interface OZRoleHolders { + [contract: Address]: { + [role: HexStrPrefixed]: Address[]; + }; +} + +const DEFAULT_ADMIN_ROLE_HASH = ethers.ZeroHash; + +function formatContractRolesSection(ozContractsInfo: OZRolesInfo) { + const resSectionLines: string[] = ["### OpenZeppelin Roles Transition \n"]; + + let totalModifiedRoles = 0; + + for (const [contractName, roles] of Object.entries(ozContractsInfo)) { + if (roles.length === 0) continue; + resSectionLines.push(`#### ${contractName}\n`); + const [modifiedRolesCount, rowsText] = formatRolesInfoTable(roles); + resSectionLines.push(rowsText); + resSectionLines.push("\n"); + totalModifiedRoles += modifiedRolesCount; + } + + resSectionLines.push(`\n **Total Roles Modified: ${totalModifiedRoles}** \n`); + + return resSectionLines.join("\n"); +} + +async function collectRolesInfo( + provider: JsonRpcProvider, + config: Record +) { + const ozRolesInfo: OZRolesInfo = {}; + + const ozRolesHolders = await fetchRoleHolders(provider, config); + + for (const [contractName, { address, roles }] of Object.entries(config)) { + ozRolesInfo[contractName] = []; + + for (const [roleName, desiredRoleGrantees] of Object.entries(roles)) { + const roleHash = await getRoleHash(provider, address, roleName); + + const currentlyGrantedTo = (ozRolesHolders[address][roleHash] || []).map( + (roleHolderAddress) => { + if (LIDO_CONTRACTS_NAMES[roleHolderAddress] === undefined) { + throw new Error( + `Unknown contract with address ${roleHolderAddress}` + ); + } + return LIDO_CONTRACTS_NAMES[roleHolderAddress]; + } + ); + + const holdersToGrantRole = desiredRoleGrantees.filter( + (roleHolder) => !currentlyGrantedTo.includes(roleHolder) + ); + const holdersToRevokeRole = currentlyGrantedTo.filter( + (roleHolderName) => !desiredRoleGrantees.includes(roleHolderName) + ); + const holderAlreadyGrantedWithRole = desiredRoleGrantees.filter( + (roleHolder) => currentlyGrantedTo.includes(roleHolder) + ); + + ozRolesInfo[contractName].push({ + roleName, + holdersToGrantRole, + holdersToRevokeRole, + holderAlreadyGrantedWithRole, + isModified: + holdersToGrantRole.length > 0 || holdersToRevokeRole.length > 0, + }); + } + } + return ozRolesInfo; +} + +function formatRolesInfoTable(ozRolesInfo: OZRoleInfo[]) { + const columnHeaders = ["Role", "Revoked", "Granted"]; + const rows: string[][] = []; + + let modifiedRolesCount = 0; + + for (const role of ozRolesInfo.sort( + (a, b) => Number(!a.isModified) - Number(!b.isModified) + )) { + if (role.isModified) { + modifiedRolesCount += 1; + } + + const revokedFromItems = + role.holdersToRevokeRole.length === 0 + ? [md.empty()] + : role.holdersToRevokeRole.map((roleHolder) => + md.bold(md.label(CONTRACT_LABELS[roleHolder] ?? roleHolder)) + ); + + const grantedToItems: string[] = [ + ...role.holderAlreadyGrantedWithRole.map((roleHolder) => + md.label(CONTRACT_LABELS[roleHolder] ?? roleHolder) + ), + ...role.holdersToGrantRole.map((roleHolder) => + md.bold(md.label(CONTRACT_LABELS[roleHolder] ?? roleHolder)) + ), + ]; + + if (grantedToItems.length === 0) { + grantedToItems.push(md.empty()); + } + + const roleNameText = role.isModified + ? md.modified(md.roleName(role.roleName)) + : md.unchanged(md.roleName(role.roleName)); + + rows.push([ + roleNameText, + revokedFromItems.join(", "), + grantedToItems.join(", "), + ]); + } + return [ + modifiedRolesCount, + [md.header(columnHeaders), ...rows.map((row) => md.row(row))].join("\n"), + ] as const; +} + +async function fetchRoleHolders( + provider: JsonRpcProvider, + config: Record +) { + const ozRolesHolders: OZRoleHolders = {}; + + for (const { address: ozContractAddress } of Object.values(config)) { + ozRolesHolders[ozContractAddress] = {}; + + const roleGrantedRevokedEvents = await getRoleGrantedRevokedEvents( + provider, + ozContractAddress, + { + fromBlock: LIDO_GENESIS_BLOCK, + } + ); + + for (const event of roleGrantedRevokedEvents) { + const role = bytes.normalize(event.args.role); + const account = bytes.normalize(event.args.account); + if (!ozRolesHolders[ozContractAddress][role]) { + ozRolesHolders[ozContractAddress][role] = []; + } + + if (event.eventName === "RoleGranted") { + ozRolesHolders[ozContractAddress][role].push(account); + } else if (event.eventName === "RoleRevoked") { + ozRolesHolders[ozContractAddress][role] = ozRolesHolders[ + ozContractAddress + ][role].filter((roleHolder) => roleHolder !== account); + } else { + throw Error(`Unknown event name ${event.eventName}`); + } + } + } + + return ozRolesHolders; +} + +async function checkRoleAdmins( + provider: JsonRpcProvider, + config: Record +) { + for (const [contractName, { address, roles }] of Object.entries(config)) { + const roleNames = Object.keys(roles); + + const roleHashesAndAdmins = await Promise.all( + roleNames.map(async (roleName) => { + const roleHash = await getRoleHash(provider, address, roleName); + return [ + roleHash, + await getRoleAdminHash(provider, address, roleHash), + ] as [roleHash: string, roleAdmin: string]; + }) + ); + + console.log(`Checking role admins for "${contractName}":`); + for (let i = 0; i < roleNames.length; ++i) { + const [roleHash, roleAdminHash] = roleHashesAndAdmins[i]; + console.log(` - ${roleNames[i]}(${roleHash}): ${roleAdminHash}`); + if (bytes.normalize(roleAdminHash) !== DEFAULT_ADMIN_ROLE_HASH) { + throw new Error(`🚨 Unexpected Role Admin`); + } + } + console.log(); + + // Protection from the API requests per second limit + await new Promise((resolve) => setTimeout(resolve, 500)); + } +} + +async function getRoleAdminHash( + provider: JsonRpcProvider, + address: Address, + roleHash: string +) { + return makeContractCall( + provider, + address, + "getRoleAdmin", + ["bytes32"], + [roleHash] + ); +} + +async function getRoleHash( + provider: JsonRpcProvider, + address: Address, + roleName: string +) { + return makeContractCall(provider, address, roleName); +} + +async function getRoleGrantedRevokedEvents( + provider: JsonRpcProvider, + contract: Address, + filterRange?: { fromBlock: number; toBlock?: number } +) { + const roleGrantedTopic = id("RoleGranted(bytes32,address,address)"); + const roleRevokedTopic = id("RoleRevoked(bytes32,address,address)"); + const filterParams = { + address: contract, + fromBlock: filterRange?.fromBlock, + toBlock: filterRange?.toBlock, + }; + const [grantRoleLogs, revokeRoleLogs] = await Promise.all([ + provider.getLogs({ ...filterParams, topics: [roleGrantedTopic] }), + provider.getLogs({ ...filterParams, topics: [roleRevokedTopic] }), + ]); + return sortLogs([...grantRoleLogs, ...revokeRoleLogs]).map((log) => { + if (log.topics.length !== 4) { + throw new Error("Unexpected topics length"); + } + return { + eventName: + log.topics[0] === roleGrantedTopic ? "RoleGranted" : "RoleRevoked", + blockNumber: log.blockNumber, + index: log.index, + transactionIndex: log.transactionIndex, + args: { + role: log.topics[1], + account: AbiCoder.defaultAbiCoder().decode( + ["address"], + log.topics[2] + )[0] as HexStrPrefixed, + sender: AbiCoder.defaultAbiCoder().decode( + ["address"], + log.topics[3] + )[0] as HexStrPrefixed, + }, + }; + }); +} + +export default { + collectRolesInfo, + formatContractRolesSection, + checkRoleAdmins, +}; diff --git a/scripts/permissions-migration/src/utils.ts b/scripts/permissions-migration/src/utils.ts new file mode 100644 index 00000000..0bd23929 --- /dev/null +++ b/scripts/permissions-migration/src/utils.ts @@ -0,0 +1,62 @@ +import { AbiCoder, id, JsonRpcProvider, Log } from "ethers"; +import bytes, { Address, HexStrPrefixed } from "./bytes"; + +export function startCase(text: string) { + return ( + text + // Replace any non-alphanumeric sequences with a space + .replace(/([A-Z])/g, " $1") // Insert space before uppercase letters + .replace(/[_-]+/g, " ") // Replace underscores and hyphens with spaces + .trim() // Remove extra spaces at the start and end + .split(/\s+/) // Split by one or more spaces + .map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()) // Capitalize each word + .join(" ") + ); // Join words with a single space +} + +export async function makeContractCall( + provider: JsonRpcProvider, + address: Address, + methodName: string, + argTypes: string[] = [], + argValues: any[] = [] +) { + const methodId = id(methodName + `(${argTypes.join(",")})`).slice(0, 10); + return bytes.normalize( + await provider.call({ + to: address, + data: + argTypes.length > 0 + ? bytes.join( + methodId, + AbiCoder.defaultAbiCoder().encode(argTypes, argValues) + ) + : methodId, + }) + ); +} + +export function sortLogs(logs: Log[]) { + return logs.sort((a, b) => { + if (a.blockNumber !== b.blockNumber) { + return a.blockNumber - b.blockNumber; // Sort by block number + } + return a.index - b.index; // Sort by log index + }); +} + +export function decodeAddress(encodedAddress: string): Address { + return bytes.normalize( + AbiCoder.defaultAbiCoder().decode(["address"], encodedAddress)[0] + ); +} + +export function decodeBool(encodedBool: string): boolean { + return AbiCoder.defaultAbiCoder().decode(["bool"], encodedBool)[0] as boolean; +} + +export function decodeBytes32(encodedBytes32: string): HexStrPrefixed { + return bytes.normalize( + AbiCoder.defaultAbiCoder().decode(["bytes32"], encodedBytes32)[0] + ); +} From 776581471d37849d618728ae936cccfc04e1a50c Mon Sep 17 00:00:00 2001 From: Roman Kolpakov Date: Thu, 5 Dec 2024 16:17:38 +0300 Subject: [PATCH 2/4] Iterate a script completenes & add parsing of aragon deploy tx --- .../agent-transfer-permissions-config.ts | 317 ++++++++++-------- .../config/lido-contracts.ts | 1 + scripts/permissions-migration/index.ts | 2 + .../src/aragon-deploy.ts | 47 +++ 4 files changed, 228 insertions(+), 139 deletions(-) create mode 100644 scripts/permissions-migration/src/aragon-deploy.ts diff --git a/scripts/permissions-migration/config/agent-transfer-permissions-config.ts b/scripts/permissions-migration/config/agent-transfer-permissions-config.ts index ef7f635c..e43c86bb 100644 --- a/scripts/permissions-migration/config/agent-transfer-permissions-config.ts +++ b/scripts/permissions-migration/config/agent-transfer-permissions-config.ts @@ -46,7 +46,7 @@ export const ARAGON_CONTRACT_ROLES_CONFIG: AragonContractPermissionConfigs = { address: LIDO_CONTRACTS.TokenManager, permissions: { ISSUE_ROLE: { manager: "Voting" }, - ASSIGN_ROLE: { manager: "Voting" }, + ASSIGN_ROLE: { manager: "Voting", grantedTo: ["Voting"] }, BURN_ROLE: { manager: "Voting" }, MINT_ROLE: { manager: "Voting" }, REVOKE_VESTINGS_ROLE: { manager: "Voting" }, @@ -69,13 +69,13 @@ export const ARAGON_CONTRACT_ROLES_CONFIG: AragonContractPermissionConfigs = { address: LIDO_CONTRACTS.Agent, permissions: { ADD_PROTECTED_TOKEN_ROLE: { manager: "Voting" }, + REMOVE_PROTECTED_TOKEN_ROLE: { manager: "Voting" }, TRANSFER_ROLE: { manager: "Voting", grantedTo: ["Finance"] }, RUN_SCRIPT_ROLE: { manager: "DualGovernance", grantedTo: ["DualGovernance"], }, SAFE_EXECUTE_ROLE: { manager: "None" }, - REMOVE_PROTECTED_TOKEN_ROLE: { manager: "Voting" }, DESIGNATE_SIGNER_ROLE: { manager: "Voting" }, EXECUTE_ROLE: { manager: "DualGovernance", @@ -86,63 +86,81 @@ export const ARAGON_CONTRACT_ROLES_CONFIG: AragonContractPermissionConfigs = { }, ACL: { address: LIDO_CONTRACTS.ACL, - permissions: { CREATE_PERMISSIONS_ROLE: { manager: "DualGovernance" } }, + permissions: { CREATE_PERMISSIONS_ROLE: { manager: "Agent" } }, }, AragonPM: { address: LIDO_CONTRACTS.AragonPM, permissions: { - CREATE_REPO_ROLE: { manager: "Agent" }, + CREATE_REPO_ROLE: { manager: "None" }, + }, + }, + EVMScriptRegistry: { + address: LIDO_CONTRACTS.EVMScriptRegistry, + permissions: { + REGISTRY_ADD_EXECUTOR_ROLE: { manager: "None" }, + REGISTRY_MANAGER_ROLE: { manager: "None" }, }, }, VotingRepo: { address: LIDO_CONTRACTS.VotingRepo, permissions: { - CREATE_VERSION_ROLE: { manager: "Agent" }, + CREATE_VERSION_ROLE: { manager: "None" }, }, }, LidoRepo: { address: LIDO_CONTRACTS.LidoRepo, permissions: { - CREATE_VERSION_ROLE: { manager: "Agent" }, + CREATE_VERSION_ROLE: { manager: "None" }, }, }, LegacyOracleRepo: { address: LIDO_CONTRACTS.LegacyOracleRepo, permissions: { - CREATE_VERSION_ROLE: { manager: "Agent" }, + CREATE_VERSION_ROLE: { manager: "None" }, }, }, CuratedModuleRepo: { address: LIDO_CONTRACTS.CuratedModuleRepo, permissions: { - CREATE_VERSION_ROLE: { manager: "Agent" }, + CREATE_VERSION_ROLE: { manager: "None" }, }, }, SimpleDVTRepo: { address: LIDO_CONTRACTS.SimpleDVTRepo, permissions: { - CREATE_VERSION_ROLE: { manager: "Agent" }, + CREATE_VERSION_ROLE: { manager: "None" }, }, }, // Staking Modules CuratedModule: { address: LIDO_CONTRACTS.CuratedModule, permissions: { - STAKING_ROUTER_ROLE: { manager: "Agent" }, - MANAGE_NODE_OPERATOR_ROLE: { manager: "Agent" }, - SET_NODE_OPERATOR_LIMIT_ROLE: { manager: "Agent" }, - MANAGE_SIGNING_KEYS: { + STAKING_ROUTER_ROLE: { manager: "Agent", grantedTo: ["StakingRouter"] }, + MANAGE_NODE_OPERATOR_ROLE: { manager: "Agent", grantedTo: ["Agent"] }, + SET_NODE_OPERATOR_LIMIT_ROLE: { manager: "Agent", grantedTo: ["EasyTrackEvmScriptExecutor"], }, + MANAGE_SIGNING_KEYS: { + manager: "Agent", + }, }, }, SimpleDVT: { address: LIDO_CONTRACTS.SimpleDVT, permissions: { - STAKING_ROUTER_ROLE: { manager: "Agent" }, - MANAGE_NODE_OPERATOR_ROLE: { manager: "Agent" }, - SET_NODE_OPERATOR_LIMIT_ROLE: { manager: "Agent" }, + STAKING_ROUTER_ROLE: { + manager: "Agent", + grantedTo: ["StakingRouter", "EasyTrackEvmScriptExecutor"], + }, + MANAGE_NODE_OPERATOR_ROLE: { + manager: "Agent", + grantedTo: ["EasyTrackEvmScriptExecutor"], + }, + SET_NODE_OPERATOR_LIMIT_ROLE: { + manager: "Agent", + grantedTo: ["EasyTrackEvmScriptExecutor"], + }, MANAGE_SIGNING_KEYS: { manager: "EasyTrackEvmScriptExecutor", grantedTo: ["EasyTrackEvmScriptExecutor"], @@ -266,24 +284,24 @@ export const OZ_CONTRACT_ROLES_CONFIG: OZContractRolesConfig = { MODULE_MANAGER_ROLE: [], PAUSE_ROLE: ["CSGateSeal"], RECOVERER_ROLE: [], - REPORT_EL_REWARDS_STEALING_PENALTY_ROLE: [], + REPORT_EL_REWARDS_STEALING_PENALTY_ROLE: ["CSCommitteeMultisig"], RESUME_ROLE: [], - SETTLE_EL_REWARDS_STEALING_PENALTY_ROLE: [], - STAKING_ROUTER_ROLE: [], - VERIFIER_ROLE: [], + SETTLE_EL_REWARDS_STEALING_PENALTY_ROLE: ["EasyTrackEvmScriptExecutor"], + STAKING_ROUTER_ROLE: ["StakingRouter"], + VERIFIER_ROLE: ["CSVerifier"], }, }, CSAccounting: { address: LIDO_CONTRACTS.CSAccounting, roles: { DEFAULT_ADMIN_ROLE: ["Agent"], - ACCOUNTING_MANAGER_ROLE: ["Agent"], + ACCOUNTING_MANAGER_ROLE: [], MANAGE_BOND_CURVES_ROLE: [], - PAUSE_ROLE: [], + PAUSE_ROLE: ["CSGateSeal"], RECOVERER_ROLE: [], - RESET_BOND_CURVE_ROLE: [], + RESET_BOND_CURVE_ROLE: ["CSModule", "CSCommitteeMultisig"], RESUME_ROLE: [], - SET_BOND_CURVE_ROLE: [], + SET_BOND_CURVE_ROLE: ["CSModule", "CSCommitteeMultisig"], }, }, CSFeeDistributor: { @@ -300,7 +318,7 @@ export const OZ_CONTRACT_ROLES_CONFIG: OZContractRolesConfig = { CONTRACT_MANAGER_ROLE: [], MANAGE_CONSENSUS_CONTRACT_ROLE: [], MANAGE_CONSENSUS_VERSION_ROLE: [], - PAUSE_ROLE: [], + PAUSE_ROLE: ["CSGateSeal"], RECOVERER_ROLE: [], RESUME_ROLE: [], SUBMIT_DATA_ROLE: [], @@ -313,7 +331,7 @@ export const OZ_CONTRACT_ROLES_CONFIG: OZContractRolesConfig = { DISABLE_CONSENSUS_ROLE: [], MANAGE_FAST_LANE_CONFIG_ROLE: [], MANAGE_FRAME_CONFIG_ROLE: [], - MANAGE_MEMBERS_AND_QUORUM_ROLE: [], + MANAGE_MEMBERS_AND_QUORUM_ROLE: ["Agent"], MANAGE_REPORT_PROCESSOR_ROLE: [], }, }, @@ -322,9 +340,9 @@ export const OZ_CONTRACT_ROLES_CONFIG: OZContractRolesConfig = { address: LIDO_CONTRACTS.EasyTrack, roles: { DEFAULT_ADMIN_ROLE: ["Voting"], - CANCEL_ROLE: [], - PAUSE_ROLE: [], - UNPAUSE_ROLE: [], + CANCEL_ROLE: ["Voting"], + PAUSE_ROLE: ["Voting", "EmergencyBrakesMultisig"], + UNPAUSE_ROLE: ["Voting"], }, }, // Easy Track Factories for token transfers @@ -332,187 +350,199 @@ export const OZ_CONTRACT_ROLES_CONFIG: OZContractRolesConfig = { address: LIDO_CONTRACTS.LOLStETH_AllowedRecipientsRegistry, roles: { DEFAULT_ADMIN_ROLE: ["Voting"], - ADD_RECIPIENT_TO_ALLOWED_LIST_ROLE: ["Voting"], - REMOVE_RECIPIENT_FROM_ALLOWED_LIST_ROLE: [], - SET_PARAMETERS_ROLE: [], - UPDATE_SPENT_AMOUNT_ROLE: [], + ADD_RECIPIENT_TO_ALLOWED_LIST_ROLE: ["Voting", "EasyTrackEvmScriptExecutor"], + REMOVE_RECIPIENT_FROM_ALLOWED_LIST_ROLE: ["Voting", "EasyTrackEvmScriptExecutor"], + SET_PARAMETERS_ROLE: ["Voting"], + UPDATE_SPENT_AMOUNT_ROLE: ["Voting"], }, }, RewardsShareStETH_AllowedRecipientsRegistry: { address: LIDO_CONTRACTS.RewardsShareStETH_AllowedRecipientsRegistry, roles: { DEFAULT_ADMIN_ROLE: ["Voting"], - ADD_RECIPIENT_TO_ALLOWED_LIST_ROLE: [], - REMOVE_RECIPIENT_FROM_ALLOWED_LIST_ROLE: [], - SET_PARAMETERS_ROLE: [], - UPDATE_SPENT_AMOUNT_ROLE: [], + ADD_RECIPIENT_TO_ALLOWED_LIST_ROLE: [ + "Voting", + "EasyTrackEvmScriptExecutor", + ], + REMOVE_RECIPIENT_FROM_ALLOWED_LIST_ROLE: [ + "Voting", + "EasyTrackEvmScriptExecutor", + ], + SET_PARAMETERS_ROLE: ["Voting"], + UPDATE_SPENT_AMOUNT_ROLE: ["Voting", "EasyTrackEvmScriptExecutor"], }, }, LegoLDO_AllowedRecipientsRegistry: { address: LIDO_CONTRACTS.LegoLDO_AllowedRecipientsRegistry, roles: { DEFAULT_ADMIN_ROLE: ["Voting"], - ADD_RECIPIENT_TO_ALLOWED_LIST_ROLE: [], - REMOVE_RECIPIENT_FROM_ALLOWED_LIST_ROLE: [], - SET_PARAMETERS_ROLE: [], - UPDATE_SPENT_AMOUNT_ROLE: [], + ADD_RECIPIENT_TO_ALLOWED_LIST_ROLE: ["Voting"], + REMOVE_RECIPIENT_FROM_ALLOWED_LIST_ROLE: ["Voting"], + SET_PARAMETERS_ROLE: ["Voting"], + UPDATE_SPENT_AMOUNT_ROLE: ["Voting", "EasyTrackEvmScriptExecutor"], }, }, LegoStablecoins_AllowedRecipientsRegistry: { address: LIDO_CONTRACTS.LegoStablecoins_AllowedRecipientsRegistry, roles: { DEFAULT_ADMIN_ROLE: ["Voting"], - ADD_RECIPIENT_TO_ALLOWED_LIST_ROLE: [], - REMOVE_RECIPIENT_FROM_ALLOWED_LIST_ROLE: [], - SET_PARAMETERS_ROLE: [], - UPDATE_SPENT_AMOUNT_ROLE: [], + ADD_RECIPIENT_TO_ALLOWED_LIST_ROLE: ["Voting"], + REMOVE_RECIPIENT_FROM_ALLOWED_LIST_ROLE: ["Voting"], + SET_PARAMETERS_ROLE: ["Voting"], + UPDATE_SPENT_AMOUNT_ROLE: ["Voting", "EasyTrackEvmScriptExecutor"], }, }, RCCStableCoins_AllowedRecipientsRegistry: { address: LIDO_CONTRACTS.RCCStableCoins_AllowedRecipientsRegistry, roles: { DEFAULT_ADMIN_ROLE: ["Voting"], - ADD_RECIPIENT_TO_ALLOWED_LIST_ROLE: [], - REMOVE_RECIPIENT_FROM_ALLOWED_LIST_ROLE: [], - SET_PARAMETERS_ROLE: [], - UPDATE_SPENT_AMOUNT_ROLE: [], + ADD_RECIPIENT_TO_ALLOWED_LIST_ROLE: ["Voting"], + REMOVE_RECIPIENT_FROM_ALLOWED_LIST_ROLE: ["Voting"], + SET_PARAMETERS_ROLE: ["Voting"], + UPDATE_SPENT_AMOUNT_ROLE: ["Voting", "EasyTrackEvmScriptExecutor"], }, }, RCCStETH_AllowedRecipientsRegistry: { address: LIDO_CONTRACTS.RCCStETH_AllowedRecipientsRegistry, roles: { DEFAULT_ADMIN_ROLE: ["Voting"], - ADD_RECIPIENT_TO_ALLOWED_LIST_ROLE: [], - REMOVE_RECIPIENT_FROM_ALLOWED_LIST_ROLE: [], - SET_PARAMETERS_ROLE: [], - UPDATE_SPENT_AMOUNT_ROLE: [], + ADD_RECIPIENT_TO_ALLOWED_LIST_ROLE: ["Voting"], + REMOVE_RECIPIENT_FROM_ALLOWED_LIST_ROLE: ["Voting"], + SET_PARAMETERS_ROLE: ["Voting"], + UPDATE_SPENT_AMOUNT_ROLE: ["Voting", "EasyTrackEvmScriptExecutor"], }, }, PMLStablecoins_AllowedRecipientsRegistry: { address: LIDO_CONTRACTS.PMLStablecoins_AllowedRecipientsRegistry, roles: { DEFAULT_ADMIN_ROLE: ["Voting"], - ADD_RECIPIENT_TO_ALLOWED_LIST_ROLE: [], - REMOVE_RECIPIENT_FROM_ALLOWED_LIST_ROLE: [], - SET_PARAMETERS_ROLE: [], - UPDATE_SPENT_AMOUNT_ROLE: [], + ADD_RECIPIENT_TO_ALLOWED_LIST_ROLE: ["Voting"], + REMOVE_RECIPIENT_FROM_ALLOWED_LIST_ROLE: ["Voting"], + SET_PARAMETERS_ROLE: ["Voting"], + UPDATE_SPENT_AMOUNT_ROLE: ["Voting", "EasyTrackEvmScriptExecutor"], }, }, PMLStETH_AllowedRecipientsRegistry: { address: LIDO_CONTRACTS.PMLStETH_AllowedRecipientsRegistry, roles: { DEFAULT_ADMIN_ROLE: ["Voting"], - ADD_RECIPIENT_TO_ALLOWED_LIST_ROLE: [], - REMOVE_RECIPIENT_FROM_ALLOWED_LIST_ROLE: [], - SET_PARAMETERS_ROLE: [], - UPDATE_SPENT_AMOUNT_ROLE: [], + ADD_RECIPIENT_TO_ALLOWED_LIST_ROLE: ["Voting"], + REMOVE_RECIPIENT_FROM_ALLOWED_LIST_ROLE: ["Voting"], + SET_PARAMETERS_ROLE: ["Voting"], + UPDATE_SPENT_AMOUNT_ROLE: ["Voting", "EasyTrackEvmScriptExecutor"], }, }, ATCStablecoins_AllowedRecipientsRegistry: { address: LIDO_CONTRACTS.ATCStablecoins_AllowedRecipientsRegistry, roles: { DEFAULT_ADMIN_ROLE: ["Voting"], - ADD_RECIPIENT_TO_ALLOWED_LIST_ROLE: [], - REMOVE_RECIPIENT_FROM_ALLOWED_LIST_ROLE: [], - SET_PARAMETERS_ROLE: [], - UPDATE_SPENT_AMOUNT_ROLE: [], + ADD_RECIPIENT_TO_ALLOWED_LIST_ROLE: ["Voting"], + REMOVE_RECIPIENT_FROM_ALLOWED_LIST_ROLE: ["Voting"], + SET_PARAMETERS_ROLE: ["Voting"], + UPDATE_SPENT_AMOUNT_ROLE: ["Voting", "EasyTrackEvmScriptExecutor"], }, }, ATCStETH_AllowedRecipientsRegistry: { address: LIDO_CONTRACTS.ATCStETH_AllowedRecipientsRegistry, roles: { DEFAULT_ADMIN_ROLE: ["Voting"], - ADD_RECIPIENT_TO_ALLOWED_LIST_ROLE: [], - REMOVE_RECIPIENT_FROM_ALLOWED_LIST_ROLE: [], - SET_PARAMETERS_ROLE: [], - UPDATE_SPENT_AMOUNT_ROLE: [], + ADD_RECIPIENT_TO_ALLOWED_LIST_ROLE: ["Voting"], + REMOVE_RECIPIENT_FROM_ALLOWED_LIST_ROLE: ["Voting"], + SET_PARAMETERS_ROLE: ["Voting"], + UPDATE_SPENT_AMOUNT_ROLE: ["Voting", "EasyTrackEvmScriptExecutor"], }, }, TRPLDO_AllowedRecipientsRegistry: { address: LIDO_CONTRACTS.TRPLDO_AllowedRecipientsRegistry, roles: { DEFAULT_ADMIN_ROLE: ["Voting"], - ADD_RECIPIENT_TO_ALLOWED_LIST_ROLE: [], - REMOVE_RECIPIENT_FROM_ALLOWED_LIST_ROLE: [], - SET_PARAMETERS_ROLE: [], - UPDATE_SPENT_AMOUNT_ROLE: [], + ADD_RECIPIENT_TO_ALLOWED_LIST_ROLE: ["Voting"], + REMOVE_RECIPIENT_FROM_ALLOWED_LIST_ROLE: ["Voting"], + SET_PARAMETERS_ROLE: ["Voting"], + UPDATE_SPENT_AMOUNT_ROLE: ["Voting", "EasyTrackEvmScriptExecutor"], }, }, GasSupplyStETH_AllowedRecipientsRegistry: { address: LIDO_CONTRACTS.GasSupplyStETH_AllowedRecipientsRegistry, roles: { DEFAULT_ADMIN_ROLE: ["Voting"], - ADD_RECIPIENT_TO_ALLOWED_LIST_ROLE: [], - REMOVE_RECIPIENT_FROM_ALLOWED_LIST_ROLE: [], - SET_PARAMETERS_ROLE: [], - UPDATE_SPENT_AMOUNT_ROLE: [], + ADD_RECIPIENT_TO_ALLOWED_LIST_ROLE: [ + "Voting", + "EasyTrackEvmScriptExecutor", + ], + REMOVE_RECIPIENT_FROM_ALLOWED_LIST_ROLE: [ + "Voting", + "EasyTrackEvmScriptExecutor", + ], + SET_PARAMETERS_ROLE: ["Voting"], + UPDATE_SPENT_AMOUNT_ROLE: ["Voting", "EasyTrackEvmScriptExecutor"], }, }, AllianceOpsStablecoins_AllowedRecipientsRegistry: { address: LIDO_CONTRACTS.AllianceOpsStablecoins_AllowedRecipientsRegistry, roles: { DEFAULT_ADMIN_ROLE: ["Voting"], - ADD_RECIPIENT_TO_ALLOWED_LIST_ROLE: [], - REMOVE_RECIPIENT_FROM_ALLOWED_LIST_ROLE: [], - SET_PARAMETERS_ROLE: [], - UPDATE_SPENT_AMOUNT_ROLE: [], + ADD_RECIPIENT_TO_ALLOWED_LIST_ROLE: ["Voting"], + REMOVE_RECIPIENT_FROM_ALLOWED_LIST_ROLE: ["Voting"], + SET_PARAMETERS_ROLE: ["Voting"], + UPDATE_SPENT_AMOUNT_ROLE: ["Voting", "EasyTrackEvmScriptExecutor"], }, }, StonksStETH_AllowedRecipientsRegistry: { address: LIDO_CONTRACTS.StonksStETH_AllowedRecipientsRegistry, roles: { DEFAULT_ADMIN_ROLE: ["Voting"], - ADD_RECIPIENT_TO_ALLOWED_LIST_ROLE: [], - REMOVE_RECIPIENT_FROM_ALLOWED_LIST_ROLE: [], - SET_PARAMETERS_ROLE: [], - UPDATE_SPENT_AMOUNT_ROLE: [], + ADD_RECIPIENT_TO_ALLOWED_LIST_ROLE: ["Voting"], + REMOVE_RECIPIENT_FROM_ALLOWED_LIST_ROLE: ["Voting"], + SET_PARAMETERS_ROLE: ["Voting"], + UPDATE_SPENT_AMOUNT_ROLE: ["Voting", "EasyTrackEvmScriptExecutor"], }, }, StonksStablecoins_AllowedRecipientsRegistry: { address: LIDO_CONTRACTS.StonksStablecoins_AllowedRecipientsRegistry, roles: { DEFAULT_ADMIN_ROLE: ["Voting"], - ADD_RECIPIENT_TO_ALLOWED_LIST_ROLE: [], - REMOVE_RECIPIENT_FROM_ALLOWED_LIST_ROLE: [], - SET_PARAMETERS_ROLE: [], - UPDATE_SPENT_AMOUNT_ROLE: [], + ADD_RECIPIENT_TO_ALLOWED_LIST_ROLE: ["Voting"], + REMOVE_RECIPIENT_FROM_ALLOWED_LIST_ROLE: ["Voting"], + SET_PARAMETERS_ROLE: ["Voting"], + UPDATE_SPENT_AMOUNT_ROLE: ["Voting", "EasyTrackEvmScriptExecutor"], }, }, AllowedTokensRegistry: { address: LIDO_CONTRACTS.AllowedTokensRegistry, roles: { DEFAULT_ADMIN_ROLE: ["Voting"], - ADD_TOKEN_TO_ALLOWED_LIST_ROLE: [], - REMOVE_TOKEN_FROM_ALLOWED_LIST_ROLE: [], + ADD_TOKEN_TO_ALLOWED_LIST_ROLE: ["Voting"], + REMOVE_TOKEN_FROM_ALLOWED_LIST_ROLE: ["Voting"], }, }, // Arbitrum L1ERC20TokenGateway_Arbitrum: { address: LIDO_CONTRACTS.L1ERC20TokenGateway_Arbitrum, roles: { - DEFAULT_ADMIN_ROLE: ["DualGovernance"], - DEPOSITS_DISABLER_ROLE: [], - DEPOSITS_ENABLER_ROLE: [], - WITHDRAWALS_DISABLER_ROLE: [], - WITHDRAWALS_ENABLER_ROLE: [], + DEFAULT_ADMIN_ROLE: ["Agent"], + DEPOSITS_DISABLER_ROLE: ["Agent", "EmergencyBrakesMultisig"], + DEPOSITS_ENABLER_ROLE: ["Agent"], + WITHDRAWALS_DISABLER_ROLE: ["Agent", "EmergencyBrakesMultisig"], + WITHDRAWALS_ENABLER_ROLE: ["Agent"], }, }, // Optimism L1TokensBridge_Optimism: { address: LIDO_CONTRACTS.L1TokensBridge_Optimism, roles: { - DEFAULT_ADMIN_ROLE: ["DualGovernance"], - DEPOSITS_DISABLER_ROLE: [], - DEPOSITS_ENABLER_ROLE: [], - WITHDRAWALS_DISABLER_ROLE: [], - WITHDRAWALS_ENABLER_ROLE: [], + DEFAULT_ADMIN_ROLE: ["Agent"], + DEPOSITS_DISABLER_ROLE: ["Agent", "EmergencyBrakesMultisig"], + DEPOSITS_ENABLER_ROLE: ["Agent"], + WITHDRAWALS_DISABLER_ROLE: ["Agent", "EmergencyBrakesMultisig"], + WITHDRAWALS_ENABLER_ROLE: ["Agent"], }, }, // Polygon ERC20Predicate_Polygon: { address: LIDO_CONTRACTS.ERC20Predicate_Polygon, roles: { - DEFAULT_ADMIN_ROLE: ["DualGovernance"], + DEFAULT_ADMIN_ROLE: [], MANAGER_ROLE: [], }, }, @@ -520,57 +550,54 @@ export const OZ_CONTRACT_ROLES_CONFIG: OZContractRolesConfig = { L1ERC20TokenBridge_Base: { address: LIDO_CONTRACTS.L1ERC20TokenBridge_Base, roles: { - DEFAULT_ADMIN_ROLE: ["DualGovernance"], - DEPOSITS_DISABLER_ROLE: [], - DEPOSITS_ENABLER_ROLE: [], - WITHDRAWALS_DISABLER_ROLE: [], - WITHDRAWALS_ENABLER_ROLE: [], + DEFAULT_ADMIN_ROLE: ["Agent"], + DEPOSITS_DISABLER_ROLE: ["Agent", "EmergencyBrakesMultisig"], + DEPOSITS_ENABLER_ROLE: ["Agent"], + WITHDRAWALS_DISABLER_ROLE: ["Agent", "EmergencyBrakesMultisig"], + WITHDRAWALS_ENABLER_ROLE: ["Agent"], }, }, L1ERC20Bridge_zkSync: { address: LIDO_CONTRACTS.L1ERC20Bridge_zkSync, roles: { - DEFAULT_ADMIN_ROLE: ["DualGovernance"], - DEPOSITS_DISABLER_ROLE: [], - DEPOSITS_ENABLER_ROLE: [], - WITHDRAWALS_DISABLER_ROLE: [], - WITHDRAWALS_ENABLER_ROLE: [], + DEFAULT_ADMIN_ROLE: ["Agent"], + DEPOSITS_DISABLER_ROLE: ["Agent", "EmergencyBrakesMultisig"], + DEPOSITS_ENABLER_ROLE: ["Agent"], + WITHDRAWALS_DISABLER_ROLE: ["Agent", "EmergencyBrakesMultisig"], + WITHDRAWALS_ENABLER_ROLE: ["Agent"], }, }, // Mantle L1ERC20TokenBridge_Mantle: { address: LIDO_CONTRACTS.L1ERC20TokenBridge_Mantle, roles: { - DEFAULT_ADMIN_ROLE: ["DualGovernance"], - DEPOSITS_DISABLER_ROLE: [], - DEPOSITS_ENABLER_ROLE: [], - WITHDRAWALS_DISABLER_ROLE: [], - WITHDRAWALS_ENABLER_ROLE: [], + DEFAULT_ADMIN_ROLE: ["Agent"], + DEPOSITS_DISABLER_ROLE: ["Agent", "EmergencyBrakesMultisig"], + DEPOSITS_ENABLER_ROLE: ["Agent"], + WITHDRAWALS_DISABLER_ROLE: ["Agent", "EmergencyBrakesMultisig"], + WITHDRAWALS_ENABLER_ROLE: ["Agent"], }, }, - // Scroll - // Note: Scroll uses modified version of AccessControl from OZ, which allows grant roles only to - // standalone owner contract - // L1LidoGateway_Scroll: { - // address: LIDO_CONTRACTS.L1LidoGateway_Scroll, - // roles: { - // DEPOSITS_DISABLER_ROLE: [], - // DEPOSITS_ENABLER_ROLE: [], - // WITHDRAWALS_DISABLER_ROLE: [], - // WITHDRAWALS_ENABLER_ROLE: [], - // }, - // }, + L1LidoGateway_Scroll: { + address: LIDO_CONTRACTS.L1LidoGateway_Scroll, + roles: { + DEPOSITS_DISABLER_ROLE: ["Agent", "EmergencyBrakesMultisig"], + DEPOSITS_ENABLER_ROLE: ["Agent"], + WITHDRAWALS_DISABLER_ROLE: ["Agent", "EmergencyBrakesMultisig"], + WITHDRAWALS_ENABLER_ROLE: ["Agent"], + }, + }, // Mode L1ERC20TokenBridge_Mode: { address: LIDO_CONTRACTS.L1ERC20TokenBridge_Mode, roles: { - DEFAULT_ADMIN_ROLE: ["DualGovernance"], - DEPOSITS_DISABLER_ROLE: [], - DEPOSITS_ENABLER_ROLE: [], - WITHDRAWALS_DISABLER_ROLE: [], - WITHDRAWALS_ENABLER_ROLE: [], + DEFAULT_ADMIN_ROLE: ["Agent"], + DEPOSITS_DISABLER_ROLE: ["Agent", "EmergencyBrakesMultisig"], + DEPOSITS_ENABLER_ROLE: ["Agent"], + WITHDRAWALS_DISABLER_ROLE: ["Agent", "EmergencyBrakesMultisig"], + WITHDRAWALS_ENABLER_ROLE: ["Agent"], }, }, @@ -578,11 +605,11 @@ export const OZ_CONTRACT_ROLES_CONFIG: OZContractRolesConfig = { L1ERC20TokenBridge_Zircuit: { address: LIDO_CONTRACTS.L1ERC20TokenBridge_Zircuit, roles: { - DEFAULT_ADMIN_ROLE: ["DualGovernance"], - DEPOSITS_DISABLER_ROLE: [], - DEPOSITS_ENABLER_ROLE: [], - WITHDRAWALS_DISABLER_ROLE: [], - WITHDRAWALS_ENABLER_ROLE: [], + DEFAULT_ADMIN_ROLE: ["Agent"], + DEPOSITS_DISABLER_ROLE: ["Agent", "EmergencyBrakesMultisig"], + DEPOSITS_ENABLER_ROLE: ["Agent"], + WITHDRAWALS_DISABLER_ROLE: ["Agent", "EmergencyBrakesMultisig"], + WITHDRAWALS_ENABLER_ROLE: ["Agent"], }, }, } as const; @@ -618,9 +645,21 @@ export const MANAGED_CONTRACTS: ManagedContractsConfig = { address: "0x0De4Ea0184c2ad0BacA7183356Aea5B8d5Bf5c6e", properties: { admin: { property: "proxy__getAdmin", managedBy: "Agent" } }, }, + ScrollL1LidoGateway: { + address: "0x6625c6332c9f91f2d27c304e729b86db87a3f504", + properties: { owner: { property: "owner", managedBy: "Agent" } }, + }, + ScrollProxyAdmin: { + address: '0xCC2C53556Bc75217cf698721b29071d6f12628A9', + properties: { owner: { property: "owner", managedBy: "Agent" } }, + }, InsuranceFund: { address: "0x8B3f33234ABD88493c0Cd28De33D583B70beDe35", properties: { owner: { property: "owner", managedBy: "Voting" } }, // ?? }, + ZKSync_L1Executor: { + address: LIDO_CONTRACTS.L1Executor_zkSync, + properties: { owner: { property: "owner", managedBy: "Agent" } }, + } // TODO: Add missing contracts }; diff --git a/scripts/permissions-migration/config/lido-contracts.ts b/scripts/permissions-migration/config/lido-contracts.ts index 5e74e1a9..caaabb9d 100644 --- a/scripts/permissions-migration/config/lido-contracts.ts +++ b/scripts/permissions-migration/config/lido-contracts.ts @@ -43,6 +43,7 @@ export const LIDO_CONTRACTS = { CuratedModuleRepo: "0x0D97E876ad14DB2b183CFeEB8aa1A5C788eB1831", SimpleDVTRepo: "0x2325b0a607808dE42D918DB07F925FFcCfBb2968", OraclesGateSeal: "0x79243345edbe01a7e42edff5900156700d22611c", + EVMScriptRegistry: "0x853cc0D5917f49B57B8e9F89e491F5E18919093A", // Staking Modules CuratedModule: "0x55032650b14df07b85bF18A3a3eC8E0Af2e028d5", diff --git a/scripts/permissions-migration/index.ts b/scripts/permissions-migration/index.ts index ff1bb98b..83a92b16 100644 --- a/scripts/permissions-migration/index.ts +++ b/scripts/permissions-migration/index.ts @@ -4,6 +4,7 @@ import { JsonRpcProvider } from "ethers"; import oz from "./src/oz-roles"; import aragon from "./src/aragon-permissions"; import managed from "./src/managed-contracts"; +import { retrieveDeployConfiguration } from "./src/aragon-deploy"; import { ARAGON_CONTRACT_ROLES_CONFIG, @@ -38,6 +39,7 @@ async function main() { await managed.collectManagedContractsInfo(provider, MANAGED_CONTRACTS) ) ); + console.log(await retrieveDeployConfiguration(provider)); } main().catch((error) => { diff --git a/scripts/permissions-migration/src/aragon-deploy.ts b/scripts/permissions-migration/src/aragon-deploy.ts new file mode 100644 index 00000000..7ea232c8 --- /dev/null +++ b/scripts/permissions-migration/src/aragon-deploy.ts @@ -0,0 +1,47 @@ +import { ethers, id, JsonRpcProvider, getAddress } from "ethers"; +import { LIDO_CONTRACTS } from "../config/lido-contracts"; + +import md from "./markdown"; + +const txHash = + "0x3feabd79e8549ad68d1827c074fa7123815c80206498946293d5373a160fd866"; + +export async function retrieveDeployConfiguration(provider: JsonRpcProvider) { + const txReceipt = await provider.getTransactionReceipt(txHash); + const iface = new ethers.Interface([ + "event SetApp(bytes32 indexed namespace, bytes32 indexed appId, address app)", + ]); + + const deployedApps: Set = new Set(); + + if (!txReceipt) return; + + for (const log of txReceipt.logs) { + const mutableLog = { ...log, topics: [...log.topics] }; + if (mutableLog.topics[0] === id("SetApp(bytes32,bytes32,address)")) { + const decoded = iface.parseLog(mutableLog); + + if (decoded) { + deployedApps.add(decoded.args[2]); + } + } + } + + for (const lidoContract of Object.entries(LIDO_CONTRACTS)) { + const [name, address] = lidoContract; + const normalizedAddress = getAddress(address); + + if (deployedApps.has(normalizedAddress)) { + deployedApps.delete(normalizedAddress); + deployedApps.add(name); + } + } + + const result = [md.header(["Deployed apps"])]; + + for (const app of deployedApps) { + result.push(md.row([app])); + } + + return result.join("\n"); +} From 357ea7783fd2a4e36f67f54175e6483a0c32a8a3 Mon Sep 17 00:00:00 2001 From: Roman Kolpakov Date: Tue, 21 Jan 2025 16:53:18 +0300 Subject: [PATCH 3/4] fix: remove payment factories --- .../agent-transfer-permissions-config.ts | 171 +----------------- 1 file changed, 3 insertions(+), 168 deletions(-) diff --git a/scripts/permissions-migration/config/agent-transfer-permissions-config.ts b/scripts/permissions-migration/config/agent-transfer-permissions-config.ts index e43c86bb..dc3e1426 100644 --- a/scripts/permissions-migration/config/agent-transfer-permissions-config.ts +++ b/scripts/permissions-migration/config/agent-transfer-permissions-config.ts @@ -21,9 +21,7 @@ export const ARAGON_CONTRACT_ROLES_CONFIG: AragonContractPermissionConfigs = { // Oracle Contracts LegacyOracle: { address: LIDO_CONTRACTS.LegacyOracle, - permissions: { - // Has no declared permissions - }, + permissions: {}, }, // DAO Contracts @@ -345,169 +343,6 @@ export const OZ_CONTRACT_ROLES_CONFIG: OZContractRolesConfig = { UNPAUSE_ROLE: ["Voting"], }, }, - // Easy Track Factories for token transfers - LOLStETH_AllowedRecipientsRegistry: { - address: LIDO_CONTRACTS.LOLStETH_AllowedRecipientsRegistry, - roles: { - DEFAULT_ADMIN_ROLE: ["Voting"], - ADD_RECIPIENT_TO_ALLOWED_LIST_ROLE: ["Voting", "EasyTrackEvmScriptExecutor"], - REMOVE_RECIPIENT_FROM_ALLOWED_LIST_ROLE: ["Voting", "EasyTrackEvmScriptExecutor"], - SET_PARAMETERS_ROLE: ["Voting"], - UPDATE_SPENT_AMOUNT_ROLE: ["Voting"], - }, - }, - RewardsShareStETH_AllowedRecipientsRegistry: { - address: LIDO_CONTRACTS.RewardsShareStETH_AllowedRecipientsRegistry, - roles: { - DEFAULT_ADMIN_ROLE: ["Voting"], - ADD_RECIPIENT_TO_ALLOWED_LIST_ROLE: [ - "Voting", - "EasyTrackEvmScriptExecutor", - ], - REMOVE_RECIPIENT_FROM_ALLOWED_LIST_ROLE: [ - "Voting", - "EasyTrackEvmScriptExecutor", - ], - SET_PARAMETERS_ROLE: ["Voting"], - UPDATE_SPENT_AMOUNT_ROLE: ["Voting", "EasyTrackEvmScriptExecutor"], - }, - }, - LegoLDO_AllowedRecipientsRegistry: { - address: LIDO_CONTRACTS.LegoLDO_AllowedRecipientsRegistry, - roles: { - DEFAULT_ADMIN_ROLE: ["Voting"], - ADD_RECIPIENT_TO_ALLOWED_LIST_ROLE: ["Voting"], - REMOVE_RECIPIENT_FROM_ALLOWED_LIST_ROLE: ["Voting"], - SET_PARAMETERS_ROLE: ["Voting"], - UPDATE_SPENT_AMOUNT_ROLE: ["Voting", "EasyTrackEvmScriptExecutor"], - }, - }, - LegoStablecoins_AllowedRecipientsRegistry: { - address: LIDO_CONTRACTS.LegoStablecoins_AllowedRecipientsRegistry, - roles: { - DEFAULT_ADMIN_ROLE: ["Voting"], - ADD_RECIPIENT_TO_ALLOWED_LIST_ROLE: ["Voting"], - REMOVE_RECIPIENT_FROM_ALLOWED_LIST_ROLE: ["Voting"], - SET_PARAMETERS_ROLE: ["Voting"], - UPDATE_SPENT_AMOUNT_ROLE: ["Voting", "EasyTrackEvmScriptExecutor"], - }, - }, - RCCStableCoins_AllowedRecipientsRegistry: { - address: LIDO_CONTRACTS.RCCStableCoins_AllowedRecipientsRegistry, - roles: { - DEFAULT_ADMIN_ROLE: ["Voting"], - ADD_RECIPIENT_TO_ALLOWED_LIST_ROLE: ["Voting"], - REMOVE_RECIPIENT_FROM_ALLOWED_LIST_ROLE: ["Voting"], - SET_PARAMETERS_ROLE: ["Voting"], - UPDATE_SPENT_AMOUNT_ROLE: ["Voting", "EasyTrackEvmScriptExecutor"], - }, - }, - RCCStETH_AllowedRecipientsRegistry: { - address: LIDO_CONTRACTS.RCCStETH_AllowedRecipientsRegistry, - roles: { - DEFAULT_ADMIN_ROLE: ["Voting"], - ADD_RECIPIENT_TO_ALLOWED_LIST_ROLE: ["Voting"], - REMOVE_RECIPIENT_FROM_ALLOWED_LIST_ROLE: ["Voting"], - SET_PARAMETERS_ROLE: ["Voting"], - UPDATE_SPENT_AMOUNT_ROLE: ["Voting", "EasyTrackEvmScriptExecutor"], - }, - }, - PMLStablecoins_AllowedRecipientsRegistry: { - address: LIDO_CONTRACTS.PMLStablecoins_AllowedRecipientsRegistry, - roles: { - DEFAULT_ADMIN_ROLE: ["Voting"], - ADD_RECIPIENT_TO_ALLOWED_LIST_ROLE: ["Voting"], - REMOVE_RECIPIENT_FROM_ALLOWED_LIST_ROLE: ["Voting"], - SET_PARAMETERS_ROLE: ["Voting"], - UPDATE_SPENT_AMOUNT_ROLE: ["Voting", "EasyTrackEvmScriptExecutor"], - }, - }, - PMLStETH_AllowedRecipientsRegistry: { - address: LIDO_CONTRACTS.PMLStETH_AllowedRecipientsRegistry, - roles: { - DEFAULT_ADMIN_ROLE: ["Voting"], - ADD_RECIPIENT_TO_ALLOWED_LIST_ROLE: ["Voting"], - REMOVE_RECIPIENT_FROM_ALLOWED_LIST_ROLE: ["Voting"], - SET_PARAMETERS_ROLE: ["Voting"], - UPDATE_SPENT_AMOUNT_ROLE: ["Voting", "EasyTrackEvmScriptExecutor"], - }, - }, - ATCStablecoins_AllowedRecipientsRegistry: { - address: LIDO_CONTRACTS.ATCStablecoins_AllowedRecipientsRegistry, - roles: { - DEFAULT_ADMIN_ROLE: ["Voting"], - ADD_RECIPIENT_TO_ALLOWED_LIST_ROLE: ["Voting"], - REMOVE_RECIPIENT_FROM_ALLOWED_LIST_ROLE: ["Voting"], - SET_PARAMETERS_ROLE: ["Voting"], - UPDATE_SPENT_AMOUNT_ROLE: ["Voting", "EasyTrackEvmScriptExecutor"], - }, - }, - ATCStETH_AllowedRecipientsRegistry: { - address: LIDO_CONTRACTS.ATCStETH_AllowedRecipientsRegistry, - roles: { - DEFAULT_ADMIN_ROLE: ["Voting"], - ADD_RECIPIENT_TO_ALLOWED_LIST_ROLE: ["Voting"], - REMOVE_RECIPIENT_FROM_ALLOWED_LIST_ROLE: ["Voting"], - SET_PARAMETERS_ROLE: ["Voting"], - UPDATE_SPENT_AMOUNT_ROLE: ["Voting", "EasyTrackEvmScriptExecutor"], - }, - }, - TRPLDO_AllowedRecipientsRegistry: { - address: LIDO_CONTRACTS.TRPLDO_AllowedRecipientsRegistry, - roles: { - DEFAULT_ADMIN_ROLE: ["Voting"], - ADD_RECIPIENT_TO_ALLOWED_LIST_ROLE: ["Voting"], - REMOVE_RECIPIENT_FROM_ALLOWED_LIST_ROLE: ["Voting"], - SET_PARAMETERS_ROLE: ["Voting"], - UPDATE_SPENT_AMOUNT_ROLE: ["Voting", "EasyTrackEvmScriptExecutor"], - }, - }, - GasSupplyStETH_AllowedRecipientsRegistry: { - address: LIDO_CONTRACTS.GasSupplyStETH_AllowedRecipientsRegistry, - roles: { - DEFAULT_ADMIN_ROLE: ["Voting"], - ADD_RECIPIENT_TO_ALLOWED_LIST_ROLE: [ - "Voting", - "EasyTrackEvmScriptExecutor", - ], - REMOVE_RECIPIENT_FROM_ALLOWED_LIST_ROLE: [ - "Voting", - "EasyTrackEvmScriptExecutor", - ], - SET_PARAMETERS_ROLE: ["Voting"], - UPDATE_SPENT_AMOUNT_ROLE: ["Voting", "EasyTrackEvmScriptExecutor"], - }, - }, - AllianceOpsStablecoins_AllowedRecipientsRegistry: { - address: LIDO_CONTRACTS.AllianceOpsStablecoins_AllowedRecipientsRegistry, - roles: { - DEFAULT_ADMIN_ROLE: ["Voting"], - ADD_RECIPIENT_TO_ALLOWED_LIST_ROLE: ["Voting"], - REMOVE_RECIPIENT_FROM_ALLOWED_LIST_ROLE: ["Voting"], - SET_PARAMETERS_ROLE: ["Voting"], - UPDATE_SPENT_AMOUNT_ROLE: ["Voting", "EasyTrackEvmScriptExecutor"], - }, - }, - StonksStETH_AllowedRecipientsRegistry: { - address: LIDO_CONTRACTS.StonksStETH_AllowedRecipientsRegistry, - roles: { - DEFAULT_ADMIN_ROLE: ["Voting"], - ADD_RECIPIENT_TO_ALLOWED_LIST_ROLE: ["Voting"], - REMOVE_RECIPIENT_FROM_ALLOWED_LIST_ROLE: ["Voting"], - SET_PARAMETERS_ROLE: ["Voting"], - UPDATE_SPENT_AMOUNT_ROLE: ["Voting", "EasyTrackEvmScriptExecutor"], - }, - }, - StonksStablecoins_AllowedRecipientsRegistry: { - address: LIDO_CONTRACTS.StonksStablecoins_AllowedRecipientsRegistry, - roles: { - DEFAULT_ADMIN_ROLE: ["Voting"], - ADD_RECIPIENT_TO_ALLOWED_LIST_ROLE: ["Voting"], - REMOVE_RECIPIENT_FROM_ALLOWED_LIST_ROLE: ["Voting"], - SET_PARAMETERS_ROLE: ["Voting"], - UPDATE_SPENT_AMOUNT_ROLE: ["Voting", "EasyTrackEvmScriptExecutor"], - }, - }, AllowedTokensRegistry: { address: LIDO_CONTRACTS.AllowedTokensRegistry, roles: { @@ -650,7 +485,7 @@ export const MANAGED_CONTRACTS: ManagedContractsConfig = { properties: { owner: { property: "owner", managedBy: "Agent" } }, }, ScrollProxyAdmin: { - address: '0xCC2C53556Bc75217cf698721b29071d6f12628A9', + address: "0xCC2C53556Bc75217cf698721b29071d6f12628A9", properties: { owner: { property: "owner", managedBy: "Agent" } }, }, InsuranceFund: { @@ -660,6 +495,6 @@ export const MANAGED_CONTRACTS: ManagedContractsConfig = { ZKSync_L1Executor: { address: LIDO_CONTRACTS.L1Executor_zkSync, properties: { owner: { property: "owner", managedBy: "Agent" } }, - } + }, // TODO: Add missing contracts }; From f85b38622220040ec0fffeaf4c5cd09868feb1d4 Mon Sep 17 00:00:00 2001 From: Roman Kolpakov Date: Tue, 21 Jan 2025 19:01:12 +0300 Subject: [PATCH 4/4] wip: to be recommited --- .../aragon-permissions.json | 8 + .../agent-transfer-permissions-config.ts | 300 +++++++++--------- .../config/lido-contracts.ts | 36 +-- .../contracts/AragonRolesVerifier.sol | 34 ++ .../deploy/deploy-aragon-verifier.ts | 63 ++++ scripts/permissions-migration/index.ts | 43 +-- .../src/aragon-generate-json.ts | 45 +++ .../src/aragon-permissions.ts | 46 +-- .../permissions-migration/src/comparison.ts | 1 + 9 files changed, 352 insertions(+), 224 deletions(-) create mode 100644 scripts/permissions-migration/aragon-permissions.json create mode 100644 scripts/permissions-migration/contracts/AragonRolesVerifier.sol create mode 100644 scripts/permissions-migration/deploy/deploy-aragon-verifier.ts create mode 100644 scripts/permissions-migration/src/aragon-generate-json.ts create mode 100644 scripts/permissions-migration/src/comparison.ts diff --git a/scripts/permissions-migration/aragon-permissions.json b/scripts/permissions-migration/aragon-permissions.json new file mode 100644 index 00000000..db90fae2 --- /dev/null +++ b/scripts/permissions-migration/aragon-permissions.json @@ -0,0 +1,8 @@ +[ + { + "where": "0x3F1c547b21f65e10480dE3ad8E19fAAC46C95034", + "what": "0xa42eee1333c0758ba72be38e728b6dadb32ea767de5b4ddbaea1dae85b1b051f", + "who": "0xE92329EC7ddB11D25e25b3c21eeBf11f15eB325d", + "granted": true + } +] diff --git a/scripts/permissions-migration/config/agent-transfer-permissions-config.ts b/scripts/permissions-migration/config/agent-transfer-permissions-config.ts index dc3e1426..cc9ca5d1 100644 --- a/scripts/permissions-migration/config/agent-transfer-permissions-config.ts +++ b/scripts/permissions-migration/config/agent-transfer-permissions-config.ts @@ -10,161 +10,161 @@ export const ARAGON_CONTRACT_ROLES_CONFIG: AragonContractPermissionConfigs = { Lido: { address: LIDO_CONTRACTS.Lido, permissions: { - STAKING_CONTROL_ROLE: { manager: "None" }, - RESUME_ROLE: { manager: "None" }, - PAUSE_ROLE: { manager: "None" }, - UNSAFE_CHANGE_DEPOSITED_VALIDATORS_ROLE: { manager: "None" }, - STAKING_PAUSE_ROLE: { manager: "None" }, + STAKING_CONTROL_ROLE: { manager: "None", grantedTo: ["Agent"] }, + // RESUME_ROLE: { manager: "None" }, + // PAUSE_ROLE: { manager: "None" }, + // UNSAFE_CHANGE_DEPOSITED_VALIDATORS_ROLE: { manager: "None" }, + // STAKING_PAUSE_ROLE: { manager: "None" }, }, }, // Oracle Contracts - LegacyOracle: { - address: LIDO_CONTRACTS.LegacyOracle, - permissions: {}, - }, + // LegacyOracle: { + // address: LIDO_CONTRACTS.LegacyOracle, + // permissions: {}, + // }, - // DAO Contracts - DAOKernel: { - address: LIDO_CONTRACTS.DAOKernel, - permissions: { - APP_MANAGER_ROLE: { manager: "Agent" }, - }, - }, - Voting: { - address: LIDO_CONTRACTS.Voting, - permissions: { - UNSAFELY_MODIFY_VOTE_TIME_ROLE: { manager: "Voting" }, - MODIFY_QUORUM_ROLE: { manager: "Voting", grantedTo: ["Voting"] }, - MODIFY_SUPPORT_ROLE: { manager: "Voting", grantedTo: ["Voting"] }, - CREATE_VOTES_ROLE: { manager: "Voting", grantedTo: ["TokenManager"] }, - }, - }, - TokenManager: { - address: LIDO_CONTRACTS.TokenManager, - permissions: { - ISSUE_ROLE: { manager: "Voting" }, - ASSIGN_ROLE: { manager: "Voting", grantedTo: ["Voting"] }, - BURN_ROLE: { manager: "Voting" }, - MINT_ROLE: { manager: "Voting" }, - REVOKE_VESTINGS_ROLE: { manager: "Voting" }, - }, - }, - Finance: { - address: LIDO_CONTRACTS.Finance, - permissions: { - CREATE_PAYMENTS_ROLE: { - manager: "Voting", - grantedTo: ["Voting", "EasyTrackEvmScriptExecutor"], - }, - CHANGE_PERIOD_ROLE: { manager: "Voting" }, - CHANGE_BUDGETS_ROLE: { manager: "Voting" }, - EXECUTE_PAYMENTS_ROLE: { manager: "Voting", grantedTo: ["Voting"] }, - MANAGE_PAYMENTS_ROLE: { manager: "Voting", grantedTo: ["Voting"] }, - }, - }, - Agent: { - address: LIDO_CONTRACTS.Agent, - permissions: { - ADD_PROTECTED_TOKEN_ROLE: { manager: "Voting" }, - REMOVE_PROTECTED_TOKEN_ROLE: { manager: "Voting" }, - TRANSFER_ROLE: { manager: "Voting", grantedTo: ["Finance"] }, - RUN_SCRIPT_ROLE: { - manager: "DualGovernance", - grantedTo: ["DualGovernance"], - }, - SAFE_EXECUTE_ROLE: { manager: "None" }, - DESIGNATE_SIGNER_ROLE: { manager: "Voting" }, - EXECUTE_ROLE: { - manager: "DualGovernance", - grantedTo: ["DualGovernance"], - }, - ADD_PRESIGNED_HASH_ROLE: { manager: "Voting" }, - }, - }, - ACL: { - address: LIDO_CONTRACTS.ACL, - permissions: { CREATE_PERMISSIONS_ROLE: { manager: "Agent" } }, - }, - AragonPM: { - address: LIDO_CONTRACTS.AragonPM, - permissions: { - CREATE_REPO_ROLE: { manager: "None" }, - }, - }, - EVMScriptRegistry: { - address: LIDO_CONTRACTS.EVMScriptRegistry, - permissions: { - REGISTRY_ADD_EXECUTOR_ROLE: { manager: "None" }, - REGISTRY_MANAGER_ROLE: { manager: "None" }, - }, - }, - VotingRepo: { - address: LIDO_CONTRACTS.VotingRepo, - permissions: { - CREATE_VERSION_ROLE: { manager: "None" }, - }, - }, - LidoRepo: { - address: LIDO_CONTRACTS.LidoRepo, - permissions: { - CREATE_VERSION_ROLE: { manager: "None" }, - }, - }, - LegacyOracleRepo: { - address: LIDO_CONTRACTS.LegacyOracleRepo, - permissions: { - CREATE_VERSION_ROLE: { manager: "None" }, - }, - }, - CuratedModuleRepo: { - address: LIDO_CONTRACTS.CuratedModuleRepo, - permissions: { - CREATE_VERSION_ROLE: { manager: "None" }, - }, - }, - SimpleDVTRepo: { - address: LIDO_CONTRACTS.SimpleDVTRepo, - permissions: { - CREATE_VERSION_ROLE: { manager: "None" }, - }, - }, - // Staking Modules - CuratedModule: { - address: LIDO_CONTRACTS.CuratedModule, - permissions: { - STAKING_ROUTER_ROLE: { manager: "Agent", grantedTo: ["StakingRouter"] }, - MANAGE_NODE_OPERATOR_ROLE: { manager: "Agent", grantedTo: ["Agent"] }, - SET_NODE_OPERATOR_LIMIT_ROLE: { - manager: "Agent", - grantedTo: ["EasyTrackEvmScriptExecutor"], - }, - MANAGE_SIGNING_KEYS: { - manager: "Agent", - }, - }, - }, - SimpleDVT: { - address: LIDO_CONTRACTS.SimpleDVT, - permissions: { - STAKING_ROUTER_ROLE: { - manager: "Agent", - grantedTo: ["StakingRouter", "EasyTrackEvmScriptExecutor"], - }, - MANAGE_NODE_OPERATOR_ROLE: { - manager: "Agent", - grantedTo: ["EasyTrackEvmScriptExecutor"], - }, - SET_NODE_OPERATOR_LIMIT_ROLE: { - manager: "Agent", - grantedTo: ["EasyTrackEvmScriptExecutor"], - }, - MANAGE_SIGNING_KEYS: { - manager: "EasyTrackEvmScriptExecutor", - grantedTo: ["EasyTrackEvmScriptExecutor"], - }, - }, - }, + // // DAO Contracts + // DAOKernel: { + // address: LIDO_CONTRACTS.DAOKernel, + // permissions: { + // APP_MANAGER_ROLE: { manager: "Agent" }, + // }, + // }, + // Voting: { + // address: LIDO_CONTRACTS.Voting, + // permissions: { + // UNSAFELY_MODIFY_VOTE_TIME_ROLE: { manager: "Voting" }, + // MODIFY_QUORUM_ROLE: { manager: "Voting", grantedTo: ["Voting"] }, + // MODIFY_SUPPORT_ROLE: { manager: "Voting", grantedTo: ["Voting"] }, + // CREATE_VOTES_ROLE: { manager: "Voting", grantedTo: ["TokenManager"] }, + // }, + // }, + // TokenManager: { + // address: LIDO_CONTRACTS.TokenManager, + // permissions: { + // ISSUE_ROLE: { manager: "Voting" }, + // ASSIGN_ROLE: { manager: "Voting", grantedTo: ["Voting"] }, + // BURN_ROLE: { manager: "Voting" }, + // MINT_ROLE: { manager: "Voting" }, + // REVOKE_VESTINGS_ROLE: { manager: "Voting" }, + // }, + // }, + // Finance: { + // address: LIDO_CONTRACTS.Finance, + // permissions: { + // CREATE_PAYMENTS_ROLE: { + // manager: "Voting", + // grantedTo: ["Voting", "EasyTrackEvmScriptExecutor"], + // }, + // CHANGE_PERIOD_ROLE: { manager: "Voting" }, + // CHANGE_BUDGETS_ROLE: { manager: "Voting" }, + // EXECUTE_PAYMENTS_ROLE: { manager: "Voting", grantedTo: ["Voting"] }, + // MANAGE_PAYMENTS_ROLE: { manager: "Voting", grantedTo: ["Voting"] }, + // }, + // }, + // Agent: { + // address: LIDO_CONTRACTS.Agent, + // permissions: { + // ADD_PROTECTED_TOKEN_ROLE: { manager: "Voting" }, + // REMOVE_PROTECTED_TOKEN_ROLE: { manager: "Voting" }, + // TRANSFER_ROLE: { manager: "Voting", grantedTo: ["Finance"] }, + // RUN_SCRIPT_ROLE: { + // manager: "DualGovernance", + // grantedTo: ["DualGovernance"], + // }, + // SAFE_EXECUTE_ROLE: { manager: "None" }, + // DESIGNATE_SIGNER_ROLE: { manager: "Voting" }, + // EXECUTE_ROLE: { + // manager: "DualGovernance", + // grantedTo: ["DualGovernance"], + // }, + // ADD_PRESIGNED_HASH_ROLE: { manager: "Voting" }, + // }, + // }, + // ACL: { + // address: LIDO_CONTRACTS.ACL, + // permissions: { CREATE_PERMISSIONS_ROLE: { manager: "Agent" } }, + // }, + // AragonPM: { + // address: LIDO_CONTRACTS.AragonPM, + // permissions: { + // CREATE_REPO_ROLE: { manager: "None" }, + // }, + // }, + // EVMScriptRegistry: { + // address: LIDO_CONTRACTS.EVMScriptRegistry, + // permissions: { + // REGISTRY_ADD_EXECUTOR_ROLE: { manager: "None" }, + // REGISTRY_MANAGER_ROLE: { manager: "None" }, + // }, + // }, + // VotingRepo: { + // address: LIDO_CONTRACTS.VotingRepo, + // permissions: { + // CREATE_VERSION_ROLE: { manager: "None" }, + // }, + // }, + // LidoRepo: { + // address: LIDO_CONTRACTS.LidoRepo, + // permissions: { + // CREATE_VERSION_ROLE: { manager: "None" }, + // }, + // }, + // LegacyOracleRepo: { + // address: LIDO_CONTRACTS.LegacyOracleRepo, + // permissions: { + // CREATE_VERSION_ROLE: { manager: "None" }, + // }, + // }, + // CuratedModuleRepo: { + // address: LIDO_CONTRACTS.CuratedModuleRepo, + // permissions: { + // CREATE_VERSION_ROLE: { manager: "None" }, + // }, + // }, + // SimpleDVTRepo: { + // address: LIDO_CONTRACTS.SimpleDVTRepo, + // permissions: { + // CREATE_VERSION_ROLE: { manager: "None" }, + // }, + // }, + // // Staking Modules + // CuratedModule: { + // address: LIDO_CONTRACTS.CuratedModule, + // permissions: { + // STAKING_ROUTER_ROLE: { manager: "Agent", grantedTo: ["StakingRouter"] }, + // MANAGE_NODE_OPERATOR_ROLE: { manager: "Agent", grantedTo: ["Agent"] }, + // SET_NODE_OPERATOR_LIMIT_ROLE: { + // manager: "Agent", + // grantedTo: ["EasyTrackEvmScriptExecutor"], + // }, + // MANAGE_SIGNING_KEYS: { + // manager: "Agent", + // }, + // }, + // }, + // SimpleDVT: { + // address: LIDO_CONTRACTS.SimpleDVT, + // permissions: { + // STAKING_ROUTER_ROLE: { + // manager: "Agent", + // grantedTo: ["StakingRouter", "EasyTrackEvmScriptExecutor"], + // }, + // MANAGE_NODE_OPERATOR_ROLE: { + // manager: "Agent", + // grantedTo: ["EasyTrackEvmScriptExecutor"], + // }, + // SET_NODE_OPERATOR_LIMIT_ROLE: { + // manager: "Agent", + // grantedTo: ["EasyTrackEvmScriptExecutor"], + // }, + // MANAGE_SIGNING_KEYS: { + // manager: "EasyTrackEvmScriptExecutor", + // grantedTo: ["EasyTrackEvmScriptExecutor"], + // }, + // }, + // }, }; export const OZ_CONTRACT_ROLES_CONFIG: OZContractRolesConfig = { diff --git a/scripts/permissions-migration/config/lido-contracts.ts b/scripts/permissions-migration/config/lido-contracts.ts index caaabb9d..4b23ae18 100644 --- a/scripts/permissions-migration/config/lido-contracts.ts +++ b/scripts/permissions-migration/config/lido-contracts.ts @@ -11,7 +11,7 @@ export type LidoContractName = export const LIDO_CONTRACTS = { // Core Protocol LidoLocator: "0xC1d0b3DE6792Bf6b4b37EccdcC24e45978Cfd2Eb", - Lido: "0xae7ab96520DE3A18E5e111B5EaAb095312D7fE84", + Lido: "0x3F1c547b21f65e10480dE3ad8E19fAAC46C95034", StakingRouter: "0xFdDf38947aFB03C621C71b06C9C70bce73f12999", DepositSecurityModule: "0xffa96d84def2ea035c7ab153d8b991128e3d72fd", ExecutionLayerRewardsVault: "0x388C818CA8B9251b393131C08a736A67ccB19297", @@ -31,11 +31,11 @@ export const LIDO_CONTRACTS = { // DAO Contracts DAOKernel: "0xb8FFC3Cd6e7Cf5a098A1c92F48009765B24088Dc", - Voting: "0x2e59A20f205bB85a89C53f1936454680651E618e", + Voting: "0xdA7d2573Df555002503F29aA4003e398d28cc00f ", Agent: "0x3e40D73EB977Dc6a537aF587D48316feE66E9C8c", TokenManager: "0xf73a1260d222f447210581DDf212D915c09a3249", Finance: "0xB9E5CBB9CA5b0d659238807E84D0176930753d86", - ACL: "0x9895f0f17cc1d1891b6f18ee0b483b6f221b37bb", + ACL: "0xfd1E42595CeC3E83239bf8dFc535250e7F48E0bC", AragonPM: "0x0cb113890b04b49455dfe06554e2d784598a29c9", VotingRepo: "0x4ee3118e3858e8d7164a634825bfe0f73d99c792", LidoRepo: "0xF5Dc67E54FC96F993CD06073f71ca732C1E654B1", @@ -66,36 +66,6 @@ export const LIDO_CONTRACTS = { EasyTrackEvmScriptExecutor: "0xFE5986E06210aC1eCC1aDCafc0cc7f8D63B3F977", // Easy Track Factories for token transfers - LOLStETH_AllowedRecipientsRegistry: - "0x48c4929630099b217136b64089E8543dB0E5163a", - RewardsShareStETH_AllowedRecipientsRegistry: - "0xdc7300622948a7AdaF339783F6991F9cdDD79776", - LegoLDO_AllowedRecipientsRegistry: - "0x97615f72c3428A393d65A84A3ea6BBD9ad6C0D74", - LegoStablecoins_AllowedRecipientsRegistry: - "0xb0FE4D300334461523D9d61AaD90D0494e1Abb43", - RCCStableCoins_AllowedRecipientsRegistry: - "0xDc1A0C7849150f466F07d48b38eAA6cE99079f80", - RCCStETH_AllowedRecipientsRegistry: - "0xAAC4FcE2c5d55D1152512fe5FAA94DB267EE4863", - PMLStablecoins_AllowedRecipientsRegistry: - "0xDFfCD3BF14796a62a804c1B16F877Cf7120379dB", - PMLStETH_AllowedRecipientsRegistry: - "0x7b9B8d00f807663d46Fb07F87d61B79884BC335B", - ATCStablecoins_AllowedRecipientsRegistry: - "0xe07305F43B11F230EaA951002F6a55a16419B707", - ATCStETH_AllowedRecipientsRegistry: - "0xd3950eB3d7A9B0aBf8515922c0d35D13e85a2c91", - TRPLDO_AllowedRecipientsRegistry: - "0x231Ac69A1A37649C6B06a71Ab32DdD92158C80b8", - GasSupplyStETH_AllowedRecipientsRegistry: - "0x49d1363016aA899bba09ae972a1BF200dDf8C55F", - AllianceOpsStablecoins_AllowedRecipientsRegistry: - "0x3B525F4c059F246Ca4aa995D21087204F30c9E2F", - StonksStETH_AllowedRecipientsRegistry: - "0x1a7cFA9EFB4D5BfFDE87B0FaEb1fC65d653868C0", - StonksStablecoins_AllowedRecipientsRegistry: - "0x3f0534CCcFb952470775C516DC2eff8396B8A368", AllowedTokensRegistry: "0x4AC40c34f8992bb1e5E856A448792158022551ca", // Arbitrum diff --git a/scripts/permissions-migration/contracts/AragonRolesVerifier.sol b/scripts/permissions-migration/contracts/AragonRolesVerifier.sol new file mode 100644 index 00000000..ea6eaded --- /dev/null +++ b/scripts/permissions-migration/contracts/AragonRolesVerifier.sol @@ -0,0 +1,34 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.26; + +interface ACL { + function hasPermission(address _who, address _where, bytes32 _what) external view returns (bool); +} + +contract AragonRolesVerifier { + struct RoleToVerify { + address who; + bytes32 what; + address where; + bool granted; + } + + address public constant ACL_ADDRESS = 0xfd1E42595CeC3E83239bf8dFc535250e7F48E0bC; + + RoleToVerify[] public rolesToVerify; + + constructor(RoleToVerify[] memory _rolesToVerify) { + for (uint256 i = 0; i < _rolesToVerify.length; i++) { + rolesToVerify.push(_rolesToVerify[i]); + } + } + + function verify() public view { + ACL acl = ACL(ACL_ADDRESS); + for (uint256 i = 0; i < rolesToVerify.length; i++) { + bool isPermissionGranted = + acl.hasPermission(rolesToVerify[i].who, rolesToVerify[i].where, rolesToVerify[i].what); + assert(isPermissionGranted == rolesToVerify[i].granted); + } + } +} diff --git a/scripts/permissions-migration/deploy/deploy-aragon-verifier.ts b/scripts/permissions-migration/deploy/deploy-aragon-verifier.ts new file mode 100644 index 00000000..54e54c05 --- /dev/null +++ b/scripts/permissions-migration/deploy/deploy-aragon-verifier.ts @@ -0,0 +1,63 @@ +import fs from "fs"; + +import { getAddress, hexlify } from "ethers"; +import { JsonRpcProvider, Wallet, ContractFactory } from "ethers"; + +export async function deployAragonVerifier( + provider: JsonRpcProvider, + privateKey?: string, + etherscanApiKey?: string, +) { + if (!privateKey) throw new Error("PRIVATE_KEY env variable is missing."); + if (!etherscanApiKey) + throw new Error("ETHERSCAN_API env variable is missing."); + + const wallet = new Wallet(privateKey, provider); + const contractJson = JSON.parse( + fs.readFileSync( + "./out/AragonRolesVerifier.sol/AragonRolesVerifier.json", + "utf8", + ), + ); + const contractABI = contractJson.abi; + const contractBytecode = contractJson.bytecode; + + const rawData = JSON.parse( + fs.readFileSync( + "./scripts/permissions-migration/aragon-permissions.json", + "utf8", + ), + ); + + console.log(rawData[0]); + + const roles = rawData.map((role: any) => ({ + who: getAddress(role.who), + what: hexlify(role.what), + where: getAddress(role.where), + granted: role.granted, + })); + + (async () => { + try { + console.log("🚀 Деплоим AragonRolesVerifier..."); + + // 📌 Создать контрактный factory + const factory = new ContractFactory( + contractABI, + contractBytecode, + wallet, + ); + + // 📌 Деплоить контракт с массивом структур + const contract = await factory.deploy(roles); + + console.log("📡 Ожидание деплоя..."); + await contract.waitForDeployment(); + + console.log(`✅ Контракт развернут по адресу: ${contract.target}`); + } catch (error) { + console.error("❌ Ошибка при деплое:", error); + } + })(); +} diff --git a/scripts/permissions-migration/index.ts b/scripts/permissions-migration/index.ts index 83a92b16..af7b38ae 100644 --- a/scripts/permissions-migration/index.ts +++ b/scripts/permissions-migration/index.ts @@ -6,6 +6,9 @@ import aragon from "./src/aragon-permissions"; import managed from "./src/managed-contracts"; import { retrieveDeployConfiguration } from "./src/aragon-deploy"; +import { aragonGenerateJson } from "./src/aragon-generate-json"; +import { deployAragonVerifier } from "./deploy/deploy-aragon-verifier"; + import { ARAGON_CONTRACT_ROLES_CONFIG, MANAGED_CONTRACTS, @@ -13,6 +16,8 @@ import { } from "./config/agent-transfer-permissions-config"; const RPC_URL = process.env.MAINNET_RPC_URL; +const PRIVATE_KEY = process.env.PRIVATE_KEY; +const ETHERSCAN_API_KEY = process.env.ETHERSCAN_API_KEY; if (!RPC_URL) { throw new Error("RPC_URL env variable not set"); @@ -21,25 +26,27 @@ if (!RPC_URL) { async function main() { const provider = new JsonRpcProvider(RPC_URL); console.log(`## Lido Permissions Transitions`); - console.log( - aragon.formatContractPermissionsSection( - await aragon.collectPermissionsData( - provider, - ARAGON_CONTRACT_ROLES_CONFIG - ) - ) - ); - console.log( - oz.formatContractRolesSection( - await oz.collectRolesInfo(provider, OZ_CONTRACT_ROLES_CONFIG) - ) - ); - console.log( - managed.formatControlledContractsSection( - await managed.collectManagedContractsInfo(provider, MANAGED_CONTRACTS) - ) + const aragonPermissions = await aragon.collectPermissionsData( + provider, + ARAGON_CONTRACT_ROLES_CONFIG, ); - console.log(await retrieveDeployConfiguration(provider)); + + aragonGenerateJson(aragonPermissions); + // deployAragonVerifier(provider, PRIVATE_KEY, ETHERSCAN_API_KEY); + + // const ozPermissions = await oz.collectRolesInfo( + // provider, + // OZ_CONTRACT_ROLES_CONFIG, + // ); + // const managedContractsInfo = await managed.collectManagedContractsInfo( + // provider, + // MANAGED_CONTRACTS, + // ); + + // console.log(aragon.formatContractPermissionsSection(aragonPermissions)); + // console.log(oz.formatContractRolesSection(ozPermissions)); + // console.log(managed.formatControlledContractsSection(managedContractsInfo)); + // console.log(await retrieveDeployConfiguration(provider)); } main().catch((error) => { diff --git a/scripts/permissions-migration/src/aragon-generate-json.ts b/scripts/permissions-migration/src/aragon-generate-json.ts new file mode 100644 index 00000000..4f054453 --- /dev/null +++ b/scripts/permissions-migration/src/aragon-generate-json.ts @@ -0,0 +1,45 @@ +import { writeFileSync } from "fs"; +import { keccak256, toUtf8Bytes } from "ethers"; +import { AragonPermissionsInfo } from "./aragon-permissions"; +import { LIDO_CONTRACTS } from "../config/lido-contracts"; + +export async function aragonGenerateJson( + aragonPermissions: AragonPermissionsInfo, +) { + const result: any = []; + + for (const contract of Object.keys(aragonPermissions)) { + const contractAddress = contract as keyof typeof LIDO_CONTRACTS; + const modifiedPermissions = aragonPermissions[contract].filter( + (permission) => permission.isModified, + ); + if (!modifiedPermissions.length) continue; + + for (const permission of modifiedPermissions) { + for (const grantee of permission.holdersToGrantRole) { + if (grantee == "DualGovernance") continue; + result.push({ + where: LIDO_CONTRACTS[contractAddress], + what: keccak256(toUtf8Bytes(permission.name)), + who: LIDO_CONTRACTS[grantee as keyof typeof LIDO_CONTRACTS], + granted: true, + }); + } + + for (const grantee of permission.holdersToRevokeRole) { + if (grantee == "DualGovernance") continue; + result.push({ + where: LIDO_CONTRACTS[contractAddress], + what: keccak256(toUtf8Bytes(permission.name)), + who: LIDO_CONTRACTS[grantee as keyof typeof LIDO_CONTRACTS], + granted: false, + }); + } + } + } + + writeFileSync( + "./scripts/permissions-migration/aragon-permissions.json", + JSON.stringify(result), + ); +} diff --git a/scripts/permissions-migration/src/aragon-permissions.ts b/scripts/permissions-migration/src/aragon-permissions.ts index b5aec105..396be772 100644 --- a/scripts/permissions-migration/src/aragon-permissions.ts +++ b/scripts/permissions-migration/src/aragon-permissions.ts @@ -33,7 +33,7 @@ export type AragonContractPermissionConfigs = Record< AragonContractPermissionConfig >; -interface AragonPermissionInfo { +export interface AragonPermissionInfo { name: string; isModified: boolean; oldManager: LidoContractName; @@ -43,7 +43,7 @@ interface AragonPermissionInfo { holderAlreadyGrantedWithRole: LidoContractName[]; } -interface AragonPermissionsInfo { +export interface AragonPermissionsInfo { [contractName: string]: AragonPermissionInfo[]; } @@ -61,7 +61,7 @@ interface AragonPermissionHolders { } function formatContractPermissionsSection( - aragonContractsInfo: AragonPermissionsInfo + aragonContractsInfo: AragonPermissionsInfo, ) { const resSectionLines: string[] = ["### Aragon Roles Transition \n"]; @@ -88,17 +88,17 @@ function formatPermissionsInfoTable(aragonRolesInfo: AragonPermissionInfo[]) { let modifiedRolesCount = 0; for (const role of aragonRolesInfo.sort( - (a, b) => Number(!a.isModified) - Number(!b.isModified) + (a, b) => Number(!a.isModified) - Number(!b.isModified), )) { if (role.isModified) { modifiedRolesCount += 1; } const oldManagerLabel = md.label( - role.oldManager === "None" ? md.empty() : role.oldManager + role.oldManager === "None" ? md.empty() : role.oldManager, ); const newManagerLabel = md.label( - role.newManager === "None" ? md.empty() : role.newManager + role.newManager === "None" ? md.empty() : role.newManager, ); const managerTransition = @@ -107,31 +107,31 @@ function formatPermissionsInfoTable(aragonRolesInfo: AragonPermissionInfo[]) { : md.label(oldManagerLabel); const unknownRoleHolders = role.holdersToRevokeRole.filter((holderName) => - holderName.startsWith("Unknown") + holderName.startsWith("Unknown"), ); const knownRoleHolders = role.holdersToRevokeRole.filter( - (holderName) => !holderName.startsWith("Unknown") + (holderName) => !holderName.startsWith("Unknown"), ); const revokedFromItems = role.holdersToRevokeRole.length === 0 ? [md.empty()] : knownRoleHolders.map((roleHolder) => - md.bold(md.label(CONTRACT_LABELS[roleHolder] ?? roleHolder)) + md.bold(md.label(CONTRACT_LABELS[roleHolder] ?? roleHolder)), ); if (unknownRoleHolders.length > 0) { revokedFromItems.push( - md.label(`+${unknownRoleHolders.length} **UNKNOWN** holders`) + md.label(`+${unknownRoleHolders.length} **UNKNOWN** holders`), ); } const grantedToItems: string[] = [ ...role.holderAlreadyGrantedWithRole.map((roleHolder) => - md.label(CONTRACT_LABELS[roleHolder] ?? roleHolder) + md.label(CONTRACT_LABELS[roleHolder] ?? roleHolder), ), ...role.holdersToGrantRole.map((roleHolder) => - md.bold(CONTRACT_LABELS[roleHolder] ?? md.label(roleHolder)) + md.bold(CONTRACT_LABELS[roleHolder] ?? md.label(roleHolder)), ), ]; @@ -158,7 +158,7 @@ function formatPermissionsInfoTable(aragonRolesInfo: AragonPermissionInfo[]) { async function collectPermissionsData( provider: JsonRpcProvider, - config: AragonContractPermissionConfigs + config: AragonContractPermissionConfigs, ) { const { managers, permissions } = await fetchACLPermissionsInfo(provider); const aragonContractsInfo: AragonPermissionsInfo = {}; @@ -168,12 +168,12 @@ async function collectPermissionsData( aragonContractsInfo[contractName] = []; for (const [permissionName, { manager, grantedTo }] of Object.entries( - contractInfo.permissions + contractInfo.permissions, )) { const permissionHash = await getPermissionHash( provider, address, - permissionName + permissionName, ); const newManager = manager; @@ -191,13 +191,13 @@ async function collectPermissionsData( }); const holdersToGrantRole = (grantedTo ?? []).filter( - (roleHolder) => !currentlyGrantedTo.includes(roleHolder) + (roleHolder) => !currentlyGrantedTo.includes(roleHolder), ); const holdersToRevokeRole = currentlyGrantedTo.filter( - (roleHolderName) => !(grantedTo ?? []).includes(roleHolderName) + (roleHolderName) => !(grantedTo ?? []).includes(roleHolderName), ); const holderAlreadyGrantedWithRole = (grantedTo ?? []).filter( - (roleHolder) => currentlyGrantedTo.includes(roleHolder) + (roleHolder) => currentlyGrantedTo.includes(roleHolder), ); aragonContractsInfo[contractName].push({ @@ -253,7 +253,7 @@ async function fetchACLPermissionsInfo(provider: JsonRpcProvider) { if (!result.permissions[app][role].includes(entity!)) { } result.permissions[app][role] = result.permissions[app][role].filter( - (e) => e !== entity + (e) => e !== entity, ); } } @@ -265,14 +265,14 @@ async function fetchACLPermissionsInfo(provider: JsonRpcProvider) { async function getACLPermissionEvents( provider: JsonRpcProvider, - filterRange?: { fromBlock: number; toBlock?: number } + filterRange?: { fromBlock: number; toBlock?: number }, ) { const setPermissionTopic = id("SetPermission(address,address,bytes32,bool)"); const setPermissionParamsTopic = id( - "SetPermissionParams(address,address,bytes32,bytes32)" + "SetPermissionParams(address,address,bytes32,bytes32)", ); const changePermissionManagerTopic = id( - "ChangePermissionManager(address,bytes32,address)" + "ChangePermissionManager(address,bytes32,address)", ); const filterParams = { @@ -343,7 +343,7 @@ async function getACLPermissionEvents( async function getPermissionHash( provider: JsonRpcProvider, address: Address, - permissionName: string + permissionName: string, ) { return makeContractCall(provider, address, permissionName); } diff --git a/scripts/permissions-migration/src/comparison.ts b/scripts/permissions-migration/src/comparison.ts new file mode 100644 index 00000000..bc19b2cc --- /dev/null +++ b/scripts/permissions-migration/src/comparison.ts @@ -0,0 +1 @@ +export async function getContractsToCompareWithOmnubis() {}