MidnightSupplyVaultSharesCallback deposits into ERC4626 without a min shares slippage bound
Summary
MidnightSupplyVaultSharesCallback::onSell() deposits the borrower's loan tokens into the configured ERC4626 vault and supplies the resulting shares as collateral, without enforcing any minimum shares received. The borrower has no protection against vault share price drift between offer creation and offer fill. Same for other vault callbacks.
Vulnerability Detail
The callback's deposit accepts whatever shares the vault returns:
IERC20(loanToken).forceApprove(vault, totalDeposit);
uint256 shares = IERC4626(vault).deposit(totalDeposit, address(this));
IERC20(vault).forceApprove(address(MORPHO_MIDNIGHT), shares);
MORPHO_MIDNIGHT.supplyCollateral(market, callbackData.collateralIndex, shares, seller);The borrower configures CallbackData when creating the sell offer. The offer sits open until a taker fills it, which may happen much later. When additionalDepositPercent > 0, fresh borrower funds are also pulled in via safeTransferFrom, so the borrower's own capital flows into the vault at the share price observed at fill time.
If the vault's share price degrades between offer creation and offer fill (vault loss, oracle drift, hostile curator action), the borrower receives fewer shares than expected, supplying less collateral than intended and ending up with a smaller LTV buffer than they signed up for.
The user-facing MidnightVaultExecutor enforces maxSharePriceE27 for the exact same flow (reference), but the offer-fill callback omits it.
Impact
The borrower's collateral position is exposed to ERC4626 share price drift with no cap, weakening their health factor relative to what the offer parameters implied.
Code Snippet
Tool Used
Manual Review
Recommendation
Add a maxSharePriceE27 (or minShares) field to CallbackData and revert when the realized share price exceeds it, matching the MidnightVaultExecutor pattern. Or document that the vault is safe.