Skip to content

@snort/wallet Examples

Real-world usage of LNWallet, Zapper, loadWallet, and wallet types from the Snort app.

WalletStore — managing wallet lifecycle

typescript
// Wallet/index.ts
export class WalletStore extends ExternalStore<WalletStoreSnapshot> {
  async #activateWallet(cfg: WalletConfig) {
    const w = await loadWallet(cfg.kind, cfg.data)  // factory: creates wallet by kind
    if (w) {
      w.on("change", d => this.#onWalletChange(cfg, d))
    }
    return w
  }
}

// Auto-detect WebLN
export function setupWebLNWalletConfig(store: WalletStore) {
  const existing = wallets.find(a => a.kind === WalletKind.WebLN)
  if (window.webln && !existing) {
    store.add({ id: "webln", kind: WalletKind.WebLN, active: wallets.length === 0, info: { alias: "WebLN" } })
  }
}

LNDHub connection UI

typescript
// LNDHub.tsx
async function tryConnect(config: string) {
  const connection = new LNDHubWallet(config)
  await connection.login()
  const info = await connection.getInfo()

  const newWallet = {
    id: uuid(),
    kind: WalletKind.LNDHub,
    active: true,
    info,
    data: config,
  } as WalletConfig
  Wallets.add(newWallet)
}

ZapModal — full zap flow with Zapper

typescript
// ZapModal.tsx
const [zapper, setZapper] = useState<Zapper>()
const [invoice, setInvoice] = useState<Array<ZapTargetResult>>()

useEffect(() => {
  if (props.targets && props.show) {
    const zapper = new Zapper(system, publisher)
    zapper.load(props.targets).then(() => {
      setZapper(zapper)
    })
  }
}, [props.targets, props.show, system, publisher])

// On user confirm:
const sends = await zapper.send(wallet, targetsWithComments, p.amount)
if (sends[0].error) {
  setError(sends[0].error.message)
} else if (sends.every(a => a.paid)) {
  setSuccess({})    // all paid immediately
} else {
  setInvoice(sends) // show invoice QR for manual payment
}

Fast zap with barrierQueue

typescript
// FooterZapButton.tsx
const fastZapInner = useCallback(async (targets: Array<ZapTarget>, amount: number) => {
  if (wallet) {
    await barrierQueue(ZapperQueue, async () => {
      const zapper = new Zapper(system, publisher)
      const result = await zapper.send(wallet, targets, amount)
      const totalSent = result.reduce((acc, v) => acc + v.sent, 0)
      if (totalSent > 0) {
        ZapPoolController?.allocate(totalSent)
      }
    })
  }
}, [wallet, system, publisher])

// Getting zap targets from event:
const getZapTarget = useCallback((): Array<ZapTarget> | undefined => {
  if (ev.tags.some(v => v[0] === "zap")) {
    return Zapper.fromEvent(ev)
  }
  return [{ type: "lnurl", value: author?.lud16 || author?.lud06, weight: 1, zap: { pubkey: ev.pubkey, event: link } }]
}, [ev, author, link])

Wallet capability checks in the UI

typescript
// wallet/index.tsx
wallet?.canCreateInvoice()   // Show receive button
wallet?.canPayInvoice()      // Show send button
wallet?.isReady()            // Show unlock UI

// Check payment state
if (result.state === WalletInvoiceState.Paid) {
  Toastore.push({ element: `Sent ${amtSend} sats...`, expire: unixNow() + 10, icon: "zap" })
}