diff --git a/__tests__/components/auth/SeizeConnectContext.addAccount.test.tsx b/__tests__/components/auth/SeizeConnectContext.addAccount.test.tsx
new file mode 100644
index 0000000000..63b986f3f2
--- /dev/null
+++ b/__tests__/components/auth/SeizeConnectContext.addAccount.test.tsx
@@ -0,0 +1,255 @@
+import React from "react";
+import { act, fireEvent, render, screen } from "@testing-library/react";
+import {
+ SeizeConnectProvider,
+ useSeizeConnectContext,
+} from "@/components/auth/SeizeConnectContext";
+import { APP_WALLET_CONNECTOR_TYPE } from "@/wagmiConfig/wagmiAppWalletConnector";
+
+const ACTIVE_ADDRESS = "0x00000000000000000000000000000000000000AA";
+
+const mockOpen = jest.fn();
+const mockDisconnect = jest.fn();
+const mockLogError = jest.fn();
+const mockLogSecurityEvent = jest.fn();
+
+let mockAppKitAccount: {
+ address?: string;
+ isConnected: boolean;
+ status: "connected" | "connecting" | "reconnecting" | "disconnected";
+};
+
+let mockWagmiAccount: {
+ connector?: {
+ type?: string;
+ };
+};
+
+let mockAppKitState: { open: boolean };
+
+jest.mock("@reown/appkit/react", () => ({
+ useAppKit: () => ({
+ open: mockOpen,
+ }),
+ useAppKitAccount: () => mockAppKitAccount,
+ useAppKitState: () => mockAppKitState,
+ useDisconnect: () => ({
+ disconnect: mockDisconnect,
+ }),
+ useWalletInfo: () => ({
+ walletInfo: undefined,
+ }),
+}));
+
+jest.mock("wagmi", () => ({
+ useAccount: () => mockWagmiAccount,
+}));
+
+jest.mock("@/config/env", () => ({
+ getNodeEnv: () => "test",
+ publicEnv: {
+ USE_DEV_AUTH: "false",
+ DEV_MODE_WALLET_ADDRESS: undefined,
+ },
+}));
+
+jest.mock("@/hooks/useConnectedAccountsUnreadNotifications", () => ({
+ useConnectedAccountsUnreadNotifications: () => ({}),
+}));
+
+jest.mock("@/services/auth/auth.utils", () => ({
+ WALLET_ACCOUNTS_UPDATED_EVENT: "6529-wallet-accounts-updated",
+ canStoreAnotherWalletAccount: jest.fn(() => true),
+ getConnectedWalletAccounts: jest.fn(() => [
+ {
+ address: ACTIVE_ADDRESS,
+ refreshToken: "refresh-token",
+ role: null,
+ jwt: null,
+ profileId: null,
+ profileHandle: null,
+ },
+ ]),
+ getWalletAddress: jest.fn(() => ACTIVE_ADDRESS),
+ removeAuthJwt: jest.fn(),
+ setActiveWalletAccount: jest.fn(() => true),
+}));
+
+jest.mock("@/src/utils/security-logger", () => ({
+ createConnectionEventContext: jest.fn(() => ({})),
+ createValidationEventContext: jest.fn(() => ({})),
+ logError: (...args: unknown[]) => mockLogError(...args),
+ logSecurityEvent: (...args: unknown[]) => mockLogSecurityEvent(...args),
+}));
+
+jest.mock("@/utils/wallet-detection", () => ({
+ isSafeWalletInfo: () => false,
+}));
+
+jest.mock("@/components/auth/error-boundary", () => ({
+ WalletErrorBoundary: ({
+ children,
+ }: {
+ readonly children: React.ReactNode;
+ }) => <>{children}>,
+}));
+
+function AddAccountButton() {
+ const { seizeAddConnectedAccount } = useSeizeConnectContext();
+
+ return ;
+}
+
+describe("SeizeConnectProvider add-account flow", () => {
+ beforeEach(() => {
+ jest.useFakeTimers();
+ jest.clearAllMocks();
+
+ mockAppKitAccount = {
+ address: ACTIVE_ADDRESS,
+ isConnected: true,
+ status: "connected",
+ };
+ mockWagmiAccount = {
+ connector: {
+ type: "injected",
+ },
+ };
+ mockAppKitState = { open: false };
+ mockDisconnect.mockResolvedValue(undefined);
+ });
+
+ afterEach(() => {
+ jest.runOnlyPendingTimers();
+ jest.useRealTimers();
+ });
+
+ it("opens the connect flow directly for app-wallet connectors and keeps add-account mode active", () => {
+ mockWagmiAccount = {
+ connector: {
+ type: APP_WALLET_CONNECTOR_TYPE,
+ },
+ };
+ mockAppKitState = { open: true };
+
+ render(
+
+
+
+ );
+
+ const addButton = screen.getByRole("button", { name: "Add account" });
+
+ act(() => {
+ fireEvent.click(addButton);
+ });
+
+ expect(mockDisconnect).not.toHaveBeenCalled();
+ expect(mockOpen).toHaveBeenCalledTimes(1);
+ expect(mockOpen).toHaveBeenLastCalledWith({ view: "Connect" });
+
+ act(() => {
+ fireEvent.click(addButton);
+ });
+
+ expect(mockDisconnect).not.toHaveBeenCalled();
+ expect(mockOpen).toHaveBeenCalledTimes(1);
+ });
+
+ it("clears a stale add-flow guard before reopening connect for app-wallet connectors", () => {
+ mockDisconnect.mockImplementation(() => new Promise(() => undefined));
+
+ const { rerender } = render(
+
+
+
+ );
+
+ act(() => {
+ fireEvent.click(screen.getByRole("button", { name: "Add account" }));
+ });
+
+ expect(mockDisconnect).toHaveBeenCalledTimes(1);
+ expect(mockOpen).not.toHaveBeenCalled();
+
+ mockWagmiAccount = {
+ connector: {
+ type: APP_WALLET_CONNECTOR_TYPE,
+ },
+ };
+ mockAppKitAccount = {
+ address: undefined,
+ isConnected: false,
+ status: "disconnected",
+ };
+ mockAppKitState = { open: false };
+
+ rerender(
+
+
+
+ );
+
+ act(() => {
+ fireEvent.click(screen.getByRole("button", { name: "Add account" }));
+ });
+
+ expect(mockDisconnect).toHaveBeenCalledTimes(1);
+ expect(mockOpen).toHaveBeenCalledTimes(1);
+ expect(mockOpen).toHaveBeenLastCalledWith({ view: "Connect" });
+ });
+
+ it("keeps the disconnect-then-connect flow for browser-wallet connectors", async () => {
+ render(
+
+
+
+ );
+
+ act(() => {
+ fireEvent.click(screen.getByRole("button", { name: "Add account" }));
+ });
+
+ expect(mockDisconnect).toHaveBeenCalledTimes(1);
+ expect(mockOpen).not.toHaveBeenCalled();
+
+ await act(async () => {
+ await Promise.resolve();
+ });
+
+ act(() => {
+ jest.advanceTimersByTime(100);
+ });
+
+ expect(mockOpen).toHaveBeenCalledTimes(1);
+ expect(mockOpen).toHaveBeenLastCalledWith({ view: "Connect" });
+ });
+
+ it("opens connect directly when there is no live connected wallet and keeps add-account mode active", () => {
+ mockAppKitAccount = {
+ address: undefined,
+ isConnected: false,
+ status: "disconnected",
+ };
+ mockAppKitState = { open: true };
+
+ render(
+
+
+
+ );
+
+ const addButton = screen.getByRole("button", { name: "Add account" });
+
+ act(() => {
+ fireEvent.click(addButton);
+ });
+
+ act(() => {
+ fireEvent.click(addButton);
+ });
+
+ expect(mockDisconnect).not.toHaveBeenCalled();
+ expect(mockOpen).toHaveBeenCalledTimes(1);
+ });
+});
diff --git a/components/auth/SeizeConnectContext.tsx b/components/auth/SeizeConnectContext.tsx
index 6fb45c0f34..86bbb64915 100644
--- a/components/auth/SeizeConnectContext.tsx
+++ b/components/auth/SeizeConnectContext.tsx
@@ -17,6 +17,7 @@ import React, {
useState,
} from "react";
import { getAddress, isAddress } from "viem";
+import { useAccount } from "wagmi";
import { getNodeEnv, publicEnv } from "@/config/env";
import { MAX_CONNECTED_PROFILES } from "@/constants/constants";
import {
@@ -38,6 +39,7 @@ import {
logSecurityEvent,
} from "@/src/utils/security-logger";
import { isSafeWalletInfo } from "@/utils/wallet-detection";
+import { APP_WALLET_CONNECTOR_TYPE } from "@/wagmiConfig/wagmiAppWalletConnector";
import { WalletErrorBoundary } from "./error-boundary";
// Custom error types for better error handling
@@ -389,6 +391,7 @@ export const SeizeConnectProvider: React.FC<{ children: React.ReactNode }> = ({
children,
}) => {
const account = useAppKitAccount();
+ const wagmiAccount = useAccount();
const { walletInfo } = useWalletInfo();
const { disconnect } = useDisconnect();
const { open } = useAppKit();
@@ -982,15 +985,11 @@ export const SeizeConnectProvider: React.FC<{ children: React.ReactNode }> = ({
return storedConnectedAccounts.length < MAX_CONNECTED_PROFILES;
}, [storedConnectedAccounts]);
- const seizeAddConnectedAccount = useCallback((): void => {
- if (!canAddConnectedAccount || !canStoreAnotherWalletAccount()) {
- return;
- }
-
- if (isAddingConnectedAccountRef.current) {
- return;
- }
+ const activeConnectorType = wagmiAccount.connector?.type;
+ const isActiveAppWalletConnector =
+ activeConnectorType === APP_WALLET_CONNECTOR_TYPE;
+ const seizeAddConnectedAccount = useCallback((): void => {
const clearAddConnectedAccountGuard = (): void => {
isAddingConnectedAccountRef.current = false;
addFlowOriginAddressRef.current = null;
@@ -1000,86 +999,122 @@ export const SeizeConnectProvider: React.FC<{ children: React.ReactNode }> = ({
}
};
- isAddingConnectedAccountRef.current = true;
+ if (!canAddConnectedAccount || !canStoreAnotherWalletAccount()) {
+ return;
+ }
const liveConnectedWallet =
account.address && account.isConnected && isAddress(account.address)
? getAddress(account.address)
: null;
+ const addFlowOriginAddress = addFlowOriginAddressRef.current;
+ const addFlowReturnedToOrigin =
+ !state.open &&
+ !!liveConnectedWallet &&
+ !!addFlowOriginAddress &&
+ normalizeAddress(liveConnectedWallet) ===
+ normalizeAddress(addFlowOriginAddress);
+ const hasStaleAddConnectedAccountGuard =
+ isAddingConnectedAccountRef.current &&
+ (!isAddingConnectedAccount ||
+ addFlowReturnedToOrigin ||
+ (!state.open &&
+ !retryConnectTimeoutRef.current &&
+ !liveConnectedWallet &&
+ account.status !== "connecting" &&
+ account.status !== "reconnecting"));
+
+ if (hasStaleAddConnectedAccountGuard) {
+ clearAddConnectedAccountGuard();
+ setIsAddingConnectedAccount(false);
+ }
- addFlowOriginAddressRef.current = liveConnectedWallet;
- setIsAddingConnectedAccount(true);
+ if (isAddingConnectedAccountRef.current) {
+ return;
+ }
- if (liveConnectedWallet) {
- if (retryConnectTimeoutRef.current) {
- clearTimeout(retryConnectTimeoutRef.current);
- retryConnectTimeoutRef.current = null;
- }
+ if (!liveConnectedWallet || isActiveAppWalletConnector) {
+ isAddingConnectedAccountRef.current = true;
+ addFlowOriginAddressRef.current = liveConnectedWallet;
+ setIsAddingConnectedAccount(true);
try {
- disconnect()
- .then(() => {
- retryConnectTimeoutRef.current = setTimeout(() => {
- retryConnectTimeoutRef.current = null;
- if (!isMountedRef.current) {
- clearAddConnectedAccountGuard();
- return;
- }
- try {
- seizeConnect();
- } catch (error: unknown) {
- clearAddConnectedAccountGuard();
- setIsAddingConnectedAccount(false);
- const connectionError = createWalletError(
- WalletConnectionError,
- "start add-account connection flow",
- error
- );
- logError("seizeAddConnectedAccount", connectionError);
- }
- }, 100);
- })
- .catch((error: unknown) => {
- clearAddConnectedAccountGuard();
- setIsAddingConnectedAccount(false);
- const walletError = createWalletError(
- WalletDisconnectionError,
- "disconnect wallet before adding account",
- error
- );
- logError("seizeAddConnectedAccount", walletError);
- });
+ seizeConnect();
} catch (error: unknown) {
clearAddConnectedAccountGuard();
setIsAddingConnectedAccount(false);
- const walletError = createWalletError(
- WalletDisconnectionError,
- "disconnect wallet before adding account",
+ const connectionError = createWalletError(
+ WalletConnectionError,
+ "start add-account connection flow",
error
);
- logError("seizeAddConnectedAccount", walletError);
+ logError("seizeAddConnectedAccount", connectionError);
}
return;
}
+ isAddingConnectedAccountRef.current = true;
+ addFlowOriginAddressRef.current = liveConnectedWallet;
+ setIsAddingConnectedAccount(true);
+
+ if (retryConnectTimeoutRef.current) {
+ clearTimeout(retryConnectTimeoutRef.current);
+ retryConnectTimeoutRef.current = null;
+ }
+
try {
- seizeConnect();
+ disconnect()
+ .then(() => {
+ retryConnectTimeoutRef.current = setTimeout(() => {
+ retryConnectTimeoutRef.current = null;
+ if (!isMountedRef.current) {
+ clearAddConnectedAccountGuard();
+ return;
+ }
+ try {
+ seizeConnect();
+ } catch (error: unknown) {
+ clearAddConnectedAccountGuard();
+ setIsAddingConnectedAccount(false);
+ const connectionError = createWalletError(
+ WalletConnectionError,
+ "start add-account connection flow",
+ error
+ );
+ logError("seizeAddConnectedAccount", connectionError);
+ }
+ }, 100);
+ })
+ .catch((error: unknown) => {
+ clearAddConnectedAccountGuard();
+ setIsAddingConnectedAccount(false);
+ const walletError = createWalletError(
+ WalletDisconnectionError,
+ "disconnect wallet before adding account",
+ error
+ );
+ logError("seizeAddConnectedAccount", walletError);
+ });
} catch (error: unknown) {
clearAddConnectedAccountGuard();
setIsAddingConnectedAccount(false);
- const connectionError = createWalletError(
- WalletConnectionError,
- "start add-account connection flow",
+ const walletError = createWalletError(
+ WalletDisconnectionError,
+ "disconnect wallet before adding account",
error
);
- logError("seizeAddConnectedAccount", connectionError);
+ logError("seizeAddConnectedAccount", walletError);
}
}, [
account.address,
account.isConnected,
+ account.status,
canAddConnectedAccount,
disconnect,
+ isActiveAppWalletConnector,
+ isAddingConnectedAccount,
seizeConnect,
+ state.open,
]);
const connectedAccounts = useMemo(() => {