Refactor wallet error handling with inheritance

This commit is contained in:
ekzyis 2024-11-26 05:01:23 +01:00
parent ed82d9cfc0
commit 413f76c33a
4 changed files with 34 additions and 26 deletions

View File

@ -8,7 +8,7 @@ import Bolt11Info from './bolt11-info'
import { useQuery } from '@apollo/client' import { useQuery } from '@apollo/client'
import { INVOICE } from '@/fragments/wallet' import { INVOICE } from '@/fragments/wallet'
import { FAST_POLL_INTERVAL, SSR } from '@/lib/constants' import { FAST_POLL_INTERVAL, SSR } from '@/lib/constants'
import { NoWalletAvailableError } from '@/wallets/errors' import { WalletError } from '@/wallets/errors'
import ItemJob from './item-job' import ItemJob from './item-job'
import Item from './item' import Item from './item'
import { CommentFlat } from './comment' import { CommentFlat } from './comment'
@ -104,7 +104,7 @@ export default function Invoice ({
return ( return (
<> <>
{/* TODO: handle aggregated wallet errors */} {/* TODO: handle aggregated wallet errors */}
{walletError && !(walletError instanceof NoWalletAvailableError) && {walletError instanceof WalletError &&
<div className='text-center fw-bold text-info mb-3' style={{ lineHeight: 1.25 }}> <div className='text-center fw-bold text-info mb-3' style={{ lineHeight: 1.25 }}>
Paying from attached wallet failed: Paying from attached wallet failed:
<code> {walletError.message}</code> <code> {walletError.message}</code>

View File

@ -1,7 +1,7 @@
import { useApolloClient, useLazyQuery, useMutation } from '@apollo/client' import { useApolloClient, useLazyQuery, useMutation } from '@apollo/client'
import { useCallback, useState } from 'react' import { useCallback, useState } from 'react'
import { useInvoice, useQrPayment } from './payment' import { useInvoice, useQrPayment } from './payment'
import { InvoiceCanceledError, InvoiceExpiredError, NoWalletAvailableError, WalletAggregateError } from '@/wallets/errors' import { InvoiceCanceledError, InvoiceExpiredError, WalletError } from '@/wallets/errors'
import { GET_PAID_ACTION } from '@/fragments/paidAction' import { GET_PAID_ACTION } from '@/fragments/paidAction'
import { useWalletPayment } from '@/wallets/payment' import { useWalletPayment } from '@/wallets/payment'
@ -39,7 +39,7 @@ export function usePaidMutation (mutation,
try { try {
return await waitForWalletPayment(walletInvoice, waitFor) return await waitForWalletPayment(walletInvoice, waitFor)
} catch (err) { } catch (err) {
if (err instanceof WalletAggregateError || err instanceof NoWalletAvailableError) { if (err instanceof WalletError) {
walletError = err walletError = err
// wallet payment error handling always creates a new invoice to retry // wallet payment error handling always creates a new invoice to retry
if (err.newInvoice) walletInvoice = err.newInvoice if (err.newInvoice) walletInvoice = err.newInvoice

View File

@ -14,39 +14,44 @@ export class InvoiceExpiredError extends Error {
} }
} }
export class WalletNotEnabledError extends Error { export class WalletError extends Error {}
export class WalletPaymentError extends WalletError {}
export class WalletConfigurationError extends WalletError {}
export class WalletNotEnabledError extends WalletConfigurationError {
constructor (name) { constructor (name) {
super(`wallet is not enabled: ${name}`) super(`wallet is not enabled: ${name}`)
this.name = 'WalletNotEnabledError' this.name = 'WalletNotEnabledError'
} }
} }
export class WalletSendNotConfiguredError extends Error { export class WalletSendNotConfiguredError extends WalletConfigurationError {
constructor (name) { constructor (name) {
super(`wallet send is not configured: ${name}`) super(`wallet send is not configured: ${name}`)
this.name = 'WalletSendNotConfiguredError' this.name = 'WalletSendNotConfiguredError'
} }
} }
export class SenderError extends Error { export class WalletSenderError extends WalletPaymentError {
constructor (name, invoice, message) { constructor (name, invoice, message) {
super(`${name} failed to pay invoice ${invoice.hash}: ${message}`) super(`${name} failed to pay invoice ${invoice.hash}: ${message}`)
this.name = 'SenderError' this.name = 'WalletSenderError'
this.invoice = invoice this.invoice = invoice
} }
} }
export class WalletAggregateError extends AggregateError { export class WalletsNotAvailableError extends WalletConfigurationError {
constructor (errors, newInvoice) { constructor () {
super(errors) super('no wallet available')
this.name = 'WalletAggregateError' this.name = 'WalletsNotAvailableError'
this.newInvoice = newInvoice
} }
} }
export class NoWalletAvailableError extends Error { export class WalletAggregateError extends WalletError {
constructor () { constructor (errors, newInvoice) {
super('no wallet for payments available') super('WalletAggregateError')
this.name = 'NoWalletAvailableError' this.name = 'WalletAggregateError'
this.errors = errors
this.newInvoice = newInvoice
} }
} }

View File

@ -5,7 +5,10 @@ import { formatSats } from '@/lib/format'
import { useWalletLogger } from '@/components/wallet-logger' import { useWalletLogger } from '@/components/wallet-logger'
import { useInvoice } from '@/components/payment' import { useInvoice } from '@/components/payment'
import { FAST_POLL_INTERVAL } from '@/lib/constants' import { FAST_POLL_INTERVAL } from '@/lib/constants'
import { NoWalletAvailableError, SenderError, WalletAggregateError, WalletNotEnabledError, WalletSendNotConfiguredError } from '@/wallets/errors' import {
WalletsNotAvailableError, WalletSenderError, WalletAggregateError, WalletNotEnabledError,
WalletSendNotConfiguredError, WalletPaymentError, WalletError, WalletConfigurationError
} from '@/wallets/errors'
import { canSend } from './common' import { canSend } from './common'
export function useWalletPayment () { export function useWalletPayment () {
@ -57,7 +60,7 @@ export function useWalletPayment () {
} catch (err) { } catch (err) {
// cancel invoice to make sure it cannot be paid later. // cancel invoice to make sure it cannot be paid later.
// we only need to do this if payment was attempted which is not the case if the wallet is not enabled. // we only need to do this if payment was attempted which is not the case if the wallet is not enabled.
const paymentAttempt = !(err instanceof WalletNotEnabledError || err instanceof WalletSendNotConfiguredError) const paymentAttempt = err instanceof WalletPaymentError
if (paymentAttempt) { if (paymentAttempt) {
await invoiceHelper.cancel(walletInvoice) await invoiceHelper.cancel(walletInvoice)
@ -75,7 +78,7 @@ export function useWalletPayment () {
// try next wallet if the payment failed because of the wallet // try next wallet if the payment failed because of the wallet
// and not because it expired or was canceled // and not because it expired or was canceled
const isWalletError = err instanceof WalletNotEnabledError || err instanceof WalletSendNotConfiguredError || err instanceof SenderError const isWalletError = err instanceof WalletError
if (isWalletError) { if (isWalletError) {
walletError = new WalletAggregateError([...walletError.errors, err], walletInvoice) walletError = new WalletAggregateError([...walletError.errors, err], walletInvoice)
continue continue
@ -90,14 +93,14 @@ export function useWalletPayment () {
// if we reach this line, no wallet payment succeeded // if we reach this line, no wallet payment succeeded
// if no wallet is enabled, throw a special error that caller can handle separately // throw a special error that caller can handle separately if no payment was attempted
const noWalletAvailable = walletError.errors.every(e => e instanceof WalletNotEnabledError) const noWalletAvailable = walletError.errors.every(e => e instanceof WalletConfigurationError)
if (noWalletAvailable) { if (noWalletAvailable) {
throw new NoWalletAvailableError() throw new WalletsNotAvailableError()
} }
// ignore errors from disabled wallets, only return payment errors // only return payment errors
const paymentErrors = walletError.errors.filter(e => !(e instanceof WalletNotEnabledError)) const paymentErrors = walletError.errors.filter(e => e instanceof WalletPaymentError)
throw new WalletAggregateError(paymentErrors, walletInvoice) throw new WalletAggregateError(paymentErrors, walletInvoice)
}, [walletsWithPayments, invoiceHelper]) }, [walletsWithPayments, invoiceHelper])
@ -160,7 +163,7 @@ function sendPayment (wallet, logger) {
} catch (err) { } catch (err) {
const message = err.message || err.toString?.() const message = err.message || err.toString?.()
logger.error(`payment failed: ${message}`, { bolt11 }) logger.error(`payment failed: ${message}`, { bolt11 })
throw new SenderError(wallet.def.name, invoice, message) throw new WalletSenderError(wallet.def.name, invoice, message)
} }
} }
} }