From 8ed4c33beb0e1a242187688e6a30574fca6874d0 Mon Sep 17 00:00:00 2001 From: Adwait Gharpure <69599306+Adw8@users.noreply.github.com> Date: Mon, 18 Mar 2024 10:57:20 +0530 Subject: [PATCH] Refactor code for WalletConnect Integration (#59) * Disconnect pairing request when app is reset * Move files to respective folders * Add comments to describe flow * Add new line * remove request session context * Fix imports * Move hook to folder * Add undefined type * Move types to src * Move util functions to correct files * Remove typeroots from tsconfig --------- Co-authored-by: Adw8 --- __tests__/App.test.tsx | 2 +- index.js | 6 +- App.tsx => src/App.tsx | 50 ++++---- .../assets}/WalletConnect-Icon-Blueberry.png | Bin .../components}/AccountDetails.tsx | 0 {components => src/components}/Accounts.tsx | 16 ++- .../components}/CreateWallet.tsx | 0 {components => src/components}/Dialog.tsx | 0 {components => src/components}/Grid.tsx | 0 {components => src/components}/HDPath.tsx | 0 .../components}/HDPathDialog.tsx | 0 .../components}/NetworkDropdown.tsx | 0 .../components}/PairingModal.tsx | 6 +- .../components}/ResetWalletDialog.tsx | 0 {context => src/context}/AccountsContext.tsx | 0 .../context}/WalletConnectContext.tsx | 12 +- src/hooks/useInitialization.ts | 23 ++++ {hooks => src/hooks}/usePrevious.ts | 1 + {components => src/screens}/AddSession.tsx | 0 {components => src/screens}/HomeScreen.tsx | 10 +- {components => src/screens}/InvalidPath.tsx | 0 {components => src/screens}/SignMessage.tsx | 2 +- {components => src/screens}/SignRequest.tsx | 51 ++++---- {components => src/screens}/WalletConnect.tsx | 0 {styles => src/styles}/stylesheet.js | 0 types.ts => src/types.ts | 12 +- {utils => src/utils}/accounts.ts | 114 +++++++++++++++++- utils/utils.ts => src/utils/misc.ts | 107 +--------------- {utils => src/utils}/sign-message.ts | 8 +- .../utils}/wallet-connect/EIP155Lib.ts | 0 .../utils}/wallet-connect/Helpers.ts | 0 .../wallet-connect/WalletConnectRequests.ts | 5 +- .../wallet-connect/WalletConnectUtils.tsx | 28 +---- tsconfig.json | 2 +- 34 files changed, 227 insertions(+), 228 deletions(-) rename App.tsx => src/App.tsx (83%) rename {assets => src/assets}/WalletConnect-Icon-Blueberry.png (100%) rename {components => src/components}/AccountDetails.tsx (100%) rename {components => src/components}/Accounts.tsx (86%) rename {components => src/components}/CreateWallet.tsx (100%) rename {components => src/components}/Dialog.tsx (100%) rename {components => src/components}/Grid.tsx (100%) rename {components => src/components}/HDPath.tsx (100%) rename {components => src/components}/HDPathDialog.tsx (100%) rename {components => src/components}/NetworkDropdown.tsx (100%) rename {components => src/components}/PairingModal.tsx (98%) rename {components => src/components}/ResetWalletDialog.tsx (100%) rename {context => src/context}/AccountsContext.tsx (100%) rename {context => src/context}/WalletConnectContext.tsx (74%) create mode 100644 src/hooks/useInitialization.ts rename {hooks => src/hooks}/usePrevious.ts (99%) rename {components => src/screens}/AddSession.tsx (100%) rename {components => src/screens}/HomeScreen.tsx (94%) rename {components => src/screens}/InvalidPath.tsx (100%) rename {components => src/screens}/SignMessage.tsx (96%) rename {components => src/screens}/SignRequest.tsx (86%) rename {components => src/screens}/WalletConnect.tsx (100%) rename {styles => src/styles}/stylesheet.js (100%) rename types.ts => src/types.ts (87%) rename {utils => src/utils}/accounts.ts (71%) rename utils/utils.ts => src/utils/misc.ts (55%) rename {utils => src/utils}/sign-message.ts (96%) rename {utils => src/utils}/wallet-connect/EIP155Lib.ts (100%) rename {utils => src/utils}/wallet-connect/Helpers.ts (100%) rename {utils => src/utils}/wallet-connect/WalletConnectRequests.ts (94%) rename {utils => src/utils}/wallet-connect/WalletConnectUtils.tsx (55%) diff --git a/__tests__/App.test.tsx b/__tests__/App.test.tsx index 9eac6fb..fe831ed 100644 --- a/__tests__/App.test.tsx +++ b/__tests__/App.test.tsx @@ -4,7 +4,7 @@ import 'react-native'; import React from 'react'; -import App from '../App'; +import App from '../src/App'; // Note: import explicitly to use the types shipped with jest. import {it} from '@jest/globals'; diff --git a/index.js b/index.js index 1b0e7b3..5a8a4aa 100644 --- a/index.js +++ b/index.js @@ -5,9 +5,9 @@ import { PaperProvider } from 'react-native-paper'; import { NavigationContainer } from '@react-navigation/native'; -import App from './App'; -import { AccountsProvider } from './context/AccountsContext'; -import { WalletConnectProvider } from './context/WalletConnectContext'; +import App from './src/App'; +import { AccountsProvider } from './src/context/AccountsContext'; +import { WalletConnectProvider } from './src/context/WalletConnectContext'; import { name as appName } from './app.json'; export default function Main() { diff --git a/App.tsx b/src/App.tsx similarity index 83% rename from App.tsx rename to src/App.tsx index a9becb0..92b34b2 100644 --- a/App.tsx +++ b/src/App.tsx @@ -9,20 +9,22 @@ import { NativeStackNavigationProp, createNativeStackNavigator, } from '@react-navigation/native-stack'; +import { getSdkError } from '@walletconnect/utils'; import { Web3WalletTypes } from '@walletconnect/web3wallet'; -import SignMessage from './components/SignMessage'; -import HomeScreen from './components/HomeScreen'; -import SignRequest from './components/SignRequest'; -import InvalidPath from './components/InvalidPath'; import PairingModal from './components/PairingModal'; -import AddSession from './components/AddSession'; -import WalletConnect from './components/WalletConnect'; +import { useWalletConnect } from './context/WalletConnectContext'; +import { useAccounts } from './context/AccountsContext'; +import InvalidPath from './screens/InvalidPath'; +import SignMessage from './screens/SignMessage'; +import HomeScreen from './screens/HomeScreen'; +import SignRequest from './screens/SignRequest'; +import AddSession from './screens/AddSession'; +import WalletConnect from './screens/WalletConnect'; import { StackParamsList } from './types'; import { web3wallet } from './utils/wallet-connect/WalletConnectUtils'; import { EIP155_SIGNING_METHODS } from './utils/wallet-connect/EIP155Lib'; import { getSignParamsMessage } from './utils/wallet-connect/Helpers'; -import { useWalletConnect } from './context/WalletConnectContext'; const Stack = createNativeStackNavigator(); @@ -30,9 +32,8 @@ const App = (): React.JSX.Element => { const navigation = useNavigation>(); - const { requestSession, setRequestSession, setActiveSessions } = - useWalletConnect(); - + const { setActiveSessions } = useWalletConnect(); + const { accounts } = useAccounts(); const [modalVisible, setModalVisible] = useState(false); const [toastVisible, setToastVisible] = useState(false); const [currentProposal, setCurrentProposal] = useState< @@ -40,11 +41,19 @@ const App = (): React.JSX.Element => { >(); const onSessionProposal = useCallback( - (proposal: SignClientTypes.EventArguments['session_proposal']) => { + async (proposal: SignClientTypes.EventArguments['session_proposal']) => { + if (!accounts.ethAccounts.length || !accounts.cosmosAccounts.length) { + const { id } = proposal; + await web3wallet!.rejectSession({ + id, + reason: getSdkError('UNSUPPORTED_ACCOUNTS'), + }); + return; + } setModalVisible(true); setCurrentProposal(proposal); }, - [], + [accounts.ethAccounts, accounts.cosmosAccounts], ); const onSessionRequest = useCallback( @@ -53,10 +62,7 @@ const App = (): React.JSX.Element => { const { request } = params; const requestSessionData = - web3wallet.engine.signClient.session.get(topic); - - setRequestSession(requestSessionData); - + web3wallet!.engine.signClient.session.get(topic); switch (request.method) { case EIP155_SIGNING_METHODS.PERSONAL_SIGN: navigation.navigate('SignRequest', { @@ -64,7 +70,7 @@ const App = (): React.JSX.Element => { address: request.params[1], message: getSignParamsMessage(request.params), requestEvent, - requestSession, + requestSessionData, }); break; @@ -90,7 +96,7 @@ const App = (): React.JSX.Element => { address: request.params.signerAddress, message: JSON.stringify(message, undefined, 2), requestEvent, - requestSession, + requestSessionData, }); break; @@ -100,7 +106,7 @@ const App = (): React.JSX.Element => { address: request.params.signerAddress, message: request.params.signDoc.memo, requestEvent, - requestSession, + requestSessionData, }); break; @@ -108,13 +114,13 @@ const App = (): React.JSX.Element => { throw new Error('Invalid method'); } }, - [requestSession, setRequestSession, navigation], + [navigation], ); const onSessionDelete = useCallback(() => { - let sessions = web3wallet?.getActiveSessions(); + const sessions = web3wallet!.getActiveSessions(); setActiveSessions(sessions); - }, []); + }, [setActiveSessions]); useEffect(() => { web3wallet?.on('session_proposal', onSessionProposal); diff --git a/assets/WalletConnect-Icon-Blueberry.png b/src/assets/WalletConnect-Icon-Blueberry.png similarity index 100% rename from assets/WalletConnect-Icon-Blueberry.png rename to src/assets/WalletConnect-Icon-Blueberry.png diff --git a/components/AccountDetails.tsx b/src/components/AccountDetails.tsx similarity index 100% rename from components/AccountDetails.tsx rename to src/components/AccountDetails.tsx diff --git a/components/Accounts.tsx b/src/components/Accounts.tsx similarity index 86% rename from components/Accounts.tsx rename to src/components/Accounts.tsx index d27bc5b..31c1a4c 100644 --- a/components/Accounts.tsx +++ b/src/components/Accounts.tsx @@ -54,38 +54,48 @@ const Accounts = ({ useEffect(() => { const updateSessions = async () => { - const sessions = web3wallet?.getActiveSessions() || {}; + const sessions = (web3wallet && web3wallet.getActiveSessions()) || {}; + // Iterate through each session for (const topic in sessions) { const session = sessions[topic]; const requiredNamespaces = session.requiredNamespaces; const namespaces = session.namespaces; + // Check if EIP155 namespace exists and Ethereum accounts have changed if ( namespaces.hasOwnProperty('eip155') && prevEthAccountsRef !== accounts.ethAccounts ) { + // Iterate through each chain ID in required EIP155 namespaces requiredNamespaces.eip155.chains?.forEach(chainId => { + // Update Ethereum accounts in namespaces with chain prefix namespaces.eip155.accounts = accounts.ethAccounts.map( ethAccount => `${chainId}:${ethAccount.address}`, ); }); - await web3wallet.updateSession({ topic, namespaces }); + // update session with modified namespace + await web3wallet!.updateSession({ topic, namespaces }); } + // Check if Cosmos namespace exists and Cosmos accounts have changed if ( namespaces.hasOwnProperty('cosmos') && prevCosmosAccountsRef !== accounts.cosmosAccounts ) { + // Iterate through each chain ID in required Cosmos namespaces requiredNamespaces?.cosmos.chains?.forEach(chainId => { + // Iterate through each chain ID in required Cosmos namespaces namespaces.cosmos.accounts = accounts.cosmosAccounts.map( cosmosAccount => `${chainId}:${cosmosAccount.address}`, ); }); - await web3wallet.updateSession({ topic, namespaces }); + // update session with modified namespace + await web3wallet!.updateSession({ topic, namespaces }); } } }; + // Call the updateSessions function when the 'accounts' dependency changes updateSessions(); // eslint-disable-next-line react-hooks/exhaustive-deps }, [accounts]); diff --git a/components/CreateWallet.tsx b/src/components/CreateWallet.tsx similarity index 100% rename from components/CreateWallet.tsx rename to src/components/CreateWallet.tsx diff --git a/components/Dialog.tsx b/src/components/Dialog.tsx similarity index 100% rename from components/Dialog.tsx rename to src/components/Dialog.tsx diff --git a/components/Grid.tsx b/src/components/Grid.tsx similarity index 100% rename from components/Grid.tsx rename to src/components/Grid.tsx diff --git a/components/HDPath.tsx b/src/components/HDPath.tsx similarity index 100% rename from components/HDPath.tsx rename to src/components/HDPath.tsx diff --git a/components/HDPathDialog.tsx b/src/components/HDPathDialog.tsx similarity index 100% rename from components/HDPathDialog.tsx rename to src/components/HDPathDialog.tsx diff --git a/components/NetworkDropdown.tsx b/src/components/NetworkDropdown.tsx similarity index 100% rename from components/NetworkDropdown.tsx rename to src/components/NetworkDropdown.tsx diff --git a/components/PairingModal.tsx b/src/components/PairingModal.tsx similarity index 98% rename from components/PairingModal.tsx rename to src/components/PairingModal.tsx index 0248486..aa19cf8 100644 --- a/components/PairingModal.tsx +++ b/src/components/PairingModal.tsx @@ -137,13 +137,13 @@ const PairingModal = ({ }; }); - await web3wallet.approveSession({ + await web3wallet!.approveSession({ id, relayProtocol: relays[0].protocol, namespaces, }); - const sessions = web3wallet.getActiveSessions(); + const sessions = web3wallet!.getActiveSessions(); setActiveSessions(sessions); setModalVisible(false); setToastVisible(true); @@ -159,7 +159,7 @@ const PairingModal = ({ const handleReject = async () => { if (currentProposal) { const { id } = currentProposal; - await web3wallet.rejectSession({ + await web3wallet!.rejectSession({ id, reason: getSdkError('USER_REJECTED_METHODS'), }); diff --git a/components/ResetWalletDialog.tsx b/src/components/ResetWalletDialog.tsx similarity index 100% rename from components/ResetWalletDialog.tsx rename to src/components/ResetWalletDialog.tsx diff --git a/context/AccountsContext.tsx b/src/context/AccountsContext.tsx similarity index 100% rename from context/AccountsContext.tsx rename to src/context/AccountsContext.tsx diff --git a/context/WalletConnectContext.tsx b/src/context/WalletConnectContext.tsx similarity index 74% rename from context/WalletConnectContext.tsx rename to src/context/WalletConnectContext.tsx index 8161d69..903319d 100644 --- a/context/WalletConnectContext.tsx +++ b/src/context/WalletConnectContext.tsx @@ -3,13 +3,10 @@ import React, { createContext, useContext, useEffect, useState } from 'react'; import { SessionTypes } from '@walletconnect/types'; import { WalletConnectContextProps } from '../types'; -import useInitialization, { - web3wallet, -} from '../utils/wallet-connect/WalletConnectUtils'; +import { web3wallet } from '../utils/wallet-connect/WalletConnectUtils'; +import useInitialization from '../hooks/useInitialization'; const WalletConnectContext = createContext({ - requestSession: {}, - setRequestSession: () => {}, activeSessions: {}, setActiveSessions: () => {}, }); @@ -23,11 +20,10 @@ const WalletConnectProvider = ({ children }: { children: React.ReactNode }) => { useInitialization(); useEffect(() => { - const sessions = web3wallet?.getActiveSessions() ?? {}; + const sessions = (web3wallet && web3wallet.getActiveSessions()) || {}; setActiveSessions(sessions); }, []); - const [requestSession, setRequestSession] = useState({}); const [activeSessions, setActiveSessions] = useState< Record >({}); @@ -35,8 +31,6 @@ const WalletConnectProvider = ({ children }: { children: React.ReactNode }) => { return ( diff --git a/src/hooks/useInitialization.ts b/src/hooks/useInitialization.ts new file mode 100644 index 0000000..a77129d --- /dev/null +++ b/src/hooks/useInitialization.ts @@ -0,0 +1,23 @@ +import { useCallback, useEffect, useState } from 'react'; +import { createWeb3Wallet } from '../utils/wallet-connect/WalletConnectUtils'; + +export default function useInitialization() { + const [initialized, setInitialized] = useState(false); + + const onInitialize = useCallback(async () => { + try { + await createWeb3Wallet(); + setInitialized(true); + } catch (err: unknown) { + console.log('Error for initializing', err); + } + }, []); + + useEffect(() => { + if (!initialized) { + onInitialize(); + } + }, [initialized, onInitialize]); + + return initialized; +} diff --git a/hooks/usePrevious.ts b/src/hooks/usePrevious.ts similarity index 99% rename from hooks/usePrevious.ts rename to src/hooks/usePrevious.ts index 42a8bfb..5789c15 100644 --- a/hooks/usePrevious.ts +++ b/src/hooks/usePrevious.ts @@ -2,6 +2,7 @@ import { useEffect, useRef } from 'react'; export function usePrevious(value: T): T | undefined { const ref = useRef(value); + useEffect(() => { ref.current = value; }, [value]); diff --git a/components/AddSession.tsx b/src/screens/AddSession.tsx similarity index 100% rename from components/AddSession.tsx rename to src/screens/AddSession.tsx diff --git a/components/HomeScreen.tsx b/src/screens/HomeScreen.tsx similarity index 94% rename from components/HomeScreen.tsx rename to src/screens/HomeScreen.tsx index 4872aee..89d1ace 100644 --- a/components/HomeScreen.tsx +++ b/src/screens/HomeScreen.tsx @@ -7,11 +7,11 @@ import { useNavigation } from '@react-navigation/native'; import { getSdkError } from '@walletconnect/utils'; import { createWallet, resetWallet, retrieveAccounts } from '../utils/accounts'; -import { DialogComponent } from './Dialog'; -import { NetworkDropdown } from './NetworkDropdown'; -import Accounts from './Accounts'; -import CreateWallet from './CreateWallet'; -import ResetWalletDialog from './ResetWalletDialog'; +import { DialogComponent } from '../components/Dialog'; +import { NetworkDropdown } from '../components/NetworkDropdown'; +import Accounts from '../components/Accounts'; +import CreateWallet from '../components/CreateWallet'; +import ResetWalletDialog from '../components/ResetWalletDialog'; import styles from '../styles/stylesheet'; import { useAccounts } from '../context/AccountsContext'; import { useWalletConnect } from '../context/WalletConnectContext'; diff --git a/components/InvalidPath.tsx b/src/screens/InvalidPath.tsx similarity index 100% rename from components/InvalidPath.tsx rename to src/screens/InvalidPath.tsx diff --git a/components/SignMessage.tsx b/src/screens/SignMessage.tsx similarity index 96% rename from components/SignMessage.tsx rename to src/screens/SignMessage.tsx index a193741..39df73f 100644 --- a/components/SignMessage.tsx +++ b/src/screens/SignMessage.tsx @@ -7,7 +7,7 @@ import { NativeStackScreenProps } from '@react-navigation/native-stack'; import { StackParamsList } from '../types'; import styles from '../styles/stylesheet'; import { signMessage } from '../utils/sign-message'; -import AccountDetails from './AccountDetails'; +import AccountDetails from '../components/AccountDetails'; type SignProps = NativeStackScreenProps; diff --git a/components/SignRequest.tsx b/src/screens/SignRequest.tsx similarity index 86% rename from components/SignRequest.tsx rename to src/screens/SignRequest.tsx index b37efd9..8248d2c 100644 --- a/components/SignRequest.tsx +++ b/src/screens/SignRequest.tsx @@ -10,22 +10,20 @@ import { import { getHeaderTitle } from '@react-navigation/elements'; import { Account, StackParamsList } from '../types'; -import AccountDetails from './AccountDetails'; +import AccountDetails from '../components/AccountDetails'; import styles from '../styles/stylesheet'; import { signMessage } from '../utils/sign-message'; import { retrieveSingleAccount } from '../utils/accounts'; import { approveWalletConnectRequest, - rejectEIP155Request, + rejectWalletConnectRequest, } from '../utils/wallet-connect/WalletConnectRequests'; import { web3wallet } from '../utils/wallet-connect/WalletConnectUtils'; -import { useWalletConnect } from '../context/WalletConnectContext'; type SignRequestProps = NativeStackScreenProps; const SignRequest = ({ route }: SignRequestProps) => { - const { requestSession } = useWalletConnect(); - + const requestSession = route.params?.requestSessionData; const requestName = requestSession?.peer?.metadata?.name; const requestIcon = requestSession?.peer?.metadata?.icons[0]; const requestURL = requestSession?.peer?.metadata?.url; @@ -159,7 +157,7 @@ const SignRequest = ({ route }: SignRequestProps) => { const rejectRequestHandler = async () => { if (route.params?.requestEvent) { - const response = rejectEIP155Request(route.params?.requestEvent); + const response = rejectWalletConnectRequest(route.params?.requestEvent); const { topic } = route.params?.requestEvent; await web3wallet.respondSessionRequest({ topic, @@ -169,26 +167,29 @@ const SignRequest = ({ route }: SignRequestProps) => { navigation.navigate('Laconic'); }; - navigation.setOptions({ - // eslint-disable-next-line react/no-unstable-nested-components - header: ({ options, back }) => { - const title = getHeaderTitle(options, route.name); + useEffect(() => { + navigation.setOptions({ + // eslint-disable-next-line react/no-unstable-nested-components + header: ({ options, back }) => { + const title = getHeaderTitle(options, route.name); - return ( - - {back && ( - { - await rejectRequestHandler(); - navigation.navigate('Laconic'); - }} - /> - )} - - - ); - }, - }); + return ( + + {back && ( + { + await rejectRequestHandler(); + navigation.navigate('Laconic'); + }} + /> + )} + + + ); + }, + }); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [navigation, route.name]); return ( <> diff --git a/components/WalletConnect.tsx b/src/screens/WalletConnect.tsx similarity index 100% rename from components/WalletConnect.tsx rename to src/screens/WalletConnect.tsx diff --git a/styles/stylesheet.js b/src/styles/stylesheet.js similarity index 100% rename from styles/stylesheet.js rename to src/styles/stylesheet.js diff --git a/types.ts b/src/types.ts similarity index 87% rename from types.ts rename to src/types.ts index 998cb40..4ac0616 100644 --- a/types.ts +++ b/src/types.ts @@ -10,7 +10,7 @@ export type StackParamsList = { address: string; message: string; requestEvent?: Web3WalletTypes.SessionRequest; - requestSession?: any; + requestSessionData?: SessionTypes.Struct; } | undefined; InvalidPath: undefined; @@ -101,17 +101,7 @@ export interface PairingModalProps { setToastVisible: (arg1: boolean) => void; } -export interface SignModalProps { - visible: boolean; - setModalVisible: (arg1: boolean) => void; - requestSession: any; - requestEvent: SignClientTypes.EventArguments['session_request'] | undefined; - currentEthAddresses: string[]; -} - export interface WalletConnectContextProps { - requestSession: any; - setRequestSession: (requestSession: any) => void; activeSessions: Record; setActiveSessions: ( activeSessions: Record, diff --git a/utils/accounts.ts b/src/utils/accounts.ts similarity index 71% rename from utils/accounts.ts rename to src/utils/accounts.ts index 75e25d3..382d79f 100644 --- a/utils/accounts.ts +++ b/src/utils/accounts.ts @@ -12,19 +12,18 @@ import { getInternetCredentials, } from 'react-native-keychain'; +import { Secp256k1HdWallet } from '@cosmjs/amino'; +import { AccountData } from '@cosmjs/proto-signing'; +import { stringToPath } from '@cosmjs/crypto'; + import { Account, WalletDetails } from '../types'; import { - accountInfoFromHDPath, - getAddress, - getCosmosAccounts, getHDPath, getMnemonic, - getNextAccountId, getPathKey, resetKeyServers, - updateAccountIndices, updateGlobalCounter, -} from './utils'; +} from './misc'; const createWallet = async (): Promise => { try { @@ -240,6 +239,105 @@ const resetWallet = async () => { } }; +const accountInfoFromHDPath = async ( + hdPath: string, +): Promise< + | { privKey: string; pubKey: string; address: string; network: string } + | undefined +> => { + const mnemonicStore = await getInternetCredentials('mnemonicServer'); + if (!mnemonicStore) { + throw new Error('Mnemonic not found!'); + } + + const mnemonic = mnemonicStore.password; + const hdNode = HDNode.fromMnemonic(mnemonic); + const node = hdNode.derivePath(hdPath); + + const privKey = node.privateKey; + const pubKey = node.publicKey; + + const parts = hdPath.split('/'); + const path = parts.slice(-3).join('/'); + const coinType = parts[2]; + + let network: string; + let address: string; + + switch (coinType) { + case "60'": + network = 'eth'; + address = node.address; + break; + case "118'": + network = 'cosmos'; + address = (await getCosmosAccounts(mnemonic, path)).data.address; + break; + default: + throw new Error('Invalid wallet type'); + } + return { privKey, pubKey, address, network }; +}; + +const getNextAccountId = async (network: string): Promise => { + const idStore = await getInternetCredentials(`${network}:accountIndices`); + if (!idStore) { + throw new Error('Account id not found'); + } + + const accountIds = idStore.password; + const ids = accountIds.split(',').map(Number); + return ids[ids.length - 1] + 1; +}; + +const updateAccountIndices = async ( + network: string, + id: number, +): Promise => { + const idStore = await getInternetCredentials(`${network}:accountIndices`); + if (!idStore) { + throw new Error('Account id not found'); + } + + const updatedIndices = `${idStore.password},${id.toString()}`; + await resetInternetCredentials(`${network}:accountIndices`); + await setInternetCredentials( + `${network}:accountIndices`, + `${network}Counter`, + updatedIndices, + ); +}; + +const getCosmosAccounts = async ( + mnemonic: string, + path: string, +): Promise<{ cosmosWallet: Secp256k1HdWallet; data: AccountData }> => { + const cosmosWallet = await Secp256k1HdWallet.fromMnemonic(mnemonic, { + hdPaths: [stringToPath(`m/44'/118'/${path}`)], + }); + + const accountsData = await cosmosWallet.getAccounts(); + const data = accountsData[0]; + + return { cosmosWallet, data }; +}; + +const getAddress = async ( + network: string, + mnemonic: string, + path: string, +): Promise => { + switch (network) { + case 'eth': + return HDNode.fromMnemonic(mnemonic).derivePath(`m/44'/60'/${path}`) + .address; + case 'cosmos': + return (await getCosmosAccounts(mnemonic, `${path}`)).data.address; + default: + throw new Error('Invalid wallet type'); + } +}; + export { createWallet, addAccount, @@ -247,4 +345,8 @@ export { retrieveAccounts, retrieveSingleAccount, resetWallet, + accountInfoFromHDPath, + getNextAccountId, + updateAccountIndices, + getCosmosAccounts, }; diff --git a/utils/utils.ts b/src/utils/misc.ts similarity index 55% rename from utils/utils.ts rename to src/utils/misc.ts index b878eaa..801904f 100644 --- a/utils/utils.ts +++ b/src/utils/misc.ts @@ -4,14 +4,13 @@ import 'react-native-get-random-values'; import '@ethersproject/shims'; -import { HDNode } from 'ethers/lib/utils'; import { getInternetCredentials, resetInternetCredentials, setInternetCredentials, } from 'react-native-keychain'; -import { AccountData, Secp256k1HdWallet } from '@cosmjs/amino'; +import { AccountData } from '@cosmjs/amino'; import { DirectSecp256k1HdWallet } from '@cosmjs/proto-signing'; import { stringToPath } from '@cosmjs/crypto'; @@ -29,36 +28,6 @@ const getHDPath = (network: string, path: string): string => { return network === 'eth' ? `m/44'/60'/${path}` : `m/44'/118'/${path}`; }; -const getAddress = async ( - network: string, - mnemonic: string, - path: string, -): Promise => { - switch (network) { - case 'eth': - return HDNode.fromMnemonic(mnemonic).derivePath(`m/44'/60'/${path}`) - .address; - case 'cosmos': - return (await getCosmosAccounts(mnemonic, `${path}`)).data.address; - default: - throw new Error('Invalid wallet type'); - } -}; - -const getCosmosAccounts = async ( - mnemonic: string, - path: string, -): Promise<{ cosmosWallet: Secp256k1HdWallet; data: AccountData }> => { - const cosmosWallet = await Secp256k1HdWallet.fromMnemonic(mnemonic, { - hdPaths: [stringToPath(`m/44'/118'/${path}`)], - }); - - const accountsData = await cosmosWallet.getAccounts(); - const data = accountsData[0]; - - return { cosmosWallet, data }; -}; - export const getDirectWallet = async ( mnemonic: string, path: string, @@ -72,46 +41,6 @@ export const getDirectWallet = async ( return { directWallet, data }; }; -const accountInfoFromHDPath = async ( - hdPath: string, -): Promise< - | { privKey: string; pubKey: string; address: string; network: string } - | undefined -> => { - const mnemonicStore = await getInternetCredentials('mnemonicServer'); - if (!mnemonicStore) { - throw new Error('Mnemonic not found!'); - } - - const mnemonic = mnemonicStore.password; - const hdNode = HDNode.fromMnemonic(mnemonic); - const node = hdNode.derivePath(hdPath); - - const privKey = node.privateKey; - const pubKey = node.publicKey; - - const parts = hdPath.split('/'); - const path = parts.slice(-3).join('/'); - const coinType = parts[2]; - - let network: string; - let address: string; - - switch (coinType) { - case "60'": - network = 'eth'; - address = node.address; - break; - case "118'": - network = 'cosmos'; - address = (await getCosmosAccounts(mnemonic, path)).data.address; - break; - default: - throw new Error('Invalid wallet type'); - } - return { privKey, pubKey, address, network }; -}; - const getPathKey = async ( network: string, accountId: number, @@ -177,35 +106,6 @@ const updateGlobalCounter = async ( return { accountCounter: updatedAccountCounter, counterId }; }; -const getNextAccountId = async (network: string): Promise => { - const idStore = await getInternetCredentials(`${network}:accountIndices`); - if (!idStore) { - throw new Error('Account id not found'); - } - - const accountIds = idStore.password; - const ids = accountIds.split(',').map(Number); - return ids[ids.length - 1] + 1; -}; - -const updateAccountIndices = async ( - network: string, - id: number, -): Promise => { - const idStore = await getInternetCredentials(`${network}:accountIndices`); - if (!idStore) { - throw new Error('Account id not found'); - } - - const updatedIndices = `${idStore.password},${id.toString()}`; - await resetInternetCredentials(`${network}:accountIndices`); - await setInternetCredentials( - `${network}:accountIndices`, - `${network}Counter`, - updatedIndices, - ); -}; - const resetKeyServers = async (prefix: string) => { const idStore = await getInternetCredentials(`${prefix}:accountIndices`); if (!idStore) { @@ -222,14 +122,9 @@ const resetKeyServers = async (prefix: string) => { }; export { - accountInfoFromHDPath, - getCosmosAccounts, getMnemonic, getPathKey, - getNextAccountId, updateGlobalCounter, - updateAccountIndices, getHDPath, - getAddress, resetKeyServers, }; diff --git a/utils/sign-message.ts b/src/utils/sign-message.ts similarity index 96% rename from utils/sign-message.ts rename to src/utils/sign-message.ts index 4256d8a..384d27a 100644 --- a/utils/sign-message.ts +++ b/src/utils/sign-message.ts @@ -8,12 +8,8 @@ import { Wallet } from 'ethers'; import { SignDoc } from 'cosmjs-types/cosmos/tx/v1beta1/tx'; import { SignMessageParams } from '../types'; -import { - getCosmosAccounts, - getDirectWallet, - getMnemonic, - getPathKey, -} from './utils'; +import { getDirectWallet, getMnemonic, getPathKey } from './misc'; +import { getCosmosAccounts } from './accounts'; const signMessage = async ({ message, diff --git a/utils/wallet-connect/EIP155Lib.ts b/src/utils/wallet-connect/EIP155Lib.ts similarity index 100% rename from utils/wallet-connect/EIP155Lib.ts rename to src/utils/wallet-connect/EIP155Lib.ts diff --git a/utils/wallet-connect/Helpers.ts b/src/utils/wallet-connect/Helpers.ts similarity index 100% rename from utils/wallet-connect/Helpers.ts rename to src/utils/wallet-connect/Helpers.ts diff --git a/utils/wallet-connect/WalletConnectRequests.ts b/src/utils/wallet-connect/WalletConnectRequests.ts similarity index 94% rename from utils/wallet-connect/WalletConnectRequests.ts rename to src/utils/wallet-connect/WalletConnectRequests.ts index 57fe268..a4d42ff 100644 --- a/utils/wallet-connect/WalletConnectRequests.ts +++ b/src/utils/wallet-connect/WalletConnectRequests.ts @@ -7,7 +7,8 @@ import { getSdkError } from '@walletconnect/utils'; import { EIP155_SIGNING_METHODS } from './EIP155Lib'; import { signDirectMessage, signEthMessage } from '../sign-message'; import { Account } from '../../types'; -import { getCosmosAccounts, getMnemonic, getPathKey } from '../utils'; +import { getMnemonic, getPathKey } from '../misc'; +import { getCosmosAccounts } from '../accounts'; export async function approveWalletConnectRequest( requestEvent: SignClientTypes.EventArguments['session_request'], @@ -70,7 +71,7 @@ export async function approveWalletConnectRequest( } } -export function rejectEIP155Request( +export function rejectWalletConnectRequest( request: SignClientTypes.EventArguments['session_request'], ) { const { id } = request; diff --git a/utils/wallet-connect/WalletConnectUtils.tsx b/src/utils/wallet-connect/WalletConnectUtils.tsx similarity index 55% rename from utils/wallet-connect/WalletConnectUtils.tsx rename to src/utils/wallet-connect/WalletConnectUtils.tsx index e982a64..cf8329e 100644 --- a/utils/wallet-connect/WalletConnectUtils.tsx +++ b/src/utils/wallet-connect/WalletConnectUtils.tsx @@ -1,4 +1,3 @@ -import { useState, useCallback, useEffect } from 'react'; import Config from 'react-native-config'; import '@walletconnect/react-native-compat'; @@ -7,7 +6,7 @@ import { Core } from '@walletconnect/core'; import { ICore } from '@walletconnect/types'; import { Web3Wallet, IWeb3Wallet } from '@walletconnect/web3wallet'; -export let web3wallet: IWeb3Wallet; +export let web3wallet: IWeb3Wallet | undefined; export let core: ICore; export async function createWeb3Wallet() { @@ -26,27 +25,8 @@ export async function createWeb3Wallet() { }); } -export default function useInitialization() { - const [initialized, setInitialized] = useState(false); - - const onInitialize = useCallback(async () => { - try { - await createWeb3Wallet(); - setInitialized(true); - } catch (err: unknown) { - console.log('Error for initializing', err); - } - }, []); - - useEffect(() => { - if (!initialized) { - onInitialize(); - } - }, [initialized, onInitialize]); - - return initialized; -} - export async function web3WalletPair(params: { uri: string }) { - return await web3wallet.core.pairing.pair({ uri: params.uri }); + if (web3wallet) { + return await web3wallet.core.pairing.pair({ uri: params.uri }); + } } diff --git a/tsconfig.json b/tsconfig.json index 304ab4e..13b5ca4 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,3 +1,3 @@ { - "extends": "@react-native/typescript-config/tsconfig.json" + "extends": "@react-native/typescript-config/tsconfig.json", }