@@ -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()
@@ -396,6 +409,7 @@ const Submission = ({ state = initState, wallets = [], dispatch }: WizardElement
name,
password,
mnemonic: imported,
+ isHardware,
}
openDialog()
setTimeout(() => {
diff --git a/packages/neuron-ui/src/components/withWizard/index.tsx b/packages/neuron-ui/src/components/withWizard/index.tsx
index fa69695cd5..aac353913d 100644
--- a/packages/neuron-ui/src/components/withWizard/index.tsx
+++ b/packages/neuron-ui/src/components/withWizard/index.tsx
@@ -7,8 +7,14 @@ export interface Element {
comp: React.FC
}
-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 {
@@ -27,7 +33,7 @@ export interface WizardElementProps {
}
const reducer = (
- state: { [key: string]: string },
+ state: WithWizardState,
{
type,
payload,
diff --git a/packages/neuron-ui/src/types/Controller/index.d.ts b/packages/neuron-ui/src/types/Controller/index.d.ts
index 380129f808..6ae6e86a0b 100644
--- a/packages/neuron-ui/src/types/Controller/index.d.ts
+++ b/packages/neuron-ui/src/types/Controller/index.d.ts
@@ -27,6 +27,7 @@ declare namespace Controller {
name: string
mnemonic: string
password: string
+ isHardware?: boolean
}
interface ImportKeystoreParams {
diff --git a/packages/neuron-ui/src/widgets/MnemonicInput/index.tsx b/packages/neuron-ui/src/widgets/MnemonicInput/index.tsx
index 3830b686de..a2d2e39adb 100644
--- a/packages/neuron-ui/src/widgets/MnemonicInput/index.tsx
+++ b/packages/neuron-ui/src/widgets/MnemonicInput/index.tsx
@@ -9,6 +9,7 @@ const MnemonicInput = ({
inputsWords,
onChangeInputWord,
blankIndexes,
+ wordsCount,
}: {
disabled?: boolean
words: string
@@ -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(null)
diff --git a/packages/neuron-wallet/src/controllers/api.ts b/packages/neuron-wallet/src/controllers/api.ts
index f7f75d5b9e..e9ae8c5399 100644
--- a/packages/neuron-wallet/src/controllers/api.ts
+++ b/packages/neuron-wallet/src/controllers/api.ts
@@ -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)
diff --git a/packages/neuron-wallet/src/controllers/app/menu.ts b/packages/neuron-wallet/src/controllers/app/menu.ts
index 1e1b728330..233e09440d 100644
--- a/packages/neuron-wallet/src/controllers/app/menu.ts
+++ b/packages/neuron-wallet/src/controllers/app/menu.ts
@@ -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,
diff --git a/packages/neuron-wallet/src/controllers/wallets.ts b/packages/neuron-wallet/src/controllers/wallets.ts
index a47f3c2015..3f0f874f1a 100644
--- a/packages/neuron-wallet/src/controllers/wallets.ts
+++ b/packages/neuron-wallet/src/controllers/wallets.ts
@@ -70,12 +70,14 @@ export default class WalletsController {
name,
password,
mnemonic,
+ isHardware,
}: {
name: string
password: string
mnemonic: string
+ isHardware?: boolean
}): Promise>> {
- return await this.createByMnemonic({ name, password, mnemonic, isImporting: true })
+ return await this.createByMnemonic({ name, password, mnemonic, isImporting: true, isHardware })
}
public async create({
@@ -95,11 +97,13 @@ export default class WalletsController {
password,
mnemonic,
isImporting,
+ isHardware,
}: {
name: string
password: string
mnemonic: string
isImporting: boolean
+ isHardware?: boolean
}): Promise>> {
if (!validateMnemonic(mnemonic)) {
throw new InvalidMnemonic()
@@ -139,6 +143,7 @@ export default class WalletsController {
extendedKey: accountExtendedPublicKey.serialize(),
keystore,
startBlockNumber: startBlockNumber,
+ hardwareFromSeed: isHardware,
})
wallet.checkAndGenerateAddresses(isImporting)
diff --git a/packages/neuron-wallet/src/locales/en.ts b/packages/neuron-wallet/src/locales/en.ts
index fec1bc382c..b88ae9771b 100644
--- a/packages/neuron-wallet/src/locales/en.ts
+++ b/packages/neuron-wallet/src/locales/en.ts
@@ -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',
diff --git a/packages/neuron-wallet/src/locales/es.ts b/packages/neuron-wallet/src/locales/es.ts
index 9d70452efd..efc5e8987e 100644
--- a/packages/neuron-wallet/src/locales/es.ts
+++ b/packages/neuron-wallet/src/locales/es.ts
@@ -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',
diff --git a/packages/neuron-wallet/src/locales/fr.ts b/packages/neuron-wallet/src/locales/fr.ts
index 58e11e3d3a..eb5f33454d 100644
--- a/packages/neuron-wallet/src/locales/fr.ts
+++ b/packages/neuron-wallet/src/locales/fr.ts
@@ -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',
diff --git a/packages/neuron-wallet/src/locales/zh-tw.ts b/packages/neuron-wallet/src/locales/zh-tw.ts
index cc75c175f4..33a4887216 100644
--- a/packages/neuron-wallet/src/locales/zh-tw.ts
+++ b/packages/neuron-wallet/src/locales/zh-tw.ts
@@ -25,6 +25,7 @@ export default {
'import-keystore': '導入 Keystore 檔案',
'import-xpubkey': '導入 Extended Public Key',
'import-hardware': '導入硬體錢包',
+ 'import-hardware-mnemonic': '導入硬體錢包助記詞',
},
edit: {
label: '編輯',
diff --git a/packages/neuron-wallet/src/locales/zh.ts b/packages/neuron-wallet/src/locales/zh.ts
index 5200e52497..1f47efa9f8 100644
--- a/packages/neuron-wallet/src/locales/zh.ts
+++ b/packages/neuron-wallet/src/locales/zh.ts
@@ -25,6 +25,7 @@ export default {
'import-keystore': '导入 Keystore 文件',
'import-xpubkey': '导入 Extended Public Key',
'import-hardware': '导入硬件钱包',
+ 'import-hardware-mnemonic': '导入硬件钱包助记词',
},
edit: {
label: '编辑',
diff --git a/packages/neuron-wallet/src/models/keys/hd-public-key-info.ts b/packages/neuron-wallet/src/models/keys/hd-public-key-info.ts
index b8981a636f..d23afffcc5 100644
--- a/packages/neuron-wallet/src/models/keys/hd-public-key-info.ts
+++ b/packages/neuron-wallet/src/models/keys/hd-public-key-info.ts
@@ -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
@@ -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)
}
diff --git a/packages/neuron-wallet/src/services/wallets.ts b/packages/neuron-wallet/src/services/wallets.ts
index 77ac8df4a5..0bdd3d6638 100644
--- a/packages/neuron-wallet/src/services/wallets.ts
+++ b/packages/neuron-wallet/src/services/wallets.ts
@@ -18,6 +18,7 @@ import { NetworkType } from '../models/network'
import { resetSyncTaskQueue } from '../block-sync-renderer'
import SyncProgressService from './sync-progress'
import { prefixWith0x } from '../utils/scriptAndAddress'
+import { ROOT_ADDRESS_INDEX } from '../models/keys/hd-public-key-info'
const fileService = FileService.getInstance()
@@ -31,6 +32,7 @@ export interface WalletProperties {
device?: DeviceInfo
keystore?: hd.Keystore
startBlockNumber?: string
+ hardwareFromSeed?: boolean
}
export abstract class Wallet {
@@ -40,9 +42,10 @@ export abstract class Wallet {
protected extendedKey: string = ''
protected isHD: boolean
protected startBlockNumber?: string
+ protected hardwareFromSeed?: boolean
constructor(props: WalletProperties) {
- const { id, name, extendedKey, device, isHDWallet, startBlockNumber } = props
+ const { id, name, extendedKey, device, isHDWallet, startBlockNumber, hardwareFromSeed } = props
if (id === undefined) {
throw new IsRequired('ID')
@@ -61,6 +64,7 @@ export abstract class Wallet {
this.device = device
this.isHD = isHDWallet ?? true
this.startBlockNumber = startBlockNumber
+ this.hardwareFromSeed = hardwareFromSeed
}
public toJSON = () => ({
@@ -70,6 +74,7 @@ export abstract class Wallet {
device: this.device,
isHD: this.isHD,
startBlockNumber: this.startBlockNumber,
+ hardwareFromSeed: this.hardwareFromSeed,
})
public fromJSON = () => {
@@ -153,17 +158,6 @@ export class FileKeystoreWallet extends Wallet {
return hd.AccountExtendedPublicKey.parse(this.extendedKey)
}
- public toJSON = () => {
- return {
- id: this.id,
- name: this.name,
- extendedKey: this.extendedKey,
- device: this.device,
- isHD: this.isHD,
- startBlockNumber: this.startBlockNumber,
- }
- }
-
public loadKeystore = () => {
const data = fileService.readFileSync(MODULE_NAME, this.keystoreFileName())
return hd.Keystore.fromJson(data)
@@ -221,6 +215,59 @@ export class FileKeystoreWallet extends Wallet {
}
}
+export class HardwareFromSeedWallet extends FileKeystoreWallet {
+ static fromJSON = (json: WalletProperties) => {
+ return new HardwareFromSeedWallet(json)
+ }
+
+ public toJSON = () => {
+ return {
+ id: this.id,
+ name: this.name,
+ extendedKey: this.extendedKey,
+ device: this.device,
+ isHD: this.isHD,
+ startBlockNumber: this.startBlockNumber,
+ hardwareFromSeed: true,
+ }
+ }
+
+ public checkAndGenerateAddresses = async (): Promise => {
+ const { publicKey } = hd.AccountExtendedPublicKey.parse(this.extendedKey)
+ const address = await AddressService.generateAndSaveForPublicKeyQueue.asyncPush({
+ walletId: this.id,
+ publicKey,
+ addressType: hd.AddressType.Receiving,
+ addressIndex: ROOT_ADDRESS_INDEX,
+ })
+
+ if (address) {
+ return [address]
+ }
+ }
+
+ public getNextAddress = async (): Promise => {
+ return AddressService.getFirstAddressByWalletId(this.id)
+ }
+
+ public getNextChangeAddress = async (): Promise => {
+ return AddressService.getFirstAddressByWalletId(this.id)
+ }
+
+ public getNextReceivingAddresses = async (): Promise => {
+ const address = await AddressService.getFirstAddressByWalletId(this.id)
+ if (address) {
+ return [address]
+ }
+
+ return []
+ }
+
+ public getAllAddresses = async (): Promise => {
+ return AddressService.getAddressesByWalletId(this.id)
+ }
+}
+
export class HardwareWallet extends Wallet {
public isHardware = (): boolean => {
return true
@@ -329,6 +376,9 @@ export default class WalletService {
if (json.device) {
return HardwareWallet.fromJSON(json)
}
+ if (json.hardwareFromSeed) {
+ return HardwareFromSeedWallet.fromJSON(json)
+ }
return FileKeystoreWallet.fromJSON(json)
}