feat(trading): calculate required margin base on open volume, active … (#2957)

Co-authored-by: mattrussell36 <mattrussell36@users.noreply.github.com>
This commit is contained in:
Bartłomiej Głownia 2023-03-09 11:03:50 +01:00 committed by GitHub
parent 9d3fc04597
commit 6705eb4398
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
95 changed files with 1395 additions and 1398 deletions

View File

@ -4,9 +4,8 @@ import {
getMarketExpiryDateFormatted,
} from '@vegaprotocol/utils';
import { t } from '@vegaprotocol/i18n';
import type { MarketInfoNoCandlesQuery } from '@vegaprotocol/market-info';
import type { MarketInfoWithData } from '@vegaprotocol/market-info';
import { MarketInfoTable } from '@vegaprotocol/market-info';
import pick from 'lodash/pick';
import {
MarketStateMapping,
MarketTradingModeMapping,
@ -17,11 +16,7 @@ import BigNumber from 'bignumber.js';
import { useMemo } from 'react';
import { Link } from 'react-router-dom';
export const MarketDetails = ({
market,
}: {
market: MarketInfoNoCandlesQuery['market'];
}) => {
export const MarketDetails = ({ market }: { market: MarketInfoWithData }) => {
const quoteUnit = market?.tradableInstrument.instrument.product.quoteName;
const assetId = useMemo(
() => market?.tradableInstrument.instrument.product?.settlementAsset.id,
@ -32,7 +27,9 @@ export const MarketDetails = ({
if (!market) return null;
const keyDetails = {
...pick(market, 'decimalPlaces', 'positionDecimalPlaces', 'tradingMode'),
decimalPlaces: market.decimalPlaces,
positionDecimalPlaces: market.positionDecimalPlaces,
tradingMode: market.tradingMode,
state: MarketStateMapping[market.state],
};
const assetDecimals =

View File

@ -12,6 +12,7 @@ export const Proposals = () => {
const { data, loading, error } = useDataProvider({
dataProvider: proposalsDataProvider,
variables: {},
});
useDocumentTitle([t('Governance Proposals')]);

View File

@ -1,14 +1,14 @@
import { t } from '@vegaprotocol/i18n';
import { useDataProvider } from '@vegaprotocol/react-helpers';
import { AsyncRenderer, Button } from '@vegaprotocol/ui-toolkit';
import { useMemo, useState } from 'react';
import { useState } from 'react';
import { useParams } from 'react-router-dom';
import { MarketDetails } from '../../components/markets/market-details';
import { useScrollToLocation } from '../../hooks/scroll-to-location';
import { useDocumentTitle } from '../../hooks/use-document-title';
import compact from 'lodash/compact';
import { JsonViewerDialog } from '../../components/dialogs/json-viewer-dialog';
import { marketInfoNoCandlesDataProvider } from '@vegaprotocol/market-info';
import { marketInfoProvider } from '@vegaprotocol/market-info';
import { PageTitle } from '../../components/page-helpers/page-title';
export const MarketPage = () => {
@ -16,24 +16,17 @@ export const MarketPage = () => {
const { marketId } = useParams<{ marketId: string }>();
const variables = useMemo(
() => ({
marketId,
}),
[marketId]
);
const { data, loading, error } = useDataProvider({
dataProvider: marketInfoNoCandlesDataProvider,
dataProvider: marketInfoProvider,
skipUpdates: true,
variables,
variables: {
marketId: marketId || '',
skip: !marketId,
},
});
useDocumentTitle(
compact([
'Market details',
data?.market?.tradableInstrument.instrument.name,
])
compact(['Market details', data?.tradableInstrument.instrument.name])
);
const [dialogOpen, setDialogOpen] = useState<boolean>(false);
@ -43,10 +36,10 @@ export const MarketPage = () => {
<section className="relative">
<PageTitle
data-testid="markets-heading"
title={data?.market?.tradableInstrument.instrument.name || ''}
title={data?.tradableInstrument.instrument.name || ''}
actions={
<Button
disabled={!data?.market}
disabled={!data}
size="xs"
onClick={() => setDialogOpen(true)}
>
@ -60,14 +53,14 @@ export const MarketPage = () => {
loading={loading}
error={error}
>
<MarketDetails market={data?.market} />
{data && <MarketDetails market={data} />}
</AsyncRenderer>
</section>
<JsonViewerDialog
open={dialogOpen}
onChange={(isOpen) => setDialogOpen(isOpen)}
title={data?.market?.tradableInstrument.instrument.name || ''}
content={data?.market}
title={data?.tradableInstrument.instrument.name || ''}
content={data}
/>
</>
);

View File

@ -13,6 +13,7 @@ export const MarketsPage = () => {
const { data, loading, error } = useDataProvider({
dataProvider: marketsProvider,
variables: undefined,
skipUpdates: true,
});

View File

@ -1,5 +1,4 @@
import { useParams } from 'react-router-dom';
import { useMemo } from 'react';
import { makeDerivedDataProvider } from '@vegaprotocol/utils';
import { t } from '@vegaprotocol/i18n';
import { useDataProvider } from '@vegaprotocol/react-helpers';
@ -43,7 +42,7 @@ const useMarketDetails = (marketId: string | undefined) => {
const { data, loading, error } = useDataProvider({
dataProvider: lpDataProvider,
skipUpdates: true,
variables: useMemo(() => ({ marketId }), [marketId]),
variables: { marketId: marketId || '' },
});
const liquidityProviders = data?.liquidityProviders || [];

View File

@ -39,14 +39,11 @@ export const Last24hVolume = ({
[marketId, yTimestamp]
);
const variables24hAgo = useMemo(
() => ({
marketId: marketId,
interval: Schema.Interval.INTERVAL_I1D,
since: yTimestamp,
}),
[marketId, yTimestamp]
);
const variables24hAgo = {
marketId: marketId,
interval: Schema.Interval.INTERVAL_I1D,
since: yTimestamp,
};
const throttledSetCandles = useRef(
throttle((data: Candle[]) => {
@ -64,7 +61,7 @@ export const Last24hVolume = ({
[throttledSetCandles]
);
const { data, error } = useDataProvider<Candle[], Candle>({
const { data, error } = useDataProvider({
dataProvider: marketCandlesProvider,
variables: variables,
update,
@ -88,7 +85,7 @@ export const Last24hVolume = ({
[throttledSetVolumeChange]
);
useDataProvider<Candle[], Candle>({
useDataProvider({
dataProvider: marketCandlesProvider,
update: updateCandle24hAgo,
variables: variables24hAgo,

View File

@ -26,20 +26,20 @@ describe('market info is displayed', { tags: '@smoke' }, () => {
it('market price', () => {
cy.getByTestId(marketTitle).contains('Market price').click();
validateMarketDataRow(0, 'Mark Price', '0.05749');
validateMarketDataRow(1, 'Best Bid Price', '6.81765 ');
validateMarketDataRow(2, 'Best Offer Price', '6.81769 ');
validateMarketDataRow(0, 'Mark Price', '46,126.90058');
validateMarketDataRow(1, 'Best Bid Price', '44,126.90058 ');
validateMarketDataRow(2, 'Best Offer Price', '48,126.90058 ');
validateMarketDataRow(3, 'Quote Unit', 'BTC');
});
it('market volume displayed', () => {
cy.getByTestId(marketTitle).contains('Market volume').click();
validateMarketDataRow(0, '24 Hour Volume', '-');
validateMarketDataRow(0, '24 Hour Volume', '1');
validateMarketDataRow(1, 'Open Interest', '0');
validateMarketDataRow(2, 'Best Bid Volume', '5');
validateMarketDataRow(3, 'Best Offer Volume', '1');
validateMarketDataRow(4, 'Best Static Bid Volume', '5');
validateMarketDataRow(5, 'Best Static Offer Volume', '1');
validateMarketDataRow(2, 'Best Bid Volume', '1');
validateMarketDataRow(3, 'Best Offer Volume', '3');
validateMarketDataRow(4, 'Best Static Bid Volume', '2');
validateMarketDataRow(5, 'Best Static Offer Volume', '4');
});
it('insurance pool displayed', () => {
@ -149,9 +149,9 @@ describe('market info is displayed', { tags: '@smoke' }, () => {
.contains(/Liquidity(?! m)/)
.click();
validateMarketDataRow(0, 'Target Stake', '0.56789 tBTC');
validateMarketDataRow(1, 'Supplied Stake', '0.56767 tBTC');
validateMarketDataRow(2, 'Market Value Proxy', '6.77678 tBTC');
validateMarketDataRow(0, 'Target Stake', '10.00 tBTC');
validateMarketDataRow(1, 'Supplied Stake', '0.01 tBTC');
validateMarketDataRow(2, 'Market Value Proxy', '20.00 tBTC');
cy.getByTestId('view-liquidity-link').should(
'have.text',
@ -163,8 +163,8 @@ describe('market info is displayed', { tags: '@smoke' }, () => {
cy.getByTestId(marketTitle).contains('Liquidity price range').click();
validateMarketDataRow(0, 'Liquidity Price Range', '2.00% of mid price');
validateMarketDataRow(1, 'Lowest Price', '0.05634 BTC');
validateMarketDataRow(2, 'Highest Price', '0.05864 BTC');
validateMarketDataRow(1, 'Lowest Price', '45,204.362 BTC');
validateMarketDataRow(2, 'Highest Price', '47,049.438 BTC');
});
it('oracle displayed', () => {

View File

@ -38,7 +38,7 @@ describe('accounts', { tags: '@smoke' }, () => {
cy.getByTestId('tab-accounts')
.get(tradingAccountRowId)
.find('[col-id="deposited"]')
.should('have.text', '1,001.00');
.should('have.text', '100,001.01');
});
describe('sorting by ag-grid columns should work well', () => {
it('sorting by asset', () => {
@ -58,24 +58,24 @@ describe('accounts', { tags: '@smoke' }, () => {
cy.getByTestId('Collateral').click();
const marketsSortedDefault = [
'1,000.00002',
'1,001.00',
'1,000.01',
'100,001.01',
'1,000.01',
'1,000.00',
'1,000.00001',
];
const marketsSortedAsc = [
'1,000.00',
'1,000.00001',
'1,000.00002',
'1,000.01',
'1,000.01',
'1,001.00',
'100,001.01',
];
const marketsSortedDesc = [
'1,001.00',
'1,000.01',
'100,001.01',
'1,000.01',
'1,000.00002',
'1,000.00001',
'1,000.00',
];
checkSorting(
'deposited',
@ -87,9 +87,9 @@ describe('accounts', { tags: '@smoke' }, () => {
it('sorting by used', () => {
cy.getByTestId('Collateral').click();
const marketsSortedDefault = ['0.00', '1.00', '0.01', '0.01', '0.00'];
const marketsSortedAsc = ['0.00', '0.00', '0.01', '0.01', '1.00'];
const marketsSortedDesc = ['1.00', '0.01', '0.01', '0.00', '0.00'];
const marketsSortedDefault = ['0.00', '1.01', '0.01', '0.00', '0.00'];
const marketsSortedAsc = ['0.00', '0.00', '0.00', '0.01', '1.01'];
const marketsSortedDesc = ['1.01', '0.01', '0.00', '0.00', '0.00'];
checkSorting(
'used',
marketsSortedDefault,
@ -102,24 +102,24 @@ describe('accounts', { tags: '@smoke' }, () => {
cy.getByTestId('Collateral').click();
const marketsSortedDefault = [
'1,000.00002',
'1,000.00',
'100,000.00',
'1,000.00',
'1,000.00',
'1,000.00001',
];
const marketsSortedAsc = [
'1,000.00',
'1,000.00',
'1,000.00',
'1,000.00001',
'1,000.00002',
'100,000.00',
];
const marketsSortedDesc = [
'100,000.00',
'1,000.00002',
'1,000.00001',
'1,000.00',
'1,000.00',
'1,000.00',
];
checkSorting(

View File

@ -2,7 +2,11 @@ import * as Schema from '@vegaprotocol/types';
import { aliasGQLQuery, mockConnectWallet } from '@vegaprotocol/cypress';
import { testOrderSubmission } from '../support/order-validation';
import type { OrderSubmission } from '@vegaprotocol/wallet';
import { accountsQuery, estimateOrderQuery } from '@vegaprotocol/mock';
import {
accountsQuery,
estimateOrderQuery,
amendGeneralAccountBalance,
} from '@vegaprotocol/mock';
import { createOrder } from '../support/create-order';
const orderSizeField = 'order-size';
@ -583,6 +587,10 @@ describe('suspended market validation', { tags: '@regression' }, () => {
Schema.MarketTradingMode.TRADING_MODE_MONITORING_AUCTION,
Schema.AuctionTrigger.AUCTION_TRIGGER_LIQUIDITY
);
const accounts = accountsQuery();
cy.mockGQL((req) => {
aliasGQLQuery(req, 'Accounts', accounts);
});
cy.mockSubscription();
cy.visit('/#/markets/market-0');
cy.wait('@Markets');
@ -632,30 +640,10 @@ describe('account validation', { tags: '@regression' }, () => {
beforeEach(() => {
cy.setVegaWallet();
cy.mockTradingPage();
const accounts = accountsQuery();
amendGeneralAccountBalance(accounts, 'market-0', '0');
cy.mockGQL((req) => {
aliasGQLQuery(
req,
'Accounts',
accountsQuery({
party: {
accountsConnection: {
edges: [
{
node: {
type: Schema.AccountType.ACCOUNT_TYPE_GENERAL,
balance: '0',
market: null,
asset: {
__typename: 'Asset',
id: 'asset-0',
},
},
},
],
},
},
})
);
aliasGQLQuery(req, 'Accounts', accounts);
});
cy.mockSubscription();
cy.visit('/#/markets/market-0');
@ -679,19 +667,13 @@ describe('account validation', { tags: '@regression' }, () => {
beforeEach(() => {
cy.setVegaWallet();
cy.mockTradingPage();
const accounts = accountsQuery();
amendGeneralAccountBalance(accounts, 'market-0', '100000000');
cy.mockGQL((req) => {
aliasGQLQuery(
req,
'EstimateOrder',
estimateOrderQuery({
estimateOrder: {
marginLevels: {
__typename: 'MarginLevels',
initialLevel: '1000000000',
},
},
})
);
aliasGQLQuery(req, 'Accounts', accounts);
});
cy.mockGQL((req) => {
aliasGQLQuery(req, 'EstimateOrder', estimateOrderQuery());
});
cy.mockSubscription();
cy.visit('/#/markets/market-0');
@ -707,7 +689,7 @@ describe('account validation', { tags: '@regression' }, () => {
);
cy.getByTestId('dealticket-warning-margin').should(
'contain.text',
'9,999.99 tDAI is currently required. You have only 1,000.00 tDAI available.Deposit tDAI'
'You may not have enough margin available to open this position. 2,354.72283 tDAI is currently required. You have only 1,000.01 tDAI available.'
);
cy.getByTestId('deal-ticket-deposit-dialog-button').click();
cy.getByTestId('dialog-content')

View File

@ -31,7 +31,7 @@ describe('orders list', { tags: '@smoke' }, () => {
cy.visit('/#/markets/market-0');
cy.getByTestId('Orders').click();
cy.wait('@Orders').then(() => {
expect(subscriptionMocks.OrdersUpdate).to.be.calledTwice;
expect(subscriptionMocks.OrdersUpdate).to.be.calledThrice;
});
cy.wait('@Markets');
});
@ -136,7 +136,7 @@ describe('subscribe orders', { tags: '@smoke' }, () => {
cy.visit('/#/markets/market-0');
cy.getByTestId('Orders').click();
cy.wait('@Orders').then(() => {
expect(subscriptionMocks.OrdersUpdate).to.be.calledTwice;
expect(subscriptionMocks.OrdersUpdate).to.be.calledThrice;
});
});
const orderId = '1234567890';
@ -354,7 +354,7 @@ describe('amend and cancel order', { tags: '@smoke' }, () => {
cy.visit('/#/markets/market-0');
cy.getByTestId('Orders').click();
cy.wait('@Orders').then(() => {
expect(subscriptionMocks.OrdersUpdate).to.be.calledTwice;
expect(subscriptionMocks.OrdersUpdate).to.be.calledThrice;
});
cy.mockVegaWalletTransaction();
});

View File

@ -164,10 +164,10 @@ describe('positions', { tags: '@smoke' }, () => {
cy.get('[col-id="liquidationPrice"]').should('contain.text', '0'); // liquidation price
cy.get('[col-id="currentLeverage"]').should('contain.text', '138.446.1');
cy.get('[col-id="currentLeverage"]').should('contain.text', '2.846.1');
cy.get('[col-id="marginAccountBalance"]') // margin allocated
.should('contain.text', '1,000');
.should('contain.text', '0.01');
cy.get('[col-id="unrealisedPNL"]').each(($unrealisedPnl) => {
cy.wrap($unrealisedPnl).invoke('text').should('not.be.empty');

View File

@ -7,13 +7,13 @@ import type { onMessage } from '@vegaprotocol/cypress';
import type { PartialDeep } from 'type-fest';
import { orderUpdateSubscription } from '@vegaprotocol/mock';
let sendOrderUpdate: (data: OrdersUpdateSubscription) => void;
const sendOrderUpdate: ((data: OrdersUpdateSubscription) => void)[] = [];
const getOnOrderUpdate = () => {
const onOrderUpdate: onMessage<
OrdersUpdateSubscription,
OrdersUpdateSubscriptionVariables
> = (send) => {
sendOrderUpdate = send;
sendOrderUpdate.push(send);
};
return onOrderUpdate;
};
@ -31,5 +31,5 @@ export function updateOrder(
if (!sendOrderUpdate) {
throw new Error('OrderSub not called');
}
sendOrderUpdate(update);
sendOrderUpdate.forEach((send) => send(update));
}

View File

@ -28,7 +28,6 @@ import {
} from '@vegaprotocol/mock';
import type { PartialDeep } from 'type-fest';
import type { MarketDataQuery, MarketsQuery } from '@vegaprotocol/market-list';
import type { MarketInfoQuery } from '@vegaprotocol/market-info';
type MarketPageMockData = {
state: Schema.MarketState;
@ -69,18 +68,6 @@ const marketsDataOverride = (
},
});
const marketInfoOverride = (
data: MarketPageMockData
): PartialDeep<MarketInfoQuery> => ({
market: {
state: data.state,
tradingMode: data.tradingMode,
data: {
trigger: data.trigger,
},
},
});
const mockTradingPage = (
req: CyHttpMessages.IncomingHttpRequest,
state: Schema.MarketState = Schema.MarketState.STATE_ACTIVE,
@ -109,11 +96,7 @@ const mockTradingPage = (
aliasGQLQuery(req, 'Margins', marginsQuery());
aliasGQLQuery(req, 'Assets', assetsQuery());
aliasGQLQuery(req, 'Asset', assetQuery());
aliasGQLQuery(
req,
'MarketInfo',
marketInfoQuery(marketInfoOverride({ state, tradingMode, trigger }))
);
aliasGQLQuery(req, 'MarketInfo', marketInfoQuery());
aliasGQLQuery(req, 'Trades', tradesQuery());
aliasGQLQuery(req, 'Chart', chartQuery());
aliasGQLQuery(req, 'Candles', candlesQuery());

View File

@ -12,6 +12,7 @@ export const Home = () => {
// should be the oldest market that is currently trading in us mode(i.e. not in auction).
const { data, error, loading } = useDataProvider({
dataProvider: marketsWithDataProvider,
variables: undefined,
});
const update = useGlobalStore((store) => store.update);
const marketId = useGlobalStore((store) => store.marketId);

View File

@ -47,7 +47,8 @@ export const Liquidity = () => {
const useReloadLiquidityData = (marketId: string | undefined) => {
const { reload } = useDataProvider({
dataProvider: liquidityProvisionsDataProvider,
variables: useMemo(() => ({ marketId }), [marketId]),
variables: { marketId: marketId || '' },
skip: !marketId,
});
useEffect(() => {
const interval = setInterval(reload, 10000);
@ -77,7 +78,8 @@ export const LiquidityContainer = ({
const { data, loading, error } = useDataProvider({
dataProvider: lpAggregatedDataProvider,
update,
variables: useMemo(() => ({ marketId }), [marketId]),
variables: { marketId: marketId || '' },
skip: !marketId,
});
const assetDecimalPlaces =
@ -161,7 +163,8 @@ export const LiquidityViewContainer = ({
} = useDataProvider({
dataProvider: lpAggregatedDataProvider,
update,
variables: useMemo(() => ({ marketId }), [marketId]),
variables: { marketId: marketId || '' },
skip: !marketId,
});
const targetStake = marketData?.targetStake;

View File

@ -7,10 +7,6 @@ import {
useThrottledDataProvider,
} from '@vegaprotocol/react-helpers';
import { AsyncRenderer, ExternalLink, Splash } from '@vegaprotocol/ui-toolkit';
import type {
MarketData,
MarketDataUpdateFieldsFragment,
} from '@vegaprotocol/market-list';
import { marketProvider, marketDataProvider } from '@vegaprotocol/market-list';
import { useGlobalStore, usePageTitleStore } from '../../stores';
@ -35,13 +31,10 @@ const TitleUpdater = ({
}) => {
const pageTitle = usePageTitleStore((store) => store.pageTitle);
const updateTitle = usePageTitleStore((store) => store.updateTitle);
const { data: marketData } = useThrottledDataProvider<
MarketData,
MarketDataUpdateFieldsFragment
>(
const { data: marketData } = useThrottledDataProvider(
{
dataProvider: marketDataProvider,
variables: useMemo(() => ({ marketId }), [marketId]),
variables: { marketId: marketId || '' },
skip: !marketId,
},
1000

View File

@ -1,5 +1,4 @@
import type { RefObject } from 'react';
import { useMemo } from 'react';
import { useInView } from 'react-intersection-observer';
import { isNumeric } from '@vegaprotocol/utils';
import {
@ -9,7 +8,6 @@ import {
import { PriceChangeCell } from '@vegaprotocol/datagrid';
import * as Schema from '@vegaprotocol/types';
import type { CandleClose } from '@vegaprotocol/types';
import type { Candle } from '@vegaprotocol/market-list';
import { marketCandlesProvider } from '@vegaprotocol/market-list';
import { THROTTLE_UPDATE_TIME } from '../constants';
@ -30,19 +28,14 @@ export const Last24hPriceChange = ({
}: Props) => {
const [ref, inView] = useInView({ root: inViewRoot?.current });
const yesterday = useYesterday();
const variables = useMemo(
() => ({
marketId: marketId,
interval: Schema.Interval.INTERVAL_I1H,
since: new Date(yesterday).toISOString(),
}),
[marketId, yesterday]
);
const { data, error } = useThrottledDataProvider<Candle[], Candle>(
const { data, error } = useThrottledDataProvider(
{
dataProvider: marketCandlesProvider,
variables,
variables: {
marketId: marketId || '',
interval: Schema.Interval.INTERVAL_I1H,
since: new Date(yesterday).toISOString(),
},
skip: !marketId || !inView,
},
THROTTLE_UPDATE_TIME

View File

@ -10,8 +10,6 @@ import {
useYesterday,
} from '@vegaprotocol/react-helpers';
import * as Schema from '@vegaprotocol/types';
import { useMemo } from 'react';
import type { Candle } from '@vegaprotocol/market-list';
import { THROTTLE_UPDATE_TIME } from '../constants';
interface Props {
@ -32,19 +30,14 @@ export const Last24hVolume = ({
const yesterday = useYesterday();
const [ref, inView] = useInView({ root: inViewRoot?.current });
const variables = useMemo(
() => ({
marketId: marketId,
interval: Schema.Interval.INTERVAL_I1H,
since: new Date(yesterday).toISOString(),
}),
[marketId, yesterday]
);
const { data } = useThrottledDataProvider<Candle[], Candle>(
const { data } = useThrottledDataProvider(
{
dataProvider: marketCandlesProvider,
variables,
variables: {
marketId: marketId || '',
interval: Schema.Interval.INTERVAL_I1H,
since: new Date(yesterday).toISOString(),
},
skip: !(inView && marketId),
},
THROTTLE_UPDATE_TIME

View File

@ -4,10 +4,7 @@ import {
useDataProvider,
useNetworkParams,
} from '@vegaprotocol/react-helpers';
import type {
MarketData,
MarketDataUpdateFieldsFragment,
} from '@vegaprotocol/market-list';
import type { MarketData } from '@vegaprotocol/market-list';
import { marketDataProvider, marketProvider } from '@vegaprotocol/market-list';
import { HeaderStat } from '../header';
import {
@ -70,7 +67,7 @@ export const MarketLiquiditySupplied = ({
[noUpdate]
);
useDataProvider<MarketData, MarketDataUpdateFieldsFragment>({
useDataProvider({
dataProvider: marketDataProvider,
update,
variables,

View File

@ -1,13 +1,8 @@
import type { RefObject } from 'react';
import { useMemo } from 'react';
import { useInView } from 'react-intersection-observer';
import { addDecimalsFormatNumber, isNumeric } from '@vegaprotocol/utils';
import { useThrottledDataProvider } from '@vegaprotocol/react-helpers';
import { PriceCell } from '@vegaprotocol/datagrid';
import type {
MarketData,
MarketDataUpdateFieldsFragment,
} from '@vegaprotocol/market-list';
import { marketDataProvider } from '@vegaprotocol/market-list';
import { THROTTLE_UPDATE_TIME } from '../constants';
@ -27,15 +22,10 @@ export const MarketMarkPrice = ({
asPriceCell,
}: Props) => {
const [ref, inView] = useInView({ root: inViewRoot?.current });
const variables = useMemo(() => ({ marketId }), [marketId]);
const { data } = useThrottledDataProvider<
MarketData,
MarketDataUpdateFieldsFragment
>(
const { data } = useThrottledDataProvider(
{
dataProvider: marketDataProvider,
variables,
variables: { marketId: marketId || '' },
skip: !inView,
},
THROTTLE_UPDATE_TIME

View File

@ -1,15 +1,11 @@
import throttle from 'lodash/throttle';
import type {
MarketData,
MarketDataUpdateFieldsFragment,
Market,
} from '@vegaprotocol/market-list';
import type { MarketData, Market } from '@vegaprotocol/market-list';
import { marketDataProvider } from '@vegaprotocol/market-list';
import { t } from '@vegaprotocol/i18n';
import { useDataProvider } from '@vegaprotocol/react-helpers';
import * as Schema from '@vegaprotocol/types';
import { HeaderStat } from '../header';
import { useCallback, useMemo, useRef, useState } from 'react';
import { useCallback, useRef, useState } from 'react';
import * as constants from '../constants';
export const MarketState = ({ market }: { market: Market | null }) => {
@ -33,14 +29,10 @@ export const MarketState = ({ market }: { market: Market | null }) => {
[throttledSetMarketState]
);
const variables = useMemo(
() => ({ marketId: market?.id || '' }),
[market?.id]
);
useDataProvider<MarketData, MarketDataUpdateFieldsFragment>({
useDataProvider({
dataProvider: marketDataProvider,
update,
variables,
variables: { marketId: market?.id || '' },
skip: !market?.id,
});

View File

@ -1,24 +1,16 @@
import { useCallback, useMemo, useRef, useState } from 'react';
import { useCallback, useRef, useState } from 'react';
import throttle from 'lodash/throttle';
import { addDecimalsFormatNumber } from '@vegaprotocol/utils';
import { t } from '@vegaprotocol/i18n';
import { useDataProvider } from '@vegaprotocol/react-helpers';
import type {
MarketData,
MarketDataUpdateFieldsFragment,
} from '@vegaprotocol/market-list';
import type { MarketData } from '@vegaprotocol/market-list';
import { marketDataProvider, marketProvider } from '@vegaprotocol/market-list';
import { HeaderStat } from '../header';
import * as constants from '../constants';
export const MarketVolume = ({ marketId }: { marketId: string }) => {
const [marketVolume, setMarketVolume] = useState<string>('-');
const variables = useMemo(
() => ({
marketId: marketId,
}),
[marketId]
);
const variables = { marketId };
const { data } = useDataProvider({
dataProvider: marketProvider,
variables,
@ -46,7 +38,7 @@ export const MarketVolume = ({ marketId }: { marketId: string }) => {
[data?.positionDecimalPlaces, throttledSetMarketVolume]
);
useDataProvider<MarketData, MarketDataUpdateFieldsFragment>({
useDataProvider({
dataProvider: marketDataProvider,
update,
variables,

View File

@ -106,14 +106,13 @@ export const SelectMarketPopover = ({
loading: marketsLoading,
reload: marketListReload,
} = useMarketList();
const variables = useMemo(() => ({ partyId: pubKey }), [pubKey]);
const {
data: positions,
loading: positionsLoading,
reload,
} = useDataProvider({
dataProvider: positionsDataProvider,
variables,
variables: { partyId: pubKey || '' },
skip: !pubKey,
});
const onSelectMarket = useCallback(

View File

@ -20,6 +20,7 @@ export const WelcomeDialog = () => {
const [riskAccepted] = useLocalStorage(constants.RISK_ACCEPTED_KEY);
const { data } = useDataProvider({
dataProvider: activeMarketsProvider,
variables: undefined,
});
const { update, shouldDisplayWelcomeDialog } = useGlobalStore((store) => ({

View File

@ -18,6 +18,7 @@ import type {
AccountFieldsFragment,
AccountsQuery,
AccountEventsSubscription,
AccountsQueryVariables,
} from './__generated__/Accounts';
import type { Market } from '@vegaprotocol/market-list';
import type { Asset } from '@vegaprotocol/assets';
@ -85,7 +86,8 @@ export const accountsOnlyDataProvider = makeDataProvider<
AccountsQuery,
AccountFieldsFragment[],
AccountEventsSubscription,
AccountEventsSubscription['accounts']
AccountEventsSubscription['accounts'],
AccountsQueryVariables
>({
query: AccountsDocument,
subscriptionQuery: AccountEventsDocument,
@ -159,8 +161,16 @@ const getAssetAccountAggregation = (
return { ...balanceAccount, breakdown };
};
export const accountsDataProvider = makeDerivedDataProvider<Account[], never>(
[accountsOnlyDataProvider, marketsProvider, assetsProvider],
export const accountsDataProvider = makeDerivedDataProvider<
Account[],
never,
AccountsQueryVariables
>(
[
accountsOnlyDataProvider,
(callback, client) => marketsProvider(callback, client, undefined),
(callback, client) => assetsProvider(callback, client, undefined),
],
([accounts, markets, assets]): Account[] | null => {
return accounts
? accounts
@ -194,7 +204,8 @@ export const accountsDataProvider = makeDerivedDataProvider<Account[], never>(
export const aggregatedAccountsDataProvider = makeDerivedDataProvider<
AccountFields[],
never
never,
AccountsQueryVariables
>(
[accountsDataProvider],
(parts) => parts[0] && getAccountData(parts[0] as Account[])

View File

@ -31,10 +31,7 @@ export const AccountManager = ({
}: AccountManagerProps) => {
const gridRef = useRef<AgGridReact | null>(null);
const variables = useMemo(() => ({ partyId }), [partyId]);
const { data, loading, error, reload } = useDataProvider<
AccountFields[],
never
>({
const { data, loading, error, reload } = useDataProvider({
dataProvider: aggregatedAccountsDataProvider,
variables,
});

View File

@ -1,9 +1,14 @@
import { forwardRef, useMemo, useState } from 'react';
import { addDecimalsFormatNumber, isNumeric } from '@vegaprotocol/utils';
import {
addDecimalsFormatNumber,
isNumeric,
toBigNum,
} from '@vegaprotocol/utils';
import { t } from '@vegaprotocol/i18n';
import type {
VegaICellRendererParams,
VegaValueFormatterParams,
VegaValueGetterParams,
} from '@vegaprotocol/datagrid';
import { Button, ButtonLink, Dialog } from '@vegaprotocol/ui-toolkit';
import { TooltipCellComponent } from '@vegaprotocol/ui-toolkit';
@ -117,17 +122,23 @@ export const AccountTable = forwardRef<AgGridReact, AccountTableProps>(
headerTooltip={t(
'This is the total amount of collateral used plus the amount available in your general account.'
)}
valueGetter={({
data,
}: VegaValueGetterParams<AccountFields, 'deposited'>) => {
return !data?.deposited
? undefined
: toBigNum(data.deposited, data.asset.decimals).toNumber();
}}
maxWidth={300}
cellRenderer={({
data,
value,
node,
}: VegaICellRendererParams<AccountFields, 'deposited'>) => {
const valueFormatted =
data &&
data.asset &&
isNumeric(value) &&
addDecimalsFormatNumber(value, data.asset.decimals);
isNumeric(data.deposited) &&
addDecimalsFormatNumber(data.deposited, data.asset.decimals);
return node.rowPinned ? (
<CenteredGridCellWrapper className="h-[30px] justify-end">
{valueFormatted}
@ -144,17 +155,23 @@ export const AccountTable = forwardRef<AgGridReact, AccountTableProps>(
headerTooltip={t(
'This is the amount of collateral used from your general account.'
)}
valueGetter={({
data,
}: VegaValueGetterParams<AccountFields, 'used'>) => {
return !data?.used
? undefined
: toBigNum(data.used, data.asset.decimals).toNumber();
}}
maxWidth={300}
cellRenderer={({
data,
value,
node,
}: VegaICellRendererParams<AccountFields, 'used'>) => {
const valueFormatted =
data &&
data.asset &&
isNumeric(value) &&
addDecimalsFormatNumber(value, data.asset.decimals);
isNumeric(data.used) &&
addDecimalsFormatNumber(data.used, data.asset.decimals);
return node.rowPinned ? (
<CenteredGridCellWrapper className="h-[30px] justify-end">
{valueFormatted}
@ -171,26 +188,31 @@ export const AccountTable = forwardRef<AgGridReact, AccountTableProps>(
headerTooltip={t(
'This is the amount of collateral available in your general account.'
)}
valueGetter={({
data,
}: VegaValueGetterParams<AccountFields, 'available'>) => {
return !data?.available
? undefined
: toBigNum(data.available, data.asset.decimals).toNumber();
}}
valueFormatter={({
value,
data,
}: VegaValueFormatterParams<AccountFields, 'available'>) =>
data &&
data.asset &&
isNumeric(value) &&
addDecimalsFormatNumber(value, data.asset.decimals)
isNumeric(data.available) &&
addDecimalsFormatNumber(data.available, data.asset.decimals)
}
maxWidth={300}
cellRenderer={({
data,
value,
node,
}: VegaICellRendererParams<AccountFields, 'available'>) => {
const valueFormatted =
data &&
data.asset &&
isNumeric(value) &&
addDecimalsFormatNumber(value, data.asset.decimals);
isNumeric(data.available) &&
addDecimalsFormatNumber(data.available, data.asset.decimals);
return node.rowPinned ? (
<CenteredGridCellWrapper className="h-[30px] justify-end">
{valueFormatted}

View File

@ -43,10 +43,6 @@ export const accountFields: AccountFieldsFragment[] = [
__typename: 'AccountBalance',
type: Schema.AccountType.ACCOUNT_TYPE_GENERAL,
balance: '100000000',
market: {
id: 'market-0',
__typename: 'Market',
},
asset: {
__typename: 'Asset',
id: 'asset-id-2',
@ -75,7 +71,7 @@ export const accountFields: AccountFieldsFragment[] = [
},
asset: {
__typename: 'Asset',
id: 'asset-id-2',
id: 'asset-0',
},
},
{
@ -94,7 +90,7 @@ export const accountFields: AccountFieldsFragment[] = [
{
__typename: 'AccountBalance',
type: Schema.AccountType.ACCOUNT_TYPE_GENERAL,
balance: '100000000',
balance: '10000000000',
market: null,
asset: {
__typename: 'Asset',
@ -141,3 +137,25 @@ export const accountEventsSubscription = (
};
return merge(defaultResult, override);
};
export const amendGeneralAccountBalance = (
accounts: AccountsQuery,
marketId: string,
balance: string
) => {
if (accounts.party?.accountsConnection?.edges) {
const marginAccount = accounts.party.accountsConnection.edges.find(
(edge) => edge?.node.market?.id === marketId
);
if (marginAccount) {
const generalAccount = accounts.party.accountsConnection.edges.find(
(edge) =>
edge?.node.asset.id === marginAccount.node.asset.id &&
!edge?.node.market
);
if (generalAccount) {
generalAccount.node.balance = balance;
}
}
}
};

View File

@ -20,7 +20,7 @@ export const TransferContainer = () => {
const { param } = useNetworkParam(NetworkParams.transfer_fee_factor);
const { data } = useDataProvider({
dataProvider: accountsDataProvider,
variables: { partyId: pubKey },
variables: { partyId: pubKey || '' },
skip: !pubKey,
});
const create = useVegaTransactionStore((store) => store.create);

View File

@ -1,8 +1,11 @@
import { makeDataProvider } from '@vegaprotocol/utils';
import { useDataProvider } from '@vegaprotocol/react-helpers';
import { useMemo } from 'react';
import type { AssetQuery, AssetFieldsFragment } from './__generated__/Asset';
import type {
AssetQuery,
AssetFieldsFragment,
AssetQueryVariables,
} from './__generated__/Asset';
import { AssetDocument } from './__generated__/Asset';
export type Asset = AssetFieldsFragment;
@ -15,21 +18,21 @@ export const getData = (responseData: AssetQuery | null | undefined) => {
return null;
};
export const assetProvider = makeDataProvider<AssetQuery, Asset, never, never>({
export const assetProvider = makeDataProvider<
AssetQuery,
Asset,
never,
never,
AssetQueryVariables
>({
query: AssetDocument,
getData,
});
export const useAssetDataProvider = (assetId: string) => {
const variables = useMemo(
() => ({
assetId,
}),
[assetId]
);
return useDataProvider({
dataProvider: assetProvider,
variables,
variables: { assetId: assetId || '' },
skip: !assetId,
});
};

View File

@ -40,4 +40,5 @@ export const enabledAssetsProvider = makeDerivedDataProvider<
export const useAssetsDataProvider = () =>
useDataProvider({
dataProvider: assetsProvider,
variables: undefined,
});

View File

@ -1,4 +1,4 @@
import { formatNumber } from '@vegaprotocol/utils';
import { addDecimalsFormatNumber } from '@vegaprotocol/utils';
import { t } from '@vegaprotocol/i18n';
import { Notification, Intent } from '@vegaprotocol/ui-toolkit';
import { useDepositDialog } from '@vegaprotocol/deposits';
@ -19,14 +19,14 @@ export const MarginWarning = ({ margin, balance, asset }: Props) => {
<Notification
intent={Intent.Warning}
testId="dealticket-warning-margin"
message={`You may not have enough margin available to open this position. ${formatNumber(
message={`You may not have enough margin available to open this position. ${addDecimalsFormatNumber(
margin,
asset.decimals
)} ${asset.symbol} ${t(
'is currently required. You have only'
)} ${formatNumber(balance, asset.decimals)} ${asset.symbol} ${t(
'available.'
)}`}
)} ${addDecimalsFormatNumber(balance, asset.decimals)} ${
asset.symbol
} ${t('available.')}`}
buttonProps={{
text: t(`Deposit ${asset.symbol}`),
action: () => openDepositDialog(asset.id),

View File

@ -1,4 +1,3 @@
import { useMemo } from 'react';
import { AsyncRenderer, Splash } from '@vegaprotocol/ui-toolkit';
import { t } from '@vegaprotocol/i18n';
import { useThrottledDataProvider } from '@vegaprotocol/react-helpers';
@ -29,7 +28,7 @@ export const DealTicketContainer = ({
} = useThrottledDataProvider(
{
dataProvider: marketDataProvider,
variables: useMemo(() => ({ marketId }), [marketId]),
variables: { marketId },
},
1000
);

View File

@ -1,6 +1,5 @@
import { Tooltip } from '@vegaprotocol/ui-toolkit';
import type { ReactNode } from 'react';
import { useMemo } from 'react';
import type { OrderSubmissionBody } from '@vegaprotocol/wallet';
import type { Market, MarketData } from '@vegaprotocol/market-list';
import {
@ -12,22 +11,51 @@ interface DealTicketFeeDetailsProps {
order: OrderSubmissionBody['orderSubmission'];
market: Market;
marketData: MarketData;
margin: string;
totalMargin: string;
balance: string;
}
export interface DealTicketFeeDetails {
export interface DealTicketFeeDetailProps {
label: string;
value?: string | number | null;
labelDescription?: string | ReactNode;
symbol?: string;
}
export const DealTicketFeeDetail = ({
label,
value,
labelDescription,
symbol,
}: DealTicketFeeDetailProps) => (
<div className="text-xs mt-2 flex justify-between items-center gap-4 flex-wrap">
<div>
<Tooltip description={labelDescription}>
<div>{label}</div>
</Tooltip>
</div>
<div className="text-neutral-500 dark:text-neutral-300">{`${value ?? '-'} ${
symbol || ''
}`}</div>
</div>
);
export const DealTicketFeeDetails = ({
order,
market,
marketData,
margin,
totalMargin,
balance,
}: DealTicketFeeDetailsProps) => {
const feeDetails = useFeeDealTicketDetails(order, market, marketData);
const details = useMemo(() => getFeeDetailsValues(feeDetails), [feeDetails]);
const details = getFeeDetailsValues({
...feeDetails,
margin,
totalMargin,
balance,
});
return (
<div>
{details.map(({ label, value, labelDescription, symbol }) => (

View File

@ -21,8 +21,7 @@ import {
Intent,
Notification,
} from '@vegaprotocol/ui-toolkit';
import { useOrderMarginValidation } from '../../hooks/use-order-margin-validation';
import { MarginWarning } from '../deal-ticket-validation/margin-warning';
import {
validateExpiration,
validateMarketState,
@ -32,11 +31,16 @@ import {
} from '../../utils';
import { ZeroBalanceError } from '../deal-ticket-validation/zero-balance-error';
import { SummaryValidationType } from '../../constants';
import { useHasNoBalance } from '../../hooks/use-has-no-balance';
import { useInitialMargin } from '../../hooks/use-initial-margin';
import type { Market, MarketData } from '@vegaprotocol/market-list';
import { MarginWarning } from '../deal-ticket-validation/margin-warning';
import {
useMarketAccountBalance,
useAccountBalance,
} from '@vegaprotocol/accounts';
import { OrderTimeInForce, OrderType } from '@vegaprotocol/types';
import { useOrderForm } from '../../hooks/use-order-form';
import type { OrderObj } from '@vegaprotocol/orders';
export interface DealTicketProps {
market: Market;
@ -67,17 +71,41 @@ export const DealTicket = ({
update,
handleSubmit,
} = useOrderForm(market.id);
const marketStateError = validateMarketState(marketData.marketState);
const hasNoBalance = useHasNoBalance(
market.tradableInstrument.instrument.product.settlementAsset.id
const asset = market.tradableInstrument.instrument.product.settlementAsset;
const { accountBalance: marginAccountBalance } = useMarketAccountBalance(
market.id
);
const { accountBalance: generalAccountBalance } = useAccountBalance(asset.id);
const balance = (
BigInt(marginAccountBalance) + BigInt(generalAccountBalance)
).toString();
const marketStateError = validateMarketState(marketData.marketState);
const hasNoBalance = generalAccountBalance === '0';
const marketTradingModeError = validateMarketTradingMode(
marketData.marketTradingMode
);
const normalizedOrder =
order &&
normalizeOrderSubmission(
order,
market.decimalPlaces,
market.positionDecimalPlaces
);
const { margin, totalMargin } = useInitialMargin(
market.id,
normalizedOrder?.size,
order?.side
);
const checkForErrors = useCallback(() => {
if (!pubKey) {
setError('summary', { message: t('No public key selected') });
setError('summary', {
message: t('No public key selected'),
type: SummaryValidationType.NoPubKey,
});
return;
}
@ -114,6 +142,7 @@ export const DealTicket = ({
useEffect(() => {
if (
(pubKey && errors.summary?.type === SummaryValidationType.NoPubKey) ||
(!hasNoBalance &&
errors.summary?.type === SummaryValidationType.NoCollateral) ||
(marketStateError === true &&
@ -125,6 +154,7 @@ export const DealTicket = ({
}
checkForErrors();
}, [
pubKey,
hasNoBalance,
marketStateError,
marketTradingModeError,
@ -254,9 +284,10 @@ export const DealTicket = ({
)}
<SummaryMessage
errorMessage={errors.summary?.message}
market={market}
marketData={marketData}
order={order}
asset={asset}
marketTradingMode={marketData.marketTradingMode}
balance={balance}
margin={totalMargin}
isReadOnly={isReadOnly}
pubKey={pubKey}
onClickCollateral={onClickCollateral}
@ -266,9 +297,16 @@ export const DealTicket = ({
variant={order.side === Schema.Side.SIDE_BUY ? 'ternary' : 'secondary'}
/>
<DealTicketFeeDetails
order={order}
order={normalizeOrderSubmission(
order,
market.decimalPlaces,
market.positionDecimalPlaces
)}
market={market}
marketData={marketData}
margin={margin}
totalMargin={totalMargin}
balance={marginAccountBalance}
/>
</form>
);
@ -280,9 +318,10 @@ export const DealTicket = ({
*/
interface SummaryMessageProps {
errorMessage?: string;
market: Market;
marketData: MarketData;
order: OrderObj;
asset: { id: string; symbol: string; name: string; decimals: number };
marketTradingMode: MarketData['marketTradingMode'];
balance: string;
margin: string;
isReadOnly: boolean;
pubKey: string | null;
onClickCollateral?: () => void;
@ -290,22 +329,17 @@ interface SummaryMessageProps {
const SummaryMessage = memo(
({
errorMessage,
market,
marketData,
order,
asset,
marketTradingMode,
balance,
margin,
isReadOnly,
pubKey,
onClickCollateral,
}: SummaryMessageProps) => {
// Specific error UI for if balance is so we can
// render a deposit dialog
const asset = market.tradableInstrument.instrument.product.settlementAsset;
const assetSymbol = asset.symbol;
const { balanceError, balance, margin } = useOrderMarginValidation({
market,
marketData,
order,
});
const openVegaWalletDialog = useVegaWalletDialogStore(
(store) => store.openVegaWalletDialog
);
@ -349,7 +383,7 @@ const SummaryMessage = memo(
return (
<div className="mb-2">
<ZeroBalanceError
asset={market.tradableInstrument.instrument.product.settlementAsset}
asset={asset}
onClickCollateral={onClickCollateral}
/>
</div>
@ -370,21 +404,16 @@ const SummaryMessage = memo(
// If there is no blocking error but user doesn't have enough
// balance render the margin warning, but still allow submission
if (balanceError) {
return (
<div className="mb-2">
<MarginWarning balance={balance} margin={margin} asset={asset} />
</div>
);
if (BigInt(balance) < BigInt(margin)) {
return <MarginWarning balance={balance} margin={margin} asset={asset} />;
}
// Show auction mode warning
if (
[
Schema.MarketTradingMode.TRADING_MODE_BATCH_AUCTION,
Schema.MarketTradingMode.TRADING_MODE_MONITORING_AUCTION,
Schema.MarketTradingMode.TRADING_MODE_OPENING_AUCTION,
].includes(marketData.marketTradingMode)
].includes(marketTradingMode)
) {
return (
<div className="mb-2">

View File

@ -55,6 +55,7 @@ export const MarketSelector = ({ market, setMarket, ItemRenderer }: Props) => {
const { data, loading, error } = useDataProvider({
dataProvider: marketsProvider,
variables: undefined,
skipUpdates: true,
});

View File

@ -7,6 +7,15 @@ export const EST_MARGIN_TOOLTIP_TEXT = (settlementAsset: string) =>
For example, for a notional size of $500, if the margin requirement is 10%, then the estimated margin would be approximately $50.`,
[settlementAsset]
);
export const EST_TOTAL_MARGIN_TOOLTIP_TEXT = t(
'Estimated total margin that will cover open position, active orders and this order.'
);
export const MARGIN_ACCOUNT_TOOLTIP_TEXT = t('Margin account balance');
export const MARGIN_DIFF_TOOLTIP_TEXT = (settlementAsset: string) =>
t(
"The additional margin required for your new position (taking into account volume and open orders), compared to your current margin. Measured in the market's settlement asset ($s).",
[settlementAsset]
);
export const CONTRACTS_MARGIN_TOOLTIP_TEXT = t(
'The number of contracts determines how many units of the futures contract to buy or sell. For example, this is similar to buying one share of a listed company. The value of 1 contract is equivalent to the price of the contract. For example, if the current price is $50, then one contract is worth $50.'
);
@ -41,6 +50,7 @@ export enum MarketModeValidationType {
}
export enum SummaryValidationType {
NoPubKey = 'NoPubKey',
NoCollateral = 'NoCollateral',
TradingMode = 'MarketTradingMode',
MarketState = 'MarketState',

View File

@ -4,5 +4,3 @@ export * from './use-fee-deal-ticket-details';
export * from './use-market-positions';
export * from './use-maximum-position-size';
export * from './use-order-closeout';
export * from './use-order-margin';
export * from './use-order-margin-validation';

View File

@ -1,4 +1,3 @@
import { useMemo } from 'react';
import { marketDepthProvider } from '@vegaprotocol/market-depth';
import * as Schema from '@vegaprotocol/types';
import type { Market } from '@vegaprotocol/market-list';
@ -13,11 +12,10 @@ interface Props {
}
export const useCalculateSlippage = ({ market, order }: Props) => {
const variables = useMemo(() => ({ marketId: market.id }), [market.id]);
const { data } = useThrottledDataProvider(
{
dataProvider: marketDepthProvider,
variables,
variables: { marketId: market.id },
},
1000
);

View File

@ -3,24 +3,26 @@ import {
addDecimal,
addDecimalsFormatNumber,
formatNumber,
toBigNum,
} from '@vegaprotocol/utils';
import { t } from '@vegaprotocol/i18n';
import * as Schema from '@vegaprotocol/types';
import { useVegaWallet } from '@vegaprotocol/wallet';
import BigNumber from 'bignumber.js';
import { useMemo } from 'react';
import type { Market, MarketData } from '@vegaprotocol/market-list';
import type { OrderSubmissionBody } from '@vegaprotocol/wallet';
import {
EST_CLOSEOUT_TOOLTIP_TEXT,
EST_MARGIN_TOOLTIP_TEXT,
// EST_MARGIN_TOOLTIP_TEXT,
EST_TOTAL_MARGIN_TOOLTIP_TEXT,
NOTIONAL_SIZE_TOOLTIP_TEXT,
MARGIN_ACCOUNT_TOOLTIP_TEXT,
MARGIN_DIFF_TOOLTIP_TEXT,
} from '../constants';
import { useCalculateSlippage } from './use-calculate-slippage';
import { useOrderCloseOut } from './use-order-closeout';
import { useOrderMargin } from './use-order-margin';
import type { OrderMargin } from './use-order-margin';
import { useMarketAccountBalance } from '@vegaprotocol/accounts';
import { getDerivedPrice } from '../utils/get-price';
import { useEstimateOrderQuery } from './__generated__/EstimateOrder';
import type { EstimateOrderQuery } from './__generated__/EstimateOrder';
export const useFeeDealTicketDetails = (
order: OrderSubmissionBody['orderSubmission'],
@ -28,33 +30,23 @@ export const useFeeDealTicketDetails = (
marketData: MarketData
) => {
const { pubKey } = useVegaWallet();
const slippage = useCalculateSlippage({ market, order });
const { accountBalance } = useMarketAccountBalance(market.id);
const derivedPrice = useMemo(() => {
return getDerivedPrice(order, market, marketData);
}, [order, market, marketData]);
const price = useMemo(() => {
return getDerivedPrice(order, marketData);
}, [order, marketData]);
// Note this isn't currently used anywhere
const slippageAdjustedPrice = useMemo(() => {
if (derivedPrice) {
if (slippage && parseFloat(slippage) !== 0) {
const isLong = order.side === Schema.Side.SIDE_BUY;
const multiplier = new BigNumber(1)[isLong ? 'plus' : 'minus'](
parseFloat(slippage) / 100
);
return new BigNumber(derivedPrice).multipliedBy(multiplier).toNumber();
}
return derivedPrice;
}
return null;
}, [derivedPrice, order.side, slippage]);
const estMargin = useOrderMargin({
order,
market,
marketData,
partyId: pubKey || '',
derivedPrice,
const { data: estMargin } = useEstimateOrderQuery({
variables: {
marketId: market.id,
partyId: pubKey || '',
price,
size: order.size,
side: order.side,
timeInForce: order.timeInForce,
type: order.type,
},
skip: !pubKey || !market || !order.size || !price,
});
const estCloseOut = useOrderCloseOut({
@ -64,13 +56,13 @@ export const useFeeDealTicketDetails = (
});
const notionalSize = useMemo(() => {
if (derivedPrice && order.size) {
return new BigNumber(order.size)
.multipliedBy(addDecimal(derivedPrice, market.decimalPlaces))
if (price && order.size) {
return toBigNum(order.size, market.positionDecimalPlaces)
.multipliedBy(addDecimal(price, market.decimalPlaces))
.toString();
}
return null;
}, [derivedPrice, order.size, market.decimalPlaces]);
}, [price, order.size, market.decimalPlaces, market.positionDecimalPlaces]);
const assetSymbol =
market.tradableInstrument.instrument.product.settlementAsset.symbol;
@ -80,37 +72,40 @@ export const useFeeDealTicketDetails = (
market,
assetSymbol,
notionalSize,
estMargin,
accountBalance,
estimateOrder: estMargin?.estimateOrder,
estCloseOut,
slippage,
slippageAdjustedPrice,
};
}, [
market,
assetSymbol,
notionalSize,
accountBalance,
estMargin,
estCloseOut,
slippage,
slippageAdjustedPrice,
]);
};
export interface FeeDetails {
balance: string;
market: Market;
assetSymbol: string;
notionalSize: string | null;
estMargin: OrderMargin | null;
estCloseOut: string | null;
slippage: string | null;
estimateOrder: EstimateOrderQuery['estimateOrder'] | undefined;
margin: string;
totalMargin: string;
}
export const getFeeDetailsValues = ({
balance,
assetSymbol,
notionalSize,
estMargin,
estCloseOut,
estimateOrder,
margin,
market,
notionalSize,
totalMargin,
}: FeeDetails) => {
const assetDecimals =
market.tradableInstrument.instrument.product.settlementAsset.decimals;
@ -129,7 +124,12 @@ export const getFeeDetailsValues = ({
? addDecimalsFormatNumber(value, assetDecimals)
: '-';
};
return [
const details: {
label: string;
value?: string | null;
symbol: string;
labelDescription: React.ReactNode;
}[] = [
{
label: t('Notional'),
value: formatValueWithMarketDp(notionalSize),
@ -139,8 +139,8 @@ export const getFeeDetailsValues = ({
{
label: t('Fees'),
value:
estMargin?.totalFees &&
`~${formatValueWithAssetDp(estMargin?.totalFees)}`,
estimateOrder?.totalFeeAmount &&
`~${formatValueWithAssetDp(estimateOrder?.totalFeeAmount)}`,
labelDescription: (
<>
<span>
@ -149,7 +149,7 @@ export const getFeeDetailsValues = ({
)}
</span>
<FeesBreakdown
fees={estMargin?.fees}
fees={estimateOrder?.fee}
feeFactors={market.fees.factors}
symbol={assetSymbol}
decimals={assetDecimals}
@ -158,18 +158,46 @@ export const getFeeDetailsValues = ({
),
symbol: assetSymbol,
},
/*
{
label: t('Margin'),
value:
estMargin?.margin && `~${formatValueWithAssetDp(estMargin?.margin)}`,
label: t('Initial margin'),
value: margin && `~${formatValueWithAssetDp(margin)}`,
symbol: assetSymbol,
labelDescription: EST_MARGIN_TOOLTIP_TEXT(assetSymbol),
},
*/
{
label: t('Liquidation'),
value: estCloseOut && `~${formatValueWithMarketDp(estCloseOut)}`,
symbol: market.tradableInstrument.instrument.product.quoteName,
labelDescription: EST_CLOSEOUT_TOOLTIP_TEXT(quoteName),
label: t('Margin required'),
value: `~${formatValueWithAssetDp(
balance
? (BigInt(totalMargin) - BigInt(balance)).toString()
: totalMargin
)}`,
symbol: assetSymbol,
labelDescription: MARGIN_DIFF_TOOLTIP_TEXT(assetSymbol),
},
];
if (balance) {
details.push({
label: t('Projected margin'),
value: `~${formatValueWithAssetDp(totalMargin)}`,
symbol: assetSymbol,
labelDescription: EST_TOTAL_MARGIN_TOOLTIP_TEXT,
});
}
details.push({
label: t('Current margin allocation'),
value: balance
? `~${formatValueWithAssetDp(balance)}`
: `${formatValueWithAssetDp(balance)}`,
symbol: assetSymbol,
labelDescription: MARGIN_ACCOUNT_TOOLTIP_TEXT,
});
details.push({
label: t('Liquidation'),
value: estCloseOut && `~${formatValueWithMarketDp(estCloseOut)}`,
symbol: market.tradableInstrument.instrument.product.quoteName,
labelDescription: EST_CLOSEOUT_TOOLTIP_TEXT(quoteName),
});
return details;
};

View File

@ -1,11 +0,0 @@
import { useAccountBalance } from '@vegaprotocol/accounts';
import { toBigNum } from '@vegaprotocol/utils';
export const useHasNoBalance = (assetId: string) => {
const { accountBalance, accountDecimals } = useAccountBalance(assetId);
const balance =
accountBalance && accountDecimals !== null
? toBigNum(accountBalance, accountDecimals)
: toBigNum('0', 0);
return balance.isZero();
};

View File

@ -0,0 +1,70 @@
import { useMemo } from 'react';
import { useDataProvider } from '@vegaprotocol/react-helpers';
import { useVegaWallet } from '@vegaprotocol/wallet';
import { marketDataProvider } from '@vegaprotocol/market-list';
import {
calculateMargins,
// getDerivedPrice,
volumeAndMarginProvider,
} from '@vegaprotocol/positions';
import { Side } from '@vegaprotocol/types';
import type { OrderSubmissionBody } from '@vegaprotocol/wallet';
import { marketInfoProvider } from '@vegaprotocol/market-info';
export const useInitialMargin = (
marketId: OrderSubmissionBody['orderSubmission']['marketId'],
size?: OrderSubmissionBody['orderSubmission']['size'],
side?: OrderSubmissionBody['orderSubmission']['side']
) => {
const { pubKey: partyId } = useVegaWallet();
const commonVariables = { marketId, partyId: partyId || '' };
const { data: marketData } = useDataProvider({
dataProvider: marketDataProvider,
variables: { marketId },
});
const { data: activeVolumeAndMargin } = useDataProvider({
dataProvider: volumeAndMarginProvider,
variables: commonVariables,
skip: !partyId,
});
const { data: marketInfo } = useDataProvider({
dataProvider: marketInfoProvider,
variables: commonVariables,
});
let totalMargin = '0';
let margin = '0';
if (marketInfo?.riskFactors && marketData && size && side) {
const {
positionDecimalPlaces,
decimalPlaces,
tradableInstrument,
riskFactors,
} = marketInfo;
const { marginCalculator, instrument } = tradableInstrument;
const { decimals } = instrument.product.settlementAsset;
margin = totalMargin = calculateMargins({
side,
size,
price: marketData.markPrice, // getDerivedPrice(order, marketData), same in positions-data-providers
positionDecimalPlaces,
decimalPlaces,
decimals,
scalingFactors: marginCalculator?.scalingFactors,
riskFactors,
}).initialMargin;
}
if (activeVolumeAndMargin) {
let sellMargin = BigInt(activeVolumeAndMargin.sellInitialMargin);
let buyMargin = BigInt(activeVolumeAndMargin.buyInitialMargin);
if (side === Side.SIDE_SELL) {
sellMargin += BigInt(totalMargin);
} else {
buyMargin += BigInt(totalMargin);
}
totalMargin =
sellMargin > buyMargin ? sellMargin.toString() : buyMargin.toString();
}
return useMemo(() => ({ totalMargin, margin }), [totalMargin, margin]);
};

View File

@ -1,48 +0,0 @@
import { useMemo } from 'react';
import { useVegaWallet } from '@vegaprotocol/wallet';
import { toBigNum } from '@vegaprotocol/utils';
import { useAccountBalance } from '@vegaprotocol/accounts';
import type { OrderSubmissionBody } from '@vegaprotocol/wallet';
import { useOrderMargin } from './use-order-margin';
import type { Market, MarketData } from '@vegaprotocol/market-list';
interface Props {
market: Market;
marketData: MarketData;
order: OrderSubmissionBody['orderSubmission'];
}
export const useOrderMarginValidation = ({
market,
marketData,
order,
}: Props) => {
const { pubKey } = useVegaWallet();
const estMargin = useOrderMargin({
order,
market,
marketData,
partyId: pubKey || '',
});
const { id: assetId, decimals: assetDecimals } =
market.tradableInstrument.instrument.product.settlementAsset;
const { accountBalance, accountDecimals } = useAccountBalance(assetId);
const balance =
accountBalance && accountDecimals !== null
? toBigNum(accountBalance, accountDecimals)
: toBigNum('0', assetDecimals);
const margin = toBigNum(estMargin?.margin || 0, assetDecimals);
// return only simple types (bool, string) for make memo sensible
const balanceError = balance.isGreaterThan(0) && balance.isLessThan(margin);
const balanceAsString = balance.toString();
const marginAsString = margin.toString();
return useMemo(() => {
return {
balance: balanceAsString,
margin: marginAsString,
balanceError,
};
}, [balanceAsString, marginAsString, balanceError]);
};

View File

@ -1,116 +0,0 @@
import { renderHook } from '@testing-library/react';
import { useQuery } from '@apollo/client';
import { BigNumber } from 'bignumber.js';
import type { PositionMargin } from './use-market-positions';
import type { Props } from './use-order-margin';
import { useOrderMargin } from './use-order-margin';
import * as Schema from '@vegaprotocol/types';
import type { Market, MarketData } from '@vegaprotocol/market-list';
let mockEstimateData = {
estimateOrder: {
fee: {
makerFee: '100000.000',
infrastructureFee: '100000.000',
liquidityFee: '100000.000',
},
marginLevels: {
initialLevel: '200000',
},
},
};
jest.mock('@apollo/client', () => ({
...jest.requireActual('@apollo/client'),
useQuery: jest.fn(() => ({ data: mockEstimateData })),
}));
let mockMarketPositions: PositionMargin = {
openVolume: '1',
balance: '100000',
};
jest.mock('./use-market-positions', () => ({
useMarketPositions: ({
marketId,
partyId,
}: {
marketId: string;
partyId: string;
}) => mockMarketPositions,
}));
describe('useOrderMargin', () => {
const marketId = 'marketId';
const args: Props = {
order: {
marketId,
size: '2',
side: Schema.Side.SIDE_BUY,
timeInForce: Schema.OrderTimeInForce.TIME_IN_FORCE_IOC,
type: Schema.OrderType.TYPE_MARKET,
},
market: {
id: marketId,
decimalPlaces: 2,
positionDecimalPlaces: 0,
tradingMode: Schema.MarketTradingMode.TRADING_MODE_CONTINUOUS,
} as unknown as Market,
marketData: {
indicativePrice: '100',
markPrice: '200',
} as unknown as MarketData,
partyId: 'partyId',
};
afterEach(() => {
jest.clearAllMocks();
});
it('should calculate margin correctly', () => {
const { result } = renderHook(() => useOrderMargin(args));
expect(result.current?.margin).toEqual('100000');
expect((useQuery as jest.Mock).mock.calls[0][1].variables.size).toEqual(
args.order.size
);
});
it('should calculate fees correctly', () => {
const { result } = renderHook(() => useOrderMargin(args));
expect(result.current?.totalFees).toEqual('300000');
});
it('should not subtract initialMargin if there is no position', () => {
mockMarketPositions = null;
const { result } = renderHook(() => useOrderMargin(args));
expect(result.current?.margin).toEqual('200000');
expect((useQuery as jest.Mock).mock.calls[0][1].variables.size).toEqual(
args.order.size
);
});
it('should return empty value if API fails', () => {
mockEstimateData = {
estimateOrder: {
fee: {
makerFee: '100000.000',
infrastructureFee: '100000.000',
liquidityFee: '100000.000',
},
marginLevels: {
initialLevel: '',
},
},
};
const { result } = renderHook(() => useOrderMargin(args));
expect(result.current).toEqual(null);
const calledSize = new BigNumber(mockMarketPositions?.openVolume || 0)
.plus(args.order.size)
.toString();
expect((useQuery as jest.Mock).mock.calls[0][1].variables.size).toEqual(
calledSize
);
});
});

View File

@ -1,76 +0,0 @@
import { useMemo } from 'react';
import { BigNumber } from 'bignumber.js';
import type { OrderSubmissionBody } from '@vegaprotocol/wallet';
import { removeDecimal } from '@vegaprotocol/utils';
import { useMarketPositions } from './use-market-positions';
import { useEstimateOrderQuery } from './__generated__/EstimateOrder';
import type { Market, MarketData } from '@vegaprotocol/market-list';
import { getDerivedPrice } from '../utils/get-price';
export interface Props {
order: OrderSubmissionBody['orderSubmission'];
market: Market;
marketData: MarketData;
partyId: string;
derivedPrice?: string;
}
export interface OrderMargin {
margin: string;
totalFees: string | null;
fees: {
makerFee: string;
liquidityFee: string;
infrastructureFee: string;
};
}
export const useOrderMargin = ({
order,
market,
marketData,
partyId,
derivedPrice,
}: Props): OrderMargin | null => {
const { balance } = useMarketPositions({ marketId: market.id }) || {};
const priceForEstimate =
derivedPrice || getDerivedPrice(order, market, marketData);
const { data } = useEstimateOrderQuery({
variables: {
marketId: market.id,
partyId,
price: priceForEstimate,
size: removeDecimal(order.size, market.positionDecimalPlaces),
side: order.side,
timeInForce: order.timeInForce,
type: order.type,
},
skip: !partyId || !market.id || !order.size || !priceForEstimate,
});
const { makerFee, liquidityFee, infrastructureFee } = data?.estimateOrder
.fee || { makerFee: '', liquidityFee: '', infrastructureFee: '' };
const { initialLevel } = data?.estimateOrder.marginLevels ?? {};
return useMemo(() => {
if (initialLevel) {
const margin = BigNumber.maximum(
0,
new BigNumber(initialLevel).minus(balance || 0)
).toString();
const fees = new BigNumber(makerFee)
.plus(liquidityFee)
.plus(infrastructureFee)
.toString();
return {
margin,
totalFees: fees,
fees: {
makerFee,
liquidityFee,
infrastructureFee,
},
};
}
return null;
}, [initialLevel, makerFee, liquidityFee, infrastructureFee, balance]);
};

View File

@ -64,20 +64,27 @@ export function generateMarketData(
id: 'market-id',
__typename: 'Market',
},
auctionStart: '2022-06-21T17:18:43.484055236Z',
auctionEnd: '2022-06-21T17:18:43.484055236Z',
targetStake: '1000000',
suppliedStake: '1000',
marketTradingMode: Schema.MarketTradingMode.TRADING_MODE_CONTINUOUS,
marketState: Schema.MarketState.STATE_ACTIVE,
staticMidPrice: '0',
indicativePrice: '100',
bestStaticBidPrice: '0',
bestStaticOfferPrice: '0',
indicativeVolume: '10',
auctionStart: '2022-06-21T17:18:43.484055236Z',
bestBidPrice: '0',
bestBidVolume: '0',
bestOfferPrice: '0',
bestOfferVolume: '0',
bestStaticBidPrice: '0',
bestStaticBidVolume: '0',
bestStaticOfferPrice: '0',
bestStaticOfferVolume: '0',
indicativePrice: '100',
indicativeVolume: '10',
marketState: Schema.MarketState.STATE_ACTIVE,
marketTradingMode: Schema.MarketTradingMode.TRADING_MODE_CONTINUOUS,
marketValueProxy: '',
markPrice: '200',
midPrice: '0',
openInterest: '',
staticMidPrice: '0',
suppliedStake: '1000',
targetStake: '1000000',
trigger: Schema.AuctionTrigger.AUCTION_TRIGGER_BATCH,
};
return merge(defaultMarketData, override);

View File

@ -1,7 +1,6 @@
import { removeDecimal } from '@vegaprotocol/utils';
import * as Schema from '@vegaprotocol/types';
import { isMarketInAuction } from './is-market-in-auction';
import type { MarketData, Market } from '@vegaprotocol/market-list';
import type { MarketData } from '@vegaprotocol/market-list';
/**
* Get the market price based on market mode (auction or not auction)
@ -34,7 +33,6 @@ export const getDerivedPrice = (
type: Schema.OrderType;
price?: string | undefined;
},
market: Market,
marketData: MarketData
) => {
// If order type is market we should use either the mark price
@ -44,7 +42,7 @@ export const getDerivedPrice = (
// Use the market price if order is a market order
let price;
if (order.type === Schema.OrderType.TYPE_LIMIT && order.price) {
price = removeDecimal(order.price, market.decimalPlaces);
price = order.price;
} else {
price = getMarketPrice(marketData);
}

View File

@ -12,6 +12,7 @@ export const DepositContainer = ({ assetId }: { assetId?: string }) => {
const { VEGA_ENV } = useEnvironment();
const { data, loading, error } = useDataProvider({
dataProvider: enabledAssetsProvider,
variables: undefined,
});
return (

View File

@ -13,6 +13,7 @@ import type { PageInfo, Edge } from '@vegaprotocol/utils';
import { FillsDocument, FillsEventDocument } from './__generated__/Fills';
import type {
FillsQuery,
FillsQueryVariables,
FillFieldsFragment,
FillEdgeFragment,
FillsEventSubscription,
@ -62,13 +63,19 @@ export type TradeEdge = Edge<Trade>;
const getData = (responseData: FillsQuery | null): FillEdgeFragment[] =>
responseData?.party?.tradesConnection?.edges || [];
const getPageInfo = (responseData: FillsQuery): PageInfo | null =>
responseData.party?.tradesConnection?.pageInfo || null;
const getPageInfo = (responseData: FillsQuery | null): PageInfo | null =>
responseData?.party?.tradesConnection?.pageInfo || null;
const getDelta = (subscriptionData: FillsEventSubscription) =>
subscriptionData.trades || [];
export const fillsProvider = makeDataProvider({
export const fillsProvider = makeDataProvider<
Parameters<typeof getData>['0'],
ReturnType<typeof getData>,
Parameters<typeof getDelta>['0'],
ReturnType<typeof getDelta>,
FillsQueryVariables
>({
query: FillsDocument,
subscriptionQuery: FillsEventDocument,
update,
@ -83,9 +90,13 @@ export const fillsProvider = makeDataProvider({
export const fillsWithMarketProvider = makeDerivedDataProvider<
(TradeEdge | null)[],
Trade[]
Trade[],
FillsQueryVariables
>(
[fillsProvider, marketsProvider],
[
fillsProvider,
(callback, client) => marketsProvider(callback, client, undefined),
],
(partsData): (TradeEdge | null)[] =>
(partsData[0] as ReturnType<typeof getData>)?.map(
(edge) =>

View File

@ -1,6 +1,6 @@
import type { RefObject } from 'react';
import type { AgGridReact } from 'ag-grid-react';
import { useCallback, useMemo, useRef } from 'react';
import { useCallback, useRef } from 'react';
import { makeInfiniteScrollGetRows } from '@vegaprotocol/utils';
import { useDataProvider, updateGridData } from '@vegaprotocol/react-helpers';
import type { Trade, TradeEdge } from './fills-data-provider';
@ -73,16 +73,11 @@ export const useFillsList = ({
[gridRef]
);
const variables = useMemo(() => ({ partyId, marketId }), [partyId, marketId]);
const { data, error, loading, load, totalCount, reload } = useDataProvider<
(TradeEdge | null)[],
Trade[]
>({
const { data, error, loading, load, totalCount, reload } = useDataProvider({
dataProvider: fillsWithMarketProvider,
update,
insert,
variables,
variables: { partyId, marketId: marketId || '' },
});
totalCountRef.current = totalCount;

View File

@ -42,7 +42,7 @@ export const update = (
data: ReturnType<typeof getData> | null,
delta: ReturnType<typeof getData>,
reload: () => void,
variables?: LedgerEntriesQueryVariables
variables: LedgerEntriesQueryVariables
) => {
if (!data) {
return data;
@ -110,8 +110,8 @@ export const ledgerEntriesProvider = makeDerivedDataProvider<
>(
[
ledgerEntriesOnlyProvider,
(callback, client) => assetsProvider(callback, client),
marketsProvider,
(callback, client) => assetsProvider(callback, client, undefined),
(callback, client) => marketsProvider(callback, client, undefined),
],
([entries, assets, markets]) => {
return entries.map((edge: AggregatedLedgerEntriesEdge) => {

View File

@ -14,11 +14,14 @@ import {
import type {
MarketLpQuery,
MarketLpQueryVariables,
LiquidityProviderFeeShareFieldsFragment,
LiquidityProviderFeeShareQuery,
LiquidityProviderFeeShareQueryVariables,
LiquidityProviderFeeShareUpdateSubscription,
LiquidityProvisionFieldsFragment,
LiquidityProvisionsQuery,
LiquidityProvisionsQueryVariables,
LiquidityProvisionsUpdateSubscription,
} from './__generated__/MarketLiquidity';
import type { IterableElement } from 'type-fest';
@ -27,7 +30,8 @@ export const liquidityProvisionsDataProvider = makeDataProvider<
LiquidityProvisionsQuery,
LiquidityProvisionFieldsFragment[],
LiquidityProvisionsUpdateSubscription,
LiquidityProvisionsUpdateSubscription['liquidityProvisions']
LiquidityProvisionsUpdateSubscription['liquidityProvisions'],
LiquidityProvisionsQueryVariables
>({
query: LiquidityProvisionsDocument,
subscriptionQuery: LiquidityProvisionsUpdateDocument,
@ -99,7 +103,8 @@ export const marketLiquidityDataProvider = makeDataProvider<
MarketLpQuery,
MarketLpQuery,
never,
never
never,
MarketLpQueryVariables
>({
query: MarketLpDocument,
getData: (responseData: MarketLpQuery | null) => {
@ -111,7 +116,8 @@ export const liquidityFeeShareDataProvider = makeDataProvider<
LiquidityProviderFeeShareQuery,
LiquidityProviderFeeShareFieldsFragment[],
LiquidityProviderFeeShareUpdateSubscription,
LiquidityProviderFeeShareUpdateSubscription['marketsData'][0]['liquidityProviderFeeShare']
LiquidityProviderFeeShareUpdateSubscription['marketsData'][0]['liquidityProviderFeeShare'],
LiquidityProviderFeeShareQueryVariables
>({
query: LiquidityProviderFeeShareDocument,
subscriptionQuery: LiquidityProviderFeeShareUpdateDocument,
@ -147,7 +153,11 @@ export const liquidityFeeShareDataProvider = makeDataProvider<
},
});
export const lpAggregatedDataProvider = makeDerivedDataProvider(
export const lpAggregatedDataProvider = makeDerivedDataProvider<
ReturnType<typeof getLiquidityProvision>,
never,
MarketLpQueryVariables
>(
[
liquidityProvisionsDataProvider,
marketLiquidityDataProvider,

View File

@ -5,12 +5,10 @@ import { useDataProvider, useYesterday } from '@vegaprotocol/react-helpers';
import type {
MarketCandles,
MarketMaybeWithDataAndCandles,
MarketsCandlesQueryVariables,
} from '@vegaprotocol/market-list';
import {
marketsCandlesProvider,
marketListProvider,
} from '@vegaprotocol/market-list';
import { marketListProvider } from '@vegaprotocol/market-list';
import type { LiquidityProvisionMarketsQuery } from './__generated__/MarketsLiquidity';
import { LiquidityProvisionMarketsDocument } from './__generated__/MarketsLiquidity';
@ -97,15 +95,18 @@ export const liquidityMarketsProvider = makeDataProvider<
getData,
});
const liquidityProvisionProvider = makeDerivedDataProvider<Market[], never>(
const liquidityProvisionProvider = makeDerivedDataProvider<
Market[],
never,
Exclude<MarketsCandlesQueryVariables, 'interval'>
>(
[
marketListProvider,
(callback, client, variables) =>
marketsCandlesProvider(callback, client, {
...variables,
marketListProvider(callback, client, {
since: variables.since,
interval: Schema.Interval.INTERVAL_I1D,
}),
liquidityMarketsProvider,
(callback, client) => liquidityMarketsProvider(callback, client, undefined),
],
(parts) => {
return addData(

View File

@ -31,7 +31,7 @@ describe('market depth provider update', () => {
sequenceNumber: '',
previousSequenceNumber: '',
};
const updatedData = update(data, [delta], reload);
const updatedData = update(data, [delta], reload, { marketId: '1' });
expect(updatedData).toBe(data);
});
@ -54,8 +54,12 @@ describe('market depth provider update', () => {
previousSequenceNumber: '',
},
];
expect(update(data, delta.slice(0, 1), reload)).toBe(data);
expect(update(data, delta.slice(1, 2), reload)).toBe(data);
expect(update(data, delta.slice(0, 1), reload, { marketId: '1' })).toBe(
data
);
expect(update(data, delta.slice(1, 2), reload, { marketId: '1' })).toBe(
data
);
});
it('restarts and captureException when there is gap in updates', () => {
@ -72,7 +76,7 @@ describe('market depth provider update', () => {
previousSequenceNumber: '12',
},
];
const updatedData = update(data, delta, reload);
const updatedData = update(data, delta, reload, { marketId: '1' });
expect(updatedData).toBe(data);
expect(reload).toBeCalled();
expect(mockCaptureException).toBeCalled();

View File

@ -9,12 +9,14 @@ import {
} from './__generated__/MarketDepth';
import type {
MarketDepthQuery,
MarketDepthQueryVariables,
MarketDepthUpdateSubscription,
} from './__generated__/MarketDepth';
export const update: Update<
ReturnType<typeof getData>,
ReturnType<typeof getDelta>
ReturnType<typeof getDelta>,
MarketDepthQueryVariables
> = (data, deltas, reload) => {
if (!data) {
return data;
@ -61,12 +63,19 @@ export const update: Update<
return data;
};
const getData = (responseData: MarketDepthQuery | null) => responseData?.market;
const getData = (responseData: MarketDepthQuery | null) =>
responseData?.market || null;
const getDelta = (subscriptionData: MarketDepthUpdateSubscription) =>
subscriptionData.marketsDepthUpdate;
export const marketDepthProvider = makeDataProvider({
export const marketDepthProvider = makeDataProvider<
MarketDepthQuery,
ReturnType<typeof getData>,
MarketDepthUpdateSubscription,
ReturnType<typeof getDelta>,
MarketDepthQueryVariables
>({
query: MarketDepthDocument,
subscriptionQuery: MarketDepthUpdateDocument,
update,

View File

@ -7,12 +7,13 @@ import { useDataProvider } from '@vegaprotocol/react-helpers';
import { marketDepthProvider } from './market-depth-provider';
import { marketDataProvider, marketProvider } from '@vegaprotocol/market-list';
import type { MarketData } from '@vegaprotocol/market-list';
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { useCallback, useEffect, useRef, useState } from 'react';
import type {
MarketDepthUpdateSubscription,
MarketDepthQuery,
PriceLevelFieldsFragment,
MarketDepthQueryVariables,
} from './__generated__/MarketDepth';
import type { PriceLevelFieldsFragment } from './__generated__/MarketDepth';
import {
compactRows,
updateCompactedRows,
@ -28,7 +29,7 @@ interface OrderbookManagerProps {
export const OrderbookManager = ({ marketId }: OrderbookManagerProps) => {
const [resolution, setResolution] = useState(1);
const variables = useMemo(() => ({ marketId }), [marketId]);
const variables = { marketId };
const resolutionRef = useRef(resolution);
const [orderbookData, setOrderbookData] = useState<OrderbookData>({
rows: null,
@ -79,8 +80,8 @@ export const OrderbookManager = ({ marketId }: OrderbookManagerProps) => {
delta: deltas,
data: rawData,
}: {
delta?: MarketDepthUpdateSubscription['marketsDepthUpdate'];
data?: MarketDepthQuery['market'];
delta?: MarketDepthUpdateSubscription['marketsDepthUpdate'] | null;
data: NonNullable<MarketDepthQuery['market']> | null | undefined;
}) => {
if (!dataRef.current.rows) {
return false;
@ -103,7 +104,11 @@ export const OrderbookManager = ({ marketId }: OrderbookManagerProps) => {
[marketId, updateOrderbookData]
);
const { data, error, loading, flush, reload } = useDataProvider({
const { data, error, loading, flush, reload } = useDataProvider<
MarketDepthQuery['market'] | undefined,
MarketDepthUpdateSubscription['marketsDepthUpdate'] | null,
MarketDepthQueryVariables
>({
dataProvider: marketDepthProvider,
update,
variables,

View File

@ -1,4 +1,5 @@
import { totalFeesPercentage } from '@vegaprotocol/market-list';
import type { TradeFee, FeeFactors } from '@vegaprotocol/types';
import {
addDecimalsFormatNumber,
formatNumberPercentage,
@ -7,12 +8,7 @@ import { t } from '@vegaprotocol/i18n';
import { Tooltip } from '@vegaprotocol/ui-toolkit';
import BigNumber from 'bignumber.js';
import type { Market } from '@vegaprotocol/market-list';
export const FeesCell = ({
feeFactors,
}: {
feeFactors: Market['fees']['factors'];
}) => (
export const FeesCell = ({ feeFactors }: { feeFactors: FeeFactors }) => (
<Tooltip description={<FeesBreakdownPercentage feeFactors={feeFactors} />}>
<span>{totalFeesPercentage(feeFactors) ?? '-'}</span>
</Tooltip>
@ -21,7 +17,7 @@ export const FeesCell = ({
export const FeesBreakdownPercentage = ({
feeFactors,
}: {
feeFactors?: Market['fees']['factors'];
feeFactors?: FeeFactors;
}) => {
if (!feeFactors) return null;
return (
@ -54,12 +50,8 @@ export const FeesBreakdown = ({
symbol,
decimals,
}: {
fees?: {
infrastructureFee: string;
liquidityFee: string;
makerFee: string;
};
feeFactors?: Market['fees']['factors'];
fees?: TradeFee;
feeFactors?: FeeFactors;
symbol?: string;
decimals: number;
}) => {

View File

@ -1,4 +1,4 @@
query MarketInfo($marketId: ID!, $interval: Interval!, $since: String!) {
query MarketInfo($marketId: ID!) {
market(id: $marketId) {
id
decimalPlaces
@ -32,7 +32,6 @@ query MarketInfo($marketId: ID!, $interval: Interval!, $since: String!) {
}
}
}
tradingMode
fees {
factors {
makerFee
@ -54,35 +53,6 @@ query MarketInfo($marketId: ID!, $interval: Interval!, $since: String!) {
short
long
}
data {
market {
id
}
markPrice
midPrice
bestBidVolume
bestOfferVolume
bestStaticBidVolume
bestStaticOfferVolume
bestBidPrice
bestOfferPrice
trigger
openInterest
suppliedStake
openInterest
targetStake
marketValueProxy
priceMonitoringBounds {
minValidPrice
maxValidPrice
trigger {
horizonSecs
probability
auctionExtensionSecs
}
referencePrice
}
}
liquidityMonitoringParameters {
triggeringRatio
targetStakeParameters {
@ -90,13 +60,6 @@ query MarketInfo($marketId: ID!, $interval: Interval!, $since: String!) {
scalingFactor
}
}
candlesConnection(interval: $interval, since: $since) {
edges {
node {
volume
}
}
}
tradableInstrument {
instrument {
id
@ -144,10 +107,12 @@ query MarketInfo($marketId: ID!, $interval: Interval!, $since: String!) {
}
}
}
}
depth {
lastTrade {
price
marginCalculator {
scalingFactors {
searchLevel
initialMargin
collateralRelease
}
}
}
}

View File

@ -1,147 +0,0 @@
query MarketInfoNoCandles($marketId: ID!) {
market(id: $marketId) {
id
decimalPlaces
positionDecimalPlaces
state
tradingMode
lpPriceRange
proposal {
id
rationale {
title
description
}
}
marketTimestamps {
open
close
}
openingAuction {
durationSecs
volume
}
accountsConnection {
edges {
node {
type
asset {
id
}
balance
}
}
}
tradingMode
fees {
factors {
makerFee
infrastructureFee
liquidityFee
}
}
priceMonitoringSettings {
parameters {
triggers {
horizonSecs
probability
auctionExtensionSecs
}
}
}
riskFactors {
market
short
long
}
data {
market {
id
}
markPrice
midPrice
bestBidVolume
bestOfferVolume
bestStaticBidVolume
bestStaticOfferVolume
bestBidPrice
bestOfferPrice
trigger
openInterest
suppliedStake
openInterest
targetStake
marketValueProxy
priceMonitoringBounds {
minValidPrice
maxValidPrice
trigger {
horizonSecs
probability
auctionExtensionSecs
}
referencePrice
}
}
liquidityMonitoringParameters {
triggeringRatio
targetStakeParameters {
timeWindow
scalingFactor
}
}
tradableInstrument {
instrument {
id
name
code
metadata {
tags
}
product {
... on Future {
quoteName
settlementAsset {
id
symbol
name
decimals
}
dataSourceSpecForSettlementData {
id
}
dataSourceSpecForTradingTermination {
id
}
dataSourceSpecBinding {
settlementDataProperty
tradingTerminationProperty
}
}
}
}
riskModel {
... on LogNormalRiskModel {
tau
riskAversionParameter
params {
r
sigma
mu
}
}
... on SimpleRiskModel {
params {
factorLong
factorShort
}
}
}
}
depth {
lastTrade {
price
}
}
}
}

View File

@ -5,16 +5,14 @@ import * as Apollo from '@apollo/client';
const defaultOptions = {} as const;
export type MarketInfoQueryVariables = Types.Exact<{
marketId: Types.Scalars['ID'];
interval: Types.Interval;
since: Types.Scalars['String'];
}>;
export type MarketInfoQuery = { __typename?: 'Query', market?: { __typename?: 'Market', id: string, decimalPlaces: number, positionDecimalPlaces: number, state: Types.MarketState, tradingMode: Types.MarketTradingMode, lpPriceRange: string, proposal?: { __typename?: 'Proposal', id?: string | null, rationale: { __typename?: 'ProposalRationale', title: string, description: string } } | null, marketTimestamps: { __typename?: 'MarketTimestamps', open: any, close: any }, openingAuction: { __typename?: 'AuctionDuration', durationSecs: number, volume: number }, accountsConnection?: { __typename?: 'AccountsConnection', edges?: Array<{ __typename?: 'AccountEdge', node: { __typename?: 'AccountBalance', type: Types.AccountType, balance: string, asset: { __typename?: 'Asset', id: string } } } | null> | null } | null, fees: { __typename?: 'Fees', factors: { __typename?: 'FeeFactors', makerFee: string, infrastructureFee: string, liquidityFee: string } }, priceMonitoringSettings: { __typename?: 'PriceMonitoringSettings', parameters?: { __typename?: 'PriceMonitoringParameters', triggers?: Array<{ __typename?: 'PriceMonitoringTrigger', horizonSecs: number, probability: number, auctionExtensionSecs: number }> | null } | null }, riskFactors?: { __typename?: 'RiskFactor', market: string, short: string, long: string } | null, data?: { __typename?: 'MarketData', markPrice: string, midPrice: string, bestBidVolume: string, bestOfferVolume: string, bestStaticBidVolume: string, bestStaticOfferVolume: string, bestBidPrice: string, bestOfferPrice: string, trigger: Types.AuctionTrigger, openInterest: string, suppliedStake?: string | null, targetStake?: string | null, marketValueProxy: string, market: { __typename?: 'Market', id: string }, priceMonitoringBounds?: Array<{ __typename?: 'PriceMonitoringBounds', minValidPrice: string, maxValidPrice: string, referencePrice: string, trigger: { __typename?: 'PriceMonitoringTrigger', horizonSecs: number, probability: number, auctionExtensionSecs: number } }> | null } | null, liquidityMonitoringParameters: { __typename?: 'LiquidityMonitoringParameters', triggeringRatio: string, targetStakeParameters: { __typename?: 'TargetStakeParameters', timeWindow: number, scalingFactor: number } }, candlesConnection?: { __typename?: 'CandleDataConnection', edges?: Array<{ __typename?: 'CandleEdge', node: { __typename?: 'Candle', volume: string } } | null> | null } | null, tradableInstrument: { __typename?: 'TradableInstrument', instrument: { __typename?: 'Instrument', id: string, name: string, code: string, metadata: { __typename?: 'InstrumentMetadata', tags?: Array<string> | null }, product: { __typename?: 'Future', quoteName: string, settlementAsset: { __typename?: 'Asset', id: string, symbol: string, name: string, decimals: number }, dataSourceSpecForSettlementData: { __typename?: 'DataSourceSpec', id: string }, dataSourceSpecForTradingTermination: { __typename?: 'DataSourceSpec', id: string }, dataSourceSpecBinding: { __typename?: 'DataSourceSpecToFutureBinding', settlementDataProperty: string, tradingTerminationProperty: string } } }, riskModel: { __typename?: 'LogNormalRiskModel', tau: number, riskAversionParameter: number, params: { __typename?: 'LogNormalModelParams', r: number, sigma: number, mu: number } } | { __typename?: 'SimpleRiskModel', params: { __typename?: 'SimpleRiskModelParams', factorLong: number, factorShort: number } } }, depth: { __typename?: 'MarketDepth', lastTrade?: { __typename?: 'Trade', price: string } | null } } | null };
export type MarketInfoQuery = { __typename?: 'Query', market?: { __typename?: 'Market', id: string, decimalPlaces: number, positionDecimalPlaces: number, state: Types.MarketState, tradingMode: Types.MarketTradingMode, lpPriceRange: string, proposal?: { __typename?: 'Proposal', id?: string | null, rationale: { __typename?: 'ProposalRationale', title: string, description: string } } | null, marketTimestamps: { __typename?: 'MarketTimestamps', open: any, close: any }, openingAuction: { __typename?: 'AuctionDuration', durationSecs: number, volume: number }, accountsConnection?: { __typename?: 'AccountsConnection', edges?: Array<{ __typename?: 'AccountEdge', node: { __typename?: 'AccountBalance', type: Types.AccountType, balance: string, asset: { __typename?: 'Asset', id: string } } } | null> | null } | null, fees: { __typename?: 'Fees', factors: { __typename?: 'FeeFactors', makerFee: string, infrastructureFee: string, liquidityFee: string } }, priceMonitoringSettings: { __typename?: 'PriceMonitoringSettings', parameters?: { __typename?: 'PriceMonitoringParameters', triggers?: Array<{ __typename?: 'PriceMonitoringTrigger', horizonSecs: number, probability: number, auctionExtensionSecs: number }> | null } | null }, riskFactors?: { __typename?: 'RiskFactor', market: string, short: string, long: string } | null, liquidityMonitoringParameters: { __typename?: 'LiquidityMonitoringParameters', triggeringRatio: string, targetStakeParameters: { __typename?: 'TargetStakeParameters', timeWindow: number, scalingFactor: number } }, tradableInstrument: { __typename?: 'TradableInstrument', instrument: { __typename?: 'Instrument', id: string, name: string, code: string, metadata: { __typename?: 'InstrumentMetadata', tags?: Array<string> | null }, product: { __typename?: 'Future', quoteName: string, settlementAsset: { __typename?: 'Asset', id: string, symbol: string, name: string, decimals: number }, dataSourceSpecForSettlementData: { __typename?: 'DataSourceSpec', id: string }, dataSourceSpecForTradingTermination: { __typename?: 'DataSourceSpec', id: string }, dataSourceSpecBinding: { __typename?: 'DataSourceSpecToFutureBinding', settlementDataProperty: string, tradingTerminationProperty: string } } }, riskModel: { __typename?: 'LogNormalRiskModel', tau: number, riskAversionParameter: number, params: { __typename?: 'LogNormalModelParams', r: number, sigma: number, mu: number } } | { __typename?: 'SimpleRiskModel', params: { __typename?: 'SimpleRiskModelParams', factorLong: number, factorShort: number } }, marginCalculator?: { __typename?: 'MarginCalculator', scalingFactors: { __typename?: 'ScalingFactors', searchLevel: number, initialMargin: number, collateralRelease: number } } | null } } | null };
export const MarketInfoDocument = gql`
query MarketInfo($marketId: ID!, $interval: Interval!, $since: String!) {
query MarketInfo($marketId: ID!) {
market(id: $marketId) {
id
decimalPlaces
@ -48,7 +46,6 @@ export const MarketInfoDocument = gql`
}
}
}
tradingMode
fees {
factors {
makerFee
@ -70,35 +67,6 @@ export const MarketInfoDocument = gql`
short
long
}
data {
market {
id
}
markPrice
midPrice
bestBidVolume
bestOfferVolume
bestStaticBidVolume
bestStaticOfferVolume
bestBidPrice
bestOfferPrice
trigger
openInterest
suppliedStake
openInterest
targetStake
marketValueProxy
priceMonitoringBounds {
minValidPrice
maxValidPrice
trigger {
horizonSecs
probability
auctionExtensionSecs
}
referencePrice
}
}
liquidityMonitoringParameters {
triggeringRatio
targetStakeParameters {
@ -106,13 +74,6 @@ export const MarketInfoDocument = gql`
scalingFactor
}
}
candlesConnection(interval: $interval, since: $since) {
edges {
node {
volume
}
}
}
tradableInstrument {
instrument {
id
@ -160,10 +121,12 @@ export const MarketInfoDocument = gql`
}
}
}
}
depth {
lastTrade {
price
marginCalculator {
scalingFactors {
searchLevel
initialMargin
collateralRelease
}
}
}
}
@ -183,8 +146,6 @@ export const MarketInfoDocument = gql`
* const { data, loading, error } = useMarketInfoQuery({
* variables: {
* marketId: // value for 'marketId'
* interval: // value for 'interval'
* since: // value for 'since'
* },
* });
*/

View File

@ -1,190 +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 MarketInfoNoCandlesQueryVariables = Types.Exact<{
marketId: Types.Scalars['ID'];
}>;
export type MarketInfoNoCandlesQuery = { __typename?: 'Query', market?: { __typename?: 'Market', id: string, decimalPlaces: number, positionDecimalPlaces: number, state: Types.MarketState, tradingMode: Types.MarketTradingMode, lpPriceRange: string, proposal?: { __typename?: 'Proposal', id?: string | null, rationale: { __typename?: 'ProposalRationale', title: string, description: string } } | null, marketTimestamps: { __typename?: 'MarketTimestamps', open: any, close: any }, openingAuction: { __typename?: 'AuctionDuration', durationSecs: number, volume: number }, accountsConnection?: { __typename?: 'AccountsConnection', edges?: Array<{ __typename?: 'AccountEdge', node: { __typename?: 'AccountBalance', type: Types.AccountType, balance: string, asset: { __typename?: 'Asset', id: string } } } | null> | null } | null, fees: { __typename?: 'Fees', factors: { __typename?: 'FeeFactors', makerFee: string, infrastructureFee: string, liquidityFee: string } }, priceMonitoringSettings: { __typename?: 'PriceMonitoringSettings', parameters?: { __typename?: 'PriceMonitoringParameters', triggers?: Array<{ __typename?: 'PriceMonitoringTrigger', horizonSecs: number, probability: number, auctionExtensionSecs: number }> | null } | null }, riskFactors?: { __typename?: 'RiskFactor', market: string, short: string, long: string } | null, data?: { __typename?: 'MarketData', markPrice: string, midPrice: string, bestBidVolume: string, bestOfferVolume: string, bestStaticBidVolume: string, bestStaticOfferVolume: string, bestBidPrice: string, bestOfferPrice: string, trigger: Types.AuctionTrigger, openInterest: string, suppliedStake?: string | null, targetStake?: string | null, marketValueProxy: string, market: { __typename?: 'Market', id: string }, priceMonitoringBounds?: Array<{ __typename?: 'PriceMonitoringBounds', minValidPrice: string, maxValidPrice: string, referencePrice: string, trigger: { __typename?: 'PriceMonitoringTrigger', horizonSecs: number, probability: number, auctionExtensionSecs: number } }> | null } | null, liquidityMonitoringParameters: { __typename?: 'LiquidityMonitoringParameters', triggeringRatio: string, targetStakeParameters: { __typename?: 'TargetStakeParameters', timeWindow: number, scalingFactor: number } }, tradableInstrument: { __typename?: 'TradableInstrument', instrument: { __typename?: 'Instrument', id: string, name: string, code: string, metadata: { __typename?: 'InstrumentMetadata', tags?: Array<string> | null }, product: { __typename?: 'Future', quoteName: string, settlementAsset: { __typename?: 'Asset', id: string, symbol: string, name: string, decimals: number }, dataSourceSpecForSettlementData: { __typename?: 'DataSourceSpec', id: string }, dataSourceSpecForTradingTermination: { __typename?: 'DataSourceSpec', id: string }, dataSourceSpecBinding: { __typename?: 'DataSourceSpecToFutureBinding', settlementDataProperty: string, tradingTerminationProperty: string } } }, riskModel: { __typename?: 'LogNormalRiskModel', tau: number, riskAversionParameter: number, params: { __typename?: 'LogNormalModelParams', r: number, sigma: number, mu: number } } | { __typename?: 'SimpleRiskModel', params: { __typename?: 'SimpleRiskModelParams', factorLong: number, factorShort: number } } }, depth: { __typename?: 'MarketDepth', lastTrade?: { __typename?: 'Trade', price: string } | null } } | null };
export const MarketInfoNoCandlesDocument = gql`
query MarketInfoNoCandles($marketId: ID!) {
market(id: $marketId) {
id
decimalPlaces
positionDecimalPlaces
state
tradingMode
lpPriceRange
proposal {
id
rationale {
title
description
}
}
marketTimestamps {
open
close
}
openingAuction {
durationSecs
volume
}
accountsConnection {
edges {
node {
type
asset {
id
}
balance
}
}
}
tradingMode
fees {
factors {
makerFee
infrastructureFee
liquidityFee
}
}
priceMonitoringSettings {
parameters {
triggers {
horizonSecs
probability
auctionExtensionSecs
}
}
}
riskFactors {
market
short
long
}
data {
market {
id
}
markPrice
midPrice
bestBidVolume
bestOfferVolume
bestStaticBidVolume
bestStaticOfferVolume
bestBidPrice
bestOfferPrice
trigger
openInterest
suppliedStake
openInterest
targetStake
marketValueProxy
priceMonitoringBounds {
minValidPrice
maxValidPrice
trigger {
horizonSecs
probability
auctionExtensionSecs
}
referencePrice
}
}
liquidityMonitoringParameters {
triggeringRatio
targetStakeParameters {
timeWindow
scalingFactor
}
}
tradableInstrument {
instrument {
id
name
code
metadata {
tags
}
product {
... on Future {
quoteName
settlementAsset {
id
symbol
name
decimals
}
dataSourceSpecForSettlementData {
id
}
dataSourceSpecForTradingTermination {
id
}
dataSourceSpecBinding {
settlementDataProperty
tradingTerminationProperty
}
}
}
}
riskModel {
... on LogNormalRiskModel {
tau
riskAversionParameter
params {
r
sigma
mu
}
}
... on SimpleRiskModel {
params {
factorLong
factorShort
}
}
}
}
depth {
lastTrade {
price
}
}
}
}
`;
/**
* __useMarketInfoNoCandlesQuery__
*
* To run a query within a React component, call `useMarketInfoNoCandlesQuery` and pass it any options that fit your needs.
* When your component renders, `useMarketInfoNoCandlesQuery` 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 } = useMarketInfoNoCandlesQuery({
* variables: {
* marketId: // value for 'marketId'
* },
* });
*/
export function useMarketInfoNoCandlesQuery(baseOptions: Apollo.QueryHookOptions<MarketInfoNoCandlesQuery, MarketInfoNoCandlesQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useQuery<MarketInfoNoCandlesQuery, MarketInfoNoCandlesQueryVariables>(MarketInfoNoCandlesDocument, options);
}
export function useMarketInfoNoCandlesLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<MarketInfoNoCandlesQuery, MarketInfoNoCandlesQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useLazyQuery<MarketInfoNoCandlesQuery, MarketInfoNoCandlesQueryVariables>(MarketInfoNoCandlesDocument, options);
}
export type MarketInfoNoCandlesQueryHookResult = ReturnType<typeof useMarketInfoNoCandlesQuery>;
export type MarketInfoNoCandlesLazyQueryHookResult = ReturnType<typeof useMarketInfoNoCandlesLazyQuery>;
export type MarketInfoNoCandlesQueryResult = Apollo.QueryResult<MarketInfoNoCandlesQuery, MarketInfoNoCandlesQueryVariables>;

View File

@ -2,5 +2,4 @@ export * from './info-key-value-table';
export * from './info-market';
export * from './tooltip-mapping';
export * from './__generated__/MarketInfo';
export * from './__generated__/MarketInfoNoCandles';
export * from './market-info-data-provider';

View File

@ -1,6 +1,9 @@
import { AssetDetailsTable, useAssetDataProvider } from '@vegaprotocol/assets';
import { useEnvironment } from '@vegaprotocol/environment';
import { totalFeesPercentage } from '@vegaprotocol/market-list';
import {
totalFeesPercentage,
calcCandleVolume,
} from '@vegaprotocol/market-list';
import {
addDecimalsFormatNumber,
formatNumber,
@ -20,31 +23,20 @@ import {
Splash,
} from '@vegaprotocol/ui-toolkit';
import BigNumber from 'bignumber.js';
import pick from 'lodash/pick';
import { useMemo } from 'react';
import { generatePath, Link } from 'react-router-dom';
import { MarketInfoTable } from './info-key-value-table';
import { marketInfoDataProvider } from './market-info-data-provider';
import { marketInfoWithDataAndCandlesProvider } from './market-info-data-provider';
import type { MarketInfoQuery } from './__generated__/MarketInfo';
import type { MarketInfoWithDataAndCandles } from './market-info-data-provider';
import { MarketProposalNotification } from '@vegaprotocol/proposals';
export interface InfoProps {
market: MarketInfoQuery['market'];
market: MarketInfoWithDataAndCandles;
onSelect: (id: string) => void;
}
export const calcCandleVolume = (
m: MarketInfoQuery['market']
): string | undefined => {
return m?.candlesConnection?.edges
?.reduce((acc: BigNumber, c) => {
return acc.plus(new BigNumber(c?.node?.volume ?? 0));
}, new BigNumber(m?.candlesConnection?.edges[0]?.node.volume ?? 0))
?.toString();
};
export interface MarketInfoContainerProps {
marketId: string;
onSelect?: (id: string) => void;
@ -67,15 +59,15 @@ export const MarketInfoContainer = ({
);
const { data, loading, error, reload } = useDataProvider({
dataProvider: marketInfoDataProvider,
dataProvider: marketInfoWithDataAndCandlesProvider,
skipUpdates: true,
variables,
});
return (
<AsyncRenderer data={data} loading={loading} error={error} reload={reload}>
{data && data.market ? (
<Info market={data.market} onSelect={(id) => onSelect?.(id)} />
{data ? (
<Info market={data} onSelect={(id) => onSelect?.(id)} />
) : (
<Splash>
<p>{t('Could not load market')}</p>
@ -88,7 +80,6 @@ export const MarketInfoContainer = ({
export const Info = ({ market, onSelect }: InfoProps) => {
const { VEGA_TOKEN_URL, VEGA_EXPLORER_URL } = useEnvironment();
const headerClassName = 'uppercase text-lg';
const dayVolume = calcCandleVolume(market);
const assetSymbol =
market?.tradableInstrument.instrument.product?.settlementAsset.symbol || '';
const quoteUnit =
@ -105,6 +96,8 @@ export const Info = ({ market, onSelect }: InfoProps) => {
market.accountsConnection?.edges
);
const last24hourVolume = market.candles && calcCandleVolume(market.candles);
const marketDataPanels = [
{
title: t('Current fees'),
@ -131,13 +124,9 @@ export const Info = ({ market, onSelect }: InfoProps) => {
<>
<MarketInfoTable
data={{
...pick(
market.data,
'name',
'markPrice',
'bestBidPrice',
'bestOfferPrice'
),
markPrice: market.data?.markPrice,
bestBidPrice: market.data?.bestBidPrice,
bestOfferPrice: market.data?.bestOfferPrice,
quoteUnit: market.tradableInstrument.instrument.product.quoteName,
}}
decimalPlaces={market.decimalPlaces}
@ -157,16 +146,17 @@ export const Info = ({ market, onSelect }: InfoProps) => {
<MarketInfoTable
data={{
'24hourVolume':
dayVolume && dayVolume !== '0' ? formatNumber(dayVolume) : '-',
...pick(
market.data,
'openInterest',
'name',
'bestBidVolume',
'bestOfferVolume',
'bestStaticBidVolume',
'bestStaticOfferVolume'
),
last24hourVolume && last24hourVolume !== '0'
? addDecimalsFormatNumber(
last24hourVolume,
market.positionDecimalPlaces
)
: '-',
openInterest: market.data?.openInterest,
bestBidVolume: market.data?.bestBidVolume,
bestOfferVolume: market.data?.bestOfferVolume,
bestStaticBidVolume: market.data?.bestStaticBidVolume,
bestStaticOfferVolume: market.data?.bestStaticOfferVolume,
}}
decimalPlaces={market.positionDecimalPlaces}
/>
@ -192,7 +182,9 @@ export const Info = ({ market, onSelect }: InfoProps) => {
];
const keyDetails = {
...pick(market, 'decimalPlaces', 'positionDecimalPlaces', 'tradingMode'),
decimalPlaces: market.decimalPlaces,
positionDecimalPlaces: market.positionDecimalPlaces,
tradingMode: market.tradingMode,
state: Schema.MarketStateMapping[market.state],
};

View File

@ -1,25 +1,68 @@
import { makeDataProvider } from '@vegaprotocol/utils';
import type { MarketInfoQuery } from './__generated__/MarketInfo';
import { makeDataProvider, makeDerivedDataProvider } from '@vegaprotocol/utils';
import type {
MarketInfoQuery,
MarketInfoQueryVariables,
} from './__generated__/MarketInfo';
import {
marketDataProvider,
marketCandlesProvider,
} from '@vegaprotocol/market-list';
import type {
MarketData,
Candle,
MarketCandlesQueryVariables,
} from '@vegaprotocol/market-list';
import { MarketInfoDocument } from './__generated__/MarketInfo';
import type { MarketInfoNoCandlesQuery } from './__generated__/MarketInfoNoCandles';
import { MarketInfoNoCandlesDocument } from './__generated__/MarketInfoNoCandles';
export const marketInfoDataProvider = makeDataProvider<
MarketInfoQuery,
export type MarketInfo = NonNullable<MarketInfoQuery['market']>;
export type MarketInfoWithData = MarketInfo & { data?: MarketData };
export type MarketInfoWithDataAndCandles = MarketInfoWithData & {
candles?: Candle[];
};
const getData = (responseData: MarketInfoQuery | null) =>
responseData?.market || null;
export const marketInfoProvider = makeDataProvider<
MarketInfoQuery,
MarketInfoQuery['market'],
never,
never
never,
MarketInfoQueryVariables
>({
query: MarketInfoDocument,
getData: (responseData: MarketInfoQuery | null) => responseData,
getData,
});
export const marketInfoNoCandlesDataProvider = makeDataProvider<
MarketInfoNoCandlesQuery,
MarketInfoNoCandlesQuery,
export const marketInfoWithDataProvider = makeDerivedDataProvider<
MarketInfoWithData,
never,
never
>({
query: MarketInfoNoCandlesDocument,
getData: (responseData: MarketInfoNoCandlesQuery | null) => responseData,
MarketInfoQueryVariables
>([marketInfoProvider, marketDataProvider], (parts) => {
const market: MarketInfo | null = parts[0];
const marketData: MarketData | null = parts[1];
return (
market && {
...market,
data: marketData || undefined,
}
);
});
export const marketInfoWithDataAndCandlesProvider = makeDerivedDataProvider<
MarketInfoWithDataAndCandles,
never,
MarketCandlesQueryVariables
>([marketInfoProvider, marketDataProvider, marketCandlesProvider], (parts) => {
const market: MarketInfo | null = parts[0];
const marketData: MarketData | null = parts[1];
const candles: Candle[] | null = parts[2];
return (
market && {
...market,
data: marketData || undefined,
candles: candles || undefined,
}
);
});

View File

@ -93,40 +93,6 @@ export const marketInfoQuery = (
long: '0.008508132993273576',
},
lpPriceRange: '0.02',
data: {
__typename: 'MarketData',
market: {
__typename: 'Market',
id: '54b78c1b877e106842ae156332ccec740ad98d6bad43143ac6a029501dd7c6e0',
},
midPrice: '5749',
markPrice: '5749',
suppliedStake: '56767',
marketValueProxy: '677678',
targetStake: '56789',
bestBidVolume: '5',
bestOfferVolume: '1',
bestStaticBidVolume: '5',
bestStaticOfferVolume: '1',
openInterest: '0',
bestBidPrice: '681765',
bestOfferPrice: '681769',
trigger: Schema.AuctionTrigger.AUCTION_TRIGGER_UNSPECIFIED,
priceMonitoringBounds: [
{
minValidPrice: '654701',
maxValidPrice: '797323',
trigger: {
horizonSecs: 43200,
probability: 0.9999999,
auctionExtensionSecs: 600,
__typename: 'PriceMonitoringTrigger',
},
referencePrice: '722625',
__typename: 'PriceMonitoringBounds',
},
],
},
liquidityMonitoringParameters: {
triggeringRatio: '0',
targetStakeParameters: {
@ -136,7 +102,6 @@ export const marketInfoQuery = (
},
__typename: 'LiquidityMonitoringParameters',
},
candlesConnection: null,
tradableInstrument: {
__typename: 'TradableInstrument',
instrument: {
@ -192,13 +157,6 @@ export const marketInfoQuery = (
},
},
},
depth: {
__typename: 'MarketDepth',
lastTrade: {
__typename: 'Trade',
price: '100',
},
},
},
};

View File

@ -3,40 +3,59 @@ import * as Types from '@vegaprotocol/types';
import { gql } from '@apollo/client';
import * as Apollo from '@apollo/client';
const defaultOptions = {} as const;
export type MarketDataUpdateFieldsFragment = { __typename?: 'ObservableMarketData', marketId: string, bestBidPrice: string, bestOfferPrice: string, markPrice: string, trigger: Types.AuctionTrigger, staticMidPrice: string, marketTradingMode: Types.MarketTradingMode, marketState: Types.MarketState, indicativeVolume: string, indicativePrice: string, bestStaticBidPrice: string, bestStaticOfferPrice: string, targetStake?: string | null, suppliedStake?: string | null };
export type MarketDataUpdateFieldsFragment = { __typename?: 'ObservableMarketData', marketId: string, auctionEnd?: string | null, auctionStart?: string | null, bestBidPrice: string, bestBidVolume: string, bestOfferPrice: string, bestOfferVolume: string, bestStaticBidPrice: string, bestStaticBidVolume: string, bestStaticOfferPrice: string, bestStaticOfferVolume: string, indicativePrice: string, indicativeVolume: string, marketState: Types.MarketState, marketTradingMode: Types.MarketTradingMode, marketValueProxy: string, markPrice: string, midPrice: string, openInterest: string, staticMidPrice: string, suppliedStake?: string | null, targetStake?: string | null, trigger: Types.AuctionTrigger, priceMonitoringBounds?: Array<{ __typename?: 'PriceMonitoringBounds', minValidPrice: string, maxValidPrice: string, referencePrice: string, trigger: { __typename?: 'PriceMonitoringTrigger', horizonSecs: number, probability: number, auctionExtensionSecs: number } }> | null };
export type MarketDataUpdateSubscriptionVariables = Types.Exact<{
marketId: Types.Scalars['ID'];
}>;
export type MarketDataUpdateSubscription = { __typename?: 'Subscription', marketsData: Array<{ __typename?: 'ObservableMarketData', marketId: string, bestBidPrice: string, bestOfferPrice: string, markPrice: string, trigger: Types.AuctionTrigger, staticMidPrice: string, marketTradingMode: Types.MarketTradingMode, marketState: Types.MarketState, indicativeVolume: string, indicativePrice: string, bestStaticBidPrice: string, bestStaticOfferPrice: string, targetStake?: string | null, suppliedStake?: string | null }> };
export type MarketDataUpdateSubscription = { __typename?: 'Subscription', marketsData: Array<{ __typename?: 'ObservableMarketData', marketId: string, auctionEnd?: string | null, auctionStart?: string | null, bestBidPrice: string, bestBidVolume: string, bestOfferPrice: string, bestOfferVolume: string, bestStaticBidPrice: string, bestStaticBidVolume: string, bestStaticOfferPrice: string, bestStaticOfferVolume: string, indicativePrice: string, indicativeVolume: string, marketState: Types.MarketState, marketTradingMode: Types.MarketTradingMode, marketValueProxy: string, markPrice: string, midPrice: string, openInterest: string, staticMidPrice: string, suppliedStake?: string | null, targetStake?: string | null, trigger: Types.AuctionTrigger, priceMonitoringBounds?: Array<{ __typename?: 'PriceMonitoringBounds', minValidPrice: string, maxValidPrice: string, referencePrice: string, trigger: { __typename?: 'PriceMonitoringTrigger', horizonSecs: number, probability: number, auctionExtensionSecs: number } }> | null }> };
export type MarketDataFieldsFragment = { __typename?: 'MarketData', bestBidPrice: string, bestOfferPrice: string, markPrice: string, trigger: Types.AuctionTrigger, staticMidPrice: string, marketTradingMode: Types.MarketTradingMode, marketState: Types.MarketState, indicativeVolume: string, indicativePrice: string, bestStaticBidPrice: string, bestStaticOfferPrice: string, targetStake?: string | null, suppliedStake?: string | null, auctionStart?: string | null, auctionEnd?: string | null, market: { __typename?: 'Market', id: string } };
export type MarketDataFieldsFragment = { __typename?: 'MarketData', auctionEnd?: string | null, auctionStart?: string | null, bestBidPrice: string, bestBidVolume: string, bestOfferPrice: string, bestOfferVolume: string, bestStaticBidPrice: string, bestStaticBidVolume: string, bestStaticOfferPrice: string, bestStaticOfferVolume: string, indicativePrice: string, indicativeVolume: string, marketState: Types.MarketState, marketTradingMode: Types.MarketTradingMode, marketValueProxy: string, markPrice: string, midPrice: string, openInterest: string, staticMidPrice: string, suppliedStake?: string | null, targetStake?: string | null, trigger: Types.AuctionTrigger, market: { __typename?: 'Market', id: string }, priceMonitoringBounds?: Array<{ __typename?: 'PriceMonitoringBounds', minValidPrice: string, maxValidPrice: string, referencePrice: string, trigger: { __typename?: 'PriceMonitoringTrigger', horizonSecs: number, probability: number, auctionExtensionSecs: number } }> | null };
export type MarketDataQueryVariables = Types.Exact<{
marketId: Types.Scalars['ID'];
}>;
export type MarketDataQuery = { __typename?: 'Query', marketsConnection?: { __typename?: 'MarketConnection', edges: Array<{ __typename?: 'MarketEdge', node: { __typename?: 'Market', data?: { __typename?: 'MarketData', bestBidPrice: string, bestOfferPrice: string, markPrice: string, trigger: Types.AuctionTrigger, staticMidPrice: string, marketTradingMode: Types.MarketTradingMode, marketState: Types.MarketState, indicativeVolume: string, indicativePrice: string, bestStaticBidPrice: string, bestStaticOfferPrice: string, targetStake?: string | null, suppliedStake?: string | null, auctionStart?: string | null, auctionEnd?: string | null, market: { __typename?: 'Market', id: string } } | null } }> } | null };
export type MarketDataQuery = { __typename?: 'Query', marketsConnection?: { __typename?: 'MarketConnection', edges: Array<{ __typename?: 'MarketEdge', node: { __typename?: 'Market', data?: { __typename?: 'MarketData', auctionEnd?: string | null, auctionStart?: string | null, bestBidPrice: string, bestBidVolume: string, bestOfferPrice: string, bestOfferVolume: string, bestStaticBidPrice: string, bestStaticBidVolume: string, bestStaticOfferPrice: string, bestStaticOfferVolume: string, indicativePrice: string, indicativeVolume: string, marketState: Types.MarketState, marketTradingMode: Types.MarketTradingMode, marketValueProxy: string, markPrice: string, midPrice: string, openInterest: string, staticMidPrice: string, suppliedStake?: string | null, targetStake?: string | null, trigger: Types.AuctionTrigger, market: { __typename?: 'Market', id: string }, priceMonitoringBounds?: Array<{ __typename?: 'PriceMonitoringBounds', minValidPrice: string, maxValidPrice: string, referencePrice: string, trigger: { __typename?: 'PriceMonitoringTrigger', horizonSecs: number, probability: number, auctionExtensionSecs: number } }> | null } | null } }> } | null };
export const MarketDataUpdateFieldsFragmentDoc = gql`
fragment MarketDataUpdateFields on ObservableMarketData {
marketId
auctionEnd
auctionStart
bestBidPrice
bestBidVolume
bestOfferPrice
markPrice
trigger
staticMidPrice
marketTradingMode
marketState
indicativeVolume
indicativePrice
bestOfferVolume
bestStaticBidPrice
bestStaticBidVolume
bestStaticOfferPrice
targetStake
bestStaticOfferVolume
indicativePrice
indicativeVolume
marketState
marketTradingMode
marketValueProxy
markPrice
midPrice
openInterest
priceMonitoringBounds {
minValidPrice
maxValidPrice
trigger {
horizonSecs
probability
auctionExtensionSecs
}
referencePrice
}
staticMidPrice
suppliedStake
targetStake
trigger
}
`;
export const MarketDataFieldsFragmentDoc = gql`
@ -44,21 +63,38 @@ export const MarketDataFieldsFragmentDoc = gql`
market {
id
}
bestBidPrice
bestOfferPrice
markPrice
trigger
staticMidPrice
marketTradingMode
marketState
indicativeVolume
indicativePrice
bestStaticBidPrice
bestStaticOfferPrice
targetStake
suppliedStake
auctionStart
auctionEnd
auctionStart
bestBidPrice
bestBidVolume
bestOfferPrice
bestOfferVolume
bestStaticBidPrice
bestStaticBidVolume
bestStaticOfferPrice
bestStaticOfferVolume
indicativePrice
indicativeVolume
marketState
marketTradingMode
marketValueProxy
markPrice
midPrice
openInterest
priceMonitoringBounds {
minValidPrice
maxValidPrice
trigger {
horizonSecs
probability
auctionExtensionSecs
}
referencePrice
}
staticMidPrice
suppliedStake
targetStake
trigger
}
`;
export const MarketDataUpdateDocument = gql`

View File

@ -13,13 +13,14 @@ export const MarketsContainer = ({ onSelect }: MarketsContainerProps) => {
const { data, error, loading, reload } = useDataProvider({
dataProvider,
skipUpdates: true,
variables: undefined,
});
return (
<div className="h-full relative">
<MarketListTable
rowData={error ? [] : data}
noRowsOverlayComponent={() => null}
suppressLoadingOverlay
suppressNoRowsOverlay
onRowClicked={(rowEvent: RowClickedEvent) => {
const { data, event } = rowEvent;
// filters out clicks on the symbol column because it should display asset details

View File

@ -1,6 +1,7 @@
import { makeDataProvider } from '@vegaprotocol/utils';
import type {
MarketCandlesQuery,
MarketCandlesQueryVariables,
MarketCandlesUpdateSubscription,
MarketCandlesFieldsFragment,
} from './__generated__/market-candles';
@ -36,7 +37,8 @@ export const marketCandlesProvider = makeDataProvider<
MarketCandlesQuery,
Candle[],
MarketCandlesUpdateSubscription,
Candle
Candle,
MarketCandlesQueryVariables
>({
query: MarketCandlesDocument,
subscriptionQuery: MarketCandlesUpdateDocument,

View File

@ -1,5 +1,4 @@
import produce from 'immer';
import { useMemo } from 'react';
import { makeDataProvider, makeDerivedDataProvider } from '@vegaprotocol/utils';
import { useDataProvider } from '@vegaprotocol/react-helpers';
import {
@ -11,6 +10,7 @@ import type {
MarketDataFieldsFragment,
MarketDataUpdateSubscription,
MarketDataUpdateFieldsFragment,
MarketDataQueryVariables,
} from './__generated__/market-data';
export type MarketData = MarketDataFieldsFragment;
@ -29,7 +29,7 @@ const update = (
};
const getData = (responseData: MarketDataQuery | null): MarketData | null =>
responseData?.marketsConnection?.edges[0].node.data || null;
responseData?.marketsConnection?.edges[0]?.node?.data || null;
const getDelta = (
subscriptionData: MarketDataUpdateSubscription
@ -39,7 +39,8 @@ export const marketDataProvider = makeDataProvider<
MarketDataQuery,
MarketData,
MarketDataUpdateSubscription,
MarketDataUpdateFieldsFragment
MarketDataUpdateFieldsFragment,
MarketDataQueryVariables
>({
query: MarketDataDocument,
subscriptionQuery: MarketDataUpdateDocument,
@ -48,6 +49,12 @@ export const marketDataProvider = makeDataProvider<
getDelta,
});
export const markPriceProvider = makeDerivedDataProvider<
string,
never,
MarketDataQueryVariables
>([marketDataProvider], ([marketData]) => (marketData as MarketData).markPrice);
export type StaticMarketData = Pick<
MarketData,
| 'marketTradingMode'
@ -63,7 +70,8 @@ export type StaticMarketData = Pick<
export const staticMarketDataProvider = makeDerivedDataProvider<
StaticMarketData,
never
never,
MarketDataQueryVariables
>([marketDataProvider], (parts, variables, prevData) => {
const marketData = parts[0] as ReturnType<typeof getData>;
if (!marketData) {
@ -89,10 +97,9 @@ export const staticMarketDataProvider = makeDerivedDataProvider<
});
export const useStaticMarketData = (marketId?: string, skip?: boolean) => {
const variables = useMemo(() => ({ marketId }), [marketId]);
return useDataProvider({
dataProvider: staticMarketDataProvider,
variables,
variables: { marketId: marketId || '' },
skip: skip || !marketId,
});
};

View File

@ -1,18 +1,37 @@
fragment MarketDataUpdateFields on ObservableMarketData {
marketId
auctionEnd
auctionStart
bestBidPrice
bestBidVolume
bestOfferPrice
markPrice
trigger
staticMidPrice
marketTradingMode
marketState
indicativeVolume
indicativePrice
bestOfferVolume
bestStaticBidPrice
bestStaticBidVolume
bestStaticOfferPrice
targetStake
bestStaticOfferVolume
indicativePrice
indicativeVolume
marketState
marketTradingMode
marketValueProxy
markPrice
midPrice
openInterest
priceMonitoringBounds {
minValidPrice
maxValidPrice
trigger {
horizonSecs
probability
auctionExtensionSecs
}
referencePrice
}
staticMidPrice
suppliedStake
targetStake
trigger
}
subscription MarketDataUpdate($marketId: ID!) {
@ -25,21 +44,38 @@ fragment MarketDataFields on MarketData {
market {
id
}
bestBidPrice
bestOfferPrice
markPrice
trigger
staticMidPrice
marketTradingMode
marketState
indicativeVolume
indicativePrice
bestStaticBidPrice
bestStaticOfferPrice
targetStake
suppliedStake
auctionStart
auctionEnd
auctionStart
bestBidPrice
bestBidVolume
bestOfferPrice
bestOfferVolume
bestStaticBidPrice
bestStaticBidVolume
bestStaticOfferPrice
bestStaticOfferVolume
indicativePrice
indicativeVolume
marketState
marketTradingMode
marketValueProxy
markPrice
midPrice
openInterest
priceMonitoringBounds {
minValidPrice
maxValidPrice
trigger {
horizonSecs
probability
auctionExtensionSecs
}
referencePrice
}
staticMidPrice
suppliedStake
targetStake
trigger
}
query MarketData($marketId: ID!) {

View File

@ -44,34 +44,62 @@ const marketDataFields: MarketDataFieldsFragment = {
id: 'market-0',
__typename: 'Market',
},
auctionStart: '2022-06-21T17:18:43.484055236Z',
auctionEnd: '2022-06-21T17:18:43.484055236Z',
targetStake: '1000000',
suppliedStake: '1000',
marketTradingMode: Schema.MarketTradingMode.TRADING_MODE_CONTINUOUS,
marketState: Schema.MarketState.STATE_ACTIVE,
staticMidPrice: '0',
auctionStart: '2022-06-21T17:18:43.484055236Z',
bestBidPrice: '4412690058',
bestBidVolume: '1',
bestOfferPrice: '4812690058',
bestOfferVolume: '3',
bestStaticBidPrice: '4512690058',
bestStaticBidVolume: '2',
bestStaticOfferPrice: '4712690058',
bestStaticOfferVolume: '4',
indicativePrice: '0',
bestStaticBidPrice: '0',
bestStaticOfferPrice: '0',
indicativeVolume: '0',
bestBidPrice: '0',
bestOfferPrice: '0',
marketState: Schema.MarketState.STATE_ACTIVE,
marketTradingMode: Schema.MarketTradingMode.TRADING_MODE_CONTINUOUS,
marketValueProxy: '2000000',
markPrice: '4612690058',
midPrice: '4612690000',
openInterest: '0',
priceMonitoringBounds: [
{
minValidPrice: '654701',
maxValidPrice: '797323',
trigger: {
horizonSecs: 43200,
probability: 0.9999999,
auctionExtensionSecs: 600,
__typename: 'PriceMonitoringTrigger',
},
referencePrice: '722625',
__typename: 'PriceMonitoringBounds',
},
],
staticMidPrice: '4612690001',
suppliedStake: '1000',
targetStake: '1000000',
trigger: Schema.AuctionTrigger.AUCTION_TRIGGER_UNSPECIFIED,
};
const marketDataUpdateFields: MarketDataUpdateFieldsFragment = {
marketId: 'market-0',
marketTradingMode: Schema.MarketTradingMode.TRADING_MODE_CONTINUOUS,
marketState: Schema.MarketState.STATE_ACTIVE,
staticMidPrice: '0',
indicativePrice: '0',
bestStaticBidPrice: '0',
bestStaticOfferPrice: '0',
indicativeVolume: '0',
bestBidPrice: '0',
bestBidVolume: '0',
bestOfferPrice: '0',
bestOfferVolume: '0',
bestStaticBidPrice: '0',
bestStaticBidVolume: '0',
bestStaticOfferPrice: '0',
bestStaticOfferVolume: '0',
indicativePrice: '0',
indicativeVolume: '0',
marketId: 'market-0',
marketState: Schema.MarketState.STATE_ACTIVE,
marketTradingMode: Schema.MarketTradingMode.TRADING_MODE_CONTINUOUS,
marketValueProxy: '',
markPrice: '4612690058',
midPrice: '0',
openInterest: '0',
staticMidPrice: '0',
trigger: Schema.AuctionTrigger.AUCTION_TRIGGER_UNSPECIFIED,
};

View File

@ -1,6 +1,9 @@
import { makeDataProvider } from '@vegaprotocol/utils';
import { MarketsCandlesDocument } from './__generated__/markets-candles';
import type { MarketsCandlesQuery } from './__generated__/markets-candles';
import type {
MarketsCandlesQuery,
MarketsCandlesQueryVariables,
} from './__generated__/markets-candles';
import type { Candle } from './market-candles-provider';
export interface MarketCandles {
@ -22,7 +25,8 @@ export const marketsCandlesProvider = makeDataProvider<
MarketsCandlesQuery,
MarketCandles[],
never,
never
never,
MarketsCandlesQueryVariables
>({
query: MarketsCandlesDocument,
getData,

View File

@ -4,6 +4,8 @@ import type {
MarketsQuery,
MarketFieldsFragment,
} from './__generated__/markets';
import type { MarketsCandlesQueryVariables } from './__generated__/markets-candles';
import { marketsDataProvider } from './markets-data-provider';
import { marketDataProvider } from './market-data-provider';
import { marketsCandlesProvider } from './markets-candles-provider';
@ -37,7 +39,7 @@ export const marketProvider = makeDerivedDataProvider<
never,
{ marketId: string }
>(
[marketsProvider],
[(callback, client) => marketsProvider(callback, client, undefined)],
([markets], variables) =>
((markets as ReturnType<typeof getData>) || []).find(
(market) => market.id === variables?.marketId
@ -84,10 +86,11 @@ const addCandles = <T extends Market>(
export const marketsWithCandlesProvider = makeDerivedDataProvider<
MarketMaybeWithCandles[],
never
never,
MarketsCandlesQueryVariables
>(
[
(callback, client) => activeMarketsProvider(callback, client),
(callback, client) => activeMarketsProvider(callback, client, undefined),
marketsCandlesProvider,
],
(parts) => addCandles(parts[0] as Market[], parts[1] as MarketCandles[])
@ -113,10 +116,11 @@ export type MarketMaybeWithDataAndCandles = MarketMaybeWithData &
export const marketListProvider = makeDerivedDataProvider<
MarketMaybeWithDataAndCandles[],
never
never,
MarketsCandlesQueryVariables
>(
[
(callback, client) => marketsWithDataProvider(callback, client),
(callback, client) => marketsWithDataProvider(callback, client, undefined),
marketsCandlesProvider,
],
(parts) =>

View File

@ -76,7 +76,9 @@ const orderMatchFilters = (
return true;
};
const getData = (responseData: OrdersQuery | null) =>
const getData = (
responseData: OrdersQuery | null
): Edge<OrderFieldsFragment>[] =>
responseData?.party?.ordersConnection?.edges || [];
const getDelta = (subscriptionData: OrdersUpdateSubscription) =>
@ -142,14 +144,19 @@ export const update = (
__typename: 'Order',
},
cursor: '',
__typename: 'OrderEdge',
});
}
});
});
};
export const ordersProvider = makeDataProvider({
export const ordersProvider = makeDataProvider<
OrdersQuery,
ReturnType<typeof getData>,
OrdersUpdateSubscription,
ReturnType<typeof getDelta>,
OrdersQueryVariables
>({
query: OrdersDocument,
subscriptionQuery: OrdersUpdateDocument,
update,
@ -168,7 +175,10 @@ export const ordersWithMarketProvider = makeDerivedDataProvider<
Order[],
OrdersQueryVariables
>(
[ordersProvider, marketsProvider],
[
ordersProvider,
(callback, client) => marketsProvider(callback, client, undefined),
],
(partsData): OrderEdge[] =>
((partsData[0] as ReturnType<typeof getData>) || []).map((edge) => ({
cursor: edge.cursor,
@ -183,7 +193,13 @@ export const ordersWithMarketProvider = makeDerivedDataProvider<
combineInsertionData<Order>
);
const hasActiveOrderProviderInternal = makeDataProvider({
const hasActiveOrderProviderInternal = makeDataProvider<
OrdersQuery,
boolean,
OrdersUpdateSubscription,
ReturnType<typeof getDelta>,
OrdersQueryVariables
>({
query: OrdersDocument,
subscriptionQuery: OrdersUpdateDocument,
update: (

View File

@ -9,6 +9,8 @@ import type {
} from 'ag-grid-community';
import { Button } from '@vegaprotocol/ui-toolkit';
import type { AgGridReact } from 'ag-grid-react';
import type { GridReadyEvent } from 'ag-grid-community';
import { OrderListTable } from '../order-list/order-list';
import { useOrderListData } from './use-order-list-data';
import { useHasActiveOrder } from '../../order-hooks/use-has-active-order';
@ -156,6 +158,16 @@ export const OrderListManager = ({
},
[create]
);
const onGridReady = useCallback(
(event: GridReadyEvent) => {
event.api.setDatasource({
getRows,
});
},
[getRows]
);
const cancelAll = useCallback(
(marketId?: string) => {
create({
@ -177,7 +189,7 @@ export const OrderListManager = ({
<OrderListTable
ref={gridRef}
rowModelType="infinite"
datasource={{ getRows }}
onGridReady={onGridReady}
onBodyScrollEnd={onBodyScrollEnd}
onBodyScroll={onBodyScroll}
onFilterChanged={onFilterChanged}

View File

@ -139,18 +139,15 @@ export const useOrderListData = ({
});
totalCountRef.current = totalCount;
const getRows = makeInfiniteScrollGetRows<OrderEdge>(
dataRef,
totalCountRef,
load,
newRows
const getRows = useRef(
makeInfiniteScrollGetRows<OrderEdge>(dataRef, totalCountRef, load, newRows)
);
return {
loading,
error,
data,
addNewRows,
getRows,
getRows: getRows.current,
reload,
makeBottomPlaceholders,
};

View File

@ -1,7 +1,8 @@
export * from './__generated__/OrdersSubscription';
export * from './use-has-active-order';
export * from './use-order-cancel';
export * from './use-order-submit';
export * from './use-order-edit';
export * from './use-order-submit';
export * from './use-order-update';
export * from './use-pending-orders-volume';
export * from './use-order-store';

View File

@ -0,0 +1,74 @@
import { useState, useCallback } from 'react';
import { OrderStatus, Side } from '@vegaprotocol/types';
import { ordersProvider } from '../components/order-data-provider/order-data-provider';
import type { OrderFieldsFragment } from '../components/order-data-provider/__generated__/Orders';
import type { Edge } from '@vegaprotocol/utils';
import { useDataProvider } from '@vegaprotocol/react-helpers';
const sumVolume = (orders: (Edge<OrderFieldsFragment> | null)[], side: Side) =>
orders
.reduce(
(sum, order) =>
order?.node.side === side
? sum +
BigInt(
order?.node.status === OrderStatus.STATUS_PARTIALLY_FILLED
? order?.node.remaining
: order?.node.size
)
: sum,
BigInt(0)
)
.toString();
export const useActiveOrdersVolumeAndMargin = (
partyId: string | null | undefined,
marketId: string
) => {
const [buyVolume, setBuyVolume] = useState<string | undefined>();
const [sellVolume, setSellVolume] = useState<string | undefined>();
const [buyInitialMargin, setBuyInitialMargin] = useState<
string | undefined
>();
const [sellInitialMargin, setSellInitialMargin] = useState<
string | undefined
>();
const update = useCallback(
({ data }: { data: (Edge<OrderFieldsFragment> | null)[] | null }) => {
if (!data) {
setBuyVolume(undefined);
setSellVolume(undefined);
setBuyInitialMargin(undefined);
setSellInitialMargin(undefined);
} else {
setBuyVolume(sumVolume(data, Side.SIDE_BUY));
setSellVolume(sumVolume(data, Side.SIDE_SELL));
}
return true;
},
[]
);
useDataProvider({
dataProvider: ordersProvider,
update,
variables: {
partyId: partyId || '',
marketId,
filter: {
status: [
OrderStatus.STATUS_ACTIVE,
OrderStatus.STATUS_PARTIALLY_FILLED,
],
},
},
skip: !partyId,
});
return buyVolume || sellVolume
? {
buyVolume,
sellVolume,
buyInitialMargin,
sellInitialMargin,
}
: undefined;
};

View File

@ -1,8 +1,11 @@
export * from './lib/__generated__/Positions';
export * from './lib/positions-container';
export * from './lib/positions-data-providers';
export * from './lib/margin-data-provider';
export * from './lib/margin-calculator';
export * from './lib/positions-table';
export * from './lib/use-close-position';
export * from './lib/use-positions-data';
export * from './lib/use-market-position-open-volume';
export * from './lib/use-market-margin';
export * from './lib/use-market-position-open-volume';
export * from './lib/use-open-volume';
export * from './lib/use-positions-data';

View File

@ -5,9 +5,9 @@ import * as Apollo from '@apollo/client';
const defaultOptions = {} as const;
export type PositionFieldsFragment = { __typename?: 'Position', realisedPNL: string, openVolume: string, unrealisedPNL: string, averageEntryPrice: string, updatedAt?: any | null, positionStatus: Types.PositionStatus, lossSocializationAmount: string, market: { __typename?: 'Market', id: string } };
export type PositionsQueryVariables = Types.Exact<{
export type PositionsQueryVariables = {
partyId: Types.Scalars['ID'];
}>;
};
export type PositionsQuery = { __typename?: 'Query', party?: { __typename?: 'Party', id: string, positionsConnection?: { __typename?: 'PositionConnection', edges?: Array<{ __typename?: 'PositionEdge', node: { __typename?: 'Position', realisedPNL: string, openVolume: string, unrealisedPNL: string, averageEntryPrice: string, updatedAt?: any | null, positionStatus: Types.PositionStatus, lossSocializationAmount: string, market: { __typename?: 'Market', id: string } } }> | null } | null } | null };

View File

@ -0,0 +1,95 @@
import { toBigNum } from '@vegaprotocol/utils';
import { Side, MarketTradingMode, OrderType } from '@vegaprotocol/types';
import type { ScalingFactors, RiskFactor } from '@vegaprotocol/types';
import type { MarketData } from '@vegaprotocol/market-list';
export const isMarketInAuction = (marketTradingMode: MarketTradingMode) => {
return [
MarketTradingMode.TRADING_MODE_BATCH_AUCTION,
MarketTradingMode.TRADING_MODE_MONITORING_AUCTION,
MarketTradingMode.TRADING_MODE_OPENING_AUCTION,
].includes(marketTradingMode);
};
/**
* Get the market price based on market mode (auction or not auction)
*/
export const getMarketPrice = ({
marketTradingMode,
indicativePrice,
markPrice,
}: Pick<MarketData, 'marketTradingMode' | 'indicativePrice' | 'markPrice'>) => {
if (isMarketInAuction(marketTradingMode)) {
// 0 can never be a valid uncrossing price
// as it would require there being orders on the book at that price.
if (
indicativePrice &&
indicativePrice !== '0' &&
BigInt(indicativePrice) !== BigInt(0)
) {
return indicativePrice;
}
}
return markPrice;
};
/**
* Gets the price for an order, order limit this is the user
* entered value, for market this will be the mark price or
* if in auction the indicative uncrossing price
*/
export const getDerivedPrice = (
order: {
type?: OrderType | null;
price?: string;
},
marketData: Pick<
MarketData,
'marketTradingMode' | 'indicativePrice' | 'markPrice'
>
) => {
// If order type is market we should use either the mark price
// or the uncrossing price. If order type is limit use the price
// the user has input
// Use the market price if order is a market order
if (order.type === OrderType.TYPE_LIMIT && order.price) {
return order.price;
}
return getMarketPrice(marketData);
};
export const calculateMargins = ({
size,
side,
price,
decimals,
positionDecimalPlaces,
decimalPlaces,
scalingFactors,
riskFactors,
}: {
size: string;
side: Side;
positionDecimalPlaces: number;
decimalPlaces: number;
decimals: number;
price: string;
scalingFactors?: ScalingFactors;
riskFactors: RiskFactor;
}) => {
const maintenanceMargin = toBigNum(size, positionDecimalPlaces)
.multipliedBy(
side === Side.SIDE_SELL ? riskFactors.short : riskFactors.long
)
.multipliedBy(toBigNum(price, decimalPlaces));
return {
maintenanceMargin: maintenanceMargin
.multipliedBy(Math.pow(10, decimals))
.toFixed(0),
initialMargin: maintenanceMargin
.multipliedBy(scalingFactors?.initialMargin ?? 1)
.multipliedBy(Math.pow(10, decimals))
.toFixed(0),
};
};

View File

@ -1,5 +1,9 @@
import produce from 'immer';
import { makeDataProvider, removePaginationWrapper } from '@vegaprotocol/utils';
import {
makeDataProvider,
makeDerivedDataProvider,
removePaginationWrapper,
} from '@vegaprotocol/utils';
import {
MarginsSubscriptionDocument,
MarginsDocument,
@ -8,6 +12,7 @@ import type {
MarginsQuery,
MarginFieldsFragment,
MarginsSubscriptionSubscription,
MarginsQueryVariables,
} from './__generated__/Positions';
const update = (
@ -56,7 +61,8 @@ export const marginsDataProvider = makeDataProvider<
MarginsQuery,
MarginFieldsFragment[],
MarginsSubscriptionSubscription,
MarginsSubscriptionSubscription['margins']
MarginsSubscriptionSubscription['margins'],
MarginsQueryVariables
>({
query: MarginsDocument,
subscriptionQuery: MarginsSubscriptionDocument,
@ -64,3 +70,15 @@ export const marginsDataProvider = makeDataProvider<
getData,
getDelta,
});
export const marketMarginDataProvider = makeDerivedDataProvider<
MarginFieldsFragment,
never,
MarginsQueryVariables & { marketId: string }
>(
[marginsDataProvider],
(data, { marketId }) =>
(data[0] as MarginFieldsFragment[]).find(
(margin) => margin.market.id === marketId
) || null
);

View File

@ -11,19 +11,32 @@ import {
removePaginationWrapper,
} from '@vegaprotocol/utils';
import * as Schema from '@vegaprotocol/types';
import type { MarketMaybeWithData } from '@vegaprotocol/market-list';
import type {
MarketMaybeWithData,
MarketDataQueryVariables,
} from '@vegaprotocol/market-list';
import { marketsWithDataProvider } from '@vegaprotocol/market-list';
import type {
PositionsQuery,
PositionFieldsFragment,
PositionsSubscriptionSubscription,
MarginFieldsFragment,
PositionsQueryVariables,
} from './__generated__/Positions';
import {
PositionsDocument,
PositionsSubscriptionDocument,
} from './__generated__/Positions';
import { marginsDataProvider } from './margin-data-provider';
import { calculateMargins } from './margin-calculator';
import type { Edge } from '@vegaprotocol/utils';
import { OrderStatus, Side } from '@vegaprotocol/types';
import { marketInfoProvider } from '@vegaprotocol/market-info';
import type { MarketInfoQuery } from '@vegaprotocol/market-info';
import { marketDataProvider } from '@vegaprotocol/market-list';
import type { MarketData } from '@vegaprotocol/market-list';
import { ordersProvider } from '@vegaprotocol/orders';
import type { OrderFieldsFragment } from '@vegaprotocol/orders';
import type { PositionStatus } from '@vegaprotocol/types';
type PositionMarginLevel = Pick<
@ -229,7 +242,8 @@ export const positionsDataProvider = makeDataProvider<
PositionsQuery,
PositionFieldsFragment[],
PositionsSubscriptionSubscription,
PositionsSubscriptionSubscription['positions']
PositionsSubscriptionSubscription['positions'],
PositionsQueryVariables
>({
query: PositionsDocument,
subscriptionQuery: PositionsSubscriptionDocument,
@ -260,6 +274,32 @@ const upgradeMarginsConnection = (
return null;
};
export const positionDataProvider = makeDerivedDataProvider<
PositionFieldsFragment,
never,
PositionsQueryVariables & MarketDataQueryVariables
>(
[
(callback, client, variables) =>
positionsDataProvider(callback, client, {
partyId: variables?.partyId || '',
}),
],
(data, variables) =>
(data[0] as PositionFieldsFragment[] | null)?.find(
(p) => p.market.id === variables?.marketId
) || null
);
export const openVolumeDataProvider = makeDerivedDataProvider<
string,
never,
PositionsQueryVariables & MarketDataQueryVariables
>(
[positionDataProvider],
(data) => (data[0] as PositionFieldsFragment | null)?.openVolume || null
);
export const rejoinPositionData = (
positions: PositionFieldsFragment[] | null,
marketsData: MarketMaybeWithData[] | null,
@ -284,19 +324,15 @@ export const rejoinPositionData = (
return null;
};
export interface PositionsMetricsProviderVariables {
partyId: string;
}
export const positionsMetricsProvider = makeDerivedDataProvider<
Position[],
Position[],
PositionsMetricsProviderVariables
PositionsQueryVariables
>(
[
positionsDataProvider,
accountsDataProvider,
marketsWithDataProvider,
(callback, client) => marketsWithDataProvider(callback, client, undefined),
marginsDataProvider,
],
([positions, accounts, marketsData, margins], variables) => {
@ -317,3 +353,103 @@ export const positionsMetricsProvider = makeDerivedDataProvider<
return !(previousRow && isEqual(previousRow, row));
})
);
export const volumeAndMarginProvider = makeDerivedDataProvider<
{
buyVolume: string;
sellVolume: string;
buyInitialMargin: string;
sellInitialMargin: string;
},
never,
PositionsQueryVariables & MarketDataQueryVariables
>(
[
(callback, client, variables) =>
ordersProvider(callback, client, {
...variables,
filter: {
status: [
OrderStatus.STATUS_ACTIVE,
OrderStatus.STATUS_PARTIALLY_FILLED,
],
},
}),
(callback, client, variables) =>
marketDataProvider(callback, client, { marketId: variables.marketId }),
(callback, client, variables) =>
marketInfoProvider(callback, client, { marketId: variables.marketId }),
openVolumeDataProvider,
],
(data) => {
const orders = data[0] as (Edge<OrderFieldsFragment> | null)[] | null;
const marketData = data[1] as MarketData | null;
const marketInfo = data[2] as MarketInfoQuery['market'];
let openVolume = (data[3] as string | null) || '0';
const shortPosition = openVolume?.startsWith('-');
if (shortPosition) {
openVolume = openVolume.substring(1);
}
let buyVolume = BigInt(shortPosition ? 0 : openVolume);
let sellVolume = BigInt(shortPosition ? openVolume : 0);
let buyInitialMargin = BigInt(0);
let sellInitialMargin = BigInt(0);
if (marketInfo?.riskFactors && marketData) {
const {
positionDecimalPlaces,
decimalPlaces,
tradableInstrument,
riskFactors,
} = marketInfo;
const { marginCalculator, instrument } = tradableInstrument;
const { decimals } = instrument.product.settlementAsset;
const calculatorParams = {
positionDecimalPlaces,
decimalPlaces,
decimals,
scalingFactors: marginCalculator?.scalingFactors,
riskFactors,
};
if (openVolume !== '0') {
const { initialMargin } = calculateMargins({
side: shortPosition ? Side.SIDE_SELL : Side.SIDE_BUY,
size: openVolume,
price: marketData.markPrice,
...calculatorParams,
});
if (shortPosition) {
sellInitialMargin += BigInt(initialMargin);
} else {
buyInitialMargin += BigInt(initialMargin);
}
}
orders?.forEach((order) => {
if (!order) {
return;
}
const { side, remaining: size } = order.node;
const initialMargin = BigInt(
calculateMargins({
side,
size,
price: marketData.markPrice, //getDerivedPrice(order.node, marketData), same use-initial-margin
...calculatorParams,
}).initialMargin
);
if (order.node.side === Side.SIDE_BUY) {
buyVolume += BigInt(size);
buyInitialMargin += initialMargin;
} else {
sellVolume += BigInt(size);
sellInitialMargin += initialMargin;
}
});
}
return {
buyVolume: buyVolume.toString(),
sellVolume: sellVolume.toString(),
buyInitialMargin: buyInitialMargin.toString(),
sellInitialMargin: sellInitialMargin.toString(),
};
}
);

View File

@ -0,0 +1,21 @@
import { useState, useCallback } from 'react';
import { openVolumeDataProvider } from './positions-data-providers';
import { useDataProvider } from '@vegaprotocol/react-helpers';
export const useOpenVolume = (
partyId: string | null | undefined,
marketId: string
) => {
const [openVolume, setOpenVolume] = useState<string | undefined>(undefined);
const update = useCallback(({ data }: { data: string | null }) => {
setOpenVolume(data ?? undefined);
return true;
}, []);
useDataProvider({
dataProvider: openVolumeDataProvider,
update,
variables: { partyId: partyId || '', marketId },
skip: !partyId,
});
return openVolume;
};

View File

@ -3,7 +3,7 @@ import type { RefObject } from 'react';
import type { AgGridReact } from 'ag-grid-react';
import type { Position } from './positions-data-providers';
import { positionsMetricsProvider } from './positions-data-providers';
import type { PositionsMetricsProviderVariables } from './positions-data-providers';
import type { PositionsQueryVariables } from './__generated__/Positions';
import { useDataProvider, updateGridData } from '@vegaprotocol/react-helpers';
import type { GetRowsParams } from '@vegaprotocol/datagrid';
@ -14,7 +14,7 @@ export const usePositionsData = (
gridRef: RefObject<AgGridReact>,
clientSideModel?: boolean
) => {
const variables = useMemo<PositionsMetricsProviderVariables>(
const variables = useMemo<PositionsQueryVariables>(
() => ({ partyId }),
[partyId]
);

View File

@ -1,6 +1,7 @@
import { makeDataProvider } from '@vegaprotocol/utils';
import type {
ProposalsListQuery,
ProposalsListQueryVariables,
ProposalListFieldsFragment,
} from './__generated__/Proposals';
import { ProposalsListDocument } from './__generated__/Proposals';
@ -14,5 +15,6 @@ export const proposalsDataProvider = makeDataProvider<
ProposalsListQuery,
ProposalListFieldsFragment[],
never,
never
never,
ProposalsListQueryVariables
>({ query: ProposalsListDocument, getData });

View File

@ -3,7 +3,7 @@ import { AgGridDynamic as AgGrid } from '@vegaprotocol/datagrid';
import { useDataProvider } from '@vegaprotocol/react-helpers';
import * as Types from '@vegaprotocol/types';
import { proposalsDataProvider } from '../proposals-data-provider';
import { useCallback, useMemo, useRef } from 'react';
import { useCallback, useRef } from 'react';
import type { AgGridReact } from 'ag-grid-react';
import { useColumnDefs } from './use-column-defs';
import type { ProposalListFieldsFragment } from '../proposals-data-provider/__generated__/Proposals';
@ -22,14 +22,11 @@ export const ProposalsList = () => {
const handleOnGridReady = useCallback(() => {
gridRef.current?.api?.sizeColumnsToFit();
}, [gridRef]);
const variables = useMemo(() => {
return {
proposalType: Types.ProposalType.TYPE_NEW_MARKET,
};
}, []);
const { data, loading, error, reload } = useDataProvider({
dataProvider: proposalsDataProvider,
variables,
variables: {
proposalType: Types.ProposalType.TYPE_NEW_MARKET,
},
});
const filteredData = getNewMarketProposals(data || []);
const { columnDefs, defaultColDef } = useColumnDefs();

View File

@ -6,6 +6,7 @@ import { MockedProvider } from '@apollo/client/testing';
type Data = number;
type Delta = number;
type Variables = { partyId: string };
const unsubscribe = jest.fn();
const reload = jest.fn();
@ -23,8 +24,8 @@ const updateCallbackPayload: Parameters<UpdateCallback<Data, Delta>>['0'] = {
};
const dataProvider = jest.fn<
ReturnType<Subscribe<Data, Delta>>,
Parameters<Subscribe<Data, Delta>>
ReturnType<Subscribe<Data, Delta, Variables>>,
Parameters<Subscribe<Data, Delta, Variables>>
>();
dataProvider.mockReturnValue({
@ -37,10 +38,12 @@ dataProvider.mockReturnValue({
jest.useFakeTimers();
describe('useDataProvider hook', () => {
const render = (initialProps?: useDataProviderParams<Data, Delta>) =>
const render = (
initialProps?: useDataProviderParams<Data, Delta, Variables>
) =>
renderHook<
ReturnType<typeof useDataProvider>,
useDataProviderParams<Data, Delta>
useDataProviderParams<Data, Delta, Variables>
>((props) => useDataProvider(props), {
wrapper: MockedProvider,
initialProps,
@ -224,6 +227,7 @@ describe('useThrottledDataProvider hook', () => {
useThrottledDataProvider(
{
dataProvider,
variables,
},
wait
),

View File

@ -9,7 +9,7 @@ import type { Subscribe, Load, UpdateCallback } from '@vegaprotocol/utils';
export interface useDataProviderParams<
Data,
Delta,
Variables extends OperationVariables = OperationVariables
Variables extends OperationVariables | undefined = undefined
> {
dataProvider: Subscribe<Data, Delta, Variables>;
update?: ({
@ -30,7 +30,7 @@ export interface useDataProviderParams<
data: Data | null;
totalCount?: number;
}) => boolean;
variables?: Variables;
variables: Variables;
skipUpdates?: boolean;
skip?: boolean;
}
@ -45,7 +45,7 @@ export interface useDataProviderParams<
export const useDataProvider = <
Data,
Delta,
Variables extends OperationVariables = OperationVariables
Variables extends OperationVariables | undefined = undefined
>({
dataProvider,
update,
@ -163,8 +163,12 @@ export const useDataProvider = <
};
};
export const useThrottledDataProvider = <Data, Delta>(
params: Omit<useDataProviderParams<Data, Delta>, 'update'>,
export const useThrottledDataProvider = <
Data,
Delta,
Variables extends OperationVariables = OperationVariables
>(
params: Omit<useDataProviderParams<Data, Delta, Variables>, 'update'>,
wait?: number
) => {
const [data, setData] = useState<Data | null>(null);

View File

@ -2,12 +2,11 @@ import { makeInfiniteScrollGetRows } from '@vegaprotocol/utils';
import { useDataProvider } from '@vegaprotocol/react-helpers';
import { AsyncRenderer } from '@vegaprotocol/ui-toolkit';
import type { AgGridReact } from 'ag-grid-react';
import { useCallback, useMemo, useRef } from 'react';
import { useCallback, useRef } from 'react';
import type { BodyScrollEvent, BodyScrollEndEvent } from 'ag-grid-community';
import { MAX_TRADES, tradesWithMarketProvider } from './trades-data-provider';
import { tradesWithMarketProvider } from './trades-data-provider';
import { TradesTable } from './trades-table';
import type { Trade, TradeEdge } from './trades-data-provider';
import type { TradesQueryVariables } from './__generated__/Trades';
import { useOrderStore } from '@vegaprotocol/orders';
interface TradesContainerProps {
@ -22,11 +21,6 @@ export const TradesContainer = ({ marketId }: TradesContainerProps) => {
const scrolledToTop = useRef(true);
const updateOrder = useOrderStore((store) => store.update);
const variables = useMemo<TradesQueryVariables>(
() => ({ marketId, maxTrades: MAX_TRADES }),
[marketId]
);
const addNewRows = useCallback(() => {
if (newRows.current === 0) {
return;
@ -84,7 +78,7 @@ export const TradesContainer = ({ marketId }: TradesContainerProps) => {
dataProvider: tradesWithMarketProvider,
update,
insert,
variables,
variables: { marketId },
});
totalCountRef.current = totalCount;
const getRows = makeInfiniteScrollGetRows<TradeEdge>(

View File

@ -10,6 +10,7 @@ import type { Market } from '@vegaprotocol/market-list';
import { marketsProvider } from '@vegaprotocol/market-list';
import type {
TradesQuery,
TradesQueryVariables,
TradeFieldsFragment,
TradesUpdateSubscription,
} from './__generated__/Trades';
@ -67,10 +68,16 @@ const update = (
export type Trade = Omit<TradeFieldsFragment, 'market'> & { market?: Market };
export type TradeEdge = Edge<Trade>;
const getPageInfo = (responseData: TradesQuery): PageInfo | null =>
responseData.market?.tradesConnection?.pageInfo || null;
const getPageInfo = (responseData: TradesQuery | null): PageInfo | null =>
responseData?.market?.tradesConnection?.pageInfo || null;
export const tradesProvider = makeDataProvider({
export const tradesProvider = makeDataProvider<
Parameters<typeof getData>['0'],
ReturnType<typeof getData>,
Parameters<typeof getDelta>['0'],
ReturnType<typeof getDelta>,
TradesQueryVariables
>({
query: TradesDocument,
subscriptionQuery: TradesUpdateDocument,
update,
@ -85,9 +92,13 @@ export const tradesProvider = makeDataProvider({
export const tradesWithMarketProvider = makeDerivedDataProvider<
(TradeEdge | null)[],
Trade[]
Trade[],
TradesQueryVariables
>(
[tradesProvider, marketsProvider],
[
tradesProvider,
(callback, client) => marketsProvider(callback, client, undefined),
],
(partsData): (TradeEdge | null)[] | null => {
const edges = partsData[0] as ReturnType<typeof getData>;
return edges.map((edge) => {

View File

@ -45,10 +45,11 @@ type CombinedData = {
type SubscriptionData = QueryData;
type Delta = Data;
type Variables = { var: string };
const update = jest.fn<
ReturnType<Update<Data, Delta>>,
Parameters<Update<Data, Delta>>
ReturnType<Update<Data, Delta, Variables>>,
Parameters<Update<Data, Delta, Variables>>
>();
const callback = jest.fn<
@ -66,7 +67,13 @@ const getData = jest.fn((r: QueryData | null) => r?.data || null);
const getDelta = jest.fn((r: SubscriptionData) => r.data);
const subscribe = makeDataProvider<QueryData, Data, SubscriptionData, Delta>({
const subscribe = makeDataProvider<
QueryData,
Data,
SubscriptionData,
Delta,
Variables
>({
query,
subscriptionQuery,
update,
@ -75,18 +82,18 @@ const subscribe = makeDataProvider<QueryData, Data, SubscriptionData, Delta>({
});
const combineData = jest.fn<
ReturnType<CombineDerivedData<CombinedData>>,
Parameters<CombineDerivedData<CombinedData>>
ReturnType<CombineDerivedData<CombinedData, Variables>>,
Parameters<CombineDerivedData<CombinedData, Variables>>
>();
const combineDelta = jest.fn<
ReturnType<CombineDerivedDelta<CombinedData, CombinedData>>,
Parameters<CombineDerivedDelta<CombinedData, CombinedData>>
ReturnType<CombineDerivedDelta<CombinedData, CombinedData, Variables>>,
Parameters<CombineDerivedDelta<CombinedData, CombinedData, Variables>>
>();
const combineInsertionData = jest.fn<
ReturnType<CombineInsertionData<CombinedData>>,
Parameters<CombineInsertionData<CombinedData>>
ReturnType<CombineInsertionData<CombinedData, Variables>>,
Parameters<CombineInsertionData<CombinedData, Variables>>
>();
const first = 100;
@ -94,7 +101,8 @@ const paginatedSubscribe = makeDataProvider<
QueryData,
Data,
SubscriptionData,
Delta
Delta,
Variables
>({
query,
subscriptionQuery,
@ -116,7 +124,8 @@ const errorGuardedSubscribe = makeDataProvider<
QueryData,
Data,
SubscriptionData,
Delta
Delta,
Variables
>({
query,
subscriptionQuery,
@ -210,6 +219,8 @@ const clearPendingQueries = () => {
}
};
const variables = { var: 'val' };
describe('data provider', () => {
beforeEach(() => {
clearPendingQueries();
@ -220,7 +231,6 @@ describe('data provider', () => {
clientSubscribeSubscribe.mockClear();
});
it('memoize instance and unsubscribe if no subscribers', () => {
const variables = { var: 'val' };
const subscription1 = subscribe(jest.fn(), client, variables);
const subscription2 = subscribe(jest.fn(), client, { ...variables });
// const subscription1 = subscribe(jest.fn(), client);
@ -234,7 +244,7 @@ describe('data provider', () => {
it('calls callback before and after initial fetch', async () => {
const data: Item[] = [];
const subscription = subscribe(callback, client);
const subscription = subscribe(callback, client, variables);
expect(callback.mock.calls.length).toBe(1);
expect(callback.mock.calls[0][0].data).toBe(null);
expect(callback.mock.calls[0][0].loading).toBe(true);
@ -246,7 +256,7 @@ describe('data provider', () => {
});
it('calls callback on error', async () => {
const subscription = subscribe(callback, client);
const subscription = subscribe(callback, client, variables);
expect(callback.mock.calls.length).toBe(1);
expect(callback.mock.calls[0][0].data).toBe(null);
expect(callback.mock.calls[0][0].loading).toBe(true);
@ -260,7 +270,7 @@ describe('data provider', () => {
});
it('calls successful callback on NotFound error on party path', async () => {
const subscription = subscribe(callback, client);
const subscription = subscribe(callback, client, variables);
expect(callback.mock.calls.length).toBe(1);
expect(callback.mock.calls[0][0].data).toBe(null);
expect(callback.mock.calls[0][0].loading).toBe(true);
@ -280,7 +290,7 @@ describe('data provider', () => {
const data: Data = [];
getData.mockReturnValueOnce(data);
await rejectQuery(error);
expect(getData).toHaveBeenCalledWith(null, undefined);
expect(getData).toHaveBeenCalledWith(null, variables);
expect(callback.mock.calls.length).toBe(2);
expect(callback.mock.calls[1][0].data).toEqual(data);
expect(callback.mock.calls[1][0].error).toEqual(undefined);
@ -290,7 +300,7 @@ describe('data provider', () => {
it('calls update and callback on each update', async () => {
const data: Item[] = [];
const subscription = subscribe(callback, client);
const subscription = subscribe(callback, client, variables);
await resolveQuery({ data });
const delta: Item[] = [];
update.mockImplementationOnce((data, delta) => [...(data || []), ...delta]);
@ -308,7 +318,7 @@ describe('data provider', () => {
it("don't calls callback on update if data doesn't change", async () => {
const data: Item[] = [];
const subscription = subscribe(callback, client);
const subscription = subscribe(callback, client, variables);
await resolveQuery({ data });
const delta: Item[] = [];
update.mockImplementationOnce((data) => data || []);
@ -325,7 +335,7 @@ describe('data provider', () => {
it('refetch data on reload', async () => {
const data: Item[] = [];
const subscription = subscribe(callback, client);
const subscription = subscribe(callback, client, variables);
await resolveQuery({ data });
subscription.reload();
await resolveQuery({ data });
@ -337,7 +347,7 @@ describe('data provider', () => {
it('refetch data and restart subscription on reload with force', async () => {
const data: Item[] = [];
const subscription = subscribe(callback, client);
const subscription = subscribe(callback, client, variables);
await resolveQuery({ data });
subscription.reload(true);
await resolveQuery({ data });
@ -349,7 +359,7 @@ describe('data provider', () => {
it('calls callback on flush', async () => {
const data: Item[] = [];
const subscription = subscribe(callback, client);
const subscription = subscribe(callback, client, variables);
await resolveQuery({ data });
const callbackCallsLength = callback.mock.calls.length;
subscription.flush();
@ -365,7 +375,7 @@ describe('data provider', () => {
id: i.toString(),
},
}));
const subscription = paginatedSubscribe(callback, client);
const subscription = paginatedSubscribe(callback, client, variables);
await resolveQuery({
data,
totalCount,
@ -379,7 +389,7 @@ describe('data provider', () => {
it('loads requested data blocks and inserts data with total count', async () => {
const totalCount = 1000;
const subscription = paginatedSubscribe(callback, client);
const subscription = paginatedSubscribe(callback, client, variables);
await resolveQuery({
data: generateData(),
totalCount,
@ -503,7 +513,7 @@ describe('data provider', () => {
it('loads requested data blocks and inserts data without totalCount', async () => {
const totalCount = undefined;
const subscription = paginatedSubscribe(callback, client);
const subscription = paginatedSubscribe(callback, client, variables);
await resolveQuery({
data: generateData(),
totalCount,
@ -542,7 +552,7 @@ describe('data provider', () => {
});
it('sets total count when first page has no next page', async () => {
const subscription = paginatedSubscribe(callback, client);
const subscription = paginatedSubscribe(callback, client, variables);
await resolveQuery({
data: generateData(),
pageInfo: {
@ -556,8 +566,8 @@ describe('data provider', () => {
subscription.unsubscribe();
});
it('errorPolicyGuard should work properly', async () => {
const subscription = errorGuardedSubscribe(callback, client);
it('should retry with ignore error policy if errorPolicyGuard returns true', async () => {
const subscription = errorGuardedSubscribe(callback, client, variables);
const graphQLError = new GraphQLError(
'',
undefined,
@ -579,7 +589,7 @@ describe('data provider', () => {
});
expect(mockErrorPolicyGuard).toHaveBeenNthCalledWith(1, graphQLErrors);
await waitFor(() =>
expect(getData).toHaveBeenCalledWith({ data }, undefined)
expect(getData).toHaveBeenCalledWith({ data }, variables)
);
subscription.unsubscribe();
});
@ -589,7 +599,6 @@ describe('derived data provider', () => {
it('memoize instance and unsubscribe if no subscribers', () => {
clientSubscribeSubscribe.mockClear();
clientSubscribeUnsubscribe.mockClear();
const variables = {};
const subscription1 = derivedSubscribe(jest.fn(), client, variables);
const subscription2 = derivedSubscribe(jest.fn(), client, variables);
expect(clientSubscribeSubscribe.mock.calls.length).toEqual(2);
@ -615,7 +624,7 @@ describe('derived data provider', () => {
ReturnType<UpdateCallback<CombinedData, CombinedData>>,
Parameters<UpdateCallback<CombinedData, CombinedData>>
>();
const subscription = derivedSubscribe(callback, client);
const subscription = derivedSubscribe(callback, client, variables);
const data = { totalCount: 0 };
combineData.mockReturnValueOnce(data);
expect(callback.mock.calls.length).toBe(0);
@ -642,7 +651,7 @@ describe('derived data provider', () => {
Parameters<UpdateCallback<CombinedData, CombinedData>>
>();
expect(callback.mock.calls.length).toBe(0);
const subscription = derivedSubscribe(callback, client);
const subscription = derivedSubscribe(callback, client, variables);
const data = { totalCount: 0 };
combineData.mockReturnValueOnce(data);
expect(callback.mock.calls.length).toBe(0);
@ -677,7 +686,7 @@ describe('derived data provider', () => {
ReturnType<UpdateCallback<CombinedData, CombinedData>>,
Parameters<UpdateCallback<CombinedData, CombinedData>>
>();
const subscription = derivedSubscribe(callback, client);
const subscription = derivedSubscribe(callback, client, variables);
const data = { totalCount: 0 };
combineData.mockReturnValueOnce(data);
await resolveQuery({ data: part1 });
@ -709,7 +718,7 @@ describe('derived data provider', () => {
ReturnType<UpdateCallback<CombinedData, CombinedData>>,
Parameters<UpdateCallback<CombinedData, CombinedData>>
>();
const subscription = derivedSubscribe(callback, client);
const subscription = derivedSubscribe(callback, client, variables);
const data = { totalCount: 0 };
combineData.mockReturnValueOnce(data);
await resolveQuery({

View File

@ -53,12 +53,12 @@ export interface PageInfo {
export interface Subscribe<
Data,
Delta,
Variables extends OperationVariables = OperationVariables
Variables extends OperationVariables | undefined = undefined
> {
(
callback: UpdateCallback<Data, Delta>,
client: ApolloClient<object>,
variables?: Variables
variables: Variables
): {
unsubscribe: () => void;
reload: Reload;
@ -73,14 +73,9 @@ export type Query<Result> = DocumentNode | TypedDocumentNode<Result, any>;
export interface Update<
Data,
Delta,
Variables extends OperationVariables = OperationVariables
Variables extends OperationVariables | undefined = undefined
> {
(
data: Data | null,
delta: Delta,
reload: Reload,
variables?: Variables
): Data;
(data: Data | null, delta: Delta, reload: Reload, variables: Variables): Data;
}
export interface Append<Data> {
@ -165,7 +160,7 @@ interface DataProviderParams<
Data,
SubscriptionData,
Delta,
Variables extends OperationVariables = OperationVariables
Variables extends OperationVariables | undefined = undefined
> {
query: Query<QueryData>;
subscriptionQuery?: Query<SubscriptionData>;
@ -200,7 +195,7 @@ function makeDataProviderInternal<
Data,
SubscriptionData,
Delta,
Variables extends OperationVariables = OperationVariables
Variables extends OperationVariables | undefined = undefined
>({
query,
subscriptionQuery,
@ -225,7 +220,7 @@ function makeDataProviderInternal<
const updateQueue: Delta[] = [];
let resetTimer: ReturnType<typeof setTimeout>;
let variables: Variables | undefined;
let variables: Variables;
let data: Data | null = null;
let error: Error | undefined;
let loading = true;
@ -492,7 +487,9 @@ function makeDataProviderInternal<
callbacks.push(callback);
if (callbacks.length === 1) {
client = c;
variables = v;
if (v) {
variables = v;
}
initialize();
} else {
notify(callback);
@ -515,7 +512,7 @@ function makeDataProviderInternal<
const memoize = <
Data,
Delta,
Variables extends OperationVariables = OperationVariables
Variables extends OperationVariables | undefined = undefined
>(
fn: () => Subscribe<Data, Delta, Variables>
) => {
@ -563,7 +560,7 @@ export function makeDataProvider<
Data,
SubscriptionData,
Delta,
Variables extends OperationVariables = OperationVariables
Variables extends OperationVariables | undefined = undefined
>(
params: DataProviderParams<
QueryData,
@ -585,27 +582,27 @@ export function makeDataProvider<
* This effects in parts in combine function has any[] type
*/
type DependencySubscribe<
Variables extends OperationVariables = OperationVariables
Variables extends OperationVariables | undefined = undefined
> = Subscribe<any, any, Variables>; // eslint-disable-line @typescript-eslint/no-explicit-any
type DependencyUpdateCallback<
Variables extends OperationVariables = OperationVariables
Variables extends OperationVariables | undefined = undefined
> = Parameters<DependencySubscribe<Variables>>['0'];
export type DerivedPart<
Variables extends OperationVariables = OperationVariables
Variables extends OperationVariables | undefined = undefined
> = Parameters<DependencyUpdateCallback<Variables>>['0'];
export type CombineDerivedData<
Data,
Variables extends OperationVariables = OperationVariables
Variables extends OperationVariables | undefined = undefined
> = (
data: DerivedPart<Variables>['data'][],
variables: Variables | undefined,
variables: Variables,
prevData: Data | null
) => Data | null;
export type CombineDerivedDelta<
Data,
Delta,
Variables extends OperationVariables = OperationVariables
Variables extends OperationVariables | undefined = undefined
> = (
data: Data,
parts: DerivedPart<Variables>[],
@ -615,7 +612,7 @@ export type CombineDerivedDelta<
export type CombineInsertionData<
Data,
Variables extends OperationVariables = OperationVariables
Variables extends OperationVariables | undefined = undefined
> = (
data: Data,
parts: DerivedPart<Variables>[],
@ -625,7 +622,7 @@ export type CombineInsertionData<
function makeDerivedDataProviderInternal<
Data,
Delta,
Variables extends OperationVariables = OperationVariables
Variables extends OperationVariables | undefined = undefined
>(
dependencies: DependencySubscribe<Variables>[],
combineData: CombineDerivedData<Data, Variables>,
@ -635,7 +632,7 @@ function makeDerivedDataProviderInternal<
let subscriptions: ReturnType<DependencySubscribe>[] | undefined;
let client: ApolloClient<object>;
const callbacks: UpdateCallback<Data, Delta>[] = [];
let variables: Variables | undefined;
let variables: Variables;
const parts: DerivedPart<Variables>[] = [];
let data: Data | null = null;
let error: Error | undefined;
@ -756,7 +753,9 @@ function makeDerivedDataProviderInternal<
callbacks.push(callback);
if (callbacks.length === 1) {
client = c;
variables = v;
if (v) {
variables = v;
}
initialize();
} else {
notify(callback);
@ -777,7 +776,7 @@ function makeDerivedDataProviderInternal<
export function makeDerivedDataProvider<
Data,
Delta,
Variables extends OperationVariables = OperationVariables
Variables extends OperationVariables | undefined = undefined
>(
dependencies: DependencySubscribe<Variables>[],
combineData: CombineDerivedData<Data, Variables>,

View File

@ -29,12 +29,16 @@ export const encodeTransaction = (tx: Transaction): string => {
);
};
export const normalizeOrderSubmission = <T extends Exact<OrderSubmission, T>>(
order: T,
export const normalizeOrderSubmission = (
order: OrderSubmission,
decimalPlaces: number,
positionDecimalPlaces: number
): OrderSubmission => ({
...order,
marketId: order.marketId,
reference: order.reference,
type: order.type,
side: order.side,
timeInForce: order.timeInForce,
price:
order.type === OrderType.TYPE_LIMIT && order.price
? removeDecimal(order.price, decimalPlaces)

View File

@ -19,10 +19,9 @@ export const WithdrawFormContainer = ({
partyId,
submit,
}: WithdrawFormContainerProps) => {
const variables = useMemo(() => ({ partyId }), [partyId]);
const { data, loading, error } = useDataProvider({
dataProvider: accountsDataProvider,
variables,
variables: { partyId: partyId || '' },
});
const filteredAsset = useMemo(