diff --git a/.github/workflows/lock.yml b/.github/workflows/lock.yml index e33e1efe40..65bec761d4 100644 --- a/.github/workflows/lock.yml +++ b/.github/workflows/lock.yml @@ -2,7 +2,7 @@ name: 'Lock Threads' on: schedule: - - cron: '4 4 * * *' + - cron: '9 9 * * *' jobs: lock: diff --git a/packages/apps-config/src/endpoints/development.ts b/packages/apps-config/src/endpoints/development.ts index 7ec627db24..f6c9b9d647 100644 --- a/packages/apps-config/src/endpoints/development.ts +++ b/packages/apps-config/src/endpoints/development.ts @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 import type { TFunction } from 'i18next'; -import type { LinkOption } from '../settings/types'; +import type { LinkOption } from './types'; export const CUSTOM_ENDPOINT_KEY = 'polkadot-app-custom-endpoints'; diff --git a/packages/apps-config/src/endpoints/index.ts b/packages/apps-config/src/endpoints/index.ts index 590e7776f7..72293f77cf 100644 --- a/packages/apps-config/src/endpoints/index.ts +++ b/packages/apps-config/src/endpoints/index.ts @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 import type { TFunction } from 'i18next'; -import type { LinkOption } from '../settings/types'; +import type { LinkOption } from './types'; import { createCustom, createDev, createOwn } from './development'; import { createProduction } from './production'; diff --git a/packages/apps-config/src/endpoints/production.ts b/packages/apps-config/src/endpoints/production.ts index 1d56b1f072..0d65eae89a 100644 --- a/packages/apps-config/src/endpoints/production.ts +++ b/packages/apps-config/src/endpoints/production.ts @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 import type { TFunction } from 'i18next'; -import type { LinkOption } from '../settings/types'; +import type { LinkOption } from './types'; import { expandEndpoints } from './util'; diff --git a/packages/apps-config/src/endpoints/productionRelays.ts b/packages/apps-config/src/endpoints/productionRelays.ts index 9b4adc7715..4e773f7cfa 100644 --- a/packages/apps-config/src/endpoints/productionRelays.ts +++ b/packages/apps-config/src/endpoints/productionRelays.ts @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 import type { TFunction } from 'i18next'; -import type { LinkOption } from '../settings/types'; +import type { LinkOption } from './types'; import { createKusama } from './productionRelayKusama'; import { createPolkadot } from './productionRelayPolkadot'; diff --git a/packages/apps-config/src/endpoints/testing.ts b/packages/apps-config/src/endpoints/testing.ts index 06601fd6f7..ed2b12c961 100644 --- a/packages/apps-config/src/endpoints/testing.ts +++ b/packages/apps-config/src/endpoints/testing.ts @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 import type { TFunction } from 'i18next'; -import type { LinkOption } from '../settings/types'; +import type { LinkOption } from './types'; import { expandEndpoints } from './util'; diff --git a/packages/apps-config/src/endpoints/testingRelayWestend.ts b/packages/apps-config/src/endpoints/testingRelayWestend.ts index c9a8d9f44c..0667b6941f 100644 --- a/packages/apps-config/src/endpoints/testingRelayWestend.ts +++ b/packages/apps-config/src/endpoints/testingRelayWestend.ts @@ -33,6 +33,7 @@ export function createWestend (t: TFunction): EndpointOption { // (2) common good, leave as second group { info: 'westmint', + allowTeleport: true, paraId: 1000, text: t('rpc.westend.shell', 'Westmint', { ns: 'apps-config' }), providers: { diff --git a/packages/apps-config/src/endpoints/testingRelays.ts b/packages/apps-config/src/endpoints/testingRelays.ts index 029a0076de..a593aca521 100644 --- a/packages/apps-config/src/endpoints/testingRelays.ts +++ b/packages/apps-config/src/endpoints/testingRelays.ts @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 import type { TFunction } from 'i18next'; -import type { LinkOption } from '../settings/types'; +import type { LinkOption } from './types'; import { createRococo } from './testingRelayRococo'; import { createWestend } from './testingRelayWestend'; diff --git a/packages/apps-config/src/endpoints/types.ts b/packages/apps-config/src/endpoints/types.ts index 412a5b6a18..6bf648b90d 100644 --- a/packages/apps-config/src/endpoints/types.ts +++ b/packages/apps-config/src/endpoints/types.ts @@ -1,7 +1,10 @@ // Copyright 2017-2021 @polkadot/apps-config authors & contributors // SPDX-License-Identifier: Apache-2.0 +import type { Option } from '../settings/types'; + export interface EndpointOption { + allowTeleport?: boolean; dnslink?: string; genesisHash?: string; isChild?: boolean; @@ -13,3 +16,17 @@ export interface EndpointOption { providers: Record; text: React.ReactNode; } + +export interface LinkOption extends Option { + allowTeleport?: boolean; + dnslink?: string; + genesisHash?: string; + genesisHashRelay?: string; + isChild?: boolean; + isDevelopment?: boolean; + isRelay?: boolean; + isSpaced?: boolean; + linked?: LinkOption[]; + paraId?: number; + textBy: string; +} diff --git a/packages/apps-config/src/endpoints/util.ts b/packages/apps-config/src/endpoints/util.ts index 3aafd0e804..18d172a857 100644 --- a/packages/apps-config/src/endpoints/util.ts +++ b/packages/apps-config/src/endpoints/util.ts @@ -2,8 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 import type { TFunction } from 'i18next'; -import type { LinkOption } from '../settings/types'; -import type { EndpointOption } from './types'; +import type { EndpointOption, LinkOption } from './types'; export function expandLinked (input: LinkOption[]): LinkOption[] { return input.reduce((result: LinkOption[], entry): LinkOption[] => { @@ -22,9 +21,9 @@ export function expandLinked (input: LinkOption[]): LinkOption[] { }, []); } -export function expandEndpoint (t: TFunction, input: EndpointOption): LinkOption[] { - const { dnslink, genesisHash, info, isChild, isDisabled, linked, paraId, providers, text } = input; +export function expandEndpoint (t: TFunction, { allowTeleport, dnslink, genesisHash, info, isChild, isDisabled, linked, paraId, providers, text }: EndpointOption): LinkOption[] { const base = { + allowTeleport, genesisHash, info, isChild, @@ -36,6 +35,7 @@ export function expandEndpoint (t: TFunction, input: EndpointOption): LinkOption const result = Object.entries(providers).map(([host, value], index): LinkOption => ({ ...base, dnslink: index === 0 ? dnslink : undefined, + isRelay: false, textBy: t('rpc.hosted.by', 'hosted by {{host}}', { ns: 'apps-config', replace: { host } }), value })); @@ -45,7 +45,15 @@ export function expandEndpoint (t: TFunction, input: EndpointOption): LinkOption const options: LinkOption[] = []; linked.forEach((o) => options.push(...expandEndpoint(t, o))); + last.isRelay = true; last.linked = options; + + // if one of the children allows teleport, add it to the root as well + const allowTeleport = options.some(({ allowTeleport }) => allowTeleport); + + result.forEach((r): void => { + r.allowTeleport = allowTeleport; + }); } return expandLinked(result); diff --git a/packages/apps-config/src/settings/types.ts b/packages/apps-config/src/settings/types.ts index 349fbefc8f..43a0512064 100644 --- a/packages/apps-config/src/settings/types.ts +++ b/packages/apps-config/src/settings/types.ts @@ -8,15 +8,3 @@ export interface Option { text: React.ReactNode; value: string | number; } - -export interface LinkOption extends Option { - dnslink?: string; - genesisHash?: string; - genesisHashRelay?: string; - isChild?: boolean; - isDevelopment?: boolean; - isSpaced?: boolean; - linked?: LinkOption[]; - paraId?: number; - textBy: string; -} diff --git a/packages/apps-routing/src/index.ts b/packages/apps-routing/src/index.ts index 9003f55026..64f65a2e39 100644 --- a/packages/apps-routing/src/index.ts +++ b/packages/apps-routing/src/index.ts @@ -28,6 +28,7 @@ import staking from './staking'; import storage from './storage'; import sudo from './sudo'; import techcomm from './techcomm'; +import teleport from './teleport'; import transfer from './transfer'; import treasury from './treasury'; @@ -39,6 +40,7 @@ export default function create (t: TFunction): Routes { claims(t), poll(t), transfer(t), + teleport(t), staking(t), democracy(t), council(t), diff --git a/packages/apps-routing/src/teleport.ts b/packages/apps-routing/src/teleport.ts new file mode 100644 index 0000000000..d9830db91f --- /dev/null +++ b/packages/apps-routing/src/teleport.ts @@ -0,0 +1,30 @@ +// Copyright 2017-2021 @polkadot/apps-routing authors & contributors +// SPDX-License-Identifier: Apache-2.0 + +import type { TFunction } from 'i18next'; +import type { Route } from './types'; + +import Modal from '@polkadot/app-parachains/Teleport'; + +export default function create (t: TFunction): Route { + return { + Component: Modal, + Modal, + display: { + isHidden: false, + needsAccounts: true, + needsApi: [ + [ + 'tx.xcm.teleportAssets', + 'tx.xcmPallet.teleportAssets', + 'tx.polkadotXcm.teleportAssets' + ] + ], + needsTeleport: true + }, + group: 'accounts', + icon: 'share-square', + name: 'teleport', + text: t('nav.teleport', 'Teleport', { ns: 'apps-routing' }) + }; +} diff --git a/packages/apps-routing/src/types.ts b/packages/apps-routing/src/types.ts index e58fe5965d..cd2fe55814 100644 --- a/packages/apps-routing/src/types.ts +++ b/packages/apps-routing/src/types.ts @@ -11,14 +11,16 @@ export interface RouteProps extends AppProps, BareProps { } export interface Route { - Component: React.ComponentType; - Modal?: React.ComponentType; + // FIXME This is weird, we really expect the memo to be there... + Component: React.ComponentType | React.MemoExoticComponent; + Modal?: React.ComponentType | React.MemoExoticComponent; display: { isHidden?: boolean; isModal?: boolean; needsAccounts?: boolean; needsApi?: (string | string[])[]; needsSudo?: boolean; + needsTeleport?: boolean; }; group: RouteGroup; icon: IconName; diff --git a/packages/apps/src/Endpoints/index.tsx b/packages/apps/src/Endpoints/index.tsx index dd3f944b26..2532f5d875 100644 --- a/packages/apps/src/Endpoints/index.tsx +++ b/packages/apps/src/Endpoints/index.tsx @@ -1,7 +1,7 @@ // Copyright 2017-2021 @polkadot/apps authors & contributors // SPDX-License-Identifier: Apache-2.0 -import type { LinkOption } from '@polkadot/apps-config/settings/types'; +import type { LinkOption } from '@polkadot/apps-config/endpoints/types'; import type { Group } from './types'; // ok, this seems to be an eslint bug, this _is_ a package import diff --git a/packages/apps/src/Menu/index.tsx b/packages/apps/src/Menu/index.tsx index 988c6de77f..903ddd3cd1 100644 --- a/packages/apps/src/Menu/index.tsx +++ b/packages/apps/src/Menu/index.tsx @@ -12,7 +12,7 @@ import { useLocation } from 'react-router-dom'; import styled from 'styled-components'; import createRoutes from '@polkadot/apps-routing'; -import { useAccounts, useApi, useCall } from '@polkadot/react-hooks'; +import { useAccounts, useApi, useCall, useTeleport } from '@polkadot/react-hooks'; import { findMissingApis } from '../endpoint'; import { useTranslation } from '../translate'; @@ -42,7 +42,7 @@ function createExternals (t: TFunction): ItemRoute[] { ]; } -function checkVisible ({ api, isApiConnected, isApiReady }: ApiProps, hasAccounts: boolean, hasSudo: boolean, { isHidden, needsAccounts, needsApi, needsSudo }: Route['display']): boolean { +function checkVisible ({ api, isApiConnected, isApiReady }: ApiProps, allowTeleport: boolean, hasAccounts: boolean, hasSudo: boolean, { isHidden, needsAccounts, needsApi, needsSudo, needsTeleport }: Route['display']): boolean { if (isHidden) { return false; } else if (needsAccounts && !hasAccounts) { @@ -53,12 +53,14 @@ function checkVisible ({ api, isApiConnected, isApiReady }: ApiProps, hasAccount return false; } else if (needsSudo && !hasSudo) { return false; + } else if (needsTeleport && !allowTeleport) { + return false; } return findMissingApis(api, needsApi).length === 0; } -function extractGroups (routing: Routes, groupNames: Record, apiProps: ApiProps, hasAccounts: boolean, hasSudo: boolean): Group[] { +function extractGroups (routing: Routes, groupNames: Record, apiProps: ApiProps, allowTeleport: boolean, hasAccounts: boolean, hasSudo: boolean): Group[] { return Object .values( routing.reduce((all: Groups, route): Groups => { @@ -77,7 +79,7 @@ function extractGroups (routing: Routes, groupNames: Record, api .map(({ name, routes }): Group => ({ name, routes: routes.filter(({ display }) => - checkVisible(apiProps, hasAccounts, hasSudo, display) + checkVisible(apiProps, allowTeleport, hasAccounts, hasSudo, display) ) })) .filter(({ routes }) => routes.length); @@ -87,6 +89,7 @@ function Menu ({ className = '' }: Props): React.ReactElement { const { t } = useTranslation(); const { allAccounts, hasAccounts } = useAccounts(); const apiProps = useApi(); + const { allowTeleport } = useTeleport(); const sudoKey = useCall(apiProps.isApiReady && apiProps.api.query.sudo?.key); const location = useLocation(); @@ -107,8 +110,8 @@ function Menu ({ className = '' }: Props): React.ReactElement { ); const visibleGroups = useMemo( - () => extractGroups(routeRef.current, groupRef.current, apiProps, hasAccounts, hasSudo), - [apiProps, hasAccounts, hasSudo] + () => extractGroups(routeRef.current, groupRef.current, apiProps, allowTeleport, hasAccounts, hasSudo), + [allowTeleport, apiProps, hasAccounts, hasSudo] ); const activeRoute = useMemo( diff --git a/packages/page-parachains/src/Teleport.tsx b/packages/page-parachains/src/Teleport.tsx new file mode 100644 index 0000000000..43b627dfa4 --- /dev/null +++ b/packages/page-parachains/src/Teleport.tsx @@ -0,0 +1,124 @@ +// Copyright 2017-2021 @polkadot/app-parachains authors & contributors +// SPDX-License-Identifier: Apache-2.0 + +import BN from 'bn.js'; +import React, { useMemo, useState } from 'react'; + +import { Dropdown, InputAddress, InputBalance, Modal, TxButton } from '@polkadot/react-components'; +import { useApi, useTeleport } from '@polkadot/react-hooks'; +import { Available } from '@polkadot/react-query'; +import { BN_ZERO } from '@polkadot/util'; + +import { useTranslation } from './translate'; + +interface Props { + onClose: () => void; +} + +const INVALID_PARAID = Number.MAX_SAFE_INTEGER; +const DEFAULT_WEIGHT = 1_000_000_000; + +function Teleport ({ onClose }: Props): React.ReactElement | null { + const { t } = useTranslation(); + const { api } = useApi(); + const [amount, setAmount] = useState(BN_ZERO); + const [hasAvailable] = useState(true); + const [recipientId, setRecipientId] = useState(null); + const [senderId, setSenderId] = useState(null); + const [recipientParaId, setParaId] = useState(INVALID_PARAID); + // FIXME We need to actually calculate the destination weight + const [weight] = useState(DEFAULT_WEIGHT); + const { allowTeleport, destinations, isParachain } = useTeleport(); + + const chainOpts = useMemo( + () => destinations.map(({ paraId, text }, index) => ({ text, value: paraId || index })), + [destinations] + ); + + const params = useMemo( + () => isParachain + ? [ + { X1: 'Parent' }, + { X1: { AccountId32: { id: recipientId, network: 'Any' } } }, + [{ ConcreteFungible: { amount, id: { X1: 'Parent' } } }], + weight + ] + : [ + { X1: { ParaChain: recipientParaId } }, + { X1: { AccountId32: { id: recipientId, network: 'Any' } } }, + [{ ConcreteFungible: { amount, id: 'Null' } }], + weight + ], + [amount, isParachain, recipientId, recipientParaId, weight] + ); + + console.log('Teleport', allowTeleport, chainOpts); + + if (!allowTeleport || !chainOpts.length) { + return null; + } + + return ( + ('Teleport assets')} + size='large' + > + + ('The transferred balance will be subtracted (along with fees) from the sender account.')}> + ('send from account')} + labelExtra={ + ('transferrable')} + params={senderId} + /> + } + onChange={setSenderId} + type='account' + /> + + ('The destination chain for this asset teleport. The transferred value will appear on this chain.')}> + ('destination chain')} + onChange={setParaId} + options={chainOpts} + /> + + ('The beneficiary will have access to the transferred amount when the transaction is included in a block.')}> + ('send to address')} + onChange={setRecipientId} + type='allPlus' + /> + + ('If the recipient account is new, the balance needs to be more than the existential deposit on the recipient chain.')}> + ('amount')} + onChange={setAmount} + /> + + + + ('Teleport')} + onStart={onClose} + params={params} + tx={ + (api.tx.xcm && api.tx.xcm.teleportAssets) || + (api.tx.xcmPallet && api.tx.xcmPallet.teleportAssets) || + (api.tx.polkadotXcm && api.tx.polkadotXcm.teleportAssets) + } + /> + + + ); +} + +export default React.memo(Teleport); diff --git a/packages/react-api/src/Api.tsx b/packages/react-api/src/Api.tsx index c076fdc942..895d65cc80 100644 --- a/packages/react-api/src/Api.tsx +++ b/packages/react-api/src/Api.tsx @@ -186,8 +186,8 @@ function Api ({ children, store, url }: Props): React.ReactElement | null const [extensions, setExtensions] = useState(); const value = useMemo( - () => ({ ...state, api, apiError, extensions, isApiConnected, isApiInitialized, isWaitingInjected: !extensions }), - [apiError, extensions, isApiConnected, isApiInitialized, state] + () => ({ ...state, api, apiError, apiUrl: url, extensions, isApiConnected, isApiInitialized, isWaitingInjected: !extensions }), + [apiError, extensions, isApiConnected, isApiInitialized, state, url] ); // initial initialization diff --git a/packages/react-api/src/types.ts b/packages/react-api/src/types.ts index 9f07b3153d..fb7bf1b64c 100644 --- a/packages/react-api/src/types.ts +++ b/packages/react-api/src/types.ts @@ -31,6 +31,7 @@ export interface ApiState { export interface ApiProps extends ApiState { api: ApiPromise; apiError: string | null; + apiUrl?: string; extensions?: InjectedExtension[]; isApiConnected: boolean; isApiInitialized: boolean; diff --git a/packages/react-hooks/src/index.ts b/packages/react-hooks/src/index.ts index 2d33207fdf..8540731308 100644 --- a/packages/react-hooks/src/index.ts +++ b/packages/react-hooks/src/index.ts @@ -40,6 +40,7 @@ export { useRegistrars } from './useRegistrars'; export { useSavedFlags } from './useSavedFlags'; export { useStepper } from './useStepper'; export { useSudo } from './useSudo'; +export { useTeleport } from './useTeleport'; export { useToggle } from './useToggle'; export { useTreasury } from './useTreasury'; export { useTxBatch } from './useTxBatch'; diff --git a/packages/react-hooks/src/useParaApi.ts b/packages/react-hooks/src/useParaApi.ts index aa1aff7430..31be4414d6 100644 --- a/packages/react-hooks/src/useParaApi.ts +++ b/packages/react-hooks/src/useParaApi.ts @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 import type BN from 'bn.js'; -import type { LinkOption } from '@polkadot/apps-config/settings/types'; +import type { LinkOption } from '@polkadot/apps-config/endpoints/types'; import { useCallback, useEffect, useRef, useState } from 'react'; diff --git a/packages/react-hooks/src/useParaEndpoints.ts b/packages/react-hooks/src/useParaEndpoints.ts index ff5a66553f..86c0e43cae 100644 --- a/packages/react-hooks/src/useParaEndpoints.ts +++ b/packages/react-hooks/src/useParaEndpoints.ts @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 import type BN from 'bn.js'; -import type { LinkOption } from '@polkadot/apps-config/settings/types'; +import type { LinkOption } from '@polkadot/apps-config/endpoints/types'; import { useMemo } from 'react'; @@ -11,8 +11,10 @@ import { bnToBn } from '@polkadot/util'; import { useApi } from './useApi'; +const endpoints = createWsEndpoints((key: string, value: string | undefined) => value || key); + function extractRelayEndpoints (genesisHash: string): LinkOption[] { - return createWsEndpoints((key: string, value: string | undefined) => value || key).filter(({ genesisHashRelay }) => + return endpoints.filter(({ genesisHashRelay }) => genesisHash === genesisHashRelay ); } @@ -23,7 +25,7 @@ function extractParaEndpoints (allEndpoints: LinkOption[], paraId: BN | number): return allEndpoints.filter(({ paraId }) => paraId === numId); } -function useRelayEndpoints (): LinkOption[] { +export function useRelayEndpoints (): LinkOption[] { const { api } = useApi(); return useMemo( @@ -46,7 +48,10 @@ export function useIsParasLinked (ids?: (BN | number)[] | null): Record ids - ? ids.reduce((all: Record, id) => ({ ...all, [id.toString()]: extractParaEndpoints(endpoints, id).length !== 0 }), {}) + ? ids.reduce((all: Record, id) => ({ + ...all, + [id.toString()]: extractParaEndpoints(endpoints, id).length !== 0 + }), {}) : {}, [endpoints, ids] ); diff --git a/packages/react-hooks/src/useTeleport.ts b/packages/react-hooks/src/useTeleport.ts new file mode 100644 index 0000000000..9a0da5cdc7 --- /dev/null +++ b/packages/react-hooks/src/useTeleport.ts @@ -0,0 +1,93 @@ +// Copyright 2017-2021 @polkadot/react-hooks authors & contributors +// SPDX-License-Identifier: Apache-2.0 + +import type { LinkOption } from '@polkadot/apps-config/endpoints/types'; +import type { ParaId } from '@polkadot/types/interfaces'; + +import { useEffect, useState } from 'react'; + +import { createWsEndpoints } from '@polkadot/apps-config'; + +import { useApi } from './useApi'; +import { useCall } from './useCall'; + +interface Teleport { + allowTeleport: boolean; + destinations: LinkOption[]; + isParachain: boolean; + paraId?: ParaId; +} + +const endpoints = createWsEndpoints((k: string, v: string | undefined) => v || k).filter(({ allowTeleport }) => allowTeleport); + +function extractRelayDestinations (relayGenesis: string, filter: (l: LinkOption) => boolean): LinkOption[] { + return endpoints + .filter((l) => + ( + l.genesisHashRelay === relayGenesis || + l.genesisHash === relayGenesis + ) && filter(l) + ) + .reduce((result: LinkOption[], curr): LinkOption[] => { + if (!result.some(({ genesisHash, paraId }) => paraId === curr.paraId || genesisHash === curr.genesisHash)) { + result.push(curr); + } + + return result; + }, []) + .sort((a, b) => + a.isRelay === b.isRelay + ? 0 + : a.isRelay + ? -1 + : 1 + ); +} + +export function useTeleport (): Teleport { + const { api, apiUrl, isApiReady } = useApi(); + const chainParaId = useCall(isApiReady && api.query.parachainInfo?.parachainId); + const [state, setState] = useState(() => ({ + allowTeleport: false, + destinations: [], + isParachain: false + })); + + useEffect((): void => { + if (isApiReady) { + const relayGenesis = api.genesisHash.toHex(); + const destinations = extractRelayDestinations(relayGenesis, ({ genesisHash }) => relayGenesis !== genesisHash); + + setState((prev) => ({ + ...prev, + allowTeleport: destinations.length !== 0, + destinations + })); + } + }, [api, isApiReady]); + + useEffect((): void => { + if (chainParaId) { + const endpoint = endpoints.find(({ paraId, value }) => + chainParaId.eq(paraId) && + value === apiUrl + ); + + setState((prev) => ({ + ...prev, + // FIXME Cannot quite get this working... + allowTeleport: false, // !!endpoint, + destinations: endpoint && endpoint.genesisHashRelay + // FIXME we probably just want to check for !chainParaId.eq(paraId) || !!genesisHash (would affect Teleport modal) + ? extractRelayDestinations(endpoint.genesisHashRelay, ({ genesisHash }) => !!genesisHash) + : [], + isParachain: true, + paraId: chainParaId + })); + } + }, [apiUrl, chainParaId]); + + console.log('useTeleport', JSON.stringify(state)); + + return state; +}