From 0c7d65a03053f08c2fa4bdb2e9125743e49d7069 Mon Sep 17 00:00:00 2001 From: Roland Bewick Date: Tue, 14 Nov 2023 20:07:25 +0700 Subject: [PATCH 1/3] feat: add decode invoice function --- package.json | 5 +- src/decodeInvoice.test.ts | 32 +++++++++++++ src/decodeInvoice.ts | 13 +++++ src/index.ts | 1 + src/light-bolt11-decoder.d.ts | 90 +++++++++++++++++++++++++++++++++++ yarn.lock | 7 +++ 6 files changed, 146 insertions(+), 2 deletions(-) create mode 100644 src/decodeInvoice.test.ts create mode 100644 src/decodeInvoice.ts create mode 100644 src/light-bolt11-decoder.d.ts diff --git a/package.json b/package.json index 8d98310..2a638cc 100644 --- a/package.json +++ b/package.json @@ -40,8 +40,9 @@ }, "dependencies": { "crypto-js": "^4.1.1", - "nostr-tools": "1.13.1", - "events": "^3.3.0" + "events": "^3.3.0", + "light-bolt11-decoder": "^3.0.0", + "nostr-tools": "1.13.1" }, "devDependencies": { "@commitlint/cli": "^17.7.1", diff --git a/src/decodeInvoice.test.ts b/src/decodeInvoice.test.ts new file mode 100644 index 0000000..7d0857c --- /dev/null +++ b/src/decodeInvoice.test.ts @@ -0,0 +1,32 @@ +import { decodeInvoice } from "./decodeInvoice"; + +const invoice = + "lnbc10n1pj4xmazpp5ns890al37jpreen4rlpl6fsw2hlp9n9hm0ts4dvwvcxq8atf4v6qhp50kncf9zk35xg4lxewt4974ry6mudygsztsz8qn3ar8pn3mtpe50scqzzsxqyz5vqsp5k508kdmvfpuac6lvn9wumr9x4mcpnh2t6jyp5kkxcjhueq4xjxqq9qyyssq0m88mwgknhkqfsa9u8e9dp8v93xlm0lqggslzj8mpsnx3mdzm8z5k9ns7g299pfm9zwm4crs00a364cmpraxr54jw5cf2qx9vycucggqz2ggul"; + +describe("decodeInvoice", () => { + test("get amount from invoice", () => { + const decodedInvoice = decodeInvoice(invoice); + const amountSection = decodedInvoice.sections.find( + (section) => section.name === "amount", + ); + if (amountSection?.name !== "amount") { + throw new Error("did not find amount section"); + } + const value = amountSection.value as string; + expect(value).toEqual("1000"); + }); + + test("get payment hash from invoice", () => { + const decodedInvoice = decodeInvoice(invoice); + const paymentHashSection = decodedInvoice.sections.find( + (section) => section.name === "payment_hash", + ); + if (paymentHashSection?.name !== "payment_hash") { + throw new Error("did not find payment_hash section"); + } + const value = paymentHashSection.value as string; + expect(value).toEqual( + "9c0e57f7f1f4823ce6751fc3fd260e55fe12ccb7dbd70ab58e660c03f569ab34", + ); + }); +}); diff --git a/src/decodeInvoice.ts b/src/decodeInvoice.ts new file mode 100644 index 0000000..246868e --- /dev/null +++ b/src/decodeInvoice.ts @@ -0,0 +1,13 @@ +import { decode } from "light-bolt11-decoder"; +export function decodeInvoice(invoice: string) { + if (!invoice) { + throw new Error("No invoice provided"); + } + + try { + return decode(invoice); + } catch (error) { + console.error("Failed to decode invoice", error); + throw error; + } +} diff --git a/src/index.ts b/src/index.ts index f8e7ed0..85f53c2 100644 --- a/src/index.ts +++ b/src/index.ts @@ -2,3 +2,4 @@ export * as auth from "./auth"; export * as types from "./types"; export * as webln from "./webln"; export { Client } from "./client"; +export * from "./decodeInvoice"; diff --git a/src/light-bolt11-decoder.d.ts b/src/light-bolt11-decoder.d.ts new file mode 100644 index 0000000..2a908dc --- /dev/null +++ b/src/light-bolt11-decoder.d.ts @@ -0,0 +1,90 @@ +// TODO: submit PR to https://github.com/nbd-wtf/light-bolt11-decoder +declare module "light-bolt11-decoder" { + type NetworkSection = { + name: string; + letters: string; + value?: { + bech32: string; + pubKeyHash: number; + scriptHash: number; + validWitnessVersions: number[]; + }; + }; + + type FeatureBits = { + option_data_loss_protect: string; + initial_routing_sync: string; + option_upfront_shutdown_script: string; + gossip_queries: string; + var_onion_optin: string; + gossip_queries_ex: string; + option_static_remotekey: string; + payment_secret: string; + basic_mpp: string; + option_support_large_channel: string; + extra_bits: { + start_bit: number; + bits: unknown[]; + has_required: boolean; + }; + }; + + type RouteHint = { + pubkey: string; + short_channel_id: string; + fee_base_msat: number; + fee_proportional_millionths: number; + cltv_expiry_delta: number; + }; + + type RouteHintSection = { + name: "route_hint"; + tag: "r"; + letters: string; + value: RouteHint[]; + }; + + type FeatureBitsSection = { + name: "feature_bits"; + tag: "9"; + letters: string; + value: FeatureBits; + }; + + type Section = + | { name: "paymentRequest"; value: string } + | { name: "expiry"; value: number } + | { name: "checksum"; letters: string } + | NetworkSection + | { name: "amount"; letters: string; value: string } + | { name: "separator"; letters: string } + | { name: "timestamp"; letters: string; value: number } + | { name: "payment_hash"; tag: "p"; letters: string; value: string } + | { name: "description"; tag: "d"; letters: string; value: string } + | { name: "payment_secret"; tag: "s"; letters: string; value: string } + | { + name: "min_final_cltv_expiry"; + tag: "c"; + letters: string; + value: number; + } + | FeatureBitsSection + | RouteHintSection + | { name: "signature"; letters: string; value: string }; + + type PaymentJSON = { + paymentRequest: string; + sections: Section[]; + expiry: number; + route_hints: RouteHint[][]; + }; + + type DecodedInvoice = { + paymentRequest: string; + sections: Section[]; + expiry: number; + route_hints: RouteHint[][]; + }; + + function decode(invoice: string): DecodedInvoice; +} diff --git a/yarn.lock b/yarn.lock index 8d3718f..8a70ac1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5412,6 +5412,13 @@ levn@^0.4.1: prelude-ls "^1.2.1" type-check "~0.4.0" +light-bolt11-decoder@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/light-bolt11-decoder/-/light-bolt11-decoder-3.0.0.tgz#f644576120426c9ef65621bde254f11016055044" + integrity sha512-AKvOigD2pmC8ktnn2TIqdJu0K0qk6ukUmTvHwF3JNkm8uWCqt18Ijn33A/a7gaRZ4PghJ59X+8+MXrzLKdBTmQ== + dependencies: + "@scure/base" "1.1.1" + lilconfig@2.1.0, lilconfig@^2.0.3, lilconfig@^2.0.5: version "2.1.0" resolved "https://registry.yarnpkg.com/lilconfig/-/lilconfig-2.1.0.tgz#78e23ac89ebb7e1bfbf25b18043de756548e7f52" From 80254fdfdbdae29379c81982b3b568c3a5816c26 Mon Sep 17 00:00:00 2001 From: Roland <33993199+rolznz@users.noreply.github.com> Date: Tue, 14 Nov 2023 20:29:59 +0700 Subject: [PATCH 2/3] chore: update TODO in light-bolt11-decoder.d.ts --- src/light-bolt11-decoder.d.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/light-bolt11-decoder.d.ts b/src/light-bolt11-decoder.d.ts index 2a908dc..087aad5 100644 --- a/src/light-bolt11-decoder.d.ts +++ b/src/light-bolt11-decoder.d.ts @@ -1,4 +1,4 @@ -// TODO: submit PR to https://github.com/nbd-wtf/light-bolt11-decoder +// TODO: remove when https://github.com/nbd-wtf/light-bolt11-decoder/pull/4 is merged declare module "light-bolt11-decoder" { type NetworkSection = { name: string; From 152eb1c7a32da2696c53be75bcbb7d89487e66aa Mon Sep 17 00:00:00 2001 From: Roland Bewick Date: Wed, 15 Nov 2023 11:47:11 +0700 Subject: [PATCH 3/3] fix: network section name type --- src/decodeInvoice.test.ts | 4 ++-- src/light-bolt11-decoder.d.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/decodeInvoice.test.ts b/src/decodeInvoice.test.ts index 7d0857c..487d006 100644 --- a/src/decodeInvoice.test.ts +++ b/src/decodeInvoice.test.ts @@ -12,7 +12,7 @@ describe("decodeInvoice", () => { if (amountSection?.name !== "amount") { throw new Error("did not find amount section"); } - const value = amountSection.value as string; + const value = amountSection.value; expect(value).toEqual("1000"); }); @@ -24,7 +24,7 @@ describe("decodeInvoice", () => { if (paymentHashSection?.name !== "payment_hash") { throw new Error("did not find payment_hash section"); } - const value = paymentHashSection.value as string; + const value = paymentHashSection.value; expect(value).toEqual( "9c0e57f7f1f4823ce6751fc3fd260e55fe12ccb7dbd70ab58e660c03f569ab34", ); diff --git a/src/light-bolt11-decoder.d.ts b/src/light-bolt11-decoder.d.ts index 2a908dc..2ea53ad 100644 --- a/src/light-bolt11-decoder.d.ts +++ b/src/light-bolt11-decoder.d.ts @@ -1,7 +1,7 @@ // TODO: submit PR to https://github.com/nbd-wtf/light-bolt11-decoder declare module "light-bolt11-decoder" { type NetworkSection = { - name: string; + name: "coin_network"; letters: string; value?: { bech32: string;