diff --git a/native/account/registration/missing-registration-data/missing-siwe-backup-message.react.js b/native/account/registration/missing-registration-data/missing-siwe-backup-message.react.js index 9b848d5b6..d49bf41be 100644 --- a/native/account/registration/missing-registration-data/missing-siwe-backup-message.react.js +++ b/native/account/registration/missing-registration-data/missing-siwe-backup-message.react.js @@ -1,53 +1,74 @@ // @flow +import invariant from 'invariant'; import * as React from 'react'; +import { Alert } from 'react-native'; import { SafeAreaView } from 'react-native-safe-area-context'; import { type SIWEResult } from 'lib/types/siwe-types.js'; +import { isValidEthereumAddress } from 'lib/utils/siwe-utils.js'; import { commCoreModule } from '../../../native-modules.js'; import { type RootNavigationProp } from '../../../navigation/root-navigator.react.js'; import { type NavigationRoute } from '../../../navigation/route-names.js'; +import { useSelector } from '../../../redux/redux-utils.js'; import { useStyles } from '../../../themes/colors.js'; import { CreateSIWEBackupMessageBase } from '../siwe-backup-message-creation.react.js'; type Props = { +navigation: RootNavigationProp<'CreateMissingSIWEBackupMessage'>, +route: NavigationRoute<'CreateMissingSIWEBackupMessage'>, }; function CreateMissingSIWEBackupMessage(props: Props): React.Node { const styles = useStyles(unboundStyles); const { goBack } = props.navigation; + const currentUserInfo = useSelector(state => state.currentUserInfo); + const loggedInEthereumAccountAddress = currentUserInfo?.username; + + invariant( + loggedInEthereumAccountAddress && + isValidEthereumAddress(loggedInEthereumAccountAddress), + 'current username must be valid ethereum address to attempt ' + + 'backup message generation', + ); const onSuccessfulWalletSignature = React.useCallback( (result: SIWEResult) => { void (async () => { - const { message, signature } = result; + const { message, signature, address } = result; + if (loggedInEthereumAccountAddress !== address) { + Alert.alert( + 'Mismatched Ethereum address', + 'You picked a different wallet than the one you use to sign in.', + ); + return; + } + await commCoreModule.setSIWEBackupSecrets({ message, signature }); goBack(); })(); }, - [goBack], + [goBack, loggedInEthereumAccountAddress], ); return ( ); } const safeAreaEdges = ['top']; const unboundStyles = { container: { flex: 1, backgroundColor: 'panelBackground', justifyContent: 'space-between', }, }; export default CreateMissingSIWEBackupMessage; diff --git a/native/account/registration/siwe-backup-message-creation.react.js b/native/account/registration/siwe-backup-message-creation.react.js index 7e59f1578..35146ee2f 100644 --- a/native/account/registration/siwe-backup-message-creation.react.js +++ b/native/account/registration/siwe-backup-message-creation.react.js @@ -1,240 +1,250 @@ // @flow import Icon from '@expo/vector-icons/MaterialIcons.js'; import invariant from 'invariant'; import * as React from 'react'; -import { View, Text } from 'react-native'; +import { View, Text, Alert } from 'react-native'; import { type SIWEResult, SIWEMessageTypes } from 'lib/types/siwe-types.js'; import RegistrationButtonContainer from './registration-button-container.react.js'; import RegistrationButton from './registration-button.react.js'; import RegistrationContainer from './registration-container.react.js'; import RegistrationContentContainer from './registration-content-container.react.js'; import { RegistrationContext } from './registration-context.js'; import { type RegistrationNavigationProp } from './registration-navigator.react.js'; import type { CoolOrNerdMode, AccountSelection, AvatarData, } from './registration-types.js'; import { type NavigationRoute, RegistrationTermsRouteName, } from '../../navigation/route-names.js'; import { useStyles } from '../../themes/colors.js'; import SIWEPanel from '../siwe-panel.react.js'; type PanelState = 'closed' | 'opening' | 'open' | 'closing'; type CreateSIWEBackupMessageBaseProps = { +onSuccessfulWalletSignature: (result: SIWEResult) => void, +onExistingWalletSignature?: () => void, +onSkip?: () => void, }; const CreateSIWEBackupMessageBase: React.ComponentType = React.memo( function CreateSIWEBackupMessageBase( props: CreateSIWEBackupMessageBaseProps, ): React.Node { const { onSuccessfulWalletSignature, onExistingWalletSignature, onSkip } = props; const styles = useStyles(unboundStyles); const [panelState, setPanelState] = React.useState('closed'); const openPanel = React.useCallback(() => { setPanelState('opening'); }, []); const onPanelClosed = React.useCallback(() => { setPanelState('closed'); }, []); const onPanelClosing = React.useCallback(() => { setPanelState('closing'); }, []); const siwePanelSetLoading = React.useCallback( (loading: boolean) => { if (panelState === 'closing' || panelState === 'closed') { return; } setPanelState(loading ? 'opening' : 'open'); }, [panelState], ); let siwePanel; if (panelState !== 'closed') { siwePanel = ( ); } const newSignatureButtonText = onExistingWalletSignature ? 'Encrypt with new signature' : 'Encrypt with Ethereum signature'; const newSignatureButtonVariant = onExistingWalletSignature ? 'outline' : 'enabled'; let useExistingSignatureButton; if (onExistingWalletSignature) { useExistingSignatureButton = ( ); } let onSkipButton; if (onSkip) { onSkipButton = ( ); } return ( <> Encrypting your Comm backup To make sure we can’t see your data, Comm encrypts your backup using a signature from your wallet. You can always recover your data as long as you still control your wallet. {useExistingSignatureButton} {onSkipButton} {siwePanel} ); }, ); export type CreateSIWEBackupMessageParams = { +userSelections: { +coolOrNerdMode?: ?CoolOrNerdMode, +keyserverURL?: ?string, +farcasterID: ?string, +accountSelection: AccountSelection, +avatarData: ?AvatarData, }, }; type Props = { +navigation: RegistrationNavigationProp<'CreateSIWEBackupMessage'>, +route: NavigationRoute<'CreateSIWEBackupMessage'>, }; function CreateSIWEBackupMessage(props: Props): React.Node { const { navigate } = props.navigation; const { params } = props.route; const { userSelections } = params; const registrationContext = React.useContext(RegistrationContext); invariant(registrationContext, 'registrationContext should be set'); const { cachedSelections, setCachedSelections } = registrationContext; const onSuccessfulWalletSignature = React.useCallback( (result: SIWEResult) => { - const { message, signature } = result; + const selectedEthereumAddress = userSelections.accountSelection.address; + const { message, signature, address } = result; + + if (address !== selectedEthereumAddress) { + Alert.alert( + 'Mismatched Ethereum address', + 'You picked a different wallet than the one you use to sign in.', + ); + return; + } + const newUserSelections = { ...userSelections, siweBackupSecrets: { message, signature }, }; setCachedSelections(oldUserSelections => ({ ...oldUserSelections, siweBackupSecrets: { message, signature }, })); navigate<'RegistrationTerms'>({ name: RegistrationTermsRouteName, params: { userSelections: newUserSelections }, }); }, [navigate, setCachedSelections, userSelections], ); const { siweBackupSecrets } = cachedSelections; const onExistingWalletSignature = React.useCallback(() => { const registrationTermsParams = { userSelections: { ...userSelections, siweBackupSecrets, }, }; navigate<'RegistrationTerms'>({ name: RegistrationTermsRouteName, params: registrationTermsParams, }); }, [navigate, siweBackupSecrets, userSelections]); if (siweBackupSecrets) { return ( ); } return ( ); } const unboundStyles = { scrollViewContentContainer: { flexGrow: 1, }, header: { fontSize: 24, color: 'panelForegroundLabel', paddingBottom: 16, }, body: { fontFamily: 'Arial', fontSize: 15, lineHeight: 20, color: 'panelForegroundSecondaryLabel', paddingBottom: 16, }, siweBackupIcon: { color: 'panelForegroundIcon', }, siweBackupIconContainer: { flexGrow: 1, alignItems: 'center', justifyContent: 'center', }, }; export { CreateSIWEBackupMessageBase, CreateSIWEBackupMessage };