> ## 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](https://docs.dynamic.xyz/docs/javascript/reference/evm/adding-evm-extensions.md) 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.

> [!NOTE]
>
> Integrating MetaMask? Use the [MetaMask Connection
> Guide](https://docs.dynamic.xyz/docs/javascript/reference/wallets/metamask-connection-guide.md). MetaMask SDK
> pairing uses `metamaskSdkUri`, not the generic WalletConnect flow documented
> on this page.

***

## 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 in your WalletConnect picker. 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 walletKey = 'rainbow';
const catalog = await getWalletConnectCatalog();
const wallet = catalog.wallets[walletKey];
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 }`.

On mobile, `metadata.nativeLink` and `metadata.universalLink` are also the wallet's return target after the user approves. On web, `metadata.universalLink` defaults to the current page URL when you do not set it. If your app embeds the SDK inside an iframe on another page, set `metadata.universalLink` to the host page's URL so the wallet returns to the real page rather than the iframe URL that the default resolves to. Note that this URL is also the domain wallets show in their sign-in prompts, so it should match the page your users see. On React Native, keep `metadata.universalLink` on your app's own domain instead, since it also drives the signer origin and passkey domain there.

| 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:**

> [tab] 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 {
>     const qrImage = document.getElementById('qrcode');
>     if (qrImage instanceof HTMLImageElement) {
>       qrImage.src = await QRCode.toDataURL(uri);
>     }
>   }
>
>   const { walletAccounts } = await approval();
>   return walletAccounts;
> }
> ```

> [tab] 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 {
>     const qrImage = document.getElementById('qrcode');
>     if (qrImage instanceof HTMLImageElement) {
>       qrImage.src = await QRCode.toDataURL(uri);
>     }
>   }
>
>   const { walletAccounts } = await approval();
>   return walletAccounts;
> }
> ```

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

***

## 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](https://docs.dynamic.xyz/docs/javascript/reference/wallets/wallet-provider-events.md) 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.

> [tab] 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(walletKey: string) {
>   await waitForClientInitialized();
>   const catalog = await getWalletConnectCatalog();
>   const { uri, approval } = await connectWithWalletConnectEvm();
>
>   if (isMobile()) {
>     const w = catalog.wallets[walletKey];
>     const base = w?.deeplinks?.native ?? w?.deeplinks?.universal;
>     if (base) {
>       window.location.href = appendWalletConnectUriToDeepLink({
>         deepLinkUrl: base,
>         walletConnectUri: uri,
>       });
>     }
>   } else {
>     const qrImage = document.getElementById('qrcode');
>     if (qrImage instanceof HTMLImageElement) {
>       qrImage.src = await QRCode.toDataURL(uri);
>     }
>   }
>
>   const { walletAccounts } = await approval();
>   onWalletProviderEvent({
>     walletProviderKey: walletAccounts[0].walletProviderKey,
>     event: 'accountsChanged',
>     callback: ({ addresses }) => console.log('Accounts:', addresses),
>   });
> }
> ```

> [tab] 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 {
>     const qrImage = document.getElementById('qrcode');
>     if (qrImage instanceof HTMLImageElement) {
>       qrImage.src = await QRCode.toDataURL(uri);
>     }
>   }
>
>   const { walletAccounts } = await approval();
>   onWalletProviderEvent({
>     walletProviderKey: walletAccounts[0].walletProviderKey,
>     event: 'accountsChanged',
>     callback: ({ addresses }) => console.log('Accounts:', addresses),
>   });
> }
> ```

> [tab] 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 {
>     const qrImage = document.getElementById('qrcode');
>     if (qrImage instanceof HTMLImageElement) {
>       qrImage.src = await QRCode.toDataURL(uri);
>     }
>   }
>
>   const { walletAccounts } = await approval();
>   onWalletProviderEvent({
>     walletProviderKey: walletAccounts[0].walletProviderKey,
>     event: 'accountsChanged',
>     callback: ({ addresses }) => console.log('Accounts:', addresses),
>   });
> }
> ```

***

## 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 { connectWithWalletConnectEvm } from '@dynamic-labs-sdk/evm/wallet-connect';
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](https://docs.dynamic.xyz/docs/get-started/troubleshooting/general.md) and [WalletConnect unsupported chain](https://docs.dynamic.xyz/docs/get-started/troubleshooting/walletconnect/walletconnect-unsupported-chain.md).

> [accordion] 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] 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] 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] 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 Dynamic Console, or the WalletConnect project ID is not set in the Dynamic Console.

***

## 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>
  );
}
```

## React Native

The core WalletConnect functions are identical on React Native. The differences are how you send the user to their wallet app and that there is no QR code — on mobile the user always deep links straight to the wallet app:

1. **Open the wallet** — use `Linking.openURL` instead of `window.open` / `window.location.href`. The only difference between Expo and bare React Native is where `Linking` comes from: `expo-linking` on Expo, `react-native` on bare.
2. **No QR code** — skip the `qrcode` rendering; mobile connects by deep link only.
3. **Deep links** — set `metadata.nativeLink` and `metadata.universalLink` when creating the client so the wallet can redirect back to your app. These links are also the return target after the user approves the connection in their wallet: on web `universalLink` defaults to the current page URL, but React Native has no page to default to, so without them some wallets (for example Backpack) fall back to opening your dapp in the system browser. See [React Native Setup](https://docs.dynamic.xyz/docs/javascript/react-native/setup.md).

> [!NOTE]
>
> Before wiring up the code below, set up the Metro `ws` alias described in [Adding EVM Extensions → React Native](https://docs.dynamic.xyz/docs/javascript/reference/evm/adding-evm-extensions.md#react-native) — `@dynamic-labs-sdk/evm` needs it on React Native regardless of which extension (WalletConnect included) you use.

### Install the WalletConnect dependency

WalletConnect needs a native shim that the base [React Native Setup](https://docs.dynamic.xyz/docs/javascript/react-native/setup.md) does not install. Add it before wiring up the code below.

> [tab] Expo
>
> ```shell theme={"system"}
> npx expo install @walletconnect/react-native-compat
> ```

> [tab] Bare React Native
>
> ```shell theme={"system"}
> npm install @walletconnect/react-native-compat @react-native-community/netinfo
> ```
>
> `@react-native-community/netinfo` is a peer dependency of the compat package — without it, sessions will not reconnect reliably after network changes.

Then import the shim as the **first** line of your `polyfills.ts`, before the crypto shims, so it loads before anything WalletConnect-related:

```ts polyfills.ts theme={"system"}
import '@walletconnect/react-native-compat';
```

`walletConnectUserActionRequested` fires whenever the connected wallet needs the user to act (sign a message, switch network). The listener is where **you** decide how to route the user to their wallet — you are not required to deep link immediately.

> [!NOTE]
>
> Opening the wallet the instant the event fires (shown below) is the simplest option, but an abrupt app switch can confuse users. Many apps instead surface their own UI first — a modal or banner such as "Continue in your wallet to approve" with an **Open wallet** button — and only call `Linking.openURL` when the user taps it. See [Prompt the user before opening the wallet](#prompt-the-user-before-opening-the-wallet).

The simplest approach registers the listener once at app setup and opens the wallet directly:

> [tab] Expo
>
> ```tsx theme={"system"}
> import {
>   waitForClientInitialized,
>   getWalletConnectCatalog,
>   getWalletConnectCatalogWalletByWalletProviderKey,
>   onEvent,
> } from '@dynamic-labs-sdk/client';
> import { connectWithWalletConnectEvm } from '@dynamic-labs-sdk/evm/wallet-connect';
> import { appendWalletConnectUriToDeepLink } from '@dynamic-labs-sdk/wallet-connect';
> import * as Linking from 'expo-linking';
>
> // Open the wallet app when it needs the user to act (sign, switch network)
> onEvent({
>   event: 'walletConnectUserActionRequested',
>   listener: async ({ walletProviderKey }) => {
>     const wallet = await getWalletConnectCatalogWalletByWalletProviderKey({
>       walletProviderKey,
>     });
>     const deepLink = wallet?.deeplinks?.native ?? wallet?.deeplinks?.universal;
>     if (deepLink) await Linking.openURL(deepLink);
>   },
> });
>
> async function connect() {
>   await waitForClientInitialized();
>   const { uri, approval } = await connectWithWalletConnectEvm({
>     addToDynamicWalletAccounts: true,
>   });
>
>   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) {
>     await Linking.openURL(
>       appendWalletConnectUriToDeepLink({
>         deepLinkUrl: base,
>         walletConnectUri: uri,
>       }),
>     );
>   }
>
>   const { walletAccounts } = await approval();
>   return walletAccounts;
> }
> ```

> [tab] Bare React Native
>
> ```tsx theme={"system"}
> import {
>   waitForClientInitialized,
>   getWalletConnectCatalog,
>   getWalletConnectCatalogWalletByWalletProviderKey,
>   onEvent,
> } from '@dynamic-labs-sdk/client';
> import { connectWithWalletConnectEvm } from '@dynamic-labs-sdk/evm/wallet-connect';
> import { appendWalletConnectUriToDeepLink } from '@dynamic-labs-sdk/wallet-connect';
> import { Linking } from 'react-native';
>
> // Open the wallet app when it needs the user to act (sign, switch network)
> onEvent({
>   event: 'walletConnectUserActionRequested',
>   listener: async ({ walletProviderKey }) => {
>     const wallet = await getWalletConnectCatalogWalletByWalletProviderKey({
>       walletProviderKey,
>     });
>     const deepLink = wallet?.deeplinks?.native ?? wallet?.deeplinks?.universal;
>     if (deepLink) await Linking.openURL(deepLink);
>   },
> });
>
> async function connect() {
>   await waitForClientInitialized();
>   const { uri, approval } = await connectWithWalletConnectEvm({
>     addToDynamicWalletAccounts: true,
>   });
>
>   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) {
>     await Linking.openURL(
>       appendWalletConnectUriToDeepLink({
>         deepLinkUrl: base,
>         walletConnectUri: uri,
>       }),
>     );
>   }
>
>   const { walletAccounts } = await approval();
>   return walletAccounts;
> }
> ```

### Prompt the user before opening the wallet

Instead of deep linking the moment `walletConnectUserActionRequested` fires, store the pending deep link and show your own UI first, then open the wallet when the user taps a button. The user knows the app is about to switch, and you control the messaging.

Register the listener with `useOnEvent` and keep the pending deep link in component state. Only the `Linking` import differs between Expo and bare React Native.

> [tab] Expo
>
> ```tsx theme={"system"}
> import { useState } from 'react';
> import { Modal, View, Text, Button } from 'react-native';
> import { getWalletConnectCatalogWalletByWalletProviderKey } from '@dynamic-labs-sdk/client';
> import { useOnEvent } from '@dynamic-labs-sdk/react-hooks';
> import * as Linking from 'expo-linking';
>
> function WalletActionPrompt() {
>   const [pendingDeepLink, setPendingDeepLink] = useState<string | null>(null);
>
>   useOnEvent({
>     event: 'walletConnectUserActionRequested',
>     listener: async ({ walletProviderKey }) => {
>       const wallet = await getWalletConnectCatalogWalletByWalletProviderKey({
>         walletProviderKey,
>       });
>       const deepLink = wallet?.deeplinks?.native ?? wallet?.deeplinks?.universal;
>       if (deepLink) {
>         setPendingDeepLink(deepLink);
>       }
>     },
>   });
>
>   const openWallet = async () => {
>     if (pendingDeepLink) {
>       await Linking.openURL(pendingDeepLink);
>       setPendingDeepLink(null);
>     }
>   };
>
>   return (
>     <Modal visible={pendingDeepLink !== null} transparent animationType="slide">
>       <View>
>         <Text>Continue in your wallet to approve the request.</Text>
>         <Button title="Open wallet" onPress={openWallet} />
>       </View>
>     </Modal>
>   );
> }
> ```

> [tab] Bare React Native
>
> ```tsx theme={"system"}
> import { useState } from 'react';
> import { Modal, View, Text, Button, Linking } from 'react-native';
> import { getWalletConnectCatalogWalletByWalletProviderKey } from '@dynamic-labs-sdk/client';
> import { useOnEvent } from '@dynamic-labs-sdk/react-hooks';
>
> function WalletActionPrompt() {
>   const [pendingDeepLink, setPendingDeepLink] = useState<string | null>(null);
>
>   useOnEvent({
>     event: 'walletConnectUserActionRequested',
>     listener: async ({ walletProviderKey }) => {
>       const wallet = await getWalletConnectCatalogWalletByWalletProviderKey({
>         walletProviderKey,
>       });
>       const deepLink = wallet?.deeplinks?.native ?? wallet?.deeplinks?.universal;
>       if (deepLink) {
>         setPendingDeepLink(deepLink);
>       }
>     },
>   });
>
>   const openWallet = async () => {
>     if (pendingDeepLink) {
>       await Linking.openURL(pendingDeepLink);
>       setPendingDeepLink(null);
>     }
>   };
>
>   return (
>     <Modal visible={pendingDeepLink !== null} transparent animationType="slide">
>       <View>
>         <Text>Continue in your wallet to approve the request.</Text>
>         <Button title="Open wallet" onPress={openWallet} />
>       </View>
>     </Modal>
>   );
> }
> ```

## 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](https://docs.dynamic.xyz/docs/javascript/reference/wallets/wallet-provider-events.md)
* [Getting Available Wallets](https://docs.dynamic.xyz/docs/javascript/reference/wallets/get-available-wallets-to-connect.md)
* [Connecting and Verifying a Wallet](https://docs.dynamic.xyz/docs/javascript/reference/wallets/connect-and-verify-wallet.md)
* [Adding EVM Extensions](https://docs.dynamic.xyz/docs/javascript/reference/evm/adding-evm-extensions.md)
* [Adding Solana Extensions](https://docs.dynamic.xyz/docs/javascript/reference/solana/adding-solana-extensions.md)


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