Vulnerability: Users are forced to use the first pool returned by the Curve Registry
Severity: medium

Description:
If multiple pools support the exchange, users are forced to use the first pool returned by the Curve Registry. The first pool returned by Curve Registry might not be the most optimal pool to trade with. The first pool might have lesser liquidity, larger slippage, and higher fee than the other pools, resulting in the trade returning lesser assets than expected.\\nWhen performing a trade via the `CurveAdapter._exactInSingle` function, it will call the `CURVE_REGISTRY.find_pool_for_coins` function to find the available pools for exchanging two coins.\\n```\\nFile: CurveAdapter.sol\\n    function _exactInSingle(Trade memory trade)\\n        internal view returns (address target, bytes memory executionCallData)\\n    {\\n        address sellToken = _getTokenAddress(trade.sellToken);\\n        address buyToken = _getTokenAddress(trade.buyToken);\\n        ICurvePool pool = ICurvePool(Deployments.CURVE_REGISTRY.find_pool_for_coins(sellToken, buyToken));\\n\\n        if (address(pool) == address(0)) revert InvalidTrade();\\n\\n        int128 i = -1;\\n        int128 j = -1;\\n        for (int128 c = 0; c < MAX_TOKENS; c++) {\\n            address coin = pool.coins(uint256(int256(c)));\\n            if (coin == sellToken) i = c;\\n            if (coin == buyToken) j = c;\\n            if (i > -1 && j > -1) break;\\n        }\\n\\n        if (i == -1 || j == -1) revert InvalidTrade();\\n\\n        return (\\n            address(pool),\\n            abi.encodeWithSelector(\\n                ICurvePool.exchange.selector,\\n                i,\\n                j,\\n                trade.amount,\\n                trade.limit\\n            )\\n        );\\n    }\\n```\\n\\nHowever, it was observed that when multiple pools are available, users can choose the pool to return by defining the `i` parameter of the `find_pool_for_coins` function as shown below.\\n```\\n@view\\n@external\\ndef find_pool_for_coins(_from: address, _to: address, i: uint256 = 0) -> address:\\n    """\\n    @notice Find an available pool for exchanging two coins\\n    @param _from Address of coin to be sent\\n    @param _to Address of coin to be received\\n    @param i Index value. When multiple pools are available\\n            this value is used to return the n'th address.\\n    @return Pool address\\n    """\\n    key: uint256 = bitwise_xor(convert(_from, uint256), convert(_to, uint256))\\n    return self.markets[key][i]\\n```\\n\\nHowever, the `CurveAdapter._exactInSingle` did not allow users to define the `i` parameter of the `find_pool_for_coins` function. As a result, users are forced to trade against the first pool returned by the Curve Registry.

Impact:
The first pool returned by Curve Registry might not be the most optimal pool to trade with. The first pool might have lesser liquidity, larger slippage, and higher fee than the other pools, resulting in the trade returning lesser assets than expected.

Recommended mitigation:
If multiple pools support the exchange, consider allowing the users to choose which pool they want to trade against.\\n```\\nfunction _exactInSingle(Trade memory trade)\\n internal view returns (address target, bytes memory executionCallData)\\n{\\n address sellToken = _getTokenAddress(trade.sellToken);\\n address buyToken = _getTokenAddress(trade.buyToken);\\n// Remove the line below\\n ICurvePool pool = ICurvePool(Deployments.CURVE_REGISTRY.find_pool_for_coins(sellToken, buyToken));\\n// Add the line below\\n ICurvePool pool = ICurvePool(Deployments.CURVE_REGISTRY.find_pool_for_coins(sellToken, buyToken, trade.pool_index)); \\n```\\n