diff --git a/config.json b/config.json index 559295249..86261c237 100644 --- a/config.json +++ b/config.json @@ -7,7 +7,7 @@ "SMART_WALLET_FACTORY_ADDRESS_TESTNET":"0xBaDb31cAf5B95edd785446B76219b60fB1f07233", "RELAY_VERIFIER_ADDRESS_MAINNET":"0x5C9c7d96E6C59E55dA4dCf7F791AE58dAF8DBc86", "RELAY_VERIFIER_ADDRESS_TESTNET":"0x5897E84216220663F306676458Afc7bf2A6A3C52", - "DEPLOY_VERIFIER_ADDRESS_MAINET":"0x2fd633e358bc50ccf6bf926d621e8612b55264c9", + "DEPLOY_VERIFIER_ADDRESS_MAINNET":"0x2fd633e358bc50ccf6bf926d621e8612b55264c9", "DEPLOY_VERIFIER_ADDRESS_TESTNET":"0xAe59e767768c6c25d64619Ee1c498Fd7D83e3c24", "BTC_EXPLORER_ADDRESS_URL_MAINNET":"https://explorer.btc.com/btc/transaction", "BTC_EXPLORER_ADDRESS_URL_TESTNET":"https://live.blockcypher.com/btc-testnet", diff --git a/package.json b/package.json index 89e8d2ee2..6401d0a5c 100644 --- a/package.json +++ b/package.json @@ -34,7 +34,7 @@ "@react-navigation/stack": "^6.0.9", "@reduxjs/toolkit": "^1.9.0", "@rsksmart/rif-id-mnemonic": "^0.1.1", - "@rsksmart/rif-relay-light-sdk": "^1.0.3", + "@rsksmart/rif-relay-light-sdk": "^1.0.11", "@rsksmart/rif-wallet-abi-enhancer": "^1.0.5", "@rsksmart/rif-wallet-adapters": "^1.0.0", "@rsksmart/rif-wallet-bitcoin": "^1.2.0", @@ -141,7 +141,8 @@ "wait-for-expect": "^3.0.2" }, "resolutions": { - "@types/react": "^17" + "@types/react": "^17", + "@rsksmart/rif-relay-light-sdk": "1.0.11" }, "jest": { "preset": "react-native", diff --git a/src/components/GlobalErrorHandler/GlobalErrorHandlerContext.tsx b/src/components/GlobalErrorHandler/GlobalErrorHandlerContext.tsx index 37ec6894e..677382a4d 100644 --- a/src/components/GlobalErrorHandler/GlobalErrorHandlerContext.tsx +++ b/src/components/GlobalErrorHandler/GlobalErrorHandlerContext.tsx @@ -14,7 +14,7 @@ interface GlobalErrorHandlerProviderType { GlobalErrorHandlerViewComp?: FC } -const GlobalErrorHandlerContext = createContext({ +export const GlobalErrorHandlerContext = createContext({ setGlobalError: () => {}, globalError: null, handleReload: () => {}, diff --git a/src/core/CoreWithStore.tsx b/src/core/CoreWithStore.tsx index 8a00d742f..94dff3a65 100644 --- a/src/core/CoreWithStore.tsx +++ b/src/core/CoreWithStore.tsx @@ -1,14 +1,18 @@ import { Provider } from 'react-redux' import { PersistGate } from 'redux-persist/integration/react' -import { store, persistor } from 'store/store' +import { createNewStore } from 'store/store' import { Core } from './Core' -export const CoreWithStore = () => ( - - - - - -) +export const CoreWithStore = () => { + const { store, persistor } = createNewStore() + + return ( + + + + + + ) +} diff --git a/src/core/operations.ts b/src/core/operations.ts index e06b6fc88..25e2d7e1e 100644 --- a/src/core/operations.ts +++ b/src/core/operations.ts @@ -4,18 +4,21 @@ import { RIFWallet } from '@rsksmart/rif-wallet-core' import { KeyManagementSystem } from 'lib/core' import { saveKeys } from 'storage/SecureStorage' - -import { MMKVStorage } from '../storage/MMKVStorage' +import { ChainTypesByIdType } from 'shared/constants/chainConstants' +import { MMKVStorage } from 'storage/MMKVStorage' type CreateRIFWallet = (wallet: Wallet) => Promise export const loadExistingWallet = - (createRIFWallet: CreateRIFWallet) => async (serializedKeys: string) => { + (createRIFWallet: CreateRIFWallet) => + async (serializedKeys: string, chainId: ChainTypesByIdType) => { try { - const { kms, wallets } = - KeyManagementSystem.fromSerialized(serializedKeys) + const { kms, wallets } = KeyManagementSystem.fromSerialized( + serializedKeys, + chainId, + ) - const rifWallet = await createRIFWallet(Object.values(wallets)[0]) + const rifWallet = await createRIFWallet(wallets[0]) const rifWalletIsDeployed = await rifWallet.smartWalletFactory.isDeployed() diff --git a/src/lib/core/KeyManagementSystem.ts b/src/lib/core/KeyManagementSystem.ts index 785571f1d..d2f80b042 100644 --- a/src/lib/core/KeyManagementSystem.ts +++ b/src/lib/core/KeyManagementSystem.ts @@ -84,16 +84,34 @@ export class KeyManagementSystem implements IKeyManagementSystem { /** * Use this method to recover a stored serialized wallet * @param serialized the serialized string + * @param chainId * @returns the KeyManagementSystem that was serialized */ - static fromSerialized (serialized: string): { kms: KeyManagementSystem, wallets: Wallet[] } { + static fromSerialized (serialized: string, chainId: number): { kms: KeyManagementSystem, wallets: Wallet[] } { const { mnemonic, state }: KeyManagementSystemSerialization = JSON.parse(serialized) const kms = new KeyManagementSystem(mnemonic, state) + // If for some reason the chainId that was passed has not been generated, generate and save it + if (!state.lastDerivedAccountIndex[chainId]) { + const newChain = kms.getWalletByChainIdAccountIndex(chainId, 0) + newChain.save() + } + // @TODO Save this state using saveKeys() function + // This is for incremental rollout, we should add both chains when an user creates a wallet + // Now, get the derivation path and use that one to return the wallet + const derivationPath = getDPathByChainId(chainId, 0) // try to create the wallet using the private key which is faster, if it fails, the fallback // is to use the derivedPath and mnemonic to regenerate the private key and then wallet - const wallets = Object.keys(state.derivedPaths) + let wallet + try { + wallet = new Wallet(state.derivedPaths[derivationPath]) + } catch (_error) { + const derivedWalletContainer = kms.deriveWallet(derivationPath) + wallet = derivedWalletContainer.wallet + } + // Old logic, should be analyzed to see if it can be removed or the app modified + /*const wallets = Object.keys(state.derivedPaths) .map((derivedPath: string) => { try { return new Wallet(state.derivedPaths[derivedPath]) @@ -101,11 +119,10 @@ export class KeyManagementSystem implements IKeyManagementSystem { const { wallet } = kms.deriveWallet(derivedPath) return wallet } - }) - + })*/ return { kms, - wallets + wallets: [wallet] } } @@ -160,6 +177,23 @@ export class KeyManagementSystem implements IKeyManagementSystem { } } + /** + * This will get the wallet according to the chain id and accountIndex passed to the function + * @param chainId + * @param accountIndex + */ + getWalletByChainIdAccountIndex(chainId: number, accountIndex: number): SaveableWallet { + const derivationPath = getDPathByChainId(chainId, accountIndex) + const { wallet, privateKey } = this.deriveWallet(derivationPath) + return { + derivationPath, + wallet, + save: () => { + this.state.derivedPaths[derivationPath] = privateKey + } + } + } + /** * Get the account for an arbitrary derivation path * @param derivationPath an arbitrary derivation path diff --git a/src/redux/rootReducer.ts b/src/redux/rootReducer.ts index b0a274709..9fc2d90d0 100644 --- a/src/redux/rootReducer.ts +++ b/src/redux/rootReducer.ts @@ -4,6 +4,7 @@ import { persistReducer, createMigrate, PersistConfig } from 'redux-persist' import { reduxStorage } from 'storage/ReduxStorage' import { contactsReducer } from 'store/slices/contactsSlice' import { ProfileStatus } from 'navigation/profileNavigator/types' +import { getCurrentChainId } from 'storage/ChainStorage' import { accountsReducer } from './slices/accountsSlice' import { balancesReducer } from './slices/balancesSlice' @@ -28,41 +29,37 @@ const migrations = { }), } -const settingsPersistConfig: PersistConfig = { - key: 'settings', - whitelist: [ - 'pin', - 'chainId', - 'keysExist', - 'isFirstLaunch', - 'usedBitcoinAddresses', - ], - storage: reduxStorage, -} +export const createRootReducer = () => { + const settingsPersistConfig: PersistConfig = { + key: 'settings', + whitelist: ['pin', 'keysExist', 'isFirstLaunch', 'usedBitcoinAddresses'], + storage: reduxStorage(getCurrentChainId()), + } -const rootPersistConfig = { - key: 'root', - storage: reduxStorage, - version: 0, - migrate: createMigrate(migrations), - whitelist: [ - 'profile', - 'accounts', - 'contacts', - 'balances', - 'usdPrices', - 'transactions', - ], -} + const rootPersistConfig = { + key: 'root', + version: 0, + migrate: createMigrate(migrations), + whitelist: [ + 'profile', + 'accounts', + 'contacts', + 'balances', + 'usdPrices', + 'transactions', + ], + storage: reduxStorage(getCurrentChainId()), + } -const reducers = combineReducers({ - usdPrices: usdPriceReducer, - balances: balancesReducer, - transactions: transactionsReducer, - settings: persistReducer(settingsPersistConfig, settingsSliceReducer), - profile: profileReducer, - accounts: accountsReducer, - contacts: contactsReducer, -}) + const reducers = combineReducers({ + usdPrices: usdPriceReducer, + balances: balancesReducer, + transactions: transactionsReducer, + settings: persistReducer(settingsPersistConfig, settingsSliceReducer), + profile: profileReducer, + accounts: accountsReducer, + contacts: contactsReducer, + }) -export const rootReducer = persistReducer(rootPersistConfig, reducers) + return persistReducer(rootPersistConfig, reducers) +} diff --git a/src/redux/slices/settingsSlice/index.ts b/src/redux/slices/settingsSlice/index.ts index ec3c12349..a71df7499 100644 --- a/src/redux/slices/settingsSlice/index.ts +++ b/src/redux/slices/settingsSlice/index.ts @@ -25,7 +25,11 @@ import { SocketsEvents, socketsEvents, } from 'src/subscriptions/rifSockets' -import { ChainTypesByIdType } from 'shared/constants/chainConstants' +import { + chainTypesById, + ChainTypesByIdType, +} from 'shared/constants/chainConstants' +import { getCurrentChainId } from 'storage/ChainStorage' import { Bitcoin, @@ -195,8 +199,7 @@ export const unlockApp = createAsyncThunk< request => thunkAPI.dispatch(onRequest({ request })), chainId, ), - )(serializedKeys) - + )(serializedKeys, chainId) if (!existingWallet) { return thunkAPI.rejectWithValue('No Existing Wallet') } @@ -332,7 +335,11 @@ const initialState: SettingsSlice = { const settingsSlice = createSlice({ name: 'settings', - initialState, + initialState: () => ({ + ...initialState, + chainId: getCurrentChainId(), + chainType: chainTypesById[getCurrentChainId()], + }), reducers: { setKeysExist: (state, { payload }: PayloadAction) => { state.keysExist = payload diff --git a/src/redux/store.ts b/src/redux/store.ts index b39d0e379..911b5047b 100644 --- a/src/redux/store.ts +++ b/src/redux/store.ts @@ -10,13 +10,13 @@ import { // REGISTER, } from 'redux-persist' -import { rootReducer } from './rootReducer' +import { createRootReducer } from './rootReducer' // Must use redux-debugger plugin in flipper for the redux debugger to work export const createStore = (preloadedState = {}) => configureStore({ - reducer: rootReducer, + reducer: createRootReducer(), preloadedState, middleware: getDefaultMiddlewares => { const middlewares = getDefaultMiddlewares({ @@ -31,8 +31,14 @@ export const createStore = (preloadedState = {}) => export const store = createStore() -export const persistor = persistStore(store) - +export const createNewStore = () => { + const newStore = createStore() + const newPersistor = persistStore(newStore) + return { + store: newStore, + persistor: newPersistor, + } +} // Infer the `RootState` and `AppDispatch` types from the store itself export type RootState = ReturnType // Inferred type: {posts: PostsState, comments: CommentsState, users: UsersState} diff --git a/src/screens/settings/SettingsScreen.tsx b/src/screens/settings/SettingsScreen.tsx index 02f6e88d5..638cfec1b 100644 --- a/src/screens/settings/SettingsScreen.tsx +++ b/src/screens/settings/SettingsScreen.tsx @@ -1,5 +1,5 @@ import { version } from 'package.json' -import { useCallback, useEffect, useMemo } from 'react' +import { useCallback, useContext, useEffect, useMemo } from 'react' import { useTranslation } from 'react-i18next' import { Platform, ScrollView, StyleSheet, View } from 'react-native' @@ -15,17 +15,26 @@ import { sharedColors, sharedStyles } from 'shared/constants' import { castStyle } from 'shared/utils' import { selectChainId, + selectChainType, selectPin, selectWalletIsDeployed, } from 'store/slices/settingsSlice' import { useAppSelector } from 'store/storeUtils' -import { chainTypesById } from 'shared/constants/chainConstants' +import { ChainTypeEnum, chainTypesById } from 'shared/constants/chainConstants' +import { GlobalErrorHandlerContext } from 'components/GlobalErrorHandler/GlobalErrorHandlerContext' +import { getCurrentChainId, setCurrentChainId } from 'storage/ChainStorage' + +const ChainTypesInversed = { + [ChainTypeEnum.TESTNET]: ChainTypeEnum.MAINNET, + [ChainTypeEnum.MAINNET]: ChainTypeEnum.TESTNET, +} export const SettingsScreen = ({ navigation, }: SettingsScreenProps) => { const statePIN = useAppSelector(selectPin) const chainId = useAppSelector(selectChainId) + const chainType = useAppSelector(selectChainType) const walletIsDeployed = useAppSelector(selectWalletIsDeployed) const smartWalletFactoryAddress = useMemo( @@ -78,6 +87,12 @@ export const SettingsScreen = ({ }, [navigation]) const { t } = useTranslation() + const { handleReload } = useContext(GlobalErrorHandlerContext) + const onSwitchChains = () => { + const currentChainId = getCurrentChainId() + setCurrentChainId(currentChainId === 31 ? 30 : 31) + handleReload() + } return ( @@ -129,6 +144,15 @@ export const SettingsScreen = ({ )} + + + Switch to {ChainTypesInversed[chainType]} + + ChainTypesByIdType = () => + MainStorage.get('chainId') || 31 + +export const setCurrentChainId = (chainId: ChainTypesByIdType) => + MainStorage.set('chainId', chainId) diff --git a/src/storage/ReduxStorage.ts b/src/storage/ReduxStorage.ts index 2ffef08cf..c9ba4f257 100644 --- a/src/storage/ReduxStorage.ts +++ b/src/storage/ReduxStorage.ts @@ -4,17 +4,50 @@ import { MMKVStorage } from './MMKVStorage' const storage = new MMKVStorage() -export const reduxStorage: Storage = { +export const reduxStorage: (chainId: number) => Storage = ( + chainId: number, +) => ({ setItem: (key, value) => { - storage.set(key, value) + const keyToUse = `${key}_${chainId}` + if (chainId === 31) { + storage.set(key, value) + } + storage.set(keyToUse, value) return Promise.resolve(true) }, getItem: key => { - const value = storage.get(key) + let keyToUse = `${key}_${chainId}` + if (chainId === 31) { + keyToUse = key + } + const value = storage.get(keyToUse) return Promise.resolve(value) }, removeItem: key => { - storage.delete(key) + const keyToUse = `${key}_${chainId}` + if (chainId === 31) { + storage.delete(key) + } + storage.delete(keyToUse) return Promise.resolve() }, -} +}) + +// Incremental rollout - uncomment this and remove code above after 1 month (due date: 12 Oct 2023) +/* +export const reduxStorage: (chainId: number) => Storage = ( + chainId: number, +) => ({ + setItem: (key, value) => { + storage.set(`${key}_${chainId}`, value) + return Promise.resolve(true) + }, + getItem: key => { + const value = storage.get(`${key}_${chainId}`) + return Promise.resolve(value) + }, + removeItem: key => { + storage.delete(`${key}_${chainId}`) + return Promise.resolve() + }, +})*/ diff --git a/src/subscriptions/rifSockets.ts b/src/subscriptions/rifSockets.ts index d4439cc97..07068ef7b 100644 --- a/src/subscriptions/rifSockets.ts +++ b/src/subscriptions/rifSockets.ts @@ -85,6 +85,10 @@ export const rifSockets = ({ currentInstance.cache = new MMKVStorage('txs', encryptionKey) }, }, + { + cacheBlockNumberText: `blockNumber_${chainId}`, + cacheTxsText: `cachedTxs_${chainId}`, + }, ) const connectSocket = () => { diff --git a/yarn.lock b/yarn.lock index e9ec27275..2646421f8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2023,10 +2023,19 @@ lodash "^4.17.15" randombytes "^2.1.0" -"@rsksmart/rif-relay-light-sdk@*", "@rsksmart/rif-relay-light-sdk@^1.0.3": - version "1.0.3" - resolved "https://npm.pkg.github.com/download/@rsksmart/rif-relay-light-sdk/1.0.3/2dfc039b136c83259f5337886df2187e424b7e09#2dfc039b136c83259f5337886df2187e424b7e09" - integrity sha512-qUxgnN0h1hS6k2qqtSjJMU6oZo5AiLDFDe4mw/E+nNKeWqs7bWCCHou72XBqRKrFDTG/ZuGeVZeu1s5sM7ibhg== +"@rsksmart/rif-relay-light-sdk@*", "@rsksmart/rif-relay-light-sdk@1.0.10": + version "1.0.10" + resolved "https://npm.pkg.github.com/download/@rsksmart/rif-relay-light-sdk/1.0.10/1ec76ecd23747dde0bebd66651ec0b7327ae08d7#1ec76ecd23747dde0bebd66651ec0b7327ae08d7" + integrity sha512-RrTI5UH0EF8xMmtlwxlX1fVjQkkC+ROY/PPTcTdY8B81LxzT/bFE7xDZJBH1ob94MJ5Jd1btu4LHiG6PGpfY7g== + dependencies: + "@ethersproject/abstract-provider" "^5.7.0" + axios "^1.2.3" + ethers "^5.7.2" + +"@rsksmart/rif-relay-light-sdk@^1.0.11": + version "1.0.11" + resolved "https://npm.pkg.github.com/download/@rsksmart/rif-relay-light-sdk/1.0.11/4b1f889bd9c32eb77932a1ddf357a1b46415094f#4b1f889bd9c32eb77932a1ddf357a1b46415094f" + integrity sha512-GXIlGzKhaV/aIbxPihyXj38nlQow9Q/9kDgXW/yl8gNUAd5iAm0fOp8/GOzdNAUVDqqc2YbPq10nHjX1BNXQEA== dependencies: "@ethersproject/abstract-provider" "^5.7.0" axios "^1.2.3"