@@ -99,7 +99,16 @@ contract PriorityWithdrawalQueue is
9999 uint32 creationTime
100100 );
101101 event WithdrawRequestCancelled (bytes32 indexed requestId , address indexed user , uint96 amountOfEEth , uint96 sharesOfEEth , uint32 nonce , uint32 timestamp );
102- event WithdrawRequestFinalized (bytes32 indexed requestId , address indexed user , uint96 amountOfEEth , uint96 sharesOfEEth , uint32 nonce , uint32 timestamp );
102+ event WithdrawRequestFinalized (
103+ bytes32 indexed pendingRequestId ,
104+ bytes32 indexed finalizedRequestId ,
105+ address indexed user ,
106+ uint96 amountOfEEth ,
107+ uint96 shareOfEEth ,
108+ uint32 nonce ,
109+ uint32 creationTime ,
110+ uint96 amountLocked
111+ );
103112 event WithdrawRequestClaimed (bytes32 indexed requestId , address indexed user , uint96 amountOfEEth , uint96 sharesOfEEth , uint32 nonce , uint32 timestamp );
104113 event WithdrawRequestInvalidated (bytes32 indexed requestId , uint96 amountOfEEth , uint96 sharesOfEEth , uint32 nonce , uint32 timestamp );
105114 event WhitelistUpdated (address indexed user , bool status );
@@ -275,31 +284,62 @@ contract PriorityWithdrawalQueue is
275284
276285 /// @notice Request manager finalizes withdrawal requests after maturity
277286 /// @dev Checks maturity and deadline, marks requests as finalized
278- /// @param requests Array of requests to finalize
287+ /// Pending requests have amountLocked=0, must pass such requests to this function
288+ /// @param requests Array of pending requests to finalize (with amountLocked=0)
279289 function fulfillRequests (WithdrawRequest[] calldata requests ) external onlyRequestManager whenNotPaused {
280- uint256 totalSharesToFinalize = 0 ;
290+ uint256 totalAmountToLock = 0 ;
281291
282292 for (uint256 i = 0 ; i < requests.length ; ++ i) {
283293 WithdrawRequest calldata request = requests[i];
284294 bytes32 requestId = keccak256 (abi.encode (request));
285295
296+ // Pending requests must have amountLocked = 0
297+ if (request.amountLocked != 0 ) revert BadInput ();
298+
286299 // Verify request exists in pending set
287300 if (! _withdrawRequests.contains (requestId)) revert RequestNotFound ();
288- if (_finalizedRequests.contains (requestId)) revert RequestAlreadyFinalized ();
289301
290302 // Check MIN_DELAY has passed (request must wait at least MIN_DELAY seconds)
291303 uint256 earliestFulfillTime = request.creationTime + MIN_DELAY;
292304 if (block .timestamp < earliestFulfillTime) revert NotMatured ();
293305
294- // Add to finalized set
295- _finalizedRequests.add (requestId);
296- totalSharesToFinalize += request.shareOfEEth;
297-
298- emit WithdrawRequestFinalized (requestId, request.user, request.amountOfEEth, request.shareOfEEth, request.nonce, uint32 (block .timestamp ));
306+ // Calculate and store the locked amount for this request
307+ // Cap at original amount to prevent locking more than user requested
308+ uint256 amountForShares = liquidityPool.amountForShare (request.shareOfEEth);
309+ uint96 amountToLock = request.amountOfEEth < amountForShares
310+ ? request.amountOfEEth
311+ : uint96 (amountForShares);
312+ totalAmountToLock += amountToLock;
313+
314+ // Create finalized request with amountLocked set
315+ bytes32 finalizedRequestId = keccak256 (abi.encode (WithdrawRequest ({
316+ user: request.user,
317+ amountOfEEth: request.amountOfEEth,
318+ shareOfEEth: request.shareOfEEth,
319+ nonce: request.nonce,
320+ creationTime: request.creationTime,
321+ amountLocked: amountToLock
322+ })));
323+
324+ // Remove from pending set, add finalized request to finalized set
325+ _withdrawRequests.remove (requestId);
326+
327+ bool addedToFinalized = _finalizedRequests.add (finalizedRequestId);
328+ if (! addedToFinalized) revert RequestAlreadyFinalized ();
329+
330+ emit WithdrawRequestFinalized (
331+ requestId,
332+ finalizedRequestId,
333+ request.user,
334+ request.amountOfEEth,
335+ request.shareOfEEth,
336+ request.nonce,
337+ request.creationTime,
338+ amountToLock
339+ );
299340 }
300341
301342 // Lock ETH in LiquidityPool for priority withdrawals
302- uint256 totalAmountToLock = liquidityPool.amountForShare (totalSharesToFinalize);
303343 liquidityPool.addEthAmountLockedForPriorityWithdrawal (uint128 (totalAmountToLock));
304344 }
305345
@@ -337,14 +377,18 @@ contract PriorityWithdrawalQueue is
337377 }
338378 }
339379
340- /// @notice Invalidate a withdrawal request (prevents finalization )
380+ /// @notice Invalidate withdrawal requests (can be pending or finalized )
341381 /// @param requests Array of requests to invalidate
342382 /// @return invalidatedRequestIds Array of request IDs that were invalidated
343383 function invalidateRequests (WithdrawRequest[] calldata requests ) external onlyRequestManager returns (bytes32 [] memory invalidatedRequestIds ) {
344384 invalidatedRequestIds = new bytes32 [](requests.length );
345385 for (uint256 i = 0 ; i < requests.length ; ++ i) {
346386 bytes32 requestId = keccak256 (abi.encode (requests[i]));
347- if (! _withdrawRequests.contains (requestId)) revert RequestNotFound ();
387+
388+ // Verify request exists (either in pending or finalized set based on amountLocked)
389+ bool isPending = requests[i].amountLocked == 0 && _withdrawRequests.contains (requestId);
390+ bool isFinalized = requests[i].amountLocked > 0 && _finalizedRequests.contains (requestId);
391+ if (! isPending && ! isFinalized) revert RequestNotFound ();
348392
349393 _cancelWithdrawRequest (requests[i]);
350394 invalidatedRequestIds[i] = requestId;
@@ -376,7 +420,7 @@ contract PriorityWithdrawalQueue is
376420
377421 if (beforeEEthShares - eEthSharesMoved != eETH.shares (address (this ))) revert InvalidEEthSharesAfterRemainderHandling ();
378422
379- emit RemainderHandled (uint96 (eEthAmountToTreasury), uint96 (liquidityPool. amountForShare ( eEthSharesToBurn) ));
423+ emit RemainderHandled (uint96 (eEthAmountToTreasury), uint96 (eEthSharesToBurn));
380424 }
381425
382426 /// @notice Update the share remainder split to treasury
@@ -492,7 +536,8 @@ contract PriorityWithdrawalQueue is
492536 amountOfEEth: amountOfEEth,
493537 shareOfEEth: shareOfEEth,
494538 nonce: requestNonce,
495- creationTime: timeNow
539+ creationTime: timeNow,
540+ amountLocked: 0 // Set to 0 for pending requests
496541 });
497542
498543 requestId = keccak256 (abi.encode (req));
@@ -510,21 +555,17 @@ contract PriorityWithdrawalQueue is
510555 );
511556 }
512557
513- function _dequeueWithdrawRequest (WithdrawRequest calldata request ) internal returns (bytes32 requestId ) {
514- requestId = keccak256 (abi.encode (request));
515- bool removedFromSet = _withdrawRequests.remove (requestId);
516- if (! removedFromSet) revert RequestNotFound ();
517-
518- _finalizedRequests.remove (requestId);
519- }
520-
521558 function _cancelWithdrawRequest (WithdrawRequest calldata request ) internal returns (bytes32 requestId ) {
522559 requestId = keccak256 (abi.encode (request));
523560
524- // Check if finalized BEFORE dequeue (dequeue removes from finalized set )
525- bool wasFinalized = _finalizedRequests. contains (requestId) ;
561+ // Check if this is a finalized request (amountLocked > 0) or pending request (amountLocked = 0 )
562+ bool isFinalized = request.amountLocked > 0 ;
526563
527- _dequeueWithdrawRequest (request);
564+ if (isFinalized) {
565+ if (! _finalizedRequests.remove (requestId)) revert RequestNotFound ();
566+ } else {
567+ if (! _withdrawRequests.remove (requestId)) revert RequestNotFound ();
568+ }
528569
529570 // Calculate current value of shares
530571 uint256 amountForShares = liquidityPool.amountForShare (request.shareOfEEth);
@@ -542,8 +583,9 @@ contract PriorityWithdrawalQueue is
542583 : 0 ;
543584 totalRemainderShares += uint96 (remainder);
544585
545- if (wasFinalized) {
546- liquidityPool.reduceEthAmountLockedForPriorityWithdrawal (uint128 (amountToReturn));
586+ if (isFinalized) {
587+ // Use the amountLocked from the request struct
588+ liquidityPool.reduceEthAmountLockedForPriorityWithdrawal (request.amountLocked);
547589 }
548590
549591 IERC20 (address (eETH)).safeTransfer (request.user, amountToReturn);
@@ -554,19 +596,18 @@ contract PriorityWithdrawalQueue is
554596 function _claimWithdraw (WithdrawRequest calldata request ) internal {
555597 if (request.user != msg .sender ) revert NotRequestOwner ();
556598
599+ // Finalized requests must have amountLocked > 0
600+ if (request.amountLocked == 0 ) revert RequestNotFinalized ();
601+
557602 bytes32 requestId = keccak256 (abi.encode (request));
558603
559- if ( ! _withdrawRequests. contains (requestId)) revert RequestNotFound ();
604+ // Finalized requests are only in _finalizedRequests (removed from _withdrawRequests during fulfillment)
560605 if (! _finalizedRequests.contains (requestId)) revert RequestNotFinalized ();
561606
562- uint256 amountForShares = liquidityPool.amountForShare (request.shareOfEEth);
563- uint256 amountToWithdraw = request.amountOfEEth < amountForShares
564- ? request.amountOfEEth
565- : amountForShares;
607+ uint256 amountToWithdraw = request.amountLocked;
566608
567609 uint256 sharesToBurn = liquidityPool.sharesForWithdrawalAmount (amountToWithdraw);
568610
569- _withdrawRequests.remove (requestId);
570611 _finalizedRequests.remove (requestId);
571612
572613 // Track remainder (difference between original shares and burned shares)
@@ -595,20 +636,23 @@ contract PriorityWithdrawalQueue is
595636 /// @param _shareOfEEth The share of eETH
596637 /// @param _nonce The request nonce
597638 /// @param _creationTime The creation timestamp
639+ /// @param _amountLocked The amount locked (0 for pending, >0 for finalized)
598640 /// @return requestId The keccak256 hash of the request
599641 function generateWithdrawRequestId (
600642 address _user ,
601643 uint96 _amountOfEEth ,
602644 uint96 _shareOfEEth ,
603645 uint32 _nonce ,
604- uint32 _creationTime
646+ uint32 _creationTime ,
647+ uint96 _amountLocked
605648 ) public pure returns (bytes32 requestId ) {
606649 WithdrawRequest memory req = WithdrawRequest ({
607650 user: _user,
608651 amountOfEEth: _amountOfEEth,
609652 shareOfEEth: _shareOfEEth,
610653 nonce: _nonce,
611- creationTime: _creationTime
654+ creationTime: _creationTime,
655+ amountLocked: _amountLocked
612656 });
613657 requestId = keccak256 (abi.encode (req));
614658 }
@@ -617,13 +661,7 @@ contract PriorityWithdrawalQueue is
617661 /// @param request The withdrawal request
618662 /// @return requestId The keccak256 hash of the request
619663 function getRequestId (WithdrawRequest calldata request ) external pure returns (bytes32 ) {
620- return generateWithdrawRequestId (
621- request.user,
622- request.amountOfEEth,
623- request.shareOfEEth,
624- request.nonce,
625- request.creationTime
626- );
664+ return keccak256 (abi.encode (request));
627665 }
628666
629667 /// @notice Get all active request IDs
@@ -654,13 +692,12 @@ contract PriorityWithdrawalQueue is
654692
655693 /// @notice Get the claimable amount for a request
656694 /// @param request The withdrawal request
657- /// @return The claimable ETH amount
695+ /// @return The claimable ETH amount (locked at fulfillment time)
658696 function getClaimableAmount (WithdrawRequest calldata request ) external view returns (uint256 ) {
659697 bytes32 requestId = keccak256 (abi.encode (request));
660698 if (! _finalizedRequests.contains (requestId)) revert RequestNotFinalized ();
661699
662- uint256 amountForShares = liquidityPool.amountForShare (request.shareOfEEth);
663- return request.amountOfEEth < amountForShares ? request.amountOfEEth : amountForShares;
700+ return request.amountLocked;
664701 }
665702
666703 /// @notice Get the total number of active requests
0 commit comments