diff --git a/ci/test.yml b/ci/test.yml
index 19611299dff..5c8616bec0f 100644
--- a/ci/test.yml
+++ b/ci/test.yml
@@ -18,6 +18,7 @@
- docker login $CI_DEPENDENCY_PROXY_SERVER -u $CI_DEPENDENCY_PROXY_USER -p $CI_DEPENDENCY_PROXY_PASSWORD
script:
- yarn install --pure-lockfile --cache-folder .yarn --prefer-offline
+ - yarn workspace @trezor/utils build:lib
- docker-compose pull
- docker-compose up -d ${CONTAINERS}
- docker-compose run test-run
@@ -128,6 +129,7 @@ rollout:
COMPOSE_FILE: ./docker/docker-compose.transport-test.yml
script:
- yarn install --pure-lockfile --cache-folder .yarn --prefer-offline
+ - yarn workspace @trezor/utils build:lib
- yarn workspace @trezor/transport build:lib
- ./docker/docker-transport-test.sh
after_script:
@@ -148,6 +150,7 @@ transport nightly:
stage: integration testing
script:
- yarn install --frozen-lockfile --cache-folder .yarn --prefer-offline
+ - yarn workspace @trezor/utils build:lib
- yarn workspace @trezor/blockchain-link build:lib
- yarn workspace @trezor/blockchain-link build:workers
- yarn workspace @trezor/blockchain-link test:integration
diff --git a/packages/blockchain-link/package.json b/packages/blockchain-link/package.json
index bc9bb5c9be6..53bc58373e7 100644
--- a/packages/blockchain-link/package.json
+++ b/packages/blockchain-link/package.json
@@ -49,6 +49,7 @@
"worker-loader": "^3.0.8"
},
"dependencies": {
+ "@trezor/utils": "1.0.0",
"bignumber.js": "^9.0.1",
"events": "^3.3.0",
"ripple-lib": "1.10.0",
diff --git a/packages/blockchain-link/src/index.ts b/packages/blockchain-link/src/index.ts
index bb3d0c063c1..5f92d5265bb 100644
--- a/packages/blockchain-link/src/index.ts
+++ b/packages/blockchain-link/src/index.ts
@@ -1,7 +1,7 @@
import { EventEmitter } from 'events';
+import { createDeferred, Deferred } from '@trezor/utils/lib/createDeferred';
import { CustomError } from './constants/errors';
import { MESSAGES, RESPONSES } from './constants';
-import { create as createDeferred, Deferred } from './utils/deferred';
import type { BlockchainSettings } from './types';
import type * as ResponseTypes from './types/responses';
import type * as MessageTypes from './types/messages';
diff --git a/packages/blockchain-link/src/workers/blockbook/websocket.ts b/packages/blockchain-link/src/workers/blockbook/websocket.ts
index 21d1664ade8..25e522f1246 100644
--- a/packages/blockchain-link/src/workers/blockbook/websocket.ts
+++ b/packages/blockchain-link/src/workers/blockbook/websocket.ts
@@ -1,7 +1,8 @@
import * as WebSocket from 'ws';
import { EventEmitter } from 'events';
+import { createDeferred, Deferred } from '@trezor/utils/lib/createDeferred';
+
import { CustomError } from '../../constants/errors';
-import { create as createDeferred, Deferred } from '../../utils/deferred';
import type {
BlockNotification,
AddressNotification,
diff --git a/packages/blockchain-link/src/workers/blockfrost/websocket.ts b/packages/blockchain-link/src/workers/blockfrost/websocket.ts
index 4eade9d6978..c4ef44a99c4 100644
--- a/packages/blockchain-link/src/workers/blockfrost/websocket.ts
+++ b/packages/blockchain-link/src/workers/blockfrost/websocket.ts
@@ -1,7 +1,8 @@
import * as WebSocket from 'ws';
import { EventEmitter } from 'events';
+import { createDeferred, Deferred } from '@trezor/utils/lib/createDeferred';
+
import { CustomError } from '../../constants/errors';
-import { create as createDeferred, Deferred } from '../../utils/deferred';
import type { Send, BlockContent, BlockfrostTransaction } from '../../types/blockfrost';
import type {
AccountInfoParams,
diff --git a/packages/integration-tests/websocket-client.js b/packages/integration-tests/websocket-client.js
index 157851a321f..e3ec0886493 100644
--- a/packages/integration-tests/websocket-client.js
+++ b/packages/integration-tests/websocket-client.js
@@ -1,25 +1,9 @@
const WebSocket = require('ws');
const { EventEmitter } = require('events');
+const { createDeferred } = require('@trezor/utils');
const NOT_INITIALIZED = new Error('websocket_not_initialized');
-const createDeferred = id => {
- let localResolve = t => () => {};
- let localReject = e => () => {};
-
- const promise = new Promise((resolve, reject) => {
- localResolve = resolve;
- localReject = reject;
- });
-
- return {
- id,
- resolve: localResolve,
- reject: localReject,
- promise,
- };
-};
-
// Making the timeout high because the controller in trezor-user-env
// must synchronously run actions on emulator and they may take a long time
// (for example in case of Shamir backup)
diff --git a/packages/suite-desktop/package.json b/packages/suite-desktop/package.json
index b90cf06d588..9e5a465e34f 100644
--- a/packages/suite-desktop/package.json
+++ b/packages/suite-desktop/package.json
@@ -153,6 +153,7 @@
"afterSign": "scripts/notarize.js"
},
"dependencies": {
+ "@trezor/utils": "*",
"chalk": "^4.1.2",
"electron-localshortcut": "^3.2.1",
"electron-store": "^8.0.1",
diff --git a/packages/suite-desktop/src-electron/libs/info.ts b/packages/suite-desktop/src-electron/libs/info.ts
index abb7a1a9319..8832114b180 100644
--- a/packages/suite-desktop/src-electron/libs/info.ts
+++ b/packages/suite-desktop/src-electron/libs/info.ts
@@ -3,7 +3,7 @@ import si from 'systeminformation';
import { isDev } from '@suite-utils/build';
import { b2t } from '@desktop-electron/libs/utils';
-import { toHumanReadable } from '@suite-utils/file';
+import { bytesToHumanReadable } from '@trezor/utils';
export const buildInfo = () => {
global.logger.info('build', [
@@ -41,6 +41,6 @@ export const computerInfo = async () => {
`- Cores: ${cpu.processors}x${cpu.physicalCores}(+${cpu.cores - cpu.physicalCores}) @ ${
cpu.speed
}GHz`,
- `- RAM: ${toHumanReadable(mem.total)}`,
+ `- RAM: ${bytesToHumanReadable(mem.total)}`,
]);
};
diff --git a/packages/suite-desktop/src-electron/modules/auto-updater.ts b/packages/suite-desktop/src-electron/modules/auto-updater.ts
index 3cfcbfbed97..99832ef8b75 100644
--- a/packages/suite-desktop/src-electron/modules/auto-updater.ts
+++ b/packages/suite-desktop/src-electron/modules/auto-updater.ts
@@ -14,7 +14,7 @@ import {
import { isDev } from '@suite-utils/build';
import { b2t } from '@desktop-electron/libs/utils';
import { verifySignature } from '@desktop-electron/libs/update-checker';
-import { toHumanReadable } from '@suite-utils/file';
+import { bytesToHumanReadable } from '@trezor/utils';
import { isEnabled } from '@suite-utils/features';
// Runtime flags
@@ -142,9 +142,9 @@ const init = ({ mainWindow, store }: Dependencies) => {
autoUpdater.on('download-progress', progressObj => {
logger.debug(
'auto-updater',
- `Downloading ${progressObj.percent}% (${toHumanReadable(
+ `Downloading ${progressObj.percent}% (${bytesToHumanReadable(
progressObj.transferred,
- )}/${toHumanReadable(progressObj.total)})`,
+ )}/${bytesToHumanReadable(progressObj.total)})`,
);
mainWindow.webContents.send('update/downloading', { ...progressObj });
});
diff --git a/packages/suite-desktop/src/support/DesktopUpdater/Downloading.tsx b/packages/suite-desktop/src/support/DesktopUpdater/Downloading.tsx
index 46bbfef4162..5615e4ff6c5 100644
--- a/packages/suite-desktop/src/support/DesktopUpdater/Downloading.tsx
+++ b/packages/suite-desktop/src/support/DesktopUpdater/Downloading.tsx
@@ -5,7 +5,7 @@ import { H2, variables } from '@trezor/components';
import { Translation, Modal } from '@suite-components';
import { Row } from './styles';
-import { toHumanReadable } from '@suite-utils/file';
+import { bytesToHumanReadable } from '@trezor/utils';
import { UpdateProgress } from '@suite-types/desktop';
const ModalHeadingWrapper = styled.div`
@@ -76,9 +76,9 @@ const Downloading = ({ hideWindow, progress }: Props) => {
- {toHumanReadable(progress?.transferred || 0)}
+ {bytesToHumanReadable(progress?.transferred || 0)}
- /{toHumanReadable(progress?.total || 0)}
+ /{bytesToHumanReadable(progress?.total || 0)}
>
)}
diff --git a/packages/suite/package.json b/packages/suite/package.json
index dacaf33ad01..00e3f60053f 100644
--- a/packages/suite/package.json
+++ b/packages/suite/package.json
@@ -26,6 +26,7 @@
"@trezor/components": "1.0.0",
"@trezor/suite-data": "1.0.0",
"@trezor/suite-storage": "1.0.0",
+ "@trezor/utils": "*",
"bignumber.js": "^9.0.2",
"date-fns": "^2.27.0",
"dropbox": "^10.23.0",
diff --git a/packages/suite/src/actions/backup/__tests__/backupActions.test.ts b/packages/suite/src/actions/backup/__tests__/backupActions.test.ts
index a4d8af247cd..6a4e15ecf37 100644
--- a/packages/suite/src/actions/backup/__tests__/backupActions.test.ts
+++ b/packages/suite/src/actions/backup/__tests__/backupActions.test.ts
@@ -4,7 +4,7 @@
import configureStore from 'redux-mock-store';
import thunk from 'redux-thunk';
-import { mergeObj } from '@suite-utils/mergeObj';
+import { mergeObject } from '@trezor/utils';
import { init } from '@suite-actions/trezorConnectActions';
import { SUITE } from '@suite-actions/constants';
import { BACKUP } from '@backup-actions/constants';
@@ -52,7 +52,7 @@ export const getInitialState = (override: any) => {
devices: [],
};
if (override) {
- return mergeObj(defaults, override);
+ return mergeObject(defaults, override);
}
return defaults;
};
diff --git a/packages/suite/src/actions/suite/__tests__/analyticsActions.test.ts b/packages/suite/src/actions/suite/__tests__/analyticsActions.test.ts
index 4ec128ae61d..ed14893aee3 100644
--- a/packages/suite/src/actions/suite/__tests__/analyticsActions.test.ts
+++ b/packages/suite/src/actions/suite/__tests__/analyticsActions.test.ts
@@ -48,9 +48,6 @@ const oldWindowLocation = window.location;
describe('Analytics Actions', () => {
beforeAll(() => {
- // eslint-disable-next-line global-require
- require('@suite-utils/random');
-
// @ts-ignore The operand of a 'delete' operator must be optional.
delete window.location;
diff --git a/packages/suite/src/actions/suite/metadataActions.ts b/packages/suite/src/actions/suite/metadataActions.ts
index 830e8cf864d..bc5af557cab 100644
--- a/packages/suite/src/actions/suite/metadataActions.ts
+++ b/packages/suite/src/actions/suite/metadataActions.ts
@@ -1,6 +1,6 @@
import TrezorConnect from 'trezor-connect';
+import { createDeferred } from '@trezor/utils';
import { METADATA } from '@suite-actions/constants';
-import { createDeferred } from '@suite-utils/deferred';
import { Dispatch, GetState } from '@suite-types';
import {
MetadataProviderType,
diff --git a/packages/suite/src/actions/suite/modalActions.ts b/packages/suite/src/actions/suite/modalActions.ts
index ac3e5793a74..3f415254138 100644
--- a/packages/suite/src/actions/suite/modalActions.ts
+++ b/packages/suite/src/actions/suite/modalActions.ts
@@ -1,8 +1,8 @@
import TrezorConnect, { UI } from 'trezor-connect';
+import { createDeferred, Deferred, DeferredResponse } from '@trezor/utils';
import { MODAL, SUITE } from '@suite-actions/constants';
import { Route, Dispatch, GetState, TrezorDevice } from '@suite-types';
import { Account, WalletAccountTransaction } from '@wallet-types';
-import { createDeferred, Deferred, DeferredResponse } from '@suite-utils/deferred';
export type UserContextPayload =
| {
diff --git a/packages/suite/src/components/suite/NotificationRenderer/renderers/UriSchemeRenderers.tsx b/packages/suite/src/components/suite/NotificationRenderer/renderers/UriSchemeRenderers.tsx
index e5e00d19e13..948e803ef80 100644
--- a/packages/suite/src/components/suite/NotificationRenderer/renderers/UriSchemeRenderers.tsx
+++ b/packages/suite/src/components/suite/NotificationRenderer/renderers/UriSchemeRenderers.tsx
@@ -5,7 +5,7 @@ import * as protocolActions from '@suite-actions/protocolActions';
import { Translation } from '@suite-components';
import { CoinLogo } from '@trezor/components';
import { useActions, useSelector } from '@suite-hooks';
-import { capitalizeFirstLetter } from '@suite-utils/string';
+import { capitalizeFirstLetter } from '@trezor/utils';
import { PROTOCOL_TO_NETWORK } from '@suite-constants/protocol';
import ConditionalActionRenderer from './ConditionalActionRenderer';
diff --git a/packages/suite/src/components/suite/modals/AddAccount/index.tsx b/packages/suite/src/components/suite/modals/AddAccount/index.tsx
index b239160a571..7a2ecfde463 100644
--- a/packages/suite/src/components/suite/modals/AddAccount/index.tsx
+++ b/packages/suite/src/components/suite/modals/AddAccount/index.tsx
@@ -9,7 +9,7 @@ import { useSelector, useActions } from '@suite-hooks';
import * as accountActions from '@wallet-actions/accountActions';
import * as walletSettingsActions from '@settings-actions/walletSettingsActions';
import * as routerActions from '@suite-actions/routerActions';
-import { partition } from '@suite-utils/array';
+import { arrayPartition } from '@trezor/utils';
import { AccountTypeSelect } from './components/AccountTypeSelect';
import { SelectNetwork } from './components/SelectNetwork';
@@ -68,12 +68,12 @@ const AddAccountModal = ({ device, onCancel, symbol, noRedirect }: Props) => {
const selectedNetworkEnabled =
!!selectedNetwork && enabledNetworksSymbols.includes(selectedNetwork.symbol);
- const [enabledNetworks, disabledNetworks] = partition(internalNetworks, network =>
+ const [enabledNetworks, disabledNetworks] = arrayPartition(internalNetworks, network =>
enabledNetworksSymbols.includes(network.symbol),
);
const hasDisabledNetworks = !!disabledNetworks?.length;
- const [disabledMainnetNetworks, disabledTestnetNetworks] = partition(
+ const [disabledMainnetNetworks, disabledTestnetNetworks] = arrayPartition(
disabledNetworks,
network => !network?.testnet,
);
diff --git a/packages/suite/src/components/suite/modals/Passphrase/components/PassphraseTypeCard/index.tsx b/packages/suite/src/components/suite/modals/Passphrase/components/PassphraseTypeCard/index.tsx
index 4a79a3dd57a..abd9368c842 100644
--- a/packages/suite/src/components/suite/modals/Passphrase/components/PassphraseTypeCard/index.tsx
+++ b/packages/suite/src/components/suite/modals/Passphrase/components/PassphraseTypeCard/index.tsx
@@ -6,7 +6,7 @@ import styled, { css } from 'styled-components';
import { Button, useTheme, variables, Input, Tooltip, Checkbox, Icon } from '@trezor/components';
import { Translation } from '@suite-components/Translation';
import { MAX_LENGTH } from '@suite-constants/inputs';
-import { countBytesInString } from '@suite-utils/string';
+import { countBytesInString } from '@trezor/utils';
import { OpenGuideFromTooltip } from '@guide-views';
import PasswordStrengthIndicator from '@suite-components/PasswordStrengthIndicator';
import { useTranslation } from '@suite-hooks';
diff --git a/packages/suite/src/components/suite/modals/TransactionDetail/components/ChangeFee/components/AffectedTransactions/index.tsx b/packages/suite/src/components/suite/modals/TransactionDetail/components/ChangeFee/components/AffectedTransactions/index.tsx
index 8864cd6f7b7..c8f0b1a50ce 100644
--- a/packages/suite/src/components/suite/modals/TransactionDetail/components/ChangeFee/components/AffectedTransactions/index.tsx
+++ b/packages/suite/src/components/suite/modals/TransactionDetail/components/ChangeFee/components/AffectedTransactions/index.tsx
@@ -4,7 +4,7 @@ import { Icon, Button, useTheme, variables } from '@trezor/components';
import { FormattedCryptoAmount, Sign, Translation, FormattedDate } from '@suite-components';
import { useRbfContext } from '@wallet-hooks/useRbfForm';
import { useLayoutSize } from '@suite-hooks/useLayoutSize';
-import { truncateMiddle } from '@suite-utils/string';
+import { truncateMiddle } from '@trezor/utils';
import GreyCard from '../GreyCard';
import WarnHeader from '../WarnHeader';
diff --git a/packages/suite/src/components/suite/modals/confirm/CoinmarketBuyTerms/index.tsx b/packages/suite/src/components/suite/modals/confirm/CoinmarketBuyTerms/index.tsx
index 7b51532c0e2..2341bdd43ae 100644
--- a/packages/suite/src/components/suite/modals/confirm/CoinmarketBuyTerms/index.tsx
+++ b/packages/suite/src/components/suite/modals/confirm/CoinmarketBuyTerms/index.tsx
@@ -2,7 +2,7 @@ import { Button, Icon, variables, Checkbox, H3 } from '@trezor/components';
import React, { useState } from 'react';
import { Translation, Modal } from '@suite-components';
import styled, { css } from 'styled-components';
-import { Deferred } from '@suite-utils/deferred';
+import type { Deferred } from '@trezor/utils';
const Text = styled.div<{ isLast?: boolean; isFirst?: boolean }>`
padding: 20px 0;
diff --git a/packages/suite/src/components/suite/modals/confirm/CoinmarketExchangeDexTerms/index.tsx b/packages/suite/src/components/suite/modals/confirm/CoinmarketExchangeDexTerms/index.tsx
index 2e29b9f01c8..af453524a9a 100644
--- a/packages/suite/src/components/suite/modals/confirm/CoinmarketExchangeDexTerms/index.tsx
+++ b/packages/suite/src/components/suite/modals/confirm/CoinmarketExchangeDexTerms/index.tsx
@@ -2,7 +2,7 @@ import { Button, Icon, variables, Checkbox } from '@trezor/components';
import React, { useState } from 'react';
import { Translation, Modal } from '@suite-components';
import styled, { css } from 'styled-components';
-import { Deferred } from '@suite-utils/deferred';
+import type { Deferred } from '@trezor/utils';
const Text = styled.div<{ isLast?: boolean; isFirst?: boolean }>`
padding: 20px 0;
diff --git a/packages/suite/src/components/suite/modals/confirm/CoinmarketExchangeTerms/index.tsx b/packages/suite/src/components/suite/modals/confirm/CoinmarketExchangeTerms/index.tsx
index 124353be97f..ed888c293aa 100644
--- a/packages/suite/src/components/suite/modals/confirm/CoinmarketExchangeTerms/index.tsx
+++ b/packages/suite/src/components/suite/modals/confirm/CoinmarketExchangeTerms/index.tsx
@@ -2,7 +2,7 @@ import { Button, Icon, variables, Checkbox, H3 } from '@trezor/components';
import React, { useState } from 'react';
import { Translation, Modal } from '@suite-components';
import styled, { css } from 'styled-components';
-import { Deferred } from '@suite-utils/deferred';
+import type { Deferred } from '@trezor/utils';
const Text = styled.div<{ isLast?: boolean; isFirst?: boolean }>`
padding: 20px 0;
diff --git a/packages/suite/src/components/suite/modals/confirm/CoinmarketSellTerms/index.tsx b/packages/suite/src/components/suite/modals/confirm/CoinmarketSellTerms/index.tsx
index 01eaaa70a9b..5f415157a3e 100644
--- a/packages/suite/src/components/suite/modals/confirm/CoinmarketSellTerms/index.tsx
+++ b/packages/suite/src/components/suite/modals/confirm/CoinmarketSellTerms/index.tsx
@@ -2,7 +2,7 @@ import { Button, Icon, variables, Checkbox, H3 } from '@trezor/components';
import React, { useState } from 'react';
import { Translation, Modal } from '@suite-components';
import styled, { css } from 'styled-components';
-import { Deferred } from '@suite-utils/deferred';
+import type { Deferred } from '@trezor/utils';
const Text = styled.div<{ isLast?: boolean; isFirst?: boolean }>`
padding: 20px 0;
diff --git a/packages/suite/src/components/suite/modals/metadata/MetadataProvider/index.tsx b/packages/suite/src/components/suite/modals/metadata/MetadataProvider/index.tsx
index 2ec1049a537..ec033dd1493 100644
--- a/packages/suite/src/components/suite/modals/metadata/MetadataProvider/index.tsx
+++ b/packages/suite/src/components/suite/modals/metadata/MetadataProvider/index.tsx
@@ -5,7 +5,7 @@ import { P, Button, variables } from '@trezor/components';
import { Translation, Modal } from '@suite-components';
import { useActions } from '@suite-hooks';
import * as metadataActions from '@suite-actions/metadataActions';
-import { Deferred } from '@suite-utils/deferred';
+import type { Deferred } from '@trezor/utils';
import { MetadataProviderType } from '@suite-types/metadata';
import { isEnabled } from '@suite-utils/features';
diff --git a/packages/suite/src/hooks/settings/backends/useBackendsForm.ts b/packages/suite/src/hooks/settings/backends/useBackendsForm.ts
index 93db612d9ff..a7475f38a83 100644
--- a/packages/suite/src/hooks/settings/backends/useBackendsForm.ts
+++ b/packages/suite/src/hooks/settings/backends/useBackendsForm.ts
@@ -1,7 +1,7 @@
import { useState } from 'react';
import { useForm } from 'react-hook-form';
import { useActions, useSelector, useTranslation } from '@suite-hooks';
-import { isUrl } from '@suite-utils/validators';
+import { isUrl } from '@trezor/utils';
import { setBackend as setBackendAction } from '@settings-actions/walletSettingsActions';
import type { Network } from '@wallet-types';
import type { BackendType } from '@wallet-reducers/settingsReducer';
diff --git a/packages/suite/src/hooks/suite/useAsyncDebounce.ts b/packages/suite/src/hooks/suite/useAsyncDebounce.ts
index b82f6607efd..fe7e7d9c026 100644
--- a/packages/suite/src/hooks/suite/useAsyncDebounce.ts
+++ b/packages/suite/src/hooks/suite/useAsyncDebounce.ts
@@ -1,5 +1,5 @@
import { useCallback, useRef } from 'react';
-import { createDeferred } from '@suite-utils/deferred';
+import { createDeferred } from '@trezor/utils';
type TimeoutType = ReturnType; // resolves to Timeout type in react-native, number otherwise
diff --git a/packages/suite/src/hooks/wallet/sign-verify/useSignVerifyForm.ts b/packages/suite/src/hooks/wallet/sign-verify/useSignVerifyForm.ts
index 4f5eb085975..a5ef0f83d58 100644
--- a/packages/suite/src/hooks/wallet/sign-verify/useSignVerifyForm.ts
+++ b/packages/suite/src/hooks/wallet/sign-verify/useSignVerifyForm.ts
@@ -3,7 +3,7 @@ import { useForm, useController } from 'react-hook-form';
import { useTranslation, useSelector, useActions } from '@suite-hooks';
import * as protocolActions from '@suite-actions/protocolActions';
import { isHex } from '@wallet-utils/ethUtils';
-import { isASCII } from '@suite-utils/validators';
+import { isAscii } from '@trezor/utils';
import { isAddressValid } from '@wallet-utils/validation';
import type { Account } from '@wallet-types';
import type { AoppState } from '@suite-reducers/protocolReducer';
@@ -114,7 +114,7 @@ export const useSignVerifyForm = (page: 'sign' | 'verify', account?: Account) =>
? translationString('DATA_NOT_VALID_HEX')
: undefined,
ascii: (message: string) =>
- !formValues.hex && !isASCII(message)
+ !formValues.hex && !isAscii(message)
? translationString('TR_ASCII_ONLY')
: undefined,
},
diff --git a/packages/suite/src/reducers/suite/resizeReducer.ts b/packages/suite/src/reducers/suite/resizeReducer.ts
index 65301c9daff..606481b8f01 100644
--- a/packages/suite/src/reducers/suite/resizeReducer.ts
+++ b/packages/suite/src/reducers/suite/resizeReducer.ts
@@ -1,15 +1,15 @@
import produce from 'immer';
import * as variables from '@trezor/components/lib/config/variables'; // can't import from index cause it would import all UI components
import { RESIZE } from '@suite-actions/constants';
-import { getNumberFromPxString } from '@suite-utils/string';
+import { getNumberFromPixelString } from '@trezor/utils';
import { Action } from '@suite-types';
const sizes = {
- UNAVAILABLE: getNumberFromPxString(variables.SCREEN_SIZE.UNAVAILABLE),
- SMALL: getNumberFromPxString(variables.SCREEN_SIZE.SM),
- MEDIUM: getNumberFromPxString(variables.SCREEN_SIZE.MD),
- LARGE: getNumberFromPxString(variables.SCREEN_SIZE.LG),
- XLARGE: getNumberFromPxString(variables.SCREEN_SIZE.XL),
+ UNAVAILABLE: getNumberFromPixelString(variables.SCREEN_SIZE.UNAVAILABLE),
+ SMALL: getNumberFromPixelString(variables.SCREEN_SIZE.SM),
+ MEDIUM: getNumberFromPixelString(variables.SCREEN_SIZE.MD),
+ LARGE: getNumberFromPixelString(variables.SCREEN_SIZE.LG),
+ XLARGE: getNumberFromPixelString(variables.SCREEN_SIZE.XL),
};
const getSize = (screenWidth: number | null): State['size'] => {
diff --git a/packages/suite/src/reducers/suite/suiteReducer.ts b/packages/suite/src/reducers/suite/suiteReducer.ts
index bca69bfbf1b..a73ad42eee8 100644
--- a/packages/suite/src/reducers/suite/suiteReducer.ts
+++ b/packages/suite/src/reducers/suite/suiteReducer.ts
@@ -7,7 +7,7 @@ import { Action, TrezorDevice, Lock, SuiteThemeColors } from '@suite-types';
import type { Locale } from '@suite-config/languages';
import { isWeb, getWindowWidth } from '@suite-utils/env';
import { ensureLocale } from '@suite-utils/l10n';
-import { getNumberFromPxString } from '@suite-utils/string';
+import { getNumberFromPixelString } from '@trezor/utils';
export interface DebugModeOptions {
invityAPIUrl?: string;
@@ -79,7 +79,8 @@ const initialState: SuiteState = {
taprootBannerClosed: false,
securityStepsHidden: false,
dashboardGraphHidden: false,
- dashboardAssetsGridMode: getWindowWidth() < getNumberFromPxString(variables.SCREEN_SIZE.SM),
+ dashboardAssetsGridMode:
+ getWindowWidth() < getNumberFromPixelString(variables.SCREEN_SIZE.SM),
},
settings: {
theme: {
diff --git a/packages/suite/src/reducers/wallet/discoveryReducer.ts b/packages/suite/src/reducers/wallet/discoveryReducer.ts
index 5386bc5092a..a0be96fbadf 100644
--- a/packages/suite/src/reducers/wallet/discoveryReducer.ts
+++ b/packages/suite/src/reducers/wallet/discoveryReducer.ts
@@ -1,7 +1,7 @@
import produce from 'immer';
import { DISCOVERY } from '@wallet-actions/constants';
import { STORAGE } from '@suite-actions/constants';
-import { Deferred, createDeferred } from '@suite-utils/deferred';
+import { Deferred, createDeferred } from '@trezor/utils';
import { ObjectValues } from '@suite/types/utils';
import { Action as SuiteAction } from '@suite-types';
import { WalletAction, Network } from '@wallet-types';
diff --git a/packages/suite/src/services/suite/metadata/DropboxProvider.ts b/packages/suite/src/services/suite/metadata/DropboxProvider.ts
index 49591c5c3b6..a1a417325f9 100644
--- a/packages/suite/src/services/suite/metadata/DropboxProvider.ts
+++ b/packages/suite/src/services/suite/metadata/DropboxProvider.ts
@@ -4,7 +4,7 @@ import type { users } from 'dropbox';
import { AbstractMetadataProvider } from '@suite-types/metadata';
import { extractCredentialsFromAuthorizationFlow, getOauthReceiverUrl } from '@suite-utils/oauth';
import { METADATA } from '@suite-actions/constants';
-import { getRandomId } from '@suite-utils/random';
+import { getWeakRandomId } from '@trezor/utils';
// this is incorrectly typed in dropbox
@@ -54,7 +54,7 @@ class DropboxProvider extends AbstractMetadataProvider {
const url = await this.auth.getAuthenticationUrl(
redirectUrl,
- getRandomId(10),
+ getWeakRandomId(10),
'code',
'offline',
undefined, // If this parameter is omitted, the authorization page will request all scopes selected on the Permissions tab
diff --git a/packages/suite/src/utils/suite/__tests__/random.test.ts b/packages/suite/src/utils/suite/__tests__/random.test.ts
index 20dbfa1e4a2..a170b4ddba5 100644
--- a/packages/suite/src/utils/suite/__tests__/random.test.ts
+++ b/packages/suite/src/utils/suite/__tests__/random.test.ts
@@ -1,25 +1,6 @@
-import { getRandomId, getCodeChallenge } from '../random';
+import { getCodeChallenge } from '../random';
describe('random', () => {
- describe('getRandomId', () => {
- it('should return random id of fixed length', () => {
- expect(getRandomId(12)).toHaveLength(12);
- });
- it('should generate few results which should be unique', () => {
- const ids: any = {};
- for (let i = 0; i < 10; i++) {
- const random = getRandomId(10);
- if (!ids[random]) {
- ids[random] = 0;
- }
- ids[random]++;
- }
- Object.values(ids).forEach(id => {
- expect(id).toEqual(1);
- });
- });
- });
-
describe('getCodeChallenge', () => {
it('should match regexp [0-9a-zA-Z-._~], {43,128}', () => {
expect(getCodeChallenge()).toMatch(/[0-9a-zA-Z\-._~]{128}/);
diff --git a/packages/suite/src/utils/suite/__tests__/string.test.ts b/packages/suite/src/utils/suite/__tests__/string.test.ts
deleted file mode 100644
index 97d155d73ad..00000000000
--- a/packages/suite/src/utils/suite/__tests__/string.test.ts
+++ /dev/null
@@ -1,18 +0,0 @@
-import * as stringUtils from '../string';
-
-it('capitalizeFirstLetter', () => {
- expect(stringUtils.capitalizeFirstLetter('god')).toBe('God');
- expect(stringUtils.capitalizeFirstLetter('dog')).toBe('Dog');
-});
-
-it('countBytesInString', () => {
- expect(stringUtils.countBytesInString('aaa')).toBe(3);
- expect(stringUtils.countBytesInString('č')).toBe(2);
- expect(stringUtils.countBytesInString('😀')).toBe(4);
- expect(stringUtils.countBytesInString('+ěčřěžšýžřšý')).toBe(23);
-});
-
-it('getNumberFromPxString', () => {
- expect(stringUtils.getNumberFromPxString('1px')).toBe(1);
- expect(stringUtils.getNumberFromPxString('1')).toBe(1);
-});
diff --git a/packages/suite/src/utils/suite/__tests__/validators.test.ts b/packages/suite/src/utils/suite/__tests__/validators.test.ts
deleted file mode 100644
index 31a50979e94..00000000000
--- a/packages/suite/src/utils/suite/__tests__/validators.test.ts
+++ /dev/null
@@ -1,130 +0,0 @@
-import { isEmail, isASCII, hasDecimals, hasUppercase, isNumber } from '../validators';
-
-describe('utils/suite/validators', () => {
- describe('isEmail', () => {
- it('should return true for valid email', () => {
- expect(isEmail('satoshi@nakamoto.io')).toEqual(true);
- });
-
- it('should return false for invalid email', () => {
- const fooMails = ['', 'satoshi', 'satoshi@seznam', 'satoshi @foo.io'];
- fooMails.forEach(mail => {
- expect(isEmail(mail)).toEqual(false);
- });
- });
- });
-
- describe('isASCII', () => {
- it('should return true for ASCII only string', () => {
- expect(isASCII('this is only ascii')).toEqual(true);
- });
-
- it('should return true when called without parameter', () => {
- expect(isASCII()).toEqual(true);
- });
-
- it('should return false strings with non ASCII chars', () => {
- const fooStrings = ['¥', 'železniční přejezd'];
- fooStrings.forEach(str => {
- expect(isASCII(str)).toEqual(false);
- });
- });
- });
-
- it('hasDecimals', () => {
- expect(hasDecimals('0', 18)).toBe(true);
- expect(hasDecimals('0.0', 18)).toBe(true);
- expect(hasDecimals('0.00000000', 18)).toBe(true);
- expect(hasDecimals('0.00000001', 18)).toBe(true);
- expect(hasDecimals('+0.0', 18)).toBe(false);
- expect(hasDecimals('-0.0', 18)).toBe(false);
- expect(hasDecimals('1', 18)).toBe(true);
- expect(hasDecimals('+1', 18)).toBe(false);
- expect(hasDecimals('+100000', 18)).toBe(false);
- expect(hasDecimals('.', 18)).toBe(false);
- expect(hasDecimals('-.1', 18)).toBe(false);
- expect(hasDecimals('0.1', 18)).toBe(true);
- expect(hasDecimals('0.12314841', 18)).toBe(true);
- expect(hasDecimals('0.1381841848184814818391931933', 18)).toBe(false); // 28 decimals
- expect(hasDecimals('0.100000000000000000', 18)).toBe(true); // 18s decimals
-
- expect(hasDecimals('100.', 18)).toBe(true);
- expect(hasDecimals('.1', 18)).toBe(false);
- expect(hasDecimals('.000000001', 18)).toBe(false);
- expect(hasDecimals('.13134818481481841', 18)).toBe(false);
-
- expect(hasDecimals('001.12314841', 18)).toBe(false);
- expect(hasDecimals('83819319391491949941', 18)).toBe(true);
- expect(hasDecimals('-83819319391491949941', 18)).toBe(false);
- expect(hasDecimals('+0.131831848184', 18)).toBe(false);
- expect(hasDecimals('0.127373193981774718318371831731761626162613', 18)).toBe(false);
-
- expect(hasDecimals('0.131831848184a', 18)).toBe(false);
- expect(hasDecimals('100a', 18)).toBe(false);
- expect(hasDecimals('.100a', 18)).toBe(false);
- expect(hasDecimals('a.100', 18)).toBe(false);
- expect(hasDecimals('abc', 18)).toBe(false);
- expect(hasDecimals('1abc0', 18)).toBe(false);
- });
-
- it('hasDecimals decimals=0', () => {
- expect(hasDecimals('0', 0)).toBe(true);
- expect(hasDecimals('0.1', 0)).toBe(false);
- expect(hasDecimals('0.12345', 0)).toBe(false);
- expect(hasDecimals('1', 0)).toBe(true);
- expect(hasDecimals('1.1', 0)).toBe(false);
- expect(hasDecimals('1000000', 0)).toBe(true);
- expect(hasDecimals('-1000000', 0)).toBe(false);
- expect(hasDecimals('.0', 0)).toBe(false);
- expect(hasDecimals('0.', 0)).toBe(false);
- expect(hasDecimals('.', 0)).toBe(false);
- });
-
- it('hasUppercase', () => {
- expect(hasUppercase('0')).toBe(false);
- expect(hasUppercase('abc')).toBe(false);
- expect(hasUppercase('abcD')).toBe(true);
- expect(hasUppercase('Abcd')).toBe(true);
- expect(hasUppercase('aBcd')).toBe(true);
- expect(hasUppercase('123abc123')).toBe(false);
- expect(hasUppercase('0x123abc456')).toBe(false);
- expect(hasUppercase('0x123aBc456')).toBe(true);
- });
-
- it('isNumber', () => {
- expect(isNumber('0')).toBe(true);
- expect(isNumber('0.0')).toBe(true);
- expect(isNumber('0.00000000')).toBe(true);
- expect(isNumber('0.00000001')).toBe(true);
- expect(isNumber('+0.0')).toBe(false);
- expect(isNumber('-0.0')).toBe(false);
- expect(isNumber('1')).toBe(true);
- expect(isNumber('+1')).toBe(false);
- expect(isNumber('+100000')).toBe(false);
- expect(isNumber('.')).toBe(false);
- expect(isNumber('')).toBe(false);
- expect(isNumber(' ')).toBe(false);
- expect(isNumber('-.1')).toBe(false);
- expect(isNumber('0.1')).toBe(true);
- expect(isNumber('0.12314841')).toBe(true);
- expect(isNumber('0.1381841848184814818391931933')).toBe(true); // 28 decimals
- expect(isNumber('0.100000000000000000')).toBe(true); // 18s decimals
-
- expect(isNumber('100.')).toBe(true);
- expect(isNumber('.1')).toBe(false);
- expect(isNumber('.000000001')).toBe(false);
- expect(isNumber('.13134818481481841')).toBe(false);
-
- expect(isNumber('001.12314841')).toBe(false);
- expect(isNumber('83819319391491949941')).toBe(true);
- expect(isNumber('-83819319391491949941')).toBe(false);
- expect(isNumber('+0.131831848184')).toBe(false);
-
- expect(isNumber('0.131831848184a')).toBe(false);
- expect(isNumber('100a')).toBe(false);
- expect(isNumber('.100a')).toBe(false);
- expect(isNumber('a.100')).toBe(false);
- expect(isNumber('abc')).toBe(false);
- expect(isNumber('1abc0')).toBe(false);
- });
-});
diff --git a/packages/suite/src/utils/suite/deferred.ts b/packages/suite/src/utils/suite/deferred.ts
deleted file mode 100644
index 820904dd302..00000000000
--- a/packages/suite/src/utils/suite/deferred.ts
+++ /dev/null
@@ -1,28 +0,0 @@
-export interface Deferred {
- data?: Data;
- promise: Promise;
- resolve: (t: Response) => void;
- reject: (e: Error) => void;
-}
-
-// unwrap promise response from Deferred
-export type DeferredResponse = D extends Deferred ? R : never;
-
-export const createDeferred = (
- data?: Data,
-): Deferred => {
- let localResolve: (t: Response) => void = () => {};
- let localReject: (e: Error) => void = () => {};
-
- const promise: Promise = new Promise((resolve, reject) => {
- localResolve = resolve;
- localReject = reject;
- });
-
- return {
- data,
- resolve: localResolve,
- reject: localReject,
- promise,
- };
-};
diff --git a/packages/suite/src/utils/suite/oauth.ts b/packages/suite/src/utils/suite/oauth.ts
index 8587cb5e152..80f0ea8f362 100644
--- a/packages/suite/src/utils/suite/oauth.ts
+++ b/packages/suite/src/utils/suite/oauth.ts
@@ -2,7 +2,7 @@
import { getPrefixedURL } from '@suite-utils/router';
import { METADATA } from '@suite-actions/constants';
-import { Deferred, createDeferred } from '@suite-utils/deferred';
+import { Deferred, createDeferred } from '@trezor/utils';
import { urlHashParams, urlSearchParams } from '@suite-utils/metadata';
/**
diff --git a/packages/suite/src/utils/suite/random.ts b/packages/suite/src/utils/suite/random.ts
index 7cbc3799eaf..24e74263ead 100644
--- a/packages/suite/src/utils/suite/random.ts
+++ b/packages/suite/src/utils/suite/random.ts
@@ -1,20 +1,9 @@
-// todo: rework to something cryptographically stronger, probably use random bytes from crypto node module?
-export const getRandomId = (length: number) => {
- let id = '';
- const list = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
- for (let i = 0; i < length; i++) {
- id += list.charAt(Math.floor(Math.random() * list.length));
- }
- return id;
-};
+import { getWeakRandomId } from '@trezor/utils';
-export const getAnalyticsRandomId = () => getRandomId(10);
+export const getAnalyticsRandomId = () => getWeakRandomId(10);
/**
* Generate code_challenge for Oauth2
* Authorization code with PKCE flow
*/
-export const getCodeChallenge = () => getRandomId(128);
-
-export const getRandomNumberInRange = (min: number, max: number) =>
- Math.floor(Math.random() * (max - min + 1)) + min;
+export const getCodeChallenge = () => getWeakRandomId(128);
diff --git a/packages/suite/src/utils/suite/validators.ts b/packages/suite/src/utils/suite/validators.ts
deleted file mode 100644
index 0e4db4013a7..00000000000
--- a/packages/suite/src/utils/suite/validators.ts
+++ /dev/null
@@ -1,44 +0,0 @@
-// RFC 5322 - http://emailregex.com/
-const EMAIL_REGEX =
- /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
-// Simple URL regex
-const URL_REGEX =
- /^(http|ws)s?:\/\/[a-z0-9]([a-z0-9.-]+)?(:[0-9]{1,5})?((\/)?(([a-z0-9-_])+(\/)?)+)$/i;
-
-export function isEmail(value?: string): boolean {
- if (!value) return false;
- return EMAIL_REGEX.test(value);
-}
-
-export const isUrl = (value: string): boolean => URL_REGEX.test(value);
-
-export function isASCII(value?: string): boolean {
- if (!value) return true;
- return /^[\x00-\x7F]*$/.test(value); // eslint-disable-line
-}
-
-export const hasUppercase = (value: string) => {
- const UPPERCASE_RE = new RegExp('^(.*[A-Z].*)$');
- return UPPERCASE_RE.test(value);
-};
-
-export const isAbs = (value: string) => {
- const ABS_RE = new RegExp('^[0-9]+$');
- return ABS_RE.test(value);
-};
-
-export const hasDecimals = (value: string, decimals: number) => {
- if (decimals === 0) {
- return isAbs(value);
- }
-
- const ETH_DECIMALS_RE = new RegExp(
- `^(0|0\\.([0-9]{0,${decimals}})?|[1-9][0-9]*\\.?([0-9]{0,${decimals}})?)$`,
- );
- return ETH_DECIMALS_RE.test(value);
-};
-
-export const isNumber = (value: string) => {
- const NUMBER_RE = new RegExp(`^(0|0\\.([0-9]+)?|[1-9][0-9]*\\.?([0-9]+)?)$`);
- return NUMBER_RE.test(value);
-};
diff --git a/packages/suite/src/utils/wallet/ethUtils.ts b/packages/suite/src/utils/wallet/ethUtils.ts
index d1f200148c5..eedac91b411 100644
--- a/packages/suite/src/utils/wallet/ethUtils.ts
+++ b/packages/suite/src/utils/wallet/ethUtils.ts
@@ -1,4 +1,5 @@
import * as EthereumjsUtil from 'ethereumjs-util';
+import { hasUppercaseLetter } from '@trezor/utils';
import BigNumber from 'bignumber.js';
export const decimalToHex = (dec: number): string => new BigNumber(dec).toString(16);
@@ -24,14 +25,13 @@ export const strip = (str: string): string => {
};
export const validateAddress = (address: string): string | null => {
- const hasUpperCase = new RegExp('^(.*[A-Z].*)$');
if (address.length < 1) {
return 'Address is not set';
}
if (!EthereumjsUtil.isValidAddress(address)) {
return 'Address is not valid';
}
- if (address.match(hasUpperCase) && !EthereumjsUtil.isValidChecksumAddress(address)) {
+ if (hasUppercaseLetter(address) && !EthereumjsUtil.isValidChecksumAddress(address)) {
return 'Address is not a valid checksum';
}
return null;
diff --git a/packages/suite/src/utils/wallet/promiseUtils.ts b/packages/suite/src/utils/wallet/promiseUtils.ts
deleted file mode 100644
index 1fd353af2ab..00000000000
--- a/packages/suite/src/utils/wallet/promiseUtils.ts
+++ /dev/null
@@ -1,4 +0,0 @@
-export const resolveAfter = (msec: number, ...params: any[]): Promise =>
- new Promise(resolve => {
- setTimeout(resolve, msec, ...params);
- });
diff --git a/packages/suite/src/views/settings/index.tsx b/packages/suite/src/views/settings/index.tsx
index 8d20e124173..ed6c2c20d9a 100644
--- a/packages/suite/src/views/settings/index.tsx
+++ b/packages/suite/src/views/settings/index.tsx
@@ -17,7 +17,7 @@ import {
import { FIAT } from '@suite-config';
import { useAnalytics, useDevice, useSelector, useActions } from '@suite-hooks';
import { Button, Tooltip, Switch, Link } from '@trezor/components';
-import { capitalizeFirstLetter } from '@suite-utils/string';
+import { capitalizeFirstLetter } from '@trezor/utils';
import * as suiteActions from '@suite-actions/suiteActions';
import * as walletSettingsActions from '@settings-actions/walletSettingsActions';
diff --git a/packages/suite/src/views/wallet/transactions/components/TransactionList/components/NoSearchResults/index.tsx b/packages/suite/src/views/wallet/transactions/components/TransactionList/components/NoSearchResults/index.tsx
index 0377eedeb5e..5d505b77ef3 100644
--- a/packages/suite/src/views/wallet/transactions/components/TransactionList/components/NoSearchResults/index.tsx
+++ b/packages/suite/src/views/wallet/transactions/components/TransactionList/components/NoSearchResults/index.tsx
@@ -2,7 +2,7 @@ import React, { useState } from 'react';
import styled from 'styled-components';
import { Card, variables } from '@trezor/components';
import { Translation } from '@suite-components';
-import { getRandomNumberInRange } from '@suite-utils/random';
+import { getRandomNumberInRange } from '@trezor/utils';
const NoResults = styled(Card)`
display: flex;
diff --git a/packages/transport/src/lowlevel/sharedConnectionWorker.ts b/packages/transport/src/lowlevel/sharedConnectionWorker.ts
index e8f5424e08c..e890f56ccec 100644
--- a/packages/transport/src/lowlevel/sharedConnectionWorker.ts
+++ b/packages/transport/src/lowlevel/sharedConnectionWorker.ts
@@ -6,9 +6,7 @@
// about intent to acquire/release and then send another message when that is done.
// Other windows then can acquire/release
-// @ts-ignore
-import { create as createDefered } from '../utils/defered';
-import type { Deferred } from '../utils/defered';
+import { createDeferred, Deferred } from '@trezor/utils';
import type { TrezorDeviceInfoDebug } from './sharedPlugin';
import type { MessageFromSharedWorker, MessageToSharedWorker } from './withSharedConnections';
@@ -34,7 +32,7 @@ let waitPromise: Promise = Promise.resolve();
type PortObject = { postMessage: (message: Object) => void };
function startLock(): void {
- const newLock = createDefered();
+ const newLock = createDeferred();
lock = newLock;
setTimeout(() => newLock.reject(new Error(`Timed out`)), 10 * 1000);
}
diff --git a/packages/transport/src/lowlevel/withSharedConnections.ts b/packages/transport/src/lowlevel/withSharedConnections.ts
index 6e8d3e05502..ff171f6fe8f 100644
--- a/packages/transport/src/lowlevel/withSharedConnections.ts
+++ b/packages/transport/src/lowlevel/withSharedConnections.ts
@@ -1,12 +1,12 @@
// @ts-nocheck
-import { create as createDefered, resolveTimeoutPromise } from '../utils/defered';
+import { createDeferred, Deferred } from '@trezor/utils';
+import { resolveTimeoutPromise } from '../utils/defered';
import { parseConfigure } from './protobuf/messages';
import { buildAndSend } from './send';
import { receiveAndParse } from './receive';
import type { LowlevelTransportSharedPlugin, TrezorDeviceInfoDebug } from './sharedPlugin';
-import type { Deferred } from '../utils/defered';
import type { MessageFromTrezor, TrezorDeviceInfoWithSession, AcquireInput } from '../types';
import { postModuleMessage } from './sharedConnectionWorker';
@@ -253,9 +253,9 @@ export default class LowlevelTransportWithSharedConnections {
const session: string = messBack2.number;
if (debugLink) {
- this.deferedDebugOnRelease[session] = createDefered();
+ this.deferedDebugOnRelease[session] = createDeferred();
} else {
- this.deferedNormalOnRelease[session] = createDefered();
+ this.deferedNormalOnRelease[session] = createDeferred();
}
return session;
}
@@ -430,7 +430,7 @@ export default class LowlevelTransportWithSharedConnections {
this.latestId++;
const id = this.latestId;
- this.defereds[id] = createDefered();
+ this.defereds[id] = createDeferred();
// when shared worker is not loaded as a shared loader, use it as a module instead
if (this.sharedWorker != null) {
diff --git a/packages/transport/src/utils/defered.ts b/packages/transport/src/utils/defered.ts
index 3cbbb9b441e..46b34223177 100644
--- a/packages/transport/src/utils/defered.ts
+++ b/packages/transport/src/utils/defered.ts
@@ -1,30 +1,4 @@
-export type Deferred = {
- promise: Promise;
- resolve: (t: T) => void;
- reject: (e: Error) => void;
- rejectingPromise: Promise;
-};
-
-export function create(): Deferred {
- let localResolve: (t: T) => void = () => {};
- let localReject: (e?: Error) => void = () => {};
-
- const promise = new Promise((resolve, reject) => {
- localResolve = resolve;
- localReject = reject;
- });
- const rejectingPromise = promise.then(() => {
- throw new Error(`Promise is always rejecting`);
- });
- rejectingPromise.catch(() => {});
-
- return {
- resolve: localResolve,
- reject: localReject,
- promise,
- rejectingPromise,
- };
-}
+// todo: move to @trezor/utils. probably "resolveAfter"?
export function resolveTimeoutPromise(delay: number, result: T): Promise {
return new Promise(resolve => {
@@ -33,11 +7,3 @@ export function resolveTimeoutPromise(delay: number, result: T): Promise {
}, delay);
});
}
-
-export function rejectTimeoutPromise(delay: number, error: Error): Promise {
- return new Promise((_resolve, reject) => {
- setTimeout(() => {
- reject(error);
- }, delay);
- });
-}
diff --git a/packages/utils/.eslintrc.js b/packages/utils/.eslintrc.js
new file mode 100644
index 00000000000..ee73f4a0b50
--- /dev/null
+++ b/packages/utils/.eslintrc.js
@@ -0,0 +1,6 @@
+module.exports = {
+ rules: {
+ 'no-console': 'warn',
+ 'import/no-default-export': 'error',
+ },
+};
diff --git a/packages/utils/LICENSE.md b/packages/utils/LICENSE.md
new file mode 100644
index 00000000000..1bebd9dc53b
--- /dev/null
+++ b/packages/utils/LICENSE.md
@@ -0,0 +1,50 @@
+# TREZOR REFERENCE SOURCE LICENSE (T-RSL)
+
+This license governs use of the accompanying software. If you use the software,
+you accept this license. If you do not accept the license, do not use the
+software.
+
+## 1. Definitions
+
+The terms "reproduce," "reproduction" and "distribution" have the same meaning
+here as under U.S. copyright law.
+
+"You" means the licensee of the software.
+
+"Your company" means the company you worked for when you downloaded the
+software.
+
+"Reference use" means use of the software within your company as a reference,
+in read only form, for the sole purposes of debugging your products,
+maintaining your products, or enhancing the interoperability of your products
+with the software, and specifically excludes the right to distribute the
+software outside of your company.
+
+"Licensed patents" means any Licensor patent claims which read directly on the
+software as distributed by the Licensor under this license.
+
+## 2. Grant of Rights
+
+(A) Copyright Grant - Subject to the terms of this license, the Licensor grants
+you a non-transferable, non-exclusive, worldwide, royalty-free copyright
+license to reproduce the software for reference use.
+
+(B) Patent Grant - Subject to the terms of this license, the Licensor grants
+you a non-transferable, non-exclusive, worldwide, royalty-free patent license
+under licensed patents for reference use.
+
+## 3. Limitations
+
+(A) No Trademark License - This license does not grant you any rights to use
+the Licensor's name, logo, or trademarks.
+
+(B) If you begin patent litigation against the Licensor over patents that you
+think may apply to the software (including a cross-claim or counterclaim in
+a lawsuit), your license to the software ends automatically.
+
+(C) The software is licensed "as-is." You bear the risk of using it. The
+Licensor gives no express warranties, guarantees or conditions. You may have
+additional consumer rights under your local laws which this license cannot
+change. To the extent permitted under your local laws, the Licensor excludes
+the implied warranties of merchantability, fitness for a particular purpose and
+non-infringement.
diff --git a/packages/utils/README.md b/packages/utils/README.md
new file mode 100644
index 00000000000..63007ad8ffd
--- /dev/null
+++ b/packages/utils/README.md
@@ -0,0 +1,3 @@
+# Trezor utils library (@trezor/utils)
+
+A collection of typescript utils that are intended to be used across trezor-suite monorepo.
\ No newline at end of file
diff --git a/packages/utils/jest.config.js b/packages/utils/jest.config.js
new file mode 100644
index 00000000000..4dd314555ea
--- /dev/null
+++ b/packages/utils/jest.config.js
@@ -0,0 +1,17 @@
+module.exports = {
+ rootDir: './',
+ globals: {
+ 'ts-jest': {
+ tsconfig: 'tsconfig.lib.json',
+ },
+ },
+ moduleFileExtensions: ['js', 'ts'],
+ testMatch: ['**/*.test.ts'],
+ coverageDirectory: './coverage/',
+ collectCoverage: true,
+ collectCoverageFrom: ['**/src/**/*.ts'],
+ modulePathIgnorePatterns: ['node_modules'],
+ transform: {
+ '^.+\\.ts$': 'ts-jest',
+ },
+};
diff --git a/packages/utils/package.json b/packages/utils/package.json
new file mode 100644
index 00000000000..9e2ee70ab9e
--- /dev/null
+++ b/packages/utils/package.json
@@ -0,0 +1,25 @@
+{
+ "name": "@trezor/utils",
+ "version": "1.0.0",
+ "author": "Trezor ",
+ "homepage": "https://github.com/trezor/trezor-suite/packages/utils",
+ "description": "A collection of typescript utils that are intended to be used across trezor-suite monorepo.",
+ "license": "SEE LICENSE IN LICENSE.md",
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/trezor/trezor-suite.git"
+ },
+ "bugs": {
+ "url": "https://github.com/trezor/trezor-suite/issues"
+ },
+ "main": "./lib/index.js",
+ "files": ["lib/"],
+ "types": "lib/index.d.ts",
+ "typings": "lib/index.d.ts",
+ "scripts": {
+ "lint": "eslint '**/*{.ts,.tsx}'",
+ "test:unit": "jest --verbose -c jest.config.js",
+ "type-check": "tsc --project tsconfig.json",
+ "build:lib": "rimraf lib && tsc --p ./tsconfig.lib.json"
+ }
+}
diff --git a/packages/suite/src/utils/suite/array.ts b/packages/utils/src/arrayPartition.ts
similarity index 78%
rename from packages/suite/src/utils/suite/array.ts
rename to packages/utils/src/arrayPartition.ts
index 75f1656bc99..fe735f3405a 100644
--- a/packages/suite/src/utils/suite/array.ts
+++ b/packages/utils/src/arrayPartition.ts
@@ -4,10 +4,9 @@
* @param condition Condition for inclusion in the first part.
* @returns Array of two arrays - the items in the first array satisfy the condition and the rest is in the second array. Preserving original order.
*/
-export function partition(array: T[], condition: (elem: T) => boolean): [T[], T[]] {
- return array.reduce(
+export const arrayPartition = (array: T[], condition: (elem: T) => boolean): [T[], T[]] =>
+ array.reduce(
([pass, fail], elem) =>
condition(elem) ? [[...pass, elem], fail] : [pass, [...fail, elem]],
[[], []] as [T[], T[]],
);
-}
diff --git a/packages/suite/src/utils/suite/file.ts b/packages/utils/src/bytesToHumanReadable.ts
similarity index 58%
rename from packages/suite/src/utils/suite/file.ts
rename to packages/utils/src/bytesToHumanReadable.ts
index 8ba243032bc..b5be25b7c2c 100644
--- a/packages/suite/src/utils/suite/file.ts
+++ b/packages/utils/src/bytesToHumanReadable.ts
@@ -1,6 +1,11 @@
const units = ['B', 'KB', 'MB', 'GB', 'TB'];
-export const toHumanReadable = (bytes: number): string => {
+/**
+ *
+ * @param bytes amount fo bytes
+ * @returns String with the human redable size of bytes
+ */
+export const bytesToHumanReadable = (bytes: number): string => {
let size = Math.abs(bytes);
let i = 0;
diff --git a/packages/utils/src/capitalizeFirstLetter.ts b/packages/utils/src/capitalizeFirstLetter.ts
new file mode 100644
index 00000000000..ba4a60be0e1
--- /dev/null
+++ b/packages/utils/src/capitalizeFirstLetter.ts
@@ -0,0 +1 @@
+export const capitalizeFirstLetter = (s: string) => s.charAt(0).toUpperCase() + s.slice(1);
diff --git a/packages/utils/src/countBytesInString.ts b/packages/utils/src/countBytesInString.ts
new file mode 100644
index 00000000000..a750924b08c
--- /dev/null
+++ b/packages/utils/src/countBytesInString.ts
@@ -0,0 +1 @@
+export const countBytesInString = (input: string) => encodeURI(input).split(/%..|./).length - 1;
diff --git a/packages/blockchain-link/src/utils/deferred.ts b/packages/utils/src/createDeferred.ts
similarity index 60%
rename from packages/blockchain-link/src/utils/deferred.ts
rename to packages/utils/src/createDeferred.ts
index 440cd6a4058..33ac848c83b 100644
--- a/packages/blockchain-link/src/utils/deferred.ts
+++ b/packages/utils/src/createDeferred.ts
@@ -1,8 +1,5 @@
-export function create(id: number | string): Deferred {
- // intentionally ignore below lines in test coverage, they will be overridden in promise creation
- /* istanbul ignore next */
+export const createDeferred = (id?: P) => {
let localResolve: (t: T) => void = () => {};
- /* istanbul ignore next */
let localReject: (e?: Error) => void = () => {};
const promise: Promise = new Promise((resolve, reject) => {
@@ -16,11 +13,14 @@ export function create(id: number | string): Deferred {
reject: localReject,
promise,
};
-}
+};
-export interface Deferred {
- id: number | string;
+export interface Deferred {
+ id: P;
promise: Promise;
resolve: (t: T) => void;
reject: (e: Error) => void;
}
+
+// unwrap promise response from Deferred
+export type DeferredResponse = D extends Deferred ? R : never;
diff --git a/packages/utils/src/getNumberFromPixelString.ts b/packages/utils/src/getNumberFromPixelString.ts
new file mode 100644
index 00000000000..cf11f31566f
--- /dev/null
+++ b/packages/utils/src/getNumberFromPixelString.ts
@@ -0,0 +1,2 @@
+export const getNumberFromPixelString = (size: string): number =>
+ parseInt(size.replace('px', ''), 10);
diff --git a/packages/utils/src/getRandomNumberInRange.ts b/packages/utils/src/getRandomNumberInRange.ts
new file mode 100644
index 00000000000..045e0789027
--- /dev/null
+++ b/packages/utils/src/getRandomNumberInRange.ts
@@ -0,0 +1,2 @@
+export const getRandomNumberInRange = (min: number, max: number) =>
+ Math.floor(Math.random() * (max - min + 1)) + min;
diff --git a/packages/utils/src/getWeakRandomId.ts b/packages/utils/src/getWeakRandomId.ts
new file mode 100644
index 00000000000..e8b000799d4
--- /dev/null
+++ b/packages/utils/src/getWeakRandomId.ts
@@ -0,0 +1,8 @@
+export const getWeakRandomId = (length: number) => {
+ let id = '';
+ const list = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
+ for (let i = 0; i < length; i++) {
+ id += list.charAt(Math.floor(Math.random() * list.length));
+ }
+ return id;
+};
diff --git a/packages/utils/src/hasUppercaseLetter.ts b/packages/utils/src/hasUppercaseLetter.ts
new file mode 100644
index 00000000000..c743ad8fe2e
--- /dev/null
+++ b/packages/utils/src/hasUppercaseLetter.ts
@@ -0,0 +1,3 @@
+const HAS_UPPERCASE_LATER_REGEXP = new RegExp('^(.*[A-Z].*)$');
+
+export const hasUppercaseLetter = (value: string) => HAS_UPPERCASE_LATER_REGEXP.test(value);
diff --git a/packages/utils/src/index.ts b/packages/utils/src/index.ts
new file mode 100644
index 00000000000..8ccb6f3e9fb
--- /dev/null
+++ b/packages/utils/src/index.ts
@@ -0,0 +1,13 @@
+export * from './createDeferred';
+export * from './arrayPartition';
+export * from './bytesToHumanReadable';
+export * from './mergeObject';
+export * from './getWeakRandomId';
+export * from './getRandomNumberInRange';
+export * from './capitalizeFirstLetter';
+export * from './countBytesInString';
+export * from './truncateMiddle';
+export * from './getNumberFromPixelString';
+export * from './isUrl';
+export * from './hasUppercaseLetter';
+export * from './isAscii';
diff --git a/packages/utils/src/isAscii.ts b/packages/utils/src/isAscii.ts
new file mode 100644
index 00000000000..8b3e3b23d70
--- /dev/null
+++ b/packages/utils/src/isAscii.ts
@@ -0,0 +1,4 @@
+export function isAscii(value?: string): boolean {
+ if (!value) return true;
+ return /^[\x00-\x7F]*$/.test(value); // eslint-disable-line no-control-regex
+}
diff --git a/packages/utils/src/isUrl.ts b/packages/utils/src/isUrl.ts
new file mode 100644
index 00000000000..afe462c4142
--- /dev/null
+++ b/packages/utils/src/isUrl.ts
@@ -0,0 +1,4 @@
+const URL_REGEXP =
+ /^(http|ws)s?:\/\/[a-z0-9]([a-z0-9.-]+)?(:[0-9]{1,5})?((\/)?(([a-z0-9-_])+(\/)?)+)$/i;
+
+export const isUrl = (value: string): boolean => URL_REGEXP.test(value);
diff --git a/packages/suite/src/utils/suite/mergeObj.ts b/packages/utils/src/mergeObject.ts
similarity index 75%
rename from packages/suite/src/utils/suite/mergeObj.ts
rename to packages/utils/src/mergeObject.ts
index d101d33a99a..b781927814f 100644
--- a/packages/suite/src/utils/suite/mergeObj.ts
+++ b/packages/utils/src/mergeObject.ts
@@ -1,10 +1,10 @@
-export const mergeObj = (
+export const mergeObject = (
target: Record,
source: Record | string | boolean | number,
) => {
Object.keys(source).forEach(key => {
if (source instanceof Object && source[key] instanceof Object) {
- Object.assign(source[key], mergeObj(target[key], source[key]));
+ Object.assign(source[key], mergeObject(target[key], source[key]));
}
});
// Join `target` and modified `source`
diff --git a/packages/suite/src/utils/suite/string.ts b/packages/utils/src/truncateMiddle.ts
similarity index 50%
rename from packages/suite/src/utils/suite/string.ts
rename to packages/utils/src/truncateMiddle.ts
index 019aa989290..6ac3b3e4028 100644
--- a/packages/suite/src/utils/suite/string.ts
+++ b/packages/utils/src/truncateMiddle.ts
@@ -1,9 +1,3 @@
-export const capitalizeFirstLetter = (s: string) => s.charAt(0).toUpperCase() + s.slice(1);
-
-export const countBytesInString = (input: string) => encodeURI(input).split(/%..|./).length - 1;
-
-export const getNumberFromPxString = (size: string): number => parseInt(size.replace('px', ''), 10);
-
export const truncateMiddle = (text: string, startChars: number, endChars: number) => {
if (text.length <= startChars + endChars) return text;
const start = text.substring(0, startChars);
diff --git a/packages/suite/src/utils/suite/__tests__/array.test.ts b/packages/utils/tests/arrayPartition.test.ts
similarity index 84%
rename from packages/suite/src/utils/suite/__tests__/array.test.ts
rename to packages/utils/tests/arrayPartition.test.ts
index 3f1a4b99bcb..9156f3f4d20 100644
--- a/packages/suite/src/utils/suite/__tests__/array.test.ts
+++ b/packages/utils/tests/arrayPartition.test.ts
@@ -1,6 +1,6 @@
-import { partition } from '../array';
+import { arrayPartition } from '../src/arrayPartition';
-describe('Array utils', () => {
+describe('arrayPartition', () => {
describe('partition of the array by condition', () => {
it('partition array of objects', () => {
const arrayOfObjects = [
@@ -10,7 +10,7 @@ describe('Array utils', () => {
{ value: true, name: 'd' },
{ value: false, name: 'e' },
];
- const partitionedObjects = partition(arrayOfObjects, element => element.value);
+ const partitionedObjects = arrayPartition(arrayOfObjects, element => element.value);
const [truthy, falsy] = partitionedObjects;
expect(arrayOfObjects.length).toEqual(truthy.length + falsy.length);
expect(partitionedObjects).toStrictEqual([
@@ -28,7 +28,7 @@ describe('Array utils', () => {
it('partition array of numbers', () => {
const arrayOfNumbers = [3, 1, 4, 5, 2, 1, 2];
- const partitionedNumbers = partition(arrayOfNumbers, element => element < 3);
+ const partitionedNumbers = arrayPartition(arrayOfNumbers, element => element < 3);
const [lessThanThree, fromThree] = partitionedNumbers;
expect(arrayOfNumbers.length).toEqual(lessThanThree.length + fromThree.length);
expect(partitionedNumbers).toStrictEqual([
@@ -39,7 +39,7 @@ describe('Array utils', () => {
it('partition array of strings', () => {
const arrayOfStrings = ['a', 'b', 'c', 'd', 'e', 'a'];
- const partitionedStrings = partition(
+ const partitionedStrings = arrayPartition(
arrayOfStrings,
element => element === 'a' || element === 'b',
);
diff --git a/packages/utils/tests/capitalizeFirstLetter.test.ts b/packages/utils/tests/capitalizeFirstLetter.test.ts
new file mode 100644
index 00000000000..7d34b4be0e4
--- /dev/null
+++ b/packages/utils/tests/capitalizeFirstLetter.test.ts
@@ -0,0 +1,6 @@
+import { capitalizeFirstLetter } from '../src/capitalizeFirstLetter';
+
+it('capitalizeFirstLetter', () => {
+ expect(capitalizeFirstLetter('god')).toBe('God');
+ expect(capitalizeFirstLetter('dog')).toBe('Dog');
+});
diff --git a/packages/utils/tests/countBytesInString.test.ts b/packages/utils/tests/countBytesInString.test.ts
new file mode 100644
index 00000000000..5bba01c9be4
--- /dev/null
+++ b/packages/utils/tests/countBytesInString.test.ts
@@ -0,0 +1,8 @@
+import { countBytesInString } from '../src/countBytesInString';
+
+it('countBytesInString', () => {
+ expect(countBytesInString('aaa')).toBe(3);
+ expect(countBytesInString('č')).toBe(2);
+ expect(countBytesInString('😀')).toBe(4);
+ expect(countBytesInString('+ěčřěžšýžřšý')).toBe(23);
+});
diff --git a/packages/utils/tests/getNumberFromPixelString.test.ts b/packages/utils/tests/getNumberFromPixelString.test.ts
new file mode 100644
index 00000000000..b76275a6789
--- /dev/null
+++ b/packages/utils/tests/getNumberFromPixelString.test.ts
@@ -0,0 +1,6 @@
+import { getNumberFromPixelString } from '../src/getNumberFromPixelString';
+
+it('getNumberFromPixelString', () => {
+ expect(getNumberFromPixelString('1px')).toBe(1);
+ expect(getNumberFromPixelString('1')).toBe(1);
+});
diff --git a/packages/utils/tests/getWeakRandomId.test.ts b/packages/utils/tests/getWeakRandomId.test.ts
new file mode 100644
index 00000000000..cd50e377035
--- /dev/null
+++ b/packages/utils/tests/getWeakRandomId.test.ts
@@ -0,0 +1,22 @@
+import { getWeakRandomId } from '../src/getWeakRandomId';
+
+describe('random', () => {
+ describe('getWeakRandomId', () => {
+ it('should return random id of fixed length', () => {
+ expect(getWeakRandomId(12)).toHaveLength(12);
+ });
+ it('should generate few results which should be unique', () => {
+ const ids: any = {};
+ for (let i = 0; i < 10; i++) {
+ const random = getWeakRandomId(10);
+ if (!ids[random]) {
+ ids[random] = 0;
+ }
+ ids[random]++;
+ }
+ Object.values(ids).forEach(id => {
+ expect(id).toEqual(1);
+ });
+ });
+ });
+});
diff --git a/packages/utils/tests/hasUppercaseLetter.test.ts b/packages/utils/tests/hasUppercaseLetter.test.ts
new file mode 100644
index 00000000000..10a19d5fbb4
--- /dev/null
+++ b/packages/utils/tests/hasUppercaseLetter.test.ts
@@ -0,0 +1,14 @@
+import { hasUppercaseLetter } from '../src/hasUppercaseLetter';
+
+describe('hasUppercaseLetter', () => {
+ it('hasUppercaseLetter', () => {
+ expect(hasUppercaseLetter('0')).toBe(false);
+ expect(hasUppercaseLetter('abc')).toBe(false);
+ expect(hasUppercaseLetter('abcD')).toBe(true);
+ expect(hasUppercaseLetter('Abcd')).toBe(true);
+ expect(hasUppercaseLetter('aBcd')).toBe(true);
+ expect(hasUppercaseLetter('123abc123')).toBe(false);
+ expect(hasUppercaseLetter('0x123abc456')).toBe(false);
+ expect(hasUppercaseLetter('0x123aBc456')).toBe(true);
+ });
+});
diff --git a/packages/utils/tests/isAscii.test.ts b/packages/utils/tests/isAscii.test.ts
new file mode 100644
index 00000000000..50af1f16982
--- /dev/null
+++ b/packages/utils/tests/isAscii.test.ts
@@ -0,0 +1,18 @@
+import { isAscii } from '../src/isAscii';
+
+describe('isAscii', () => {
+ describe('isAscii', () => {
+ it('should return true for ASCII only string', () => {
+ expect(isAscii('this is only ascii')).toEqual(true);
+ });
+
+ it('should return true when called without parameter', () => {
+ expect(isAscii()).toEqual(true);
+ });
+
+ it('should return false strings with non ASCII chars', () => {
+ expect(isAscii('¥')).toEqual(false);
+ expect(isAscii('železniční přejezd')).toEqual(false);
+ });
+ });
+});
diff --git a/packages/suite/src/utils/suite/__tests__/mergeObj.test.ts b/packages/utils/tests/mergeObject.test.ts
similarity index 74%
rename from packages/suite/src/utils/suite/__tests__/mergeObj.test.ts
rename to packages/utils/tests/mergeObject.test.ts
index ddd92e92d5f..a1e2ab838bc 100644
--- a/packages/suite/src/utils/suite/__tests__/mergeObj.test.ts
+++ b/packages/utils/tests/mergeObject.test.ts
@@ -1,6 +1,6 @@
-import { mergeObj } from '../mergeObj';
+import { mergeObject } from '../src/mergeObject';
-describe('mergeObj', () => {
+describe('mergeObject', () => {
it('should deep merge two objects', () => {
const target = {
a: 1,
@@ -22,6 +22,6 @@ describe('mergeObj', () => {
a: 1,
},
};
- expect(mergeObj(target, source)).toEqual(expected);
+ expect(mergeObject(target, source)).toEqual(expected);
});
});
diff --git a/packages/utils/tsconfig.json b/packages/utils/tsconfig.json
new file mode 100644
index 00000000000..04d111db55f
--- /dev/null
+++ b/packages/utils/tsconfig.json
@@ -0,0 +1,8 @@
+{
+ "extends": "./tsconfig.lib.json",
+ "compilerOptions": {
+ "noEmit": true,
+ },
+ "include": ["**/*.ts"],
+ "exclude": ["node_modules"]
+}
diff --git a/packages/utils/tsconfig.lib.json b/packages/utils/tsconfig.lib.json
new file mode 100644
index 00000000000..d95047d17df
--- /dev/null
+++ b/packages/utils/tsconfig.lib.json
@@ -0,0 +1,22 @@
+{
+ "compileOnSave": false,
+ "compilerOptions": {
+ "module": "commonjs",
+ "target": "esnext",
+ "outDir": "./lib",
+ "strict": true,
+ "declaration": true,
+ "removeComments": true,
+ "noUnusedLocals": true,
+ "noUnusedParameters": true,
+ "noErrorTruncation": true,
+ "types": ["jest", "node"]
+ },
+ "include": ["./src/index.ts", "./src/global.d.ts"],
+ "exclude": [
+ "node_modules",
+ "**/__tests__/**",
+ "**/__fixtures__/**",
+ "**/*.test.ts"
+ ]
+}