Mesh LogoMesh
ResourcesChallenges

Wallet Integration

Connect any Cardano wallet to your dApp with Mesh SDK's unified API.

Wallet integration is complex due to multiple providers with varying CIP-30 implementations. Mesh provides a unified API that works with Eternl, Lace, Yoroi, and all CIP-30 wallets.

The challenge

CIP-30 variations

IssueDescription
Method availabilitySome wallets skip optional methods
Return formatsHex strings vs byte arrays
Error handlingInconsistent error messages and codes
Connection stateSome persist across reloads, others don't
Network detectionDifferent implementations

Cardano vs Ethereum

// Cardano: each wallet registers separately
window.cardano.eternl
window.cardano.lace
window.cardano.yoroi

// Ethereum: mostly standardized
window.ethereum

The solution

Mesh normalizes wallet differences with a unified API.

Quick start

npm install @meshsdk/core @meshsdk/react

Option 1: CardanoWallet component

The fastest way to add wallet connectivity:

import { CardanoWallet, MeshProvider } from "@meshsdk/react";

function App() {
  return (
    <MeshProvider>
      <CardanoWallet />
    </MeshProvider>
  );
}

This component handles:

  • Wallet detection
  • Selection dropdown
  • Connection flow
  • Address display
  • Disconnect functionality

Option 2: useWallet hook

For custom wallet UIs:

import { useWallet } from "@meshsdk/react";

function WalletButton() {
  const { connected, wallet, connect, disconnect } = useWallet();

  if (!connected) {
    return <button onClick={() => connect("eternl")}>Connect</button>;
  }

  return (
    <div>
      <button onClick={handleTransaction}>Send</button>
      <button onClick={disconnect}>Disconnect</button>
    </div>
  );
}

Option 3: MeshCardanoBrowserWallet class

For full control:

import { MeshCardanoBrowserWallet } from "@meshsdk/wallet";

// Detect installed wallets
const wallets = MeshCardanoBrowserWallet.getInstalledWallets();

// Connect to specific wallet
const wallet = await MeshCardanoBrowserWallet.enable("eternl");

// Unified API regardless of wallet
const utxos = await wallet.getUtxosMesh();
const address = await wallet.getChangeAddressBech32();
const balance = await wallet.getBalanceMesh();

Common patterns

Persist wallet connection

import { useEffect } from "react";
import { useWallet } from "@meshsdk/react";

function WalletManager() {
  const { connected, connect, wallet } = useWallet();

  // Auto-connect on page load
  useEffect(() => {
    const saved = localStorage.getItem("connectedWallet");
    if (saved && !connected) connect(saved);
  }, []);

  // Save wallet choice
  useEffect(() => {
    if (connected && wallet) {
      localStorage.setItem("connectedWallet", wallet.walletId);
    }
  }, [connected, wallet]);

  return <CardanoWallet />;
}

Validate network

import { useWallet } from "@meshsdk/react";

function NetworkGuard({ children, requiredNetwork }) {
  const { wallet, connected } = useWallet();
  const [networkId, setNetworkId] = useState(null);

  useEffect(() => {
    if (connected) wallet.getNetworkId().then(setNetworkId);
  }, [connected, wallet]);

  if (connected && networkId !== requiredNetwork) {
    return <div>Please switch to {requiredNetwork === 1 ? "Mainnet" : "Testnet"}</div>;
  }

  return children;
}

Handle errors

async function connectWallet(walletId) {
  try {
    await connect(walletId);
  } catch (error) {
    if (error.message.includes("popup")) {
      showNotification("Please allow popups for wallet connection");
    } else if (error.message.includes("User")) {
      showNotification("Connection cancelled");
    } else {
      showNotification("Connection failed. Please try again.");
    }
  }
}

Custom wallet UI

Build a branded wallet selector:

import { MeshCardanoBrowserWallet } from "@meshsdk/wallet";
import { useWallet } from "@meshsdk/react";

function CustomWalletSelector() {
  const { connect, connected, wallet, disconnect } = useWallet();
  const installedWallets = MeshCardanoBrowserWallet.getInstalledWallets();

  if (connected) {
    return (
      <div className="wallet-connected">
        <span>Connected</span>
        <button onClick={disconnect}>Disconnect</button>
      </div>
    );
  }

  return (
    <div className="wallet-grid">
      {installedWallets.map((w) => (
        <button key={w.id} onClick={() => connect(w.id)}>
          <img src={w.icon} alt={w.name} />
          <span>{w.name}</span>
        </button>
      ))}
    </div>
  );
}

Server-side wallets

For backend operations:

import { MeshCardanoHeadlessWallet, AddressType } from "@meshsdk/wallet";
import { BlockfrostProvider } from "@meshsdk/core";

const provider = new BlockfrostProvider("<api-key>");

const wallet = await MeshCardanoHeadlessWallet.fromMnemonic({
  networkId: 0,
  walletAddressType: AddressType.Base,
  fetcher: provider,
  submitter: provider,
  mnemonic: ["your", "mnemonic", "words", "..."],
});

// Same API as MeshCardanoBrowserWallet
const address = await wallet.getChangeAddressBech32();
const utxos = await wallet.getUtxosMesh();

Wallet feature support

FeatureSupport
Basic CIP-30All wallets
CollateralMost wallets
Multi-accountSome wallets (Eternl)
Hardware walletVia extension

On this page