Skip to content

WalletConnect Class

The WalletConnect class provides a client-side implementation for connecting to NIP-47 wallet services. It handles all communication with wallet services, including request encryption, response decryption, and notification handling.

Overview

WalletConnect is designed to be a complete client for interacting with Nostr Wallet Connect services. It provides:

  • Reactive Architecture: Built on RxJS for efficient event handling
  • Encryption Support: Automatic handling of NIP-04 and NIP-44 encryption
  • Request Management: Built-in timeout handling and error management
  • Notification Support: Real-time wallet notifications
  • Type Safety: Full TypeScript support with typed responses

Relay Connections

The WalletConnect requires two methods for communicating with relays: a subscription method for receiving events and a publish method for sending events.

These methods can be set either through the constructor or globally on the class. At least one of these approaches must be used before creating a WalletConnect instance.

typescript
import { Observable } from "rxjs";

function subscriptionMethod(relays, filters) {
  return new Observable((observer) => {
    // Create subscription to relays
    const cleanup = subscribeToRelays(relays, filters, (event) => {
      observer.next(event);
    });

    return () => cleanup();
  });
}

async function publishMethod(relays, event) {
  for (const relay of relays) {
    await publishToRelay(relay, event);
  }
}

// Set methods globally once at app initialization
WalletConnect.subscriptionMethod = subscriptionMethod;
WalletConnect.publishMethod = publishMethod;

// Or pass them as options when creating a signer
const signer = new WalletConnect({
  relays: ["wss://relay.example.com"],
  subscriptionMethod,
  publishMethod,
  // ... other options
});

Using the relay pool

The simplest way to set these methods is to use the RelayPool from the applesauce-relay package.

typescript
import { RelayPool } from "applesauce-relay";

const pool = new RelayPool();

// Set the pool globally
WalletConnect.pool = pool;

// Or pass the pool as an option when creating a client
const client = new WalletConnect({
  relays: ["wss://relay.example.com"],
  pool,
  // ... other options
});

Using a nostr+walletconnect:// URI

The most common way to create a client is using a connection string:

typescript
import { WalletConnect } from "applesauce-wallet-connect";

// Create a new client from a connection string
const wallet = WalletConnect.fromConnectionString(
  "nostr+walletconnect://relay.example.com?secret=abc123&pubkey=def456",
);

// Start using the wallet
await wallet.getInfo();
await wallet.payInvoice("lnbc1...");

Using a nostr+walletauth:// URI

Some wallets support a nostr+walletauth:// URI for the client app to request a connection from the wallet service.

typescript
import { WalletConnect } from "applesauce-wallet-connect";

// Create a new client with a random secret key
const secret = generateSecretKey();
const wallet = new WalletConnect({
  secret,
  relays: ["wss://relay.wallet.com"],
});

// Get the auth URI and show it to the user as a QR code
const authUri = wallet.getAuthURI();

// Wait for the service to respond
await wallet.waitForService();
console.log("Connected to wallet service!");

// Start using the wallet
await wallet.getInfo();

Relay Optimization

By default, the WalletConnect client has acceptRelayHint enabled, which allows it to automatically switch to the recommended relay if the wallet service provides one. This only applies when using the nostr+walletauth:// URIs to connect to the wallet service.

typescript
// Create a client with relay optimization enabled (default)
const connect = new WalletConnect({
  secret,
  relays: ["wss://relay1.com", "wss://relay2.com", "wss://relay3.com"],
  acceptRelayHint: true, // This is the default value
});

const authUri = connect.getAuthURI();

// On the service side
const service = await WalletService.fromAuthURI(authUri, {
  signer,
  handlers,
  // Randomly select a single relay
  overrideRelay: (relays) => relays[Math.floor(Math.random() * relays.length)],
});

When acceptRelayHint is enabled the client will switch to the single relay when the wallet service responds and includes a relay recommendation in the wallet support events

This optimization only applies to the nostr+walletauth:// flow and is ignored when using nostr+walletconnect:// connection strings.

Checking Wallet Capabilities

Before using specific methods, it's good practice to check what the wallet supports:

typescript
// Get the wallet's supported methods and capabilities
const support = await wallet.getSupport();

if (support) {
  console.log("Supported methods:", support.methods);
  console.log("Encryption methods:", support.encryption);
  console.log("Notification types:", support.notifications);
}

// Check specific method support
if (await wallet.supportsMethod("pay_invoice")) {
  console.log("Wallet supports invoice payments");
}

if (await wallet.supportsNotifications()) {
  console.log("Wallet supports notifications");
}

Basic Wallet Operations

Getting Wallet Information

typescript
// Get basic wallet info
const info = await wallet.getInfo();
console.log("Wallet alias:", info.alias);
console.log("Network:", info.network);

// Get current balance
const balance = await wallet.getBalance();
console.log("Balance:", balance.balance, "msats");

Transaction History

typescript
// Get recent transactions
const transactions = await wallet.listTransactions({
  limit: 10,
  type: "incoming",
});

transactions.transactions.forEach((tx) => {
  console.log(`${tx.type}: ${tx.amount} msats`);
});

Payment Operations

Invoice Payments

typescript
// Pay a Lightning invoice
const result = await wallet.payInvoice("lnbc1...");
console.log("Payment successful:", result.preimage);

// Pay multiple invoices at once
const results = await wallet.payMultipleInvoices([{ invoice: "lnbc1..." }, { invoice: "lnbc2..." }]);

Keysend Payments

typescript
// Send a keysend payment
const result = await wallet.payKeysend(
  "pubkey123...",
  100000, // 100 sats in msats
  undefined, // preimage (optional)
  [{ type: 696969, value: "Hello from Nostr!" }], // TLV records
);

Creating Invoices

typescript
// Create a new invoice
const invoice = await wallet.makeInvoice(50000, {
  description: "Coffee payment",
  expiry: 3600, // 1 hour
});

console.log("Invoice created:", invoice.payment_request);

Notifications

Using the notification Method

The notification method allows you to listen for specific types of notifications:

typescript
// Listen for payment received notifications
const subscription = wallet.notification("payment_received", (notification) => {
  const { payment_hash, amount, fee } = notification;
  console.log(`Payment received: ${amount} msats`);
});

// Listen for payment sent notifications
const sentSubscription = wallet.notification("payment_sent", (notification) => {
  console.log("Payment sent successfully");
});

// Clean up when done
subscription.unsubscribe();
sentSubscription.unsubscribe();

Using Observables

For more advanced usage, you can subscribe to the observables directly:

typescript
import { filter, map } from "rxjs";

// Subscribe to all notifications
wallet.notifications$.subscribe((notification) => {
  console.log("Notification:", notification);
});

// Filter notifications by type
wallet.notifications$
  .pipe(
    filter((n) => n.notification_type === "payment_received"),
    map((n) => n.notification),
  )
  .subscribe((notification) => {
    console.log("Payment received:", notification);
  });

Available Observables

support$

Emits wallet support information when it changes:

typescript
wallet.support$.subscribe((support) => {
  if (support) {
    console.log("Wallet supports:", support.methods);
  }
});

notifications$

Emits all wallet notifications:

typescript
wallet.notifications$.subscribe((notification) => {
  console.log("Notification:", notification);
});

encryption$

Emits the preferred encryption method:

typescript
wallet.encryption$.subscribe((method) => {
  console.log("Using encryption:", method);
});

Error Handling

WalletConnect provides comprehensive error handling with typed errors:

typescript
try {
  const result = await wallet.payInvoice("lnbc1...");
  console.log("Payment successful:", result.preimage);
} catch (error) {
  if (error instanceof InsufficientBalanceError) {
    console.log("Not enough balance:", error.message);
  } else if (error instanceof InvalidInvoiceError) {
    console.log("Invalid invoice:", error.message);
  } else {
    console.log("Unexpected error:", error.message);
  }
}

Common Error Types

  • InsufficientBalanceError: When the wallet doesn't have enough balance
  • InvalidInvoiceError: When an invoice is malformed
  • RateLimitedError: When too many requests are made
  • UserRejectedError: When the user rejects the operation

For a complete list of error types and their meanings, see the typedocs.