diff --git a/package.json b/package.json index 7c84661b..34ca5261 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "minibits_wallet", - "version": "0.1.10-beta.4", + "version": "0.1.10-beta.5", "private": true, "scripts": { "android:clean": "cd android && ./gradlew clean", @@ -19,8 +19,7 @@ "postinstall": "patch-package --exclude 'nothing'" }, "dependencies": { - "@cashu/cashu-ts": "minibits-cash/cashu-ts#commit=8dcac7ce6d192c419aee5547f0db03c23b593a57", - "@exodus/borc": "^2.1.1", + "@cashu/cashu-ts": "minibits-cash/cashu-ts#cashu-ts-2.0", "@fortawesome/fontawesome-svg-core": "^6.6.0", "@fortawesome/free-brands-svg-icons": "^6.6.0", "@fortawesome/free-regular-svg-icons": "^6.6.0", diff --git a/src/models/Mint.ts b/src/models/Mint.ts index 86d87602..9d0362b5 100644 --- a/src/models/Mint.ts +++ b/src/models/Mint.ts @@ -14,8 +14,8 @@ import AppError, { Err } from '../utils/AppError' import { MintUnit, MintUnits } from '../services/wallet/currency' import { getRootStore } from './helpers/getRootStore' import { generateId } from '../utils/utils' -import { ProofV3 } from '../services/cashu/cashuUtils' import { Proof } from './Proof' +import { CashuProof } from '../services/cashu/cashuUtils' export type MintBalance = { @@ -369,7 +369,7 @@ export const MintModel = types self.proofsCounters = cast(self.proofsCounters) }, - getMintFeeReserve(proofs: ProofV3[] | Proof[]): number { + getMintFeeReserve(proofs: CashuProof[] | Proof[]): number { // Find the corresponding keyset for each proof and sum the input fees const totalInputFees = proofs.reduce((sum, proof) => { const keyset = self.keysets.find(k => k.id === proof.id) diff --git a/src/models/WalletStore.ts b/src/models/WalletStore.ts index 2b06b30b..7fee3090 100644 --- a/src/models/WalletStore.ts +++ b/src/models/WalletStore.ts @@ -1,30 +1,31 @@ import {Instance, SnapshotOut, types, flow, getRoot, getSnapshot} from 'mobx-state-tree' -import { - AmountPreference, +import { CashuMint, CashuWallet, - MeltQuoteResponse, - MeltTokensResponse, - setGlobalRequestOptions, - deriveSeedFromMnemonic, + MeltQuoteResponse, + setGlobalRequestOptions, type MintKeys, type MintKeyset, MintAllKeysets, - MintActiveKeys + MintActiveKeys, + OutputAmounts, + Token, + MeltProofsResponse, + ProofState, + CheckStateEnum } from '@cashu/cashu-ts' -import { debounce } from "lodash" +import { isObj } from '@cashu/cashu-ts/src/utils' import { JS_BUNDLE_VERSION } from '@env' import {KeyChain, MinibitsClient} from '../services' import {log} from '../services/logService' import AppError, { Err } from '../utils/AppError' import { Currencies, CurrencyCode, MintUnit } from '../services/wallet/currency' -import { CashuUtils, ProofV3, TokenV3 } from '../services/cashu/cashuUtils' +import { CashuProof, CashuUtils } from '../services/cashu/cashuUtils' import { Proof } from './Proof' -import { isObj } from '@cashu/cashu-ts/src/utils' + import { Mint } from './Mint' import { getRootStore } from './helpers/getRootStore' -import { getUnixTime } from 'date-fns/getUnixTime' -import { boolean } from 'mobx-state-tree/dist/internal' +import {deriveSeedFromMnemonic} from '@cashu/crypto/modules/client/NUT09' /* Not persisted, in-memory only model of the cashu-ts wallet instances and seed. @@ -136,7 +137,7 @@ export const WalletStoreModel = types return new Uint8Array(Buffer.from(self.seedBase64, 'base64')) } - const seed = yield KeyChain.loadSeed() + const seed: Uint8Array = yield KeyChain.loadSeed() if (!seed) { return undefined @@ -218,8 +219,7 @@ export const WalletStoreModel = types // syncs mint model in wallet state and returns cashu-ts mint class instance const cashuMint = yield self.getMint(mintUrl) - // get uptodate mint model from wallet state - // const mintsStore = getRootStore(self).mintsStore + // get uptodate mint model from wallet state const mintInstance = self.getMintModelInstance(mintUrl) if(!mintInstance) { throw new AppError(Err.NOTFOUND_ERROR, 'Mint not found in the wallet state.', { @@ -234,7 +234,7 @@ export const WalletStoreModel = types const requestedKeys = mintInstance.keys!.find((k: MintKeys) => k.id === options.keysetId) if(!requestedKeys) { - throw new AppError(Err.NOTFOUND_ERROR, 'Wallet has not keys with provided keyset id.', { + throw new AppError(Err.NOTFOUND_ERROR, 'Mint has no keys with provided keyset id.', { mintUrl, keysetId: options.keysetId }) @@ -253,7 +253,7 @@ export const WalletStoreModel = types // if not we find active keyset with lowest fees and related keys const activeKeyset: MintKeyset = self.getOptimalKeyset(mintInstance, unit) // throws - log.trace('[WalletStore.getWallet]', {activeKeyset, mintUrl}) + log.trace('[WalletStore.getWallet] Optimal keyset for this unit', {activeKeyset, unit, mintUrl}) const activeKeys = mintInstance.keys!.find((k: MintKeys) => k.id === activeKeyset.id) @@ -271,7 +271,7 @@ export const WalletStoreModel = types const seedWallet: CashuWallet | undefined = self.seedWallets.find( w => w.mint.mintUrl === mintUrl && - w.keys.id === walletKeys.id + w.keysetId === walletKeys.id ) if (seedWallet) { @@ -283,10 +283,13 @@ export const WalletStoreModel = types seed = yield self.getSeed() const newSeedWallet = new CashuWallet(cashuMint, { - keys: walletKeys, - mnemonicOrSeed: seed + unit, + keys: mintInstance.keys, + keysets: mintInstance.keysets, + bip39seed: seed }) + newSeedWallet.keysetId = walletKeys.id self.seedWallets.push(newSeedWallet) log.trace('[WalletStore.getWallet]', 'Returning NEW cashuWallet instance with seed', {mintUrl}) @@ -296,7 +299,7 @@ export const WalletStoreModel = types const wallet: CashuWallet | undefined = self.wallets.find( w => w.mint.mintUrl === mintUrl && - w.keys.id === walletKeys.id + w.keysetId === walletKeys.id ) if (wallet) { @@ -305,10 +308,13 @@ export const WalletStoreModel = types } const newWallet = new CashuWallet(cashuMint, { - keys: walletKeys, - mnemonicOrSeed: undefined + unit, + keys: mintInstance.keys, + keysets: mintInstance.keysets, + bip39seed: undefined }) + newWallet.keysetId = walletKeys.id self.wallets.push(newWallet) log.trace('[WalletStore.getWallet]', 'Returning NEW cashuWallet instance', {mintUrl}) @@ -347,10 +353,9 @@ export const WalletStoreModel = types receive: flow(function* receive( mintUrl: string, unit: MintUnit, - decodedToken: TokenV3, - swapFeeReserve: number, + decodedToken: Token, options: { - preference: AmountPreference[], + outputAmounts: OutputAmounts, counter: number }) { @@ -362,17 +367,16 @@ export const WalletStoreModel = types withSeed: true, }) - const amountToReceive: number = CashuUtils.getTokenAmounts(decodedToken).totalAmount + const amountToReceive: number = CashuUtils.getProofsAmount(decodedToken.proofs) + + // log.trace('[WalletStore.receive]', {decodedToken, keysetId: cashuWallet.keysetId, outputAmounts: options.outputAmounts, counter: options.counter}) const proofs = yield cashuWallet.receive( - decodedToken, - swapFeeReserve, + decodedToken, { - keysetId: cashuWallet.keys.id, - preference: options.preference, + keysetId: cashuWallet.keysetId, + outputAmounts: options.outputAmounts, counter: options.counter, - pubkey: undefined, - privkey: undefined }) const receivedAmount: number = CashuUtils.getProofsAmount(proofs as Proof[]) @@ -388,13 +392,12 @@ export const WalletStoreModel = types } }), send: flow(function* send(mintUrl: string, - amountToSend: number, - swapFeeReserve: number, + amountToSend: number, unit: MintUnit, proofsToSendFrom: Proof[], options: { - preference: AmountPreference[], - counter: number + outputAmounts: OutputAmounts, + counter: number, } ) { try { const cashuWallet = yield self.getWallet( @@ -406,25 +409,23 @@ export const WalletStoreModel = types log.debug('[WalletStore.send] counter', options.counter) - const {returnChange, send} = yield cashuWallet.send( - amountToSend, - swapFeeReserve, + const {keep, send} = yield cashuWallet.swap( + amountToSend, proofsToSendFrom, { - keysetId: cashuWallet.keys.id, - preference: options.preference, + keysetId: cashuWallet.keysetId, + outputAmounts: options.outputAmounts, counter: options.counter, - pubkey: undefined, - privkey: undefined + includeFees: false // fee reserve needs to be already in proofsToSendFrom } ) - log.trace(`[WalletStore.send] ${returnChange.length} returnedProofs`) + log.trace(`[WalletStore.send] ${keep.length} returnedProofs`) log.trace(`[WalletStore.send] ${send.length} proofsToSend`) // do some basic validations that proof amounts from mints match const totalAmountToSendFrom: number = CashuUtils.getProofsAmount(proofsToSendFrom) - const returnedAmount: number = CashuUtils.getProofsAmount(returnChange) + const returnedAmount: number = CashuUtils.getProofsAmount(keep) const sendAmount: number = CashuUtils.getProofsAmount(send) if (sendAmount !== amountToSend) { @@ -437,8 +438,8 @@ export const WalletStoreModel = types const swapFeePaid = totalAmountToSendFrom - amountToSend - returnedAmount return { - returnedProofs: returnChange as ProofV3[], - proofsToSend: send as ProofV3[], + returnedProofs: keep as CashuProof[], + proofsToSend: send as CashuProof[], swapFeePaid } } catch (e: any) { @@ -455,23 +456,20 @@ export const WalletStoreModel = types ) } }), - getSpentOrPendingProofsFromMint: flow(function* getSpentOrPendingProofsFromMint( + getProofsStatesFromMint: flow(function* getProofsStatesFromMint( mintUrl: string, unit: MintUnit, proofs: Proof[] ) { try { - log.trace('[WalletStore.getSpentOrPendingProofsFromMint] start', {mintUrl, unit}) + log.trace('[WalletStore.getProofsStatesFromMint] start', {mintUrl, unit}) const cashuWallet = yield self.getWallet(mintUrl, unit, {withSeed: false}) - const spentPendingProofs = yield cashuWallet.checkProofsSpent(proofs) // seems to work for all units + const proofsByState: {[CheckStateEnum: string]: Proof[]} = yield cashuWallet.checkProofsStates(proofs) - log.trace('[WalletStore.getSpentOrPendingProofsFromMint]', {mintUrl, spentPendingProofs}) + log.trace('[WalletStore.getProofsStatesFromMint]', {mintUrl, proofsByState}) - return spentPendingProofs as { - spent: ProofV3[] - pending: ProofV3[] - } + return proofsByState } catch (e: any) { let message = 'Could not get response from the mint.' @@ -481,7 +479,7 @@ export const WalletStoreModel = types message, { message: e.message, - caller: 'Wallet.getSpentOrPendingProofsFromMint', + caller: 'WalletStore.getProofsStatesFromMint', mintUrl } ) @@ -565,23 +563,22 @@ export const WalletStoreModel = types unit: MintUnit, mintQuote: string, options: { - preference: AmountPreference[], + outputAmounts: OutputAmounts, counter: number } ) { try { const cashuWallet: CashuWallet = yield self.getWallet(mintUrl, unit, {withSeed: true}) // with seed - const {proofs} = yield cashuWallet.mintTokens( + const proofs = yield cashuWallet.mintProofs( amount, mintQuote, { - keysetId: cashuWallet.keys.id, - preference: options.preference, - counter: options.counter, - pubkey: undefined + keysetId: cashuWallet.keysetId, + outputAmounts: options.outputAmounts, + counter: options.counter, } - ) as Promise<{proofs: ProofV3[]}> + ) log.info('[mintProofs]', {proofs}) @@ -637,7 +634,7 @@ export const WalletStoreModel = types mintUrl: string, unit: MintUnit, lightningMeltQuote: MeltQuoteResponse, // invoice is stored by mint by quote - proofsToPayFrom: ProofV3[], // proofAmount >= amount + fee_reserve + proofsToPayFrom: CashuProof[], // proofAmount >= amount + fee_reserve options: { counter: number } @@ -645,12 +642,11 @@ export const WalletStoreModel = types try { const cashuWallet: CashuWallet = yield self.getWallet(mintUrl, unit, {withSeed: true}) // with seed - const meltResponse: MeltTokensResponse = - yield cashuWallet.meltTokens( + const meltResponse: MeltProofsResponse = yield cashuWallet.meltProofs( lightningMeltQuote, proofsToPayFrom, { - keysetId: cashuWallet.keys.id, + keysetId: cashuWallet.keysetId, counter: options.counter } ) @@ -689,7 +685,7 @@ export const WalletStoreModel = types const seedWallet = new CashuWallet(cashuMint, { unit: 'sat', // just use default unit as we restore by keyset - mnemonicOrSeed: seed + bip39seed: seed }) const count = Math.abs(indexTo - indexFrom) diff --git a/src/navigation/TabsNavigator.tsx b/src/navigation/TabsNavigator.tsx index 73976803..4c986824 100644 --- a/src/navigation/TabsNavigator.tsx +++ b/src/navigation/TabsNavigator.tsx @@ -9,9 +9,7 @@ import { Icon, Text } from "../components" import { translate } from "../i18n" import { WalletScreen, - ReceiveOptionsScreen, ReceiveScreen, - SendOptionsScreen, SendScreen, ScanScreen, LightningPayScreen, @@ -129,14 +127,12 @@ export function TabsNavigator() { export type WalletStackParamList = { - Wallet: {scannedMintUrl? : string} - ReceiveOptions: {unit: MintUnit} // to be removed + Wallet: {scannedMintUrl? : string} TokenReceive: { unit: MintUnit mintUrl?: string, } - Receive: {unit: MintUnit, encodedToken? : string} - SendOptions: {unit: MintUnit} + Receive: {unit: MintUnit, encodedToken? : string} Send: { unit: MintUnit, paymentOption?: SendOption, @@ -189,11 +185,9 @@ const WalletNavigator = function WalletNavigator() { headerShown: false, }} > - - + - - + diff --git a/src/screens/ContactDetailScreen.tsx b/src/screens/ContactDetailScreen.tsx index e5ccb1b1..5daa11a6 100644 --- a/src/screens/ContactDetailScreen.tsx +++ b/src/screens/ContactDetailScreen.tsx @@ -12,8 +12,8 @@ import { ContactType } from '../models/Contact' import { NostrClient, log } from '../services' import { getImageSource } from '../utils/utils' import { verticalScale } from '@gocodingnow/rn-size-matters' -import { ReceiveOption } from './ReceiveOptionsScreen' -import { SendOption } from './SendOptionsScreen' +import { ReceiveOption } from './ReceiveScreen' +import { SendOption } from './SendScreen' import { IncomingDataType, IncomingParser } from '../services/incomingParser' import { translate } from '../i18n' diff --git a/src/screens/Contacts/ContactListItem.tsx b/src/screens/Contacts/ContactListItem.tsx index 6a6fe670..9c4e9814 100644 --- a/src/screens/Contacts/ContactListItem.tsx +++ b/src/screens/Contacts/ContactListItem.tsx @@ -1,11 +1,10 @@ import { formatDistance } from 'date-fns' import { observer } from "mobx-react-lite" import React from "react" -import { Image, ImageStyle, ScrollView, TextStyle, View, ViewStyle } from "react-native" -import { Button, Icon, ListItem, Screen, Text } from "../../components" -import { Contact, ContactType } from "../../models/Contact" -import { NostrClient } from "../../services" -import { colors, spacing, typography, useThemeColor } from "../../theme" +import { Image, ImageStyle, TextStyle, View, ViewStyle } from "react-native" +import { ListItem } from "../../components" +import { Contact } from "../../models/Contact" +import { spacing } from "../../theme" import { getImageSource } from '../../utils/utils' diff --git a/src/screens/Contacts/OwnName.tsx b/src/screens/Contacts/OwnName.tsx index 0ce36471..fac8c108 100644 --- a/src/screens/Contacts/OwnName.tsx +++ b/src/screens/Contacts/OwnName.tsx @@ -11,7 +11,7 @@ import { TransactionStatus } from '../../models/Transaction' import { poller, stopPolling } from '../../utils/poller' import { ResultModalInfo } from '../Wallet/ResultModalInfo' import { MINIBITS_NIP05_DOMAIN } from '@env' -import { SendOption } from '../SendOptionsScreen' +import { SendOption } from '../SendScreen' import { translate } from '../../i18n' import { CurrencyAmount } from '../Wallet/CurrencyAmount' import { CurrencyCode } from '../../services/wallet/currency' diff --git a/src/screens/Contacts/PrivateContacts.tsx b/src/screens/Contacts/PrivateContacts.tsx index d5525852..81634fde 100644 --- a/src/screens/Contacts/PrivateContacts.tsx +++ b/src/screens/Contacts/PrivateContacts.tsx @@ -13,8 +13,8 @@ import { log } from '../../services/logService' import { ContactListItem } from './ContactListItem' import { Contact, ContactType } from '../../models/Contact' import { StackNavigationProp } from '@react-navigation/stack' -import { ReceiveOption } from '../ReceiveOptionsScreen' -import { SendOption } from '../SendOptionsScreen' +import { ReceiveOption } from '../ReceiveScreen' +import { SendOption } from '../SendScreen' import { infoMessage, warningMessage } from '../../utils/utils' import { IncomingDataType, IncomingParser } from '../../services/incomingParser' import { translate } from '../../i18n' @@ -422,8 +422,7 @@ const $newContainer: TextStyle = { const $contactInput: TextStyle = { flex: 1, // borderRadius: 0, - // borderBottomRightRadius: spacing.extraSmall, - // borderTopLeftRadius: spacing.extraSmall, + borderRadius: spacing.extraSmall, fontSize: verticalScale(16), padding: spacing.small, alignSelf: 'stretch', @@ -433,6 +432,7 @@ const $contactInput: TextStyle = { const $contactDomain: TextStyle = { marginRight: spacing.small, + marginLeft: -spacing.small, borderTopRightRadius: spacing.extraSmall, borderBottomRightRadius: spacing.extraSmall, padding: spacing.extraSmall, diff --git a/src/screens/Contacts/PublicContacts.tsx b/src/screens/Contacts/PublicContacts.tsx index b59d13f5..e898b7f6 100644 --- a/src/screens/Contacts/PublicContacts.tsx +++ b/src/screens/Contacts/PublicContacts.tsx @@ -13,8 +13,8 @@ import { log } from '../../services/logService' import { Contact, ContactType } from '../../models/Contact' import { StackNavigationProp } from '@react-navigation/stack' import { ContactsStackParamList } from '../../navigation' -import { SendOption } from '../SendOptionsScreen' -import { ReceiveOption } from '../ReceiveOptionsScreen' +import { SendOption } from '../SendScreen' +import { ReceiveOption } from '../ReceiveScreen' import { useSafeAreaInsets } from 'react-native-safe-area-context' import { IncomingDataType, IncomingParser } from '../../services/incomingParser' import { translate } from '../../i18n' diff --git a/src/screens/ContactsScreen.tsx b/src/screens/ContactsScreen.tsx index eae145a3..cdb5aef4 100644 --- a/src/screens/ContactsScreen.tsx +++ b/src/screens/ContactsScreen.tsx @@ -10,8 +10,8 @@ import { PrivateContacts } from './Contacts/PrivateContacts' import { PublicContacts } from './Contacts/PublicContacts' import { log } from '../services/logService' import { getImageSource } from '../utils/utils' -import { ReceiveOption } from './ReceiveOptionsScreen' -import { SendOption } from './SendOptionsScreen' +import { ReceiveOption } from './ReceiveScreen' +import { SendOption } from './SendScreen' import { verticalScale } from '@gocodingnow/rn-size-matters' diff --git a/src/screens/ExportBackupScreen.tsx b/src/screens/ExportBackupScreen.tsx index a8872c9b..f8769e44 100644 --- a/src/screens/ExportBackupScreen.tsx +++ b/src/screens/ExportBackupScreen.tsx @@ -28,7 +28,7 @@ import AppError from '../utils/AppError' import { Proof } from '../models/Proof' import { useStores } from '../models' import EventEmitter from '../utils/eventEmitter' -import { CashuUtils, ProofV3, TokenV3 } from '../services/cashu/cashuUtils' +import { CashuProof, CashuUtils } from '../services/cashu/cashuUtils' import Clipboard from '@react-native-clipboard/clipboard' import { translate } from '../i18n' import { ProofsStoreSnapshot } from '../models/ProofsStore' @@ -39,6 +39,7 @@ import { Database, TransactionTaskResult, WalletTask, WalletTaskResult } from '. import { Transaction, TransactionStatus } from '../models/Transaction' import { ResultModalInfo } from './Wallet/ResultModalInfo' import { verticalScale } from '@gocodingnow/rn-size-matters' +import { Token, getDecodedToken, getEncodedToken } from '@cashu/cashu-ts' interface ExportBackupScreenProps extends SettingsStackScreenProps<'ExportBackup'> {} @@ -117,9 +118,9 @@ export const ExportBackupScreen: FC = // now we batch receive the pending encoded token // this forces the proofs swap with the mint for standard denomination amounts const encodedTokenToReceive: string = result.encodedTokenToSend - const tokenToReceive = CashuUtils.decodeToken(encodedTokenToReceive) - const {totalAmount: tokenAmount} = CashuUtils.getTokenAmounts(tokenToReceive) - const proofsCount = tokenToReceive.token[0].proofs.length + const tokenToReceive = getDecodedToken(encodedTokenToReceive) + const tokenAmount = CashuUtils.getProofsAmount(tokenToReceive.proofs) + const proofsCount = tokenToReceive.proofs.length setTotalSentProofsCount(prev => prev + proofsCount) setIsReceiveBatchSentToQueue(true) @@ -366,11 +367,11 @@ export const ExportBackupScreen: FC = for (const keysetId in groupedByKeyset) { const proofsByKeysetId = groupedByKeyset[keysetId] - const proofsToExport: ProofV3[] = [] + const proofsToExport: CashuProof[] = [] for (const p of proofsByKeysetId) { // clean private params - const proofToExport: ProofV3 = { + const proofToExport: CashuProof = { id: p.id, amount: p.amount, secret: p.secret, @@ -380,19 +381,15 @@ export const ExportBackupScreen: FC = proofsToExport.push(proofToExport) } - const tokenByKeysetId: TokenV3 = { - token: [ - { - mint, - proofs: proofsToExport - } - ], + const tokenByKeysetId: Token = { + mint, + proofs: proofsToExport, unit: proofsByKeysetId[0].unit } log.trace('[copyEncodedTokens]', {tokenByKeysetId}) - const encodedByMint = CashuUtils.encodeToken(tokenByKeysetId) + const encodedByMint = getEncodedToken(tokenByKeysetId) encodedTokens.push(encodedByMint) } } @@ -501,7 +498,7 @@ export const ExportBackupScreen: FC = continue } - const amount = CashuUtils.getProofsAmount(proofsToImport) + const amount = sumProofs(proofsToImport) const unit = proofsByKeysetId[0].unit log.trace('[doLocalRecovery] to be recovered', {mint, keysetId, unit, amount}) diff --git a/src/screens/ImportBackupScreen.tsx b/src/screens/ImportBackupScreen.tsx index 96cc0ae4..04559047 100644 --- a/src/screens/ImportBackupScreen.tsx +++ b/src/screens/ImportBackupScreen.tsx @@ -25,9 +25,9 @@ import { Database, KeyChain, log, MinibitsClient, NostrClient } from '../service import Clipboard from '@react-native-clipboard/clipboard' import { rootStoreInstance, useStores } from '../models' import {MnemonicInput} from './Recovery/MnemonicInput' -import { TransactionData, TransactionStatus } from '../models/Transaction' +import { TransactionStatus } from '../models/Transaction' import { ResultModalInfo } from './Wallet/ResultModalInfo' -import { deriveSeedFromMnemonic } from '@cashu/cashu-ts' +import {deriveSeedFromMnemonic} from '@cashu/crypto/modules/client/NUT09' import { MINIBITS_MINT_URL, MINIBITS_NIP05_DOMAIN } from '@env' import { delay } from '../utils/utils' import { applySnapshot} from 'mobx-state-tree' diff --git a/src/screens/LightningPayScreen.tsx b/src/screens/LightningPayScreen.tsx index 7db01c3f..e336d54b 100644 --- a/src/screens/LightningPayScreen.tsx +++ b/src/screens/LightningPayScreen.tsx @@ -10,13 +10,12 @@ import {colors, spacing, useThemeColor} from '../theme' import {log} from '../services/logService' import { IncomingDataType, IncomingParser } from '../services/incomingParser' import AppError, { Err } from '../utils/AppError' -import { Button, Card, ErrorModal, Header, Icon, InfoModal, ListItem, ScanIcon, Screen, Text } from '../components' -import { LnurlUtils } from '../services/lnurl/lnurlUtils' +import { Button, Card, ErrorModal, Icon, InfoModal, ListItem, ScanIcon, Screen, Text } from '../components' import { infoMessage } from '../utils/utils' import Clipboard from '@react-native-clipboard/clipboard' import { SvgXml } from 'react-native-svg' -import { SendOption } from './SendOptionsScreen' -import { CurrencyCode, MintUnit } from '../services/wallet/currency' +import { SendOption } from './SendScreen' +import { MintUnit } from '../services/wallet/currency' import { useStores } from '../models' import { Mint } from '../models/Mint' import { MintHeader } from './Mints/MintHeader' diff --git a/src/screens/MintInfoScreen.tsx b/src/screens/MintInfoScreen.tsx index 5554538e..223fc2a8 100644 --- a/src/screens/MintInfoScreen.tsx +++ b/src/screens/MintInfoScreen.tsx @@ -1,5 +1,5 @@ import React, { FC, useEffect, useMemo, useState } from 'react' -import { GetInfoResponse, NUT15Entry, NUT17Entry, SwapMethod } from '@cashu/cashu-ts' +import { GetInfoResponse, SwapMethod } from '@cashu/cashu-ts' import { isObj } from '@cashu/cashu-ts/src/utils' import Clipboard from '@react-native-clipboard/clipboard' import { observer } from 'mobx-react-lite' @@ -21,7 +21,6 @@ import { Screen, Text, } from '../components' -import { AvatarHeader } from '../components/AvatarHeader' import { CollapsibleText } from '../components/CollapsibleText' import { translate } from '../i18n' import { useStores } from '../models' @@ -387,11 +386,11 @@ function NutsCard(props: {info: GetInfoResponse}) { // detailed nuts are separated from simple ones if we want to show more info abt them in the future for (const [nut, info] of Object.entries(props.info.nuts)) { - if (nut === '15' && (info as NUT15Entry[]).length > 0) { + if (nut === '15' && info) { // see https://github.com/cashubtc/nuts/blob/main/15.md - multipath payments // in the future, it might be nice to show for which currencies are multipath payments supported // for example by extending NutItem - nutsSimple.push(['15', (info as NUT15Entry[])[0]?.mpp ?? false]) + nutsSimple.push(['15', (info)[0]?.mpp ?? false]) continue; } // bug: nutshell 0.15.3 returns NUT17Entry[], instead of { supported: NUT17Entry[] } diff --git a/src/screens/MintsScreen.tsx b/src/screens/MintsScreen.tsx index bdc3e2df..d5f495b9 100644 --- a/src/screens/MintsScreen.tsx +++ b/src/screens/MintsScreen.tsx @@ -5,7 +5,7 @@ import Clipboard from '@react-native-clipboard/clipboard' import { MINIBITS_MINT_URL } from '@env' -import {spacing, typography, useThemeColor} from '../theme' +import {colors, spacing, typography, useThemeColor} from '../theme' import {SettingsStackScreenProps} from '../navigation' import { Button, @@ -279,6 +279,7 @@ export const MintsScreen: FC> = observer( const iconColor = useThemeColor('textDim') const inputBg = useThemeColor('background') const headerTitle = useThemeColor('headerTitle') + const buttonBorder = useThemeColor('card') return ( @@ -432,7 +433,9 @@ export const MintsScreen: FC> = observer( tx='common.paste' style={{ borderRadius: 0, - marginLeft: 1, + marginLeft: -spacing.small, + borderLeftWidth: 1, + borderLeftColor: buttonBorder }} onPress={pasteMintUrl} /> @@ -532,32 +535,13 @@ const $card: ViewStyle = { // paddingTop: 0, } -const $cardHeading: TextStyle = { - fontFamily: typography.primary?.medium, - fontSize: 20, -} - -const $cardContent: TextStyle = { - fontSize: 14, -} - -const $bottomContainer: ViewStyle = { - flex: 1, - justifyContent: 'flex-end', - marginBottom: spacing.large, - alignSelf: 'stretch', -} - const $bottomModal: ViewStyle = { alignItems: 'center', } - const $mintInput: TextStyle = { flex: 1, - borderTopLeftRadius: spacing.extraSmall, - borderBottomLeftRadius: spacing.extraSmall, - // fontSize: 16, + borderRadius: spacing.extraSmall, padding: spacing.extraSmall, alignSelf: 'stretch', // textAlignVertical: 'top', diff --git a/src/screens/MnemonicScreen.tsx b/src/screens/MnemonicScreen.tsx index 64c1ffed..86f38e81 100644 --- a/src/screens/MnemonicScreen.tsx +++ b/src/screens/MnemonicScreen.tsx @@ -107,7 +107,7 @@ export const MnemonicScreen: FC> = observer const headerTitle = useThemeColor('headerTitle') return ( - + diff --git a/src/screens/NwcScreen.tsx b/src/screens/NwcScreen.tsx index 282f1096..e012ca9c 100644 --- a/src/screens/NwcScreen.tsx +++ b/src/screens/NwcScreen.tsx @@ -1,6 +1,5 @@ import {observer} from 'mobx-react-lite' import { Observer } from 'mobx-react-lite' -import Clipboard from '@react-native-clipboard/clipboard' import React, {FC, useEffect, useRef, useState} from 'react' import {FlatList, TextInput, TextStyle, View, ViewStyle} from 'react-native' import {colors, spacing, useThemeColor} from '../theme' @@ -14,7 +13,6 @@ import { verticalScale } from '@gocodingnow/rn-size-matters' import { NwcConnection } from '../models/NwcStore' import { QRCodeBlock } from './Wallet/QRCode' import { CollapsibleText } from '../components/CollapsibleText' -import { isSameDay } from 'date-fns/isSameDay' interface NwcScreenProps extends SettingsStackScreenProps<'Nwc'> {} diff --git a/src/screens/OwnKeysScreen.tsx b/src/screens/OwnKeysScreen.tsx index ecf9ebf6..443f7f43 100644 --- a/src/screens/OwnKeysScreen.tsx +++ b/src/screens/OwnKeysScreen.tsx @@ -2,6 +2,7 @@ import {observer} from 'mobx-react-lite' import React, {FC, useRef, useState} from 'react' import {Image, LayoutAnimation, Platform, TextInput, TextStyle, UIManager, View, ViewStyle} from 'react-native' import {getPublicKey} from 'nostr-tools' +import { hexToBytes } from '@noble/hashes/utils' import {colors, spacing, typography, useThemeColor} from '../theme' import {ContactsStackScreenProps} from '../navigation' import {Icon, ListItem, Screen, Text, Card, BottomModal, Button, InfoModal, ErrorModal, Loading} from '../components' @@ -169,7 +170,7 @@ export const OwnKeysScreen: FC = observer(function OwnKeysSc } // validate that nsec matches profile pubkey const privateKey = NostrClient.getHexkey(ownNsec) - const publicKey = getPublicKey(privateKey) + const publicKey = getPublicKey(hexToBytes(privateKey)) if(publicKey !== ownProfile.pubkey) { throw new AppError(Err.VALIDATION_ERROR, translate("nsecPrivatePublicKeyMismatchError"), {publicKey}) @@ -397,7 +398,7 @@ export const OwnKeysScreen: FC = observer(function OwnKeysSc isVisible={isProfileChangeCompleted ? true : false} ContentComponent={ - + > = observer( - function ReceiveOptionsScreen({route, navigation}) { - useHeader({ - leftIcon: 'faArrowLeft', - onLeftPress: () => navigation.goBack(), - }) - - - const isInternetReachable = useIsInternetReachable() - - const [info, setInfo] = useState('') - const [error, setError] = useState() - - const gotoContacts = function () { - navigation.navigate('ContactsNavigator', { - screen: 'Contacts', - params: { - paymentOption: ReceiveOption.SEND_PAYMENT_REQUEST - } - }) - } - - - const gotoTopup = function () { - navigation.navigate('Topup', { paymentOption: ReceiveOption.SHOW_INVOICE }) - } - - - const onScan = async function () { - navigation.navigate('Scan') - } - - - const handleError = function (e: AppError): void { - log.error(e.name, e.message) - setError(e) - } - - const headerBg = useThemeColor('header') - const iconColor = useThemeColor('textDim') - - return ( - - - - - - - - - - - - } - // leftIcon='faQrcode' - // leftIconColor={colors.palette.success200} - // leftIconInverse={true} - style={$item} - topSeparator={true} - onPress={onScan} - /> - - } - /> - - {info && } - {error && } - - ) - }, -) - -const $screen: ViewStyle = { - flex: 1, -} - -const $headerContainer: TextStyle = { - alignItems: 'center', - padding: spacing.medium, - height: spacing.screenHeight * 0.20, -} - -const $contentContainer: TextStyle = { - // flex: 1, - padding: spacing.extraSmall, - // alignItems: 'center', -} - -const $optionsCard: ViewStyle = { - marginTop: -spacing.extraLarge * 2, - marginBottom: spacing.small, - //paddingTop: 0, -} - - -const $item: ViewStyle = { - paddingHorizontal: spacing.small, - paddingLeft: 0, -} - - diff --git a/src/screens/ReceiveScreen.tsx b/src/screens/ReceiveScreen.tsx index 48365c70..e227e480 100644 --- a/src/screens/ReceiveScreen.tsx +++ b/src/screens/ReceiveScreen.tsx @@ -24,24 +24,33 @@ import {log} from '../services/logService' import AppError, { Err } from '../utils/AppError' import EventEmitter from '../utils/eventEmitter' -import {CashuUtils, TokenV3} from '../services/cashu/cashuUtils' +import {CashuUtils, CashuProof} from '../services/cashu/cashuUtils' import {ResultModalInfo} from './Wallet/ResultModalInfo' import {MintListItem} from './Mints/MintListItem' import useIsInternetReachable from '../utils/useIsInternetReachable' import { verticalScale } from '@gocodingnow/rn-size-matters' -import { CurrencyCode, MintUnit, formatCurrency, getCurrency } from "../services/wallet/currency" +import { CurrencyCode, MintUnit, getCurrency } from "../services/wallet/currency" import { MintHeader } from './Mints/MintHeader' import { round, toNumber } from '../utils/number' import numbro from 'numbro' import { TranItem } from './TranDetailScreen' import { translate } from '../i18n' +import { Token, getDecodedToken } from '@cashu/cashu-ts' + +export enum ReceiveOption { + // CREATE_AND_SEND_PAYMENT_REQUEST = 'CREATE_AND_SEND_PAYMENT_REQUEST', + SEND_PAYMENT_REQUEST = 'SEND_PAYMENT_REQUEST', + PASTE_OR_SCAN_TOKEN = 'PASTE_OR_SCAN_TOKEN', + SHOW_INVOICE = 'SHOW_INVOICE', + LNURL_WITHDRAW = 'LNURL_WITHDRAW' +} export const ReceiveScreen: FC> = observer( function ReceiveScreen({route, navigation}) { const isInternetReachable = useIsInternetReachable() const {mintsStore, walletStore} = useStores() - const [token, setToken] = useState() + const [token, setToken] = useState() const [encodedToken, setEncodedToken] = useState() const [amountToReceive, setAmountToReceive] = useState('0') const [unit, setUnit] = useState('sat') @@ -165,11 +174,11 @@ export const ReceiveScreen: FC> = observer( try { navigation.setParams({encodedToken: undefined}) - const decoded = CashuUtils.decodeToken(encoded) - const tokenAmounts = CashuUtils.getTokenAmounts(decoded) + const decoded = getDecodedToken(encoded) + const tokenAmount = CashuUtils.getProofsAmount(decoded.proofs) log.trace('decoded token', {decoded}) - log.trace('tokenAmounts', {tokenAmounts}) + log.trace('tokenAmount', {tokenAmount}) if(!decoded.unit) { setInfo(translate("decodedMissingCurrencyUnit", { unit: CurrencyCode.SAT })) @@ -179,7 +188,7 @@ export const ReceiveScreen: FC> = observer( const currency = getCurrency(decoded.unit as MintUnit) setToken(decoded) - setAmountToReceive(numbro(tokenAmounts.totalAmount / currency.precision).format({thousandSeparated: true, mantissa: currency.mantissa})) + setAmountToReceive(numbro(tokenAmount / currency.precision).format({thousandSeparated: true, mantissa: currency.mantissa})) setUnit(decoded.unit as MintUnit) if (decoded.memo && decoded.memo.length > 0) { @@ -197,12 +206,12 @@ export const ReceiveScreen: FC> = observer( setIsReceiveTaskSentToQueue(true) const amountToReceiveInt = round(toNumber(amountToReceive) * getCurrency(unit).precision, 0) - const proofsCount = token!.token.flatMap(entry => entry.proofs).length + const proofsCount = token!.proofs.length if(proofsCount > MAX_SWAP_INPUT_SIZE) { WalletTask.receiveBatch( - token as TokenV3, + token as Token, amountToReceiveInt, memo, encodedToken as string, @@ -211,7 +220,7 @@ export const ReceiveScreen: FC> = observer( } else { WalletTask.receive( - token as TokenV3, + token as Token, amountToReceiveInt, memo, encodedToken as string, @@ -234,7 +243,7 @@ export const ReceiveScreen: FC> = observer( ) const mintInstance = mintsStore.findByUrl(mint) - const counter = mintInstance!.getProofsCounterByKeysetId!(walletInstance.keys.id) + const counter = mintInstance!.getProofsCounterByKeysetId!(walletInstance.keysetId) counter!.increaseProofsCounter(20) // retry receive @@ -254,7 +263,7 @@ export const ReceiveScreen: FC> = observer( const amountToReceiveInt = round(toNumber(amountToReceive) * getCurrency(unit).precision, 0) WalletTask.receiveOfflinePrepare( - token as TokenV3, + token as Token, amountToReceiveInt, memo, encodedToken as string, diff --git a/src/screens/ScanScreen.tsx b/src/screens/ScanScreen.tsx index 8292a075..18af2737 100644 --- a/src/screens/ScanScreen.tsx +++ b/src/screens/ScanScreen.tsx @@ -6,17 +6,15 @@ import { ViewStyle, View, TextStyle, - TextInput, } from 'react-native' import {WalletStackScreenProps} from '../navigation' import {Camera, CameraType} from 'react-native-camera-kit' import { URDecoder } from '@gandlaf21/bc-ur' import {spacing, typography, useThemeColor} from '../theme' -import {useHeader} from '../utils/useHeader' import {log} from '../services/logService' import { IncomingDataType, IncomingParser } from '../services/incomingParser' import AppError, { Err } from '../utils/AppError' -import { BottomModal, Button, ErrorModal, Header, Icon, Screen, Text } from '../components' +import { Button, ErrorModal, Header, Icon, Screen } from '../components' import { LnurlUtils } from '../services/lnurl/lnurlUtils' import { infoMessage } from '../utils/utils' import Clipboard from '@react-native-clipboard/clipboard' diff --git a/src/screens/SeedRecoveryScreen.tsx b/src/screens/SeedRecoveryScreen.tsx index dca278c6..32d4ac03 100644 --- a/src/screens/SeedRecoveryScreen.tsx +++ b/src/screens/SeedRecoveryScreen.tsx @@ -31,7 +31,7 @@ import { CashuUtils } from '../services/cashu/cashuUtils' import { Proof } from '../models/Proof' import { Transaction, TransactionData, TransactionRecord, TransactionStatus, TransactionType } from '../models/Transaction' import { ResultModalInfo } from './Wallet/ResultModalInfo' -import { deriveSeedFromMnemonic } from '@cashu/cashu-ts' +import {deriveSeedFromMnemonic} from '@cashu/crypto/modules/client/NUT09' import { MINIBITS_MINT_URL, MINIBITS_NIP05_DOMAIN } from '@env' import { delay } from '../utils/utils' import { getSnapshot, isStateTreeNode } from 'mobx-state-tree' @@ -39,7 +39,7 @@ import { scale } from '@gocodingnow/rn-size-matters' import { WalletUtils } from '../services/wallet/utils' import { WalletScreen } from './WalletScreen' import { MintUnit, formatCurrency, getCurrency } from '../services/wallet/currency' -import { isObj } from '@cashu/cashu-ts/src/utils' +import { isObj, sumProofs } from '@cashu/cashu-ts/src/utils' import { WalletProfileRecord } from '../models/WalletProfileStore' import { translate } from '../i18n' @@ -271,7 +271,7 @@ export const SeedRecoveryScreen: FC> = obser setStatusMessage(translate("recovery.spentProofsAmount", { amount: spent.length })) - const spentAmount = CashuUtils.getProofsAmount(spent as Proof[]) + const spentAmount = sumProofs(spent as Proof[]) alreadySpentAmount += spentAmount const unspent = proofs.filter((proof: Proof) => !spent.includes(proof)) diff --git a/src/screens/SendOptionsScreen.tsx b/src/screens/SendOptionsScreen.tsx deleted file mode 100644 index cbd9882b..00000000 --- a/src/screens/SendOptionsScreen.tsx +++ /dev/null @@ -1,153 +0,0 @@ -import {observer} from 'mobx-react-lite' -import React, {FC, useState, useCallback, useEffect} from 'react' -import {Alert, TextStyle, View, ViewStyle} from 'react-native' -import {spacing, useThemeColor, colors} from '../theme' -import {WalletStackScreenProps} from '../navigation' -import { - Button, - Icon, - Card, - Screen, - ListItem, - Text, -} from '../components' -import {useHeader} from '../utils/useHeader' -import {log} from '../services/logService' -import AppError from '../utils/AppError' - -export enum SendOption { - SEND_TOKEN = 'SEND_TOKEN', - PASTE_OR_SCAN_INVOICE = 'PASTE_OR_SCAN_INVOICE', - SHOW_TOKEN = 'SHOW_TOKEN', - PAY_PAYMENT_REQUEST = 'PAY_PAYMENT_REQUEST', - LNURL_PAY = 'LNURL_PAY', - LNURL_ADDRESS = 'LNURL_ADDRESS', - DONATION = 'DONATION', -} - -export const SendOptionsScreen: FC> = observer( - function SendOptionsScreen({route, navigation}) { - useHeader({ - leftIcon: 'faArrowLeft', - onLeftPress: () => navigation.goBack(), - }) - - const [error, setError] = useState() - - const gotoContacts = function () { - navigation.navigate('ContactsNavigator', { - screen: 'Contacts', - params: {paymentOption: SendOption.SEND_TOKEN}}) - } - - - const gotoSend = function () { - navigation.navigate('Send', { - paymentOption: SendOption.SHOW_TOKEN} - ) - } - - - const gotoLightningPay = async function () { - navigation.navigate('LightningPay') - } - - - const handleError = function (e: AppError): void { - log.error(e.name, e.message) - setError(e) - } - - const headerBg = useThemeColor('header') - const subtitleColor = useThemeColor('textDim') - - return ( - - - - - - - - - - } - /> - - - - } - - /> - - - ) - }, -) - -const $screen: ViewStyle = { - flex: 1, -} - -const $headerContainer: TextStyle = { - alignItems: 'center', - padding: spacing.medium, - height: spacing.screenHeight * 0.20, -} - -const $contentContainer: TextStyle = { - // flex: 1, - padding: spacing.extraSmall, - // alignItems: 'center', -} - -const $ecashCard: ViewStyle = { - marginTop: -spacing.extraLarge * 2, - marginBottom: spacing.small, - // paddingTop: 0, -} - -const $lightningCard: ViewStyle = { - marginVertical: spacing.small, - // paddingTop: 0, - } - -const $item: ViewStyle = { - paddingHorizontal: spacing.small, - paddingLeft: 0, -} diff --git a/src/screens/SendScreen.tsx b/src/screens/SendScreen.tsx index 3e0ae5d0..99ac0c0d 100644 --- a/src/screens/SendScreen.tsx +++ b/src/screens/SendScreen.tsx @@ -53,7 +53,6 @@ import useIsInternetReachable from '../utils/useIsInternetReachable' import { Proof } from '../models/Proof' import { Contact, ContactType } from '../models/Contact' import { getImageSource, infoMessage } from '../utils/utils' -import { SendOption } from './SendOptionsScreen' import { verticalScale } from '@gocodingnow/rn-size-matters' import { MintUnit, formatCurrency, getCurrency } from "../services/wallet/currency" import { MintHeader } from './Mints/MintHeader' @@ -64,6 +63,15 @@ import numbro from 'numbro' import { TranItem } from './TranDetailScreen' import { MemoInputCard } from '../components/MemoInputCard' +export enum SendOption { + SEND_TOKEN = 'SEND_TOKEN', + PASTE_OR_SCAN_INVOICE = 'PASTE_OR_SCAN_INVOICE', + SHOW_TOKEN = 'SHOW_TOKEN', + PAY_PAYMENT_REQUEST = 'PAY_PAYMENT_REQUEST', + LNURL_PAY = 'LNURL_PAY', + LNURL_ADDRESS = 'LNURL_ADDRESS', + DONATION = 'DONATION', +} if (Platform.OS === 'android' && UIManager.setLayoutAnimationEnabledExperimental) { @@ -449,8 +457,8 @@ export const SendScreen: FC> = observer( {withSeed: true} ) const mintInstance = mintsStore.findByUrl(mintBalanceToSendFrom?.mintUrl as string) - const counter = mintInstance!.getProofsCounterByKeysetId!(walletInstance.keys.id) - counter!.increaseProofsCounter(50) + const counter = mintInstance!.getProofsCounterByKeysetId!(walletInstance.keysetId) + counter!.increaseProofsCounter(20) // retry send onMintBalanceConfirm() @@ -698,7 +706,7 @@ export const SendScreen: FC> = observer( !previousState) - const increaseProofsCounter = async function (tokenToRetry: TokenV3) { + const increaseProofsCounter = async function (tokenToRetry: Token) { const {mint} = transaction const walletInstance = await walletStore.getWallet( mint, @@ -612,7 +613,7 @@ const ReceiveInfoBlock = function (props: { ) const mintInstance = mintsStore.findByUrl(mint) - const counter = mintInstance!.getProofsCounterByKeysetId!(walletInstance.keys.id) + const counter = mintInstance!.getProofsCounterByKeysetId!(walletInstance.keysetId) counter!.increaseProofsCounter(20) } @@ -624,8 +625,8 @@ const ReceiveInfoBlock = function (props: { setIsLoading(true) try { - const tokenToRetry: TokenV3 = CashuUtils.decodeToken(transaction.inputToken) - const amountToReceive = CashuUtils.getTokenAmounts(tokenToRetry).totalAmount + const tokenToRetry = getDecodedToken(transaction.inputToken) + const amountToReceive = CashuUtils.getProofsAmount(tokenToRetry.proofs) const memo = tokenToRetry.memo || '' if(isCounterIncreaseNeeded) { diff --git a/src/screens/Transactions/TransactionListItem.tsx b/src/screens/Transactions/TransactionListItem.tsx index 50227143..89e000b4 100644 --- a/src/screens/Transactions/TransactionListItem.tsx +++ b/src/screens/Transactions/TransactionListItem.tsx @@ -210,7 +210,7 @@ export const TransactionListItem = observer(function ( amountStyle={{color: txReceiveColor}} /> )} - {[TransactionStatus.ERROR, TransactionStatus.BLOCKED, TransactionStatus.PREPARED_OFFLINE].includes(tx.status) && ( + {[TransactionStatus.ERROR, TransactionStatus.BLOCKED, TransactionStatus.PREPARED_OFFLINE, TransactionStatus.EXPIRED].includes(tx.status) && ( () - const [encodedV4Token, setEncodedV4Token] = useState() - const [decodedV3Token, setDecodedV3Token] = useState() + const [qrError, setQrError] = useState() + const [encodedV3Token, setEncodedV3Token] = useState() + const [decodedToken, setDecodedToken] = useState() const [keysetFormat, setKeysetFormat] = useState<'hex' | 'base64' | undefined>() const [isLoadingQRCode, setIsLoadingQRCode] = useState(false) const [isQRCodeError, setIsQRCodeError] = useState(false) @@ -37,20 +38,20 @@ export const QRCodeBlock = function (props: { useEffect(() => { const detectKeysetFormat = () => { - if(type === 'EncodedV3Token') { - const decoded = CashuUtils.decodeToken(qrCodeData) as TokenV3 - setDecodedV3Token(decoded) + if(type === 'EncodedV4Token') { + const decoded = getDecodedToken(qrCodeData) + setDecodedToken(decoded) - if(decoded.token[0].proofs[0].id.startsWith('00')) { - setKeysetFormat('hex') + if(decoded.proofs[0].id.startsWith('00')) { + setKeysetFormat('hex') + } else { try { - // make v4 default one - const encodedV4 = CashuUtils.encodeToken(decoded, 4) - setEncodedV4Token(encodedV4) + // make v3 legacy format + const encodedV3 = getEncodedToken(decoded, {version: 3}) + setEncodedV3Token(encodedV3) } catch (e: any) { handleQrError(e) } - } else { setKeysetFormat('base64') } } @@ -122,19 +123,19 @@ export const QRCodeBlock = function (props: { const switchTokenEncoding = function () { try { - if(encodedV4Token) { - setEncodedV4Token(undefined) - } else if(type === 'EncodedV3Token') { - log.trace('[v3]', qrCodeData) + if(encodedV3Token) { + setEncodedV3Token(undefined) + } else if(type === 'EncodedV4Token') { + log.trace('[v4]', qrCodeData) - if(!decodedV3Token) { + if(!decodedToken) { return false } - const encodedV4 = CashuUtils.encodeToken(decodedV3Token, 4) + const encodedV3 = getEncodedToken(decodedToken, {version: 3}) - log.trace('[v4]', encodedV4) - setEncodedV4Token(encodedV4) + log.trace('[v3]', encodedV3) + setEncodedV3Token(encodedV3) } } catch (e: any) { handleQrError(e) @@ -163,7 +164,7 @@ export const QRCodeBlock = function (props: { const onCopy = function () { try { - Clipboard.setString(encodedV4Token || qrCodeData as string) + Clipboard.setString(encodedV3Token || qrCodeData as string) } catch (e: any) { handleQrError(e) } @@ -205,7 +206,7 @@ export const QRCodeBlock = function (props: { /> ) : ( )} @@ -240,10 +241,10 @@ export const QRCodeBlock = function (props: { paddingVertical: verticalScale(spacing.tiny) }} /> - {type === 'EncodedV3Token' && keysetFormat === 'hex' && !isAnimating && ( + {type === 'EncodedV4Token' && keysetFormat === 'hex' && !isAnimating && (