Skip to main content
WalletConnect lets users connect mobile wallets (e.g., MetaMask, Trust Wallet) to your dapp. The SDK handles the connection flow, session management, and wallet provider creation.

Setup

Add the WalletConnect EVM extension to enable WalletConnect for EVM chains. Call this once during app initialization, after creating your Dynamic client.
import { createDynamicClient, initializeClient } from '@dynamic-labs-sdk/client';
import { addWalletConnectEvmExtension } from '@dynamic-labs-sdk/evm/wallet-connect';

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

await initializeClient();
await addWalletConnectEvmExtension(client);

Connecting wallets

connectWithWalletConnectEvm

Connects to a WalletConnect wallet without automatic verification. Returns a URI to display (QR code or deep link) and an approval promise that resolves when the user approves.
import { connectWithWalletConnectEvm } from '@dynamic-labs-sdk/evm/wallet-connect';
import { waitForClientInitialized } from '@dynamic-labs-sdk/client';

async function connectWallet() {
  await waitForClientInitialized();

  const { uri, approval } = await connectWithWalletConnectEvm({
    addToDynamicWalletAccounts: true // Default: true
  });

  // Display URI as QR code or use deep link
  console.log('Connection URI:', uri);

  // Wait for user to approve in their wallet app
  const { walletAccounts } = await approval();
  console.log('Connected wallets:', walletAccounts);
}

Displaying a QR code

import { connectWithWalletConnectEvm } from '@dynamic-labs-sdk/evm/wallet-connect';
import QRCode from 'qrcode';

async function connectWithQRCode() {
  const { uri, approval } = await connectWithWalletConnectEvm();

  // Generate QR code from URI
  const qrCodeDataUrl = await QRCode.toDataURL(uri);
  document.getElementById('qrcode').src = qrCodeDataUrl;

  const { walletAccounts } = await approval();
  console.log('Connected:', walletAccounts);
}
Use the WalletConnect catalog to get wallet deep links:
import { connectWithWalletConnectEvm } from '@dynamic-labs-sdk/evm/wallet-connect';
import { getWalletConnectCatalog } from '@dynamic-labs-sdk/client';

async function connectWithDeepLink() {
  const { uri, approval } = await connectWithWalletConnectEvm();

  // Get wallet catalog to find deep link
  const catalog = await getWalletConnectCatalog();
  const wallet = Object.values(catalog.wallets)[0]; // Or select specific wallet

  // Open wallet app with deep link
  const deepLink = wallet.deeplinks.native || wallet.deeplinks.universal;
  if (deepLink) {
    window.location.href = `${deepLink}?uri=${encodeURIComponent(uri)}`;
  }

  const { walletAccounts } = await approval();
}

connectAndVerifyWithWalletConnectEvm

Connects and verifies wallet ownership in one step. Uses WalletConnect authentication when supported; otherwise connects first, then verifies.
import { connectAndVerifyWithWalletConnectEvm } from '@dynamic-labs-sdk/evm/wallet-connect';
import { waitForClientInitialized } from '@dynamic-labs-sdk/client';

async function connectAndVerify() {
  await waitForClientInitialized();

  const { uri, approval } = await connectAndVerifyWithWalletConnectEvm();
  console.log('Connection URI:', uri);

  // Wait for user to approve and sign message
  const { walletAccounts } = await approval();
  console.log('Verified wallets:', walletAccounts);
}

Handling user actions

The walletConnectUserActionRequested event fires when a user action is required after a WalletConnect request (e.g., signing a message, switching network). Use this to trigger deep links or prompt the user to check their wallet app.
import { onEvent } from '@dynamic-labs-sdk/client';
import { getWalletConnectCatalogWalletByWalletProviderKey } from '@dynamic-labs-sdk/client';

onEvent({
  event: 'walletConnectUserActionRequested',
  listener: async ({ walletProviderKey, walletMetadata }) => {
    const wallet = await getWalletConnectCatalogWalletByWalletProviderKey({
      walletProviderKey
    });

    if (wallet?.deeplinks) {
      const deepLink = wallet.deeplinks.native || wallet.deeplinks.universal;
      if (deepLink) {
        window.location.href = deepLink;
      }
    } else {
      console.log('Please approve the action in your wallet app');
    }
  }
});

Listening to wallet events

After connecting, use wallet provider events to track account and network changes:
import { onWalletProviderEvent } from '@dynamic-labs-sdk/client';

const walletProviderKey = walletAccounts[0].walletProviderKey;

onWalletProviderEvent({
  walletProviderKey,
  event: 'accountsChanged',
  callback: ({ addresses }) => {
    console.log('Accounts updated:', addresses);
  }
});

onWalletProviderEvent({
  walletProviderKey,
  event: 'disconnected',
  callback: () => {
    console.log('Wallet disconnected');
  }
});

Complete example

import {
  createDynamicClient,
  initializeClient,
  waitForClientInitialized,
  onEvent,
  getWalletConnectCatalog,
  getWalletConnectCatalogWalletByWalletProviderKey
} from '@dynamic-labs-sdk/client';
import {
  addWalletConnectEvmExtension,
  connectWithWalletConnectEvm
} from '@dynamic-labs-sdk/evm/wallet-connect';
import { onWalletProviderEvent } from '@dynamic-labs-sdk/client';

// Setup
const client = createDynamicClient({
  environmentId: 'your-environment-id',
});

await initializeClient();
await addWalletConnectEvmExtension(client);

// Listen for user action requests
onEvent({
  event: 'walletConnectUserActionRequested',
  listener: async ({ walletProviderKey }) => {
    const wallet = await getWalletConnectCatalogWalletByWalletProviderKey({
      walletProviderKey
    });

    if (wallet?.deeplinks?.native) {
      window.location.href = wallet.deeplinks.native;
    }
  }
});

// Connect wallet
async function connectWallet() {
  await waitForClientInitialized();

  const catalog = await getWalletConnectCatalog();
  const wallets = Object.values(catalog.wallets);

  // User selects a wallet
  const selectedWallet = wallets.find(w => w.name === 'MetaMask');

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

  // On mobile, open wallet app with deep link
  if (selectedWallet?.deeplinks?.native) {
    window.location.href = `${selectedWallet.deeplinks.native}?uri=${encodeURIComponent(uri)}`;
  } else {
    // On desktop, show QR code
    displayQRCode(uri);
  }

  const { walletAccounts } = await approval();
  console.log('Connected wallets:', walletAccounts);

  // Listen to wallet events
  const walletProviderKey = walletAccounts[0].walletProviderKey;

  onWalletProviderEvent({
    walletProviderKey,
    event: 'accountsChanged',
    callback: ({ addresses }) => {
      console.log('Accounts updated:', addresses);
    }
  });

  onWalletProviderEvent({
    walletProviderKey,
    event: 'disconnected',
    callback: () => {
      console.log('Wallet disconnected');
    }
  });
}

Error handling

import { connectWithWalletConnectEvm } from '@dynamic-labs-sdk/evm/wallet-connect';

async function connectWithErrorHandling() {
  try {
    const { uri, approval } = await connectWithWalletConnectEvm();
    const { walletAccounts } = await approval();
    console.log('Success:', walletAccounts);
  } catch (error) {
    if (error.message.includes('User rejected')) {
      console.log('User cancelled connection');
    } else if (error.message.includes('Session expired')) {
      console.log('Connection expired, please try again');
    } else {
      console.error('Connection failed:', error);
    }
  }
}

Type definitions

type WalletConnectConnectionResult = {
  approval: () => Promise<{ walletAccounts: WalletAccount[] }>;
  uri: string;
};