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