From f7e99b87f8eef0773980eaf96f5dde7b531d7164 Mon Sep 17 00:00:00 2001 From: devchenyan Date: Sat, 28 Dec 2024 20:35:23 +0800 Subject: [PATCH 01/10] feat: Ability to display/export the private key --- .../AddressBook/addressBook.module.scss | 14 ++ .../src/components/AddressBook/index.tsx | 23 ++- .../src/components/Receive/index.tsx | 36 +++-- .../components/Receive/receive.module.scss | 49 ++++-- .../src/components/ViewPrivateKey/index.tsx | 145 ++++++++++++++++++ .../ViewPrivateKey/viewPrivateKey.module.scss | 39 +++++ packages/neuron-ui/src/locales/ar.json | 9 +- packages/neuron-ui/src/locales/en.json | 5 +- packages/neuron-ui/src/locales/es.json | 5 +- packages/neuron-ui/src/locales/fr.json | 5 +- packages/neuron-ui/src/locales/zh-tw.json | 5 +- packages/neuron-ui/src/locales/zh.json | 5 +- .../src/services/remote/remoteApiWrapper.ts | 1 + .../neuron-ui/src/services/remote/wallets.ts | 1 + packages/neuron-ui/src/styles/theme.scss | 2 + .../neuron-ui/src/types/Controller/index.d.ts | 6 + .../src/widgets/Icons/PrivateKey.svg | 4 + packages/neuron-ui/src/widgets/Icons/icon.tsx | 2 + .../neuron-ui/src/widgets/TextField/index.tsx | 4 +- packages/neuron-wallet/src/controllers/api.ts | 8 + .../neuron-wallet/src/controllers/wallets.ts | 20 +++ .../neuron-wallet/src/services/addresses.ts | 36 +++++ 22 files changed, 384 insertions(+), 40 deletions(-) create mode 100644 packages/neuron-ui/src/components/ViewPrivateKey/index.tsx create mode 100644 packages/neuron-ui/src/components/ViewPrivateKey/viewPrivateKey.module.scss create mode 100644 packages/neuron-ui/src/widgets/Icons/PrivateKey.svg diff --git a/packages/neuron-ui/src/components/AddressBook/addressBook.module.scss b/packages/neuron-ui/src/components/AddressBook/addressBook.module.scss index 118ab0d4b2..325e953d3e 100644 --- a/packages/neuron-ui/src/components/AddressBook/addressBook.module.scss +++ b/packages/neuron-ui/src/components/AddressBook/addressBook.module.scss @@ -168,6 +168,20 @@ } } +.privateKey { + background: transparent; + border: none; + cursor: pointer; + &:hover { + svg { + g, + path { + stroke: var(--primary-color); + } + } + } +} + @media screen and (max-width: 1330px) { .container { .balance { diff --git a/packages/neuron-ui/src/components/AddressBook/index.tsx b/packages/neuron-ui/src/components/AddressBook/index.tsx index 19c5e5d7c3..947c8675cb 100644 --- a/packages/neuron-ui/src/components/AddressBook/index.tsx +++ b/packages/neuron-ui/src/components/AddressBook/index.tsx @@ -3,7 +3,8 @@ import { useTranslation } from 'react-i18next' import { useState as useGlobalState, useDispatch } from 'states' import Dialog from 'widgets/Dialog' import CopyZone from 'widgets/CopyZone' -import { Copy } from 'widgets/Icons/icon' +import ViewPrivateKey from 'components/ViewPrivateKey' +import { Copy, PrivateKey } from 'widgets/Icons/icon' import Table, { TableProps, SortType } from 'widgets/Table' import { shannonToCKBFormatter, useLocalDescription } from 'utils' import { HIDE_BALANCE } from 'utils/const' @@ -44,6 +45,7 @@ const AddressBook = ({ onClose }: { onClose?: () => void }) => { const dispatch = useDispatch() const { onChangeEditStatus, onSubmitDescription } = useLocalDescription('address', walletId, dispatch) + const [viewPrivateKeyAddress, setViewPrivateKeyAddress] = useState('') const columns = useMemo['columns']>( () => [ @@ -149,6 +151,21 @@ const AddressBook = ({ onClose }: { onClose?: () => void }) => { return 0 }, }, + { + title: '', + dataIndex: 'key', + align: 'left', + width: '40px', + render(_, __, { address }) { + return ( + + + + ) + }, + }, ], [t] ) @@ -179,6 +196,10 @@ const AddressBook = ({ onClose }: { onClose?: () => void }) => { } /> + + {!!viewPrivateKeyAddress && ( + setViewPrivateKeyAddress('')} /> + )} ) diff --git a/packages/neuron-ui/src/components/Receive/index.tsx b/packages/neuron-ui/src/components/Receive/index.tsx index 5387b873d0..a182a1ea2e 100644 --- a/packages/neuron-ui/src/components/Receive/index.tsx +++ b/packages/neuron-ui/src/components/Receive/index.tsx @@ -7,7 +7,8 @@ import Button from 'widgets/Button' import CopyZone from 'widgets/CopyZone' import QRCode from 'widgets/QRCode' import Tooltip from 'widgets/Tooltip' -import { AddressTransform, Download, Copy, Attention, SuccessNoBorder } from 'widgets/Icons/icon' +import ViewPrivateKey from 'components/ViewPrivateKey' +import { AddressTransform, Download, Copy, Attention, SuccessNoBorder, PrivateKey } from 'widgets/Icons/icon' import VerifyHardwareAddress from './VerifyHardwareAddress' import styles from './receive.module.scss' import { useCopyAndDownloadQrCode, useSwitchAddress } from './hooks' @@ -29,6 +30,7 @@ export const AddressQrCodeWithCopyZone = ({ ) const [isCopySuccess, setIsCopySuccess] = useState(false) + const [showViewPrivateKey, setShowViewPrivateKey] = useState(false) const timer = useRef>() const { ref, onCopyQrCode, onDownloadQrCode, showCopySuccess } = useCopyAndDownloadQrCode() @@ -70,19 +72,27 @@ export const AddressQrCodeWithCopyZone = ({ {showAddress} - +
+ + +
+ + {showViewPrivateKey && setShowViewPrivateKey(false)} />} ) } diff --git a/packages/neuron-ui/src/components/Receive/receive.module.scss b/packages/neuron-ui/src/components/Receive/receive.module.scss index 68b6ab7a56..0f9587a1ea 100644 --- a/packages/neuron-ui/src/components/Receive/receive.module.scss +++ b/packages/neuron-ui/src/components/Receive/receive.module.scss @@ -125,26 +125,43 @@ color: var(--main-text-color); } -.addressToggle { - width: 100%; +.actionWrap { margin-top: 8px; - appearance: none; - border: none; - background: none; display: flex; justify-content: center; - align-items: center; - font-size: 12px; - font-family: PingFang SC; - font-style: normal; - font-weight: 500; - color: var(--primary-color); - line-height: normal; - cursor: pointer; + gap: 32px; - svg { - pointer-events: none; - margin-right: 5px; + button { + appearance: none; + border: none; + background: none; + font-size: 12px; + font-family: PingFang SC; + font-style: normal; + font-weight: 500; + color: var(--primary-color); + line-height: normal; + cursor: pointer; + display: flex; + align-items: center; + } + + .addressToggle { + svg { + pointer-events: none; + margin-right: 5px; + } + } + + .privateKey { + svg { + width: 16px; + margin-right: 3px; + g, + path { + stroke: var(--primary-color); + } + } } } diff --git a/packages/neuron-ui/src/components/ViewPrivateKey/index.tsx b/packages/neuron-ui/src/components/ViewPrivateKey/index.tsx new file mode 100644 index 0000000000..b076b8dd1d --- /dev/null +++ b/packages/neuron-ui/src/components/ViewPrivateKey/index.tsx @@ -0,0 +1,145 @@ +import React, { useCallback, useEffect, useState } from 'react' +import { useTranslation } from 'react-i18next' +import { useState as useGlobalState } from 'states' +import Dialog from 'widgets/Dialog' +import TextField from 'widgets/TextField' +import Alert from 'widgets/Alert' +import { errorFormatter, useCopy, isSuccessResponse } from 'utils' +import { Attention, Copy } from 'widgets/Icons/icon' +import { getPrivateKeyByAddress } from 'services/remote' +import styles from './viewPrivateKey.module.scss' + +const ViewPrivateKey = ({ onClose, address }: { onClose?: () => void; address?: string }) => { + const [t] = useTranslation() + const [password, setPassword] = useState('') + const [error, setError] = useState('') + const [privateKey, setPrivateKey] = useState('') + const [isLoading, setIsLoading] = useState(false) + const { copied, onCopy, copyTimes } = useCopy() + const { + wallet: { id: walletID = '' }, + } = useGlobalState() + + useEffect(() => { + setPassword('') + setError('') + }, [setError, setPassword]) + + const onChange = useCallback( + (e: React.SyntheticEvent) => { + const { value } = e.target as HTMLInputElement + setPassword(value) + setError('') + }, + [setPassword, setError] + ) + + const onSubmit = useCallback( + async (e?: React.FormEvent) => { + if (e) { + e.preventDefault() + } + if (!password) { + return + } + setIsLoading(true) + try { + const res = await getPrivateKeyByAddress({ + walletID, + address, + password, + }) + + setIsLoading(false) + + if (!isSuccessResponse(res)) { + setError(errorFormatter(res.message, t)) + return + } + setPrivateKey(res.result) + } catch (err) { + setIsLoading(false) + } + }, + [walletID, password, setError, t] + ) + + if (privateKey) { + return ( + +
+
+ + {t('addresses.view-private-key-tip')} +
+ + {t('addresses.private-key')}} + value={privateKey} + field="password" + type="password" + disabled + suffix={ +
+ onCopy(privateKey)} /> +
+ } + /> + + {copied ? ( + + {t('common.copied')} + + ) : null} +
+
+ ) + } + return ( + +
+
+ + {t('addresses.view-private-key-tip')} +
+ + +
+
+ ) +} + +ViewPrivateKey.displayName = 'ViewPrivateKey' + +export default ViewPrivateKey diff --git a/packages/neuron-ui/src/components/ViewPrivateKey/viewPrivateKey.module.scss b/packages/neuron-ui/src/components/ViewPrivateKey/viewPrivateKey.module.scss new file mode 100644 index 0000000000..c5ecfa4b63 --- /dev/null +++ b/packages/neuron-ui/src/components/ViewPrivateKey/viewPrivateKey.module.scss @@ -0,0 +1,39 @@ +@import '../../styles/mixin.scss'; + +.passwordInput { + margin-top: 16px; +} + +.dialog { + width: 700px; +} + +.tip { + color: var(--warn-text-color); + background: var(--warn-background-color); + margin: -20px -16px 0; + display: flex; + align-items: center; + justify-content: center; + height: 32px; + font-size: 12px; + gap: 4px; + font-weight: 500; + border-bottom: 1px solid var(--warn-border-color); +} + +.label { + font-weight: 500; + color: var(--main-text-color); + font-size: 14px; +} + +.copy { + display: flex; + align-items: center; + margin-left: 6px; +} + +.notice { + @include dialog-copy-animation; +} diff --git a/packages/neuron-ui/src/locales/ar.json b/packages/neuron-ui/src/locales/ar.json index 3703ca6595..5ccbd9fce9 100644 --- a/packages/neuron-ui/src/locales/ar.json +++ b/packages/neuron-ui/src/locales/ar.json @@ -383,7 +383,10 @@ "request-payment": "طلب الدفع", "view-on-explorer": "عرض على المستكشف", "default-description": "لا شيء", - "all-address": "كل العناوين" + "all-address": "كل العناوين", + "view-private-key": "عرض المفتاح الخاص", + "private-key": "المفتاح الخاص", + "view-private-key-tip": "إذا فقدت مفتاحك الخاص، فستفقد أصولك. (عرضه في بيئة آمنة)" }, "settings": { "title": { @@ -822,7 +825,7 @@ "lock-info-dialog": { "address-info": "معلومات العنوان", "deprecated-address": "عنوان مهمل" - }, + }, "updates": { "check-updates": "تحقق من التحديثات", "checking-updates": "جارٍ التحقق...", @@ -1327,4 +1330,4 @@ "verify-wallet": "تحقق" } } -} \ No newline at end of file +} diff --git a/packages/neuron-ui/src/locales/en.json b/packages/neuron-ui/src/locales/en.json index 4c5142990e..51e7787181 100644 --- a/packages/neuron-ui/src/locales/en.json +++ b/packages/neuron-ui/src/locales/en.json @@ -383,7 +383,10 @@ "request-payment": "Request Payment", "view-on-explorer": "View on Explorer", "default-description": "None", - "all-address": "All" + "all-address": "All", + "view-private-key": "View Private Key", + "private-key": "Private Key", + "view-private-key-tip": "If you lose your private key, your assets will be gone. (View in secure environment)" }, "settings": { "title": { diff --git a/packages/neuron-ui/src/locales/es.json b/packages/neuron-ui/src/locales/es.json index 2ba59528b9..5ac87dd864 100644 --- a/packages/neuron-ui/src/locales/es.json +++ b/packages/neuron-ui/src/locales/es.json @@ -375,7 +375,10 @@ "request-payment": "Solicitar Pago", "view-on-explorer": "Ver en el Explorador", "default-description": "Ninguna", - "all-address": "Todas" + "all-address": "Todas", + "view-private-key": "Ver clave privada", + "private-key": "Clave privada", + "view-private-key-tip": "Si pierdes tu clave privada, tus activos desaparecerán. (Ver en un entorno seguro)" }, "settings": { "title": { diff --git a/packages/neuron-ui/src/locales/fr.json b/packages/neuron-ui/src/locales/fr.json index 9973dc4e07..8db344661e 100644 --- a/packages/neuron-ui/src/locales/fr.json +++ b/packages/neuron-ui/src/locales/fr.json @@ -382,7 +382,10 @@ "request-payment": "Demander un paiement", "view-on-explorer": "Voir sur l'explorateur", "default-description": "Aucun", - "all-address": "Toutes" + "all-address": "Toutes", + "view-private-key": "Voir la clé privée", + "private-key": "Clé privée", + "view-private-key-tip": "Si vous perdez votre clé privée, vos actifs seront perdus. (Voir dans un environnement sécurisé)" }, "settings": { "title": { diff --git a/packages/neuron-ui/src/locales/zh-tw.json b/packages/neuron-ui/src/locales/zh-tw.json index 6f69bd6bfd..72f4eb29b4 100644 --- a/packages/neuron-ui/src/locales/zh-tw.json +++ b/packages/neuron-ui/src/locales/zh-tw.json @@ -378,7 +378,10 @@ "request-payment": "請求支付", "view-on-explorer": "在瀏覽器中查看", "default-description": "無", - "all-address": "全部地址" + "all-address": "全部地址", + "view-private-key": "查看私鑰", + "private-key": "私鑰", + "view-private-key-tip": "如果您丟失了私鑰,您的資產將無法找回。(請在安全環境中查看)" }, "settings": { "title": { diff --git a/packages/neuron-ui/src/locales/zh.json b/packages/neuron-ui/src/locales/zh.json index 5f2e16103e..1fe8278e5f 100644 --- a/packages/neuron-ui/src/locales/zh.json +++ b/packages/neuron-ui/src/locales/zh.json @@ -376,7 +376,10 @@ "request-payment": "请求支付", "view-on-explorer": "在浏览器中查看", "default-description": "无", - "all-address": "全部地址" + "all-address": "全部地址", + "view-private-key": "查看私钥", + "private-key": "私钥", + "view-private-key-tip": "如果您丢失了私钥,您的资产将无法找回。(请在安全环境中查看)" }, "settings": { "title": { diff --git a/packages/neuron-ui/src/services/remote/remoteApiWrapper.ts b/packages/neuron-ui/src/services/remote/remoteApiWrapper.ts index c8cb6f0722..ba8703f318 100644 --- a/packages/neuron-ui/src/services/remote/remoteApiWrapper.ts +++ b/packages/neuron-ui/src/services/remote/remoteApiWrapper.ts @@ -69,6 +69,7 @@ type Action = | 'backup-wallet' | 'update-wallet-start-block-number' | 'get-all-addresses' + | 'get-private-key-by-address' | 'update-address-description' | 'request-password' | 'send-tx' diff --git a/packages/neuron-ui/src/services/remote/wallets.ts b/packages/neuron-ui/src/services/remote/wallets.ts index 3f97e5b4ea..0610cb6521 100644 --- a/packages/neuron-ui/src/services/remote/wallets.ts +++ b/packages/neuron-ui/src/services/remote/wallets.ts @@ -14,6 +14,7 @@ export const updateWalletStartBlockNumber = remoteApi('get-all-addresses') +export const getPrivateKeyByAddress = remoteApi('get-private-key-by-address') export const updateAddressDescription = remoteApi('update-address-description') export const requestPassword = remoteApi('request-password') diff --git a/packages/neuron-ui/src/styles/theme.scss b/packages/neuron-ui/src/styles/theme.scss index 1421495687..4ef564a699 100644 --- a/packages/neuron-ui/src/styles/theme.scss +++ b/packages/neuron-ui/src/styles/theme.scss @@ -34,6 +34,7 @@ body { --main-pagination-text-color: #666666; --warn-text-color: #f68c2a; --warn-background-color: #fff6eb; + --warn-border-color: rgba(252, 136, 0, 0.2); --svg-fill-color: #ffffff; --lock-info-title-border: #e5e5e5; --script-tag-background-color: #eafbf6; @@ -89,6 +90,7 @@ body { --main-pagination-text-color: #999999; --warn-text-color: #f68c2a; --warn-background-color: rgba(255, 140, 0, 0.2); + --warn-border-color: rgba(252, 136, 0, 0.2); --svg-fill-color: #333333; --lock-info-title-border: #343e3c; --script-tag-background-color: #171b1a; diff --git a/packages/neuron-ui/src/types/Controller/index.d.ts b/packages/neuron-ui/src/types/Controller/index.d.ts index 6a1b6f22b2..3df4fe6782 100644 --- a/packages/neuron-ui/src/types/Controller/index.d.ts +++ b/packages/neuron-ui/src/types/Controller/index.d.ts @@ -184,6 +184,12 @@ declare namespace Controller { message: string } + interface GetPrivateKeyParams { + walletID: string + address?: string + password: string + } + interface VerifyMessageParams { address: string signature: string diff --git a/packages/neuron-ui/src/widgets/Icons/PrivateKey.svg b/packages/neuron-ui/src/widgets/Icons/PrivateKey.svg new file mode 100644 index 0000000000..c102dee0fd --- /dev/null +++ b/packages/neuron-ui/src/widgets/Icons/PrivateKey.svg @@ -0,0 +1,4 @@ + + + + diff --git a/packages/neuron-ui/src/widgets/Icons/icon.tsx b/packages/neuron-ui/src/widgets/Icons/icon.tsx index 21eabcb23a..f9d0a6faf8 100644 --- a/packages/neuron-ui/src/widgets/Icons/icon.tsx +++ b/packages/neuron-ui/src/widgets/Icons/icon.tsx @@ -62,6 +62,7 @@ import { ReactComponent as QuestionSvg } from './Question.svg' import { ReactComponent as DetailsSvg } from './Details.svg' import { ReactComponent as ConfirmSvg } from './Confirm.svg' import { ReactComponent as UploadSvg } from './Upload.svg' +import { ReactComponent as PrivateKeySvg } from './PrivateKey.svg' import styles from './icon.module.scss' @@ -140,3 +141,4 @@ export const Question = WrapSvg(QuestionSvg) export const Details = WrapSvg(DetailsSvg) export const Confirm = WrapSvg(ConfirmSvg) export const Upload = WrapSvg(UploadSvg) +export const PrivateKey = WrapSvg(PrivateKeySvg) diff --git a/packages/neuron-ui/src/widgets/TextField/index.tsx b/packages/neuron-ui/src/widgets/TextField/index.tsx index 3f2208d302..d4f71acae0 100644 --- a/packages/neuron-ui/src/widgets/TextField/index.tsx +++ b/packages/neuron-ui/src/widgets/TextField/index.tsx @@ -132,8 +132,7 @@ const TextField = React.forwardRef( {...rest} /> )} - {suffix && (typeof suffix === 'string' ? {suffix} : suffix)} - {!suffix && type === 'password' && ( + {type === 'password' && ( : } )} + {suffix && (typeof suffix === 'string' ? {suffix} : suffix)} {hint ? {hint} : null} {error ? ( diff --git a/packages/neuron-wallet/src/controllers/api.ts b/packages/neuron-wallet/src/controllers/api.ts index f7f75d5b9e..f8cf5e4e50 100644 --- a/packages/neuron-wallet/src/controllers/api.ts +++ b/packages/neuron-wallet/src/controllers/api.ts @@ -440,6 +440,14 @@ export default class ApiController { return this.#walletsController.getAllAddresses(id) }) + handle('get-private-key-by-address', async (_, { walletID, password, address }) => { + return this.#walletsController.getPrivateKeyByAddress({ + walletID, + password, + address, + }) + }) + handle( 'update-address-description', async (_, params: { walletID: string; address: string; description: string }) => { diff --git a/packages/neuron-wallet/src/controllers/wallets.ts b/packages/neuron-wallet/src/controllers/wallets.ts index a47f3c2015..2307c6be40 100644 --- a/packages/neuron-wallet/src/controllers/wallets.ts +++ b/packages/neuron-wallet/src/controllers/wallets.ts @@ -699,4 +699,24 @@ export default class WalletsController { } }) } + + public async getPrivateKeyByAddress({ + walletID, + password, + address, + }: { + walletID: string + password: string + address?: string + }) { + const privateKey = await AddressService.getPrivateKeyByAddress({ + walletID, + password, + address, + }) + return { + status: ResponseCode.Success, + result: privateKey, + } + } } diff --git a/packages/neuron-wallet/src/services/addresses.ts b/packages/neuron-wallet/src/services/addresses.ts index 8f16b045a1..115e1927f1 100644 --- a/packages/neuron-wallet/src/services/addresses.ts +++ b/packages/neuron-wallet/src/services/addresses.ts @@ -1,5 +1,9 @@ import { hd } from '@ckb-lumos/lumos' +import { bytes } from '@ckb-lumos/lumos/codec' +import WalletService from './wallets' +import { AddressNotFound } from '../exceptions' import { publicKeyToAddress, DefaultAddressNumber } from '../utils/scriptAndAddress' +import AssetAccountService from './asset-account-service' import { Address as AddressInterface } from '../models/address' import AddressCreatedSubject from '../models/subjects/address-created-subject' import NetworksService from '../services/networks' @@ -445,4 +449,36 @@ export default class AddressService { public static async deleteByWalletId(walletId: string): Promise { await getConnection().createQueryBuilder().delete().from(HdPublicKeyInfo).where({ walletId }).execute() } + + public static async getPrivateKeyByAddress({ + walletID, + password, + address, + }: { + walletID: string + password: string + address?: string + }): Promise { + const wallet = WalletService.getInstance().get(walletID) + + const addresses = await AddressService.getAddressesByWalletId(walletID) + let addr = address ? addresses.find(addr => addr.address === address) : addresses[0] + + if (!addr) { + const usedBlake160s = new Set(await AssetAccountService.blake160sOfAssetAccounts()) + addr = addresses.find(a => !usedBlake160s.has(a.blake160))! + } + + if (!addr) { + throw new AddressNotFound() + } + + const masterPrivateKey = wallet.loadKeystore().extendedPrivateKey(password) + const masterKeychain = new hd.Keychain( + Buffer.from(bytes.bytify(masterPrivateKey.privateKey)), + Buffer.from(bytes.bytify(masterPrivateKey.chainCode)) + ) + + return `0x${masterKeychain.derivePath(addr.path).privateKey.toString('hex')}` + } } From 6ccc0f085e39df2919be11c3039e8d348a478203 Mon Sep 17 00:00:00 2001 From: devchenyan Date: Sat, 4 Jan 2025 21:32:02 +0800 Subject: [PATCH 02/10] feat: test --- .../tests/services/address.test.ts | 63 +++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/packages/neuron-wallet/tests/services/address.test.ts b/packages/neuron-wallet/tests/services/address.test.ts index 3083b5a588..b546176a5b 100644 --- a/packages/neuron-wallet/tests/services/address.test.ts +++ b/packages/neuron-wallet/tests/services/address.test.ts @@ -1,6 +1,7 @@ import SystemScriptInfo from '../../src/models/system-script-info' import { OutputStatus } from '../../src/models/chain/output' import OutputEntity from '../../src/database/chain/entities/output' +import { bytes } from '@ckb-lumos/lumos/codec' import { hd } from '@ckb-lumos/lumos' import { Address } from '../../src/models/address' import Transaction from '../../src/database/chain/entities/transaction' @@ -9,6 +10,8 @@ import { when } from 'jest-when' import HdPublicKeyInfo from '../../src/database/chain/entities/hd-public-key-info' import { closeConnection, getConnection, initConnection } from '../setupAndTeardown' import { NetworkType } from '../../src/models/network' +import SignMessage from '../../src/services/sign-message' +import WalletService from '../../src/services/wallets' const { AddressType, AccountExtendedPublicKey } = hd @@ -707,5 +710,65 @@ describe('integration tests for AddressService', () => { expect(stubbedAddressDbChangedSubjectNext).toHaveBeenCalledTimes(1) }) }) + + describe('getPrivateKeyByAddress', () => { + const walletService = WalletService.getInstance() + const mnemonic = 'tank planet champion pottery together intact quick police asset flower sudden question' + const password = '1234abc~' + const message = 'Hello World' + let address = 'ckb1qzda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50xwsqwe5xyvcxv95l22x8c5ra8tkc0jgxhvrqsda3p3k' + + beforeEach(() => { + walletService.clearAll() + }) + + it('return private key', async () => { + const seed = hd.mnemonic.mnemonicToSeedSync(mnemonic) + const masterKeychain = hd.Keychain.fromSeed(seed) + const extendedKey = new hd.ExtendedPrivateKey( + bytes.hexify(masterKeychain.privateKey), + bytes.hexify(masterKeychain.chainCode) + ) + + const keystore = hd.Keystore.create(extendedKey, password) + + const accountKeychain = masterKeychain.derivePath(hd.AccountExtendedPublicKey.ckbAccountPath) + const accountExtendedPublicKey = new hd.AccountExtendedPublicKey( + bytes.hexify(accountKeychain.publicKey), + bytes.hexify(accountKeychain.chainCode) + ) + + const wallet = walletService.create({ + id: '', + name: 'Test Wallet', + extendedKey: accountExtendedPublicKey.serialize(), + keystore, + }) + + const addresses = await wallet.checkAndGenerateAddresses(false, 5, 5) + + if (addresses) { + const obj = addresses[Math.floor(Math.random() * addresses.length)] + address = obj.address + } + + const privateKey = await AddressService.getPrivateKeyByAddress({ + walletID: wallet.id, + password: password, + address, + }) + + // @ts-ignore: Private method + const sig = SignMessage.signByPrivateKey(privateKey, message) + const signature = await SignMessage.sign({ + walletID: wallet.id, + password: password, + message: message, + address, + }) + + expect(sig).toEqual(signature) + }) + }) }) }) From ea6b9a727c1e90f13b8bef2a03601992c2f9f964 Mon Sep 17 00:00:00 2001 From: devchenyan Date: Sat, 4 Jan 2025 21:59:45 +0800 Subject: [PATCH 03/10] fix: comment --- .../components/Receive/receive.module.scss | 1 - .../src/components/ViewPrivateKey/index.tsx | 32 +++++++++---------- .../tests/services/address.test.ts | 4 ++- 3 files changed, 18 insertions(+), 19 deletions(-) diff --git a/packages/neuron-ui/src/components/Receive/receive.module.scss b/packages/neuron-ui/src/components/Receive/receive.module.scss index 0f9587a1ea..cf85d6b6aa 100644 --- a/packages/neuron-ui/src/components/Receive/receive.module.scss +++ b/packages/neuron-ui/src/components/Receive/receive.module.scss @@ -136,7 +136,6 @@ border: none; background: none; font-size: 12px; - font-family: PingFang SC; font-style: normal; font-weight: 500; color: var(--primary-color); diff --git a/packages/neuron-ui/src/components/ViewPrivateKey/index.tsx b/packages/neuron-ui/src/components/ViewPrivateKey/index.tsx index b076b8dd1d..d1d63d5e15 100644 --- a/packages/neuron-ui/src/components/ViewPrivateKey/index.tsx +++ b/packages/neuron-ui/src/components/ViewPrivateKey/index.tsx @@ -35,7 +35,7 @@ const ViewPrivateKey = ({ onClose, address }: { onClose?: () => void; address?: ) const onSubmit = useCallback( - async (e?: React.FormEvent) => { + (e?: React.FormEvent) => { if (e) { e.preventDefault() } @@ -43,23 +43,21 @@ const ViewPrivateKey = ({ onClose, address }: { onClose?: () => void; address?: return } setIsLoading(true) - try { - const res = await getPrivateKeyByAddress({ - walletID, - address, - password, + getPrivateKeyByAddress({ + walletID, + address, + password, + }) + .then(res => { + if (!isSuccessResponse(res)) { + setError(errorFormatter(res.message, t)) + return + } + setPrivateKey(res.result) + }) + .finally(() => { + setIsLoading(false) }) - - setIsLoading(false) - - if (!isSuccessResponse(res)) { - setError(errorFormatter(res.message, t)) - return - } - setPrivateKey(res.result) - } catch (err) { - setIsLoading(false) - } }, [walletID, password, setError, t] ) diff --git a/packages/neuron-wallet/tests/services/address.test.ts b/packages/neuron-wallet/tests/services/address.test.ts index b546176a5b..5720bccc60 100644 --- a/packages/neuron-wallet/tests/services/address.test.ts +++ b/packages/neuron-wallet/tests/services/address.test.ts @@ -748,7 +748,9 @@ describe('integration tests for AddressService', () => { const addresses = await wallet.checkAndGenerateAddresses(false, 5, 5) if (addresses) { - const obj = addresses[Math.floor(Math.random() * addresses.length)] + const crypto = require('crypto') + const randomIndex = crypto.randomInt(addresses.length) + const obj = addresses[randomIndex] address = obj.address } From 691b1947c1f576ed7ae2180c4d0559b2ba9fb164 Mon Sep 17 00:00:00 2001 From: devchenyan Date: Sat, 4 Jan 2025 22:36:32 +0800 Subject: [PATCH 04/10] fix: check --- .../src/utils/hooks/useGetCountDownAndFeeRateStats.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/neuron-ui/src/utils/hooks/useGetCountDownAndFeeRateStats.ts b/packages/neuron-ui/src/utils/hooks/useGetCountDownAndFeeRateStats.ts index fa6f181910..ac25ab7690 100644 --- a/packages/neuron-ui/src/utils/hooks/useGetCountDownAndFeeRateStats.ts +++ b/packages/neuron-ui/src/utils/hooks/useGetCountDownAndFeeRateStats.ts @@ -15,7 +15,7 @@ const useGetCountDownAndFeeRateStats = ({ seconds = 30, interval = 1000 }: Count suggestFeeRate: number }>({ suggestFeeRate: MEDIUM_FEE_RATE }) - const handleGetFeeRateStatis = useCallback(() => { + const handleGetFeeRateStatistics = useCallback(() => { getFeeRateStatistics() .then(res => { const { median } = res ?? {} @@ -50,7 +50,7 @@ const useGetCountDownAndFeeRateStats = ({ seconds = 30, interval = 1000 }: Count useEffect(() => { if (countDown === seconds) { - handleGetFeeRateStatis() + handleGetFeeRateStatistics() } }, [countDown, seconds]) From 2f123036c4f068e81bd4e44404a232b46f777c1a Mon Sep 17 00:00:00 2001 From: devchenyan Date: Sat, 4 Jan 2025 23:35:42 +0800 Subject: [PATCH 05/10] fix: test --- packages/neuron-wallet/tests/services/address.test.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/neuron-wallet/tests/services/address.test.ts b/packages/neuron-wallet/tests/services/address.test.ts index 5720bccc60..46e4cccfe9 100644 --- a/packages/neuron-wallet/tests/services/address.test.ts +++ b/packages/neuron-wallet/tests/services/address.test.ts @@ -13,6 +13,8 @@ import { NetworkType } from '../../src/models/network' import SignMessage from '../../src/services/sign-message' import WalletService from '../../src/services/wallets' +jest.setTimeout(15000) + const { AddressType, AccountExtendedPublicKey } = hd const walletId = '1' From 2e55f739a508957bbfdf174d74ce3093df1e650f Mon Sep 17 00:00:00 2001 From: devchenyan Date: Sun, 5 Jan 2025 00:43:43 +0800 Subject: [PATCH 06/10] fix: test --- .../src/components/Receive/index.tsx | 10 ++- .../src/components/SUDTAccountList/index.tsx | 1 + .../components/SUDTReceiveDialog/index.tsx | 4 +- .../src/components/ViewPrivateKey/index.tsx | 11 +++- .../src/stories/SUDTReceiveDialog.stories.tsx | 1 + .../neuron-ui/src/types/Controller/index.d.ts | 1 + packages/neuron-wallet/src/controllers/api.ts | 3 +- .../neuron-wallet/src/controllers/wallets.ts | 3 + .../neuron-wallet/src/services/addresses.ts | 12 ++-- .../tests/services/address.test.ts | 64 +++++++++++++++---- 10 files changed, 90 insertions(+), 20 deletions(-) diff --git a/packages/neuron-ui/src/components/Receive/index.tsx b/packages/neuron-ui/src/components/Receive/index.tsx index a182a1ea2e..be1578ec41 100644 --- a/packages/neuron-ui/src/components/Receive/index.tsx +++ b/packages/neuron-ui/src/components/Receive/index.tsx @@ -15,12 +15,14 @@ import { useCopyAndDownloadQrCode, useSwitchAddress } from './hooks' type AddressTransformWithCopyZoneProps = { showAddress: string + assetAccountId?: string isInShortFormat: boolean onClick: () => void } export const AddressQrCodeWithCopyZone = ({ showAddress, + assetAccountId, isInShortFormat, onClick, }: AddressTransformWithCopyZoneProps) => { @@ -92,7 +94,13 @@ export const AddressQrCodeWithCopyZone = ({ - {showViewPrivateKey && setShowViewPrivateKey(false)} />} + {showViewPrivateKey && ( + setShowViewPrivateKey(false)} + /> + )} ) } diff --git a/packages/neuron-ui/src/components/SUDTAccountList/index.tsx b/packages/neuron-ui/src/components/SUDTAccountList/index.tsx index b961ca079d..71eec2f9c2 100644 --- a/packages/neuron-ui/src/components/SUDTAccountList/index.tsx +++ b/packages/neuron-ui/src/components/SUDTAccountList/index.tsx @@ -123,6 +123,7 @@ const SUDTAccountList = () => { break } setReceiveData({ + acccountId: account.accountId, address: account.address, accountName: account.accountName ?? DEFAULT_SUDT_FIELDS.accountName, tokenName: account.tokenName ?? DEFAULT_SUDT_FIELDS.tokenName, diff --git a/packages/neuron-ui/src/components/SUDTReceiveDialog/index.tsx b/packages/neuron-ui/src/components/SUDTReceiveDialog/index.tsx index 8dbbac6c7b..53f7d7c7fa 100644 --- a/packages/neuron-ui/src/components/SUDTReceiveDialog/index.tsx +++ b/packages/neuron-ui/src/components/SUDTReceiveDialog/index.tsx @@ -13,6 +13,7 @@ const { DEFAULT_SUDT_FIELDS } = CONSTANTS export interface DataProps { address: string + acccountId: string accountName: string tokenName: string symbol: string @@ -21,7 +22,7 @@ export interface DataProps { const SUDTReceiveDialog = ({ data, onClose }: { data: DataProps; onClose?: () => void }) => { const [t] = useTranslation() const [isInShortFormat, setIsInShortFormat] = useState(false) - const { address, accountName, tokenName, symbol } = data + const { address, acccountId, accountName, tokenName, symbol } = data const displayedAddr = isInShortFormat ? addressToAddress(address, { deprecated: true }) : address @@ -52,6 +53,7 @@ const SUDTReceiveDialog = ({ data, onClose }: { data: DataProps; onClose?: () => setIsInShortFormat(is => !is)} /> diff --git a/packages/neuron-ui/src/components/ViewPrivateKey/index.tsx b/packages/neuron-ui/src/components/ViewPrivateKey/index.tsx index d1d63d5e15..963621fe43 100644 --- a/packages/neuron-ui/src/components/ViewPrivateKey/index.tsx +++ b/packages/neuron-ui/src/components/ViewPrivateKey/index.tsx @@ -9,7 +9,15 @@ import { Attention, Copy } from 'widgets/Icons/icon' import { getPrivateKeyByAddress } from 'services/remote' import styles from './viewPrivateKey.module.scss' -const ViewPrivateKey = ({ onClose, address }: { onClose?: () => void; address?: string }) => { +const ViewPrivateKey = ({ + onClose, + address, + assetAccountId, +}: { + onClose?: () => void + address?: string + assetAccountId?: string +}) => { const [t] = useTranslation() const [password, setPassword] = useState('') const [error, setError] = useState('') @@ -45,6 +53,7 @@ const ViewPrivateKey = ({ onClose, address }: { onClose?: () => void; address?: setIsLoading(true) getPrivateKeyByAddress({ walletID, + assetAccountId, address, password, }) diff --git a/packages/neuron-ui/src/stories/SUDTReceiveDialog.stories.tsx b/packages/neuron-ui/src/stories/SUDTReceiveDialog.stories.tsx index 8de39e5d01..853f2654b1 100644 --- a/packages/neuron-ui/src/stories/SUDTReceiveDialog.stories.tsx +++ b/packages/neuron-ui/src/stories/SUDTReceiveDialog.stories.tsx @@ -14,6 +14,7 @@ type Story = StoryObj export const Default: Story = { args: { data: { + acccountId: '1', address: 'ckt1q9gry5zg8stq8ruq5wfz3lm5wn2k7qw3ulsfmdhe98f2j1', accountName: 'account name', tokenName: 'token name', diff --git a/packages/neuron-ui/src/types/Controller/index.d.ts b/packages/neuron-ui/src/types/Controller/index.d.ts index 3df4fe6782..0898da1ab7 100644 --- a/packages/neuron-ui/src/types/Controller/index.d.ts +++ b/packages/neuron-ui/src/types/Controller/index.d.ts @@ -186,6 +186,7 @@ declare namespace Controller { interface GetPrivateKeyParams { walletID: string + assetAccountId?: string address?: string password: string } diff --git a/packages/neuron-wallet/src/controllers/api.ts b/packages/neuron-wallet/src/controllers/api.ts index f8cf5e4e50..d2f2b88c65 100644 --- a/packages/neuron-wallet/src/controllers/api.ts +++ b/packages/neuron-wallet/src/controllers/api.ts @@ -440,9 +440,10 @@ export default class ApiController { return this.#walletsController.getAllAddresses(id) }) - handle('get-private-key-by-address', async (_, { walletID, password, address }) => { + handle('get-private-key-by-address', async (_, { walletID, assetAccountId, password, address }) => { return this.#walletsController.getPrivateKeyByAddress({ walletID, + assetAccountId, password, address, }) diff --git a/packages/neuron-wallet/src/controllers/wallets.ts b/packages/neuron-wallet/src/controllers/wallets.ts index 2307c6be40..b363700139 100644 --- a/packages/neuron-wallet/src/controllers/wallets.ts +++ b/packages/neuron-wallet/src/controllers/wallets.ts @@ -702,15 +702,18 @@ export default class WalletsController { public async getPrivateKeyByAddress({ walletID, + assetAccountId, password, address, }: { walletID: string + assetAccountId?: string password: string address?: string }) { const privateKey = await AddressService.getPrivateKeyByAddress({ walletID, + assetAccountId, password, address, }) diff --git a/packages/neuron-wallet/src/services/addresses.ts b/packages/neuron-wallet/src/services/addresses.ts index 115e1927f1..4004a5c8bf 100644 --- a/packages/neuron-wallet/src/services/addresses.ts +++ b/packages/neuron-wallet/src/services/addresses.ts @@ -452,21 +452,25 @@ export default class AddressService { public static async getPrivateKeyByAddress({ walletID, + assetAccountId, password, address, }: { walletID: string + assetAccountId?: string password: string address?: string }): Promise { const wallet = WalletService.getInstance().get(walletID) - const addresses = await AddressService.getAddressesByWalletId(walletID) let addr = address ? addresses.find(addr => addr.address === address) : addresses[0] - if (!addr) { - const usedBlake160s = new Set(await AssetAccountService.blake160sOfAssetAccounts()) - addr = addresses.find(a => !usedBlake160s.has(a.blake160))! + if (assetAccountId) { + const assetAccount = await AssetAccountService.getAccount({ walletID, id: Number(assetAccountId) }) + if (!assetAccount) { + throw new AddressNotFound() + } + addr = addresses.find(a => a.blake160 === assetAccount.blake160) } if (!addr) { diff --git a/packages/neuron-wallet/tests/services/address.test.ts b/packages/neuron-wallet/tests/services/address.test.ts index 46e4cccfe9..46eeef17cc 100644 --- a/packages/neuron-wallet/tests/services/address.test.ts +++ b/packages/neuron-wallet/tests/services/address.test.ts @@ -4,6 +4,7 @@ import OutputEntity from '../../src/database/chain/entities/output' import { bytes } from '@ckb-lumos/lumos/codec' import { hd } from '@ckb-lumos/lumos' import { Address } from '../../src/models/address' +import AssetAccount from '../../src/models/asset-account' import Transaction from '../../src/database/chain/entities/transaction' import { TransactionStatus } from '../../src/models/chain/transaction' import { when } from 'jest-when' @@ -12,8 +13,7 @@ import { closeConnection, getConnection, initConnection } from '../setupAndTeard import { NetworkType } from '../../src/models/network' import SignMessage from '../../src/services/sign-message' import WalletService from '../../src/services/wallets' - -jest.setTimeout(15000) +import AssetAccountService from '../../src/services/asset-account-service' const { AddressType, AccountExtendedPublicKey } = hd @@ -720,11 +720,13 @@ describe('integration tests for AddressService', () => { const message = 'Hello World' let address = 'ckb1qzda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50xwsqwe5xyvcxv95l22x8c5ra8tkc0jgxhvrqsda3p3k' - beforeEach(() => { + let walletID = '' + + beforeAll(() => { + jest.setTimeout(15000) + walletService.clearAll() - }) - it('return private key', async () => { const seed = hd.mnemonic.mnemonicToSeedSync(mnemonic) const masterKeychain = hd.Keychain.fromSeed(seed) const extendedKey = new hd.ExtendedPrivateKey( @@ -746,9 +748,11 @@ describe('integration tests for AddressService', () => { extendedKey: accountExtendedPublicKey.serialize(), keystore, }) + walletID = wallet.id + }) - const addresses = await wallet.checkAndGenerateAddresses(false, 5, 5) - + it('hdWallet address', async () => { + const addresses = await walletService.get(walletID).checkAndGenerateAddresses(false, 5, 5) if (addresses) { const crypto = require('crypto') const randomIndex = crypto.randomInt(addresses.length) @@ -757,22 +761,58 @@ describe('integration tests for AddressService', () => { } const privateKey = await AddressService.getPrivateKeyByAddress({ - walletID: wallet.id, - password: password, + walletID, + password, address, }) // @ts-ignore: Private method const sig = SignMessage.signByPrivateKey(privateKey, message) const signature = await SignMessage.sign({ - walletID: wallet.id, - password: password, - message: message, + walletID, + password, + message, address, }) expect(sig).toEqual(signature) }) + + it('asset account address', async () => { + const addresses = await walletService.get(walletID).getNextReceivingAddresses() + const usedBlake160s = new Set(await AssetAccountService.blake160sOfAssetAccounts()) + const addrObj = addresses.find(a => !usedBlake160s.has(a.blake160)) + + if (addrObj) { + const assetAccount = AssetAccount.fromObject({ + tokenID: 'CKBytes', + symbol: 'ckb', + tokenName: 'ckb', + decimal: '0', + balance: '0', + accountName: 'ckb', + blake160: addrObj.blake160, + }) + + const privateKey = await AddressService.getPrivateKeyByAddress({ + walletID, + accountId: assetAccount.id, + password, + address, + }) + + // @ts-ignore: Private method + const sig = SignMessage.signByPrivateKey(privateKey, message) + const signature = await SignMessage.sign({ + walletID, + password, + message, + address, + }) + + expect(sig).toEqual(signature) + } + }) }) }) }) From f1d2f752e0ae04cb4f452b5177dad58a292d0229 Mon Sep 17 00:00:00 2001 From: devchenyan Date: Sun, 5 Jan 2025 00:47:02 +0800 Subject: [PATCH 07/10] fix: check --- packages/neuron-ui/src/components/SUDTAccountList/index.tsx | 2 +- .../neuron-ui/src/components/SUDTReceiveDialog/index.tsx | 6 +++--- .../neuron-ui/src/stories/SUDTReceiveDialog.stories.tsx | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/neuron-ui/src/components/SUDTAccountList/index.tsx b/packages/neuron-ui/src/components/SUDTAccountList/index.tsx index 71eec2f9c2..6777efcd53 100644 --- a/packages/neuron-ui/src/components/SUDTAccountList/index.tsx +++ b/packages/neuron-ui/src/components/SUDTAccountList/index.tsx @@ -123,7 +123,7 @@ const SUDTAccountList = () => { break } setReceiveData({ - acccountId: account.accountId, + acccountID: account.acccountID, address: account.address, accountName: account.accountName ?? DEFAULT_SUDT_FIELDS.accountName, tokenName: account.tokenName ?? DEFAULT_SUDT_FIELDS.tokenName, diff --git a/packages/neuron-ui/src/components/SUDTReceiveDialog/index.tsx b/packages/neuron-ui/src/components/SUDTReceiveDialog/index.tsx index 53f7d7c7fa..4bece854fb 100644 --- a/packages/neuron-ui/src/components/SUDTReceiveDialog/index.tsx +++ b/packages/neuron-ui/src/components/SUDTReceiveDialog/index.tsx @@ -13,7 +13,7 @@ const { DEFAULT_SUDT_FIELDS } = CONSTANTS export interface DataProps { address: string - acccountId: string + acccountID: string accountName: string tokenName: string symbol: string @@ -22,7 +22,7 @@ export interface DataProps { const SUDTReceiveDialog = ({ data, onClose }: { data: DataProps; onClose?: () => void }) => { const [t] = useTranslation() const [isInShortFormat, setIsInShortFormat] = useState(false) - const { address, acccountId, accountName, tokenName, symbol } = data + const { address, acccountID, accountName, tokenName, symbol } = data const displayedAddr = isInShortFormat ? addressToAddress(address, { deprecated: true }) : address @@ -53,7 +53,7 @@ const SUDTReceiveDialog = ({ data, onClose }: { data: DataProps; onClose?: () => setIsInShortFormat(is => !is)} /> diff --git a/packages/neuron-ui/src/stories/SUDTReceiveDialog.stories.tsx b/packages/neuron-ui/src/stories/SUDTReceiveDialog.stories.tsx index 853f2654b1..aa261cbefd 100644 --- a/packages/neuron-ui/src/stories/SUDTReceiveDialog.stories.tsx +++ b/packages/neuron-ui/src/stories/SUDTReceiveDialog.stories.tsx @@ -14,7 +14,7 @@ type Story = StoryObj export const Default: Story = { args: { data: { - acccountId: '1', + acccountID: '1', address: 'ckt1q9gry5zg8stq8ruq5wfz3lm5wn2k7qw3ulsfmdhe98f2j1', accountName: 'account name', tokenName: 'token name', From 2da3fb355e039b91e0a1bbb55b31bd68a521bcf4 Mon Sep 17 00:00:00 2001 From: devchenyan Date: Sun, 5 Jan 2025 00:49:28 +0800 Subject: [PATCH 08/10] fix: check --- packages/neuron-ui/src/components/SUDTAccountList/index.tsx | 2 +- .../neuron-ui/src/components/SUDTReceiveDialog/index.tsx | 6 +++--- .../neuron-ui/src/stories/SUDTReceiveDialog.stories.tsx | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/neuron-ui/src/components/SUDTAccountList/index.tsx b/packages/neuron-ui/src/components/SUDTAccountList/index.tsx index 6777efcd53..ee69ed6e71 100644 --- a/packages/neuron-ui/src/components/SUDTAccountList/index.tsx +++ b/packages/neuron-ui/src/components/SUDTAccountList/index.tsx @@ -123,7 +123,7 @@ const SUDTAccountList = () => { break } setReceiveData({ - acccountID: account.acccountID, + accountId: account.accountId, address: account.address, accountName: account.accountName ?? DEFAULT_SUDT_FIELDS.accountName, tokenName: account.tokenName ?? DEFAULT_SUDT_FIELDS.tokenName, diff --git a/packages/neuron-ui/src/components/SUDTReceiveDialog/index.tsx b/packages/neuron-ui/src/components/SUDTReceiveDialog/index.tsx index 4bece854fb..0a2ff7c420 100644 --- a/packages/neuron-ui/src/components/SUDTReceiveDialog/index.tsx +++ b/packages/neuron-ui/src/components/SUDTReceiveDialog/index.tsx @@ -13,7 +13,7 @@ const { DEFAULT_SUDT_FIELDS } = CONSTANTS export interface DataProps { address: string - acccountID: string + accountId: string accountName: string tokenName: string symbol: string @@ -22,7 +22,7 @@ export interface DataProps { const SUDTReceiveDialog = ({ data, onClose }: { data: DataProps; onClose?: () => void }) => { const [t] = useTranslation() const [isInShortFormat, setIsInShortFormat] = useState(false) - const { address, acccountID, accountName, tokenName, symbol } = data + const { address, accountId, accountName, tokenName, symbol } = data const displayedAddr = isInShortFormat ? addressToAddress(address, { deprecated: true }) : address @@ -53,7 +53,7 @@ const SUDTReceiveDialog = ({ data, onClose }: { data: DataProps; onClose?: () => setIsInShortFormat(is => !is)} /> diff --git a/packages/neuron-ui/src/stories/SUDTReceiveDialog.stories.tsx b/packages/neuron-ui/src/stories/SUDTReceiveDialog.stories.tsx index aa261cbefd..5148534f5e 100644 --- a/packages/neuron-ui/src/stories/SUDTReceiveDialog.stories.tsx +++ b/packages/neuron-ui/src/stories/SUDTReceiveDialog.stories.tsx @@ -14,7 +14,7 @@ type Story = StoryObj export const Default: Story = { args: { data: { - acccountID: '1', + accountId: '1', address: 'ckt1q9gry5zg8stq8ruq5wfz3lm5wn2k7qw3ulsfmdhe98f2j1', accountName: 'account name', tokenName: 'token name', From 68d2c2c99156472e89cf7a983e857b1e3fb301a5 Mon Sep 17 00:00:00 2001 From: devchenyan Date: Mon, 6 Jan 2025 01:58:08 +0800 Subject: [PATCH 09/10] fix: comment --- .../src/components/Receive/index.tsx | 10 +-- .../components/SUDTReceiveDialog/index.tsx | 3 +- .../src/components/ViewPrivateKey/index.tsx | 11 +-- .../neuron-ui/src/types/Controller/index.d.ts | 1 - packages/neuron-wallet/src/controllers/api.ts | 3 +- .../neuron-wallet/src/controllers/wallets.ts | 3 - .../neuron-wallet/src/services/addresses.ts | 14 +--- .../tests/services/address.test.ts | 80 +++++-------------- 8 files changed, 27 insertions(+), 98 deletions(-) diff --git a/packages/neuron-ui/src/components/Receive/index.tsx b/packages/neuron-ui/src/components/Receive/index.tsx index be1578ec41..a182a1ea2e 100644 --- a/packages/neuron-ui/src/components/Receive/index.tsx +++ b/packages/neuron-ui/src/components/Receive/index.tsx @@ -15,14 +15,12 @@ import { useCopyAndDownloadQrCode, useSwitchAddress } from './hooks' type AddressTransformWithCopyZoneProps = { showAddress: string - assetAccountId?: string isInShortFormat: boolean onClick: () => void } export const AddressQrCodeWithCopyZone = ({ showAddress, - assetAccountId, isInShortFormat, onClick, }: AddressTransformWithCopyZoneProps) => { @@ -94,13 +92,7 @@ export const AddressQrCodeWithCopyZone = ({ - {showViewPrivateKey && ( - setShowViewPrivateKey(false)} - /> - )} + {showViewPrivateKey && setShowViewPrivateKey(false)} />} ) } diff --git a/packages/neuron-ui/src/components/SUDTReceiveDialog/index.tsx b/packages/neuron-ui/src/components/SUDTReceiveDialog/index.tsx index 0a2ff7c420..36eaf810b8 100644 --- a/packages/neuron-ui/src/components/SUDTReceiveDialog/index.tsx +++ b/packages/neuron-ui/src/components/SUDTReceiveDialog/index.tsx @@ -22,7 +22,7 @@ export interface DataProps { const SUDTReceiveDialog = ({ data, onClose }: { data: DataProps; onClose?: () => void }) => { const [t] = useTranslation() const [isInShortFormat, setIsInShortFormat] = useState(false) - const { address, accountId, accountName, tokenName, symbol } = data + const { address, accountName, tokenName, symbol } = data const displayedAddr = isInShortFormat ? addressToAddress(address, { deprecated: true }) : address @@ -53,7 +53,6 @@ const SUDTReceiveDialog = ({ data, onClose }: { data: DataProps; onClose?: () => setIsInShortFormat(is => !is)} /> diff --git a/packages/neuron-ui/src/components/ViewPrivateKey/index.tsx b/packages/neuron-ui/src/components/ViewPrivateKey/index.tsx index 963621fe43..d1d63d5e15 100644 --- a/packages/neuron-ui/src/components/ViewPrivateKey/index.tsx +++ b/packages/neuron-ui/src/components/ViewPrivateKey/index.tsx @@ -9,15 +9,7 @@ import { Attention, Copy } from 'widgets/Icons/icon' import { getPrivateKeyByAddress } from 'services/remote' import styles from './viewPrivateKey.module.scss' -const ViewPrivateKey = ({ - onClose, - address, - assetAccountId, -}: { - onClose?: () => void - address?: string - assetAccountId?: string -}) => { +const ViewPrivateKey = ({ onClose, address }: { onClose?: () => void; address?: string }) => { const [t] = useTranslation() const [password, setPassword] = useState('') const [error, setError] = useState('') @@ -53,7 +45,6 @@ const ViewPrivateKey = ({ setIsLoading(true) getPrivateKeyByAddress({ walletID, - assetAccountId, address, password, }) diff --git a/packages/neuron-ui/src/types/Controller/index.d.ts b/packages/neuron-ui/src/types/Controller/index.d.ts index 0898da1ab7..3df4fe6782 100644 --- a/packages/neuron-ui/src/types/Controller/index.d.ts +++ b/packages/neuron-ui/src/types/Controller/index.d.ts @@ -186,7 +186,6 @@ declare namespace Controller { interface GetPrivateKeyParams { walletID: string - assetAccountId?: string address?: string password: string } diff --git a/packages/neuron-wallet/src/controllers/api.ts b/packages/neuron-wallet/src/controllers/api.ts index d2f2b88c65..f8cf5e4e50 100644 --- a/packages/neuron-wallet/src/controllers/api.ts +++ b/packages/neuron-wallet/src/controllers/api.ts @@ -440,10 +440,9 @@ export default class ApiController { return this.#walletsController.getAllAddresses(id) }) - handle('get-private-key-by-address', async (_, { walletID, assetAccountId, password, address }) => { + handle('get-private-key-by-address', async (_, { walletID, password, address }) => { return this.#walletsController.getPrivateKeyByAddress({ walletID, - assetAccountId, password, address, }) diff --git a/packages/neuron-wallet/src/controllers/wallets.ts b/packages/neuron-wallet/src/controllers/wallets.ts index b363700139..2307c6be40 100644 --- a/packages/neuron-wallet/src/controllers/wallets.ts +++ b/packages/neuron-wallet/src/controllers/wallets.ts @@ -702,18 +702,15 @@ export default class WalletsController { public async getPrivateKeyByAddress({ walletID, - assetAccountId, password, address, }: { walletID: string - assetAccountId?: string password: string address?: string }) { const privateKey = await AddressService.getPrivateKeyByAddress({ walletID, - assetAccountId, password, address, }) diff --git a/packages/neuron-wallet/src/services/addresses.ts b/packages/neuron-wallet/src/services/addresses.ts index 4004a5c8bf..85b8cb4ffd 100644 --- a/packages/neuron-wallet/src/services/addresses.ts +++ b/packages/neuron-wallet/src/services/addresses.ts @@ -2,8 +2,7 @@ import { hd } from '@ckb-lumos/lumos' import { bytes } from '@ckb-lumos/lumos/codec' import WalletService from './wallets' import { AddressNotFound } from '../exceptions' -import { publicKeyToAddress, DefaultAddressNumber } from '../utils/scriptAndAddress' -import AssetAccountService from './asset-account-service' +import { publicKeyToAddress, DefaultAddressNumber, addressToScript } from '../utils/scriptAndAddress' import { Address as AddressInterface } from '../models/address' import AddressCreatedSubject from '../models/subjects/address-created-subject' import NetworksService from '../services/networks' @@ -452,12 +451,10 @@ export default class AddressService { public static async getPrivateKeyByAddress({ walletID, - assetAccountId, password, address, }: { walletID: string - assetAccountId?: string password: string address?: string }): Promise { @@ -465,12 +462,9 @@ export default class AddressService { const addresses = await AddressService.getAddressesByWalletId(walletID) let addr = address ? addresses.find(addr => addr.address === address) : addresses[0] - if (assetAccountId) { - const assetAccount = await AssetAccountService.getAccount({ walletID, id: Number(assetAccountId) }) - if (!assetAccount) { - throw new AddressNotFound() - } - addr = addresses.find(a => a.blake160 === assetAccount.blake160) + if (!addr && address) { + const lock = addressToScript(address) + addr = addresses.find(a => a.blake160 === lock.args) } if (!addr) { diff --git a/packages/neuron-wallet/tests/services/address.test.ts b/packages/neuron-wallet/tests/services/address.test.ts index 46eeef17cc..00ebda11f7 100644 --- a/packages/neuron-wallet/tests/services/address.test.ts +++ b/packages/neuron-wallet/tests/services/address.test.ts @@ -4,16 +4,13 @@ import OutputEntity from '../../src/database/chain/entities/output' import { bytes } from '@ckb-lumos/lumos/codec' import { hd } from '@ckb-lumos/lumos' import { Address } from '../../src/models/address' -import AssetAccount from '../../src/models/asset-account' import Transaction from '../../src/database/chain/entities/transaction' import { TransactionStatus } from '../../src/models/chain/transaction' import { when } from 'jest-when' import HdPublicKeyInfo from '../../src/database/chain/entities/hd-public-key-info' import { closeConnection, getConnection, initConnection } from '../setupAndTeardown' import { NetworkType } from '../../src/models/network' -import SignMessage from '../../src/services/sign-message' import WalletService from '../../src/services/wallets' -import AssetAccountService from '../../src/services/asset-account-service' const { AddressType, AccountExtendedPublicKey } = hd @@ -717,8 +714,20 @@ describe('integration tests for AddressService', () => { const walletService = WalletService.getInstance() const mnemonic = 'tank planet champion pottery together intact quick police asset flower sudden question' const password = '1234abc~' - const message = 'Hello World' - let address = 'ckb1qzda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50xwsqwe5xyvcxv95l22x8c5ra8tkc0jgxhvrqsda3p3k' + + const addressObj = { + walletId: '5af2473e-78f5-4799-a193-d2b1c2989838', + address: 'ckb1qzda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50xwsqvfewjgc69nj783sh03nuckxjacwr55vwgngf9dr', + path: "m/44'/309'/0'/0/0", + addressType: 0, + addressIndex: 0, + blake160: '0x89cba48c68b3978f185df19f31634bb870e94639', + } + + const privateKey = '0x848422863825f69e66dc7f48a3302459ec845395370c23578817456ad6b04b14' + + const AddressService = require('../../src/services/addresses').default + const getAddressesByWalletIdMock = jest.spyOn(AddressService, 'getAddressesByWalletId') let walletID = '' @@ -751,67 +760,16 @@ describe('integration tests for AddressService', () => { walletID = wallet.id }) - it('hdWallet address', async () => { - const addresses = await walletService.get(walletID).checkAndGenerateAddresses(false, 5, 5) - if (addresses) { - const crypto = require('crypto') - const randomIndex = crypto.randomInt(addresses.length) - const obj = addresses[randomIndex] - address = obj.address - } - - const privateKey = await AddressService.getPrivateKeyByAddress({ - walletID, - password, - address, - }) + it('getPrivateKeyByAddress', async () => { + getAddressesByWalletIdMock.mockReturnValueOnce([addressObj]) - // @ts-ignore: Private method - const sig = SignMessage.signByPrivateKey(privateKey, message) - const signature = await SignMessage.sign({ + const pk = await AddressService.getPrivateKeyByAddress({ walletID, password, - message, - address, + address: addressObj.address, }) - expect(sig).toEqual(signature) - }) - - it('asset account address', async () => { - const addresses = await walletService.get(walletID).getNextReceivingAddresses() - const usedBlake160s = new Set(await AssetAccountService.blake160sOfAssetAccounts()) - const addrObj = addresses.find(a => !usedBlake160s.has(a.blake160)) - - if (addrObj) { - const assetAccount = AssetAccount.fromObject({ - tokenID: 'CKBytes', - symbol: 'ckb', - tokenName: 'ckb', - decimal: '0', - balance: '0', - accountName: 'ckb', - blake160: addrObj.blake160, - }) - - const privateKey = await AddressService.getPrivateKeyByAddress({ - walletID, - accountId: assetAccount.id, - password, - address, - }) - - // @ts-ignore: Private method - const sig = SignMessage.signByPrivateKey(privateKey, message) - const signature = await SignMessage.sign({ - walletID, - password, - message, - address, - }) - - expect(sig).toEqual(signature) - } + expect(pk).toEqual(privateKey) }) }) }) From 63d00ed79be1545b0811ae4ae3c7396d5c6ce33e Mon Sep 17 00:00:00 2001 From: devchenyan Date: Mon, 6 Jan 2025 10:21:17 +0800 Subject: [PATCH 10/10] fix: comment --- packages/neuron-ui/src/components/SUDTAccountList/index.tsx | 1 - packages/neuron-ui/src/components/SUDTReceiveDialog/index.tsx | 1 - packages/neuron-ui/src/stories/SUDTReceiveDialog.stories.tsx | 1 - 3 files changed, 3 deletions(-) diff --git a/packages/neuron-ui/src/components/SUDTAccountList/index.tsx b/packages/neuron-ui/src/components/SUDTAccountList/index.tsx index ee69ed6e71..b961ca079d 100644 --- a/packages/neuron-ui/src/components/SUDTAccountList/index.tsx +++ b/packages/neuron-ui/src/components/SUDTAccountList/index.tsx @@ -123,7 +123,6 @@ const SUDTAccountList = () => { break } setReceiveData({ - accountId: account.accountId, address: account.address, accountName: account.accountName ?? DEFAULT_SUDT_FIELDS.accountName, tokenName: account.tokenName ?? DEFAULT_SUDT_FIELDS.tokenName, diff --git a/packages/neuron-ui/src/components/SUDTReceiveDialog/index.tsx b/packages/neuron-ui/src/components/SUDTReceiveDialog/index.tsx index 36eaf810b8..8dbbac6c7b 100644 --- a/packages/neuron-ui/src/components/SUDTReceiveDialog/index.tsx +++ b/packages/neuron-ui/src/components/SUDTReceiveDialog/index.tsx @@ -13,7 +13,6 @@ const { DEFAULT_SUDT_FIELDS } = CONSTANTS export interface DataProps { address: string - accountId: string accountName: string tokenName: string symbol: string diff --git a/packages/neuron-ui/src/stories/SUDTReceiveDialog.stories.tsx b/packages/neuron-ui/src/stories/SUDTReceiveDialog.stories.tsx index 5148534f5e..8de39e5d01 100644 --- a/packages/neuron-ui/src/stories/SUDTReceiveDialog.stories.tsx +++ b/packages/neuron-ui/src/stories/SUDTReceiveDialog.stories.tsx @@ -14,7 +14,6 @@ type Story = StoryObj export const Default: Story = { args: { data: { - accountId: '1', address: 'ckt1q9gry5zg8stq8ruq5wfz3lm5wn2k7qw3ulsfmdhe98f2j1', accountName: 'account name', tokenName: 'token name',