From 5ded194d859818f5291c60ce9180e23d4249102d Mon Sep 17 00:00:00 2001 From: devchenyan Date: Fri, 7 Mar 2025 21:30:35 +0800 Subject: [PATCH 1/2] fix: HardWallet connection tip --- .../src/components/CellManagement/hooks.ts | 24 ++++++++++++++----- .../src/components/HardwareSign/hooks.ts | 15 ++++++++---- .../Receive/VerifyHardwareAddress.tsx | 21 +++++++++++----- packages/neuron-ui/src/exceptions/hardware.ts | 10 ++++++++ packages/neuron-ui/src/locales/ar.json | 1 + packages/neuron-ui/src/locales/en.json | 1 + packages/neuron-ui/src/locales/es.json | 1 + packages/neuron-ui/src/locales/fr.json | 1 + packages/neuron-ui/src/locales/zh-tw.json | 1 + packages/neuron-ui/src/locales/zh.json | 1 + .../neuron-ui/src/services/remote/hardware.ts | 2 +- packages/neuron-ui/src/utils/enums.ts | 1 + packages/neuron-wallet/src/controllers/api.ts | 4 ++-- .../neuron-wallet/src/controllers/hardware.ts | 14 +++++++++-- .../neuron-wallet/src/exceptions/hardware.ts | 4 ++++ 15 files changed, 79 insertions(+), 22 deletions(-) diff --git a/packages/neuron-ui/src/components/CellManagement/hooks.ts b/packages/neuron-ui/src/components/CellManagement/hooks.ts index 8f1eecc658..23becbee88 100644 --- a/packages/neuron-ui/src/components/CellManagement/hooks.ts +++ b/packages/neuron-ui/src/components/CellManagement/hooks.ts @@ -1,4 +1,4 @@ -import { CkbAppNotFoundException, DeviceNotFoundException } from 'exceptions' +import { CkbAppNotFoundException, DeviceNotFoundException, DeviceNotMatchWalletException } from 'exceptions' import { TFunction } from 'i18next' import { useCallback, useEffect, useMemo, useState } from 'react' import { useNavigate } from 'react-router-dom' @@ -415,7 +415,11 @@ export const useHardWallet = ({ wallet, t }: { wallet: State.WalletIdentity; t: }, []) const [error, setError] = useState() const isNotAvailable = useMemo(() => { - return error === ErrorCode.DeviceNotFound || error === ErrorCode.CkbAppNotFound + return ( + error === ErrorCode.DeviceNotFound || + error === ErrorCode.CkbAppNotFound || + error === ErrorCode.DeviceNotMatchWallet + ) }, [error]) const [deviceInfo, setDeviceInfo] = useState(wallet.device) @@ -424,14 +428,16 @@ export const useHardWallet = ({ wallet, t }: { wallet: State.WalletIdentity; t: const ensureDeviceAvailable = useCallback( async (device: State.DeviceInfo) => { try { - const connectionRes = await connectDevice(device) + const connectionRes = await connectDevice({ ...device, walletID: wallet.id }) let { descriptor } = device if (!isSuccessResponse(connectionRes)) { // for win32, opening or closing the ckb app changes the HID descriptor(deviceInfo), // so if we can't connect to the device, we need to re-search device automatically. // for unix, the descriptor never changes unless user plugs the device into another USB port, // in that case, mannauly re-search device one time will do. - if (isWin32) { + if (connectionRes.status === ErrorCode.DeviceNotMatchWallet) { + throw new DeviceNotMatchWalletException() + } else if (isWin32) { setIsReconnecting(true) const devicesRes = await getDevices(device) setIsReconnecting(false) @@ -467,13 +473,17 @@ export const useHardWallet = ({ wallet, t }: { wallet: State.WalletIdentity; t: setError(undefined) return true } catch (err) { - if (err instanceof CkbAppNotFoundException || err instanceof DeviceNotFoundException) { + if ( + err instanceof CkbAppNotFoundException || + err instanceof DeviceNotFoundException || + err instanceof DeviceNotMatchWalletException + ) { setError(err.code) } return false } }, - [isWin32] + [isWin32, wallet] ) const reconnect = useCallback(async () => { @@ -515,6 +525,8 @@ export const useHardWallet = ({ wallet, t }: { wallet: State.WalletIdentity; t: return t('hardware-verify-address.status.disconnect') case ErrorCode.CkbAppNotFound: return t(CkbAppNotFoundException.message) + case ErrorCode.DeviceNotMatchWallet: + return t(DeviceNotMatchWalletException.message) default: return error } diff --git a/packages/neuron-ui/src/components/HardwareSign/hooks.ts b/packages/neuron-ui/src/components/HardwareSign/hooks.ts index 2ccc38400b..c3ad29f369 100644 --- a/packages/neuron-ui/src/components/HardwareSign/hooks.ts +++ b/packages/neuron-ui/src/components/HardwareSign/hooks.ts @@ -1,4 +1,4 @@ -import { CkbAppNotFoundException, DeviceNotFoundException } from 'exceptions' +import { CkbAppNotFoundException, DeviceNotFoundException, DeviceNotMatchWalletException } from 'exceptions' import { useCallback, useMemo, useState } from 'react' import { useTranslation } from 'react-i18next' import { useNavigate } from 'react-router-dom' @@ -69,9 +69,10 @@ export default ({ const userInputStatus = t('hardware-sign.status.user-input') const disconnectStatus = t('hardware-sign.status.disconnect') const ckbAppNotFoundStatus = t(CkbAppNotFoundException.message) + const deviceNotMatchWalletStatus = t(DeviceNotMatchWalletException.message) const isNotAvailableToSign = useMemo(() => { - return status === disconnectStatus || status === ckbAppNotFoundStatus - }, [status, disconnectStatus, ckbAppNotFoundStatus]) + return status === disconnectStatus || status === ckbAppNotFoundStatus || status === deviceNotMatchWalletStatus + }, [status, disconnectStatus, ckbAppNotFoundStatus, deviceNotMatchWalletStatus]) const [error, setError] = useState('') const [deviceInfo, setDeviceInfo] = useState(wallet.device!) const [isReconnecting, setIsReconnecting] = useState(false) @@ -162,14 +163,16 @@ export default ({ const ensureDeviceAvailable = useCallback( async (device: DeviceInfo) => { try { - const connectionRes = await connectDevice(device) + const connectionRes = await connectDevice({ ...device, walletID: wallet.id }) let { descriptor } = device if (!isSuccessResponse(connectionRes)) { // for win32, opening or closing the ckb app changes the HID descriptor(deviceInfo), // so if we can't connect to the device, we need to re-search device automatically. // for unix, the descriptor never changes unless user plugs the device into another USB port, // in that case, mannauly re-search device one time will do. - if (isWin32) { + if (connectionRes.status === ErrorCode.DeviceNotMatchWallet) { + throw new DeviceNotMatchWalletException() + } else if (isWin32) { setIsReconnecting(true) const devicesRes = await getDevices(device) setIsReconnecting(false) @@ -206,6 +209,8 @@ export default ({ } catch (err) { if (err instanceof CkbAppNotFoundException) { setStatus(ckbAppNotFoundStatus) + } else if (err instanceof DeviceNotMatchWalletException) { + setStatus(deviceNotMatchWalletStatus) } else { setStatus(disconnectStatus) } diff --git a/packages/neuron-ui/src/components/Receive/VerifyHardwareAddress.tsx b/packages/neuron-ui/src/components/Receive/VerifyHardwareAddress.tsx index e6bd7284fb..7fcd8896a5 100644 --- a/packages/neuron-ui/src/components/Receive/VerifyHardwareAddress.tsx +++ b/packages/neuron-ui/src/components/Receive/VerifyHardwareAddress.tsx @@ -12,7 +12,7 @@ import { getPlatform, } from 'services/remote' import { ErrorCode, clsx, errorFormatter, isSuccessResponse, addressToAddress, useDidMount } from 'utils' -import { CkbAppNotFoundException, DeviceNotFoundException } from 'exceptions' +import { CkbAppNotFoundException, DeviceNotFoundException, DeviceNotMatchWalletException } from 'exceptions' import Alert from 'widgets/Alert' import styles from './receive.module.scss' @@ -42,9 +42,14 @@ const VerifyHardwareAddress = ({ address, wallet, onClose = () => {} }: VerifyHa const userInputStatus = t('hardware-verify-address.status.user-input') const disconnectStatus = t('hardware-verify-address.status.disconnect') const ckbAppNotFoundStatus = t(CkbAppNotFoundException.message) + const deviceNotMatchWalletStatus = t(DeviceNotMatchWalletException.message) const isNotAvailableToVerify = useMemo(() => { - return status?.message === disconnectStatus || status?.message === ckbAppNotFoundStatus - }, [status, disconnectStatus, ckbAppNotFoundStatus]) + return ( + status?.message === disconnectStatus || + status?.message === ckbAppNotFoundStatus || + status?.message === deviceNotMatchWalletStatus + ) + }, [status, disconnectStatus, ckbAppNotFoundStatus, deviceNotMatchWalletStatus]) const [deviceInfo, setDeviceInfo] = useState(wallet.device!) const [isReconnecting, setIsReconnecting] = useState(false) @@ -55,14 +60,16 @@ const VerifyHardwareAddress = ({ address, wallet, onClose = () => {} }: VerifyHa const ensureDeviceAvailable = useCallback( async (device: DeviceInfo) => { try { - const connectionRes = await connectDevice(device) + const connectionRes = await connectDevice({ ...device, walletID: wallet.id }) let { descriptor } = device if (!isSuccessResponse(connectionRes)) { // for win32, opening or closing the ckb app changes the HID descriptor(deviceInfo), // so if we can't connect to the device, we need to re-search device automatically. // for unix, the descriptor never changes unless user plugs the device into another USB port, // in that case, mannauly re-search device one time will do. - if (isWin32) { + if (connectionRes.status === ErrorCode.DeviceNotMatchWallet) { + throw new DeviceNotMatchWalletException() + } else if (isWin32) { setIsReconnecting(true) const devicesRes = await getDevices(device) setIsReconnecting(false) @@ -99,12 +106,14 @@ const VerifyHardwareAddress = ({ address, wallet, onClose = () => {} }: VerifyHa } catch (err) { if (err instanceof CkbAppNotFoundException) { setStatus({ type: 'error', message: ckbAppNotFoundStatus }) + } else if (err instanceof DeviceNotMatchWalletException) { + setStatus({ type: 'error', message: deviceNotMatchWalletStatus }) } else { setStatus({ type: 'error', message: disconnectStatus }) } } }, - [disconnectStatus, ckbAppNotFoundStatus, isWin32] + [disconnectStatus, ckbAppNotFoundStatus, isWin32, wallet] ) const reconnect = useCallback(async () => { diff --git a/packages/neuron-ui/src/exceptions/hardware.ts b/packages/neuron-ui/src/exceptions/hardware.ts index aa6e6c2306..23f3480ff0 100644 --- a/packages/neuron-ui/src/exceptions/hardware.ts +++ b/packages/neuron-ui/src/exceptions/hardware.ts @@ -35,3 +35,13 @@ export class MultiDeviceException extends Error { super(`${I18N_PATH}${ErrorCode.MultiDevice}`) } } + +export class DeviceNotMatchWalletException extends Error { + public code = ErrorCode.DeviceNotMatchWallet + + static message = `${I18N_PATH}${ErrorCode.DeviceNotMatchWallet}` + + constructor() { + super(DeviceNotMatchWalletException.message) + } +} diff --git a/packages/neuron-ui/src/locales/ar.json b/packages/neuron-ui/src/locales/ar.json index 4ec74ff0c9..6e979ac5df 100644 --- a/packages/neuron-ui/src/locales/ar.json +++ b/packages/neuron-ui/src/locales/ar.json @@ -717,6 +717,7 @@ "402": "تطبيق CKB غير مفتوح. يرجى فتح تطبيق CKB على جهازك.", "403": "لم يتم اكتشاف أي جهاز. يرجى توصيل جهازك", "404": "تم اكتشاف أجهزة متعددة. يمكن توصيل جهاز واحد فقط من نفس الطراز.", + "408": "المحفظة الصلبة المتصلة حاليًا لا تتطابق مع المحفظة الحالية.", "600": "يرجى التأكد من أن المزامنة قد انتهت قبل القيام بأي عملية متعلقة بالمعاملات." } }, diff --git a/packages/neuron-ui/src/locales/en.json b/packages/neuron-ui/src/locales/en.json index 5749401afa..d943e24dcc 100644 --- a/packages/neuron-ui/src/locales/en.json +++ b/packages/neuron-ui/src/locales/en.json @@ -717,6 +717,7 @@ "402": "CKB App does not open. Please open the CKB App on your device.", "403": "No device detected. Please connect your device", "404": "Multiple device detected. Only one device of the same model can be connected.", + "408": "The hardware wallet currently connected does not match the current wallet.", "600": "Please make sure the sync has finalized before you do any transaction related operation." } }, diff --git a/packages/neuron-ui/src/locales/es.json b/packages/neuron-ui/src/locales/es.json index 9ee79cd2d2..38179b3161 100644 --- a/packages/neuron-ui/src/locales/es.json +++ b/packages/neuron-ui/src/locales/es.json @@ -700,6 +700,7 @@ "402": "La aplicación CKB no se abre. Abra la aplicación CKB en su dispositivo.", "403": "No se detectó ningún dispositivo. Por favor conecte su dispositivo", "404": "Se detectaron varios dispositivos. Sólo se puede conectar un dispositivo del mismo modelo.", + "408": "La billetera de hardware conectada actualmente no coincide con la billetera actual.", "600": "Asegúrese de que la sincronización haya finalizado antes de realizar cualquier operación relacionada con transacciones." } }, diff --git a/packages/neuron-ui/src/locales/fr.json b/packages/neuron-ui/src/locales/fr.json index 209bd5e28a..31e566bc62 100644 --- a/packages/neuron-ui/src/locales/fr.json +++ b/packages/neuron-ui/src/locales/fr.json @@ -707,6 +707,7 @@ "402": "L'application CKB n'est pas ouverte. Veuillez ouvrir l'application CKB sur votre appareil.", "403": "Aucun appareil détecté. Veuillez connecter votre appareil", "404": "Plusieurs appareils détectés. Un seul appareil du même modèle peut être connecté.", + "408": "Le portefeuille matériel actuellement connecté ne correspond pas au portefeuille actuel.", "600": "Veuillez vous assurer que la synchronisation est finalisée avant d'effectuer toute opération liée à une transaction." } }, diff --git a/packages/neuron-ui/src/locales/zh-tw.json b/packages/neuron-ui/src/locales/zh-tw.json index cc5bfc12e2..8018cb5f86 100644 --- a/packages/neuron-ui/src/locales/zh-tw.json +++ b/packages/neuron-ui/src/locales/zh-tw.json @@ -711,6 +711,7 @@ "402": "CKB 應用未打開。請在妳的設備打開 CKB 應用。", "403": "未檢測到設備,請檢查妳的設備連接", "404": "檢測到多個設備,同一型號的設備只能同時連接一個。", + "408": "當前連接的硬體錢包與當前錢包不匹配。", "600": "做任何交易相關的錢包操作前請先確保同步已完成。" } }, diff --git a/packages/neuron-ui/src/locales/zh.json b/packages/neuron-ui/src/locales/zh.json index 87d3c553c7..9b958cde38 100644 --- a/packages/neuron-ui/src/locales/zh.json +++ b/packages/neuron-ui/src/locales/zh.json @@ -710,6 +710,7 @@ "402": "CKB 应用未打开。请在你的设备打开 CKB 应用。", "403": "未检测到设备,请检查你的设备连接", "404": "检查到多个设备,同一型号的设备只能同时连接一个。", + "408": "当前连接的硬件钱包与当前钱包不匹配。", "600": "做任何交易相关的钱包操作前请先确保同步已完成。" } }, diff --git a/packages/neuron-ui/src/services/remote/hardware.ts b/packages/neuron-ui/src/services/remote/hardware.ts index 9a7f933ccc..21977d628b 100644 --- a/packages/neuron-ui/src/services/remote/hardware.ts +++ b/packages/neuron-ui/src/services/remote/hardware.ts @@ -34,7 +34,7 @@ export const getDevices = remoteApi('detect-device') export const getDeviceCkbAppVersion = remoteApi('get-device-ckb-app-version') export const getDeviceExtendedPublickey = remoteApi('get-device-extended-public-key') export const getDevicePublicKey = remoteApi('get-device-public-key') -export const connectDevice = remoteApi('connect-device') +export const connectDevice = remoteApi('connect-device') export const createHardwareWallet = remoteApi( 'create-hardware-wallet' ) diff --git a/packages/neuron-ui/src/utils/enums.ts b/packages/neuron-ui/src/utils/enums.ts index 2ae3a13711..15263e9bc4 100644 --- a/packages/neuron-ui/src/utils/enums.ts +++ b/packages/neuron-ui/src/utils/enums.ts @@ -107,6 +107,7 @@ export enum ErrorCode { UnknownError = 405, SignMessageFailed = 406, UnsupportedManufacturer = 407, + DeviceNotMatchWallet = 408, // offline DeviceInSleep = 501, // active warning diff --git a/packages/neuron-wallet/src/controllers/api.ts b/packages/neuron-wallet/src/controllers/api.ts index 914ef68464..777ded8338 100644 --- a/packages/neuron-wallet/src/controllers/api.ts +++ b/packages/neuron-wallet/src/controllers/api.ts @@ -945,8 +945,8 @@ export default class ApiController { }) // Hardware wallet - handle('connect-device', async (_, deviceInfo: DeviceInfo) => { - await this.#hardwareController.connectDevice(deviceInfo) + handle('connect-device', async (_, params: DeviceInfo & { walletID?: string }) => { + await this.#hardwareController.connectDevice(params) }) handle('detect-device', async (_, model: Pick) => { diff --git a/packages/neuron-wallet/src/controllers/hardware.ts b/packages/neuron-wallet/src/controllers/hardware.ts index 563fe58c89..44697e0020 100644 --- a/packages/neuron-wallet/src/controllers/hardware.ts +++ b/packages/neuron-wallet/src/controllers/hardware.ts @@ -1,11 +1,13 @@ import { DeviceInfo, ExtendedPublicKey, PublicKey } from '../services/hardware/common' import { ResponseCode } from '../utils/const' import HardwareWalletService from '../services/hardware' -import { connectDeviceFailed } from '../exceptions' +import WalletService from '../services/wallets' +import { connectDeviceFailed, DeviceNotMatchWallet } from '../exceptions' import { hd } from '@ckb-lumos/lumos' export default class HardwareController { - public async connectDevice(deviceInfo: DeviceInfo): Promise> { + public async connectDevice(params: DeviceInfo & { walletID?: string }): Promise> { + const { walletID, ...deviceInfo } = params const device = await HardwareWalletService.getInstance().initHardware(deviceInfo) try { await device!.connect() @@ -13,6 +15,14 @@ export default class HardwareController { throw new connectDeviceFailed(error.message) } + if (walletID) { + const walletPK = WalletService.getInstance().get(walletID).accountExtendedPublicKey() + const devicePK = await device.getExtendedPublicKey() + if (!walletPK.publicKey.includes(devicePK.publicKey)) { + throw new DeviceNotMatchWallet() + } + } + return { status: ResponseCode.Success, } diff --git a/packages/neuron-wallet/src/exceptions/hardware.ts b/packages/neuron-wallet/src/exceptions/hardware.ts index eeb3b88c28..d002f1dddf 100644 --- a/packages/neuron-wallet/src/exceptions/hardware.ts +++ b/packages/neuron-wallet/src/exceptions/hardware.ts @@ -19,3 +19,7 @@ export class UnsupportedManufacturer extends Error { super(t('messages.unsupported-manufacturer', { manufacturer })) } } + +export class DeviceNotMatchWallet extends Error { + public code = 408 +} From f38cc9f4519fe7d7f00f1fbf735ab90ec51d95f1 Mon Sep 17 00:00:00 2001 From: devchenyan Date: Mon, 10 Mar 2025 19:48:08 +0800 Subject: [PATCH 2/2] fix: comments --- packages/neuron-ui/src/components/CellManagement/hooks.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/neuron-ui/src/components/CellManagement/hooks.ts b/packages/neuron-ui/src/components/CellManagement/hooks.ts index 23becbee88..10bba2aa96 100644 --- a/packages/neuron-ui/src/components/CellManagement/hooks.ts +++ b/packages/neuron-ui/src/components/CellManagement/hooks.ts @@ -416,9 +416,8 @@ export const useHardWallet = ({ wallet, t }: { wallet: State.WalletIdentity; t: const [error, setError] = useState() const isNotAvailable = useMemo(() => { return ( - error === ErrorCode.DeviceNotFound || - error === ErrorCode.CkbAppNotFound || - error === ErrorCode.DeviceNotMatchWallet + typeof error === 'number' && + [ErrorCode.DeviceNotFound, ErrorCode.CkbAppNotFound, ErrorCode.DeviceNotMatchWallet].includes(error) ) }, [error])