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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -31,7 +31,7 @@ describe('orders list', { tags: '@smoke' }, () => {
cy.visit('/#/markets/market-0'); cy.visit('/#/markets/market-0');
cy.getByTestId('Orders').click(); cy.getByTestId('Orders').click();
cy.wait('@Orders').then(() => { cy.wait('@Orders').then(() => {
expect(subscriptionMocks.OrdersUpdate).to.be.calledTwice; expect(subscriptionMocks.OrdersUpdate).to.be.calledThrice;
}); });
cy.wait('@Markets'); cy.wait('@Markets');
}); });
@ -136,7 +136,7 @@ describe('subscribe orders', { tags: '@smoke' }, () => {
cy.visit('/#/markets/market-0'); cy.visit('/#/markets/market-0');
cy.getByTestId('Orders').click(); cy.getByTestId('Orders').click();
cy.wait('@Orders').then(() => { cy.wait('@Orders').then(() => {
expect(subscriptionMocks.OrdersUpdate).to.be.calledTwice; expect(subscriptionMocks.OrdersUpdate).to.be.calledThrice;
}); });
}); });
const orderId = '1234567890'; const orderId = '1234567890';
@ -354,7 +354,7 @@ describe('amend and cancel order', { tags: '@smoke' }, () => {
cy.visit('/#/markets/market-0'); cy.visit('/#/markets/market-0');
cy.getByTestId('Orders').click(); cy.getByTestId('Orders').click();
cy.wait('@Orders').then(() => { cy.wait('@Orders').then(() => {
expect(subscriptionMocks.OrdersUpdate).to.be.calledTwice; expect(subscriptionMocks.OrdersUpdate).to.be.calledThrice;
}); });
cy.mockVegaWalletTransaction(); 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="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 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.get('[col-id="unrealisedPNL"]').each(($unrealisedPnl) => {
cy.wrap($unrealisedPnl).invoke('text').should('not.be.empty'); 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 type { PartialDeep } from 'type-fest';
import { orderUpdateSubscription } from '@vegaprotocol/mock'; import { orderUpdateSubscription } from '@vegaprotocol/mock';
let sendOrderUpdate: (data: OrdersUpdateSubscription) => void; const sendOrderUpdate: ((data: OrdersUpdateSubscription) => void)[] = [];
const getOnOrderUpdate = () => { const getOnOrderUpdate = () => {
const onOrderUpdate: onMessage< const onOrderUpdate: onMessage<
OrdersUpdateSubscription, OrdersUpdateSubscription,
OrdersUpdateSubscriptionVariables OrdersUpdateSubscriptionVariables
> = (send) => { > = (send) => {
sendOrderUpdate = send; sendOrderUpdate.push(send);
}; };
return onOrderUpdate; return onOrderUpdate;
}; };
@ -31,5 +31,5 @@ export function updateOrder(
if (!sendOrderUpdate) { if (!sendOrderUpdate) {
throw new Error('OrderSub not called'); throw new Error('OrderSub not called');
} }
sendOrderUpdate(update); sendOrderUpdate.forEach((send) => send(update));
} }

View File

@ -28,7 +28,6 @@ import {
} from '@vegaprotocol/mock'; } from '@vegaprotocol/mock';
import type { PartialDeep } from 'type-fest'; import type { PartialDeep } from 'type-fest';
import type { MarketDataQuery, MarketsQuery } from '@vegaprotocol/market-list'; import type { MarketDataQuery, MarketsQuery } from '@vegaprotocol/market-list';
import type { MarketInfoQuery } from '@vegaprotocol/market-info';
type MarketPageMockData = { type MarketPageMockData = {
state: Schema.MarketState; 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 = ( const mockTradingPage = (
req: CyHttpMessages.IncomingHttpRequest, req: CyHttpMessages.IncomingHttpRequest,
state: Schema.MarketState = Schema.MarketState.STATE_ACTIVE, state: Schema.MarketState = Schema.MarketState.STATE_ACTIVE,
@ -109,11 +96,7 @@ const mockTradingPage = (
aliasGQLQuery(req, 'Margins', marginsQuery()); aliasGQLQuery(req, 'Margins', marginsQuery());
aliasGQLQuery(req, 'Assets', assetsQuery()); aliasGQLQuery(req, 'Assets', assetsQuery());
aliasGQLQuery(req, 'Asset', assetQuery()); aliasGQLQuery(req, 'Asset', assetQuery());
aliasGQLQuery( aliasGQLQuery(req, 'MarketInfo', marketInfoQuery());
req,
'MarketInfo',
marketInfoQuery(marketInfoOverride({ state, tradingMode, trigger }))
);
aliasGQLQuery(req, 'Trades', tradesQuery()); aliasGQLQuery(req, 'Trades', tradesQuery());
aliasGQLQuery(req, 'Chart', chartQuery()); aliasGQLQuery(req, 'Chart', chartQuery());
aliasGQLQuery(req, 'Candles', candlesQuery()); 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). // should be the oldest market that is currently trading in us mode(i.e. not in auction).
const { data, error, loading } = useDataProvider({ const { data, error, loading } = useDataProvider({
dataProvider: marketsWithDataProvider, dataProvider: marketsWithDataProvider,
variables: undefined,
}); });
const update = useGlobalStore((store) => store.update); const update = useGlobalStore((store) => store.update);
const marketId = useGlobalStore((store) => store.marketId); const marketId = useGlobalStore((store) => store.marketId);

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -43,10 +43,6 @@ export const accountFields: AccountFieldsFragment[] = [
__typename: 'AccountBalance', __typename: 'AccountBalance',
type: Schema.AccountType.ACCOUNT_TYPE_GENERAL, type: Schema.AccountType.ACCOUNT_TYPE_GENERAL,
balance: '100000000', balance: '100000000',
market: {
id: 'market-0',
__typename: 'Market',
},
asset: { asset: {
__typename: 'Asset', __typename: 'Asset',
id: 'asset-id-2', id: 'asset-id-2',
@ -75,7 +71,7 @@ export const accountFields: AccountFieldsFragment[] = [
}, },
asset: { asset: {
__typename: 'Asset', __typename: 'Asset',
id: 'asset-id-2', id: 'asset-0',
}, },
}, },
{ {
@ -94,7 +90,7 @@ export const accountFields: AccountFieldsFragment[] = [
{ {
__typename: 'AccountBalance', __typename: 'AccountBalance',
type: Schema.AccountType.ACCOUNT_TYPE_GENERAL, type: Schema.AccountType.ACCOUNT_TYPE_GENERAL,
balance: '100000000', balance: '10000000000',
market: null, market: null,
asset: { asset: {
__typename: 'Asset', __typename: 'Asset',
@ -141,3 +137,25 @@ export const accountEventsSubscription = (
}; };
return merge(defaultResult, override); 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 { param } = useNetworkParam(NetworkParams.transfer_fee_factor);
const { data } = useDataProvider({ const { data } = useDataProvider({
dataProvider: accountsDataProvider, dataProvider: accountsDataProvider,
variables: { partyId: pubKey }, variables: { partyId: pubKey || '' },
skip: !pubKey, skip: !pubKey,
}); });
const create = useVegaTransactionStore((store) => store.create); const create = useVegaTransactionStore((store) => store.create);

View File

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

View File

@ -40,4 +40,5 @@ export const enabledAssetsProvider = makeDerivedDataProvider<
export const useAssetsDataProvider = () => export const useAssetsDataProvider = () =>
useDataProvider({ useDataProvider({
dataProvider: assetsProvider, 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 { t } from '@vegaprotocol/i18n';
import { Notification, Intent } from '@vegaprotocol/ui-toolkit'; import { Notification, Intent } from '@vegaprotocol/ui-toolkit';
import { useDepositDialog } from '@vegaprotocol/deposits'; import { useDepositDialog } from '@vegaprotocol/deposits';
@ -19,14 +19,14 @@ export const MarginWarning = ({ margin, balance, asset }: Props) => {
<Notification <Notification
intent={Intent.Warning} intent={Intent.Warning}
testId="dealticket-warning-margin" 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, margin,
asset.decimals asset.decimals
)} ${asset.symbol} ${t( )} ${asset.symbol} ${t(
'is currently required. You have only' 'is currently required. You have only'
)} ${formatNumber(balance, asset.decimals)} ${asset.symbol} ${t( )} ${addDecimalsFormatNumber(balance, asset.decimals)} ${
'available.' asset.symbol
)}`} } ${t('available.')}`}
buttonProps={{ buttonProps={{
text: t(`Deposit ${asset.symbol}`), text: t(`Deposit ${asset.symbol}`),
action: () => openDepositDialog(asset.id), action: () => openDepositDialog(asset.id),

View File

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

View File

@ -1,6 +1,5 @@
import { Tooltip } from '@vegaprotocol/ui-toolkit'; import { Tooltip } from '@vegaprotocol/ui-toolkit';
import type { ReactNode } from 'react'; import type { ReactNode } from 'react';
import { useMemo } from 'react';
import type { OrderSubmissionBody } from '@vegaprotocol/wallet'; import type { OrderSubmissionBody } from '@vegaprotocol/wallet';
import type { Market, MarketData } from '@vegaprotocol/market-list'; import type { Market, MarketData } from '@vegaprotocol/market-list';
import { import {
@ -12,22 +11,51 @@ interface DealTicketFeeDetailsProps {
order: OrderSubmissionBody['orderSubmission']; order: OrderSubmissionBody['orderSubmission'];
market: Market; market: Market;
marketData: MarketData; marketData: MarketData;
margin: string;
totalMargin: string;
balance: string;
} }
export interface DealTicketFeeDetails { export interface DealTicketFeeDetailProps {
label: string; label: string;
value?: string | number | null; value?: string | number | null;
labelDescription?: string | ReactNode; labelDescription?: string | ReactNode;
symbol?: string; 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 = ({ export const DealTicketFeeDetails = ({
order, order,
market, market,
marketData, marketData,
margin,
totalMargin,
balance,
}: DealTicketFeeDetailsProps) => { }: DealTicketFeeDetailsProps) => {
const feeDetails = useFeeDealTicketDetails(order, market, marketData); const feeDetails = useFeeDealTicketDetails(order, market, marketData);
const details = useMemo(() => getFeeDetailsValues(feeDetails), [feeDetails]); const details = getFeeDetailsValues({
...feeDetails,
margin,
totalMargin,
balance,
});
return ( return (
<div> <div>
{details.map(({ label, value, labelDescription, symbol }) => ( {details.map(({ label, value, labelDescription, symbol }) => (

View File

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

View File

@ -55,6 +55,7 @@ export const MarketSelector = ({ market, setMarket, ItemRenderer }: Props) => {
const { data, loading, error } = useDataProvider({ const { data, loading, error } = useDataProvider({
dataProvider: marketsProvider, dataProvider: marketsProvider,
variables: undefined,
skipUpdates: true, 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.`, For example, for a notional size of $500, if the margin requirement is 10%, then the estimated margin would be approximately $50.`,
[settlementAsset] [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( 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.' '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 { export enum SummaryValidationType {
NoPubKey = 'NoPubKey',
NoCollateral = 'NoCollateral', NoCollateral = 'NoCollateral',
TradingMode = 'MarketTradingMode', TradingMode = 'MarketTradingMode',
MarketState = 'MarketState', MarketState = 'MarketState',

View File

@ -4,5 +4,3 @@ export * from './use-fee-deal-ticket-details';
export * from './use-market-positions'; export * from './use-market-positions';
export * from './use-maximum-position-size'; export * from './use-maximum-position-size';
export * from './use-order-closeout'; 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 { marketDepthProvider } from '@vegaprotocol/market-depth';
import * as Schema from '@vegaprotocol/types'; import * as Schema from '@vegaprotocol/types';
import type { Market } from '@vegaprotocol/market-list'; import type { Market } from '@vegaprotocol/market-list';
@ -13,11 +12,10 @@ interface Props {
} }
export const useCalculateSlippage = ({ market, order }: Props) => { export const useCalculateSlippage = ({ market, order }: Props) => {
const variables = useMemo(() => ({ marketId: market.id }), [market.id]);
const { data } = useThrottledDataProvider( const { data } = useThrottledDataProvider(
{ {
dataProvider: marketDepthProvider, dataProvider: marketDepthProvider,
variables, variables: { marketId: market.id },
}, },
1000 1000
); );

View File

@ -3,24 +3,26 @@ import {
addDecimal, addDecimal,
addDecimalsFormatNumber, addDecimalsFormatNumber,
formatNumber, formatNumber,
toBigNum,
} from '@vegaprotocol/utils'; } from '@vegaprotocol/utils';
import { t } from '@vegaprotocol/i18n'; import { t } from '@vegaprotocol/i18n';
import * as Schema from '@vegaprotocol/types';
import { useVegaWallet } from '@vegaprotocol/wallet'; import { useVegaWallet } from '@vegaprotocol/wallet';
import BigNumber from 'bignumber.js';
import { useMemo } from 'react'; import { useMemo } from 'react';
import type { Market, MarketData } from '@vegaprotocol/market-list'; import type { Market, MarketData } from '@vegaprotocol/market-list';
import type { OrderSubmissionBody } from '@vegaprotocol/wallet'; import type { OrderSubmissionBody } from '@vegaprotocol/wallet';
import { import {
EST_CLOSEOUT_TOOLTIP_TEXT, EST_CLOSEOUT_TOOLTIP_TEXT,
EST_MARGIN_TOOLTIP_TEXT, // EST_MARGIN_TOOLTIP_TEXT,
EST_TOTAL_MARGIN_TOOLTIP_TEXT,
NOTIONAL_SIZE_TOOLTIP_TEXT, NOTIONAL_SIZE_TOOLTIP_TEXT,
MARGIN_ACCOUNT_TOOLTIP_TEXT,
MARGIN_DIFF_TOOLTIP_TEXT,
} from '../constants'; } from '../constants';
import { useCalculateSlippage } from './use-calculate-slippage';
import { useOrderCloseOut } from './use-order-closeout'; import { useOrderCloseOut } from './use-order-closeout';
import { useOrderMargin } from './use-order-margin'; import { useMarketAccountBalance } from '@vegaprotocol/accounts';
import type { OrderMargin } from './use-order-margin';
import { getDerivedPrice } from '../utils/get-price'; import { getDerivedPrice } from '../utils/get-price';
import { useEstimateOrderQuery } from './__generated__/EstimateOrder';
import type { EstimateOrderQuery } from './__generated__/EstimateOrder';
export const useFeeDealTicketDetails = ( export const useFeeDealTicketDetails = (
order: OrderSubmissionBody['orderSubmission'], order: OrderSubmissionBody['orderSubmission'],
@ -28,33 +30,23 @@ export const useFeeDealTicketDetails = (
marketData: MarketData marketData: MarketData
) => { ) => {
const { pubKey } = useVegaWallet(); const { pubKey } = useVegaWallet();
const slippage = useCalculateSlippage({ market, order }); const { accountBalance } = useMarketAccountBalance(market.id);
const derivedPrice = useMemo(() => { const price = useMemo(() => {
return getDerivedPrice(order, market, marketData); return getDerivedPrice(order, marketData);
}, [order, market, marketData]); }, [order, marketData]);
// Note this isn't currently used anywhere const { data: estMargin } = useEstimateOrderQuery({
const slippageAdjustedPrice = useMemo(() => { variables: {
if (derivedPrice) { marketId: market.id,
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 || '', partyId: pubKey || '',
derivedPrice, price,
size: order.size,
side: order.side,
timeInForce: order.timeInForce,
type: order.type,
},
skip: !pubKey || !market || !order.size || !price,
}); });
const estCloseOut = useOrderCloseOut({ const estCloseOut = useOrderCloseOut({
@ -64,13 +56,13 @@ export const useFeeDealTicketDetails = (
}); });
const notionalSize = useMemo(() => { const notionalSize = useMemo(() => {
if (derivedPrice && order.size) { if (price && order.size) {
return new BigNumber(order.size) return toBigNum(order.size, market.positionDecimalPlaces)
.multipliedBy(addDecimal(derivedPrice, market.decimalPlaces)) .multipliedBy(addDecimal(price, market.decimalPlaces))
.toString(); .toString();
} }
return null; return null;
}, [derivedPrice, order.size, market.decimalPlaces]); }, [price, order.size, market.decimalPlaces, market.positionDecimalPlaces]);
const assetSymbol = const assetSymbol =
market.tradableInstrument.instrument.product.settlementAsset.symbol; market.tradableInstrument.instrument.product.settlementAsset.symbol;
@ -80,37 +72,40 @@ export const useFeeDealTicketDetails = (
market, market,
assetSymbol, assetSymbol,
notionalSize, notionalSize,
estMargin, accountBalance,
estimateOrder: estMargin?.estimateOrder,
estCloseOut, estCloseOut,
slippage,
slippageAdjustedPrice,
}; };
}, [ }, [
market, market,
assetSymbol, assetSymbol,
notionalSize, notionalSize,
accountBalance,
estMargin, estMargin,
estCloseOut, estCloseOut,
slippage,
slippageAdjustedPrice,
]); ]);
}; };
export interface FeeDetails { export interface FeeDetails {
balance: string;
market: Market; market: Market;
assetSymbol: string; assetSymbol: string;
notionalSize: string | null; notionalSize: string | null;
estMargin: OrderMargin | null;
estCloseOut: string | null; estCloseOut: string | null;
slippage: string | null; estimateOrder: EstimateOrderQuery['estimateOrder'] | undefined;
margin: string;
totalMargin: string;
} }
export const getFeeDetailsValues = ({ export const getFeeDetailsValues = ({
balance,
assetSymbol, assetSymbol,
notionalSize,
estMargin,
estCloseOut, estCloseOut,
estimateOrder,
margin,
market, market,
notionalSize,
totalMargin,
}: FeeDetails) => { }: FeeDetails) => {
const assetDecimals = const assetDecimals =
market.tradableInstrument.instrument.product.settlementAsset.decimals; market.tradableInstrument.instrument.product.settlementAsset.decimals;
@ -129,7 +124,12 @@ export const getFeeDetailsValues = ({
? addDecimalsFormatNumber(value, assetDecimals) ? addDecimalsFormatNumber(value, assetDecimals)
: '-'; : '-';
}; };
return [ const details: {
label: string;
value?: string | null;
symbol: string;
labelDescription: React.ReactNode;
}[] = [
{ {
label: t('Notional'), label: t('Notional'),
value: formatValueWithMarketDp(notionalSize), value: formatValueWithMarketDp(notionalSize),
@ -139,8 +139,8 @@ export const getFeeDetailsValues = ({
{ {
label: t('Fees'), label: t('Fees'),
value: value:
estMargin?.totalFees && estimateOrder?.totalFeeAmount &&
`~${formatValueWithAssetDp(estMargin?.totalFees)}`, `~${formatValueWithAssetDp(estimateOrder?.totalFeeAmount)}`,
labelDescription: ( labelDescription: (
<> <>
<span> <span>
@ -149,7 +149,7 @@ export const getFeeDetailsValues = ({
)} )}
</span> </span>
<FeesBreakdown <FeesBreakdown
fees={estMargin?.fees} fees={estimateOrder?.fee}
feeFactors={market.fees.factors} feeFactors={market.fees.factors}
symbol={assetSymbol} symbol={assetSymbol}
decimals={assetDecimals} decimals={assetDecimals}
@ -158,18 +158,46 @@ export const getFeeDetailsValues = ({
), ),
symbol: assetSymbol, symbol: assetSymbol,
}, },
/*
{ {
label: t('Margin'), label: t('Initial margin'),
value: value: margin && `~${formatValueWithAssetDp(margin)}`,
estMargin?.margin && `~${formatValueWithAssetDp(estMargin?.margin)}`,
symbol: assetSymbol, symbol: assetSymbol,
labelDescription: EST_MARGIN_TOOLTIP_TEXT(assetSymbol), labelDescription: EST_MARGIN_TOOLTIP_TEXT(assetSymbol),
}, },
*/
{ {
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'), label: t('Liquidation'),
value: estCloseOut && `~${formatValueWithMarketDp(estCloseOut)}`, value: estCloseOut && `~${formatValueWithMarketDp(estCloseOut)}`,
symbol: market.tradableInstrument.instrument.product.quoteName, symbol: market.tradableInstrument.instrument.product.quoteName,
labelDescription: EST_CLOSEOUT_TOOLTIP_TEXT(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', id: 'market-id',
__typename: 'Market', __typename: 'Market',
}, },
auctionStart: '2022-06-21T17:18:43.484055236Z',
auctionEnd: '2022-06-21T17:18:43.484055236Z', auctionEnd: '2022-06-21T17:18:43.484055236Z',
targetStake: '1000000', auctionStart: '2022-06-21T17:18:43.484055236Z',
suppliedStake: '1000',
marketTradingMode: Schema.MarketTradingMode.TRADING_MODE_CONTINUOUS,
marketState: Schema.MarketState.STATE_ACTIVE,
staticMidPrice: '0',
indicativePrice: '100',
bestStaticBidPrice: '0',
bestStaticOfferPrice: '0',
indicativeVolume: '10',
bestBidPrice: '0', bestBidPrice: '0',
bestBidVolume: '0',
bestOfferPrice: '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', markPrice: '200',
midPrice: '0',
openInterest: '',
staticMidPrice: '0',
suppliedStake: '1000',
targetStake: '1000000',
trigger: Schema.AuctionTrigger.AUCTION_TRIGGER_BATCH, trigger: Schema.AuctionTrigger.AUCTION_TRIGGER_BATCH,
}; };
return merge(defaultMarketData, override); return merge(defaultMarketData, override);

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,4 +1,4 @@
query MarketInfo($marketId: ID!, $interval: Interval!, $since: String!) { query MarketInfo($marketId: ID!) {
market(id: $marketId) { market(id: $marketId) {
id id
decimalPlaces decimalPlaces
@ -32,7 +32,6 @@ query MarketInfo($marketId: ID!, $interval: Interval!, $since: String!) {
} }
} }
} }
tradingMode
fees { fees {
factors { factors {
makerFee makerFee
@ -54,35 +53,6 @@ query MarketInfo($marketId: ID!, $interval: Interval!, $since: String!) {
short short
long 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 { liquidityMonitoringParameters {
triggeringRatio triggeringRatio
targetStakeParameters { targetStakeParameters {
@ -90,13 +60,6 @@ query MarketInfo($marketId: ID!, $interval: Interval!, $since: String!) {
scalingFactor scalingFactor
} }
} }
candlesConnection(interval: $interval, since: $since) {
edges {
node {
volume
}
}
}
tradableInstrument { tradableInstrument {
instrument { instrument {
id id
@ -144,10 +107,12 @@ query MarketInfo($marketId: ID!, $interval: Interval!, $since: String!) {
} }
} }
} }
} marginCalculator {
depth { scalingFactors {
lastTrade { searchLevel
price 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; const defaultOptions = {} as const;
export type MarketInfoQueryVariables = Types.Exact<{ export type MarketInfoQueryVariables = Types.Exact<{
marketId: Types.Scalars['ID']; 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` export const MarketInfoDocument = gql`
query MarketInfo($marketId: ID!, $interval: Interval!, $since: String!) { query MarketInfo($marketId: ID!) {
market(id: $marketId) { market(id: $marketId) {
id id
decimalPlaces decimalPlaces
@ -48,7 +46,6 @@ export const MarketInfoDocument = gql`
} }
} }
} }
tradingMode
fees { fees {
factors { factors {
makerFee makerFee
@ -70,35 +67,6 @@ export const MarketInfoDocument = gql`
short short
long 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 { liquidityMonitoringParameters {
triggeringRatio triggeringRatio
targetStakeParameters { targetStakeParameters {
@ -106,13 +74,6 @@ export const MarketInfoDocument = gql`
scalingFactor scalingFactor
} }
} }
candlesConnection(interval: $interval, since: $since) {
edges {
node {
volume
}
}
}
tradableInstrument { tradableInstrument {
instrument { instrument {
id id
@ -160,10 +121,12 @@ export const MarketInfoDocument = gql`
} }
} }
} }
marginCalculator {
scalingFactors {
searchLevel
initialMargin
collateralRelease
} }
depth {
lastTrade {
price
} }
} }
} }
@ -183,8 +146,6 @@ export const MarketInfoDocument = gql`
* const { data, loading, error } = useMarketInfoQuery({ * const { data, loading, error } = useMarketInfoQuery({
* variables: { * variables: {
* marketId: // value for 'marketId' * 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 './info-market';
export * from './tooltip-mapping'; export * from './tooltip-mapping';
export * from './__generated__/MarketInfo'; export * from './__generated__/MarketInfo';
export * from './__generated__/MarketInfoNoCandles';
export * from './market-info-data-provider'; export * from './market-info-data-provider';

View File

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

View File

@ -1,25 +1,68 @@
import { makeDataProvider } from '@vegaprotocol/utils'; import { makeDataProvider, makeDerivedDataProvider } from '@vegaprotocol/utils';
import type { MarketInfoQuery } from './__generated__/MarketInfo'; 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 { MarketInfoDocument } from './__generated__/MarketInfo';
import type { MarketInfoNoCandlesQuery } from './__generated__/MarketInfoNoCandles';
import { MarketInfoNoCandlesDocument } from './__generated__/MarketInfoNoCandles';
export const marketInfoDataProvider = makeDataProvider< export type MarketInfo = NonNullable<MarketInfoQuery['market']>;
MarketInfoQuery, 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,
MarketInfoQuery['market'],
never, never,
never never,
MarketInfoQueryVariables
>({ >({
query: MarketInfoDocument, query: MarketInfoDocument,
getData: (responseData: MarketInfoQuery | null) => responseData, getData,
}); });
export const marketInfoNoCandlesDataProvider = makeDataProvider< export const marketInfoWithDataProvider = makeDerivedDataProvider<
MarketInfoNoCandlesQuery, MarketInfoWithData,
MarketInfoNoCandlesQuery,
never, never,
never MarketInfoQueryVariables
>({ >([marketInfoProvider, marketDataProvider], (parts) => {
query: MarketInfoNoCandlesDocument, const market: MarketInfo | null = parts[0];
getData: (responseData: MarketInfoNoCandlesQuery | null) => responseData, 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', long: '0.008508132993273576',
}, },
lpPriceRange: '0.02', 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: { liquidityMonitoringParameters: {
triggeringRatio: '0', triggeringRatio: '0',
targetStakeParameters: { targetStakeParameters: {
@ -136,7 +102,6 @@ export const marketInfoQuery = (
}, },
__typename: 'LiquidityMonitoringParameters', __typename: 'LiquidityMonitoringParameters',
}, },
candlesConnection: null,
tradableInstrument: { tradableInstrument: {
__typename: 'TradableInstrument', __typename: 'TradableInstrument',
instrument: { 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 { gql } from '@apollo/client';
import * as Apollo from '@apollo/client'; import * as Apollo from '@apollo/client';
const defaultOptions = {} as const; 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<{ export type MarketDataUpdateSubscriptionVariables = Types.Exact<{
marketId: Types.Scalars['ID']; 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<{ export type MarketDataQueryVariables = Types.Exact<{
marketId: Types.Scalars['ID']; 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` export const MarketDataUpdateFieldsFragmentDoc = gql`
fragment MarketDataUpdateFields on ObservableMarketData { fragment MarketDataUpdateFields on ObservableMarketData {
marketId marketId
auctionEnd
auctionStart
bestBidPrice bestBidPrice
bestBidVolume
bestOfferPrice bestOfferPrice
markPrice bestOfferVolume
trigger
staticMidPrice
marketTradingMode
marketState
indicativeVolume
indicativePrice
bestStaticBidPrice bestStaticBidPrice
bestStaticBidVolume
bestStaticOfferPrice bestStaticOfferPrice
targetStake bestStaticOfferVolume
indicativePrice
indicativeVolume
marketState
marketTradingMode
marketValueProxy
markPrice
midPrice
openInterest
priceMonitoringBounds {
minValidPrice
maxValidPrice
trigger {
horizonSecs
probability
auctionExtensionSecs
}
referencePrice
}
staticMidPrice
suppliedStake suppliedStake
targetStake
trigger
} }
`; `;
export const MarketDataFieldsFragmentDoc = gql` export const MarketDataFieldsFragmentDoc = gql`
@ -44,21 +63,38 @@ export const MarketDataFieldsFragmentDoc = gql`
market { market {
id id
} }
bestBidPrice
bestOfferPrice
markPrice
trigger
staticMidPrice
marketTradingMode
marketState
indicativeVolume
indicativePrice
bestStaticBidPrice
bestStaticOfferPrice
targetStake
suppliedStake
auctionStart
auctionEnd 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` export const MarketDataUpdateDocument = gql`

View File

@ -13,13 +13,14 @@ export const MarketsContainer = ({ onSelect }: MarketsContainerProps) => {
const { data, error, loading, reload } = useDataProvider({ const { data, error, loading, reload } = useDataProvider({
dataProvider, dataProvider,
skipUpdates: true, skipUpdates: true,
variables: undefined,
}); });
return ( return (
<div className="h-full relative"> <div className="h-full relative">
<MarketListTable <MarketListTable
rowData={error ? [] : data} rowData={error ? [] : data}
noRowsOverlayComponent={() => null} suppressLoadingOverlay
suppressNoRowsOverlay
onRowClicked={(rowEvent: RowClickedEvent) => { onRowClicked={(rowEvent: RowClickedEvent) => {
const { data, event } = rowEvent; const { data, event } = rowEvent;
// filters out clicks on the symbol column because it should display asset details // 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 { makeDataProvider } from '@vegaprotocol/utils';
import type { import type {
MarketCandlesQuery, MarketCandlesQuery,
MarketCandlesQueryVariables,
MarketCandlesUpdateSubscription, MarketCandlesUpdateSubscription,
MarketCandlesFieldsFragment, MarketCandlesFieldsFragment,
} from './__generated__/market-candles'; } from './__generated__/market-candles';
@ -36,7 +37,8 @@ export const marketCandlesProvider = makeDataProvider<
MarketCandlesQuery, MarketCandlesQuery,
Candle[], Candle[],
MarketCandlesUpdateSubscription, MarketCandlesUpdateSubscription,
Candle Candle,
MarketCandlesQueryVariables
>({ >({
query: MarketCandlesDocument, query: MarketCandlesDocument,
subscriptionQuery: MarketCandlesUpdateDocument, subscriptionQuery: MarketCandlesUpdateDocument,

View File

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

View File

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

View File

@ -44,34 +44,62 @@ const marketDataFields: MarketDataFieldsFragment = {
id: 'market-0', id: 'market-0',
__typename: 'Market', __typename: 'Market',
}, },
auctionStart: '2022-06-21T17:18:43.484055236Z',
auctionEnd: '2022-06-21T17:18:43.484055236Z', auctionEnd: '2022-06-21T17:18:43.484055236Z',
targetStake: '1000000', auctionStart: '2022-06-21T17:18:43.484055236Z',
suppliedStake: '1000', bestBidPrice: '4412690058',
marketTradingMode: Schema.MarketTradingMode.TRADING_MODE_CONTINUOUS, bestBidVolume: '1',
marketState: Schema.MarketState.STATE_ACTIVE, bestOfferPrice: '4812690058',
staticMidPrice: '0', bestOfferVolume: '3',
bestStaticBidPrice: '4512690058',
bestStaticBidVolume: '2',
bestStaticOfferPrice: '4712690058',
bestStaticOfferVolume: '4',
indicativePrice: '0', indicativePrice: '0',
bestStaticBidPrice: '0',
bestStaticOfferPrice: '0',
indicativeVolume: '0', indicativeVolume: '0',
bestBidPrice: '0', marketState: Schema.MarketState.STATE_ACTIVE,
bestOfferPrice: '0', marketTradingMode: Schema.MarketTradingMode.TRADING_MODE_CONTINUOUS,
marketValueProxy: '2000000',
markPrice: '4612690058', 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, trigger: Schema.AuctionTrigger.AUCTION_TRIGGER_UNSPECIFIED,
}; };
const marketDataUpdateFields: MarketDataUpdateFieldsFragment = { 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', bestBidPrice: '0',
bestBidVolume: '0',
bestOfferPrice: '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', markPrice: '4612690058',
midPrice: '0',
openInterest: '0',
staticMidPrice: '0',
trigger: Schema.AuctionTrigger.AUCTION_TRIGGER_UNSPECIFIED, trigger: Schema.AuctionTrigger.AUCTION_TRIGGER_UNSPECIFIED,
}; };

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,7 +1,8 @@
export * from './__generated__/OrdersSubscription'; export * from './__generated__/OrdersSubscription';
export * from './use-has-active-order'; export * from './use-has-active-order';
export * from './use-order-cancel'; export * from './use-order-cancel';
export * from './use-order-submit';
export * from './use-order-edit'; export * from './use-order-edit';
export * from './use-order-submit';
export * from './use-order-update'; export * from './use-order-update';
export * from './use-pending-orders-volume';
export * from './use-order-store'; 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/__generated__/Positions';
export * from './lib/positions-container'; export * from './lib/positions-container';
export * from './lib/positions-data-providers'; 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/positions-table';
export * from './lib/use-close-position'; 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-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; 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 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']; 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 }; 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 produce from 'immer';
import { makeDataProvider, removePaginationWrapper } from '@vegaprotocol/utils'; import {
makeDataProvider,
makeDerivedDataProvider,
removePaginationWrapper,
} from '@vegaprotocol/utils';
import { import {
MarginsSubscriptionDocument, MarginsSubscriptionDocument,
MarginsDocument, MarginsDocument,
@ -8,6 +12,7 @@ import type {
MarginsQuery, MarginsQuery,
MarginFieldsFragment, MarginFieldsFragment,
MarginsSubscriptionSubscription, MarginsSubscriptionSubscription,
MarginsQueryVariables,
} from './__generated__/Positions'; } from './__generated__/Positions';
const update = ( const update = (
@ -56,7 +61,8 @@ export const marginsDataProvider = makeDataProvider<
MarginsQuery, MarginsQuery,
MarginFieldsFragment[], MarginFieldsFragment[],
MarginsSubscriptionSubscription, MarginsSubscriptionSubscription,
MarginsSubscriptionSubscription['margins'] MarginsSubscriptionSubscription['margins'],
MarginsQueryVariables
>({ >({
query: MarginsDocument, query: MarginsDocument,
subscriptionQuery: MarginsSubscriptionDocument, subscriptionQuery: MarginsSubscriptionDocument,
@ -64,3 +70,15 @@ export const marginsDataProvider = makeDataProvider<
getData, getData,
getDelta, 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, removePaginationWrapper,
} from '@vegaprotocol/utils'; } from '@vegaprotocol/utils';
import * as Schema from '@vegaprotocol/types'; 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 { marketsWithDataProvider } from '@vegaprotocol/market-list';
import type { import type {
PositionsQuery, PositionsQuery,
PositionFieldsFragment, PositionFieldsFragment,
PositionsSubscriptionSubscription, PositionsSubscriptionSubscription,
MarginFieldsFragment, MarginFieldsFragment,
PositionsQueryVariables,
} from './__generated__/Positions'; } from './__generated__/Positions';
import { import {
PositionsDocument, PositionsDocument,
PositionsSubscriptionDocument, PositionsSubscriptionDocument,
} from './__generated__/Positions'; } from './__generated__/Positions';
import { marginsDataProvider } from './margin-data-provider'; 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'; import type { PositionStatus } from '@vegaprotocol/types';
type PositionMarginLevel = Pick< type PositionMarginLevel = Pick<
@ -229,7 +242,8 @@ export const positionsDataProvider = makeDataProvider<
PositionsQuery, PositionsQuery,
PositionFieldsFragment[], PositionFieldsFragment[],
PositionsSubscriptionSubscription, PositionsSubscriptionSubscription,
PositionsSubscriptionSubscription['positions'] PositionsSubscriptionSubscription['positions'],
PositionsQueryVariables
>({ >({
query: PositionsDocument, query: PositionsDocument,
subscriptionQuery: PositionsSubscriptionDocument, subscriptionQuery: PositionsSubscriptionDocument,
@ -260,6 +274,32 @@ const upgradeMarginsConnection = (
return null; 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 = ( export const rejoinPositionData = (
positions: PositionFieldsFragment[] | null, positions: PositionFieldsFragment[] | null,
marketsData: MarketMaybeWithData[] | null, marketsData: MarketMaybeWithData[] | null,
@ -284,19 +324,15 @@ export const rejoinPositionData = (
return null; return null;
}; };
export interface PositionsMetricsProviderVariables {
partyId: string;
}
export const positionsMetricsProvider = makeDerivedDataProvider< export const positionsMetricsProvider = makeDerivedDataProvider<
Position[], Position[],
Position[], Position[],
PositionsMetricsProviderVariables PositionsQueryVariables
>( >(
[ [
positionsDataProvider, positionsDataProvider,
accountsDataProvider, accountsDataProvider,
marketsWithDataProvider, (callback, client) => marketsWithDataProvider(callback, client, undefined),
marginsDataProvider, marginsDataProvider,
], ],
([positions, accounts, marketsData, margins], variables) => { ([positions, accounts, marketsData, margins], variables) => {
@ -317,3 +353,103 @@ export const positionsMetricsProvider = makeDerivedDataProvider<
return !(previousRow && isEqual(previousRow, row)); 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 { AgGridReact } from 'ag-grid-react';
import type { Position } from './positions-data-providers'; import type { Position } from './positions-data-providers';
import { positionsMetricsProvider } 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 { useDataProvider, updateGridData } from '@vegaprotocol/react-helpers';
import type { GetRowsParams } from '@vegaprotocol/datagrid'; import type { GetRowsParams } from '@vegaprotocol/datagrid';
@ -14,7 +14,7 @@ export const usePositionsData = (
gridRef: RefObject<AgGridReact>, gridRef: RefObject<AgGridReact>,
clientSideModel?: boolean clientSideModel?: boolean
) => { ) => {
const variables = useMemo<PositionsMetricsProviderVariables>( const variables = useMemo<PositionsQueryVariables>(
() => ({ partyId }), () => ({ partyId }),
[partyId] [partyId]
); );

View File

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

View File

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

View File

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

View File

@ -9,7 +9,7 @@ import type { Subscribe, Load, UpdateCallback } from '@vegaprotocol/utils';
export interface useDataProviderParams< export interface useDataProviderParams<
Data, Data,
Delta, Delta,
Variables extends OperationVariables = OperationVariables Variables extends OperationVariables | undefined = undefined
> { > {
dataProvider: Subscribe<Data, Delta, Variables>; dataProvider: Subscribe<Data, Delta, Variables>;
update?: ({ update?: ({
@ -30,7 +30,7 @@ export interface useDataProviderParams<
data: Data | null; data: Data | null;
totalCount?: number; totalCount?: number;
}) => boolean; }) => boolean;
variables?: Variables; variables: Variables;
skipUpdates?: boolean; skipUpdates?: boolean;
skip?: boolean; skip?: boolean;
} }
@ -45,7 +45,7 @@ export interface useDataProviderParams<
export const useDataProvider = < export const useDataProvider = <
Data, Data,
Delta, Delta,
Variables extends OperationVariables = OperationVariables Variables extends OperationVariables | undefined = undefined
>({ >({
dataProvider, dataProvider,
update, update,
@ -163,8 +163,12 @@ export const useDataProvider = <
}; };
}; };
export const useThrottledDataProvider = <Data, Delta>( export const useThrottledDataProvider = <
params: Omit<useDataProviderParams<Data, Delta>, 'update'>, Data,
Delta,
Variables extends OperationVariables = OperationVariables
>(
params: Omit<useDataProviderParams<Data, Delta, Variables>, 'update'>,
wait?: number wait?: number
) => { ) => {
const [data, setData] = useState<Data | null>(null); 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 { useDataProvider } from '@vegaprotocol/react-helpers';
import { AsyncRenderer } from '@vegaprotocol/ui-toolkit'; import { AsyncRenderer } from '@vegaprotocol/ui-toolkit';
import type { AgGridReact } from 'ag-grid-react'; 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 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 { TradesTable } from './trades-table';
import type { Trade, TradeEdge } from './trades-data-provider'; import type { Trade, TradeEdge } from './trades-data-provider';
import type { TradesQueryVariables } from './__generated__/Trades';
import { useOrderStore } from '@vegaprotocol/orders'; import { useOrderStore } from '@vegaprotocol/orders';
interface TradesContainerProps { interface TradesContainerProps {
@ -22,11 +21,6 @@ export const TradesContainer = ({ marketId }: TradesContainerProps) => {
const scrolledToTop = useRef(true); const scrolledToTop = useRef(true);
const updateOrder = useOrderStore((store) => store.update); const updateOrder = useOrderStore((store) => store.update);
const variables = useMemo<TradesQueryVariables>(
() => ({ marketId, maxTrades: MAX_TRADES }),
[marketId]
);
const addNewRows = useCallback(() => { const addNewRows = useCallback(() => {
if (newRows.current === 0) { if (newRows.current === 0) {
return; return;
@ -84,7 +78,7 @@ export const TradesContainer = ({ marketId }: TradesContainerProps) => {
dataProvider: tradesWithMarketProvider, dataProvider: tradesWithMarketProvider,
update, update,
insert, insert,
variables, variables: { marketId },
}); });
totalCountRef.current = totalCount; totalCountRef.current = totalCount;
const getRows = makeInfiniteScrollGetRows<TradeEdge>( const getRows = makeInfiniteScrollGetRows<TradeEdge>(

View File

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

View File

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

View File

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

View File

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