@@ -21,14 +21,17 @@ library SessionLib {
2121 error ZeroSigner ();
2222 error InvalidSigner (address recovered , address expected );
2323 error InvalidCallType (bytes1 callType , bytes1 expected );
24+ error InvalidExecType (bytes1 execType );
2425 error InvalidTopLevelSelector (bytes4 selector , bytes4 expected );
26+ error InvalidData ();
2527 error SessionAlreadyExists (bytes32 sessionHash );
2628 error UnlimitedFees ();
2729 error SessionExpiresTooSoon (uint256 expiresAt );
2830 error SessionNotActive ();
31+ error EmptyTimeRange ();
2932 error LifetimeUsageExceeded (uint256 lifetimeUsage , uint256 maxUsage );
3033 error AllowanceExceeded (uint256 allowance , uint256 maxAllowance , uint64 period );
31- error InvalidDataLength (uint256 actualLength , uint256 expectedMinimumLength );
34+ error InvalidDataLength (uint256 actualLength , uint256 expectedLength );
3235 error ConditionViolated (bytes32 param , bytes32 refValue , uint8 condition );
3336 error CallPolicyViolated (address target , bytes4 selector );
3437 error TransferPolicyViolated (address target );
@@ -159,24 +162,24 @@ library SessionLib {
159162 /// @dev Reverts if the limit is exceeded or the period is invalid.
160163 function checkAndUpdate (UsageLimit memory limit , UsageTracker storage tracker , uint256 value , uint48 period )
161164 internal
162- returns (uint48 validAfter , uint48 validUntil )
165+ returns (uint48 [ 2 ] memory validTimeRange )
163166 {
167+ validTimeRange = [0 , type (uint48 ).max];
164168 if (limit.limitType == LimitType.Lifetime) {
165- validAfter = 0 ;
166- validUntil = type (uint48 ).max;
167169 require (
168170 tracker.lifetimeUsage[msg .sender ] + value <= limit.limit,
169171 LifetimeUsageExceeded (tracker.lifetimeUsage[msg .sender ], limit.limit)
170172 );
171173 tracker.lifetimeUsage[msg .sender ] += value;
172174 } else if (limit.limitType == LimitType.Allowance) {
173- validAfter = period * limit.period;
174- validUntil = (period + 1 ) * limit.period;
175175 require (
176176 tracker.allowanceUsage[period][msg .sender ] + value <= limit.limit,
177177 AllowanceExceeded (tracker.allowanceUsage[period][msg .sender ], limit.limit, period)
178178 );
179179 tracker.allowanceUsage[period][msg .sender ] += value;
180+ uint48 validAfter = period * limit.period;
181+ uint48 validUntil = (period + 1 ) * limit.period - 1 ;
182+ validTimeRange = [validAfter, validUntil];
180183 }
181184 }
182185
@@ -192,7 +195,7 @@ library SessionLib {
192195 UsageTracker storage tracker ,
193196 bytes memory data ,
194197 uint48 period
195- ) internal returns (uint48 , uint48 ) {
198+ ) internal returns (uint48 [ 2 ] memory validTimeRange ) {
196199 uint256 expectedLength = 4 + constraint.index * 32 + 32 ;
197200 if (data.length < expectedLength) {
198201 revert InvalidDataLength (data.length , expectedLength);
@@ -237,22 +240,34 @@ library SessionLib {
237240 PackedUserOperation calldata userOp ,
238241 SessionSpec memory spec ,
239242 uint48 periodId
240- ) internal returns (uint48 validAfter , uint48 validUntil ) {
243+ ) internal returns (uint48 [ 2 ] memory validTimeRange ) {
241244 // If a paymaster is paying the fee, we don't need to check the fee limit
242245 if (userOp.paymasterAndData.length == 0 ) {
243246 uint256 gasPrice = userOp.gasPrice ();
244- uint256 gasLimit = userOp.unpackVerificationGasLimit () + userOp.unpackCallGasLimit ();
247+ uint256 gasLimit =
248+ userOp.preVerificationGas + userOp.unpackVerificationGasLimit () + userOp.unpackCallGasLimit ();
245249 uint256 fee = gasPrice * gasLimit;
246250 // slither-disable-next-line unused-return
247251 return spec.feeLimit.checkAndUpdate (state.fee, fee, periodId);
248252 } else {
249- return ( 0 , type (uint48 ).max) ;
253+ return [ 0 , type (uint48 ).max] ;
250254 }
251255 }
252256
253- function shrinkRange (uint48 [2 ] memory range , uint48 newAfter , uint48 newUntil ) internal pure {
254- range[0 ] = newAfter > range[0 ] ? newAfter : range[0 ];
255- range[1 ] = newUntil < range[1 ] ? newUntil : range[1 ];
257+ /// @notice Shrinks the time range to the intersection of itself and the new range.
258+ /// @param range The original time range to shrink: validAfter, validUntil
259+ /// @param otherRange The new time range to intersect with: newValidAfter, newValidUntil
260+ /// @return newRange The shrunk time range: validAfter, validUntil
261+ function shrinkRange (uint48 [2 ] memory range , uint48 [2 ] memory otherRange )
262+ internal
263+ pure
264+ returns (uint48 [2 ] memory newRange )
265+ {
266+ newRange = [
267+ otherRange[0 ] > range[0 ] ? otherRange[0 ] : range[0 ], // max(validAfter, newValidAfter)
268+ otherRange[1 ] < range[1 ] ? otherRange[1 ] : range[1 ] // min(validUntil, newValidUntil)
269+ ];
270+ require (newRange[0 ] <= newRange[1 ], EmptyTimeRange ());
256271 }
257272
258273 /// @notice Validates the transaction against the session spec and updates the usage trackers.
@@ -272,13 +287,17 @@ library SessionLib {
272287 PackedUserOperation calldata userOp ,
273288 SessionSpec memory spec ,
274289 uint48 [] memory periodIds
275- ) internal returns (uint48 , uint48 ) {
290+ ) internal returns (uint48 [ 2 ] memory validTimeRange ) {
276291 require (state.status[msg .sender ] == Status.Active, SessionNotActive ());
277292
278293 bytes4 topLevelSelector = bytes4 (userOp.callData[:4 ]);
279294 bytes1 callType = userOp.callData[4 ];
295+ bytes1 execType = userOp.callData[5 ];
280296
281297 require (callType == LibERC7579.CALLTYPE_SINGLE, InvalidCallType (callType, LibERC7579.CALLTYPE_SINGLE));
298+ require (
299+ execType == LibERC7579.EXECTYPE_DEFAULT || execType == LibERC7579.EXECTYPE_TRY, InvalidExecType (execType)
300+ );
282301 require (
283302 topLevelSelector == IERC7579Account .execute.selector ,
284303 InvalidTopLevelSelector (topLevelSelector, IERC7579Account .execute.selector )
@@ -288,15 +307,20 @@ library SessionLib {
288307 // - first 4 bytes: selector
289308 // - next 32 bytes: mode
290309 // - next 32 bytes: data offset
291- // - at offset : data length
310+ // - next 32 bytes : data length
292311 // - next 32 bytes: data
293- uint256 offset = uint256 (bytes32 (userOp.callData[36 :68 ])) + 4 ; // offset does not include the selector
294- uint256 length = uint256 (bytes32 (userOp.callData[offset:offset + 32 ]));
312+ uint256 offsetOffset = 4 + 32 ;
313+ uint256 lengthOffset = 4 + 32 + 32 ;
314+ uint256 dataOffset = 4 + 32 + 32 + 32 ;
315+ // Offset does not include the selector, hence +4
316+ uint256 offset = uint256 (bytes32 (userOp.callData[offsetOffset:offsetOffset + 32 ])) + 4 ;
317+ require (offset == lengthOffset, InvalidData ());
318+ uint256 length = uint256 (bytes32 (userOp.callData[lengthOffset:lengthOffset + 32 ]));
295319 (address target , uint256 value , bytes calldata callData ) =
296- LibERC7579.decodeSingle (userOp.callData[offset + 32 :offset + 32 + length]);
320+ LibERC7579.decodeSingle (userOp.callData[dataOffset:dataOffset + length]);
297321
298322 // Time range within which the transaction is valid.
299- uint48 [ 2 ] memory timeRange = [0 , spec.expiresAt];
323+ validTimeRange = [0 , spec.expiresAt];
300324
301325 if (callData.length >= 4 ) {
302326 bytes4 selector = bytes4 (callData[:4 ]);
@@ -313,17 +337,21 @@ library SessionLib {
313337
314338 require (found, CallPolicyViolated (target, selector));
315339 require (value <= callPolicy.maxValuePerUse, MaxValueExceeded (value, callPolicy.maxValuePerUse));
316- (uint48 newValidAfter , uint48 newValidUntil ) =
317- callPolicy.valueLimit.checkAndUpdate (state.callValue[target][selector], value, periodIds[1 ]);
318- shrinkRange (timeRange, newValidAfter, newValidUntil);
340+ validTimeRange = shrinkRange (
341+ validTimeRange,
342+ callPolicy.valueLimit.checkAndUpdate (state.callValue[target][selector], value, periodIds[1 ])
343+ );
319344
320345 for (uint256 i = 0 ; i < callPolicy.constraints.length ; ++ i) {
321- (newValidAfter, newValidUntil) = callPolicy.constraints[i].checkAndUpdate (
322- state.params[target][selector][i], callData, periodIds[2 + i]
346+ validTimeRange = shrinkRange (
347+ validTimeRange,
348+ callPolicy.constraints[i].checkAndUpdate (
349+ state.params[target][selector][i], callData, periodIds[2 + i]
350+ )
323351 );
324- shrinkRange (timeRange, newValidAfter, newValidUntil);
325352 }
326353 } else {
354+ require (callData.length == 0 , InvalidDataLength (callData.length , 0 ));
327355 TransferSpec memory transferPolicy;
328356 bool found = false ;
329357
@@ -337,12 +365,11 @@ library SessionLib {
337365
338366 require (found, TransferPolicyViolated (target));
339367 require (value <= transferPolicy.maxValuePerUse, MaxValueExceeded (value, transferPolicy.maxValuePerUse));
340- (uint48 newValidAfter , uint48 newValidUntil ) =
341- transferPolicy.valueLimit.checkAndUpdate (state.transferValue[target], value, periodIds[1 ]);
342- shrinkRange (timeRange, newValidAfter, newValidUntil);
368+ validTimeRange = shrinkRange (
369+ validTimeRange,
370+ transferPolicy.valueLimit.checkAndUpdate (state.transferValue[target], value, periodIds[1 ])
371+ );
343372 }
344-
345- return (timeRange[0 ], timeRange[1 ]);
346373 }
347374
348375 /// @notice Getter for the remainder of a usage limit.
0 commit comments