Skip to content

Commit

Permalink
Merge pull request #122 from getAlby/feat/remove-browserify
Browse files Browse the repository at this point in the history
feat: remove browserify, crypto-js and source maps
  • Loading branch information
rolznz authored Dec 7, 2023
2 parents 4334ecb + dafc1fc commit ba08367
Show file tree
Hide file tree
Showing 15 changed files with 106 additions and 54 deletions.
38 changes: 30 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,19 @@ yarn add @getalby/lightning-tools

or for use without any build tools:

```
// lightning-tools now available at window.lightningTools
<script src="https://cdn.jsdelivr.net/npm/@getalby/lightning-tools@latest/dist/index.browser.js"></script>
```html
<script type="module">
import { LightningAddress } from "https://esm.sh/@getalby/lightning-tools@5.0.0"; // jsdelivr.net, skypack.dev also work
// use LightningAddress normally...
(async () => {
const ln = new LightningAddress("hello@getalby.com");
// fetch the LNURL data
await ln.fetch();
// get the LNURL-pay data:
console.log(ln.lnurlpData);
})();
</script>
```

**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.)
Expand Down Expand Up @@ -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);
Expand All @@ -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);
Expand All @@ -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() }
);
```

Expand Down Expand Up @@ -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:

Expand Down
13 changes: 13 additions & 0 deletions examples/request-invoice.js
Original file line number Diff line number Diff line change
@@ -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
12 changes: 4 additions & 8 deletions package.json
Original file line number Diff line number Diff line change
@@ -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",
Expand Down Expand Up @@ -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",
Expand All @@ -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",
Expand Down
4 changes: 2 additions & 2 deletions setupJest.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
import jestFetchMock from 'jest-fetch-mock';
import jestFetchMock from "jest-fetch-mock";

jestFetchMock.enableMocks();
jestFetchMock.enableMocks();
11 changes: 11 additions & 0 deletions src/invoice.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
});
});
8 changes: 4 additions & 4 deletions src/invoice.ts
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -44,11 +44,11 @@ export default class Invoice {
}
}

validatePreimage(preimage: string): boolean {
async validatePreimage(preimage: string): Promise<boolean> {
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;
Expand Down
8 changes: 4 additions & 4 deletions src/lightning-address.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand All @@ -99,7 +99,7 @@ export default class LightningAddress {
nostrData = await nostrResult.json();
}

this.parseResponse(lnurlData, keysendData, nostrData);
await this.parseResponse(lnurlData, keysendData, nostrData);
}

lnurlpUrl() {
Expand Down Expand Up @@ -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);
Expand Down
5 changes: 5 additions & 0 deletions src/utils/hex.ts
Original file line number Diff line number Diff line change
@@ -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)),
);
4 changes: 2 additions & 2 deletions src/utils/lnurl.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand All @@ -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);
});
Expand Down
12 changes: 6 additions & 6 deletions src/utils/lnurl.ts
Original file line number Diff line number Diff line change
@@ -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]*))?)/;
Expand All @@ -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<LnUrlPayResponse> => {
if (data.tag !== TAG_PAY_REQUEST)
throw new Error("Invalid pay service params");

Expand All @@ -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 = "";
Expand Down
9 changes: 4 additions & 5 deletions src/utils/nostr.ts
Original file line number Diff line number Diff line change
@@ -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,
Expand Down Expand Up @@ -32,7 +31,7 @@ export async function generateZapEvent(
content: comment ?? "",
};

nostrEvent.id = getEventHash(nostrEvent);
nostrEvent.id = await getEventHash(nostrEvent);
return await nostr.signEvent(nostrEvent);
}

Expand Down Expand Up @@ -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<string> {
return sha256(serializeEvent(event));
}

export function parseNostrResponse(
Expand Down
18 changes: 18 additions & 0 deletions src/utils/sha256.ts
Original file line number Diff line number Diff line change
@@ -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;
}
3 changes: 0 additions & 3 deletions src/window.js

This file was deleted.

5 changes: 3 additions & 2 deletions tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -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/**/*"],
Expand Down
10 changes: 0 additions & 10 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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"
Expand Down

0 comments on commit ba08367

Please sign in to comment.