From 29f96232d760bed560e95bb4950fab35ec71d381 Mon Sep 17 00:00:00 2001 From: Gabriel Date: Tue, 27 Sep 2022 10:39:24 -0300 Subject: [PATCH] [REF] simplify adding new chains --- src/components/amount/Amount.tsx | 5 +- .../currency-image/CurrencyImage.tsx | 4 +- src/components/list/ContactRow.tsx | 5 +- src/components/list/CurrencySelectionRow.tsx | 47 +- src/components/list/GlobalSelectRow.tsx | 5 +- src/components/list/WalletRow.tsx | 5 +- .../modal/biometric/BiometricModal.tsx | 1 - src/constants/SupportedCurrencyOptions.tsx | 112 ++-- src/constants/currencies.ts | 523 +++++++++--------- src/constants/tokens.ts | 4 +- src/lib/bwc.ts | 4 +- .../screens/CoinbaseWithdrawConfirm.tsx | 8 +- src/navigation/onboarding/screens/Pin.tsx | 5 - .../services/buy-crypto/BuyCryptoStack.tsx | 1 + .../components/WalletSelectorModal.tsx | 10 +- .../buy-crypto/screens/BuyCryptoOffers.tsx | 33 +- .../buy-crypto/screens/BuyCryptoRoot.tsx | 30 +- .../swap-crypto/screens/ChangellyCheckout.tsx | 45 +- .../swap-crypto/screens/SwapCryptoRoot.tsx | 71 ++- .../tabs/contacts/components/ContactIcon.tsx | 26 +- .../tabs/contacts/screens/ContactsAdd.tsx | 285 +++++++--- .../tabs/contacts/screens/ContactsRoot.tsx | 4 +- src/navigation/tabs/home/HomeRoot.tsx | 2 +- .../exchange-rates/ExchangeRatesList.tsx | 4 +- .../settings/screens/NewtorkFeePolicy.tsx | 8 +- .../components/WalletSelector.tsx | 5 +- .../screens/WalletConnectConfirm.tsx | 2 +- .../screens/WalletConnectHome.tsx | 12 +- .../screens/WalletConnectStart.tsx | 6 +- .../wallet/components/AddressCard.tsx | 1 + .../wallet/components/MultipleOutputsTx.tsx | 34 +- .../wallet/components/ReceiveAddress.tsx | 8 +- .../wallet/components/RecoveryPhrase.tsx | 7 +- .../wallet/components/SendToAddress.tsx | 10 +- .../wallet/components/SendToContact.tsx | 5 +- .../wallet/components/SendToPill.tsx | 9 +- src/navigation/wallet/screens/AddWallet.tsx | 19 +- .../wallet/screens/AmountScreen.tsx | 3 + .../wallet/screens/CreateMultisig.tsx | 5 +- .../wallet/screens/CurrencySelection.tsx | 166 +++--- .../wallet/screens/CurrencyTokenSelection.tsx | 10 +- .../wallet/screens/GlobalSelect.tsx | 67 ++- src/navigation/wallet/screens/KeyOverview.tsx | 22 +- src/navigation/wallet/screens/KeySettings.tsx | 22 +- src/navigation/wallet/screens/PriceCharts.tsx | 14 +- .../wallet/screens/SelectInputs.tsx | 8 +- .../wallet/screens/SendToOptions.tsx | 3 +- .../wallet/screens/TransactionDetails.tsx | 25 +- .../screens/TransactionProposalDetails.tsx | 5 +- .../wallet/screens/WalletDetails.tsx | 59 +- .../RequestSpecificAmountQR.tsx | 8 +- src/navigation/wallet/screens/send/SendTo.tsx | 33 +- .../wallet/screens/send/TransactionLevel.tsx | 26 +- .../wallet/screens/send/confirm/Confirm.tsx | 13 +- .../screens/send/confirm/PayProConfirm.tsx | 4 +- .../wallet/screens/send/confirm/Shared.tsx | 24 +- .../screens/wallet-settings/Addresses.tsx | 48 +- .../screens/wallet-settings/AllAddresses.tsx | 13 +- .../screens/wallet-settings/ExportWallet.tsx | 4 +- .../wallet-settings/WalletInformation.tsx | 13 +- src/store/coinbase/coinbase.effects.ts | 2 +- src/store/contact/contact.reducer.ts | 9 +- src/store/scan/scan.effects.ts | 67 ++- src/store/transforms/transforms.ts | 15 +- src/store/wallet/effects/amount/amount.ts | 27 +- .../create-multisig/create-multisig.ts | 32 +- src/store/wallet/effects/create/create.ts | 164 ++++-- .../wallet/effects/currencies/currencies.ts | 22 +- src/store/wallet/effects/fee/fee.ts | 31 +- src/store/wallet/effects/import/import.ts | 91 ++- .../effects/join-multisig/join-multisig.ts | 31 +- src/store/wallet/effects/paypro/paypro.ts | 13 +- src/store/wallet/effects/rates/rates.ts | 4 +- src/store/wallet/effects/send/send.ts | 103 ++-- src/store/wallet/effects/status/status.ts | 53 +- .../effects/transactions/transactions.ts | 115 ++-- src/store/wallet/utils/currency.ts | 179 ++++-- src/store/wallet/utils/wallet.ts | 184 +++--- src/store/wallet/wallet.models.ts | 6 + src/utils/helper-methods.ts | 36 +- 80 files changed, 1927 insertions(+), 1217 deletions(-) diff --git a/src/components/amount/Amount.tsx b/src/components/amount/Amount.tsx index 547d74b757..51b71fd11f 100644 --- a/src/components/amount/Amount.tsx +++ b/src/components/amount/Amount.tsx @@ -83,6 +83,7 @@ const CurrencyText = styled(BaseText)` export interface AmountProps { cryptoCurrencyAbbreviation?: string; fiatCurrencyAbbreviation?: string; + chain?: string; context?: string; buttonState?: ButtonState; @@ -95,6 +96,7 @@ export interface AmountProps { const Amount: React.VFC = ({ cryptoCurrencyAbbreviation, fiatCurrencyAbbreviation, + chain, context, buttonState, onSubmit, @@ -150,7 +152,7 @@ const Amount: React.VFC = ({ const updateAmount = (_val: string) => { const val = Number(_val); - if (isNaN(val) || !cryptoCurrencyAbbreviation) { + if (isNaN(val) || !cryptoCurrencyAbbreviation || !chain) { updateAmountConfig(current => ({ ...current, displayAmount: _val, @@ -167,6 +169,7 @@ const Amount: React.VFC = ({ ParseAmount( primaryIsFiat ? val / rate : val, cryptoCurrencyAbbreviation.toLowerCase(), + chain, ), ).amount; diff --git a/src/components/currency-image/CurrencyImage.tsx b/src/components/currency-image/CurrencyImage.tsx index 9deefd7c04..0acba8936a 100644 --- a/src/components/currency-image/CurrencyImage.tsx +++ b/src/components/currency-image/CurrencyImage.tsx @@ -21,8 +21,8 @@ const CurrencyImageContainer = styled.View` `; const BadgeContainer = styled.View<{size?: number}>` - height: ${({size = 56}) => size}%; - width: ${({size = 56}) => size}%; + height: ${({size = 54}) => size}%; + width: ${({size = 54}) => size}%; position: absolute; right: 0; bottom: 0; diff --git a/src/components/list/ContactRow.tsx b/src/components/list/ContactRow.tsx index d5cf04e13f..903ca1aff4 100644 --- a/src/components/list/ContactRow.tsx +++ b/src/components/list/ContactRow.tsx @@ -32,6 +32,7 @@ const RowContainer = styled.View` export interface ContactRowProps { address: string; coin: string; + chain: string; network: string; name: string; tag?: number; // backward compatibility @@ -47,12 +48,12 @@ interface Props { const ContactRow = ({contact, onPress}: Props) => { const theme = useTheme(); const underlayColor = theme.dark ? '#121212' : '#fbfbff'; - const {coin, name, email, address} = contact; + const {coin, name, email, address, chain} = contact; return ( - +
{name}
diff --git a/src/components/list/CurrencySelectionRow.tsx b/src/components/list/CurrencySelectionRow.tsx index 83a9c2f6f5..cf3e1789c6 100644 --- a/src/components/list/CurrencySelectionRow.tsx +++ b/src/components/list/CurrencySelectionRow.tsx @@ -1,4 +1,4 @@ -import React, {memo, useCallback} from 'react'; +import React, {memo, ReactElement, useCallback} from 'react'; import {useTranslation} from 'react-i18next'; import {ImageRequireSource, View} from 'react-native'; import styled from 'styled-components/native'; @@ -12,6 +12,7 @@ import { Slate30, SlateDark, } from '../../styles/colors'; +import {getBadgeImg} from '../../utils/helper-methods'; import Checkbox from '../checkbox/Checkbox'; import {CurrencyImage} from '../currency-image/CurrencyImage'; import haptic from '../haptic-feedback/haptic'; @@ -23,6 +24,7 @@ export type CurrencySelectionItem = Pick< SupportedCurrencyOption, 'id' | 'currencyAbbreviation' | 'currencyName' | 'img' | 'isToken' > & { + chain: string; imgSrc?: ImageRequireSource | undefined; selected?: boolean; disabled?: boolean; @@ -34,7 +36,7 @@ export type CurrencySelectionRowProps = { description?: string; hideCheckbox?: boolean; selectionMode?: CurrencySelectionMode; - onToggle?: (id: string) => void; + onToggle?: (currencyAbbreviation: string) => void; onViewAllTokensPressed?: ( currency: CurrencySelectionItem, tokens: CurrencySelectionItem[], @@ -142,28 +144,42 @@ export const ChainSelectionRow: React.VFC = memo( ); interface TokenSelectionRowProps { - chainImg?: CurrencySelectionItem['img']; token: CurrencySelectionItem; hideCheckbox?: boolean; selectionMode?: CurrencySelectionMode; - onToggle?: (id: string) => any; + onToggle?: (currencyAbbreviation: string) => any; + hideArrow?: boolean; + badgeUri?: string | ((props?: any) => ReactElement); } export const TokenSelectionRow: React.VFC = memo( props => { - const {chainImg, token, hideCheckbox, selectionMode, onToggle} = props; + const { + token, + hideCheckbox, + selectionMode, + onToggle, + hideArrow, + badgeUri: _badgeUri, + } = props; + const badgeUri = + _badgeUri || getBadgeImg(token.currencyAbbreviation, token.chain); return ( - onToggle?.(token.id)}> - - - + onToggle?.(token.currencyAbbreviation)}> + {!hideArrow ? ( + + + + ) : null} @@ -181,7 +197,7 @@ export const TokenSelectionRow: React.VFC = memo( checked={!!token.selected} radio={selectionMode === 'single'} disabled={!!token.disabled} - onPress={() => onToggle?.(token.id)} + onPress={() => onToggle?.(token.currencyAbbreviation)} /> ) : null} @@ -210,9 +226,9 @@ const CurrencySelectionRow: React.VFC = ({ const {t} = useTranslation(); const {currencyName} = currency; const onPress = useCallback( - (id: string): void => { + (currencyAbbreviation: string): void => { haptic(IS_ANDROID ? 'keyboardPress' : 'impactLight'); - onToggle?.(id); + onToggle?.(currencyAbbreviation); }, [onToggle], ); @@ -221,7 +237,7 @@ const CurrencySelectionRow: React.VFC = ({ onPress(currency.currencyAbbreviation)} hideCheckbox={hideCheckbox} selectionMode={selectionMode} /> @@ -237,9 +253,8 @@ const CurrencySelectionRow: React.VFC = ({ {tokens.map(token => ( onPress(token.currencyAbbreviation)} hideCheckbox={hideCheckbox} selectionMode={selectionMode} /> diff --git a/src/components/list/GlobalSelectRow.tsx b/src/components/list/GlobalSelectRow.tsx index a1b73fd62c..8e2a7f66ae 100644 --- a/src/components/list/GlobalSelectRow.tsx +++ b/src/components/list/GlobalSelectRow.tsx @@ -30,11 +30,12 @@ export const AvailableWalletsPill = styled.View` const GlobalSelectRow = ({item, emit}: Props) => { const {t} = useTranslation(); - const {currencyName, total, img} = item; + const {currencyName, total, img, badgeImg} = item; + return ( emit(item)}> - +
{currencyName}
diff --git a/src/components/list/WalletRow.tsx b/src/components/list/WalletRow.tsx index a0a96a998b..d8ad449ad0 100644 --- a/src/components/list/WalletRow.tsx +++ b/src/components/list/WalletRow.tsx @@ -32,8 +32,10 @@ const NestedArrowContainer = styled.View` export interface WalletRowProps { id: string; img: string | ((props: any) => ReactElement); + badgeImg?: string | ((props: any) => ReactElement); currencyName: string; currencyAbbreviation: string; + chain: string; walletName?: string; cryptoBalance: string; cryptoLockedBalance?: string; @@ -85,6 +87,7 @@ const WalletRow = ({wallet, onPress}: Props) => { currencyAbbreviation, walletName, img, + badgeImg, cryptoBalance, fiatBalance, isToken, @@ -104,7 +107,7 @@ const WalletRow = ({wallet, onPress}: Props) => { )} - + diff --git a/src/components/modal/biometric/BiometricModal.tsx b/src/components/modal/biometric/BiometricModal.tsx index 9db88821f8..d44e91d4aa 100644 --- a/src/components/modal/biometric/BiometricModal.tsx +++ b/src/components/modal/biometric/BiometricModal.tsx @@ -125,7 +125,6 @@ const BiometricModal: React.FC = () => { onClose?.(true); }) .catch((error: BiometricError) => { - console.log(error); if (error.code && TO_HANDLE_ERRORS[error.code]) { const err = TO_HANDLE_ERRORS[error.code]; dispatch( diff --git a/src/constants/SupportedCurrencyOptions.tsx b/src/constants/SupportedCurrencyOptions.tsx index 42126d0fb8..b9725131c0 100644 --- a/src/constants/SupportedCurrencyOptions.tsx +++ b/src/constants/SupportedCurrencyOptions.tsx @@ -23,7 +23,9 @@ export interface SupportedCurrencyOption { hasMultisig?: boolean; currencyAbbreviation: string; isToken?: boolean; - imgSrc: ImageSourcePropType; + imgSrc?: ImageSourcePropType; + badgeUri?: string | ((props?: any) => ReactElement); + badgeSrc?: ImageSourcePropType; } export const CurrencyListIcons: { @@ -46,124 +48,158 @@ export const CurrencyListIcons: { euroc: props => , }; -export const SupportedCurrencyOptions: Array = [ +export const SupportedUtxoCurrencyOptions: Array = [ { - id: 'btc', + id: Math.random().toString(), img: CurrencyListIcons.btc, currencyName: 'Bitcoin', - currencyAbbreviation: 'BTC', + currencyAbbreviation: 'btc', hasMultisig: true, imgSrc: require('../../assets/img/currencies/png/BTC.png'), }, { - id: 'bch', + id: Math.random().toString(), img: CurrencyListIcons.bch, currencyName: 'Bitcoin Cash', - currencyAbbreviation: 'BCH', + currencyAbbreviation: 'bch', hasMultisig: true, imgSrc: require('../../assets/img/currencies/png/BCH.png'), }, { - id: 'eth', - img: CurrencyListIcons.eth, - currencyName: 'Ethereum', - currencyAbbreviation: 'ETH', - hasMultisig: false, // TODO - imgSrc: require('../../assets/img/currencies/png/ETH.png'), - }, - { - id: 'doge', + id: Math.random().toString(), img: CurrencyListIcons.doge, currencyName: 'Dogecoin', - currencyAbbreviation: 'DOGE', + currencyAbbreviation: 'doge', hasMultisig: true, imgSrc: require('../../assets/img/currencies/png/DOGE.png'), }, { - id: 'ltc', + id: Math.random().toString(), img: CurrencyListIcons.ltc, currencyName: 'Litecoin', - currencyAbbreviation: 'LTC', + currencyAbbreviation: 'ltc', hasMultisig: true, imgSrc: require('../../assets/img/currencies/png/LTC.png'), }, { - id: 'xrp', + id: Math.random().toString(), img: CurrencyListIcons.xrp, currencyName: 'XRP', - currencyAbbreviation: 'XRP', + currencyAbbreviation: 'xrp', imgSrc: require('../../assets/img/currencies/png/XRP.png'), }, +]; + +export const SupportedEvmCurrencyOptions: Array = [ + { + id: Math.random().toString(), + img: CurrencyListIcons.eth, + currencyName: 'Ethereum', + currencyAbbreviation: 'eth', + hasMultisig: false, + imgSrc: require('../../assets/img/currencies/png/ETH.png'), + }, +]; + +export const SupportedTokenOptions: Array = [ { - id: 'usdc', + id: Math.random().toString(), img: CurrencyListIcons.usdc, currencyName: 'USD Coin', - currencyAbbreviation: 'USDC', + currencyAbbreviation: 'usdc', isToken: true, imgSrc: require('../../assets/img/currencies/png/USDC.png'), + badgeSrc: require('../../assets/img/currencies/png/ETH.png'), + badgeUri: CurrencyListIcons.eth, }, { - id: 'ape', + id: Math.random().toString(), img: CurrencyListIcons.ape, currencyName: 'ApeCoin', - currencyAbbreviation: 'APE', + currencyAbbreviation: 'ape', isToken: true, imgSrc: require('../../assets/img/currencies/png/APE.png'), + badgeSrc: require('../../assets/img/currencies/png/ETH.png'), + badgeUri: CurrencyListIcons.eth, }, { - id: 'euroc', + id: Math.random().toString(), img: CurrencyListIcons.euroc, currencyName: 'Euro Coin', - currencyAbbreviation: 'EUROC', + currencyAbbreviation: 'euroc', isToken: true, imgSrc: require('../../assets/img/currencies/png/EUROC.png'), + badgeSrc: require('../../assets/img/currencies/png/ETH.png'), + badgeUri: CurrencyListIcons.eth, }, { - id: 'shib', + id: Math.random().toString(), img: CurrencyListIcons.shib, currencyName: 'Shiba Inu', - currencyAbbreviation: 'SHIB', + currencyAbbreviation: 'shib', isToken: true, imgSrc: require('../../assets/img/currencies/png/SHIB.png'), + badgeSrc: require('../../assets/img/currencies/png/ETH.png'), + badgeUri: CurrencyListIcons.eth, }, { - id: 'gusd', + id: Math.random().toString(), img: CurrencyListIcons.gusd, currencyName: 'Gemini Dollar', - currencyAbbreviation: 'GUSD', + currencyAbbreviation: 'gusd', isToken: true, imgSrc: require('../../assets/img/currencies/png/GUSD.png'), + badgeSrc: require('../../assets/img/currencies/png/ETH.png'), + badgeUri: CurrencyListIcons.eth, }, { - id: 'busd', + id: Math.random().toString(), img: CurrencyListIcons.busd, currencyName: 'Binance USD Coin', - currencyAbbreviation: 'BUSD', + currencyAbbreviation: 'busd', isToken: true, imgSrc: require('../../assets/img/currencies/png/BUSD.png'), + badgeSrc: require('../../assets/img/currencies/png/ETH.png'), + badgeUri: CurrencyListIcons.eth, }, { - id: 'dai', + id: Math.random().toString(), img: CurrencyListIcons.dai, currencyName: 'Dai', - currencyAbbreviation: 'DAI', + currencyAbbreviation: 'dai', isToken: true, imgSrc: require('../../assets/img/currencies/png/DAI.png'), + badgeSrc: require('../../assets/img/currencies/png/ETH.png'), + badgeUri: CurrencyListIcons.eth, }, { - id: 'usdp', + id: Math.random().toString(), img: CurrencyListIcons.usdp, currencyName: 'Pax Dollar', - currencyAbbreviation: 'USDP', + currencyAbbreviation: 'usdp', isToken: true, imgSrc: require('../../assets/img/currencies/png/USDP.png'), + badgeSrc: require('../../assets/img/currencies/png/ETH.png'), + badgeUri: CurrencyListIcons.eth, }, { - id: 'wbtc', + id: Math.random().toString(), img: CurrencyListIcons.wbtc, currencyName: 'Wrapped Bitcoin', - currencyAbbreviation: 'WBTC', + currencyAbbreviation: 'wbtc', isToken: true, imgSrc: require('../../assets/img/currencies/png/WBTC.png'), + badgeSrc: require('../../assets/img/currencies/png/ETH.png'), + badgeUri: CurrencyListIcons.eth, }, ]; + +export const SupportedCoinsOptions: Array = [ + ...SupportedUtxoCurrencyOptions, + ...SupportedEvmCurrencyOptions, +]; + +export const SupportedCurrencyOptions: Array = [ + ...SupportedCoinsOptions, + ...SupportedTokenOptions, +]; diff --git a/src/constants/currencies.ts b/src/constants/currencies.ts index 25cc1fb6a6..8349e0cc5a 100644 --- a/src/constants/currencies.ts +++ b/src/constants/currencies.ts @@ -1,5 +1,5 @@ export type SupportedCoins = 'btc' | 'bch' | 'ltc' | 'doge' | 'eth'; -export type SupportedTokens = +export type SupportedEthereumTokens = | 'usdc' | 'gusd' | 'usdp' @@ -10,7 +10,9 @@ export type SupportedTokens = | 'shib' | 'ape' | 'euroc'; -export type SupportedCurrencies = SupportedCoins | SupportedTokens; +export type SupportedCurrencies = SupportedCoins | SupportedEthereumTokens; +export type EVM_CHAINS = 'eth'; +export type UTXO_CHAINS = 'btc' | 'bch' | 'doge' | 'ltc'; export interface CurrencyOpts { // Bitcore-node @@ -55,194 +57,10 @@ export interface CurrencyOpts { backgroundColor: string; gradientBackgroundColor: string; }; + tokens?: {[key in string]: CurrencyOpts}; } -export const Currencies: {[key in string]: CurrencyOpts} = { - btc: { - name: 'Bitcoin', - chain: 'BTC', - coin: 'btc', - unitInfo: { - unitName: 'BTC', - unitToSatoshi: 100000000, - unitDecimals: 8, - unitCode: 'btc', - }, - properties: { - hasMultiSig: true, - hasMultiSend: true, - isUtxo: true, - isERCToken: false, - isStableCoin: false, - singleAddress: false, - }, - paymentInfo: { - paymentCode: 'BIP73', - protocolPrefix: {livenet: 'bitcoin', testnet: 'bitcoin'}, - ratesApi: 'https://bws.bitpay.com/bws/api/v3/fiatrates/btc', - blockExplorerUrls: 'bitpay.com/insight/#/BTC/mainnet/', - blockExplorerUrlsTestnet: 'bitpay.com/insight/#/BTC/testnet/', - }, - feeInfo: { - feeUnit: 'sat/byte', - feeUnitAmount: 1000, - blockTime: 10, - maxMerchantFee: 'urgent', - }, - theme: { - coinColor: '#f7931a', - backgroundColor: '#f7921a', - gradientBackgroundColor: '#f7921a', - }, - }, - bch: { - name: 'Bitcoin Cash', - chain: 'BCH', - coin: 'bch', - unitInfo: { - unitName: 'BCH', - unitToSatoshi: 100000000, - unitDecimals: 8, - unitCode: 'bch', - }, - properties: { - hasMultiSig: true, - hasMultiSend: true, - isUtxo: true, - isERCToken: false, - isStableCoin: false, - singleAddress: false, - }, - paymentInfo: { - paymentCode: 'BIP73', - protocolPrefix: {livenet: 'bitcoincash', testnet: 'bchtest'}, - ratesApi: 'https://bws.bitpay.com/bws/api/v3/fiatrates/bch', - blockExplorerUrls: 'bitpay.com/insight/#/BCH/mainnet/', - blockExplorerUrlsTestnet: 'bitpay.com/insight/#/BCH/testnet/', - }, - feeInfo: { - feeUnit: 'sat/byte', - feeUnitAmount: 1000, - blockTime: 10, - maxMerchantFee: 'normal', - }, - theme: { - coinColor: '#2fcf6e', - backgroundColor: '#2fcf6e', - gradientBackgroundColor: '#2fcf6e', - }, - }, - eth: { - name: 'Ethereum', - chain: 'ETH', - coin: 'eth', - unitInfo: { - unitName: 'ETH', - unitToSatoshi: 1e18, - unitDecimals: 18, - unitCode: 'eth', - }, - properties: { - hasMultiSig: true, - hasMultiSend: false, - isUtxo: false, - isERCToken: false, - isStableCoin: false, - singleAddress: true, - }, - paymentInfo: { - paymentCode: 'EIP681', - protocolPrefix: {livenet: 'ethereum', testnet: 'ethereum'}, - ratesApi: 'https://bws.bitpay.com/bws/api/v3/fiatrates/eth', - blockExplorerUrls: 'etherscan.io/', - blockExplorerUrlsTestnet: 'kovan.etherscan.io/', - }, - feeInfo: { - feeUnit: 'Gwei', - feeUnitAmount: 1e9, - blockTime: 0.2, - maxMerchantFee: 'urgent', - }, - theme: { - coinColor: '#6b71d6', - backgroundColor: '#6b71d6', - gradientBackgroundColor: '#6b71d6', - }, - }, - xrp: { - name: 'XRP', - chain: 'XRP', - coin: 'xrp', - unitInfo: { - unitName: 'XRP', - unitToSatoshi: 1e6, - unitDecimals: 6, - unitCode: 'xrp', - }, - properties: { - hasMultiSig: false, - hasMultiSend: false, - isUtxo: false, - isERCToken: false, - isStableCoin: false, - singleAddress: true, - }, - paymentInfo: { - paymentCode: 'BIP73', - protocolPrefix: {livenet: 'ripple', testnet: 'ripple'}, - ratesApi: 'https://bws.bitpay.com/bws/api/v3/fiatrates/xrp', - blockExplorerUrls: 'xrpscan.com/', - blockExplorerUrlsTestnet: 'test.bithomp.com/explorer/', - }, - feeInfo: { - feeUnit: 'drops', - feeUnitAmount: 1e6, - blockTime: 0.05, - maxMerchantFee: 'normal', - }, - theme: { - coinColor: '#000000', - backgroundColor: '#565d6d', - gradientBackgroundColor: '#565d6d', - }, - }, - busd: { - name: 'Binance USD Coin', - chain: 'ETH', - coin: 'busd', - unitInfo: { - unitName: 'BUSD', - unitToSatoshi: 1e18, - unitDecimals: 18, - unitCode: 'busd', - }, - properties: { - hasMultiSig: false, - hasMultiSend: false, - isUtxo: false, - isERCToken: true, - isStableCoin: true, - singleAddress: true, - }, - paymentInfo: { - paymentCode: 'EIP681b', - protocolPrefix: {livenet: 'ethereum', testnet: 'ethereum'}, - ratesApi: 'https://bws.bitpay.com/bws/api/v3/fiatrates/busd', - blockExplorerUrls: 'etherscan.io/', - blockExplorerUrlsTestnet: 'kovan.etherscan.io/', - }, - feeInfo: { - feeUnit: 'Gwei', - feeUnitAmount: 1e9, - blockTime: 0.2, - maxMerchantFee: 'urgent', - }, - theme: { - coinColor: '#f3ba2d', - backgroundColor: 'rgba(135,206,250,1)', - gradientBackgroundColor: 'rgba(30,144,255, 0.2)', - }, - }, +export const BitpaySupportedEthereumTokens: {[key in string]: CurrencyOpts} = { usdp: { name: 'Paxos Dollar', chain: 'ETH', @@ -466,80 +284,6 @@ export const Currencies: {[key in string]: CurrencyOpts} = { gradientBackgroundColor: '#282A47', }, }, - doge: { - name: 'Dogecoin', - chain: 'DOGE', - coin: 'doge', - unitInfo: { - unitName: 'DOGE', - unitToSatoshi: 1e8, - unitDecimals: 8, - unitCode: 'doge', - }, - properties: { - hasMultiSig: true, - hasMultiSend: true, - isUtxo: true, - isERCToken: false, - isStableCoin: false, - singleAddress: false, - }, - paymentInfo: { - paymentCode: 'BIP73', - protocolPrefix: {livenet: 'dogecoin', testnet: 'dogecoin'}, - ratesApi: 'https://bws.bitpay.com/bws/api/v3/fiatrates/doge', - blockExplorerUrls: 'blockchair.com/', - blockExplorerUrlsTestnet: 'sochain.com/', - }, - feeInfo: { - feeUnit: 'sat/byte', - feeUnitAmount: 1e8, - blockTime: 10, - maxMerchantFee: 'normal', - }, - theme: { - coinColor: '#d8c172', - backgroundColor: '#d8c172', - gradientBackgroundColor: '#d8c172', - }, - }, - ltc: { - name: 'Litecoin', - chain: 'LTC', - coin: 'ltc', - unitInfo: { - unitName: 'LTC', - unitToSatoshi: 100000000, - unitDecimals: 8, - unitCode: 'ltc', - }, - properties: { - hasMultiSig: true, - hasMultiSend: true, - isUtxo: true, - isERCToken: false, - isStableCoin: false, - singleAddress: false, - }, - paymentInfo: { - paymentCode: 'BIP73', - protocolPrefix: {livenet: 'litecoin', testnet: 'litecoin'}, - ratesApi: 'https://bws.bitpay.com/bws/api/v3/fiatrates/ltc', - blockExplorerUrls: 'bitpay.com/insight/#/LTC/mainnet/', - blockExplorerUrlsTestnet: 'bitpay.com/insight/#/LTC/testnet/', - }, - feeInfo: { - feeUnit: 'sat/byte', - feeUnitAmount: 1000, - blockTime: 2.5, - maxMerchantFee: 'normal', - }, - theme: { - coinColor: '#A6A9AA', - backgroundColor: '#A6A9AA', - gradientBackgroundColor: '#A6A9AA', - }, - }, shib: { name: 'SHIBA INU', chain: 'ETH', @@ -656,19 +400,239 @@ export const Currencies: {[key in string]: CurrencyOpts} = { }, }; -export const SUPPORTED_TOKENS = [ - 'usdc', - 'gusd', - 'usdp', - 'busd', - 'dai', - 'wbtc', - 'shib', - 'ape', - 'euroc', -]; -export const SUPPORTED_COINS = ['btc', 'bch', 'eth', 'doge', 'ltc', 'xrp']; -export const SUPPORTED_CURRENCIES = [...SUPPORTED_COINS, ...SUPPORTED_TOKENS]; +export const BitpaySupportedUtxoCoins: {[key in string]: CurrencyOpts} = { + btc: { + name: 'Bitcoin', + chain: 'BTC', + coin: 'btc', + unitInfo: { + unitName: 'BTC', + unitToSatoshi: 100000000, + unitDecimals: 8, + unitCode: 'btc', + }, + properties: { + hasMultiSig: true, + hasMultiSend: true, + isUtxo: true, + isERCToken: false, + isStableCoin: false, + singleAddress: false, + }, + paymentInfo: { + paymentCode: 'BIP73', + protocolPrefix: {livenet: 'bitcoin', testnet: 'bitcoin'}, + ratesApi: 'https://bws.bitpay.com/bws/api/v3/fiatrates/btc', + blockExplorerUrls: 'bitpay.com/insight/#/BTC/mainnet/', + blockExplorerUrlsTestnet: 'bitpay.com/insight/#/BTC/testnet/', + }, + feeInfo: { + feeUnit: 'sat/byte', + feeUnitAmount: 1000, + blockTime: 10, + maxMerchantFee: 'urgent', + }, + theme: { + coinColor: '#f7931a', + backgroundColor: '#f7921a', + gradientBackgroundColor: '#f7921a', + }, + }, + bch: { + name: 'Bitcoin Cash', + chain: 'BCH', + coin: 'bch', + unitInfo: { + unitName: 'BCH', + unitToSatoshi: 100000000, + unitDecimals: 8, + unitCode: 'bch', + }, + properties: { + hasMultiSig: true, + hasMultiSend: true, + isUtxo: true, + isERCToken: false, + isStableCoin: false, + singleAddress: false, + }, + paymentInfo: { + paymentCode: 'BIP73', + protocolPrefix: {livenet: 'bitcoincash', testnet: 'bchtest'}, + ratesApi: 'https://bws.bitpay.com/bws/api/v3/fiatrates/bch', + blockExplorerUrls: 'bitpay.com/insight/#/BCH/mainnet/', + blockExplorerUrlsTestnet: 'bitpay.com/insight/#/BCH/testnet/', + }, + feeInfo: { + feeUnit: 'sat/byte', + feeUnitAmount: 1000, + blockTime: 10, + maxMerchantFee: 'normal', + }, + theme: { + coinColor: '#2fcf6e', + backgroundColor: '#2fcf6e', + gradientBackgroundColor: '#2fcf6e', + }, + }, + xrp: { + name: 'XRP', + chain: 'XRP', + coin: 'xrp', + unitInfo: { + unitName: 'XRP', + unitToSatoshi: 1e6, + unitDecimals: 6, + unitCode: 'xrp', + }, + properties: { + hasMultiSig: false, + hasMultiSend: false, + isUtxo: false, + isERCToken: false, + isStableCoin: false, + singleAddress: true, + }, + paymentInfo: { + paymentCode: 'BIP73', + protocolPrefix: {livenet: 'ripple', testnet: 'ripple'}, + ratesApi: 'https://bws.bitpay.com/bws/api/v3/fiatrates/xrp', + blockExplorerUrls: 'xrpscan.com/', + blockExplorerUrlsTestnet: 'test.bithomp.com/explorer/', + }, + feeInfo: { + feeUnit: 'drops', + feeUnitAmount: 1e6, + blockTime: 0.05, + maxMerchantFee: 'normal', + }, + theme: { + coinColor: '#000000', + backgroundColor: '#565d6d', + gradientBackgroundColor: '#565d6d', + }, + }, + doge: { + name: 'Dogecoin', + chain: 'DOGE', + coin: 'doge', + unitInfo: { + unitName: 'DOGE', + unitToSatoshi: 1e8, + unitDecimals: 8, + unitCode: 'doge', + }, + properties: { + hasMultiSig: true, + hasMultiSend: true, + isUtxo: true, + isERCToken: false, + isStableCoin: false, + singleAddress: false, + }, + paymentInfo: { + paymentCode: 'BIP73', + protocolPrefix: {livenet: 'dogecoin', testnet: 'dogecoin'}, + ratesApi: 'https://bws.bitpay.com/bws/api/v3/fiatrates/doge', + blockExplorerUrls: 'blockchair.com/', + blockExplorerUrlsTestnet: 'sochain.com/', + }, + feeInfo: { + feeUnit: 'sat/byte', + feeUnitAmount: 1e8, + blockTime: 10, + maxMerchantFee: 'normal', + }, + theme: { + coinColor: '#d8c172', + backgroundColor: '#d8c172', + gradientBackgroundColor: '#d8c172', + }, + }, + ltc: { + name: 'Litecoin', + chain: 'LTC', + coin: 'ltc', + unitInfo: { + unitName: 'LTC', + unitToSatoshi: 100000000, + unitDecimals: 8, + unitCode: 'ltc', + }, + properties: { + hasMultiSig: true, + hasMultiSend: true, + isUtxo: true, + isERCToken: false, + isStableCoin: false, + singleAddress: false, + }, + paymentInfo: { + paymentCode: 'BIP73', + protocolPrefix: {livenet: 'litecoin', testnet: 'litecoin'}, + ratesApi: 'https://bws.bitpay.com/bws/api/v3/fiatrates/ltc', + blockExplorerUrls: 'bitpay.com/insight/#/LTC/mainnet/', + blockExplorerUrlsTestnet: 'bitpay.com/insight/#/LTC/testnet/', + }, + feeInfo: { + feeUnit: 'sat/byte', + feeUnitAmount: 1000, + blockTime: 2.5, + maxMerchantFee: 'normal', + }, + theme: { + coinColor: '#A6A9AA', + backgroundColor: '#A6A9AA', + gradientBackgroundColor: '#A6A9AA', + }, + }, +}; +export const BitpaySupportedEvmCoins: {[key in string]: CurrencyOpts} = { + eth: { + name: 'Ethereum', + chain: 'ETH', + coin: 'eth', + unitInfo: { + unitName: 'ETH', + unitToSatoshi: 1e18, + unitDecimals: 18, + unitCode: 'eth', + }, + properties: { + hasMultiSig: true, + hasMultiSend: false, + isUtxo: false, + isERCToken: false, + isStableCoin: false, + singleAddress: true, + }, + paymentInfo: { + paymentCode: 'EIP681', + protocolPrefix: {livenet: 'ethereum', testnet: 'ethereum'}, + ratesApi: 'https://bws.bitpay.com/bws/api/v3/fiatrates/eth', + blockExplorerUrls: 'etherscan.io/', + blockExplorerUrlsTestnet: 'kovan.etherscan.io/', + }, + feeInfo: { + feeUnit: 'Gwei', + feeUnitAmount: 1e9, + blockTime: 0.2, + maxMerchantFee: 'urgent', + }, + theme: { + coinColor: '#6b71d6', + backgroundColor: '#6b71d6', + gradientBackgroundColor: '#6b71d6', + }, + tokens: BitpaySupportedEthereumTokens, + }, +}; + +export const BitpaySupportedCoins: {[key in string]: CurrencyOpts} = { + ...BitpaySupportedUtxoCoins, + ...BitpaySupportedEvmCoins, +}; + export const POPULAR_TOKENS = [ 'UNI', 'SUSHI', @@ -692,3 +656,12 @@ export const POPULAR_TOKENS = [ 'CRV', 'RUNE', ]; + +export const SUPPORTED_ETHEREUM_TOKENS = Object.keys( + BitpaySupportedEthereumTokens, +); +export const SUPPORTED_COINS = Object.keys(BitpaySupportedCoins); +export const SUPPORTED_CURRENCIES = [ + ...SUPPORTED_COINS, + ...SUPPORTED_ETHEREUM_TOKENS, +]; diff --git a/src/constants/tokens.ts b/src/constants/tokens.ts index 59ae1187c4..e1f69ad386 100644 --- a/src/constants/tokens.ts +++ b/src/constants/tokens.ts @@ -7,7 +7,7 @@ export type TokenOptsType = { export const TokensListAPIUrl = 'https://bitpay.api.enterprise.1inch.exchange/v3.0/1/tokens'; -export const BitpaySupportedTokenOpts: TokenOptsType = { +export const BitpaySupportedEthereumTokenOpts: TokenOptsType = { usdc: { name: 'USD Coin', symbol: 'USDC', @@ -71,7 +71,7 @@ export const BitpaySupportedTokenOpts: TokenOptsType = { }, }; -export const BitpaySupportedTokenOptsByAddress: TokenOptsType = { +export const BitpaySupportedEthereumTokenOptsByAddress: TokenOptsType = { '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48': { name: 'USD Coin', symbol: 'USDC', diff --git a/src/lib/bwc.ts b/src/lib/bwc.ts index 4bcc9709a9..b8c818ee28 100644 --- a/src/lib/bwc.ts +++ b/src/lib/bwc.ts @@ -18,9 +18,7 @@ export class BwcProvider { static instance: BwcProvider; static API = BWC; - constructor() { - //console.log('BWC instance created'); - } + constructor() {} // creating singleton public static getInstance(): BwcProvider { if (!BwcProvider.instance) { diff --git a/src/navigation/coinbase/screens/CoinbaseWithdrawConfirm.tsx b/src/navigation/coinbase/screens/CoinbaseWithdrawConfirm.tsx index 0080597b0e..5a21843d0f 100644 --- a/src/navigation/coinbase/screens/CoinbaseWithdrawConfirm.tsx +++ b/src/navigation/coinbase/screens/CoinbaseWithdrawConfirm.tsx @@ -36,7 +36,7 @@ import prompt from 'react-native-prompt-android'; export interface CoinbaseWithdrawConfirmParamList { accountId: string; - wallet: Wallet | undefined; + wallet: Wallet; amount: number; } @@ -67,12 +67,12 @@ const CoinbaseWithdrawConfirm = () => { const apiLoading = useAppSelector(({COINBASE}) => COINBASE.isApiLoading); - const currency = wallet?.credentials.coin; + const currency = wallet.currencyAbbreviation; const recipientData = { - recipientName: wallet?.credentials.walletName || 'BitPay Wallet', + recipientName: wallet.credentials.walletName || 'BitPay Wallet', recipientAddress: receiveAddress, - img: wallet?.img || wallet?.credentials.coin, + img: wallet.img || wallet.currencyAbbreviation, }; const sendingFrom = { diff --git a/src/navigation/onboarding/screens/Pin.tsx b/src/navigation/onboarding/screens/Pin.tsx index 2630f5aa47..aab09f1946 100644 --- a/src/navigation/onboarding/screens/Pin.tsx +++ b/src/navigation/onboarding/screens/Pin.tsx @@ -87,11 +87,6 @@ const PinScreen: React.VFC< haptic('impactLight'); TouchID.isSupported(isSupportedOptionalConfigObject) .then(biometryType => { - if (biometryType === 'FaceID') { - console.log('FaceID is supported.'); - } else { - console.log('TouchID is supported.'); - } return TouchID.authenticate( 'Authentication Check', authOptionalConfigObject, diff --git a/src/navigation/services/buy-crypto/BuyCryptoStack.tsx b/src/navigation/services/buy-crypto/BuyCryptoStack.tsx index 9b0a341190..fd83861e77 100644 --- a/src/navigation/services/buy-crypto/BuyCryptoStack.tsx +++ b/src/navigation/services/buy-crypto/BuyCryptoStack.tsx @@ -23,6 +23,7 @@ export type BuyCryptoStackParamList = { amount: number; fiatCurrency: string; coin: string; + chain: string; country: string; selectedWallet: any; paymentMethod: any; diff --git a/src/navigation/services/buy-crypto/components/WalletSelectorModal.tsx b/src/navigation/services/buy-crypto/components/WalletSelectorModal.tsx index ffcb8281d4..e2be64f195 100644 --- a/src/navigation/services/buy-crypto/components/WalletSelectorModal.tsx +++ b/src/navigation/services/buy-crypto/components/WalletSelectorModal.tsx @@ -4,7 +4,10 @@ import {Black, White} from '../../../../styles/colors'; import styled from 'styled-components/native'; import SheetModal from '../../../../components/modal/base/sheet/SheetModal'; import {includes, sortBy} from 'lodash'; -import {Currencies} from '../../../../constants/currencies'; +import { + BitpaySupportedCoins, + BitpaySupportedEthereumTokens, +} from '../../../../constants/currencies'; const GlobalSelectContainer = styled.View` flex: 1; @@ -26,7 +29,10 @@ const WalletSelectorModal: React.FC = ({ onDismiss, modalTitle, }) => { - const BitpaySupportedCurrencies: string[] = Object.keys(Currencies); + const BitpaySupportedCurrencies: string[] = { + ...Object.keys(BitpaySupportedCoins), + ...Object.keys(BitpaySupportedEthereumTokens), + }; const sortedCustomSupportedCurrencies = sortBy( customSupportedCurrencies, coin => (includes(BitpaySupportedCurrencies, coin) ? -1 : 1), diff --git a/src/navigation/services/buy-crypto/screens/BuyCryptoOffers.tsx b/src/navigation/services/buy-crypto/screens/BuyCryptoOffers.tsx index 77b81fce22..3cc62ae888 100644 --- a/src/navigation/services/buy-crypto/screens/BuyCryptoOffers.tsx +++ b/src/navigation/services/buy-crypto/screens/BuyCryptoOffers.tsx @@ -20,7 +20,10 @@ import { } from '../../../../components/styled/Text'; import {CurrencyImage} from '../../../../components/currency-image/CurrencyImage'; import {useLogger} from '../../../../utils/hooks/useLogger'; -import {Currencies} from '../../../../constants/currencies'; +import { + BitpaySupportedCoins, + BitpaySupportedEthereumTokens, +} from '../../../../constants/currencies'; import SimplexLogo from '../../../../components/icons/external-services/simplex/simplex-logo'; import WyreLogo from '../../../../components/icons/external-services/wyre/wyre-logo'; import {BuyCryptoExpandibleCard, ItemDivisor} from '../styled/BuyCryptoCard'; @@ -40,7 +43,7 @@ import { } from '../utils/simplex-utils'; import {getWyreFiatAmountLimits, wyreEnv} from '../utils/wyre-utils'; import {RootState} from '../../../../store'; -import {GetChain, GetPrecision} from '../../../../store/wallet/utils/currency'; +import {GetPrecision} from '../../../../store/wallet/utils/currency'; import { showBottomNotificationModal, dismissBottomNotificationModal, @@ -70,6 +73,7 @@ export interface BuyCryptoOffersProps { amount: number; fiatCurrency: string; coin: string; + chain: string; country: string; selectedWallet: Wallet; paymentMethod: PaymentMethod; @@ -288,6 +292,7 @@ const BuyCryptoOffers: React.FC = () => { amount, fiatCurrency, coin, + chain, country, selectedWallet, paymentMethod, @@ -388,7 +393,7 @@ const BuyCryptoOffers: React.FC = () => { offers.simplex.fee = data.fiat_money.total_amount - data.fiat_money.base_amount; - const precision = dispatch(GetPrecision(coin)); + const precision = dispatch(GetPrecision(coin, chain)); if (offers.simplex.buyAmount && coin && precision) { offers.simplex.fiatMoney = Number( offers.simplex.buyAmount / data.digital_money.amount, @@ -420,7 +425,6 @@ const BuyCryptoOffers: React.FC = () => { } }) .catch((err: any) => { - console.log('Simplex getting quote: FAILED', err); const reason = 'simplexGetQuote Error'; showSimplexError(err, reason); }); @@ -573,7 +577,7 @@ const BuyCryptoOffers: React.FC = () => { showWyreError(err, reason); } - const dest = setPrefix(address, coin, selectedWallet.credentials.network); + const dest = setPrefix(address, coin, selectedWallet.network); let walletType: string; switch (paymentMethod.method) { @@ -629,7 +633,6 @@ const BuyCryptoOffers: React.FC = () => { setFinishedWyre(!finishedWyre); }) .catch((err: any) => { - console.log('Wyre getting quote: FAILED', err); const reason = 'wyreWalletOrderQuotation Error'; showWyreError(err, reason); }); @@ -641,8 +644,10 @@ const BuyCryptoOffers: React.FC = () => { coin: string, network: 'livenet' | 'testnet', ): string => { + const _coin = coin.toLowerCase(); const prefix = - Currencies[coin.toLocaleLowerCase()].paymentInfo.protocolPrefix[network]; + BitpaySupportedCoins[_coin].paymentInfo.protocolPrefix[network] || + BitpaySupportedEthereumTokens[_coin].paymentInfo.protocolPrefix[network]; const addr = `${prefix}:${address}`; return addr; }; @@ -684,9 +689,7 @@ const BuyCryptoOffers: React.FC = () => { payment_id: req.payment_id, }; - const destinationChain = dispatch( - GetChain(selectedWallet.currencyAbbreviation), - ); + const destinationChain = selectedWallet.chain; const newData: simplexPaymentData = { address, @@ -760,9 +763,7 @@ const BuyCryptoOffers: React.FC = () => { _paymentMethod = 'debit-card'; break; } - const destinationChain = dispatch( - GetChain(selectedWallet.currencyAbbreviation), - ); + const destinationChain = selectedWallet.chain; const redirectUrl = APP_DEEPLINK_PREFIX + 'wyre?walletId=' + @@ -772,7 +773,7 @@ const BuyCryptoOffers: React.FC = () => { '&destChain=' + destinationChain; const failureRedirectUrl = APP_DEEPLINK_PREFIX + 'wyreError'; - const dest = setPrefix(address, coin, selectedWallet.credentials.network); + const dest = setPrefix(address, coin, selectedWallet.network); const requestData = { sourceAmount: offers.wyre.fiatAmount.toString(), dest, @@ -806,9 +807,7 @@ const BuyCryptoOffers: React.FC = () => { }; const continueToWyre = (paymentUrl: string) => { - const destinationChain = dispatch( - GetChain(selectedWallet.currencyAbbreviation), - ); + const destinationChain = selectedWallet.chain; dispatch( logSegmentEvent('track', 'Requested Crypto Purchase', { exchange: 'wyre', diff --git a/src/navigation/services/buy-crypto/screens/BuyCryptoRoot.tsx b/src/navigation/services/buy-crypto/screens/BuyCryptoRoot.tsx index 09cb6c141d..cafcaf6425 100644 --- a/src/navigation/services/buy-crypto/screens/BuyCryptoRoot.tsx +++ b/src/navigation/services/buy-crypto/screens/BuyCryptoRoot.tsx @@ -133,7 +133,9 @@ const BuyCryptoRoot: React.FC< setWalletData( SupportedCurrencyOptions.find( currency => - selectedWallet && currency.id == selectedWallet.credentials.coin, + selectedWallet && + currency.currencyAbbreviation == + selectedWallet.currencyAbbreviation, ), ); } @@ -202,14 +204,14 @@ const BuyCryptoRoot: React.FC< const walletIsSupported = (wallet: Wallet): boolean => { return ( wallet.credentials && - ((wallet.credentials.network === 'livenet' && + ((wallet.network === 'livenet' && buyCryptoSupportedCoins.includes( - wallet.credentials.coin.toLowerCase(), + wallet.currencyAbbreviation.toLowerCase(), )) || (__DEV__ && - wallet.credentials.network === 'testnet' && + wallet.network === 'testnet' && wyreSupportedCoins.includes( - wallet.credentials.coin.toLowerCase(), + wallet.currencyAbbreviation.toLowerCase(), ))) && wallet.isComplete() && (!fromCurrencyAbbreviation || @@ -220,13 +222,15 @@ const BuyCryptoRoot: React.FC< const setWallet = (wallet: Wallet) => { if ( wallet.credentials && - ((wallet.credentials.network === 'livenet' && + ((wallet.network === 'livenet' && buyCryptoSupportedCoins.includes( - wallet.credentials.coin.toLowerCase(), + wallet.currencyAbbreviation.toLowerCase(), )) || (__DEV__ && - wallet.credentials.network === 'testnet' && - wyreSupportedCoins.includes(wallet.credentials.coin.toLowerCase()))) + wallet.network === 'testnet' && + wyreSupportedCoins.includes( + wallet.currencyAbbreviation.toLowerCase(), + ))) ) { if (wallet.isComplete()) { if (allKeys[wallet.keyId].backupComplete) { @@ -290,10 +294,7 @@ const BuyCryptoRoot: React.FC< await sleep(600); showTokensInfoSheet(); }; - if ( - !!selectedWallet && - dispatch(IsERCToken(selectedWallet.currencyAbbreviation)) - ) { + if (!!selectedWallet && IsERCToken(selectedWallet.currencyAbbreviation)) { tokensWarn(); } else { continueToViewOffers(); @@ -314,6 +315,7 @@ const BuyCryptoRoot: React.FC< amount, fiatCurrency, coin: selectedWallet?.currencyAbbreviation || '', + chain: selectedWallet?.chain || '', country: countryData?.shortCode || 'US', selectedWallet, paymentMethod: selectedPaymentMethod, @@ -530,7 +532,7 @@ const BuyCryptoRoot: React.FC< - {selectedWallet.credentials.coin.toUpperCase()} + {selectedWallet.currencyAbbreviation.toUpperCase()} diff --git a/src/navigation/services/swap-crypto/screens/ChangellyCheckout.tsx b/src/navigation/services/swap-crypto/screens/ChangellyCheckout.tsx index fa1e5920ad..c5a1a9ec50 100644 --- a/src/navigation/services/swap-crypto/screens/ChangellyCheckout.tsx +++ b/src/navigation/services/swap-crypto/screens/ChangellyCheckout.tsx @@ -15,7 +15,7 @@ import { useLogger, } from '../../../../utils/hooks'; import ChangellyCheckoutSkeleton from './ChangellyCheckoutSkeleton'; -import {BitpaySupportedTokenOpts} from '../../../../constants/tokens'; +import {BitpaySupportedEthereumTokenOpts} from '../../../../constants/tokens'; import {BWCErrorMessage} from '../../../../constants/BWCError'; import {Black, White, Slate, Caution} from '../../../../styles/colors'; import {BwcProvider} from '../../../../lib/bwc'; @@ -39,7 +39,6 @@ import { import { GetPrecision, IsERCToken, - GetChain, } from '../../../../store/wallet/utils/currency'; import { FormatAmountStr, @@ -87,7 +86,6 @@ import {changellyTxData} from '../../../../store/swap-crypto/swap-crypto.models' import {SwapCryptoActions} from '../../../../store/swap-crypto'; import SelectorArrowRight from '../../../../../assets/img/selector-arrow-right.svg'; import {useTranslation} from 'react-i18next'; -import {Currencies} from '../../../../constants/currencies'; import {swapCryptoCoin} from './SwapCryptoRoot'; // Styled @@ -173,9 +171,10 @@ const ChangellyCheckout: React.FC = () => { if (fromWalletSelected.currencyAbbreviation.toLowerCase() === 'bch') { addressFrom = dispatch( GetProtocolPrefixAddress( - fromWalletSelected.currencyAbbreviation.toLowerCase(), + fromWalletSelected.currencyAbbreviation, fromWalletSelected.network, addressFrom, + fromWalletSelected.chain, ), ); } @@ -262,13 +261,17 @@ const ChangellyCheckout: React.FC = () => { try { const rates = await dispatch(startGetRates({})); const presicion = dispatch( - GetPrecision(toWalletSelected.currencyAbbreviation), + GetPrecision( + toWalletSelected.currencyAbbreviation, + toWalletSelected.chain, + ), ); const newFiatAmountTo = dispatch( toFiat( Number(amountTo) * presicion!.unitToSatoshi, alternativeIsoCode, toWalletSelected.currencyAbbreviation.toLowerCase(), + toWalletSelected.chain, rates, ), ); @@ -280,7 +283,10 @@ const ChangellyCheckout: React.FC = () => { paymentTimeControl(data.result.payTill); const presicion = dispatch( - GetPrecision(fromWalletSelected.currencyAbbreviation), + GetPrecision( + fromWalletSelected.currencyAbbreviation, + fromWalletSelected.chain, + ), ); // To Sat const depositSat = Number( @@ -306,7 +312,7 @@ const ChangellyCheckout: React.FC = () => { await sleep(400); if (useSendMax) { - showSendMaxWarning(ctxp.coin); + showSendMaxWarning(ctxp.coin, ctxp.chain); } return; }) @@ -439,8 +445,8 @@ const ChangellyCheckout: React.FC = () => { }, }; - if (dispatch(IsERCToken(wallet.currencyAbbreviation.toLowerCase()))) { - let tokens = Object.values(BitpaySupportedTokenOpts); + if (IsERCToken(wallet.currencyAbbreviation)) { + let tokens = Object.values(BitpaySupportedEthereumTokenOpts); const token = tokens.find( token => token.symbol === wallet.currencyAbbreviation.toUpperCase(), ); @@ -469,8 +475,7 @@ const ChangellyCheckout: React.FC = () => { } else { if ( wallet.currencyAbbreviation.toLowerCase() === 'btc' || - dispatch(GetChain(wallet.currencyAbbreviation.toLowerCase())) === - 'eth' + wallet.chain === 'eth' ) { txp.feeLevel = 'priority'; } // Avoid expired order due to slow TX confirmation @@ -564,19 +569,18 @@ const ChangellyCheckout: React.FC = () => { ); }; - const showSendMaxWarning = async (coin: string) => { + const showSendMaxWarning = async (coin: string, chain: string) => { if (!sendMaxInfo || !coin) { return; } - const warningMsg = dispatch(GetExcludedUtxosMessage(coin, sendMaxInfo)); - const chainName = dispatch(IsERCToken(coin)) - ? Currencies.eth.name - : Currencies[coin]?.name; - const fee = dispatch(SatToUnit(sendMaxInfo.fee, coin)); + const warningMsg = dispatch( + GetExcludedUtxosMessage(coin, chain, sendMaxInfo), + ); + const fee = dispatch(SatToUnit(sendMaxInfo.fee, coin, chain)); const msg = - `Because you are sending the maximum amount contained in this wallet, the ${chainName} miner fee (${fee} ${coin.toUpperCase()}) will be deducted from the total.` + + `Because you are sending the maximum amount contained in this wallet, the ${chain} miner fee (${fee} ${coin.toUpperCase()}) will be deducted from the total.` + `\n${warningMsg}`; await sleep(400); @@ -718,9 +722,8 @@ const ChangellyCheckout: React.FC = () => { {dispatch( FormatAmountStr( - dispatch( - GetChain(fromWalletSelected.currencyAbbreviation), - ).toLowerCase(), + fromWalletSelected.chain, // use chain for miner fee + fromWalletSelected.chain, fee, ), )} diff --git a/src/navigation/services/swap-crypto/screens/SwapCryptoRoot.tsx b/src/navigation/services/swap-crypto/screens/SwapCryptoRoot.tsx index c7de689e93..6376e1e3d8 100644 --- a/src/navigation/services/swap-crypto/screens/SwapCryptoRoot.tsx +++ b/src/navigation/services/swap-crypto/screens/SwapCryptoRoot.tsx @@ -4,7 +4,10 @@ import {useTheme, useNavigation, useRoute} from '@react-navigation/native'; import {RouteProp} from '@react-navigation/core'; import cloneDeep from 'lodash.clonedeep'; import {SupportedCurrencyOptions} from '../../../../constants/SupportedCurrencyOptions'; -import {Currencies} from '../../../../constants/currencies'; +import { + SUPPORTED_COINS, + SUPPORTED_ETHEREUM_TOKENS, +} from '../../../../constants/currencies'; import { Action, SlateDark, @@ -42,7 +45,7 @@ import { import {useAppDispatch, useAppSelector} from '../../../../utils/hooks'; import {sleep} from '../../../../utils/helper-methods'; import {useLogger} from '../../../../utils/hooks/useLogger'; -import {GetChain, IsERCToken} from '../../../../store/wallet/utils/currency'; +import {IsERCToken} from '../../../../store/wallet/utils/currency'; import {getFeeRatePerKb} from '../../../../store/wallet/effects/fee/fee'; import {Wallet, SendMaxInfo} from '../../../../store/wallet/wallet.models'; import {changellyGetCurrencies} from '../../../../store/swap-crypto/effects/changelly/changelly'; @@ -73,6 +76,7 @@ export interface RateData { export interface swapCryptoCoin { currencyAbbreviation: string; + chain: string; name: string; protocol?: string; logoUri?: any; @@ -113,10 +117,9 @@ const SwapCryptoRoot: React.FC = () => { const [sendMaxInfo, setSendMaxInfo] = useState(); const selectedWallet = route.params?.selectedWallet; - const SupportedCurrencies: string[] = Object.keys(Currencies); - const SupportedChains: string[] = [ - ...new Set(Object.values(Currencies).map(({chain}: any) => chain)), - ]; + const SupportedCoins: string[] = SUPPORTED_COINS; + const SupportedEthereumTokens: string[] = SUPPORTED_ETHEREUM_TOKENS; + const SupportedChains: string[] = SUPPORTED_COINS; let minAmount: number, maxAmount: number; const showModal = (id: string) => { @@ -236,6 +239,7 @@ const SwapCryptoRoot: React.FC = () => { SatToUnit( fromWalletSelected.balance.satSpendable, fromWalletSelected.currencyAbbreviation, + fromWalletSelected.chain, ), ); @@ -425,6 +429,7 @@ const SwapCryptoRoot: React.FC = () => { const warningMsg = dispatch( GetExcludedUtxosMessage( fromWalletSelected.currencyAbbreviation, + fromWalletSelected.chain, sendMaxInfo, ), ); @@ -435,6 +440,7 @@ const SwapCryptoRoot: React.FC = () => { SatToUnit( sendMaxInfo.fee, fromWalletSelected.currencyAbbreviation, + fromWalletSelected.chain, ), ); const coin = fromWalletSelected.currencyAbbreviation.toUpperCase(); @@ -481,10 +487,6 @@ const SwapCryptoRoot: React.FC = () => { }); }; - const getChain = (coin: string): string => { - return dispatch(GetChain(coin)).toLowerCase(); - }; - const getSendMaxData = (): Promise => { return new Promise(async (resolve, reject) => { if (!fromWalletSelected) { @@ -493,7 +495,7 @@ const SwapCryptoRoot: React.FC = () => { try { const feeLevel = fromWalletSelected.currencyAbbreviation == 'btc' || - getChain(fromWalletSelected.currencyAbbreviation) == 'eth' + fromWalletSelected.chain == 'eth' ? 'priority' : 'normal'; @@ -593,7 +595,7 @@ const SwapCryptoRoot: React.FC = () => { }; if ( !!toWalletSelected && - dispatch(IsERCToken(toWalletSelected.currencyAbbreviation)) + IsERCToken(toWalletSelected.currencyAbbreviation) ) { tokensWarn(); } else { @@ -619,9 +621,7 @@ const SwapCryptoRoot: React.FC = () => { toWalletData: toWalletData!, fixedRateId: rateData!.fixedRateId, amountFrom: amountFrom, - useSendMax: dispatch( - IsERCToken(fromWalletSelected!.currencyAbbreviation.toLowerCase()), - ) + useSendMax: IsERCToken(fromWalletSelected!.currencyAbbreviation) ? false : useSendMax, sendMaxInfo: sendMaxInfo, @@ -635,10 +635,14 @@ const SwapCryptoRoot: React.FC = () => { if (changellyCurrenciesData?.result?.length) { const getLogoUri = (coin: string) => { if ( - SupportedCurrencyOptions.find(({id}) => id === coin.toLowerCase()) + SupportedCurrencyOptions.find( + ({currencyAbbreviation}) => + currencyAbbreviation === coin.toLowerCase(), + ) ) { return SupportedCurrencyOptions.find( - ({id}) => id === coin.toLowerCase(), + ({currencyAbbreviation}) => + currencyAbbreviation === coin.toLowerCase(), )!.img; } else if (tokenData[coin.toLowerCase()]?.logoURI) { return tokenData[coin.toLowerCase()]?.logoURI; @@ -654,9 +658,9 @@ const SwapCryptoRoot: React.FC = () => { (coin: any) => coin.enabled && coin.fixRateEnabled && - [...SupportedChains, 'ERC20'].includes( + [...SupportedChains, 'erc20'].includes( // TODO: add MATIC - coin.protocol?.toUpperCase(), + coin.protocol?.toLowerCase(), ), ) .map( @@ -673,6 +677,9 @@ const SwapCryptoRoot: React.FC = () => { }) => ({ currencyAbbreviation: name, name: fullName, + chain: (protocol?.toLowerCase() === 'erc20' + ? 'eth' + : protocol?.toLowerCase())!, protocol, logoUri: getLogoUri(name), contractAddress, @@ -684,12 +691,20 @@ const SwapCryptoRoot: React.FC = () => { // Sort the array with our supported coins first and then the unsupported ones sorted alphabetically let supportedCoins = supportedCoinsWithFixRateEnabled.sort((a, b) => { if ( - SupportedCurrencies.includes(b.currencyAbbreviation) || - SupportedCurrencies.includes(a.currencyAbbreviation) + SupportedCoins.includes(b.currencyAbbreviation) || + SupportedCoins.includes(a.currencyAbbreviation) + ) { + return ( + +SupportedCoins.includes(b.currencyAbbreviation) - + +SupportedCoins.includes(a.currencyAbbreviation) + ); + } else if ( + SupportedEthereumTokens.includes(b.currencyAbbreviation) || + SupportedEthereumTokens.includes(a.currencyAbbreviation) ) { return ( - +SupportedCurrencies.includes(b.currencyAbbreviation) - - +SupportedCurrencies.includes(a.currencyAbbreviation) + +SupportedEthereumTokens.includes(b.currencyAbbreviation) - + +SupportedEthereumTokens.includes(a.currencyAbbreviation) ); } else { return a.name.localeCompare(b.name); @@ -981,6 +996,7 @@ const SwapCryptoRoot: React.FC = () => { hideModal('amount')} onSubmit={newAmount => { hideModal('amount'); @@ -997,11 +1013,7 @@ const SwapCryptoRoot: React.FC = () => { let newAmount: number | undefined; - if ( - dispatch( - IsERCToken(fromWalletSelected.currencyAbbreviation.toLowerCase()), - ) - ) { + if (IsERCToken(fromWalletSelected.currencyAbbreviation)) { setUseSendMax(true); setSendMaxInfo(undefined); newAmount = Number(fromWalletSelected.balance.cryptoSpendable); @@ -1013,7 +1025,8 @@ const SwapCryptoRoot: React.FC = () => { newAmount = dispatch( SatToUnit( data.amount, - fromWalletSelected.currencyAbbreviation.toLowerCase(), + fromWalletSelected.currencyAbbreviation, + fromWalletSelected.chain, ), ); } diff --git a/src/navigation/tabs/contacts/components/ContactIcon.tsx b/src/navigation/tabs/contacts/components/ContactIcon.tsx index 44c85f5c18..fe9de150a0 100644 --- a/src/navigation/tabs/contacts/components/ContactIcon.tsx +++ b/src/navigation/tabs/contacts/components/ContactIcon.tsx @@ -8,12 +8,14 @@ import {CurrencyImage} from '../../../../components/currency-image/CurrencyImage import {SUPPORTED_CURRENCIES} from '../../../../constants/currencies'; import {useAppSelector} from '../../../../utils/hooks'; import {RootState} from '../../../../store'; -import {BitpaySupportedTokenOpts} from '../../../../constants/tokens'; +import {BitpaySupportedEthereumTokenOpts} from '../../../../constants/tokens'; +import {Token} from '../../../../store/wallet/wallet.models'; interface ContactIconProps { size?: number; name?: string; coin: string; + chain: string; } interface BadgeProps { @@ -39,18 +41,26 @@ const CoinBadge: React.FC = ({size = 20, img}) => { ); }; -const ContactIcon: React.FC = ({coin, size = 50, name}) => { +const ContactIcon: React.FC = ({ + coin, + chain, + size = 50, + name, +}) => { const tokenOptions = useAppSelector(({WALLET}: RootState) => { return { - ...BitpaySupportedTokenOpts, - ...WALLET.tokenOptions, - ...WALLET.customTokenOptions, + eth: { + ...BitpaySupportedEthereumTokenOpts, + ...WALLET.tokenOptions, + ...WALLET.customTokenOptions, + }, }; - }); + }) as {[key in string]: {[key in string]: Token}}; + const img = SUPPORTED_CURRENCIES.includes(coin) ? CurrencyListIcons[coin] - : tokenOptions && tokenOptions[coin]?.logoURI - ? (tokenOptions[coin].logoURI as string) + : tokenOptions && tokenOptions[chain] && tokenOptions[chain][coin]?.logoURI + ? (tokenOptions[chain][coin].logoURI as string) : ''; const badge = coin ? : null; diff --git a/src/navigation/tabs/contacts/screens/ContactsAdd.tsx b/src/navigation/tabs/contacts/screens/ContactsAdd.tsx index a4714a173b..ac73d440b0 100644 --- a/src/navigation/tabs/contacts/screens/ContactsAdd.tsx +++ b/src/navigation/tabs/contacts/screens/ContactsAdd.tsx @@ -5,7 +5,7 @@ import React, { useLayoutEffect, useEffect, } from 'react'; -import {FlatList} from 'react-native'; +import {FlatList, View} from 'react-native'; import {yupResolver} from '@hookform/resolvers/yup'; import yup from '../../../../lib/yup'; import styled, {useTheme} from 'styled-components/native'; @@ -24,6 +24,7 @@ import { ActiveOpacity, SearchContainer, SearchInput, + Column, } from '../../../../components/styled/Containers'; import {ValidateCoinAddress} from '../../../../store/wallet/utils/validations'; import {GetCoinAndNetwork} from '../../../../store/wallet/effects/address/address'; @@ -34,29 +35,40 @@ import { createContact, updateContact, } from '../../../../store/contact/contact.actions'; -import {SupportedCurrencyOptions} from '../../../../constants/SupportedCurrencyOptions'; import SuccessIcon from '../../../../../assets/img/success.svg'; import SearchSvg from '../../../../../assets/img/search.svg'; import ScanSvg from '../../../../../assets/img/onboarding/scan.svg'; import SheetModal from '../../../../components/modal/base/sheet/SheetModal'; -import {keyExtractor, findContact} from '../../../../utils/helper-methods'; -import CurrencySelectionRow from '../../../../components/list/CurrencySelectionRow'; +import { + keyExtractor, + findContact, + getBadgeImg, +} from '../../../../utils/helper-methods'; +import CurrencySelectionRow, { + TokenSelectionRow, +} from '../../../../components/list/CurrencySelectionRow'; import NetworkSelectionRow, { NetworkSelectionProps, } from '../../../../components/list/NetworkSelectionRow'; import {LightBlack, NeutralSlate, Slate} from '../../../../styles/colors'; import {CurrencyImage} from '../../../../components/currency-image/CurrencyImage'; import WalletIcons from '../../../wallet/components/WalletIcons'; -import {SUPPORTED_CURRENCIES} from '../../../../constants/currencies'; -import {BitpaySupportedTokenOpts} from '../../../../constants/tokens'; +import {SUPPORTED_ETHEREUM_TOKENS} from '../../../../constants/currencies'; +import {BitpaySupportedEthereumTokenOpts} from '../../../../constants/tokens'; import {useAppDispatch, useAppSelector} from '../../../../utils/hooks'; -import {GetChain} from '../../../../store/wallet/utils/currency'; import {TouchableOpacity} from 'react-native-gesture-handler'; import debounce from 'lodash.debounce'; import {useTranslation} from 'react-i18next'; import {logSegmentEvent} from '../../../../store/app/app.effects'; import {ContactsStackParamList} from '../ContactsStack'; import {StackScreenProps} from '@react-navigation/stack'; +import { + SupportedCurrencyOption, + SupportedEvmCurrencyOptions, + SupportedTokenOptions, +} from '../../../../constants/SupportedCurrencyOptions'; +import Checkbox from '../../../../components/checkbox/Checkbox'; +import {IsERCToken} from '../../../../store/wallet/utils/currency'; const InputContainer = styled.View<{hideInput?: boolean}>` display: ${({hideInput}) => (!hideInput ? 'flex' : 'none')}; @@ -143,6 +155,22 @@ const SearchImageContainer = styled.View` width: 50px; align-items: center; `; + +const IsTokenAddressContainer = styled.TouchableOpacity` + flex-direction: row; + align-items: center; +`; + +const IsTokenAddressTitle = styled(BaseText)` + font-size: 16px; + color: ${({theme}) => (theme && theme.dark ? theme.colors.text : '#434d5a')}; +`; + +const CheckBoxContainer = styled.View` + flex-direction: column; + justify-content: center; +`; + const ContactsAdd = ({ route, }: StackScreenProps) => { @@ -172,58 +200,54 @@ const ContactsAdd = ({ const [addressValue, setAddressValue] = useState(''); const [coinValue, setCoinValue] = useState(''); const [networkValue, setNetworkValue] = useState(''); + const [chainValue, setChainValue] = useState(''); + const [tokenModalVisible, setTokenModalVisible] = useState(false); const [currencyModalVisible, setCurrencyModalVisible] = useState(false); const [networkModalVisible, setNetworkModalVisible] = useState(false); + const [isTokenAddress, setIsTokenAddress] = useState(false); - const tokenOptions = useAppSelector(({WALLET}: RootState) => { + const ethereumTokenOptions = useAppSelector(({WALLET}: RootState) => { return { - ...BitpaySupportedTokenOpts, + ...BitpaySupportedEthereumTokenOpts, ...WALLET.tokenOptions, ...WALLET.customTokenOptions, }; }); - const ALL_CUSTOM_TOKENS = useMemo( + const ALL_CUSTOM_ETHEREUM_TOKENS = useMemo( () => - Object.values(tokenOptions) + Object.values(ethereumTokenOptions) .filter( - token => !SUPPORTED_CURRENCIES.includes(token.symbol.toLowerCase()), + token => + !SUPPORTED_ETHEREUM_TOKENS.includes(token.symbol.toLowerCase()), ) .map(({symbol, name, logoURI}) => { + const chain = 'eth'; return { - id: symbol.toLowerCase(), + id: Math.random().toString(), + coin: symbol.toLowerCase(), currencyAbbreviation: symbol, currencyName: name, - img: logoURI, + img: logoURI || '', isToken: true, - checked: false, - }; + chain, + badgeUri: getBadgeImg(symbol.toLowerCase(), chain), + } as SupportedCurrencyOption; }), - [tokenOptions], + [ethereumTokenOptions], ); - const ALL_CURRENCIES = useMemo( - () => [...SupportedCurrencyOptions, ...ALL_CUSTOM_TOKENS], - [ALL_CUSTOM_TOKENS], + const ALL_ETHEREUM_TOKENS = useMemo( + () => [...SupportedTokenOptions, ...ALL_CUSTOM_ETHEREUM_TOKENS], + [ALL_CUSTOM_ETHEREUM_TOKENS], ); - const ETH_CHAIN_CURRENCIES = useMemo( - () => - ALL_CURRENCIES.filter( - currency => - dispatch(GetChain(currency.currencyAbbreviation)).toLowerCase() === - 'eth', - ), - [ALL_CURRENCIES, dispatch], - ); - - const [ethCurrencyOptions, setEthCurrencyOptions] = useState>([ - ...ETH_CHAIN_CURRENCIES, - ]); + const [ethTokenOptions, setEthTokenOptions] = useState(ALL_ETHEREUM_TOKENS); + const [selectedToken, setSelectedToken] = useState(ALL_ETHEREUM_TOKENS[0]); const [selectedCurrency, setSelectedCurrency] = useState( - ETH_CHAIN_CURRENCIES[0], + SupportedEvmCurrencyOptions[0], ); const networkOptions = [ @@ -247,29 +271,34 @@ const ContactsAdd = ({ let _searchList: Array = []; if (search) { search = search.toLowerCase(); - _searchList = ethCurrencyOptions.filter( + _searchList = ethTokenOptions.filter( ({currencyAbbreviation, currencyName}) => currencyAbbreviation.toLowerCase().includes(search) || currencyName.toLowerCase().includes(search), ); } else { - _searchList = ethCurrencyOptions; + _searchList = ethTokenOptions; } - setEthCurrencyOptions(_searchList); + setEthTokenOptions(_searchList); }, 300), - [ethCurrencyOptions], + [ethTokenOptions], ); - const setValidValues = (address: string, coin: string, network: string) => { + const setValidValues = ( + address: string, + coin: string, + network: string, + chain: string, + ) => { setValidAddress(true); setAddressValue(address); setCoinValue(coin); setNetworkValue(network); + setChainValue(chain); - // Selected current coin _setSelectedCurrency(coin); - switch (coin) { + switch (chain) { case 'eth': setEthValidAddress(true); return; @@ -291,7 +320,12 @@ const ContactsAdd = ({ coinAndNetwork.network, ); if (isValid) { - setValidValues(address, coinAndNetwork.coin, coinAndNetwork.network); + setValidValues( + address, + coinAndNetwork.coin, + coinAndNetwork.network, + coinAndNetwork.coin, + ); } else { // try testnet const isValidTest = ValidateCoinAddress( @@ -300,7 +334,12 @@ const ContactsAdd = ({ 'testnet', ); if (isValidTest) { - setValidValues(address, coinAndNetwork.coin, 'testnet'); + setValidValues( + address, + coinAndNetwork.coin, + 'testnet', + coinAndNetwork.coin, + ); } } } else { @@ -323,8 +362,9 @@ const ContactsAdd = ({ return; } - if (coinValue && networkValue) { + if (coinValue && chainValue && networkValue) { contact.coin = coinValue; + contact.chain = chainValue; contact.network = networkValue; } else { setError('address', { @@ -349,7 +389,9 @@ const ContactsAdd = ({ return; } - if (findContact(contacts, addressValue, coinValue, networkValue)) { + if ( + findContact(contacts, addressValue, coinValue, networkValue, chainValue) + ) { setError('address', { type: 'manual', message: t('Contact already exists'), @@ -361,16 +403,34 @@ const ContactsAdd = ({ navigation.goBack(); }); - const _setSelectedCurrency = (id: string) => { - const _selectedCurrency = ethCurrencyOptions.filter( - currency => currency.id === id, + const _setSelectedToken = (currencyAbbreviation: string) => { + const _selectedToken = ethTokenOptions.find( + token => token.currencyAbbreviation === currencyAbbreviation, + ); + setSelectedToken(_selectedToken || ethTokenOptions[0]); + }; + + const _setSelectedCurrency = (currencyAbbreviation: string) => { + const _selectedCurrency = SupportedEvmCurrencyOptions.filter( + currency => currency.currencyAbbreviation === currencyAbbreviation, ); setSelectedCurrency(_selectedCurrency[0]); }; - const currencySelected = (id: string) => { - _setSelectedCurrency(id); - setCoinValue(id); + const tokenSelected = (currencyAbbreviation: string) => { + _setSelectedToken(currencyAbbreviation); + setCoinValue(currencyAbbreviation); + setTokenModalVisible(false); + }; + + const currencySelected = ( + currencyAbbreviation: string, + isTokenAddress: boolean, + ) => { + _setSelectedCurrency(currencyAbbreviation); + isTokenAddress + ? setCoinValue(currencyAbbreviation) + : setChainValue(currencyAbbreviation); setCurrencyModalVisible(false); }; @@ -380,11 +440,27 @@ const ContactsAdd = ({ }; // Flat list + const renderTokenItem = useCallback( + ({item}) => ( + + ), + [], + ); + const renderItem = useCallback( ({item}) => ( + currencySelected(currencyAbbreviaton, isTokenAddress) + } key={item.id} hideCheckbox={true} /> @@ -392,7 +468,7 @@ const ContactsAdd = ({ [], ); - const renderItemNetowrk = useCallback( + const renderNetworkItem = useCallback( ({item}) => ( ), @@ -417,13 +493,23 @@ const ContactsAdd = ({ }; useEffect(() => { - if (contact?.address) { + if (contact?.address && contact?.coin) { + const chain = contact.chain + ? contact.chain + : IsERCToken(contact.coin) + ? 'eth' + : contact.coin; setValue('address', contact.address, {shouldDirty: true}); setValue('name', contact.name || ''); setValue('email', contact.email); + setValue('chain', chain); setValue('destinationTag', contact.tag || contact.destinationTag); - if (contact.coin) { - currencySelected(contact.coin); + if (contact.coin && contact.chain) { + setIsTokenAddress(true); + tokenSelected(contact.coin); + currencySelected(contact.chain, true); + } else { + currencySelected(contact.coin, false); } processAddress(contact.address); } @@ -509,6 +595,27 @@ const ContactsAdd = ({ )} + {!contact && ethValidAddress ? ( + { + setIsTokenAddress(!isTokenAddress); + }}> + + + {t('Is this a token address?')} + + + + { + setIsTokenAddress(!isTokenAddress); + }} + /> + + + ) : null} + - + { @@ -543,11 +650,13 @@ const ContactsAdd = ({ justifyContent: 'space-between', }}> - {selectedCurrency?.img ? ( - + {selectedCurrency ? ( + + + ) : null} - {selectedCurrency?.currencyAbbreviation} + {selectedCurrency?.currencyAbbreviation.toUpperCase()} @@ -556,6 +665,37 @@ const ContactsAdd = ({ ) : null} + {!contact && isTokenAddress ? ( + + + { + setTokenModalVisible(true); + }}> + + + {selectedToken ? ( + + + + ) : null} + {selectedToken?.currencyName} + + + + + + ) : null} + @@ -587,11 +727,26 @@ const ContactsAdd = ({ onBackdropPress={() => setCurrencyModalVisible(false)}> -

{t('Select a Coin')}

+

{isTokenAddress ? t('Select a Chain') : t('Select a Coin')}

+
+ +
+ + setTokenModalVisible(false)}> + + +

{t('Select a Token')}

{ @@ -616,9 +771,9 @@ const ContactsAdd = ({
@@ -633,7 +788,7 @@ const ContactsAdd = ({ contentContainerStyle={{paddingTop: 20, paddingBottom: 20}} data={networkOptions} keyExtractor={keyExtractor} - renderItem={renderItemNetowrk} + renderItem={renderNetworkItem} /> diff --git a/src/navigation/tabs/contacts/screens/ContactsRoot.tsx b/src/navigation/tabs/contacts/screens/ContactsRoot.tsx index 9e9117ec6f..e81ca1f35d 100644 --- a/src/navigation/tabs/contacts/screens/ContactsRoot.tsx +++ b/src/navigation/tabs/contacts/screens/ContactsRoot.tsx @@ -164,8 +164,8 @@ const ContactsRoot: React.FC = () => { setSearchResults(results); }, 300); - const keyExtractor = (item: ContactRowProps) => { - return item.address + item.coin + item.network; + const keyExtractor = (item: ContactRowProps, index: number) => { + return item.address + item.coin + item.network + index; }; const renderItem = useCallback( diff --git a/src/navigation/tabs/home/HomeRoot.tsx b/src/navigation/tabs/home/HomeRoot.tsx index d7fcf216af..80c8a928b2 100644 --- a/src/navigation/tabs/home/HomeRoot.tsx +++ b/src/navigation/tabs/home/HomeRoot.tsx @@ -123,7 +123,7 @@ const HomeRoot = () => { () => priceHistory.reduce((ratesList, history) => { const option = SupportedCurrencyOptions.find( - ({id}) => id === history.coin, + ({currencyAbbreviation}) => currencyAbbreviation === history.coin, ); if (option) { diff --git a/src/navigation/tabs/home/components/exchange-rates/ExchangeRatesList.tsx b/src/navigation/tabs/home/components/exchange-rates/ExchangeRatesList.tsx index 3a264b5c42..1cb19120b8 100644 --- a/src/navigation/tabs/home/components/exchange-rates/ExchangeRatesList.tsx +++ b/src/navigation/tabs/home/components/exchange-rates/ExchangeRatesList.tsx @@ -10,8 +10,8 @@ import ExchangeRateItem from './ExchangeRateItem'; export interface ExchangeRateItemProps { id: string; img: string | ((props: any) => ReactElement); - currencyName?: string; - currencyAbbreviation?: string; + currencyName: string; + currencyAbbreviation: string; average?: number; currentPrice?: number; priceDisplay: Array; diff --git a/src/navigation/tabs/settings/screens/NewtorkFeePolicy.tsx b/src/navigation/tabs/settings/screens/NewtorkFeePolicy.tsx index f36ac1527d..8b793f367a 100644 --- a/src/navigation/tabs/settings/screens/NewtorkFeePolicy.tsx +++ b/src/navigation/tabs/settings/screens/NewtorkFeePolicy.tsx @@ -212,10 +212,10 @@ const NetworkFeePolicy = () => { const [isLoading, setIsLoading] = useState(true); const dispatch = useAppDispatch(); - const initFeeLevel = async (currencyAbbreviation: string) => { + const initFeeLevel = async (currencyAbbreviation: string, chain: string) => { let feeOptions: any[] = []; const {feeUnit, feeUnitAmount, blockTime} = dispatch( - GetFeeUnits(currencyAbbreviation), + GetFeeUnits(currencyAbbreviation, chain), ); try { const _feeLevels = await getFeeLevelsUsingBwcClient( @@ -232,7 +232,7 @@ const NetworkFeePolicy = () => { ...fee, feeUnit, // @ts-ignore - uiLevel: dispatch(GetFeeOptions(currencyAbbreviation))[level], + uiLevel: GetFeeOptions(currencyAbbreviation)[level], }; feeOption.feePerSatByte = (feePerKb / feeUnitAmount).toFixed(); feeOption.uiFeePerSatByte = `${feeOption.feePerSatByte} ${ @@ -241,7 +241,7 @@ const NetworkFeePolicy = () => { if ( currencyAbbreviation === 'eth' || - dispatch(IsERCToken(currencyAbbreviation)) + IsERCToken(currencyAbbreviation) ) { // @ts-ignore feeOption.avgConfirmationTime = ethAvgTime[level]; diff --git a/src/navigation/wallet-connect/components/WalletSelector.tsx b/src/navigation/wallet-connect/components/WalletSelector.tsx index 73b8fc1b82..e0b5d5522c 100644 --- a/src/navigation/wallet-connect/components/WalletSelector.tsx +++ b/src/navigation/wallet-connect/components/WalletSelector.tsx @@ -128,7 +128,9 @@ export default ({ balance, hideWallet, currencyAbbreviation, - credentials: {network, walletName: fallbackName}, + network, + chain, + credentials: {walletName: fallbackName}, walletName, } = wallet; return merge(cloneDeep(wallet), { @@ -140,6 +142,7 @@ export default ({ balance.sat, defaultAltCurrency.isoCode, currencyAbbreviation, + chain, rates, ), ), diff --git a/src/navigation/wallet-connect/screens/WalletConnectConfirm.tsx b/src/navigation/wallet-connect/screens/WalletConnectConfirm.tsx index d31d36b904..190f5f471f 100644 --- a/src/navigation/wallet-connect/screens/WalletConnectConfirm.tsx +++ b/src/navigation/wallet-connect/screens/WalletConnectConfirm.tsx @@ -116,7 +116,7 @@ const WalletConnectConfirm = () => { const [gasLimit, setGasLimit] = useState(_gasLimit); const [nonce, setNonce] = useState(_nonce); - const feeOptions = dispatch(GetFeeOptions(wallet.currencyAbbreviation)); + const feeOptions = GetFeeOptions(wallet.currencyAbbreviation); const approveCallRequest = async () => { try { diff --git a/src/navigation/wallet-connect/screens/WalletConnectHome.tsx b/src/navigation/wallet-connect/screens/WalletConnectHome.tsx index 768d03b3b1..4308c8ad3c 100644 --- a/src/navigation/wallet-connect/screens/WalletConnectHome.tsx +++ b/src/navigation/wallet-connect/screens/WalletConnectHome.tsx @@ -94,6 +94,8 @@ const WalletConnectHome = () => { params: {peerId, wallet}, } = useRoute>(); + const {chain, currencyAbbreviation} = wallet; + const wcConnector: IWCConnector | undefined = useAppSelector( ({WALLET_CONNECT}) => { return WALLET_CONNECT.connectors.find(c => c.connector.peerId === peerId); @@ -128,7 +130,9 @@ const WalletConnectHome = () => { address: toAddress, }; const amountStr = value - ? dispatch(FormatAmount('eth', parseInt(value, 16))) + ? dispatch( + FormatAmount(currencyAbbreviation, chain, parseInt(value, 16)), + ) : 0; const tx = { wallet, @@ -281,7 +285,11 @@ const WalletConnectHome = () => { requests.map((request, id) => { const {value = '0x0'} = request.payload.params[0]; const amountStr = dispatch( - FormatAmountStr('eth', parseInt(value, 16)), + FormatAmountStr( + currencyAbbreviation, + chain, + parseInt(value, 16), + ), ); return ( diff --git a/src/navigation/wallet-connect/screens/WalletConnectStart.tsx b/src/navigation/wallet-connect/screens/WalletConnectStart.tsx index 0bb473be12..9509489e27 100644 --- a/src/navigation/wallet-connect/screens/WalletConnectStart.tsx +++ b/src/navigation/wallet-connect/screens/WalletConnectStart.tsx @@ -30,7 +30,7 @@ import { showBottomNotificationModal, } from '../../../store/app/app.actions'; import {Network} from '../../../constants'; -import {Currencies} from '../../../constants/currencies'; +import {BitpaySupportedCoins} from '../../../constants/currencies'; import {createWalletAddress} from '../../../store/wallet/effects/address/address'; import {IWCCustomData} from '../../../store/wallet-connect/wallet-connect.models'; import {BottomNotificationConfig} from '../../../components/modal/bottom-notification/BottomNotification'; @@ -110,8 +110,8 @@ const WalletConnectStart = () => { throw 'MISSING_WALLET_ADDRESS'; } - const {chain} = Currencies[wallet.currencyAbbreviation]; - const chainId = CHAIN_ID[chain][wallet.credentials.network]; + const {chain} = BitpaySupportedCoins[wallet.currencyAbbreviation]; + const chainId = CHAIN_ID[chain][wallet.network]; const accounts = [address]; const customData: IWCCustomData = { keyId: wallet.keyId, diff --git a/src/navigation/wallet/components/AddressCard.tsx b/src/navigation/wallet/components/AddressCard.tsx index b058c2bb9d..a511f51415 100644 --- a/src/navigation/wallet/components/AddressCard.tsx +++ b/src/navigation/wallet/components/AddressCard.tsx @@ -52,6 +52,7 @@ const AddressCard: React.FC = ({recipient}) => { ) : ( diff --git a/src/navigation/wallet/components/MultipleOutputsTx.tsx b/src/navigation/wallet/components/MultipleOutputsTx.tsx index dd5d566d09..c3607a08b0 100644 --- a/src/navigation/wallet/components/MultipleOutputsTx.tsx +++ b/src/navigation/wallet/components/MultipleOutputsTx.tsx @@ -28,6 +28,9 @@ import CopiedSvg from '../../../../assets/img/copied-success.svg'; import {useTranslation} from 'react-i18next'; import AddContactIcon from '../../../components/icons/add-contacts/AddContacts'; import {useNavigation} from '@react-navigation/native'; +import {getBadgeImg} from '../../../utils/helper-methods'; +import {CurrencyImage} from '../../../components/currency-image/CurrencyImage'; +import {BitpaySupportedEthereumTokenOpts} from '../../../constants/tokens'; const MisunderstoodOutputsText = styled(H7)` margin-bottom: 5px; @@ -60,8 +63,18 @@ const ContactsIconContainer = styled.TouchableOpacity` const MultipleOutputsTx = ({tx}: {tx: any}) => { const {t} = useTranslation(); - let {coin, network} = tx; + let {coin, network, chain} = tx; const contactList = useAppSelector(({CONTACT}) => CONTACT.list); + const {tokenOptions, customTokenOptions} = useAppSelector( + ({WALLET}) => WALLET.customTokenOptions, + ); + const tokenOpts = { + eth: { + ...BitpaySupportedEthereumTokenOpts, + ...tokenOptions, + ...customTokenOptions, + }, + }; const dispatch = useAppDispatch(); const navigation = useNavigation(); @@ -96,7 +109,7 @@ const MultipleOutputsTx = ({tx}: {tx: any}) => { } const addressToShow = dispatch( - GetProtocolPrefixAddress(coin, network, outputAddr), + GetProtocolPrefixAddress(coin, network, outputAddr, chain), ); output.addressToShow = @@ -118,10 +131,23 @@ const MultipleOutputsTx = ({tx}: {tx: any}) => { const [showMultiOptions, setShowMultiOptions] = useState(false); const getIcon = () => { + const img = SUPPORTED_CURRENCIES.includes(tx.coin) + ? CurrencyListIcons[tx.coin] + : tokenOpts && + // @ts-ignore + tokenOpts[tx.chain] && + // @ts-ignore + tokenOpts[tx.chain][tx.coin]?.logoURI + ? // @ts-ignore + (tokenOpts[tx.chain][tx.coin].logoURI as string) + : ''; + const badgeImg = getBadgeImg(tx.coin, chain); + const icon = ; + return tx.customData?.service === 'debitcard' ? ( - ) : SUPPORTED_CURRENCIES.includes(coin) ? ( - CurrencyListIcons[coin]({width: 18, height: 18}) + ) : icon ? ( + icon ) : ( ); diff --git a/src/navigation/wallet/components/ReceiveAddress.tsx b/src/navigation/wallet/components/ReceiveAddress.tsx index 2b5ab80908..6720718342 100644 --- a/src/navigation/wallet/components/ReceiveAddress.tsx +++ b/src/navigation/wallet/components/ReceiveAddress.tsx @@ -171,7 +171,7 @@ const ReceiveAddress = ({isVisible, closeModal, wallet}: Props) => { }; const createAddress = async (newAddress: boolean = false) => { - let {coin, network} = wallet.credentials; + let {currencyAbbreviation, network, chain} = wallet; const prefix = 'Could not create address'; try { @@ -179,8 +179,10 @@ const ReceiveAddress = ({isVisible, closeModal, wallet}: Props) => { createWalletAddress({wallet, newAddress}), )) as string; setLoading(false); - if (coin === 'bch') { - const protocolPrefix = dispatch(GetProtocolPrefix(coin, network)); + if (currencyAbbreviation === 'bch') { + const protocolPrefix = dispatch( + GetProtocolPrefix(currencyAbbreviation, network, chain), + ); const formattedAddr = protocolPrefix + ':' + walletAddress; setAddress(formattedAddr); setBchAddress(formattedAddr); diff --git a/src/navigation/wallet/components/RecoveryPhrase.tsx b/src/navigation/wallet/components/RecoveryPhrase.tsx index f8e68035e2..04f930b224 100644 --- a/src/navigation/wallet/components/RecoveryPhrase.tsx +++ b/src/navigation/wallet/components/RecoveryPhrase.tsx @@ -197,6 +197,7 @@ const RecoveryPhrase = () => { const [advancedOptions, setAdvancedOptions] = useState({ derivationPath: DefaultDerivationPath.defaultBTC as string, coin: CurrencyOptions[0].currencyAbbreviation, + chain: CurrencyOptions[0].currencyAbbreviation, // chain = currency for all currencies if tokens not included passphrase: undefined as string | undefined, isMultisig: false, }); @@ -307,6 +308,7 @@ const RecoveryPhrase = () => { advancedOpts: { derivationPath: string; coin: string; + chain: string; passphrase: string | undefined; isMultisig: boolean; }, @@ -337,7 +339,7 @@ const RecoveryPhrase = () => { keyOpts.coin = advancedOpts.coin.toLowerCase(); keyOpts.singleAddress = dispatch( - isSingleAddressCoin(advancedOpts.coin.toLowerCase()), + isSingleAddressCoin(advancedOpts.coin, advancedOpts.chain), ); // set opts.useLegacyPurpose @@ -474,13 +476,14 @@ const RecoveryPhrase = () => { advancedOpts: { derivationPath: string; coin: string; + chain: string; passphrase: string | undefined; isMultisig: boolean; }, ): Promise => { try { let keyOpts: Partial = { - name: dispatch(GetName(advancedOpts.coin!)), + name: dispatch(GetName(advancedOpts.coin!, advancedOpts.chain)), }; try { diff --git a/src/navigation/wallet/components/SendToAddress.tsx b/src/navigation/wallet/components/SendToAddress.tsx index 1e473d96de..4c415159d1 100644 --- a/src/navigation/wallet/components/SendToAddress.tsx +++ b/src/navigation/wallet/components/SendToAddress.tsx @@ -33,7 +33,6 @@ import { GetCoinAndNetwork, TranslateToBchCashAddress, } from '../../../store/wallet/effects/address/address'; -import {GetChain} from '../../../store/wallet/utils/currency'; import {APP_NAME_UPPERCASE} from '../../../constants/config'; import { dismissOnGoingProcessModal, @@ -110,11 +109,7 @@ const SendToAddress = () => { const navigation = useNavigation(); const route = useRoute>(); const {wallet, context} = route.params; - const { - currencyAbbreviation, - id, - credentials: {network}, - } = wallet; + const {currencyAbbreviation, id, network, chain} = wallet; const keyWallets: KeyWalletsRowProps[] = BuildKeyWalletRow( keys, @@ -148,8 +143,7 @@ const SendToAddress = () => { dispatch => { const addrData = GetCoinAndNetwork(data, network); const isValid = - dispatch(GetChain(currencyAbbreviation)).toLowerCase() === - addrData?.coin.toLowerCase() && addrData?.network === network; + chain === addrData?.coin.toLowerCase() && addrData?.network === network; if (isValid) { return true; diff --git a/src/navigation/wallet/components/SendToContact.tsx b/src/navigation/wallet/components/SendToContact.tsx index 5dc9bd316c..f45f514757 100644 --- a/src/navigation/wallet/components/SendToContact.tsx +++ b/src/navigation/wallet/components/SendToContact.tsx @@ -56,10 +56,7 @@ const SendToContact = () => { goToConfirmView, goToSelectInputsView, } = useContext(SendToOptionsContext); - const { - currencyAbbreviation, - credentials: {network}, - } = wallet; + const {currencyAbbreviation, network} = wallet; const contacts = useMemo(() => { return allContacts.filter( diff --git a/src/navigation/wallet/components/SendToPill.tsx b/src/navigation/wallet/components/SendToPill.tsx index 611d48fe8c..5c49646996 100644 --- a/src/navigation/wallet/components/SendToPill.tsx +++ b/src/navigation/wallet/components/SendToPill.tsx @@ -4,6 +4,7 @@ import {LightBlack, NeutralSlate} from '../../../styles/colors'; import {H7} from '../../../components/styled/Text'; import ArrowDownSvg from '../../../../assets/img/chevron-down.svg'; import ArrowUpSvg from '../../../../assets/img/chevron-up.svg'; +import {CurrencyImage} from '../../../components/currency-image/CurrencyImage'; interface Props { icon?: ReactElement; @@ -18,10 +19,14 @@ const PillContainer = styled.Pressable` border-radius: 40px; align-items: center; justify-content: center; - padding: 10px 15px; + padding: 0px 15px; max-width: 150px; `; +const IconContainer = styled.View` + padding: 10px 0px; +`; + const PillText = styled(H7)` margin-left: 5px; `; @@ -40,7 +45,7 @@ const SendToPill = ({icon, description, onPress, dropDown}: Props) => { }; return ( - {icon} + {icon} {description} diff --git a/src/navigation/wallet/screens/AddWallet.tsx b/src/navigation/wallet/screens/AddWallet.tsx index 86d7a5b680..49dee23e56 100644 --- a/src/navigation/wallet/screens/AddWallet.tsx +++ b/src/navigation/wallet/screens/AddWallet.tsx @@ -75,7 +75,7 @@ import {WrongPasswordError} from '../components/ErrorMessages'; import {getTokenContractInfo} from '../../../store/wallet/effects/status/status'; import {GetCoinAndNetwork} from '../../../store/wallet/effects/address/address'; import {addCustomTokenOption} from '../../../store/wallet/effects/currencies/currencies'; -import {Currencies} from '../../../constants/currencies'; +import {BitpaySupportedCoins} from '../../../constants/currencies'; import {TouchableOpacity} from 'react-native-gesture-handler'; import InfoSvg from '../../../../assets/img/info.svg'; import {URL} from '../../../constants'; @@ -85,6 +85,7 @@ type AddWalletScreenProps = StackScreenProps; export type AddWalletParamList = { key: Key; + chain?: string; currencyAbbreviation?: string; currencyName?: string; isToken?: boolean; @@ -173,6 +174,7 @@ const AddWallet: React.FC = ({navigation, route}) => { const { currencyAbbreviation: _currencyAbbreviation, currencyName: _currencyName, + chain, key, isToken, isCustomToken, @@ -193,8 +195,8 @@ const AddWallet: React.FC = ({navigation, route}) => { ); const singleAddressCurrency = - Currencies[_currencyAbbreviation?.toLowerCase() as string]?.properties - ?.singleAddress; + BitpaySupportedCoins[_currencyAbbreviation?.toLowerCase() as string] + ?.properties?.singleAddress; const nativeSegwitCurrency = _currencyAbbreviation ? ['btc', 'ltc'].includes(_currencyAbbreviation.toLowerCase()) : false; @@ -335,8 +337,11 @@ const AddWallet: React.FC = ({navigation, route}) => { addWallet({ key, associatedWallet: _associatedWallet, - isToken, - currency, + currency: { + chain: chain!, + currencyAbbreviation: currencyAbbreviation!, + isToken: isToken!, + }, options: { password, network: isTestnet ? Network.testnet : network, @@ -392,8 +397,6 @@ const AddWallet: React.FC = ({navigation, route}) => { setAssociatedWallet(item); if (isCustomToken && !!customTokenAddress) { setCustomTokenAddress(undefined); - setCurrencyAbbreviation(undefined); - setCurrencyName(undefined); } setAssociatedWalletModalVisible(false); }} @@ -410,8 +413,6 @@ const AddWallet: React.FC = ({navigation, route}) => { } setCustomTokenAddress(tokenAddress); - setCurrencyAbbreviation(undefined); - setCurrencyName(undefined); const opts = { tokenAddress, diff --git a/src/navigation/wallet/screens/AmountScreen.tsx b/src/navigation/wallet/screens/AmountScreen.tsx index 62447f4721..634784aa74 100644 --- a/src/navigation/wallet/screens/AmountScreen.tsx +++ b/src/navigation/wallet/screens/AmountScreen.tsx @@ -29,6 +29,7 @@ export interface AmountScreenParamList { sendMaxEnabled?: boolean; cryptoCurrencyAbbreviation?: string; fiatCurrencyAbbreviation?: string; + chain?: string; context?: string; } @@ -43,6 +44,7 @@ const AmountScreen: React.VFC< sendMaxEnabled, cryptoCurrencyAbbreviation, fiatCurrencyAbbreviation, + chain, context, } = route.params || {}; @@ -76,6 +78,7 @@ const AmountScreen: React.VFC< context={context} cryptoCurrencyAbbreviation={cryptoCurrencyAbbreviation} fiatCurrencyAbbreviation={fiatCurrencyAbbreviation} + chain={chain} onSubmit={amt => { onAmountSelected?.(amt.toString(), setButtonState); }} diff --git a/src/navigation/wallet/screens/CreateMultisig.tsx b/src/navigation/wallet/screens/CreateMultisig.tsx index effb6a79b0..1365a27e59 100644 --- a/src/navigation/wallet/screens/CreateMultisig.tsx +++ b/src/navigation/wallet/screens/CreateMultisig.tsx @@ -43,7 +43,7 @@ import { import Haptic from '../../../components/haptic-feedback/haptic'; import ChevronDownSvg from '../../../../assets/img/chevron-down.svg'; import ChevronUpSvg from '../../../../assets/img/chevron-up.svg'; -import {Currencies} from '../../../constants/currencies'; +import {BitpaySupportedCoins} from '../../../constants/currencies'; import Checkbox from '../../../components/checkbox/Checkbox'; import {WalletStackParamList} from '../WalletStack'; import { @@ -203,7 +203,8 @@ const CreateMultisig = () => { } = useForm({resolver: yupResolver(schema)}); const singleAddressCurrency = - Currencies[currency?.toLowerCase() as string].properties.singleAddress; + BitpaySupportedCoins[currency?.toLowerCase() as string].properties + .singleAddress; const showErrorModal = (e: string) => { dispatch( diff --git a/src/navigation/wallet/screens/CurrencySelection.tsx b/src/navigation/wallet/screens/CurrencySelection.tsx index 11cea74311..b2b3f92882 100644 --- a/src/navigation/wallet/screens/CurrencySelection.tsx +++ b/src/navigation/wallet/screens/CurrencySelection.tsx @@ -19,9 +19,8 @@ import CurrencySelectionRow, { import Button from '../../../components/button/Button'; import { - Currencies, - SUPPORTED_TOKENS, - SupportedCurrencies, + BitpaySupportedCoins, + BitpaySupportedEthereumTokens, } from '../../../constants/currencies'; import {startCreateKey} from '../../../store/wallet/effects'; import { @@ -39,8 +38,10 @@ import {useNavigation} from '@react-navigation/native'; import {HeaderTitle} from '../../../components/styled/Text'; import haptic from '../../../components/haptic-feedback/haptic'; import { + SupportedCoinsOptions, SupportedCurrencyOption, SupportedCurrencyOptions, + SupportedTokenOptions, } from '../../../constants/SupportedCurrencyOptions'; import {WalletScreens, WalletStackParamList} from '../WalletStack'; import { @@ -53,7 +54,7 @@ import {StackScreenProps} from '@react-navigation/stack'; import {sleep} from '../../../utils/helper-methods'; import {useLogger} from '../../../utils/hooks/useLogger'; import {useAppSelector, useAppDispatch} from '../../../utils/hooks'; -import {BitpaySupportedTokenOpts} from '../../../constants/tokens'; +import {BitpaySupportedEthereumTokenOpts} from '../../../constants/tokens'; import {useTranslation} from 'react-i18next'; import CurrencySelectionSearchInput from '../components/CurrencySelectionSearchInput'; import CurrencySelectionNoResults from '../components/CurrencySelectionNoResults'; @@ -112,12 +113,6 @@ export const SearchContainer = styled.View` margin: 20px ${ScreenGutter} 20px; `; -const SupportedChainCurrencyOptions = SupportedCurrencyOptions.filter( - currency => { - return !currency.isToken; - }, -); - const SupportedMultisigCurrencyOptions: SupportedCurrencyOption[] = SupportedCurrencyOptions.filter(currency => { return currency.hasMultisig; @@ -130,7 +125,6 @@ const DESCRIPTIONS: Record = { const POPULAR_TOKENS: Record = { eth: ['usdc', 'busd', 'ape'], - matic: ['usdc', 'busd', 'gusd'], }; const keyExtractor = (item: CurrencySelectionListItem) => item.currency.id; @@ -207,6 +201,7 @@ const CurrencySelection: React.VFC = ({ imgSrc: undefined, selected: false, disabled: false, + chain: currency.currencyAbbreviation, }, tokens: [], popularTokens: [], @@ -222,8 +217,9 @@ const CurrencySelection: React.VFC = ({ const chainMap: Record = {}; // Add all chain currencies to list - const list: CurrencySelectionListItem[] = SupportedChainCurrencyOptions.map( + const list: CurrencySelectionListItem[] = SupportedCoinsOptions.map( ({id, currencyAbbreviation, currencyName, img}) => { + const chain = currencyAbbreviation.toLowerCase(); const item: CurrencySelectionListItem = { currency: { id, @@ -232,36 +228,47 @@ const CurrencySelection: React.VFC = ({ img, selected: false, disabled: false, + chain: chain, }, tokens: [], popularTokens: [], - description: DESCRIPTIONS[id] ? t(DESCRIPTIONS[id]) : '', + description: DESCRIPTIONS[chain] ? t(DESCRIPTIONS[chain]) : '', }; - chainMap[id.toLowerCase()] = item; + chainMap[chain] = item; return item; }, ); // For each token, add it to the token list for its parent chain object - const allTokenOptions: Record = { - ...BitpaySupportedTokenOpts, + const ethereumTokenOptions: Record = { + ...BitpaySupportedEthereumTokenOpts, ...appTokenOptions, ...appCustomTokenOptions, }; - Object.entries(allTokenOptions).forEach(([k, tokenOpt]) => { + Object.entries(ethereumTokenOptions).forEach(([k, tokenOpt]) => { if ( - !(Currencies[k] || appTokenData[k] || appCustomTokenData[k]) || + !( + BitpaySupportedCoins[k] || + BitpaySupportedEthereumTokens[k] || + appTokenData[k] || + appCustomTokenData[k] + ) || k === 'pax' ) { return; } const tokenData = - Currencies[k] || appTokenData[k] || appCustomTokenData[k]; + BitpaySupportedCoins[k] || + BitpaySupportedEthereumTokens[k] || + appTokenData[k] || + appCustomTokenData[k]; const chainData = chainMap[tokenData.chain.toLowerCase()]; - const imgSrc = SupportedCurrencyOptions.find(c => c.id === k)?.imgSrc; + const imgSrc = SupportedTokenOptions.find( + c => c.currencyAbbreviation === k, + )?.imgSrc; const isReqSrc = ( src: ImageSourcePropType | undefined, ): src is ImageRequireSource => typeof src === 'number'; @@ -275,6 +282,7 @@ const CurrencySelection: React.VFC = ({ selected: false, disabled: false, isToken: true, + chain: tokenData.chain.toLowerCase(), }; if (chainData) { @@ -284,7 +292,11 @@ const CurrencySelection: React.VFC = ({ chainData.tokens.push(token); - if (POPULAR_TOKENS[tokenData.chain.toLowerCase()].includes(token.id)) { + if ( + POPULAR_TOKENS[tokenData.chain.toLowerCase()].includes( + token.currencyAbbreviation.toLowerCase(), + ) + ) { chainData.popularTokens.push(token); } } else { @@ -325,46 +337,26 @@ const CurrencySelection: React.VFC = ({ ); }; - const checkEthIfTokenSelected = ( - currencies: Array, - ): Array => { - const isEthSelected = currencies.some(c => c.toLowerCase() === 'eth'); - - if (isEthSelected) { - return currencies; - } - - const ethState = allListItems.find( - item => item.currency.id.toLowerCase() === 'eth', - ); - - const isEthTokenSelected = currencies.some(c => { - const selectedLower = c.toLowerCase(); - - return ( - SUPPORTED_TOKENS.includes(selectedLower) || - ethState?.tokens.some(token => { - return token.id.toLowerCase() === selectedLower && token.selected; - }) - ); - }); - - if (isEthTokenSelected) { - currencies.push('ETH'); - } - - return currencies; - }; - const selectedCurrencies = useMemo(() => { - return allListItems.reduce((accum, item) => { + return allListItems.reduce< + Array<{chain: string; currencyAbbreviation: string; isToken: boolean}> + >((accum, item) => { if (item.currency.selected) { - accum.push(item.currency.currencyAbbreviation); + accum.push({ + chain: item.currency.currencyAbbreviation.toLowerCase(), + currencyAbbreviation: + item.currency.currencyAbbreviation.toLowerCase(), + isToken: false, + }); } item.tokens.forEach(token => { if (token.selected) { - accum.push(token.currencyAbbreviation); + accum.push({ + chain: item.currency.currencyAbbreviation.toLowerCase(), + currencyAbbreviation: token.currencyAbbreviation.toLowerCase(), + isToken: true, + }); } }); return accum; @@ -380,18 +372,14 @@ const CurrencySelection: React.VFC = ({ ctaTitle: t('Create Key'), onCtaPress: async () => { try { - const currencies = checkEthIfTokenSelected( - selectedCurrencies, - )?.map(selected => - selected.toLowerCase(), - ) as Array; - await dispatch( startOnGoingProcessModal( t(OnGoingProcessMessages.CREATING_KEY), ), ); - const createdKey = await dispatch(startCreateKey(currencies)); + const createdKey = await dispatch( + startCreateKey(selectedCurrencies), + ); dispatch(setHomeCarouselConfig({id: createdKey.id, show: true})); @@ -405,7 +393,7 @@ const CurrencySelection: React.VFC = ({ dispatch( logSegmentEvent('track', 'Created Key', { context, - coins: currencies, + coins: selectedCurrencies, }), ); dispatch(dismissOnGoingProcessModal()); @@ -436,12 +424,13 @@ const CurrencySelection: React.VFC = ({ return; } - const selectedId = selectedCurrencies[0]; + const selectedId = selectedCurrencies[0].currencyAbbreviation; const item = allListItems.find( i => - i.currency.currencyAbbreviation === selectedId || + i.currency.currencyAbbreviation.toLowerCase() === selectedId || i.tokens.some( - token => token.currencyAbbreviation === selectedId, + token => + token.currencyAbbreviation.toLowerCase() === selectedId, ), ); let currency: CurrencySelectionItem | undefined; @@ -451,11 +440,14 @@ const CurrencySelection: React.VFC = ({ return; } - if (item.currency.currencyAbbreviation === selectedId) { + if ( + item.currency.currencyAbbreviation.toLowerCase() === selectedId + ) { currency = item.currency; } else { currency = item.tokens.find( - token => token.currencyAbbreviation === selectedId, + token => + token.currencyAbbreviation.toLowerCase() === selectedId, ); } @@ -468,9 +460,11 @@ const CurrencySelection: React.VFC = ({ screen: 'AddWallet', params: { key, - currencyAbbreviation: currency.currencyAbbreviation, + currencyAbbreviation: + currency.currencyAbbreviation.toLowerCase(), currencyName: currency.currencyName, isToken: !!currency.isToken, + chain: currency.chain, }, }); }, @@ -489,7 +483,10 @@ const CurrencySelection: React.VFC = ({ navigation.navigate('Wallet', { screen: 'CreateMultisig', - params: {currency: selectedCurrencies[0], key}, + params: { + currency: selectedCurrencies[0].currencyAbbreviation, + key, + }, }); }, }; @@ -528,11 +525,14 @@ const CurrencySelection: React.VFC = ({ }); }, [navigation, t, context, headerTitle]); - const onToggle = (id: string) => { + const onToggle = (currencyAbbreviation: string) => { setAllListItems(previous => previous.map(item => { - const isCurrencyMatch = item.currency.id === id; - const tokenMatch = item.tokens.find(token => token.id === id); + const isCurrencyMatch = + item.currency.currencyAbbreviation === currencyAbbreviation; + const tokenMatch = item.tokens.find( + token => token.currencyAbbreviation === currencyAbbreviation, + ); // if multi, just toggle the selected item and rerender if (selectionMode === 'multi') { @@ -565,18 +565,22 @@ const CurrencySelection: React.VFC = ({ // update token state item.tokens = item.tokens.map(token => { - return token.id === id ? updatedToken : token; + return token.currencyAbbreviation === currencyAbbreviation + ? updatedToken + : token; }); // update popular token state // append tokens once selected so user can see their entire selection let appendToPopular = true; item.popularTokens = item.popularTokens.map(token => { - if (token.id === id) { + if (token.currencyAbbreviation === currencyAbbreviation) { appendToPopular = false; } - return token.id === id ? updatedToken : token; + return token.currencyAbbreviation === currencyAbbreviation + ? updatedToken + : token; }); if (appendToPopular) { @@ -634,7 +638,7 @@ const CurrencySelection: React.VFC = ({ // update token state item.tokens = item.tokens.map(token => { - if (token.id === id) { + if (token.currencyAbbreviation === currencyAbbreviation) { return updatedToken; } @@ -645,7 +649,7 @@ const CurrencySelection: React.VFC = ({ // append tokens once selected so user can see their entire selection let appendToPopular = true; item.popularTokens = item.popularTokens.map(token => { - if (token.id === id) { + if (token.currencyAbbreviation === currencyAbbreviation) { appendToPopular = false; return updatedToken; } @@ -667,14 +671,14 @@ const CurrencySelection: React.VFC = ({ const onToggleRef = useRef(onToggle); onToggleRef.current = onToggle; - const memoizedOnToggle = useCallback((id: string) => { - onToggleRef.current(id); + const memoizedOnToggle = useCallback((currencyAbbreviation: string) => { + onToggleRef.current(currencyAbbreviation); }, []); const memoizedOnViewAllPressed = useMemo(() => { return (currency: CurrencySelectionItem) => { const item = allListItemsRef.current.find( - i => i.currency.id === currency.id, + i => i.currency.currencyAbbreviation === currency.currencyAbbreviation, ); if (!item) { diff --git a/src/navigation/wallet/screens/CurrencyTokenSelection.tsx b/src/navigation/wallet/screens/CurrencyTokenSelection.tsx index 7fedc0b274..c2d87867bf 100644 --- a/src/navigation/wallet/screens/CurrencyTokenSelection.tsx +++ b/src/navigation/wallet/screens/CurrencyTokenSelection.tsx @@ -102,6 +102,7 @@ const CurrencyTokenSelectionScreen: React.VFC< key: params.key, isCustomToken: true, isToken: true, + chain: chain.chain, }); } }; @@ -151,7 +152,7 @@ const CurrencyTokenSelectionScreen: React.VFC< [], ); - const onTokenToggle = (id: string) => { + const onTokenToggle = (currencyAbbreviation: string) => { haptic(IS_ANDROID ? 'keyboardPress' : 'impactLight'); if (params.selectionMode === 'multi') { @@ -164,7 +165,7 @@ const CurrencyTokenSelectionScreen: React.VFC< setTokens(prev => prev.map(token => - token.id === id + token.currencyAbbreviation === currencyAbbreviation ? { ...token, selected: !token.selected, @@ -184,7 +185,7 @@ const CurrencyTokenSelectionScreen: React.VFC< setTokens(prev => prev.map(token => { - if (token.id === id) { + if (token.currencyAbbreviation === currencyAbbreviation) { return { ...token, selected: !token.selected, @@ -201,7 +202,7 @@ const CurrencyTokenSelectionScreen: React.VFC< ); } - params.onToggle(id); + params.onToggle(currencyAbbreviation); }; const onTokenToggleRef = useRef(onTokenToggle); @@ -245,7 +246,6 @@ const CurrencyTokenSelectionScreen: React.VFC< return ( ReactElement); + badgeImg?: string | ((props?: any) => ReactElement); total: number; availableWalletsByKey: { [key in string]: Wallet[]; @@ -147,16 +151,18 @@ export interface GlobalSelectObj { const buildList = (category: string[], wallets: Wallet[]) => { const coins: GlobalSelectObj[] = []; + category.forEach(coin => { const availableWallets = wallets.filter( wallet => wallet.currencyAbbreviation === coin, ); if (availableWallets.length) { - const {currencyName, img} = availableWallets[0]; + const {currencyName, img, badgeImg} = availableWallets[0]; coins.push({ id: Math.random().toString(), currencyName, img, + badgeImg, total: availableWallets.length, availableWalletsByKey: _.groupBy( availableWallets, @@ -203,9 +209,9 @@ const GlobalSelect: React.FC = ({ const dispatch = useAppDispatch(); const {keys} = useAppSelector(({WALLET}) => WALLET); const {rates} = useAppSelector(({RATE}) => RATE); - const tokens = useAppSelector(({WALLET}: RootState) => { + const ethereumTokens = useAppSelector(({WALLET}: RootState) => { return { - ...BitpaySupportedTokenOpts, + ...BitpaySupportedEthereumTokenOpts, ...WALLET.tokenOptions, ...WALLET.customTokenOptions, }; @@ -221,9 +227,9 @@ const GlobalSelect: React.FC = ({ const [keyWallets, setKeysWallets] = useState[]>(); - const NON_BITPAY_SUPPORTED_TOKENS = Object.keys(tokens).filter( - token => !SUPPORTED_CURRENCIES.includes(token), - ); + const NON_BITPAY_SUPPORTED_ETHEREUM_TOKENS = Object.keys( + ethereumTokens, + ).filter(token => !SUPPORTED_ETHEREUM_TOKENS.includes(token)); // all wallets let wallets = Object.values(keys) @@ -244,39 +250,43 @@ const GlobalSelect: React.FC = ({ wallet => wallet.currencyAbbreviation === recipient?.currency || (recipient?.opts?.showERC20Tokens && - dispatch(IsERCToken(wallet.currencyAbbreviation))), + IsERCToken(wallet.currencyAbbreviation)), ); } if (recipient?.network) { - wallets = wallets.filter( - wallet => wallet.credentials.network === recipient?.network, - ); + wallets = wallets.filter(wallet => wallet.network === recipient?.network); } } if (livenetOnly) { - wallets = wallets.filter( - wallet => wallet.credentials.network === 'livenet', - ); + wallets = wallets.filter(wallet => wallet.network === 'livenet'); } const supportedCoins = useMemo( () => buildList( - customSupportedCurrencies - ? customSupportedCurrencies - : SUPPORTED_CURRENCIES, + customSupportedCurrencies ? customSupportedCurrencies : SUPPORTED_COINS, wallets, ), [wallets, customSupportedCurrencies], ); - const otherCoins = useMemo( + + const ethereumCoins = useMemo( + () => + buildList( + customSupportedCurrencies ? [] : SUPPORTED_ETHEREUM_TOKENS, + wallets, + ), + [wallets, customSupportedCurrencies, SUPPORTED_ETHEREUM_TOKENS], + ); + + const otherEthereumCoins = useMemo( () => buildList( - customSupportedCurrencies ? [] : NON_BITPAY_SUPPORTED_TOKENS, + customSupportedCurrencies ? [] : NON_BITPAY_SUPPORTED_ETHEREUM_TOKENS, wallets, ), - [wallets, customSupportedCurrencies, NON_BITPAY_SUPPORTED_TOKENS], + [wallets, customSupportedCurrencies, NON_BITPAY_SUPPORTED_ETHEREUM_TOKENS], ); const openKeyWalletSelector = useCallback( @@ -294,7 +304,9 @@ const GlobalSelect: React.FC = ({ balance, hideWallet, currencyAbbreviation, - credentials: {network, walletName: fallbackName}, + network, + chain, + credentials: {walletName: fallbackName}, walletName, } = wallet; return merge(cloneDeep(wallet), { @@ -307,6 +319,7 @@ const GlobalSelect: React.FC = ({ balance.sat, defaultAltCurrency.isoCode, currencyAbbreviation, + chain, rates, ), ), @@ -322,6 +335,7 @@ const GlobalSelect: React.FC = ({ balance.satLocked, defaultAltCurrency.isoCode, currencyAbbreviation, + chain, rates, ), ), @@ -372,6 +386,7 @@ const GlobalSelect: React.FC = ({ params: { cryptoCurrencyAbbreviation: wallet.currencyAbbreviation.toUpperCase(), + chain: wallet.chain, onAmountSelected: async (amount, setButtonState, opts) => { dispatch( _createProposalAndBuildTxDetails({ @@ -579,15 +594,17 @@ const GlobalSelect: React.FC = ({ )} - {[...supportedCoins, ...otherCoins].length > 0 && ( + {[...supportedCoins, ...ethereumCoins, ...otherEthereumCoins].length > + 0 && ( )} - {[...supportedCoins, ...otherCoins].length === 0 && + {[...supportedCoins, ...ethereumCoins, ...otherEthereumCoins].length === + 0 && context === 'send' && ( {t( diff --git a/src/navigation/wallet/screens/KeyOverview.tsx b/src/navigation/wallet/screens/KeyOverview.tsx index b5360fca80..db8ce57573 100644 --- a/src/navigation/wallet/screens/KeyOverview.tsx +++ b/src/navigation/wallet/screens/KeyOverview.tsx @@ -166,8 +166,11 @@ export const buildUIFormattedWallet: ( { id, img, + badgeImg, currencyName, currencyAbbreviation, + chain, + network, walletName, balance, credentials, @@ -185,8 +188,10 @@ export const buildUIFormattedWallet: ( id, keyId, img, + badgeImg, currencyName, currencyAbbreviation: currencyAbbreviation.toUpperCase(), + chain, walletName: walletName || credentials.walletName, cryptoBalance: balance.crypto, cryptoLockedBalance: balance.cryptoLocked, @@ -200,11 +205,12 @@ export const buildUIFormattedWallet: ( balance.sat, defaultAltCurrencyIsoCode, currencyAbbreviation, + chain, rates, ), ), hideWallet, - credentials.network, + network, ), defaultAltCurrencyIsoCode, { @@ -218,11 +224,12 @@ export const buildUIFormattedWallet: ( balance.satLocked, defaultAltCurrencyIsoCode, currencyAbbreviation, + chain, rates, ), ), hideWallet, - credentials.network, + network, ), defaultAltCurrencyIsoCode, { @@ -236,11 +243,12 @@ export const buildUIFormattedWallet: ( balance.satConfirmedLocked, defaultAltCurrencyIsoCode, currencyAbbreviation, + chain, rates, ), ), hideWallet, - credentials.network, + network, ), defaultAltCurrencyIsoCode, { @@ -254,11 +262,12 @@ export const buildUIFormattedWallet: ( balance.satSpendable, defaultAltCurrencyIsoCode, currencyAbbreviation, + chain, rates, ), ), hideWallet, - credentials.network, + network, ), defaultAltCurrencyIsoCode, { @@ -272,18 +281,19 @@ export const buildUIFormattedWallet: ( balance.satPending, defaultAltCurrencyIsoCode, currencyAbbreviation, + chain, rates, ), ), hideWallet, - credentials.network, + network, ), defaultAltCurrencyIsoCode, { currencyDisplay, }, ), - network: credentials.network, + network: network, isRefreshing, hideWallet, hideBalance, diff --git a/src/navigation/wallet/screens/KeySettings.tsx b/src/navigation/wallet/screens/KeySettings.tsx index a78c3a74fc..80455dd8a0 100644 --- a/src/navigation/wallet/screens/KeySettings.tsx +++ b/src/navigation/wallet/screens/KeySettings.tsx @@ -50,6 +50,7 @@ import { import { buildWalletObj, generateKeyExportCode, + mapAbbreviationAndName, } from '../../../store/wallet/utils/wallet'; import {Key} from '../../../store/wallet/wallet.models'; import { @@ -64,7 +65,7 @@ import { } from '../../../store/wallet/wallet.actions'; import {BWCErrorMessage} from '../../../constants/BWCError'; import {RootState} from '../../../store'; -import {BitpaySupportedTokenOpts} from '../../../constants/tokens'; +import {BitpaySupportedEthereumTokenOpts} from '../../../constants/tokens'; import ToggleSwitch from '../../../components/toggle-switch/ToggleSwitch'; import {useTranslation} from 'react-i18next'; @@ -180,9 +181,11 @@ const KeySettings = () => { const _tokenOptions = useAppSelector(({WALLET}: RootState) => { return { - ...BitpaySupportedTokenOpts, - ...WALLET.tokenOptions, - ...WALLET.customTokenOptions, + eth: { + ...BitpaySupportedEthereumTokenOpts, + ...WALLET.tokenOptions, + ...WALLET.customTokenOptions, + }, }; }); @@ -216,9 +219,18 @@ const KeySettings = () => { .map(syncWallet => { // update to keyId syncWallet.credentials.keyId = key.properties!.id; + const {currencyAbbreviation, currencyName} = dispatch( + mapAbbreviationAndName( + syncWallet.credentials.coin, + syncWallet.credentials.chain, + ), + ); return merge( syncWallet, - dispatch(buildWalletObj(syncWallet.credentials, _tokenOptions)), + buildWalletObj( + {...syncWallet.credentials, currencyAbbreviation, currencyName}, + _tokenOptions, + ), ); }); diff --git a/src/navigation/wallet/screens/PriceCharts.tsx b/src/navigation/wallet/screens/PriceCharts.tsx index 9d7d409356..d14e0db306 100644 --- a/src/navigation/wallet/screens/PriceCharts.tsx +++ b/src/navigation/wallet/screens/PriceCharts.tsx @@ -25,7 +25,7 @@ import { } from '../../../utils/helper-methods'; import RangeDateSelector from '../components/RangeDateSelector'; import {WalletScreens, WalletStackParamList} from '../WalletStack'; -import {Currencies} from '../../../constants/currencies'; +import {BitpaySupportedCoins} from '../../../constants/currencies'; import {ExchangeRateItemProps} from '../../tabs/home/components/exchange-rates/ExchangeRatesList'; import {fetchHistoricalRates} from '../../../store/wallet/effects'; import {useAppDispatch, useAppSelector} from '../../../utils/hooks'; @@ -178,17 +178,11 @@ const PriceCharts = () => { } = useRoute>(); const defaultAltCurrency = useAppSelector(({APP}) => APP.defaultAltCurrency); - const { - currencyName, - currentPrice, - priceDisplay, - id, - currencyAbbreviation, - img, - } = item; + const {currencyName, currentPrice, priceDisplay, currencyAbbreviation, img} = + item; const {coinColor, gradientBackgroundColor} = - Currencies[id.toLowerCase()].theme; + BitpaySupportedCoins[currencyAbbreviation.toLowerCase()].theme; const chartStyle = { data: { diff --git a/src/navigation/wallet/screens/SelectInputs.tsx b/src/navigation/wallet/screens/SelectInputs.tsx index aeb9bbd1b1..d15ce72f42 100644 --- a/src/navigation/wallet/screens/SelectInputs.tsx +++ b/src/navigation/wallet/screens/SelectInputs.tsx @@ -98,8 +98,8 @@ const SelectInputs = () => { ); const [inputs, setInputs] = useState([]); const {wallet, recipient} = route.params; - const {currencyAbbreviation} = wallet; - const precision = dispatch(GetPrecision(wallet?.credentials.coin)); + const {currencyAbbreviation, chain} = wallet; + const precision = dispatch(GetPrecision(currencyAbbreviation, chain)); const [totalAmount, setTotalAmount] = useState( Number(0).toFixed(precision?.unitDecimals), ); @@ -117,7 +117,7 @@ const SelectInputs = () => { recipientData = { recipientName: recipient.name, recipientAddress: recipient.address, - img: wallet?.img || wallet?.credentials.coin, + img: wallet?.img || currencyAbbreviation, }; } @@ -189,7 +189,7 @@ const SelectInputs = () => { const estimatedFee = await GetMinFee(wallet, 1, selectedInputs.length); logger.debug(`Estimated fee: ${estimatedFee}`); const formattedestimatedFee = dispatch( - SatToUnit(estimatedFee, currencyAbbreviation), + SatToUnit(estimatedFee, currencyAbbreviation, chain), ); const amount = Number(totalAmount) - formattedestimatedFee!; diff --git a/src/navigation/wallet/screens/SendToOptions.tsx b/src/navigation/wallet/screens/SendToOptions.tsx index 45b3b6a4ac..2f0c05fe5e 100644 --- a/src/navigation/wallet/screens/SendToOptions.tsx +++ b/src/navigation/wallet/screens/SendToOptions.tsx @@ -85,7 +85,7 @@ export const RecipientList: React.FC = ({ recipientData = { recipientName: recipient.name, recipientAddress: recipient.address, - img: wallet?.img || wallet?.credentials.coin, + img: wallet?.img || wallet?.currencyAbbreviation, }; } @@ -297,6 +297,7 @@ const SendToOptions = () => { { setRecipientAmount({showModal: false}); }} diff --git a/src/navigation/wallet/screens/TransactionDetails.tsx b/src/navigation/wallet/screens/TransactionDetails.tsx index a62b2591f1..e3d4b3236e 100644 --- a/src/navigation/wallet/screens/TransactionDetails.tsx +++ b/src/navigation/wallet/screens/TransactionDetails.tsx @@ -227,8 +227,9 @@ const TransactionDetails = () => { const title = getDetailsTitle(transaction, wallet); let { currencyAbbreviation, - keyId, - credentials: {network, coin, walletName, walletId}, + network, + chain, + credentials: {walletId}, } = wallet; currencyAbbreviation = currencyAbbreviation.toLowerCase(); const isTestnet = network === 'testnet'; @@ -278,7 +279,11 @@ const TransactionDetails = () => { txp.outputs.forEach((output: any) => { recipientList!.push({ address: output.toAddress, - amount: Number(dispatch(FormatAmount(coin, output.amount))), + amount: Number( + dispatch( + FormatAmount(currencyAbbreviation, chain, output.amount), + ), + ), }); }); } @@ -286,9 +291,13 @@ const TransactionDetails = () => { wallet, walletId, context: 'fromReplaceByFee' as TransactionOptionsContext, - amount: Number(dispatch(FormatAmount(coin, transaction.amount))), + amount: Number( + dispatch( + FormatAmount(currencyAbbreviation, chain, transaction.amount), + ), + ), toAddress, - coin, + currencyAbbreviation, network, inputs: txp.inputs, recipientList, @@ -354,7 +363,9 @@ const TransactionDetails = () => { }, [copied]); const goToBlockchain = () => { - let url = dispatch(GetBlockExplorerUrl(currencyAbbreviation, network)); + let url = dispatch( + GetBlockExplorerUrl(currencyAbbreviation, network, chain), + ); switch (currencyAbbreviation) { case 'doge': url = @@ -413,7 +424,7 @@ const TransactionDetails = () => { {/* --------- Info ----------------*/} {(currencyAbbreviation === 'eth' || - dispatch(IsERCToken(currencyAbbreviation))) && + IsERCToken(currencyAbbreviation)) && txs.error ? ( { const [lastSigner, setLastSigner] = useState(false); const title = getDetailsTitle(transaction, wallet); - let { - currencyAbbreviation, - credentials: {network}, - } = wallet; + let {currencyAbbreviation, network} = wallet; currencyAbbreviation = currencyAbbreviation.toLowerCase(); const isTestnet = network === 'testnet'; diff --git a/src/navigation/wallet/screens/WalletDetails.tsx b/src/navigation/wallet/screens/WalletDetails.tsx index 28adcdcb45..566879de5b 100644 --- a/src/navigation/wallet/screens/WalletDetails.tsx +++ b/src/navigation/wallet/screens/WalletDetails.tsx @@ -107,9 +107,8 @@ import { import KeySvg from '../../../../assets/img/key.svg'; import TimerSvg from '../../../../assets/img/timer.svg'; import InfoSvg from '../../../../assets/img/info.svg'; -import {Effect} from '../../../store'; import {TouchableOpacity} from 'react-native-gesture-handler'; -import {Currencies} from '../../../constants/currencies'; +import {BitpaySupportedCoins} from '../../../constants/currencies'; import i18next from 'i18next'; import {logSegmentEvent} from '../../../store/app/app.effects'; import _ from 'lodash'; @@ -259,19 +258,6 @@ const getWalletType = ( return; }; -const getChain = - (currencyAbbreviation: string, network: string): Effect => - dispatch => { - if ( - currencyAbbreviation === 'eth' || - dispatch(IsERCToken(currencyAbbreviation)) - ) { - return network === 'testnet' ? 'Kovan' : 'Ethereum Mainnet'; - } - - return network === 'testnet' ? 'Testnet' : undefined; - }; - const WalletDetails: React.FC = ({route}) => { const navigation = useNavigation(); const dispatch = useAppDispatch(); @@ -354,6 +340,7 @@ const WalletDetails: React.FC = ({route}) => { params: { cryptoCurrencyAbbreviation: fullWalletObj.currencyAbbreviation.toUpperCase(), + chain: fullWalletObj.chain, onAmountSelected: async (amount, setButtonState) => { setButtonState('success'); await sleep(500); @@ -586,7 +573,7 @@ const WalletDetails: React.FC = ({route}) => { let tx: any; if ( currencyAbbreviation.toLowerCase() === 'eth' || - dispatch(IsERCToken(currencyAbbreviation)) + IsERCToken(currencyAbbreviation) ) { tx = await dispatch( buildEthERCTokenSpeedupTx(fullWalletObj, transaction), @@ -691,7 +678,7 @@ const WalletDetails: React.FC = ({route}) => { const viewOnBlockchain = async () => { const coin = fullWalletObj.currencyAbbreviation.toLowerCase(); - if (['eth', 'xrp'].includes(coin) || dispatch(IsERCToken(coin))) { + if (['eth', 'xrp'].includes(coin) || IsERCToken(coin)) { let address; try { address = (await dispatch( @@ -705,20 +692,20 @@ const WalletDetails: React.FC = ({route}) => { if (coin === 'xrp') { url = fullWalletObj.network === 'livenet' - ? `https://${Currencies.xrp.paymentInfo.blockExplorerUrls}account/${address}` - : `https://${Currencies.xrp.paymentInfo.blockExplorerUrlsTestnet}account/${address}`; + ? `https://${BitpaySupportedCoins.xrp.paymentInfo.blockExplorerUrls}account/${address}` + : `https://${BitpaySupportedCoins.xrp.paymentInfo.blockExplorerUrlsTestnet}account/${address}`; } if (coin === 'eth') { url = fullWalletObj.network === 'livenet' - ? `https://${Currencies.eth.paymentInfo.blockExplorerUrls}address/${address}` - : `https://${Currencies.eth.paymentInfo.blockExplorerUrlsTestnet}address/${address}`; + ? `https://${BitpaySupportedCoins.eth.paymentInfo.blockExplorerUrls}address/${address}` + : `https://${BitpaySupportedCoins.eth.paymentInfo.blockExplorerUrlsTestnet}address/${address}`; } - if (dispatch(IsERCToken(coin))) { + if (IsERCToken(coin)) { url = fullWalletObj.network === 'livenet' - ? `https://${Currencies.eth?.paymentInfo.blockExplorerUrls}address/${address}#tokentxns` - : `https://${Currencies.eth?.paymentInfo.blockExplorerUrlsTestnet}address/${address}#tokentxns`; + ? `https://${BitpaySupportedCoins.eth?.paymentInfo.blockExplorerUrls}address/${address}#tokentxns` + : `https://${BitpaySupportedCoins.eth?.paymentInfo.blockExplorerUrlsTestnet}address/${address}#tokentxns`; } if (url) { @@ -777,8 +764,8 @@ const WalletDetails: React.FC = ({route}) => { ), ), ); - } else if (dispatch(CanSpeedupTx(transaction, currency))) { - if (currency === 'eth' || dispatch(IsERCToken(currency))) { + } else if (CanSpeedupTx(transaction, currency)) { + if (currency === 'eth' || IsERCToken(currency)) { dispatch( showBottomNotificationModal( SpeedupEthTransaction( @@ -865,7 +852,21 @@ const WalletDetails: React.FC = ({route}) => { [], ); - const chain = dispatch(getChain(currencyAbbreviation.toLowerCase(), network)); + const getNetworkName = ( + currencyAbbreviation: string, + network: string, + ): string | undefined => { + if (currencyAbbreviation === 'eth' || IsERCToken(currencyAbbreviation)) { + return network === 'testnet' ? 'Kovan' : 'Ethereum Mainnet'; + } + + return network === 'testnet' ? 'Testnet' : undefined; + }; + + const networkName = getNetworkName( + currencyAbbreviation.toLowerCase(), + network, + ); return ( @@ -928,12 +929,12 @@ const WalletDetails: React.FC = ({route}) => { {walletType.title} )} - {chain ? ( + {networkName ? ( - {chain} + {networkName} ) : null} {IsShared(fullWalletObj) ? ( diff --git a/src/navigation/wallet/screens/request-specific-amount/RequestSpecificAmountQR.tsx b/src/navigation/wallet/screens/request-specific-amount/RequestSpecificAmountQR.tsx index cbc71d05b7..99ae387256 100644 --- a/src/navigation/wallet/screens/request-specific-amount/RequestSpecificAmountQR.tsx +++ b/src/navigation/wallet/screens/request-specific-amount/RequestSpecificAmountQR.tsx @@ -107,8 +107,10 @@ const RequestSpecificAmountQR = () => { useRoute>(); const {wallet, requestAmount} = route.params; const { - credentials: {walletName, network}, + credentials: {walletName}, currencyAbbreviation, + network, + chain, } = wallet; const navigation = useNavigation(); const dispatch = useAppDispatch(); @@ -145,12 +147,12 @@ const RequestSpecificAmountQR = () => { let _qrValue; _qrValue = - dispatch(GetProtocolPrefix(currencyAbbreviation, network)) + + dispatch(GetProtocolPrefix(currencyAbbreviation, network, chain)) + ':' + address; const _formattedAmountObj = dispatch( - ParseAmount(requestAmount, currencyAbbreviation), + ParseAmount(requestAmount, currencyAbbreviation, chain), ); if (IsUtxoCoin(currencyAbbreviation) || currencyAbbreviation === 'xrp') { diff --git a/src/navigation/wallet/screens/send/SendTo.tsx b/src/navigation/wallet/screens/send/SendTo.tsx index 3252ff3d40..a43baa4caa 100644 --- a/src/navigation/wallet/screens/send/SendTo.tsx +++ b/src/navigation/wallet/screens/send/SendTo.tsx @@ -56,7 +56,7 @@ import { dismissOnGoingProcessModal, showBottomNotificationModal, } from '../../../../store/app/app.actions'; -import {Currencies} from '../../../../constants/currencies'; +import {BitpaySupportedCoins} from '../../../../constants/currencies'; import { AppDispatch, useAppDispatch, @@ -75,7 +75,7 @@ import { TranslateToBchCashAddress, } from '../../../../store/wallet/effects/address/address'; import {APP_NAME_UPPERCASE} from '../../../../constants/config'; -import {GetChain, IsUtxoCoin} from '../../../../store/wallet/utils/currency'; +import {IsUtxoCoin} from '../../../../store/wallet/utils/currency'; import {goToAmount, incomingData} from '../../../../store/scan/scan.effects'; import {useTranslation} from 'react-i18next'; import {toFiat} from '../../../../store/wallet/utils/wallet'; @@ -147,7 +147,7 @@ export const BuildKeyWalletRow = ( value.wallets .filter(({hideWallet}) => !hideWallet) .filter( - ({currencyAbbreviation, id, credentials: {network, walletName}}) => + ({currencyAbbreviation, id, network, credentials: {walletName}}) => currencyAbbreviation.toLowerCase() === currentCurrencyAbbreviation.toLowerCase() && id !== currentWalletId && @@ -159,7 +159,9 @@ export const BuildKeyWalletRow = ( balance, hideWallet, currencyAbbreviation, - credentials: {network, walletName: fallbackName}, + network, + chain, + credentials: {walletName: fallbackName}, walletName, } = wallet; // Clone wallet to avoid altering store values @@ -173,6 +175,7 @@ export const BuildKeyWalletRow = ( balance.sat, defaultAltCurrencyIsoCode, currencyAbbreviation, + chain, rates, ), ), @@ -215,11 +218,7 @@ const SendTo = () => { const [showWalletOptions, setShowWalletOptions] = useState(false); const {wallet} = route.params; - const { - currencyAbbreviation, - id, - credentials: {network}, - } = wallet; + const {currencyAbbreviation, id, chain, network} = wallet; const isUtxo = IsUtxoCoin(wallet?.currencyAbbreviation); @@ -316,14 +315,13 @@ const SendTo = () => { let isValid, addrData: CoinNetwork | null; if (isPayPro) { isValid = - data?.chain === - Currencies[currencyAbbreviation.toLowerCase()].chain && - data?.network === network; + data?.chain?.toLowerCase() === chain.toLowerCase() && + data?.network.toLowerCase() === network.toLowerCase(); } else { addrData = GetCoinAndNetwork(data, network); isValid = - dispatch(GetChain(currencyAbbreviation)).toLowerCase() === - addrData?.coin.toLowerCase() && addrData?.network === network; + chain === addrData?.coin.toLowerCase() && + addrData?.network === network; } if (isValid) { @@ -456,7 +454,12 @@ const SendTo = () => { }; dispatch( - goToAmount({coin: wallet.currencyAbbreviation, recipient, wallet}), + goToAmount({ + coin: wallet.currencyAbbreviation, + chain: wallet.chain, + recipient, + wallet, + }), ); } catch (err: any) { logger.error(`Send To: ${getErrorString(err)}`); diff --git a/src/navigation/wallet/screens/send/TransactionLevel.tsx b/src/navigation/wallet/screens/send/TransactionLevel.tsx index 9cd5378f7e..b64e5af80d 100644 --- a/src/navigation/wallet/screens/send/TransactionLevel.tsx +++ b/src/navigation/wallet/screens/send/TransactionLevel.tsx @@ -192,17 +192,16 @@ const TransactionLevel = ({ feeLevel, feePerSatByte: paramFeePerSatByte, }: TransactionSpeedParamList) => { - const { - img, - credentials: {coin, network}, - } = wallet; + const {img, currencyAbbreviation, network, chain} = wallet; const {t} = useTranslation(); const dispatch = useAppDispatch(); const insets = useSafeAreaInsets(); const theme = useTheme(); const [speedUpMinFeePerKb, setSpeedUpMinFeePerKb] = useState(); - const {feeUnit, feeUnitAmount, blockTime} = dispatch(GetFeeUnits(coin)); + const {feeUnit, feeUnitAmount, blockTime} = dispatch( + GetFeeUnits(currencyAbbreviation, chain), + ); const [feeOptions, setFeeOptions] = useState(); const [feePerSatByte, setFeePerSatByte] = useState< number | string | undefined @@ -218,16 +217,17 @@ const TransactionLevel = ({ const minFeeAllowed = FEE_MIN; const [maxFeeAllowed, setMaxFeeAllowed] = useState(); - const {coinColor: backgroundColor} = - coin === 'btc' ? dispatch(GetTheme(coin)) : dispatch(GetTheme('eth')); + const {coinColor: backgroundColor} = dispatch( + GetTheme(currencyAbbreviation, chain), + ); const themedBackground = theme.dark ? '#464646' : NeutralSlate; const setSpeedUpMinFee = (_feeLevels: Fee[]): number | undefined => { - const minFeeLevel = coin === 'btc' ? 'custom' : 'priority'; + const minFeeLevel = currencyAbbreviation === 'btc' ? 'custom' : 'priority'; let feeLevelsAllowed: Fee[] = []; let _speedUpMinFeePerKb; - if (coin === 'btc') { + if (currencyAbbreviation === 'btc') { feeLevelsAllowed = _feeLevels.filter( (f: Fee) => f.feePerKb >= customFeePerKB, ); @@ -260,15 +260,15 @@ const TransactionLevel = ({ ...fee, feeUnit, // @ts-ignore - uiLevel: dispatch(GetFeeOptions(coin))[level], + uiLevel: GetFeeOptions(currencyAbbreviation, chain)[level], }; feeOption.feePerSatByte = (feePerKb / feeUnitAmount).toFixed(); feeOption.uiFeePerSatByte = `${feeOption.feePerSatByte} ${ - coin === 'btc' ? t('Satoshis per byte') : feeUnit + currencyAbbreviation === 'btc' ? t('Satoshis per byte') : feeUnit }`; - if (coin === 'eth' || dispatch(IsERCToken(coin))) { + if (currencyAbbreviation === 'eth' || IsERCToken(currencyAbbreviation)) { // @ts-ignore feeOption.avgConfirmationTime = ethAvgTime[level]; } else { @@ -496,7 +496,7 @@ const TransactionLevel = ({

- {coin === 'btc' ? 'Bitcoin' : 'Ethereum'}{' '} + {currencyAbbreviation === 'btc' ? 'Bitcoin' : 'Ethereum'}{' '} {t('Network Fee Policy')}

diff --git a/src/navigation/wallet/screens/send/confirm/Confirm.tsx b/src/navigation/wallet/screens/send/confirm/Confirm.tsx index c2ffc60490..0cc5b0d0db 100644 --- a/src/navigation/wallet/screens/send/confirm/Confirm.tsx +++ b/src/navigation/wallet/screens/send/confirm/Confirm.tsx @@ -162,9 +162,10 @@ const Confirm = () => { const [destinationTag, setDestinationTag] = useState( recipient?.destinationTag || _destinationTag, ); - const {currencyAbbreviation} = wallet; - const feeOptions = dispatch(GetFeeOptions(currencyAbbreviation)); - const {unitToSatoshi} = dispatch(GetPrecision(currencyAbbreviation)) || {}; + const {currencyAbbreviation, chain} = wallet; + const feeOptions = GetFeeOptions(currencyAbbreviation); + const {unitToSatoshi} = + dispatch(GetPrecision(currencyAbbreviation, chain)) || {}; useLayoutEffect(() => { navigation.setOptions({ headerTitle: () => ( @@ -329,11 +330,14 @@ const Confirm = () => { img: r.type === 'contact' ? r.type : wallet.img, recipientAmountStr: `${r.amount} ${currencyAbbreviation.toUpperCase()}`, recipientAltAmountStr: formatFiatAmount( - dispatch(toFiat(amountSat, isoCode, currencyAbbreviation, rates)), + dispatch( + toFiat(amountSat, isoCode, currencyAbbreviation, chain, rates), + ), isoCode, ), recipientType: r.type, recipientCoin: currencyAbbreviation, + recipientChain: r.chain, }; }); } @@ -346,6 +350,7 @@ const Confirm = () => { recipientName: recipient.name, recipientAddress: sendingTo.recipientAddress, img: recipient.type, + recipientChain: recipient.chain, }; } else { recipientData = sendingTo; diff --git a/src/navigation/wallet/screens/send/confirm/PayProConfirm.tsx b/src/navigation/wallet/screens/send/confirm/PayProConfirm.tsx index ea1b9e9871..f44fb0c109 100644 --- a/src/navigation/wallet/screens/send/confirm/PayProConfirm.tsx +++ b/src/navigation/wallet/screens/send/confirm/PayProConfirm.tsx @@ -347,9 +347,7 @@ const PayProConfirm = () => { ) : null} diff --git a/src/navigation/wallet/screens/send/confirm/Shared.tsx b/src/navigation/wallet/screens/send/confirm/Shared.tsx index 4987825e57..b15ca6d5f0 100644 --- a/src/navigation/wallet/screens/send/confirm/Shared.tsx +++ b/src/navigation/wallet/screens/send/confirm/Shared.tsx @@ -18,7 +18,7 @@ import styled from 'styled-components/native'; import {Pressable, ScrollView, View} from 'react-native'; import {CurrencyImage} from '../../../../../components/currency-image/CurrencyImage'; import ChevronRightSvg from '../../../../../../assets/img/angle-right.svg'; -import {sleep} from '../../../../../utils/helper-methods'; +import {getBadgeImg, sleep} from '../../../../../utils/helper-methods'; import {WalletsAndAccounts} from '../../../../../store/wallet/utils/wallet'; import {WalletRowProps} from '../../../../../components/list/WalletRow'; import KeyWalletsRow, { @@ -41,6 +41,7 @@ import {useTranslation} from 'react-i18next'; import {KeyboardAwareScrollView} from 'react-native-keyboard-aware-scroll-view'; import AddressCard from '../../../components/AddressCard'; import {LuckySevens} from '../../../../../styles/colors'; +import {IsERCToken} from '../../../../../store/wallet/utils/currency'; // Styled export const ConfirmContainer = styled.SafeAreaView` @@ -143,9 +144,20 @@ export const SendingTo: React.VFC = ({ return null; } - const {recipientName, recipientAddress, img, recipientFullAddress} = - recipient; + const { + recipientName, + recipientAddress, + img, + recipientFullAddress, + recipientCoin, + recipientChain, + } = recipient; + let badgeImg; + + if (recipientCoin && recipientChain && IsERCToken(recipientCoin)) { + badgeImg = getBadgeImg(recipientCoin, recipientChain); + } const copyText = (text: string) => { if (!copied && !!text) { Clipboard.setString(text); @@ -178,7 +190,7 @@ export const SendingTo: React.VFC = ({ copied ? ( ) : ( - + ) } description={description} @@ -264,8 +276,8 @@ export const SendingFrom: React.VFC = ({ return null; } - const {walletName, img} = sender; - const icon = ; + const {walletName, img, badgeImg} = sender; + const icon = ; return ( <> diff --git a/src/navigation/wallet/screens/wallet-settings/Addresses.tsx b/src/navigation/wallet/screens/wallet-settings/Addresses.tsx index b564d201df..8042ce2fe9 100644 --- a/src/navigation/wallet/screens/wallet-settings/Addresses.tsx +++ b/src/navigation/wallet/screens/wallet-settings/Addresses.tsx @@ -95,9 +95,11 @@ const Addresses = () => { } = useRoute>(); const { - credentials: {token, multisigEthInfo, coin}, + credentials: {token, multisigEthInfo}, walletName, currencyName, + currencyAbbreviation, + chain, } = wallet; const navigation = useNavigation(); const [loading, setLoading] = useState(true); @@ -197,9 +199,23 @@ const Addresses = () => { setLowUtxosNb(response.lowUtxos.length); setAllUtxosNb(response.allUtxos.length); - setLowUtxosSum(dispatch(FormatAmountStr(coin, _lowUtoxosSum))); - setAllUtxosSum(dispatch(FormatAmountStr(coin, allSum))); - setMinFee(dispatch(FormatAmountStr(coin, response.minFee || 0))); + setLowUtxosSum( + dispatch( + FormatAmountStr(currencyAbbreviation, chain, _lowUtoxosSum), + ), + ); + setAllUtxosSum( + dispatch(FormatAmountStr(currencyAbbreviation, chain, allSum)), + ); + setMinFee( + dispatch( + FormatAmountStr( + currencyAbbreviation, + chain, + response.minFee || 0, + ), + ), + ); setMinFeePer(per.toFixed(2) + '%'); } } catch (e) { @@ -219,13 +235,16 @@ const Addresses = () => { }; const buildUiFormatList = (list: any, wallet: Wallet) => { - const { - credentials: {coin, network}, - } = wallet; + const {currencyAbbreviation, network, chain} = wallet; list.forEach((item: any) => { item.path = item.path ? item.path.replace(/^m/g, 'xpub') : null; item.address = dispatch( - GetProtocolPrefixAddress(coin, network, item.address), + GetProtocolPrefixAddress( + currencyAbbreviation, + network, + item.address, + chain, + ), ); if (item.createdOn) { @@ -316,7 +335,8 @@ const Addresses = () => { navigation.navigate('Wallet', { screen: 'AllAddresses', params: { - currencyAbbreviation: coin, + currencyAbbreviation, + chain, walletName: walletName || currencyName, usedAddresses: usedAddress, unusedAddresses: unusedAddress, @@ -396,7 +416,15 @@ const Addresses = () => { - {dispatch(FormatAmountStr(coin, amount))} + + {dispatch( + FormatAmountStr( + currencyAbbreviation, + chain, + amount, + ), + )} +
diff --git a/src/navigation/wallet/screens/wallet-settings/AllAddresses.tsx b/src/navigation/wallet/screens/wallet-settings/AllAddresses.tsx index f33cc64af1..e37cca8786 100644 --- a/src/navigation/wallet/screens/wallet-settings/AllAddresses.tsx +++ b/src/navigation/wallet/screens/wallet-settings/AllAddresses.tsx @@ -29,6 +29,7 @@ export type AllAddressesParamList = { usedAddresses?: any[]; unusedAddresses?: any[]; currencyAbbreviation: string; + chain: string; }; const AddressesContainer = styled.SafeAreaView` @@ -66,7 +67,13 @@ const CopyImgContainerRight = styled.View` const AllAddresses = () => { const {t} = useTranslation(); const { - params: {walletName, currencyAbbreviation, usedAddresses, unusedAddresses}, + params: { + walletName, + currencyAbbreviation, + usedAddresses, + unusedAddresses, + chain, + }, } = useRoute>(); const navigation = useNavigation(); @@ -166,7 +173,9 @@ const AllAddresses = () => { - {dispatch(FormatAmountStr(currencyAbbreviation, amount))} + {dispatch( + FormatAmountStr(currencyAbbreviation, chain, amount), + )} diff --git a/src/navigation/wallet/screens/wallet-settings/ExportWallet.tsx b/src/navigation/wallet/screens/wallet-settings/ExportWallet.tsx index 5fe5c4506a..b8e1d6ea13 100644 --- a/src/navigation/wallet/screens/wallet-settings/ExportWallet.tsx +++ b/src/navigation/wallet/screens/wallet-settings/ExportWallet.tsx @@ -91,9 +91,7 @@ const ExportWallet = () => { params: {wallet, keyObj}, } = useRoute>(); - const { - credentials: {network}, - } = wallet; + const {network} = wallet; const navigation = useNavigation(); const [showOptions, setShowOptions] = useState(false); diff --git a/src/navigation/wallet/screens/wallet-settings/WalletInformation.tsx b/src/navigation/wallet/screens/wallet-settings/WalletInformation.tsx index 6b5f86e39a..3128aa38bd 100644 --- a/src/navigation/wallet/screens/wallet-settings/WalletInformation.tsx +++ b/src/navigation/wallet/screens/wallet-settings/WalletInformation.tsx @@ -89,14 +89,15 @@ const WalletInformation = () => { } = useRoute>(); const { + chain, + currencyAbbreviation, + network, credentials: { walletName, - coin, walletId, token, m, n, - network, addressType, rootPath, keyId, @@ -161,7 +162,7 @@ const WalletInformation = () => { return () => clearTimeout(timer); }, [copiedAddress]); - const {unitToSatoshi, unitDecimals} = dispatch(GetPrecision(coin))!; + const {unitToSatoshi} = dispatch(GetPrecision(currencyAbbreviation, chain))!; const [copayers, setCopayers] = useState(); const [balanceByAddress, setBalanceByAddress] = useState(); @@ -208,7 +209,7 @@ const WalletInformation = () => { {t('Coin')} - {coin.toUpperCase()} + {currencyAbbreviation.toUpperCase()}
@@ -268,7 +269,7 @@ const WalletInformation = () => {
- {IsUtxoCoin(coin) ? ( + {IsUtxoCoin(currencyAbbreviation) ? ( <> {t('Address Type')} @@ -411,7 +412,7 @@ const WalletInformation = () => { {(a.amount / unitToSatoshi).toFixed(8)}{' '} - {coin.toUpperCase()} + {currencyAbbreviation.toUpperCase()} diff --git a/src/store/coinbase/coinbase.effects.ts b/src/store/coinbase/coinbase.effects.ts index a0fc1f9004..e4c355b295 100644 --- a/src/store/coinbase/coinbase.effects.ts +++ b/src/store/coinbase/coinbase.effects.ts @@ -278,7 +278,7 @@ export const coinbaseGetAccountsAndBalance = const supportedCurrency: string[] = []; for (let i = 0; i < SupportedCurrencyOptions.length; i++) { - supportedCurrency.push(SupportedCurrencyOptions[i].id); + supportedCurrency.push(SupportedCurrencyOptions[i].currencyAbbreviation); } try { diff --git a/src/store/contact/contact.reducer.ts b/src/store/contact/contact.reducer.ts index c2965ebd23..6ddfd95fa5 100644 --- a/src/store/contact/contact.reducer.ts +++ b/src/store/contact/contact.reducer.ts @@ -24,6 +24,7 @@ export const contactReducer = ( action.contact.address, action.contact.coin, action.contact.network, + action.contact.chain, ) ) { return { @@ -34,14 +35,15 @@ export const contactReducer = ( return {...state}; case ContactActionTypes.UPDATE_CONTACT: - const {address, coin, network} = action.contact; + const {address, coin, chain, network} = action.contact; return { ...state, list: state.list.map((contact: ContactRowProps) => { if ( contact.address === address && contact.coin === coin && - contact.network === network + contact.network === network && + contact.chain === chain ) { contact = action.contact; } @@ -56,7 +58,8 @@ export const contactReducer = ( return ( contact.address !== action.address || contact.coin !== action.coin || - contact.network !== action.network + contact.network !== action.network || + contact.chain !== action.chain ); }), }; diff --git a/src/store/scan/scan.effects.ts b/src/store/scan/scan.effects.ts index e2043235f2..f5d76c197d 100644 --- a/src/store/scan/scan.effects.ts +++ b/src/store/scan/scan.effects.ts @@ -64,6 +64,7 @@ import axios from 'axios'; import {t} from 'i18next'; import {GeneralError} from '../../navigation/wallet/components/ErrorMessages'; import {StackActions} from '@react-navigation/native'; +import {BitpaySupportedEvmCoins} from '../../constants/currencies'; export const incomingData = ( @@ -80,6 +81,7 @@ export const incomingData = await sleep(200); const coin = opts?.wallet?.currencyAbbreviation?.toLowerCase(); + const chain = opts?.wallet?.credentials?.chain.toLowerCase(); try { if (IsValidBitPayInvoice(data)) { dispatch(handleUnlock(data)); @@ -122,22 +124,24 @@ export const incomingData = dispatch(handleBitPayUri(data, opts?.wallet)); // Plain Address (Bitcoin) } else if (IsValidBitcoinAddress(data)) { - dispatch(handlePlainAddress(data, coin || 'btc', opts)); + dispatch(handlePlainAddress(data, coin || 'btc', chain || 'btc', opts)); // Plain Address (Bitcoin Cash) } else if (IsValidBitcoinCashAddress(data)) { - dispatch(handlePlainAddress(data, coin || 'bch', opts)); + dispatch(handlePlainAddress(data, coin || 'bch', chain || 'bch', opts)); // Address (Ethereum) } else if (IsValidEthereumAddress(data)) { - dispatch(handlePlainAddress(data, coin || 'eth', opts)); + dispatch(handlePlainAddress(data, coin || 'eth', chain || 'eth', opts)); // Address (Ripple) } else if (IsValidRippleAddress(data)) { - dispatch(handlePlainAddress(data, coin || 'xrp', opts)); + dispatch(handlePlainAddress(data, coin || 'xrp', chain || 'xrp', opts)); // Plain Address (Doge) } else if (IsValidDogecoinAddress(data)) { - dispatch(handlePlainAddress(data, coin || 'doge', opts)); + dispatch( + handlePlainAddress(data, coin || 'doge', chain || 'doge', opts), + ); // Plain Address (Litecoin) } else if (IsValidLitecoinAddress(data)) { - dispatch(handlePlainAddress(data, coin || 'ltc', opts)); + dispatch(handlePlainAddress(data, coin || 'ltc', chain || 'ltc', opts)); // Import Private Key } else if (IsValidImportPrivateKey(data)) { goToImport(data); @@ -414,7 +418,8 @@ const goToConfirm = ...recipient, ...{ opts: { - showERC20Tokens: recipient.currency.toLowerCase() === 'eth', // no wallet selected - if ETH address show token wallets in next view + showERC20Tokens: + !!BitpaySupportedEvmCoins[recipient.currency.toLowerCase()], // no wallet selected - if ETH address show token wallets in next view message: opts?.message || '', }, }, @@ -495,11 +500,13 @@ const goToConfirm = export const goToAmount = ({ coin, + chain, recipient, wallet, opts: urlOpts, }: { coin: string; + chain: string; recipient: { type: string; address: string; @@ -523,7 +530,8 @@ export const goToAmount = ...recipient, ...{ opts: { - showERC20Tokens: recipient.currency.toLowerCase() === 'eth', // no wallet selected - if ETH address show token wallets in next view + showERC20Tokens: + !!BitpaySupportedEvmCoins[recipient.currency.toLowerCase()], // no wallet selected - if ETH address show token wallets in next view }, }, }, @@ -531,12 +539,12 @@ export const goToAmount = }); return Promise.resolve(); } - navigationRef.navigate('Wallet', { screen: WalletScreens.AMOUNT, params: { sendMaxEnabled: true, cryptoCurrencyAbbreviation: coin.toUpperCase(), + chain, onAmountSelected: async (amount, setButtonState, amountOpts) => { dispatch( goToConfirm({ @@ -602,7 +610,8 @@ const handleBitPayUri = }; if (!params.get('amount')) { - dispatch(goToAmount({coin, recipient, wallet, opts: {message}})); + const chain = wallet!.chain; + dispatch(goToAmount({coin, chain, recipient, wallet, opts: {message}})); } else { const amount = Number(params.get('amount')); dispatch( @@ -622,6 +631,7 @@ const handleBitcoinUri = dispatch => { dispatch(LogActions.info('[scan] Incoming-data: Bitcoin URI')); const coin = 'btc'; + const chain = 'btc'; const parsed = BwcProvider.getInstance().getBitcore().URI(data); const address = parsed.address ? parsed.address.toString() : ''; const message = parsed.message; @@ -634,9 +644,9 @@ const handleBitcoinUri = if (parsed.r) { dispatch(goToPayPro(parsed.r)); } else if (!parsed.amount) { - dispatch(goToAmount({coin, recipient, wallet, opts: {message}})); + dispatch(goToAmount({coin, chain, recipient, wallet, opts: {message}})); } else { - const amount = Number(dispatch(FormatAmount(coin, parsed.amount))); + const amount = Number(dispatch(FormatAmount(coin, chain, parsed.amount))); dispatch(goToConfirm({recipient, amount, wallet, opts: {message}})); } }; @@ -646,6 +656,7 @@ const handleBitcoinCashUri = dispatch => { dispatch(LogActions.info('[scan] Incoming-data: BitcoinCash URI')); const coin = 'bch'; + const chain = 'bch'; const parsed = BwcProvider.getInstance().getBitcoreCash().URI(data); const message = parsed.message; let address = parsed.address ? parsed.address.toString() : ''; @@ -664,9 +675,9 @@ const handleBitcoinCashUri = if (parsed.r) { dispatch(goToPayPro(parsed.r)); } else if (!parsed.amount) { - dispatch(goToAmount({coin, recipient, wallet, opts: {message}})); + dispatch(goToAmount({coin, chain, recipient, wallet, opts: {message}})); } else { - const amount = Number(dispatch(FormatAmount(coin, parsed.amount))); + const amount = Number(dispatch(FormatAmount(coin, chain, parsed.amount))); dispatch(goToConfirm({recipient, amount, wallet, opts: {message}})); } }; @@ -680,6 +691,7 @@ const handleBitcoinCashUriLegacyAddress = ), ); const coin = 'bch'; + const chain = 'bch'; const parsed = BwcProvider.getInstance() .getBitcore() .URI(data.replace(/^(bitcoincash:|bchtest:)/, 'bitcoin:')); @@ -716,9 +728,9 @@ const handleBitcoinCashUriLegacyAddress = if (parsed.r) { dispatch(goToPayPro(parsed.r)); } else if (!parsed.amount) { - dispatch(goToAmount({coin, recipient, wallet, opts: {message}})); + dispatch(goToAmount({coin, chain, recipient, wallet, opts: {message}})); } else { - const amount = Number(dispatch(FormatAmount(coin, parsed.amount))); + const amount = Number(dispatch(FormatAmount(coin, chain, parsed.amount))); dispatch(goToConfirm({recipient, amount, wallet, opts: {message}})); } }; @@ -728,6 +740,7 @@ const handleEthereumUri = dispatch => { dispatch(LogActions.info('[scan] Incoming-data: Ethereum URI')); const coin = 'eth'; + const chain = 'eth'; const value = /[\?\&]value=(\d+([\,\.]\d+)?)/i; const gasPrice = /[\?\&]gasPrice=(\d+([\,\.]\d+)?)/i; let feePerKb; @@ -741,10 +754,12 @@ const handleEthereumUri = address, }; if (!value.exec(data)) { - dispatch(goToAmount({coin, recipient, wallet, opts: {feePerKb}})); + dispatch(goToAmount({coin, chain, recipient, wallet, opts: {feePerKb}})); } else { const parsedAmount = value.exec(data)![1]; - const amount = Number(dispatch(FormatAmount(coin, Number(parsedAmount)))); + const amount = Number( + dispatch(FormatAmount(coin, chain, Number(parsedAmount))), + ); dispatch( goToConfirm({ recipient, @@ -761,6 +776,7 @@ const handleRippleUri = dispatch => { dispatch(LogActions.info('[scan] Incoming-data: Ripple URI')); const coin = 'xrp'; + const chain = 'xrp'; const amountParam = /[\?\&]amount=(\d+([\,\.]\d+)?)/i; const tagParam = /[\?\&]dt=(\d+([\,\.]\d+)?)/i; let destinationTag; @@ -776,7 +792,7 @@ const handleRippleUri = destinationTag: Number(destinationTag), }; if (!amountParam.exec(data)) { - dispatch(goToAmount({coin, recipient, wallet})); + dispatch(goToAmount({coin, chain, recipient, wallet})); } else { const parsedAmount = amountParam.exec(data)![1]; const amount = Number(parsedAmount); @@ -795,6 +811,7 @@ const handleDogecoinUri = dispatch => { dispatch(LogActions.info('[scan] Incoming-data: Dogecoin URI')); const coin = 'doge'; + const chain = 'doge'; const parsed = BwcProvider.getInstance().getBitcoreDoge().URI(data); const address = parsed.address ? parsed.address.toString() : ''; const message = parsed.message; @@ -809,9 +826,9 @@ const handleDogecoinUri = if (parsed.r) { dispatch(goToPayPro(parsed.r)); } else if (!parsed.amount) { - dispatch(goToAmount({coin, recipient, wallet, opts: {message}})); + dispatch(goToAmount({coin, chain, recipient, wallet, opts: {message}})); } else { - const amount = Number(dispatch(FormatAmount(coin, parsed.amount))); + const amount = Number(dispatch(FormatAmount(coin, chain, parsed.amount))); dispatch(goToConfirm({recipient, amount, wallet, opts: {message}})); } }; @@ -821,6 +838,7 @@ const handleLitecoinUri = dispatch => { dispatch(LogActions.info('[scan] Incoming-data: Litecoin URI')); const coin = 'ltc'; + const chain = 'ltc'; const parsed = BwcProvider.getInstance().getBitcoreLtc().URI(data); const address = parsed.address ? parsed.address.toString() : ''; const message = parsed.message; @@ -834,9 +852,9 @@ const handleLitecoinUri = if (parsed.r) { dispatch(goToPayPro(parsed.r)); } else if (!parsed.amount) { - dispatch(goToAmount({coin, recipient, wallet, opts: {message}})); + dispatch(goToAmount({coin, chain, recipient, wallet, opts: {message}})); } else { - const amount = Number(dispatch(FormatAmount(coin, parsed.amount))); + const amount = Number(dispatch(FormatAmount(coin, chain, parsed.amount))); dispatch(goToConfirm({recipient, amount, wallet, opts: {message}})); } }; @@ -977,6 +995,7 @@ const handlePlainAddress = ( address: string, coin: string, + chain: string, opts?: { wallet?: Wallet; context?: string; @@ -997,7 +1016,7 @@ const handlePlainAddress = network, destinationTag: opts?.destinationTag, }; - dispatch(goToAmount({coin, recipient, wallet: opts?.wallet})); + dispatch(goToAmount({coin, chain, recipient, wallet: opts?.wallet})); }; const goToImport = (importQrCodeData: string): void => { diff --git a/src/store/transforms/transforms.ts b/src/store/transforms/transforms.ts index 7d3b97dee0..e7003aca8c 100644 --- a/src/store/transforms/transforms.ts +++ b/src/store/transforms/transforms.ts @@ -5,6 +5,7 @@ import {BwcProvider} from '../../lib/bwc'; import {SUPPORTED_CURRENCIES} from '../../constants/currencies'; import {CurrencyListIcons} from '../../constants/SupportedCurrencyOptions'; import Flatted from 'flatted'; +import {buildWalletObj} from '../wallet/utils/wallet'; const BWCProvider = BwcProvider.getInstance(); export const bindWalletClient = createTransform( @@ -32,7 +33,7 @@ export const bindWalletClient = createTransform( _outboundState as {[key in string]: Key}, )) { const wallets = key.wallets.map(wallet => { - const {img, currencyAbbreviation} = wallet; + const {img, currencyAbbreviation, currencyName} = wallet; if (!img && SUPPORTED_CURRENCIES.includes(currencyAbbreviation)) { wallet.img = CurrencyListIcons[currencyAbbreviation]; } @@ -43,10 +44,18 @@ export const bindWalletClient = createTransform( hasConfirmingTxs: false, }; console.log(`bindWalletClient - ${wallet.id}`); - return merge( - BWCProvider.getClient(JSON.stringify(wallet.credentials)), + const _wallet = merge( + buildWalletObj({ + ...wallet.credentials, + currencyAbbreviation, + currencyName, + }), wallet, ); + return merge( + BWCProvider.getClient(JSON.stringify(_wallet.credentials)), + _wallet, + ); }); outboundState[id] = { diff --git a/src/store/wallet/effects/amount/amount.ts b/src/store/wallet/effects/amount/amount.ts index ea6fc8e17c..9dc344d7c2 100644 --- a/src/store/wallet/effects/amount/amount.ts +++ b/src/store/wallet/effects/amount/amount.ts @@ -18,12 +18,13 @@ export const ParseAmount = ( amount: number, currencyAbbreviation: string, + chain: string, fullPrecision?: boolean, ): Effect => dispatch => { // @ts-ignore const {unitToSatoshi, unitDecimals} = dispatch( - GetPrecision(currencyAbbreviation), + GetPrecision(currencyAbbreviation, chain), ); const satToUnit = 1 / unitToSatoshi; let amountUnitStr; @@ -32,7 +33,7 @@ export const ParseAmount = amountSat = Number((amount * unitToSatoshi).toFixed(0)); amountUnitStr = dispatch( - FormatAmountStr(currencyAbbreviation, amountSat, fullPrecision), + FormatAmountStr(currencyAbbreviation, chain, amountSat, fullPrecision), ) || ''; // workaround to prevent miscalculations with decimal numbers that javascript can't handle with precision @@ -72,6 +73,7 @@ const countDecimals = (num: number): number => { export const FormatAmountStr = ( currencyAbbreviation: string, + chain: string, satoshis: number, fullPrecision?: boolean, ): Effect => @@ -82,7 +84,9 @@ export const FormatAmountStr = try { return ( - dispatch(FormatAmount(currencyAbbreviation, satoshis, fullPrecision)) + + dispatch( + FormatAmount(currencyAbbreviation, chain, satoshis, fullPrecision), + ) + ' ' + currencyAbbreviation.toUpperCase() ); @@ -94,6 +98,7 @@ export const FormatAmountStr = export const FormatAmount = ( currencyAbbreviation: string, + chain: string, satoshis: number, fullPrecision?: boolean, ): Effect => @@ -105,7 +110,7 @@ export const FormatAmount = if (currencyAbbreviation && IsCustomERCToken(currencyAbbreviation)) { opts.toSatoshis = dispatch( - GetPrecision(currencyAbbreviation), + GetPrecision(currencyAbbreviation, chain), )?.unitToSatoshi; opts.decimals = { full: { @@ -125,10 +130,14 @@ export const FormatAmount = }; export const SatToUnit = - (amount: number, currencyAbbreviation: string): Effect => + ( + amount: number, + currencyAbbreviation: string, + chain: string, + ): Effect => dispatch => { const {unitToSatoshi, unitDecimals} = - dispatch(GetPrecision(currencyAbbreviation.toLowerCase())) || {}; + dispatch(GetPrecision(currencyAbbreviation, chain)) || {}; let spendableAmount: number | undefined; if (unitToSatoshi && unitDecimals) { const satToUnit = 1 / unitToSatoshi; @@ -139,12 +148,12 @@ export const SatToUnit = }; export const GetExcludedUtxosMessage = - (coin: string, sendMaxInfo: SendMaxInfo): Effect => + (coin: string, chain: string, sendMaxInfo: SendMaxInfo): Effect => dispatch => { const warningMsg = []; if (sendMaxInfo.utxosBelowFee > 0) { const amountBelowFeeStr = dispatch( - SatToUnit(sendMaxInfo.amountBelowFee, coin), + SatToUnit(sendMaxInfo.amountBelowFee, coin, chain), ); const message = `A total of ${amountBelowFeeStr} ${coin.toUpperCase()} were excluded. These funds come from UTXOs smaller than the network fee provided.`; warningMsg.push(message); @@ -152,7 +161,7 @@ export const GetExcludedUtxosMessage = if (sendMaxInfo.utxosAboveMaxSize > 0) { const amountAboveMaxSizeStr = dispatch( - SatToUnit(sendMaxInfo.amountAboveMaxSize, coin), + SatToUnit(sendMaxInfo.amountAboveMaxSize, coin, chain), ); const message = `A total of ${amountAboveMaxSizeStr} ${coin.toUpperCase()} were excluded. The maximum size allowed for a transaction was exceeded.`; warningMsg.push(message); diff --git a/src/store/wallet/effects/create-multisig/create-multisig.ts b/src/store/wallet/effects/create-multisig/create-multisig.ts index 3eef7ca771..a19160be86 100644 --- a/src/store/wallet/effects/create-multisig/create-multisig.ts +++ b/src/store/wallet/effects/create-multisig/create-multisig.ts @@ -1,7 +1,11 @@ import {Effect} from '../../../index'; import {BwcProvider} from '../../../../lib/bwc'; import merge from 'lodash.merge'; -import {buildKeyObj, buildWalletObj} from '../../utils/wallet'; +import { + buildKeyObj, + buildWalletObj, + mapAbbreviationAndName, +} from '../../utils/wallet'; import {successCreateKey, successAddWallet} from '../../wallet.actions'; import {Key, KeyOptions, Wallet} from '../../wallet.models'; import {createWalletWithOpts} from '../create/create'; @@ -51,10 +55,21 @@ export const startCreateKeyMultisig = dispatch(subscribeEmailNotifications(_wallet, prefs)); } + const {currencyAbbreviation, currencyName} = dispatch( + mapAbbreviationAndName( + _wallet.credentials.coin, + _wallet.credentials.chain, + ), + ); + // build out app specific props const wallet = merge( _wallet, - dispatch(buildWalletObj(_wallet.credentials)), + buildWalletObj({ + ..._wallet.credentials, + currencyAbbreviation, + currencyName, + }), ) as Wallet; const key = buildKeyObj({key: _key, wallets: [wallet]}); @@ -111,10 +126,21 @@ export const addWalletMultisig = dispatch(subscribeEmailNotifications(newWallet, prefs)); } + const {currencyAbbreviation, currencyName} = dispatch( + mapAbbreviationAndName( + newWallet.credentials.coin, + newWallet.credentials.chain, + ), + ); + key.wallets.push( merge( newWallet, - dispatch(buildWalletObj(newWallet.credentials)), + buildWalletObj({ + ...newWallet.credentials, + currencyAbbreviation, + currencyName, + }), ) as Wallet, ); diff --git a/src/store/wallet/effects/create/create.ts b/src/store/wallet/effects/create/create.ts index a0dc275fd4..dd1735e758 100644 --- a/src/store/wallet/effects/create/create.ts +++ b/src/store/wallet/effects/create/create.ts @@ -1,10 +1,7 @@ import { - Currencies, - SUPPORTED_COINS, - SUPPORTED_CURRENCIES, - SUPPORTED_TOKENS, + BitpaySupportedCoins, SupportedCoins, - SupportedTokens, + SUPPORTED_COINS, } from '../../../../constants/currencies'; import {Effect} from '../../../index'; import {Credentials} from 'bitcore-wallet-client/ts_build/lib/credentials'; @@ -14,6 +11,7 @@ import { buildKeyObj, buildWalletObj, checkEncryptPassword, + mapAbbreviationAndName, } from '../../utils/wallet'; import { failedAddWallet, @@ -23,7 +21,7 @@ import { import API from 'bitcore-wallet-client/ts_build'; import {Key, KeyMethods, KeyOptions, Token, Wallet} from '../../wallet.models'; import {Network} from '../../../../constants'; -import {BitpaySupportedTokenOpts} from '../../../../constants/tokens'; +import {BitpaySupportedEthereumTokenOpts} from '../../../../constants/tokens'; import { subscribeEmailNotifications, subscribePushNotifications, @@ -48,7 +46,13 @@ export interface CreateOptions { const BWC = BwcProvider.getInstance(); export const startCreateKey = - (currencies: string[]): Effect> => + ( + currencies: Array<{ + chain: string; + currencyAbbreviation: string; + isToken: boolean; + }>, + ): Effect> => async (dispatch, getState) => { return new Promise(async (resolve, reject) => { try { @@ -91,13 +95,15 @@ export const addWallet = key, currency, associatedWallet, - isToken, options, }: { key: Key; - currency: string; + currency: { + chain: string; + currencyAbbreviation: string; + isToken: boolean; + }; associatedWallet?: Wallet; - isToken?: boolean; options: CreateOptions; }): Effect> => async (dispatch, getState): Promise => { @@ -114,37 +120,54 @@ export const addWallet = WALLET, } = getState(); const tokenOpts = { - ...BitpaySupportedTokenOpts, - ...WALLET.tokenOptions, - ...WALLET.customTokenOptions, + eth: { + ...BitpaySupportedEthereumTokenOpts, + ...WALLET.tokenOptions, + ...WALLET.customTokenOptions, + }, }; const {walletName} = options; - if (isToken) { + if (currency.isToken) { if (!associatedWallet) { associatedWallet = (await createWallet({ key: key.methods!, - coin: 'eth', + coin: currency.chain as SupportedCoins, options, })) as Wallet; + const {currencyAbbreviation, currencyName} = dispatch( + mapAbbreviationAndName( + associatedWallet.credentials.coin, + associatedWallet.credentials.chain, + ), + ); key.wallets.push( merge( associatedWallet, - dispatch( - buildWalletObj(associatedWallet.credentials, tokenOpts), + buildWalletObj( + { + ...associatedWallet.credentials, + currencyAbbreviation, + currencyName, + }, + tokenOpts, ), ), ); } newWallet = (await dispatch( - createTokenWallet(associatedWallet, currency, tokenOpts), + createTokenWallet( + associatedWallet, + currency.currencyAbbreviation.toLowerCase(), + tokenOpts, + ), )) as Wallet; } else { newWallet = (await createWallet({ key: key.methods!, - coin: currency as SupportedCoins, + coin: currency.currencyAbbreviation as SupportedCoins, options, })) as Wallet; } @@ -171,13 +194,22 @@ export const addWallet = dispatch(subscribeEmailNotifications(newWallet, prefs)); } + const {currencyAbbreviation, currencyName} = dispatch( + mapAbbreviationAndName( + newWallet.credentials.coin, + newWallet.credentials.chain, + ), + ); + key.wallets.push( merge( newWallet, - dispatch( - buildWalletObj(newWallet.credentials, tokenOpts, { + buildWalletObj( + {...newWallet.credentials, currencyAbbreviation, currencyName}, + tokenOpts, + { walletName, - }), + }, ), ), ); @@ -204,7 +236,11 @@ const createMultipleWallets = options, }: { key: KeyMethods; - currencies: string[]; + currencies: Array<{ + chain: string; + currencyAbbreviation: string; + isToken: boolean; + }>; options: CreateOptions; }): Effect> => async (dispatch, getState) => { @@ -218,39 +254,37 @@ const createMultipleWallets = }, } = getState(); const tokenOpts = { - ...BitpaySupportedTokenOpts, - ...WALLET.tokenOptions, - ...WALLET.customTokenOptions, + eth: { + ...BitpaySupportedEthereumTokenOpts, + ...WALLET.tokenOptions, + ...WALLET.customTokenOptions, + }, }; - const supportedCoins = currencies.filter( - (currency): currency is SupportedCoins => - SUPPORTED_COINS.includes(currency), - ); - const supportedTokens = currencies.filter( - (currency): currency is SupportedTokens => - SUPPORTED_TOKENS.includes(currency), - ); - const customTokens = currencies.filter( - currency => !SUPPORTED_CURRENCIES.includes(currency), - ); - const tokens = [...supportedTokens, ...customTokens]; const wallets: API[] = []; - - for (const coin of supportedCoins) { + const tokens = currencies.filter(({isToken}) => isToken); + const coins = currencies.filter(({isToken}) => !isToken); + for (const coin of coins) { const wallet = (await createWallet({ key, - coin, - options: {...options, useNativeSegwit: ['btc', 'ltc'].includes(coin)}, + coin: coin.currencyAbbreviation as SupportedCoins, + options: { + ...options, + useNativeSegwit: ['btc', 'ltc'].includes(coin.currencyAbbreviation), + }, })) as Wallet; wallets.push(wallet); - if (coin === 'eth') { - wallet.preferences = wallet.preferences || { - tokenAddresses: [], - }; + if (coin.chain === 'eth') { for (const token of tokens) { + wallet.preferences = wallet.preferences || { + tokenAddresses: [], + }; const tokenWallet = await dispatch( - createTokenWallet(wallet, token, tokenOpts), + createTokenWallet( + wallet, + token.currencyAbbreviation.toLowerCase(), + tokenOpts, + ), ); wallets.push(tokenWallet); } @@ -276,9 +310,18 @@ const createMultipleWallets = }; dispatch(subscribeEmailNotifications(wallet, prefs)); } + const {currencyAbbreviation, currencyName} = dispatch( + mapAbbreviationAndName( + wallet.credentials.coin, + wallet.credentials.chain, + ), + ); return merge( wallet, - dispatch(buildWalletObj(wallet.credentials, tokenOpts)), + buildWalletObj( + {...wallet.credentials, currencyAbbreviation, currencyName}, + tokenOpts, + ), ); }); }; @@ -315,8 +358,9 @@ const createWallet = (params: { }), ); + const name = BitpaySupportedCoins[coin.toLowerCase()].name; bwcClient.createWallet( - Currencies[coin.toLowerCase()].name, + name, 'me', 1, 1, @@ -328,7 +372,6 @@ const createWallet = (params: { }, (err: any) => { if (err) { - console.log(err); switch (err.name) { case 'bwc.ErrorCOPAYER_REGISTERED': { // eslint-disable-next-line no-shadow @@ -368,14 +411,17 @@ const createTokenWallet = ( wallet: Wallet, token: string, - tokenOpts: {[key in string]: Token}, + tokenOpts: {[key in string]: {[key in string]: Token}}, ): Effect> => async (dispatch): Promise => { return new Promise((resolve, reject) => { try { const bwcClient = BWC.getClient(); const tokenCredentials: Credentials = - wallet.credentials.getTokenCredentials(tokenOpts[token]); + wallet.credentials.getTokenCredentials( + tokenOpts[wallet.credentials.chain][token], + wallet.credentials.chain, + ); bwcClient.fromObj(tokenCredentials); // push walletId as reference - this is used later to build out nested overview lists wallet.tokens = wallet.tokens || []; @@ -444,10 +490,21 @@ export const startCreateKeyWithOpts = dispatch(subscribeEmailNotifications(_wallet, prefs)); } + const {currencyAbbreviation, currencyName} = dispatch( + mapAbbreviationAndName( + _wallet.credentials.coin, + _wallet.credentials.chain, + ), + ); + // build out app specific props const wallet = merge( _wallet, - dispatch(buildWalletObj(_wallet.credentials)), + buildWalletObj({ + ..._wallet.credentials, + currencyAbbreviation, + currencyName, + }), ) as Wallet; const key = buildKeyObj({ @@ -502,7 +559,6 @@ export const createWalletWithOpts = (params: { }, (err: Error) => { if (err) { - console.log(err); switch (err.name) { case 'bwc.ErrorCOPAYER_REGISTERED': { const account = opts.account || 0; diff --git a/src/store/wallet/effects/currencies/currencies.ts b/src/store/wallet/effects/currencies/currencies.ts index afd0b90f4d..744bead861 100644 --- a/src/store/wallet/effects/currencies/currencies.ts +++ b/src/store/wallet/effects/currencies/currencies.ts @@ -6,7 +6,11 @@ import { successGetCustomTokenOptions, successGetTokenOptions, } from '../../wallet.actions'; -import {Currencies, CurrencyOpts} from '../../../../constants/currencies'; +import { + BitpaySupportedCoins, + BitpaySupportedEthereumTokens, + CurrencyOpts, +} from '../../../../constants/currencies'; import {LogActions} from '../../../log'; export const startGetTokenOptions = @@ -23,10 +27,14 @@ export const startGetTokenOptions = const tokenOptionsByAddress: {[key in string]: Token} = {}; const tokenData: {[key in string]: CurrencyOpts} = {}; Object.values(tokens).forEach(token => { - if (Currencies[token.symbol.toLowerCase()]) { + if ( + BitpaySupportedCoins[token.symbol.toLowerCase()] || + BitpaySupportedEthereumTokens[token.symbol.toLowerCase()] + ) { return; } // remove bitpay supported tokens and currencies populateTokenInfo({ + chain: 'eth', token, tokenOptions, tokenData, @@ -61,10 +69,14 @@ export const addCustomTokenOption = const customTokenOptions: {[key in string]: Token} = {}; const customTokenOptionsByAddress: {[key in string]: Token} = {}; const customTokenData: {[key in string]: CurrencyOpts} = {}; - if (Currencies[token.symbol.toLowerCase()]) { + if ( + BitpaySupportedCoins[token.symbol.toLowerCase()] || + BitpaySupportedEthereumTokens[token.symbol.toLowerCase()] + ) { return; } // remove bitpay supported tokens and currencies populateTokenInfo({ + chain: 'eth', token, tokenOptions: customTokenOptions, tokenData: customTokenData, @@ -85,11 +97,13 @@ export const addCustomTokenOption = }; const populateTokenInfo = ({ + chain, token, tokenOptions, tokenData, tokenOptionsByAddress, }: { + chain: string; token: Token; tokenOptions: {[key in string]: Token}; tokenData: {[key in string]: CurrencyOpts}; @@ -99,7 +113,7 @@ const populateTokenInfo = ({ tokenOptionsByAddress[token.address.toLowerCase()] = token; tokenData[token.symbol.toLowerCase()] = { name: token.name, - chain: 'ETH', + chain, coin: token.symbol, logoURI: token.logoURI, unitInfo: { diff --git a/src/store/wallet/effects/fee/fee.ts b/src/store/wallet/effects/fee/fee.ts index e9dca0de13..80844562bd 100644 --- a/src/store/wallet/effects/fee/fee.ts +++ b/src/store/wallet/effects/fee/fee.ts @@ -21,21 +21,18 @@ export interface Fee { nbBlocks: number; } -export const GetFeeOptions = - (currencyAbbreviation: string): Effect => - dispatch => { - const isEthOrToken = - currencyAbbreviation === 'eth' || - dispatch(IsERCToken(currencyAbbreviation)); - return { - urgent: isEthOrToken ? t('High') : t('Urgent'), - priority: isEthOrToken ? t('Average') : t('Priority'), - normal: isEthOrToken ? t('Low') : t('Normal'), - economy: t('Economy'), - superEconomy: t('Super Economy'), - custom: t('Custom'), - }; +export const GetFeeOptions = (currencyAbbreviation: string): FeeOptions => { + const isEthOrToken = + currencyAbbreviation === 'eth' || IsERCToken(currencyAbbreviation); + return { + urgent: isEthOrToken ? t('High') : t('Urgent'), + priority: isEthOrToken ? t('Average') : t('Priority'), + normal: isEthOrToken ? t('Low') : t('Normal'), + economy: t('Economy'), + superEconomy: t('Super Economy'), + custom: t('Custom'), }; +}; const removeLowFeeLevels = (feeLevels: Fee[]) => { const removeLevels = ['economy', 'superEconomy']; @@ -50,12 +47,12 @@ export const getFeeRatePerKb = ({ feeLevel: string; }): Promise => { return new Promise(async (resolve, reject) => { - const {credentials} = wallet; + const {network} = wallet; try { // get fee levels const feeLevels = await getFeeLevels({ wallet, - network: credentials.network, + network: network, }); // find fee level based on setting @@ -82,7 +79,7 @@ export const getFeeLevels = ({ return new Promise(async (resolve, reject) => { try { wallet.getFeeLevels( - wallet.currencyAbbreviation.toUpperCase(), + wallet.chain, network, (err: Error, feeLevels: Fee[]) => { if (err) { diff --git a/src/store/wallet/effects/import/import.ts b/src/store/wallet/effects/import/import.ts index 6dafefd6d7..9798782588 100644 --- a/src/store/wallet/effects/import/import.ts +++ b/src/store/wallet/effects/import/import.ts @@ -17,6 +17,7 @@ import { getReadOnlyKey, isMatch, isMatchedWallet, + mapAbbreviationAndName, } from '../../utils/wallet'; import {LogActions} from '../../../../store/log'; import { @@ -32,7 +33,7 @@ import { updateDeferredImport, updatePortfolioBalance, } from '../../wallet.actions'; -import {BitpaySupportedTokenOpts} from '../../../../constants/tokens'; +import {BitpaySupportedEthereumTokenOpts} from '../../../../constants/tokens'; import {Platform} from 'react-native'; import RNFS from 'react-native-fs'; import { @@ -95,6 +96,7 @@ import { import {t} from 'i18next'; import {sleep} from '../../../../utils/helper-methods'; import {backupRedirect} from '../../../../navigation/wallet/screens/Backup'; +import {SUPPORTED_COINS} from '../../../../constants/currencies'; const BWC = BwcProvider.getInstance(); @@ -674,9 +676,11 @@ export const migrateKeyAndWallets = const state = getState(); const {backupComplete, keyName} = migrationData.keyConfig; const tokenOpts = { - ...BitpaySupportedTokenOpts, - ...state.WALLET.tokenOptions, - ...state.WALLET.customTokenOptions, + eth: { + ...BitpaySupportedEthereumTokenOpts, + ...state.WALLET.tokenOptions, + ...state.WALLET.customTokenOptions, + }, }; const keyObj = merge(migrationData.key, { methods: BWC.createKey({ @@ -706,14 +710,26 @@ export const migrateKeyAndWallets = } catch (e) { // not found. Continue anyway } + + const {currencyAbbreviation, currencyName} = dispatch( + mapAbbreviationAndName( + walletObj.credentials.coin, + walletObj.credentials.chain, + ), + ); + wallets.push( merge( walletObj, - dispatch( - buildWalletObj( - {...walletObj.credentials, hideBalance, hideWallet}, - tokenOpts, - ), + buildWalletObj( + { + ...walletObj.credentials, + hideBalance, + hideWallet, + currencyAbbreviation, + currencyName, + }, + tokenOpts, ), ), ); @@ -885,9 +901,11 @@ export const startImportMnemonic = }, } = getState(); const tokenOpts = { - ...BitpaySupportedTokenOpts, - ...WALLET.tokenOptions, - ...WALLET.customTokenOptions, + eth: { + ...BitpaySupportedEthereumTokenOpts, + ...WALLET.tokenOptions, + ...WALLET.customTokenOptions, + }, }; const {words, xPrivKey} = importData; opts.words = normalizeMnemonic(words); @@ -928,9 +946,18 @@ export const startImportMnemonic = }; dispatch(subscribeEmailNotifications(wallet, prefs)); } + const {currencyAbbreviation, currencyName} = dispatch( + mapAbbreviationAndName( + wallet.credentials.coin, + wallet.credentials.chain, + ), + ); return merge( wallet, - dispatch(buildWalletObj(wallet.credentials, tokenOpts)), + buildWalletObj( + {...wallet.credentials, currencyAbbreviation, currencyName}, + tokenOpts, + ), ); }), backupComplete: true, @@ -964,9 +991,11 @@ export const startImportFile = }, } = getState(); const tokenOpts = { - ...BitpaySupportedTokenOpts, - ...WALLET.tokenOptions, - ...WALLET.customTokenOptions, + eth: { + ...BitpaySupportedEthereumTokenOpts, + ...WALLET.tokenOptions, + ...WALLET.customTokenOptions, + }, }; let {key: _key, wallet} = await createKeyAndCredentialsWithFile( decryptBackupText, @@ -1018,9 +1047,18 @@ export const startImportFile = }; dispatch(subscribeEmailNotifications(wallet, prefs)); } + const {currencyAbbreviation, currencyName} = dispatch( + mapAbbreviationAndName( + wallet.credentials.coin, + wallet.credentials.chain, + ), + ); return merge( wallet, - dispatch(buildWalletObj(wallet.credentials, tokenOpts)), + buildWalletObj( + {...wallet.credentials, currencyAbbreviation, currencyName}, + tokenOpts, + ), ); }), backupComplete: true, @@ -1058,9 +1096,11 @@ export const startImportWithDerivationPath = }, } = getState(); const tokenOpts = { - ...BitpaySupportedTokenOpts, - ...WALLET.tokenOptions, - ...WALLET.customTokenOptions, + eth: { + ...BitpaySupportedEthereumTokenOpts, + ...WALLET.tokenOptions, + ...WALLET.customTokenOptions, + }, }; const {words, xPrivKey} = importData; opts.mnemonic = words; @@ -1105,12 +1145,21 @@ export const startImportWithDerivationPath = }; dispatch(subscribeEmailNotifications(wallet, prefs)); } + const {currencyAbbreviation, currencyName} = dispatch( + mapAbbreviationAndName( + wallet.credentials.coin, + wallet.credentials.chain, + ), + ); const key = buildKeyObj({ key: _key, wallets: [ merge( wallet, - dispatch(buildWalletObj(wallet.credentials, tokenOpts)), + buildWalletObj( + {...wallet.credentials, currencyAbbreviation, currencyName}, + tokenOpts, + ), ), ], backupComplete: true, diff --git a/src/store/wallet/effects/join-multisig/join-multisig.ts b/src/store/wallet/effects/join-multisig/join-multisig.ts index 74096dfe9e..a5d37ba1d6 100644 --- a/src/store/wallet/effects/join-multisig/join-multisig.ts +++ b/src/store/wallet/effects/join-multisig/join-multisig.ts @@ -1,7 +1,11 @@ import {Effect} from '../../../index'; import {BwcProvider} from '../../../../lib/bwc'; import merge from 'lodash.merge'; -import {buildKeyObj, buildWalletObj} from '../../utils/wallet'; +import { + buildKeyObj, + buildWalletObj, + mapAbbreviationAndName, +} from '../../utils/wallet'; import {successCreateKey, successAddWallet} from '../../wallet.actions'; import API from 'bitcore-wallet-client/ts_build'; import {Key, KeyMethods, KeyOptions, Wallet} from '../../wallet.models'; @@ -59,11 +63,21 @@ export const startJoinMultisig = }; dispatch(subscribeEmailNotifications(_wallet, prefs)); } + const {currencyAbbreviation, currencyName} = dispatch( + mapAbbreviationAndName( + _wallet.credentials.coin, + _wallet.credentials.chain, + ), + ); // build out app specific props const wallet = merge( _wallet, - dispatch(buildWalletObj(_wallet.credentials)), + buildWalletObj({ + ..._wallet.credentials, + currencyAbbreviation, + currencyName, + }), ) as Wallet; const key = buildKeyObj({key: _key, wallets: [wallet]}); @@ -125,10 +139,21 @@ export const addWalletJoinMultisig = dispatch(subscribeEmailNotifications(newWallet, prefs)); } + const {currencyAbbreviation, currencyName} = dispatch( + mapAbbreviationAndName( + newWallet.credentials.coin, + newWallet.credentials.chain, + ), + ); + key.wallets.push( merge( newWallet, - dispatch(buildWalletObj(newWallet.credentials)), + buildWalletObj({ + ...newWallet.credentials, + currencyAbbreviation, + currencyName, + }), ) as Wallet, ); diff --git a/src/store/wallet/effects/paypro/paypro.ts b/src/store/wallet/effects/paypro/paypro.ts index 2abf2a3475..6aafa67982 100644 --- a/src/store/wallet/effects/paypro/paypro.ts +++ b/src/store/wallet/effects/paypro/paypro.ts @@ -1,5 +1,5 @@ import {BwcProvider} from '../../../../lib/bwc'; -import {Currencies} from '../../../../constants/currencies'; +import {BitpaySupportedCoins} from '../../../../constants/currencies'; const BWC = BwcProvider.getInstance(); export interface PayProPaymentOption { @@ -45,15 +45,15 @@ export const GetPayProOptions = async ( export const GetPayProDetails = async (params: { paymentUrl: string; coin: string; + chain: string; payload?: {address?: string}; attempt?: number; }): Promise => { - let {paymentUrl, coin, payload, attempt = 1} = params; + let {paymentUrl, coin, chain, payload, attempt = 1} = params; const bwc = BWC.getPayProV2(); - const chain = Currencies[coin.toLowerCase()].chain; const options: any = { paymentUrl, - chain, + chain: chain.toUpperCase(), currency: coin.toUpperCase(), payload, }; @@ -82,11 +82,13 @@ export const HandlePayPro = async ({ payProOptions, url, coin, + chain, }: { payProDetails: any; payProOptions?: PayProOptions; url: string; coin: string; + chain: string; }) => { if (!payProDetails) { return; @@ -95,7 +97,8 @@ export const HandlePayPro = async ({ let requiredFeeRate; if (payProDetails.requiredFeeRate) { - requiredFeeRate = !Currencies[coin.toLowerCase()].properties.isUtxo + requiredFeeRate = !BitpaySupportedCoins[chain.toLowerCase()].properties + .isUtxo ? parseInt((payProDetails.requiredFeeRate * 1.1).toFixed(0), 10) // Workaround to avoid gas price supplied is lower than requested error : Math.ceil(payProDetails.requiredFeeRate * 1000); } diff --git a/src/store/wallet/effects/rates/rates.ts b/src/store/wallet/effects/rates/rates.ts index 42bfcba952..edc9ab26cb 100644 --- a/src/store/wallet/effects/rates/rates.ts +++ b/src/store/wallet/effects/rates/rates.ts @@ -27,7 +27,7 @@ import moment from 'moment'; import {addAltCurrencyList} from '../../../app/app.actions'; import {AltCurrenciesRowProps} from '../../../../components/list/AltCurrenciesRow'; import {LogActions} from '../../../log'; -import {BitpaySupportedTokenOptsByAddress} from '../../../../constants/tokens'; +import {BitpaySupportedEthereumTokenOptsByAddress} from '../../../../constants/tokens'; export const getPriceHistory = (defaultAltCurrencyIsoCode: string): Effect => @@ -206,7 +206,7 @@ export const getTokenRates = } = getState(); const tokens = { - ...BitpaySupportedTokenOptsByAddress, + ...BitpaySupportedEthereumTokenOptsByAddress, ...tokenOptionsByAddress, ...customTokenOptionsByAddress, }; diff --git a/src/store/wallet/effects/send/send.ts b/src/store/wallet/effects/send/send.ts index b4ca2da596..1913507e62 100644 --- a/src/store/wallet/effects/send/send.ts +++ b/src/store/wallet/effects/send/send.ts @@ -46,7 +46,7 @@ import { showBottomNotificationModal, showDecryptPasswordModal, } from '../../../app/app.actions'; -import {GetPrecision, GetChain, IsERCToken} from '../../utils/currency'; +import {GetPrecision, IsERCToken} from '../../utils/currency'; import {CommonActions} from '@react-navigation/native'; import {BwcProvider} from '../../../../lib/bwc'; import {createWalletAddress, ToCashAddress} from '../address/address'; @@ -93,14 +93,11 @@ export const createProposalAndBuildTxDetails = payProDetails, } = tx; - let {credentials, currencyAbbreviation} = wallet; - const {token} = credentials; + let {credentials, currencyAbbreviation, network} = wallet; + const {token, chain} = credentials; const formattedAmount = dispatch( - ParseAmount(amount, currencyAbbreviation), + ParseAmount(amount, currencyAbbreviation, chain), ); - - const chain = dispatch(GetChain(currencyAbbreviation)).toLowerCase(); - const { WALLET: { feeLevel: cachedFeeLevel, @@ -169,10 +166,11 @@ export const createProposalAndBuildTxDetails = ...tx, context, currency: currencyAbbreviation, + chain, tokenAddress: token ? token.address : null, toAddress: recipient.address, amount: formattedAmount.amountSat, - network: credentials.network, + network, payProUrl, feePerKb, feeLevel, @@ -224,10 +222,10 @@ const setEthAddressNonce = try { const { coin: currencyAbbreviation, + chain, keyId, walletId, } = wallet.credentials; - const chain = dispatch(GetChain(currencyAbbreviation)).toLowerCase(); if (chain !== 'eth' || tx.context === 'speedupEth') { return resolve(); @@ -237,7 +235,7 @@ const setEthAddressNonce = let nonceWallet: Wallet; // linked eth wallet could have pendings txs from different tokens // this means we need to check pending txs from the linked wallet if is ERC20Token instead of the sending wallet - if (dispatch(IsERCToken(wallet.currencyAbbreviation))) { + if (IsERCToken(currencyAbbreviation)) { const {WALLET} = getState(); const key = WALLET.keys[keyId]; const linkedWallet = key.wallets.find(({tokens}) => @@ -337,9 +335,9 @@ export const getNonce = ( }; export const getInvoiceEffectiveRate = - (invoice: Invoice, coin: string): Effect => + (invoice: Invoice, coin: string, chain: string): Effect => dispatch => { - const precision = dispatch(GetPrecision(coin.toLowerCase())); + const precision = dispatch(GetPrecision(coin, chain)); return ( precision && invoice.price / @@ -371,19 +369,21 @@ export const buildTxDetails = feeLevel?: string; }): Effect => dispatch => { - const {coin, fee, gasPrice, gasLimit, nonce, destinationTag} = proposal || { - coin: invoice!.buyerProvidedInfo!.selectedTransactionCurrency!.toLowerCase(), - fee: 0, - }; + const {coin, fee, gasPrice, gasLimit, nonce, destinationTag, chain} = + proposal || { + coin: invoice!.buyerProvidedInfo!.selectedTransactionCurrency!.toLowerCase(), + chain: + invoice!.buyerProvidedInfo!.selectedTransactionCurrency!.toLowerCase(), // TODO POLYGON + fee: 0, + }; const invoiceCoin = GetInvoiceCurrency(coin).toLowerCase(); let {amount} = proposal || { amount: invoice!.paymentTotals[invoiceCoin.toUpperCase()], }; const effectiveRate = - invoice && dispatch(getInvoiceEffectiveRate(invoice, invoiceCoin)); + invoice && dispatch(getInvoiceEffectiveRate(invoice, invoiceCoin, chain)); const networkCost = invoice?.minerFees[invoiceCoin.toUpperCase()]?.totalFee; - const chain = dispatch(GetChain(coin)).toLowerCase(); // always use chain for fee values - const isERC20 = dispatch(IsERCToken(coin)); + const isERC20 = IsERCToken(coin); const effectiveRateForFee = isERC20 ? undefined : effectiveRate; // always use chain rates for fee values if (context === 'paypro') { @@ -402,16 +402,18 @@ export const buildTxDetails = recipientAddress: address && formatCryptoAddress(address), img: wallet.img, recipientFullAddress: address, + recipientChain: chain, }, fee: { feeLevel, - cryptoAmount: dispatch(FormatAmountStr(chain, fee)), + cryptoAmount: dispatch(FormatAmountStr(chain, chain, fee)), fiatAmount: formatFiatAmount( dispatch( toFiat( fee, defaultAltCurrencyIsoCode, chain, + chain, rates, effectiveRateForFee, ), @@ -423,13 +425,14 @@ export const buildTxDetails = }, ...(networkCost && { networkCost: { - cryptoAmount: dispatch(FormatAmountStr(chain, networkCost)), + cryptoAmount: dispatch(FormatAmountStr(chain, chain, networkCost)), fiatAmount: formatFiatAmount( dispatch( toFiat( networkCost, defaultAltCurrencyIsoCode, chain, + chain, rates, effectiveRateForFee, ), @@ -441,15 +444,17 @@ export const buildTxDetails = sendingFrom: { walletName: wallet.walletName || wallet.credentials.walletName, img: wallet.img, + badgeImg: wallet.badgeImg, }, subTotal: { - cryptoAmount: dispatch(FormatAmountStr(coin, amount)), + cryptoAmount: dispatch(FormatAmountStr(coin, chain, amount)), fiatAmount: formatFiatAmount( dispatch( toFiat( amount, defaultAltCurrencyIsoCode, coin, + chain, rates, effectiveRate, ), @@ -459,16 +464,17 @@ export const buildTxDetails = }, total: { cryptoAmount: isERC20 - ? `${dispatch(FormatAmountStr(coin, amount))} + ${dispatch( - FormatAmountStr(chain, fee), + ? `${dispatch(FormatAmountStr(coin, chain, amount))} + ${dispatch( + FormatAmountStr(chain, chain, fee), )}` - : dispatch(FormatAmountStr(coin, amount + fee)), + : dispatch(FormatAmountStr(coin, chain, amount + fee)), fiatAmount: formatFiatAmount( dispatch( toFiat( amount, defaultAltCurrencyIsoCode, coin, + chain, rates, effectiveRate, ), @@ -478,6 +484,7 @@ export const buildTxDetails = fee, defaultAltCurrencyIsoCode, chain, + chain, rates, effectiveRateForFee, ), @@ -502,6 +509,7 @@ const buildTransactionProposal = try { const { currency, + chain, feeLevel, feePerKb, invoiceID, @@ -526,7 +534,6 @@ const buildTransactionProposal = } } - const chain = dispatch(GetChain(currency!)).toLowerCase(); // base tx const txp: Partial = { coin: currency, @@ -571,7 +578,8 @@ const buildTransactionProposal = if (sendMaxInfo.utxosBelowFee > 0) { const amountBelowFeeStr = sendMaxInfo.amountBelowFee / - dispatch(GetPrecision(currencyAbbreviation))!.unitToSatoshi!; + dispatch(GetPrecision(currencyAbbreviation, chain!))! + .unitToSatoshi!; const message = t( 'A total of were excluded. These funds come from UTXOs smaller than the network fee provided', { @@ -585,7 +593,8 @@ const buildTransactionProposal = if (sendMaxInfo.utxosAboveMaxSize > 0) { const amountAboveMaxSizeStr = sendMaxInfo.amountAboveMaxSize / - dispatch(GetPrecision(currencyAbbreviation))!.unitToSatoshi; + dispatch(GetPrecision(currencyAbbreviation, chain!))! + .unitToSatoshi; const message = t( 'A total of were excluded. The maximum size allowed for a transaction was exceeded.', { @@ -600,7 +609,7 @@ const buildTransactionProposal = // send max if (sendMax && wallet) { - if (dispatch(IsERCToken(wallet.currencyAbbreviation))) { + if (IsERCToken(wallet.currencyAbbreviation)) { txp.amount = tx.amount = wallet.balance.satAvailable; } else { const sendMaxInfo = await getSendMaxInfo({ @@ -644,7 +653,7 @@ const buildTransactionProposal = if (recipientList) { recipientList.forEach(r => { const formattedAmount = dispatch( - ParseAmount(r.amount || 0, chain), + ParseAmount(r.amount || 0, chain!, chain!), ); txp.outputs?.push({ toAddress: @@ -691,7 +700,7 @@ const buildTransactionProposal = if (recipientList) { recipientList.forEach(r => { const formattedAmount = dispatch( - ParseAmount(r.amount || 0, chain), + ParseAmount(r.amount || 0, chain!, chain!), ); txp.outputs?.push({ toAddress: r.address, @@ -796,7 +805,6 @@ export const startSendPayment = null, ); } catch (err) { - console.log(err); reject(err); } }); @@ -1135,8 +1143,9 @@ export const handleCreateTxProposalError = if ( !getState().WALLET.useUnconfirmedFunds && wallet.balance.sat >= - dispatch(ParseAmount(amount, wallet.currencyAbbreviation)) - .amountSat + + dispatch( + ParseAmount(amount, wallet.currencyAbbreviation, wallet.chain), + ).amountSat + feeRatePerKb ) { return CustomErrorMessage({ @@ -1185,12 +1194,14 @@ export const createPayProTxProposal = const payProDetails = await GetPayProDetails({ paymentUrl, coin: wallet!.currencyAbbreviation, + chain: wallet!.chain, }); const confirmScreenParams = await HandlePayPro({ payProDetails, payProOptions, url: paymentUrl, coin: wallet!.currencyAbbreviation, + chain: wallet!.chain, }); const { toAddress: address, @@ -1201,7 +1212,7 @@ export const createPayProTxProposal = amount, } = confirmScreenParams!; const {unitToSatoshi} = dispatch( - GetPrecision(wallet.currencyAbbreviation), + GetPrecision(wallet.currencyAbbreviation, wallet.chain), ) || { unitToSatoshi: 100000000, }; @@ -1251,18 +1262,26 @@ export const buildEthERCTokenSpeedupTx = return new Promise((resolve, reject) => { try { const { - credentials: {coin, walletName, walletId, network}, + currencyAbbreviation, + network, + chain, + credentials: {walletName, walletId}, keyId, } = wallet; const {customData, addressTo, nonce, data, gasLimit} = transaction; - const amount = Number(dispatch(FormatAmount(coin, transaction.amount))); + const amount = Number( + dispatch( + FormatAmount(currencyAbbreviation, chain, transaction.amount), + ), + ); const recipient = { type: 'wallet', name: customData ? customData.toWalletName : walletName, walletId, keyId, address: addressTo, + chain, }; return resolve({ @@ -1270,7 +1289,7 @@ export const buildEthERCTokenSpeedupTx = amount, recipient, network, - currency: coin, + currency: currencyAbbreviation, toAddress: addressTo, nonce, data, @@ -1293,7 +1312,9 @@ export const buildBtcSpeedupTx = const {data, customData, txid, size} = tx; const { - credentials: {coin, walletName, walletId, network}, + currencyAbbreviation, + network, + credentials: {walletName, walletId}, keyId, } = wallet; @@ -1319,7 +1340,7 @@ export const buildBtcSpeedupTx = return reject('NoInput'); } - const {unitToSatoshi} = dispatch(GetPrecision('btc')) || { + const {unitToSatoshi} = dispatch(GetPrecision('btc', 'btc')) || { unitToSatoshi: 100000000, }; @@ -1332,7 +1353,7 @@ export const buildBtcSpeedupTx = name: walletName, toAddress: address, network, - currency: coin, + currency: currencyAbbreviation, amount, recipient, inputs, diff --git a/src/store/wallet/effects/status/status.ts b/src/store/wallet/effects/status/status.ts index df72aec0a1..11962cbbb3 100644 --- a/src/store/wallet/effects/status/status.ts +++ b/src/store/wallet/effects/status/status.ts @@ -164,11 +164,7 @@ export const startUpdateWalletStatus = RATE: {rates, lastDayRates}, } = getState(); - const { - id, - currencyAbbreviation, - credentials: {network}, - } = wallet; + const {id, currencyAbbreviation, network} = wallet; if ( !isCacheKeyStale(balanceCacheKey[id], BALANCE_CACHE_DURATION) && @@ -209,6 +205,7 @@ export const startUpdateWalletStatus = sat, defaultAltCurrency.isoCode, wallets[index].currencyAbbreviation, + wallets[index].chain, rates, ), ), @@ -226,6 +223,7 @@ export const startUpdateWalletStatus = sat, defaultAltCurrency.isoCode, wallets[index].currencyAbbreviation, + wallets[index].chain, lastDayRates, ), ), @@ -693,7 +691,7 @@ const updateWalletStatus = const buildBalance = ({wallet, status}: {wallet: Wallet; status: Status}): Effect => (dispatch, getState) => { - const {currencyAbbreviation} = wallet; + const {currencyAbbreviation, chain} = wallet; const { WALLET: {useUnconfirmedFunds}, @@ -735,19 +733,23 @@ const buildBalance = satSpendable: spendableAmount, satPending: pendingAmount, crypto: dispatch( - FormatAmount(currencyAbbreviation, Number(satTotalAmount)), + FormatAmount(currencyAbbreviation, chain, Number(satTotalAmount)), ), cryptoLocked: dispatch( - FormatAmount(currencyAbbreviation, Number(satLockedAmount)), + FormatAmount(currencyAbbreviation, chain, Number(satLockedAmount)), ), cryptoConfirmedLocked: dispatch( - FormatAmount(currencyAbbreviation, Number(lockedConfirmedAmount)), + FormatAmount( + currencyAbbreviation, + chain, + Number(lockedConfirmedAmount), + ), ), cryptoSpendable: dispatch( - FormatAmount(currencyAbbreviation, Number(spendableAmount)), + FormatAmount(currencyAbbreviation, chain, Number(spendableAmount)), ), cryptoPending: dispatch( - FormatAmount(currencyAbbreviation, Number(pendingAmount)), + FormatAmount(currencyAbbreviation, chain, Number(pendingAmount)), ), }; }; @@ -767,11 +769,7 @@ const buildFiatBalance = cryptoBalance: CryptoBalance; }): Effect => dispatch => { - const { - currencyAbbreviation, - credentials: {network}, - hideWallet, - } = wallet; + const {currencyAbbreviation, network, chain, hideWallet} = wallet; let {sat, satLocked, satConfirmedLocked, satSpendable, satPending} = cryptoBalance; @@ -779,7 +777,13 @@ const buildFiatBalance = return { fiat: convertToFiat( dispatch( - toFiat(sat, defaultAltCurrencyIsoCode, currencyAbbreviation, rates), + toFiat( + sat, + defaultAltCurrencyIsoCode, + currencyAbbreviation, + chain, + rates, + ), ), hideWallet, network, @@ -790,6 +794,7 @@ const buildFiatBalance = satLocked, defaultAltCurrencyIsoCode, currencyAbbreviation, + chain, rates, ), ), @@ -802,6 +807,7 @@ const buildFiatBalance = satConfirmedLocked, defaultAltCurrencyIsoCode, currencyAbbreviation, + chain, rates, ), ), @@ -814,6 +820,7 @@ const buildFiatBalance = satSpendable, defaultAltCurrencyIsoCode, currencyAbbreviation, + chain, rates, ), ), @@ -826,6 +833,7 @@ const buildFiatBalance = satPending, defaultAltCurrencyIsoCode, currencyAbbreviation, + chain, rates, ), ), @@ -838,6 +846,7 @@ const buildFiatBalance = sat, defaultAltCurrencyIsoCode, currencyAbbreviation, + chain, lastDayRates, ), ), @@ -932,16 +941,17 @@ export const startFormatBalanceAllWalletsForKey = const { currencyAbbreviation, balance: cachedBalance, - credentials: {network}, + network, + chain, hideWallet, } = wallet; try { const {sat, satLocked} = cachedBalance; const newBalance = { - crypto: dispatch(FormatAmount(currencyAbbreviation, sat)), + crypto: dispatch(FormatAmount(currencyAbbreviation, chain, sat)), cryptoLocked: dispatch( - FormatAmount(currencyAbbreviation, satLocked), + FormatAmount(currencyAbbreviation, chain, satLocked), ), fiat: convertToFiat( dispatch( @@ -949,6 +959,7 @@ export const startFormatBalanceAllWalletsForKey = sat, defaultAltCurrencyIsoCode, currencyAbbreviation, + chain, rates, ), ), @@ -961,6 +972,7 @@ export const startFormatBalanceAllWalletsForKey = satLocked, defaultAltCurrencyIsoCode, currencyAbbreviation, + chain, rates, ), ), @@ -973,6 +985,7 @@ export const startFormatBalanceAllWalletsForKey = sat, defaultAltCurrencyIsoCode, currencyAbbreviation, + chain, lastDayRates, ), ), diff --git a/src/store/wallet/effects/transactions/transactions.ts b/src/store/wallet/effects/transactions/transactions.ts index e54a660b01..66a58957eb 100644 --- a/src/store/wallet/effects/transactions/transactions.ts +++ b/src/store/wallet/effects/transactions/transactions.ts @@ -7,12 +7,7 @@ import { DEFAULT_RBF_SEQ_NUMBER, SAFE_CONFIRMATIONS, } from '../../../../constants/wallet'; -import { - GetChain, - IsCustomERCToken, - IsERCToken, - IsUtxoCoin, -} from '../../utils/currency'; +import {IsCustomERCToken, IsERCToken, IsUtxoCoin} from '../../utils/currency'; import {ToAddress, ToLtcAddress} from '../address/address'; import { IsDateInCurrentMonth, @@ -39,13 +34,11 @@ const Errors = BWC.getErrors(); export const TX_HISTORY_LIMIT = 25; const GetCoinsForTx = (wallet: Wallet, txId: string): Promise => { - const { - credentials: {coin, network}, - } = wallet; + const {currencyAbbreviation, network} = wallet; return new Promise((resolve, reject) => { wallet.getCoinsForTx( { - coin, + coin: currencyAbbreviation, network, txId, }, @@ -85,10 +78,10 @@ export const ProcessPendingTxps = (txps: TransactionProposal[], wallet: any): Effect => dispatch => { const now = Math.floor(Date.now() / 1000); - const {currencyAbbreviation} = wallet; + const {currencyAbbreviation, chain} = wallet; txps.forEach((tx: TransactionProposal) => { - tx = dispatch(ProcessTx(currencyAbbreviation, tx)); + tx = dispatch(ProcessTx(currencyAbbreviation, chain, tx)); // no future transactions... if (tx.createdOn > now) { @@ -124,6 +117,7 @@ export const ProcessPendingTxps = const ProcessTx = ( currencyAbbreviation: string, + chain: string, tx: TransactionProposal, ): Effect => dispatch => { @@ -143,7 +137,7 @@ const ProcessTx = } tx.amount = tx.outputs.reduce((total: number, o: any) => { o.amountStr = dispatch( - FormatAmountStr(currencyAbbreviation, o.amount), + FormatAmountStr(currencyAbbreviation, chain, o.amount), ); return total + o.amount; }, 0); @@ -172,14 +166,14 @@ const ProcessTx = ]; } - tx.amountStr = dispatch(FormatAmountStr(currencyAbbreviation, tx.amount)); - - const chain = dispatch(GetChain(currencyAbbreviation)).toLowerCase(); + tx.amountStr = dispatch( + FormatAmountStr(currencyAbbreviation, chain, tx.amount), + ); tx.feeStr = tx.fee - ? dispatch(FormatAmountStr(chain, tx.fee)) + ? dispatch(FormatAmountStr(chain, chain, tx.fee)) : tx.fees - ? dispatch(FormatAmountStr(chain, tx.fees)) + ? dispatch(FormatAmountStr(chain, chain, tx.fees)) : 'N/A'; if (tx.amountStr) { @@ -204,10 +198,10 @@ const ProcessNewTxs = const now = Math.floor(Date.now() / 1000); const txHistoryUnique: any = {}; const ret = []; - const {currencyAbbreviation} = wallet; + const {currencyAbbreviation, chain} = wallet; for (let tx of txs) { - tx = dispatch(ProcessTx(currencyAbbreviation, tx)); + tx = dispatch(ProcessTx(currencyAbbreviation, chain, tx)); // no future transactions... if (tx.time > now) { @@ -477,7 +471,7 @@ export const GetTransactionHistory = let transactionHistory; // linked eth wallet could have pendings txs from different tokens // this means we need to check pending txs from the linked wallet if is ERC20Token instead of the sending wallet - if (dispatch(IsERCToken(wallet.currencyAbbreviation))) { + if (IsERCToken(wallet.currencyAbbreviation)) { const {WALLET} = getState(); const key = WALLET.keys[keyId]; const linkedWallet = key.wallets.find(({tokens}) => @@ -761,39 +755,37 @@ export const BuildUiFriendlyList = ( }); }; -export const CanSpeedupTx = - (tx: any, currencyAbbreviation: string): Effect => - dispatch => { - const isERC20Wallet = dispatch(IsERCToken(currencyAbbreviation)); - const isEthWallet = currencyAbbreviation === 'eth'; - - if (currencyAbbreviation !== 'btc' && isEthWallet && isERC20Wallet) { - return false; - } +export const CanSpeedupTx = ( + tx: any, + currencyAbbreviation: string, +): boolean => { + const isERC20Wallet = IsERCToken(currencyAbbreviation); + const isEthWallet = currencyAbbreviation === 'eth'; - const {action, abiType, confirmations} = tx || {}; - const isERC20Transfer = abiType?.name === 'transfer'; - const isUnconfirmed = !confirmations || confirmations === 0; + if (currencyAbbreviation !== 'btc' && isEthWallet && isERC20Wallet) { + return false; + } - if ( - (isEthWallet && !isERC20Transfer) || - (isERC20Wallet && isERC20Transfer) - ) { - // Can speed up the eth/erc20 tx instantly - return isUnconfirmed && (IsSent(action) || IsMoved(action)); - } else { - const currentTime = moment(); - const txTime = moment(tx.time * 1000); - - // Can speed up the btc tx after 1 hours without confirming - return ( - currentTime.diff(txTime, 'hours') >= 1 && - isUnconfirmed && - IsReceived(action) && - currencyAbbreviation === 'btc' - ); - } - }; + const {action, abiType, confirmations} = tx || {}; + const isERC20Transfer = abiType?.name === 'transfer'; + const isUnconfirmed = !confirmations || confirmations === 0; + + if ((isEthWallet && !isERC20Transfer) || (isERC20Wallet && isERC20Transfer)) { + // Can speed up the eth/erc20 tx instantly + return isUnconfirmed && (IsSent(action) || IsMoved(action)); + } else { + const currentTime = moment(); + const txTime = moment(tx.time * 1000); + + // Can speed up the btc tx after 1 hours without confirming + return ( + currentTime.diff(txTime, 'hours') >= 1 && + isUnconfirmed && + IsReceived(action) && + currencyAbbreviation === 'btc' + ); + } +}; ///////////////////////////////////////// Transaction Details //////////////////////////////////////////////// @@ -830,7 +822,8 @@ export const buildTransactionDetails = async dispatch => { return new Promise(async (resolve, reject) => { try { - const _transaction = {...transaction}; + const {coin, chain} = wallet.credentials; + const _transaction = {...transaction, coin, chain}; const { fees, fee, @@ -841,8 +834,6 @@ export const buildTransactionDetails = time, hasMultiplesOutputs, } = transaction; - const {coin: currency} = wallet.credentials; - const chain = dispatch(GetChain(currency)).toLowerCase(); const _fee = fees || fee; const alternativeCurrency = defaultAltCurrencyIsoCode; @@ -850,7 +841,7 @@ export const buildTransactionDetails = const rates = await dispatch(startGetRates({})); _transaction.feeFiatStr = formatFiatAmount( - dispatch(toFiat(_fee, alternativeCurrency, chain, rates)), + dispatch(toFiat(_fee, alternativeCurrency, chain, chain, rates)), alternativeCurrency, ); @@ -859,7 +850,7 @@ export const buildTransactionDetails = _transaction.outputs = _transaction.outputs.map((o: any) => { o.alternativeAmountStr = formatFiatAmount( dispatch( - toFiat(o.amount, alternativeCurrency, currency, rates), + toFiat(o.amount, alternativeCurrency, coin, chain, rates), ), alternativeCurrency, ); @@ -868,7 +859,7 @@ export const buildTransactionDetails = } } - if (IsUtxoCoin(currency)) { + if (IsUtxoCoin(coin)) { _transaction.feeRateStr = ((_fee / (amount + _fee)) * 100).toFixed(2) + '%'; try { @@ -891,7 +882,7 @@ export const buildTransactionDetails = const historicFiatRate = await getHistoricFiatRate( alternativeCurrency, - currency, + coin, (time * 1000).toString(), ); _transaction.fiatRateStr = dispatch( @@ -899,8 +890,9 @@ export const buildTransactionDetails = historicFiatRate, transaction, rates, - currency, + coin, alternativeCurrency, + chain, ), ); @@ -918,6 +910,7 @@ const UpdateFiatRate = rates: Rates = {}, currency: string, alternativeCurrency: string, + chain: string, ): Effect => dispatch => { const {amountValueStr, amount} = transaction; @@ -935,7 +928,7 @@ const UpdateFiatRate = } else { // Get current fiat value when historic rates are unavailable fiatRateStr = dispatch( - toFiat(amount, alternativeCurrency, currency, rates), + toFiat(amount, alternativeCurrency, currency, chain, rates), ); fiatRateStr = formatFiatAmount(fiatRateStr, alternativeCurrency) + diff --git a/src/store/wallet/utils/currency.ts b/src/store/wallet/utils/currency.ts index 12209cbf11..8918ecec23 100644 --- a/src/store/wallet/utils/currency.ts +++ b/src/store/wallet/utils/currency.ts @@ -1,25 +1,45 @@ import {Effect} from '../..'; -import {Currencies} from '../../../constants/currencies'; +import { + BitpaySupportedCoins, + BitpaySupportedEthereumTokens, + SUPPORTED_COINS, +} from '../../../constants/currencies'; export const GetProtocolPrefix = - (currencyAbbreviation: string, network: string = 'livenet'): Effect => + ( + currencyAbbreviation: string, + network: string = 'livenet', + chain: string, + ): Effect => (dispatch, getState) => { const { WALLET: {tokenData, customTokenData}, } = getState(); - const tokens = {...tokenData, ...customTokenData}; + + let tokens; const currency = currencyAbbreviation.toLowerCase(); - return ( - // @ts-ignore - Currencies[currency]?.paymentInfo.protocolPrefix[network] || - // @ts-ignore - tokens[currency]?.paymentInfo.protocolPrefix[network] - ); + if (IsERCToken(currencyAbbreviation)) { + switch (chain) { + case 'eth': + tokens = {...tokenData, ...customTokenData}; + return ( + // @ts-ignore + BitpaySupportedEthereumTokens[currency]?.paymentInfo.protocolPrefix[ + network + ] || + // @ts-ignore + tokens[currency]?.paymentInfo.protocolPrefix[network] + ); + } + } + // @ts-ignore + return BitpaySupportedCoins[currency]?.paymentInfo.protocolPrefix[network]; }; export const GetPrecision = ( currencyAbbreviation: string, + chain: string, ): Effect< | { unitName: string; @@ -33,9 +53,20 @@ export const GetPrecision = const { WALLET: {tokenData, customTokenData}, } = getState(); - const tokens = {...tokenData, ...customTokenData}; + + let tokens; const currency = currencyAbbreviation.toLowerCase(); - return Currencies[currency]?.unitInfo || tokens[currency]?.unitInfo; + if (IsERCToken(currencyAbbreviation)) { + switch (chain) { + case 'eth': + tokens = {...tokenData, ...customTokenData}; + return ( + BitpaySupportedEthereumTokens[currency]?.unitInfo || + tokens[currency]?.unitInfo + ); + } + } + return BitpaySupportedCoins[currency]?.unitInfo; }; export const IsUtxoCoin = (currencyAbbreviation: string): boolean => { @@ -45,52 +76,52 @@ export const IsUtxoCoin = (currencyAbbreviation: string): boolean => { }; export const IsCustomERCToken = (currencyAbbreviation: string) => { - return !Currencies[currencyAbbreviation.toLowerCase()]; + return ( + !BitpaySupportedCoins[currencyAbbreviation.toLowerCase()] || + !BitpaySupportedEthereumTokens[currencyAbbreviation.toLowerCase()] + ); }; -export const GetChain = - (currencyAbbreviation: string): Effect => - (dispatch, getState) => { - const { - WALLET: {tokenData, customTokenData}, - } = getState(); - const tokens = {...tokenData, ...customTokenData}; - const currency = currencyAbbreviation.toLowerCase(); - return Currencies[currency]?.chain || tokens[currency]?.chain; - }; - -export const IsERCToken = - (currencyAbbreviation: string): Effect => - (dispatch, getState) => { - const { - WALLET: {tokenData, customTokenData}, - } = getState(); - const tokens = {...tokenData, ...customTokenData}; - const currency = currencyAbbreviation.toLowerCase(); - return ( - Currencies[currency]?.properties.isERCToken || - tokens[currency]?.properties.isERCToken - ); - }; +export const IsERCToken = (currencyAbbreviation: string): boolean => { + const currency = currencyAbbreviation.toLowerCase(); + return !SUPPORTED_COINS.includes(currency); +}; export const GetBlockExplorerUrl = - (currencyAbbreviation: string, network: string = 'livenet'): Effect => + ( + currencyAbbreviation: string, + network: string = 'livenet', + chain: string, + ): Effect => (dispatch, getState) => { const { WALLET: {tokenData, customTokenData}, } = getState(); - const tokens = {...tokenData, ...customTokenData}; + + let tokens; const currency = currencyAbbreviation.toLowerCase(); + if (IsERCToken(currencyAbbreviation)) { + switch (chain) { + case 'eth': + tokens = {...tokenData, ...customTokenData}; + return network === 'livenet' + ? BitpaySupportedEthereumTokens[currency]?.paymentInfo + .blockExplorerUrls || + tokens[currency]?.paymentInfo.blockExplorerUrls + : BitpaySupportedEthereumTokens[currency]?.paymentInfo + .blockExplorerUrlsTestnet || + tokens[currency]?.paymentInfo.blockExplorerUrlsTestnet; + } + } return network === 'livenet' - ? Currencies[currency]?.paymentInfo.blockExplorerUrls || - tokens[currency]?.paymentInfo.blockExplorerUrls - : Currencies[currency]?.paymentInfo.blockExplorerUrlsTestnet || - tokens[currency]?.paymentInfo.blockExplorerUrlsTestnet; + ? BitpaySupportedCoins[currency]?.paymentInfo.blockExplorerUrls + : BitpaySupportedCoins[currency]?.paymentInfo.blockExplorerUrlsTestnet; }; export const GetFeeUnits = ( currencyAbbreviation: string, + chain: string, ): Effect<{ feeUnit: string; feeUnitAmount: number; @@ -101,14 +132,25 @@ export const GetFeeUnits = const { WALLET: {tokenData, customTokenData}, } = getState(); - const tokens = {...tokenData, ...customTokenData}; + let tokens; const currency = currencyAbbreviation.toLowerCase(); - return Currencies[currency]?.feeInfo || tokens[currency]?.feeInfo; + if (IsERCToken(currencyAbbreviation)) { + switch (chain) { + case 'eth': + tokens = {...tokenData, ...customTokenData}; + return ( + BitpaySupportedEthereumTokens[currency]?.feeInfo || + tokens[currency]?.feeInfo + ); + } + } + return BitpaySupportedCoins[currency]?.feeInfo; }; export const GetTheme = ( currencyAbbreviation: string, + chain: string, ): Effect<{ coinColor: string; backgroundColor: string; @@ -118,32 +160,59 @@ export const GetTheme = const { WALLET: {tokenData, customTokenData}, } = getState(); - const tokens = {...tokenData, ...customTokenData}; + let tokens; const currency = currencyAbbreviation.toLowerCase(); - return Currencies[currency]?.theme || tokens[currency]?.theme; + if (IsERCToken(currencyAbbreviation)) { + switch (chain) { + case 'eth': + tokens = {...tokenData, ...customTokenData}; + return ( + BitpaySupportedEthereumTokens[currency]?.theme || + tokens[currency]?.theme + ); + } + } + return BitpaySupportedCoins[currency]?.theme; }; export const GetName = - (currencyAbbreviation: string): Effect => + (currencyAbbreviation: string, chain: string): Effect => (dispatch, getState) => { const { WALLET: {tokenData, customTokenData}, } = getState(); - const tokens = {...tokenData, ...customTokenData}; + let tokens; const currency = currencyAbbreviation.toLowerCase(); - return Currencies[currency]?.name || tokens[currency]?.name; + if (IsERCToken(currencyAbbreviation)) { + switch (chain) { + case 'eth': + tokens = {...tokenData, ...customTokenData}; + return ( + BitpaySupportedEthereumTokens[currency]?.name || + tokens[currency]?.name + ); + } + } + return BitpaySupportedCoins[currency]?.name; }; export const isSingleAddressCoin = - (currencyAbbreviation: string): Effect => + (currencyAbbreviation: string, chain: string): Effect => (dispatch, getState) => { const { WALLET: {tokenData, customTokenData}, } = getState(); - const tokens = {...tokenData, ...customTokenData}; + let tokens; const currency = currencyAbbreviation.toLowerCase(); - return ( - Currencies[currency]?.properties.singleAddress || - tokens[currency]?.properties.singleAddress - ); + if (IsERCToken(currencyAbbreviation)) { + switch (chain) { + case 'eth': + tokens = {...tokenData, ...customTokenData}; + return ( + BitpaySupportedEthereumTokens[currency]?.properties.singleAddress || + tokens[currency]?.properties.singleAddress + ); + } + } + return BitpaySupportedCoins[currency]?.properties.singleAddress; }; diff --git a/src/store/wallet/utils/wallet.ts b/src/store/wallet/utils/wallet.ts index 01c407a7a8..df1b7c4ab9 100644 --- a/src/store/wallet/utils/wallet.ts +++ b/src/store/wallet/utils/wallet.ts @@ -14,7 +14,11 @@ import {BwcProvider} from '../../../lib/bwc'; import {GetName, GetPrecision, GetProtocolPrefix} from './currency'; import merge from 'lodash.merge'; import cloneDeep from 'lodash.clonedeep'; -import {convertToFiat, formatFiatAmount} from '../../../utils/helper-methods'; +import { + convertToFiat, + formatFiatAmount, + getBadgeImg, +} from '../../../utils/helper-methods'; import {WALLET_DISPLAY_LIMIT} from '../../../navigation/tabs/home/components/Wallet'; import {Network} from '../../../constants'; import {GetInvoiceCurrency, PayProOptions} from '../effects/paypro/paypro'; @@ -34,97 +38,101 @@ import { import {AppDispatch} from '../../../utils/hooks'; import {find, isEqual} from 'lodash'; -const mapAbbreviationAndName = +export const mapAbbreviationAndName = ( coin: string, + chain: string, ): Effect<{currencyAbbreviation: string; currencyName: string}> => dispatch => { switch (coin) { case 'pax': return { currencyAbbreviation: 'usdp', - currencyName: dispatch(GetName('usdp')), + currencyName: dispatch(GetName('usdp', chain)), }; default: return { currencyAbbreviation: coin, - currencyName: dispatch(GetName(coin)), + currencyName: dispatch(GetName(coin, chain)), }; } }; // Formatted wallet obj - this is merged with BWC client -export const buildWalletObj = - ( - { - walletId, - coin, - balance = { - crypto: '0.00', - cryptoLocked: '0.00', - cryptoConfirmedLocked: '0.00', - cryptoSpendable: '0.00', - cryptoPending: '0.00', - fiat: 0, - fiatLastDay: 0, - fiatLocked: 0, - fiatConfirmedLocked: 0, - fiatSpendable: 0, - fiatPending: 0, - sat: 0, - satAvailable: 0, - satLocked: 0, - satConfirmedLocked: 0, - satConfirmed: 0, - satConfirmedAvailable: 0, - satSpendable: 0, - satPending: 0, - }, - tokens, - keyId, - network, - n, - m, - hideWallet = false, - hideBalance = false, - }: Credentials & { - balance?: WalletBalance; - tokens?: any; - hideWallet?: boolean; // ionic migration only - hideBalance?: boolean; // ionic migration only - network: Network; - }, - tokenOpts?: {[key in string]: Token}, - otherOpts?: { - walletName?: string; +export const buildWalletObj = ( + { + walletId, + chain, + balance = { + crypto: '0.00', + cryptoLocked: '0.00', + cryptoConfirmedLocked: '0.00', + cryptoSpendable: '0.00', + cryptoPending: '0.00', + fiat: 0, + fiatLastDay: 0, + fiatLocked: 0, + fiatConfirmedLocked: 0, + fiatSpendable: 0, + fiatPending: 0, + sat: 0, + satAvailable: 0, + satLocked: 0, + satConfirmedLocked: 0, + satConfirmed: 0, + satConfirmedAvailable: 0, + satSpendable: 0, + satPending: 0, }, - ): Effect => - dispatch => { - const {currencyName, currencyAbbreviation} = dispatch( - mapAbbreviationAndName(coin), - ); - return { - id: walletId, - currencyName, - currencyAbbreviation, - walletName: otherOpts?.walletName, - balance, - tokens, - network, - keyId: keyId ? keyId : 'readonly', - img: SUPPORTED_CURRENCIES.includes(currencyAbbreviation) - ? CurrencyListIcons[currencyAbbreviation] - : tokenOpts && tokenOpts[currencyAbbreviation]?.logoURI - ? (tokenOpts[currencyAbbreviation].logoURI as string) - : '', - n, - m, - isRefreshing: false, - hideWallet, - hideBalance, - pendingTxps: [], - }; + tokens, + keyId, + network, + n, + m, + hideWallet = false, + hideBalance = false, + currencyAbbreviation, + currencyName, + }: Credentials & { + balance?: WalletBalance; + tokens?: any; + hideWallet?: boolean; // ionic migration only + hideBalance?: boolean; // ionic migration only + network: Network; + currencyAbbreviation: string; + currencyName: string; + }, + tokenOpts?: {[key in string]: {[key in string]: Token}}, + otherOpts?: { + walletName?: string; + }, +): WalletObj => { + return { + id: walletId, + currencyName, + currencyAbbreviation, + chain, + walletName: otherOpts?.walletName, + balance, + tokens, + network, + keyId: keyId ? keyId : 'readonly', + img: SUPPORTED_CURRENCIES.includes(currencyAbbreviation) + ? CurrencyListIcons[currencyAbbreviation] + : tokenOpts && + tokenOpts[chain] && + tokenOpts[chain][currencyAbbreviation]?.logoURI + ? (tokenOpts[chain][currencyAbbreviation].logoURI as string) + : '', + badgeImg: getBadgeImg(currencyAbbreviation, chain), + n, + m, + isRefreshing: false, + hideWallet, + hideBalance, + pendingTxps: [], }; +}; // Formatted key Obj export const buildKeyObj = ({ @@ -203,11 +211,12 @@ export const toFiat = totalAmount: number, fiatCode: string = 'USD', currencyAbbreviation: string, + chain: string, rates: Rates = {}, customRate?: number, ): Effect => dispatch => { - const ratesPerCurrency = rates[currencyAbbreviation]; + const ratesPerCurrency = rates[currencyAbbreviation.toLowerCase()]; if (!ratesPerCurrency) { // Rate not found return 0 @@ -229,7 +238,7 @@ export const toFiat = return 0; } - const precision = dispatch(GetPrecision(currencyAbbreviation)); + const precision = dispatch(GetPrecision(currencyAbbreviation, chain)); if (!precision) { // precision not found return 0 @@ -279,12 +288,21 @@ export const isSegwit = (addressType: string): boolean => { }; export const GetProtocolPrefixAddress = - (coin: string, network: string, address: string): Effect => + ( + currencyAbbreviation: string, + network: string, + address: string, + chain: string, + ): Effect => dispatch => { - if (coin !== 'bch') { + if (currencyAbbreviation !== 'bch') { return address; } - return dispatch(GetProtocolPrefix(coin, network)) + ':' + address; + return ( + dispatch(GetProtocolPrefix(currencyAbbreviation, network, chain)) + + ':' + + address + ); }; export const getRemainingWalletCount = ( @@ -418,7 +436,9 @@ export const BuildKeysAndWalletsList = ({ return paymentOptions.some( ({currency, network: optionNetwork}) => { return ( - GetInvoiceCurrency(wallet.currencyAbbreviation).toLowerCase() === currency.toLowerCase() && + GetInvoiceCurrency( + wallet.currencyAbbreviation, + ).toLowerCase() === currency.toLowerCase() && wallet.network === optionNetwork ); }, @@ -434,7 +454,9 @@ export const BuildKeysAndWalletsList = ({ currencyAbbreviation, hideWallet, balance, - credentials: {network, walletName: fallbackName}, + network, + chain, + credentials: {walletName: fallbackName}, walletName, } = walletObj; return merge(cloneDeep(walletObj), { @@ -446,6 +468,7 @@ export const BuildKeysAndWalletsList = ({ balance.sat, defaultAltCurrencyIsoCode, currencyAbbreviation, + chain, rates, ), ), @@ -462,6 +485,7 @@ export const BuildKeysAndWalletsList = ({ balance.satLocked, defaultAltCurrencyIsoCode, currencyAbbreviation, + chain, rates, ), ), diff --git a/src/store/wallet/wallet.models.ts b/src/store/wallet/wallet.models.ts index d6f609b044..0697b7685a 100644 --- a/src/store/wallet/wallet.models.ts +++ b/src/store/wallet/wallet.models.ts @@ -89,6 +89,7 @@ export interface WalletStatus { export interface WalletObj { id: string; keyId: string; + chain: string; currencyName: string; currencyAbbreviation: string; m: number; @@ -101,6 +102,7 @@ export interface WalletObj { tokenAddresses?: []; }; img: string | ((props?: any) => ReactElement); + badgeImg?: string | ((props?: any) => ReactElement); receiveAddress?: string; isRefreshing?: boolean; transactionHistory?: { @@ -184,6 +186,7 @@ export interface Recipient { address: string; amount?: number; destinationTag?: number; + chain?: string; } export interface CustomTransactionData { @@ -210,6 +213,7 @@ export interface TransactionOptions { amount: number; context?: TransactionOptionsContext; currency?: string; + chain?: string; toAddress?: string; network?: string; feeLevel?: string; @@ -352,11 +356,13 @@ export interface TxDetailsSendingTo { currencyAbbreviation?: string; recipientAltAmountStr?: string; recipientCoin?: string; + recipientChain?: string; } export interface TxDetailsSendingFrom { walletName: string; img: string | ((props?: any) => ReactElement); + badgeImg?: string | ((props: any) => ReactElement); } export interface TxDetails { diff --git a/src/utils/helper-methods.ts b/src/utils/helper-methods.ts index 78701fc7a5..a6ce6e35ec 100644 --- a/src/utils/helper-methods.ts +++ b/src/utils/helper-methods.ts @@ -1,17 +1,14 @@ -import {Currencies} from '../constants/currencies'; +import {SUPPORTED_COINS} from '../constants/currencies'; import {Key} from '../store/wallet/wallet.models'; import {ContactRowProps} from '../components/list/ContactRow'; import {Network} from '../constants'; +import {CurrencyListIcons} from '../constants/SupportedCurrencyOptions'; +import {ReactElement} from 'react'; +import {IsERCToken} from '../store/wallet/utils/currency'; export const sleep = (duration: number) => new Promise(resolve => setTimeout(resolve, duration)); -export const coinSupported = (coin: string): boolean => { - return Object.keys(Currencies).some( - availableCoin => availableCoin === coin.toLowerCase(), - ); -}; - export const titleCasing = (str: string) => `${str.charAt(0).toUpperCase()}${str.slice(1)}`; @@ -205,13 +202,21 @@ export const findContact = ( address: string, coin: string, network: string, + chain: string, ) => { - const foundContacts = contactList.filter( - (contact: ContactRowProps) => + const foundContacts = contactList.filter((contact: ContactRowProps) => { + const chain = contact.chain + ? contact.chain + : IsERCToken(contact.coin) + ? 'eth' + : contact.coin; + return ( contact.address === address && contact.coin === coin && - contact.network === network, - ); + contact.network === network && + chain === chain + ); + }); return !!foundContacts.length; }; @@ -289,3 +294,12 @@ export const convertToFiat = ( export const getErrorString = (err: any): string => { return err instanceof Error ? err.message : JSON.stringify(err); }; + +export const getBadgeImg = ( + currencyAbbreviation: string, + chain: string, +): string | ((props?: any) => ReactElement) => { + return !SUPPORTED_COINS.includes(currencyAbbreviation) + ? CurrencyListIcons[chain] + : ''; +};