From 820e65aa63b83ef5ca33195017dba037b3fceba6 Mon Sep 17 00:00:00 2001 From: Denis Makarov Date: Thu, 13 Aug 2026 21:55:31 +0400 Subject: [PATCH 1/2] fix(wallet): swallow ConnectorAlreadyConnectedError in widget dapp-mode connect race --- .../WidgetStandaloneMode.updater.test.tsx | 30 +++++++++++++++++-- .../updaters/WidgetStandaloneMode.updater.tsx | 17 +++++++++-- 2 files changed, 42 insertions(+), 5 deletions(-) diff --git a/libs/wallet/src/updaters/WidgetStandaloneMode.updater.test.tsx b/libs/wallet/src/updaters/WidgetStandaloneMode.updater.test.tsx index 18f54dc3a8f..c13ce0cd2d7 100644 --- a/libs/wallet/src/updaters/WidgetStandaloneMode.updater.test.tsx +++ b/libs/wallet/src/updaters/WidgetStandaloneMode.updater.test.tsx @@ -1,6 +1,6 @@ -import { useConnection } from 'wagmi' +import { ConnectorAlreadyConnectedError, useConnection } from 'wagmi' -import { isInjectedWidget } from '@cowprotocol/common-utils' +import { isInjectedWidget, logWallet } from '@cowprotocol/common-utils' import { ConnectorController, OptionsController } from '@reown/appkit-controllers' import { render, RenderResult, waitFor } from '@testing-library/react' @@ -15,10 +15,13 @@ import { useDisconnectWallet } from '../wagmi/hooks/useDisconnectWallet' jest.mock('@cowprotocol/common-utils', () => ({ isInjectedWidget: jest.fn(), + logWallet: { error: jest.fn() }, + normalizeError: (err: unknown) => (err instanceof Error ? err : new Error(String(err))), })) jest.mock('wagmi', () => ({ useConnection: jest.fn(), + ConnectorAlreadyConnectedError: class MockConnectorAlreadyConnectedError extends Error {}, })) jest.mock('../utils/connectWalletById', () => ({ @@ -63,6 +66,7 @@ const wagmiAdapterSyncConnectionsMock = wagmiAdapter.syncConnections as jest.Moc const wagmiAdapterSyncConnectorsMock = wagmiAdapter.syncConnectors as jest.Mock const optionsControllerSetEIP6963EnabledMock = OptionsController.setEIP6963Enabled as jest.Mock const connectorControllerSubscribeMock = ConnectorController.subscribe as jest.Mock +const logWalletErrorMock = logWallet.error as jest.Mock const disconnectMock = jest.fn() @@ -150,6 +154,28 @@ describe('WidgetStandaloneModeUpdater', () => { expect(connectWalletByIdMock).toHaveBeenCalledTimes(1) }) + + it('swallows ConnectorAlreadyConnectedError without logging (auto-reconnect or the bridge beat us to it)', async () => { + connectWalletByIdMock.mockRejectedValue(new ConnectorAlreadyConnectedError()) + + renderUpdater(DAPP_MODE) + + await waitFor(() => { + expect(connectWalletByIdMock).toHaveBeenCalledTimes(1) + }) + + expect(logWalletErrorMock).not.toHaveBeenCalled() + }) + + it('logs unexpected connect errors', async () => { + connectWalletByIdMock.mockRejectedValue(new Error('provider unavailable')) + + renderUpdater(DAPP_MODE) + + await waitFor(() => { + expect(logWalletErrorMock).toHaveBeenCalledTimes(1) + }) + }) }) describe('standalone mode: injected wallet discovery', () => { diff --git a/libs/wallet/src/updaters/WidgetStandaloneMode.updater.tsx b/libs/wallet/src/updaters/WidgetStandaloneMode.updater.tsx index ac744267f4d..fb87b83ecc6 100644 --- a/libs/wallet/src/updaters/WidgetStandaloneMode.updater.tsx +++ b/libs/wallet/src/updaters/WidgetStandaloneMode.updater.tsx @@ -1,9 +1,9 @@ import { useSetAtom } from 'jotai' import { useEffect, useRef } from 'react' -import { useConnection } from 'wagmi' +import { ConnectorAlreadyConnectedError, useConnection } from 'wagmi' -import { isInjectedWidget } from '@cowprotocol/common-utils' +import { isInjectedWidget, logWallet, normalizeError } from '@cowprotocol/common-utils' import { ConnectorController, OptionsController } from '@reown/appkit-controllers' @@ -71,7 +71,18 @@ export function WidgetStandaloneModeUpdater({ standaloneMode }: WidgetStandalone console.debug('[WidgetStandaloneModeUpdater] connect widget connector') await reownAppKit.disconnect() - connectWalletById(COW_WIDGET_CONNECTOR_ID, 'injected') + + try { + await connectWalletById(COW_WIDGET_CONNECTOR_ID, 'injected') + } catch (err: unknown) { + const error = normalizeError(err) + + // Auto-reconnect or the bridged provider's own connect event can beat us to it - + // wagmi is already connected to this connector, nothing left to do. + if (error instanceof ConnectorAlreadyConnectedError) return + + logWallet.error(new Error('Failed to connect widget connector', { cause: error })) + } })() } }, [isDappMode, isSafeApp]) From d8fcc3403be3d6a98333f414e746b4f1b981b4ad Mon Sep 17 00:00:00 2001 From: "cowswap-release-sync[bot]" <274575433+cowswap-release-sync[bot]@users.noreply.github.com> Date: Fri, 14 Aug 2026 13:30:38 +0000 Subject: [PATCH 2/2] chore(i18n): extract i18n strings [automatic] --- apps/cowswap-frontend/src/locales/en-US.po | 3 --- 1 file changed, 3 deletions(-) diff --git a/apps/cowswap-frontend/src/locales/en-US.po b/apps/cowswap-frontend/src/locales/en-US.po index 9d35edccb8e..447c94978d2 100644 --- a/apps/cowswap-frontend/src/locales/en-US.po +++ b/apps/cowswap-frontend/src/locales/en-US.po @@ -686,7 +686,6 @@ msgid "Sell amount" msgstr "Sell amount" #: apps/cowswap-frontend/src/modules/ordersTable/pure/OrderEstimatedExecutionPrice/OrderEstimatedExecutionPrice.pure.tsx -#: apps/cowswap-frontend/src/modules/ordersTable/pure/OrdersTable/Row/WarningEstimatedPrice/OrderRowWarningEstimatedPrice.pure.tsx #: apps/cowswap-frontend/src/modules/ordersTable/pure/OrderStatusBox/getOrderStatusTitleAndColor.ts #: apps/cowswap-frontend/src/modules/ordersTable/state/params/ordersTableParams.atom.ts msgid "Unfillable" @@ -4400,7 +4399,6 @@ msgid "Limit Order" msgstr "Limit Order" #: apps/cowswap-frontend/src/modules/ordersTable/pure/OrderEstimatedExecutionPrice/OrderEstimatedExecutionPrice.pure.tsx -#: apps/cowswap-frontend/src/modules/ordersTable/pure/OrdersTable/Row/WarningEstimatedPrice/OrderRowWarningEstimatedPrice.pure.tsx msgid "Insufficient allowance" msgstr "Insufficient allowance" @@ -8367,7 +8365,6 @@ msgid "View on Bridge Explorer ↗" msgstr "View on Bridge Explorer ↗" #: apps/cowswap-frontend/src/modules/ordersTable/pure/OrderEstimatedExecutionPrice/OrderEstimatedExecutionPrice.pure.tsx -#: apps/cowswap-frontend/src/modules/ordersTable/pure/OrdersTable/Row/WarningEstimatedPrice/OrderRowWarningEstimatedPrice.pure.tsx msgid "Insufficient balance" msgstr "Insufficient balance"