|
| 1 | +// SPDX-License-Identifier: Apache-2.0 |
| 2 | +pragma solidity ^0.8.11; |
| 3 | + |
| 4 | +import { ECDSA } from "solady/utils/ECDSA.sol"; |
| 5 | +import { SeaportOrderParser } from "./SeaportOrderParser.sol"; |
| 6 | +import { OrderParameters } from "seaport-types/src/lib/ConsiderationStructs.sol"; |
| 7 | + |
| 8 | +abstract contract SeaportEIP1271 is SeaportOrderParser { |
| 9 | + using ECDSA for bytes32; |
| 10 | + |
| 11 | + bytes32 private constant ACCOUNT_MESSAGE_TYPEHASH = keccak256("AccountMessage(bytes message)"); |
| 12 | + bytes32 private constant EIP712_TYPEHASH = |
| 13 | + keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"); |
| 14 | + bytes32 private immutable HASHED_NAME; |
| 15 | + bytes32 private immutable HASHED_VERSION; |
| 16 | + |
| 17 | + /// @notice The function selector of EIP1271.isValidSignature to be returned on sucessful signature verification. |
| 18 | + bytes4 public constant MAGICVALUE = 0x1626ba7e; |
| 19 | + |
| 20 | + constructor(string memory _name, string memory _version) { |
| 21 | + HASHED_NAME = keccak256(bytes(_name)); |
| 22 | + HASHED_VERSION = keccak256(bytes(_version)); |
| 23 | + } |
| 24 | + |
| 25 | + /// @notice See EIP-1271: https://eips.ethereum.org/EIPS/eip-1271 |
| 26 | + function isValidSignature( |
| 27 | + bytes32 _message, |
| 28 | + bytes memory _signature |
| 29 | + ) public view virtual returns (bytes4 magicValue) { |
| 30 | + bytes32 targetDigest; |
| 31 | + bytes memory targetSig; |
| 32 | + |
| 33 | + // Handle OpenSea bulk order signatures that are >65 bytes in length. |
| 34 | + if (_signature.length > 65) { |
| 35 | + // Decode packed signature and order parameters. |
| 36 | + (bytes memory extractedPackedSig, OrderParameters memory orderParameters, uint256 counter) = abi.decode( |
| 37 | + _signature, |
| 38 | + (bytes, OrderParameters, uint256) |
| 39 | + ); |
| 40 | + |
| 41 | + // Verify that the original digest matches the digest built with order parameters. |
| 42 | + bytes32 domainSeparator = _buildSeaportDomainSeparator(msg.sender); |
| 43 | + bytes32 orderHash = _deriveOrderHash(orderParameters, counter); |
| 44 | + |
| 45 | + require( |
| 46 | + _deriveEIP712Digest(domainSeparator, orderHash) == _message, |
| 47 | + "Seaport: order hash does not match the provided message." |
| 48 | + ); |
| 49 | + |
| 50 | + // Build bulk signature digest |
| 51 | + targetDigest = _deriveEIP712Digest(domainSeparator, _computeBulkOrderProof(extractedPackedSig, orderHash)); |
| 52 | + |
| 53 | + // Extract the signature, which is the first 65 bytes |
| 54 | + targetSig = new bytes(65); |
| 55 | + for (uint256 i = 0; i < 65; i++) { |
| 56 | + targetSig[i] = extractedPackedSig[i]; |
| 57 | + } |
| 58 | + } else { |
| 59 | + targetDigest = getMessageHash(_message); |
| 60 | + targetSig = _signature; |
| 61 | + } |
| 62 | + |
| 63 | + address signer = targetDigest.recover(targetSig); |
| 64 | + |
| 65 | + if (_isAuthorizedSigner(signer)) { |
| 66 | + magicValue = MAGICVALUE; |
| 67 | + } |
| 68 | + } |
| 69 | + |
| 70 | + /** |
| 71 | + * @notice Returns the hash of message that should be signed for EIP1271 verification. |
| 72 | + * @param _hash The message hash pre replay protection. |
| 73 | + * @return messageHash The digest (with replay protection) to sign for EIP-1271 verification. |
| 74 | + */ |
| 75 | + function getMessageHash(bytes32 _hash) public view returns (bytes32) { |
| 76 | + bytes32 messageHash = keccak256(abi.encode(_hash)); |
| 77 | + bytes32 typedDataHash = keccak256(abi.encode(ACCOUNT_MESSAGE_TYPEHASH, messageHash)); |
| 78 | + return keccak256(abi.encodePacked("\x19\x01", _accountDomainSeparator(), typedDataHash)); |
| 79 | + } |
| 80 | + |
| 81 | + /// @notice Returns the EIP712 domain separator for the contract. |
| 82 | + function _accountDomainSeparator() private view returns (bytes32) { |
| 83 | + return keccak256(abi.encode(EIP712_TYPEHASH, HASHED_NAME, HASHED_VERSION, block.chainid, address(this))); |
| 84 | + } |
| 85 | + |
| 86 | + /// @notice Returns whether a given signer is an authorized signer for the contract. |
| 87 | + function _isAuthorizedSigner(address _signer) internal view virtual returns (bool); |
| 88 | +} |
0 commit comments