feat(oracles): add oracle full profile

This commit is contained in:
Madalina Raicu
2023-04-26 01:13:28 +03:00
parent 74dab3c338
commit f6ad6aed4d
8 changed files with 223 additions and 27 deletions
@@ -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
View File
@@ -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';