Compare commits

..
52 changed files with 282 additions and 1079 deletions
@@ -1,46 +0,0 @@
import { t } from '@vegaprotocol/i18n';
import { TxDetailsShared } from '../shared/tx-details-shared';
import { TableWithTbody } from '../../../table';
import type { components } from '../../../../../types/explorer';
import type { BlockExplorerTransactionResult } from '../../../../routes/types/block-explorer-response';
import type { TendermintBlocksResponse } from '../../../../routes/blocks/tendermint-blocks-response';
import { TableCell, TableRow } from '../../../table';
type Update = components['schemas']['v1UpdatePartyProfile'];
interface TxDetailsUpdatePartyProfileProps {
txData: BlockExplorerTransactionResult | undefined;
pubKey: string | undefined;
blockData: TendermintBlocksResponse | undefined;
}
/**
* Party profiles can be an alias and arbitrary key/values pairs.
* This component displays the alias, if any, but not the metadata. When there is
* some wider usage, we can decide how to render it. For now, it's available in the
* full TX details.
*/
export const TxDetailsUpdatePartyProfile = ({
txData,
pubKey,
blockData,
}: TxDetailsUpdatePartyProfileProps) => {
if (!txData?.command.updatePartyProfile) {
return <>{t('Awaiting Block Explorer transaction details')}</>;
}
const update: Update = txData.command.updatePartyProfile;
return (
<TableWithTbody className="mb-8" allowWrap={true}>
<TxDetailsShared txData={txData} pubKey={pubKey} blockData={blockData} />
{update.alias && (
<TableRow modifier="bordered">
<TableCell>{t('New alias')}</TableCell>
<TableCell>{update.alias}</TableCell>
</TableRow>
)}
</TableWithTbody>
);
};
@@ -34,7 +34,6 @@ import { TxDetailsUpdateReferralSet } from './tx-update-referral-set';
import { TxDetailsJoinTeam } from './tx-join-team';
import { TxDetailsUpdateMarginMode } from './tx-update-margin-mode';
import { TxBatchProposal } from './tx-batch-proposal';
import { TxDetailsUpdatePartyProfile } from './proposal/tx-update-party-profile';
interface TxDetailsWrapperProps {
txData: BlockExplorerTransactionResult | undefined;
@@ -140,8 +139,6 @@ function getTransactionComponent(txData?: BlockExplorerTransactionResult) {
return TxDetailsUpdateMarginMode;
case 'Batch Proposal':
return TxBatchProposal;
case 'Update Party Profile':
return TxDetailsUpdatePartyProfile;
default:
return TxDetailsGeneric;
}
@@ -44,7 +44,6 @@ export type FilterOption =
| 'Submit Order'
| 'Transfer Funds'
| 'Undelegate'
| 'Update Party Profile'
| 'Update Referral Set'
| 'Update Margin Mode'
| 'Validator Heartbeat'
@@ -80,7 +79,6 @@ export const filterOptions: Record<string, FilterOption[]> = {
'Apply Referral Code',
'Create Referral Set',
'Join Team',
'Update Party Profile',
'Update Referral Set',
],
'External Data': ['Chain Event', 'Submit Oracle Data'],
@@ -5,10 +5,7 @@ import { useTranslation } from 'react-i18next';
import { useAppState } from '../../../contexts/app-state/app-state-context';
import type { BigNumber } from '../../../lib/bignumber';
import type { UndelegateSubmissionBody } from '@vegaprotocol/wallet';
import {
UndelegateSubmissionMethod,
useVegaWallet,
} from '@vegaprotocol/wallet';
import { useVegaWallet } from '@vegaprotocol/wallet';
import { removeDecimal } from '@vegaprotocol/utils';
interface PendingStakeProps {
@@ -41,7 +38,7 @@ export const PendingStake = ({
undelegateSubmission: {
nodeId,
amount: removeDecimal(pendingAmount.toString(), appState.decimals),
method: UndelegateSubmissionMethod.METHOD_NOW,
method: 'METHOD_NOW',
},
};
await sendTx(pubKey, command);
@@ -28,16 +28,12 @@ import {
NetworkParams,
} from '@vegaprotocol/network-parameters';
import { useBalances } from '../../../lib/balances/balances-store';
import {
UndelegateSubmissionMethod,
useVegaWallet,
type DelegateSubmissionBody,
type UndelegateSubmissionBody,
} from '@vegaprotocol/wallet';
import { useVegaWallet } from '@vegaprotocol/wallet';
import { SubHeading } from '../../../components/heading';
import type {} from '@vegaprotocol/wallet';
import type {
DelegateSubmissionBody,
UndelegateSubmissionBody,
} from '@vegaprotocol/wallet';
import Routes from '../../routes';
export enum FormState {
@@ -132,8 +128,8 @@ export const StakingForm = ({
amount: removeDecimal(amount, appState.decimals),
method:
removeType === RemoveType.Now
? UndelegateSubmissionMethod.METHOD_NOW
: UndelegateSubmissionMethod.METHOD_AT_END_OF_EPOCH,
? 'METHOD_NOW'
: 'METHOD_AT_END_OF_EPOCH',
},
};
try {
+1 -1
View File
@@ -1,4 +1,4 @@
NX_ETHEREUM_PROVIDER_URL=https://eth-mainnet.rpc.grove.city/v1/af6a2d529a11f8158bc8ca2a
NX_ETHEREUM_PROVIDER_URL=https://eth-mainnet.gateway.pokt.network/v1/lb/af6a2d529a11f8158bc8ca2a
NX_ETHERSCAN_URL=https://etherscan.io
NX_GITHUB_FEEDBACK_URL=https://github.com/vegaprotocol/feedback/discussions
NX_SENTRY_DSN=https://2ffce43721964aafa78277c50654ece4@o286262.ingest.sentry.io/6300613
@@ -1,10 +1,5 @@
import { Link, useSearchParams } from 'react-router-dom';
import {
Intent,
TradingAnchorButton,
VegaIcon,
VegaIconNames,
} from '@vegaprotocol/ui-toolkit';
import { useSearchParams } from 'react-router-dom';
import { Intent, TradingAnchorButton } from '@vegaprotocol/ui-toolkit';
import { useVegaWallet, useVegaWalletDialogStore } from '@vegaprotocol/wallet';
import { addDecimalsFormatNumber } from '@vegaprotocol/utils';
import { useT } from '../../lib/use-t';
@@ -35,19 +30,6 @@ export const CompetitionsCreateTeam = () => {
<LayoutWithGradient>
<div className="mx-auto md:w-2/3 max-w-xl">
<Box className="flex flex-col gap-4">
<Link
to={Links.COMPETITIONS()}
className="text-xs inline-flex items-center gap-1 group"
>
<VegaIcon
name={VegaIconNames.CHEVRON_LEFT}
size={12}
className="text-vega-clight-100 dark:text-vega-cdark-100"
/>{' '}
<span className="group-hover:underline">
{t('Go back to the competitions')}
</span>
</Link>
<h1 className="calt text-2xl lg:text-3xl xl:text-4xl">
{isSolo ? t('Create solo team') : t('Create a team')}
</h1>
@@ -96,17 +78,15 @@ const CreateTeamFormContainer = ({ isSolo }: { isSolo: boolean }) => {
<p className="text-sm">{t('Team creation transaction successful')}</p>
{code && (
<>
<dl>
<dt className="text-sm">{t('Your team ID:')}</dt>
<dl>
<span
className="font-mono break-all bg-rainbow bg-clip-text text-transparent text-2xl"
data-testid="team-id-display"
>
{code}
</span>
</dl>
</dl>
<p className="text-sm">
Your team ID is:{' '}
<span
className="font-mono break-all"
data-testid="team-id-display"
>
{code}
</span>
</p>
<TradingAnchorButton
href={Links.COMPETITIONS_TEAM(code)}
intent={Intent.Info}
@@ -3,14 +3,7 @@ import { usePageTitle } from '../../lib/hooks/use-page-title';
import { Box } from '../../components/competitions/box';
import { useT } from '../../lib/use-t';
import { useVegaWallet, useVegaWalletDialogStore } from '@vegaprotocol/wallet';
import {
Intent,
Loader,
Splash,
TradingAnchorButton,
VegaIcon,
VegaIconNames,
} from '@vegaprotocol/ui-toolkit';
import { Loader, Splash } from '@vegaprotocol/ui-toolkit';
import { RainbowButton } from '../../components/rainbow-button';
import { Link, Navigate, useParams } from 'react-router-dom';
import { Links } from '../../lib/links';
@@ -18,7 +11,6 @@ import { useReferralSetTransaction } from '../../lib/hooks/use-referral-set-tran
import { type FormFields, TeamForm, TransactionType } from './team-form';
import { useTeam } from '../../lib/hooks/use-team';
import { LayoutWithGradient } from '../../components/layouts-inner';
import { useEffect, useState } from 'react';
export const CompetitionsUpdateTeam = () => {
const t = useT();
@@ -37,19 +29,6 @@ export const CompetitionsUpdateTeam = () => {
<LayoutWithGradient>
<div className="mx-auto md:w-2/3 max-w-xl">
<Box className="flex flex-col gap-4">
<Link
to={Links.COMPETITIONS_TEAM(teamId)}
className="text-xs inline-flex items-center gap-1 group"
>
<VegaIcon
name={VegaIconNames.CHEVRON_LEFT}
size={12}
className="text-vega-clight-100 dark:text-vega-cdark-100"
/>{' '}
<span className="group-hover:underline">
{t('Go back to the team profile')}
</span>
</Link>
<h1 className="calt text-2xl lg:text-3xl xl:text-5xl">
{t('Update a team')}
</h1>
@@ -78,8 +57,7 @@ const UpdateTeamFormContainer = ({
pubKey: string;
}) => {
const t = useT();
const [refetching, setRefetching] = useState<boolean>(false);
const { team, loading, error, refetch } = useTeam(teamId, pubKey);
const { team, loading, error } = useTeam(teamId, pubKey);
const { err, status, onSubmit } = useReferralSetTransaction({
onSuccess: () => {
@@ -87,15 +65,7 @@ const UpdateTeamFormContainer = ({
},
});
// refetch when saved
useEffect(() => {
if (refetch && status === 'confirmed') {
refetch();
setRefetching(true);
}
}, [refetch, status]);
if (loading && !refetching) {
if (loading) {
return <Loader size="small" />;
}
if (error) {
@@ -114,33 +84,6 @@ const UpdateTeamFormContainer = ({
return <Navigate to={Links.COMPETITIONS_TEAM(teamId)} />;
}
if (status === 'confirmed') {
return (
<div
className="flex flex-col items-start gap-2"
data-testid="team-creation-success-message"
>
<p className="text-sm">
<VegaIcon
name={VegaIconNames.TICK}
size={18}
className="text-vega-green-500"
/>{' '}
{t('Changes successfully saved to your team.')}
</p>
<TradingAnchorButton
href={Links.COMPETITIONS_TEAM(teamId)}
intent={Intent.Info}
size="small"
data-testid="view-team-button"
>
{t('View team')}
</TradingAnchorButton>
</div>
);
}
const defaultValues: FormFields = {
id: team.teamId,
name: team.name,
@@ -1,235 +0,0 @@
import { Route, Routes } from 'react-router-dom';
import {
Intent,
MobileActionsDropdown,
Tooltip,
TradingButton,
TradingDropdownItem,
VegaIcon,
VegaIconNames,
} from '@vegaprotocol/ui-toolkit';
import { type BarView, ViewType, useSidebar } from '../../components/sidebar';
import { useGetCurrentRouteId } from '../../lib/hooks/use-get-current-route-id';
import { useT } from '../../lib/use-t';
import { useScreenDimensions } from '@vegaprotocol/react-helpers';
import { useEffect } from 'react';
import classNames from 'classnames';
import { useVegaWallet, useVegaWalletDialogStore } from '@vegaprotocol/wallet';
const ViewInitializer = () => {
const currentRouteId = useGetCurrentRouteId();
const { setViews, getView } = useSidebar();
const view = getView(currentRouteId);
const { screenSize } = useScreenDimensions();
const largeScreen = ['lg', 'xl', 'xxl', 'xxxl'].includes(screenSize);
useEffect(() => {
if (largeScreen && view === undefined) {
setViews({ type: ViewType.Order }, currentRouteId);
}
}, [setViews, view, currentRouteId, largeScreen]);
return null;
};
export const MarketsMobileSidebar = () => {
const t = useT();
const currentRouteId = useGetCurrentRouteId();
const { pubKeys, isReadOnly } = useVegaWallet();
const openVegaWalletDialog = useVegaWalletDialogStore(
(store) => store.openVegaWalletDialog
);
return (
<Routes>
<Route
path=":marketId"
element={
<>
<ViewInitializer />
<div className="grid grid-cols-3 grow md:grow-0 md:flex lg:flex-col items-center gap-2 lg:gap-4 p-1">
{!pubKeys || isReadOnly ? (
<>
<TradingButton
intent={Intent.Primary}
size="medium"
onClick={() => {
openVegaWalletDialog();
}}
>
{t('Connect')}
</TradingButton>
<MobileButton
view={ViewType.Order}
tooltip={t('Trade')}
routeId={currentRouteId}
/>
<MobileBarActionsDropdown currentRouteId={currentRouteId} />
</>
) : (
<>
<MobileButton
view={ViewType.Order}
tooltip={t('Trade')}
routeId={currentRouteId}
/>
<MobileButton
view={ViewType.Deposit}
tooltip={t('Deposit')}
routeId={currentRouteId}
/>
<MobileBarActionsDropdown currentRouteId={currentRouteId} />
</>
)}
</div>
</>
}
/>
</Routes>
);
};
export const MobileButton = ({
view,
tooltip: label,
disabled = false,
onClick,
routeId,
}: {
view?: ViewType;
tooltip: string;
disabled?: boolean;
onClick?: () => void;
routeId: string;
}) => {
const { setViews, getView } = useSidebar((store) => ({
setViews: store.setViews,
getView: store.getView,
}));
const currView = getView(routeId);
const onSelect = (view: BarView['type']) => {
if (view === currView?.type) {
setViews(null, routeId);
} else {
setViews({ type: view }, routeId);
}
};
const buttonClasses = classNames(
'flex items-center p-1 rounded',
'disabled:cursor-not-allowed disabled:text-vega-clight-500 dark:disabled:text-vega-cdark-500',
{
'text-vega-clight-200 dark:text-vega-cdark-200 enabled:hover:bg-vega-clight-500 dark:enabled:hover:bg-vega-cdark-500':
!view || view !== currView?.type,
'bg-vega-yellow enabled:hover:bg-vega-yellow-550 text-black':
view && view === currView?.type,
}
);
return (
<Tooltip description={label} align="center" side="right" sideOffset={10}>
<TradingButton
className={buttonClasses}
data-testid={view}
onClick={onClick || (() => onSelect(view as BarView['type']))}
disabled={disabled}
>
{label}
</TradingButton>
</Tooltip>
);
};
export const MobileDropdownItem = ({
view,
icon,
tooltip,
disabled = false,
onClick,
routeId,
}: {
view?: ViewType;
icon: VegaIconNames;
tooltip: string;
disabled?: boolean;
onClick?: () => void;
routeId: string;
}) => {
const { setViews, getView } = useSidebar((store) => ({
setViews: store.setViews,
getView: store.getView,
}));
const currView = getView(routeId);
const onSelect = (view: BarView['type']) => {
if (view === currView?.type) {
setViews(null, routeId);
} else {
setViews({ type: view }, routeId);
}
};
const buttonClasses = classNames(
'flex items-center p-1 rounded',
'disabled:cursor-not-allowed disabled:text-vega-clight-500 dark:disabled:text-vega-cdark-500',
{
'text-vega-clight-200 dark:text-vega-cdark-200 enabled:hover:bg-vega-clight-500 dark:enabled:hover:bg-vega-cdark-500':
!view || view !== currView?.type,
'bg-vega-yellow enabled:hover:bg-vega-yellow-550 text-black':
view && view === currView?.type,
}
);
return (
<Tooltip description={tooltip} align="center" side="right" sideOffset={10}>
<TradingDropdownItem
className={buttonClasses}
data-testid={view}
onClick={onClick || (() => onSelect(view as BarView['type']))}
disabled={disabled}
>
<VegaIcon name={icon} size={20} />
{tooltip}
</TradingDropdownItem>
</Tooltip>
);
};
export const MobileBarActionsDropdown = ({
currentRouteId,
}: {
currentRouteId: string;
}) => {
const t = useT();
return (
<MobileActionsDropdown>
<MobileDropdownItem
view={ViewType.Deposit}
icon={VegaIconNames.DEPOSIT}
tooltip={t('Deposit')}
routeId={currentRouteId}
/>
<MobileDropdownItem
view={ViewType.Withdraw}
icon={VegaIconNames.WITHDRAW}
tooltip={t('Withdraw')}
routeId={currentRouteId}
/>
<MobileDropdownItem
view={ViewType.Transfer}
icon={VegaIconNames.TRANSFER}
tooltip={t('Transfer')}
routeId={currentRouteId}
/>
<MobileDropdownItem
view={ViewType.Info}
icon={VegaIconNames.BREAKDOWN}
tooltip={t('Market specification')}
routeId={currentRouteId}
/>
<MobileDropdownItem
view={ViewType.Settings}
icon={VegaIconNames.COG}
tooltip={t('Settings')}
routeId={currentRouteId}
/>
</MobileActionsDropdown>
);
};
@@ -2,7 +2,6 @@ import { VegaIconNames } from '@vegaprotocol/ui-toolkit';
import { SidebarButton, ViewType } from '../../components/sidebar';
import { useGetCurrentRouteId } from '../../lib/hooks/use-get-current-route-id';
import { useT } from '../../lib/use-t';
import { MobileButton } from '../markets/mobile-buttons';
export const PortfolioSidebar = () => {
const t = useT();
@@ -31,28 +30,3 @@ export const PortfolioSidebar = () => {
</>
);
};
export const PortfolioMobileSidebar = () => {
const t = useT();
const currentRouteId = useGetCurrentRouteId();
return (
<div className="grid grid-cols-3 grow md:grow-0 md:flex lg:flex-col items-center gap-2 lg:gap-4 p-1">
<MobileButton
view={ViewType.Deposit}
tooltip={t('Deposit')}
routeId={currentRouteId}
/>
<MobileButton
view={ViewType.Withdraw}
tooltip={t('Withdraw')}
routeId={currentRouteId}
/>
<MobileButton
view={ViewType.Transfer}
tooltip={t('Transfer')}
routeId={currentRouteId}
/>
</div>
);
};
@@ -1,4 +1,4 @@
import { formatNumber } from '@vegaprotocol/utils';
import { getNumberFormat } from '@vegaprotocol/utils';
import sortBy from 'lodash/sortBy';
import omit from 'lodash/omit';
import { useReferralProgramQuery } from './__generated__/CurrentReferralProgram';
@@ -107,7 +107,9 @@ export const useReferralProgram = () => {
discountFactor: Number(t.referralDiscountFactor),
discount: BigNumber(t.referralDiscountFactor).times(100).toFixed(2) + '%',
minimumVolume: Number(t.minimumRunningNotionalTakerVolume),
volume: formatNumber(t.minimumRunningNotionalTakerVolume, 0),
volume: getNumberFormat(0).format(
Number(t.minimumRunningNotionalTakerVolume)
),
epochs: Number(t.minimumEpochs),
};
});
@@ -14,9 +14,9 @@ import {
import { useVegaWallet } from '@vegaprotocol/wallet';
import {
addDecimalsFormatNumber,
formatNumber,
getDateFormat,
getDateTimeFormat,
getNumberFormat,
getUserLocale,
removePaginationWrapper,
} from '@vegaprotocol/utils';
@@ -323,7 +323,7 @@ export const Statistics = ({
}
description={<QUSDTooltip />}
>
{formatNumber(totalCommissionValue, 0)}
{getNumberFormat(0).format(Number(totalCommissionValue))}
</StatTile>
);
@@ -563,8 +563,8 @@ export const RefereesTable = ({
)
.map((r) => ({
...r,
volume: formatNumber(r.volume, 0),
commission: formatNumber(r.commission, 0),
volume: getNumberFormat(0).format(r.volume),
commission: getNumberFormat(0).format(r.commission),
}))
.reverse()}
/>
@@ -1,6 +1,6 @@
import { Link } from 'react-router-dom';
import { Splash } from '@vegaprotocol/ui-toolkit';
import { formatNumber } from '@vegaprotocol/utils';
import { getNumberFormat } from '@vegaprotocol/utils';
import { type useTeams } from '../../lib/hooks/use-teams';
import { useT } from '../../lib/use-t';
import { Table } from '../table';
@@ -15,7 +15,8 @@ export const CompetitionsLeaderboard = ({
}) => {
const t = useT();
const num = (n?: number | string) => (!n ? '-' : formatNumber(n, 0));
const num = (n?: number | string) =>
!n ? '-' : getNumberFormat(0).format(Number(n));
if (!data || data.length === 0) {
return <Splash>{t('Could not find any teams')}</Splash>;
@@ -32,9 +33,9 @@ export const CompetitionsLeaderboard = ({
{ name: 'status', displayName: t('Status') },
{ name: 'volume', displayName: t('Volume') },
]}
data={data.map((td) => {
data={data.map((td, i) => {
// leaderboard place or medal
let rank: number | React.ReactNode = td.rank;
let rank: number | React.ReactNode = i + 1;
if (rank === 1) rank = <Rank variant="gold" />;
if (rank === 2) rank = <Rank variant="silver" />;
if (rank === 3) rank = <Rank variant="bronze" />;
@@ -1,11 +1,6 @@
import { type TransferNode } from '@vegaprotocol/types';
import {
ActiveRewardCard,
isActiveReward,
} from '../rewards-container/active-rewards';
import { ActiveRewardCard } from '../rewards-container/active-rewards';
import { useT } from '../../lib/use-t';
import { useAssetsMapProvider } from '@vegaprotocol/assets';
import { useMarketsMapProvider } from '@vegaprotocol/markets';
export const GamesContainer = ({
data,
@@ -15,35 +10,8 @@ export const GamesContainer = ({
currentEpoch: number;
}) => {
const t = useT();
// Re-load markets and assets in the games container to ensure that the
// the cards are updated (not grayed out) when the user navigates to the games page
const { data: assets } = useAssetsMapProvider();
const { data: markets } = useMarketsMapProvider();
const enrichedTransfers = data
.filter((node) => isActiveReward(node, currentEpoch))
.map((node) => {
if (node.transfer.kind.__typename !== 'RecurringTransfer') {
return node;
}
const asset =
assets &&
assets[
node.transfer.kind.dispatchStrategy?.dispatchMetricAssetId || ''
];
const marketsInScope =
node.transfer.kind.dispatchStrategy?.marketIdsInScope?.map(
(id) => markets && markets[id]
);
return { ...node, asset, markets: marketsInScope };
});
if (!enrichedTransfers || !enrichedTransfers.length) return null;
if (!enrichedTransfers || enrichedTransfers.length === 0) {
if (!data || data.length === 0) {
return (
<p className="mb-6 text-muted">
{t('There are currently no games available.')}
@@ -53,7 +21,7 @@ export const GamesContainer = ({
return (
<div className="mb-12 grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
{enrichedTransfers.map((game, i) => {
{data.map((game, i) => {
// TODO: Remove `kind` prop from ActiveRewardCard
const { transfer } = game;
if (
@@ -1,6 +1,4 @@
import { isValidUrl } from '@vegaprotocol/utils';
import classNames from 'classnames';
import { useEffect, useState } from 'react';
const NUM_AVATARS = 20;
const AVATAR_PATHNAME_PATTERN = '/team-avatars/{id}.png';
@@ -13,26 +11,6 @@ export const getFallbackAvatar = (teamId: string) => {
return AVATAR_PATHNAME_PATTERN.replace('{id}', avatarId);
};
const useAvatar = (teamId: string, url: string) => {
const fallback = getFallbackAvatar(teamId);
const [avatar, setAvatar] = useState<string>(fallback);
useEffect(() => {
if (!isValidUrl(url)) return;
fetch(url, { cache: 'force-cache' })
.then((response) => {
if (response.ok) {
setAvatar(url);
}
})
.catch(() => {
/** noop */
});
});
return avatar;
};
export const TeamAvatar = ({
teamId,
imgUrl,
@@ -44,7 +22,7 @@ export const TeamAvatar = ({
alt?: string;
size?: 'large' | 'small';
}) => {
const img = useAvatar(teamId, imgUrl);
const img = imgUrl && imgUrl.length > 0 ? imgUrl : getFallbackAvatar(teamId);
return (
// eslint-disable-next-line @next/next/no-img-element
<img
@@ -16,7 +16,7 @@ export const LayoutWithSidebar = ({
const sidebarOpen = sidebarView !== null;
const gridClasses = classNames(
'h-full relative z-0 grid',
'grid-rows-[min-content_1fr_50px]',
'grid-rows-[min-content_1fr_40px]',
'lg:grid-rows-[min-content_1fr]',
'lg:grid-cols-[1fr_280px_40px]',
'xxxl:grid-cols-[1fr_320px_40px]'
@@ -468,7 +468,7 @@ export const ActiveRewardCard = ({
}
</div>
{dispatchStrategy?.dispatchMetric && (
<span className="text-muted text-sm h-[3rem]">
<span className="text-muted text-sm h-[2rem]">
{t(DispatchMetricDescription[dispatchStrategy?.dispatchMetric])}
</span>
)}
+25 -60
View File
@@ -17,7 +17,6 @@ import { useVegaWallet, useViewAsDialog } from '@vegaprotocol/wallet';
import { useGetCurrentRouteId } from '../../lib/hooks/use-get-current-route-id';
import { useT } from '../../lib/use-t';
import { ErrorBoundary } from '../error-boundary';
import { useScreenDimensions } from '@vegaprotocol/react-helpers';
export enum ViewType {
Order = 'Order',
@@ -27,10 +26,9 @@ export enum ViewType {
Transfer = 'Transfer',
Settings = 'Settings',
ViewAs = 'ViewAs',
Close = 'Close',
}
export type BarView =
type SidebarView =
| {
type: ViewType.Deposit;
assetId?: string;
@@ -51,9 +49,6 @@ export type BarView =
}
| {
type: ViewType.Settings;
}
| {
type: ViewType.Close;
};
export const Sidebar = ({ options }: { options?: ReactNode }) => {
@@ -62,52 +57,26 @@ export const Sidebar = ({ options }: { options?: ReactNode }) => {
const navClasses = 'flex lg:flex-col items-center gap-2 lg:gap-4 p-1';
const setViewAsDialogOpen = useViewAsDialog((state) => state.setOpen);
const { pubKeys } = useVegaWallet();
const { isMobile } = useScreenDimensions();
const { getView } = useSidebar((store) => ({
setViews: store.setViews,
getView: store.getView,
}));
const currView = getView(currentRouteId);
return (
<div className="flex h-full lg:flex-col gap-1" data-testid="sidebar">
{options && (
<nav className={classNames(navClasses, 'flex grow')}>{options}</nav>
)}
<nav
className={classNames(
navClasses,
'ml-auto lg:mt-auto lg:ml-0 shrink-0'
)}
>
{!isMobile ? (
<>
<SidebarButton
view={ViewType.ViewAs}
onClick={() => {
setViewAsDialogOpen(true);
}}
icon={VegaIconNames.EYE}
tooltip={t('View as party')}
disabled={Boolean(pubKeys)}
routeId={currentRouteId}
/>
<SidebarButton
view={ViewType.Settings}
icon={VegaIconNames.COG}
tooltip={t('Settings')}
routeId={currentRouteId}
/>
</>
) : (
currView && (
<SidebarButton
view={ViewType.Close}
icon={VegaIconNames.ARROW_LEFT}
tooltip={t('Back')}
routeId={currentRouteId}
/>
)
)}
<div className="flex h-full p-1 lg:flex-col gap-2" data-testid="sidebar">
{options && <nav className={navClasses}>{options}</nav>}
<nav className={classNames(navClasses, 'ml-auto lg:mt-auto lg:ml-0')}>
<SidebarButton
view={ViewType.ViewAs}
onClick={() => {
setViewAsDialogOpen(true);
}}
icon={VegaIconNames.EYE}
tooltip={t('View as party')}
disabled={Boolean(pubKeys)}
routeId={currentRouteId}
/>
<SidebarButton
view={ViewType.Settings}
icon={VegaIconNames.COG}
tooltip={t('Settings')}
routeId={currentRouteId}
/>
<NodeHealthContainer />
</nav>
</div>
@@ -134,7 +103,7 @@ export const SidebarButton = ({
getView: store.getView,
}));
const currView = getView(routeId);
const onSelect = (view: BarView['type']) => {
const onSelect = (view: SidebarView['type']) => {
if (view === currView?.type) {
setViews(null, routeId);
} else {
@@ -164,7 +133,7 @@ export const SidebarButton = ({
<button
className={buttonClasses}
data-testid={view}
onClick={onClick || (() => onSelect(view as BarView['type']))}
onClick={onClick || (() => onSelect(view as SidebarView['type']))}
disabled={disabled}
>
<VegaIcon name={icon} size={20} />
@@ -211,10 +180,6 @@ export const SidebarContent = () => {
}
}
if (view.type === ViewType.Close) {
return <CloseSidebar />;
}
if (view.type === ViewType.Info) {
if (params.marketId) {
return (
@@ -302,9 +267,9 @@ const CloseSidebar = () => {
};
export const useSidebar = create<{
views: { [key: string]: BarView | null };
setViews: (view: BarView | null, routeId: string) => void;
getView: (routeId: string) => BarView | null | undefined;
views: { [key: string]: SidebarView | null };
setViews: (view: SidebarView | null, routeId: string) => void;
getView: (routeId: string) => SidebarView | null | undefined;
}>()((set, get) => ({
views: {},
setViews: (x, routeId) =>
+1 -1
View File
@@ -288,7 +288,7 @@ def test_game_card(competitions_page: Page):
expect(game_1.get_by_test_id("distribution-strategy")
).to_have_text("Pro rata")
expect(game_1.get_by_test_id("dispatch-metric-info")
).to_have_text("Price maker fees paid • tDAI")
).to_have_text("Price maker fees paid • ")
expect(game_1.get_by_test_id("assessed-over")).to_have_text("15 epochs")
expect(game_1.get_by_test_id("scope")).to_have_text("In team")
expect(game_1.get_by_test_id("staking-requirement")).to_have_text("0.00")
+1 -3
View File
@@ -36,9 +36,7 @@ export const useTeams = (aggregationEpochs = DEFAULT_AGGREGATION_EPOCHS) => {
...stats.find((s) => s.teamId === t.teamId),
}));
return orderBy(data, (d) => Number(d.totalQuantumRewards || 0), 'desc').map(
(d, i) => ({ ...d, rank: i + 1 })
);
return orderBy(data, (d) => Number(d.totalQuantumRewards || 0), 'desc');
}, [teams, stats]);
return {
+8 -23
View File
@@ -24,10 +24,7 @@ import { compact } from 'lodash';
import { useFeatureFlags } from '@vegaprotocol/environment';
import { LiquidityHeader } from '../components/liquidity-header';
import { MarketHeader, MobileMarketHeader } from '../components/market-header';
import {
PortfolioMobileSidebar,
PortfolioSidebar,
} from '../client-pages/portfolio/portfolio-sidebar';
import { PortfolioSidebar } from '../client-pages/portfolio/portfolio-sidebar';
import { LiquiditySidebar } from '../client-pages/liquidity/liquidity-sidebar';
import { MarketsSidebar } from '../client-pages/markets/markets-sidebar';
import { useT } from '../lib/use-t';
@@ -36,10 +33,9 @@ import { CompetitionsTeams } from '../client-pages/competitions/competitions-tea
import { CompetitionsTeam } from '../client-pages/competitions/competitions-team';
import { CompetitionsCreateTeam } from '../client-pages/competitions/competitions-create-team';
import { CompetitionsUpdateTeam } from '../client-pages/competitions/competitions-update-team';
import { MarketsMobileSidebar } from '../client-pages/markets/mobile-buttons';
import { useScreenDimensions } from '@vegaprotocol/react-helpers';
// These must remain dynamically imported as pennant cannot be compiled by Next.js due to ESM
// These must remain dynamically imported as pennant cannot be compiled by nextjs due to ESM
// Using dynamic imports is a workaround for this until pennant is published as ESM
const MarketPage = lazy(() => import('../client-pages/market'));
const Portfolio = lazy(() => import('../client-pages/portfolio'));
@@ -58,17 +54,6 @@ export const useRouterConfig = (): RouteObject[] => {
const { screenSize } = useScreenDimensions();
const largeScreen = ['lg', 'xl', 'xxl', 'xxxl'].includes(screenSize);
const marketHeader = largeScreen ? <MarketHeader /> : <MobileMarketHeader />;
const marketsSidebar = largeScreen ? (
<MarketsSidebar />
) : (
<MarketsMobileSidebar />
);
const portfolioSidebar = largeScreen ? (
<PortfolioSidebar />
) : (
<PortfolioMobileSidebar />
);
const routeConfig = compact([
{
index: true,
@@ -85,7 +70,7 @@ export const useRouterConfig = (): RouteObject[] => {
featureFlags.REFERRALS
? {
path: AppRoutes.REFERRALS,
element: <LayoutWithSidebar sidebar={portfolioSidebar} />,
element: <LayoutWithSidebar sidebar={<PortfolioSidebar />} />,
children: [
{
element: (
@@ -118,7 +103,7 @@ export const useRouterConfig = (): RouteObject[] => {
featureFlags.TEAM_COMPETITION
? {
path: AppRoutes.COMPETITIONS,
element: <LayoutWithSidebar sidebar={portfolioSidebar} />,
element: <LayoutWithSidebar sidebar={<PortfolioSidebar />} />,
children: [
// pages with planets and stars
{
@@ -149,7 +134,7 @@ export const useRouterConfig = (): RouteObject[] => {
: undefined,
{
path: 'fees/*',
element: <LayoutWithSidebar sidebar={portfolioSidebar} />,
element: <LayoutWithSidebar sidebar={<PortfolioSidebar />} />,
children: [
{
index: true,
@@ -159,7 +144,7 @@ export const useRouterConfig = (): RouteObject[] => {
},
{
path: 'rewards/*',
element: <LayoutWithSidebar sidebar={portfolioSidebar} />,
element: <LayoutWithSidebar sidebar={<PortfolioSidebar />} />,
children: [
{
index: true,
@@ -170,7 +155,7 @@ export const useRouterConfig = (): RouteObject[] => {
{
path: 'markets/*',
element: (
<LayoutWithSidebar header={marketHeader} sidebar={marketsSidebar} />
<LayoutWithSidebar header={marketHeader} sidebar={<MarketsSidebar />} />
),
children: [
{
@@ -191,7 +176,7 @@ export const useRouterConfig = (): RouteObject[] => {
},
{
path: 'portfolio/*',
element: <LayoutWithSidebar sidebar={portfolioSidebar} />,
element: <LayoutWithSidebar sidebar={<PortfolioSidebar />} />,
children: [
{
index: true,
-18
View File
@@ -1,18 +0,0 @@
query TransferFee(
$fromAccount: ID!
$fromAccountType: AccountType!
$toAccount: ID!
$amount: String!
$assetId: String!
) {
estimateTransferFee(
fromAccount: $fromAccount
fromAccountType: $fromAccountType
toAccount: $toAccount
amount: $amount
assetId: $assetId
) {
fee
discount
}
}
-63
View File
@@ -1,63 +0,0 @@
import * as Types from '@vegaprotocol/types';
import { gql } from '@apollo/client';
import * as Apollo from '@apollo/client';
const defaultOptions = {} as const;
export type TransferFeeQueryVariables = Types.Exact<{
fromAccount: Types.Scalars['ID'];
fromAccountType: Types.AccountType;
toAccount: Types.Scalars['ID'];
amount: Types.Scalars['String'];
assetId: Types.Scalars['String'];
}>;
export type TransferFeeQuery = { __typename?: 'Query', estimateTransferFee?: { __typename?: 'EstimatedTransferFee', fee: string, discount: string } | null };
export const TransferFeeDocument = gql`
query TransferFee($fromAccount: ID!, $fromAccountType: AccountType!, $toAccount: ID!, $amount: String!, $assetId: String!) {
estimateTransferFee(
fromAccount: $fromAccount
fromAccountType: $fromAccountType
toAccount: $toAccount
amount: $amount
assetId: $assetId
) {
fee
discount
}
}
`;
/**
* __useTransferFeeQuery__
*
* To run a query within a React component, call `useTransferFeeQuery` and pass it any options that fit your needs.
* When your component renders, `useTransferFeeQuery` returns an object from Apollo Client that contains loading, error, and data properties
* you can use to render your UI.
*
* @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
*
* @example
* const { data, loading, error } = useTransferFeeQuery({
* variables: {
* fromAccount: // value for 'fromAccount'
* fromAccountType: // value for 'fromAccountType'
* toAccount: // value for 'toAccount'
* amount: // value for 'amount'
* assetId: // value for 'assetId'
* },
* });
*/
export function useTransferFeeQuery(baseOptions: Apollo.QueryHookOptions<TransferFeeQuery, TransferFeeQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useQuery<TransferFeeQuery, TransferFeeQueryVariables>(TransferFeeDocument, options);
}
export function useTransferFeeLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<TransferFeeQuery, TransferFeeQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useLazyQuery<TransferFeeQuery, TransferFeeQueryVariables>(TransferFeeDocument, options);
}
export type TransferFeeQueryHookResult = ReturnType<typeof useTransferFeeQuery>;
export type TransferFeeLazyQueryHookResult = ReturnType<typeof useTransferFeeLazyQuery>;
export type TransferFeeQueryResult = Apollo.QueryResult<TransferFeeQuery, TransferFeeQueryVariables>;
@@ -25,6 +25,7 @@ export const TransferContainer = ({ assetId }: { assetId?: string }) => {
const t = useT();
const { pubKey, pubKeys, isReadOnly } = useVegaWallet();
const { params } = useNetworkParams([
NetworkParams.transfer_fee_factor,
NetworkParams.transfer_minTransferQuantumMultiple,
]);
@@ -71,6 +72,7 @@ export const TransferContainer = ({ assetId }: { assetId?: string }) => {
pubKeys={pubKeys ? pubKeys?.map((pk) => pk.publicKey) : null}
isReadOnly={isReadOnly}
assetId={assetId}
feeFactor={params.transfer_fee_factor}
minQuantumMultiple={params.transfer_minTransferQuantumMultiple}
submitTransfer={transfer}
accounts={sortedAccounts}
+60 -79
View File
@@ -15,30 +15,6 @@ import {
} from './transfer-form';
import { AccountType, AccountTypeMapping } from '@vegaprotocol/types';
import { removeDecimal } from '@vegaprotocol/utils';
import type { TransferFeeQuery } from './__generated__/TransferFee';
const feeFactor = 0.001;
const mockUseTransferFeeQuery = jest.fn(
({
variables: { amount },
}: {
variables: { amount: string };
}): { data: TransferFeeQuery } => {
return {
data: {
estimateTransferFee: {
discount: '0',
fee: (Number(amount) * feeFactor).toFixed(),
},
},
};
}
);
jest.mock('./__generated__/TransferFee', () => ({
useTransferFeeQuery: (props: { variables: { amount: string } }) =>
mockUseTransferFeeQuery(props),
}));
describe('TransferForm', () => {
const renderComponent = (props: TransferFormProps) => {
@@ -80,6 +56,7 @@ describe('TransferForm', () => {
const props = {
pubKey,
pubKeys: [pubKey, '2'.repeat(64)],
feeFactor: '0.001',
submitTransfer: jest.fn(),
accounts: [
{
@@ -102,6 +79,7 @@ describe('TransferForm', () => {
pubKey,
'a4b6e3de5d7ef4e31ae1b090be49d1a2ef7bcefff60cccf7658a0d4922651cce',
],
feeFactor: '0.001',
submitTransfer: jest.fn(),
accounts: [],
minQuantumMultiple: '1',
@@ -118,6 +96,10 @@ describe('TransferForm', () => {
});
it.each([
{
targetText: 'Transfer fee',
tooltipText: /transfer\.fee\.factor/,
},
{
targetText: 'Amount to be transferred',
tooltipText: /without the fee/,
@@ -127,6 +109,9 @@ describe('TransferForm', () => {
tooltipText: /total amount taken from your account/,
},
])('Tooltip for "$targetText" shows', async (o) => {
// 1003-TRAN-015
// 1003-TRAN-016
// 1003-TRAN-017
// 1003-TRAN-018
// 1003-TRAN-019
renderComponent(props);
@@ -139,10 +124,6 @@ describe('TransferForm', () => {
// Select asset
await selectAsset(asset);
await userEvent.selectOptions(
screen.getByLabelText('From account'),
`${AccountType.ACCOUNT_TYPE_GENERAL}-${asset.id}`
);
// set valid amount
const amountInput = screen.getByLabelText('Amount');
await userEvent.type(amountInput, amount);
@@ -233,7 +214,9 @@ describe('TransferForm', () => {
// set valid amount
await userEvent.clear(amountInput);
await userEvent.type(amountInput, amount);
expect(screen.getByTestId('transfer-fee')).toHaveTextContent('1');
expect(screen.getByTestId('transfer-fee')).toHaveTextContent(
new BigNumber(props.feeFactor).times(amount).toFixed()
);
await submit();
@@ -402,44 +385,47 @@ describe('TransferForm', () => {
});
});
});
describe('IncludeFeesCheckbox', () => {
it('validates fields when checkbox is not checked', async () => {
renderComponent(props);
it('validates fields', async () => {
renderComponent(props);
// check current pubkey not shown
const keySelect: HTMLSelectElement = screen.getByLabelText('To Vega key');
const pubKeyOptions = ['', pubKey, props.pubKeys[1]];
expect(keySelect.children).toHaveLength(pubKeyOptions.length);
expect(Array.from(keySelect.options).map((o) => o.value)).toEqual(
pubKeyOptions
);
// check current pubkey not shown
const keySelect: HTMLSelectElement = screen.getByLabelText('To Vega key');
const pubKeyOptions = ['', pubKey, props.pubKeys[1]];
expect(keySelect.children).toHaveLength(pubKeyOptions.length);
expect(Array.from(keySelect.options).map((o) => o.value)).toEqual(
pubKeyOptions
);
await submit();
expect(await screen.findAllByText('Required')).toHaveLength(2); // pubkey set as default value
await submit();
expect(await screen.findAllByText('Required')).toHaveLength(2); // pubkey set as default value
// Select a pubkey
await userEvent.selectOptions(
screen.getByLabelText('To Vega key'),
props.pubKeys[1]
);
// Select a pubkey
await userEvent.selectOptions(
screen.getByLabelText('To Vega key'),
props.pubKeys[1]
);
// Select asset
await selectAsset(asset);
// Select asset
await selectAsset(asset);
await userEvent.selectOptions(
screen.getByLabelText('From account'),
`${AccountType.ACCOUNT_TYPE_GENERAL}-${asset.id}`
);
await userEvent.selectOptions(
screen.getByLabelText('From account'),
`${AccountType.ACCOUNT_TYPE_GENERAL}-${asset.id}`
);
const amountInput = screen.getByLabelText('Amount');
const amountInput = screen.getByLabelText('Amount');
await userEvent.type(amountInput, amount);
const expectedFee = new BigNumber(amount).times(feeFactor).toFixed();
const total = new BigNumber(amount).plus(expectedFee).toFixed();
// 1003-TRAN-021
expect(screen.getByTestId('transfer-fee')).toHaveTextContent(expectedFee);
expect(screen.getByTestId('transfer-amount')).toHaveTextContent(amount);
expect(screen.getByTestId('total-transfer-fee')).toHaveTextContent(total);
await userEvent.type(amountInput, amount);
const expectedFee = new BigNumber(amount)
.times(props.feeFactor)
.toFixed();
const total = new BigNumber(amount).plus(expectedFee).toFixed();
// 1003-TRAN-021
expect(screen.getByTestId('transfer-fee')).toHaveTextContent(expectedFee);
expect(screen.getByTestId('transfer-amount')).toHaveTextContent(amount);
expect(screen.getByTestId('total-transfer-fee')).toHaveTextContent(total);
});
});
describe('AddressField', () => {
@@ -471,29 +457,24 @@ describe('TransferForm', () => {
describe('TransferFee', () => {
const props = {
amount: '20000',
discount: '0',
fee: '20',
decimals: 2,
amount: '200',
feeFactor: '0.001',
fee: '0.2',
transferAmount: '200',
decimals: 8,
};
it('calculates and renders amounts and fee', () => {
it('calculates and renders the transfer fee', () => {
render(<TransferFee {...props} />);
expect(screen.queryByTestId('discount')).not.toBeInTheDocument();
expect(screen.getByTestId('transfer-fee')).toHaveTextContent('0.2');
expect(screen.getByTestId('transfer-amount')).toHaveTextContent('200.00');
expect(screen.getByTestId('total-transfer-fee')).toHaveTextContent(
'200.20'
);
});
it('calculates and renders amounts, fee and discount', () => {
render(<TransferFee {...props} discount="10" />);
expect(screen.getByTestId('discount')).toHaveTextContent('0.1');
expect(screen.getByTestId('transfer-fee')).toHaveTextContent('0.2');
expect(screen.getByTestId('transfer-amount')).toHaveTextContent('200.00');
expect(screen.getByTestId('total-transfer-fee')).toHaveTextContent(
'200.10'
const expected = new BigNumber(props.amount)
.times(props.feeFactor)
.toFixed();
const total = new BigNumber(props.amount).plus(expected).toFixed();
expect(screen.getByTestId('transfer-fee')).toHaveTextContent(expected);
expect(screen.getByTestId('transfer-amount')).toHaveTextContent(
props.amount
);
expect(screen.getByTestId('total-transfer-fee')).toHaveTextContent(total);
});
});
});
+31 -49
View File
@@ -4,9 +4,8 @@ import {
useRequired,
useVegaPublicKey,
addDecimal,
formatNumber,
toBigNum,
removeDecimal,
addDecimalsFormatNumber,
} from '@vegaprotocol/utils';
import { useT } from './use-t';
import {
@@ -22,11 +21,10 @@ import type { Transfer } from '@vegaprotocol/wallet';
import { normalizeTransfer } from '@vegaprotocol/wallet';
import BigNumber from 'bignumber.js';
import type { ReactNode } from 'react';
import { useCallback, useEffect, useState } from 'react';
import { useCallback, useEffect, useMemo, useState } from 'react';
import { Controller, useForm } from 'react-hook-form';
import { AssetOption, Balance } from '@vegaprotocol/assets';
import { AccountType, AccountTypeMapping } from '@vegaprotocol/types';
import { useTransferFeeQuery } from './__generated__/TransferFee';
interface FormFields {
toVegaKey: string;
@@ -53,6 +51,7 @@ export interface TransferFormProps {
asset: Asset;
}>;
assetId?: string;
feeFactor: string | null;
minQuantumMultiple: string | null;
submitTransfer: (transfer: Transfer) => void;
}
@@ -62,6 +61,7 @@ export const TransferForm = ({
pubKeys,
isReadOnly,
assetId: initialAssetId,
feeFactor,
submitTransfer,
accounts,
minQuantumMultiple,
@@ -136,22 +136,11 @@ export const TransferForm = ({
// Max amount given selected asset and from account
const max = accountBalance ? new BigNumber(accountBalance) : new BigNumber(0);
const normalizedAmount =
(amount && asset && removeDecimal(amount, asset.decimals)) || '0';
const transferFeeQuery = useTransferFeeQuery({
variables: {
fromAccount: pubKey || '',
fromAccountType: accountType || AccountType.ACCOUNT_TYPE_GENERAL,
amount: normalizedAmount,
assetId: asset?.id || '',
toAccount: selectedPubKey,
},
skip: !pubKey || !amount || !asset || !selectedPubKey || fromVested,
});
const transferFee = transferFeeQuery.loading
? transferFeeQuery.data || transferFeeQuery.previousData
: transferFeeQuery.data;
const fee = useMemo(
() => feeFactor && new BigNumber(feeFactor).times(amount).toString(),
[amount, feeFactor]
);
const onSubmit = useCallback(
(fields: FormFields) => {
@@ -443,14 +432,12 @@ export const TransferForm = ({
</TradingInputError>
)}
</TradingFormGroup>
{(transferFee?.estimateTransferFee || fromVested) && amount && asset && (
{amount && fee && (
<TransferFee
amount={normalizedAmount}
fee={fromVested ? '0' : transferFee?.estimateTransferFee?.fee}
discount={
fromVested ? '0' : transferFee?.estimateTransferFee?.discount
}
decimals={asset.decimals}
amount={amount}
feeFactor={feeFactor}
fee={fromVested ? '0' : fee}
decimals={asset?.decimals}
/>
)}
<TradingButton type="submit" fill={true} disabled={isReadOnly}>
@@ -462,44 +449,39 @@ export const TransferForm = ({
export const TransferFee = ({
amount,
feeFactor,
fee,
discount,
decimals,
}: {
amount: string;
feeFactor: string | null;
fee?: string;
discount?: string;
decimals: number;
decimals?: number;
}) => {
const t = useT();
if (!amount || !fee) return null;
if (isNaN(Number(amount)) || isNaN(Number(fee))) {
if (!feeFactor || !amount || !fee) return null;
if (isNaN(Number(feeFactor)) || isNaN(Number(amount)) || isNaN(Number(fee))) {
return null;
}
const totalValue = (
BigInt(amount) +
BigInt(fee) -
BigInt(discount || '0')
).toString();
const totalValue = new BigNumber(amount).plus(fee).toString();
return (
<div className="mb-4 flex flex-col gap-2 text-xs">
<div className="flex flex-wrap items-center justify-between gap-1">
<div>{t('Transfer fee')}</div>
<Tooltip
description={t(
`The transfer fee is set by the network parameter transfer.fee.factor, currently set to {{feeFactor}}`,
{ feeFactor }
)}
>
<div>{t('Transfer fee')}</div>
</Tooltip>
<div data-testid="transfer-fee" className="text-muted">
{addDecimalsFormatNumber(fee, decimals)}
{formatNumber(fee, decimals)}
</div>
</div>
{discount && discount !== '0' && (
<div className="flex flex-wrap items-center justify-between gap-1">
<div>{t('Discount')}</div>
<div data-testid="discount" className="text-muted">
{addDecimalsFormatNumber(discount, decimals)}
</div>
</div>
)}
<div className="flex flex-wrap items-center justify-between gap-1">
<Tooltip
description={t(
@@ -510,7 +492,7 @@ export const TransferFee = ({
</Tooltip>
<div data-testid="transfer-amount" className="text-muted">
{addDecimalsFormatNumber(amount, decimals)}
{formatNumber(amount, decimals)}
</div>
</div>
<div className="flex flex-wrap items-center justify-between gap-1">
@@ -523,7 +505,7 @@ export const TransferFee = ({
</Tooltip>
<div data-testid="total-transfer-fee" className="text-muted">
{addDecimalsFormatNumber(totalValue, decimals)}
{formatNumber(totalValue, decimals)}
</div>
</div>
</div>
-1
View File
@@ -86,7 +86,6 @@ export const DocsLinks = VEGA_DOCS_URL
POST_REDUCE_ONLY: `${VEGA_DOCS_URL}/concepts/trading-on-vega/orders#conditional-order-parameters`,
QUANTUM: `${VEGA_DOCS_URL}/concepts/assets/asset-framework#quantum`,
REFERRALS: `${VEGA_DOCS_URL}/tutorials/proposals/referral-program-proposal`,
LIQUIDITY_FEE_PERCENTAGE: `${VEGA_DOCS_URL}/concepts/liquidity/rewards-penalties#determining-the-liquidity-fee-percentage`,
}
: undefined;
+1
View File
@@ -35,6 +35,7 @@
"The total amount of each asset on this key. Includes used and available collateral.": "The total amount of each asset on this key. Includes used and available collateral.",
"The total amount taken from your account. The amount to be transferred plus the fee.": "The total amount taken from your account. The amount to be transferred plus the fee.",
"The total amount to be transferred (without the fee)": "The total amount to be transferred (without the fee)",
"The transfer fee is set by the network parameter transfer.fee.factor, currently set to {{feeFactor}}": "The transfer fee is set by the network parameter transfer.fee.factor, currently set to {{feeFactor}}",
"To account": "To account",
"To Vega key": "To Vega key",
"Total": "Total",
+1 -6
View File
@@ -110,7 +110,6 @@
"Fills": "Fills",
"Final commission rate": "Final commission rate",
"Find out more": "Find out more",
"For more info, visit the documentation": "For more info, visit the documentation",
"Free from the risks of real trading, Fairground is a safe and fun place to try out Vega yourself with virtual assets.": "Free from the risks of real trading, Fairground is a safe and fun place to try out Vega yourself with virtual assets.",
"From epoch": "From epoch",
"Fully decentralised high performance peer-to-network trading.": "Fully decentralised high performance peer-to-network trading.",
@@ -447,9 +446,5 @@
"Choose a team": "Choose a team",
"Join a team": "Join a team",
"Solo team / lone wolf": "Solo team / lone wolf",
"Choose a team to get involved": "Choose a team to get involved",
"Go back to the team's profile": "Go back to the team's profile",
"Go back to the competitions": "Go back to the competitions",
"Your team ID:": "Your team ID:",
"Changes successfully saved to your team.": "Changes successfully saved to your team."
"Choose a team to get involved": "Choose a team to get involved"
}
+6 -6
View File
@@ -94,7 +94,7 @@ export const LiquidityTable = ({
return `${addDecimalsFormatNumberQuantum(
value,
assetDecimalPlaces ?? 0,
quantum ?? 1
quantum ?? 0
)}`;
};
@@ -165,7 +165,7 @@ export const LiquidityTable = ({
return `${addDecimalsFormatNumberQuantum(
newValue,
assetDecimalPlaces ?? 0,
quantum ?? 1
quantum ?? 0
)}`;
};
@@ -227,7 +227,7 @@ export const LiquidityTable = ({
addDecimalsFormatNumberQuantum(
pendingCommitmentAmount,
assetDecimalPlaces ?? 0,
quantum ?? 1
quantum ?? 0
);
if (
@@ -238,7 +238,7 @@ export const LiquidityTable = ({
addDecimalsFormatNumberQuantum(
currentCommitmentAmount,
assetDecimalPlaces ?? 0,
quantum ?? 1
quantum ?? 0
);
return (
@@ -286,7 +286,7 @@ export const LiquidityTable = ({
addDecimalsFormatNumberQuantum(
pendingCommitmentAmount,
assetDecimalPlaces ?? 0,
quantum ?? 1
quantum ?? 0
);
if (
@@ -297,7 +297,7 @@ export const LiquidityTable = ({
addDecimalsFormatNumberQuantum(
currentCommitmentAmount,
assetDecimalPlaces ?? 0,
quantum ?? 1
quantum ?? 0
);
return (
+4 -3
View File
@@ -1,7 +1,7 @@
import { DepthChart } from 'pennant';
import throttle from 'lodash/throttle';
import { AsyncRenderer } from '@vegaprotocol/ui-toolkit';
import { addDecimal, formatNumber } from '@vegaprotocol/utils';
import { addDecimal, getNumberFormat } from '@vegaprotocol/utils';
import { useThemeSwitcher } from '@vegaprotocol/react-helpers';
import { useDataProvider } from '@vegaprotocol/data-provider';
import { marketDepthProvider } from './market-depth-provider';
@@ -216,12 +216,13 @@ export const DepthChartContainer = ({ marketId }: DepthChartManagerProps) => {
const volumeFormat = useCallback(
(volume: number) =>
formatNumber(volume, market?.positionDecimalPlaces || 0),
getNumberFormat(market?.positionDecimalPlaces || 0).format(volume),
[market?.positionDecimalPlaces]
);
const priceFormat = useCallback(
(price: number) => formatNumber(price, market?.decimalPlaces || 0),
(price: number) =>
getNumberFormat(market?.decimalPlaces || 0).format(price),
[market?.decimalPlaces]
);
File diff suppressed because one or more lines are too long
@@ -171,10 +171,6 @@ query MarketInfo($marketId: ID!) {
infrastructureFee
liquidityFee
}
liquidityFeeSettings {
feeConstant
method
}
}
priceMonitoringSettings {
parameters {
File diff suppressed because one or more lines are too long
@@ -28,7 +28,6 @@ import {
InsurancePoolInfoPanel,
KeyDetailsInfoPanel,
LiquidationStrategyInfoPanel,
LiquidityFeesSettings,
LiquidityInfoPanel,
LiquidityMonitoringParametersInfoPanel,
LiquidityPriceRangeInfoPanel,
@@ -301,11 +300,6 @@ export const MarketInfoAccordion = ({
}
content={<LiquiditySLAParametersInfoPanel market={market} />}
/>
<AccordionItem
itemId="lp-fee-settings"
title={t('Liquidity fee settings')}
content={<LiquidityFeesSettings market={market} />}
/>
<AccordionItem
itemId="liquidity"
title={t('Liquidity')}
@@ -44,8 +44,6 @@ import type {
} from '@vegaprotocol/types';
import {
ConditionOperatorMapping,
LiquidityFeeMethodMapping,
LiquidityFeeMethodMappingDescription,
MarketStateMapping,
MarketTradingModeMapping,
} from '@vegaprotocol/types';
@@ -56,7 +54,6 @@ import {
TOKEN_PROPOSAL,
useEnvironment,
useLinks,
DocsLinks,
} from '@vegaprotocol/environment';
import type { Provider } from '../../oracle-schema';
import { OracleBasicProfile } from '../../components/oracle-basic-profile';
@@ -113,44 +110,6 @@ export const CurrentFeesInfoPanel = ({ market }: MarketInfoProps) => {
);
};
export const LiquidityFeesSettings = ({ market }: MarketInfoProps) => {
const t = useT();
return (
<>
<MarketInfoTable
data={{
feeConstant: market.fees.liquidityFeeSettings?.feeConstant,
method: market.fees.liquidityFeeSettings && (
<Tooltip
description={
LiquidityFeeMethodMappingDescription[
market.fees.liquidityFeeSettings?.method
]
}
>
<span>
{
LiquidityFeeMethodMapping[
market.fees.liquidityFeeSettings?.method
]
}
</span>
</Tooltip>
),
}}
/>
<p className="text-xs">
<ExternalLink
href={DocsLinks?.LIQUIDITY_FEE_PERCENTAGE}
className="mt-2"
>
{t('Fore more info, visit the documentation')}
</ExternalLink>
</p>
</>
);
};
export const MarketPriceInfoPanel = ({ market }: MarketInfoProps) => {
const t = useT();
const assetSymbol = getAsset(market).symbol;
@@ -16,6 +16,7 @@ export const useTooltipMapping: () => Record<string, ReactNode> = () => {
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.'
),
@@ -153,9 +154,5 @@ export const useTooltipMapping: () => Record<string, ReactNode> = () => {
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.'
),
method: t(`The method used to calculate the market's liquidity fee.`),
feeConstant: t(
'The constant liquidity fee used when using the constant fee method .'
),
};
};
@@ -240,7 +240,7 @@ export const OracleFullProfile = ({
<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-2">{t('Specifications')}</div>
<div className="col-span-1">{t('Specifications')}</div>
</div>
<div className="max-h-60 overflow-auto">
{oracleMarkets?.map((market) => (
-4
View File
@@ -12,10 +12,6 @@ fragment MarketFields on Market {
infrastructureFee
liquidityFee
}
liquidityFeeSettings {
feeConstant
method
}
}
tradableInstrument {
instrument {
-5
View File
@@ -52,11 +52,6 @@ export const createMarketFragment = (
infrastructureFee: '',
liquidityFee: '',
},
liquidityFeeSettings: {
__typename: 'LiquidityFeeSettings',
method: Schema.LiquidityFeeMethod.METHOD_MARGINAL_COST,
feeConstant: '',
},
},
tradableInstrument: {
instrument: {
+6 -58
View File
@@ -14,32 +14,6 @@ export type Scalars = {
Timestamp: any;
};
/** Margins for a hypothetical position not related to any existing party */
export type AbstractMarginLevels = {
__typename?: 'AbstractMarginLevels';
/** Asset for the current margins */
asset: Asset;
/**
* If the margin of the party is greater than this level, then collateral will be released from the margin account into
* the general account of the party for the given asset.
*/
collateralReleaseLevel: Scalars['String'];
/** This is the minimum margin required for a party to place a new order on the network, expressed as unsigned integer */
initialLevel: Scalars['String'];
/** Minimal margin for the position to be maintained in the network (unsigned integer) */
maintenanceLevel: Scalars['String'];
/** Margin factor, only relevant for isolated margin mode, else 0 */
marginFactor: Scalars['String'];
/** Margin mode of the party, cross margin or isolated margin */
marginMode: MarginMode;
/** Market in which the margin is required for this party */
market: Market;
/** When in isolated margin, the required order margin level, otherwise, 0 */
orderMarginLevel: Scalars['String'];
/** If the margin is between maintenance and search, the network will initiate a collateral search, expressed as unsigned integer */
searchLevel: Scalars['String'];
};
/** An account record */
export type AccountBalance = {
__typename?: 'AccountBalance';
@@ -385,8 +359,6 @@ export enum AuctionTrigger {
export type BatchProposal = {
__typename?: 'BatchProposal';
/** Terms of all the proposals in the batch */
batchTerms?: Maybe<BatchProposalTerms>;
/** RFC3339Nano time and date when the proposal reached the network */
datetime: Scalars['Timestamp'];
/** Details of the rejection reason */
@@ -417,10 +389,10 @@ export type BatchProposal = {
votes: ProposalVotes;
};
/** The terms for the batch proposal */
/** The rationale for the proposal */
export type BatchProposalTerms = {
__typename?: 'BatchProposalTerms';
/** Actual changes being introduced by the batch proposal - actions the proposal triggers if passed and enacted. */
/** Actual changes being introduced by the proposal - actions the proposal triggers if passed and enacted. */
changes: Array<Maybe<BatchProposalTermsChange>>;
/**
* RFC3339Nano time and date when voting closes for this proposal.
@@ -559,22 +531,6 @@ export type CompositePriceConfiguration = {
decayWeight: Scalars['String'];
};
export type CompositePriceSource = {
__typename?: 'CompositePriceSource';
/** The source of the price */
PriceSource: Scalars['String'];
/** The last time the price source was updated in RFC3339Nano */
lastUpdated: Scalars['Timestamp'];
/** The current value of the composite source price */
price: Scalars['String'];
};
export type CompositePriceState = {
__typename?: 'CompositePriceState';
/** Underlying state of the composite price */
priceSources?: Maybe<Array<CompositePriceSource>>;
};
export enum CompositePriceType {
/** Composite price is set to the last trade (legacy) */
COMPOSITE_PRICE_TYPE_LAST_TRADE = 'COMPOSITE_PRICE_TYPE_LAST_TRADE',
@@ -2209,9 +2165,9 @@ export type MarginEdge = {
export type MarginEstimate = {
__typename?: 'MarginEstimate';
/** Margin level estimate assuming no slippage */
bestCase: AbstractMarginLevels;
bestCase: MarginLevels;
/** Margin level estimate assuming slippage cap is applied */
worstCase: AbstractMarginLevels;
worstCase: MarginLevels;
};
/** Margins for a given a party */
@@ -2483,8 +2439,6 @@ export type MarketData = {
liquidityProviderSla?: Maybe<Array<LiquidityProviderSLA>>;
/** The mark price (an unsigned integer) */
markPrice: Scalars['String'];
/** State of the underlying internal composite price */
markPriceState?: Maybe<CompositePriceState>;
/** The methodology used for the calculation of the mark price */
markPriceType: CompositePriceType;
/** Market of the associated mark price */
@@ -3099,8 +3053,6 @@ export type ObservableMarketData = {
liquidityProviderSla?: Maybe<Array<ObservableLiquidityProviderSLA>>;
/** The mark price (an unsigned integer) */
markPrice: Scalars['String'];
/** State of the underlying internal composite price */
markPriceState?: Maybe<CompositePriceState>;
/** The methodology used to calculated mark price */
markPriceType: CompositePriceType;
/** The market growth factor for the last market time window */
@@ -4069,8 +4021,6 @@ export type PerpetualData = {
fundingRate?: Maybe<Scalars['String']>;
/** Internal composite price used as input to the internal VWAP */
internalCompositePrice: Scalars['String'];
/** The internal state of the underlying internal composite price */
internalCompositePriceState?: Maybe<CompositePriceState>;
/** The methodology used to calculated internal composite price for perpetual markets */
internalCompositePriceType: CompositePriceType;
/** Time-weighted average price calculated from data points for this period from the internal data source. */
@@ -4081,8 +4031,6 @@ export type PerpetualData = {
seqNum: Scalars['Int'];
/** Time at which the funding period started */
startTime: Scalars['Timestamp'];
/** The last value from the external oracle */
underlyingIndexPrice: Scalars['String'];
};
export type PerpetualProduct = {
@@ -4380,7 +4328,7 @@ export type ProposalDetail = {
__typename?: 'ProposalDetail';
/** Batch proposal ID that is provided by Vega once proposal reaches the network */
batchId?: Maybe<Scalars['ID']>;
/** Terms of all the proposals in the batch */
/** Terms of the proposal for a batch proposal */
batchTerms?: Maybe<BatchProposalTerms>;
/** RFC3339Nano time and date when the proposal reached the Vega network */
datetime: Scalars['Timestamp'];
@@ -4406,7 +4354,7 @@ export type ProposalDetail = {
requiredParticipation: Scalars['String'];
/** State of the proposal */
state: ProposalState;
/** Terms of the proposal */
/** Terms of the proposal for proposal */
terms?: Maybe<ProposalTerms>;
};
+9 -30
View File
@@ -1,13 +1,12 @@
import {
type LiquidityFeeMethod,
type ConditionOperator,
type EntityScope,
type GovernanceTransferKind,
type GovernanceTransferType,
type IndividualScope,
type PeggedReference,
type ProposalChange,
type TransferStatus,
import type {
ConditionOperator,
EntityScope,
GovernanceTransferKind,
GovernanceTransferType,
IndividualScope,
PeggedReference,
ProposalChange,
TransferStatus,
} from './__generated__/types';
import type { AccountType } from './__generated__/types';
import type {
@@ -735,23 +734,3 @@ export const ProposalProductTypeShortName: Record<ProposalProductType, string> =
SpotProduct: 'Spot',
PerpetualProduct: 'Perp',
};
export const LiquidityFeeMethodMapping: { [e in LiquidityFeeMethod]: string } =
{
/** Fee is set by the market to a constant value irrespective of any liquidity provider's nominated fee */
METHOD_CONSTANT: 'Constant',
/** Fee is smallest value of all bids, such that liquidity providers with nominated fees less than or equal to this value still have sufficient commitment to fulfil the market's target stake. */
METHOD_MARGINAL_COST: 'Marginal cost',
METHOD_UNSPECIFIED: 'Unspecified',
/** Fee is the weighted average of all liquidity providers' nominated fees, weighted by their commitment */
METHOD_WEIGHTED_AVERAGE: 'Weighted average',
};
export const LiquidityFeeMethodMappingDescription: {
[e in LiquidityFeeMethod]: string;
} = {
METHOD_CONSTANT: `This liquidity fee is a constant value, set in the market parameters, and overrides the liquidity providers' nominated fees.`,
METHOD_MARGINAL_COST: `This liquidity fee factor is determined by sorting all LP fee bids from lowest to highest, with LPs' commitments tallied up to the point of fulfilling the market's target stake. The last LP's bid becomes the fee factor.`,
METHOD_UNSPECIFIED: 'Unspecified',
METHOD_WEIGHTED_AVERAGE: `This liquidity fee is the weighted average of all liquidity providers' nominated fees, weighted by their commitment.`,
};
@@ -38,7 +38,7 @@ export function Dialog({
);
const wrapperClasses = classNames(
// Dimensions
'max-w-[95vw] sm:max-w-[90vw] p-4 md:p-8',
'w-screen sm:max-w-[90vw] p-4 md:p-8',
// Need to apply background and text colors again as content is rendered in a portal
'dark:bg-black bg-white dark:text-white',
getIntentBorder(intent),
@@ -1,5 +1,4 @@
import { VegaIcon, VegaIconNames } from '../icon';
import { TradingButton } from '../trading-button';
import {
TradingDropdown,
TradingDropdownContent,
@@ -16,16 +15,6 @@ export const ActionsDropdownTrigger = () => {
);
};
export const MobileActionsDropdownTrigger = () => {
return (
<TradingDropdownTrigger data-testid="dropdown-menu">
<TradingButton size="medium">
<VegaIcon name={VegaIconNames.KEBAB} />
</TradingButton>
</TradingDropdownTrigger>
);
};
type ActionMenuContentProps = React.ComponentProps<
typeof TradingDropdownContent
>;
@@ -37,11 +26,3 @@ export const ActionsDropdown = (props: ActionMenuContentProps) => {
</TradingDropdown>
);
};
export const MobileActionsDropdown = (props: ActionMenuContentProps) => {
return (
<TradingDropdown trigger={<MobileActionsDropdownTrigger />}>
<TradingDropdownContent {...props} side="bottom" align="end" />
</TradingDropdown>
);
};
@@ -1,2 +1,2 @@
export * from './actions-dropdown';
export * from './trading-dropdown';
export * from './actions-dropdown';
+21 -5
View File
@@ -23,7 +23,6 @@ describe('number utils', () => {
{ v: new BigNumber(123000), d: 1, o: '12,300.0' },
{ v: new BigNumber(123001), d: 2, o: '1,230.01' },
{ v: new BigNumber(123001000), d: 2, o: '1,230,010.00' },
{ v: '100000000000000000001', d: 18, o: '100.000000000000000001' },
])(
'formats with addDecimalsFormatNumber given number correctly',
({ v, d, o }) => {
@@ -32,10 +31,27 @@ describe('number utils', () => {
);
it.each([
{ v: '1234000000000000000', d: 18, q: '1000000000000000000', o: '1.23' }, //vega
{ v: '1235000000000000000', d: 18, q: '1000000000000000000', o: '1.24' }, //vega
{ v: '1230012', d: 6, q: '1000000', o: '1.23' }, // USDT
{ v: '1234560000000000000', d: 18, q: '500000000000000', o: '1.2346' }, // WEth
{ v: new BigNumber(123000), d: 5, o: '1.23', q: 0.1 },
{ v: new BigNumber(123000), d: 3, o: '123.00', q: 0.1 },
{ v: new BigNumber(123000), d: 1, o: '12,300.00', q: 0.1 },
{ v: new BigNumber(123001000), d: 2, o: '1,230,010.00', q: 0.1 },
{ v: new BigNumber(123001), d: 2, o: '1,230.01', q: 100 },
{ v: new BigNumber(123001), d: 2, o: '1,230.01', q: 0.1 },
{ v: new BigNumber(123001), d: 2, o: '1,230.01', q: 1 },
{
v: BigNumber('123456789123456789'),
d: 10,
o: '12,345,678.91234568',
q: '0.00003846',
},
{
v: BigNumber('123456789123456789'),
d: 10,
o: '12,345,678.91234568',
q: '1',
},
// USDT / USDC
{ v: new BigNumber(12345678), d: 6, o: '12.35', q: 1000000 },
])(
'formats with addDecimalsFormatNumberQuantum given number correctly',
({ v, d, o, q }) => {
+36 -60
View File
@@ -1,4 +1,5 @@
import { BigNumber } from 'bignumber.js';
import isNil from 'lodash/isNil';
import memoize from 'lodash/memoize';
import { getUserLocale } from '../get-user-locale';
@@ -52,36 +53,36 @@ export function removeDecimal(
return new BigNumber(value || 0).times(times).toFixed(0);
}
export const getDecimalSeparator = memoize(
() =>
new Intl.NumberFormat(getUserLocale())
.formatToParts(1.1)
.find((part) => part.type === 'decimal')?.value ?? '.'
);
export const getGroupFormat = memoize(() => {
const parts = new Intl.NumberFormat(getUserLocale()).formatToParts(
100000000000.1
);
const groupSeparator = parts.find((part) => part.type === 'group')?.value;
const groupSize =
(groupSeparator &&
parts.reverse().find((part) => part.type === 'integer')?.value.length) ||
0;
return {
groupSize,
groupSeparator,
};
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/NumberFormat/NumberFormat
export const getNumberFormat = memoize((digits: number) => {
if (isNil(digits) || digits < 0) {
return new Intl.NumberFormat(getUserLocale());
}
return new Intl.NumberFormat(getUserLocale(), {
minimumFractionDigits: Math.min(Math.max(0, digits), MIN_FRACTION_DIGITS),
maximumFractionDigits: Math.min(Math.max(0, digits), MAX_FRACTION_DIGITS),
});
});
const getFormat = memoize(() => ({
decimalSeparator: getDecimalSeparator(),
...getGroupFormat(),
}));
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/NumberFormat/NumberFormat
export const getFixedNumberFormat = memoize((digits: number) => {
if (isNil(digits) || digits < 0) {
return new Intl.NumberFormat(getUserLocale());
}
return new Intl.NumberFormat(getUserLocale(), {
minimumFractionDigits: Math.min(Math.max(0, digits), MAX_FRACTION_DIGITS),
maximumFractionDigits: Math.min(Math.max(0, digits), MAX_FRACTION_DIGITS),
});
});
/**
* formatNumber will format the number with maximum number of decimals
* trailing zeros are removed but min(MIN_FRACTION_DIGITS, formatDecimals) decimal places will be kept
export const getDecimalSeparator = memoize(
() =>
getNumberFormat(1)
.formatToParts(1.1)
.find((part) => part.type === 'decimal')?.value
);
/** formatNumber will format the number with fixed decimals
* @param rawValue - should be a number that is not outside the safe range fail as in https://mikemcl.github.io/bignumber.js/#toN
* @param formatDecimals - number of decimals to use
*/
@@ -89,23 +90,7 @@ export const formatNumber = (
rawValue: string | number | BigNumber,
formatDecimals = 0
) => {
const decimalPlaces = Math.min(
Math.max(0, formatDecimals),
MAX_FRACTION_DIGITS
);
const format = getFormat();
const formatted = new BigNumber(rawValue).toFormat(decimalPlaces, format);
// if there are no decimal places just return formatted value
if (!decimalPlaces) {
return formatted;
}
// minimum number of decimal places to keep when removing trailing zeros
const minimumFractionDigits = Math.min(decimalPlaces, MIN_FRACTION_DIGITS);
const parts = formatted.split(format.decimalSeparator);
parts[1] = (parts[1] || '')
.replace(/0+$/, '')
.padEnd(minimumFractionDigits, '0');
return parts.join(format.decimalSeparator);
return getNumberFormat(formatDecimals).format(Number(rawValue));
};
/** formatNumberFixed will format the number with fixed decimals
@@ -116,10 +101,7 @@ export const formatNumberFixed = (
rawValue: string | number | BigNumber,
formatDecimals = 0
) => {
return new BigNumber(rawValue).toFormat(
Math.min(Math.max(0, formatDecimals), MAX_FRACTION_DIGITS),
getFormat()
);
return getFixedNumberFormat(formatDecimals).format(Number(rawValue));
};
export const quantumDecimalPlaces = (
@@ -149,14 +131,9 @@ export const addDecimalsFormatNumberQuantum = (
if (isNaN(Number(quantum))) {
return addDecimalsFormatNumber(rawValue, decimalPlaces);
}
const numberDP = Math.ceil(
Math.abs(Math.log10(toBigNum(quantum, decimalPlaces).toNumber()))
);
return addDecimalsFormatNumber(
rawValue,
decimalPlaces,
Math.max(MIN_FRACTION_DIGITS, numberDP)
);
const quantumValue = addDecimal(quantum, decimalPlaces);
const numberDP = Math.max(0, Math.log10(100 / Number(quantumValue)));
return addDecimalsFormatNumber(rawValue, decimalPlaces, Math.ceil(numberDP));
};
export const addDecimalsFormatNumber = (
@@ -164,10 +141,9 @@ export const addDecimalsFormatNumber = (
decimalPlaces: number,
formatDecimals: number = decimalPlaces
) => {
return formatNumber(
new BigNumber(rawValue || 0).dividedBy(Math.pow(10, decimalPlaces)),
formatDecimals
);
const x = addDecimal(rawValue, decimalPlaces);
return formatNumber(x, formatDecimals);
};
export const addDecimalsFixedFormatNumber = (
+12 -12
View File
@@ -10,24 +10,24 @@ describe('formatValue', () => {
{
v: '123456789123456789',
d: 10,
o: '12,345,678.9123456789',
o: '12,345,678.91234568',
},
])('formats values correctly', ({ v, d, o }) => {
expect(formatValue(v, d)).toStrictEqual(o);
});
it.each([
{ v: 123000, d: 5, o: '1.23', q: '1' },
{ v: 123000, d: 3, o: '123.00', q: '1' },
{ v: 123000, d: 1, o: '12,300.00', q: '1' },
{ v: 123001000, d: 2, o: '1,230,010.00', q: '1' },
{ v: 123000, d: 5, o: '1.23', q: '0.1' },
{ v: 123000, d: 3, o: '123.00', q: '0.1' },
{ v: 123000, d: 1, o: '12,300.00', q: '0.1' },
{ v: 123001000, d: 2, o: '1,230,010.00', q: '0.1' },
{ v: 123001, d: 2, o: '1,230.01', q: '100' },
{ v: 123001, d: 2, o: '1,230.01', q: '1' },
{ v: 123001, d: 2, o: '1,230.01', q: '0.1' },
{
v: '123456789123456789',
d: 10,
o: '12,345,678.91235',
q: '384600',
o: '12,345,678.91234568',
q: '0.00003846',
},
])(
'formats with formatValue with quantum given number correctly',
@@ -42,15 +42,15 @@ describe('formatRange', () => {
min: 123000,
max: 12300011111,
d: 5,
o: '1.23 - 123,000.11',
q: '1000',
o: '1.23 - 123,000.11111',
q: '0.1',
},
{
min: 123000,
max: 12300011111,
d: 3,
o: '123.00 - 12,300,011.11',
q: '100',
o: '123.00 - 12,300,011.111',
q: '0.1',
},
{
min: 123000,
+6 -9
View File
@@ -1,10 +1,6 @@
import { WalletClientError } from '@vegaprotocol/wallet-client';
import type * as Schema from '@vegaprotocol/types';
import type { PeggedReference } from '@vegaprotocol/types';
import {
vegaMarginMode,
v1UndelegateSubmissionMethod,
} from '@vegaprotocol/rest-clients/dist/trading-data';
export interface LiquidityProvisionSubmission {
liquidityProvisionSubmission: LiquidityProvisionBody;
@@ -36,7 +32,7 @@ export interface UndelegateSubmissionBody {
undelegateSubmission: {
nodeId: string;
amount: string;
method: v1UndelegateSubmissionMethod;
method: 'METHOD_NOW' | 'METHOD_AT_END_OF_EPOCH';
};
}
@@ -476,12 +472,13 @@ export type UpdateReferralSet = {
};
};
export { vegaMarginMode as MarginMode };
export { v1UndelegateSubmissionMethod as UndelegateSubmissionMethod };
export enum MarginMode {
MARGIN_MODE_CROSS_MARGIN = 1,
MARGIN_MODE_ISOLATED_MARGIN,
}
export interface UpdateMarginMode {
marketId: string;
mode: vegaMarginMode;
mode: MarginMode;
marginFactor?: string;
}
-1
View File
@@ -42,7 +42,6 @@
"@sentry/nextjs": "^6.19.3",
"@sentry/react": "^6.19.2",
"@sentry/tracing": "^6.19.2",
"@vegaprotocol/rest-clients": "^0.0.5",
"@vegaprotocol/wallet-client": "0.1.9",
"@walletconnect/ethereum-provider": "^2.6.0",
"@web3-react/coinbase-wallet": "8.1.2-beta.0",
+2
View File
@@ -40,6 +40,8 @@
## Transfer
- **Must** display tooltip for "Transfer fee when hovered over.(<a name="1003-TRAN-017" href="#1003-TRAN-017">1003-TRAN-017</a>)
- **Must** display tooltip for "Amount to be transferred" when hovered over.(<a name="1003-TRAN-018" href="#1003-TRAN-018">1003-TRAN-018</a>)
- **Must** display tooltip for "Total amount (with fee)" when hovered over.(<a name="1003-TRAN-019" href="#1003-TRAN-019">1003-TRAN-019</a>)
-5
View File
@@ -7808,11 +7808,6 @@
"@typescript-eslint/types" "6.11.0"
eslint-visitor-keys "^3.4.1"
"@vegaprotocol/rest-clients@^0.0.5":
version "0.0.5"
resolved "https://registry.yarnpkg.com/@vegaprotocol/rest-clients/-/rest-clients-0.0.5.tgz#b75f14fc549e8d170518e973435f2b91ec19f553"
integrity sha512-UTsGJKrzMsXHkuAmRxEA3ei9GEGFKwIHPdVl2DHlRMQRnP39atSu4IZsP6kK2HqLgJi8QiEZCyIkXQk+jrNRDA==
"@vegaprotocol/wallet-client@0.1.9":
version "0.1.9"
resolved "https://registry.yarnpkg.com/@vegaprotocol/wallet-client/-/wallet-client-0.1.9.tgz#8c6a71c8b2222b3de5d73cade8fc6db57e332de9"