+
{stateIcon(a.state)}
{a.amount.toLocaleString()} sats
diff --git a/packages/app/src/Pages/messages.js b/packages/app/src/Pages/messages.js
deleted file mode 100644
index 87a38ad9..00000000
--- a/packages/app/src/Pages/messages.js
+++ /dev/null
@@ -1,8 +0,0 @@
-import { defineMessages } from "react-intl";
-import { addIdAndDefaultMessageToMessages } from "Util";
-
-const messages = defineMessages({
- Login: "Login",
-});
-
-export default addIdAndDefaultMessageToMessages(messages, "Pages");
diff --git a/packages/app/src/Wallet/LNDHub.ts b/packages/app/src/Wallet/LNDHub.ts
index 0a35dd5a..76465b40 100644
--- a/packages/app/src/Wallet/LNDHub.ts
+++ b/packages/app/src/Wallet/LNDHub.ts
@@ -1,3 +1,5 @@
+import { EventPublisher } from "Feed/EventPublisher";
+import EventKind from "Nostr/EventKind";
import {
InvoiceRequest,
LNWallet,
@@ -15,20 +17,35 @@ const defaultHeaders = {
};
export default class LNDHubWallet implements LNWallet {
+ type: "lndhub" | "snort";
url: string;
user: string;
password: string;
auth?: AuthResponse;
+ publisher?: EventPublisher;
- constructor(url: string) {
- const regex = /^lndhub:\/\/([\S-]+):([\S-]+)@(.*)$/i;
- const parsedUrl = url.match(regex);
- if (!parsedUrl || parsedUrl.length !== 4) {
- throw new Error("Invalid LNDHUB config");
+ constructor(url: string, publisher?: EventPublisher) {
+ if (url.startsWith("lndhub://")) {
+ const regex = /^lndhub:\/\/([\S-]+):([\S-]+)@(.*)$/i;
+ const parsedUrl = url.match(regex);
+ console.debug(parsedUrl);
+ if (!parsedUrl || parsedUrl.length !== 4) {
+ throw new Error("Invalid LNDHUB config");
+ }
+ this.url = new URL(parsedUrl[3]).toString();
+ this.user = parsedUrl[1];
+ this.password = parsedUrl[2];
+ this.type = "lndhub";
+ } else if (url.startsWith("snort://")) {
+ const u = new URL(url);
+ this.url = `https://${u.host}${u.pathname}`;
+ this.user = "";
+ this.password = "";
+ this.type = "snort";
+ this.publisher = publisher;
+ } else {
+ throw new Error("Invalid config");
}
- this.url = new URL(parsedUrl[3]).toString();
- this.user = parsedUrl[1];
- this.password = parsedUrl[2];
}
async createAccount() {
@@ -40,6 +57,8 @@ export default class LNDHubWallet implements LNWallet {
}
async login() {
+ if (this.type === "snort") return true;
+
const rsp = await this.getJson
("POST", "/auth?type=auth", {
login: this.user,
password: this.password,
@@ -53,31 +72,56 @@ export default class LNDHubWallet implements LNWallet {
}
async getBalance(): Promise {
- let rsp = await this.getJson("GET", "/balance");
+ const rsp = await this.getJson("GET", "/balance");
if ("error" in rsp) {
return rsp as WalletError;
}
- let bal = Math.floor((rsp as GetBalanceResponse).BTC.AvailableBalance);
+ const bal = Math.floor((rsp as GetBalanceResponse).BTC.AvailableBalance);
return bal as Sats;
}
async createInvoice(req: InvoiceRequest) {
- return Promise.resolve(UnknownWalletError);
- }
-
- async payInvoice(pr: string) {
- return Promise.resolve(UnknownWalletError);
- }
-
- async getInvoices(): Promise {
- let rsp = await this.getJson(
- "GET",
- "/getuserinvoices"
- );
+ const rsp = await this.getJson("POST", "/addinvoice", {
+ amt: req.amount,
+ memo: req.memo,
+ });
if ("error" in rsp) {
return rsp as WalletError;
}
- return (rsp as GetUserInvoicesResponse[]).map((a) => {
+
+ const pRsp = rsp as UserInvoicesResponse;
+ return {
+ pr: pRsp.payment_request,
+ memo: req.memo,
+ amount: req.amount,
+ paymentHash: pRsp.payment_hash,
+ timestamp: pRsp.timestamp,
+ } as WalletInvoice;
+ }
+
+ async payInvoice(pr: string) {
+ const rsp = await this.getJson("POST", "/payinvoice", {
+ invoice: pr,
+ });
+
+ if ("error" in rsp) {
+ return rsp as WalletError;
+ }
+
+ const pRsp = rsp as PayInvoiceResponse;
+ return {
+ pr: pr,
+ paymentHash: pRsp.payment_hash,
+ state: pRsp.payment_error === undefined ? WalletInvoiceState.Paid : WalletInvoiceState.Pending,
+ } as WalletInvoice;
+ }
+
+ async getInvoices(): Promise {
+ const rsp = await this.getJson("GET", "/getuserinvoices");
+ if ("error" in rsp) {
+ return rsp as WalletError;
+ }
+ return (rsp as UserInvoicesResponse[]).map(a => {
return {
memo: a.description,
amount: Math.floor(a.amt),
@@ -89,17 +133,18 @@ export default class LNDHubWallet implements LNWallet {
});
}
- private async getJson(
- method: "GET" | "POST",
- path: string,
- body?: any
- ): Promise {
+ private async getJson(method: "GET" | "POST", path: string, body?: any): Promise {
+ let auth = `Bearer ${this.auth?.access_token}`;
+ if (this.type === "snort") {
+ const ev = await this.publisher?.generic(`${new URL(this.url).pathname}${path}`, EventKind.Ephemeral);
+ auth = JSON.stringify(ev?.ToObject());
+ }
const rsp = await fetch(`${this.url}${path}`, {
method: method,
body: body ? JSON.stringify(body) : undefined,
headers: {
...defaultHeaders,
- Authorization: `Bearer ${this.auth?.access_token}`,
+ Authorization: auth,
},
});
const json = await rsp.json();
@@ -122,7 +167,7 @@ interface GetBalanceResponse {
};
}
-interface GetUserInvoicesResponse {
+interface UserInvoicesResponse {
amt: number;
description: string;
ispaid: boolean;
@@ -131,4 +176,12 @@ interface GetUserInvoicesResponse {
pay_req: string;
payment_hash: string;
payment_request: string;
+ r_hash: string;
+}
+
+interface PayInvoiceResponse {
+ payment_error?: string;
+ payment_hash: string;
+ payment_preimage: string;
+ payment_route?: { total_amt: number; total_fees: number };
}
diff --git a/packages/app/src/Wallet/index.ts b/packages/app/src/Wallet/index.ts
index 101d3097..84f87a91 100644
--- a/packages/app/src/Wallet/index.ts
+++ b/packages/app/src/Wallet/index.ts
@@ -1,3 +1,5 @@
+import useEventPublisher, { EventPublisher } from "Feed/EventPublisher";
+import { useEffect, useState } from "react";
import LNDHubWallet from "./LNDHub";
export enum WalletErrorCode {
@@ -74,8 +76,26 @@ export interface LNWallet {
getInvoices: () => Promise;
}
-export async function openWallet(config: string) {
- let wallet = new LNDHubWallet(config);
+export async function openWallet(config: string, publisher?: EventPublisher) {
+ const wallet = new LNDHubWallet(config, publisher);
await wallet.login();
return wallet;
}
+
+export function useWallet() {
+ const [wallet, setWallet] = useState();
+ const publisher = useEventPublisher();
+
+ useEffect(() => {
+ if (publisher) {
+ const cfg = window.localStorage.getItem("wallet-lndhub");
+ if (cfg) {
+ openWallet(cfg, publisher)
+ .then(a => setWallet(a))
+ .catch(console.error);
+ }
+ }
+ }, [publisher]);
+
+ return wallet;
+}
diff --git a/packages/app/src/index.tsx b/packages/app/src/index.tsx
index f15f79c5..f5bb266d 100644
--- a/packages/app/src/index.tsx
+++ b/packages/app/src/index.tsx
@@ -26,10 +26,9 @@ import HashTagsPage from "Pages/HashTagsPage";
import SearchPage from "Pages/SearchPage";
import HelpPage from "Pages/HelpPage";
import { NewUserRoutes } from "Pages/new";
-import NostrLinkHandler from "Pages/NostrLinkHandler";
-import { IntlProvider } from "./IntlProvider";
-import { unwrap } from "Util";
import { WalletRoutes } from "Pages/WalletPage";
+import NostrLinkHandler from "Pages/NostrLinkHandler";
+import { unwrap } from "Util";
/**
* HTTP query provider
diff --git a/packages/nostr/src/legacy/EventKind.ts b/packages/nostr/src/legacy/EventKind.ts
index 2ab6297d..5b4bb0d2 100644
--- a/packages/nostr/src/legacy/EventKind.ts
+++ b/packages/nostr/src/legacy/EventKind.ts
@@ -9,6 +9,7 @@ enum EventKind {
Repost = 6, // NIP-18
Reaction = 7, // NIP-25
Relays = 10002, // NIP-65
+ Ephemeral = 20_000,
Auth = 22242, // NIP-42
PubkeyLists = 30000, // NIP-51a
NoteLists = 30001, // NIP-51b