diff --git a/.gitignore b/.gitignore index ac7798aa4..b307cb271 100644 --- a/.gitignore +++ b/.gitignore @@ -31,3 +31,8 @@ yarn-error.log # local env .envrc + +# Hardhat +cache/ +hardhat.config.js +package-lock.json \ No newline at end of file diff --git a/README.md b/README.md index 133f4a561..7e02c731a 100644 --- a/README.md +++ b/README.md @@ -169,8 +169,7 @@ To start a new vote please provide the `DEPLOYER` brownie account name (wallet): export DEPLOYER= ``` -To run tests with a contract name resolution guided by the Etherscan you should -provide the etherscan API token: +To run scripts that require decoding of EVM scripts and tests with contract name resolution via Etherscan you should provide the etherscan API token: ```bash export ETHERSCAN_TOKEN= diff --git a/configs/config_holesky.py b/configs/config_holesky.py index c9b22258d..0948c29ee 100644 --- a/configs/config_holesky.py +++ b/configs/config_holesky.py @@ -63,6 +63,8 @@ EASYTRACK_SIMPLE_DVT_UPDATE_TARGET_VALIDATOR_LIMITS_FACTORY = "0xC91a676A69Eb49be9ECa1954fE6fc861AE07A9A2" EASYTRACK_SIMPLE_DVT_CHANGE_NODE_OPERATOR_MANAGERS_FACTORY = "0xb8C4728bc0826bA5864D02FA53148de7A44C2f7E" +EASYTRACK_ALLOWED_TOKENS_REGISTRY = "0x091C0eC8B4D54a9fcB36269B5D5E5AF43309e666" + # Multisigs FINANCE_MULTISIG = "" diff --git a/configs/config_mainnet.py b/configs/config_mainnet.py index 024f72d63..df60bbe60 100644 --- a/configs/config_mainnet.py +++ b/configs/config_mainnet.py @@ -70,6 +70,8 @@ EASYTRACK_SIMPLE_DVT_CHANGE_NODE_OPERATOR_MANAGERS_FACTORY = "0xE31A0599A6772BCf9b2bFc9e25cf941e793c9a7D" EASYTRACK_CSM_SETTLE_EL_REWARDS_STEALING_PENALTY_FACTORY = "0xF6B6E7997338C48Ea3a8BCfa4BB64a315fDa76f4" +EASYTRACK_ALLOWED_TOKENS_REGISTRY = "0x4AC40c34f8992bb1e5E856A448792158022551ca" + # Multisigs FINANCE_MULTISIG = "0x48F300bD3C52c7dA6aAbDE4B683dEB27d38B9ABb" L1_EMERGENCY_BRAKES_MULTISIG = "0x73b047fe6337183A454c5217241D780a932777bD" diff --git a/interfaces/DualGovernance.json b/interfaces/DualGovernance.json new file mode 100644 index 000000000..9f9c30d40 --- /dev/null +++ b/interfaces/DualGovernance.json @@ -0,0 +1,1117 @@ +[ + { + "inputs": [ + { + "components": [ + { + "internalType": "contract ITimelock", + "name": "timelock", + "type": "address" + }, + { + "internalType": "contract IResealManager", + "name": "resealManager", + "type": "address" + }, + { + "internalType": "contract IDualGovernanceConfigProvider", + "name": "configProvider", + "type": "address" + } + ], + "internalType": "struct DualGovernance.DualGovernanceComponents", + "name": "components", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "contract IStETH", + "name": "stETH", + "type": "address" + }, + { + "internalType": "contract IWstETH", + "name": "wstETH", + "type": "address" + }, + { + "internalType": "contract IWithdrawalQueue", + "name": "withdrawalQueue", + "type": "address" + } + ], + "internalType": "struct DualGovernance.SignallingTokens", + "name": "signallingTokens", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "minWithdrawalsBatchSize", + "type": "uint256" + }, + { + "internalType": "Duration", + "name": "minTiebreakerActivationTimeout", + "type": "uint32" + }, + { + "internalType": "Duration", + "name": "maxTiebreakerActivationTimeout", + "type": "uint32" + }, + { + "internalType": "uint256", + "name": "maxSealableWithdrawalBlockersCount", + "type": "uint256" + }, + { + "internalType": "Duration", + "name": "maxMinAssetsLockDuration", + "type": "uint32" + } + ], + "internalType": "struct DualGovernance.SanityCheckParams", + "name": "sanityCheckParams", + "type": "tuple" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { "inputs": [], "name": "AlreadyInitialized", "type": "error" }, + { + "inputs": [ + { "internalType": "address", "name": "caller", "type": "address" } + ], + "name": "CallerIsNotAdminExecutor", + "type": "error" + }, + { + "inputs": [ + { "internalType": "address", "name": "caller", "type": "address" } + ], + "name": "CallerIsNotProposalsCanceller", + "type": "error" + }, + { + "inputs": [ + { "internalType": "address", "name": "caller", "type": "address" } + ], + "name": "CallerIsNotResealCommittee", + "type": "error" + }, + { + "inputs": [ + { "internalType": "address", "name": "caller", "type": "address" } + ], + "name": "CallerIsNotTiebreakerCommittee", + "type": "error" + }, + { "inputs": [], "name": "DurationOverflow", "type": "error" }, + { "inputs": [], "name": "DurationUnderflow", "type": "error" }, + { "inputs": [], "name": "ERC1167FailedCreateClone", "type": "error" }, + { + "inputs": [ + { "internalType": "address", "name": "executor", "type": "address" } + ], + "name": "ExecutorNotRegistered", + "type": "error" + }, + { "inputs": [], "name": "IndexOneBasedOverflow", "type": "error" }, + { "inputs": [], "name": "IndexOneBasedUnderflow", "type": "error" }, + { + "inputs": [ + { + "internalType": "contract IDualGovernanceConfigProvider", + "name": "configProvider", + "type": "address" + } + ], + "name": "InvalidConfigProvider", + "type": "error" + }, + { + "inputs": [ + { "internalType": "address", "name": "executor", "type": "address" } + ], + "name": "InvalidExecutor", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "Duration", + "name": "minAssetsLockDuration", + "type": "uint32" + } + ], + "name": "InvalidMinAssetsLockDuration", + "type": "error" + }, + { + "inputs": [ + { "internalType": "address", "name": "canceller", "type": "address" } + ], + "name": "InvalidProposalsCanceller", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "proposerAccount", + "type": "address" + } + ], + "name": "InvalidProposerAccount", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "Duration", + "name": "rageQuitEthWithdrawalsMinDelay", + "type": "uint32" + }, + { + "internalType": "Duration", + "name": "rageQuitEthWithdrawalsMaxDelay", + "type": "uint32" + } + ], + "name": "InvalidRageQuitEthWithdrawalsDelayRange", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "PercentD16", + "name": "firstSealRageQuitSupport", + "type": "uint128" + }, + { + "internalType": "PercentD16", + "name": "secondSealRageQuitSupport", + "type": "uint128" + } + ], + "name": "InvalidRageQuitSupportRange", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "resealCommittee", + "type": "address" + } + ], + "name": "InvalidResealCommittee", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "contract IResealManager", + "name": "resealManager", + "type": "address" + } + ], + "name": "InvalidResealManager", + "type": "error" + }, + { + "inputs": [ + { "internalType": "address", "name": "sealable", "type": "address" } + ], + "name": "InvalidSealable", + "type": "error" + }, + { + "inputs": [ + { "internalType": "Duration", "name": "timeout", "type": "uint32" } + ], + "name": "InvalidTiebreakerActivationTimeout", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "Duration", + "name": "minTiebreakerActivationTimeout", + "type": "uint32" + }, + { + "internalType": "Duration", + "name": "maxTiebreakerActivationTimeout", + "type": "uint32" + } + ], + "name": "InvalidTiebreakerActivationTimeoutBounds", + "type": "error" + }, + { + "inputs": [ + { "internalType": "address", "name": "account", "type": "address" } + ], + "name": "InvalidTiebreakerCommittee", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "Duration", + "name": "vetoSignallingMinDuration", + "type": "uint32" + }, + { + "internalType": "Duration", + "name": "vetoSignallingMaxDuration", + "type": "uint32" + } + ], + "name": "InvalidVetoSignallingDurationRange", + "type": "error" + }, + { "inputs": [], "name": "PercentD16Underflow", "type": "error" }, + { + "inputs": [ + { "internalType": "uint256", "name": "proposalId", "type": "uint256" } + ], + "name": "ProposalSchedulingBlocked", + "type": "error" + }, + { "inputs": [], "name": "ProposalSubmissionBlocked", "type": "error" }, + { + "inputs": [ + { + "internalType": "address", + "name": "proposerAccount", + "type": "address" + } + ], + "name": "ProposerAlreadyRegistered", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "proposerAccount", + "type": "address" + } + ], + "name": "ProposerNotRegistered", + "type": "error" + }, + { "inputs": [], "name": "ResealIsNotAllowedInNormalState", "type": "error" }, + { + "inputs": [ + { "internalType": "address", "name": "sealable", "type": "address" } + ], + "name": "SealableWithdrawalBlockerAlreadyAdded", + "type": "error" + }, + { + "inputs": [ + { "internalType": "address", "name": "sealable", "type": "address" } + ], + "name": "SealableWithdrawalBlockerNotFound", + "type": "error" + }, + { + "inputs": [], + "name": "SealableWithdrawalBlockersLimitReached", + "type": "error" + }, + { "inputs": [], "name": "TiebreakNotAllowed", "type": "error" }, + { "inputs": [], "name": "TimestampOverflow", "type": "error" }, + { + "anonymous": false, + "inputs": [], + "name": "CancelAllPendingProposalsExecuted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [], + "name": "CancelAllPendingProposalsSkipped", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "contract IDualGovernanceConfigProvider", + "name": "newConfigProvider", + "type": "address" + } + ], + "name": "ConfigProviderSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "enum State", + "name": "from", + "type": "uint8" + }, + { + "indexed": true, + "internalType": "enum State", + "name": "to", + "type": "uint8" + }, + { + "components": [ + { "internalType": "enum State", "name": "state", "type": "uint8" }, + { + "internalType": "Timestamp", + "name": "enteredAt", + "type": "uint40" + }, + { + "internalType": "Timestamp", + "name": "vetoSignallingActivatedAt", + "type": "uint40" + }, + { + "internalType": "contract ISignallingEscrow", + "name": "signallingEscrow", + "type": "address" + }, + { "internalType": "uint8", "name": "rageQuitRound", "type": "uint8" }, + { + "internalType": "Timestamp", + "name": "vetoSignallingReactivationTime", + "type": "uint40" + }, + { + "internalType": "Timestamp", + "name": "normalOrVetoCooldownExitedAt", + "type": "uint40" + }, + { + "internalType": "contract IRageQuitEscrow", + "name": "rageQuitEscrow", + "type": "address" + }, + { + "internalType": "contract IDualGovernanceConfigProvider", + "name": "configProvider", + "type": "address" + } + ], + "indexed": false, + "internalType": "struct DualGovernanceStateMachine.Context", + "name": "state", + "type": "tuple" + } + ], + "name": "DualGovernanceStateChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "contract IEscrowBase", + "name": "escrowMasterCopy", + "type": "address" + } + ], + "name": "EscrowMasterCopyDeployed", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "contract ISignallingEscrow", + "name": "escrow", + "type": "address" + } + ], + "name": "NewSignallingEscrowDeployed", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "proposerAccount", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "proposalId", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "string", + "name": "metadata", + "type": "string" + } + ], + "name": "ProposalSubmitted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "proposalsCanceller", + "type": "address" + } + ], + "name": "ProposalsCancellerSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "proposerAccount", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "executor", + "type": "address" + } + ], + "name": "ProposerExecutorSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "proposerAccount", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "executor", + "type": "address" + } + ], + "name": "ProposerRegistered", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "proposerAccount", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "executor", + "type": "address" + } + ], + "name": "ProposerUnregistered", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "resealCommittee", + "type": "address" + } + ], + "name": "ResealCommitteeSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "contract IResealManager", + "name": "resealManager", + "type": "address" + } + ], + "name": "ResealManagerSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "sealable", + "type": "address" + } + ], + "name": "SealableWithdrawalBlockerAdded", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "sealable", + "type": "address" + } + ], + "name": "SealableWithdrawalBlockerRemoved", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "Duration", + "name": "newTiebreakerActivationTimeout", + "type": "uint32" + } + ], + "name": "TiebreakerActivationTimeoutSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "newTiebreakerCommittee", + "type": "address" + } + ], + "name": "TiebreakerCommitteeSet", + "type": "event" + }, + { + "inputs": [], + "name": "MAX_SEALABLE_WITHDRAWAL_BLOCKERS_COUNT", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "MAX_TIEBREAKER_ACTIVATION_TIMEOUT", + "outputs": [{ "internalType": "Duration", "name": "", "type": "uint32" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "MIN_TIEBREAKER_ACTIVATION_TIMEOUT", + "outputs": [{ "internalType": "Duration", "name": "", "type": "uint32" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "TIMELOCK", + "outputs": [ + { "internalType": "contract ITimelock", "name": "", "type": "address" } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "activateNextState", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "sealableWithdrawalBlocker", + "type": "address" + } + ], + "name": "addTiebreakerSealableWithdrawalBlocker", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "canCancelAllPendingProposals", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "proposalId", "type": "uint256" } + ], + "name": "canScheduleProposal", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "canSubmitProposal", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "cancelAllPendingProposals", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "getConfigProvider", + "outputs": [ + { + "internalType": "contract IDualGovernanceConfigProvider", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getEffectiveState", + "outputs": [ + { + "internalType": "enum State", + "name": "effectiveState", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getPersistedState", + "outputs": [ + { + "internalType": "enum State", + "name": "persistedState", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getProposalsCanceller", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "proposerAccount", + "type": "address" + } + ], + "name": "getProposer", + "outputs": [ + { + "components": [ + { "internalType": "address", "name": "account", "type": "address" }, + { "internalType": "address", "name": "executor", "type": "address" } + ], + "internalType": "struct Proposers.Proposer", + "name": "proposer", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getProposers", + "outputs": [ + { + "components": [ + { "internalType": "address", "name": "account", "type": "address" }, + { "internalType": "address", "name": "executor", "type": "address" } + ], + "internalType": "struct Proposers.Proposer[]", + "name": "proposers", + "type": "tuple[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getRageQuitEscrow", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getResealCommittee", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getResealManager", + "outputs": [ + { + "internalType": "contract IResealManager", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getStateDetails", + "outputs": [ + { + "components": [ + { + "internalType": "enum State", + "name": "effectiveState", + "type": "uint8" + }, + { + "internalType": "enum State", + "name": "persistedState", + "type": "uint8" + }, + { + "internalType": "Timestamp", + "name": "persistedStateEnteredAt", + "type": "uint40" + }, + { + "internalType": "Timestamp", + "name": "vetoSignallingActivatedAt", + "type": "uint40" + }, + { + "internalType": "Timestamp", + "name": "vetoSignallingReactivationTime", + "type": "uint40" + }, + { + "internalType": "Timestamp", + "name": "normalOrVetoCooldownExitedAt", + "type": "uint40" + }, + { + "internalType": "uint256", + "name": "rageQuitRound", + "type": "uint256" + }, + { + "internalType": "Duration", + "name": "vetoSignallingDuration", + "type": "uint32" + } + ], + "internalType": "struct IDualGovernance.StateDetails", + "name": "stateDetails", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getTiebreakerDetails", + "outputs": [ + { + "components": [ + { "internalType": "bool", "name": "isTie", "type": "bool" }, + { + "internalType": "address", + "name": "tiebreakerCommittee", + "type": "address" + }, + { + "internalType": "Duration", + "name": "tiebreakerActivationTimeout", + "type": "uint32" + }, + { + "internalType": "address[]", + "name": "sealableWithdrawalBlockers", + "type": "address[]" + } + ], + "internalType": "struct ITiebreaker.TiebreakerDetails", + "name": "tiebreakerState", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getVetoSignallingEscrow", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "executor", "type": "address" } + ], + "name": "isExecutor", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "proposerAccount", + "type": "address" + } + ], + "name": "isProposer", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "proposerAccount", + "type": "address" + }, + { "internalType": "address", "name": "executor", "type": "address" } + ], + "name": "registerProposer", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "sealableWithdrawalBlocker", + "type": "address" + } + ], + "name": "removeTiebreakerSealableWithdrawalBlocker", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "sealable", "type": "address" } + ], + "name": "resealSealable", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "proposalId", "type": "uint256" } + ], + "name": "scheduleProposal", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract IDualGovernanceConfigProvider", + "name": "newConfigProvider", + "type": "address" + } + ], + "name": "setConfigProvider", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newProposalsCanceller", + "type": "address" + } + ], + "name": "setProposalsCanceller", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "proposerAccount", + "type": "address" + }, + { "internalType": "address", "name": "newExecutor", "type": "address" } + ], + "name": "setProposerExecutor", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newResealCommittee", + "type": "address" + } + ], + "name": "setResealCommittee", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract IResealManager", + "name": "newResealManager", + "type": "address" + } + ], + "name": "setResealManager", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "Duration", + "name": "newTiebreakerActivationTimeout", + "type": "uint32" + } + ], + "name": "setTiebreakerActivationTimeout", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newTiebreakerCommittee", + "type": "address" + } + ], + "name": "setTiebreakerCommittee", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { "internalType": "address", "name": "target", "type": "address" }, + { "internalType": "uint96", "name": "value", "type": "uint96" }, + { "internalType": "bytes", "name": "payload", "type": "bytes" } + ], + "internalType": "struct ExternalCall[]", + "name": "calls", + "type": "tuple[]" + }, + { "internalType": "string", "name": "metadata", "type": "string" } + ], + "name": "submitProposal", + "outputs": [ + { "internalType": "uint256", "name": "proposalId", "type": "uint256" } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "sealable", "type": "address" } + ], + "name": "tiebreakerResumeSealable", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "proposalId", "type": "uint256" } + ], + "name": "tiebreakerScheduleProposal", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "proposerAccount", + "type": "address" + } + ], + "name": "unregisterProposer", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/interfaces/DualGovernanceAdminExecutor.json b/interfaces/DualGovernanceAdminExecutor.json new file mode 100644 index 000000000..ccf0c519b --- /dev/null +++ b/interfaces/DualGovernanceAdminExecutor.json @@ -0,0 +1,142 @@ +[ + { + "inputs": [ + { "internalType": "address", "name": "owner", "type": "address" } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [ + { "internalType": "address", "name": "target", "type": "address" } + ], + "name": "AddressEmptyCode", + "type": "error" + }, + { + "inputs": [ + { "internalType": "address", "name": "account", "type": "address" } + ], + "name": "AddressInsufficientBalance", + "type": "error" + }, + { "inputs": [], "name": "FailedInnerCall", "type": "error" }, + { + "inputs": [ + { "internalType": "address", "name": "owner", "type": "address" } + ], + "name": "OwnableInvalidOwner", + "type": "error" + }, + { + "inputs": [ + { "internalType": "address", "name": "account", "type": "address" } + ], + "name": "OwnableUnauthorizedAccount", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "ETHReceived", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "target", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "ethValue", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "bytes", + "name": "data", + "type": "bytes" + }, + { + "indexed": false, + "internalType": "bytes", + "name": "returndata", + "type": "bytes" + } + ], + "name": "Executed", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "inputs": [ + { "internalType": "address", "name": "target", "type": "address" }, + { "internalType": "uint256", "name": "value", "type": "uint256" }, + { "internalType": "bytes", "name": "payload", "type": "bytes" } + ], + "name": "execute", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "newOwner", "type": "address" } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { "stateMutability": "payable", "type": "receive" } +] diff --git a/interfaces/DualGovernanceLaunchVerifier.json b/interfaces/DualGovernanceLaunchVerifier.json new file mode 100644 index 000000000..742fcaae2 --- /dev/null +++ b/interfaces/DualGovernanceLaunchVerifier.json @@ -0,0 +1 @@ +[{"inputs":[{"components":[{"internalType":"address","name":"timelock","type":"address"},{"internalType":"address","name":"dualGovernance","type":"address"},{"internalType":"address","name":"emergencyGovernance","type":"address"},{"internalType":"address","name":"emergencyActivationCommittee","type":"address"},{"internalType":"address","name":"emergencyExecutionCommittee","type":"address"},{"internalType":"Timestamp","name":"emergencyProtectionEndDate","type":"uint40"},{"internalType":"Duration","name":"emergencyModeDuration","type":"uint32"},{"internalType":"uint256","name":"proposalsCount","type":"uint256"}],"internalType":"struct DGLaunchVerifier.ConstructorParams","name":"params","type":"tuple"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"EmergencyModeEnabledAfterLaunch","type":"error"},{"inputs":[{"internalType":"string","name":"paramName","type":"string"},{"internalType":"address","name":"expectedValue","type":"address"},{"internalType":"address","name":"actualValue","type":"address"}],"name":"InvalidDGLaunchConfigAddress","type":"error"},{"inputs":[{"internalType":"string","name":"paramName","type":"string"},{"internalType":"uint256","name":"expectedValue","type":"uint256"},{"internalType":"uint256","name":"actualValue","type":"uint256"}],"name":"InvalidDGLaunchConfigParameter","type":"error"},{"anonymous":false,"inputs":[],"name":"DGLaunchConfigurationValidated","type":"event"},{"inputs":[],"name":"DUAL_GOVERNANCE","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"EMERGENCY_ACTIVATION_COMMITTEE","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"EMERGENCY_EXECUTION_COMMITTEE","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"EMERGENCY_GOVERNANCE","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"EMERGENCY_MODE_DURATION","outputs":[{"internalType":"Duration","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"EMERGENCY_PROTECTION_END_DATE","outputs":[{"internalType":"Timestamp","name":"","type":"uint40"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PROPOSALS_COUNT","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"TIMELOCK","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"verify","outputs":[],"stateMutability":"nonpayable","type":"function"}] \ No newline at end of file diff --git a/interfaces/EmergencyProtectedTimelock.json b/interfaces/EmergencyProtectedTimelock.json new file mode 100644 index 000000000..9d294c377 --- /dev/null +++ b/interfaces/EmergencyProtectedTimelock.json @@ -0,0 +1,836 @@ +[ + { + "inputs": [ + { + "components": [ + { + "internalType": "Duration", + "name": "minExecutionDelay", + "type": "uint32" + }, + { + "internalType": "Duration", + "name": "maxAfterSubmitDelay", + "type": "uint32" + }, + { + "internalType": "Duration", + "name": "maxAfterScheduleDelay", + "type": "uint32" + }, + { + "internalType": "Duration", + "name": "maxEmergencyModeDuration", + "type": "uint32" + }, + { + "internalType": "Duration", + "name": "maxEmergencyProtectionDuration", + "type": "uint32" + } + ], + "internalType": "struct EmergencyProtectedTimelock.SanityCheckParams", + "name": "sanityCheckParams", + "type": "tuple" + }, + { "internalType": "address", "name": "adminExecutor", "type": "address" }, + { + "internalType": "Duration", + "name": "afterSubmitDelay", + "type": "uint32" + }, + { + "internalType": "Duration", + "name": "afterScheduleDelay", + "type": "uint32" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "proposalId", "type": "uint256" } + ], + "name": "AfterScheduleDelayNotPassed", + "type": "error" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "proposalId", "type": "uint256" } + ], + "name": "AfterSubmitDelayNotPassed", + "type": "error" + }, + { + "inputs": [ + { "internalType": "address", "name": "caller", "type": "address" } + ], + "name": "CallerIsNotAdminExecutor", + "type": "error" + }, + { + "inputs": [ + { "internalType": "address", "name": "caller", "type": "address" } + ], + "name": "CallerIsNotEmergencyActivationCommittee", + "type": "error" + }, + { + "inputs": [ + { "internalType": "address", "name": "caller", "type": "address" } + ], + "name": "CallerIsNotEmergencyExecutionCommittee", + "type": "error" + }, + { + "inputs": [ + { "internalType": "address", "name": "caller", "type": "address" } + ], + "name": "CallerIsNotGovernance", + "type": "error" + }, + { "inputs": [], "name": "DurationOverflow", "type": "error" }, + { + "inputs": [ + { "internalType": "Timestamp", "name": "protectedTill", "type": "uint40" } + ], + "name": "EmergencyProtectionExpired", + "type": "error" + }, + { "inputs": [], "name": "EmptyCalls", "type": "error" }, + { + "inputs": [ + { "internalType": "address", "name": "adminExecutor", "type": "address" } + ], + "name": "InvalidAdminExecutor", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "Duration", + "name": "afterScheduleDelay", + "type": "uint32" + } + ], + "name": "InvalidAfterScheduleDelay", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "Duration", + "name": "afterSubmitDelay", + "type": "uint32" + } + ], + "name": "InvalidAfterSubmitDelay", + "type": "error" + }, + { + "inputs": [ + { "internalType": "address", "name": "committee", "type": "address" } + ], + "name": "InvalidEmergencyActivationCommittee", + "type": "error" + }, + { + "inputs": [ + { "internalType": "address", "name": "committee", "type": "address" } + ], + "name": "InvalidEmergencyExecutionCommittee", + "type": "error" + }, + { + "inputs": [ + { "internalType": "address", "name": "governance", "type": "address" } + ], + "name": "InvalidEmergencyGovernance", + "type": "error" + }, + { + "inputs": [ + { "internalType": "Duration", "name": "value", "type": "uint32" } + ], + "name": "InvalidEmergencyModeDuration", + "type": "error" + }, + { + "inputs": [ + { "internalType": "Timestamp", "name": "value", "type": "uint40" } + ], + "name": "InvalidEmergencyProtectionEndDate", + "type": "error" + }, + { + "inputs": [ + { "internalType": "Duration", "name": "executionDelay", "type": "uint32" } + ], + "name": "InvalidExecutionDelay", + "type": "error" + }, + { + "inputs": [ + { "internalType": "address", "name": "governance", "type": "address" } + ], + "name": "InvalidGovernance", + "type": "error" + }, + { "inputs": [], "name": "TimestampOverflow", "type": "error" }, + { + "inputs": [{ "internalType": "bool", "name": "state", "type": "bool" }], + "name": "UnexpectedEmergencyModeState", + "type": "error" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "proposalId", "type": "uint256" }, + { "internalType": "enum Status", "name": "status", "type": "uint8" } + ], + "name": "UnexpectedProposalStatus", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "newAdminExecutor", + "type": "address" + } + ], + "name": "AdminExecutorSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "Duration", + "name": "newAfterScheduleDelay", + "type": "uint32" + } + ], + "name": "AfterScheduleDelaySet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "Duration", + "name": "newAfterSubmitDelay", + "type": "uint32" + } + ], + "name": "AfterSubmitDelaySet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "newActivationCommittee", + "type": "address" + } + ], + "name": "EmergencyActivationCommitteeSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "newExecutionCommittee", + "type": "address" + } + ], + "name": "EmergencyExecutionCommitteeSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "newEmergencyGovernance", + "type": "address" + } + ], + "name": "EmergencyGovernanceSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [], + "name": "EmergencyModeActivated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [], + "name": "EmergencyModeDeactivated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "Duration", + "name": "newEmergencyModeDuration", + "type": "uint32" + } + ], + "name": "EmergencyModeDurationSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "Timestamp", + "name": "newEmergencyProtectionEndDate", + "type": "uint40" + } + ], + "name": "EmergencyProtectionEndDateSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "newGovernance", + "type": "address" + } + ], + "name": "GovernanceSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "name": "ProposalExecuted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "name": "ProposalScheduled", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "executor", + "type": "address" + }, + { + "components": [ + { "internalType": "address", "name": "target", "type": "address" }, + { "internalType": "uint96", "name": "value", "type": "uint96" }, + { "internalType": "bytes", "name": "payload", "type": "bytes" } + ], + "indexed": false, + "internalType": "struct ExternalCall[]", + "name": "calls", + "type": "tuple[]" + } + ], + "name": "ProposalSubmitted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "proposalId", + "type": "uint256" + } + ], + "name": "ProposalsCancelledTill", + "type": "event" + }, + { + "inputs": [], + "name": "MAX_AFTER_SCHEDULE_DELAY", + "outputs": [{ "internalType": "Duration", "name": "", "type": "uint32" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "MAX_AFTER_SUBMIT_DELAY", + "outputs": [{ "internalType": "Duration", "name": "", "type": "uint32" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "MAX_EMERGENCY_MODE_DURATION", + "outputs": [{ "internalType": "Duration", "name": "", "type": "uint32" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "MAX_EMERGENCY_PROTECTION_DURATION", + "outputs": [{ "internalType": "Duration", "name": "", "type": "uint32" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "MIN_EXECUTION_DELAY", + "outputs": [{ "internalType": "Duration", "name": "", "type": "uint32" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "activateEmergencyMode", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "proposalId", "type": "uint256" } + ], + "name": "canExecute", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "proposalId", "type": "uint256" } + ], + "name": "canSchedule", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "cancelAllNonExecutedProposals", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "deactivateEmergencyMode", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "proposalId", "type": "uint256" } + ], + "name": "emergencyExecute", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "emergencyReset", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "proposalId", "type": "uint256" } + ], + "name": "execute", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "getAdminExecutor", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getAfterScheduleDelay", + "outputs": [{ "internalType": "Duration", "name": "", "type": "uint32" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getAfterSubmitDelay", + "outputs": [{ "internalType": "Duration", "name": "", "type": "uint32" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getEmergencyActivationCommittee", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getEmergencyExecutionCommittee", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getEmergencyGovernance", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getEmergencyProtectionDetails", + "outputs": [ + { + "components": [ + { + "internalType": "Duration", + "name": "emergencyModeDuration", + "type": "uint32" + }, + { + "internalType": "Timestamp", + "name": "emergencyModeEndsAfter", + "type": "uint40" + }, + { + "internalType": "Timestamp", + "name": "emergencyProtectionEndsAfter", + "type": "uint40" + } + ], + "internalType": "struct IEmergencyProtectedTimelock.EmergencyProtectionDetails", + "name": "details", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getGovernance", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "proposalId", "type": "uint256" } + ], + "name": "getProposal", + "outputs": [ + { + "components": [ + { "internalType": "uint256", "name": "id", "type": "uint256" }, + { "internalType": "address", "name": "executor", "type": "address" }, + { + "internalType": "Timestamp", + "name": "submittedAt", + "type": "uint40" + }, + { + "internalType": "Timestamp", + "name": "scheduledAt", + "type": "uint40" + }, + { "internalType": "enum Status", "name": "status", "type": "uint8" } + ], + "internalType": "struct ITimelock.ProposalDetails", + "name": "proposalDetails", + "type": "tuple" + }, + { + "components": [ + { "internalType": "address", "name": "target", "type": "address" }, + { "internalType": "uint96", "name": "value", "type": "uint96" }, + { "internalType": "bytes", "name": "payload", "type": "bytes" } + ], + "internalType": "struct ExternalCall[]", + "name": "calls", + "type": "tuple[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "proposalId", "type": "uint256" } + ], + "name": "getProposalCalls", + "outputs": [ + { + "components": [ + { "internalType": "address", "name": "target", "type": "address" }, + { "internalType": "uint96", "name": "value", "type": "uint96" }, + { "internalType": "bytes", "name": "payload", "type": "bytes" } + ], + "internalType": "struct ExternalCall[]", + "name": "calls", + "type": "tuple[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "proposalId", "type": "uint256" } + ], + "name": "getProposalDetails", + "outputs": [ + { + "components": [ + { "internalType": "uint256", "name": "id", "type": "uint256" }, + { "internalType": "address", "name": "executor", "type": "address" }, + { + "internalType": "Timestamp", + "name": "submittedAt", + "type": "uint40" + }, + { + "internalType": "Timestamp", + "name": "scheduledAt", + "type": "uint40" + }, + { "internalType": "enum Status", "name": "status", "type": "uint8" } + ], + "internalType": "struct ITimelock.ProposalDetails", + "name": "proposalDetails", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getProposalsCount", + "outputs": [ + { "internalType": "uint256", "name": "count", "type": "uint256" } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "isEmergencyModeActive", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "isEmergencyProtectionEnabled", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "proposalId", "type": "uint256" } + ], + "name": "schedule", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newAdminExecutor", + "type": "address" + } + ], + "name": "setAdminExecutor", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "Duration", + "name": "newAfterScheduleDelay", + "type": "uint32" + } + ], + "name": "setAfterScheduleDelay", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "Duration", + "name": "newAfterSubmitDelay", + "type": "uint32" + } + ], + "name": "setAfterSubmitDelay", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newEmergencyGovernance", + "type": "address" + } + ], + "name": "setEmergencyGovernance", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "Duration", + "name": "newEmergencyModeDuration", + "type": "uint32" + } + ], + "name": "setEmergencyModeDuration", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newEmergencyActivationCommittee", + "type": "address" + } + ], + "name": "setEmergencyProtectionActivationCommittee", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "Timestamp", + "name": "newEmergencyProtectionEndDate", + "type": "uint40" + } + ], + "name": "setEmergencyProtectionEndDate", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newEmergencyExecutionCommittee", + "type": "address" + } + ], + "name": "setEmergencyProtectionExecutionCommittee", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "newGovernance", "type": "address" } + ], + "name": "setGovernance", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "executor", "type": "address" }, + { + "components": [ + { "internalType": "address", "name": "target", "type": "address" }, + { "internalType": "uint96", "name": "value", "type": "uint96" }, + { "internalType": "bytes", "name": "payload", "type": "bytes" } + ], + "internalType": "struct ExternalCall[]", + "name": "calls", + "type": "tuple[]" + } + ], + "name": "submit", + "outputs": [ + { "internalType": "uint256", "name": "newProposalId", "type": "uint256" } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "executor", "type": "address" }, + { "internalType": "address", "name": "owner", "type": "address" } + ], + "name": "transferExecutorOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/interfaces/Foo.json b/interfaces/Foo.json new file mode 100644 index 000000000..3aa189434 --- /dev/null +++ b/interfaces/Foo.json @@ -0,0 +1,29 @@ +[ + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" + } + ], + "name": "BarCall", + "type": "event" + }, + { + "inputs": [], + "name": "bar", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "callCount", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + } +] diff --git a/interfaces/RolesValidator.json b/interfaces/RolesValidator.json new file mode 100644 index 000000000..f963945f1 --- /dev/null +++ b/interfaces/RolesValidator.json @@ -0,0 +1,132 @@ +[ + { "inputs": [], "stateMutability": "nonpayable", "type": "constructor" }, + { + "inputs": [ + { "internalType": "address", "name": "entity", "type": "address" }, + { "internalType": "string", "name": "roleName", "type": "string" }, + { "internalType": "address", "name": "app", "type": "address" } + ], + "name": "AragonPermissionGranted", + "type": "error" + }, + { + "inputs": [ + { "internalType": "address", "name": "entity", "type": "address" }, + { "internalType": "string", "name": "roleName", "type": "string" }, + { + "internalType": "address", + "name": "expectedManager", + "type": "address" + }, + { "internalType": "address", "name": "actualManager", "type": "address" } + ], + "name": "AragonPermissionInvalidManager", + "type": "error" + }, + { + "inputs": [ + { "internalType": "address", "name": "entity", "type": "address" }, + { "internalType": "string", "name": "roleName", "type": "string" }, + { "internalType": "address", "name": "app", "type": "address" } + ], + "name": "AragonPermissionNotGranted", + "type": "error" + }, + { + "inputs": [ + { "internalType": "address", "name": "entity", "type": "address" }, + { "internalType": "string", "name": "roleName", "type": "string" }, + { "internalType": "address", "name": "app", "type": "address" } + ], + "name": "OZRoleNotGranted", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "entity", + "type": "address" + }, + { + "indexed": false, + "internalType": "string", + "name": "roleName", + "type": "string" + } + ], + "name": "RoleValidated", + "type": "event" + }, + { + "inputs": [], + "name": "ACL", + "outputs": [ + { "internalType": "contract IACL", "name": "", "type": "address" } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "ACL_ADDRESS", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "AGENT", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "EASYTRACK_ALLOWED_TOKENS_REGISTRY", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "LIDO", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "VOTING", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "WITHDRAWAL_QUEUE", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "dgAdminExecutor", + "type": "address" + }, + { + "internalType": "address", + "name": "dgResealManager", + "type": "address" + } + ], + "name": "validate", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/interfaces/TimeConstraints.json b/interfaces/TimeConstraints.json new file mode 100644 index 000000000..f46fb65db --- /dev/null +++ b/interfaces/TimeConstraints.json @@ -0,0 +1,69 @@ +[ + { + "inputs": [ + { + "internalType": "Duration", + "name": "currentDayTime", + "type": "uint32" + }, + { "internalType": "Duration", "name": "startDayTime", "type": "uint32" }, + { "internalType": "Duration", "name": "endDayTime", "type": "uint32" } + ], + "name": "DayTimeOutOfRange", + "type": "error" + }, + { "inputs": [], "name": "DayTimeOverflow", "type": "error" }, + { "inputs": [], "name": "DurationOverflow", "type": "error" }, + { + "inputs": [ + { "internalType": "Duration", "name": "startDayTime", "type": "uint32" }, + { "internalType": "Duration", "name": "endDayTime", "type": "uint32" } + ], + "name": "InvalidDayTimeRange", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "Timestamp", + "name": "requiredTimestamp", + "type": "uint40" + } + ], + "name": "TimestampNotReached", + "type": "error" + }, + { + "inputs": [], + "name": "DAY_DURATION", + "outputs": [{ "internalType": "Duration", "name": "", "type": "uint32" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "Timestamp", "name": "timestamp", "type": "uint40" } + ], + "name": "checkExecuteAfterTimestamp", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "Duration", "name": "startDayTime", "type": "uint32" }, + { "internalType": "Duration", "name": "endDayTime", "type": "uint32" } + ], + "name": "checkExecuteWithinDayTime", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getCurrentDayTime", + "outputs": [{ "internalType": "Duration", "name": "", "type": "uint32" }], + "stateMutability": "view", + "type": "function" + } +] diff --git a/interfaces/VotingContract.json b/interfaces/VotingContract.json new file mode 100644 index 000000000..95c9b0e67 --- /dev/null +++ b/interfaces/VotingContract.json @@ -0,0 +1,97 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "dualGovernance", + "type": "address" + }, + { + "internalType": "address", + "name": "adminExecutor", + "type": "address" + }, + { + "internalType": "address", + "name": "resealManager", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [], + "name": "DurationOverflow", + "type": "error" + }, + { + "inputs": [], + "name": "getEVMCallScript", + "outputs": [ + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getVoteItems", + "outputs": [ + { + "components": [ + { + "internalType": "string", + "name": "description", + "type": "string" + }, + { + "components": [ + { + "internalType": "address", + "name": "target", + "type": "address" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "internalType": "struct EvmScriptUtils.EvmScriptCall", + "name": "call", + "type": "tuple" + } + ], + "internalType": "struct Voting.VoteItem[]", + "name": "", + "type": "tuple[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "voteId", + "type": "uint256" + } + ], + "name": "validateVote", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + } +] \ No newline at end of file diff --git a/scripts/dual_governance_downgrade_holesky.py b/scripts/dual_governance_downgrade_holesky.py new file mode 100644 index 000000000..f5dac92ed --- /dev/null +++ b/scripts/dual_governance_downgrade_holesky.py @@ -0,0 +1,140 @@ +import time + +from typing import Dict + +from utils.agent import agent_forward +from utils.voting import bake_vote_items, confirm_vote_script, create_vote +from utils.ipfs import upload_vote_ipfs_description, calculate_vote_ipfs_description +from utils.config import ( + contracts, + get_deployer_account, + get_is_live, + get_priority_fee, +) +from utils.permissions import ( + encode_permission_set_manager, + encode_permission_revoke, + encode_permission_grant, +) +from utils.mainnet_fork import pass_and_exec_dao_vote + +description = "Holesky dual governance downgrade dry-run" + +DUAL_GOVERNANCE_ADMIN_EXECUTOR = "0x3Cc908B004422fd66FdB40Be062Bf9B0bd5BDbed" +RESEAL_MANAGER = "0x517C93bb27aD463FE3AD8f15DaFDAD56EC0bEeC3" + +def start_vote(tx_params: Dict[str, str], silent: bool = False): + vote_desc_items, call_script_items = zip( + ( + "Change permission manager for Lido STAKING_CONTROL_ROLE.", + agent_forward( + [ + encode_permission_set_manager(contracts.lido, "STAKING_CONTROL_ROLE", contracts.voting) + ] + ) + ), + ( + "Revoke permission for STAKING_CONTROL_ROLE from Agent contract.", + encode_permission_revoke( + target_app=contracts.lido, permission_name="STAKING_CONTROL_ROLE", revoke_from=contracts.agent + ), + ), + ( + "Grant permission for STAKING_CONTROL_ROLE to Voting contract.", + encode_permission_grant( + target_app=contracts.lido, permission_name="STAKING_CONTROL_ROLE", grant_to=contracts.voting + ), + ), + ( + "Revoke WithdrawalQueue PAUSE_ROLE from Reseal Manager.", + ( + agent_forward( + [ + ( + contracts.withdrawal_queue.address, + contracts.withdrawal_queue.revokeRole.encode_input( + contracts.withdrawal_queue.PAUSE_ROLE(), RESEAL_MANAGER + ), + ) + ] + ) + ), + ), + ( + "Revoke WithdrawalQueue RESUME_ROLE from Reseal Manager.", + ( + agent_forward( + [ + ( + contracts.withdrawal_queue.address, + contracts.withdrawal_queue.revokeRole.encode_input( + contracts.withdrawal_queue.RESUME_ROLE(), RESEAL_MANAGER + ), + ) + ] + ) + ), + ), + ( + "Grant AllowedTokensRegistry DEFAULT_ADMIN_ROLE to Agent.", + ( + contracts.allowed_tokens_registry.address, + contracts.allowed_tokens_registry.grantRole.encode_input( + contracts.allowed_tokens_registry.DEFAULT_ADMIN_ROLE(), contracts.agent + ), + ), + ), + ( + "Revoke AllowedTokensRegistry DEFAULT_ADMIN_ROLE from Voting.", + ( + contracts.allowed_tokens_registry.address, + contracts.allowed_tokens_registry.revokeRole.encode_input( + contracts.allowed_tokens_registry.DEFAULT_ADMIN_ROLE(), contracts.voting + ), + ), + ), + ( + "Revoke permission for RUN_SCRIPT_ROLE from DG Executor contract.", + encode_permission_revoke( + target_app=contracts.agent, + permission_name="RUN_SCRIPT_ROLE", + revoke_from=DUAL_GOVERNANCE_ADMIN_EXECUTOR, + ), + ), + ) + + vote_items = bake_vote_items(list(vote_desc_items), list(call_script_items)) + + if silent: + desc_ipfs = calculate_vote_ipfs_description(description) + else: + desc_ipfs = upload_vote_ipfs_description(description) + + return confirm_vote_script(vote_items, silent, desc_ipfs) and list( + create_vote(vote_items, tx_params, desc_ipfs=desc_ipfs) + ) + + +def main(): + tx_params: Dict[str, str] = {"from": get_deployer_account().address} + if get_is_live(): + tx_params["priority_fee"] = get_priority_fee() + + vote_id, _ = start_vote(tx_params=tx_params, silent=False) + + vote_id >= 0 and print(f"Vote created: {vote_id}.") + + time.sleep(5) # hack for waiting thread #2. + + +def start_and_execute_vote_on_fork(): + if get_is_live(): + raise Exception("This script is for local testing only.") + + tx_params = {"from": get_deployer_account()} + vote_id, _ = start_vote(tx_params=tx_params, silent=True) + + time.sleep(5) # hack for waiting thread #2. + + print(f"Vote created: {vote_id}.") + pass_and_exec_dao_vote(int(vote_id)) diff --git a/scripts/dual_governance_upgrade_holesky.py b/scripts/dual_governance_upgrade_holesky.py new file mode 100644 index 000000000..c4438599e --- /dev/null +++ b/scripts/dual_governance_upgrade_holesky.py @@ -0,0 +1,67 @@ +import time + +from typing import Dict +from brownie import interface +from utils.voting import bake_vote_items, confirm_vote_script, create_vote +from utils.ipfs import upload_vote_ipfs_description, calculate_vote_ipfs_description +from utils.config import ( + get_deployer_account, + get_is_live, + get_priority_fee, +) +from utils.mainnet_fork import pass_and_exec_dao_vote + +voting_contract = "0xD62837Cc18FB25791B9Cc51B1862bb8e06004204" +description = "Holesky dual governance upgrade dry-run" + +def get_vote_items(): + voting_items = interface.VotingContract(voting_contract).getVoteItems() + + vote_desc_items = [] + call_script_items = [] + + for desc, call_script in voting_items: + vote_desc_items.append(desc) + call_script_items.append((call_script[0], call_script[1].hex())) + + return vote_desc_items, call_script_items + +def start_vote(tx_params: Dict[str, str], silent: bool = False): + vote_desc_items, call_script_items = get_vote_items() + vote_items = bake_vote_items(list(vote_desc_items), list(call_script_items)) + + if silent: + desc_ipfs = calculate_vote_ipfs_description(description) + else: + desc_ipfs = upload_vote_ipfs_description(description) + + return confirm_vote_script(vote_items, silent, desc_ipfs) and list( + create_vote(vote_items, tx_params, desc_ipfs=desc_ipfs) + ) + + +def main(): + tx_params: Dict[str, str] = {"from": get_deployer_account().address} + if get_is_live(): + tx_params["priority_fee"] = get_priority_fee() + + vote_id, _ = start_vote(tx_params=tx_params, silent=False) + + assert interface.VotingContract(voting_contract).validateVote(vote_id) + + vote_id >= 0 and print(f"Vote created: {vote_id}.") + + time.sleep(5) # hack for waiting thread #2. + + +def start_and_execute_vote_on_fork(): + if get_is_live(): + raise Exception("This script is for local testing only.") + + tx_params = {"from": get_deployer_account()} + vote_id, _ = start_vote(tx_params=tx_params, silent=True) + + time.sleep(5) # hack for waiting thread #2. + + print(f"Vote created: {vote_id}.") + pass_and_exec_dao_vote(int(vote_id)) diff --git a/tests/dual_governance_downgrade_holesky_test.py b/tests/dual_governance_downgrade_holesky_test.py new file mode 100644 index 000000000..5b5998979 --- /dev/null +++ b/tests/dual_governance_downgrade_holesky_test.py @@ -0,0 +1,52 @@ +from scripts.dual_governance_downgrade_holesky import start_vote +from utils.config import contracts +from utils.test.tx_tracing_helpers import * +from brownie.network.transaction import TransactionReceipt +from utils.config import contracts +from brownie.network.account import Account + +DUAL_GOVERNANCE_ADMIN_EXECUTOR = "0x3Cc908B004422fd66FdB40Be062Bf9B0bd5BDbed" +RESEAL_MANAGER = "0x517C93bb27aD463FE3AD8f15DaFDAD56EC0bEeC3" + +def test_vote(helpers, accounts, ldo_holder, vote_ids_from_env, bypass_events_decoding, stranger: Account): + dao_voting = contracts.voting + + # Lido + assert contracts.acl.getPermissionManager(contracts.lido, contracts.lido.STAKING_CONTROL_ROLE()) == contracts.agent + assert not contracts.acl.hasPermission(contracts.voting, contracts.lido, contracts.lido.STAKING_CONTROL_ROLE()) + assert contracts.acl.hasPermission(contracts.agent, contracts.lido, contracts.lido.STAKING_CONTROL_ROLE()) + + # Reseal manager + assert contracts.withdrawal_queue.hasRole(contracts.withdrawal_queue.PAUSE_ROLE(), RESEAL_MANAGER) + assert contracts.withdrawal_queue.hasRole(contracts.withdrawal_queue.RESUME_ROLE(), RESEAL_MANAGER) + + # Allowed tokens registry + assert contracts.allowed_tokens_registry.hasRole(contracts.allowed_tokens_registry.DEFAULT_ADMIN_ROLE(), contracts.voting) + assert not contracts.allowed_tokens_registry.hasRole(contracts.allowed_tokens_registry.DEFAULT_ADMIN_ROLE(), contracts.agent) + + # Agent + assert contracts.acl.hasPermission(DUAL_GOVERNANCE_ADMIN_EXECUTOR, contracts.agent, contracts.agent.RUN_SCRIPT_ROLE()) + assert contracts.acl.hasPermission(contracts.voting, contracts.agent, contracts.agent.RUN_SCRIPT_ROLE()) + + # START VOTE + vote_id = vote_ids_from_env[0] if vote_ids_from_env else start_vote({"from": ldo_holder}, silent=True)[0] + + tx: TransactionReceipt = helpers.execute_vote( + vote_id=vote_id, accounts=accounts, dao_voting=dao_voting, skip_time=3 * 60 * 60 * 24 + ) + + # Lido + assert contracts.acl.getPermissionManager(contracts.lido, contracts.lido.STAKING_CONTROL_ROLE()) == contracts.voting + assert contracts.acl.hasPermission(contracts.voting, contracts.lido, contracts.lido.STAKING_CONTROL_ROLE()) + + # Reseal manager + assert not contracts.withdrawal_queue.hasRole(contracts.withdrawal_queue.PAUSE_ROLE(), RESEAL_MANAGER) + assert not contracts.withdrawal_queue.hasRole(contracts.withdrawal_queue.RESUME_ROLE(), RESEAL_MANAGER) + + # Allowed tokens registry + assert contracts.allowed_tokens_registry.hasRole(contracts.allowed_tokens_registry.DEFAULT_ADMIN_ROLE(), contracts.agent) + assert not contracts.allowed_tokens_registry.hasRole(contracts.allowed_tokens_registry.DEFAULT_ADMIN_ROLE(), contracts.voting) + + # Agent + assert not contracts.acl.hasPermission(DUAL_GOVERNANCE_ADMIN_EXECUTOR, contracts.agent, contracts.agent.RUN_SCRIPT_ROLE()) + assert contracts.acl.hasPermission(contracts.voting, contracts.agent, contracts.agent.RUN_SCRIPT_ROLE()) diff --git a/tests/dual_governance_upgrade_holesky_test.py b/tests/dual_governance_upgrade_holesky_test.py new file mode 100644 index 000000000..f131c702e --- /dev/null +++ b/tests/dual_governance_upgrade_holesky_test.py @@ -0,0 +1,68 @@ +from brownie import interface, chain +from scripts.dual_governance_upgrade_holesky import start_vote +from utils.config import contracts +from utils.test.tx_tracing_helpers import * +from brownie.network.transaction import TransactionReceipt +from utils.config import contracts +from brownie.network.account import Account + +DUAL_GOVERNANCE = "0xE29D4d0CAD66D87a054b5A93867C708000DaE1E6" +DUAL_GOVERNANCE_ADMIN_EXECUTOR = "0x3Cc908B004422fd66FdB40Be062Bf9B0bd5BDbed" +RESEAL_MANAGER = "0x517C93bb27aD463FE3AD8f15DaFDAD56EC0bEeC3" + +def test_vote(helpers, accounts, ldo_holder, vote_ids_from_env, stranger: Account): + dao_voting = contracts.voting + dual_governance = interface.DualGovernance(DUAL_GOVERNANCE) + timelock = interface.EmergencyProtectedTimelock(dual_governance.TIMELOCK()) + + # Lido + assert contracts.acl.getPermissionManager(contracts.lido, contracts.lido.STAKING_CONTROL_ROLE()) == contracts.voting + assert contracts.acl.hasPermission(contracts.voting, contracts.lido, contracts.lido.STAKING_CONTROL_ROLE()) + + # Allowed tokens registry + assert contracts.allowed_tokens_registry.hasRole(contracts.allowed_tokens_registry.DEFAULT_ADMIN_ROLE(), contracts.agent) + assert not contracts.allowed_tokens_registry.hasRole(contracts.allowed_tokens_registry.DEFAULT_ADMIN_ROLE(), contracts.voting) + + # Agent + assert not contracts.acl.hasPermission(DUAL_GOVERNANCE_ADMIN_EXECUTOR, contracts.agent, contracts.agent.RUN_SCRIPT_ROLE()) + assert contracts.acl.hasPermission(contracts.voting, contracts.agent, contracts.agent.RUN_SCRIPT_ROLE()) + + # Reseal manager + assert not contracts.withdrawal_queue.hasRole(contracts.withdrawal_queue.PAUSE_ROLE(), RESEAL_MANAGER) + assert not contracts.withdrawal_queue.hasRole(contracts.withdrawal_queue.RESUME_ROLE(), RESEAL_MANAGER) + + # START VOTE + vote_id = vote_ids_from_env[0] if vote_ids_from_env else start_vote({"from": ldo_holder}, silent=True)[0] + + tx: TransactionReceipt = helpers.execute_vote( + vote_id=vote_id, accounts=accounts, dao_voting=dao_voting, skip_time=3 * 60 * 60 * 24 + ) + + # Lido + assert contracts.acl.getPermissionManager(contracts.lido, contracts.lido.STAKING_CONTROL_ROLE()) == contracts.agent + assert not contracts.acl.hasPermission(contracts.voting, contracts.lido, contracts.lido.STAKING_CONTROL_ROLE()) + assert contracts.acl.hasPermission(contracts.agent, contracts.lido, contracts.lido.STAKING_CONTROL_ROLE()) + + # # Allowed tokens registry + assert contracts.allowed_tokens_registry.hasRole(contracts.allowed_tokens_registry.DEFAULT_ADMIN_ROLE(), contracts.voting) + assert not contracts.allowed_tokens_registry.hasRole(contracts.allowed_tokens_registry.DEFAULT_ADMIN_ROLE(), contracts.agent) + + # Agent + assert contracts.acl.hasPermission(DUAL_GOVERNANCE_ADMIN_EXECUTOR, contracts.agent, contracts.agent.RUN_SCRIPT_ROLE()) + assert contracts.acl.hasPermission(contracts.voting, contracts.agent, contracts.agent.RUN_SCRIPT_ROLE()) + + # Reseal manager + assert contracts.withdrawal_queue.hasRole(contracts.withdrawal_queue.PAUSE_ROLE(), RESEAL_MANAGER) + assert contracts.withdrawal_queue.hasRole(contracts.withdrawal_queue.RESUME_ROLE(), RESEAL_MANAGER) + + proposal_id = timelock.getProposalsCount() + + # while not contracts.dual_governance.canScheduleProposal(proposal_id): + chain.sleep(60 * 24) + + dual_governance.scheduleProposal(proposal_id, {"from": stranger}) + + # while not timelock.canExecute(proposal_id): + chain.sleep(60 * 24) + + timelock.execute(proposal_id, {"from": stranger}) diff --git a/utils/agent.py b/utils/agent.py index 4538e4b81..c7f803bcb 100644 --- a/utils/agent.py +++ b/utils/agent.py @@ -14,7 +14,6 @@ def agent_forward(call_script: Sequence[Tuple[str, str]]) -> Tuple[str, str]: return (AGENT, agent.forward.encode_input(encode_call_script(call_script))) - def agent_execute(target: str, value: str, data: str) -> Tuple[str, str]: agent = contracts.agent return (AGENT, agent.execute.encode_input(target, value, data)) diff --git a/utils/config.py b/utils/config.py index 767e86afc..28f991187 100644 --- a/utils/config.py +++ b/utils/config.py @@ -391,6 +391,10 @@ def trp_escrow_factory(self) -> interface.VestingEscrowFactory: def token_rate_notifier(self) -> interface.TokenRateNotifier: return interface.TokenRateNotifier(L1_TOKEN_RATE_NOTIFIER) + @property + def allowed_tokens_registry(self) -> interface.AllowedTokensRegistry: + return interface.AllowedTokensRegistry(EASYTRACK_ALLOWED_TOKENS_REGISTRY) + def __getattr__(name: str) -> Any: if name == "contracts": return ContractsLazyLoader() diff --git a/utils/evm_script.py b/utils/evm_script.py index 3267d1ca3..33ab12554 100644 --- a/utils/evm_script.py +++ b/utils/evm_script.py @@ -2,15 +2,16 @@ import os from collections import defaultdict from functools import lru_cache -from typing import List, Union, Optional, Callable +from typing import List, Union, Optional, Callable, Any import eth_abi +from brownie import Contract, convert from brownie.utils import color from eth_typing.evm import HexAddress -from eth_utils import keccak -from hexbytes import HexBytes from web3 import Web3 +# NOTE: The decode_function_call() method is currently unused; it is retained for fallback to the previous decoder version +# (refer to the NOTEs in the decode_evm_script method). from avotes_parser.core import parse_script, EncodedCall, Call, FuncInput, decode_function_call from avotes_parser.core.ABI import get_cached_combined @@ -22,7 +23,7 @@ ) EMPTY_CALLSCRIPT = "0x00000001" -ETHERSCAN_API_KEY = os.getenv("ETHERSCAN_API_KEY", "TGXU5WGVTVYRDDV2MY71R5JYB7147M13FC") +ETHERSCAN_TOKEN = os.getenv("ETHERSCAN_TOKEN", "TGXU5WGVTVYRDDV2MY71R5JYB7147M13FC") def create_executor_id(id) -> str: @@ -78,13 +79,18 @@ def decode_evm_script( logging.basicConfig(level=logging.INFO) return [repr(err)] - abi_storage = get_abi_cache(ETHERSCAN_API_KEY, specific_net) + # NOTE: The line below is not used in the current version; it is retained for fallback to the previous decoder version (see NOTE below). + abi_storage = get_abi_cache(ETHERSCAN_TOKEN, specific_net) calls = [] called_contracts = defaultdict(lambda: defaultdict(dict)) for ind, call in enumerate(parsed.calls): try: - call_info = decode_function_call(call.address, call.method_id, call.encoded_call_data, abi_storage) + call_info = decode_encoded_call(call) + + # NOTE: If the decode_encoded_call(call) method fails, uncomment the line below to fall back to the previous version: + # + # call_info = decode_function_call(call.address, call.method_id, call.encoded_call_data, abi_storage) if call_info is not None: for inp in filter(is_encoded_script, call_info.inputs): @@ -135,10 +141,76 @@ def calls_info_pretty_print(call: Union[str, Call, EncodedCall]) -> str: """Format printing for Call instance.""" return color.highlight(repr(call)) + def encode_error(error: str, values=None) -> str: - encoded_error = error.split('(')[0] + ': ' - args = '' + encoded_error = error.split("(")[0] + ": " + args = "" if values is not None: - args = ', '.join(str(x) for x in values) + args = ", ".join(str(x) for x in values) return f"{encoded_error}{args}" return encoded_error + + +def decode_encoded_call(encoded_call: EncodedCall) -> Optional[Call]: + """ + Decodes an encoded contract call using Brownie's Contract API. + + This function replaces AVotesParser.decode_function_call() and converts the provided + EncodedCall into a Call object or returns None if the decoding wasn't successfull. + Unsuccessfull deconding usually happens when the contract is not verified contract on etherscan + + Parameters: + encoded_call (EncodedCall): An object containing the target contract address, method id, + and encoded call data and encoded call data length. + + Returns: + Call: A Call object with decoded call details if successful, otherwise None if the method + call cannot be decoded. + """ + contract = Contract(encoded_call.address) + + # If the method selector is not found in the locally stored contracts, fetch the full ABI from Etherscan. + if encoded_call.method_id not in contract.selectors: + # For proxy contracts, Brownie automatically retrieves the implementation ABI. + contract = Contract.from_explorer(encoded_call.address) + + # If the method selector is still not found, the call may target the proxy contract directly rather than its implementation. + if encoded_call.method_id not in contract.selectors: + # Explicitly fetch the ABI for the proxy contract itself by setting `as_proxy_for` to the proxy's address. + # NOTE: Normalization via `convert.to_address()` is required; without it, the internal check in `from_explorer()` may fail, + # resulting in the implementation's ABI being downloaded instead. + contract = Contract.from_explorer(encoded_call.address, as_proxy_for=convert.to_address(encoded_call.address)) + + # If the method selector is still not found, the contract is likely not verified. + if encoded_call.method_id not in contract.selectors: + return None + + method_name = contract.selectors[encoded_call.method_id] + contract_method = getattr(contract, method_name) + + method_abi = contract_method.abi + + calldata_with_selector = encoded_call.method_id + encoded_call.encoded_call_data[2:] + decoded_calldata = contract_method.decode_input(calldata_with_selector) + + inputs = [get_func_input(method_abi["inputs"][idx], arg) for idx, arg in enumerate(decoded_calldata)] + + properties = { + "constant": "unknown", # Typically False even for pure methods, but not guaranteed. + "payable": method_abi["stateMutability"] == "payable", + "stateMutability": method_abi["stateMutability"], + "type": "function", + } + + return Call( + contract.address, + encoded_call.method_id, + method_name, + inputs, + properties, + method_abi["outputs"], + ) + + +def get_func_input(input_abi: dict, value: Any) -> FuncInput: + return FuncInput(input_abi["name"], input_abi.get("internalType", input_abi.get("type")), value) diff --git a/utils/mainnet_fork.py b/utils/mainnet_fork.py index 3c03ee6ac..7db63b5dc 100644 --- a/utils/mainnet_fork.py +++ b/utils/mainnet_fork.py @@ -1,7 +1,7 @@ from contextlib import contextmanager from brownie import chain, accounts, interface -from utils.config import VOTING +from utils.config import VOTING, network_name @contextmanager @@ -27,12 +27,16 @@ def pass_and_exec_dao_vote(vote_id): if not dao_voting.canExecute(vote_id): print(f"Passing vote {vote_id}") - # together these accounts hold 15% of LDO total supply - ldo_holders = [ - "0x3e40d73eb977dc6a537af587d48316fee66e9c8c", - "0xb8d83908aab38a159f3da47a59d84db8e1838712", - "0xa2dfc431297aee387c05beef507e5335e684fbcd", - ] + + if network_name() in ("holesky", "holesky-fork"): + ldo_holders = ["0xc807d4036B400dE8f6cD2aDbd8d9cf9a3a01CC30"] + else: + # together these accounts hold 15% of LDO total supply + ldo_holders = [ + "0x3e40d73eb977dc6a537af587d48316fee66e9c8c", + "0xb8d83908aab38a159f3da47a59d84db8e1838712", + "0xa2dfc431297aee387c05beef507e5335e684fbcd", + ] for holder_addr in ldo_holders: print(f" voting from {holder_addr}") diff --git a/utils/permissions.py b/utils/permissions.py index 4089f05ed..c6a5d1856 100644 --- a/utils/permissions.py +++ b/utils/permissions.py @@ -24,6 +24,18 @@ def encode_permission_revoke(target_app, permission_name, revoke_from) -> Tuple[ return acl.address, acl.revokePermission.encode_input(revoke_from, target_app, permission_id) +def encode_permission_set_manager(target_app, permission_name: str, grant_to: str) -> Tuple[str, str]: + acl = contracts.acl + permission_id = convert.to_uint(Web3.keccak(text=permission_name)) + return acl.address, acl.setPermissionManager.encode_input(grant_to, target_app, permission_id) + + +def encode_permission_create(target_app, permission_name: str, grant_to, manager) -> Tuple[str, str]: + acl = contracts.acl + permission_id = convert.to_uint(Web3.keccak(text=permission_name)) + return acl.address, acl.createPermission.encode_input(grant_to, target_app, permission_id, manager) + + def encode_permission_grant_p( target_app, permission_name: str,