Skip to content

Commit

Permalink
fix wallet usage and add logs
Browse files Browse the repository at this point in the history
  • Loading branch information
riccardobl committed Nov 5, 2024
1 parent 1a14f8d commit 947cc1a
Show file tree
Hide file tree
Showing 2 changed files with 40 additions and 21 deletions.
4 changes: 2 additions & 2 deletions api/paidAction/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ export default async function performPaidAction (actionType, args, context) {

const receiverUserId = await paidAction.invoiceablePeer?.(args, context) ?? SN_WALLET

// if forceFeeCredit or the action is free or prioritizeFeeCredits perform a fee credit action
// if forceFeeCredits or the action is free or prioritizeFeeCredits perform a fee credit action
if (me && !disableFeeCredit && ((forceFeeCredits || cost === 0n) || (prioritizeFeeCredits && (me?.msats ?? 0n) >= cost))) {
const invoiceData = await createFeeCreditInvoice(receiverUserId, { msats: cost, description })
return await performFeeCreditAction(invoiceData, actionType, paidAction, args, context)
Expand Down Expand Up @@ -263,7 +263,7 @@ export async function retryPaidAction (actionType, { invoice, forceFeeCredits },
throw new Error(`retryPaidAction - missing invoice ${actionType}`)
}

if (!failedInvoice.retriable) {
if (!failedInvoice.retriable && !forceFeeCredits) { // always retriable with fee credits
throw new Error(`retryPaidAction - invoice is not retriable ${actionType}`)
}

Expand Down
57 changes: 38 additions & 19 deletions components/use-paid-mutation.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { useCallback, useState } from 'react'
import { useInvoice, useQrPayment, useWalletPayment } from './payment'
import { InvoiceCanceledError } from '@/wallets/errors'
import { GET_PAID_ACTION, RETRY_PAID_ACTION } from '@/fragments/paidAction'
import { useWallets } from '@/wallets/index'
import { useWallets, useWallet } from '@/wallets/index'
import { canSend } from '@/wallets/common'

/*
Expand All @@ -30,64 +30,81 @@ export function usePaidMutation (mutation,
const invoiceHelper = useInvoice()
const waitForQrPayment = useQrPayment()
const client = useApolloClient()
const { wallets } = useWallets()
// innerResult is used to store/control the result of the mutation when innerMutate runs
const [innerResult, setInnerResult] = useState(result)
const { wallets: walletDefs } = useWallets()

// walletDefs shouldn't change on rerender, so it should be safe
const usableWallets = walletDefs
.map(w => w.def.name)
.map(name => useWallet(name))
.filter(w => !w.def.isAvailable || w.def.isAvailable())
.filter(w => w.config?.enabled && canSend(w))

const waitForActionPayment = useCallback(async (invoice, { wallet, alwaysShowQROnFailure = false, persistOnNavigate = false, waitFor }, originalResponse, action) => {
const walletErrors = []
let response = originalResponse

while (true) {
const usableWallets = wallets.filter(w => !w.def.isAvailable || w.def.isAvailable())
.filter(w => w.config?.enabled && canSend(w))[0]

// we try all the sending wallets one by one
for (const wallet of usableWallets) {
try {
await waitForWalletPayment(invoice, waitFor, wallet)
console.log('paid with wallet', wallet.def.name)
return { invoice, response }
} catch (err) {
walletErrors.push(err)
console.log('could not pay with wallet', wallet.def.name, 'will try another one ...')
}
}

try {
await invoiceHelper.cancel(invoice)
console.log('old invoice canceled')
} catch (err) {
console.error('could not cancel old invoice', err)
}

if (response.retriable) {
// every wallet failed, but we can still retry, so let's just cancel the invoice and ask for a better one
try {
await invoiceHelper.cancel(invoice)
} catch (err) {
console.error('usePaidMutation: failed to cancel invoice', err)
}
// every wallet failed, but we can still retry, so let's ask for a better invoice
console.log('could not pay with any wallet, will retry with a new invoice...')
const retry = await retryPaidAction({ variables: { invoiceId: parseInt(invoice.id) } })
response = retry.data?.retryPaidAction
invoice = response?.invoice
} else break
} else {
console.log('could not pay with any wallet, and the invoice is not retriable, will fallback to another method')
break
}
}

// we try one last time using fee credits
try {
const retry = await retryPaidAction({ variables: { invoiceId: parseInt(invoice.id), forceFeeCredit: true } })
console.log('could not pay with any wallet, will try with fee credits...')
const retry = await retryPaidAction({ variables: { invoiceId: parseInt(invoice.id), forceFeeCredits: true } })
response = retry.data?.retryPaidAction
// we paid with fee credits, so we don't need the invoice anymore
invoiceHelper.cancel(invoice).catch(console.error)
invoice = undefined
return { invoice, response }
} catch (err) {
console.log('could not pay with fee credits, will fallback to another method')
walletErrors.push(err)
}

if (alwaysShowQROnFailure) {
// TODO give another change to retry manually
// currently this fails because all the retry are exhausted
console.log('show qr code for manual payment')
const retry = await retryPaidAction({ variables: { invoiceId: parseInt(invoice.id) } })
response = retry.data?.retryPaidAction
invoice = response?.invoice
// if we get here, we've exhausted all retries and the last invoice is still pending, so we will try to pay it with a qr code
await waitForQrPayment(invoice, walletErrors[walletErrors.length - 1], { persistOnNavigate, waitFor })
} else {
// we failed to pay the invoice, so we will cancel the invoice and throw the all the errors
invoiceHelper.cancel(invoice).catch(console.error)
console.log('we are out of options, we will throw the errors')
throw new Error(walletErrors.map(e => e.message).join('\n'))
}

return { invoice, response }
}, [waitForWalletPayment, waitForQrPayment, invoiceHelper, wallets])
}, [waitForWalletPayment, waitForQrPayment, invoiceHelper, usableWallets])

const innerMutate = useCallback(async ({
onCompleted: innerOnCompleted, ...innerOptions
Expand Down Expand Up @@ -125,6 +142,8 @@ export function usePaidMutation (mutation,
if (!wait) {
// onCompleted is called before the invoice is paid for optimistic updates
ourOnCompleted?.(data)
} else {
setInnerResult({ data, ...rest })
}
// don't wait to pay the invoice
const p = waitForActionPayment(invoice, { alwaysShowQROnFailure, persistOnNavigate, waitFor }, response, innerOptions).then(async ({ invoice, response }) => {
Expand Down Expand Up @@ -160,7 +179,7 @@ export function usePaidMutation (mutation,
onPaid?.(client.cache, { data })
}

return innerResult
return { data, ...rest }
}, [mutate, options, waitForActionPayment, onCompleted, client.cache, getPaidAction, setInnerResult])

return [innerMutate, innerResult]
Expand Down

0 comments on commit 947cc1a

Please sign in to comment.