Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "@getalby/cli",
"description": "CLI for Nostr Wallet Connect (NIP-47) with a few additional useful lightning tools",
"repository": "https://github.com/getAlby/cli.git",
"version": "0.10.1",
"version": "0.10.2",
"type": "module",
"main": "build/index.js",
"bin": {
Expand Down Expand Up @@ -36,7 +36,7 @@
"node": ">=20"
},
"dependencies": {
"@getalby/lightning-tools": "^9.0.1",
"@getalby/lightning-tools": "^10.0.0",
"@getalby/sdk": "^8.0.3",
"@lendasat/lendaswap-sdk-pure": "^0.2.38",
"@noble/hashes": "^2.0.1",
Expand Down
2 changes: 1 addition & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ program
" $ npx @getalby/cli pay alice@getalby.com --amount 5 --currency USD --network lightning\n" +
' $ npx @getalby/cli receive --amount 2100 --currency BTC --unit sats --network lightning --description "Coffee"',
)
.version("0.10.1")
.version("0.10.2")
.configureHelp({ showGlobalOptions: true })
.option(
"-w, --wallet-name <name>",
Expand Down
6 changes: 3 additions & 3 deletions src/test/discover-bridge.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -164,12 +164,12 @@ describe("discover l402.space bridge wrapping", () => {
protocol: "x402",
payment_network: "Lightning, Base",
},
// ...or when the index reports the rail as the raw Bitcoin CAIP-2 id
// (like x402.albylabs.com's own challenge does) instead of "Lightning".
// ...or when the index reports the rail as the raw lightning mainnet
// CAIP-2 id (as x402 `exact` lightning challenges do) instead of "Lightning".
{
url: "https://x402btc.example/api",
protocol: "x402",
payment_network: "bip122:000000000019d6689c085ae165831e93",
payment_network: "lnbtc:000000000019d6689c085ae165831e93",
},
// MPP is rail-agnostic too: an MPP service on the Lightning rail is paid
// directly, never bridged.
Expand Down
26 changes: 24 additions & 2 deletions src/test/fetch-dry-run.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,10 @@ describe("fetch --dry-run price preview", () => {
x402Version: 2,
accepts: [
{
network: "bip122:000000000019d6689c085ae165831e93",
scheme: "exact",
network: "lnbtc:000000000019d6689c085ae165831e93",
asset: "BTC",
extra: { paymentMethod: "lightning", invoice: exampleInvoice },
extra: { assetTransferMethod: "bolt11", invoice: exampleInvoice },
},
],
}),
Expand All @@ -61,6 +62,27 @@ describe("fetch --dry-run price preview", () => {
expect(result.amount_in_sats).toBe(100);
});

test("ignores invoices in x402 entries that are not exact/lnbtc", async () => {
const x402Challenge = Buffer.from(
JSON.stringify({
x402Version: 2,
accepts: [
{
network: "bip122:000000000019d6689c085ae165831e93",
asset: "BTC",
extra: { paymentMethod: "lightning", invoice: exampleInvoice },
},
],
}),
).toString("base64");
stub402({ "payment-required": x402Challenge });

const result = await dryRun402({ url: "https://x402old.example/api" });

expect(result.payment_required).toBe(true);
expect(result.amount_in_sats).toBeUndefined();
});

test("suggests the l402.space bridge when no lightning invoice is offered", async () => {
stub402({
"www-authenticate": 'Payment realm="svc", method="tempo", intent="charge"',
Expand Down
2 changes: 1 addition & 1 deletion src/test/lightning-tools.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import type { MakeInvoiceResult } from "../tools/nwc/make_invoice.js";
const exampleInvoice =
"lnbc1u1p5hlrr8dqqnp4qwmtpr4p72ms7gnq3pkfk2876y2msvl33s3840dlp6xsv2w59dpscpp55utq6s8u5407namwt4jvhgsaf9fyszppjfwyxp7qsw6cyc8vxukqsp583usez9yhmkcavvvjz8cq56v3nglh2q37xkf4ufrgwxfrfjkm54s9qyysgqcqzp2xqyz5vqgtyysw64zt9sj6kfpqnekzwc37y2uyg0xdapgxqqth4uahff0x89sjfsvukjlllasg5dn05u2uha6qcvxz2y3ye5k7958qtes4pv4ggqtnjyky";

const exampleLightningAddress = "nwc1779952113427@getalby.com";
const exampleLightningAddress = "nwc1793599674328@getalby.com";

interface ErrorOutput {
error: string;
Expand Down
10 changes: 5 additions & 5 deletions src/tools/lightning/discover.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,13 @@ function bridgeUrl(url: string): string {
const BRIDGE_FUNDED_NETWORKS = new Set(["base", "solana", "tempo"]);

// The index reports some rails as CAIP-2 chain ids; normalize the ones that
// map onto rails we handle (eip155:8453 is Base mainnet; the bip122 id is
// Bitcoin mainnet, which lightning-native x402 challenges use - e.g.
// x402.albylabs.com - and must stay unwrapped, not bridged). Testnets like
// Base Sepolia (eip155:84532 / "Base Sepolia") intentionally don't match.
// map onto rails we handle (eip155:8453 is Base mainnet; the lnbtc id is
// lightning mainnet, which x402 `exact` lightning challenges use and must stay
// unwrapped, not bridged). Testnets like Base Sepolia (eip155:84532 /
// "Base Sepolia") intentionally don't match.
const NETWORK_ALIASES: Record<string, string> = {
"eip155:8453": "base",
"bip122:000000000019d6689c085ae165831e93": "lightning",
"lnbtc:000000000019d6689c085ae165831e93": "lightning",
};

// payment_network can list several rails, e.g. "Base, Solana".
Expand Down
24 changes: 18 additions & 6 deletions src/tools/lightning/fetch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -257,8 +257,8 @@ const BOLT11_PATTERN = /ln(?:bc|tb|bcrt|tbs)[0-9a-z]+/i;
// Find the lightning invoice a 402 challenge offers. Only the headers are
// checked, matching the pay path in lightning-tools (which never reads the
// 402 body): L402 and MPP carry the invoice in WWW-Authenticate
// (invoice="lnbc..."); lightning-native x402 embeds it in the base64
// Payment-Required header as extra.invoice.
// (invoice="lnbc..."); x402 `exact` on lightning (`lnbtc` networks) embeds it
// in the base64 Payment-Required header as extra.invoice.
function extractLightningInvoice(response: Response): string | null {
const wwwAuthenticate = response.headers.get("www-authenticate") ?? "";
const headerMatch = wwwAuthenticate.match(
Expand All @@ -269,11 +269,23 @@ function extractLightningInvoice(response: Response): string | null {
const paymentRequired = response.headers.get("payment-required");
if (paymentRequired) {
try {
const decoded = Buffer.from(paymentRequired, "base64").toString("utf-8");
const match = decoded.match(BOLT11_PATTERN);
if (match) return match[0];
const decoded = JSON.parse(
Buffer.from(paymentRequired, "base64").toString("utf-8"),
) as {
accepts?: {
scheme?: string;
network?: string;
extra?: { invoice?: unknown };
}[];
};
const invoice = decoded.accepts?.find(
(e) => e?.scheme === "exact" && e.network?.startsWith("lnbtc:"),
)?.extra?.invoice;
if (typeof invoice === "string" && BOLT11_PATTERN.test(invoice)) {
return invoice;
}
} catch {
// Not base64 - no usable invoice.
// Not base64 JSON - no usable invoice.
}
}

Expand Down
10 changes: 5 additions & 5 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -73,16 +73,16 @@
dependencies:
tslib "^2.4.0"

"@getalby/lightning-tools@^10.0.0":
version "10.0.0"
resolved "https://registry.yarnpkg.com/@getalby/lightning-tools/-/lightning-tools-10.0.0.tgz#2c697febe28e208805d4f2b237724434de871937"
integrity sha512-SU4KXcASldsy4UUgDf04Qh2zLjFd0oXQUIbfdocVJy13MhzaudyanOVR+CYrRKzBJbSMgpZMdZIMM853gviuPg==

"@getalby/lightning-tools@^8.1.1":
version "8.1.1"
resolved "https://registry.yarnpkg.com/@getalby/lightning-tools/-/lightning-tools-8.1.1.tgz#4bf8be9f30dd76f7dd082262e862e63c16591fae"
integrity sha512-gB+3fvTGz9bbl31JNgKMWqG4dZWgCxSB51fYn+F4tZ3x2h5oI/yHjvb6W9GBo2Zscr5gMP64gs0LlzKYoETlvQ==

"@getalby/lightning-tools@^9.0.1":
version "9.0.1"
resolved "https://registry.yarnpkg.com/@getalby/lightning-tools/-/lightning-tools-9.0.1.tgz#8b73c001e4e3598684dab594a1eeed986d9b3d9d"
integrity sha512-pTCuJB13V8qq5pDyDSCB5NVxUaT5DmLHLg2jGINYRbU06PM+KXmHoJ3zN9C6Jk31aFil28vsdO6z7OwFhEybLA==

"@getalby/sdk@^8.0.3":
version "8.0.3"
resolved "https://registry.yarnpkg.com/@getalby/sdk/-/sdk-8.0.3.tgz#dd59ce94682c1c041fa90b42c16e959aa2d59f03"
Expand Down
Loading