From 167f1c5e65bd73842b7d1f6d46522267454a9375 Mon Sep 17 00:00:00 2001 From: Kieran Date: Wed, 17 May 2023 16:30:23 +0100 Subject: [PATCH] feat: improve error reporting for wallet payments --- packages/app/src/Toaster.tsx | 7 +++-- packages/app/src/Wallet/LNDHub.ts | 2 +- packages/app/src/Wallet/NostrWalletConnect.ts | 30 +++++++++++++++++-- packages/app/src/ZapPoolController.ts | 13 +++++++- 4 files changed, 46 insertions(+), 6 deletions(-) diff --git a/packages/app/src/Toaster.tsx b/packages/app/src/Toaster.tsx index 4b48c887..29a41379 100644 --- a/packages/app/src/Toaster.tsx +++ b/packages/app/src/Toaster.tsx @@ -1,6 +1,7 @@ +import { ReactNode, useSyncExternalStore } from "react"; +import { v4 as uuid } from "uuid"; import ExternalStore from "ExternalStore"; import Icon from "Icons/Icon"; -import { ReactNode, useSyncExternalStore } from "react"; import { unixNow } from "Util"; import "./Toaster.css"; @@ -9,6 +10,7 @@ interface ToastNotification { element: ReactNode; expire?: number; icon?: string; + id?: string; } class ToasterSlots extends ExternalStore> { @@ -17,6 +19,7 @@ class ToasterSlots extends ExternalStore> { push(n: ToastNotification) { n.expire ??= unixNow() + 3; + n.id ??= uuid(); this.#stack.push(n); this.notifyChange(); } @@ -43,7 +46,7 @@ export default function Toaster() { return (
{toast.map(a => ( -
+
{a.element}
diff --git a/packages/app/src/Wallet/LNDHub.ts b/packages/app/src/Wallet/LNDHub.ts index a2e3af1e..07653744 100644 --- a/packages/app/src/Wallet/LNDHub.ts +++ b/packages/app/src/Wallet/LNDHub.ts @@ -123,7 +123,7 @@ export default class LNDHubWallet implements LNWallet { }, }); const json = await rsp.json(); - if ("error" in json) { + if ("code" in json && !rsp.ok) { const err = json as ErrorResponse; throw new WalletError(err.code, err.message); } diff --git a/packages/app/src/Wallet/NostrWalletConnect.ts b/packages/app/src/Wallet/NostrWalletConnect.ts index e53696b6..0b7cca2d 100644 --- a/packages/app/src/Wallet/NostrWalletConnect.ts +++ b/packages/app/src/Wallet/NostrWalletConnect.ts @@ -1,7 +1,7 @@ import { Connection, RawEvent } from "@snort/nostr"; import { EventBuilder } from "System"; import { EventExt } from "System/EventExt"; -import { LNWallet, WalletError, WalletErrorCode, WalletInfo, WalletInvoice } from "Wallet"; +import { LNWallet, WalletError, WalletErrorCode, WalletInfo, WalletInvoice, WalletInvoiceState } from "Wallet"; interface WalletConnectConfig { relayUrl: string; @@ -14,6 +14,23 @@ interface QueueObj { reject: (e: Error) => void; } +interface WalletConnectResponse { + result_type?: string; + result?: T; + error?: { + code: + | "RATE_LIMITED" + | "NOT_IMPLEMENTED" + | "INSUFFICIENT_BALANCE" + | "QUOTA_EXCEEDED" + | "RESTRICTED" + | "UNAUTHORIZED" + | "INTERNAL" + | "OTHER"; + message: string; + }; +} + export class NostrConnectWallet implements LNWallet { #config: WalletConnectConfig; #conn?: Connection; @@ -83,9 +100,18 @@ export class NostrConnectWallet implements LNWallet { async payInvoice(pr: string) { await this.login(); - return await this.#rpc("pay_invoice", { + const rsp = await this.#rpc>("pay_invoice", { invoice: pr, }); + if (!rsp.error) { + return { + ...rsp.result, + pr, + state: WalletInvoiceState.Paid, + } as WalletInvoice; + } else { + throw new WalletError(WalletErrorCode.GeneralError, rsp.error.message); + } } getInvoices() { diff --git a/packages/app/src/ZapPoolController.ts b/packages/app/src/ZapPoolController.ts index 8ee240f2..f1165cc2 100644 --- a/packages/app/src/ZapPoolController.ts +++ b/packages/app/src/ZapPoolController.ts @@ -47,6 +47,7 @@ class ZapPool extends ExternalStore> { const invoice = await svc.getInvoice(amtSend, `SnortZapPool: ${x.split}%`); if (invoice.pr) { const result = await wallet.payInvoice(invoice.pr); + console.debug("ZPC", invoice, result); if (result.state === WalletInvoiceState.Paid) { x.sum -= amtSend; Toastore.push({ @@ -58,13 +59,23 @@ class ZapPool extends ExternalStore> { icon: "zap", }); } else { - throw new Error("Payment failed"); + throw new Error(`Failed to pay invoice, unknown reason`); } } else { throw new Error(invoice.reason ?? "Failed to get invoice"); } } catch (e) { console.error(e); + if (e instanceof Error) { + const profile = UserCache.getFromCache(x.pubkey); + Toastore.push({ + element: `Failed to send sats to ${getDisplayName(profile, x.pubkey)} (${ + e.message + }), please try again later`, + expire: unixNow() + 10, + icon: "close", + }); + } } } this.#save();