Skip to content
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

feat: remove browserify, crypto-js and source maps #122

Merged
merged 4 commits into from
Dec 7, 2023
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
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);
reneaaron marked this conversation as resolved.
Show resolved Hide resolved

// 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