Seaport bulk sig support for smart accounts: AccountSeaportBulkSigSupport#633
Conversation
| 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 { |
Check warning
Code scanning / Slither
Write after write
| 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 { |
Check warning
Code scanning / Slither
Write after write
| 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 { |
Check warning
Code scanning / Slither
Write after write
| 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 { |
Check warning
Code scanning / Slither
Write after write
| 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 { |
Check warning
Code scanning / Slither
Write after write
| 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 | ||
| } |
Check warning
Code scanning / Slither
Assembly usage
| function ternary(cond, ifTrue, ifFalse) -> c { | ||
| c := xor(ifFalse, mul(cond, xor(ifFalse, ifTrue))) | ||
| } |
Check warning
Code scanning / Slither
Assembly usage
| 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 | ||
| } |
Check warning
Code scanning / Slither
Cyclomatic complexity
| 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) | ||
| } | ||
| } |
Check warning
Code scanning / Slither
Assembly usage
| 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) | ||
| } | ||
| } |
Check warning
Code scanning / Slither
Assembly usage
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #633 +/- ##
==========================================
- Coverage 65.26% 64.69% -0.58%
==========================================
Files 217 220 +3
Lines 6847 6908 +61
==========================================
Hits 4469 4469
- Misses 2378 2439 +61 ☔ View full report in Codecov by Sentry. |
No description provided.