Refactor iframe request and response types

This commit is contained in:
Shreerang Kale 2025-05-02 12:15:23 +05:30
parent 2611950c10
commit d94980efe1
8 changed files with 68 additions and 44 deletions

View File

@ -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<StackParamsList>();
@ -390,8 +390,8 @@ const App = (): React.JSX.Element => {
}}
/>
<Stack.Screen
name="send-tx-embed"
component={SendTxEmbed}
name="sign-tx-embed"
component={SignTxEmbed}
options={{
header: () => <></>,
}}

View File

@ -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;
@ -48,7 +49,7 @@ const useGetOrCreateAccounts = () => {
const accountsData = await getOrCreateAccountsForChain(event.data.chainId);
sendMessage(
event.source as Window, 'WALLET_ACCOUNTS_DATA',
event.source as Window, WALLET_ACCOUNTS_DATA,
accountsData.map(account => account.address),
event.origin
);

View File

@ -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);

View File

@ -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<NativeStackNavigationProp<StackParamsList>>();
@ -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) {

View File

@ -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<boolean>(false);
const [transactionDetails, setTransactionDetails] = useState<TransactionDetails | null>(null);
const [isTxLoading, setIsTxLoading] = useState(false);
@ -74,26 +72,27 @@ export const SendTxEmbed = () => {
// Message Handlers
const handleGetAccountsRequest = useCallback(async (event: MessageEvent<IncomingMessageEventData>) => {
const handleGetCosmosAccountsRequest = useCallback(async (event: MessageEvent<IncomingMessageEventData>) => {
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<IncomingMessageEventData>);
case REQUEST_COSMOS_ACCOUNTS_DATA:
handleGetCosmosAccountsRequest(event as MessageEvent<IncomingMessageEventData>);
break;
case 'SIGN_ONBOARD_TX_REQUEST':
case REQUEST_SIGN_TX:
handleSignOnboardTxRequest(event as MessageEvent<IncomingMessageEventData>);
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);

View File

@ -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, '*');
}
}}

View File

@ -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 = {

View File

@ -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';