Create hook to add network

This commit is contained in:
IshaVenikar 2025-06-05 10:03:22 +05:30
parent fe3d2411a2
commit 8d60fd9e4b
5 changed files with 113 additions and 64 deletions

View File

@ -45,6 +45,7 @@ import SignRequestEmbed from "./screens/SignRequestEmbed";
import useAddAccountEmbed from "./hooks/useAddAccountEmbed"; import useAddAccountEmbed from "./hooks/useAddAccountEmbed";
import useExportPKEmbed from "./hooks/useExportPrivateKeyEmbed"; import useExportPKEmbed from "./hooks/useExportPrivateKeyEmbed";
import { SignTxEmbed } from "./screens/SignTxEmbed"; import { SignTxEmbed } from "./screens/SignTxEmbed";
import useAddNetworkEmbed from "./hooks/useAddNetworkEmbed";
const Stack = createStackNavigator<StackParamsList>(); const Stack = createStackNavigator<StackParamsList>();
@ -287,6 +288,7 @@ const App = (): React.JSX.Element => {
useWebViewHandler(); useWebViewHandler();
useAddAccountEmbed(); useAddAccountEmbed();
useExportPKEmbed(); useExportPKEmbed();
useAddNetworkEmbed();
return ( return (
<Surface style={styles.appSurface}> <Surface style={styles.appSurface}>

View File

@ -0,0 +1,39 @@
import { useEffect } from 'react';
import { addNewNetwork } from '../utils/accounts';
import { ADD_NETWORK } from '../utils/constants';
const REACT_APP_ALLOWED_URLS = import.meta.env.REACT_APP_ALLOWED_URLS;
const useAddNetworkEmbed = () => {
useEffect(() => {
const handleAddNetwork = async (event: MessageEvent) => {
if (event.data.type !== ADD_NETWORK) return;
if (!REACT_APP_ALLOWED_URLS) {
console.log('Allowed URLs are not set');
return;
}
const allowedUrls = REACT_APP_ALLOWED_URLS.split(',').map(url => url.trim());
if (!allowedUrls.includes(event.origin)) {
console.log('Unauthorized app.');
return;
}
try {
const { networkData } = event.data;
await addNewNetwork(networkData);
} catch (err) {
console.error('Error preparing sign request:', err);
}
};
window.addEventListener('message', handleAddNetwork);
return () => window.removeEventListener('message', handleAddNetwork);
}, []);
}
export default useAddNetworkEmbed;

View File

@ -1,8 +1,6 @@
import React, { useCallback, useEffect, useState } from "react"; import React, { useCallback, useEffect, useState } from "react";
import { useForm, Controller, useWatch, FieldErrors } from "react-hook-form"; import { useForm, Controller, useWatch, FieldErrors } from "react-hook-form";
import { TextInput, HelperText } from "react-native-paper"; import { TextInput, HelperText } from "react-native-paper";
import { HDNode } from "ethers/lib/utils";
import { chains } from "chain-registry"; import { chains } from "chain-registry";
import { useDebouncedCallback } from "use-debounce"; import { useDebouncedCallback } from "use-debounce";
import { z } from "zod"; import { z } from "zod";
@ -10,27 +8,21 @@ import { z } from "zod";
import { NativeStackNavigationProp } from "@react-navigation/native-stack"; import { NativeStackNavigationProp } from "@react-navigation/native-stack";
import { useNavigation } from "@react-navigation/native"; import { useNavigation } from "@react-navigation/native";
import { zodResolver } from "@hookform/resolvers/zod"; import { zodResolver } from "@hookform/resolvers/zod";
import { Divider, Grid } from "@mui/material";
import { LoadingButton } from "@mui/lab";
import { StackParamsList } from "../types"; import { StackParamsList } from "../types";
import { SelectNetworkType } from "../components/SelectNetworkType"; import { SelectNetworkType } from "../components/SelectNetworkType";
import { addAccountsForNetwork, getNextAccountId, storeNetworkData } from "../utils/accounts"; import { addAccountsForNetwork, addNewNetwork, getNextAccountId, storeNetworkData } from "../utils/accounts";
import { useNetworks } from "../context/NetworksContext"; import { useNetworks } from "../context/NetworksContext";
import { import {
COSMOS,
EIP155, EIP155,
CHAINID_DEBOUNCE_DELAY, CHAINID_DEBOUNCE_DELAY,
EMPTY_FIELD_ERROR, EMPTY_FIELD_ERROR,
INVALID_URL_ERROR, INVALID_URL_ERROR,
IS_NUMBER_REGEX, IS_NUMBER_REGEX,
} from "../utils/constants"; } from "../utils/constants";
import { getCosmosAccountByHDPath } from "../utils/accounts";
import ETH_CHAINS from "../assets/ethereum-chains.json"; import ETH_CHAINS from "../assets/ethereum-chains.json";
import {
getInternetCredentials,
setInternetCredentials,
} from "../utils/key-store";
import { Divider, Grid } from "@mui/material";
import { LoadingButton } from "@mui/lab";
import { Layout } from "../components/Layout"; import { Layout } from "../components/Layout";
const ethNetworkDataSchema = z.object({ const ethNetworkDataSchema = z.object({
@ -143,42 +135,8 @@ const AddNetwork = () => {
isDefault: false, isDefault: false,
}; };
const mnemonicServer = await getInternetCredentials("mnemonicServer"); const retrievedNetworksData = await addNewNetwork(newNetworkData);
const mnemonic = mnemonicServer;
if (!mnemonic) {
throw new Error("Mnemonic not found");
}
const hdNode = HDNode.fromMnemonic(mnemonic);
const hdPath = `m/44'/${newNetworkData.coinType}'/0'/0/0`;
const node = hdNode.derivePath(hdPath);
let address;
switch (newNetworkData.namespace) {
case EIP155:
address = node.address;
break;
case COSMOS:
address = (
await getCosmosAccountByHDPath(
mnemonic,
hdPath,
(newNetworkData as z.infer<typeof cosmosNetworkDataSchema>)
.addressPrefix,
)
).data.address;
break;
default:
throw new Error("Unsupported namespace");
}
const accountInfo = `${hdPath},${node.privateKey},${node.publicKey},${address}`;
const retrievedNetworksData = await storeNetworkData(newNetworkData);
setNetworksData(retrievedNetworksData); setNetworksData(retrievedNetworksData);
// Get number of accounts in first network // Get number of accounts in first network
@ -190,24 +148,6 @@ const AddNetwork = () => {
(network) => network.chainId === newNetworkData.chainId, (network) => network.chainId === newNetworkData.chainId,
); );
await Promise.all([
setInternetCredentials(
`accounts/${newNetworkData.namespace}:${newNetworkData.chainId}/0`,
"_",
accountInfo,
),
setInternetCredentials(
`addAccountCounter/${newNetworkData.namespace}:${newNetworkData.chainId}`,
"_",
"1",
),
setInternetCredentials(
`accountIndices/${newNetworkData.namespace}:${newNetworkData.chainId}`,
"_",
"0",
),
]);
await addAccountsForNetwork(selectedNetwork!, nextAccountId - 1); await addAccountsForNetwork(selectedNetwork!, nextAccountId - 1);
navigation.navigate("Home"); navigation.navigate("Home");

View File

@ -160,6 +160,66 @@ const addAccountFromHDPath = async (
} }
}; };
const addNewNetwork = async (
newNetworkData: NetworksFormData
): Promise<NetworksDataState[]> => {
const mnemonicServer = await getInternetCredentials("mnemonicServer");
const mnemonic = mnemonicServer;
if (!mnemonic) {
throw new Error("Mnemonic not found");
}
const hdNode = HDNode.fromMnemonic(mnemonic);
const hdPath = `m/44'/${newNetworkData.coinType}'/0'/0/0`;
const node = hdNode.derivePath(hdPath);
let address;
switch (newNetworkData.namespace) {
case EIP155:
address = node.address;
break;
case COSMOS:
address = (
await getCosmosAccountByHDPath(
mnemonic,
hdPath,
newNetworkData.addressPrefix,
)
).data.address;
break;
default:
throw new Error("Unsupported namespace");
}
const accountInfo = `${hdPath},${node.privateKey},${node.publicKey},${address}`;
await Promise.all([
setInternetCredentials(
`accounts/${newNetworkData.namespace}:${newNetworkData.chainId}/0`,
"_",
accountInfo,
),
setInternetCredentials(
`addAccountCounter/${newNetworkData.namespace}:${newNetworkData.chainId}`,
"_",
"1",
),
setInternetCredentials(
`accountIndices/${newNetworkData.namespace}:${newNetworkData.chainId}`,
"_",
"0",
),
]);
const retrievedNetworksData = await storeNetworkData(newNetworkData);
return retrievedNetworksData;
}
const storeNetworkData = async ( const storeNetworkData = async (
networkData: NetworksFormData, networkData: NetworksFormData,
): Promise<NetworksDataState[]> => { ): Promise<NetworksDataState[]> => {
@ -180,11 +240,13 @@ const storeNetworkData = async (
networkId: String(networkId), networkId: String(networkId),
}, },
]; ];
await setInternetCredentials( await setInternetCredentials(
'networks', 'networks',
'_', '_',
JSON.stringify(updatedNetworks), JSON.stringify(updatedNetworks),
); );
return updatedNetworks; return updatedNetworks;
}; };
@ -194,7 +256,9 @@ const retrieveNetworksData = async (): Promise<NetworksDataState[]> => {
if(!networks){ if(!networks){
return []; return [];
} }
const parsedNetworks: NetworksDataState[] = JSON.parse(networks); const parsedNetworks: NetworksDataState[] = JSON.parse(networks);
return parsedNetworks; return parsedNetworks;
}; };
@ -217,6 +281,7 @@ export const retrieveAccountsForNetwork = async (
address, address,
hdPath: path, hdPath: path,
}; };
return account; return account;
}), }),
); );
@ -234,6 +299,7 @@ const retrieveAccounts = async (
if (!accountIndices) { if (!accountIndices) {
return; return;
} }
const loadedAccounts = await retrieveAccountsForNetwork( const loadedAccounts = await retrieveAccountsForNetwork(
`${currentNetworkData.namespace}:${currentNetworkData.chainId}`, `${currentNetworkData.namespace}:${currentNetworkData.chainId}`,
accountIndices, accountIndices,
@ -382,4 +448,5 @@ export {
getNextAccountId, getNextAccountId,
updateAccountCounter, updateAccountCounter,
getCosmosAccountByHDPath, getCosmosAccountByHDPath,
addNewNetwork
}; };

View File

@ -98,6 +98,7 @@ export const REQUEST_ACCOUNT_PK = 'REQUEST_ACCOUNT_PK';
export const REQUEST_ADD_ACCOUNT = 'REQUEST_ADD_ACCOUNT'; export const REQUEST_ADD_ACCOUNT = 'REQUEST_ADD_ACCOUNT';
export const AUTO_SIGN_IN = 'AUTO_SIGN_IN'; export const AUTO_SIGN_IN = 'AUTO_SIGN_IN';
export const CHECK_BALANCE = 'CHECK_BALANCE'; export const CHECK_BALANCE = 'CHECK_BALANCE';
export const ADD_NETWORK = 'ADD_NETWORK';
// iframe response types // iframe response types
export const COSMOS_ACCOUNTS_RESPONSE = 'COSMOS_ACCOUNTS_RESPONSE'; export const COSMOS_ACCOUNTS_RESPONSE = 'COSMOS_ACCOUNTS_RESPONSE';