Mesh LogoMesh

Bitcoin Browser Wallet

Connect, query and sign with Bitcoin browser extension wallets (e.g. Xverse) via a unified API.

BitcoinBrowserWallet wraps any installed Bitcoin browser extension and exposes a single, consistent API for connecting, querying balances, signing messages, and broadcasting transactions. It communicates with extensions via the Sats Connect protocol — no SDK dependency on the extension itself is required.

Currently supported extensions: Xverse

SSR / Next.js note — All methods guard against server-side rendering. Calling any method outside a browser context throws a clear error. In Next.js, gate calls with typeof window !== "undefined" or place them inside useEffect.


Get Installed Wallets

Returns the list of Bitcoin wallet extensions the user currently has installed.

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

const wallets = BitcoinBrowserWallet.getInstalledWallets();
// [{ id: "xverse", name: "Xverse", icon: "https://..." }]

Connect Wallet

Opens the extension's approval popup and returns a connected BitcoinBrowserWallet instance.

const wallet = await BitcoinBrowserWallet.enable("xverse");

Persist session across page reloads

Pass { persist: true } to save the wallet name in localStorage. On subsequent page loads the wallet reconnects silently (no popup) when hasPersistedSession() returns true.

// First visit — shows popup
const wallet = await BitcoinBrowserWallet.enable("xverse", { persist: true });

// On every subsequent page load — silent reconnect
if (BitcoinBrowserWallet.hasPersistedSession()) {
  const wallet = await BitcoinBrowserWallet.enable("xverse", { persist: true });
}

Restore silently

restore() is a one-liner alternative: it reads the persisted name and reconnects, returning null if nothing was saved or the extension is no longer available.

const wallet = await BitcoinBrowserWallet.restore();
if (wallet) {
  // already connected — no popup shown
}

Disconnect

Clears the persisted session so restore() returns null on the next load.

wallet.disconnect();

Get Network

const network = await wallet.getNetwork();
// "Mainnet" | "Testnet4"

Get Addresses

Returns the wallet's addresses for the requested purpose(s). Each address includes its public key, address type, and derivation purpose.

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

const addresses = await wallet.getAddresses([
  AddressPurpose.Payment,   // P2WPKH (BIP-84)
  AddressPurpose.Ordinals,  // P2TR   (BIP-86)
]);

Example response:

[
  {
    "address": "bc1q...",
    "publicKey": "02abc...",
    "purpose": "payment",
    "addressType": "p2wpkh",
    "walletType": "software"
  },
  {
    "address": "bc1p...",
    "publicKey": "02def...",
    "purpose": "ordinals",
    "addressType": "p2tr",
    "walletType": "software"
  }
]

Get Accounts

Same as getAddresses but returns BitcoinAccount objects (identical shape, different semantic role — used when you need the full account record).

const accounts = await wallet.getAccounts([AddressPurpose.Payment]);

Get Balance

Returns confirmed, unconfirmed, and total balances in satoshis as strings.

const balance = await wallet.getBalance();
// { confirmed: "100000", unconfirmed: "5000", total: "105000" }

Fetch UTXOs

Returns all unspent outputs for the requested address purposes, each annotated with the owning address and purpose.

const utxos = await wallet.fetchUTXOs([AddressPurpose.Payment]);

Example response:

[
  {
    "txid": "abc123...",
    "vout": 0,
    "value": 50000,
    "address": "bc1q...",
    "purpose": "payment",
    "status": { "confirmed": true, "block_height": 840000 }
  }
]

Get Transaction History

Returns paginated transaction history across the requested purposes, sorted newest-first (unconfirmed transactions appear first).

const txs = await wallet.getTransactionHistory({
  purposes: [AddressPurpose.Payment],
  lastSeenTxid: "abc123...", // optional — for pagination
});

Sign Message

Signs an arbitrary message using the BIP-137 ECDSA compact format (65 bytes, base64-encoded).

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

const result = await wallet.signMessage(
  "bc1q...",           // address to sign with
  "Hello, Bitcoin!",  // message
  MessageSigningProtocols.ECDSA,
);

// {
//   signature: "H...(base64)",
//   messageHash: "...(hex)",
//   address: "bc1q...",
//   protocol: "ECDSA"
// }

Verify Message

Verifies a BIP-137 ECDSA signature locally (no extension call). Returns a result object with .valid, an optional .recoveredPublicKey, and an optional .reason string on failure.

const result = await wallet.verifyMessage(
  "bc1q...",
  "Hello, Bitcoin!",
  signatureBase64,
);

if (result.valid) {
  console.log("Signed by:", result.recoveredPublicKey);
} else {
  console.log("Invalid:", result.reason);
}

Non-ECDSA / BIP-322 signatures (e.g. Taproot Schnorr) return { valid: false, reason: "..." } rather than throwing.


Sign Transfer

Prompts the user to sign and broadcast a transfer to one or more recipients in a single step. Returns the broadcast txid (64-char hex).

const txid = await wallet.signTransfer([
  { address: "bc1q...", amount: 10_000 }, // amount in satoshis
]);
// "a1b2c3..." (64-char txid)

Multi-recipient:

const txid = await wallet.signTransfer([
  { address: "bc1q...", amount: 10_000 },
  { address: "bc1p...", amount: 25_000 },
]);

Sign PSBT

Signs an externally-constructed Partially Signed Bitcoin Transaction (PSBT). The PSBT must be provided as a base64-encoded string.

const result = await wallet.signPsbt({
  psbt: base64Psbt,
  signInputs: {
    "bc1q...": [0], // address → input indices to sign
  },
  broadcast: false, // set true to sign and broadcast in one call
});
// returns signed PSBT (base64) when broadcast=false
// returns txid when broadcast=true

On this page