feat(oracles): add oracle full profile
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import type { ComponentProps } from 'react';
|
||||
import { useState } from 'react';
|
||||
import { useMemo } from 'react';
|
||||
import { AssetDetailsTable, useAssetDataProvider } from '@vegaprotocol/assets';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
@@ -6,7 +7,7 @@ import {
|
||||
totalFeesPercentage,
|
||||
marketDataProvider,
|
||||
} from '@vegaprotocol/market-list';
|
||||
import { ExternalLink, Splash } from '@vegaprotocol/ui-toolkit';
|
||||
import { Dialog, ExternalLink, Splash } from '@vegaprotocol/ui-toolkit';
|
||||
import {
|
||||
addDecimalsFormatNumber,
|
||||
formatNumber,
|
||||
@@ -26,6 +27,7 @@ import { ConditionOperatorMapping } from '@vegaprotocol/types';
|
||||
import { MarketTradingModeMapping } from '@vegaprotocol/types';
|
||||
import { useEnvironment } from '@vegaprotocol/environment';
|
||||
import type { Provider } from '@vegaprotocol/oracles';
|
||||
import { OracleProfileTitle, OracleFullProfile } from '@vegaprotocol/oracles';
|
||||
import { OracleBasicProfile, useOracleProofs } from '@vegaprotocol/oracles';
|
||||
import { useDataProvider } from '@vegaprotocol/react-helpers';
|
||||
|
||||
@@ -456,6 +458,11 @@ export const OracleInfoPanel = ({
|
||||
const { VEGA_EXPLORER_URL, ORACLE_PROOFS_URL } = useEnvironment();
|
||||
const { data } = useOracleProofs(ORACLE_PROOFS_URL);
|
||||
|
||||
const oracleId =
|
||||
type === 'settlementData'
|
||||
? product.dataSourceSpecForSettlementData.id
|
||||
: product.dataSourceSpecForTradingTermination.id;
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-4">
|
||||
<DataSourceProof
|
||||
@@ -467,14 +474,11 @@ export const OracleInfoPanel = ({
|
||||
}
|
||||
providers={data}
|
||||
type={type}
|
||||
id={oracleId}
|
||||
/>
|
||||
<ExternalLink
|
||||
data-testid="oracle-spec-links"
|
||||
href={`${VEGA_EXPLORER_URL}/oracles/${
|
||||
type === 'settlementData'
|
||||
? product.dataSourceSpecForSettlementData.id
|
||||
: product.dataSourceSpecForTradingTermination.id
|
||||
}`}
|
||||
href={`${VEGA_EXPLORER_URL}/oracles/${oracleId}`}
|
||||
>
|
||||
{type === 'settlementData'
|
||||
? t('View settlement data specification')
|
||||
@@ -488,10 +492,12 @@ export const DataSourceProof = ({
|
||||
data,
|
||||
providers,
|
||||
type,
|
||||
id,
|
||||
}: {
|
||||
data: DataSourceDefinition;
|
||||
providers: Provider[] | undefined;
|
||||
type: 'settlementData' | 'termination';
|
||||
id: string;
|
||||
}) => {
|
||||
if (data.sourceType.__typename === 'DataSourceDefinitionExternal') {
|
||||
const signers = data.sourceType.sourceType.signers || [];
|
||||
@@ -509,7 +515,7 @@ export const DataSourceProof = ({
|
||||
providers={providers}
|
||||
signer={signer}
|
||||
type={type}
|
||||
index={i}
|
||||
id={id}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
@@ -540,12 +546,14 @@ const OracleLink = ({
|
||||
providers,
|
||||
signer,
|
||||
type,
|
||||
id,
|
||||
}: {
|
||||
providers: Provider[];
|
||||
signer: SignerKind;
|
||||
type: 'settlementData' | 'termination';
|
||||
index: number;
|
||||
id: string;
|
||||
}) => {
|
||||
const [dialogOpen, setDialogOpen] = useState(false);
|
||||
const signerProviders = providers.filter((p) => {
|
||||
if (signer.__typename === 'PubKey') {
|
||||
if (
|
||||
@@ -575,10 +583,20 @@ const OracleLink = ({
|
||||
return (
|
||||
<div>
|
||||
{signerProviders.map((provider) => (
|
||||
<OracleBasicProfile
|
||||
key={provider.name}
|
||||
provider={provider}
|
||||
></OracleBasicProfile>
|
||||
<div key={provider.name}>
|
||||
<OracleBasicProfile
|
||||
provider={provider}
|
||||
onClick={() => setDialogOpen(!dialogOpen)}
|
||||
></OracleBasicProfile>
|
||||
<Dialog
|
||||
title={<OracleProfileTitle provider={provider} />}
|
||||
open={dialogOpen}
|
||||
onChange={() => setDialogOpen(!dialogOpen)}
|
||||
aria-labelledby="oracle-proof-dialog"
|
||||
>
|
||||
<OracleFullProfile provider={provider} id={id} />
|
||||
</Dialog>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
export * from './oracle-basic-profile';
|
||||
export * from './oracle-full-profile';
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import type { Provider } from '../../oracle-schema';
|
||||
import {
|
||||
ButtonLink,
|
||||
ExternalLink,
|
||||
Icon,
|
||||
Intent,
|
||||
Link,
|
||||
VegaIcon,
|
||||
VegaIconNames,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
@@ -12,7 +12,7 @@ import type { IconName } from '@blueprintjs/icons';
|
||||
import { IconNames } from '@blueprintjs/icons';
|
||||
import classNames from 'classnames';
|
||||
|
||||
const getVerifiedStatusIcon = (provider: Provider) => {
|
||||
export const getVerifiedStatusIcon = (provider: Provider) => {
|
||||
const getIconIntent = () => {
|
||||
switch (provider.oracle.status) {
|
||||
case 'GOOD':
|
||||
@@ -54,7 +54,13 @@ const getVerifiedStatusIcon = (provider: Provider) => {
|
||||
};
|
||||
};
|
||||
|
||||
export const OracleBasicProfile = ({ provider }: { provider: Provider }) => {
|
||||
export const OracleBasicProfile = ({
|
||||
provider,
|
||||
onClick,
|
||||
}: {
|
||||
provider: Provider;
|
||||
onClick?: (value?: boolean) => void;
|
||||
}) => {
|
||||
const { icon, message, intent } = getVerifiedStatusIcon(provider);
|
||||
|
||||
const verifiedProofs = provider.proofs.filter(
|
||||
@@ -77,18 +83,14 @@ export const OracleBasicProfile = ({ provider }: { provider: Provider }) => {
|
||||
<>
|
||||
<span className="flex gap-1">
|
||||
{provider.url && (
|
||||
<Link
|
||||
href={provider.github_link}
|
||||
className="flex align-items-bottom text-md"
|
||||
target="_blank"
|
||||
>
|
||||
<span>
|
||||
<span className="underline pr-1">{provider.name}</span>
|
||||
<span className="dark:text-vega-light-300 text-vega-dark-300">
|
||||
({verifiedProofs.length})
|
||||
</span>
|
||||
<span className="flex align-items-bottom text-md gap-1">
|
||||
<ButtonLink onClick={() => onClick && onClick(true)}>
|
||||
{provider.name}
|
||||
</ButtonLink>
|
||||
<span className="dark:text-vega-light-300 text-vega-dark-300">
|
||||
({verifiedProofs.length})
|
||||
</span>
|
||||
</Link>
|
||||
</span>
|
||||
)}
|
||||
<span
|
||||
className={classNames(
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
export * from './oracle-full-profile.stories';
|
||||
export * from './oracle-full-profile';
|
||||
@@ -0,0 +1,41 @@
|
||||
import type { Story, Meta } from '@storybook/react';
|
||||
import { OracleFullProfile } from './oracle-full-profile';
|
||||
|
||||
export default {
|
||||
component: OracleFullProfile,
|
||||
title: 'OracleFullProfile',
|
||||
} as Meta;
|
||||
|
||||
const Template: Story = (args) => (
|
||||
<OracleFullProfile provider={args['provider']} id="4578" />
|
||||
);
|
||||
|
||||
export const OraclePrimary = Template.bind({});
|
||||
|
||||
OraclePrimary.args = {
|
||||
provider: {
|
||||
name: 'Test oracle',
|
||||
url: 'https://zombo.com',
|
||||
description_markdown:
|
||||
'Some markdown describing the oracle provider.\n\nTwitter: @FacesPics2\n',
|
||||
oracle: {
|
||||
status: 'GOOD',
|
||||
status_reason: '',
|
||||
first_verified: '2023-02-28T00:00:00.000Z',
|
||||
last_verified: '2023-02-28T00:00:00.000Z',
|
||||
type: 'eth_address',
|
||||
eth_address: '0xfCEAdAFab14d46e20144F48824d0C09B1a03F2BC',
|
||||
},
|
||||
proofs: [
|
||||
{
|
||||
format: 'signed_message',
|
||||
available: true,
|
||||
type: 'eth_address',
|
||||
eth_address: '0x949AF81E51D57831AE52591d17fBcdd1014a5f52',
|
||||
message: 'SOMEHEX',
|
||||
},
|
||||
],
|
||||
github_link:
|
||||
'https://github.com/vegaprotocol/well-known/blob/main/oracle-providers/eth_address-0xfCEAdAFab14d46e20144F48824d0C09B1a03F2BC.toml',
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,132 @@
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import type { Provider } from '../../oracle-schema';
|
||||
import {
|
||||
ExternalLink,
|
||||
Icon,
|
||||
Intent,
|
||||
VegaIcon,
|
||||
VegaIconNames,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import type { IconName } from '@blueprintjs/icons';
|
||||
import classNames from 'classnames';
|
||||
import { getLinkIcon, getVerifiedStatusIcon } from '../oracle-basic-profile';
|
||||
import { useEnvironment } from '@vegaprotocol/environment';
|
||||
|
||||
export const OracleProfileTitle = ({ provider }: { provider: Provider }) => {
|
||||
const { icon, intent } = getVerifiedStatusIcon(provider);
|
||||
const verifiedProofs = provider.proofs.filter(
|
||||
(proof) => proof.available === true
|
||||
);
|
||||
return (
|
||||
<span className="flex gap-1">
|
||||
{provider.url && (
|
||||
<span>
|
||||
<span className="pr-1">{provider.name}</span>
|
||||
<span className="dark:text-vega-light-300 text-vega-dark-300">
|
||||
({verifiedProofs.length})
|
||||
</span>
|
||||
</span>
|
||||
)}
|
||||
<span
|
||||
className={classNames(
|
||||
{
|
||||
'text-gray-700 dark:text-gray-300': intent === Intent.None,
|
||||
'text-vega-blue': intent === Intent.Primary,
|
||||
'text-vega-green dark:text-vega-green': intent === Intent.Success,
|
||||
'text-yellow-600 dark:text-yellow': intent === Intent.Warning,
|
||||
'text-vega-pink': intent === Intent.Danger,
|
||||
},
|
||||
'flex items-start align-text-bottom p-1'
|
||||
)}
|
||||
>
|
||||
<Icon size={6} name={icon as IconName} />
|
||||
</span>
|
||||
</span>
|
||||
);
|
||||
};
|
||||
|
||||
export const OracleFullProfile = ({
|
||||
provider,
|
||||
id,
|
||||
}: {
|
||||
provider: Provider;
|
||||
id: string;
|
||||
}) => {
|
||||
const { message } = getVerifiedStatusIcon(provider);
|
||||
const { VEGA_EXPLORER_URL } = useEnvironment();
|
||||
|
||||
const signedProofs = provider.proofs.filter(
|
||||
(proof) => proof.format === 'signed_message' && proof.available === true
|
||||
);
|
||||
|
||||
const links = provider.proofs
|
||||
.filter((proof) => proof.format === 'url' && proof.available === true)
|
||||
.map((proof) => ({
|
||||
...proof,
|
||||
url: 'url' in proof ? proof.url : '',
|
||||
icon: getLinkIcon(proof.type),
|
||||
}));
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-4">
|
||||
<p className="dark:text-vega-light-300 text-vega-dark-300 pb-2">
|
||||
{message}
|
||||
</p>
|
||||
|
||||
<div className="grid grid-cols-2 gap-2">
|
||||
<div className="col-span-1">
|
||||
<p className="dark:text-vega-light-300 text-vega-dark-300 uppercase">
|
||||
{t('%s verified accounts', links.length.toString())}
|
||||
</p>
|
||||
{links.length > 0 ? (
|
||||
<div className="flex flex-col gap-1">
|
||||
{links.map((link) => (
|
||||
<ExternalLink
|
||||
key={link.url}
|
||||
href={link.url}
|
||||
className="flex align-items-bottom underline text-sm"
|
||||
>
|
||||
<span className="pt-1">
|
||||
<VegaIcon name={getLinkIcon(link.type)} />
|
||||
</span>
|
||||
<span className="underline capitalize">
|
||||
{link.type}{' '}
|
||||
<VegaIcon name={VegaIconNames.OPEN_EXTERNAL} size={13} />
|
||||
</span>
|
||||
</ExternalLink>
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
<p className="dark:text-vega-light-300 text-vega-dark-300">
|
||||
{t('This oracle has not proven ownership of any accounts.')}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
<div className="col-span-1 gap-1 py-2 flex flex-col">
|
||||
<p className="dark:text-vega-light-300 text-vega-dark-300 uppercase">
|
||||
{t('Details')}
|
||||
</p>
|
||||
{id && (
|
||||
<ExternalLink href={`${VEGA_EXPLORER_URL}/oracles/${id}`}>
|
||||
{t('Block explorer')}
|
||||
</ExternalLink>
|
||||
)}
|
||||
{provider.github_link && (
|
||||
<ExternalLink href={provider.github_link}>
|
||||
{t('Oracle repository')}
|
||||
</ExternalLink>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<p className="dark:text-vega-light-300 text-vega-dark-300 uppercase">
|
||||
{t('Oracle in %s %s', [
|
||||
signedProofs.length.toString(),
|
||||
signedProofs.length === 1 ? 'market' : 'markets',
|
||||
])}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -13,7 +13,7 @@ interface DialogProps {
|
||||
onChange?: (isOpen: boolean) => void;
|
||||
onCloseAutoFocus?: (e: Event) => void;
|
||||
onInteractOutside?: (e: Event) => void;
|
||||
title?: string;
|
||||
title?: string | ReactNode;
|
||||
icon?: ReactNode;
|
||||
intent?: Intent;
|
||||
size?: 'small' | 'medium';
|
||||
|
||||
Reference in New Issue
Block a user