diff --git a/.gitmodules b/.gitmodules index 6cf023db0..c57f0eaa8 100644 --- a/.gitmodules +++ b/.gitmodules @@ -25,3 +25,27 @@ [submodule "lib/solady"] path = lib/solady url = https://github.com/vectorized/solady +[submodule "lib/seaport"] + path = lib/seaport + url = https://github.com/ProjectOpenSea/seaport +[submodule "lib/murky"] + path = lib/murky + url = https://github.com/dmfxyz/murky +[submodule "lib/openzeppelin-contracts"] + path = lib/openzeppelin-contracts + url = https://github.com/openzeppelin/openzeppelin-contracts +[submodule "lib/solmate"] + path = lib/solmate + url = https://github.com/transmissions11/solmate +[submodule "lib/solarray"] + path = lib/solarray + url = https://github.com/emo-eth/solarray +[submodule "lib/seaport-types"] + path = lib/seaport-types + url = https://github.com/projectopensea/seaport-types +[submodule "lib/seaport-core"] + path = lib/seaport-core + url = https://github.com/projectopensea/seaport-core +[submodule "lib/seaport-sol"] + path = lib/seaport-sol + url = https://github.com/projectopensea/seaport-sol diff --git a/contracts/extension/SeaportEIP1271.sol b/contracts/extension/SeaportEIP1271.sol new file mode 100644 index 000000000..711c847ce --- /dev/null +++ b/contracts/extension/SeaportEIP1271.sol @@ -0,0 +1,88 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity ^0.8.11; + +import { ECDSA } from "solady/utils/ECDSA.sol"; +import { SeaportOrderParser } from "./SeaportOrderParser.sol"; +import { OrderParameters } from "seaport-types/src/lib/ConsiderationStructs.sol"; + +abstract contract SeaportEIP1271 is SeaportOrderParser { + using ECDSA for bytes32; + + bytes32 private constant ACCOUNT_MESSAGE_TYPEHASH = keccak256("AccountMessage(bytes message)"); + bytes32 private constant EIP712_TYPEHASH = + keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"); + bytes32 private immutable HASHED_NAME; + bytes32 private immutable HASHED_VERSION; + + /// @notice The function selector of EIP1271.isValidSignature to be returned on sucessful signature verification. + bytes4 public constant MAGICVALUE = 0x1626ba7e; + + constructor(string memory _name, string memory _version) { + HASHED_NAME = keccak256(bytes(_name)); + HASHED_VERSION = keccak256(bytes(_version)); + } + + /// @notice See EIP-1271: https://eips.ethereum.org/EIPS/eip-1271 + function isValidSignature( + bytes32 _message, + bytes memory _signature + ) public view virtual returns (bytes4 magicValue) { + bytes32 targetDigest; + bytes memory targetSig; + + // Handle OpenSea bulk order signatures that are >65 bytes in length. + if (_signature.length > 65) { + // Decode packed signature and order parameters. + (bytes memory extractedPackedSig, OrderParameters memory orderParameters, uint256 counter) = abi.decode( + _signature, + (bytes, OrderParameters, uint256) + ); + + // Verify that the original digest matches the digest built with order parameters. + bytes32 domainSeparator = _buildSeaportDomainSeparator(msg.sender); + bytes32 orderHash = _deriveOrderHash(orderParameters, counter); + + require( + _deriveEIP712Digest(domainSeparator, orderHash) == _message, + "Seaport: order hash does not match the provided message." + ); + + // Build bulk signature digest + targetDigest = _deriveEIP712Digest(domainSeparator, _computeBulkOrderProof(extractedPackedSig, orderHash)); + + // Extract the signature, which is the first 65 bytes + targetSig = new bytes(65); + for (uint256 i = 0; i < 65; i++) { + targetSig[i] = extractedPackedSig[i]; + } + } else { + targetDigest = getMessageHash(_message); + targetSig = _signature; + } + + address signer = targetDigest.recover(targetSig); + + if (_isAuthorizedSigner(signer)) { + magicValue = MAGICVALUE; + } + } + + /** + * @notice Returns the hash of message that should be signed for EIP1271 verification. + * @param _hash The message hash pre replay protection. + * @return messageHash The digest (with replay protection) to sign for EIP-1271 verification. + */ + function getMessageHash(bytes32 _hash) public view returns (bytes32) { + bytes32 messageHash = keccak256(abi.encode(_hash)); + bytes32 typedDataHash = keccak256(abi.encode(ACCOUNT_MESSAGE_TYPEHASH, messageHash)); + return keccak256(abi.encodePacked("\x19\x01", _accountDomainSeparator(), typedDataHash)); + } + + /// @notice Returns the EIP712 domain separator for the contract. + function _accountDomainSeparator() private view returns (bytes32) { + return keccak256(abi.encode(EIP712_TYPEHASH, HASHED_NAME, HASHED_VERSION, block.chainid, address(this))); + } + + /// @notice Returns whether a given signer is an authorized signer for the contract. + function _isAuthorizedSigner(address _signer) internal view virtual returns (bool); +} diff --git a/contracts/extension/SeaportOrderParser.sol b/contracts/extension/SeaportOrderParser.sol new file mode 100644 index 000000000..f76e3e337 --- /dev/null +++ b/contracts/extension/SeaportOrderParser.sol @@ -0,0 +1,550 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity ^0.8.11; + +/* solhint-disable */ + +import { OrderParameters } from "seaport-types/src/lib/ConsiderationStructs.sol"; +import { EIP_712_PREFIX, EIP712_ConsiderationItem_size, EIP712_DigestPayload_size, EIP712_DomainSeparator_offset, EIP712_OfferItem_size, EIP712_Order_size, EIP712_OrderHash_offset, OneWord, OneWordShift, OrderParameters_consideration_head_offset, OrderParameters_counter_offset, OrderParameters_offer_head_offset, TwoWords, BulkOrderProof_keyShift, BulkOrderProof_keySize, BulkOrder_Typehash_Height_One, BulkOrder_Typehash_Height_Two, BulkOrder_Typehash_Height_Three, BulkOrder_Typehash_Height_Four, BulkOrder_Typehash_Height_Five, BulkOrder_Typehash_Height_Six, BulkOrder_Typehash_Height_Seven, BulkOrder_Typehash_Height_Eight, BulkOrder_Typehash_Height_Nine, BulkOrder_Typehash_Height_Ten, BulkOrder_Typehash_Height_Eleven, BulkOrder_Typehash_Height_Twelve, BulkOrder_Typehash_Height_Thirteen, BulkOrder_Typehash_Height_Fourteen, BulkOrder_Typehash_Height_Fifteen, BulkOrder_Typehash_Height_Sixteen, BulkOrder_Typehash_Height_Seventeen, BulkOrder_Typehash_Height_Eighteen, BulkOrder_Typehash_Height_Nineteen, BulkOrder_Typehash_Height_Twenty, BulkOrder_Typehash_Height_TwentyOne, BulkOrder_Typehash_Height_TwentyTwo, BulkOrder_Typehash_Height_TwentyThree, BulkOrder_Typehash_Height_TwentyFour, FreeMemoryPointerSlot } from "seaport-types/src/lib/ConsiderationConstants.sol"; + +contract SeaportOrderParser { + uint256 constant ECDSA_MAXLENGTH = 65; + + bytes32 private immutable _NAME_HASH; + bytes32 private immutable _VERSION_HASH; + bytes32 private immutable _EIP_712_DOMAIN_TYPEHASH; + bytes32 private immutable _OFFER_ITEM_TYPEHASH; + bytes32 private immutable _CONSIDERATION_ITEM_TYPEHASH; + bytes32 private immutable _ORDER_TYPEHASH; + + constructor() { + ( + _NAME_HASH, + _VERSION_HASH, + _EIP_712_DOMAIN_TYPEHASH, + _OFFER_ITEM_TYPEHASH, + _CONSIDERATION_ITEM_TYPEHASH, + _ORDER_TYPEHASH + ) = _deriveTypehashes(); + } + + function _nameString() internal pure virtual returns (string memory) { + // Return the name of the contract. + return "Seaport"; + } + + function _buildSeaportDomainSeparator(address _domainAddress) internal view returns (bytes32) { + return + keccak256(abi.encode(_EIP_712_DOMAIN_TYPEHASH, _NAME_HASH, _VERSION_HASH, block.chainid, _domainAddress)); + } + + function _deriveOrderHash( + OrderParameters memory orderParameters, + uint256 counter + ) internal view returns (bytes32 orderHash) { + // Get length of original consideration array and place it on the stack. + uint256 originalConsiderationLength = (orderParameters.totalOriginalConsiderationItems); + + /* + * Memory layout for an array of structs (dynamic or not) is similar + * to ABI encoding of dynamic types, with a head segment followed by + * a data segment. The main difference is that the head of an element + * is a memory pointer rather than an offset. + */ + + // Declare a variable for the derived hash of the offer array. + bytes32 offerHash; + + // Read offer item EIP-712 typehash from runtime code & place on stack. + bytes32 typeHash = _OFFER_ITEM_TYPEHASH; + + // Utilize assembly so that memory regions can be reused across hashes. + assembly { + // Retrieve the free memory pointer and place on the stack. + let hashArrPtr := mload(FreeMemoryPointerSlot) + + // Get the pointer to the offers array. + let offerArrPtr := mload(add(orderParameters, OrderParameters_offer_head_offset)) + + // Load the length. + let offerLength := mload(offerArrPtr) + + // Set the pointer to the first offer's head. + offerArrPtr := add(offerArrPtr, OneWord) + + // Iterate over the offer items. + for { + let i := 0 + } lt(i, offerLength) { + i := add(i, 1) + } { + // Read the pointer to the offer data and subtract one word + // to get typeHash pointer. + let ptr := sub(mload(offerArrPtr), OneWord) + + // Read the current value before the offer data. + let value := mload(ptr) + + // Write the type hash to the previous word. + mstore(ptr, typeHash) + + // Take the EIP712 hash and store it in the hash array. + mstore(hashArrPtr, keccak256(ptr, EIP712_OfferItem_size)) + + // Restore the previous word. + mstore(ptr, value) + + // Increment the array pointers by one word. + offerArrPtr := add(offerArrPtr, OneWord) + hashArrPtr := add(hashArrPtr, OneWord) + } + + // Derive the offer hash using the hashes of each item. + offerHash := keccak256(mload(FreeMemoryPointerSlot), shl(OneWordShift, offerLength)) + } + + // Declare a variable for the derived hash of the consideration array. + bytes32 considerationHash; + + // Read consideration item typehash from runtime code & place on stack. + typeHash = _CONSIDERATION_ITEM_TYPEHASH; + + // Utilize assembly so that memory regions can be reused across hashes. + assembly { + // Retrieve the free memory pointer and place on the stack. + let hashArrPtr := mload(FreeMemoryPointerSlot) + + // Get the pointer to the consideration array. + let considerationArrPtr := add( + mload(add(orderParameters, OrderParameters_consideration_head_offset)), + OneWord + ) + + // Iterate over the consideration items (not including tips). + for { + let i := 0 + } lt(i, originalConsiderationLength) { + i := add(i, 1) + } { + // Read the pointer to the consideration data and subtract one + // word to get typeHash pointer. + let ptr := sub(mload(considerationArrPtr), OneWord) + + // Read the current value before the consideration data. + let value := mload(ptr) + + // Write the type hash to the previous word. + mstore(ptr, typeHash) + + // Take the EIP712 hash and store it in the hash array. + mstore(hashArrPtr, keccak256(ptr, EIP712_ConsiderationItem_size)) + + // Restore the previous word. + mstore(ptr, value) + + // Increment the array pointers by one word. + considerationArrPtr := add(considerationArrPtr, OneWord) + hashArrPtr := add(hashArrPtr, OneWord) + } + + // Derive the consideration hash using the hashes of each item. + considerationHash := keccak256(mload(FreeMemoryPointerSlot), shl(OneWordShift, originalConsiderationLength)) + } + + // Read order item EIP-712 typehash from runtime code & place on stack. + typeHash = _ORDER_TYPEHASH; + + // Utilize assembly to access derived hashes & other arguments directly. + assembly { + // Retrieve pointer to the region located just behind parameters. + let typeHashPtr := sub(orderParameters, OneWord) + + // Store the value at that pointer location to restore later. + let previousValue := mload(typeHashPtr) + + // Store the order item EIP-712 typehash at the typehash location. + mstore(typeHashPtr, typeHash) + + // Retrieve the pointer for the offer array head. + let offerHeadPtr := add(orderParameters, OrderParameters_offer_head_offset) + + // Retrieve the data pointer referenced by the offer head. + let offerDataPtr := mload(offerHeadPtr) + + // Store the offer hash at the retrieved memory location. + mstore(offerHeadPtr, offerHash) + + // Retrieve the pointer for the consideration array head. + let considerationHeadPtr := add(orderParameters, OrderParameters_consideration_head_offset) + + // Retrieve the data pointer referenced by the consideration head. + let considerationDataPtr := mload(considerationHeadPtr) + + // Store the consideration hash at the retrieved memory location. + mstore(considerationHeadPtr, considerationHash) + + // Retrieve the pointer for the counter. + let counterPtr := add(orderParameters, OrderParameters_counter_offset) + + // Store the counter at the retrieved memory location. + mstore(counterPtr, counter) + + // Derive the order hash using the full range of order parameters. + orderHash := keccak256(typeHashPtr, EIP712_Order_size) + + // Restore the value previously held at typehash pointer location. + mstore(typeHashPtr, previousValue) + + // Restore offer data pointer at the offer head pointer location. + mstore(offerHeadPtr, offerDataPtr) + + // Restore consideration data pointer at the consideration head ptr. + mstore(considerationHeadPtr, considerationDataPtr) + + // Restore consideration item length at the counter pointer. + mstore(counterPtr, originalConsiderationLength) + } + } + + function _deriveTypehashes() + internal + pure + returns ( + bytes32 nameHash, + bytes32 versionHash, + bytes32 eip712DomainTypehash, + bytes32 offerItemTypehash, + bytes32 considerationItemTypehash, + bytes32 orderTypehash + ) + { + // Derive hash of the name of the contract. + nameHash = keccak256(bytes(_nameString())); + + // Derive hash of the version string of the contract. + versionHash = keccak256(bytes("1.5")); + + // Construct the OfferItem type string. + bytes memory offerItemTypeString = bytes( + "OfferItem(" + "uint8 itemType," + "address token," + "uint256 identifierOrCriteria," + "uint256 startAmount," + "uint256 endAmount" + ")" + ); + + // Construct the ConsiderationItem type string. + bytes memory considerationItemTypeString = bytes( + "ConsiderationItem(" + "uint8 itemType," + "address token," + "uint256 identifierOrCriteria," + "uint256 startAmount," + "uint256 endAmount," + "address recipient" + ")" + ); + + // Construct the OrderComponents type string, not including the above. + bytes memory orderComponentsPartialTypeString = bytes( + "OrderComponents(" + "address offerer," + "address zone," + "OfferItem[] offer," + "ConsiderationItem[] consideration," + "uint8 orderType," + "uint256 startTime," + "uint256 endTime," + "bytes32 zoneHash," + "uint256 salt," + "bytes32 conduitKey," + "uint256 counter" + ")" + ); + + // Construct the primary EIP-712 domain type string. + eip712DomainTypehash = keccak256( + bytes( + "EIP712Domain(" + "string name," + "string version," + "uint256 chainId," + "address verifyingContract" + ")" + ) + ); + + // Derive the OfferItem type hash using the corresponding type string. + offerItemTypehash = keccak256(offerItemTypeString); + + // Derive ConsiderationItem type hash using corresponding type string. + considerationItemTypehash = keccak256(considerationItemTypeString); + + bytes memory orderTypeString = bytes.concat( + orderComponentsPartialTypeString, + considerationItemTypeString, + offerItemTypeString + ); + + // Derive OrderItem type hash via combination of relevant type strings. + orderTypehash = keccak256(orderTypeString); + } + + function _computeBulkOrderProof( + bytes memory proofAndSignature, + bytes32 leaf + ) internal pure returns (bytes32 bulkOrderHash) { + // Declare arguments for the root hash and the height of the proof. + bytes32 root; + uint256 height; + + // Utilize assembly to efficiently derive the root hash using the proof. + assembly { + // Retrieve the length of the proof, key, and signature combined. + let fullLength := mload(proofAndSignature) + + // If proofAndSignature has odd length, it is a compact signature + // with 64 bytes. + let signatureLength := sub(ECDSA_MAXLENGTH, and(fullLength, 1)) + + // Derive height (or depth of tree) with signature and proof length. + height := shr(OneWordShift, sub(fullLength, signatureLength)) + + // Update the length in memory to only include the signature. + mstore(proofAndSignature, signatureLength) + + // Derive the pointer for the key using the signature length. + let keyPtr := add(proofAndSignature, add(OneWord, signatureLength)) + + // Retrieve the three-byte key using the derived pointer. + let key := shr(BulkOrderProof_keyShift, mload(keyPtr)) + + /// Retrieve pointer to first proof element by applying a constant + // for the key size to the derived key pointer. + let proof := add(keyPtr, BulkOrderProof_keySize) + + // Compute level 1. + let scratchPtr1 := shl(OneWordShift, and(key, 1)) + mstore(scratchPtr1, leaf) + mstore(xor(scratchPtr1, OneWord), mload(proof)) + + // Compute remaining proofs. + for { + let i := 1 + } lt(i, height) { + i := add(i, 1) + } { + proof := add(proof, OneWord) + let scratchPtr := shl(OneWordShift, and(shr(i, key), 1)) + mstore(scratchPtr, keccak256(0, TwoWords)) + mstore(xor(scratchPtr, OneWord), mload(proof)) + } + + // Compute root hash. + root := keccak256(0, TwoWords) + } + + // Retrieve appropriate typehash constant based on height. + bytes32 rootTypeHash = _lookupBulkOrderTypehash(height); + + // Use the typehash and the root hash to derive final bulk order hash. + assembly { + mstore(0, rootTypeHash) + mstore(OneWord, root) + bulkOrderHash := keccak256(0, TwoWords) + } + } + + function _lookupBulkOrderTypehash(uint256 _treeHeight) internal pure returns (bytes32 _typeHash) { + // Utilize assembly to efficiently retrieve correct bulk order typehash. + assembly { + // Use a Yul function to enable use of the `leave` keyword + // to stop searching once the appropriate type hash is found. + function lookupTypeHash(treeHeight) -> typeHash { + // Handle tree heights one through eight. + if lt(treeHeight, 9) { + // Handle tree heights one through four. + if lt(treeHeight, 5) { + // Handle tree heights one and two. + if lt(treeHeight, 3) { + // Utilize branchless logic to determine typehash. + typeHash := ternary( + eq(treeHeight, 1), + BulkOrder_Typehash_Height_One, + BulkOrder_Typehash_Height_Two + ) + + // Exit the function once typehash has been located. + leave + } + + // Handle height three and four via branchless logic. + typeHash := ternary( + eq(treeHeight, 3), + BulkOrder_Typehash_Height_Three, + BulkOrder_Typehash_Height_Four + ) + + // Exit the function once typehash has been located. + leave + } + + // Handle tree height five and six. + if lt(treeHeight, 7) { + // Utilize branchless logic to determine typehash. + typeHash := ternary( + eq(treeHeight, 5), + BulkOrder_Typehash_Height_Five, + BulkOrder_Typehash_Height_Six + ) + + // Exit the function once typehash has been located. + leave + } + + // Handle height seven and eight via branchless logic. + typeHash := ternary( + eq(treeHeight, 7), + BulkOrder_Typehash_Height_Seven, + BulkOrder_Typehash_Height_Eight + ) + + // Exit the function once typehash has been located. + leave + } + + // Handle tree height nine through sixteen. + if lt(treeHeight, 17) { + // Handle tree height nine through twelve. + if lt(treeHeight, 13) { + // Handle tree height nine and ten. + if lt(treeHeight, 11) { + // Utilize branchless logic to determine typehash. + typeHash := ternary( + eq(treeHeight, 9), + BulkOrder_Typehash_Height_Nine, + BulkOrder_Typehash_Height_Ten + ) + + // Exit the function once typehash has been located. + leave + } + + // Handle height eleven and twelve via branchless logic. + typeHash := ternary( + eq(treeHeight, 11), + BulkOrder_Typehash_Height_Eleven, + BulkOrder_Typehash_Height_Twelve + ) + + // Exit the function once typehash has been located. + leave + } + + // Handle tree height thirteen and fourteen. + if lt(treeHeight, 15) { + // Utilize branchless logic to determine typehash. + typeHash := ternary( + eq(treeHeight, 13), + BulkOrder_Typehash_Height_Thirteen, + BulkOrder_Typehash_Height_Fourteen + ) + + // Exit the function once typehash has been located. + leave + } + // Handle height fifteen and sixteen via branchless logic. + typeHash := ternary( + eq(treeHeight, 15), + BulkOrder_Typehash_Height_Fifteen, + BulkOrder_Typehash_Height_Sixteen + ) + + // Exit the function once typehash has been located. + leave + } + + // Handle tree height seventeen through twenty. + if lt(treeHeight, 21) { + // Handle tree height seventeen and eighteen. + if lt(treeHeight, 19) { + // Utilize branchless logic to determine typehash. + typeHash := ternary( + eq(treeHeight, 17), + BulkOrder_Typehash_Height_Seventeen, + BulkOrder_Typehash_Height_Eighteen + ) + + // Exit the function once typehash has been located. + leave + } + + // Handle height nineteen and twenty via branchless logic. + typeHash := ternary( + eq(treeHeight, 19), + BulkOrder_Typehash_Height_Nineteen, + BulkOrder_Typehash_Height_Twenty + ) + + // Exit the function once typehash has been located. + leave + } + + // Handle tree height twenty-one and twenty-two. + if lt(treeHeight, 23) { + // Utilize branchless logic to determine typehash. + typeHash := ternary( + eq(treeHeight, 21), + BulkOrder_Typehash_Height_TwentyOne, + BulkOrder_Typehash_Height_TwentyTwo + ) + + // Exit the function once typehash has been located. + leave + } + + // Handle height twenty-three & twenty-four w/ branchless logic. + typeHash := ternary( + eq(treeHeight, 23), + BulkOrder_Typehash_Height_TwentyThree, + BulkOrder_Typehash_Height_TwentyFour + ) + + // Exit the function once typehash has been located. + leave + } + + // Implement ternary conditional using branchless logic. + function ternary(cond, ifTrue, ifFalse) -> c { + c := xor(ifFalse, mul(cond, xor(ifFalse, ifTrue))) + } + + // Look up the typehash using the supplied tree height. + _typeHash := lookupTypeHash(_treeHeight) + } + } + + function _deriveEIP712Digest(bytes32 domainSeparator, bytes32 orderHash) internal pure returns (bytes32 value) { + // Leverage scratch space to perform an efficient hash. + assembly { + // Place the EIP-712 prefix at the start of scratch space. + mstore(0, EIP_712_PREFIX) + + // Place the domain separator in the next region of scratch space. + mstore(EIP712_DomainSeparator_offset, domainSeparator) + + // Place the order hash in scratch space, spilling into the first + // two bytes of the free memory pointer — this should never be set + // as memory cannot be expanded to that size, and will be zeroed out + // after the hash is performed. + mstore(EIP712_OrderHash_offset, orderHash) + + // Hash the relevant region (65 bytes). + value := keccak256(0, EIP712_DigestPayload_size) + + // Clear out the dirtied bits in the memory pointer. + mstore(EIP712_OrderHash_offset, 0) + } + } +} diff --git a/contracts/prebuilts/account/utils/AccountSeaportBulkSigSupport.sol b/contracts/prebuilts/account/utils/AccountSeaportBulkSigSupport.sol new file mode 100644 index 000000000..e5a1dcb7a --- /dev/null +++ b/contracts/prebuilts/account/utils/AccountSeaportBulkSigSupport.sol @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity ^0.8.11; + +import { SeaportEIP1271 } from "../../../extension/SeaportEIP1271.sol"; +import { AccountPermissions, AccountPermissionsStorage } from "../../../extension/upgradeable/AccountPermissions.sol"; +import { EnumerableSet } from "../../../external-deps/openzeppelin/utils/structs/EnumerableSet.sol"; + +contract AccountSeaportBulkSigSupport is SeaportEIP1271 { + using EnumerableSet for EnumerableSet.AddressSet; + + constructor() SeaportEIP1271("Account", "1") {} + + /// @notice Returns whether a given signer is an authorized signer for the contract. + function _isAuthorizedSigner(address _signer) internal view virtual override returns (bool authorized) { + // is signer an admin? + if (AccountPermissionsStorage.data().isAdmin[_signer]) { + authorized = true; + } + + address caller = msg.sender; + EnumerableSet.AddressSet storage approvedTargets = AccountPermissionsStorage.data().approvedTargets[_signer]; + + require( + approvedTargets.contains(caller) || (approvedTargets.length() == 1 && approvedTargets.at(0) == address(0)), + "Account: caller not approved target." + ); + + // is signer an active signer of account? + AccountPermissions.SignerPermissionsStatic memory permissions = AccountPermissionsStorage + .data() + .signerPermissions[_signer]; + if ( + permissions.startTimestamp <= block.timestamp && + block.timestamp < permissions.endTimestamp && + AccountPermissionsStorage.data().approvedTargets[_signer].length() > 0 + ) { + authorized = true; + } + } +} diff --git a/foundry.toml b/foundry.toml index 1512dadaa..0a5adca0c 100644 --- a/foundry.toml +++ b/foundry.toml @@ -40,6 +40,9 @@ remappings = [ '@thirdweb-dev/dynamic-contracts/=lib/dynamic-contracts/', 'lib/sstore2=lib/dynamic-contracts/lib/sstore2/', '@solady/=lib/solady/', + '@seaport/=lib/seaport/contracts/', + 'seaport-types/=lib/seaport/lib/seaport-types/', + 'seaport-core/=lib/seaport/lib/seaport-core/' ] fs_permissions = [{ access = "read-write", path = "./src/test/smart-wallet/utils"}] src = 'contracts' diff --git a/lib/murky b/lib/murky new file mode 160000 index 000000000..40de6e801 --- /dev/null +++ b/lib/murky @@ -0,0 +1 @@ +Subproject commit 40de6e80117f39cda69d71b07b7c824adac91b29 diff --git a/lib/seaport b/lib/seaport new file mode 160000 index 000000000..1d12e33b7 --- /dev/null +++ b/lib/seaport @@ -0,0 +1 @@ +Subproject commit 1d12e33b71b6988cbbe955373ddbc40a87bd5b16 diff --git a/lib/seaport-core b/lib/seaport-core new file mode 160000 index 000000000..d4e8c74ad --- /dev/null +++ b/lib/seaport-core @@ -0,0 +1 @@ +Subproject commit d4e8c74adc472b311ab64b5c9f9757b5bba57a15 diff --git a/lib/seaport-sol b/lib/seaport-sol new file mode 160000 index 000000000..040d00576 --- /dev/null +++ b/lib/seaport-sol @@ -0,0 +1 @@ +Subproject commit 040d005768abafe3308b5f996aca3fd843d9c20e diff --git a/lib/seaport-types b/lib/seaport-types new file mode 160000 index 000000000..25bae8ddf --- /dev/null +++ b/lib/seaport-types @@ -0,0 +1 @@ +Subproject commit 25bae8ddfa8709e5c51ab429fe06024e46a18f15