feat(markets): use i18next (#5255)

This commit is contained in:
Bartłomiej Głownia 2023-11-17 21:01:18 +01:00 committed by GitHub
parent 6e8894936e
commit b6052bc3e5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 520 additions and 295 deletions

View File

@ -1,6 +1,6 @@
import {
getAsset,
tooltipMapping,
useTooltipMapping,
useMarket,
useStaticMarketData,
} from '@vegaprotocol/markets';
@ -27,6 +27,7 @@ import { useT } from '../../lib/use-t';
export const LiquidityHeader = () => {
const t = useT();
const tooltipMapping = useTooltipMapping();
const { marketId } = useParams();
const { data: market } = useMarket(marketId);
const { data: marketData } = useStaticMarketData(marketId);
@ -122,7 +123,7 @@ export const LiquidityHeader = () => {
<CopyWithTooltip text={marketId}>
<button
data-testid="copy-eth-oracle-address"
className="uppercase text-right"
className="text-right uppercase"
>
<span className="flex gap-1">
{truncateMiddle(marketId)}

View File

@ -11,6 +11,7 @@ import en_funding_payments from './locales/en/funding-payments.json';
import en_governance from './locales/en/governance.json';
import en_trading from './locales/en/trading.json';
import en_markets from './locales/en/markets.json';
export const locales = {
en: {
accounts: en_accounts,
@ -24,5 +25,6 @@ export const locales = {
'funding-payments': en_funding_payments,
governance: en_governance,
trading: en_trading,
markets: en_markets,
},
};

View File

@ -0,0 +1,143 @@
{
"{{liquidityPriceRange}} of mid price": "{{liquidityPriceRange}} of mid price",
"{{probability}} probability price bounds": "{{probability}} probability price bounds",
"24 hour change is unavailable at this time. The price change in the last 120 hours is:": "24 hour change is unavailable at this time. The price change in the last 120 hours is:",
"24 hour change is unavailable at this time. The volume change in the last 120 hours is {{candleVolumeValue}}": "24 hour change is unavailable at this time. The volume change in the last 120 hours is {{candleVolumeValue}}",
"A concept derived from traditional markets. It is a calculated value for the current market price on a market.": "A concept derived from traditional markets. It is a calculated value for the current market price on a market.",
"A number that will be calculated by an appropriate stochastic risk model, dependent on the type of risk model used and its parameters.": "A number that will be calculated by an appropriate stochastic risk model, dependent on the type of risk model used and its parameters.",
"A sliding penalty for how much an LP bond is slashed if an LP fails to reach the minimum SLA. This is a network parameter.": "A sliding penalty for how much an LP bond is slashed if an LP fails to reach the minimum SLA. This is a network parameter.",
"ABI specification": "ABI specification",
"Added": "Added",
"Address": "Address",
"All fees are paid by price takers and are a % of the trade notional value. Fees are not paid during auction uncrossing.": "All fees are paid by price takers and are a % of the trade notional value. Fees are not paid during auction uncrossing.",
"Auction extension duration in seconds, should the price breach its theoretical level over the specified horizon at the specified probability level.": "Auction extension duration in seconds, should the price breach its theoretical level over the specified horizon at the specified probability level.",
"Block explorer": "Block explorer",
"Conditions": "Conditions",
"Could not load market": "Could not load market",
"Current fees": "Current fees",
"Data about the sector. Example: 'automotive' for a market based on value of Tesla shares.": "Data about the sector. Example: 'automotive' for a market based on value of Tesla shares.",
"Details": "Details",
"Determines how the probability of trading is scaled from the risk model, and is used to measure the relative competitiveness of an LP's supplied volume. This is a network parameter.": "Determines how the probability of trading is scaled from the risk model, and is used to measure the relative competitiveness of an LP's supplied volume. This is a network parameter.",
"Ethereum Oracle": "Ethereum Oracle",
"every {{duration}}": "every {{duration}}",
"every {{duration}} from {{initialTime}}": "every {{duration}} from {{initialTime}}",
"Fees paid to validators as a reward for running the infrastructure of the network.": "Fees paid to validators as a reward for running the infrastructure of the network.",
"Filters": "Filters",
"For liquidity orders to count towards a commitment, they must be within the liquidity monitoring bounds.": "For liquidity orders to count towards a commitment, they must be within the liquidity monitoring bounds.",
"Funding": "Funding",
"How big the smallest order / position on the market can be.": "How big the smallest order / position on the market can be.",
"How long an epoch is. LP rewards from liquidity fees are paid out once per epoch. How much they receive depends on whether they met the liquidity SLA and their previous performance in recent epochs. This is a network parameter.": "How long an epoch is. LP rewards from liquidity fees are paid out once per epoch. How much they receive depends on whether they met the liquidity SLA and their previous performance in recent epochs. This is a network parameter.",
"How often the quality of liquidity supplied by each liquidity provider is evaluated and the fees arising from that period are earmarked for specific providers. This is a market parameter. ": "How often the quality of liquidity supplied by each liquidity provider is evaluated and the fees arising from that period are earmarked for specific providers. This is a market parameter. ",
"Instrument": "Instrument",
"Insurance pool": "Insurance pool",
"Internal conditions": "Internal conditions",
"Invalid data source": "Invalid data source",
"involvedInMarkets_other": "Involved in {{count}} markets",
"involvedInMarkets_one": "Involved in {{count}} market",
"Key": "Key",
"Key details": "Key details",
"Liquidity": "Liquidity",
"Liquidity monitoring parameters": "Liquidity monitoring parameters",
"Liquidity portion of the fee is paid to liquidity providers, and is transferred to the liquidity fee pool for the market.": "Liquidity portion of the fee is paid to liquidity providers, and is transferred to the liquidity fee pool for the market.",
"Liquidity price range": "Liquidity price range",
"Liquidity SLA protocol": "Liquidity SLA protocol",
"Maker portion of the fee is transferred to the non-aggressive, or passive party in the trade (the maker, as opposed to the taker).": "Maker portion of the fee is transferred to the non-aggressive, or passive party in the trade (the maker, as opposed to the taker).",
"Margin scaling factors": "Margin scaling factors",
"Market": "Market",
"Market data": "Market data",
"Market governance": "Market governance",
"Market ID": "Market ID",
"Market price": "Market price",
"Market specification": "Market specification",
"Market volume": "Market volume",
"Maximum fraction of an LP's accrued fees that an LP would lose to liquidity providers that achieved a higher SLA performance than them. This is a market parameter.": "Maximum fraction of an LP's accrued fees that an LP would lose to liquidity providers that achieved a higher SLA performance than them. This is a market parameter.",
"Maximum value that a proposed fee amount can be, which is submitted as part of the LP commitment transaction. Note that a value of 0.05 = 5%. This is a network parameter.": "Maximum value that a proposed fee amount can be, which is submitted as part of the LP commitment transaction. Note that a value of 0.05 = 5%. This is a network parameter.",
"Metadata": "Metadata",
"moreProofs_one": "And {{count}} more proof",
"moreProofs_other": "And {{count}} more proofs",
"Multiplier used to translate an LP's commitment amount to their liquidity obligation. This is a network parameter.": "Multiplier used to translate an LP's commitment amount to their liquidity obligation. This is a network parameter.",
"No data": "No data",
"No oracle proof for settlement data": "No oracle proof for settlement data",
"No oracle proof for termination": "No oracle proof for termination",
"No oracle spec for trading termination. Internal timestamp used": "No oracle spec for trading termination. Internal timestamp used",
"Normalisers": "Normalisers",
"Not verified": "Not verified",
"Number of epochs over which past performance will continue to affect rewards. This is a market parameter.": "Number of epochs over which past performance will continue to affect rewards. This is a market parameter.",
"Oracle": "Oracle",
"Oracle repository": "Oracle repository",
"Oracle status for this market is <0>{{status}}</0>. {{description}} <1>Show more</1>": "Oracle status for this market is <0>{{status}}</0>. {{description}} <1>Show more</1>",
"Oracle status: {{status}}. {{description}}": "Oracle status: {{status}}. {{description}}",
"oracleInMarkets_one": "Oracle in {{count}} market",
"oracleInMarkets_other": "Oracle in {{count}} markets",
"Price monitoring bounds {{index}}": "Price monitoring bounds {{index}}",
"Probability level for price projection, e.g. value of 0.95 will result in a price range such that over the specified projection horizon, the prices observed in the market should be in that range 95% of the time.": "Probability level for price projection, e.g. value of 0.95 will result in a price range such that over the specified projection horizon, the prices observed in the market should be in that range 95% of the time.",
"Probability level used in <0>Expected Shortfall</0> calculation when obtaining Risk Factor Long and Risk Factor Short": "Probability level used in <0>Expected Shortfall</0> calculation when obtaining Risk Factor Long and Risk Factor Short",
"Projection horizon measured as a year fraction used in <0>Expected Shortfall</0> calculation when obtaining Risk Factor Long and Risk Factor Short": "Projection horizon measured as a year fraction used in <0>Expected Shortfall</0> calculation when obtaining Risk Factor Long and Risk Factor Short",
"proofsOfOwnership_one": "{{count}} proof of ownership",
"proofsOfOwnership_other": "{{count}} proofs of ownership",
"Proposal": "Proposal",
"Propose a change to market": "Propose a change to market",
"Read more": "Read more",
"Results in {{auctionExtensionSecs}} seconds auction if breached": "Results in {{auctionExtensionSecs}} seconds auction if breached",
"Risk factors": "Risk factors",
"Risk model": "Risk model",
"Settlement": "Settlement",
"Settlement asset": "Settlement asset",
"Settlement oracle": "Settlement oracle",
"Settlement schedule oracle": "Settlement schedule oracle",
"Show less": "Show less",
"SLA protocol = a part of the Vega protocol that creates similar incentives within the decentralised system to those achieved by a Service Level Agreement between parties in traditional finance. The SLA protocol involves no discussion, agreement, or contracts between parties but instead relies upon rules and an economic mechanism implemented in code running on the network": "SLA protocol = a part of the Vega protocol that creates similar incentives within the decentralised system to those achieved by a Service Level Agreement between parties in traditional finance. The SLA protocol involves no discussion, agreement, or contracts between parties but instead relies upon rules and an economic mechanism implemented in code running on the network",
"Specifications": "Specifications",
"Specifies the minimum fraction of time LPs must spend 'on the book' providing their committed liquidity. This is a market parameter.": "Specifies the minimum fraction of time LPs must spend 'on the book' providing their committed liquidity. This is a market parameter.",
"Status": "Status",
"Succession line": "Succession line",
"Termination": "Termination",
"Termination oracle": "Termination oracle",
"The aggregated volume being bid at the best bid price on the market.": "The aggregated volume being bid at the best bid price on the market.",
"The aggregated volume being bid at the best static bid price on the market.": "The aggregated volume being bid at the best static bid price on the market.",
"The aggregated volume being offered at the best offer price on the market.": "The aggregated volume being offered at the best offer price on the market.",
"The aggregated volume being offered at the best static offer price on the market.": "The aggregated volume being offered at the best static offer price on the market.",
"The classification of the product. Examples: shares, commodities, crypto, FX.": "The classification of the product. Examples: shares, commodities, crypto, FX.",
"The current amount of liquidity supplied for this market.": "The current amount of liquidity supplied for this market.",
"The current state of the market": "The current state of the market",
"The first currency in a pair for a currency-based derivatives market.": "The first currency in a pair for a currency-based derivatives market.",
"The fraction of the insurance pool balance that is carried over from the parent market to the successor.": "The fraction of the insurance pool balance that is carried over from the parent market to the successor.",
"The ID of the market this market succeeds.": "The ID of the market this market succeeds.",
"The length of time over which open interest is measured.": "The length of time over which open interest is measured.",
"The liquidity price range is a {{{liquidityPriceRange}} difference from the mid price.": "The liquidity price range is a {{{liquidityPriceRange}} difference from the mid price.",
"The lower bound for the probability of trading calculation, used to measure liquidity available on a market to determine if LPs are meeting their commitment. This is a network parameter.": "The lower bound for the probability of trading calculation, used to measure liquidity available on a market to determine if LPs are meeting their commitment. This is a network parameter.",
"The market's liquidity requirement which is derived from the maximum open interest observed over a rolling time window.": "The market's liquidity requirement which is derived from the maximum open interest observed over a rolling time window.",
"The maximum amount, as a fraction, that an LP's bond can be slashed by if they fail to reach the minimum SLA. This is a network parameter.": "The maximum amount, as a fraction, that an LP's bond can be slashed by if they fail to reach the minimum SLA. This is a network parameter.",
"The percentage of their bond an LP forfeits if they reduce their commitment while the market is below target stake. If 100%, an LP's entire bond is forfeited when they cancel their full commitment. This is a network parameter.": "The percentage of their bond an LP forfeits if they reduce their commitment while the market is below target stake. If 100%, an LP's entire bond is forfeited when they cancel their full commitment. This is a network parameter.",
"The scaling between the liquidity demand estimate, based on open interest and target stake.": "The scaling between the liquidity demand estimate, based on open interest and target stake.",
"The second currency in a pair for a currency-based derivatives market.": "The second currency in a pair for a currency-based derivatives market.",
"The smallest price increment on the book.": "The smallest price increment on the book.",
"The total number of contracts traded in the last 24 hours.": "The total number of contracts traded in the last 24 hours.",
"The trading mode the market is currently running.": "The trading mode the market is currently running.",
"The triggering ratio for entering liquidity auction.": "The triggering ratio for entering liquidity auction.",
"The underlying that is being priced by the market, described by the market's oracle.": "The underlying that is being priced by the market, described by the market's oracle.",
"The volume at which all trades would occur if the auction was uncrossed now (when in auction mode).": "The volume at which all trades would occur if the auction was uncrossed now (when in auction mode).",
"The volume of all open positions in a given market (the sum of the size of all positions greater than 0).": "The volume of all open positions in a given market (the sum of the size of all positions greater than 0).",
"There is 1 unit of the settlement asset ({{assetSymbol}}) to every 1 quote unit ({{quoteUnit}}).": "There is 1 unit of the settlement asset ({{assetSymbol}}) to every 1 quote unit ({{quoteUnit}}).",
"This market": "This market",
"This oracle has not proven ownership of any accounts.": "This oracle has not proven ownership of any accounts.",
"This public key has been observed acting in bad faith.": "This public key has been observed acting in bad faith.",
"This public key is no longer in the control of its original owners.": "This public key is no longer in the control of its original owners.",
"This public key is no longer in use.": "This public key is no longer in use.",
"This public key is suspected to be acting in bad faith, pending investigation.": "This public key is suspected to be acting in bad faith, pending investigation.",
"This public key's proofs have been verified.": "This public key's proofs have been verified.",
"This public key's proofs have not been verified yet, or no proofs have been provided yet.": "This public key's proofs have not been verified yet, or no proofs have been provided yet.",
"Time horizon of the price projection in seconds.": "Time horizon of the price projection in seconds.",
"Updated": "Updated",
"Used to calculate the penalty to liquidity providers when they cannot support their open position with the assets in their margin and general accounts. This is a network parameter.": "Used to calculate the penalty to liquidity providers when they cannot support their open position with the assets in their margin and general accounts. This is a network parameter.",
"Verified since {{lastVerified}}": "Verified since {{lastVerified}}",
"verifyProofs_one": "Verify {{count}} proof of ownership",
"verifyProofs_other": "Verify {{count}} proofs of ownership",
"View governance proposal": "View governance proposal",
"View liquidity provision table": "View liquidity provision table",
"View on Etherscan": "View on Etherscan",
"View settlement data specification": "View settlement data specification",
"View settlement schedule specification": "View settlement schedule specification",
"View termination specification": "View termination specification",
"Within %s seconds": "Within %s seconds"
}

View File

@ -7,10 +7,10 @@ import {
} from '@vegaprotocol/utils';
import { PriceChangeCell, signedNumberCssClass } from '@vegaprotocol/datagrid';
import { Tooltip, VegaIcon, VegaIconNames } from '@vegaprotocol/ui-toolkit';
import { t } from '@vegaprotocol/i18n';
import { useCandles } from '../../hooks/use-candles';
import BigNumber from 'bignumber.js';
import classNames from 'classnames';
import { useT } from '../../use-t';
interface Props {
marketId?: string;
@ -25,6 +25,7 @@ export const Last24hPriceChange = ({
decimalPlaces,
initialValue,
}: Props) => {
const t = useT();
const { oneDayCandles, error, fiveDaysCandles } = useCandles({
marketId,
});

View File

@ -1,8 +1,8 @@
import { calcCandleVolume } from '../../market-utils';
import { addDecimalsFormatNumber, isNumeric } from '@vegaprotocol/utils';
import { t } from '@vegaprotocol/i18n';
import { Tooltip } from '@vegaprotocol/ui-toolkit';
import { useCandles } from '../../hooks';
import { useT } from '../../use-t';
interface Props {
marketId?: string;
@ -17,6 +17,7 @@ export const Last24hVolume = ({
formatDecimals,
initialValue,
}: Props) => {
const t = useT();
const { oneDayCandles, fiveDaysCandles } = useCandles({
marketId,
});
@ -41,8 +42,8 @@ export const Last24hVolume = ({
<div>
<span className="flex flex-col">
{t(
'24 hour change is unavailable at this time. The volume change in the last 120 hours is %s',
[candleVolumeValue]
'24 hour change is unavailable at this time. The volume change in the last 120 hours is {{candleVolumeValue}}',
{ candleVolumeValue }
)}
</span>
</div>

View File

@ -2,7 +2,6 @@ import {
addDecimalsFormatNumber,
formatNumberPercentage,
} from '@vegaprotocol/utils';
import { t } from '@vegaprotocol/i18n';
import {
Intent,
KeyValueTable,
@ -13,9 +12,10 @@ import {
import BigNumber from 'bignumber.js';
import startCase from 'lodash/startCase';
import { tooltipMapping } from './tooltip-mapping';
import { useTooltipMapping } from './tooltip-mapping';
import type { ReactNode } from 'react';
import { useT } from '../../use-t';
interface RowProps {
field: string;
value: ReactNode;
@ -39,6 +39,8 @@ export const Row = ({
parentValue,
hasParentData,
}: RowProps) => {
const t = useT();
const tooltipMapping = useTooltipMapping();
// Note: we need both 'parentValue' and 'hasParentData' to do a conditional
// check to differentiate between when parentData itself is missing and when
// a specific parentValue is missing. These values are only used when we

View File

@ -4,7 +4,6 @@ import {
useEnvironment,
} from '@vegaprotocol/environment';
import { removePaginationWrapper } from '@vegaprotocol/utils';
import { t } from '@vegaprotocol/i18n';
import { useDataProvider } from '@vegaprotocol/data-provider';
import * as Schema from '@vegaprotocol/types';
import {
@ -52,6 +51,7 @@ import {
isFuture,
getSigners,
} from '../../product';
import { useT } from '../../use-t';
export interface MarketInfoAccordionProps {
market: MarketInfo;
@ -66,6 +66,7 @@ export const MarketInfoAccordionContainer = ({
marketId,
onSelect,
}: MarketInfoContainerProps) => {
const t = useT();
const { data, loading, error, reload } = useDataProvider({
dataProvider: marketInfoProvider,
skipUpdates: true,
@ -89,6 +90,7 @@ export const MarketInfoAccordion = ({
market,
onSelect,
}: MarketInfoAccordionProps) => {
const t = useT();
const { VEGA_TOKEN_URL } = useEnvironment();
const headerClassName = 'uppercase text-lg';
@ -252,7 +254,9 @@ export const MarketInfoAccordion = ({
<AccordionItem
key={id}
itemId={id}
title={t(`Price monitoring bounds ${triggerIndex + 1}`)}
title={t('Price monitoring bounds {{index}}', {
index: triggerIndex + 1,
})}
content={
<PriceMonitoringBoundsInfoPanel
market={market}

View File

@ -2,7 +2,6 @@ import isEqual from 'lodash/isEqual';
import type { ReactNode } from 'react';
import { Fragment, useMemo, useState } from 'react';
import { AssetDetailsTable, useAssetDataProvider } from '@vegaprotocol/assets';
import { t } from '@vegaprotocol/i18n';
import { marketDataProvider } from '../../market-data-provider';
import { totalFeesFactorsPercentage } from '../../market-utils';
import {
@ -77,6 +76,7 @@ import {
import type { DataSourceFragment } from './__generated__/MarketInfo';
import { formatDuration } from 'date-fns';
import * as AccordionPrimitive from '@radix-ui/react-accordion';
import { useT } from '../../use-t';
type MarketInfoProps = {
market: MarketInfo;
@ -84,26 +84,30 @@ type MarketInfoProps = {
children?: ReactNode;
};
export const CurrentFeesInfoPanel = ({ market }: MarketInfoProps) => (
<>
<MarketInfoTable
data={{
makerFee: market.fees.factors.makerFee,
infrastructureFee: market.fees.factors.infrastructureFee,
liquidityFee: market.fees.factors.liquidityFee,
totalFees: totalFeesFactorsPercentage(market.fees.factors),
}}
asPercentage={true}
/>
<p className="text-xs">
{t(
'All fees are paid by price takers and are a % of the trade notional value. Fees are not paid during auction uncrossing.'
)}
</p>
</>
);
export const CurrentFeesInfoPanel = ({ market }: MarketInfoProps) => {
const t = useT();
return (
<>
<MarketInfoTable
data={{
makerFee: market.fees.factors.makerFee,
infrastructureFee: market.fees.factors.infrastructureFee,
liquidityFee: market.fees.factors.liquidityFee,
totalFees: totalFeesFactorsPercentage(market.fees.factors),
}}
asPercentage={true}
/>
<p className="text-xs">
{t(
'All fees are paid by price takers and are a % of the trade notional value. Fees are not paid during auction uncrossing.'
)}
</p>
</>
);
};
export const MarketPriceInfoPanel = ({ market }: MarketInfoProps) => {
const t = useT();
const assetSymbol = getAsset(market).symbol;
const quoteUnit = getQuoteName(market);
const { data } = useDataProvider({
@ -123,8 +127,8 @@ export const MarketPriceInfoPanel = ({ market }: MarketInfoProps) => {
/>
<p className="mt-2 text-xs">
{t(
'There is 1 unit of the settlement asset (%s) to every 1 quote unit (%s).',
[assetSymbol, quoteUnit]
'There is 1 unit of the settlement asset ({{assetSymbol}}) to every 1 quote unit ({{quoteUnit}}).',
{ assetSymbol, quoteUnit }
)}
</p>
</>
@ -185,6 +189,7 @@ export const KeyDetailsInfoPanel = ({
market,
parentMarket,
}: MarketInfoProps) => {
const t = useT();
const { data: parentMarketIdData } = useParentMarketIdQuery({
variables: {
marketId: market.id,
@ -228,7 +233,7 @@ export const KeyDetailsInfoPanel = ({
<CopyWithTooltip text={market.id}>
<button
data-testid="copy-eth-oracle-address"
className="uppercase text-right"
className="text-right uppercase"
>
<span className="flex gap-1">
{truncateMiddle(market.id)}
@ -303,6 +308,7 @@ const SuccessionLineItem = ({
marketId: string;
isCurrent?: boolean;
}) => {
const t = useT();
const { data } = useSuccessorMarketQuery({
variables: {
marketId,
@ -319,7 +325,7 @@ const SuccessionLineItem = ({
<div
data-testid="succession-line-item"
className={classNames(
'rounded p-2 bg-vega-clight-700 dark:bg-vega-cdark-700',
'bg-vega-clight-700 dark:bg-vega-cdark-700 rounded p-2',
'font-alpha',
'flex flex-col '
)}
@ -335,7 +341,7 @@ const SuccessionLineItem = ({
marketData.tradableInstrument.instrument.code
)
) : (
<span className="block w-20 h-4 mb-1 bg-vega-clight-500 dark:bg-vega-cdark-500 animate-pulse"></span>
<span className="bg-vega-clight-500 dark:bg-vega-cdark-500 mb-1 block h-4 w-20 animate-pulse"></span>
)}
</div>
{isCurrent && (
@ -350,12 +356,12 @@ const SuccessionLineItem = ({
{marketData ? (
marketData.tradableInstrument.instrument.name
) : (
<span className="block h-4 w-28 bg-vega-clight-500 dark:bg-vega-cdark-500 animate-pulse"></span>
<span className="bg-vega-clight-500 dark:bg-vega-cdark-500 block h-4 w-28 animate-pulse"></span>
)}
</div>
<div
data-testid="succession-line-item-market-id"
className="mt-1 text-xs truncate"
className="mt-1 truncate text-xs"
>
{marketId}
</div>
@ -364,7 +370,7 @@ const SuccessionLineItem = ({
};
const SuccessionLink = () => (
<div className="leading-none text-center" aria-hidden>
<div className="text-center leading-none" aria-hidden>
<VegaIcon name={VegaIconNames.ARROW_DOWN} size={12} />
</div>
);
@ -442,6 +448,7 @@ export const InstrumentInfoPanel = ({
};
export const SettlementAssetInfoPanel = ({ market }: MarketInfoProps) => {
const t = useT();
const assetSymbol = getAsset(market).symbol;
const quoteUnit = getQuoteName(market);
const assetId = useMemo(() => getAsset(market).id, [market]);
@ -458,8 +465,8 @@ export const SettlementAssetInfoPanel = ({ market }: MarketInfoProps) => {
/>
<p className="mt-4 text-xs">
{t(
'There is 1 unit of the settlement asset (%s) to every 1 quote unit (%s).',
[assetSymbol, quoteUnit]
'There is 1 unit of the settlement asset ({{assetSymbol}}) to every 1 quote unit ({{quoteUnit}}).',
{ assetSymbol, quoteUnit }
)}
</p>
</>
@ -686,6 +693,7 @@ export const PriceMonitoringBoundsInfoPanel = ({
}: MarketInfoProps & {
triggerIndex: number;
}) => {
const t = useT();
const { data } = useDataProvider({
dataProvider: marketDataProvider,
variables: { marketId: market.id },
@ -706,16 +714,18 @@ export const PriceMonitoringBoundsInfoPanel = ({
}
return (
<>
<div className="mb-2 text-sm grid grid-cols-2">
<div className="mb-2 grid grid-cols-2 text-sm">
<p className="col-span-1">
{t('%s probability price bounds', [
formatNumberPercentage(
{t('{{probability}} probability price bounds', {
probability: formatNumberPercentage(
new BigNumber(trigger.probability).times(100)
),
])}
})}
</p>
<p className="text-right col-span-1">
{t('Within %s seconds', [formatNumber(trigger.horizonSecs)])}
<p className="col-span-1 text-right">
{t('Within %s seconds', {
horizonSecs: formatNumber(trigger.horizonSecs),
})}
</p>
</div>
{bounds && (
@ -729,9 +739,9 @@ export const PriceMonitoringBoundsInfoPanel = ({
/>
)}
<p className="mt-2 text-xs">
{t('Results in %s seconds auction if breached', [
trigger.auctionExtensionSecs.toString(),
])}
{t('Results in {{auctionExtensionSecs}} seconds auction if breached', {
auctionExtensionSecs: trigger.auctionExtensionSecs.toString(),
})}
</p>
</>
);
@ -766,6 +776,7 @@ export const LiquidityMonitoringParametersInfoPanel = ({
};
export const EthOraclePanel = ({ sourceType }: { sourceType: EthCallSpec }) => {
const t = useT();
const abis = sourceType.abi?.map((abi) => JSON.parse(abi));
const header = 'uppercase my-1 text-left';
return (
@ -779,7 +790,7 @@ export const EthOraclePanel = ({ sourceType }: { sourceType: EthCallSpec }) => {
<CopyWithTooltip text={sourceType.address}>
<button
data-testid="copy-eth-oracle-address"
className="uppercase text-right"
className="text-right uppercase"
>
<span className="flex gap-1">
{truncateMiddle(sourceType.address)}
@ -820,9 +831,9 @@ export const EthOraclePanel = ({ sourceType }: { sourceType: EthCallSpec }) => {
<div
data-testid={`abi-dropdown`}
key={'value-dropdown'}
className="flex items-center gap-2 w-full"
className="flex w-full items-center gap-2"
>
<div className="underline underline-offset-4 mb-1 uppercase">
<div className="mb-1 uppercase underline underline-offset-4">
{t('ABI specification')}
</div>
<AccordionChevron size={14} />
@ -860,6 +871,7 @@ export const LiquidityPriceRangeInfoPanel = ({
market,
parentMarket,
}: MarketInfoProps) => {
const t = useT();
const marketLpPriceRange = market.liquiditySLAParameters?.priceRange;
const parentMarketLpPriceRange =
parentMarket?.liquiditySLAParameters?.priceRange;
@ -898,7 +910,9 @@ export const LiquidityPriceRangeInfoPanel = ({
if (parentMarket && parentMarketData && quoteUnit === parentQuoteUnit) {
parentData = {
liquidityPriceRange: `${parentLiquidityPriceRange} of mid price`,
liquidityPriceRange: t(`{{parentLiquidityPriceRange}} of mid price`, {
parentLiquidityPriceRange,
}),
lowestPrice:
parentMarketLpPriceRange &&
parentMarketData?.midPrice &&
@ -924,13 +938,16 @@ export const LiquidityPriceRangeInfoPanel = ({
return (
<>
<p className="text-xs mb-2 border-l-2 pl-2">
{`For liquidity orders to count towards a commitment, they must be
within the liquidity monitoring bounds.`}
<p className="mb-2 border-l-2 pl-2 text-xs">
{t(
`For liquidity orders to count towards a commitment, they must be within the liquidity monitoring bounds.`
)}
</p>
<MarketInfoTable
data={{
liquidityPriceRange: `${liquidityPriceRange} of mid price`,
liquidityPriceRange: t(`{{liquidityPriceRange}} of mid price`, {
liquidityPriceRange,
}),
lowestPrice:
marketLpPriceRange &&
data?.midPrice &&
@ -954,16 +971,18 @@ export const LiquidityPriceRangeInfoPanel = ({
}}
parentData={parentData}
/>
<p className="text-xs mb-2 border-l-2 pl-2 mt-2">
{`The liquidity price range is a ${liquidityPriceRange} difference from the mid
price.`}
<p className="mb-2 mt-2 border-l-2 pl-2 text-xs">
{t(
'The liquidity price range is a {{{liquidityPriceRange}} difference from the mid price.',
{ liquidityPriceRange }
)}
</p>
</>
);
};
const fromNanoSecondsToSeconds = (nanoseconds: number | string) =>
t('%ss', [new BigNumber(nanoseconds).dividedBy(1e9).toString()]);
`${new BigNumber(nanoseconds).dividedBy(1e9).toString()}s`;
export const LiquiditySLAParametersInfoPanel = ({
market,
@ -1069,6 +1088,7 @@ export const FundingInfoPanel = ({
}: {
dataSource: DataSourceFragment;
}) => {
const t = useT();
const sourceType = dataSource.data.sourceType.sourceType;
if (
sourceType.__typename !== 'DataSourceSpecConfigurationTimeTrigger' ||
@ -1079,13 +1099,16 @@ export const FundingInfoPanel = ({
const { every, initial } = sourceType.triggers[0];
const hours = Math.floor(every / (60 * 60));
const minutes = Math.floor(every / 60) % 60;
const initialLabel = initial
? ` ${t('from')} ${getDateTimeFormat().format(new Date(initial * 1000))}`
: '';
return `${t('every')} ${formatDuration({
const duration = formatDuration({
hours,
minutes,
})} ${initialLabel}`;
});
return initial
? t('every {{duration}} from {{initialTime}}', {
duration,
initialTime: getDateTimeFormat().format(new Date(initial * 1000)),
})
: t('every {{duration}}', { duration });
};
export const OracleInfoPanel = ({
@ -1097,6 +1120,7 @@ export const OracleInfoPanel = ({
}) => {
// If this is a successor market, this component will only receive parent market
// data if the termination or settlement data is different from the parent.
const t = useT();
const product = market.tradableInstrument.instrument.product;
const parentProduct = parentMarket?.tradableInstrument?.instrument?.product;
const { VEGA_EXPLORER_URL, ORACLE_PROOFS_URL } = useEnvironment();
@ -1137,7 +1161,7 @@ export const OracleInfoPanel = ({
parentDataSourceSpec &&
parentDataSourceSpecId &&
parentProduct && (
<div className="flex flex-col line-through gap-2 text-vega-dark-300">
<div className="text-vega-dark-300 flex flex-col gap-2 line-through">
<DataSourceProof
data-testid="oracle-proof-links"
data={parentDataSourceSpec}
@ -1208,6 +1232,7 @@ export const DataSourceProof = ({
type: 'settlementData' | 'termination' | 'settlementSchedule';
dataSourceSpecId: string;
}) => {
const t = useT();
if (data.sourceType.__typename === 'DataSourceDefinitionExternal') {
const signers =
('signers' in data.sourceType.sourceType &&
@ -1375,12 +1400,12 @@ const NoOracleProof = ({
}: {
type: 'settlementData' | 'termination' | 'settlementSchedule';
}) => {
const t = useT();
return (
<p>
{t(
'No oracle proof for %s',
type === 'settlementData' ? 'settlement data' : 'termination'
)}
{type === 'settlementData'
? t('No oracle proof for settlement data')
: t('No oracle proof for termination')}
</p>
);
};

View File

@ -1,148 +1,160 @@
import { ExternalLinks } from '@vegaprotocol/environment';
import { ExternalLink } from '@vegaprotocol/ui-toolkit';
import { t } from '@vegaprotocol/i18n';
import type { ReactNode } from 'react';
import { Trans } from 'react-i18next';
import { useT } from '../../use-t';
export const tooltipMapping: Record<string, ReactNode> = {
makerFee: t(
'Maker portion of the fee is transferred to the non-aggressive, or passive party in the trade (the maker, as opposed to the taker).'
),
liquidityFee: t(
'Liquidity portion of the fee is paid to liquidity providers, and is transferred to the liquidity fee pool for the market.'
),
infrastructureFee: t(
'Fees paid to validators as a reward for running the infrastructure of the network.'
),
export const useTooltipMapping: () => Record<string, ReactNode> = () => {
const t = useT();
return {
makerFee: t(
'Maker portion of the fee is transferred to the non-aggressive, or passive party in the trade (the maker, as opposed to the taker).'
),
liquidityFee: t(
'Liquidity portion of the fee is paid to liquidity providers, and is transferred to the liquidity fee pool for the market.'
),
infrastructureFee: t(
'Fees paid to validators as a reward for running the infrastructure of the network.'
),
markPrice: t(
'A concept derived from traditional markets. It is a calculated value for the current market price on a market.'
),
quoteUnit: t(
`The underlying that is being priced by the market, described by the market's oracle.`
),
openInterest: t(
'The volume of all open positions in a given market (the sum of the size of all positions greater than 0).'
),
indicativeVolume: t(
'The volume at which all trades would occur if the auction was uncrossed now (when in auction mode).'
),
bestBidVolume: t(
'The aggregated volume being bid at the best bid price on the market.'
),
bestOfferVolume: t(
'The aggregated volume being offered at the best offer price on the market.'
),
bestStaticBidVolume: t(
'The aggregated volume being bid at the best static bid price on the market.'
),
bestStaticOfferVolume: t(
'The aggregated volume being offered at the best static offer price on the market.'
),
marketDecimalPlaces: t('The smallest price increment on the book.'),
decimalPlaces: t('The smallest price increment on the book.'),
positionDecimalPlaces: t(
'How big the smallest order / position on the market can be.'
),
tradingMode: t('The trading mode the market is currently running.'),
state: t('The current state of the market'),
markPrice: t(
'A concept derived from traditional markets. It is a calculated value for the current market price on a market.'
),
quoteUnit: t(
`The underlying that is being priced by the market, described by the market's oracle.`
),
openInterest: t(
'The volume of all open positions in a given market (the sum of the size of all positions greater than 0).'
),
indicativeVolume: t(
'The volume at which all trades would occur if the auction was uncrossed now (when in auction mode).'
),
bestBidVolume: t(
'The aggregated volume being bid at the best bid price on the market.'
),
bestOfferVolume: t(
'The aggregated volume being offered at the best offer price on the market.'
),
bestStaticBidVolume: t(
'The aggregated volume being bid at the best static bid price on the market.'
),
bestStaticOfferVolume: t(
'The aggregated volume being offered at the best static offer price on the market.'
),
marketDecimalPlaces: t('The smallest price increment on the book.'),
decimalPlaces: t('The smallest price increment on the book.'),
positionDecimalPlaces: t(
'How big the smallest order / position on the market can be.'
),
tradingMode: t('The trading mode the market is currently running.'),
state: t('The current state of the market'),
base: t(
'The first currency in a pair for a currency-based derivatives market.'
),
quote: t(
'The second currency in a pair for a currency-based derivatives market.'
),
class: t(
'The classification of the product. Examples: shares, commodities, crypto, FX.'
),
sector: t(
'Data about the sector. Example: "automotive" for a market based on value of Tesla shares.'
),
base: t(
'The first currency in a pair for a currency-based derivatives market.'
),
quote: t(
'The second currency in a pair for a currency-based derivatives market.'
),
class: t(
'The classification of the product. Examples: shares, commodities, crypto, FX.'
),
sector: t(
'Data about the sector. Example: "automotive" for a market based on value of Tesla shares.'
),
short: t(
'A number that will be calculated by an appropriate stochastic risk model, dependent on the type of risk model used and its parameters.'
),
long: t(
'A number that will be calculated by an appropriate stochastic risk model, dependent on the type of risk model used and its parameters.'
),
short: t(
'A number that will be calculated by an appropriate stochastic risk model, dependent on the type of risk model used and its parameters.'
),
long: t(
'A number that will be calculated by an appropriate stochastic risk model, dependent on the type of risk model used and its parameters.'
),
tau: (
<span>
{t('Projection horizon measured as a year fraction used in ')}
<ExternalLink href={ExternalLinks.MARGIN_CREDIT_RISK}>
{t('Expected Shortfall')}
</ExternalLink>
{t(' calculation when obtaining Risk Factor Long and Risk Factor Short')}
</span>
),
riskAversionParameter: (
<span>
{t('Probability level used in ')}
<ExternalLink href={ExternalLinks.MARGIN_CREDIT_RISK}>
{t('Expected Shortfall')}
</ExternalLink>
{t(' calculation when obtaining Risk Factor Long and Risk Factor Short')}
</span>
),
tau: (
<span>
<Trans
defaults="Projection horizon measured as a year fraction used in <0>Expected Shortfall</0> calculation when obtaining Risk Factor Long and Risk Factor Short"
components={[
<ExternalLink href={ExternalLinks.MARGIN_CREDIT_RISK}>
Expected Shortfall
</ExternalLink>,
]}
/>
</span>
),
riskAversionParameter: (
<span>
<Trans
defaults="Probability level used in <0>Expected Shortfall</0> calculation when obtaining Risk Factor Long and Risk Factor Short"
components={[
<ExternalLink href={ExternalLinks.MARGIN_CREDIT_RISK}>
Expected Shortfall
</ExternalLink>,
]}
/>
</span>
),
horizonSecs: t('Time horizon of the price projection in seconds.'),
probability: t(
'Probability level for price projection, e.g. value of 0.95 will result in a price range such that over the specified projection horizon, the prices observed in the market should be in that range 95% of the time.'
),
auctionExtensionSecs: t(
'Auction extension duration in seconds, should the price breach its theoretical level over the specified horizon at the specified probability level.'
),
horizonSecs: t('Time horizon of the price projection in seconds.'),
probability: t(
'Probability level for price projection, e.g. value of 0.95 will result in a price range such that over the specified projection horizon, the prices observed in the market should be in that range 95% of the time.'
),
auctionExtensionSecs: t(
'Auction extension duration in seconds, should the price breach its theoretical level over the specified horizon at the specified probability level.'
),
triggeringRatio: t('The triggering ratio for entering liquidity auction.'),
timeWindow: t('The length of time over which open interest is measured.'),
scalingFactor: t(
'The scaling between the liquidity demand estimate, based on open interest and target stake.'
),
targetStake: t(
`The market's liquidity requirement which is derived from the maximum open interest observed over a rolling time window.`
),
suppliedStake: t('The current amount of liquidity supplied for this market.'),
parentMarketID: t('The ID of the market this market succeeds.'),
insurancePoolFraction: t(
'The fraction of the insurance pool balance that is carried over from the parent market to the successor.'
),
commitmentMinTimeFraction: t(
`Specifies the minimum fraction of time LPs must spend 'on the book' providing their committed liquidity. This is a market parameter.`
),
feeCalculationTimeStep: t(
'How often the quality of liquidity supplied by each liquidity provider is evaluated and the fees arising from that period are earmarked for specific providers. This is a market parameter. '
),
performanceHysteresisEpochs: t(
'Number of epochs over which past performance will continue to affect rewards. This is a market parameter.'
),
SLACompetitionFactor: t(
`Maximum fraction of an LP's accrued fees that an LP would lose to liquidity providers that achieved a higher SLA performance than them. This is a market parameter. `
),
bondPenaltyParameter: t(
'Used to calculate the penalty to liquidity providers when they cannot support their open position with the assets in their margin and general accounts. This is a network parameter.'
),
nonPerformanceBondPenaltySlope: t(
'A sliding penalty for how much an LP bond is slashed if an LP fails to reach the minimum SLA. This is a network parameter.'
),
nonPerformanceBondPenaltyMax: t(
`The maximum amount, as a fraction, that an LP's bond can be slashed by if they fail to reach the minimum SLA. This is a network parameter.`
),
maxLiquidityFeeFactorLevel: t(
'Maximum value that a proposed fee amount can be, which is submitted as part of the LP commitment transaction. Note that a value of 0.05 = 5%. This is a network parameter.'
),
stakeToCCYVolume: t(
`Multiplier used to translate an LP's commitment amount to their liquidity obligation. This is a network parameter.`
),
epochLength: t(
'How long an epoch is. LP rewards from liquidity fees are paid out once per epoch. How much they receive depends on whether they met the liquidity SLA and their previous performance in recent epochs. This is a network parameter.'
),
earlyExitPenalty: t(
`The percentage of their bond an LP forfeits if they reduce their commitment while the market is below target stake. If 100%, an LP's entire bond is forfeited when they cancel their full commitment. This is a network parameter.`
),
probabilityOfTradingTauScaling: t(
`Determines how the probability of trading is scaled from the risk model, and is used to measure the relative competitiveness of an LP's supplied volume. This is a network parameter.`
),
minProbabilityOfTradingLPOrders: t(
'The lower bound for the probability of trading calculation, used to measure liquidity available on a market to determine if LPs are meeting their commitment. This is a network parameter.'
),
triggeringRatio: t('The triggering ratio for entering liquidity auction.'),
timeWindow: t('The length of time over which open interest is measured.'),
scalingFactor: t(
'The scaling between the liquidity demand estimate, based on open interest and target stake.'
),
targetStake: t(
`The market's liquidity requirement which is derived from the maximum open interest observed over a rolling time window.`
),
suppliedStake: t(
'The current amount of liquidity supplied for this market.'
),
parentMarketID: t('The ID of the market this market succeeds.'),
insurancePoolFraction: t(
'The fraction of the insurance pool balance that is carried over from the parent market to the successor.'
),
commitmentMinTimeFraction: t(
`Specifies the minimum fraction of time LPs must spend 'on the book' providing their committed liquidity. This is a market parameter.`
),
feeCalculationTimeStep: t(
'How often the quality of liquidity supplied by each liquidity provider is evaluated and the fees arising from that period are earmarked for specific providers. This is a market parameter. '
),
performanceHysteresisEpochs: t(
'Number of epochs over which past performance will continue to affect rewards. This is a market parameter.'
),
SLACompetitionFactor: t(
`Maximum fraction of an LP's accrued fees that an LP would lose to liquidity providers that achieved a higher SLA performance than them. This is a market parameter. `
),
bondPenaltyParameter: t(
'Used to calculate the penalty to liquidity providers when they cannot support their open position with the assets in their margin and general accounts. This is a network parameter.'
),
nonPerformanceBondPenaltySlope: t(
'A sliding penalty for how much an LP bond is slashed if an LP fails to reach the minimum SLA. This is a network parameter.'
),
nonPerformanceBondPenaltyMax: t(
`The maximum amount, as a fraction, that an LP's bond can be slashed by if they fail to reach the minimum SLA. This is a network parameter.`
),
maxLiquidityFeeFactorLevel: t(
'Maximum value that a proposed fee amount can be, which is submitted as part of the LP commitment transaction. Note that a value of 0.05 = 5%. This is a network parameter.'
),
stakeToCCYVolume: t(
`Multiplier used to translate an LP's commitment amount to their liquidity obligation. This is a network parameter.`
),
epochLength: t(
'How long an epoch is. LP rewards from liquidity fees are paid out once per epoch. How much they receive depends on whether they met the liquidity SLA and their previous performance in recent epochs. This is a network parameter.'
),
earlyExitPenalty: t(
`The percentage of their bond an LP forfeits if they reduce their commitment while the market is below target stake. If 100%, an LP's entire bond is forfeited when they cancel their full commitment. This is a network parameter.`
),
probabilityOfTradingTauScaling: t(
`Determines how the probability of trading is scaled from the risk model, and is used to measure the relative competitiveness of an LP's supplied volume. This is a network parameter.`
),
minProbabilityOfTradingLPOrders: t(
'The lower bound for the probability of trading calculation, used to measure liquidity available on a market to determine if LPs are meeting their commitment. This is a network parameter.'
),
};
};

View File

@ -1,5 +1,4 @@
import { useState } from 'react';
import { t } from '@vegaprotocol/i18n';
import { useMarketOracle } from '../../hooks';
import {
Intent,
@ -7,9 +6,11 @@ import {
ButtonLink,
} from '@vegaprotocol/ui-toolkit';
import { OracleDialog } from '../oracle-dialog';
import { oracleStatuses } from './oracle-statuses';
import { useOracleStatuses } from './oracle-statuses';
import { Trans } from 'react-i18next';
export const OracleBanner = ({ marketId }: { marketId: string }) => {
const oracleStatuses = useOracleStatuses();
const [open, onChange] = useState(false);
const { data: settlementOracle } = useMarketOracle(marketId);
const { data: tradingTerminationOracle } = useMarketOracle(
@ -30,17 +31,22 @@ export const OracleBanner = ({ marketId }: { marketId: string }) => {
<OracleDialog open={open} onChange={onChange} {...maliciousOracle} />
<NotificationBanner intent={Intent.Danger}>
<div>
Oracle status for this market is{' '}
<span data-testid="oracle-banner-status">
{provider.oracle.status}
</span>
. {oracleStatuses[provider.oracle.status]}{' '}
<ButtonLink
onClick={() => onChange(!open)}
data-testid="oracle-banner-dialog-trigger"
>
{t('Show more')}
</ButtonLink>
<Trans
defaults="Oracle status for this market is <0>{{status}}</0>. {{description}} <1>Show more</1>"
components={[
<span data-testid="oracle-banner-status">status</span>,
<ButtonLink
onClick={() => onChange(!open)}
data-testid="oracle-banner-dialog-trigger"
>
Show more
</ButtonLink>,
]}
values={{
status: provider.oracle.status,
description: oracleStatuses[provider.oracle.status],
}}
/>
</div>
</NotificationBanner>
</>

View File

@ -1,16 +1,19 @@
import { t } from '@vegaprotocol/i18n';
import { useT } from '../../use-t';
export const oracleStatuses = {
UNKNOWN: t(
"This public key's proofs have not been verified yet, or no proofs have been provided yet."
),
GOOD: t("This public key's proofs have been verified."),
SUSPICIOUS: t(
'This public key is suspected to be acting in bad faith, pending investigation.'
),
MALICIOUS: t('This public key has been observed acting in bad faith.'),
RETIRED: t('This public key is no longer in use.'),
COMPROMISED: t(
'This public key is no longer in the control of its original owners.'
),
export const useOracleStatuses = () => {
const t = useT();
return {
UNKNOWN: t(
"This public key's proofs have not been verified yet, or no proofs have been provided yet."
),
GOOD: t("This public key's proofs have been verified."),
SUSPICIOUS: t(
'This public key is suspected to be acting in bad faith, pending investigation.'
),
MALICIOUS: t('This public key has been observed acting in bad faith.'),
RETIRED: t('This public key is no longer in use.'),
COMPROMISED: t(
'This public key is no longer in the control of its original owners.'
),
};
};

View File

@ -1,4 +1,3 @@
import { t } from '@vegaprotocol/i18n';
import type { Provider } from '../../oracle-schema';
import {
ButtonLink,
@ -12,8 +11,10 @@ import type { IconName } from '@blueprintjs/icons';
import { IconNames } from '@blueprintjs/icons';
import classNames from 'classnames';
import type { OracleMarketSpecFieldsFragment } from '../../__generated__/OracleMarketsSpec';
import { useT } from '../../use-t';
export const getVerifiedStatusIcon = (provider: Provider) => {
export const useVerifiedStatusIcon = (provider: Provider) => {
const t = useT();
const getIconIntent = () => {
switch (provider.oracle.status) {
case 'GOOD':
@ -46,13 +47,12 @@ export const getVerifiedStatusIcon = (provider: Provider) => {
return {
...getIconIntent(),
message: t(
'Verified since %s',
lastVerified.toLocaleDateString(undefined, {
message: t('Verified since {{lastVerified}}', {
lastVerified: lastVerified.toLocaleDateString(undefined, {
year: 'numeric',
month: 'long',
})
),
}),
}),
};
};
@ -65,7 +65,8 @@ export const OracleBasicProfile = ({
markets?: OracleMarketSpecFieldsFragment[] | undefined;
onClick?: (value?: boolean) => void;
}) => {
const { icon, message, intent } = getVerifiedStatusIcon(provider);
const t = useT();
const { icon, message, intent } = useVerifiedStatusIcon(provider);
const verifiedProofs = provider.proofs.filter(
(proof) => proof.available === true
@ -121,10 +122,9 @@ export const OracleBasicProfile = ({
data-testid="signed-proofs"
className="mb-2 text-sm dark:text-vega-light-300 text-vega-dark-300"
>
{t('Involved in %s %s', [
oracleMarkets.length.toString(),
oracleMarkets.length !== 1 ? t('markets') : t('market'),
])}
{t('involvedInMarkets', 'Involved in {{count}} markets', {
count: oracleMarkets.length,
})}
</p>
)}
{links.length > 0 && (

View File

@ -1,4 +1,3 @@
import { t } from '@vegaprotocol/i18n';
import type { Provider } from '../../oracle-schema';
import { MarketState, MarketStateMapping } from '@vegaprotocol/types';
import {
@ -10,14 +9,15 @@ import {
VegaIcon,
VegaIconNames,
} from '@vegaprotocol/ui-toolkit';
import { oracleStatuses } from '../oracle-banner/oracle-statuses';
import { useOracleStatuses } from '../oracle-banner/oracle-statuses';
import type { IconName } from '@blueprintjs/icons';
import classNames from 'classnames';
import { getLinkIcon, getVerifiedStatusIcon } from '../oracle-basic-profile';
import { getLinkIcon, useVerifiedStatusIcon } from '../oracle-basic-profile';
import { useEnvironment } from '@vegaprotocol/environment';
import type { OracleMarketSpecFieldsFragment } from '../../__generated__/OracleMarketsSpec';
import ReactMarkdown from 'react-markdown';
import { useState } from 'react';
import { useT } from '../../use-t';
export const OracleProfileTitle = ({
provider,
@ -26,10 +26,11 @@ export const OracleProfileTitle = ({
provider: Provider;
parentProvider?: Provider;
}) => {
const t = useT();
// If this is a successor market, the parent provider will only have been passed
// in if it differs from the current provider. If it is different, we'll just
// show the change in name, not icons and proofs.
const { icon, intent } = getVerifiedStatusIcon(provider);
const { icon, intent } = useVerifiedStatusIcon(provider);
const verifiedProofs = provider.proofs.filter(
(proof) => proof.available === true
);
@ -55,10 +56,10 @@ export const OracleProfileTitle = ({
'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,
'dark:text-yellow text-yellow-600': intent === Intent.Warning,
'text-vega-red': intent === Intent.Danger,
},
'flex items-start align-text-bottom p-1'
'flex items-start p-1 align-text-bottom'
)}
>
<Icon size={6} name={icon as IconName} />
@ -67,23 +68,30 @@ export const OracleProfileTitle = ({
);
};
const OracleStatus = ({ oracle }: { oracle: Provider['oracle'] }) => (
<div>
{t(`Oracle status`)}: {oracle.status}. {oracleStatuses[oracle.status]}
{oracle.status_reason ? (
<div>
<ReactMarkdown
className="react-markdown-container"
skipHtml={true}
disallowedElements={['img']}
linkTarget="_blank"
>
{oracle.status_reason}
</ReactMarkdown>
</div>
) : null}
</div>
);
const OracleStatus = ({ oracle }: { oracle: Provider['oracle'] }) => {
const oracleStatuses = useOracleStatuses();
const t = useT();
return (
<div>
{t(`Oracle status: {{status}}. {{description}}`, {
status: oracle.status,
description: oracleStatuses[oracle.status],
})}
{oracle.status_reason ? (
<div>
<ReactMarkdown
className="react-markdown-container"
skipHtml={true}
disallowedElements={['img']}
linkTarget="_blank"
>
{oracle.status_reason}
</ReactMarkdown>
</div>
) : null}
</div>
);
};
export const OracleFullProfile = ({
provider,
@ -94,7 +102,8 @@ export const OracleFullProfile = ({
dataSourceSpecId: string;
markets?: OracleMarketSpecFieldsFragment[] | undefined;
}) => {
const { message } = getVerifiedStatusIcon(provider);
const t = useT();
const { message } = useVerifiedStatusIcon(provider);
const { VEGA_EXPLORER_URL } = useEnvironment();
const [showMore, setShowMore] = useState(false);
@ -140,10 +149,9 @@ export const OracleFullProfile = ({
className="dark:text-vega-light-300 text-vega-dark-300 uppercase"
data-testid="verified-accounts"
>
{t('%s %s of ownership', [
provider.proofs.length.toString(),
provider.proofs.length === 1 ? 'proof' : 'proofs',
])}
{t('proofsOfOwnership', '{{count}} proofs of ownership', {
count: provider.proofs.length,
})}
</p>
{provider.proofs.length > 0 ? (
<div className="flex flex-col gap-1">
@ -151,12 +159,12 @@ export const OracleFullProfile = ({
<ExternalLink
key={link.url}
href={link.url}
className="flex align-items-bottom underline text-sm"
className="align-items-bottom flex text-sm underline"
>
<span className="pt-1 pr-1">
<span className="pr-1 pt-1">
<VegaIcon name={getLinkIcon(link.type)} />
</span>
<span className="underline capitalize">
<span className="capitalize underline">
{link.type}{' '}
<VegaIcon name={VegaIconNames.OPEN_EXTERNAL} size={13} />
</span>
@ -166,22 +174,24 @@ export const OracleFullProfile = ({
<ExternalLink
key={'more-proofs'}
href={provider.github_link}
className="flex align-items-bottom underline text-sm pt-2"
className="align-items-bottom flex pt-2 text-sm underline"
>
{links.length > 0 ? (
<span className="underline">
{t('And %s more %s', [
signedMessageProofs.length.toString(),
signedMessageProofs.length === 1 ? 'proof' : 'proofs',
])}{' '}
{t('moreProofs', 'And {{count}} more proofs', {
count: signedMessageProofs.length,
})}{' '}
<VegaIcon name={VegaIconNames.OPEN_EXTERNAL} size={13} />
</span>
) : (
<span className="underline">
{t('Verify %s %s of ownership', [
signedMessageProofs.length.toString(),
signedMessageProofs.length === 1 ? 'proof' : 'proofs',
])}{' '}
{t(
'verifyProofs',
'Verify {{count}} proofs of ownership',
{
proofs: signedMessageProofs,
}
)}{' '}
<VegaIcon name={VegaIconNames.OPEN_EXTERNAL} size={13} />
</span>
)}
@ -194,7 +204,7 @@ export const OracleFullProfile = ({
</p>
)}
</div>
<div className="col-span-1 gap-2 py-2 flex flex-col">
<div className="col-span-1 flex flex-col gap-2 py-2">
<p className="dark:text-vega-light-300 text-vega-dark-300 uppercase">
{t('Details')}
</p>
@ -215,20 +225,19 @@ export const OracleFullProfile = ({
</div>
<div>
{oracleMarkets && (
<p className="dark:text-vega-light-300 text-vega-dark-300 uppercase mt-4">
{t('Oracle in %s %s', [
oracleMarkets.length.toString(),
oracleMarkets.length === 1 ? 'market' : 'markets',
])}
<p className="dark:text-vega-light-300 text-vega-dark-300 mt-4 uppercase">
{t('oracleInMarkets', 'Oracle in {{count}} markets', {
count: oracleMarkets.length,
})}
</p>
)}
</div>
{oracleMarkets && oracleMarkets.length > 0 && (
<div
data-testid="oracle-markets"
className="border-vega-light-200 dark:border-vega-dark-200 border-solid border-2 py-4 px-2 rounded-lg my-2"
className="border-vega-light-200 dark:border-vega-dark-200 my-2 rounded-lg border-2 border-solid px-2 py-4"
>
<div className="grid grid-cols-4 gap-1 uppercase mb-2 font-alpha calt dark:text-vega-light-300 text-vega-dark-300">
<div className="font-alpha calt dark:text-vega-light-300 text-vega-dark-300 mb-2 grid grid-cols-4 gap-1 uppercase">
<div className="col-span-1">{t('Market')}</div>
<div className="col-span-1">{t('Status')}</div>
<div className="col-span-1">{t('Specifications')}</div>
@ -236,7 +245,7 @@ export const OracleFullProfile = ({
<div className="max-h-60 overflow-auto">
{oracleMarkets?.map((market) => (
<div
className="grid grid-cols-4 gap-1 capitalize mb-2 last:mb-0"
className="mb-2 grid grid-cols-4 gap-1 capitalize last:mb-0"
key={`oracle-market-${market.id}`}
>
<div className="col-span-1">

View File

@ -0,0 +1,3 @@
import { useTranslation } from 'react-i18next';
export const useT = () => useTranslation('funding-payments').t;

View File

@ -1,4 +1,17 @@
import '@testing-library/jest-dom';
import { locales } from '@vegaprotocol/i18n';
import i18n from 'i18next';
import { initReactI18next } from 'react-i18next';
import ResizeObserver from 'resize-observer-polyfill';
// Set up i18n instance so that components have the correct default
// en translations
i18n.use(initReactI18next).init({
// we init with resources
resources: locales,
fallbackLng: 'en',
ns: ['markets'],
defaultNS: 'markets',
});
global.ResizeObserver = ResizeObserver;