`previewRedeem` and `redeem` functions deviate from the ERC4626 specification
Important The contest page explicitly mentioned that the `LMPVault` must conform with the ERC4626. Thus, issues related to EIP compliance should be considered valid in the context of this audit.\\nQ: Is the code/contract expected to comply with any EIPs? Are there specific assumptions around adhering to those EIPs that Watsons should be aware of?\\nsrc/vault/LMPVault.sol should be 4626 compatible\\nLet the value returned by `previewRedeem` function be $asset_{preview}$ and the actual number of assets obtained from calling the `redeem` function be $asset_{actual}$.\\nThe following specification of `previewRedeem` function is taken from ERC4626 specification:\\nAllows an on-chain or off-chain user to simulate the effects of their redeemption at the current block, given current on-chain conditions.\\nMUST return as close to and no more than the exact amount of `assets` that would be withdrawn in a `redeem` call in the same transaction. I.e. `redeem` should return the same or more `assets` as `previewRedeem` if called in the same transaction.\\nIt mentioned that the `redeem` should return the same or more `assets` as `previewRedeem` if called in the same transaction, which means that it must always be $asset_{preview} \\le asset_{actual}$.\\nHowever, it is possible that the `redeem` function might return fewer assets than the number of assets previewed by the `previewRedeem` ($asset_{preview} > asset_{actual}$), thus it does not conform to the specification.\\n```\\nFile: LMPVault.sol\\n    function redeem(\\n        uint256 shares,\\n        address receiver,\\n        address owner\\n    ) public virtual override nonReentrant noNavDecrease ensureNoNavOps returns (uint256 assets) {\\n        uint256 maxShares = maxRedeem(owner);\\n        if (shares > maxShares) {\\n            revert ERC4626ExceededMaxRedeem(owner, shares, maxShares);\\n        }\\n        uint256 possibleAssets = previewRedeem(shares); // @audit-info  round down, which is correct because user won't get too many\\n\\n        assets = _withdraw(possibleAssets, shares, receiver, owner);\\n    }\\n```\\n\\nNote that the `previewRedeem` function performs its computation based on the cached `totalDebt` and `totalIdle`, which might not have been updated to reflect the actual on-chain market condition. Thus, these cached values might be higher than expected.\\nAssume that `totalIdle` is zero and all WETH has been invested in the destination vaults. Thus, `totalAssetsToPull` will be set to $asset_{preview}$.\\nIf a DV is making a loss, users could only burn an amount proportional to their ownership of this vault. The code will go through all the DVs in the withdrawal queue (withdrawalQueueLength) in an attempt to withdraw as many assets as possible. However, it is possible that the `totalAssetsPulled` to be less than $asset_{preview}$.\\n```\\nFile: LMPVault.sol\\n    function _withdraw(\\n        uint256 assets,\\n        uint256 shares,\\n        address receiver,\\n        address owner\\n    ) internal virtual returns (uint256) {\\n        uint256 idle = totalIdle;\\n        WithdrawInfo memory info = WithdrawInfo({\\n            currentIdle: idle,\\n            assetsFromIdle: assets >= idle ? idle : assets,\\n            totalAssetsToPull: assets - (assets >= idle ? idle : assets),\\n            totalAssetsPulled: 0,\\n            idleIncrease: 0,\\n            debtDecrease: 0\\n        });\\n\\n        // If not enough funds in idle, then pull what we need from destinations\\n        if (info.totalAssetsToPull > 0) {\\n            uint256 totalVaultShares = totalSupply();\\n\\n            // Using pre-set withdrawalQueue for withdrawal order to help minimize user gas\\n            uint256 withdrawalQueueLength = withdrawalQueue.length;\\n            for (uint256 i = 0; i < withdrawalQueueLength; ++i) {\\n                IDestinationVault destVault = IDestinationVault(withdrawalQueue[i]);\\n                (uint256 sharesToBurn, uint256 totalDebtBurn) = _calcUserWithdrawSharesToBurn(\\n                    destVault,\\n                    shares,\\n                    info.totalAssetsToPull - Math.max(info.debtDecrease, info.totalAssetsPulled),\\n                    totalVaultShares\\n                );\\n..SNIP..\\n        // At this point should have all the funds we need sitting in in the vault\\n        uint256 returnedAssets = info.assetsFromIdle + info.totalAssetsPulled;\\n```\\n
Ensure that $asset_{preview} \\le asset_{actual}$.\\nAlternatively, document that the `previewRedeem` and `redeem` functions deviate from the ERC4626 specification in the comments and/or documentation.
It was understood from the protocol team that they anticipate external parties to integrate directly with the LMPVault (e.g., vault shares as collateral). Thus, the LMPVault must be ERC4626 compliance. Otherwise, the caller (internal or external) of the `previewRedeem` function might receive incorrect information, leading to the wrong action being executed.
```\\nFile: LMPVault.sol\\n    function redeem(\\n        uint256 shares,\\n        address receiver,\\n        address owner\\n    ) public virtual override nonReentrant noNavDecrease ensureNoNavOps returns (uint256 assets) {\\n        uint256 maxShares = maxRedeem(owner);\\n        if (shares > maxShares) {\\n            revert ERC4626ExceededMaxRedeem(owner, shares, maxShares);\\n        }\\n        uint256 possibleAssets = previewRedeem(shares); // @audit-info  round down, which is correct because user won't get too many\\n\\n        assets = _withdraw(possibleAssets, shares, receiver, owner);\\n    }\\n```\\n