`VaultAccountSecondaryDebtShareStorage.maturity` will be cleared prematurely
`VaultAccountSecondaryDebtShareStorage.maturity` will be cleared prematurely during liquidation\\nIf both the `accountDebtOne` and `accountDebtTwo` of secondary currencies are zero, Notional will consider both debt shares to be cleared to zero, and the maturity will be cleared as well as shown below.\\n```\\nFile: VaultSecondaryBorrow.sol\\n    function _setAccountMaturity(\\n        VaultAccountSecondaryDebtShareStorage storage accountStorage,\\n        int256 accountDebtOne,\\n        int256 accountDebtTwo,\\n        uint40 maturity\\n    ) private {\\n        if (accountDebtOne == 0 && accountDebtTwo == 0) {\\n            // If both debt shares are cleared to zero, clear the maturity as well.\\n            accountStorage.maturity = 0;\\n        } else {\\n            // In all other cases, set the account to the designated maturity\\n            accountStorage.maturity = maturity;\\n        }\\n    }\\n```\\n\\n`VaultLiquidationAction.deleverageAccount` function\\nWithin the `VaultLiquidationAction.deleverageAccount` function, it will call the `_reduceAccountDebt` function.\\nReferring to the `_reduceAccountDebt` function below. Assume that the `currencyIndex` reference to a secondary currency. In this case, the else logic in Line 251 will be executed. An important point to take note of that is critical to understand this bug is that only ONE of the prime rates will be set as it assumes that the other prime rate will not be used (Refer to Line 252 - 255). However, this assumption is incorrect.\\nAssume that the `currencyIndex` is `1`. Then `netUnderlyingDebtOne` parameter will be set to a non-zero value (depositUnderlyingInternal) at Line 261 while `netUnderlyingDebtTwo` parameter will be set to zero at Line 262. This is because, in Line 263 of the `_reduceAccountDebt` function, the `pr[0]` will be set to the prime rate, while the `pr[1]` will be zero or empty. It will then proceed to call the `VaultSecondaryBorrow.updateAccountSecondaryDebt`\\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\\nWithin the `updateAccountSecondaryDebt` function, at Line 272, assume that `accountStorage.accountDebtTwo` is `100`. However, since `pr[1]` is not initialized, the `VaultStateLib.readDebtStorageToUnderlying` will return a zero value and set the `accountDebtTwo` to zero.\\nAssume that the liquidator calls the `deleverageAccount` function to clear all the debt of the `currencyIndex` secondary currency. Line 274 will be executed, and `accountDebtOne` will be set to zero.\\nNote that at this point, both `accountDebtOne` and `accountDebtTwo` are zero. At Line 301, the `_setAccountMaturity` will set the `accountStorage.maturity = 0` , which clears the vault account's maturity.\\nAn important point here is that the liquidator did not clear the `accountDebtTwo`. Yet, `accountDebtTwo` became zero in memory during the execution and caused Notional to wrongly assume that both debt shares had been cleared to zero.\\n```\\nFile: VaultSecondaryBorrow.sol\\n    function updateAccountSecondaryDebt(\\n        VaultConfig memory vaultConfig,\\n        address account,\\n        uint256 maturity,\\n        int256 netUnderlyingDebtOne,\\n        int256 netUnderlyingDebtTwo,\\n        PrimeRate[2] memory pr,\\n        bool checkMinBorrow\\n    ) internal {\\n        VaultAccountSecondaryDebtShareStorage storage accountStorage = \\n            LibStorage.getVaultAccountSecondaryDebtShare()[account][vaultConfig.vault];\\n        // Check maturity\\n        uint256 accountMaturity = accountStorage.maturity;\\n        require(accountMaturity == maturity || accountMaturity == 0);\\n        \\n        int256 accountDebtOne = VaultStateLib.readDebtStorageToUnderlying(pr[0], maturity, accountStorage.accountDebtOne); \\n        int256 accountDebtTwo = VaultStateLib.readDebtStorageToUnderlying(pr[1], maturity, accountStorage.accountDebtTwo);\\n        if (netUnderlyingDebtOne != 0) {\\n            accountDebtOne = accountDebtOne.add(netUnderlyingDebtOne);\\n\\n            _updateTotalSecondaryDebt(\\n                vaultConfig, account, vaultConfig.secondaryBorrowCurrencies[0], maturity, netUnderlyingDebtOne, pr[0]\\n            );\\n\\n            accountStorage.accountDebtOne = VaultStateLib.calculateDebtStorage(pr[0], maturity, accountDebtOne)\\n                .neg().toUint().toUint80();\\n        }\\n\\n        if (netUnderlyingDebtTwo != 0) {\\n            accountDebtTwo = accountDebtTwo.add(netUnderlyingDebtTwo);\\n\\n            _updateTotalSecondaryDebt(\\n                vaultConfig, account, vaultConfig.secondaryBorrowCurrencies[1], maturity, netUnderlyingDebtTwo, pr[1]\\n            );\\n\\n            accountStorage.accountDebtTwo = VaultStateLib.calculateDebtStorage(pr[1], maturity, accountDebtTwo)\\n                .neg().toUint().toUint80();\\n        }\\n\\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        _setAccountMaturity(accountStorage, accountDebtOne, accountDebtTwo, maturity.toUint40());\\n    }\\n```\\n\\nThe final state will be `VaultAccountSecondaryDebtShareStorage` as follows:\\n`maturity` and `accountDebtOne` are zero\\n`accountDebtTwo` = 100\\n```\\nstruct VaultAccountSecondaryDebtShareStorage {\\n    // Maturity for the account's secondary borrows. This is stored separately from\\n    // the vault account maturity to ensure that we have access to the proper state\\n    // during a roll borrow position. It should never be allowed to deviate from the\\n    // vaultAccount.maturity value (unless it is cleared to zero).\\n    uint40 maturity;\\n    // Account debt for the first secondary currency in either fCash or pCash denomination\\n    uint80 accountDebtOne;\\n    // Account debt for the second secondary currency in either fCash or pCash denomination\\n    uint80 accountDebtTwo;\\n}\\n```\\n\\nFirstly, it does not make sense to have `accountDebtTwo` but no `maturity` in storage, which also means the vault account data is corrupted. Secondly, when `maturity` is zero, it also means that the vault account did not borrow anything from Notional. Lastly, many vault logic would break since it relies on the `maturity` value.\\n`VaultLiquidationAction.liquidateVaultCashBalance` function\\nThe root cause lies in the implementation of the `_reduceAccountDebt` function. Since `liquidateVaultCashBalance` function calls the `_reduceAccountDebt` function to reduce the debt of the vault account being liquidated, the same issue will occur here.
Fetch the prime rate of both secondary currencies because they are both needed within the `updateAccountSecondaryDebt` function when converting debt storage to underlying.\\n```\\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// Remove the line below\\n           pr[currencyIndex // Remove the line below\\n 1] = primeRate;\\n// Add the line below\\n     pr = VaultSecondaryBorrow.getSecondaryPrimeRateStateful(vaultConfig);\\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
Any vault logic that relies on the VaultAccountSecondaryDebtShareStorage's maturity value would break since it has been cleared (set to zero). For instance, a vault account cannot be settled anymore as the following `settleSecondaryBorrow` function will always revert. Since `storedMaturity == 0` but `accountDebtTwo` is not zero, Line 399 below will always revert.\\nAs a result, a vault account with secondary currency debt cannot be settled. This also means that the vault account cannot exit since a vault account needs to be settled before exiting, causing users' assets to be stuck within the protocol.\\n```\\nFile: VaultSecondaryBorrow.sol\\n    function settleSecondaryBorrow(VaultConfig memory vaultConfig, address account) internal returns (bool) {\\n        if (!vaultConfig.hasSecondaryBorrows()) return false;\\n\\n        VaultAccountSecondaryDebtShareStorage storage accountStorage = \\n            LibStorage.getVaultAccountSecondaryDebtShare()[account][vaultConfig.vault];\\n        uint256 storedMaturity = accountStorage.maturity;\\n\\n        // NOTE: we can read account debt directly since prime cash maturities never enter this block of code.\\n        int256 accountDebtOne = -int256(uint256(accountStorage.accountDebtOne));\\n        int256 accountDebtTwo = -int256(uint256(accountStorage.accountDebtTwo));\\n        \\n        if (storedMaturity == 0) {\\n            // Handles edge condition where an account is holding vault shares past maturity without\\n            // any debt position.\\n            require(accountDebtOne == 0 && accountDebtTwo == 0); \\n        } else {\\n```\\n\\nIn addition, the vault account data is corrupted as there is a secondary debt without maturity, which might affect internal accounting and tracking.
```\\nFile: VaultSecondaryBorrow.sol\\n    function _setAccountMaturity(\\n        VaultAccountSecondaryDebtShareStorage storage accountStorage,\\n        int256 accountDebtOne,\\n        int256 accountDebtTwo,\\n        uint40 maturity\\n    ) private {\\n        if (accountDebtOne == 0 && accountDebtTwo == 0) {\\n            // If both debt shares are cleared to zero, clear the maturity as well.\\n            accountStorage.maturity = 0;\\n        } else {\\n            // In all other cases, set the account to the designated maturity\\n            accountStorage.maturity = maturity;\\n        }\\n    }\\n```\\n