Possible to create vault positions ineligible for liquidation
Users can self-liquidate their secondary debt holdings in such a way that it is no longer possible to deleverage their vault account as `checkMinBorrow` will fail post-maturity.\\nWhen deleveraging a vault account, the liquidator will pay down account debt directly and the account will not accrue any cash. Under most circumstances, it is not possible to put an account's debt below its minimum borrow size.\\nHowever, there are two exceptions to this:\\nLiquidators purchasing cash from a vault account. This only applies to non-prime vault accounts.\\nA vault account is being settled and `checkMinBorrow` is skipped to ensure an account can always be settled.\\n```\\nFile: VaultLiquidationAction.sol\\n    function deleverageAccount(\\n        address account,\\n        address vault,\\n        address liquidator,\\n        uint16 currencyIndex,\\n        int256 depositUnderlyingInternal\\n    ) external payable nonReentrant override returns (\\n        uint256 vaultSharesToLiquidator,\\n        int256 depositAmountPrimeCash\\n    ) {\\n        require(currencyIndex < 3);\\n        (\\n            VaultConfig memory vaultConfig,\\n            VaultAccount memory vaultAccount,\\n            VaultState memory vaultState\\n        ) = _authenticateDeleverage(account, vault, liquidator);\\n\\n        PrimeRate memory pr;\\n        // Currency Index is validated in this method\\n        (\\n            depositUnderlyingInternal,\\n            vaultSharesToLiquidator,\\n            pr\\n        ) = IVaultAccountHealth(address(this)).calculateDepositAmountInDeleverage(\\n            currencyIndex, vaultAccount, vaultConfig, vaultState, depositUnderlyingInternal\\n        );\\n\\n        uint16 currencyId = vaultConfig.borrowCurrencyId;\\n        if (currencyIndex == 1) currencyId = vaultConfig.secondaryBorrowCurrencies[0];\\n        else if (currencyIndex == 2) currencyId = vaultConfig.secondaryBorrowCurrencies[1];\\n\\n        Token memory token = TokenHandler.getUnderlyingToken(currencyId);\\n        // Excess ETH is returned to the liquidator natively\\n        (/* */, depositAmountPrimeCash) = TokenHandler.depositUnderlyingExternal(\\n            liquidator, currencyId, token.convertToExternal(depositUnderlyingInternal), pr, false \\n        );\\n\\n        // Do not skip the min borrow check here\\n        vaultAccount.vaultShares = vaultAccount.vaultShares.sub(vaultSharesToLiquidator);\\n        if (vaultAccount.maturity == Constants.PRIME_CASH_VAULT_MATURITY) {\\n            // Vault account will not incur a cash balance if they are in the prime cash maturity, their debts\\n            // will be paid down directly.\\n            _reduceAccountDebt(\\n                vaultConfig, vaultState, vaultAccount, pr, currencyIndex, depositUnderlyingInternal, true\\n            );\\n            depositAmountPrimeCash = 0;\\n        }\\n\\n        // Check min borrow in this liquidation method, the deleverage calculation should adhere to the min borrow\\n        vaultAccount.setVaultAccountForLiquidation(vaultConfig, currencyIndex, depositAmountPrimeCash, true);\\n\\n        emit VaultDeleverageAccount(vault, account, currencyId, vaultSharesToLiquidator, depositAmountPrimeCash);\\n        emit VaultLiquidatorProfit(vault, account, liquidator, vaultSharesToLiquidator, true);\\n\\n        _transferVaultSharesToLiquidator(\\n            liquidator, vaultConfig, vaultSharesToLiquidator, vaultAccount.maturity\\n        );\\n\\n        Emitter.emitVaultDeleverage(\\n            liquidator, account, vault, currencyId, vaultState.maturity,\\n            depositAmountPrimeCash, vaultSharesToLiquidator\\n        );\\n    }\\n```\\n\\n`currencyIndex` represents which currency is being liquidated and `depositUnderlyingInternal` the amount of debt being reduced. Only one currency's debt can be updated here.\\n```\\nFile: VaultLiquidationAction.sol\\n    function _reduceAccountDebt(\\n        VaultConfig memory vaultConfig,\\n        VaultState memory vaultState,\\n        VaultAccount memory vaultAccount,\\n        PrimeRate memory primeRate,\\n        uint256 currencyIndex,\\n        int256 depositUnderlyingInternal,\\n        bool checkMinBorrow\\n    ) private {\\n        if (currencyIndex == 0) {\\n            vaultAccount.updateAccountDebt(vaultState, depositUnderlyingInternal, 0);\\n            vaultState.setVaultState(vaultConfig);\\n        } else {\\n            // Only set one of the prime rates, the other prime rate is not used since\\n            // the net debt amount is set to zero\\n            PrimeRate[2] memory pr;\\n            pr[currencyIndex - 1] = primeRate;\\n\\n            VaultSecondaryBorrow.updateAccountSecondaryDebt(\\n                vaultConfig,\\n                vaultAccount.account,\\n                vaultAccount.maturity,\\n                currencyIndex == 1 ? depositUnderlyingInternal : 0,\\n                currencyIndex == 2 ? depositUnderlyingInternal : 0,\\n                pr,\\n                checkMinBorrow\\n            );\\n        }\\n    }\\n```\\n\\nIn the case of vault settlement, through self-liquidation, users can setup their debt and cash holdings post-settlement, such that both `accountDebtOne` and `accountDebtTwo` are non-zero and less than `vaultConfig.minAccountSecondaryBorrow`. The objective would be to have zero primary debt and `Y` secondary debt and `X` secondary cash. Post-settlement, cash is used to offset debt (Y - `X` < minAccountSecondaryBorrow) and due to the lack of `checkMinBorrow` in `VaultAccountAction.settleVaultAccount()`, both secondary currencies can have debt holdings below the minimum amount.\\nNow when `deleverageAccount()` is called on a prime vault account, debts are paid down directly. However, if we are only able to pay down one secondary currency at a time, `checkMinBorrow` will fail in `VaultSecondaryBorrow.updateAccountSecondaryDebt()` because both debts are checked.\\n```\\nFile: VaultSecondaryBorrow.sol\\n        if (checkMinBorrow) {\\n            // No overflow on negation due to overflow checks above\\n            require(accountDebtOne == 0 || vaultConfig.minAccountSecondaryBorrow[0] <= -accountDebtOne, "min borrow");\\n            require(accountDebtTwo == 0 || vaultConfig.minAccountSecondaryBorrow[1] <= -accountDebtTwo, "min borrow");\\n        }\\n```\\n\\nNo prime fees accrue on secondary debt, hence, this debt will never reach a point where it is above the minimum borrow amount.
Either allow for multiple currencies to be liquidated or ensure that `checkMinBorrow` is performed only on the currency which is being liquidated.
Malicious actors can generate vault accounts which cannot be liquidated. Through opening numerous vault positions, Notional can rack up significant exposure and accrue bad debt as a result.
```\\nFile: VaultLiquidationAction.sol\\n    function deleverageAccount(\\n        address account,\\n        address vault,\\n        address liquidator,\\n        uint16 currencyIndex,\\n        int256 depositUnderlyingInternal\\n    ) external payable nonReentrant override returns (\\n        uint256 vaultSharesToLiquidator,\\n        int256 depositAmountPrimeCash\\n    ) {\\n        require(currencyIndex < 3);\\n        (\\n            VaultConfig memory vaultConfig,\\n            VaultAccount memory vaultAccount,\\n            VaultState memory vaultState\\n        ) = _authenticateDeleverage(account, vault, liquidator);\\n\\n        PrimeRate memory pr;\\n        // Currency Index is validated in this method\\n        (\\n            depositUnderlyingInternal,\\n            vaultSharesToLiquidator,\\n            pr\\n        ) = IVaultAccountHealth(address(this)).calculateDepositAmountInDeleverage(\\n            currencyIndex, vaultAccount, vaultConfig, vaultState, depositUnderlyingInternal\\n        );\\n\\n        uint16 currencyId = vaultConfig.borrowCurrencyId;\\n        if (currencyIndex == 1) currencyId = vaultConfig.secondaryBorrowCurrencies[0];\\n        else if (currencyIndex == 2) currencyId = vaultConfig.secondaryBorrowCurrencies[1];\\n\\n        Token memory token = TokenHandler.getUnderlyingToken(currencyId);\\n        // Excess ETH is returned to the liquidator natively\\n        (/* */, depositAmountPrimeCash) = TokenHandler.depositUnderlyingExternal(\\n            liquidator, currencyId, token.convertToExternal(depositUnderlyingInternal), pr, false \\n        );\\n\\n        // Do not skip the min borrow check here\\n        vaultAccount.vaultShares = vaultAccount.vaultShares.sub(vaultSharesToLiquidator);\\n        if (vaultAccount.maturity == Constants.PRIME_CASH_VAULT_MATURITY) {\\n            // Vault account will not incur a cash balance if they are in the prime cash maturity, their debts\\n            // will be paid down directly.\\n            _reduceAccountDebt(\\n                vaultConfig, vaultState, vaultAccount, pr, currencyIndex, depositUnderlyingInternal, true\\n            );\\n            depositAmountPrimeCash = 0;\\n        }\\n\\n        // Check min borrow in this liquidation method, the deleverage calculation should adhere to the min borrow\\n        vaultAccount.setVaultAccountForLiquidation(vaultConfig, currencyIndex, depositAmountPrimeCash, true);\\n\\n        emit VaultDeleverageAccount(vault, account, currencyId, vaultSharesToLiquidator, depositAmountPrimeCash);\\n        emit VaultLiquidatorProfit(vault, account, liquidator, vaultSharesToLiquidator, true);\\n\\n        _transferVaultSharesToLiquidator(\\n            liquidator, vaultConfig, vaultSharesToLiquidator, vaultAccount.maturity\\n        );\\n\\n        Emitter.emitVaultDeleverage(\\n            liquidator, account, vault, currencyId, vaultState.maturity,\\n            depositAmountPrimeCash, vaultSharesToLiquidator\\n        );\\n    }\\n```\\n