diff --git a/babel.config.js b/babel.config.js index 518e7381..2d82d11e 100644 --- a/babel.config.js +++ b/babel.config.js @@ -12,6 +12,7 @@ module.exports = { }, ], 'module:react-native-dotenv', - 'react-native-reanimated/plugin' + 'react-native-reanimated/plugin', + '@babel/plugin-proposal-export-namespace-from' ], }; diff --git a/metro.config.js b/metro.config.js index 9d41685e..7f358075 100644 --- a/metro.config.js +++ b/metro.config.js @@ -6,6 +6,7 @@ const {getDefaultConfig, mergeConfig} = require('@react-native/metro-config'); * * @type {import('metro-config').MetroConfig} */ -const config = {}; +const config = { +}; module.exports = mergeConfig(getDefaultConfig(__dirname), config); diff --git a/package.json b/package.json index 1a071630..06936e73 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "minibits_wallet", - "version": "0.1.10-beta.16", + "version": "0.1.10-beta.17", "private": true, "scripts": { "android:clean": "cd android && ./gradlew clean", @@ -18,7 +18,7 @@ "adb:inspect": "adb shell input keyevent 82", "postinstall": "patch-package --exclude 'nothing'" }, - "dependencies": { + "dependencies": { "@cashu/cashu-ts": "minibits-cash/cashu-ts#2b51a7ba7a8461a0040359eecda37b98b603692c", "@fortawesome/fontawesome-svg-core": "^6.6.0", "@fortawesome/free-brands-svg-icons": "^6.6.0", @@ -47,7 +47,7 @@ "mobx": "^6.13.2", "mobx-react-lite": "^4.0.7", "mobx-state-tree": "^6.0.1", - "nostr-tools": "2.9.4", + "nostr-tools": "minibits-cash/nostr-tools#2991f59d0150a0cb2c71e2f9a4cb6c8aca3b19d7", "numbro": "^2.5.0", "patch-package": "^8.0.0", "postinstall-postinstall": "^2.1.0", @@ -80,8 +80,9 @@ "text-encoding-polyfill": "^0.6.7", "util": "^0.12.5" }, - "devDependencies": { + "devDependencies": { "@babel/core": "^7.20.0", + "@babel/plugin-proposal-export-namespace-from": "^7.18.9", "@babel/preset-env": "^7.20.0", "@babel/runtime": "^7.20.0", "@react-native/babel-preset": "0.75.3", diff --git a/src/models/NwcStore.ts b/src/models/NwcStore.ts index 518aa97c..02104192 100644 --- a/src/models/NwcStore.ts +++ b/src/models/NwcStore.ts @@ -7,7 +7,7 @@ import { isStateTreeNode, detach, } from 'mobx-state-tree' -import { kinds as NostrKinds } from 'nostr-tools' +import { kinds } from 'nostr-tools' import {withSetPropAction} from './helpers/withSetPropAction' import {log} from '../services/logService' import EventEmitter from '../utils/eventEmitter' @@ -162,7 +162,7 @@ export const NwcConnectionModel = types.model('NwcConnection', { const responseEvent: NostrUnsignedEvent = { pubkey: self.walletPubkey, - kind: NostrKinds.NWCWalletResponse, + kind: kinds.NWCWalletResponse, tags: [["p", requestEvent.pubkey], ["e", requestEvent.id]], content: encryptedContent, created_at: Math.floor(Date.now() / 1000) @@ -673,7 +673,7 @@ export const NwcStoreModel = types self.nwcConnections.push(newConnection) const filter = { - kinds: [NostrKinds.NWCWalletInfo], + kinds: [kinds.NWCWalletInfo], authors: [newConnection.walletPubkey], } @@ -683,7 +683,7 @@ export const NwcStoreModel = types if(!existingInfoEvent) { // publish info replacable event // seems to be a relict replaced by get_info request? const infoEvent: NostrUnsignedEvent = { - kind: NostrKinds.NWCWalletInfo, + kind: kinds.NWCWalletInfo, pubkey: newConnection.walletPubkey, tags: [], content: self.supportedMethods.join(' '), @@ -730,7 +730,7 @@ export const NwcStoreModel = types let eventsBatch: NostrEvent[] = [] const filter = [{ - kinds: [NostrKinds.NWCWalletRequest], + kinds: [kinds.NWCWalletRequest], authors: connectionsPubkeys, "#p": [self.walletPubkey], since @@ -740,7 +740,7 @@ export const NwcStoreModel = types const sub = pool.subscribeMany(self.connectionRelays , filter, { onevent(event) { - if (event.kind != NostrKinds.NWCWalletRequest) { + if (event.kind != kinds.NWCWalletRequest) { return } diff --git a/src/models/WalletProfileStore.ts b/src/models/WalletProfileStore.ts index 1109dc61..ca8c082d 100644 --- a/src/models/WalletProfileStore.ts +++ b/src/models/WalletProfileStore.ts @@ -1,5 +1,5 @@ import {Instance, SnapshotOut, types, flow} from 'mobx-state-tree' -import { kinds as NostrKinds } from 'nostr-tools' +import { kinds } from 'nostr-tools' import {KeyChain, MinibitsClient, NostrClient, NostrUnsignedEvent, WalletTask} from '../services' import {log} from '../services/logService' import { Err } from '../utils/AppError' @@ -15,6 +15,7 @@ export type WalletProfile = { picture: string lud16?: string | null device?: string | null + seedHash?: string | null isOwnProfile: boolean // isBatchClaimOn: boolean } @@ -51,7 +52,7 @@ export const WalletProfileStoreModel = types // announce to minibits relay + default public relays with replaceable event const profileEvent: NostrUnsignedEvent = { - kind: NostrKinds.Metadata, + kind: kinds.Metadata, pubkey, tags: [], content: JSON.stringify({ @@ -94,7 +95,7 @@ export const WalletProfileStoreModel = types // announce to new minibits relay const profileEvent: NostrUnsignedEvent = { - kind: NostrKinds.Metadata, + kind: kinds.Metadata, pubkey, tags: [], content: JSON.stringify({ @@ -133,7 +134,10 @@ export const WalletProfileStoreModel = types self.name = name // default name is set on server side, equals walletId self.nip05 = nip05 // default is name@minibits.cash set on server side self.lud16 = lud16 // equals for all @minibits.cash addresses - self.picture = avatar // default picture is set on server side + self.picture = avatar // default picture is set on server side + + const userSettingsStore = getRootStore(self).userSettingsStore + userSettingsStore.setWalletId(walletId) const publishedEvent = yield self.publishToRelays() }) @@ -184,12 +188,9 @@ export const WalletProfileStoreModel = types } ) - self.name = profileRecord.name - self.nip05 = profileRecord.nip05 - self.lud16 = profileRecord.lud16 - const publishedEvent = yield self.publishToRelays() + self.hydrate(profileRecord) - log.debug('[updateName]', 'Wallet name updated in the WalletProfileStore', {self, publishedEvent}) + log.debug('[updateName]', 'Wallet name updated in the WalletProfileStore', {self}) return self }), updatePicture: flow(function* updatePicture(picture: string) { @@ -224,16 +225,8 @@ export const WalletProfileStoreModel = types ) log.trace('[updateNip05]', 'profileRecord', {profileRecord}) - - self.pubkey = newPubkey - self.walletId = profileRecord.walletId - self.nip05 = profileRecord.nip05 - self.lud16 = profileRecord.lud16 - self.name = profileRecord.name - self.picture = profileRecord.avatar - self.isOwnProfile = isOwnProfile - - // do not publish to relay as this is external + + self.hydrate(profileRecord) log.info('[updateNip05]', 'Wallet nip05 updated in the WalletProfileStore', {self}) return self @@ -247,18 +240,14 @@ export const WalletProfileStoreModel = types } ) - self.pubkey = currentPubkey + // in seed based or backup import recovery we rotate the wallet seed to provided one if(!isAddressOnly) { self.seedHash = seedHash - } - self.walletId = profileRecord.walletId - self.nip05 = profileRecord.nip05 - self.lud16 = profileRecord.lud16 - self.name = profileRecord.name - self.picture = profileRecord.avatar - - const publishedEvent = yield self.publishToRelays() - log.info('[recover]', 'Wallet profile recovered in WalletProfileStore', {self, publishedEvent}) + } + + self.hydrate(profileRecord) + + log.info('[recover]', 'Wallet profile recovered in WalletProfileStore', {self}) return self }), setDevice: flow(function* setDevice(device: string) { @@ -277,14 +266,7 @@ export const WalletProfileStoreModel = types }, setSeedHash(seedHash: string) { // used in migration to v8 model self.seedHash = seedHash - }, - /* setIsBatchClaimOn: (isBatchClaimOn: boolean) => { - self.isBatchClaimOn = isBatchClaimOn - return isBatchClaimOn - }, - setPicture(picture: string) { - self.picture = picture - }*/ + } })) .views(self => ({ get npub() { diff --git a/src/models/WalletStore.ts b/src/models/WalletStore.ts index 8c38ea0d..9d87bbad 100644 --- a/src/models/WalletStore.ts +++ b/src/models/WalletStore.ts @@ -10,10 +10,10 @@ import { MintActiveKeys, OutputAmounts, Token, - MeltProofsResponse, - ProofState, + MeltProofsResponse, CheckStateEnum } from '@cashu/cashu-ts' +import { mnemonicToSeedSync } from '@scure/bip39' import { isObj } from '@cashu/cashu-ts/src/utils' import { JS_BUNDLE_VERSION } from '@env' import {KeyChain, MinibitsClient} from '../services' @@ -25,7 +25,7 @@ import { Proof } from './Proof' import { Mint } from './Mint' import { getRootStore } from './helpers/getRootStore' -import {deriveSeedFromMnemonic} from '@cashu/crypto/modules/client/NUT09' + /* Not persisted, in-memory only model of the cashu-ts wallet instances and seed. @@ -156,7 +156,7 @@ export const WalletStoreModel = types if (!mnemonic) { mnemonic = KeyChain.generateMnemonic() as string - const seed = deriveSeedFromMnemonic(mnemonic) // expensive + const seed = mnemonicToSeedSync(mnemonic) // expensive yield KeyChain.saveMnemonic(mnemonic) yield KeyChain.saveSeed(seed) diff --git a/src/screens/DeveloperScreen.tsx b/src/screens/DeveloperScreen.tsx index c21b5a4a..133876df 100644 --- a/src/screens/DeveloperScreen.tsx +++ b/src/screens/DeveloperScreen.tsx @@ -346,6 +346,7 @@ Sentry id: ${userSettingsStore.userSettings.walletId} RightComponent={} style={$item} onPress={deletePending} + topSeparator /> > = obser } const start = performance.now() - const binarySeed = deriveSeedFromMnemonic(mnemonic) // expensive + const binarySeed = mnemonicToSeedSync(mnemonic) // expensive const end = performance.now() - log.debug(`[onConfirm] deriveSeedFromMnemonic took ${end - start} ms.`) + log.debug(`[onConfirm] mnemonicToSeedSync took ${end - start} ms.`) setSeed(binarySeed) setIsValidMnemonic(true) @@ -312,7 +312,7 @@ export const ImportBackupScreen: FC> = obser if(isAddressOnlyRecovery) { // in case of wallet address only recovery we have existing profile linked to currentPubkey // on server. Then we migrate wallet address linked to seed to that profile. - // we keep current wallet seed. + // we keep current wallet seed, not rotating to the provided one. await walletProfileStore.recover(seedHash as string, currentPubkey, true) } else { // In case of recovery from backup we have new nostr keys and we link the new pubkey to the profile @@ -341,6 +341,7 @@ export const ImportBackupScreen: FC> = obser await delay(1000) setStatusMessage('') setIsLoading(false) + // go directly to the wallet (profile hase been rehydrated from the one with the seed) navigation.navigate('Tabs', {screen: 'WalletNavigator', params: {screen: 'Wallet', params: {}}}) } } catch (e: any) { diff --git a/src/screens/NwcScreen.tsx b/src/screens/NwcScreen.tsx index e012ca9c..05d581ef 100644 --- a/src/screens/NwcScreen.tsx +++ b/src/screens/NwcScreen.tsx @@ -165,6 +165,7 @@ export const NwcScreen: FC = observer( const labelText = useThemeColor('textDim') const $subText = {color: useThemeColor('textDim'), fontSize: verticalScale(14)} const headerTitle = useThemeColor('headerTitle') + const buttonBorder = useThemeColor('card') return ( @@ -347,15 +348,17 @@ export const NwcScreen: FC = observer( maxLength={8} placeholder='Enter daily limit in satoshi' selectTextOnFocus={true} - style={[$connInput, {backgroundColor: inputBg, borderBottomRightRadius: 0, borderTopRightRadius: 0}]} + style={[$connInput, {backgroundColor: inputBg}]} />