Skip to content

Commit

Permalink
feat: Support import hardware wallet 24-bit seed
Browse files Browse the repository at this point in the history
  • Loading branch information
yanguoyu committed Dec 11, 2024
1 parent 483a289 commit 1ad4164
Show file tree
Hide file tree
Showing 16 changed files with 131 additions and 28 deletions.
5 changes: 3 additions & 2 deletions packages/neuron-ui/src/components/Receive/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -90,8 +90,9 @@ export const AddressQrCodeWithCopyZone = ({
const Receive = ({ onClose, address }: { onClose?: () => void; address?: string }) => {
const [t] = useTranslation()
const { wallet } = useGlobalState()
const { addresses } = wallet
const { addresses, isHD } = wallet
const isSingleAddress = addresses.length === 1
const isHardwareWallet = !isHD && isSingleAddress

const accountAddress = useMemo(() => {
if (isSingleAddress) {
Expand Down Expand Up @@ -128,7 +129,7 @@ const Receive = ({ onClose, address }: { onClose?: () => void; address?: string
onClick={() => setIsInShortFormat(is => !is)}
/>

{isSingleAddress && <VerifyHardwareAddress address={accountAddress} wallet={wallet} onClose={onClose} />}
{isHardwareWallet && <VerifyHardwareAddress address={accountAddress} wallet={wallet} onClose={onClose} />}
</div>
</Dialog>
)
Expand Down
9 changes: 6 additions & 3 deletions packages/neuron-ui/src/components/WalletWizard/hooks.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { useState, useCallback } from 'react'
import { useState, useCallback, useEffect } from 'react'

const MNEMONIC_SENTENCE_WORDS = 12

export const useInputWords = () => {
const [inputsWords, setInputsWords] = useState<string[]>(new Array(MNEMONIC_SENTENCE_WORDS).fill(''))
export const useInputWords = (wordsCount?: number) => {
const [inputsWords, setInputsWords] = useState<string[]>(new Array(wordsCount ?? MNEMONIC_SENTENCE_WORDS).fill(''))
const onChangeInput = useCallback(
(
e:
Expand Down Expand Up @@ -37,6 +37,9 @@ export const useInputWords = () => {
},
[setInputsWords]
)
useEffect(() => {
setInputsWords(new Array(wordsCount ?? MNEMONIC_SENTENCE_WORDS).fill(''))
}, [wordsCount])
return {
inputsWords,
onChangeInput,
Expand Down
18 changes: 16 additions & 2 deletions packages/neuron-ui/src/components/WalletWizard/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ const initState: WithWizardState = {
password: '',
confirmPassword: '',
name: '',
isHardware: false,
}

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

Welcome.displayName = 'Welcome'

const LEDGER_WORDS_COUNT = 24

const Mnemonic = ({ state = initState, rootPath = '/wizard/', dispatch }: WizardElementProps) => {
const { generated, imported } = state
const navigate = useNavigate()
Expand All @@ -193,8 +196,9 @@ const Mnemonic = ({ state = initState, rootPath = '/wizard/', dispatch }: Wizard
[MnemonicAction.Verify]: 'wizard.replenish-your-seed',
[MnemonicAction.Import]: 'wizard.input-your-seed',
}[type]
const { inputsWords, onChangeInput, setInputsWords } = useInputWords()
const [searchParams] = useSearchParams()
const isHardware = searchParams.get('isHardware') === 'true'
const { inputsWords, onChangeInput, setInputsWords } = useInputWords(isHardware ? LEDGER_WORDS_COUNT : undefined)
const disableNext =
(type === MnemonicAction.Import && inputsWords.some(v => !v)) ||
(type === MnemonicAction.Verify && generated !== inputsWords.join(' '))
Expand Down Expand Up @@ -228,6 +232,14 @@ const Mnemonic = ({ state = initState, rootPath = '/wizard/', dispatch }: Wizard
})
}
}, [dispatch, type, navigate, setBlankIndexes])
useEffect(() => {
if (isHardware) {
dispatch({
type: 'isHardware',
payload: true,
})
}
}, [dispatch, isHardware])

const globalDispatch = useDispatch()

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

const Submission = ({ state = initState, wallets = [], dispatch }: WizardElementProps) => {
const { name, password, confirmPassword, imported } = state
const { name, password, confirmPassword, imported, isHardware } = state
const navigate = useNavigate()
const { type = MnemonicAction.Create } = useParams<{ type: MnemonicAction }>()
const [t] = useTranslation()
Expand Down Expand Up @@ -396,6 +409,7 @@ const Submission = ({ state = initState, wallets = [], dispatch }: WizardElement
name,
password,
mnemonic: imported,
isHardware,
}
openDialog()
setTimeout(() => {
Expand Down
12 changes: 9 additions & 3 deletions packages/neuron-ui/src/components/withWizard/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,14 @@ export interface Element {
comp: React.FC<any>
}

export interface WithWizardState {
[key: string]: string
export type WithWizardState = {
generated: string
imported: string
password: string
confirmPassword: string
name: string
isHardware: boolean
[propName: string]: any
}

export interface WizardProps {
Expand All @@ -27,7 +33,7 @@ export interface WizardElementProps {
}

const reducer = (
state: { [key: string]: string },
state: WithWizardState,
{
type,
payload,
Expand Down
1 change: 1 addition & 0 deletions packages/neuron-ui/src/types/Controller/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ declare namespace Controller {
name: string
mnemonic: string
password: string
isHardware?: boolean
}

interface ImportKeystoreParams {
Expand Down
8 changes: 6 additions & 2 deletions packages/neuron-ui/src/widgets/MnemonicInput/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ const MnemonicInput = ({
inputsWords,
onChangeInputWord,
blankIndexes,
wordsCount,
}: {
disabled?: boolean
words: string
Expand All @@ -23,10 +24,13 @@ const MnemonicInput = ({
}
}
) => void

wordsCount?: number
blankIndexes?: number[]
}) => {
const wordList = useMemo(() => Object.assign(new Array(12).fill(''), words?.split(' ')), [words])
const wordList = useMemo(
() => Object.assign(new Array(wordsCount ?? 12).fill(''), words?.split(' ')),
[words, wordsCount]
)
const [focusIndex, setFocusIndex] = useState(-1)
const mounted = useRef(true)
const root = useRef<HTMLDivElement>(null)
Expand Down
9 changes: 6 additions & 3 deletions packages/neuron-wallet/src/controllers/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -368,9 +368,12 @@ export default class ApiController {
return this.#walletsController.activate(id)
})

handle('import-mnemonic', async (_, params: { name: string; password: string; mnemonic: string }) => {
return this.#walletsController.importMnemonic(params)
})
handle(
'import-mnemonic',
async (_, params: { name: string; password: string; mnemonic: string; isHardware?: boolean }) => {
return this.#walletsController.importMnemonic(params)
}
)

handle('import-keystore', async (_, params: { name: string; password: string; keystorePath: string }) => {
return this.#walletsController.importKeystore(params)
Expand Down
7 changes: 7 additions & 0 deletions packages/neuron-wallet/src/controllers/app/menu.ts
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,13 @@ const updateApplicationMenu = (mainWindow: BrowserWindow | null) => {
importHardware(URL.ImportHardware)
},
},
{
id: 'import-hardware-seed',
label: t('application-menu.wallet.import-hardware-mnemonic'),
click: () => {
importHardware(`${URL.ImportMnemonic}?isHardware=true`)
},
},
],
},
separator,
Expand Down
7 changes: 6 additions & 1 deletion packages/neuron-wallet/src/controllers/wallets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,12 +70,14 @@ export default class WalletsController {
name,
password,
mnemonic,
isHardware,
}: {
name: string
password: string
mnemonic: string
isHardware?: boolean
}): Promise<Controller.Response<Omit<WalletProperties, 'extendedKey'>>> {
return await this.createByMnemonic({ name, password, mnemonic, isImporting: true })
return await this.createByMnemonic({ name, password, mnemonic, isImporting: true, isHardware })
}

public async create({
Expand All @@ -95,11 +97,13 @@ export default class WalletsController {
password,
mnemonic,
isImporting,
isHardware,
}: {
name: string
password: string
mnemonic: string
isImporting: boolean
isHardware?: boolean
}): Promise<Controller.Response<Omit<WalletProperties, 'extendedKey'>>> {
if (!validateMnemonic(mnemonic)) {
throw new InvalidMnemonic()
Expand Down Expand Up @@ -139,6 +143,7 @@ export default class WalletsController {
extendedKey: accountExtendedPublicKey.serialize(),
keystore,
startBlockNumber: startBlockNumber,
hardwareFromSeed: isHardware,
})

wallet.checkAndGenerateAddresses(isImporting)
Expand Down
1 change: 1 addition & 0 deletions packages/neuron-wallet/src/locales/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ export default {
'import-keystore': 'Import from Keystore',
'import-xpubkey': 'Import Extended Public Key',
'import-hardware': 'Import Hardware Wallet',
'import-hardware-mnemonic': 'Import Hardware Wallet Seed',
},
edit: {
label: 'Edit',
Expand Down
1 change: 1 addition & 0 deletions packages/neuron-wallet/src/locales/es.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ export default {
'import-keystore': 'Importar desde Keystore',
'import-xpubkey': 'Importar Clave Pública Extendida',
'import-hardware': 'Importar Billetera de Hardware',
'import-hardware-mnemonic': 'Importar semilla de billetera de hardware',
},
edit: {
label: 'Editar',
Expand Down
1 change: 1 addition & 0 deletions packages/neuron-wallet/src/locales/fr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ export default {
'import-keystore': 'Importer depuis le fichier Keystore',
'import-xpubkey': 'Importer la clé publique étendue',
'import-hardware': 'Importer un Wallet matériel',
'import-hardware-mnemonic': 'Importer des semences de portefeuille matériel',
},
edit: {
label: 'Édition',
Expand Down
1 change: 1 addition & 0 deletions packages/neuron-wallet/src/locales/zh-tw.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ export default {
'import-keystore': '導入 Keystore 檔案',
'import-xpubkey': '導入 Extended Public Key',
'import-hardware': '導入硬體錢包',
'import-hardware-mnemonic': '導入硬體錢包助記詞',
},
edit: {
label: '編輯',
Expand Down
1 change: 1 addition & 0 deletions packages/neuron-wallet/src/locales/zh.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ export default {
'import-keystore': '导入 Keystore 文件',
'import-xpubkey': '导入 Extended Public Key',
'import-hardware': '导入硬件钱包',
'import-hardware-mnemonic': '导入硬件钱包助记词',
},
edit: {
label: '编辑',
Expand Down
4 changes: 4 additions & 0 deletions packages/neuron-wallet/src/models/keys/hd-public-key-info.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { scriptToAddress } from '../../utils/scriptAndAddress'
import SystemScriptInfo from '../../models/system-script-info'
import NetworksService from '../../services/networks'

export const ROOT_ADDRESS_INDEX = -1
export default class HdPublicKeyInfoModel {
public walletId: string
public addressType: hd.AddressType
Expand All @@ -22,6 +23,9 @@ export default class HdPublicKeyInfoModel {
}

public get path(): string {
if (this.addressIndex === ROOT_ADDRESS_INDEX) {
return hd.AccountExtendedPublicKey.ckbAccountPath
}
return hd.AccountExtendedPublicKey.pathFor(this.addressType, this.addressIndex)
}

Expand Down
Loading

0 comments on commit 1ad4164

Please sign in to comment.