diff --git a/src/components/ChainSelector.tsx b/src/components/ChainSelector.tsx index 10256b9a8d..04392a7081 100644 --- a/src/components/ChainSelector.tsx +++ b/src/components/ChainSelector.tsx @@ -9,18 +9,18 @@ import UnstyledNetworkIcon from '~/shared/components/NetworkIcon' import SvgIcon from '~/shared/components/SvgIcon' import { COLORS, LAPTOP } from '~/shared/utils/styled' import { StreamDraft } from '~/stores/streamDraft' -import { getChainSlug, useCurrentChain } from '~/utils/chains' +import { getChainDisplayName, getChainSlug, useCurrentChain } from '~/utils/chains' type MenuItemProps = { - chain: Chain + chainId: number isSelected: boolean onClick: () => void } -const MenuItem = ({ chain, isSelected, onClick }: MenuItemProps) => ( +const MenuItem = ({ chainId, isSelected, onClick }: MenuItemProps) => ( - -
{chain.name}
+ +
{getChainDisplayName(chainId)}
{isSelected ? :
} ) @@ -40,7 +40,7 @@ const Menu = ({ chains, selectedChain, toggle }: MenuProps) => { {chains.map((c) => ( { toggle(false) @@ -67,7 +67,7 @@ interface Props { } export const ChainSelector = ({ menuAlignment = 'left', ...props }: Props) => { - const availableChains = getEnvironmentConfig().availableChains + const { availableChains } = getEnvironmentConfig() const selectedChain = useCurrentChain() @@ -89,7 +89,7 @@ export const ChainSelector = ({ menuAlignment = 'left', ...props }: Props) => { {(toggle, isOpen) => ( toggle((v) => !v)}> -
{selectedChain.name}
+
{getChainDisplayName(selectedChain.id)}
)} diff --git a/src/components/SalePointSelector/SalePointOption.tsx b/src/components/SalePointSelector/SalePointOption.tsx index 7a4fe1b3c5..1366992a2c 100644 --- a/src/components/SalePointSelector/SalePointOption.tsx +++ b/src/components/SalePointSelector/SalePointOption.tsx @@ -1,17 +1,16 @@ +import { produce } from 'immer' import React, { ComponentProps, ReactNode, useEffect, useRef, useState } from 'react' import styled, { css } from 'styled-components' import { z } from 'zod' -import { produce } from 'immer' -import SvgIcon from '~/shared/components/SvgIcon' -import { SalePoint } from '~/shared/types' -import { COLORS } from '~/shared/utils/styled' +import { getDataUnion, getDataUnionsOwnedByInChain } from '~/getters/du' +import { SelectField2 } from '~/marketplace/components/SelectField2' import { Tick as PrestyledTick } from '~/shared/components/Checkbox' import NetworkIcon from '~/shared/components/NetworkIcon' -import { formatChainName } from '~/utils' +import SvgIcon from '~/shared/components/SvgIcon' import { useWalletAccount } from '~/shared/stores/wallet' -import { SelectField2 } from '~/marketplace/components/SelectField2' -import { getDataUnion, getDataUnionsOwnedByInChain } from '~/getters/du' -import { getChainConfig } from '~/utils/chains' +import { SalePoint } from '~/shared/types' +import { COLORS } from '~/shared/utils/styled' +import { getChainDisplayName } from '~/utils/chains' import { Root as SalePointTokenSelectorRoot } from './SalePointTokenSelector' export interface OptionProps { @@ -32,10 +31,6 @@ export default function SalePointOption({ }: SalePointOptionProps) { const { chainId, enabled, readOnly } = salePoint - const chain = getChainConfig(chainId) - - const formattedChainName = formatChainName(chain.name) - return ( )} - {formattedChainName} + {getChainDisplayName(chainId)} {multiSelect && } diff --git a/src/config/chains.toml b/src/config/chains.toml index b7602ffd2d..6bd4cc8c4e 100644 --- a/src/config/chains.toml +++ b/src/config/chains.toml @@ -1,11 +1,28 @@ +[ethereum] +displayName = "Ethereum Mainnet" + +[binance] +coingeckoNetworkId = "binance-smart-chain" +displayName = "BNB Smart Chain" + +[gnosis] +coingeckoNetworkId = "xdai" +displayName = "Gnosis" + [dev0] +coingeckoNetworkId = "ethereum" dockerHost = "localhost" +displayName = "Dev0" [dev1] +coingeckoNetworkId = "ethereum" dockerHost = "localhost" +displayName = "Dev1" [dev2] +coingeckoNetworkId = "ethereum" dataUnionJoinServerUrl = ":5555" +displayName = "Dev2" dockerHost = "localhost" marketplaceChains = ['dev2'] networkSubgraphUrl = ":8800/subgraphs/name/streamr-dev/network-subgraphs" @@ -21,6 +38,7 @@ name = "streamr-dev/dataunion" [polygonAmoy] dataUnionJoinServerUrl = "https://join.dataunions.org/" +displayName = "Amoy" marketplaceChains = ['polygonAmoy'] slug = 'amoy' diff --git a/src/modals/AccessPeriodModal.tsx b/src/modals/AccessPeriodModal.tsx index 11d75adbd2..db0ef44b76 100644 --- a/src/modals/AccessPeriodModal.tsx +++ b/src/modals/AccessPeriodModal.tsx @@ -10,9 +10,8 @@ import Text from '~/shared/components/Ui/Text' import useIsMounted from '~/shared/hooks/useIsMounted' import { COLORS, LIGHT, MEDIUM } from '~/shared/utils/styled' import { TimeUnit, timeUnits } from '~/shared/utils/timeUnit' -import { formatChainName } from '~/utils' import { toFloat } from '~/utils/bn' -import { getChainConfig } from '~/utils/chains' +import { getChainDisplayName } from '~/utils/chains' import { RejectionReason } from '~/utils/exceptions' import { convertPrice } from '~/utils/price' import ProjectModal, { Actions } from './ProjectModal' @@ -202,8 +201,6 @@ export default function AccessPeriodModal({ setSelectedUnit(unit) }, [unit]) - const chainName = formatChainName(getChainConfig(chainId).name) - const total = ((a: bigint) => (a > 0n ? a : 0n))( convertPrice(pricePerSecond, [length, selectedUnit]), ) @@ -289,7 +286,7 @@ export default function AccessPeriodModal({ - {chainName} + {getChainDisplayName(chainId)} diff --git a/src/modals/ChainSelectorModal.tsx b/src/modals/ChainSelectorModal.tsx index 9a937bf3ac..d5e355ff0c 100644 --- a/src/modals/ChainSelectorModal.tsx +++ b/src/modals/ChainSelectorModal.tsx @@ -6,9 +6,8 @@ import NetworkIcon from '~/shared/components/NetworkIcon' import useIsMounted from '~/shared/hooks/useIsMounted' import { getUsdRate } from '~/shared/utils/coingecko' import { MEDIUM } from '~/shared/utils/styled' -import { formatChainName } from '~/utils' import { getBalance } from '~/utils/balance' -import { getChainConfig } from '~/utils/chains' +import { getChainDisplayName } from '~/utils/chains' import { RejectionReason } from '~/utils/exceptions' import networkPreflight from '~/utils/networkPreflight' import { getTokenInfo } from '~/utils/tokens' @@ -241,9 +240,7 @@ export default function ChainSelectorModal({ } > - - {formatChainName(getChainConfig(chainId).name)} - + {getChainDisplayName(chainId)} diff --git a/src/modals/SwitchNetworkModal.tsx b/src/modals/SwitchNetworkModal.tsx index 234f502a4d..26036e7895 100644 --- a/src/modals/SwitchNetworkModal.tsx +++ b/src/modals/SwitchNetworkModal.tsx @@ -2,28 +2,26 @@ import React from 'react' import styled from 'styled-components' import { Buttons } from '~/components/Buttons' import PngIcon from '~/shared/components/PngIcon' -import { getChainConfig } from '~/utils/chains' +import { getChainDisplayName, getChainKey, isKnownChainId } from '~/utils/chains' import { RejectionReason } from '~/utils/exceptions' import { Footer } from './BaseModal' import Modal, { ModalProps } from './Modal' interface Props extends Pick { - expectedNetwork: number | string - actualNetwork: number | string + expectedChainId: number + actualChainId: number onResolve?: () => void } -function getChainName(chainId: number | string) { - try { - return getChainConfig(chainId).name - } catch (_) { - return `#${chainId}` - } +function getChainName(chainId: number) { + return isKnownChainId(chainId) + ? getChainDisplayName(getChainKey(chainId)) + : `#${chainId}` } export default function SwitchNetworkModal({ - expectedNetwork, - actualNetwork, + expectedChainId, + actualChainId, onReject, onResolve, ...props @@ -47,9 +45,9 @@ export default function SwitchNetworkModal({

- Please switch to the {getChainName(expectedNetwork)} network + Please switch to the {getChainName(expectedChainId)} network in your Ethereum wallet. It's currently in{' '} - {getChainName(actualNetwork)} + {getChainName(actualChainId)}  network.

diff --git a/src/pages/ProjectPage/AccessManifest.tsx b/src/pages/ProjectPage/AccessManifest.tsx index 41fadaaa98..a2540af2ba 100644 --- a/src/pages/ProjectPage/AccessManifest.tsx +++ b/src/pages/ProjectPage/AccessManifest.tsx @@ -13,8 +13,11 @@ import { ProjectType, SalePoint } from '~/shared/types' import { REGULAR, TABLET } from '~/shared/utils/styled' import { timeUnits } from '~/shared/utils/timeUnit' import { useIsAccessibleByCurrentWallet } from '~/stores/projectDraft' -import { formatChainName } from '~/utils' -import { getChainConfig, useCurrentChainId, useCurrentChainKey } from '~/utils/chains' +import { + getChainDisplayName, + useCurrentChainId, + useCurrentChainKey, +} from '~/utils/chains' import { Route as R, routeOptions } from '~/utils/routes' import { errorToast } from '~/utils/toast' @@ -65,7 +68,7 @@ export function AccessManifest({ timeUnit={timeUnits.hour} /> {' '} - on {formatChainName(getChainConfig(chainId).name)} + on {getChainDisplayName(chainId)} {count > 0 && ( <> {' '} diff --git a/src/pages/ProjectPage/GetAccess.tsx b/src/pages/ProjectPage/GetAccess.tsx index 8927af78ca..f4818ffc2c 100644 --- a/src/pages/ProjectPage/GetAccess.tsx +++ b/src/pages/ProjectPage/GetAccess.tsx @@ -12,8 +12,7 @@ import { import { ProjectType, SalePoint } from '~/shared/types' import { MEDIUM } from '~/shared/utils/styled' import { timeUnits } from '~/shared/utils/timeUnit' -import { formatChainName } from '~/utils' -import { getChainConfig, useCurrentChainId } from '~/utils/chains' +import { getChainDisplayName, useCurrentChainId } from '~/utils/chains' import { errorToast } from '~/utils/toast' const GetAccessContainer = styled.div` @@ -90,7 +89,7 @@ export default function GetAccess({ timeUnit={timeUnits.hour} /> {' '} - on {formatChainName(getChainConfig(chainId).name)} + on {getChainDisplayName(chainId)} {count > 0 && ( <> and on {count} other chain{count > 1 && 's'} diff --git a/src/pages/ProjectPage/ProjectEditorPage.tsx b/src/pages/ProjectPage/ProjectEditorPage.tsx index 332ed22095..d195f853e9 100644 --- a/src/pages/ProjectPage/ProjectEditorPage.tsx +++ b/src/pages/ProjectPage/ProjectEditorPage.tsx @@ -25,10 +25,10 @@ import useIsMounted from '~/shared/hooks/useIsMounted' import { ProjectType, SalePoint } from '~/shared/types' import { ProjectDraft } from '~/stores/projectDraft' import { SalePointsPayload } from '~/types/projects' -import { formatChainName } from '~/utils' import { - getChainConfig, - getChainConfigExtension, + getChainDisplayName, + getChainKey, + getMarketplaceChainConfigs, useCurrentChainId, } from '~/utils/chains' import { Route as R, routeOptions } from '~/utils/routes' @@ -59,15 +59,13 @@ export default function ProjectEditorPage() { const chainId = useCurrentChainId() - const availableChains = useMemo( - () => getChainConfigExtension(chainId).marketplaceChains.map(getChainConfig), + const availableChainConfigs = useMemo( + () => getMarketplaceChainConfigs(chainId), [chainId], ) - const salePoints = availableChains - .map( - ({ name: chainName }) => existingSalePoints[chainName], - ) + const salePoints = availableChainConfigs + .map(({ id }) => existingSalePoints[getChainKey(id)]) .filter(Boolean) as SalePoint[] function onSalePointChange(value: SalePoint) { @@ -76,16 +74,16 @@ export default function ProjectEditorPage() { } update((draft) => { - const { name: chainName } = getChainConfig(value.chainId) + const chainKey = getChainKey(value.chainId) - if (draft.salePoints[chainName]?.readOnly) { + if (draft.salePoints[chainKey]?.readOnly) { /** * Read-only sale point must not be updated. */ return } - draft.salePoints[chainName] = value + draft.salePoints[chainKey] = value if (draft.type !== ProjectType.DataUnion) { return @@ -151,14 +149,15 @@ export default function ProjectEditorPage() { {salePoints.map((salePoint) => { - const chainName = getChainConfig( + const chainKey = getChainKey( salePoint.chainId, - ).name + ) - const formattedChainName = - formatChainName(chainName) + const chainName = getChainDisplayName( + salePoint.chainId, + ) - const beneficiaryErrorKey = `salePoints.${chainName}.beneficiaryAddress` + const beneficiaryErrorKey = `salePoints.${chainKey}.beneficiaryAddress` const beneficiaryInvalid = !!errors[beneficiaryErrorKey] @@ -172,7 +171,7 @@ export default function ProjectEditorPage() { >

Set the payment token and price on - the {formattedChainName} chain + the {chainName} chain

You can set a price for others to @@ -191,7 +190,7 @@ export default function ProjectEditorPage() {

This wallet address receives the payments for this product on{' '} - {formattedChainName} chain. + {chainName} chain.

Set the payment token and price on the  - {formatChainName( - getChainConfig( - salePoint.chainId, - ).name, + {getChainDisplayName( + salePoint.chainId, )}{' '} chain diff --git a/src/parsers/ProjectParser.ts b/src/parsers/ProjectParser.ts index e7da300a36..985a26bc98 100644 --- a/src/parsers/ProjectParser.ts +++ b/src/parsers/ProjectParser.ts @@ -7,14 +7,15 @@ import { getMostRelevantTimeUnit } from '~/marketplace/utils/price' import { ProjectType, SalePoint } from '~/shared/types' import { TimeUnit, - timeUnitSecondsMultiplierMap, timeUnits, + timeUnitSecondsMultiplierMap, } from '~/shared/utils/timeUnit' import { toBigInt } from '~/utils/bn' import { getChainConfig, - getChainConfigExtension, + getChainKey, getCurrentChainId, + getMarketplaceChainConfigs, } from '~/utils/chains' import { getContractAddress } from '~/utils/contracts' import { getTokenInfo } from '~/utils/tokens' @@ -149,13 +150,12 @@ export function parseProject(value: unknown, options: ParseProjectOptions) { } } - const chains: Chain[] = - getChainConfigExtension(chainId).marketplaceChains.map(getChainConfig) + const chains: Chain[] = getMarketplaceChainConfigs(chainId) const salePoints: Record = {} - chains.map(({ id, name: chainName }) => { - salePoints[chainName] = { + chains.map(({ id }) => { + salePoints[getChainKey(id)] = { beneficiaryAddress: '', chainId: id, enabled: false, @@ -172,9 +172,11 @@ export function parseProject(value: unknown, options: ParseProjectOptions) { const { domainId, pricingTokenAddress, pricePerSecond, beneficiary } = paymentDetails[i] - const { id: chainId, name: chainName } = getChainConfig( - Number(domainId), - ) + /** + * @todo Make sure we can trust domainId's type. It says it's a number thus + * the explicit type coercion *should be* redundant. + */ + const { id: chainId } = getChainConfig(Number(domainId)) const { decimals } = await getTokenInfo(pricingTokenAddress, chainId) @@ -189,7 +191,7 @@ export function parseProject(value: unknown, options: ParseProjectOptions) { throw new Error('Invalid multiplier') } - salePoints[chainName] = { + salePoints[getChainKey(chainId)] = { beneficiaryAddress: isOpenData ? address0 : beneficiary.toLowerCase(), @@ -239,13 +241,12 @@ export function parseProject(value: unknown, options: ParseProjectOptions) { export type ParsedProject = Awaited> function getEmptySalePoints(chainId: number) { - const chains: Chain[] = - getChainConfigExtension(chainId).marketplaceChains.map(getChainConfig) + const marketplaceChainConfigs: Chain[] = getMarketplaceChainConfigs(chainId) const salePoints: Record = {} - chains.map(({ id, name: chainName }) => { - salePoints[chainName] = { + marketplaceChainConfigs.map(({ id }) => { + salePoints[getChainKey(id)] = { beneficiaryAddress: '', chainId: id, enabled: false, diff --git a/src/shared/utils/constants.ts b/src/shared/utils/constants.ts index 45206bfdc3..2c249c05f3 100644 --- a/src/shared/utils/constants.ts +++ b/src/shared/utils/constants.ts @@ -19,18 +19,4 @@ export const paymentCurrencies = { NATIVE: 'NATIVE', } -export const ethereumNetworks = { - '1': 'Ethereum Mainnet', - '3': 'Ropsten', - '4': 'Rinkeby', - '5': 'Görli', - '42': 'Kovan', - '100': 'Gnosis', - '137': 'Polygon', - '8995': 'Dev0', - '8997': 'Dev1', - '31337': 'Dev2', - '80002': 'Amoy', -} - export const maxFileSizeForImageUpload = 5242880 diff --git a/src/shared/utils/tokenAssets.ts b/src/shared/utils/tokenAssets.ts index ba61aeecf8..bb85b923ee 100644 --- a/src/shared/utils/tokenAssets.ts +++ b/src/shared/utils/tokenAssets.ts @@ -1,4 +1,4 @@ -import { getChainConfig } from '~/utils/chains' +import { getCoingeckoNetworkId } from '~/utils/chains' const BASE_URL = 'https://streamr-public.s3.amazonaws.com/truswallet-assets/blockchains' @@ -6,21 +6,11 @@ export const getTokenLogoUrl = ( tokenContractAddress: string, chainId: number, ): string => { - const network = (() => { - switch (chainId) { - case 100: - return 'xdai' - case 8995: - case 8996: - return 'ethereum' - default: - return getChainConfig(chainId).name - } - })() + const networkId = getCoingeckoNetworkId(chainId) /** * For more details see: * https://api.coingecko.com/api/v3/asset_platforms */ - return `${BASE_URL}/${network}/assets/${tokenContractAddress}/logo.png` + return `${BASE_URL}/${networkId}/assets/${tokenContractAddress}/logo.png` } diff --git a/src/types/projects.ts b/src/types/projects.ts index d17bf64991..9ef2042797 100644 --- a/src/types/projects.ts +++ b/src/types/projects.ts @@ -3,16 +3,23 @@ import { z } from 'zod' import { address0 } from '~/consts' import { ProjectType, SalePoint } from '~/shared/types' import { timeUnits } from '~/shared/utils/timeUnit' -import { formatChainName } from '~/utils' -import { getCurrentChain } from '~/utils/chains' +import { + getChainDisplayName, + getChainKey, + getCurrentChain, + isChainKey, +} from '~/utils/chains' import { getContractAddress } from '~/utils/contracts' -function getFormattedChainNameFromContext({ path: [, chainName] }: z.RefinementCtx) { - if (typeof chainName !== 'string' || !chainName) { - return '' - } +function getFormattedChainNameFromContext({ path: [, chainKey] }: z.RefinementCtx) { + const chainName = + typeof chainKey === 'number' + ? `#${chainKey}` + : isChainKey(chainKey) + ? getChainDisplayName(chainKey) + : `"${chainKey}"` - return `for ${formatChainName(chainName)} network` + return `for ${chainName} network` } export const SalePointsPayload = z.record( @@ -192,10 +199,10 @@ export const OpenDataPayload = z.object({ .transform((v) => v || undefined), }), salePoints: SalePointsPayload.transform(() => { - const { name: chainName, id: chainId } = getCurrentChain() + const { id: chainId } = getCurrentChain() return { - [chainName]: { + [getChainKey(chainId)]: { beneficiaryAddress: address0, chainId, enabled: true, diff --git a/src/utils/chainConfigExtension.ts b/src/utils/chainConfigExtension.ts index 9916629ac5..e90c71339d 100644 --- a/src/utils/chainConfigExtension.ts +++ b/src/utils/chainConfigExtension.ts @@ -5,6 +5,7 @@ import config from '~/config/chains.toml' import formatConfigUrl from '~/utils/formatConfigUrl' const ChainConfigExtension = z.object({ + coingeckoNetworkId: z.string().optional(), dataUnionJoinServerUrl: z.string().optional(), dataunionGraphNames: z .array( @@ -15,6 +16,7 @@ const ChainConfigExtension = z.object({ ) .optional() .default([]), + displayName: z.string().optional(), dockerHost: z.string().optional(), ipfs: z .object({ diff --git a/src/utils/chains.test.ts b/src/utils/chains.test.ts new file mode 100644 index 0000000000..ada534fb3d --- /dev/null +++ b/src/utils/chains.test.ts @@ -0,0 +1,74 @@ +import { config } from '@streamr/config' +import { defaultChainKey } from '../consts' +import { parsedChainConfigExtension } from './chainConfigExtension' +import { + getChainDisplayName, + getChainKey, + getMarketplaceChainConfigs, + isChainKey, +} from './chains' + +describe('getChainKey', () => { + it('defaults to the default chain key', () => { + expect(getChainKey('whatever')).toEqual(defaultChainKey) + + expect(getChainKey(0)).toEqual(defaultChainKey) + }) + + it('extracts chain key by slug', () => { + expect(getChainKey('amoy')).toEqual('polygonAmoy') + }) + + it('is case insensitive', () => { + expect(getChainKey('AMOY')).toEqual('polygonAmoy') + + expect(getChainKey('POlyGON')).toEqual('polygon') + + expect(getChainKey('polygonamoy')).toEqual('polygonAmoy') + }) + + it('resolves a number to a chain key', () => { + expect(getChainKey(137)).toEqual('polygon') + + expect(getChainKey(100)).toEqual('gnosis') + }) +}) + +describe('isChainKey', () => { + it('correctly identifies chain keys', () => { + expect(isChainKey('whatever')).toBe(false) + + expect(isChainKey('polygon')).toBe(true) + + expect(isChainKey('gnosis')).toBe(true) + }) +}) + +describe('getChainDisplayName', () => { + it('used custom naming for chains that we provide it for', () => { + // Local config extension provides a custom value + expect(parsedChainConfigExtension['polygonAmoy']?.displayName).toEqual('Amoy') + + // Config provides a diffrent value + expect(config.polygonAmoy.name).not.toEqual('Amoy') + + // Ultimately local name counts + expect(getChainDisplayName('polygonAmoy')).toEqual('Amoy') + }) +}) + +describe('getMarketplaceChainConfigs', () => { + it('gives a list of configs for given keys', () => { + const [config0, config1, config2] = getMarketplaceChainConfigs('polygon') + + expect(config0.id).toEqual(config.gnosis.id) + + expect(config1.id).toEqual(config.polygon.id) + + expect(config2).toBeUndefined() + }) + + it('gives an empty list of configs if there are no marketplace chain keys provided', () => { + expect(getMarketplaceChainConfigs('ethereum').length).toEqual(0) + }) +}) diff --git a/src/utils/chains.ts b/src/utils/chains.ts index 6bc8014aa4..7c78cf5bc5 100644 --- a/src/utils/chains.ts +++ b/src/utils/chains.ts @@ -3,7 +3,6 @@ import { produce } from 'immer' import { useMemo } from 'react' import { useSearchParams } from 'react-router-dom' import { defaultChainKey } from '~/consts' -import { ethereumNetworks } from '~/shared/utils/constants' import { ChainConfigExtension, fallbackChainConfigExtension, @@ -67,35 +66,35 @@ export function getChainKey(candidate: string | number): ChainKey { return defaultChainKey } -export function getCurrentChain() { +export function getCurrentChain(): Chain { return getChainConfig( new URLSearchParams(window.location.search).get('chain') || defaultChainKey, ) } -export function getCurrentChainId() { +export function getCurrentChainId(): number { return getCurrentChain().id } -export function useCurrentChain() { +export function useCurrentChain(): Chain { const chainName = useSearchParams()[0].get('chain') || defaultChainKey return useMemo(() => getChainConfig(chainName), [chainName]) } -export function useCurrentChainId() { +export function useCurrentChainId(): number { return useCurrentChain().id } -export function useCurrentChainKey() { +export function useCurrentChainKey(): ChainKey { return getChainKey(useCurrentChainId()) } /** * @todo rename to `useCurrentFullChainName`. */ -export function useCurrentChainFullName() { - return getChainConfig(useCurrentChainId()).name +export function useCurrentChainFullName(): string { + return getChainDisplayName(useCurrentChainId()) } interface ChainEntry { @@ -119,8 +118,6 @@ function getChainEntry(chainKey: ChainKey): ChainEntry { const { dockerHost } = configExtension const sanitizedConfig = produce(config, (draft) => { - draft.name = ethereumNetworks[config.id] || config.name - for (const rpc of draft.rpcEndpoints) { rpc.url = formatConfigUrl(rpc.url, { dockerHost, @@ -170,6 +167,39 @@ export function getChainSlug(chainIdOrChainKey: ChainKey | number): string { * @param candidate Any string. * @returns `true` if the given string is config's own key. */ -function isChainKey(candidate: string): candidate is ChainKey { +export function isChainKey(candidate: string): candidate is ChainKey { return Object.prototype.hasOwnProperty.call(configs, candidate) } + +export function isKnownChainId(candidate: number): boolean { + return Object.entries(configs).some(([, { id }]) => id === candidate) +} + +export function getChainDisplayName(chainIdOrChainKey: ChainKey | number): string { + const { config, configExtension } = getChainEntry(getChainKey(chainIdOrChainKey)) + + return configExtension.displayName || config.name +} + +export function getMarketplaceChainConfigs( + chainIdOrChainKey: ChainKey | number, +): Chain[] { + const marketplaceChainKeys = getChainEntry(getChainKey(chainIdOrChainKey)) + .configExtension.marketplaceChains + + const result: Chain[] = [] + + for (const key of marketplaceChainKeys) { + if (isChainKey(key)) { + result.push(getChainConfig(key)) + } + } + + return result +} + +export function getCoingeckoNetworkId(chainIdOrChainKey: ChainKey | number) { + const { config, configExtension } = getChainEntry(getChainKey(chainIdOrChainKey)) + + return configExtension.coingeckoNetworkId || config.name +} diff --git a/src/utils/index.tsx b/src/utils/index.tsx index 7bee2d3d3f..c9f54fb7c5 100644 --- a/src/utils/index.tsx +++ b/src/utils/index.tsx @@ -130,27 +130,6 @@ export async function sleep(millis: number) { await new Promise((resolve) => void setTimeout(resolve, millis)) } -/** - * Turns `abc`, `ABC`, `aBc` into `Abc`. - */ -function titleize(value: string): string { - return value.toLowerCase().replace(/\w/, (firstLetter) => firstLetter.toUpperCase()) -} - -/** - * Converts a string into a good-looking display-ready chain name. - */ -export function formatChainName(chainName: string): string { - switch (chainName.toLowerCase()) { - case 'xdai': - return formatChainName('gnosis') - case 'bsc': - return 'Binance Smart Chain' - default: - return titleize(chainName) - } -} - /** * Takes the user back in history, but only if they've already navigated * somewhere within the app. diff --git a/src/utils/networkPreflight.ts b/src/utils/networkPreflight.ts index 8fafd11fd6..1286e92f72 100644 --- a/src/utils/networkPreflight.ts +++ b/src/utils/networkPreflight.ts @@ -2,7 +2,7 @@ import { toaster } from 'toasterhea' import SwitchNetworkModal from '~/modals/SwitchNetworkModal' import { getWalletProvider } from '~/shared/stores/wallet' import { Layer } from '~/utils/Layer' -import { getChainConfig } from '~/utils/chains' +import { getChainConfig, getChainDisplayName } from '~/utils/chains' import getChainId from '~/utils/web3/getChainId' /** @@ -14,15 +14,15 @@ export default async function networkPreflight(expectedChainId: number) { const provider = await getWalletProvider() try { - const currentChainId = await getChainId() + const actualChainId = await getChainId() - if (currentChainId === expectedChainId) { + if (actualChainId === expectedChainId) { return false } await toaster(SwitchNetworkModal, Layer.Modal).pop({ - expectedNetwork: expectedChainId, - actualNetwork: currentChainId, + expectedChainId, + actualChainId, }) await provider.request({ @@ -45,7 +45,7 @@ export default async function networkPreflight(expectedChainId: number) { params: [ { chainId: `0x${chainConfig.id.toString(16)}`, - chainName: chainConfig.name, + chainName: getChainDisplayName(chainConfig.id), rpcUrls: chainConfig.rpcEndpoints.map(({ url }) => url), nativeCurrency: chainConfig.nativeCurrency, blockExplorerUrls: [chainConfig.blockExplorerUrl].filter(