Single-sided instead of proportional exit is performed during emergency exit
Single-sided instead of proportional exit is performed during emergency exit, which could lead to a loss of assets during emergency exit and vault restoration.\\nPer the comment in Line 476 below, the BPT should be redeemed proportionally to underlying tokens during an emergency exit. However, it was found that the `_unstakeAndExitPool` function is executed with the `isSingleSided` parameter set to `true`.\\n```\\nFile: SingleSidedLPVaultBase.sol\\n    /// @notice Allows the emergency exit role to trigger an emergency exit on the vault.\\n    /// In this situation, the `claimToExit` is withdrawn proportionally to the underlying\\n    /// tokens and held on the vault. The vault is locked so that no entries, exits or\\n    /// valuations of vaultShares can be performed.\\n    /// @param claimToExit if this is set to zero, the entire pool claim is withdrawn\\n    function emergencyExit(\\n        uint256 claimToExit, bytes calldata /* data */\\n    ) external override onlyRole(EMERGENCY_EXIT_ROLE) {\\n        StrategyVaultState memory state = VaultStorage.getStrategyVaultState();\\n        if (claimToExit == 0) claimToExit = state.totalPoolClaim;\\n\\n        // By setting min amounts to zero, we will accept whatever tokens come from the pool\\n        // in a proportional exit. Front running will not have an effect since no trading will\\n        // occur during a proportional exit.\\n        _unstakeAndExitPool(claimToExit, new uint256[](NUM_TOKENS()), true);\\n```\\n\\nIf the `isSingleSided` is set to `True`, the `EXACT_BPT_IN_FOR_ONE_TOKEN_OUT` will be used, which is incorrect. Per the Balancer's documentation, `EXACT_BPT_IN_FOR_ONE_TOKEN_OUT` is a single asset exit where the user sends a precise quantity of BPT, and receives an estimated but unknown (computed at run time) quantity of a single token.\\nTo perform a proportional exit, the `EXACT_BPT_IN_FOR_TOKENS_OUT` should be used instead.\\n```\\nFile: BalancerComposableAuraVault.sol\\n    function _unstakeAndExitPool(\\n        uint256 poolClaim, uint256[] memory minAmounts, bool isSingleSided\\n    ) internal override returns (uint256[] memory exitBalances) {\\n        bool success = AURA_REWARD_POOL.withdrawAndUnwrap(poolClaim, false); // claimRewards = false\\n        require(success);\\n\\n        bytes memory customData;\\n        if (isSingleSided) {\\n..SNIP..\\n            uint256 primaryIndex = PRIMARY_INDEX();\\n            customData = abi.encode(\\n                IBalancerVault.ComposableExitKind.EXACT_BPT_IN_FOR_ONE_TOKEN_OUT,\\n                poolClaim,\\n                primaryIndex < BPT_INDEX ?  primaryIndex : primaryIndex - 1\\n            );\\n```\\n\\nThe same issue affects the Curve's implementation of the `_unstakeAndExitPool` function.\\n```\\nFile: Curve2TokenConvexVault.sol\\n    function _unstakeAndExitPool(\\n        uint256 poolClaim, uint256[] memory _minAmounts, bool isSingleSided\\n    ) internal override returns (uint256[] memory exitBalances) {\\n..SNIP..\\n        ICurve2TokenPool pool = ICurve2TokenPool(CURVE_POOL);\\n        exitBalances = new uint256[](2);\\n        if (isSingleSided) {\\n            // Redeem single-sided\\n            exitBalances[_PRIMARY_INDEX] = pool.remove_liquidity_one_coin(\\n                poolClaim, int8(_PRIMARY_INDEX), _minAmounts[_PRIMARY_INDEX]\\n            );\\n```\\n
Set the `isSingleSided` parameter to `false` when calling the `_unstakeAndExitPool` function to ensure that the proportional exit is performed.\\n```\\nfunction emergencyExit(\\n    uint256 claimToExit, bytes calldata /* data */\\n) external override onlyRole(EMERGENCY_EXIT_ROLE) {\\n    StrategyVaultState memory state = VaultStorage.getStrategyVaultState();\\n    if (claimToExit == 0) claimToExit = state.totalPoolClaim;\\n ..SNIP..\\n// Remove the line below\\n   _unstakeAndExitPool(claimToExit, new uint256[](NUM_TOKENS()), true);\\n// Add the line below\\n   _unstakeAndExitPool(claimToExit, new uint256[](NUM_TOKENS()), false);\\n```\\n
The following are some of the impacts of this issue, which lead to loss of assets:\\nRedeeming LP tokens one-sided incurs unnecessary slippage as tokens have to be swapped internally to one specific token within the pool, resulting in fewer assets received.\\nPer the source code comment below, in other words, unless a proportional exit is performed, the emergency exit will be subjected to front-run attack and slippage.\\n`File: SingleSidedLPVaultBase.sol\\n486:         // By setting min amounts to zero, we will accept whatever tokens come from the pool\\n487:         // in a proportional exit. Front running will not have an effect since no trading will\\n488:         // occur during a proportional exit.\\n489:         _unstakeAndExitPool(claimToExit, new uint256[](NUM_TOKENS()), true);`\\nAfter the emergency exit, the vault only held one of the pool tokens. To re-enter the pool, the vault has to either swap the token to other pool tokens on external DEXs to maintain the proportion or perform a single-sided join. Both of these methods will incur unnecessary slippage, resulting in fewer LP tokens received at the end.
```\\nFile: SingleSidedLPVaultBase.sol\\n    /// @notice Allows the emergency exit role to trigger an emergency exit on the vault.\\n    /// In this situation, the `claimToExit` is withdrawn proportionally to the underlying\\n    /// tokens and held on the vault. The vault is locked so that no entries, exits or\\n    /// valuations of vaultShares can be performed.\\n    /// @param claimToExit if this is set to zero, the entire pool claim is withdrawn\\n    function emergencyExit(\\n        uint256 claimToExit, bytes calldata /* data */\\n    ) external override onlyRole(EMERGENCY_EXIT_ROLE) {\\n        StrategyVaultState memory state = VaultStorage.getStrategyVaultState();\\n        if (claimToExit == 0) claimToExit = state.totalPoolClaim;\\n\\n        // By setting min amounts to zero, we will accept whatever tokens come from the pool\\n        // in a proportional exit. Front running will not have an effect since no trading will\\n        // occur during a proportional exit.\\n        _unstakeAndExitPool(claimToExit, new uint256[](NUM_TOKENS()), true);\\n```\\n