github.com/ethereum-optimism/optimism@v1.7.2/packages/contracts-bedrock/test/safe-tools/CompatibilityFallbackHandler_1_3_0.sol (about) 1 // SPDX-License-Identifier: LGPL-3.0-only 2 pragma solidity >=0.7.0 <0.9.0; 3 4 import "safe-contracts/interfaces/ERC1155TokenReceiver.sol"; 5 import "safe-contracts/interfaces/ERC721TokenReceiver.sol"; 6 import "safe-contracts/interfaces/ERC777TokensRecipient.sol"; 7 import "safe-contracts/interfaces/IERC165.sol"; 8 import "safe-contracts/interfaces/ISignatureValidator.sol"; 9 import { Safe as GnosisSafe } from "safe-contracts/Safe.sol"; 10 11 /// author: Colin Nielsen 12 /// https://github.com/colinnielsen/safe-tools/blob/ce6c654a76d91b619ab7778c77d1a76b3ced6666/src/CompatibilityFallbackHandler_1_3_0.sol 13 contract DefaultCallbackHandler is ERC1155TokenReceiver, ERC777TokensRecipient, ERC721TokenReceiver, IERC165 { 14 function onERC1155Received( 15 address, 16 address, 17 uint256, 18 uint256, 19 bytes calldata 20 ) 21 external 22 pure 23 override 24 returns (bytes4) 25 { 26 return 0xf23a6e61; 27 } 28 29 function onERC1155BatchReceived( 30 address, 31 address, 32 uint256[] calldata, 33 uint256[] calldata, 34 bytes calldata 35 ) 36 external 37 pure 38 override 39 returns (bytes4) 40 { 41 return 0xbc197c81; 42 } 43 44 function onERC721Received(address, address, uint256, bytes calldata) external pure override returns (bytes4) { 45 return 0x150b7a02; 46 } 47 48 function tokensReceived( 49 address, 50 address, 51 address, 52 uint256, 53 bytes calldata, 54 bytes calldata 55 ) 56 external 57 pure 58 override 59 { 60 // We implement this for completeness, doesn't really have any value 61 } 62 63 function supportsInterface(bytes4 interfaceId) external view virtual override returns (bool) { 64 return interfaceId == type(ERC1155TokenReceiver).interfaceId 65 || interfaceId == type(ERC721TokenReceiver).interfaceId || interfaceId == type(IERC165).interfaceId; 66 } 67 } 68 69 address constant SENTINEL_MODULES = address(0x1); 70 71 /// @title Compatibility Fallback Handler - fallback handler to provider compatibility between pre 1.3.0 and 1.3.0+ Safe 72 /// contracts 73 /// @author Richard Meissner - <richard@gnosis.pm> 74 contract CompatibilityFallbackHandler is DefaultCallbackHandler, ISignatureValidator { 75 //keccak256( 76 // "SafeMessage(bytes message)" 77 //); 78 bytes32 private constant SAFE_MSG_TYPEHASH = 0x60b3cbf8b4a223d68d641b3b6ddf9a298e7f33710cf3d3a9d1146b5a6150fbca; 79 80 bytes4 internal constant SIMULATE_SELECTOR = bytes4(keccak256("simulate(address,bytes)")); 81 82 bytes4 internal constant UPDATED_MAGIC_VALUE = 0x1626ba7e; 83 84 /** 85 * Implementation of ISignatureValidator (see `interfaces/ISignatureValidator.sol`) 86 * @dev Should return whether the signature provided is valid for the provided data. 87 * @param _data Arbitrary length data signed on the behalf of address(msg.sender) 88 * @param _signature Signature byte array associated with _data 89 * @return a bool upon valid or invalid signature with corresponding _data 90 */ 91 function isValidSignature(bytes memory _data, bytes memory _signature) public view override returns (bytes4) { 92 // Caller should be a Safe 93 GnosisSafe safe = GnosisSafe(payable(msg.sender)); 94 bytes32 messageHash = getMessageHashForSafe(safe, _data); 95 if (_signature.length == 0) { 96 require(safe.signedMessages(messageHash) != 0, "Hash not approved"); 97 } else { 98 safe.checkSignatures(messageHash, _data, _signature); 99 } 100 return EIP1271_MAGIC_VALUE; 101 } 102 103 /// @dev Returns hash of a message that can be signed by owners. 104 /// @param message Message that should be hashed 105 /// @return Message hash. 106 function getMessageHash(bytes memory message) public view returns (bytes32) { 107 return getMessageHashForSafe(GnosisSafe(payable(msg.sender)), message); 108 } 109 110 /// @dev Returns hash of a message that can be signed by owners. 111 /// @param safe Safe to which the message is targeted 112 /// @param message Message that should be hashed 113 /// @return Message hash. 114 function getMessageHashForSafe(GnosisSafe safe, bytes memory message) public view returns (bytes32) { 115 bytes32 safeMessageHash = keccak256(abi.encode(SAFE_MSG_TYPEHASH, keccak256(message))); 116 return keccak256(abi.encodePacked(bytes1(0x19), bytes1(0x01), safe.domainSeparator(), safeMessageHash)); 117 } 118 119 /** 120 * Implementation of updated EIP-1271 121 * @dev Should return whether the signature provided is valid for the provided data. 122 * The save does not implement the interface since `checkSignatures` is not a view method. 123 * The method will not perform any state changes (see parameters of `checkSignatures`) 124 * @param _dataHash Hash of the data signed on the behalf of address(msg.sender) 125 * @param _signature Signature byte array associated with _dataHash 126 * @return a bool upon valid or invalid signature with corresponding _dataHash 127 * @notice See 128 * https://github.com/gnosis/util-contracts/blob/bb5fe5fb5df6d8400998094fb1b32a178a47c3a1/contracts/StorageAccessible.sol 129 */ 130 function isValidSignature(bytes32 _dataHash, bytes calldata _signature) external view returns (bytes4) { 131 ISignatureValidator validator = ISignatureValidator(msg.sender); 132 bytes4 value = validator.isValidSignature(abi.encode(_dataHash), _signature); 133 return (value == EIP1271_MAGIC_VALUE) ? UPDATED_MAGIC_VALUE : bytes4(0); 134 } 135 136 /// @dev Returns array of first 10 modules. 137 /// @return Array of modules. 138 function getModules() external view returns (address[] memory) { 139 // Caller should be a Safe 140 GnosisSafe safe = GnosisSafe(payable(msg.sender)); 141 (address[] memory array,) = safe.getModulesPaginated(SENTINEL_MODULES, 10); 142 return array; 143 } 144 145 /** 146 * @dev Performs a delegetecall on a targetContract in the context of self. 147 * Internally reverts execution to avoid side effects (making it static). Catches revert and returns encoded result 148 * as bytes. 149 * @param targetContract Address of the contract containing the code to execute. 150 * @param calldataPayload Calldata that should be sent to the target contract (encoded method name and arguments). 151 */ 152 function simulate( 153 address targetContract, 154 bytes calldata calldataPayload 155 ) 156 external 157 returns (bytes memory response) 158 { 159 // Suppress compiler warnings about not using parameters, while allowing 160 // parameters to keep names for documentation purposes. This does not 161 // generate code. 162 targetContract; 163 calldataPayload; 164 165 assembly { 166 let internalCalldata := mload(0x40) 167 // Store `simulateAndRevert.selector`. 168 // String representation is used to force right padding 169 mstore(internalCalldata, "\xb4\xfa\xba\x09") 170 // Abuse the fact that both this and the internal methods have the 171 // same signature, and differ only in symbol name (and therefore, 172 // selector) and copy calldata directly. This saves us approximately 173 // 250 bytes of code and 300 gas at runtime over the 174 // `abi.encodeWithSelector` builtin. 175 calldatacopy(add(internalCalldata, 0x04), 0x04, sub(calldatasize(), 0x04)) 176 177 // `pop` is required here by the compiler, as top level expressions 178 // can't have return values in inline assembly. `call` typically 179 // returns a 0 or 1 value indicated whether or not it reverted, but 180 // since we know it will always revert, we can safely ignore it. 181 pop( 182 call( 183 gas(), 184 // address() has been changed to caller() to use the implementation of the Safe 185 caller(), 186 0, 187 internalCalldata, 188 calldatasize(), 189 // The `simulateAndRevert` call always reverts, and 190 // instead encodes whether or not it was successful in the return 191 // data. The first 32-byte word of the return data contains the 192 // `success` value, so write it to memory address 0x00 (which is 193 // reserved Solidity scratch space and OK to use). 194 0x00, 195 0x20 196 ) 197 ) 198 199 // Allocate and copy the response bytes, making sure to increment 200 // the free memory pointer accordingly (in case this method is 201 // called as an internal function). The remaining `returndata[0x20:]` 202 // contains the ABI encoded response bytes, so we can just write it 203 // as is to memory. 204 let responseSize := sub(returndatasize(), 0x20) 205 response := mload(0x40) 206 mstore(0x40, add(response, responseSize)) 207 returndatacopy(response, 0x20, responseSize) 208 209 if iszero(mload(0x00)) { revert(add(response, 0x20), mload(response)) } 210 } 211 } 212 }