From 6c96a29c913e5841108bd58738977aac5f026d14 Mon Sep 17 00:00:00 2001 From: zeroknots Date: Fri, 15 Mar 2024 09:05:19 +0700 Subject: [PATCH 1/2] rm: remove not implemented calltype --- accounts/safe7579/src/core/ModuleManager.sol | 2 -- 1 file changed, 2 deletions(-) diff --git a/accounts/safe7579/src/core/ModuleManager.sol b/accounts/safe7579/src/core/ModuleManager.sol index 055cc6a0..ca516be2 100644 --- a/accounts/safe7579/src/core/ModuleManager.sol +++ b/accounts/safe7579/src/core/ModuleManager.sol @@ -9,8 +9,6 @@ import { Receiver } from "erc7579/core/Receiver.sol"; import { AccessControl } from "./AccessControl.sol"; import { CallType, CALLTYPE_SINGLE, CALLTYPE_DELEGATECALL } from "erc7579/lib/ModeLib.sol"; -CallType constant CALLTYPE_STATIC = CallType.wrap(0xFE); - struct FallbackHandler { address handler; CallType calltype; From 00261acd24a2cc29ce9773f0e3451acfd983f307 Mon Sep 17 00:00:00 2001 From: zeroknots Date: Sat, 16 Mar 2024 08:02:25 +0700 Subject: [PATCH 2/2] =?UTF-8?q?=E2=9C=A8=20Add=20ERC1271=20fallback=20for?= =?UTF-8?q?=20safe=20native=20signatures?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit if no signature or address(0) validation module or an not installed 7579 validation module is provided within the abi.encodePacked signature data in erc1271, it falls back to safe's signature check --- accounts/safe7579/src/SafeERC7579.sol | 54 ++++++++++++++++++- accounts/safe7579/src/interfaces/IERC1271.sol | 22 ++++++++ accounts/safe7579/src/interfaces/ISafe.sol | 2 + 3 files changed, 76 insertions(+), 2 deletions(-) create mode 100644 accounts/safe7579/src/interfaces/IERC1271.sol diff --git a/accounts/safe7579/src/SafeERC7579.sol b/accounts/safe7579/src/SafeERC7579.sol index 81ba53b2..7fe55a07 100644 --- a/accounts/safe7579/src/SafeERC7579.sol +++ b/accounts/safe7579/src/SafeERC7579.sol @@ -30,6 +30,7 @@ import { import { _packValidationData } from "@ERC4337/account-abstraction/contracts/core/Helpers.sol"; import { IEntryPoint } from "@ERC4337/account-abstraction/contracts/interfaces/IEntryPoint.sol"; import { ISafe7579Init } from "./interfaces/ISafe7579Init.sol"; +import { IERC1271 } from "./interfaces/IERC1271.sol"; /** * @title ERC7579 Adapter for Safe accounts. @@ -56,6 +57,12 @@ contract SafeERC7579 is bytes32 private constant DOMAIN_SEPARATOR_TYPEHASH = 0x47e79534a245952e8b16893a336b85a3d9ea9fa8c573f3d803afb92a79469218; + // keccak256("SafeMessage(bytes message)"); + bytes32 private constant SAFE_MSG_TYPEHASH = + 0x60b3cbf8b4a223d68d641b3b6ddf9a298e7f33710cf3d3a9d1146b5a6150fbca; + // keccak256("safeSignature(bytes32,bytes32,bytes,bytes)"); + bytes4 private constant SAFE_SIGNATURE_MAGIC_VALUE = 0x5fd7e97d; + /** * @inheritdoc IERC7579Account */ @@ -201,7 +208,13 @@ contract SafeERC7579 is } /** - * @inheritdoc IERC7579Account + * Will use Safe's signed messages or checkSignatures features or ERC7579 validation modules + * if no signature is provided, it makes use of Safe's signedMessages + * if address(0) or a non-installed validator module is provided, it will use Safe's + * checkSignatures + * if a valid validator module is provided, it will use the module's validateUserOp function + * @param hash message hash of ERC1271 request + * @param data abi.encodePacked(address validationModule, bytes signatures) */ function isValidSignature( bytes32 hash, @@ -211,9 +224,27 @@ contract SafeERC7579 is view returns (bytes4 magicValue) { + ISafe safe = ISafe(msg.sender); + + // check for safe's approved hashes + if (data.length == 0 && safe.signedMessages(hash) != 0) { + // return magic value + return IERC1271.isValidSignature.selector; + } address validationModule = address(bytes20(data[:20])); - if (!_isValidatorInstalled(validationModule)) return 0xFFFFFFFF; + if (validationModule == address(0) || !_isValidatorInstalled(validationModule)) { + bytes memory messageData = EIP712.encodeMessageData( + safe.domainSeparator(), SAFE_MSG_TYPEHASH, abi.encode(keccak256(abi.encode(hash))) + ); + + bytes32 messageHash = keccak256(messageData); + + safe.checkSignatures(messageHash, messageData, data[20:]); + return IERC1271.isValidSignature.selector; + } + + // use 7579 validation module magicValue = IValidator(validationModule).isValidSignatureWithSender(msg.sender, hash, data[20:]); } @@ -454,3 +485,22 @@ contract SafeERC7579 is nonce = IEntryPoint(entryPoint()).getNonce(safe, key); } } + +library EIP712 { + function encodeMessageData( + bytes32 domainSeparator, + bytes32 typeHash, + bytes memory message + ) + internal + pure + returns (bytes memory) + { + return abi.encodePacked( + bytes1(0x19), + bytes1(0x01), + domainSeparator, + keccak256(abi.encodePacked(typeHash, message)) + ); + } +} diff --git a/accounts/safe7579/src/interfaces/IERC1271.sol b/accounts/safe7579/src/interfaces/IERC1271.sol new file mode 100644 index 00000000..5aff1322 --- /dev/null +++ b/accounts/safe7579/src/interfaces/IERC1271.sol @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: LGPL-3.0-only +pragma solidity ^0.8.23; + +interface IERC1271 { + /** + * @dev Should return whether the signature provided is valid for the provided data + * @param _dataHash Arbitrary length data signed on behalf of address(this) + * @param _signature Signature byte array associated with _data + * + * MUST return the bytes4 magic value 0x1626ba7e when function passes. + * MUST NOT modify state (using STATICCALL for solc < 0.5, view modifier for solc > + * 0.5) + * MUST allow external calls + */ + function isValidSignature( + bytes32 _dataHash, + bytes calldata _signature + ) + external + view + returns (bytes4); +} diff --git a/accounts/safe7579/src/interfaces/ISafe.sol b/accounts/safe7579/src/interfaces/ISafe.sol index be60fa30..9d770160 100644 --- a/accounts/safe7579/src/interfaces/ISafe.sol +++ b/accounts/safe7579/src/interfaces/ISafe.sol @@ -65,6 +65,8 @@ interface ISafe { external view; + function signedMessages(bytes32) external view returns (uint256); + /** * @dev Returns the domain separator for this contract, as defined in the EIP-712 standard. * @return bytes32 The domain separator hash.