From 20dee55dcad3ab789fccc03760e562c703818ab5 Mon Sep 17 00:00:00 2001 From: AdityaSalunkhe21 Date: Wed, 9 Apr 2025 20:16:55 +0530 Subject: [PATCH] Use SignRequest --- src/App.tsx | 26 +++- src/components/SignRequestHandler.tsx | 192 ++++++++++++++++++-------- src/global.d.ts | 26 ++-- src/hooks/useGetOrCreateAccounts.ts | 3 - src/screens/SignMessage.tsx | 6 +- src/screens/SignRequest.tsx | 14 +- src/types.ts | 3 + 7 files changed, 192 insertions(+), 78 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index f297288..d6a52ec 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -40,6 +40,7 @@ import { WalletEmbed } from "./screens/WalletEmbed"; import { AutoSignIn } from "./screens/AutoSignIn"; import { checkSufficientFunds, getPathKey, sendMessage } from "./utils/misc"; import useAccountsData from "./hooks/useAccountsData"; +import { SignRequestAndroid } from "./components/SignRequestHandler"; const Stack = createStackNavigator(); @@ -132,16 +133,28 @@ const App = (): React.JSX.Element => { break; case EIP155_SIGNING_METHODS.PERSONAL_SIGN: + const chainId = request.params.chainId?.split(':')[1]; + const account = accounts.find(acc => acc.address === request.params[1]); + if (!account) { + throw new Error('Account not found'); + } navigation.navigate("SignRequest", { namespace: EIP155, + chainId, address: request.params[1], message: getSignParamsMessage(request.params), + accountInfo: account, requestEvent, requestSessionData, }); break; case COSMOS_METHODS.COSMOS_SIGN_DIRECT: + const cosmosChainId = request.params.chainId?.split(':')[1]; + const cosmosAccount = accounts.find(acc => acc.address === request.params.signerAddress); + if (!cosmosAccount) { + throw new Error('Account not found'); + } const message = { txbody: TxBody.toJSON( TxBody.decode( @@ -160,18 +173,27 @@ const App = (): React.JSX.Element => { }; navigation.navigate("SignRequest", { namespace: COSMOS, + chainId: cosmosChainId, address: request.params.signerAddress, message: JSON.stringify(message, undefined, 2), + accountInfo: cosmosAccount, requestEvent, requestSessionData, }); break; case COSMOS_METHODS.COSMOS_SIGN_AMINO: + const aminoChainId = request.params.chainId?.split(':')[1]; + const aminoAccount = accounts.find(acc => acc.address === request.params.signerAddress); + if (!aminoAccount) { + throw new Error('Account not found'); + } navigation.navigate("SignRequest", { namespace: COSMOS, + chainId: aminoChainId, address: request.params.signerAddress, message: request.params.signDoc.memo, + accountInfo: aminoAccount, requestEvent, requestSessionData, }); @@ -206,6 +228,7 @@ const App = (): React.JSX.Element => { setCurrentIndex, selectedNetwork, web3wallet, + accounts, ], ); @@ -254,7 +277,7 @@ const App = (): React.JSX.Element => { ).privKey; const sender = await DirectSecp256k1Wallet.fromKey( - Buffer.from(cosmosPrivKey.split('0x')[1], 'hex'), + Uint8Array.from(Buffer.from(cosmosPrivKey.split('0x')[1], 'hex')), network.addressPrefix ); @@ -402,6 +425,7 @@ const App = (): React.JSX.Element => { > Session approved + ); }; diff --git a/src/components/SignRequestHandler.tsx b/src/components/SignRequestHandler.tsx index 78808ee..d606ca8 100644 --- a/src/components/SignRequestHandler.tsx +++ b/src/components/SignRequestHandler.tsx @@ -1,4 +1,4 @@ -import React, { useState, useEffect, useRef } from 'react'; +import React, { useState, useEffect, useRef, useCallback } from 'react'; import { useNavigation } from '@react-navigation/native'; import { NativeStackNavigationProp } from '@react-navigation/native-stack'; import { useAccounts } from '../context/AccountsContext'; @@ -12,62 +12,158 @@ export const SignRequestAndroid: React.FC = () => { const { selectedNetwork } = useNetworks(); const pendingMessageRef = useRef(null); + const [isDataReady, setIsDataReady] = useState(false); // Run on mount useGetOrCreateAccounts(); - // Retry navigation if message is pending and accounts are ready + // Check if data is ready useEffect(() => { - if ( - pendingMessageRef.current && - selectedNetwork && - accounts && - accounts.length > 0 - ) { - const message = pendingMessageRef.current; - pendingMessageRef.current = null; + const logData = { + selectedNetwork: selectedNetwork ? { + namespace: selectedNetwork.namespace, + chainId: selectedNetwork.chainId, + } : 'undefined', + accounts: accounts ? `Array of length ${accounts.length}` : 'undefined', + currentIndex + }; + console.log('Checking data readiness:', JSON.stringify(logData, null, 2)); - navigation.reset({ - index: 0, - routes: [ - { - name: 'SignMessage', - params: { - selectedNamespace: selectedNetwork.namespace, - selectedChainId: selectedNetwork.chainId, - accountInfo: accounts[currentIndex], - prefillMessage: message, - }, - }, - ], - }); + if (selectedNetwork && accounts && accounts.length > 0) { + setIsDataReady(true); } - }, [accounts, selectedNetwork]); + }, [selectedNetwork, accounts, currentIndex]); - useEffect(() => { - window.receiveSignRequestFromAndroid = (message: string) => { - console.log('Sign request received with message:', message); + // Handle navigation to SignRequest screen + const navigateToSignRequest = useCallback(async (message: string) => { + const logData = { + isDataReady, + selectedNetwork: selectedNetwork ? { + namespace: selectedNetwork.namespace, + chainId: selectedNetwork.chainId, + } : 'undefined', + accounts: accounts ? `Array of length ${accounts.length}` : 'undefined', + currentIndex, + message + }; + console.log('Attempting to navigate with data:', JSON.stringify(logData, null, 2)); - if (!selectedNetwork || !accounts || accounts.length === 0) { - console.warn('Delaying sign request until data is ready...'); - pendingMessageRef.current = message; + if (!isDataReady) { + console.log('Delaying sign request until data is ready...'); + pendingMessageRef.current = message; + return; + } + + if (!selectedNetwork) { + console.error('No network selected'); + if (window.Android?.onSignatureError) { + window.Android.onSignatureError('No network selected'); + } + return; + } + + if (!accounts || accounts.length === 0) { + console.error('No accounts available'); + if (window.Android?.onSignatureError) { + window.Android.onSignatureError('No accounts available'); + } + return; + } + + const currentAccount = accounts[currentIndex]; + if (!currentAccount) { + console.error('Current account not found'); + if (window.Android?.onSignatureError) { + window.Android.onSignatureError('Current account not found'); + } + return; + } + + // Verify network properties exist before using them + if (!selectedNetwork.namespace || !selectedNetwork.chainId) { + const errorData = { + namespace: selectedNetwork.namespace, + chainId: selectedNetwork.chainId + }; + console.error('Network missing required properties:', JSON.stringify(errorData, null, 2)); + if (window.Android?.onSignatureError) { + window.Android.onSignatureError('Network missing required properties'); + } + return; + } + + const navigationData = { + namespace: selectedNetwork.namespace, + chainId: selectedNetwork.chainId, + address: currentAccount.address, + message + }; + console.log('Navigating to SignRequest with:', JSON.stringify(navigationData, null, 2)); + + try { + // Ensure the path matches the expected regex pattern: /sign/(eip155|cosmos)/(.+)/(.+)/(.+) + const path = `/sign/${selectedNetwork.namespace}/${selectedNetwork.chainId}/${currentAccount.address}/${encodeURIComponent(message)}`; + + // Verify the path matches the expected pattern + const pathRegex = /^\/sign\/(eip155|cosmos)\/(.+)\/(.+)\/(.+)$/; + if (!pathRegex.test(path)) { + console.error('Path does not match expected pattern:', path); + if (window.Android?.onSignatureError) { + window.Android.onSignatureError('Invalid path format'); + } return; } + // Parse the path to get the components + const match = path.match(pathRegex); + if (!match) { + console.error('Failed to parse path:', path); + if (window.Android?.onSignatureError) { + window.Android.onSignatureError('Failed to parse path'); + } + return; + } + + const [, pathNamespace, pathChainId, pathAddress, pathMessage] = match; + navigation.reset({ index: 0, routes: [ { - name: 'SignMessage', + name: 'SignRequest', + path: `/sign/${selectedNetwork.namespace}/${selectedNetwork.chainId}/${currentAccount.address}/${encodeURIComponent(message)}`, params: { - selectedNamespace: selectedNetwork.namespace, - selectedChainId: selectedNetwork.chainId, - accountInfo: accounts[currentIndex], - prefillMessage: message, + namespace: pathNamespace, + chainId: pathChainId, + address: pathAddress, + message: decodeURIComponent(pathMessage), + accountInfo: currentAccount, }, }, ], }); + } catch (error) { + console.error('Navigation error:', error); + if (window.Android?.onSignatureError) { + window.Android.onSignatureError(`Navigation error: ${error}`); + } + } + }, [isDataReady, selectedNetwork, accounts, currentIndex, navigation]); + + // Handle pending message when data becomes ready + useEffect(() => { + if (pendingMessageRef.current && isDataReady) { + const message = pendingMessageRef.current; + pendingMessageRef.current = null; + navigateToSignRequest(message); + } + }, [isDataReady, navigateToSignRequest]); + + // Setup Android bridge and message handler + useEffect(() => { + window.receiveSignRequestFromAndroid = (message: string) => { + console.log('Sign request received with message:', message); + navigateToSignRequest(message); }; if (window.Android) { @@ -80,29 +176,7 @@ export const SignRequestAndroid: React.FC = () => { return () => { window.receiveSignRequestFromAndroid = undefined; }; - }, []); + }, [navigateToSignRequest]); return null; }; - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/global.d.ts b/src/global.d.ts index 903501c..fc978ba 100644 --- a/src/global.d.ts +++ b/src/global.d.ts @@ -1,15 +1,19 @@ declare global { - interface Window { - Android: { - onSignatureComplete?: (signature: string) => void; - onSignatureError?: (error: string) => void; - onSignatureCancelled?: () => void; - onJsBridgeReady?: () => void; - }; - receiveTimeFromAndroid?: (timestamp: number) => void; - receiveDataFromAndroid?: (data: string) => void; - receiveSignRequestFromAndroid?: (message: string) => void; - } + interface Window { + Android: { + onSignatureComplete?: (signature: string) => void; + onSignatureError?: (error: string) => void; + onSignatureCancelled?: () => void; + onJsBridgeReady?: () => void; + }; + // Android WebView bridge methods (from TimeDisplay) + receiveTimeFromAndroid?: (timestamp: number) => void; + receiveDataFromAndroid?: (data: string) => void; + receiveSignRequestFromAndroid?: (message: string) => void; + + // Android native bridge object (from useGetOrCreateAccount) + } +} export {}; \ No newline at end of file diff --git a/src/hooks/useGetOrCreateAccounts.ts b/src/hooks/useGetOrCreateAccounts.ts index fc6bff4..edbe46c 100644 --- a/src/hooks/useGetOrCreateAccounts.ts +++ b/src/hooks/useGetOrCreateAccounts.ts @@ -20,7 +20,6 @@ const useGetOrCreateAccounts = (onWalletCreated?: () => void) => { // Re-fetch newly created accounts accountsData = await getAccountsData(event.data.chainId); - onWalletCreated?.(); } @@ -41,7 +40,6 @@ const useGetOrCreateAccounts = (onWalletCreated?: () => void) => { console.log("Auto-creating wallet..."); await createWallet(networksData); accountsData = await getAccountsData(defaultChainId); - onWalletCreated?.(); } }; @@ -49,7 +47,6 @@ const useGetOrCreateAccounts = (onWalletCreated?: () => void) => { window.addEventListener('message', handleCreateAccounts); const isAndroidWebView = !!(window.Android); - if (isAndroidWebView) { autoCreateAccounts(); } diff --git a/src/screens/SignMessage.tsx b/src/screens/SignMessage.tsx index 3883334..a96304d 100644 --- a/src/screens/SignMessage.tsx +++ b/src/screens/SignMessage.tsx @@ -23,8 +23,10 @@ const SignMessage = ({ route, navigation }: SignProps) => { useEffect(() => { if (prefillMessage) { setMessage(prefillMessage); - // Show confirmation dialog automatically when prefilled from Android - setShowConfirmDialog(true); + // Show confirmation dialog immediately + setTimeout(() => { + setShowConfirmDialog(true); + }, 0); } }, [prefillMessage]); diff --git a/src/screens/SignRequest.tsx b/src/screens/SignRequest.tsx index 3e0839d..9259ae7 100644 --- a/src/screens/SignRequest.tsx +++ b/src/screens/SignRequest.tsx @@ -202,7 +202,13 @@ const SignRequest = ({ route }: SignRequestProps) => { chainId, accountId: account.index, }); - alert(`Signature ${signedMessage}`); + + // Send the result back to Android and close dialog + if (window.Android?.onSignatureComplete) { + window.Android.onSignatureComplete(signedMessage || ""); + } else { + alert(`Signature: ${signedMessage}`); + } } }; @@ -230,7 +236,11 @@ const SignRequest = ({ route }: SignRequestProps) => { } setIsRejecting(false); - navigation.navigate('Home'); + if (window.Android?.onSignatureCancelled) { + window.Android.onSignatureCancelled(); + } else { + navigation.navigate('Home'); + } }; useEffect(() => { diff --git a/src/types.ts b/src/types.ts index a5b8dd0..8abcb46 100644 --- a/src/types.ts +++ b/src/types.ts @@ -13,9 +13,12 @@ export type StackParamsList = { prefillMessage?: string; }; SignRequest: { + path?: string; namespace: string; + chainId?: string; address: string; message: string; + accountInfo?: Account; requestEvent?: Web3WalletTypes.SessionRequest; requestSessionData?: SessionTypes.Struct; };