fix(trading): stabilize the liquidity provision table (#3320)

Co-authored-by: Bartłomiej Głownia <bglownia@gmail.com>
This commit is contained in:
Maciek 2023-03-31 12:41:16 +02:00 committed by GitHub
parent b9c4057ce5
commit c899da52c2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 229 additions and 153 deletions

View File

@ -1,4 +1,5 @@
import { import {
getId,
liquidityProvisionsDataProvider, liquidityProvisionsDataProvider,
LiquidityTable, LiquidityTable,
lpAggregatedDataProvider, lpAggregatedDataProvider,
@ -25,7 +26,7 @@ import {
Indicator, Indicator,
} from '@vegaprotocol/ui-toolkit'; } from '@vegaprotocol/ui-toolkit';
import { useVegaWallet } from '@vegaprotocol/wallet'; import { useVegaWallet } from '@vegaprotocol/wallet';
import { useCallback, useEffect, useMemo, useRef } from 'react'; import { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { Header, HeaderStat, HeaderTitle } from '../../components/header'; import { Header, HeaderStat, HeaderTitle } from '../../components/header';
@ -37,6 +38,13 @@ import { Link, useParams } from 'react-router-dom';
import { Links, Routes } from '../../pages/client-router'; import { Links, Routes } from '../../pages/client-router';
import { useMarket, useStaticMarketData } from '@vegaprotocol/market-list'; import { useMarket, useStaticMarketData } from '@vegaprotocol/market-list';
import isEqual from 'lodash/isEqual';
const enum LiquidityTabs {
Active = 'active',
Inactive = 'inactive',
MyLiquidityProvision = 'myLP',
}
export const Liquidity = () => { export const Liquidity = () => {
const params = useParams(); const params = useParams();
@ -48,10 +56,11 @@ const useReloadLiquidityData = (marketId: string | undefined) => {
const { reload } = useDataProvider({ const { reload } = useDataProvider({
dataProvider: liquidityProvisionsDataProvider, dataProvider: liquidityProvisionsDataProvider,
variables: { marketId: marketId || '' }, variables: { marketId: marketId || '' },
update: () => true,
skip: !marketId, skip: !marketId,
}); });
useEffect(() => { useEffect(() => {
const interval = setInterval(reload, 10000); const interval = setInterval(reload, 30000);
return () => clearInterval(interval); return () => clearInterval(interval);
}, [reload]); }, [reload]);
}; };
@ -126,47 +135,9 @@ export const LiquidityContainer = ({
); );
}; };
export const LiquidityViewContainer = ({ const LiquidityViewHeader = memo(({ marketId }: { marketId?: string }) => {
marketId,
}: {
marketId: string | undefined;
}) => {
const { pubKey } = useVegaWallet();
const gridRef = useRef<AgGridReact | null>(null);
const { data: market } = useMarket(marketId); const { data: market } = useMarket(marketId);
const { data: marketData } = useStaticMarketData(marketId); const { data: marketData } = useStaticMarketData(marketId);
const dataRef = useRef<LiquidityProvisionData[] | null>(null);
// To be removed when liquidityProvision subscriptions are working
useReloadLiquidityData(marketId);
const update = useCallback(
({ data }: { data: LiquidityProvisionData[] | null }) => {
if (!gridRef.current?.api) {
return false;
}
if (dataRef.current?.length) {
dataRef.current = data;
gridRef.current.api.refreshInfiniteCache();
return true;
}
return false;
},
[gridRef]
);
const {
data: liquidityProviders,
loading,
error,
} = useDataProvider({
dataProvider: lpAggregatedDataProvider,
update,
variables: { marketId: marketId || '' },
skip: !marketId,
});
const targetStake = marketData?.targetStake; const targetStake = marketData?.targetStake;
const suppliedStake = marketData?.suppliedStake; const suppliedStake = marketData?.suppliedStake;
const assetDecimalPlaces = const assetDecimalPlaces =
@ -178,44 +149,8 @@ export const LiquidityViewContainer = ({
NetworkParams.market_liquidity_stakeToCcyVolume, NetworkParams.market_liquidity_stakeToCcyVolume,
NetworkParams.market_liquidity_targetstake_triggering_ratio, NetworkParams.market_liquidity_targetstake_triggering_ratio,
]); ]);
const stakeToCcyVolume = params.market_liquidity_stakeToCcyVolume;
const triggeringRatio = const triggeringRatio =
params.market_liquidity_targetstake_triggering_ratio || '1'; params.market_liquidity_targetstake_triggering_ratio || '1';
const myLpEdges = useMemo(
() => liquidityProviders?.filter((e) => e.party.id === pubKey),
[liquidityProviders, pubKey]
);
const activeEdges = useMemo(
() =>
liquidityProviders?.filter(
(e) => e.status === Schema.LiquidityProvisionStatus.STATUS_ACTIVE
),
[liquidityProviders]
);
const inactiveEdges = useMemo(
() =>
liquidityProviders?.filter(
(e) => e.status !== Schema.LiquidityProvisionStatus.STATUS_ACTIVE
),
[liquidityProviders]
);
const enum LiquidityTabs {
Active = 'active',
Inactive = 'inactive',
MyLiquidityProvision = 'myLP',
}
const getActiveDefaultId = () => {
if (myLpEdges && myLpEdges.length > 0) {
return LiquidityTabs.MyLiquidityProvision;
}
if (activeEdges?.length) return LiquidityTabs.Active;
else if (inactiveEdges && inactiveEdges.length > 0) {
return LiquidityTabs.Inactive;
}
return LiquidityTabs.Active;
};
const { percentage, status } = useCheckLiquidityStatus({ const { percentage, status } = useCheckLiquidityStatus({
suppliedStake: suppliedStake || 0, suppliedStake: suppliedStake || 0,
@ -224,17 +159,15 @@ export const LiquidityViewContainer = ({
}); });
return ( return (
<AsyncRenderer loading={loading} error={error} data={liquidityProviders}>
<div className="h-full grid grid-rows-[min-content_1fr]">
<Header <Header
title={ title={
market?.tradableInstrument.instrument.name && market?.tradableInstrument.instrument.name &&
market?.tradableInstrument.instrument.code && market?.tradableInstrument.instrument.code &&
marketId && ( marketId && (
<HeaderTitle <HeaderTitle
primaryContent={`${ primaryContent={`${market.tradableInstrument.instrument.code} ${t(
market.tradableInstrument.instrument.code 'liquidity provision'
} ${t('liquidity provision')}`} )}`}
secondaryContent={ secondaryContent={
<Link to={Links[Routes.MARKET](marketId)}> <Link to={Links[Routes.MARKET](marketId)}>
<UiToolkitLink>{t('Go to trading')}</UiToolkitLink> <UiToolkitLink>{t('Go to trading')}</UiToolkitLink>
@ -270,10 +203,7 @@ export const LiquidityViewContainer = ({
: '-'} : '-'}
</div> </div>
</HeaderStat> </HeaderStat>
<HeaderStat <HeaderStat heading={t('Liquidity supplied')} testId="liquidity-supplied">
heading={t('Liquidity supplied')}
testId="liquidity-supplied"
>
<Indicator variant={status} /> <Indicator variant={status} />
{formatNumberPercentage(percentage, 2)} {formatNumberPercentage(percentage, 2)}
@ -282,7 +212,143 @@ export const LiquidityViewContainer = ({
<div className="break-word">{marketId}</div> <div className="break-word">{marketId}</div>
</HeaderStat> </HeaderStat>
</Header> </Header>
<Tabs defaultValue={getActiveDefaultId()}> );
});
LiquidityViewHeader.displayName = 'LiquidityViewHeader';
const filterLiquidities = (
tab: string,
liquidities?: LiquidityProvisionData[] | null,
pubKey?: string | null
) => {
switch (tab) {
case LiquidityTabs.MyLiquidityProvision:
return pubKey
? (liquidities || []).filter((e) => e.party.id === pubKey)
: [];
break;
case LiquidityTabs.Active:
return (liquidities || []).filter(
(e) => e.status === Schema.LiquidityProvisionStatus.STATUS_ACTIVE
);
case LiquidityTabs.Inactive:
return (liquidities || []).filter(
(e) => e.status !== Schema.LiquidityProvisionStatus.STATUS_ACTIVE
);
default:
return [];
}
};
export const LiquidityViewContainer = ({
marketId,
}: {
marketId: string | undefined;
}) => {
const [tab, setTab] = useState('');
const { pubKey } = useVegaWallet();
const [liquidityProviders, setLiquidityProviders] = useState<
LiquidityProvisionData[] | null
>();
const gridRef = useRef<AgGridReact | null>(null);
const { data: market } = useMarket(marketId);
const dataRef = useRef<LiquidityProvisionData[] | null>(null);
// To be removed when liquidityProvision subscriptions are working
useReloadLiquidityData(marketId);
const update = useCallback(
({ data }: { data: LiquidityProvisionData[] | null }) => {
if (!dataRef.current) {
setLiquidityProviders(data);
dataRef.current = data;
}
if (!gridRef.current?.api) {
return false;
}
const updateRows: LiquidityProvisionData[] = [];
const addRows: LiquidityProvisionData[] = [];
if (gridRef.current?.api?.getModel().getType() === 'infinite') {
dataRef.current = data;
gridRef.current.api.refreshInfiniteCache();
} else {
const filteredData = filterLiquidities(tab, data, pubKey as string);
filteredData?.forEach((d) => {
const rowNode = gridRef.current?.api?.getRowNode(getId(d));
if (rowNode) {
if (!isEqual(rowNode.data, d)) {
updateRows.push(d);
}
} else {
addRows.push(d);
}
});
gridRef.current?.api?.applyTransaction({
update: updateRows,
add: addRows,
addIndex: 0,
});
}
return true;
},
[gridRef, tab, pubKey]
);
const { loading, error } = useDataProvider({
dataProvider: lpAggregatedDataProvider,
update,
variables: { marketId: marketId || '' },
skip: !marketId,
});
const assetDecimalPlaces =
market?.tradableInstrument.instrument.product.settlementAsset.decimals || 0;
const symbol =
market?.tradableInstrument.instrument.product.settlementAsset.symbol;
const { params } = useNetworkParams([
NetworkParams.market_liquidity_stakeToCcyVolume,
NetworkParams.market_liquidity_targetstake_triggering_ratio,
]);
const stakeToCcyVolume = params.market_liquidity_stakeToCcyVolume;
const myLpEdges = useMemo(
() =>
filterLiquidities(
LiquidityTabs.MyLiquidityProvision,
liquidityProviders,
pubKey
),
[liquidityProviders, pubKey]
);
const activeEdges = useMemo(
() => filterLiquidities(LiquidityTabs.Active, liquidityProviders),
[liquidityProviders]
);
const inactiveEdges = useMemo(
() => filterLiquidities(LiquidityTabs.Inactive, liquidityProviders),
[liquidityProviders]
);
useEffect(() => {
if (tab) {
return;
}
let initialTab = LiquidityTabs.Active;
if (myLpEdges.length > 0) {
initialTab = LiquidityTabs.MyLiquidityProvision;
}
if (activeEdges?.length) {
initialTab = LiquidityTabs.Active;
} else if (inactiveEdges.length > 0) {
initialTab = LiquidityTabs.Inactive;
}
setTab(initialTab);
}, [tab, myLpEdges?.length, activeEdges?.length, inactiveEdges?.length]);
return (
<AsyncRenderer loading={loading} error={error} data={liquidityProviders}>
<div className="h-full grid grid-rows-[min-content_1fr]">
<LiquidityViewHeader marketId={marketId} />
<Tabs value={tab} onValueChange={setTab}>
<Tab <Tab
id={LiquidityTabs.MyLiquidityProvision} id={LiquidityTabs.MyLiquidityProvision}
name={t('My liquidity provision')} name={t('My liquidity provision')}
@ -309,7 +375,6 @@ export const LiquidityViewContainer = ({
/> />
)} )}
</Tab> </Tab>
{
<Tab id={LiquidityTabs.Inactive} name={t('Inactive')}> <Tab id={LiquidityTabs.Inactive} name={t('Inactive')}>
{inactiveEdges && ( {inactiveEdges && (
<LiquidityTable <LiquidityTable
@ -321,7 +386,6 @@ export const LiquidityViewContainer = ({
/> />
)} )}
</Tab> </Tab>
}
</Tabs> </Tabs>
</div> </div>
</AsyncRenderer> </AsyncRenderer>

View File

@ -65,11 +65,23 @@ export const liquidityProvisionsDataProvider = makeDataProvider<
}); });
}, },
getData: (responseData: LiquidityProvisionsQuery | null) => { getData: (responseData: LiquidityProvisionsQuery | null) => {
// to remove dedupe after core fix https://github.com/vegaprotocol/vega/issues/8043
const dedupeArr: string[] = [];
return ( return (
responseData?.market?.liquidityProvisionsConnection?.edges?.map( responseData?.market?.liquidityProvisionsConnection?.edges?.map(
(e) => e?.node (e) => e?.node
) ?? [] ) ?? []
).filter((e) => !!e) as LiquidityProvisionFieldsFragment[]; ).filter((e) => {
if (e) {
const id = getId(e);
if (dedupeArr.includes(id)) {
return false;
}
dedupeArr.push(id);
return true;
}
return false;
}) as LiquidityProvisionFieldsFragment[];
}, },
getDelta: ( getDelta: (
subscriptionData: LiquidityProvisionsUpdateSubscription subscriptionData: LiquidityProvisionsUpdateSubscription
@ -96,8 +108,8 @@ export const getId = (
> >
) => ) =>
isLpFragment(entry) isLpFragment(entry)
? `${entry.party.id}${entry.status}${entry.createdAt}` ? `${entry.party.id}${entry.status}${entry.createdAt}${entry.updatedAt}`
: `${entry.partyID}${entry.status}${entry.createdAt}`; : `${entry.partyID}${entry.status}${entry.createdAt}${entry.updatedAt}`;
export const marketLiquidityDataProvider = makeDataProvider< export const marketLiquidityDataProvider = makeDataProvider<
MarketLpQuery, MarketLpQuery,

View File

@ -339,7 +339,7 @@ function makeDataProviderInternal<
} }
}; };
const initialFetch = async () => { const initialFetch = async (isUpdate = false) => {
if (!client) { if (!client) {
return; return;
} }
@ -394,7 +394,7 @@ function makeDataProviderInternal<
subscription = undefined; subscription = undefined;
} finally { } finally {
loading = false; loading = false;
notifyAll(); notifyAll({ isUpdate });
} }
}; };
@ -410,7 +410,7 @@ function makeDataProviderInternal<
} else { } else {
loading = true; loading = true;
error = undefined; error = undefined;
initialFetch(); initialFetch(true);
} }
}; };