Use page for handling sign requests (#42)

* Use sign request page instead of modal

* Fix context

* Remove multiple if statements

* Change metadata

* Remove sign modal

* Make review changes

* Remove state

---------

Co-authored-by: Adw8 <adwait@deepstacksoft.com>
This commit is contained in:
Adwait Gharpure 2024-03-07 12:44:05 +05:30 committed by GitHub
parent a4e0dc5406
commit 05be6008de
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 107 additions and 149 deletions

52
App.tsx
View File

@ -15,7 +15,6 @@ import HomeScreen from './components/HomeScreen';
import SignRequest from './components/SignRequest';
import InvalidPath from './components/InvalidPath';
import PairingModal from './components/PairingModal';
import SignModal from './components/SignModal';
import WalletConnect from './components/WalletConnect';
import { StackParamsList } from './types';
import useInitialization, {
@ -23,6 +22,8 @@ import useInitialization, {
} from './utils/wallet-connect/WalletConnectUtils';
import { EIP155_SIGNING_METHODS } from './utils/wallet-connect/EIP155Lib';
import { AccountsContext } from './context/AccountsContext';
import { getSignParamsMessage } from './utils/wallet-connect/Helpers';
import { navigationRef, navigateTo } from './utils/RootNavigation';
const Stack = createNativeStackNavigator<StackParamsList>();
@ -37,8 +38,13 @@ const App = (): React.JSX.Element => {
SignClientTypes.EventArguments['session_proposal'] | undefined
>();
const [requestSession, setRequestSession] = useState<any>();
const [requestEventData, setRequestEventData] = useState<any>();
const [signModalVisible, setSignModalVisible] = useState(false);
const currentEthAddresses = useMemo(() => {
if (accounts.ethAccounts.length > 0) {
return accounts.ethAccounts.map(account => account.address);
}
return [];
}, [accounts]);
const onSessionProposal = useCallback(
(proposal: SignClientTypes.EventArguments['session_proposal']) => {
@ -52,6 +58,8 @@ const App = (): React.JSX.Element => {
async (requestEvent: SignClientTypes.EventArguments['session_request']) => {
const { topic, params } = requestEvent;
const { request } = params;
const address = request.params[1];
const message = getSignParamsMessage(request.params);
const requestSessionData =
web3wallet.engine.signClient.session.get(topic);
@ -59,27 +67,31 @@ const App = (): React.JSX.Element => {
case EIP155_SIGNING_METHODS.ETH_SIGN:
case EIP155_SIGNING_METHODS.PERSONAL_SIGN:
setRequestSession(requestSessionData);
setRequestEventData(requestEvent);
setSignModalVisible(true);
if (address && message) {
navigateTo('SignRequest', {
network: 'eth',
address,
message,
requestEvent,
requestSession,
});
}
return;
}
},
[],
[requestSession],
);
useEffect(() => {
web3wallet?.on('session_proposal', onSessionProposal);
web3wallet?.on('session_request', onSessionRequest);
return () => {
web3wallet?.off('session_proposal', onSessionProposal);
web3wallet?.off('session_request', onSessionRequest);
};
//TODO: Investigate dependancies
});
const currentEthAddresses = useMemo(() => {
if (accounts.ethAccounts.length > 0) {
return accounts.ethAccounts.map(account => account.address);
}
return [];
}, [accounts]);
const linking = {
prefixes: ['https://www.laconic-wallet.com'],
config: {
@ -92,7 +104,7 @@ const App = (): React.JSX.Element => {
};
return (
<NavigationContainer linking={linking}>
<NavigationContainer linking={linking} ref={navigationRef}>
<Stack.Navigator>
<Stack.Screen
name="Laconic"
@ -114,7 +126,7 @@ const App = (): React.JSX.Element => {
name="SignRequest"
component={SignRequest}
options={{
title: 'Sign Message?',
title: 'Sign this message?',
}}
/>
<Stack.Screen
@ -140,14 +152,6 @@ const App = (): React.JSX.Element => {
setCurrentProposal={setCurrentProposal}
currentEthAddresses={currentEthAddresses}
/>
<SignModal
visible={signModalVisible}
setModalVisible={setSignModalVisible}
requestEvent={requestEventData}
requestSession={requestSession}
currentEthAddresses={currentEthAddresses}
/>
</NavigationContainer>
);
};

View File

@ -1,100 +0,0 @@
import React, { useContext } from 'react';
import { Button, Text } from 'react-native-paper';
import { Image, Modal, View } from 'react-native';
import { getSignParamsMessage } from '../utils/wallet-connect/Helpers';
import { web3wallet } from '../utils/wallet-connect/WalletConnectUtils';
import {
approveEIP155Request,
rejectEIP155Request,
} from '../utils/wallet-connect/EIP155Requests';
import styles from '../styles/stylesheet';
import { SignModalProps } from '../types';
import { AccountsContext } from '../context/AccountsContext';
const SignModal = ({
visible,
setModalVisible,
requestEvent,
requestSession,
currentEthAddresses,
}: SignModalProps) => {
const { accounts } = useContext(AccountsContext);
if (!requestEvent || !requestSession) {
return null;
}
const chainID = requestEvent?.params?.chainId?.toUpperCase();
const message = getSignParamsMessage(requestEvent?.params?.request?.params);
const requestName = requestSession?.peer?.metadata?.name;
const requestIcon = requestSession?.peer?.metadata?.icons[0];
const requestURL = requestSession?.peer?.metadata?.url;
const { topic } = requestEvent;
const onApprove = async () => {
if (requestEvent) {
const response = await approveEIP155Request(
requestEvent,
currentEthAddresses,
accounts.ethAccounts,
);
await web3wallet.respondSessionRequest({
topic,
response,
});
setModalVisible(false);
}
};
const onReject = async () => {
if (requestEvent) {
const response = rejectEIP155Request(requestEvent);
await web3wallet.respondSessionRequest({
topic,
response,
});
setModalVisible(false);
}
};
return (
<Modal visible={visible} animationType="slide" transparent>
<View style={styles.container}>
<View style={styles.modalContentContainer}>
<Text variant="titleLarge">Sign this message?</Text>
<Image
style={styles.dappLogo}
source={{
uri: requestIcon,
}}
/>
<Text>{requestName}</Text>
<Text variant="bodyMedium">{requestURL}</Text>
<View style={styles.messageBody}>
<Text variant="bodyLarge">{message}</Text>
</View>
<Text>Chains: {chainID}</Text>
<View style={styles.flexRow}>
<Button mode="outlined" onPress={() => onReject()}>
Cancel
</Button>
<View style={styles.space} />
<Button mode="contained" onPress={() => onApprove()}>
Accept
</Button>
</View>
</View>
</View>
</Modal>
);
};
export default SignModal;

View File

@ -1,4 +1,4 @@
import React, { useEffect, useState } from 'react';
import React, { useContext, useEffect, useState } from 'react';
import { Alert, View } from 'react-native';
import { ActivityIndicator, Button, Text } from 'react-native-paper';
@ -13,6 +13,11 @@ import AccountDetails from './AccountDetails';
import styles from '../styles/stylesheet';
import { signMessage } from '../utils/sign-message';
import { retrieveSingleAccount } from '../utils/accounts';
import {
approveEIP155Request,
rejectEIP155Request,
} from '../utils/wallet-connect/EIP155Requests';
import { web3wallet } from '../utils/wallet-connect/WalletConnectUtils';
type SignRequestProps = NativeStackScreenProps<StackParamsList, 'SignRequest'>;
@ -20,7 +25,7 @@ const SignRequest = ({ route }: SignRequestProps) => {
const [account, setAccount] = useState<Account>();
const [message, setMessage] = useState<string>('');
const [network, setNetwork] = useState<string>('');
const [loaded, setLoaded] = useState(false);
const [isLoading, setIsLoading] = useState(true);
const navigation =
useNavigation<NativeStackNavigationProp<StackParamsList>>();
@ -36,7 +41,9 @@ const SignRequest = ({ route }: SignRequestProps) => {
);
if (!requestAccount) {
navigation.navigate('InvalidPath');
return;
}
if (requestAccount && requestAccount !== account) {
setAccount(requestAccount);
}
@ -66,7 +73,6 @@ const SignRequest = ({ route }: SignRequestProps) => {
};
useEffect(() => {
setLoaded(true);
if (route.path) {
const sanitizedRoute = sanitizePath(route.path);
sanitizedRoute &&
@ -76,11 +82,28 @@ const SignRequest = ({ route }: SignRequestProps) => {
route.params?.address,
route.params?.message,
);
return;
}
setLoaded(false);
route.params &&
retrieveData(
route.params?.network,
route.params?.address,
route.params?.message,
);
setIsLoading(false);
}, [route]);
const signMessageHandler = async () => {
const handleEIP155Request = async () => {
const { requestEvent } = route.params || {};
const response = await approveEIP155Request(
requestEvent,
account?.counterId,
);
const { topic } = requestEvent;
await web3wallet.respondSessionRequest({ topic, response });
};
const handleIntent = async () => {
if (!account) {
throw new Error('Account is not valid');
}
@ -91,17 +114,36 @@ const SignRequest = ({ route }: SignRequestProps) => {
accountId: account.counterId,
});
Alert.alert('Signature', signedMessage);
navigation.navigate('Laconic');
}
};
const signMessageHandler = async () => {
if (route.params?.requestEvent) {
await handleEIP155Request();
} else {
await handleIntent();
}
navigation.navigate('Laconic');
};
const rejectRequestHandler = async () => {
if (route.params?.requestEvent) {
const response = rejectEIP155Request(route.params?.requestEvent);
const { topic } = route.params?.requestEvent;
await web3wallet.respondSessionRequest({
topic,
response,
});
}
navigation.navigate('Laconic');
};
return (
<>
{!loaded ? (
{isLoading ? (
<ActivityIndicator />
) : (
<View style={styles.appContainer}>
<AccountDetails account={account} />
<View style={styles.requestMessage}>
@ -119,8 +161,6 @@ const SignRequest = ({ route }: SignRequestProps) => {
</Button>
</View>
</View>
) : (
<ActivityIndicator />
)}
</>
);

View File

@ -4,7 +4,14 @@ export type StackParamsList = {
Laconic: undefined;
SignMessage: { selectedNetwork: string; accountInfo: Account } | undefined;
SignRequest:
| { network: string; address: string; message: string }
| {
network: string;
address: string;
message: string;
// TODO: remove any
requestEvent?: any;
requestSession?: any;
}
| undefined;
InvalidPath: undefined;
WalletConnect: undefined;

14
utils/RootNavigation.ts Normal file
View File

@ -0,0 +1,14 @@
import {
CommonActions,
createNavigationContainerRef,
} from '@react-navigation/native';
import { StackParamsList } from '../types';
export const navigationRef = createNavigationContainerRef<StackParamsList>();
export function navigateTo(routeName: string, params?: object) {
if (navigationRef.isReady()) {
navigationRef.dispatch(CommonActions.navigate(routeName, params));
}
}

View File

@ -5,27 +5,20 @@ import { SignClientTypes } from '@walletconnect/types';
import { getSdkError } from '@walletconnect/utils';
import { EIP155_SIGNING_METHODS } from './EIP155Lib';
import { getSignParamsMessage, getAccountNumberFromParams } from './Helpers';
import { getSignParamsMessage } from './Helpers';
import { signEthMessage } from '../sign-message';
import { Account } from '../../types';
export async function approveEIP155Request(
requestEvent: SignClientTypes.EventArguments['session_request'],
currentEthAddresses: string[],
ethAccounts: Account[],
counterId: number | undefined,
) {
const { params, id } = requestEvent;
const { request } = params;
const counterId = await getAccountNumberFromParams(
currentEthAddresses,
ethAccounts,
params,
);
switch (request.method) {
case EIP155_SIGNING_METHODS.PERSONAL_SIGN:
const message = getSignParamsMessage(request.params);
const signedMessage = await signEthMessage(message, counterId);
const signedMessage =
counterId && (await signEthMessage(message, counterId));
return formatJsonRpcResult(id, signedMessage);
default:

View File

@ -28,10 +28,10 @@ export async function createWeb3Wallet() {
web3wallet = await Web3Wallet.init({
core,
metadata: {
name: 'Web3Wallet React Native Tutorial',
description: 'ReactNative Web3Wallet',
url: 'https://walletconnect.com/',
icons: ['https://avatars.githubusercontent.com/u/37784886'],
name: 'Laconic Wallet',
description: 'ReactNative Laconic Wallet',
url: 'https://wallet.laconic.com/',
icons: ['https://avatars.githubusercontent.com/u/92608123'],
},
});
}