Faster processing of Nostr Walle Connect commands, stability improvements.

This commit is contained in:
minibits-cash
2025-01-16 14:44:34 +01:00
parent b3fd9b165d
commit 4e78da2245
23 changed files with 1470 additions and 1161 deletions
+29 -16
View File
@@ -8,7 +8,14 @@ import notifee from '@notifee/react-native'
import messaging from '@react-native-firebase/messaging'
import {AppRegistry} from 'react-native'
import { rootStoreInstance, setupRootStore } from './src/models'
import {WalletTask, NotificationService, NWC_CHANNEL_ID, TASK_QUEUE_CHANNEL_ID} from './src/services'
import {
WalletTask,
NotificationService,
SWAP_ALL_TASK,
TEST_TASK,
SYNC_STATE_WITH_ALL_MINTS_TASK,
HANDLE_NWC_REQUEST_TASK
} from './src/services'
import { log } from './src/services/logService'
import App from './src/App'
import {name as appName} from './app.json'
@@ -17,38 +24,44 @@ function BootstrapApp() {
return <App appName={appName} />
}
// Process nwc commands as long running headless tasks
// long running tasks
notifee.registerForegroundService(async (notification) => {
return new Promise(async (resolve) => {
log.trace('[registerForegroundService] Foreground service running for task:', notification)
try {
if(notification.data.task === 'handleNwcRequestFromNotification') {
if(notification.data.task === HANDLE_NWC_REQUEST_TASK) {
log.debug(`[registerForegroundService] Submitting task ${HANDLE_NWC_REQUEST_TASK} to the queue.`)
const {nwcStore} = rootStoreInstance
// if an app is in killed state, state is not loaded
if(nwcStore.all.length === 0) {
await setupRootStore(rootStoreInstance)
}
}
nwcStore.handleNwcRequestFromNotification(notification.data.data).catch((e) => {
log.error('[registerForegroundService]', e.message)
})
WalletTask.handleNwcRequestQueue({requestEvent: notification.data.data})
}
if(notification.data.task === 'syncSpendableStateWithMints') {
WalletTask.syncSpendableStateWithMints()
if(notification.data.task === SYNC_STATE_WITH_ALL_MINTS_TASK) {
log.debug(`[registerForegroundService] Submitting task ${SYNC_STATE_WITH_ALL_MINTS_TASK} to the queue.`)
WalletTask.syncStateWithAllMintsQueue({isPending: false})
}
if(notification.data.task === 'sendAll') {
WalletTask.sendAll()
if(notification.data.task === SWAP_ALL_TASK) {
log.debug(`[registerForegroundService] Submitting task ${SWAP_ALL_TASK} to the queue.`)
WalletTask.swapAllQueue()
}
/*if(notification.android.channelId === TASK_QUEUE_CHANNEL_ID) {
const tasksArray = notification.data.tasksToRun.split('|')
}*/
if(notification.data.task === TEST_TASK) {
log.debug(`[registerForegroundService] Submitting task ${TEST_TASK} to the queue.`)
WalletTask.testQueue()
}
} catch(e) {
log.error('[registerForegroundService]', e.message)
BIN
View File
Binary file not shown.
+4 -4
View File
@@ -1,6 +1,6 @@
{
"name": "minibits_wallet",
"version": "0.1.10-beta.20",
"version": "0.1.10-beta.21",
"private": true,
"scripts": {
"android:clean": "cd android && ./gradlew clean",
@@ -18,7 +18,7 @@
"adb:inspect": "adb shell input keyevent 82",
"postinstall": "patch-package --exclude 'nothing'"
},
"dependencies": {
"dependencies": {
"@cashu/cashu-ts": "minibits-cash/cashu-ts#2b51a7ba7a8461a0040359eecda37b98b603692c",
"@fortawesome/fontawesome-svg-core": "^6.6.0",
"@fortawesome/free-brands-svg-icons": "^6.6.0",
@@ -76,11 +76,11 @@
"react-native-svg": "^15.6.0",
"react-native-tab-view": "^3.5.2",
"react-native-url-polyfill": "^2.0.0",
"simple-js-task-queue": "^0.2.2",
"taskon": "^0.3.0",
"text-encoding-polyfill": "^0.6.7",
"util": "^0.12.5"
},
"devDependencies": {
"devDependencies": {
"@babel/core": "^7.20.0",
"@babel/plugin-proposal-export-namespace-from": "^7.18.9",
"@babel/preset-env": "^7.20.0",
-2
View File
@@ -18,8 +18,6 @@ export function Loading(props: ViewProps & { statusMessage?: string, textStyle?:
const $loading = (shiftedUp = false) => ({
flex: 1,
width: '100%',
height: '100%',
alignItems: 'center',
justifyContent: 'center',
backgroundColor: colors.dark.background,
+141 -175
View File
@@ -18,6 +18,7 @@ import {
NostrClient,
NostrEvent,
NostrUnsignedEvent,
SyncQueue,
TransactionTaskResult,
WalletTask
} from '../services'
@@ -35,6 +36,8 @@ import { PaymentRequest } from './PaymentRequest'
import { PaymentRequests } from './PaymentRequestsStore'
import { MINIBITS_MINT_URL } from '@env'
import { MintBalance } from './Mint'
import { transferTask } from '../services/wallet/transferTask'
import { topupTask } from '../services/wallet/topupTask'
type NwcError = {
result_type: string,
@@ -169,6 +172,7 @@ export const NwcConnectionModel = types.model('NwcConnection', {
}
// notify errors
if((nwcResponse as NwcError).error) {
let body = ''
if(nwcResponse.result_type === 'pay_invoice') {
@@ -189,147 +193,27 @@ export const NwcConnectionModel = types.model('NwcConnection', {
if(nwcResponse.result_type === 'lookup_invoice') {
body = 'Lookup invoice error: '
}
}
yield NotificationService.createLocalNotification(
`<b>${self.name}</b> - Nostr Wallet Connect`,
body + (nwcResponse as NwcError).error.message,
nwcPngUrl
)
}
const enabled = yield NotificationService.areNotificationsEnabled()
if(enabled) {
yield NotificationService.createLocalNotification(
`<b>${self.name}</b> - Nostr Wallet Connect`,
body + (nwcResponse as NwcError).error.message,
nwcPngUrl
)
}
}
NostrClient.publish(
yield NostrClient.publish(
responseEvent,
self.connectionRelays
)
NotificationService.stopForegroundService()
yield NotificationService.stopForegroundService()
}),
}))
.actions(self => ({
handleTransferTaskResult: flow(function* handleTransferTaskResult(result: TransactionTaskResult) {
log.debug('[handleTransferTaskResult] Got transfer task result', {
connection: self.name,
meltQuote: result.meltQuote?.quote,
txId: result.transaction?.id,
})
if(result.meltQuote?.quote === self.lastMeltQuoteId) {
log.error('Meltquote has been already handled, skipping...', {
meltQuoteId: self.lastMeltQuoteId,
caller: 'handleTransferTaskResult'
})
return
}
self.setLastMeltQuoteId(result.meltQuote?.quote)
let nwcResponse: NwcResponse | NwcError
if(result.transaction?.status === TransactionStatus.COMPLETED) {
const updatedLimit = self.remainingDailyLimit -
(result.transaction.amount + result.transaction.fee)
nwcResponse = {
result_type: 'pay_invoice',
result: {
preimage: result.preimage,
}
} as NwcResponse
log.trace('[handleTransferTaskResult] Updating remainingLimit', {
connection: self.name,
beforeUpdate: self.remainingDailyLimit,
afterUpdate: updatedLimit
})
self.setRemainingDailyLimit(updatedLimit)
// notify completed payment
const enabled = yield NotificationService.areNotificationsEnabled()
if(enabled) {
yield NotificationService.createLocalNotification(
`<b>${self.name}</b> - Nostr Wallet Connect`,
`Paid ${result.transaction.amount} SAT${result.transaction.fee > 0 ? ', fee ' + result.transaction.fee + ' SAT' : ''}. Remaining today's limit is ${self.remainingDailyLimit} SAT`,
nwcPngUrl
)
}
} else {
nwcResponse = {
result_type: 'pay_invoice',
error: { code: 'INTERNAL', message: result.message}
} as NwcError
}
if(!result.nwcEvent) {
log.error('Missing nwcEvent.', {caller: 'handleTransferTaskResult'})
NotificationService.stopForegroundService()
return
}
yield self.sendResponse(nwcResponse, result.nwcEvent)
}),
handleTopupTaskResult: flow(function* handleTopupTaskResult(result: TransactionTaskResult) {
log.debug('Got topup task result', {
connection: self.name,
encodedInvoice: result.encodedInvoice,
caller: 'handleTopupTaskResult'
})
if(!result.nwcEvent) {
log.error('Missing nwcEvent.', {caller: 'handleTopupTaskResult'})
return
}
let nwcResponse: NwcResponse | NwcError
if(result.paymentRequest) {
const pr: PaymentRequest = result.paymentRequest
nwcResponse = {
result_type: 'make_invoice',
result: {
type: 'incoming',
invoice: pr.encodedInvoice,
description: pr.description,
payment_hash: pr.paymentHash,
amount: pr.invoicedAmount * 1000,
fees_paid: 0,
created_at: Math.floor(pr.createdAt!.getTime() / 1000),
expires_at: Math.floor(pr.expiresAt!.getTime() / 1000),
preimage: null,
settled_at: null
} as NwcTransaction
} as NwcResponse
// notify created topup
const enabled = yield NotificationService.areNotificationsEnabled()
if(enabled) {
yield NotificationService.stopForegroundService()
yield NotificationService.createLocalNotification(
`<b>${self.name}</b> - Nostr Wallet Connect`,
`Invoice for ${result.paymentRequest.invoicedAmount} SATS has been created.`,
nwcPngUrl
)
}
} else {
nwcResponse = {
result_type: 'make_invoice',
error: { code: 'INTERNAL', message: result.message}
} as NwcError
}
yield self.sendResponse(nwcResponse, result.nwcEvent)
})
}))
.actions(self => ({
handleGetInfo (nwcRequest: NwcRequest): NwcResponse {
const nwcResponse = {
const nwcResponse: NwcResponse = {
result_type: nwcRequest.method,
result: {
alias: 'Minibits',
@@ -369,7 +253,7 @@ export const NwcConnectionModel = types.model('NwcConnection', {
} as NwcTransaction
})
const nwcResponse = {
const nwcResponse: NwcResponse = {
result_type: nwcRequest.method,
result: {
transactions
@@ -389,7 +273,7 @@ export const NwcConnectionModel = types.model('NwcConnection', {
resultBalanceMsat = 0
}
const nwcResponse = {
const nwcResponse: NwcResponse = {
result_type: nwcRequest.method,
result: {
balance: resultBalanceMsat
@@ -417,14 +301,56 @@ export const NwcConnectionModel = types.model('NwcConnection', {
} as NwcError
}
yield WalletTask.topup(
const result = yield topupTask(
mintBalance,
roundUp(amontMsat / 1000, 0),
'sat',
description,
undefined,
requestEvent
)
)
log.debug('Got topup task result', {
connection: self.name,
encodedInvoice: result.encodedInvoice,
caller: 'handleTopupTaskResult'
})
let nwcResponse: NwcResponse | NwcError
if(result.paymentRequest) {
const pr: PaymentRequest = result.paymentRequest
nwcResponse = {
result_type: 'make_invoice',
result: {
type: 'incoming',
invoice: pr.encodedInvoice,
description: pr.description,
payment_hash: pr.paymentHash,
amount: pr.invoicedAmount * 1000,
fees_paid: 0,
created_at: Math.floor(pr.createdAt!.getTime() / 1000),
expires_at: Math.floor(pr.expiresAt!.getTime() / 1000),
preimage: null,
settled_at: null
} as NwcTransaction
} as NwcResponse
yield NotificationService.createLocalNotification(
`<b>${self.name}</b> - Nostr Wallet Connect`,
`Invoice for ${result.paymentRequest.invoicedAmount} SATS has been created.`,
nwcPngUrl
)
} else {
nwcResponse = {
result_type: 'make_invoice',
error: { code: 'INTERNAL', message: result.message}
} as NwcError
}
return nwcResponse
}),
handleLookupInvoice(nwcRequest: NwcRequest) {
const paymentRequestsStore = self.getPaymentRequestsStore()
@@ -490,13 +416,11 @@ export const NwcConnectionModel = types.model('NwcConnection', {
const feeReserve = Math.max(MIN_LIGHTNING_FEE, amountToPay * LIGHTNING_FEE_PERCENT / 100)
const totalAmountToPay = amountToPay + feeReserve
let mintBalance: MintBalance | undefined = undefined
let isMinibitsMintSelected: boolean = false
let mintBalance: MintBalance | undefined = undefined
const minibitsBalance = proofsStore.getMintBalance(MINIBITS_MINT_URL)
if(minibitsBalance && minibitsBalance.balances.sat! >= totalAmountToPay) {
mintBalance = minibitsBalance
isMinibitsMintSelected = true
mintBalance = minibitsBalance
} else {
mintBalance = proofsStore.getMintBalanceWithMaxBalance('sat')
}
@@ -525,7 +449,7 @@ export const NwcConnectionModel = types.model('NwcConnection', {
encoded,
)
WalletTask.transfer(
const result = yield transferTask(
mintBalance,
amountToPay,
'sat',
@@ -536,11 +460,51 @@ export const NwcConnectionModel = types.model('NwcConnection', {
requestEvent
)
if(result.meltQuote?.quote === self.lastMeltQuoteId) {
throw new AppError(Err.ALREADY_EXISTS_ERROR, 'Already processed', {quote: result.meltQuote?.quote})
}
self.setLastMeltQuoteId(result.meltQuote?.quote)
let nwcResponse: NwcResponse | NwcError
if(result.transaction?.status === TransactionStatus.COMPLETED) {
const updatedLimit = self.remainingDailyLimit -
(result.transaction.amount + result.transaction.fee)
nwcResponse = {
result_type: 'pay_invoice',
result: {
preimage: result.preimage,
}
} as NwcResponse
log.trace('[handleTransferTaskResult] Updating remainingLimit', {
connection: self.name,
beforeUpdate: self.remainingDailyLimit,
afterUpdate: updatedLimit
})
self.setRemainingDailyLimit(updatedLimit)
yield NotificationService.createLocalNotification(
`<b>${self.name}</b> - Nostr Wallet Connect`,
`Paid ${result.transaction.amount} SAT${result.transaction.fee > 0 ? ', fee ' + result.transaction.fee + ' SAT' : ''}. Remaining today's limit is ${self.remainingDailyLimit} SAT`,
nwcPngUrl
)
} else {
nwcResponse = {
result_type: 'pay_invoice',
error: { code: 'INTERNAL', message: result.message}
} as NwcError
}
return nwcResponse
} catch (e: any) {
log.error(`[NwcConnection.handlePayInvoice] ${e.message}`)
yield NotificationService.stopForegroundService()
return {
result_type: nwcRequest.method,
error: { code: 'INTERNAL', message: e.message}
@@ -549,7 +513,7 @@ export const NwcConnectionModel = types.model('NwcConnection', {
})
}))
.actions(self => ({
handleRequest: flow(function* handleRequest(requestEvent: NostrEvent, decryptedNwcRequest?: NwcRequest) {
handleNwcRequestTask: flow(function* handleNwcRequestTask(requestEvent: NostrEvent, decryptedNwcRequest?: NwcRequest) {
let nwcRequest: NwcRequest
if(!decryptedNwcRequest) {
const decryptedContent = yield NostrClient.decryptNip04(requestEvent.pubkey, requestEvent.content)
@@ -575,25 +539,12 @@ export const NwcConnectionModel = types.model('NwcConnection', {
break
case 'make_invoice':
nwcResponse = yield self.handleMakeInvoice(nwcRequest, requestEvent) as Promise<NwcError | undefined>
// no early error, topup initiated, register topup result event handler and exit
if(!nwcResponse) {
EventEmitter.on('ev_topupTask_result', self.handleTopupTaskResult)
return
}
break
case 'lookup_invoice':
nwcResponse = self.handleLookupInvoice(nwcRequest)
break
case 'pay_invoice':
// only early errors are immediately returned
case 'pay_invoice':
nwcResponse = yield self.handlePayInvoice(nwcRequest, requestEvent) as Promise<NwcError | undefined>
// no early error, transfer initiated, register transfer result event handler and exit
if(!nwcResponse) {
EventEmitter.on('ev_transferTask_result', self.handleTransferTaskResult)
return
}
break
default:
const message = `NWC method ${nwcRequest.method} is unknown or not yet supported.`
@@ -711,7 +662,7 @@ export const NwcStoreModel = types
log.debug('[remove]', 'Connection removed from NwcStore')
}
},
receiveNwcEvents () {
receiveNwcEventsQueue () {
log.trace('[receiveNwcEvents] start listening for NWC events', {
walletPubkey: self.walletPubkey
})
@@ -754,8 +705,13 @@ export const NwcStoreModel = types
if(!targetConnection) {
throw new AppError(Err.VALIDATION_ERROR, 'Missing connection matching event pubkey', {pubkey: event.pubkey})
}
// dispatch to correct connection
targetConnection.handleRequest(event)
// dispatch to correct connection, process over sync queue
const now = new Date().getTime()
SyncQueue.addTask(
`handleNwcRequestTask-${now}`,
async () => await targetConnection.handleNwcRequestTask(event)
)
},
oneose() {
log.trace('[receiveNwcEvents]', `Eose: Got ${eventsBatch.length} NWC events`)
@@ -768,7 +724,7 @@ export const NwcStoreModel = types
return
}
},
handleNwcRequestFromNotification: flow(function* handleNwcRequestFromNotification(event: NostrEvent, decryptedNwcRequest: NwcRequest) {
handleNwcRequestTask: flow(function* handleNwcRequestTask(event: NostrEvent, decryptedNwcRequest?: NwcRequest) {
// find connection the nwc request is sent to
const targetConnection = self.nwcConnections.find(c =>
c.connectionPubkey === event.pubkey
@@ -776,21 +732,31 @@ export const NwcStoreModel = types
if(!targetConnection) {
log.error('[handleNwcRequestFromNotification] Missing connection matching event pubkey.', {pubkey: event.pubkey})
// notify created topup
const enabled = yield NotificationService.areNotificationsEnabled()
if(enabled) {
yield NotificationService.stopForegroundService()
yield NotificationService.createLocalNotification(
`<b>Nostr Wallet Connect</b> error`,
`Your wallet has received a NWC command, but could not find related NWC connection to handle it.`,
nwcPngUrl
)
}
yield NotificationService.stopForegroundService()
yield NotificationService.createLocalNotification(
`<b>Nostr Wallet Connect</b> error`,
`Your wallet has received a NWC command, but could not find related NWC connection to handle it.`,
nwcPngUrl
)
return
}
yield targetConnection.handleRequest(event, decryptedNwcRequest)
if(!event) {
log.error('[handleNwcRequestFromNotification] Missing connection matching event pubkey.')
yield NotificationService.stopForegroundService()
yield NotificationService.createLocalNotification(
`<b>Nostr Wallet Connect</b> error`,
`Your wallet has received a NWC command, but could not retrieve the required data.`,
nwcPngUrl
)
return
}
yield targetConnection.handleNwcRequestTask(event, decryptedNwcRequest)
})
}))
.views(self => ({
File diff suppressed because it is too large Load Diff
+12 -23
View File
@@ -36,6 +36,7 @@ import numbro from 'numbro'
import { TranItem } from './TranDetailScreen'
import { translate } from '../i18n'
import { Token, getDecodedToken } from '@cashu/cashu-ts'
import { RECEIVE_OFFLINE_PREPARE_TASK, RECEIVE_TASK } from '../services/wallet/receiveTask'
export enum ReceiveOption {
// CREATE_AND_SEND_PAYMENT_REQUEST = 'CREATE_AND_SEND_PAYMENT_REQUEST',
@@ -125,14 +126,14 @@ export const ReceiveScreen: FC<WalletStackScreenProps<'Receive'>> = observer(
if(isReceiveTaskSentToQueue) {
EventEmitter.on('ev_receiveTask_result', handleReceiveTaskResult)
EventEmitter.on('ev_receiveOfflinePrepareTask_result', handleReceiveTaskResult)
EventEmitter.on(`ev_${RECEIVE_TASK}_result`, handleReceiveTaskResult)
EventEmitter.on(`ev_${RECEIVE_OFFLINE_PREPARE_TASK}_result`, handleReceiveTaskResult)
}
// Unsubscribe from the 'sendCompleted' event on component unmount
return () => {
EventEmitter.off('ev_receiveTask_result', handleReceiveTaskResult)
EventEmitter.off('ev_receiveOfflinePrepareTask_result', handleReceiveTaskResult)
EventEmitter.off(`ev_${RECEIVE_TASK}_result`, handleReceiveTaskResult)
EventEmitter.off(`ev_${RECEIVE_OFFLINE_PREPARE_TASK}_result`, handleReceiveTaskResult)
}
}, [isReceiveTaskSentToQueue])
@@ -208,24 +209,12 @@ export const ReceiveScreen: FC<WalletStackScreenProps<'Receive'>> = observer(
const amountToReceiveInt = round(toNumber(amountToReceive) * getCurrency(unit).precision, 0)
const proofsCount = token!.proofs.length
if(proofsCount > MAX_SWAP_INPUT_SIZE) {
WalletTask.receiveBatch(
token as Token,
amountToReceiveInt,
memo,
encodedToken as string,
)
} else {
WalletTask.receive(
token as Token,
amountToReceiveInt,
memo,
encodedToken as string,
)
}
WalletTask.receiveQueue(
token as Token,
amountToReceiveInt,
memo,
encodedToken as string,
)
}
@@ -262,7 +251,7 @@ export const ReceiveScreen: FC<WalletStackScreenProps<'Receive'>> = observer(
const amountToReceiveInt = round(toNumber(amountToReceive) * getCurrency(unit).precision, 0)
WalletTask.receiveOfflinePrepare(
WalletTask.receiveOfflinePrepareQueue(
token as Token,
amountToReceiveInt,
memo,
+9 -63
View File
@@ -19,7 +19,7 @@ import {useHeader} from '../utils/useHeader'
import {log} from '../services/logService'
import AppError from '../utils/AppError'
import { useStores } from '../models'
import { SyncStateTaskResult, WalletTask } from '../services/walletService'
import { SYNC_STATE_WITH_ALL_MINTS_TASK, SyncStateTaskResult } from '../services/walletService'
import EventEmitter from '../utils/eventEmitter'
import { translate } from '../i18n'
import { NotificationService, TASK_QUEUE_CHANNEL_ID, TASK_QUEUE_CHANNEL_NAME } from '../services/notificationService'
@@ -38,8 +38,6 @@ export const RecoveryOptionsScreen: FC<AppStackScreenProps<'RecoveryOptions'>> =
const [error, setError] = useState<AppError | undefined>()
const [info, setInfo] = useState('')
const [isSyncStateSentToQueue, setIsSyncStateSentToQueue] = useState<boolean>(false)
const [totalSpentAmount, setTotalSpentAmount] = useState<number>(0)
const [syncErrors, setSyncErrors] = useState<AppError[]>([])
useEffect(() => {
@@ -49,70 +47,20 @@ export const RecoveryOptionsScreen: FC<AppStackScreenProps<'RecoveryOptions'>> =
if (!isSyncStateSentToQueue) { return false }
setIsLoading(false)
// runs per each mint
if (result.error && result.error) {
setSyncErrors(prev => [...prev, result.error!])
}
if (result && result.transactionStateUpdates.length > 0) {
log.trace('[removeSpentByMintTaskResult]', {transactionStateUpdates: result.transactionStateUpdates})
let totalSpentPerMint = 0
for (const update of result.transactionStateUpdates) {
if(update.spentByMintAmount) {
totalSpentPerMint += update.spentByMintAmount
}
}
setTotalSpentAmount(prev => prev + totalSpentPerMint)
}
if(totalSpentAmount === 0 && result.transactionStateUpdates.length === 0) {
setInfo(`No spent ecash from ${new URL(result.mintUrl).hostname}.`)
}
setInfo(result.message)
}
if(isSyncStateSentToQueue) {
EventEmitter.on('ev__syncStateWithMintTask_result', removeSpentByMintTaskResult)
EventEmitter.on(`ev_${SYNC_STATE_WITH_ALL_MINTS_TASK}_result`, removeSpentByMintTaskResult)
}
return () => {
EventEmitter.off('ev__syncStateWithMintTask_result', removeSpentByMintTaskResult)
NotificationService.stopForegroundService()
EventEmitter.off(`ev_${SYNC_STATE_WITH_ALL_MINTS_TASK}_result`, removeSpentByMintTaskResult)
}
}, [isSyncStateSentToQueue])
useEffect(() => {
const showSpentEcashResult = () => {
log.trace('[showSpentEcashResult] got update', {totalSpentAmount})
if (totalSpentAmount === 0) { return false }
setInfo(`Removed spent ecash with amount ${totalSpentAmount}.`)
}
showSpentEcashResult()
}, [totalSpentAmount])
useEffect(() => {
const showSyncErrors = () => {
log.trace('[showSyncErrors] got update', {syncErrors})
if (syncErrors.length === 0) { return false }
setError(syncErrors[syncErrors.length - 1])
}
showSyncErrors()
}, [syncErrors])
const gotoSeedRecovery = function () {
navigation.navigate('SeedRecovery')
}
@@ -131,11 +79,10 @@ export const RecoveryOptionsScreen: FC<AppStackScreenProps<'RecoveryOptions'>> =
const checkSpent = async function () {
setIsLoading(true)
setIsSyncStateSentToQueue(true)
// long task background processing
notifee.displayNotification({
await notifee.displayNotification({
title: TASK_QUEUE_CHANNEL_NAME,
body: 'Processing pending transactions...',
body: 'Cleaning spent ecash from spendable balance...',
android: {
channelId: TASK_QUEUE_CHANNEL_ID,
asForegroundService: true,
@@ -145,9 +92,8 @@ export const RecoveryOptionsScreen: FC<AppStackScreenProps<'RecoveryOptions'>> =
indeterminate: true,
},
},
data: {task: 'syncSpendableStateWithMints'},
})
// WalletTask.syncSpendableStateWithMints()
data: {task: SYNC_STATE_WITH_ALL_MINTS_TASK},
})
}
+7 -6
View File
@@ -37,7 +37,7 @@ import {
} from '../components'
import {TransactionStatus, Transaction} from '../models/Transaction'
import {useStores} from '../models'
import {NostrClient, SyncStateTaskResult, TransactionTaskResult, WalletTask} from '../services'
import {NostrClient, SYNC_STATE_WITH_MINT_TASK, SyncStateTaskResult, TransactionTaskResult, WalletTask} from '../services'
import {log} from '../services/logService'
import AppError, {Err} from '../utils/AppError'
import {translate} from '../i18n'
@@ -60,6 +60,7 @@ import { MemoInputCard } from '../components/MemoInputCard'
import { PaymentRequest as CashuPaymentRequest, PaymentRequestTransport, PaymentRequestTransportType, decodePaymentRequest, getDecodedToken } from '@cashu/cashu-ts'
import { ProfilePointer } from 'nostr-tools/nip19'
import { MINIBITS_NIP05_DOMAIN } from '@env'
import { SEND_TASK } from '../services/wallet/sendTask'
export enum SendOption {
SEND_TOKEN = 'SEND_TOKEN',
@@ -447,12 +448,12 @@ export const SendScreen: FC<WalletStackScreenProps<'Send'>> = observer(
// Subscribe to the 'sendCompleted' event
if(isSendTaskSentToQueue) {
EventEmitter.on('ev_sendTask_result', handleSendTaskResult)
EventEmitter.on(`ev_${SEND_TASK}_result`, handleSendTaskResult)
}
// Unsubscribe from the 'sendCompleted' event on component unmount
return () => {
EventEmitter.off('ev_sendTask_result', handleSendTaskResult)
EventEmitter.off(`ev_${SEND_TASK}_result`, handleSendTaskResult)
}
}, [isSendTaskSentToQueue])
@@ -508,12 +509,12 @@ export const SendScreen: FC<WalletStackScreenProps<'Send'>> = observer(
// Subscribe to the '_syncStateWithMintTask' event
if(transactionId) {
EventEmitter.on('ev__syncStateWithMintTask_result', handleSendCompleted)
EventEmitter.on(`ev_${SYNC_STATE_WITH_MINT_TASK}_result`, handleSendCompleted)
}
// Unsubscribe from the '_syncStateWithMintTask' event on component unmount
return () => {
EventEmitter.off('ev__syncStateWithMintTask_result', handleSendCompleted)
EventEmitter.off(`ev_${SYNC_STATE_WITH_MINT_TASK}_result`, handleSendCompleted)
}
}, [transactionId])
@@ -603,7 +604,7 @@ export const SendScreen: FC<WalletStackScreenProps<'Send'>> = observer(
const amountToSendInt = round(toNumber(amountToSend) * getCurrency(unitRef.current).precision, 0)
WalletTask.send(
WalletTask.sendQueue(
mintBalanceToSendFrom as MintBalance,
amountToSendInt,
unitRef.current,
+7 -5
View File
@@ -29,6 +29,7 @@ import {
import {TransactionStatus, Transaction} from '../models/Transaction'
import {useStores} from '../models'
import {
HANDLE_PENDING_TOPUP_TASK,
NostrClient,
NostrProfile,
TransactionTaskResult,
@@ -64,6 +65,7 @@ import numbro from 'numbro'
import {MintListItem} from './Mints/MintListItem'
import {TranItem} from './TranDetailScreen'
import {translate} from '../i18n'
import { TOPUP_TASK } from '../services/wallet/topupTask'
if (
Platform.OS === 'android' &&
@@ -303,11 +305,11 @@ export const TopupScreen: FC<WalletStackScreenProps<'Topup'>> = observer(
}
// Subscribe to the 'sendCompleted' event
EventEmitter.on('ev_topupTask_result', handleTopupTaskResult)
EventEmitter.on(`ev_${TOPUP_TASK}_result`, handleTopupTaskResult)
// Unsubscribe from the 'sendCompleted' event on component unmount
return () => {
EventEmitter.off('ev_topupTask_result', handleTopupTaskResult)
EventEmitter.off(`ev_${TOPUP_TASK}_result`, handleTopupTaskResult)
}
}, [isTopupTaskSentToQueue])
@@ -346,13 +348,13 @@ export const TopupScreen: FC<WalletStackScreenProps<'Topup'>> = observer(
}
EventEmitter.on(
'ev__handlePendingTopupTask_result',
`ev_${HANDLE_PENDING_TOPUP_TASK}_result`,
handlePendingTopupTaskResult,
)
return () => {
EventEmitter.off(
'ev__handlePendingTopupTask_result',
`ev_${HANDLE_PENDING_TOPUP_TASK}_result`,
handlePendingTopupTaskResult,
)
}
@@ -470,7 +472,7 @@ export const TopupScreen: FC<WalletStackScreenProps<'Topup'>> = observer(
0,
)
WalletTask.topup(
WalletTask.topupQueue(
mintBalanceToTopup as MintBalance,
amountToTopupInt,
unit,
+17 -15
View File
@@ -41,7 +41,7 @@ import {
import AppError, {Err} from '../utils/AppError'
import {log} from '../services/logService'
import {isArray} from 'lodash'
import {NostrClient, NostrEvent, NostrProfile, TransactionTaskResult, WalletTask} from '../services'
import {HANDLE_PENDING_TOPUP_TASK, NostrClient, NostrEvent, NostrProfile, TransactionTaskResult, WalletTask} from '../services'
import {Proof} from '../models/Proof'
import useColorScheme from '../theme/useThemeColor'
import useIsInternetReachable from '../utils/useIsInternetReachable'
@@ -58,6 +58,8 @@ import { QRCodeBlock } from './Wallet/QRCode'
import { MintListItem } from './Mints/MintListItem'
import { Token, getDecodedToken } from '@cashu/cashu-ts'
import { faTruckMedical } from '@fortawesome/free-solid-svg-icons'
import { RECEIVE_OFFLINE_COMPLETE_TASK, RECEIVE_TASK } from '../services/wallet/receiveTask'
import { REVERT_TASK } from '../services/wallet/revertTask'
if (Platform.OS === 'android' && UIManager.setLayoutAnimationEnabledExperimental) {
UIManager.setLayoutAnimationEnabledExperimental(true)
@@ -603,12 +605,12 @@ const ReceiveInfoBlock = function (props: {
// Subscribe to the 'receiveTask' event
if(isReceiveTaskSentToQueue === true) {
EventEmitter.on('ev_receiveTask_result', handleReceiveTaskResult)
EventEmitter.on(`ev_${RECEIVE_TASK}_result`, handleReceiveTaskResult)
}
// Unsubscribe from the 'receiveTask' event on component unmount
return () => {
EventEmitter.off('ev_receiveTask_result', handleReceiveTaskResult)
EventEmitter.off(`ev_${RECEIVE_TASK}_result`, handleReceiveTaskResult)
}
}, [isReceiveTaskSentToQueue])
@@ -647,7 +649,7 @@ const ReceiveInfoBlock = function (props: {
}
setIsReceiveTaskSentToQueue(true)
WalletTask.receive(
WalletTask.receiveQueue(
tokenToRetry,
amountToReceive,
memo,
@@ -886,12 +888,12 @@ const ReceiveOfflineInfoBlock = function (props: {
// Subscribe to the 'sendCompleted' event
if(isReceiveOfflineCompleteTaskSentToQueue) {
EventEmitter.on('ev_receiveOfflineCompleteTask_result', handleReceiveOfflineCompleteTaskResult)
EventEmitter.on(`ev_${RECEIVE_OFFLINE_COMPLETE_TASK}_result`, handleReceiveOfflineCompleteTaskResult)
}
// Unsubscribe from the 'sendCompleted' event on component unmount
return () => {
EventEmitter.off('ev_receiveOfflineCompleteTask_result', handleReceiveOfflineCompleteTaskResult)
EventEmitter.off(`ev_${RECEIVE_OFFLINE_COMPLETE_TASK}_result`, handleReceiveOfflineCompleteTaskResult)
}
}, [isReceiveOfflineCompleteTaskSentToQueue])
@@ -902,7 +904,7 @@ const ReceiveOfflineInfoBlock = function (props: {
const receiveOfflineComplete = async function () {
setIsLoading(true)
setIsReceiveOfflineCompleteTaskSentToQueue(true)
WalletTask.receiveOfflineComplete(transaction.id!)
WalletTask.receiveOfflineCompleteQueue(transaction.id!)
}
const onGoBack = () => {
@@ -1095,12 +1097,12 @@ const SendInfoBlock = function (props: {
// Subscribe to the 'sendCompleted' event
if(isRevertTaskSentToQueue) {
EventEmitter.on('ev_revertTask_result', handleRevertTaskResult)
EventEmitter.on(`ev_${REVERT_TASK}_result`, handleRevertTaskResult)
}
// Unsubscribe from the 'sendCompleted' event on component unmount
return () => {
EventEmitter.off('ev_revertTask_result', handleRevertTaskResult)
EventEmitter.off(`ev_${REVERT_TASK}_result`, handleRevertTaskResult)
}
}, [isRevertTaskSentToQueue])
@@ -1135,7 +1137,7 @@ const SendInfoBlock = function (props: {
})
}
WalletTask.revert(transaction)
WalletTask.revertQueue(transaction)
setIsLoading(true)
setIsRevertTaskSentToQueue(true)
@@ -1308,7 +1310,7 @@ const TopupInfoBlock = function (props: {
setIsLoading(false)
// do not react to an active poller :)
if(pollerExists(`handlePendingTopupTaskPoller-${result.paymentHash}`)) {
if(pollerExists(`handlePendingTopupPoller-${result.paymentHash}`)) {
return false
}
@@ -1328,11 +1330,11 @@ const TopupInfoBlock = function (props: {
}
// Subscribe to the 'sendCompleted' event
EventEmitter.on('ev__handlePendingTopupTask_result', handlePendingTopupTaskResult)
EventEmitter.on(`ev_${HANDLE_PENDING_TOPUP_TASK}_result`, handlePendingTopupTaskResult)
// Unsubscribe from the 'sendCompleted' event on component unmount
return () => {
EventEmitter.off('ev__handlePendingTopupTask_result', handlePendingTopupTaskResult)
EventEmitter.off(`ev_${HANDLE_PENDING_TOPUP_TASK}_result`, handlePendingTopupTaskResult)
}
}, [isPendingTopupTaskSentToQueue]))
@@ -1348,7 +1350,7 @@ const TopupInfoBlock = function (props: {
setIsLoading(true)
setIsPendingTopupTaskSentToQueue(true)
WalletTask.handlePendingTopup(
WalletTask.handlePendingTopupQueue(
{paymentRequest}
)
@@ -1895,7 +1897,7 @@ const getPaymentRequestToRetry = (
const paymentRequest: PaymentRequest = pendingRecord.paymentRequest
if(!paymentRequest) {return undefined}
if(pollerExists(`handlePendingTopupTaskPoller-${paymentRequest.paymentHash}`)) {return undefined}
if(pollerExists(`handlePendingTopupPoller-${paymentRequest.paymentHash}`)) {return undefined}
// return paymentRequest to retry if wallet somehow failed to retreive proofs for paid invoice
return paymentRequest
+4 -3
View File
@@ -50,6 +50,7 @@ import { TranItem } from './TranDetailScreen'
import useIsInternetReachable from '../utils/useIsInternetReachable'
import { translate } from '../i18n'
import { MemoInputCard } from '../components/MemoInputCard'
import { TRANSFER_TASK } from '../services/wallet/transferTask'
if (
@@ -428,12 +429,12 @@ useEffect(() => {
// Subscribe to the task result event
if(isTransferTaskSentToQueue) {
EventEmitter.on('ev_transferTask_result', handleTransferTaskResult)
EventEmitter.on(`ev_${TRANSFER_TASK}_result`, handleTransferTaskResult)
}
// Unsubscribe from the task result event on component unmount
return () => {
EventEmitter.off('ev_transferTask_result', handleTransferTaskResult)
EventEmitter.off(`ev_${TRANSFER_TASK}_result`, handleTransferTaskResult)
}
}, [isTransferTaskSentToQueue])
@@ -636,7 +637,7 @@ const transfer = async function () {
const amountToTransferInt = round(toNumber(amountToTransfer) * getCurrency(unit).precision, 0)
WalletTask.transfer(
WalletTask.transferQueue(
mintBalanceToTransferFrom,
amountToTransferInt,
unit,
+11 -12
View File
@@ -42,7 +42,7 @@ import {useStores} from '../models'
import {WalletStackScreenProps} from '../navigation'
import {Mint, UnitBalance} from '../models/Mint'
import {MintsByUnit} from '../models/MintsStore'
import {log, NostrClient, WalletTaskResult} from '../services'
import {HANDLE_CLAIM_TASK, HANDLE_RECEIVED_EVENT_TASK, log, NostrClient, WalletTaskResult} from '../services'
import {Env} from '../utils/envtypes'
import {Transaction} from '../models/Transaction'
import {TransactionListItem} from './Transactions/TransactionListItem'
@@ -64,7 +64,6 @@ import { CurrencyCode, MintUnit, MintUnitCurrencyPairs, convertToFromSats, getCu
import { CurrencyAmount } from './Wallet/CurrencyAmount'
import { LeftProfileHeader } from './ContactsScreen'
import { getUnixTime } from 'date-fns/getUnixTime'
import { minibitsPngIcon } from '../components/MinibitsIcon'
const deploymentKey = APP_ENV === Env.PROD ? CODEPUSH_PRODUCTION_DEPLOYMENT_KEY : CODEPUSH_STAGING_DEPLOYMENT_KEY
const MINT_CHECK_INTERVAL = 60
@@ -172,7 +171,7 @@ export const WalletScreen: FC<WalletScreenProps> = observer(
}
// Only once on startup - Create websocket subscriptions to receive tokens or payment requests by NOSTR DMs
WalletTask.receiveEventsFromRelays().catch(e => false)
WalletTask.receiveEventsFromRelaysQueue()
// Set wallet tab to preferred unit
const preferredUnit: MintUnit = userSettingsStore.preferredUnit
@@ -185,7 +184,7 @@ export const WalletScreen: FC<WalletScreenProps> = observer(
// go through websockets only if remote notifications not working as push data messages are
// delivered even if notifications are disabled on device
const isRemoteDataPushEnabled = walletProfileStore.device ? true : false
if(!isRemoteDataPushEnabled) {nwcStore.receiveNwcEvents()}
if(!isRemoteDataPushEnabled) {nwcStore.receiveNwcEventsQueue()}
}
const handleReceivedEventTaskResult = async (result: WalletTaskResult) => {
@@ -203,16 +202,16 @@ export const WalletScreen: FC<WalletScreenProps> = observer(
}
Linking.addEventListener('url', handleDeeplink)
EventEmitter.on('ev__handleReceivedEventTask_result', handleReceivedEventTaskResult)
EventEmitter.on('ev__handleClaimTask_result', handleClaimTaskResult)
EventEmitter.on(`ev_${HANDLE_RECEIVED_EVENT_TASK}_result`, handleReceivedEventTaskResult)
EventEmitter.on(`ev_${HANDLE_CLAIM_TASK}_result`, handleClaimTaskResult)
getInitialData()
// Unsubscribe from the task result event on component unmount
return () => {
EventEmitter.off('ev__handleReceivedEventTask_result', handleReceivedEventTaskResult)
EventEmitter.off('ev__handleClaimTask_result', handleClaimTaskResult)
EventEmitter.off(`ev_${HANDLE_RECEIVED_EVENT_TASK}_result`, handleReceivedEventTaskResult)
EventEmitter.off(`ev_${HANDLE_CLAIM_TASK}_result`, handleClaimTaskResult)
}
}, [])
@@ -279,10 +278,10 @@ export const WalletScreen: FC<WalletScreenProps> = observer(
if (nowInSec - lastMintCheckRef.current > MINT_CHECK_INTERVAL) {
lastMintCheckRef.current = nowInSec
WalletTask.handleInFlight()
WalletTask.handleClaim()
WalletTask.syncPendingStateWithMints()
WalletTask.handlePendingTopups()
WalletTask.handleInFlightQueue()
WalletTask.handleClaimQueue()
WalletTask.syncStateWithAllMintsQueue({isPending: true})
WalletTask.handlePendingTopupsQueue()
if(userSettingsStore.exchangeCurrency) {
walletStore.refreshExchangeRate(userSettingsStore.exchangeCurrency)
+2 -2
View File
@@ -97,10 +97,10 @@ const reconnectToRelays = async function () {
// recreate subscriptions if all relays down
if(isRefreshSubNeeded) {
log.trace('[reconnectToRelays] Refreshing Nostr subscriptions')
WalletTask.receiveEventsFromRelays().catch(e => false)
WalletTask.receiveEventsFromRelaysQueue().catch(e => false)
if(!walletProfileStore.device) {
nwcStore.receiveNwcEvents()
nwcStore.receiveNwcEventsQueue()
}
}
}
+18 -4
View File
@@ -11,6 +11,7 @@ import AppError, { Err } from '../utils/AppError'
import { Platform } from 'react-native'
import { rootStoreInstance, setupRootStore } from '../models'
import { NwcRequest, nwcPngUrl } from '../models/NwcStore';
import { HANDLE_NWC_REQUEST_TASK } from './walletService'
export type NotifyReceiveToLnurlData = {
type: 'NotifyReceiveToLnurlData',
@@ -36,7 +37,10 @@ const NWC_CHANNEL_ID = 'nwcDefault';
const NWC_CHANNEL_NAME = 'Minibits NWC payment'
export const TASK_QUEUE_CHANNEL_ID = 'internalDefault'
export const TASK_QUEUE_CHANNEL_NAME = 'Minibits internal tasks'
export const TASK_QUEUE_CHANNEL_NAME = 'Minibits tasks'
export const TEST_CHANNEL_ID = 'testDefault'
export const TEST_CHANNEL_NAME = 'Minibits test tasks'
export const initNotifications = async () => {
let enabled = await areNotificationsEnabled()
@@ -93,6 +97,16 @@ export const initNotifications = async () => {
}
})
notifee.isChannelCreated(TEST_CHANNEL_ID).then(isCreated => {
if (!isCreated) {
notifee.createChannel({
id: TEST_CHANNEL_ID,
name: TASK_QUEUE_CHANNEL_NAME,
sound: 'default',
})
}
})
}
@@ -176,7 +190,7 @@ const _nwcRequestHandler = async function(remoteData: NotifyNwcRequestData) {
await notifee.displayNotification({
title: NWC_CHANNEL_NAME,
body: 'Nostr wallet command is being processed.',
body: 'Processing remote NWC command...',
android: {
channelId: NWC_CHANNEL_ID,
asForegroundService: true,
@@ -186,10 +200,10 @@ const _nwcRequestHandler = async function(remoteData: NotifyNwcRequestData) {
indeterminate: true,
},
},
data: {task: 'handleNwcRequestFromNotification', data: cleanedRequestEvent}, // Pass the task data to the foreground service
data: {task: HANDLE_NWC_REQUEST_TASK, data: cleanedRequestEvent}, // Pass the task data to the foreground service
})
} else {
await nwcStore.handleNwcRequestFromNotification(requestEvent, nwcRequest)
await nwcStore.handleNwcRequestTask(requestEvent, nwcRequest)
}
}
+14 -12
View File
@@ -1,11 +1,11 @@
import { Task, TaskId, TaskQueue, TaskStatus } from "simple-js-task-queue"
import { Task, TaskId, TaskQueue, TaskStatus } from "taskon"
import {log} from './logService'
import EventEmitter from '../utils/eventEmitter'
import { TransactionTaskResult, WalletTaskResult } from "./walletService"
import { NotificationService } from "./notificationService"
let _queue: any = undefined
// const start = new Date().getTime()
const getSyncQueue = function () {
if(!_queue) {
@@ -15,12 +15,12 @@ const getSyncQueue = function () {
stopOnError: false,
// verbose: true,
taskPrioritizationMode: "head",
// memorizeTasks: true
memorizeTasks: true
})
return _queue
return _queue as TaskQueue
}
return _queue
return _queue as TaskQueue
}
const addTask = function (taskId: TaskId, task: Promise<any> | any) {
@@ -54,18 +54,19 @@ const addPrioritizedTask = function (taskId: TaskId, task: Promise<any> | any) {
const _handleTaskResult = async (taskId: TaskId, result: WalletTaskResult | TransactionTaskResult) => {
log.info(
`[_handleTaskResult] The result of task ${taskId}`, result
)
const queue: TaskQueue = getSyncQueue()
)
EventEmitter.emit(`ev_${result.taskFunction}_result`, result)
/* if(queue.getAllTasksDetails(['idle', 'running']).length === 0) {
if(await NotificationService.isNotificationDispayed()) {
const queue: TaskQueue = getSyncQueue()
if(queue.getAllTasksDetails(['idle', 'running']).length === 0) {
if(await NotificationService.isNotificationDispayed()) {
log.trace('[_handleTaskResult] stopForegroundService')
NotificationService.stopForegroundService()
}
} */
}
}
// Helper function to handle the task status changes
@@ -76,7 +77,8 @@ const _handleTaskStatusChange = (status: TaskStatus, task: Task) => {
}
export const SyncQueue = {
export const SyncQueue = {
getSyncQueue,
addTask,
addPrioritizedTask,
}
+15 -13
View File
@@ -24,9 +24,9 @@ const {
} = rootStoreInstance
// function names to pass to task results
const RECEIVE = 'receiveTask'
const RECEIVE_OFFLINE_PREPARE = 'receiveOfflinePrepareTask'
const RECEIVE_OFFLINE_COMPLETE = 'receiveOfflineCompleteTask'
export const RECEIVE_TASK = 'receiveTask'
export const RECEIVE_OFFLINE_PREPARE_TASK = 'receiveOfflinePrepareTask'
export const RECEIVE_OFFLINE_COMPLETE_TASK = 'receiveOfflineCompleteTask'
export const receiveTask = async function (
token: Token,
@@ -86,14 +86,15 @@ export const receiveTask = async function (
)
return {
taskFunction: RECEIVE,
taskFunction: RECEIVE_TASK,
transaction,
message: `The mint ${mintToReceive} is blocked. You can unblock it in Settings.`,
} as TransactionTaskResult
}
const {
receivedAmount,
receivedAmount,
receivedProofs,
outputToken,
swapFeePaid,
} = await receiveSync(
@@ -133,11 +134,12 @@ export const receiveTask = async function (
}
return {
taskFunction: RECEIVE,
taskFunction: RECEIVE_TASK,
mintUrl: mintToReceive,
transaction,
message: `You've received ${formatCurrency(receivedAmount, getCurrency(unit).code)} ${getCurrency(unit).code} to your Minibits wallet.`,
receivedAmount,
receivedProofsCount: receivedProofs.length
} as TransactionTaskResult
} catch (e: any) {
@@ -158,7 +160,7 @@ export const receiveTask = async function (
log.error(e.name, e.message)
return {
taskFunction: RECEIVE,
taskFunction: RECEIVE_TASK,
mintUrl: mintToReceive,
transaction,
message: e.message,
@@ -221,7 +223,7 @@ export const receiveOfflinePrepareTask = async function (
)
return {
taskFunction: RECEIVE_OFFLINE_PREPARE,
taskFunction: RECEIVE_OFFLINE_PREPARE_TASK,
mintUrl: mintToReceive,
transaction: blockedTransaction,
message: `The mint ${mintToReceive} is blocked. You can unblock it in Settings.`,
@@ -240,7 +242,7 @@ export const receiveOfflinePrepareTask = async function (
)
return {
taskFunction: RECEIVE_OFFLINE_PREPARE,
taskFunction: RECEIVE_OFFLINE_PREPARE_TASK,
mintUrl: mintToReceive,
transaction,
message: `You received ${formatCurrency(amountToReceive, getCurrency(unit).code)} ${getCurrency(unit).code} while offline. You need to redeem them to your wallet when you will be online again.`,
@@ -262,7 +264,7 @@ export const receiveOfflinePrepareTask = async function (
log.error(e.name, e.message)
return {
taskFunction: RECEIVE_OFFLINE_PREPARE,
taskFunction: RECEIVE_OFFLINE_PREPARE_TASK,
mintUrl: mintToReceive,
transaction,
message: '',
@@ -307,7 +309,7 @@ export const receiveOfflineCompleteTask = async function (
)
return {
taskFunction: RECEIVE_OFFLINE_COMPLETE,
taskFunction: RECEIVE_OFFLINE_COMPLETE_TASK,
mintUrl: mintToReceive,
transaction,
message: `The mint ${mintToReceive} is blocked. You can unblock it in Settings.`,
@@ -354,7 +356,7 @@ export const receiveOfflineCompleteTask = async function (
}
return {
taskFunction: RECEIVE_OFFLINE_COMPLETE,
taskFunction: RECEIVE_OFFLINE_COMPLETE_TASK,
mintUrl: mintToReceive,
transaction,
message: `You've received ${formatCurrency(receivedAmount, getCurrency(unit).code)} ${getCurrency(unit).code} to your Minibits wallet.`,
@@ -378,7 +380,7 @@ export const receiveOfflineCompleteTask = async function (
}
return {
taskFunction: RECEIVE_OFFLINE_COMPLETE,
taskFunction: RECEIVE_OFFLINE_COMPLETE_TASK,
mintUrl: mintToReceive,
transaction,
message: '',
+3 -3
View File
@@ -17,7 +17,7 @@ const {
walletStore
} = rootStoreInstance
const REVERT = 'revertTask'
export const REVERT_TASK = 'revertTask'
export const revertTask = async function (
transaction: Transaction
@@ -101,7 +101,7 @@ try {
}
return {
taskFunction: REVERT,
taskFunction: REVERT_TASK,
mintUrl: mintInstance.mintUrl,
transaction,
message: `Transaction has been reverted and funds were returned to spendable balance.`,
@@ -125,7 +125,7 @@ try {
log.error(e.name, e.message)
return {
taskFunction: REVERT,
taskFunction: REVERT_TASK,
mintUrl: transaction.mint,
transaction,
message: e.message,
+8 -6
View File
@@ -28,6 +28,8 @@ const {
walletStore
} = rootStoreInstance
export const SEND_TASK = 'sendTask'
export const sendTask = async function (
mintBalanceToSendFrom: MintBalance,
amountToSend: number,
@@ -38,8 +40,8 @@ export const sendTask = async function (
const mintUrl = mintBalanceToSendFrom.mintUrl
log.trace('[send]', 'mintBalanceToSendFrom', mintBalanceToSendFrom)
log.trace('[send]', 'amountToSend', {amountToSend, unit})
log.trace('[sendTask]', 'mintBalanceToSendFrom', mintBalanceToSendFrom)
log.trace('[sendTask]', 'amountToSend', {amountToSend, unit})
// create draft transaction
const transactionData: TransactionData[] = [
@@ -135,7 +137,7 @@ export const sendTask = async function (
poller(
`syncStateWithMintPoller-${mintUrl}`,
WalletTask.syncStateWithMint,
WalletTask.syncStateWithMintQueue,
{
interval: 6 * 1000,
maxPolls: 5,
@@ -147,7 +149,7 @@ export const sendTask = async function (
}
return {
taskFunction: 'sendTask',
taskFunction: SEND_TASK,
mintUrl,
transaction,
message: '',
@@ -170,7 +172,7 @@ export const sendTask = async function (
}
return {
taskFunction: 'sendTask',
taskFunction: SEND_TASK,
mintUrl,
transaction,
message: e.message,
@@ -421,7 +423,7 @@ export const sendFromMintSync = async function (
log.error('[sendFromMintSync] Going to clean spent proofs from pending', {transactionId})
await WalletTask.syncStateWithMintSync({
await WalletTask.syncStateWithMintTask({
proofsToSync: proofsStore.getByMint(mintUrl, {isPending: true, unit}),
mintUrl,
isPending: true
+6 -6
View File
@@ -21,7 +21,7 @@ const {
// const {walletStore} = nonPersistedStores
const TOPUP = 'topupTask'
export const TOPUP_TASK = 'topupTask'
export const topupTask = async function (
mintBalanceToTopup: MintBalance,
@@ -138,19 +138,19 @@ export const topupTask = async function (
if(!nwcEvent) {
poller(
`handlePendingTopupTaskPoller-${paymentRequest.paymentHash}`,
WalletTask.handlePendingTopup,
`handlePendingTopupPoller-${paymentRequest.paymentHash}`,
WalletTask.handlePendingTopupQueue,
{
interval: 6 * 1000,
maxPolls: 10,
maxErrors: 2
},
{paymentRequest})
.then(() => log.trace('Polling completed', [], `handlePendingTopupTaskPoller`))
.then(() => log.trace('Polling completed', [], `handlePendingTopupPoller`))
}
return {
taskFunction: TOPUP,
taskFunction: TOPUP_TASK,
mintUrl,
transaction,
message: '',
@@ -177,7 +177,7 @@ export const topupTask = async function (
log.error(e.name, e.message)
return {
taskFunction: TOPUP,
taskFunction: TOPUP_TASK,
transaction,
message: e.message,
error: WalletUtils.formatError(e),
+5 -5
View File
@@ -21,7 +21,7 @@ const {
// const {walletStore} = nonPersistedStores
const TRANSFER = 'transferTask'
export const TRANSFER_TASK = 'transferTask'
export const transferTask = async function (
mintBalanceToTransferFrom: MintBalance,
@@ -248,7 +248,7 @@ export const transferTask = async function (
transaction.setBalanceAfter(balanceAfter)
return {
taskFunction: TRANSFER,
taskFunction: TRANSFER_TASK,
mintUrl,
transaction,
message: `Lightning invoice has been successfully paid and settled with your Minibits ecash. Fee has been ${formatCurrency(transaction.fee, getCurrency(unit).code)} ${getCurrency(unit).code}.`,
@@ -268,7 +268,7 @@ export const transferTask = async function (
})
return {
taskFunction: TRANSFER,
taskFunction: TRANSFER_TASK,
mintUrl,
transaction,
message: `Lightning payment did not complete in time. Your ecash will remain pending until the payment completes or fails.`,
@@ -287,7 +287,7 @@ export const transferTask = async function (
} catch (e: any) {
let message = e.message
let taskResult: TransactionTaskResult = {
taskFunction: TRANSFER,
taskFunction: TRANSFER_TASK,
mintUrl,
transaction,
message,
@@ -350,7 +350,7 @@ export const transferTask = async function (
}
}
await WalletTask.syncStateWithMintSync({
await WalletTask.syncStateWithMintTask({
proofsToSync: proofsStore.getByMint(mintUrl, {isPending: true, unit}),
mintUrl,
isPending: true
File diff suppressed because it is too large Load Diff
+298 -10
View File
@@ -1712,6 +1712,174 @@ __metadata:
languageName: node
linkType: hard
"@esbuild/aix-ppc64@npm:0.23.1":
version: 0.23.1
resolution: "@esbuild/aix-ppc64@npm:0.23.1"
conditions: os=aix & cpu=ppc64
languageName: node
linkType: hard
"@esbuild/android-arm64@npm:0.23.1":
version: 0.23.1
resolution: "@esbuild/android-arm64@npm:0.23.1"
conditions: os=android & cpu=arm64
languageName: node
linkType: hard
"@esbuild/android-arm@npm:0.23.1":
version: 0.23.1
resolution: "@esbuild/android-arm@npm:0.23.1"
conditions: os=android & cpu=arm
languageName: node
linkType: hard
"@esbuild/android-x64@npm:0.23.1":
version: 0.23.1
resolution: "@esbuild/android-x64@npm:0.23.1"
conditions: os=android & cpu=x64
languageName: node
linkType: hard
"@esbuild/darwin-arm64@npm:0.23.1":
version: 0.23.1
resolution: "@esbuild/darwin-arm64@npm:0.23.1"
conditions: os=darwin & cpu=arm64
languageName: node
linkType: hard
"@esbuild/darwin-x64@npm:0.23.1":
version: 0.23.1
resolution: "@esbuild/darwin-x64@npm:0.23.1"
conditions: os=darwin & cpu=x64
languageName: node
linkType: hard
"@esbuild/freebsd-arm64@npm:0.23.1":
version: 0.23.1
resolution: "@esbuild/freebsd-arm64@npm:0.23.1"
conditions: os=freebsd & cpu=arm64
languageName: node
linkType: hard
"@esbuild/freebsd-x64@npm:0.23.1":
version: 0.23.1
resolution: "@esbuild/freebsd-x64@npm:0.23.1"
conditions: os=freebsd & cpu=x64
languageName: node
linkType: hard
"@esbuild/linux-arm64@npm:0.23.1":
version: 0.23.1
resolution: "@esbuild/linux-arm64@npm:0.23.1"
conditions: os=linux & cpu=arm64
languageName: node
linkType: hard
"@esbuild/linux-arm@npm:0.23.1":
version: 0.23.1
resolution: "@esbuild/linux-arm@npm:0.23.1"
conditions: os=linux & cpu=arm
languageName: node
linkType: hard
"@esbuild/linux-ia32@npm:0.23.1":
version: 0.23.1
resolution: "@esbuild/linux-ia32@npm:0.23.1"
conditions: os=linux & cpu=ia32
languageName: node
linkType: hard
"@esbuild/linux-loong64@npm:0.23.1":
version: 0.23.1
resolution: "@esbuild/linux-loong64@npm:0.23.1"
conditions: os=linux & cpu=loong64
languageName: node
linkType: hard
"@esbuild/linux-mips64el@npm:0.23.1":
version: 0.23.1
resolution: "@esbuild/linux-mips64el@npm:0.23.1"
conditions: os=linux & cpu=mips64el
languageName: node
linkType: hard
"@esbuild/linux-ppc64@npm:0.23.1":
version: 0.23.1
resolution: "@esbuild/linux-ppc64@npm:0.23.1"
conditions: os=linux & cpu=ppc64
languageName: node
linkType: hard
"@esbuild/linux-riscv64@npm:0.23.1":
version: 0.23.1
resolution: "@esbuild/linux-riscv64@npm:0.23.1"
conditions: os=linux & cpu=riscv64
languageName: node
linkType: hard
"@esbuild/linux-s390x@npm:0.23.1":
version: 0.23.1
resolution: "@esbuild/linux-s390x@npm:0.23.1"
conditions: os=linux & cpu=s390x
languageName: node
linkType: hard
"@esbuild/linux-x64@npm:0.23.1":
version: 0.23.1
resolution: "@esbuild/linux-x64@npm:0.23.1"
conditions: os=linux & cpu=x64
languageName: node
linkType: hard
"@esbuild/netbsd-x64@npm:0.23.1":
version: 0.23.1
resolution: "@esbuild/netbsd-x64@npm:0.23.1"
conditions: os=netbsd & cpu=x64
languageName: node
linkType: hard
"@esbuild/openbsd-arm64@npm:0.23.1":
version: 0.23.1
resolution: "@esbuild/openbsd-arm64@npm:0.23.1"
conditions: os=openbsd & cpu=arm64
languageName: node
linkType: hard
"@esbuild/openbsd-x64@npm:0.23.1":
version: 0.23.1
resolution: "@esbuild/openbsd-x64@npm:0.23.1"
conditions: os=openbsd & cpu=x64
languageName: node
linkType: hard
"@esbuild/sunos-x64@npm:0.23.1":
version: 0.23.1
resolution: "@esbuild/sunos-x64@npm:0.23.1"
conditions: os=sunos & cpu=x64
languageName: node
linkType: hard
"@esbuild/win32-arm64@npm:0.23.1":
version: 0.23.1
resolution: "@esbuild/win32-arm64@npm:0.23.1"
conditions: os=win32 & cpu=arm64
languageName: node
linkType: hard
"@esbuild/win32-ia32@npm:0.23.1":
version: 0.23.1
resolution: "@esbuild/win32-ia32@npm:0.23.1"
conditions: os=win32 & cpu=ia32
languageName: node
linkType: hard
"@esbuild/win32-x64@npm:0.23.1":
version: 0.23.1
resolution: "@esbuild/win32-x64@npm:0.23.1"
conditions: os=win32 & cpu=x64
languageName: node
linkType: hard
"@eslint-community/eslint-utils@npm:^4.2.0, @eslint-community/eslint-utils@npm:^4.4.0":
version: 4.4.0
resolution: "@eslint-community/eslint-utils@npm:4.4.0"
@@ -6225,6 +6393,89 @@ __metadata:
languageName: node
linkType: hard
"esbuild@npm:~0.23.0":
version: 0.23.1
resolution: "esbuild@npm:0.23.1"
dependencies:
"@esbuild/aix-ppc64": 0.23.1
"@esbuild/android-arm": 0.23.1
"@esbuild/android-arm64": 0.23.1
"@esbuild/android-x64": 0.23.1
"@esbuild/darwin-arm64": 0.23.1
"@esbuild/darwin-x64": 0.23.1
"@esbuild/freebsd-arm64": 0.23.1
"@esbuild/freebsd-x64": 0.23.1
"@esbuild/linux-arm": 0.23.1
"@esbuild/linux-arm64": 0.23.1
"@esbuild/linux-ia32": 0.23.1
"@esbuild/linux-loong64": 0.23.1
"@esbuild/linux-mips64el": 0.23.1
"@esbuild/linux-ppc64": 0.23.1
"@esbuild/linux-riscv64": 0.23.1
"@esbuild/linux-s390x": 0.23.1
"@esbuild/linux-x64": 0.23.1
"@esbuild/netbsd-x64": 0.23.1
"@esbuild/openbsd-arm64": 0.23.1
"@esbuild/openbsd-x64": 0.23.1
"@esbuild/sunos-x64": 0.23.1
"@esbuild/win32-arm64": 0.23.1
"@esbuild/win32-ia32": 0.23.1
"@esbuild/win32-x64": 0.23.1
dependenciesMeta:
"@esbuild/aix-ppc64":
optional: true
"@esbuild/android-arm":
optional: true
"@esbuild/android-arm64":
optional: true
"@esbuild/android-x64":
optional: true
"@esbuild/darwin-arm64":
optional: true
"@esbuild/darwin-x64":
optional: true
"@esbuild/freebsd-arm64":
optional: true
"@esbuild/freebsd-x64":
optional: true
"@esbuild/linux-arm":
optional: true
"@esbuild/linux-arm64":
optional: true
"@esbuild/linux-ia32":
optional: true
"@esbuild/linux-loong64":
optional: true
"@esbuild/linux-mips64el":
optional: true
"@esbuild/linux-ppc64":
optional: true
"@esbuild/linux-riscv64":
optional: true
"@esbuild/linux-s390x":
optional: true
"@esbuild/linux-x64":
optional: true
"@esbuild/netbsd-x64":
optional: true
"@esbuild/openbsd-arm64":
optional: true
"@esbuild/openbsd-x64":
optional: true
"@esbuild/sunos-x64":
optional: true
"@esbuild/win32-arm64":
optional: true
"@esbuild/win32-ia32":
optional: true
"@esbuild/win32-x64":
optional: true
bin:
esbuild: bin/esbuild
checksum: 0413c3b9257327fb598427688b7186ea335bf1693746fe5713cc93c95854d6388b8ed4ad643fddf5b5ace093f7dcd9038dd58e087bf2da1f04dfb4c5571660af
languageName: node
linkType: hard
"escalade@npm:^3.1.1, escalade@npm:^3.1.2":
version: 3.2.0
resolution: "escalade@npm:3.2.0"
@@ -7029,7 +7280,7 @@ __metadata:
languageName: node
linkType: hard
"fsevents@npm:^2.3.2":
"fsevents@npm:^2.3.2, fsevents@npm:~2.3.3":
version: 2.3.3
resolution: "fsevents@npm:2.3.3"
dependencies:
@@ -7039,7 +7290,7 @@ __metadata:
languageName: node
linkType: hard
"fsevents@patch:fsevents@^2.3.2#~builtin<compat/fsevents>":
"fsevents@patch:fsevents@^2.3.2#~builtin<compat/fsevents>, fsevents@patch:fsevents@~2.3.3#~builtin<compat/fsevents>":
version: 2.3.3
resolution: "fsevents@patch:fsevents@npm%3A2.3.3#~builtin<compat/fsevents>::version=2.3.3&hash=df0bf1"
dependencies:
@@ -7126,6 +7377,15 @@ __metadata:
languageName: node
linkType: hard
"get-tsconfig@npm:^4.7.5":
version: 4.8.1
resolution: "get-tsconfig@npm:4.8.1"
dependencies:
resolve-pkg-maps: ^1.0.0
checksum: 12df01672e691d2ff6db8cf7fed1ddfef90ed94a5f3d822c63c147a26742026d582acd86afcd6f65db67d809625d17dd7f9d34f4d3f38f69bc2f48e19b2bdd5b
languageName: node
linkType: hard
"get-uri@npm:^6.0.1":
version: 6.0.3
resolution: "get-uri@npm:6.0.3"
@@ -9534,7 +9794,7 @@ __metadata:
react-native-tab-view: ^3.5.2
react-native-url-polyfill: ^2.0.0
react-test-renderer: 18.3.1
simple-js-task-queue: ^0.2.2
taskon: ^0.3.0
text-encoding-polyfill: ^0.6.7
typescript: 5.0.4
util: ^0.12.5
@@ -11338,6 +11598,13 @@ __metadata:
languageName: node
linkType: hard
"resolve-pkg-maps@npm:^1.0.0":
version: 1.0.0
resolution: "resolve-pkg-maps@npm:1.0.0"
checksum: 1012afc566b3fdb190a6309cc37ef3b2dcc35dff5fa6683a9d00cd25c3247edfbc4691b91078c97adc82a29b77a2660c30d791d65dab4fc78bfc473f60289977
languageName: node
linkType: hard
"resolve.exports@npm:^2.0.0":
version: 2.0.2
resolution: "resolve.exports@npm:2.0.2"
@@ -11727,13 +11994,6 @@ __metadata:
languageName: node
linkType: hard
"simple-js-task-queue@npm:^0.2.2":
version: 0.2.2
resolution: "simple-js-task-queue@npm:0.2.2"
checksum: bd56fcbea3ecbc081238f02c8892ebbbb692468ecdac9f0d229e96dea85db308889adf1ef128346a3acc8585913813f2415eed0aa4336be6467a4995cf42f39e
languageName: node
linkType: hard
"simple-plist@npm:^1.1.0":
version: 1.4.0
resolution: "simple-plist@npm:1.4.0"
@@ -12209,6 +12469,18 @@ __metadata:
languageName: node
linkType: hard
"taskon@npm:^0.3.0":
version: 0.3.0
resolution: "taskon@npm:0.3.0"
dependencies:
tsx: ^4.19.2
dependenciesMeta:
tsx:
optional: true
checksum: 7632189a17a8f51a2528e9ed580f9b6892d36ae266ca8e1d81c4779db83252a177f561d37f324d4806027e7eca98a8ad43dde2c5e5e6355a73e9013d4ddf3fc3
languageName: node
linkType: hard
"temp@npm:^0.8.4":
version: 0.8.4
resolution: "temp@npm:0.8.4"
@@ -12368,6 +12640,22 @@ __metadata:
languageName: node
linkType: hard
"tsx@npm:^4.19.2":
version: 4.19.2
resolution: "tsx@npm:4.19.2"
dependencies:
esbuild: ~0.23.0
fsevents: ~2.3.3
get-tsconfig: ^4.7.5
dependenciesMeta:
fsevents:
optional: true
bin:
tsx: dist/cli.mjs
checksum: 7f9f1b338a73297725a9217cedaaad862f7c81d5264093c74b98a71491ad5413b11248d604c0e650f4f7da6f365249f1426fdb58a1325ab9e15448156b1edff6
languageName: node
linkType: hard
"type-check@npm:^0.4.0, type-check@npm:~0.4.0":
version: 0.4.0
resolution: "type-check@npm:0.4.0"