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 { INVOICE } from '@/fragments/wallet'
import { FAST_POLL_INTERVAL, SSR } from '@/lib/constants'
import { NoWalletAvailableError } from '@/wallets/errors'
import { WalletError } from '@/wallets/errors'
import ItemJob from './item-job'
import Item from './item'
import { CommentFlat } from './comment'
@ -104,7 +104,7 @@ export default function Invoice ({
return (
<>
{/* 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 }}>
Paying from attached wallet failed:
<code> {walletError.message}</code>

View File

@ -1,7 +1,7 @@
import { useApolloClient, useLazyQuery, useMutation } from '@apollo/client'
import { useCallback, useState } from 'react'
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 { useWalletPayment } from '@/wallets/payment'
@ -39,7 +39,7 @@ export function usePaidMutation (mutation,
try {
return await waitForWalletPayment(walletInvoice, waitFor)
} catch (err) {
if (err instanceof WalletAggregateError || err instanceof NoWalletAvailableError) {
if (err instanceof WalletError) {
walletError = err
// wallet payment error handling always creates a new invoice to retry
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) {
super(`wallet is not enabled: ${name}`)
this.name = 'WalletNotEnabledError'
}
}
export class WalletSendNotConfiguredError extends Error {
export class WalletSendNotConfiguredError extends WalletConfigurationError {
constructor (name) {
super(`wallet send is not configured: ${name}`)
this.name = 'WalletSendNotConfiguredError'
}
}
export class SenderError extends Error {
export class WalletSenderError extends WalletPaymentError {
constructor (name, invoice, message) {
super(`${name} failed to pay invoice ${invoice.hash}: ${message}`)
this.name = 'SenderError'
this.name = 'WalletSenderError'
this.invoice = invoice
}
}
export class WalletAggregateError extends AggregateError {
constructor (errors, newInvoice) {
super(errors)
this.name = 'WalletAggregateError'
this.newInvoice = newInvoice
export class WalletsNotAvailableError extends WalletConfigurationError {
constructor () {
super('no wallet available')
this.name = 'WalletsNotAvailableError'
}
}
export class NoWalletAvailableError extends Error {
constructor () {
super('no wallet for payments available')
this.name = 'NoWalletAvailableError'
export class WalletAggregateError extends WalletError {
constructor (errors, newInvoice) {
super('WalletAggregateError')
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 { useInvoice } from '@/components/payment'
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'
export function useWalletPayment () {
@ -57,7 +60,7 @@ export function useWalletPayment () {
} catch (err) {
// 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.
const paymentAttempt = !(err instanceof WalletNotEnabledError || err instanceof WalletSendNotConfiguredError)
const paymentAttempt = err instanceof WalletPaymentError
if (paymentAttempt) {
await invoiceHelper.cancel(walletInvoice)
@ -75,7 +78,7 @@ export function useWalletPayment () {
// try next wallet if the payment failed because of the wallet
// 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) {
walletError = new WalletAggregateError([...walletError.errors, err], walletInvoice)
continue
@ -90,14 +93,14 @@ export function useWalletPayment () {
// if we reach this line, no wallet payment succeeded
// if no wallet is enabled, throw a special error that caller can handle separately
const noWalletAvailable = walletError.errors.every(e => e instanceof WalletNotEnabledError)
// throw a special error that caller can handle separately if no payment was attempted
const noWalletAvailable = walletError.errors.every(e => e instanceof WalletConfigurationError)
if (noWalletAvailable) {
throw new NoWalletAvailableError()
throw new WalletsNotAvailableError()
}
// ignore errors from disabled wallets, only return payment errors
const paymentErrors = walletError.errors.filter(e => !(e instanceof WalletNotEnabledError))
// only return payment errors
const paymentErrors = walletError.errors.filter(e => e instanceof WalletPaymentError)
throw new WalletAggregateError(paymentErrors, walletInvoice)
}, [walletsWithPayments, invoiceHelper])
@ -160,7 +163,7 @@ function sendPayment (wallet, logger) {
} catch (err) {
const message = err.message || err.toString?.()
logger.error(`payment failed: ${message}`, { bolt11 })
throw new SenderError(wallet.def.name, invoice, message)
throw new WalletSenderError(wallet.def.name, invoice, message)
}
}
}