Switch bip39 generator (#92)

* adding needed polyfills

* adding new package

* updated key validation

* updated suggusted words component

* updated breez test function

* updated mnemoinc generator to new package

* updated mnemoic to seed to new package

* updated back btn

* added a minute sync count

* updated restore page
This commit is contained in:
Blake Kaufman
2025-03-30 12:34:07 -04:00
committed by GitHub
parent c2ec055384
commit 13970b70e4
18 changed files with 431 additions and 381 deletions
+1 -2
View File
@@ -6,8 +6,7 @@
*/
import {NavigationContainer} from '@react-navigation/native';
import 'text-encoding-polyfill';
import 'react-native-gesture-handler';
import './pollyfills';
import './i18n'; // for translation option
import {createNativeStackNavigator} from '@react-navigation/native-stack';
import React, {useCallback, useEffect, useMemo, useRef, useState} from 'react';
+12 -15
View File
@@ -1,15 +1,19 @@
import {TouchableOpacity, View, Text, StyleSheet, Image} from 'react-native';
import {SIZES, COLORS, ICONS, FONT, CENTER} from '../../constants';
import {TouchableOpacity, StyleSheet, Image} from 'react-native';
import {ICONS} from '../../constants';
import {useNavigation} from '@react-navigation/native';
import {keyboardGoBack} from '../../functions/customNavigation';
import ThemeImage from '../../functions/CustomElements/themeImage';
export default function Back_BTN(props) {
export default function Back_BTN() {
const navigate = useNavigation();
return (
<TouchableOpacity
onPress={() => props.navigation(props.destination)}
onPress={() => keyboardGoBack(navigate)}
style={styles.container}>
<Image
source={ICONS.smallArrowLeft}
style={{width: 30, height: 30, marginRight: 4}}
resizeMode="contain"
<ThemeImage
lightModeIcon={ICONS.smallArrowLeft}
darkModeIcon={ICONS.smallArrowLeft}
lightsOutIcon={ICONS.smallArrowLeft}
/>
</TouchableOpacity>
);
@@ -17,13 +21,6 @@ export default function Back_BTN(props) {
const styles = StyleSheet.create({
container: {
backgroundColor: 'transparent',
marginBottom: 10,
flexDirection: 'row',
alignItems: 'center',
},
text: {
fontSize: SIZES.large,
fontFamily: FONT.Other_Medium,
},
});
+2 -3
View File
@@ -2,15 +2,14 @@ import {useNavigation} from '@react-navigation/native';
import {StyleSheet, View} from 'react-native';
import Back_BTN from './back_BTN';
import CustomButton from '../../functions/CustomElements/button';
import {SIZES} from '../../constants';
import {useTranslation} from 'react-i18next';
export default function LoginNavbar({destination}) {
export default function LoginNavbar() {
const navigate = useNavigation();
const {t} = useTranslation();
return (
<View style={styles.container}>
<Back_BTN navigation={navigate.navigate} destination={destination} />
<Back_BTN />
<CustomButton
buttonStyles={{
width: 'auto',
+39 -34
View File
@@ -1,7 +1,8 @@
import {Wordlists} from '@dreson4/react-native-quick-bip39';
import {COLORS, SIZES} from '../../constants';
import {StyleSheet, TouchableOpacity, View} from 'react-native';
import {ThemeText} from '../../functions/CustomElements';
import {wordlist} from '@scure/bip39/wordlists/english';
import {useMemo} from 'react';
export default function SuggestedWordContainer({
inputedKey,
@@ -10,40 +11,44 @@ export default function SuggestedWordContainer({
keyRefs,
}) {
const searchingWord = inputedKey[`key${selectedKey}`] || '';
const suggestedWordElements = Wordlists.en
.filter(word => word.toLowerCase().startsWith(searchingWord.toLowerCase()))
.map(word => {
return (
<TouchableOpacity
style={{
minHeight: 60,
width: '100%',
alignItems: 'center',
justifyContent: 'center',
}}
onPress={() => {
setInputedKey(prev => ({...prev, [`key${selectedKey}`]: word}));
if (selectedKey === 12) {
keyRefs.current[12].blur();
return;
}
keyRefs.current[selectedKey + 1].focus();
}}
key={word}>
<ThemeText
CustomNumberOfLines={1}
styles={{
textTransform: 'capitalize',
fontSize: SIZES.large,
color: COLORS.lightModeText,
includeFontPadding: false,
const suggestedWordElements = useMemo(() => {
return wordlist
.filter(word =>
word.toLowerCase().startsWith(searchingWord.toLowerCase()),
)
.map(word => {
return (
<TouchableOpacity
style={{
minHeight: 60,
width: '100%',
alignItems: 'center',
justifyContent: 'center',
}}
content={word}
/>
</TouchableOpacity>
);
});
onPress={() => {
setInputedKey(prev => ({...prev, [`key${selectedKey}`]: word}));
if (selectedKey === 12) {
keyRefs.current[12].blur();
return;
}
keyRefs.current[selectedKey + 1].focus();
}}
key={word}>
<ThemeText
CustomNumberOfLines={1}
styles={{
textTransform: 'capitalize',
fontSize: SIZES.large,
color: COLORS.lightModeText,
includeFontPadding: false,
}}
content={word}
/>
</TouchableOpacity>
);
});
}, [selectedKey, inputedKey, setInputedKey, keyRefs]);
return (
<View
+2 -2
View File
@@ -1,5 +1,5 @@
import {CashuMint, CashuWallet} from '@cashu/cashu-ts';
import {mnemonicToSeed} from '@dreson4/react-native-quick-bip39';
import {mnemonicToSeed} from '@scure/bip39';
import EventEmitter from 'events';
import {sumProofsValue} from './proofs';
import {getStoredProofs, setMintCounter, storeProofs} from './db';
@@ -13,7 +13,7 @@ export const restoreMintProofs = async mintURL => {
const mnemonic = await retrieveData('mnemonic');
try {
const seed = mnemonicToSeed(mnemonic);
const seed = await mnemonicToSeed(mnemonic);
let progress = 0;
restoreProofsEventListener.emit(
+2 -3
View File
@@ -6,8 +6,7 @@ import {
MintQuoteState,
} from '@cashu/cashu-ts';
import {retrieveData} from '../secureStore';
import {mnemonicToSeed} from '@dreson4/react-native-quick-bip39';
import {parseInput} from '@breeztech/react-native-breez-sdk';
import {mnemonicToSeed} from '@scure/bip39';
import {getLocalStorageItem, setLocalStorageItem} from '../localStorage';
import {BLITZ_DEFAULT_PAYMENT_DESCRIPTION} from '../../constants';
@@ -42,7 +41,7 @@ export const initEcashWallet = async mintURL => {
ks => ks.unit === 'sat',
);
const keys = (await mint.getKeys()).keysets.find(ks => ks.unit === 'sat');
const seed = mnemonicToSeed(mnemonic);
const seed = await mnemonicToSeed(mnemonic);
const wallet = new CashuWallet(mint, {
bip39seed: Uint8Array.from(seed),
mintInfo: await mint.getInfo(),
+9 -11
View File
@@ -1,16 +1,14 @@
import {mnemonicToSeed} from '@breeztech/react-native-breez-sdk';
export default async function isValidMnemonic(mnemonic) {
import * as bip39 from '@scure/bip39';
import {wordlist} from '@scure/bip39/wordlists/english';
export default function isValidMnemonic(mnemonic) {
const mnemoincToString = mnemonic.join(' ');
try {
await mnemonicToSeed(mnemoincToString);
return new Promise(resolve => {
resolve(true);
});
const isValid = bip39.validateMnemonic(mnemoincToString, wordlist);
if (!isValid) throw new Error('Not a valid mnemoinc');
return true;
} catch (err) {
// console.log(err);
return new Promise(resolve => {
resolve(false);
});
console.log('validate mnemoinc error', err);
return false;
}
}
+8 -7
View File
@@ -1,18 +1,19 @@
import {generateMnemonic} from '@dreson4/react-native-quick-bip39';
import {storeData} from './secureStore';
import {generateMnemonic} from '@scure/bip39';
import {wordlist} from '@scure/bip39/wordlists/english';
export default function createAccountMnemonic() {
export default async function createAccountMnemonic() {
try {
let generatedMnemonic = generateMnemonic();
let generatedMnemonic = generateMnemonic(wordlist);
const unuiqueKeys = new Set(generatedMnemonic.split(' '));
if (unuiqueKeys.size != 12) {
if (unuiqueKeys.size !== 12) {
let runCount = 0;
let didFindValidMnemoinc = false;
while (runCount < 50 && !didFindValidMnemoinc) {
console.log('RUNNING IN WHILE LOOP');
console.log(`Running retry for account mnemoinc count: ${runCount}`);
runCount += 1;
const newTry = generateMnemonic();
const newTry = generateMnemonic(wordlist);
const uniqueItems = new Set(newTry.split(' '));
if (uniqueItems.size != 12) continue;
didFindValidMnemoinc = true;
@@ -24,7 +25,7 @@ export default function createAccountMnemonic() {
.split(' ')
.filter(word => word.length > 2)
.join(' ');
storeData('mnemonic', generatedMnemonic);
await storeData('mnemonic', generatedMnemonic);
return filtedMnemoinc;
} catch (err) {
console.log('generate mnemoinc error:', err);
+3 -4
View File
@@ -5,12 +5,11 @@ import {
NodeConfigVariant,
connect,
defaultConfig,
listFiatCurrencies,
mnemonicToSeed,
nodeInfo,
} from '@breeztech/react-native-breez-sdk';
import {btoa, atob, toByteArray} from 'react-native-quick-base64';
import {generateMnemonic} from '@dreson4/react-native-quick-bip39';
import {generateMnemonic} from '@scure/bip39';
import {wordlist} from '@scure/bip39/wordlists/english';
import {ThemeText} from '../functions/CustomElements';
import {startLiquidSession} from '../functions/breezLiquid';
@@ -46,7 +45,7 @@ async function connectToBreezNode() {
try {
// Create the default config
// const mnemoinc = await retrieveData('mnemonic');
const mnemonic = generateMnemonic();
const mnemonic = generateMnemonic(wordlist);
const seed = await mnemonicToSeed(mnemonic);
+1 -1
View File
@@ -15,7 +15,7 @@ export default function DislaimerPage({navigation: {navigate}}) {
return (
<GlobalThemeView useStandardWidth={true}>
<View style={styles.contentContainer}>
<LoginNavbar destination={'Home'} />
<LoginNavbar />
<ThemeText
styles={{
fontSize: SIZES.xxLarge,
-2
View File
@@ -2,8 +2,6 @@ import React, {useEffect} from 'react';
import {View, StyleSheet} from 'react-native';
import {COLORS, SIZES} from '../../constants';
import {useTranslation} from 'react-i18next';
import {useGlobalContextProvider} from '../../../context-store/context';
import {GlobalThemeView, ThemeText} from '../../functions/CustomElements';
import CustomButton from '../../functions/CustomElements/button';
import {createAccountMnemonic} from '../../functions';
@@ -29,7 +29,7 @@ export default function GenerateKey() {
return (
<GlobalThemeView useStandardWidth={true}>
<View style={styles.contentContainer}>
<LoginNavbar destination={'DisclaimerPage'} />
<LoginNavbar />
<View style={styles.container}>
<ThemeText
styles={{...styles.header, marginTop: 30, marginBottom: 30}}
+268 -249
View File
@@ -2,19 +2,20 @@ import {
View,
TextInput,
StyleSheet,
KeyboardAvoidingView,
TouchableWithoutFeedback,
Keyboard,
ScrollView,
Platform,
} from 'react-native';
import {Back_BTN} from '../../../components/login';
import {retrieveData, storeData} from '../../../functions';
import {CENTER, COLORS, FONT, SIZES} from '../../../constants';
import {CENTER, COLORS, FONT, ICONS, SIZES} from '../../../constants';
import {useCallback, useEffect, useMemo, useRef, useState} from 'react';
import isValidMnemonic from '../../../functions/isValidMnemonic';
import {useTranslation} from 'react-i18next';
import {GlobalThemeView, ThemeText} from '../../../functions/CustomElements';
import {
CustomKeyboardAvoidingView,
ThemeText,
} from '../../../functions/CustomElements';
import SuggestedWordContainer from '../../../components/login/suggestedWords';
import CustomButton from '../../../functions/CustomElements/button';
import FullLoadingScreen from '../../../functions/CustomElements/loadingScreen';
@@ -24,14 +25,17 @@ import {WINDOWWIDTH} from '../../../constants/theme';
import {useGlobalThemeContext} from '../../../../context-store/theme';
import useHandleBackPressNew from '../../../hooks/useHandleBackPressNew';
import getClipboardText from '../../../functions/getClipboardText';
import {useNavigation} from '@react-navigation/native';
const NUMARRAY = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12];
const NUMARRAY = Array.from({length: 12}, (_, i) => i + 1);
const INITIAL_KEY_STATE = NUMARRAY.reduce((acc, num) => {
acc[`key${num}`] = '';
return acc;
}, {});
export default function RestoreWallet({
navigation: {navigate, reset},
route: {params},
}) {
export default function RestoreWallet({navigation: {reset}, route: {params}}) {
useHandleBackPressNew();
const navigate = useNavigation();
const {t} = useTranslation();
const {theme, darkModeType} = useGlobalThemeContext();
const insets = useSafeAreaInsets();
@@ -42,20 +46,16 @@ export default function RestoreWallet({
const [isValidating, setIsValidating] = useState(false);
const [currentFocused, setCurrentFocused] = useState(null);
const keyRefs = useRef({});
const [inputedKey, setInputedKey] = useState({
key1: '',
key2: '',
key3: '',
key4: '',
key5: '',
key6: '',
key7: '',
key8: '',
key9: '',
key10: '',
key11: '',
key12: '',
});
const [inputedKey, setInputedKey] = useState(INITIAL_KEY_STATE);
// Helper functions
const navigateToError = useCallback(
errorMessage => {
navigate.navigate('ErrorScreen', {errorMessage});
},
[navigate],
);
const handleInputElement = useCallback((text, keyNumber) => {
setInputedKey(prev => ({...prev, [`key${keyNumber}`]: text}));
}, []);
@@ -76,46 +76,166 @@ export default function RestoreWallet({
[keyRefs],
);
const handleSeedFromClipboard = useCallback(async () => {
try {
const response = await getClipboardText();
if (!response.didWork) throw new Error(response.reason);
const data = response.data;
const splitSeed = data.split(' ');
if (!splitSeed.every(word => word.trim().length > 0))
throw new Error('Not every word is of valid length');
if (splitSeed.length != 12)
throw new Error('Unable to find 12 words from copied recovery phrase.');
console.log(Object.entries(inputedKey));
const newKeys = {};
NUMARRAY.forEach((num, index) => {
newKeys[`key${num}`] = splitSeed[index];
});
setInputedKey(newKeys);
} catch (err) {
console.log('Error getting data from clipbarod', err);
navigateToError(err.message);
}
}, [navigateToError]);
const didEnterCorrectSeed = useCallback(async () => {
try {
const keys = await retrieveData('mnemonic');
const didEnterAllKeys =
Object.keys(inputedKey).filter(value => inputedKey[value]).length ===
12;
if (!didEnterAllKeys)
throw new Error(t('createAccount.restoreWallet.home.error1'));
const enteredMnemonic = Object.values(inputedKey).map(val =>
val.trim().toLowerCase(),
);
const savedMnemonic = keys.split(' ').filter(item => item);
if (JSON.stringify(savedMnemonic) === JSON.stringify(enteredMnemonic)) {
navigate.navigate('PinSetup', {didRestoreWallet: true});
} else throw new Error(t('createAccount.restoreWallet.home.error3'));
} catch (err) {
console.log('did enter correct seed error', err);
navigateToError(err.message);
}
}, [inputedKey, navigateToError]);
const keyValidation = useCallback(async () => {
try {
setIsValidating(true);
const enteredKeys =
Object.keys(inputedKey).filter(value => inputedKey[value]).length ===
12;
if (!enteredKeys)
throw new Error(t('createAccount.restoreWallet.home.error1'));
const mnemonic = Object.values(inputedKey).map(val =>
val.trim().toLowerCase(),
);
const hasAccount = isValidMnemonic(mnemonic);
const hasPin = await retrieveData('pin');
if (!hasAccount)
throw new Error(t('createAccount.restoreWallet.home.error2'));
else {
await storeData('mnemonic', mnemonic.join(' '));
if (hasPin) {
reset({
index: 0,
routes: [
{
name: 'ConnectingToNodeLoadingScreen',
params: {
isInitialLoad: true,
didRestoreWallet: true,
},
},
],
});
} else navigate.navigate('PinSetup', {didRestoreWallet: true});
}
} catch (err) {
console.log('key validation error', err);
navigateToError(err.message);
} finally {
setIsValidating(false);
}
}, [inputedKey, reset, navigate, navigateToError, t]);
const seedItemBackgroundColor = useMemo(
() => (theme ? COLORS.darkModeBackgroundOffset : COLORS.darkModeText),
[theme],
);
const inputKeys = useMemo(() => {
let keyRows = [];
let keyItem = [];
NUMARRAY.forEach(item => {
keyItem.push(
const rows = [];
// Process input fields in pairs
for (let i = 0; i < NUMARRAY.length; i += 2) {
const item1 = NUMARRAY[i];
const item2 = NUMARRAY[i + 1];
rows.push(
<View
key={item}
style={{
...styles.seedItem,
backgroundColor: theme
? COLORS.darkModeBackgroundOffset
: COLORS.darkModeText,
}}>
<ThemeText styles={styles.numberText} content={`${item}.`} />
<TextInput
keyboardAppearance={theme ? 'dark' : 'light'}
ref={ref => (keyRefs.current[item] = ref)} // Store ref for each input
value={inputedKey[`key${item}`]}
onFocus={() => handleFocus(item)} // Track the currently focused input
onSubmitEditing={() => handleSubmit(item)} // Move to next input on submit
onChangeText={e => handleInputElement(e, item)}
blurOnSubmit={false}
cursorColor={COLORS.lightModeText}
style={styles.textInputStyle}
/>
key={`row${item1}`}
style={[styles.seedRow, {marginBottom: item2 !== 12 ? 10 : 0}]}>
{/* First item in row */}
<View
style={[
styles.seedItem,
{backgroundColor: seedItemBackgroundColor},
]}>
<ThemeText styles={styles.numberText} content={`${item1}.`} />
<TextInput
keyboardAppearance={theme ? 'dark' : 'light'}
ref={ref => (keyRefs.current[item1] = ref)}
value={inputedKey[`key${item1}`]}
onFocus={() => handleFocus(item1)}
onSubmitEditing={() => handleSubmit(item1)}
onChangeText={e => handleInputElement(e, item1)}
blurOnSubmit={false}
cursorColor={COLORS.lightModeText}
style={styles.textInputStyle}
/>
</View>
{/* Second item in row */}
<View
style={[
styles.seedItem,
{backgroundColor: seedItemBackgroundColor},
]}>
<ThemeText styles={styles.numberText} content={`${item2}.`} />
<TextInput
keyboardAppearance={theme ? 'dark' : 'light'}
ref={ref => (keyRefs.current[item2] = ref)}
value={inputedKey[`key${item2}`]}
onFocus={() => handleFocus(item2)}
onSubmitEditing={() => handleSubmit(item2)}
onChangeText={e => handleInputElement(e, item2)}
blurOnSubmit={false}
cursorColor={COLORS.lightModeText}
style={styles.textInputStyle}
/>
</View>
</View>,
);
if (item % 2 === 0) {
keyRows.push(
<View
key={`row${item - 1}`}
style={[styles.seedRow, {marginBottom: item !== 12 ? 10 : 0}]}>
{keyItem}
</View>,
);
keyItem = [];
}
});
return keyRows;
}, [handleSubmit, theme, inputedKey, keyRefs]);
}
return rows;
}, [
handleFocus,
handleSubmit,
handleInputElement,
theme,
inputedKey,
seedItemBackgroundColor,
]);
useEffect(() => {
const keyboardDidHideListener = Keyboard.addListener(
@@ -130,206 +250,105 @@ export default function RestoreWallet({
};
}, []);
if (isValidating) {
return <FullLoadingScreen text={t('constants.validating')} />;
}
return (
<TouchableWithoutFeedback
onPress={() => {
console.log('RUNNING');
Keyboard.dismiss();
}}
style={{flex: 1}}>
<KeyboardAvoidingView
behavior={Platform.OS === 'ios' ? 'padding' : null}
style={{flex: 1}}>
<GlobalThemeView styles={{paddingBottom: 0}}>
{isValidating ? (
<FullLoadingScreen text={t('constants.validating')} />
) : (
<>
<View
style={{
flex: 1,
width: WINDOWWIDTH,
...CENTER,
}}>
<Back_BTN
navigation={navigate}
destination={params ? params.goBackName : 'Home'}
/>
<CustomKeyboardAvoidingView
touchableWithoutFeedbackFunction={Keyboard.dismiss}
useLocalPadding={false}
useTouchableWithoutFeedback={true}>
<View style={styles.keyContainer}>
<View style={styles.navContainer}>
<Back_BTN />
</View>
<ThemeText
styles={styles.headerText}
content={
params
? t('createAccount.verifyKeyPage.header')
: t('createAccount.restoreWallet.home.header')
}
/>
<ThemeText
styles={{...styles.headerText}}
content={
params
? t('createAccount.verifyKeyPage.header')
: t('createAccount.restoreWallet.home.header')
}
/>
<ScrollView
showsVerticalScrollIndicator={false}
style={styles.contentContainer}
contentContainerStyle={{
paddingBottom: 10,
paddingTop: 20,
}}>
{inputKeys}
</ScrollView>
{params && !currentFocused && (
<CustomButton
buttonStyles={styles.pasteButton}
textContent={t('constants.paste')}
actionFunction={handleSeedFromClipboard}
/>
)}
{!currentFocused && (
<View
style={{
...styles.mainBTCContainer,
paddingBottom: bottomOffset,
}}>
<CustomButton
buttonStyles={{
width: 145,
marginRight: 10,
}}
textStyles={{
color: COLORS.lightModeText,
}}
textContent={params ? t('constants.skip') : 'Paste'}
actionFunction={() =>
params
? navigate('PinSetup', {isInitialLoad: true})
: handleSeedFromClipboard()
}
/>
<CustomButton
buttonStyles={{
width: 145,
backgroundColor: COLORS.primary,
}}
textStyles={{
color: COLORS.darkModeText,
}}
textContent={params ? t('constants.verify') : 'Restore'}
actionFunction={params ? didEnterCorrectSeed : keyValidation}
/>
</View>
)}
</View>
<ScrollView
showsVerticalScrollIndicator={false}
style={styles.contentContainer}
contentContainerStyle={{paddingBottom: 10}}>
{inputKeys}
</ScrollView>
{params && !currentFocused && (
<CustomButton
buttonStyles={styles.pasteButton}
textContent={t('constants.paste')}
actionFunction={handleSeedFromClipboard}
/>
)}
{!currentFocused && (
<View
style={{
...styles.mainBTCContainer,
paddingBottom: bottomOffset,
}}>
<CustomButton
buttonStyles={{
width: 145,
marginRight: 10,
}}
textStyles={{
color: COLORS.lightModeText,
}}
textContent={params ? t('constants.skip') : 'Paste'}
actionFunction={() =>
params
? navigate('PinSetup', {isInitialLoad: true})
: handleSeedFromClipboard()
}
/>
<CustomButton
buttonStyles={{
width: 145,
backgroundColor: COLORS.primary,
}}
textStyles={{
color: COLORS.darkModeText,
}}
textContent={params ? t('constants.verify') : 'Restore'}
actionFunction={
params ? didEnterCorrectSeed : keyValidation
}
/>
</View>
)}
</View>
{currentFocused && (
<SuggestedWordContainer
inputedKey={inputedKey}
setInputedKey={setInputedKey}
selectedKey={currentFocused}
keyRefs={keyRefs}
/>
)}
</>
)}
</GlobalThemeView>
</KeyboardAvoidingView>
</TouchableWithoutFeedback>
{currentFocused && (
<SuggestedWordContainer
inputedKey={inputedKey}
setInputedKey={setInputedKey}
selectedKey={currentFocused}
keyRefs={keyRefs}
/>
)}
</CustomKeyboardAvoidingView>
);
async function handleSeedFromClipboard() {
const response = await getClipboardText();
if (!response.didWork) {
navigate('ErrorScreen', {errorMessage: response.reason});
return;
}
const data = response.data;
const splitSeed = data.split(' ');
if (!splitSeed.every(word => word.trim().length > 0)) return;
if (splitSeed.length != 12) return;
console.log(Object.entries(inputedKey));
const newKeys = Object.entries(inputedKey).reduce((acc, key) => {
const index = Object.entries(acc).length;
acc[key[0]] = splitSeed[index];
return acc;
}, {});
setInputedKey(newKeys);
}
async function didEnterCorrectSeed() {
const keys = await retrieveData('mnemonic');
const didEnterAllKeys =
Object.keys(inputedKey).filter(value => inputedKey[value]).length === 12;
if (!didEnterAllKeys) {
navigate('ErrorScreen', {
errorMessage: t('createAccount.restoreWallet.home.error1'),
});
return;
}
const enteredMnemonic = Object.values(inputedKey).map(val =>
val.trim().toLowerCase(),
);
const savedMnemonic = keys.split(' ').filter(item => item);
if (JSON.stringify(savedMnemonic) === JSON.stringify(enteredMnemonic)) {
navigate('PinSetup', {didRestoreWallet: true});
} else {
navigate('ErrorScreen', {
errorMessage: t('createAccount.restoreWallet.home.error3'),
});
}
}
async function keyValidation() {
setIsValidating(true);
const enteredKeys =
Object.keys(inputedKey).filter(value => inputedKey[value]).length === 12;
if (!enteredKeys) {
setIsValidating(false);
navigate('ErrorScreen', {
errorMessage: t('createAccount.restoreWallet.home.error1'),
});
return;
}
const mnemonic = Object.values(inputedKey).map(val =>
val.trim().toLowerCase(),
);
const hasAccount = await isValidMnemonic(mnemonic);
const hasPin = await retrieveData('pin');
if (!hasAccount) {
setIsValidating(false);
navigate('ErrorScreen', {
errorMessage: t('createAccount.restoreWallet.home.error2'),
});
return;
} else {
storeData('mnemonic', mnemonic.join(' '));
if (hasPin) {
reset({
index: 0,
routes: [
{
name: 'ConnectingToNodeLoadingScreen',
params: {
isInitialLoad: true,
didRestoreWallet: true,
},
},
],
});
} else navigate('PinSetup', {didRestoreWallet: true});
}
}
}
const styles = StyleSheet.create({
keyContainer: {
flex: 1,
width: WINDOWWIDTH,
...CENTER,
},
navContainer: {
marginRight: 'auto',
},
headerText: {
width: '95%',
fontSize: SIZES.xLarge,
textAlign: 'center',
marginBottom: 30,
...CENTER,
marginBottom: 10,
},
contentContainer: {
flex: 1,
+11
View File
@@ -24,6 +24,7 @@ export function LiquidEventProvider({children}) {
const [pendingNavigation, setPendingNavigation] = useState(null);
const [liquidEvent, setLiquidEvent] = useState(null);
const receivedPayments = useRef([]);
const syncRunCounter = useRef(0);
// Add debug logging
useEffect(() => {
console.log('liquidEvent changed:', liquidEvent);
@@ -163,6 +164,16 @@ export function LiquidEventProvider({children}) {
debouncedStartInterval(
e.type === SdkEventVariant.PAYMENT_SUCCEEDED ? 1 : 0,
);
} else {
console.log(
`Running in sync else statment for liquiid on sync count:${syncRunCounter.current}`,
);
if (syncRunCounter.current > 6) {
console.log('running debounce sync else statment for liquiid');
debouncedStartInterval(0);
syncRunCounter.current = 0;
}
syncRunCounter.current = syncRunCounter.current + 1;
}
};
+6
View File
@@ -2690,6 +2690,8 @@ PODS:
- Yoga
- react-native-context-menu-view (1.16.0):
- React
- react-native-get-random-values (1.11.0):
- React-Core
- react-native-image-picker (8.0.0):
- DoubleConversion
- glog
@@ -3282,6 +3284,7 @@ DEPENDENCIES:
- React-Mapbuffer (from `../node_modules/react-native/ReactCommon`)
- React-microtasksnativemodule (from `../node_modules/react-native/ReactCommon/react/nativemodule/microtasks`)
- react-native-context-menu-view (from `../node_modules/react-native-context-menu-view`)
- react-native-get-random-values (from `../node_modules/react-native-get-random-values`)
- react-native-image-picker (from `../node_modules/react-native-image-picker`)
- react-native-pager-view (from `../node_modules/react-native-pager-view`)
- react-native-quick-base64 (from `../node_modules/react-native-quick-base64`)
@@ -3483,6 +3486,8 @@ EXTERNAL SOURCES:
:path: "../node_modules/react-native/ReactCommon/react/nativemodule/microtasks"
react-native-context-menu-view:
:path: "../node_modules/react-native-context-menu-view"
react-native-get-random-values:
:path: "../node_modules/react-native-get-random-values"
react-native-image-picker:
:path: "../node_modules/react-native-image-picker"
react-native-pager-view:
@@ -3668,6 +3673,7 @@ SPEC CHECKSUMS:
React-Mapbuffer: 9f1c73f53cb27bba0d913545c0ae14c6f0e6e865
React-microtasksnativemodule: e8d0d98c5928b3993bc1f558acde32b70fd94cf4
react-native-context-menu-view: 3bb7e1aa97c897e26a027d23895a60066f7e4e17
react-native-get-random-values: d16467cf726c618e9c7a8c3c39c31faa2244bbba
react-native-image-picker: 07dcbae358126947798da44f3ceab1c73f066527
react-native-pager-view: b466d0c2a37d506d20a5041b8a5eba79fa175420
react-native-quick-base64: d63ae7b77d91c24cb72b46dc66b10c1f3b45ae6e
+3 -1
View File
@@ -19,7 +19,7 @@
"@breeztech/react-native-breez-sdk": "^0.6.6",
"@breeztech/react-native-breez-sdk-liquid": "0.7.1",
"@cashu/cashu-ts": "^2.4.1",
"@dreson4/react-native-quick-bip39": "^0.0.6",
"@craftzdog/react-native-buffer": "^6.0.5",
"@getalby/sdk": "^3.7.0",
"@miblanchard/react-native-slider": "^2.6.0",
"@noble/secp256k1": "^2.2.3",
@@ -33,6 +33,7 @@
"@react-navigation/drawer": "^6.7.2",
"@react-navigation/native": "^6.1.18",
"@react-navigation/native-stack": "^6.11.0",
"@scure/bip39": "^1.5.4",
"bip21": "^3.0.0",
"bolt11": "^1.4.1",
"boltz-swap-web-context": "https://github.com/BlakeKaufman/boltz-swap-web-context.git",
@@ -65,6 +66,7 @@
"react-native-device-info": "^14.0.1",
"react-native-email-link": "^1.16.1",
"react-native-gesture-handler": "^2.19.0",
"react-native-get-random-values": "^1.11.0",
"react-native-image-picker": "^8.0.0",
"react-native-pager-view": "^6.4.1",
"react-native-qrcode-svg": "^6.3.2",
+16
View File
@@ -0,0 +1,16 @@
// Must be the first import
import 'react-native-get-random-values';
import 'react-native-gesture-handler';
// Buffer polyfill
import {Buffer} from '@craftzdog/react-native-buffer';
global.Buffer = Buffer;
// Text encoder/decoder polyfill (if needed)
import 'text-encoding-polyfill';
// Process polyfill
global.process = global.process || {};
global.process.env = global.process.env || {};
global.process.browser = true;
+47 -46
View File
@@ -1821,7 +1821,7 @@ __metadata:
languageName: node
linkType: hard
"@craftzdog/react-native-buffer@npm:^6.0.4, @craftzdog/react-native-buffer@npm:^6.0.5":
"@craftzdog/react-native-buffer@npm:^6.0.5":
version: 6.0.5
resolution: "@craftzdog/react-native-buffer@npm:6.0.5"
dependencies:
@@ -1831,17 +1831,6 @@ __metadata:
languageName: node
linkType: hard
"@dreson4/react-native-quick-bip39@npm:^0.0.6":
version: 0.0.6
resolution: "@dreson4/react-native-quick-bip39@npm:0.0.6"
dependencies:
"@craftzdog/react-native-buffer": ^6.0.5
react-native-quick-crypto: ^0.5.0
unorm: ^1.6.0
checksum: 5cb15b73befbd26f6fb8eefbaea48b58101cfe868e5dfdffe070880db2303d0bfcc0ecfb900abaa174ebd220569f3ba05859bba630f4e3ea9f78126b2bc57369
languageName: node
linkType: hard
"@egjs/hammerjs@npm:^2.0.17":
version: 2.0.17
resolution: "@egjs/hammerjs@npm:2.0.17"
@@ -3317,6 +3306,13 @@ __metadata:
languageName: node
linkType: hard
"@noble/hashes@npm:~1.7.1":
version: 1.7.1
resolution: "@noble/hashes@npm:1.7.1"
checksum: 4f1b56428a10323feef17e4f437c9093556cb18db06f94d254043fadb69c3da8475f96eb3f8322d41e8670117d7486475a8875e68265c2839f60fd03edd6a616
languageName: node
linkType: hard
"@noble/secp256k1@npm:^1.7.1":
version: 1.7.1
resolution: "@noble/secp256k1@npm:1.7.1"
@@ -4240,6 +4236,13 @@ __metadata:
languageName: node
linkType: hard
"@scure/base@npm:~1.2.4":
version: 1.2.4
resolution: "@scure/base@npm:1.2.4"
checksum: db554eb550a1bd17684af9282e1ad751050a13d4add0e83ad61cc496680d7d1c1c1120ca780e72935a293bb59721c20a006a53a5eec6f6b5bdcd702cf27c8cae
languageName: node
linkType: hard
"@scure/bip32@npm:1.3.1":
version: 1.3.1
resolution: "@scure/bip32@npm:1.3.1"
@@ -4282,6 +4285,16 @@ __metadata:
languageName: node
linkType: hard
"@scure/bip39@npm:^1.5.4":
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
"@segment/loosely-validate-event@npm:^2.0.0":
version: 2.0.0
resolution: "@segment/loosely-validate-event@npm:2.0.0"
@@ -4482,13 +4495,6 @@ __metadata:
languageName: node
linkType: hard
"@types/node@npm:^17.0.31":
version: 17.0.45
resolution: "@types/node@npm:17.0.45"
checksum: aa04366b9103b7d6cfd6b2ef64182e0eaa7d4462c3f817618486ea0422984c51fc69fd0d436eae6c9e696ddfdbec9ccaa27a917f7c2e8c75c5d57827fe3d95e8
languageName: node
linkType: hard
"@types/node@npm:^18.0.0":
version: 18.19.50
resolution: "@types/node@npm:18.19.50"
@@ -4828,7 +4834,7 @@ __metadata:
"@breeztech/react-native-breez-sdk": ^0.6.6
"@breeztech/react-native-breez-sdk-liquid": 0.7.1
"@cashu/cashu-ts": ^2.4.1
"@dreson4/react-native-quick-bip39": ^0.0.6
"@craftzdog/react-native-buffer": ^6.0.5
"@getalby/sdk": ^3.7.0
"@miblanchard/react-native-slider": ^2.6.0
"@noble/secp256k1": ^2.2.3
@@ -4846,6 +4852,7 @@ __metadata:
"@react-navigation/drawer": ^6.7.2
"@react-navigation/native": ^6.1.18
"@react-navigation/native-stack": ^6.11.0
"@scure/bip39": ^1.5.4
"@types/react": ^18.2.6
"@types/react-test-renderer": ^18.0.0
babel-jest: ^29.6.3
@@ -4887,6 +4894,7 @@ __metadata:
react-native-dotenv: ^3.4.11
react-native-email-link: ^1.16.1
react-native-gesture-handler: ^2.19.0
react-native-get-random-values: ^1.11.0
react-native-image-picker: ^8.0.0
react-native-pager-view: ^6.4.1
react-native-qrcode-svg: ^6.3.2
@@ -8080,6 +8088,13 @@ __metadata:
languageName: node
linkType: hard
"fast-base64-decode@npm:^1.0.0":
version: 1.0.0
resolution: "fast-base64-decode@npm:1.0.0"
checksum: 4c59eb1775a7f132333f296c5082476fdcc8f58d023c42ed6d378d2e2da4c328c7a71562f271181a725dd17cdaa8f2805346cc330cdbad3b8e4b9751508bd0a3
languageName: node
linkType: hard
"fast-deep-equal@npm:^3.1.1, fast-deep-equal@npm:^3.1.3":
version: 3.1.3
resolution: "fast-deep-equal@npm:3.1.3"
@@ -12713,6 +12728,17 @@ __metadata:
languageName: node
linkType: hard
"react-native-get-random-values@npm:^1.11.0":
version: 1.11.0
resolution: "react-native-get-random-values@npm:1.11.0"
dependencies:
fast-base64-decode: ^1.0.0
peerDependencies:
react-native: ">=0.56"
checksum: 07729f70a007f7a3b8f98ebf687c1298ba288b87dd71d8ba385be6b5a377718b27b97547bbe1db6b225b83ee109dfce0b01721e6ed535d53892f3ac81e6bf975
languageName: node
linkType: hard
"react-native-image-picker@npm:^8.0.0":
version: 8.0.0
resolution: "react-native-image-picker@npm:8.0.0"
@@ -12748,7 +12774,7 @@ __metadata:
languageName: node
linkType: hard
"react-native-quick-base64@npm:^2.0.2, react-native-quick-base64@npm:^2.0.5, react-native-quick-base64@npm:^2.0.8":
"react-native-quick-base64@npm:^2.0.5, react-native-quick-base64@npm:^2.0.8":
version: 2.1.2
resolution: "react-native-quick-base64@npm:2.1.2"
dependencies:
@@ -12760,24 +12786,6 @@ __metadata:
languageName: node
linkType: hard
"react-native-quick-crypto@npm:^0.5.0":
version: 0.5.0
resolution: "react-native-quick-crypto@npm:0.5.0"
dependencies:
"@craftzdog/react-native-buffer": ^6.0.4
"@types/node": ^17.0.31
crypto-browserify: ^3.12.0
events: ^3.3.0
react-native-quick-base64: ^2.0.2
stream-browserify: ^3.0.0
string_decoder: ^1.3.0
peerDependencies:
react: "*"
react-native: "*"
checksum: b9ddb2f83b0190d2831dd912dd425a5a873a89744fe02248ac7125c5afee1945a67df61c343295ab22267a4ec1f4a8e9d380a7ad5deee415d22d7437d4c554e7
languageName: node
linkType: hard
"react-native-quick-crypto@npm:^0.7.4":
version: 0.7.5
resolution: "react-native-quick-crypto@npm:0.7.5"
@@ -14797,13 +14805,6 @@ __metadata:
languageName: node
linkType: hard
"unorm@npm:^1.6.0":
version: 1.6.0
resolution: "unorm@npm:1.6.0"
checksum: 9a86546256a45f855b6cfe719086785d6aada94f63778cecdecece8d814ac26af76cb6da70130da0a08b8803bbf0986e56c7ec4249038198f3de02607fffd811
languageName: node
linkType: hard
"unpipe@npm:~1.0.0":
version: 1.0.0
resolution: "unpipe@npm:1.0.0"