Skip to content

Commit 9dca6eb

Browse files
authored
feat: Support import hardware wallet 24-bit seed (#3275)
1 parent 483a289 commit 9dca6eb

File tree

16 files changed

+131
-28
lines changed

16 files changed

+131
-28
lines changed

packages/neuron-ui/src/components/Receive/index.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -90,8 +90,9 @@ export const AddressQrCodeWithCopyZone = ({
9090
const Receive = ({ onClose, address }: { onClose?: () => void; address?: string }) => {
9191
const [t] = useTranslation()
9292
const { wallet } = useGlobalState()
93-
const { addresses } = wallet
93+
const { addresses, isHD } = wallet
9494
const isSingleAddress = addresses.length === 1
95+
const isHardwareWallet = !isHD && isSingleAddress
9596

9697
const accountAddress = useMemo(() => {
9798
if (isSingleAddress) {
@@ -128,7 +129,7 @@ const Receive = ({ onClose, address }: { onClose?: () => void; address?: string
128129
onClick={() => setIsInShortFormat(is => !is)}
129130
/>
130131

131-
{isSingleAddress && <VerifyHardwareAddress address={accountAddress} wallet={wallet} onClose={onClose} />}
132+
{isHardwareWallet && <VerifyHardwareAddress address={accountAddress} wallet={wallet} onClose={onClose} />}
132133
</div>
133134
</Dialog>
134135
)

packages/neuron-ui/src/components/WalletWizard/hooks.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
import { useState, useCallback } from 'react'
1+
import { useState, useCallback, useEffect } from 'react'
22

33
const MNEMONIC_SENTENCE_WORDS = 12
44

5-
export const useInputWords = () => {
6-
const [inputsWords, setInputsWords] = useState<string[]>(new Array(MNEMONIC_SENTENCE_WORDS).fill(''))
5+
export const useInputWords = (wordsCount: number = MNEMONIC_SENTENCE_WORDS) => {
6+
const [inputsWords, setInputsWords] = useState<string[]>(new Array(wordsCount).fill(''))
77
const onChangeInput = useCallback(
88
(
99
e:
@@ -37,6 +37,9 @@ export const useInputWords = () => {
3737
},
3838
[setInputsWords]
3939
)
40+
useEffect(() => {
41+
setInputsWords(new Array(wordsCount).fill(''))
42+
}, [wordsCount])
4043
return {
4144
inputsWords,
4245
onChangeInput,

packages/neuron-ui/src/components/WalletWizard/index.tsx

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ const initState: WithWizardState = {
7676
password: '',
7777
confirmPassword: '',
7878
name: '',
79+
isHardware: false,
7980
}
8081

8182
const submissionInputs = [
@@ -182,6 +183,8 @@ const Welcome = ({ rootPath = '/wizard/', wallets = [], dispatch }: WizardElemen
182183

183184
Welcome.displayName = 'Welcome'
184185

186+
const LEDGER_WORDS_COUNT = 24
187+
185188
const Mnemonic = ({ state = initState, rootPath = '/wizard/', dispatch }: WizardElementProps) => {
186189
const { generated, imported } = state
187190
const navigate = useNavigate()
@@ -193,8 +196,9 @@ const Mnemonic = ({ state = initState, rootPath = '/wizard/', dispatch }: Wizard
193196
[MnemonicAction.Verify]: 'wizard.replenish-your-seed',
194197
[MnemonicAction.Import]: 'wizard.input-your-seed',
195198
}[type]
196-
const { inputsWords, onChangeInput, setInputsWords } = useInputWords()
197199
const [searchParams] = useSearchParams()
200+
const isHardware = searchParams.get('isHardware') === 'true'
201+
const { inputsWords, onChangeInput, setInputsWords } = useInputWords(isHardware ? LEDGER_WORDS_COUNT : undefined)
198202
const disableNext =
199203
(type === MnemonicAction.Import && inputsWords.some(v => !v)) ||
200204
(type === MnemonicAction.Verify && generated !== inputsWords.join(' '))
@@ -228,6 +232,14 @@ const Mnemonic = ({ state = initState, rootPath = '/wizard/', dispatch }: Wizard
228232
})
229233
}
230234
}, [dispatch, type, navigate, setBlankIndexes])
235+
useEffect(() => {
236+
if (isHardware) {
237+
dispatch({
238+
type: 'isHardware',
239+
payload: true,
240+
})
241+
}
242+
}, [dispatch, isHardware])
231243

232244
const globalDispatch = useDispatch()
233245

@@ -317,6 +329,7 @@ const Mnemonic = ({ state = initState, rootPath = '/wizard/', dispatch }: Wizard
317329
inputsWords={inputsWords}
318330
onChangeInputWord={onChangeInput}
319331
blankIndexes={MnemonicAction.Import ? undefined : blankIndexes}
332+
wordsCount={isHardware ? LEDGER_WORDS_COUNT : undefined}
320333
/>
321334
{type === MnemonicAction.Import && <div className={styles.tips}>{t('wizard.input-seed-first-empty-space')}</div>}
322335
<div className={styles.actions}>
@@ -337,7 +350,7 @@ export const getAlertStatus = (fieldInit: boolean, success: boolean) => {
337350
}
338351

339352
const Submission = ({ state = initState, wallets = [], dispatch }: WizardElementProps) => {
340-
const { name, password, confirmPassword, imported } = state
353+
const { name, password, confirmPassword, imported, isHardware } = state
341354
const navigate = useNavigate()
342355
const { type = MnemonicAction.Create } = useParams<{ type: MnemonicAction }>()
343356
const [t] = useTranslation()
@@ -396,6 +409,7 @@ const Submission = ({ state = initState, wallets = [], dispatch }: WizardElement
396409
name,
397410
password,
398411
mnemonic: imported,
412+
isHardware,
399413
}
400414
openDialog()
401415
setTimeout(() => {

packages/neuron-ui/src/components/withWizard/index.tsx

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,14 @@ export interface Element {
77
comp: React.FC<any>
88
}
99

10-
export interface WithWizardState {
11-
[key: string]: string
10+
export type WithWizardState = {
11+
generated: string
12+
imported: string
13+
password: string
14+
confirmPassword: string
15+
name: string
16+
isHardware: boolean
17+
[propName: string]: any
1218
}
1319

1420
export interface WizardProps {
@@ -27,7 +33,7 @@ export interface WizardElementProps {
2733
}
2834

2935
const reducer = (
30-
state: { [key: string]: string },
36+
state: WithWizardState,
3137
{
3238
type,
3339
payload,

packages/neuron-ui/src/types/Controller/index.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ declare namespace Controller {
2727
name: string
2828
mnemonic: string
2929
password: string
30+
isHardware?: boolean
3031
}
3132

3233
interface ImportKeystoreParams {

packages/neuron-ui/src/widgets/MnemonicInput/index.tsx

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ const MnemonicInput = ({
99
inputsWords,
1010
onChangeInputWord,
1111
blankIndexes,
12+
wordsCount,
1213
}: {
1314
disabled?: boolean
1415
words: string
@@ -23,10 +24,13 @@ const MnemonicInput = ({
2324
}
2425
}
2526
) => void
26-
27+
wordsCount?: number
2728
blankIndexes?: number[]
2829
}) => {
29-
const wordList = useMemo(() => Object.assign(new Array(12).fill(''), words?.split(' ')), [words])
30+
const wordList = useMemo(
31+
() => Object.assign(new Array(wordsCount ?? 12).fill(''), words?.split(' ')),
32+
[words, wordsCount]
33+
)
3034
const [focusIndex, setFocusIndex] = useState(-1)
3135
const mounted = useRef(true)
3236
const root = useRef<HTMLDivElement>(null)

packages/neuron-wallet/src/controllers/api.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -368,9 +368,12 @@ export default class ApiController {
368368
return this.#walletsController.activate(id)
369369
})
370370

371-
handle('import-mnemonic', async (_, params: { name: string; password: string; mnemonic: string }) => {
372-
return this.#walletsController.importMnemonic(params)
373-
})
371+
handle(
372+
'import-mnemonic',
373+
async (_, params: { name: string; password: string; mnemonic: string; isHardware?: boolean }) => {
374+
return this.#walletsController.importMnemonic(params)
375+
}
376+
)
374377

375378
handle('import-keystore', async (_, params: { name: string; password: string; keystorePath: string }) => {
376379
return this.#walletsController.importKeystore(params)

packages/neuron-wallet/src/controllers/app/menu.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,13 @@ const updateApplicationMenu = (mainWindow: BrowserWindow | null) => {
231231
importHardware(URL.ImportHardware)
232232
},
233233
},
234+
{
235+
id: 'import-hardware-seed',
236+
label: t('application-menu.wallet.import-hardware-mnemonic'),
237+
click: () => {
238+
importHardware(`${URL.ImportMnemonic}?isHardware=true`)
239+
},
240+
},
234241
],
235242
},
236243
separator,

packages/neuron-wallet/src/controllers/wallets.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,12 +70,14 @@ export default class WalletsController {
7070
name,
7171
password,
7272
mnemonic,
73+
isHardware,
7374
}: {
7475
name: string
7576
password: string
7677
mnemonic: string
78+
isHardware?: boolean
7779
}): Promise<Controller.Response<Omit<WalletProperties, 'extendedKey'>>> {
78-
return await this.createByMnemonic({ name, password, mnemonic, isImporting: true })
80+
return await this.createByMnemonic({ name, password, mnemonic, isImporting: true, isHardware })
7981
}
8082

8183
public async create({
@@ -95,11 +97,13 @@ export default class WalletsController {
9597
password,
9698
mnemonic,
9799
isImporting,
100+
isHardware,
98101
}: {
99102
name: string
100103
password: string
101104
mnemonic: string
102105
isImporting: boolean
106+
isHardware?: boolean
103107
}): Promise<Controller.Response<Omit<WalletProperties, 'extendedKey'>>> {
104108
if (!validateMnemonic(mnemonic)) {
105109
throw new InvalidMnemonic()
@@ -139,6 +143,7 @@ export default class WalletsController {
139143
extendedKey: accountExtendedPublicKey.serialize(),
140144
keystore,
141145
startBlockNumber: startBlockNumber,
146+
hardwareFromSeed: isHardware,
142147
})
143148

144149
wallet.checkAndGenerateAddresses(isImporting)

packages/neuron-wallet/src/locales/en.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ export default {
2525
'import-keystore': 'Import from Keystore',
2626
'import-xpubkey': 'Import Extended Public Key',
2727
'import-hardware': 'Import Hardware Wallet',
28+
'import-hardware-mnemonic': 'Import Hardware Wallet Seed',
2829
},
2930
edit: {
3031
label: 'Edit',

0 commit comments

Comments
 (0)