@@ -91,6 +91,14 @@ contract SequenceV3IntegrationTest is Test {
9191 }
9292 }
9393
94+ function _abiDynamicArgDataOffset (bytes memory callData , uint256 argIndex ) private pure returns (uint256 offset ) {
95+ uint256 tailOffset;
96+ assembly ("memory-safe" ) {
97+ tailOffset := mload (add (add (callData, 36 ), shl (5 , argIndex)))
98+ }
99+ return 4 + tailOffset + 32 ;
100+ }
101+
94102 function _randomBytes (uint256 len , bytes32 seed ) private pure returns (bytes memory data ) {
95103 data = new bytes (len);
96104
@@ -339,6 +347,88 @@ contract SequenceV3IntegrationTest is Test {
339347 assertEq (receiver.lastData (), dataB);
340348 }
341349
350+ function test_integration_sapientStaticHydrateRange_isNotShiftedByPrependedTypeZeroCommand () external {
351+ TrailsUtils trailsUtils = new TrailsUtils ();
352+
353+ RecordingReceiver receiver = new RecordingReceiver ();
354+ address signedReceiver = makeAddr ("signedReceiver " );
355+ address relayer = makeAddr ("relayer " );
356+ address origin = makeAddr ("origin " );
357+
358+ LocalPayload.Call[] memory innerCalls = new LocalPayload.Call [](1 );
359+ innerCalls[0 ] = LocalPayload.Call ({
360+ to: address (receiver),
361+ value: 0 ,
362+ data: new bytes (20 ),
363+ gasLimit: 0 ,
364+ delegateCall: false ,
365+ onlyFallback: false ,
366+ behaviorOnError: LocalPayload.BEHAVIOR_REVERT_ON_ERROR
367+ });
368+
369+ bytes memory innerPacked = innerCalls.packCalls ();
370+ bytes memory hydratePayload = bytes .concat (
371+ abi.encodePacked (uint8 (0 )),
372+ abi.encodePacked (uint8 (0x13 ), signedReceiver, uint16 (0 )),
373+ abi.encodePacked (uint8 (0x00 ))
374+ );
375+ bytes memory shiftedHydratePayload = bytes .concat (
376+ abi.encodePacked (uint8 (0 )),
377+ abi.encodePacked (uint8 (0x01 ), uint16 (0xBEEF )),
378+ abi.encodePacked (uint8 (0x13 ), signedReceiver, uint16 (0 )),
379+ abi.encodePacked (uint8 (0x00 ))
380+ );
381+
382+ bytes memory outerData = abi.encodeWithSelector (HydrateProxy.hydrateExecute.selector , innerPacked, hydratePayload);
383+ uint16 signedRangeOffset = uint16 (_abiDynamicArgDataOffset (outerData, 1 ) + 2 );
384+ bytes memory sapientSig = abi.encodePacked (uint8 (0 ), signedRangeOffset, uint16 (20 ));
385+
386+ LocalPayload.Call[] memory outerCalls = new LocalPayload.Call [](1 );
387+ outerCalls[0 ] = LocalPayload.Call ({
388+ to: address (trailsUtils),
389+ value: 0 ,
390+ data: outerData,
391+ gasLimit: 0 ,
392+ delegateCall: true ,
393+ onlyFallback: false ,
394+ behaviorOnError: LocalPayload.BEHAVIOR_REVERT_ON_ERROR
395+ });
396+
397+ bytes memory shiftedOuterData =
398+ abi.encodeWithSelector (HydrateProxy.hydrateExecute.selector , innerPacked, shiftedHydratePayload);
399+ LocalPayload.Call[] memory shiftedOuterCalls = new LocalPayload.Call [](1 );
400+ shiftedOuterCalls[0 ] = LocalPayload.Call ({
401+ to: address (trailsUtils),
402+ value: 0 ,
403+ data: shiftedOuterData,
404+ gasLimit: 0 ,
405+ delegateCall: true ,
406+ onlyFallback: false ,
407+ behaviorOnError: LocalPayload.BEHAVIOR_REVERT_ON_ERROR
408+ });
409+
410+ bytes memory outerPacked = outerCalls.packCalls ();
411+ bytes memory shiftedOuterPacked = shiftedOuterCalls.packCalls ();
412+
413+ SeqPayload.Decoded memory payload = _asSeqPayload (outerCalls, 0 , 0 );
414+ bytes32 sapientImageHash = ISapient (address (trailsUtils)).recoverSapientSignature (payload, sapientSig);
415+ address wallet = _deployWalletWithSapient (address (trailsUtils), sapientImageHash);
416+ bytes memory signature = _buildSapientSignature (address (trailsUtils), 1 , 1 , sapientSig);
417+
418+ vm.prank (relayer, origin);
419+ vm.expectRevert ();
420+ SeqStage1Module (payable (wallet)).execute (shiftedOuterPacked, signature);
421+
422+ assertEq (SeqStage1Module (payable (wallet)).readNonce (0 ), 0 );
423+
424+ vm.prank (relayer, origin);
425+ SeqStage1Module (payable (wallet)).execute (outerPacked, signature);
426+
427+ assertEq (receiver.lastSender (), wallet);
428+ assertEq (_readAddress (receiver.lastData (), 0 ), signedReceiver);
429+ assertEq (SeqStage1Module (payable (wallet)).readNonce (0 ), 1 );
430+ }
431+
342432 function test_integration_walletDelegatecallsHydrateProxy_andHydrates () external {
343433 TrailsUtils trailsUtils = new TrailsUtils ();
344434
0 commit comments