diff --git a/src/components/index.ts b/src/components/index.ts
index 74b916654..0b65de469 100644
--- a/src/components/index.ts
+++ b/src/components/index.ts
@@ -10,6 +10,7 @@ export * from './Cards';
export * from './CollapsibleArrows';
export * from './CollectionBlock';
export * from './CopyButton';
+export * from './CustomNetwork';
export * from './DataDecode';
export * from './DetailItem';
export * from './ExpandRow';
diff --git a/src/config/config.devnet.ts b/src/config/config.devnet.ts
index a119d982b..953305006 100644
--- a/src/config/config.devnet.ts
+++ b/src/config/config.devnet.ts
@@ -1,7 +1,11 @@
import { NetworkType } from 'types/network.types';
+
+import { getStorageCustomNetworks } from './helpers';
import { allApps, schema } from './sharedConfig';
export * from './sharedConfig';
+export const hasExtraNetworks = true;
+
export const networks: NetworkType[] = [
{
default: true,
@@ -15,7 +19,10 @@ export const networks: NetworkType[] = [
explorerAddress: 'https://devnet-explorer.multiversx.com',
nftExplorerAddress: 'https://devnet.xspotlight.com',
apiAddress: 'https://devnet-api.multiversx.com'
- }
+ },
+
+ // Saved Custom Network Configs
+ ...getStorageCustomNetworks()
];
export const multiversxApps = allApps([
diff --git a/src/config/config.mainnet.ts b/src/config/config.mainnet.ts
index a689a950c..5ee63208a 100644
--- a/src/config/config.mainnet.ts
+++ b/src/config/config.mainnet.ts
@@ -1,4 +1,6 @@
import { NetworkType } from 'types/network.types';
+
+import { getStorageCustomNetworks } from './helpers';
import { allApps, schema } from './sharedConfig';
export * from './sharedConfig';
@@ -16,7 +18,10 @@ export const networks: NetworkType[] = [
nftExplorerAddress: 'https://xspotlight.com',
apiAddress: 'https://api.multiversx.com',
growthApi: 'https://tools.multiversx.com/growth-api'
- }
+ },
+
+ // Saved Custom Network Configs
+ ...getStorageCustomNetworks()
];
export const multiversxApps = allApps();
diff --git a/src/config/config.multiple.ts b/src/config/config.multiple.ts
index a33b3681b..8163decc5 100644
--- a/src/config/config.multiple.ts
+++ b/src/config/config.multiple.ts
@@ -1,9 +1,15 @@
import { NetworkType } from 'types/network.types';
-import { getInternalNetworks, getInternalLinks } from './helpers';
+import {
+ getInternalNetworks,
+ getStorageCustomNetworks,
+ getInternalLinks
+} from './helpers';
import { allApps, schema } from './sharedConfig';
export * from './sharedConfig';
+export const hasExtraNetworks = true;
+
export const networks: NetworkType[] = [
{
default: true,
@@ -32,7 +38,10 @@ export const networks: NetworkType[] = [
},
// Internal Testnets
- ...getInternalNetworks()
+ ...getInternalNetworks(),
+
+ // Saved Custom Network Configs
+ ...getStorageCustomNetworks()
];
export const links = getInternalLinks(networks);
diff --git a/src/config/config.testnet.ts b/src/config/config.testnet.ts
index 7e74adad2..c02021642 100644
--- a/src/config/config.testnet.ts
+++ b/src/config/config.testnet.ts
@@ -1,7 +1,11 @@
import { NetworkType } from 'types/network.types';
+
+import { getStorageCustomNetworks } from './helpers';
import { allApps, schema } from './sharedConfig';
export * from './sharedConfig';
+export const hasExtraNetworks = true;
+
export const networks: NetworkType[] = [
{
default: true,
@@ -15,7 +19,10 @@ export const networks: NetworkType[] = [
explorerAddress: 'https://testnet-explorer.multiversx.com',
nftExplorerAddress: 'https://testnet.xspotlight.com',
apiAddress: 'https://testnet-api.multiversx.com'
- }
+ },
+
+ // Saved Custom Network Configs
+ ...getStorageCustomNetworks()
];
export const multiversxApps = allApps([
diff --git a/src/config/helpers/getInternalLinks.ts b/src/config/helpers/getInternalLinks.ts
index cc681d669..8b226289e 100644
--- a/src/config/helpers/getInternalLinks.ts
+++ b/src/config/helpers/getInternalLinks.ts
@@ -1,3 +1,4 @@
+import { DEFAULT_HOSTNAME } from 'config';
import { NetworkType, NetworkUrlType } from 'types/network.types';
export const getInternalLinks = (networks: NetworkType[]): NetworkUrlType[] => {
@@ -6,12 +7,12 @@ export const getInternalLinks = (networks: NetworkType[]): NetworkUrlType[] => {
process.env.VITE_APP_SHARE_PREFIX === 'internal-'
) {
const internalLinks = networks
- .filter(({ id, name }) => id && name)
+ .filter(({ id, name, isCustom }) => id && name && !isCustom)
.map(({ id = '', name = '' }) => {
return {
id,
name,
- url: `https://${id}.${process.env.VITE_APP_SHARE_PREFIX}explorer.multiversx.com`
+ url: `https://${id}.${process.env.VITE_APP_SHARE_PREFIX}${DEFAULT_HOSTNAME}`
};
});
diff --git a/src/config/helpers/getInternalNetworks.ts b/src/config/helpers/getInternalNetworks.ts
index c2756d844..e68ba05ee 100644
--- a/src/config/helpers/getInternalNetworks.ts
+++ b/src/config/helpers/getInternalNetworks.ts
@@ -1,4 +1,4 @@
-import { NetworkType } from 'types/network.types';
+import { NetworkAdapterEnum, NetworkType } from 'types';
export const getInternalNetworks = (): NetworkType[] => {
if (process.env.VITE_APP_INTERNAL_NETWORKS) {
@@ -12,7 +12,7 @@ export const getInternalNetworks = (): NetworkType[] => {
return parsedNetworks.map((network: NetworkType) => {
return {
...network,
- ...(!network?.adapter ? { adapter: 'api' } : {}),
+ ...(!network?.adapter ? { adapter: NetworkAdapterEnum.api } : {}),
...(!network?.egldLabel ? { egldLabel: 'xEGLD' } : {}),
...(!network?.chainId ? { chainId: 'T' } : {})
};
diff --git a/src/config/helpers/getStorageCustomNetworks.ts b/src/config/helpers/getStorageCustomNetworks.ts
new file mode 100644
index 000000000..3d0e8878a
--- /dev/null
+++ b/src/config/helpers/getStorageCustomNetworks.ts
@@ -0,0 +1,53 @@
+import moment from 'moment';
+
+import { CUSTOM_NETWORK_ID } from 'appConstants';
+import { hasExtraNetworks } from 'config';
+import { cookie } from 'helpers/cookie';
+import { storage } from 'helpers/storage';
+import { NetworkAdapterEnum, NetworkType } from 'types';
+
+export const getStorageCustomNetworks = (): NetworkType[] => {
+ if (!hasExtraNetworks) {
+ return [];
+ }
+
+ try {
+ const cookieNetworks = cookie.getFromCookies(CUSTOM_NETWORK_ID);
+
+ // change custom network across sub-subdomains
+ if (cookieNetworks) {
+ try {
+ const parsedCookieNetworks = JSON.parse(cookieNetworks);
+ if (parsedCookieNetworks && parsedCookieNetworks.length > 0) {
+ const in30Days = new Date(moment().add(30, 'days').toDate());
+ storage.saveToLocal({
+ key: CUSTOM_NETWORK_ID,
+ data: JSON.stringify(parsedCookieNetworks),
+ expirationDate: in30Days
+ });
+
+ cookie.removeFromCookies(CUSTOM_NETWORK_ID);
+ }
+ } catch {}
+ }
+
+ const storageNetworks = storage.getFromLocal(CUSTOM_NETWORK_ID);
+ const parsedNetworks = JSON.parse(storageNetworks);
+
+ if (parsedNetworks && parsedNetworks.length > 0) {
+ return parsedNetworks.map((network: NetworkType) => {
+ return {
+ ...network,
+ ...(!network?.adapter ? { adapter: NetworkAdapterEnum.api } : {}),
+ ...(!network?.egldLabel ? { egldLabel: 'xEGLD' } : {}),
+ ...(!network?.chainId ? { chainId: 'T' } : {}),
+ isCustom: true
+ };
+ });
+ }
+ } catch {
+ return [];
+ }
+
+ return [];
+};
diff --git a/src/config/helpers/index.ts b/src/config/helpers/index.ts
index 0aa015e6e..8cb7c6a5e 100644
--- a/src/config/helpers/index.ts
+++ b/src/config/helpers/index.ts
@@ -1,2 +1,3 @@
export * from './getInternalLinks';
export * from './getInternalNetworks';
+export * from './getStorageCustomNetworks';
diff --git a/src/config/sharedConfig.ts b/src/config/sharedConfig.ts
index 83c36a81f..bddd0b56d 100644
--- a/src/config/sharedConfig.ts
+++ b/src/config/sharedConfig.ts
@@ -31,6 +31,11 @@ export const SHARE_PREFIX = process.env.VITE_APP_SHARE_PREFIX
? process.env.VITE_APP_SHARE_PREFIX.replace('-', '')
: '';
+export const DEFAULT_HOSTNAME =
+ process.env.VITE_APP_DEFAULT_HOSTNAME ?? 'explorer.multiversx.com';
+
+export const hasExtraNetworks = false;
+
export const links: NetworkUrlType[] = [
{
id: 'mainnet',
diff --git a/src/helpers/cookie.ts b/src/helpers/cookie.ts
new file mode 100644
index 000000000..5cbefd48b
--- /dev/null
+++ b/src/helpers/cookie.ts
@@ -0,0 +1,38 @@
+import { CUSTOM_NETWORK_ID } from 'appConstants';
+import { DEFAULT_HOSTNAME } from 'config/sharedConfig';
+
+type KeyType = typeof CUSTOM_NETWORK_ID;
+
+const domain = `domain=.${process.env.VITE_APP_SHARE_PREFIX}${DEFAULT_HOSTNAME}`;
+
+export const cookie = {
+ saveToCookies: ({
+ key,
+ data,
+ expirationDate
+ }: {
+ key: KeyType;
+ data: string;
+ expirationDate: Date;
+ }) => {
+ const expires = `expires=${expirationDate.toUTCString()}`;
+ document.cookie = `${key}=${data}; ${expires}; ${domain}; path=/;`;
+ },
+ getFromCookies: (key: KeyType) => {
+ const name = key + '=';
+ const cookies = document.cookie.split(';');
+ for (let i = 0; i < cookies.length; i++) {
+ let c = cookies[i];
+ while (c.charAt(0) == ' ') {
+ c = c.substring(1);
+ }
+ if (c.indexOf(name) == 0) {
+ return c.substring(name.length, c.length);
+ }
+ }
+ return '';
+ },
+ removeFromCookies: (key: KeyType) => {
+ document.cookie = `${key}=; expires=Thu, 01 Jan 1970 00:00:00 UTC; ${domain}; path=/;`;
+ }
+};
diff --git a/src/helpers/getValue/getItemsPage.ts b/src/helpers/getValue/getItemsPage.ts
new file mode 100644
index 000000000..822ce3022
--- /dev/null
+++ b/src/helpers/getValue/getItemsPage.ts
@@ -0,0 +1,48 @@
+import BigNumber from 'bignumber.js';
+
+interface GetItemsPageType {
+ currentPage: number;
+ itemsPerPage: number;
+ items: any[];
+}
+
+export const getItemsPage = ({
+ items,
+ currentPage,
+ itemsPerPage
+}: GetItemsPageType) => {
+ const itemsPerPageBigNumber = new BigNumber(itemsPerPage);
+ const currentPageBigNumber = new BigNumber(currentPage);
+ const itemsLengthBigNumber = new BigNumber(items.length);
+
+ const totalPages = Math.ceil(
+ itemsLengthBigNumber.dividedBy(itemsPerPage).toNumber()
+ );
+
+ const totalPagesArray = Array.from({ length: totalPages });
+ const ranges = totalPagesArray.map((_, index) => [
+ itemsPerPageBigNumber.times(index),
+ itemsPerPageBigNumber.times(new BigNumber(index).plus(1))
+ ]);
+
+ const rangesLengthBigNumber = new BigNumber(ranges.length);
+ const currentRange = ranges.find((_, index) => {
+ if (rangesLengthBigNumber.lte(currentPage)) {
+ return rangesLengthBigNumber.minus(1).isEqualTo(index);
+ }
+
+ return currentPageBigNumber.minus(1).isEqualTo(index);
+ });
+
+ if (!currentRange) {
+ return items;
+ }
+
+ const [currentRangeStart, currentRangeEnd] = currentRange;
+ const slicedTokensArray = items.slice(
+ currentRangeStart.toNumber(),
+ currentRangeEnd.toNumber()
+ );
+
+ return slicedTokensArray;
+};
diff --git a/src/helpers/getValue/index.ts b/src/helpers/getValue/index.ts
index f5ebe40aa..f2b5ed3ba 100644
--- a/src/helpers/getValue/index.ts
+++ b/src/helpers/getValue/index.ts
@@ -4,6 +4,7 @@ export * from './getAccountStakingDetails';
export * from './getAccountValidatorStakeDetails';
export * from './getColors';
export * from './getDisplayReceiver';
+export * from './getItemsPage';
export * from './getNftText';
export * from './getNodeIcon';
export * from './getNodeIssue';
diff --git a/src/helpers/index.ts b/src/helpers/index.ts
index 056f1094a..4cd02b7a2 100644
--- a/src/helpers/index.ts
+++ b/src/helpers/index.ts
@@ -5,6 +5,7 @@ export * from './amountWithoutRounding';
export * from './analytics';
export * from './capitalize';
export * from './capitalizeFirstLetter';
+export * from './cookie';
export * from './copyToClipboard';
export * from './downloadFile';
export * from './formatValue';
@@ -15,9 +16,11 @@ export * from './isEllipsisActive';
export * from './isHash';
export * from './isMetachain';
export * from './isUtf8';
+export * from './isValidTokenValue';
export * from './parseAmount';
export * from './parseJwt';
export * from './partitionBy';
+export * from './scrollToElement';
export * from './processData';
export * from './storage';
export * from './stringIsFloat';
diff --git a/src/helpers/isValidTokenValue.ts b/src/helpers/isValidTokenValue.ts
new file mode 100644
index 000000000..4252f7767
--- /dev/null
+++ b/src/helpers/isValidTokenValue.ts
@@ -0,0 +1,14 @@
+import BigNumber from 'bignumber.js';
+
+import { LOW_LIQUIDITY_DISPLAY_TRESHOLD } from 'appConstants';
+import { TokenType } from 'types';
+
+export const isValidTokenValue = (token: TokenType) => {
+ return Boolean(
+ token.valueUsd &&
+ (!token.isLowLiquidity ||
+ new BigNumber(token.valueUsd).isLessThan(
+ LOW_LIQUIDITY_DISPLAY_TRESHOLD
+ ))
+ );
+};
diff --git a/src/helpers/scrollToElement.ts b/src/helpers/scrollToElement.ts
new file mode 100644
index 000000000..1f6c066eb
--- /dev/null
+++ b/src/helpers/scrollToElement.ts
@@ -0,0 +1,12 @@
+export const scrollToElement = (selector: string, timeout?: number) => {
+ const activeElement = document.querySelector(selector);
+ setTimeout(() => {
+ if (activeElement) {
+ activeElement.scrollIntoView({
+ behavior: 'smooth',
+ block: 'start',
+ inline: 'start'
+ });
+ }
+ }, timeout);
+};
diff --git a/src/helpers/storage.ts b/src/helpers/storage.ts
index d2e7d1de4..4ca8428cc 100644
--- a/src/helpers/storage.ts
+++ b/src/helpers/storage.ts
@@ -1,9 +1,13 @@
import moment from 'moment';
-import { TEMP_LOCAL_NOTIFICATION_DISMISSED } from 'appConstants';
+import {
+ TEMP_LOCAL_NOTIFICATION_DISMISSED,
+ CUSTOM_NETWORK_ID
+} from 'appConstants';
type KeyType =
| 'theme'
| 'accessToken'
+ | typeof CUSTOM_NETWORK_ID
| typeof TEMP_LOCAL_NOTIFICATION_DISMISSED;
export const storage = {
diff --git a/src/hooks/adapter/adapter.ts b/src/hooks/adapter/adapter.ts
index 6b619de34..2207e898c 100644
--- a/src/hooks/adapter/adapter.ts
+++ b/src/hooks/adapter/adapter.ts
@@ -739,6 +739,13 @@ export const useAdapter = () => {
provider({ baseUrl: `${growthApi}/explorer/widgets`, url }),
getGrowthHeaders: (url: string) =>
- provider({ baseUrl: `${growthApi}/explorer/headers`, url })
+ provider({ baseUrl: `${growthApi}/explorer/headers`, url }),
+
+ // Network Config
+ getNetworkConfig: (baseUrl: string) =>
+ provider({
+ baseUrl,
+ url: '/dapp/config'
+ })
};
};
diff --git a/src/hooks/adapter/useAdapterConfig.ts b/src/hooks/adapter/useAdapterConfig.ts
index f33076d51..33575cba1 100644
--- a/src/hooks/adapter/useAdapterConfig.ts
+++ b/src/hooks/adapter/useAdapterConfig.ts
@@ -3,6 +3,7 @@ import { useSelector } from 'react-redux';
import { METACHAIN_SHARD_ID, TIMEOUT } from 'appConstants';
import { activeNetworkSelector } from 'redux/selectors';
import {
+ NetworkAdapterEnum,
AdapterProviderPropsType,
ApiAdapterResponseType
} from 'types/adapter.types';
@@ -56,7 +57,7 @@ export const useAdapterConfig = () => {
}
};
- const adapter: 'api' | 'elastic' = networkAdapter as any;
+ const adapter = networkAdapter as NetworkAdapterEnum;
const {
provider,
diff --git a/src/hooks/index.ts b/src/hooks/index.ts
index fc4aa4f87..999449b40 100644
--- a/src/hooks/index.ts
+++ b/src/hooks/index.ts
@@ -5,9 +5,11 @@ export * from './pageStats';
export * from './urlFilters';
export * from './useActiveRoute';
export * from './useCheckVersion';
+export * from './useCustomNetwork';
export * from './useDebounce';
export * from './useGetExplorerTitle';
export * from './useGetHash';
+export * from './useGetNetworkChangeLink';
export * from './useGetNodesCategoryCount';
export * from './useGetShardText';
export * from './useGetEpochRemainingTime';
diff --git a/src/hooks/useCustomNetwork.ts b/src/hooks/useCustomNetwork.ts
new file mode 100644
index 000000000..5a5bdce32
--- /dev/null
+++ b/src/hooks/useCustomNetwork.ts
@@ -0,0 +1,132 @@
+import { useEffect, useState } from 'react';
+import moment from 'moment';
+import { useSelector } from 'react-redux';
+
+import { CUSTOM_NETWORK_ID } from 'appConstants';
+import { networks } from 'config';
+import { cookie, storage, getSubdomainNetwork } from 'helpers';
+import { useAdapter, useGetNetworkChangeLink } from 'hooks';
+import { activeNetworkSelector } from 'redux/selectors';
+import { DappNetworkConfigType, NetworkType, NetworkAdapterEnum } from 'types';
+
+export interface CustomNetworkErrorType {
+ apiAddress?: string;
+ chainId?: string;
+ adapter?: string;
+ egldLabel?: string;
+ explorerAddress?: string;
+}
+
+const validateUrl = (url: string) => {
+ if (!url) {
+ return 'Required';
+ }
+
+ try {
+ new URL(url);
+ return '';
+ } catch (err) {
+ return 'Invalid Url';
+ }
+};
+
+export const useCustomNetwork = (customUrl: string) => {
+ const { getNetworkConfig } = useAdapter();
+ const getNetworkChangeLink = useGetNetworkChangeLink();
+ const { isSubSubdomain } = getSubdomainNetwork();
+ const activeNetwork = useSelector(activeNetworkSelector);
+
+ const { isCustom: activeNetworkIsCustom } = activeNetwork;
+ const configCustomNetwork = networks.filter((network) => network.isCustom)[0];
+
+ const existingNetwork = activeNetworkIsCustom
+ ? activeNetwork
+ : configCustomNetwork;
+
+ const [isSaving, setIsSaving] = useState
();
+ const [errors, setErrors] = useState();
+ const [customNetworkConfig, setCustomNetworkConfig] = useState<
+ NetworkType | undefined
+ >(existingNetwork);
+
+ const setCustomNetwork = async () => {
+ setIsSaving(true);
+ setErrors(undefined);
+ const urlError = validateUrl(customUrl);
+ if (urlError) {
+ setErrors((errors) => {
+ return { ...errors, apiAddress: urlError };
+ });
+ setIsSaving(false);
+ return;
+ }
+
+ const apiAddress = new URL(customUrl).toString().replace(/\/+$/, '');
+
+ const { data, success } = await getNetworkConfig(apiAddress);
+ if (data && success) {
+ const { chainId, egldLabel, explorerAddress, walletAddress, name } =
+ data as DappNetworkConfigType;
+
+ if (chainId && egldLabel && walletAddress && explorerAddress) {
+ const customNetwork = {
+ id: CUSTOM_NETWORK_ID,
+ name: `Custom ${name ?? 'Network'}`,
+ adapter: NetworkAdapterEnum.api,
+ theme: 'testnet',
+ isCustom: true,
+ apiAddress,
+ chainId,
+ egldLabel,
+ walletAddress,
+ explorerAddress
+ };
+
+ try {
+ const in2Minutes = new Date(moment().add(2, 'minutes').toDate());
+ const in30Days = new Date(moment().add(30, 'days').toDate());
+ const configData = {
+ key: CUSTOM_NETWORK_ID as typeof CUSTOM_NETWORK_ID,
+ data: JSON.stringify([customNetwork]),
+ expirationDate: isSubSubdomain ? in2Minutes : in30Days
+ };
+ if (isSubSubdomain) {
+ cookie.saveToCookies(configData);
+ } else {
+ storage.saveToLocal(configData);
+ }
+ } catch (error) {
+ console.error('Unable to Save Custom Network: ', error);
+ setErrors((errors) => {
+ return { ...errors, apiAddress: 'Unable to Save Custom Network' };
+ });
+ setIsSaving(false);
+ return;
+ }
+
+ setCustomNetworkConfig(customNetwork);
+ setIsSaving(false);
+
+ // we want to reset the whole state, react router's navigate might lead to unwanted innacuracies
+ window.location.href = getNetworkChangeLink({
+ networkId: CUSTOM_NETWORK_ID
+ });
+
+ return;
+ }
+ }
+
+ setErrors((errors) => {
+ return { ...errors, apiAddress: 'Invalid API Config' };
+ });
+ setIsSaving(false);
+ };
+
+ useEffect(() => {
+ if (customUrl) {
+ setIsSaving(false);
+ }
+ }, [customUrl]);
+
+ return { setCustomNetwork, isSaving, customNetworkConfig, errors };
+};
diff --git a/src/hooks/useGetNetworkChangeLink.ts b/src/hooks/useGetNetworkChangeLink.ts
new file mode 100644
index 000000000..37ed220d7
--- /dev/null
+++ b/src/hooks/useGetNetworkChangeLink.ts
@@ -0,0 +1,20 @@
+import { useSelector } from 'react-redux';
+
+import { getSubdomainNetwork } from 'helpers';
+import { defaultNetworkSelector } from 'redux/selectors';
+
+export const useGetNetworkChangeLink = () => {
+ const { id: defaultNetworkId } = useSelector(defaultNetworkSelector);
+ const { isSubSubdomain } = getSubdomainNetwork();
+
+ const getNetworkChangeLink = ({ networkId }: { networkId?: string }) => {
+ if (isSubSubdomain && window?.location?.hostname) {
+ const [_omit, ...rest] = window.location.hostname.split('.');
+ return `https://${[networkId, ...rest].join('.')}`;
+ }
+
+ return networkId === defaultNetworkId ? '/' : `/${networkId}`;
+ };
+
+ return getNetworkChangeLink;
+};
diff --git a/src/icons/regular/fontawesomeFree.ts b/src/icons/regular/fontawesomeFree.ts
index f4383e9f4..cd69fa66e 100644
--- a/src/icons/regular/fontawesomeFree.ts
+++ b/src/icons/regular/fontawesomeFree.ts
@@ -110,6 +110,7 @@ import {
faStream,
faSync,
faTimes,
+ faTrash,
faTrophy,
faUpLong as faUp,
faUserCheck,
@@ -228,6 +229,7 @@ export {
faSync,
faTerminal,
faTimes,
+ faTrash,
faTrophy,
faUp,
faUpRight,
diff --git a/src/icons/regular/fontawesomePro.ts b/src/icons/regular/fontawesomePro.ts
index 3f6d4c58c..4213aaa2f 100644
--- a/src/icons/regular/fontawesomePro.ts
+++ b/src/icons/regular/fontawesomePro.ts
@@ -108,6 +108,7 @@ import {
faSync,
faTerminal,
faTimes,
+ faTrash,
faTrophy,
faUp,
faUpRight,
@@ -230,6 +231,7 @@ export {
faSync,
faTerminal,
faTimes,
+ faTrash,
faTrophy,
faUp,
faUpRight,
diff --git a/src/icons/solid/fontawesomeFree.ts b/src/icons/solid/fontawesomeFree.ts
index 3bdd499b0..2ce86b230 100644
--- a/src/icons/solid/fontawesomeFree.ts
+++ b/src/icons/solid/fontawesomeFree.ts
@@ -107,6 +107,7 @@ import {
faStream,
faSync,
faTimes,
+ faTrash,
faTrophy,
faUpLong as faUp,
faUser,
@@ -226,6 +227,7 @@ export {
faSync,
faTerminal,
faTimes,
+ faTrash,
faTrophy,
faUp,
faUpRight,
diff --git a/src/icons/solid/fontawesomePro.ts b/src/icons/solid/fontawesomePro.ts
index b43b034b6..6b699d5ca 100644
--- a/src/icons/solid/fontawesomePro.ts
+++ b/src/icons/solid/fontawesomePro.ts
@@ -108,6 +108,7 @@ import {
faSync,
faTerminal,
faTimes,
+ faTrash,
faTrophy,
faUp,
faUpRight,
@@ -230,6 +231,7 @@ export {
faSync,
faTerminal,
faTimes,
+ faTrash,
faTrophy,
faUp,
faUpRight,
diff --git a/src/layouts/AccountLayout/AccountDetailsCard/AccountDetailsCard.tsx b/src/layouts/AccountLayout/AccountDetailsCard/AccountDetailsCard.tsx
index d2af317b8..6960b3f28 100644
--- a/src/layouts/AccountLayout/AccountDetailsCard/AccountDetailsCard.tsx
+++ b/src/layouts/AccountLayout/AccountDetailsCard/AccountDetailsCard.tsx
@@ -1,13 +1,8 @@
import React, { useEffect } from 'react';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import BigNumber from 'bignumber.js';
import { useDispatch, useSelector } from 'react-redux';
-import {
- ELLIPSIS,
- MAX_ACOUNT_TOKENS_BALANCE,
- LOW_LIQUIDITY_DISPLAY_TRESHOLD
-} from 'appConstants';
+import { ELLIPSIS, MAX_ACOUNT_TOKENS_BALANCE } from 'appConstants';
import { NativeTokenSymbol } from 'components';
import {
CardItem,
@@ -22,7 +17,8 @@ import {
urlBuilder,
formatHerotag,
formatBigNumber,
- getTotalTokenUsdValue
+ getTotalTokenUsdValue,
+ isValidTokenValue
} from 'helpers';
import { useAdapter, useIsSovereign } from 'hooks';
import { faClock, faExclamationTriangle } from 'icons/regular';
@@ -118,12 +114,7 @@ export const AccountDetailsCard = () => {
}
if (accountTokensValueData.success) {
const validTokenValues = accountTokensValueData.data.filter(
- (token: TokenType) =>
- token.valueUsd &&
- (!token.isLowLiquidity ||
- new BigNumber(token.valueUsd).isLessThan(
- LOW_LIQUIDITY_DISPLAY_TRESHOLD
- ))
+ (token: TokenType) => isValidTokenValue(token)
);
const tokenBalance = getTotalTokenUsdValue(validTokenValues);
accountExtraDetails.tokenBalance = tokenBalance;
diff --git a/src/layouts/Layout/components/Header/components/EcosystemMenu/ecosystemMenu.styles.scss b/src/layouts/Layout/components/Header/components/EcosystemMenu/ecosystemMenu.styles.scss
index 0946a3c59..0b373b0ef 100644
--- a/src/layouts/Layout/components/Header/components/EcosystemMenu/ecosystemMenu.styles.scss
+++ b/src/layouts/Layout/components/Header/components/EcosystemMenu/ecosystemMenu.styles.scss
@@ -2,7 +2,6 @@
display: flex;
flex-direction: column;
margin-top: 0;
- overflow: hidden;
&-item {
display: block;
diff --git a/src/layouts/Layout/components/Header/components/Switcher/Switcher.tsx b/src/layouts/Layout/components/Header/components/Switcher/Switcher.tsx
index f50b9fb14..ac477844c 100644
--- a/src/layouts/Layout/components/Header/components/Switcher/Switcher.tsx
+++ b/src/layouts/Layout/components/Header/components/Switcher/Switcher.tsx
@@ -3,55 +3,34 @@ import classNames from 'classnames';
import { Anchor, Dropdown } from 'react-bootstrap';
import { useSelector } from 'react-redux';
-import { networks, links } from 'config';
+import { CustomNetworkMenu } from 'components';
+import { networks, links, hasExtraNetworks } from 'config';
import { getSubdomainNetwork } from 'helpers';
+import { useGetNetworkChangeLink } from 'hooks';
import { faAngleDown } from 'icons/solid';
-
-import { activeNetworkSelector, defaultNetworkSelector } from 'redux/selectors';
+import { activeNetworkSelector } from 'redux/selectors';
export const Switcher = () => {
const { id: activeNetworkId, name: activeNetworkName } = useSelector(
activeNetworkSelector
);
- const { id: defaultNetworkId } = useSelector(defaultNetworkSelector);
const { isSubSubdomain } = getSubdomainNetwork();
+ const getNetworkChangeLink = useGetNetworkChangeLink();
- const networkLinks = networks.map(({ name, id }) => {
- let url = id === defaultNetworkId ? '/' : `/${id}`;
- if (isSubSubdomain && window?.location?.hostname) {
- const [_omit, ...rest] = window.location.hostname.split('.');
- url = `https://${[id, ...rest].join('.')}`;
- }
- return {
- name,
- url,
- id
- };
- });
-
- return (
-
-
- {activeNetworkName}
-
-
+ const networkLinks = networks
+ .filter((network) => !network.isCustom)
+ .map(({ name, id }) => {
+ const url = getNetworkChangeLink({ networkId: id });
+ return {
+ name,
+ url,
+ id
+ };
+ });
-
- {token.valueUsd &&
- (!token.isLowLiquidity ||
- new BigNumber(token.valueUsd).isLessThan(
- LOW_LIQUIDITY_DISPLAY_TRESHOLD
- )) && (
-