> ## Documentation Index
> Fetch the complete documentation index at: https://docs.dynamic.xyz/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Wallet authentication

> Migrate external wallet discovery, connection, and verification from the React SDK to the JavaScript SDK.

Connecting an external wallet becomes an explicit choice between *connecting* and *connecting + verifying*, and the multi-wallet prompts the React SDK rendered are now yours to build. This page maps the APIs and links to the implementation guides.

## Wallet discovery

The catalogue of available wallet providers moves from `useWalletOptions` to a single function.

| React SDK | JavaScript SDK |
| - | - |
| `useWalletOptions()` | `getWalletOptionsCatalogue()` / `useGetWalletOptionsCatalogue()` |

Each entry carries the provider `key`, display name, icon, and `installationUrls` so you can render install links for wallets the user doesn't have. For the full wallet picker (search, install links, deeplinks), see [Connecting external wallets](https://docs.dynamic.xyz/docs/javascript/building-ui/connecting-external-wallets.md).

## Connect vs. connect-and-verify

The React SDK decided when to prompt for a signature. Now you choose explicitly.

The recommended default is `connectWalletOption({ walletKey })`, which takes a wallet options catalogue key — the closest equivalent of connecting through the React SDK's `useWalletOptions` flow. It picks the connection path the wallet supports on the user's device (installed provider, URI pairing, or the wallet's in-app browser) and returns an unverified `WalletAccount`, which you pass to `verifyWalletAccount` to link it to the user. See [Connect a wallet by catalogue key](https://docs.dynamic.xyz/docs/javascript/reference/wallets/connect-wallet-option.md) for the full reference.

The provider-key functions are the lower-level path. They drive an already-registered wallet provider only — installed extensions and redirect providers such as Phantom on mobile — and cannot reach URI-pairing or in-app-browser wallets:

* `connectWithWalletProvider({ walletProviderKey })` — connect only (read the address, no signature).
* `connectAndVerifyWithWalletProvider({ walletProviderKey })` — connect and prompt for a sign-in signature, proving ownership and linking the wallet to the user.

| React SDK | JavaScript SDK |
| - | - |
| `useWalletOptions()` + connect flow | `connectWalletOption()` / `useConnectWalletOption()` + `verifyWalletAccount()` |
| `primaryWallet.connector.connect()` | `connectWithWalletProvider()` / `useConnectWithWalletProvider()` (provider-key path) |
| authenticate connected wallet | `connectAndVerifyWithWalletProvider()` / `useConnectAndVerifyWithWalletProvider()` |
| `isAuthenticatedWithAWallet` | `isWalletAccountVerified({ walletAccount })` |

> [tab] TypeScript
>
> ```typescript theme={"system"}
> import {
>   connectWalletOption,
>   isMobile,
>   redirectToInAppBrowser,
>   verifyWalletAccount,
> } from '@dynamic-labs-sdk/client';
>
> const walletAccount = await connectWalletOption({
>   handleWalletBrowserRedirect: ({ chain, walletKey }) =>
>     redirectToInAppBrowser({
>       appUrl: window.location.href,
>       chain,
>       walletKey,
>     }),
>   onConnectionUri: ({ uri }) => {
>     if (isMobile()) {
>       window.location.href = uri;
>       return;
>     }
>
>     renderQrCode(uri);
>   },
>   walletKey,
> });
>
> const verifiedAccount = await verifyWalletAccount({ walletAccount });
> ```

> [tab] React
>
> ```tsx theme={"system"}
> import { isMobile } from '@dynamic-labs-sdk/client';
> import {
>   useConnectWalletOption,
>   useVerifyWalletAccount,
> } from '@dynamic-labs-sdk/react-hooks';
>
> const { mutateAsync: connect } = useConnectWalletOption();
> const { mutateAsync: verify } = useVerifyWalletAccount();
>
> const walletAccount = await connect({
>   onConnectionUri: ({ uri }) => {
>     if (isMobile()) {
>       window.location.href = uri;
>       return;
>     }
>
>     renderQrCode(uri);
>   },
>   walletKey,
> });
>
> await verify({ walletAccount });
> ```

## Hardware wallets

The dedicated hardware-wallet selection view is gone; check support and account type with functions.

| React SDK | JavaScript SDK |
| - | - |
| hardware wallet selection view | `canConnectWithHardwareWallet()` |
| — | `isHardwareWalletAccount({ walletAccount })` |

## Multi-wallet

Automatic multi-wallet prompts (a new wallet detected, switching the active wallet) are gone, and the SDK doesn't track a selected wallet for you. Read the full set with `getWalletAccounts()` and build your own selection UI — see [Connected wallets management](https://docs.dynamic.xyz/docs/javascript/building-ui/connected-wallets-management.md).

## Errors you now own

The React SDK rendered built-in screens for these cases; in the JavaScript SDK you catch the typed error and render your own UI.

| Case | JavaScript SDK | Build it in |
| - | - | - |
| Wallet already linked to another user | catch `WalletAlreadyLinkedToAnotherUserError` | [Account conflict resolution](https://docs.dynamic.xyz/docs/javascript/building-ui/account-conflict-resolution.md) |
| Sanctioned / screened wallet blocked | catch `WalletScreeningBlockedError` | [Handling blocked wallets](https://docs.dynamic.xyz/docs/javascript/building-ui/blocked-wallet-screening.md) |

```typescript theme={"system"}
import {
  connectAndVerifyWithWalletProvider,
  WalletAlreadyLinkedToAnotherUserError,
  WalletScreeningBlockedError,
} from '@dynamic-labs-sdk/client';

try {
  await connectAndVerifyWithWalletProvider({ walletProviderKey });
} catch (error) {
  if (error instanceof WalletScreeningBlockedError) {
    // showAccessDenied is your implementation.
    showAccessDenied();
  } else if (error instanceof WalletAlreadyLinkedToAnotherUserError) {
    // showWalletAlreadyLinked is your implementation.
    showWalletAlreadyLinked();
  } else {
    throw error;
  }
}
```

> [!NOTE]
>
> Wallet screening (Chainalysis sanctions checks) still runs server-side during verification and is configured per environment in the console — nothing changes there. What changes is that you handle the blocked result and render the UI. The token-gating (`gate_blocked`) and allowlist (`missing_from_list`) cases are still surfaced via their raw API error codes.

## See also

* [Connect a wallet by catalogue key](https://docs.dynamic.xyz/docs/javascript/reference/wallets/connect-wallet-option.md)
* [Connecting external wallets](https://docs.dynamic.xyz/docs/javascript/building-ui/connecting-external-wallets.md)
* [Connected wallets management](https://docs.dynamic.xyz/docs/javascript/building-ui/connected-wallets-management.md)
* [Account conflict resolution](https://docs.dynamic.xyz/docs/javascript/building-ui/account-conflict-resolution.md)
* [Handling blocked wallets](https://docs.dynamic.xyz/docs/javascript/building-ui/blocked-wallet-screening.md)
* [Post-login user setup](https://docs.dynamic.xyz/docs/javascript/migrating-from-react/post-login-user-setup.md)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.