diff --git a/packages/contracts-bedrock/interfaces/safe/ILivenessModule2.sol b/packages/contracts-bedrock/interfaces/safe/ILivenessModule2.sol index 921d1a794c7..6a40033d7ea 100644 --- a/packages/contracts-bedrock/interfaces/safe/ILivenessModule2.sol +++ b/packages/contracts-bedrock/interfaces/safe/ILivenessModule2.sol @@ -1,6 +1,8 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; +interface ISafe { } + /// @title ILivenessModule2 /// @notice Interface for LivenessModule2, a singleton module for challenge-based ownership transfer interface ILivenessModule2 { @@ -10,18 +12,34 @@ interface ILivenessModule2 { address fallbackOwner; } + event ChallengeCancelled(address indexed safe); + event ChallengeStarted(address indexed safe, uint256 challengeStartTime); + event ChallengeSucceeded(address indexed safe, address fallbackOwner); + event ModuleCleared(address indexed safe); + event ModuleConfigured(address indexed safe, uint256 livenessResponsePeriod, address fallbackOwner); + + error LivenessModule2_ChallengeAlreadyExists(); + error LivenessModule2_ChallengeDoesNotExist(); + error LivenessModule2_InvalidFallbackOwner(); + error LivenessModule2_InvalidResponsePeriod(); + error LivenessModule2_InvalidVersion(); + error LivenessModule2_ModuleNotConfigured(); + error LivenessModule2_ModuleNotEnabled(); + error LivenessModule2_ModuleStillEnabled(); + error LivenessModule2_OwnershipTransferFailed(); + error LivenessModule2_ResponsePeriodActive(); + error LivenessModule2_ResponsePeriodEnded(); + error LivenessModule2_UnauthorizedCaller(); + error SemverComp_InvalidSemverParts(); + /// @notice Returns the configuration for a Safe - /// @return livenessResponsePeriod The response period - /// @return fallbackOwner The fallback owner address - function livenessSafeConfiguration(address) external view returns (uint256 livenessResponsePeriod, address fallbackOwner); + /// @param _safe The Safe to query + /// @return The ModuleConfig for the Safe + function livenessSafeConfiguration(ISafe _safe) external view returns (ModuleConfig memory); /// @notice Returns the challenge start time for a Safe (0 if no challenge) /// @return The challenge start timestamp - function challengeStartTime(address) external view returns (uint256); - - /// @notice Semantic version - /// @return version The contract version - function version() external view returns (string memory); + function challengeStartTime(ISafe) external view returns (uint256); /// @notice Configures the module for a Safe that has already enabled it /// @param _config The configuration parameters for the module @@ -33,16 +51,16 @@ interface ILivenessModule2 { /// @notice Returns challenge_start_time + liveness_response_period if there is a challenge, or 0 if not /// @param _safe The Safe address to query /// @return The challenge end timestamp, or 0 if no challenge - function getChallengePeriodEnd(address _safe) external view returns (uint256); + function getChallengePeriodEnd(ISafe _safe) external view returns (uint256); /// @notice Challenges an enabled safe /// @param _safe The Safe to challenge - function challenge(address _safe) external; + function challenge(ISafe _safe) external; /// @notice Responds to a challenge for an enabled safe, canceling it function respond() external; /// @notice Removes all current owners from an enabled safe and appoints fallback as sole owner /// @param _safe The Safe to transfer ownership of - function changeOwnershipToFallback(address _safe) external; + function changeOwnershipToFallback(ISafe _safe) external; } diff --git a/packages/contracts-bedrock/interfaces/safe/ISaferSafes.sol b/packages/contracts-bedrock/interfaces/safe/ISaferSafes.sol index 7fc50d05eda..27044452cf3 100644 --- a/packages/contracts-bedrock/interfaces/safe/ISaferSafes.sol +++ b/packages/contracts-bedrock/interfaces/safe/ISaferSafes.sol @@ -1,68 +1,28 @@ // SPDX-License-Identifier: MIT -pragma solidity 0.8.15; +pragma solidity ^0.8.0; -import {GnosisSafe} from "safe-contracts/GnosisSafe.sol"; -import {Enum} from "safe-contracts/common/Enum.sol"; -import {ISemver} from "interfaces/universal/ISemver.sol"; +import { ITimelockGuard, IEnum, ISafe } from "interfaces/safe/ITimelockGuard.sol"; +import { ILivenessModule2 } from "interfaces/safe/ILivenessModule2.sol"; +import { ISemver } from "interfaces/universal/ISemver.sol"; interface ISaferSafes is ISemver { - struct ModuleConfig { - uint256 livenessResponsePeriod; - address fallbackOwner; - } - - struct ExecTransactionParams { - address to; - uint256 value; - bytes data; - Enum.Operation operation; - uint256 safeTxGas; - uint256 baseGas; - uint256 gasPrice; - address gasToken; - address payable refundReceiver; - } - - enum TransactionState { - PENDING, - CANCELLED, - EXECUTED - } - - struct ScheduledTransaction { - uint256 executionTime; - TransactionState state; - ExecTransactionParams params; - } - - event CancellationThresholdUpdated( - GnosisSafe indexed safe, - uint256 oldThreshold, - uint256 newThreshold - ); + event CancellationThresholdUpdated(ISafe indexed safe, uint256 oldThreshold, uint256 newThreshold); event ChallengeCancelled(address indexed safe); event ChallengeStarted(address indexed safe, uint256 challengeStartTime); event ChallengeSucceeded(address indexed safe, address fallbackOwner); - event GuardConfigured(GnosisSafe indexed safe, uint256 timelockDelay); + event GuardConfigured(ISafe indexed safe, uint256 timelockDelay); event Message(string message); event ModuleCleared(address indexed safe); - event ModuleConfigured( - address indexed safe, - uint256 livenessResponsePeriod, - address fallbackOwner - ); - event TransactionCancelled(GnosisSafe indexed safe, bytes32 indexed txHash); - event TransactionExecuted(GnosisSafe indexed safe, bytes32 txHash); - event TransactionScheduled( - GnosisSafe indexed safe, - bytes32 indexed txHash, - uint256 executionTime - ); + event ModuleConfigured(address indexed safe, uint256 livenessResponsePeriod, address fallbackOwner); + event TransactionCancelled(ISafe indexed safe, bytes32 indexed txHash); + event TransactionExecuted(ISafe indexed safe, bytes32 indexed txHash); + event TransactionScheduled(ISafe indexed safe, bytes32 indexed txHash, uint256 executionTime); error LivenessModule2_ChallengeAlreadyExists(); error LivenessModule2_ChallengeDoesNotExist(); error LivenessModule2_InvalidFallbackOwner(); error LivenessModule2_InvalidResponsePeriod(); + error LivenessModule2_InvalidVersion(); error LivenessModule2_ModuleNotConfigured(); error LivenessModule2_ModuleNotEnabled(); error LivenessModule2_ModuleStillEnabled(); @@ -74,8 +34,10 @@ interface ISaferSafes is ISemver { error SemverComp_InvalidSemverParts(); error TimelockGuard_GuardNotConfigured(); error TimelockGuard_GuardNotEnabled(); + error TimelockGuard_GuardStillEnabled(); error TimelockGuard_InvalidTimelockDelay(); error TimelockGuard_InvalidVersion(); + error TimelockGuard_NotOwner(); error TimelockGuard_TransactionAlreadyCancelled(); error TimelockGuard_TransactionAlreadyExecuted(); error TimelockGuard_TransactionAlreadyScheduled(); @@ -83,21 +45,20 @@ interface ISaferSafes is ISemver { error TimelockGuard_TransactionNotScheduled(); function cancelTransaction( - GnosisSafe _safe, + ISafe _safe, bytes32 _txHash, uint256 _nonce, bytes calldata _signatures - ) external; + ) + external; - function cancellationThreshold( - GnosisSafe _safe - ) external view returns (uint256); + function cancellationThreshold(ISafe _safe) external view returns (uint256); - function challenge(address _safe) external; + function challenge(ISafe _safe) external; - function challengeStartTime(address _safe) external view returns (uint256); + function challengeStartTime(ISafe) external view returns (uint256); - function changeOwnershipToFallback(address _safe) external; + function changeOwnershipToFallback(ISafe _safe) external; function checkAfterExecution(bytes32 _txHash, bool _success) external; @@ -105,60 +66,56 @@ interface ISaferSafes is ISemver { address _to, uint256 _value, bytes calldata _data, - Enum.Operation _operation, + IEnum.Operation _operation, uint256 _safeTxGas, uint256 _baseGas, uint256 _gasPrice, address _gasToken, address payable _refundReceiver, bytes calldata, - address - ) external view; + address _msgSender + ) + external; function clearLivenessModule() external; - function configureLivenessModule(ModuleConfig calldata _config) external; + function clearTimelockGuard() external; + + function configureLivenessModule(ILivenessModule2.ModuleConfig calldata _config) external; function configureTimelockGuard(uint256 _timelockDelay) external; - function getChallengePeriodEnd( - address _safe - ) external view returns (uint256); + function getChallengePeriodEnd(ISafe _safe) external view returns (uint256); - function livenessSafeConfiguration( - address _safe - ) - external - view - returns (uint256 livenessResponsePeriod, address fallbackOwner); + function livenessSafeConfiguration(ISafe _safe) external view returns (ILivenessModule2.ModuleConfig memory); - function maxCancellationThreshold( - GnosisSafe _safe - ) external view returns (uint256); + function maxCancellationThreshold(ISafe _safe) external view returns (uint256); - function pendingTransactions( - GnosisSafe _safe - ) external view returns (ScheduledTransaction[] memory); + function pendingTransactions(ISafe _safe) external view returns (ITimelockGuard.ScheduledTransaction[] memory); function respond() external; function scheduleTransaction( - GnosisSafe _safe, + ISafe _safe, uint256 _nonce, - ExecTransactionParams calldata _params, + ITimelockGuard.ExecTransactionParams calldata _params, bytes calldata _signatures - ) external; + ) + external; function scheduledTransaction( - GnosisSafe _safe, + ISafe _safe, bytes32 _txHash - ) external view returns (ScheduledTransaction memory); + ) + external + view + returns (ITimelockGuard.ScheduledTransaction memory); + + function signCancellation(bytes32) external; - function signCancellation(bytes32 _txHash) external; + function supportsInterface(bytes4 interfaceId) external view returns (bool); - function timelockConfiguration( - GnosisSafe _safe - ) external view returns (uint256); + function timelockDelay(ISafe _safe) external view returns (uint256); function version() external pure returns (string memory); } diff --git a/packages/contracts-bedrock/interfaces/safe/ITimelockGuard.sol b/packages/contracts-bedrock/interfaces/safe/ITimelockGuard.sol index 6ab35526a74..86871be9ecc 100644 --- a/packages/contracts-bedrock/interfaces/safe/ITimelockGuard.sol +++ b/packages/contracts-bedrock/interfaces/safe/ITimelockGuard.sol @@ -1,8 +1,13 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.4; +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; -library Enum { - type Operation is uint8; +interface ISafe { } + +interface IEnum { + enum Operation { + Call, + DelegateCall + } } interface ITimelockGuard { @@ -25,7 +30,7 @@ interface ITimelockGuard { address to; uint256 value; bytes data; - Enum.Operation operation; + IEnum.Operation operation; uint256 safeTxGas; uint256 baseGas; uint256 gasPrice; @@ -37,58 +42,60 @@ interface ITimelockGuard { error TimelockGuard_GuardNotEnabled(); error TimelockGuard_GuardStillEnabled(); error TimelockGuard_InvalidTimelockDelay(); + error TimelockGuard_NotOwner(); error TimelockGuard_TransactionAlreadyCancelled(); error TimelockGuard_TransactionAlreadyScheduled(); error TimelockGuard_TransactionNotScheduled(); error TimelockGuard_TransactionNotReady(); error TimelockGuard_TransactionAlreadyExecuted(); error TimelockGuard_InvalidVersion(); + error SemverComp_InvalidSemverParts(); - event CancellationThresholdUpdated(address indexed safe, uint256 oldThreshold, uint256 newThreshold); - event GuardConfigured(address indexed safe, uint256 timelockDelay); - event TransactionCancelled(address indexed safe, bytes32 indexed txHash); - event TransactionScheduled(address indexed safe, bytes32 indexed txHash, uint256 executionTime); - event TransactionExecuted(address indexed safe, bytes32 indexed txHash); + event CancellationThresholdUpdated(ISafe indexed safe, uint256 oldThreshold, uint256 newThreshold); + event GuardConfigured(ISafe indexed safe, uint256 timelockDelay); + event TransactionCancelled(ISafe indexed safe, bytes32 indexed txHash); + event TransactionScheduled(ISafe indexed safe, bytes32 indexed txHash, uint256 executionTime); + event TransactionExecuted(ISafe indexed safe, bytes32 indexed txHash); event Message(string message); - event TransactionsNotCancelled(address indexed safe, uint256 uncancelledCount); - function cancelTransaction(address _safe, bytes32 _txHash, uint256 _nonce, bytes memory _signatures) external; - function signCancellation(bytes32 _txHash) external; - function cancellationThreshold(address _safe) external view returns (uint256); + function cancelTransaction(ISafe _safe, bytes32 _txHash, uint256 _nonce, bytes memory _signatures) external; + function signCancellation(bytes32) external; + function cancellationThreshold(ISafe _safe) external view returns (uint256); + function supportsInterface(bytes4 interfaceId) external view returns (bool); function checkTransaction( address _to, uint256 _value, bytes memory _data, - Enum.Operation _operation, + IEnum.Operation _operation, uint256 _safeTxGas, uint256 _baseGas, uint256 _gasPrice, address _gasToken, address payable _refundReceiver, - bytes memory _signatures, + bytes memory, address _msgSender ) external; - function checkAfterExecution(bytes32, bool) external; + function checkAfterExecution(bytes32 _txHash, bool _success) external; function configureTimelockGuard(uint256 _timelockDelay) external; + function clearTimelockGuard() external; function scheduledTransaction( - address _safe, + ISafe _safe, bytes32 _txHash ) external view returns (ScheduledTransaction memory); - function safeConfigs(address) external view returns (uint256 timelockDelay); function scheduleTransaction( - address _safe, + ISafe _safe, uint256 _nonce, ExecTransactionParams memory _params, bytes memory _signatures ) external; - function timelockConfiguration(address _safe) external view returns (uint256 timelockDelay); - function maxCancellationThreshold(address _safe) external view returns (uint256); - function pendingTransactions(address _safe) + function timelockDelay(ISafe _safe) external view returns (uint256); + function maxCancellationThreshold(ISafe _safe) external view returns (uint256); + function pendingTransactions(ISafe _safe) external view returns (ScheduledTransaction[] memory); diff --git a/packages/contracts-bedrock/scripts/deploy/DeploySaferSafes.s.sol b/packages/contracts-bedrock/scripts/deploy/DeploySaferSafes.s.sol new file mode 100644 index 00000000000..3c411baed94 --- /dev/null +++ b/packages/contracts-bedrock/scripts/deploy/DeploySaferSafes.s.sol @@ -0,0 +1,47 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.15; + +// Forge +import { Script } from "forge-std/Script.sol"; + +// Scripts +import { DeployUtils } from "scripts/libraries/DeployUtils.sol"; + +// Interfaces +import { ISaferSafes } from "interfaces/safe/ISaferSafes.sol"; + +// Libraries +import { SemverComp } from "src/libraries/SemverComp.sol"; + +/// @title DeploySaferSafes +/// @notice Deploys the SaferSafes singleton contract using CREATE2 with the default salt. +contract DeploySaferSafes is Script { + struct Output { + ISaferSafes saferSafesSingleton; + } + + /// @notice Deploys SaferSafes and returns the output struct. + function run() public returns (Output memory output_) { + output_ = _deploy(); + assertValidOutput(output_); + } + + /// @notice Deploys SaferSafes without broadcasting (for use by other scripts). + function _deploy() internal returns (Output memory output_) { + output_.saferSafesSingleton = ISaferSafes( + DeployUtils.createDeterministic({ + _name: "SaferSafes", + _args: DeployUtils.encodeConstructor(bytes("")), + _salt: DeployUtils.DEFAULT_SALT + }) + ); + vm.label(address(output_.saferSafesSingleton), "SaferSafesSingleton"); + } + + /// @notice Validates the deployment output. + function assertValidOutput(Output memory _output) public view { + DeployUtils.assertValidContractAddress(address(_output.saferSafesSingleton)); + + require(SemverComp.eq(_output.saferSafesSingleton.version(), "1.10.1"), "DeploySaferSafes: unexpected version"); + } +}