Skip to content

Commit a9e6477

Browse files
authored
Seaport bulk sig support for smart accounts: AccountSeaportBulkSigSupport (#633)
* Add Seaport bulk sig support for smart accounts: AccountSeaportBulkSigSupport * fix build * fix build * fix build * run prettier * update gitmodules * fix lint errors
1 parent b44b563 commit a9e6477

File tree

10 files changed

+710
-0
lines changed

10 files changed

+710
-0
lines changed

Diff for: .gitmodules

+24
Original file line numberDiff line numberDiff line change
@@ -25,3 +25,27 @@
2525
[submodule "lib/solady"]
2626
path = lib/solady
2727
url = https://github.com/vectorized/solady
28+
[submodule "lib/seaport"]
29+
path = lib/seaport
30+
url = https://github.com/ProjectOpenSea/seaport
31+
[submodule "lib/murky"]
32+
path = lib/murky
33+
url = https://github.com/dmfxyz/murky
34+
[submodule "lib/openzeppelin-contracts"]
35+
path = lib/openzeppelin-contracts
36+
url = https://github.com/openzeppelin/openzeppelin-contracts
37+
[submodule "lib/solmate"]
38+
path = lib/solmate
39+
url = https://github.com/transmissions11/solmate
40+
[submodule "lib/solarray"]
41+
path = lib/solarray
42+
url = https://github.com/emo-eth/solarray
43+
[submodule "lib/seaport-types"]
44+
path = lib/seaport-types
45+
url = https://github.com/projectopensea/seaport-types
46+
[submodule "lib/seaport-core"]
47+
path = lib/seaport-core
48+
url = https://github.com/projectopensea/seaport-core
49+
[submodule "lib/seaport-sol"]
50+
path = lib/seaport-sol
51+
url = https://github.com/projectopensea/seaport-sol

Diff for: contracts/extension/SeaportEIP1271.sol

+88
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
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

Comments
 (0)