@@ -7,6 +7,7 @@ import {ComposableExecutionModule} from "contracts/composability/ComposableExecu
77import {Storage} from "contracts/composability/Storage.sol " ;
88import {IComposableExecution} from "contracts/interfaces/IComposableExecution.sol " ;
99import {ComposableExecution, InputParam, OutputParam, ParamValueType, OutputParamFetcherType, InputParamFetcherType} from "contracts/composability/ComposableExecutionLib.sol " ;
10+ import { CONSTRAINT_TYPE_EQ, CONSTRAINT_TYPE_GTE, CONSTRAINT_TYPE_LTE, CONSTRAINT_TYPE_IN } from "contracts/types/Constants.sol " ;
1011
1112event Uint256Emitted (uint256 value );
1213event Uint256Emitted2 (uint256 value1 , uint256 value2 );
@@ -110,12 +111,268 @@ contract ComposableExecutionTest is ComposabilityTestBase {
110111 _outputExecResultAddress (address (mockAccount), address (mockAccount));
111112 }
112113
114+ function test_inputsWithConstraints () public {
115+ _inputParamUsingGteConstraints (address (mockAccount), address (mockAccount));
116+ _inputParamUsingLteConstraints (address (mockAccount), address (mockAccount));
117+ _inputParamUsingInConstraints (address (mockAccount), address (mockAccount));
118+ _inputParamUsingEqConstraints (address (mockAccount), address (mockAccount));
119+ }
120+
113121 // TODO:
114122 // test that input value works correctly with all types: address, bool.
115123
116124
117125 // ================================ TEST SCENARIOS ================================
118126
127+ function _inputParamUsingGteConstraints (address account , address caller ) internal {
128+ vm.startPrank (ENTRYPOINT_V07_ADDRESS);
129+
130+ // Prepare invalid input param - call should revert
131+ InputParam[] memory invalidInputParams = new InputParam [](1 );
132+ invalidInputParams[0 ] = InputParam ({
133+ fetcherType: InputParamFetcherType.RAW_BYTES,
134+ valueType: ParamValueType.UINT256,
135+ paramData: abi.encode (42 ),
136+ constraints: abi.encodePacked (CONSTRAINT_TYPE_GTE, bytes32 (uint256 (43 ))) // value must be >= 43 but 42 provided
137+ });
138+
139+ // Prepare valid input param - call should succeed
140+ InputParam[] memory validInputParams = new InputParam [](1 );
141+ validInputParams[0 ] = InputParam ({
142+ fetcherType: InputParamFetcherType.RAW_BYTES,
143+ valueType: ParamValueType.UINT256,
144+ paramData: abi.encode (43 ),
145+ constraints: abi.encodePacked (CONSTRAINT_TYPE_GTE, bytes32 (uint256 (43 ))) // value must be >= 42
146+ });
147+
148+ OutputParam[] memory outputParams = new OutputParam [](1 );
149+ outputParams[0 ] = OutputParam ({
150+ fetcherType: OutputParamFetcherType.STATIC_CALL,
151+ valueType: ParamValueType.UINT256,
152+ paramData: abi.encode (address (dummyContract), abi.encodeWithSelector (DummyContract.A.selector ), address (storageContract), SLOT_A)
153+ });
154+
155+ // Call empty function and it should revert because dynamic param value doesnt meet constraints
156+ ComposableExecution[] memory failingExecutions = new ComposableExecution [](1 );
157+ failingExecutions[0 ] = ComposableExecution ({
158+ to: address (0 ), // no function call
159+ value: 0 , // no value sent
160+ functionSig: "" , // no calldata encoded
161+ inputParams: invalidInputParams, // use constrainted input parameter that's going to fail
162+ outputParams: outputParams
163+ });
164+ vm.expectRevert (bytes ("GTE constraint not met. " ));
165+ IComposableExecution (address (account)).executeComposable (failingExecutions);
166+
167+ // Call empty function and it should NOT revert because dynamic param value meets constraints
168+ ComposableExecution[] memory validExecutions = new ComposableExecution [](1 );
169+ validExecutions[0 ] = ComposableExecution ({
170+ to: address (0 ), // no function call
171+ value: 0 , // no value sent
172+ functionSig: "" , // no calldata encoded
173+ inputParams: validInputParams, // use valid input params
174+ outputParams: outputParams
175+ });
176+ IComposableExecution (address (account)).executeComposable (validExecutions);
177+
178+ // Expect valid composable execution to store result as per given output params
179+ bytes32 namespace = storageContract.getNamespace (address (account), address (caller));
180+ bytes32 storedValueA = storageContract.readStorage (namespace, SLOT_A);
181+ assertEq (uint256 (storedValueA), 42 , "Function A result not stored correctly " );
182+ }
183+
184+ function _inputParamUsingLteConstraints (address account , address caller ) internal {
185+ vm.startPrank (ENTRYPOINT_V07_ADDRESS);
186+
187+ // Prepare invalid input param - call should revert
188+ InputParam[] memory invalidInputParams = new InputParam [](1 );
189+ invalidInputParams[0 ] = InputParam ({
190+ fetcherType: InputParamFetcherType.RAW_BYTES,
191+ valueType: ParamValueType.UINT256,
192+ paramData: abi.encode (42 ),
193+ constraints: abi.encodePacked (CONSTRAINT_TYPE_LTE, bytes32 (uint256 (41 ))) // value must be <= 41 but 42 provided
194+ });
195+
196+ // Prepare valid input param - call should succeed
197+ InputParam[] memory validInputParams = new InputParam [](1 );
198+ validInputParams[0 ] = InputParam ({
199+ fetcherType: InputParamFetcherType.RAW_BYTES,
200+ valueType: ParamValueType.UINT256,
201+ paramData: abi.encode (41 ),
202+ constraints: abi.encodePacked (CONSTRAINT_TYPE_LTE, bytes32 (uint256 (41 ))) // value must be <= 41
203+ });
204+
205+ OutputParam[] memory outputParams = new OutputParam [](1 );
206+ outputParams[0 ] = OutputParam ({
207+ fetcherType: OutputParamFetcherType.STATIC_CALL,
208+ valueType: ParamValueType.UINT256,
209+ paramData: abi.encode (address (dummyContract), abi.encodeWithSelector (DummyContract.A.selector ), address (storageContract), SLOT_A)
210+ });
211+
212+ // Call empty function and it should revert because dynamic param value doesnt meet constraints
213+ ComposableExecution[] memory failingExecutions = new ComposableExecution [](1 );
214+ failingExecutions[0 ] = ComposableExecution ({
215+ to: address (0 ), // no function call
216+ value: 0 , // no value sent
217+ functionSig: "" , // no calldata encoded
218+ inputParams: invalidInputParams, // use constrainted input parameter that's going to fail
219+ outputParams: outputParams
220+ });
221+ vm.expectRevert (bytes ("LTE constraint not met. " ));
222+ IComposableExecution (address (account)).executeComposable (failingExecutions);
223+
224+ // Call empty function and it should NOT revert because dynamic param value meets constraints
225+ ComposableExecution[] memory validExecutions = new ComposableExecution [](1 );
226+ validExecutions[0 ] = ComposableExecution ({
227+ to: address (0 ), // no function call
228+ value: 0 , // no value sent
229+ functionSig: "" , // no calldata encoded
230+ inputParams: validInputParams, // use valid input params
231+ outputParams: outputParams
232+ });
233+ IComposableExecution (address (account)).executeComposable (validExecutions);
234+
235+ // Expect valid composable execution to store result as per given output params
236+ bytes32 namespace = storageContract.getNamespace (address (account), address (caller));
237+ bytes32 storedValueA = storageContract.readStorage (namespace, SLOT_A);
238+ assertEq (uint256 (storedValueA), 42 , "Function A result not stored correctly " );
239+ }
240+
241+ function _inputParamUsingInConstraints (address account , address caller ) internal {
242+ vm.startPrank (ENTRYPOINT_V07_ADDRESS);
243+
244+ // Prepare invalid input param - call should revert (param value below lowerBound)
245+ InputParam[] memory invalidInputParamsA = new InputParam [](1 );
246+ invalidInputParamsA[0 ] = InputParam ({
247+ fetcherType: InputParamFetcherType.RAW_BYTES,
248+ valueType: ParamValueType.UINT256,
249+ paramData: abi.encode (40 ),
250+ constraints: abi.encodePacked (CONSTRAINT_TYPE_IN, abi.encode (bytes32 (uint256 (41 )), bytes32 (uint256 (43 )))) // value must be between 41 & 43
251+ });
252+
253+ // Prepare invalid input param - call should revert (param value above upperBound)
254+ InputParam[] memory invalidInputParamsB = new InputParam [](1 );
255+ invalidInputParamsB[0 ] = InputParam ({
256+ fetcherType: InputParamFetcherType.RAW_BYTES,
257+ valueType: ParamValueType.UINT256,
258+ paramData: abi.encode (44 ),
259+ constraints: abi.encodePacked (CONSTRAINT_TYPE_IN, abi.encode (bytes32 (uint256 (41 )), bytes32 (uint256 (43 )))) // value must be between 41 & 43
260+ });
261+
262+ // Prepare valid input param - call should succeed (param value in bounds)
263+ InputParam[] memory validInputParams = new InputParam [](1 );
264+ validInputParams[0 ] = InputParam ({
265+ fetcherType: InputParamFetcherType.RAW_BYTES,
266+ valueType: ParamValueType.UINT256,
267+ paramData: abi.encode (42 ),
268+ constraints: abi.encodePacked (CONSTRAINT_TYPE_IN, abi.encode (bytes32 (uint256 (41 )), bytes32 (uint256 (43 )))) // value must be between 41 & 43
269+ });
270+
271+ OutputParam[] memory outputParams = new OutputParam [](1 );
272+ outputParams[0 ] = OutputParam ({
273+ fetcherType: OutputParamFetcherType.STATIC_CALL,
274+ valueType: ParamValueType.UINT256,
275+ paramData: abi.encode (address (dummyContract), abi.encodeWithSelector (DummyContract.A.selector ), address (storageContract), SLOT_A)
276+ });
277+
278+ // Call empty function and it should revert because dynamic param value doesnt meet constraints (value below lower bound)
279+ ComposableExecution[] memory failingExecutionsA = new ComposableExecution [](1 );
280+ failingExecutionsA[0 ] = ComposableExecution ({
281+ to: address (0 ), // no function call
282+ value: 0 , // no value sent
283+ functionSig: "" , // no calldata encoded
284+ inputParams: invalidInputParamsA, // use constrainted input parameter that's going to fail
285+ outputParams: outputParams
286+ });
287+ vm.expectRevert (bytes ("IN constraint not met " ));
288+ IComposableExecution (address (account)).executeComposable (failingExecutionsA);
289+
290+ // Call empty function and it should revert because dynamic param value doesnt meet constraints (value below lower bound)
291+ ComposableExecution[] memory failingExecutionsB = new ComposableExecution [](1 );
292+ failingExecutionsB[0 ] = ComposableExecution ({
293+ to: address (0 ), // no function call
294+ value: 0 , // no value sent
295+ functionSig: "" , // no calldata encoded
296+ inputParams: invalidInputParamsB, // use constrainted input parameter that's going to fail
297+ outputParams: outputParams
298+ });
299+ vm.expectRevert (bytes ("IN constraint not met " ));
300+ IComposableExecution (address (account)).executeComposable (failingExecutionsB);
301+
302+ // Call empty function and it should NOT revert because dynamic param value meets constraints
303+ ComposableExecution[] memory validExecutions = new ComposableExecution [](1 );
304+ validExecutions[0 ] = ComposableExecution ({
305+ to: address (0 ), // no function call
306+ value: 0 , // no value sent
307+ functionSig: "" , // no calldata encoded
308+ inputParams: validInputParams, // use valid input params
309+ outputParams: outputParams
310+ });
311+ IComposableExecution (address (account)).executeComposable (validExecutions);
312+
313+ // Expect valid composable execution to store result as per given output params
314+ bytes32 namespace = storageContract.getNamespace (address (account), address (caller));
315+ bytes32 storedValueA = storageContract.readStorage (namespace, SLOT_A);
316+ assertEq (uint256 (storedValueA), 42 , "Function A result not stored correctly " );
317+ }
318+
319+ function _inputParamUsingEqConstraints (address account , address caller ) internal {
320+ vm.startPrank (ENTRYPOINT_V07_ADDRESS);
321+
322+ // Prepare invalid input param - call should revert
323+ InputParam[] memory invalidInputParams = new InputParam [](1 );
324+ invalidInputParams[0 ] = InputParam ({
325+ fetcherType: InputParamFetcherType.RAW_BYTES,
326+ valueType: ParamValueType.UINT256,
327+ paramData: abi.encode (43 ),
328+ constraints: abi.encodePacked (CONSTRAINT_TYPE_EQ, bytes32 (uint256 (42 ))) // value must be exactly 42
329+ });
330+
331+ // Prepare valid input param - call should succeed
332+ InputParam[] memory validInputParams = new InputParam [](1 );
333+ validInputParams[0 ] = InputParam ({
334+ fetcherType: InputParamFetcherType.RAW_BYTES,
335+ valueType: ParamValueType.UINT256,
336+ paramData: abi.encode (42 ),
337+ constraints: abi.encodePacked (CONSTRAINT_TYPE_EQ, bytes32 (uint256 (42 ))) // value must be exactly 42
338+ });
339+
340+ OutputParam[] memory outputParams = new OutputParam [](1 );
341+ outputParams[0 ] = OutputParam ({
342+ fetcherType: OutputParamFetcherType.STATIC_CALL,
343+ valueType: ParamValueType.UINT256,
344+ paramData: abi.encode (address (dummyContract), abi.encodeWithSelector (DummyContract.A.selector ), address (storageContract), SLOT_A)
345+ });
346+
347+ // Call empty function and it should revert because dynamic param value doesnt meet constraints
348+ ComposableExecution[] memory failingExecutions = new ComposableExecution [](1 );
349+ failingExecutions[0 ] = ComposableExecution ({
350+ to: address (0 ), // no function call
351+ value: 0 , // no value sent
352+ functionSig: "" , // no calldata encoded
353+ inputParams: invalidInputParams, // use constrainted input parameter that's going to fail
354+ outputParams: outputParams
355+ });
356+ vm.expectRevert (bytes ("EQ constraint not met. " ));
357+ IComposableExecution (address (account)).executeComposable (failingExecutions);
358+
359+ // Call empty function and it should NOT revert because dynamic param value meets constraints
360+ ComposableExecution[] memory validExecutions = new ComposableExecution [](1 );
361+ validExecutions[0 ] = ComposableExecution ({
362+ to: address (0 ), // no function call
363+ value: 0 , // no value sent
364+ functionSig: "" , // no calldata encoded
365+ inputParams: validInputParams, // use valid input params
366+ outputParams: outputParams
367+ });
368+ IComposableExecution (address (account)).executeComposable (validExecutions);
369+
370+ // Expect valid composable execution to store result as per given output params
371+ bytes32 namespace = storageContract.getNamespace (address (account), address (caller));
372+ bytes32 storedValueA = storageContract.readStorage (namespace, SLOT_A);
373+ assertEq (uint256 (storedValueA), 42 , "Function A result not stored correctly " );
374+ }
375+
119376 function _inputStaticCallOutputExecResult (address account , address caller ) internal {
120377 vm.startPrank (ENTRYPOINT_V07_ADDRESS);
121378
@@ -152,7 +409,8 @@ contract ComposableExecutionTest is ComposabilityTestBase {
152409 inputParamsB[0 ] = InputParam ({
153410 fetcherType: InputParamFetcherType.STATIC_CALL,
154411 valueType: ParamValueType.UINT256,
155- paramData: abi.encode (storageContract, abi.encodeCall (Storage.readStorage, (namespace, SLOT_A)))
412+ paramData: abi.encode (storageContract, abi.encodeCall (Storage.readStorage, (namespace, SLOT_A))),
413+ constraints: ""
156414 });
157415
158416 // Prepare return value config for function B
@@ -190,7 +448,8 @@ contract ComposableExecutionTest is ComposabilityTestBase {
190448 inputParams[0 ] = InputParam ({
191449 fetcherType: InputParamFetcherType.RAW_BYTES,
192450 valueType: ParamValueType.UINT256,
193- paramData: abi.encode (1 )
451+ paramData: abi.encode (1 ),
452+ constraints: ""
194453 });
195454
196455 // Prepare return value config for function B
@@ -262,12 +521,14 @@ contract ComposableExecutionTest is ComposabilityTestBase {
262521 inputParams_execution1[0 ] = InputParam ({
263522 fetcherType: InputParamFetcherType.RAW_BYTES,
264523 valueType: ParamValueType.UINT256,
265- paramData: abi.encode (input1)
524+ paramData: abi.encode (input1),
525+ constraints: ""
266526 });
267527 inputParams_execution1[1 ] = InputParam ({
268528 fetcherType: InputParamFetcherType.RAW_BYTES,
269529 valueType: ParamValueType.UINT256,
270- paramData: abi.encode (input2)
530+ paramData: abi.encode (input2),
531+ constraints: ""
271532 });
272533
273534 OutputParam[] memory outputParams_execution1 = new OutputParam [](2 );
@@ -289,12 +550,14 @@ contract ComposableExecutionTest is ComposabilityTestBase {
289550 inputParams_execution2[0 ] = InputParam ({
290551 fetcherType: InputParamFetcherType.STATIC_CALL,
291552 valueType: ParamValueType.UINT256,
292- paramData: abi.encode (storageContract, abi.encodeCall (Storage.readStorage, (namespace, SLOT_A)))
553+ paramData: abi.encode (storageContract, abi.encodeCall (Storage.readStorage, (namespace, SLOT_A))),
554+ constraints: ""
293555 });
294556 inputParams_execution2[1 ] = InputParam ({
295557 fetcherType: InputParamFetcherType.STATIC_CALL,
296558 valueType: ParamValueType.UINT256,
297- paramData: abi.encode (storageContract, abi.encodeCall (Storage.readStorage, (namespace, SLOT_B)))
559+ paramData: abi.encode (storageContract, abi.encodeCall (Storage.readStorage, (namespace, SLOT_B))),
560+ constraints: ""
298561 });
299562 OutputParam[] memory outputParams_execution2 = new OutputParam [](0 );
300563
0 commit comments