Source Code
Overview
ETH Balance
0 ETH
More Info
ContractCreator
Multichain Info
N/A
Loading...
Loading
Contract Source Code Verified (Exact Match)
Contract Name:
TimelockedGovernance
Compiler Version
v0.8.26+commit.8a97fa7a
Optimization Enabled:
Yes with 200 runs
Other Settings:
cancun EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-FileCopyrightText: 2024 Lido <[email protected]> // SPDX-License-Identifier: MIT pragma solidity 0.8.26; import {ITimelock} from "./interfaces/ITimelock.sol"; import {IGovernance} from "./interfaces/IGovernance.sol"; import {ExternalCall} from "./libraries/ExternalCalls.sol"; /// @title Timelocked Governance /// @notice A contract that serves as the interface for submitting and scheduling the execution of governance proposals. contract TimelockedGovernance is IGovernance { // --- // Errors // --- error CallerIsNotGovernance(address caller); error InvalidGovernance(address governance); error InvalidTimelock(ITimelock timelock); // --- // Immutable Variables // --- address public immutable GOVERNANCE; ITimelock public immutable TIMELOCK; // --- // Main Functionality // --- /// @notice Initializes the TimelockedGovernance contract. /// @param governance The address of the governance contract. /// @param timelock The address of the timelock contract. constructor(address governance, ITimelock timelock) { if (governance == address(0)) { revert InvalidGovernance(governance); } if (address(timelock) == address(0)) { revert InvalidTimelock(timelock); } GOVERNANCE = governance; TIMELOCK = timelock; } /// @notice Submits a proposal to the timelock. /// @param calls An array of ExternalCall structs representing the calls to be executed in the proposal. /// @param metadata A string containing additional information about the proposal. /// @return proposalId The id of the submitted proposal. function submitProposal( ExternalCall[] calldata calls, string calldata metadata ) external returns (uint256 proposalId) { _checkCallerIsGovernance(); proposalId = TIMELOCK.submit(TIMELOCK.getAdminExecutor(), calls); emit ProposalSubmitted(msg.sender, proposalId, metadata); } /// @notice Schedules a submitted proposal. /// @param proposalId The id of the proposal to be scheduled. function scheduleProposal(uint256 proposalId) external { TIMELOCK.schedule(proposalId); } /// @notice Checks if a proposal can be scheduled. /// @param proposalId The id of the proposal to check. /// @return A boolean indicating whether the proposal can be scheduled. function canScheduleProposal(uint256 proposalId) external view returns (bool) { return TIMELOCK.canSchedule(proposalId); } /// @notice Cancels all pending proposals that have not been executed. /// @return A boolean indicating whether the operation was successful. function cancelAllPendingProposals() external returns (bool) { _checkCallerIsGovernance(); TIMELOCK.cancelAllNonExecutedProposals(); return true; } // --- // Internal Methods // --- /// @notice Checks if the msg.sender is the governance address. function _checkCallerIsGovernance() internal view { if (msg.sender != GOVERNANCE) { revert CallerIsNotGovernance(msg.sender); } } }
// SPDX-FileCopyrightText: 2024 Lido <[email protected]> // SPDX-License-Identifier: MIT pragma solidity 0.8.26; import {Duration} from "../types/Duration.sol"; import {Timestamp} from "../types/Timestamp.sol"; import {ExternalCall} from "../libraries/ExternalCalls.sol"; import {Status as ProposalStatus} from "../libraries/ExecutableProposals.sol"; interface ITimelock { struct ProposalDetails { uint256 id; address executor; Timestamp submittedAt; Timestamp scheduledAt; ProposalStatus status; } function submit(address executor, ExternalCall[] calldata calls) external returns (uint256 newProposalId); function schedule(uint256 proposalId) external; function execute(uint256 proposalId) external; function cancelAllNonExecutedProposals() external; function canSchedule(uint256 proposalId) external view returns (bool); function canExecute(uint256 proposalId) external view returns (bool); function getAdminExecutor() external view returns (address); function setAdminExecutor(address newAdminExecutor) external; function getGovernance() external view returns (address); function setGovernance(address newGovernance) external; function getProposal(uint256 proposalId) external view returns (ProposalDetails memory proposalDetails, ExternalCall[] memory calls); function getProposalDetails(uint256 proposalId) external view returns (ProposalDetails memory proposalDetails); function getProposalCalls(uint256 proposalId) external view returns (ExternalCall[] memory calls); function getProposalsCount() external view returns (uint256 count); function getAfterSubmitDelay() external view returns (Duration); function getAfterScheduleDelay() external view returns (Duration); function setAfterSubmitDelay(Duration newAfterSubmitDelay) external; function setAfterScheduleDelay(Duration newAfterScheduleDelay) external; function transferExecutorOwnership(address executor, address owner) external; }
// SPDX-FileCopyrightText: 2024 Lido <[email protected]> // SPDX-License-Identifier: MIT pragma solidity 0.8.26; import {ITimelock} from "./ITimelock.sol"; import {ExternalCall} from "../libraries/ExternalCalls.sol"; interface IGovernance { event ProposalSubmitted(address indexed proposerAccount, uint256 indexed proposalId, string metadata); function TIMELOCK() external view returns (ITimelock); function submitProposal( ExternalCall[] calldata calls, string calldata metadata ) external returns (uint256 proposalId); function scheduleProposal(uint256 proposalId) external; function cancelAllPendingProposals() external returns (bool); function canScheduleProposal(uint256 proposalId) external view returns (bool); }
// SPDX-FileCopyrightText: 2024 Lido <[email protected]> // SPDX-License-Identifier: MIT pragma solidity 0.8.26; import {IExternalExecutor} from "../interfaces/IExternalExecutor.sol"; /// @notice Represents an external call to a specific address with an optional ETH transfer. /// @param target The address to call. /// @param value The amount of ETH (in wei) to transfer with the call, capped at approximately 7.9 billion ETH. /// @param payload The calldata payload sent to the target address. struct ExternalCall { address target; uint96 value; bytes payload; } /// @title External Calls Library /// @notice Provides functionality for executing multiple external calls through an `IExternalExecutor` contract. library ExternalCalls { /// @notice Executes a series of external calls using the provided executor, which implements the /// `IExternalExecutor` interface. /// @param calls An array of `ExternalCall` structs, each specifying a call to be executed. /// @param executor The contract responsible for executing each call, conforming to the `IExternalExecutor` interface. function execute(IExternalExecutor executor, ExternalCall[] memory calls) internal { uint256 callsCount = calls.length; for (uint256 i = 0; i < callsCount; ++i) { executor.execute(calls[i].target, calls[i].value, calls[i].payload); } } }
// SPDX-FileCopyrightText: 2024 Lido <[email protected]> // SPDX-License-Identifier: MIT pragma solidity 0.8.26; import {Timestamp, Timestamps} from "./Timestamp.sol"; // --- // Type Definition // --- type Duration is uint32; // --- // Assign Global Operations // --- using {lt as <, lte as <=, eq as ==, neq as !=, gte as >=, gt as >} for Duration global; using {addTo, plusSeconds, minusSeconds, multipliedBy, dividedBy, toSeconds} for Duration global; using {plus as +, minus as -} for Duration global; // --- // Errors // --- error DivisionByZero(); error DurationOverflow(); error DurationUnderflow(); // --- // Constants // --- /// @dev The maximum possible duration is approximately 136 years (assuming 365 days per year). uint32 constant MAX_DURATION_VALUE = type(uint32).max; // --- // Comparison Operations // --- function lt(Duration d1, Duration d2) pure returns (bool) { return Duration.unwrap(d1) < Duration.unwrap(d2); } function lte(Duration d1, Duration d2) pure returns (bool) { return Duration.unwrap(d1) <= Duration.unwrap(d2); } function eq(Duration d1, Duration d2) pure returns (bool) { return Duration.unwrap(d1) == Duration.unwrap(d2); } function neq(Duration d1, Duration d2) pure returns (bool) { return Duration.unwrap(d1) != Duration.unwrap(d2); } function gte(Duration d1, Duration d2) pure returns (bool) { return Duration.unwrap(d1) >= Duration.unwrap(d2); } function gt(Duration d1, Duration d2) pure returns (bool) { return Duration.unwrap(d1) > Duration.unwrap(d2); } // --- // Conversion Operations // --- function toSeconds(Duration d) pure returns (uint256) { return Duration.unwrap(d); } // --- // Arithmetic Operations // --- function plus(Duration d1, Duration d2) pure returns (Duration) { unchecked { /// @dev Both `d1.toSeconds()` and `d2.toSeconds()` are <= type(uint32).max. Therefore, their /// sum is <= type(uint256).max. return Durations.from(d1.toSeconds() + d2.toSeconds()); } } function minus(Duration d1, Duration d2) pure returns (Duration) { uint256 d1Seconds = d1.toSeconds(); uint256 d2Seconds = d2.toSeconds(); if (d1Seconds < d2Seconds) { revert DurationUnderflow(); } unchecked { /// @dev Subtraction is safe because `d1Seconds` >= `d2Seconds`. /// Both `d1Seconds` and `d2Seconds` <= `type(uint32).max`, so the difference fits within `uint32`. return Duration.wrap(uint32(d1Seconds - d2Seconds)); } } // --- // Custom Operations // --- function plusSeconds(Duration d, uint256 secondsToAdd) pure returns (Duration) { return Durations.from(d.toSeconds() + secondsToAdd); } function minusSeconds(Duration d, uint256 secondsToSubtract) pure returns (Duration) { uint256 durationSeconds = d.toSeconds(); if (durationSeconds < secondsToSubtract) { revert DurationUnderflow(); } unchecked { /// @dev Subtraction is safe because `durationSeconds` >= `secondsToSubtract`. /// Both `durationSeconds` and `secondsToSubtract` <= `type(uint32).max`, /// so the difference fits within `uint32`. return Duration.wrap(uint32(durationSeconds - secondsToSubtract)); } } function dividedBy(Duration d, uint256 divisor) pure returns (Duration) { if (divisor == 0) { revert DivisionByZero(); } return Duration.wrap(uint32(d.toSeconds() / divisor)); } function multipliedBy(Duration d, uint256 multiplicand) pure returns (Duration) { return Durations.from(multiplicand * d.toSeconds()); } function addTo(Duration d, Timestamp t) pure returns (Timestamp) { unchecked { /// @dev Both `t.toSeconds()` <= `type(uint40).max` and `d.toSeconds()` <= `type(uint32).max`, so their /// sum fits within `uint256`. return Timestamps.from(t.toSeconds() + d.toSeconds()); } } // --- // Namespaced Helper Methods // --- library Durations { Duration internal constant ZERO = Duration.wrap(0); function from(uint256 durationInSeconds) internal pure returns (Duration res) { if (durationInSeconds > MAX_DURATION_VALUE) { revert DurationOverflow(); } /// @dev Casting `durationInSeconds` to `uint32` is safe as the check ensures it is less than or equal /// to `MAX_DURATION_VALUE`, which fits within the `uint32`. res = Duration.wrap(uint32(durationInSeconds)); } }
// SPDX-FileCopyrightText: 2024 Lido <[email protected]> // SPDX-License-Identifier: MIT pragma solidity 0.8.26; // --- // Type Definition // --- type Timestamp is uint40; // --- // Assign Global Operations // --- using {lt as <, lte as <=, eq as ==, neq as !=, gte as >=, gt as >} for Timestamp global; using {isZero, isNotZero, toSeconds} for Timestamp global; // --- // Errors // --- error TimestampOverflow(); // --- // Constants // --- /// @dev The maximum value for a `Timestamp`, corresponding to approximately the year 36812. uint40 constant MAX_TIMESTAMP_VALUE = type(uint40).max; // --- // Comparison Operations // --- function lt(Timestamp t1, Timestamp t2) pure returns (bool) { return Timestamp.unwrap(t1) < Timestamp.unwrap(t2); } function lte(Timestamp t1, Timestamp t2) pure returns (bool) { return Timestamp.unwrap(t1) <= Timestamp.unwrap(t2); } function eq(Timestamp t1, Timestamp t2) pure returns (bool) { return Timestamp.unwrap(t1) == Timestamp.unwrap(t2); } function neq(Timestamp t1, Timestamp t2) pure returns (bool) { return Timestamp.unwrap(t1) != Timestamp.unwrap(t2); } function gte(Timestamp t1, Timestamp t2) pure returns (bool) { return Timestamp.unwrap(t1) >= Timestamp.unwrap(t2); } function gt(Timestamp t1, Timestamp t2) pure returns (bool) { return Timestamp.unwrap(t1) > Timestamp.unwrap(t2); } // --- // Conversion Operations // --- function toSeconds(Timestamp t) pure returns (uint256) { return Timestamp.unwrap(t); } // --- // Custom Operations // --- function isZero(Timestamp t) pure returns (bool) { return Timestamp.unwrap(t) == 0; } function isNotZero(Timestamp t) pure returns (bool) { return Timestamp.unwrap(t) > 0; } // --- // Namespaced Helper Methods // --- library Timestamps { Timestamp internal constant ZERO = Timestamp.wrap(0); function from(uint256 timestampInSeconds) internal pure returns (Timestamp res) { if (timestampInSeconds > MAX_TIMESTAMP_VALUE) { revert TimestampOverflow(); } /// @dev Casting `timestampInSeconds` to `uint40` is safe as the check ensures it is less than or equal /// to `MAX_TIMESTAMP_VALUE`, which fits within the `uint40`. return Timestamp.wrap(uint40(timestampInSeconds)); } function now() internal view returns (Timestamp res) { /// @dev Skipping the check that `block.timestamp` <= `MAX_TIMESTAMP_VALUE` for gas efficiency. /// Overflow is possible only after approximately 34,000 years from the Unix epoch. res = Timestamp.wrap(uint40(block.timestamp)); } function max(Timestamp t1, Timestamp t2) internal pure returns (Timestamp) { return t1 > t2 ? t1 : t2; } }
// SPDX-FileCopyrightText: 2024 Lido <[email protected]> // SPDX-License-Identifier: MIT pragma solidity 0.8.26; import {Duration} from "../types/Duration.sol"; import {Timestamp, Timestamps} from "../types/Timestamp.sol"; import {ITimelock} from "../interfaces/ITimelock.sol"; import {ExternalCall, ExternalCalls, IExternalExecutor} from "./ExternalCalls.sol"; /// @notice Describes the lifecycle state of a proposal, defining its current status. /// @param NotExist Proposal has not been submitted yet. /// @param Submitted Proposal has been successfully submitted but not scheduled yet. This state is /// only reachable from NotExist. /// @param Scheduled Proposal has been successfully scheduled after submission. This state is only /// reachable from Submitted. /// @param Executed Proposal has been successfully executed after being scheduled. This state is /// only reachable from Scheduled and is the final state of the proposal. /// @param Cancelled Proposal was cancelled before execution. Cancelled proposals cannot be scheduled /// or executed. This state is only reachable from Submitted or Scheduled and is the final state /// of the proposal. /// @dev A proposal is considered cancelled if it was not executed and its id is less than /// the id of the last submitted proposal at the time the `cancelAll()` method was called. /// To check if a proposal is in the `Cancelled` state, use the `_isProposalCancelled()` /// view function. enum Status { NotExist, Submitted, Scheduled, Executed, Cancelled } /// @title Executable Proposals Library /// @notice Manages a collection of proposals with associated external calls stored as Proposal struct. /// Proposals are uniquely identified by sequential ids, starting from one. library ExecutableProposals { // --- // Data Types // --- /// @notice Efficiently stores proposal data within a single EVM slot. /// @param status The current status of the proposal. See `Status` for details. /// @param executor The address of the associated executor used for executing the proposal's calls. /// @param submittedAt The timestamp when the proposal was submitted. /// @param scheduledAt The timestamp when the proposal was scheduled for execution. /// Equals zero if the proposal hasn't been scheduled yet. struct ProposalData { /// @dev slot 0: [0..7] Status status; /// @dev slot 0: [8..167] address executor; /// @dev slot 0: [168..207] Timestamp submittedAt; /// @dev slot 0: [208..247] Timestamp scheduledAt; } /// @notice A struct representing a proposal data with associated external calls. /// @param data Proposal data packed into a struct for efficient loading into memory. /// @param calls List of external calls associated with the proposal struct Proposal { ProposalData data; ExternalCall[] calls; } /// @notice The context for the library, storing relevant proposals data. /// @param proposalsCount The total number of proposals submitted so far. /// @param lastCancelledProposalId The id of the most recently cancelled proposal. /// @param proposals A mapping of proposal ids to their corresponding `Proposal` data. struct Context { uint64 proposalsCount; uint64 lastCancelledProposalId; mapping(uint256 proposalId => Proposal) proposals; } // --- // Errors // --- error EmptyCalls(); error UnexpectedProposalStatus(uint256 proposalId, Status status); error AfterSubmitDelayNotPassed(uint256 proposalId); error AfterScheduleDelayNotPassed(uint256 proposalId); error MinExecutionDelayNotPassed(uint256 proposalId); // --- // Events // --- event ProposalSubmitted(uint256 indexed id, address indexed executor, ExternalCall[] calls); event ProposalScheduled(uint256 indexed id); event ProposalExecuted(uint256 indexed id); event ProposalsCancelledTill(uint256 proposalId); // --- // Proposal lifecycle // --- /// @notice Submits a new proposal with the specified executor and external calls. /// @param self The context of the Executable Proposal library. /// @param executor The address authorized to execute the proposal. /// @param calls The list of external calls to include in the proposal. /// @return newProposalId The id of the newly submitted proposal. function submit( Context storage self, address executor, ExternalCall[] memory calls ) internal returns (uint256 newProposalId) { if (calls.length == 0) { revert EmptyCalls(); } /// @dev: proposal ids are one-based. The first item has id = 1 newProposalId = ++self.proposalsCount; Proposal storage newProposal = self.proposals[newProposalId]; newProposal.data.executor = executor; newProposal.data.status = Status.Submitted; newProposal.data.submittedAt = Timestamps.now(); uint256 callsCount = calls.length; for (uint256 i = 0; i < callsCount; ++i) { newProposal.calls.push(calls[i]); } emit ProposalSubmitted(newProposalId, executor, calls); } /// @notice Marks a previously submitted proposal as scheduled for execution if the required delay period /// has passed since submission and the proposal was not cancelled. /// @param self The context of the Executable Proposal library. /// @param proposalId The id of the proposal to schedule. /// @param afterSubmitDelay The required delay duration after submission before the proposal can be scheduled. /// function schedule(Context storage self, uint256 proposalId, Duration afterSubmitDelay) internal { ProposalData memory proposalData = self.proposals[proposalId].data; _checkProposalNotCancelled(self, proposalId, proposalData); if (proposalData.status != Status.Submitted) { revert UnexpectedProposalStatus(proposalId, proposalData.status); } if (afterSubmitDelay.addTo(proposalData.submittedAt) > Timestamps.now()) { revert AfterSubmitDelayNotPassed(proposalId); } proposalData.status = Status.Scheduled; proposalData.scheduledAt = Timestamps.now(); self.proposals[proposalId].data = proposalData; emit ProposalScheduled(proposalId); } /// @notice Marks a previously scheduled proposal as executed and runs the associated external calls if the /// required delay period has passed since scheduling and the proposal has not been cancelled. /// @param self The context of the Executable Proposal library. /// @param proposalId The id of the proposal to execute. /// @param afterScheduleDelay The minimum delay required after scheduling before execution is allowed. /// @param minExecutionDelay The minimum time that must elapse after submission before execution is allowed. function execute( Context storage self, uint256 proposalId, Duration afterScheduleDelay, Duration minExecutionDelay ) internal { Proposal memory proposal = self.proposals[proposalId]; _checkProposalNotCancelled(self, proposalId, proposal.data); if (proposal.data.status != Status.Scheduled) { revert UnexpectedProposalStatus(proposalId, proposal.data.status); } if (afterScheduleDelay.addTo(proposal.data.scheduledAt) > Timestamps.now()) { revert AfterScheduleDelayNotPassed(proposalId); } if (minExecutionDelay.addTo(proposal.data.submittedAt) > Timestamps.now()) { revert MinExecutionDelayNotPassed(proposalId); } self.proposals[proposalId].data.status = Status.Executed; ExternalCalls.execute(IExternalExecutor(proposal.data.executor), proposal.calls); emit ProposalExecuted(proposalId); } /// @notice Marks all non-executed proposals up to the most recently submitted as cancelled, preventing their execution. /// @param self The context of the Executable Proposal library. function cancelAll(Context storage self) internal { uint64 lastCancelledProposalId = self.proposalsCount; self.lastCancelledProposalId = lastCancelledProposalId; emit ProposalsCancelledTill(lastCancelledProposalId); } // --- // Getters // --- /// @notice Determines whether a proposal is eligible to be scheduled based on its status and required delay. /// @param self The context of the Executable Proposal library. /// @param proposalId The id of the proposal to check for scheduling eligibility. /// @param afterSubmitDelay The minimum delay required after submission before the proposal can be scheduled. /// @return bool `true` if the proposal is eligible for scheduling, otherwise `false`. function canSchedule( Context storage self, uint256 proposalId, Duration afterSubmitDelay ) internal view returns (bool) { ProposalData memory proposalData = self.proposals[proposalId].data; return proposalId > self.lastCancelledProposalId && proposalData.status == Status.Submitted && Timestamps.now() >= afterSubmitDelay.addTo(proposalData.submittedAt); } /// @notice Determines whether a proposal is eligible for execution based on its status and delays requirements. /// @param self The context of the Executable Proposal library. /// @param proposalId The id of the proposal to check for execution eligibility. /// @param afterScheduleDelay The required delay duration after scheduling before the proposal can be executed. /// @param minExecutionDelay The required minimum delay after submission before execution is allowed. /// @return bool `true` if the proposal is eligible for execution, otherwise `false`. function canExecute( Context storage self, uint256 proposalId, Duration afterScheduleDelay, Duration minExecutionDelay ) internal view returns (bool) { Timestamp currentTime = Timestamps.now(); ProposalData memory proposalData = self.proposals[proposalId].data; return proposalId > self.lastCancelledProposalId && proposalData.status == Status.Scheduled && currentTime >= afterScheduleDelay.addTo(proposalData.scheduledAt) && currentTime >= minExecutionDelay.addTo(proposalData.submittedAt); } /// @notice Returns the total count of submitted proposals. /// @param self The context of the Executable Proposal library. /// @return uint256 The number of submitted proposal function getProposalsCount(Context storage self) internal view returns (uint256) { return self.proposalsCount; } /// @notice Retrieves detailed information about a specific previously submitted proposal. /// @param self The context of the Executable Proposal library. /// @param proposalId The id of the proposal to retrieve details for. /// @return proposalDetails A struct containing the proposal’s id, executor, submission timestamp, /// scheduling timestamp and status, if applicable function getProposalDetails( Context storage self, uint256 proposalId ) internal view returns (ITimelock.ProposalDetails memory proposalDetails) { ProposalData memory proposalData = self.proposals[proposalId].data; _checkProposalExists(proposalId, proposalData); proposalDetails.id = proposalId; proposalDetails.status = _isProposalCancelled(self, proposalId, proposalData) ? Status.Cancelled : proposalData.status; proposalDetails.executor = address(proposalData.executor); proposalDetails.submittedAt = proposalData.submittedAt; proposalDetails.scheduledAt = proposalData.scheduledAt; } /// @notice Retrieves the list of external calls associated with a specific previously submitted proposal. /// @param self The storage context for managing proposals within the Executable Proposal library. /// @param proposalId The id of the proposal to retrieve calls for. /// @return calls An array containing all external calls associated with the specified proposal function getProposalCalls( Context storage self, uint256 proposalId ) internal view returns (ExternalCall[] memory calls) { Proposal memory proposal = self.proposals[proposalId]; _checkProposalExists(proposalId, proposal.data); calls = proposal.calls; } // --- // Private methods // --- function _checkProposalExists(uint256 proposalId, ProposalData memory proposalData) private pure { if (proposalData.status == Status.NotExist) { revert UnexpectedProposalStatus(proposalId, Status.NotExist); } } function _checkProposalNotCancelled( Context storage self, uint256 proposalId, ProposalData memory proposalData ) private view { if (_isProposalCancelled(self, proposalId, proposalData)) { revert UnexpectedProposalStatus(proposalId, Status.Cancelled); } } function _isProposalCancelled( Context storage self, uint256 proposalId, ProposalData memory proposalData ) private view returns (bool) { return proposalId <= self.lastCancelledProposalId && proposalData.status != Status.Executed; } }
// SPDX-FileCopyrightText: 2024 Lido <[email protected]> // SPDX-License-Identifier: MIT pragma solidity 0.8.26; interface IExternalExecutor { function execute(address target, uint256 value, bytes calldata payload) external payable; }
{ "remappings": [ "@openzeppelin/=lib/openzeppelin-contracts/", "ds-test/=lib/openzeppelin-contracts/lib/forge-std/lib/ds-test/src/", "erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/", "eth-gas-reporter/=node_modules/eth-gas-reporter/", "forge-std/=lib/forge-std/src/", "hardhat/=node_modules/hardhat/", "kontrol-cheatcodes/=lib/kontrol-cheatcodes/src/", "openzeppelin-contracts/=lib/openzeppelin-contracts/" ], "optimizer": { "enabled": true, "runs": 200 }, "metadata": { "useLiteralContent": false, "bytecodeHash": "ipfs", "appendCBOR": true }, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } }, "evmVersion": "cancun", "viaIR": false, "libraries": {} }
Contract ABI
API[{"inputs":[{"internalType":"address","name":"governance","type":"address"},{"internalType":"contract ITimelock","name":"timelock","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"address","name":"caller","type":"address"}],"name":"CallerIsNotGovernance","type":"error"},{"inputs":[{"internalType":"address","name":"governance","type":"address"}],"name":"InvalidGovernance","type":"error"},{"inputs":[{"internalType":"contract ITimelock","name":"timelock","type":"address"}],"name":"InvalidTimelock","type":"error"},{"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"},{"inputs":[],"name":"GOVERNANCE","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"TIMELOCK","outputs":[{"internalType":"contract ITimelock","name":"","type":"address"}],"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":"cancelAllPendingProposals","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"proposalId","type":"uint256"}],"name":"scheduleProposal","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"}]
Contract Creation Code
60c060405234801561000f575f80fd5b5060405161089e38038061089e83398101604081905261002e916100c5565b6001600160a01b03821661006557604051633a56e4b960e01b81526001600160a01b03831660048201526024015b60405180910390fd5b6001600160a01b038116610097576040516310f6994d60e11b81526001600160a01b038216600482015260240161005c565b6001600160a01b039182166080521660a0526100fd565b6001600160a01b03811681146100c2575f80fd5b50565b5f80604083850312156100d6575f80fd5b82516100e1816100ae565b60208401519092506100f2816100ae565b809150509250929050565b60805160a0516107586101465f395f818160e60152818161013b0152818161016a015281816102a10152818161032a01526103a401525f81816069015261042601526107585ff3fe608060405234801561000f575f80fd5b5060043610610060575f3560e01c8063146278341461006457806353e51f8b146100a85780636c246870146100c95780637aadef8b146100e1578063df5aa6b114610108578063e52b17f61461011d575b5f80fd5b61008b7f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b0390911681526020015b60405180910390f35b6100bb6100b63660046104b1565b610130565b60405190815260200161009f565b6100d1610296565b604051901515815260200161009f565b61008b7f000000000000000000000000000000000000000000000000000000000000000081565b61011b61011636600461054c565b610314565b005b6100d161012b36600461054c565b61038c565b5f61013961041b565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166389bc65cf7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316636608e2e26040518163ffffffff1660e01b8152600401602060405180830381865afa1580156101c4573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906101e8919061057a565b87876040518463ffffffff1660e01b8152600401610208939291906105c4565b6020604051808303815f875af1158015610224573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061024891906106d1565b905080336001600160a01b03167f232ce03ddb9384ef5aeb6333cad16b1c7e68e1977e0e6e5a3666e934569a15fc85856040516102869291906106e8565b60405180910390a3949350505050565b5f61029f61041b565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663021937306040518163ffffffff1660e01b81526004015f604051808303815f87803b1580156102f7575f80fd5b505af1158015610309573d5f803e3d5ffd5b505050506001905090565b604051636fbfd40960e01b8152600481018290527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031690636fbfd409906024015f604051808303815f87803b158015610373575f80fd5b505af1158015610385573d5f803e3d5ffd5b5050505050565b604051635f1ff5af60e01b8152600481018290525f907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031690635f1ff5af90602401602060405180830381865afa1580156103f1573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906104159190610703565b92915050565b336001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000161461046a576040516382a9ceaf60e01b815233600482015260240160405180910390fd5b565b5f8083601f84011261047c575f80fd5b50813567ffffffffffffffff811115610493575f80fd5b6020830191508360208285010111156104aa575f80fd5b9250929050565b5f805f80604085870312156104c4575f80fd5b843567ffffffffffffffff8111156104da575f80fd5b8501601f810187136104ea575f80fd5b803567ffffffffffffffff811115610500575f80fd5b8760208260051b8401011115610514575f80fd5b60209182019550935085013567ffffffffffffffff811115610534575f80fd5b6105408782880161046c565b95989497509550505050565b5f6020828403121561055c575f80fd5b5035919050565b6001600160a01b0381168114610577575f80fd5b50565b5f6020828403121561058a575f80fd5b815161059581610563565b9392505050565b81835281816020850137505f828201602090810191909152601f909101601f19169091010190565b6001600160a01b038416815260406020820181905281018290525f6060600584901b830181019083018583605e1936839003015b878210156106c357868503605f190184528235818112610616575f80fd5b8901803561062381610563565b6001600160a01b0316865260208101356bffffffffffffffffffffffff811680821461064d575f80fd5b602088015250604081013536829003601e1901811261066a575f80fd5b0160208101903567ffffffffffffffff811115610685575f80fd5b803603821315610693575f80fd5b606060408801526106a860608801828461059c565b965050506020830192506020840193506001820191506105f8565b509298975050505050505050565b5f602082840312156106e1575f80fd5b5051919050565b602081525f6106fb60208301848661059c565b949350505050565b5f60208284031215610713575f80fd5b81518015158114610595575f80fdfea2646970667358221220de0c15afd212968dbf3ff787f12ef82c8df86dc76223c9f1244b0370a8eac98364736f6c634300081a003300000000000000000000000049b3512c44891bef83f8967d075121bd1b07a01b0000000000000000000000000a5e22782c0bd4addf10d771f0bf0406b038282d
Deployed Bytecode
0x608060405234801561000f575f80fd5b5060043610610060575f3560e01c8063146278341461006457806353e51f8b146100a85780636c246870146100c95780637aadef8b146100e1578063df5aa6b114610108578063e52b17f61461011d575b5f80fd5b61008b7f00000000000000000000000049b3512c44891bef83f8967d075121bd1b07a01b81565b6040516001600160a01b0390911681526020015b60405180910390f35b6100bb6100b63660046104b1565b610130565b60405190815260200161009f565b6100d1610296565b604051901515815260200161009f565b61008b7f0000000000000000000000000a5e22782c0bd4addf10d771f0bf0406b038282d81565b61011b61011636600461054c565b610314565b005b6100d161012b36600461054c565b61038c565b5f61013961041b565b7f0000000000000000000000000a5e22782c0bd4addf10d771f0bf0406b038282d6001600160a01b03166389bc65cf7f0000000000000000000000000a5e22782c0bd4addf10d771f0bf0406b038282d6001600160a01b0316636608e2e26040518163ffffffff1660e01b8152600401602060405180830381865afa1580156101c4573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906101e8919061057a565b87876040518463ffffffff1660e01b8152600401610208939291906105c4565b6020604051808303815f875af1158015610224573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061024891906106d1565b905080336001600160a01b03167f232ce03ddb9384ef5aeb6333cad16b1c7e68e1977e0e6e5a3666e934569a15fc85856040516102869291906106e8565b60405180910390a3949350505050565b5f61029f61041b565b7f0000000000000000000000000a5e22782c0bd4addf10d771f0bf0406b038282d6001600160a01b031663021937306040518163ffffffff1660e01b81526004015f604051808303815f87803b1580156102f7575f80fd5b505af1158015610309573d5f803e3d5ffd5b505050506001905090565b604051636fbfd40960e01b8152600481018290527f0000000000000000000000000a5e22782c0bd4addf10d771f0bf0406b038282d6001600160a01b031690636fbfd409906024015f604051808303815f87803b158015610373575f80fd5b505af1158015610385573d5f803e3d5ffd5b5050505050565b604051635f1ff5af60e01b8152600481018290525f907f0000000000000000000000000a5e22782c0bd4addf10d771f0bf0406b038282d6001600160a01b031690635f1ff5af90602401602060405180830381865afa1580156103f1573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906104159190610703565b92915050565b336001600160a01b037f00000000000000000000000049b3512c44891bef83f8967d075121bd1b07a01b161461046a576040516382a9ceaf60e01b815233600482015260240160405180910390fd5b565b5f8083601f84011261047c575f80fd5b50813567ffffffffffffffff811115610493575f80fd5b6020830191508360208285010111156104aa575f80fd5b9250929050565b5f805f80604085870312156104c4575f80fd5b843567ffffffffffffffff8111156104da575f80fd5b8501601f810187136104ea575f80fd5b803567ffffffffffffffff811115610500575f80fd5b8760208260051b8401011115610514575f80fd5b60209182019550935085013567ffffffffffffffff811115610534575f80fd5b6105408782880161046c565b95989497509550505050565b5f6020828403121561055c575f80fd5b5035919050565b6001600160a01b0381168114610577575f80fd5b50565b5f6020828403121561058a575f80fd5b815161059581610563565b9392505050565b81835281816020850137505f828201602090810191909152601f909101601f19169091010190565b6001600160a01b038416815260406020820181905281018290525f6060600584901b830181019083018583605e1936839003015b878210156106c357868503605f190184528235818112610616575f80fd5b8901803561062381610563565b6001600160a01b0316865260208101356bffffffffffffffffffffffff811680821461064d575f80fd5b602088015250604081013536829003601e1901811261066a575f80fd5b0160208101903567ffffffffffffffff811115610685575f80fd5b803603821315610693575f80fd5b606060408801526106a860608801828461059c565b965050506020830192506020840193506001820191506105f8565b509298975050505050505050565b5f602082840312156106e1575f80fd5b5051919050565b602081525f6106fb60208301848661059c565b949350505050565b5f60208284031215610713575f80fd5b81518015158114610595575f80fdfea2646970667358221220de0c15afd212968dbf3ff787f12ef82c8df86dc76223c9f1244b0370a8eac98364736f6c634300081a0033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
00000000000000000000000049b3512c44891bef83f8967d075121bd1b07a01b0000000000000000000000000a5e22782c0bd4addf10d771f0bf0406b038282d
-----Decoded View---------------
Arg [0] : governance (address): 0x49B3512c44891bef83F8967d075121Bd1b07a01B
Arg [1] : timelock (address): 0x0A5E22782C0Bd4AddF10D771f0bF0406B038282d
-----Encoded View---------------
2 Constructor Arguments found :
Arg [0] : 00000000000000000000000049b3512c44891bef83f8967d075121bd1b07a01b
Arg [1] : 0000000000000000000000000a5e22782c0bd4addf10d771f0bf0406b038282d
Loading...
Loading
Loading...
Loading
Loading...
Loading
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.