@@ -471,6 +471,13 @@ contract ComposableExecutionTestConstraintsAndReverts is ComposabilityTestBase {
471471 _inputParamUsingOrWithSignedConstraints (address (mockAccountDelegateCaller), address (mockAccountDelegateCaller));
472472 }
473473
474+ function test_Nested_Or_Reverts_With_InvalidConstraintType () public {
475+ _nestedOrReverts (address (mockAccount), address (mockAccount));
476+ _nestedOrReverts (address (mockAccountFallback), address (composabilityHandler));
477+ _nestedOrReverts (address (mockAccountCaller), address (composabilityHandler));
478+ _nestedOrReverts (address (mockAccountDelegateCaller), address (mockAccountDelegateCaller));
479+ }
480+
474481 // -----------------------------------------------------------------------
475482 // GTE_SIGNED: checks that int256(-5) is the lower bound.
476483 // value = int256(-10) => fails (below bound)
@@ -949,4 +956,52 @@ contract ComposableExecutionTestConstraintsAndReverts is ComposabilityTestBase {
949956
950957 vm.stopPrank ();
951958 }
959+
960+ // -----------------------------------------------------------------------
961+ // Nested OR is intentionally rejected to keep the signed payload flat and
962+ // easy to display. An OR whose sub-array contains another OR must revert
963+ // with InvalidConstraintType when _checkConstraint encounters the inner OR.
964+ // -----------------------------------------------------------------------
965+ function _nestedOrReverts (address account , address caller ) internal {
966+ // Inner OR with two leaf alternatives
967+ Constraint[] memory innerSubs = new Constraint [](2 );
968+ innerSubs[0 ] = Constraint ({ constraintType: ConstraintType.EQ, referenceData: abi.encode (bytes32 (uint256 (1 ))) });
969+ innerSubs[1 ] = Constraint ({ constraintType: ConstraintType.EQ, referenceData: abi.encode (bytes32 (uint256 (2 ))) });
970+
971+ // Outer OR whose second alternative is the inner OR (nesting)
972+ Constraint[] memory outerSubs = new Constraint [](2 );
973+ outerSubs[0 ] = Constraint ({ constraintType: ConstraintType.EQ, referenceData: abi.encode (bytes32 (uint256 (0 ))) });
974+ outerSubs[1 ] = Constraint ({ constraintType: ConstraintType.OR, referenceData: abi.encode (innerSubs) });
975+
976+ Constraint[] memory constraints = new Constraint [](1 );
977+ constraints[0 ] = Constraint ({ constraintType: ConstraintType.OR, referenceData: abi.encode (outerSubs) });
978+
979+ vm.startPrank (ENTRYPOINT_V07_ADDRESS);
980+
981+ // value = 7: outer OR's first alternative (EQ(0)) fails for value=7, so it reaches the
982+ // nested inner OR, which triggers InvalidConstraintType inside _checkConstraint.
983+ InputParam[] memory inputParams = new InputParam [](3 );
984+ inputParams[0 ] = InputParam ({
985+ paramType: InputParamType.CALL_DATA, fetcherType: InputParamFetcherType.RAW_BYTES, paramData: abi.encode (uint256 (7 )), constraints: constraints
986+ });
987+ inputParams[1 ] = _createRawTargetInputParam (address (0 ));
988+ inputParams[2 ] = _createRawValueInputParam (0 );
989+
990+ OutputParam[] memory outputParams = new OutputParam [](0 );
991+ ComposableExecution[] memory executions = new ComposableExecution [](1 );
992+ executions[0 ] = ComposableExecution ({ functionSig: "" , inputParams: inputParams, outputParams: outputParams });
993+
994+ bytes memory expectedRevert;
995+ if (address (account) == address (mockAccountFallback)) {
996+ expectedRevert = abi.encodeWithSelector (
997+ MockAccountFallback.FallbackFailed.selector , abi.encodeWithSelector (ComposableExecutionLib.InvalidConstraintType.selector )
998+ );
999+ } else {
1000+ expectedRevert = abi.encodeWithSelector (ComposableExecutionLib.InvalidConstraintType.selector );
1001+ }
1002+ vm.expectRevert (expectedRevert);
1003+ IComposableExecution (address (account)).executeComposable (executions);
1004+
1005+ vm.stopPrank ();
1006+ }
9521007}
0 commit comments