-
Notifications
You must be signed in to change notification settings - Fork 4
AI suggestions #25
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
AI suggestions #25
Changes from all commits
fdd7027
b353e39
a5ca2b5
c1961f8
c7c28a2
74980b8
0d07ee4
4652b49
db69040
8c5f621
14cefab
0a66386
72bca31
c37bcd0
91ef744
2e8fb34
0f9a72b
03d768c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -17,6 +17,78 @@ Use this skill to understand how to build apps that require bitcoin lightning wa | |
| - [Lightning Tools: Request invoices from a lightning address, parse BOLT-11 invoices, verify a preimage for a BOLT-11 invoice, LNURL-Verify, do bitcoin <-> fiat conversions](./references/lightning-tools/lightning-tools.md) | ||
| - [Bitcoin Connect: Browser-only UI components for connecting wallets and accepting payments in React, Vue, or pure HTML web apps](./references/bitcoin-connect/bitcoin-connect.md) | ||
|
|
||
| ## Which library to use | ||
|
|
||
| | Scenario | Library | Runtime | | ||
| |---|---|---| | ||
| | Backend / server-side / console app wallet operations (send, receive, balance, invoices, notifications) | NWC Client (`@getalby/sdk`) | Node.js, Deno, Bun, Browser | | ||
| | Browser / frontend wallet connection UI and payment modals | Bitcoin Connect (`@getalby/bitcoin-connect`) | Browser only | | ||
| | Utility: parse invoices, lightning address lookups, fiat conversion, LNURL | Lightning Tools (`@getalby/lightning-tools`) | Node.js, Deno, Bun, Browser | | ||
| | Backend + Frontend in the same app | NWC Client (backend) + Bitcoin Connect (frontend) | Both | | ||
|
|
||
| - **Do NOT use Bitcoin Connect in Node.js / server-side environments** — it requires a browser DOM. | ||
| - **Do NOT use NWC Client in the frontend if the goal is wallet connection UI** — use Bitcoin Connect instead, which provides the UI and manages the NWC connection for you. | ||
| - NWC Client and Lightning Tools can be freely combined in any environment. | ||
|
|
||
| ## ⚠️ Unit Warning | ||
|
|
||
| NWC Client operates in **millisats** (1 sat = 1,000 millisats). | ||
| Lightning Tools and Bitcoin Connect/WebLN operate in **sats**. | ||
|
|
||
| When combining libraries, always convert: | ||
| - NWC millisats → sats: `Math.floor(millisats / 1000)` | ||
| - sats → NWC millisats: `sats * 1000` | ||
|
|
||
| ## Node.js Project Setup | ||
|
|
||
| All packages in this skill are **ESM-only**. When creating a new Node.js project: | ||
|
|
||
| 1. Set `"type": "module"` in `package.json` | ||
| 2. For TypeScript, use the following minimal `tsconfig.json`: | ||
|
|
||
| ```json | ||
| { | ||
| "compilerOptions": { | ||
| "target": "ES2022", | ||
| "module": "nodenext", | ||
| "moduleResolution": "nodenext", | ||
| "esModuleInterop": true, | ||
| "outDir": "dist", | ||
| "strict": true | ||
| } | ||
| } | ||
| ``` | ||
|
|
||
| 3. Install dependencies based on what you need: | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The individual tools already have installation instructions. I don't think we should add them here, and we shouldn't specify which package manager to use |
||
|
|
||
| ```bash | ||
| # NWC Client (wallet operations) | ||
| npm install @getalby/sdk | ||
|
|
||
| # Lightning Tools (invoices, lightning addresses, fiat conversion) | ||
| npm install @getalby/lightning-tools | ||
|
|
||
| # Both (common for backend apps) | ||
| npm install @getalby/sdk @getalby/lightning-tools | ||
|
|
||
| # Bitcoin Connect (browser only — do NOT install for Node.js-only projects) | ||
| npm install @getalby/bitcoin-connect | ||
| # or for React specifically: | ||
| npm install @getalby/bitcoin-connect-react | ||
| ``` | ||
|
|
||
| 4. If using TypeScript with Bitcoin Connect, also install WebLN types: | ||
|
|
||
| ```bash | ||
| npm install -D @webbtc/webln-types | ||
| ``` | ||
|
|
||
| Then create a `webln-types.d.ts` file: | ||
|
|
||
| ```ts | ||
| /// <reference types="@webbtc/webln-types" /> | ||
| ``` | ||
|
|
||
| ## Prefer Typescript | ||
|
|
||
| When the user says to use "JS" or "Javascript" or "NodeJS" or something similar, use typescript unless the user explicitly says to not use typescript or the project does not support it. | ||
|
|
@@ -44,6 +116,46 @@ Testing wallets should be used for [automated testing](./references/automated-te | |
|
|
||
| It is recommended to write tests so that the agent can test its own work and fix bugs itself without requiring human input. | ||
|
|
||
| ## Cross-Library Recipe | ||
|
|
||
| When combining NWC Client and Lightning Tools (e.g. fiat conversion + invoicing + forwarding payments), see the [cross-library recipe](./references/cross-library-recipe.md) for a full end-to-end example with proper unit conversion. | ||
|
|
||
| ## Production Wallet | ||
|
|
||
| If they do not have a wallet yet [here are some options](./references/production-wallets.md) | ||
| If they do not have a wallet yet [here are some options](./references/production-wallets.md) | ||
|
|
||
| ## Quickstart Decision Guide | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we are duplicating a lot of info here and putting it all in the root of the skill is not so great. We already link to individual reference files and describe what each reference file is for |
||
|
|
||
| - **Backend wallet ops (send/receive/balance/notifications)** → Use `NWCClient` (`@getalby/sdk`). Combine with Lightning Tools for fiat conversion and invoice parsing. Units: msats. | ||
| - **Browser wallet connection + payment UI** → Use Bitcoin Connect (`@getalby/bitcoin-connect` or `-react`). Units: sats. SSR frameworks must gate imports to the client. | ||
| - **Full-stack app** → Backend: `NWCClient` for wallet ops. Frontend: Bitcoin Connect for connect/pay UI. Shared utils: Lightning Tools for fiat, LNURL, invoice parsing. | ||
| - **Lightning address pay/receive utilities** → Use Lightning Tools `lnurl` APIs (sats). For payment, either WebLN (browser) or `NWCClient.payInvoice` (backend). | ||
| - **Fiat pricing** → Lightning Tools `fiat` APIs to convert fiat↔sats; multiply/divide by 1000 when handing amounts to/from `NWCClient`. | ||
| - **Zaps (Nostr-tied payments)** → Lightning Tools zap helpers + WebLN provider (browser) or `NWCClient.payInvoice` (backend). | ||
| - **L402 client** → Browser: `fetchWithL402` + Bitcoin Connect provider. Node: `fetchWithL402` + `NostrWebLNProvider` from `@getalby/sdk/webln`. | ||
| - **L402 server** → `NWCClient.makeInvoice` + macaroon verification (see `lightning-tools/l402.md`). | ||
| - **Testing** → Always prefer [testing wallets](./references/testing-wallets.md) and wire them into automated tests (Jest/Vitest/Playwright) per [automated testing](./references/automated-testing.md). | ||
|
|
||
| ## NWC Secret Handling (Security) | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is mostly duplicated (already in nwc-client.md) but could also be added to bitcoin connect (the browser specific stuff) |
||
|
|
||
| - Treat `nostrWalletConnectUrl` as a secret API key. Never log, print, or expose it. | ||
| - Backend: keep in environment variables (e.g., `NWC_URL`), never commit to source control, and redact in error messages. | ||
| - Browser: request from user input; keep only in memory unless the user explicitly opts into persistence. Do not bake into bundles or HTML. | ||
| - When wrapping errors, strip or mask the connection URL before surfacing to logs/telemetry. | ||
|
|
||
| ## Invoice Safety & Common Pitfalls | ||
|
|
||
| - Always decode and check expiry before paying a BOLT-11 invoice; warn if expiry is under ~60 seconds. | ||
| - Units: `NWCClient` = **msats**; Lightning Tools/Bitcoin Connect/WebLN = **sats**. Convert carefully when mixing. | ||
| - Do not import Lightning Tools from the package root; always use subpath imports (e.g., `@getalby/lightning-tools/fiat`). | ||
| - Bitcoin Connect requires a browser DOM; never import it in SSR server code. Call `init()` exactly once on the client. | ||
| - Close resources: `unsub()` notifications and `client.close()` when shutting down long-lived processes. | ||
| - Handle permission/budget errors: on `QUOTA_EXCEEDED` or `RESTRICTED`, prompt for a new or expanded connection; on `RATE_LIMITED`, back off and retry later. | ||
|
|
||
| ## Recipe Pointers | ||
|
|
||
| - End-to-end msats↔sats with invoicing and forwarding: [cross-library recipe](./references/cross-library-recipe.md). | ||
| - L402 client/server patterns: [L402 guide](./references/lightning-tools/l402.md). | ||
| - LNURL-pay, comments, payer data, and verify: [lnurl guide](./references/lightning-tools/lnurl.md). | ||
| - Invoice parsing/expiry/preimage verification: [invoice guide](./references/lightning-tools/invoice.md). | ||
| - Automated wallet creation for tests: [testing wallets](./references/testing-wallets.md) and [automated testing](./references/automated-testing.md). | ||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -35,6 +35,76 @@ or | |||||
| </script> | ||||||
| ``` | ||||||
|
|
||||||
| ## ⚠️ SSR / SSG Warning (Next.js, Nuxt, SvelteKit, Remix, Astro) | ||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nice additional docs. I would link to a separate md file to reduce the context for non-SSR apps (progressive disclosure) |
||||||
|
|
||||||
| Bitcoin Connect requires a browser DOM and **will crash if imported on the server**. In frameworks that do server-side rendering or static site generation, you MUST use dynamic imports or client-only wrappers. Never import `@getalby/bitcoin-connect` or `@getalby/bitcoin-connect-react` in server code or shared modules that execute during SSR — gate imports to the client and dynamically load components. | ||||||
|
|
||||||
| ### Next.js (App Router) | ||||||
|
|
||||||
| Mark the component as client-only and use dynamic import: | ||||||
|
|
||||||
| ```tsx | ||||||
| "use client"; | ||||||
|
|
||||||
| import dynamic from "next/dynamic"; | ||||||
| import { useEffect } from "react"; | ||||||
|
|
||||||
| // Dynamic import — prevents server-side import of bitcoin-connect | ||||||
| const BitcoinConnectButton = dynamic( | ||||||
| () => import("@getalby/bitcoin-connect-react").then((mod) => { | ||||||
| // init() must be called once before using components | ||||||
| mod.init({ appName: "My App" }); | ||||||
| return { default: mod.Button }; | ||||||
| }), | ||||||
| { ssr: false } | ||||||
| ); | ||||||
|
|
||||||
| export default function WalletButton() { | ||||||
| return <BitcoinConnectButton />; | ||||||
| } | ||||||
| ``` | ||||||
|
|
||||||
| ### Next.js (Pages Router) | ||||||
|
|
||||||
| ```tsx | ||||||
| import dynamic from "next/dynamic"; | ||||||
|
|
||||||
| const WalletButton = dynamic( | ||||||
| () => import("../components/WalletButton"), | ||||||
| { ssr: false } | ||||||
| ); | ||||||
| ``` | ||||||
|
|
||||||
| ### Nuxt 3 | ||||||
|
|
||||||
| ```vue | ||||||
| <template> | ||||||
| <ClientOnly> | ||||||
| <BitcoinConnectButton /> | ||||||
| </ClientOnly> | ||||||
| </template> | ||||||
| ``` | ||||||
|
|
||||||
| ### SvelteKit | ||||||
|
|
||||||
| ```svelte | ||||||
| <script> | ||||||
| import { onMount } from "svelte"; | ||||||
| import { browser } from "$app/environment"; | ||||||
|
|
||||||
| let Button; | ||||||
| onMount(async () => { | ||||||
| const bc = await import("@getalby/bitcoin-connect"); | ||||||
| bc.init({ appName: "My App" }); | ||||||
| // use bc.launchModal(), bc.requestProvider(), etc. | ||||||
| }); | ||||||
| </script> | ||||||
| ``` | ||||||
|
|
||||||
| ### General Rule | ||||||
|
|
||||||
| If using any SSR framework, **never import `@getalby/bitcoin-connect` or `@getalby/bitcoin-connect-react` at the top level of a server-rendered file**. Always gate the import behind a browser/client check or dynamic import. | ||||||
|
|
||||||
| ## Key concepts | ||||||
|
|
||||||
| - Web components for connecting Lightning wallets and enabling WebLN | ||||||
|
|
@@ -49,6 +119,15 @@ Unlike NWC, WebLN operates on sats, not millisats. (1000 millisats = 1 satoshi) | |||||
|
|
||||||
| ## Initialization | ||||||
|
|
||||||
| Call `init()` **once** when your app starts. Do NOT call it multiple times or conditionally inside render loops. If you have multiple entry points/components, centralize `init()` to a single client-only location to avoid duplicate initialization. | ||||||
|
|
||||||
| **Where to call `init()`:** | ||||||
|
|
||||||
| - **React (Vite / CRA):** in `main.tsx` or `App.tsx`, outside any component, at the top level | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Use “top‑level” as a compound adjective. Change “at the top level” → “at the top‑level” for standard hyphenation in technical docs. ✏️ Proposed fix-- **React (Vite / CRA):** in `main.tsx` or `App.tsx`, outside any component, at the top level
+- **React (Vite / CRA):** in `main.tsx` or `App.tsx`, outside any component, at the top‑level📝 Committable suggestion
Suggested change
🧰 Tools🪛 LanguageTool[uncategorized] ~126-~126: If this is a compound adjective that modifies the following noun, use a hyphen. (EN_COMPOUND_ADJECTIVE_INTERNAL) 🤖 Prompt for AI Agents |
||||||
| - **React (Next.js):** inside the dynamically imported client component (see SSR warning above) | ||||||
| - **Vue:** in `main.ts` before `createApp()` | ||||||
| - **Plain HTML:** in a `<script type="module">` tag in the `<head>` or before your app code | ||||||
|
|
||||||
| ```ts | ||||||
| import { init } from "@getalby/bitcoin-connect"; | ||||||
|
|
||||||
|
|
||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,104 @@ | ||
| # Cross-Library Recipe: NWC Client + Lightning Tools | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think we need this. AI is smart enough to figure out how to combine usage of the 2 packages. If we think it is an issue maybe it's better to focus on the root problem that we have 2 separate npm packages |
||
|
|
||
| This example combines NWC Client and Lightning Tools to build a common real-world pattern: | ||
|
|
||
| > Receive a payment in USD, then forward 90% to a lightning address. | ||
|
|
||
| ## Libraries Used | ||
|
|
||
| - **NWC Client** (`@getalby/sdk`) — create invoices, subscribe to payment notifications, send payments | ||
| - **Lightning Tools** (`@getalby/lightning-tools`) — convert fiat to sats, request invoices from a lightning address | ||
|
|
||
| ## ⚠️ Unit Conversion | ||
|
|
||
| NWC Client uses **millisats**. Lightning Tools uses **sats**. This recipe converts between them: | ||
| - NWC → Lightning Tools: `Math.floor(millisats / 1000)` | ||
| - Lightning Tools → NWC: `sats * 1000` | ||
|
|
||
| ## Full Example | ||
|
|
||
| IMPORTANT: read the [NWC Client typings](./nwc-client/nwc.d.ts) and [Lightning Tools typings](./lightning-tools/index.d.ts) to better understand how this works. | ||
|
|
||
| ```ts | ||
| import { NWCClient, Nip47WalletError } from "@getalby/sdk/nwc"; | ||
| import { getSatoshiValue } from "@getalby/lightning-tools/fiat"; | ||
| import { LightningAddress } from "@getalby/lightning-tools/lnurl"; | ||
|
|
||
| const client = new NWCClient({ | ||
| nostrWalletConnectUrl: process.env.NWC_URL, | ||
| }); | ||
|
|
||
| // Step 1: Convert $5 USD to sats using Lightning Tools | ||
| const amountSats = await getSatoshiValue({ amount: 5, currency: "USD" }); | ||
| console.log(`$5 USD = ${amountSats} sats`); | ||
|
|
||
| // Step 2: Create an invoice using NWC Client (amount in millisats) | ||
| const amountMillisats = amountSats * 1000; | ||
| const transaction = await client.makeInvoice({ | ||
| amount: amountMillisats, | ||
| description: "Payment for service - $5 USD", | ||
| }); | ||
| console.log("Invoice created:", transaction.invoice); | ||
| console.log("Share this invoice with the payer."); | ||
|
|
||
| // Step 3: Wait for the payment to arrive | ||
| const unsub = await client.subscribeNotifications(async (notification) => { | ||
| if (notification.notification_type !== "payment_received") { | ||
| return; | ||
| } | ||
| if (notification.notification.payment_hash !== transaction.payment_hash) { | ||
| return; | ||
| } | ||
|
|
||
| const receivedMillisats = notification.notification.amount; | ||
| const receivedSats = Math.floor(receivedMillisats / 1000); | ||
| console.log(`Payment received: ${receivedSats} sats`); | ||
|
|
||
| // Step 4: Calculate 90% to forward | ||
| const forwardSats = Math.floor(receivedSats * 0.9); | ||
| console.log(`Forwarding 90%: ${forwardSats} sats to recipient`); | ||
|
|
||
| // Step 5: Request an invoice from a lightning address using Lightning Tools | ||
| const recipientAddress = new LightningAddress("hello@getalby.com", { | ||
| proxy: false, // server-side, no CORS proxy needed | ||
| }); | ||
| await recipientAddress.fetch(); | ||
| const recipientInvoice = await recipientAddress.requestInvoice({ | ||
| satoshi: forwardSats, | ||
| }); | ||
|
|
||
| // Step 6: Pay the invoice using NWC Client | ||
| try { | ||
| const payResponse = await client.payInvoice({ | ||
| invoice: recipientInvoice.paymentRequest, | ||
| }); | ||
| console.log("Forwarded payment! Preimage:", payResponse.preimage); | ||
| } catch (error) { | ||
| if (error instanceof Nip47WalletError) { | ||
| console.error(`Payment failed [${error.code}]: ${error.message}`); | ||
| } else { | ||
| throw error; | ||
| } | ||
| } | ||
|
|
||
| unsub(); | ||
| client.close(); | ||
| }); | ||
|
|
||
| // Graceful shutdown | ||
| process.on("SIGINT", () => { | ||
| unsub(); | ||
| client.close(); | ||
| process.exit(); | ||
| }); | ||
| ``` | ||
|
|
||
| ## Key Takeaways | ||
|
|
||
| 1. **Fiat conversion** happens via Lightning Tools (`getSatoshiValue`) which returns sats. | ||
| 2. **Invoice creation** happens via NWC Client (`makeInvoice`) which expects millisats — so multiply by 1000. | ||
| 3. **Notification amounts** from NWC Client are in millisats — divide by 1000 before passing to Lightning Tools. | ||
| 4. **Lightning address invoice requests** happen via Lightning Tools (`requestInvoice`) which expects sats. | ||
| 5. **Paying the invoice** happens via NWC Client (`payInvoice`) which takes a BOLT-11 string directly. | ||
| 6. **Error handling** uses `Nip47WalletError` for wallet-level failures. | ||
| 7. **Cleanup** always unsubscribes and closes the client. | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If this is specific to Node.js maybe it can be referenced as a separate file