Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 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 @@ -545,6 +545,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": "0x22730f6ebe10e0d78b23b3e0f3d58f867f420039981455b09a508755e512c127",
"sourceCodeHash": "0xe3d2fd50724baf6d5e83e2c9d89fcfcbc678d966187bd38e2d9a840716bcafa3"
"initCodeHash": "0xaf28767ff36bee4fed0ca3338c3948464f777dc4613f18b5740fd38d22a5f6ef",
"sourceCodeHash": "0xaaa7f49d206ea79bc2709b03d06f0b0198d7752e86443fdb032aab50411c5742"
},
"src/universal/OptimismMintableERC20.sol:OptimismMintableERC20": {
"initCodeHash": "0x3c85eed0d017dca8eda6396aa842ddc12492587b061e8c756a8d32c4610a9658",
Expand Down
12 changes: 10 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.0.0
string public constant version = "1.0.0";
/// @custom:semver 1.1.0
string public constant version = "1.1.0";

/// @notice Error for when the liveness response period is insufficient.
error SaferSafes_InsufficientLivenessResponsePeriod();
Expand Down Expand Up @@ -60,4 +60,12 @@ contract SaferSafes is LivenessModule2, TimelockGuard, ISemver {
revert SaferSafes_InsufficientLivenessResponsePeriod();
}
}

/// @notice ERC165 interface detection
/// @dev Combines interface support from both LivenessModule2 and TimelockGuard
/// @param _interfaceId The interface identifier to check
/// @return True if the contract implements the interface
function supportsInterface(bytes4 _interfaceId) public view override(TimelockGuard) returns (bool) {
return TimelockGuard.supportsInterface(_interfaceId);
}
Comment thread
maurelian marked this conversation as resolved.
Outdated
}
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,11 +5,15 @@ 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";

// 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 @@ -64,7 +68,7 @@ import { SemverComp } from "src/libraries/SemverComp.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 @@ -612,4 +616,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) public view virtual override returns (bool) {
Comment thread
JosepBove marked this conversation as resolved.
Outdated
return _interfaceId == type(ITransactionGuard).interfaceId // 0xe6d7a83a
|| _interfaceId == type(IERC165).interfaceId; // 0x01ffc9a7
}
}
19 changes: 19 additions & 0 deletions packages/contracts-bedrock/test/safe/SaferSafes.t.sol
Comment thread
JosepBove marked this conversation as resolved.
Original file line number Diff line number Diff line change
Expand Up @@ -144,3 +144,22 @@ contract SaferSafes_Uncategorized_Test is SaferSafes_TestInit {
saferSafes.configureLivenessModule(moduleConfig);
}
}

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

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

function test_supportsInterface_invalidInterface_fails() external view {
bytes4 interfaceId = 0xffffffff; // Invalid interface ID
Comment thread
JosepBove marked this conversation as resolved.
Outdated
assertFalse(saferSafes.supportsInterface(interfaceId), "Should not support invalid interface");
}
}
19 changes: 19 additions & 0 deletions packages/contracts-bedrock/test/safe/TimelockGuard.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -880,3 +880,22 @@ contract TimelockGuard_Integration_Test is TimelockGuard_TestInit {
assertEq(timelockGuard.cancellationThreshold(safeInstance.safe), maxThreshold);
}
}

/// @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_reverts() external view {
bytes4 interfaceId = 0xffffffff; // Invalid interface ID
assertFalse(timelockGuard.supportsInterface(interfaceId), "Should not support invalid interface");
Comment thread
JosepBove marked this conversation as resolved.
Outdated
}
}