Fix receiver fallback on caller error (#1907)
* Rename to createUserInvoice * Fix no receiver fallback on wrap, direct or autowithdrawal error * Fix missing error logs for direct payments
This commit is contained in:
		
							parent
							
								
									0032e064b2
								
							
						
					
					
						commit
						5e85147578
					
				@ -3,7 +3,7 @@ import { datePivot } from '@/lib/time'
 | 
			
		||||
import { PAID_ACTION_PAYMENT_METHODS, USER_ID } from '@/lib/constants'
 | 
			
		||||
import { createHmac } from '@/api/resolvers/wallet'
 | 
			
		||||
import { Prisma } from '@prisma/client'
 | 
			
		||||
import { createWrappedInvoice, createInvoice as createUserInvoice } from '@/wallets/server'
 | 
			
		||||
import { createWrappedInvoice, createUserInvoice } from '@/wallets/server'
 | 
			
		||||
import { assertBelowMaxPendingInvoices, assertBelowMaxPendingDirectPayments } from './lib/assert'
 | 
			
		||||
 | 
			
		||||
import * as ITEM_CREATE from './itemCreate'
 | 
			
		||||
@ -264,42 +264,51 @@ async function performDirectAction (actionType, args, incomingContext) {
 | 
			
		||||
    throw new NonInvoiceablePeerError()
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  let invoiceObject
 | 
			
		||||
 | 
			
		||||
  try {
 | 
			
		||||
    await assertBelowMaxPendingDirectPayments(userId, incomingContext)
 | 
			
		||||
 | 
			
		||||
    const description = actionDescription ?? await paidActions[actionType].describe(args, incomingContext)
 | 
			
		||||
    invoiceObject = await createUserInvoice(userId, {
 | 
			
		||||
 | 
			
		||||
    for await (const { invoice, logger, wallet } of createUserInvoice(userId, {
 | 
			
		||||
      msats: cost,
 | 
			
		||||
      description,
 | 
			
		||||
      expiry: INVOICE_EXPIRE_SECS
 | 
			
		||||
    }, { models, lnd })
 | 
			
		||||
  } catch (e) {
 | 
			
		||||
    console.error('failed to create outside invoice', e)
 | 
			
		||||
    throw new NonInvoiceablePeerError()
 | 
			
		||||
  }
 | 
			
		||||
    }, { models, lnd })) {
 | 
			
		||||
      let hash
 | 
			
		||||
      try {
 | 
			
		||||
        hash = parsePaymentRequest({ request: invoice }).id
 | 
			
		||||
      } catch (e) {
 | 
			
		||||
        console.error('failed to parse invoice', e)
 | 
			
		||||
        logger?.error('failed to parse invoice: ' + e.message, { bolt11: invoice })
 | 
			
		||||
        continue
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
  const { invoice, wallet } = invoiceObject
 | 
			
		||||
  const hash = parsePaymentRequest({ request: invoice }).id
 | 
			
		||||
 | 
			
		||||
  const payment = await models.directPayment.create({
 | 
			
		||||
    data: {
 | 
			
		||||
      comment,
 | 
			
		||||
      lud18Data,
 | 
			
		||||
      desc: noteStr,
 | 
			
		||||
      bolt11: invoice,
 | 
			
		||||
      msats: cost,
 | 
			
		||||
      hash,
 | 
			
		||||
      walletId: wallet.id,
 | 
			
		||||
      receiverId: userId
 | 
			
		||||
      try {
 | 
			
		||||
        return {
 | 
			
		||||
          invoice: await models.directPayment.create({
 | 
			
		||||
            data: {
 | 
			
		||||
              comment,
 | 
			
		||||
              lud18Data,
 | 
			
		||||
              desc: noteStr,
 | 
			
		||||
              bolt11: invoice,
 | 
			
		||||
              msats: cost,
 | 
			
		||||
              hash,
 | 
			
		||||
              walletId: wallet.id,
 | 
			
		||||
              receiverId: userId
 | 
			
		||||
            }
 | 
			
		||||
          }),
 | 
			
		||||
          paymentMethod: 'DIRECT'
 | 
			
		||||
        }
 | 
			
		||||
      } catch (e) {
 | 
			
		||||
        console.error('failed to create direct payment', e)
 | 
			
		||||
        logger?.error('failed to create direct payment: ' + e.message, { bolt11: invoice })
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
  return {
 | 
			
		||||
    invoice: payment,
 | 
			
		||||
    paymentMethod: 'DIRECT'
 | 
			
		||||
  } catch (e) {
 | 
			
		||||
    console.error('failed to create user invoice', e)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  throw new NonInvoiceablePeerError()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export async function retryPaidAction (actionType, args, incomingContext) {
 | 
			
		||||
 | 
			
		||||
@ -24,7 +24,7 @@ export default [lnd, cln, lnAddr, lnbits, nwc, phoenixd, blink, lnc, webln]
 | 
			
		||||
 | 
			
		||||
const MAX_PENDING_INVOICES_PER_WALLET = 25
 | 
			
		||||
 | 
			
		||||
export async function createInvoice (userId, { msats, description, descriptionHash, expiry = 360 }, { paymentAttempt, predecessorId, models }) {
 | 
			
		||||
export async function * createUserInvoice (userId, { msats, description, descriptionHash, expiry = 360 }, { paymentAttempt, predecessorId, models }) {
 | 
			
		||||
  // get the wallets in order of priority
 | 
			
		||||
  const wallets = await getInvoiceableWallets(userId, {
 | 
			
		||||
    paymentAttempt,
 | 
			
		||||
@ -72,44 +72,42 @@ export async function createInvoice (userId, { msats, description, descriptionHa
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      return { invoice, wallet, logger }
 | 
			
		||||
      yield { invoice, wallet, logger }
 | 
			
		||||
    } catch (err) {
 | 
			
		||||
      console.error('failed to create user invoice:', err)
 | 
			
		||||
      logger.error(err.message, { status: true })
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  throw new Error('no wallet to receive available')
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export async function createWrappedInvoice (userId,
 | 
			
		||||
  { msats, feePercent, description, descriptionHash, expiry = 360 },
 | 
			
		||||
  { paymentAttempt, predecessorId, models, me, lnd }) {
 | 
			
		||||
  let logger, bolt11
 | 
			
		||||
  try {
 | 
			
		||||
    const { invoice, wallet } = await createInvoice(userId, {
 | 
			
		||||
      // this is the amount the stacker will receive, the other (feePercent)% is our fee
 | 
			
		||||
      msats: toPositiveBigInt(msats) * (100n - feePercent) / 100n,
 | 
			
		||||
      description,
 | 
			
		||||
      descriptionHash,
 | 
			
		||||
      expiry
 | 
			
		||||
    }, { paymentAttempt, predecessorId, models })
 | 
			
		||||
 | 
			
		||||
    logger = walletLogger({ wallet, models })
 | 
			
		||||
    bolt11 = invoice
 | 
			
		||||
 | 
			
		||||
    const { invoice: wrappedInvoice, maxFee } =
 | 
			
		||||
      await wrapInvoice({ bolt11, feePercent }, { msats, description, descriptionHash }, { me, lnd })
 | 
			
		||||
 | 
			
		||||
    return {
 | 
			
		||||
      invoice,
 | 
			
		||||
      wrappedInvoice: wrappedInvoice.request,
 | 
			
		||||
      wallet,
 | 
			
		||||
      maxFee
 | 
			
		||||
  // loop over all receiver wallet invoices until we successfully wrapped one
 | 
			
		||||
  for await (const { invoice, logger, wallet } of createUserInvoice(userId, {
 | 
			
		||||
    // this is the amount the stacker will receive, the other (feePercent)% is our fee
 | 
			
		||||
    msats: toPositiveBigInt(msats) * (100n - feePercent) / 100n,
 | 
			
		||||
    description,
 | 
			
		||||
    descriptionHash,
 | 
			
		||||
    expiry
 | 
			
		||||
  }, { paymentAttempt, predecessorId, models })) {
 | 
			
		||||
    let bolt11
 | 
			
		||||
    try {
 | 
			
		||||
      bolt11 = invoice
 | 
			
		||||
      const { invoice: wrappedInvoice, maxFee } = await wrapInvoice({ bolt11, feePercent }, { msats, description, descriptionHash }, { me, lnd })
 | 
			
		||||
      return {
 | 
			
		||||
        invoice,
 | 
			
		||||
        wrappedInvoice: wrappedInvoice.request,
 | 
			
		||||
        wallet,
 | 
			
		||||
        maxFee
 | 
			
		||||
      }
 | 
			
		||||
    } catch (e) {
 | 
			
		||||
      console.error('failed to wrap invoice:', e)
 | 
			
		||||
      logger?.error('failed to wrap invoice: ' + e.message, { bolt11 })
 | 
			
		||||
    }
 | 
			
		||||
  } catch (e) {
 | 
			
		||||
    logger?.error('invalid invoice: ' + e.message, { bolt11 })
 | 
			
		||||
    throw e
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  throw new Error('no wallet to receive available')
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export async function getInvoiceableWallets (userId, { paymentAttempt, predecessorId, models }) {
 | 
			
		||||
 | 
			
		||||
@ -1,6 +1,6 @@
 | 
			
		||||
import { msatsSatsFloor, msatsToSats, satsToMsats } from '@/lib/format'
 | 
			
		||||
import { createWithdrawal } from '@/api/resolvers/wallet'
 | 
			
		||||
import { createInvoice } from '@/wallets/server'
 | 
			
		||||
import { createUserInvoice } from '@/wallets/server'
 | 
			
		||||
 | 
			
		||||
export async function autoWithdraw ({ data: { id }, models, lnd }) {
 | 
			
		||||
  const user = await models.user.findUnique({ where: { id } })
 | 
			
		||||
@ -42,14 +42,18 @@ export async function autoWithdraw ({ data: { id }, models, lnd }) {
 | 
			
		||||
 | 
			
		||||
  if (pendingOrFailed.exists) return
 | 
			
		||||
 | 
			
		||||
  const { invoice, wallet, logger } = await createInvoice(id, { msats, description: 'SN: autowithdrawal', expiry: 360 }, { models })
 | 
			
		||||
 | 
			
		||||
  try {
 | 
			
		||||
    return await createWithdrawal(null,
 | 
			
		||||
      { invoice, maxFee: msatsToSats(maxFeeMsats) },
 | 
			
		||||
      { me: { id }, models, lnd, wallet, logger })
 | 
			
		||||
  } catch (err) {
 | 
			
		||||
    logger.error(`incoming payment failed: ${err}`, { bolt11: invoice })
 | 
			
		||||
    throw err
 | 
			
		||||
  for await (const { invoice, wallet, logger } of createUserInvoice(id, {
 | 
			
		||||
    msats,
 | 
			
		||||
    description: 'SN: autowithdrawal',
 | 
			
		||||
    expiry: 360
 | 
			
		||||
  }, { models })) {
 | 
			
		||||
    try {
 | 
			
		||||
      return await createWithdrawal(null,
 | 
			
		||||
        { invoice, maxFee: msatsToSats(maxFeeMsats) },
 | 
			
		||||
        { me: { id }, models, lnd, wallet, logger })
 | 
			
		||||
    } catch (err) {
 | 
			
		||||
      console.error('failed to create autowithdrawal:', err)
 | 
			
		||||
      logger?.error(`incoming payment failed: ${err}`, { bolt11: invoice })
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user