Vulnerability: Incorrect Spot Price
Severity: high

Description:
Multiple discrepancies between the implementation of Leverage Vault's `_calcSpotPrice` function and SDK were observed, which indicate that the computed spot price is incorrect.\\nIf the spot price is incorrect, it might potentially fail to detect the pool has been manipulated. In the worst-case scenario, the trade proceeds to execute against the manipulated pool, leading to a loss of assets.\\nThe `BalancerSpotPrice._calculateStableMathSpotPrice` function relies on the `StableMath._calcSpotPrice` to compute the spot price of two tokens.\\n```\\nFile: BalancerSpotPrice.sol\\n    function _calculateStableMathSpotPrice(\\n..SNIP..\\n        // Apply scale factors\\n        uint256 secondary = balances[index2] * scalingFactors[index2] / BALANCER_PRECISION;\\n\\n        uint256 invariant = StableMath._calculateInvariant(\\n            ampParam, StableMath._balances(scaledPrimary, secondary), true // round up\\n        );\\n\\n        spotPrice = StableMath._calcSpotPrice(ampParam, invariant, scaledPrimary, secondary);\\n```\\n\\n```\\nFile: StableMath.sol\\n    /**\\n     * @dev Calculates the spot price of token Y in token X.\\n     */\\n    function _calcSpotPrice(\\n        uint256 amplificationParameter,\\n        uint256 invariant, \\n        uint256 balanceX,\\n        uint256 balanceY\\n    ) internal pure returns (uint256) {\\n        /**************************************************************************************************************\\n        //                                                                                                           //\\n        //                             2.a.x.y + a.y^2 + b.y                                                         //\\n        // spot price Y/X = - dx/dy = -----------------------                                                        //\\n        //                             2.a.x.y + a.x^2 + b.x                                                         //\\n        //                                                                                                           //\\n        // n = 2                                                                                                     //\\n        // a = amp param * n                                                                                         //\\n        // b = D + a.(S - D)                                                                                         //\\n        // D = invariant                                                                                             //\\n        // S = sum of balances but x,y = 0 since x  and y are the only tokens                                        //\\n        **************************************************************************************************************/\\n\\n        unchecked {\\n            uint256 a = (amplificationParameter * 2) / _AMP_PRECISION;\\n            uint256 b = Math.mul(invariant, a).sub(invariant);\\n\\n            uint256 axy2 = Math.mul(a * 2, balanceX).mulDown(balanceY); // n = 2\\n\\n            // dx = a.x.y.2 + a.y^2 - b.y\\n            uint256 derivativeX = axy2.add(Math.mul(a, balanceY).mulDown(balanceY)).sub(b.mulDown(balanceY));\\n\\n            // dy = a.x.y.2 + a.x^2 - b.x\\n            uint256 derivativeY = axy2.add(Math.mul(a, balanceX).mulDown(balanceX)).sub(b.mulDown(balanceX));\\n\\n            // The rounding direction is irrelevant as we're about to introduce a much larger error when converting to log\\n            // space. We use `divUp` as it prevents the result from being zero, which would make the logarithm revert. A\\n            // result of zero is therefore only possible with zero balances, which are prevented via other means.\\n            return derivativeX.divUp(derivativeY);\\n        }\\n    }\\n```\\n\\nOn a high level, the spot price is computed by determining the pool derivatives. The Balancer SDK's provide a feature to compute the spot price of any two tokens within a pool, and it leverages the `_poolDerivatives` function.\\nThe existing function for computing the spot price of any two tokens of a composable pool has the following errors or discrepancies from the approach used to compute the spot price in Balancer SDK, which might lead to an inaccurate spot price being computed.\\nInstance 1\\nThe comments and SDK add `b.y` and `b.x` to the numerator and denominator, respectively, in the formula. However, the code performs a subtraction.\\nInstance 2\\nPer the comment and SDK code, $b = (S - D) a + D$.\\nHowever, assuming that $S$ is zero (for a two-token pool), the following code in the Leverage Vault to compute $b$ is not equivalent to the above.\\n```\\nuint256 b = Math.mul(invariant, a).sub(invariant);\\n```\\n\\nInstance 3\\nThe $S$ in the code will always be zero because the code is catered only for two-token pools. However, for a composable pool, it can support up to five (5) tokens in a pool. $S$ should be as follows, where $balances$ is all the tokens in a composable pool except for BPT.\\n$$ S = \\sum_{i \\neq \\text{tokenIndexIn}, i \\neq \\text{tokenIndexOut}} \\text{balances}[i] $$\\nInstance 4\\nInstance 5\\nPer SDK, the amplification factor is scaled down by $n^{(n - 1)}$ where $n$ is the number of tokens in a composable pool (excluding BPT). Otherwise, this was not implemented within the code.

Impact:
The spot price is used to verify if the pool has been manipulated before executing certain key vault actions (e.g. reinvest rewards). If the spot price is incorrect, it might potentially fail to detect the pool has been manipulated or result in unintended reverts due to false positives. In the worst-case scenario, the trade proceeds to execute against the manipulated pool, leading to a loss of assets.

Recommended mitigation:
Given multiple discrepancies between the implementation of Leverage Vault's `_calcSpotPrice` function and SDK and due to the lack of information on the web, it is recommended to reach out to the Balancer's protocol team to identify the actual formula used to determine a spot price of any two tokens within a composable pool and check out if the formula in the SDK is up-to-date to be used against the composable pool.\\nIt is also recommended to implement additional tests to ensure that the `_calcSpotPrice` returns the correct spot price of composable pools.\\nIn addition, the `StableMath._calcSpotPrice` function is no longer used or found within the current version of Balancer's composable pool. Thus, there is no guarantee that the math within the `StableMath._calcSpotPrice` works with the current implementation. It is recommended to use the existing method in the current Composable Pool's StableMath, such as `_calcOutGivenIn` (ensure the fee is excluded) to compute the spot price.