From a8d382657bf8be56d83ccd8df6b81e79c14f81fd Mon Sep 17 00:00:00 2001 From: Martin Homola Date: Fri, 31 Jan 2025 11:30:39 +0100 Subject: [PATCH] chore(trading): move common trading utils to suite-common --- .../wallet/trading/tradingCommonActions.ts | 3 +- .../modals/ReduxModal/ConfirmAddressModal.tsx | 2 +- .../trading/form/common/useTradingAccount.ts | 3 +- .../form/common/useTradingFiatValues.tsx | 7 +- .../form/common/useTradingFormActions.ts | 2 +- .../wallet/trading/form/useTradingBuyForm.tsx | 17 +- .../form/useTradingBuyFormDefaultValues.tsx | 3 +- .../trading/form/useTradingExchangeForm.ts | 15 +- .../useTradingExchangeFormDefaultValues.ts | 2 +- .../trading/form/useTradingPaymentMethod.tsx | 7 +- .../wallet/trading/form/useTradingSellForm.ts | 17 +- .../form/useTradingSellFormDefaultValues.ts | 3 +- .../trading/form/useTradingVerifyAccount.tsx | 10 +- .../hooks/wallet/trading/useTradingInfo.ts | 12 +- packages/suite/src/types/trading/trading.ts | 8 - .../suite/src/types/trading/tradingForm.ts | 8 +- .../trading/__fixtures__/exchangeUtils.ts | 2 +- .../trading/__fixtures__/tradingUtils.ts | 45 ----- .../trading/__tests__/tradingUtils.test.ts | 123 ------------ .../wallet/trading/tradingTypingUtils.ts | 6 +- .../src/utils/wallet/trading/tradingUtils.ts | 181 +----------------- .../tokens/common/TokensTable/TokenRow.tsx | 5 +- .../wallet/trading/common/TradingCoinLogo.tsx | 6 +- .../TradingFeaturedOffersItem.tsx | 3 +- .../TradingFormInputAccountOption.tsx | 7 +- .../TradingFormInputCryptoSelect.tsx | 14 +- .../common/TradingForm/TradingFormOffer.tsx | 3 +- .../TradingFormOffersSwitcherItem.tsx | 2 +- .../TradingOffers/TradingOffersItem.tsx | 5 +- .../TradingInfo/TradingInfoHeader.tsx | 2 +- .../TradingOfferExchangeSendApproval.tsx | 3 +- .../TradingVerify/TradingVerify.tsx | 3 +- .../TradingVerify/TradingVerifyOptions.tsx | 2 +- .../TradingVerifyOptionsItem.tsx | 2 +- .../TradingUtils/TradingUtilsProvider.tsx | 2 +- suite-common/trading/package.json | 5 +- .../trading/src/__fixtures__/buyUtils.ts | 114 +++++++++++ .../trading/src/__fixtures__/exchangeUtils.ts | 139 ++++++++++++++ .../trading/src/__fixtures__/sellUtils.ts | 50 +++++ .../trading/src/__fixtures__/utils.ts | 44 +++++ .../trading/src/__tests__/utils.test.ts | 133 +++++++++++++ suite-common/trading/src/constants.ts | 5 + suite-common/trading/src/index.ts | 2 + suite-common/trading/src/types.ts | 14 ++ suite-common/trading/src/utils.ts | 157 +++++++++++++++ suite-common/trading/tsconfig.json | 2 + yarn.lock | 3 + 47 files changed, 757 insertions(+), 446 deletions(-) create mode 100644 suite-common/trading/src/__fixtures__/buyUtils.ts create mode 100644 suite-common/trading/src/__fixtures__/exchangeUtils.ts create mode 100644 suite-common/trading/src/__fixtures__/sellUtils.ts create mode 100644 suite-common/trading/src/__fixtures__/utils.ts create mode 100644 suite-common/trading/src/__tests__/utils.test.ts create mode 100644 suite-common/trading/src/constants.ts create mode 100644 suite-common/trading/src/utils.ts diff --git a/packages/suite/src/actions/wallet/trading/tradingCommonActions.ts b/packages/suite/src/actions/wallet/trading/tradingCommonActions.ts index 7433cf80084..6a777e86c01 100644 --- a/packages/suite/src/actions/wallet/trading/tradingCommonActions.ts +++ b/packages/suite/src/actions/wallet/trading/tradingCommonActions.ts @@ -1,7 +1,7 @@ import { CryptoId } from 'invity-api'; import { notificationsActions } from '@suite-common/toast-notifications'; -import type { TradingType } from '@suite-common/trading'; +import { type TradingType, getUnusedAddressFromAccount } from '@suite-common/trading'; import { confirmAddressOnDeviceThunk, selectSelectedDevice, @@ -26,7 +26,6 @@ import { ComposedTransactionInfo } from 'src/reducers/wallet/tradingReducer'; import { Dispatch, GetState } from 'src/types/suite'; import { Account } from 'src/types/wallet'; import { submitRequestForm as envSubmitRequestForm } from 'src/utils/suite/env'; -import { getUnusedAddressFromAccount } from 'src/utils/wallet/trading/tradingUtils'; export type TradingCommonAction = | { diff --git a/packages/suite/src/components/suite/modals/ReduxModal/ConfirmAddressModal.tsx b/packages/suite/src/components/suite/modals/ReduxModal/ConfirmAddressModal.tsx index cfc498cdaaf..f87410c00b2 100644 --- a/packages/suite/src/components/suite/modals/ReduxModal/ConfirmAddressModal.tsx +++ b/packages/suite/src/components/suite/modals/ReduxModal/ConfirmAddressModal.tsx @@ -1,5 +1,6 @@ import { useCallback } from 'react'; +import { cryptoIdToSymbol } from '@suite-common/trading'; import { getDisplaySymbol, getNetwork, @@ -17,7 +18,6 @@ import { import { useSelector } from 'src/hooks/suite'; import { useTradingInfo } from 'src/hooks/wallet/trading/useTradingInfo'; import { selectAccountIncludingChosenInTrading } from 'src/reducers/wallet/selectedAccountReducer'; -import { cryptoIdToSymbol } from 'src/utils/wallet/trading/tradingUtils'; import { ConfirmActionModal } from './DeviceContextModal/ConfirmActionModal'; diff --git a/packages/suite/src/hooks/wallet/trading/form/common/useTradingAccount.ts b/packages/suite/src/hooks/wallet/trading/form/common/useTradingAccount.ts index a7add8c6556..bb89a3cc527 100644 --- a/packages/suite/src/hooks/wallet/trading/form/common/useTradingAccount.ts +++ b/packages/suite/src/hooks/wallet/trading/form/common/useTradingAccount.ts @@ -1,11 +1,12 @@ import { useState } from 'react'; +import { mapTestnetSymbol } from '@suite-common/trading'; import { selectAccounts, selectSelectedDevice } from '@suite-common/wallet-core'; import { Account, SelectedAccountLoaded } from '@suite-common/wallet-types'; import { isTestnet } from '@suite-common/wallet-utils'; import { useSelector } from 'src/hooks/suite'; -import { mapTestnetSymbol, tradingGetSortedAccounts } from 'src/utils/wallet/trading/tradingUtils'; +import { tradingGetSortedAccounts } from 'src/utils/wallet/trading/tradingUtils'; interface TradingUseAccountProps { tradingAccount: Account | undefined; diff --git a/packages/suite/src/hooks/wallet/trading/form/common/useTradingFiatValues.tsx b/packages/suite/src/hooks/wallet/trading/form/common/useTradingFiatValues.tsx index 8490b181c78..333b94bf3bb 100644 --- a/packages/suite/src/hooks/wallet/trading/form/common/useTradingFiatValues.tsx +++ b/packages/suite/src/hooks/wallet/trading/form/common/useTradingFiatValues.tsx @@ -2,6 +2,7 @@ import { useCallback, useEffect } from 'react'; import { FiatCurrencyCode } from 'invity-api'; +import { cryptoIdToSymbol, mapTestnetSymbol } from '@suite-common/trading'; import { NetworkSymbol, networks } from '@suite-common/wallet-config'; import { selectFiatRatesByFiatRateKey, updateFiatRatesThunk } from '@suite-common/wallet-core'; import { FiatRatesResult, Rate, Timestamp, TokenAddress } from '@suite-common/wallet-types'; @@ -11,11 +12,7 @@ import { useDispatch, useSelector } from 'src/hooks/suite'; import { useBitcoinAmountUnit } from 'src/hooks/wallet/useBitcoinAmountUnit'; import { selectLocalCurrency } from 'src/reducers/wallet/settingsReducer'; import { TradingAccountOptionsGroupOptionProps } from 'src/types/trading/trading'; -import { - cryptoIdToSymbol, - getTradingNetworkDecimals, - mapTestnetSymbol, -} from 'src/utils/wallet/trading/tradingUtils'; +import { getTradingNetworkDecimals } from 'src/utils/wallet/trading/tradingUtils'; interface TradingBalanceProps { sendCryptoSelect?: TradingAccountOptionsGroupOptionProps; diff --git a/packages/suite/src/hooks/wallet/trading/form/common/useTradingFormActions.ts b/packages/suite/src/hooks/wallet/trading/form/common/useTradingFormActions.ts index cde3272cade..e95997c7585 100644 --- a/packages/suite/src/hooks/wallet/trading/form/common/useTradingFormActions.ts +++ b/packages/suite/src/hooks/wallet/trading/form/common/useTradingFormActions.ts @@ -5,6 +5,7 @@ import { useDebounce } from 'react-use'; import { FiatCurrencyCode } from 'invity-api'; import { isChanged } from '@suite-common/suite-utils'; +import { cryptoIdToSymbol } from '@suite-common/trading'; import { selectAccounts, selectSelectedDevice } from '@suite-common/wallet-core'; import { amountToSmallestUnit, @@ -38,7 +39,6 @@ import { } from 'src/types/trading/tradingForm'; import { tradingGetExchangeReceiveCryptoId } from 'src/utils/wallet/trading/exchangeUtils'; import { - cryptoIdToSymbol, getAddressAndTokenFromAccountOptionsGroupProps, getTradingNetworkDecimals, tradingGetSortedAccounts, diff --git a/packages/suite/src/hooks/wallet/trading/form/useTradingBuyForm.tsx b/packages/suite/src/hooks/wallet/trading/form/useTradingBuyForm.tsx index 1d0ef7abd56..300caa6400c 100644 --- a/packages/suite/src/hooks/wallet/trading/form/useTradingBuyForm.tsx +++ b/packages/suite/src/hooks/wallet/trading/form/useTradingBuyForm.tsx @@ -6,7 +6,14 @@ import useDebounce from 'react-use/lib/useDebounce'; import { isChanged } from '@suite-common/suite-utils'; import { notificationsActions } from '@suite-common/toast-notifications'; -import { type TradingBuyType, invityAPI } from '@suite-common/trading'; +import { + type TradingBuyType, + addIdsToQuotes, + cryptoIdToNetwork, + filterQuotesAccordingTags, + invityAPI, + tradingGetSuccessQuotes, +} from '@suite-common/trading'; import { networks } from '@suite-common/wallet-config'; import { formatAmount } from '@suite-common/wallet-utils'; import { isDesktop } from '@trezor/env-utils'; @@ -37,13 +44,7 @@ import { UseTradingFormProps } from 'src/types/trading/trading'; import { TradingBuyFormContextProps, TradingBuyFormProps } from 'src/types/trading/tradingForm'; import type { AmountLimitProps } from 'src/utils/suite/validation'; import { createQuoteLink, createTxLink, getAmountLimits } from 'src/utils/wallet/trading/buyUtils'; -import { - addIdsToQuotes, - cryptoIdToNetwork, - filterQuotesAccordingTags, - getTradingNetworkDecimals, - tradingGetSuccessQuotes, -} from 'src/utils/wallet/trading/tradingUtils'; +import { getTradingNetworkDecimals } from 'src/utils/wallet/trading/tradingUtils'; import { useTradingInitializer } from './common/useTradingInitializer'; diff --git a/packages/suite/src/hooks/wallet/trading/form/useTradingBuyFormDefaultValues.tsx b/packages/suite/src/hooks/wallet/trading/form/useTradingBuyFormDefaultValues.tsx index d51acf9ece9..284b59fa5b3 100644 --- a/packages/suite/src/hooks/wallet/trading/form/useTradingBuyFormDefaultValues.tsx +++ b/packages/suite/src/hooks/wallet/trading/form/useTradingBuyFormDefaultValues.tsx @@ -2,6 +2,7 @@ import { useMemo } from 'react'; import { CryptoId, FiatCurrencyCode } from 'invity-api'; +import { getDefaultCountry } from '@suite-common/trading'; import { networks } from '@suite-common/wallet-config'; import { BuyInfo } from 'src/actions/wallet/tradingBuyActions'; @@ -14,7 +15,7 @@ import { useTradingInfo } from 'src/hooks/wallet/trading/useTradingInfo'; import { TradingPaymentMethodListProps } from 'src/types/trading/trading'; import { TradingBuyFormDefaultValuesProps } from 'src/types/trading/tradingForm'; import { Account } from 'src/types/wallet'; -import { buildFiatOption, getDefaultCountry } from 'src/utils/wallet/trading/tradingUtils'; +import { buildFiatOption } from 'src/utils/wallet/trading/tradingUtils'; export const useTradingBuyFormDefaultValues = ( accountSymbol: Account['symbol'], diff --git a/packages/suite/src/hooks/wallet/trading/form/useTradingExchangeForm.ts b/packages/suite/src/hooks/wallet/trading/form/useTradingExchangeForm.ts index d77f97b7bdb..b6c1bc61783 100644 --- a/packages/suite/src/hooks/wallet/trading/form/useTradingExchangeForm.ts +++ b/packages/suite/src/hooks/wallet/trading/form/useTradingExchangeForm.ts @@ -11,7 +11,13 @@ import useDebounce from 'react-use/lib/useDebounce'; import { isChanged } from '@suite-common/suite-utils'; import { notificationsActions } from '@suite-common/toast-notifications'; -import { type TradingExchangeType, invityAPI } from '@suite-common/trading'; +import { + type TradingExchangeType, + addIdsToQuotes, + getUnusedAddressFromAccount, + invityAPI, + tradingGetSuccessQuotes, +} from '@suite-common/trading'; import { networks } from '@suite-common/wallet-config'; import { Account } from '@suite-common/wallet-types'; import { amountToSmallestUnit, formatAmount, toFiatCurrency } from '@suite-common/wallet-utils'; @@ -52,12 +58,7 @@ import { getSuccessQuotesOrdered, tradingGetExchangeReceiveCryptoId, } from 'src/utils/wallet/trading/exchangeUtils'; -import { - addIdsToQuotes, - getTradingNetworkDecimals, - getUnusedAddressFromAccount, - tradingGetSuccessQuotes, -} from 'src/utils/wallet/trading/tradingUtils'; +import { getTradingNetworkDecimals } from 'src/utils/wallet/trading/tradingUtils'; import { useTradingInitializer } from './common/useTradingInitializer'; diff --git a/packages/suite/src/hooks/wallet/trading/form/useTradingExchangeFormDefaultValues.ts b/packages/suite/src/hooks/wallet/trading/form/useTradingExchangeFormDefaultValues.ts index 80f5f44c158..6e3530d00b7 100644 --- a/packages/suite/src/hooks/wallet/trading/form/useTradingExchangeFormDefaultValues.ts +++ b/packages/suite/src/hooks/wallet/trading/form/useTradingExchangeFormDefaultValues.ts @@ -1,5 +1,6 @@ import { useMemo } from 'react'; +import { cryptoIdToSymbol } from '@suite-common/trading'; import { DEFAULT_PAYMENT, DEFAULT_VALUES } from '@suite-common/wallet-constants'; import { FormState, Output } from '@suite-common/wallet-types'; @@ -28,7 +29,6 @@ import { Account } from 'src/types/wallet'; import { tradingGetExchangeReceiveCryptoId } from 'src/utils/wallet/trading/exchangeUtils'; import { buildFiatOption, - cryptoIdToSymbol, getAddressAndTokenFromAccountOptionsGroupProps, } from 'src/utils/wallet/trading/tradingUtils'; diff --git a/packages/suite/src/hooks/wallet/trading/form/useTradingPaymentMethod.tsx b/packages/suite/src/hooks/wallet/trading/form/useTradingPaymentMethod.tsx index 39cc8c464cb..ad08bbf447c 100644 --- a/packages/suite/src/hooks/wallet/trading/form/useTradingPaymentMethod.tsx +++ b/packages/suite/src/hooks/wallet/trading/form/useTradingPaymentMethod.tsx @@ -1,11 +1,12 @@ import { useCallback } from 'react'; +import type { TradingTradeMapProps } from '@suite-common/trading'; + import { useSelector } from 'src/hooks/suite'; import { TradingPaymentMethodListProps, TradingPaymentMethodProps, TradingTradeBuySellType, - TradingTradeDetailMapProps, } from 'src/types/trading/trading'; import { TradingPaymentMethodHookProps } from 'src/types/trading/tradingForm'; @@ -14,7 +15,7 @@ const useTradingPaymentMethod = < >(): TradingPaymentMethodHookProps => { const paymentMethods = useSelector(state => state.wallet.trading.info.paymentMethods); - const getPaymentMethods = (quotes: TradingTradeDetailMapProps[T][]) => { + const getPaymentMethods = (quotes: TradingTradeMapProps[T][]) => { const newPaymentMethods: TradingPaymentMethodListProps[] = []; quotes.forEach(quote => { @@ -33,7 +34,7 @@ const useTradingPaymentMethod = < const getQuotesByPaymentMethod = useCallback( ( - quotes: TradingTradeDetailMapProps[T][] | undefined, + quotes: TradingTradeMapProps[T][] | undefined, currentPaymentMethod: TradingPaymentMethodProps, ) => { if (!quotes) return; diff --git a/packages/suite/src/hooks/wallet/trading/form/useTradingSellForm.ts b/packages/suite/src/hooks/wallet/trading/form/useTradingSellForm.ts index a919b425508..f014737b9dc 100644 --- a/packages/suite/src/hooks/wallet/trading/form/useTradingSellForm.ts +++ b/packages/suite/src/hooks/wallet/trading/form/useTradingSellForm.ts @@ -6,7 +6,14 @@ import useDebounce from 'react-use/lib/useDebounce'; import { isChanged } from '@suite-common/suite-utils'; import { notificationsActions } from '@suite-common/toast-notifications'; -import { type TradingSellType, invityAPI } from '@suite-common/trading'; +import { + type TradingSellType, + addIdsToQuotes, + filterQuotesAccordingTags, + getUnusedAddressFromAccount, + invityAPI, + tradingGetSuccessQuotes, +} from '@suite-common/trading'; import { networks } from '@suite-common/wallet-config'; import { amountToSmallestUnit, formatAmount } from '@suite-common/wallet-utils'; import { EventType, analytics } from '@trezor/suite-analytics'; @@ -49,13 +56,7 @@ import { import { TradeSell } from 'src/types/wallet/tradingCommonTypes'; import type { AmountLimitProps } from 'src/utils/suite/validation'; import { createQuoteLink, getAmountLimits } from 'src/utils/wallet/trading/sellUtils'; -import { - addIdsToQuotes, - filterQuotesAccordingTags, - getTradingNetworkDecimals, - getUnusedAddressFromAccount, - tradingGetSuccessQuotes, -} from 'src/utils/wallet/trading/tradingUtils'; +import { getTradingNetworkDecimals } from 'src/utils/wallet/trading/tradingUtils'; import { useTradingInitializer } from './common/useTradingInitializer'; diff --git a/packages/suite/src/hooks/wallet/trading/form/useTradingSellFormDefaultValues.ts b/packages/suite/src/hooks/wallet/trading/form/useTradingSellFormDefaultValues.ts index b2d0b70f7a2..73c1ece1c95 100644 --- a/packages/suite/src/hooks/wallet/trading/form/useTradingSellFormDefaultValues.ts +++ b/packages/suite/src/hooks/wallet/trading/form/useTradingSellFormDefaultValues.ts @@ -1,5 +1,6 @@ import { useMemo } from 'react'; +import { cryptoIdToSymbol, getDefaultCountry } from '@suite-common/trading'; import { DEFAULT_PAYMENT, DEFAULT_VALUES } from '@suite-common/wallet-constants'; import { FormState, Output } from '@suite-common/wallet-types'; @@ -15,9 +16,7 @@ import { TradingSellFormDefaultValuesProps } from 'src/types/trading/tradingForm import { Account } from 'src/types/wallet'; import { buildFiatOption, - cryptoIdToSymbol, getAddressAndTokenFromAccountOptionsGroupProps, - getDefaultCountry, } from 'src/utils/wallet/trading/tradingUtils'; export const useTradingSellFormDefaultValues = ( diff --git a/packages/suite/src/hooks/wallet/trading/form/useTradingVerifyAccount.tsx b/packages/suite/src/hooks/wallet/trading/form/useTradingVerifyAccount.tsx index 19f13e8a1a1..4526f6c1ffa 100644 --- a/packages/suite/src/hooks/wallet/trading/form/useTradingVerifyAccount.tsx +++ b/packages/suite/src/hooks/wallet/trading/form/useTradingVerifyAccount.tsx @@ -2,6 +2,11 @@ import { useEffect, useMemo, useState } from 'react'; import { useForm } from 'react-hook-form'; import { TrezorDevice } from '@suite-common/suite-types'; +import { + cryptoIdToSymbol, + getUnusedAddressFromAccount, + parseCryptoId, +} from '@suite-common/trading'; import { selectSelectedDevice } from '@suite-common/wallet-core'; import { Account } from '@suite-common/wallet-types'; import { filterReceiveAccounts } from '@suite-common/wallet-utils'; @@ -19,11 +24,6 @@ import { TradingVerifyFormAccountOptionProps, TradingVerifyFormProps, } from 'src/types/trading/tradingVerify'; -import { - cryptoIdToSymbol, - getUnusedAddressFromAccount, - parseCryptoId, -} from 'src/utils/wallet/trading/tradingUtils'; const getSelectAccountOptions = ( suiteReceiveAccounts: Account[] | undefined, diff --git a/packages/suite/src/hooks/wallet/trading/useTradingInfo.ts b/packages/suite/src/hooks/wallet/trading/useTradingInfo.ts index b6df2916882..bd15e11b3a6 100644 --- a/packages/suite/src/hooks/wallet/trading/useTradingInfo.ts +++ b/packages/suite/src/hooks/wallet/trading/useTradingInfo.ts @@ -2,6 +2,12 @@ import { useCallback } from 'react'; import { CoinInfo, CryptoId } from 'invity-api'; +import { + cryptoIdToNetwork, + isCryptoIdForNativeToken, + parseCryptoId, + testnetToProdCryptoId, +} from '@suite-common/trading'; import { NetworkSymbolExtended, getDisplaySymbol, @@ -16,12 +22,6 @@ import { TradingCryptoSelectOptionProps, TradingInfoProps, } from 'src/types/trading/trading'; -import { - cryptoIdToNetwork, - isCryptoIdForNativeToken, - parseCryptoId, - testnetToProdCryptoId, -} from 'src/utils/wallet/trading/tradingUtils'; const supportedAddressValidatorSymbols = new Set( addressValidator.getCurrencies().map(c => c.symbol), diff --git a/packages/suite/src/types/trading/trading.ts b/packages/suite/src/types/trading/trading.ts index 6664da93acd..cf4a3f4f419 100644 --- a/packages/suite/src/types/trading/trading.ts +++ b/packages/suite/src/types/trading/trading.ts @@ -4,7 +4,6 @@ import { BuyTrade, CryptoId, ExchangeProviderInfo, - ExchangeTrade, FiatCurrencyCode, SellFiatTrade, SellProviderInfo, @@ -78,13 +77,6 @@ export type TradingTradeMapProps = { export type TradingTradeDetailBuySellType = BuyTrade | SellFiatTrade; -export type TradingTradeDetailMapProps = { - buy: BuyTrade; - sell: SellFiatTrade; - exchange: ExchangeTrade; -}; -export type TradingTradeBuySellDetailMapProps = Omit; - export type TradingTradeInfoMapProps = { buy: BuyInfo; sell: SellInfo; diff --git a/packages/suite/src/types/trading/tradingForm.ts b/packages/suite/src/types/trading/tradingForm.ts index 8d067a3b09f..9daeddbe516 100644 --- a/packages/suite/src/types/trading/tradingForm.ts +++ b/packages/suite/src/types/trading/tradingForm.ts @@ -16,6 +16,7 @@ import type { TradingExchangeType, TradingPaymentMethodType, TradingSellType, + TradingTradeMapProps, TradingTradeType, TradingType, } from '@suite-common/trading'; @@ -59,7 +60,6 @@ import { TradingGetProvidersInfoProps, TradingPaymentMethodListProps, TradingPaymentMethodProps, - TradingTradeDetailMapProps, TradingTradeSellExchangeType, } from 'src/types/trading/trading'; import type { Account } from 'src/types/wallet'; @@ -279,11 +279,11 @@ export type TradingFormContextValues = TradingFormMapProp export type TradingPaymentMethodHookProps = { paymentMethods: TradingPaymentMethodListProps[]; - getPaymentMethods: (quotes: TradingTradeDetailMapProps[T][]) => TradingPaymentMethodListProps[]; + getPaymentMethods: (quotes: TradingTradeMapProps[T][]) => TradingPaymentMethodListProps[]; getQuotesByPaymentMethod: ( - quotes: TradingTradeDetailMapProps[T][] | undefined, + quotes: TradingTradeMapProps[T][] | undefined, currentPaymentMethod: TradingPaymentMethodProps, - ) => TradingTradeDetailMapProps[T][] | undefined; + ) => TradingTradeMapProps[T][] | undefined; }; export interface TradingFormInputDefaultProps { diff --git a/packages/suite/src/utils/wallet/trading/__fixtures__/exchangeUtils.ts b/packages/suite/src/utils/wallet/trading/__fixtures__/exchangeUtils.ts index bb1a65fcec5..843952700e9 100644 --- a/packages/suite/src/utils/wallet/trading/__fixtures__/exchangeUtils.ts +++ b/packages/suite/src/utils/wallet/trading/__fixtures__/exchangeUtils.ts @@ -5,7 +5,7 @@ import { ExchangeInfo } from 'src/actions/wallet/tradingExchangeActions'; const litecoin = 'litecoin' as CryptoId; const bitcoin = 'bitcoin' as CryptoId; const ethereum = 'ethereum' as CryptoId; -const cronoseth = 'ethereum__0xa0b73e1ff0b80914ab6fe0444e65848c4c34450b' as CryptoId; +const cronoseth = 'ethereum--0xa0b73e1ff0b80914ab6fe0444e65848c4c34450b' as CryptoId; const tickers1 = [ethereum, 'monero', bitcoin] as CryptoId[]; const tickers2 = [ethereum, bitcoin] as CryptoId[]; diff --git a/packages/suite/src/utils/wallet/trading/__fixtures__/tradingUtils.ts b/packages/suite/src/utils/wallet/trading/__fixtures__/tradingUtils.ts index 725c8c87dd0..2a1d2e67f97 100644 --- a/packages/suite/src/utils/wallet/trading/__fixtures__/tradingUtils.ts +++ b/packages/suite/src/utils/wallet/trading/__fixtures__/tradingUtils.ts @@ -5,51 +5,6 @@ import { Account } from '@suite-common/wallet-types'; import { TradingAccountOptionsGroupOptionProps } from 'src/types/trading/trading'; -export const accountBtc = { - index: 1, - accountType: 'segwit', - networkType: 'bitcoin', - symbol: 'btc', - addresses: { - unused: [ - { - address: '177BUDVZqTTzK1Fogqcrfbb5ketHEUDGSJ', - transfers: 0, - path: "m/44'/0'/3'/0/0", - }, - ], - }, -}; - -export const accountEth = { - index: 1, - accountType: 'normal', - networkType: 'ethereum', - symbol: 'eth', - descriptor: '0x2e0DC981d301cdd443C3987cf19Eb9671CB99ddC', - path: "m/44'/60'/0'/0/1", - tokens: [ - { - type: 'ERC20', - contract: '0x1234123412341234123412341234123412341234', - symbol: 'usdt', - decimals: 18, - }, - { - type: 'ERC20', - contract: '0x1234123412341234123412341234123412341235', - symbol: 'usdc', - decimals: 18, - }, - { - type: 'ERC20', - contract: '0x1234123412341234123412341234123412341236', - symbol: 'other', - decimals: 18, - }, - ], -}; - export const coinDefinitions: TokenDefinitions[DefinitionType.COIN] = { error: false, data: [ diff --git a/packages/suite/src/utils/wallet/trading/__tests__/tradingUtils.test.ts b/packages/suite/src/utils/wallet/trading/__tests__/tradingUtils.test.ts index 2ad3a754ff5..73397f65386 100644 --- a/packages/suite/src/utils/wallet/trading/__tests__/tradingUtils.test.ts +++ b/packages/suite/src/utils/wallet/trading/__tests__/tradingUtils.test.ts @@ -8,23 +8,14 @@ import * as SELL_FIXTURE from 'src/utils/wallet/trading/__fixtures__/sellUtils'; import { FIXTURE_ACCOUNTS, FIXTURE_ACCOUNT_OPTIONS, - accountBtc, - accountEth, coinDefinitions, } from 'src/utils/wallet/trading/__fixtures__/tradingUtils'; import { - addIdsToQuotes, buildFiatOption, - filterQuotesAccordingTags, getAddressAndTokenFromAccountOptionsGroupProps, getBestRatedQuote, getCountryLabelParts, - getTagAndInfoNote, getTradeTypeByRoute, - getUnusedAddressFromAccount, - isCryptoIdForNativeToken, - mapTestnetSymbol, - testnetToProdCryptoId, tradingBuildAccountOptions, tradingGetAccountLabel, tradingGetAmountLabels, @@ -42,18 +33,6 @@ describe('trading utils', () => { expect(buildFiatOption('czk')).toStrictEqual({ value: 'czk', label: 'CZK' }); }); - it('getUnusedAddressFromAccount', () => { - expect(getUnusedAddressFromAccount(accountBtc as Account)).toStrictEqual({ - address: '177BUDVZqTTzK1Fogqcrfbb5ketHEUDGSJ', - path: "m/44'/0'/3'/0/0", - }); - - expect(getUnusedAddressFromAccount(accountEth as Account)).toStrictEqual({ - address: '0x2e0DC981d301cdd443C3987cf19Eb9671CB99ddC', - path: "m/44'/60'/0'/0/1", - }); - }); - it('getCountryLabelParts', () => { expect(getCountryLabelParts('🇨🇿 Czech Republic')).toStrictEqual({ flag: '🇨🇿', @@ -65,70 +44,6 @@ describe('trading utils', () => { }); }); - it('mapTestnetCryptoCurrency', () => { - expect(mapTestnetSymbol('btc')).toStrictEqual('btc'); - expect(mapTestnetSymbol('eth')).toStrictEqual('eth'); - expect(mapTestnetSymbol('test')).toStrictEqual('btc'); - expect(mapTestnetSymbol('txrp')).toStrictEqual('xrp'); - }); - - it('getTagAndInfoNote', () => { - expect(getTagAndInfoNote({})).toStrictEqual({ infoNote: '', tag: '' }); - expect(getTagAndInfoNote({ infoNote: '' })).toStrictEqual({ infoNote: '', tag: '' }); - expect(getTagAndInfoNote({ infoNote: 'Foo' })).toStrictEqual({ infoNote: 'Foo', tag: '' }); - expect(getTagAndInfoNote({ infoNote: ' #Foo' })).toStrictEqual({ - infoNote: '', - tag: 'Foo', - }); - expect(getTagAndInfoNote({ infoNote: 'Foo#Bar' })).toStrictEqual({ - infoNote: 'Foo#Bar', - tag: '', - }); - expect(getTagAndInfoNote({ infoNote: '#Foo' })).toStrictEqual({ infoNote: '', tag: 'Foo' }); - expect(getTagAndInfoNote({ infoNote: '# Foo' })).toStrictEqual({ - infoNote: '', - tag: ' Foo', - }); - expect(getTagAndInfoNote({ infoNote: '##Bar' })).toStrictEqual({ - infoNote: 'Bar', - tag: '', - }); - expect(getTagAndInfoNote({ infoNote: '#Foo#Bar' })).toStrictEqual({ - infoNote: 'Bar', - tag: 'Foo', - }); - expect(getTagAndInfoNote({ infoNote: ' #Foo#Bar \t' })).toStrictEqual({ - infoNote: 'Bar', - tag: 'Foo', - }); - }); - - it('filterQuotesAccordingTags', () => { - const quotes = [ - ...BUY_FIXTURE.MIN_MAX_QUOTES_OK, - ...BUY_FIXTURE.ALTERNATIVE_QUOTES, - ...SELL_FIXTURE.MIN_MAX_QUOTES_HIGH, - ]; - - expect(filterQuotesAccordingTags([])).toStrictEqual([]); - expect(filterQuotesAccordingTags(quotes).length).toStrictEqual( - quotes.filter(q => !q.tags || !q.tags.includes('alternativeCurrency')).length, - ); - }); - - it('addIdsToQuotes', () => { - const quotes = [...BUY_FIXTURE.MIN_MAX_QUOTES_OK]; - const quotesExchange = [...EXCHANGE_FIXTURE.MIN_MAX_QUOTES_OK]; - - expect(addIdsToQuotes([], 'buy')).toStrictEqual([]); - expect(addIdsToQuotes(quotes, 'buy').length).toStrictEqual( - quotes.filter(q => q.orderId && q.paymentId).length, - ); - expect(addIdsToQuotes(quotesExchange, 'exchange').length).toStrictEqual( - quotesExchange.filter(q => q.orderId).length, - ); - }); - describe('getBestRatedQuote', () => { it('buy trades (shuffled with error)', () => { expect(getBestRatedQuote(BUY_FIXTURE.MIN_MAX_QUOTES_OK, 'buy')).toStrictEqual( @@ -295,25 +210,6 @@ describe('trading utils', () => { expect(tradingGetAccountLabel('USDT', false)).toBe('USDT'); }); - it('testnetToProdCryptoId', () => { - expect(testnetToProdCryptoId('test-bitcoin' as CryptoId)).toEqual('bitcoin'); - expect(testnetToProdCryptoId('bitcoin' as CryptoId)).toEqual('bitcoin'); - - expect(testnetToProdCryptoId('test-ripple' as CryptoId)).toEqual('ripple'); - expect(testnetToProdCryptoId('ripple' as CryptoId)).toEqual('ripple'); - - expect( - testnetToProdCryptoId( - 'test-ethereum--0x1234123412341234123412341234123412341236' as CryptoId, - ), - ).toEqual('ethereum--0x1234123412341234123412341234123412341236'); - expect( - testnetToProdCryptoId( - 'ethereum--0x1234123412341234123412341234123412341236' as CryptoId, - ), - ).toEqual('ethereum--0x1234123412341234123412341234123412341236'); - }); - it('getAddressAndTokenFromAccountOptionsGroupProps - testing correct returning value fot setting FormState to send currency', () => { FIXTURE_ACCOUNT_OPTIONS.forEach(item => { expect(getAddressAndTokenFromAccountOptionsGroupProps(item.option)).toEqual( @@ -322,25 +218,6 @@ describe('trading utils', () => { }); }); - it('isCryptoIdForNativeToken - test if token is L2 native token', () => { - expect(isCryptoIdForNativeToken('ethereum' as CryptoId)).toEqual(false); - expect( - isCryptoIdForNativeToken( - 'ethereum--0x1234123412341234123412341234123412341236' as CryptoId, - ), - ).toEqual(false); - expect( - isCryptoIdForNativeToken( - 'ethereum--0x0000000000000000000000000000000000000000' as CryptoId, - ), - ).toEqual(true); - expect( - isCryptoIdForNativeToken( - 'base--0x0000000000000000000000000000000000000000' as CryptoId, - ), - ).toEqual(true); - }); - it('getTradeTypeByRoute - testing correct returning trade section according to route', () => { expect(getTradeTypeByRoute('wallet-trading-buy')).toEqual('buy'); expect(getTradeTypeByRoute('wallet-trading-buy-detail')).toEqual('buy'); diff --git a/packages/suite/src/utils/wallet/trading/tradingTypingUtils.ts b/packages/suite/src/utils/wallet/trading/tradingTypingUtils.ts index 6508b400386..78875888f55 100644 --- a/packages/suite/src/utils/wallet/trading/tradingTypingUtils.ts +++ b/packages/suite/src/utils/wallet/trading/tradingTypingUtils.ts @@ -4,6 +4,7 @@ import type { TradingBuyType, TradingExchangeType, TradingSellType, + TradingTradeMapProps, TradingTradeType, TradingType, } from '@suite-common/trading'; @@ -16,7 +17,6 @@ import { TradingGetFiatCurrenciesProps, TradingGetPaymentMethodProps, TradingGetProvidersInfoProps, - TradingTradeDetailMapProps, } from 'src/types/trading/trading'; import { TradingFormContextValues, TradingFormMapProps } from 'src/types/trading/tradingForm'; @@ -114,9 +114,9 @@ export const getFiatCurrenciesProps = ( export const getSelectQuoteTyped = ( context: TradingFormContextValues, -): ((quote: TradingTradeDetailMapProps[typeof context.type]) => void) => { +): ((quote: TradingTradeMapProps[typeof context.type]) => void) => { const selectQuote = context.selectQuote as ( - quote: TradingTradeDetailMapProps[typeof context.type], + quote: TradingTradeMapProps[typeof context.type], ) => void; return selectQuote; diff --git a/packages/suite/src/utils/wallet/trading/tradingUtils.ts b/packages/suite/src/utils/wallet/trading/tradingUtils.ts index 356a72b4d30..0f1362ccdf9 100644 --- a/packages/suite/src/utils/wallet/trading/tradingUtils.ts +++ b/packages/suite/src/utils/wallet/trading/tradingUtils.ts @@ -1,15 +1,15 @@ -import { BuyTrade, CryptoId, ExchangeTrade, SellFiatTrade } from 'invity-api'; -import { v4 as uuidv4 } from 'uuid'; +import { CryptoId } from 'invity-api'; import { DefinitionType, isTokenDefinitionKnown } from '@suite-common/token-definitions'; -import { type TradingTradeType, type TradingType, regional } from '@suite-common/trading'; +import { + type TradingTradeType, + type TradingType, + cryptoIdToSymbol, + toTokenCryptoId, +} from '@suite-common/trading'; import { Network, - NetworkSymbol, - getCoingeckoId, getNetwork, - getNetworkByCoingeckoId, - getNetworkByTradeCryptoId, getNetworkDisplaySymbol, getNetworkDisplaySymbolName, getNetworkFeatures, @@ -31,58 +31,10 @@ import { TradingGetAmountLabelsProps, TradingGetAmountLabelsReturnProps, TradingGetSortedAccountsProps, - TradingTradeBuySellDetailMapProps, - TradingTradeBuySellType, - TradingTradeDetailMapProps, } from 'src/types/trading/trading'; import { Account } from 'src/types/wallet'; -export const cryptoPlatformSeparator = '--'; -/** - * Used for for L2 networks (e.g. base, op) - */ -export const contractAddressForNativeToken = '0x0000000000000000000000000000000000000000'; - -interface ParsedCryptoId { - networkId: CryptoId; - contractAddress: string | undefined; -} - -export const parseCryptoId = (cryptoId: CryptoId): ParsedCryptoId => { - const parts = cryptoId.split(cryptoPlatformSeparator); - - return { networkId: parts[0] as CryptoId, contractAddress: parts[1] }; -}; - -export const cryptoIdToNetwork = (cryptoId: CryptoId): Network | undefined => { - const { networkId, contractAddress } = parseCryptoId(cryptoId); - - return contractAddress - ? getNetworkByCoingeckoId(networkId) - : getNetworkByTradeCryptoId(networkId); -}; - -export const cryptoIdToSymbol = (cryptoId: CryptoId): NetworkSymbol | undefined => - cryptoIdToNetwork(cryptoId)?.symbol; - -export const toTokenCryptoId = (symbol: NetworkSymbol, contractAddress: string): CryptoId => - `${getCoingeckoId(symbol)}${cryptoPlatformSeparator}${contractAddress}` as CryptoId; - -/** Convert testnet cryptoId to prod cryptoId (test-bitcoin -> bitcoin) */ -export const testnetToProdCryptoId = (cryptoId: CryptoId): CryptoId => { - const { networkId, contractAddress } = parseCryptoId(cryptoId); - - return ((networkId.split('test-')?.[1] ?? networkId) + - (contractAddress ? `${cryptoPlatformSeparator}${contractAddress}` : '')) as CryptoId; -}; - -export const isCryptoIdForNativeToken = (cryptoId: CryptoId) => { - const { contractAddress } = parseCryptoId(cryptoId); - - return contractAddress === contractAddressForNativeToken; -}; - -export interface TradingGetDecimalsProps { +interface TradingGetDecimalsProps { sendCryptoSelect?: TradingAccountOptionsGroupOptionProps; network?: Network | null; } @@ -98,49 +50,11 @@ export const getTradingNetworkDecimals = ({ return network?.decimals ?? 8; }; -/** @deprecated */ -const suiteToInvitySymbols: { - suiteSymbol: string; - invitySymbol: string; -}[] = []; - export const buildFiatOption = (currency: string) => ({ value: currency, label: currency.toUpperCase(), }); -/** @deprecated */ -export const invityApiSymbolToSymbol = (symbol?: string) => { - if (!symbol) return 'UNKNOWN'; - const lowercaseSymbol = symbol.toLowerCase(); - const result = suiteToInvitySymbols.find(s => s.invitySymbol === lowercaseSymbol); - - return result ? result.suiteSymbol : lowercaseSymbol; -}; - -export const getUnusedAddressFromAccount = (account: Account) => { - switch (account.networkType) { - case 'cardano': - case 'bitcoin': { - const firstUnused = account.addresses?.unused[0]; - if (firstUnused) { - return { address: firstUnused.address, path: firstUnused.path }; - } - - return { address: undefined, path: undefined }; - } - case 'ripple': - case 'ethereum': - case 'solana': { - return { - address: account.descriptor, - path: account.path, - }; - } - // no default - } -}; - export const getCountryLabelParts = (label: string) => { try { const parts = label.split(' '); @@ -217,85 +131,6 @@ export const getComposeAddressPlaceholder = async ( } }; -export const mapTestnetSymbol = ( - symbol: NetworkSymbol, -): Exclude => { - if (symbol === 'test') return 'btc'; - if (symbol === 'tsep') return 'eth'; - if (symbol === 'thol') return 'eth'; - if (symbol === 'txrp') return 'xrp'; - if (symbol === 'tada') return 'ada'; - - return symbol; -}; - -export const getTagAndInfoNote = (quote: { infoNote?: string }) => { - let tag = ''; - let infoNote = (quote?.infoNote || '').trim(); - if (infoNote.startsWith('#')) { - const splitNote = infoNote?.split('#') || []; - if (splitNote.length === 3) { - // infoNote contains "#badge_text#info_note_text" - [, tag, infoNote] = splitNote; - } else if (splitNote.length === 2) { - // infoNote contains "#badge_text" - infoNote = ''; - tag = splitNote.pop() || ''; - } - } - - return { tag, infoNote }; -}; - -export const tradingGetSuccessQuotes = ( - quotes: TradingTradeDetailMapProps[T][] | undefined, -) => (quotes ? quotes.filter(quote => quote.error === undefined) : undefined); - -export const getDefaultCountry = (country: string = regional.UNKNOWN_COUNTRY) => { - const label = regional.countriesMap.get(country); - - if (!label) - return { - label: regional.countriesMap.get(regional.UNKNOWN_COUNTRY)!, - value: regional.UNKNOWN_COUNTRY, - }; - - return { - label, - value: country, - }; -}; - -export const filterQuotesAccordingTags = ( - allQuotes: TradingTradeBuySellDetailMapProps[T][], -) => allQuotes.filter(q => !q.tags || !q.tags.includes('alternativeCurrency')); - -// fill orderId for all, paymentId for sell and buy, quoteId for exchange -export const addIdsToQuotes = ( - allQuotes: TradingTradeDetailMapProps[T][] | undefined, - type: TradingType, -): TradingTradeDetailMapProps[T][] => { - if (!allQuotes) allQuotes = []; - - allQuotes.forEach(q => { - const sellBuyQuote = ['buy', 'sell'].includes(type) - ? (q as BuyTrade | SellFiatTrade) - : null; - - if (sellBuyQuote && !sellBuyQuote.paymentId) { - sellBuyQuote.paymentId = uuidv4(); - } - - if (type === 'exchange' && !q.quoteId) { - (q as ExchangeTrade).quoteId = uuidv4(); - } - - q.orderId = uuidv4(); - }); - - return allQuotes; -}; - export const getBestRatedQuote = ( quotes: TradingTradeType[] | undefined, type: TradingType, diff --git a/packages/suite/src/views/wallet/tokens/common/TokensTable/TokenRow.tsx b/packages/suite/src/views/wallet/tokens/common/TokensTable/TokenRow.tsx index b8fa4951d76..fe82e2fe270 100644 --- a/packages/suite/src/views/wallet/tokens/common/TokensTable/TokenRow.tsx +++ b/packages/suite/src/views/wallet/tokens/common/TokensTable/TokenRow.tsx @@ -7,6 +7,7 @@ import { selectIsSpecificCoinDefinitionKnown, tokenDefinitionsActions, } from '@suite-common/token-definitions'; +import { getUnusedAddressFromAccount, toTokenCryptoId } from '@suite-common/trading'; import { Network, getCoingeckoId } from '@suite-common/wallet-config'; import { selectSelectedDevice, sendFormActions } from '@suite-common/wallet-core'; import { Account, TokenAddress } from '@suite-common/wallet-types'; @@ -56,10 +57,6 @@ import { selectIsUnhideTokenModalShown, } from 'src/reducers/suite/suiteReducer'; import { formatTokenSymbol } from 'src/utils/wallet/tokenUtils'; -import { - getUnusedAddressFromAccount, - toTokenCryptoId, -} from 'src/utils/wallet/trading/tradingUtils'; import { BlurUrls } from '../BlurUrls'; diff --git a/packages/suite/src/views/wallet/trading/common/TradingCoinLogo.tsx b/packages/suite/src/views/wallet/trading/common/TradingCoinLogo.tsx index 57520140dbe..43fdeffe7d5 100644 --- a/packages/suite/src/views/wallet/trading/common/TradingCoinLogo.tsx +++ b/packages/suite/src/views/wallet/trading/common/TradingCoinLogo.tsx @@ -1,14 +1,10 @@ import styled from 'styled-components'; +import { cryptoIdToNetwork, isCryptoIdForNativeToken, parseCryptoId } from '@suite-common/trading'; import { AssetLogo } from '@trezor/components'; import { CoinLogo } from '@trezor/product-components'; import { TradingCoinLogoProps } from 'src/types/trading/trading'; -import { - cryptoIdToNetwork, - isCryptoIdForNativeToken, - parseCryptoId, -} from 'src/utils/wallet/trading/tradingUtils'; const Wrapper = styled.div``; diff --git a/packages/suite/src/views/wallet/trading/common/TradingFeaturedOffers/TradingFeaturedOffersItem.tsx b/packages/suite/src/views/wallet/trading/common/TradingFeaturedOffers/TradingFeaturedOffersItem.tsx index dfc4765fc91..9f611ceb139 100644 --- a/packages/suite/src/views/wallet/trading/common/TradingFeaturedOffers/TradingFeaturedOffersItem.tsx +++ b/packages/suite/src/views/wallet/trading/common/TradingFeaturedOffers/TradingFeaturedOffersItem.tsx @@ -1,7 +1,7 @@ import { SellFiatTrade } from 'invity-api'; import styled, { useTheme } from 'styled-components'; -import type { TradingTradeType, TradingType } from '@suite-common/trading'; +import { type TradingTradeType, type TradingType, getTagAndInfoNote } from '@suite-common/trading'; import { Badge, Button, Card, Text } from '@trezor/components'; import { SCREEN_QUERY } from '@trezor/components/src/config/variables'; import { spacings, spacingsPx } from '@trezor/theme'; @@ -17,7 +17,6 @@ import { isTradingExchangeContext, isTradingSellContext, } from 'src/utils/wallet/trading/tradingTypingUtils'; -import { getTagAndInfoNote } from 'src/utils/wallet/trading/tradingUtils'; import { TradingFeaturedOffersAmounts } from 'src/views/wallet/trading/common/TradingFeaturedOffers/TradingFeaturedOffersAmounts'; import { TradingFeaturedOffersPaymentInfo } from 'src/views/wallet/trading/common/TradingFeaturedOffers/TradingFeaturedOffersPaymentInfo'; import { TradingUtilsProvider } from 'src/views/wallet/trading/common/TradingUtils/TradingUtilsProvider'; diff --git a/packages/suite/src/views/wallet/trading/common/TradingForm/TradingFormInput/TradingFormInputAccountOption.tsx b/packages/suite/src/views/wallet/trading/common/TradingForm/TradingFormInput/TradingFormInputAccountOption.tsx index 061447997f3..09cc533cedd 100644 --- a/packages/suite/src/views/wallet/trading/common/TradingForm/TradingFormInput/TradingFormInputAccountOption.tsx +++ b/packages/suite/src/views/wallet/trading/common/TradingForm/TradingFormInput/TradingFormInputAccountOption.tsx @@ -1,3 +1,4 @@ +import { cryptoIdToNetwork, parseCryptoId } from '@suite-common/trading'; import { amountToSmallestUnit } from '@suite-common/wallet-utils'; import { Badge, Row, Text } from '@trezor/components'; import { spacings } from '@trezor/theme'; @@ -8,11 +9,7 @@ import { TradingAccountOptionsGroupOptionProps, TradingAccountsOptionsGroupProps, } from 'src/types/trading/trading'; -import { - cryptoIdToNetwork, - parseCryptoId, - tradingGetAccountLabel, -} from 'src/utils/wallet/trading/tradingUtils'; +import { tradingGetAccountLabel } from 'src/utils/wallet/trading/tradingUtils'; import { TradingCoinLogo } from 'src/views/wallet/trading/common/TradingCoinLogo'; interface TradingFormInputAccountOptionProps { diff --git a/packages/suite/src/views/wallet/trading/common/TradingForm/TradingFormInput/TradingFormInputCryptoSelect.tsx b/packages/suite/src/views/wallet/trading/common/TradingForm/TradingFormInput/TradingFormInputCryptoSelect.tsx index fd4238d87e3..1711ff51333 100644 --- a/packages/suite/src/views/wallet/trading/common/TradingForm/TradingFormInput/TradingFormInputCryptoSelect.tsx +++ b/packages/suite/src/views/wallet/trading/common/TradingForm/TradingFormInput/TradingFormInputCryptoSelect.tsx @@ -3,6 +3,12 @@ import { Controller } from 'react-hook-form'; import { CryptoId } from 'invity-api'; +import { + CRYPTO_PLATFORM_SEPARATOR, + cryptoIdToNetwork, + isCryptoIdForNativeToken, + parseCryptoId, +} from '@suite-common/trading'; import { Network, NetworkSymbol, getNetworkByCoingeckoId } from '@suite-common/wallet-config'; import { Badge, Row, Select, Text } from '@trezor/components'; import { @@ -35,12 +41,6 @@ import { TradingFormInputCryptoSelectProps, } from 'src/types/trading/tradingForm'; import { isTradingExchangeContext } from 'src/utils/wallet/trading/tradingTypingUtils'; -import { - cryptoIdToNetwork, - cryptoPlatformSeparator, - isCryptoIdForNativeToken, - parseCryptoId, -} from 'src/utils/wallet/trading/tradingUtils'; import { TradingCoinLogo } from '../../TradingCoinLogo'; @@ -144,7 +144,7 @@ export const TradingFormInputCryptoSelect = < const isNativeTokenSymbol = isCryptoIdForNativeToken(coingeckoId as CryptoId); const tokenCryptoId = isNativeTokenSymbol ? coingeckoId - : `${coingeckoId}${cryptoPlatformSeparator}${contractAddress}`; + : `${coingeckoId}${CRYPTO_PLATFORM_SEPARATOR}${contractAddress}`; const cryptoId = contractAddress ? tokenCryptoId : coingeckoId; diff --git a/packages/suite/src/views/wallet/trading/common/TradingForm/TradingFormOffer.tsx b/packages/suite/src/views/wallet/trading/common/TradingForm/TradingFormOffer.tsx index 12f5b6337c2..a420556b1b9 100644 --- a/packages/suite/src/views/wallet/trading/common/TradingForm/TradingFormOffer.tsx +++ b/packages/suite/src/views/wallet/trading/common/TradingForm/TradingFormOffer.tsx @@ -2,7 +2,7 @@ import { useState } from 'react'; import { CryptoId } from 'invity-api'; -import type { TradingTradeType, TradingType } from '@suite-common/trading'; +import { type TradingTradeType, type TradingType, parseCryptoId } from '@suite-common/trading'; import { Button, Column, Paragraph, Row, TextButton } from '@trezor/components'; import { spacings } from '@trezor/theme'; @@ -21,7 +21,6 @@ import { } from 'src/utils/wallet/trading/tradingTypingUtils'; import { getBestRatedQuote, - parseCryptoId, tradingGetAmountLabels, tradingGetRoundedFiatAmount, tradingGetSectionActionLabel, diff --git a/packages/suite/src/views/wallet/trading/common/TradingForm/TradingFormOffersSwitcherItem.tsx b/packages/suite/src/views/wallet/trading/common/TradingForm/TradingFormOffersSwitcherItem.tsx index 618bac2d2b0..c020787c784 100644 --- a/packages/suite/src/views/wallet/trading/common/TradingForm/TradingFormOffersSwitcherItem.tsx +++ b/packages/suite/src/views/wallet/trading/common/TradingForm/TradingFormOffersSwitcherItem.tsx @@ -1,7 +1,7 @@ import { ExchangeTrade } from 'invity-api'; import styled from 'styled-components'; -import { TradingUtilsProvidersProps } from '@suite-common/trading'; +import { type TradingUtilsProvidersProps } from '@suite-common/trading'; import { Badge, Radio, Row, Text, Tooltip, useElevation } from '@trezor/components'; import { Elevation, borders, mapElevationToBackground, spacings, spacingsPx } from '@trezor/theme'; diff --git a/packages/suite/src/views/wallet/trading/common/TradingOffers/TradingOffersItem.tsx b/packages/suite/src/views/wallet/trading/common/TradingOffers/TradingOffersItem.tsx index b2f7be586d0..8e6c4f3a6d9 100644 --- a/packages/suite/src/views/wallet/trading/common/TradingOffers/TradingOffersItem.tsx +++ b/packages/suite/src/views/wallet/trading/common/TradingOffers/TradingOffersItem.tsx @@ -1,6 +1,7 @@ import { SellFiatTrade } from 'invity-api'; import styled, { useTheme } from 'styled-components'; +import { TradingTradeMapProps, getTagAndInfoNote } from '@suite-common/trading'; import { Badge, Button, Card, Row, Text } from '@trezor/components'; import { SCREEN_QUERY } from '@trezor/components/src/config/variables'; import { spacings, spacingsPx } from '@trezor/theme'; @@ -8,7 +9,6 @@ import { spacings, spacingsPx } from '@trezor/theme'; import { Translation } from 'src/components/suite'; import { useTradingDeviceDisconnected } from 'src/hooks/wallet/trading/form/common/useTradingDeviceDisconnected'; import { useTradingFormContext } from 'src/hooks/wallet/trading/form/useTradingCommonForm'; -import { TradingTradeDetailMapProps } from 'src/types/trading/trading'; import { getCryptoQuoteAmountProps, getProvidersInfoProps, @@ -16,7 +16,6 @@ import { isTradingExchangeContext, isTradingSellContext, } from 'src/utils/wallet/trading/tradingTypingUtils'; -import { getTagAndInfoNote } from 'src/utils/wallet/trading/tradingUtils'; import { TradingTestWrapper } from 'src/views/wallet/trading'; import { TradingUtilsKyc } from 'src/views/wallet/trading/common/TradingUtils/TradingUtilsKyc'; import { TradingUtilsPrice } from 'src/views/wallet/trading/common/TradingUtils/TradingUtilsPrice'; @@ -89,7 +88,7 @@ const ButtonWrapper = styled.div` `; export interface TradingOffersItemProps { - quote: TradingTradeDetailMapProps[keyof TradingTradeDetailMapProps]; + quote: TradingTradeMapProps[keyof TradingTradeMapProps]; isBestRate: boolean; } diff --git a/packages/suite/src/views/wallet/trading/common/TradingSelectedOffer/TradingInfo/TradingInfoHeader.tsx b/packages/suite/src/views/wallet/trading/common/TradingSelectedOffer/TradingInfo/TradingInfoHeader.tsx index ce673e9dead..3a9aa5904ce 100644 --- a/packages/suite/src/views/wallet/trading/common/TradingSelectedOffer/TradingInfo/TradingInfoHeader.tsx +++ b/packages/suite/src/views/wallet/trading/common/TradingSelectedOffer/TradingInfo/TradingInfoHeader.tsx @@ -1,12 +1,12 @@ import { CryptoId } from 'invity-api'; +import { parseCryptoId } from '@suite-common/trading'; import { getDisplaySymbol } from '@suite-common/wallet-config'; import { Row, Text } from '@trezor/components'; import { spacings } from '@trezor/theme'; import { Translation } from 'src/components/suite'; import { useTradingInfo } from 'src/hooks/wallet/trading/useTradingInfo'; -import { parseCryptoId } from 'src/utils/wallet/trading/tradingUtils'; import { TradingCoinLogo } from 'src/views/wallet/trading/common/TradingCoinLogo'; interface TradingInfoHeaderProps { diff --git a/packages/suite/src/views/wallet/trading/common/TradingSelectedOffer/TradingOfferExchange/TradingOfferExchangeSendApproval.tsx b/packages/suite/src/views/wallet/trading/common/TradingSelectedOffer/TradingOfferExchange/TradingOfferExchangeSendApproval.tsx index 0c0e3442c99..1cfa118d9fb 100644 --- a/packages/suite/src/views/wallet/trading/common/TradingSelectedOffer/TradingOfferExchange/TradingOfferExchangeSendApproval.tsx +++ b/packages/suite/src/views/wallet/trading/common/TradingSelectedOffer/TradingOfferExchange/TradingOfferExchangeSendApproval.tsx @@ -3,7 +3,7 @@ import { useState } from 'react'; import { DexApprovalType } from 'invity-api'; import styled from 'styled-components'; -import type { TradingExchangeType } from '@suite-common/trading'; +import { type TradingExchangeType, cryptoIdToSymbol, parseCryptoId } from '@suite-common/trading'; import { Banner, Button, @@ -26,7 +26,6 @@ import { useTradingFormContext } from 'src/hooks/wallet/trading/form/useTradingC import { useTradingExchangeWatchSendApproval } from 'src/hooks/wallet/trading/form/useTradingExchangeWatchSendApproval'; import { useTradingInfo } from 'src/hooks/wallet/trading/useTradingInfo'; import { useTradingNavigation } from 'src/hooks/wallet/useTradingNavigation'; -import { cryptoIdToSymbol, parseCryptoId } from 'src/utils/wallet/trading/tradingUtils'; // add APPROVED means no approval request is necessary type ExtendedDexApprovalType = DexApprovalType | 'APPROVED'; diff --git a/packages/suite/src/views/wallet/trading/common/TradingSelectedOffer/TradingVerify/TradingVerify.tsx b/packages/suite/src/views/wallet/trading/common/TradingSelectedOffer/TradingVerify/TradingVerify.tsx index 4c51c14308a..c564de5c011 100644 --- a/packages/suite/src/views/wallet/trading/common/TradingSelectedOffer/TradingVerify/TradingVerify.tsx +++ b/packages/suite/src/views/wallet/trading/common/TradingSelectedOffer/TradingVerify/TradingVerify.tsx @@ -2,7 +2,7 @@ import { useEffect } from 'react'; import { CryptoId } from 'invity-api'; -import { TradingExchangeType } from '@suite-common/trading'; +import { TradingExchangeType, cryptoIdToNetwork } from '@suite-common/trading'; import { getDisplaySymbol } from '@suite-common/wallet-config'; import { isHexValid, isInteger } from '@suite-common/wallet-utils'; import addressValidator from '@trezor/address-validator'; @@ -21,7 +21,6 @@ import { isTradingBuyContext, isTradingExchangeContext, } from 'src/utils/wallet/trading/tradingTypingUtils'; -import { cryptoIdToNetwork } from 'src/utils/wallet/trading/tradingUtils'; import { ConfirmedOnTrezor } from 'src/views/wallet/trading/common/ConfirmedOnTrezor'; import { TradingAddressOptions } from 'src/views/wallet/trading/common/TradingAddressOptions'; import { TradingVerifyOptions } from 'src/views/wallet/trading/common/TradingSelectedOffer/TradingVerify/TradingVerifyOptions'; diff --git a/packages/suite/src/views/wallet/trading/common/TradingSelectedOffer/TradingVerify/TradingVerifyOptions.tsx b/packages/suite/src/views/wallet/trading/common/TradingSelectedOffer/TradingVerify/TradingVerifyOptions.tsx index 41131d5aca1..7ab7543615a 100644 --- a/packages/suite/src/views/wallet/trading/common/TradingSelectedOffer/TradingVerify/TradingVerifyOptions.tsx +++ b/packages/suite/src/views/wallet/trading/common/TradingSelectedOffer/TradingVerify/TradingVerifyOptions.tsx @@ -1,3 +1,4 @@ +import { parseCryptoId } from '@suite-common/trading'; import { getDisplaySymbol } from '@suite-common/wallet-config'; import { Select } from '@trezor/components'; @@ -7,7 +8,6 @@ import { TradingVerifyFormAccountOptionProps, TradingVerifyOptionsProps, } from 'src/types/trading/tradingVerify'; -import { parseCryptoId } from 'src/utils/wallet/trading/tradingUtils'; import { TradingVerifyOptionsItem } from 'src/views/wallet/trading/common/TradingSelectedOffer/TradingVerify/TradingVerifyOptionsItem'; export const TradingVerifyOptions = ({ diff --git a/packages/suite/src/views/wallet/trading/common/TradingSelectedOffer/TradingVerify/TradingVerifyOptionsItem.tsx b/packages/suite/src/views/wallet/trading/common/TradingSelectedOffer/TradingVerify/TradingVerifyOptionsItem.tsx index 0ae7786db9f..8acc8568e9e 100644 --- a/packages/suite/src/views/wallet/trading/common/TradingSelectedOffer/TradingVerify/TradingVerifyOptionsItem.tsx +++ b/packages/suite/src/views/wallet/trading/common/TradingSelectedOffer/TradingVerify/TradingVerifyOptionsItem.tsx @@ -1,5 +1,6 @@ import styled from 'styled-components'; +import { parseCryptoId } from '@suite-common/trading'; import { getNetwork } from '@suite-common/wallet-config'; import { Column, Icon, Row, variables } from '@trezor/components'; import { CoinLogo } from '@trezor/product-components'; @@ -11,7 +12,6 @@ import { useTradingFormContext } from 'src/hooks/wallet/trading/form/useTradingC import { useTradingInfo } from 'src/hooks/wallet/trading/useTradingInfo'; import { TradingVerifyOptionsItemProps } from 'src/types/trading/tradingVerify'; import { isTradingExchangeContext } from 'src/utils/wallet/trading/tradingTypingUtils'; -import { parseCryptoId } from 'src/utils/wallet/trading/tradingUtils'; import { TradingBalance } from 'src/views/wallet/trading/common/TradingBalance'; const AccountName = styled.div` diff --git a/packages/suite/src/views/wallet/trading/common/TradingUtils/TradingUtilsProvider.tsx b/packages/suite/src/views/wallet/trading/common/TradingUtils/TradingUtilsProvider.tsx index 6410ab2c80a..110f1279e8a 100644 --- a/packages/suite/src/views/wallet/trading/common/TradingUtils/TradingUtilsProvider.tsx +++ b/packages/suite/src/views/wallet/trading/common/TradingUtils/TradingUtilsProvider.tsx @@ -1,6 +1,6 @@ import styled from 'styled-components'; -import { TradingUtilsProvidersProps, invityAPI } from '@suite-common/trading'; +import { type TradingUtilsProvidersProps, invityAPI } from '@suite-common/trading'; import { Row } from '@trezor/components'; import { spacings } from '@trezor/theme'; diff --git a/suite-common/trading/package.json b/suite-common/trading/package.json index 237f9ea625e..57ffb0d742a 100644 --- a/suite-common/trading/package.json +++ b/suite-common/trading/package.json @@ -10,8 +10,11 @@ "type-check": "yarn g:tsc --build" }, "dependencies": { + "@suite-common/wallet-config": "workspace:*", + "@suite-common/wallet-types": "workspace:*", "@trezor/env-utils": "workspace:*", - "@trezor/utils": "workspace:*" + "@trezor/utils": "workspace:*", + "uuid": "^11.0.4" }, "devDependencies": { "@types/invity-api": "^1.1.2" diff --git a/suite-common/trading/src/__fixtures__/buyUtils.ts b/suite-common/trading/src/__fixtures__/buyUtils.ts new file mode 100644 index 00000000000..46b2be0c6d9 --- /dev/null +++ b/suite-common/trading/src/__fixtures__/buyUtils.ts @@ -0,0 +1,114 @@ +import { BuyTrade, CryptoId } from 'invity-api'; + +const bitcoin = 'bitcoin' as CryptoId; + +export const MIN_MAX_QUOTES_OK: BuyTrade[] = [ + { + fiatStringAmount: '10', + fiatCurrency: 'EUR', + receiveCurrency: bitcoin, + receiveStringAmount: '0.0005', + rate: 20000, + quoteId: 'fc12d4c4-9078-4175-becd-90fc58a3145c', + error: 'Amount too low, minimum is EUR 25 or BTC 0.002.', + exchange: 'cexdirect', + minFiat: 25, + maxFiat: 1000, + minCrypto: 0.002, + maxCrypto: 0.10532, + paymentMethod: 'creditCard', + paymentId: 'e709df77-ee9e-4d12-98c2-84004a19c546', + }, + { + fiatStringAmount: '10', + fiatCurrency: 'EUR', + receiveCurrency: bitcoin, + receiveStringAmount: '0.0010001683607972866', + rate: 9998.316675433, + quoteId: 'ff259797-6cbe-4fea-8330-5181353f64a0', + exchange: 'mercuryo', + minFiat: 20, + maxFiat: 1999.96, + minCrypto: 0.002, + maxCrypto: 0.20003, + paymentMethod: 'creditCard', + }, + { + fiatStringAmount: '10', + fiatCurrency: 'EUR', + receiveCurrency: bitcoin, + receiveStringAmount: '0', + rate: 0, + error: 'Transaction amount too low. Please enter a value of 43 EUR or more.', + exchange: 'simplex', + minFiat: 43, + maxFiat: 17044, + minCrypto: 0.00415525, + maxCrypto: 1.66210137, + paymentMethod: 'creditCard', + }, +]; + +export const ALTERNATIVE_QUOTES: BuyTrade[] = [ + { + fiatStringAmount: '47.12', + fiatCurrency: 'EUR', + receiveCurrency: bitcoin, + receiveStringAmount: '0.004705020432603938', + rate: 10014.834297738, + quoteId: 'd369ba9e-7370-4a6e-87dc-aefd3851c735', + exchange: 'mercuryo', + minFiat: 20.03, + maxFiat: 2000.05, + minCrypto: 0.002, + maxCrypto: 0.19952, + paymentMethod: 'creditCard', + tags: ['alternativeCurrency'], + }, + { + fiatStringAmount: '47.12', + fiatCurrency: 'EUR', + receiveCurrency: bitcoin, + receiveStringAmount: '0.0041', + rate: 11492.682926829268, + quoteId: '53233267-8181-4151-9a67-9d8efc9a15db', + exchange: 'cexdirect', + minFiat: 25, + maxFiat: 1000, + minCrypto: 0.002, + maxCrypto: 0.1055, + paymentMethod: 'creditCard', + tags: ['alternativeCurrency'], + }, + { + fiatStringAmount: '47.12', + fiatCurrency: 'EUR', + receiveCurrency: bitcoin, + receiveStringAmount: '0.00391181', + rate: 12045.57481063753, + quoteId: 'd12bbe83-32a9-4dd9-abb4-0fdbddee70fb', + exchange: 'simplex', + validUntil: '2020-08-04T13:57:57.757Z', + minFiat: 43, + maxFiat: 17044, + minCrypto: 0.00416233, + maxCrypto: 1.66493314, + paymentMethod: 'creditCard', + tags: ['alternativeCurrency'], + }, + { + fiatStringAmount: '1234', + fiatCurrency: 'CZK', + receiveCurrency: bitcoin, + receiveStringAmount: '0.00386933', + rate: 318918.26233482285, + quoteId: 'c9ff416d-2c52-4cc1-ba9c-7c61cb136f1c', + exchange: 'simplex', + validUntil: '2020-08-04T13:57:57.743Z', + minFiat: 1116, + maxFiat: 446382, + minCrypto: 0.00416233, + maxCrypto: 1.66493314, + paymentMethod: 'creditCard', + }, +]; diff --git a/suite-common/trading/src/__fixtures__/exchangeUtils.ts b/suite-common/trading/src/__fixtures__/exchangeUtils.ts new file mode 100644 index 00000000000..7da59318ca0 --- /dev/null +++ b/suite-common/trading/src/__fixtures__/exchangeUtils.ts @@ -0,0 +1,139 @@ +import { CryptoId, ExchangeTrade } from 'invity-api'; + +const litecoin = 'litecoin' as CryptoId; +const bitcoin = 'bitcoin' as CryptoId; +const ethereum = 'ethereum' as CryptoId; +const cronoseth = 'ethereum--0xa0b73e1ff0b80914ab6fe0444e65848c4c34450b' as CryptoId; + +export const MIN_MAX_QUOTES_OK: ExchangeTrade[] = [ + { + send: litecoin, + sendStringAmount: '12', + receive: bitcoin, + receiveStringAmount: '0.0609979', + rate: 0.005083158333333333, + min: 0.5688, + max: 'NONE', + fee: 'UNKNOWN', + exchange: 'changelly', + }, + { + send: litecoin, + sendStringAmount: '12', + receive: bitcoin, + receiveStringAmount: '0.0605096167302', + rate: 0.00504246806085, + min: 1.68, + max: 130, + fee: 'UNKNOWN', + exchange: 'foxexchange', + quoteToken: '', + }, + { + send: litecoin, + sendStringAmount: '12', + receive: bitcoin, + receiveStringAmount: '0.06047229', + rate: 0.0050393575, + min: 1.68, + max: 130, + fee: 'INCLUDED', + exchange: 'foxexchangefr', + quoteToken: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9', + }, + { + send: litecoin, + sendStringAmount: '12', + receive: bitcoin, + receiveStringAmount: '0.06036741979259172204', + rate: 0.005030618316049311, + min: 0.35206704205749473, + max: 195.59280114305264, + fee: 'INCLUDED', + rateIdentificator: '6ad45ee9-a8fa-4d25-a3c8-05361176b49d', + exchange: 'changeherofr', + }, + { + send: litecoin, + sendStringAmount: '12', + receive: bitcoin, + receiveStringAmount: '0.060238681', + rate: 0.005019890083333333, + min: 0.35121471511608626, + max: 'NONE', + fee: 'UNKNOWN', + exchange: 'changehero', + }, + { + send: litecoin, + sendStringAmount: '12', + receive: bitcoin, + receiveStringAmount: '0.059907672', + rate: 0.004992305999999999, + min: 0.5245852799999999, + max: 'NONE', + fee: 'UNKNOWN', + exchange: 'changenow', + }, + { + send: litecoin, + sendStringAmount: '12', + receive: bitcoin, + receiveStringAmount: '0.05984359', + rate: 0.004986965833333333, + min: 0.8295, + max: 719.15, + fee: 'INCLUDED', + offerReferenceId: 'db551578-915a-47c9-9197-a656e9372f2c', + exchange: 'coinswitchfr', + }, + { + send: litecoin, + sendStringAmount: '12', + receive: bitcoin, + receiveStringAmount: '0.05980199613749862984', + rate: 0.004983499678124886, + min: 0.85, + max: 80, + fee: 'UNKNOWN', + exchange: 'coinswitch', + }, + { + send: litecoin, + sendStringAmount: '12', + receive: bitcoin, + receiveStringAmount: '0.0595996968', + rate: 0.0049666414, + min: 0.948, + max: 757, + fee: 'INCLUDED', + rateIdentificator: 'e98f80e3e29dd57c0356141836c113f1c19ecadf66ee77fde5835796499cfe065e73', + exchange: 'changellyfr', + }, + { + send: litecoin, + sendStringAmount: '12', + receive: bitcoin, + receiveStringAmount: '0.0595048', + rate: 0.004958733333333333, + min: 0.70367472, + max: 293.1036283, + fee: 'INCLUDED', + exchange: 'changenowfr', + }, + { + send: ethereum, + sendStringAmount: '0.033466001597224105', + receive: cronoseth, + receiveStringAmount: '1116.96945045', + rate: 33376.2444612639, + min: 0, + max: 'NONE', + fee: 'UNKNOWN', + exchange: '1inch', + isDex: true, + approvalGasEstimate: 57000, + swapGasEstimate: 189386, + swapSlippage: '1', + }, +]; diff --git a/suite-common/trading/src/__fixtures__/sellUtils.ts b/suite-common/trading/src/__fixtures__/sellUtils.ts new file mode 100644 index 00000000000..db898687dad --- /dev/null +++ b/suite-common/trading/src/__fixtures__/sellUtils.ts @@ -0,0 +1,50 @@ +import { CryptoId, SellFiatTrade } from 'invity-api'; + +const bitcoin = 'bitcoin' as CryptoId; + +export const MIN_MAX_QUOTES_HIGH: SellFiatTrade[] = [ + { + fiatStringAmount: '100000', + fiatCurrency: 'EUR', + cryptoCurrency: bitcoin, + cryptoStringAmount: '9.8414', + rate: 10161.15593309895, + quoteId: '0e1966dc-83c7-4b98-9b93-d8a5e426b3d3', + error: 'Amount too high, maximum is EUR 1000 or BTC 0.10595.', + exchange: 'cexdirect', + minFiat: 25, + maxFiat: 1000, + minCrypto: 0.002, + maxCrypto: 0.10595, + paymentMethod: 'creditCard', + }, + { + fiatStringAmount: '100000', + fiatCurrency: 'EUR', + cryptoCurrency: bitcoin, + cryptoStringAmount: '10.010215580340265', + rate: 9989.794844818, + quoteId: 'f1a4cf48-ebdb-4dac-b049-2dbd086dd68d', + error: 'Amount too high, maximum is EUR 2000.03 or BTC 0.20015000.', + exchange: 'simplecoin', + minFiat: 19.98, + maxFiat: 2000.03, + minCrypto: 0.002, + maxCrypto: 0.20015, + paymentMethod: 'creditCard', + }, + { + fiatStringAmount: '100000', + fiatCurrency: 'EUR', + cryptoCurrency: bitcoin, + cryptoStringAmount: '9.8414', + rate: 0, + error: 'Transaction amount too high. Please enter a value of 17050 EUR or less.', + exchange: 'simplex', + minFiat: 43, + maxFiat: 17045, + minCrypto: 0.00418032, + maxCrypto: 1.67212968, + paymentMethod: 'creditCard', + }, +]; diff --git a/suite-common/trading/src/__fixtures__/utils.ts b/suite-common/trading/src/__fixtures__/utils.ts new file mode 100644 index 00000000000..c6b6c595bde --- /dev/null +++ b/suite-common/trading/src/__fixtures__/utils.ts @@ -0,0 +1,44 @@ +export const accountBtc = { + index: 1, + accountType: 'segwit', + networkType: 'bitcoin', + symbol: 'btc', + addresses: { + unused: [ + { + address: '177BUDVZqTTzK1Fogqcrfbb5ketHEUDGSJ', + transfers: 0, + path: "m/44'/0'/3'/0/0", + }, + ], + }, +}; + +export const accountEth = { + index: 1, + accountType: 'normal', + networkType: 'ethereum', + symbol: 'eth', + descriptor: '0x2e0DC981d301cdd443C3987cf19Eb9671CB99ddC', + path: "m/44'/60'/0'/0/1", + tokens: [ + { + type: 'ERC20', + contract: '0x1234123412341234123412341234123412341234', + symbol: 'usdt', + decimals: 18, + }, + { + type: 'ERC20', + contract: '0x1234123412341234123412341234123412341235', + symbol: 'usdc', + decimals: 18, + }, + { + type: 'ERC20', + contract: '0x1234123412341234123412341234123412341236', + symbol: 'other', + decimals: 18, + }, + ], +}; diff --git a/suite-common/trading/src/__tests__/utils.test.ts b/suite-common/trading/src/__tests__/utils.test.ts new file mode 100644 index 00000000000..9f65837d7d2 --- /dev/null +++ b/suite-common/trading/src/__tests__/utils.test.ts @@ -0,0 +1,133 @@ +import type { CryptoId } from 'invity-api'; + +import type { Account } from '@suite-common/wallet-types'; + +import * as BUY_FIXTURE from '../__fixtures__/buyUtils'; +import * as EXCHANGE_FIXTURE from '../__fixtures__/exchangeUtils'; +import * as SELL_FIXTURE from '../__fixtures__/sellUtils'; +import { accountBtc, accountEth } from '../__fixtures__/utils'; +import { + addIdsToQuotes, + filterQuotesAccordingTags, + getTagAndInfoNote, + getUnusedAddressFromAccount, + isCryptoIdForNativeToken, + mapTestnetSymbol, + testnetToProdCryptoId, +} from '../utils'; + +describe('testing trading utils', () => { + it('getUnusedAddressFromAccount', () => { + expect(getUnusedAddressFromAccount(accountBtc as Account)).toStrictEqual({ + address: '177BUDVZqTTzK1Fogqcrfbb5ketHEUDGSJ', + path: "m/44'/0'/3'/0/0", + }); + + expect(getUnusedAddressFromAccount(accountEth as Account)).toStrictEqual({ + address: '0x2e0DC981d301cdd443C3987cf19Eb9671CB99ddC', + path: "m/44'/60'/0'/0/1", + }); + }); + + it('mapTestnetCryptoCurrency', () => { + expect(mapTestnetSymbol('btc')).toStrictEqual('btc'); + expect(mapTestnetSymbol('eth')).toStrictEqual('eth'); + expect(mapTestnetSymbol('test')).toStrictEqual('btc'); + expect(mapTestnetSymbol('txrp')).toStrictEqual('xrp'); + }); + + it('getTagAndInfoNote', () => { + expect(getTagAndInfoNote({})).toStrictEqual({ infoNote: '', tag: '' }); + expect(getTagAndInfoNote({ infoNote: '' })).toStrictEqual({ infoNote: '', tag: '' }); + expect(getTagAndInfoNote({ infoNote: 'Foo' })).toStrictEqual({ infoNote: 'Foo', tag: '' }); + expect(getTagAndInfoNote({ infoNote: ' #Foo' })).toStrictEqual({ + infoNote: '', + tag: 'Foo', + }); + expect(getTagAndInfoNote({ infoNote: 'Foo#Bar' })).toStrictEqual({ + infoNote: 'Foo#Bar', + tag: '', + }); + expect(getTagAndInfoNote({ infoNote: '#Foo' })).toStrictEqual({ infoNote: '', tag: 'Foo' }); + expect(getTagAndInfoNote({ infoNote: '# Foo' })).toStrictEqual({ + infoNote: '', + tag: ' Foo', + }); + expect(getTagAndInfoNote({ infoNote: '##Bar' })).toStrictEqual({ + infoNote: 'Bar', + tag: '', + }); + expect(getTagAndInfoNote({ infoNote: '#Foo#Bar' })).toStrictEqual({ + infoNote: 'Bar', + tag: 'Foo', + }); + expect(getTagAndInfoNote({ infoNote: ' #Foo#Bar \t' })).toStrictEqual({ + infoNote: 'Bar', + tag: 'Foo', + }); + }); + + it('filterQuotesAccordingTags', () => { + const quotes = [ + ...BUY_FIXTURE.MIN_MAX_QUOTES_OK, + ...BUY_FIXTURE.ALTERNATIVE_QUOTES, + ...SELL_FIXTURE.MIN_MAX_QUOTES_HIGH, + ]; + + expect(filterQuotesAccordingTags([])).toStrictEqual([]); + expect(filterQuotesAccordingTags(quotes).length).toStrictEqual( + quotes.filter(q => !q.tags || !q.tags.includes('alternativeCurrency')).length, + ); + }); + + it('addIdsToQuotes', () => { + const quotes = [...BUY_FIXTURE.MIN_MAX_QUOTES_OK]; + const quotesExchange = [...EXCHANGE_FIXTURE.MIN_MAX_QUOTES_OK]; + + expect(addIdsToQuotes([], 'buy')).toStrictEqual([]); + expect(addIdsToQuotes(quotes, 'buy').length).toStrictEqual( + quotes.filter(q => q.orderId && q.paymentId).length, + ); + expect(addIdsToQuotes(quotesExchange, 'exchange').length).toStrictEqual( + quotesExchange.filter(q => q.orderId).length, + ); + }); + + it('testnetToProdCryptoId', () => { + expect(testnetToProdCryptoId('test-bitcoin' as CryptoId)).toEqual('bitcoin'); + expect(testnetToProdCryptoId('bitcoin' as CryptoId)).toEqual('bitcoin'); + + expect(testnetToProdCryptoId('test-ripple' as CryptoId)).toEqual('ripple'); + expect(testnetToProdCryptoId('ripple' as CryptoId)).toEqual('ripple'); + + expect( + testnetToProdCryptoId( + 'test-ethereum--0x1234123412341234123412341234123412341236' as CryptoId, + ), + ).toEqual('ethereum--0x1234123412341234123412341234123412341236'); + expect( + testnetToProdCryptoId( + 'ethereum--0x1234123412341234123412341234123412341236' as CryptoId, + ), + ).toEqual('ethereum--0x1234123412341234123412341234123412341236'); + }); + + it('isCryptoIdForNativeToken - test if token is L2 native token', () => { + expect(isCryptoIdForNativeToken('ethereum' as CryptoId)).toEqual(false); + expect( + isCryptoIdForNativeToken( + 'ethereum--0x1234123412341234123412341234123412341236' as CryptoId, + ), + ).toEqual(false); + expect( + isCryptoIdForNativeToken( + 'ethereum--0x0000000000000000000000000000000000000000' as CryptoId, + ), + ).toEqual(true); + expect( + isCryptoIdForNativeToken( + 'base--0x0000000000000000000000000000000000000000' as CryptoId, + ), + ).toEqual(true); + }); +}); diff --git a/suite-common/trading/src/constants.ts b/suite-common/trading/src/constants.ts new file mode 100644 index 00000000000..8992fdd48e9 --- /dev/null +++ b/suite-common/trading/src/constants.ts @@ -0,0 +1,5 @@ +export const CRYPTO_PLATFORM_SEPARATOR = '--'; +/** + * Used for for L2 networks (e.g. base, op) + */ +export const CONTRACT_ADDRESS_FOR_NATIVE_TOKEN = '0x0000000000000000000000000000000000000000'; diff --git a/suite-common/trading/src/index.ts b/suite-common/trading/src/index.ts index 4482c50da7c..8f5f7547871 100644 --- a/suite-common/trading/src/index.ts +++ b/suite-common/trading/src/index.ts @@ -1,3 +1,5 @@ export * from './types'; export * from './invityAPI'; export * from './regional'; +export * from './utils'; +export * from './constants'; diff --git a/suite-common/trading/src/types.ts b/suite-common/trading/src/types.ts index 4c83725f9d8..8c369595bb0 100644 --- a/suite-common/trading/src/types.ts +++ b/suite-common/trading/src/types.ts @@ -2,6 +2,7 @@ import type { BuyCryptoPaymentMethod, BuyTrade, BuyTradeStatus, + CryptoId, ExchangeTrade, ExchangeTradeStatus, SellCryptoPaymentMethod, @@ -20,8 +21,16 @@ export type TradingSellType = 'sell'; export type TradingExchangeType = 'exchange'; export type TradingType = TradingBuyType | TradingSellType | TradingExchangeType; +export type TradingTradeBuySellType = Exclude; + // information about created trade export type TradingTradeType = BuyTrade | SellFiatTrade | ExchangeTrade; +export type TradingTradeMapProps = { + buy: BuyTrade; + sell: SellFiatTrade; + exchange: ExchangeTrade; +}; +export type TradingTradeBuySellMapProps = Omit; export type TradingWatchTradeResponsePropsMap = { buy: WatchBuyTradeResponse; @@ -38,3 +47,8 @@ export type TradingUtilsProvidersProps = { brandName?: string; }; }; + +export type TradingParsedCryptoIdProps = { + networkId: CryptoId; + contractAddress: string | undefined; +}; diff --git a/suite-common/trading/src/utils.ts b/suite-common/trading/src/utils.ts new file mode 100644 index 00000000000..4b5f649a91f --- /dev/null +++ b/suite-common/trading/src/utils.ts @@ -0,0 +1,157 @@ +import { BuyTrade, CryptoId, ExchangeTrade, SellFiatTrade } from 'invity-api'; +import { v4 as uuidv4 } from 'uuid'; + +import { + type Network, + type NetworkSymbol, + getCoingeckoId, + getNetworkByCoingeckoId, + getNetworkByTradeCryptoId, +} from '@suite-common/wallet-config'; +import { Account } from '@suite-common/wallet-types'; + +import { CONTRACT_ADDRESS_FOR_NATIVE_TOKEN, CRYPTO_PLATFORM_SEPARATOR } from './constants'; +import { regional } from './regional'; +import { + TradingParsedCryptoIdProps, + TradingTradeBuySellMapProps, + TradingTradeBuySellType, + TradingTradeMapProps, + TradingType, +} from './types'; + +export const parseCryptoId = (cryptoId: CryptoId): TradingParsedCryptoIdProps => { + const parts = cryptoId.split(CRYPTO_PLATFORM_SEPARATOR); + + return { networkId: parts[0] as CryptoId, contractAddress: parts[1] }; +}; + +export const cryptoIdToNetwork = (cryptoId: CryptoId): Network | undefined => { + const { networkId, contractAddress } = parseCryptoId(cryptoId); + + return contractAddress + ? getNetworkByCoingeckoId(networkId) + : getNetworkByTradeCryptoId(networkId); +}; + +export const cryptoIdToSymbol = (cryptoId: CryptoId): NetworkSymbol | undefined => + cryptoIdToNetwork(cryptoId)?.symbol; + +export const toTokenCryptoId = (symbol: NetworkSymbol, contractAddress: string): CryptoId => + `${getCoingeckoId(symbol)}${CRYPTO_PLATFORM_SEPARATOR}${contractAddress}` as CryptoId; + +/** Convert testnet cryptoId to prod cryptoId (test-bitcoin -> bitcoin) */ +export const testnetToProdCryptoId = (cryptoId: CryptoId): CryptoId => { + const { networkId, contractAddress } = parseCryptoId(cryptoId); + + return ((networkId.split('test-')?.[1] ?? networkId) + + (contractAddress ? `${CRYPTO_PLATFORM_SEPARATOR}${contractAddress}` : '')) as CryptoId; +}; + +export const isCryptoIdForNativeToken = (cryptoId: CryptoId) => { + const { contractAddress } = parseCryptoId(cryptoId); + + return contractAddress === CONTRACT_ADDRESS_FOR_NATIVE_TOKEN; +}; + +export const getUnusedAddressFromAccount = (account: Account) => { + switch (account.networkType) { + case 'cardano': + case 'bitcoin': { + const firstUnused = account.addresses?.unused[0]; + if (firstUnused) { + return { address: firstUnused.address, path: firstUnused.path }; + } + + return { address: undefined, path: undefined }; + } + case 'ripple': + case 'ethereum': + case 'solana': { + return { + address: account.descriptor, + path: account.path, + }; + } + // no default + } +}; + +export const mapTestnetSymbol = ( + symbol: NetworkSymbol, +): Exclude => { + if (symbol === 'test') return 'btc'; + if (symbol === 'tsep') return 'eth'; + if (symbol === 'thol') return 'eth'; + if (symbol === 'txrp') return 'xrp'; + if (symbol === 'tada') return 'ada'; + + return symbol; +}; + +export const getTagAndInfoNote = (quote: { infoNote?: string }) => { + let tag = ''; + let infoNote = (quote?.infoNote || '').trim(); + if (infoNote.startsWith('#')) { + const splitNote = infoNote?.split('#') || []; + if (splitNote.length === 3) { + // infoNote contains "#badge_text#info_note_text" + [, tag, infoNote] = splitNote; + } else if (splitNote.length === 2) { + // infoNote contains "#badge_text" + infoNote = ''; + tag = splitNote.pop() || ''; + } + } + + return { tag, infoNote }; +}; + +export const tradingGetSuccessQuotes = ( + quotes: TradingTradeMapProps[T][] | undefined, +) => (quotes ? quotes.filter(quote => quote.error === undefined) : undefined); + +export const getDefaultCountry = (country: string = regional.UNKNOWN_COUNTRY) => { + const label = regional.countriesMap.get(country); + + if (!label) + return { + label: regional.countriesMap.get(regional.UNKNOWN_COUNTRY)!, + value: regional.UNKNOWN_COUNTRY, + }; + + return { + label, + value: country, + }; +}; + +export const filterQuotesAccordingTags = ( + allQuotes: TradingTradeBuySellMapProps[T][], +) => allQuotes.filter(q => !q.tags || !q.tags.includes('alternativeCurrency')); + +// fill orderId for all, paymentId for sell and buy, quoteId for exchange +export const addIdsToQuotes = ( + allQuotes: TradingTradeMapProps[T][] | undefined, + type: TradingType, +): TradingTradeMapProps[T][] => { + if (!allQuotes) allQuotes = []; + + allQuotes.forEach(quote => { + const sellBuyQuote = ['buy', 'sell'].includes(type) + ? (quote as BuyTrade | SellFiatTrade) + : null; + + if (sellBuyQuote && !sellBuyQuote.paymentId) { + sellBuyQuote.paymentId = uuidv4(); + } + + if (type === 'exchange' && !quote.quoteId) { + (quote as ExchangeTrade).quoteId = uuidv4(); + } + + quote.orderId = uuidv4(); + }); + + return allQuotes; +}; diff --git a/suite-common/trading/tsconfig.json b/suite-common/trading/tsconfig.json index e98b468c5b7..3534dd20242 100644 --- a/suite-common/trading/tsconfig.json +++ b/suite-common/trading/tsconfig.json @@ -2,6 +2,8 @@ "extends": "../../tsconfig.base.json", "compilerOptions": { "outDir": "libDev" }, "references": [ + { "path": "../wallet-config" }, + { "path": "../wallet-types" }, { "path": "../../packages/env-utils" }, { "path": "../../packages/utils" } ] diff --git a/yarn.lock b/yarn.lock index f58fbc6987c..c6fd3f103ca 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9765,9 +9765,12 @@ __metadata: version: 0.0.0-use.local resolution: "@suite-common/trading@workspace:suite-common/trading" dependencies: + "@suite-common/wallet-config": "workspace:*" + "@suite-common/wallet-types": "workspace:*" "@trezor/env-utils": "workspace:*" "@trezor/utils": "workspace:*" "@types/invity-api": "npm:^1.1.2" + uuid: "npm:^11.0.4" languageName: unknown linkType: soft