From 47dbb0fac32f344199020c11c3eedcaf8590c2bd Mon Sep 17 00:00:00 2001 From: Hanwen Cheng Date: Mon, 31 Aug 2020 17:56:38 +0200 Subject: [PATCH 01/11] load networks from db --- src/constants/networkSpecs.ts | 50 ++++-- .../{networkSpecsTypes.ts => networkTypes.ts} | 31 +++- src/utils/db.ts | 157 ++++++++++++------ 3 files changed, 173 insertions(+), 65 deletions(-) rename src/types/{networkSpecsTypes.ts => networkTypes.ts} (80%) diff --git a/src/constants/networkSpecs.ts b/src/constants/networkSpecs.ts index d6c6995f4e..a203a9bae1 100644 --- a/src/constants/networkSpecs.ts +++ b/src/constants/networkSpecs.ts @@ -16,12 +16,15 @@ import colors from 'styles/colors'; import { + EthereumNetworkDefaultConstants, EthereumNetworkParams, NetworkParams, NetworkProtocol, + SubstrateNetworkBasics, + SubstrateNetworkDefaultConstant, SubstrateNetworkParams, UnknownNetworkParams -} from 'types/networkSpecsTypes'; +} from 'types/networkTypes'; export const unknownNetworkPathId = ''; @@ -80,7 +83,7 @@ const unknownNetworkBase: Record = { } }; -const substrateNetworkBase: Record> = { +const substrateNetworkBase: Record = { [SubstrateNetworkKeys.CENTRIFUGE]: { color: '#FCC367', decimals: 18, @@ -179,7 +182,7 @@ const substrateNetworkBase: Record> = { } }; -const ethereumNetworkBase: Record> = { +const ethereumNetworkBase: Record = { [EthereumNetworkKeys.FRONTIER]: { color: '#8B94B3', ethereumChainId: EthereumNetworkKeys.FRONTIER, @@ -226,16 +229,37 @@ const substrateDefaultValues = { secondaryColor: colors.background.card }; -function setDefault( - networkBase: any, - defaultProps: object -): { [key: string]: any } { - return Object.keys(networkBase).reduce((acc, networkKey) => { +function setEthereumNetworkDefault( + ethereumNetworkBase: Record, + defaultProps: Pick< + EthereumNetworkParams, + 'color' | 'logo' | 'protocol' | 'secondaryColor' + > +): Record { + return Object.keys(ethereumNetworkBase).reduce((acc, networkKey) => { return { ...acc, [networkKey]: { ...defaultProps, - ...networkBase[networkKey] + ...ethereumNetworkBase[networkKey] + } + }; + }, {}); +} + +function setSubstrateNetworkDefault( + substrateNetworkBase: Record, + defaultProps: Pick< + SubstrateNetworkParams, + 'color' | 'logo' | 'protocol' | 'secondaryColor' + > +): Record { + return Object.keys(substrateNetworkBase).reduce((acc, networkKey) => { + return { + ...acc, + [networkKey]: { + ...defaultProps, + ...substrateNetworkBase[networkKey] } }; }, {}); @@ -244,11 +268,15 @@ function setDefault( export const ETHEREUM_NETWORK_LIST: Record< string, EthereumNetworkParams -> = Object.freeze(setDefault(ethereumNetworkBase, ethereumDefaultValues)); +> = Object.freeze( + setEthereumNetworkDefault(ethereumNetworkBase, ethereumDefaultValues) +); export const SUBSTRATE_NETWORK_LIST: Record< string, SubstrateNetworkParams -> = Object.freeze(setDefault(substrateNetworkBase, substrateDefaultValues)); +> = Object.freeze( + setSubstrateNetworkDefault(substrateNetworkBase, substrateDefaultValues) +); export const UNKNOWN_NETWORK: Record< string, UnknownNetworkParams diff --git a/src/types/networkSpecsTypes.ts b/src/types/networkTypes.ts similarity index 80% rename from src/types/networkSpecsTypes.ts rename to src/types/networkTypes.ts index 58265e158e..e26d208946 100644 --- a/src/types/networkSpecsTypes.ts +++ b/src/types/networkTypes.ts @@ -7,14 +7,29 @@ export type NetworkParams = | EthereumNetworkParams | UnknownNetworkParams; +export type SubstrateNetworkDefaultConstant = { + color: string; + decimals: number; + genesisHash: string; + logo?: number; + order: number; + pathId: string; + protocol?: NetworkProtocol; + prefix: number; + secondaryColor?: string; + title: string; + unit: string; +}; + export type SubstrateNetworkBasics = { color?: string; - decimals?: number; + decimals: number; + deleted?: boolean; genesisHash: string; - logo?: number; + order?: number; pathId: string; protocol?: NetworkProtocol; - prefix?: number; + prefix: number; secondaryColor?: string; title: string; unit: string; @@ -34,6 +49,16 @@ export type SubstrateNetworkParams = { unit: string; }; +export type EthereumNetworkDefaultConstants = { + color?: string; + ethereumChainId: string; + logo?: number; + order: number; + protocol?: NetworkProtocol; + secondaryColor?: string; + title: string; +}; + export type EthereumNetworkParams = { color: string; ethereumChainId: string; diff --git a/src/utils/db.ts b/src/utils/db.ts index b143338633..2d181c5935 100644 --- a/src/utils/db.ts +++ b/src/utils/db.ts @@ -20,11 +20,26 @@ import SecureStorage from 'react-native-secure-storage'; import { generateAccountId } from './account'; import { deserializeIdentities, serializeIdentities } from './identitiesUtils'; -import { SubstrateNetworkParams } from 'types/networkSpecsTypes'; +import { deserializeNetworks, mergeNetworks } from 'utils/networksUtils'; +import { SUBSTRATE_NETWORK_LIST } from 'constants/networkSpecs'; +import { + SubstrateNetworkBasics, + SubstrateNetworkParams +} from 'types/networkTypes'; import { defaultNetworkSpecs } from 'modules/network/utils'; import { Account, Identity } from 'types/identityTypes'; import { Tx, TxParticipant } from 'types/tx'; +function handleError(e: Error, label: string): any[] { + console.warn(`loading ${label} error`, e); + return []; +} + +/* + * ======================================== + * Accounts Store + * ======================================== + */ const currentAccountsStore = { keychainService: 'accounts_v3', sharedPreferencesName: 'accounts_v3' @@ -55,6 +70,11 @@ export async function loadAccounts(version = 3): Promise> { ); } +/* + * ======================================== + * Identities Store + * ======================================== + */ const identitiesStore = { keychainService: 'parity_signer_identities', sharedPreferencesName: 'parity_signer_identities' @@ -62,11 +82,6 @@ const identitiesStore = { const currentIdentityStorageLabel = 'identities_v4'; export async function loadIdentities(version = 4): Promise { - function handleError(e: Error): Identity[] { - console.warn('loading identities error', e); - return []; - } - const identityStorageLabel = `identities_v${version}`; try { const identities = await SecureStorage.getItem( @@ -76,7 +91,7 @@ export async function loadIdentities(version = 4): Promise { if (!identities) return []; return deserializeIdentities(identities); } catch (e) { - return handleError(e); + return handleError(e, 'identity'); } } @@ -88,6 +103,56 @@ export const saveIdentities = (identities: Identity[]): void => { ); }; +/* + * ======================================== + * Networks Store + * ======================================== + */ +const networkStorage = { + keychainService: 'parity_signer_networks', + sharedPreferencesName: 'parity_signer_networks' +}; +const currentNetworkStorageLabel = 'networks_v4'; + +export async function loadNetworks(): Promise< + Map +> { + try { + const networksJson = await SecureStorage.getItem( + currentNetworkStorageLabel, + networkStorage + ); + if (!networksJson) return new Map(); + const networksEntries = JSON.parse(networksJson); + return mergeNetworks(SUBSTRATE_NETWORK_LIST, networksEntries); + } catch (e) { + handleError(e, 'networks'); + return new Map(); + } +} + +/* + * ======================================== + * Privacy Policy and Terms Conditions Store + * ======================================== + */ + +export async function loadToCAndPPConfirmation(): Promise { + const result = await AsyncStorage.getItem('ToCAndPPConfirmation_v4'); + + return !!result; +} + +export async function saveToCAndPPConfirmation(): Promise { + await AsyncStorage.setItem('ToCAndPPConfirmation_v4', 'yes'); +} + +/* + * ======================================== + * Tx Store (Archived) + * ======================================== + */ + function accountTxsKey({ address, networkKey @@ -161,53 +226,43 @@ export async function loadAccountTxs( ).map((v: [string, any]) => [v[0], JSON.parse(v[1])]); } -export async function loadToCAndPPConfirmation(): Promise { - const result = await AsyncStorage.getItem('ToCAndPPConfirmation_v4'); - - return !!result; -} - -export async function saveToCAndPPConfirmation(): Promise { - await AsyncStorage.setItem('ToCAndPPConfirmation_v4', 'yes'); -} - /* * ======================================== * NETWORK SPECS * ======================================== */ -const networkSpecsStorageLabel = 'network_specs_v4'; - -/* - * save the new network specs array - */ -export function saveNetworkSpecs(networkSpecs: SubstrateNetworkParams[]): void { - AsyncStorage.setItem(networkSpecsStorageLabel, JSON.stringify(networkSpecs)); -} - -/* - * get all the network specs - */ -export async function getNetworkSpecs(): Promise { - let networkSpecs; - try { - const networkSpecsString = await AsyncStorage.getItem( - networkSpecsStorageLabel - ); - networkSpecs = JSON.parse(networkSpecsString ?? ''); - } catch (e) { - console.warn('loading network specifications error', e); - } - if (networkSpecs === null) return defaultNetworkSpecs(); - - return JSON.parse(networkSpecs ?? ''); -} - -/* - * Called once during onboarding. Populate the local storage with the default network specs. - */ -export async function saveDefaultNetworks(): Promise { - const networkSpecsString = JSON.stringify(defaultNetworkSpecs()); - // AsyncStorage.setItem(networkSpecsStorageLabel, networkSpecsString); -} +// const networkSpecsStorageLabel = 'network_specs_v4'; +// +// /* +// * save the new network specs array +// */ +// export function saveNetworkSpecs(networkSpecs: SubstrateNetworkParams[]): void { +// AsyncStorage.setItem(networkSpecsStorageLabel, JSON.stringify(networkSpecs)); +// } +// +// /* +// * get all the network specs +// */ +// export async function getNetworkSpecs(): Promise { +// let networkSpecs; +// try { +// const networkSpecsString = await AsyncStorage.getItem( +// networkSpecsStorageLabel +// ); +// networkSpecs = JSON.parse(networkSpecsString ?? ''); +// } catch (e) { +// console.warn('loading network specifications error', e); +// } +// if (networkSpecs === null) return defaultNetworkSpecs(); +// +// return JSON.parse(networkSpecs ?? ''); +// } +// +// /* +// * Called once during onboarding. Populate the local storage with the default network specs. +// */ +// export async function saveDefaultNetworks(): Promise { +// const networkSpecsString = JSON.stringify(defaultNetworkSpecs()); +// // AsyncStorage.setItem(networkSpecsStorageLabel, networkSpecsString); +// } From 0ceecf6e7b6d0a1c0eeb87a45175a97478efa4c4 Mon Sep 17 00:00:00 2001 From: Hanwen Cheng Date: Mon, 31 Aug 2020 17:56:54 +0200 Subject: [PATCH 02/11] renaming networkTypes --- src/components/AccountIcon.tsx | 2 +- src/components/AccountIconChooser.tsx | 2 +- src/components/Address.tsx | 2 +- src/components/PathCard.tsx | 2 +- src/components/PathGroupCard.tsx | 2 +- .../main/components/NetworkSelector.tsx | 2 +- .../network/screens/NetworkSettings.tsx | 2 +- src/modules/network/utils.ts | 2 +- src/modules/sign/screens/SignedTx.tsx | 2 +- src/modules/sign/utils.ts | 2 +- src/screens/AccountNew.tsx | 2 +- src/screens/LegacyNetworkChooser.tsx | 2 +- src/screens/PathsList.tsx | 2 +- src/stores/NetworkContext.ts | 113 ++++++++---------- src/stores/ScannerContext.ts | 2 +- src/utils/account.ts | 2 +- src/utils/identitiesUtils.ts | 2 +- src/utils/networksUtils.ts | 54 +++++++++ 18 files changed, 123 insertions(+), 76 deletions(-) create mode 100644 src/utils/networksUtils.ts diff --git a/src/components/AccountIcon.tsx b/src/components/AccountIcon.tsx index fca102b12b..4f65566e4b 100644 --- a/src/components/AccountIcon.tsx +++ b/src/components/AccountIcon.tsx @@ -23,7 +23,7 @@ import FontAwesome from 'react-native-vector-icons/FontAwesome'; import colors from 'styles/colors'; import { NetworkProtocols } from 'constants/networkSpecs'; import { blockiesIcon } from 'utils/native'; -import { NetworkParams, SubstrateNetworkParams } from 'types/networkSpecsTypes'; +import { NetworkParams, SubstrateNetworkParams } from 'types/networkTypes'; export default function AccountIcon(props: { address: string; diff --git a/src/components/AccountIconChooser.tsx b/src/components/AccountIconChooser.tsx index f000c69d45..6813df8fbe 100644 --- a/src/components/AccountIconChooser.tsx +++ b/src/components/AccountIconChooser.tsx @@ -33,7 +33,7 @@ import fonts from 'styles/fonts'; import { debounce } from 'utils/debounce'; import { brainWalletAddress, substrateAddress, words } from 'utils/native'; import { constructSURI } from 'utils/suri'; -import { NetworkParams, SubstrateNetworkParams } from 'types/networkSpecsTypes'; +import { NetworkParams, SubstrateNetworkParams } from 'types/networkTypes'; interface IconType { address: string; diff --git a/src/components/Address.tsx b/src/components/Address.tsx index 6f6c26b6c4..2ddffb6d14 100644 --- a/src/components/Address.tsx +++ b/src/components/Address.tsx @@ -19,7 +19,7 @@ import { Text, TextStyle } from 'react-native'; import fontStyles from 'styles/fontStyles'; import { NetworkProtocols } from 'constants/networkSpecs'; -import { NetworkProtocol } from 'types/networkSpecsTypes'; +import { NetworkProtocol } from 'types/networkTypes'; export default function Address(props: { address: string; diff --git a/src/components/PathCard.tsx b/src/components/PathCard.tsx index 7742ff3537..4583ba3ff7 100644 --- a/src/components/PathCard.tsx +++ b/src/components/PathCard.tsx @@ -36,7 +36,7 @@ import { isSubstrateNetworkParams, isUnknownNetworkParams, SubstrateNetworkParams -} from 'types/networkSpecsTypes'; +} from 'types/networkTypes'; import { ButtonListener } from 'types/props'; import { getAddressWithPath, diff --git a/src/components/PathGroupCard.tsx b/src/components/PathGroupCard.tsx index 52cffd897f..ffcbd5f84a 100644 --- a/src/components/PathGroupCard.tsx +++ b/src/components/PathGroupCard.tsx @@ -37,7 +37,7 @@ import { isUnknownNetworkParams, SubstrateNetworkParams, UnknownNetworkParams -} from 'types/networkSpecsTypes'; +} from 'types/networkTypes'; import { removeSlash } from 'utils/identitiesUtils'; import { useSeedRef } from 'utils/seedRefHooks'; import { unlockSeedPhrase } from 'utils/navigationHelpers'; diff --git a/src/modules/main/components/NetworkSelector.tsx b/src/modules/main/components/NetworkSelector.tsx index 191de6a03f..e02f714543 100644 --- a/src/modules/main/components/NetworkSelector.tsx +++ b/src/modules/main/components/NetworkSelector.tsx @@ -31,7 +31,7 @@ import { isSubstrateNetworkParams, NetworkParams, SubstrateNetworkParams -} from 'types/networkSpecsTypes'; +} from 'types/networkTypes'; import { NavigationAccountIdentityProps } from 'types/props'; import { alertPathDerivationError } from 'utils/alertUtils'; import { withCurrentIdentity } from 'utils/HOC'; diff --git a/src/modules/network/screens/NetworkSettings.tsx b/src/modules/network/screens/NetworkSettings.tsx index a2fcfefd67..3e8250f050 100644 --- a/src/modules/network/screens/NetworkSettings.tsx +++ b/src/modules/network/screens/NetworkSettings.tsx @@ -21,7 +21,7 @@ import { SUBSTRATE_NETWORK_LIST } from 'constants/networkSpecs'; import { NetworkCard } from 'components/AccountCard'; import { filterSubstrateNetworks } from 'modules/network/utils'; import { SafeAreaViewContainer } from 'components/SafeAreaContainer'; -import { NetworkParams, SubstrateNetworkParams } from 'types/networkSpecsTypes'; +import { NetworkParams, SubstrateNetworkParams } from 'types/networkTypes'; import { NavigationProps } from 'types/props'; import colors from 'styles/colors'; import fonts from 'styles/fonts'; diff --git a/src/modules/network/utils.ts b/src/modules/network/utils.ts index 35997307e9..ec2ebe550a 100644 --- a/src/modules/network/utils.ts +++ b/src/modules/network/utils.ts @@ -26,7 +26,7 @@ import { NetworkParams, SubstrateNetworkBasics, SubstrateNetworkParams -} from 'types/networkSpecsTypes'; +} from 'types/networkTypes'; export const filterSubstrateNetworks = ( networkList: Record, diff --git a/src/modules/sign/screens/SignedTx.tsx b/src/modules/sign/screens/SignedTx.tsx index 688d9b0f2a..72f1a3f66b 100644 --- a/src/modules/sign/screens/SignedTx.tsx +++ b/src/modules/sign/screens/SignedTx.tsx @@ -23,7 +23,7 @@ import testIDs from 'e2e/testIDs'; import { AccountsContext } from 'stores/AccountsContext'; import { ScannerContext } from 'stores/ScannerContext'; import { FoundAccount } from 'types/identityTypes'; -import { isEthereumNetworkParams } from 'types/networkSpecsTypes'; +import { isEthereumNetworkParams } from 'types/networkTypes'; import { NavigationProps, NavigationScannerProps } from 'types/props'; import TxDetailsCard from 'modules/sign/components/TxDetailsCard'; import QrView from 'components/QrView'; diff --git a/src/modules/sign/utils.ts b/src/modules/sign/utils.ts index baf12e0893..f1f17f042f 100644 --- a/src/modules/sign/utils.ts +++ b/src/modules/sign/utils.ts @@ -22,7 +22,7 @@ import { AccountsContext } from 'stores/AccountsContext'; import { ScannerContext } from 'stores/ScannerContext'; import { SeedRefsContext, SeedRefsState } from 'stores/SeedRefStore'; import { FoundIdentityAccount } from 'types/identityTypes'; -import { isEthereumNetworkParams } from 'types/networkSpecsTypes'; +import { isEthereumNetworkParams } from 'types/networkTypes'; import { RootStackParamList } from 'types/routes'; import { CompletedParsedData, diff --git a/src/screens/AccountNew.tsx b/src/screens/AccountNew.tsx index d99ab22a16..ce3efcd574 100644 --- a/src/screens/AccountNew.tsx +++ b/src/screens/AccountNew.tsx @@ -20,7 +20,7 @@ import { StyleSheet, Text, View } from 'react-native'; import { NETWORK_LIST, NetworkProtocols } from 'constants/networkSpecs'; import { AccountsContext } from 'stores/AccountsContext'; import { Account, UnlockedAccount } from 'types/identityTypes'; -import { NetworkParams } from 'types/networkSpecsTypes'; +import { NetworkParams } from 'types/networkTypes'; import { NavigationProps } from 'types/props'; import colors from 'styles/colors'; import AccountCard from 'components/AccountCard'; diff --git a/src/screens/LegacyNetworkChooser.tsx b/src/screens/LegacyNetworkChooser.tsx index 834102c7e6..d9973dd742 100644 --- a/src/screens/LegacyNetworkChooser.tsx +++ b/src/screens/LegacyNetworkChooser.tsx @@ -24,7 +24,7 @@ import { UnknownNetworkKeys, SubstrateNetworkKeys } from 'constants/networkSpecs'; -import { NetworkParams } from 'types/networkSpecsTypes'; +import { NetworkParams } from 'types/networkTypes'; import { NavigationProps } from 'types/props'; import colors from 'styles/colors'; import fonts from 'styles/fonts'; diff --git a/src/screens/PathsList.tsx b/src/screens/PathsList.tsx index be3e553592..8cbd922a94 100644 --- a/src/screens/PathsList.tsx +++ b/src/screens/PathsList.tsx @@ -29,7 +29,7 @@ import testIDs from 'e2e/testIDs'; import { isEthereumNetworkParams, isUnknownNetworkParams -} from 'types/networkSpecsTypes'; +} from 'types/networkTypes'; import { NavigationAccountIdentityProps } from 'types/props'; import { withCurrentIdentity } from 'utils/HOC'; import { diff --git a/src/stores/NetworkContext.ts b/src/stores/NetworkContext.ts index bd18096558..7b2d3afae9 100644 --- a/src/stores/NetworkContext.ts +++ b/src/stores/NetworkContext.ts @@ -19,25 +19,23 @@ import { useEffect, useState } from 'react'; import { SubstrateNetworkParams, SubstrateNetworkBasics -} from 'types/networkSpecsTypes'; -import { getNetworkSpecs, saveNetworkSpecs } from 'utils/db'; +} from 'types/networkTypes'; import { getCompleteSubstrateNetworkSpec, checkNewNetworkSpecs } from 'modules/network/utils'; +import { loadNetworks } from 'utils/db'; // https://github.com/polkadot-js/ui/blob/f2f36e2db07f5faec14ee43cf4295f5e8a6f3cfa/packages/reactnative-identicon/src/icons/Polkadot.tsx#L37. // we will need the generate function to be standardized to take an ss58 check address and isSixPoint boolean flag and returns a Circle https://github.com/polkadot-js/ui/blob/ff351a0f3160552f38e393b87fdf6e85051270de/packages/ui-shared/src/polkadotIcon.ts#L12. type NetworkContextState = { - networkSpecs: Array; - newNetworkSpecs: SubstrateNetworkBasics | null; + networkSpecs: Map; }; const defaultState: NetworkContextState = { - networkSpecs: [], - newNetworkSpecs: null + networkSpecs: new Map() }; const deepCopy = ( @@ -46,70 +44,65 @@ const deepCopy = ( export function useNetworksContext(): NetworkContextState { const [networkSpecs, setNetworkSpecs] = useState< - Array + Map >(defaultState.networkSpecs); - const [ - newNetworkSpecs, - setNewNetworkSpecs - ] = useState(defaultState.newNetworkSpecs); useEffect(() => { const refreshList = async function (): Promise { - const initNetworkSpecs = await getNetworkSpecs(); + const initNetworkSpecs = await loadNetworks(); setNetworkSpecs(initNetworkSpecs); }; refreshList(); }, []); - async function submitNewNetworkSpec(): Promise { - if (newNetworkSpecs === null) - throw new Error('NetworkKey is not initialized.'); - - checkNewNetworkSpecs(newNetworkSpecs); - const updatedNetworkSpecs = deepCopy(networkSpecs); - const networkIndex = updatedNetworkSpecs.findIndex( - networkSpec => networkSpec.genesisHash === newNetworkSpecs.genesisHash - ); - const completeNetworkSpec = getCompleteSubstrateNetworkSpec( - newNetworkSpecs - ); - if (networkIndex === -1) { - updatedNetworkSpecs.push(completeNetworkSpec); - } else { - updatedNetworkSpecs.splice(networkIndex, 1, completeNetworkSpec); - } - - setNetworkSpecs(updatedNetworkSpecs); - setNewNetworkSpecs(defaultState.newNetworkSpecs); - - try { - await saveNetworkSpecs(updatedNetworkSpecs); - } catch (e) { - //TODO give feedback to UI - console.error(e); - } - } - - async function deleteNetwork(networkKey: string): Promise { - const updatedNetworkSpecs = deepCopy(networkSpecs); - const networkIndex = updatedNetworkSpecs.findIndex( - networkSpec => networkSpec.genesisHash === networkKey - ); - if (networkIndex === -1) return; - - updatedNetworkSpecs.splice(networkIndex, 1); - setNetworkSpecs(networkSpecs); - - try { - await saveNetworkSpecs(updatedNetworkSpecs); - } catch (e) { - //TODO give feedback to UI - console.error(e); - } - } + // async function submitNewNetworkSpec(): Promise { + // if (newNetworkSpecs === null) + // throw new Error('NetworkKey is not initialized.'); + // + // checkNewNetworkSpecs(newNetworkSpecs); + // const updatedNetworkSpecs = deepCopy(networkSpecs); + // const networkIndex = updatedNetworkSpecs.findIndex( + // networkSpec => networkSpec.genesisHash === newNetworkSpecs.genesisHash + // ); + // const completeNetworkSpec = getCompleteSubstrateNetworkSpec( + // newNetworkSpecs + // ); + // if (networkIndex === -1) { + // updatedNetworkSpecs.push(completeNetworkSpec); + // } else { + // updatedNetworkSpecs.splice(networkIndex, 1, completeNetworkSpec); + // } + // + // setNetworkSpecs(updatedNetworkSpecs); + // setNewNetworkSpecs(defaultState.newNetworkSpecs); + // + // try { + // await saveNetworkSpecs(updatedNetworkSpecs); + // } catch (e) { + // //TODO give feedback to UI + // console.error(e); + // } + // } + // + // async function deleteNetwork(networkKey: string): Promise { + // const updatedNetworkSpecs = deepCopy(networkSpecs); + // const networkIndex = updatedNetworkSpecs.findIndex( + // networkSpec => networkSpec.genesisHash === networkKey + // ); + // if (networkIndex === -1) return; + // + // updatedNetworkSpecs.splice(networkIndex, 1); + // setNetworkSpecs(networkSpecs); + // + // try { + // await saveNetworkSpecs(updatedNetworkSpecs); + // } catch (e) { + // //TODO give feedback to UI + // console.error(e); + // } + // } return { - networkSpecs, - newNetworkSpecs + networkSpecs }; } diff --git a/src/stores/ScannerContext.ts b/src/stores/ScannerContext.ts index 3a29a88d51..faf0bbe8f0 100644 --- a/src/stores/ScannerContext.ts +++ b/src/stores/ScannerContext.ts @@ -28,7 +28,7 @@ import React, { useReducer } from 'react'; import { AccountsContextState } from 'stores/AccountsContext'; import { NETWORK_LIST } from 'constants/networkSpecs'; import { Account, FoundAccount } from 'types/identityTypes'; -import { isEthereumNetworkParams } from 'types/networkSpecsTypes'; +import { isEthereumNetworkParams } from 'types/networkTypes'; import { CompletedParsedData, EthereumParsedData, diff --git a/src/utils/account.ts b/src/utils/account.ts index 19d64aa051..f1881f1eee 100644 --- a/src/utils/account.ts +++ b/src/utils/account.ts @@ -20,7 +20,7 @@ import { EthereumNetworkParams, isSubstrateNetworkParams, isUnknownNetworkParams -} from 'types/networkSpecsTypes'; +} from 'types/networkTypes'; import { ValidSeed } from 'types/utilTypes'; import { getNetworkParams } from 'utils/identitiesUtils'; diff --git a/src/utils/identitiesUtils.ts b/src/utils/identitiesUtils.ts index 4219ef126f..96e0ce55bf 100644 --- a/src/utils/identitiesUtils.ts +++ b/src/utils/identitiesUtils.ts @@ -19,7 +19,7 @@ import { decryptData, substrateAddress } from './native'; import { constructSURI, parseSURI } from './suri'; import { generateAccountId } from './account'; -import { NetworkParams } from 'types/networkSpecsTypes'; +import { NetworkParams } from 'types/networkTypes'; import { TryCreateFunc } from 'utils/seedRefHooks'; import { NETWORK_LIST, diff --git a/src/utils/networksUtils.ts b/src/utils/networksUtils.ts new file mode 100644 index 0000000000..533f2f6680 --- /dev/null +++ b/src/utils/networksUtils.ts @@ -0,0 +1,54 @@ +import { + NetworkParams, + SubstrateNetworkBasics, + SubstrateNetworkParams +} from 'types/networkTypes'; + +export const serializeNetworks = ( + networks: Map +): string => { + const networksEntries = Object.entries(networks); + return JSON.stringify(networksEntries); +}; + +export const deserializeNetworks = ( + networksJson: string +): Map => { + const networksEntries = JSON.parse(networksJson); + return networksEntries.map(new Map(networksEntries)); +}; + +export const deepCopyNetworks = ( + networks: Map +): Map => + deserializeNetworks(serializeNetworks(networks)); + +export const deepCopyNetwork = ( + identity: SubstrateNetworkBasics +): SubstrateNetworkBasics => JSON.parse(JSON.stringify(identity)); + +export const mergeNetworks = ( + defaultNetworks: Record, + newNetworksEntries: [string, SubstrateNetworkParams][] +): Map => { + const mergedNetworksObject = newNetworksEntries.reduce( + ( + acc, + [networkKey, networkParams], + index + ): Record => { + if (!defaultNetworks.hasOwnProperty(networkKey)) { + acc[networkKey] = { + ...networkParams, + logo: require('res/img/logos/Substrate_Dev.png') + }; + return acc; + } + const defaultParams = defaultNetworks[networkKey]; + acc[networkKey] = { ...networkParams, logo: defaultParams.logo }; + return acc; + }, + defaultNetworks + ); + return new Map(Object.entries(mergedNetworksObject)); +}; From d004827c28f97c682938610723a6015d881f47e7 Mon Sep 17 00:00:00 2001 From: Hanwen Cheng Date: Tue, 1 Sep 2020 10:44:50 +0200 Subject: [PATCH 03/11] inject network Context into code --- docs/wiki/HDKD.md | 2 +- src/App.tsx | 40 ++--- src/components/AccountCard.tsx | 10 +- src/components/DerivationNetworkSelector.tsx | 17 +- src/components/PathCard.tsx | 6 +- src/components/PathGroupCard.tsx | 2 +- src/constants/networkSpecs.ts | 30 ++-- .../main/components/NetworkSelector.tsx | 18 ++- .../network/screens/NetworkDetails.tsx | 11 +- .../network/screens/NetworkSettings.tsx | 12 +- src/modules/network/utils.ts | 22 +-- .../sign/components/PayloadDetailsCard.tsx | 147 +++++++++--------- src/modules/sign/screens/QrScanner.tsx | 7 +- src/modules/sign/screens/SignedTx.tsx | 5 +- src/modules/sign/strings.ts | 2 + src/modules/sign/utils.ts | 29 +++- src/screens/AccountDetails.tsx | 5 +- src/screens/AccountPin.tsx | 4 +- src/screens/AccountUnlock.tsx | 4 +- src/screens/LegacyAccountBackup.tsx | 5 +- src/screens/PathDerivation.tsx | 29 ++-- src/screens/PathDetails.tsx | 22 ++- src/screens/PathSecret.tsx | 6 +- src/screens/PathsList.tsx | 11 +- src/stores/AccountsContext.ts | 105 ++++++++----- src/stores/NetworkContext.ts | 62 +++++--- src/stores/RegistriesContext.ts | 16 +- src/stores/ScannerContext.ts | 82 ++++++---- src/stores/TxContext.ts | 22 ++- src/types/networkTypes.ts | 3 + src/utils/account.ts | 22 +-- src/utils/db.ts | 45 ++++-- src/utils/decoders.ts | 13 +- src/utils/identitiesUtils.ts | 94 ++++++----- src/utils/migrationUtils.ts | 7 +- src/utils/networksUtils.ts | 4 +- test/unit/specs/util/decoders.spec.ts | 7 +- test/unit/specs/util/identitiesUtils.spec.ts | 22 ++- 38 files changed, 570 insertions(+), 380 deletions(-) diff --git a/docs/wiki/HDKD.md b/docs/wiki/HDKD.md index 9e9b91d575..ec177eb929 100644 --- a/docs/wiki/HDKD.md +++ b/docs/wiki/HDKD.md @@ -16,7 +16,7 @@ In the v4 version, the data schema is significantly refactored in order to suppo Furthermore, in the new Identity schema, we support hard spoon network in the Substrate network like Kusama, since the Substrate keypairs/accounts generated on Signer will decouple with network's genesisHash, but will use the path as identifier of their networks. From network genesisHash and path's meta data, the `accountId` is generated for signing. ```javascript - const networkParams = getNetwork(meta.path) + const networkParams = getSubstrateNetworkParams(meta.path) const accountId = getAccountId(meta.address, networkParams.genesisHash) ``` Once the network is hard-spooned, users just need to update network params by scanning QR code, without requring new version of the Signer. diff --git a/src/App.tsx b/src/App.tsx index 66707f42cd..9599144bdd 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -29,6 +29,7 @@ import { ScreenStack } from './screens'; +import { useNetworksContext, NetworksContext } from 'stores/NetworkContext'; import { useScannerContext, ScannerContext } from 'stores/ScannerContext'; import { useAccountContext, AccountsContext } from 'stores/AccountsContext'; import CustomAlert from 'components/CustomAlert'; @@ -62,6 +63,7 @@ export default function App(props: AppProps): React.ReactElement { const alertContext = useAlertContext(); const globalContext: GlobalState = useGlobalStateContext(); const seedRefContext = useSeedRefStore(); + const networkContext = useNetworksContext(); const accountsContext = useAccountContext(); const scannerContext = useScannerContext(); @@ -87,24 +89,26 @@ export default function App(props: AppProps): React.ReactElement { return ( - - - - - - - - - {renderStacks()} - - - - - - + + + + + + + + + + {renderStacks()} + + + + + + + ); } diff --git a/src/components/AccountCard.tsx b/src/components/AccountCard.tsx index e7e00415d6..15ed46b2ee 100644 --- a/src/components/AccountCard.tsx +++ b/src/components/AccountCard.tsx @@ -14,7 +14,7 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . -import React, { ReactElement } from 'react'; +import React, { ReactElement, useContext } from 'react'; import { StyleSheet, Text, View, ViewStyle } from 'react-native'; import Icon from 'react-native-vector-icons/MaterialIcons'; @@ -23,7 +23,7 @@ import Address from './Address'; import TouchableItem from './TouchableItem'; import AccountPrefixedTitle from './AccountPrefixedTitle'; -import { getNetworkParams } from 'utils/identitiesUtils'; +import { NetworksContext } from 'stores/NetworkContext'; import Separator from 'components/Separator'; import fontStyles from 'styles/fontStyles'; import colors from 'styles/colors'; @@ -66,7 +66,8 @@ export function NetworkCard({ testID?: string; title: string; }): ReactElement { - const networkParams = getNetworkParams(networkKey); + const { getNetwork } = useContext(NetworksContext); + const networkParams = getNetwork(networkKey ?? ''); const isDisabled = onPress === undefined; return ( @@ -120,10 +121,11 @@ export default function AccountCard({ title, titlePrefix }: AccountCardProps): ReactElement { + const { getNetwork } = useContext(NetworksContext); const defaultTitle = 'No name'; const displayTitle = title.length > 0 ? title : defaultTitle; const seedTypeDisplay = seedType || ''; - const network = getNetworkParams(networkKey); + const network = getNetwork(networkKey ?? ''); return ( . -import React from 'react'; +import React, { useContext } from 'react'; import { Image, Platform, @@ -29,10 +29,8 @@ import Icon from 'react-native-vector-icons/MaterialIcons'; import TransparentBackground from './TransparentBackground'; -import { - SUBSTRATE_NETWORK_LIST, - SubstrateNetworkKeys -} from 'constants/networkSpecs'; +import { NetworksContext } from 'stores/NetworkContext'; +import { SubstrateNetworkKeys } from 'constants/networkSpecs'; import fontStyles from 'styles/fontStyles'; import colors from 'styles/colors'; @@ -53,14 +51,14 @@ export function DerivationNetworkSelector({ networkKey: string; setVisible: (shouldVisible: boolean) => void; }): React.ReactElement { + const { getSubstrateNetwork } = useContext(NetworksContext); + const network = getSubstrateNetwork(networkKey); return ( {ACCOUNT_NETWORK} setVisible(true)}> - - {SUBSTRATE_NETWORK_LIST[networkKey].title} - + {network.title} @@ -77,12 +75,13 @@ export function NetworkOptions({ visible: boolean; setVisible: (shouldVisible: boolean) => void; }): React.ReactElement { + const { networks } = useContext(NetworksContext); const onNetworkSelected = (networkKey: string): void => { setNetworkKey(networkKey); setVisible(false); }; - const menuOptions = Object.entries(SUBSTRATE_NETWORK_LIST) + const menuOptions = Object.entries(networks) .filter(([networkKey]) => !excludedNetworks.includes(networkKey)) .map(([networkKey, networkParams]) => { return ( diff --git a/src/components/PathCard.tsx b/src/components/PathCard.tsx index 4583ba3ff7..45a1480205 100644 --- a/src/components/PathCard.tsx +++ b/src/components/PathCard.tsx @@ -14,7 +14,7 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . -import React, { useEffect, useState } from 'react'; +import React, { useContext, useEffect, useState } from 'react'; import { StyleSheet, Text, View } from 'react-native'; import AntIcon from 'react-native-vector-icons/AntDesign'; @@ -23,6 +23,7 @@ import AccountPrefixedTitle from './AccountPrefixedTitle'; import Address from './Address'; import TouchableItem from './TouchableItem'; +import { NetworksContext } from 'stores/NetworkContext'; import Separator from 'components/Separator'; import { defaultNetworkKey, @@ -64,6 +65,7 @@ export default function PathCard({ testID?: string; titlePrefix?: string; }): React.ReactElement { + const { networks } = useContext(NetworksContext); const isNotEmptyName = name && name !== ''; const pathName = isNotEmptyName ? name : getPathName(path, identity); const { isSeedRefValid, substrateAddress } = useSeedRef( @@ -71,7 +73,7 @@ export default function PathCard({ ); const [address, setAddress] = useState(''); const computedNetworkKey = - networkKey || getNetworkKeyByPath(path, identity.meta.get(path)!); + networkKey || getNetworkKeyByPath(path, identity.meta.get(path)!, networks); useEffect(() => { const getAddress = async (): Promise => { const existedAddress = getAddressWithPath(path, identity); diff --git a/src/components/PathGroupCard.tsx b/src/components/PathGroupCard.tsx index ffcbd5f84a..4fdf998dfd 100644 --- a/src/components/PathGroupCard.tsx +++ b/src/components/PathGroupCard.tsx @@ -89,7 +89,7 @@ export default function PathGroupCard({ await accountsStore.deriveNewPath( nextPath, substrateAddress, - (networkParams as SubstrateNetworkParams).genesisHash, + networkParams as SubstrateNetworkParams, name, '' ); diff --git a/src/constants/networkSpecs.ts b/src/constants/networkSpecs.ts index a203a9bae1..36c21d223c 100644 --- a/src/constants/networkSpecs.ts +++ b/src/constants/networkSpecs.ts @@ -20,7 +20,6 @@ import { EthereumNetworkParams, NetworkParams, NetworkProtocol, - SubstrateNetworkBasics, SubstrateNetworkDefaultConstant, SubstrateNetworkParams, UnknownNetworkParams @@ -71,16 +70,27 @@ export const SubstrateNetworkKeys: Record = Object.freeze({ WESTEND: '0xe143f23803ac50e8f6f8e62695d1ce9e4e1d68aa36c1cd2cfd15340213f3423e' }); +const unknownNetworkParams = { + color: colors.signal.error, + order: 99, + pathId: unknownNetworkPathId, + prefix: 2, + protocol: NetworkProtocols.UNKNOWN, + secondaryColor: colors.background.card, + title: 'Unknown network' +}; + +export const dummySubstrateNetworkParams: SubstrateNetworkParams = { + ...unknownNetworkParams, + decimals: 12, + genesisHash: UnknownNetworkKeys.UNKNOWN, + logo: require('res/img/logos/Substrate_Dev.png'), + protocol: NetworkProtocols.SUBSTRATE, + unit: 'UNIT' +}; + const unknownNetworkBase: Record = { - [UnknownNetworkKeys.UNKNOWN]: { - color: colors.signal.error, - order: 99, - pathId: unknownNetworkPathId, - prefix: 2, - protocol: NetworkProtocols.UNKNOWN, - secondaryColor: colors.background.card, - title: 'Unknown network' - } + [UnknownNetworkKeys.UNKNOWN]: unknownNetworkParams }; const substrateNetworkBase: Record = { diff --git a/src/modules/main/components/NetworkSelector.tsx b/src/modules/main/components/NetworkSelector.tsx index e02f714543..4a084c367f 100644 --- a/src/modules/main/components/NetworkSelector.tsx +++ b/src/modules/main/components/NetworkSelector.tsx @@ -18,13 +18,13 @@ import React, { ReactElement, useContext, useMemo, useState } from 'react'; import { BackHandler, FlatList, FlatListProps } from 'react-native'; import { useFocusEffect } from '@react-navigation/native'; -import { NETWORK_LIST } from 'constants/networkSpecs'; import { filterSubstrateNetworks } from 'modules/network/utils'; import { NetworkCard } from 'components/AccountCard'; import { SafeAreaViewContainer } from 'components/SafeAreaContainer'; import ScreenHeading, { IdentityHeading } from 'components/ScreenHeading'; import testIDs from 'e2e/testIDs'; import { AlertStateContext } from 'stores/alertContext'; +import { NetworksContext } from 'stores/NetworkContext'; import colors from 'styles/colors'; import { isEthereumNetworkParams, @@ -53,6 +53,9 @@ function NetworkSelector({ const isNew = route.params?.isNew ?? false; const [shouldShowMoreNetworks, setShouldShowMoreNetworks] = useState(false); const { identities, currentIdentity } = accountsStore.state; + const { networks, getSubstrateNetwork, allNetworks } = useContext( + NetworksContext + ); const seedRefHooks = useSeedRef(currentIdentity.encryptedSeed); const { unlockWithoutPassword } = useUnlockSeed(seedRefHooks.isSeedRefValid); @@ -93,7 +96,7 @@ function NetworkSelector({ await accountsStore.deriveNewPath( fullPath, seedRefHooks.substrateAddress, - networkKey, + getSubstrateNetwork(networkKey), `${networkParams.title} root`, '' ); @@ -108,7 +111,8 @@ function NetworkSelector({ try { await accountsStore.deriveEthereumAccount( seedRefHooks.brainWalletAddress, - networkKey + networkKey, + allNetworks ); navigateToPathsList(navigation, networkKey); } catch (e) { @@ -177,13 +181,13 @@ function NetworkSelector({ }; const availableNetworks = useMemo( - () => getExistedNetworkKeys(currentIdentity), - [currentIdentity] + () => getExistedNetworkKeys(currentIdentity, networks), + [currentIdentity, networks] ); const networkList = useMemo( () => - filterSubstrateNetworks(NETWORK_LIST, (networkKey, shouldExclude) => { + filterSubstrateNetworks(networks, (networkKey, shouldExclude) => { if (isNew && !shouldExclude) return true; if (shouldShowMoreNetworks) { @@ -192,7 +196,7 @@ function NetworkSelector({ } return availableNetworks.includes(networkKey); }), - [availableNetworks, isNew, shouldShowMoreNetworks] + [availableNetworks, isNew, shouldShowMoreNetworks, networks] ); const renderNetwork = ({ diff --git a/src/modules/network/screens/NetworkDetails.tsx b/src/modules/network/screens/NetworkDetails.tsx index 44b843f06c..35bb81a000 100644 --- a/src/modules/network/screens/NetworkDetails.tsx +++ b/src/modules/network/screens/NetworkDetails.tsx @@ -14,21 +14,22 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . -import React from 'react'; +import React, { useContext } from 'react'; import { NetworkCard } from 'components/AccountCard'; import NetworkInfoCard from 'modules/network/components/NetworkInfoCard'; import { SafeAreaScrollViewContainer } from 'components/SafeAreaContainer'; -import { SUBSTRATE_NETWORK_LIST } from 'constants/networkSpecs'; +import { NetworksContext } from 'stores/NetworkContext'; import { NavigationProps } from 'types/props'; -import { getNetworkKeyByPathId } from 'utils/identitiesUtils'; +import { getSubstrateNetworkKeyByPathId } from 'utils/identitiesUtils'; export default function NetworkDetails({ route }: NavigationProps<'NetworkDetails'>): React.ReactElement { const networkPathId = route.params.pathId; - const networkKey = getNetworkKeyByPathId(networkPathId); - const networkParams = SUBSTRATE_NETWORK_LIST[networkKey]; + const { networks, getSubstrateNetwork } = useContext(NetworksContext); + const networkKey = getSubstrateNetworkKeyByPathId(networkPathId, networks); + const networkParams = getSubstrateNetwork(networkKey); return ( . -import React, { ReactElement } from 'react'; +import React, { ReactElement, useContext } from 'react'; import { FlatList, StyleSheet } from 'react-native'; -import { SUBSTRATE_NETWORK_LIST } from 'constants/networkSpecs'; import { NetworkCard } from 'components/AccountCard'; import { filterSubstrateNetworks } from 'modules/network/utils'; import { SafeAreaViewContainer } from 'components/SafeAreaContainer'; +import { NetworksContext } from 'stores/NetworkContext'; import { NetworkParams, SubstrateNetworkParams } from 'types/networkTypes'; import { NavigationProps } from 'types/props'; import colors from 'styles/colors'; @@ -30,10 +30,10 @@ import ScreenHeading from 'components/ScreenHeading'; export default function NetworkSettings({ navigation }: NavigationProps<'NetworkSettings'>): React.ReactElement { - // const { networkSpecs } = useNetworksContext(); - const networkParams = filterSubstrateNetworks( - SUBSTRATE_NETWORK_LIST - ) as Array<[string, SubstrateNetworkParams]>; + const { networks } = useContext(NetworksContext); + const networkParams = filterSubstrateNetworks(networks) as Array< + [string, SubstrateNetworkParams] + >; const renderNetwork = ({ item }: { diff --git a/src/modules/network/utils.ts b/src/modules/network/utils.ts index ec2ebe550a..67556aa27b 100644 --- a/src/modules/network/utils.ts +++ b/src/modules/network/utils.ts @@ -15,7 +15,6 @@ // along with Parity. If not, see . import { - SUBSTRATE_NETWORK_LIST, SubstrateNetworkKeys, defaultNetworkKey, NETWORK_LIST, @@ -29,7 +28,7 @@ import { } from 'types/networkTypes'; export const filterSubstrateNetworks = ( - networkList: Record, + networkList: Map, extraFilter?: (networkKey: string, shouldExclude: boolean) => boolean ): Array<[string, NetworkParams]> => { const excludedNetworks = [UnknownNetworkKeys.UNKNOWN]; @@ -89,22 +88,3 @@ export function getCompleteSubstrateNetworkSpec( }; return { ...defaultNewNetworkSpecParams, ...newNetworkParams }; } - -export function defaultNetworkSpecs(): SubstrateNetworkParams[] { - const excludedNetworks: string[] = []; - if (!__DEV__) { - excludedNetworks.push(SubstrateNetworkKeys.SUBSTRATE_DEV); - excludedNetworks.push(SubstrateNetworkKeys.KUSAMA_DEV); - } - return Object.entries(SUBSTRATE_NETWORK_LIST).reduce( - ( - networkSpecsList: SubstrateNetworkParams[], - [networkKey, networkParams]: [string, SubstrateNetworkParams] - ) => { - if (excludedNetworks.includes(networkKey)) return networkSpecsList; - networkSpecsList.push(networkParams); - return networkSpecsList; - }, - [] - ); -} diff --git a/src/modules/sign/components/PayloadDetailsCard.tsx b/src/modules/sign/components/PayloadDetailsCard.tsx index 4ca7f05148..cd673c33ce 100644 --- a/src/modules/sign/components/PayloadDetailsCard.tsx +++ b/src/modules/sign/components/PayloadDetailsCard.tsx @@ -24,9 +24,9 @@ import { AnyU8a, IExtrinsicEra, IMethod } from '@polkadot/types/types'; import { ExtrinsicEra } from '@polkadot/types/interfaces'; import { AlertStateContext } from 'stores/alertContext'; +import { NetworksContext } from 'stores/NetworkContext'; import { RegistriesStoreState } from 'stores/RegistriesContext'; import colors from 'styles/colors'; -import { SUBSTRATE_NETWORK_LIST } from 'constants/networkSpecs'; import { withRegistriesStore } from 'utils/HOC'; import { shortString } from 'utils/strings'; import fontStyles from 'styles/fontStyles'; @@ -57,12 +57,14 @@ const ExtrinsicPart = withRegistriesStore( const [tip, setTip] = useState(); const [useFallback, setUseFallBack] = useState(false); const { setAlert } = useContext(AlertStateContext); - const prefix = SUBSTRATE_NETWORK_LIST[networkKey].prefix; + const { networks, getSubstrateNetwork } = useContext(NetworksContext); + const networkParams = getSubstrateNetwork(networkKey); + const prefix = networkParams.prefix; useEffect(() => { if (label === 'Method' && !fallback) { try { - const registry = registriesStore.get(networkKey); + const registry = registriesStore.get(networks, networkKey); const call = registry.createType('Call', value); const methodArgs = {}; @@ -130,7 +132,16 @@ const ExtrinsicPart = withRegistriesStore( if (label === 'Tip' && !fallback) { setTip(formatBalance(value as any)); } - }, [fallback, label, prefix, value, networkKey, registriesStore, setAlert]); + }, [ + fallback, + label, + prefix, + value, + networkKey, + registriesStore, + setAlert, + networks + ]); const renderEraDetails = (): React.ReactElement => { if (period && phase) { @@ -255,79 +266,67 @@ interface PayloadDetailsCardProps { networkKey: string; } -export default class PayloadDetailsCard extends React.PureComponent< - PayloadDetailsCardProps, - { - fallback: boolean; - } -> { - constructor(props: PayloadDetailsCardProps) { - super(props); - const { networkKey } = this.props; - const isKnownNetworkKey = SUBSTRATE_NETWORK_LIST.hasOwnProperty(networkKey); - - if (isKnownNetworkKey) { - formatBalance.setDefaults({ - decimals: SUBSTRATE_NETWORK_LIST[networkKey].decimals, - unit: SUBSTRATE_NETWORK_LIST[networkKey].unit - }); - } +export default function PayloadDetailsCard( + props: PayloadDetailsCardProps +): React.ReactElement { + const { networks, getSubstrateNetwork } = useContext(NetworksContext); + const { networkKey, description, payload, signature, style } = props; + const isKnownNetworkKey = networks.has(networkKey); + const fallback = !isKnownNetworkKey; + const networkParams = getSubstrateNetwork(networkKey); - this.state = { - fallback: !isKnownNetworkKey - }; + if (isKnownNetworkKey) { + formatBalance.setDefaults({ + decimals: networkParams.decimals, + unit: networkParams.unit + }); } - render(): React.ReactElement { - const { fallback } = this.state; - const { description, payload, networkKey, signature, style } = this.props; - - return ( - - {!!description && {description}} - {!!payload && ( - - - - - - - - - )} - {!!signature && ( - - Signature - {signature} - - )} - - ); - } + return ( + + {!!description && {description}} + {!!payload && ( + + + + + + + + + )} + {!!signature && ( + + Signature + {signature} + + )} + + ); } const styles = StyleSheet.create({ diff --git a/src/modules/sign/screens/QrScanner.tsx b/src/modules/sign/screens/QrScanner.tsx index 5707a5fe1a..de682d7707 100644 --- a/src/modules/sign/screens/QrScanner.tsx +++ b/src/modules/sign/screens/QrScanner.tsx @@ -23,6 +23,7 @@ import Button from 'components/Button'; import { useProcessBarCode } from 'modules/sign/utils'; import { useInjectionQR } from 'e2e/injections'; import { AlertStateContext } from 'stores/alertContext'; +import { NetworksContext } from 'stores/NetworkContext'; import { ScannerContext } from 'stores/ScannerContext'; import { NavigationProps } from 'types/props'; import colors from 'styles/colors'; @@ -34,6 +35,7 @@ export default function Scanner({}: NavigationProps< 'QrScanner' >): React.ReactElement { const scannerStore = useContext(ScannerContext); + const networksContextState = useContext(NetworksContext); const { setAlert } = useContext(AlertStateContext); const [enableScan, setEnableScan] = useState(true); const [lastFrame, setLastFrame] = useState(null); @@ -60,7 +62,10 @@ export default function Scanner({}: NavigationProps< ]); } - const processBarCode = useProcessBarCode(showErrorMessage); + const processBarCode = useProcessBarCode( + showErrorMessage, + networksContextState + ); // useEffect((): (() => void) => { // const unsubscribeFocus = navigation.addListener('focus', () => { // setLastFrame(null); diff --git a/src/modules/sign/screens/SignedTx.tsx b/src/modules/sign/screens/SignedTx.tsx index 72f1a3f66b..71ff0ef571 100644 --- a/src/modules/sign/screens/SignedTx.tsx +++ b/src/modules/sign/screens/SignedTx.tsx @@ -21,6 +21,7 @@ import strings from 'modules/sign/strings'; import { SafeAreaScrollViewContainer } from 'components/SafeAreaContainer'; import testIDs from 'e2e/testIDs'; import { AccountsContext } from 'stores/AccountsContext'; +import { NetworksContext } from 'stores/NetworkContext'; import { ScannerContext } from 'stores/ScannerContext'; import { FoundAccount } from 'types/identityTypes'; import { isEthereumNetworkParams } from 'types/networkTypes'; @@ -29,7 +30,6 @@ import TxDetailsCard from 'modules/sign/components/TxDetailsCard'; import QrView from 'components/QrView'; import fontStyles from 'styles/fontStyles'; import CompatibleCard from 'components/CompatibleCard'; -import { getNetworkParams } from 'utils/identitiesUtils'; import { Transaction } from 'utils/transaction'; import styles from 'modules/sign/styles'; import Separator from 'components/Separator'; @@ -63,8 +63,9 @@ function SignedTxView({ scannerStore }: Props): React.ReactElement { const accountsStore = useContext(AccountsContext); + const { getNetwork } = useContext(NetworksContext); const { signedData, tx } = scannerStore.state; - const senderNetworkParams = getNetworkParams(sender.networkKey); + const senderNetworkParams = getNetwork(sender.networkKey); const isEthereum = isEthereumNetworkParams(senderNetworkParams); const { value, gas, gasPrice } = tx as Transaction; diff --git a/src/modules/sign/strings.ts b/src/modules/sign/strings.ts index 2bbceed771..4a7d0000ed 100644 --- a/src/modules/sign/strings.ts +++ b/src/modules/sign/strings.ts @@ -1,6 +1,8 @@ const strings = { ERROR_ADDRESS_MESSAGE: 'Please create a transaction using software such as MyCrypto or Fether so that Parity Signer can sign it.', + ERROR_NO_NETWORK: + 'Signer does not currently recognize a chain with genesis hash, please add the network first', ERROR_NO_RAW_DATA: 'There is no raw data from the request', ERROR_NO_SENDER_FOUND: 'There is no related account in the app', ERROR_NO_SENDER_IDENTITY: 'There is no related identity in the app', diff --git a/src/modules/sign/utils.ts b/src/modules/sign/utils.ts index f1f17f042f..2713d0e0e3 100644 --- a/src/modules/sign/utils.ts +++ b/src/modules/sign/utils.ts @@ -19,6 +19,7 @@ import { useContext } from 'react'; import strings from 'modules/sign/strings'; import { AccountsContext } from 'stores/AccountsContext'; +import { NetworksContextState } from 'stores/NetworkContext'; import { ScannerContext } from 'stores/ScannerContext'; import { SeedRefsContext, SeedRefsState } from 'stores/SeedRefStore'; import { FoundIdentityAccount } from 'types/identityTypes'; @@ -38,7 +39,7 @@ import { isJsonString, rawDataToU8A } from 'utils/decoders'; -import { getIdentityFromSender, getNetworkParams } from 'utils/identitiesUtils'; +import { getIdentityFromSender } from 'utils/identitiesUtils'; import { SeedRefClass } from 'utils/native'; import { unlockSeedPhrase, @@ -56,8 +57,10 @@ function getSeedRef( } export function useProcessBarCode( - showErrorMessage: (title: string, message: string) => void + showErrorMessage: (title: string, message: string) => void, + networksContextState: NetworksContextState ): (txRequestData: TxRequestData) => Promise { + const { allNetworks, networks } = networksContextState; const accountsStore = useContext(AccountsContext); const scannerStore = useContext(ScannerContext); const [seedRefs] = useContext(SeedRefsContext); @@ -77,7 +80,11 @@ export function useProcessBarCode( } else if (!scannerStore.state.multipartComplete) { const strippedData = rawDataToU8A(txRequestData.rawData); if (strippedData === null) throw new Error(strings.ERROR_NO_RAW_DATA); - const parsedData = await constructDataFromBytes(strippedData, false); + const parsedData = await constructDataFromBytes( + strippedData, + false, + networks + ); return parsedData; } else { throw new Error(strings.ERROR_NO_RAW_DATA); @@ -91,7 +98,8 @@ export function useProcessBarCode( const multiFramesResult = await scannerStore.setPartData( parsedData.currentFrame, parsedData.frameCount, - parsedData.partData + parsedData.partData, + networksContextState ); if (isMultiFramesInfo(multiFramesResult)) { return null; @@ -103,11 +111,12 @@ export function useProcessBarCode( } } - async function unlockSeedAndSign( + async function _unlockSeedAndSign( sender: FoundIdentityAccount, qrInfo: QrInfo ): Promise { - const senderNetworkParams = getNetworkParams(sender.networkKey); + const senderNetworkParams = allNetworks.get(sender.networkKey); + if (!senderNetworkParams) throw new Error(strings.ERROR_NO_NETWORK); const isEthereum = isEthereumNetworkParams(senderNetworkParams); // 1. check if sender existed @@ -181,7 +190,7 @@ export function useProcessBarCode( const seedRef = getSeedRef(sender.encryptedSeed, seedRefs); const isSeedRefInvalid = seedRef && seedRef.isValid(); - await unlockSeedAndSign(sender, qrInfo); + await _unlockSeedAndSign(sender, qrInfo); const nextRoute = type === 'transaction' ? 'SignedTx' : 'SignedMessage'; if (isSeedRefInvalid) { @@ -196,7 +205,11 @@ export function useProcessBarCode( const parsedData = await parseQrData(txRequestData); const unsignedData = await checkMultiFramesData(parsedData); if (unsignedData === null) return; - const qrInfo = await scannerStore.setData(accountsStore, unsignedData); + const qrInfo = await scannerStore.setData( + accountsStore, + unsignedData, + networksContextState + ); await unlockAndNavigationToSignedQR(qrInfo); scannerStore.clearMultipartProgress(); } catch (e) { diff --git a/src/screens/AccountDetails.tsx b/src/screens/AccountDetails.tsx index 0b7bdf9cf6..b9df7076d5 100644 --- a/src/screens/AccountDetails.tsx +++ b/src/screens/AccountDetails.tsx @@ -21,12 +21,12 @@ import { SafeAreaViewContainer } from 'components/SafeAreaContainer'; import { NetworkProtocols } from 'constants/networkSpecs'; import { AccountsContext } from 'stores/AccountsContext'; import { AlertStateContext } from 'stores/alertContext'; +import { NetworksContext } from 'stores/NetworkContext'; import colors from 'styles/colors'; import AccountCard from 'components/AccountCard'; import QrView from 'components/QrView'; import PopupMenu from 'components/PopupMenu'; import { alertDeleteLegacyAccount } from 'utils/alertUtils'; -import { getNetworkParams } from 'utils/identitiesUtils'; import { navigateToLandingPage, navigateToLegacyAccountList @@ -42,12 +42,13 @@ export default function AccountDetails({ }: NavigationProps<'AccountDetails'>): React.ReactElement { const accountsStore = useContext(AccountsContext); const account = accountsStore.getSelected(); + const { getNetwork } = useContext(NetworksContext); const { setAlert } = useContext(AlertStateContext); const { accounts, selectedKey } = accountsStore.state; if (!account) return ; - const network = getNetworkParams(account.networkKey); + const network = getNetwork(account.networkKey); const protocol = network.protocol; diff --git a/src/screens/AccountPin.tsx b/src/screens/AccountPin.tsx index 6f356441b7..5331d8c38f 100644 --- a/src/screens/AccountPin.tsx +++ b/src/screens/AccountPin.tsx @@ -20,6 +20,7 @@ import { StyleSheet, Text } from 'react-native'; import { CommonActions } from '@react-navigation/native'; import { AccountsContext } from 'stores/AccountsContext'; +import { NetworksContext } from 'stores/NetworkContext'; import fontStyles from 'styles/fontStyles'; import { NavigationProps } from 'types/props'; import colors from 'styles/colors'; @@ -41,6 +42,7 @@ function AccountPin({ route }: NavigationProps<'AccountPin'>): React.ReactElement { const accountsStore = useContext(AccountsContext); + const { allNetworks } = useContext(NetworksContext); const initialState: State = { confirmation: '', focusConfirmation: false, @@ -62,7 +64,7 @@ function AccountPin({ const account = accountCreation ? newAccount : accountsStore.getSelected()!; if (pin.length >= 6 && pin === confirmation) { if (accountCreation) { - await accountsStore.submitNew(pin); + await accountsStore.submitNew(pin, allNetworks); return navigateToLegacyAccountList(navigation); } else { await accountsStore.save(selectedKey, account, pin); diff --git a/src/screens/AccountUnlock.tsx b/src/screens/AccountUnlock.tsx index b73d39620b..e6782ace19 100644 --- a/src/screens/AccountUnlock.tsx +++ b/src/screens/AccountUnlock.tsx @@ -20,6 +20,7 @@ import { CommonActions } from '@react-navigation/native'; import { AccountsContext } from 'stores/AccountsContext'; import { SafeAreaViewContainer } from 'components/SafeAreaContainer'; +import { NetworksContext } from 'stores/NetworkContext'; import { ScannerContext } from 'stores/ScannerContext'; import { NavigationProps } from 'types/props'; import colors from 'styles/colors'; @@ -34,12 +35,13 @@ export function AccountUnlockAndSign( const { navigation, route } = props; const next = route.params.next ?? 'SignedTx'; const scannerStore = useContext(ScannerContext); + const { getNetwork } = useContext(NetworksContext); return ( => { try { - await scannerStore.signDataLegacy(pin); + await scannerStore.signDataLegacy(pin, getNetwork); return true; } catch (e) { return false; diff --git a/src/screens/LegacyAccountBackup.tsx b/src/screens/LegacyAccountBackup.tsx index bd82afc327..32c3199132 100644 --- a/src/screens/LegacyAccountBackup.tsx +++ b/src/screens/LegacyAccountBackup.tsx @@ -21,6 +21,7 @@ import { SafeAreaScrollViewContainer } from 'components/SafeAreaContainer'; import { NetworkProtocols } from 'constants/networkSpecs'; import { AccountsContext } from 'stores/AccountsContext'; import { AlertStateContext } from 'stores/alertContext'; +import { NetworksContext } from 'stores/NetworkContext'; import { UnlockedAccount } from 'types/identityTypes'; import { NavigationProps } from 'types/props'; import colors from 'styles/colors'; @@ -32,13 +33,13 @@ import ScreenHeading from 'components/ScreenHeading'; import TouchableItem from 'components/TouchableItem'; import DerivationPasswordVerify from 'components/DerivationPasswordVerify'; import { alertBackupDone, alertCopyBackupPhrase } from 'utils/alertUtils'; -import { getNetworkParams } from 'utils/identitiesUtils'; function LegacyAccountBackup({ navigation, route }: NavigationProps<'LegacyAccountBackup'>): React.ReactElement { const accountsStore = useContext(AccountsContext); + const { getNetwork } = useContext(NetworksContext); const { selectedKey, newAccount } = accountsStore.state; useEffect(() => { const handleAppStateChange = (nextAppState: AppStateStatus): void => { @@ -69,7 +70,7 @@ function LegacyAccountBackup({ seed = '', seedPhrase = '' } = isNew ? newAccount : (accountsStore.getSelected() as UnlockedAccount); - const protocol = getNetworkParams(networkKey).protocol; + const protocol = getNetwork(networkKey).protocol; return ( diff --git a/src/screens/PathDerivation.tsx b/src/screens/PathDerivation.tsx index cee901edf1..ccc2900262 100644 --- a/src/screens/PathDerivation.tsx +++ b/src/screens/PathDerivation.tsx @@ -20,6 +20,7 @@ import PasswordInput from 'components/PasswordInput'; import testIDs from 'e2e/testIDs'; import { defaultNetworkKey, UnknownNetworkKeys } from 'constants/networkSpecs'; import { AlertStateContext } from 'stores/alertContext'; +import { NetworksContext } from 'stores/NetworkContext'; import { Identity } from 'types/identityTypes'; import { NavigationAccountIdentityProps } from 'types/props'; import TextInput from 'components/TextInput'; @@ -27,7 +28,7 @@ import { withCurrentIdentity } from 'utils/HOC'; import { extractPathId, getNetworkKey, - getNetworkKeyByPathId, + getSubstrateNetworkKeyByPathId, validateDerivedPath } from 'utils/identitiesUtils'; import { unlockSeedPhrase } from 'utils/navigationHelpers'; @@ -43,17 +44,6 @@ import { useSeedRef } from 'utils/seedRefHooks'; import Button from 'components/Button'; import { KeyboardAwareContainer } from 'modules/unlock/components/Container'; -function getParentNetworkKey( - parentPath: string, - currentIdentity: Identity -): string { - if (currentIdentity.meta.has(parentPath)) { - return getNetworkKey(parentPath, currentIdentity); - } - const pathId = extractPathId(parentPath); - return getNetworkKeyByPathId(pathId); -} - function PathDerivation({ accountsStore, navigation, @@ -63,6 +53,7 @@ function PathDerivation({ const [keyPairsName, setKeyPairsName] = useState(''); const [modalVisible, setModalVisible] = useState(false); const [password, setPassword] = useState(''); + const { networks, getSubstrateNetwork } = useContext(NetworksContext); const pathNameInput = useRef(null); const { setAlert } = useContext(AlertStateContext); const currentIdentity = accountsStore.state.currentIdentity; @@ -70,6 +61,18 @@ function PathDerivation({ currentIdentity.encryptedSeed ); const parentPath = route.params.parentPath; + + function getParentNetworkKey( + parentPath: string, + currentIdentity: Identity + ): string { + if (currentIdentity.meta.has(parentPath)) { + return getNetworkKey(parentPath, currentIdentity, networks); + } + const pathId = extractPathId(parentPath); + return getSubstrateNetworkKeyByPathId(pathId, networks); + } + const parentNetworkKey = useMemo( () => getParentNetworkKey(parentPath, currentIdentity), [parentPath, currentIdentity] @@ -93,7 +96,7 @@ function PathDerivation({ await accountsStore.deriveNewPath( completePath, substrateAddress, - currentNetworkKey, + getSubstrateNetwork(currentNetworkKey), keyPairsName, password ); diff --git a/src/screens/PathDetails.tsx b/src/screens/PathDetails.tsx index 2482ec8080..ded6845471 100644 --- a/src/screens/PathDetails.tsx +++ b/src/screens/PathDetails.tsx @@ -23,6 +23,7 @@ import { SafeAreaViewContainer } from 'components/SafeAreaContainer'; import { defaultNetworkKey, UnknownNetworkKeys } from 'constants/networkSpecs'; import testIDs from 'e2e/testIDs'; import { AlertStateContext } from 'stores/alertContext'; +import { NetworksContext } from 'stores/NetworkContext'; // TODO use typescript 3.8's type import, Wait for prettier update. import { AccountsStoreStateWithIdentity } from 'types/identityTypes'; import { NavigationAccountIdentityProps } from 'types/props'; @@ -70,13 +71,16 @@ export function PathDetailsView({ const { unlockWithoutPassword, unlockWithPassword } = useUnlockSeed( isSeedRefValid ); + const networksContextState = useContext(NetworksContext); + const { allNetworks, networks } = networksContextState; if (!address) return ; const isUnknownNetwork = networkKey === UnknownNetworkKeys.UNKNOWN; const formattedNetworkKey = isUnknownNetwork ? defaultNetworkKey : networkKey; - const accountId = generateAccountId({ + const accountId = generateAccountId( address, - networkKey: formattedNetworkKey - }); + formattedNetworkKey, + allNetworks + ); const onTapDeriveButton = (): Promise => unlockWithoutPassword({ @@ -89,11 +93,12 @@ export function PathDetailsView({ case 'PathDelete': alertDeleteAccount(setAlert, 'this account', async () => { try { - await accountsStore.deletePath(path); + await accountsStore.deletePath(path, networksContextState); if (isSubstratePath(path)) { const listedPaths = getPathsWithSubstrateNetworkKey( accountsStore.state.currentIdentity!, - networkKey + networkKey, + networks ); const hasOtherPaths = listedPaths.length > 0; hasOtherPaths @@ -181,7 +186,12 @@ function PathDetails({ route }: NavigationAccountIdentityProps<'PathDetails'>): React.ReactElement { const path = route.params.path; - const networkKey = getNetworkKey(path, accountsStore.state.currentIdentity); + const { networks } = useContext(NetworksContext); + const networkKey = getNetworkKey( + path, + accountsStore.state.currentIdentity, + networks + ); return ( . -import React, { useEffect, useState } from 'react'; +import React, { useContext, useEffect, useState } from 'react'; import { ScrollView } from 'react-native'; import ScreenHeading from 'components/ScreenHeading'; @@ -23,6 +23,7 @@ import QrView from 'components/QrView'; import { SafeAreaViewContainer } from 'components/SafeAreaContainer'; import { PasswordedAccountExportWarning } from 'components/Warnings'; import testIDs from 'e2e/testIDs'; +import { NetworksContext } from 'stores/NetworkContext'; import { NavigationAccountIdentityProps } from 'types/props'; import { withCurrentIdentity } from 'utils/HOC'; import { getNetworkKey, getPathName } from 'utils/identitiesUtils'; @@ -33,6 +34,7 @@ function PathSecret({ route, navigation }: NavigationAccountIdentityProps<'PathSecret'>): React.ReactElement { + const { networks } = useContext(NetworksContext); const { currentIdentity } = accountsStore.state; const [secret, setSecret] = useState(''); const { substrateSecret, isSeedRefValid } = useSeedRef( @@ -43,7 +45,7 @@ function PathSecret({ useEffect(() => { const getAndSetSecret = async (): Promise => { - const networkKey = getNetworkKey(path, currentIdentity); + const networkKey = getNetworkKey(path, currentIdentity, networks); const password = route.params.password ?? ''; const accountName = getPathName(path, currentIdentity); const generatedSecret = await substrateSecret(`${path}///${password}`); diff --git a/src/screens/PathsList.tsx b/src/screens/PathsList.tsx index 8cbd922a94..c4d894a59f 100644 --- a/src/screens/PathsList.tsx +++ b/src/screens/PathsList.tsx @@ -14,11 +14,12 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . -import React, { useMemo } from 'react'; +import React, { useContext, useMemo } from 'react'; import { ScrollView } from 'react-native'; import { PathDetailsView } from './PathDetails'; +import { NetworksContext } from 'stores/NetworkContext'; import { PathGroup } from 'types/identityTypes'; import PathGroupCard from 'components/PathGroupCard'; import { useUnlockSeed } from 'utils/navigationHelpers'; @@ -47,7 +48,8 @@ function PathsList({ route }: NavigationAccountIdentityProps<'PathsList'>): React.ReactElement { const networkKey = route.params.networkKey ?? UnknownNetworkKeys.UNKNOWN; - const networkParams = NETWORK_LIST[networkKey]; + const { networks, getNetwork } = useContext(NetworksContext); + const networkParams = getNetwork(networkKey); const { currentIdentity } = accountsStore.state; const isEthereumPath = isEthereumNetworkParams(networkParams); @@ -56,9 +58,10 @@ function PathsList({ if (!currentIdentity || isEthereumPath) return null; const listedPaths = getPathsWithSubstrateNetworkKey( currentIdentity, - networkKey + networkKey, + networks ); - return groupPaths(listedPaths); + return groupPaths(listedPaths, networks); }, [currentIdentity, isEthereumPath, networkKey]); const { isSeedRefValid } = useSeedRef(currentIdentity.encryptedSeed); const { unlockWithoutPassword } = useUnlockSeed(isSeedRefValid); diff --git a/src/stores/AccountsContext.ts b/src/stores/AccountsContext.ts index d49e06c59e..ce81c52abe 100644 --- a/src/stores/AccountsContext.ts +++ b/src/stores/AccountsContext.ts @@ -3,9 +3,9 @@ import { default as React, useEffect, useReducer } from 'react'; import { ETHEREUM_NETWORK_LIST, NetworkProtocols, - SUBSTRATE_NETWORK_LIST, UnknownNetworkKeys } from 'constants/networkSpecs'; +import { NetworksContextState } from 'stores/NetworkContext'; import { Account, AccountsStoreState, @@ -16,6 +16,7 @@ import { LockedAccount, UnlockedAccount } from 'types/identityTypes'; +import { NetworkParams, SubstrateNetworkParams } from 'types/networkTypes'; import { emptyAccount, generateAccountId } from 'utils/account'; import { deleteAccount as deleteDbAccount, @@ -58,24 +59,29 @@ export type AccountsContextState = { state: AccountsStoreState; select: (accountKey: string) => void; updateNew: (accountUpdate: Partial) => void; - submitNew: (pin: string) => Promise; + submitNew: ( + pin: string, + allNetworks: Map + ) => Promise; deriveEthereumAccount: ( createBrainWalletAddress: TryBrainWalletAddress, - networkKey: string + networkKey: string, + allNetworks: Map ) => Promise; updateSelectedAccount: (updatedAccount: Partial) => void; save: (accountKey: string, account: Account, pin?: string) => Promise; deleteAccount: (accountKey: string) => Promise; unlockAccount: (accountKey: string, pin: string) => Promise; lockAccount: (accountKey: string) => void; - getById: ({ - address, - networkKey - }: { - address: string; - networkKey: string; - }) => null | FoundAccount; - getAccountByAddress: (address: string) => false | FoundAccount; + getById: ( + address: string, + networkKey: string, + networkContext: NetworksContextState + ) => null | FoundAccount; + getAccountByAddress: ( + address: string, + networkContext: NetworksContextState + ) => false | FoundAccount; getSelected: () => Account | undefined; getIdentityByAccountId: (accountId: string) => Identity | undefined; resetCurrentIdentity: () => void; @@ -91,11 +97,11 @@ export type AccountsContextState = { deriveNewPath: ( newPath: string, createSubstrateAddress: TrySubstrateAddress, - networkKey: string, + networkParams: SubstrateNetworkParams, name: string, password: string ) => Promise; - deletePath: (path: string) => void; + deletePath: (path: string, networkContext: NetworksContextState) => void; deleteCurrentIdentity: () => Promise; }; @@ -173,11 +179,18 @@ export function useAccountContext(): AccountsContextState { } } - async function submitNew(pin: string): Promise { + async function submitNew( + pin: string, + allNetworks: Map + ): Promise { const account = state.newAccount; if (!account.seed) return; - const accountKey = generateAccountId(account); + const accountKey = generateAccountId( + account.address, + account.networkKey, + allNetworks + ); await save(accountKey, account, pin); setState({ @@ -219,7 +232,8 @@ export function useAccountContext(): AccountsContextState { async function deriveEthereumAccount( createBrainWalletAddress: TryBrainWalletAddress, - networkKey: string + networkKey: string, + allNetworks: Map ): Promise { const networkParams = ETHEREUM_NETWORK_LIST[networkKey]; const ethereumAddress = await brainWalletAddressWithRef( @@ -227,10 +241,11 @@ export function useAccountContext(): AccountsContextState { ); if (ethereumAddress.address === '') throw new Error(addressGenerateError); const { ethereumChainId } = networkParams; - const accountId = generateAccountId({ - address: ethereumAddress.address, - networkKey - }); + const accountId = generateAccountId( + ethereumAddress.address, + networkKey, + allNetworks + ); if (state.currentIdentity === null) throw new Error(emptyIdentityError); const updatedCurrentIdentity = deepCopyIdentity(state.currentIdentity); if (updatedCurrentIdentity.meta.has(ethereumChainId)) @@ -335,8 +350,10 @@ export function useAccountContext(): AccountsContextState { } function _getAccountFromIdentity( - accountIdOrAddress: string + accountIdOrAddress: string, + networkContext: NetworksContextState ): false | FoundIdentityAccount { + const { networks, allNetworks } = networkContext; const isAccountId = accountIdOrAddress.split(':').length > 1; let targetAccountId = null; let targetIdentity = null; @@ -345,13 +362,13 @@ export function useAccountContext(): AccountsContextState { for (const identity of state.identities) { const searchList = Array.from(identity.addresses.entries()); for (const [addressKey, path] of searchList) { - const networkKey = getNetworkKey(path, identity); + const networkKey = getNetworkKey(path, identity, networks); let accountId, address; if (isEthereumAccountId(addressKey)) { accountId = addressKey; address = extractAddressFromAccountId(addressKey); } else { - accountId = generateAccountId({ address: addressKey, networkKey }); + accountId = generateAccountId(addressKey, networkKey, allNetworks); address = addressKey; } const searchAccountIdOrAddress = isAccountId ? accountId : address; @@ -391,30 +408,33 @@ export function useAccountContext(): AccountsContextState { }; } - function getById({ - address, - networkKey - }: { - address: string; - networkKey: string; - }): null | FoundAccount { - const accountId = generateAccountId({ address, networkKey }); + function getById( + address: string, + networkKey: string, + networkContext: NetworksContextState + ): null | FoundAccount { + const { allNetworks } = networkContext; + const accountId = generateAccountId(address, networkKey, allNetworks); const legacyAccount = _getAccountWithoutCaseSensitive(accountId); if (legacyAccount) return parseFoundLegacyAccount(legacyAccount, accountId); let derivedAccount; //assume it is an accountId if (networkKey !== UnknownNetworkKeys.UNKNOWN) { - derivedAccount = _getAccountFromIdentity(accountId); + derivedAccount = _getAccountFromIdentity(accountId, networkContext); } //TODO backward support for user who has create account in known network for an unknown network. removed after offline network update - derivedAccount = derivedAccount || _getAccountFromIdentity(address); + derivedAccount = + derivedAccount || _getAccountFromIdentity(address, networkContext); if (derivedAccount instanceof Object) return { ...derivedAccount, isLegacy: false }; return null; } - function getAccountByAddress(address: string): false | FoundAccount { + function getAccountByAddress( + address: string, + networkContext: NetworksContextState + ): false | FoundAccount { if (!address) { return false; } @@ -424,7 +444,7 @@ export function useAccountContext(): AccountsContextState { return { ...v, accountId: k, isLegacy: true }; } } - return _getAccountFromIdentity(address); + return _getAccountFromIdentity(address, networkContext); } function getSelected(): Account | undefined { @@ -451,10 +471,10 @@ export function useAccountContext(): AccountsContextState { createSubstrateAddress: TrySubstrateAddress, updatedIdentity: Identity, name: string, - networkKey: string, + networkParams: SubstrateNetworkParams, password: string ): Promise { - const { prefix, pathId } = SUBSTRATE_NETWORK_LIST[networkKey]; + const { prefix, pathId } = networkParams; const suriSuffix = constructSuriSuffix({ derivePath: newPath, password @@ -534,7 +554,7 @@ export function useAccountContext(): AccountsContextState { async function deriveNewPath( newPath: string, createSubstrateAddress: TrySubstrateAddress, - networkKey: string, + networkParams: SubstrateNetworkParams, name: string, password: string ): Promise { @@ -544,19 +564,22 @@ export function useAccountContext(): AccountsContextState { createSubstrateAddress, updatedCurrentIdentity, name, - networkKey, + networkParams, password ); _updateCurrentIdentity(updatedCurrentIdentity); } - function deletePath(path: string): void { + function deletePath( + path: string, + networkContext: NetworksContextState + ): void { if (state.currentIdentity === null) throw new Error(emptyIdentityError); const updatedCurrentIdentity = deepCopyIdentity(state.currentIdentity); const pathMeta = updatedCurrentIdentity.meta.get(path)!; updatedCurrentIdentity.meta.delete(path); updatedCurrentIdentity.addresses.delete( - getAddressKeyByPath(path, pathMeta) + getAddressKeyByPath(path, pathMeta, networkContext) ); _updateCurrentIdentity(updatedCurrentIdentity); } diff --git a/src/stores/NetworkContext.ts b/src/stores/NetworkContext.ts index 7b2d3afae9..87e9a703ca 100644 --- a/src/stores/NetworkContext.ts +++ b/src/stores/NetworkContext.ts @@ -14,47 +14,66 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . -import { useEffect, useState } from 'react'; +import { default as React, useEffect, useMemo, useState } from 'react'; import { - SubstrateNetworkParams, - SubstrateNetworkBasics -} from 'types/networkTypes'; -import { - getCompleteSubstrateNetworkSpec, - checkNewNetworkSpecs -} from 'modules/network/utils'; + dummySubstrateNetworkParams, + ETHEREUM_NETWORK_LIST +} from 'constants/networkSpecs'; +import { SubstrateNetworkParams, NetworkParams } from 'types/networkTypes'; import { loadNetworks } from 'utils/db'; // https://github.com/polkadot-js/ui/blob/f2f36e2db07f5faec14ee43cf4295f5e8a6f3cfa/packages/reactnative-identicon/src/icons/Polkadot.tsx#L37. // we will need the generate function to be standardized to take an ss58 check address and isSixPoint boolean flag and returns a Circle https://github.com/polkadot-js/ui/blob/ff351a0f3160552f38e393b87fdf6e85051270de/packages/ui-shared/src/polkadotIcon.ts#L12. -type NetworkContextState = { - networkSpecs: Map; -}; - -const defaultState: NetworkContextState = { - networkSpecs: new Map() +export type GetNetwork = (networkKey: string) => NetworkParams; +export type GetSubstrateNetwork = ( + networkKey: string +) => SubstrateNetworkParams; +export type NetworksContextState = { + networks: Map; + allNetworks: Map; + getSubstrateNetwork: GetSubstrateNetwork; + getNetwork: GetNetwork; }; const deepCopy = ( networkSpecs: Array ): Array => JSON.parse(JSON.stringify(networkSpecs)); -export function useNetworksContext(): NetworkContextState { - const [networkSpecs, setNetworkSpecs] = useState< +export function useNetworksContext(): NetworksContextState { + const [substrateNetworks, setSubstrateNetworks] = useState< Map - >(defaultState.networkSpecs); + >(new Map()); + const allNetworks: Map = useMemo(() => { + const ethereumNetworks: Map = new Map( + Object.entries(ETHEREUM_NETWORK_LIST) + ); + const all = new Map([...ethereumNetworks, ...substrateNetworks]); + console.log('all is', all); + return all; + }, [substrateNetworks]); useEffect(() => { const refreshList = async function (): Promise { const initNetworkSpecs = await loadNetworks(); - setNetworkSpecs(initNetworkSpecs); + setSubstrateNetworks(initNetworkSpecs); + console.log('loaded Networks are:', initNetworkSpecs); }; refreshList(); }, []); + function getSubstrateNetworkParams( + networkKey: string + ): SubstrateNetworkParams { + return substrateNetworks.get(networkKey) || dummySubstrateNetworkParams; + } + + function getNetwork(networkKey: string): NetworkParams { + return allNetworks.get(networkKey) || dummySubstrateNetworkParams; + } + // async function submitNewNetworkSpec(): Promise { // if (newNetworkSpecs === null) // throw new Error('NetworkKey is not initialized.'); @@ -103,6 +122,11 @@ export function useNetworksContext(): NetworkContextState { // } return { - networkSpecs + allNetworks: allNetworks, + getNetwork, + getSubstrateNetwork: getSubstrateNetworkParams, + networks: substrateNetworks }; } + +export const NetworksContext = React.createContext({} as NetworksContextState); diff --git a/src/stores/RegistriesContext.ts b/src/stores/RegistriesContext.ts index 3fd2c30183..b30db8b11b 100644 --- a/src/stores/RegistriesContext.ts +++ b/src/stores/RegistriesContext.ts @@ -18,8 +18,8 @@ import { Metadata, TypeRegistry } from '@polkadot/types'; import { getSpecTypes } from '@polkadot/types-known'; import React, { useState } from 'react'; -import { SUBSTRATE_NETWORK_LIST } from 'constants/networkSpecs'; import { deepCopyMap } from 'stores/utils'; +import { SubstrateNetworkParams } from 'types/networkTypes'; import { getMetadata } from 'utils/identitiesUtils'; //Map PathId to Polkadot.js/api spec names and chain names @@ -72,18 +72,24 @@ export const getOverrideTypes = ( export type RegistriesStoreState = { registries: Map; - get: (networkKey: string) => TypeRegistry; + get: ( + networks: Map, + networkKey: string + ) => TypeRegistry; }; export function useRegistriesStore(): RegistriesStoreState { const dumbRegistry = new TypeRegistry(); const [registries, setRegistries] = useState(new Map()); - function get(networkKey: string): TypeRegistry { - if (!SUBSTRATE_NETWORK_LIST.hasOwnProperty(networkKey)) return dumbRegistry; + function get( + networks: Map, + networkKey: string + ): TypeRegistry { + if (!networks.has(networkKey)) return dumbRegistry; if (registries.has(networkKey)) return registries.get(networkKey)!; - const networkParams = SUBSTRATE_NETWORK_LIST[networkKey]; + const networkParams = networks.get(networkKey)!; const newRegistry = new TypeRegistry(); const networkMetadataRaw = getMetadata(networkKey); const overrideTypes = getOverrideTypes(newRegistry, networkParams.pathId); diff --git a/src/stores/ScannerContext.ts b/src/stores/ScannerContext.ts index faf0bbe8f0..d0f90f197f 100644 --- a/src/stores/ScannerContext.ts +++ b/src/stores/ScannerContext.ts @@ -27,8 +27,13 @@ import React, { useReducer } from 'react'; import { AccountsContextState } from 'stores/AccountsContext'; import { NETWORK_LIST } from 'constants/networkSpecs'; +import { GetNetwork, NetworksContextState } from 'stores/NetworkContext'; import { Account, FoundAccount } from 'types/identityTypes'; -import { isEthereumNetworkParams } from 'types/networkTypes'; +import { + isEthereumNetworkParams, + NetworkParams, + SubstrateNetworkParams +} from 'types/networkTypes'; import { CompletedParsedData, EthereumParsedData, @@ -48,7 +53,6 @@ import { constructDataFromBytes, encodeNumber } from 'utils/decoders'; -import { getNetworkParams } from 'utils/identitiesUtils'; import { brainWalletSign, decryptData, @@ -97,11 +101,13 @@ export type ScannerContextState = { setPartData: ( currentFrame: number, frameCount: number, - partData: string + partData: string, + networkContext: NetworksContextState ) => Promise; setData: ( accountsStore: AccountsContextState, - unsignedData: CompletedParsedData + unsignedData: CompletedParsedData, + networkContext: NetworksContextState ) => Promise; signEthereumData: ( signFunction: TryBrainWalletSignFunc, @@ -112,7 +118,7 @@ export type ScannerContextState = { suriSuffix: string, qrInfo: QrInfo ) => Promise; - signDataLegacy: (pin: string) => Promise; + signDataLegacy: (pin: string, getNetwork: GetNetwork) => Promise; }; const DEFAULT_STATE: ScannerStoreState = { @@ -165,7 +171,8 @@ export function useScannerContext(): ScannerContextState { async function _integrateMultiPartData( multipartData: Array, - totalFrameCount: number + totalFrameCount: number, + networkContext: NetworksContextState ): Promise { // concatenate all the parts into one binary blob let concatMultipartData = multipartData!.reduce( @@ -189,7 +196,8 @@ export function useScannerContext(): ScannerContextState { const parsedData = (await constructDataFromBytes( concatMultipartData, - true + true, + networkContext.networks )) as SubstrateCompletedParsedData; return parsedData; @@ -198,7 +206,8 @@ export function useScannerContext(): ScannerContextState { async function setPartData( currentFrame: number, frameCount: number, - partData: string + partData: string, + networkContext: NetworksContextState ): Promise { const newArray = new Array(frameCount).fill(null); const totalFrameCount = frameCount; @@ -248,7 +257,11 @@ export function useScannerContext(): ScannerContextState { !multipartComplete ) { // all the frames are filled - return await _integrateMultiPartData(nextDataState, totalFrameCount); + return await _integrateMultiPartData( + nextDataState, + totalFrameCount, + networkContext + ); } return { completedFramesCount: nextCompletedFramesCount, @@ -274,10 +287,12 @@ export function useScannerContext(): ScannerContextState { async function _setTXRequest( txRequest: EthereumParsedData | SubstrateTransactionParsedData, - accountsStore: AccountsContextState + accountsStore: AccountsContextState, + networkContext: NetworksContextState ): Promise { setBusy(); + const { allNetworks, getNetwork } = networkContext; const isOversized = (txRequest as SubstrateCompletedParsedData)?.oversized || false; @@ -310,12 +325,13 @@ export function useScannerContext(): ScannerContextState { dataToSign = payloadU8a.subarray(offset); } - const sender = await accountsStore.getById({ - address: txRequest.data.account, - networkKey - }); + const sender = await accountsStore.getById( + txRequest.data.account, + networkKey, + networkContext + ); - const networkTitle = getNetworkParams(networkKey).title; + const networkTitle = getNetwork(networkKey).title; if (!sender) { throw new Error( @@ -324,10 +340,11 @@ export function useScannerContext(): ScannerContextState { } const recipient = - (await accountsStore.getById({ - address: recipientAddress, - networkKey - })) || emptyAccount(recipientAddress, networkKey); + (await accountsStore.getById( + recipientAddress, + networkKey, + networkContext + )) || emptyAccount(recipientAddress, networkKey); const qrInfo: TxQRInfo = { dataToSign: dataToSign as string, @@ -345,7 +362,8 @@ export function useScannerContext(): ScannerContextState { async function _setDataToSign( signRequest: SubstrateMessageParsedData | EthereumParsedData, - accountsStore: AccountsContextState + accountsStore: AccountsContextState, + networkContext: NetworksContextState ): Promise { setBusy(); @@ -367,7 +385,7 @@ export function useScannerContext(): ScannerContextState { dataToSign = await ethSign(message.toString()); } - const sender = accountsStore.getAccountByAddress(address); + const sender = accountsStore.getAccountByAddress(address, networkContext); if (!sender) { throw new Error( `No private key found for ${address} in your signer key storage.` @@ -390,14 +408,23 @@ export function useScannerContext(): ScannerContextState { async function setData( accountsStore: AccountsContextState, - unsignedData: CompletedParsedData + unsignedData: CompletedParsedData, + networkContext: NetworksContextState ): Promise { if (unsignedData !== null) { switch (unsignedData.action) { case 'signTransaction': - return await _setTXRequest(unsignedData, accountsStore); + return await _setTXRequest( + unsignedData, + accountsStore, + networkContext + ); case 'signData': - return await _setDataToSign(unsignedData, accountsStore); + return await _setDataToSign( + unsignedData, + accountsStore, + networkContext + ); default: throw new Error( 'Scanned QR should contain either transaction or a message to sign' @@ -453,11 +480,14 @@ export function useScannerContext(): ScannerContextState { } // signing data with legacy account. - async function signDataLegacy(pin = '1'): Promise { + async function signDataLegacy( + pin = '1', + getNetwork: GetNetwork + ): Promise { const { sender, dataToSign, isHash } = state; if (!sender || !sender.encryptedSeed) throw new Error('Signing Error: sender could not be found.'); - const networkParams = getNetworkParams(sender.networkKey); + const networkParams = getNetwork(sender.networkKey); const isEthereum = isEthereumNetworkParams(networkParams); const seed = await decryptData(sender.encryptedSeed, pin); let signedData; diff --git a/src/stores/TxContext.ts b/src/stores/TxContext.ts index deb4eefcff..1fdbe28919 100644 --- a/src/stores/TxContext.ts +++ b/src/stores/TxContext.ts @@ -17,28 +17,38 @@ import React, { useState } from 'react'; import { deepCopyMap } from 'stores/utils'; +import { NetworkParams } from 'types/networkTypes'; import { loadAccountTxs, saveTx as saveTxDB } from 'utils/db'; import { TxParticipant } from 'types/tx'; type TxContextState = { - saveTx: (tx: any) => Promise; + saveTx: (tx: any, allNetworks: Map) => Promise; getTxList: ({ address }: { address: string }) => string[]; - loadTxsForAccount: (account: TxParticipant) => Promise; + loadTxsForAccount: ( + account: TxParticipant, + allNetworks: Map + ) => Promise; signedTxs: Map>; }; export function useTxStore(): TxContextState { const [signedTxs, setSignedTxs] = useState(new Map()); - async function saveTx(tx: any): Promise { - await saveTxDB(tx); + async function saveTx( + tx: any, + allNetworks: Map + ): Promise { + await saveTxDB(tx, allNetworks); const newSignedTxs = deepCopyMap(signedTxs); signedTxs.set(tx.hash, tx); setSignedTxs(newSignedTxs); } - async function loadTxsForAccount(account: TxParticipant): Promise { - const txs = await loadAccountTxs(account); + async function loadTxsForAccount( + account: TxParticipant, + allNetworks: Map + ): Promise { + const txs = await loadAccountTxs(account, allNetworks); const newSignedTxs = new Map([...signedTxs, ...txs]); setSignedTxs(newSignedTxs); } diff --git a/src/types/networkTypes.ts b/src/types/networkTypes.ts index e26d208946..c3bda9519c 100644 --- a/src/types/networkTypes.ts +++ b/src/types/networkTypes.ts @@ -79,6 +79,9 @@ export type UnknownNetworkParams = { title: string; }; +export type Networks = Map; +export type SubstrateNetworks = Map; + export function isSubstrateNetworkParams( networkParams: | SubstrateNetworkParams diff --git a/src/utils/account.ts b/src/utils/account.ts index f1881f1eee..a0e72cf011 100644 --- a/src/utils/account.ts +++ b/src/utils/account.ts @@ -19,27 +19,27 @@ import { UnlockedAccount } from 'types/identityTypes'; import { EthereumNetworkParams, isSubstrateNetworkParams, - isUnknownNetworkParams + isUnknownNetworkParams, + NetworkParams } from 'types/networkTypes'; import { ValidSeed } from 'types/utilTypes'; -import { getNetworkParams } from 'utils/identitiesUtils'; -export function generateAccountId({ - address, - networkKey -}: { - address: string; - networkKey: string; -}): string { +export function generateAccountId( + address: string, + networkKey: string, + allNetworks: Map +): string { if (typeof address !== 'string' || address.length === 0 || !networkKey) { throw new Error( "Couldn't create an accountId. Address or networkKey missing, or network key was invalid." ); } - const networkParams = getNetworkParams(networkKey); - const { protocol } = networkParams; + const networkParams = allNetworks.get(networkKey); + if (networkParams === undefined) + return `substrate:${address}:${networkKey ?? ''}`; + const { protocol } = networkParams; if (isSubstrateNetworkParams(networkParams)) { const { genesisHash } = networkParams; return `${protocol}:${address}:${genesisHash ?? ''}`; diff --git a/src/utils/db.ts b/src/utils/db.ts index 2d181c5935..36caab045a 100644 --- a/src/utils/db.ts +++ b/src/utils/db.ts @@ -23,10 +23,10 @@ import { deserializeIdentities, serializeIdentities } from './identitiesUtils'; import { deserializeNetworks, mergeNetworks } from 'utils/networksUtils'; import { SUBSTRATE_NETWORK_LIST } from 'constants/networkSpecs'; import { + NetworkParams, SubstrateNetworkBasics, SubstrateNetworkParams } from 'types/networkTypes'; -import { defaultNetworkSpecs } from 'modules/network/utils'; import { Account, Identity } from 'types/identityTypes'; import { Tx, TxParticipant } from 'types/tx'; @@ -122,7 +122,7 @@ export async function loadNetworks(): Promise< currentNetworkStorageLabel, networkStorage ); - if (!networksJson) return new Map(); + if (!networksJson) return new Map(Object.entries(SUBSTRATE_NETWORK_LIST)); const networksEntries = JSON.parse(networksJson); return mergeNetworks(SUBSTRATE_NETWORK_LIST, networksEntries); } catch (e) { @@ -153,14 +153,12 @@ export async function saveToCAndPPConfirmation(): Promise { * ======================================== */ -function accountTxsKey({ - address, - networkKey -}: { - address: string; - networkKey: string; -}): string { - return 'account_txs_' + generateAccountId({ address, networkKey }); +function accountTxsKey( + address: string, + networkKey: string, + allNetworks: Map +): string { + return 'account_txs_' + generateAccountId(address, networkKey, allNetworks); } function txKey(hash: string): string { @@ -192,7 +190,10 @@ async function storagePushValue(key: string, value: string): Promise { } } -export async function saveTx(tx: Tx): Promise { +export async function saveTx( + tx: Tx, + allNetworks: Map +): Promise { if (!tx.sender) { throw new Error('Tx should contain sender to save'); } @@ -202,24 +203,34 @@ export async function saveTx(tx: Tx): Promise { } await Promise.all([ - storagePushValue(accountTxsKey(tx.sender), tx.hash), - storagePushValue(accountTxsKey(tx.recipient), tx.hash), + storagePushValue( + accountTxsKey(tx.sender.address, tx.sender.networkKey, allNetworks), + tx.hash + ), + storagePushValue( + accountTxsKey(tx.recipient.address, tx.sender.networkKey, allNetworks), + tx.hash + ), AsyncStorage.setItem(txKey(tx.hash), JSON.stringify(tx)) ]); } export async function loadAccountTxHashes( - account: TxParticipant + account: TxParticipant, + allNetworks: Map ): Promise { - const result = await AsyncStorage.getItem(accountTxsKey(account)); + const result = await AsyncStorage.getItem( + accountTxsKey(account.address, account.networkKey, allNetworks) + ); return result ? JSON.parse(result) : []; } export async function loadAccountTxs( - account: TxParticipant + account: TxParticipant, + allNetworks: Map ): Promise> { - const hashes = await loadAccountTxHashes(account); + const hashes = await loadAccountTxHashes(account, allNetworks); return ( await AsyncStorage.multiGet(hashes.map(txKey)) diff --git a/src/utils/decoders.ts b/src/utils/decoders.ts index 7c2de4a09c..1081bafe51 100644 --- a/src/utils/decoders.ts +++ b/src/utils/decoders.ts @@ -22,7 +22,9 @@ import { } from '@polkadot/util'; import { encodeAddress } from '@polkadot/util-crypto'; -import { SUBSTRATE_NETWORK_LIST } from 'constants/networkSpecs'; +import strings from 'modules/sign/strings'; +import { NetworksContextState } from 'stores/NetworkContext'; +import { NetworkParams, SubstrateNetworkParams } from 'types/networkTypes'; import { EthereumParsedData, ParsedData, @@ -103,7 +105,8 @@ export function rawDataToU8A(rawData: string): Uint8Array | null { export async function constructDataFromBytes( bytes: Uint8Array, - multipartComplete = false + multipartComplete = false, + networks: Map ): Promise { const frameInfo = hexStripPrefix(u8aToHex(bytes.slice(0, 5))); const frameCount = parseInt(frameInfo.substr(2, 4), 16); @@ -175,11 +178,9 @@ export async function constructDataFromBytes( const rawPayload = hexToU8a(hexPayload); data.data.genesisHash = genesisHash; const isOversized = rawPayload.length > 256; - const network = SUBSTRATE_NETWORK_LIST[genesisHash]; + const network = networks.get(genesisHash); if (!network) { - throw new Error( - `Signer does not currently support a chain with genesis hash: ${genesisHash}` - ); + throw new Error(strings.ERROR_NO_NETWORK); } switch (secondByte) { diff --git a/src/utils/identitiesUtils.ts b/src/utils/identitiesUtils.ts index 96e0ce55bf..35893241e2 100644 --- a/src/utils/identitiesUtils.ts +++ b/src/utils/identitiesUtils.ts @@ -19,12 +19,14 @@ import { decryptData, substrateAddress } from './native'; import { constructSURI, parseSURI } from './suri'; import { generateAccountId } from './account'; -import { NetworkParams } from 'types/networkTypes'; +import { NetworksContextState } from 'stores/NetworkContext'; +import strings from 'modules/sign/strings'; +import { NetworkParams, SubstrateNetworkParams } from 'types/networkTypes'; import { TryCreateFunc } from 'utils/seedRefHooks'; import { + ETHEREUM_NETWORK_LIST, NETWORK_LIST, PATH_IDS_LIST, - SUBSTRATE_NETWORK_LIST, SubstrateNetworkKeys, UnknownNetworkKeys, unknownNetworkPathId @@ -104,15 +106,18 @@ export const extractAddressFromAccountId = (id: string): string => { export const getAddressKeyByPath = ( path: string, - pathMeta: AccountMeta + pathMeta: AccountMeta, + networkContext: NetworksContextState ): string => { + const { networks, allNetworks } = networkContext; const address = pathMeta.address; return isSubstratePath(path) ? address - : generateAccountId({ + : generateAccountId( address, - networkKey: getNetworkKeyByPath(path, pathMeta) - }); + getNetworkKeyByPath(path, pathMeta, networks), + allNetworks + ); }; export function emptyIdentity(): Identity { @@ -170,11 +175,13 @@ export const deepCopyIdentity = (identity: Identity): Identity => export const getPathsWithSubstrateNetworkKey = ( identity: Identity, - networkKey: string + networkKey: string, + networks: Map ): string[] => { const pathEntries = Array.from(identity.meta.entries()); - const targetPathId = - SUBSTRATE_NETWORK_LIST[networkKey]?.pathId || unknownNetworkPathId; + const targetPathId = networks.has(networkKey) + ? networks.get(networkKey)!.pathId + : unknownNetworkPathId; const pathReducer = ( groupedPaths: string[], [path, pathMeta]: [string, AccountMeta] @@ -197,33 +204,45 @@ export const getPathsWithSubstrateNetworkKey = ( return pathEntries.reduce(pathReducer, []); }; -export const getNetworkKeyByPathId = (pathId: string): string => { - const networkKeyIndex = Object.values(SUBSTRATE_NETWORK_LIST).findIndex( - networkParams => networkParams.pathId === pathId +export const getSubstrateNetworkKeyByPathId = ( + pathId: string, + networks: Map +): string => { + const networkKeyIndex = Object.entries(networks).findIndex( + ([, networkParams]) => networkParams.pathId === pathId ); - if (networkKeyIndex !== -1) - return Object.keys(SUBSTRATE_NETWORK_LIST)[networkKeyIndex]; + if (networkKeyIndex !== -1) { + const findNetworkEntry: [string, SubstrateNetworkParams] = Object.entries( + networks + )[networkKeyIndex]; + return findNetworkEntry[0]; + } return UnknownNetworkKeys.UNKNOWN; }; -export const getNetworkKey = (path: string, identity: Identity): string => { +export const getNetworkKey = ( + path: string, + identity: Identity, + networks: Map +): string => { if (identity.meta.has(path)) { - return getNetworkKeyByPath(path, identity.meta.get(path)!); + return getNetworkKeyByPath(path, identity.meta.get(path)!, networks); } return UnknownNetworkKeys.UNKNOWN; }; export const getNetworkKeyByPath = ( path: string, - pathMeta: AccountMeta + pathMeta: AccountMeta, + networks: Map ): string => { - if (!isSubstratePath(path) && NETWORK_LIST.hasOwnProperty(path)) { + if (!isSubstratePath(path) && ETHEREUM_NETWORK_LIST.hasOwnProperty(path)) { //It is a ethereum path return path; } const pathId = pathMeta.networkPathId || extractPathId(path); - return getNetworkKeyByPathId(pathId); + return getSubstrateNetworkKeyByPathId(pathId, networks); }; export const parseFoundLegacyAccount = ( @@ -282,36 +301,32 @@ export const verifyPassword = async ( password: string, seedPhrase: string, identity: Identity, - path: string + path: string, + networks: Map ): Promise => { const suri = constructSURI({ derivePath: path, password: password, phrase: seedPhrase }); - const networkKey = getNetworkKey(path, identity); - const networkParams = SUBSTRATE_NETWORK_LIST[networkKey]; + const networkKey = getNetworkKey(path, identity, networks); + const networkParams = networks.get(networkKey); + if (!networkParams) throw new Error(strings.ERROR_NO_NETWORK); const address = await substrateAddress(suri, networkParams.prefix); const accountMeta = identity.meta.get(path); return address === accountMeta?.address; }; -export const getNetworkParams = ( - networkKey: string | undefined -): NetworkParams => { - if (networkKey === undefined) return NETWORK_LIST[UnknownNetworkKeys.UNKNOWN]; - return NETWORK_LIST.hasOwnProperty(networkKey) - ? NETWORK_LIST[networkKey] - : NETWORK_LIST[UnknownNetworkKeys.UNKNOWN]; -}; - -export const getExistedNetworkKeys = (identity: Identity): string[] => { +export const getExistedNetworkKeys = ( + identity: Identity, + networks: Map +): string[] => { const pathEntries = Array.from(identity.meta.entries()); const networkKeysSet = pathEntries.reduce( (networksSet, [path, pathMeta]: [string, AccountMeta]) => { let networkKey; if (isSubstratePath(path)) { - networkKey = getNetworkKeyByPath(path, pathMeta); + networkKey = getNetworkKeyByPath(path, pathMeta, networks); } else { networkKey = path; } @@ -391,7 +406,10 @@ const _comparePathsInGroup = (a: string, b: string): number => { * If the network is known: group by the second subpath, e.g. '//staking' of '//kusama//staking/0' * Please refer to identitiesUtils.spec.js for more examples. **/ -export const groupPaths = (paths: string[]): PathGroup[] => { +export const groupPaths = ( + paths: string[], + networks: Map +): PathGroup[] => { const insertPathIntoGroup = ( matchingPath: string, fullPath: string, @@ -419,10 +437,10 @@ export const groupPaths = (paths: string[]): PathGroup[] => { const rootPath = path.match(pathsRegex.firstPath)?.[0]; if (rootPath === undefined) return groupedPath; - const networkParams = Object.values(SUBSTRATE_NETWORK_LIST).find( - v => `//${v.pathId}` === rootPath + const networkEntry = Object.entries(networks).find( + ([, v]) => `//${v.pathId}` === rootPath ); - if (networkParams === undefined) { + if (networkEntry === undefined) { insertPathIntoGroup(path, path, groupedPath); return groupedPath; } @@ -431,7 +449,7 @@ export const groupPaths = (paths: string[]): PathGroup[] => { if (isRootPath) { groupedPath.push({ paths: [path], - title: `${networkParams.title} root` + title: `${networkEntry[1].title} root` }); return groupedPath; } diff --git a/src/utils/migrationUtils.ts b/src/utils/migrationUtils.ts index bc4e246bf6..d2d6edee27 100644 --- a/src/utils/migrationUtils.ts +++ b/src/utils/migrationUtils.ts @@ -14,6 +14,7 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . +import { NETWORK_LIST } from 'constants/networkSpecs'; import { Account, AccountMeta, @@ -111,8 +112,12 @@ export const migrateAccounts = async (): Promise => { ); accounts.forEach((account: Account): void => { + const allNetworks = new Map(Object.entries(NETWORK_LIST)); try { - saveAccount(generateAccountId(account), account); + saveAccount( + generateAccountId(account.address, account.networkKey, allNetworks), + account + ); } catch (e) { console.error(e); } diff --git a/src/utils/networksUtils.ts b/src/utils/networksUtils.ts index 533f2f6680..edaa33bde6 100644 --- a/src/utils/networksUtils.ts +++ b/src/utils/networksUtils.ts @@ -1,5 +1,4 @@ import { - NetworkParams, SubstrateNetworkBasics, SubstrateNetworkParams } from 'types/networkTypes'; @@ -34,8 +33,7 @@ export const mergeNetworks = ( const mergedNetworksObject = newNetworksEntries.reduce( ( acc, - [networkKey, networkParams], - index + [networkKey, networkParams] ): Record => { if (!defaultNetworks.hasOwnProperty(networkKey)) { acc[networkKey] = { diff --git a/test/unit/specs/util/decoders.spec.ts b/test/unit/specs/util/decoders.spec.ts index afba118b70..2155e104f2 100644 --- a/test/unit/specs/util/decoders.spec.ts +++ b/test/unit/specs/util/decoders.spec.ts @@ -179,7 +179,12 @@ describe('decoders', () => { describe('UOS parsing', () => { it('from Substrate UOS Payload Mortal', async () => { - const unsignedData = await constructDataFromBytes(SIGN_TX_TEST); + const networks = new Map(Object.entries(SUBSTRATE_NETWORK_LIST)); + const unsignedData = await constructDataFromBytes( + SIGN_TX_TEST, + false, + networks + ); const rawPayload = (unsignedData as SubstrateCompletedParsedData).data .data; diff --git a/test/unit/specs/util/identitiesUtils.spec.ts b/test/unit/specs/util/identitiesUtils.spec.ts index 4efadccce8..38a421cfac 100644 --- a/test/unit/specs/util/identitiesUtils.spec.ts +++ b/test/unit/specs/util/identitiesUtils.spec.ts @@ -26,10 +26,15 @@ import { } from 'utils/identitiesUtils'; import { EthereumNetworkKeys, + NETWORK_LIST, + SUBSTRATE_NETWORK_LIST, SubstrateNetworkKeys, UnknownNetworkKeys } from 'constants/networkSpecs'; +const networks = new Map(Object.entries(SUBSTRATE_NETWORK_LIST)); +const allNetworks = new Map(Object.entries(NETWORK_LIST)); + const raw = [ { address: 'addressDefault', @@ -167,7 +172,7 @@ describe('IdentitiesUtils', () => { }); it('regroup the kusama paths', () => { - const groupResult = groupPaths(kusamaPaths); + const groupResult = groupPaths(kusamaPaths, networks); expect(groupResult).toEqual([ { paths: ['//kusama'], @@ -201,7 +206,7 @@ describe('IdentitiesUtils', () => { '/kusama/1', '/polkadot_test/1' ]; - const groupResult = groupPaths(unKnownPaths); + const groupResult = groupPaths(unKnownPaths, networks); expect(groupResult).toEqual([ { paths: [''], @@ -234,7 +239,7 @@ describe('IdentitiesUtils', () => { }); it('get the correspond networkKeys', () => { - const networkKeys = getExistedNetworkKeys(testIdentities[0]); + const networkKeys = getExistedNetworkKeys(testIdentities[0], networks); expect(networkKeys).toEqual([ EthereumNetworkKeys.FRONTIER, SubstrateNetworkKeys.KUSAMA, @@ -246,7 +251,11 @@ describe('IdentitiesUtils', () => { it('get networkKey correctly by path', () => { const getNetworkKeyByPathTest = (path: string): string => { - return getNetworkKeyByPath(path, testIdentities[0].meta.get(path)); + return getNetworkKeyByPath( + path, + testIdentities[0].meta.get(path), + networks + ); }; expect(getNetworkKeyByPathTest('')).toEqual(UnknownNetworkKeys.UNKNOWN); expect(getNetworkKeyByPathTest('//kusama')).toEqual( @@ -263,7 +272,7 @@ describe('IdentitiesUtils', () => { it('group path under their network correctly, has no missing accounts', () => { const mockIdentity = testIdentities[0]; - const existedNetworks = getExistedNetworkKeys(mockIdentity); + const existedNetworks = getExistedNetworkKeys(mockIdentity, networks); const existedAccountsSize = mockIdentity.meta.size; const allListedAccounts = existedNetworks.reduce( @@ -277,7 +286,8 @@ describe('IdentitiesUtils', () => { } else { const networkAccounts = getPathsWithSubstrateNetworkKey( mockIdentity, - networkKey + networkKey, + networks ); return acc.concat(networkAccounts); } From ab0845f07834c210bc99529f28e90fcd0bca3f92 Mon Sep 17 00:00:00 2001 From: Hanwen Cheng Date: Tue, 1 Sep 2020 11:33:30 +0200 Subject: [PATCH 04/11] inject network context part 2 --- src/components/DerivationNetworkSelector.tsx | 2 +- src/components/PathCard.tsx | 12 +++++------- src/components/ScreenHeading.tsx | 6 ++++-- src/modules/main/components/NetworkSelector.tsx | 4 ++-- src/modules/network/screens/NetworkSettings.tsx | 4 ++-- src/modules/network/utils.ts | 6 +++--- src/modules/sign/utils.ts | 3 ++- src/screens/AccountNew.tsx | 7 +++++-- src/screens/LegacyNetworkChooser.tsx | 5 +++-- src/screens/PathsList.tsx | 2 +- src/stores/ScannerContext.ts | 13 +++++++------ src/utils/identitiesUtils.ts | 11 +++++------ src/utils/networksUtils.ts | 2 +- 13 files changed, 41 insertions(+), 36 deletions(-) diff --git a/src/components/DerivationNetworkSelector.tsx b/src/components/DerivationNetworkSelector.tsx index 51564abb29..92aa35f5db 100644 --- a/src/components/DerivationNetworkSelector.tsx +++ b/src/components/DerivationNetworkSelector.tsx @@ -81,7 +81,7 @@ export function NetworkOptions({ setVisible(false); }; - const menuOptions = Object.entries(networks) + const menuOptions = Array.from(networks.entries()) .filter(([networkKey]) => !excludedNetworks.includes(networkKey)) .map(([networkKey, networkParams]) => { return ( diff --git a/src/components/PathCard.tsx b/src/components/PathCard.tsx index 45a1480205..deddd5908d 100644 --- a/src/components/PathCard.tsx +++ b/src/components/PathCard.tsx @@ -65,7 +65,7 @@ export default function PathCard({ testID?: string; titlePrefix?: string; }): React.ReactElement { - const { networks } = useContext(NetworksContext); + const { networks, allNetworks } = useContext(NetworksContext); const isNotEmptyName = name && name !== ''; const pathName = isNotEmptyName ? name : getPathName(path, identity); const { isSeedRefValid, substrateAddress } = useSeedRef( @@ -78,10 +78,8 @@ export default function PathCard({ const getAddress = async (): Promise => { const existedAddress = getAddressWithPath(path, identity); if (existedAddress !== '') return setAddress(existedAddress); - if (isSeedRefValid && isPathValid) { - const prefix = (NETWORK_LIST[ - computedNetworkKey - ] as SubstrateNetworkParams).prefix; + if (isSeedRefValid && isPathValid && networks.has(computedNetworkKey)) { + const prefix = networks.get(computedNetworkKey)!.prefix; const generatedAddress = await substrateAddress(path, prefix); return setAddress(generatedAddress); } @@ -101,9 +99,9 @@ export default function PathCard({ const isUnknownAddress = address === ''; const hasPassword = identity.meta.get(path)?.hasPassword ?? false; const networkParams = - computedNetworkKey === UnknownNetworkKeys.UNKNOWN && !isUnknownAddress + computedNetworkKey === UnknownNetworkKeys.UNKNOWN && !isUnknownAddress && !allNetworks.has(computedNetworkKey) ? NETWORK_LIST[defaultNetworkKey] - : NETWORK_LIST[computedNetworkKey]; + : allNetworks.get(computedNetworkKey)!; const nonSubstrateCard = ( <> diff --git a/src/components/ScreenHeading.tsx b/src/components/ScreenHeading.tsx index c9b174c9d0..554cde435c 100644 --- a/src/components/ScreenHeading.tsx +++ b/src/components/ScreenHeading.tsx @@ -14,10 +14,11 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . -import React, { ReactElement, ReactNode } from 'react'; +import React, {ReactElement, ReactNode, useContext} from 'react'; import { View, StyleSheet, Text, ViewStyle, TextStyle } from 'react-native'; import AntIcon from 'react-native-vector-icons/AntDesign'; import { Icon } from 'react-native-elements'; +import {NetworksContext} from 'stores/NetworkContext'; import ButtonIcon from './ButtonIcon'; import AccountIcon from './AccountIcon'; @@ -115,6 +116,7 @@ export function LeftScreenHeading({ ...baseStyles.text, ...baseStyles.t_left }; + const {getNetwork} = useContext(NetworksContext); const isDisabled = onPress === undefined; return ( diff --git a/src/modules/main/components/NetworkSelector.tsx b/src/modules/main/components/NetworkSelector.tsx index 4a084c367f..13c9601b05 100644 --- a/src/modules/main/components/NetworkSelector.tsx +++ b/src/modules/main/components/NetworkSelector.tsx @@ -18,7 +18,7 @@ import React, { ReactElement, useContext, useMemo, useState } from 'react'; import { BackHandler, FlatList, FlatListProps } from 'react-native'; import { useFocusEffect } from '@react-navigation/native'; -import { filterSubstrateNetworks } from 'modules/network/utils'; +import { filterNetworks } from 'modules/network/utils'; import { NetworkCard } from 'components/AccountCard'; import { SafeAreaViewContainer } from 'components/SafeAreaContainer'; import ScreenHeading, { IdentityHeading } from 'components/ScreenHeading'; @@ -187,7 +187,7 @@ function NetworkSelector({ const networkList = useMemo( () => - filterSubstrateNetworks(networks, (networkKey, shouldExclude) => { + filterNetworks(allNetworks, (networkKey, shouldExclude) => { if (isNew && !shouldExclude) return true; if (shouldShowMoreNetworks) { diff --git a/src/modules/network/screens/NetworkSettings.tsx b/src/modules/network/screens/NetworkSettings.tsx index f695bda9a3..763ed02053 100644 --- a/src/modules/network/screens/NetworkSettings.tsx +++ b/src/modules/network/screens/NetworkSettings.tsx @@ -18,7 +18,7 @@ import React, { ReactElement, useContext } from 'react'; import { FlatList, StyleSheet } from 'react-native'; import { NetworkCard } from 'components/AccountCard'; -import { filterSubstrateNetworks } from 'modules/network/utils'; +import { filterNetworks } from 'modules/network/utils'; import { SafeAreaViewContainer } from 'components/SafeAreaContainer'; import { NetworksContext } from 'stores/NetworkContext'; import { NetworkParams, SubstrateNetworkParams } from 'types/networkTypes'; @@ -31,7 +31,7 @@ export default function NetworkSettings({ navigation }: NavigationProps<'NetworkSettings'>): React.ReactElement { const { networks } = useContext(NetworksContext); - const networkParams = filterSubstrateNetworks(networks) as Array< + const networkParams = filterNetworks(networks) as Array< [string, SubstrateNetworkParams] >; const renderNetwork = ({ diff --git a/src/modules/network/utils.ts b/src/modules/network/utils.ts index 67556aa27b..7c2ebf8d98 100644 --- a/src/modules/network/utils.ts +++ b/src/modules/network/utils.ts @@ -27,8 +27,8 @@ import { SubstrateNetworkParams } from 'types/networkTypes'; -export const filterSubstrateNetworks = ( - networkList: Map, +export const filterNetworks = ( + networkList: Map, extraFilter?: (networkKey: string, shouldExclude: boolean) => boolean ): Array<[string, NetworkParams]> => { const excludedNetworks = [UnknownNetworkKeys.UNKNOWN]; @@ -43,7 +43,7 @@ export const filterSubstrateNetworks = ( return extraFilter(networkKey, shouldExclude); return !shouldExclude; }; - return Object.entries(networkList) + return Array.from(networkList.entries()) .filter(filterNetworkKeys) .sort((a, b) => a[1].order - b[1].order); }; diff --git a/src/modules/sign/utils.ts b/src/modules/sign/utils.ts index 2713d0e0e3..28b7213019 100644 --- a/src/modules/sign/utils.ts +++ b/src/modules/sign/utils.ts @@ -164,7 +164,8 @@ export function useProcessBarCode( await scannerStore.signSubstrateData( seedRef.trySubstrateSign.bind(seedRef), suriSuffix, - qrInfo + qrInfo, + networks ); } } diff --git a/src/screens/AccountNew.tsx b/src/screens/AccountNew.tsx index ce3efcd574..8e6c416391 100644 --- a/src/screens/AccountNew.tsx +++ b/src/screens/AccountNew.tsx @@ -17,8 +17,9 @@ import React, { useContext, useEffect, useReducer } from 'react'; import { StyleSheet, Text, View } from 'react-native'; -import { NETWORK_LIST, NetworkProtocols } from 'constants/networkSpecs'; +import { NetworkProtocols } from 'constants/networkSpecs'; import { AccountsContext } from 'stores/AccountsContext'; +import {NetworksContext} from 'stores/NetworkContext'; import { Account, UnlockedAccount } from 'types/identityTypes'; import { NetworkParams } from 'types/networkTypes'; import { NavigationProps } from 'types/props'; @@ -47,6 +48,8 @@ export default function AccountNew({ navigation }: NavigationProps<'AccountNew'>): React.ReactElement { const accountsStore = useContext(AccountsContext); + const {getNetwork} = useContext(NetworksContext); + const initialState = { derivationPassword: '', derivationPath: '', @@ -67,7 +70,7 @@ export default function AccountNew({ useEffect((): void => { const selectedAccount = accountsStore.state.newAccount; - const selectedNetwork = NETWORK_LIST[selectedAccount.networkKey]; + const selectedNetwork = getNetwork(selectedAccount.networkKey); updateState({ selectedAccount, selectedNetwork diff --git a/src/screens/LegacyNetworkChooser.tsx b/src/screens/LegacyNetworkChooser.tsx index d9973dd742..d40f11b4d1 100644 --- a/src/screens/LegacyNetworkChooser.tsx +++ b/src/screens/LegacyNetworkChooser.tsx @@ -20,10 +20,10 @@ import { StyleSheet, Text } from 'react-native'; import { AccountsContext } from 'stores/AccountsContext'; import { SafeAreaScrollViewContainer } from 'components/SafeAreaContainer'; import { - NETWORK_LIST, UnknownNetworkKeys, SubstrateNetworkKeys } from 'constants/networkSpecs'; +import {NetworksContext} from 'stores/NetworkContext'; import { NetworkParams } from 'types/networkTypes'; import { NavigationProps } from 'types/props'; import colors from 'styles/colors'; @@ -35,6 +35,7 @@ export default function LegacyNetworkChooserView({ navigation }: NavigationProps<'LegacyNetworkChooser'>): React.ReactElement { const accountsStore = useContext(AccountsContext); + const {allNetworks} = useContext(NetworksContext); const excludedNetworks = [UnknownNetworkKeys.UNKNOWN]; if (!__DEV__) { @@ -45,7 +46,7 @@ export default function LegacyNetworkChooserView({ return ( CHOOSE NETWORK - {Object.entries(NETWORK_LIST) + {Array.from(allNetworks.entries()) .filter( ([networkKey]: [string, any]): boolean => !excludedNetworks.includes(networkKey) diff --git a/src/screens/PathsList.tsx b/src/screens/PathsList.tsx index c4d894a59f..8bdf646597 100644 --- a/src/screens/PathsList.tsx +++ b/src/screens/PathsList.tsx @@ -25,7 +25,7 @@ import PathGroupCard from 'components/PathGroupCard'; import { useUnlockSeed } from 'utils/navigationHelpers'; import { useSeedRef } from 'utils/seedRefHooks'; import { SafeAreaViewContainer } from 'components/SafeAreaContainer'; -import { NETWORK_LIST, UnknownNetworkKeys } from 'constants/networkSpecs'; +import { UnknownNetworkKeys } from 'constants/networkSpecs'; import testIDs from 'e2e/testIDs'; import { isEthereumNetworkParams, diff --git a/src/stores/ScannerContext.ts b/src/stores/ScannerContext.ts index d0f90f197f..213b145602 100644 --- a/src/stores/ScannerContext.ts +++ b/src/stores/ScannerContext.ts @@ -26,12 +26,11 @@ import { import React, { useReducer } from 'react'; import { AccountsContextState } from 'stores/AccountsContext'; -import { NETWORK_LIST } from 'constants/networkSpecs'; +import {ETHEREUM_NETWORK_LIST} from 'constants/networkSpecs'; import { GetNetwork, NetworksContextState } from 'stores/NetworkContext'; import { Account, FoundAccount } from 'types/identityTypes'; import { isEthereumNetworkParams, - NetworkParams, SubstrateNetworkParams } from 'types/networkTypes'; import { @@ -116,7 +115,8 @@ export type ScannerContextState = { signSubstrateData: ( signFunction: TrySignFunc, suriSuffix: string, - qrInfo: QrInfo + qrInfo: QrInfo, + networks: Map ) => Promise; signDataLegacy: (pin: string, getNetwork: GetNetwork) => Promise; }; @@ -443,7 +443,7 @@ export function useScannerContext(): ScannerContextState { qrInfo: QrInfo ): Promise { const { dataToSign, sender } = qrInfo; - if (!sender || !NETWORK_LIST.hasOwnProperty(sender.networkKey)) + if (!sender || !ETHEREUM_NETWORK_LIST.hasOwnProperty(sender.networkKey)) throw new Error('Signing Error: sender could not be found.'); const signedData = await signFunction(dataToSign as string); setState({ signedData }); @@ -453,10 +453,11 @@ export function useScannerContext(): ScannerContextState { async function signSubstrateData( signFunction: TrySignFunc, suriSuffix: string, - qrInfo: QrInfo + qrInfo: QrInfo, + networks: Map ): Promise { const { dataToSign, isHash, sender } = qrInfo; - if (!sender || !NETWORK_LIST.hasOwnProperty(sender.networkKey)) + if (!sender || !networks.has(sender.networkKey)) throw new Error('Signing Error: sender could not be found.'); let signable; diff --git a/src/utils/identitiesUtils.ts b/src/utils/identitiesUtils.ts index 35893241e2..ee13149f8d 100644 --- a/src/utils/identitiesUtils.ts +++ b/src/utils/identitiesUtils.ts @@ -21,11 +21,10 @@ import { generateAccountId } from './account'; import { NetworksContextState } from 'stores/NetworkContext'; import strings from 'modules/sign/strings'; -import { NetworkParams, SubstrateNetworkParams } from 'types/networkTypes'; +import { SubstrateNetworkParams } from 'types/networkTypes'; import { TryCreateFunc } from 'utils/seedRefHooks'; import { ETHEREUM_NETWORK_LIST, - NETWORK_LIST, PATH_IDS_LIST, SubstrateNetworkKeys, UnknownNetworkKeys, @@ -208,12 +207,12 @@ export const getSubstrateNetworkKeyByPathId = ( pathId: string, networks: Map ): string => { - const networkKeyIndex = Object.entries(networks).findIndex( + const networkKeyIndex = Array.from(networks.entries()).findIndex( ([, networkParams]) => networkParams.pathId === pathId ); if (networkKeyIndex !== -1) { - const findNetworkEntry: [string, SubstrateNetworkParams] = Object.entries( - networks + const findNetworkEntry: [string, SubstrateNetworkParams] = Array.from( + networks.entries() )[networkKeyIndex]; return findNetworkEntry[0]; } @@ -437,7 +436,7 @@ export const groupPaths = ( const rootPath = path.match(pathsRegex.firstPath)?.[0]; if (rootPath === undefined) return groupedPath; - const networkEntry = Object.entries(networks).find( + const networkEntry = Array.from(networks.entries()).find( ([, v]) => `//${v.pathId}` === rootPath ); if (networkEntry === undefined) { diff --git a/src/utils/networksUtils.ts b/src/utils/networksUtils.ts index edaa33bde6..d00c599421 100644 --- a/src/utils/networksUtils.ts +++ b/src/utils/networksUtils.ts @@ -6,7 +6,7 @@ import { export const serializeNetworks = ( networks: Map ): string => { - const networksEntries = Object.entries(networks); + const networksEntries = Array.from(networks.entries()); return JSON.stringify(networksEntries); }; From 3813466d73dad47ab4f7631011a43d361c381a69 Mon Sep 17 00:00:00 2001 From: Hanwen Cheng Date: Tue, 1 Sep 2020 12:00:32 +0200 Subject: [PATCH 05/11] make lint happy --- src/components/PathCard.tsx | 4 +++- src/components/ScreenHeading.tsx | 6 +++--- src/screens/AccountNew.tsx | 4 ++-- src/screens/LegacyNetworkChooser.tsx | 4 ++-- src/stores/ScannerContext.ts | 2 +- 5 files changed, 11 insertions(+), 9 deletions(-) diff --git a/src/components/PathCard.tsx b/src/components/PathCard.tsx index deddd5908d..f2e05b3dff 100644 --- a/src/components/PathCard.tsx +++ b/src/components/PathCard.tsx @@ -99,7 +99,9 @@ export default function PathCard({ const isUnknownAddress = address === ''; const hasPassword = identity.meta.get(path)?.hasPassword ?? false; const networkParams = - computedNetworkKey === UnknownNetworkKeys.UNKNOWN && !isUnknownAddress && !allNetworks.has(computedNetworkKey) + computedNetworkKey === UnknownNetworkKeys.UNKNOWN && + !isUnknownAddress && + !allNetworks.has(computedNetworkKey) ? NETWORK_LIST[defaultNetworkKey] : allNetworks.get(computedNetworkKey)!; diff --git a/src/components/ScreenHeading.tsx b/src/components/ScreenHeading.tsx index 554cde435c..7dea2d41f1 100644 --- a/src/components/ScreenHeading.tsx +++ b/src/components/ScreenHeading.tsx @@ -14,15 +14,15 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . -import React, {ReactElement, ReactNode, useContext} from 'react'; +import React, { ReactElement, ReactNode, useContext } from 'react'; import { View, StyleSheet, Text, ViewStyle, TextStyle } from 'react-native'; import AntIcon from 'react-native-vector-icons/AntDesign'; import { Icon } from 'react-native-elements'; -import {NetworksContext} from 'stores/NetworkContext'; import ButtonIcon from './ButtonIcon'; import AccountIcon from './AccountIcon'; +import { NetworksContext } from 'stores/NetworkContext'; import TouchableItem from 'components/TouchableItem'; import testIDs from 'e2e/testIDs'; import { NETWORK_LIST } from 'constants/networkSpecs'; @@ -116,7 +116,7 @@ export function LeftScreenHeading({ ...baseStyles.text, ...baseStyles.t_left }; - const {getNetwork} = useContext(NetworksContext); + const { getNetwork } = useContext(NetworksContext); const isDisabled = onPress === undefined; return ( ): React.ReactElement { const accountsStore = useContext(AccountsContext); - const {getNetwork} = useContext(NetworksContext); + const { getNetwork } = useContext(NetworksContext); const initialState = { derivationPassword: '', diff --git a/src/screens/LegacyNetworkChooser.tsx b/src/screens/LegacyNetworkChooser.tsx index d40f11b4d1..0fb2832e64 100644 --- a/src/screens/LegacyNetworkChooser.tsx +++ b/src/screens/LegacyNetworkChooser.tsx @@ -23,7 +23,7 @@ import { UnknownNetworkKeys, SubstrateNetworkKeys } from 'constants/networkSpecs'; -import {NetworksContext} from 'stores/NetworkContext'; +import { NetworksContext } from 'stores/NetworkContext'; import { NetworkParams } from 'types/networkTypes'; import { NavigationProps } from 'types/props'; import colors from 'styles/colors'; @@ -35,7 +35,7 @@ export default function LegacyNetworkChooserView({ navigation }: NavigationProps<'LegacyNetworkChooser'>): React.ReactElement { const accountsStore = useContext(AccountsContext); - const {allNetworks} = useContext(NetworksContext); + const { allNetworks } = useContext(NetworksContext); const excludedNetworks = [UnknownNetworkKeys.UNKNOWN]; if (!__DEV__) { diff --git a/src/stores/ScannerContext.ts b/src/stores/ScannerContext.ts index 213b145602..0a4ac42c61 100644 --- a/src/stores/ScannerContext.ts +++ b/src/stores/ScannerContext.ts @@ -26,7 +26,7 @@ import { import React, { useReducer } from 'react'; import { AccountsContextState } from 'stores/AccountsContext'; -import {ETHEREUM_NETWORK_LIST} from 'constants/networkSpecs'; +import { ETHEREUM_NETWORK_LIST } from 'constants/networkSpecs'; import { GetNetwork, NetworksContextState } from 'stores/NetworkContext'; import { Account, FoundAccount } from 'types/identityTypes'; import { From dc064a14406e7d9b9cb056b4f3f9fc15e6298e2c Mon Sep 17 00:00:00 2001 From: Hanwen Cheng Date: Tue, 1 Sep 2020 12:47:35 +0200 Subject: [PATCH 06/11] add pathsIds into networkContext --- src/components/PathCard.tsx | 6 ++- src/constants/networkSpecs.ts | 8 ---- .../main/components/NetworkSelector.tsx | 7 ++-- src/screens/PathDerivation.tsx | 7 ++-- src/screens/PathDetails.tsx | 6 +-- src/screens/PathSecret.tsx | 8 +++- src/screens/PathsList.tsx | 5 ++- src/stores/AccountsContext.ts | 4 +- src/stores/NetworkContext.ts | 15 ++++++- src/utils/identitiesUtils.ts | 39 +++++++++++-------- test/unit/specs/util/identitiesUtils.spec.ts | 35 ++++++++++++++--- 11 files changed, 91 insertions(+), 49 deletions(-) diff --git a/src/components/PathCard.tsx b/src/components/PathCard.tsx index f2e05b3dff..f5d01ecdcd 100644 --- a/src/components/PathCard.tsx +++ b/src/components/PathCard.tsx @@ -65,7 +65,8 @@ export default function PathCard({ testID?: string; titlePrefix?: string; }): React.ReactElement { - const { networks, allNetworks } = useContext(NetworksContext); + const networksContext = useContext(NetworksContext); + const { networks, allNetworks } = networksContext; const isNotEmptyName = name && name !== ''; const pathName = isNotEmptyName ? name : getPathName(path, identity); const { isSeedRefValid, substrateAddress } = useSeedRef( @@ -73,7 +74,8 @@ export default function PathCard({ ); const [address, setAddress] = useState(''); const computedNetworkKey = - networkKey || getNetworkKeyByPath(path, identity.meta.get(path)!, networks); + networkKey || + getNetworkKeyByPath(path, identity.meta.get(path)!, networksContext); useEffect(() => { const getAddress = async (): Promise => { const existedAddress = getAddressWithPath(path, identity); diff --git a/src/constants/networkSpecs.ts b/src/constants/networkSpecs.ts index 36c21d223c..b811565b79 100644 --- a/src/constants/networkSpecs.ts +++ b/src/constants/networkSpecs.ts @@ -292,14 +292,6 @@ export const UNKNOWN_NETWORK: Record< UnknownNetworkParams > = Object.freeze(unknownNetworkBase); -const substrateNetworkMetas = Object.values({ - ...SUBSTRATE_NETWORK_LIST, - ...UNKNOWN_NETWORK -}); -export const PATH_IDS_LIST = substrateNetworkMetas.map( - (meta: UnknownNetworkParams | SubstrateNetworkParams) => meta.pathId -); - export const NETWORK_LIST: Record = Object.freeze( Object.assign( {}, diff --git a/src/modules/main/components/NetworkSelector.tsx b/src/modules/main/components/NetworkSelector.tsx index 13c9601b05..04b5bb0ada 100644 --- a/src/modules/main/components/NetworkSelector.tsx +++ b/src/modules/main/components/NetworkSelector.tsx @@ -53,9 +53,8 @@ function NetworkSelector({ const isNew = route.params?.isNew ?? false; const [shouldShowMoreNetworks, setShouldShowMoreNetworks] = useState(false); const { identities, currentIdentity } = accountsStore.state; - const { networks, getSubstrateNetwork, allNetworks } = useContext( - NetworksContext - ); + const networkContextState = useContext(NetworksContext); + const { networks, getSubstrateNetwork, allNetworks } = networkContextState; const seedRefHooks = useSeedRef(currentIdentity.encryptedSeed); const { unlockWithoutPassword } = useUnlockSeed(seedRefHooks.isSeedRefValid); @@ -181,7 +180,7 @@ function NetworkSelector({ }; const availableNetworks = useMemo( - () => getExistedNetworkKeys(currentIdentity, networks), + () => getExistedNetworkKeys(currentIdentity, networkContextState), [currentIdentity, networks] ); diff --git a/src/screens/PathDerivation.tsx b/src/screens/PathDerivation.tsx index ccc2900262..3c1f2b0add 100644 --- a/src/screens/PathDerivation.tsx +++ b/src/screens/PathDerivation.tsx @@ -53,7 +53,8 @@ function PathDerivation({ const [keyPairsName, setKeyPairsName] = useState(''); const [modalVisible, setModalVisible] = useState(false); const [password, setPassword] = useState(''); - const { networks, getSubstrateNetwork } = useContext(NetworksContext); + const networkContextState = useContext(NetworksContext); + const { networks, getSubstrateNetwork, pathIds } = networkContextState; const pathNameInput = useRef(null); const { setAlert } = useContext(AlertStateContext); const currentIdentity = accountsStore.state.currentIdentity; @@ -67,9 +68,9 @@ function PathDerivation({ currentIdentity: Identity ): string { if (currentIdentity.meta.has(parentPath)) { - return getNetworkKey(parentPath, currentIdentity, networks); + return getNetworkKey(parentPath, currentIdentity, networkContextState); } - const pathId = extractPathId(parentPath); + const pathId = extractPathId(parentPath, pathIds); return getSubstrateNetworkKeyByPathId(pathId, networks); } diff --git a/src/screens/PathDetails.tsx b/src/screens/PathDetails.tsx index ded6845471..9fca27f2ae 100644 --- a/src/screens/PathDetails.tsx +++ b/src/screens/PathDetails.tsx @@ -98,7 +98,7 @@ export function PathDetailsView({ const listedPaths = getPathsWithSubstrateNetworkKey( accountsStore.state.currentIdentity!, networkKey, - networks + networksContextState ); const hasOtherPaths = listedPaths.length > 0; hasOtherPaths @@ -186,11 +186,11 @@ function PathDetails({ route }: NavigationAccountIdentityProps<'PathDetails'>): React.ReactElement { const path = route.params.path; - const { networks } = useContext(NetworksContext); + const networksContextState = useContext(NetworksContext); const networkKey = getNetworkKey( path, accountsStore.state.currentIdentity, - networks + networksContextState ); return ( ): React.ReactElement { - const { networks } = useContext(NetworksContext); + const networksContextState = useContext(NetworksContext); const { currentIdentity } = accountsStore.state; const [secret, setSecret] = useState(''); const { substrateSecret, isSeedRefValid } = useSeedRef( @@ -45,7 +45,11 @@ function PathSecret({ useEffect(() => { const getAndSetSecret = async (): Promise => { - const networkKey = getNetworkKey(path, currentIdentity, networks); + const networkKey = getNetworkKey( + path, + currentIdentity, + networksContextState + ); const password = route.params.password ?? ''; const accountName = getPathName(path, currentIdentity); const generatedSecret = await substrateSecret(`${path}///${password}`); diff --git a/src/screens/PathsList.tsx b/src/screens/PathsList.tsx index 8bdf646597..cd677d4c8a 100644 --- a/src/screens/PathsList.tsx +++ b/src/screens/PathsList.tsx @@ -48,7 +48,8 @@ function PathsList({ route }: NavigationAccountIdentityProps<'PathsList'>): React.ReactElement { const networkKey = route.params.networkKey ?? UnknownNetworkKeys.UNKNOWN; - const { networks, getNetwork } = useContext(NetworksContext); + const networkContextState = useContext(NetworksContext); + const { networks, getNetwork } = networkContextState; const networkParams = getNetwork(networkKey); const { currentIdentity } = accountsStore.state; @@ -59,7 +60,7 @@ function PathsList({ const listedPaths = getPathsWithSubstrateNetworkKey( currentIdentity, networkKey, - networks + networkContextState ); return groupPaths(listedPaths, networks); }, [currentIdentity, isEthereumPath, networkKey]); diff --git a/src/stores/AccountsContext.ts b/src/stores/AccountsContext.ts index ce81c52abe..fd8c7755a6 100644 --- a/src/stores/AccountsContext.ts +++ b/src/stores/AccountsContext.ts @@ -353,7 +353,7 @@ export function useAccountContext(): AccountsContextState { accountIdOrAddress: string, networkContext: NetworksContextState ): false | FoundIdentityAccount { - const { networks, allNetworks } = networkContext; + const { allNetworks } = networkContext; const isAccountId = accountIdOrAddress.split(':').length > 1; let targetAccountId = null; let targetIdentity = null; @@ -362,7 +362,7 @@ export function useAccountContext(): AccountsContextState { for (const identity of state.identities) { const searchList = Array.from(identity.addresses.entries()); for (const [addressKey, path] of searchList) { - const networkKey = getNetworkKey(path, identity, networks); + const networkKey = getNetworkKey(path, identity, networkContext); let accountId, address; if (isEthereumAccountId(addressKey)) { accountId = addressKey; diff --git a/src/stores/NetworkContext.ts b/src/stores/NetworkContext.ts index 87e9a703ca..947a3c8e5d 100644 --- a/src/stores/NetworkContext.ts +++ b/src/stores/NetworkContext.ts @@ -18,7 +18,8 @@ import { default as React, useEffect, useMemo, useState } from 'react'; import { dummySubstrateNetworkParams, - ETHEREUM_NETWORK_LIST + ETHEREUM_NETWORK_LIST, + unknownNetworkPathId } from 'constants/networkSpecs'; import { SubstrateNetworkParams, NetworkParams } from 'types/networkTypes'; import { loadNetworks } from 'utils/db'; @@ -36,6 +37,7 @@ export type NetworksContextState = { allNetworks: Map; getSubstrateNetwork: GetSubstrateNetwork; getNetwork: GetNetwork; + pathIds: string[]; }; const deepCopy = ( @@ -55,6 +57,14 @@ export function useNetworksContext(): NetworksContextState { return all; }, [substrateNetworks]); + const pathIds = useMemo(() => { + const result = Array.from(substrateNetworks.values()) + .map(n => n.pathId) + .concat([unknownNetworkPathId]); + console.log('path ids are', result); + return result; + }, [substrateNetworks]); + useEffect(() => { const refreshList = async function (): Promise { const initNetworkSpecs = await loadNetworks(); @@ -125,7 +135,8 @@ export function useNetworksContext(): NetworksContextState { allNetworks: allNetworks, getNetwork, getSubstrateNetwork: getSubstrateNetworkParams, - networks: substrateNetworks + networks: substrateNetworks, + pathIds }; } diff --git a/src/utils/identitiesUtils.ts b/src/utils/identitiesUtils.ts index ee13149f8d..85c473bd80 100644 --- a/src/utils/identitiesUtils.ts +++ b/src/utils/identitiesUtils.ts @@ -25,7 +25,6 @@ import { SubstrateNetworkParams } from 'types/networkTypes'; import { TryCreateFunc } from 'utils/seedRefHooks'; import { ETHEREUM_NETWORK_LIST, - PATH_IDS_LIST, SubstrateNetworkKeys, UnknownNetworkKeys, unknownNetworkPathId @@ -61,14 +60,15 @@ export function isLegacyFoundAccount( return foundAccount.isLegacy; } -export const extractPathId = (path: string): string => { +export const extractPathId = (path: string, pathIds: string[]): string => { const matchNetworkPath = path.match(pathsRegex.networkPath); if (matchNetworkPath && matchNetworkPath[0]) { const targetPathId = removeSlash(matchNetworkPath[0]); - if (PATH_IDS_LIST.includes(targetPathId)) { + if (pathIds.includes(targetPathId)) { return targetPathId; } } + debugger; return unknownNetworkPathId; }; @@ -108,13 +108,13 @@ export const getAddressKeyByPath = ( pathMeta: AccountMeta, networkContext: NetworksContextState ): string => { - const { networks, allNetworks } = networkContext; + const { allNetworks } = networkContext; const address = pathMeta.address; return isSubstratePath(path) ? address : generateAccountId( address, - getNetworkKeyByPath(path, pathMeta, networks), + getNetworkKeyByPath(path, pathMeta, networkContext), allNetworks ); }; @@ -175,8 +175,9 @@ export const deepCopyIdentity = (identity: Identity): Identity => export const getPathsWithSubstrateNetworkKey = ( identity: Identity, networkKey: string, - networks: Map + networkContextState: NetworksContextState ): string[] => { + const { networks, pathIds } = networkContextState; const pathEntries = Array.from(identity.meta.entries()); const targetPathId = networks.has(networkKey) ? networks.get(networkKey)!.pathId @@ -188,11 +189,11 @@ export const getPathsWithSubstrateNetworkKey = ( let pathId; if (!isSubstratePath(path)) return groupedPaths; if (pathMeta.networkPathId !== undefined) { - pathId = PATH_IDS_LIST.includes(pathMeta.networkPathId) + pathId = pathIds.includes(pathMeta.networkPathId) ? pathMeta.networkPathId : unknownNetworkPathId; } else { - pathId = extractPathId(path); + pathId = extractPathId(path, pathIds); } if (pathId === targetPathId) { groupedPaths.push(path); @@ -222,10 +223,14 @@ export const getSubstrateNetworkKeyByPathId = ( export const getNetworkKey = ( path: string, identity: Identity, - networks: Map + networkContextState: NetworksContextState ): string => { if (identity.meta.has(path)) { - return getNetworkKeyByPath(path, identity.meta.get(path)!, networks); + return getNetworkKeyByPath( + path, + identity.meta.get(path)!, + networkContextState + ); } return UnknownNetworkKeys.UNKNOWN; }; @@ -233,13 +238,14 @@ export const getNetworkKey = ( export const getNetworkKeyByPath = ( path: string, pathMeta: AccountMeta, - networks: Map + networkContextState: NetworksContextState ): string => { + const { networks, pathIds } = networkContextState; if (!isSubstratePath(path) && ETHEREUM_NETWORK_LIST.hasOwnProperty(path)) { //It is a ethereum path return path; } - const pathId = pathMeta.networkPathId || extractPathId(path); + const pathId = pathMeta.networkPathId || extractPathId(path, pathIds); return getSubstrateNetworkKeyByPathId(pathId, networks); }; @@ -301,14 +307,15 @@ export const verifyPassword = async ( seedPhrase: string, identity: Identity, path: string, - networks: Map + networkContextState: NetworksContextState ): Promise => { + const { networks } = networkContextState; const suri = constructSURI({ derivePath: path, password: password, phrase: seedPhrase }); - const networkKey = getNetworkKey(path, identity, networks); + const networkKey = getNetworkKey(path, identity, networkContextState); const networkParams = networks.get(networkKey); if (!networkParams) throw new Error(strings.ERROR_NO_NETWORK); const address = await substrateAddress(suri, networkParams.prefix); @@ -318,14 +325,14 @@ export const verifyPassword = async ( export const getExistedNetworkKeys = ( identity: Identity, - networks: Map + networkContextState: NetworksContextState ): string[] => { const pathEntries = Array.from(identity.meta.entries()); const networkKeysSet = pathEntries.reduce( (networksSet, [path, pathMeta]: [string, AccountMeta]) => { let networkKey; if (isSubstratePath(path)) { - networkKey = getNetworkKeyByPath(path, pathMeta, networks); + networkKey = getNetworkKeyByPath(path, pathMeta, networkContextState); } else { networkKey = path; } diff --git a/test/unit/specs/util/identitiesUtils.spec.ts b/test/unit/specs/util/identitiesUtils.spec.ts index 38a421cfac..83f8a81fd3 100644 --- a/test/unit/specs/util/identitiesUtils.spec.ts +++ b/test/unit/specs/util/identitiesUtils.spec.ts @@ -14,6 +14,11 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . +import { + GetNetwork, + GetSubstrateNetwork, + NetworksContextState +} from 'stores/NetworkContext'; import { deserializeIdentities, getExistedNetworkKeys, @@ -29,11 +34,25 @@ import { NETWORK_LIST, SUBSTRATE_NETWORK_LIST, SubstrateNetworkKeys, - UnknownNetworkKeys + UnknownNetworkKeys, + unknownNetworkPathId } from 'constants/networkSpecs'; const networks = new Map(Object.entries(SUBSTRATE_NETWORK_LIST)); const allNetworks = new Map(Object.entries(NETWORK_LIST)); +const pathIds = Object.values(SUBSTRATE_NETWORK_LIST) + .map(n => n.pathId) + .concat(unknownNetworkPathId); +const getNetwork = allNetworks.get as GetNetwork; +const getSubstrateNetwork = networks.get as GetSubstrateNetwork; + +const dummyNetworkContext: NetworksContextState = { + allNetworks, + getNetwork, + getSubstrateNetwork, + networks, + pathIds +}; const raw = [ { @@ -239,7 +258,10 @@ describe('IdentitiesUtils', () => { }); it('get the correspond networkKeys', () => { - const networkKeys = getExistedNetworkKeys(testIdentities[0], networks); + const networkKeys = getExistedNetworkKeys( + testIdentities[0], + dummyNetworkContext + ); expect(networkKeys).toEqual([ EthereumNetworkKeys.FRONTIER, SubstrateNetworkKeys.KUSAMA, @@ -254,7 +276,7 @@ describe('IdentitiesUtils', () => { return getNetworkKeyByPath( path, testIdentities[0].meta.get(path), - networks + dummyNetworkContext ); }; expect(getNetworkKeyByPathTest('')).toEqual(UnknownNetworkKeys.UNKNOWN); @@ -272,7 +294,10 @@ describe('IdentitiesUtils', () => { it('group path under their network correctly, has no missing accounts', () => { const mockIdentity = testIdentities[0]; - const existedNetworks = getExistedNetworkKeys(mockIdentity, networks); + const existedNetworks = getExistedNetworkKeys( + mockIdentity, + dummyNetworkContext + ); const existedAccountsSize = mockIdentity.meta.size; const allListedAccounts = existedNetworks.reduce( @@ -287,7 +312,7 @@ describe('IdentitiesUtils', () => { const networkAccounts = getPathsWithSubstrateNetworkKey( mockIdentity, networkKey, - networks + dummyNetworkContext ); return acc.concat(networkAccounts); } From 094f59dc846cd60bd8094d1ec1ffe5d1a5744ff4 Mon Sep 17 00:00:00 2001 From: Hanwen Cheng Date: Tue, 1 Sep 2020 15:14:51 +0200 Subject: [PATCH 07/11] implement add network to app context function --- src/constants/networkSpecs.ts | 4 +- src/modules/network/utils.ts | 1 + src/modules/sign/screens/QrScanner.tsx | 50 +++++++++---- src/modules/sign/strings.ts | 4 +- src/modules/sign/utils.ts | 39 ++++++++-- src/stores/NetworkContext.ts | 78 ++++++-------------- src/types/networkTypes.ts | 2 + src/types/scannerTypes.ts | 23 +++++- src/utils/db.ts | 6 +- src/utils/identitiesUtils.ts | 1 - src/utils/navigationHelpers.ts | 6 ++ src/utils/networksUtils.ts | 36 +++++---- test/e2e/mockScanRequests.ts | 9 ++- test/e2e/specs/passwordedAccount.spec.ts | 2 +- test/unit/specs/util/identitiesUtils.spec.ts | 4 +- 15 files changed, 162 insertions(+), 103 deletions(-) diff --git a/src/constants/networkSpecs.ts b/src/constants/networkSpecs.ts index b811565b79..3755ad4ae2 100644 --- a/src/constants/networkSpecs.ts +++ b/src/constants/networkSpecs.ts @@ -70,7 +70,7 @@ export const SubstrateNetworkKeys: Record = Object.freeze({ WESTEND: '0xe143f23803ac50e8f6f8e62695d1ce9e4e1d68aa36c1cd2cfd15340213f3423e' }); -const unknownNetworkParams = { +export const unknownNetworkParams: UnknownNetworkParams = { color: colors.signal.error, order: 99, pathId: unknownNetworkPathId, @@ -83,6 +83,7 @@ const unknownNetworkParams = { export const dummySubstrateNetworkParams: SubstrateNetworkParams = { ...unknownNetworkParams, decimals: 12, + deleted: false, genesisHash: UnknownNetworkKeys.UNKNOWN, logo: require('res/img/logos/Substrate_Dev.png'), protocol: NetworkProtocols.SUBSTRATE, @@ -234,6 +235,7 @@ const ethereumDefaultValues = { const substrateDefaultValues = { color: '#4C4646', + deleted: false, logo: require('res/img/logos/Substrate_Dev.png'), protocol: NetworkProtocols.SUBSTRATE, secondaryColor: colors.background.card diff --git a/src/modules/network/utils.ts b/src/modules/network/utils.ts index 7c2ebf8d98..7672d165c6 100644 --- a/src/modules/network/utils.ts +++ b/src/modules/network/utils.ts @@ -80,6 +80,7 @@ export function getCompleteSubstrateNetworkSpec( const defaultNewNetworkSpecParams = { color: generateRandomColor(), decimals: defaultNetworkSpec.decimals, + deleted: false, logo: defaultNetworkSpec.logo, order: 102, //TODO prefix: defaultNetworkSpec.prefix, diff --git a/src/modules/sign/screens/QrScanner.tsx b/src/modules/sign/screens/QrScanner.tsx index de682d7707..eeddadaba4 100644 --- a/src/modules/sign/screens/QrScanner.tsx +++ b/src/modules/sign/screens/QrScanner.tsx @@ -30,10 +30,11 @@ import colors from 'styles/colors'; import fonts from 'styles/fonts'; import ScreenHeading from 'components/ScreenHeading'; import { Frames, TxRequestData } from 'types/scannerTypes'; +import { navigateToNetworkSettings } from 'utils/navigationHelpers'; -export default function Scanner({}: NavigationProps< - 'QrScanner' ->): React.ReactElement { +export default function Scanner({ + navigation +}: NavigationProps<'QrScanner'>): React.ReactElement { const scannerStore = useContext(ScannerContext); const networksContextState = useContext(NetworksContext); const { setAlert } = useContext(AlertStateContext); @@ -47,23 +48,40 @@ export default function Scanner({}: NavigationProps< missingFramesMessage: '', totalFramesCount: 0 }); - function showErrorMessage(title: string, message: string): void { + function showAlertMessage( + title: string, + message: string, + isSuccess?: boolean + ): void { + const clearByTap = async (): Promise => { + scannerStore.cleanup(); + scannerStore.setReady(); + setLastFrame(null); + setEnableScan(true); + }; setEnableScan(false); - setAlert(title, message, [ - { - onPress: async (): Promise => { - scannerStore.cleanup(); - scannerStore.setReady(); - setLastFrame(null); - setEnableScan(true); - }, - text: 'Try again' - } - ]); + if (isSuccess) { + setAlert(title, message, [ + { + onPress: clearByTap, + text: 'Try again' + } + ]); + } else { + setAlert(title, message, [ + { + onPress: async () => { + await clearByTap(); + navigateToNetworkSettings(navigation); + }, + text: 'Done' + } + ]); + } } const processBarCode = useProcessBarCode( - showErrorMessage, + showAlertMessage, networksContextState ); // useEffect((): (() => void) => { diff --git a/src/modules/sign/strings.ts b/src/modules/sign/strings.ts index 4a7d0000ed..0fc5ccaf4f 100644 --- a/src/modules/sign/strings.ts +++ b/src/modules/sign/strings.ts @@ -9,7 +9,9 @@ const strings = { ERROR_TITLE: 'Unable to parse QR data', INFO_ETH_TX: 'You are about to send the following amount', INFO_MULTI_PART: - 'You are about to send the following extrinsic. We will sign the hash of the payload as it is oversized.' + 'You are about to send the following extrinsic. We will sign the hash of the payload as it is oversized.', + SUCCESS_ADD_NETWORK: 'Successfully updated new network: ', + SUCCESS_TITLE: 'Success' }; export default strings; diff --git a/src/modules/sign/utils.ts b/src/modules/sign/utils.ts index 28b7213019..131c7c055f 100644 --- a/src/modules/sign/utils.ts +++ b/src/modules/sign/utils.ts @@ -27,10 +27,14 @@ import { isEthereumNetworkParams } from 'types/networkTypes'; import { RootStackParamList } from 'types/routes'; import { CompletedParsedData, + EthereumParsedData, isMultiFramesInfo, isMultipartData, + isNetworkParsedData, + NetworkParsedData, ParsedData, QrInfo, + SubstrateParsedData, TxRequestData } from 'types/scannerTypes'; import { @@ -57,7 +61,11 @@ function getSeedRef( } export function useProcessBarCode( - showErrorMessage: (title: string, message: string) => void, + showAlertMessage: ( + title: string, + message: string, + isSuccess?: boolean + ) => void, networksContextState: NetworksContextState ): (txRequestData: TxRequestData) => Promise { const { allNetworks, networks } = networksContextState; @@ -72,11 +80,20 @@ export function useProcessBarCode( async function parseQrData( txRequestData: TxRequestData ): Promise { + console.log('txrequest data is', txRequestData); if (isAddressString(txRequestData.data)) { throw new Error(strings.ERROR_ADDRESS_MESSAGE); } else if (isJsonString(txRequestData.data)) { + // Add Network + const parsedJsonData = JSON.parse(txRequestData.data); + if (parsedJsonData.hasOwnProperty('genesisHash')) { + return { + action: 'addNetwork', + data: parsedJsonData + } as NetworkParsedData; + } // Ethereum Legacy - return JSON.parse(txRequestData.data); + return parsedJsonData; } else if (!scannerStore.state.multipartComplete) { const strippedData = rawDataToU8A(txRequestData.rawData); if (strippedData === null) throw new Error(strings.ERROR_NO_RAW_DATA); @@ -92,7 +109,7 @@ export function useProcessBarCode( } async function checkMultiFramesData( - parsedData: ParsedData + parsedData: SubstrateParsedData | EthereumParsedData ): Promise { if (isMultipartData(parsedData)) { const multiFramesResult = await scannerStore.setPartData( @@ -173,7 +190,7 @@ export function useProcessBarCode( async function unlockAndNavigationToSignedQR(qrInfo: QrInfo): Promise { const { sender, type } = qrInfo; if (!sender) - return showErrorMessage( + return showAlertMessage( strings.ERROR_TITLE, strings.ERROR_NO_SENDER_FOUND ); @@ -201,9 +218,21 @@ export function useProcessBarCode( } } + function addNewNetwork(networkParsedData: NetworkParsedData) { + networksContextState.addNetwork(networkParsedData); + return showAlertMessage( + strings.SUCCESS_TITLE, + strings.SUCCESS_ADD_NETWORK + networkParsedData.data.title + ); + } + async function processBarCode(txRequestData: TxRequestData): Promise { try { const parsedData = await parseQrData(txRequestData); + if (isNetworkParsedData(parsedData)) { + addNewNetwork(parsedData); + return; + } const unsignedData = await checkMultiFramesData(parsedData); if (unsignedData === null) return; const qrInfo = await scannerStore.setData( @@ -214,7 +243,7 @@ export function useProcessBarCode( await unlockAndNavigationToSignedQR(qrInfo); scannerStore.clearMultipartProgress(); } catch (e) { - return showErrorMessage(strings.ERROR_TITLE, e.message); + return showAlertMessage(strings.ERROR_TITLE, e.message); } } diff --git a/src/stores/NetworkContext.ts b/src/stores/NetworkContext.ts index 947a3c8e5d..a87fc28910 100644 --- a/src/stores/NetworkContext.ts +++ b/src/stores/NetworkContext.ts @@ -19,10 +19,17 @@ import { default as React, useEffect, useMemo, useState } from 'react'; import { dummySubstrateNetworkParams, ETHEREUM_NETWORK_LIST, + UnknownNetworkKeys, + unknownNetworkParams, unknownNetworkPathId } from 'constants/networkSpecs'; import { SubstrateNetworkParams, NetworkParams } from 'types/networkTypes'; +import { NetworkParsedData } from 'types/scannerTypes'; import { loadNetworks } from 'utils/db'; +import { + deepCopyNetworks, + generateNetworkParamsFromParsedData +} from 'utils/networksUtils'; // https://github.com/polkadot-js/ui/blob/f2f36e2db07f5faec14ee43cf4295f5e8a6f3cfa/packages/reactnative-identicon/src/icons/Polkadot.tsx#L37. @@ -33,6 +40,7 @@ export type GetSubstrateNetwork = ( networkKey: string ) => SubstrateNetworkParams; export type NetworksContextState = { + addNetwork(networkParsedData: NetworkParsedData): void; networks: Map; allNetworks: Map; getSubstrateNetwork: GetSubstrateNetwork; @@ -40,10 +48,6 @@ export type NetworksContextState = { pathIds: string[]; }; -const deepCopy = ( - networkSpecs: Array -): Array => JSON.parse(JSON.stringify(networkSpecs)); - export function useNetworksContext(): NetworksContextState { const [substrateNetworks, setSubstrateNetworks] = useState< Map @@ -52,9 +56,11 @@ export function useNetworksContext(): NetworksContextState { const ethereumNetworks: Map = new Map( Object.entries(ETHEREUM_NETWORK_LIST) ); - const all = new Map([...ethereumNetworks, ...substrateNetworks]); - console.log('all is', all); - return all; + return new Map([ + ...ethereumNetworks, + ...substrateNetworks, + [UnknownNetworkKeys.UNKNOWN, unknownNetworkParams] + ]); }, [substrateNetworks]); const pathIds = useMemo(() => { @@ -84,55 +90,19 @@ export function useNetworksContext(): NetworksContextState { return allNetworks.get(networkKey) || dummySubstrateNetworkParams; } - // async function submitNewNetworkSpec(): Promise { - // if (newNetworkSpecs === null) - // throw new Error('NetworkKey is not initialized.'); - // - // checkNewNetworkSpecs(newNetworkSpecs); - // const updatedNetworkSpecs = deepCopy(networkSpecs); - // const networkIndex = updatedNetworkSpecs.findIndex( - // networkSpec => networkSpec.genesisHash === newNetworkSpecs.genesisHash - // ); - // const completeNetworkSpec = getCompleteSubstrateNetworkSpec( - // newNetworkSpecs - // ); - // if (networkIndex === -1) { - // updatedNetworkSpecs.push(completeNetworkSpec); - // } else { - // updatedNetworkSpecs.splice(networkIndex, 1, completeNetworkSpec); - // } - // - // setNetworkSpecs(updatedNetworkSpecs); - // setNewNetworkSpecs(defaultState.newNetworkSpecs); - // - // try { - // await saveNetworkSpecs(updatedNetworkSpecs); - // } catch (e) { - // //TODO give feedback to UI - // console.error(e); - // } - // } - // - // async function deleteNetwork(networkKey: string): Promise { - // const updatedNetworkSpecs = deepCopy(networkSpecs); - // const networkIndex = updatedNetworkSpecs.findIndex( - // networkSpec => networkSpec.genesisHash === networkKey - // ); - // if (networkIndex === -1) return; - // - // updatedNetworkSpecs.splice(networkIndex, 1); - // setNetworkSpecs(networkSpecs); - // - // try { - // await saveNetworkSpecs(updatedNetworkSpecs); - // } catch (e) { - // //TODO give feedback to UI - // console.error(e); - // } - // } + function addNetwork(networkParsedData: NetworkParsedData): void { + const newNetworkParams = generateNetworkParamsFromParsedData( + networkParsedData + ); + const networkKey = newNetworkParams.genesisHash; + const newNetworksList = deepCopyNetworks(substrateNetworks); + newNetworksList.set(networkKey, newNetworkParams); + setSubstrateNetworks(newNetworksList); + } return { - allNetworks: allNetworks, + addNetwork, + allNetworks, getNetwork, getSubstrateNetwork: getSubstrateNetworkParams, networks: substrateNetworks, diff --git a/src/types/networkTypes.ts b/src/types/networkTypes.ts index c3bda9519c..520a65a116 100644 --- a/src/types/networkTypes.ts +++ b/src/types/networkTypes.ts @@ -10,6 +10,7 @@ export type NetworkParams = export type SubstrateNetworkDefaultConstant = { color: string; decimals: number; + deleted?: boolean; genesisHash: string; logo?: number; order: number; @@ -38,6 +39,7 @@ export type SubstrateNetworkBasics = { export type SubstrateNetworkParams = { color: string; decimals: number; + deleted: boolean; genesisHash: string; logo: number; order: number; diff --git a/src/types/scannerTypes.ts b/src/types/scannerTypes.ts index a1df8468b1..fef122d5ae 100644 --- a/src/types/scannerTypes.ts +++ b/src/types/scannerTypes.ts @@ -30,7 +30,22 @@ export interface TxRequestData { target?: number; } -export type ParsedData = SubstrateParsedData | EthereumParsedData; +export type ParsedData = + | SubstrateParsedData + | EthereumParsedData + | NetworkParsedData; + +export type NetworkParsedData = { + action: 'addNetwork'; + data: { + color: string; + decimals: number; + genesisHash: string; + prefix: number; + title: string; + unit: string; + }; +}; export type EthereumParsedData = { data: { @@ -156,3 +171,9 @@ export function isMultipartData( (parsedData as SubstrateMultiParsedData)?.isMultipart || hasMultiFrames ); } + +export function isNetworkParsedData( + parsedData: ParsedData | null +): parsedData is NetworkParsedData { + return (parsedData as NetworkParsedData).action === 'addNetwork'; +} diff --git a/src/utils/db.ts b/src/utils/db.ts index 36caab045a..765fcb0ccb 100644 --- a/src/utils/db.ts +++ b/src/utils/db.ts @@ -22,11 +22,7 @@ import { deserializeIdentities, serializeIdentities } from './identitiesUtils'; import { deserializeNetworks, mergeNetworks } from 'utils/networksUtils'; import { SUBSTRATE_NETWORK_LIST } from 'constants/networkSpecs'; -import { - NetworkParams, - SubstrateNetworkBasics, - SubstrateNetworkParams -} from 'types/networkTypes'; +import { NetworkParams, SubstrateNetworkParams } from 'types/networkTypes'; import { Account, Identity } from 'types/identityTypes'; import { Tx, TxParticipant } from 'types/tx'; diff --git a/src/utils/identitiesUtils.ts b/src/utils/identitiesUtils.ts index 85c473bd80..63847d9ad8 100644 --- a/src/utils/identitiesUtils.ts +++ b/src/utils/identitiesUtils.ts @@ -68,7 +68,6 @@ export const extractPathId = (path: string, pathIds: string[]): string => { return targetPathId; } } - debugger; return unknownNetworkPathId; }; diff --git a/src/utils/navigationHelpers.ts b/src/utils/navigationHelpers.ts index 14938476ca..80e5c9009f 100644 --- a/src/utils/navigationHelpers.ts +++ b/src/utils/navigationHelpers.ts @@ -252,6 +252,12 @@ export const resetNavigationWithScanner = < navigation.dispatch(resetAction); }; +export const navigateToNetworkSettings = < + RouteName extends keyof RootStackParamList +>( + navigation: GenericNavigationProps +): void => resetNavigationWithNetworkChooser(navigation, 'NetworkSettings'); + export const navigateToPathsList = ( navigation: GenericNavigationProps, networkKey: string diff --git a/src/utils/networksUtils.ts b/src/utils/networksUtils.ts index d00c599421..e07eef6105 100644 --- a/src/utils/networksUtils.ts +++ b/src/utils/networksUtils.ts @@ -1,10 +1,9 @@ -import { - SubstrateNetworkBasics, - SubstrateNetworkParams -} from 'types/networkTypes'; +import colors from 'styles/colors'; +import { SubstrateNetworkParams } from 'types/networkTypes'; +import { NetworkParsedData } from 'types/scannerTypes'; export const serializeNetworks = ( - networks: Map + networks: Map ): string => { const networksEntries = Array.from(networks.entries()); return JSON.stringify(networksEntries); @@ -12,20 +11,16 @@ export const serializeNetworks = ( export const deserializeNetworks = ( networksJson: string -): Map => { +): Map => { const networksEntries = JSON.parse(networksJson); - return networksEntries.map(new Map(networksEntries)); + return new Map(networksEntries); }; export const deepCopyNetworks = ( - networks: Map -): Map => + networks: Map +): Map => deserializeNetworks(serializeNetworks(networks)); -export const deepCopyNetwork = ( - identity: SubstrateNetworkBasics -): SubstrateNetworkBasics => JSON.parse(JSON.stringify(identity)); - export const mergeNetworks = ( defaultNetworks: Record, newNetworksEntries: [string, SubstrateNetworkParams][] @@ -50,3 +45,18 @@ export const mergeNetworks = ( ); return new Map(Object.entries(mergedNetworksObject)); }; + +export const generateNetworkParamsFromParsedData = ( + networkParsedData: NetworkParsedData +): SubstrateNetworkParams => { + const pathId = networkParsedData.data.title.toLowerCase(); + return { + ...networkParsedData.data, + deleted: false, + logo: require('res/img/logos/Substrate_Dev.png'), + order: 0, + pathId: pathId.replace(/ /g, '_'), + protocol: 'substrate', + secondaryColor: colors.background.card + }; +}; diff --git a/test/e2e/mockScanRequests.ts b/test/e2e/mockScanRequests.ts index 352cc82c7e..0643e57156 100644 --- a/test/e2e/mockScanRequests.ts +++ b/test/e2e/mockScanRequests.ts @@ -24,7 +24,8 @@ export enum ScanTestRequest { SetRemarkMultiPartPolkadot, EthereumTransaction, EthereumMessage, - passwordedAccountExtrinsic + PasswordedAccountExtrinsic, + AddNewNetwork } export const scanRequestDataMap = { @@ -58,6 +59,8 @@ export const scanRequestDataMap = { rawData: '47b7b22616374696f6e223a227369676e44617461222c2264617461223a7b226163636f756e74223a2233313161364430333334313431353937383238393939636338396346656237333864303646646332222c2264617461223a22343236353635373232303639373332303734363836353732363137303739227d7d0ec11ec11ec11ec11ec11ec11ec11ec11ec11ec11ec11ec11ec11ec11ec' }, - [ScanTestRequest.passwordedAccountExtrinsic]: - '49500000100005301028a476a30a7fd07657fa2e3648ec74b76aea0e9c032772ed108b78924539ce61f10000104112501000026040000b0a8d493285c2df73290dfb7e61f870f17b41801197a149ca93654499ea3dafe3ca87fad296dc36e2070c536142e2e9a74fe221f0b9dbce0e9f85921bd0c5e77b0a8d493285c2df73290dfb7e61f870f17b41801197a149ca93654499ea3dafe0ec11ec' + [ScanTestRequest.PasswordedAccountExtrinsic]: + '49500000100005301028a476a30a7fd07657fa2e3648ec74b76aea0e9c032772ed108b78924539ce61f10000104112501000026040000b0a8d493285c2df73290dfb7e61f870f17b41801197a149ca93654499ea3dafe3ca87fad296dc36e2070c536142e2e9a74fe221f0b9dbce0e9f85921bd0c5e77b0a8d493285c2df73290dfb7e61f870f17b41801197a149ca93654499ea3dafe0ec11ec', + [ScanTestRequest.AddNewNetwork]: + '{"color":"#98F789","decimals":18,"genesisHash":"0x783c78945a4e4a3118190bcf93002bb2d2903192bed10040eb52d54500aade36","prefix":0,"title":"Acala Mandala TC4","unit":"ACA"}' }; diff --git a/test/e2e/specs/passwordedAccount.spec.ts b/test/e2e/specs/passwordedAccount.spec.ts index 5b9e63e693..f903edc43d 100644 --- a/test/e2e/specs/passwordedAccount.spec.ts +++ b/test/e2e/specs/passwordedAccount.spec.ts @@ -51,7 +51,7 @@ describe('passworded account test', () => { describe('Kusama signing Test', () => { it('should sign the set remarks request', async () => { - await launchWithScanRequest(ScanTestRequest.passwordedAccountExtrinsic); + await launchWithScanRequest(ScanTestRequest.PasswordedAccountExtrinsic); await testTap(SecurityHeader.scanButton); await testInputWithDone(IdentityPin.unlockPinInput, pinCode); await testInput(IdentityPin.passwordInput, password); diff --git a/test/unit/specs/util/identitiesUtils.spec.ts b/test/unit/specs/util/identitiesUtils.spec.ts index 83f8a81fd3..98255a59cc 100644 --- a/test/unit/specs/util/identitiesUtils.spec.ts +++ b/test/unit/specs/util/identitiesUtils.spec.ts @@ -46,13 +46,13 @@ const pathIds = Object.values(SUBSTRATE_NETWORK_LIST) const getNetwork = allNetworks.get as GetNetwork; const getSubstrateNetwork = networks.get as GetSubstrateNetwork; -const dummyNetworkContext: NetworksContextState = { +const dummyNetworkContext = { allNetworks, getNetwork, getSubstrateNetwork, networks, pathIds -}; +} as NetworksContextState; const raw = [ { From b57172d87c10b750fe445a8c21e51111e460692f Mon Sep 17 00:00:00 2001 From: Hanwen Cheng Date: Tue, 1 Sep 2020 15:50:24 +0200 Subject: [PATCH 08/11] fix lint, first version save --- src/components/PathCard.tsx | 6 ++-- src/components/ScreenHeading.tsx | 1 - src/constants/networkSpecs.ts | 28 ++++--------------- .../main/components/NetworkSelector.tsx | 6 ++-- src/modules/sign/screens/QrScanner.tsx | 2 +- src/modules/sign/utils.ts | 5 ++-- src/screens/AccountNew.tsx | 2 +- src/screens/PathDerivation.tsx | 28 ++++++++----------- src/screens/PathDetails.tsx | 2 +- src/screens/PathSecret.tsx | 3 +- src/screens/PathsList.tsx | 8 +++++- src/stores/NetworkContext.ts | 4 +-- src/stores/ScannerContext.ts | 2 +- src/utils/db.ts | 18 +++++++++++- src/utils/decoders.ts | 3 +- 15 files changed, 58 insertions(+), 60 deletions(-) diff --git a/src/components/PathCard.tsx b/src/components/PathCard.tsx index f5d01ecdcd..b87a3f7e98 100644 --- a/src/components/PathCard.tsx +++ b/src/components/PathCard.tsx @@ -35,8 +35,7 @@ import fontStyles from 'styles/fontStyles'; import { Identity } from 'types/identityTypes'; import { isSubstrateNetworkParams, - isUnknownNetworkParams, - SubstrateNetworkParams + isUnknownNetworkParams } from 'types/networkTypes'; import { ButtonListener } from 'types/props'; import { @@ -95,7 +94,8 @@ export default function PathCard({ networkKey, computedNetworkKey, isSeedRefValid, - substrateAddress + substrateAddress, + networks ]); const isUnknownAddress = address === ''; diff --git a/src/components/ScreenHeading.tsx b/src/components/ScreenHeading.tsx index 7dea2d41f1..6a2ffacdf3 100644 --- a/src/components/ScreenHeading.tsx +++ b/src/components/ScreenHeading.tsx @@ -25,7 +25,6 @@ import AccountIcon from './AccountIcon'; import { NetworksContext } from 'stores/NetworkContext'; import TouchableItem from 'components/TouchableItem'; import testIDs from 'e2e/testIDs'; -import { NETWORK_LIST } from 'constants/networkSpecs'; import fontStyles from 'styles/fontStyles'; import fonts from 'styles/fonts'; import colors from 'styles/colors'; diff --git a/src/constants/networkSpecs.ts b/src/constants/networkSpecs.ts index 3755ad4ae2..e62e7064ee 100644 --- a/src/constants/networkSpecs.ts +++ b/src/constants/networkSpecs.ts @@ -241,36 +241,24 @@ const substrateDefaultValues = { secondaryColor: colors.background.card }; -function setEthereumNetworkDefault( - ethereumNetworkBase: Record, - defaultProps: Pick< - EthereumNetworkParams, - 'color' | 'logo' | 'protocol' | 'secondaryColor' - > -): Record { +function setEthereumNetworkDefault(): Record { return Object.keys(ethereumNetworkBase).reduce((acc, networkKey) => { return { ...acc, [networkKey]: { - ...defaultProps, + ...ethereumDefaultValues, ...ethereumNetworkBase[networkKey] } }; }, {}); } -function setSubstrateNetworkDefault( - substrateNetworkBase: Record, - defaultProps: Pick< - SubstrateNetworkParams, - 'color' | 'logo' | 'protocol' | 'secondaryColor' - > -): Record { +function setSubstrateNetworkDefault(): Record { return Object.keys(substrateNetworkBase).reduce((acc, networkKey) => { return { ...acc, [networkKey]: { - ...defaultProps, + ...substrateDefaultValues, ...substrateNetworkBase[networkKey] } }; @@ -280,15 +268,11 @@ function setSubstrateNetworkDefault( export const ETHEREUM_NETWORK_LIST: Record< string, EthereumNetworkParams -> = Object.freeze( - setEthereumNetworkDefault(ethereumNetworkBase, ethereumDefaultValues) -); +> = Object.freeze(setEthereumNetworkDefault()); export const SUBSTRATE_NETWORK_LIST: Record< string, SubstrateNetworkParams -> = Object.freeze( - setSubstrateNetworkDefault(substrateNetworkBase, substrateDefaultValues) -); +> = Object.freeze(setSubstrateNetworkDefault()); export const UNKNOWN_NETWORK: Record< string, UnknownNetworkParams diff --git a/src/modules/main/components/NetworkSelector.tsx b/src/modules/main/components/NetworkSelector.tsx index 04b5bb0ada..bfcaa630be 100644 --- a/src/modules/main/components/NetworkSelector.tsx +++ b/src/modules/main/components/NetworkSelector.tsx @@ -54,7 +54,7 @@ function NetworkSelector({ const [shouldShowMoreNetworks, setShouldShowMoreNetworks] = useState(false); const { identities, currentIdentity } = accountsStore.state; const networkContextState = useContext(NetworksContext); - const { networks, getSubstrateNetwork, allNetworks } = networkContextState; + const { getSubstrateNetwork, allNetworks } = networkContextState; const seedRefHooks = useSeedRef(currentIdentity.encryptedSeed); const { unlockWithoutPassword } = useUnlockSeed(seedRefHooks.isSeedRefValid); @@ -181,7 +181,7 @@ function NetworkSelector({ const availableNetworks = useMemo( () => getExistedNetworkKeys(currentIdentity, networkContextState), - [currentIdentity, networks] + [currentIdentity, networkContextState] ); const networkList = useMemo( @@ -195,7 +195,7 @@ function NetworkSelector({ } return availableNetworks.includes(networkKey); }), - [availableNetworks, isNew, shouldShowMoreNetworks, networks] + [availableNetworks, isNew, shouldShowMoreNetworks, allNetworks] ); const renderNetwork = ({ diff --git a/src/modules/sign/screens/QrScanner.tsx b/src/modules/sign/screens/QrScanner.tsx index eeddadaba4..875b2a6386 100644 --- a/src/modules/sign/screens/QrScanner.tsx +++ b/src/modules/sign/screens/QrScanner.tsx @@ -70,7 +70,7 @@ export default function Scanner({ } else { setAlert(title, message, [ { - onPress: async () => { + onPress: async (): Promise => { await clearByTap(); navigateToNetworkSettings(navigation); }, diff --git a/src/modules/sign/utils.ts b/src/modules/sign/utils.ts index 131c7c055f..e19629628a 100644 --- a/src/modules/sign/utils.ts +++ b/src/modules/sign/utils.ts @@ -218,7 +218,7 @@ export function useProcessBarCode( } } - function addNewNetwork(networkParsedData: NetworkParsedData) { + function addNewNetwork(networkParsedData: NetworkParsedData): void { networksContextState.addNetwork(networkParsedData); return showAlertMessage( strings.SUCCESS_TITLE, @@ -230,8 +230,7 @@ export function useProcessBarCode( try { const parsedData = await parseQrData(txRequestData); if (isNetworkParsedData(parsedData)) { - addNewNetwork(parsedData); - return; + return addNewNetwork(parsedData); } const unsignedData = await checkMultiFramesData(parsedData); if (unsignedData === null) return; diff --git a/src/screens/AccountNew.tsx b/src/screens/AccountNew.tsx index 13db7faef6..7fb46276a3 100644 --- a/src/screens/AccountNew.tsx +++ b/src/screens/AccountNew.tsx @@ -75,7 +75,7 @@ export default function AccountNew({ selectedAccount, selectedNetwork }); - }, [accountsStore.state.newAccount]); + }, [accountsStore.state.newAccount, getNetwork]); const { derivationPassword, diff --git a/src/screens/PathDerivation.tsx b/src/screens/PathDerivation.tsx index 3c1f2b0add..1888bc8297 100644 --- a/src/screens/PathDerivation.tsx +++ b/src/screens/PathDerivation.tsx @@ -21,7 +21,6 @@ import testIDs from 'e2e/testIDs'; import { defaultNetworkKey, UnknownNetworkKeys } from 'constants/networkSpecs'; import { AlertStateContext } from 'stores/alertContext'; import { NetworksContext } from 'stores/NetworkContext'; -import { Identity } from 'types/identityTypes'; import { NavigationAccountIdentityProps } from 'types/props'; import TextInput from 'components/TextInput'; import { withCurrentIdentity } from 'utils/HOC'; @@ -54,7 +53,6 @@ function PathDerivation({ const [modalVisible, setModalVisible] = useState(false); const [password, setPassword] = useState(''); const networkContextState = useContext(NetworksContext); - const { networks, getSubstrateNetwork, pathIds } = networkContextState; const pathNameInput = useRef(null); const { setAlert } = useContext(AlertStateContext); const currentIdentity = accountsStore.state.currentIdentity; @@ -63,21 +61,17 @@ function PathDerivation({ ); const parentPath = route.params.parentPath; - function getParentNetworkKey( - parentPath: string, - currentIdentity: Identity - ): string { - if (currentIdentity.meta.has(parentPath)) { - return getNetworkKey(parentPath, currentIdentity, networkContextState); + const parentNetworkKey = useMemo((): string => { + const { networks, pathIds } = networkContextState; + function getParentNetworkKey(): string { + if (currentIdentity.meta.has(parentPath)) { + return getNetworkKey(parentPath, currentIdentity, networkContextState); + } + const pathId = extractPathId(parentPath, pathIds); + return getSubstrateNetworkKeyByPathId(pathId, networks); } - const pathId = extractPathId(parentPath, pathIds); - return getSubstrateNetworkKeyByPathId(pathId, networks); - } - - const parentNetworkKey = useMemo( - () => getParentNetworkKey(parentPath, currentIdentity), - [parentPath, currentIdentity] - ); + return getParentNetworkKey(); + }, [currentIdentity, networkContextState, parentPath]); const [customNetworkKey, setCustomNetworkKey] = useState( parentNetworkKey === UnknownNetworkKeys.UNKNOWN @@ -97,7 +91,7 @@ function PathDerivation({ await accountsStore.deriveNewPath( completePath, substrateAddress, - getSubstrateNetwork(currentNetworkKey), + networkContextState.getSubstrateNetwork(currentNetworkKey), keyPairsName, password ); diff --git a/src/screens/PathDetails.tsx b/src/screens/PathDetails.tsx index 9fca27f2ae..9c7551530a 100644 --- a/src/screens/PathDetails.tsx +++ b/src/screens/PathDetails.tsx @@ -72,7 +72,7 @@ export function PathDetailsView({ isSeedRefValid ); const networksContextState = useContext(NetworksContext); - const { allNetworks, networks } = networksContextState; + const { allNetworks } = networksContextState; if (!address) return ; const isUnknownNetwork = networkKey === UnknownNetworkKeys.UNKNOWN; const formattedNetworkKey = isUnknownNetwork ? defaultNetworkKey : networkKey; diff --git a/src/screens/PathSecret.tsx b/src/screens/PathSecret.tsx index be4eaf738e..60beb99772 100644 --- a/src/screens/PathSecret.tsx +++ b/src/screens/PathSecret.tsx @@ -64,7 +64,8 @@ function PathSecret({ navigation, currentIdentity, isSeedRefValid, - substrateSecret + substrateSecret, + networksContextState ]); return ( diff --git a/src/screens/PathsList.tsx b/src/screens/PathsList.tsx index cd677d4c8a..4739b28aa9 100644 --- a/src/screens/PathsList.tsx +++ b/src/screens/PathsList.tsx @@ -63,7 +63,13 @@ function PathsList({ networkContextState ); return groupPaths(listedPaths, networks); - }, [currentIdentity, isEthereumPath, networkKey]); + }, [ + currentIdentity, + isEthereumPath, + networkKey, + networkContextState, + networks + ]); const { isSeedRefValid } = useSeedRef(currentIdentity.encryptedSeed); const { unlockWithoutPassword } = useUnlockSeed(isSeedRefValid); diff --git a/src/stores/NetworkContext.ts b/src/stores/NetworkContext.ts index a87fc28910..e23b7ef5ac 100644 --- a/src/stores/NetworkContext.ts +++ b/src/stores/NetworkContext.ts @@ -25,7 +25,7 @@ import { } from 'constants/networkSpecs'; import { SubstrateNetworkParams, NetworkParams } from 'types/networkTypes'; import { NetworkParsedData } from 'types/scannerTypes'; -import { loadNetworks } from 'utils/db'; +import { loadNetworks, saveNetworks } from 'utils/db'; import { deepCopyNetworks, generateNetworkParamsFromParsedData @@ -67,7 +67,6 @@ export function useNetworksContext(): NetworksContextState { const result = Array.from(substrateNetworks.values()) .map(n => n.pathId) .concat([unknownNetworkPathId]); - console.log('path ids are', result); return result; }, [substrateNetworks]); @@ -98,6 +97,7 @@ export function useNetworksContext(): NetworksContextState { const newNetworksList = deepCopyNetworks(substrateNetworks); newNetworksList.set(networkKey, newNetworkParams); setSubstrateNetworks(newNetworksList); + saveNetworks(newNetworksList); } return { diff --git a/src/stores/ScannerContext.ts b/src/stores/ScannerContext.ts index 0a4ac42c61..37f2213fde 100644 --- a/src/stores/ScannerContext.ts +++ b/src/stores/ScannerContext.ts @@ -292,7 +292,7 @@ export function useScannerContext(): ScannerContextState { ): Promise { setBusy(); - const { allNetworks, getNetwork } = networkContext; + const { getNetwork } = networkContext; const isOversized = (txRequest as SubstrateCompletedParsedData)?.oversized || false; diff --git a/src/utils/db.ts b/src/utils/db.ts index 765fcb0ccb..a0e324e257 100644 --- a/src/utils/db.ts +++ b/src/utils/db.ts @@ -20,7 +20,7 @@ import SecureStorage from 'react-native-secure-storage'; import { generateAccountId } from './account'; import { deserializeIdentities, serializeIdentities } from './identitiesUtils'; -import { deserializeNetworks, mergeNetworks } from 'utils/networksUtils'; +import { mergeNetworks, serializeNetworks } from 'utils/networksUtils'; import { SUBSTRATE_NETWORK_LIST } from 'constants/networkSpecs'; import { NetworkParams, SubstrateNetworkParams } from 'types/networkTypes'; import { Account, Identity } from 'types/identityTypes'; @@ -114,10 +114,12 @@ export async function loadNetworks(): Promise< Map > { try { + console.log('start load'); const networksJson = await SecureStorage.getItem( currentNetworkStorageLabel, networkStorage ); + console.log('start finished with json', networksJson); if (!networksJson) return new Map(Object.entries(SUBSTRATE_NETWORK_LIST)); const networksEntries = JSON.parse(networksJson); return mergeNetworks(SUBSTRATE_NETWORK_LIST, networksEntries); @@ -127,6 +129,20 @@ export async function loadNetworks(): Promise< } } +export async function saveNetworks( + networks: Map +): Promise { + try { + SecureStorage.setItem( + currentNetworkStorageLabel, + serializeNetworks(networks), + networkStorage + ); + } catch (e) { + handleError(e, 'networks'); + } +} + /* * ======================================== * Privacy Policy and Terms Conditions Store diff --git a/src/utils/decoders.ts b/src/utils/decoders.ts index 1081bafe51..f63ec88c93 100644 --- a/src/utils/decoders.ts +++ b/src/utils/decoders.ts @@ -23,8 +23,7 @@ import { import { encodeAddress } from '@polkadot/util-crypto'; import strings from 'modules/sign/strings'; -import { NetworksContextState } from 'stores/NetworkContext'; -import { NetworkParams, SubstrateNetworkParams } from 'types/networkTypes'; +import { SubstrateNetworkParams } from 'types/networkTypes'; import { EthereumParsedData, ParsedData, From 3f34bb691e7dfeb0552611c8a5d5c22811e9db8d Mon Sep 17 00:00:00 2001 From: Hanwen Cheng Date: Tue, 1 Sep 2020 16:16:35 +0200 Subject: [PATCH 09/11] complete adding network --- src/modules/sign/utils.ts | 1 - src/stores/NetworkContext.ts | 3 +-- src/utils/db.ts | 19 +++++++++++++------ src/utils/networksUtils.ts | 14 ++++++++++---- 4 files changed, 24 insertions(+), 13 deletions(-) diff --git a/src/modules/sign/utils.ts b/src/modules/sign/utils.ts index e19629628a..82bea2fdf8 100644 --- a/src/modules/sign/utils.ts +++ b/src/modules/sign/utils.ts @@ -80,7 +80,6 @@ export function useProcessBarCode( async function parseQrData( txRequestData: TxRequestData ): Promise { - console.log('txrequest data is', txRequestData); if (isAddressString(txRequestData.data)) { throw new Error(strings.ERROR_ADDRESS_MESSAGE); } else if (isJsonString(txRequestData.data)) { diff --git a/src/stores/NetworkContext.ts b/src/stores/NetworkContext.ts index e23b7ef5ac..375afca561 100644 --- a/src/stores/NetworkContext.ts +++ b/src/stores/NetworkContext.ts @@ -74,7 +74,6 @@ export function useNetworksContext(): NetworksContextState { const refreshList = async function (): Promise { const initNetworkSpecs = await loadNetworks(); setSubstrateNetworks(initNetworkSpecs); - console.log('loaded Networks are:', initNetworkSpecs); }; refreshList(); }, []); @@ -97,7 +96,7 @@ export function useNetworksContext(): NetworksContextState { const newNetworksList = deepCopyNetworks(substrateNetworks); newNetworksList.set(networkKey, newNetworkParams); setSubstrateNetworks(newNetworksList); - saveNetworks(newNetworksList); + saveNetworks(newNetworkParams); } return { diff --git a/src/utils/db.ts b/src/utils/db.ts index a0e324e257..21bc72c00f 100644 --- a/src/utils/db.ts +++ b/src/utils/db.ts @@ -105,8 +105,8 @@ export const saveIdentities = (identities: Identity[]): void => { * ======================================== */ const networkStorage = { - keychainService: 'parity_signer_networks', - sharedPreferencesName: 'parity_signer_networks' + keychainService: 'parity_signer_updated_networks', + sharedPreferencesName: 'parity_signer_updated_networks' }; const currentNetworkStorageLabel = 'networks_v4'; @@ -114,12 +114,11 @@ export async function loadNetworks(): Promise< Map > { try { - console.log('start load'); const networksJson = await SecureStorage.getItem( currentNetworkStorageLabel, networkStorage ); - console.log('start finished with json', networksJson); + if (!networksJson) return new Map(Object.entries(SUBSTRATE_NETWORK_LIST)); const networksEntries = JSON.parse(networksJson); return mergeNetworks(SUBSTRATE_NETWORK_LIST, networksEntries); @@ -130,12 +129,20 @@ export async function loadNetworks(): Promise< } export async function saveNetworks( - networks: Map + newNetwork: SubstrateNetworkParams ): Promise { try { + let addedNetworks = new Map(); + const addedNetworkJson = await SecureStorage.getItem( + currentNetworkStorageLabel, + networkStorage + ); + if (addedNetworkJson) addedNetworks = new Map(JSON.parse(addedNetworkJson)); + + addedNetworks.set(newNetwork.genesisHash, newNetwork); SecureStorage.setItem( currentNetworkStorageLabel, - serializeNetworks(networks), + serializeNetworks(addedNetworks), networkStorage ); } catch (e) { diff --git a/src/utils/networksUtils.ts b/src/utils/networksUtils.ts index e07eef6105..0da6058a6e 100644 --- a/src/utils/networksUtils.ts +++ b/src/utils/networksUtils.ts @@ -30,16 +30,22 @@ export const mergeNetworks = ( acc, [networkKey, networkParams] ): Record => { + const newNetworksList = Object.assign({}, acc); if (!defaultNetworks.hasOwnProperty(networkKey)) { - acc[networkKey] = { + // const newAcc = Object.assign({}, acc); + newNetworksList[networkKey] = { ...networkParams, logo: require('res/img/logos/Substrate_Dev.png') }; - return acc; + return newNetworksList; } + const defaultParams = defaultNetworks[networkKey]; - acc[networkKey] = { ...networkParams, logo: defaultParams.logo }; - return acc; + newNetworksList[networkKey] = { + ...networkParams, + logo: defaultParams.logo + }; + return newNetworksList; }, defaultNetworks ); From 17149b598ff9245c270bacd1000bc089b52b695e Mon Sep 17 00:00:00 2001 From: Hanwen Cheng Date: Tue, 1 Sep 2020 17:15:05 +0200 Subject: [PATCH 10/11] add e2e test --- .../network/screens/NetworkSettings.tsx | 12 +-- src/modules/sign/screens/QrScanner.tsx | 2 + test/e2e/mockScanRequests.ts | 18 ++++- test/e2e/specs/addNewNetwork.spec.ts | 75 +++++++++++++++++++ test/e2e/testIDs.ts | 6 ++ 5 files changed, 100 insertions(+), 13 deletions(-) create mode 100644 test/e2e/specs/addNewNetwork.spec.ts diff --git a/src/modules/network/screens/NetworkSettings.tsx b/src/modules/network/screens/NetworkSettings.tsx index 763ed02053..2d70bb1251 100644 --- a/src/modules/network/screens/NetworkSettings.tsx +++ b/src/modules/network/screens/NetworkSettings.tsx @@ -17,6 +17,7 @@ import React, { ReactElement, useContext } from 'react'; import { FlatList, StyleSheet } from 'react-native'; +import testIDs from 'e2e/testIDs'; import { NetworkCard } from 'components/AccountCard'; import { filterNetworks } from 'modules/network/utils'; import { SafeAreaViewContainer } from 'components/SafeAreaContainer'; @@ -42,6 +43,7 @@ export default function NetworkSettings({ const networkSpec = item[1]; return ( @@ -62,16 +64,6 @@ export default function NetworkSettings({ renderItem={renderNetwork} keyExtractor={(item: [string, NetworkParams]): string => item[0]} /> - {/** -