Links to parachains (?rpc=) & best block (#4395)
* Links to parachains (?rpc=) & best block * Adjust * Add known types to ParaApi
This commit is contained in:
parent
a642db2f0d
commit
f93cc49db6
@ -16,6 +16,7 @@ import type { EndpointOption } from './types';
|
||||
export function createRococo (t: TFunction): EndpointOption {
|
||||
return {
|
||||
dnslink: 'rococo',
|
||||
genesisHash: '0x481550b70e974177e83cff4d554cea7166be3eaeb32f020b86542e76968ccd0e',
|
||||
info: 'rococo',
|
||||
text: t('rpc.rococo', 'Rococo', { ns: 'apps-config' }),
|
||||
providers: {
|
||||
@ -27,6 +28,7 @@ export function createRococo (t: TFunction): EndpointOption {
|
||||
// these are the base chains
|
||||
{
|
||||
info: 'rococoTick',
|
||||
paraId: 100,
|
||||
text: t('rpc.rococo.tick', 'Tick', { ns: 'apps-config' }),
|
||||
providers: {
|
||||
Parity: 'wss://tick-rpc.polkadot.io'
|
||||
@ -34,6 +36,7 @@ export function createRococo (t: TFunction): EndpointOption {
|
||||
},
|
||||
{
|
||||
info: 'rococoTrick',
|
||||
paraId: 110,
|
||||
text: t('rpc.rococo.trick', 'Trick', { ns: 'apps-config' }),
|
||||
providers: {
|
||||
Parity: 'wss://trick-rpc.polkadot.io'
|
||||
@ -41,6 +44,7 @@ export function createRococo (t: TFunction): EndpointOption {
|
||||
},
|
||||
{
|
||||
info: 'rococoTrack',
|
||||
paraId: 120,
|
||||
text: t('rpc.rococo.track', 'Track', { ns: 'apps-config' }),
|
||||
providers: {
|
||||
Parity: 'wss://track-rpc.polkadot.io'
|
||||
@ -49,6 +53,7 @@ export function createRococo (t: TFunction): EndpointOption {
|
||||
// add any additional parachains here, alphabetical
|
||||
{
|
||||
info: 'rococoPlasm',
|
||||
paraId: 5000,
|
||||
text: t('rpc.rococo.plasm', 'Plasm PC2', { ns: 'apps-config' }),
|
||||
providers: {
|
||||
PlasmNetwork: 'wss://rpc.rococo.plasmnet.io'
|
||||
|
||||
@ -3,11 +3,13 @@
|
||||
|
||||
export interface EndpointOption {
|
||||
dnslink?: string;
|
||||
genesisHash?: string;
|
||||
isChild?: boolean;
|
||||
isDevelopment?: boolean;
|
||||
isDisabled?: boolean;
|
||||
linked?: EndpointOption[];
|
||||
info?: string;
|
||||
paraId?: number;
|
||||
providers: Record<string, string>;
|
||||
text: React.ReactNode;
|
||||
}
|
||||
|
||||
@ -11,10 +11,11 @@ export function expandLinked (input: LinkOption[]): LinkOption[] {
|
||||
|
||||
return entry.linked
|
||||
? result.concat(
|
||||
expandLinked(entry.linked).map((entry): LinkOption => {
|
||||
entry.isChild = true;
|
||||
expandLinked(entry.linked).map((child): LinkOption => {
|
||||
child.genesisHashRelay = entry.genesisHash;
|
||||
child.isChild = true;
|
||||
|
||||
return entry;
|
||||
return child;
|
||||
})
|
||||
)
|
||||
: result;
|
||||
@ -22,11 +23,13 @@ export function expandLinked (input: LinkOption[]): LinkOption[] {
|
||||
}
|
||||
|
||||
export function expandEndpoint (t: TFunction, input: EndpointOption): LinkOption[] {
|
||||
const { dnslink, info, isChild, isDisabled, linked, providers, text } = input;
|
||||
const { dnslink, genesisHash, info, isChild, isDisabled, linked, paraId, providers, text } = input;
|
||||
const base = {
|
||||
genesisHash,
|
||||
info,
|
||||
isChild,
|
||||
isDisabled,
|
||||
paraId,
|
||||
text
|
||||
};
|
||||
|
||||
|
||||
@ -11,8 +11,11 @@ export interface Option {
|
||||
|
||||
export interface LinkOption extends Option {
|
||||
dnslink?: string;
|
||||
genesisHash?: string;
|
||||
genesisHashRelay?: string;
|
||||
isChild?: boolean;
|
||||
isDevelopment?: boolean;
|
||||
linked?: LinkOption[];
|
||||
paraId?: number;
|
||||
textBy: string;
|
||||
}
|
||||
|
||||
@ -1,20 +1,21 @@
|
||||
// Copyright 2017-2021 @polkadot/app-parachains authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { LinkOption } from '@polkadot/apps-config/settings/types';
|
||||
import type { Option } from '@polkadot/types';
|
||||
import type { BlockNumber, HeadData, ParaId } from '@polkadot/types/interfaces';
|
||||
|
||||
import BN from 'bn.js';
|
||||
import React, { useMemo } from 'react';
|
||||
|
||||
import { useApi, useCall } from '@polkadot/react-hooks';
|
||||
import { useApi, useCall, useParaApi } from '@polkadot/react-hooks';
|
||||
import { BlockToTime } from '@polkadot/react-query';
|
||||
import { formatNumber } from '@polkadot/util';
|
||||
|
||||
import { sliceHex } from './util';
|
||||
|
||||
interface Props {
|
||||
bestNumber?: BN;
|
||||
bestNumberFinalized?: BN;
|
||||
className?: string;
|
||||
id: ParaId;
|
||||
}
|
||||
@ -33,23 +34,42 @@ const transformMark = {
|
||||
: null
|
||||
};
|
||||
|
||||
function Parachain ({ bestNumber, className = '', id }: Props): React.ReactElement<Props> {
|
||||
function getChainLink (endpoints: LinkOption[]): React.ReactNode {
|
||||
if (!endpoints.length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const { text, value } = endpoints[endpoints.length - 1];
|
||||
|
||||
return <a href={`${window.location.origin}${window.location.pathname}?rpc=${encodeURIComponent(value)}`}>{text}</a>;
|
||||
}
|
||||
|
||||
function Parachain ({ bestNumberFinalized, className = '', id }: Props): React.ReactElement<Props> {
|
||||
const { api } = useApi();
|
||||
const { api: paraApi, endpoints } = useParaApi(id);
|
||||
const headHex = useCall<string | null>(api.query.paras.heads, [id], transformHead);
|
||||
const watermark = useCall<BlockNumber | null>(api.query.hrmp?.hrmpWatermarks, [id], transformMark);
|
||||
const paraBest = useCall<BlockNumber>(paraApi?.derive.chain.bestNumber);
|
||||
// const endpoints = useParaEndpoints(id);
|
||||
|
||||
const blockDelay = useMemo(
|
||||
() => watermark && bestNumber && bestNumber.sub(watermark),
|
||||
[bestNumber, watermark]
|
||||
() => watermark && bestNumberFinalized && bestNumberFinalized.sub(watermark),
|
||||
[bestNumberFinalized, watermark]
|
||||
);
|
||||
|
||||
const chainLink = useMemo(
|
||||
() => getChainLink(endpoints),
|
||||
[endpoints]
|
||||
);
|
||||
|
||||
return (
|
||||
<tr className={className}>
|
||||
<td className='number'><h1>{formatNumber(id)}</h1></td>
|
||||
<td />
|
||||
<td className='together'>{chainLink}</td>
|
||||
<td className='all start together hash'>{headHex}</td>
|
||||
<td className='number'>{formatNumber(watermark)}</td>
|
||||
<td className='number'>{blockDelay && <BlockToTime blocks={blockDelay} />}</td>
|
||||
<td className='number'>{formatNumber(watermark)}</td>
|
||||
<td className='number'>{paraBest && formatNumber(paraBest)}</td>
|
||||
</tr>
|
||||
);
|
||||
}
|
||||
|
||||
@ -19,12 +19,13 @@ interface Props {
|
||||
function ParachainList ({ ids }: Props): React.ReactElement<Props> {
|
||||
const { t } = useTranslation();
|
||||
const { api } = useApi();
|
||||
const bestNumber = useCall<BN>(api.derive.chain.bestNumber);
|
||||
const bestNumberFinalized = useCall<BN>(api.derive.chain.bestNumberFinalized);
|
||||
|
||||
const headerRef = useRef([
|
||||
[t('parachains'), 'start', 2],
|
||||
[t('heads'), 'start'],
|
||||
[t('watermark'), 2]
|
||||
[t('watermark'), 'undefined', 2],
|
||||
[t('chain best')]
|
||||
]);
|
||||
|
||||
return (
|
||||
@ -34,7 +35,7 @@ function ParachainList ({ ids }: Props): React.ReactElement<Props> {
|
||||
>
|
||||
{ids?.map((id): React.ReactNode => (
|
||||
<Parachain
|
||||
bestNumber={bestNumber}
|
||||
bestNumberFinalized={bestNumberFinalized}
|
||||
id={id}
|
||||
key={id.toString()}
|
||||
/>
|
||||
|
||||
@ -49,9 +49,9 @@ function Summary ({ parachainCount, proposalCount, upcomingCount }: Props): Reac
|
||||
<section>
|
||||
<CardSummary
|
||||
className='media--800'
|
||||
label={t<string>('best block')}
|
||||
label={t<string>('finalized block')}
|
||||
>
|
||||
<BestNumber />
|
||||
<BestNumber isFinalized />
|
||||
</CardSummary>
|
||||
<SummarySession withEra={false} />
|
||||
</section>
|
||||
|
||||
@ -27,6 +27,8 @@ export { useNonZeroBn } from './useNonZeroBn';
|
||||
export { useOwnEraRewards } from './useOwnEraRewards';
|
||||
export { useOwnStashes, useOwnStashIds } from './useOwnStashes';
|
||||
export { useOwnStashInfos } from './useOwnStashInfos';
|
||||
export { useParaApi } from './useParaApi';
|
||||
export { useParaEndpoints } from './useParaEndpoints';
|
||||
export { usePassword } from './usePassword';
|
||||
export { useRegistrars } from './useRegistrars';
|
||||
export { useSavedFlags } from './useSavedFlags';
|
||||
|
||||
66
packages/react-hooks/src/useParaApi.ts
Normal file
66
packages/react-hooks/src/useParaApi.ts
Normal file
@ -0,0 +1,66 @@
|
||||
// Copyright 2017-2021 @polkadot/react-hooks authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type BN from 'bn.js';
|
||||
import type { LinkOption } from '@polkadot/apps-config/settings/types';
|
||||
|
||||
import { useCallback, useEffect, useRef, useState } from 'react';
|
||||
|
||||
import { ApiPromise, WsProvider } from '@polkadot/api';
|
||||
import { typesBundle, typesChain, typesSpec } from '@polkadot/apps-config';
|
||||
|
||||
import { useIsMountedRef } from './useIsMountedRef';
|
||||
import { useParaEndpoints } from './useParaEndpoints';
|
||||
|
||||
interface Result {
|
||||
api?: ApiPromise | null;
|
||||
endpoints: LinkOption[];
|
||||
}
|
||||
|
||||
function disconnect (api: ApiPromise | null): void {
|
||||
api && api.disconnect().catch(console.error);
|
||||
}
|
||||
|
||||
export function useParaApi (paraId: BN | number): Result {
|
||||
const apiRef = useRef<ApiPromise | null>(null);
|
||||
const mountedRef = useIsMountedRef();
|
||||
const endpoints = useParaEndpoints(paraId);
|
||||
const [state, setState] = useState<Result>({ api: null, endpoints });
|
||||
|
||||
useEffect((): () => void => {
|
||||
return (): void => {
|
||||
disconnect(apiRef.current);
|
||||
apiRef.current = null;
|
||||
};
|
||||
}, []);
|
||||
|
||||
const _setApi = useCallback(
|
||||
(api: ApiPromise | null): void => {
|
||||
disconnect(apiRef.current);
|
||||
|
||||
if (mountedRef.current) {
|
||||
apiRef.current = api;
|
||||
setState(({ endpoints }) => ({ api, endpoints }));
|
||||
}
|
||||
},
|
||||
[mountedRef]
|
||||
);
|
||||
|
||||
useEffect((): void => {
|
||||
if (endpoints.length) {
|
||||
ApiPromise
|
||||
.create({
|
||||
provider: new WsProvider(endpoints[endpoints.length - 1].value as string),
|
||||
typesBundle,
|
||||
typesChain,
|
||||
typesSpec
|
||||
})
|
||||
.then(_setApi)
|
||||
.catch(console.error);
|
||||
} else {
|
||||
_setApi(null);
|
||||
}
|
||||
}, [_setApi, endpoints]);
|
||||
|
||||
return state;
|
||||
}
|
||||
29
packages/react-hooks/src/useParaEndpoints.ts
Normal file
29
packages/react-hooks/src/useParaEndpoints.ts
Normal file
@ -0,0 +1,29 @@
|
||||
// Copyright 2017-2021 @polkadot/react-hooks authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type BN from 'bn.js';
|
||||
import type { LinkOption } from '@polkadot/apps-config/settings/types';
|
||||
|
||||
import { useMemo } from 'react';
|
||||
|
||||
import { createWsEndpoints } from '@polkadot/apps-config';
|
||||
import { bnToBn } from '@polkadot/util';
|
||||
|
||||
import { useApi } from './useApi';
|
||||
|
||||
function extractResults (genesisHash: string, paraId: BN | number): LinkOption[] {
|
||||
const numId = bnToBn(paraId).toNumber();
|
||||
|
||||
return createWsEndpoints((key: string, value: string | undefined) => value || key).filter(({ genesisHashRelay, paraId }) =>
|
||||
paraId === numId && genesisHashRelay === genesisHash
|
||||
);
|
||||
}
|
||||
|
||||
export function useParaEndpoints (paraId: BN | number): LinkOption[] {
|
||||
const { api } = useApi();
|
||||
|
||||
return useMemo(
|
||||
() => extractResults(api.genesisHash.toHex(), paraId),
|
||||
[api, paraId]
|
||||
);
|
||||
}
|
||||
@ -11,13 +11,14 @@ import { formatNumber } from '@polkadot/util';
|
||||
interface Props {
|
||||
children?: React.ReactNode;
|
||||
className?: string;
|
||||
isFinalized?: boolean;
|
||||
label?: React.ReactNode;
|
||||
withPound?: boolean;
|
||||
}
|
||||
|
||||
function BestNumber ({ children, className = '', label, withPound }: Props): React.ReactElement<Props> {
|
||||
function BestNumber ({ children, className = '', isFinalized, label, withPound }: Props): React.ReactElement<Props> {
|
||||
const { api, isApiReady } = useApi();
|
||||
const bestNumber = useCall<BlockNumber>(isApiReady && api.derive.chain.bestNumber);
|
||||
const bestNumber = useCall<BlockNumber>(isApiReady && (isFinalized ? api.derive.chain.bestNumberFinalized : api.derive.chain.bestNumber));
|
||||
|
||||
return (
|
||||
<div className={className}>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user