Excess funds withdrawn from the money market
Excessive amounts of assets are being withdrawn from the money market.\\n```\\nFile: TokenHandler.sol\\n    function _redeemMoneyMarketIfRequired(\\n        uint16 currencyId,\\n        Token memory underlying,\\n        uint256 withdrawAmountExternal\\n    ) private {\\n        // If there is sufficient balance of the underlying to withdraw from the contract\\n        // immediately, just return.\\n        mapping(address => uint256) storage store = LibStorage.getStoredTokenBalances();\\n        uint256 currentBalance = store[underlying.tokenAddress];\\n        if (withdrawAmountExternal <= currentBalance) return;\\n\\n        IPrimeCashHoldingsOracle oracle = PrimeCashExchangeRate.getPrimeCashHoldingsOracle(currencyId);\\n        // Redemption data returns an array of contract calls to make from the Notional proxy (which\\n        // is holding all of the money market tokens).\\n        (RedeemData[] memory data) = oracle.getRedemptionCalldata(withdrawAmountExternal);\\n\\n        // This is the total expected underlying that we should redeem after all redemption calls\\n        // are executed.\\n        uint256 totalUnderlyingRedeemed = executeMoneyMarketRedemptions(underlying, data);\\n\\n        // Ensure that we have sufficient funds before we exit\\n        require(withdrawAmountExternal <= currentBalance.add(totalUnderlyingRedeemed)); // dev: insufficient redeem\\n    }\\n```\\n\\nIf the `currentBalance` is `999,900` USDC and the `withdrawAmountExternal` is `1,000,000` USDC, then there is insufficient balance in the contract, and additional funds need to be withdrawn from the money market (e.g. Compound).\\nSince the contract already has `999,900` USDC, only an additional `100` USDC needs to be withdrawn from the money market to fulfill the withdrawal request of `1,000,000` USDC\\nHowever, instead of withdrawing `100` USDC from the money market, Notional withdraw `1,000,000` USDC from the market as per the `oracle.getRedemptionCalldata(withdrawAmountExternal)` function. As a result, an excess of `990,000` USDC is being withdrawn from the money market
Consider withdrawing only the shortfall amount from the money market.\\n```\\n// Remove the line below\\n (RedeemData[] memory data) = oracle.getRedemptionCalldata(withdrawAmountExternal);\\n// Add the line below\\n (RedeemData[] memory data) = oracle.getRedemptionCalldata(withdrawAmountExternal // Remove the line below\\n currentBalance);\\n```\\n
This led to an excessive amount of assets idling in Notional and not generating any returns or interest in the money market, which led to a loss of assets for the users as they would receive a lower interest rate than expected and incur opportunity loss.\\nAttackers could potentially abuse this to pull the funds Notional invested in the money market leading to griefing and loss of returns/interest for the protocol.
```\\nFile: TokenHandler.sol\\n    function _redeemMoneyMarketIfRequired(\\n        uint16 currencyId,\\n        Token memory underlying,\\n        uint256 withdrawAmountExternal\\n    ) private {\\n        // If there is sufficient balance of the underlying to withdraw from the contract\\n        // immediately, just return.\\n        mapping(address => uint256) storage store = LibStorage.getStoredTokenBalances();\\n        uint256 currentBalance = store[underlying.tokenAddress];\\n        if (withdrawAmountExternal <= currentBalance) return;\\n\\n        IPrimeCashHoldingsOracle oracle = PrimeCashExchangeRate.getPrimeCashHoldingsOracle(currencyId);\\n        // Redemption data returns an array of contract calls to make from the Notional proxy (which\\n        // is holding all of the money market tokens).\\n        (RedeemData[] memory data) = oracle.getRedemptionCalldata(withdrawAmountExternal);\\n\\n        // This is the total expected underlying that we should redeem after all redemption calls\\n        // are executed.\\n        uint256 totalUnderlyingRedeemed = executeMoneyMarketRedemptions(underlying, data);\\n\\n        // Ensure that we have sufficient funds before we exit\\n        require(withdrawAmountExternal <= currentBalance.add(totalUnderlyingRedeemed)); // dev: insufficient redeem\\n    }\\n```\\n