Skip to content

Lightning Zaps ​

Nostrify provides an LNURL class for implementing NIP-57 zaps.

The LNURL class ​

The LNURL class can be created from a lud06 or lud16 entry in a user's kind 0 metadata, then used to generate an invoice.

typescript
import { LNURL } from '@nostrify/nostrify/ln';

const lnurl = LNURL.fromString(lud06); // eg "lnurl1..."
const lnurl = LNURL.fromLightningAddress(lud16); // eg "[email protected]"

Once an LNURL object is created, it can be used to generate an invoice.

typescript
const { pr } = await lnurl.getInvoice({
  amount: 1000,
  nostr: zapRequest, // { kind: 9734, ... }
});

Finally, the user will need to pay the invoice. For example, using WebLN:

typescript
await window.webln.enable();
await window.webln.sendPayment(pr);

Full Example ​

This function implements a full zap request flow with Nostrify.

typescript
import { NostrEvent, NostrSigner, NRelay, NSchema as n } from '@nostrify/nostrify';
import { LNURL } from '@nostrify/nostrify/ln';

/**
 * Initiate a zap for a particular event.
 * Returns a Lightning invoice that must be paid by the user to complete the zap.
 */
async function zapRequest(opts: {
  /** Nostrify relay or pool implementation. */
  nostr: NRelay;
  /** Logged-in user's signer object. */
  signer: NostrSigner;
  /** Event to be zapped. */
  target: NostrEvent;
  /** Amount to zap in millisatoshis. */
  amount: number;
  /** List of relays the zap recipt should be sent to. At least 1 must be provided. */
  relays: [string, ...string[]];
  /** Optional signal to abort the zap request. */
  signal?: AbortSignal;
}): Promise<`lnbc1${string}`> {
  const { nostr, signer, target, amount, relays, signal } = opts;

  // Fetch the author's kind 0 event
  const [author] = await nostr.query(
    [{ kinds: [0], authors: [target.pubkey], limit: 1 }],
    { signal },
  );
  if (!author) {
    throw new Error('Author not found');
  }

  // Parse author metadata
  const { lud06, lud16 } = n.json().pipe(n.metadata()).parse(author.content);

  // Get author's LNURL
  let lnurl: LNURL | undefined;
  if (lud16) {
    lnurl = LNURL.fromLightningAddress(lud16);
  } else if (lud06) {
    lnurl = LNURL.fromString(lud06);
  }
  if (!lnurl) {
    throw new Error('No LNURL found');
  }

  // Create zap request
  const zapRequest = await signer.signEvent({
    kind: 9734,
    content: '',
    tags: [
      ['e', target.id],
      ['p', target.pubkey],
      ['amount', amount.toString()],
      ['relays', ...relays],
      ['lnurl', lnurl.toString()],
    ],
    created_at: Math.floor(Date.now() / 1000),
  });

  // Return invoice
  const { pr } = await lnurl.getInvoice({ amount, nostr: zapRequest, signal });
  return pr;
}
Soapbox