diff --git a/src/services/wallet/sendTask.ts b/src/services/wallet/sendTask.ts index cd3dfa69..d05d2f53 100644 --- a/src/services/wallet/sendTask.ts +++ b/src/services/wallet/sendTask.ts @@ -88,6 +88,8 @@ export const sendTask = async function ( } as TransactionTaskResult } catch (e: any) { // Mark the tx ERROR if one was created during prepare. + // prepare() errors thrown after the draft exists carry its transactionId. + transactionIdForRecovery ??= e?.params?.transactionId if (transactionIdForRecovery) { const tx = transactionsStore.findById(transactionIdForRecovery) if (tx) { diff --git a/src/services/wallet/transferTask.ts b/src/services/wallet/transferTask.ts index e948304e..6bbd2042 100644 --- a/src/services/wallet/transferTask.ts +++ b/src/services/wallet/transferTask.ts @@ -100,6 +100,9 @@ export const transferTask = async function ( nwcEvent, } as TransactionTaskResult } catch (e: any) { + // prepare() may throw after creating the draft (e.g. expired invoice); its + // errors carry the transactionId so the draft still gets stamped ERROR/EXPIRED. + transactionIdForRecovery ??= e?.params?.transactionId let txAfterError = transactionIdForRecovery ? transactionsStore.findById(transactionIdForRecovery) : undefined @@ -111,15 +114,17 @@ export const transferTask = async function ( txAfterError.status !== TransactionStatus.RECOVERED && txAfterError.status !== TransactionStatus.ERROR ) { + // Only prepare's expiry check puts `expiry` in the error params. + const status = e?.params?.expiry ? TransactionStatus.EXPIRED : TransactionStatus.ERROR let transactionData: TransactionData[] = [] try { transactionData = JSON.parse(txAfterError.data) } catch {} transactionData.push({ - status: TransactionStatus.ERROR, + status, error: WalletUtils.formatError(e), createdAt: new Date(), }) txAfterError.update({ - status: TransactionStatus.ERROR, + status, data: JSON.stringify(transactionData), }) } @@ -216,6 +221,9 @@ export const transferOnchainTask = async function ( nwcEvent, } as TransactionTaskResult } catch (e: any) { + // prepare() may throw after creating the draft (e.g. expired invoice); its + // errors carry the transactionId so the draft still gets stamped ERROR/EXPIRED. + transactionIdForRecovery ??= e?.params?.transactionId const txAfterError = transactionIdForRecovery ? transactionsStore.findById(transactionIdForRecovery) : undefined @@ -228,15 +236,17 @@ export const transferOnchainTask = async function ( txAfterError.status !== TransactionStatus.RECOVERED && txAfterError.status !== TransactionStatus.ERROR ) { + // Only prepare's expiry check puts `expiry` in the error params. + const status = e?.params?.expiry ? TransactionStatus.EXPIRED : TransactionStatus.ERROR let transactionData: TransactionData[] = [] try { transactionData = JSON.parse(txAfterError.data) } catch {} transactionData.push({ - status: TransactionStatus.ERROR, + status, error: WalletUtils.formatError(e), createdAt: new Date(), }) txAfterError.update({ - status: TransactionStatus.ERROR, + status, data: JSON.stringify(transactionData), }) }