From 6d188a78f4fa401a4fe29b6db4da0ec45af7195a Mon Sep 17 00:00:00 2001 From: minibits-cash Date: Tue, 13 Jan 2026 22:16:45 +0100 Subject: [PATCH] Fix keysets mutation --- ios/minibits_wallet.xcodeproj/project.pbxproj | 10 +++- src/models/Mint.ts | 42 ++++++++----- src/models/MintsStore.ts | 4 +- src/models/WalletStore.ts | 60 +++++++++---------- src/screens/DeveloperScreen.tsx | 2 +- src/services/logService.ts | 2 +- src/services/minibitsService.ts | 2 +- src/services/walletService.ts | 4 +- 8 files changed, 73 insertions(+), 53 deletions(-) diff --git a/ios/minibits_wallet.xcodeproj/project.pbxproj b/ios/minibits_wallet.xcodeproj/project.pbxproj index a45d7feb..714e6495 100644 --- a/ios/minibits_wallet.xcodeproj/project.pbxproj +++ b/ios/minibits_wallet.xcodeproj/project.pbxproj @@ -447,7 +447,10 @@ "-DFOLLY_CFG_NO_COROUTINES=1", "-DFOLLY_HAVE_CLOCK_GETTIME=1", ); - OTHER_LDFLAGS = "$(inherited) "; + OTHER_LDFLAGS = ( + "$(inherited)", + " ", + ); REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native"; SDKROOT = iphoneos; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) DEBUG"; @@ -530,7 +533,10 @@ "-DFOLLY_CFG_NO_COROUTINES=1", "-DFOLLY_HAVE_CLOCK_GETTIME=1", ); - OTHER_LDFLAGS = "$(inherited) "; + OTHER_LDFLAGS = ( + "$(inherited)", + " ", + ); REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native"; SDKROOT = iphoneos; SWIFT_COMPILATION_MODE = wholemodule; diff --git a/src/models/Mint.ts b/src/models/Mint.ts index 655865b9..ff243a9f 100644 --- a/src/models/Mint.ts +++ b/src/models/Mint.ts @@ -297,20 +297,30 @@ export const MintModel = types } }, setIsActive(freshKeyset: CashuMintKeyset) { - const keyset = self.keysets.find(k => k.id === freshKeyset.id) + const index = self.keysets.findIndex(k => k.id === freshKeyset.id) - if(keyset) { - keyset.active = freshKeyset.active + if(index !== -1) { + // Since keysets is a frozen array, we need to replace the entire keyset object + const updatedKeyset = { + ...self.keysets[index], + active: freshKeyset.active + } + self.keysets[index] = updatedKeyset self.keysets = cast(self.keysets) - } + } }, setInputFeePpk(keysetId: string, inputFeePpk: number) { - const keyset = self.keysets.find(k => k.id === keysetId) + const index = self.keysets.findIndex(k => k.id === keysetId) - if(keyset) { - keyset.input_fee_ppk = inputFeePpk + if(index !== -1) { + // Since keysets is a frozen array, we need to replace the entire keyset object + const updatedKeyset = { + ...self.keysets[index], + input_fee_ppk: inputFeePpk + } + self.keysets[index] = updatedKeyset self.keysets = cast(self.keysets) - } + } }, addKeys(keys: CashuMintKeys) { const alreadyExists = self.keys.some(k => k.id === keys.id) @@ -415,18 +425,22 @@ export const MintModel = types const existing = self.keysets.find(k => k.id === keyset.id) if(existing) { - if (existing.unit !== keyset.unit) { + if (existing.unit !== keyset.unit) { throw new AppError( - Err.VALIDATION_ERROR, + Err.VALIDATION_ERROR, `Keyset unit mismatch.`, {caller: 'initKeyset', existingUnit: existing.unit, keysetUnit: keyset.unit} - ) + ) } if(keyset.input_fee_ppk && existing.input_fee_ppk !== keyset.input_fee_ppk) { self.setInputFeePpk(existing.id, keyset.input_fee_ppk) } + if(existing.active !== keyset.active) { + self.setIsActive(keyset) + } + return existing } @@ -479,16 +493,16 @@ export const MintModel = types }, })) .actions(self => ({ - refreshKeysets(freshKeysets: CashuMintKeyset[]) { + refreshKeysets(freshKeysets: CashuMintKeyset[]) { const mintsStore = getRootStore(self).mintsStore const allKeysetIds = mintsStore.allKeysetIds log.trace('[refreshKeysets]', {freshKeysets, allKeysetIds}) - // add new keyset if not exists + // add new keyset if not exists for (const keyset of freshKeysets) { + // initKeyset now handles active status updates internally self.initKeyset(keyset, allKeysetIds) - self.setIsActive(keyset) } }, refreshKeys(freshKeys: CashuMintKeys[]) { diff --git a/src/models/MintsStore.ts b/src/models/MintsStore.ts index 56bf7f46..51a9009a 100644 --- a/src/models/MintsStore.ts +++ b/src/models/MintsStore.ts @@ -182,8 +182,8 @@ export const MintsStoreModel = types // refresh up to date mint keys const newMint = new CashuMint(mintUrl) // get fresh keysets - const keySetResult: MintAllKeysets = yield newMint.getKeySets() - const keysResult: MintActiveKeys = yield newMint.getKeys() + const keySetResult: GetKeysetsResponse = yield newMint.getKeySets() + const keysResult: GetKeysResponse = yield newMint.getKeys() const {keysets} = keySetResult const {keysets: keys} = keysResult diff --git a/src/models/WalletStore.ts b/src/models/WalletStore.ts index da1d0b30..82ad1eef 100644 --- a/src/models/WalletStore.ts +++ b/src/models/WalletStore.ts @@ -28,7 +28,7 @@ import { InFlightRequest, Mint } from './Mint' import { getRootStore } from './helpers/getRootStore' import { Transaction } from './Transaction' -//refresh + /* Not persisted, in-memory only model of the cashu-ts wallet instances and wallet keys persisted in the device secure store. It is instantiated on first use so that wallet retrieves fresh mint keysets, then cached, @@ -375,8 +375,8 @@ export const WalletStoreModel = types const cashuMint: CashuMint = yield self.getMint(mintUrl) try { - const {keysets} = yield cashuMint.getKeys() as Promise // all active keys - return keysets as MintKeys[] + const {keysets: keys} = yield cashuMint.getKeys() as Promise // all active keys + return keys as MintKeys[] } catch (e: any) { let message = 'Could not connect to the selected mint.' if (isOnionMint(mintUrl)) message += TorVPNSetupInstructions; @@ -1026,7 +1026,7 @@ export const WalletStoreModel = types const currentCounter = mintInstance.getProofsCounterByKeysetId!(transaction.keysetId) const meltCounterValue = currentCounter.getMeltCounterValue(transaction.id) - if(!meltCounterValue) { + if(!meltCounterValue || !meltCounterValue.meltPreview) { throw new AppError(Err.VALIDATION_ERROR, 'Change already claimed - melt data not available for this transaction', {mintUrl, transactionId}) } @@ -1078,32 +1078,32 @@ export const WalletStoreModel = types } ) { try { - const {indexFrom, indexTo, keysetId} = options - // need special wallet instance to pass seed and keysetId directly - const cashuMint = yield self.getMint(mintUrl) - - const seedWallet = new CashuWallet(cashuMint, { - unit: 'sat', // just use default unit as we restore by keyset - keys: cashuMint.keys, - keysets: cashuMint.keysets, - keysetId, - bip39seed: seed - }) - - const count = Math.abs(indexTo - indexFrom) - - const {proofs} = yield seedWallet.restore( - indexFrom, - count, - {keysetId} - ) - - - log.info('[restore]', 'Number of recovered proofs', {proofs: proofs.length}) - - return { - proofs: proofs || [] as Proof[] - } + const {indexFrom, indexTo, keysetId} = options + // need special wallet instance to pass seed and keysetId directly + const cashuMint = yield self.getMint(mintUrl) + + const seedWallet = new CashuWallet(cashuMint, { + unit: 'sat', // just use default unit as we restore by keyset + keys: cashuMint.keys, + keysets: cashuMint.keysets, + keysetId, + bip39seed: seed + }) + + const count = Math.abs(indexTo - indexFrom) + + const {proofs} = yield seedWallet.restore( + indexFrom, + count, + {keysetId} + ) + + + log.info('[restore]', 'Number of recovered proofs', {proofs: proofs.length}) + + return { + proofs: proofs || [] as Proof[] + } } catch (e: any) { throw new AppError(Err.MINT_ERROR, CashuUtils.isObj(e.message) ? JSON.stringify(e.message) : e.message, {mintUrl}) } diff --git a/src/screens/DeveloperScreen.tsx b/src/screens/DeveloperScreen.tsx index 1d0bda61..91591a03 100644 --- a/src/screens/DeveloperScreen.tsx +++ b/src/screens/DeveloperScreen.tsx @@ -34,7 +34,7 @@ import RNExitApp from 'react-native-exit-app' import { TransactionStatus } from '../models/Transaction' import { maxTransactionsInHistory } from '../models/TransactionsStore' import { StaticScreenProps, useNavigation } from '@react-navigation/native' -// +// refresh type Props = StaticScreenProps diff --git a/src/services/logService.ts b/src/services/logService.ts index 76d9ec31..6bc2e256 100644 --- a/src/services/logService.ts +++ b/src/services/logService.ts @@ -18,7 +18,7 @@ import { Platform } from "react-native" import AppError, { Err } from "../utils/AppError" const { userSettingsStore } = rootStoreInstance - +// refresh if (!__DEV__) { Sentry.init({ diff --git a/src/services/minibitsService.ts b/src/services/minibitsService.ts index 17afa3c5..ddd1376b 100644 --- a/src/services/minibitsService.ts +++ b/src/services/minibitsService.ts @@ -8,7 +8,7 @@ import { WalletProfileRecord } from "../models/WalletProfileStore" import { CurrencyCode } from "./wallet/currency" import { rootStoreInstance } from "../models" import { AuthChallengeResponse, TokenPair, VerifyChallengeResponse } from "../models/AuthStore" - // refresh + // type MinibitsRequestArgs = { method: 'POST' | 'PUT' | 'DELETE' | 'GET' diff --git a/src/services/walletService.ts b/src/services/walletService.ts index 70acc372..4a6a1657 100644 --- a/src/services/walletService.ts +++ b/src/services/walletService.ts @@ -1680,8 +1680,8 @@ const handleInFlightByMintTask = async (mint: Mint): Promise = tx.update({ outputToken }) } - if (quote.payment_preimage) { - tx.update({ proof: quote.payment_preimage }) + if (meltQuoteCheck.payment_preimage) { + tx.update({ proof: meltQuoteCheck.payment_preimage }) } const inputAmount = CashuUtils.getProofsAmount(inFlight.request.proofsToSend)