> ## 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.

# Authenticate with WalletConnect

> Connect mobile wallets to your dapp - add extension, get wallet list, connect (QR or deep link), handle prompts

**Prerequisites:** Create and initialize a Dynamic client and add the extension(s) for your chain(s): [EVM](/javascript/reference/evm/adding-evm-extensions) and/or Solana. The SDK supports WalletConnect for **EVM** and **Solana** only.

**What is WalletConnect?** Users connect a wallet from another device (e.g. mobile) by scanning a QR code or opening a deep link. Use it for cross-device or mobile-to-web flows. You build the UI yourself; there is no built-in picker.

***

## 1. Add the extension(s)

Add the WalletConnect extension for each chain you support, once at app setup. Client is optional when using a single Dynamic client.

```typescript theme={"system"}
import {
  createDynamicClient,
  initializeClient,
} from '@dynamic-labs-sdk/client';
import { addWalletConnectEvmExtension } from '@dynamic-labs-sdk/evm/wallet-connect';
import { addWalletConnectSolanaExtension } from '@dynamic-labs-sdk/solana/wallet-connect';

const client = createDynamicClient({ environmentId: 'your-environment-id' });
await initializeClient();

await addWalletConnectEvmExtension(client);
await addWalletConnectSolanaExtension(client); // omit if you only use EVM
```

***

## 2. Get the wallet list

Use `getWalletConnectCatalog()` for names, icons, and deep links (e.g. for a picker or "Open in MetaMask"). To look up one wallet by provider key after a wallet is connected, see [When the user must act](#4-when-the-user-must-act-in-their-wallet).

```typescript theme={"system"}
import { getWalletConnectCatalog } from '@dynamic-labs-sdk/client';

const catalog = await getWalletConnectCatalog();
const wallet = catalog.wallets['metamask'];
const deepLink = wallet?.deeplinks?.native ?? wallet?.deeplinks?.universal;
```

**Wallet entry:** `name`, `chain` (EVM/SOL/BTC), `spriteUrl`, `primaryColor`, `deeplinks.native`, `deeplinks.universal`, `downloadLinks.androidUrl`, `downloadLinks.iosUrl`.

***

## 3. Connect a wallet

Every connect flow returns `{ uri, approval }`. You show the **URI** as a QR code (desktop) or pass it into a deep link (mobile via `appendWalletConnectUriToDeepLink`); the user approves in their wallet app; then `approval()` resolves with `{ walletAccounts }`.

| Chain      | Connect only                     | Connect and verify                                                                               |
| ---------- | -------------------------------- | ------------------------------------------------------------------------------------------------ |
| **EVM**    | `connectWithWalletConnectEvm`    | `connectAndVerifyWithWalletConnectEvm` (one step)                                                |
| **Solana** | `connectWithWalletConnectSolana` | `connectAndVerifyWithWalletConnectSolana` (connect then verify; no single-step auth for non-EVM) |

**Connect:**

<Tabs>
  <Tab title="EVM">
    ```typescript theme={"system"}
    import {
      waitForClientInitialized,
      isMobile,
      getWalletConnectCatalog,
    } from '@dynamic-labs-sdk/client';
    import { connectWithWalletConnectEvm } from '@dynamic-labs-sdk/evm/wallet-connect';
    import { appendWalletConnectUriToDeepLink } from '@dynamic-labs-sdk/wallet-connect';
    import QRCode from 'qrcode';

    async function connect() {
      await waitForClientInitialized();
      const { uri, approval } = await connectWithWalletConnectEvm({
        addToDynamicWalletAccounts: true,
      });

      if (isMobile()) {
        const catalog = await getWalletConnectCatalog();
        const wallet = Object.values(catalog.wallets).find(w => w.chain === 'EVM');
        const base = wallet?.deeplinks?.native ?? wallet?.deeplinks?.universal;
        if (base) {
          window.location.href = appendWalletConnectUriToDeepLink({
            deepLinkUrl: base,
            walletConnectUri: uri,
          });
        }
      } else {
        document.getElementById('qrcode').src = await QRCode.toDataURL(uri);
      }

      const { walletAccounts } = await approval();
      return walletAccounts;
    }
    ```
  </Tab>

  <Tab title="Solana">
    ```typescript theme={"system"}
    import {
      waitForClientInitialized,
      isMobile,
      getWalletConnectCatalog,
    } from '@dynamic-labs-sdk/client';
    import { connectWithWalletConnectSolana } from '@dynamic-labs-sdk/solana/wallet-connect';
    import { appendWalletConnectUriToDeepLink } from '@dynamic-labs-sdk/wallet-connect';
    import QRCode from 'qrcode';

    async function connect() {
      await waitForClientInitialized();
      const { uri, approval } = await connectWithWalletConnectSolana({
        addToDynamicWalletAccounts: true,
      });

      if (isMobile()) {
        const catalog = await getWalletConnectCatalog();
        const wallet = Object.values(catalog.wallets).find(w => w.chain === 'SOL');
        const base = wallet?.deeplinks?.native ?? wallet?.deeplinks?.universal;
        if (base) {
          window.location.href = appendWalletConnectUriToDeepLink({
            deepLinkUrl: base,
            walletConnectUri: uri,
          });
        }
      } else {
        document.getElementById('qrcode').src = await QRCode.toDataURL(uri);
      }

      const { walletAccounts } = await approval();
      return walletAccounts;
    }
    ```
  </Tab>
</Tabs>

**Connect and verify:**

For connect and verify, call `connectAndVerifyWithWalletConnectEvm()` or `connectAndVerifyWithWalletConnectSolana()` instead; same `uri` / `approval` pattern.

<Note>
  Solana has no single-step WalletConnect authenticate method (only EIP-155 chains support it), so `connectAndVerifyWithWalletConnectSolana` performs connect then verify in sequence internally.
</Note>

***

## 4. When the user must act in their wallet

After a request (e.g. sign, switch network), the user approves in their wallet app. Listen for `walletConnectUserActionRequested` and either open the wallet app (mobile) or show a prompt (desktop). Use `getWalletConnectCatalogWalletByWalletProviderKey({ walletProviderKey })` to get the wallet's deep link when you have a connected wallet or the event's `walletProviderKey`.

```typescript theme={"system"}
import {
  onEvent,
  isMobile,
  getWalletConnectCatalogWalletByWalletProviderKey,
} from '@dynamic-labs-sdk/client';

onEvent({
  event: 'walletConnectUserActionRequested',
  listener: async ({ walletProviderKey }) => {
    if (isMobile()) {
      const wallet = await getWalletConnectCatalogWalletByWalletProviderKey({
        walletProviderKey,
      });
      const deepLink = wallet?.deeplinks?.native ?? wallet?.deeplinks?.universal;
      if (deepLink) window.open(deepLink, '_blank');
    } else {
      // e.g. toast.info('Please approve the action in your wallet application');
    }
  },
});
```

***

## 5. Account and network changes

Use [wallet provider events](/javascript/reference/wallets/wallet-provider-events) with `onWalletProviderEvent`:

```typescript theme={"system"}
import { onWalletProviderEvent } from '@dynamic-labs-sdk/client';

onWalletProviderEvent({
  walletProviderKey: walletAccounts[0].walletProviderKey,
  event: 'accountsChanged',
  callback: ({ addresses }) => console.log('Accounts:', addresses),
});
```

***

## 6. Full flow example

End-to-end: setup, connect (QR on desktop, deep link on mobile), handle user actions, listen to events.

<Tabs>
  <Tab title="EVM">
    ```typescript theme={"system"}
    import {
      createDynamicClient,
      initializeClient,
      waitForClientInitialized,
      onEvent,
      isMobile,
      getWalletConnectCatalog,
      getWalletConnectCatalogWalletByWalletProviderKey,
      onWalletProviderEvent,
    } from '@dynamic-labs-sdk/client';
    import {
      addWalletConnectEvmExtension,
      connectWithWalletConnectEvm,
    } from '@dynamic-labs-sdk/evm/wallet-connect';
    import { appendWalletConnectUriToDeepLink } from '@dynamic-labs-sdk/wallet-connect';
    import QRCode from 'qrcode';

    const client = createDynamicClient({ environmentId: 'your-environment-id' });
    await initializeClient();
    await addWalletConnectEvmExtension(client);

    onEvent({
      event: 'walletConnectUserActionRequested',
      listener: async ({ walletProviderKey }) => {
        if (isMobile()) {
          const wallet = await getWalletConnectCatalogWalletByWalletProviderKey({
            walletProviderKey,
          });
          const deepLink = wallet?.deeplinks?.native ?? wallet?.deeplinks?.universal;
          if (deepLink) window.open(deepLink, '_blank');
        } else {
          console.log('Please approve the action in your wallet application');
        }
      },
    });

    async function connectWallet() {
      await waitForClientInitialized();
      const catalog = await getWalletConnectCatalog();
      const { uri, approval } = await connectWithWalletConnectEvm();

      if (isMobile()) {
        const w = Object.values(catalog.wallets).find((x) => x.name === 'MetaMask');
        const base = w?.deeplinks?.native ?? w?.deeplinks?.universal;
        if (base) {
          window.location.href = appendWalletConnectUriToDeepLink({
            deepLinkUrl: base,
            walletConnectUri: uri,
          });
        }
      } else {
        document.getElementById('qrcode').src = await QRCode.toDataURL(uri);
      }

      const { walletAccounts } = await approval();
      onWalletProviderEvent({
        walletProviderKey: walletAccounts[0].walletProviderKey,
        event: 'accountsChanged',
        callback: ({ addresses }) => console.log('Accounts:', addresses),
      });
    }
    ```
  </Tab>

  <Tab title="Solana">
    ```typescript theme={"system"}
    import {
      createDynamicClient,
      initializeClient,
      waitForClientInitialized,
      onEvent,
      isMobile,
      getWalletConnectCatalog,
      getWalletConnectCatalogWalletByWalletProviderKey,
      onWalletProviderEvent,
    } from '@dynamic-labs-sdk/client';
    import {
      addWalletConnectSolanaExtension,
      connectWithWalletConnectSolana,
    } from '@dynamic-labs-sdk/solana/wallet-connect';
    import { appendWalletConnectUriToDeepLink } from '@dynamic-labs-sdk/wallet-connect';
    import QRCode from 'qrcode';

    const client = createDynamicClient({ environmentId: 'your-environment-id' });
    await initializeClient();
    await addWalletConnectSolanaExtension(client);

    onEvent({
      event: 'walletConnectUserActionRequested',
      listener: async ({ walletProviderKey }) => {
        if (isMobile()) {
          const wallet = await getWalletConnectCatalogWalletByWalletProviderKey({
            walletProviderKey,
          });
          const deepLink = wallet?.deeplinks?.native ?? wallet?.deeplinks?.universal;
          if (deepLink) window.open(deepLink, '_blank');
        } else {
          console.log('Please approve the action in your wallet application');
        }
      },
    });

    async function connectWallet() {
      await waitForClientInitialized();
      const catalog = await getWalletConnectCatalog();
      const { uri, approval } = await connectWithWalletConnectSolana();

      if (isMobile()) {
        // Find a Solana wallet (e.g., Phantom)
        const w = Object.values(catalog.wallets).find((x) => x.chain === 'SOL');
        const base = w?.deeplinks?.native ?? w?.deeplinks?.universal;
        if (base) {
          window.location.href = appendWalletConnectUriToDeepLink({
            deepLinkUrl: base,
            walletConnectUri: uri,
          });
        }
      } else {
        document.getElementById('qrcode').src = await QRCode.toDataURL(uri);
      }

      const { walletAccounts } = await approval();
      onWalletProviderEvent({
        walletProviderKey: walletAccounts[0].walletProviderKey,
        event: 'accountsChanged',
        callback: ({ addresses }) => console.log('Accounts:', addresses),
      });
    }
    ```
  </Tab>

  <Tab title="Multi-chain">
    ```typescript theme={"system"}
    import {
      createDynamicClient,
      initializeClient,
      waitForClientInitialized,
      onEvent,
      isMobile,
      getWalletConnectCatalog,
      getWalletConnectCatalogWalletByWalletProviderKey,
      onWalletProviderEvent,
    } from '@dynamic-labs-sdk/client';
    import {
      addWalletConnectEvmExtension,
      connectWithWalletConnectEvm,
    } from '@dynamic-labs-sdk/evm/wallet-connect';
    import {
      addWalletConnectSolanaExtension,
      connectWithWalletConnectSolana,
    } from '@dynamic-labs-sdk/solana/wallet-connect';
    import { appendWalletConnectUriToDeepLink } from '@dynamic-labs-sdk/wallet-connect';
    import QRCode from 'qrcode';

    const client = createDynamicClient({ environmentId: 'your-environment-id' });
    await initializeClient();
    await addWalletConnectEvmExtension(client);
    await addWalletConnectSolanaExtension(client);

    onEvent({
      event: 'walletConnectUserActionRequested',
      listener: async ({ walletProviderKey }) => {
        if (isMobile()) {
          const wallet = await getWalletConnectCatalogWalletByWalletProviderKey({
            walletProviderKey,
          });
          const deepLink = wallet?.deeplinks?.native ?? wallet?.deeplinks?.universal;
          if (deepLink) window.open(deepLink, '_blank');
        } else {
          console.log('Please approve the action in your wallet application');
        }
      },
    });

    // Let user choose chain, then connect
    async function connectWallet(chain: 'EVM' | 'SOL') {
      await waitForClientInitialized();
      const catalog = await getWalletConnectCatalog();

      const connect = chain === 'EVM'
        ? connectWithWalletConnectEvm
        : connectWithWalletConnectSolana;

      const { uri, approval } = await connect();

      if (isMobile()) {
        const w = Object.values(catalog.wallets).find((x) => x.chain === chain);
        const base = w?.deeplinks?.native ?? w?.deeplinks?.universal;
        if (base) {
          window.location.href = appendWalletConnectUriToDeepLink({
            deepLinkUrl: base,
            walletConnectUri: uri,
          });
        }
      } else {
        document.getElementById('qrcode').src = await QRCode.toDataURL(uri);
      }

      const { walletAccounts } = await approval();
      onWalletProviderEvent({
        walletProviderKey: walletAccounts[0].walletProviderKey,
        event: 'accountsChanged',
        callback: ({ addresses }) => console.log('Accounts:', addresses),
      });
    }
    ```
  </Tab>
</Tabs>

***

## 7. Error handling

Use `instanceof` with the exported error classes. The following errors can be thrown by the connect functions or by `approval()`:

```typescript theme={"system"}
import { UserRejectedError } from '@dynamic-labs-sdk/client';
import { SessionClosedUnexpectedlyError } from '@dynamic-labs-sdk/wallet-connect';

try {
  const { uri, approval } = await connectWithWalletConnectEvm();
  const { walletAccounts } = await approval();
} catch (error) {
  if (error instanceof UserRejectedError) {
    console.log('User cancelled');
  } else if (error instanceof SessionClosedUnexpectedlyError) {
    console.log('Session expired, try again');
  } else {
    throw error;
  }
}
```

<Tip>
  For recurring connection or signing issues, see [Troubleshooting](/overview/troubleshooting/general) and [WalletConnect unsupported chain](/overview/troubleshooting/walletconnect/walletconnect-unsupported-chain).
</Tip>

<AccordionGroup>
  <Accordion title="From `@dynamic-labs-sdk/client`">
    | Error                                   | When                                                                                     |
    | --------------------------------------- | ---------------------------------------------------------------------------------------- |
    | `UserRejectedError`                     | User rejects the connection or signing request in the wallet app.                        |
    | `WalletAccountAlreadyVerifiedError`     | (`connectAndVerifyWithWalletConnectSolana` only) The wallet account is already verified. |
    | `WalletAlreadyLinkedToAnotherUserError` | (`connectAndVerifyWithWalletConnectSolana` only) The wallet is linked to another user.   |
  </Accordion>

  <Accordion title="From `@dynamic-labs-sdk/client/core`">
    | Error                     | When                                                                                                                                                                                                                          |
    | ------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
    | `ValueMustBeDefinedError` | A required value is missing: project settings, EVM/Solana networks not configured, no URI returned from WalletConnect, session namespace not established, or (connect-and-verify EVM) universal link not set or nonce failed. |
  </Accordion>

  <Accordion title="From `@dynamic-labs-sdk/wallet-connect`">
    | Error                            | When                                                                                                                                                                                          |
    | -------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
    | `SessionClosedUnexpectedlyError` | The WalletConnect session was closed or is no longer valid (e.g. user disconnected, or session expired). Often thrown when using the wallet provider after disconnect or during `approval()`. |
  </Accordion>

  <Accordion title="From getSignClient (before connect)">
    If the client or project is misconfigured, `getSignClient` can throw `ValueMustBeDefinedError` when: the app name (display name) is not set in the dashboard, or the WalletConnect project ID is not set in the dashboard.
  </Accordion>
</AccordionGroup>

***

## React

The core WalletConnect functions are identical in React. The main differences are:

1. **Extensions** — add them in your `dynamicClient.ts` module alongside `initializeClient`, not inside a component.
2. **QR code display** — store the `uri` in state and render it in a component.
3. **`walletConnectUserActionRequested`** — register the listener once in a top-level `useEffect`.

```tsx theme={"system"}
import {
  isMobile,
  getWalletConnectCatalogWalletByWalletProviderKey,
} from '@dynamic-labs-sdk/client';
import { useOnEvent } from '@dynamic-labs-sdk/react-hooks';
import { connectWithWalletConnectEvm } from '@dynamic-labs-sdk/evm/wallet-connect';
import { useState } from 'react';
import QRCode from 'qrcode';

// Register the action-requested listener once at app level
function useWalletConnectActionHandler() {
  useOnEvent({
    event: 'walletConnectUserActionRequested',
    listener: async ({ walletProviderKey }) => {
      if (isMobile()) {
        const wallet = await getWalletConnectCatalogWalletByWalletProviderKey({ walletProviderKey });
        const deepLink = wallet?.deeplinks?.native ?? wallet?.deeplinks?.universal;
        if (deepLink) window.open(deepLink, '_blank');
      } else {
        // Show a toast or modal prompting the user to act in their wallet app
      }
    },
  });
}

// Component that shows the QR code and awaits approval
function WalletConnectButton() {
  const [qrCodeUrl, setQrCodeUrl] = useState<string | null>(null);

  const handleConnect = async () => {
    const { uri, approval } = await connectWithWalletConnectEvm({ addToDynamicWalletAccounts: true });
    setQrCodeUrl(await QRCode.toDataURL(uri));
    await approval();
    setQrCodeUrl(null);
    // Wallet accounts updated — useGetWalletAccounts() will re-render consumers
  };

  return (
    <div>
      <button onClick={handleConnect}>Connect via WalletConnect</button>
      {qrCodeUrl && <img src={qrCodeUrl} alt="WalletConnect QR code" />}
    </div>
  );
}
```

## Types and related

**Connection result:** `{ approval: () => Promise<{ walletAccounts }>, uri: string }` (same for EVM and Solana).

## API reference

| Chain  | Function                                  | Import                                    |
| ------ | ----------------------------------------- | ----------------------------------------- |
| EVM    | `addWalletConnectEvmExtension`            | `@dynamic-labs-sdk/evm/wallet-connect`    |
| EVM    | `connectWithWalletConnectEvm`             | `@dynamic-labs-sdk/evm/wallet-connect`    |
| EVM    | `connectAndVerifyWithWalletConnectEvm`    | `@dynamic-labs-sdk/evm/wallet-connect`    |
| Solana | `addWalletConnectSolanaExtension`         | `@dynamic-labs-sdk/solana/wallet-connect` |
| Solana | `connectWithWalletConnectSolana`          | `@dynamic-labs-sdk/solana/wallet-connect` |
| Solana | `connectAndVerifyWithWalletConnectSolana` | `@dynamic-labs-sdk/solana/wallet-connect` |
| Both   | `appendWalletConnectUriToDeepLink`        | `@dynamic-labs-sdk/wallet-connect`        |

**Related**

* [Wallet Provider Events](/javascript/reference/wallets/wallet-provider-events)
* [Getting Available Wallets](/javascript/reference/wallets/get-available-wallets-to-connect)
* [Connecting and Verifying a Wallet](/javascript/reference/wallets/connect-and-verify-wallet)
* [Adding EVM Extensions](/javascript/reference/evm/adding-evm-extensions)
* [Adding Solana Extensions](/javascript/reference/solana/adding-solana-extensions)
