Skip to content

Commit

Permalink
fix: avoid regenerating mnemonics on back button hit (#8885)
Browse files Browse the repository at this point in the history
  • Loading branch information
NeOMakinG authored Feb 19, 2025
1 parent 2ba7679 commit e0c2281
Show file tree
Hide file tree
Showing 5 changed files with 78 additions and 120 deletions.
116 changes: 51 additions & 65 deletions src/context/WalletProvider/NativeWallet/components/NativeCreate.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import {
AlertIcon,
Button,
Code,
Link,
ModalBody,
ModalFooter,
ModalHeader,
Expand All @@ -13,11 +12,10 @@ import {
} from '@chakra-ui/react'
import { Default } from '@shapeshiftoss/hdwallet-native/dist/crypto/isolation/engines'
import type { Vault } from '@shapeshiftoss/hdwallet-native-vault'
import { useQuery } from '@tanstack/react-query'
import { range } from 'lodash'
import type { ReactNode } from 'react'
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
import { FaEye } from 'react-icons/fa'
import { useTranslate } from 'react-polyglot'
import { useHistory, useLocation } from 'react-router-dom'
import { Text } from 'components/Text'
import { NativeWalletRoutes } from 'context/WalletProvider/types'
Expand All @@ -43,18 +41,24 @@ export const NativeCreate = () => {
const history = useHistory()
const location = useLocation<LocationState>()
const [revealed, setRevealed] = useState<boolean>(false)
const translate = useTranslate()
const mixpanel = getMixPanel()
const revealedOnce = useRef<boolean>(false)
const handleShow = useCallback(() => {
revealedOnce.current = true
setRevealed(!revealed)
}, [revealed])
const [vault, setVault] = useState<Vault | null>(null)
const [words, setWords] = useState<ReactNode[] | null>(null)
const [revoker] = useState(new (Revocable(class {}))())

const isLegacyWallet = !!location.state?.vault
const revokerRef = useRef(new (Revocable(class {}))())
const initiatedWordsRef = useRef(false)

const { data: vault } = useQuery({
queryKey: ['native-create-vault', location.state?.vault],
queryFn: async () => {
return location.state?.vault ?? (await getVault())
},
staleTime: Infinity,
gcTime: Infinity,
})

const placeholders = useMemo(() => {
return range(1, 13).map(i => (
Expand All @@ -76,75 +80,57 @@ export const NativeCreate = () => {
if (vault) {
history.push(NativeWalletRoutes.CreateTest, {
vault,
isLegacyWallet,
})
mixpanel?.track(MixPanelEvent.NativeCreate)
}
}, [history, isLegacyWallet, mixpanel, vault])
}, [history, mixpanel, vault])

useEffect(() => {
;(async () => {
try {
// If the vault is already passed from the legacy wallet flow, use it.
const vault = isLegacyWallet ? location.state.vault : await getVault()
setVault(vault)
} catch (e) {
console.error(e)
}
})()
}, [setVault, location.state?.vault, isLegacyWallet])
const { data: words } = useQuery({
queryKey: ['native-create-words', vault],
queryFn: async () => {
if (!vault) return []

useEffect(() => {
if (!vault) return
;(async () => {
try {
setWords(
(await vault.unwrap().get('#mnemonic')).split(' ').map((word: string, index: number) =>
revocable(
<Tag
p={2}
flexBasis='31%'
justifyContent='flex-start'
fontSize='md'
key={word}
colorScheme='blue'
>
<Code mr={2}>{index + 1}</Code>
{word}
</Tag>,
revoker.addRevoker.bind(revocable),
),
),
)
} catch (e) {
console.error(e)
setWords(null)
}
})()
revokerRef.current?.revoke()
revokerRef.current = new (Revocable(class {}))()

const mnemonic = await vault.unwrap().get('#mnemonic')

initiatedWordsRef.current = true

return mnemonic.split(' ').map((word: string, index: number) =>
revocable(
<Tag
p={2}
flexBasis='31%'
justifyContent='flex-start'
fontSize='md'
key={`${word}-${index}`}
colorScheme='blue'
>
<Code mr={2}>{index + 1}</Code>
{word}
</Tag>,
revokerRef.current.addRevoker.bind(revokerRef.current),
),
)
},
enabled: !!vault,
staleTime: 0,
gcTime: 0,
})

useEffect(() => {
return () => {
revoker.revoke()
if (initiatedWordsRef.current) {
revokerRef.current?.revoke()
initiatedWordsRef.current = false
}
}
}, [setWords, vault, revoker])
}, [])

return (
<>
<ModalHeader>
{isLegacyWallet && (
<Alert status='error' mb={4}>
<AlertIcon />
<AlertDescription fontSize='md'>
<Text translation={'walletProvider.shapeShift.legacy.deprecatedWarning'} />
<Link
href={'https://shapeshift.zendesk.com/hc/en-us/articles/6161030693517'}
fontWeight='normal'
isExternal
>
{translate('walletProvider.shapeShift.legacy.learnMore')}
</Link>
</AlertDescription>
</Alert>
)}
<Text translation={'walletProvider.shapeShift.create.header'} />
</ModalHeader>
<ModalBody>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
import type { Vault } from '@shapeshiftoss/hdwallet-native-vault'
import { useCallback } from 'react'
import { useHistory, useLocation } from 'react-router-dom'
import { NativeWalletRoutes } from 'context/WalletProvider/types'

import type { LocationState } from '../types'
import { LegacyLoginSuccess } from './LegacyMigration/LegacyLoginSuccess'

export const NativeLegacySuccess = () => {
const history = useHistory<{ vault: Vault }>()
const location = useLocation<{ vault: Vault }>()
const history = useHistory<LocationState>()
const location = useLocation<LocationState>()

const handleContinue = useCallback(() => {
history.push(NativeWalletRoutes.Create, location.state)
history.push(NativeWalletRoutes.Create, {
vault: location.state.vault,
})
}, [history, location.state])

return <LegacyLoginSuccess onContinue={handleContinue} />
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import {
Box,
Button,
Checkbox,
Divider,
Flex,
Icon,
ModalBody,
Expand Down Expand Up @@ -44,7 +42,6 @@ export const NativeTestPhrase = ({ history, location }: NativeSetupProps) => {
const borderColor = useColorModeValue('gray.300', 'whiteAlpha.200')
const dottedTitleBackground = useColorModeValue('#f7fafc', '#2e3236')
const [testState, setTestState] = useState<TestState | null>(null)
const [hasAlreadySaved, setHasAlreadySaved] = useState(false)
const testCount = useRef(0)
const [revoker] = useState(new (Revocable(class {}))())
const [shuffledNumbers] = useState(slice(shuffle(range(12)), 0, TEST_COUNT_REQUIRED))
Expand All @@ -69,13 +66,7 @@ export const NativeTestPhrase = ({ history, location }: NativeSetupProps) => {
[borderColor],
)

const onCheck = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {
// Check the captcha in case the captcha has been validated
setHasAlreadySaved(e.target.checked)
return
}, [])

const { vault, isLegacyWallet } = location.state
const { vault } = location.state

const shuffleMnemonic = useCallback(async () => {
if (testCount.current >= TEST_COUNT_REQUIRED) return
Expand Down Expand Up @@ -135,11 +126,6 @@ export const NativeTestPhrase = ({ history, location }: NativeSetupProps) => {
}
}

const handleSkipClick = useCallback(
() => history.push('/native/password', { vault }),
[history, vault],
)

return !testState ? null : (
<>
<ModalHeader>
Expand Down Expand Up @@ -233,37 +219,6 @@ export const NativeTestPhrase = ({ history, location }: NativeSetupProps) => {
))}
</Flex>
</Flex>

{isLegacyWallet && (
<Box>
<Box position='relative' mb={8} mt={10}>
<Divider />
<Text
translation={'common.or'}
transform='translate(-50%, -50%)'
left='50%'
position='absolute'
color='text.subtle'
/>
</Box>
<Checkbox mb={4} spacing={4} onChange={onCheck} isChecked={hasAlreadySaved}>
<Text
fontSize='sm'
translation={'walletProvider.shapeShift.legacy.alreadySavedConfirm'}
/>
</Checkbox>
<Button
colorScheme='blue'
width='full'
size='md'
isDisabled={!hasAlreadySaved}
data-test='wallet-native-login-skip'
onClick={handleSkipClick}
>
<Text translation={'common.skip'} />
</Button>
</Box>
)}
</ModalBody>
</>
)
Expand Down
1 change: 0 additions & 1 deletion src/context/WalletProvider/NativeWallet/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ export type NativeWalletValues = {

export interface LocationState {
vault: Vault
isLegacyWallet?: boolean
error?: {
message: string
}
Expand Down
24 changes: 20 additions & 4 deletions src/context/WalletProvider/NewWalletViews/NewWalletViewsSwitch.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,14 @@ import { useTranslate } from 'react-polyglot'
import { reactQueries } from 'react-queries'
import type { StaticContext } from 'react-router'
import type { RouteComponentProps } from 'react-router-dom'
import { Route, Switch, useHistory } from 'react-router-dom'
import { Route, Switch, useHistory, useLocation } from 'react-router-dom'
import { Text } from 'components/Text'
import { WalletActions } from 'context/WalletProvider/actions'
import { KeepKeyRoutes as KeepKeyRoutesEnum } from 'context/WalletProvider/routes'
import { useWallet } from 'hooks/useWallet/useWallet'

import type { KeyManager } from '../KeyManager'
import type { LocationState } from '../NativeWallet/types'
import { NativeWalletRoutes } from '../types'
import { RDNS_TO_FIRST_CLASS_KEYMANAGER } from './constants'
import { KeepKeyRoutes } from './routes/KeepKeyRoutes'
Expand Down Expand Up @@ -96,6 +97,7 @@ export const NewWalletViewsSwitch = () => {
const [selectedWalletId, setSelectedWalletId] = useState<string | null>(null)

const history = useHistory()
const location = useLocation<LocationState>()
const toast = useToast()
const translate = useTranslate()
const {
Expand Down Expand Up @@ -129,15 +131,29 @@ export const NewWalletViewsSwitch = () => {
}, [toast, translate, wallet])

const handleBack = useCallback(async () => {
history.goBack()
const { pathname } = history.location

if (location.state?.vault && pathname === NativeWalletRoutes.CreateTest) {
history.replace({
pathname: NativeWalletRoutes.Create,
state: { vault: location.state.vault },
})

// Queue navigation in the next tick to ensure state is updated
setTimeout(() => {
history.goBack()
}, 0)
} else {
history.goBack()
}

// If we're back at the select wallet modal, remove the initial route
// otherwise clicking the button for the same wallet doesn't do anything
const { pathname } = history.location
if ([INITIAL_WALLET_MODAL_ROUTE, NativeWalletRoutes.Load].includes(pathname)) {
dispatch({ type: WalletActions.SET_INITIAL_ROUTE, payload: '' })
}
await cancelWalletRequests()
}, [cancelWalletRequests, dispatch, history])
}, [cancelWalletRequests, dispatch, history, location.state])

const handleRouteReset = useCallback(() => {
history.replace(INITIAL_WALLET_MODAL_ROUTE)
Expand Down

0 comments on commit e0c2281

Please sign in to comment.