Source Code
Overview
ETH Balance
0 ETH
More Info
ContractCreator
Multichain Info
N/A
Loading...
Loading
Contract Source Code Verified (Exact Match)
Contract Name:
ResealManager
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 {Address} from "@openzeppelin/contracts/utils/Address.sol"; import {ISealable} from "./interfaces/ISealable.sol"; import {ITimelock} from "./interfaces/ITimelock.sol"; import {IResealManager} from "./interfaces/IResealManager.sol"; /// @title Reseal Manager /// @notice Allows to extend pause of temporarily paused contracts to permanent pause or resume it. contract ResealManager is IResealManager { // --- // Errors // --- error SealableWrongPauseState(); error CallerIsNotGovernance(address caller); // --- // Immutables & Constants // --- uint256 public constant PAUSE_INFINITELY = type(uint256).max; ITimelock public immutable EMERGENCY_PROTECTED_TIMELOCK; // --- // Constructor // --- /// @notice Initializes the ResealManager contract. /// @param emergencyProtectedTimelock The address of the Timelock contract. constructor(ITimelock emergencyProtectedTimelock) { EMERGENCY_PROTECTED_TIMELOCK = emergencyProtectedTimelock; } // --- // Main Functionality // --- /// @notice Extends the pause of the specified sealable contract. /// @dev Works only if conditions are met: /// - ResealManager has PAUSE_ROLE and RESUME_ROLE for target contract; /// - Contract are paused until timestamp after current timestamp and not for infinite time; /// - Function is called by the governance contract. /// @param sealable The address of the sealable contract. function reseal(address sealable) external { _checkCallerIsGovernance(); uint256 sealableResumeSinceTimestamp = ISealable(sealable).getResumeSinceTimestamp(); if (block.timestamp >= sealableResumeSinceTimestamp || sealableResumeSinceTimestamp == PAUSE_INFINITELY) { revert SealableWrongPauseState(); } Address.functionCall(sealable, abi.encodeWithSelector(ISealable.resume.selector)); Address.functionCall(sealable, abi.encodeWithSelector(ISealable.pauseFor.selector, PAUSE_INFINITELY)); } /// @notice Resumes the specified sealable contract if it is paused. /// @dev Works only if conditions are met: /// - ResealManager has RESUME_ROLE for target contract; /// - Contract are paused until timestamp after current timestamp; /// - Function is called by the governance contract. /// @param sealable The address of the sealable contract. function resume(address sealable) external { _checkCallerIsGovernance(); uint256 sealableResumeSinceTimestamp = ISealable(sealable).getResumeSinceTimestamp(); if (block.timestamp >= sealableResumeSinceTimestamp) { revert SealableWrongPauseState(); } Address.functionCall(sealable, abi.encodeWithSelector(ISealable.resume.selector)); } // --- // Internal methods // --- /// @notice Ensures that the function can only be called by the governance address. function _checkCallerIsGovernance() internal view { address governance = EMERGENCY_PROTECTED_TIMELOCK.getGovernance(); if (msg.sender != governance) { revert CallerIsNotGovernance(msg.sender); } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (utils/Address.sol) pragma solidity ^0.8.20; /** * @dev Collection of functions related to the address type */ library Address { /** * @dev The ETH balance of the account is not enough to perform the operation. */ error AddressInsufficientBalance(address account); /** * @dev There's no code at `target` (it is not a contract). */ error AddressEmptyCode(address target); /** * @dev A call to an address target failed. The target may have reverted. */ error FailedInnerCall(); /** * @dev Replacement for Solidity's `transfer`: sends `amount` wei to * `recipient`, forwarding all available gas and reverting on errors. * * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost * of certain opcodes, possibly making contracts go over the 2300 gas limit * imposed by `transfer`, making them unable to receive funds via * `transfer`. {sendValue} removes this limitation. * * https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more]. * * IMPORTANT: because control is transferred to `recipient`, care must be * taken to not create reentrancy vulnerabilities. Consider using * {ReentrancyGuard} or the * https://solidity.readthedocs.io/en/v0.8.20/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. */ function sendValue(address payable recipient, uint256 amount) internal { if (address(this).balance < amount) { revert AddressInsufficientBalance(address(this)); } (bool success, ) = recipient.call{value: amount}(""); if (!success) { revert FailedInnerCall(); } } /** * @dev Performs a Solidity function call using a low level `call`. A * plain `call` is an unsafe replacement for a function call: use this * function instead. * * If `target` reverts with a revert reason or custom error, it is bubbled * up by this function (like regular Solidity function calls). However, if * the call reverted with no returned reason, this function reverts with a * {FailedInnerCall} error. * * Returns the raw returned data. To convert to the expected return value, * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. * * Requirements: * * - `target` must be a contract. * - calling `target` with `data` must not revert. */ function functionCall(address target, bytes memory data) internal returns (bytes memory) { return functionCallWithValue(target, data, 0); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but also transferring `value` wei to `target`. * * Requirements: * * - the calling contract must have an ETH balance of at least `value`. * - the called Solidity function must be `payable`. */ function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) { if (address(this).balance < value) { revert AddressInsufficientBalance(address(this)); } (bool success, bytes memory returndata) = target.call{value: value}(data); return verifyCallResultFromTarget(target, success, returndata); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a static call. */ function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { (bool success, bytes memory returndata) = target.staticcall(data); return verifyCallResultFromTarget(target, success, returndata); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a delegate call. */ function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { (bool success, bytes memory returndata) = target.delegatecall(data); return verifyCallResultFromTarget(target, success, returndata); } /** * @dev Tool to verify that a low level call to smart-contract was successful, and reverts if the target * was not a contract or bubbling up the revert reason (falling back to {FailedInnerCall}) in case of an * unsuccessful call. */ function verifyCallResultFromTarget( address target, bool success, bytes memory returndata ) internal view returns (bytes memory) { if (!success) { _revert(returndata); } else { // only check if target is a contract if the call was successful and the return data is empty // otherwise we already know that it was a contract if (returndata.length == 0 && target.code.length == 0) { revert AddressEmptyCode(target); } return returndata; } } /** * @dev Tool to verify that a low level call was successful, and reverts if it wasn't, either by bubbling the * revert reason or with a default {FailedInnerCall} error. */ function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) { if (!success) { _revert(returndata); } else { return returndata; } } /** * @dev Reverts with returndata if present. Otherwise reverts with {FailedInnerCall}. */ function _revert(bytes memory returndata) private pure { // Look for revert reason and bubble it up if present if (returndata.length > 0) { // The easiest way to bubble the revert reason is using memory via assembly /// @solidity memory-safe-assembly assembly { let returndata_size := mload(returndata) revert(add(32, returndata), returndata_size) } } else { revert FailedInnerCall(); } } }
// SPDX-FileCopyrightText: 2024 Lido <[email protected]> // SPDX-License-Identifier: MIT pragma solidity 0.8.26; interface ISealable { function resume() external; function pauseFor(uint256 duration) external; function getResumeSinceTimestamp() external view returns (uint256); }
// 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; interface IResealManager { function resume(address sealable) external; function reseal(address sealable) external; }
// 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 {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 {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":"contract ITimelock","name":"emergencyProtectedTimelock","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":[{"internalType":"address","name":"caller","type":"address"}],"name":"CallerIsNotGovernance","type":"error"},{"inputs":[],"name":"FailedInnerCall","type":"error"},{"inputs":[],"name":"SealableWrongPauseState","type":"error"},{"inputs":[],"name":"EMERGENCY_PROTECTED_TIMELOCK","outputs":[{"internalType":"contract ITimelock","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PAUSE_INFINITELY","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"sealable","type":"address"}],"name":"reseal","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"sealable","type":"address"}],"name":"resume","outputs":[],"stateMutability":"nonpayable","type":"function"}]
Contract Creation Code
60a0604052348015600e575f80fd5b506040516105c03803806105c0833981016040819052602b91603b565b6001600160a01b03166080526066565b5f60208284031215604a575f80fd5b81516001600160a01b0381168114605f575f80fd5b9392505050565b60805161053c6100845f395f8181609601526102aa015261053c5ff3fe608060405234801561000f575f80fd5b506004361061004a575f3560e01c80631e4894b91461004e578063793c194614610063578063a302ee3814610076578063aa1e0dd614610091575b5f80fd5b61006161005c3660046104a3565b6100d0565b005b6100616100713660046104a3565b6101e8565b61007e5f1981565b6040519081526020015b60405180910390f35b6100b87f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b039091168152602001610088565b6100d86102a7565b5f816001600160a01b031663589ff76c6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610115573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061013991906104be565b9050804210158061014a57505f1981145b1561016857604051636aab9d5360e11b815260040160405180910390fd5b6040805160048152602481019091526020810180516001600160e01b0316630237bed160e11b17905261019c90839061035d565b50604080515f196024808301919091528251808303909101815260449091019091526020810180516001600160e01b031663f3f449c760e01b1790526101e390839061035d565b505050565b6101f06102a7565b5f816001600160a01b031663589ff76c6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561022d573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061025191906104be565b905080421061027357604051636aab9d5360e11b815260040160405180910390fd5b6040805160048152602481019091526020810180516001600160e01b0316630237bed160e11b1790526101e390839061035d565b5f7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663289b3c0d6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610304573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061032891906104d5565b9050336001600160a01b0382161461035a576040516382a9ceaf60e01b81523360048201526024015b60405180910390fd5b50565b606061036a83835f610371565b9392505050565b6060814710156103965760405163cd78605960e01b8152306004820152602401610351565b5f80856001600160a01b031684866040516103b191906104f0565b5f6040518083038185875af1925050503d805f81146103eb576040519150601f19603f3d011682016040523d82523d5f602084013e6103f0565b606091505b509150915061040086838361040a565b9695505050505050565b60608261041f5761041a82610466565b61036a565b815115801561043657506001600160a01b0384163b155b1561045f57604051639996b31560e01b81526001600160a01b0385166004820152602401610351565b508061036a565b8051156104765780518082602001fd5b604051630a12f52160e11b815260040160405180910390fd5b6001600160a01b038116811461035a575f80fd5b5f602082840312156104b3575f80fd5b813561036a8161048f565b5f602082840312156104ce575f80fd5b5051919050565b5f602082840312156104e5575f80fd5b815161036a8161048f565b5f82518060208501845e5f92019182525091905056fea26469706673582212209fd22d65a62658d0083be4fbefd7fdc297a1a79df0471c7be97ec000b136751864736f6c634300081a00330000000000000000000000000a5e22782c0bd4addf10d771f0bf0406b038282d
Deployed Bytecode
0x608060405234801561000f575f80fd5b506004361061004a575f3560e01c80631e4894b91461004e578063793c194614610063578063a302ee3814610076578063aa1e0dd614610091575b5f80fd5b61006161005c3660046104a3565b6100d0565b005b6100616100713660046104a3565b6101e8565b61007e5f1981565b6040519081526020015b60405180910390f35b6100b87f0000000000000000000000000a5e22782c0bd4addf10d771f0bf0406b038282d81565b6040516001600160a01b039091168152602001610088565b6100d86102a7565b5f816001600160a01b031663589ff76c6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610115573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061013991906104be565b9050804210158061014a57505f1981145b1561016857604051636aab9d5360e11b815260040160405180910390fd5b6040805160048152602481019091526020810180516001600160e01b0316630237bed160e11b17905261019c90839061035d565b50604080515f196024808301919091528251808303909101815260449091019091526020810180516001600160e01b031663f3f449c760e01b1790526101e390839061035d565b505050565b6101f06102a7565b5f816001600160a01b031663589ff76c6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561022d573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061025191906104be565b905080421061027357604051636aab9d5360e11b815260040160405180910390fd5b6040805160048152602481019091526020810180516001600160e01b0316630237bed160e11b1790526101e390839061035d565b5f7f0000000000000000000000000a5e22782c0bd4addf10d771f0bf0406b038282d6001600160a01b031663289b3c0d6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610304573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061032891906104d5565b9050336001600160a01b0382161461035a576040516382a9ceaf60e01b81523360048201526024015b60405180910390fd5b50565b606061036a83835f610371565b9392505050565b6060814710156103965760405163cd78605960e01b8152306004820152602401610351565b5f80856001600160a01b031684866040516103b191906104f0565b5f6040518083038185875af1925050503d805f81146103eb576040519150601f19603f3d011682016040523d82523d5f602084013e6103f0565b606091505b509150915061040086838361040a565b9695505050505050565b60608261041f5761041a82610466565b61036a565b815115801561043657506001600160a01b0384163b155b1561045f57604051639996b31560e01b81526001600160a01b0385166004820152602401610351565b508061036a565b8051156104765780518082602001fd5b604051630a12f52160e11b815260040160405180910390fd5b6001600160a01b038116811461035a575f80fd5b5f602082840312156104b3575f80fd5b813561036a8161048f565b5f602082840312156104ce575f80fd5b5051919050565b5f602082840312156104e5575f80fd5b815161036a8161048f565b5f82518060208501845e5f92019182525091905056fea26469706673582212209fd22d65a62658d0083be4fbefd7fdc297a1a79df0471c7be97ec000b136751864736f6c634300081a0033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
0000000000000000000000000a5e22782c0bd4addf10d771f0bf0406b038282d
-----Decoded View---------------
Arg [0] : emergencyProtectedTimelock (address): 0x0A5E22782C0Bd4AddF10D771f0bF0406B038282d
-----Encoded View---------------
1 Constructor Arguments found :
Arg [0] : 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.