# Withdrawal Queue Mechanics

### Overview

The withdrawal queue system orchestrates asset withdrawals across multiple chains and vaults through two fixed-size queues:

* Local queue for same-chain vaults
* Cross-chain queue for vaults on other networks

### Queue Structure

```solidity
    // Fixed size for all queues
    uint256 public constant WITHDRAWAL_QUEUE_SIZE = 30;
    
    // Queue storage
    uint256[WITHDRAWAL_QUEUE_SIZE] public localWithdrawalQueue;
    uint256[WITHDRAWAL_QUEUE_SIZE] public xChainWithdrawalQueue;
```

### Queue Management

#### Queue Registration

```solidity
    /// @notice Add a new vault to the portfolio
    /// @param chainId chainId of the vault
    /// @param superformId id of superform in case its crosschain
    /// @param vault vault address
    /// @param vaultDecimals decimals of ERC4626 token
    /// @param oracle vault shares price oracle
   function addVault(
        uint32 chainId,
        uint256 superformId,
        address vault,
        uint8 vaultDecimals,
        ISharePriceOracle oracle
    )
        external
        onlyRoles(MANAGER_ROLE)
    {
        if (superformId == 0) revert();
        // If its already listed revert
        if (isVaultListed(vault)) revert VaultAlreadyListed();

        // Save it into storage
        vaults[superformId].chainId = chainId;
        vaults[superformId].superformId = superformId;
        vaults[superformId].vaultAddress = vault;
        vaults[superformId].decimals = vaultDecimals;
        vaults[superformId].oracle = oracle;
        uint192 lastSharePrice = vaults[superformId].sharePrice(asset()).toUint192();
        if (lastSharePrice == 0) revert();
        _vaultToSuperformId[vault] = superformId;

        if (chainId == THIS_CHAIN_ID) {
            // Push it to the local withdrawal queue
            uint256[WITHDRAWAL_QUEUE_SIZE] memory queue = localWithdrawalQueue;
            for (uint256 i = 0; i != WITHDRAWAL_QUEUE_SIZE; i++) {
                if (queue[i] == 0) {
                    localWithdrawalQueue[i] = superformId;
                    break;
                }
            }
            // If its on the same chain perfom approval to vault
            asset().safeApprove(vault, type(uint256).max);
        } else {
            // Push it to the crosschain withdrawal queue
            uint256[WITHDRAWAL_QUEUE_SIZE] memory queue = xChainWithdrawalQueue;
            for (uint256 i = 0; i != WITHDRAWAL_QUEUE_SIZE; i++) {
                if (queue[i] == 0) {
                    xChainWithdrawalQueue[i] = superformId;
                    break;
                }
            }
        }

        emit AddVault(chainId, vault);
    }
```

### Withdrawal Processing

#### State Tracking

```solidity
struct ProcessRedeemRequestCache {
    // Per-chain tracking
    uint256[WITHDRAWAL_QUEUE_SIZE][N_CHAINS] dstVaults;      // Target vaults
    uint256[WITHDRAWAL_QUEUE_SIZE][N_CHAINS] sharesPerVault; // Shares to withdraw
    uint256[WITHDRAWAL_QUEUE_SIZE][N_CHAINS] assetsPerVault; // Expected assets
    uint256[N_CHAINS] lens;                                  // Vaults per chain
    
    // Global tracking
    uint256 amountToWithdraw;       // Remaining to withdraw
    uint256 sharesFulfilled;        // Processed shares
    uint256 totalClaimableWithdraw; // Available for immediate withdrawal
    uint256 totalAssets;           // Total vault assets
    uint256 totalIdle;             // Unallocated assets
    uint256 totalDebt;             // Allocated assets
    uint256 assets;                // Requested withdrawal
    
    // Operation type flags
    bool isSingleChain;
    bool isMultiChain;
    bool isMultiVault;
}
```

#### Withdrawal Route Calculation

```solidity
    /// @dev Precomputes the withdrawal route following the order of the withdrawal queue
    /// according to the needed assets
    /// @param cache the memory pointer of the cache
    /// @dev writes the route to the cache struct
    ///
    /// Note: First it will try to fulfill the request with idle assets, after that it will
    /// loop through the withdrawal queue and compute the destination chains and vaults on each
    /// destionation chain, plus the shaes to redeem on each vault
    function _prepareWithdrawalRoute(
        ProcessRedeemRequestCache memory cache,
        bool despiseDust    
    ) private view {
        // Use the local vaults first
        _exhaustWithdrawalQueue(cache, localWithdrawalQueue, false, false);
        // Use the crosschain vaults after
        _exhaustWithdrawalQueue(cache, xChainWithdrawalQueue, true, despiseDust);
    }
```

#### Queue Processing Logic

<pre class="language-solidity"><code class="lang-solidity"><strong>    // Withdrawal Queue Processing
</strong>    function _exhaustWithdrawalQueue(
        ProcessRedeemRequestCache memory cache,
        uint256[WITHDRAWAL_QUEUE_SIZE] memory queue,
        bool resetValues
    )
        private
        view
    {
        // Cache how many chains we need and how many vaults in each chain
        for (uint256 i = 0; i != WITHDRAWAL_QUEUE_SIZE; i++) {
            // If we exhausted the queue stop
            if (queue[i] == 0) {
                if (resetValues) {
                    // reset values
                    cache.amountToWithdraw = cache.assets - cache.totalIdle;
                }
                break;
            }
            if (resetValues) {
                // If its fulfilled stop
                if (cache.amountToWithdraw == 0) {
                    break;
                }
            }
            // Cache next vault from the withdrawal queue
            VaultData memory vault = vaults[queue[i]];
            // Calcualate the maxWithdraw of the vault
            uint256 maxWithdraw = vault.convertToAssets(_sharesBalance(vault), true);

            // Dont withdraw more than max
            uint256 withdrawAssets = Math.min(maxWithdraw, cache.amountToWithdraw);
            if (withdrawAssets == 0) continue;
            // Cache chain index
            uint256 chainIndex = chainIndexes[vault.chainId];
            // Cache chain length
            uint256 len = cache.lens[chainIndex];
            // Push the superformId to the last index of the array
            cache.dstVaults[chainIndex][len] = vault.superformId;

            uint256 shares;
            if (cache.amountToWithdraw >= maxWithdraw) {
                uint256 balance = _sharesBalance(vault);
                shares = balance;
            } else {
                shares = vault.convertToShares(withdrawAssets, true);
            }

            if (shares == 0) continue;
            // Push the shares to redeeem of that vault
            cache.sharesPerVault[chainIndex][len] = shares;
            // Push the assetse to withdraw of that vault
            cache.assetsPerVault[chainIndex][len] = withdrawAssets;
            // Reduce the total debt by no more than the debt of this vault
            uint256 debtReduction = Math.min(vault.totalDebt, withdrawAssets);
            // Reduce totalDebt
            cache.totalDebt -= debtReduction;
            // Reduce needed assets
            cache.amountToWithdraw -= withdrawAssets;

            // Cache wether is single chain or multichain
            if (vault.chainId != THIS_CHAIN_ID) {
                if (!cache.isSingleChain &#x26;&#x26; !cache.isMultiChain) {
                    // First external chain encountered
                    cache.isSingleChain = true;
                } else if (cache.isSingleChain) {
                    // Find the first external chain ID
                    uint256 firstChainId;
                    for (uint256 j = 0; j &#x3C; N_CHAINS; j++) {
                        if (cache.lens[j] > 0 &#x26;&#x26; j != chainIndexes[THIS_CHAIN_ID]) {
                            firstChainId = j;
                            break;
                        }
                    }
                    // If this vault is from a different chain than the first one, it's multi-chain
                    if (chainIndex != firstChainId) {
                        cache.isSingleChain = false;
                        cache.isMultiChain = true;
                    }
                }
            
                // Check if there are multiple vaults in this chain
                if (cache.lens[chainIndex] >= 1) {
                    cache.isMultiVault = true;
                }
            }

            // Increase index for iteration
            unchecked {
                cache.lens[chainIndex]++;
            }
        }
    }
</code></pre>

### Asset Tracking

```solidity
    /// @notice Returns the total amount of the underlying assets that are settled.
    function totalWithdrawableAssets() public view returns (uint256 assets) {
        return totalLocalAssets() + totalXChainAssets();
    }

    /// @notice Returns the total amount of the underlying asset that are located on this
    /// same chain and can be transferred synchronously
    function totalLocalAssets() public view returns (uint256 assets) {
        assets = _totalIdle;
        for (uint256 i = 0; i != WITHDRAWAL_QUEUE_SIZE;) {
            VaultData memory vault = vaults[localWithdrawalQueue[i]];
            if (vault.vaultAddress == address(0)) break;
            assets += vault.convertToAssets(_sharesBalance(vault), asset(), false);
            ++i;
        }
        return assets;
    }

    /// @notice Returns the total amount of the underlying asset that are located on
    /// other chains and need asynchronous transfers
    function totalXChainAssets() public view returns (uint256 assets) {
        for (uint256 i = 0; i != WITHDRAWAL_QUEUE_SIZE;) {
            VaultData memory vault = vaults[xChainWithdrawalQueue[i]];
            if (vault.vaultAddress == address(0)) break;
            assets += vault.convertToAssets(_sharesBalance(vault), asset(), false);
            ++i;
        }
        return assets;
    }
```

### Optimization Strategies

#### Gas Optimizations

1. Prioritizes idle assets (no withdrawal cost)
2. Processes local withdrawals before cross-chain
3. Batches cross-chain operations
4. Uses unchecked blocks for counters
5. Caches chain indexes and lengths

#### Slippage Protection

```solidity
    // In investment/divestment operations
    if (shares < minSharesOut) {
        revert InsufficientAssets();
    }
```

#### Asset Validation

```solidity
    // Available assets check
    if (cache.assets > cache.totalAssets - gateway.totalpendingXChainInvests()) {
        revert InsufficientAvailableAssets();
    }
```

### Gas Analysis

1. **Estimated Costs**
   * Local withdrawals: \~100k-200k gas
   * Cross-chain operations: varies by chain
   * Batch processing savings
2. **Queue Security Implications**
   * Maximum queue size impact
   * Reordering attack vectors
   * Front-running mitigations
3. **Slippage Protection Analysis**
   * Minimum output validation
   * Cross-chain slippage handling
   * Price impact considerations

### Error Types

```solidity
    error VaultAlreadyListed();
    error InvalidSuperformId();
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://devs.maxapy.io/maxapy-erc7540/metavault/withdrawal-queue-mechanics.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
