@@ -133,7 +136,7 @@ export const OracleBasicProfile = ({ provider }: { provider: Provider }) => {
- {link.type}{' '}
+ {link.type}
diff --git a/libs/oracles/src/lib/components/oracle-full-profile/index.ts b/libs/oracles/src/lib/components/oracle-full-profile/index.ts
new file mode 100644
index 000000000..a16060bc4
--- /dev/null
+++ b/libs/oracles/src/lib/components/oracle-full-profile/index.ts
@@ -0,0 +1,2 @@
+export * from './oracle-full-profile.stories';
+export * from './oracle-full-profile';
diff --git a/libs/oracles/src/lib/components/oracle-full-profile/oracle-full-profile.spec.tsx b/libs/oracles/src/lib/components/oracle-full-profile/oracle-full-profile.spec.tsx
new file mode 100644
index 000000000..c495cfef8
--- /dev/null
+++ b/libs/oracles/src/lib/components/oracle-full-profile/oracle-full-profile.spec.tsx
@@ -0,0 +1,54 @@
+import { OracleFullProfile } from './oracle-full-profile';
+import type { Provider } from '../../oracle-schema';
+import { render, screen } from '@testing-library/react';
+
+describe('OracleFullProfile', () => {
+ const testProvider = {
+ 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',
+ } as Provider;
+
+ it('should render successfully', () => {
+ const component = (
+
+ );
+ expect(component).toBeTruthy();
+ });
+
+ it('should render the name', () => {
+ render(
+
+ );
+ expect(screen.getByTestId('github-link')).toHaveTextContent(
+ 'Oracle repository'
+ );
+ expect(screen.getByTestId('block-explorer-link')).toHaveTextContent(
+ 'Block explorer'
+ );
+ });
+});
diff --git a/libs/oracles/src/lib/components/oracle-full-profile/oracle-full-profile.stories.tsx b/libs/oracles/src/lib/components/oracle-full-profile/oracle-full-profile.stories.tsx
new file mode 100644
index 000000000..18ec32975
--- /dev/null
+++ b/libs/oracles/src/lib/components/oracle-full-profile/oracle-full-profile.stories.tsx
@@ -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) => (
+
+);
+
+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',
+ },
+};
diff --git a/libs/oracles/src/lib/components/oracle-full-profile/oracle-full-profile.tsx b/libs/oracles/src/lib/components/oracle-full-profile/oracle-full-profile.tsx
new file mode 100644
index 000000000..494a522fb
--- /dev/null
+++ b/libs/oracles/src/lib/components/oracle-full-profile/oracle-full-profile.tsx
@@ -0,0 +1,216 @@
+import { t } from '@vegaprotocol/i18n';
+import type { Provider } from '../../oracle-schema';
+import { MarketState, MarketStateMapping } from '@vegaprotocol/types';
+import {
+ ButtonLink,
+ 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';
+import type { OracleMarketSpecFieldsFragment } from '../../__generated__/OracleMarketsSpec';
+import { useState } from 'react';
+
+export const OracleProfileTitle = ({ provider }: { provider: Provider }) => {
+ const { icon, intent } = getVerifiedStatusIcon(provider);
+ const verifiedProofs = provider.proofs.filter(
+ (proof) => proof.available === true
+ );
+ return (
+
+ {provider.url && (
+
+ {provider.name}
+
+ ({verifiedProofs.length})
+
+
+ )}
+
+
+
+
+ );
+};
+
+export const OracleFullProfile = ({
+ provider,
+ id,
+ markets: oracleMarkets,
+}: {
+ provider: Provider;
+ id: string;
+ markets?: OracleMarketSpecFieldsFragment[] | undefined;
+}) => {
+ const { message } = getVerifiedStatusIcon(provider);
+ const { VEGA_EXPLORER_URL } = useEnvironment();
+ const [showMore, setShowMore] = useState(false);
+
+ 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 (
+
+
+
{message}
+ {!showMore && (
+
+ {provider.description_markdown.slice(0, 100)}
+ {'... '}
+
+ setShowMore(!showMore)}>
+ Read more
+
+
+
+ )}
+ {showMore && (
+
+ {provider.description_markdown}
+
+ setShowMore(!showMore)}>
+ Show less
+
+
+
+ )}
+
+
+
+
+ {t('%s proofs of ownership', links.length.toString())}
+
+ {links.length > 0 ? (
+
+ {links.map((link) => (
+
+
+
+
+
+ {link.type}{' '}
+
+
+
+ ))}
+
+ ) : (
+
+ {t('This oracle has not proven ownership of any accounts.')}
+
+ )}
+
+
+
+ {t('Details')}
+
+ {id && (
+
+ {t('Block explorer')}
+
+ )}
+ {provider.github_link && (
+
+ {t('Oracle repository')}
+
+ )}
+
+
+
+ {oracleMarkets && (
+
+ {t('Oracle in %s %s', [
+ oracleMarkets.length.toString(),
+ oracleMarkets.length === 1 ? 'market' : 'markets',
+ ])}
+
+ )}
+
+ {oracleMarkets && oracleMarkets.length > 0 && (
+
+
+
{t('Market')}
+
{t('Status')}
+
{t('Specifications')}
+
+
+ {oracleMarkets?.map((market) => (
+
+
+ {market.tradableInstrument.instrument.code}
+
+
+ {MarketStateMapping[market.state]}
+
+
+ {
+
+ {t('Settlement')}
+
+ }
+
+
+ {
+
+ {t('Termination')}
+
+ }
+
+
+ ))}
+
+
+ )}
+
+ );
+};
diff --git a/libs/oracles/src/lib/hooks/index.ts b/libs/oracles/src/lib/hooks/index.ts
new file mode 100644
index 000000000..285081890
--- /dev/null
+++ b/libs/oracles/src/lib/hooks/index.ts
@@ -0,0 +1,3 @@
+export * from './use-oracle-markets';
+export * from './use-oracle-proofs';
+export * from './use-oracle-spec-binding-data';
diff --git a/libs/oracles/src/lib/hooks/use-oracle-markets.ts b/libs/oracles/src/lib/hooks/use-oracle-markets.ts
new file mode 100644
index 000000000..da5b55a05
--- /dev/null
+++ b/libs/oracles/src/lib/hooks/use-oracle-markets.ts
@@ -0,0 +1,50 @@
+import type { Provider } from '../oracle-schema';
+import type { OracleMarketSpecFieldsFragment } from '../__generated__/OracleMarketsSpec';
+import { useOracleMarketsSpecQuery } from '../__generated__/OracleMarketsSpec';
+
+export const useOracleMarkets = (
+ provider: Provider
+): OracleMarketSpecFieldsFragment[] | undefined => {
+ const signedProofs = provider.proofs.filter(
+ (proof) => proof.format === 'signed_message' && proof.available === true
+ );
+
+ const { data: markets } = useOracleMarketsSpecQuery();
+
+ const oracleMarkets = markets?.marketsConnection?.edges
+ ?.map((edge) => edge.node)
+ ?.filter((node) => {
+ const p = node.tradableInstrument.instrument.product;
+ const sourceType = p.dataSourceSpecForSettlementData.data.sourceType;
+ if (sourceType.__typename !== 'DataSourceDefinitionExternal') {
+ return false;
+ }
+ const signers = sourceType?.sourceType.signers;
+
+ const signerKeys = signers?.filter(Boolean).map((signer) => {
+ if (signer.signer.__typename === 'ETHAddress') {
+ return signer.signer.address;
+ }
+
+ if (signer.signer.__typename === 'PubKey') {
+ return signer.signer.key;
+ }
+
+ return undefined;
+ });
+
+ const signedProofsKeys = signedProofs.map((proof) => {
+ if ('public_key' in proof && proof.public_key) {
+ return proof.public_key;
+ }
+ if ('eth_address' in proof && proof.eth_address) {
+ return proof.eth_address;
+ }
+ return undefined;
+ });
+
+ const key = signedProofsKeys.find((key) => signerKeys?.includes(key));
+ return !!key;
+ });
+ return oracleMarkets;
+};
diff --git a/libs/oracles/src/lib/use-oracle-proofs.spec.ts b/libs/oracles/src/lib/hooks/use-oracle-proofs.spec.ts
similarity index 98%
rename from libs/oracles/src/lib/use-oracle-proofs.spec.ts
rename to libs/oracles/src/lib/hooks/use-oracle-proofs.spec.ts
index d377fdb15..750f398d0 100644
--- a/libs/oracles/src/lib/use-oracle-proofs.spec.ts
+++ b/libs/oracles/src/lib/hooks/use-oracle-proofs.spec.ts
@@ -1,5 +1,5 @@
import { renderHook, waitFor } from '@testing-library/react';
-import type { Provider } from './oracle-schema';
+import type { Provider } from '../oracle-schema';
import { useOracleProofs, cache, invalidateCache } from './use-oracle-proofs';
global.fetch = jest.fn();
diff --git a/libs/oracles/src/lib/use-oracle-proofs.ts b/libs/oracles/src/lib/hooks/use-oracle-proofs.ts
similarity index 92%
rename from libs/oracles/src/lib/use-oracle-proofs.ts
rename to libs/oracles/src/lib/hooks/use-oracle-proofs.ts
index 7953743c2..3669dd260 100644
--- a/libs/oracles/src/lib/use-oracle-proofs.ts
+++ b/libs/oracles/src/lib/hooks/use-oracle-proofs.ts
@@ -1,6 +1,6 @@
import { useEffect, useState } from 'react';
-import type { Provider } from './oracle-schema';
-import { providersSchema } from './oracle-schema';
+import type { Provider } from '../oracle-schema';
+import { providersSchema } from '../oracle-schema';
export let cache: {
[url: string]: Provider[];
diff --git a/libs/oracles/src/lib/use-oracle-spec-binding-data.spec.tsx b/libs/oracles/src/lib/hooks/use-oracle-spec-binding-data.spec.tsx
similarity index 94%
rename from libs/oracles/src/lib/use-oracle-spec-binding-data.spec.tsx
rename to libs/oracles/src/lib/hooks/use-oracle-spec-binding-data.spec.tsx
index 57c19d63e..23292b63f 100644
--- a/libs/oracles/src/lib/use-oracle-spec-binding-data.spec.tsx
+++ b/libs/oracles/src/lib/hooks/use-oracle-spec-binding-data.spec.tsx
@@ -4,8 +4,8 @@ import { MockedProvider } from '@apollo/client/testing';
import type { ReactNode } from 'react';
import { useOracleSpecBindingData } from './use-oracle-spec-binding-data';
import type { Property } from '@vegaprotocol/types';
-import type { OracleSpecDataConnectionQuery } from './__generated__/OracleSpecDataConnection';
-import { OracleSpecDataConnectionDocument } from './__generated__/OracleSpecDataConnection';
+import type { OracleSpecDataConnectionQuery } from '../__generated__/OracleSpecDataConnection';
+import { OracleSpecDataConnectionDocument } from '../__generated__/OracleSpecDataConnection';
describe('useSettlementPrice', () => {
const setup = (
diff --git a/libs/oracles/src/lib/use-oracle-spec-binding-data.ts b/libs/oracles/src/lib/hooks/use-oracle-spec-binding-data.ts
similarity index 86%
rename from libs/oracles/src/lib/use-oracle-spec-binding-data.ts
rename to libs/oracles/src/lib/hooks/use-oracle-spec-binding-data.ts
index fa9afac50..b5ff55cfa 100644
--- a/libs/oracles/src/lib/use-oracle-spec-binding-data.ts
+++ b/libs/oracles/src/lib/hooks/use-oracle-spec-binding-data.ts
@@ -1,4 +1,4 @@
-import { useOracleSpecDataConnectionQuery } from './__generated__/OracleSpecDataConnection';
+import { useOracleSpecDataConnectionQuery } from '../__generated__/OracleSpecDataConnection';
export const useOracleSpecBindingData = (
oracleSpecId: string | undefined,
diff --git a/libs/oracles/src/lib/index.ts b/libs/oracles/src/lib/index.ts
index e45917945..28605920d 100644
--- a/libs/oracles/src/lib/index.ts
+++ b/libs/oracles/src/lib/index.ts
@@ -2,5 +2,4 @@ export * from './__generated__';
export * from './components';
export * from './oracle-schema';
export * from './oracle-spec-data-connection.mock';
-export * from './use-oracle-proofs';
-export * from './use-oracle-spec-binding-data';
+export * from './hooks';
diff --git a/libs/oracles/tsconfig.lib.json b/libs/oracles/tsconfig.lib.json
index 6a440c7bd..5ad10446b 100644
--- a/libs/oracles/tsconfig.lib.json
+++ b/libs/oracles/tsconfig.lib.json
@@ -6,7 +6,7 @@
},
"files": [
"../../node_modules/@nrwl/react/typings/cssmodule.d.ts",
- "../../node_modules/@nrwl/react/typings/image.d.ts"
+ "../../node_modules/@nrwl/next/typings/image.d.ts"
],
"exclude": [
"**/*.spec.ts",
diff --git a/libs/ui-toolkit/src/components/dialog/dialog.tsx b/libs/ui-toolkit/src/components/dialog/dialog.tsx
index 21918b352..6e719beb9 100644
--- a/libs/ui-toolkit/src/components/dialog/dialog.tsx
+++ b/libs/ui-toolkit/src/components/dialog/dialog.tsx
@@ -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';
diff --git a/libs/ui-toolkit/src/components/link/link.spec.tsx b/libs/ui-toolkit/src/components/link/link.spec.tsx
index 389105dbf..1988a655e 100644
--- a/libs/ui-toolkit/src/components/link/link.spec.tsx
+++ b/libs/ui-toolkit/src/components/link/link.spec.tsx
@@ -40,7 +40,7 @@ describe('ExternalLink', () => {
render(
Go to Vega);
const link = screen.getByTestId('external-link');
expect(link.children.length).toEqual(2);
- expect(link.children[1].tagName.toUpperCase()).toEqual('SVG');
+ expect(link.children[1].tagName.toUpperCase()).toEqual('SPAN');
});
it('should have an underlined text part', () => {
diff --git a/libs/ui-toolkit/src/components/link/link.tsx b/libs/ui-toolkit/src/components/link/link.tsx
index 6329ece62..a557194d9 100644
--- a/libs/ui-toolkit/src/components/link/link.tsx
+++ b/libs/ui-toolkit/src/components/link/link.tsx
@@ -1,7 +1,6 @@
-import { IconNames } from '@blueprintjs/icons';
import classNames from 'classnames';
import type { ReactNode, AnchorHTMLAttributes } from 'react';
-import { Icon } from '../icon';
+import { VegaIcon, VegaIconNames } from '../icon';
type LinkProps = AnchorHTMLAttributes
& {
children?: ReactNode;
@@ -24,7 +23,7 @@ export const Link = ({ className, children, ...props }: LinkProps) => {
};
// if no href is passed just render a span, this is so that we can wrap an
- // element with our links styles with a react router link compoment
+ // element with our links styles with a react router link component
if (!props.href) {
return (
@@ -43,7 +42,10 @@ Link.displayName = 'Link';
export const ExternalLink = ({ children, className, ...props }: LinkProps) => (
(
>
{children}
-
+
>
) : (
children