diff --git a/dist/BUNDLE_ID b/dist/BUNDLE_ID deleted file mode 100644 index a61a7263..00000000 --- a/dist/BUNDLE_ID +++ /dev/null @@ -1 +0,0 @@ -019b98cc-e66d-7947-b6a6-e253c1830c03 \ No newline at end of file diff --git a/metro.config.js b/metro.config.js index e5c8c0e4..eeea0767 100644 --- a/metro.config.js +++ b/metro.config.js @@ -8,7 +8,46 @@ const { withSentryConfig } = require("@sentry/react-native/metro"); * @type {import('@react-native/metro-config').MetroConfig} */ const config = { + resolver: { + // Ensure Metro can resolve .cjs files (CommonJS modules) + sourceExts: ['jsx', 'js', 'ts', 'tsx', 'json', 'cjs'], + // Workaround for Unicode character issues in @noble/curves + // Files with Greek letters (β, α) cause "Property β does not exist" errors in Hermes + /*resolveRequest: (context, moduleName, platform) => { + // Force cashu-ts to use pre-built CJS bundle + if (moduleName === '@cashu/cashu-ts') { + return { + filePath: context.resolveRequest(context, '@cashu/cashu-ts/lib/cashu-ts.cjs', platform).filePath, + type: 'sourceFile', + }; + } + // Block problematic @noble/curves files that contain Unicode characters + // Allow only essential modules needed for secp256k1 + if (moduleName.startsWith('@noble/curves/')) { + const allowed = [ + 'secp256k1', + 'abstract/modular', + 'abstract/weierstrass', + '_shortw_utils', + 'utils' + ]; + const isAllowed = allowed.some(path => moduleName.includes(path)); + + if (!isAllowed) { + // Return empty stub for blocked imports (contains Unicode chars incompatible with Hermes) + const stubPath = require('path').resolve(__dirname, 'node_modules/.metro-stub.js'); + return { + filePath: stubPath, + type: 'sourceFile', + }; + } + } + + // Default resolution + return context.resolveRequest(context, moduleName, platform); + },*/ + }, }; module.exports = withSentryConfig(mergeConfig(getDefaultConfig(__dirname), config)); diff --git a/package.json b/package.json index 43026597..d7c185cf 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "minibits_wallet", - "version": "0.3.4-beta.13", + "version": "0.3.4-beta.14", "private": true, "scripts": { "android:clean": "cd android && ./gradlew clean", @@ -21,7 +21,7 @@ "machine-translate": "npx @inlang/cli machine translate --project minibits.inlang" }, "dependencies": { - "@cashu/cashu-ts": "minibits-cash/cashu-ts#e97b1a4ee9a7ddbb1468302c6996a9d6d3efcb6f", + "@cashu/cashu-ts": "3.2.2", "@fortawesome/fontawesome-svg-core": "^7.1.0", "@fortawesome/free-brands-svg-icons": "^7.1.0", "@fortawesome/free-regular-svg-icons": "^7.1.0", diff --git a/patches/@scure+bip32+1.6.2.patch b/patches/@scure+bip32+1.6.2.patch index b23b0a2f..58296843 100644 --- a/patches/@scure+bip32+1.6.2.patch +++ b/patches/@scure+bip32+1.6.2.patch @@ -1,53 +1,36 @@ -diff --git a/node_modules/@scure/bip32/index.ts b/node_modules/@scure/bip32/index.ts -index 0c9c91c..14c9ae2 100644 ---- a/node_modules/@scure/bip32/index.ts -+++ b/node_modules/@scure/bip32/index.ts -@@ -25,6 +25,7 @@ import { bytesToHex, concatBytes, createView, hexToBytes, utf8ToBytes } from '@n +diff --git a/node_modules/@scure/bip32/lib/esm/index.js b/node_modules/@scure/bip32/lib/esm/index.js +index ea306d0..c0ed23e 100644 +--- a/node_modules/@scure/bip32/lib/esm/index.js ++++ b/node_modules/@scure/bip32/lib/esm/index.js +@@ -25,6 +25,8 @@ import { bytesToHex, concatBytes, createView, hexToBytes, utf8ToBytes } from '@n import { secp256k1 as secp } from '@noble/curves/secp256k1'; import { mod } from '@noble/curves/abstract/modular'; import { createBase58check } from '@scure/base'; -+import QuickCrypto from 'react-native-quick-crypto'; - ++import quickCrypto from 'react-native-quick-crypto' ++ const Point = secp.ProjectivePoint; const base58check = createBase58check(sha256); -@@ -114,7 +115,8 @@ export class HDKey { - seed.length - ); - } -- const I = hmac(sha512, MASTER_SECRET, seed); -+ // const I = hmac(sha512, MASTER_SECRET, seed); -+ const I = new Uint8Array(QuickCrypto.createHmac('sha512', MASTER_SECRET).update(seed).digest()); - return new HDKey({ - versions, - chainCode: I.slice(32), -@@ -237,7 +239,8 @@ export class HDKey { - // Normal child: serP(point(kpar)) || ser32(index) - data = concatBytes(this.pubKey, data); - } -- const I = hmac(sha512, this.chainCode, data); -+ //const I = hmac(sha512, this.chainCode, data); -+ const I = new Uint8Array(QuickCrypto.createHmac('sha512', this.chainCode).update(data).digest()) - const childTweak = bytesToNumber(I.slice(0, 32)); - const chainCode = I.slice(32); - if (!secp.utils.isValidPrivateKey(childTweak)) { -diff --git a/node_modules/@scure/bip32/package.json b/node_modules/@scure/bip32/package.json -index e866fba..6389575 100644 ---- a/node_modules/@scure/bip32/package.json -+++ b/node_modules/@scure/bip32/package.json -@@ -6,15 +6,8 @@ - "index.ts", - "./lib" - ], -- "main": "./lib/index.js", -- "module": "./lib/esm/index.js", -+ "main": "./index.ts", - "types": "./lib/index.d.ts", -- "exports": { -- ".": { -- "import": "./lib/esm/index.js", -- "require": "./lib/index.js" -- } -- }, - "dependencies": { - "@noble/curves": "~1.8.1", - "@noble/hashes": "~1.7.1", + function bytesToNumber(bytes) { +@@ -89,7 +91,9 @@ export class HDKey { + throw new Error('HDKey: seed length must be between 128 and 512 bits; 256 bits is advised, got ' + + seed.length); + } +- const I = hmac(sha512, MASTER_SECRET, seed); ++ // const I = hmac(sha512, MASTER_SECRET, seed); ++ console.log('[bip32.fromMasterSeed] Using ESM quickCrypto HMAC'); ++ const I = new Uint8Array(quickCrypto.createHmac('sha512', MASTER_SECRET).update(seed).digest()); + return new HDKey({ + versions, + chainCode: I.slice(32), +@@ -206,7 +210,10 @@ export class HDKey { + // Normal child: serP(point(kpar)) || ser32(index) + data = concatBytes(this.pubKey, data); + } +- const I = hmac(sha512, this.chainCode, data); ++ // const I = hmac(sha512, this.chainCode, data); ++ console.log('[bip32.deriveChild] Using ESM quickCrypto HMAC'); ++ const I = new Uint8Array(quickCrypto.createHmac('sha512', this.chainCode).update(data).digest()) ++ + const childTweak = bytesToNumber(I.slice(0, 32)); + const chainCode = I.slice(32); + if (!secp.utils.isValidPrivateKey(childTweak)) { diff --git a/patches/@scure+bip39+1.6.0.patch b/patches/@scure+bip39+1.6.0.patch index c9683a46..b22972d1 100644 --- a/patches/@scure+bip39+1.6.0.patch +++ b/patches/@scure+bip39+1.6.0.patch @@ -1,5 +1,26 @@ +diff --git a/node_modules/@scure/bip39/esm/index.js b/node_modules/@scure/bip39/esm/index.js +index d5734b0..deb1491 100644 +--- a/node_modules/@scure/bip39/esm/index.js ++++ b/node_modules/@scure/bip39/esm/index.js +@@ -32,6 +32,7 @@ import { pbkdf2, pbkdf2Async } from '@noble/hashes/pbkdf2'; + import { sha256, sha512 } from '@noble/hashes/sha2'; + import { abytes, anumber, randomBytes } from '@noble/hashes/utils'; + import { utils as baseUtils } from '@scure/base'; ++import quickCrypto from 'react-native-quick-crypto' + // Japanese wordlist + const isJapanese = (wordlist) => wordlist[0] === '\u3042\u3044\u3053\u304f\u3057\u3093'; + // Normalization replaces equivalent sequences of characters +@@ -157,5 +158,7 @@ export function mnemonicToSeed(mnemonic, passphrase = '') { + * // new Uint8Array([...64 bytes]) + */ + export function mnemonicToSeedSync(mnemonic, passphrase = '') { +- return pbkdf2(sha512, normalize(mnemonic).nfkd, psalt(passphrase), { c: 2048, dkLen: 64 }); ++ console.log('[bip39.mnemonicToSeedSync] Using ESM quickCrypto PBKDF2 Sync'); ++ return new Uint8Array(quickCrypto.pbkdf2Sync(normalize(mnemonic).nfkd, psalt(passphrase), 2048, 64, 'sha512')); ++ //return pbkdf2(sha512, normalize(mnemonic).nfkd, psalt(passphrase), { c: 2048, dkLen: 64 }); + } diff --git a/node_modules/@scure/bip39/src/index.ts b/node_modules/@scure/bip39/src/index.ts -index 4284c3e..6ff83b2 100644 +index 4284c3e..71e39e9 100644 --- a/node_modules/@scure/bip39/src/index.ts +++ b/node_modules/@scure/bip39/src/index.ts @@ -33,6 +33,7 @@ import { pbkdf2, pbkdf2Async } from '@noble/hashes/pbkdf2'; @@ -16,7 +37,7 @@ index 4284c3e..6ff83b2 100644 export function mnemonicToSeedSync(mnemonic: string, passphrase = ''): Uint8Array { - return pbkdf2(sha512, normalize(mnemonic).nfkd, psalt(passphrase), { c: 2048, dkLen: 64 }); + // return pbkdf2(sha512, normalize(mnemonic).nfkd, psalt(passphrase), { c: 2048, dkLen: 64 }); -+ ++ console.log('[mnemonicToSeedSync] Using TS quickCrypto PBKDF2 Sync'); + const updated = new Uint8Array(QuickCrypto.pbkdf2Sync(normalize(mnemonic).nfkd, psalt(passphrase), 2048, 64, 'sha512')); + // console.log('[@scure.bip39.mnemonicToSeedSync] Patched to use QuickCrypto.pbkdf2Sync') + return updated diff --git a/src/App.tsx b/src/App.tsx index 298afaa8..fa478c99 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -97,6 +97,12 @@ function App() { if (isInternetReachable === true) { + if(userSettingsStore.isOnboarded === false) { + log.trace('[App] User not onboarded → new install, skipping device re-enrollment') + setIsDeviceAuthenticated(true) + return + } + if(authStore.isRefreshTokenExpired) { log.trace('[App] Network is online and refresh token expired → attempting device re-enrollment') @@ -110,12 +116,7 @@ function App() { log.trace('[App] Device re-enrollment successful') } catch (e: any) { - if (e.name !== Err.NOTFOUND_ERROR) { - log.error('[App] Device re-enrollment failed', { message: e.message }) - } else { - // Expected on fresh install - userSettingsStore.setIsOnboarded(false) - } + log.error('[App] Device re-enrollment failed', { message: e.message }) } finally { // Always unblock the app setIsDeviceAuthenticated(true) diff --git a/src/components/ErrorModal.tsx b/src/components/ErrorModal.tsx index 9c6d5696..96c42cbb 100644 --- a/src/components/ErrorModal.tsx +++ b/src/components/ErrorModal.tsx @@ -3,7 +3,7 @@ import { View, ScrollView, LayoutAnimation, Platform, UIManager, ViewStyle } fro import { BottomModal, Button, Icon, ListItem, Text } from '../components' import AppError, { Err } from '../utils/AppError' import { spacing, useThemeColor, colors } from '../theme' -import { isObj } from '@cashu/cashu-ts/src/utils' +import { CashuUtils } from '../services/cashu/cashuUtils' import JSONTree from 'react-native-json-tree' import Clipboard from '@react-native-clipboard/clipboard' import { useStores } from '../models' @@ -85,7 +85,7 @@ export const ErrorModal: FC = function ({ error }) { /> ) : ( <> - {error.params && isObj(error.params) && ( + {error.params && CashuUtils.isObj(error.params) && ( <> {isParamsVisible ? ( <> diff --git a/src/models/Mint.ts b/src/models/Mint.ts index 6b1d4a76..5f38774e 100644 --- a/src/models/Mint.ts +++ b/src/models/Mint.ts @@ -4,7 +4,7 @@ import { type GetInfoResponse, type MintKeys as CashuMintKeys, type MintKeyset as CashuMintKeyset, - CashuMint, + Mint as CashuMint, } from '@cashu/cashu-ts' import {colors, getRandomIconColor} from '../theme' import { log } from '../services' @@ -16,7 +16,7 @@ import { generateId } from '../utils/utils' import { Proof } from './Proof' import { CashuProof, CashuUtils } from '../services/cashu/cashuUtils' - +ß export type MintBalance = { mintUrl: string balances: { diff --git a/src/models/MintsStore.ts b/src/models/MintsStore.ts index 1c93785f..56bf7f46 100644 --- a/src/models/MintsStore.ts +++ b/src/models/MintsStore.ts @@ -13,7 +13,9 @@ import { import {log} from '../services/logService' import AppError, { Err } from '../utils/AppError' import { - CashuMint, + Mint as CashuMint, + GetKeysetsResponse, + GetKeysResponse, MintActiveKeys, MintAllKeysets, type MintKeys as CashuMintKeys, @@ -124,8 +126,8 @@ export const MintsStoreModel = types 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/ProofsStore.ts b/src/models/ProofsStore.ts index b71ebb33..4eafbeca 100644 --- a/src/models/ProofsStore.ts +++ b/src/models/ProofsStore.ts @@ -310,7 +310,7 @@ import { log.error('[revertToSpendable]', 'Proof instance is not alive, aborting state update', { secret: proof.secret }) continue } - + proof.setMintUrl(updatedMintUrl) } } @@ -321,6 +321,14 @@ import { Database.updateProofsMintUrl(currentMintUrl, updatedMintUrl) log.trace('[updateMintUrl] Updated mint URL in proofs') }, + + // Action for importing proofs from backup without validation or side effects + importProofs(proofs: Proof[]) { + for (const proof of proofs) { + self.proofs.put(ProofModel.create(proof)) + } + log.trace('[importProofs]', `Imported ${proofs.length} proofs from backup`) + }, })) // ───────────────────── DERIVED VIEWS ───────────────────── diff --git a/src/models/WalletStore.ts b/src/models/WalletStore.ts index 43e05f37..9765b576 100644 --- a/src/models/WalletStore.ts +++ b/src/models/WalletStore.ts @@ -1,20 +1,20 @@ import {Instance, SnapshotOut, types, flow, getRoot, getSnapshot} from 'mobx-state-tree' -import { - CashuMint, - CashuWallet, - MeltQuoteResponse, - setGlobalRequestOptions, +import { + Mint as CashuMint, + Wallet as CashuWallet, + MeltQuoteBolt11Response, + setGlobalRequestOptions, type MintKeys, type MintKeyset, - MintAllKeysets, - MintActiveKeys, Token, - MeltProofsResponse, + MeltProofsResponse, CheckStateEnum, - OutputAmounts, - MintQuoteResponse, + GetKeysetsResponse, + GetKeysResponse, + MintQuoteBolt11Response, + type ProofState, + type OperationCounters, } from '@cashu/cashu-ts' -import { isObj } from '@cashu/cashu-ts/src/utils' import { JS_BUNDLE_VERSION } from '@env' import {KeyChain, MinibitsClient, WalletKeys} from '../services' import {log} from '../services/logService' @@ -25,7 +25,6 @@ import { Proof } from './Proof' import { InFlightRequest, Mint } from './Mint' import { getRootStore } from './helpers/getRootStore' -import { getUnixTime } from 'date-fns/getUnixTime' //refresh /* @@ -43,7 +42,6 @@ export type ReceiveParams = { token: string | Token, options?: { keysetId?: string; - outputAmounts?: OutputAmounts; proofsWeHave?: Array; counter?: number; pubkey?: string; @@ -57,7 +55,6 @@ export type SendParams = { amount: number, proofs: Array, options?: { - outputAmounts?: OutputAmounts; proofsWeHave?: Array; counter?: number; pubkey?: string; @@ -76,7 +73,6 @@ export type MintParams = { quote: string, options?: { keysetId?: string; - outputAmounts?: OutputAmounts; proofsWeHave?: Array; counter?: number; pubkey?: string; @@ -85,7 +81,7 @@ export type MintParams = { export type MeltParams = { - meltQuote: MeltQuoteResponse, + meltQuote: MeltQuoteBolt11Response, proofsToSend: Array, options?: { keysetId?: string; @@ -208,7 +204,7 @@ export const WalletStoreModel = types const mint = self.mints.find(m => m.mintUrl === mintUrl) if (mint) { - return mint + return mint as CashuMint } setGlobalRequestOptions({ @@ -255,7 +251,7 @@ export const WalletStoreModel = types } ) { // syncs mint model in wallet state and returns cashu-ts mint class instance - const cashuMint = yield self.getMint(mintUrl) + const cashuMint: CashuMint = yield self.getMint(mintUrl) // get uptodate mint model from wallet state const mintInstance = self.getMintModelInstance(mintUrl) @@ -315,7 +311,7 @@ export const WalletStoreModel = types if (seedWallet) { log.trace('[WalletStore.getWallet]', 'Returning CACHED cashuWallet instance with seed', {mintUrl}) - return seedWallet + return seedWallet as CashuWallet } const seed = yield self.getCachedSeed() @@ -323,14 +319,14 @@ export const WalletStoreModel = types const newSeedWallet = new CashuWallet(cashuMint, { unit, keys: mintInstance.keys, - keysets: mintInstance.keysets, + keysets: mintInstance.keysets, + keysetId: walletKeys.id, bip39seed: seed }) // Load uptodate mint info to wallet.mintInfo (NUTS support etc) - yield newSeedWallet.getMintInfo() + yield newSeedWallet.loadMint() - newSeedWallet.keysetId = walletKeys.id self.seedWallets.push(newSeedWallet) log.trace('[WalletStore.getWallet]', 'Returning NEW cashuWallet instance with seed', {mintUrl}) @@ -351,11 +347,11 @@ export const WalletStoreModel = types const newWallet = new CashuWallet(cashuMint, { unit, keys: mintInstance.keys, - keysets: mintInstance.keysets, + keysets: mintInstance.keysets, + keysetId: walletKeys.id, bip39seed: undefined }) - newWallet.keysetId = walletKeys.id self.wallets.push(newWallet) log.trace('[WalletStore.getWallet]', 'Returning NEW cashuWallet instance', {mintUrl}) @@ -365,7 +361,7 @@ export const WalletStoreModel = types const cashuMint: CashuMint = yield self.getMint(mintUrl) try { - const {keysets} = yield cashuMint.getKeySets() as Promise // all keysets + const {keysets} = yield cashuMint.getKeySets() as Promise // all keysets return keysets as MintKeyset[] } catch (e: any) { let message = 'Could not connect to the selected mint.' @@ -377,7 +373,7 @@ export const WalletStoreModel = types const cashuMint: CashuMint = yield self.getMint(mintUrl) try { - const {keysets} = yield cashuMint.getKeys() as Promise // all active keys + const {keysets} = yield cashuMint.getKeys() as Promise // all active keys return keysets as MintKeys[] } catch (e: any) { let message = 'Could not connect to the selected mint.' @@ -449,7 +445,7 @@ export const WalletStoreModel = types } } - if(cashuWallet.mintInfo.nuts['19'] && !options?.inFlightRequest) { + if(cashuWallet.getMintInfo().nuts['19'] && !options?.inFlightRequest) { currentCounter.addInFlightRequest(transactionId, receiveParams) } @@ -540,7 +536,7 @@ export const WalletStoreModel = types } } - if(cashuWallet.mintInfo.nuts['19'] && !options?.inFlightRequest) { + if(cashuWallet.getMintInfo().nuts['19'] && !options?.inFlightRequest) { currentCounter.addInFlightRequest(transactionId, sendParams) } @@ -588,31 +584,51 @@ export const WalletStoreModel = types ) } }), - getProofsStatesFromMint: flow(function* getProofsStatesFromMint( + getProofsStatesFromMint: flow(function* getProofsStatesFromMint( mintUrl: string, - unit: MintUnit, + unit: MintUnit, proofs: Proof[] ) { try { log.trace('[WalletStore.getProofsStatesFromMint] start', {mintUrl, unit}) - - const cashuWallet: CashuWallet = yield self.getWallet(mintUrl, unit, {withSeed: false}) - const proofsByState: {[key in CheckStateEnum]: CashuProof[]} = yield cashuWallet.checkProofsStates(proofs) - + + const cashuWallet: CashuWallet = yield self.getWallet(mintUrl, unit, {withSeed: false}) + + // v3.x returns ProofState[] array, not grouped by state + const proofStatesArray: ProofState[] = yield cashuWallet.checkProofsStates(proofs) + + // Transform array into grouped structure expected by the rest of the code + // Map proof states back to original proofs using secret matching + const proofsByState: {[key in CheckStateEnum]: CashuProof[]} = { + SPENT: [], + PENDING: [], + UNSPENT: [] + } + + // proofStatesArray is returned in the same order as input proofs + for (let i = 0; i < proofStatesArray.length; i++) { + const proofState = proofStatesArray[i] + const originalProof = proofs[i] + + if (originalProof) { + proofsByState[proofState.state].push(originalProof as CashuProof) + } + } + log.trace('[WalletStore.getProofsStatesFromMint]', {mintUrl, proofsByState}) - + return proofsByState - - } catch (e: any) { + + } catch (e: any) { let message = 'Could not get response from the mint.' if (isOnionMint(mintUrl)) message += TorVPNSetupInstructions; throw new AppError( - Err.MINT_ERROR, - message, + Err.MINT_ERROR, + message, { message: e.message, - caller: 'WalletStore.getProofsStatesFromMint', - mintUrl + caller: 'WalletStore.getProofsStatesFromMint', + mintUrl } ) } @@ -625,8 +641,8 @@ export const WalletStoreModel = types ) { try { const cashuMint = yield self.getMint(mintUrl) - const mintQuoteResponse: MintQuoteResponse = yield cashuMint.createMintQuote({ - unit, + const mintQuoteResponse: MintQuoteBolt11Response = yield cashuMint.createMintQuoteBolt11({ + unit, amount, description }) @@ -657,7 +673,7 @@ export const WalletStoreModel = types ) { try { const cashuMint: CashuMint = yield self.getMint(mintUrl) - const quoteResponse: MintQuoteResponse = yield cashuMint.checkMintQuote( + const quoteResponse: MintQuoteBolt11Response = yield cashuMint.checkMintQuoteBolt11( quote ) @@ -700,7 +716,7 @@ export const WalletStoreModel = types throw new AppError(Err.VALIDATION_ERROR, 'Missing mint instance', {mintUrl}) } - const cashuWallet = yield self.getWallet( + const cashuWallet: CashuWallet = yield self.getWallet( mintUrl, unit, { @@ -714,34 +730,56 @@ export const WalletStoreModel = types if(options && options.increaseCounterBy) { currentCounter.increaseProofsCounter(options.increaseCounterBy) } - + log.debug('[WalletStore.mintProofs] counter', currentCounter.counter) - + + // Sync wallet's internal counter with our stored counter (v3.x) + yield cashuWallet.counters.advanceToAtLeast(cashuWallet.keysetId, currentCounter.counter) + const mintParams: MintParams = options?.inFlightRequest?.request || { - amount, + amount, quote: mintQuote, - options: { - keysetId: cashuWallet.keysetId, - counter: currentCounter.counter + options: { + keysetId: cashuWallet.keysetId + // Note: counter is no longer passed in v3.x, wallet manages it internally } - } - - if(cashuWallet.mintInfo.nuts['19'] && !options?.inFlightRequest) { + } + + if(cashuWallet.getMintInfo().nuts['19'] && !options?.inFlightRequest) { currentCounter.addInFlightRequest(transactionId, mintParams) } - - try { - - const proofs = yield cashuWallet.mintProofs( + + let reservedCounters: OperationCounters | undefined = undefined + + try { + + const proofs = yield cashuWallet.mintProofsBolt11( mintParams.amount, mintParams.quote, - mintParams.options + { + keysetId: mintParams.options?.keysetId, + onCountersReserved: (info) => { + reservedCounters = info as OperationCounters + log.debug('[mintProofsBolt11] Counters reserved', info) + } + } ) - currentCounter.removeInFlightRequest(transactionId) - - log.debug('[mintProofs]', {amount: mintParams.amount, quote: mintParams.quote, proofs}) - + currentCounter.removeInFlightRequest(transactionId) + + // Update our counter to match what the wallet used (v3.x) + if (reservedCounters) { + currentCounter.counter = reservedCounters.next + log.debug('[mintProofs] Updated counter', { + keysetId: reservedCounters.keysetId, + start: reservedCounters.start, + count: reservedCounters.count, + next: reservedCounters.next + }) + } + + log.debug('[mintProofs]', {amount: mintParams.amount, quote: mintParams.quote, proofs}) + return proofs } catch (e: any) { @@ -770,8 +808,8 @@ export const WalletStoreModel = types encodedInvoice: string, ) { try { - const cashuMint = yield self.getMint(mintUrl) - const lightningQuote: MeltQuoteResponse = yield cashuMint.createMeltQuote({ + const cashuMint: CashuMint = yield self.getMint(mintUrl) + const lightningQuote: MeltQuoteBolt11Response = yield cashuMint.createMeltQuoteBolt11({ unit, request: encodedInvoice }) @@ -797,7 +835,7 @@ export const WalletStoreModel = types payLightningMelt: flow(function* payLightningMelt( mintUrl: string, unit: MintUnit, - meltQuote: MeltQuoteResponse, // invoice is stored by mint by quote + meltQuote: MeltQuoteBolt11Response, // invoice is stored by mint by quote proofsToMeltFrom: Proof[], transactionId: number, options?: { @@ -849,13 +887,13 @@ export const WalletStoreModel = types } } - if(cashuWallet.mintInfo.nuts['19'] && !options?.inFlightRequest) { + if(cashuWallet.getMintInfo().nuts['19'] && !options?.inFlightRequest) { currentCounter.addInFlightRequest(transactionId, meltParams) } - try { - - const meltResponse: MeltProofsResponse = yield cashuWallet.meltProofs( + try { + + const meltResponse = yield cashuWallet.meltProofsBolt11( meltParams.meltQuote, meltParams.proofsToSend, meltParams.options @@ -865,7 +903,7 @@ export const WalletStoreModel = types log.trace('[payLightningMelt]', {meltResponse}) // we normalize naming of returned parameters - return meltResponse + return meltResponse as MeltProofsResponse } catch (e: any) { if(!e.message.toLowerCase().includes('timeout') && @@ -896,7 +934,7 @@ export const WalletStoreModel = types ) { try { const cashuMint: CashuMint = yield self.getMint(mintUrl) - const quoteResponse: MeltQuoteResponse = yield cashuMint.checkMeltQuote( + const quoteResponse: MeltQuoteBolt11Response = yield cashuMint.checkMeltQuoteBolt11( quote ) @@ -920,7 +958,7 @@ export const WalletStoreModel = types }), recoverMeltQuoteChange: flow(function* recoverMeltQuoteChange( mintUrl: string, - meltQuote: MeltQuoteResponse, + meltQuote: MeltQuoteBolt11Response, transactionId: number ) { try { @@ -991,10 +1029,9 @@ export const WalletStoreModel = types unit: 'sat', // just use default unit as we restore by keyset keys: cashuMint.keys, keysets: cashuMint.keysets, + keysetId, bip39seed: seed }) - - seedWallet.keysetId = keysetId const count = Math.abs(indexTo - indexFrom) @@ -1011,7 +1048,7 @@ export const WalletStoreModel = types proofs: proofs || [] as Proof[] } } catch (e: any) { - throw new AppError(Err.MINT_ERROR, isObj(e.message) ? JSON.stringify(e.message) : e.message, {mintUrl}) + throw new AppError(Err.MINT_ERROR, CashuUtils.isObj(e.message) ? JSON.stringify(e.message) : e.message, {mintUrl}) } }), getMintInfo: flow(function* getMintInfo(mintUrl: string) { diff --git a/src/screens/ImportBackupScreen.tsx b/src/screens/ImportBackupScreen.tsx index ea3ef4a7..d667a2cb 100644 --- a/src/screens/ImportBackupScreen.tsx +++ b/src/screens/ImportBackupScreen.tsx @@ -35,7 +35,7 @@ import { MintsStoreSnapshot } from '../models/MintsStore' import { ContactsStoreSnapshot } from '../models/ContactsStore' import { CashuMint, MintActiveKeys } from '@cashu/cashu-ts' import { StaticScreenProps, useNavigation } from '@react-navigation/native' -import { Proof, ProofModel } from '../models/Proof' +import { Proof } from '../models/Proof' type Props = StaticScreenProps @@ -199,11 +199,7 @@ export const ImportBackupScreen = observer(function ImportBackupScreen({ route } } // applySnapshot(proofsStore, walletSnapshot.proofsStore) - for (const proof of walletSnapshot.proofsStore.proofs) { - proofsStore.proofs.put( - ProofModel.create(proof) - ) - } + proofsStore.importProofs(walletSnapshot.proofsStore.proofs) for(const secret of walletSnapshot.proofsStore.pendingByMintSecrets) { proofsStore.pendingByMintSecrets.push(secret) } diff --git a/src/screens/MintInfoScreen.tsx b/src/screens/MintInfoScreen.tsx index f7888a33..3ad569ef 100644 --- a/src/screens/MintInfoScreen.tsx +++ b/src/screens/MintInfoScreen.tsx @@ -1,6 +1,6 @@ import React, { useEffect, useMemo, useState } from 'react' import { GetInfoResponse as CashuGetInfoResponse, SwapMethod } from '@cashu/cashu-ts' -import { isObj } from '@cashu/cashu-ts/src/utils' +import { CashuUtils } from '../services/cashu/cashuUtils' import Clipboard from '@react-native-clipboard/clipboard' import { observer } from 'mobx-react-lite' import { getSnapshot } from 'mobx-state-tree' @@ -529,7 +529,7 @@ function MintInfoDetails(props: { info: GetInfoResponse, popupMessage: (msg: str text={translate('mintInfo_emptyValueParam', { param: key })} /> - let stringValue = isObj(value) ? JSON.stringify(value) : value.toString() ?? '' + let stringValue = CashuUtils.isObj(value) ? JSON.stringify(value) : value.toString() ?? '' let valueComponent = stringValue.trim() !== '' ? : missingComponent; diff --git a/src/screens/SeedRecoveryScreen.tsx b/src/screens/SeedRecoveryScreen.tsx index 242feb1c..db02ae05 100644 --- a/src/screens/SeedRecoveryScreen.tsx +++ b/src/screens/SeedRecoveryScreen.tsx @@ -36,7 +36,6 @@ import { getSnapshot } from 'mobx-state-tree' import { scale } from '@gocodingnow/rn-size-matters' import { WalletUtils } from '../services/wallet/utils' import { MintUnit, formatCurrency, getCurrency } from '../services/wallet/currency' -import { isObj } from '@cashu/cashu-ts/src/utils' import { translate } from '../i18n' import { WalletProfileRecord } from '../models/WalletProfileStore' import { MnemonicInput } from './Recovery/MnemonicInput' @@ -357,7 +356,7 @@ export const SeedRecoveryScreen = observer(function SeedRecoveryScreen({ route } e.params = {mintUrl: selectedMintUrl} } - log.error('[doRecovery]', {name: e.name, message: isObj(e.message) ? JSON.stringify(e.message) : e.message, params: e.params}) + log.error('[doRecovery]', {name: e.name, message: CashuUtils.isObj(e.message) ? JSON.stringify(e.message) : e.message, params: e.params}) errors.push({name: e.name, message: e.message}) // TODO this could now be single error as we do not loop anymore if (transaction) { diff --git a/src/screens/SendScreen.tsx b/src/screens/SendScreen.tsx index 9e899cfd..1a1ecdc2 100644 --- a/src/screens/SendScreen.tsx +++ b/src/screens/SendScreen.tsx @@ -55,7 +55,6 @@ 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' import FastImage from 'react-native-fast-image' import { CurrencyAmount } from './Wallet/CurrencyAmount' import { CashuUtils } from '../services/cashu/cashuUtils' @@ -128,7 +127,6 @@ export const SendScreen = observer(function SendScreen({ route }: Props) { const [isCashuPrWithDesc, setIsCashuPrWithDesc] = useState(false) const [isNostrDMModalVisible, setIsNostrDMModalVisible] = useState(false) const [isProofSelectorModalVisible, setIsProofSelectorModalVisible] = useState(false) // offline mode - const [isSendTaskSentToQueue, setIsSendTaskSentToQueue] = useState(false) const [isResultModalVisible, setIsResultModalVisible] = useState(false) const [isNostrDMSending, setIsNostrDMSending] = useState(false) const [isNostrDMSuccess, setIsNostrDMSuccess] = useState(false) @@ -770,8 +768,6 @@ export const SendScreen = observer(function SendScreen({ route }: Props) { } } - setIsSendTaskSentToQueue(true) - const result = await WalletTask.sendQueueAwaitable( mintBalanceToSendFrom as MintBalance, amountToSendInt, diff --git a/src/screens/Transactions/TransactionListItem.tsx b/src/screens/Transactions/TransactionListItem.tsx index 194b31eb..cb155f95 100644 --- a/src/screens/Transactions/TransactionListItem.tsx +++ b/src/screens/Transactions/TransactionListItem.tsx @@ -255,7 +255,7 @@ export const TransactionListItem = observer(function (props: TransactionListProp amountStyle={{color: txReceiveColor}} /> )} - {[TransactionStatus.ERROR, TransactionStatus.BLOCKED, TransactionStatus.PREPARED_OFFLINE, TransactionStatus.EXPIRED].includes(tx.status) && ( + {[TransactionStatus.ERROR, TransactionStatus.BLOCKED, TransactionStatus.PREPARED_OFFLINE, TransactionStatus.PENDING, TransactionStatus.EXPIRED].includes(tx.status) && ( lnurlPayParams.maxSendable / 1000) { @@ -506,7 +506,7 @@ const onRequestLnurlInvoice = async function () { amount: roundDown(lnurlPayParams.maxSendable / 1000, 0), currency: CurrencyCode.SAT })) - return; + return } if (lnurlPayParams.payerData) { diff --git a/src/services/cashu/cashuUtils.ts b/src/services/cashu/cashuUtils.ts index 3dbd6919..3f145ff2 100644 --- a/src/services/cashu/cashuUtils.ts +++ b/src/services/cashu/cashuUtils.ts @@ -10,13 +10,27 @@ import AppError, {Err} from '../../utils/AppError' import { getDecodedToken } from '@cashu/cashu-ts' import {Proof} from '../../models/Proof' import { log } from '../logService' -import { decodePaymentRequest, sumProofs } from '@cashu/cashu-ts/src/utils' +import { decodePaymentRequest } from '@cashu/cashu-ts' import { NostrClient } from '../nostrService' import { getUnixTime } from 'date-fns/getUnixTime' import { getSnapshot, isStateTreeNode } from 'mobx-state-tree' export {CashuProof} +/** + * Type guard to check if a value is an object + */ +const isObj = function(v: unknown): v is object { + return typeof v === 'object' +} + +/** + * Sum the amounts of an array of proofs + */ +const sumProofs = function(proofs: CashuProof[]): number { + return proofs.reduce((acc: number, proof: CashuProof) => acc + proof.amount, 0) +} + const CASHU_URI_PREFIXES = [ 'https://wallet.nutstash.app/#', 'https://wallet.cashu.me/?token=', @@ -395,7 +409,7 @@ export const CashuUtils = { findEncodedCashuPaymentRequestPayload, extractEncodedCashuToken, extractEncodedCashuPaymentRequest, - getProofsAmount, + getProofsAmount, getMintsFromToken, findExactMatch, findMinExcess, @@ -407,7 +421,9 @@ export const CashuUtils = { getP2PKPubkeySecret, getP2PKLocktime, isTokenP2PKLocked, - isCollidingKeysetId + isCollidingKeysetId, + isObj, + sumProofs } diff --git a/src/services/keyChain.ts b/src/services/keyChain.ts index 174455e3..4ac2f4f6 100644 --- a/src/services/keyChain.ts +++ b/src/services/keyChain.ts @@ -90,6 +90,7 @@ const generateWalletKeys = function () { const mnemonic = generateMnemonic() const seedBytesArray = mnemonicToSeedSync(mnemonic) const seed = Buffer.from(seedBytesArray).toString('base64') + log.trace('[generateWalletKeys] Seed generated from mnemonic (base64)', {seed}) const seedHash = QuickCrypto .createHash('sha256') .update(seedBytesArray) diff --git a/src/services/wallet/receiveTask.ts b/src/services/wallet/receiveTask.ts index 09653abe..b3fd3c71 100644 --- a/src/services/wallet/receiveTask.ts +++ b/src/services/wallet/receiveTask.ts @@ -12,8 +12,7 @@ import { TransactionTaskResult } from '../walletService' import { WalletUtils } from './utils' import { MintUnit, formatCurrency, getCurrency } from './currency' import { PaymentRequestPayload, Token, getDecodedToken } from '@cashu/cashu-ts' -import { getEncodedToken, getKeepAmounts } from '@cashu/cashu-ts/src/utils' -import { Proof } from '../../models/Proof' +import { getEncodedToken } from '@cashu/cashu-ts' const { mintsStore, diff --git a/src/services/wallet/revertTask.ts b/src/services/wallet/revertTask.ts index 8610dc86..a091b73c 100644 --- a/src/services/wallet/revertTask.ts +++ b/src/services/wallet/revertTask.ts @@ -10,7 +10,7 @@ import { TransactionTaskResult } from '../walletService' import { WalletUtils } from './utils' import { MintUnit } from './currency' import { Token } from '@cashu/cashu-ts' -import { getEncodedToken, getKeepAmounts } from '@cashu/cashu-ts/src/utils' +import { getEncodedToken, getKeepAmounts } from '@cashu/cashu-ts' const { mintsStore, diff --git a/src/services/wallet/sendTask.ts b/src/services/wallet/sendTask.ts index 9dd933e7..4a0da74f 100644 --- a/src/services/wallet/sendTask.ts +++ b/src/services/wallet/sendTask.ts @@ -8,9 +8,9 @@ import { import {rootStoreInstance} from '../../models' import {CashuUtils, CashuProof} from '../cashu/cashuUtils' import AppError, {Err} from '../../utils/AppError' -import { - CashuMint, - CashuWallet, +import { + Mint as CashuMint, + Wallet as CashuWallet, CheckStateEnum, MintKeyset, ProofState, @@ -144,7 +144,7 @@ export const sendTask = async function ( try { log.trace('[send] Subscribing to onProofStateUpdates for proof', {secret: proofsToSend[0]}) - const unsub = await wsWallet.onProofStateUpdates( + const unsub = await wsWallet.on.proofStateUpdates( [proofsToSend[0]], async (proofState: ProofState) => { log.trace(`Websocket: proof state updated: ${proofState.state} with secret: ${proofsToSend[0].secret}`) @@ -338,7 +338,7 @@ export const sendFromMintSync = async function ( */ if(isSwapNeeded) { // Calculate feeReserve from mint fee rate - const walletInstance = await walletStore.getWallet(mintUrl, unit, {withSeed: true}) + const walletInstance = await walletStore.getWallet(mintUrl, unit, {withSeed: true}) as CashuWallet swapFeeReserve = walletInstance.getFeesForProofs(proofsToSendFrom) const amountWithFees = amountToSend + swapFeeReserve diff --git a/src/services/wallet/topupTask.ts b/src/services/wallet/topupTask.ts index 7e04e99e..b10d6594 100644 --- a/src/services/wallet/topupTask.ts +++ b/src/services/wallet/topupTask.ts @@ -11,7 +11,11 @@ import { WalletUtils } from './utils' import { MintUnit } from './currency' import { NostrEvent } from '../nostrService' import AppError, { Err } from '../../utils/AppError' -import { CashuMint, CashuWallet, MintQuoteResponse } from '@cashu/cashu-ts' +import { + MintQuoteBolt11Response, + Mint as CashuMint, + Wallet as CashuWallet, + } from '@cashu/cashu-ts' import { addSeconds } from 'date-fns/addSeconds' const { @@ -125,9 +129,9 @@ export const topupTask = async function ( const wsWallet = new CashuWallet(wsMint) try { - const unsub = await wsWallet.onMintQuotePaid( + const unsub = await wsWallet.on.mintQuotePaid( mintQuote, - async (m: MintQuoteResponse) => { + async (m: MintQuoteBolt11Response) => { log.trace(`Websocket: mint quote PAID: ${m.quote}`) WalletTask.handlePendingQueue() unsub() diff --git a/src/services/wallet/transferTask.ts b/src/services/wallet/transferTask.ts index 0ddda380..8f1ffb13 100644 --- a/src/services/wallet/transferTask.ts +++ b/src/services/wallet/transferTask.ts @@ -1,6 +1,6 @@ import {CashuUtils} from '../cashu/cashuUtils' import AppError, {Err} from '../../utils/AppError' -import {MeltProofsResponse, MeltQuoteResponse, MeltQuoteState, getEncodedToken} from '@cashu/cashu-ts' +import {MeltProofsResponse, MeltQuoteBolt11Response, MeltQuoteResponse, MeltQuoteState, getEncodedToken} from '@cashu/cashu-ts' import {rootStoreInstance} from '../../models' import { TransactionTaskResult, WalletTask } from '../walletService' import { MintBalance } from '../../models/Mint' @@ -50,7 +50,8 @@ export const transferTask = async function ( let proofsToMeltFrom: Proof[] = [] let proofsToMeltFromAmount: number = 0 let meltFeeReserve: number = 0 - let meltResponse: MeltProofsResponse + let meltResponse: MeltProofsResponse + let meltQuoteCheck: MeltQuoteBolt11Response try { @@ -247,7 +248,9 @@ export const transferTask = async function ( totalFeePaid = totalFeePaid - returnedAmount lightningFeePaid = totalFeePaid - meltFeeReserve - } + } + + meltQuoteCheck = await walletStore.checkLightningMeltQuote(mintUrl, meltQuote.quote) const balanceAfter = proofsStore.getUnitBalance(unit)?.unitBalance @@ -257,7 +260,7 @@ export const transferTask = async function ( lightningFeePaid, meltFeePaid, returnedAmount, - preimage: meltResponse.quote.payment_preimage, + preimage: meltQuoteCheck.payment_preimage, createdAt: new Date(), } transactionData.push(completedDataItem) @@ -273,8 +276,8 @@ export const transferTask = async function ( updatePayload.outputToken = outputToken } - if (meltResponse.quote.payment_preimage) { - updatePayload.proof = meltResponse.quote.payment_preimage + if (meltQuoteCheck.payment_preimage) { + updatePayload.proof = meltQuoteCheck.payment_preimage } transaction.update(updatePayload) @@ -287,8 +290,8 @@ export const transferTask = async function ( lightningFeePaid, meltFeePaid, totalFeePaid, - meltQuote: meltResponse.quote, - preimage: meltResponse.quote.payment_preimage, + meltQuote: meltQuoteCheck, + preimage: meltQuoteCheck.payment_preimage, nwcEvent } as TransactionTaskResult @@ -330,31 +333,31 @@ export const transferTask = async function ( if (proofsToMeltFrom.length > 0) { - const refreshedMeltQuote = await walletStore.checkLightningMeltQuote(mintUrl, meltQuote.quote) - taskResult.meltQuote = refreshedMeltQuote + meltQuoteCheck = await walletStore.checkLightningMeltQuote(mintUrl, meltQuote.quote) + taskResult.meltQuote = meltQuoteCheck - if(refreshedMeltQuote.state === MeltQuoteState.PAID) { + if(meltQuoteCheck.state === MeltQuoteState.PAID) { message = `Lightning invoice has been successfully paid, however some error occured: ${e.message}` - taskResult.preimage = refreshedMeltQuote.payment_preimage + taskResult.preimage = meltQuoteCheck.payment_preimage taskResult.message = message const {recoveredAmount} = await WalletTask.recoverMeltQuoteChange({ mintUrl, - meltQuote: refreshedMeltQuote, + meltQuote: meltQuoteCheck, }) log.error('[transfer]', message, { recoveredAmount, error: e.message, - refreshedMeltQuote, + meltQuoteCheck, unit, transactionId: transaction.id }) - } else if(refreshedMeltQuote.state === MeltQuoteState.PENDING) { + } else if(meltQuoteCheck.state === MeltQuoteState.PENDING) { message = 'Lightning payment did not complete in time. Your ecash will remain pending until the payment completes or fails.' taskResult.message = message @@ -362,7 +365,7 @@ export const transferTask = async function ( log.error('[transfer]', message, { error: `${e.message}: ${e.params.message}`, unit, - refreshedMeltQuote, + meltQuoteCheck, transactionId: transaction.id }) diff --git a/src/services/wallet/utils.ts b/src/services/wallet/utils.ts index 1024a4f0..9ae504fc 100644 --- a/src/services/wallet/utils.ts +++ b/src/services/wallet/utils.ts @@ -1,10 +1,10 @@ import AppError from "../../utils/AppError" -import { isObj } from '@cashu/cashu-ts/src/utils' +import { CashuUtils } from '../cashu/cashuUtils' const formatError = function (e: AppError) { return { name: e.name, - message: isObj(e.message) ? JSON.stringify(e.message) : e.message.slice(0, 200), + message: CashuUtils.isObj(e.message) ? JSON.stringify(e.message) : e.message.slice(0, 200), params: e.params || {}, } as AppError } diff --git a/src/services/walletService.ts b/src/services/walletService.ts index b9d0f3b3..9e39845b 100644 --- a/src/services/walletService.ts +++ b/src/services/walletService.ts @@ -15,7 +15,7 @@ import {CashuProof, CashuUtils} from './cashu/cashuUtils' import {LightningUtils} from './lightning/lightningUtils' import AppError, {Err} from '../utils/AppError' import {MintBalance, MintStatus} from '../models/Mint' -import {MeltQuoteResponse, MeltQuoteState, MintQuoteState, PaymentRequestPayload, Token, getDecodedToken, getEncodedToken} from '@cashu/cashu-ts' +import {MeltQuoteBolt11Response, MeltQuoteResponse, MeltQuoteState, MintQuoteState, PaymentRequestPayload, Token, getDecodedToken, getEncodedToken} from '@cashu/cashu-ts' import {Mint} from '../models/Mint' import {pollerExists, stopPolling} from '../utils/poller' import { NostrClient, NostrEvent, NostrProfile } from './nostrService' @@ -40,7 +40,7 @@ import { MinibitsClient } from './minibitsService' import { UnsignedEvent } from 'nostr-tools' import { Platform } from 'react-native' import { cashuPaymentRequestTask } from './wallet/cashuPaymentRequestTask' -import { decodePaymentRequest } from '@cashu/cashu-ts/src/utils' +import { decodePaymentRequest } from '@cashu/cashu-ts' @@ -168,7 +168,7 @@ export interface TransactionTaskResult extends WalletTaskResult { swapFeePaid?: number lightningFeePaid?: number meltFeePaid?: number - meltQuote?: MeltQuoteResponse + meltQuote?: MeltQuoteBolt11Response nwcEvent?: NostrEvent } @@ -1636,7 +1636,16 @@ const handleInFlightByMintTask = async (mint: Mint): Promise = // ─── TRANSFER (melt / lightning out retry) ───────────── case TransactionType.TRANSFER: { - const { quote, change } = await walletStore.payLightningMelt( + const meltQuoteCheck = await walletStore.checkLightningMeltQuote(mintUrl, inFlight.request.meltQuote) + + if(meltQuoteCheck.state === MeltQuoteState.PAID || meltQuoteCheck.state === MeltQuoteState.PENDING) { + log.debug('[handleInFlightByMintTask] Melt quote already PAID or PENDING', { tId: tx.id }) + // TODO recover change if any + counter.removeInFlightRequest(inFlight.transactionId) + break + } + + const { change } = await walletStore.payLightningMelt( mintUrl, unit, inFlight.request.meltQuote, @@ -1667,9 +1676,9 @@ const handleInFlightByMintTask = async (mint: Mint): Promise = tx.update({ outputToken }) } - if (quote.payment_preimage) { + /*if (quote.payment_preimage) { tx.update({ proof: quote.payment_preimage }) - } + }*/ const inputAmount = CashuUtils.getProofsAmount(inFlight.request.proofsToSend) const changeAmount = CashuUtils.getProofsAmount(change) @@ -1680,7 +1689,7 @@ const handleInFlightByMintTask = async (mint: Mint): Promise = txData.push({ status: TransactionStatus.COMPLETED, changeAmount, - preimage: quote.payment_preimage, + //preimage: quote.payment_preimage, createdAt: new Date(), }) @@ -2060,7 +2069,7 @@ const recoverMintQuote = async ( const recoverMeltQuoteChange = async ( params: { mintUrl: string - meltQuote: string | MeltQuoteResponse + meltQuote: string | MeltQuoteBolt11Response } ): Promise<{ recoveredAmount: number }> => { const { mintUrl, meltQuote } = params @@ -2073,7 +2082,7 @@ const recoverMintQuote = async ( log.trace('[recoverMeltQuoteChange] start', { mintUrl, meltQuote }) - const meltQuoteResponse: MeltQuoteResponse = + const meltQuoteResponse: MeltQuoteBolt11Response = typeof meltQuote === 'string' ? await walletStore.checkLightningMeltQuote(mintUrl, meltQuote) : meltQuote diff --git a/src/utils/AppError.ts b/src/utils/AppError.ts index 1376c784..9c44b6d8 100644 --- a/src/utils/AppError.ts +++ b/src/utils/AppError.ts @@ -25,7 +25,7 @@ export enum Err { } export class AppError extends Error { - public readonly code: Err + public readonly code: number | undefined public readonly params?: Record public readonly caller?: string diff --git a/yarn.lock b/yarn.lock index 5bde8c31..72102785 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2722,28 +2722,14 @@ __metadata: languageName: node linkType: hard -"@cashu/cashu-ts@minibits-cash/cashu-ts#e97b1a4ee9a7ddbb1468302c6996a9d6d3efcb6f": - version: 2.4.0 - resolution: "@cashu/cashu-ts@https://github.com/minibits-cash/cashu-ts.git#commit=e97b1a4ee9a7ddbb1468302c6996a9d6d3efcb6f" +"@cashu/cashu-ts@npm:3.2.2": + version: 3.2.2 + resolution: "@cashu/cashu-ts@npm:3.2.2" dependencies: - "@cashu/crypto": ^0.3.4 - "@noble/curves": ^1.3.0 - "@noble/hashes": ^1.3.3 - buffer: ^6.0.3 - checksum: 68d46ceb9c60e9cef305aad86534c08b23de434de1efcd217d00c73bdf84704801c3cd700901903256de5a408caf029cea7886e9a303dd423027f08184bc1cb5 - languageName: node - linkType: hard - -"@cashu/crypto@npm:^0.3.4": - version: 0.3.4 - resolution: "@cashu/crypto@npm:0.3.4" - dependencies: - "@noble/curves": ^1.6.0 - "@noble/hashes": ^1.5.0 - "@scure/bip32": ^1.5.0 - "@scure/bip39": ^1.4.0 - buffer: ^6.0.3 - checksum: 634afdd9d8fb262cd3db415794661ba5810f4ba1e64d1af0972c9a9b0a8c45106710a00ee25949014a361458ef544bdfcafe926c548c95b05fff9a41600d34a3 + "@noble/curves": ^2.0.1 + "@noble/hashes": ^2.0.1 + "@scure/bip32": ^2.0.1 + checksum: 49516a22f3afbf249c52d0f36a9e6513948b763af94c703a05375b513b0d3f90bb1feaade201214d315f5a26305e182ee00493634519ad9b69c7bd44615de11c languageName: node linkType: hard @@ -5299,12 +5285,12 @@ __metadata: languageName: node linkType: hard -"@noble/curves@npm:^1.3.0, @noble/curves@npm:^1.6.0, @noble/curves@npm:~1.8.1": - version: 1.8.1 - resolution: "@noble/curves@npm:1.8.1" +"@noble/curves@npm:2.0.1, @noble/curves@npm:^2.0.1": + version: 2.0.1 + resolution: "@noble/curves@npm:2.0.1" dependencies: - "@noble/hashes": 1.7.1 - checksum: 4143f1248ed57c1ae46dfef5c692a91383e5830420b9c72d3ff1061aa9ebbf8999297da6d2aed8a9716fef8e6b1f5a45737feeab02abf55ca2a4f514bf9339ec + "@noble/hashes": 2.0.1 + checksum: b6844f350d629bb6de55d3a123bc148403901c8b8eed77d2132a389d080d553242bcf1e06913bf51f51e395849ec70b9a8e8902c19e562ea3c6538bb0240d92f languageName: node linkType: hard @@ -5317,6 +5303,15 @@ __metadata: languageName: node linkType: hard +"@noble/curves@npm:~1.8.1": + version: 1.8.1 + resolution: "@noble/curves@npm:1.8.1" + dependencies: + "@noble/hashes": 1.7.1 + checksum: 4143f1248ed57c1ae46dfef5c692a91383e5830420b9c72d3ff1061aa9ebbf8999297da6d2aed8a9716fef8e6b1f5a45737feeab02abf55ca2a4f514bf9339ec + languageName: node + linkType: hard + "@noble/hashes@npm:1.3.1": version: 1.3.1 resolution: "@noble/hashes@npm:1.3.1" @@ -5331,13 +5326,20 @@ __metadata: languageName: node linkType: hard -"@noble/hashes@npm:1.7.1, @noble/hashes@npm:^1.3.3, @noble/hashes@npm:^1.5.0, @noble/hashes@npm:~1.7.1": +"@noble/hashes@npm:1.7.1, @noble/hashes@npm:^1.3.3, @noble/hashes@npm:~1.7.1": version: 1.7.1 resolution: "@noble/hashes@npm:1.7.1" checksum: 4f1b56428a10323feef17e4f437c9093556cb18db06f94d254043fadb69c3da8475f96eb3f8322d41e8670117d7486475a8875e68265c2839f60fd03edd6a616 languageName: node linkType: hard +"@noble/hashes@npm:2.0.1, @noble/hashes@npm:^2.0.1": + version: 2.0.1 + resolution: "@noble/hashes@npm:2.0.1" + checksum: aea67a671ca464027cd512d07666a804bd99abc343ba98c93ee8463a1c870c5594f8de468279ef3c60f3b23b6330db9f1a92bd23723b8a911ec73bddc9420248 + languageName: node + linkType: hard + "@noble/hashes@npm:^1.1.5, @noble/hashes@npm:~1.8.0": version: 1.8.0 resolution: "@noble/hashes@npm:1.8.0" @@ -6275,6 +6277,13 @@ __metadata: languageName: node linkType: hard +"@scure/base@npm:2.0.0": + version: 2.0.0 + resolution: "@scure/base@npm:2.0.0" + checksum: 15d10f154256f52bb3e02b63cdf93b6df6db3814fa4c61a8bb2d339831aec7859609fb715b93c7fc852755f0fe67fb6411664c06159745711bf38af3b10ab93e + languageName: node + linkType: hard + "@scure/base@npm:~1.1.0": version: 1.1.9 resolution: "@scure/base@npm:1.1.9" @@ -6282,7 +6291,7 @@ __metadata: languageName: node linkType: hard -"@scure/base@npm:~1.2.2, @scure/base@npm:~1.2.4": +"@scure/base@npm:~1.2.2": version: 1.2.4 resolution: "@scure/base@npm:1.2.4" checksum: db554eb550a1bd17684af9282e1ad751050a13d4add0e83ad61cc496680d7d1c1c1120ca780e72935a293bb59721c20a006a53a5eec6f6b5bdcd702cf27c8cae @@ -6307,7 +6316,7 @@ __metadata: languageName: node linkType: hard -"@scure/bip32@npm:1.6.2, @scure/bip32@npm:^1.5.0": +"@scure/bip32@npm:1.6.2": version: 1.6.2 resolution: "@scure/bip32@npm:1.6.2" dependencies: @@ -6318,6 +6327,17 @@ __metadata: languageName: node linkType: hard +"@scure/bip32@npm:^2.0.1": + version: 2.0.1 + resolution: "@scure/bip32@npm:2.0.1" + dependencies: + "@noble/curves": 2.0.1 + "@noble/hashes": 2.0.1 + "@scure/base": 2.0.0 + checksum: 5e6c7b455c4a5599673d5fa1b7edc1b3655fe2f7b8388dc19ebe4a89adb9c80c511215d4af624eb774c63c216fd8b7bb8fecf1eb8c3d5e979e279c7542657a42 + languageName: node + linkType: hard + "@scure/bip39@npm:1.2.1": version: 1.2.1 resolution: "@scure/bip39@npm:1.2.1" @@ -6338,16 +6358,6 @@ __metadata: languageName: node linkType: hard -"@scure/bip39@npm:^1.4.0": - version: 1.5.4 - resolution: "@scure/bip39@npm:1.5.4" - dependencies: - "@noble/hashes": ~1.7.1 - "@scure/base": ~1.2.4 - checksum: 744f302559ad05ee6ea4928572ac8f0b5443e8068fd53234c9c2e158814e910a043c54f0688d05546decadd2ff66e0d0c76355d10e103a28cb8f44efe140857a - languageName: node - linkType: hard - "@sentry-internal/browser-utils@npm:10.26.0": version: 10.26.0 resolution: "@sentry-internal/browser-utils@npm:10.26.0" @@ -14456,7 +14466,7 @@ __metadata: "@babel/plugin-proposal-export-namespace-from": ^7.18.9 "@babel/preset-env": ^7.25.3 "@babel/runtime": ^7.25.0 - "@cashu/cashu-ts": "minibits-cash/cashu-ts#e97b1a4ee9a7ddbb1468302c6996a9d6d3efcb6f" + "@cashu/cashu-ts": 3.2.2 "@fortawesome/fontawesome-svg-core": ^7.1.0 "@fortawesome/free-brands-svg-icons": ^7.1.0 "@fortawesome/free-regular-svg-icons": ^7.1.0