Fixes: Can not init transactions after recovery from backup, fix retry of SEND transaction.

This commit is contained in:
minibits-cash
2025-01-30 22:25:40 +01:00
parent b2d2f53e48
commit 98f6ff29cb
10 changed files with 108 additions and 39 deletions
+1 -1
View File
@@ -32,7 +32,7 @@ notifee.registerForegroundService(async (notification) => {
try {
if(notification.data.task === HANDLE_NWC_REQUEST_TASK) {
log.debug(`[registerForegroundService] Submitting task ${HANDLE_NWC_REQUEST_TASK} to the queue.`)
log.info(`[registerForegroundService] Submitting task ${HANDLE_NWC_REQUEST_TASK} to the queue.`)
const {nwcStore} = rootStoreInstance
// if an app is in killed state, state is not loaded
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "minibits_wallet",
"version": "0.1.11-beta.5",
"version": "0.1.11-beta.6",
"private": true,
"scripts": {
"android:clean": "cd android && ./gradlew clean",
+5
View File
@@ -194,6 +194,11 @@ export const WalletProfileStoreModel = types
}),
setDevice: flow(function* setDevice(device: string) {
try {
if(!self.pubkey) {
// skip call for new installs without a profile
return
}
yield MinibitsClient.updateDeviceToken(self.pubkey, {deviceToken: device})
self.device = device
} catch (e: any) {
+29 -9
View File
@@ -22,11 +22,10 @@ import {
} from '../components'
import {useHeader} from '../utils/useHeader'
import AppError, { Err } from '../utils/AppError'
import { Database, KeyChain, log, MinibitsClient, NostrClient } from '../services'
import { Database, KeyChain, log, MinibitsClient } from '../services'
import Clipboard from '@react-native-clipboard/clipboard'
import { useStores } from '../models'
import { rootStoreInstance, useStores } from '../models'
import {MnemonicInput} from './Recovery/MnemonicInput'
import { TransactionStatus } from '../models/Transaction'
import { MINIBITS_MINT_URL, MINIBITS_NIP05_DOMAIN } from '@env'
import { delay } from '../utils/utils'
import { applySnapshot} from 'mobx-state-tree'
@@ -36,6 +35,8 @@ import { translate } from '../i18n'
import { ProofsStoreSnapshot } from '../models/ProofsStore'
import { MintsStoreSnapshot } from '../models/MintsStore'
import { ContactsStoreSnapshot } from '../models/ContactsStore'
import { CashuMint, MintActiveKeys } from '@cashu/cashu-ts'
import { MintUnit } from '../services/wallet/currency'
if (Platform.OS === 'android' && UIManager.setLayoutAnimationEnabledExperimental) {
@@ -224,12 +225,30 @@ export const ImportBackupScreen: FC<AppStackScreenProps<'ImportBackup'>> = obser
// import wallet snapshot into the state
// const rootStore = rootStoreInstance
// hydrate mint keys back to the backup as they are stripped from backup
for (const mint of walletSnapshot.mintsStore.mints) {
const cashuMint = new CashuMint(mint.mintUrl)
const keysResult: MintActiveKeys = await cashuMint.getKeys()
const {keysets: keys} = keysResult
for(const key of keys) {
if(!key.unit) {
key.unit = 'sat'
}
log.trace('[importWallet] Hydrating keys for', {keysetId: key.id})
mint.keys.push(key)
}
}
applySnapshot(proofsStore, walletSnapshot.proofsStore)
applySnapshot(mintsStore, walletSnapshot.mintsStore)
applySnapshot(contactsStore, walletSnapshot.contactsStore)
// log.trace('After import', {rootStore})
// log.trace('After import', {rootStore})
const rootStore = rootStoreInstance
log.trace('After import and mint keys hydration', {mintsStore})
// import proofs into the db
if(proofsStore.proofsCount > 0) {
@@ -270,7 +289,11 @@ export const ImportBackupScreen: FC<AppStackScreenProps<'ImportBackup'>> = obser
mnemonic
}
keys.SEED = seed
keys.SEED = seed
// save keys as we need them next for publishing the recovered profile to relays
await KeyChain.saveWalletKeys(keys)
walletStore.cleanCachedWalletKeys()
if(isNewProfileNeeded) {
@@ -290,10 +313,7 @@ export const ImportBackupScreen: FC<AppStackScreenProps<'ImportBackup'>> = obser
)
}
// save keys after successful profile creation / recovery
await KeyChain.saveWalletKeys(keys)
walletStore.cleanCachedWalletKeys()
userSettingsStore.setIsOnboarded(true)
+21
View File
@@ -22,6 +22,7 @@ import { useStores } from '../models'
import { translate } from '../i18n'
import { MintUnit } from '../services/wallet/currency'
import { Mint } from '../models/Mint'
import { CashuUtils } from '../services/cashu/cashuUtils'
const hasAndroidCameraPermission = async () => {
const cameraPermission = await PermissionsAndroid.request(PermissionsAndroid.PERMISSIONS.CAMERA)
@@ -202,6 +203,26 @@ export const ScanScreen: FC<WalletStackScreenProps<'Scan'>> = function ScanScree
break
}
}
const maybeCashuPaymentRequest = CashuUtils.findEncodedCashuPaymentRequest(incoming)
if(maybeCashuPaymentRequest) {
try {
log.trace('Found Cashu Payment request instead of an invoice', maybeCashuPaymentRequest, 'onIncomingData')
const encodedPr = CashuUtils.extractEncodedCashuPaymentRequest(maybeCashuPaymentRequest)
if(encodedPr) {
await IncomingParser.navigateWithIncomingData({
type: IncomingDataType.CASHU_PAYMENT_REQUEST,
encoded: encodedPr
}, navigation, unit, mint && mint.mintUrl)
}
return
} catch (e3: any) {
handleError(e3)
break
}
}
e.params = incoming
handleError(e)
+5 -6
View File
@@ -510,7 +510,11 @@ export const SeedRecoveryScreen: FC<AppStackScreenProps<'SeedRecovery'>> = obser
mnemonic
}
keys.SEED = seed
keys.SEED = seed
// save keys as we need them next for publishing the recovered profile to relays
await KeyChain.saveWalletKeys(keys)
walletStore.cleanCachedWalletKeys()
if(isNewProfileNeeded) {
@@ -530,11 +534,6 @@ export const SeedRecoveryScreen: FC<AppStackScreenProps<'SeedRecovery'>> = obser
)
}
// save keys after successful profile creation / recovery
await KeyChain.saveWalletKeys(keys)
walletStore.cleanCachedWalletKeys()
userSettingsStore.setIsOnboarded(true)
if(!mintsStore.mintExists(MINIBITS_MINT_URL)) {
+1 -1
View File
@@ -124,7 +124,7 @@ export const WalletScreen: FC<WalletScreenProps> = observer(
setUpdateSize(`${round(update.packageSize * 0.000001, 2)}MB`)
setIsUpdateAvailable(true)
toggleUpdateModal()
log.info('OTA Update available', update, 'checkForUpdate')
log.trace('OTA Update available', update, 'checkForUpdate')
}
} catch (e: any) {
return false // silent
+1 -1
View File
@@ -166,7 +166,7 @@ const saveWalletKeys = async function (
return keys
}
log.warn('[getWalletKeys]', 'Did not find existing wallet keys in the KeyChain')
log.debug('[getWalletKeys]', 'Did not find existing wallet keys in the KeyChain')
return undefined
} catch (e: any) {
throw new AppError(Err.KEYCHAIN_ERROR, e.message, e)
+36 -18
View File
@@ -14,7 +14,7 @@ import { NwcRequest, nwcPngUrl } from '../models/NwcStore';
import { HANDLE_NWC_REQUEST_TASK, WalletTask, WalletTaskResult } from './walletService'
import { SyncQueue } from './syncQueueService'
import { delay } from '../utils/delay'
import TaskQueue from 'taskon'
import TaskQueue, { Task, TaskId, TaskStatus } from 'taskon'
export type NotifyReceiveToLnurlData = {
type: 'NotifyReceiveToLnurlData',
@@ -50,6 +50,28 @@ const getNwcQueue = function () {
}
const addNwcQueueTask = function (taskId: TaskId, task: Promise<any> | any) {
const queue = getNwcQueue()
log.info(`Adding new nwcQueue task ${taskId} to the queue`)
queue
.addTask(
task,
taskId, _handleNwcQueueTaskStatusChange)
.then((result: any) => {
log.info(`nwcQueue task ${taskId} completed.`)
})
}
const _handleNwcQueueTaskStatusChange = (status: TaskStatus) => {
log.trace(
`[_handleNwcQueueTaskStatusChange] The status of task changed to ${status}`,
)
}
const DEFAULT_CHANNEL_ID = 'default'
const DEFAULT_CHANNEL_NAME = 'Minibits notifications'
@@ -64,7 +86,7 @@ export const TEST_CHANNEL_NAME = 'Minibits test tasks'
const initNotifications = async () => {
let enabled = await areNotificationsEnabled()
log.trace(`[initNotifications] Push notifications are ${enabled ? 'enabled' : 'disabled'}.`)
log.debug(`[initNotifications] Push notifications are ${enabled ? 'enabled' : 'disabled'}.`)
if(!enabled) return
@@ -143,14 +165,12 @@ const onForegroundNotification = async function(remoteMessage: FirebaseMessaging
// Process NWC request notified by FCM message by dedicated queue to avoid race condition
// when starting foreground service
if(remoteData.type === 'NotifyNwcRequestData') {
const nwcQueue = getNwcQueue()
nwcQueue
.addTask(async () => {
await _nwcRequestHandler(remoteData)
})
.then((result) => {
log.trace('nwcQueue task completed.')
})
const now = new Date().getTime()
addNwcQueueTask(
`_nwcRequestHandler-${now}`,
async () => await _nwcRequestHandler(remoteData)
)
return
}
@@ -177,14 +197,12 @@ const onBackgroundNotification = async function(remoteMessage: FirebaseMessaging
// Process NWC request notified by FCM message by dedicated queue to avoid race condition
// when starting foreground service
if(remoteData.type === 'NotifyNwcRequestData') {
const nwcQueue = getNwcQueue()
nwcQueue
.addTask(async () => {
await _nwcRequestHandler(remoteData)
})
.then((result) => {
log.trace('nwcQueue task completed.')
})
const now = new Date().getTime()
addNwcQueueTask(
`_nwcRequestHandler-${now}`,
async () => await _nwcRequestHandler(remoteData)
)
return
}
+8 -2
View File
@@ -342,7 +342,7 @@ export const sendFromMintSync = async function (
amountWithFees,
returnedAmount,
transactionId
})
})
const sendResult = await walletStore.send(
mintUrl,
@@ -420,7 +420,13 @@ export const sendFromMintSync = async function (
// try to clean spent proofs if that was the swap error cause
if (e.params && e.params.message && e.params.message.includes('Token already spent')) {
log.error('[sendFromMintSync] Going to clean spent proofs from pending', {transactionId})
log.error('[sendFromMintSync] Going to clean spent proofs from proofsToSendFrom and from pending', {transactionId})
await WalletTask.syncStateWithMintTask({
proofsToSync: proofsToSendFrom,
mintUrl,
isPending: true
})
await WalletTask.syncStateWithMintTask({
proofsToSync: proofsStore.getByMint(mintUrl, {isPending: true, unit}),