diff --git a/packages/connect/src/data/coinInfo.ts b/packages/connect/src/data/coinInfo.ts index 7085ab028cc..96eeaf8ea20 100644 --- a/packages/connect/src/data/coinInfo.ts +++ b/packages/connect/src/data/coinInfo.ts @@ -1,5 +1,6 @@ // origin: https://github.com/trezor/connect/blob/develop/src/js/data/CoinInfo.js +import { deepClone } from '@trezor/utils'; import { ERRORS } from '../constants'; import { toHardened, fromHardened } from '../utils/pathUtils'; import type { @@ -14,7 +15,7 @@ const ethereumNetworks: EthereumNetworkInfo[] = []; const miscNetworks: MiscNetworkInfo[] = []; export const getBitcoinNetwork = (pathOrName: number[] | string) => { - const networks: BitcoinNetworkInfo[] = structuredClone(bitcoinNetworks); + const networks: BitcoinNetworkInfo[] = deepClone(bitcoinNetworks); if (typeof pathOrName === 'string') { const name = pathOrName.toLowerCase(); return networks.find( @@ -29,7 +30,7 @@ export const getBitcoinNetwork = (pathOrName: number[] | string) => { }; export const getEthereumNetwork = (pathOrName: number[] | string) => { - const networks: EthereumNetworkInfo[] = structuredClone(ethereumNetworks); + const networks: EthereumNetworkInfo[] = deepClone(ethereumNetworks); if (typeof pathOrName === 'string') { const name = pathOrName.toLowerCase(); return networks.find( @@ -41,7 +42,7 @@ export const getEthereumNetwork = (pathOrName: number[] | string) => { }; export const getMiscNetwork = (pathOrName: number[] | string) => { - const networks: MiscNetworkInfo[] = structuredClone(miscNetworks); + const networks: MiscNetworkInfo[] = deepClone(miscNetworks); if (typeof pathOrName === 'string') { const name = pathOrName.toLowerCase(); return networks.find( @@ -84,7 +85,7 @@ export const getBech32Network = (coin: BitcoinNetworkInfo) => { // fix coinInfo network values from path (segwit/legacy) export const fixCoinInfoNetwork = (ci: BitcoinNetworkInfo, path: number[]) => { - const coinInfo: BitcoinNetworkInfo = structuredClone(ci); + const coinInfo: BitcoinNetworkInfo = deepClone(ci); if (path[0] === toHardened(84)) { const bech32Network = getBech32Network(coinInfo); if (bech32Network) { @@ -120,7 +121,7 @@ const detectBtcVersion = (data: { subversion?: string }) => { // TODO: https://github.com/trezor/trezor-suite/issues/4886 export const getCoinInfoByHash = (hash: string, networkInfo: any) => { - const networks: BitcoinNetworkInfo[] = structuredClone(bitcoinNetworks); + const networks: BitcoinNetworkInfo[] = deepClone(bitcoinNetworks); const result = networks.find( info => hash.toLowerCase() === info.hashGenesisBlock.toLowerCase(), ); diff --git a/packages/suite/src/actions/wallet/sendFormActions.ts b/packages/suite/src/actions/wallet/sendFormActions.ts index 6eb248208cc..69b4451aaeb 100644 --- a/packages/suite/src/actions/wallet/sendFormActions.ts +++ b/packages/suite/src/actions/wallet/sendFormActions.ts @@ -1,5 +1,7 @@ import TrezorConnect, { PROTO } from '@trezor/connect'; import BigNumber from 'bignumber.js'; +import { deepClone } from '@trezor/utils'; + import * as accountActions from '@wallet-actions/accountActions'; import * as blockchainActions from '@wallet-actions/blockchainActions'; import * as transactionActions from '@wallet-actions/transactionActions'; @@ -133,7 +135,7 @@ export const convertDrafts = () => (dispatch: Dispatch, getState: GetState) => { const conversionToUse = areSatsSelected && areSatsSupported ? amountToSatoshi : formatAmount; - const updatedDraft: FormState = structuredClone(draft); + const updatedDraft: FormState = deepClone(draft); const decimals = getAccountDecimals(relatedAccount.symbol)!; updatedDraft.outputs.forEach(output => { diff --git a/packages/utils/src/deepClone.ts b/packages/utils/src/deepClone.ts new file mode 100644 index 00000000000..c7a6e3d1476 --- /dev/null +++ b/packages/utils/src/deepClone.ts @@ -0,0 +1,15 @@ +export const deepClone = (value: T): T => { + if (value === undefined) { + return value; + } + + let clone; + + if (typeof structuredClone === 'function') { + clone = structuredClone(value); + } else { + clone = JSON.parse(JSON.stringify(value)); + } + + return clone; +}; diff --git a/packages/utils/src/index.ts b/packages/utils/src/index.ts index 14017554c9c..6bbb19b96cf 100644 --- a/packages/utils/src/index.ts +++ b/packages/utils/src/index.ts @@ -21,3 +21,4 @@ export * from './throwError'; export * from './isHex'; export * from './resolveStaticPath'; export * from './createTimeoutPromise'; +export * from './deepClone'; diff --git a/packages/utils/tests/deepClone.test.ts b/packages/utils/tests/deepClone.test.ts new file mode 100644 index 00000000000..2d9ad7ad729 --- /dev/null +++ b/packages/utils/tests/deepClone.test.ts @@ -0,0 +1,24 @@ +import { deepClone } from '../src/deepClone'; + +describe('deepClone', () => { + it('deepClone works', () => { + const original = { + a: 1, + b: '2', + c: { + a: 1, + b: '2', + c: { + a: [1, 2, 3], + b: '2', + }, + }, + }; + + const copy = deepClone(original); + const shallowCopy = { ...original }; + + expect(copy.c.c.a === original.c.c.a).toBeFalsy(); + expect(shallowCopy.c.c.a === original.c.c.a).toBeTruthy(); + }); +});