Skip to content

Using a Cashu Wallet (NIP-60) with NDK

This snippet demonstrates how to create, configure, and use an NDKCashuWallet for managing Cashu tokens.

typescript
import NDK, { NDKKind, NDKCashuMintList } from "@nostr-dev-kit/ndk";
import { NDKCashuWallet, NDKWalletStatus } from "@nostr-dev-kit/ndk-wallet";

/**
 * Example function to set up an NDKCashuWallet
 */
async function setupCashuWallet(ndk: NDK, mints: string[], relays: string[]) {
    // Create the Cashu wallet instance
    const wallet = new NDKCashuWallet(ndk);
    
    // Add mints to the wallet
    wallet.mints = mints;
    wallet.relays = relays;
    
    // Generate or load a p2pk (Pay-to-Public-Key) token
    // This is used for receiving payments with NIP-61 (nutzaps)
    const p2pk = await wallet.getP2pk();
    console.log(`Wallet p2pk: ${p2pk}`);

    await wallet.publish();
    console.log('published the wallet event')

    // configure reception of NIP-61 nutzaps for the user
    // this publishes an event that tells others who want to zap
    // this user the information they need to publish a NIP-61 nutzap.
    const mintlistForNutzapReception = new NDKCashuMintList(ndk);
    mintlistForNutzapReception.relays = wallet.relays;
    mintlistForNutzapReception.mints = wallet.mints;
    mintlistForNutzapReception.p2pk = wallet.p2pk;
    await mintlistForNutzapReception.publish();
    console.log('published he nutzap mintlist event to receive nutzaps', mintlistForNutzapReception.rawEvent())
    
    return wallet;
}

/**
 * Get balance for a specific mint
 */
function getMintBalance(wallet: NDKCashuWallet, mintUrl: string) {
    const balance = wallet.mintBalance(mintUrl);
    console.log(`Balance for mint ${mintUrl}: ${balance} sats`);
    return balance;
}

/**
 * Check if the user already has a nutsack (NIP-60) wallet.
 **/
async function findExistingWallet(ndk: NDK): Promise<NDKCashuWallet | undefined> {
    const activeUser = ndk.activeUser;

    if (!activeUser) throw "we need a user first, set a signer in ndk";
    
    const event = await ndk.fetchEvent([
        { kinds: [ NDKKind.CashuWallet], authors: [activeUser.pubkey] }
    ])

    // if we receive a CashuWallet event we load the wallet
    if (event) return await NDKCashuWallet.from(event);
}

/**
 * Example usage
 */
async function main() {
    // we assume ndk is already connected and ready
    // ...
    
    let wallet: NDKCashuWallet | undefined;

    wallet = await findExistingWallet(ndk);
    
    // if we don't have a wallet, we create one
    if (!wallet) {
        // List of mints to use
        const mints = [ "https://8333.space:3338" ];

        // Setup the wallet
        wallet = await setupCashuWallet(ndk, mints);
    }

    wallet.on("balance_updated", (balance) => {
        console.log(`Wallet balance updated: ${balance?.amount} sats`);
        // You might want to update your UI here
    });

    wallet.start();
    
    // Example: Check wallet balance
    const totalBalance = wallet.balance?.amount || 0;
    console.log(`Total wallet balance: ${totalBalance} sats`);
    
    // Example: Need to fund wallet?
    // See the Cashu Deposits snippet for funding your wallet with lightning
    
    // Example: Get balance for specific mint
    for (const mint of mints) {
        getMintBalance(wallet, mint);
    }
    
    // Note: For monitoring nutzaps, see the Nutzap Monitor snippet
    
    // Keep the connection open for monitoring
    // In a real app, you'd use proper lifecycle management
}

Notes

  • The Cashu wallet implements the NIP-60 specification for Nostr eCash
  • The wallet needs to be assigned to the NDK instance with ndk.wallet = wallet for full integration
  • To receive tokens, you need to generate a p2pk (Pay-to-Public-Key) identifier
  • The wallet monitors and processes events continuously while active
  • Always handle wallet operations with proper error handling
  • For handling nutzaps (Cashu tokens sent via Nostr), see the Nutzap Monitor snippet
  • For depositing funds to your wallet, see the Cashu Deposits snippet