diff --git a/packages/connect-explorer/src/components/Menu.tsx b/packages/connect-explorer/src/components/Menu.tsx index 3b103ba76e3..49d77f2af41 100644 --- a/packages/connect-explorer/src/components/Menu.tsx +++ b/packages/connect-explorer/src/components/Menu.tsx @@ -2,6 +2,7 @@ import React, { useEffect, useState } from 'react'; import { withRouter } from 'react-router-dom'; import InfinityMenu from 'react-infinity-menu'; import { createGlobalStyle } from 'styled-components'; +import { deepClone } from '@trezor/utils'; import config from '../data/menu'; @@ -139,7 +140,7 @@ const findFiltered = (url: any, tree: any, node: any, key: any) => { const getTree = (url: string) => { // clone config - const tree = JSON.parse(JSON.stringify(config)); + const tree = deepClone(config); const filteredTree = tree.reduce((prev, curr, key) => { if (key === undefined) return prev; return findFiltered(url, prev, curr, key); diff --git a/packages/connect-explorer/src/reducers/methodReducer.ts b/packages/connect-explorer/src/reducers/methodReducer.ts index 9d684a492c9..7e5f6f998cb 100644 --- a/packages/connect-explorer/src/reducers/methodReducer.ts +++ b/packages/connect-explorer/src/reducers/methodReducer.ts @@ -1,6 +1,7 @@ /* eslint-disable no-eval */ import stringifyObject from 'stringify-object'; +import { deepClone } from '@trezor/utils'; import { TAB_CHANGE, @@ -182,7 +183,7 @@ const findField = (state: MethodState, field: any) => { const onFieldChange = (state: MethodState, _field: any, value: any) => { const newState = { - ...JSON.parse(JSON.stringify(state)), + ...deepClone(state), ...state, }; const field = findField(newState, _field); @@ -206,10 +207,7 @@ const getMethodState = (url: string) => { const method = config.find(m => m.url === url); if (!method) return initialState; // clone object - const state = { - ...JSON.parse(JSON.stringify(method)), - // ...method, - }; + const state = deepClone(method) as MethodState; // set default values state.fields = state.fields.map(f => setAffectedValues(state, prepareBundle(f))); @@ -219,9 +217,9 @@ const getMethodState = (url: string) => { }; const onAddBatch = (state: MethodState, _field: Field, item: any) => { - const newState = JSON.parse(JSON.stringify(state)); + const newState = deepClone(state) as any; const field = newState.fields.find(f => f.name === _field.name); - field.items = [...field.items, item]; + field.items = [...field?.items, item]; prepareBundle(field); return updateParams(newState); @@ -231,8 +229,8 @@ const onRemoveBatch = (state: MethodState, _field: any, _batch: any) => { const field = state.fields.find(f => f.name === _field.name); const items = field?.items?.filter(batch => batch !== _batch); - const newState = JSON.parse(JSON.stringify(state)); - const newField = newState.fields.find(f => f.name === field?.name); + const newState = deepClone(state); + const newField = newState.fields.find(f => f.name === field?.name) as any; newField.items = items; prepareBundle(newField); diff --git a/packages/connect/src/api/common/paramsValidator.ts b/packages/connect/src/api/common/paramsValidator.ts index e1f96bd0a01..75419064afe 100644 --- a/packages/connect/src/api/common/paramsValidator.ts +++ b/packages/connect/src/api/common/paramsValidator.ts @@ -1,4 +1,5 @@ // origin: https://github.com/trezor/connect/blob/develop/src/js/core/methods/helpers/paramsValidator.js +import { deepClone } from '@trezor/utils'; import { ERRORS } from '../../constants'; import { fromHardened } from '../../utils/pathUtils'; @@ -102,7 +103,7 @@ export const getFirmwareRange = ( coinInfo: CoinInfo | null | undefined, currentRange: FirmwareRange, ) => { - const current: FirmwareRange = JSON.parse(JSON.stringify(currentRange)); + const current: FirmwareRange = deepClone(currentRange); // set minimum required firmware from coins.json (coinInfo) if (coinInfo) { if (!coinInfo.support || typeof coinInfo.support.trezor1 !== 'string') { diff --git a/packages/connect/src/api/customMessage.ts b/packages/connect/src/api/customMessage.ts index 0597f09e7cd..ccf378342d0 100644 --- a/packages/connect/src/api/customMessage.ts +++ b/packages/connect/src/api/customMessage.ts @@ -1,5 +1,6 @@ // origin: https://github.com/trezor/connect/blob/develop/src/js/core/methods/CustomMessage.js +import { deepClone } from '@trezor/utils'; import { AbstractMethod } from '../core/AbstractMethod'; import { validateParams } from './common/paramsValidator'; import { ERRORS } from '../constants'; @@ -26,7 +27,7 @@ export default class CustomMessage extends AbstractMethod<'customMessage', Param if (payload.messages) { try { - JSON.parse(JSON.stringify(payload.messages)); + deepClone(payload.messages); } catch (error) { throw ERRORS.TypedError( 'Method_InvalidParameter', diff --git a/packages/suite/src/actions/suite/metadataActions.ts b/packages/suite/src/actions/suite/metadataActions.ts index 6e9fc09669b..88608ee44fc 100644 --- a/packages/suite/src/actions/suite/metadataActions.ts +++ b/packages/suite/src/actions/suite/metadataActions.ts @@ -1,7 +1,7 @@ import TrezorConnect from '@trezor/connect'; import { analytics, EventType } from '@trezor/suite-analytics'; -import { createDeferred } from '@trezor/utils'; +import { createDeferred, deepClone } from '@trezor/utils'; import { METADATA } from '@suite-actions/constants'; import { Dispatch, GetState } from '@suite-types'; import { @@ -518,7 +518,7 @@ export const addAccountMetadata = const account = getState().wallet.accounts.find(a => a.key === payload.accountKey); if (!account) return false; // clone Account.metadata - const metadata = JSON.parse(JSON.stringify(account.metadata)); + const metadata = deepClone(account.metadata); if (payload.type === 'outputLabel') { if (typeof payload.value !== 'string' || payload.value.length === 0) { diff --git a/packages/suite/src/actions/wallet/formDraftActions.ts b/packages/suite/src/actions/wallet/formDraftActions.ts index 0cca37e4261..e53c81e18b0 100644 --- a/packages/suite/src/actions/wallet/formDraftActions.ts +++ b/packages/suite/src/actions/wallet/formDraftActions.ts @@ -3,6 +3,7 @@ import { getFormDraftKey } from '@suite-common/wallet-utils'; import { FORM_DRAFT } from './constants'; import type { FormDraftKeyPrefix, FormDraft } from '@wallet-types/form'; +import { deepClone } from '@trezor/utils'; export type FormDraftAction = | { @@ -36,7 +37,7 @@ export const getDraft = if (draft) { // draft is a read-only redux object. make a copy to be able to modify values - return JSON.parse(JSON.stringify(draft)) as T; + return deepClone(draft) as T; } }; diff --git a/packages/suite/src/actions/wallet/sendFormActions.ts b/packages/suite/src/actions/wallet/sendFormActions.ts index 69b4451aaeb..23652b92677 100644 --- a/packages/suite/src/actions/wallet/sendFormActions.ts +++ b/packages/suite/src/actions/wallet/sendFormActions.ts @@ -84,7 +84,7 @@ export const getDraft = () => (_dispatch: Dispatch, getState: GetState) => { const draft = send.drafts[selectedAccount.account.key]; if (draft) { // draft is a read-only redux object. make a copy to be able to modify values - return JSON.parse(JSON.stringify(draft)); + return deepClone(draft); } }; diff --git a/packages/utxo-lib/tests/compose.test.ts b/packages/utxo-lib/tests/compose.test.ts index a8dd9c071ed..9ce8908cbb7 100644 --- a/packages/utxo-lib/tests/compose.test.ts +++ b/packages/utxo-lib/tests/compose.test.ts @@ -1,3 +1,4 @@ +import { deepClone } from '@trezor/utils'; import { composeTx } from '../src'; import * as utils from '../src/compose/utils'; import { Permutation } from '../src/compose/permutation'; @@ -26,7 +27,7 @@ describe('composeTx', () => { delete input.REV_hash; }); const o = result.transaction.PERM_outputs; - const sorted = JSON.parse(JSON.stringify(o.sorted)); + const sorted = deepClone(o.sorted); sorted.forEach((ss: any) => { const s = ss; if (s.opReturnData != null) {