From 77d99fb1ec9304aba7e57608f5e90ef4f7835de4 Mon Sep 17 00:00:00 2001 From: devchenyan Date: Fri, 7 Jun 2024 16:26:11 +0800 Subject: [PATCH] feat: compatible ckb-cli tx multisig file (#3169) --- packages/neuron-ui/src/utils/calculateFee.ts | 4 +- .../neuron-wallet/src/controllers/multisig.ts | 10 +- .../src/models/chain/cell-dep.ts | 5 +- .../src/models/chain/transaction.ts | 2 +- .../neuron-wallet/src/models/offline-sign.ts | 1 + .../src/utils/deep-camelize-keys.ts | 20 ++++ packages/neuron-wallet/src/utils/multisig.ts | 25 +++++ .../utils/deep-camelize-keys/fixtures.json | 101 ++++++++++++++++++ .../utils/deep-camelize-keys/index.test.ts | 9 ++ 9 files changed, 170 insertions(+), 7 deletions(-) create mode 100644 packages/neuron-wallet/src/utils/deep-camelize-keys.ts create mode 100644 packages/neuron-wallet/tests/utils/deep-camelize-keys/fixtures.json create mode 100644 packages/neuron-wallet/tests/utils/deep-camelize-keys/index.test.ts diff --git a/packages/neuron-ui/src/utils/calculateFee.ts b/packages/neuron-ui/src/utils/calculateFee.ts index 2c7103f747..358709730c 100644 --- a/packages/neuron-ui/src/utils/calculateFee.ts +++ b/packages/neuron-ui/src/utils/calculateFee.ts @@ -3,11 +3,11 @@ export const calculateFee = (tx: any) => { return '0' } const inputCapacities = tx.inputs.reduce( - (result: bigint, input: { capacity: string }) => result + BigInt(input.capacity), + (result: bigint, input: { capacity: string }) => result + BigInt(input.capacity || '0'), BigInt(0) ) const outputCapacities = tx.outputs.reduce( - (result: bigint, output: { capacity: string }) => result + BigInt(output.capacity), + (result: bigint, output: { capacity: string }) => result + BigInt(output.capacity || '0'), BigInt(0) ) diff --git a/packages/neuron-wallet/src/controllers/multisig.ts b/packages/neuron-wallet/src/controllers/multisig.ts index 9ba5bc5ac3..ad5912cc84 100644 --- a/packages/neuron-wallet/src/controllers/multisig.ts +++ b/packages/neuron-wallet/src/controllers/multisig.ts @@ -5,6 +5,7 @@ import { t } from 'i18next' import { computeScriptHash as scriptToHash } from '@ckb-lumos/base/lib/utils' import { scriptToAddress, addressToScript } from '../utils/scriptAndAddress' import { ResponseCode } from '../utils/const' +import { parseMultisigTxJsonFromCkbCli } from '../utils/multisig' import MultisigConfig from '../database/chain/entities/multisig-config' import MultisigConfigModel from '../models/multisig-config' import MultisigService from '../services/multisig' @@ -246,7 +247,12 @@ export default class MultisigController { } const tx = result.json const lockHash = scriptToHash(addressToScript(fullPayload)) - if (tx.transaction.inputs.every(v => v.lockHash !== lockHash)) { + + if (tx.transaction) { + tx.transaction = parseMultisigTxJsonFromCkbCli(tx) + } + + if (tx.transaction.inputs.every(v => v.lockHash && v.lockHash !== lockHash)) { ShowGlobalDialogSubject.next({ type: 'failed', title: t('common.error'), @@ -258,7 +264,7 @@ export default class MultisigController { } return { status: ResponseCode.Success, - result: result?.json, + result: tx, } } } diff --git a/packages/neuron-wallet/src/models/chain/cell-dep.ts b/packages/neuron-wallet/src/models/chain/cell-dep.ts index c8830809ed..cb1979a065 100644 --- a/packages/neuron-wallet/src/models/chain/cell-dep.ts +++ b/packages/neuron-wallet/src/models/chain/cell-dep.ts @@ -1,4 +1,5 @@ import OutPoint from './out-point' +import { snakeToCamel } from '../../utils/deep-camelize-keys' export enum DepType { Code = 'code', @@ -14,8 +15,8 @@ export default class CellDep { this.depType = depType } - public static fromObject({ outPoint, depType }: { outPoint: OutPoint; depType: DepType }): CellDep { - return new CellDep(OutPoint.fromObject(outPoint), depType) + public static fromObject({ outPoint, depType }: { outPoint: OutPoint; depType: DepType | 'dep_group' }): CellDep { + return new CellDep(OutPoint.fromObject(outPoint), snakeToCamel(depType) as DepType) } public toSDK(): CKBComponents.CellDep { diff --git a/packages/neuron-wallet/src/models/chain/transaction.ts b/packages/neuron-wallet/src/models/chain/transaction.ts index a8942bb43c..613143dbb4 100644 --- a/packages/neuron-wallet/src/models/chain/transaction.ts +++ b/packages/neuron-wallet/src/models/chain/transaction.ts @@ -123,7 +123,7 @@ export default class Transaction { this.description = description this.nervosDao = nervosDao this.assetAccountType = assetAccountType - this.version = BigInt(version).toString() + this.version = BigInt(version || '0x0').toString() this.value = value ? BigInt(value).toString() : value this.fee = fee ? BigInt(fee).toString() : fee this.interest = interest ? BigInt(interest).toString() : interest diff --git a/packages/neuron-wallet/src/models/offline-sign.ts b/packages/neuron-wallet/src/models/offline-sign.ts index 2fd73a0d93..934f5d3ad9 100644 --- a/packages/neuron-wallet/src/models/offline-sign.ts +++ b/packages/neuron-wallet/src/models/offline-sign.ts @@ -44,6 +44,7 @@ export interface OfflineSignJSON { description?: string asset_account?: AssetAccount multisig_configs?: MultisigConfigs + signatures?: Signatures } export default class OfflineSign implements OfflineSignProps { diff --git a/packages/neuron-wallet/src/utils/deep-camelize-keys.ts b/packages/neuron-wallet/src/utils/deep-camelize-keys.ts new file mode 100644 index 0000000000..94343db948 --- /dev/null +++ b/packages/neuron-wallet/src/utils/deep-camelize-keys.ts @@ -0,0 +1,20 @@ +export const snakeToCamel = (str: string): string => { + if (!str) return '' + return str.replace(/([-_][a-z])/gi, c => c.toUpperCase().replace(/[-_]/g, '')) +} + +export const deepCamelizeKeys = (item: unknown): unknown => { + if (Array.isArray(item)) { + return item.map((el: unknown) => deepCamelizeKeys(el)) + } else if (typeof item === 'function' || item !== Object(item)) { + return item + } + return Object.fromEntries( + Object.entries(item as Record).map(([key, value]: [string, unknown]) => [ + key.replace(/([-_][a-z])/gi, c => c.toUpperCase().replace(/[-_]/g, '')), + deepCamelizeKeys(value), + ]) + ) +} + +export default { deepCamelizeKeys, snakeToCamel } diff --git a/packages/neuron-wallet/src/utils/multisig.ts b/packages/neuron-wallet/src/utils/multisig.ts index 4ef4e7c058..5caef9529a 100644 --- a/packages/neuron-wallet/src/utils/multisig.ts +++ b/packages/neuron-wallet/src/utils/multisig.ts @@ -2,6 +2,11 @@ import { computeScriptHash as scriptToHash } from '@ckb-lumos/base/lib/utils' import Multisig from '../models/multisig' import MultisigConfigModel from '../models/multisig-config' import { Signatures, SignStatus } from '../models/offline-sign' +import { OfflineSignJSON } from 'src/models/offline-sign' +import Transaction from '../models/chain/transaction' +import SystemScriptInfo from '../models/system-script-info' +import Input from '../models/chain/input' +import { deepCamelizeKeys } from './deep-camelize-keys' export const getMultisigStatus = (multisigConfig: MultisigConfigModel, signatures: Signatures) => { const multisigLockHash = scriptToHash( @@ -27,6 +32,26 @@ export const getMultisigStatus = (multisigConfig: MultisigConfigModel, signature return SignStatus.Signed } +export const parseMultisigTxJsonFromCkbCli = (tx: OfflineSignJSON): Transaction => { + const { multisig_configs, transaction } = tx + 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) + + txObj.inputs.forEach((input: Input) => { + if (!input?.lock) { + input.lock = lock + } + }) + } + if (!txObj?.signatures && tx?.signatures) { + txObj.signatures = tx.signatures + } + return txObj +} + export default { getMultisigStatus, + parseMultisigTxJsonFromCkbCli, } diff --git a/packages/neuron-wallet/tests/utils/deep-camelize-keys/fixtures.json b/packages/neuron-wallet/tests/utils/deep-camelize-keys/fixtures.json new file mode 100644 index 0000000000..1919e853f7 --- /dev/null +++ b/packages/neuron-wallet/tests/utils/deep-camelize-keys/fixtures.json @@ -0,0 +1,101 @@ +{ + "value": { + "version": "0x0", + "cell_deps": [ + { + "out_point": { + "tx_hash": "0xf8de3bb47d055cdf460d93a2a6e1b05f7432f9777c8c474abf4eec1d4aee5d37", + "index": "0x1" + }, + "dep_type": "dep_group" + } + ], + "header_deps": [], + "inputs": [ + { + "since": "0x0", + "previous_output": { + "tx_hash": "0x00bc60fd23dd556a9bd139791e4cb95d678550054953793b79c864abcc733eae", + "index": "0x1" + } + }, + { + "previous_output": { + "txHash": "0x8b37ded770fe457e1d3969e93722873308aef1e6810757d248f437de4d009d8f", + "index": "0x1" + }, + "since": "0x0" + } + ], + "outputs": [ + { + "capacity": "6600000000", + "lock": { + "args": "0x2d8765d2d0e007aa227e61d9bc1ebd7343360a58", + "code_hash": "0x9bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce8", + "hash_type": "type" + }, + "type": null + }, + { + "capacity": "10399999375", + "lock": { + "args": "0x5b34cc4f76644ec6da564c1ace41711ee0f1e071", + "code_hash": "0x5c5069eb0857efc65e1bca0c07df34c31663b3622fd3876c876320fc9634e2a8", + "hash_type": "type" + }, + "type": null + } + ], + "outputs_data": ["0x", "0x"], + "witnesses": [] + }, + "expected": { + "version": "0x0", + "inputs": [ + { + "since": "0x0", + "previousOutput": { + "index": "0x1", + "txHash": "0x00bc60fd23dd556a9bd139791e4cb95d678550054953793b79c864abcc733eae" + } + }, + { + "since": "0x0", + "previousOutput": { + "txHash": "0x8b37ded770fe457e1d3969e93722873308aef1e6810757d248f437de4d009d8f", + "index": "0x1" + } + } + ], + "outputs": [ + { + "capacity": "6600000000", + "lock": { + "args": "0x2d8765d2d0e007aa227e61d9bc1ebd7343360a58", + "codeHash": "0x9bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce8", + "hashType": "type" + }, + "type": null + }, + { + "capacity": "10399999375", + "lock": { + "args": "0x5b34cc4f76644ec6da564c1ace41711ee0f1e071", + "codeHash": "0x5c5069eb0857efc65e1bca0c07df34c31663b3622fd3876c876320fc9634e2a8", + "hashType": "type" + }, + "type": null + } + ], + "witnesses": [], + "cellDeps": [ + { + "outPoint": { "index": "0x1", "txHash": "0xf8de3bb47d055cdf460d93a2a6e1b05f7432f9777c8c474abf4eec1d4aee5d37" }, + "depType": "dep_group" + } + ], + "headerDeps": [], + "outputsData": ["0x", "0x"] + } +} diff --git a/packages/neuron-wallet/tests/utils/deep-camelize-keys/index.test.ts b/packages/neuron-wallet/tests/utils/deep-camelize-keys/index.test.ts new file mode 100644 index 0000000000..2545c058e7 --- /dev/null +++ b/packages/neuron-wallet/tests/utils/deep-camelize-keys/index.test.ts @@ -0,0 +1,9 @@ +import { deepCamelizeKeys } from '../../../src/utils/deep-camelize-keys' +import fixtures from './fixtures.json' + +describe('test json to hump', () => { + it('json to hump', () => { + const result = deepCamelizeKeys(fixtures.value) + expect(fixtures.expected).toEqual(result) + }) +})