mirror of
https://github.com/minibits-cash/minibits_wallet.git
synced 2026-10-05 19:28:26 +00:00
Handling pending transactions
This commit is contained in:
@@ -513,6 +513,8 @@
|
||||
"from": "From %{sender}",
|
||||
"paidFrom": "Paid from",
|
||||
"paidTo": "Paid to %{receiver}",
|
||||
"payTo": "Pay to %{receiver}",
|
||||
"payNow": "Pay now",
|
||||
"receivedFrom": "Received from %{sender}",
|
||||
"receivedTo": "Received to",
|
||||
"receiveFailed": "Receive failed",
|
||||
|
||||
+2
-2
@@ -3,10 +3,10 @@ import {withSetPropAction} from './helpers/withSetPropAction'
|
||||
import { MintUnit } from '../services/wallet/currency'
|
||||
|
||||
/**
|
||||
* Proof type saved in database backup
|
||||
* Proof db record
|
||||
*/
|
||||
|
||||
export type BackupProof = Proof & {
|
||||
export type ProofRecord = Proof & {
|
||||
isPending: boolean
|
||||
isSpent: boolean
|
||||
updatedAt: Date
|
||||
|
||||
@@ -7,7 +7,7 @@ import {
|
||||
flow,
|
||||
} from 'mobx-state-tree'
|
||||
import {withSetPropAction} from './helpers/withSetPropAction'
|
||||
import {ProofModel, Proof, BackupProof} from './Proof'
|
||||
import {ProofModel, Proof, ProofRecord} from './Proof'
|
||||
import {log} from '../services/logService'
|
||||
import {getRootStore} from './helpers/getRootStore'
|
||||
import AppError, {Err} from '../utils/AppError'
|
||||
@@ -110,7 +110,7 @@ export const ProofsStoreModel = types
|
||||
|
||||
// Group into isUnspent and isPending
|
||||
const groupedProofs = unspentAndPendingProofs.reduce(
|
||||
(acc: {isPending: BackupProof[], isUnspent: BackupProof[]}, proof: BackupProof) => {
|
||||
(acc: {isPending: ProofRecord[], isUnspent: ProofRecord[]}, proof: ProofRecord) => {
|
||||
if (proof.isPending) {
|
||||
acc.isPending.push(proof);
|
||||
} else {
|
||||
@@ -121,7 +121,7 @@ export const ProofsStoreModel = types
|
||||
{ isUnspent: [], isPending: [] } // Initialize the groups
|
||||
)
|
||||
|
||||
const cleanProofs = (proofs: BackupProof[]) => {
|
||||
const cleanProofs = (proofs: ProofRecord[]) => {
|
||||
return proofs.map(p => {
|
||||
// Destructure the unwanted properties and collect the rest in `cleaned`
|
||||
const { isPending, isSpent, updatedAt, ...cleaned } = p
|
||||
@@ -165,7 +165,7 @@ export const ProofsStoreModel = types
|
||||
throw new AppError(Err.STORAGE_ERROR, 'Could not find mint', {mintUrl: newProofs[0].mintUrl})
|
||||
}
|
||||
|
||||
for (const [keysetId, keysetProofs] of proofsByKeysetId.entries()) {
|
||||
for (const [keysetId, keysetProofs] of Array.from(proofsByKeysetId.entries())) {
|
||||
|
||||
for (const proof of keysetProofs) {
|
||||
if(self.alreadyExists(proof)) {
|
||||
|
||||
@@ -3,37 +3,6 @@ import { log } from '../services/logService'
|
||||
import { MintUnit } from '../services/wallet/currency'
|
||||
import { Database } from '../services'
|
||||
|
||||
/**
|
||||
* Transaction model with MST actions for database updates.
|
||||
* Note: Individual setter actions (setStatus, setFee, etc.) are deprecated.
|
||||
* Use the generic update() action instead for all mutations.
|
||||
*/
|
||||
export interface TransactionRecord {
|
||||
id: number
|
||||
type: TransactionType
|
||||
amount: number
|
||||
fee: number
|
||||
unit: MintUnit
|
||||
data: string
|
||||
sentFrom?: string | null
|
||||
sentTo?: string | null
|
||||
profile?: string | null
|
||||
memo?: string | null
|
||||
paymentId?: string | null
|
||||
quote?: string | null
|
||||
paymentRequest?: string | null
|
||||
zapRequest?: string | null
|
||||
inputToken?: string | null
|
||||
outputToken?: string | null
|
||||
proof?: string | null
|
||||
mint: string
|
||||
balanceAfter?: number | null
|
||||
noteToSelf?: string | null
|
||||
tags?: Array<string> | null
|
||||
status: TransactionStatus
|
||||
expiresAt?: Date | null
|
||||
createdAt: Date
|
||||
}
|
||||
|
||||
export type TransactionData = {
|
||||
status: TransactionStatus
|
||||
@@ -100,16 +69,16 @@ export const TransactionModel = types
|
||||
self.outputToken = outputToken.slice(0, 40)
|
||||
log.trace('[pruneOutputToken]', 'Transaction outputToken pruned in store', { id: self.id })
|
||||
},
|
||||
update(fields: Partial<TransactionRecord>) {
|
||||
update(fields: Partial<Transaction>) {
|
||||
// Update multiple fields in database with a single query
|
||||
Database.updateTransaction(self.id!, fields)
|
||||
const updatedTransaction = Database.updateTransaction(self.id, fields)
|
||||
|
||||
// Update the model to keep store in sync
|
||||
Object.keys(fields).forEach(key => {
|
||||
;(self as any)[key] = (fields as any)[key]
|
||||
Object.keys(updatedTransaction).forEach(key => {
|
||||
;(self as any)[key] = (updatedTransaction as any)[key]
|
||||
})
|
||||
|
||||
log.trace('[update]', 'Transaction updated', { id: self.id, fields: Object.keys(fields) })
|
||||
log.trace('[update]', 'Transaction updated', { id: self.id, updatedData: fields})
|
||||
},
|
||||
}))
|
||||
|
||||
|
||||
@@ -3,14 +3,12 @@ import {
|
||||
SnapshotOut,
|
||||
types,
|
||||
flow,
|
||||
detach,
|
||||
} from 'mobx-state-tree'
|
||||
import {withSetPropAction} from './helpers/withSetPropAction'
|
||||
import {
|
||||
TransactionModel,
|
||||
Transaction,
|
||||
TransactionStatus,
|
||||
TransactionRecord,
|
||||
} from './Transaction'
|
||||
import {Database} from '../services'
|
||||
import {log} from '../services/logService'
|
||||
@@ -76,16 +74,14 @@ import {
|
||||
.filter(t => t.unit === unit)
|
||||
},
|
||||
getPendingTopups() {
|
||||
const dbTopups: TransactionRecord[] = Database.getPendingTopups()
|
||||
const dbTopups: Transaction[] = Database.getPendingTopups()
|
||||
|
||||
if(dbTopups.length > 0) {
|
||||
|
||||
const pendingTopups: Transaction[] = []
|
||||
|
||||
for(const topup of dbTopups) {
|
||||
const createdAt = new Date(topup.createdAt)
|
||||
const expiresAt = topup.expiresAt ? new Date(topup.expiresAt) : null
|
||||
const inStoreTransaction = {...topup, createdAt, expiresAt}
|
||||
const inStoreTransaction = {...topup}
|
||||
|
||||
const topupInstance = TransactionModel.create(inStoreTransaction)
|
||||
pendingTopups.push(topupInstance)
|
||||
@@ -98,8 +94,26 @@ import {
|
||||
return []
|
||||
|
||||
},
|
||||
getPendingTopupsCount() {
|
||||
return Database.getPendingTopupsCount()
|
||||
getPendingTransfers() {
|
||||
const dbTransfers: Transaction[] = Database.getPendingTopups()
|
||||
|
||||
if(dbTransfers.length > 0) {
|
||||
|
||||
const pendingTransfers: Transaction[] = []
|
||||
|
||||
for(const transfer of dbTransfers) {
|
||||
const inStoreTransaction = {...transfer}
|
||||
|
||||
const transferInstance = TransactionModel.create(inStoreTransaction)
|
||||
pendingTransfers.push(transferInstance)
|
||||
}
|
||||
|
||||
return pendingTransfers
|
||||
|
||||
}
|
||||
|
||||
return []
|
||||
|
||||
},
|
||||
countRecentByUnit(unit: MintUnit) {
|
||||
return this.getRecentByUnit(unit).length
|
||||
@@ -115,9 +129,7 @@ import {
|
||||
const dbTransaction = Database.getTransactionById(id)
|
||||
|
||||
if(dbTransaction) {
|
||||
const createdAt = new Date(dbTransaction.createdAt)
|
||||
const expiresAt = dbTransaction.expiresAt ? new Date(dbTransaction.expiresAt) : null
|
||||
const inStoreTransaction = {...dbTransaction, createdAt}
|
||||
const inStoreTransaction = {...dbTransaction}
|
||||
const {id} = dbTransaction
|
||||
|
||||
if(!loadTokens) {
|
||||
@@ -143,9 +155,7 @@ import {
|
||||
const dbTransaction = Database.getTransactionByPaymentId(paymentId)
|
||||
|
||||
if(dbTransaction) {
|
||||
const createdAt = new Date(dbTransaction.createdAt)
|
||||
const expiresAt = dbTransaction.expiresAt ? new Date(dbTransaction.expiresAt) : null
|
||||
const inStoreTransaction = {...dbTransaction, createdAt, expiresAt}
|
||||
const inStoreTransaction = {...dbTransaction}
|
||||
const {id} = dbTransaction
|
||||
|
||||
// Shorten for performance reasons
|
||||
@@ -169,9 +179,7 @@ import {
|
||||
const dbTransaction = Database.getTransactionByQuote(quote)
|
||||
|
||||
if(dbTransaction) {
|
||||
const createdAt = new Date(dbTransaction.createdAt)
|
||||
const expiresAt = dbTransaction.expiresAt ? new Date(dbTransaction.expiresAt) : null
|
||||
const inStoreTransaction = {...dbTransaction, createdAt, expiresAt}
|
||||
const inStoreTransaction = {...dbTransaction}
|
||||
const {id} = dbTransaction
|
||||
|
||||
// Shorten for performance reasons
|
||||
@@ -195,9 +203,7 @@ import {
|
||||
const dbTransaction = Database.getTransactionByPaymentRequest(pr)
|
||||
|
||||
if(dbTransaction) {
|
||||
const createdAt = new Date(dbTransaction.createdAt)
|
||||
const expiresAt = dbTransaction.expiresAt ? new Date(dbTransaction.expiresAt) : null
|
||||
const inStoreTransaction = {...dbTransaction, createdAt, expiresAt}
|
||||
const inStoreTransaction = {...dbTransaction}
|
||||
const {id} = dbTransaction
|
||||
|
||||
// Shorten for performance reasons
|
||||
@@ -276,11 +282,10 @@ import {
|
||||
.actions(self => ({
|
||||
addTransaction: flow(function* addTransaction(newTransaction){
|
||||
// First let's store the transaction into the database
|
||||
const dbTransaction: TransactionRecord = yield Database.addTransactionAsync(newTransaction)
|
||||
const dbTransaction: Transaction = yield Database.addTransactionAsync(newTransaction)
|
||||
|
||||
// Add the new transaction to the transactions store
|
||||
const createdAt = new Date(dbTransaction.createdAt)
|
||||
const inStoreTransaction = {...dbTransaction, createdAt}
|
||||
const inStoreTransaction = {...dbTransaction}
|
||||
const {id} = dbTransaction
|
||||
|
||||
if (!self.transactionsMap.has(id)) {
|
||||
@@ -301,14 +306,12 @@ import {
|
||||
}),
|
||||
addToHistory(limit: number, offset: number, onlyPending: boolean){
|
||||
// Appends transaction to the map and adds reference to history from database.
|
||||
const result = Database.getTransactions(limit, offset, onlyPending)
|
||||
log.trace('[addToHistory] dbResult ids', {ids: result?._array.map(t => t.id)})
|
||||
const transactions = Database.getTransactions(limit, offset, onlyPending)
|
||||
log.trace('[addToHistory] transactions ids', {ids: transactions ? transactions.map(t => t.id) : []})
|
||||
|
||||
if (result && result.length > 0) {
|
||||
for (const dbTransaction of result._array) {
|
||||
const createdAt = new Date(dbTransaction.createdAt)
|
||||
const expiresAt = dbTransaction.expiresAt ? new Date(dbTransaction.expiresAt) : null
|
||||
const inStoreTransaction = {...dbTransaction, createdAt, expiresAt}
|
||||
if (transactions && transactions.length > 0) {
|
||||
for (const dbTransaction of transactions) {
|
||||
const inStoreTransaction = {...dbTransaction}
|
||||
|
||||
// Shorten for performance reasons
|
||||
if(inStoreTransaction.inputToken && inStoreTransaction.inputToken.length > 0) {
|
||||
@@ -337,13 +340,11 @@ import {
|
||||
},
|
||||
addRecentByUnit() {
|
||||
// Rehydrates recent from database.
|
||||
const dbTransactions = Database.getRecentTransactionsByUnit(maxTransactionsByUnit)
|
||||
const transactions = Database.getRecentTransactionsByUnit(maxTransactionsByUnit)
|
||||
|
||||
if (dbTransactions && dbTransactions.length > 0) {
|
||||
for (const dbTransaction of dbTransactions) {
|
||||
const createdAt = new Date(dbTransaction.createdAt)
|
||||
const expiresAt = dbTransaction.expiresAt ? new Date(dbTransaction.expiresAt) : null
|
||||
const inStoreTransaction = {...dbTransaction, createdAt, expiresAt}
|
||||
if (transactions && transactions.length > 0) {
|
||||
for (const dbTransaction of transactions) {
|
||||
const inStoreTransaction = {...dbTransaction}
|
||||
|
||||
// Shorten for performance reasons
|
||||
if(inStoreTransaction.inputToken && inStoreTransaction.inputToken.length > 0) {
|
||||
@@ -431,17 +432,10 @@ import {
|
||||
|
||||
return prunedSnapshot
|
||||
})
|
||||
|
||||
const getHostname = function (mintUrl: string) {
|
||||
try {
|
||||
return new URL(mintUrl).hostname
|
||||
} catch (e) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// refresh
|
||||
export interface TransactionsStore
|
||||
extends Instance<typeof TransactionsStoreModel> {}
|
||||
export interface TransactionsStoreSnapshot
|
||||
extends SnapshotOut<typeof TransactionsStoreModel> {}
|
||||
extends SnapshotOut<typeof TransactionsStoreModel> {}
|
||||
|
||||
@@ -102,7 +102,7 @@ export const TranDetailScreen = observer(function TranDetailScreen({ route }: Pr
|
||||
useFocusEffect(useCallback(() => {
|
||||
try {
|
||||
const tx = transactionsStore.findById(id, true) // load full tokens
|
||||
log.trace('Transaction loaded', {id: tx?.id, unit: tx?.unit, note: tx?.noteToSelf, inputToken: tx?.inputToken, outputToken: tx?.inputToken})
|
||||
log.trace('Transaction loaded', {tx})
|
||||
|
||||
if (!tx) {
|
||||
throw new AppError(
|
||||
@@ -1385,10 +1385,12 @@ const TopupInfoBlock = function (props: {
|
||||
useFocusEffect(useCallback(() => {
|
||||
log.trace('[TopupInfoBlock] useFocusEffect start')
|
||||
|
||||
if (transaction.status === TransactionStatus.ERROR ||
|
||||
transaction.status === TransactionStatus.PENDING ||
|
||||
transaction.status === TransactionStatus.EXPIRED) {
|
||||
setIsPendingTopupRetriable(true)
|
||||
if ([
|
||||
TransactionStatus.ERROR,
|
||||
TransactionStatus.PENDING,
|
||||
TransactionStatus.EXPIRED
|
||||
].includes(transaction.status)) {
|
||||
setIsPendingTopupRetriable(true)
|
||||
}
|
||||
}, []))
|
||||
|
||||
@@ -1530,7 +1532,7 @@ const TopupInfoBlock = function (props: {
|
||||
label="tranDetailScreen.createdAt"
|
||||
value={(transaction.createdAt as Date).toLocaleString()}
|
||||
/>
|
||||
{transaction.expiresAt && (
|
||||
{transaction.expiresAt && transaction.status !== TransactionStatus.COMPLETED && (
|
||||
<TranItem
|
||||
label="tranDetailScreen.expiresAt"
|
||||
value={(new Date(transaction.expiresAt!)).toLocaleString()}
|
||||
@@ -1675,11 +1677,19 @@ const TransferInfoBlock = function (props: {
|
||||
// Pay invoice received over Nostr
|
||||
const onPayDraftTransfer = async function () {
|
||||
|
||||
if(!transaction.paymentRequest || !transaction.unit || transaction.mint) {
|
||||
log.trace('[onPayDraftTransfer] start', {tId: transaction.id})
|
||||
|
||||
const {paymentRequest, unit, mint} = transaction
|
||||
|
||||
if(!paymentRequest || !unit || !mint) {
|
||||
log.error('[onPayDraftTransfer] Missing params', {paymentRequest, unit, mint})
|
||||
|
||||
setResultModalInfo({
|
||||
status: TransactionStatus.ERROR,
|
||||
message: 'This transaction is missing data needed to be paid.'
|
||||
})
|
||||
|
||||
toggleResultModal()
|
||||
return
|
||||
}
|
||||
|
||||
@@ -1758,8 +1768,7 @@ const TransferInfoBlock = function (props: {
|
||||
/>
|
||||
<Button
|
||||
style={{marginTop: spacing.medium}}
|
||||
preset="secondary"
|
||||
text="Pay"
|
||||
tx="transactionCommon.payNow"
|
||||
onPress={onPayDraftTransfer}
|
||||
/>
|
||||
</View>
|
||||
@@ -1817,7 +1826,7 @@ const TransferInfoBlock = function (props: {
|
||||
value={transaction.status as string}
|
||||
/>
|
||||
)}
|
||||
{transaction.status !== TransactionStatus.ERROR && (
|
||||
{transaction.status === TransactionStatus.COMPLETED && (
|
||||
<TranItem
|
||||
label="tranDetailScreen.balanceAfter"
|
||||
value={transaction.balanceAfter || 0}
|
||||
@@ -1825,11 +1834,16 @@ const TransferInfoBlock = function (props: {
|
||||
isCurrency={true}
|
||||
/>
|
||||
)}
|
||||
|
||||
<TranItem
|
||||
label="tranDetailScreen.createdAt"
|
||||
value={(transaction.createdAt as Date).toLocaleString()}
|
||||
/>
|
||||
{transaction.expiresAt && transaction.status !== TransactionStatus.COMPLETED && (
|
||||
<TranItem
|
||||
label="tranDetailScreen.expiresAt"
|
||||
value={(new Date(transaction.expiresAt!)).toLocaleString()}
|
||||
/>
|
||||
)}
|
||||
{transaction.paymentId && (
|
||||
<TranItem
|
||||
label="tranDetailScreen.paymentId"
|
||||
|
||||
@@ -1,22 +1,17 @@
|
||||
import {observer} from 'mobx-react-lite'
|
||||
import React, {FC, useState, useEffect, useRef, useMemo} from 'react'
|
||||
import React, {useState, useEffect, useCallback} from 'react'
|
||||
import {
|
||||
TextStyle,
|
||||
ViewStyle,
|
||||
View,
|
||||
FlatList,
|
||||
SectionList,
|
||||
Platform,
|
||||
UIManager,
|
||||
LayoutAnimation,
|
||||
} from 'react-native'
|
||||
import Animated, {
|
||||
useSharedValue,
|
||||
useAnimatedStyle,
|
||||
withTiming,
|
||||
} from 'react-native-reanimated'
|
||||
import {formatDistance, toDate} from 'date-fns'
|
||||
import {useThemeColor, spacing, colors, typography} from '../theme'
|
||||
import {useThemeColor, spacing, typography} from '../theme'
|
||||
import {
|
||||
Button,
|
||||
Icon,
|
||||
@@ -37,12 +32,14 @@ import {TransactionListItem} from './Transactions/TransactionListItem'
|
||||
import { Transaction, TransactionStatus } from '../models/Transaction'
|
||||
import { translate } from '../i18n'
|
||||
import { maxTransactionsInHistory } from '../models/TransactionsStore'
|
||||
import { StaticScreenProps, useNavigation } from '@react-navigation/native'
|
||||
import { StaticScreenProps, useFocusEffect, useNavigation } from '@react-navigation/native'
|
||||
|
||||
// Number of transactions held in TransactionsStore model
|
||||
const limit = maxTransactionsInHistory
|
||||
|
||||
type Props = StaticScreenProps<undefined>
|
||||
type Props = StaticScreenProps<{
|
||||
showPending?: boolean
|
||||
}>
|
||||
|
||||
export const TranHistoryScreen = observer(function TranHistoryScreen({ route }: Props) {
|
||||
const navigation = useNavigation()
|
||||
@@ -98,11 +95,6 @@ export const TranHistoryScreen = observer(function TranHistoryScreen({ route }:
|
||||
log.trace('[init] setAll true')
|
||||
setIsAll(true)
|
||||
}
|
||||
|
||||
/* if (countByStatus[TransactionStatus.PENDING] <= limit) {
|
||||
log.trace('setPendingAll true')
|
||||
setPendingIsAll(true)
|
||||
}*/
|
||||
}
|
||||
|
||||
init()
|
||||
@@ -119,6 +111,21 @@ export const TranHistoryScreen = observer(function TranHistoryScreen({ route }:
|
||||
}
|
||||
}, [])
|
||||
|
||||
|
||||
const initFilter = useCallback(() => {
|
||||
log.trace('[initFilter] start')
|
||||
if(route.params && route.params.showPending) {
|
||||
log.trace('[initFilter] toggleShowPendingOnly ')
|
||||
toggleShowPendingOnly()
|
||||
//@ts-ignore
|
||||
/* navigation.setParams({
|
||||
showPending: undefined,
|
||||
})*/
|
||||
}
|
||||
}, [])
|
||||
|
||||
useFocusEffect(initFilter)
|
||||
|
||||
const toggleDeleteModal = () => {
|
||||
setIsDeleteModalVisible(previousState => !previousState)
|
||||
}
|
||||
|
||||
@@ -94,7 +94,9 @@ export const TransactionListItem = observer(function (props: TransactionListProp
|
||||
return (tx.memo && tx.memo !== 'LNbits'
|
||||
? tx.memo
|
||||
: tx.sentTo
|
||||
? translate('transactionCommon.paidTo', {receiver: getProfileName(tx.sentTo)})
|
||||
? tx.status === TransactionStatus.COMPLETED ?
|
||||
translate('transactionCommon.paidTo', {receiver: getProfileName(tx.sentTo)})
|
||||
: translate('transactionCommon.payTo', {receiver: getProfileName(tx.sentTo)})
|
||||
: translate('transactionCommon.youPaid'))
|
||||
default:
|
||||
return translate('transactionCommon.unknown')
|
||||
|
||||
@@ -36,8 +36,8 @@ import EventEmitter from '../utils/eventEmitter'
|
||||
import {useStores} from '../models'
|
||||
import {Mint, UnitBalance} from '../models/Mint'
|
||||
import {MintsByUnit} from '../models/MintsStore'
|
||||
import {HANDLE_CLAIM_TASK, HANDLE_RECEIVED_EVENT_TASK, log, NostrClient, WalletTaskResult} from '../services'
|
||||
import {Transaction} from '../models/Transaction'
|
||||
import {Database, HANDLE_CLAIM_TASK, HANDLE_RECEIVED_EVENT_TASK, log, NostrClient, WalletTaskResult} from '../services'
|
||||
import {Transaction, TransactionStatus} from '../models/Transaction'
|
||||
import {TransactionListItem} from './Transactions/TransactionListItem'
|
||||
import {WalletTask} from '../services'
|
||||
import {translate} from '../i18n'
|
||||
@@ -92,7 +92,7 @@ export const WalletScreen = observer(function WalletScreen({ route }: Props) {
|
||||
|
||||
const [info, setInfo] = useState<string>('')
|
||||
const [defaultMintUrl, setDefaultMintUrl] = useState<string>(MINIBITS_MINT_URL)
|
||||
const [pendingTopupsCount, setPendingTopupsCount] = useState<number>(0)
|
||||
const [pendingCount, setPendingCount] = useState<number>(0)
|
||||
const [error, setError] = useState<AppError | undefined>()
|
||||
const [isLoading, setIsLoading] = useState<boolean>(false)
|
||||
const [isMintsModalVisible, setIsMintsModalVisible] = useState<boolean>(false)
|
||||
@@ -171,17 +171,6 @@ export const WalletScreen = observer(function WalletScreen({ route }: Props) {
|
||||
await addMint()
|
||||
}
|
||||
|
||||
/*
|
||||
TODO rethink
|
||||
const pending = transactionsStore.getPendingTopupsCount()
|
||||
|
||||
if(pending > 0) {
|
||||
setPendingTopupsCount(pending)
|
||||
} */
|
||||
|
||||
// const nostrKeyPair = await KeyChain.loadNostrKeyPair()
|
||||
// log.trace('[getInitialData] KEYS CHECK', {keychainPubkey: nostrKeyPair?.publicKey, profilePubkey: walletProfileStore.pubkey})
|
||||
|
||||
// Only once on startup - Create websocket subscriptions to receive tokens or payment requests by NOSTR DMs
|
||||
WalletTask.receiveEventsFromRelaysQueue()
|
||||
|
||||
@@ -299,11 +288,17 @@ export const WalletScreen = observer(function WalletScreen({ route }: Props) {
|
||||
WalletTask.handleInFlightQueue()
|
||||
WalletTask.handleClaimQueue().catch(e => setInfo(e.message))
|
||||
WalletTask.syncStateWithAllMintsQueue({isPending: true})
|
||||
WalletTask.handlePendingTopupsQueue()
|
||||
WalletTask.handlePendingQueue()
|
||||
|
||||
if(userSettingsStore.exchangeCurrency) {
|
||||
walletStore.refreshExchangeRate(userSettingsStore.exchangeCurrency)
|
||||
}
|
||||
|
||||
// TODO rethink
|
||||
const countByStatus = Database.getTransactionsCount()
|
||||
if(countByStatus[TransactionStatus.PENDING] && countByStatus[TransactionStatus.PENDING] > 0) {
|
||||
setPendingCount(countByStatus[TransactionStatus.PENDING] || 0)
|
||||
}
|
||||
} else {
|
||||
log.trace('[performChecks] Skipping mint server checks...')
|
||||
}
|
||||
@@ -399,9 +394,14 @@ export const WalletScreen = observer(function WalletScreen({ route }: Props) {
|
||||
navigation.navigate('TokenReceive', {unit: currentUnit})
|
||||
}
|
||||
|
||||
const gotoPendingTopups = function () {
|
||||
const gotoPendingTransactions = function () {
|
||||
// @ts-ignore
|
||||
navigation.navigate('Transactions')
|
||||
navigation.navigate('TransactionsNavigator', {
|
||||
screen: 'TranHistory',
|
||||
params: {
|
||||
showPending: true
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const gotoProfile = function () {
|
||||
@@ -632,17 +632,15 @@ export const WalletScreen = observer(function WalletScreen({ route }: Props) {
|
||||
TitleActionComponent={headerTitle.length > 0 ? <HeaderTitle /> : undefined}
|
||||
RightActionComponent={
|
||||
<>
|
||||
{/* TODO this should be unpaid payment requests
|
||||
|
||||
pendingTopupsCount > 0 && (
|
||||
{pendingCount > 0 && (
|
||||
<Pressable
|
||||
style={{flexDirection: 'row', alignItems:'center', marginRight: spacing.medium}}
|
||||
onPress={() => gotoPendingTopups()}
|
||||
onPress={gotoPendingTransactions}
|
||||
>
|
||||
<Icon icon='faPaperPlane' color={headerTitleColor}/>
|
||||
<Text text={`${pendingTopupsCount}`} style={{color: headerTitleColor}} />
|
||||
<Icon icon='faClock' color={headerTitleColor}/>
|
||||
<Text text={`${pendingCount}`} style={{color: headerTitleColor}} />
|
||||
</Pressable>
|
||||
)*/ }
|
||||
)}
|
||||
</>
|
||||
}
|
||||
/>
|
||||
|
||||
@@ -12,7 +12,6 @@ export * from './SendScreen'
|
||||
export * from './ScanScreen'
|
||||
export * from './TranDetailScreen'
|
||||
export * from './TranHistoryScreen'
|
||||
export * from './PaymentRequestsScreen'
|
||||
export * from './TransferScreen'
|
||||
export * from './TopupScreen'
|
||||
export * from './LightningPayScreen'
|
||||
|
||||
+143
-84
@@ -6,12 +6,23 @@ import {
|
||||
import {Proof} from '../models/Proof'
|
||||
import {
|
||||
Transaction,
|
||||
TransactionRecord,
|
||||
TransactionStatus,
|
||||
} from '../models/Transaction'
|
||||
import AppError, {Err} from '../utils/AppError'
|
||||
import {log} from './logService'
|
||||
import {BackupProof} from '../models/Proof'
|
||||
import {ProofRecord} from '../models/Proof'
|
||||
import { isDate } from 'date-fns'
|
||||
|
||||
// Helper functions to normalize transaction records with Date objects
|
||||
const normalizeTransactionRecord = function (r: any) {
|
||||
if (r.createdAt) r.createdAt = new Date(r.createdAt);
|
||||
if (r.expiresAt) r.expiresAt = r.expiresAt ? new Date(r.expiresAt) : null;
|
||||
return r as Transaction;
|
||||
}
|
||||
|
||||
const normalizeTransactionRows = function(rows: any) {
|
||||
return rows?._array.map(normalizeTransactionRecord) as Transaction[];
|
||||
}
|
||||
|
||||
let _db: QuickSQLiteConnection
|
||||
|
||||
@@ -255,43 +266,57 @@ const getDatabaseVersion = function (db: QuickSQLiteConnection): {version: numbe
|
||||
*/
|
||||
|
||||
|
||||
const updateTransaction = function (id: number, fields: {[key:string]: any}): TransactionRecord {
|
||||
const updateTransaction = function (id: number, fields: Partial<Transaction>): Transaction {
|
||||
|
||||
const allowedColumns = ['amount','fee','unit','data','sentFrom','sentTo','profile','memo','paymentId','quote','paymentRequest','zapRequest','inputToken','outputToken','proof','balanceAfter','noteToSelf','tags','status','expiresAt'];
|
||||
|
||||
try {
|
||||
// Filter keys against allowed columns to prevent SQL injection
|
||||
const validKeys = Object.keys(fields).filter(key => allowedColumns.includes(key));
|
||||
// Normalize data types for sqlite
|
||||
for (const key in fields) {
|
||||
const value = fields[key]
|
||||
|
||||
if (value === '') {
|
||||
fields[key] = null
|
||||
}
|
||||
|
||||
if (isDate(value)) {
|
||||
fields[key] = value.toISOString()
|
||||
}
|
||||
}
|
||||
|
||||
// Filter keys against allowed columns
|
||||
const validKeys = Object.keys(fields).filter(key => allowedColumns.includes(key))
|
||||
|
||||
if (validKeys.length === 0) {
|
||||
// No valid keys to update, return existing transaction
|
||||
return getTransactionById(id);
|
||||
return getTransactionById(id)
|
||||
}
|
||||
|
||||
// Build SET clauses and parameters
|
||||
const setClauses = validKeys.map(key => `${key} = ?`);
|
||||
const params = validKeys.map(key => fields[key]);
|
||||
params.push(id); // Add id at the end for WHERE clause
|
||||
const setClauses = validKeys.map(key => `${key} = ?`)
|
||||
const params = validKeys.map(key => fields[key])
|
||||
params.push(id) // Add id at the end for WHERE clause
|
||||
|
||||
const query = `
|
||||
UPDATE transactions
|
||||
SET ${setClauses.join(', ')}
|
||||
WHERE id = ?
|
||||
`;
|
||||
`
|
||||
|
||||
const db = getInstance();
|
||||
db.execute(query, params);
|
||||
const db = getInstance()
|
||||
const result = db.execute(query, params)
|
||||
|
||||
log.debug('[updateTransaction]', `Transaction updated in the database`, {id, fields: validKeys});
|
||||
const updated = getTransactionById(id) // already normalized
|
||||
|
||||
const updatedTx = getTransactionById(id);
|
||||
return updatedTx as TransactionRecord;
|
||||
log.trace('[updateTransaction] Transaction updated in the database', {id: updated.id})
|
||||
|
||||
return updated as Transaction
|
||||
} catch (e: any) {
|
||||
throw new AppError(
|
||||
Err.DATABASE_ERROR,
|
||||
'Could not update transaction in database',
|
||||
e.message,
|
||||
);
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -324,7 +349,9 @@ const getTransactions = function (limit: number, offset: number, onlyPending: bo
|
||||
const {rows} = db.execute(query, params)
|
||||
|
||||
log.trace(`[getTransactions], Returned ${rows?.length} rows`)
|
||||
return rows
|
||||
|
||||
return normalizeTransactionRows(rows)
|
||||
|
||||
} catch (e: any) {
|
||||
throw new AppError(
|
||||
Err.DATABASE_ERROR,
|
||||
@@ -335,9 +362,8 @@ const getTransactions = function (limit: number, offset: number, onlyPending: bo
|
||||
}
|
||||
|
||||
const getPendingTopups = function () {
|
||||
let query: string = ''
|
||||
try {
|
||||
query = `
|
||||
const query = `
|
||||
SELECT *
|
||||
FROM transactions
|
||||
WHERE status = 'PENDING'
|
||||
@@ -350,7 +376,34 @@ const getPendingTopups = function () {
|
||||
|
||||
log.trace(`[getPendingTopups], Returned ${rows?.length} rows`)
|
||||
|
||||
return rows?._array
|
||||
return normalizeTransactionRows(rows)
|
||||
|
||||
} catch (e: any) {
|
||||
throw new AppError(
|
||||
Err.DATABASE_ERROR,
|
||||
'Transactions could not be retrieved from the database',
|
||||
e.message,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const getPendingTransfers = function () {
|
||||
try {
|
||||
const query = `
|
||||
SELECT *
|
||||
FROM transactions
|
||||
WHERE status = 'PENDING'
|
||||
AND type = 'TRANSFER'
|
||||
ORDER BY id DESC
|
||||
`
|
||||
|
||||
const db = getInstance()
|
||||
const {rows} = db.execute(query)
|
||||
|
||||
log.trace(`[getPendingTransfers], Returned ${rows?.length} rows`)
|
||||
|
||||
return normalizeTransactionRows(rows)
|
||||
|
||||
} catch (e: any) {
|
||||
throw new AppError(
|
||||
@@ -390,6 +443,34 @@ const getPendingTopupsCount = function () {
|
||||
}
|
||||
|
||||
|
||||
const getPendingTransfersCount = function () {
|
||||
let query: string = ''
|
||||
try {
|
||||
query = `
|
||||
SELECT COUNT(*)
|
||||
AS total
|
||||
FROM transactions
|
||||
WHERE status = 'PENDING'
|
||||
AND type = 'TRANSFER'
|
||||
`
|
||||
|
||||
const db = getInstance()
|
||||
const {rows} = db.execute(query)
|
||||
|
||||
log.trace(`[getPendingTransfersCount], Returned ${rows?.item(0)}`)
|
||||
|
||||
return rows?.item(0)['total'] as number
|
||||
|
||||
} catch (e: any) {
|
||||
throw new AppError(
|
||||
Err.DATABASE_ERROR,
|
||||
'Transactions could not be retrieved from the database',
|
||||
e.message,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const getTransactionsCount = function (status?: TransactionStatus) {
|
||||
let query: string
|
||||
let params: any[] = []
|
||||
@@ -458,7 +539,7 @@ const getRecentTransactionsByUnit = (countRecent: number) => {
|
||||
const db = getInstance()
|
||||
const { rows } = db.execute(query, params)
|
||||
|
||||
return rows?._array
|
||||
return normalizeTransactionRows(rows)
|
||||
|
||||
} catch (e: any) {
|
||||
throw new AppError(Err.DATABASE_ERROR, 'Error retrieving last 3 transactions by unit', e.message)
|
||||
@@ -498,7 +579,7 @@ const getTransactionById = function (id: number) {
|
||||
const db = getInstance()
|
||||
const {rows} = db.execute(query, params)
|
||||
|
||||
return rows?.item(0) as TransactionRecord
|
||||
return normalizeTransactionRecord(rows?.item(0))
|
||||
} catch (e: any) {
|
||||
throw new AppError(Err.DATABASE_ERROR, 'Transaction not found', e.message)
|
||||
}
|
||||
@@ -516,7 +597,7 @@ const getTransactionByPaymentId = function (id: string) {
|
||||
const db = getInstance()
|
||||
const {rows} = db.execute(query, params)
|
||||
|
||||
return rows?.item(0) as TransactionRecord
|
||||
return normalizeTransactionRecord(rows?.item(0))
|
||||
} catch (e: any) {
|
||||
throw new AppError(Err.DATABASE_ERROR, 'Transaction not found', e.message)
|
||||
}
|
||||
@@ -534,7 +615,7 @@ const getTransactionByQuote = function (quote: string) {
|
||||
const db = getInstance()
|
||||
const {rows} = db.execute(query, params)
|
||||
|
||||
return rows?.item(0) as TransactionRecord
|
||||
return normalizeTransactionRecord(rows?.item(0))
|
||||
} catch (e: any) {
|
||||
throw new AppError(Err.DATABASE_ERROR, 'Transaction not found', e.message)
|
||||
}
|
||||
@@ -552,14 +633,14 @@ const getTransactionByPaymentRequest = function (pr: string) {
|
||||
const db = getInstance()
|
||||
const {rows} = db.execute(query, params)
|
||||
|
||||
return rows?.item(0) as TransactionRecord
|
||||
return normalizeTransactionRecord(rows?.item(0))
|
||||
} catch (e: any) {
|
||||
throw new AppError(Err.DATABASE_ERROR, 'Transaction not found', e.message)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const addTransactionAsync = async function (tx: Transaction): Promise<TransactionRecord> {
|
||||
const addTransactionAsync = async function (tx: Transaction): Promise<Transaction> {
|
||||
try {
|
||||
const {type, amount, fee, unit, data, memo, mint, status} = tx
|
||||
const now = new Date()
|
||||
@@ -575,9 +656,8 @@ const addTransactionAsync = async function (tx: Transaction): Promise<Transactio
|
||||
|
||||
log.info('[addTransactionAsync]', 'New transaction added to the database', {id: result.insertId, type, mint, status})
|
||||
|
||||
const newTx = getTransactionById(result.insertId as number)
|
||||
return getTransactionById(result.insertId as number) // already normalized
|
||||
|
||||
return newTx as TransactionRecord
|
||||
} catch (e: any) {
|
||||
throw new AppError(
|
||||
Err.DATABASE_ERROR,
|
||||
@@ -653,55 +733,6 @@ const expireAllAfterRecovery = async function () {
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/* const cleanTransactionData = async function (transactionIds: number[]) {
|
||||
try {
|
||||
|
||||
const transactionIdsString = transactionIds.join(',')
|
||||
|
||||
const query = `
|
||||
UPDATE transactions
|
||||
SET data = ?
|
||||
WHERE id IN (${transactionIdsString})
|
||||
`
|
||||
const params = ['[]']
|
||||
|
||||
const _db = getInstance()
|
||||
const result = await _db.executeAsync(query, params)
|
||||
|
||||
log.info('cleanTransactionData executed in the database')
|
||||
|
||||
} catch (e: any) {
|
||||
throw new AppError(Err.DATABASE_ERROR, 'Could not cleanTransactionData in database', e.message)
|
||||
}
|
||||
} */
|
||||
|
||||
|
||||
const deleteTransactionsByStatus = function (status: TransactionStatus) {
|
||||
try {
|
||||
const query = `
|
||||
@@ -720,12 +751,37 @@ const deleteTransactionsByStatus = function (status: TransactionStatus) {
|
||||
} catch (e: any) {
|
||||
throw new AppError(
|
||||
Err.DATABASE_ERROR,
|
||||
'Could not delete transactions',
|
||||
'Could not delete transactions.',
|
||||
e.message,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const deleteTransactionById = function (id: number) {
|
||||
try {
|
||||
const query = `
|
||||
DELETE FROM transactions
|
||||
WHERE id = ?
|
||||
`
|
||||
const params = [id]
|
||||
|
||||
const db = getInstance()
|
||||
const {rows} = db.execute(query, params)
|
||||
|
||||
log.debug('[deleteTransactionById]', 'Transaction has been deleted', {id})
|
||||
|
||||
return rows
|
||||
|
||||
} catch (e: any) {
|
||||
throw new AppError(
|
||||
Err.DATABASE_ERROR,
|
||||
'Could not delete transaction.',
|
||||
e.message,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Proofs - backup of store model
|
||||
*/
|
||||
@@ -762,7 +818,7 @@ const addOrUpdateProof = function (
|
||||
|
||||
const newProof = getProofById(result.insertId as number)
|
||||
|
||||
return newProof as BackupProof
|
||||
return newProof as ProofRecord
|
||||
} catch (e: any) {
|
||||
throw new AppError(
|
||||
Err.DATABASE_ERROR,
|
||||
@@ -907,7 +963,7 @@ const getProofById = function (id: number) {
|
||||
const db = getInstance()
|
||||
const {rows} = db.execute(query, params)
|
||||
|
||||
return rows?.item(0) as BackupProof
|
||||
return rows?.item(0) as ProofRecord
|
||||
} catch (e: any) {
|
||||
throw new AppError(Err.DATABASE_ERROR, 'proof not found', e.message)
|
||||
}
|
||||
@@ -917,7 +973,7 @@ const getProofs = async function (
|
||||
isUnspent: boolean,
|
||||
isPending: boolean,
|
||||
isSpent: boolean,
|
||||
): Promise<BackupProof[]> {
|
||||
): Promise<ProofRecord[]> {
|
||||
let query: string = ''
|
||||
|
||||
try {
|
||||
@@ -961,7 +1017,7 @@ const getProofs = async function (
|
||||
const db = getInstance()
|
||||
const {rows} = await db.executeAsync(query)
|
||||
|
||||
return rows?._array as BackupProof[]
|
||||
return rows?._array as ProofRecord[]
|
||||
|
||||
} catch (e: any) {
|
||||
throw new AppError(
|
||||
@@ -972,7 +1028,7 @@ const getProofs = async function (
|
||||
}
|
||||
}
|
||||
|
||||
const getProofsByTransaction = function (transactionId: number): BackupProof[] {
|
||||
const getProofsByTransaction = function (transactionId: number): ProofRecord[] {
|
||||
try {
|
||||
const query = `
|
||||
SELECT *
|
||||
@@ -984,7 +1040,7 @@ const getProofsByTransaction = function (transactionId: number): BackupProof[] {
|
||||
const db = getInstance()
|
||||
const {rows} = db.execute(query, params)
|
||||
|
||||
return rows?._array as BackupProof[]
|
||||
return rows?._array as ProofRecord[]
|
||||
} catch (e: any) {
|
||||
throw new AppError(
|
||||
Err.DATABASE_ERROR,
|
||||
@@ -1028,11 +1084,14 @@ export const Database = {
|
||||
getTransactions,
|
||||
getPendingTopups,
|
||||
getPendingTopupsCount,
|
||||
getPendingTransfers,
|
||||
getPendingTransfersCount,
|
||||
addTransactionAsync,
|
||||
updateTransaction,
|
||||
expireAllAfterRecovery,
|
||||
updateStatusesAsync,
|
||||
deleteTransactionsByStatus,
|
||||
deleteTransactionById,
|
||||
getPendingAmount,
|
||||
addOrUpdateProof,
|
||||
addOrUpdateProofs,
|
||||
|
||||
@@ -1,20 +1,12 @@
|
||||
import {rootStoreInstance} from '../../models'
|
||||
import { TransactionTaskResult, WalletTask } from '../walletService'
|
||||
import { TransactionTaskResult } from '../walletService'
|
||||
import { MintBalance } from '../../models/Mint'
|
||||
import { poller } from '../../utils/poller'
|
||||
import { Transaction, TransactionData, TransactionRecord, TransactionStatus, TransactionType } from '../../models/Transaction'
|
||||
import { Transaction, TransactionData, TransactionStatus, TransactionType } from '../../models/Transaction'
|
||||
import { log } from '../logService'
|
||||
import { Contact } from '../../models/Contact'
|
||||
import { LightningUtils } from '../lightning/lightningUtils'
|
||||
import { getSnapshot, isStateTreeNode } from 'mobx-state-tree'
|
||||
import { WalletUtils } from './utils'
|
||||
import { MintUnit } from './currency'
|
||||
import { NostrClient, NostrEvent } from '../nostrService'
|
||||
import { Err } from '../../utils/AppError'
|
||||
import { NostrClient } from '../nostrService'
|
||||
import {
|
||||
CashuMint,
|
||||
CashuWallet,
|
||||
MintQuoteResponse,
|
||||
PaymentRequest as CashuPaymentRequest,
|
||||
PaymentRequestTransport,
|
||||
PaymentRequestTransportType
|
||||
|
||||
@@ -105,16 +105,19 @@ export const topupTask = async function (
|
||||
const expiresAtDate = addSeconds(new Date(timestamp * 1000), expiry)
|
||||
const sentFromValue = contactTo?.nip05 || contactTo?.name || ''
|
||||
const sentToValue = walletProfileStore.nip05 || ''
|
||||
|
||||
log.trace('[topupTask]', expiresAtDate)
|
||||
|
||||
transaction.update({
|
||||
quote: mintQuote,
|
||||
paymentId: paymentHash,
|
||||
paymentRequest: encodedInvoice,
|
||||
expiresAt: expiresAtDate,
|
||||
expiresAt: new Date(expiresAtDate),
|
||||
sentFrom: sentFromValue,
|
||||
sentTo: sentToValue
|
||||
})
|
||||
|
||||
log.trace('[topupTask] invoice', {amount, paymentHash, expiry, timestamp})
|
||||
log.trace('[topupTask] invoice', {amount, paymentHash, expiry, timestamp, expiresAtDate})
|
||||
|
||||
transactionData.push({
|
||||
status: TransactionStatus.PENDING,
|
||||
|
||||
@@ -3,9 +3,9 @@ import AppError, {Err} from '../../utils/AppError'
|
||||
import {MeltProofsResponse, MeltQuoteResponse, MeltQuoteState, getEncodedToken} from '@cashu/cashu-ts'
|
||||
import {rootStoreInstance} from '../../models'
|
||||
import { TransactionTaskResult, WalletTask } from '../walletService'
|
||||
import { MintBalance, MintProofsCounter } from '../../models/Mint'
|
||||
import { MintBalance } from '../../models/Mint'
|
||||
import { Proof } from '../../models/Proof'
|
||||
import { Transaction, TransactionData, TransactionRecord, TransactionStatus, TransactionType } from '../../models/Transaction'
|
||||
import { Transaction, TransactionData, TransactionStatus, TransactionType } from '../../models/Transaction'
|
||||
import { log } from '../logService'
|
||||
import { WalletUtils } from './utils'
|
||||
import {isBefore} from 'date-fns'
|
||||
|
||||
@@ -41,6 +41,7 @@ import { UnsignedEvent } from 'nostr-tools'
|
||||
import { Platform } from 'react-native'
|
||||
import { cashuPaymentRequestTask } from './wallet/cashuPaymentRequestTask'
|
||||
import { decodePaymentRequest, sumBlindSignatures } from '@cashu/cashu-ts/src/utils'
|
||||
import { Database } from './sqlite'
|
||||
|
||||
/**
|
||||
* The default number of proofs per denomination to keep in a wallet.
|
||||
@@ -81,7 +82,7 @@ type WalletTaskService = {
|
||||
}
|
||||
) => Promise<SyncStateTaskResult | void>
|
||||
handleInFlightQueue: () => Promise<void>
|
||||
handlePendingTopupsQueue: () => Promise<void>
|
||||
handlePendingQueue: () => Promise<void>
|
||||
handlePendingTopupQueue: (params: {
|
||||
transaction: Transaction
|
||||
}) => Promise<void>
|
||||
@@ -1504,20 +1505,42 @@ const handleInFlightByMintTask = async function (mint: Mint): Promise<WalletTask
|
||||
|
||||
|
||||
|
||||
const handlePendingTopupsQueue = async function (): Promise<void> {
|
||||
const handlePendingQueue = async function (): Promise<void> {
|
||||
const pendingTopups: Transaction[] = transactionsStore.getPendingTopups()
|
||||
const pendingTransfers: Transaction[] = transactionsStore.getPendingTransfers()
|
||||
|
||||
log.trace('[handlePendingTopupsQueue] start', {pendingTopups})
|
||||
log.trace('[handlePendingQueue] start', {pendingTopups, pendingTransfers})
|
||||
|
||||
// we just expire expired transfers, no ecash ops so no need of queue
|
||||
if(pendingTransfers.length > 0) {
|
||||
for (const transfer of pendingTransfers) {
|
||||
if (isBefore(transfer.expiresAt as Date, new Date())) {
|
||||
log.debug('[handlePendingQueue]', `Transfer invoice expired: ${transfer.paymentId} ${transfer.quote}`)
|
||||
|
||||
const transactionData = JSON.parse(transfer.data)
|
||||
transactionData.push({
|
||||
status: TransactionStatus.EXPIRED,
|
||||
message: 'Invoice expired',
|
||||
createdAt: new Date(),
|
||||
})
|
||||
|
||||
transfer.update({
|
||||
status: TransactionStatus.EXPIRED,
|
||||
data: JSON.stringify(transactionData)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (pendingTopups.length === 0) {
|
||||
log.trace('[handlePendingTopupsQueue]', 'No pending topups - skipping task send to the queue...')
|
||||
log.trace('[handlePendingQueue]', 'No pending topups - skipping task send to the queue...')
|
||||
return
|
||||
}
|
||||
|
||||
for (const topup of pendingTopups) {
|
||||
// skip pr if active poller exists
|
||||
if(pollerExists(`handlePendingTopupPoller-${topup.paymentId}`)) {
|
||||
log.trace('[handlePendingTopupsQueue] Skipping check of pendingTopup, poller exists', {paymentHash: topup.paymentId})
|
||||
log.trace('[handlePendingQueue] Skipping check of pendingTopup, poller exists', {paymentHash: topup.paymentId})
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -1558,12 +1581,15 @@ const handlePendingTopupTask = async function (params: {transaction: Transaction
|
||||
expiresAt
|
||||
} = transaction
|
||||
|
||||
log.trace('[handlePendingTopupTask] start', transaction)
|
||||
|
||||
const transactionData = JSON.parse(transaction.data)
|
||||
const mintInstance = mintsStore.findByUrl(mintUrl as string)
|
||||
|
||||
try {
|
||||
if(!mintInstance || !mintQuote || !unit || !amount) {
|
||||
throw new AppError(Err.VALIDATION_ERROR, 'Missing mint, mint quote, unit, amount', {transactionId})
|
||||
// Database.deleteTransactionById(transactionId)
|
||||
throw new AppError(Err.VALIDATION_ERROR, 'Missing mint, mint quote, unit, amount', {transactionId, caller: 'handlePendingTopupTask'})
|
||||
}
|
||||
|
||||
// check is quote has been paid
|
||||
@@ -1576,19 +1602,17 @@ const handlePendingTopupTask = async function (params: {transaction: Transaction
|
||||
if (isBefore(expiresAt as Date, new Date())) {
|
||||
log.debug('[handlePendingTopupTask]', `Invoice expired, removing: ${paymentHash} ${mintQuote}`)
|
||||
|
||||
// expire related tx - but only if it has not been completed before this check
|
||||
if(transaction.status !== TransactionStatus.COMPLETED) {
|
||||
transactionData.push({
|
||||
status: TransactionStatus.EXPIRED,
|
||||
message: 'Invoice expired',
|
||||
createdAt: new Date(),
|
||||
})
|
||||
transactionData.push({
|
||||
status: TransactionStatus.EXPIRED,
|
||||
message: 'Invoice expired',
|
||||
createdAt: new Date(),
|
||||
})
|
||||
|
||||
transaction.update({
|
||||
status: TransactionStatus.EXPIRED,
|
||||
data: JSON.stringify(transactionData)
|
||||
})
|
||||
}
|
||||
transaction.update({
|
||||
status: TransactionStatus.EXPIRED,
|
||||
data: JSON.stringify(transactionData)
|
||||
})
|
||||
|
||||
|
||||
stopPolling(`handlePendingTopupPoller-${paymentHash}`)
|
||||
}
|
||||
@@ -2678,7 +2702,7 @@ export const WalletTask: WalletTaskService = {
|
||||
syncStateWithMintQueue,
|
||||
syncStateWithMintTask,
|
||||
handleInFlightQueue,
|
||||
handlePendingTopupsQueue,
|
||||
handlePendingQueue,
|
||||
handlePendingTopupQueue,
|
||||
handleClaimQueue,
|
||||
handleNwcRequestQueue,
|
||||
|
||||
Reference in New Issue
Block a user