Compare commits

...
21 changed files with 344 additions and 58 deletions
+2
View File
@@ -1,2 +1,4 @@
* @vegaprotocol/frontend
apps/ @vegaprotocol/frontend-qa
libs/ @vegaprotocol/frontend-qa
*.graphql @vegaprotocol/core
@@ -17,7 +17,7 @@ import { useReferral } from './hooks/use-referral';
import { Routes } from '../../lib/links';
import { useTransactionEventSubscription } from '@vegaprotocol/web3';
import { t } from '@vegaprotocol/i18n';
import { Statistics } from './referral-statistics';
import { Statistics, useStats } from './referral-statistics';
import { useReferralProgram } from './hooks/use-referral-program';
const RELOAD_DELAY = 3000;
@@ -132,6 +132,8 @@ export const ApplyCodeForm = () => {
}),
});
const { epochsValue, nextBenefitTierValue } = useStats({ program });
// go to main page when successfully applied
useEffect(() => {
if (status === 'successful') {
@@ -196,6 +198,10 @@ export const ApplyCodeForm = () => {
};
};
const nextBenefitTierEpochsValue = nextBenefitTierValue
? nextBenefitTierValue.epochs - epochsValue
: 0;
return (
<>
<div className="w-2/3 max-w-md mx-auto bg-vega-clight-800 dark:bg-vega-cdark-800 p-8 rounded-lg">
@@ -238,7 +244,12 @@ export const ApplyCodeForm = () => {
) : null}
{previewData ? (
<div className="mt-10">
<h2 className="text-2xl mb-5">{t('You are joining')}</h2>
<h2 className="text-2xl mb-5">
{t(
'You are joining the group shown, but will not have access to benefits until you have completed at least %s epochs.',
[nextBenefitTierEpochsValue.toString()]
)}
</h2>
<Statistics data={previewData} program={program} as="referee" />
</div>
) : null}
@@ -25,7 +25,7 @@ import compact from 'lodash/compact';
import { useReferralProgram } from './hooks/use-referral-program';
import { useStakeAvailable } from './hooks/use-stake-available';
import sortBy from 'lodash/sortBy';
import { useLayoutEffect, useRef, useState } from 'react';
import { useLayoutEffect, useMemo, useRef, useState } from 'react';
import { useCurrentEpochInfoQuery } from './hooks/__generated__/Epoch';
import BigNumber from 'bignumber.js';
import { t } from '@vegaprotocol/i18n';
@@ -59,21 +59,20 @@ export const ReferralStatistics = () => {
return <CreateCodeContainer />;
};
export const Statistics = ({
export const useStats = ({
data,
program,
as,
}: {
data: NonNullable<ReturnType<typeof useReferral>['data']>;
data?: NonNullable<ReturnType<typeof useReferral>['data']>;
program: ReturnType<typeof useReferralProgram>;
as: 'referrer' | 'referee';
as?: 'referrer' | 'referee';
}) => {
const { benefitTiers, details } = program;
const { benefitTiers } = program;
const { data: epochData } = useCurrentEpochInfoQuery();
const { stakeAvailable } = useStakeAvailable();
const { data: statsData } = useReferralSetStatsQuery({
variables: {
code: data.code,
code: data?.code || '',
},
skip: !data?.code,
fetchPolicy: 'cache-and-network',
@@ -81,19 +80,12 @@ export const Statistics = ({
const currentEpoch = Number(epochData?.epoch.id);
const compactNumFormat = new Intl.NumberFormat(getUserLocale(), {
minimumFractionDigits: 0,
maximumFractionDigits: 2,
notation: 'compact',
compactDisplay: 'short',
});
const stats =
statsData?.referralSetStats.edges &&
compact(removePaginationWrapper(statsData.referralSetStats.edges));
const refereeInfo = data.referee;
const refereeInfo = data?.referee;
const refereeStats = stats?.find(
(r) => r.partyId === data.referee?.refereeId
(r) => r.partyId === data?.referee?.refereeId
);
const statsAvailable = stats && stats.length > 0 && stats[0];
@@ -136,6 +128,60 @@ export const Statistics = ({
? nextBenefitTierValue.epochs - epochsValue
: 0;
return {
baseCommissionValue,
runningVolumeValue,
referrerVolumeValue,
multiplier,
finalCommissionValue,
discountFactorValue,
currentBenefitTierValue,
nextBenefitTierValue,
epochsValue,
nextBenefitTierVolumeValue,
nextBenefitTierEpochsValue,
};
};
export const Statistics = ({
data,
program,
as,
}: {
data: NonNullable<ReturnType<typeof useReferral>['data']>;
program: ReturnType<typeof useReferralProgram>;
as: 'referrer' | 'referee';
}) => {
const {
baseCommissionValue,
runningVolumeValue,
referrerVolumeValue,
multiplier,
finalCommissionValue,
discountFactorValue,
currentBenefitTierValue,
epochsValue,
nextBenefitTierVolumeValue,
nextBenefitTierEpochsValue,
} = useStats({ data, program, as });
const isApplyCodePreview = useMemo(
() => data.referee === null,
[data.referee]
);
const { benefitTiers } = useReferralProgram();
const { stakeAvailable } = useStakeAvailable();
const { details } = program;
const compactNumFormat = new Intl.NumberFormat(getUserLocale(), {
minimumFractionDigits: 0,
maximumFractionDigits: 2,
notation: 'compact',
compactDisplay: 'short',
});
const baseCommissionTile = (
<StatTile
title={t('Base commission rate')}
@@ -229,11 +275,18 @@ export const Statistics = ({
const currentBenefitTierTile = (
<StatTile title={t('Current tier')}>
{currentBenefitTierValue?.tier || 'None'}
{isApplyCodePreview
? currentBenefitTierValue?.tier || benefitTiers[0]?.tier || 'None'
: currentBenefitTierValue?.tier || 'None'}
</StatTile>
);
const discountFactorTile = (
<StatTile title={t('Discount')}>{discountFactorValue * 100}%</StatTile>
<StatTile title={t('Discount')}>
{isApplyCodePreview
? benefitTiers[0].discountFactor * 100
: discountFactorValue * 100}
%
</StatTile>
);
const runningVolumeTile = (
<StatTile
@@ -265,11 +318,11 @@ export const Statistics = ({
<>
<div className="grid grid-rows-1 gap-5 grid-cols-1 md:grid-cols-3">
{currentBenefitTierTile}
{discountFactorTile}
{runningVolumeTile}
{codeTile}
</div>
<div className="grid grid-rows-1 gap-5 grid-cols-1 sm:grid-cols-2 xl:grid-cols-4">
{runningVolumeTile}
{discountFactorTile}
{nextTierVolumeTile}
{epochsTile}
{nextTierEpochsTile}
@@ -36,6 +36,22 @@ query Fees(
}
}
}
referrer: referralSets(referrer: $partyId) {
edges {
node {
id
referrer
}
}
}
referee: referralSets(referee: $partyId) {
edges {
node {
id
referrer
}
}
}
referralSetReferees(referee: $partyId) {
edges {
node {
+17 -1
View File
@@ -15,7 +15,7 @@ export type FeesQueryVariables = Types.Exact<{
}>;
export type FeesQuery = { __typename?: 'Query', epoch: { __typename?: 'Epoch', id: string }, volumeDiscountStats: { __typename?: 'VolumeDiscountStatsConnection', edges: Array<{ __typename?: 'VolumeDiscountStatsEdge', node: { __typename?: 'VolumeDiscountStats', atEpoch: number, discountFactor: string, runningVolume: string } } | null> }, referralSetReferees: { __typename?: 'ReferralSetRefereeConnection', edges: Array<{ __typename?: 'ReferralSetRefereeEdge', node: { __typename?: 'ReferralSetReferee', atEpoch: number } } | null> }, referralSetStats: { __typename?: 'ReferralSetStatsConnection', edges: Array<{ __typename?: 'ReferralSetStatsEdge', node: { __typename?: 'ReferralSetStats', atEpoch: number, discountFactor: string, referralSetRunningNotionalTakerVolume: string } } | null> } };
export type FeesQuery = { __typename?: 'Query', epoch: { __typename?: 'Epoch', id: string }, volumeDiscountStats: { __typename?: 'VolumeDiscountStatsConnection', edges: Array<{ __typename?: 'VolumeDiscountStatsEdge', node: { __typename?: 'VolumeDiscountStats', atEpoch: number, discountFactor: string, runningVolume: string } } | null> }, referrer: { __typename?: 'ReferralSetConnection', edges: Array<{ __typename?: 'ReferralSetEdge', node: { __typename?: 'ReferralSet', id: string, referrer: string } } | null> }, referee: { __typename?: 'ReferralSetConnection', edges: Array<{ __typename?: 'ReferralSetEdge', node: { __typename?: 'ReferralSet', id: string, referrer: string } } | null> }, referralSetReferees: { __typename?: 'ReferralSetRefereeConnection', edges: Array<{ __typename?: 'ReferralSetRefereeEdge', node: { __typename?: 'ReferralSetReferee', atEpoch: number } } | null> }, referralSetStats: { __typename?: 'ReferralSetStatsConnection', edges: Array<{ __typename?: 'ReferralSetStatsEdge', node: { __typename?: 'ReferralSetStats', atEpoch: number, discountFactor: string, referralSetRunningNotionalTakerVolume: string } } | null> } };
export const DiscountProgramsDocument = gql`
@@ -81,6 +81,22 @@ export const FeesDocument = gql`
}
}
}
referrer: referralSets(referrer: $partyId) {
edges {
node {
id
referrer
}
}
}
referee: referralSets(referee: $partyId) {
edges {
node {
id
referrer
}
}
}
referralSetReferees(referee: $partyId) {
edges {
node {
@@ -17,6 +17,14 @@ import { useReferralStats } from './use-referral-stats';
import { formatPercentage, getAdjustedFee } from './utils';
import { Table, Td, Th, THead, Tr } from './table';
import BigNumber from 'bignumber.js';
import { Links } from '../../lib/links';
import { Link } from 'react-router-dom';
import {
Tooltip,
VegaIcon,
VegaIconNames,
truncateMiddle,
} from '@vegaprotocol/ui-toolkit';
export const FeesContainer = () => {
const { pubKey } = useVegaWallet();
@@ -56,16 +64,25 @@ export const FeesContainer = () => {
referralTierIndex,
referralTiers,
epochsInSet,
code,
isReferrer,
} = useReferralStats(
feesData?.referralSetStats,
feesData?.referralSetReferees,
programData?.currentReferralProgram,
feesData?.epoch
feesData?.epoch,
feesData?.referrer,
feesData?.referee
);
const loading = paramsLoading || feesLoading || programLoading;
const isConnected = Boolean(pubKey);
const isReferralProgramRunning = Boolean(programData?.currentReferralProgram);
const isVolumeDiscountProgramRunning = Boolean(
programData?.currentVolumeDiscountProgram
);
return (
<div className="grid auto-rows-min grid-cols-4 gap-3">
{isConnected && (
@@ -90,6 +107,8 @@ export const FeesContainer = () => {
<TotalDiscount
referralDiscount={referralDiscount}
volumeDiscount={volumeDiscount}
isReferralProgramRunning={isReferralProgramRunning}
isVolumeDiscountProgramRunning={isVolumeDiscountProgramRunning}
/>
</FeeCard>
<FeeCard
@@ -97,23 +116,37 @@ export const FeesContainer = () => {
className="sm:col-span-2"
loading={loading}
>
<CurrentVolume
tiers={volumeTiers}
tierIndex={volumeTierIndex}
windowLengthVolume={volumeInWindow}
windowLength={volumeDiscountWindowLength}
/>
{isVolumeDiscountProgramRunning ? (
<CurrentVolume
tiers={volumeTiers}
tierIndex={volumeTierIndex}
windowLengthVolume={volumeInWindow}
windowLength={volumeDiscountWindowLength}
/>
) : (
<p className="pt-3 text-sm text-muted">
{t('No volume discount program active')}
</p>
)}
</FeeCard>
<FeeCard
title={t('Referral benefits')}
className="sm:col-span-2"
loading={loading}
>
<ReferralBenefits
setRunningNotionalTakerVolume={referralVolumeInWindow}
epochsInSet={epochsInSet}
epochs={referralDiscountWindowLength}
/>
{isReferrer ? (
<ReferrerInfo code={code} />
) : isReferralProgramRunning ? (
<ReferralBenefits
setRunningNotionalTakerVolume={referralVolumeInWindow}
epochsInSet={epochsInSet}
epochs={referralDiscountWindowLength}
/>
) : (
<p className="pt-3 text-sm text-muted">
{t('No referral program active')}
</p>
)}
</FeeCard>
</>
)}
@@ -142,7 +175,7 @@ export const FeesContainer = () => {
/>
</FeeCard>
<FeeCard
title={t('Liquidity fees')}
title={t('Fees by market')}
className="lg:col-span-full"
loading={marketsLoading}
>
@@ -325,26 +358,64 @@ const ReferralBenefits = ({
const TotalDiscount = ({
referralDiscount,
volumeDiscount,
isReferralProgramRunning,
isVolumeDiscountProgramRunning,
}: {
referralDiscount: number;
volumeDiscount: number;
isReferralProgramRunning: boolean;
isVolumeDiscountProgramRunning: boolean;
}) => {
const totalDiscount = 1 - (1 - volumeDiscount) * (1 - referralDiscount);
const totalDiscountDescription = t(
'The total discount is calculated according to the following formula: '
);
const formula = (
<span className="italic">
1 - (1 - d<sub>volume</sub>) (1 - d<sub>referral</sub>)
</span>
);
return (
<div>
<Stat
value={formatPercentage(referralDiscount + volumeDiscount) + '%'}
description={
<>
{totalDiscountDescription}
{formula}
</>
}
value={formatPercentage(totalDiscount) + '%'}
highlight={true}
/>
<table className="w-full mt-0.5 text-xs text-muted">
<tbody>
<tr>
<th className="font-normal text-left">{t('Volume discount')}</th>
<td className="text-right">{formatPercentage(volumeDiscount)}%</td>
<td className="text-right">
{formatPercentage(volumeDiscount)}%
{!isVolumeDiscountProgramRunning && (
<Tooltip description={t('No active volume discount programme')}>
<span className="cursor-help">
{' '}
<VegaIcon name={VegaIconNames.INFO} size={12} />
</span>
</Tooltip>
)}
</td>
</tr>
<tr>
<th className="font-normal text-left ">{t('Referral discount')}</th>
<td className="text-right">
{formatPercentage(referralDiscount)}%
{!isReferralProgramRunning && (
<Tooltip description={t('No active referral programme')}>
<span className="cursor-help">
{' '}
<VegaIcon name={VegaIconNames.INFO} size={12} />
</span>
</Tooltip>
)}
</td>
</tr>
</tbody>
@@ -491,3 +562,31 @@ const YourTier = () => {
</span>
);
};
const ReferrerInfo = ({ code }: { code?: string }) => (
<div className="pt-3 text-sm text-vega-clight-200 dark:vega-cdark-200">
<p className="mb-1">
{t('Connected key is owner of the referral set')}
{code && (
<>
{' '}
<span className="text-transparent bg-rainbow bg-clip-text">
{truncateMiddle(code)}
</span>
</>
)}
{'. '}
{t('As owner, it is eligible for commission not fee discounts.')}
</p>
<p>
{t('See')}{' '}
<Link
className="underline text-black dark:text-white"
to={Links.REFERRALS()}
>
{t('Referrals')}
</Link>{' '}
{t('for more information.')}
</p>
</div>
);
@@ -1,23 +1,31 @@
import { Tooltip } from '@vegaprotocol/ui-toolkit';
import classNames from 'classnames';
import type { ReactNode } from 'react';
export const Stat = ({
value,
text,
highlight,
description,
}: {
value: string | number;
text?: string;
highlight?: boolean;
description?: ReactNode;
}) => {
const val = (
<span
className={classNames('inline-block text-3xl leading-none', {
'text-transparent bg-rainbow bg-clip-text': highlight,
'cursor-help': description,
})}
>
{value}
</span>
);
return (
<p className="pt-3 leading-none first:pt-6">
<span
className={classNames('inline-block text-3xl leading-none', {
'text-transparent bg-rainbow bg-clip-text': highlight,
})}
>
{value}
</span>
{description ? <Tooltip description={description}>{val}</Tooltip> : val}
{text && (
<small className="block mt-0.5 text-xs text-muted">{text}</small>
)}
@@ -73,6 +73,8 @@ describe('useReferralStats', () => {
referralTierIndex: -1,
referralTiers: [],
epochsInSet: 0,
code: undefined,
isReferrer: false,
});
});
@@ -93,6 +95,8 @@ describe('useReferralStats', () => {
referralTierIndex: 1,
referralTiers: program.benefitTiers,
epochsInSet: Number(epoch.id) - set.atEpoch,
code: undefined,
isReferrer: false,
});
});
@@ -2,12 +2,15 @@ import compact from 'lodash/compact';
import maxBy from 'lodash/maxBy';
import { getReferralBenefitTier } from './utils';
import type { DiscountProgramsQuery, FeesQuery } from './__generated__/Fees';
import { first } from 'lodash';
export const useReferralStats = (
setStats?: FeesQuery['referralSetStats'],
setReferees?: FeesQuery['referralSetReferees'],
program?: DiscountProgramsQuery['currentReferralProgram'],
epoch?: FeesQuery['epoch']
epoch?: FeesQuery['epoch'],
setIfReferrer?: FeesQuery['referrer'],
setIfReferee?: FeesQuery['referee']
) => {
const referralTiers = program?.benefitTiers || [];
@@ -18,9 +21,18 @@ export const useReferralStats = (
referralTierIndex: -1,
referralTiers,
epochsInSet: 0,
code: undefined,
isReferrer: false,
};
}
const setIfReferrerData = first(
compact(setIfReferrer?.edges).map((e) => e.node)
);
const setIfRefereeData = first(
compact(setIfReferee?.edges).map((e) => e.node)
);
const referralSetsStats = compact(setStats.edges).map((e) => e.node);
const referralSets = compact(setReferees.edges).map((e) => e.node);
@@ -48,5 +60,7 @@ export const useReferralStats = (
referralTierIndex,
referralTiers,
epochsInSet,
code: (setIfReferrerData || setIfRefereeData)?.id,
isReferrer: Boolean(setIfReferrerData),
};
};
@@ -21,7 +21,7 @@ describe('getAdjustedFee', () => {
new BigNumber(referralDiscount),
];
// 1 - 0.5 - 0.5
// 1 - 0.5 = 0.5
const v = new BigNumber(1).minus(new BigNumber(volumeDiscount));
// 1 - 0.5 = 0.5
@@ -34,13 +34,15 @@ describe('getAdjustedFee', () => {
// 0.1 + 0.1 + 0.1 = 0.3
const totalFees = fees.reduce((sum, x) => sum.plus(x), new BigNumber(0));
// 0.3 * 0.75 = 0.225
const expected = new BigNumber(totalFees).times(factor).toNumber();
// (1 - 0.3) * 0.75 = 0.525
const expected = new BigNumber(totalFees)
.times(new BigNumber(1).minus(factor))
.toNumber();
expect(getAdjustedFee(fees, discounts)).toBe(expected);
});
it('combines discount factors multiplicativly', () => {
it('combines discount factors multiplicatively', () => {
const volumeDiscount = 0.4;
const referralDiscount = 0.1;
@@ -67,7 +69,9 @@ describe('getAdjustedFee', () => {
// summed fees
const totalFees = fees.reduce((sum, x) => sum.plus(x), new BigNumber(0));
const expected = new BigNumber(totalFees).times(factor).toNumber();
const expected = new BigNumber(totalFees)
.times(new BigNumber(1).minus(factor))
.toNumber();
expect(getAdjustedFee(fees, discounts)).toBe(expected);
});
@@ -12,7 +12,9 @@ export const formatPercentage = (num: number) => {
const pct = new BigNumber(num).times(100);
const dps = pct.decimalPlaces();
const formatter = new Intl.NumberFormat(getUserLocale(), {
minimumFractionDigits: dps || 0,
// set to 0 in order to remove the "trailing zeroes" for numbers such as:
// 0.123456789 -non-zero-min-> 12.3456800% -zero-min-> 12.34568%
minimumFractionDigits: 0,
maximumFractionDigits: dps || 0,
});
return formatter.format(parseFloat(pct.toFixed(5)));
@@ -101,5 +103,7 @@ export const getAdjustedFee = (fees: BigNumber[], discounts: BigNumber[]) => {
const totalFactor = new BigNumber(1).minus(combinedFactors);
return totalFee.times(BigNumber.max(0, totalFactor)).toNumber();
return totalFee
.times(new BigNumber(1).minus(BigNumber.max(0, totalFactor)))
.toNumber();
};
@@ -685,7 +685,7 @@ export const DealTicket = ({
subLabel={`${formatValue(
normalizedOrder.size,
market.positionDecimalPlaces
)} ${baseQuote} @ ${
)} ${baseQuote || ''} @ ${
type === Schema.OrderType.TYPE_MARKET
? 'market'
: `${formatValue(
+6 -2
View File
@@ -16,7 +16,7 @@ import type {
MarketDataQueryVariables,
} from './__generated__/market-data';
import { getMarketPrice } from './get-price';
import { isMarketInAuction } from './is-market-in-auction';
import { MarketTradingMode } from '@vegaprotocol/types';
export type MarketData = MarketDataFieldsFragment;
@@ -142,7 +142,11 @@ export const fundingRateProvider = makeDerivedDataProvider<
MarketDataQueryVariables
>([marketDataProvider], (parts) => {
const marketData = parts[0] as ReturnType<typeof getData>;
return marketData && !isMarketInAuction(marketData.marketTradingMode)
return marketData &&
![
MarketTradingMode.TRADING_MODE_OPENING_AUCTION,
MarketTradingMode.TRADING_MODE_SUSPENDED_VIA_GOVERNANCE,
].includes(marketData.marketTradingMode)
? marketData?.productData?.fundingRate || null
: null;
});
@@ -148,6 +148,7 @@ export const OrderListManager = ({
expiresAt: editOrder.expiresAt,
side: editOrder.side,
marketId: editOrder.market.id,
remaining: editOrder.remaining,
};
create({ orderAmendment }, originalOrder);
setEditOrder(null);
@@ -1,4 +1,5 @@
import {
MAXGOINT64,
addDecimalsFormatNumber,
getDateTimeFormat,
isNumeric,
@@ -144,11 +145,22 @@ export const OrderListTable = memo<
if (!data?.market || !isNumeric(data.size)) {
return '-';
}
const prefix = data
? data.side === Schema.Side.SIDE_BUY
? '+'
: '-'
: '';
if (
data.size === MAXGOINT64 &&
data.timeInForce ===
Schema.OrderTimeInForce.TIME_IN_FORCE_IOC &&
data.reduceOnly
) {
return t('MAX');
}
return (
prefix +
addDecimalsFormatNumber(
@@ -65,6 +65,7 @@ fragment OrderTxUpdateFields on OrderUpdate {
expiresAt
side
marketId
remaining
}
subscription OrderTxUpdate($partyId: ID!) {
+3 -2
View File
@@ -21,14 +21,14 @@ export type WithdrawalBusEventSubscriptionVariables = Types.Exact<{
export type WithdrawalBusEventSubscription = { __typename?: 'Subscription', busEvents?: Array<{ __typename?: 'BusEvent', event: { __typename?: 'Deposit' } | { __typename?: 'TimeUpdate' } | { __typename?: 'TransactionResult' } | { __typename?: 'Withdrawal', id: string, status: Types.WithdrawalStatus, amount: string, createdTimestamp: any, withdrawnTimestamp?: any | null, txHash?: string | null, pendingOnForeignChain: boolean, asset: { __typename?: 'Asset', id: string, name: string, symbol: string, decimals: number, status: Types.AssetStatus, source: { __typename?: 'BuiltinAsset' } | { __typename?: 'ERC20', contractAddress: string } }, details?: { __typename?: 'Erc20WithdrawalDetails', receiverAddress: string } | null } }> | null };
export type OrderTxUpdateFieldsFragment = { __typename?: 'OrderUpdate', type?: Types.OrderType | null, id: string, status: Types.OrderStatus, rejectionReason?: Types.OrderRejectionReason | null, createdAt: any, size: string, price: string, timeInForce: Types.OrderTimeInForce, expiresAt?: any | null, side: Types.Side, marketId: string };
export type OrderTxUpdateFieldsFragment = { __typename?: 'OrderUpdate', type?: Types.OrderType | null, id: string, status: Types.OrderStatus, rejectionReason?: Types.OrderRejectionReason | null, createdAt: any, size: string, price: string, timeInForce: Types.OrderTimeInForce, expiresAt?: any | null, side: Types.Side, marketId: string, remaining: string };
export type OrderTxUpdateSubscriptionVariables = Types.Exact<{
partyId: Types.Scalars['ID'];
}>;
export type OrderTxUpdateSubscription = { __typename?: 'Subscription', orders?: Array<{ __typename?: 'OrderUpdate', type?: Types.OrderType | null, id: string, status: Types.OrderStatus, rejectionReason?: Types.OrderRejectionReason | null, createdAt: any, size: string, price: string, timeInForce: Types.OrderTimeInForce, expiresAt?: any | null, side: Types.Side, marketId: string }> | null };
export type OrderTxUpdateSubscription = { __typename?: 'Subscription', orders?: Array<{ __typename?: 'OrderUpdate', type?: Types.OrderType | null, id: string, status: Types.OrderStatus, rejectionReason?: Types.OrderRejectionReason | null, createdAt: any, size: string, price: string, timeInForce: Types.OrderTimeInForce, expiresAt?: any | null, side: Types.Side, marketId: string, remaining: string }> | null };
export type DepositBusEventFieldsFragment = { __typename?: 'Deposit', id: string, status: Types.DepositStatus, amount: string, createdTimestamp: any, creditedTimestamp?: any | null, txHash?: string | null, asset: { __typename?: 'Asset', id: string, symbol: string, decimals: number } };
@@ -88,6 +88,7 @@ export const OrderTxUpdateFieldsFragmentDoc = gql`
expiresAt
side
marketId
remaining
}
`;
export const DepositBusEventFieldsFragmentDoc = gql`
@@ -56,6 +56,7 @@ describe('useVegaTransactionStore', () => {
side: Side.SIDE_BUY,
marketId:
'3aa2a828687cc3d59e92445d294891cbbd40e2165bbfb15674158ef5d4e8848d',
remaining: '12',
};
const processedTransactionUpdate = {
@@ -193,6 +193,7 @@ const submitOrder: VegaStoredTxState = {
createdAt: new Date(),
marketId: 'market-1',
status: OrderStatus.STATUS_ACTIVE,
remaining: '10',
},
};
@@ -249,6 +250,7 @@ const editOrder: VegaStoredTxState = {
createdAt: new Date(),
marketId: 'market-1',
status: OrderStatus.STATUS_ACTIVE,
remaining: '10',
},
};
@@ -489,6 +491,7 @@ describe('getRejectionReason', () => {
timeInForce: Types.OrderTimeInForce.TIME_IN_FORCE_FOK,
side: Types.Side.SIDE_BUY,
marketId: '',
remaining: '',
})
).toBe('Insufficient asset balance');
});
@@ -505,6 +508,7 @@ describe('getRejectionReason', () => {
timeInForce: Types.OrderTimeInForce.TIME_IN_FORCE_FOK,
side: Types.Side.SIDE_BUY,
marketId: '',
remaining: '',
})
).toBe(
'Your Fill or Kill (FOK) order was not filled and it has been stopped'
@@ -41,6 +41,7 @@ import {
toBigNum,
truncateByChars,
formatTrigger,
MAXGOINT64,
} from '@vegaprotocol/utils';
import { t } from '@vegaprotocol/i18n';
import { useAssetsMapProvider } from '@vegaprotocol/assets';
@@ -585,6 +586,23 @@ export const VegaTransactionDetails = ({ tx }: { tx: VegaStoredTxState }) => {
<Panel>
{t('Close position for')}{' '}
<strong>{market.tradableInstrument.instrument.code}</strong>
{tx.order?.remaining && (
<p>
{t('Filled')}{' '}
<SizeAtPrice
meta={{
positionDecimalPlaces: market.positionDecimalPlaces,
decimalPlaces: market.decimalPlaces,
asset: getAsset(market).symbol,
}}
side={tx.order.side}
size={(
BigInt(tx.order.size) - BigInt(tx.order.remaining)
).toString()}
price={tx.order.price}
/>
</p>
)}
</Panel>
);
}
@@ -953,7 +971,19 @@ export const getVegaTransactionContentIntent = (tx: VegaStoredTxState) => {
isWithdrawTransaction(tx.body) &&
Intent.Warning;
// Toast for an IOC should go green when it is stopped,
// because stopping an IOC once all available volume has filled is the correct behaviour (it is immediate or cancel),
// this behaviour should apply to all IOC, not just those created due to "Close position"
const intentForClosedPosition =
tx.order &&
tx.order.status === Schema.OrderStatus.STATUS_STOPPED &&
tx.order.timeInForce === Schema.OrderTimeInForce.TIME_IN_FORCE_IOC &&
tx.order.size === MAXGOINT64 &&
// isClosePositionTransaction(tx) &&
Intent.Success;
const intent =
intentForClosedPosition ||
intentForRejectedOrder ||
intentForCompletedWithdrawal ||
intentMap[tx.status];
@@ -76,6 +76,7 @@ const orderUpdate: OrderTxUpdateFieldsFragment = {
createdAt: '2022-07-05T14:25:47.815283706Z',
expiresAt: '2022-07-05T14:25:47.815283706Z',
size: '10',
remaining: '10',
price: '300000',
timeInForce: OrderTimeInForce.TIME_IN_FORCE_GTC,
side: Side.SIDE_BUY,