diff --git a/README.md b/README.md
index 3bcc11e..b3c9061 100644
--- a/README.md
+++ b/README.md
@@ -20,9 +20,19 @@ yarn add @getalby/lightning-tools
or for use without any build tools:
-```
-// lightning-tools now available at window.lightningTools
-
+```html
+
```
**This library relies on a global `fetch()` function which will work in [browsers](https://caniuse.com/?search=fetch) and node v18 or newer.** (In older versions you have to use a polyfill.)
@@ -179,7 +189,7 @@ import { fetchWithL402 } from "@getalby/lightning-tools";
await fetchWithL402(
"https://lsat-weather-api.getalby.repl.co/kigali",
{},
- { store: window.localStorage },
+ { store: window.localStorage }
)
.then((res) => res.json())
.then(console.log);
@@ -198,7 +208,7 @@ const nwc = new webln.NostrWebLNProvider({
await fetchWithL402(
"https://lsat-weather-api.getalby.repl.co/kigali",
{},
- { webln: nwc },
+ { webln: nwc }
)
.then((res) => res.json())
.then(console.log);
@@ -211,7 +221,7 @@ import { l402 } from "@getalby/lightning-tools";
await l402.fetchWithL402(
"https://lsat-weather-api.getalby.repl.co/kigali",
{},
- { store: new l402.storage.NoStorage() },
+ { store: new l402.storage.NoStorage() }
);
```
@@ -262,13 +272,25 @@ This library uses a [proxy](https://github.com/getAlby/lightning-address-details
You can disable the proxy by explicitly setting the proxy to false when initializing a lightning address:
+```js
+const lightningAddress = new LightningAddress("hello@getalby.com", {
+ proxy: false,
+});
```
-const lightningAddress = new LightningAddress("hello@getalby.com", {proxy: false});
+
+## crypto dependency
+
+If you get an `crypto is not defined` in NodeJS error you have to import it first:
+
+```js
+import * as crypto from 'crypto'; // or 'node:crypto'
+globalThis.crypto = crypto as any;
+//or: global.crypto = require('crypto');
```
## fetch() dependency
-This library relies on a global fetch object which will work in browsers and node v18.x or newer. In old version yoi can manually install a global fetch option or polyfill if needed.
+This library relies on a global fetch object which will work in browsers and node v18.x or newer. In old version you can manually install a global fetch option or polyfill if needed.
For example:
diff --git a/examples/request-invoice.js b/examples/request-invoice.js
new file mode 100644
index 0000000..7d2f60f
--- /dev/null
+++ b/examples/request-invoice.js
@@ -0,0 +1,13 @@
+import * as crypto from "crypto"; // or 'node:crypto'
+global.crypto = crypto;
+import { LightningAddress } from "@getalby/lightning-tools";
+
+const ln = new LightningAddress("hello@getalby.com");
+
+await ln.fetch();
+// request an invoice for 1000 satoshis
+// this returns a new `Invoice` class that can also be used to validate the payment
+const invoice = await ln.requestInvoice({ satoshi: 1000 });
+
+console.log(invoice.paymentRequest); // print the payment request
+console.log(invoice.paymentHash); // print the payment hash
diff --git a/package.json b/package.json
index 97fa43d..363fd88 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "@getalby/lightning-tools",
- "version": "4.2.1",
+ "version": "5.0.0",
"description": "Collection of helpful building blocks and tools to develop Bitcoin Lightning web apps",
"type": "module",
"source": "src/index.ts",
@@ -36,21 +36,16 @@
"tsc:compile": "tsc --noEmit",
"format": "prettier --check '**/*.(md|json)' 'src/**/*.(js|ts)' 'examples/**/*.js'",
"format:fix": "prettier --loglevel silent --write '**/*.(md|json)' 'src/**/*.(js|ts)' 'examples/**/*.js'",
- "build:browser": "cp src/window.js dist && browserify dist/window.js > dist/index.browser.js",
"test": "jest",
"clean": "rm -rf dist",
- "build": "microbundle && yarn build:browser",
+ "build": "microbundle --no-sourcemap",
"dev": "microbundle watch",
"prepare": "husky install"
},
- "dependencies": {
- "crypto-js": "^4.1.1",
- "light-bolt11-decoder": "^3.0.0"
- },
+ "dependencies": {},
"devDependencies": {
"@commitlint/cli": "^17.7.1",
"@commitlint/config-conventional": "^17.7.0",
- "@types/crypto-js": "^4.1.1",
"@types/jest": "^29.4.0",
"@types/node": "^20.8.2",
"@typescript-eslint/eslint-plugin": "^6.4.0",
@@ -62,6 +57,7 @@
"husky": "^8.0.3",
"jest": "^29.5.0",
"jest-fetch-mock": "^3.0.3",
+ "light-bolt11-decoder": "^3.0.0",
"lint-staged": "^14.0.0",
"microbundle": "^0.15.1",
"nostr-tools": "^1.17.0",
diff --git a/setupJest.ts b/setupJest.ts
index 79e0bb2..1d5b38c 100644
--- a/setupJest.ts
+++ b/setupJest.ts
@@ -1,3 +1,3 @@
-import jestFetchMock from 'jest-fetch-mock';
+import jestFetchMock from "jest-fetch-mock";
-jestFetchMock.enableMocks();
\ No newline at end of file
+jestFetchMock.enableMocks();
diff --git a/src/invoice.test.ts b/src/invoice.test.ts
index b20d9a8..adbc249 100644
--- a/src/invoice.test.ts
+++ b/src/invoice.test.ts
@@ -27,4 +27,15 @@ describe("Invoice", () => {
const decodedInvoice = new Invoice({ pr: paymentRequestWithMemo });
expect(decodedInvoice.description).toBe("Test memo");
});
+
+ test("validate preimage", async () => {
+ const decodedInvoice = new Invoice({
+ pr: "lnbc120n1p3ecwp5pp5z8n0tzytydn57x6q0kqgfearewkx6kdh90svrkrc64azwy9jpnfqdq4f35kw6r5wdshgueqw35hqcqzpgxqyz5vqsp535pwwk083jvpnf87nl3mr4ext8q5f576s57cds72nvu7fpr037nq9qyyssqtq40wszjzs0vpaka2uckjf4xs2fu24f4vp9eev8r230m6epcp2kxdg8xztlw89p2kzkdpadujuflv6f8avgw3jhnvcxjkegdtydd95sp8hwns5",
+ });
+ expect(
+ await decodedInvoice.validatePreimage(
+ "dedbef581d83342848d99c02519053f01856add237f94437bc9bbec7bd6f6e55",
+ ),
+ ).toBe(true);
+ });
});
diff --git a/src/invoice.ts b/src/invoice.ts
index 8ad900f..ae8e4dc 100644
--- a/src/invoice.ts
+++ b/src/invoice.ts
@@ -1,7 +1,7 @@
import { decodeInvoice } from "./utils/invoice";
-import Hex from "crypto-js/enc-hex.js";
-import sha256 from "crypto-js/sha256.js";
import { InvoiceArgs } from "./types";
+import { sha256 } from "./utils/sha256";
+import { fromHexString } from "./utils/hex";
export default class Invoice {
paymentRequest: string;
@@ -44,11 +44,11 @@ export default class Invoice {
}
}
- validatePreimage(preimage: string): boolean {
+ async validatePreimage(preimage: string): Promise {
if (!preimage || !this.paymentHash) return false;
try {
- const preimageHash = sha256(Hex.parse(preimage)).toString(Hex);
+ const preimageHash = await sha256(fromHexString(preimage));
return this.paymentHash === preimageHash;
} catch {
return false;
diff --git a/src/lightning-address.ts b/src/lightning-address.ts
index 3d019c4..3409af7 100644
--- a/src/lightning-address.ts
+++ b/src/lightning-address.ts
@@ -75,7 +75,7 @@ export default class LightningAddress {
);
const json = await result.json();
- this.parseResponse(json.lnurlp, json.keysend, json.nostr);
+ await this.parseResponse(json.lnurlp, json.keysend, json.nostr);
}
async fetchWithoutProxy() {
@@ -99,7 +99,7 @@ export default class LightningAddress {
nostrData = await nostrResult.json();
}
- this.parseResponse(lnurlData, keysendData, nostrData);
+ await this.parseResponse(lnurlData, keysendData, nostrData);
}
lnurlpUrl() {
@@ -249,13 +249,13 @@ export default class LightningAddress {
return response;
}
- private parseResponse(
+ private async parseResponse(
lnurlpData: LnUrlRawData | undefined,
keysendData: KeySendRawData | undefined,
nostrData: NostrResponse | undefined,
) {
if (lnurlpData) {
- this.lnurlpData = parseLnUrlPayResponse(lnurlpData);
+ this.lnurlpData = await parseLnUrlPayResponse(lnurlpData);
}
if (keysendData) {
this.keysendData = parseKeysendResponse(keysendData);
diff --git a/src/utils/hex.ts b/src/utils/hex.ts
new file mode 100644
index 0000000..2151dd1
--- /dev/null
+++ b/src/utils/hex.ts
@@ -0,0 +1,5 @@
+// from https://stackoverflow.com/a/50868276
+export const fromHexString = (hexString: string) =>
+ Uint8Array.from(
+ hexString.match(/.{1,2}/g)!.map((byte) => parseInt(byte, 16)),
+ );
diff --git a/src/utils/lnurl.test.ts b/src/utils/lnurl.test.ts
index ca94013..632a2eb 100644
--- a/src/utils/lnurl.test.ts
+++ b/src/utils/lnurl.test.ts
@@ -27,7 +27,7 @@ describe("isUrl", () => {
});
describe("parseLnUrlPayResponse", () => {
- test("min/max must be in millisats", () => {
+ test("min/max must be in millisats", async () => {
const response = {
status: "OK",
tag: "payRequest",
@@ -42,7 +42,7 @@ describe("parseLnUrlPayResponse", () => {
"79f00d3f5a19ec806189fcab03c1be4ff81d18ee4f653c88fac41fe03570f432",
allowsNostr: true,
};
- const parsed = parseLnUrlPayResponse(response);
+ const parsed = await parseLnUrlPayResponse(response);
expect(parsed.min).toBe(1000);
expect(parsed.max).toBe(11000000000);
});
diff --git a/src/utils/lnurl.ts b/src/utils/lnurl.ts
index 47e5b91..0fcc2f2 100644
--- a/src/utils/lnurl.ts
+++ b/src/utils/lnurl.ts
@@ -1,11 +1,9 @@
-import Hex from "crypto-js/enc-hex.js";
-import sha256 from "crypto-js/sha256.js";
-
import type {
LUD18ServicePayerData,
LnUrlPayResponse,
LnUrlRawData,
} from "../types";
+import { sha256 } from "./sha256";
const URL_REGEX =
/((([A-Za-z]{3,9}:(?:\/\/)?)(?:[-;:&=+$,\w]+@)?[A-Za-z0-9.-]+|(?:www.|[-;:&=+$,\w]+@)[A-Za-z0-9.-]+)((?:\/[+~%/.\w-_]*)?\??(?:[-+=&;%@.\w_]*)#?(?:[\w]*))?)/;
@@ -30,7 +28,9 @@ export const isValidAmount = ({
const TAG_PAY_REQUEST = "payRequest";
// From: https://github.com/dolcalmi/lnurl-pay/blob/main/src/request-pay-service-params.ts
-export const parseLnUrlPayResponse = (data: LnUrlRawData): LnUrlPayResponse => {
+export const parseLnUrlPayResponse = async (
+ data: LnUrlRawData,
+): Promise => {
if (data.tag !== TAG_PAY_REQUEST)
throw new Error("Invalid pay service params");
@@ -45,10 +45,10 @@ export const parseLnUrlPayResponse = (data: LnUrlRawData): LnUrlPayResponse => {
let metadataHash: string;
try {
metadata = JSON.parse(data.metadata + "");
- metadataHash = sha256(data.metadata + "").toString(Hex);
+ metadataHash = await sha256(data.metadata + "");
} catch {
metadata = [];
- metadataHash = sha256("[]").toString(Hex);
+ metadataHash = await sha256("[]");
}
let image = "";
diff --git a/src/utils/nostr.ts b/src/utils/nostr.ts
index f806b60..97cf0e7 100644
--- a/src/utils/nostr.ts
+++ b/src/utils/nostr.ts
@@ -1,6 +1,5 @@
-import Hex from "crypto-js/enc-hex.js";
-import sha256 from "crypto-js/sha256.js";
import { Event, NostrResponse, ZapArgs, ZapOptions } from "../types";
+import { sha256 } from "./sha256";
export async function generateZapEvent(
{ satoshi, comment, p, e, relays }: ZapArgs,
@@ -32,7 +31,7 @@ export async function generateZapEvent(
content: comment ?? "",
};
- nostrEvent.id = getEventHash(nostrEvent);
+ nostrEvent.id = await getEventHash(nostrEvent);
return await nostr.signEvent(nostrEvent);
}
@@ -69,8 +68,8 @@ export function serializeEvent(evt: Event): string {
]);
}
-export function getEventHash(event: Event): string {
- return sha256(serializeEvent(event)).toString(Hex);
+export function getEventHash(event: Event): Promise {
+ return sha256(serializeEvent(event));
}
export function parseNostrResponse(
diff --git a/src/utils/sha256.ts b/src/utils/sha256.ts
new file mode 100644
index 0000000..83c9753
--- /dev/null
+++ b/src/utils/sha256.ts
@@ -0,0 +1,18 @@
+// from https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/digest
+export async function sha256(message: string | Uint8Array) {
+ // encode as UTF-8
+ const msgBuffer =
+ typeof message === "string" ? new TextEncoder().encode(message) : message;
+
+ // hash the message
+ const hashBuffer = await crypto.subtle.digest("SHA-256", msgBuffer);
+
+ // convert ArrayBuffer to Array
+ const hashArray = Array.from(new Uint8Array(hashBuffer));
+
+ // convert bytes to hex string
+ const hashHex = hashArray
+ .map((b) => b.toString(16).padStart(2, "0"))
+ .join("");
+ return hashHex;
+}
diff --git a/src/window.js b/src/window.js
deleted file mode 100644
index 915186d..0000000
--- a/src/window.js
+++ /dev/null
@@ -1,3 +0,0 @@
-// assign alby-tools exports to global window object (for index.browser.js)
-// @ts-ignore this file is created at build time
-window["lightningTools"] = require("./index.cjs");
diff --git a/tsconfig.json b/tsconfig.json
index 576273a..0bd1179 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -6,13 +6,14 @@
"skipLibCheck": true,
"checkJs": true,
"allowJs": true,
- "declarationMap": true,
+ "declarationMap": false,
"declaration": true,
"allowSyntheticDefaultImports": true,
"target": "es2020",
"rootDir": "./src",
+ "module": "ESNext",
"moduleResolution": "node",
- "sourceMap": true,
+ "sourceMap": false,
"noImplicitAny": false
},
"include": ["src/**/*"],
diff --git a/yarn.lock b/yarn.lock
index 0d94405..1dcf19d 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1899,11 +1899,6 @@
dependencies:
"@babel/types" "^7.3.0"
-"@types/crypto-js@^4.1.1":
- version "4.1.1"
- resolved "https://registry.yarnpkg.com/@types/crypto-js/-/crypto-js-4.1.1.tgz#602859584cecc91894eb23a4892f38cfa927890d"
- integrity sha512-BG7fQKZ689HIoc5h+6D2Dgq1fABRa0RbBWKBd9SP/MVRVXROflpm5fhwyATX5duFmbStzyzyycPB8qUYKDH3NA==
-
"@types/estree@*":
version "1.0.0"
resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.0.tgz#5fb2e536c1ae9bf35366eed879e827fa59ca41c2"
@@ -3066,11 +3061,6 @@ crypto-browserify@^3.0.0:
randombytes "^2.0.0"
randomfill "^1.0.3"
-crypto-js@^4.1.1:
- version "4.2.0"
- resolved "https://registry.yarnpkg.com/crypto-js/-/crypto-js-4.2.0.tgz#4d931639ecdfd12ff80e8186dba6af2c2e856631"
- integrity sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==
-
css-declaration-sorter@^6.3.0:
version "6.3.1"
resolved "https://registry.yarnpkg.com/css-declaration-sorter/-/css-declaration-sorter-6.3.1.tgz#be5e1d71b7a992433fb1c542c7a1b835e45682ec"