diff --git a/android/app/build.gradle b/android/app/build.gradle
index 36acb3d4..f09a69fd 100755
--- a/android/app/build.gradle
+++ b/android/app/build.gradle
@@ -5,7 +5,7 @@ import com.android.build.OutputFile
def versionMajor = 1
def versionMinor = 0
-def versionRevision = 22
+def versionRevision = 23
def versionBuild = 0
def keystorePropertiesFile = rootProject.file("keystore.properties");
diff --git a/env/main.android.json b/env/main.android.json
index 2540c6c6..e9f671c7 100644
--- a/env/main.android.json
+++ b/env/main.android.json
@@ -1,5 +1,5 @@
{
- "APP_VERSION": "1.0.22",
+ "APP_VERSION": "1.0.23",
"ELECTRUM_PROTOCOL_CHANGE": 1.4,
"KEY_DERIVATION_VERSION": 1,
diff --git a/env/main.ios.json b/env/main.ios.json
index 4c9e72e1..cf93259c 100644
--- a/env/main.ios.json
+++ b/env/main.ios.json
@@ -1,5 +1,5 @@
{
- "APP_VERSION": "1.0.22",
+ "APP_VERSION": "1.0.23",
"ELECTRUM_PROTOCOL_CHANGE": 1.4,
"KEY_DERIVATION_VERSION": 1,
diff --git a/ios/assets/env/main.json b/ios/assets/env/main.json
index 4c9e72e1..cf93259c 100644
--- a/ios/assets/env/main.json
+++ b/ios/assets/env/main.json
@@ -1,5 +1,5 @@
{
- "APP_VERSION": "1.0.22",
+ "APP_VERSION": "1.0.23",
"ELECTRUM_PROTOCOL_CHANGE": 1.4,
"KEY_DERIVATION_VERSION": 1,
diff --git a/ios/verusMobile.xcodeproj/project.pbxproj b/ios/verusMobile.xcodeproj/project.pbxproj
index eb32e3be..622298c7 100644
--- a/ios/verusMobile.xcodeproj/project.pbxproj
+++ b/ios/verusMobile.xcodeproj/project.pbxproj
@@ -745,7 +745,7 @@
"\"${PODS_CONFIGURATION_BUILD_DIR}/react-native-webview\"",
"\"${PODS_CONFIGURATION_BUILD_DIR}/rn-fetch-blob\"",
);
- MARKETING_VERSION = 1.0.22;
+ MARKETING_VERSION = 1.0.23;
PRODUCT_BUNDLE_IDENTIFIER = org.reactjs.native.verusmobile;
PRODUCT_NAME = verusmobile;
PROVISIONING_PROFILE_SPECIFIER = "";
@@ -817,7 +817,7 @@
"\"${PODS_CONFIGURATION_BUILD_DIR}/react-native-webview\"",
"\"${PODS_CONFIGURATION_BUILD_DIR}/rn-fetch-blob\"",
);
- MARKETING_VERSION = 1.0.22;
+ MARKETING_VERSION = 1.0.23;
PRODUCT_BUNDLE_IDENTIFIER = org.reactjs.native.verusmobile;
PRODUCT_NAME = verusmobile;
PROVISIONING_PROFILE_SPECIFIER = "";
diff --git a/ios/verusMobile/Info.plist b/ios/verusMobile/Info.plist
index db904dcb..2a642ed7 100755
--- a/ios/verusMobile/Info.plist
+++ b/ios/verusMobile/Info.plist
@@ -48,16 +48,36 @@
- NSCameraUsageDescription
- Verus Mobile needs access to the camera to scan QR codes, and to allow you to add images to your locally encrypted personal profile
- NSFaceIDUsageDescription
- Enabling Face ID allows you quick and secure access to your account.
- NSMicrophoneUsageDescription
- Verus Mobile needs access to the camera to scan QR codes
- NSPhotoLibraryAddUsageDescription
- The user can save QR code invoices to their photo library.
- NSPhotoLibraryUsageDescription
- The user can use the photo library to add images to their locally encrypted personal profile
+ NSAppleMusicUsageDescription
+ The app does not request this permission or utilize this functionality but it is included in our info.plist since our app utilizes the react-native-permissions library, which references this permission in its code.
+ NSBluetoothAlwaysUsageDescription
+ The app does not request this permission or utilize this functionality but it is included in our info.plist since our app utilizes the react-native-permissions library, which references this permission in its code.
+ NSBluetoothPeripheralUsageDescription
+ The app does not request this permission or utilize this functionality but it is included in our info.plist since our app utilizes the react-native-permissions library, which references this permission in its code.
+ NSCalendarsUsageDescription
+ The app does not request this permission or utilize this functionality but it is included in our info.plist since our app utilizes the react-native-permissions library, which references this permission in its code.
+ NSCameraUsageDescription
+ Verus Mobile needs access to the camera to scan QR codes, and to allow you to add images to your locally encrypted personal profile
+ NSContactsUsageDescription
+ The app does not request this permission or utilize this functionality but it is included in our info.plist since our app utilizes the react-native-permissions library, which references this permission in its code.
+ NSFaceIDUsageDescription
+ Enabling Face ID allows you quick and secure access to your account.
+ NSLocationAlwaysUsageDescription
+ The app does not request this permission or utilize this functionality but it is included in our info.plist since our app utilizes the react-native-permissions library, which references this permission in its code.
+ NSLocationWhenInUseUsageDescription
+ The app does not request this permission or utilize this functionality but it is included in our info.plist since our app utilizes the react-native-permissions library, which references this permission in its code.
+ NSMicrophoneUsageDescription
+ Verus Mobile needs access to the camera to scan QR codes
+ NSMotionUsageDescription
+ The app does not request this permission or utilize this functionality but it is included in our info.plist since our app utilizes the react-native-permissions library, which references this permission in its code.
+ NSPhotoLibraryAddUsageDescription
+ The user can save QR code invoices to their photo library.
+ NSPhotoLibraryUsageDescription
+ The user can use the photo library to add images to their locally encrypted personal profile
+ NSSiriUsageDescription
+ The app does not request this permission or utilize this functionality but it is included in our info.plist since our app utilizes the react-native-permissions library, which references this permission in its code.
+ NSSpeechRecognitionUsageDescription
+ The app does not request this permission or utilize this functionality but it is included in our info.plist since our app utilizes the react-native-permissions library, which references this permission in its code.
UIAppFonts
AntDesign.ttf
diff --git a/package.json b/package.json
index 2a0057c0..f7905d11 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "verusmobile",
- "version": "1.0.22",
+ "version": "1.0.23",
"private": true,
"scripts": {
"postinstall": "./node_modules/.bin/rn-nodeify --hack --install --yarn && npx jetify",
diff --git a/src/components/BarcodeReader/BarcodeReader.js b/src/components/BarcodeReader/BarcodeReader.js
index 3c49d5de..d0c84980 100644
--- a/src/components/BarcodeReader/BarcodeReader.js
+++ b/src/components/BarcodeReader/BarcodeReader.js
@@ -108,7 +108,7 @@ const BarcodeReader = props => {
}}
device={device}
codeScanner={codeScanner}
- isActive={appStateVisible === 'active'}
+ isActive={appStateVisible === 'active' && !props.cameraDisabled}
{...cameraProps}
/>
{
+ const [preventExit, setPreventExit] = useState(false);
+ const [modalHeight, setModalHeight] = useState(600); // Adjust as needed
+
+ const activeCoinsForUser = useSelector(state => state.coins.activeCoinsForUser);
+ const [modalTitle, setModalTitle] = useState("Title");
+
+ useEffect(() => {
+ // Handle side effects here if necessary
+ }, []);
+
+ const cancel = () => {
+ if (!preventExit) {
+ setVisible(false);
+ if (onClose) {
+ onClose();
+ }
+ }
+ };
+
+ const showHelpModal = () => {
+ // Implement your help modal logic here
+ };
+
+ return (
+
+
+
+ {loading ? :
+
+ (
+
+
+
+ {modalTitle}
+
+
+ ),
+ headerStyle: {
+ height: 52,
+ },
+ }}
+ >
+
+ {() => (
+ ,
+ }}
+ >
+ {
+ e.preventDefault();
+ },
+ }}
+ >
+ {tabProps => (
+
+ )}
+
+ {
+ e.preventDefault();
+ },
+ }}
+ >
+ {tabProps => (
+
+ )}
+
+ {mode === CONVERT_CARD_MODAL_MODES.RECEIVE && (
+ {
+ e.preventDefault();
+ },
+ }}
+ >
+ {tabProps => (
+
+ )}
+
+ )}
+ {
+ e.preventDefault();
+ },
+ }}
+ >
+ {tabProps => (
+
+ )}
+
+
+ )}
+
+
+
+ }
+
+
+
+ );
+};
+
+export default ConvertCardModal;
\ No newline at end of file
diff --git a/src/components/SearchableList.js b/src/components/SearchableList.js
new file mode 100644
index 00000000..bfc7535d
--- /dev/null
+++ b/src/components/SearchableList.js
@@ -0,0 +1,115 @@
+import React, { useMemo, useState } from 'react';
+import { View, TextInput, FlatList } from 'react-native';
+import { List, Text } from 'react-native-paper';
+import { RenderCircleCoinLogo } from '../utils/CoinData/Graphics';
+import Colors from '../globals/colors';
+import { useNavigation } from '@react-navigation/native';
+
+const SearchableList = (props) => {
+ const [searchQuery, setSearchQuery] = useState('');
+ const items = props.items ? props.items : [];
+
+ const navigation = useNavigation();
+
+ // Filter the data based on the search query
+ const filteredData = useMemo(() => {
+ return items.filter(item =>
+ (
+ item.title.toLowerCase().includes(searchQuery.toLowerCase()) ||
+ item.description.toLowerCase().includes(searchQuery.toLowerCase())
+ )
+ );
+ }, [searchQuery, items]);
+
+ const handleSelect = (key) => {
+ if (props.onSelect) props.onSelect(key);
+ if (props.nextScreen) navigation.navigate(props.nextScreen)
+ }
+
+ // Render each item in the FlatList
+ const renderItem = ({ item }) => (
+ handleSelect(item.key)}
+ left={() => (
+
+ {RenderCircleCoinLogo(item.logo)}
+
+ )}
+ right={props =>
+
+ {item.rightTitle && (
+
+ {item.rightTitle}
+
+ )}
+ {item.rightDescription && (
+
+ {item.rightDescription}
+
+ )}
+
+ }
+ />
+ );
+
+ return (
+
+ setSearchQuery(text)}
+ />
+ item.key}
+ renderItem={renderItem}
+ ListEmptyComponent={
+
+ No items found
+
+ }
+ />
+
+ );
+};
+
+export default SearchableList;
\ No newline at end of file
diff --git a/src/components/SendModal/ConvertOrCrossChainSend/ConvertOrCrossChainSendForm/ConvertOrCrossChainSendForm.js b/src/components/SendModal/ConvertOrCrossChainSend/ConvertOrCrossChainSendForm/ConvertOrCrossChainSendForm.js
index de4b6ac9..e2f408d7 100644
--- a/src/components/SendModal/ConvertOrCrossChainSend/ConvertOrCrossChainSendForm/ConvertOrCrossChainSendForm.js
+++ b/src/components/SendModal/ConvertOrCrossChainSend/ConvertOrCrossChainSendForm/ConvertOrCrossChainSendForm.js
@@ -4,7 +4,7 @@ import { Alert, View, TouchableWithoutFeedback, Keyboard, FlatList, Animated, To
import { KeyboardAwareScrollView } from 'react-native-keyboard-aware-scroll-view';
import { TextInput, Button, Divider, Checkbox, List, Text, IconButton } from "react-native-paper";
import { createAlert } from "../../../../actions/actions/alert/dispatchers/alert";
-import { API_SEND, DLIGHT_PRIVATE, ERC20, ETH } from "../../../../utils/constants/intervalConstants";
+import { API_SEND, DLIGHT_PRIVATE, ERC20, ETH, VRPC } from "../../../../utils/constants/intervalConstants";
import {
SEND_MODAL_ADVANCED_FORM,
SEND_MODAL_AMOUNT_FIELD,
@@ -57,6 +57,7 @@ import { useObjectSelector } from "../../../../hooks/useObjectSelector";
const ConvertOrCrossChainSendForm = ({ setLoading, setModalHeight, updateSendFormData, navigation }) => {
const { height } = Dimensions.get('window');
const sendModal = useObjectSelector(state => state.sendModal);
+ const allSubWallets = useObjectSelector(state => state.coinMenus.allSubWallets);
const activeUser = useObjectSelector(state => state.authentication.activeAccount);
const addresses = useObjectSelector(state => selectAddresses(state));
const activeAccount = useObjectSelector(state => state.authentication.activeAccount);
@@ -76,9 +77,17 @@ const ConvertOrCrossChainSendForm = ({ setLoading, setModalHeight, updateSendFor
[SEND_MODAL_EXPORTTO_FIELD]: "Destination network",
[SEND_MODAL_VIA_FIELD]: "Convert via",
[SEND_MODAL_CONVERTTO_FIELD]: sendModal.data[SEND_MODAL_IS_PRECONVERT] ? "Preconvert to" : "Convert to",
- [SEND_MODAL_MAPPING_FIELD]: "Receive as"
+ [SEND_MODAL_MAPPING_FIELD]: "Receive as",
+ [SEND_MODAL_TO_ADDRESS_FIELD]: "Recipient address"
}
+ const CONVERSION_PATH_FIELDS = [
+ SEND_MODAL_EXPORTTO_FIELD,
+ SEND_MODAL_VIA_FIELD,
+ SEND_MODAL_CONVERTTO_FIELD,
+ SEND_MODAL_MAPPING_FIELD
+ ];
+
const [searchMode, setSearchMode] = useState(false);
const [selectedField, setSelectedField] = useState("");
@@ -529,6 +538,80 @@ const ConvertOrCrossChainSendForm = ({ setLoading, setModalHeight, updateSendFor
}
};
+ const processSelfSuggestionPaths = () => {
+ const addresses = [];
+ const seen = new Set();
+
+ if (activeAccount) {
+ if (allSubWallets[coinsList.VRSC.id]) {
+ const vrscKeys = activeAccount.keys[coinsList.VRSC.id];
+
+ for (const channelId in vrscKeys) {
+ const [channelName, addr, network] = channelId.split('.');
+
+ if (channelName === VRPC && !seen.has(addr)) {
+ const walletId = `SUBWALLET_${channelId}`;
+
+ if (allSubWallets[coinsList.VRSC.id]) {
+ const wallet = allSubWallets[coinsList.VRSC.id].find(x => x.id === walletId);
+
+ if (wallet) {
+ seen.add(addr);
+ addresses.push({
+ title: wallet.name,
+ logoid: coinsList.VRSC.id,
+ key: addr,
+ description: addr,
+ values: {
+ [SEND_MODAL_TO_ADDRESS_FIELD]: addr
+ },
+ right: "",
+ keywords: [
+ addr,
+ wallet.name
+ ],
+ })
+ }
+ }
+ }
+ }
+ }
+
+ for (const coinId in activeAccount.keys) {
+ if (activeAccount.keys[coinId] && (activeAccount.keys[coinId][ETH] || activeAccount.keys[coinId][ERC20])) {
+ const ethAddresses = activeAccount.keys[coinId][ETH] ?
+ activeAccount.keys[coinId][ETH].addresses : activeAccount.keys[coinId][ERC20].addresses;
+
+ if (ethAddresses && ethAddresses.length > 0) {
+ const addr = ethAddresses[0];
+ const addrTitle = addr.substring(0, 8) + '...' + addr.substring(addr.length - 8);
+
+ if (!seen.has(addr)) {
+ seen.add(addr);
+ addresses.push({
+ title: addrTitle,
+ logoid: coinsList.ETH.id,
+ key: addr,
+ description: addr,
+ values: {
+ [SEND_MODAL_TO_ADDRESS_FIELD]: addr
+ },
+ right: "",
+ keywords: [
+ addr
+ ]
+ })
+ }
+
+ break;
+ }
+ }
+ }
+ }
+
+ return addresses;
+ };
+
const fetchSuggestionsBase = async (field) => {
if (loadingSuggestions) return;
let newSuggestionsBase = []
@@ -547,25 +630,27 @@ const ConvertOrCrossChainSendForm = ({ setLoading, setModalHeight, updateSendFor
// mapping: boolean;
// bounceback: boolean;
// }>}
-
- const paths = conversionPaths
- ? conversionPaths
- : await getConversionPaths(
- sendModal.coinObj,
- sendModal.subWallet.api_channels[API_SEND],
- {
- src: sendModal.coinObj.currency_id,
- },
- );
let flatPaths = []
+
+ if (CONVERSION_PATH_FIELDS.includes(field)) {
+ const paths = conversionPaths
+ ? conversionPaths
+ : await getConversionPaths(
+ sendModal.coinObj,
+ sendModal.subWallet.api_channels[API_SEND],
+ {
+ src: sendModal.coinObj.currency_id,
+ },
+ );
- for (const destinationid in paths) {
- flatPaths = flatPaths.concat(paths[destinationid])
+ for (const destinationid in paths) {
+ flatPaths = flatPaths.concat(paths[destinationid])
+ }
+
+ setConversionPaths(paths)
}
- setConversionPaths(paths)
-
switch (field) {
case SEND_MODAL_CONVERTTO_FIELD:
newSuggestionsBase = await processConverttoSuggestionPaths(flatPaths, sendModal.coinObj);
@@ -582,6 +667,9 @@ const ConvertOrCrossChainSendForm = ({ setLoading, setModalHeight, updateSendFor
case SEND_MODAL_MAPPING_FIELD:
newSuggestionsBase = await processMappingSuggestionPaths(flatPaths, sendModal.coinObj);
setSuggestionBase(newSuggestionsBase);
+ case SEND_MODAL_TO_ADDRESS_FIELD:
+ newSuggestionsBase = processSelfSuggestionPaths();
+ setSuggestionBase(newSuggestionsBase);
default:
setSuggestionBase(newSuggestionsBase);
break;
@@ -817,7 +905,7 @@ const ConvertOrCrossChainSendForm = ({ setLoading, setModalHeight, updateSendFor
if (addr.endsWith("@")) {
const identityRes = await getIdentity(coinObj, activeAccount, channel, addr);
- if (identityRes.error) throw new Error("Failed to fetch " + addr);
+ if (identityRes.error) throw new Error(`Failed to get information about ${addr}. Try using the i-address of this VerusID.`);
keyhash = identityRes.result.identity.identityaddress;
} else keyhash = addr;
@@ -1018,7 +1106,8 @@ const ConvertOrCrossChainSendForm = ({ setLoading, setModalHeight, updateSendFor
}
right={() =>
selectedField !== SEND_MODAL_EXPORTTO_FIELD &&
- selectedField !== SEND_MODAL_MAPPING_FIELD ? (
+ selectedField !== SEND_MODAL_MAPPING_FIELD &&
+ selectedField !== SEND_MODAL_TO_ADDRESS_FIELD ? (
updateSendFormData(SEND_MODAL_TO_ADDRESS_FIELD, text)
}
- onSelfPress={() => setAddressSelf()}
+ onSelfPress={() => handleFieldFocus(SEND_MODAL_TO_ADDRESS_FIELD)}
amountValue={sendModal.data[SEND_MODAL_AMOUNT_FIELD]}
onAmountChange={text =>
updateSendFormData(SEND_MODAL_AMOUNT_FIELD, text)
diff --git a/src/containers/Coin/DynamicHeader.js b/src/containers/Coin/DynamicHeader.js
index cf757ccf..bab4291d 100644
--- a/src/containers/Coin/DynamicHeader.js
+++ b/src/containers/Coin/DynamicHeader.js
@@ -38,6 +38,7 @@ import { GestureDetector, Gesture, Directions } from 'react-native-gesture-handl
import { useSelector, useDispatch } from 'react-redux';
import Styles from '../../styles';
import { useObjectSelector } from '../../hooks/useObjectSelector';
+import { coinsList } from '../../utils/CoinData/CoinsList';
const DynamicHeader = ({ switchTab }) => {
const dispatch = useDispatch();
@@ -144,7 +145,7 @@ const DynamicHeader = ({ switchTab }) => {
let mappedCoin = null;
- if (activeCoin.mapped_to != null) {
+ if (activeCoin.mapped_to != null && activeCoin.id !== coinsList.VRSC.id) {
try {
mappedCoin = CoinDirectory.getBasicCoinObj(activeCoin.mapped_to);
} catch (e) {
diff --git a/src/containers/Coin/SendCoin/SendCoin.js b/src/containers/Coin/SendCoin/SendCoin.js
index ab7e923a..b0b46b5c 100644
--- a/src/containers/Coin/SendCoin/SendCoin.js
+++ b/src/containers/Coin/SendCoin/SendCoin.js
@@ -43,7 +43,9 @@ const SendCoin = ({ navigation }) => {
const generalWalletSettings = useObjectSelector(
state => state.settings.generalWalletSettings,
);
+
const dispatch = useDispatch()
+ const [cameraDisabled, setCameraDisabled] = useState(generalWalletSettings.enableSendCoinCameraToggle === true);
const CONVERT_OR_CROSS_CHAIN_OPTIONS = CONVERSION_DISABLED ? [
{
@@ -207,6 +209,10 @@ const SendCoin = ({ navigation }) => {
openConvertOrCrossChainSendModal(activeCoin, subWallet, option.data)
}
+ const toggleCameraDisabled = () => {
+ setCameraDisabled(!cameraDisabled);
+ }
+
const openConvertOrCrossChainModal = () => {
const { ackedCurrencyDisclaimer } = generalWalletSettings;
@@ -261,6 +267,7 @@ By proceeding, you confirm that you've read, understood, and agreed to this. Ens
openConvertOrCrossChainModal()
}
+ >
+ {CONVERSION_DISABLED ? "Send cross-chain" : "Convert or cross-chain"}
+
+ )}
+ {generalWalletSettings.enableSendCoinCameraToggle && (
+
)}
diff --git a/src/containers/Convert/Convert.js b/src/containers/Convert/Convert.js
index 3f79f9d6..350092fb 100644
--- a/src/containers/Convert/Convert.js
+++ b/src/containers/Convert/Convert.js
@@ -4,11 +4,839 @@ import Styles from "../../styles";
import { Button } from "react-native-paper";
import Colors from "../../globals/colors";
import ConvertCard from "./ConvertCard/ConvertCard";
+import { useSelector } from "react-redux";
+import { extractLedgerData } from "../../utils/ledger/extractLedgerData";
+import { API_GET_BALANCES, API_SEND, ERC20, ETH, GENERAL, IS_CONVERTABLE_WITH_VRSC_ETH_BRIDGE, IS_PBAAS_ROOT, VRPC } from "../../utils/constants/intervalConstants";
+import { IS_PBAAS_CHAIN, USD } from "../../utils/constants/currencies";
+import BigNumber from 'bignumber.js';
+import ConvertCardModal from "../../components/ConvertCardModal/ConvertCardModal";
+import { CONVERT_CARD_MODAL_MODES } from "../../utils/constants/convert";
+import { normalizeNum } from "../../utils/normalizeNum";
+import { formatCurrency } from "react-native-format-currency";
+import { useObjectSelector } from "../../hooks/useObjectSelector";
+import { CoinDirectory } from "../../utils/CoinData/CoinDirectory";
+import { closeLoadingModal, openLoadingModal } from "../../actions/actionDispatchers";
+import { createAlert } from "../../actions/actions/alert/dispatchers/alert";
+import { getConversionPaths } from "../../utils/api/routers/getConversionPaths";
+import { VETH } from "../../utils/constants/web3Constants";
+import { coinsList } from "../../utils/CoinData/CoinsList";
const Convert = (props) => {
- useEffect(() => {
+ const displayCurrency = useSelector(state => state.settings.generalWalletSettings.displayCurrency || USD);
+
+ const allSubWallets = useObjectSelector(state => state.coinMenus.allSubWallets);
+ const activeCoinsForUser = useObjectSelector(state => state.coins.activeCoinsForUser);
+ const rates = useObjectSelector(state => state.ledger.rates);
+ const balances = useObjectSelector(state => extractLedgerData(state, 'balances', API_GET_BALANCES));
+ const activeAccount = useObjectSelector(state => state.authentication.activeAccount);
+
+ const [loading, setLoading] = useState(false);
+
+ const [totalBalances, setTotalBalances] = useState({});
+
+ const [convertCardModalMode, setConvertCardModalMode] = useState(CONVERT_CARD_MODAL_MODES.SEND);
+ const [convertCardModalVisible, setConvertCardModalVisible] = useState(false);
+
+ // These key value maps that contain the data that the form needs to execute on a user's selection
+ const [sourceCurrencyMap, setSourceCurrencyMap] = useState({});
+ const [destCurrencyMap, setDestCurrencyMap] = useState({});
+
+ // These are in display format, with props: title, description, rightTitle, rightDescription, logo, and key
+ const [sourceCurrencyOptionsList, setSourceCurrencyOptionsList] = useState([]);
+ const [destCurrencyOptionsList, setDestCurrencyOptionsList] = useState([]);
+
+ const [sourceNetworkOptionsList, setSourceNetworkOptionsList] = useState([]);
+ const [destNetworkOptionsList, setDestNetworkOptionsList] = useState([]);
+
+ const [destConverterOptionsList, setDestConverterOptionsList] = useState([]);
+
+ const [sourceWalletOptionsList, setSourceWalletOptionsList] = useState([]);
+ const [destAddressOptionsList, setDestAddressOptionsList] = useState([]);
+
+ const [selectedSourceCurrency, setSelectedSourceCurrency] = useState(null);
+ const [selectedSourceCoinObj, setSelectedSourceCoinObj] = useState(null);
+ const [selectedSourceNetwork, setSelectedSourceNetwork] = useState(null);
+ const [selectedSourceWallet, setSelectedSourceWallet] = useState(null);
+
+ const [selectedDestCurrencyId, setSelectedDestCurrencyId] = useState(null);
+ const [selectedDestCoinObj, setSelectedDestCoinObj] = useState(null);
+ const [selectedDestNetworkId, setSelectedDestNetworkId] = useState(null);
+
+ const [selectedConverterId, setSelectedConverterId] = useState(null);
+ const [selectedDestAddress, setSelectedDestAddress] = useState(null);
+
+ const [sendAmount, setSendAmount] = useState(null);
+ const [sourceBalance, setSourceBalance] = useState(null);
+
+ const [conversionPaths, setConversionPaths] = useState(null);
+
+ const getTotalBalances = () => {
+ let coinBalances = {};
+
+ activeCoinsForUser.map(coinObj => {
+ coinBalances[coinObj.id] = {
+ crypto: BigNumber('0'),
+ rate: null
+ };
+
+ allSubWallets[coinObj.id].map(wallet => {
+ if (
+ balances[coinObj.id] != null &&
+ balances[coinObj.id][wallet.id] != null
+ ) {
+ const cryptoBalance = coinBalances[coinObj.id].crypto.plus(
+ balances[coinObj.id] &&
+ balances[coinObj.id][wallet.id] &&
+ balances[coinObj.id][wallet.id].total != null
+ ? BigNumber(balances[coinObj.id][wallet.id].total)
+ : null,
+ );
+
+ const uniRate = rates[GENERAL] && rates[GENERAL][coinObj.id] ? rates[GENERAL][coinObj.id][displayCurrency] : null
+
+ coinBalances[coinObj.id] = {
+ crypto: cryptoBalance,
+ rate: uniRate != null ? BigNumber(uniRate) : null
+ }
+ }
+ });
+ });
+
+ return coinBalances;
+ };
+
+ const getSourceCurrencyMap = () => {
+ const currencyMap = {};
+ const usedCoins = [];
+
+ for (const coinObj of activeCoinsForUser.sort(x => (x.mapped_to ? -1 : 1))) {
+ if (!usedCoins.includes(coinObj.id) && (coinObj.proto === 'vrsc' || coinObj.tags.includes(IS_CONVERTABLE_WITH_VRSC_ETH_BRIDGE))) {
+ currencyMap[coinObj.currency_id] = [coinObj];
+
+ if (coinObj.mapped_to != null && coinObj.tags.includes(IS_CONVERTABLE_WITH_VRSC_ETH_BRIDGE)) {
+ const mappedCoinObj = activeCoinsForUser.find(x => x.id === coinObj.mapped_to);
+
+ if (mappedCoinObj != null) {
+ usedCoins.push(mappedCoinObj.id)
+ currencyMap[coinObj.currency_id].push(mappedCoinObj);
+ }
+ }
+ }
+ }
+
+ return currencyMap;
+ };
+
+ const formatFiatValue = (n) => {
+ const rawFiatDisplayValue = normalizeNum(
+ n,
+ 2,
+ )[3];
+
+ const [valueFormattedWithSymbol] = formatCurrency({amount: rawFiatDisplayValue, code: displayCurrency});
- }, [])
+ return valueFormattedWithSymbol;
+ }
+
+ const getRightText = (coinId, walletIds) => {
+ let title = '-';
+ let description = '-';
+
+ if (balances[coinId] != null) {
+ const uniRate = rates[GENERAL] && rates[GENERAL][coinId] ? BigNumber(rates[GENERAL][coinId][displayCurrency]) : null;
+
+ let totalCryptoBalance = BigNumber(0);
+
+ for (const walletId of walletIds) {
+ const cryptoBalance = balances[coinId][walletId];
+
+ if (cryptoBalance != null) {
+ totalCryptoBalance = totalCryptoBalance.plus(BigNumber(cryptoBalance.confirmed));
+ }
+ }
+
+ description = `${Number(totalCryptoBalance.toString())}`;
+
+ if (uniRate != null) {
+ title = formatFiatValue(Number((totalCryptoBalance.multipliedBy(uniRate)).toString()));
+ }
+ }
+
+ return { title, description };
+ }
+
+ const getSourceCurrencyOptionsList = () => {
+ const currencies = [];
+
+ for (const coinObjs of Object.values(sourceCurrencyMap)) {
+ const rootCoinObj = coinObjs[0];
+ const mappedCoinObj = coinObjs.length > 1 ? coinObjs[1] : null;
+
+ const titleCoinObj = mappedCoinObj && mappedCoinObj.display_name.length < rootCoinObj.display_name.length ?
+ mappedCoinObj
+ :
+ rootCoinObj;
+
+ const title = titleCoinObj.display_name;
+ const description = coinObjs.map(x => x.display_ticker).join(' / ');
+
+ let rightTitle = '-';
+ let rightDescription = '-';
+
+ let totalCryptoBalance = BigNumber(0);
+ let fiatRate;
+
+ if (totalBalances[rootCoinObj.id]) {
+ if (totalBalances[rootCoinObj.id].crypto) {
+ totalCryptoBalance = totalCryptoBalance.plus(totalBalances[rootCoinObj.id].crypto);
+ }
+
+ if (totalBalances[rootCoinObj.id].rate) fiatRate = totalBalances[rootCoinObj.id].rate;
+ }
+
+ if (mappedCoinObj && totalBalances[mappedCoinObj.id]) {
+ if (totalBalances[mappedCoinObj.id].crypto) {
+ totalCryptoBalance = totalCryptoBalance.plus(totalBalances[mappedCoinObj.id].crypto);
+ }
+
+ if (!fiatRate && totalBalances[mappedCoinObj.id].rate) fiatRate = totalBalances[mappedCoinObj.id].rate;
+ }
+
+ const totalFiatBalance = fiatRate != null ? totalCryptoBalance.multipliedBy(fiatRate) : null;
+
+ if (totalFiatBalance != null) {
+ rightTitle = formatFiatValue(Number(totalFiatBalance.toString()));
+ }
+
+ rightDescription = `${Number(totalCryptoBalance.toString())}`;
+
+ currencies.push({
+ title,
+ description,
+ rightDescription,
+ rightTitle,
+ key: rootCoinObj.currency_id,
+ logo: rootCoinObj.id
+ });
+ }
+
+ return currencies;
+ };
+
+ const getDestCurrencyOptionsList = () => {
+ const currencies = [];
+
+ if (conversionPaths) {
+ for (const destinationCurrencyId in conversionPaths) {
+ let title = "-";
+ let logo;
+ let aliases = [];
+ let rightTitle = formatFiatValue(0);
+ let rightDescription = "-";
+
+ if (conversionPaths[destinationCurrencyId].length > 0) {
+ try {
+ const destCurrencyObj = CoinDirectory.findSimpleCoinObj(destinationCurrencyId);
+ aliases.push(destCurrencyObj.display_ticker);
+
+ let totalCryptoBalance = BigNumber(0);
+ let fiatRate;
+
+ if (totalBalances[destCurrencyObj.id]) {
+ if (totalBalances[destCurrencyObj.id].crypto) {
+ totalCryptoBalance = totalCryptoBalance.plus(totalBalances[destCurrencyObj.id].crypto);
+ }
+
+ if (totalBalances[destCurrencyObj.id].rate) fiatRate = totalBalances[destCurrencyObj.id].rate;
+ }
+
+ if (destCurrencyObj.mapped_to && conversionPaths[destinationCurrencyId].some(x => {
+ return x.ethdest || (x.exportto && x.exportto.fullyqualifiedname === VETH)
+ })) {
+ const mappedCoinObj = CoinDirectory.findSimpleCoinObj(destCurrencyObj.mapped_to);
+
+ if (totalBalances[mappedCoinObj.id]) {
+ if (totalBalances[mappedCoinObj.id].crypto) {
+ totalCryptoBalance = totalCryptoBalance.plus(totalBalances[mappedCoinObj.id].crypto);
+ }
+
+ if (!fiatRate && totalBalances[mappedCoinObj.id].rate) fiatRate = totalBalances[mappedCoinObj.id].rate;
+ }
+
+ const titleCoinObj = mappedCoinObj && mappedCoinObj.display_name.length < destCurrencyObj.display_name.length ?
+ mappedCoinObj
+ :
+ destCurrencyObj;
+
+ title = titleCoinObj.display_name;
+
+ aliases.push(mappedCoinObj.display_ticker);
+
+ logo = titleCoinObj.id;
+ } else {
+ title = destCurrencyObj.display_name;
+ logo = destCurrencyObj.id;
+ }
+
+ const totalFiatBalance = fiatRate != null ? totalCryptoBalance.multipliedBy(fiatRate) : null;
+
+ if (totalFiatBalance != null) {
+ rightTitle = formatFiatValue(Number(totalFiatBalance.toString()));
+ }
+
+ rightDescription = `${Number(totalCryptoBalance.toString())}`;
+
+ currencies.push({
+ title,
+ description: aliases.join(' / '),
+ rightDescription,
+ rightTitle,
+ key: destinationCurrencyId,
+ logo
+ });
+ } catch(e) {
+ title = conversionPaths[destinationCurrencyId][0].ethdest ?
+ conversionPaths[destinationCurrencyId][0].destination.mapto.fullyqualifiedname
+ :
+ conversionPaths[destinationCurrencyId][0].destination.fullyqualifiedname;
+
+ currencies.push({
+ title,
+ description: title,
+ rightDescription: "0",
+ rightTitle,
+ key: destinationCurrencyId,
+ logo: conversionPaths[destinationCurrencyId][0].ethdest ?
+ conversionPaths[destinationCurrencyId][0].destination.mapto.currencyid
+ :
+ destinationCurrencyId
+ });
+ }
+ }
+ }
+ }
+
+ return currencies;
+ };
+
+ const getSourceNetworkOptionsList = () => {
+ const networks = [];
+
+ if (selectedSourceCurrency) {
+ for (const alias of selectedSourceCurrency) {
+ if (alias.proto === 'vrsc') {
+ if (allSubWallets[alias.id]) {
+ const networkWallets = {};
+
+ for (const subWallet of allSubWallets[alias.id]) {
+ const [channelName, addr, network] = subWallet.channel.split('.');
+
+ if (channelName === VRPC) {
+ if (!networkWallets[network]) networkWallets[network] = [subWallet.id];
+ else networkWallets[network].push(subWallet.id);
+ }
+ }
+
+ for (const network in networkWallets) {
+ try {
+ const networkObj = CoinDirectory.getBasicCoinObj(network);
+
+ if (networkObj) {
+ const rightText = getRightText(alias.id, networkWallets[network]);
+
+ networks.push({
+ title: `From ${networkObj.display_name} network`,
+ description: `as ${alias.display_ticker}`,
+ logo: networkObj.id,
+ key: networkObj.id,
+ rightTitle: rightText.title,
+ rightDescription: rightText.description
+ });
+ }
+ } catch(e) {
+ console.warn(e)
+ }
+ }
+ }
+ } else if (alias.proto === 'eth' || alias.proto === 'erc20') {
+ const rightText = getRightText(alias.id, ['MAIN_WALLET']);
+
+ networks.push({
+ title: 'From Ethereum network',
+ description: `as ${alias.display_ticker}`,
+ logo: alias.testnet ? 'GETH' : 'ETH',
+ key: alias.testnet ? 'GETH' : 'ETH',
+ rightTitle: rightText.title,
+ rightDescription: rightText.description
+ });
+ }
+ }
+ }
+
+ return networks;
+ };
+
+ const getDestNetworkOptionsList = () => {
+ const networks = [];
+
+ if (selectedSourceNetwork && selectedSourceCoinObj && selectedDestCurrencyId && conversionPaths[selectedDestCurrencyId]) {
+ const conversionOptions = conversionPaths[selectedDestCurrencyId];
+
+ const exportDests = {};
+ let hasLocalConversion = false;
+
+ for (const option of conversionOptions) {
+ let networkName;
+ let coinAlias;
+
+ let logo;
+ let key;
+ let rightTitle = formatFiatValue(0);
+ let rightDescription = "0";
+
+ function setRightText (networkId) {
+ if (selectedDestCoinObj && allSubWallets[selectedDestCoinObj.id]) {
+ const walletIds = [];
+
+ for (const wallet of allSubWallets[selectedDestCoinObj.id]) {
+ const [channelName, addr, network] = wallet.channel.split('.');
+ if (network === networkId) {
+ walletIds.push(wallet.id);
+ }
+ }
+
+ const rightText = getRightText(selectedDestCoinObj.id, walletIds);
+ rightTitle = rightText.title;
+ rightDescription = rightText.description;
+ }
+ }
+
+ function pushNetwork() {
+ networks.push({
+ title: `On ${networkName}`,
+ description: `as ${coinAlias}`,
+ logo,
+ key,
+ rightTitle,
+ rightDescription
+ })
+ }
+
+ if (option.exportto && !exportDests[option.exportto.currencyid]) {
+ exportDests[option.exportto.currencyid] = option.exportto;
+ let exportToEth = false;
+
+ try {
+ const exportNetworkObj = CoinDirectory.getBasicCoinObj(option.exportto.currencyid);
+
+ if (exportNetworkObj.mapped_to && !((exportNetworkObj.pbaas_options & IS_PBAAS_CHAIN) === IS_PBAAS_CHAIN)) {
+ const mappedObj = CoinDirectory.getBasicCoinObj(exportNetworkObj.mapped_to);
+
+ networkName = mappedObj.display_name;
+ logo = mappedObj.id
+ exportToEth = true;
+ } else {
+ networkName = exportNetworkObj.display_name;
+ logo = exportNetworkObj.id
+ }
+ } catch(e) {
+ networkName = option.exportto.fullyqualifiedname;
+ logo = 'VRSC'
+ }
+
+ if (selectedDestCoinObj) {
+ if (selectedDestCoinObj.mapped_to && exportToEth) {
+ const mappedObj = CoinDirectory.getBasicCoinObj(selectedDestCoinObj.mapped_to);
+
+ coinAlias = mappedObj.display_ticker;
+ } else {
+ coinAlias = selectedDestCoinObj.display_ticker;
+ }
+ } else {
+ coinAlias = option.ethdest ? option.destination.name : option.destination.fullyqualifiedname;
+ }
+
+ key = option.exportto.currencyid;
+
+ setRightText(option.exportto.currencyid ? option.exportto.currencyid : option.exportto.currencyid);
+ pushNetwork();
+ } else if (!hasLocalConversion) {
+ hasLocalConversion = true;
+
+ networkName = selectedSourceNetwork.display_name;
+
+ if (option.ethdest) {
+ if (selectedDestCoinObj && selectedDestCoinObj.mapped_to) {
+ try {
+ const mappedObj = CoinDirectory.getBasicCoinObj(selectedDestCoinObj.mapped_to);
+ coinAlias = mappedObj.display_ticker;
+ } catch(e) {
+ coinAlias = option.destination.name;
+ }
+ } else {
+ coinAlias = option.destination.name;
+ }
+ } else if (selectedDestCoinObj) {
+ coinAlias = selectedDestCoinObj.display_ticker;
+ } else {
+ coinAlias = option.destination.fullyqualifiedname;
+ }
+
+ logo = selectedSourceNetwork.id;
+ key = selectedSourceNetwork.currency_id;
+
+ setRightText(selectedSourceNetwork.currencyid);
+ pushNetwork();
+ }
+ }
+ }
+
+ return networks;
+ };
+
+ const getDestConverterOptionsList = () => {
+ const converters = [];
+
+ function pushConverter(title, ticker, logo, key, price) {
+ converters.push({
+ title: title,
+ description: '',
+ logo,
+ key,
+ rightTitle: price,
+ rightDescription: `Est. ${ticker} per ${selectedSourceCoinObj.display_ticker}`
+ })
+ }
+
+ if (selectedSourceNetwork && selectedDestCurrencyId && conversionPaths[selectedDestCurrencyId] && selectedDestNetworkId) {
+ for (let i = 0; i < conversionPaths[selectedDestCurrencyId].length; i++) {
+ const path = conversionPaths[selectedDestCurrencyId][i];
+
+ if ((selectedDestNetworkId === selectedSourceNetwork.currency_id && path.exportto == null) ||
+ (path.exportto && (path.exportto.currencyid === selectedDestNetworkId))) {
+ let title;
+ let logo;
+ const ticker = selectedDestCoinObj ?
+ selectedDestCoinObj.display_ticker : path.ethdest ?
+ path.mapto.destination.fullyqualifiedname : path.destination.fullyqualifiedname;
+
+ if (path.via) {
+ try {
+ const converterObj = CoinDirectory.getBasicCoinObj(path.via.currencyid);
+
+ title = `Via ${converterObj.display_name}`;
+ logo = converterObj.id;
+ } catch(e) {
+ title = `Via ${path.via.fullyqualifiedname}`;
+ logo = path.via.currencyid;
+ }
+ } else {
+ title = "Direct"
+ logo = path.ethdest ? path.destination.mapto.currencyid : path.destination.currencyid;
+ }
+
+ pushConverter(title, ticker, logo, `${selectedDestCurrencyId}:${i}`, normalizeNum(path.price, 8)[3]);
+ }
+ }
+ }
+
+ return converters;
+ };
+
+ const getSourceWalletOptionsList = () => {
+ const wallets = [];
+
+ if (selectedSourceCurrency && selectedSourceNetwork) {
+ if (selectedSourceCoinObj && allSubWallets[selectedSourceCoinObj.id]) {
+ for (const wallet of allSubWallets[selectedSourceCoinObj.id]) {
+ const [channelName, addr, network] = wallet.channel.split('.');
+
+ if (
+ channelName === ERC20 ||
+ channelName === ETH ||
+ (channelName === VRPC && selectedSourceNetwork.currency_id === network)
+ ) {
+ const rightText = getRightText(selectedSourceCoinObj.id, [wallet.id]);
+
+ wallets.push({
+ title: wallet.name,
+ description: `as ${selectedSourceCoinObj.display_ticker}`,
+ logo: selectedSourceNetwork.id,
+ key: wallet.id,
+ rightTitle: rightText.title,
+ rightDescription: rightText.description
+ })
+ }
+ }
+ }
+ }
+
+ return wallets;
+ };
+
+ const getDestAddressOptionsList = () => {
+ const addresses = [];
+
+ if (activeAccount && allSubWallets[coinsList.VRSC.id] && conversionPaths && selectedConverterId && selectedDestNetworkId) {
+ const [currencyKey, converterIndex] = selectedConverterId.split(':');
+
+ if (conversionPaths[currencyKey] && conversionPaths[currencyKey][converterIndex]) {
+ const converter = conversionPaths[currencyKey][converterIndex];
+ const currencyAlias = selectedDestCoinObj ?
+ selectedDestCoinObj.display_ticker : converter.ethdest ?
+ converter.destination.name : converter.destination.fullyqualifiedname;
+ const allowEthAddrs = converter.ethdest || (converter.exportto && converter.exportto.fullyqualifiedname === VETH);
+
+ if (!converter.ethdest) {
+ const vrscKeys = activeAccount.keys[coinsList.VRSC.id];
+
+ for (const channelId in vrscKeys) {
+ const [channelName, addr, network] = channelId.split('.');
+ let rightTitle = formatFiatValue(0);
+ let rightDescription = '0';
+
+ if (channelName === VRPC) {
+ const walletId = `SUBWALLET_${channelId}`;
+
+ if (allSubWallets[coinsList.VRSC.id]) {
+ const wallet = allSubWallets[coinsList.VRSC.id].find(x => x.id === walletId);
+
+ if (wallet) {
+ if (selectedDestCoinObj) {
+ const rightText = getRightText(selectedDestCoinObj.id, [walletId]);
+ rightTitle = rightText.title;
+ rightDescription = rightText.description;
+ }
+
+ addresses.push({
+ title: wallet.name,
+ description: `as ${currencyAlias}`,
+ logo: selectedDestNetworkId,
+ key: addr,
+ rightTitle,
+ rightDescription
+ })
+ }
+ }
+ }
+ }
+ }
+
+ if (allowEthAddrs) {
+ for (const coinId in activeAccount.keys) {
+ if (activeAccount.keys[coinId] && (activeAccount.keys[coinId][ETH] || activeAccount.keys[coinId][ERC20])) {
+ const ethAddresses = activeAccount.keys[coinId][ETH] ?
+ activeAccount.keys[coinId][ETH].addresses : activeAccount.keys[coinId][ERC20].addresses;
+
+ if (ethAddresses && ethAddresses.length > 0) {
+ const addr = ethAddresses[0];
+ const addrTitle = addr.substring(0, 8) + '...' + addr.substring(addr.length - 8);
+
+ let rightTitle = formatFiatValue(0);
+ let rightDescription = "0";
+
+ if (selectedDestCoinObj) {
+ const rightText = getRightText(selectedDestCoinObj.id, ['MAIN_WALLET']);
+ rightTitle = rightText.title;
+ rightDescription = rightText.description;
+ }
+
+ addresses.push({
+ title: addrTitle,
+ description: `as ${currencyAlias}`,
+ logo: coinsList.ETH.id,
+ key: addr,
+ rightTitle,
+ rightDescription
+ })
+
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return addresses;
+ };
+
+ const getSourceBalance = () => {
+ return balances[selectedSourceCoinObj.id][selectedSourceWallet.id].confirmed;
+ }
+
+ const fetchConversionPaths = async () => {
+ try {
+ const paths = await getConversionPaths(
+ selectedSourceCoinObj,
+ selectedSourceWallet.api_channels[API_SEND],
+ {
+ src: selectedSourceCoinObj.currency_id,
+ },
+ );
+
+ const processedPaths = {};
+ const bounceBacks = {};
+
+ for (const destId in paths) {
+ processedPaths[destId] = paths[destId].filter(x => {
+ if (x.bounceback && x.ethdest) bounceBacks[x.destination.mapto.currencyid] = x;
+
+ return !x.mapping && !x.bounceback;
+ });
+ }
+
+ for (const destId in bounceBacks) {
+ if (processedPaths[destId]) {
+ processedPaths[destId].push(bounceBacks[destId])
+ } else {
+ processedPaths[destId] = [bounceBacks[destId]]
+ }
+ }
+
+ setConversionPaths(processedPaths)
+ } catch(e) {
+ console.warn(e);
+
+ createAlert("Error", "Error fetching conversion options. Try going into the wallet for the coin you want to send, and converting through the send tab.")
+ }
+ }
+
+ const handleCurrencySelection = (key) => {
+ if (convertCardModalMode === CONVERT_CARD_MODAL_MODES.SEND) {
+ setSelectedSourceCurrency(sourceCurrencyMap[key]);
+ } else {
+ setSelectedDestCurrencyId(key);
+ }
+ }
+
+ const handleNetworkSelection = (key) => {
+ try {
+ if (convertCardModalMode === CONVERT_CARD_MODAL_MODES.SEND) {
+ const networkObj = CoinDirectory.getBasicCoinObj(key);
+
+ setSelectedSourceNetwork(networkObj);
+ } else {
+ setSelectedDestNetworkId(key);
+ }
+ } catch (e) {
+ console.warn(e)
+ }
+ }
+
+ const handleAddressSelection = (key) => {
+ try {
+ if (convertCardModalMode === CONVERT_CARD_MODAL_MODES.SEND) {
+ if (selectedSourceCoinObj) {
+ const subWallets = allSubWallets[selectedSourceCoinObj.id];
+
+ if (subWallets) {
+ const selectedWallet = subWallets.find(x => x.id === key);
+
+ setSelectedSourceWallet(selectedWallet);
+ setConvertCardModalVisible(false);
+ }
+ }
+ } else {
+ setSelectedDestAddress(key);
+ setConvertCardModalVisible(false);
+ }
+ } catch (e) {
+ console.warn(e)
+ }
+ }
+
+ const handleConverterSelection = (key) => {
+ setSelectedConverterId(key);
+ }
+
+ const handleSendSelectPressed = () => {
+ setSelectedSourceCoinObj(null);
+ setSelectedDestCoinObj(null);
+ setSelectedSourceWallet(null);
+ setSelectedSourceNetwork(null);
+ setSelectedSourceCurrency(null);
+ setConvertCardModalMode(CONVERT_CARD_MODAL_MODES.SEND);
+ setConvertCardModalVisible(true);
+ }
+
+ const handleDestSelectPressed = async () => {
+ setSelectedDestCurrencyId(null);
+
+ setLoading(true);
+ setConvertCardModalVisible(true);
+ setConvertCardModalMode(CONVERT_CARD_MODAL_MODES.RECEIVE);
+ await fetchConversionPaths();
+ setLoading(false);
+ }
+
+ useEffect(() => {
+ setTotalBalances(getTotalBalances());
+ }, [allSubWallets, activeCoinsForUser, balances, displayCurrency, rates]);
+
+ useEffect(() => {
+ if (selectedSourceCoinObj &&
+ selectedSourceWallet &&
+ balances[selectedSourceCoinObj.id] &&
+ balances[selectedSourceCoinObj.id][selectedSourceWallet.id]) {
+ setSourceBalance(getSourceBalance())
+ }
+ }, [selectedSourceCoinObj, selectedSourceWallet, balances])
+
+ useEffect(() => {
+ setSourceCurrencyMap(getSourceCurrencyMap());
+ }, [activeCoinsForUser]);
+
+ useEffect(() => {
+ setSourceCurrencyOptionsList(getSourceCurrencyOptionsList());
+ }, [sourceCurrencyMap, totalBalances]);
+
+ useEffect(() => {
+ if (conversionPaths != null && totalBalances != null && selectedSourceCoinObj != null) {
+ setDestCurrencyOptionsList(getDestCurrencyOptionsList());
+ }
+ }, [conversionPaths, totalBalances, selectedSourceCoinObj]);
+
+ useEffect(() => {
+ setSourceNetworkOptionsList(getSourceNetworkOptionsList());
+ }, [selectedSourceCurrency, totalBalances]);
+
+ useEffect(() => {
+ setDestNetworkOptionsList(getDestNetworkOptionsList());
+ }, [selectedDestCurrencyId, selectedSourceCoinObj, selectedDestCoinObj, conversionPaths]);
+
+ useEffect(() => {
+ setDestConverterOptionsList(getDestConverterOptionsList());
+ }, [selectedDestCurrencyId, selectedSourceCoinObj, selectedDestNetworkId, conversionPaths]);
+
+ useEffect(() => {
+ setDestAddressOptionsList(getDestAddressOptionsList());
+ }, [activeAccount, allSubWallets, conversionPaths, selectedConverterId, selectedDestCurrencyId, selectedSourceCoinObj, selectedDestNetworkId]);
+
+ useEffect(() => {
+ if (selectedSourceCurrency && selectedSourceNetwork) {
+ const sourceCoinObj = selectedSourceCurrency.find(x => {
+ const networkProtocol = x.proto === 'erc20' ? 'eth' : x.proto;
+ return networkProtocol === selectedSourceNetwork.proto
+ });
+
+ setSelectedSourceCoinObj(sourceCoinObj);
+ } else setSelectedSourceCoinObj(null);
+ }, [selectedSourceCurrency, selectedSourceNetwork]);
+
+ useEffect(() => {
+ if (selectedDestCurrencyId) {
+ try {
+ setSelectedDestCoinObj(CoinDirectory.findSimpleCoinObj(selectedDestCurrencyId));
+ } catch(e) {
+ setSelectedDestCoinObj(null);
+ }
+ } else setSelectedDestCoinObj(null);
+ }, [selectedDestCurrencyId]);
+
+ useEffect(() => {
+ setSourceWalletOptionsList(getSourceWalletOptionsList());
+ }, [selectedSourceCoinObj, totalBalances]);
return (
{
justifyContent: 'space-between'
}}
>
+ setConvertCardModalVisible(false)}
+ mode={convertCardModalMode}
+ totalBalances={totalBalances}
+ currencies={convertCardModalMode === CONVERT_CARD_MODAL_MODES.SEND ? sourceCurrencyOptionsList : destCurrencyOptionsList}
+ networks={convertCardModalMode === CONVERT_CARD_MODAL_MODES.SEND ? sourceNetworkOptionsList : destNetworkOptionsList}
+ converters={convertCardModalMode === CONVERT_CARD_MODAL_MODES.SEND ? null : destConverterOptionsList}
+ addresses={convertCardModalMode === CONVERT_CARD_MODAL_MODES.SEND ? sourceWalletOptionsList : destAddressOptionsList}
+ onSelectCurrency={(currencyId) => handleCurrencySelection(currencyId)}
+ onSelectNetwork={(networkId) => handleNetworkSelection(networkId)}
+ onSelectAddress={(addressKey) => handleAddressSelection(addressKey)}
+ onSelectConverter={(converterId) => handleConverterSelection(converterId)}
+ setVisible={setConvertCardModalVisible}
+ loading={loading}
+ />
-
+ setSendAmount(sourceBalance ? sourceBalance.toString() : 0)}
+ />
+ setSendAmount(sourceBalance ? sourceBalance.toString() : 0)}
+ />
{
+ setCardActive(false);
+
+ if (onSelectPressed) onSelectPressed();
+ }
+
return (
setAmount(x)}
mode="outlined"
keyboardType="numeric"
+ disabled={!setAmount}
/>
{!cardActive ?
:
-
+
{RenderCircleCoinLogo(coinObj.id)}
-
+
}
- {!cardActive ? '' : `${balance == null ? balance : '-'} ${coinObj.display_ticker}`}
+ {!cardActive ? '' : `${balance != null ? balance : '-'} ${coinObj.display_ticker}`}