Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import { Enum } from "safe-contracts/common/Enum.sol";
import { IERC165 } from "safe-contracts/interfaces/IERC165.sol";

/// @title ITransactionGuard Interface
interface ITransactionGuard is IERC165 {
/// @notice Checks the transaction details.
/// @dev The function needs to implement transaction validation logic.
/// @param to The address to which the transaction is intended.
/// @param value The native token value of the transaction in Wei.
/// @param data The transaction data.
/// @param operation Operation type (0 for `CALL`, 1 for `DELEGATECALL`).
/// @param safeTxGas Gas used for the transaction.
/// @param baseGas The base gas for the transaction.
/// @param gasPrice The price of gas in Wei for the transaction.
/// @param gasToken The token used to pay for gas.
/// @param refundReceiver The address which should receive the refund.
/// @param signatures The signatures of the transaction.
/// @param msgSender The address of the message sender.
function checkTransaction(
address to,
uint256 value,
bytes memory data,
Enum.Operation operation,
uint256 safeTxGas,
uint256 baseGas,
uint256 gasPrice,
address gasToken,
address payable refundReceiver,
bytes memory signatures,
address msgSender
) external;

/// @notice Checks after execution of the transaction.
/// @dev The function needs to implement a check after the execution of the transaction.
/// @param hash The hash of the executed transaction.
/// @param success The status of the transaction execution.
function checkAfterExecution(bytes32 hash, bool success) external;
}
19 changes: 19 additions & 0 deletions packages/contracts-bedrock/snapshots/abi/SaferSafes.json
Original file line number Diff line number Diff line change
Expand Up @@ -559,6 +559,25 @@
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "bytes4",
"name": "_interfaceId",
"type": "bytes4"
}
],
"name": "supportsInterface",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
Expand Down
4 changes: 2 additions & 2 deletions packages/contracts-bedrock/snapshots/semver-lock.json
Original file line number Diff line number Diff line change
Expand Up @@ -208,8 +208,8 @@
"sourceCodeHash": "0x950725f8b9ad9bb3b6b5e836f67e18db824a7864bac547ee0eeba88ada3de0e9"
},
"src/safe/SaferSafes.sol:SaferSafes": {
"initCodeHash": "0xaa17bb150c9bcf19675a33e9762b050148aceae9f6a9a6ba020fc6947ebaab39",
"sourceCodeHash": "0xc4201612048ff051ed795521efa3eece1a6556f2c514a268b180d84a2ad8b2d1"
"initCodeHash": "0x95ee7ae09ee281f224425f152c9154e43e49838edbe3eee48c15301e5f410d25",
"sourceCodeHash": "0xdc794e3d6decb47c51d86bb2f69523feade4fd3f81feb4734800f24b40d40a50"
},
"src/universal/OptimismMintableERC20.sol:OptimismMintableERC20": {
"initCodeHash": "0x3c85eed0d017dca8eda6396aa842ddc12492587b061e8c756a8d32c4610a9658",
Expand Down
4 changes: 2 additions & 2 deletions packages/contracts-bedrock/src/safe/SaferSafes.sol
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ import { ISemver } from "interfaces/universal/ISemver.sol";
/// functionality is not desired, then there is no need to enable or configure it.
contract SaferSafes is LivenessModule2, TimelockGuard, ISemver {
/// @notice Semantic version.
/// @custom:semver 1.2.0
string public constant version = "1.2.0";
/// @custom:semver 1.3.0
string public constant version = "1.3.0";

/// @notice Error for when the liveness response period is insufficient.
error SaferSafes_InsufficientLivenessResponsePeriod();
Expand Down
18 changes: 17 additions & 1 deletion packages/contracts-bedrock/src/safe/TimelockGuard.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,16 @@ pragma solidity 0.8.15;
import { GnosisSafe as Safe } from "safe-contracts/GnosisSafe.sol";
import { Enum } from "safe-contracts/common/Enum.sol";
import { Guard as IGuard } from "safe-contracts/base/GuardManager.sol";
import { IERC165 } from "safe-contracts/interfaces/IERC165.sol";

// Libraries
import { EnumerableSet } from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
import { SemverComp } from "src/libraries/SemverComp.sol";
import { Constants } from "src/libraries/Constants.sol";

// Interfaces
import { ITransactionGuard } from "interfaces/safe/ITransactionGuard.sol";

/// @title TimelockGuard
/// @notice This guard provides timelock functionality for Safe transactions
/// @dev This is a singleton contract, any Safe on the network can use this guard to enforce a timelock delay, and
Expand Down Expand Up @@ -65,7 +69,7 @@ import { Constants } from "src/libraries/Constants.sol";
/// | Quorum+ | challenge + | cancelTransaction |
/// | | changeOwnershipToFallback | |
/// +-------------------------------------------------------------------------------------------------+
abstract contract TimelockGuard is IGuard {
abstract contract TimelockGuard is IGuard, IERC165 {
using EnumerableSet for EnumerableSet.Bytes32Set;

/// @notice Allowed states of a transaction
Expand Down Expand Up @@ -673,4 +677,16 @@ abstract contract TimelockGuard is IGuard {
function signCancellation(bytes32) public {
emit Message("This function is not meant to be called, did you mean to call cancelTransaction?");
}

////////////////////////////////////////////////////////////////
// ERC165 Support //
////////////////////////////////////////////////////////////////

/// @notice ERC165 interface detection
/// @param _interfaceId The interface identifier to check
/// @return True if the contract implements the interface
function supportsInterface(bytes4 _interfaceId) external view virtual override returns (bool) {
return _interfaceId == type(ITransactionGuard).interfaceId // 0xe6d7a83a
|| _interfaceId == type(IERC165).interfaceId; // 0x01ffc9a7
}
}
21 changes: 21 additions & 0 deletions packages/contracts-bedrock/test/safe/TimelockGuard.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ pragma solidity 0.8.15;
import { Test } from "forge-std/Test.sol";
import { GnosisSafe as Safe } from "safe-contracts/GnosisSafe.sol";
import { GuardManager } from "safe-contracts/base/GuardManager.sol";
import { ITransactionGuard } from "interfaces/safe/ITransactionGuard.sol";
import "test/safe-tools/SafeTestTools.sol";

import { TimelockGuard } from "src/safe/TimelockGuard.sol";
Expand Down Expand Up @@ -981,3 +982,23 @@ contract TimelockGuard_ClearTimelockGuard_Test is TimelockGuard_TestInit {
timelockGuard.clearTimelockGuard();
}
}

/// @title TimelockGuard_SupportsInterface_Test
/// @notice Tests ERC165 interface support for TimelockGuard
contract TimelockGuard_SupportsInterface_Test is TimelockGuard_TestInit {
function test_supportsInterface_iTransactionGuard_succeeds() external view {
bytes4 interfaceId = 0xe6d7a83a; // ITransactionGuard interface ID
assertTrue(timelockGuard.supportsInterface(interfaceId), "Should support ITransactionGuard");
}

function test_supportsInterface_ierc165_succeeds() external view {
bytes4 interfaceId = 0x01ffc9a7; // IERC165 interface ID
assertTrue(timelockGuard.supportsInterface(interfaceId), "Should support IERC165");
}

function test_supportsInterface_invalidInterface_fails(bytes4 _interfaceId) external view {
vm.assume(_interfaceId != type(ITransactionGuard).interfaceId);
vm.assume(_interfaceId != type(IERC165).interfaceId);
assertFalse(timelockGuard.supportsInterface(_interfaceId), "Should not support invalid interface");
}
}