@@ -167,16 +167,69 @@ contract FlowYieldVaultsRequests is ReentrancyGuard, Ownable2Step {
167167 /// @notice All requests indexed by request ID
168168 mapping (uint256 => Request) public requests;
169169
170- /// @notice Array of pending request IDs awaiting processing (FIFO order)
171- uint256 [] public pendingRequestIds;
172-
173- /// @notice Index of request ID in global pending array (for O(1) lookup)
174- mapping (uint256 => uint256 ) private _requestIndexInGlobalArray;
175-
176170 /// @notice Index of yieldVaultId in user's yieldVaultsByUser array (for O(1) removal)
177171 /// @dev Internal visibility allows test helpers to properly initialize state
178172 mapping (address => mapping (uint64 => uint256 )) internal _yieldVaultIndexInUserArray;
179173
174+ mapping (uint256 => uint256 ) queue;
175+ uint256 first = 1 ;
176+ uint256 last = 1 ;
177+
178+ function _enqueue (uint256 requestId ) internal {
179+ // empty queue
180+ if (first == last) {
181+ queue[first] = requestId;
182+ } else {
183+ queue[last] = requestId;
184+ }
185+ last += 1 ;
186+ }
187+
188+ function _dequeue () internal returns (uint256 ) {
189+ require (last > first);
190+
191+ uint256 requestId;
192+ requestId = queue[first];
193+
194+ delete queue[first];
195+ first += 1 ;
196+
197+ return requestId;
198+ }
199+
200+ function _drop (uint256 requestId ) internal {
201+ bool slide = false ;
202+ for (uint256 i = first; i <= last;) {
203+ if (queue[i] == requestId) {
204+ delete queue[i];
205+ slide = true ;
206+ }
207+
208+ if (slide) {
209+ queue[i] = queue[i+1 ];
210+ }
211+
212+ unchecked {
213+ ++ i;
214+ }
215+ }
216+
217+ last -= 1 ;
218+ }
219+
220+ function _peek () internal view returns (uint256 ) {
221+ require (last > first);
222+
223+ uint256 requestId;
224+ requestId = queue[first];
225+
226+ return requestId;
227+ }
228+
229+ function _queueLength () internal view returns (uint256 ) {
230+ return last - first;
231+ }
232+
180233 // ============================================
181234 // Errors
182235 // ============================================
@@ -728,6 +781,7 @@ contract FlowYieldVaultsRequests is ReentrancyGuard, Ownable2Step {
728781
729782 // Remove from pending queues (both global and user-specific)
730783 _removePendingRequest (requestId);
784+ _drop (requestId);
731785
732786 emit RequestProcessed (
733787 requestId,
@@ -929,6 +983,7 @@ contract FlowYieldVaultsRequests is ReentrancyGuard, Ownable2Step {
929983 userPendingRequestCount[request.user]-- ;
930984 }
931985 _removePendingRequest (requestId);
986+ _drop (requestId);
932987
933988 // === REFUND HANDLING (pull pattern) ===
934989 // For CREATE/DEPOSIT requests, move funds from pendingUserBalances to claimableRefunds
@@ -1008,9 +1063,10 @@ contract FlowYieldVaultsRequests is ReentrancyGuard, Ownable2Step {
10081063 * - Funds will be bridged back from Cadence in completeProcessing
10091064 *
10101065 * The PROCESSING status prevents request cancellation and double-processing.
1011- * @param requestId The unique identifier of the request to start processing.
10121066 */
1013- function startProcessing (uint256 requestId ) external onlyAuthorizedCOA nonReentrant {
1067+ function startProcessing () external onlyAuthorizedCOA nonReentrant {
1068+ // Pick the request at the front of the queue, for processing
1069+ uint256 requestId = _peek ();
10141070 Request storage request = requests[requestId];
10151071
10161072 // === VALIDATION ===
@@ -1094,17 +1150,17 @@ contract FlowYieldVaultsRequests is ReentrancyGuard, Ownable2Step {
10941150 * - Successful CLOSE: Unregisters YieldVault ownership
10951151 *
10961152 * For all other cases (success, WITHDRAW/CLOSE), msg.value must be 0.
1097- * @param requestId The unique identifier of the request to complete.
10981153 * @param success True if the Cadence operation succeeded, false otherwise.
10991154 * @param yieldVaultId The YieldVault Id from Cadence (for CREATE: newly assigned; for others: existing).
11001155 * @param message Human-readable status message or error description.
11011156 */
11021157 function completeProcessing (
1103- uint256 requestId ,
11041158 bool success ,
11051159 uint64 yieldVaultId ,
11061160 string calldata message
11071161 ) external payable onlyAuthorizedCOA nonReentrant {
1162+ // Pick the request at the front of the queue, for processing
1163+ uint256 requestId = _peek ();
11081164 Request storage request = requests[requestId];
11091165
11101166 // === VALIDATION ===
@@ -1181,6 +1237,7 @@ contract FlowYieldVaultsRequests is ReentrancyGuard, Ownable2Step {
11811237 userPendingRequestCount[request.user]-- ;
11821238 }
11831239 _removePendingRequest (requestId);
1240+ _dequeue ();
11841241
11851242 emit RequestProcessed (requestId, request.user, request.requestType, newStatus, yieldVaultId, message);
11861243 }
@@ -1222,12 +1279,19 @@ contract FlowYieldVaultsRequests is ReentrancyGuard, Ownable2Step {
12221279 /// @notice Gets the count of pending requests
12231280 /// @return Number of pending requests
12241281 function getPendingRequestCount () external view returns (uint256 ) {
1225- return pendingRequestIds. length ;
1282+ return _queueLength () ;
12261283 }
12271284
12281285 /// @notice Gets all pending request IDs
12291286 /// @return Array of pending request IDs
12301287 function getPendingRequestIds () external view returns (uint256 [] memory ) {
1288+ uint256 [] memory pendingRequestIds = new uint256 [](_queueLength ());
1289+ for (uint256 i = first; i <= last;) {
1290+ pendingRequestIds[i] = queue[i];
1291+ unchecked {
1292+ ++ i;
1293+ }
1294+ }
12311295 return pendingRequestIds;
12321296 }
12331297
@@ -1266,7 +1330,7 @@ contract FlowYieldVaultsRequests is ReentrancyGuard, Ownable2Step {
12661330 string [] memory strategyIdentifiers
12671331 )
12681332 {
1269- if (startIndex >= pendingRequestIds. length ) {
1333+ if (startIndex >= _queueLength () ) {
12701334 return (
12711335 new uint256 [](0 ),
12721336 new address [](0 ),
@@ -1282,7 +1346,7 @@ contract FlowYieldVaultsRequests is ReentrancyGuard, Ownable2Step {
12821346 );
12831347 }
12841348
1285- uint256 remaining = pendingRequestIds. length - startIndex;
1349+ uint256 remaining = _queueLength () - startIndex;
12861350 uint256 size = count == 0
12871351 ? remaining
12881352 : (count < remaining ? count : remaining);
@@ -1299,8 +1363,8 @@ contract FlowYieldVaultsRequests is ReentrancyGuard, Ownable2Step {
12991363 vaultIdentifiers = new string [](size);
13001364 strategyIdentifiers = new string [](size);
13011365
1302- for (uint256 i = 0 ; i < size; ) {
1303- Request memory req = requests[pendingRequestIds[ startIndex + i]];
1366+ for (uint256 i = 0 ; i < size;) {
1367+ Request memory req = requests[queue[first + startIndex + i]];
13041368 ids[i] = req.id;
13051369 users[i] = req.user;
13061370 requestTypes[i] = uint8 (req.requestType);
@@ -1672,8 +1736,7 @@ contract FlowYieldVaultsRequests is ReentrancyGuard, Ownable2Step {
16721736 });
16731737
16741738 // Add to global pending queue with index tracking for O(1) lookup
1675- _requestIndexInGlobalArray[requestId] = pendingRequestIds.length ;
1676- pendingRequestIds.push (requestId);
1739+ _enqueue (requestId);
16771740 userPendingRequestCount[msg .sender ]++ ;
16781741
16791742 // Add to user's pending array with index tracking for O(1) removal
@@ -1719,29 +1782,6 @@ contract FlowYieldVaultsRequests is ReentrancyGuard, Ownable2Step {
17191782 * @param requestId The request ID to remove from pending queues.
17201783 */
17211784 function _removePendingRequest (uint256 requestId ) internal {
1722- // === GLOBAL PENDING ARRAY REMOVAL ===
1723- // Uses O(1) lookup + O(n) shift to maintain FIFO order
1724- // FIFO order is critical for DeFi fairness - requests must be processed in submission order
1725- uint256 indexInGlobal = _requestIndexInGlobalArray[requestId];
1726- uint256 globalLength = pendingRequestIds.length ;
1727-
1728- // Safety check: verify element exists at expected index
1729- if (globalLength > 0 && indexInGlobal < globalLength && pendingRequestIds[indexInGlobal] == requestId) {
1730- // Shift all subsequent elements left to maintain FIFO order
1731- for (uint256 j = indexInGlobal; j < globalLength - 1 ; ) {
1732- pendingRequestIds[j] = pendingRequestIds[j + 1 ];
1733- // Update index mapping for each shifted element
1734- _requestIndexInGlobalArray[pendingRequestIds[j]] = j;
1735- unchecked {
1736- ++ j;
1737- }
1738- }
1739- // Remove the last element (now duplicated or the one to remove)
1740- pendingRequestIds.pop ();
1741- // Clean up index mapping
1742- delete _requestIndexInGlobalArray[requestId];
1743- }
1744-
17451785 // === USER PENDING ARRAY REMOVAL ===
17461786 // Uses swap-and-pop for O(1) removal (order doesn't affect FIFO processing)
17471787 address user = requests[requestId].user;
0 commit comments