From fea1c180cb0964d53a55c9da62a7178a33510ba2 Mon Sep 17 00:00:00 2001 From: Kristof Csillag Date: Sun, 11 Feb 2024 18:13:48 +0100 Subject: [PATCH 1/4] Add support for hidden layers in Layer selector --- src/app/components/LayerPicker/LayerMenu.tsx | 3 ++- src/types/layers.ts | 4 ++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/app/components/LayerPicker/LayerMenu.tsx b/src/app/components/LayerPicker/LayerMenu.tsx index 523c43fe2..a8420a036 100644 --- a/src/app/components/LayerPicker/LayerMenu.tsx +++ b/src/app/components/LayerPicker/LayerMenu.tsx @@ -11,7 +11,7 @@ import { Layer } from '../../../oasis-nexus/api' import { getLayerLabels } from '../../utils/content' import { RouteUtils } from '../../utils/route-utils' import { Network } from '../../../types/network' -import { orderByLayer } from '../../../types/layers' +import { isLayerHidden, orderByLayer } from '../../../types/layers' import { useScreenSize } from '../../hooks/useScreensize' type BaseLayerMenuItemProps = { @@ -109,6 +109,7 @@ export const LayerMenu: FC = ({ }) => { const [hoveredLayer, setHoveredLayer] = useState() const options = Object.values(Layer) + .filter(layer => !isLayerHidden(layer)) .map(layer => ({ layer, enabled: RouteUtils.getEnabledLayersForNetwork(selectedNetwork || network).includes(layer), diff --git a/src/types/layers.ts b/src/types/layers.ts index 74122df7e..1f4586dd6 100644 --- a/src/types/layers.ts +++ b/src/types/layers.ts @@ -26,6 +26,8 @@ const layerOrder: Record = { [Layer.cipher]: 4, } +const hiddenLayers: Layer[] = [] + export const orderByLayer = (itemA: HasLayer, itemB: HasLayer): number => layerOrder[itemA.layer] - layerOrder[itemB.layer] @@ -36,3 +38,5 @@ export const doesLayerSupportEncryptedTransactions = (layer: Layer): boolean => export const doesAnyOfTheseLayersSupportEncryptedTransactions = (layers: Layer[] | undefined): boolean => uniq(layers).some(doesLayerSupportEncryptedTransactions) + +export const isLayerHidden = (layer: Layer): boolean => hiddenLayers.includes(layer) From d668a436955a1f1b0459f7410ace53662c679b82 Mon Sep 17 00:00:00 2001 From: Kristof Csillag Date: Sun, 11 Feb 2024 18:17:58 +0100 Subject: [PATCH 2/4] Add Pontus-X as a hidden layer - It's not shown on the landing page graph - It's not listed in the layer selector - It can be accessed using the correct URL - Currently all the data is mock data, coming for Sapphire --- .changelog/1245.feature.md | 1 + src/app/components/Search/search-utils.ts | 8 ++++++ .../pages/HomePage/Graph/Graph/graph-utils.ts | 4 +++ src/app/pages/HomePage/Graph/Graph/index.tsx | 2 +- .../Graph/GraphTooltipMobile/index.tsx | 6 +++-- .../LearningMaterials.tsx | 4 ++- .../GlobalSearchResultsView.tsx | 4 +-- src/app/utils/content.tsx | 1 + src/app/utils/externalLinks.ts | 1 + src/app/utils/route-utils.ts | 2 ++ src/app/utils/rpc-utils.ts | 1 + src/config.ts | 27 +++++++++++++++++++ src/locales/en/translation.json | 2 ++ src/oasis-nexus/api.ts | 9 +++++++ src/oasis-nexus/generated/api.ts | 2 ++ src/types/layers.ts | 6 ++++- 16 files changed, 73 insertions(+), 7 deletions(-) create mode 100644 .changelog/1245.feature.md diff --git a/.changelog/1245.feature.md b/.changelog/1245.feature.md new file mode 100644 index 000000000..5716493c1 --- /dev/null +++ b/.changelog/1245.feature.md @@ -0,0 +1 @@ +Add Pontus-X as a hidden layer diff --git a/src/app/components/Search/search-utils.ts b/src/app/components/Search/search-utils.ts index 32ae1e383..79d013ef1 100644 --- a/src/app/components/Search/search-utils.ts +++ b/src/app/components/Search/search-utils.ts @@ -33,6 +33,7 @@ export const searchSuggestionTerms = { suggestedTokenFragment: 'mock', }, cipher: undefined, + pontusx: undefined, consensus: undefined, }, testnet: { @@ -49,6 +50,13 @@ export const searchSuggestionTerms = { suggestedTokenFragment: 'USD', }, cipher: undefined, + pontusx: { + // TODO: provide proper suggestions + suggestedBlock: '4260', + suggestedTransaction: '0xd9b5c08be1cb74229abedd9b3e1afb8b43228085a6abf72993db415959ab6b35', + suggestedAccount: '0xfA3AC9f65C9D75EE3978ab76c6a1105f03156204', + suggestedTokenFragment: 'USD', + }, consensus: undefined, }, } satisfies SpecifiedPerEnabledLayer diff --git a/src/app/pages/HomePage/Graph/Graph/graph-utils.ts b/src/app/pages/HomePage/Graph/Graph/graph-utils.ts index 0f138c90b..a658da6ea 100644 --- a/src/app/pages/HomePage/Graph/Graph/graph-utils.ts +++ b/src/app/pages/HomePage/Graph/Graph/graph-utils.ts @@ -27,6 +27,10 @@ export abstract class GraphUtils { x: 1.2 * width, y: 0.7 * height, } + case Layer.pontusx: + // TODO: update this if/when we want to display this layer + // We meed this case here since this switch/case is declared to be exhaustive. + return initialValue case Layer.sapphire: return { scale: 2.4, diff --git a/src/app/pages/HomePage/Graph/Graph/index.tsx b/src/app/pages/HomePage/Graph/Graph/index.tsx index c4f49642a..0def0bfe0 100644 --- a/src/app/pages/HomePage/Graph/Graph/index.tsx +++ b/src/app/pages/HomePage/Graph/Graph/index.tsx @@ -293,7 +293,7 @@ const GraphCmp: ForwardRefRenderFunction = ( return !RouteUtils.getEnabledLayersForNetwork(network).includes(Layer) } - const disabledMap: Record = { + const disabledMap: Partial> = { [Layer.emerald]: isLayerDisabled(Layer.emerald), [Layer.consensus]: isLayerDisabled(Layer.consensus), [Layer.cipher]: isLayerDisabled(Layer.cipher), diff --git a/src/app/pages/HomePage/Graph/GraphTooltipMobile/index.tsx b/src/app/pages/HomePage/Graph/GraphTooltipMobile/index.tsx index fafd6fec8..c84160e8b 100644 --- a/src/app/pages/HomePage/Graph/GraphTooltipMobile/index.tsx +++ b/src/app/pages/HomePage/Graph/GraphTooltipMobile/index.tsx @@ -140,7 +140,7 @@ const layerTooltipBodyCaption = (t: TFunction, layer: Layer, enabled: boolean, o : t('common.paraTimeOnline') } -const useLayerTooltipMap = (network: Network): Record => { +const useLayerTooltipMap = (network: Network): Partial> => { const isSapphireEnabled = RouteUtils.getEnabledLayersForNetwork(network).includes(Layer.sapphire) const isEmeraldEnabled = RouteUtils.getEnabledLayersForNetwork(network).includes(Layer.emerald) const isCipherEnabled = RouteUtils.getEnabledLayersForNetwork(network).includes(Layer.cipher) @@ -270,7 +270,9 @@ export const GraphTooltipMobile: FC = ({ network, layer const navigate = useNavigate() const { t } = useTranslation() const { isMobile } = useScreenSize() - const { body, disabled, failing } = useLayerTooltipMap(network)[layer] + const tooltip = useLayerTooltipMap(network)[layer] + if (!tooltip) return + const { body, disabled, failing } = tooltip const navigateTo = () => { if (disabled) { diff --git a/src/app/pages/ParatimeDashboardPage/LearningMaterials.tsx b/src/app/pages/ParatimeDashboardPage/LearningMaterials.tsx index eaf42f13e..118f70e5e 100644 --- a/src/app/pages/ParatimeDashboardPage/LearningMaterials.tsx +++ b/src/app/pages/ParatimeDashboardPage/LearningMaterials.tsx @@ -62,6 +62,7 @@ const getContent = (t: TFunction) => { }, }, [Layer.cipher]: undefined, + [Layer.pontusx]: undefined, }, [Network.testnet]: { [Layer.emerald]: { @@ -99,8 +100,9 @@ const getContent = (t: TFunction) => { }, }, [Layer.cipher]: undefined, + [Layer.pontusx]: undefined, }, - } satisfies SpecifiedPerEnabledRuntime + } satisfies SpecifiedPerEnabledRuntime } export const LearningMaterials: FC<{ scope: SearchScope }> = ({ scope }) => { diff --git a/src/app/pages/SearchResultsPage/GlobalSearchResultsView.tsx b/src/app/pages/SearchResultsPage/GlobalSearchResultsView.tsx index 94a18fab5..223480793 100644 --- a/src/app/pages/SearchResultsPage/GlobalSearchResultsView.tsx +++ b/src/app/pages/SearchResultsPage/GlobalSearchResultsView.tsx @@ -16,7 +16,7 @@ import { } from '../../../types/network' import { HideMoreResults, ShowMoreResults } from './notifications' import { getThemesForNetworks } from '../../../styles/theme' -import { orderByLayer } from '../../../types/layers' +import { isNotOnHiddenLayer, orderByLayer } from '../../../types/layers' import { useRedirectIfSingleResult } from './useRedirectIfSingleResult' export const GlobalSearchResultsView: FC<{ @@ -33,7 +33,7 @@ export const GlobalSearchResultsView: FC<{ const otherNetworks = RouteUtils.getEnabledNetworks().filter(isNotMainnet) const notificationTheme = themes[Network.testnet] const mainnetResults = searchResults.filter(isOnMainnet).sort(orderByLayer) - const otherResults = searchResults.filter(isNotOnMainnet).sort(orderByLayer) + const otherResults = searchResults.filter(isNotOnMainnet).filter(isNotOnHiddenLayer).sort(orderByLayer) return ( <> diff --git a/src/app/utils/content.tsx b/src/app/utils/content.tsx index 0a5635048..e71420998 100644 --- a/src/app/utils/content.tsx +++ b/src/app/utils/content.tsx @@ -9,6 +9,7 @@ export const getLayerLabels = (t: TFunction): Record => ({ [Layer.emerald]: t('common.emerald'), [Layer.sapphire]: t('common.sapphire'), [Layer.cipher]: t('common.cipher'), + [Layer.pontusx]: t('common.pontusx'), [Layer.consensus]: t('common.consensus'), }) diff --git a/src/app/utils/externalLinks.ts b/src/app/utils/externalLinks.ts index 2e6d10de5..5a412d9c9 100644 --- a/src/app/utils/externalLinks.ts +++ b/src/app/utils/externalLinks.ts @@ -48,6 +48,7 @@ export const faucet = { [Layer.consensus]: faucetUrl, [Layer.emerald]: `${faucetParaTimeBaseUrl}emerald`, [Layer.sapphire]: `${faucetParaTimeBaseUrl}sapphire`, + [Layer.pontusx]: 'mailto:contact@delta-dao.com?subject=tokens', [Layer.cipher]: `${faucetParaTimeBaseUrl}cipher`, } diff --git a/src/app/utils/route-utils.ts b/src/app/utils/route-utils.ts index 1bc292cb6..b8f1916c1 100644 --- a/src/app/utils/route-utils.ts +++ b/src/app/utils/route-utils.ts @@ -25,6 +25,7 @@ export abstract class RouteUtils { [Layer.emerald]: true, [Layer.sapphire]: true, [Layer.cipher]: false, + [Layer.pontusx]: false, // Disable WIP Consensus on production an staging [Layer.consensus]: !isStableDeploy, }, @@ -32,6 +33,7 @@ export abstract class RouteUtils { [Layer.emerald]: true, [Layer.sapphire]: true, [Layer.cipher]: false, + [Layer.pontusx]: true, [Layer.consensus]: false, }, } satisfies Record> diff --git a/src/app/utils/rpc-utils.ts b/src/app/utils/rpc-utils.ts index fe690f289..267de2676 100644 --- a/src/app/utils/rpc-utils.ts +++ b/src/app/utils/rpc-utils.ts @@ -20,6 +20,7 @@ const LAYER_DECIMALS: Record = { [Layer.emerald]: paraTimesConfig[Layer.emerald].decimals, [Layer.sapphire]: paraTimesConfig[Layer.sapphire].decimals, [Layer.cipher]: paraTimesConfig[Layer.cipher].decimals, + [Layer.pontusx]: paraTimesConfig[Layer.pontusx].decimals, [Layer.consensus]: consensusDecimals, } diff --git a/src/config.ts b/src/config.ts index 37073c85a..1e6fe2b4b 100644 --- a/src/config.ts +++ b/src/config.ts @@ -108,6 +108,32 @@ const sapphireConfig: LayerConfig = { outOfDateThreshold: 2 * 60 * 1000, } +const pontusxConfig: LayerConfig = { + mainnet: { + activeNodes: undefined, + address: undefined, + blockGasLimit: undefined, + runtimeId: undefined, + }, + testnet: { + activeNodes: 8, // TODO use correct number + address: 'oasis1qqczuf3x6glkgjuf0xgtcpjjw95r3crf7y2323xd', // TODO use correct address + // See max_batch_gas https://github.com/oasisprotocol/sapphire-paratime/blob/main/runtime/src/lib.rs#L166 + blockGasLimit: 15_000_000, + runtimeId: '000000000000000000000000000000000000000000000000a6d1e3ebf60dff6c', + }, + local: { + activeNodes: undefined, + address: undefined, + blockGasLimit: undefined, + runtimeId: undefined, + }, + + decimals: 18, + type: RuntimeTypes.Evm, + outOfDateThreshold: 2 * 60 * 1000, +} + type LayersConfig = { [key in Layer]: LayerConfig | null } @@ -116,6 +142,7 @@ export const paraTimesConfig = { [Layer.cipher]: cipherConfig, [Layer.emerald]: emeraldConfig, [Layer.sapphire]: sapphireConfig, + [Layer.pontusx]: pontusxConfig, [Layer.consensus]: null, } satisfies LayersConfig diff --git a/src/locales/en/translation.json b/src/locales/en/translation.json index 405ad7075..050556ee5 100644 --- a/src/locales/en/translation.json +++ b/src/locales/en/translation.json @@ -87,6 +87,7 @@ "paratime": "Paratime", "parentheses": "({{subject}})", "percentage": "Percentage", + "pontusx": "Pontus-X", "proposal": "Proposal", "proposer": "Proposer", "rank": "Rank", @@ -472,6 +473,7 @@ "sapphireParaTimeDesc": "Confidential EVM Compatible ParaTime providing a smart contract development environment with EVM compatibility", "emeraldParaTimeDesc": "EVM Compatible ParaTime providing smart contract environment with EVM compatibility.", "cipherParaTimeDesc": "The Cipher ParaTime is a Confidential ParaTime for executing Wasm smart contracts.", + "pontusxParaTimeDesc": "Pontus-X ParaTime", "consensusDesc": "Our scalable, high-throughput, secure, proof-of-stake consensus layer run by a decentralized set of validator nodes.", "close": "Close" }, diff --git a/src/oasis-nexus/api.ts b/src/oasis-nexus/api.ts index bd1e14cac..68595930f 100644 --- a/src/oasis-nexus/api.ts +++ b/src/oasis-nexus/api.ts @@ -124,6 +124,15 @@ function arrayify(arrayOrItem: null | undefined | T | T[]): T[] { return arrayOrItem } +// TODO: remove when pontusx API is ready +axios.interceptors.request.use(config => { + // Mock pontusx + if (config.url?.includes('/v1/pontusx')) { + config.url = config.url.replace('/v1/pontusx', '/v1/sapphire') + } + return config +}) + export const useGetConsensusTransactions: typeof generated.useGetConsensusTransactions = ( network, params?, diff --git a/src/oasis-nexus/generated/api.ts b/src/oasis-nexus/generated/api.ts index a2d927f42..a46c803f4 100644 --- a/src/oasis-nexus/generated/api.ts +++ b/src/oasis-nexus/generated/api.ts @@ -1759,6 +1759,7 @@ export const Runtime = { emerald: 'emerald', sapphire: 'sapphire', cipher: 'cipher', + pontusx: 'pontusx', } as const; export type Layer = typeof Layer[keyof typeof Layer]; @@ -1769,6 +1770,7 @@ export const Layer = { emerald: 'emerald', sapphire: 'sapphire', cipher: 'cipher', + pontusx: 'pontusx', consensus: 'consensus', } as const; diff --git a/src/types/layers.ts b/src/types/layers.ts index 1f4586dd6..34c8356a4 100644 --- a/src/types/layers.ts +++ b/src/types/layers.ts @@ -10,6 +10,7 @@ export const getLayerNames = (t: TFunction): Record => ({ [Layer.emerald]: t('common.emerald'), [Layer.sapphire]: t('common.sapphire'), [Layer.cipher]: t('common.cipher'), + [Layer.pontusx]: t('common.pontusx'), [Layer.consensus]: t('common.consensus'), }) @@ -24,9 +25,10 @@ const layerOrder: Record = { [Layer.sapphire]: 2, [Layer.emerald]: 3, [Layer.cipher]: 4, + [Layer.pontusx]: 5, } -const hiddenLayers: Layer[] = [] +const hiddenLayers: Layer[] = [Layer.pontusx] export const orderByLayer = (itemA: HasLayer, itemB: HasLayer): number => layerOrder[itemA.layer] - layerOrder[itemB.layer] @@ -40,3 +42,5 @@ export const doesAnyOfTheseLayersSupportEncryptedTransactions = (layers: Layer[] uniq(layers).some(doesLayerSupportEncryptedTransactions) export const isLayerHidden = (layer: Layer): boolean => hiddenLayers.includes(layer) + +export const isNotOnHiddenLayer = (item: HasLayer) => !isLayerHidden(item.layer) From 429b6face0cf51ce79840d5e676d093b7b389af6 Mon Sep 17 00:00:00 2001 From: Kristof Csillag Date: Tue, 13 Feb 2024 02:42:22 +0100 Subject: [PATCH 3/4] Simplify: remove double function --- src/app/pages/ParatimeDashboardPage/ParaTimeSnapshot.tsx | 4 ++-- src/types/layers.ts | 9 --------- src/types/searchScope.ts | 4 ++-- 3 files changed, 4 insertions(+), 13 deletions(-) diff --git a/src/app/pages/ParatimeDashboardPage/ParaTimeSnapshot.tsx b/src/app/pages/ParatimeDashboardPage/ParaTimeSnapshot.tsx index ca6f701a4..442c9f429 100644 --- a/src/app/pages/ParatimeDashboardPage/ParaTimeSnapshot.tsx +++ b/src/app/pages/ParatimeDashboardPage/ParaTimeSnapshot.tsx @@ -10,7 +10,7 @@ import { ChartDuration } from '../../utils/chart-utils' import { useTranslation } from 'react-i18next' import { useConstant } from '../../hooks/useConstant' import { Network } from '../../../types/network' -import { getLayerNames } from '../../../types/layers' +import { getLayerLabels } from '../../utils/content' import { TestnetFaucet } from './TestnetFaucet' import { SearchScope } from '../../../types/searchScope' import { Snapshot } from 'app/components/Snapshots/Snapshot' @@ -23,7 +23,7 @@ export const ParaTimeSnapshot: FC<{ scope: SearchScope }> = ({ scope }) => { const { t } = useTranslation() const defaultChartDurationValue = useConstant(() => ChartDuration.TODAY) const [chartDuration, setChartDuration] = useState(defaultChartDurationValue) - const paratime = getLayerNames(t)[scope.layer] + const paratime = getLayerLabels(t)[scope.layer] const handleDurationSelectedChange = (duration: ChartDuration | null) => { if (!duration) { return diff --git a/src/types/layers.ts b/src/types/layers.ts index 34c8356a4..53ed43c17 100644 --- a/src/types/layers.ts +++ b/src/types/layers.ts @@ -4,15 +4,6 @@ import { uniq } from '../app/utils/helpers' // a cycle of imports which confuse jest // eslint-disable-next-line no-restricted-imports import { Layer } from '../oasis-nexus/generated/api' -import { TFunction } from 'i18next' - -export const getLayerNames = (t: TFunction): Record => ({ - [Layer.emerald]: t('common.emerald'), - [Layer.sapphire]: t('common.sapphire'), - [Layer.cipher]: t('common.cipher'), - [Layer.pontusx]: t('common.pontusx'), - [Layer.consensus]: t('common.consensus'), -}) interface HasLayer { layer: Layer diff --git a/src/types/searchScope.ts b/src/types/searchScope.ts index c72deba16..74a8a5888 100644 --- a/src/types/searchScope.ts +++ b/src/types/searchScope.ts @@ -1,5 +1,5 @@ import { getNetworkNames, Network } from './network' -import { getLayerNames } from './layers' +import { getLayerLabels } from '../app/utils/content' import { HasScope, Layer } from '../oasis-nexus/api' import { TFunction } from 'i18next' @@ -14,7 +14,7 @@ export const MainnetEmerald: SearchScope = { } export const getNameForScope = (t: TFunction, scope: SearchScope) => - `${getLayerNames(t)[scope.layer]} ${getNetworkNames(t)[scope.network]}` + `${getLayerLabels(t)[scope.layer]} ${getNetworkNames(t)[scope.network]}` export const getKeyForScope: (scope: SearchScope) => string = ({ network, layer }) => `${network}.${layer}` From feae7fd44aff889e9aad7fe3456d995d2ca10c73 Mon Sep 17 00:00:00 2001 From: Kristof Csillag Date: Tue, 13 Feb 2024 03:05:52 +0100 Subject: [PATCH 4/4] Fix test case to handle email addresses --- src/app/utils/__tests__/externalLinks.test.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/app/utils/__tests__/externalLinks.test.ts b/src/app/utils/__tests__/externalLinks.test.ts index 3726687a9..ac39610c5 100644 --- a/src/app/utils/__tests__/externalLinks.test.ts +++ b/src/app/utils/__tests__/externalLinks.test.ts @@ -18,7 +18,7 @@ onlyRunOnCI('externalLinks', () => { // eslint-disable-next-line @typescript-eslint/no-unused-vars for (const [_linkName, url] of Object.entries(linksGroup)) { expect(typeof url).toBe('string') - expect(url).toMatch(/^https:/) + expect(url).toMatch(/^(https|mailto):/) } } }) @@ -32,6 +32,7 @@ onlyRunOnCI('externalLinks', () => { if (url.startsWith(externalLinksModule.github.commit)) continue // We store only partial url in constants if (url.startsWith(externalLinksModule.github.releaseTag)) continue // We store only partial url in constants if (url.startsWith(externalLinksModule.ipfs.proxyPrefix)) continue // We store only partial url in constants + if (url.startsWith('mailto')) continue // We can't test email addresses it.concurrent(`${linksGroupName} ${linkName} ${url}`, async () => { const response = await nodeFetch(url, { method: 'GET' })