@@ -62,11 +62,7 @@ library ComposableExecutionLib {
6262 // we don't restrict it since some calls may want to call address(0)
6363 // if a param with VALUE type was not provided, it will be 0
6464 // this is even more often case, as many calls happen with 0 value
65- return Execution ({
66- target: composedTarget,
67- value: composedValue,
68- callData: composedCalldata
69- });
65+ return Execution ({ target: composedTarget, value: composedValue, callData: composedCalldata });
7066 }
7167
7268 // Process a single input parameter and return the composed calldata
@@ -99,8 +95,7 @@ library ComposableExecutionLib {
9995
10096 // expect paramData to be abi.encodePacked(address token, address account)
10197 // Validate exact length requirement
102- require (paramData.length == 40 ,
103- InvalidParameterEncoding ("Invalid paramData length " ));
98+ require (paramData.length == 40 , InvalidParameterEncoding ("Invalid paramData length " ));
10499 assembly {
105100 tokenAddr := shr (96 , calldataload (paramData.offset))
106101 account := shr (96 , calldataload (add (paramData.offset, 0x14 )))
@@ -170,31 +165,54 @@ library ComposableExecutionLib {
170165 }
171166 }
172167
173- /// @dev Validate the constraints => compare the value with the reference data
168+ /// @dev Validate the constraints => compare the value with the reference data.
169+ /// Each constraints[i] is checked against the i-th 32-byte word of rawValue (AND semantics).
170+ /// Use ConstraintType.OR to express OR semantics within a single word position.
174171 function _validateConstraints (bytes memory rawValue , Constraint[] calldata constraints ) private pure {
175- if (constraints.length > 0 ) {
176- for (uint256 i; i < constraints.length ; i++ ) {
177- Constraint memory constraint = constraints[i];
178- bytes32 returnValue;
179- assembly {
180- returnValue := mload (add (rawValue, add (0x20 , mul (i, 0x20 ))))
181- }
182- if (constraint.constraintType == ConstraintType.EQ) {
183- require (returnValue == bytes32 (constraint.referenceData), ConstraintNotMet (ConstraintType.EQ));
184- } else if (constraint.constraintType == ConstraintType.GTE) {
185- require (returnValue >= bytes32 (constraint.referenceData), ConstraintNotMet (ConstraintType.GTE));
186- } else if (constraint.constraintType == ConstraintType.LTE) {
187- require (returnValue <= bytes32 (constraint.referenceData), ConstraintNotMet (ConstraintType.LTE));
188- } else if (constraint.constraintType == ConstraintType.IN) {
189- (bytes32 lowerBound , bytes32 upperBound ) = abi.decode (constraint.referenceData, (bytes32 , bytes32 ));
190- require (returnValue >= lowerBound && returnValue <= upperBound, ConstraintNotMet (ConstraintType.IN));
191- } else {
192- revert InvalidConstraintType ();
172+ uint256 len = constraints.length ;
173+ for (uint256 i; i < len; i++ ) {
174+ Constraint memory c = constraints[i];
175+ bytes32 value;
176+ assembly {
177+ value := mload (add (rawValue, add (0x20 , mul (i, 0x20 ))))
178+ }
179+ if (c.constraintType == ConstraintType.OR) {
180+ Constraint[] memory subs = abi.decode (c.referenceData, (Constraint[]));
181+ bool anyMet;
182+ for (uint256 j; j < subs.length ; j++ ) {
183+ if (_checkConstraint (value, subs[j])) {
184+ anyMet = true ;
185+ break ;
186+ }
193187 }
188+ if (! anyMet) revert ConstraintNotMet (ConstraintType.OR);
189+ } else {
190+ if (! _checkConstraint (value, c)) revert ConstraintNotMet (c.constraintType);
194191 }
195192 }
196193 }
197194
195+ /// @dev Returns true if value satisfies constraint c. OR nesting is not supported.
196+ function _checkConstraint (bytes32 value , Constraint memory c ) private pure returns (bool ) {
197+ ConstraintType ct = c.constraintType;
198+ if (ct == ConstraintType.EQ) {
199+ return value == bytes32 (c.referenceData);
200+ } else if (ct == ConstraintType.GTE) {
201+ return value >= bytes32 (c.referenceData);
202+ } else if (ct == ConstraintType.LTE) {
203+ return value <= bytes32 (c.referenceData);
204+ } else if (ct == ConstraintType.IN) {
205+ (bytes32 lower , bytes32 upper ) = abi.decode (c.referenceData, (bytes32 , bytes32 ));
206+ return value >= lower && value <= upper;
207+ } else if (ct == ConstraintType.GTE_SIGNED) {
208+ return int256 (uint256 (value)) >= int256 (uint256 (bytes32 (c.referenceData)));
209+ } else if (ct == ConstraintType.LTE_SIGNED) {
210+ return int256 (uint256 (value)) <= int256 (uint256 (bytes32 (c.referenceData)));
211+ } else {
212+ revert InvalidConstraintType ();
213+ }
214+ }
215+
198216 /// @dev Parse the return data and write to the appropriate storage contract
199217 function _parseReturnDataAndWriteToStorage (
200218 uint256 returnValues ,
0 commit comments