From 8cb89574aed5e1515891a0c7edecbfd725a4fc1b Mon Sep 17 00:00:00 2001 From: ekzyis Date: Wed, 11 Dec 2024 14:39:01 +0100 Subject: [PATCH 1/2] Fix pending forwards considered paid by client --- components/use-invoice.js | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/components/use-invoice.js b/components/use-invoice.js index f5af94da..dc32aacf 100644 --- a/components/use-invoice.js +++ b/components/use-invoice.js @@ -4,6 +4,8 @@ import { InvoiceCanceledError, InvoiceExpiredError, WalletReceiverError } from ' import { RETRY_PAID_ACTION } from '@/fragments/paidAction' import { INVOICE, CANCEL_INVOICE } from '@/fragments/wallet' +const PENDING_FORWARD_STATES = ['PENDING_HELD', 'FORWARDING'] + export default function useInvoice () { const client = useApolloClient() const [retryPaidAction] = useMutation(RETRY_PAID_ACTION) @@ -16,24 +18,29 @@ export default function useInvoice () { throw error } - const { cancelled, cancelledAt, actionError, expiresAt, forwardStatus } = data.invoice + const { cancelled, cancelledAt, actionState, actionError, expiresAt, forwardStatus } = data.invoice const expired = cancelledAt && new Date(expiresAt) < new Date(cancelledAt) if (expired) { throw new InvoiceExpiredError(data.invoice) } - const failed = cancelled || actionError - - if (failed && (forwardStatus && forwardStatus !== 'CONFIRMED')) { + const failedForward = forwardStatus && forwardStatus !== 'CONFIRMED' + if (failedForward) { throw new WalletReceiverError(data.invoice) } + const failed = cancelled || actionError if (failed) { throw new InvoiceCanceledError(data.invoice, actionError) } - return { invoice: data.invoice, check: that(data.invoice) } + // never let check pass if a forward is pending + // see https://github.com/stackernews/stacker.news/issues/1707 + const pendingForward = PENDING_FORWARD_STATES.includes(actionState) + const check = that(data.invoice) && !pendingForward + + return { invoice: data.invoice, check } }, [client]) const cancel = useCallback(async ({ hash, hmac }) => { From 4e6fb40c0bad5a99886cf73fef90e169b480f9a3 Mon Sep 17 00:00:00 2001 From: ekzyis Date: Wed, 11 Dec 2024 19:26:56 +0100 Subject: [PATCH 2/2] Use conditional waitFor to fix premature payment success --- components/item-act.js | 8 +++++++- components/use-invoice.js | 11 ++--------- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/components/item-act.js b/components/item-act.js index 37289567..459fff7b 100644 --- a/components/item-act.js +++ b/components/item-act.js @@ -232,9 +232,15 @@ export function useAct ({ query = ACT_MUTATION, ...options } = {}) { // because the mutation name we use varies, // we need to extract the result/invoice from the response const getPaidActionResult = data => Object.values(data)[0] + const wallets = useSendWallets() const [act] = usePaidMutation(query, { - waitFor: inv => inv?.satsReceived > 0, + waitFor: inv => + // if we have attached wallets, we might be paying a wrapped invoice in which case we need to make sure + // we don't prematurely consider the payment as successful (important for receiver fallbacks) + wallets.length > 0 + ? inv?.actionState === 'PAID' + : inv?.satsReceived > 0, ...options, update: (cache, { data }) => { const response = getPaidActionResult(data) diff --git a/components/use-invoice.js b/components/use-invoice.js index dc32aacf..cfdb2c6d 100644 --- a/components/use-invoice.js +++ b/components/use-invoice.js @@ -4,8 +4,6 @@ import { InvoiceCanceledError, InvoiceExpiredError, WalletReceiverError } from ' import { RETRY_PAID_ACTION } from '@/fragments/paidAction' import { INVOICE, CANCEL_INVOICE } from '@/fragments/wallet' -const PENDING_FORWARD_STATES = ['PENDING_HELD', 'FORWARDING'] - export default function useInvoice () { const client = useApolloClient() const [retryPaidAction] = useMutation(RETRY_PAID_ACTION) @@ -18,7 +16,7 @@ export default function useInvoice () { throw error } - const { cancelled, cancelledAt, actionState, actionError, expiresAt, forwardStatus } = data.invoice + const { cancelled, cancelledAt, actionError, expiresAt, forwardStatus } = data.invoice const expired = cancelledAt && new Date(expiresAt) < new Date(cancelledAt) if (expired) { @@ -35,12 +33,7 @@ export default function useInvoice () { throw new InvoiceCanceledError(data.invoice, actionError) } - // never let check pass if a forward is pending - // see https://github.com/stackernews/stacker.news/issues/1707 - const pendingForward = PENDING_FORWARD_STATES.includes(actionState) - const check = that(data.invoice) && !pendingForward - - return { invoice: data.invoice, check } + return { invoice: data.invoice, check: that(data.invoice) } }, [client]) const cancel = useCallback(async ({ hash, hmac }) => {