From 6e70c94ce1eccbb0409ead1d787095ba32953a61 Mon Sep 17 00:00:00 2001 From: devchenyan Date: Thu, 13 Mar 2025 21:03:07 +0800 Subject: [PATCH 01/22] fix: Manage Nervos DAO with multisig address --- .../src/components/MultisigAddress/index.tsx | 34 ++++++++++- packages/neuron-ui/src/locales/ar.json | 2 + packages/neuron-ui/src/locales/en.json | 2 + packages/neuron-ui/src/locales/es.json | 2 + packages/neuron-ui/src/locales/fr.json | 2 + packages/neuron-ui/src/locales/zh-tw.json | 2 + packages/neuron-ui/src/locales/zh.json | 2 + packages/neuron-wallet/src/services/cells.ts | 23 +------- .../neuron-wallet/src/services/multisig.ts | 59 ++++++++++++++++--- packages/neuron-wallet/src/utils/const.ts | 1 + 10 files changed, 97 insertions(+), 32 deletions(-) diff --git a/packages/neuron-ui/src/components/MultisigAddress/index.tsx b/packages/neuron-ui/src/components/MultisigAddress/index.tsx index c586003db0..f8defd8bf0 100644 --- a/packages/neuron-ui/src/components/MultisigAddress/index.tsx +++ b/packages/neuron-ui/src/components/MultisigAddress/index.tsx @@ -204,6 +204,25 @@ const MultisigAddress = () => { [listActionOptions] ) + const daoDisabledMessage = useMemo(() => { + if (wallet.device) { + if ( + (daoDepositAction.depositFromMultisig && daoDepositAction.isDialogOpen) || + (daoWithdrawAction.withdrawFromMultisig && daoWithdrawAction.isDialogOpen) + ) { + const multisigConfig = daoDepositAction.depositFromMultisig || daoWithdrawAction.withdrawFromMultisig + if (!multisigConfig) return '' + const { canSign } = getMultisigSignStatus({ + multisigConfig, + addresses, + }) + + return canSign ? 'dao-ledger-notice' : 'dao-hardware-not-match' + } + } + return '' + }, [daoDepositAction, daoWithdrawAction, wallet.device, addresses]) + const { keywords, onChange, onBlur } = useSearch(clearSelected, onFilterConfig) const sendTotalBalance = useMemo(() => { @@ -584,7 +603,7 @@ const MultisigAddress = () => { /> ) : null} - {daoDepositAction.depositFromMultisig && daoDepositAction.isDialogOpen ? ( + {!daoDisabledMessage && daoDepositAction.depositFromMultisig && daoDepositAction.isDialogOpen ? ( { /> ) : null} - {daoWithdrawAction.withdrawFromMultisig && daoWithdrawAction.isDialogOpen ? ( + {!daoDisabledMessage && daoWithdrawAction.withdrawFromMultisig && daoWithdrawAction.isDialogOpen ? ( ) : null} + + { + daoDepositAction.closeDialog() + daoWithdrawAction.closeDialog() + }} + /> ) } diff --git a/packages/neuron-ui/src/locales/ar.json b/packages/neuron-ui/src/locales/ar.json index 6e979ac5df..c0891e05be 100644 --- a/packages/neuron-ui/src/locales/ar.json +++ b/packages/neuron-ui/src/locales/ar.json @@ -1171,6 +1171,8 @@ "daoWithdraw": "سحب من DAO" } }, + "dao-ledger-notice": "تدعم محافظ Ledger حاليًا توقيع معاملات DAO فقط، ولا تدعم بدء الإيداعات أو السحوبات. يرجى التحقق من التحديثات المستقبلية.", + "dao-hardware-not-match": "المحفظة الصلبة المتصلة حاليًا لا تتطابق مع المحفظة الحالية.", "import-dialog": { "actions": { "cancel": "إلغاء", diff --git a/packages/neuron-ui/src/locales/en.json b/packages/neuron-ui/src/locales/en.json index d943e24dcc..62856f24c0 100644 --- a/packages/neuron-ui/src/locales/en.json +++ b/packages/neuron-ui/src/locales/en.json @@ -1171,6 +1171,8 @@ "daoWithdraw": "DAO Withdraw" } }, + "dao-ledger-notice": "Ledger wallets currently support only signing DAO transactions, not initiating deposits or withdrawals. Please check back for future updates.", + "dao-hardware-not-match": "The hardware wallet currently connected does not match the current wallet.", "import-dialog": { "actions": { "cancel": "Cancel", diff --git a/packages/neuron-ui/src/locales/es.json b/packages/neuron-ui/src/locales/es.json index 38179b3161..a06a1d2c55 100644 --- a/packages/neuron-ui/src/locales/es.json +++ b/packages/neuron-ui/src/locales/es.json @@ -1154,6 +1154,8 @@ "daoWithdraw": "Retirar de DAO" } }, + "dao-ledger-notice": "Las billeteras Ledger actualmente solo admiten la firma de transacciones DAO, no la iniciación de depósitos o retiros. Vuelva a consultar para futuras actualizaciones.", + "dao-hardware-not-match": "La billetera de hardware conectada actualmente no coincide con la billetera actual.", "import-dialog": { "actions": { "cancel": "Cancelar", diff --git a/packages/neuron-ui/src/locales/fr.json b/packages/neuron-ui/src/locales/fr.json index 31e566bc62..02cddfdf75 100644 --- a/packages/neuron-ui/src/locales/fr.json +++ b/packages/neuron-ui/src/locales/fr.json @@ -1161,6 +1161,8 @@ "daoWithdraw": "Retirer de DAO" } }, + "dao-ledger-notice": "Les portefeuilles Ledger prennent actuellement en charge uniquement la signature des transactions DAO, mais pas l’initiation des dépôts ou des retraits. Veuillez vérifier les mises à jour futures.", + "dao-hardware-not-match": "Le portefeuille matériel actuellement connecté ne correspond pas au portefeuille actuel.", "import-dialog": { "actions": { "cancel": "Annuler", diff --git a/packages/neuron-ui/src/locales/zh-tw.json b/packages/neuron-ui/src/locales/zh-tw.json index 8018cb5f86..f254fc07aa 100644 --- a/packages/neuron-ui/src/locales/zh-tw.json +++ b/packages/neuron-ui/src/locales/zh-tw.json @@ -1164,6 +1164,8 @@ "daoWithdraw": "從 DAO 取出" } }, + "dao-ledger-notice": "Ledger 錢包目前僅支援簽署 DAO 交易,無法發起存款或提取。請關注未來的更新。", + "dao-hardware-not-match": "當前連接的硬體錢包與當前錢包不匹配。", "import-dialog": { "actions": { "cancel": "取消", diff --git a/packages/neuron-ui/src/locales/zh.json b/packages/neuron-ui/src/locales/zh.json index 9b958cde38..04414759a7 100644 --- a/packages/neuron-ui/src/locales/zh.json +++ b/packages/neuron-ui/src/locales/zh.json @@ -1163,6 +1163,8 @@ "daoWithdraw": "从 DAO 取出" } }, + "dao-ledger-notice": "Ledger 钱包目前仅支持签署 DAO 交易,无法发起存款或提取。请关注未来的更新。", + "dao-hardware-not-match": "当前连接的硬件钱包与当前钱包不匹配。", "import-dialog": { "actions": { "cancel": "取消", diff --git a/packages/neuron-wallet/src/services/cells.ts b/packages/neuron-wallet/src/services/cells.ts index 235648ef46..fae732840e 100644 --- a/packages/neuron-wallet/src/services/cells.ts +++ b/packages/neuron-wallet/src/services/cells.ts @@ -1321,15 +1321,6 @@ export default class CellsService { return {} } const lockHashes = multisigAddresses.map(v => scriptToHash(addressToScript(v))) - - const outputs = await getConnection() - .getRepository(OutputEntity) - .createQueryBuilder('output') - .where('output.lockHash IN (:...lockHashes)', { lockHashes }) - .andWhere('output.hasData = :hasData', { hasData: true }) - .andWhere('output.typeHash IS NOT NULL') - .getMany() - const connection = await getConnection() const [sql, parameters] = connection.driver.escapeQueryWithParameters( ` @@ -1345,7 +1336,7 @@ export default class CellsService { `, { lockHashes, - statuses: [OutputStatus.Live, OutputStatus.Sent], + statuses: [OutputStatus.Live], }, {} ) @@ -1369,18 +1360,6 @@ export default class CellsService { ] = c.balance }) - outputs.forEach(item => { - const key = scriptToAddress( - { - args: item.lockArgs, - codeHash: SystemScriptInfo.MULTI_SIGN_CODE_HASH, - hashType: SystemScriptInfo.MULTI_SIGN_HASH_TYPE, - }, - isMainnet - ) - balances[key] = (BigInt(balances[key]) - BigInt(item.capacity)).toString() - }) - return balances } diff --git a/packages/neuron-wallet/src/services/multisig.ts b/packages/neuron-wallet/src/services/multisig.ts index a2baccd5ee..22bd92ce02 100644 --- a/packages/neuron-wallet/src/services/multisig.ts +++ b/packages/neuron-wallet/src/services/multisig.ts @@ -13,6 +13,12 @@ import Multisig from '../models/multisig' import SyncProgress, { SyncAddressType } from '../database/chain/entities/sync-progress' import { NetworkType } from '../models/network' import logger from '../utils/logger' +import { TransactionPersistor } from './tx' +import { DAO_DATA } from '../utils/const' +import RpcService from '../services/rpc-service' +import TransactionWithStatus from '../models/chain/transaction-with-status' +import TxStatus from '../models/chain/tx-status' +import SystemScriptInfo from '../models/system-script-info' const max64Int = '0x' + 'f'.repeat(16) export default class MultisigService { @@ -107,8 +113,8 @@ export default class MultisigService { }) } - static async getLiveCells(multisigConfigs: MultisigConfig[]) { - const liveCells: MultisigOutput[] = [] + static async getCells(multisigConfigs: MultisigConfig[]) { + const cells: any[] = [] const addressCursorMap: Map = new Map() let currentMultisigConfigs = MultisigService.removeDulpicateConfig(multisigConfigs) const network = NetworksService.getInstance().getCurrent() @@ -144,17 +150,18 @@ export default class MultisigService { const config = currentMultisigConfigs[idx] const script = Multisig.getMultisigScript(config.blake160s, config.r, config.m, config.n) addressCursorMap.set(script.args, v?.result?.last_cursor) - liveCells.push( - ...v.result.objects - .filter((object: any) => !object?.output?.type) - .map((object: any) => MultisigOutput.fromIndexer(object)) - ) + cells.push(...v.result.objects) nextMultisigConfigs.push(currentMultisigConfigs[idx]) } }) currentMultisigConfigs = nextMultisigConfigs } - return liveCells + return cells + } + + static async getLiveCells(multisigConfigs: MultisigConfig[]) { + const cells = await MultisigService.getCells(multisigConfigs) + return cells.filter((object: any) => !object?.output?.type).map((object: any) => MultisigOutput.fromIndexer(object)) } static async saveLiveMultisigOutput() { @@ -166,6 +173,41 @@ export default class MultisigService { } } + static async saveMultisigDaoTx(multisigConfigs: MultisigConfig[]) { + const cells = await MultisigService.getCells(multisigConfigs) + if (cells.length) { + const daoTxHash = new Set() + cells.forEach((cell: any) => { + if (cell?.output?.type?.code_hash === SystemScriptInfo.DAO_CODE_HASH) { + daoTxHash.add(cell.out_point.tx_hash) + } + }) + if (daoTxHash.size > 0) { + const network = NetworksService.getInstance().getCurrent() + const rpcService = new RpcService(network.remote, network.type) + for (const txHash of daoTxHash) { + const txWithStatus: TransactionWithStatus | undefined | { transaction: null; txStatus: TxStatus } = + await rpcService.getTransaction(txHash) + if (txWithStatus?.transaction) { + const tx = Transaction.fromSDK(txWithStatus.transaction) + tx.blockHash = txWithStatus.txStatus.blockHash || undefined + if (tx.blockHash) { + const header = await rpcService.getHeader(tx.blockHash) + tx.timestamp = header?.timestamp + tx.blockNumber = header?.number + } + tx.outputsData.forEach((item, index) => { + if (item === DAO_DATA) { + tx.outputs[index].daoData = DAO_DATA + } + }) + await TransactionPersistor.saveFetchTx(tx) + } + } + } + } + } + static async getMultisigTransactionHashList(multisigConfigs: MultisigConfig[]) { const multisigOutputTxHashList = new Set() const addressCursorMap: Map = new Map() @@ -302,6 +344,7 @@ export default class MultisigService { try { const multisigConfigs = await getConnection().getRepository(MultisigConfig).createQueryBuilder().getMany() await MultisigService.saveLiveMultisigOutput() + await MultisigService.saveMultisigDaoTx(multisigConfigs) await MultisigService.deleteDeadMultisigOutput(multisigConfigs) await MultisigService.saveMultisigSyncBlockNumber(multisigConfigs, lastestBlockNumber) MultisigOutputChangedSubject.getSubject().next('update') diff --git a/packages/neuron-wallet/src/utils/const.ts b/packages/neuron-wallet/src/utils/const.ts index 28ed3e21c6..25b9c5cd31 100644 --- a/packages/neuron-wallet/src/utils/const.ts +++ b/packages/neuron-wallet/src/utils/const.ts @@ -19,6 +19,7 @@ export const DEFAULT_ARGS_LENGTH = 42 export const LOCKTIME_ARGS_LENGTH = 58 export const CHEQUE_ARGS_LENGTH = 82 export const CKB_NODE_DATA_SIZE_BUFFER_RATIO = 1.2 +export const DAO_DATA = '0x0000000000000000' export enum ResponseCode { Fail, From eb20d1907b970a7222ade0af76370fdebe263458 Mon Sep 17 00:00:00 2001 From: chenyan Date: Fri, 14 Mar 2025 19:59:30 +0800 Subject: [PATCH 02/22] fix: comments --- .../src/components/MultisigAddress/index.tsx | 30 ++++++++++--------- .../src/components/WithdrawDialog/index.tsx | 2 +- .../WithdrawDialog/withdrawDialog.module.scss | 2 +- packages/neuron-ui/src/locales/ar.json | 2 +- packages/neuron-ui/src/locales/en.json | 2 +- packages/neuron-ui/src/locales/es.json | 2 +- packages/neuron-ui/src/locales/fr.json | 2 +- packages/neuron-ui/src/locales/zh-tw.json | 2 +- packages/neuron-ui/src/locales/zh.json | 2 +- .../neuron-wallet/src/services/multisig.ts | 8 ++--- 10 files changed, 28 insertions(+), 26 deletions(-) diff --git a/packages/neuron-ui/src/components/MultisigAddress/index.tsx b/packages/neuron-ui/src/components/MultisigAddress/index.tsx index f8defd8bf0..653a0f6d95 100644 --- a/packages/neuron-ui/src/components/MultisigAddress/index.tsx +++ b/packages/neuron-ui/src/components/MultisigAddress/index.tsx @@ -205,21 +205,23 @@ const MultisigAddress = () => { ) const daoDisabledMessage = useMemo(() => { - if (wallet.device) { - if ( - (daoDepositAction.depositFromMultisig && daoDepositAction.isDialogOpen) || - (daoWithdrawAction.withdrawFromMultisig && daoWithdrawAction.isDialogOpen) - ) { - const multisigConfig = daoDepositAction.depositFromMultisig || daoWithdrawAction.withdrawFromMultisig - if (!multisigConfig) return '' - const { canSign } = getMultisigSignStatus({ - multisigConfig, - addresses, - }) - - return canSign ? 'dao-ledger-notice' : 'dao-hardware-not-match' - } + if (!wallet.device) return '' + + const multisigConfig = daoDepositAction.depositFromMultisig || daoWithdrawAction.withdrawFromMultisig + if (!multisigConfig) return '' + + if ( + (daoDepositAction.depositFromMultisig && daoDepositAction.isDialogOpen) || + (daoWithdrawAction.withdrawFromMultisig && daoWithdrawAction.isDialogOpen) + ) { + const { canSign } = getMultisigSignStatus({ + multisigConfig, + addresses, + }) + + return canSign ? 'dao-ledger-notice' : 'dao-hardware-not-match' } + return '' }, [daoDepositAction, daoWithdrawAction, wallet.device, addresses]) diff --git a/packages/neuron-ui/src/components/WithdrawDialog/index.tsx b/packages/neuron-ui/src/components/WithdrawDialog/index.tsx index db6c6035cd..d5d5545d4a 100644 --- a/packages/neuron-ui/src/components/WithdrawDialog/index.tsx +++ b/packages/neuron-ui/src/components/WithdrawDialog/index.tsx @@ -75,7 +75,7 @@ const WithdrawDialog = ({ (Number(currentEpochInfo.number) + Number(currentEpochInfo.index) / Number(currentEpochInfo.length)) ).toFixed(1) const message = - epochs >= 0 ? ( + epochs > 5 ? ( <> {t('nervos-dao.notice-wait-time', { diff --git a/packages/neuron-ui/src/components/WithdrawDialog/withdrawDialog.module.scss b/packages/neuron-ui/src/components/WithdrawDialog/withdrawDialog.module.scss index c406f379ae..c3dcb2feea 100644 --- a/packages/neuron-ui/src/components/WithdrawDialog/withdrawDialog.module.scss +++ b/packages/neuron-ui/src/components/WithdrawDialog/withdrawDialog.module.scss @@ -60,7 +60,7 @@ margin: 44px 0 0 0; border: 1px solid rgba(252, 136, 0, 0.2); padding: 8px 36px; - width: max-content; + max-width: 760px; border-radius: 4px; background: #fff6eb; color: #f68c2a; diff --git a/packages/neuron-ui/src/locales/ar.json b/packages/neuron-ui/src/locales/ar.json index c0891e05be..03433f7d16 100644 --- a/packages/neuron-ui/src/locales/ar.json +++ b/packages/neuron-ui/src/locales/ar.json @@ -1171,7 +1171,7 @@ "daoWithdraw": "سحب من DAO" } }, - "dao-ledger-notice": "تدعم محافظ Ledger حاليًا توقيع معاملات DAO فقط، ولا تدعم بدء الإيداعات أو السحوبات. يرجى التحقق من التحديثات المستقبلية.", + "dao-ledger-notice": "محافظ Ledger لا تدعم حاليًا معاملات DAO لعناوين التوقيع المتعدد. يُرجى استخدام عنوان بتوقيع فردي أو الانتظار للتحديثات المستقبلية.", "dao-hardware-not-match": "المحفظة الصلبة المتصلة حاليًا لا تتطابق مع المحفظة الحالية.", "import-dialog": { "actions": { diff --git a/packages/neuron-ui/src/locales/en.json b/packages/neuron-ui/src/locales/en.json index 62856f24c0..367b55d3e1 100644 --- a/packages/neuron-ui/src/locales/en.json +++ b/packages/neuron-ui/src/locales/en.json @@ -1171,7 +1171,7 @@ "daoWithdraw": "DAO Withdraw" } }, - "dao-ledger-notice": "Ledger wallets currently support only signing DAO transactions, not initiating deposits or withdrawals. Please check back for future updates.", + "dao-ledger-notice": "Ledger wallets currently do not support DAO transactions for multisig addresses. Please use a single-signature address or await future updates.", "dao-hardware-not-match": "The hardware wallet currently connected does not match the current wallet.", "import-dialog": { "actions": { diff --git a/packages/neuron-ui/src/locales/es.json b/packages/neuron-ui/src/locales/es.json index a06a1d2c55..c960b4e222 100644 --- a/packages/neuron-ui/src/locales/es.json +++ b/packages/neuron-ui/src/locales/es.json @@ -1154,7 +1154,7 @@ "daoWithdraw": "Retirar de DAO" } }, - "dao-ledger-notice": "Las billeteras Ledger actualmente solo admiten la firma de transacciones DAO, no la iniciación de depósitos o retiros. Vuelva a consultar para futuras actualizaciones.", + "dao-ledger-notice": "Las billeteras Ledger actualmente no admiten transacciones DAO para direcciones multifirma. Por favor, use una dirección de firma única o espere futuras actualizaciones.", "dao-hardware-not-match": "La billetera de hardware conectada actualmente no coincide con la billetera actual.", "import-dialog": { "actions": { diff --git a/packages/neuron-ui/src/locales/fr.json b/packages/neuron-ui/src/locales/fr.json index 02cddfdf75..43441d83a7 100644 --- a/packages/neuron-ui/src/locales/fr.json +++ b/packages/neuron-ui/src/locales/fr.json @@ -1161,7 +1161,7 @@ "daoWithdraw": "Retirer de DAO" } }, - "dao-ledger-notice": "Les portefeuilles Ledger prennent actuellement en charge uniquement la signature des transactions DAO, mais pas l’initiation des dépôts ou des retraits. Veuillez vérifier les mises à jour futures.", + "dao-ledger-notice": "Les portefeuilles Ledger ne prennent actuellement pas en charge les transactions DAO pour les adresses multisignatures. Veuillez utiliser une adresse à signature unique ou attendre les futures mises à jour.", "dao-hardware-not-match": "Le portefeuille matériel actuellement connecté ne correspond pas au portefeuille actuel.", "import-dialog": { "actions": { diff --git a/packages/neuron-ui/src/locales/zh-tw.json b/packages/neuron-ui/src/locales/zh-tw.json index f254fc07aa..17c8bd7d3e 100644 --- a/packages/neuron-ui/src/locales/zh-tw.json +++ b/packages/neuron-ui/src/locales/zh-tw.json @@ -1164,7 +1164,7 @@ "daoWithdraw": "從 DAO 取出" } }, - "dao-ledger-notice": "Ledger 錢包目前僅支援簽署 DAO 交易,無法發起存款或提取。請關注未來的更新。", + "dao-ledger-notice": "Ledger 錢包目前不支援多重簽名地址的 DAO 交易。請使用單一簽名地址或等待未來的更新。", "dao-hardware-not-match": "當前連接的硬體錢包與當前錢包不匹配。", "import-dialog": { "actions": { diff --git a/packages/neuron-ui/src/locales/zh.json b/packages/neuron-ui/src/locales/zh.json index 04414759a7..dd79782209 100644 --- a/packages/neuron-ui/src/locales/zh.json +++ b/packages/neuron-ui/src/locales/zh.json @@ -1163,7 +1163,7 @@ "daoWithdraw": "从 DAO 取出" } }, - "dao-ledger-notice": "Ledger 钱包目前仅支持签署 DAO 交易,无法发起存款或提取。请关注未来的更新。", + "dao-ledger-notice": "Ledger 钱包目前不支持多签地址的 DAO 交易。请使用单签地址或等待未来的更新。", "dao-hardware-not-match": "当前连接的硬件钱包与当前钱包不匹配。", "import-dialog": { "actions": { diff --git a/packages/neuron-wallet/src/services/multisig.ts b/packages/neuron-wallet/src/services/multisig.ts index 22bd92ce02..1df8ec6e3f 100644 --- a/packages/neuron-wallet/src/services/multisig.ts +++ b/packages/neuron-wallet/src/services/multisig.ts @@ -114,7 +114,7 @@ export default class MultisigService { } static async getCells(multisigConfigs: MultisigConfig[]) { - const cells: any[] = [] + const cells: RPC.IndexerCell[] = [] const addressCursorMap: Map = new Map() let currentMultisigConfigs = MultisigService.removeDulpicateConfig(multisigConfigs) const network = NetworksService.getInstance().getCurrent() @@ -161,7 +161,7 @@ export default class MultisigService { static async getLiveCells(multisigConfigs: MultisigConfig[]) { const cells = await MultisigService.getCells(multisigConfigs) - return cells.filter((object: any) => !object?.output?.type).map((object: any) => MultisigOutput.fromIndexer(object)) + return cells.filter(object => !object?.output?.type).map(object => MultisigOutput.fromIndexer(object)) } static async saveLiveMultisigOutput() { @@ -177,8 +177,8 @@ export default class MultisigService { const cells = await MultisigService.getCells(multisigConfigs) if (cells.length) { const daoTxHash = new Set() - cells.forEach((cell: any) => { - if (cell?.output?.type?.code_hash === SystemScriptInfo.DAO_CODE_HASH) { + cells.forEach(cell => { + if (cell.output?.type?.code_hash === SystemScriptInfo.DAO_CODE_HASH) { daoTxHash.add(cell.out_point.tx_hash) } }) From 80215f48b00518ccd01a2cfc2738bb6ff028220c Mon Sep 17 00:00:00 2001 From: chenyan Date: Sat, 15 Mar 2025 17:15:17 +0800 Subject: [PATCH 03/22] fix: comments --- packages/neuron-ui/src/components/MultisigAddress/index.tsx | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/packages/neuron-ui/src/components/MultisigAddress/index.tsx b/packages/neuron-ui/src/components/MultisigAddress/index.tsx index 653a0f6d95..640b548392 100644 --- a/packages/neuron-ui/src/components/MultisigAddress/index.tsx +++ b/packages/neuron-ui/src/components/MultisigAddress/index.tsx @@ -207,15 +207,13 @@ const MultisigAddress = () => { const daoDisabledMessage = useMemo(() => { if (!wallet.device) return '' - const multisigConfig = daoDepositAction.depositFromMultisig || daoWithdrawAction.withdrawFromMultisig - if (!multisigConfig) return '' - if ( (daoDepositAction.depositFromMultisig && daoDepositAction.isDialogOpen) || (daoWithdrawAction.withdrawFromMultisig && daoWithdrawAction.isDialogOpen) ) { + const multisigConfig = daoDepositAction.depositFromMultisig || daoWithdrawAction.withdrawFromMultisig const { canSign } = getMultisigSignStatus({ - multisigConfig, + multisigConfig: multisigConfig!, addresses, }) From 4df15053411357b425b0753c448a60e30c3bf09e Mon Sep 17 00:00:00 2001 From: chenyan Date: Sat, 15 Mar 2025 17:27:04 +0800 Subject: [PATCH 04/22] fix --- .../neuron-wallet/src/services/multisig.ts | 50 +++++++++++++------ 1 file changed, 35 insertions(+), 15 deletions(-) diff --git a/packages/neuron-wallet/src/services/multisig.ts b/packages/neuron-wallet/src/services/multisig.ts index 1df8ec6e3f..c90b995a37 100644 --- a/packages/neuron-wallet/src/services/multisig.ts +++ b/packages/neuron-wallet/src/services/multisig.ts @@ -19,6 +19,7 @@ import RpcService from '../services/rpc-service' import TransactionWithStatus from '../models/chain/transaction-with-status' import TxStatus from '../models/chain/tx-status' import SystemScriptInfo from '../models/system-script-info' +import OutPoint from '../models/chain/out-point' const max64Int = '0x' + 'f'.repeat(16) export default class MultisigService { @@ -182,25 +183,44 @@ export default class MultisigService { daoTxHash.add(cell.out_point.tx_hash) } }) + + const network = NetworksService.getInstance().getCurrent() + const rpcService = new RpcService(network.remote, network.type) + + const getTx = async (txHash: string) => { + const txWithStatus: TransactionWithStatus | undefined | { transaction: null; txStatus: TxStatus } = + await rpcService.getTransaction(txHash) + if (txWithStatus?.transaction) { + const tx = Transaction.fromSDK(txWithStatus.transaction) + tx.blockHash = txWithStatus.txStatus.blockHash || undefined + if (tx.blockHash) { + const header = await rpcService.getHeader(tx.blockHash) + tx.timestamp = header?.timestamp + tx.blockNumber = header?.number + } + return tx + } + } + if (daoTxHash.size > 0) { - const network = NetworksService.getInstance().getCurrent() - const rpcService = new RpcService(network.remote, network.type) for (const txHash of daoTxHash) { - const txWithStatus: TransactionWithStatus | undefined | { transaction: null; txStatus: TxStatus } = - await rpcService.getTransaction(txHash) - if (txWithStatus?.transaction) { - const tx = Transaction.fromSDK(txWithStatus.transaction) - tx.blockHash = txWithStatus.txStatus.blockHash || undefined - if (tx.blockHash) { - const header = await rpcService.getHeader(tx.blockHash) - tx.timestamp = header?.timestamp - tx.blockNumber = header?.number - } - tx.outputsData.forEach((item, index) => { - if (item === DAO_DATA) { - tx.outputs[index].daoData = DAO_DATA + const tx = await getTx(txHash) + if (tx) { + const previousTxHashes: string[] = [] + tx.outputs.forEach((output, index) => { + if (output.type?.codeHash === SystemScriptInfo.DAO_CODE_HASH) { + output.daoData = tx.outputsData[index] + if (tx.outputsData[index] !== DAO_DATA) { + const previousTxHash = tx.inputs[index].previousOutput!.txHash + previousTxHashes.push(previousTxHash) + output.setDepositOutPoint(new OutPoint(previousTxHash, tx.inputs[index].previousOutput!.index)) + } } }) + for (const previousTxHash of previousTxHashes) { + const previousTx = await getTx(previousTxHash) + if (previousTx) await TransactionPersistor.saveFetchTx(previousTx) + } await TransactionPersistor.saveFetchTx(tx) } } From 9df0c7d5547a44e981aad1422f7f3e57ee73bbb8 Mon Sep 17 00:00:00 2001 From: chenyan Date: Sat, 15 Mar 2025 17:33:01 +0800 Subject: [PATCH 05/22] fix --- packages/neuron-wallet/src/services/transaction-sender.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/packages/neuron-wallet/src/services/transaction-sender.ts b/packages/neuron-wallet/src/services/transaction-sender.ts index 5ac0ec0ca6..89a6581957 100644 --- a/packages/neuron-wallet/src/services/transaction-sender.ts +++ b/packages/neuron-wallet/src/services/transaction-sender.ts @@ -896,8 +896,7 @@ export default class TransactionSender { multisigConfig.m, multisigConfig.n ) - const multisigAddresses = scriptToAddress(lockScript, NetworksService.getInstance().isMainnet()) - output = new Output(outputCapacity.toString(), AddressParser.parse(multisigAddresses), undefined, '0x') + output = new Output(outputCapacity.toString(), lockScript, undefined, '0x') } else { const wallet = WalletService.getInstance().get(walletID) const address = await wallet.getNextAddress() @@ -919,7 +918,10 @@ export default class TransactionSender { withdrawOutput.lock ) - const withdrawWitnessArgs: WitnessArgs = new WitnessArgs(WitnessArgs.EMPTY_LOCK, '0x0000000000000000') + const withdrawWitnessArgs: WitnessArgs = new WitnessArgs( + multisigConfig ? '' : WitnessArgs.EMPTY_LOCK, + '0x0000000000000000' + ) const tx: Transaction = Transaction.fromObject({ version: '0', cellDeps: [cellDep, daoCellDep], From 4fed46e2470dc8f71ee9fc2273d990653c4c7ef8 Mon Sep 17 00:00:00 2001 From: chenyan Date: Mon, 17 Mar 2025 17:20:33 +0800 Subject: [PATCH 06/22] fix: checkbox is not displaying properly --- packages/neuron-ui/src/styles/mixin.scss | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/neuron-ui/src/styles/mixin.scss b/packages/neuron-ui/src/styles/mixin.scss index 9dfa2f6de3..4d36e5f6f9 100644 --- a/packages/neuron-ui/src/styles/mixin.scss +++ b/packages/neuron-ui/src/styles/mixin.scss @@ -196,23 +196,23 @@ height: 20px; padding-left: 30px; line-height: 20px; - background: url('../widgets/Icons/Checkbox.svg') no-repeat left top; + background: url('widgets/Icons/Checkbox.svg') no-repeat left top; user-select: none; } input[type='checkbox']:checked + span { - background: url('../widgets/Icons/CheckboxSelected.svg') no-repeat left top; + background: url('widgets/Icons/CheckboxSelected.svg') no-repeat left top; } input[type='checkbox']:disabled:checked + span { - background: url('../widgets/Icons/CheckboxSelected.svg') no-repeat left top; + background: url('widgets/Icons/CheckboxSelected.svg') no-repeat left top; opacity: 0.5; } input[type='checkbox']:disabled + span { cursor: not-allowed; - background: url('../widgets/Icons/CheckboxDisabled.svg') no-repeat left top; + background: url('widgets/Icons/CheckboxDisabled.svg') no-repeat left top; @media (prefers-color-scheme: dark) { - background: url('../widgets/Icons/CheckboxDisabledDark.svg') no-repeat left top; + background: url('widgets/Icons/CheckboxDisabledDark.svg') no-repeat left top; } } } From d5b81be90a0be3538df2d744a64652673f6122c6 Mon Sep 17 00:00:00 2001 From: chenyan Date: Wed, 23 Apr 2025 23:43:54 +0800 Subject: [PATCH 07/22] feat: Update for new multisig script --- .../src/components/MultisigAddress/hooks.ts | 110 +++++++++++++- .../src/components/MultisigAddress/index.tsx | 134 +++++++++++++++--- .../multisigAddress.module.scss | 72 +++++++++- .../src/components/ScriptTag/index.tsx | 3 +- packages/neuron-ui/src/locales/ar.json | 10 +- packages/neuron-ui/src/locales/en.json | 10 +- packages/neuron-ui/src/locales/es.json | 10 +- packages/neuron-ui/src/locales/fr.json | 10 +- packages/neuron-ui/src/locales/zh-tw.json | 10 +- packages/neuron-ui/src/locales/zh.json | 10 +- packages/neuron-ui/src/services/localCache.ts | 11 ++ .../neuron-ui/src/services/remote/multisig.ts | 6 + .../src/services/remote/remoteApiWrapper.ts | 1 + packages/neuron-ui/src/utils/enums.ts | 10 +- .../src/utils/getLockSupportShortAddress.ts | 16 ++- packages/neuron-ui/src/utils/multisig.ts | 20 ++- .../neuron-ui/src/utils/validators/address.ts | 2 + .../src/widgets/AlertDialog/index.tsx | 8 +- .../src/widgets/Icons/Regenerate.svg | 4 + packages/neuron-ui/src/widgets/Icons/icon.tsx | 2 + packages/neuron-wallet/.env | 6 +- packages/neuron-wallet/src/controllers/api.ts | 4 + .../neuron-wallet/src/controllers/multisig.ts | 119 +++++++++++++--- .../src/database/address/meta.ts | 5 +- .../chain/entities/multisig-config.ts | 8 ++ .../1681360188494-AddTypeSyncProgress.ts | 2 +- .../1744960856059-AddLockCodeHash.ts | 16 +++ .../src/database/chain/ormconfig.ts | 2 + packages/neuron-wallet/src/locales/en.ts | 2 +- packages/neuron-wallet/src/locales/es.ts | 2 +- .../src/models/multisig-config.ts | 18 ++- packages/neuron-wallet/src/models/multisig.ts | 4 +- .../src/models/system-script-info.ts | 56 ++++++-- packages/neuron-wallet/src/services/cells.ts | 72 +++++++++- .../neuron-wallet/src/services/multisig.ts | 19 +-- .../src/services/transaction-sender.ts | 33 +++-- .../src/services/tx/transaction-generator.ts | 42 ++++-- packages/neuron-wallet/src/utils/multisig.ts | 10 +- .../neuron-wallet/src/utils/systemScripts.ts | 4 - .../tests/controllers/multisig.test.ts | 2 + .../tests/models/system-script-info.test.ts | 15 +- .../tests/services/cells.test.ts | 62 ++++---- .../tests/services/multisig.test.ts | 40 ++++-- .../services/tx/transaction-generator.test.ts | 10 +- .../services/tx/transaction-persistor.test.ts | 3 +- .../services/tx/transaction-sender.test.ts | 30 ++-- packages/neuron-wallet/tests/setup.ts | 5 +- .../tests/utils/multisig.test.ts | 3 +- 48 files changed, 867 insertions(+), 186 deletions(-) create mode 100644 packages/neuron-ui/src/widgets/Icons/Regenerate.svg create mode 100644 packages/neuron-wallet/src/database/chain/migrations/1744960856059-AddLockCodeHash.ts diff --git a/packages/neuron-ui/src/components/MultisigAddress/hooks.ts b/packages/neuron-ui/src/components/MultisigAddress/hooks.ts index 55dcb2b2d6..2e2dbcd369 100644 --- a/packages/neuron-ui/src/components/MultisigAddress/hooks.ts +++ b/packages/neuron-ui/src/components/MultisigAddress/hooks.ts @@ -1,5 +1,13 @@ import React, { useCallback, useState, useEffect, useMemo } from 'react' -import { isSuccessResponse, getMultisigAddress, DefaultLockInfo, addressToScript, scriptToAddress } from 'utils' +import { + isSuccessResponse, + getMultisigAddress, + DefaultLockInfo, + addressToScript, + scriptToAddress, + LegacyMultiSigLockInfo, + MultiSigLockInfo, +} from 'utils' import { MultisigOutputUpdate } from 'services/subjects' import { MultisigConfig, @@ -11,11 +19,13 @@ import { exportMultisigConfig, deleteMultisigConfig, getMultisigBalances, + getMultisigDAOBalances, loadMultisigTxJson, OfflineSignJSON, getMultisigSyncProgress, } from 'services/remote' import { computeScriptHash } from '@ckb-lumos/lumos/utils' +import { remaindRegenerateMultisigAddress } from 'services/localCache' export const useSearch = (clearSelected: () => void, onFilterConfig: (searchKey: string) => void) => { const [keywords, setKeywords] = useState('') @@ -55,13 +65,45 @@ export const useSearch = (clearSelected: () => void, onFilterConfig: (searchKey: export const useConfigManage = ({ walletId, isMainnet }: { walletId: string; isMainnet: boolean }) => { const [entities, setEntities] = useState([]) const saveConfig = useCallback( - ({ m, n, r, addresses }: { m: number; n: number; r: number; addresses: string[] }) => { + ({ + m, + n, + r, + addresses, + lockCodeHash = MultiSigLockInfo.CodeHash, + }: { + m: number + n: number + r: number + addresses: string[] + lockCodeHash?: string + }) => { return saveMultisigConfig({ m, n, r, blake160s: addresses.map(v => addressToScript(v).args), walletId, + lockCodeHash, + }).then(res => { + if (isSuccessResponse(res)) { + setEntities(v => (res.result ? [res.result, ...v] : v)) + } else { + throw new Error(typeof res.message === 'string' ? res.message : res.message.content) + } + }) + }, + [walletId, setEntities] + ) + const regenerateConfig = useCallback( + (config: MultisigConfig) => { + return saveMultisigConfig({ + m: config.m, + n: config.n, + r: config.r, + blake160s: config.blake160s, + walletId, + lockCodeHash: MultiSigLockInfo.CodeHash, }).then(res => { if (isSuccessResponse(res)) { setEntities(v => (res.result ? [res.result, ...v] : v)) @@ -132,7 +174,8 @@ export const useConfigManage = ({ walletId, isMainnet }: { walletId: string; isM { isMainnet } ) ), - fullPayload: getMultisigAddress(entity.blake160s, entity.r, entity.m, entity.n, isMainnet), + fullPayload: getMultisigAddress(entity.blake160s, entity.r, entity.m, entity.n, isMainnet, entity.lockCodeHash), + isLegacy: entity.lockCodeHash === LegacyMultiSigLockInfo.CodeHash, })), [entities, isMainnet] ) @@ -145,6 +188,7 @@ export const useConfigManage = ({ walletId, isMainnet }: { walletId: string; isM ) return { saveConfig, + regenerateConfig, allConfigs, onUpdateConfigAlias, onUpdateConfig, @@ -267,6 +311,47 @@ const useDeleteAction = (deleteConfigById: (id: number) => void) => { } } +const useRegenerateAction = (regenerateConfig: (config: MultisigConfig) => Promise) => { + const [isDialogOpen, setIsDialogOpen] = useState(false) + const [config, setConfig] = useState() + const [isNoRemind, setIsNoRemind] = useState(false) + const [regenerateErrorMessage, setRegenerateErrorMessage] = useState() + + const handleCheckbox = useCallback( + (e: React.ChangeEvent) => { + const { checked } = e.target + setIsNoRemind(checked) + remaindRegenerateMultisigAddress.save(checked) + }, + [setIsNoRemind] + ) + + const regenerate = useCallback( + async (multisigConfig: MultisigConfig) => { + try { + await regenerateConfig(multisigConfig) + } catch (error) { + setIsDialogOpen(true) + setRegenerateErrorMessage(error instanceof Error ? error.message : '') + } + }, + [setIsDialogOpen, config, isNoRemind] + ) + const closeDialog = useCallback(() => { + setIsDialogOpen(false) + }, [setIsDialogOpen]) + return { + action: regenerate, + closeDialog, + config, + setConfig, + isDialogOpen, + isNoRemind, + handleCheckbox, + regenerateErrorMessage, + } +} + const useApproveAction = () => { const [isDialogOpen, setIsDialogOpen] = useState(false) const [multisigConfig, setMultisigConfig] = useState() @@ -342,7 +427,13 @@ const useDaoWithdrawAction = () => { } } -export const useActions = ({ deleteConfigById }: { deleteConfigById: (id: number) => void }) => { +export const useActions = ({ + deleteConfigById, + regenerateConfig, +}: { + deleteConfigById: (id: number) => void + regenerateConfig: (config: MultisigConfig) => Promise +}) => { return { deleteAction: useDeleteAction(deleteConfigById), infoAction: useInfoAction(), @@ -350,6 +441,7 @@ export const useActions = ({ deleteConfigById }: { deleteConfigById: (id: number approveAction: useApproveAction(), daoDepositAction: useDaoDepositAction(), daoWithdrawAction: useDaoWithdrawAction(), + regenerateAction: useRegenerateAction(regenerateConfig), } } @@ -365,6 +457,7 @@ export const useSubscription = ({ isLightClient: boolean }) => { const [multisigBanlances, setMultisigBanlances] = useState>({}) + const [multisigDaoBalances, setMultisigDaoBalances] = useState>({}) const [multisigSyncProgress, setMultisigSyncProgress] = useState>({}) const getAndSaveMultisigBalances = useCallback(() => { getMultisigBalances({ isMainnet, multisigAddresses: configs.map(v => v.fullPayload) }).then(res => { @@ -372,7 +465,12 @@ export const useSubscription = ({ setMultisigBanlances(res.result) } }) - }, [setMultisigBanlances, isMainnet, configs]) + getMultisigDAOBalances({ isMainnet, multisigAddresses: configs.map(v => v.fullPayload) }).then(res => { + if (isSuccessResponse(res) && res.result) { + setMultisigDaoBalances(res.result) + } + }) + }, [setMultisigBanlances, setMultisigDaoBalances, isMainnet, configs]) const hashToPayload = useMemo( () => configs.reduce>( @@ -415,7 +513,7 @@ export const useSubscription = ({ clearInterval(interval) } }, [isLightClient, getAndSaveMultisigSyncProgress]) - return { multisigBanlances, multisigSyncProgress } + return { multisigBanlances, multisigDaoBalances, multisigSyncProgress } } export const useCancelWithLightClient = () => { diff --git a/packages/neuron-ui/src/components/MultisigAddress/index.tsx b/packages/neuron-ui/src/components/MultisigAddress/index.tsx index 640b548392..3426c13d93 100644 --- a/packages/neuron-ui/src/components/MultisigAddress/index.tsx +++ b/packages/neuron-ui/src/components/MultisigAddress/index.tsx @@ -37,6 +37,8 @@ import { LineDownArrow, DAODeposit, DAOWithdrawal, + Attention, + Regenerate, } from 'widgets/Icons/icon' import { getHeader } from 'services/chain' import AttentionCloseDialog from 'widgets/Icons/Attention.png' @@ -48,6 +50,7 @@ import Button from 'widgets/Button' import SetStartBlockNumberDialog from 'components/SetStartBlockNumberDialog' import { type TFunction } from 'i18next' import hooks from 'components/NervosDAO/hooks' +import { remaindRegenerateMultisigAddress } from 'services/localCache' import { useSearch, useConfigManage, @@ -86,6 +89,10 @@ const tableActions = [ key: 'daoWithdraw', icon: , }, + { + key: 'regenerate', + icon: , + }, ] const LearnMore = React.memo(({ t }: { t: TFunction }) => ( @@ -133,20 +140,24 @@ const MultisigAddress = () => { onImportConfig, configs, onFilterConfig, + regenerateConfig, } = useConfigManage({ walletId, isMainnet, }) - const { multisigBanlances, multisigSyncProgress } = useSubscription({ + const { multisigBanlances, multisigDaoBalances, multisigSyncProgress } = useSubscription({ walletId, isMainnet, configs: allConfigs, isLightClient, }) - const { deleteAction, infoAction, sendAction, approveAction, daoDepositAction, daoWithdrawAction } = useActions({ - deleteConfigById, - }) + const { deleteAction, infoAction, sendAction, approveAction, daoDepositAction, daoWithdrawAction, regenerateAction } = + useActions({ + deleteConfigById, + regenerateConfig, + }) const [showDeleteDialog, setShowDeleteDialog] = useState(false) + const [showRegenerateDialog, setShowRegenerateDialog] = useState(false) const { suggestFeeRate } = useGetCountDownAndFeeRateStats() const [globalAPC, setGlobalAPC] = useState(0) const [genesisBlockTimestamp, setGenesisBlockTimestamp] = useState(undefined) @@ -177,6 +188,14 @@ const MultisigAddress = () => { case 'daoWithdraw': daoWithdrawAction.action(multisigConfig) break + case 'regenerate': + if (remaindRegenerateMultisigAddress.get()) { + regenerateAction.action(multisigConfig) + } else { + regenerateAction.setConfig(multisigConfig) + setShowRegenerateDialog(true) + } + break default: break } @@ -195,13 +214,29 @@ const MultisigAddress = () => { })), [t, selectIds] ) - const listNoBalanceActionOptions = useMemo( - () => - listActionOptions.map(item => ({ - ...item, - disabled: item.disabled || item.key === 'send', - })), - [listActionOptions] + + const getListActionOptions = useCallback( + (config: MultisigConfig) => { + const options: typeof listActionOptions = [] + listActionOptions.forEach(item => { + if (config.isLegacy && ['daoDeposit', 'daoWithdraw'].includes(item.key)) { + return + } + if (!config.isLegacy && ['regenerate'].includes(item.key)) { + return + } + if (!multisigBanlances[config.fullPayload] || multisigBanlances[config.fullPayload] === '0') { + options.push({ + ...item, + disabled: item.disabled || item.key === 'send', + }) + } else { + options.push(item) + } + }) + return options + }, + [listActionOptions, multisigBanlances] ) const daoDisabledMessage = useMemo(() => { @@ -297,6 +332,10 @@ const MultisigAddress = () => { } }, [t, setNotice, daoDepositAction, daoWithdrawAction]) + const showDaoMultisigScriptNotice = useMemo(() => { + return allConfigs.some(config => config.isLegacy) + }, [allConfigs]) + return (
{ showFooter={false} >
+ {showDaoMultisigScriptNotice && ( +
+ +
+ openExternal('https://github.com/Magickbase/neuron-public-issues/issues/457')} + > + {' '} + , + ]} + /> +
+
+ )}
@@ -378,6 +435,7 @@ const MultisigAddress = () => { return (
{item.fullPayload.slice(0, 5)}...{item.fullPayload.slice(-5)} + {item.isLegacy &&
Legacy
}
) }, @@ -460,7 +518,15 @@ const MultisigAddress = () => { render(_, __, item, show) { return (
- {show ? shannonToCKBFormatter(multisigBanlances[item.fullPayload] || '0') : HIDE_BALANCE} CKB +
+ {show ? shannonToCKBFormatter(multisigBanlances[item.fullPayload] || '0') : HIDE_BALANCE} CKB +
+ {!item.isLegacy && multisigDaoBalances[item.fullPayload] && ( +
+ (Nervos DAO: + {show ? shannonToCKBFormatter(multisigDaoBalances[item.fullPayload]) : HIDE_BALANCE} CKB) +
+ )}
) }, @@ -478,10 +544,7 @@ const MultisigAddress = () => { className={styles.tipContent} tip={
- {(!multisigBanlances[item.fullPayload] || multisigBanlances[item.fullPayload] === '0' - ? listNoBalanceActionOptions - : listActionOptions - ).map(({ key, label, icon, disabled }) => ( + {getListActionOptions(item).map(({ key, label, icon, disabled }) => (
+ } + type="warning" + onCancel={() => setShowRegenerateDialog(false)} + okText={t('multisig-address.regenerate-dialog.title')} + onOk={() => { + regenerateAction.action(regenerateAction.config!) + setShowRegenerateDialog(false) + }} + /> + + + svg { + width: 16px; + flex-shrink: 0; + } + button { + color: var(--warn-text-color); + font-size: 12px; + padding: 0; + font-weight: 500; + display: inline-block; + background: transparent; + border: none; + text-decoration: underline; + cursor: pointer; + } + } + .head { display: flex; align-items: center; @@ -98,6 +129,20 @@ .address { font-family: 'JetBrains Mono'; + position: relative; + + .legacy { + font-size: 10px; + height: 16px; + color: var(--third-text-color); + font-weight: 500; + position: absolute; + top: -16px; + right: -10px; + background: var(--table-head-border-color); + padding: 0 4px; + border-radius: 48px; + } } .descTipRoot { @@ -206,6 +251,15 @@ fill: var(--main-text-color); } } + + &[data-key='regenerate'] { + g, + path { + stroke: var(--main-text-color); + fill: none; + } + } + &:hover { border-radius: 8px; background-color: var(--tooltip-hover-background-color); @@ -220,6 +274,22 @@ text-align: center; } +.regenerateDialog { + width: 568px; + @include checkbox; + .regenerateDialogDetail { + color: var(--main-text-color); + margin-bottom: -24px; + & > div { + margin-bottom: 32px; + } + } + .checkboxWrap { + font-size: 14px; + color: var(--third-text-color); + } +} + .closeMultisigDialog { width: 568px; @@ -277,7 +347,7 @@ &:hover, &:active { path { - stroke: var(--primary-color); + stroke: var(--secondary-text-color); } } } diff --git a/packages/neuron-ui/src/components/ScriptTag/index.tsx b/packages/neuron-ui/src/components/ScriptTag/index.tsx index a6ddd7f0e5..35c298ef8e 100644 --- a/packages/neuron-ui/src/components/ScriptTag/index.tsx +++ b/packages/neuron-ui/src/components/ScriptTag/index.tsx @@ -1,6 +1,7 @@ import React from 'react' import { type CKBComponents } from '@ckb-lumos/lumos/rpc' import { + LegacyMultiSigLockInfo, MultiSigLockInfo, LocktimeLockInfo, DefaultLockInfo, @@ -27,7 +28,7 @@ const ScriptTag = ({ return null } - const commonLockArray = [MultiSigLockInfo, LocktimeLockInfo, DefaultLockInfo] + const commonLockArray = [LegacyMultiSigLockInfo, MultiSigLockInfo, LocktimeLockInfo, DefaultLockInfo] const lockArray: Array> = isMainnet ? [...commonLockArray, AnyoneCanPayLockInfoOnLina, ChequeLockInfoOnLina] diff --git a/packages/neuron-ui/src/locales/ar.json b/packages/neuron-ui/src/locales/ar.json index 03433f7d16..6b17d215b6 100644 --- a/packages/neuron-ui/src/locales/ar.json +++ b/packages/neuron-ui/src/locales/ar.json @@ -1168,7 +1168,8 @@ "approve": "الموافقة", "delete": "حذف", "daoDeposit": "إيداع في DAO", - "daoWithdraw": "سحب من DAO" + "daoWithdraw": "سحب من DAO", + "regenerate": "إعادة إنشاء" } }, "dao-ledger-notice": "محافظ Ledger لا تدعم حاليًا معاملات DAO لعناوين التوقيع المتعدد. يُرجى استخدام عنوان بتوقيع فردي أو الانتظار للتحديثات المستقبلية.", @@ -1193,6 +1194,13 @@ "cancel": "إلغاء", "export": "تصدير المعاملة" }, + "multisig-script-update-notice": "نظرًا لـ<0>ترقية برنامج النص المتعدد التوقيعات، نوصي بإعادة إنشاء عناوين التوقيع المتعدد ونقل الأصول من العنوان القديم لضمان الأداء السليم.", + "regenerate-dialog": { + "title": "إعادة الإنشاء", + "detail": "سيتم إنشاء عنوان جديد باستخدام إعدادات عنوان التوقيع المتعدد القديم، مطورًا إلى أحدث تنسيق للبرنامج النصي.", + "donnot-worry": "لا تقلق — لا يزال من الممكن الوصول إلى العنوان القديم.", + "donnot-remind-again": "لا تذكرني مرة أخرى" + }, "multi-details": "تفاصيل عنوان التوقيع المتعدد", "synchronization-abort": "تم إلغاء المزامنة", "synchronization-abort-msg": "سيؤدي مغادرة النافذة الحالية إلى إلغاء مزامنة التوقيع المتعدد، لذا يرجى التأكد قبل المغادرة.", diff --git a/packages/neuron-ui/src/locales/en.json b/packages/neuron-ui/src/locales/en.json index 367b55d3e1..3dfb0b16e4 100644 --- a/packages/neuron-ui/src/locales/en.json +++ b/packages/neuron-ui/src/locales/en.json @@ -1168,7 +1168,8 @@ "approve": "Approve", "delete": "Delete", "daoDeposit": "DAO Deposit", - "daoWithdraw": "DAO Withdraw" + "daoWithdraw": "DAO Withdraw", + "regenerate": "Regenerate" } }, "dao-ledger-notice": "Ledger wallets currently do not support DAO transactions for multisig addresses. Please use a single-signature address or await future updates.", @@ -1193,6 +1194,13 @@ "cancel": "Cancel", "export": "Export Tx" }, + "multisig-script-update-notice": "Due to the <0>multisig script upgrade, we recommend regenerating the multisig addresses and transferring assets from the legacy one to ensure proper functionality. ", + "regenerate-dialog": { + "title": "Regenerate", + "detail": "A new address will be generated using the settings from your legacy multisig address, upgraded to the latest script format.", + "donnot-worry": "Don't worry — the legacy address will still be accessible.", + "donnot-remind-again": "Don't Remind Again" + }, "multi-details": "Multisig address details", "synchronization-abort": "Synchronization Abort", "synchronization-abort-msg": "Leaving the current window will cause the multisig synchronization to be aborted, so please confirm to leave.", diff --git a/packages/neuron-ui/src/locales/es.json b/packages/neuron-ui/src/locales/es.json index c960b4e222..92f576b504 100644 --- a/packages/neuron-ui/src/locales/es.json +++ b/packages/neuron-ui/src/locales/es.json @@ -1151,7 +1151,8 @@ "approve": "Aprobar", "delete": "Eliminar", "daoDeposit": "Depositar en DAO", - "daoWithdraw": "Retirar de DAO" + "daoWithdraw": "Retirar de DAO", + "regenerate": "Regenerar" } }, "dao-ledger-notice": "Las billeteras Ledger actualmente no admiten transacciones DAO para direcciones multifirma. Por favor, use una dirección de firma única o espere futuras actualizaciones.", @@ -1176,6 +1177,13 @@ "cancel": "Cancelar", "export": "Exportar Tx" }, + "multisig-script-update-notice": "Debido a la <0>actualización del script multisig, recomendamos regenerar las direcciones multisig y transferir los activos desde la antigua para garantizar el funcionamiento adecuado.", + "regenerate-dialog": { + "title": "Regenerar", + "detail": "Se generará una nueva dirección utilizando la configuración de tu dirección multisig anterior, actualizada al formato de script más reciente.", + "donnot-worry": "No te preocupes — la dirección antigua seguirá siendo accesible.", + "donnot-remind-again": "No volver a recordar" + }, "multi-details": "Detalles de la dirección multifirma", "synchronization-abort": "Abortar sincronización", "synchronization-abort-msg": "Salir de la ventana actual provocará que se aborte la sincronización de multisig, por favor confirme si desea salir.", diff --git a/packages/neuron-ui/src/locales/fr.json b/packages/neuron-ui/src/locales/fr.json index 43441d83a7..5dc8fa9ead 100644 --- a/packages/neuron-ui/src/locales/fr.json +++ b/packages/neuron-ui/src/locales/fr.json @@ -1158,7 +1158,8 @@ "approve": "Approuver", "delete": "Supprimer", "daoDeposit": "Déposer dans DAO", - "daoWithdraw": "Retirer de DAO" + "daoWithdraw": "Retirer de DAO", + "regenerate": "Régénérer" } }, "dao-ledger-notice": "Les portefeuilles Ledger ne prennent actuellement pas en charge les transactions DAO pour les adresses multisignatures. Veuillez utiliser une adresse à signature unique ou attendre les futures mises à jour.", @@ -1183,6 +1184,13 @@ "cancel": "Annuler", "export": "Exporter Tx" }, + "multisig-script-update-notice": "En raison de la <0>mise à jour du script multisig, nous recommandons de régénérer les adresses multisig et de transférer les actifs depuis l’ancienne adresse pour garantir un bon fonctionnement.", + "regenerate-dialog": { + "title": "Régénérer", + "detail": "Une nouvelle adresse sera générée en utilisant les paramètres de votre ancienne adresse multisig, mise à jour au nouveau format de script.", + "donnot-worry": "Ne vous inquiétez pas — l’ancienne adresse restera accessible.", + "donnot-remind-again": "Ne plus me le rappeler" + }, "multi-details": "Détails de l'adresse multisig", "synchronization-abort": "Abandon de la synchronisation", "synchronization-abort-msg": "Quitter la fenêtre actuelle entraînera l'abandon de la synchronisation multisig, veuillez confirmer si vous souhaitez quitter.", diff --git a/packages/neuron-ui/src/locales/zh-tw.json b/packages/neuron-ui/src/locales/zh-tw.json index 17c8bd7d3e..17e221b29b 100644 --- a/packages/neuron-ui/src/locales/zh-tw.json +++ b/packages/neuron-ui/src/locales/zh-tw.json @@ -1161,7 +1161,8 @@ "approve": "確認", "delete": "删除", "daoDeposit": "存入 DAO", - "daoWithdraw": "從 DAO 取出" + "daoWithdraw": "從 DAO 取出", + "regenerate": "重新生成" } }, "dao-ledger-notice": "Ledger 錢包目前不支援多重簽名地址的 DAO 交易。請使用單一簽名地址或等待未來的更新。", @@ -1186,6 +1187,13 @@ "cancel": "取消", "export": "導出交易" }, + "multisig-script-update-notice": "由於<0>多簽腳本升級,我們建議重新生成多簽地址,並將資產從舊地址轉移,以確保正常功能。", + "regenerate-dialog": { + "title": "重新生成", + "detail": "將使用您舊多簽地址的設定生成新地址,並升級至最新腳本格式。", + "donnot-worry": "別擔心 — 舊地址仍然可以使用。", + "donnot-remind-again": "不再提醒" + }, "multi-details": "多簽地址詳情", "synchronization-abort": "同步中止", "synchronization-abort-msg": "離開當前窗口將導致多簽同步中止,請確認是否離開。", diff --git a/packages/neuron-ui/src/locales/zh.json b/packages/neuron-ui/src/locales/zh.json index dd79782209..031b76370b 100644 --- a/packages/neuron-ui/src/locales/zh.json +++ b/packages/neuron-ui/src/locales/zh.json @@ -1160,7 +1160,8 @@ "approve": "确认", "delete": "删除", "daoDeposit": "存入 DAO", - "daoWithdraw": "从 DAO 取出" + "daoWithdraw": "从 DAO 取出", + "regenerate": "重新生成" } }, "dao-ledger-notice": "Ledger 钱包目前不支持多签地址的 DAO 交易。请使用单签地址或等待未来的更新。", @@ -1185,6 +1186,13 @@ "cancel": "取消", "export": "导出交易" }, + "multisig-script-update-notice": "由于<0>多签脚本升级,我们建议重新生成多签地址,并将资产从旧地址转移,以确保正常使用。", + "regenerate-dialog": { + "title": "重新生成", + "detail": "将使用您旧多签地址的设置生成一个新地址,升级为最新脚本格式", + "donnot-worry": "别担心 — 旧地址仍然可以访问", + "donnot-remind-again": "不再提醒" + }, "multi-details": "多签地址详情", "synchronization-abort": "同步中止", "synchronization-abort-msg": "离开当前窗口将导致多签同步中止,请确认是否离开。", diff --git a/packages/neuron-ui/src/services/localCache.ts b/packages/neuron-ui/src/services/localCache.ts index 84851f4c03..2323645a4d 100644 --- a/packages/neuron-ui/src/services/localCache.ts +++ b/packages/neuron-ui/src/services/localCache.ts @@ -13,6 +13,7 @@ export enum LocalCacheKey { ShownNodeId = 'ShownNodeId', ScreenAwake = 'ScreenAwake', RetryUnlockWindowInfo = 'RetryUnlockWindowInfo', + RemaindRegenerateMultisigAddress = 'RemaindRegenerateMultisigAddress', } export const addresses = { @@ -194,3 +195,13 @@ export const retryUnlockWindow = { } }, } + +export const remaindRegenerateMultisigAddress = { + get: () => { + const value = window.localStorage.getItem(LocalCacheKey.RemaindRegenerateMultisigAddress) + return !!value && value === 'true' + }, + save: (value: boolean) => { + window.localStorage.setItem(LocalCacheKey.RemaindRegenerateMultisigAddress, value.toString()) + }, +} diff --git a/packages/neuron-ui/src/services/remote/multisig.ts b/packages/neuron-ui/src/services/remote/multisig.ts index 29a1b4819e..58d5179dde 100644 --- a/packages/neuron-ui/src/services/remote/multisig.ts +++ b/packages/neuron-ui/src/services/remote/multisig.ts @@ -22,11 +22,13 @@ export type MultisigEntity = MultisigParams & { walletId: string alias?: string startBlockNumber?: number + lockCodeHash: string } export type MultisigConfig = MultisigEntity & { addresses: string[] fullPayload: string + isLegacy: boolean } export const saveMultisigConfig = remoteApi, MultisigEntity>('save-multisig-config') @@ -41,6 +43,10 @@ export const getMultisigBalances = remoteApi< { isMainnet: boolean; multisigAddresses: string[] }, Record >('get-multisig-balances') +export const getMultisigDAOBalances = remoteApi< + { isMainnet: boolean; multisigAddresses: string[] }, + Record +>('get-multisig-dao-balances') export const generateMultisigTx = remoteApi<{ items: { address: string; capacity: string }[] multisigConfig: MultisigConfig diff --git a/packages/neuron-ui/src/services/remote/remoteApiWrapper.ts b/packages/neuron-ui/src/services/remote/remoteApiWrapper.ts index 3f9b6eaf4f..91b9074845 100644 --- a/packages/neuron-ui/src/services/remote/remoteApiWrapper.ts +++ b/packages/neuron-ui/src/services/remote/remoteApiWrapper.ts @@ -162,6 +162,7 @@ type Action = | 'update-multisig-config' | 'delete-multisig-config' | 'get-multisig-balances' + | 'get-multisig-dao-balances' | 'generate-multisig-tx' | 'generate-multisig-send-all-tx' | 'load-multisig-tx-json' diff --git a/packages/neuron-ui/src/utils/enums.ts b/packages/neuron-ui/src/utils/enums.ts index 15263e9bc4..2c3165e68b 100644 --- a/packages/neuron-ui/src/utils/enums.ts +++ b/packages/neuron-ui/src/utils/enums.ts @@ -150,11 +150,19 @@ export enum DefaultLockInfo { TagName = 'secp256k1/blake160', } -export enum MultiSigLockInfo { +export enum LegacyMultiSigLockInfo { CodeHash = '0x5c5069eb0857efc65e1bca0c07df34c31663b3622fd3876c876320fc9634e2a8', HashType = 'type', CodeHashIndex = '0x01', ArgsLen = '20', + TagName = 'secp256k1/multisig/legacy', +} + +export enum MultiSigLockInfo { + CodeHash = '0x36c971b8d41fbd94aabca77dc75e826729ac98447b46f91e00796155dddb0d29', + HashType = 'data1', + CodeHashIndex = '0x01', + ArgsLen = '20', TagName = 'secp256k1/multisig', } diff --git a/packages/neuron-ui/src/utils/getLockSupportShortAddress.ts b/packages/neuron-ui/src/utils/getLockSupportShortAddress.ts index 82b5ac806f..616458de19 100644 --- a/packages/neuron-ui/src/utils/getLockSupportShortAddress.ts +++ b/packages/neuron-ui/src/utils/getLockSupportShortAddress.ts @@ -1,8 +1,20 @@ import { type CKBComponents } from '@ckb-lumos/lumos/rpc' -import { AnyoneCanPayLockInfoOnAggron, AnyoneCanPayLockInfoOnLina, DefaultLockInfo, MultiSigLockInfo } from './enums' +import { + AnyoneCanPayLockInfoOnAggron, + AnyoneCanPayLockInfoOnLina, + DefaultLockInfo, + LegacyMultiSigLockInfo, + MultiSigLockInfo, +} from './enums' const getLockSupportShortAddress = (lock: CKBComponents.Script) => { - return [MultiSigLockInfo, DefaultLockInfo, AnyoneCanPayLockInfoOnAggron, AnyoneCanPayLockInfoOnLina].find( + return [ + LegacyMultiSigLockInfo, + MultiSigLockInfo, + DefaultLockInfo, + AnyoneCanPayLockInfoOnAggron, + AnyoneCanPayLockInfoOnLina, + ].find( info => lock.codeHash === info.CodeHash && lock.hashType === info.HashType && diff --git a/packages/neuron-ui/src/utils/multisig.ts b/packages/neuron-ui/src/utils/multisig.ts index 232acc6a00..d913cc7fd9 100644 --- a/packages/neuron-ui/src/utils/multisig.ts +++ b/packages/neuron-ui/src/utils/multisig.ts @@ -1,8 +1,13 @@ import { ckbHash } from '@ckb-lumos/lumos/utils' import { scriptToAddress } from 'utils' -import { MultiSigLockInfo } from './enums' +import { LegacyMultiSigLockInfo, MultiSigLockInfo } from './enums' import { MAX_M_N_NUMBER } from './const' +const MultiSigLockInfoMap = { + [LegacyMultiSigLockInfo.CodeHash]: LegacyMultiSigLockInfo, + [MultiSigLockInfo.CodeHash]: MultiSigLockInfo, +} + function getMultisigParamsHex(v: number) { if (v < 0 || v > MAX_M_N_NUMBER) { throw new Error(`Multisig's r/m/n should between 0 and ${MAX_M_N_NUMBER}`) @@ -25,12 +30,19 @@ function multisigHash(blake160s: string[], r: number = 0, m: number = 1, n: numb return ckbHash(multisigSerialize(blake160s, r, m, n)).slice(0, MULTISIGN_HASH_LENGTH) } -export function getMultisigAddress(blake160s: string[], r: number, m: number, n: number, isMainnet: boolean) { +export function getMultisigAddress( + blake160s: string[], + r: number, + m: number, + n: number, + isMainnet: boolean, + lockCodeHash: string = MultiSigLockInfo.CodeHash +) { return scriptToAddress( { args: multisigHash(blake160s, r, m, n), - codeHash: MultiSigLockInfo.CodeHash, - hashType: MultiSigLockInfo.HashType, + codeHash: MultiSigLockInfoMap[lockCodeHash as keyof typeof MultiSigLockInfoMap].CodeHash, + hashType: MultiSigLockInfoMap[lockCodeHash as keyof typeof MultiSigLockInfoMap].HashType, }, { isMainnet } ) diff --git a/packages/neuron-ui/src/utils/validators/address.ts b/packages/neuron-ui/src/utils/validators/address.ts index d6f20b917e..077185bd6c 100644 --- a/packages/neuron-ui/src/utils/validators/address.ts +++ b/packages/neuron-ui/src/utils/validators/address.ts @@ -8,6 +8,7 @@ import { } from 'exceptions' import { DefaultLockInfo, + LegacyMultiSigLockInfo, MultiSigLockInfo, LocktimeLockInfo, AnyoneCanPayLockInfoOnAggron, @@ -44,6 +45,7 @@ export const validateAddress = (address: string, isMainnet: boolean): boolean => const addressTagMap = { [DefaultLockInfo.TagName]: [DefaultLockInfo, DefaultLockInfo], + [LegacyMultiSigLockInfo.TagName]: [LegacyMultiSigLockInfo, LegacyMultiSigLockInfo], [MultiSigLockInfo.TagName]: [MultiSigLockInfo, MultiSigLockInfo], [LocktimeLockInfo.TagName]: [LocktimeLockInfo, LocktimeLockInfo], [AnyoneCanPayLockInfoOnAggron.TagName]: [AnyoneCanPayLockInfoOnLina, AnyoneCanPayLockInfoOnAggron], diff --git a/packages/neuron-ui/src/widgets/AlertDialog/index.tsx b/packages/neuron-ui/src/widgets/AlertDialog/index.tsx index 3edcd6ce0d..0126efb7ab 100644 --- a/packages/neuron-ui/src/widgets/AlertDialog/index.tsx +++ b/packages/neuron-ui/src/widgets/AlertDialog/index.tsx @@ -1,6 +1,6 @@ import React, { useMemo, useRef } from 'react' import { useTranslation } from 'react-i18next' -import { useDialog } from 'utils' +import { useDialog, clsx } from 'utils' import Button from 'widgets/Button' import Failed from 'widgets/Icons/Failed.png' import Success from 'widgets/Icons/Success.png' @@ -23,6 +23,7 @@ const AlertDialog = ({ cancelText, cancelProps, okProps, + className, }: { show?: boolean title?: string @@ -36,6 +37,7 @@ const AlertDialog = ({ cancelText?: string cancelProps?: object okProps?: object + className?: string }) => { const [t] = useTranslation() const dialogRef = useRef(null) @@ -48,12 +50,12 @@ const AlertDialog = ({ }, [action, type, onCancel]) return ( - + {type === 'failed' && failed} {type === 'success' && success} {type === 'warning' && warning}

{title}

-

{message}

+
{message}
{actions.map(v => v === 'cancel' ? ( diff --git a/packages/neuron-ui/src/widgets/Icons/Regenerate.svg b/packages/neuron-ui/src/widgets/Icons/Regenerate.svg new file mode 100644 index 0000000000..49021ea530 --- /dev/null +++ b/packages/neuron-ui/src/widgets/Icons/Regenerate.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 4e426e842a..1877e7a3a9 100644 --- a/packages/neuron-ui/src/widgets/Icons/icon.tsx +++ b/packages/neuron-ui/src/widgets/Icons/icon.tsx @@ -67,6 +67,7 @@ import DAODepositSvg from './DAODeposit.svg?react' import DAOWithdrawalSvg from './DAOWithdrawal.svg?react' import RecycleSvg from './Recycle.svg?react' import MoreSvg from './More.svg?react' +import RegenerateSvg from './Regenerate.svg?react' import styles from './icon.module.scss' @@ -150,3 +151,4 @@ export const DAODeposit = WrapSvg(DAODepositSvg) export const DAOWithdrawal = WrapSvg(DAOWithdrawalSvg) export const Recycle = WrapSvg(RecycleSvg) export const More = WrapSvg(MoreSvg) +export const Regenerate = WrapSvg(RegenerateSvg) diff --git a/packages/neuron-wallet/.env b/packages/neuron-wallet/.env index 29e074fea1..99149d6187 100644 --- a/packages/neuron-wallet/.env +++ b/packages/neuron-wallet/.env @@ -114,7 +114,11 @@ TESTNET_NFT_SCRIPT_HASH_TYPE=type # DEFAULT SCRIPT SECP256K1_CODE_HASH=0x9bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce8 DAO_CODE_HASH=0x82d76d1b75fe2fd9a27dfbaa65a039221a380d76c926f378d3f81cf3e7e13f2e -MULTISIG_CODE_HASH=0x5c5069eb0857efc65e1bca0c07df34c31663b3622fd3876c876320fc9634e2a8 +LEGACY_MULTISIG_CODE_HASH=0x5c5069eb0857efc65e1bca0c07df34c31663b3622fd3876c876320fc9634e2a8 + +MULTISIG_CODE_HASH=0x36c971b8d41fbd94aabca77dc75e826729ac98447b46f91e00796155dddb0d29 +MAINNET_MULTISIG_TXHASH=0x6888aa39ab30c570c2c30d9d5684d3769bf77265a7973211a3c087fe8efbf738 +TESTNET_MULTISIG_TXHASH=0x2eefdeb21f3a3edf697c28a52601b4419806ed60bb427420455cc29a090b26d5 # CKB NODE OPTIONS CKB_NODE_ASSUME_VALID_TARGET='0x53dd03a0420b68e082d660cb2e86f167aa8f1bd95c637228285bf36f5caa9e1d' diff --git a/packages/neuron-wallet/src/controllers/api.ts b/packages/neuron-wallet/src/controllers/api.ts index 95fdd231bf..4be48f6976 100644 --- a/packages/neuron-wallet/src/controllers/api.ts +++ b/packages/neuron-wallet/src/controllers/api.ts @@ -1035,6 +1035,10 @@ export default class ApiController { return this.#multisigController.getMultisigBalances(params) }) + handle('get-multisig-dao-balances', async (_, params) => { + return this.#multisigController.getMultisigDAOBalances(params) + }) + handle('load-multisig-tx-json', async (_, fullPayload) => { return this.#multisigController.loadMultisigTxJson(fullPayload) }) diff --git a/packages/neuron-wallet/src/controllers/multisig.ts b/packages/neuron-wallet/src/controllers/multisig.ts index 8dfa307756..c4d4b1d173 100644 --- a/packages/neuron-wallet/src/controllers/multisig.ts +++ b/packages/neuron-wallet/src/controllers/multisig.ts @@ -21,6 +21,19 @@ import { LightRPC } from '../utils/ckb-rpc' import SyncProgressService from '../services/sync-progress' interface MultisigConfigOutput { + multisig_configs: Record< + string, + { + sighash_addresses: string[] + require_first_n: number + threshold: number + alias?: string | string[] + lockCodeHash: string | string[] + } + > +} + +interface MultisigConfigInput { multisig_configs: Record< string, { @@ -28,11 +41,12 @@ interface MultisigConfigOutput { require_first_n: number threshold: number alias?: string + lockCodeHash?: string | string[] } > } -const validateImportConfig = (configOutput: MultisigConfigOutput) => { +const validateImportConfig = (configOutput: MultisigConfigInput) => { return ( configOutput.multisig_configs && Object.values(configOutput.multisig_configs).length && @@ -49,9 +63,25 @@ export default class MultisigController { this.#multisigService = new MultisigService() } - async saveConfig(params: { walletId: string; r: number; m: number; n: number; blake160s: string[]; alias?: string }) { + async saveConfig(params: { + walletId: string + r: number + m: number + n: number + blake160s: string[] + alias?: string + lockCodeHash?: string + }) { const multiSignConfig = MultisigConfig.fromModel( - new MultisigConfigModel(params.walletId, params.r, params.m, params.n, params.blake160s, params.alias) + new MultisigConfigModel( + params.walletId, + params.r, + params.m, + params.n, + params.blake160s, + params.lockCodeHash || SystemScriptInfo.MULTI_SIGN_CODE_HASH, + params.alias + ) ) const result = await this.#multisigService.saveMultisigConfig(multiSignConfig) return { @@ -63,7 +93,7 @@ export default class MultisigController { async resetMultisigSync(config: MultisigConfig, startBlockNumber: number) { const network = NetworksService.getInstance().getCurrent() const lightRpc = new LightRPC(network.remote) - const script = Multisig.getMultisigScript(config.blake160s, config.r, config.m, config.n) + const script = Multisig.getMultisigScript(config.blake160s, config.r, config.m, config.n, config.lockCodeHash) await lightRpc.setScripts( [ { @@ -137,7 +167,7 @@ export default class MultisigController { } try { const json = fs.readFileSync(filePaths[0], 'utf-8') - const configOutput: MultisigConfigOutput = JSON.parse(json) + const configOutput: MultisigConfigInput = JSON.parse(json) if (!validateImportConfig(configOutput)) { ShowGlobalDialogSubject.next({ type: 'failed', @@ -146,14 +176,40 @@ export default class MultisigController { }) return } - const saveConfigs = Object.values(configOutput.multisig_configs).map(config => ({ - r: +config.require_first_n, - m: +config.threshold, - n: config.sighash_addresses.length, - blake160s: config.sighash_addresses.map(v => addressToScript(v).args), - walletId, - alias: config.alias, - })) + const saveConfigs: { + r: number + m: number + n: number + blake160s: string[] + walletId: string + alias?: string + lockCodeHash?: string + }[] = [] + Object.values(configOutput.multisig_configs).forEach(config => { + if (Array.isArray(config.lockCodeHash)) { + config.lockCodeHash.forEach((lockCodeHash, index) => { + saveConfigs.push({ + r: +config.require_first_n, + m: +config.threshold, + n: config.sighash_addresses.length, + blake160s: config.sighash_addresses.map(v => addressToScript(v).args), + walletId, + alias: config.alias?.[index], + lockCodeHash, + }) + }) + } else { + saveConfigs.push({ + r: +config.require_first_n, + m: +config.threshold, + n: config.sighash_addresses.length, + blake160s: config.sighash_addresses.map(v => addressToScript(v).args), + walletId, + alias: config.alias, + lockCodeHash: config.lockCodeHash, + }) + } + }) const savedResult = await Promise.allSettled(saveConfigs.map(config => this.saveConfig(config))) const saveSuccessConfigs: MultisigConfig[] = [] for (let idx = 0; idx < savedResult.length; idx++) { @@ -191,6 +247,7 @@ export default class MultisigController { m: number n: number blake160s: string[] + lockCodeHash: string alias?: string }[] ) { @@ -204,13 +261,27 @@ export default class MultisigController { const isMainnet = NetworksService.getInstance().isMainnet() const output: MultisigConfigOutput = { multisig_configs: {} } configs.forEach(v => { - output.multisig_configs[Multisig.hash(v.blake160s, v.r, v.m, v.n)] = { - sighash_addresses: v.blake160s.map(args => - scriptToAddress(SystemScriptInfo.generateSecpScript(args), isMainnet) - ), - require_first_n: v.r, - threshold: v.m, - alias: v.alias, + const hash = Multisig.hash(v.blake160s, v.r, v.m, v.n) + if (output.multisig_configs[hash]) { + const existingLockCodeHash = output.multisig_configs[hash].lockCodeHash + output.multisig_configs[hash].lockCodeHash = Array.isArray(existingLockCodeHash) + ? [...existingLockCodeHash, v.lockCodeHash] + : [existingLockCodeHash, v.lockCodeHash] + + const existingAlias = output.multisig_configs[hash].alias || '' + output.multisig_configs[hash].alias = Array.isArray(existingAlias) + ? [...existingAlias, v.alias || ''] + : [existingAlias, v.alias || ''] + } else { + output.multisig_configs[hash] = { + sighash_addresses: v.blake160s.map(args => + scriptToAddress(SystemScriptInfo.generateSecpScript(args), isMainnet) + ), + require_first_n: v.r, + threshold: v.m, + lockCodeHash: v.lockCodeHash, + alias: v.alias, + } } }) @@ -238,6 +309,14 @@ export default class MultisigController { } } + async getMultisigDAOBalances({ isMainnet, multisigAddresses }: { isMainnet: boolean; multisigAddresses: string[] }) { + const balances = await CellsService.getMultisigDAOBalances(isMainnet, multisigAddresses) + return { + status: ResponseCode.Success, + result: balances, + } + } + async loadMultisigTxJson(fullPayload: string) { const result = await OfflineSignService.loadTransactionJSON() if (!result) { diff --git a/packages/neuron-wallet/src/database/address/meta.ts b/packages/neuron-wallet/src/database/address/meta.ts index 5ef8db354d..60d8e38838 100644 --- a/packages/neuron-wallet/src/database/address/meta.ts +++ b/packages/neuron-wallet/src/database/address/meta.ts @@ -101,7 +101,10 @@ export default class AddressMeta implements Address { } public generateSingleMultiSignLockScript(): Script { - return SystemScriptInfo.generateMultiSignScript(Multisig.hash([this.blake160])) + return SystemScriptInfo.generateMultiSignScript( + Multisig.hash([this.blake160]), + SystemScriptInfo.LEGACY_MULTI_SIGN_CODE_HASH + ) } public generateACPLockScript(): Script { diff --git a/packages/neuron-wallet/src/database/chain/entities/multisig-config.ts b/packages/neuron-wallet/src/database/chain/entities/multisig-config.ts index 3e62b73e5b..b3206769c7 100644 --- a/packages/neuron-wallet/src/database/chain/entities/multisig-config.ts +++ b/packages/neuron-wallet/src/database/chain/entities/multisig-config.ts @@ -32,6 +32,12 @@ export default class MultisigConfig { @Column() startBlockNumber?: number + @Column({ + type: 'varchar', + nullable: false, + }) + lockCodeHash!: string + public static fromModel(model: MultisigConfigModel): MultisigConfig { const multisigConfig = new MultisigConfig() @@ -43,6 +49,7 @@ export default class MultisigConfig { if (model.alias) { multisigConfig.alias = model.alias } + multisigConfig.lockCodeHash = model.lockCodeHash return multisigConfig } @@ -57,6 +64,7 @@ export default class MultisigConfig { if (this.alias) { multisigConfig.alias = this.alias } + multisigConfig.lockCodeHash = this.lockCodeHash return multisigConfig } diff --git a/packages/neuron-wallet/src/database/chain/migrations/1681360188494-AddTypeSyncProgress.ts b/packages/neuron-wallet/src/database/chain/migrations/1681360188494-AddTypeSyncProgress.ts index 9a268ee990..b99a265486 100644 --- a/packages/neuron-wallet/src/database/chain/migrations/1681360188494-AddTypeSyncProgress.ts +++ b/packages/neuron-wallet/src/database/chain/migrations/1681360188494-AddTypeSyncProgress.ts @@ -18,7 +18,7 @@ export class AddTypeSyncProgress1681360188494 implements MigrationInterface { // after add a column for multisig_config here will throw exception if use `queryRunner.manager.find(MultisigConfig)` // so it's better to use query to find the items const multisigConfigs: MultisigConfig[] = await queryRunner.manager.query('select * from multisig_config') - const scriptHashes = multisigConfigs.map(v => scriptToHash(Multisig.getMultisigScript(v.blake160s, v.r, v.m, v.n))) + const scriptHashes = multisigConfigs.map(v => scriptToHash(Multisig.getMultisigScript(v.blake160s, v.r, v.m, v.n, v.lockCodeHash))) await queryRunner.query(`UPDATE sync_progress set addressType=1 where hash in (${scriptHashes.map(v => `'${v}'`).join(',')})`) } diff --git a/packages/neuron-wallet/src/database/chain/migrations/1744960856059-AddLockCodeHash.ts b/packages/neuron-wallet/src/database/chain/migrations/1744960856059-AddLockCodeHash.ts new file mode 100644 index 0000000000..edd49280a2 --- /dev/null +++ b/packages/neuron-wallet/src/database/chain/migrations/1744960856059-AddLockCodeHash.ts @@ -0,0 +1,16 @@ +import { MigrationInterface, QueryRunner } from "typeorm"; +import SystemScriptInfo from "../../../models/system-script-info"; + +export class AddLockCodeHash1744960856059 implements MigrationInterface { + name = 'AddLockCodeHash1744960856059' + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(`ALTER TABLE "multisig_config" ADD COLUMN "lockCodeHash" character(32);`) + await queryRunner.query(`UPDATE "multisig_config" SET lockCodeHash = "${SystemScriptInfo.LEGACY_MULTI_SIGN_CODE_HASH}" where lockCodeHash IS NULL`) + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query(`ALTER TABLE "multisig_config" DROP COLUMN "lockCodeHash";`) + } + +} diff --git a/packages/neuron-wallet/src/database/chain/ormconfig.ts b/packages/neuron-wallet/src/database/chain/ormconfig.ts index b8699466d2..b531cd4f5b 100644 --- a/packages/neuron-wallet/src/database/chain/ormconfig.ts +++ b/packages/neuron-wallet/src/database/chain/ormconfig.ts @@ -70,6 +70,7 @@ import SudtTokenInfoSubscribe from './subscriber/sudt-token-info-subscriber' import AssetAccountSubscribe from './subscriber/asset-account-subscriber' import { AddStartBlockNumber1716539079505 } from './migrations/1716539079505-AddStartBlockNumber' import { AddUdtType1720089814860 } from './migrations/1720089814860-AddUdtType' +import { AddLockCodeHash1744960856059 } from './migrations/1744960856059-AddLockCodeHash' export const CONNECTION_NOT_FOUND_NAME = 'ConnectionNotFoundError' export type ConnectionName = 'light' | 'full' @@ -177,6 +178,7 @@ const getConnectionOptions = (genesisBlockHash: string, connectionName: Connecti UpdateOutputChequeLockHash1652945662504, RemoveAddressesMultisigConfig1651820157100, AddSyncProgress1676441837373, + AddLockCodeHash1744960856059, AddTypeSyncProgress1681360188494, TxLock1684488676083, ResetSyncProgressPrimaryKey1690361215400, diff --git a/packages/neuron-wallet/src/locales/en.ts b/packages/neuron-wallet/src/locales/en.ts index 82e3b7d27c..842c05b5d6 100644 --- a/packages/neuron-wallet/src/locales/en.ts +++ b/packages/neuron-wallet/src/locales/en.ts @@ -136,7 +136,7 @@ export default { 'multisig-lock-hash-mismatch': 'The current multisig address does not match the transaction to be approved', 'sudt-acp-have-data': 'The destroying sUDT acp account have amount', 'no-match-address-for-sign': 'Not found matched address', - 'target-lock-error': 'CKB asset account can only transfer to sepe256k1 or acp address', + 'target-lock-error': 'CKB asset account can only transfer to secp256k1 or acp address', 'no-exist-ckb-node-data': '{{path}} has no CKB Node config and storage, press confirm to synchronize from scratch', 'light-client-sudt-acp-error': "Light client mode doesn't support sending assets to other's asset account", diff --git a/packages/neuron-wallet/src/locales/es.ts b/packages/neuron-wallet/src/locales/es.ts index 6c6855becd..21a53720b1 100644 --- a/packages/neuron-wallet/src/locales/es.ts +++ b/packages/neuron-wallet/src/locales/es.ts @@ -137,7 +137,7 @@ export default { 'La dirección multifirma actual no coincide con la transacción que se va a aprobar', 'sudt-acp-have-data': 'La cuenta ACP de sUDT destruida tiene saldo', 'no-match-address-for-sign': 'No se encontró una dirección coincidente', - 'target-lock-error': 'La cuenta de activos CKB solo puede transferirse a una dirección sepe256k1 o acp', + 'target-lock-error': 'La cuenta de activos CKB solo puede transferirse a una dirección secp256k1 o acp', 'no-exist-ckb-node-data': '{{path}} no tiene configuración y almacenamiento de nodo CKB, presiona confirmar para sincronizar desde cero', 'light-client-sudt-acp-error': diff --git a/packages/neuron-wallet/src/models/multisig-config.ts b/packages/neuron-wallet/src/models/multisig-config.ts index a0a49f077d..db534c99f8 100644 --- a/packages/neuron-wallet/src/models/multisig-config.ts +++ b/packages/neuron-wallet/src/models/multisig-config.ts @@ -9,13 +9,24 @@ export default class MultisigConfigModel { public n: number public blake160s: string[] public alias?: string + public lockCodeHash: string - constructor(walletId: string, r: number, m: number, n: number, blake160s: string[], alias?: string, id?: number) { + constructor( + walletId: string, + r: number, + m: number, + n: number, + blake160s: string[], + lockCodeHash: string, + alias?: string, + id?: number + ) { this.walletId = walletId this.r = r this.m = m this.n = n this.blake160s = blake160s + this.lockCodeHash = lockCodeHash this.alias = alias this.id = id } @@ -26,6 +37,7 @@ export default class MultisigConfigModel { n: number r: number blake160s: string[] + lockCodeHash: string alias?: string id?: number }): MultisigConfigModel { @@ -35,6 +47,7 @@ export default class MultisigConfigModel { params.m, params.n, params.blake160s, + params.lockCodeHash, params.alias, params.id ) @@ -47,10 +60,11 @@ export default class MultisigConfigModel { r: this.r, blake160s: this.blake160s, alias: this.alias, + lockCodeHash: this.lockCodeHash, } } public getLockHash() { - return scriptToHash(Multisig.getMultisigScript(this.blake160s, this.r, this.m, this.n)) + return scriptToHash(Multisig.getMultisigScript(this.blake160s, this.r, this.m, this.n, this.lockCodeHash)) } } diff --git a/packages/neuron-wallet/src/models/multisig.ts b/packages/neuron-wallet/src/models/multisig.ts index e2810df24d..eeae543af7 100644 --- a/packages/neuron-wallet/src/models/multisig.ts +++ b/packages/neuron-wallet/src/models/multisig.ts @@ -45,8 +45,8 @@ export default class Multisig { return Multisig.hash([blake160]) + this.since(minutes, headerEpoch).slice(2) } - static getMultisigScript(blake160s: string[], r: number, m: number, n: number) { - return SystemScriptInfo.generateMultiSignScript(Multisig.hash(blake160s, r, m, n)) + static getMultisigScript(blake160s: string[], r: number, m: number, n: number, lockCodeHash: string) { + return SystemScriptInfo.generateMultiSignScript(Multisig.hash(blake160s, r, m, n), lockCodeHash) } static parseSince(args: string): bigint { diff --git a/packages/neuron-wallet/src/models/system-script-info.ts b/packages/neuron-wallet/src/models/system-script-info.ts index 755ef58ec4..6a68430e3c 100644 --- a/packages/neuron-wallet/src/models/system-script-info.ts +++ b/packages/neuron-wallet/src/models/system-script-info.ts @@ -5,15 +5,18 @@ import RpcService from '../services/rpc-service' import Script, { ScriptHashType } from './chain/script' import { systemScripts } from '../utils/systemScripts' import { NetworkType } from './network' +import { MAINNET_CLIENT_LIST } from '../utils/const' export default class SystemScriptInfo { static SECP_CODE_HASH = process.env.SECP256K1_CODE_HASH! static DAO_CODE_HASH = process.env.DAO_CODE_HASH! + static LEGACY_MULTI_SIGN_CODE_HASH = process.env.LEGACY_MULTISIG_CODE_HASH! static MULTI_SIGN_CODE_HASH = process.env.MULTISIG_CODE_HASH! static SECP_HASH_TYPE = ScriptHashType.Type static DAO_HASH_TYPE = ScriptHashType.Type - static MULTI_SIGN_HASH_TYPE = ScriptHashType.Type + static LEGACY_MULTI_SIGN_HASH_TYPE = ScriptHashType.Type + static MULTI_SIGN_HASH_TYPE = ScriptHashType.Data1 static DAO_SCRIPT_HASH = new Script( systemScripts.DAO.CODE_HASH, @@ -35,8 +38,16 @@ export default class SystemScriptInfo { private daoOutPointInfo = new Map() + private legacyMultiSignOutPointInfo = new Map() private multiSignOutPointInfo = new Map() + public static getMultiSignHashType(lockCodeHash: string): ScriptHashType { + if (lockCodeHash === SystemScriptInfo.MULTI_SIGN_CODE_HASH) { + return SystemScriptInfo.MULTI_SIGN_HASH_TYPE + } + return SystemScriptInfo.LEGACY_MULTI_SIGN_HASH_TYPE + } + // need network url and genesisBlockHash public async getSecpCellDep( network: { remote: string; genesisHash: string; type: NetworkType } = NetworksService.getInstance().getCurrent() @@ -63,13 +74,24 @@ export default class SystemScriptInfo { } public async getMultiSignCellDep( - network: { remote: string; genesisHash: string; type: NetworkType } = NetworksService.getInstance().getCurrent() + lockCodeHash: string = SystemScriptInfo.LEGACY_MULTI_SIGN_CODE_HASH, + network: { + remote: string + genesisHash: string + type: NetworkType + chain: string + } = NetworksService.getInstance().getCurrent() ): Promise { const genesisBlockHash = network.genesisHash - let outPoint = this.multiSignOutPointInfo.get(genesisBlockHash) + const multiSignOutPointInfo = + lockCodeHash === SystemScriptInfo.MULTI_SIGN_CODE_HASH + ? this.multiSignOutPointInfo + : this.legacyMultiSignOutPointInfo + + let outPoint = multiSignOutPointInfo.get(genesisBlockHash) if (!outPoint) { - await this.loadInfos(network.remote, network.type) - outPoint = this.multiSignOutPointInfo.get(genesisBlockHash)! + await this.loadInfos(network.remote, network.type, MAINNET_CLIENT_LIST.includes(network.chain)) + outPoint = multiSignOutPointInfo.get(genesisBlockHash)! } return new CellDep(outPoint, DepType.DepGroup) } @@ -82,18 +104,24 @@ export default class SystemScriptInfo { return new Script(SystemScriptInfo.DAO_CODE_HASH, args, SystemScriptInfo.DAO_HASH_TYPE) } - public static generateMultiSignScript(args: string): Script { - return new Script(SystemScriptInfo.MULTI_SIGN_CODE_HASH, args, SystemScriptInfo.MULTI_SIGN_HASH_TYPE) + public static generateMultiSignScript(args: string, lockCodeHash: string): Script { + return new Script(lockCodeHash, args, SystemScriptInfo.getMultiSignHashType(lockCodeHash)) } public static isSecpScript(script: Script): boolean { return script.codeHash === SystemScriptInfo.SECP_CODE_HASH && script.hashType === SystemScriptInfo.SECP_HASH_TYPE } + public static isMultiSignCodeHash(codeHash: string): boolean { + return [SystemScriptInfo.LEGACY_MULTI_SIGN_CODE_HASH, SystemScriptInfo.MULTI_SIGN_CODE_HASH].includes(codeHash) + } + public static isMultiSignScript(script: Script): boolean { return ( - script.codeHash === SystemScriptInfo.MULTI_SIGN_CODE_HASH && - script.hashType === SystemScriptInfo.MULTI_SIGN_HASH_TYPE + (script.codeHash === SystemScriptInfo.LEGACY_MULTI_SIGN_CODE_HASH && + script.hashType === SystemScriptInfo.LEGACY_MULTI_SIGN_HASH_TYPE) || + (script.codeHash === SystemScriptInfo.MULTI_SIGN_CODE_HASH && + script.hashType === SystemScriptInfo.MULTI_SIGN_HASH_TYPE) ) } @@ -101,7 +129,7 @@ export default class SystemScriptInfo { return script.codeHash === SystemScriptInfo.DAO_CODE_HASH && script.hashType === SystemScriptInfo.DAO_HASH_TYPE } - private async loadInfos(url: string, type: NetworkType): Promise { + private async loadInfos(url: string, type: NetworkType, isMainnet?: boolean): Promise { const rpcService = new RpcService(url, type) const genesisBlock = (await rpcService.getGenesisBlock())! const genesisBlockHash = genesisBlock.header.hash @@ -116,6 +144,12 @@ export default class SystemScriptInfo { // set multi sign info const multiSignOutPointTxHash = genesisBlock.transactions[1].hash! - this.multiSignOutPointInfo.set(genesisBlockHash, new OutPoint(multiSignOutPointTxHash, '1')) + this.legacyMultiSignOutPointInfo.set(genesisBlockHash, new OutPoint(multiSignOutPointTxHash, '1')) + + if (isMainnet) { + this.multiSignOutPointInfo.set(genesisBlockHash, new OutPoint(process.env.MAINNET_MULTISIG_TXHASH!, '0')) + } else { + this.multiSignOutPointInfo.set(genesisBlockHash, new OutPoint(process.env.TESTNET_MULTISIG_TXHASH!, '0')) + } } } diff --git a/packages/neuron-wallet/src/services/cells.ts b/packages/neuron-wallet/src/services/cells.ts index fae732840e..abb1ec6699 100644 --- a/packages/neuron-wallet/src/services/cells.ts +++ b/packages/neuron-wallet/src/services/cells.ts @@ -353,7 +353,7 @@ export default class CellsService { `, { liveStatus: OutputStatus.Live, - multiSignlockCodeHash: SystemScriptInfo.MULTI_SIGN_CODE_HASH, + multiSignlockCodeHash: SystemScriptInfo.LEGACY_MULTI_SIGN_CODE_HASH, chequeLockCodeHash, nftIssuerCodehash, nftClassCodehash, @@ -467,7 +467,7 @@ export default class CellsService { data: 'withdraw-able', }) } - } else if (o.lockCodeHash === SystemScriptInfo.MULTI_SIGN_CODE_HASH) { + } else if (o.lockCodeHash === SystemScriptInfo.LEGACY_MULTI_SIGN_CODE_HASH) { cell.setCustomizedAssetInfo({ lock: CustomizedLock.SingleMultiSign, type: '', @@ -770,7 +770,7 @@ export default class CellsService { if (inputs.find(el => el.lockHash === cell.lockHash!)) { totalSize += TransactionSize.emptyWitness() } else { - if (lockClass.codeHash === SystemScriptInfo.MULTI_SIGN_CODE_HASH) { + if (lockClass.codeHash === SystemScriptInfo.LEGACY_MULTI_SIGN_CODE_HASH) { const multisigConfig = multisigConfigMap[cell.lockHash] if (!multisigConfig) { throw new MultisigConfigNeedError() @@ -1321,12 +1321,15 @@ export default class CellsService { return {} } const lockHashes = multisigAddresses.map(v => scriptToHash(addressToScript(v))) + const connection = await getConnection() const [sql, parameters] = connection.driver.escapeQueryWithParameters( ` select CAST(SUM(CAST(multisig_output.capacity AS UNSIGNED BIG INT)) AS VARCHAR) as balance, - lockArgs + lockArgs, + lockCodeHash, + lockHashType from multisig_output where @@ -1343,6 +1346,8 @@ export default class CellsService { const cells: { lockArgs: string balance: string + lockCodeHash: string + lockHashType: string }[] = await connection.getRepository(MultisigOutput).manager.query(sql, parameters) const balances: Record = {} @@ -1352,8 +1357,8 @@ export default class CellsService { scriptToAddress( { args: c.lockArgs, - codeHash: SystemScriptInfo.MULTI_SIGN_CODE_HASH, - hashType: SystemScriptInfo.MULTI_SIGN_HASH_TYPE, + codeHash: c.lockCodeHash, + hashType: c.lockHashType, }, isMainnet ) @@ -1368,7 +1373,7 @@ export default class CellsService { switch (output.lock.codeHash) { case assetAccountInfo.getChequeInfo().codeHash: return LockScriptCategory.Cheque - case SystemScriptInfo.MULTI_SIGN_CODE_HASH: + case SystemScriptInfo.LEGACY_MULTI_SIGN_CODE_HASH: if (output.lock.args.length === LOCKTIME_ARGS_LENGTH) { return LockScriptCategory.MULTI_LOCK_TIME } @@ -1447,4 +1452,57 @@ export default class CellsService { }) .filter((v): v is { withdrawBlockHash: string; depositOutPoint: OutPoint } => !!v) } + + public static async getMultisigDAOBalances(isMainnet: boolean, multisigAddresses: string[]) { + if (!multisigAddresses.length) { + return {} + } + const lockHashes = multisigAddresses.map(v => scriptToHash(addressToScript(v))) + + const connection = await getConnection() + const [sql, parameters] = connection.driver.escapeQueryWithParameters( + ` + select + CAST(SUM(CAST(output.capacity AS UNSIGNED BIG INT)) AS VARCHAR) as balance, + lockArgs, + lockCodeHash, + lockHashType + from + output + where + output.daoData = '0x0000000000000000' AND + output.lockHash in (:...lockHashes) AND + status in (:...statuses) + group by output.lockArgs + `, + { + lockHashes, + statuses: [OutputStatus.Live], + }, + {} + ) + const cells: { + lockArgs: string + balance: string + lockCodeHash: string + lockHashType: string + }[] = await connection.getRepository(OutputEntity).manager.query(sql, parameters) + + const balances: Record = {} + + cells.forEach(c => { + balances[ + scriptToAddress( + { + args: c.lockArgs, + codeHash: c.lockCodeHash, + hashType: c.lockHashType, + }, + isMainnet + ) + ] = c.balance + }) + + return balances + } } diff --git a/packages/neuron-wallet/src/services/multisig.ts b/packages/neuron-wallet/src/services/multisig.ts index c90b995a37..bc17832347 100644 --- a/packages/neuron-wallet/src/services/multisig.ts +++ b/packages/neuron-wallet/src/services/multisig.ts @@ -32,6 +32,7 @@ export default class MultisigService { m: multisigConfig.m, n: multisigConfig.n, blake160s: multisigConfig.blake160s.toString(), + lockCodeHash: multisigConfig.lockCodeHash, }) .getCount() if (result > 0) { @@ -105,7 +106,7 @@ export default class MultisigService { private static removeDulpicateConfig(multisigConfigs: MultisigConfig[]) { const existMultisigLockHash: Set = new Set() return multisigConfigs.filter(v => { - const multisigLockHash = Multisig.getMultisigScript(v.blake160s, v.r, v.m, v.n).computeHash() + const multisigLockHash = Multisig.getMultisigScript(v.blake160s, v.r, v.m, v.n, v.lockCodeHash).computeHash() if (existMultisigLockHash.has(multisigLockHash)) { return false } @@ -123,7 +124,7 @@ export default class MultisigService { const res = await rpcBatchRequest( network.remote, currentMultisigConfigs.map(v => { - const script = Multisig.getMultisigScript(v.blake160s, v.r, v.m, v.n) + const script = Multisig.getMultisigScript(v.blake160s, v.r, v.m, v.n, v.lockCodeHash) return { method: 'get_cells', params: [ @@ -149,7 +150,7 @@ export default class MultisigService { res.forEach((v, idx) => { if (!v.error && v?.result?.objects?.length) { const config = currentMultisigConfigs[idx] - const script = Multisig.getMultisigScript(config.blake160s, config.r, config.m, config.n) + const script = Multisig.getMultisigScript(config.blake160s, config.r, config.m, config.n, config.lockCodeHash) addressCursorMap.set(script.args, v?.result?.last_cursor) cells.push(...v.result.objects) nextMultisigConfigs.push(currentMultisigConfigs[idx]) @@ -237,7 +238,7 @@ export default class MultisigService { const res = await rpcBatchRequest( network.remote, currentMultisigConfigs.map(v => { - const script = Multisig.getMultisigScript(v.blake160s, v.r, v.m, v.n) + const script = Multisig.getMultisigScript(v.blake160s, v.r, v.m, v.n, v.lockCodeHash) return { method: 'get_transactions', params: [ @@ -263,7 +264,7 @@ export default class MultisigService { res.forEach((v, idx) => { if (!v.error && v?.result?.objects?.length) { const config = currentMultisigConfigs[idx] - const script = Multisig.getMultisigScript(config.blake160s, config.r, config.m, config.n) + const script = Multisig.getMultisigScript(config.blake160s, config.r, config.m, config.n, config.lockCodeHash) addressCursorMap.set(script.args, v?.result?.last_cursor) v.result.objects.forEach((obj: any) => { multisigOutputTxHashList.add(obj.tx_hash || obj.transaction?.hash) @@ -310,7 +311,7 @@ export default class MultisigService { static async deleteRemovedMultisigOutput() { const multisigConfigs = await getConnection().getRepository(MultisigConfig).createQueryBuilder().getMany() const multisigLockHashList = multisigConfigs.map(v => - scriptToHash(Multisig.getMultisigScript(v.blake160s, v.r, v.m, v.n)) + scriptToHash(Multisig.getMultisigScript(v.blake160s, v.r, v.m, v.n, v.lockCodeHash)) ) await getConnection() .createQueryBuilder() @@ -327,7 +328,7 @@ export default class MultisigService { const network = await NetworksService.getInstance().getCurrent() if (network.type === NetworkType.Light) { const multisigScriptHashList = multisigConfigs.map(v => - scriptToHash(Multisig.getMultisigScript(v.blake160s, v.r, v.m, v.n)) + scriptToHash(Multisig.getMultisigScript(v.blake160s, v.r, v.m, v.n, v.lockCodeHash)) ) const syncBlockNumbers = await getConnection() .getRepository(SyncProgress) @@ -343,7 +344,7 @@ export default class MultisigService { .save( multisigConfigs.map(v => { const blockNumber = - syncBlockNumbersMap[scriptToHash(Multisig.getMultisigScript(v.blake160s, v.r, v.m, v.n))] + syncBlockNumbersMap[scriptToHash(Multisig.getMultisigScript(v.blake160s, v.r, v.m, v.n, v.lockCodeHash))] v.lastestBlockNumber = `0x${BigInt(blockNumber ?? v.lastestBlockNumber).toString(16)}` return v }) @@ -409,7 +410,7 @@ export default class MultisigService { const multisigConfigs = await getConnection().getRepository(MultisigConfig).createQueryBuilder().getMany() return multisigConfigs.map(v => ({ walletId: v.walletId, - script: Multisig.getMultisigScript(v.blake160s, v.r, v.m, v.n), + script: Multisig.getMultisigScript(v.blake160s, v.r, v.m, v.n, v.lockCodeHash), addressType: SyncAddressType.Multisig, scriptType: 'lock' as CKBRPC.ScriptType, startBlockNumber: v.startBlockNumber, diff --git a/packages/neuron-wallet/src/services/transaction-sender.ts b/packages/neuron-wallet/src/services/transaction-sender.ts index 89a6581957..451b9cd584 100644 --- a/packages/neuron-wallet/src/services/transaction-sender.ts +++ b/packages/neuron-wallet/src/services/transaction-sender.ts @@ -583,7 +583,8 @@ export default class TransactionSender { multisigConfig.blake160s, multisigConfig.r, multisigConfig.m, - multisigConfig.n + multisigConfig.n, + multisigConfig.lockCodeHash ) const multisigAddresses = scriptToAddress(lockScript, NetworksService.getInstance().isMainnet()) const tx: Transaction = await TransactionGenerator.generateTx({ @@ -594,8 +595,8 @@ export default class TransactionSender { feeRate: '1000', lockClass: { lockArgs: [lockScript.args], - codeHash: SystemScriptInfo.MULTI_SIGN_CODE_HASH, - hashType: SystemScriptInfo.MULTI_SIGN_HASH_TYPE, + codeHash: lockScript.codeHash, + hashType: lockScript.hashType, }, multisigConfig, }) @@ -713,7 +714,8 @@ export default class TransactionSender { multisigConfig.blake160s, multisigConfig.r, multisigConfig.m, - multisigConfig.n + multisigConfig.n, + multisigConfig.lockCodeHash ) const multisigAddresses = scriptToAddress(lockScript, NetworksService.getInstance().isMainnet()) @@ -726,8 +728,8 @@ export default class TransactionSender { feeRate, { lockArgs: [lockScript.args], - codeHash: SystemScriptInfo.MULTI_SIGN_CODE_HASH, - hashType: SystemScriptInfo.MULTI_SIGN_HASH_TYPE, + codeHash: lockScript.codeHash, + hashType: lockScript.hashType, }, multisigConfig ) @@ -783,7 +785,8 @@ export default class TransactionSender { multisigConfig.blake160s, multisigConfig.r, multisigConfig.m, - multisigConfig.n + multisigConfig.n, + multisigConfig.lockCodeHash ) const multisigAddresses = scriptToAddress(lockScript, NetworksService.getInstance().isMainnet()) @@ -811,8 +814,8 @@ export default class TransactionSender { feeRate, { lockArgs: [lockScript.args], - codeHash: SystemScriptInfo.MULTI_SIGN_CODE_HASH, - hashType: SystemScriptInfo.MULTI_SIGN_HASH_TYPE, + codeHash: lockScript.codeHash, + hashType: lockScript.hashType, }, multisigConfig ) @@ -847,7 +850,7 @@ export default class TransactionSender { } const cellDep = multisigConfig - ? await SystemScriptInfo.getInstance().getMultiSignCellDep() + ? await SystemScriptInfo.getInstance().getMultiSignCellDep(multisigConfig.lockCodeHash) : await SystemScriptInfo.getInstance().getSecpCellDep() const daoCellDep = await SystemScriptInfo.getInstance().getDaoCellDep() @@ -894,7 +897,8 @@ export default class TransactionSender { multisigConfig.blake160s, multisigConfig.r, multisigConfig.m, - multisigConfig.n + multisigConfig.n, + multisigConfig.lockCodeHash ) output = new Output(outputCapacity.toString(), lockScript, undefined, '0x') } else { @@ -979,7 +983,8 @@ export default class TransactionSender { multisigConfig.blake160s, multisigConfig.r, multisigConfig.m, - multisigConfig.n + multisigConfig.n, + multisigConfig.lockCodeHash ) const multisigAddresses = scriptToAddress(lockScript, NetworksService.getInstance().isMainnet()) @@ -992,8 +997,8 @@ export default class TransactionSender { feeRate, { lockArgs: [lockScript.args], - codeHash: SystemScriptInfo.MULTI_SIGN_CODE_HASH, - hashType: SystemScriptInfo.MULTI_SIGN_HASH_TYPE, + codeHash: lockScript.codeHash, + hashType: lockScript.hashType, }, multisigConfig ) diff --git a/packages/neuron-wallet/src/services/tx/transaction-generator.ts b/packages/neuron-wallet/src/services/tx/transaction-generator.ts index d223311622..3e321e1e3b 100644 --- a/packages/neuron-wallet/src/services/tx/transaction-generator.ts +++ b/packages/neuron-wallet/src/services/tx/transaction-generator.ts @@ -190,8 +190,8 @@ export class TransactionGenerator { enableUseSentCell?: boolean }): Promise => { let cellDep: CellDep - if (lockClass.codeHash === SystemScriptInfo.MULTI_SIGN_CODE_HASH) { - cellDep = await SystemScriptInfo.getInstance().getMultiSignCellDep() + if (SystemScriptInfo.isMultiSignCodeHash(lockClass.codeHash)) { + cellDep = await SystemScriptInfo.getInstance().getMultiSignCellDep(lockClass.codeHash) } else { cellDep = await SystemScriptInfo.getInstance().getSecpCellDep() } @@ -212,7 +212,10 @@ export class TransactionGenerator { if (date) { const blake160 = lockScript.args const minutes: number = +((BigInt(date) - BigInt(tipHeaderTimestamp)) / BigInt(1000 * 60)).toString() - const script = SystemScriptInfo.generateMultiSignScript(Multisig.args(blake160, +minutes, tipHeaderEpoch)) + const script = SystemScriptInfo.generateMultiSignScript( + Multisig.args(blake160, +minutes, tipHeaderEpoch), + SystemScriptInfo.LEGACY_MULTI_SIGN_CODE_HASH + ) output.setLock(script) output.setMultiSignBlake160(script.args.slice(0, 42)) } @@ -292,7 +295,7 @@ export class TransactionGenerator { }): Promise => { let cellDep: CellDep if (multisigConfig) { - cellDep = await SystemScriptInfo.getInstance().getMultiSignCellDep() + cellDep = await SystemScriptInfo.getInstance().getMultiSignCellDep(multisigConfig.lockCodeHash) } else { cellDep = await SystemScriptInfo.getInstance().getSecpCellDep() } @@ -308,7 +311,13 @@ export class TransactionGenerator { walletID, multisigConfig ? Script.fromSDK( - Multisig.getMultisigScript(multisigConfig.blake160s, multisigConfig.r, multisigConfig.m, multisigConfig.n) + Multisig.getMultisigScript( + multisigConfig.blake160s, + multisigConfig.r, + multisigConfig.m, + multisigConfig.n, + multisigConfig.lockCodeHash + ) ) : undefined, consumeOutPoints, @@ -333,7 +342,8 @@ export class TransactionGenerator { const blake160 = lockScript.args const minutes: number = +((BigInt(date) - BigInt(tipHeaderTimestamp)) / BigInt(1000 * 60)).toString() const script: Script = SystemScriptInfo.generateMultiSignScript( - Multisig.args(blake160, minutes, tipHeaderEpoch) + Multisig.args(blake160, minutes, tipHeaderEpoch), + SystemScriptInfo.LEGACY_MULTI_SIGN_CODE_HASH ) output.setLock(script) output.setMultiSignBlake160(script.args.slice(0, 42)) @@ -417,8 +427,8 @@ export class TransactionGenerator { multisigConfig?: MultisigConfigModel ): Promise => { let cellDep: CellDep - if (lockClass.codeHash === SystemScriptInfo.MULTI_SIGN_CODE_HASH) { - cellDep = await SystemScriptInfo.getInstance().getMultiSignCellDep() + if (SystemScriptInfo.isMultiSignCodeHash(lockClass.codeHash)) { + cellDep = await SystemScriptInfo.getInstance().getMultiSignCellDep(lockClass.codeHash) } else { cellDep = await SystemScriptInfo.getInstance().getSecpCellDep() } @@ -491,8 +501,8 @@ export class TransactionGenerator { multisigConfig?: MultisigConfigModel ): Promise => { let cellDep: CellDep - if (lockClass.codeHash === SystemScriptInfo.MULTI_SIGN_CODE_HASH) { - cellDep = await SystemScriptInfo.getInstance().getMultiSignCellDep() + if (SystemScriptInfo.isMultiSignCodeHash(lockClass.codeHash)) { + cellDep = await SystemScriptInfo.getInstance().getMultiSignCellDep(lockClass.codeHash) } else { cellDep = await SystemScriptInfo.getInstance().getSecpCellDep() } @@ -505,7 +515,13 @@ export class TransactionGenerator { const allInputs: Input[] = await CellsService.gatherAllInputs( walletId, multisigConfig - ? Multisig.getMultisigScript(multisigConfig.blake160s, multisigConfig.r, multisigConfig.m, multisigConfig.n) + ? Multisig.getMultisigScript( + multisigConfig.blake160s, + multisigConfig.r, + multisigConfig.m, + multisigConfig.n, + multisigConfig.lockCodeHash + ) : undefined ) if (allInputs.length === 0) { @@ -573,8 +589,8 @@ export class TransactionGenerator { multisigConfig?: MultisigConfigModel ): Promise => { let cellDep: CellDep - if (lockClass.codeHash === SystemScriptInfo.MULTI_SIGN_CODE_HASH) { - cellDep = await SystemScriptInfo.getInstance().getMultiSignCellDep() + if (SystemScriptInfo.isMultiSignCodeHash(lockClass.codeHash)) { + cellDep = await SystemScriptInfo.getInstance().getMultiSignCellDep(lockClass.codeHash) } else { cellDep = await SystemScriptInfo.getInstance().getSecpCellDep() } diff --git a/packages/neuron-wallet/src/utils/multisig.ts b/packages/neuron-wallet/src/utils/multisig.ts index d692479f75..e06eb254c2 100644 --- a/packages/neuron-wallet/src/utils/multisig.ts +++ b/packages/neuron-wallet/src/utils/multisig.ts @@ -10,7 +10,13 @@ import { deepCamelizeKeys } from './deep-camelize-keys' export const getMultisigStatus = (multisigConfig: MultisigConfigModel, signatures: Signatures) => { const multisigLockHash = scriptToHash( - Multisig.getMultisigScript(multisigConfig.blake160s, multisigConfig.r, multisigConfig.m, multisigConfig.n) + Multisig.getMultisigScript( + multisigConfig.blake160s, + multisigConfig.r, + multisigConfig.m, + multisigConfig.n, + multisigConfig.lockCodeHash + ) ) let signed = 0 signatures?.[multisigLockHash]?.forEach(blake160 => { @@ -37,7 +43,7 @@ export const parseMultisigTxJsonFromCkbCli = (tx: OfflineSignJSON): Transaction const txObj = Transaction.fromObject(deepCamelizeKeys(transaction) as any) if (multisig_configs && Object.keys(multisig_configs).length) { const args = Object.keys(multisig_configs)[0] - const lock = SystemScriptInfo.generateMultiSignScript(args) + const lock = SystemScriptInfo.generateMultiSignScript(args, SystemScriptInfo.LEGACY_MULTI_SIGN_CODE_HASH) txObj.inputs.forEach((input: Input) => { if (!input?.lock) { diff --git a/packages/neuron-wallet/src/utils/systemScripts.ts b/packages/neuron-wallet/src/utils/systemScripts.ts index 469491cfe5..df6641ae4c 100644 --- a/packages/neuron-wallet/src/utils/systemScripts.ts +++ b/packages/neuron-wallet/src/utils/systemScripts.ts @@ -12,10 +12,6 @@ export const systemScripts = { CODE_HASH: systemScriptsMainnet.DAO.CODE_HASH, HASH_TYPE: systemScriptsMainnet.DAO.HASH_TYPE, }, - SECP256K1_BLAKE160_MULTISIG: { - CODE_HASH: systemScriptsMainnet.SECP256K1_BLAKE160_MULTISIG.CODE_HASH, - HASH_TYPE: systemScriptsMainnet.SECP256K1_BLAKE160_MULTISIG.HASH_TYPE, - }, ANYONE_CAN_PAY_MAINNET: { CODE_HASH: systemScriptsMainnet.ANYONE_CAN_PAY.CODE_HASH, HASH_TYPE: systemScriptsMainnet.ANYONE_CAN_PAY.HASH_TYPE, diff --git a/packages/neuron-wallet/tests/controllers/multisig.test.ts b/packages/neuron-wallet/tests/controllers/multisig.test.ts index fff2030f95..369f54316e 100644 --- a/packages/neuron-wallet/tests/controllers/multisig.test.ts +++ b/packages/neuron-wallet/tests/controllers/multisig.test.ts @@ -5,6 +5,7 @@ import CellsService from '../../src/services/cells' import { scriptToAddress } from '../../src/utils/scriptAndAddress' import { systemScripts } from '../../src/utils/systemScripts' import { NetworkType } from '../../src/models/network' +import SystemScriptInfo from '../../src/models/system-script-info' let response = 0 let dialogRes = { canceled: false, filePaths: ['./'], filePath: './' } @@ -139,6 +140,7 @@ describe('test for multisig controller', () => { m: 1, n: 1, blake160s: [], + lockCodeHash: SystemScriptInfo.MULTI_SIGN_CODE_HASH, alias: 'string', changed: expect.any(Function), lastestBlockNumber: '', diff --git a/packages/neuron-wallet/tests/models/system-script-info.test.ts b/packages/neuron-wallet/tests/models/system-script-info.test.ts index 3ac55ff8f7..346212b09f 100644 --- a/packages/neuron-wallet/tests/models/system-script-info.test.ts +++ b/packages/neuron-wallet/tests/models/system-script-info.test.ts @@ -4,10 +4,15 @@ import SystemScriptInfo from '../../src/models/system-script-info' describe('SystemScriptInfo', () => { const SECP_CODE_HASH = '0x9bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce8' const DAO_CODE_HASH = '0x82d76d1b75fe2fd9a27dfbaa65a039221a380d76c926f378d3f81cf3e7e13f2e' - const MULTI_SIGN_CODE_HASH = '0x5c5069eb0857efc65e1bca0c07df34c31663b3622fd3876c876320fc9634e2a8' + const LEGACY_MULTI_SIGN_CODE_HASH = '0x5c5069eb0857efc65e1bca0c07df34c31663b3622fd3876c876320fc9634e2a8' + const MULTI_SIGN_CODE_HASH = '0x36c971b8d41fbd94aabca77dc75e826729ac98447b46f91e00796155dddb0d29' const secpScript = SystemScriptInfo.generateSecpScript('0x' + '0'.repeat(40)) - const multiSignScript = SystemScriptInfo.generateMultiSignScript('0x' + '0'.repeat(40)) + const legacyMultiSignScript = SystemScriptInfo.generateMultiSignScript( + '0x' + '0'.repeat(40), + LEGACY_MULTI_SIGN_CODE_HASH + ) + const multiSignScript = SystemScriptInfo.generateMultiSignScript('0x' + '0'.repeat(40), MULTI_SIGN_CODE_HASH) const daoScript = SystemScriptInfo.generateDaoScript('0x') it('secp code hash', () => { @@ -18,16 +23,22 @@ describe('SystemScriptInfo', () => { expect(SystemScriptInfo.DAO_CODE_HASH).toEqual(DAO_CODE_HASH) }) + it('legacy multi sign code hash', () => { + expect(SystemScriptInfo.LEGACY_MULTI_SIGN_CODE_HASH).toEqual(LEGACY_MULTI_SIGN_CODE_HASH) + }) + it('multi sign code hash', () => { expect(SystemScriptInfo.MULTI_SIGN_CODE_HASH).toEqual(MULTI_SIGN_CODE_HASH) }) it('isSecpScript', () => { expect(SystemScriptInfo.isSecpScript(secpScript)).toBeTruthy() + expect(SystemScriptInfo.isSecpScript(legacyMultiSignScript)).toBeFalsy() expect(SystemScriptInfo.isSecpScript(multiSignScript)).toBeFalsy() }) it('isMultiSignScript', () => { + expect(SystemScriptInfo.isMultiSignScript(legacyMultiSignScript)).toBeTruthy() expect(SystemScriptInfo.isMultiSignScript(multiSignScript)).toBeTruthy() expect(SystemScriptInfo.isMultiSignScript(secpScript)).toBeFalsy() }) diff --git a/packages/neuron-wallet/tests/services/cells.test.ts b/packages/neuron-wallet/tests/services/cells.test.ts index a50acacc32..980356f7b9 100644 --- a/packages/neuron-wallet/tests/services/cells.test.ts +++ b/packages/neuron-wallet/tests/services/cells.test.ts @@ -77,7 +77,10 @@ describe('CellsService', () => { } const multisigPublicKeyHash = '0x447ff8941a6f0162d2194e9b592bb2534a3e6b74' - const multisigLockScript = SystemScriptInfo.generateMultiSignScript(multisigPublicKeyHash) + const multisigLockScript = SystemScriptInfo.generateMultiSignScript( + multisigPublicKeyHash, + SystemScriptInfo.LEGACY_MULTI_SIGN_CODE_HASH + ) const multisigInfo = { lockScript: multisigLockScript, lockHash: multisigLockScript.computeHash(), @@ -138,7 +141,7 @@ describe('CellsService', () => { output.lockCodeHash = who.lockScript.codeHash output.lockArgs = who.lockScript.args output.lockHashType = who.lockScript.hashType - if (who.lockScript.codeHash === SystemScriptInfo.MULTI_SIGN_CODE_HASH) { + if (who.lockScript.codeHash === SystemScriptInfo.LEGACY_MULTI_SIGN_CODE_HASH) { output.multiSignBlake160 = who.lockScript.args } output.lockHash = who.lockScript.computeHash() @@ -555,8 +558,8 @@ describe('CellsService', () => { await expect( CellsService.gatherInputs(toShannon('1001'), '', '0', '1000', 0, 0, 0, undefined, { lockArgs: ['bob.blake160'], - hashType: SystemScriptInfo.MULTI_SIGN_HASH_TYPE, - codeHash: SystemScriptInfo.MULTI_SIGN_CODE_HASH, + hashType: SystemScriptInfo.LEGACY_MULTI_SIGN_HASH_TYPE, + codeHash: SystemScriptInfo.LEGACY_MULTI_SIGN_CODE_HASH, }) ).rejects.toThrow(new CapacityNotEnough()) }) @@ -565,8 +568,8 @@ describe('CellsService', () => { await expect( CellsService.gatherInputs(toShannon('1001'), '', '0', '1000', 0, 0, 0, undefined, { lockArgs: [multisigInfo.lockScript.args], - hashType: SystemScriptInfo.MULTI_SIGN_HASH_TYPE, - codeHash: SystemScriptInfo.MULTI_SIGN_CODE_HASH, + hashType: SystemScriptInfo.LEGACY_MULTI_SIGN_HASH_TYPE, + codeHash: SystemScriptInfo.LEGACY_MULTI_SIGN_CODE_HASH, }) ).rejects.toThrow(new MultisigConfigNeedError()) }) @@ -584,8 +587,8 @@ describe('CellsService', () => { undefined, { lockArgs: [multisigInfo.lockScript.args], - hashType: SystemScriptInfo.MULTI_SIGN_HASH_TYPE, - codeHash: SystemScriptInfo.MULTI_SIGN_CODE_HASH, + hashType: SystemScriptInfo.LEGACY_MULTI_SIGN_HASH_TYPE, + codeHash: SystemScriptInfo.LEGACY_MULTI_SIGN_CODE_HASH, }, [ { @@ -614,8 +617,8 @@ describe('CellsService', () => { undefined, { lockArgs: [multisigInfo.lockScript.args], - hashType: SystemScriptInfo.MULTI_SIGN_HASH_TYPE, - codeHash: SystemScriptInfo.MULTI_SIGN_CODE_HASH, + hashType: SystemScriptInfo.LEGACY_MULTI_SIGN_HASH_TYPE, + codeHash: SystemScriptInfo.LEGACY_MULTI_SIGN_CODE_HASH, }, [ { @@ -644,8 +647,8 @@ describe('CellsService', () => { undefined, { lockArgs: [multisigInfo.lockScript.args], - hashType: SystemScriptInfo.MULTI_SIGN_HASH_TYPE, - codeHash: SystemScriptInfo.MULTI_SIGN_CODE_HASH, + hashType: SystemScriptInfo.LEGACY_MULTI_SIGN_HASH_TYPE, + codeHash: SystemScriptInfo.LEGACY_MULTI_SIGN_CODE_HASH, }, [ { @@ -673,8 +676,8 @@ describe('CellsService', () => { undefined, { lockArgs: [multisigInfo.lockScript.args], - hashType: SystemScriptInfo.MULTI_SIGN_HASH_TYPE, - codeHash: SystemScriptInfo.MULTI_SIGN_CODE_HASH, + hashType: SystemScriptInfo.LEGACY_MULTI_SIGN_HASH_TYPE, + codeHash: SystemScriptInfo.LEGACY_MULTI_SIGN_CODE_HASH, }, [ { @@ -703,8 +706,8 @@ describe('CellsService', () => { undefined, { lockArgs: [multisigInfo.lockScript.args], - hashType: SystemScriptInfo.MULTI_SIGN_HASH_TYPE, - codeHash: SystemScriptInfo.MULTI_SIGN_CODE_HASH, + hashType: SystemScriptInfo.LEGACY_MULTI_SIGN_HASH_TYPE, + codeHash: SystemScriptInfo.LEGACY_MULTI_SIGN_CODE_HASH, }, [ { @@ -780,8 +783,8 @@ describe('CellsService', () => { }) it('gather with exist args', async () => { const inputs = await CellsService.gatherAllInputs(walletId1, { - codeHash: SystemScriptInfo.MULTI_SIGN_CODE_HASH, - hashType: SystemScriptInfo.MULTI_SIGN_HASH_TYPE, + codeHash: SystemScriptInfo.LEGACY_MULTI_SIGN_CODE_HASH, + hashType: SystemScriptInfo.LEGACY_MULTI_SIGN_HASH_TYPE, args: multisigInfo.lockScript.args, }) expect(inputs).toHaveLength(1) @@ -789,8 +792,8 @@ describe('CellsService', () => { }) it('gather with non-exist args', async () => { const inputs = await CellsService.gatherAllInputs(walletId1, { - codeHash: SystemScriptInfo.MULTI_SIGN_CODE_HASH, - hashType: SystemScriptInfo.MULTI_SIGN_HASH_TYPE, + codeHash: SystemScriptInfo.LEGACY_MULTI_SIGN_CODE_HASH, + hashType: SystemScriptInfo.LEGACY_MULTI_SIGN_HASH_TYPE, args: 'non-exist-args', }) expect(inputs).toHaveLength(0) @@ -1231,7 +1234,10 @@ describe('CellsService', () => { const publicKeyHash = bob.lockScript.args const bobDefaultLock = SystemScriptInfo.generateSecpScript(publicKeyHash) const multiSignHash = Multisig.hash([publicKeyHash]) - const multiSignLockScript = SystemScriptInfo.generateMultiSignScript(multiSignHash) + const multiSignLockScript = SystemScriptInfo.generateMultiSignScript( + multiSignHash, + SystemScriptInfo.LEGACY_MULTI_SIGN_CODE_HASH + ) const pageSize = 2 describe('with indexed customized asset outputs', () => { @@ -1689,7 +1695,10 @@ describe('CellsService', () => { '61', OutputStatus.Sent, false, - SystemScriptInfo.generateMultiSignScript(Multisig.args(bob.blake160, +10, '0x7080291000049')) + SystemScriptInfo.generateMultiSignScript( + Multisig.args(bob.blake160, +10, '0x7080291000049'), + SystemScriptInfo.LEGACY_MULTI_SIGN_CODE_HASH + ) ) await expect(CellsService.getLiveOrSentCellByWalletId(bob.walletId)).resolves.toHaveLength(1) }) @@ -1731,14 +1740,17 @@ describe('CellsService', () => { it('MULTI_LOCK_TIME', () => { const output = Output.fromObject({ capacity: '1000', - lock: SystemScriptInfo.generateMultiSignScript(Multisig.args(bob.blake160, +10, '0x7080291000049')), + lock: SystemScriptInfo.generateMultiSignScript( + Multisig.args(bob.blake160, +10, '0x7080291000049'), + SystemScriptInfo.LEGACY_MULTI_SIGN_CODE_HASH + ), }) expect(CellsService.getCellLockType(output)).toBe(LockScriptCategory.MULTI_LOCK_TIME) }) it('MULTI_LOCK', () => { const output = Output.fromObject({ capacity: '1000', - lock: Multisig.getMultisigScript([bob.blake160], 1, 1, 1), + lock: Multisig.getMultisigScript([bob.blake160], 1, 1, 1, SystemScriptInfo.LEGACY_MULTI_SIGN_CODE_HASH), }) expect(CellsService.getCellLockType(output)).toBe(LockScriptCategory.MULTISIG) }) @@ -1863,7 +1875,7 @@ describe('CellsService', () => { input.lockCodeHash = who.lockScript.codeHash input.lockArgs = who.lockScript.args input.lockHashType = who.lockScript.hashType - if (who.lockScript.codeHash === SystemScriptInfo.MULTI_SIGN_CODE_HASH) { + if (who.lockScript.codeHash === SystemScriptInfo.LEGACY_MULTI_SIGN_CODE_HASH) { input.multiSignBlake160 = who.lockScript.args } input.lockHash = who.lockScript.computeHash() diff --git a/packages/neuron-wallet/tests/services/multisig.test.ts b/packages/neuron-wallet/tests/services/multisig.test.ts index 155cfab9f0..569180bf63 100644 --- a/packages/neuron-wallet/tests/services/multisig.test.ts +++ b/packages/neuron-wallet/tests/services/multisig.test.ts @@ -52,6 +52,7 @@ describe('multisig service', () => { 2, 3, [alice.publicKeyInBlake160, bob.publicKeyInBlake160, charlie.publicKeyInBlake160], + SystemScriptInfo.MULTI_SIGN_CODE_HASH, 'alias' ) const defaultMultisigConfig = MultisigConfig.fromModel(multisigConfigModel) @@ -234,11 +235,14 @@ describe('multisig service', () => { describe('removeDulpicateConfig', () => { it('exist duplicate config', () => { - const multisigConfigModel = new MultisigConfigModel('walletId', 1, 2, 3, [ - alice.publicKeyInBlake160, - bob.publicKeyInBlake160, - charlie.publicKeyInBlake160, - ]) + const multisigConfigModel = new MultisigConfigModel( + 'walletId', + 1, + 2, + 3, + [alice.publicKeyInBlake160, bob.publicKeyInBlake160, charlie.publicKeyInBlake160], + SystemScriptInfo.MULTI_SIGN_CODE_HASH + ) const multisigConfigs = [ MultisigConfig.fromModel(multisigConfigModel), MultisigConfig.fromModel(multisigConfigModel), @@ -250,18 +254,24 @@ describe('multisig service', () => { it('non-exist duplicate config', () => { const multisigConfigs = [ MultisigConfig.fromModel( - new MultisigConfigModel('walletId', 1, 2, 3, [ - alice.publicKeyInBlake160, - bob.publicKeyInBlake160, - charlie.publicKeyInBlake160, - ]) + new MultisigConfigModel( + 'walletId', + 1, + 2, + 3, + [alice.publicKeyInBlake160, bob.publicKeyInBlake160, charlie.publicKeyInBlake160], + SystemScriptInfo.MULTI_SIGN_CODE_HASH + ) ), MultisigConfig.fromModel( - new MultisigConfigModel('walletId', 2, 2, 3, [ - alice.publicKeyInBlake160, - bob.publicKeyInBlake160, - charlie.publicKeyInBlake160, - ]) + new MultisigConfigModel( + 'walletId', + 2, + 2, + 3, + [alice.publicKeyInBlake160, bob.publicKeyInBlake160, charlie.publicKeyInBlake160], + SystemScriptInfo.MULTI_SIGN_CODE_HASH + ) ), ] //@ts-ignore private-method diff --git a/packages/neuron-wallet/tests/services/tx/transaction-generator.test.ts b/packages/neuron-wallet/tests/services/tx/transaction-generator.test.ts index 057a0c6018..d4bbe24dff 100644 --- a/packages/neuron-wallet/tests/services/tx/transaction-generator.test.ts +++ b/packages/neuron-wallet/tests/services/tx/transaction-generator.test.ts @@ -468,7 +468,7 @@ describe('TransactionGenerator', () => { expect(expectedFee).toEqual(BigInt(472)) expect(tx.fee).toEqual(expectedFee.toString()) - const multiSignOutput = tx.outputs.find(o => o.lock.codeHash === SystemScriptInfo.MULTI_SIGN_CODE_HASH) + const multiSignOutput = tx.outputs.find(o => o.lock.codeHash === SystemScriptInfo.LEGACY_MULTI_SIGN_CODE_HASH) expect(multiSignOutput).toBeDefined() const epoch = Multisig.parseSince(multiSignOutput!.lock.args) @@ -730,7 +730,7 @@ describe('TransactionGenerator', () => { feeRate, }) - expect(tx.outputs[0].lock.codeHash).toEqual(SystemScriptInfo.MULTI_SIGN_CODE_HASH) + expect(tx.outputs[0].lock.codeHash).toEqual(SystemScriptInfo.LEGACY_MULTI_SIGN_CODE_HASH) const epoch = Multisig.parseSince(tx.outputs[0].lock.args) const parsedEpoch = since.parseEpoch(epoch) @@ -764,6 +764,7 @@ describe('TransactionGenerator', () => { 'ckt1qyqdpymnu202x3p4cnrrgek5czcdsg95xznswjr98y', 'ckt1qyqwqcknusdreymrhhme00hg9af3pr5hcmwqzfxvda', ].map(v => addressToScript(v).args), + lockCodeHash: SystemScriptInfo.MULTI_SIGN_CODE_HASH, }), }) @@ -1042,7 +1043,10 @@ describe('TransactionGenerator', () => { describe('generateWithdrawMultiSignTx', () => { const prevOutput = Output.fromObject({ capacity: toShannon('1000'), - lock: SystemScriptInfo.generateMultiSignScript(Multisig.args(bob.lockScript.args, 100, '0x7080018000001')), + lock: SystemScriptInfo.generateMultiSignScript( + Multisig.args(bob.lockScript.args, 100, '0x7080018000001'), + SystemScriptInfo.LEGACY_MULTI_SIGN_CODE_HASH + ), }) const outPoint = OutPoint.fromObject({ txHash: '0x' + '0'.repeat(64), diff --git a/packages/neuron-wallet/tests/services/tx/transaction-persistor.test.ts b/packages/neuron-wallet/tests/services/tx/transaction-persistor.test.ts index e39e3d482f..5c98418991 100644 --- a/packages/neuron-wallet/tests/services/tx/transaction-persistor.test.ts +++ b/packages/neuron-wallet/tests/services/tx/transaction-persistor.test.ts @@ -145,7 +145,8 @@ describe('TransactionPersistor', () => { it('filter with multi lock time', async () => { const txWithCheque = Transaction.fromObject(tx) const multisigLockTimeLock = SystemScriptInfo.generateMultiSignScript( - Multisig.hash([txWithCheque.outputs[0].lock.args]) + Multisig.hash([txWithCheque.outputs[0].lock.args]), + SystemScriptInfo.LEGACY_MULTI_SIGN_CODE_HASH ) txWithCheque.outputs[0].setLock(multisigLockTimeLock) const args = [...tx.inputs.map(v => v.lock?.args), ...tx.outputs.map(v => v.lock.args)] diff --git a/packages/neuron-wallet/tests/services/tx/transaction-sender.test.ts b/packages/neuron-wallet/tests/services/tx/transaction-sender.test.ts index b73ae330ed..4cca24721b 100644 --- a/packages/neuron-wallet/tests/services/tx/transaction-sender.test.ts +++ b/packages/neuron-wallet/tests/services/tx/transaction-sender.test.ts @@ -692,6 +692,7 @@ describe('TransactionSender Test', () => { n: 1, r: 1, blake160s: ['blake160s'], + lockCodeHash: SystemScriptInfo.LEGACY_MULTI_SIGN_CODE_HASH, }) await transactionSender.generateMultisigSendAllTx(targetOutputs, multisigConfig) expect(stubbedGenerateSendingAllTx).toHaveBeenCalledWith({ @@ -894,8 +895,8 @@ describe('TransactionSender Test', () => { since: '0x0', lock: Script.fromObject({ args: '', - codeHash: SystemScriptInfo.MULTI_SIGN_CODE_HASH, - hashType: SystemScriptInfo.MULTI_SIGN_HASH_TYPE, + codeHash: SystemScriptInfo.LEGACY_MULTI_SIGN_CODE_HASH, + hashType: SystemScriptInfo.LEGACY_MULTI_SIGN_HASH_TYPE, }), }), ], @@ -935,6 +936,7 @@ describe('TransactionSender Test', () => { m, n: addresses.length, blake160s: addresses.map(v => addressToScript(v).args), + lockCodeHash: SystemScriptInfo.LEGACY_MULTI_SIGN_CODE_HASH, }), ] } @@ -956,7 +958,9 @@ describe('TransactionSender Test', () => { mockGAI.mockReturnValueOnce([addr]) transactionSender.getAddressInfos = mockGAI.bind(transactionSender) const tx = Transaction.fromObject(transactionObject) - tx.inputs[0]!.setLock(SystemScriptInfo.generateMultiSignScript(multiArgs)) + tx.inputs[0]!.setLock( + SystemScriptInfo.generateMultiSignScript(multiArgs, SystemScriptInfo.LEGACY_MULTI_SIGN_CODE_HASH) + ) const res = await transactionSender.signMultisig(fakeWallet.id, tx, '1234', [multisigConfig]) expect(res.witnesses[0]).toBe( '0x810000001000000081000000810000006d00000000010102729a884056fed321daaca410d94a5d6425a6ca1f0fbd88910d2348d69da5a980f2376a7a1a04feb5e3b593ad962c15abe214722ef6f84c186d757c6807a70e705adde5dea39b6856643cbf36312df13cc6a46e2a015e8508cecf2e31d5e2a65264516eb9b89ebbb701' @@ -985,7 +989,9 @@ describe('TransactionSender Test', () => { it('first sign', async () => { const getAddressInfos = transactionSender.getAddressInfos transactionSender.getAddressInfos = mockGAI.bind(transactionSender) - tx.inputs[0]!.setLock(SystemScriptInfo.generateMultiSignScript(multiArgs)) + tx.inputs[0]!.setLock( + SystemScriptInfo.generateMultiSignScript(multiArgs, SystemScriptInfo.LEGACY_MULTI_SIGN_CODE_HASH) + ) tx = await transactionSender.signMultisig(fakeWallet.id, tx, '1234', [multisigConfig]) const lock = (tx.witnesses[0] as WitnessArgs).lock! const serializedMultiSign: string = Multisig.serialize( @@ -1051,7 +1057,9 @@ describe('TransactionSender Test', () => { transactionSender.getAddressInfos = mockGAI.bind(transactionSender) const tx = Transaction.fromObject(transactionObject) - tx.inputs[0]!.setLock(SystemScriptInfo.generateMultiSignScript(multiArgs)) + tx.inputs[0]!.setLock( + SystemScriptInfo.generateMultiSignScript(multiArgs, SystemScriptInfo.LEGACY_MULTI_SIGN_CODE_HASH) + ) await expect(transactionSender.signMultisig(fakeWallet.id, tx, '1234', [multisigConfig])).rejects.toThrow( new NoMatchAddressForSign() ) @@ -1088,7 +1096,9 @@ describe('TransactionSender Test', () => { mockGAI.mockReturnValueOnce([addr]) transactionSender.getAddressInfos = mockGAI.bind(transactionSender) const tx = Transaction.fromObject(transactionObject) - tx.inputs[0]!.setLock(SystemScriptInfo.generateMultiSignScript(multiArgs)) + tx.inputs[0]!.setLock( + SystemScriptInfo.generateMultiSignScript(multiArgs, SystemScriptInfo.LEGACY_MULTI_SIGN_CODE_HASH) + ) const res = await transactionSender.signMultisig(fakeWallet.id, tx, '1234', [multisigConfig]) const expectedValue = serializeWitnessArgs({ inputType: undefined, @@ -1122,7 +1132,9 @@ describe('TransactionSender Test', () => { mockGAI.mockReturnValueOnce([addr]) transactionSender.getAddressInfos = mockGAI.bind(transactionSender) const tx = Transaction.fromObject(transactionObject) - tx.inputs[0]!.setLock(SystemScriptInfo.generateMultiSignScript(multiArgs)) + tx.inputs[0]!.setLock( + SystemScriptInfo.generateMultiSignScript(multiArgs, SystemScriptInfo.LEGACY_MULTI_SIGN_CODE_HASH) + ) tx.inputs.push( Input.fromObject({ previousOutput: OutPoint.fromObject({ @@ -1132,8 +1144,8 @@ describe('TransactionSender Test', () => { since: '0x0', lock: Script.fromObject({ args: multiArgs, - codeHash: SystemScriptInfo.MULTI_SIGN_CODE_HASH, - hashType: SystemScriptInfo.MULTI_SIGN_HASH_TYPE, + codeHash: SystemScriptInfo.LEGACY_MULTI_SIGN_CODE_HASH, + hashType: SystemScriptInfo.LEGACY_MULTI_SIGN_HASH_TYPE, }), }) ) diff --git a/packages/neuron-wallet/tests/setup.ts b/packages/neuron-wallet/tests/setup.ts index 7f7d615326..8e452a0fa0 100644 --- a/packages/neuron-wallet/tests/setup.ts +++ b/packages/neuron-wallet/tests/setup.ts @@ -95,7 +95,10 @@ jest.mock('dotenv', () => ({ process.env.SECP256K1_CODE_HASH = '0x9bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce8' process.env.DAO_CODE_HASH = '0x82d76d1b75fe2fd9a27dfbaa65a039221a380d76c926f378d3f81cf3e7e13f2e' - process.env.MULTISIG_CODE_HASH = '0x5c5069eb0857efc65e1bca0c07df34c31663b3622fd3876c876320fc9634e2a8' + process.env.LEGACY_MULTISIG_CODE_HASH = '0x5c5069eb0857efc65e1bca0c07df34c31663b3622fd3876c876320fc9634e2a8' + process.env.MULTISIG_CODE_HASH = '0x36c971b8d41fbd94aabca77dc75e826729ac98447b46f91e00796155dddb0d29' + process.env.MAINNET_MULTISIG_TXHASH = '0x6888aa39ab30c570c2c30d9d5684d3769bf77265a7973211a3c087fe8efbf738' + process.env.TESTNET_MULTISIG_TXHASH = '0x2eefdeb21f3a3edf697c28a52601b4419806ed60bb427420455cc29a090b26d5' process.env.TESTNET_NFT_SCRIPT_CODEHASH = '0xb1837b5ad01a88558731953062d1f5cb547adf89ece01e8934a9f0aeed2d959f' process.env.TESTNET_NFT_SCRIPT_HASH_TYPE = 'type' diff --git a/packages/neuron-wallet/tests/utils/multisig.test.ts b/packages/neuron-wallet/tests/utils/multisig.test.ts index a8efc90acb..6d33c95b18 100644 --- a/packages/neuron-wallet/tests/utils/multisig.test.ts +++ b/packages/neuron-wallet/tests/utils/multisig.test.ts @@ -1,7 +1,7 @@ import { getMultisigStatus } from '../../src/utils/multisig' import MultisigConfigModel from '../../src/models/multisig-config' import { SignStatus } from '../../src/models/offline-sign' - +import SystemScriptInfo from '../../src/models/system-script-info' describe('getMultisigStatus test', () => { const addressesToArgs: Record = { ckt1qyq89x5ggpt0a5epm2k2gyxeffwkgfdxeg0s543mh4: '0x729a884056fed321daaca410d94a5d6425a6ca1f', @@ -15,6 +15,7 @@ describe('getMultisigStatus test', () => { m: 2, n: 3, blake160s: Object.values(addressesToArgs), + lockCodeHash: SystemScriptInfo.LEGACY_MULTI_SIGN_CODE_HASH, }) it('Unsigned', () => { expect(getMultisigStatus(multisigConfig, {})).toBe(SignStatus.Unsigned) From 05af30f81e3829b8afcc44a424284f6e7a361692 Mon Sep 17 00:00:00 2001 From: chenyan Date: Thu, 24 Apr 2025 00:09:58 +0800 Subject: [PATCH 08/22] fix: spell --- .../neuron-ui/src/components/MultisigAddress/hooks.ts | 4 ++-- .../neuron-ui/src/components/MultisigAddress/index.tsx | 4 ++-- .../src/components/MultisigAddressCreateDialog/index.tsx | 3 ++- packages/neuron-ui/src/services/localCache.ts | 8 ++++---- packages/neuron-ui/src/services/remote/multisig.ts | 2 +- .../src/tests/getMultisigSignStatus/index.test.ts | 5 +++-- packages/neuron-ui/src/types/App/index.d.ts | 1 + 7 files changed, 15 insertions(+), 12 deletions(-) diff --git a/packages/neuron-ui/src/components/MultisigAddress/hooks.ts b/packages/neuron-ui/src/components/MultisigAddress/hooks.ts index 2e2dbcd369..d49302be61 100644 --- a/packages/neuron-ui/src/components/MultisigAddress/hooks.ts +++ b/packages/neuron-ui/src/components/MultisigAddress/hooks.ts @@ -25,7 +25,7 @@ import { getMultisigSyncProgress, } from 'services/remote' import { computeScriptHash } from '@ckb-lumos/lumos/utils' -import { remaindRegenerateMultisigAddress } from 'services/localCache' +import { remindRegenerateMultisigAddress } from 'services/localCache' export const useSearch = (clearSelected: () => void, onFilterConfig: (searchKey: string) => void) => { const [keywords, setKeywords] = useState('') @@ -321,7 +321,7 @@ const useRegenerateAction = (regenerateConfig: (config: MultisigConfig) => Promi (e: React.ChangeEvent) => { const { checked } = e.target setIsNoRemind(checked) - remaindRegenerateMultisigAddress.save(checked) + remindRegenerateMultisigAddress.save(checked) }, [setIsNoRemind] ) diff --git a/packages/neuron-ui/src/components/MultisigAddress/index.tsx b/packages/neuron-ui/src/components/MultisigAddress/index.tsx index 2b2260ced2..405e794b3c 100644 --- a/packages/neuron-ui/src/components/MultisigAddress/index.tsx +++ b/packages/neuron-ui/src/components/MultisigAddress/index.tsx @@ -50,7 +50,7 @@ import Button from 'widgets/Button' import SetStartBlockNumberDialog from 'components/SetStartBlockNumberDialog' import { type TFunction } from 'i18next' import hooks from 'components/NervosDAO/hooks' -import { remaindRegenerateMultisigAddress } from 'services/localCache' +import { remindRegenerateMultisigAddress } from 'services/localCache' import { useSearch, useConfigManage, @@ -189,7 +189,7 @@ const MultisigAddress = () => { daoWithdrawAction.action(multisigConfig) break case 'regenerate': - if (remaindRegenerateMultisigAddress.get()) { + if (remindRegenerateMultisigAddress.get()) { regenerateAction.action(multisigConfig) } else { regenerateAction.setConfig(multisigConfig) diff --git a/packages/neuron-ui/src/components/MultisigAddressCreateDialog/index.tsx b/packages/neuron-ui/src/components/MultisigAddressCreateDialog/index.tsx index b6ad621098..ff2c938706 100644 --- a/packages/neuron-ui/src/components/MultisigAddressCreateDialog/index.tsx +++ b/packages/neuron-ui/src/components/MultisigAddressCreateDialog/index.tsx @@ -3,7 +3,7 @@ import { useTranslation } from 'react-i18next' import { useState as useGlobalState } from 'states' import { MultisigConfig } from 'services/remote' import MultisigAddressInfo, { MultisigAddressTable } from 'components/MultisigAddressInfo' -import { isMainnet as isMainnetUtil } from 'utils' +import { isMainnet as isMainnetUtil, MultiSigLockInfo } from 'utils' import Dialog from 'widgets/Dialog' import TextField from 'widgets/TextField' import { useMAndN, useMultiAddress, useViewMultisigAddress } from './hooks' @@ -90,6 +90,7 @@ const MultisigAddressCreateDialog = ({ n: Number(n), r: Number(r), addresses, + lockCodeHash: MultiSigLockInfo.CodeHash, }) .then(() => { closeDialog() diff --git a/packages/neuron-ui/src/services/localCache.ts b/packages/neuron-ui/src/services/localCache.ts index 2323645a4d..45dc67e936 100644 --- a/packages/neuron-ui/src/services/localCache.ts +++ b/packages/neuron-ui/src/services/localCache.ts @@ -13,7 +13,7 @@ export enum LocalCacheKey { ShownNodeId = 'ShownNodeId', ScreenAwake = 'ScreenAwake', RetryUnlockWindowInfo = 'RetryUnlockWindowInfo', - RemaindRegenerateMultisigAddress = 'RemaindRegenerateMultisigAddress', + RemindRegenerateMultisigAddress = 'RemindRegenerateMultisigAddress', } export const addresses = { @@ -196,12 +196,12 @@ export const retryUnlockWindow = { }, } -export const remaindRegenerateMultisigAddress = { +export const remindRegenerateMultisigAddress = { get: () => { - const value = window.localStorage.getItem(LocalCacheKey.RemaindRegenerateMultisigAddress) + const value = window.localStorage.getItem(LocalCacheKey.RemindRegenerateMultisigAddress) return !!value && value === 'true' }, save: (value: boolean) => { - window.localStorage.setItem(LocalCacheKey.RemaindRegenerateMultisigAddress, value.toString()) + window.localStorage.setItem(LocalCacheKey.RemindRegenerateMultisigAddress, value.toString()) }, } diff --git a/packages/neuron-ui/src/services/remote/multisig.ts b/packages/neuron-ui/src/services/remote/multisig.ts index 58d5179dde..9c266b5e2c 100644 --- a/packages/neuron-ui/src/services/remote/multisig.ts +++ b/packages/neuron-ui/src/services/remote/multisig.ts @@ -28,7 +28,7 @@ export type MultisigEntity = MultisigParams & { export type MultisigConfig = MultisigEntity & { addresses: string[] fullPayload: string - isLegacy: boolean + isLegacy?: boolean } export const saveMultisigConfig = remoteApi, MultisigEntity>('save-multisig-config') diff --git a/packages/neuron-ui/src/tests/getMultisigSignStatus/index.test.ts b/packages/neuron-ui/src/tests/getMultisigSignStatus/index.test.ts index cadcac984c..11d2dbc27c 100644 --- a/packages/neuron-ui/src/tests/getMultisigSignStatus/index.test.ts +++ b/packages/neuron-ui/src/tests/getMultisigSignStatus/index.test.ts @@ -1,6 +1,6 @@ import { describe, it, expect } from 'vitest' import { MultisigConfig } from 'services/remote' -import { addressToScript, getMultisigSignStatus } from 'utils' +import { addressToScript, getMultisigSignStatus, MultiSigLockInfo } from 'utils' import { computeScriptHash } from '@ckb-lumos/lumos/utils' const addresses = [ @@ -16,7 +16,8 @@ const multisigConfig: MultisigConfig = { n: 3, addresses, blake160s: addresses.map(v => addressToScript(v).args), - fullPayload: 'ckt1qpw9q60tppt7l3j7r09qcp7lxnp3vcanvgha8pmvsa3jplykxn32sq2f2scddm0lvmq36hmzx8nfhw8ucxzslhqussgky', + lockCodeHash: MultiSigLockInfo.CodeHash, + fullPayload: 'ckt1qqmvjudc6s0mm992hjnhm367sfnjntycg3a5d7g7qpukz4wamvxjjq593v2gev3yp5sxmfr002ydqrcdpepwfkqwqz8gp', } const fullPayloadHash = computeScriptHash(addressToScript(multisigConfig.fullPayload)) diff --git a/packages/neuron-ui/src/types/App/index.d.ts b/packages/neuron-ui/src/types/App/index.d.ts index e1784957d6..d312807913 100644 --- a/packages/neuron-ui/src/types/App/index.d.ts +++ b/packages/neuron-ui/src/types/App/index.d.ts @@ -126,6 +126,7 @@ declare namespace State { m: number n: number blake160s: string[] + lockCodeHash: string } onSuccess?: () => void showType?: 'Global' | '' From 5ab3a884af135d028a250aa6bfeb685326e25aaa Mon Sep 17 00:00:00 2001 From: chenyan Date: Thu, 24 Apr 2025 00:29:02 +0800 Subject: [PATCH 09/22] fix --- .../tests/services/tx/transaction-generator.test.ts | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/packages/neuron-wallet/tests/services/tx/transaction-generator.test.ts b/packages/neuron-wallet/tests/services/tx/transaction-generator.test.ts index d4bbe24dff..a8a9c7d6a9 100644 --- a/packages/neuron-wallet/tests/services/tx/transaction-generator.test.ts +++ b/packages/neuron-wallet/tests/services/tx/transaction-generator.test.ts @@ -110,13 +110,21 @@ describe('TransactionGenerator', () => { ]) // @ts-ignore: Private method - SystemScriptInfo.getInstance().multiSignOutPointInfo = new Map([ + SystemScriptInfo.getInstance().legacyMultiSignOutPointInfo = new Map([ [ '0x92b197aa1fba0f63633922c61c92375c9c074a93e85963554f5499fe1450d0e5', new OutPoint('0x71a7ba8fc96349fea0ed3a5c47992e3b4084b031a42264a018e0072e8172e46c', '1'), ], ]) + // @ts-ignore: Private method + SystemScriptInfo.getInstance().multiSignOutPointInfo = new Map([ + [ + '0x92b197aa1fba0f63633922c61c92375c9c074a93e85963554f5499fe1450d0e5', + new OutPoint('0x6888aa39ab30c570c2c30d9d5684d3769bf77265a7973211a3c087fe8efbf738', '1'), + ], + ]) + const mockTipHeader = jest.fn() mockTipHeader.mockReturnValue(blockHeader) // @ts-ignore: Private method @@ -764,7 +772,7 @@ describe('TransactionGenerator', () => { 'ckt1qyqdpymnu202x3p4cnrrgek5czcdsg95xznswjr98y', 'ckt1qyqwqcknusdreymrhhme00hg9af3pr5hcmwqzfxvda', ].map(v => addressToScript(v).args), - lockCodeHash: SystemScriptInfo.MULTI_SIGN_CODE_HASH, + lockCodeHash: SystemScriptInfo.LEGACY_MULTI_SIGN_CODE_HASH, }), }) From 4b0181991e09fad364c4494d4d11996b4f2c0d42 Mon Sep 17 00:00:00 2001 From: chenyan Date: Thu, 24 Apr 2025 11:05:30 +0800 Subject: [PATCH 10/22] update --- .../services/tx/transaction-sender.test.ts | 288 +++++++++++++++++- 1 file changed, 287 insertions(+), 1 deletion(-) diff --git a/packages/neuron-wallet/tests/services/tx/transaction-sender.test.ts b/packages/neuron-wallet/tests/services/tx/transaction-sender.test.ts index 4cca24721b..9f11d9b376 100644 --- a/packages/neuron-wallet/tests/services/tx/transaction-sender.test.ts +++ b/packages/neuron-wallet/tests/services/tx/transaction-sender.test.ts @@ -873,7 +873,7 @@ describe('TransactionSender Test', () => { }) }) - describe('#signMultisig', () => { + describe('#signLegacyMultisig', () => { const transactionObject = { version: '0x0', cellDeps: [ @@ -1158,5 +1158,291 @@ describe('TransactionSender Test', () => { ) }) }) + + describe('#signMultisig', () => { + const transactionObject = { + version: '0x0', + cellDeps: [ + CellDep.fromObject({ + outPoint: OutPoint.fromObject({ + txHash: '0x0d9c4af3dd158d6359c9d25d0a600f1dd20b86072b85a095e7bc70c34509b73d', + index: '0x0', + }), + depType: 'depGroup' as DepType, + }), + ], + headerDeps: [], + inputs: [ + Input.fromObject({ + previousOutput: OutPoint.fromObject({ + txHash: '0x1879851943fa686af29bed5c95acd566d0244e7b3ca89cf7c435622a5a5b4cb3', + index: '0x0', + }), + since: '0x0', + lock: Script.fromObject({ + args: '', + codeHash: SystemScriptInfo.MULTI_SIGN_CODE_HASH, + hashType: SystemScriptInfo.MULTI_SIGN_HASH_TYPE, + }), + }), + ], + outputs: [ + Output.fromObject({ + capacity: '0x174876e800', + lock: Script.fromObject({ + codeHash: '0x36c971b8d41fbd94aabca77dc75e826729ac98447b46f91e00796155dddb0d29', + args: '0xe2193df51d78411601796b35b17b4f8f2cd85bd0', + hashType: 'type' as ScriptHashType, + }), + type: null, + }), + Output.fromObject({ + capacity: '0x12319d9962f4', + lock: Script.fromObject({ + codeHash: '0x36c971b8d41fbd94aabca77dc75e826729ac98447b46f91e00796155dddb0d29', + args: '0x36c329ed630d6ce750712a477543672adab57f4c', + hashType: 'type' as ScriptHashType, + }), + type: null, + }), + ], + outputsData: ['0x', '0x'], + witnesses: [], + hash: '0x230ab250ee0ae681e88e462102e5c01a9994ac82bf0effbfb58d6c11a86579f1', + } + + const createMultisigConfig = (r: number, m: number, addresses: string[]): [string, MultisigConfigModel] => { + const blake160s = addresses.map(v => addressToScript(v).args) + const multiArgs = Multisig.hash(blake160s, r, m, addresses.length) + return [ + multiArgs, + MultisigConfigModel.fromObject({ + walletId: fakeWallet.id, + r, + m, + n: addresses.length, + blake160s: addresses.map(v => addressToScript(v).args), + lockCodeHash: SystemScriptInfo.MULTI_SIGN_CODE_HASH, + }), + ] + } + + it('m is 1', async () => { + const addresses = [ + 'ckt1qyq89x5ggpt0a5epm2k2gyxeffwkgfdxeg0s543mh4', + 'ckt1qyqql0vgjyxjxjxknkj6nq8jxa485xsyl66sy7c5f6', + ] + const [multiArgs, multisigConfig] = createMultisigConfig(1, 1, addresses) + const addr = { + walletId: fakeWallet.id, + path: `m/44'/309'/0'/0/0`, + blake160: addressToScript(addresses[0]).args, + version: 'testnet', + } + + const mockGAI = jest.fn() + mockGAI.mockReturnValueOnce([addr]) + transactionSender.getAddressInfos = mockGAI.bind(transactionSender) + const tx = Transaction.fromObject(transactionObject) + tx.inputs[0]!.setLock( + SystemScriptInfo.generateMultiSignScript(multiArgs, SystemScriptInfo.MULTI_SIGN_CODE_HASH) + ) + const res = await transactionSender.signMultisig(fakeWallet.id, tx, '1234', [multisigConfig]) + expect(res.witnesses[0]).toBe( + '0x810000001000000081000000810000006d00000000010102729a884056fed321daaca410d94a5d6425a6ca1f0fbd88910d2348d69da5a980f2376a7a1a04feb56b75ef075f1cb4a3e681846c7d8c6ad1575dc5889a2685df215dc566c346757d6d3d46b4201b38f55f87d381fc65e074c1292145fedda1e6d179e6b1052194e200' + ) + }) + + describe('m is 2', () => { + const addresses = [ + 'ckt1qyq89x5ggpt0a5epm2k2gyxeffwkgfdxeg0s543mh4', + 'ckt1qyqql0vgjyxjxjxknkj6nq8jxa485xsyl66sy7c5f6', + 'ckt1qyqt9wqszk2lurw7h86wrt826cg8zx2f0lnq6e4vpl', + ] + const [multiArgs, multisigConfig] = createMultisigConfig(1, 2, addresses) + const addr = { + walletId: fakeWallet.id, + path: `m/44'/309'/0'/0/0`, + blake160: '', + version: 'testnet', + } + + const mockGAI = jest.fn() + mockGAI.mockReturnValue( + [addr, addr, addr].map((v, idx) => ({ ...v, blake160: addressToScript(addresses[idx]).args })) + ) + let tx = Transaction.fromObject(transactionObject) + it('first sign', async () => { + const getAddressInfos = transactionSender.getAddressInfos + transactionSender.getAddressInfos = mockGAI.bind(transactionSender) + tx.inputs[0]!.setLock( + SystemScriptInfo.generateMultiSignScript(multiArgs, SystemScriptInfo.MULTI_SIGN_CODE_HASH) + ) + tx = await transactionSender.signMultisig(fakeWallet.id, tx, '1234', [multisigConfig]) + const lock = (tx.witnesses[0] as WitnessArgs).lock! + const serializedMultiSign: string = Multisig.serialize( + addresses.map(v => addressToScript(v).args), + 1, + 2, + 3 + ) + expect(lock.startsWith(serializedMultiSign)).toBeTruthy() + transactionSender.getAddressInfos = getAddressInfos + }) + it('second sign', async () => { + const getAddressInfos = transactionSender.getAddressInfos + transactionSender.getAddressInfos = mockGAI.bind(transactionSender) + const res = await transactionSender.signMultisig(fakeWallet.id, tx, '1234', [multisigConfig]) + expect(res.witnesses[0]).toBe( + '0xd600000010000000d6000000d6000000c200000000010203729a884056fed321daaca410d94a5d6425a6ca1f0fbd88910d2348d69da5a980f2376a7a1a04feb5b2b8101595fe0ddeb9f4e1acead6107119497fe622bca1e7a693dec7beaf28fdde42c60adc1ba0c4e4279bd1b252d6dbf297eb222775697d59f6f14be56411ebe8b1d38e2646410e7d6a77a417f3b54e67fb86e90122bca1e7a693dec7beaf28fdde42c60adc1ba0c4e4279bd1b252d6dbf297eb222775697d59f6f14be56411ebe8b1d38e2646410e7d6a77a417f3b54e67fb86e901' + ) + transactionSender.getAddressInfos = getAddressInfos + }) + }) + + it('no matched multisig config, ignore and continue', async () => { + const showMessageBoxMock = jest + .spyOn(dialog, 'showMessageBox') + .mockImplementation(() => Promise.resolve({ response: 1, checkboxChecked: true })) + mockGAI.mockReturnValueOnce([{ path: '' }]) + transactionSender.getAddressInfos = mockGAI.bind(transactionSender) + const tx = Transaction.fromObject(transactionObject) + await expect(transactionSender.signMultisig(fakeWallet.id, tx, '1234', [])).resolves.not.toThrow() + expect(showMessageBoxMock).toHaveBeenCalled() + }) + + it('no matched multisig config, throw exception', async () => { + const showMessageBoxMock = jest + .spyOn(dialog, 'showMessageBox') + .mockImplementation(() => Promise.resolve({ response: 0, checkboxChecked: false })) + mockGAI.mockReturnValueOnce([{ path: '' }]) + transactionSender.getAddressInfos = mockGAI.bind(transactionSender) + const tx = Transaction.fromObject(transactionObject) + await expect(transactionSender.signMultisig(fakeWallet.id, tx, '1234', [])).rejects.toThrowError( + new MultisigConfigNeedError() + ) + expect(showMessageBoxMock).toHaveBeenCalled() + }) + + it('throw exception no matched multisig config addresses', async () => { + const addresses = [ + 'ckt1qyq89x5ggpt0a5epm2k2gyxeffwkgfdxeg0s543mh4', + 'ckt1qyqql0vgjyxjxjxknkj6nq8jxa485xsyl66sy7c5f6', + ] + const noMatchAddress = 'ckt1qyqf5v66n4vrxu75kks2ku06g7trnkdwt52s8000ee' + const [multiArgs, multisigConfig] = createMultisigConfig(1, 1, addresses) + const addr = { + walletId: fakeWallet.id, + address: noMatchAddress, + blake160: addressToScript(noMatchAddress).args, + version: 'testnet', + } + + const mockGAI = jest.fn() + mockGAI.mockReturnValueOnce([addr]) + transactionSender.getAddressInfos = mockGAI.bind(transactionSender) + + const tx = Transaction.fromObject(transactionObject) + tx.inputs[0]!.setLock( + SystemScriptInfo.generateMultiSignScript(multiArgs, SystemScriptInfo.MULTI_SIGN_CODE_HASH) + ) + await expect(transactionSender.signMultisig(fakeWallet.id, tx, '1234', [multisigConfig])).rejects.toThrow( + new NoMatchAddressForSign() + ) + }) + + describe('sign with hard wallet', () => { + beforeEach(() => { + stubbedGetWallet.mockReturnValue({ + ...fakeWallet, + isHardware() { + return true + }, + }) + }) + + it('m is 1', async () => { + const witnessLock = '0'.repeat(130) + stubbedHardWalletGetCurrent.mockReturnValueOnce({ + signTransaction: jest.fn().mockResolvedValueOnce(witnessLock), + }) + const addresses = [ + 'ckt1qyq89x5ggpt0a5epm2k2gyxeffwkgfdxeg0s543mh4', + 'ckt1qyqql0vgjyxjxjxknkj6nq8jxa485xsyl66sy7c5f6', + ] + const [multiArgs, multisigConfig] = createMultisigConfig(1, 1, addresses) + const addr = { + walletId: fakeWallet.id, + path: `m/44'/309'/0'/0/0`, + blake160: addressToScript(addresses[0]).args, + version: 'testnet', + } + + const mockGAI = jest.fn() + mockGAI.mockReturnValueOnce([addr]) + transactionSender.getAddressInfos = mockGAI.bind(transactionSender) + const tx = Transaction.fromObject(transactionObject) + tx.inputs[0]!.setLock( + SystemScriptInfo.generateMultiSignScript(multiArgs, SystemScriptInfo.MULTI_SIGN_CODE_HASH) + ) + const res = await transactionSender.signMultisig(fakeWallet.id, tx, '1234', [multisigConfig]) + const expectedValue = serializeWitnessArgs({ + inputType: undefined, + outputType: undefined, + lock: + Multisig.serialize( + addresses.map(v => addressToScript(v).args), + 1, + 1, + 2 + ) + witnessLock, + }) + expect(res.witnesses[0]).toBe(expectedValue) + }) + }) + + it(`input cell's length is 2`, async () => { + const addresses = [ + 'ckt1qyq89x5ggpt0a5epm2k2gyxeffwkgfdxeg0s543mh4', + 'ckt1qyqql0vgjyxjxjxknkj6nq8jxa485xsyl66sy7c5f6', + ] + const [multiArgs, multisigConfig] = createMultisigConfig(1, 1, addresses) + const addr = { + walletId: fakeWallet.id, + path: `m/44'/309'/0'/0/0`, + blake160: addressToScript(addresses[0]).args, + version: 'testnet', + } + + const mockGAI = jest.fn() + mockGAI.mockReturnValueOnce([addr]) + transactionSender.getAddressInfos = mockGAI.bind(transactionSender) + const tx = Transaction.fromObject(transactionObject) + tx.inputs[0]!.setLock( + SystemScriptInfo.generateMultiSignScript(multiArgs, SystemScriptInfo.MULTI_SIGN_CODE_HASH) + ) + tx.inputs.push( + Input.fromObject({ + previousOutput: OutPoint.fromObject({ + txHash: '0x1879851943fa686af29bed5c95acd566d0244e7b3ca89cf7c435622a5a5b4cb3', + index: '0x0', + }), + since: '0x0', + lock: Script.fromObject({ + args: multiArgs, + codeHash: SystemScriptInfo.MULTI_SIGN_CODE_HASH, + hashType: SystemScriptInfo.MULTI_SIGN_HASH_TYPE, + }), + }) + ) + tx.witnesses = ['0x', '0x'] + const res = await transactionSender.signMultisig(fakeWallet.id, tx, '1234', [multisigConfig]) + expect(res.witnesses).toHaveLength(2) + expect(res.witnesses[1]).toBe('0x') + expect(res.witnesses[0]).toBe( + '0x810000001000000081000000810000006d00000000010102729a884056fed321daaca410d94a5d6425a6ca1f0fbd88910d2348d69da5a980f2376a7a1a04feb5ffd5e0f69af2c2f1e2b1f687f9be8e758ae93ba8a8f5178cfaccb947865192f03899004fe92c9f27446bbf4dc86ba5088c55a2df969e1b1c9f9ab43c00c6443c01' + ) + }) + }) }) }) From faabae21d158b27d137aace8cc026548b030f376 Mon Sep 17 00:00:00 2001 From: chenyan Date: Thu, 24 Apr 2025 22:22:16 +0800 Subject: [PATCH 11/22] fix: comments --- .../src/components/MultisigAddress/hooks.ts | 11 ++--- .../src/components/MultisigAddress/index.tsx | 4 +- packages/neuron-ui/src/locales/zh-tw.json | 2 +- packages/neuron-ui/src/locales/zh.json | 2 +- .../src/widgets/Dialog/dialog.module.scss | 9 +++++ .../neuron-wallet/src/controllers/multisig.ts | 2 +- .../src/database/address/meta.ts | 2 +- .../1744960856059-AddLockCodeHash.ts | 2 +- .../src/models/system-script-info.ts | 16 ++++---- packages/neuron-wallet/src/services/cells.ts | 8 ++-- .../src/services/tx/transaction-generator.ts | 4 +- packages/neuron-wallet/src/utils/multisig.ts | 2 +- .../tests/controllers/multisig.test.ts | 2 +- .../tests/models/system-script-info.test.ts | 12 +++--- .../tests/services/cells.test.ts | 32 +++++++-------- .../tests/services/multisig.test.ts | 10 ++--- .../services/tx/transaction-generator.test.ts | 8 ++-- .../services/tx/transaction-persistor.test.ts | 2 +- .../services/tx/transaction-sender.test.ts | 40 ++++++++----------- .../tests/utils/multisig.test.ts | 2 +- 20 files changed, 85 insertions(+), 87 deletions(-) diff --git a/packages/neuron-ui/src/components/MultisigAddress/hooks.ts b/packages/neuron-ui/src/components/MultisigAddress/hooks.ts index d49302be61..967fe64370 100644 --- a/packages/neuron-ui/src/components/MultisigAddress/hooks.ts +++ b/packages/neuron-ui/src/components/MultisigAddress/hooks.ts @@ -312,7 +312,6 @@ const useDeleteAction = (deleteConfigById: (id: number) => void) => { } const useRegenerateAction = (regenerateConfig: (config: MultisigConfig) => Promise) => { - const [isDialogOpen, setIsDialogOpen] = useState(false) const [config, setConfig] = useState() const [isNoRemind, setIsNoRemind] = useState(false) const [regenerateErrorMessage, setRegenerateErrorMessage] = useState() @@ -331,24 +330,20 @@ const useRegenerateAction = (regenerateConfig: (config: MultisigConfig) => Promi try { await regenerateConfig(multisigConfig) } catch (error) { - setIsDialogOpen(true) setRegenerateErrorMessage(error instanceof Error ? error.message : '') } }, - [setIsDialogOpen, config, isNoRemind] + [config, isNoRemind, setRegenerateErrorMessage] ) - const closeDialog = useCallback(() => { - setIsDialogOpen(false) - }, [setIsDialogOpen]) + return { action: regenerate, - closeDialog, config, setConfig, - isDialogOpen, isNoRemind, handleCheckbox, regenerateErrorMessage, + setRegenerateErrorMessage, } } diff --git a/packages/neuron-ui/src/components/MultisigAddress/index.tsx b/packages/neuron-ui/src/components/MultisigAddress/index.tsx index 405e794b3c..f14f46a47a 100644 --- a/packages/neuron-ui/src/components/MultisigAddress/index.tsx +++ b/packages/neuron-ui/src/components/MultisigAddress/index.tsx @@ -656,10 +656,10 @@ const MultisigAddress = () => { /> regenerateAction.setRegenerateErrorMessage('')} /> 多簽腳本升級,我們建議重新生成多簽地址,並將資產從舊地址轉移,以確保正常功能。", "regenerate-dialog": { "title": "重新生成", - "detail": "將使用您舊多簽地址的設定生成新地址,並升級至最新腳本格式。", + "detail": "將使用您舊多簽地址的設置生成一個新地址,升級為新多簽合約", "donnot-worry": "別擔心 — 舊地址仍然可以使用。", "donnot-remind-again": "不再提醒" }, diff --git a/packages/neuron-ui/src/locales/zh.json b/packages/neuron-ui/src/locales/zh.json index d9984bd875..04f08bcd2d 100644 --- a/packages/neuron-ui/src/locales/zh.json +++ b/packages/neuron-ui/src/locales/zh.json @@ -1189,7 +1189,7 @@ "multisig-script-update-notice": "由于<0>多签脚本升级,我们建议重新生成多签地址,并将资产从旧地址转移,以确保正常使用。", "regenerate-dialog": { "title": "重新生成", - "detail": "将使用您旧多签地址的设置生成一个新地址,升级为最新脚本格式", + "detail": "将使用您旧多签地址的设置生成一个新地址, 升级为新多签合约", "donnot-worry": "别担心 — 旧地址仍然可以访问", "donnot-remind-again": "不再提醒" }, diff --git a/packages/neuron-ui/src/widgets/Dialog/dialog.module.scss b/packages/neuron-ui/src/widgets/Dialog/dialog.module.scss index 75d3640c14..1e4b156c03 100644 --- a/packages/neuron-ui/src/widgets/Dialog/dialog.module.scss +++ b/packages/neuron-ui/src/widgets/Dialog/dialog.module.scss @@ -5,11 +5,13 @@ padding: 0; position: relative; + /* 對話框背景遮罩 */ &::backdrop { @include overlay; } } +/* 對話框頭部 */ .header { position: -webkit-sticky; position: sticky; @@ -45,13 +47,20 @@ } } +/* 對話框內容區域 */ .content { padding: 20px 16px; position: relative; max-height: calc(100vh - 260px); overflow-y: auto; + + /* 隱藏滾動條但保持滾動功能 */ + &::-webkit-scrollbar { + width: 0; + } } +/* 對話框底部區域 */ .footerWrap { height: 104px; } diff --git a/packages/neuron-wallet/src/controllers/multisig.ts b/packages/neuron-wallet/src/controllers/multisig.ts index c4d4b1d173..375f4df088 100644 --- a/packages/neuron-wallet/src/controllers/multisig.ts +++ b/packages/neuron-wallet/src/controllers/multisig.ts @@ -79,7 +79,7 @@ export default class MultisigController { params.m, params.n, params.blake160s, - params.lockCodeHash || SystemScriptInfo.MULTI_SIGN_CODE_HASH, + params.lockCodeHash || SystemScriptInfo.MULTISIG_CODE_HASH, params.alias ) ) diff --git a/packages/neuron-wallet/src/database/address/meta.ts b/packages/neuron-wallet/src/database/address/meta.ts index 60d8e38838..2a2cb6b9fe 100644 --- a/packages/neuron-wallet/src/database/address/meta.ts +++ b/packages/neuron-wallet/src/database/address/meta.ts @@ -103,7 +103,7 @@ export default class AddressMeta implements Address { public generateSingleMultiSignLockScript(): Script { return SystemScriptInfo.generateMultiSignScript( Multisig.hash([this.blake160]), - SystemScriptInfo.LEGACY_MULTI_SIGN_CODE_HASH + SystemScriptInfo.LEGACY_MULTISIG_CODE_HASH ) } diff --git a/packages/neuron-wallet/src/database/chain/migrations/1744960856059-AddLockCodeHash.ts b/packages/neuron-wallet/src/database/chain/migrations/1744960856059-AddLockCodeHash.ts index edd49280a2..fcba36ba31 100644 --- a/packages/neuron-wallet/src/database/chain/migrations/1744960856059-AddLockCodeHash.ts +++ b/packages/neuron-wallet/src/database/chain/migrations/1744960856059-AddLockCodeHash.ts @@ -6,7 +6,7 @@ export class AddLockCodeHash1744960856059 implements MigrationInterface { public async up(queryRunner: QueryRunner): Promise { await queryRunner.query(`ALTER TABLE "multisig_config" ADD COLUMN "lockCodeHash" character(32);`) - await queryRunner.query(`UPDATE "multisig_config" SET lockCodeHash = "${SystemScriptInfo.LEGACY_MULTI_SIGN_CODE_HASH}" where lockCodeHash IS NULL`) + await queryRunner.query(`UPDATE "multisig_config" SET lockCodeHash = "${SystemScriptInfo.LEGACY_MULTISIG_CODE_HASH}" where lockCodeHash IS NULL`) } public async down(queryRunner: QueryRunner): Promise { diff --git a/packages/neuron-wallet/src/models/system-script-info.ts b/packages/neuron-wallet/src/models/system-script-info.ts index 6a68430e3c..9b4a9dd75d 100644 --- a/packages/neuron-wallet/src/models/system-script-info.ts +++ b/packages/neuron-wallet/src/models/system-script-info.ts @@ -10,8 +10,8 @@ import { MAINNET_CLIENT_LIST } from '../utils/const' export default class SystemScriptInfo { static SECP_CODE_HASH = process.env.SECP256K1_CODE_HASH! static DAO_CODE_HASH = process.env.DAO_CODE_HASH! - static LEGACY_MULTI_SIGN_CODE_HASH = process.env.LEGACY_MULTISIG_CODE_HASH! - static MULTI_SIGN_CODE_HASH = process.env.MULTISIG_CODE_HASH! + static LEGACY_MULTISIG_CODE_HASH = process.env.LEGACY_MULTISIG_CODE_HASH! + static MULTISIG_CODE_HASH = process.env.MULTISIG_CODE_HASH! static SECP_HASH_TYPE = ScriptHashType.Type static DAO_HASH_TYPE = ScriptHashType.Type @@ -42,7 +42,7 @@ export default class SystemScriptInfo { private multiSignOutPointInfo = new Map() public static getMultiSignHashType(lockCodeHash: string): ScriptHashType { - if (lockCodeHash === SystemScriptInfo.MULTI_SIGN_CODE_HASH) { + if (lockCodeHash === SystemScriptInfo.MULTISIG_CODE_HASH) { return SystemScriptInfo.MULTI_SIGN_HASH_TYPE } return SystemScriptInfo.LEGACY_MULTI_SIGN_HASH_TYPE @@ -74,7 +74,7 @@ export default class SystemScriptInfo { } public async getMultiSignCellDep( - lockCodeHash: string = SystemScriptInfo.LEGACY_MULTI_SIGN_CODE_HASH, + lockCodeHash: string = SystemScriptInfo.LEGACY_MULTISIG_CODE_HASH, network: { remote: string genesisHash: string @@ -84,7 +84,7 @@ export default class SystemScriptInfo { ): Promise { const genesisBlockHash = network.genesisHash const multiSignOutPointInfo = - lockCodeHash === SystemScriptInfo.MULTI_SIGN_CODE_HASH + lockCodeHash === SystemScriptInfo.MULTISIG_CODE_HASH ? this.multiSignOutPointInfo : this.legacyMultiSignOutPointInfo @@ -113,14 +113,14 @@ export default class SystemScriptInfo { } public static isMultiSignCodeHash(codeHash: string): boolean { - return [SystemScriptInfo.LEGACY_MULTI_SIGN_CODE_HASH, SystemScriptInfo.MULTI_SIGN_CODE_HASH].includes(codeHash) + return [SystemScriptInfo.LEGACY_MULTISIG_CODE_HASH, SystemScriptInfo.MULTISIG_CODE_HASH].includes(codeHash) } public static isMultiSignScript(script: Script): boolean { return ( - (script.codeHash === SystemScriptInfo.LEGACY_MULTI_SIGN_CODE_HASH && + (script.codeHash === SystemScriptInfo.LEGACY_MULTISIG_CODE_HASH && script.hashType === SystemScriptInfo.LEGACY_MULTI_SIGN_HASH_TYPE) || - (script.codeHash === SystemScriptInfo.MULTI_SIGN_CODE_HASH && + (script.codeHash === SystemScriptInfo.MULTISIG_CODE_HASH && script.hashType === SystemScriptInfo.MULTI_SIGN_HASH_TYPE) ) } diff --git a/packages/neuron-wallet/src/services/cells.ts b/packages/neuron-wallet/src/services/cells.ts index a07420f748..67c7eebe0b 100644 --- a/packages/neuron-wallet/src/services/cells.ts +++ b/packages/neuron-wallet/src/services/cells.ts @@ -353,7 +353,7 @@ export default class CellsService { `, { liveStatus: OutputStatus.Live, - multiSignlockCodeHash: SystemScriptInfo.LEGACY_MULTI_SIGN_CODE_HASH, + multiSignlockCodeHash: SystemScriptInfo.LEGACY_MULTISIG_CODE_HASH, chequeLockCodeHash, nftIssuerCodehash, nftClassCodehash, @@ -467,7 +467,7 @@ export default class CellsService { data: 'withdraw-able', }) } - } else if (o.lockCodeHash === SystemScriptInfo.LEGACY_MULTI_SIGN_CODE_HASH) { + } else if (o.lockCodeHash === SystemScriptInfo.LEGACY_MULTISIG_CODE_HASH) { cell.setCustomizedAssetInfo({ lock: CustomizedLock.SingleMultiSign, type: '', @@ -770,7 +770,7 @@ export default class CellsService { if (inputs.find(el => el.lockHash === cell.lockHash!)) { totalSize += TransactionSize.emptyWitness() } else { - if (lockClass.codeHash === SystemScriptInfo.LEGACY_MULTI_SIGN_CODE_HASH) { + if (lockClass.codeHash === SystemScriptInfo.LEGACY_MULTISIG_CODE_HASH) { const multisigConfig = multisigConfigMap[cell.lockHash] if (!multisigConfig) { throw new MultisigConfigNeedError() @@ -1372,7 +1372,7 @@ export default class CellsService { switch (output.lock.codeHash) { case assetAccountInfo.getChequeInfo().codeHash: return LockScriptCategory.Cheque - case SystemScriptInfo.LEGACY_MULTI_SIGN_CODE_HASH: + case SystemScriptInfo.LEGACY_MULTISIG_CODE_HASH: if (output.lock.args.length === LOCKTIME_ARGS_LENGTH) { return LockScriptCategory.MULTI_LOCK_TIME } diff --git a/packages/neuron-wallet/src/services/tx/transaction-generator.ts b/packages/neuron-wallet/src/services/tx/transaction-generator.ts index 3e321e1e3b..b99c02663a 100644 --- a/packages/neuron-wallet/src/services/tx/transaction-generator.ts +++ b/packages/neuron-wallet/src/services/tx/transaction-generator.ts @@ -214,7 +214,7 @@ export class TransactionGenerator { const minutes: number = +((BigInt(date) - BigInt(tipHeaderTimestamp)) / BigInt(1000 * 60)).toString() const script = SystemScriptInfo.generateMultiSignScript( Multisig.args(blake160, +minutes, tipHeaderEpoch), - SystemScriptInfo.LEGACY_MULTI_SIGN_CODE_HASH + SystemScriptInfo.LEGACY_MULTISIG_CODE_HASH ) output.setLock(script) output.setMultiSignBlake160(script.args.slice(0, 42)) @@ -343,7 +343,7 @@ export class TransactionGenerator { const minutes: number = +((BigInt(date) - BigInt(tipHeaderTimestamp)) / BigInt(1000 * 60)).toString() const script: Script = SystemScriptInfo.generateMultiSignScript( Multisig.args(blake160, minutes, tipHeaderEpoch), - SystemScriptInfo.LEGACY_MULTI_SIGN_CODE_HASH + SystemScriptInfo.LEGACY_MULTISIG_CODE_HASH ) output.setLock(script) output.setMultiSignBlake160(script.args.slice(0, 42)) diff --git a/packages/neuron-wallet/src/utils/multisig.ts b/packages/neuron-wallet/src/utils/multisig.ts index e06eb254c2..50668213b4 100644 --- a/packages/neuron-wallet/src/utils/multisig.ts +++ b/packages/neuron-wallet/src/utils/multisig.ts @@ -43,7 +43,7 @@ export const parseMultisigTxJsonFromCkbCli = (tx: OfflineSignJSON): Transaction const txObj = Transaction.fromObject(deepCamelizeKeys(transaction) as any) if (multisig_configs && Object.keys(multisig_configs).length) { const args = Object.keys(multisig_configs)[0] - const lock = SystemScriptInfo.generateMultiSignScript(args, SystemScriptInfo.LEGACY_MULTI_SIGN_CODE_HASH) + const lock = SystemScriptInfo.generateMultiSignScript(args, SystemScriptInfo.LEGACY_MULTISIG_CODE_HASH) txObj.inputs.forEach((input: Input) => { if (!input?.lock) { diff --git a/packages/neuron-wallet/tests/controllers/multisig.test.ts b/packages/neuron-wallet/tests/controllers/multisig.test.ts index 369f54316e..c7134c47c0 100644 --- a/packages/neuron-wallet/tests/controllers/multisig.test.ts +++ b/packages/neuron-wallet/tests/controllers/multisig.test.ts @@ -140,7 +140,7 @@ describe('test for multisig controller', () => { m: 1, n: 1, blake160s: [], - lockCodeHash: SystemScriptInfo.MULTI_SIGN_CODE_HASH, + lockCodeHash: SystemScriptInfo.MULTISIG_CODE_HASH, alias: 'string', changed: expect.any(Function), lastestBlockNumber: '', diff --git a/packages/neuron-wallet/tests/models/system-script-info.test.ts b/packages/neuron-wallet/tests/models/system-script-info.test.ts index 346212b09f..255bac40c0 100644 --- a/packages/neuron-wallet/tests/models/system-script-info.test.ts +++ b/packages/neuron-wallet/tests/models/system-script-info.test.ts @@ -4,15 +4,15 @@ import SystemScriptInfo from '../../src/models/system-script-info' describe('SystemScriptInfo', () => { const SECP_CODE_HASH = '0x9bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce8' const DAO_CODE_HASH = '0x82d76d1b75fe2fd9a27dfbaa65a039221a380d76c926f378d3f81cf3e7e13f2e' - const LEGACY_MULTI_SIGN_CODE_HASH = '0x5c5069eb0857efc65e1bca0c07df34c31663b3622fd3876c876320fc9634e2a8' - const MULTI_SIGN_CODE_HASH = '0x36c971b8d41fbd94aabca77dc75e826729ac98447b46f91e00796155dddb0d29' + const LEGACY_MULTISIG_CODE_HASH = '0x5c5069eb0857efc65e1bca0c07df34c31663b3622fd3876c876320fc9634e2a8' + const MULTISIG_CODE_HASH = '0x36c971b8d41fbd94aabca77dc75e826729ac98447b46f91e00796155dddb0d29' const secpScript = SystemScriptInfo.generateSecpScript('0x' + '0'.repeat(40)) const legacyMultiSignScript = SystemScriptInfo.generateMultiSignScript( '0x' + '0'.repeat(40), - LEGACY_MULTI_SIGN_CODE_HASH + LEGACY_MULTISIG_CODE_HASH ) - const multiSignScript = SystemScriptInfo.generateMultiSignScript('0x' + '0'.repeat(40), MULTI_SIGN_CODE_HASH) + const multiSignScript = SystemScriptInfo.generateMultiSignScript('0x' + '0'.repeat(40), MULTISIG_CODE_HASH) const daoScript = SystemScriptInfo.generateDaoScript('0x') it('secp code hash', () => { @@ -24,11 +24,11 @@ describe('SystemScriptInfo', () => { }) it('legacy multi sign code hash', () => { - expect(SystemScriptInfo.LEGACY_MULTI_SIGN_CODE_HASH).toEqual(LEGACY_MULTI_SIGN_CODE_HASH) + expect(SystemScriptInfo.LEGACY_MULTISIG_CODE_HASH).toEqual(LEGACY_MULTISIG_CODE_HASH) }) it('multi sign code hash', () => { - expect(SystemScriptInfo.MULTI_SIGN_CODE_HASH).toEqual(MULTI_SIGN_CODE_HASH) + expect(SystemScriptInfo.MULTISIG_CODE_HASH).toEqual(MULTISIG_CODE_HASH) }) it('isSecpScript', () => { diff --git a/packages/neuron-wallet/tests/services/cells.test.ts b/packages/neuron-wallet/tests/services/cells.test.ts index 4a813001d3..b21f6fe5a1 100644 --- a/packages/neuron-wallet/tests/services/cells.test.ts +++ b/packages/neuron-wallet/tests/services/cells.test.ts @@ -79,7 +79,7 @@ describe('CellsService', () => { const multisigPublicKeyHash = '0x447ff8941a6f0162d2194e9b592bb2534a3e6b74' const multisigLockScript = SystemScriptInfo.generateMultiSignScript( multisigPublicKeyHash, - SystemScriptInfo.LEGACY_MULTI_SIGN_CODE_HASH + SystemScriptInfo.LEGACY_MULTISIG_CODE_HASH ) const multisigInfo = { lockScript: multisigLockScript, @@ -141,7 +141,7 @@ describe('CellsService', () => { output.lockCodeHash = who.lockScript.codeHash output.lockArgs = who.lockScript.args output.lockHashType = who.lockScript.hashType - if (who.lockScript.codeHash === SystemScriptInfo.LEGACY_MULTI_SIGN_CODE_HASH) { + if (who.lockScript.codeHash === SystemScriptInfo.LEGACY_MULTISIG_CODE_HASH) { output.multiSignBlake160 = who.lockScript.args } output.lockHash = who.lockScript.computeHash() @@ -559,7 +559,7 @@ describe('CellsService', () => { CellsService.gatherInputs(toShannon('1001'), '', '0', '1000', 0, 0, 0, undefined, { lockArgs: ['bob.blake160'], hashType: SystemScriptInfo.LEGACY_MULTI_SIGN_HASH_TYPE, - codeHash: SystemScriptInfo.LEGACY_MULTI_SIGN_CODE_HASH, + codeHash: SystemScriptInfo.LEGACY_MULTISIG_CODE_HASH, }) ).rejects.toThrow(new CapacityNotEnough()) }) @@ -569,7 +569,7 @@ describe('CellsService', () => { CellsService.gatherInputs(toShannon('1001'), '', '0', '1000', 0, 0, 0, undefined, { lockArgs: [multisigInfo.lockScript.args], hashType: SystemScriptInfo.LEGACY_MULTI_SIGN_HASH_TYPE, - codeHash: SystemScriptInfo.LEGACY_MULTI_SIGN_CODE_HASH, + codeHash: SystemScriptInfo.LEGACY_MULTISIG_CODE_HASH, }) ).rejects.toThrow(new MultisigConfigNeedError()) }) @@ -588,7 +588,7 @@ describe('CellsService', () => { { lockArgs: [multisigInfo.lockScript.args], hashType: SystemScriptInfo.LEGACY_MULTI_SIGN_HASH_TYPE, - codeHash: SystemScriptInfo.LEGACY_MULTI_SIGN_CODE_HASH, + codeHash: SystemScriptInfo.LEGACY_MULTISIG_CODE_HASH, }, [ { @@ -618,7 +618,7 @@ describe('CellsService', () => { { lockArgs: [multisigInfo.lockScript.args], hashType: SystemScriptInfo.LEGACY_MULTI_SIGN_HASH_TYPE, - codeHash: SystemScriptInfo.LEGACY_MULTI_SIGN_CODE_HASH, + codeHash: SystemScriptInfo.LEGACY_MULTISIG_CODE_HASH, }, [ { @@ -648,7 +648,7 @@ describe('CellsService', () => { { lockArgs: [multisigInfo.lockScript.args], hashType: SystemScriptInfo.LEGACY_MULTI_SIGN_HASH_TYPE, - codeHash: SystemScriptInfo.LEGACY_MULTI_SIGN_CODE_HASH, + codeHash: SystemScriptInfo.LEGACY_MULTISIG_CODE_HASH, }, [ { @@ -677,7 +677,7 @@ describe('CellsService', () => { { lockArgs: [multisigInfo.lockScript.args], hashType: SystemScriptInfo.LEGACY_MULTI_SIGN_HASH_TYPE, - codeHash: SystemScriptInfo.LEGACY_MULTI_SIGN_CODE_HASH, + codeHash: SystemScriptInfo.LEGACY_MULTISIG_CODE_HASH, }, [ { @@ -707,7 +707,7 @@ describe('CellsService', () => { { lockArgs: [multisigInfo.lockScript.args], hashType: SystemScriptInfo.LEGACY_MULTI_SIGN_HASH_TYPE, - codeHash: SystemScriptInfo.LEGACY_MULTI_SIGN_CODE_HASH, + codeHash: SystemScriptInfo.LEGACY_MULTISIG_CODE_HASH, }, [ { @@ -783,7 +783,7 @@ describe('CellsService', () => { }) it('gather with exist args', async () => { const inputs = await CellsService.gatherAllInputs(walletId1, { - codeHash: SystemScriptInfo.LEGACY_MULTI_SIGN_CODE_HASH, + codeHash: SystemScriptInfo.LEGACY_MULTISIG_CODE_HASH, hashType: SystemScriptInfo.LEGACY_MULTI_SIGN_HASH_TYPE, args: multisigInfo.lockScript.args, }) @@ -792,7 +792,7 @@ describe('CellsService', () => { }) it('gather with non-exist args', async () => { const inputs = await CellsService.gatherAllInputs(walletId1, { - codeHash: SystemScriptInfo.LEGACY_MULTI_SIGN_CODE_HASH, + codeHash: SystemScriptInfo.LEGACY_MULTISIG_CODE_HASH, hashType: SystemScriptInfo.LEGACY_MULTI_SIGN_HASH_TYPE, args: 'non-exist-args', }) @@ -1236,7 +1236,7 @@ describe('CellsService', () => { const multiSignHash = Multisig.hash([publicKeyHash]) const multiSignLockScript = SystemScriptInfo.generateMultiSignScript( multiSignHash, - SystemScriptInfo.LEGACY_MULTI_SIGN_CODE_HASH + SystemScriptInfo.LEGACY_MULTISIG_CODE_HASH ) const pageSize = 2 @@ -1697,7 +1697,7 @@ describe('CellsService', () => { false, SystemScriptInfo.generateMultiSignScript( Multisig.args(bob.blake160, +10, '0x7080291000049'), - SystemScriptInfo.LEGACY_MULTI_SIGN_CODE_HASH + SystemScriptInfo.LEGACY_MULTISIG_CODE_HASH ) ) await expect(CellsService.getLiveOrSentCellByWalletId(bob.walletId)).resolves.toHaveLength(1) @@ -1742,7 +1742,7 @@ describe('CellsService', () => { capacity: '1000', lock: SystemScriptInfo.generateMultiSignScript( Multisig.args(bob.blake160, +10, '0x7080291000049'), - SystemScriptInfo.LEGACY_MULTI_SIGN_CODE_HASH + SystemScriptInfo.LEGACY_MULTISIG_CODE_HASH ), }) expect(CellsService.getCellLockType(output)).toBe(LockScriptCategory.MULTI_LOCK_TIME) @@ -1750,7 +1750,7 @@ describe('CellsService', () => { it('MULTI_LOCK', () => { const output = Output.fromObject({ capacity: '1000', - lock: Multisig.getMultisigScript([bob.blake160], 1, 1, 1, SystemScriptInfo.LEGACY_MULTI_SIGN_CODE_HASH), + lock: Multisig.getMultisigScript([bob.blake160], 1, 1, 1, SystemScriptInfo.LEGACY_MULTISIG_CODE_HASH), }) expect(CellsService.getCellLockType(output)).toBe(LockScriptCategory.MULTISIG) }) @@ -1875,7 +1875,7 @@ describe('CellsService', () => { input.lockCodeHash = who.lockScript.codeHash input.lockArgs = who.lockScript.args input.lockHashType = who.lockScript.hashType - if (who.lockScript.codeHash === SystemScriptInfo.LEGACY_MULTI_SIGN_CODE_HASH) { + if (who.lockScript.codeHash === SystemScriptInfo.LEGACY_MULTISIG_CODE_HASH) { input.multiSignBlake160 = who.lockScript.args } input.lockHash = who.lockScript.computeHash() diff --git a/packages/neuron-wallet/tests/services/multisig.test.ts b/packages/neuron-wallet/tests/services/multisig.test.ts index 569180bf63..bcfd45a3cc 100644 --- a/packages/neuron-wallet/tests/services/multisig.test.ts +++ b/packages/neuron-wallet/tests/services/multisig.test.ts @@ -52,7 +52,7 @@ describe('multisig service', () => { 2, 3, [alice.publicKeyInBlake160, bob.publicKeyInBlake160, charlie.publicKeyInBlake160], - SystemScriptInfo.MULTI_SIGN_CODE_HASH, + SystemScriptInfo.MULTISIG_CODE_HASH, 'alias' ) const defaultMultisigConfig = MultisigConfig.fromModel(multisigConfigModel) @@ -65,7 +65,7 @@ describe('multisig service', () => { multisigConfigModel.m, multisigConfigModel.n ), - codeHash: SystemScriptInfo.MULTI_SIGN_CODE_HASH, + codeHash: SystemScriptInfo.MULTISIG_CODE_HASH, hashType: SystemScriptInfo.MULTI_SIGN_HASH_TYPE, } const defaultTxOutpoint = { tx_hash: 'tx_hash', index: '0x0' } @@ -241,7 +241,7 @@ describe('multisig service', () => { 2, 3, [alice.publicKeyInBlake160, bob.publicKeyInBlake160, charlie.publicKeyInBlake160], - SystemScriptInfo.MULTI_SIGN_CODE_HASH + SystemScriptInfo.MULTISIG_CODE_HASH ) const multisigConfigs = [ MultisigConfig.fromModel(multisigConfigModel), @@ -260,7 +260,7 @@ describe('multisig service', () => { 2, 3, [alice.publicKeyInBlake160, bob.publicKeyInBlake160, charlie.publicKeyInBlake160], - SystemScriptInfo.MULTI_SIGN_CODE_HASH + SystemScriptInfo.MULTISIG_CODE_HASH ) ), MultisigConfig.fromModel( @@ -270,7 +270,7 @@ describe('multisig service', () => { 2, 3, [alice.publicKeyInBlake160, bob.publicKeyInBlake160, charlie.publicKeyInBlake160], - SystemScriptInfo.MULTI_SIGN_CODE_HASH + SystemScriptInfo.MULTISIG_CODE_HASH ) ), ] diff --git a/packages/neuron-wallet/tests/services/tx/transaction-generator.test.ts b/packages/neuron-wallet/tests/services/tx/transaction-generator.test.ts index a8a9c7d6a9..7fbc48383f 100644 --- a/packages/neuron-wallet/tests/services/tx/transaction-generator.test.ts +++ b/packages/neuron-wallet/tests/services/tx/transaction-generator.test.ts @@ -476,7 +476,7 @@ describe('TransactionGenerator', () => { expect(expectedFee).toEqual(BigInt(472)) expect(tx.fee).toEqual(expectedFee.toString()) - const multiSignOutput = tx.outputs.find(o => o.lock.codeHash === SystemScriptInfo.LEGACY_MULTI_SIGN_CODE_HASH) + const multiSignOutput = tx.outputs.find(o => o.lock.codeHash === SystemScriptInfo.LEGACY_MULTISIG_CODE_HASH) expect(multiSignOutput).toBeDefined() const epoch = Multisig.parseSince(multiSignOutput!.lock.args) @@ -738,7 +738,7 @@ describe('TransactionGenerator', () => { feeRate, }) - expect(tx.outputs[0].lock.codeHash).toEqual(SystemScriptInfo.LEGACY_MULTI_SIGN_CODE_HASH) + expect(tx.outputs[0].lock.codeHash).toEqual(SystemScriptInfo.LEGACY_MULTISIG_CODE_HASH) const epoch = Multisig.parseSince(tx.outputs[0].lock.args) const parsedEpoch = since.parseEpoch(epoch) @@ -772,7 +772,7 @@ describe('TransactionGenerator', () => { 'ckt1qyqdpymnu202x3p4cnrrgek5czcdsg95xznswjr98y', 'ckt1qyqwqcknusdreymrhhme00hg9af3pr5hcmwqzfxvda', ].map(v => addressToScript(v).args), - lockCodeHash: SystemScriptInfo.LEGACY_MULTI_SIGN_CODE_HASH, + lockCodeHash: SystemScriptInfo.LEGACY_MULTISIG_CODE_HASH, }), }) @@ -1053,7 +1053,7 @@ describe('TransactionGenerator', () => { capacity: toShannon('1000'), lock: SystemScriptInfo.generateMultiSignScript( Multisig.args(bob.lockScript.args, 100, '0x7080018000001'), - SystemScriptInfo.LEGACY_MULTI_SIGN_CODE_HASH + SystemScriptInfo.LEGACY_MULTISIG_CODE_HASH ), }) const outPoint = OutPoint.fromObject({ diff --git a/packages/neuron-wallet/tests/services/tx/transaction-persistor.test.ts b/packages/neuron-wallet/tests/services/tx/transaction-persistor.test.ts index 5c98418991..72e7980a4b 100644 --- a/packages/neuron-wallet/tests/services/tx/transaction-persistor.test.ts +++ b/packages/neuron-wallet/tests/services/tx/transaction-persistor.test.ts @@ -146,7 +146,7 @@ describe('TransactionPersistor', () => { const txWithCheque = Transaction.fromObject(tx) const multisigLockTimeLock = SystemScriptInfo.generateMultiSignScript( Multisig.hash([txWithCheque.outputs[0].lock.args]), - SystemScriptInfo.LEGACY_MULTI_SIGN_CODE_HASH + SystemScriptInfo.LEGACY_MULTISIG_CODE_HASH ) txWithCheque.outputs[0].setLock(multisigLockTimeLock) const args = [...tx.inputs.map(v => v.lock?.args), ...tx.outputs.map(v => v.lock.args)] diff --git a/packages/neuron-wallet/tests/services/tx/transaction-sender.test.ts b/packages/neuron-wallet/tests/services/tx/transaction-sender.test.ts index 9f11d9b376..5ae1d9fd84 100644 --- a/packages/neuron-wallet/tests/services/tx/transaction-sender.test.ts +++ b/packages/neuron-wallet/tests/services/tx/transaction-sender.test.ts @@ -692,7 +692,7 @@ describe('TransactionSender Test', () => { n: 1, r: 1, blake160s: ['blake160s'], - lockCodeHash: SystemScriptInfo.LEGACY_MULTI_SIGN_CODE_HASH, + lockCodeHash: SystemScriptInfo.LEGACY_MULTISIG_CODE_HASH, }) await transactionSender.generateMultisigSendAllTx(targetOutputs, multisigConfig) expect(stubbedGenerateSendingAllTx).toHaveBeenCalledWith({ @@ -895,7 +895,7 @@ describe('TransactionSender Test', () => { since: '0x0', lock: Script.fromObject({ args: '', - codeHash: SystemScriptInfo.LEGACY_MULTI_SIGN_CODE_HASH, + codeHash: SystemScriptInfo.LEGACY_MULTISIG_CODE_HASH, hashType: SystemScriptInfo.LEGACY_MULTI_SIGN_HASH_TYPE, }), }), @@ -936,7 +936,7 @@ describe('TransactionSender Test', () => { m, n: addresses.length, blake160s: addresses.map(v => addressToScript(v).args), - lockCodeHash: SystemScriptInfo.LEGACY_MULTI_SIGN_CODE_HASH, + lockCodeHash: SystemScriptInfo.LEGACY_MULTISIG_CODE_HASH, }), ] } @@ -959,7 +959,7 @@ describe('TransactionSender Test', () => { transactionSender.getAddressInfos = mockGAI.bind(transactionSender) const tx = Transaction.fromObject(transactionObject) tx.inputs[0]!.setLock( - SystemScriptInfo.generateMultiSignScript(multiArgs, SystemScriptInfo.LEGACY_MULTI_SIGN_CODE_HASH) + SystemScriptInfo.generateMultiSignScript(multiArgs, SystemScriptInfo.LEGACY_MULTISIG_CODE_HASH) ) const res = await transactionSender.signMultisig(fakeWallet.id, tx, '1234', [multisigConfig]) expect(res.witnesses[0]).toBe( @@ -990,7 +990,7 @@ describe('TransactionSender Test', () => { const getAddressInfos = transactionSender.getAddressInfos transactionSender.getAddressInfos = mockGAI.bind(transactionSender) tx.inputs[0]!.setLock( - SystemScriptInfo.generateMultiSignScript(multiArgs, SystemScriptInfo.LEGACY_MULTI_SIGN_CODE_HASH) + SystemScriptInfo.generateMultiSignScript(multiArgs, SystemScriptInfo.LEGACY_MULTISIG_CODE_HASH) ) tx = await transactionSender.signMultisig(fakeWallet.id, tx, '1234', [multisigConfig]) const lock = (tx.witnesses[0] as WitnessArgs).lock! @@ -1058,7 +1058,7 @@ describe('TransactionSender Test', () => { const tx = Transaction.fromObject(transactionObject) tx.inputs[0]!.setLock( - SystemScriptInfo.generateMultiSignScript(multiArgs, SystemScriptInfo.LEGACY_MULTI_SIGN_CODE_HASH) + SystemScriptInfo.generateMultiSignScript(multiArgs, SystemScriptInfo.LEGACY_MULTISIG_CODE_HASH) ) await expect(transactionSender.signMultisig(fakeWallet.id, tx, '1234', [multisigConfig])).rejects.toThrow( new NoMatchAddressForSign() @@ -1097,7 +1097,7 @@ describe('TransactionSender Test', () => { transactionSender.getAddressInfos = mockGAI.bind(transactionSender) const tx = Transaction.fromObject(transactionObject) tx.inputs[0]!.setLock( - SystemScriptInfo.generateMultiSignScript(multiArgs, SystemScriptInfo.LEGACY_MULTI_SIGN_CODE_HASH) + SystemScriptInfo.generateMultiSignScript(multiArgs, SystemScriptInfo.LEGACY_MULTISIG_CODE_HASH) ) const res = await transactionSender.signMultisig(fakeWallet.id, tx, '1234', [multisigConfig]) const expectedValue = serializeWitnessArgs({ @@ -1133,7 +1133,7 @@ describe('TransactionSender Test', () => { transactionSender.getAddressInfos = mockGAI.bind(transactionSender) const tx = Transaction.fromObject(transactionObject) tx.inputs[0]!.setLock( - SystemScriptInfo.generateMultiSignScript(multiArgs, SystemScriptInfo.LEGACY_MULTI_SIGN_CODE_HASH) + SystemScriptInfo.generateMultiSignScript(multiArgs, SystemScriptInfo.LEGACY_MULTISIG_CODE_HASH) ) tx.inputs.push( Input.fromObject({ @@ -1144,7 +1144,7 @@ describe('TransactionSender Test', () => { since: '0x0', lock: Script.fromObject({ args: multiArgs, - codeHash: SystemScriptInfo.LEGACY_MULTI_SIGN_CODE_HASH, + codeHash: SystemScriptInfo.LEGACY_MULTISIG_CODE_HASH, hashType: SystemScriptInfo.LEGACY_MULTI_SIGN_HASH_TYPE, }), }) @@ -1181,7 +1181,7 @@ describe('TransactionSender Test', () => { since: '0x0', lock: Script.fromObject({ args: '', - codeHash: SystemScriptInfo.MULTI_SIGN_CODE_HASH, + codeHash: SystemScriptInfo.MULTISIG_CODE_HASH, hashType: SystemScriptInfo.MULTI_SIGN_HASH_TYPE, }), }), @@ -1222,7 +1222,7 @@ describe('TransactionSender Test', () => { m, n: addresses.length, blake160s: addresses.map(v => addressToScript(v).args), - lockCodeHash: SystemScriptInfo.MULTI_SIGN_CODE_HASH, + lockCodeHash: SystemScriptInfo.MULTISIG_CODE_HASH, }), ] } @@ -1244,9 +1244,7 @@ describe('TransactionSender Test', () => { mockGAI.mockReturnValueOnce([addr]) transactionSender.getAddressInfos = mockGAI.bind(transactionSender) const tx = Transaction.fromObject(transactionObject) - tx.inputs[0]!.setLock( - SystemScriptInfo.generateMultiSignScript(multiArgs, SystemScriptInfo.MULTI_SIGN_CODE_HASH) - ) + tx.inputs[0]!.setLock(SystemScriptInfo.generateMultiSignScript(multiArgs, SystemScriptInfo.MULTISIG_CODE_HASH)) const res = await transactionSender.signMultisig(fakeWallet.id, tx, '1234', [multisigConfig]) expect(res.witnesses[0]).toBe( '0x810000001000000081000000810000006d00000000010102729a884056fed321daaca410d94a5d6425a6ca1f0fbd88910d2348d69da5a980f2376a7a1a04feb56b75ef075f1cb4a3e681846c7d8c6ad1575dc5889a2685df215dc566c346757d6d3d46b4201b38f55f87d381fc65e074c1292145fedda1e6d179e6b1052194e200' @@ -1276,7 +1274,7 @@ describe('TransactionSender Test', () => { const getAddressInfos = transactionSender.getAddressInfos transactionSender.getAddressInfos = mockGAI.bind(transactionSender) tx.inputs[0]!.setLock( - SystemScriptInfo.generateMultiSignScript(multiArgs, SystemScriptInfo.MULTI_SIGN_CODE_HASH) + SystemScriptInfo.generateMultiSignScript(multiArgs, SystemScriptInfo.MULTISIG_CODE_HASH) ) tx = await transactionSender.signMultisig(fakeWallet.id, tx, '1234', [multisigConfig]) const lock = (tx.witnesses[0] as WitnessArgs).lock! @@ -1343,9 +1341,7 @@ describe('TransactionSender Test', () => { transactionSender.getAddressInfos = mockGAI.bind(transactionSender) const tx = Transaction.fromObject(transactionObject) - tx.inputs[0]!.setLock( - SystemScriptInfo.generateMultiSignScript(multiArgs, SystemScriptInfo.MULTI_SIGN_CODE_HASH) - ) + tx.inputs[0]!.setLock(SystemScriptInfo.generateMultiSignScript(multiArgs, SystemScriptInfo.MULTISIG_CODE_HASH)) await expect(transactionSender.signMultisig(fakeWallet.id, tx, '1234', [multisigConfig])).rejects.toThrow( new NoMatchAddressForSign() ) @@ -1383,7 +1379,7 @@ describe('TransactionSender Test', () => { transactionSender.getAddressInfos = mockGAI.bind(transactionSender) const tx = Transaction.fromObject(transactionObject) tx.inputs[0]!.setLock( - SystemScriptInfo.generateMultiSignScript(multiArgs, SystemScriptInfo.MULTI_SIGN_CODE_HASH) + SystemScriptInfo.generateMultiSignScript(multiArgs, SystemScriptInfo.MULTISIG_CODE_HASH) ) const res = await transactionSender.signMultisig(fakeWallet.id, tx, '1234', [multisigConfig]) const expectedValue = serializeWitnessArgs({ @@ -1418,9 +1414,7 @@ describe('TransactionSender Test', () => { mockGAI.mockReturnValueOnce([addr]) transactionSender.getAddressInfos = mockGAI.bind(transactionSender) const tx = Transaction.fromObject(transactionObject) - tx.inputs[0]!.setLock( - SystemScriptInfo.generateMultiSignScript(multiArgs, SystemScriptInfo.MULTI_SIGN_CODE_HASH) - ) + tx.inputs[0]!.setLock(SystemScriptInfo.generateMultiSignScript(multiArgs, SystemScriptInfo.MULTISIG_CODE_HASH)) tx.inputs.push( Input.fromObject({ previousOutput: OutPoint.fromObject({ @@ -1430,7 +1424,7 @@ describe('TransactionSender Test', () => { since: '0x0', lock: Script.fromObject({ args: multiArgs, - codeHash: SystemScriptInfo.MULTI_SIGN_CODE_HASH, + codeHash: SystemScriptInfo.MULTISIG_CODE_HASH, hashType: SystemScriptInfo.MULTI_SIGN_HASH_TYPE, }), }) diff --git a/packages/neuron-wallet/tests/utils/multisig.test.ts b/packages/neuron-wallet/tests/utils/multisig.test.ts index 6d33c95b18..3fbef5aa62 100644 --- a/packages/neuron-wallet/tests/utils/multisig.test.ts +++ b/packages/neuron-wallet/tests/utils/multisig.test.ts @@ -15,7 +15,7 @@ describe('getMultisigStatus test', () => { m: 2, n: 3, blake160s: Object.values(addressesToArgs), - lockCodeHash: SystemScriptInfo.LEGACY_MULTI_SIGN_CODE_HASH, + lockCodeHash: SystemScriptInfo.LEGACY_MULTISIG_CODE_HASH, }) it('Unsigned', () => { expect(getMultisigStatus(multisigConfig, {})).toBe(SignStatus.Unsigned) From fb35f6971999c61746a34ba7bd04439fa0d5dc59 Mon Sep 17 00:00:00 2001 From: chenyan Date: Tue, 29 Apr 2025 11:34:06 +0800 Subject: [PATCH 12/22] fix --- .../src/widgets/Dialog/dialog.module.scss | 5 ---- .../neuron-wallet/src/controllers/multisig.ts | 30 +++++++------------ 2 files changed, 10 insertions(+), 25 deletions(-) diff --git a/packages/neuron-ui/src/widgets/Dialog/dialog.module.scss b/packages/neuron-ui/src/widgets/Dialog/dialog.module.scss index 1e4b156c03..44b0debea6 100644 --- a/packages/neuron-ui/src/widgets/Dialog/dialog.module.scss +++ b/packages/neuron-ui/src/widgets/Dialog/dialog.module.scss @@ -5,13 +5,11 @@ padding: 0; position: relative; - /* 對話框背景遮罩 */ &::backdrop { @include overlay; } } -/* 對話框頭部 */ .header { position: -webkit-sticky; position: sticky; @@ -47,20 +45,17 @@ } } -/* 對話框內容區域 */ .content { padding: 20px 16px; position: relative; max-height: calc(100vh - 260px); overflow-y: auto; - /* 隱藏滾動條但保持滾動功能 */ &::-webkit-scrollbar { width: 0; } } -/* 對話框底部區域 */ .footerWrap { height: 104px; } diff --git a/packages/neuron-wallet/src/controllers/multisig.ts b/packages/neuron-wallet/src/controllers/multisig.ts index 375f4df088..c323da4878 100644 --- a/packages/neuron-wallet/src/controllers/multisig.ts +++ b/packages/neuron-wallet/src/controllers/multisig.ts @@ -20,30 +20,20 @@ import MultisigConfigDbChangedSubject from '../models/subjects/multisig-config-d import { LightRPC } from '../utils/ckb-rpc' import SyncProgressService from '../services/sync-progress' +interface MultisigConfigItem { + sighash_addresses: string[] + require_first_n: number + threshold: number + alias?: string | string[] + lockCodeHash: string | string[] +} + interface MultisigConfigOutput { - multisig_configs: Record< - string, - { - sighash_addresses: string[] - require_first_n: number - threshold: number - alias?: string | string[] - lockCodeHash: string | string[] - } - > + multisig_configs: Record } interface MultisigConfigInput { - multisig_configs: Record< - string, - { - sighash_addresses: string[] - require_first_n: number - threshold: number - alias?: string - lockCodeHash?: string | string[] - } - > + multisig_configs: Record & { lockCodeHash?: string | string[] }> } const validateImportConfig = (configOutput: MultisigConfigInput) => { From 9566a4dbef18d56acbc7d16185610afc275eb9e4 Mon Sep 17 00:00:00 2001 From: chenyan Date: Tue, 29 Apr 2025 11:43:51 +0800 Subject: [PATCH 13/22] fix --- .../src/components/MultisigAddress/index.tsx | 11 +++++++---- packages/neuron-wallet/src/controllers/multisig.ts | 2 +- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/packages/neuron-ui/src/components/MultisigAddress/index.tsx b/packages/neuron-ui/src/components/MultisigAddress/index.tsx index f14f46a47a..050756a970 100644 --- a/packages/neuron-ui/src/components/MultisigAddress/index.tsx +++ b/packages/neuron-ui/src/components/MultisigAddress/index.tsx @@ -225,14 +225,17 @@ const MultisigAddress = () => { if (!config.isLegacy && ['regenerate'].includes(item.key)) { return } - if (!multisigBanlances[config.fullPayload] || multisigBanlances[config.fullPayload] === '0') { + if ( + item.key === 'send' && + (!multisigBanlances[config.fullPayload] || multisigBanlances[config.fullPayload] === '0') + ) { options.push({ ...item, - disabled: item.disabled || item.key === 'send', + disabled: true, }) - } else { - options.push(item) + return } + options.push(item) }) return options }, diff --git a/packages/neuron-wallet/src/controllers/multisig.ts b/packages/neuron-wallet/src/controllers/multisig.ts index c323da4878..424fe8e65b 100644 --- a/packages/neuron-wallet/src/controllers/multisig.ts +++ b/packages/neuron-wallet/src/controllers/multisig.ts @@ -195,7 +195,7 @@ export default class MultisigController { n: config.sighash_addresses.length, blake160s: config.sighash_addresses.map(v => addressToScript(v).args), walletId, - alias: config.alias, + alias: config.alias as string, lockCodeHash: config.lockCodeHash, }) } From 3fbdbfd715abb02234bfe28ac25d74ce5cdf32b2 Mon Sep 17 00:00:00 2001 From: chenyan Date: Wed, 30 Apr 2025 17:41:47 +0800 Subject: [PATCH 14/22] feat: update --- .../components/HistoryDetailPage/index.tsx | 2 +- .../src/components/MultisigAddress/index.tsx | 33 +++++++- .../multisigAddress.module.scss | 62 +++++++++++--- packages/neuron-ui/src/utils/enums.ts | 4 +- .../src/widgets/Icons/LockCodeHash.svg | 11 +++ packages/neuron-ui/src/widgets/Icons/icon.tsx | 2 + .../sync/light-synchronizer.ts | 6 ++ .../neuron-wallet/src/controllers/multisig.ts | 80 +++++-------------- .../src/models/system-script-info.ts | 19 ++--- .../src/services/hardware/hardware.ts | 4 +- .../src/services/transaction-sender.ts | 6 +- .../tests/services/cells.test.ts | 18 ++--- .../tests/services/multisig.test.ts | 2 +- .../services/tx/transaction-sender.test.ts | 8 +- 14 files changed, 152 insertions(+), 105 deletions(-) create mode 100644 packages/neuron-ui/src/widgets/Icons/LockCodeHash.svg diff --git a/packages/neuron-ui/src/components/HistoryDetailPage/index.tsx b/packages/neuron-ui/src/components/HistoryDetailPage/index.tsx index 8db87ebf76..7cf9513d42 100644 --- a/packages/neuron-ui/src/components/HistoryDetailPage/index.tsx +++ b/packages/neuron-ui/src/components/HistoryDetailPage/index.tsx @@ -289,7 +289,7 @@ const HistoryDetailPage = () => { title: t('transaction.address'), dataIndex: 'type', align: 'left', - width: '560px', + width: '600px', render: (_, __, item) => { const { address } = handleListData(item) return ( diff --git a/packages/neuron-ui/src/components/MultisigAddress/index.tsx b/packages/neuron-ui/src/components/MultisigAddress/index.tsx index 050756a970..0c7c5b4666 100644 --- a/packages/neuron-ui/src/components/MultisigAddress/index.tsx +++ b/packages/neuron-ui/src/components/MultisigAddress/index.tsx @@ -7,6 +7,7 @@ import { useGoBack, useOnWindowResize, calculateFee, + clsx, } from 'utils' import appState from 'states/init/app' import { useState as useGlobalState } from 'states' @@ -22,6 +23,7 @@ import Table from 'widgets/Table' import Tooltip from 'widgets/Tooltip' import Toast from 'widgets/Toast' import AlertDialog from 'widgets/AlertDialog' +import CopyZone from 'widgets/CopyZone' import { Download, Search, @@ -39,6 +41,8 @@ import { DAOWithdrawal, Attention, Regenerate, + LockCodeHash, + Copy, } from 'widgets/Icons/icon' import { getHeader } from 'services/chain' import AttentionCloseDialog from 'widgets/Icons/Attention.png' @@ -372,9 +376,8 @@ const MultisigAddress = () => { , + aria-label=" " + />, ]} />
@@ -437,8 +440,30 @@ const MultisigAddress = () => { render(_, __, item) { return (
+ +
+

Code_hash

+
+ {item.isLegacy ? 'Legacy' : 'Recommended'} +
+
+ + {item.lockCodeHash} + + +
+ } + isTriggerNextToChild + tipClassName={styles.lockCodeHashTip} + > +
+ + @{item.lockCodeHash.slice(2, 10)} +
+ {item.fullPayload.slice(0, 5)}...{item.fullPayload.slice(-5)} - {item.isLegacy &&
Legacy
}
) }, diff --git a/packages/neuron-ui/src/components/MultisigAddress/multisigAddress.module.scss b/packages/neuron-ui/src/components/MultisigAddress/multisigAddress.module.scss index e6965a30f4..dac3a9dd7b 100644 --- a/packages/neuron-ui/src/components/MultisigAddress/multisigAddress.module.scss +++ b/packages/neuron-ui/src/components/MultisigAddress/multisigAddress.module.scss @@ -15,7 +15,7 @@ height: 44px; border-bottom: 1px solid var(--warn-border-color); padding: 0 16px; - max-width: 648px; + max-width: 780px; & > svg { width: 16px; flex-shrink: 0; @@ -112,7 +112,7 @@ .tableWrap { margin-top: 16px; min-height: 360px; - min-width: 648px; + min-width: 748px; border: 1px solid var(--table-head-border-color); border-radius: 16px; border-top: none; @@ -130,18 +130,58 @@ .address { font-family: 'JetBrains Mono'; position: relative; + display: flex; + align-items: center; + column-gap: 4px; - .legacy { - font-size: 10px; - height: 16px; - color: var(--third-text-color); + .titleWrap { + display: flex; + align-items: center; + column-gap: 4px; + color: var(--main-text-color); font-weight: 500; - position: absolute; - top: -16px; - right: -10px; - background: var(--table-head-border-color); + + .tag { + font-size: 12px; + line-height: 18px; + color: var(--tabs-active); + font-weight: 500; + background: var(--tag-background-color); + padding: 0 4px; + border-radius: 2px; + } + .legacy { + color: var(--third-text-color); + background: var(--input-border-color); + } + } + .lockCodeHash { + display: flex; + align-items: center; + font-size: 13px; + border: 1px solid var(--tabs-active); + border-radius: 4px; padding: 0 4px; - border-radius: 48px; + line-height: 24px; + color: var(--tabs-active); + font-weight: 500; + &.legacy { + border: 1px solid var(--divide-line-color); + color: var(--input-second-color); + svg { + path { + stroke: var(--input-second-color); + } + } + } + } + + .copyLockCodeHash { + @include copyAddress; + } + + .lockCodeHashTip { + margin-left: 50px; } } diff --git a/packages/neuron-ui/src/utils/enums.ts b/packages/neuron-ui/src/utils/enums.ts index e3fbb7b832..faac9373c3 100644 --- a/packages/neuron-ui/src/utils/enums.ts +++ b/packages/neuron-ui/src/utils/enums.ts @@ -155,7 +155,7 @@ export enum LegacyMultiSigLockInfo { HashType = 'type', CodeHashIndex = '0x01', ArgsLen = '20', - TagName = 'secp256k1/multisig/legacy', + TagName = 'secp256k1/multisig/@5c5069eb', } export enum MultiSigLockInfo { @@ -163,7 +163,7 @@ export enum MultiSigLockInfo { HashType = 'data1', CodeHashIndex = '0x01', ArgsLen = '20', - TagName = 'secp256k1/multisig', + TagName = 'secp256k1/multisig/@36c971b8', } export enum LocktimeLockInfo { diff --git a/packages/neuron-ui/src/widgets/Icons/LockCodeHash.svg b/packages/neuron-ui/src/widgets/Icons/LockCodeHash.svg new file mode 100644 index 0000000000..d4e5a2b51e --- /dev/null +++ b/packages/neuron-ui/src/widgets/Icons/LockCodeHash.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/packages/neuron-ui/src/widgets/Icons/icon.tsx b/packages/neuron-ui/src/widgets/Icons/icon.tsx index 1877e7a3a9..1e63907841 100644 --- a/packages/neuron-ui/src/widgets/Icons/icon.tsx +++ b/packages/neuron-ui/src/widgets/Icons/icon.tsx @@ -68,6 +68,7 @@ import DAOWithdrawalSvg from './DAOWithdrawal.svg?react' import RecycleSvg from './Recycle.svg?react' import MoreSvg from './More.svg?react' import RegenerateSvg from './Regenerate.svg?react' +import LockCodeHashSvg from './LockCodeHash.svg?react' import styles from './icon.module.scss' @@ -152,3 +153,4 @@ export const DAOWithdrawal = WrapSvg(DAOWithdrawalSvg) export const Recycle = WrapSvg(RecycleSvg) export const More = WrapSvg(MoreSvg) export const Regenerate = WrapSvg(RegenerateSvg) +export const LockCodeHash = WrapSvg(LockCodeHashSvg) diff --git a/packages/neuron-wallet/src/block-sync-renderer/sync/light-synchronizer.ts b/packages/neuron-wallet/src/block-sync-renderer/sync/light-synchronizer.ts index 71c0faca18..1ba1d7daad 100644 --- a/packages/neuron-wallet/src/block-sync-renderer/sync/light-synchronizer.ts +++ b/packages/neuron-wallet/src/block-sync-renderer/sync/light-synchronizer.ts @@ -12,6 +12,7 @@ import Multisig from '../../services/multisig' import SyncProgress, { SyncAddressType } from '../../database/chain/entities/sync-progress' import WalletService from '../../services/wallets' import AssetAccountInfo from '../../models/asset-account-info' +import SystemScriptInfo from '../../models/system-script-info' import { DepType } from '../../models/chain/cell-dep' import { vector, blockchain } from '@ckb-lumos/lumos/codec' import type { Base } from '@ckb-lumos/lumos/rpc' @@ -39,6 +40,9 @@ export default class LightSynchronizer extends Synchronizer { private async getDepTxs(): Promise { const assetAccountInfo = new AssetAccountInfo() + const multiSignCellDep = await SystemScriptInfo.getInstance().getMultiSignCellDep( + SystemScriptInfo.MULTISIG_CODE_HASH + ) const fetchCellDeps = [ assetAccountInfo.anyoneCanPayCellDep, assetAccountInfo.sudtCellDep, @@ -48,6 +52,8 @@ export default class LightSynchronizer extends Synchronizer { assetAccountInfo.getNftIssuerInfo().cellDep, assetAccountInfo.getLegacyAnyoneCanPayInfo().cellDep, assetAccountInfo.getChequeInfo().cellDep, + multiSignCellDep, + ...assetAccountInfo.getSporeInfos().map(info => info.cellDep), ...assetAccountInfo.getSporeClusterInfo().map(info => info.cellDep), ] diff --git a/packages/neuron-wallet/src/controllers/multisig.ts b/packages/neuron-wallet/src/controllers/multisig.ts index 424fe8e65b..cd7bc203f0 100644 --- a/packages/neuron-wallet/src/controllers/multisig.ts +++ b/packages/neuron-wallet/src/controllers/multisig.ts @@ -24,8 +24,8 @@ interface MultisigConfigItem { sighash_addresses: string[] require_first_n: number threshold: number - alias?: string | string[] - lockCodeHash: string | string[] + alias?: string + lockCodeHash: string } interface MultisigConfigOutput { @@ -33,7 +33,7 @@ interface MultisigConfigOutput { } interface MultisigConfigInput { - multisig_configs: Record & { lockCodeHash?: string | string[] }> + multisig_configs: Record & { lockCodeHash?: string }> } const validateImportConfig = (configOutput: MultisigConfigInput) => { @@ -157,7 +157,7 @@ export default class MultisigController { } try { const json = fs.readFileSync(filePaths[0], 'utf-8') - const configOutput: MultisigConfigInput = JSON.parse(json) + const configOutput: MultisigConfigOutput = JSON.parse(json) if (!validateImportConfig(configOutput)) { ShowGlobalDialogSubject.next({ type: 'failed', @@ -166,40 +166,15 @@ export default class MultisigController { }) return } - const saveConfigs: { - r: number - m: number - n: number - blake160s: string[] - walletId: string - alias?: string - lockCodeHash?: string - }[] = [] - Object.values(configOutput.multisig_configs).forEach(config => { - if (Array.isArray(config.lockCodeHash)) { - config.lockCodeHash.forEach((lockCodeHash, index) => { - saveConfigs.push({ - r: +config.require_first_n, - m: +config.threshold, - n: config.sighash_addresses.length, - blake160s: config.sighash_addresses.map(v => addressToScript(v).args), - walletId, - alias: config.alias?.[index], - lockCodeHash, - }) - }) - } else { - saveConfigs.push({ - r: +config.require_first_n, - m: +config.threshold, - n: config.sighash_addresses.length, - blake160s: config.sighash_addresses.map(v => addressToScript(v).args), - walletId, - alias: config.alias as string, - lockCodeHash: config.lockCodeHash, - }) - } - }) + const saveConfigs = Object.values(configOutput.multisig_configs).map(config => ({ + r: +config.require_first_n, + m: +config.threshold, + n: config.sighash_addresses.length, + blake160s: config.sighash_addresses.map(v => addressToScript(v).args), + walletId, + alias: config.alias, + lockCodeHash: config.lockCodeHash, + })) const savedResult = await Promise.allSettled(saveConfigs.map(config => this.saveConfig(config))) const saveSuccessConfigs: MultisigConfig[] = [] for (let idx = 0; idx < savedResult.length; idx++) { @@ -251,27 +226,14 @@ export default class MultisigController { const isMainnet = NetworksService.getInstance().isMainnet() const output: MultisigConfigOutput = { multisig_configs: {} } configs.forEach(v => { - const hash = Multisig.hash(v.blake160s, v.r, v.m, v.n) - if (output.multisig_configs[hash]) { - const existingLockCodeHash = output.multisig_configs[hash].lockCodeHash - output.multisig_configs[hash].lockCodeHash = Array.isArray(existingLockCodeHash) - ? [...existingLockCodeHash, v.lockCodeHash] - : [existingLockCodeHash, v.lockCodeHash] - - const existingAlias = output.multisig_configs[hash].alias || '' - output.multisig_configs[hash].alias = Array.isArray(existingAlias) - ? [...existingAlias, v.alias || ''] - : [existingAlias, v.alias || ''] - } else { - output.multisig_configs[hash] = { - sighash_addresses: v.blake160s.map(args => - scriptToAddress(SystemScriptInfo.generateSecpScript(args), isMainnet) - ), - require_first_n: v.r, - threshold: v.m, - lockCodeHash: v.lockCodeHash, - alias: v.alias, - } + output.multisig_configs[`${Multisig.hash(v.blake160s, v.r, v.m, v.n)}_${v.lockCodeHash.slice(2, 10)}`] = { + sighash_addresses: v.blake160s.map(args => + scriptToAddress(SystemScriptInfo.generateSecpScript(args), isMainnet) + ), + require_first_n: v.r, + threshold: v.m, + alias: v.alias, + lockCodeHash: v.lockCodeHash, } }) diff --git a/packages/neuron-wallet/src/models/system-script-info.ts b/packages/neuron-wallet/src/models/system-script-info.ts index 9b4a9dd75d..c0968acbce 100644 --- a/packages/neuron-wallet/src/models/system-script-info.ts +++ b/packages/neuron-wallet/src/models/system-script-info.ts @@ -15,8 +15,8 @@ export default class SystemScriptInfo { static SECP_HASH_TYPE = ScriptHashType.Type static DAO_HASH_TYPE = ScriptHashType.Type - static LEGACY_MULTI_SIGN_HASH_TYPE = ScriptHashType.Type - static MULTI_SIGN_HASH_TYPE = ScriptHashType.Data1 + static LEGACY_MULTISIGN_HASH_TYPE = ScriptHashType.Type + static MULTISIGN_HASH_TYPE = ScriptHashType.Data1 static DAO_SCRIPT_HASH = new Script( systemScripts.DAO.CODE_HASH, @@ -41,11 +41,12 @@ export default class SystemScriptInfo { private legacyMultiSignOutPointInfo = new Map() private multiSignOutPointInfo = new Map() - public static getMultiSignHashType(lockCodeHash: string): ScriptHashType { - if (lockCodeHash === SystemScriptInfo.MULTISIG_CODE_HASH) { - return SystemScriptInfo.MULTI_SIGN_HASH_TYPE + public static getMultiSignHashType(lockCodeHash: string) { + if (!SystemScriptInfo.isMultiSignCodeHash(lockCodeHash)) { + throw new Error('Unknown Multisig Code Hash') } - return SystemScriptInfo.LEGACY_MULTI_SIGN_HASH_TYPE + if (lockCodeHash === SystemScriptInfo.MULTISIG_CODE_HASH) return SystemScriptInfo.MULTISIGN_HASH_TYPE + if (lockCodeHash === SystemScriptInfo.LEGACY_MULTISIG_CODE_HASH) return SystemScriptInfo.LEGACY_MULTISIGN_HASH_TYPE } // need network url and genesisBlockHash @@ -105,7 +106,7 @@ export default class SystemScriptInfo { } public static generateMultiSignScript(args: string, lockCodeHash: string): Script { - return new Script(lockCodeHash, args, SystemScriptInfo.getMultiSignHashType(lockCodeHash)) + return new Script(lockCodeHash, args, SystemScriptInfo.getMultiSignHashType(lockCodeHash) as ScriptHashType) } public static isSecpScript(script: Script): boolean { @@ -119,9 +120,9 @@ export default class SystemScriptInfo { public static isMultiSignScript(script: Script): boolean { return ( (script.codeHash === SystemScriptInfo.LEGACY_MULTISIG_CODE_HASH && - script.hashType === SystemScriptInfo.LEGACY_MULTI_SIGN_HASH_TYPE) || + script.hashType === SystemScriptInfo.LEGACY_MULTISIGN_HASH_TYPE) || (script.codeHash === SystemScriptInfo.MULTISIG_CODE_HASH && - script.hashType === SystemScriptInfo.MULTI_SIGN_HASH_TYPE) + script.hashType === SystemScriptInfo.MULTISIGN_HASH_TYPE) ) } diff --git a/packages/neuron-wallet/src/services/hardware/hardware.ts b/packages/neuron-wallet/src/services/hardware/hardware.ts index 8ec5592068..fa474f16e5 100644 --- a/packages/neuron-wallet/src/services/hardware/hardware.ts +++ b/packages/neuron-wallet/src/services/hardware/hardware.ts @@ -43,7 +43,7 @@ export abstract class Hardware { }) const isMultisig = - tx.inputs.length === 1 && tx.inputs[0].lock!.args.length === TransactionSender.MULTI_SIGN_ARGS_LENGTH + tx.inputs.length === 1 && tx.inputs[0].lock!.args.length === TransactionSender.MULTISIGN_ARGS_LENGTH const multiSignBlake160s = isMultisig ? addressInfos.map(i => { @@ -55,7 +55,7 @@ export abstract class Hardware { : [] const findPath = (args: string) => { - if (args.length === TransactionSender.MULTI_SIGN_ARGS_LENGTH) { + if (args.length === TransactionSender.MULTISIGN_ARGS_LENGTH) { return multiSignBlake160s.find(i => args.slice(0, 42) === i.multiSignBlake160)!.path } else if (args.length === 42) { return addressInfos.find(i => i.blake160 === args)!.path diff --git a/packages/neuron-wallet/src/services/transaction-sender.ts b/packages/neuron-wallet/src/services/transaction-sender.ts index 451b9cd584..b4a82d9904 100644 --- a/packages/neuron-wallet/src/services/transaction-sender.ts +++ b/packages/neuron-wallet/src/services/transaction-sender.ts @@ -61,7 +61,7 @@ interface PathAndPrivateKey { } export default class TransactionSender { - static MULTI_SIGN_ARGS_LENGTH = 58 + static MULTISIGN_ARGS_LENGTH = 58 private walletService: WalletService @@ -147,7 +147,7 @@ export default class TransactionSender { // Only one multi sign input now. const isMultisig = - tx.inputs.length === 1 && tx.inputs[0].lock!.args.length === TransactionSender.MULTI_SIGN_ARGS_LENGTH + tx.inputs.length === 1 && tx.inputs[0].lock!.args.length === TransactionSender.MULTISIGN_ARGS_LENGTH const addressInfos = await this.getAddressInfos(walletID) const multiSignBlake160s = isMultisig @@ -162,7 +162,7 @@ export default class TransactionSender { const pathAndPrivateKeys = this.getPrivateKeys(wallet, paths, password) const findPrivateKey = (args: string) => { let path: string | undefined - if (args.length === TransactionSender.MULTI_SIGN_ARGS_LENGTH) { + if (args.length === TransactionSender.MULTISIGN_ARGS_LENGTH) { path = multiSignBlake160s.find(i => args.slice(0, 42) === i.multiSignBlake160)!.path } else if (args.length === 42) { path = addressInfos.find(i => i.blake160 === args)!.path diff --git a/packages/neuron-wallet/tests/services/cells.test.ts b/packages/neuron-wallet/tests/services/cells.test.ts index b21f6fe5a1..e361b19f4d 100644 --- a/packages/neuron-wallet/tests/services/cells.test.ts +++ b/packages/neuron-wallet/tests/services/cells.test.ts @@ -558,7 +558,7 @@ describe('CellsService', () => { await expect( CellsService.gatherInputs(toShannon('1001'), '', '0', '1000', 0, 0, 0, undefined, { lockArgs: ['bob.blake160'], - hashType: SystemScriptInfo.LEGACY_MULTI_SIGN_HASH_TYPE, + hashType: SystemScriptInfo.LEGACY_MULTISIGN_HASH_TYPE, codeHash: SystemScriptInfo.LEGACY_MULTISIG_CODE_HASH, }) ).rejects.toThrow(new CapacityNotEnough()) @@ -568,7 +568,7 @@ describe('CellsService', () => { await expect( CellsService.gatherInputs(toShannon('1001'), '', '0', '1000', 0, 0, 0, undefined, { lockArgs: [multisigInfo.lockScript.args], - hashType: SystemScriptInfo.LEGACY_MULTI_SIGN_HASH_TYPE, + hashType: SystemScriptInfo.LEGACY_MULTISIGN_HASH_TYPE, codeHash: SystemScriptInfo.LEGACY_MULTISIG_CODE_HASH, }) ).rejects.toThrow(new MultisigConfigNeedError()) @@ -587,7 +587,7 @@ describe('CellsService', () => { undefined, { lockArgs: [multisigInfo.lockScript.args], - hashType: SystemScriptInfo.LEGACY_MULTI_SIGN_HASH_TYPE, + hashType: SystemScriptInfo.LEGACY_MULTISIGN_HASH_TYPE, codeHash: SystemScriptInfo.LEGACY_MULTISIG_CODE_HASH, }, [ @@ -617,7 +617,7 @@ describe('CellsService', () => { undefined, { lockArgs: [multisigInfo.lockScript.args], - hashType: SystemScriptInfo.LEGACY_MULTI_SIGN_HASH_TYPE, + hashType: SystemScriptInfo.LEGACY_MULTISIGN_HASH_TYPE, codeHash: SystemScriptInfo.LEGACY_MULTISIG_CODE_HASH, }, [ @@ -647,7 +647,7 @@ describe('CellsService', () => { undefined, { lockArgs: [multisigInfo.lockScript.args], - hashType: SystemScriptInfo.LEGACY_MULTI_SIGN_HASH_TYPE, + hashType: SystemScriptInfo.LEGACY_MULTISIGN_HASH_TYPE, codeHash: SystemScriptInfo.LEGACY_MULTISIG_CODE_HASH, }, [ @@ -676,7 +676,7 @@ describe('CellsService', () => { undefined, { lockArgs: [multisigInfo.lockScript.args], - hashType: SystemScriptInfo.LEGACY_MULTI_SIGN_HASH_TYPE, + hashType: SystemScriptInfo.LEGACY_MULTISIGN_HASH_TYPE, codeHash: SystemScriptInfo.LEGACY_MULTISIG_CODE_HASH, }, [ @@ -706,7 +706,7 @@ describe('CellsService', () => { undefined, { lockArgs: [multisigInfo.lockScript.args], - hashType: SystemScriptInfo.LEGACY_MULTI_SIGN_HASH_TYPE, + hashType: SystemScriptInfo.LEGACY_MULTISIGN_HASH_TYPE, codeHash: SystemScriptInfo.LEGACY_MULTISIG_CODE_HASH, }, [ @@ -784,7 +784,7 @@ describe('CellsService', () => { it('gather with exist args', async () => { const inputs = await CellsService.gatherAllInputs(walletId1, { codeHash: SystemScriptInfo.LEGACY_MULTISIG_CODE_HASH, - hashType: SystemScriptInfo.LEGACY_MULTI_SIGN_HASH_TYPE, + hashType: SystemScriptInfo.LEGACY_MULTISIGN_HASH_TYPE, args: multisigInfo.lockScript.args, }) expect(inputs).toHaveLength(1) @@ -793,7 +793,7 @@ describe('CellsService', () => { it('gather with non-exist args', async () => { const inputs = await CellsService.gatherAllInputs(walletId1, { codeHash: SystemScriptInfo.LEGACY_MULTISIG_CODE_HASH, - hashType: SystemScriptInfo.LEGACY_MULTI_SIGN_HASH_TYPE, + hashType: SystemScriptInfo.LEGACY_MULTISIGN_HASH_TYPE, args: 'non-exist-args', }) expect(inputs).toHaveLength(0) diff --git a/packages/neuron-wallet/tests/services/multisig.test.ts b/packages/neuron-wallet/tests/services/multisig.test.ts index bcfd45a3cc..1201317797 100644 --- a/packages/neuron-wallet/tests/services/multisig.test.ts +++ b/packages/neuron-wallet/tests/services/multisig.test.ts @@ -66,7 +66,7 @@ describe('multisig service', () => { multisigConfigModel.n ), codeHash: SystemScriptInfo.MULTISIG_CODE_HASH, - hashType: SystemScriptInfo.MULTI_SIGN_HASH_TYPE, + hashType: SystemScriptInfo.MULTISIGN_HASH_TYPE, } const defaultTxOutpoint = { tx_hash: 'tx_hash', index: '0x0' } const defaultOutput = { diff --git a/packages/neuron-wallet/tests/services/tx/transaction-sender.test.ts b/packages/neuron-wallet/tests/services/tx/transaction-sender.test.ts index 5ae1d9fd84..4c8687b604 100644 --- a/packages/neuron-wallet/tests/services/tx/transaction-sender.test.ts +++ b/packages/neuron-wallet/tests/services/tx/transaction-sender.test.ts @@ -896,7 +896,7 @@ describe('TransactionSender Test', () => { lock: Script.fromObject({ args: '', codeHash: SystemScriptInfo.LEGACY_MULTISIG_CODE_HASH, - hashType: SystemScriptInfo.LEGACY_MULTI_SIGN_HASH_TYPE, + hashType: SystemScriptInfo.LEGACY_MULTISIGN_HASH_TYPE, }), }), ], @@ -1145,7 +1145,7 @@ describe('TransactionSender Test', () => { lock: Script.fromObject({ args: multiArgs, codeHash: SystemScriptInfo.LEGACY_MULTISIG_CODE_HASH, - hashType: SystemScriptInfo.LEGACY_MULTI_SIGN_HASH_TYPE, + hashType: SystemScriptInfo.LEGACY_MULTISIGN_HASH_TYPE, }), }) ) @@ -1182,7 +1182,7 @@ describe('TransactionSender Test', () => { lock: Script.fromObject({ args: '', codeHash: SystemScriptInfo.MULTISIG_CODE_HASH, - hashType: SystemScriptInfo.MULTI_SIGN_HASH_TYPE, + hashType: SystemScriptInfo.MULTISIGN_HASH_TYPE, }), }), ], @@ -1425,7 +1425,7 @@ describe('TransactionSender Test', () => { lock: Script.fromObject({ args: multiArgs, codeHash: SystemScriptInfo.MULTISIG_CODE_HASH, - hashType: SystemScriptInfo.MULTI_SIGN_HASH_TYPE, + hashType: SystemScriptInfo.MULTISIGN_HASH_TYPE, }), }) ) From cbc53e0e1bcf76ca58ca713a1880f4e060340d1d Mon Sep 17 00:00:00 2001 From: chenyan Date: Sat, 3 May 2025 23:05:41 +0800 Subject: [PATCH 15/22] feat: update --- .../src/components/LockInfoDialog/index.tsx | 28 ++++++++------ .../src/components/MultisigAddress/index.tsx | 2 +- .../multisigAddress.module.scss | 2 +- .../src/components/ScriptTag/index.tsx | 33 +++++++++++++++++ .../ScriptTag/scriptTag.module.scss | 37 +++++++++++++++++++ packages/neuron-ui/src/utils/enums.ts | 4 +- 6 files changed, 90 insertions(+), 16 deletions(-) diff --git a/packages/neuron-ui/src/components/LockInfoDialog/index.tsx b/packages/neuron-ui/src/components/LockInfoDialog/index.tsx index 2242042484..5eac285eaf 100644 --- a/packages/neuron-ui/src/components/LockInfoDialog/index.tsx +++ b/packages/neuron-ui/src/components/LockInfoDialog/index.tsx @@ -35,19 +35,23 @@ const ShortAddr = ({ return null } - const shortAddr = scriptToAddress(lockScript, { isMainnet, deprecated: true }) + try { + const shortAddr = scriptToAddress(lockScript, { isMainnet, deprecated: true }) - return ( - <> -
- {t('transaction.deprecated-address-format')} -
-
- {shortAddr} - onCopy(shortAddr)} /> -
- - ) + return ( + <> +
+ {t('transaction.deprecated-address-format')} +
+
+ {shortAddr} + onCopy(shortAddr)} /> +
+ + ) + } catch (error) { + return null + } } const LockInfoDialog = ({ lockInfo, isMainnet, onDismiss }: LockInfoDialogProps) => { diff --git a/packages/neuron-ui/src/components/MultisigAddress/index.tsx b/packages/neuron-ui/src/components/MultisigAddress/index.tsx index 0c7c5b4666..d08d24b483 100644 --- a/packages/neuron-ui/src/components/MultisigAddress/index.tsx +++ b/packages/neuron-ui/src/components/MultisigAddress/index.tsx @@ -445,7 +445,7 @@ const MultisigAddress = () => {

Code_hash

-
+
{item.isLegacy ? 'Legacy' : 'Recommended'}
diff --git a/packages/neuron-ui/src/components/MultisigAddress/multisigAddress.module.scss b/packages/neuron-ui/src/components/MultisigAddress/multisigAddress.module.scss index dac3a9dd7b..fea92fa4f4 100644 --- a/packages/neuron-ui/src/components/MultisigAddress/multisigAddress.module.scss +++ b/packages/neuron-ui/src/components/MultisigAddress/multisigAddress.module.scss @@ -141,7 +141,7 @@ color: var(--main-text-color); font-weight: 500; - .tag { + .badge { font-size: 12px; line-height: 18px; color: var(--tabs-active); diff --git a/packages/neuron-ui/src/components/ScriptTag/index.tsx b/packages/neuron-ui/src/components/ScriptTag/index.tsx index 35c298ef8e..a4a5ad9ac2 100644 --- a/packages/neuron-ui/src/components/ScriptTag/index.tsx +++ b/packages/neuron-ui/src/components/ScriptTag/index.tsx @@ -11,6 +11,9 @@ import { ChequeLockInfoOnLina, clsx, } from 'utils' +import Tooltip from 'widgets/Tooltip' +import CopyZone from 'widgets/CopyZone' +import { Copy } from 'widgets/Icons/icon' import styles from './scriptTag.module.scss' const ScriptTag = ({ @@ -45,6 +48,36 @@ const ScriptTag = ({ return null } + if (LegacyMultiSigLockInfo.TagName === foundLock.TagName || MultiSigLockInfo.TagName === foundLock.TagName) { + const isLegacy = LegacyMultiSigLockInfo.TagName === foundLock.TagName + return ( +
+ +
+

Code_hash

+
+ {isLegacy ? 'Legacy' : 'Recommended'} +
+
+ + {foundLock.CodeHash} + + +
+ } + showTriangle + placement="top" + > + + +
+ ) + } return ( diff --git a/packages/neuron-ui/src/components/ScriptTag/scriptTag.module.scss b/packages/neuron-ui/src/components/ScriptTag/scriptTag.module.scss index 94da20737b..48161529b8 100644 --- a/packages/neuron-ui/src/components/ScriptTag/scriptTag.module.scss +++ b/packages/neuron-ui/src/components/ScriptTag/scriptTag.module.scss @@ -42,5 +42,6 @@ .highlight { color: var(--primary-color); + margin-left: 2px; } } diff --git a/packages/neuron-ui/src/locales/ar.json b/packages/neuron-ui/src/locales/ar.json index e427c24fbc..34da53d5d8 100644 --- a/packages/neuron-ui/src/locales/ar.json +++ b/packages/neuron-ui/src/locales/ar.json @@ -1194,6 +1194,7 @@ "cancel": "إلغاء", "export": "تصدير المعاملة" }, + "nervos-dao": "نيرفوس DAO", "multisig-script-update-notice": "نظرًا لـ<0>ترقية برنامج النص المتعدد التوقيعات، نوصي بإعادة إنشاء عناوين التوقيع المتعدد ونقل الأصول من العنوان القديم لضمان الأداء السليم.", "regenerate-dialog": { "title": "إعادة الإنشاء", diff --git a/packages/neuron-ui/src/locales/en.json b/packages/neuron-ui/src/locales/en.json index d35ad08d34..0689e923a3 100644 --- a/packages/neuron-ui/src/locales/en.json +++ b/packages/neuron-ui/src/locales/en.json @@ -1194,6 +1194,7 @@ "cancel": "Cancel", "export": "Export Tx" }, + "nervos-dao": "Nervos DAO", "multisig-script-update-notice": "Due to the <0>multisig script upgrade, we recommend regenerating the multisig addresses and transferring assets from the legacy one to ensure proper functionality. ", "regenerate-dialog": { "title": "Regenerate", diff --git a/packages/neuron-ui/src/locales/es.json b/packages/neuron-ui/src/locales/es.json index 8cbbde04f6..1703994fa9 100644 --- a/packages/neuron-ui/src/locales/es.json +++ b/packages/neuron-ui/src/locales/es.json @@ -1177,6 +1177,7 @@ "cancel": "Cancelar", "export": "Exportar Tx" }, + "nervos-dao": "Nervos DAO", "multisig-script-update-notice": "Debido a la <0>actualización del script multisig, recomendamos regenerar las direcciones multisig y transferir los activos desde la antigua para garantizar el funcionamiento adecuado.", "regenerate-dialog": { "title": "Regenerar", diff --git a/packages/neuron-ui/src/locales/fr.json b/packages/neuron-ui/src/locales/fr.json index 7edd61143f..503582172b 100644 --- a/packages/neuron-ui/src/locales/fr.json +++ b/packages/neuron-ui/src/locales/fr.json @@ -1184,6 +1184,7 @@ "cancel": "Annuler", "export": "Exporter Tx" }, + "nervos-dao": "Nervos DAO", "multisig-script-update-notice": "En raison de la <0>mise à jour du script multisig, nous recommandons de régénérer les adresses multisig et de transférer les actifs depuis l’ancienne adresse pour garantir un bon fonctionnement.", "regenerate-dialog": { "title": "Régénérer", diff --git a/packages/neuron-ui/src/locales/zh-tw.json b/packages/neuron-ui/src/locales/zh-tw.json index 72dd6bf76f..f51df2ed47 100644 --- a/packages/neuron-ui/src/locales/zh-tw.json +++ b/packages/neuron-ui/src/locales/zh-tw.json @@ -1187,6 +1187,7 @@ "cancel": "取消", "export": "導出交易" }, + "nervos-dao": "Nervos DAO", "multisig-script-update-notice": "由於<0>多簽腳本升級,我們建議重新生成多簽地址,並將資產從舊地址轉移,以確保正常功能。", "regenerate-dialog": { "title": "重新生成", diff --git a/packages/neuron-wallet/src/block-sync-renderer/sync/light-synchronizer.ts b/packages/neuron-wallet/src/block-sync-renderer/sync/light-synchronizer.ts index 1ba1d7daad..ae0daf9c56 100644 --- a/packages/neuron-wallet/src/block-sync-renderer/sync/light-synchronizer.ts +++ b/packages/neuron-wallet/src/block-sync-renderer/sync/light-synchronizer.ts @@ -251,9 +251,9 @@ export default class LightSynchronizer extends Synchronizer { if (!multisigScripts.length) { return } - const existSyncArgses = await SyncProgressService.getExistingSyncArgses() + const existSyncProgressScripts = await SyncProgressService.getExistingSyncScripts() const syncScripts = await this.lightRpc.getScripts() - const retainedSyncScripts = syncScripts.filter(v => existSyncArgses.has(v.script.args)) + const retainedSyncScripts = syncScripts.filter(v => existSyncProgressScripts.has(scriptToHash(v.script))) const existSyncScripts: Record = {} retainedSyncScripts.forEach(v => { existSyncScripts[scriptToHash(v.script)] = v diff --git a/packages/neuron-wallet/src/services/sync-progress.ts b/packages/neuron-wallet/src/services/sync-progress.ts index 67de4b1d9c..5098916c6e 100644 --- a/packages/neuron-wallet/src/services/sync-progress.ts +++ b/packages/neuron-wallet/src/services/sync-progress.ts @@ -2,6 +2,7 @@ import { In, LessThan, Not } from 'typeorm' import { computeScriptHash as scriptToHash } from '@ckb-lumos/lumos/utils' import SyncProgress, { SyncAddressType } from '../database/chain/entities/sync-progress' import WalletService from './wallets' +import Script from '../models/chain/script' import { getConnection } from '../database/chain/connection' export default class SyncProgressService { @@ -76,6 +77,11 @@ export default class SyncProgressService { return new Set(syncProgresses.map(v => v.args)) } + static async getExistingSyncScripts() { + const syncProgresses = await getConnection().getRepository(SyncProgress).createQueryBuilder().getMany() + return new Set(syncProgresses.map(v => Script.fromObject(v).computeHash())) + } + static async getAllSyncStatusToMap() { const result: Map = new Map() const syncProgresses = await getConnection() diff --git a/packages/neuron-wallet/src/services/transaction-sender.ts b/packages/neuron-wallet/src/services/transaction-sender.ts index b4a82d9904..dc2000bc51 100644 --- a/packages/neuron-wallet/src/services/transaction-sender.ts +++ b/packages/neuron-wallet/src/services/transaction-sender.ts @@ -562,7 +562,7 @@ export default class TransactionSender { walletID: '', targetOutputs, fee: '0', - feeRate: '1000', + feeRate: '2000', multisigConfig, }) @@ -592,7 +592,7 @@ export default class TransactionSender { targetOutputs, changeAddress: multisigAddresses, fee: '0', - feeRate: '1000', + feeRate: '2000', lockClass: { lockArgs: [lockScript.args], codeHash: lockScript.codeHash, diff --git a/packages/neuron-wallet/tests/block-sync-renderer/light-synchronizer.test.ts b/packages/neuron-wallet/tests/block-sync-renderer/light-synchronizer.test.ts index 7221546503..086dec372e 100644 --- a/packages/neuron-wallet/tests/block-sync-renderer/light-synchronizer.test.ts +++ b/packages/neuron-wallet/tests/block-sync-renderer/light-synchronizer.test.ts @@ -13,6 +13,7 @@ const removeByHashesAndAddressType = jest.fn() const getOtherTypeSyncProgressMock = jest.fn() const getOtherTypeSyncBlockNumberMock = jest.fn() const getExistingSyncArgsesMock = jest.fn() +const getExistingSyncScriptsMock = jest.fn() const setScriptsMock = jest.fn() const getScriptsMock = jest.fn() @@ -66,6 +67,7 @@ jest.mock('../../src/services/sync-progress', () => { static getOtherTypeSyncProgress: any = () => getOtherTypeSyncProgressMock() static getOtherTypeSyncBlockNumber: any = () => getOtherTypeSyncBlockNumberMock() static getExistingSyncArgses: any = () => getExistingSyncArgsesMock() + static getExistingSyncScripts: any = () => getExistingSyncScriptsMock() } }) diff --git a/packages/neuron-wallet/tests/services/tx/transaction-sender.test.ts b/packages/neuron-wallet/tests/services/tx/transaction-sender.test.ts index 4c8687b604..457398ed6e 100644 --- a/packages/neuron-wallet/tests/services/tx/transaction-sender.test.ts +++ b/packages/neuron-wallet/tests/services/tx/transaction-sender.test.ts @@ -699,7 +699,7 @@ describe('TransactionSender Test', () => { walletID: '', targetOutputs, fee: '0', - feeRate: '1000', + feeRate: '2000', multisigConfig, }) }) From 8b8cbde5ed81f0555ca21f4ecad97ef6926f3164 Mon Sep 17 00:00:00 2001 From: chenyan Date: Sat, 24 May 2025 01:06:29 +0800 Subject: [PATCH 21/22] fix --- packages/neuron-ui/src/locales/zh.json | 1 + packages/neuron-wallet/src/services/cells.ts | 5 +- .../neuron-wallet/src/services/multisig.ts | 100 +++++++++--------- 3 files changed, 55 insertions(+), 51 deletions(-) diff --git a/packages/neuron-ui/src/locales/zh.json b/packages/neuron-ui/src/locales/zh.json index 04f08bcd2d..1b9a620607 100644 --- a/packages/neuron-ui/src/locales/zh.json +++ b/packages/neuron-ui/src/locales/zh.json @@ -1186,6 +1186,7 @@ "cancel": "取消", "export": "导出交易" }, + "nervos-dao": "Nervos DAO", "multisig-script-update-notice": "由于<0>多签脚本升级,我们建议重新生成多签地址,并将资产从旧地址转移,以确保正常使用。", "regenerate-dialog": { "title": "重新生成", diff --git a/packages/neuron-wallet/src/services/cells.ts b/packages/neuron-wallet/src/services/cells.ts index 67c7eebe0b..cdfa8a39d1 100644 --- a/packages/neuron-wallet/src/services/cells.ts +++ b/packages/neuron-wallet/src/services/cells.ts @@ -1328,13 +1328,14 @@ export default class CellsService { CAST(SUM(CAST(multisig_output.capacity AS UNSIGNED BIG INT)) AS VARCHAR) as balance, lockArgs, lockCodeHash, - lockHashType + lockHashType, + lockHash from multisig_output where multisig_output.lockHash in (:...lockHashes) AND status in (:...statuses) - group by multisig_output.lockArgs + group by multisig_output.lockHash `, { lockHashes, diff --git a/packages/neuron-wallet/src/services/multisig.ts b/packages/neuron-wallet/src/services/multisig.ts index bc17832347..0396abef0b 100644 --- a/packages/neuron-wallet/src/services/multisig.ts +++ b/packages/neuron-wallet/src/services/multisig.ts @@ -79,7 +79,24 @@ export default class MultisigService { .createQueryBuilder() .orderBy('id', 'DESC') .getMany() - return result + const existMultisigLockHash: Set = new Set() + const uniqueMultisigConfigs: MultisigConfig[] = [] + for (const multisigConfig of result) { + const multisigLockHash = Multisig.getMultisigScript( + multisigConfig.blake160s, + multisigConfig.r, + multisigConfig.m, + multisigConfig.n, + multisigConfig.lockCodeHash + ).computeHash() + if (existMultisigLockHash.has(multisigLockHash)) { + await this.deleteConfig(multisigConfig.id) + } else { + existMultisigLockHash.add(multisigLockHash) + uniqueMultisigConfigs.push(multisigConfig) + } + } + return uniqueMultisigConfigs } async getMultisigConfigById(id: number) { @@ -141,7 +158,7 @@ export default class MultisigService { }, 'desc', '0x64', - addressCursorMap.get(script.args), + addressCursorMap.get(scriptToHash(script)), ], } }) @@ -151,7 +168,7 @@ export default class MultisigService { if (!v.error && v?.result?.objects?.length) { const config = currentMultisigConfigs[idx] const script = Multisig.getMultisigScript(config.blake160s, config.r, config.m, config.n, config.lockCodeHash) - addressCursorMap.set(script.args, v?.result?.last_cursor) + addressCursorMap.set(scriptToHash(script), v?.result?.last_cursor) cells.push(...v.result.objects) nextMultisigConfigs.push(currentMultisigConfigs[idx]) } @@ -176,54 +193,39 @@ export default class MultisigService { } static async saveMultisigDaoTx(multisigConfigs: MultisigConfig[]) { - const cells = await MultisigService.getCells(multisigConfigs) - if (cells.length) { - const daoTxHash = new Set() - cells.forEach(cell => { - if (cell.output?.type?.code_hash === SystemScriptInfo.DAO_CODE_HASH) { - daoTxHash.add(cell.out_point.tx_hash) - } - }) - - const network = NetworksService.getInstance().getCurrent() - const rpcService = new RpcService(network.remote, network.type) - - const getTx = async (txHash: string) => { - const txWithStatus: TransactionWithStatus | undefined | { transaction: null; txStatus: TxStatus } = - await rpcService.getTransaction(txHash) - if (txWithStatus?.transaction) { - const tx = Transaction.fromSDK(txWithStatus.transaction) - tx.blockHash = txWithStatus.txStatus.blockHash || undefined - if (tx.blockHash) { - const header = await rpcService.getHeader(tx.blockHash) - tx.timestamp = header?.timestamp - tx.blockNumber = header?.number - } - return tx + const network = NetworksService.getInstance().getCurrent() + const rpcService = new RpcService(network.remote, network.type) + const getTx = async (txHash: string) => { + const txWithStatus: TransactionWithStatus | undefined | { transaction: null; txStatus: TxStatus } = + await rpcService.getTransaction(txHash) + if (txWithStatus?.transaction) { + const tx = Transaction.fromSDK(txWithStatus.transaction) + tx.blockHash = txWithStatus.txStatus.blockHash || undefined + if (tx.blockHash) { + const header = await rpcService.getHeader(tx.blockHash) + tx.timestamp = header?.timestamp + tx.blockNumber = header?.number } + return tx } - - if (daoTxHash.size > 0) { - for (const txHash of daoTxHash) { - const tx = await getTx(txHash) - if (tx) { - const previousTxHashes: string[] = [] - tx.outputs.forEach((output, index) => { - if (output.type?.codeHash === SystemScriptInfo.DAO_CODE_HASH) { - output.daoData = tx.outputsData[index] - if (tx.outputsData[index] !== DAO_DATA) { - const previousTxHash = tx.inputs[index].previousOutput!.txHash - previousTxHashes.push(previousTxHash) - output.setDepositOutPoint(new OutPoint(previousTxHash, tx.inputs[index].previousOutput!.index)) - } + } + const multisigTxHashList = await MultisigService.getMultisigTransactionHashList(multisigConfigs) + for (const txHash of [...multisigTxHashList].reverse()) { + const tx = await getTx(txHash) + if (tx) { + if (tx.inputs.some(input => input.since && +input.since > 0)) { + await TransactionPersistor.saveFetchTx(tx) + } else if (tx.outputs.some(output => output.type?.codeHash === SystemScriptInfo.DAO_CODE_HASH)) { + tx.outputs.forEach((output, index) => { + if (output.type?.codeHash === SystemScriptInfo.DAO_CODE_HASH) { + output.daoData = tx.outputsData[index] + if (tx.outputsData[index] !== DAO_DATA) { + const previousTxHash = tx.inputs[index].previousOutput!.txHash + output.setDepositOutPoint(new OutPoint(previousTxHash, tx.inputs[index].previousOutput!.index)) } - }) - for (const previousTxHash of previousTxHashes) { - const previousTx = await getTx(previousTxHash) - if (previousTx) await TransactionPersistor.saveFetchTx(previousTx) } - await TransactionPersistor.saveFetchTx(tx) - } + }) + await TransactionPersistor.saveFetchTx(tx) } } } @@ -255,7 +257,7 @@ export default class MultisigService { }, 'desc', '0x64', - addressCursorMap.get(script.args), + addressCursorMap.get(scriptToHash(script)), ], } }) @@ -265,7 +267,7 @@ export default class MultisigService { if (!v.error && v?.result?.objects?.length) { const config = currentMultisigConfigs[idx] const script = Multisig.getMultisigScript(config.blake160s, config.r, config.m, config.n, config.lockCodeHash) - addressCursorMap.set(script.args, v?.result?.last_cursor) + addressCursorMap.set(scriptToHash(script), v?.result?.last_cursor) v.result.objects.forEach((obj: any) => { multisigOutputTxHashList.add(obj.tx_hash || obj.transaction?.hash) }) From 7ad89ecc379af8aa2f8d5e56b32a0034d987ed6a Mon Sep 17 00:00:00 2001 From: chenyan Date: Mon, 26 May 2025 20:38:16 +0800 Subject: [PATCH 22/22] fix --- packages/neuron-ui/src/components/ScriptTag/index.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/neuron-ui/src/components/ScriptTag/index.tsx b/packages/neuron-ui/src/components/ScriptTag/index.tsx index 29efe9bc40..52661dfaaa 100644 --- a/packages/neuron-ui/src/components/ScriptTag/index.tsx +++ b/packages/neuron-ui/src/components/ScriptTag/index.tsx @@ -68,6 +68,7 @@ const ScriptTag = ({
} showTriangle + isTriggerNextToChild >