From 7b1a6b56669797ef8e43e7c3d92a33a52725bfb9 Mon Sep 17 00:00:00 2001 From: Shreerang Kale Date: Mon, 28 Apr 2025 17:18:49 +0530 Subject: [PATCH 01/19] Add iframe for sending zenith txs --- src/App.tsx | 8 ++ src/screens/SendTxEmbed.tsx | 274 ++++++++++++++++++++++++++++++++++++ src/types.ts | 1 + 3 files changed, 283 insertions(+) create mode 100644 src/screens/SendTxEmbed.tsx diff --git a/src/App.tsx b/src/App.tsx index 1ed1a09..56845fa 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -44,6 +44,7 @@ import { useWebViewHandler } from "./hooks/useWebViewHandler"; import SignRequestEmbed from "./screens/SignRequestEmbed"; import useAddAccountEmbed from "./hooks/useAddAccountEmbed"; import useExportPKEmbed from "./hooks/useExportPrivateKeyEmbed"; +import { SendTxEmbed } from "./screens/SendTxEmbed"; const Stack = createStackNavigator(); @@ -388,6 +389,13 @@ const App = (): React.JSX.Element => { header: () => <>, }} /> + <>, + }} + /> { + const [isTxRequested, setIsTxRequested] = useState(false); + const [transactionDetails, setTransactionDetails] = useState(null); + const [fees, setFees] = useState(''); + const [gasLimit, setGasLimit] = useState(''); + const [isTxLoading, setIsTxLoading] = useState(false); + const [txError, setTxError] = useState(null); + const txEventRef = useRef(null); + + const { networksData } = useNetworks(); + + const handleTxRequested = useCallback( + async (event: MessageEvent) => { + try { + if (event.data.type !== 'REQUEST_ZENITH_SEND_TX') return; + + txEventRef.current = event; + + const { chainId, signerAddress, attestation } = event.data; + const network = networksData.find(net => net.chainId === chainId); + + if (!network) { + console.error('Network not found'); + throw new Error('Requested network not supported.'); + } + + const account = await retrieveSingleAccount(network.namespace, network.chainId, signerAddress); + if (!account) { + throw new Error('Account not found for the requested address.'); + } + + const cosmosPrivKey = ( + await getPathKey(`${network.namespace}:${chainId}`, account.index) + ).privKey; + + const sender = await DirectSecp256k1Wallet.fromKey( + Buffer.from(cosmosPrivKey.split('0x')[1], 'hex'), + network.addressPrefix + ); + + const client = await SigningStargateClient.connectWithSigner(network.rpcUrl!, sender); + + const sendMsg = { + // TODO: Update with actual type + typeUrl: '/laconic.onboarding.v1beta1.MsgOnboard', + value: { + attestation + }, + }; + + // TODO: Check funds for the tx + const balance = await client.getBalance( + account.address, + network.nativeDenom!.toLowerCase() + ); + + setTransactionDetails({ + signerAddress, + chainId, + account, + requestedNetwork: network, + balance: balance.amount, + attestation, + }); + + const gasEstimation = await client.simulate(signerAddress, [sendMsg], MEMO); + const gasLimit = String( + Math.round(gasEstimation * Number(process.env.REACT_APP_GAS_ADJUSTMENT)) + ); + setGasLimit(gasLimit); + + const gasPrice = GasPrice.fromString(`${network.gasPrice}${network.nativeDenom}`); + const cosmosFees = calculateFee(Number(gasLimit), gasPrice); + setFees(cosmosFees.amount[0].amount); + + setIsTxRequested(true); + } catch (error) { + if (!(error instanceof Error)) { + throw error; + } + setTxError(error.message); + } + }, [networksData]); + + useEffect(() => { + window.addEventListener('message', handleTxRequested); + return () => window.removeEventListener('message', handleTxRequested); + }, [handleTxRequested]); + + const acceptRequestHandler = async () => { + try { + setIsTxLoading(true); + if (!transactionDetails) { + throw new Error('Tx details not set'); + } + + const cosmosPrivKey = ( + await getPathKey(`${transactionDetails.requestedNetwork.namespace}:${transactionDetails.chainId}`, transactionDetails.account.index) + ).privKey; + + const sender = await DirectSecp256k1Wallet.fromKey( + Buffer.from(cosmosPrivKey.split('0x')[1], 'hex'), + transactionDetails.requestedNetwork.addressPrefix + ); + + const client = await SigningStargateClient.connectWithSigner( + transactionDetails.requestedNetwork.rpcUrl!, + sender + ); + + const fee = calculateFee( + Number(gasLimit), + GasPrice.fromString(`${transactionDetails.requestedNetwork.gasPrice}${transactionDetails.requestedNetwork.nativeDenom}`) + ); + + const txResult = await client.signAndBroadcast(transactionDetails.signerAddress, [transactionDetails.attestation], fee); + + const event = txEventRef.current; + + if (event?.source) { + sendMessage(event.source as Window, 'ZENITH_TRANSACTION_RESPONSE', {txHash: txResult.transactionHash}, event.origin); + } else { + console.error('No event source available to send message'); + } + } catch (error) { + if (!(error instanceof Error)) { + throw error; + } + setTxError(error.message); + } finally { + setIsTxLoading(false); + } + }; + + const rejectRequestHandler = () => { + const event = txEventRef.current; + + setIsTxRequested(false); + setTransactionDetails(null); + if (event?.source) { + sendMessage(event.source as Window, 'TRANSACTION_RESPONSE', {txHash: null}, event.origin); + } else { + console.error('No event source available to send message'); + } + }; + + return ( + <> + {isTxRequested && transactionDetails ? ( + <> + + + From + + + + + +
+                {JSON.stringify(JSON.parse(transactionDetails.attestation), null, 2)}
+              
+
+ + + + + /^\d+$/.test(value) ? setGasLimit(value) : null + } + /> + + +
+ + + + + + ) : ( + + + + + )} + { + setTxError(null) + if (window.parent) { + sendMessage(window.parent, 'TRANSACTION_RESPONSE', null, '*'); + sendMessage(window.parent, 'closeIframe', null, '*'); + } + }} + /> + + ); +}; diff --git a/src/types.ts b/src/types.ts index 31d4c3f..6d34515 100644 --- a/src/types.ts +++ b/src/types.ts @@ -41,6 +41,7 @@ export type StackParamsList = { "wallet-embed": undefined; "auto-sign-in": undefined; "sign-request-embed": undefined; + "send-tx-embed": undefined; }; export type Account = { -- 2.45.2 From b85a40817e81b2907ec2a9ab5bede25c8d3fec9a Mon Sep 17 00:00:00 2001 From: Shreerang Kale Date: Wed, 30 Apr 2025 11:19:12 +0530 Subject: [PATCH 02/19] Implement handlers for signing onboard tx --- src/screens/SendTxEmbed.tsx | 534 +++++++++++++++++++++++------------- src/screens/WalletEmbed.tsx | 1 + 2 files changed, 339 insertions(+), 196 deletions(-) diff --git a/src/screens/SendTxEmbed.tsx b/src/screens/SendTxEmbed.tsx index 283f8b3..77e3704 100644 --- a/src/screens/SendTxEmbed.tsx +++ b/src/screens/SendTxEmbed.tsx @@ -1,4 +1,4 @@ -import React, { useEffect, useState, useCallback, useRef } from 'react'; +import React, { useEffect, useState, useCallback, useMemo } from 'react'; import { ScrollView, View } from 'react-native'; import { ActivityIndicator, @@ -6,268 +6,410 @@ import { Text, TextInput, } from 'react-native-paper'; +import { Box } from '@mui/system'; -import { DirectSecp256k1Wallet } from '@cosmjs/proto-signing'; -import { - calculateFee, - GasPrice, - SigningStargateClient, -} from '@cosmjs/stargate'; +import { DirectSecp256k1Wallet, Algo } from '@cosmjs/proto-signing'; +import { SignDoc } from "cosmjs-types/cosmos/tx/v1beta1/tx"; +import { calculateFee, GasPrice, SigningStargateClient } from '@cosmjs/stargate'; -import { retrieveSingleAccount } from '../utils/accounts'; +import { retrieveAccounts, retrieveSingleAccount } from '../utils/accounts'; // Use retrieveAccounts import AccountDetails from '../components/AccountDetails'; import styles from '../styles/stylesheet'; import DataBox from '../components/DataBox'; import { getPathKey, sendMessage } from '../utils/misc'; import { useNetworks } from '../context/NetworksContext'; import TxErrorDialog from '../components/TxErrorDialog'; -import { MEMO } from './ApproveTransfer'; import { Account, NetworksDataState } from '../types'; -import { Box } from '@mui/system'; + +// --- Type Definitions --- + +const GET_ACCOUNTS_RESPONSE = "GET_ACCOUNTS_RESPONSE"; +const SIGN_ONBOARD_TX_RESPONSE = "SIGN_ONBOARD_TX_RESPONSE"; + +interface SignOnboardTxRequestData { + address: string; + signDoc: SignDoc; +} + +interface GetAccountsRequestData {} // Currently no specific data needed + +type IncomingMessageData = SignOnboardTxRequestData | GetAccountsRequestData; + +interface IncomingMessageEventData { + id: string; + type: 'SIGN_ONBOARD_TX_REQUEST' | 'GET_ACCOUNTS_REQUEST'; + data: IncomingMessageData; +} type TransactionDetails = { + requestId: string; + source: MessageEventSource; + origin: string; signerAddress: string; chainId: string; - account: Account; + account: Account; // Wallet's internal Account type requestedNetwork: NetworksDataState; balance: string; - attestation: any + signDoc: SignDoc; // Deserialized SignDoc }; +interface GetAccountsResponse { + accounts: Array<{ + algo: Algo; + address: string; + pubkey: string; // hex encoded pubkey + }>; +} + +interface SignDirectResponseData { + signed: { + bodyBytes: string; // base64 + authInfoBytes: string; // base64 + chainId: string; + accountNumber: string; // string representation of BigInt + }; + signature: { + pub_key: { + type: string; // e.g., "tendermint/PubKeySecp256k1" + value: string; // base64 encoded pubkey value + }; + signature: string; // base64 encoded signature + }; +} + +// --- Component --- + export const SendTxEmbed = () => { - const [isTxRequested, setIsTxRequested] = useState(false); + const [isTxApprovalVisible, setIsTxApprovalVisible] = useState(false); const [transactionDetails, setTransactionDetails] = useState(null); const [fees, setFees] = useState(''); - const [gasLimit, setGasLimit] = useState(''); + const [gasLimit, setGasLimit] = useState('200000'); // TODO: Revisit gas estimation const [isTxLoading, setIsTxLoading] = useState(false); const [txError, setTxError] = useState(null); - const txEventRef = useRef(null); const { networksData } = useNetworks(); - const handleTxRequested = useCallback( - async (event: MessageEvent) => { + // --- Message Handlers --- + + const handleGetAccountsRequest = useCallback(async (event: MessageEvent) => { + const { id } = event.data; + const source = event.source as Window; + const origin = event.origin; + + console.log("Received GET_ACCOUNTS_REQUEST", id); + try { + const zenithNetworkData = networksData.find(networkData => networkData.chainId === "zenith-testnet") + + if(!zenithNetworkData) { + throw new Error("Zenith network data not found") + } + // Ensure retrieveAccounts exists and returns Account[] + const allAccounts = await retrieveAccounts(zenithNetworkData); // Use retrieveAccounts + + if (!allAccounts || allAccounts.length === 0) { + throw new Error("Accounts not found for zenithNetwork") + } + + const responseAccounts = allAccounts.map((acc) => ({ + algo: 'secp256k1' as Algo, // Assuming secp256k1 + address: acc.address, + pubkey: acc.pubKey.startsWith('0x') ? acc.pubKey : `0x${acc.pubKey}`, // Ensure hex format + })); + + const response: GetAccountsResponse = { accounts: responseAccounts }; + sendMessage(source, GET_ACCOUNTS_RESPONSE, {id, data: response}, origin); + } catch (error: unknown) { + console.error("Error handling GET_ACCOUNTS_REQUEST:", error); + const errorMsg = error instanceof Error ? error.message : String(error); + // Check if source is a Window before sending message + if (source instanceof Window) { + sendMessage(source, GET_ACCOUNTS_RESPONSE, { id, error: `Failed to get accounts: ${errorMsg}` }, origin); + } else { + console.error("Cannot send error message: source is not a Window"); + } + } + }, [networksData]); // Add dependencies like retrieveAccounts if needed + + const handleSignOnboardTxRequest = useCallback(async (event: MessageEvent) => { + const { id, data } = event.data; + const source = event.source as Window; + const origin = event.origin; + const requestData = data as SignOnboardTxRequestData; + + console.log("Received SIGN_ONBOARD_TX_REQUEST", id); + setIsTxApprovalVisible(false); // Hide previous request first + setTransactionDetails(null); + setTxError(null); + + try { + const { address: signerAddress, signDoc } = requestData; + + const network = networksData.find(net => net.chainId === signDoc.chainId); + if (!network) throw new Error(`Network with chainId "${signDoc.chainId}" not supported.`); + + const account = await retrieveSingleAccount(network.namespace, network.chainId, signerAddress); + if (!account) throw new Error(`Account not found for address "${signerAddress}" on chain "${signDoc.chainId}".`); + + // Balance Check + let balanceAmount = '0'; try { - if (event.data.type !== 'REQUEST_ZENITH_SEND_TX') return; - - txEventRef.current = event; - - const { chainId, signerAddress, attestation } = event.data; - const network = networksData.find(net => net.chainId === chainId); - - if (!network) { - console.error('Network not found'); - throw new Error('Requested network not supported.'); - } - - const account = await retrieveSingleAccount(network.namespace, network.chainId, signerAddress); - if (!account) { - throw new Error('Account not found for the requested address.'); - } - - const cosmosPrivKey = ( - await getPathKey(`${network.namespace}:${chainId}`, account.index) - ).privKey; - - const sender = await DirectSecp256k1Wallet.fromKey( - Buffer.from(cosmosPrivKey.split('0x')[1], 'hex'), + // Use a temporary read-only client for balance check if possible, or the signing client + const tempWallet = await DirectSecp256k1Wallet.fromKey( + new Uint8Array(Buffer.from((await getPathKey(`${network.namespace}:${network.chainId}`, account.index)).privKey.replace(/^0x/, ''), 'hex')), // Wrap in Uint8Array network.addressPrefix ); - - const client = await SigningStargateClient.connectWithSigner(network.rpcUrl!, sender); - - const sendMsg = { - // TODO: Update with actual type - typeUrl: '/laconic.onboarding.v1beta1.MsgOnboard', - value: { - attestation - }, - }; - - // TODO: Check funds for the tx - const balance = await client.getBalance( - account.address, - network.nativeDenom!.toLowerCase() - ); - - setTransactionDetails({ - signerAddress, - chainId, - account, - requestedNetwork: network, - balance: balance.amount, - attestation, - }); - - const gasEstimation = await client.simulate(signerAddress, [sendMsg], MEMO); - const gasLimit = String( - Math.round(gasEstimation * Number(process.env.REACT_APP_GAS_ADJUSTMENT)) - ); - setGasLimit(gasLimit); - - const gasPrice = GasPrice.fromString(`${network.gasPrice}${network.nativeDenom}`); - const cosmosFees = calculateFee(Number(gasLimit), gasPrice); - setFees(cosmosFees.amount[0].amount); - - setIsTxRequested(true); - } catch (error) { - if (!(error instanceof Error)) { - throw error; - } - setTxError(error.message); + const client = await SigningStargateClient.connectWithSigner(network.rpcUrl!, tempWallet); + const balance = await client.getBalance(account.address, network.nativeDenom!); + balanceAmount = balance.amount; + client.disconnect(); + } catch (balanceError) { + console.warn("Could not retrieve balance:", balanceError); } - }, [networksData]); + + // Fee Calculation + const gasPrice = GasPrice.fromString(`${network.gasPrice}${network.nativeDenom}`); + const calculatedFee = calculateFee(Number(gasLimit), gasPrice); + setFees(calculatedFee.amount[0].amount); + + setTransactionDetails({ + requestId: id, + source: source, + origin: origin, + signerAddress, + chainId: signDoc.chainId, + account, + requestedNetwork: network, + balance: balanceAmount, + signDoc, + }); + + setIsTxApprovalVisible(true); + + } catch (error: unknown) { + console.error("Error handling SIGN_ONBOARD_TX_REQUEST:", error); + const errorMsg = error instanceof Error ? error.message : String(error); + // Check if source is a Window before sending message + if (source instanceof Window) { + sendMessage(source, id, { error: `Failed to prepare transaction: ${errorMsg}` }, origin); + } else { + console.error("Cannot send error message: source is not a Window"); + } + setTxError(errorMsg); + } + }, [networksData, gasLimit]); // Dependencies: networksData, gasLimit + + const handleIncomingMessage = useCallback((event: MessageEvent) => { + if (!event.data || typeof event.data !== 'object' || !event.data.type || !event.data.id || !event.source || event.source === window) { + return; // Basic validation + } + + const messageData = event.data as IncomingMessageEventData; + + switch (messageData.type) { + case 'GET_ACCOUNTS_REQUEST': + handleGetAccountsRequest(event as MessageEvent); + break; + case 'SIGN_ONBOARD_TX_REQUEST': + handleSignOnboardTxRequest(event as MessageEvent); + break; + default: + console.warn(`Received unknown message type: ${messageData.type}`); + } + }, [handleGetAccountsRequest, handleSignOnboardTxRequest]); useEffect(() => { - window.addEventListener('message', handleTxRequested); - return () => window.removeEventListener('message', handleTxRequested); - }, [handleTxRequested]); + window.addEventListener('message', handleIncomingMessage); + console.log("SendTxEmbed: Message listener added."); + return () => { + window.removeEventListener('message', handleIncomingMessage); + console.log("SendTxEmbed: Message listener removed."); + }; + }, [handleIncomingMessage]); + + // --- UI Action Handlers --- const acceptRequestHandler = async () => { + if (!transactionDetails) { + setTxError("Transaction details are missing."); + return; + } + + setIsTxLoading(true); + setTxError(null); + + const { requestId, source, origin, requestedNetwork, chainId, account, signerAddress, signDoc } = transactionDetails; + try { - setIsTxLoading(true); - if (!transactionDetails) { - throw new Error('Tx details not set'); - } + const { privKey } = await getPathKey(`${requestedNetwork.namespace}:${chainId}`, account.index); + const privateKeyBytes = Buffer.from(privKey.replace(/^0x/, ''), 'hex'); + const wallet = await DirectSecp256k1Wallet.fromKey(new Uint8Array(privateKeyBytes), requestedNetwork.addressPrefix); // Wrap in Uint8Array - const cosmosPrivKey = ( - await getPathKey(`${transactionDetails.requestedNetwork.namespace}:${transactionDetails.chainId}`, transactionDetails.account.index) - ).privKey; + // Perform the actual signing + const signResponse = await wallet.signDirect(signerAddress, signDoc); - const sender = await DirectSecp256k1Wallet.fromKey( - Buffer.from(cosmosPrivKey.split('0x')[1], 'hex'), - transactionDetails.requestedNetwork.addressPrefix - ); + sendMessage(source as Window, SIGN_ONBOARD_TX_RESPONSE, {id: requestId, data: signResponse}, origin); + console.log("Sent signDirect response:", requestId); - const client = await SigningStargateClient.connectWithSigner( - transactionDetails.requestedNetwork.rpcUrl!, - sender - ); + setIsTxApprovalVisible(false); + setTransactionDetails(null); - const fee = calculateFee( - Number(gasLimit), - GasPrice.fromString(`${transactionDetails.requestedNetwork.gasPrice}${transactionDetails.requestedNetwork.nativeDenom}`) - ); - - const txResult = await client.signAndBroadcast(transactionDetails.signerAddress, [transactionDetails.attestation], fee); - - const event = txEventRef.current; - - if (event?.source) { - sendMessage(event.source as Window, 'ZENITH_TRANSACTION_RESPONSE', {txHash: txResult.transactionHash}, event.origin); + } catch (error: unknown) { + console.error("Error during signDirect:", error); + const errorMsg = error instanceof Error ? error.message : String(error); + setTxError(errorMsg); + // Check if source is a Window before sending message + if (source instanceof Window) { + sendMessage(source, SIGN_ONBOARD_TX_RESPONSE, {id: requestId, error: `Failed to sign transaction: ${errorMsg}` }, origin); } else { - console.error('No event source available to send message'); + console.error("Cannot send error message: source is not a Window"); } - } catch (error) { - if (!(error instanceof Error)) { - throw error; - } - setTxError(error.message); } finally { setIsTxLoading(false); } }; const rejectRequestHandler = () => { - const event = txEventRef.current; - - setIsTxRequested(false); - setTransactionDetails(null); - if (event?.source) { - sendMessage(event.source as Window, 'TRANSACTION_RESPONSE', {txHash: null}, event.origin); + if (!transactionDetails) return; + const { requestId, source, origin } = transactionDetails; + console.log("Rejecting request:", requestId); + // Check if source is a Window before sending message + if (source instanceof Window) { + sendMessage(source, SIGN_ONBOARD_TX_RESPONSE, {id: requestId, error: "User rejected the signature request." }, origin); } else { - console.error('No event source available to send message'); + console.error("Cannot send rejection message: source is not a Window"); } + setIsTxApprovalVisible(false); + setTransactionDetails(null); + setTxError(null); }; + // --- Display Logic --- + + const safeStringify = useCallback((obj: any, replacer: any = null, space: number = 2) => { + return JSON.stringify( + obj, + (key, value) => { + if (typeof value === 'bigint') { + return value.toString(); + } + return replacer ? replacer(key, value) : value; + }, + space + ); + }, []) + + const decodeUint8Arrays = useCallback((obj: any): any => { + if (obj instanceof Uint8Array) { + try { + return new TextDecoder().decode(obj); + } catch (e) { + return obj; // fallback if decoding fails + } + } else if (Array.isArray(obj)) { + return obj.map(decodeUint8Arrays); + } else if (obj && typeof obj === 'object') { + const newObj: any = {}; + for (const [key, value] of Object.entries(obj)) { + newObj[key] = decodeUint8Arrays(value); + } + return newObj; + } + return obj; + }, []) + + const displaySignDoc = useMemo(() => { + if (!transactionDetails?.signDoc) return null; + + try { + const signDocCopy = typeof structuredClone === 'function' + ? structuredClone(transactionDetails.signDoc) + : JSON.parse(safeStringify(transactionDetails.signDoc)); + + // Attempt to parse attestation + if ( + signDocCopy.msgs && + signDocCopy.msgs[0]?.value?.attestation && + typeof signDocCopy.msgs[0].value.attestation === 'string' + ) { + try { + signDocCopy.msgs[0].value.attestation = JSON.parse(signDocCopy.msgs[0].value.attestation); + } catch (e) { + console.warn('Could not parse attestation string:', e); + } + } + + const decoded = decodeUint8Arrays(signDocCopy); + return decoded; + } catch (e) { + console.error('Error processing SignDoc:', e); + return transactionDetails.signDoc; + } + }, [transactionDetails?.signDoc, decodeUint8Arrays, safeStringify]); + + // --- Render --- + return ( <> - {isTxRequested && transactionDetails ? ( - <> - - - From - - - + {isTxApprovalVisible && transactionDetails ? ( + + + Sign Request From + + Origin: {transactionDetails.origin} - -
-                {JSON.stringify(JSON.parse(transactionDetails.attestation), null, 2)}
+          
+          
+            Account
+            
+              
+            
+          
+          
+          
+            Transaction Details
+            
+              
+                {safeStringify(displaySignDoc, null, 2)}
               
- + + - - - - /^\d+$/.test(value) ? setGasLimit(value) : null - } - /> - - - + + - - - + ) : ( - - + Waiting for request... )} { - setTxError(null) - if (window.parent) { - sendMessage(window.parent, 'TRANSACTION_RESPONSE', null, '*'); - sendMessage(window.parent, 'closeIframe', null, '*'); - } - }} + hideDialog={() => setTxError(null)} /> ); diff --git a/src/screens/WalletEmbed.tsx b/src/screens/WalletEmbed.tsx index c3e836d..301c421 100644 --- a/src/screens/WalletEmbed.tsx +++ b/src/screens/WalletEmbed.tsx @@ -51,6 +51,7 @@ export const WalletEmbed = () => { useEffect(() => { const handleGetAccounts = async (event: MessageEvent) => { + // TODO: Keep event data types in constant file if (event.data.type !== 'REQUEST_WALLET_ACCOUNTS') return; const accountsData = await getAccountsData(event.data.chainId); -- 2.45.2 From 4b5d211782b14af5865117e4371eda195ff79e21 Mon Sep 17 00:00:00 2001 From: Shreerang Kale Date: Wed, 30 Apr 2025 15:13:10 +0530 Subject: [PATCH 03/19] Update reject request handler --- src/screens/SendTxEmbed.tsx | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/screens/SendTxEmbed.tsx b/src/screens/SendTxEmbed.tsx index 77e3704..00d855c 100644 --- a/src/screens/SendTxEmbed.tsx +++ b/src/screens/SendTxEmbed.tsx @@ -273,11 +273,7 @@ export const SendTxEmbed = () => { const { requestId, source, origin } = transactionDetails; console.log("Rejecting request:", requestId); // Check if source is a Window before sending message - if (source instanceof Window) { - sendMessage(source, SIGN_ONBOARD_TX_RESPONSE, {id: requestId, error: "User rejected the signature request." }, origin); - } else { - console.error("Cannot send rejection message: source is not a Window"); - } + sendMessage(source as Window, SIGN_ONBOARD_TX_RESPONSE, {id: requestId, error: "User rejected the signature request." }, origin); setIsTxApprovalVisible(false); setTransactionDetails(null); setTxError(null); -- 2.45.2 From 04d26e0e40c371924a470ed799c3fc6671480bc3 Mon Sep 17 00:00:00 2001 From: Shreerang Kale Date: Wed, 30 Apr 2025 17:42:38 +0530 Subject: [PATCH 04/19] Display decoded tx body and auth info bytes while signing tx --- package.json | 2 + src/screens/SendTxEmbed.tsx | 246 +++++++++++++++--------------------- src/styles/stylesheet.js | 27 ++++ yarn.lock | 17 +++ 4 files changed, 145 insertions(+), 147 deletions(-) diff --git a/package.json b/package.json index 527e2a3..3a5956c 100644 --- a/package.json +++ b/package.json @@ -28,6 +28,7 @@ "cosmjs-types": "^0.9.0", "ethers": "5.7.2", "https-browserify": "^1.0.0", + "json-bigint": "^1.0.0", "lodash": "^4.17.21", "react": "^18.3.1", "react-dom": "^18.3.1", @@ -75,6 +76,7 @@ }, "devDependencies": { "@babel/plugin-transform-modules-commonjs": "^7.24.8", + "@types/json-bigint": "^1.0.4", "@types/lodash": "^4.17.7", "@types/node": "^16.7.13", "@types/react": "^18.0.0", diff --git a/src/screens/SendTxEmbed.tsx b/src/screens/SendTxEmbed.tsx index 00d855c..025f0ed 100644 --- a/src/screens/SendTxEmbed.tsx +++ b/src/screens/SendTxEmbed.tsx @@ -1,16 +1,14 @@ -import React, { useEffect, useState, useCallback, useMemo } from 'react'; +import React, { useEffect, useState, useCallback } from 'react'; import { ScrollView, View } from 'react-native'; import { - ActivityIndicator, Button, Text, - TextInput, } from 'react-native-paper'; -import { Box } from '@mui/system'; +import JSONbig from 'json-bigint'; +import { AuthInfo, SignDoc } from "cosmjs-types/cosmos/tx/v1beta1/tx"; -import { DirectSecp256k1Wallet, Algo } from '@cosmjs/proto-signing'; -import { SignDoc } from "cosmjs-types/cosmos/tx/v1beta1/tx"; -import { calculateFee, GasPrice, SigningStargateClient } from '@cosmjs/stargate'; +import { DirectSecp256k1Wallet, Algo, TxBodyEncodeObject } from '@cosmjs/proto-signing'; +import { SigningStargateClient } from '@cosmjs/stargate'; import { retrieveAccounts, retrieveSingleAccount } from '../utils/accounts'; // Use retrieveAccounts import AccountDetails from '../components/AccountDetails'; @@ -21,14 +19,15 @@ import { useNetworks } from '../context/NetworksContext'; import TxErrorDialog from '../components/TxErrorDialog'; import { Account, NetworksDataState } from '../types'; -// --- Type Definitions --- - const GET_ACCOUNTS_RESPONSE = "GET_ACCOUNTS_RESPONSE"; const SIGN_ONBOARD_TX_RESPONSE = "SIGN_ONBOARD_TX_RESPONSE"; +// Type Definitions + interface SignOnboardTxRequestData { address: string; signDoc: SignDoc; + txBody: TxBodyEncodeObject; } interface GetAccountsRequestData {} // Currently no specific data needed @@ -51,6 +50,8 @@ type TransactionDetails = { requestedNetwork: NetworksDataState; balance: string; signDoc: SignDoc; // Deserialized SignDoc + txBody: TxBodyEncodeObject; + // AuthInfo: SignerInfo[]; }; interface GetAccountsResponse { @@ -61,35 +62,15 @@ interface GetAccountsResponse { }>; } -interface SignDirectResponseData { - signed: { - bodyBytes: string; // base64 - authInfoBytes: string; // base64 - chainId: string; - accountNumber: string; // string representation of BigInt - }; - signature: { - pub_key: { - type: string; // e.g., "tendermint/PubKeySecp256k1" - value: string; // base64 encoded pubkey value - }; - signature: string; // base64 encoded signature - }; -} - -// --- Component --- - export const SendTxEmbed = () => { const [isTxApprovalVisible, setIsTxApprovalVisible] = useState(false); const [transactionDetails, setTransactionDetails] = useState(null); - const [fees, setFees] = useState(''); - const [gasLimit, setGasLimit] = useState('200000'); // TODO: Revisit gas estimation const [isTxLoading, setIsTxLoading] = useState(false); const [txError, setTxError] = useState(null); const { networksData } = useNetworks(); - // --- Message Handlers --- + // Message Handlers const handleGetAccountsRequest = useCallback(async (event: MessageEvent) => { const { id } = event.data; @@ -142,7 +123,7 @@ export const SendTxEmbed = () => { setTxError(null); try { - const { address: signerAddress, signDoc } = requestData; + const { address: signerAddress, signDoc, txBody } = requestData; const network = networksData.find(net => net.chainId === signDoc.chainId); if (!network) throw new Error(`Network with chainId "${signDoc.chainId}" not supported.`); @@ -151,13 +132,14 @@ export const SendTxEmbed = () => { if (!account) throw new Error(`Account not found for address "${signerAddress}" on chain "${signDoc.chainId}".`); // Balance Check - let balanceAmount = '0'; + let balanceAmount = 'N/A'; try { // Use a temporary read-only client for balance check if possible, or the signing client const tempWallet = await DirectSecp256k1Wallet.fromKey( new Uint8Array(Buffer.from((await getPathKey(`${network.namespace}:${network.chainId}`, account.index)).privKey.replace(/^0x/, ''), 'hex')), // Wrap in Uint8Array network.addressPrefix ); + const client = await SigningStargateClient.connectWithSigner(network.rpcUrl!, tempWallet); const balance = await client.getBalance(account.address, network.nativeDenom!); balanceAmount = balance.amount; @@ -166,11 +148,6 @@ export const SendTxEmbed = () => { console.warn("Could not retrieve balance:", balanceError); } - // Fee Calculation - const gasPrice = GasPrice.fromString(`${network.gasPrice}${network.nativeDenom}`); - const calculatedFee = calculateFee(Number(gasLimit), gasPrice); - setFees(calculatedFee.amount[0].amount); - setTransactionDetails({ requestId: id, source: source, @@ -181,6 +158,7 @@ export const SendTxEmbed = () => { requestedNetwork: network, balance: balanceAmount, signDoc, + txBody }); setIsTxApprovalVisible(true); @@ -196,7 +174,7 @@ export const SendTxEmbed = () => { } setTxError(errorMsg); } - }, [networksData, gasLimit]); // Dependencies: networksData, gasLimit + }, [networksData]); // Dependencies: networksData, gasLimit const handleIncomingMessage = useCallback((event: MessageEvent) => { if (!event.data || typeof event.data !== 'object' || !event.data.type || !event.data.id || !event.source || event.source === window) { @@ -226,7 +204,7 @@ export const SendTxEmbed = () => { }; }, [handleIncomingMessage]); - // --- UI Action Handlers --- + // Action Handlers const acceptRequestHandler = async () => { if (!transactionDetails) { @@ -279,124 +257,98 @@ export const SendTxEmbed = () => { setTxError(null); }; - // --- Display Logic --- - - const safeStringify = useCallback((obj: any, replacer: any = null, space: number = 2) => { - return JSON.stringify( - obj, - (key, value) => { - if (typeof value === 'bigint') { - return value.toString(); - } - return replacer ? replacer(key, value) : value; - }, - space - ); - }, []) - - const decodeUint8Arrays = useCallback((obj: any): any => { - if (obj instanceof Uint8Array) { - try { - return new TextDecoder().decode(obj); - } catch (e) { - return obj; // fallback if decoding fails - } - } else if (Array.isArray(obj)) { - return obj.map(decodeUint8Arrays); - } else if (obj && typeof obj === 'object') { - const newObj: any = {}; - for (const [key, value] of Object.entries(obj)) { - newObj[key] = decodeUint8Arrays(value); - } - return newObj; - } - return obj; - }, []) - - const displaySignDoc = useMemo(() => { - if (!transactionDetails?.signDoc) return null; - - try { - const signDocCopy = typeof structuredClone === 'function' - ? structuredClone(transactionDetails.signDoc) - : JSON.parse(safeStringify(transactionDetails.signDoc)); - - // Attempt to parse attestation - if ( - signDocCopy.msgs && - signDocCopy.msgs[0]?.value?.attestation && - typeof signDocCopy.msgs[0].value.attestation === 'string' - ) { - try { - signDocCopy.msgs[0].value.attestation = JSON.parse(signDocCopy.msgs[0].value.attestation); - } catch (e) { - console.warn('Could not parse attestation string:', e); - } - } - - const decoded = decodeUint8Arrays(signDocCopy); - return decoded; - } catch (e) { - console.error('Error processing SignDoc:', e); - return transactionDetails.signDoc; - } - }, [transactionDetails?.signDoc, decodeUint8Arrays, safeStringify]); - - // --- Render --- - return ( <> {isTxApprovalVisible && transactionDetails ? ( - - - Sign Request From - - Origin: {transactionDetails.origin} + <> + + + Account + + + - - - Account - - + + + + + Transaction Body + + + {JSONbig.stringify(transactionDetails.txBody, null, 2)} + + - - - - Transaction Details - -
-                {safeStringify(displaySignDoc, null, 2)}
-              
-
-
- - - - + + + Auth Info + + + {JSONbig.stringify( + { + ...AuthInfo.decode(transactionDetails.signDoc.authInfoBytes), + signerInfos: AuthInfo.decode(transactionDetails.signDoc.authInfoBytes).signerInfos.map((info) => ({ + ...info, + publicKey: info.publicKey + ? { + ...info.publicKey, + value: info.publicKey.value.toString(), + } + : undefined, + })), + }, + null, + 2 + )} + + + + + + Transaction Data To Be Signed + + + {JSONbig.stringify( + { + ...transactionDetails.signDoc, + bodyBytes: transactionDetails.signDoc.bodyBytes?.toString?.() ?? transactionDetails.signDoc.bodyBytes, + authInfoBytes: transactionDetails.signDoc.authInfoBytes?.toString?.() ?? transactionDetails.signDoc.authInfoBytes, + }, + null, + 2 + )} + + + +
+ - - - + ) : ( Waiting for request... diff --git a/src/styles/stylesheet.js b/src/styles/stylesheet.js index a71df18..cb2f368 100644 --- a/src/styles/stylesheet.js +++ b/src/styles/stylesheet.js @@ -355,6 +355,33 @@ const styles = StyleSheet.create({ marginTop: 12, marginBottom: 20, }, + section: { + marginBottom: 16, + }, + sectionTitle: { + fontSize: 24, + fontWeight: 'bold', + marginBottom: 8, + }, + codeContainer: { + backgroundColor: '#e0e0e0', + borderRadius: 6, + maxHeight: 200, + }, + codeText: { + fontFamily: 'monospace', + fontSize: 12, + color: '#333', + }, + feeContainer: { + flexDirection: 'row', + justifyContent: 'space-between', + gap: 10, + marginBottom: 16, + }, + input: { + flex: 1, + }, }); export default styles; diff --git a/yarn.lock b/yarn.lock index 054ff5b..9caa949 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3939,6 +3939,11 @@ expect "^29.0.0" pretty-format "^29.0.0" +"@types/json-bigint@^1.0.4": + version "1.0.4" + resolved "https://registry.yarnpkg.com/@types/json-bigint/-/json-bigint-1.0.4.tgz#250d29e593375499d8ba6efaab22d094c3199ef3" + integrity sha512-ydHooXLbOmxBbubnA7Eh+RpBzuaIiQjh8WGJYQB50JFGFrdxW7JzVlyEV7fAXw0T2sqJ1ysTneJbiyNLqZRAag== + "@types/json-schema@*", "@types/json-schema@^7.0.12", "@types/json-schema@^7.0.4", "@types/json-schema@^7.0.5", "@types/json-schema@^7.0.8", "@types/json-schema@^7.0.9": version "7.0.15" resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841" @@ -5375,6 +5380,11 @@ big.js@^5.2.2: resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328" integrity sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ== +bignumber.js@^9.0.0: + version "9.3.0" + resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-9.3.0.tgz#bdba7e2a4c1a2eba08290e8dcad4f36393c92acd" + integrity sha512-EM7aMFTXbptt/wZdMlBv2t8IViwQL+h6SLHosp8Yf0dqJMTnY6iL32opnAB6kAdL0SZPuvcAzFr31o0c/R3/RA== + binary-extensions@^2.0.0: version "2.3.0" resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.3.0.tgz#f6e14a97858d327252200242d4ccfe522c445522" @@ -9822,6 +9832,13 @@ jsesc@~0.5.0: resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d" integrity sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA== +json-bigint@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/json-bigint/-/json-bigint-1.0.0.tgz#ae547823ac0cad8398667f8cd9ef4730f5b01ff1" + integrity sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ== + dependencies: + bignumber.js "^9.0.0" + json-buffer@3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13" -- 2.45.2 From a27b3c6233cbf36fbf3ebde33839a9dcff712b95 Mon Sep 17 00:00:00 2001 From: Shreerang Kale Date: Wed, 30 Apr 2025 18:12:00 +0530 Subject: [PATCH 05/19] Convert bytes to hex in tx data --- package.json | 1 + src/screens/SendTxEmbed.tsx | 7 ++++--- yarn.lock | 9 +++++++++ 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index 3a5956c..8d8b52d 100644 --- a/package.json +++ b/package.json @@ -6,6 +6,7 @@ "@cerc-io/registry-sdk": "^0.2.5", "@cosmjs/amino": "^0.32.3", "@cosmjs/crypto": "^0.32.3", + "@cosmjs/encoding": "^0.33.1", "@cosmjs/proto-signing": "^0.32.3", "@cosmjs/stargate": "^0.32.3", "@emotion/react": "^11.13.0", diff --git a/src/screens/SendTxEmbed.tsx b/src/screens/SendTxEmbed.tsx index 025f0ed..1a56fe8 100644 --- a/src/screens/SendTxEmbed.tsx +++ b/src/screens/SendTxEmbed.tsx @@ -9,6 +9,7 @@ import { AuthInfo, SignDoc } from "cosmjs-types/cosmos/tx/v1beta1/tx"; import { DirectSecp256k1Wallet, Algo, TxBodyEncodeObject } from '@cosmjs/proto-signing'; import { SigningStargateClient } from '@cosmjs/stargate'; +import { toHex } from '@cosmjs/encoding'; import { retrieveAccounts, retrieveSingleAccount } from '../utils/accounts'; // Use retrieveAccounts import AccountDetails from '../components/AccountDetails'; @@ -298,7 +299,7 @@ export const SendTxEmbed = () => { publicKey: info.publicKey ? { ...info.publicKey, - value: info.publicKey.value.toString(), + value: toHex(info.publicKey.value), } : undefined, })), @@ -317,8 +318,8 @@ export const SendTxEmbed = () => { {JSONbig.stringify( { ...transactionDetails.signDoc, - bodyBytes: transactionDetails.signDoc.bodyBytes?.toString?.() ?? transactionDetails.signDoc.bodyBytes, - authInfoBytes: transactionDetails.signDoc.authInfoBytes?.toString?.() ?? transactionDetails.signDoc.authInfoBytes, + bodyBytes: toHex(transactionDetails.signDoc.bodyBytes), + authInfoBytes: toHex(transactionDetails.signDoc.authInfoBytes), }, null, 2 diff --git a/yarn.lock b/yarn.lock index 9caa949..e7b7d0c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1410,6 +1410,15 @@ bech32 "^1.1.4" readonly-date "^1.0.0" +"@cosmjs/encoding@^0.33.1": + version "0.33.1" + resolved "https://registry.yarnpkg.com/@cosmjs/encoding/-/encoding-0.33.1.tgz#77d6a8e0152c658ecf07b5aee3f5968d9071da50" + integrity sha512-nuNxf29fUcQE14+1p//VVQDwd1iau5lhaW/7uMz7V2AH3GJbFJoJVaKvVyZvdFk+Cnu+s3wCqgq4gJkhRCJfKw== + dependencies: + base64-js "^1.3.0" + bech32 "^1.1.4" + readonly-date "^1.0.0" + "@cosmjs/json-rpc@^0.32.4": version "0.32.4" resolved "https://registry.yarnpkg.com/@cosmjs/json-rpc/-/json-rpc-0.32.4.tgz#be91eb89ea78bd5dc02d0a9fa184dd6790790f0b" -- 2.45.2 From 36266ca11fd02684595e671477b0660e60506d6d Mon Sep 17 00:00:00 2001 From: Shreerang Kale Date: Fri, 2 May 2025 10:11:13 +0530 Subject: [PATCH 06/19] Fix decoding cosmos pubkey --- src/screens/SendTxEmbed.tsx | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/src/screens/SendTxEmbed.tsx b/src/screens/SendTxEmbed.tsx index 1a56fe8..b17010d 100644 --- a/src/screens/SendTxEmbed.tsx +++ b/src/screens/SendTxEmbed.tsx @@ -7,7 +7,7 @@ import { import JSONbig from 'json-bigint'; import { AuthInfo, SignDoc } from "cosmjs-types/cosmos/tx/v1beta1/tx"; -import { DirectSecp256k1Wallet, Algo, TxBodyEncodeObject } from '@cosmjs/proto-signing'; +import { DirectSecp256k1Wallet, Algo, TxBodyEncodeObject, decodeOptionalPubkey } from '@cosmjs/proto-signing'; import { SigningStargateClient } from '@cosmjs/stargate'; import { toHex } from '@cosmjs/encoding'; @@ -296,12 +296,7 @@ export const SendTxEmbed = () => { ...AuthInfo.decode(transactionDetails.signDoc.authInfoBytes), signerInfos: AuthInfo.decode(transactionDetails.signDoc.authInfoBytes).signerInfos.map((info) => ({ ...info, - publicKey: info.publicKey - ? { - ...info.publicKey, - value: toHex(info.publicKey.value), - } - : undefined, + publicKey: decodeOptionalPubkey(info.publicKey) })), }, null, -- 2.45.2 From b1cefe4f6c74f8b1e86c778fe40b771f60242d4e Mon Sep 17 00:00:00 2001 From: Shreerang Kale Date: Fri, 2 May 2025 11:08:21 +0530 Subject: [PATCH 07/19] Refactor code --- src/screens/SendTxEmbed.tsx | 141 ++++++++++++++++++++++-------------- src/utils/accounts.ts | 12 +++ 2 files changed, 98 insertions(+), 55 deletions(-) diff --git a/src/screens/SendTxEmbed.tsx b/src/screens/SendTxEmbed.tsx index b17010d..71cae25 100644 --- a/src/screens/SendTxEmbed.tsx +++ b/src/screens/SendTxEmbed.tsx @@ -11,7 +11,7 @@ import { DirectSecp256k1Wallet, Algo, TxBodyEncodeObject, decodeOptionalPubkey } import { SigningStargateClient } from '@cosmjs/stargate'; import { toHex } from '@cosmjs/encoding'; -import { retrieveAccounts, retrieveSingleAccount } from '../utils/accounts'; // Use retrieveAccounts +import { getCosmosAccount, retrieveAccounts, retrieveSingleAccount } from '../utils/accounts'; import AccountDetails from '../components/AccountDetails'; import styles from '../styles/stylesheet'; import DataBox from '../components/DataBox'; @@ -25,14 +25,16 @@ const SIGN_ONBOARD_TX_RESPONSE = "SIGN_ONBOARD_TX_RESPONSE"; // Type Definitions +interface GetAccountsRequestData { + chainId: string, +} + interface SignOnboardTxRequestData { address: string; signDoc: SignDoc; txBody: TxBodyEncodeObject; } -interface GetAccountsRequestData {} // Currently no specific data needed - type IncomingMessageData = SignOnboardTxRequestData | GetAccountsRequestData; interface IncomingMessageEventData { @@ -47,19 +49,18 @@ type TransactionDetails = { origin: string; signerAddress: string; chainId: string; - account: Account; // Wallet's internal Account type + account: Account; requestedNetwork: NetworksDataState; balance: string; - signDoc: SignDoc; // Deserialized SignDoc + signDoc: SignDoc; txBody: TxBodyEncodeObject; - // AuthInfo: SignerInfo[]; }; interface GetAccountsResponse { accounts: Array<{ algo: Algo; address: string; - pubkey: string; // hex encoded pubkey + pubkey: string; }>; } @@ -74,29 +75,34 @@ export const SendTxEmbed = () => { // Message Handlers const handleGetAccountsRequest = useCallback(async (event: MessageEvent) => { - const { id } = event.data; + const { id, data } = event.data; const source = event.source as Window; const origin = event.origin; + const requestData = data as GetAccountsRequestData; console.log("Received GET_ACCOUNTS_REQUEST", id); try { - const zenithNetworkData = networksData.find(networkData => networkData.chainId === "zenith-testnet") + const requestedNetworkData = networksData.find(networkData => networkData.chainId === requestData.chainId) - if(!zenithNetworkData) { + if(!requestedNetworkData) { throw new Error("Zenith network data not found") } // Ensure retrieveAccounts exists and returns Account[] - const allAccounts = await retrieveAccounts(zenithNetworkData); // Use retrieveAccounts + const allAccounts = await retrieveAccounts(requestedNetworkData); // Use retrieveAccounts if (!allAccounts || allAccounts.length === 0) { throw new Error("Accounts not found for zenithNetwork") } - const responseAccounts = allAccounts.map((acc) => ({ - algo: 'secp256k1' as Algo, // Assuming secp256k1 - address: acc.address, - pubkey: acc.pubKey.startsWith('0x') ? acc.pubKey : `0x${acc.pubKey}`, // Ensure hex format - })); + const responseAccounts = await Promise.all( + allAccounts.map(async (acc) => { + const cosmosAccount = await getCosmosAccount(acc.hdPath, requestedNetworkData.addressPrefix!); + return { + ...cosmosAccount, + pubkey: toHex(cosmosAccount.pubkey), + }; + }) + ); const response: GetAccountsResponse = { accounts: responseAccounts }; sendMessage(source, GET_ACCOUNTS_RESPONSE, {id, data: response}, origin); @@ -110,7 +116,7 @@ export const SendTxEmbed = () => { console.error("Cannot send error message: source is not a Window"); } } - }, [networksData]); // Add dependencies like retrieveAccounts if needed + }, [networksData]); const handleSignOnboardTxRequest = useCallback(async (event: MessageEvent) => { const { id, data } = event.data; @@ -119,7 +125,7 @@ export const SendTxEmbed = () => { const requestData = data as SignOnboardTxRequestData; console.log("Received SIGN_ONBOARD_TX_REQUEST", id); - setIsTxApprovalVisible(false); // Hide previous request first + setIsTxApprovalVisible(false); setTransactionDetails(null); setTxError(null); @@ -135,9 +141,9 @@ export const SendTxEmbed = () => { // Balance Check let balanceAmount = 'N/A'; try { - // Use a temporary read-only client for balance check if possible, or the signing client + // Use a temporary read-only client for balance const tempWallet = await DirectSecp256k1Wallet.fromKey( - new Uint8Array(Buffer.from((await getPathKey(`${network.namespace}:${network.chainId}`, account.index)).privKey.replace(/^0x/, ''), 'hex')), // Wrap in Uint8Array + new Uint8Array(Buffer.from((await getPathKey(`${network.namespace}:${network.chainId}`, account.index)).privKey.replace(/^0x/, ''), 'hex')), network.addressPrefix ); @@ -167,15 +173,11 @@ export const SendTxEmbed = () => { } catch (error: unknown) { console.error("Error handling SIGN_ONBOARD_TX_REQUEST:", error); const errorMsg = error instanceof Error ? error.message : String(error); - // Check if source is a Window before sending message - if (source instanceof Window) { - sendMessage(source, id, { error: `Failed to prepare transaction: ${errorMsg}` }, origin); - } else { - console.error("Cannot send error message: source is not a Window"); - } + + sendMessage(source, id, { error: `Failed to prepare transaction: ${errorMsg}` }, origin); setTxError(errorMsg); } - }, [networksData]); // Dependencies: networksData, gasLimit + }, [networksData]); const handleIncomingMessage = useCallback((event: MessageEvent) => { if (!event.data || typeof event.data !== 'object' || !event.data.type || !event.data.id || !event.source || event.source === window) { @@ -235,13 +237,9 @@ export const SendTxEmbed = () => { } catch (error: unknown) { console.error("Error during signDirect:", error); const errorMsg = error instanceof Error ? error.message : String(error); + setTxError(errorMsg); - // Check if source is a Window before sending message - if (source instanceof Window) { - sendMessage(source, SIGN_ONBOARD_TX_RESPONSE, {id: requestId, error: `Failed to sign transaction: ${errorMsg}` }, origin); - } else { - console.error("Cannot send error message: source is not a Window"); - } + sendMessage(source as Window, SIGN_ONBOARD_TX_RESPONSE, {id: requestId, error: `Failed to sign transaction: ${errorMsg}` }, origin); } finally { setIsTxLoading(false); } @@ -251,13 +249,64 @@ export const SendTxEmbed = () => { if (!transactionDetails) return; const { requestId, source, origin } = transactionDetails; console.log("Rejecting request:", requestId); - // Check if source is a Window before sending message + sendMessage(source as Window, SIGN_ONBOARD_TX_RESPONSE, {id: requestId, error: "User rejected the signature request." }, origin); setIsTxApprovalVisible(false); setTransactionDetails(null); setTxError(null); }; + const decodedAuth = React.useMemo(() => { + if (!transactionDetails) { + return + } + + const info = AuthInfo.decode(transactionDetails.signDoc.authInfoBytes); + return { + ...info, + signerInfos: info.signerInfos.map((signerInfo) => ({ + ...signerInfo, + publicKey: decodeOptionalPubkey(signerInfo.publicKey), + })), + }; + }, [transactionDetails]); + + const formattedTxBody = React.useMemo( + () => { + if (!transactionDetails) { + return + } + + return JSONbig.stringify(transactionDetails.txBody, null, 2) + }, + [transactionDetails] + ); + + const formattedAuthInfo = React.useMemo( + () => JSONbig.stringify(decodedAuth, null, 2), + [decodedAuth] + ); + + const formattedSignDoc = React.useMemo( + () => + { + if (!transactionDetails) { + return + } + + return JSONbig.stringify( + { + ...transactionDetails.signDoc, + bodyBytes: toHex(transactionDetails.signDoc.bodyBytes), + authInfoBytes: toHex(transactionDetails.signDoc.authInfoBytes), + }, + null, + 2 + ) + }, + [transactionDetails] + ); + return ( <> {isTxApprovalVisible && transactionDetails ? ( @@ -282,7 +331,7 @@ export const SendTxEmbed = () => { contentContainerStyle={{ padding: 10 }} > - {JSONbig.stringify(transactionDetails.txBody, null, 2)} + {formattedTxBody} @@ -291,17 +340,7 @@ export const SendTxEmbed = () => { Auth Info - {JSONbig.stringify( - { - ...AuthInfo.decode(transactionDetails.signDoc.authInfoBytes), - signerInfos: AuthInfo.decode(transactionDetails.signDoc.authInfoBytes).signerInfos.map((info) => ({ - ...info, - publicKey: decodeOptionalPubkey(info.publicKey) - })), - }, - null, - 2 - )} + {formattedAuthInfo}
@@ -310,15 +349,7 @@ export const SendTxEmbed = () => { Transaction Data To Be Signed - {JSONbig.stringify( - { - ...transactionDetails.signDoc, - bodyBytes: toHex(transactionDetails.signDoc.bodyBytes), - authInfoBytes: toHex(transactionDetails.signDoc.authInfoBytes), - }, - null, - 2 - )} + {formattedSignDoc} diff --git a/src/utils/accounts.ts b/src/utils/accounts.ts index d7921a6..cc891dd 100644 --- a/src/utils/accounts.ts +++ b/src/utils/accounts.ts @@ -290,6 +290,17 @@ const accountInfoFromHDPath = async ( return { privKey, pubKey, address }; }; +const getCosmosAccount = async (hdPath: string, addressPrefix: string) => { + const mnemonicStore = getInternetCredentials('mnemonicServer'); + if (!mnemonicStore) { + throw new Error('Mnemonic not found!'); + } + + const mnemonic = mnemonicStore; + + return (await getCosmosAccounts(mnemonic, hdPath, addressPrefix)).data +} + const getNextAccountId = async (namespaceChainId: string): Promise => { const idStore = await getInternetCredentials( `addAccountCounter/${namespaceChainId}`, @@ -352,4 +363,5 @@ export { getNextAccountId, updateAccountCounter, getCosmosAccounts, + getCosmosAccount }; -- 2.45.2 From f7f9fec4a9c76d4b0ac4f43203af2947a2bdbbac Mon Sep 17 00:00:00 2001 From: Shreerang Kale Date: Fri, 2 May 2025 11:15:07 +0530 Subject: [PATCH 08/19] Rename sign tx component --- src/screens/{SendTxEmbed.tsx => SignTxEmbed.tsx} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/screens/{SendTxEmbed.tsx => SignTxEmbed.tsx} (100%) diff --git a/src/screens/SendTxEmbed.tsx b/src/screens/SignTxEmbed.tsx similarity index 100% rename from src/screens/SendTxEmbed.tsx rename to src/screens/SignTxEmbed.tsx -- 2.45.2 From 735e881a839a1068816fe166442739e0b2bb0658 Mon Sep 17 00:00:00 2001 From: Shreerang Kale Date: Fri, 2 May 2025 12:15:23 +0530 Subject: [PATCH 09/19] Refactor iframe request and response types --- src/App.tsx | 6 ++-- src/hooks/useGetOrCreateAccounts.ts | 3 +- src/screens/AutoSignIn.tsx | 6 ++-- src/screens/SignRequestEmbed.tsx | 20 +++++++++---- src/screens/SignTxEmbed.tsx | 45 ++++++++++++++--------------- src/screens/WalletEmbed.tsx | 14 ++++----- src/types.ts | 2 +- src/utils/constants.ts | 16 ++++++++++ 8 files changed, 68 insertions(+), 44 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index 56845fa..80272a4 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -44,7 +44,7 @@ import { useWebViewHandler } from "./hooks/useWebViewHandler"; import SignRequestEmbed from "./screens/SignRequestEmbed"; import useAddAccountEmbed from "./hooks/useAddAccountEmbed"; import useExportPKEmbed from "./hooks/useExportPrivateKeyEmbed"; -import { SendTxEmbed } from "./screens/SendTxEmbed"; +import { SignTxEmbed } from "./screens/SignTxEmbed"; const Stack = createStackNavigator(); @@ -390,8 +390,8 @@ const App = (): React.JSX.Element => { }} /> <>, }} diff --git a/src/hooks/useGetOrCreateAccounts.ts b/src/hooks/useGetOrCreateAccounts.ts index 2a111d9..ce11947 100644 --- a/src/hooks/useGetOrCreateAccounts.ts +++ b/src/hooks/useGetOrCreateAccounts.ts @@ -5,6 +5,7 @@ import { sendMessage } from "../utils/misc"; import useAccountsData from "./useAccountsData"; import { useNetworks } from "../context/NetworksContext"; import { useAccounts } from "../context/AccountsContext"; +import { WALLET_ACCOUNTS_DATA } from "../utils/constants"; const REACT_APP_ALLOWED_URLS = process.env.REACT_APP_ALLOWED_URLS; @@ -50,7 +51,7 @@ const useGetOrCreateAccounts = () => { console.log('Sending WALLET_ACCOUNTS_DATA accounts:', accountsAddressList); sendMessage( - event.source as Window, 'WALLET_ACCOUNTS_DATA', + event.source as Window, WALLET_ACCOUNTS_DATA, accountsAddressList, event.origin ); diff --git a/src/screens/AutoSignIn.tsx b/src/screens/AutoSignIn.tsx index 0a16e00..1354534 100644 --- a/src/screens/AutoSignIn.tsx +++ b/src/screens/AutoSignIn.tsx @@ -2,7 +2,7 @@ import React, { useEffect } from 'react'; import { useNetworks } from '../context/NetworksContext'; import { signMessage } from '../utils/sign-message'; -import { EIP155 } from '../utils/constants'; +import { AUTO_SIGN_IN, EIP155, SIGN_IN_RESPONSE } from '../utils/constants'; import { sendMessage } from '../utils/misc'; import useAccountsData from '../hooks/useAccountsData'; import useGetOrCreateAccounts from '../hooks/useGetOrCreateAccounts'; @@ -16,7 +16,7 @@ export const AutoSignIn = () => { useEffect(() => { const handleSignIn = async (event: MessageEvent) => { - if (event.data.type !== 'AUTO_SIGN_IN') return; + if (event.data.type !== AUTO_SIGN_IN) return; if (!REACT_APP_ALLOWED_URLS) { console.log('Allowed URLs are not set'); @@ -38,7 +38,7 @@ export const AutoSignIn = () => { const signature = await signMessage({ message: event.data.message, accountId: accountsData[0].index, chainId: event.data.chainId, namespace: EIP155 }) - sendMessage(event.source as Window, 'SIGN_IN_RESPONSE', { message: event.data.message, signature }, event.origin); + sendMessage(event.source as Window, SIGN_IN_RESPONSE, { message: event.data.message, signature }, event.origin); }; window.addEventListener('message', handleSignIn); diff --git a/src/screens/SignRequestEmbed.tsx b/src/screens/SignRequestEmbed.tsx index 7a3fbfd..3cafa44 100644 --- a/src/screens/SignRequestEmbed.tsx +++ b/src/screens/SignRequestEmbed.tsx @@ -14,7 +14,8 @@ import AccountDetails from '../components/AccountDetails'; import styles from '../styles/stylesheet'; import { getCosmosAccounts, retrieveSingleAccount } from '../utils/accounts'; import { getMnemonic, getPathKey, sendMessage } from '../utils/misc'; -import { COSMOS } from '../utils/constants'; +import { COSMOS, SIGN_MESSAGE, SIGNED_MESSAGE } from '../utils/constants'; +import { useNetworks } from '../context/NetworksContext'; const REACT_APP_ALLOWED_URLS = process.env.REACT_APP_ALLOWED_URLS; @@ -31,6 +32,7 @@ const SignRequestEmbed = ({ route }: SignRequestProps) => { const [isLoading, setIsLoading] = useState(true); const [isApproving, setIsApproving] = useState(false); + const { networksData } = useNetworks(); const navigation = useNavigation>(); @@ -42,7 +44,13 @@ const SignRequestEmbed = ({ route }: SignRequestProps) => { const requestAccount = await retrieveSingleAccount(COSMOS, chainId, signerAddress); const path = (await getPathKey(`${COSMOS}:${chainId}`, requestAccount!.index)).path; const mnemonic = await getMnemonic(); - const cosmosAccount = await getCosmosAccounts(mnemonic, path, 'zenith'); + + const requestedNetworkData = networksData.find(networkData => networkData.chainId === chainId) + if (!requestedNetworkData) { + throw new Error("Requested network not found") + } + + const cosmosAccount = await getCosmosAccounts(mnemonic, path, requestedNetworkData?.addressPrefix); const cosmosAminoSignature = await cosmosAccount.cosmosWallet.signAmino( signerAddress, @@ -53,7 +61,7 @@ const SignRequestEmbed = ({ route }: SignRequestProps) => { sendMessage( sourceWindow, - 'ZENITH_SIGNED_MESSAGE', + SIGNED_MESSAGE, { signature }, origin, ); @@ -63,7 +71,7 @@ const SignRequestEmbed = ({ route }: SignRequestProps) => { console.error('Signing failed:', err); sendMessage( sourceWindow!, - 'ZENITH_SIGNED_MESSAGE', + SIGNED_MESSAGE, { error: err }, origin, ); @@ -76,7 +84,7 @@ const SignRequestEmbed = ({ route }: SignRequestProps) => { if (sourceWindow && origin) { sendMessage( sourceWindow, - 'ZENITH_SIGNED_MESSAGE', + SIGNED_MESSAGE, { error: 'User rejected the request' }, origin, ); @@ -85,7 +93,7 @@ const SignRequestEmbed = ({ route }: SignRequestProps) => { useEffect(() => { const handleCosmosSignMessage = async (event: MessageEvent) => { - if (event.data.type !== 'SIGN_ZENITH_MESSAGE') return; + if (event.data.type !== SIGN_MESSAGE) return; if (!REACT_APP_ALLOWED_URLS) { diff --git a/src/screens/SignTxEmbed.tsx b/src/screens/SignTxEmbed.tsx index 71cae25..3d7153d 100644 --- a/src/screens/SignTxEmbed.tsx +++ b/src/screens/SignTxEmbed.tsx @@ -19,9 +19,7 @@ import { getPathKey, sendMessage } from '../utils/misc'; import { useNetworks } from '../context/NetworksContext'; import TxErrorDialog from '../components/TxErrorDialog'; import { Account, NetworksDataState } from '../types'; - -const GET_ACCOUNTS_RESPONSE = "GET_ACCOUNTS_RESPONSE"; -const SIGN_ONBOARD_TX_RESPONSE = "SIGN_ONBOARD_TX_RESPONSE"; +import { REQUEST_SIGN_TX, REQUEST_COSMOS_ACCOUNTS_DATA, COSMOS_ACCOUNTS_RESPONSE, SIGN_TX_RESPONSE } from '../utils/constants'; // Type Definitions @@ -39,7 +37,7 @@ type IncomingMessageData = SignOnboardTxRequestData | GetAccountsRequestData; interface IncomingMessageEventData { id: string; - type: 'SIGN_ONBOARD_TX_REQUEST' | 'GET_ACCOUNTS_REQUEST'; + type: typeof REQUEST_SIGN_TX | typeof REQUEST_COSMOS_ACCOUNTS_DATA; data: IncomingMessageData; } @@ -64,7 +62,7 @@ interface GetAccountsResponse { }>; } -export const SendTxEmbed = () => { +export const SignTxEmbed = () => { const [isTxApprovalVisible, setIsTxApprovalVisible] = useState(false); const [transactionDetails, setTransactionDetails] = useState(null); const [isTxLoading, setIsTxLoading] = useState(false); @@ -74,26 +72,27 @@ export const SendTxEmbed = () => { // Message Handlers - const handleGetAccountsRequest = useCallback(async (event: MessageEvent) => { + const handleGetCosmosAccountsRequest = useCallback(async (event: MessageEvent) => { const { id, data } = event.data; const source = event.source as Window; const origin = event.origin; const requestData = data as GetAccountsRequestData; - console.log("Received GET_ACCOUNTS_REQUEST", id); + console.log(`Received ${REQUEST_COSMOS_ACCOUNTS_DATA}`, id); try { const requestedNetworkData = networksData.find(networkData => networkData.chainId === requestData.chainId) if(!requestedNetworkData) { - throw new Error("Zenith network data not found") + throw new Error("Network data not found") } - // Ensure retrieveAccounts exists and returns Account[] - const allAccounts = await retrieveAccounts(requestedNetworkData); // Use retrieveAccounts + + const allAccounts = await retrieveAccounts(requestedNetworkData); if (!allAccounts || allAccounts.length === 0) { - throw new Error("Accounts not found for zenithNetwork") + throw new Error("Accounts not found for network") } + // TODO: Refactor getCosmosAccounts functions to return all accounts const responseAccounts = await Promise.all( allAccounts.map(async (acc) => { const cosmosAccount = await getCosmosAccount(acc.hdPath, requestedNetworkData.addressPrefix!); @@ -105,13 +104,13 @@ export const SendTxEmbed = () => { ); const response: GetAccountsResponse = { accounts: responseAccounts }; - sendMessage(source, GET_ACCOUNTS_RESPONSE, {id, data: response}, origin); + sendMessage(source, COSMOS_ACCOUNTS_RESPONSE, {id, data: response}, origin); } catch (error: unknown) { - console.error("Error handling GET_ACCOUNTS_REQUEST:", error); + console.error(`Error handling ${REQUEST_COSMOS_ACCOUNTS_DATA}:`, error); const errorMsg = error instanceof Error ? error.message : String(error); // Check if source is a Window before sending message if (source instanceof Window) { - sendMessage(source, GET_ACCOUNTS_RESPONSE, { id, error: `Failed to get accounts: ${errorMsg}` }, origin); + sendMessage(source, COSMOS_ACCOUNTS_RESPONSE, { id, error: `Failed to get accounts: ${errorMsg}` }, origin); } else { console.error("Cannot send error message: source is not a Window"); } @@ -124,7 +123,7 @@ export const SendTxEmbed = () => { const origin = event.origin; const requestData = data as SignOnboardTxRequestData; - console.log("Received SIGN_ONBOARD_TX_REQUEST", id); + console.log(`Received ${REQUEST_SIGN_TX}`, id); setIsTxApprovalVisible(false); setTransactionDetails(null); setTxError(null); @@ -171,7 +170,7 @@ export const SendTxEmbed = () => { setIsTxApprovalVisible(true); } catch (error: unknown) { - console.error("Error handling SIGN_ONBOARD_TX_REQUEST:", error); + console.error(`Error handling ${REQUEST_SIGN_TX}:`, error); const errorMsg = error instanceof Error ? error.message : String(error); sendMessage(source, id, { error: `Failed to prepare transaction: ${errorMsg}` }, origin); @@ -187,16 +186,16 @@ export const SendTxEmbed = () => { const messageData = event.data as IncomingMessageEventData; switch (messageData.type) { - case 'GET_ACCOUNTS_REQUEST': - handleGetAccountsRequest(event as MessageEvent); + case REQUEST_COSMOS_ACCOUNTS_DATA: + handleGetCosmosAccountsRequest(event as MessageEvent); break; - case 'SIGN_ONBOARD_TX_REQUEST': + case REQUEST_SIGN_TX: handleSignOnboardTxRequest(event as MessageEvent); break; default: console.warn(`Received unknown message type: ${messageData.type}`); } - }, [handleGetAccountsRequest, handleSignOnboardTxRequest]); + }, [handleGetCosmosAccountsRequest, handleSignOnboardTxRequest]); useEffect(() => { window.addEventListener('message', handleIncomingMessage); @@ -228,7 +227,7 @@ export const SendTxEmbed = () => { // Perform the actual signing const signResponse = await wallet.signDirect(signerAddress, signDoc); - sendMessage(source as Window, SIGN_ONBOARD_TX_RESPONSE, {id: requestId, data: signResponse}, origin); + sendMessage(source as Window, SIGN_TX_RESPONSE, {id: requestId, data: signResponse}, origin); console.log("Sent signDirect response:", requestId); setIsTxApprovalVisible(false); @@ -239,7 +238,7 @@ export const SendTxEmbed = () => { const errorMsg = error instanceof Error ? error.message : String(error); setTxError(errorMsg); - sendMessage(source as Window, SIGN_ONBOARD_TX_RESPONSE, {id: requestId, error: `Failed to sign transaction: ${errorMsg}` }, origin); + sendMessage(source as Window, SIGN_TX_RESPONSE, {id: requestId, error: `Failed to sign transaction: ${errorMsg}` }, origin); } finally { setIsTxLoading(false); } @@ -250,7 +249,7 @@ export const SendTxEmbed = () => { const { requestId, source, origin } = transactionDetails; console.log("Rejecting request:", requestId); - sendMessage(source as Window, SIGN_ONBOARD_TX_RESPONSE, {id: requestId, error: "User rejected the signature request." }, origin); + sendMessage(source as Window, SIGN_TX_RESPONSE, {id: requestId, error: "User rejected the signature request." }, origin); setIsTxApprovalVisible(false); setTransactionDetails(null); setTxError(null); diff --git a/src/screens/WalletEmbed.tsx b/src/screens/WalletEmbed.tsx index 301c421..8471140 100644 --- a/src/screens/WalletEmbed.tsx +++ b/src/screens/WalletEmbed.tsx @@ -26,6 +26,7 @@ import { MEMO } from '../screens/ApproveTransfer'; import { Account, NetworksDataState } from '../types'; import useGetOrCreateAccounts from '../hooks/useGetOrCreateAccounts'; import useAccountsData from '../hooks/useAccountsData'; +import { REQUEST_TX, REQUEST_WALLET_ACCOUNTS, TRANSACTION_RESPONSE, WALLET_ACCOUNTS_DATA } from '../utils/constants'; type TransactionDetails = { chainId: string; @@ -51,8 +52,7 @@ export const WalletEmbed = () => { useEffect(() => { const handleGetAccounts = async (event: MessageEvent) => { - // TODO: Keep event data types in constant file - if (event.data.type !== 'REQUEST_WALLET_ACCOUNTS') return; + if (event.data.type !== REQUEST_WALLET_ACCOUNTS) return; const accountsData = await getAccountsData(event.data.chainId); @@ -63,7 +63,7 @@ export const WalletEmbed = () => { sendMessage( event.source as Window, - 'WALLET_ACCOUNTS_DATA', + WALLET_ACCOUNTS_DATA, accountsData.map(account => account.address), event.origin ); @@ -82,7 +82,7 @@ export const WalletEmbed = () => { const handleTxRequested = useCallback( async (event: MessageEvent) => { try { - if (event.data.type !== 'REQUEST_TX') return; + if (event.data.type !== REQUEST_TX) return; txEventRef.current = event; @@ -208,7 +208,7 @@ export const WalletEmbed = () => { const event = txEventRef.current; if (event?.source) { - sendMessage(event.source as Window, 'TRANSACTION_RESPONSE', txResult.transactionHash, event.origin); + sendMessage(event.source as Window, TRANSACTION_RESPONSE, txResult.transactionHash, event.origin); } else { console.error('No event source available to send message'); } @@ -228,7 +228,7 @@ export const WalletEmbed = () => { setIsTxRequested(false); setTransactionDetails(null); if (event?.source) { - sendMessage(event.source as Window, 'TRANSACTION_RESPONSE', null, event.origin); + sendMessage(event.source as Window, TRANSACTION_RESPONSE, null, event.origin); } else { console.error('No event source available to send message'); } @@ -308,7 +308,7 @@ export const WalletEmbed = () => { hideDialog={() => { setTxError(null) if (window.parent) { - sendMessage(window.parent, 'TRANSACTION_RESPONSE', null, '*'); + sendMessage(window.parent, TRANSACTION_RESPONSE, null, '*'); sendMessage(window.parent, 'closeIframe', null, '*'); } }} diff --git a/src/types.ts b/src/types.ts index 6d34515..5c5317c 100644 --- a/src/types.ts +++ b/src/types.ts @@ -41,7 +41,7 @@ export type StackParamsList = { "wallet-embed": undefined; "auto-sign-in": undefined; "sign-request-embed": undefined; - "send-tx-embed": undefined; + "sign-tx-embed": undefined; }; export type Account = { diff --git a/src/utils/constants.ts b/src/utils/constants.ts index 5549b7c..404dd53 100644 --- a/src/utils/constants.ts +++ b/src/utils/constants.ts @@ -74,3 +74,19 @@ export const INVALID_URL_ERROR = 'Invalid URL'; export const IS_NUMBER_REGEX = /^\d+$/; export const IS_IMPORT_WALLET_ENABLED = false; + +// iframe request types +export const REQUEST_COSMOS_ACCOUNTS_DATA = 'REQUEST_COSMOS_ACCOUNTS_DATA'; +export const REQUEST_SIGN_TX = 'REQUEST_SIGN_TX'; +export const SIGN_MESSAGE = 'SIGN_MESSAGE'; +export const REQUEST_WALLET_ACCOUNTS = 'REQUEST_WALLET_ACCOUNTS'; +export const REQUEST_TX = 'REQUEST_TX'; +export const AUTO_SIGN_IN = 'AUTO_SIGN_IN'; + +// iframe response types +export const COSMOS_ACCOUNTS_RESPONSE = 'COSMOS_ACCOUNTS_RESPONSE'; +export const SIGN_TX_RESPONSE = 'SIGN_TX_RESPONSE'; +export const SIGNED_MESSAGE = 'SIGNED_MESSAGE'; +export const WALLET_ACCOUNTS_DATA = 'WALLET_ACCOUNTS_DATA'; +export const TRANSACTION_RESPONSE = 'TRANSACTION_RESPONSE'; +export const SIGN_IN_RESPONSE = 'SIGN_IN_RESPONSE'; -- 2.45.2 From 9264a39e0406352c139360030d3ca6a804b14fc2 Mon Sep 17 00:00:00 2001 From: Shreerang Kale Date: Mon, 5 May 2025 09:20:57 +0530 Subject: [PATCH 10/19] Update iframe request and response types --- src/hooks/useExportPrivateKeyEmbed.ts | 5 +++-- src/hooks/useGetOrCreateAccounts.ts | 4 ++-- src/screens/SignTxEmbed.tsx | 1 - src/utils/constants.ts | 3 +++ 4 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/hooks/useExportPrivateKeyEmbed.ts b/src/hooks/useExportPrivateKeyEmbed.ts index 40b5684..f791ad3 100644 --- a/src/hooks/useExportPrivateKeyEmbed.ts +++ b/src/hooks/useExportPrivateKeyEmbed.ts @@ -2,6 +2,7 @@ import { useEffect } from 'react'; import { useAccounts } from '../context/AccountsContext'; import { getPathKey, sendMessage } from '../utils/misc'; +import { ACCOUNT_PK_DATA, REQUEST_ACCOUNT_PK } from '../utils/constants'; const useExportPKEmbed = () => { const { accounts } = useAccounts(); @@ -10,7 +11,7 @@ const useExportPKEmbed = () => { const handleMessage = async (event: MessageEvent) => { const { type, chainId, address } = event.data; - if (type !== 'REQUEST_ACCOUNT_PK') return; + if (type !== REQUEST_ACCOUNT_PK) return; try { const selectedAccount = accounts.find(account => account.address === address); @@ -23,7 +24,7 @@ const useExportPKEmbed = () => { sendMessage( event.source as Window, - 'ACCOUNT_PK_DATA', + ACCOUNT_PK_DATA, { privateKey }, event.origin, ); diff --git a/src/hooks/useGetOrCreateAccounts.ts b/src/hooks/useGetOrCreateAccounts.ts index ce11947..a2418e8 100644 --- a/src/hooks/useGetOrCreateAccounts.ts +++ b/src/hooks/useGetOrCreateAccounts.ts @@ -5,7 +5,7 @@ import { sendMessage } from "../utils/misc"; import useAccountsData from "./useAccountsData"; import { useNetworks } from "../context/NetworksContext"; import { useAccounts } from "../context/AccountsContext"; -import { WALLET_ACCOUNTS_DATA } from "../utils/constants"; +import { REQUEST_CREATE_OR_GET_ACCOUNTS, WALLET_ACCOUNTS_DATA } from "../utils/constants"; const REACT_APP_ALLOWED_URLS = process.env.REACT_APP_ALLOWED_URLS; @@ -32,7 +32,7 @@ const useGetOrCreateAccounts = () => { useEffect(() => { const handleCreateAccounts = async (event: MessageEvent) => { - if (event.data.type !== 'REQUEST_CREATE_OR_GET_ACCOUNTS') return; + if (event.data.type !== REQUEST_CREATE_OR_GET_ACCOUNTS) return; if (!REACT_APP_ALLOWED_URLS) { console.log('Allowed URLs are not set'); diff --git a/src/screens/SignTxEmbed.tsx b/src/screens/SignTxEmbed.tsx index 3d7153d..d4eb4bc 100644 --- a/src/screens/SignTxEmbed.tsx +++ b/src/screens/SignTxEmbed.tsx @@ -22,7 +22,6 @@ import { Account, NetworksDataState } from '../types'; import { REQUEST_SIGN_TX, REQUEST_COSMOS_ACCOUNTS_DATA, COSMOS_ACCOUNTS_RESPONSE, SIGN_TX_RESPONSE } from '../utils/constants'; // Type Definitions - interface GetAccountsRequestData { chainId: string, } diff --git a/src/utils/constants.ts b/src/utils/constants.ts index 404dd53..cdbfb5e 100644 --- a/src/utils/constants.ts +++ b/src/utils/constants.ts @@ -80,8 +80,10 @@ export const REQUEST_COSMOS_ACCOUNTS_DATA = 'REQUEST_COSMOS_ACCOUNTS_DATA'; export const REQUEST_SIGN_TX = 'REQUEST_SIGN_TX'; export const SIGN_MESSAGE = 'SIGN_MESSAGE'; export const REQUEST_WALLET_ACCOUNTS = 'REQUEST_WALLET_ACCOUNTS'; +export const REQUEST_CREATE_OR_GET_ACCOUNTS = 'REQUEST_CREATE_OR_GET_ACCOUNTS'; export const REQUEST_TX = 'REQUEST_TX'; export const AUTO_SIGN_IN = 'AUTO_SIGN_IN'; +export const REQUEST_ACCOUNT_PK = 'REQUEST_ACCOUNT_PK'; // iframe response types export const COSMOS_ACCOUNTS_RESPONSE = 'COSMOS_ACCOUNTS_RESPONSE'; @@ -90,3 +92,4 @@ export const SIGNED_MESSAGE = 'SIGNED_MESSAGE'; export const WALLET_ACCOUNTS_DATA = 'WALLET_ACCOUNTS_DATA'; export const TRANSACTION_RESPONSE = 'TRANSACTION_RESPONSE'; export const SIGN_IN_RESPONSE = 'SIGN_IN_RESPONSE'; +export const ACCOUNT_PK_DATA = 'ACCOUNT_PK_DATA'; -- 2.45.2 From b90c18b79491655a04f33fa1a7fa8b139a4ff862 Mon Sep 17 00:00:00 2001 From: Shreerang Kale Date: Mon, 5 May 2025 10:48:48 +0530 Subject: [PATCH 11/19] Remove checking source type --- src/screens/SignTxEmbed.tsx | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/screens/SignTxEmbed.tsx b/src/screens/SignTxEmbed.tsx index d4eb4bc..dcd7c07 100644 --- a/src/screens/SignTxEmbed.tsx +++ b/src/screens/SignTxEmbed.tsx @@ -107,12 +107,8 @@ export const SignTxEmbed = () => { } catch (error: unknown) { console.error(`Error handling ${REQUEST_COSMOS_ACCOUNTS_DATA}:`, error); const errorMsg = error instanceof Error ? error.message : String(error); - // Check if source is a Window before sending message - if (source instanceof Window) { - sendMessage(source, COSMOS_ACCOUNTS_RESPONSE, { id, error: `Failed to get accounts: ${errorMsg}` }, origin); - } else { - console.error("Cannot send error message: source is not a Window"); - } + + sendMessage(source, COSMOS_ACCOUNTS_RESPONSE, { id, error: `Failed to get accounts: ${errorMsg}` }, origin); } }, [networksData]); -- 2.45.2 From e11eb3a1eecdc4a4004a2c9b49f8c65a9bfef1bd Mon Sep 17 00:00:00 2001 From: Shreerang Kale Date: Mon, 5 May 2025 11:20:07 +0530 Subject: [PATCH 12/19] Refactor method to get cosmos accounts --- src/screens/AddNetwork.tsx | 4 ++-- src/screens/SignRequestEmbed.tsx | 4 ++-- src/screens/SignTxEmbed.tsx | 20 +++++++++---------- src/utils/accounts.ts | 20 ++++--------------- src/utils/sign-message.ts | 4 ++-- .../wallet-connect/wallet-connect-requests.ts | 4 ++-- 6 files changed, 22 insertions(+), 34 deletions(-) diff --git a/src/screens/AddNetwork.tsx b/src/screens/AddNetwork.tsx index ee0cd68..d57597c 100644 --- a/src/screens/AddNetwork.tsx +++ b/src/screens/AddNetwork.tsx @@ -23,7 +23,7 @@ import { INVALID_URL_ERROR, IS_NUMBER_REGEX, } from "../utils/constants"; -import { getCosmosAccounts } from "../utils/accounts"; +import { getCosmosAccountByHDPath } from "../utils/accounts"; import ETH_CHAINS from "../assets/ethereum-chains.json"; import { getInternetCredentials, @@ -163,7 +163,7 @@ const AddNetwork = () => { case COSMOS: address = ( - await getCosmosAccounts( + await getCosmosAccountByHDPath( mnemonic, hdPath, (newNetworkData as z.infer) diff --git a/src/screens/SignRequestEmbed.tsx b/src/screens/SignRequestEmbed.tsx index 3cafa44..a364a2e 100644 --- a/src/screens/SignRequestEmbed.tsx +++ b/src/screens/SignRequestEmbed.tsx @@ -12,7 +12,7 @@ import { getHeaderTitle } from '@react-navigation/elements'; import { Account, StackParamsList } from '../types'; import AccountDetails from '../components/AccountDetails'; import styles from '../styles/stylesheet'; -import { getCosmosAccounts, retrieveSingleAccount } from '../utils/accounts'; +import { getCosmosAccountByHDPath, retrieveSingleAccount } from '../utils/accounts'; import { getMnemonic, getPathKey, sendMessage } from '../utils/misc'; import { COSMOS, SIGN_MESSAGE, SIGNED_MESSAGE } from '../utils/constants'; import { useNetworks } from '../context/NetworksContext'; @@ -50,7 +50,7 @@ const SignRequestEmbed = ({ route }: SignRequestProps) => { throw new Error("Requested network not found") } - const cosmosAccount = await getCosmosAccounts(mnemonic, path, requestedNetworkData?.addressPrefix); + const cosmosAccount = await getCosmosAccountByHDPath(mnemonic, path, requestedNetworkData?.addressPrefix); const cosmosAminoSignature = await cosmosAccount.cosmosWallet.signAmino( signerAddress, diff --git a/src/screens/SignTxEmbed.tsx b/src/screens/SignTxEmbed.tsx index dcd7c07..a4f2a84 100644 --- a/src/screens/SignTxEmbed.tsx +++ b/src/screens/SignTxEmbed.tsx @@ -11,11 +11,11 @@ import { DirectSecp256k1Wallet, Algo, TxBodyEncodeObject, decodeOptionalPubkey } import { SigningStargateClient } from '@cosmjs/stargate'; import { toHex } from '@cosmjs/encoding'; -import { getCosmosAccount, retrieveAccounts, retrieveSingleAccount } from '../utils/accounts'; +import { getCosmosAccountByHDPath, retrieveAccounts, retrieveSingleAccount } from '../utils/accounts'; import AccountDetails from '../components/AccountDetails'; import styles from '../styles/stylesheet'; import DataBox from '../components/DataBox'; -import { getPathKey, sendMessage } from '../utils/misc'; +import { getMnemonic, getPathKey, sendMessage } from '../utils/misc'; import { useNetworks } from '../context/NetworksContext'; import TxErrorDialog from '../components/TxErrorDialog'; import { Account, NetworksDataState } from '../types'; @@ -26,13 +26,13 @@ interface GetAccountsRequestData { chainId: string, } -interface SignOnboardTxRequestData { +interface SignTxRequestData { address: string; signDoc: SignDoc; txBody: TxBodyEncodeObject; } -type IncomingMessageData = SignOnboardTxRequestData | GetAccountsRequestData; +type IncomingMessageData = SignTxRequestData | GetAccountsRequestData; interface IncomingMessageEventData { id: string; @@ -76,6 +76,7 @@ export const SignTxEmbed = () => { const source = event.source as Window; const origin = event.origin; const requestData = data as GetAccountsRequestData; + const mnemonic = await getMnemonic(); console.log(`Received ${REQUEST_COSMOS_ACCOUNTS_DATA}`, id); try { @@ -91,10 +92,9 @@ export const SignTxEmbed = () => { throw new Error("Accounts not found for network") } - // TODO: Refactor getCosmosAccounts functions to return all accounts const responseAccounts = await Promise.all( allAccounts.map(async (acc) => { - const cosmosAccount = await getCosmosAccount(acc.hdPath, requestedNetworkData.addressPrefix!); + const cosmosAccount = (await getCosmosAccountByHDPath(mnemonic, acc.hdPath, requestedNetworkData.addressPrefix)).data; return { ...cosmosAccount, pubkey: toHex(cosmosAccount.pubkey), @@ -112,11 +112,11 @@ export const SignTxEmbed = () => { } }, [networksData]); - const handleSignOnboardTxRequest = useCallback(async (event: MessageEvent) => { + const handleSignTxRequest = useCallback(async (event: MessageEvent) => { const { id, data } = event.data; const source = event.source as Window; const origin = event.origin; - const requestData = data as SignOnboardTxRequestData; + const requestData = data as SignTxRequestData; console.log(`Received ${REQUEST_SIGN_TX}`, id); setIsTxApprovalVisible(false); @@ -185,12 +185,12 @@ export const SignTxEmbed = () => { handleGetCosmosAccountsRequest(event as MessageEvent); break; case REQUEST_SIGN_TX: - handleSignOnboardTxRequest(event as MessageEvent); + handleSignTxRequest(event as MessageEvent); break; default: console.warn(`Received unknown message type: ${messageData.type}`); } - }, [handleGetCosmosAccountsRequest, handleSignOnboardTxRequest]); + }, [handleGetCosmosAccountsRequest, handleSignTxRequest]); useEffect(() => { window.addEventListener('message', handleIncomingMessage); diff --git a/src/utils/accounts.ts b/src/utils/accounts.ts index cc891dd..3b010d5 100644 --- a/src/utils/accounts.ts +++ b/src/utils/accounts.ts @@ -56,7 +56,7 @@ const createWalletFromMnemonic = async ( case COSMOS: address = ( - await getCosmosAccounts(mnemonic, hdPath, network.addressPrefix) + await getCosmosAccountByHDPath(mnemonic, hdPath, network.addressPrefix) ).data.address; break; @@ -281,7 +281,7 @@ const accountInfoFromHDPath = async ( break; case COSMOS: address = ( - await getCosmosAccounts(mnemonic, hdPath, networkData.addressPrefix) + await getCosmosAccountByHDPath(mnemonic, hdPath, networkData.addressPrefix) ).data.address; break; default: @@ -290,17 +290,6 @@ const accountInfoFromHDPath = async ( return { privKey, pubKey, address }; }; -const getCosmosAccount = async (hdPath: string, addressPrefix: string) => { - const mnemonicStore = getInternetCredentials('mnemonicServer'); - if (!mnemonicStore) { - throw new Error('Mnemonic not found!'); - } - - const mnemonic = mnemonicStore; - - return (await getCosmosAccounts(mnemonic, hdPath, addressPrefix)).data -} - const getNextAccountId = async (namespaceChainId: string): Promise => { const idStore = await getInternetCredentials( `addAccountCounter/${namespaceChainId}`, @@ -334,7 +323,7 @@ const updateAccountCounter = async ( ); }; -const getCosmosAccounts = async ( +const getCosmosAccountByHDPath = async ( mnemonic: string, path: string, prefix: string = COSMOS, @@ -362,6 +351,5 @@ export { accountInfoFromHDPath, getNextAccountId, updateAccountCounter, - getCosmosAccounts, - getCosmosAccount + getCosmosAccountByHDPath, }; diff --git a/src/utils/sign-message.ts b/src/utils/sign-message.ts index bb3904c..ef5ec06 100644 --- a/src/utils/sign-message.ts +++ b/src/utils/sign-message.ts @@ -10,7 +10,7 @@ import { SignDoc } from 'cosmjs-types/cosmos/tx/v1beta1/tx'; import { SignMessageParams } from '../types'; import { getDirectWallet, getMnemonic, getPathKey } from './misc'; -import { getCosmosAccounts } from './accounts'; +import { getCosmosAccountByHDPath } from './accounts'; import { COSMOS, EIP155 } from './constants'; const signMessage = async ({ @@ -58,7 +58,7 @@ const signCosmosMessage = async ( const mnemonic = await getMnemonic(); const addressPrefix = fromBech32(cosmosAddress).prefix - const cosmosAccount = await getCosmosAccounts(mnemonic, path, addressPrefix); + const cosmosAccount = await getCosmosAccountByHDPath(mnemonic, path, addressPrefix); const address = cosmosAccount.data.address; const cosmosSignature = await cosmosAccount.cosmosWallet.signAmino( address, diff --git a/src/utils/wallet-connect/wallet-connect-requests.ts b/src/utils/wallet-connect/wallet-connect-requests.ts index db3d8f4..d00fef2 100644 --- a/src/utils/wallet-connect/wallet-connect-requests.ts +++ b/src/utils/wallet-connect/wallet-connect-requests.ts @@ -18,7 +18,7 @@ import { EIP155_SIGNING_METHODS } from './EIP155Data'; import { signDirectMessage, signEthMessage } from '../sign-message'; import { Account } from '../../types'; import { getMnemonic, getPathKey } from '../misc'; -import { getCosmosAccounts } from '../accounts'; +import { getCosmosAccountByHDPath } from '../accounts'; import { COSMOS_METHODS } from './COSMOSData'; import { COSMOS } from '../constants'; @@ -88,7 +88,7 @@ export async function approveWalletConnectRequest( addressPrefix = fromBech32(account.address).prefix } - const cosmosAccount = await getCosmosAccounts(mnemonic, path, addressPrefix); + const cosmosAccount = await getCosmosAccountByHDPath(mnemonic, path, addressPrefix); const address = account.address; switch (request.method) { -- 2.45.2 From c2557eb3eb314465f325a47f38f8288d87acee6b Mon Sep 17 00:00:00 2001 From: Shreerang Kale Date: Mon, 5 May 2025 17:19:35 +0530 Subject: [PATCH 13/19] Update iframe request and response types --- src/hooks/useAddAccountEmbed.ts | 5 +++-- src/hooks/useExportPrivateKeyEmbed.ts | 4 ++-- src/screens/SignRequestEmbed.tsx | 10 +++++----- src/utils/constants.ts | 10 ++++++---- 4 files changed, 16 insertions(+), 13 deletions(-) diff --git a/src/hooks/useAddAccountEmbed.ts b/src/hooks/useAddAccountEmbed.ts index d226484..2514b1d 100644 --- a/src/hooks/useAddAccountEmbed.ts +++ b/src/hooks/useAddAccountEmbed.ts @@ -6,6 +6,7 @@ import useAccountsData from '../hooks/useAccountsData'; import { addAccount } from '../utils/accounts'; import { useAccounts } from '../context/AccountsContext'; import { Account, NetworksDataState } from '../types'; +import { ADD_ACCOUNT_RESPONSE, REQUEST_ADD_ACCOUNT } from '../utils/constants'; const REACT_APP_ALLOWED_URLS = process.env.REACT_APP_ALLOWED_URLS; @@ -24,7 +25,7 @@ const useAddAccountEmbed = () => { useEffect(() => { const handleAddAccount = async (event: MessageEvent) => { - if (event.data.type !== 'ADD_ACCOUNT') return; + if (event.data.type !== REQUEST_ADD_ACCOUNT) return; if (!REACT_APP_ALLOWED_URLS) { console.log('Unauthorized app origin:', event.origin); @@ -44,7 +45,7 @@ const useAddAccountEmbed = () => { const updatedAccounts = await getAccountsData(event.data.chainId); const addresses = updatedAccounts.map((account: Account) => account.address); - sendMessage(event.source as Window, 'ADD_ACCOUNT_RESPONSE', addresses, event.origin); + sendMessage(event.source as Window, ADD_ACCOUNT_RESPONSE, addresses, event.origin); }; window.addEventListener('message', handleAddAccount); diff --git a/src/hooks/useExportPrivateKeyEmbed.ts b/src/hooks/useExportPrivateKeyEmbed.ts index f791ad3..ec0e1cb 100644 --- a/src/hooks/useExportPrivateKeyEmbed.ts +++ b/src/hooks/useExportPrivateKeyEmbed.ts @@ -2,7 +2,7 @@ import { useEffect } from 'react'; import { useAccounts } from '../context/AccountsContext'; import { getPathKey, sendMessage } from '../utils/misc'; -import { ACCOUNT_PK_DATA, REQUEST_ACCOUNT_PK } from '../utils/constants'; +import { ACCOUNT_PK_RESPONSE, REQUEST_ACCOUNT_PK } from '../utils/constants'; const useExportPKEmbed = () => { const { accounts } = useAccounts(); @@ -24,7 +24,7 @@ const useExportPKEmbed = () => { sendMessage( event.source as Window, - ACCOUNT_PK_DATA, + ACCOUNT_PK_RESPONSE, { privateKey }, event.origin, ); diff --git a/src/screens/SignRequestEmbed.tsx b/src/screens/SignRequestEmbed.tsx index a364a2e..b5ec810 100644 --- a/src/screens/SignRequestEmbed.tsx +++ b/src/screens/SignRequestEmbed.tsx @@ -14,7 +14,7 @@ import AccountDetails from '../components/AccountDetails'; import styles from '../styles/stylesheet'; import { getCosmosAccountByHDPath, retrieveSingleAccount } from '../utils/accounts'; import { getMnemonic, getPathKey, sendMessage } from '../utils/misc'; -import { COSMOS, SIGN_MESSAGE, SIGNED_MESSAGE } from '../utils/constants'; +import { COSMOS, REQUEST_SIGN_MESSAGE, SIGN_MESSAGE_RESPONSE } from '../utils/constants'; import { useNetworks } from '../context/NetworksContext'; const REACT_APP_ALLOWED_URLS = process.env.REACT_APP_ALLOWED_URLS; @@ -61,7 +61,7 @@ const SignRequestEmbed = ({ route }: SignRequestProps) => { sendMessage( sourceWindow, - SIGNED_MESSAGE, + SIGN_MESSAGE_RESPONSE, { signature }, origin, ); @@ -71,7 +71,7 @@ const SignRequestEmbed = ({ route }: SignRequestProps) => { console.error('Signing failed:', err); sendMessage( sourceWindow!, - SIGNED_MESSAGE, + SIGN_MESSAGE_RESPONSE, { error: err }, origin, ); @@ -84,7 +84,7 @@ const SignRequestEmbed = ({ route }: SignRequestProps) => { if (sourceWindow && origin) { sendMessage( sourceWindow, - SIGNED_MESSAGE, + SIGN_MESSAGE_RESPONSE, { error: 'User rejected the request' }, origin, ); @@ -93,7 +93,7 @@ const SignRequestEmbed = ({ route }: SignRequestProps) => { useEffect(() => { const handleCosmosSignMessage = async (event: MessageEvent) => { - if (event.data.type !== SIGN_MESSAGE) return; + if (event.data.type !== REQUEST_SIGN_MESSAGE) return; if (!REACT_APP_ALLOWED_URLS) { diff --git a/src/utils/constants.ts b/src/utils/constants.ts index cdbfb5e..1e1a1e8 100644 --- a/src/utils/constants.ts +++ b/src/utils/constants.ts @@ -78,18 +78,20 @@ export const IS_IMPORT_WALLET_ENABLED = false; // iframe request types export const REQUEST_COSMOS_ACCOUNTS_DATA = 'REQUEST_COSMOS_ACCOUNTS_DATA'; export const REQUEST_SIGN_TX = 'REQUEST_SIGN_TX'; -export const SIGN_MESSAGE = 'SIGN_MESSAGE'; +export const REQUEST_SIGN_MESSAGE = 'REQUEST_SIGN_MESSAGE'; export const REQUEST_WALLET_ACCOUNTS = 'REQUEST_WALLET_ACCOUNTS'; export const REQUEST_CREATE_OR_GET_ACCOUNTS = 'REQUEST_CREATE_OR_GET_ACCOUNTS'; export const REQUEST_TX = 'REQUEST_TX'; export const AUTO_SIGN_IN = 'AUTO_SIGN_IN'; export const REQUEST_ACCOUNT_PK = 'REQUEST_ACCOUNT_PK'; +export const REQUEST_ADD_ACCOUNT = 'ADD_ACCOUNT'; // iframe response types export const COSMOS_ACCOUNTS_RESPONSE = 'COSMOS_ACCOUNTS_RESPONSE'; export const SIGN_TX_RESPONSE = 'SIGN_TX_RESPONSE'; -export const SIGNED_MESSAGE = 'SIGNED_MESSAGE'; -export const WALLET_ACCOUNTS_DATA = 'WALLET_ACCOUNTS_DATA'; +export const SIGN_MESSAGE_RESPONSE = 'SIGN_MESSAGE_RESPONSE'; export const TRANSACTION_RESPONSE = 'TRANSACTION_RESPONSE'; export const SIGN_IN_RESPONSE = 'SIGN_IN_RESPONSE'; -export const ACCOUNT_PK_DATA = 'ACCOUNT_PK_DATA'; +export const ACCOUNT_PK_RESPONSE = 'ACCOUNT_PK_RESPONSE'; +export const ADD_ACCOUNT_RESPONSE = 'ADD_ACCOUNT_RESPONSE'; +export const WALLET_ACCOUNTS_DATA = 'WALLET_ACCOUNTS_DATA'; -- 2.45.2 From 610939cea5f208d547d081a84aed8b5633c49964 Mon Sep 17 00:00:00 2001 From: Shreerang Kale Date: Mon, 5 May 2025 18:53:38 +0530 Subject: [PATCH 14/19] Remove usage of request ID --- src/screens/SignTxEmbed.tsx | 32 +++++++++++++++----------------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/src/screens/SignTxEmbed.tsx b/src/screens/SignTxEmbed.tsx index a4f2a84..110648c 100644 --- a/src/screens/SignTxEmbed.tsx +++ b/src/screens/SignTxEmbed.tsx @@ -41,7 +41,6 @@ interface IncomingMessageEventData { } type TransactionDetails = { - requestId: string; source: MessageEventSource; origin: string; signerAddress: string; @@ -72,13 +71,13 @@ export const SignTxEmbed = () => { // Message Handlers const handleGetCosmosAccountsRequest = useCallback(async (event: MessageEvent) => { - const { id, data } = event.data; + const { data } = event.data; const source = event.source as Window; const origin = event.origin; const requestData = data as GetAccountsRequestData; const mnemonic = await getMnemonic(); - console.log(`Received ${REQUEST_COSMOS_ACCOUNTS_DATA}`, id); + console.log(`Received ${REQUEST_COSMOS_ACCOUNTS_DATA}`); try { const requestedNetworkData = networksData.find(networkData => networkData.chainId === requestData.chainId) @@ -103,22 +102,22 @@ export const SignTxEmbed = () => { ); const response: GetAccountsResponse = { accounts: responseAccounts }; - sendMessage(source, COSMOS_ACCOUNTS_RESPONSE, {id, data: response}, origin); + sendMessage(source, COSMOS_ACCOUNTS_RESPONSE, {data: response}, origin); } catch (error: unknown) { console.error(`Error handling ${REQUEST_COSMOS_ACCOUNTS_DATA}:`, error); const errorMsg = error instanceof Error ? error.message : String(error); - sendMessage(source, COSMOS_ACCOUNTS_RESPONSE, { id, error: `Failed to get accounts: ${errorMsg}` }, origin); + sendMessage(source, COSMOS_ACCOUNTS_RESPONSE, { error: `Failed to get accounts: ${errorMsg}` }, origin); } }, [networksData]); const handleSignTxRequest = useCallback(async (event: MessageEvent) => { - const { id, data } = event.data; + const { data } = event.data; const source = event.source as Window; const origin = event.origin; const requestData = data as SignTxRequestData; - console.log(`Received ${REQUEST_SIGN_TX}`, id); + console.log(`Received ${REQUEST_SIGN_TX}`); setIsTxApprovalVisible(false); setTransactionDetails(null); setTxError(null); @@ -150,7 +149,6 @@ export const SignTxEmbed = () => { } setTransactionDetails({ - requestId: id, source: source, origin: origin, signerAddress, @@ -168,13 +166,13 @@ export const SignTxEmbed = () => { console.error(`Error handling ${REQUEST_SIGN_TX}:`, error); const errorMsg = error instanceof Error ? error.message : String(error); - sendMessage(source, id, { error: `Failed to prepare transaction: ${errorMsg}` }, origin); + sendMessage(source, SIGN_TX_RESPONSE, { error: `Failed to prepare transaction: ${errorMsg}` }, origin); setTxError(errorMsg); } }, [networksData]); const handleIncomingMessage = useCallback((event: MessageEvent) => { - if (!event.data || typeof event.data !== 'object' || !event.data.type || !event.data.id || !event.source || event.source === window) { + if (!event.data || typeof event.data !== 'object' || !event.data.type || !event.source || event.source === window) { return; // Basic validation } @@ -212,7 +210,7 @@ export const SignTxEmbed = () => { setIsTxLoading(true); setTxError(null); - const { requestId, source, origin, requestedNetwork, chainId, account, signerAddress, signDoc } = transactionDetails; + const { source, origin, requestedNetwork, chainId, account, signerAddress, signDoc } = transactionDetails; try { const { privKey } = await getPathKey(`${requestedNetwork.namespace}:${chainId}`, account.index); @@ -222,8 +220,8 @@ export const SignTxEmbed = () => { // Perform the actual signing const signResponse = await wallet.signDirect(signerAddress, signDoc); - sendMessage(source as Window, SIGN_TX_RESPONSE, {id: requestId, data: signResponse}, origin); - console.log("Sent signDirect response:", requestId); + sendMessage(source as Window, SIGN_TX_RESPONSE, {data: signResponse}, origin); + console.log("Sent signDirect response"); setIsTxApprovalVisible(false); setTransactionDetails(null); @@ -233,7 +231,7 @@ export const SignTxEmbed = () => { const errorMsg = error instanceof Error ? error.message : String(error); setTxError(errorMsg); - sendMessage(source as Window, SIGN_TX_RESPONSE, {id: requestId, error: `Failed to sign transaction: ${errorMsg}` }, origin); + sendMessage(source as Window, SIGN_TX_RESPONSE, { error: `Failed to sign transaction: ${errorMsg}` }, origin); } finally { setIsTxLoading(false); } @@ -241,10 +239,10 @@ export const SignTxEmbed = () => { const rejectRequestHandler = () => { if (!transactionDetails) return; - const { requestId, source, origin } = transactionDetails; - console.log("Rejecting request:", requestId); + const { source, origin } = transactionDetails; + console.log("Rejecting request"); - sendMessage(source as Window, SIGN_TX_RESPONSE, {id: requestId, error: "User rejected the signature request." }, origin); + sendMessage(source as Window, SIGN_TX_RESPONSE, { error: "User rejected the signature request." }, origin); setIsTxApprovalVisible(false); setTransactionDetails(null); setTxError(null); -- 2.45.2 From 9df58b9511c81bb5e6829fa9e9601542fed25cce Mon Sep 17 00:00:00 2001 From: Shreerang Kale Date: Tue, 6 May 2025 10:40:04 +0530 Subject: [PATCH 15/19] Throw error if balance is zero --- src/App.tsx | 4 +-- src/screens/SignRequestEmbed.tsx | 2 +- src/screens/SignTxEmbed.tsx | 54 ++++++++++++++++++-------------- src/screens/WalletEmbed.tsx | 1 - src/types.ts | 4 +-- src/utils/constants.ts | 2 +- 6 files changed, 36 insertions(+), 31 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index 80272a4..55a00b7 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -390,7 +390,7 @@ const App = (): React.JSX.Element => { }} /> <>, @@ -404,7 +404,7 @@ const App = (): React.JSX.Element => { }} />
, diff --git a/src/screens/SignRequestEmbed.tsx b/src/screens/SignRequestEmbed.tsx index b5ec810..0d7fc00 100644 --- a/src/screens/SignRequestEmbed.tsx +++ b/src/screens/SignRequestEmbed.tsx @@ -19,7 +19,7 @@ import { useNetworks } from '../context/NetworksContext'; const REACT_APP_ALLOWED_URLS = process.env.REACT_APP_ALLOWED_URLS; -type SignRequestProps = NativeStackScreenProps; +type SignRequestProps = NativeStackScreenProps; const SignRequestEmbed = ({ route }: SignRequestProps) => { const [displayAccount, setDisplayAccount] = useState(); diff --git a/src/screens/SignTxEmbed.tsx b/src/screens/SignTxEmbed.tsx index 110648c..60cfc15 100644 --- a/src/screens/SignTxEmbed.tsx +++ b/src/screens/SignTxEmbed.tsx @@ -19,7 +19,7 @@ import { getMnemonic, getPathKey, sendMessage } from '../utils/misc'; import { useNetworks } from '../context/NetworksContext'; import TxErrorDialog from '../components/TxErrorDialog'; import { Account, NetworksDataState } from '../types'; -import { REQUEST_SIGN_TX, REQUEST_COSMOS_ACCOUNTS_DATA, COSMOS_ACCOUNTS_RESPONSE, SIGN_TX_RESPONSE } from '../utils/constants'; +import { REQUEST_SIGN_TX, REQUEST_COSMOS_ACCOUNTS, COSMOS_ACCOUNTS_RESPONSE, SIGN_TX_RESPONSE } from '../utils/constants'; // Type Definitions interface GetAccountsRequestData { @@ -36,7 +36,7 @@ type IncomingMessageData = SignTxRequestData | GetAccountsRequestData; interface IncomingMessageEventData { id: string; - type: typeof REQUEST_SIGN_TX | typeof REQUEST_COSMOS_ACCOUNTS_DATA; + type: typeof REQUEST_SIGN_TX | typeof REQUEST_COSMOS_ACCOUNTS; data: IncomingMessageData; } @@ -60,6 +60,8 @@ interface GetAccountsResponse { }>; } +const REACT_APP_ALLOWED_URLS = process.env.REACT_APP_ALLOWED_URLS; + export const SignTxEmbed = () => { const [isTxApprovalVisible, setIsTxApprovalVisible] = useState(false); const [transactionDetails, setTransactionDetails] = useState(null); @@ -77,7 +79,6 @@ export const SignTxEmbed = () => { const requestData = data as GetAccountsRequestData; const mnemonic = await getMnemonic(); - console.log(`Received ${REQUEST_COSMOS_ACCOUNTS_DATA}`); try { const requestedNetworkData = networksData.find(networkData => networkData.chainId === requestData.chainId) @@ -104,7 +105,7 @@ export const SignTxEmbed = () => { const response: GetAccountsResponse = { accounts: responseAccounts }; sendMessage(source, COSMOS_ACCOUNTS_RESPONSE, {data: response}, origin); } catch (error: unknown) { - console.error(`Error handling ${REQUEST_COSMOS_ACCOUNTS_DATA}:`, error); + console.error(`Error handling ${REQUEST_COSMOS_ACCOUNTS}:`, error); const errorMsg = error instanceof Error ? error.message : String(error); sendMessage(source, COSMOS_ACCOUNTS_RESPONSE, { error: `Failed to get accounts: ${errorMsg}` }, origin); @@ -117,7 +118,6 @@ export const SignTxEmbed = () => { const origin = event.origin; const requestData = data as SignTxRequestData; - console.log(`Received ${REQUEST_SIGN_TX}`); setIsTxApprovalVisible(false); setTransactionDetails(null); setTxError(null); @@ -132,20 +132,18 @@ export const SignTxEmbed = () => { if (!account) throw new Error(`Account not found for address "${signerAddress}" on chain "${signDoc.chainId}".`); // Balance Check - let balanceAmount = 'N/A'; - try { - // Use a temporary read-only client for balance - const tempWallet = await DirectSecp256k1Wallet.fromKey( - new Uint8Array(Buffer.from((await getPathKey(`${network.namespace}:${network.chainId}`, account.index)).privKey.replace(/^0x/, ''), 'hex')), - network.addressPrefix - ); + // Use a temporary read-only client for balance + const tempWallet = await DirectSecp256k1Wallet.fromKey( + new Uint8Array(Buffer.from((await getPathKey(`${network.namespace}:${network.chainId}`, account.index)).privKey.replace(/^0x/, ''), 'hex')), + network.addressPrefix + ); - const client = await SigningStargateClient.connectWithSigner(network.rpcUrl!, tempWallet); - const balance = await client.getBalance(account.address, network.nativeDenom!); - balanceAmount = balance.amount; - client.disconnect(); - } catch (balanceError) { - console.warn("Could not retrieve balance:", balanceError); + const client = await SigningStargateClient.connectWithSigner(network.rpcUrl!, tempWallet); + const balance = await client.getBalance(account.address, network.nativeDenom!); + client.disconnect(); + + if (!balance || balance.amount === "0") { + throw new Error(`${account.address} does not have any balance`) } setTransactionDetails({ @@ -155,7 +153,7 @@ export const SignTxEmbed = () => { chainId: signDoc.chainId, account, requestedNetwork: network, - balance: balanceAmount, + balance: balance.amount, signDoc, txBody }); @@ -176,10 +174,22 @@ export const SignTxEmbed = () => { return; // Basic validation } + if (!REACT_APP_ALLOWED_URLS) { + console.log('Allowed URLs are not set'); + return; + } + + const allowedUrls = REACT_APP_ALLOWED_URLS.split(',').map(url => url.trim()); + + if (!allowedUrls.includes(event.origin)) { + console.log('Unauthorized app.'); + return; + } + const messageData = event.data as IncomingMessageEventData; switch (messageData.type) { - case REQUEST_COSMOS_ACCOUNTS_DATA: + case REQUEST_COSMOS_ACCOUNTS: handleGetCosmosAccountsRequest(event as MessageEvent); break; case REQUEST_SIGN_TX: @@ -192,10 +202,8 @@ export const SignTxEmbed = () => { useEffect(() => { window.addEventListener('message', handleIncomingMessage); - console.log("SendTxEmbed: Message listener added."); return () => { window.removeEventListener('message', handleIncomingMessage); - console.log("SendTxEmbed: Message listener removed."); }; }, [handleIncomingMessage]); @@ -221,7 +229,6 @@ export const SignTxEmbed = () => { const signResponse = await wallet.signDirect(signerAddress, signDoc); sendMessage(source as Window, SIGN_TX_RESPONSE, {data: signResponse}, origin); - console.log("Sent signDirect response"); setIsTxApprovalVisible(false); setTransactionDetails(null); @@ -240,7 +247,6 @@ export const SignTxEmbed = () => { const rejectRequestHandler = () => { if (!transactionDetails) return; const { source, origin } = transactionDetails; - console.log("Rejecting request"); sendMessage(source as Window, SIGN_TX_RESPONSE, { error: "User rejected the signature request." }, origin); setIsTxApprovalVisible(false); diff --git a/src/screens/WalletEmbed.tsx b/src/screens/WalletEmbed.tsx index 8471140..8646d54 100644 --- a/src/screens/WalletEmbed.tsx +++ b/src/screens/WalletEmbed.tsx @@ -140,7 +140,6 @@ export const WalletEmbed = () => { }); if (!checkSufficientFunds(amount, balance.amount)) { - console.log("Insufficient funds detected. Throwing error."); throw new Error('Insufficient funds'); } diff --git a/src/types.ts b/src/types.ts index 5c5317c..ae93310 100644 --- a/src/types.ts +++ b/src/types.ts @@ -40,8 +40,8 @@ export type StackParamsList = { }; "wallet-embed": undefined; "auto-sign-in": undefined; - "sign-request-embed": undefined; - "sign-tx-embed": undefined; + "sign-message-request-embed": undefined; + "sign-tx-request-embed": undefined; }; export type Account = { diff --git a/src/utils/constants.ts b/src/utils/constants.ts index 1e1a1e8..c845a1a 100644 --- a/src/utils/constants.ts +++ b/src/utils/constants.ts @@ -76,7 +76,7 @@ export const IS_NUMBER_REGEX = /^\d+$/; export const IS_IMPORT_WALLET_ENABLED = false; // iframe request types -export const REQUEST_COSMOS_ACCOUNTS_DATA = 'REQUEST_COSMOS_ACCOUNTS_DATA'; +export const REQUEST_COSMOS_ACCOUNTS = 'REQUEST_COSMOS_ACCOUNTS'; export const REQUEST_SIGN_TX = 'REQUEST_SIGN_TX'; export const REQUEST_SIGN_MESSAGE = 'REQUEST_SIGN_MESSAGE'; export const REQUEST_WALLET_ACCOUNTS = 'REQUEST_WALLET_ACCOUNTS'; -- 2.45.2 From 7229e6c97c1c8be915a0fc6f8f14415bf6b98e29 Mon Sep 17 00:00:00 2001 From: Shreerang Kale Date: Tue, 6 May 2025 11:02:06 +0530 Subject: [PATCH 16/19] Refactor getting private key --- src/screens/SignTxEmbed.tsx | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/screens/SignTxEmbed.tsx b/src/screens/SignTxEmbed.tsx index 60cfc15..e6bdfa5 100644 --- a/src/screens/SignTxEmbed.tsx +++ b/src/screens/SignTxEmbed.tsx @@ -133,8 +133,10 @@ export const SignTxEmbed = () => { // Balance Check // Use a temporary read-only client for balance + const { privKey } = await getPathKey(`${network.namespace}:${network.chainId}`, account.index) + const tempWallet = await DirectSecp256k1Wallet.fromKey( - new Uint8Array(Buffer.from((await getPathKey(`${network.namespace}:${network.chainId}`, account.index)).privKey.replace(/^0x/, ''), 'hex')), + new Uint8Array(Buffer.from(privKey.replace(/^0x/, ''), 'hex')), network.addressPrefix ); @@ -222,6 +224,7 @@ export const SignTxEmbed = () => { try { const { privKey } = await getPathKey(`${requestedNetwork.namespace}:${chainId}`, account.index); + const privateKeyBytes = Buffer.from(privKey.replace(/^0x/, ''), 'hex'); const wallet = await DirectSecp256k1Wallet.fromKey(new Uint8Array(privateKeyBytes), requestedNetwork.addressPrefix); // Wrap in Uint8Array @@ -370,7 +373,7 @@ export const SignTxEmbed = () => { disabled={isTxLoading} style={{ marginTop: 10 }} > - Reject + Reject -- 2.45.2 From a48ef30a4fd333d3f680d15c2678162c549561c8 Mon Sep 17 00:00:00 2001 From: Shreerang Kale Date: Tue, 6 May 2025 15:15:25 +0530 Subject: [PATCH 17/19] Update sign message request handler --- src/App.tsx | 6 +++--- src/screens/SignRequestEmbed.tsx | 25 ++++++++++++++++++++----- src/utils/constants.ts | 4 +++- 3 files changed, 26 insertions(+), 9 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index 55a00b7..fd8c034 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -30,7 +30,7 @@ import { getSignParamsMessage } from "./utils/wallet-connect/helpers"; import ApproveTransfer from "./screens/ApproveTransfer"; import AddNetwork from "./screens/AddNetwork"; import EditNetwork from "./screens/EditNetwork"; -import { COSMOS, EIP155 } from "./utils/constants"; +import { CHECK_BALANCE, COSMOS, EIP155, IS_SUFFICIENT } from "./utils/constants"; import { useNetworks } from "./context/NetworksContext"; import { NETWORK_METHODS } from "./utils/wallet-connect/common-data"; import { COSMOS_METHODS } from "./utils/wallet-connect/COSMOSData"; @@ -232,7 +232,7 @@ const App = (): React.JSX.Element => { useEffect(() => { const handleCheckBalance = async (event: MessageEvent) => { - if (event.data.type !== 'CHECK_BALANCE') return; + if (event.data.type !== CHECK_BALANCE) return; const { chainId, amount } = event.data; const network = networksData.find(net => net.chainId === chainId); @@ -272,7 +272,7 @@ const App = (): React.JSX.Element => { const areFundsSufficient = checkSufficientFunds(amount, balance.amount); - sendMessage(event.source as Window, 'IS_SUFFICIENT', areFundsSufficient, event.origin); + sendMessage(event.source as Window, IS_SUFFICIENT, areFundsSufficient, event.origin); }; window.addEventListener('message', handleCheckBalance); diff --git a/src/screens/SignRequestEmbed.tsx b/src/screens/SignRequestEmbed.tsx index 0d7fc00..7a789d6 100644 --- a/src/screens/SignRequestEmbed.tsx +++ b/src/screens/SignRequestEmbed.tsx @@ -25,6 +25,7 @@ const SignRequestEmbed = ({ route }: SignRequestProps) => { const [displayAccount, setDisplayAccount] = useState(); const [message, setMessage] = useState(''); const [chainId, setChainId] = useState(''); + const [namespace, setNamespace] = useState(''); const [signDoc, setSignDoc] = useState(null); const [signerAddress, setSignerAddress] = useState(''); const [origin, setOrigin] = useState(''); @@ -41,8 +42,13 @@ const SignRequestEmbed = ({ route }: SignRequestProps) => { setIsApproving(true); try { - const requestAccount = await retrieveSingleAccount(COSMOS, chainId, signerAddress); - const path = (await getPathKey(`${COSMOS}:${chainId}`, requestAccount!.index)).path; + if (namespace !== COSMOS) { + // TODO: Support ETH namespace + throw new Error(`namespace ${namespace} is not supported`) + } + + const requestAccount = await retrieveSingleAccount(namespace, chainId, signerAddress); + const path = (await getPathKey(`${namespace}:${chainId}`, requestAccount!.index)).path; const mnemonic = await getMnemonic(); const requestedNetworkData = networksData.find(networkData => networkData.chainId === chainId) @@ -111,16 +117,25 @@ const SignRequestEmbed = ({ route }: SignRequestProps) => { try { const { signerAddress, signDoc } = event.data.params; + const receivedNamespace = event.data.chainId.split(':')[0] + const receivedChainId = event.data.chainId.split(':')[1] + + if (receivedNamespace !== COSMOS) { + // TODO: Support ETH namespace + throw new Error(`namespace ${receivedNamespace} is not supported`) + } + setSignerAddress(signerAddress); setSignDoc(signDoc); setMessage(signDoc.memo || ''); setOrigin(event.origin); setSourceWindow(event.source as Window); - setChainId(event.data.chainId); + setNamespace(receivedNamespace); + setChainId(receivedChainId); const requestAccount = await retrieveSingleAccount( - COSMOS, - event.data.chainId, + receivedNamespace, + receivedChainId, signerAddress, ); diff --git a/src/utils/constants.ts b/src/utils/constants.ts index c845a1a..4c537fe 100644 --- a/src/utils/constants.ts +++ b/src/utils/constants.ts @@ -82,9 +82,10 @@ export const REQUEST_SIGN_MESSAGE = 'REQUEST_SIGN_MESSAGE'; export const REQUEST_WALLET_ACCOUNTS = 'REQUEST_WALLET_ACCOUNTS'; export const REQUEST_CREATE_OR_GET_ACCOUNTS = 'REQUEST_CREATE_OR_GET_ACCOUNTS'; export const REQUEST_TX = 'REQUEST_TX'; -export const AUTO_SIGN_IN = 'AUTO_SIGN_IN'; export const REQUEST_ACCOUNT_PK = 'REQUEST_ACCOUNT_PK'; export const REQUEST_ADD_ACCOUNT = 'ADD_ACCOUNT'; +export const AUTO_SIGN_IN = 'AUTO_SIGN_IN'; +export const CHECK_BALANCE = 'CHECK_BALANCE'; // iframe response types export const COSMOS_ACCOUNTS_RESPONSE = 'COSMOS_ACCOUNTS_RESPONSE'; @@ -95,3 +96,4 @@ export const SIGN_IN_RESPONSE = 'SIGN_IN_RESPONSE'; export const ACCOUNT_PK_RESPONSE = 'ACCOUNT_PK_RESPONSE'; export const ADD_ACCOUNT_RESPONSE = 'ADD_ACCOUNT_RESPONSE'; export const WALLET_ACCOUNTS_DATA = 'WALLET_ACCOUNTS_DATA'; +export const IS_SUFFICIENT = 'IS_SUFFICIENT'; -- 2.45.2 From 1d437c988e8334c3f8afd5045355d4343dda4687 Mon Sep 17 00:00:00 2001 From: Shreerang Kale Date: Tue, 6 May 2025 16:48:57 +0530 Subject: [PATCH 18/19] Update styles for sign tx embed component --- src/screens/SignTxEmbed.tsx | 22 +++++++++++----------- src/styles/stylesheet.js | 12 ------------ src/utils/constants.ts | 2 +- 3 files changed, 12 insertions(+), 24 deletions(-) diff --git a/src/screens/SignTxEmbed.tsx b/src/screens/SignTxEmbed.tsx index e6bdfa5..d3ad844 100644 --- a/src/screens/SignTxEmbed.tsx +++ b/src/screens/SignTxEmbed.tsx @@ -14,7 +14,6 @@ import { toHex } from '@cosmjs/encoding'; import { getCosmosAccountByHDPath, retrieveAccounts, retrieveSingleAccount } from '../utils/accounts'; import AccountDetails from '../components/AccountDetails'; import styles from '../styles/stylesheet'; -import DataBox from '../components/DataBox'; import { getMnemonic, getPathKey, sendMessage } from '../utils/misc'; import { useNetworks } from '../context/NetworksContext'; import TxErrorDialog from '../components/TxErrorDialog'; @@ -320,17 +319,18 @@ export const SignTxEmbed = () => { - + + {`Balance (${transactionDetails.requestedNetwork.nativeDenom})`} + + + {transactionDetails.balance} + + + Transaction Body - + {formattedTxBody} @@ -339,7 +339,7 @@ export const SignTxEmbed = () => { Auth Info - + {formattedAuthInfo} @@ -348,7 +348,7 @@ export const SignTxEmbed = () => { Transaction Data To Be Signed - + {formattedSignDoc} diff --git a/src/styles/stylesheet.js b/src/styles/stylesheet.js index cb2f368..30cca44 100644 --- a/src/styles/stylesheet.js +++ b/src/styles/stylesheet.js @@ -363,21 +363,9 @@ const styles = StyleSheet.create({ fontWeight: 'bold', marginBottom: 8, }, - codeContainer: { - backgroundColor: '#e0e0e0', - borderRadius: 6, - maxHeight: 200, - }, codeText: { fontFamily: 'monospace', fontSize: 12, - color: '#333', - }, - feeContainer: { - flexDirection: 'row', - justifyContent: 'space-between', - gap: 10, - marginBottom: 16, }, input: { flex: 1, diff --git a/src/utils/constants.ts b/src/utils/constants.ts index 4c537fe..959cb85 100644 --- a/src/utils/constants.ts +++ b/src/utils/constants.ts @@ -83,7 +83,7 @@ export const REQUEST_WALLET_ACCOUNTS = 'REQUEST_WALLET_ACCOUNTS'; export const REQUEST_CREATE_OR_GET_ACCOUNTS = 'REQUEST_CREATE_OR_GET_ACCOUNTS'; export const REQUEST_TX = 'REQUEST_TX'; export const REQUEST_ACCOUNT_PK = 'REQUEST_ACCOUNT_PK'; -export const REQUEST_ADD_ACCOUNT = 'ADD_ACCOUNT'; +export const REQUEST_ADD_ACCOUNT = 'REQUEST_ADD_ACCOUNT'; export const AUTO_SIGN_IN = 'AUTO_SIGN_IN'; export const CHECK_BALANCE = 'CHECK_BALANCE'; -- 2.45.2 From a88082f43e523993abeb9f36596214c8b193772f Mon Sep 17 00:00:00 2001 From: Shreerang Kale Date: Tue, 6 May 2025 18:21:01 +0530 Subject: [PATCH 19/19] Remove unnecessary styles --- src/screens/SignTxEmbed.tsx | 22 +++++++++++----------- src/styles/stylesheet.js | 11 ----------- 2 files changed, 11 insertions(+), 22 deletions(-) diff --git a/src/screens/SignTxEmbed.tsx b/src/screens/SignTxEmbed.tsx index d3ad844..472fe3c 100644 --- a/src/screens/SignTxEmbed.tsx +++ b/src/screens/SignTxEmbed.tsx @@ -312,24 +312,24 @@ export const SignTxEmbed = () => { {isTxApprovalVisible && transactionDetails ? ( <> - - Account + + Account - - {`Balance (${transactionDetails.requestedNetwork.nativeDenom})`} + + {`Balance (${transactionDetails.requestedNetwork.nativeDenom})`} - + {transactionDetails.balance} - - Transaction Body + + Transaction Body {formattedTxBody} @@ -337,8 +337,8 @@ export const SignTxEmbed = () => { - - Auth Info + + Auth Info {formattedAuthInfo} @@ -346,8 +346,8 @@ export const SignTxEmbed = () => { - - Transaction Data To Be Signed + + Transaction Data To Be Signed {formattedSignDoc} diff --git a/src/styles/stylesheet.js b/src/styles/stylesheet.js index 30cca44..60091a5 100644 --- a/src/styles/stylesheet.js +++ b/src/styles/stylesheet.js @@ -355,21 +355,10 @@ const styles = StyleSheet.create({ marginTop: 12, marginBottom: 20, }, - section: { - marginBottom: 16, - }, - sectionTitle: { - fontSize: 24, - fontWeight: 'bold', - marginBottom: 8, - }, codeText: { fontFamily: 'monospace', fontSize: 12, }, - input: { - flex: 1, - }, }); export default styles; -- 2.45.2