From b1cc35d4e07717c173faedac7443403c27105daf Mon Sep 17 00:00:00 2001 From: AdityaSalunkhe21 Date: Mon, 14 Apr 2025 13:18:23 +0530 Subject: [PATCH 01/15] Fix error when using build files --- src/hooks/useWebViewHandler.ts | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/hooks/useWebViewHandler.ts b/src/hooks/useWebViewHandler.ts index 7a0c99c..cca1a20 100644 --- a/src/hooks/useWebViewHandler.ts +++ b/src/hooks/useWebViewHandler.ts @@ -1,4 +1,4 @@ -import { useEffect, useCallback } from 'react'; +import { useEffect, useCallback, useState } from 'react'; import { useNavigation } from '@react-navigation/native'; import { NativeStackNavigationProp } from '@react-navigation/native-stack'; @@ -8,6 +8,9 @@ import { StackParamsList } from '../types'; import useGetOrCreateAccounts from './useGetOrCreateAccounts'; export const useWebViewHandler = () => { + const [isReady, setIsReady] = useState(false); + useGetOrCreateAccounts(); + // Navigation and context hooks const navigation = useNavigation>(); const { selectedNetwork } = useNetworks(); @@ -19,6 +22,13 @@ export const useWebViewHandler = () => { // Core navigation handler const navigateToSignRequest = useCallback((message: string) => { try { + // Wait for accounts to be ready + if (!isReady) { + console.log('Accounts not yet ready'); + window.Android?.onSignatureError?.('Accounts not yet ready'); + return; + } + // Validation checks if (!selectedNetwork?.namespace || !selectedNetwork?.chainId) { window.Android?.onSignatureError?.('Invalid network configuration'); @@ -68,7 +78,7 @@ export const useWebViewHandler = () => { } catch (error) { window.Android?.onSignatureError?.(`Navigation error: ${error}`); } - }, [selectedNetwork, accounts, currentIndex, navigation]); + }, [selectedNetwork, accounts, currentIndex, navigation, isReady]); useEffect(() => { // Assign the function to the window object -- 2.45.2 From f2e5ba0a565cfebff2c7316cc7fe970a8bae3428 Mon Sep 17 00:00:00 2001 From: AdityaSalunkhe21 Date: Fri, 11 Apr 2025 10:20:53 +0530 Subject: [PATCH 02/15] Update ApproveTransfer for android --- src/screens/ApproveTransfer.tsx | 436 +++++++++++++++++-------- src/utils/constants.ts | 6 +- src/utils/wallet-connect/COSMOSData.ts | 6 +- 3 files changed, 303 insertions(+), 145 deletions(-) diff --git a/src/screens/ApproveTransfer.tsx b/src/screens/ApproveTransfer.tsx index 34fcdff..ea50bfd 100644 --- a/src/screens/ApproveTransfer.tsx +++ b/src/screens/ApproveTransfer.tsx @@ -46,24 +46,29 @@ export const MEMO = 'Sending signed tx from Laconic Wallet'; // Reference: https://ethereum.org/en/developers/docs/gas/#what-is-gas-limit const ETH_MINIMUM_GAS = 21000; -type SignRequestProps = NativeStackScreenProps< - StackParamsList, - 'ApproveTransfer' ->; +type ApproveTransferProps = NativeStackScreenProps & { + route: { + params: { + transaction: any; + requestEvent?: { + params: { + chainId: string; + request: { + method: string; + }; + }; + }; + requestSessionData?: any; + chainId?: string; + }; + path?: string; + }; +}; -const ApproveTransfer = ({ route }: SignRequestProps) => { +const ApproveTransfer = ({ route }: ApproveTransferProps) => { const { networksData } = useNetworks(); const { web3wallet } = 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; - const transaction = route.params.transaction; - const requestEvent = route.params.requestEvent; - const chainId = requestEvent.params.chainId; - const requestMethod = requestEvent.params.request.method; - const [account, setAccount] = useState(); const [isLoading, setIsLoading] = useState(true); const [balance, setBalance] = useState(''); @@ -80,6 +85,80 @@ const ApproveTransfer = ({ route }: SignRequestProps) => { const [ethMaxPriorityFee, setEthMaxPriorityFee] = useState(); + const navigation = + useNavigation>(); + + // Extract data from route params or path + const requestSession = route.params?.requestSessionData; + const requestName = requestSession?.peer?.metadata?.name; + const requestIcon = requestSession?.peer?.metadata?.icons?.[0]; + const requestURL = requestSession?.peer?.metadata?.url; + const transaction = route.params?.transaction; + const requestEvent = route.params?.requestEvent; + const chainId = requestEvent?.params?.chainId || route.params?.chainId; + const requestMethod = requestEvent?.params?.request?.method; + + const sanitizePath = useCallback((path: string) => { + const regex = /^\/transfer\/(eip155|cosmos)\/(.+)\/(.+)\/(.+)\/(.+)$/; + const match = path.match(regex); + if (match) { + const [, pathNamespace, pathChainId, pathAddress, pathTo, pathAmount] = match; + return { + namespace: pathNamespace, + chainId: pathChainId, + address: pathAddress, + to: pathTo, + amount: pathAmount, + }; + } else { + navigation.navigate('InvalidPath'); + } + return null; + }, [navigation]); + + const retrieveData = useCallback(async (requestNamespace: string, requestChainId: string, requestAddress: string) => { + const requestAccount = await retrieveSingleAccount( + requestNamespace, + requestChainId, + requestAddress, + ); + if (!requestAccount) { + navigation.navigate('InvalidPath'); + return; + } + + setAccount(requestAccount); + }, [navigation]); + + useEffect(() => { + if (route.path) { + const sanitizedRoute = sanitizePath(route.path); + if (sanitizedRoute) { + retrieveData( + sanitizedRoute.namespace, + sanitizedRoute.chainId, + sanitizedRoute.address, + ); + return; + } + } + + if (requestEvent) { + const requestedNetwork = networksData.find( + networkData => { + return `${networkData.namespace}:${networkData.chainId}` === chainId; + } + ); + if (requestedNetwork && transaction?.from) { + retrieveData( + requestedNetwork.namespace, + requestedNetwork.chainId, + transaction.from, + ); + } + } + }, [retrieveData, sanitizePath, route, networksData, requestEvent, chainId, transaction]); + const isSufficientFunds = useMemo(() => { if (!transaction.value) { return; @@ -139,7 +218,7 @@ const ApproveTransfer = ({ route }: SignRequestProps) => { ).privKey; const sender = await DirectSecp256k1Wallet.fromKey( - Buffer.from(cosmosPrivKey.split('0x')[1], 'hex'), + Uint8Array.from(Buffer.from(cosmosPrivKey.split('0x')[1], 'hex')), requestedNetwork?.addressPrefix, ); @@ -185,26 +264,6 @@ const ApproveTransfer = ({ route }: SignRequestProps) => { } }, [requestedNetwork, namespace]); - const navigation = - useNavigation>(); - - const retrieveData = useCallback( - async (requestAddress: string) => { - const requestAccount = await retrieveSingleAccount( - requestedNetwork!.namespace, - requestedNetwork!.chainId, - requestAddress, - ); - if (!requestAccount) { - navigation.navigate('InvalidPath'); - return; - } - - setAccount(requestAccount); - }, - [navigation, requestedNetwork], - ); - useEffect(() => { // Set loading to false when gas values for requested chain are fetched // If requested chain is EVM compatible, the cosmos gas values will be undefined and vice-versa, hence the condition checks only one of them at the same time @@ -246,9 +305,6 @@ const ApproveTransfer = ({ route }: SignRequestProps) => { requestedNetwork, ethMaxFee, ]); - useEffect(() => { - retrieveData(transaction.from!); - }, [retrieveData, transaction]); const isEIP1559 = useMemo(() => { if (cosmosGasLimit) { @@ -260,6 +316,101 @@ const ApproveTransfer = ({ route }: SignRequestProps) => { return false; }, [cosmosGasLimit, ethMaxFee, ethMaxPriorityFee]); + const handleIntent = async () => { + if (!account) { + throw new Error('Account is not valid'); + } + + if (route.path) { + const sanitizedRoute = sanitizePath(route.path); + if (!sanitizedRoute) { + throw new Error('Invalid path'); + } + + const requestedNetwork = networksData.find( + networkData => networkData.chainId === sanitizedRoute.chainId, + ); + + if (!requestedNetwork) { + throw new Error('Network not found'); + } + + const cosmosPrivKey = ( + await getPathKey( + `${requestedNetwork.namespace}:${requestedNetwork.chainId}`, + account.index, + ) + ).privKey; + + const sender = await DirectSecp256k1Wallet.fromKey( + Uint8Array.from(Buffer.from(cosmosPrivKey.split('0x')[1], 'hex')), + requestedNetwork.addressPrefix, + ); + + const client = await SigningStargateClient.connectWithSigner( + requestedNetwork.rpcUrl!, + sender, + ); + + const sendMsg: MsgSendEncodeObject = { + typeUrl: '/cosmos.bank.v1beta1.MsgSend', + value: { + fromAddress: account.address, + toAddress: sanitizedRoute.to, + amount: [ + { + amount: String(sanitizedRoute.amount), + denom: requestedNetwork.nativeDenom!, + }, + ], + }, + }; + + const gasEstimation = await client.simulate( + account.address, + [sendMsg], + MEMO, + ); + + const gasLimit = String( + Math.round(gasEstimation * Number(process.env.REACT_APP_GAS_ADJUSTMENT)), + ); + + const gasPrice = GasPrice.fromString( + requestedNetwork.gasPrice! + requestedNetwork.nativeDenom, + ); + + const cosmosFees = calculateFee(Number(gasLimit), gasPrice); + + const result = await client.signAndBroadcast( + account.address, + [sendMsg], + { + amount: [ + { + amount: cosmosFees.amount[0].amount, + denom: requestedNetwork.nativeDenom!, + }, + ], + gas: gasLimit, + }, + MEMO, + ); + + // Convert BigInt values to strings before sending to Android + const serializedResult = JSON.stringify(result, (key, value) => + typeof value === 'bigint' ? value.toString() : value + ); + + // Send the result back to Android and close dialog + if (window.Android?.onTransferComplete) { + window.Android.onTransferComplete(serializedResult); + } else { + alert(`Transaction: ${serializedResult}`); + } + } + }; + const acceptRequestHandler = async () => { setIsTxLoading(true); try { @@ -267,77 +418,80 @@ const ApproveTransfer = ({ route }: SignRequestProps) => { throw new Error('account not found'); } - if (ethGasLimit && ethGasLimit.lt(ETH_MINIMUM_GAS)) { - throw new Error(`Atleast ${ETH_MINIMUM_GAS} gas limit is required`); - } + if (requestEvent) { + // Handle WalletConnect request + if (ethGasLimit && ethGasLimit.lt(ETH_MINIMUM_GAS)) { + throw new Error(`Atleast ${ETH_MINIMUM_GAS} gas limit is required`); + } - if (ethMaxFee && ethMaxPriorityFee && ethMaxFee.lte(ethMaxPriorityFee)) { - throw new Error( - `Max fee per gas (${ethMaxFee.toNumber()}) cannot be lower than or equal to max priority fee per gas (${ethMaxPriorityFee.toNumber()})`, + if (ethMaxFee && ethMaxPriorityFee && ethMaxFee.lte(ethMaxPriorityFee)) { + throw new Error( + `Max fee per gas (${ethMaxFee.toNumber()}) cannot be lower than or equal to max priority fee per gas (${ethMaxPriorityFee.toNumber()})`, + ); + } + + let options: WalletConnectRequests; + + switch (requestMethod) { + case EIP155_SIGNING_METHODS.ETH_SEND_TRANSACTION: + if ( + ethMaxFee === undefined || + ethMaxPriorityFee === undefined || + ethGasPrice === undefined + ) { + throw new Error('Gas values not found'); + } + + options = { + type: 'eth_sendTransaction', + provider: provider!, + ethGasLimit: BigNumber.from(ethGasLimit), + ethGasPrice: ethGasPrice ? ethGasPrice.toHexString() : null, + maxFeePerGas: ethMaxFee, + maxPriorityFeePerGas: ethMaxPriorityFee, + }; + break; + case COSMOS_METHODS.COSMOS_SEND_TOKENS: + if (!cosmosStargateClient) { + throw new Error('Cosmos stargate client not found'); + } + + options = { + type: 'cosmos_sendTokens', + signingStargateClient: cosmosStargateClient, + cosmosFee: { + amount: [ + { + amount: fees, + denom: requestedNetwork!.nativeDenom!, + }, + ], + gas: cosmosGasLimit, + }, + sendMsg, + memo: MEMO, + }; + break; + default: + throw new Error('Invalid method'); + } + + const response = await approveWalletConnectRequest( + requestEvent, + account!, + namespace, + requestedNetwork!.chainId, + options, ); + + const { topic } = requestEvent; + await web3wallet!.respondSessionRequest({ topic, response }); + navigation.navigate('Home'); + } else { + // Handle direct intent + await handleIntent(); + navigation.navigate('Home'); } - - let options: WalletConnectRequests; - - switch (requestMethod) { - case EIP155_SIGNING_METHODS.ETH_SEND_TRANSACTION: - if ( - ethMaxFee === undefined || - ethMaxPriorityFee === undefined || - ethGasPrice === undefined - ) { - throw new Error('Gas values not found'); - } - - options = { - type: 'eth_sendTransaction', - provider: provider!, - ethGasLimit: BigNumber.from(ethGasLimit), - ethGasPrice: ethGasPrice ? ethGasPrice.toHexString() : null, - maxFeePerGas: ethMaxFee, - maxPriorityFeePerGas: ethMaxPriorityFee, - }; - break; - case COSMOS_METHODS.COSMOS_SEND_TOKENS: - if (!cosmosStargateClient) { - throw new Error('Cosmos stargate client not found'); - } - - options = { - type: 'cosmos_sendTokens', - signingStargateClient: cosmosStargateClient, - // StdFee object - cosmosFee: { - // This amount is total fees required for transaction - amount: [ - { - amount: fees, - denom: requestedNetwork!.nativeDenom!, - }, - ], - gas: cosmosGasLimit, - }, - sendMsg, - memo: MEMO, - }; - - break; - - default: - throw new Error('Invalid method'); - } - - const response = await approveWalletConnectRequest( - requestEvent, - account, - namespace, - requestedNetwork!.chainId, - options, - ); - - const { topic } = requestEvent; - await web3wallet!.respondSessionRequest({ topic, response }); - navigation.navigate('Home'); } catch (error) { if (!(error instanceof Error)) { throw error; @@ -350,20 +504,26 @@ const ApproveTransfer = ({ route }: SignRequestProps) => { }; const rejectRequestHandler = async () => { - const response = rejectWalletConnectRequest(requestEvent); - const { topic } = requestEvent; - await web3wallet!.respondSessionRequest({ - topic, - response, - }); + if (requestEvent) { + const response = rejectWalletConnectRequest(requestEvent); + const { topic } = requestEvent; + await web3wallet!.respondSessionRequest({ + topic, + response, + }); + } - navigation.navigate('Home'); + if (window.Android?.onTransferCancelled) { + window.Android.onTransferCancelled(); + } else { + navigation.navigate('Home'); + } }; useEffect(() => { const getAccountBalance = async () => { try { - if (!account) { + if (!account || !requestedNetwork) { return; } if (namespace === EIP155) { @@ -373,20 +533,20 @@ const ApproveTransfer = ({ route }: SignRequestProps) => { const fetchedBalance = await provider.getBalance(account.address); setBalance(fetchedBalance ? fetchedBalance.toString() : '0'); } else { - const cosmosBalance = await cosmosStargateClient?.getBalance( + if (!cosmosStargateClient) { + return; + } + const cosmosBalance = await cosmosStargateClient.getBalance( account.address, - requestedNetwork!.nativeDenom!.toLowerCase(), + requestedNetwork.nativeDenom!.toLowerCase(), ); - - setBalance(cosmosBalance?.amount!); + setBalance(cosmosBalance?.amount || '0'); } } catch (error) { - if (!(error instanceof Error)) { - throw error; - } - - setTxError(error.message); - setIsTxErrorDialogOpen(true); + console.error('Error fetching balance:', error); + setBalance('0'); + // Don't show error dialog for balance fetch failures + // Just set balance to 0 and let the transaction proceed } }; @@ -508,16 +668,18 @@ const ApproveTransfer = ({ route }: SignRequestProps) => { ) : ( <> - - {requestIcon && ( - - )} - {requestName} - {requestURL} - + {requestSession && ( + + {requestIcon && ( + + )} + {requestName} + {requestURL} + + )} From @@ -528,11 +690,7 @@ const ApproveTransfer = ({ route }: SignRequestProps) => { label={`Balance (${ namespace === EIP155 ? 'wei' : requestedNetwork!.nativeDenom })`} - data={ - balance === '' || balance === undefined - ? 'Loading balance...' - : `${balance}` - } + data={balance || '0'} /> {transaction && ( diff --git a/src/utils/constants.ts b/src/utils/constants.ts index 3f93d29..2eea12c 100644 --- a/src/utils/constants.ts +++ b/src/utils/constants.ts @@ -41,10 +41,10 @@ export const DEFAULT_NETWORKS: NetworksFormData[] = [ isDefault: true, }, { - chainId: 'theta-testnet-001', - networkName: COSMOS_TESTNET_CHAINS['cosmos:theta-testnet-001'].name, + chainId: 'provider', + networkName: COSMOS_TESTNET_CHAINS['cosmos:provider'].name, namespace: COSMOS, - rpcUrl: COSMOS_TESTNET_CHAINS['cosmos:theta-testnet-001'].rpc, + rpcUrl: COSMOS_TESTNET_CHAINS['cosmos:provider'].rpc, blockExplorerUrl: '', nativeDenom: 'uatom', addressPrefix: 'cosmos', diff --git a/src/utils/wallet-connect/COSMOSData.ts b/src/utils/wallet-connect/COSMOSData.ts index a1c6baf..2a9c50d 100644 --- a/src/utils/wallet-connect/COSMOSData.ts +++ b/src/utils/wallet-connect/COSMOSData.ts @@ -19,10 +19,10 @@ export const COSMOS_TESTNET_CHAINS: Record< namespace: string; } > = { - 'cosmos:theta-testnet-001': { - chainId: 'theta-testnet-001', + 'cosmos:provider': { + chainId: 'provider', name: 'Cosmos Hub Testnet', - rpc: 'https://rpc-t.cosmos.nodestake.top', + rpc: 'https://rpc-rs.cosmos.nodestake.top', namespace: 'cosmos', }, }; -- 2.45.2 From 49cada4fc8e75e02226d0e268c63f316a5021859 Mon Sep 17 00:00:00 2001 From: AdityaSalunkhe21 Date: Sat, 12 Apr 2025 12:00:30 +0530 Subject: [PATCH 03/15] Update useWebViewHandler for token transfer --- src/global.d.ts | 12 ++++ src/hooks/useWebViewHandler.ts | 109 +++++++++++++++++++++++++++++---- 2 files changed, 109 insertions(+), 12 deletions(-) diff --git a/src/global.d.ts b/src/global.d.ts index dfb6ad7..e9d6af1 100644 --- a/src/global.d.ts +++ b/src/global.d.ts @@ -14,10 +14,22 @@ declare global { // Called when accounts are ready for use onAccountsReady?: () => void; + + // Called when transfer is successfully completed + onTransferComplete?: (result: string) => void; + + // Called when transfer fails + onTransferError?: (error: string) => void; + + // Called when transfer is cancelled + onTransferCancelled?: () => void; }; // Handles incoming signature requests from Android receiveSignRequestFromAndroid?: (message: string) => void; + + // Handles incoming transfer requests from Android + receiveTransferRequestFromAndroid?: (to: string, amount: string) => void; } } diff --git a/src/hooks/useWebViewHandler.ts b/src/hooks/useWebViewHandler.ts index cca1a20..1e08ccb 100644 --- a/src/hooks/useWebViewHandler.ts +++ b/src/hooks/useWebViewHandler.ts @@ -6,11 +6,9 @@ import { useAccounts } from '../context/AccountsContext'; import { useNetworks } from '../context/NetworksContext'; import { StackParamsList } from '../types'; import useGetOrCreateAccounts from './useGetOrCreateAccounts'; +import { retrieveAccountsForNetwork } from '../utils/accounts'; export const useWebViewHandler = () => { - const [isReady, setIsReady] = useState(false); - useGetOrCreateAccounts(); - // Navigation and context hooks const navigation = useNavigation>(); const { selectedNetwork } = useNetworks(); @@ -22,13 +20,6 @@ export const useWebViewHandler = () => { // Core navigation handler const navigateToSignRequest = useCallback((message: string) => { try { - // Wait for accounts to be ready - if (!isReady) { - console.log('Accounts not yet ready'); - window.Android?.onSignatureError?.('Accounts not yet ready'); - return; - } - // Validation checks if (!selectedNetwork?.namespace || !selectedNetwork?.chainId) { window.Android?.onSignatureError?.('Invalid network configuration'); @@ -78,14 +69,108 @@ export const useWebViewHandler = () => { } catch (error) { window.Android?.onSignatureError?.(`Navigation error: ${error}`); } - }, [selectedNetwork, accounts, currentIndex, navigation, isReady]); + }, [selectedNetwork, accounts, currentIndex, navigation]); + + // Handle incoming transfer requests + const navigateToTransfer = useCallback(async (to: string, amount: string) => { + if (!accounts || accounts.length === 0) { + console.error('No accounts available'); + if (window.Android?.onTransferError) { + window.Android.onTransferError('No accounts available'); + } + return; + } + + const currentAccount = accounts[currentIndex]; + if (!currentAccount) { + console.error('Current account not found'); + if (window.Android?.onTransferError) { + window.Android.onTransferError('Current account not found'); + } + return; + } + + // Use Cosmos Hub Testnet network + const cosmosHubTestnet = { + namespace: 'cosmos', + chainId: 'provider', + addressPrefix: 'cosmos' + }; + + try { + // Get all accounts for Cosmos Hub Testnet + const cosmosAccounts = await retrieveAccountsForNetwork( + `${cosmosHubTestnet.namespace}:${cosmosHubTestnet.chainId}`, + '0' // Use the first account + ); + + if (!cosmosAccounts || cosmosAccounts.length === 0) { + console.error('No Cosmos Hub Testnet accounts found'); + if (window.Android?.onTransferError) { + window.Android.onTransferError('No Cosmos Hub Testnet accounts found'); + } + return; + } + + const cosmosAccount = cosmosAccounts[0]; // Use the first account + + const path = `/transfer/${cosmosHubTestnet.namespace}/${cosmosHubTestnet.chainId}/${cosmosAccount.address}/${to}/${amount}`; + + const pathRegex = /^\/transfer\/(eip155|cosmos)\/(.+)\/(.+)\/(.+)\/(.+)$/; + if (!pathRegex.test(path)) { + console.error('Path does not match expected pattern:', path); + if (window.Android?.onTransferError) { + window.Android.onTransferError('Invalid path format'); + } + return; + } + + const match = path.match(pathRegex); + if (!match) { + console.error('Failed to parse path:', path); + if (window.Android?.onTransferError) { + window.Android.onTransferError('Failed to parse path'); + } + return; + } + + navigation.reset({ + index: 0, + routes: [ + { + name: 'ApproveTransfer', + path: `/transfer/${cosmosHubTestnet.namespace}/${cosmosHubTestnet.chainId}/${cosmosAccount.address}/${to}/${amount}`, + params: { + namespace: cosmosHubTestnet.namespace, + chainId: `${cosmosHubTestnet.namespace}:${cosmosHubTestnet.chainId}`, + transaction: { + from: cosmosAccount.address, + to: to, + value: amount, + data: '' + }, + accountInfo: cosmosAccount, + }, + }, + ], + }); + } catch (error) { + console.error('Navigation error:', error); + if (window.Android?.onTransferError) { + window.Android.onTransferError(`Navigation error: ${error}`); + } + } + }, [accounts, currentIndex, navigation]); useEffect(() => { // Assign the function to the window object window.receiveSignRequestFromAndroid = navigateToSignRequest; + window.receiveTransferRequestFromAndroid = navigateToTransfer; + return () => { window.receiveSignRequestFromAndroid = undefined; + window.receiveTransferRequestFromAndroid = undefined; }; - }, [navigateToSignRequest]); // Only the function reference as dependency + }, [navigateToSignRequest, navigateToTransfer]); // Only the function reference as dependency }; -- 2.45.2 From 17669b834d7ea71706c227c6a40e3c0b126c4593 Mon Sep 17 00:00:00 2001 From: AdityaSalunkhe21 Date: Mon, 14 Apr 2025 15:19:25 +0530 Subject: [PATCH 04/15] Remove isReady check --- src/hooks/useWebViewHandler.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hooks/useWebViewHandler.ts b/src/hooks/useWebViewHandler.ts index 1e08ccb..80f22b9 100644 --- a/src/hooks/useWebViewHandler.ts +++ b/src/hooks/useWebViewHandler.ts @@ -1,4 +1,4 @@ -import { useEffect, useCallback, useState } from 'react'; +import { useEffect, useCallback } from 'react'; import { useNavigation } from '@react-navigation/native'; import { NativeStackNavigationProp } from '@react-navigation/native-stack'; -- 2.45.2 From 6515a22abce82c08f2c5449e1a5c912fe29b822a Mon Sep 17 00:00:00 2001 From: AdityaSalunkhe21 Date: Wed, 16 Apr 2025 20:12:04 +0530 Subject: [PATCH 05/15] Remove unnecessary code from ApproveTransfer --- src/screens/ApproveTransfer.tsx | 147 ++++++++++++-------------------- src/types.ts | 1 + 2 files changed, 55 insertions(+), 93 deletions(-) diff --git a/src/screens/ApproveTransfer.tsx b/src/screens/ApproveTransfer.tsx index ea50bfd..db40973 100644 --- a/src/screens/ApproveTransfer.tsx +++ b/src/screens/ApproveTransfer.tsx @@ -46,29 +46,22 @@ export const MEMO = 'Sending signed tx from Laconic Wallet'; // Reference: https://ethereum.org/en/developers/docs/gas/#what-is-gas-limit const ETH_MINIMUM_GAS = 21000; -type ApproveTransferProps = NativeStackScreenProps & { - route: { - params: { - transaction: any; - requestEvent?: { - params: { - chainId: string; - request: { - method: string; - }; - }; - }; - requestSessionData?: any; - chainId?: string; - }; - path?: string; - }; -}; +type ApproveTransferProps = NativeStackScreenProps const ApproveTransfer = ({ route }: ApproveTransferProps) => { const { networksData } = useNetworks(); const { web3wallet } = useWalletConnect(); + // Extract data from route params or path + const requestSession = route.params?.requestSessionData; + const requestName = requestSession?.peer?.metadata?.name; + const requestIcon = requestSession?.peer?.metadata?.icons?.[0]; + const requestURL = requestSession?.peer?.metadata?.url; + const transaction = route.params?.transaction; + const requestEvent = route.params?.requestEvent; + const chainId = requestEvent?.params?.chainId || route.params?.chainId; + const requestMethod = requestEvent?.params?.request?.method; + const [account, setAccount] = useState(); const [isLoading, setIsLoading] = useState(true); const [balance, setBalance] = useState(''); @@ -85,80 +78,6 @@ const ApproveTransfer = ({ route }: ApproveTransferProps) => { const [ethMaxPriorityFee, setEthMaxPriorityFee] = useState(); - const navigation = - useNavigation>(); - - // Extract data from route params or path - const requestSession = route.params?.requestSessionData; - const requestName = requestSession?.peer?.metadata?.name; - const requestIcon = requestSession?.peer?.metadata?.icons?.[0]; - const requestURL = requestSession?.peer?.metadata?.url; - const transaction = route.params?.transaction; - const requestEvent = route.params?.requestEvent; - const chainId = requestEvent?.params?.chainId || route.params?.chainId; - const requestMethod = requestEvent?.params?.request?.method; - - const sanitizePath = useCallback((path: string) => { - const regex = /^\/transfer\/(eip155|cosmos)\/(.+)\/(.+)\/(.+)\/(.+)$/; - const match = path.match(regex); - if (match) { - const [, pathNamespace, pathChainId, pathAddress, pathTo, pathAmount] = match; - return { - namespace: pathNamespace, - chainId: pathChainId, - address: pathAddress, - to: pathTo, - amount: pathAmount, - }; - } else { - navigation.navigate('InvalidPath'); - } - return null; - }, [navigation]); - - const retrieveData = useCallback(async (requestNamespace: string, requestChainId: string, requestAddress: string) => { - const requestAccount = await retrieveSingleAccount( - requestNamespace, - requestChainId, - requestAddress, - ); - if (!requestAccount) { - navigation.navigate('InvalidPath'); - return; - } - - setAccount(requestAccount); - }, [navigation]); - - useEffect(() => { - if (route.path) { - const sanitizedRoute = sanitizePath(route.path); - if (sanitizedRoute) { - retrieveData( - sanitizedRoute.namespace, - sanitizedRoute.chainId, - sanitizedRoute.address, - ); - return; - } - } - - if (requestEvent) { - const requestedNetwork = networksData.find( - networkData => { - return `${networkData.namespace}:${networkData.chainId}` === chainId; - } - ); - if (requestedNetwork && transaction?.from) { - retrieveData( - requestedNetwork.namespace, - requestedNetwork.chainId, - transaction.from, - ); - } - } - }, [retrieveData, sanitizePath, route, networksData, requestEvent, chainId, transaction]); - const isSufficientFunds = useMemo(() => { if (!transaction.value) { return; @@ -218,7 +137,7 @@ const ApproveTransfer = ({ route }: ApproveTransferProps) => { ).privKey; const sender = await DirectSecp256k1Wallet.fromKey( - Uint8Array.from(Buffer.from(cosmosPrivKey.split('0x')[1], 'hex')), + Buffer.from(cosmosPrivKey.split('0x')[1], 'hex'), requestedNetwork?.addressPrefix, ); @@ -264,6 +183,44 @@ const ApproveTransfer = ({ route }: ApproveTransferProps) => { } }, [requestedNetwork, namespace]); + const navigation = + useNavigation>(); + + const sanitizePath = useCallback((path: string) => { + const regex = /^\/transfer\/(eip155|cosmos)\/(.+)\/(.+)\/(.+)\/(.+)$/; + const match = path.match(regex); + if (match) { + const [, pathNamespace, pathChainId, pathAddress, pathTo, pathAmount] = match; + return { + namespace: pathNamespace, + chainId: pathChainId, + address: pathAddress, + to: pathTo, + amount: pathAmount, + }; + } else { + navigation.navigate('InvalidPath'); + } + return null; + }, [navigation]); + + const retrieveData = useCallback( + async (requestAddress: string) => { + const requestAccount = await retrieveSingleAccount( + requestedNetwork!.namespace, + requestedNetwork!.chainId, + requestAddress, + ); + if (!requestAccount) { + navigation.navigate('InvalidPath'); + return; + } + + setAccount(requestAccount); + }, + [navigation, requestedNetwork], + ); + useEffect(() => { // Set loading to false when gas values for requested chain are fetched // If requested chain is EVM compatible, the cosmos gas values will be undefined and vice-versa, hence the condition checks only one of them at the same time @@ -306,6 +263,10 @@ const ApproveTransfer = ({ route }: ApproveTransferProps) => { ethMaxFee, ]); + useEffect(() => { + retrieveData(transaction.from!); + }, [retrieveData, transaction]); + const isEIP1559 = useMemo(() => { if (cosmosGasLimit) { return; diff --git a/src/types.ts b/src/types.ts index f75f10b..0631492 100644 --- a/src/types.ts +++ b/src/types.ts @@ -21,6 +21,7 @@ export type StackParamsList = { requestSessionData?: SessionTypes.Struct; }; ApproveTransfer: { + chainId?: string; transaction: PopulatedTransaction; requestEvent: Web3WalletTypes.SessionRequest; requestSessionData: SessionTypes.Struct; -- 2.45.2 From 79eda18185fc5bc09f4db0926cf517d350666a4f Mon Sep 17 00:00:00 2001 From: AdityaSalunkhe21 Date: Thu, 17 Apr 2025 11:34:58 +0530 Subject: [PATCH 06/15] Update receiveTransferRequestFromAndroid --- src/global.d.ts | 2 +- src/hooks/useWebViewHandler.ts | 71 +++++++++++---------------------- src/screens/ApproveTransfer.tsx | 16 ++++---- src/types.ts | 4 +- 4 files changed, 35 insertions(+), 58 deletions(-) diff --git a/src/global.d.ts b/src/global.d.ts index e9d6af1..8b3e401 100644 --- a/src/global.d.ts +++ b/src/global.d.ts @@ -29,7 +29,7 @@ declare global { receiveSignRequestFromAndroid?: (message: string) => void; // Handles incoming transfer requests from Android - receiveTransferRequestFromAndroid?: (to: string, amount: string) => void; + receiveTransferRequestFromAndroid?: (to: string, amount: string, namespace: String, chainId: string, addressPrefix: String) => void; } } diff --git a/src/hooks/useWebViewHandler.ts b/src/hooks/useWebViewHandler.ts index 80f22b9..b64f337 100644 --- a/src/hooks/useWebViewHandler.ts +++ b/src/hooks/useWebViewHandler.ts @@ -28,12 +28,14 @@ export const useWebViewHandler = () => { if (!accounts?.length) { window.Android?.onSignatureError?.('No accounts available'); + return; } const currentAccount = accounts[currentIndex]; if (!currentAccount) { window.Android?.onSignatureError?.('Current account not found'); + return; } @@ -44,6 +46,7 @@ export const useWebViewHandler = () => { if (!match) { window.Android?.onSignatureError?.('Invalid signing path'); + return; } @@ -72,84 +75,58 @@ export const useWebViewHandler = () => { }, [selectedNetwork, accounts, currentIndex, navigation]); // Handle incoming transfer requests - const navigateToTransfer = useCallback(async (to: string, amount: string) => { + const navigateToTransfer = useCallback(async (to: string, amount: string, namespace: String, chainId: string, addressPrefix: String) => { if (!accounts || accounts.length === 0) { console.error('No accounts available'); if (window.Android?.onTransferError) { window.Android.onTransferError('No accounts available'); } + return; } - const currentAccount = accounts[currentIndex]; - if (!currentAccount) { - console.error('Current account not found'); - if (window.Android?.onTransferError) { - window.Android.onTransferError('Current account not found'); - } - return; - } - - // Use Cosmos Hub Testnet network - const cosmosHubTestnet = { - namespace: 'cosmos', - chainId: 'provider', - addressPrefix: 'cosmos' + const chainSpecs = { + namespace, + chainId, + addressPrefix }; try { - // Get all accounts for Cosmos Hub Testnet - const cosmosAccounts = await retrieveAccountsForNetwork( - `${cosmosHubTestnet.namespace}:${cosmosHubTestnet.chainId}`, + // Get all accounts + const chainAccounts = await retrieveAccountsForNetwork( + `${chainSpecs.namespace}:${chainSpecs.chainId}`, '0' // Use the first account ); - if (!cosmosAccounts || cosmosAccounts.length === 0) { - console.error('No Cosmos Hub Testnet accounts found'); + if (!chainAccounts || chainAccounts.length === 0) { + console.error('Accounts not found'); if (window.Android?.onTransferError) { - window.Android.onTransferError('No Cosmos Hub Testnet accounts found'); + window.Android.onTransferError('Accounts not found'); } + return; } - const cosmosAccount = cosmosAccounts[0]; // Use the first account + const chainAccount = chainAccounts[0]; // Use the first account - const path = `/transfer/${cosmosHubTestnet.namespace}/${cosmosHubTestnet.chainId}/${cosmosAccount.address}/${to}/${amount}`; - - const pathRegex = /^\/transfer\/(eip155|cosmos)\/(.+)\/(.+)\/(.+)\/(.+)$/; - if (!pathRegex.test(path)) { - console.error('Path does not match expected pattern:', path); - if (window.Android?.onTransferError) { - window.Android.onTransferError('Invalid path format'); - } - return; - } - - const match = path.match(pathRegex); - if (!match) { - console.error('Failed to parse path:', path); - if (window.Android?.onTransferError) { - window.Android.onTransferError('Failed to parse path'); - } - return; - } + const path = `/transfer/${chainSpecs.namespace}/${chainSpecs.chainId}/${chainAccount.address}/${to}/${amount}`; navigation.reset({ index: 0, routes: [ { name: 'ApproveTransfer', - path: `/transfer/${cosmosHubTestnet.namespace}/${cosmosHubTestnet.chainId}/${cosmosAccount.address}/${to}/${amount}`, + path: path, params: { - namespace: cosmosHubTestnet.namespace, - chainId: `${cosmosHubTestnet.namespace}:${cosmosHubTestnet.chainId}`, + namespace: chainSpecs.namespace, + chainId: `${chainSpecs.namespace}:${chainSpecs.chainId}`, transaction: { - from: cosmosAccount.address, + from: chainAccount.address, to: to, value: amount, data: '' }, - accountInfo: cosmosAccount, + accountInfo: chainAccount, }, }, ], @@ -160,7 +137,7 @@ export const useWebViewHandler = () => { window.Android.onTransferError(`Navigation error: ${error}`); } } - }, [accounts, currentIndex, navigation]); + }, [accounts, navigation]); useEffect(() => { // Assign the function to the window object diff --git a/src/screens/ApproveTransfer.tsx b/src/screens/ApproveTransfer.tsx index db40973..9d31ad8 100644 --- a/src/screens/ApproveTransfer.tsx +++ b/src/screens/ApproveTransfer.tsx @@ -53,14 +53,14 @@ const ApproveTransfer = ({ route }: ApproveTransferProps) => { const { web3wallet } = useWalletConnect(); // Extract data from route params or path - const requestSession = route.params?.requestSessionData; - const requestName = requestSession?.peer?.metadata?.name; - const requestIcon = requestSession?.peer?.metadata?.icons?.[0]; - const requestURL = requestSession?.peer?.metadata?.url; - const transaction = route.params?.transaction; - const requestEvent = route.params?.requestEvent; - const chainId = requestEvent?.params?.chainId || route.params?.chainId; - const requestMethod = requestEvent?.params?.request?.method; + const requestSession = route.params.requestSessionData ?? null; + const requestName = requestSession?.peer.metadata.name; + const requestIcon = requestSession?.peer.metadata.icons?.[0]; + const requestURL = requestSession?.peer.metadata.url; + const transaction = route.params.transaction; + const requestEvent = route.params.requestEvent ?? null; + const chainId = requestEvent?.params.chainId || route.params.chainId; + const requestMethod = requestEvent?.params.request.method; const [account, setAccount] = useState(); const [isLoading, setIsLoading] = useState(true); diff --git a/src/types.ts b/src/types.ts index 0631492..3251843 100644 --- a/src/types.ts +++ b/src/types.ts @@ -23,8 +23,8 @@ export type StackParamsList = { ApproveTransfer: { chainId?: string; transaction: PopulatedTransaction; - requestEvent: Web3WalletTypes.SessionRequest; - requestSessionData: SessionTypes.Struct; + requestEvent?: Web3WalletTypes.SessionRequest; + requestSessionData?: SessionTypes.Struct; }; InvalidPath: undefined; WalletConnect: undefined; -- 2.45.2 From 43e61b760361b434b2dd565f2628adffec668a0f Mon Sep 17 00:00:00 2001 From: AdityaSalunkhe21 Date: Thu, 17 Apr 2025 16:32:42 +0530 Subject: [PATCH 07/15] Remove addressPrefix --- src/global.d.ts | 2 +- src/hooks/useWebViewHandler.ts | 14 +-- src/screens/ApproveTransfer.tsx | 187 ++++++++++---------------------- 3 files changed, 62 insertions(+), 141 deletions(-) diff --git a/src/global.d.ts b/src/global.d.ts index 8b3e401..c4ef4a9 100644 --- a/src/global.d.ts +++ b/src/global.d.ts @@ -29,7 +29,7 @@ declare global { receiveSignRequestFromAndroid?: (message: string) => void; // Handles incoming transfer requests from Android - receiveTransferRequestFromAndroid?: (to: string, amount: string, namespace: String, chainId: string, addressPrefix: String) => void; + receiveTransferRequestFromAndroid?: (to: string, amount: string, namespace: String, chainId: string) => void; } } diff --git a/src/hooks/useWebViewHandler.ts b/src/hooks/useWebViewHandler.ts index b64f337..50d873b 100644 --- a/src/hooks/useWebViewHandler.ts +++ b/src/hooks/useWebViewHandler.ts @@ -75,20 +75,10 @@ export const useWebViewHandler = () => { }, [selectedNetwork, accounts, currentIndex, navigation]); // Handle incoming transfer requests - const navigateToTransfer = useCallback(async (to: string, amount: string, namespace: String, chainId: string, addressPrefix: String) => { - if (!accounts || accounts.length === 0) { - console.error('No accounts available'); - if (window.Android?.onTransferError) { - window.Android.onTransferError('No accounts available'); - } - - return; - } - + const navigateToTransfer = useCallback(async (to: string, amount: string, namespace: String, chainId: string) => { const chainSpecs = { namespace, chainId, - addressPrefix }; try { @@ -137,7 +127,7 @@ export const useWebViewHandler = () => { window.Android.onTransferError(`Navigation error: ${error}`); } } - }, [accounts, navigation]); + }, [navigation]); useEffect(() => { // Assign the function to the window object diff --git a/src/screens/ApproveTransfer.tsx b/src/screens/ApproveTransfer.tsx index 9d31ad8..763a7a1 100644 --- a/src/screens/ApproveTransfer.tsx +++ b/src/screens/ApproveTransfer.tsx @@ -53,12 +53,12 @@ const ApproveTransfer = ({ route }: ApproveTransferProps) => { const { web3wallet } = useWalletConnect(); // Extract data from route params or path - const requestSession = route.params.requestSessionData ?? null; + const requestSession = route.params.requestSessionData; const requestName = requestSession?.peer.metadata.name; const requestIcon = requestSession?.peer.metadata.icons?.[0]; const requestURL = requestSession?.peer.metadata.url; const transaction = route.params.transaction; - const requestEvent = route.params.requestEvent ?? null; + const requestEvent = route.params.requestEvent; const chainId = requestEvent?.params.chainId || route.params.chainId; const requestMethod = requestEvent?.params.request.method; @@ -186,24 +186,6 @@ const ApproveTransfer = ({ route }: ApproveTransferProps) => { const navigation = useNavigation>(); - const sanitizePath = useCallback((path: string) => { - const regex = /^\/transfer\/(eip155|cosmos)\/(.+)\/(.+)\/(.+)\/(.+)$/; - const match = path.match(regex); - if (match) { - const [, pathNamespace, pathChainId, pathAddress, pathTo, pathAmount] = match; - return { - namespace: pathNamespace, - chainId: pathChainId, - address: pathAddress, - to: pathTo, - amount: pathAmount, - }; - } else { - navigation.navigate('InvalidPath'); - } - return null; - }, [navigation]); - const retrieveData = useCallback( async (requestAddress: string) => { const requestAccount = await retrieveSingleAccount( @@ -221,6 +203,47 @@ const ApproveTransfer = ({ route }: ApproveTransferProps) => { [navigation, requestedNetwork], ); + const handleIntent = async () => { + if (!account) { + throw new Error('Account is not valid'); + } + + if (!requestedNetwork) { + throw new Error('Network not found'); + } + + if (!cosmosStargateClient) { + throw new Error('Cosmos stargate client not found'); + } + + const result = await cosmosStargateClient.signAndBroadcast( + account.address, + [sendMsg], + { + amount: [ + { + amount: fees, + denom: requestedNetwork.nativeDenom!, + }, + ], + gas: cosmosGasLimit, + }, + MEMO, + ); + + // Convert BigInt values to strings before sending to Android + const serializedResult = JSON.stringify(result, (key, value) => + typeof value === 'bigint' ? value.toString() : value + ); + + // Send the result back to Android and close dialog + if (window.Android?.onTransferComplete) { + window.Android.onTransferComplete(serializedResult); + } else { + alert(`Transaction: ${serializedResult}`); + } + }; + useEffect(() => { // Set loading to false when gas values for requested chain are fetched // If requested chain is EVM compatible, the cosmos gas values will be undefined and vice-versa, hence the condition checks only one of them at the same time @@ -277,101 +300,6 @@ const ApproveTransfer = ({ route }: ApproveTransferProps) => { return false; }, [cosmosGasLimit, ethMaxFee, ethMaxPriorityFee]); - const handleIntent = async () => { - if (!account) { - throw new Error('Account is not valid'); - } - - if (route.path) { - const sanitizedRoute = sanitizePath(route.path); - if (!sanitizedRoute) { - throw new Error('Invalid path'); - } - - const requestedNetwork = networksData.find( - networkData => networkData.chainId === sanitizedRoute.chainId, - ); - - if (!requestedNetwork) { - throw new Error('Network not found'); - } - - const cosmosPrivKey = ( - await getPathKey( - `${requestedNetwork.namespace}:${requestedNetwork.chainId}`, - account.index, - ) - ).privKey; - - const sender = await DirectSecp256k1Wallet.fromKey( - Uint8Array.from(Buffer.from(cosmosPrivKey.split('0x')[1], 'hex')), - requestedNetwork.addressPrefix, - ); - - const client = await SigningStargateClient.connectWithSigner( - requestedNetwork.rpcUrl!, - sender, - ); - - const sendMsg: MsgSendEncodeObject = { - typeUrl: '/cosmos.bank.v1beta1.MsgSend', - value: { - fromAddress: account.address, - toAddress: sanitizedRoute.to, - amount: [ - { - amount: String(sanitizedRoute.amount), - denom: requestedNetwork.nativeDenom!, - }, - ], - }, - }; - - const gasEstimation = await client.simulate( - account.address, - [sendMsg], - MEMO, - ); - - const gasLimit = String( - Math.round(gasEstimation * Number(process.env.REACT_APP_GAS_ADJUSTMENT)), - ); - - const gasPrice = GasPrice.fromString( - requestedNetwork.gasPrice! + requestedNetwork.nativeDenom, - ); - - const cosmosFees = calculateFee(Number(gasLimit), gasPrice); - - const result = await client.signAndBroadcast( - account.address, - [sendMsg], - { - amount: [ - { - amount: cosmosFees.amount[0].amount, - denom: requestedNetwork.nativeDenom!, - }, - ], - gas: gasLimit, - }, - MEMO, - ); - - // Convert BigInt values to strings before sending to Android - const serializedResult = JSON.stringify(result, (key, value) => - typeof value === 'bigint' ? value.toString() : value - ); - - // Send the result back to Android and close dialog - if (window.Android?.onTransferComplete) { - window.Android.onTransferComplete(serializedResult); - } else { - alert(`Transaction: ${serializedResult}`); - } - } - }; - const acceptRequestHandler = async () => { setIsTxLoading(true); try { @@ -449,7 +377,6 @@ const ApproveTransfer = ({ route }: ApproveTransferProps) => { await web3wallet!.respondSessionRequest({ topic, response }); navigation.navigate('Home'); } else { - // Handle direct intent await handleIntent(); navigation.navigate('Home'); } @@ -484,7 +411,7 @@ const ApproveTransfer = ({ route }: ApproveTransferProps) => { useEffect(() => { const getAccountBalance = async () => { try { - if (!account || !requestedNetwork) { + if (!account) { return; } if (namespace === EIP155) { @@ -494,20 +421,20 @@ const ApproveTransfer = ({ route }: ApproveTransferProps) => { const fetchedBalance = await provider.getBalance(account.address); setBalance(fetchedBalance ? fetchedBalance.toString() : '0'); } else { - if (!cosmosStargateClient) { - return; - } - const cosmosBalance = await cosmosStargateClient.getBalance( + const cosmosBalance = await cosmosStargateClient?.getBalance( account.address, - requestedNetwork.nativeDenom!.toLowerCase(), + requestedNetwork!.nativeDenom!.toLowerCase(), ); - setBalance(cosmosBalance?.amount || '0'); + + setBalance(cosmosBalance?.amount!); } } catch (error) { - console.error('Error fetching balance:', error); - setBalance('0'); - // Don't show error dialog for balance fetch failures - // Just set balance to 0 and let the transaction proceed + if (!(error instanceof Error)) { + throw error; + } + + setTxError(error.message); + setIsTxErrorDialogOpen(true); } }; @@ -651,7 +578,11 @@ const ApproveTransfer = ({ route }: ApproveTransferProps) => { label={`Balance (${ namespace === EIP155 ? 'wei' : requestedNetwork!.nativeDenom })`} - data={balance || '0'} + data={ + balance === '' || balance === undefined + ? 'Loading balance...' + : `${balance}` + } /> {transaction && ( -- 2.45.2 From 08af9ba20d331dbc3fb0971b4a1a4e1d399718ab Mon Sep 17 00:00:00 2001 From: pranavjadhav007 Date: Wed, 4 Jun 2025 14:12:41 +0530 Subject: [PATCH 08/15] Add nym chain in the network --- src/global.d.ts | 2 +- src/hooks/useWebViewHandler.ts | 3 ++- src/screens/ApproveTransfer.tsx | 26 +++++++++++++++++++++++--- src/types.ts | 1 + src/utils/constants.ts | 12 ++++++++++++ 5 files changed, 39 insertions(+), 5 deletions(-) diff --git a/src/global.d.ts b/src/global.d.ts index c4ef4a9..f082ff1 100644 --- a/src/global.d.ts +++ b/src/global.d.ts @@ -29,7 +29,7 @@ declare global { receiveSignRequestFromAndroid?: (message: string) => void; // Handles incoming transfer requests from Android - receiveTransferRequestFromAndroid?: (to: string, amount: string, namespace: String, chainId: string) => void; + receiveTransferRequestFromAndroid?: (to: string, amount: string, namespace: String, chainId: string, memo: string) => void; } } diff --git a/src/hooks/useWebViewHandler.ts b/src/hooks/useWebViewHandler.ts index 50d873b..d3f5dfb 100644 --- a/src/hooks/useWebViewHandler.ts +++ b/src/hooks/useWebViewHandler.ts @@ -75,7 +75,7 @@ export const useWebViewHandler = () => { }, [selectedNetwork, accounts, currentIndex, navigation]); // Handle incoming transfer requests - const navigateToTransfer = useCallback(async (to: string, amount: string, namespace: String, chainId: string) => { + const navigateToTransfer = useCallback(async (to: string, amount: string, namespace: String, chainId: string, memo: string) => { const chainSpecs = { namespace, chainId, @@ -117,6 +117,7 @@ export const useWebViewHandler = () => { data: '' }, accountInfo: chainAccount, + memo: memo }, }, ], diff --git a/src/screens/ApproveTransfer.tsx b/src/screens/ApproveTransfer.tsx index 763a7a1..9e60053 100644 --- a/src/screens/ApproveTransfer.tsx +++ b/src/screens/ApproveTransfer.tsx @@ -61,6 +61,8 @@ const ApproveTransfer = ({ route }: ApproveTransferProps) => { const requestEvent = route.params.requestEvent; const chainId = requestEvent?.params.chainId || route.params.chainId; const requestMethod = requestEvent?.params.request.method; + const customMemo = route.params.memo; + const finalMemo = customMemo || MEMO; const [account, setAccount] = useState(); const [isLoading, setIsLoading] = useState(true); @@ -208,6 +210,16 @@ const ApproveTransfer = ({ route }: ApproveTransferProps) => { throw new Error('Account is not valid'); } + console.log('Sending transaction request:', { + from: account.address, + to: transaction.to, + amount: transaction.value, + denom: requestedNetwork?.nativeDenom, + memo: finalMemo, + gas: cosmosGasLimit, + fees: fees + }); + if (!requestedNetwork) { throw new Error('Network not found'); } @@ -228,9 +240,11 @@ const ApproveTransfer = ({ route }: ApproveTransferProps) => { ], gas: cosmosGasLimit, }, - MEMO, + finalMemo, ); + console.log('Transaction result:', result); + // Convert BigInt values to strings before sending to Android const serializedResult = JSON.stringify(result, (key, value) => typeof value === 'bigint' ? value.toString() : value @@ -520,7 +534,7 @@ const ApproveTransfer = ({ route }: ApproveTransferProps) => { const gasEstimation = await cosmosStargateClient.simulate( transaction.from!, [sendMsg], - MEMO, + finalMemo, ); setCosmosGasLimit( @@ -538,7 +552,7 @@ const ApproveTransfer = ({ route }: ApproveTransferProps) => { } }; getCosmosGas(); - }, [cosmosStargateClient, isSufficientFunds, sendMsg, transaction]); + }, [cosmosStargateClient, isSufficientFunds, sendMsg, transaction,finalMemo]); useEffect(() => { if (balance && !isSufficientFunds) { @@ -595,6 +609,12 @@ const ApproveTransfer = ({ route }: ApproveTransferProps) => { transaction.value?.toString(), ).toString()} /> + {namespace === COSMOS && ( + + )} {namespace === EIP155 ? ( <> diff --git a/src/types.ts b/src/types.ts index 3251843..e70812b 100644 --- a/src/types.ts +++ b/src/types.ts @@ -25,6 +25,7 @@ export type StackParamsList = { transaction: PopulatedTransaction; requestEvent?: Web3WalletTypes.SessionRequest; requestSessionData?: SessionTypes.Struct; + memo?: string; }; InvalidPath: undefined; WalletConnect: undefined; diff --git a/src/utils/constants.ts b/src/utils/constants.ts index 2eea12c..041e97b 100644 --- a/src/utils/constants.ts +++ b/src/utils/constants.ts @@ -52,6 +52,18 @@ export const DEFAULT_NETWORKS: NetworksFormData[] = [ gasPrice: '0.025', isDefault: true, }, + { + chainId: 'nyx', + networkName: 'Nym', + namespace: COSMOS, + rpcUrl: 'https://rpc.nymtech.net', + blockExplorerUrl: 'https://explorer.nymtech.net', + nativeDenom: 'unym', + addressPrefix: 'n', + coinType: '118', + gasPrice: '0.025', + isDefault: true, + }, ]; export const CHAINID_DEBOUNCE_DELAY = 250; -- 2.45.2 From acf0d1fc17efe633a2e2e3a89fcbc2c1b481a31c Mon Sep 17 00:00:00 2001 From: pranavjadhav007 Date: Wed, 4 Jun 2025 16:59:14 +0530 Subject: [PATCH 09/15] Convert the nym into unym --- src/hooks/useWebViewHandler.ts | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/hooks/useWebViewHandler.ts b/src/hooks/useWebViewHandler.ts index d3f5dfb..cda4503 100644 --- a/src/hooks/useWebViewHandler.ts +++ b/src/hooks/useWebViewHandler.ts @@ -82,6 +82,10 @@ export const useWebViewHandler = () => { }; try { + const unymAmount = chainSpecs.chainId === 'nyx' + ? Math.round(parseFloat(amount) * 1_000_000).toString() + : amount; + // Get all accounts const chainAccounts = await retrieveAccountsForNetwork( `${chainSpecs.namespace}:${chainSpecs.chainId}`, @@ -96,10 +100,9 @@ export const useWebViewHandler = () => { return; } - const chainAccount = chainAccounts[0]; // Use the first account - const path = `/transfer/${chainSpecs.namespace}/${chainSpecs.chainId}/${chainAccount.address}/${to}/${amount}`; + const path = `/transfer/${chainSpecs.namespace}/${chainSpecs.chainId}/${chainAccount.address}/${to}/${unymAmount}`; navigation.reset({ index: 0, @@ -113,7 +116,7 @@ export const useWebViewHandler = () => { transaction: { from: chainAccount.address, to: to, - value: amount, + value: unymAmount, data: '' }, accountInfo: chainAccount, -- 2.45.2 From b06c56c41c026e1ff2824a3bd0c0b2dabddd9259 Mon Sep 17 00:00:00 2001 From: pranavjadhav007 Date: Fri, 6 Jun 2025 13:43:32 +0530 Subject: [PATCH 10/15] Add window function to get or create account --- src/global.d.ts | 9 ++++++ src/hooks/useGetOrCreateAccounts.ts | 25 ++++------------ src/hooks/useWebViewHandler.ts | 44 +++++++++++++++++++++++++++-- 3 files changed, 55 insertions(+), 23 deletions(-) diff --git a/src/global.d.ts b/src/global.d.ts index f082ff1..aa6c82e 100644 --- a/src/global.d.ts +++ b/src/global.d.ts @@ -23,6 +23,12 @@ declare global { // Called when transfer is cancelled onTransferCancelled?: () => void; + + // Called when account is created + onAccountCreated?: (account: string) => void; + + // Called when account creation fails + onAccountError?: (error: string) => void; }; // Handles incoming signature requests from Android @@ -30,6 +36,9 @@ declare global { // Handles incoming transfer requests from Android receiveTransferRequestFromAndroid?: (to: string, amount: string, namespace: String, chainId: string, memo: string) => void; + + // Handles account creation requests from Android + receiveGetOrCreateAccountFromAndroid?: (namespace: string, chainId: string) => void; } } diff --git a/src/hooks/useGetOrCreateAccounts.ts b/src/hooks/useGetOrCreateAccounts.ts index ce6b950..2628435 100644 --- a/src/hooks/useGetOrCreateAccounts.ts +++ b/src/hooks/useGetOrCreateAccounts.ts @@ -58,29 +58,14 @@ const useGetOrCreateAccounts = () => { ); }; - const autoCreateAccounts = async () => { - const defaultChainId = networksData[0]?.chainId; - - if (!defaultChainId) { - console.log('useGetOrCreateAccounts: No default chainId found'); - return; - } - const accounts = await getOrCreateAccountsForChain(defaultChainId); - - // Only notify Android when we actually have accounts - if (accounts.length > 0 && window.Android?.onAccountsReady) { - window.Android.onAccountsReady(); - } else { - console.log('No accounts created or Android bridge not available'); - } - }; - window.addEventListener('message', handleCreateAccounts); const isAndroidWebView = !!(window.Android); - - if (isAndroidWebView) { - autoCreateAccounts(); + if (!isAndroidWebView) { + const defaultChainId = networksData[0]?.chainId; + if (defaultChainId) { + getOrCreateAccountsForChain(defaultChainId); + } } return () => { diff --git a/src/hooks/useWebViewHandler.ts b/src/hooks/useWebViewHandler.ts index cda4503..64f5463 100644 --- a/src/hooks/useWebViewHandler.ts +++ b/src/hooks/useWebViewHandler.ts @@ -4,15 +4,50 @@ import { NativeStackNavigationProp } from '@react-navigation/native-stack'; import { useAccounts } from '../context/AccountsContext'; import { useNetworks } from '../context/NetworksContext'; +import useAccountsData from "../hooks/useAccountsData"; + import { StackParamsList } from '../types'; import useGetOrCreateAccounts from './useGetOrCreateAccounts'; -import { retrieveAccountsForNetwork } from '../utils/accounts'; +import { retrieveAccountsForNetwork, createWallet } from '../utils/accounts'; export const useWebViewHandler = () => { // Navigation and context hooks const navigation = useNavigation>(); - const { selectedNetwork } = useNetworks(); + const { selectedNetwork, networksData } = useNetworks(); const { accounts, currentIndex } = useAccounts(); + const { getAccountsData } = useAccountsData(); + + const handleGetOrCreateAccount = useCallback(async (namespace: string, chainId: string) => { + try { + // Find the requested network + const network = networksData.find(net => + net.namespace === namespace && net.chainId === chainId + ); + + if (!network) { + window.Android?.onAccountError?.('Network configuration not found'); + return; + } + + let accountsData = await getAccountsData(chainId); + + if (accountsData.length === 0) { + console.log("Account not found, creating wallet..."); + await createWallet(networksData); + accountsData = await getAccountsData(chainId); + } + + if (!accountsData || accountsData.length === 0) { + window.Android?.onAccountError?.('Failed to create/retrieve account'); + return; + } + + window.Android?.onAccountCreated?.(JSON.stringify(accountsData[0])); + } catch (error) { + console.error('Account operation error:', error); + window.Android?.onAccountError?.(`Operation failed: ${error}`); + } + }, [networksData, getAccountsData]); // Initialize accounts useGetOrCreateAccounts(); @@ -139,9 +174,12 @@ export const useWebViewHandler = () => { window.receiveTransferRequestFromAndroid = navigateToTransfer; + window.receiveGetOrCreateAccountFromAndroid = handleGetOrCreateAccount; + return () => { window.receiveSignRequestFromAndroid = undefined; window.receiveTransferRequestFromAndroid = undefined; + window.receiveGetOrCreateAccountFromAndroid = undefined; }; - }, [navigateToSignRequest, navigateToTransfer]); // Only the function reference as dependency + }, [navigateToSignRequest, navigateToTransfer, handleGetOrCreateAccount]); // Only the function reference as dependency }; -- 2.45.2 From 053d7f56188d80281134816cb95f2617eac41d20 Mon Sep 17 00:00:00 2001 From: pranavjadhav007 Date: Tue, 17 Jun 2025 19:41:08 +0530 Subject: [PATCH 11/15] Automatically approve the transaction request from app --- src/screens/ApproveTransfer.tsx | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/src/screens/ApproveTransfer.tsx b/src/screens/ApproveTransfer.tsx index 9e60053..40d1ec5 100644 --- a/src/screens/ApproveTransfer.tsx +++ b/src/screens/ApproveTransfer.tsx @@ -2,7 +2,6 @@ import React, { useCallback, useEffect, useMemo, useState } from 'react'; import { Image, ScrollView, View } from 'react-native'; import { ActivityIndicator, - Button, Text, Appbar, TextInput, @@ -41,6 +40,8 @@ import { COSMOS, EIP155, IS_NUMBER_REGEX } from '../utils/constants'; import TxErrorDialog from '../components/TxErrorDialog'; import { EIP155_SIGNING_METHODS } from '../utils/wallet-connect/EIP155Data'; import { COSMOS_METHODS } from '../utils/wallet-connect/COSMOSData'; +import { Button } from '@mui/material'; +import { LoadingButton } from '@mui/lab'; export const MEMO = 'Sending signed tx from Laconic Wallet'; // Reference: https://ethereum.org/en/developers/docs/gas/#what-is-gas-limit @@ -708,17 +709,18 @@ const ApproveTransfer = ({ route }: ApproveTransferProps) => { )} - + -- 2.45.2 From 1124805bb7d7680e0af62d7c9f37ea1761bfab9b Mon Sep 17 00:00:00 2001 From: pranavjadhav007 Date: Fri, 27 Jun 2025 11:16:46 +0530 Subject: [PATCH 12/15] Remove the code handling NYM to uNYM conversion --- .env.example | 1 - src/hooks/useWebViewHandler.ts | 23 ++++++----------- src/screens/ApproveTransfer.tsx | 44 ++++++++++++++++++++------------- src/utils/constants.ts | 4 +++ 4 files changed, 38 insertions(+), 34 deletions(-) diff --git a/.env.example b/.env.example index 2e44075..1ea38db 100644 --- a/.env.example +++ b/.env.example @@ -1,6 +1,5 @@ REACT_APP_WALLET_CONNECT_PROJECT_ID= -REACT_APP_DEFAULT_GAS_PRICE=0.025 # Reference: https://github.com/cosmos/cosmos-sdk/issues/16020 REACT_APP_GAS_ADJUSTMENT=2 REACT_APP_LACONICD_RPC_URL=https://laconicd-sapo.laconic.com diff --git a/src/hooks/useWebViewHandler.ts b/src/hooks/useWebViewHandler.ts index 64f5463..776bcc2 100644 --- a/src/hooks/useWebViewHandler.ts +++ b/src/hooks/useWebViewHandler.ts @@ -111,19 +111,12 @@ export const useWebViewHandler = () => { // Handle incoming transfer requests const navigateToTransfer = useCallback(async (to: string, amount: string, namespace: String, chainId: string, memo: string) => { - const chainSpecs = { - namespace, - chainId, - }; - try { - const unymAmount = chainSpecs.chainId === 'nyx' - ? Math.round(parseFloat(amount) * 1_000_000).toString() - : amount; - + try { + // TODO: Pass the account info for transferring tokens // Get all accounts const chainAccounts = await retrieveAccountsForNetwork( - `${chainSpecs.namespace}:${chainSpecs.chainId}`, + `${namespace}:${chainId}`, '0' // Use the first account ); @@ -137,7 +130,7 @@ export const useWebViewHandler = () => { } const chainAccount = chainAccounts[0]; // Use the first account - const path = `/transfer/${chainSpecs.namespace}/${chainSpecs.chainId}/${chainAccount.address}/${to}/${unymAmount}`; + const path = `/transfer/${namespace}/${chainId}/${chainAccount.address}/${to}/${amount}`; navigation.reset({ index: 0, @@ -146,13 +139,12 @@ export const useWebViewHandler = () => { name: 'ApproveTransfer', path: path, params: { - namespace: chainSpecs.namespace, - chainId: `${chainSpecs.namespace}:${chainSpecs.chainId}`, + namespace: namespace, + chainId: `${namespace}:${chainId}`, transaction: { from: chainAccount.address, to: to, - value: unymAmount, - data: '' + value: amount }, accountInfo: chainAccount, memo: memo @@ -161,7 +153,6 @@ export const useWebViewHandler = () => { ], }); } catch (error) { - console.error('Navigation error:', error); if (window.Android?.onTransferError) { window.Android.onTransferError(`Navigation error: ${error}`); } diff --git a/src/screens/ApproveTransfer.tsx b/src/screens/ApproveTransfer.tsx index 40d1ec5..17ff4e3 100644 --- a/src/screens/ApproveTransfer.tsx +++ b/src/screens/ApproveTransfer.tsx @@ -56,14 +56,13 @@ const ApproveTransfer = ({ route }: ApproveTransferProps) => { // Extract data from route params or path const requestSession = route.params.requestSessionData; const requestName = requestSession?.peer.metadata.name; - const requestIcon = requestSession?.peer.metadata.icons?.[0]; + const requestIcon = requestSession?.peer.metadata.icons[0]; const requestURL = requestSession?.peer.metadata.url; const transaction = route.params.transaction; const requestEvent = route.params.requestEvent; const chainId = requestEvent?.params.chainId || route.params.chainId; const requestMethod = requestEvent?.params.request.method; - const customMemo = route.params.memo; - const finalMemo = customMemo || MEMO; + const finalMemo = route.params.memo || MEMO; const [account, setAccount] = useState(); const [isLoading, setIsLoading] = useState(true); @@ -206,6 +205,7 @@ const ApproveTransfer = ({ route }: ApproveTransferProps) => { [navigation, requestedNetwork], ); + //TODO: Handle ETH transactions const handleIntent = async () => { if (!account) { throw new Error('Account is not valid'); @@ -215,7 +215,7 @@ const ApproveTransfer = ({ route }: ApproveTransferProps) => { from: account.address, to: transaction.to, amount: transaction.value, - denom: requestedNetwork?.nativeDenom, + denom: requestedNetwork!.nativeDenom, memo: finalMemo, gas: cosmosGasLimit, fees: fees @@ -390,7 +390,6 @@ const ApproveTransfer = ({ route }: ApproveTransferProps) => { const { topic } = requestEvent; await web3wallet!.respondSessionRequest({ topic, response }); - navigation.navigate('Home'); } else { await handleIntent(); navigation.navigate('Home'); @@ -407,20 +406,31 @@ const ApproveTransfer = ({ route }: ApproveTransferProps) => { }; const rejectRequestHandler = async () => { - if (requestEvent) { - const response = rejectWalletConnectRequest(requestEvent); - const { topic } = requestEvent; - await web3wallet!.respondSessionRequest({ - topic, - response, - }); - } + setIsTxLoading(true); + try { + if (requestEvent) { + const response = rejectWalletConnectRequest(requestEvent); + const { topic } = requestEvent; + await web3wallet!.respondSessionRequest({ + topic, + response, + }); + } - if (window.Android?.onTransferCancelled) { - window.Android.onTransferCancelled(); - } else { - navigation.navigate('Home'); + if (window.Android?.onTransferCancelled) { + window.Android.onTransferCancelled(); + } else { + navigation.navigate('Home'); + } + } catch (error) { + if (!(error instanceof Error)) { + throw error; + } + + setTxError(error.message); + setIsTxErrorDialogOpen(true); } + setIsTxLoading(false); }; useEffect(() => { diff --git a/src/utils/constants.ts b/src/utils/constants.ts index 041e97b..05a303e 100644 --- a/src/utils/constants.ts +++ b/src/utils/constants.ts @@ -52,6 +52,8 @@ export const DEFAULT_NETWORKS: NetworksFormData[] = [ gasPrice: '0.025', isDefault: true, }, + + //TODO: Add network from android app { chainId: 'nyx', networkName: 'Nym', @@ -61,6 +63,8 @@ export const DEFAULT_NETWORKS: NetworksFormData[] = [ nativeDenom: 'unym', addressPrefix: 'n', coinType: '118', + + // Ref: https://nym.com/docs/operators/nodes/validator-setup#apptoml-configuration gasPrice: '0.025', isDefault: true, }, -- 2.45.2 From fc90fa49bb73d50467de60af85f302f6c4790f1d Mon Sep 17 00:00:00 2001 From: pranavjadhav007 Date: Fri, 27 Jun 2025 20:21:27 +0530 Subject: [PATCH 13/15] Remove redundant code --- src/hooks/useGetOrCreateAccounts.ts | 10 ++----- src/hooks/useWebViewHandler.ts | 42 ++++++++++------------------- src/screens/ApproveTransfer.tsx | 22 ++++++++------- 3 files changed, 28 insertions(+), 46 deletions(-) diff --git a/src/hooks/useGetOrCreateAccounts.ts b/src/hooks/useGetOrCreateAccounts.ts index 2628435..1f008c3 100644 --- a/src/hooks/useGetOrCreateAccounts.ts +++ b/src/hooks/useGetOrCreateAccounts.ts @@ -60,18 +60,12 @@ const useGetOrCreateAccounts = () => { window.addEventListener('message', handleCreateAccounts); - const isAndroidWebView = !!(window.Android); - if (!isAndroidWebView) { - const defaultChainId = networksData[0]?.chainId; - if (defaultChainId) { - getOrCreateAccountsForChain(defaultChainId); - } - } - return () => { window.removeEventListener('message', handleCreateAccounts); }; }, [networksData, getAccountsData, getOrCreateAccountsForChain]); + + return { getOrCreateAccountsForChain }; }; export default useGetOrCreateAccounts; diff --git a/src/hooks/useWebViewHandler.ts b/src/hooks/useWebViewHandler.ts index 776bcc2..a9e4136 100644 --- a/src/hooks/useWebViewHandler.ts +++ b/src/hooks/useWebViewHandler.ts @@ -8,37 +8,27 @@ import useAccountsData from "../hooks/useAccountsData"; import { StackParamsList } from '../types'; import useGetOrCreateAccounts from './useGetOrCreateAccounts'; -import { retrieveAccountsForNetwork, createWallet } from '../utils/accounts'; +import { retrieveAccountsForNetwork } from '../utils/accounts'; export const useWebViewHandler = () => { // Navigation and context hooks const navigation = useNavigation>(); - const { selectedNetwork, networksData } = useNetworks(); + const { selectedNetwork } = useNetworks(); const { accounts, currentIndex } = useAccounts(); const { getAccountsData } = useAccountsData(); - const handleGetOrCreateAccount = useCallback(async (namespace: string, chainId: string) => { - try { - // Find the requested network - const network = networksData.find(net => - net.namespace === namespace && net.chainId === chainId - ); + // Initialize accounts + const { getOrCreateAccountsForChain } = useGetOrCreateAccounts(); - if (!network) { - window.Android?.onAccountError?.('Network configuration not found'); - return; - } + const handleGetOrCreateAccount = useCallback(async (chainId: string) => { + try { + + await getOrCreateAccountsForChain(chainId); let accountsData = await getAccountsData(chainId); - - if (accountsData.length === 0) { - console.log("Account not found, creating wallet..."); - await createWallet(networksData); - accountsData = await getAccountsData(chainId); - } - if (!accountsData || accountsData.length === 0) { window.Android?.onAccountError?.('Failed to create/retrieve account'); + return; } @@ -47,10 +37,7 @@ export const useWebViewHandler = () => { console.error('Account operation error:', error); window.Android?.onAccountError?.(`Operation failed: ${error}`); } - }, [networksData, getAccountsData]); - - // Initialize accounts - useGetOrCreateAccounts(); + }, [getOrCreateAccountsForChain, getAccountsData]); // Core navigation handler const navigateToSignRequest = useCallback((message: string) => { @@ -114,13 +101,13 @@ export const useWebViewHandler = () => { try { // TODO: Pass the account info for transferring tokens - // Get all accounts - const chainAccounts = await retrieveAccountsForNetwork( + // Get first account + const [chainAccount] = await retrieveAccountsForNetwork( `${namespace}:${chainId}`, - '0' // Use the first account + '0' ); - if (!chainAccounts || chainAccounts.length === 0) { + if (!chainAccount) { console.error('Accounts not found'); if (window.Android?.onTransferError) { window.Android.onTransferError('Accounts not found'); @@ -128,7 +115,6 @@ export const useWebViewHandler = () => { return; } - const chainAccount = chainAccounts[0]; // Use the first account const path = `/transfer/${namespace}/${chainId}/${chainAccount.address}/${to}/${amount}`; diff --git a/src/screens/ApproveTransfer.tsx b/src/screens/ApproveTransfer.tsx index 17ff4e3..701cda6 100644 --- a/src/screens/ApproveTransfer.tsx +++ b/src/screens/ApproveTransfer.tsx @@ -6,6 +6,7 @@ import { Appbar, TextInput, } from 'react-native-paper'; +import JSONbig from 'json-bigint'; import { providers, BigNumber } from 'ethers'; import { Deferrable } from 'ethers/lib/utils'; @@ -62,7 +63,7 @@ const ApproveTransfer = ({ route }: ApproveTransferProps) => { const requestEvent = route.params.requestEvent; const chainId = requestEvent?.params.chainId || route.params.chainId; const requestMethod = requestEvent?.params.request.method; - const finalMemo = route.params.memo || MEMO; + const txMemo = route.params.memo || MEMO; const [account, setAccount] = useState(); const [isLoading, setIsLoading] = useState(true); @@ -216,7 +217,7 @@ const ApproveTransfer = ({ route }: ApproveTransferProps) => { to: transaction.to, amount: transaction.value, denom: requestedNetwork!.nativeDenom, - memo: finalMemo, + memo: txMemo, gas: cosmosGasLimit, fees: fees }); @@ -241,15 +242,13 @@ const ApproveTransfer = ({ route }: ApproveTransferProps) => { ], gas: cosmosGasLimit, }, - finalMemo, + txMemo, ); console.log('Transaction result:', result); // Convert BigInt values to strings before sending to Android - const serializedResult = JSON.stringify(result, (key, value) => - typeof value === 'bigint' ? value.toString() : value - ); + const serializedResult = JSONbig.stringify(result); // Send the result back to Android and close dialog if (window.Android?.onTransferComplete) { @@ -390,11 +389,14 @@ const ApproveTransfer = ({ route }: ApproveTransferProps) => { const { topic } = requestEvent; await web3wallet!.respondSessionRequest({ topic, response }); + navigation.navigate('Home'); } else { await handleIntent(); - navigation.navigate('Home'); } } catch (error) { + if (window.Android?.onTransferError) { + window.Android.onTransferError(`Transaction Failed: ${error}`); + } if (!(error instanceof Error)) { throw error; } @@ -545,7 +547,7 @@ const ApproveTransfer = ({ route }: ApproveTransferProps) => { const gasEstimation = await cosmosStargateClient.simulate( transaction.from!, [sendMsg], - finalMemo, + txMemo, ); setCosmosGasLimit( @@ -563,7 +565,7 @@ const ApproveTransfer = ({ route }: ApproveTransferProps) => { } }; getCosmosGas(); - }, [cosmosStargateClient, isSufficientFunds, sendMsg, transaction,finalMemo]); + }, [cosmosStargateClient, isSufficientFunds, sendMsg, transaction,txMemo]); useEffect(() => { if (balance && !isSufficientFunds) { @@ -623,7 +625,7 @@ const ApproveTransfer = ({ route }: ApproveTransferProps) => { {namespace === COSMOS && ( )} -- 2.45.2 From 710593fc304fe9b102a0388ec217525d6fd0ea09 Mon Sep 17 00:00:00 2001 From: pranavjadhav007 Date: Sat, 28 Jun 2025 16:01:19 +0530 Subject: [PATCH 14/15] Fix the NYX accout creation --- src/hooks/useWebViewHandler.ts | 6 ++---- src/screens/ApproveTransfer.tsx | 2 +- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/hooks/useWebViewHandler.ts b/src/hooks/useWebViewHandler.ts index a9e4136..fdd352c 100644 --- a/src/hooks/useWebViewHandler.ts +++ b/src/hooks/useWebViewHandler.ts @@ -16,16 +16,14 @@ export const useWebViewHandler = () => { const { selectedNetwork } = useNetworks(); const { accounts, currentIndex } = useAccounts(); const { getAccountsData } = useAccountsData(); - - // Initialize accounts const { getOrCreateAccountsForChain } = useGetOrCreateAccounts(); + // Initialize accounts const handleGetOrCreateAccount = useCallback(async (chainId: string) => { try { - await getOrCreateAccountsForChain(chainId); + const accountsData = await getOrCreateAccountsForChain(chainId); - let accountsData = await getAccountsData(chainId); if (!accountsData || accountsData.length === 0) { window.Android?.onAccountError?.('Failed to create/retrieve account'); diff --git a/src/screens/ApproveTransfer.tsx b/src/screens/ApproveTransfer.tsx index 701cda6..f32e2a3 100644 --- a/src/screens/ApproveTransfer.tsx +++ b/src/screens/ApproveTransfer.tsx @@ -372,7 +372,7 @@ const ApproveTransfer = ({ route }: ApproveTransferProps) => { gas: cosmosGasLimit, }, sendMsg, - memo: MEMO, + memo: txMemo, }; break; default: -- 2.45.2 From 13e36f6b7db78c55b2301a608bca57c285e1817f Mon Sep 17 00:00:00 2001 From: pranavjadhav007 Date: Mon, 30 Jun 2025 09:17:02 +0530 Subject: [PATCH 15/15] Fix the window function parameters --- src/global.d.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/global.d.ts b/src/global.d.ts index aa6c82e..e3611ef 100644 --- a/src/global.d.ts +++ b/src/global.d.ts @@ -38,7 +38,7 @@ declare global { receiveTransferRequestFromAndroid?: (to: string, amount: string, namespace: String, chainId: string, memo: string) => void; // Handles account creation requests from Android - receiveGetOrCreateAccountFromAndroid?: (namespace: string, chainId: string) => void; + receiveGetOrCreateAccountFromAndroid?: (chainId: string) => void; } } -- 2.45.2