Vulnerability: Incorrect scaling of the spot price
Severity: high

Description:
The incorrect scaling of the spot price leads to the incorrect spot price, which is later compared with the oracle price.\\nIf 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.\\nPer the comment and source code at Lines 97 to 103, the `SPOT_PRICE.getComposableSpotPrices` is expected to return the spot price in native decimals.\\n```\\nFile: BalancerComposableAuraVault.sol\\n    function _checkPriceAndCalculateValue() internal view override returns (uint256) {\\n        (uint256[] memory balances, uint256[] memory spotPrices) = SPOT_PRICE.getComposableSpotPrices(\\n            BALANCER_POOL_ID,\\n            address(BALANCER_POOL_TOKEN),\\n            PRIMARY_INDEX()\\n        );\\n\\n        // Spot prices are returned in native decimals, convert them all to POOL_PRECISION\\n        // as required in the _calculateLPTokenValue method.\\n        (/* */, uint8[] memory decimals) = TOKENS();\\n        for (uint256 i; i < spotPrices.length; i++) {\\n            spotPrices[i] = spotPrices[i] * POOL_PRECISION() / 10 ** decimals[i];\\n        }\\n\\n        return _calculateLPTokenValue(balances, spotPrices);\\n    }\\n```\\n\\nWithin the `getComposableSpotPrices` function, it will trigger the `_calculateStableMathSpotPrice` function. When the primary and secondary balances are passed into the `StableMath._calculateInvariant` and `StableMath._calcSpotPrice` functions, they are scaled up to 18 decimals precision as StableMath functions only work with balances that have been normalized to 18 decimals.\\nAssuming that the following states:\\nPrimary Token = USDC (6 decimals)\\nSecondary Token = DAI (18 decimals)\\nPrimary Balance = 100 USDC (=100 * 1e6)\\nSecondary Balance = 100 DAI (=100 * 1e18)\\nscalingFactors[USDC] = 1e12 * Fixed.ONE (1e18) = 1e30\\nscalingFactors[DAI] = 1e0 * Fixed.ONE (1e18) = 1e18\\nThe price between USDC and DAI is 1:1\\nAfter scaling the primary and secondary balances, the scaled balances will be as follows:\\n```\\nscaledPrimary = balances[USDC] * scalingFactors[USDC] / BALANCER_PRECISION\\nscaledPrimary = 100 * 1e6 * 1e30 / 1e18\\nscaledPrimary = 100 * 1e18\\n\\nscaledSecondary = balances[DAI] * scalingFactors[DAI] / BALANCER_PRECISION\\nscaledSecondary = 100 * 1e18 * 1e18 / 1e18\\nscaledSecondary = 100 * 1e18\\n```\\n\\nThe spot price returned from the `StableMath._calcSpotPrice` function at Line 93 will be `1e18` (1:1).\\n```\\nFile: BalancerSpotPrice.sol\\n    function _calculateStableMathSpotPrice(\\n        uint256 ampParam,\\n        uint256[] memory scalingFactors,\\n        uint256[] memory balances,\\n        uint256 scaledPrimary,\\n        uint256 primaryIndex,\\n        uint256 index2\\n    ) internal pure returns (uint256 spotPrice) {\\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        // Remove scaling factors from spot price\\n        spotPrice = spotPrice * scalingFactors[primaryIndex] / scalingFactors[index2];\\n    }\\n```\\n\\nSubsequently, in Line 96 above, the code attempts to remove the scaling factor from the spot price (1e18).\\n```\\nspotPrice = spotPrice * scalingFactors[USDC] / scalingFactors[DAI];\\nspotPrice = 1e18 * 1e30 / 1e18\\nspotPrice = 1e30\\nspotPrice = 1e12 * 1e18\\n```\\n\\nThe `spotPrice[DAI-Secondary]` is not denominated in native precision after the scaling. The `SPOT_PRICE.getComposableSpotPrices` will return the following spot prices:\\n```\\nspotPrice[USDC-Primary] = 0\\nspotPrice[DAI-Secondary] = 1e12 * 1e18\\n```\\n\\nThe returned spot prices will be scaled to POOL_PRECISION (1e18). After the scaling, the spot price remains the same:\\n```\\nspotPrice[DAI-Secondary] = spotPrice[DAI-Secondary] * POOL_PRECISION / DAI_Decimal\\nspotPrice[DAI-Secondary] = 1e12 * 1e18 * 1e18 / 1e18\\nspotPrice[DAI-Secondary] = 1e12 * 1e18\\n```\\n\\nThe converted spot prices will be passed into the `_calculateLPTokenValue` function. Within the `_calculateLPTokenValue` function, the oracle price for DAI<>USDC will be `1e18`. From here, the `spotPrice[DAI-Secondary]` (1e12 * 1e18) is significantly different from the oracle price (1e18), which will cause the pool manipulation check to revert.\\n```\\nFile: BalancerComposableAuraVault.sol\\n    function _checkPriceAndCalculateValue() internal view override returns (uint256) {\\n        (uint256[] memory balances, uint256[] memory spotPrices) = SPOT_PRICE.getComposableSpotPrices(\\n            BALANCER_POOL_ID,\\n            address(BALANCER_POOL_TOKEN),\\n            PRIMARY_INDEX()\\n        );\\n\\n        // Spot prices are returned in native decimals, convert them all to POOL_PRECISION\\n        // as required in the _calculateLPTokenValue method.\\n        (/* */, uint8[] memory decimals) = TOKENS();\\n        for (uint256 i; i < spotPrices.length; i++) {\\n            spotPrices[i] = spotPrices[i] * POOL_PRECISION() / 10 ** decimals[i];\\n        }\\n\\n        return _calculateLPTokenValue(balances, spotPrices);\\n    }\\n```\\n

Impact:
The spot price is used to verify if the pool has been manipulated before executing certain key vault actions (e.g. reinvest rewards).\\nIf the spot price is incorrect, it might potentially result in the following:\\nFailure to detect the pool has been manipulated, resulting in the trade to execute against the manipulated pool, leading to a loss of assets.\\nUnintended reverts due to false positives, breaking core functionalities of the protocol that rely on the `_checkPriceAndCalculateValue` function.\\nThe affected `_checkPriceAndCalculateValue` function was found to be used within the following functions:\\n`reinvestReward` - If the `_checkPriceAndCalculateValue` function is malfunctioning or reverts unexpectedly, the protocol will not be able to reinvest, leading to a loss of value for the vault shareholders.\\n`convertStrategyToUnderlying` - This function is used by Notional V3 for the purpose of computing the collateral values and the account's health factor. If the `_checkPriceAndCalculateValue` function reverts unexpectedly due to an incorrect invariant/spot price, many of Notional's core functions will break. In addition, the collateral values and the account's health factor might be inflated if it fails to detect a manipulated pool due to incorrect invariant/spot price, potentially allowing the malicious actors to drain the main protocol.

Recommended mitigation:
The spot price returned from `StableMath._calcSpotPrice` is denominated in 1e18 (POOL_PRECISION) since the inputted balances are normalized to 18 decimals. The scaling factors are used to normalize a balance to 18 decimals. By dividing or scaling down the spot price by the scaling factor, the native spot price will be returned.\\n```\\nspotPrice[DAI-Secondary] = spotPrice[DAI-Secondary] * Fixed.ONE / scalingFactors[DAI];\\nspotPrice = 1e18 * Fixed.ONE / (1e0 * Fixed.ONE)\\nspotPrice = 1e18 * 1e18 / (1e0 * 1e18)\\nspotPrice = 1e18\\n```\\n