chore: update account, fills and orders subscriptions in data providers

This commit is contained in:
Bartłomiej Głownia 2022-09-13 17:25:57 +02:00
parent 434df13079
commit 9ffe28be0b
12 changed files with 84 additions and 306 deletions

View File

@ -1,4 +1,4 @@
import * as Types from '@vegaprotocol/types';
import { Schema as Types } from '@vegaprotocol/types';
import { gql } from '@apollo/client';
import * as Apollo from '@apollo/client';

View File

@ -1,30 +1,47 @@
import produce from 'immer';
import type { IterableElement } from 'type-fest';
import {
AccountsDocument,
AccountEventsDocument,
} from './__generated__/Accounts';
} from './__generated___/Accounts';
import type {
AccountFieldsFragment,
AccountsQuery,
AccountEventsSubscription,
} from './__generated__/Accounts';
} from './__generated___/Accounts';
import { makeDataProvider } from '@vegaprotocol/react-helpers';
export const getId = (data: AccountFieldsFragment) =>
`${data.type}-${data.asset.symbol}-${data.market?.id ?? 'null'}`;
function isAccount(
account:
| AccountFieldsFragment
| IterableElement<AccountEventsSubscription['accounts']>
): account is AccountFieldsFragment {
return (account as AccountFieldsFragment).__typename === 'Account';
}
export const getId = (
account:
| AccountFieldsFragment
| IterableElement<AccountEventsSubscription['accounts']>
) =>
isAccount(account)
? `${account.type}-${account.asset.id}-${account.market?.id ?? 'null'}`
: `${account.type}-${account.assetId}-${account.marketId}`;
const update = (
data: AccountFieldsFragment[],
delta: AccountFieldsFragment
deltas: AccountEventsSubscription['accounts']
) => {
return produce(data, (draft) => {
const id = getId(delta);
const index = draft.findIndex((a) => getId(a) === id);
if (index !== -1) {
draft[index] = delta;
} else {
draft.push(delta);
}
deltas.forEach((delta) => {
const id = getId(delta);
const index = draft.findIndex((a) => getId(a) === id);
if (index !== -1) {
draft[index].balance = delta.balance;
} else {
// #TODO handle new account
}
});
});
};
@ -36,13 +53,13 @@ const getData = (
const getDelta = (
subscriptionData: AccountEventsSubscription
): AccountFieldsFragment => subscriptionData.accounts;
): AccountEventsSubscription['accounts'] => subscriptionData.accounts;
export const accountsDataProvider = makeDataProvider<
AccountsQuery,
AccountFieldsFragment[],
AccountEventsSubscription,
AccountFieldsFragment
AccountEventsSubscription['accounts']
>({
query: AccountsDocument,
subscriptionQuery: AccountEventsDocument,

View File

@ -3,7 +3,10 @@ import { produce } from 'immer';
import merge from 'lodash/merge';
import { AsyncRenderer } from '@vegaprotocol/ui-toolkit';
import { useDataProvider, addSummaryRows } from '@vegaprotocol/react-helpers';
import type { AccountFieldsFragment } from './__generated__/Accounts';
import type {
AccountFieldsFragment,
AccountEventsSubscription,
} from './__generated___/Accounts';
import type { AgGridReact } from 'ag-grid-react';
import {
@ -21,26 +24,29 @@ export const AccountsManager = ({ partyId }: AccountsManagerProps) => {
const gridRef = useRef<AgGridReact | null>(null);
const variables = useMemo(() => ({ partyId }), [partyId]);
const update = useCallback(
({ delta }: { delta: AccountFieldsFragment }) => {
({ delta: deltas }: { delta: AccountEventsSubscription['accounts'] }) => {
const update: AccountFieldsFragment[] = [];
const add: AccountFieldsFragment[] = [];
if (!gridRef.current?.api) {
return false;
}
const rowNode = gridRef.current.api.getRowNode(getId(delta));
if (rowNode) {
const updatedData = produce<AccountFieldsFragment>(
rowNode.data,
(draft: AccountFieldsFragment) => {
merge(draft, delta);
const api = gridRef.current.api;
deltas.forEach((delta) => {
const rowNode = api.getRowNode(getId(delta));
if (rowNode) {
const updatedData = produce<AccountFieldsFragment>(
rowNode.data,
(draft: AccountFieldsFragment) => {
merge(draft, delta);
}
);
if (updatedData !== rowNode.data) {
update.push(updatedData);
}
);
if (updatedData !== rowNode.data) {
update.push(updatedData);
} else {
// #TODO handle new account (or leave it to data provider to handle it)
}
} else {
add.push(delta);
}
});
if (update.length || add.length) {
gridRef.current.api.applyTransactionAsync({
update,
@ -62,7 +68,7 @@ export const AccountsManager = ({ partyId }: AccountsManagerProps) => {
);
const { data, error, loading } = useDataProvider<
AccountFieldsFragment[],
AccountFieldsFragment
AccountEventsSubscription['accounts']
>({ dataProvider: accountsDataProvider, update, variables });
return (
<AsyncRenderer loading={loading} error={error} data={data}>

View File

@ -14,7 +14,7 @@ import type { SummaryRow } from '@vegaprotocol/react-helpers';
import { AgGridDynamic as AgGrid } from '@vegaprotocol/ui-toolkit';
import { AgGridColumn } from 'ag-grid-react';
import type { AgGridReact } from 'ag-grid-react';
import type { AccountFieldsFragment } from './__generated__/Accounts';
import type { AccountFieldsFragment } from './__generated___/Accounts';
import { getId } from './accounts-data-provider';
import { useAssetDetailsDialogStore } from '@vegaprotocol/assets';
import type { AccountType } from '@vegaprotocol/types';

View File

@ -3,8 +3,6 @@ import {
addDecimalsFormatNumber,
useDataProvider,
} from '@vegaprotocol/react-helpers';
import type { AccountFieldsFragment } from './__generated__/Accounts';
import { accountsDataProvider } from './accounts-data-provider';
interface AssetBalanceProps {
@ -14,10 +12,7 @@ interface AssetBalanceProps {
export const AssetBalance = ({ partyId, assetSymbol }: AssetBalanceProps) => {
const variables = useMemo(() => ({ partyId }), [partyId]);
const { data } = useDataProvider<
AccountFieldsFragment[],
AccountFieldsFragment
>({
const { data } = useDataProvider({
dataProvider: accountsDataProvider,
variables,
});

View File

@ -1,4 +1,4 @@
export * from './__generated__/Accounts';
export * from './__generated___/Accounts';
export * from './accounts-container';
export * from './accounts-data-provider';
export * from './accounts-manager';

View File

@ -57,94 +57,6 @@ export interface Fills_party_tradesConnection_edges_node_sellerFee {
liquidityFee: string;
}
export interface Fills_party_tradesConnection_edges_node_market_tradableInstrument_instrument_product_settlementAsset {
__typename: "Asset";
/**
* The ID of the asset
*/
id: string;
/**
* The symbol of the asset (e.g: GBP)
*/
symbol: string;
/**
* The precision of the asset. Should match the decimal precision of the asset on its native chain, e.g: for ERC20 assets, it is often 18
*/
decimals: number;
}
export interface Fills_party_tradesConnection_edges_node_market_tradableInstrument_instrument_product {
__typename: "Future";
/**
* The name of the asset (string)
*/
settlementAsset: Fills_party_tradesConnection_edges_node_market_tradableInstrument_instrument_product_settlementAsset;
}
export interface Fills_party_tradesConnection_edges_node_market_tradableInstrument_instrument {
__typename: "Instrument";
/**
* Uniquely identify an instrument across all instruments available on Vega (string)
*/
id: string;
/**
* A short non necessarily unique code used to easily describe the instrument (e.g: FX:BTCUSD/DEC18) (string)
*/
code: string;
/**
* Full and fairly descriptive name for the instrument
*/
name: string;
/**
* A reference to or instance of a fully specified product, including all required product parameters for that product (Product union)
*/
product: Fills_party_tradesConnection_edges_node_market_tradableInstrument_instrument_product;
}
export interface Fills_party_tradesConnection_edges_node_market_tradableInstrument {
__typename: "TradableInstrument";
/**
* An instance of, or reference to, a fully specified instrument.
*/
instrument: Fills_party_tradesConnection_edges_node_market_tradableInstrument_instrument;
}
export interface Fills_party_tradesConnection_edges_node_market {
__typename: "Market";
/**
* Market ID
*/
id: string;
/**
* decimalPlaces indicates the number of decimal places that an integer must be shifted by in order to get a correct
* number denominated in the currency of the market. (uint64)
*
* Examples:
* Currency Balance decimalPlaces Real Balance
* GBP 100 0 GBP 100
* GBP 100 2 GBP 1.00
* GBP 100 4 GBP 0.01
* GBP 1 4 GBP 0.0001 ( 0.01p )
*
* GBX (pence) 100 0 GBP 1.00 (100p )
* GBX (pence) 100 2 GBP 0.01 ( 1p )
* GBX (pence) 100 4 GBP 0.0001 ( 0.01p )
* GBX (pence) 1 4 GBP 0.000001 ( 0.0001p)
*/
decimalPlaces: number;
/**
* positionDecimalPlaces indicates the number of decimal places that an integer must be shifted in order to get a correct size (uint64).
* i.e. 0 means there are no fractional orders for the market, and order sizes are always whole sizes.
* 2 means sizes given as 10^2 * desired size, e.g. a desired size of 1.23 is represented as 123 in this market.
* This sets how big the smallest order / position on the market can be.
*/
positionDecimalPlaces: number;
/**
* An instance of, or reference to, a tradable instrument.
*/
tradableInstrument: Fills_party_tradesConnection_edges_node_market_tradableInstrument;
}
export interface Fills_party_tradesConnection_edges_node {
__typename: "Trade";
/**
@ -191,10 +103,6 @@ export interface Fills_party_tradesConnection_edges_node {
* The fee paid by the seller side of the trade
*/
sellerFee: Fills_party_tradesConnection_edges_node_sellerFee;
/**
* The market the trade occurred on
*/
market: Fills_party_tradesConnection_edges_node_market;
}
export interface Fills_party_tradesConnection_edges {

View File

@ -95,14 +95,6 @@ export interface FillsSub_trades {
* The fee paid by the seller side of the trade
*/
sellerFee: FillsSub_trades_sellerFee;
/**
* The batch in which the buyer order was submitted (applies only for auction modes)
*/
buyerAuctionBatch: number | null;
/**
* The batch in which the seller order was submitted (applies only for auction modes)
*/
sellerAuctionBatch: number | null;
}
export interface FillsSub {

View File

@ -6,13 +6,12 @@ import {
defaultAppend as append,
} from '@vegaprotocol/react-helpers';
import type { PageInfo } from '@vegaprotocol/react-helpers';
import type { FillFields } from './__generated__/FillFields';
import type {
Fills,
Fills_party_tradesConnection_edges,
Fills_party_tradesConnection_edges_node,
} from './__generated__/Fills';
import type { FillsSub } from './__generated__/FillsSub';
import type { FillsSub, FillsSub_trades } from './__generated__/FillsSub';
export const FILLS_QUERY = gql`
query Fills($partyId: ID!, $marketId: ID, $pagination: Pagination) {
@ -44,27 +43,6 @@ export const FILLS_QUERY = gql`
infrastructureFee
liquidityFee
}
market {
id
decimalPlaces
positionDecimalPlaces
tradableInstrument {
instrument {
id
code
name
product {
... on Future {
settlementAsset {
id
symbol
decimals
}
}
}
}
}
}
}
cursor
}
@ -103,15 +81,13 @@ export const FILLS_SUB = gql`
infrastructureFee
liquidityFee
}
buyerAuctionBatch
sellerAuctionBatch
}
}
`;
const update = (
data: (Fills_party_tradesConnection_edges | null)[],
delta: FillFields[]
delta: FillsSub_trades[]
) => {
return produce(data, (draft) => {
orderBy(delta, 'createdAt').forEach((node) => {
@ -126,7 +102,17 @@ const update = (
} else {
const firstNode = draft[0]?.node;
if (firstNode && node.createdAt >= firstNode.createdAt) {
draft.unshift({ node, cursor: '', __typename: 'TradeEdge' });
const { buyerId, sellerId, ...trade } = node;
draft.unshift({
node: {
...trade,
__typename: 'Trade',
buyer: { id: buyerId, __typename: 'Party' },
seller: { id: buyerId, __typename: 'Party' },
},
cursor: '',
__typename: 'TradeEdge',
});
}
}
});
@ -136,10 +122,10 @@ const update = (
const getData = (
responseData: Fills
): Fills_party_tradesConnection_edges[] | null =>
responseData.party?.tradesConnection.edges || null;
responseData.party?.tradesConnection?.edges || null;
const getPageInfo = (responseData: Fills): PageInfo | null =>
responseData.party?.tradesConnection.pageInfo || null;
responseData.party?.tradesConnection?.pageInfo || null;
const getDelta = (subscriptionData: FillsSub) => subscriptionData.trades || [];

View File

@ -14,21 +14,6 @@ export const generateOrder = (
const order: Orders_party_ordersConnection_edges_node = {
__typename: 'Order',
id: 'order-id2',
market: {
__typename: 'Market',
id: 'market-id',
decimalPlaces: 2,
positionDecimalPlaces: 2,
tradableInstrument: {
__typename: 'TradableInstrument',
instrument: {
id: 'instrument-id',
__typename: 'Instrument',
code: 'instrument-code',
name: 'instrument-market-name',
},
},
},
size: '10',
type: OrderType.TYPE_MARKET,
status: OrderStatus.STATUS_ACTIVE,
@ -64,20 +49,6 @@ export const generateMockOrders =
return [
generateOrder({
id: '066468C06549101DAF7BC51099E1412A0067DC08C246B7D8013C9D0CBF1E8EE7',
market: {
__typename: 'Market',
id: 'c9f5acd348796011c075077e4d58d9b7f1689b7c1c8e030a5e886b83aa96923d',
decimalPlaces: 5,
positionDecimalPlaces: 0,
tradableInstrument: {
__typename: 'TradableInstrument',
instrument: {
__typename: 'Instrument',
code: 'AAVEDAI.MF21',
name: 'AAVEDAI Monthly (30 Jun 2022)',
},
},
},
size: '10',
type: OrderType.TYPE_LIMIT,
status: OrderStatus.STATUS_FILLED,
@ -89,20 +60,6 @@ export const generateMockOrders =
}),
generateOrder({
id: '48DB6767E4E4E0F649C5A13ABFADE39F8451C27DA828DAF14B7A1E8E5EBDAD99',
market: {
__typename: 'Market',
id: '5a4b0b9e9c0629f0315ec56fcb7bd444b0c6e4da5ec7677719d502626658a376',
decimalPlaces: 5,
positionDecimalPlaces: 0,
tradableInstrument: {
__typename: 'TradableInstrument',
instrument: {
__typename: 'Instrument',
code: 'TSLA.QM21',
name: 'Tesla Quarterly (30 Jun 2022)',
},
},
},
size: '1',
type: OrderType.TYPE_LIMIT,
status: OrderStatus.STATUS_FILLED,
@ -114,20 +71,6 @@ export const generateMockOrders =
}),
generateOrder({
id: '4e93702990712c41f6995fcbbd94f60bb372ad12d64dfa7d96d205c49f790336',
market: {
__typename: 'Market',
id: 'c6f4337b31ed57a961969c3ba10297b369d01b9e75a4cbb96db4fc62886444e6',
decimalPlaces: 5,
positionDecimalPlaces: 0,
tradableInstrument: {
__typename: 'TradableInstrument',
instrument: {
__typename: 'Instrument',
code: 'BTCUSD.MF21',
name: 'BTCUSD Monthly (30 Jun 2022)',
},
},
},
size: '1',
type: OrderType.TYPE_LIMIT,
status: OrderStatus.STATUS_FILLED,

View File

@ -9,76 +9,12 @@ import { Pagination, OrderType, Side, OrderStatus, OrderRejectionReason, OrderTi
// GraphQL query operation: Orders
// ====================================================
export interface Orders_party_ordersConnection_edges_node_market_tradableInstrument_instrument {
__typename: "Instrument";
/**
* Uniquely identify an instrument across all instruments available on Vega (string)
*/
id: string;
/**
* A short non necessarily unique code used to easily describe the instrument (e.g: FX:BTCUSD/DEC18) (string)
*/
code: string;
/**
* Full and fairly descriptive name for the instrument
*/
name: string;
}
export interface Orders_party_ordersConnection_edges_node_market_tradableInstrument {
__typename: "TradableInstrument";
/**
* An instance of, or reference to, a fully specified instrument.
*/
instrument: Orders_party_ordersConnection_edges_node_market_tradableInstrument_instrument;
}
export interface Orders_party_ordersConnection_edges_node_market {
__typename: "Market";
/**
* Market ID
*/
id: string;
/**
* decimalPlaces indicates the number of decimal places that an integer must be shifted by in order to get a correct
* number denominated in the currency of the market. (uint64)
*
* Examples:
* Currency Balance decimalPlaces Real Balance
* GBP 100 0 GBP 100
* GBP 100 2 GBP 1.00
* GBP 100 4 GBP 0.01
* GBP 1 4 GBP 0.0001 ( 0.01p )
*
* GBX (pence) 100 0 GBP 1.00 (100p )
* GBX (pence) 100 2 GBP 0.01 ( 1p )
* GBX (pence) 100 4 GBP 0.0001 ( 0.01p )
* GBX (pence) 1 4 GBP 0.000001 ( 0.0001p)
*/
decimalPlaces: number;
/**
* positionDecimalPlaces indicates the number of decimal places that an integer must be shifted in order to get a correct size (uint64).
* i.e. 0 means there are no fractional orders for the market, and order sizes are always whole sizes.
* 2 means sizes given as 10^2 * desired size, e.g. a desired size of 1.23 is represented as 123 in this market.
* This sets how big the smallest order / position on the market can be.
*/
positionDecimalPlaces: number;
/**
* An instance of, or reference to, a tradable instrument.
*/
tradableInstrument: Orders_party_ordersConnection_edges_node_market_tradableInstrument;
}
export interface Orders_party_ordersConnection_edges_node {
__typename: "Order";
/**
* Hash of the order data
*/
id: string;
/**
* The market the order is trading on (probably stored internally as a hash of the market details)
*/
market: Orders_party_ordersConnection_edges_node_market;
id: string;
/**
* The order type
*/

View File

@ -11,7 +11,7 @@ import type {
Orders,
Orders_party_ordersConnection_edges,
OrderSub,
OrderFields,
OrderSub_orders,
} from '../';
export const ORDERS_QUERY = gql`
@ -22,18 +22,6 @@ export const ORDERS_QUERY = gql`
edges {
node {
id
market {
id
decimalPlaces
positionDecimalPlaces
tradableInstrument {
instrument {
id
code
name
}
}
}
type
side
size
@ -81,7 +69,7 @@ export const ORDERS_SUB = gql`
export const update = (
data: Orders_party_ordersConnection_edges[],
delta: OrderFields[]
delta: OrderSub_orders[]
) => {
return produce(data, (draft) => {
// A single update can contain the same order with multiple updates, so we need to find
@ -103,7 +91,14 @@ export const update = (
draft.unshift(...draft.splice(index, 1));
}
} else if (newer) {
draft.unshift({ node, cursor: '', __typename: 'OrderEdge' });
draft.unshift({
node: {
...node,
__typename: 'Order',
},
cursor: '',
__typename: 'OrderEdge',
});
}
});
});
@ -112,12 +107,12 @@ export const update = (
const getData = (
responseData: Orders
): Orders_party_ordersConnection_edges[] | null =>
responseData?.party?.ordersConnection.edges || null;
responseData?.party?.ordersConnection?.edges || null;
const getDelta = (subscriptionData: OrderSub) => subscriptionData.orders || [];
const getPageInfo = (responseData: Orders): PageInfo | null =>
responseData.party?.ordersConnection.pageInfo || null;
responseData.party?.ordersConnection?.pageInfo || null;
export const ordersDataProvider = makeDataProvider({
query: ORDERS_QUERY,