Vulnerability: Rebalance might be skipped even if the external lending is unhealthy
Severity: medium

Description:
The deviation between the target and current lending amount (offTargetPercentage) will be underestimated due to incorrect calculation. As a result, a rebalancing might be skipped even if the existing external lending is unhealthy.\\nThe formula used within the `_isExternalLendingUnhealthy` function below calculating the `offTargetPercentage` can be simplified as follows for the readability of this issue.\\n$$ offTargetPercentage = \\frac{\\mid currentExternalUnderlyingLend - targetAmount \\mid}{currentExternalUnderlyingLend + targetAmount} \\times 100% $$\\nAssume that the `targetAmount` is 100 and `currentExternalUnderlyingLend` is 90. The off-target percentage will be 5.26%, which is incorrect.\\n```\\noffTargetPercentage = abs(90 - 100) / (100 + 90) = 10 / 190 = 0.0526 = 5.26%\\n```\\n\\nThe correct approach is to calculate the off-target percentages as a ratio of the difference to the target:\\n$$ offTargetPercentage = \\frac{\\mid currentExternalUnderlyingLend - targetAmount \\mid}{targetAmount} \\times 100% $$\\n```\\noffTargetPercentage = abs(90 - 100) / (100) = 10 / 100 = 0.0526 = 10%\\n```\\n\\n```\\nFile: TreasuryAction.sol\\n    function _isExternalLendingUnhealthy(\\n        uint16 currencyId,\\n        IPrimeCashHoldingsOracle oracle,\\n        PrimeRate memory pr\\n    ) internal view returns (bool isExternalLendingUnhealthy, OracleData memory oracleData, uint256 targetAmount) {\\n        oracleData = oracle.getOracleData(); \\n\\n        RebalancingTargetData memory rebalancingTargetData =\\n            LibStorage.getRebalancingTargets()[currencyId][oracleData.holding]; \\n        PrimeCashFactors memory factors = PrimeCashExchangeRate.getPrimeCashFactors(currencyId); \\n        Token memory underlyingToken = TokenHandler.getUnderlyingToken(currencyId); \\n\\n        targetAmount = ExternalLending.getTargetExternalLendingAmount(\\n            underlyingToken, factors, rebalancingTargetData, oracleData, pr\\n        ); \\n\\n        if (oracleData.currentExternalUnderlyingLend == 0) { \\n            // If this is zero then there is no outstanding lending.\\n            isExternalLendingUnhealthy = false; \\n        } else {\\n            uint256 offTargetPercentage = oracleData.currentExternalUnderlyingLend.toInt() \\n                .sub(targetAmount.toInt()).abs()\\n                .toUint()\\n                .mul(uint256(Constants.PERCENTAGE_DECIMALS))\\n                .div(targetAmount.add(oracleData.currentExternalUnderlyingLend)); \\n            \\n            // prevent rebalance if change is not greater than 1%, important for health check and avoiding triggering\\n            // rebalance shortly after rebalance on minimum change\\n            isExternalLendingUnhealthy = \\n                (targetAmount < oracleData.currentExternalUnderlyingLend) && (offTargetPercentage > 0); \\n        }\\n    }\\n```\\n

Impact:
The deviation between the target and current lending amount (offTargetPercentage) will be underestimated by approximately half the majority of the time. As a result, a rebalance intended to remediate the unhealthy external lending might be skipped since the code incorrectly assumes that it has not hit the off-target percentage. External lending beyond the target will affect the liquidity of the protocol, potentially resulting in withdrawal or liquidation, having issues executed due to insufficient liquidity.

Recommended mitigation:
Consider calculating the off-target percentages as a ratio of the difference to the target:\\n$$ offTargetPercentage = \\frac{\\mid currentExternalUnderlyingLend - targetAmount \\mid}{targetAmount} \\times 100% $$