Compare commits

...
Author SHA1 Message Date
Dariusz Majcherczyk 83123ac3bd test: added wait for market list 2023-02-22 11:37:38 +01:00
Dariusz Majcherczyk fe234c2f18 test: remove unnecessary clear storage 2023-02-22 11:19:15 +01:00
Dariusz Majcherczyk 115bc67260 test: deposit flow from collateral window e2e tests 2023-02-22 10:59:29 +01:00
mattrussell36 8e82bf43fb chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-02-22 06:08:43 +00:00
Bartłomiej Głownia 4bb57e9c47 fix(market-depth): fix order book priceInCenter calculation and scroll to price (#2901) 2023-02-21 18:01:48 -08:00
mattrussell36 ce6d4cb35d chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-02-22 00:10:23 +00:00
mattrussell36 647f04656f chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-02-21 18:06:17 +00:00
daro-maj 67186bf4c0 test(trading): node version update for live workflow (#2954) 2023-02-21 17:46:00 +00:00
mattrussell36 96007bd230 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-02-21 12:06:41 +00:00
9 changed files with 366 additions and 257 deletions
+4
View File
@@ -18,6 +18,10 @@ jobs:
- name: Checkout
uses: actions/checkout@v2
- uses: actions/setup-node@v3
with:
node-version: 16
- name: Run Cypress tests
uses: cypress-io/github-action@v4
with:
+114 -37
View File
@@ -2761,7 +2761,7 @@
"tranche_end": "2023-12-05T00:00:00.000Z",
"total_added": "86666.297",
"total_removed": "0",
"locked_amount": "68085.0879931921036624142",
"locked_amount": "67847.5938683683107992492",
"deposits": [
{
"amount": "86666.297",
@@ -2827,7 +2827,7 @@
"tranche_end": "2023-06-01T00:00:00.000Z",
"total_added": "2500",
"total_removed": "0",
"locked_amount": "1370.11249745624725",
"locked_amount": "1356.37321301383775",
"deposits": [
{
"amount": "2500",
@@ -3214,8 +3214,8 @@
"tranche_start": "2023-02-01T00:00:00.000Z",
"tranche_end": "2023-08-01T00:00:00.000Z",
"total_added": "37500",
"total_removed": "3009.936598125",
"locked_amount": "33303.35424340085625",
"total_removed": "3143.644010625",
"locked_amount": "33096.1263620319225",
"deposits": [
{
"amount": "7500",
@@ -3249,6 +3249,11 @@
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
"tx": "0x6f125819654942fd6c08c0929e7f9e9629eb65c2cfa92acae0f8db9685a43537"
},
{
"amount": "133.7074125",
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
"tx": "0xe6d2a4fbaaba23872bffd24f3300875c90e280b3b8f1bf1c8beb33d7c2fab25f"
},
{
"amount": "183.137181525",
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
@@ -3290,6 +3295,12 @@
"tranche_id": 34,
"tx": "0x6f125819654942fd6c08c0929e7f9e9629eb65c2cfa92acae0f8db9685a43537"
},
{
"amount": "133.7074125",
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
"tranche_id": 34,
"tx": "0xe6d2a4fbaaba23872bffd24f3300875c90e280b3b8f1bf1c8beb33d7c2fab25f"
},
{
"amount": "183.137181525",
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
@@ -3304,8 +3315,8 @@
}
],
"total_tokens": "7500",
"withdrawn_tokens": "734.823415425",
"remaining_tokens": "6765.176584575"
"withdrawn_tokens": "868.530827925",
"remaining_tokens": "6631.469172075"
},
{
"address": "0x0B4e6fcE839B01ef43DA6F890FAC7B1Afb004600",
@@ -3337,7 +3348,7 @@
"tranche_end": "2023-12-05T00:00:00.000Z",
"total_added": "129999.45",
"total_removed": "0",
"locked_amount": "68022.9689171925635253",
"locked_amount": "67785.69147587502055098",
"deposits": [
{
"amount": "129999.45",
@@ -3403,7 +3414,7 @@
"tranche_end": "2023-09-03T00:00:00.000Z",
"total_added": "62600",
"total_removed": "0",
"locked_amount": "33228.45556823947058",
"locked_amount": "33056.91100329781984",
"deposits": [
{
"amount": "10000",
@@ -3596,7 +3607,7 @@
"tranche_end": "2023-09-17T00:00:00.000Z",
"total_added": "5000",
"total_removed": "0",
"locked_amount": "2845.810819381025",
"locked_amount": "2832.1091768138",
"deposits": [
{
"amount": "5000",
@@ -3807,7 +3818,7 @@
"tranche_end": "2023-04-05T00:00:00.000Z",
"total_added": "97499.58",
"total_removed": "0",
"locked_amount": "9946.3975044981425824698",
"locked_amount": "9713.6504379590992984068",
"deposits": [
{
"amount": "97499.58",
@@ -3840,7 +3851,7 @@
"tranche_end": "2023-04-05T00:00:00.000Z",
"total_added": "135173.4239508",
"total_removed": "98230.390980249184455396",
"locked_amount": "13595.008214756311272005455032",
"locked_amount": "13276.88315690216652689038794",
"deposits": [
{
"amount": "135173.4239508",
@@ -3886,7 +3897,7 @@
"tranche_end": "2023-04-05T00:00:00.000Z",
"total_added": "32499.86",
"total_removed": "0",
"locked_amount": "4184.277665044903355192",
"locked_amount": "4086.3649934787768574158",
"deposits": [
{
"amount": "32499.86",
@@ -3919,7 +3930,7 @@
"tranche_end": "2023-04-05T00:00:00.000Z",
"total_added": "10833.29",
"total_removed": "0",
"locked_amount": "1361.9417767027505250727",
"locked_amount": "1330.0721522300519402316",
"deposits": [
{
"amount": "10833.29",
@@ -3952,7 +3963,7 @@
"tranche_end": "2023-04-05T00:00:00.000Z",
"total_added": "22749.93",
"total_removed": "4720.860935375",
"locked_amount": "5091.2425455170145949404",
"locked_amount": "4972.1067712859949945882",
"deposits": [
{
"amount": "6500",
@@ -4103,8 +4114,8 @@
"tranche_start": "2022-11-01T00:00:00.000Z",
"tranche_end": "2023-05-01T00:00:00.000Z",
"total_added": "22500",
"total_removed": "4546.97235645",
"locked_amount": "8545.548457642726575",
"total_removed": "4680.6740139",
"locked_amount": "8421.2117288213625",
"deposits": [
{
"amount": "7500",
@@ -4133,6 +4144,11 @@
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
"tx": "0xb40f1c9a538e1998da74acd7fceea8a718e97b2af8e903277e16a4b729628a52"
},
{
"amount": "133.70165745",
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
"tx": "0x95c8bfd256f138974dcc7c0db2164496b30ca09f30b858453b41d70e47036d6f"
},
{
"amount": "167.6680479",
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
@@ -4234,6 +4250,12 @@
"tranche_id": 33,
"tx": "0xb40f1c9a538e1998da74acd7fceea8a718e97b2af8e903277e16a4b729628a52"
},
{
"amount": "133.70165745",
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
"tranche_id": 33,
"tx": "0x95c8bfd256f138974dcc7c0db2164496b30ca09f30b858453b41d70e47036d6f"
},
{
"amount": "167.6680479",
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
@@ -4320,8 +4342,8 @@
}
],
"total_tokens": "7500",
"withdrawn_tokens": "4546.97235645",
"remaining_tokens": "2953.02764355"
"withdrawn_tokens": "4680.6740139",
"remaining_tokens": "2819.3259861"
},
{
"address": "0x2539b51EbDE65a75672aBcfE9439a706a99D18D1",
@@ -4346,7 +4368,7 @@
"tranche_end": "2023-06-02T00:00:00.000Z",
"total_added": "1939928.38",
"total_removed": "928642.9598472029154",
"locked_amount": "535442.5012105923529199294",
"locked_amount": "530126.4601568372440486808",
"deposits": [
{
"amount": "1852091.69",
@@ -36638,7 +36660,7 @@
"tranche_end": "2023-06-05T00:00:00.000Z",
"total_added": "3732368.4671",
"total_removed": "609657.626547646980493",
"locked_amount": "847290.027811054165469569256",
"locked_amount": "839121.029033631540661543897",
"deposits": [
{
"amount": "1998.95815",
@@ -37975,8 +37997,8 @@
"tranche_start": "2022-06-05T00:00:00.000Z",
"tranche_end": "2023-12-05T00:00:00.000Z",
"total_added": "15870102.715470999700000001",
"total_removed": "572826.70366554650274952",
"locked_amount": "8304123.623039459863359272608269620829954",
"total_removed": "575133.08469788626314452",
"locked_amount": "8275156.8778745719866654003456575909002433",
"deposits": [
{
"amount": "16249.93",
@@ -38535,6 +38557,16 @@
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
"tx": "0xe5fb2af901c286e061280cff77fcf8056e52dffb23b9fc7bba3dfb15abee8abd"
},
{
"amount": "627.592612816272125",
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
"tx": "0xaf8d0b715946e8724a7013e1bbc17ace8f1ad153f37a9ae2f63ac02323d3f6a1"
},
{
"amount": "1678.78841952348827",
"user": "0xe3eB4CF43C072658401996b71D43eE26E573562D",
"tx": "0x8484839ab3c62c0f75b415b468f8297f24655622fb9fb94db7652f00093c9cec"
},
{
"amount": "856.08784586478614",
"user": "0xe3eB4CF43C072658401996b71D43eE26E573562D",
@@ -40267,6 +40299,12 @@
"tranche_id": 2,
"tx": "0xe5fb2af901c286e061280cff77fcf8056e52dffb23b9fc7bba3dfb15abee8abd"
},
{
"amount": "627.592612816272125",
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
"tranche_id": 2,
"tx": "0xaf8d0b715946e8724a7013e1bbc17ace8f1ad153f37a9ae2f63ac02323d3f6a1"
},
{
"amount": "966.75883976995675",
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
@@ -41409,8 +41447,8 @@
}
],
"total_tokens": "259998.8875",
"withdrawn_tokens": "123657.945723010546375",
"remaining_tokens": "136340.941776989453625"
"withdrawn_tokens": "124285.5383358268185",
"remaining_tokens": "135713.3491641731815"
},
{
"address": "0x89051CAb67Bc7F8CC44F7e270c6EDaf1EC57676c",
@@ -41637,6 +41675,12 @@
"tranche_id": 2,
"tx": "0x4870598253a2a664d9c96af37904f08ac772ee9620b5975560ef7e7fb51c9a7f"
},
{
"amount": "1678.78841952348827",
"user": "0xe3eB4CF43C072658401996b71D43eE26E573562D",
"tranche_id": 2,
"tx": "0x8484839ab3c62c0f75b415b468f8297f24655622fb9fb94db7652f00093c9cec"
},
{
"amount": "856.08784586478614",
"user": "0xe3eB4CF43C072658401996b71D43eE26E573562D",
@@ -41867,8 +41911,8 @@
}
],
"total_tokens": "150551.801",
"withdrawn_tokens": "70289.2123238900799",
"remaining_tokens": "80262.5886761099201"
"withdrawn_tokens": "71968.00074341356817",
"remaining_tokens": "78583.80025658643183"
},
{
"address": "0x4d982Ab0823fD2f48e934a7be2bb0a5374a26148",
@@ -43629,8 +43673,8 @@
"tranche_start": "2021-11-05T00:00:00.000Z",
"tranche_end": "2023-05-05T00:00:00.000Z",
"total_added": "14597706.0446472999",
"total_removed": "3930266.448252707129704282",
"locked_amount": "1944868.680169767119412467869710978",
"total_removed": "4397321.114354183114240906",
"locked_amount": "1918126.765987955843750323100941893",
"deposits": [
{
"amount": "129284.449",
@@ -43884,6 +43928,16 @@
"user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b",
"tx": "0x5fcd1783bbd5f8b519d94782580777ff37d84df742f617baf632fc693615f39a"
},
{
"amount": "466184.354416277260082874",
"user": "0xfc3b2D0b548d3edBb512CeE0Bb79Fb7FaD50AaF3",
"tx": "0x43c34302d1895983b4f5b28ad186ea31662a045f3c7c63075a1c9db4b059e42d"
},
{
"amount": "870.31168519872445375",
"user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b",
"tx": "0xec81b6b72e023ad393f023ea8ca6ff754d39370a61f77b3e519e3d2285835d39"
},
{
"amount": "1333.9237119810715295",
"user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b",
@@ -46804,6 +46858,12 @@
"tranche_id": 3,
"tx": "0x5fcd1783bbd5f8b519d94782580777ff37d84df742f617baf632fc693615f39a"
},
{
"amount": "870.31168519872445375",
"user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b",
"tranche_id": 3,
"tx": "0xec81b6b72e023ad393f023ea8ca6ff754d39370a61f77b3e519e3d2285835d39"
},
{
"amount": "1333.9237119810715295",
"user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b",
@@ -49248,8 +49308,8 @@
}
],
"total_tokens": "359123.469575",
"withdrawn_tokens": "310868.25548215842910375",
"remaining_tokens": "48255.21409284157089625"
"withdrawn_tokens": "311738.5671673571535575",
"remaining_tokens": "47384.9024076428464425"
},
{
"address": "0xBdd412797c1B78535Afc5F71503b91fAbD0160fB",
@@ -50127,6 +50187,12 @@
}
],
"withdrawals": [
{
"amount": "466184.354416277260082874",
"user": "0xfc3b2D0b548d3edBb512CeE0Bb79Fb7FaD50AaF3",
"tranche_id": 3,
"tx": "0x43c34302d1895983b4f5b28ad186ea31662a045f3c7c63075a1c9db4b059e42d"
},
{
"amount": "32828.670452546475795519",
"user": "0xfc3b2D0b548d3edBb512CeE0Bb79Fb7FaD50AaF3",
@@ -50135,8 +50201,8 @@
}
],
"total_tokens": "575536.79731735",
"withdrawn_tokens": "32828.670452546475795519",
"remaining_tokens": "542708.126864803524204481"
"withdrawn_tokens": "499013.024868823735878393",
"remaining_tokens": "76523.772448526264121607"
},
{
"address": "0xaAeD573103e3f981867C4cd31c6674DE1c8a13c3",
@@ -50587,7 +50653,7 @@
"tranche_end": "2023-04-05T00:00:00.000Z",
"total_added": "5778205.3912159303",
"total_removed": "2749067.463242913023296295",
"locked_amount": "451525.974462742142445752680292053",
"locked_amount": "440960.082435758127733940976567038",
"deposits": [
{
"amount": "552496.6455",
@@ -52613,8 +52679,8 @@
"tranche_start": "2022-06-05T00:00:00.000Z",
"tranche_end": "2023-06-05T00:00:00.000Z",
"total_added": "472355.6199999996",
"total_removed": "34093.0379154332685",
"locked_amount": "134257.94822294382169397518924404",
"total_removed": "34139.7667776862685",
"locked_amount": "132963.5236706734184837412785388",
"deposits": [
{
"amount": "3000",
@@ -59333,6 +59399,11 @@
"user": "0x28FC83947F02f59Cb36b40f97f2D32BBC5D00585",
"tx": "0x9d1ced8d04e8d58af92b1434bcb269487a3f6ddf4305c0739ab0f158f5401ec6"
},
{
"amount": "46.728862253",
"user": "0xB18ba44b42d48206aA9f6cBEb7a9e463F558847a",
"tx": "0x0a0e9842787da5fb2a91c18e4f83f2d32a4b2fd05a5c3f05d0541eb56cd5f0db"
},
{
"amount": "13.1116203702",
"user": "0x4cBC0C88d8FE503f62823B42f30a4900292C13bE",
@@ -69424,6 +69495,12 @@
}
],
"withdrawals": [
{
"amount": "46.728862253",
"user": "0xB18ba44b42d48206aA9f6cBEb7a9e463F558847a",
"tranche_id": 5,
"tx": "0x0a0e9842787da5fb2a91c18e4f83f2d32a4b2fd05a5c3f05d0541eb56cd5f0db"
},
{
"amount": "25.047884956",
"user": "0xB18ba44b42d48206aA9f6cBEb7a9e463F558847a",
@@ -69432,8 +69509,8 @@
}
],
"total_tokens": "100",
"withdrawn_tokens": "25.047884956",
"remaining_tokens": "74.952115044"
"withdrawn_tokens": "71.776747209",
"remaining_tokens": "28.223252791"
},
{
"address": "0x4c587d5981bC62F2821f138Ed5e33caF79b9B84d",
@@ -66,3 +66,25 @@ describe('deposit form validation', { tags: '@smoke' }, () => {
.should('have.text', 'Insufficient amount in Ethereum wallet');
});
});
describe('deposit actions', { tags: '@smoke' }, () => {
before(() => {
cy.mockWeb3Provider();
cy.mockTradingPage();
cy.mockSubscription();
cy.setVegaWallet();
cy.visit('/');
cy.wait('@MarketsCandles');
cy.getByTestId('dialog-close').click();
});
it('Deposit to trade is visble', () => {
cy.getByTestId('Collateral').click();
cy.contains('[data-testid="deposit"]', 'Deposit to trade').should(
'be.visible'
);
cy.contains('[data-testid="deposit"]', 'Deposit to trade').click();
connectEthereumWallet('MetaMask');
cy.getByTestId('deposit-submit').should('be.visible');
});
});
@@ -1,11 +1,12 @@
import { mockConnectWallet } from '@vegaprotocol/cypress';
before(() => {
beforeEach(() => {
cy.mockTradingPage();
cy.mockSubscription();
cy.visit('/');
cy.wait('@Markets');
cy.wait('@MarketsData');
cy.wait('@MarketsCandles');
cy.getByTestId('dialog-close').click();
});
@@ -19,11 +20,11 @@ describe('Desktop view', { tags: '@smoke' }, () => {
cy.getByTestId('navbar')
.find(`[data-testid="navbar-links"] a[data-testid=${link}]`)
.then((element) => {
cy.contains('Loading...').should('not.exist');
cy.wrap(element).click();
cy.wrap(element)
.get('span.absolute.md\\:h-1.w-full')
.should('exist');
cy.contains('No market').should('not.exist');
cy.location('hash').should('equal', hashes[index]);
});
});
@@ -69,7 +70,6 @@ describe('Mobile view', { tags: '@smoke' }, () => {
cy.getByTestId('button-menu-drawer').click();
cy.getByTestId('menu-drawer').within((el) => {
cy.wrap(el).getByTestId('Trading').click();
cy.contains('No market').should('not.exist');
cy.location('hash').should('equal', '#/markets/market-1');
});
});
@@ -42,10 +42,18 @@ export const update: Update<
},
};
if (delta.buy) {
updatedData.depth.buy = updateLevels(data.depth.buy ?? [], delta.buy);
updatedData.depth.buy = updateLevels(
data.depth.buy ?? [],
delta.buy,
false
);
}
if (delta.sell) {
updatedData.depth.sell = updateLevels(data.depth.sell ?? [], delta.sell);
updatedData.depth.sell = updateLevels(
data.depth.sell ?? [],
delta.sell,
true
);
}
updatedData.depth.sequenceNumber = delta.sequenceNumber;
return updatedData;
+51 -43
View File
@@ -26,9 +26,19 @@ export interface OrderbookRowData {
type PartialOrderbookRowData = Pick<OrderbookRowData, 'price' | 'ask' | 'bid'>;
export type OrderbookData = Partial<
Omit<MarketData, '__typename' | 'market'>
> & { rows: OrderbookRowData[] | null };
type OrderbookMarketData = Pick<
MarketData,
| 'bestStaticBidPrice'
| 'bestStaticOfferPrice'
| 'indicativePrice'
| 'indicativeVolume'
| 'marketTradingMode'
>;
export type OrderbookData = Partial<OrderbookMarketData> & {
rows: OrderbookRowData[] | null;
midPrice?: string;
};
export const getPriceLevel = (price: string | bigint, resolution: number) => {
const p = BigInt(price);
@@ -40,6 +50,18 @@ export const getPriceLevel = (price: string | bigint, resolution: number) => {
return priceLevel.toString();
};
export const getMidPrice = (
sell: PriceLevelFieldsFragment[] | null | undefined,
buy: PriceLevelFieldsFragment[] | null | undefined,
resolution: number
) =>
buy?.length && sell?.length
? getPriceLevel(
(BigInt(buy[0].price) + BigInt(sell[0].price)) / BigInt(2),
resolution
)
: undefined;
const getMaxVolumes = (orderbookData: OrderbookRowData[]) => ({
bid: Math.max(...orderbookData.map((data) => data.bid)),
ask: Math.max(...orderbookData.map((data) => data.ask)),
@@ -157,8 +179,15 @@ export const compactRows = (
}
orderbookData.push(row);
});
// order by price, it's safe to cast to number price diff should not exceed Number.MAX_SAFE_INTEGER
orderbookData.sort((a, b) => Number(BigInt(b.price) - BigInt(a.price)));
orderbookData.sort((a, b) => {
if (a === b) {
return 0;
}
if (BigInt(a.price) > BigInt(b.price)) {
return -1;
}
return 1;
});
// count cumulative volumes
if (orderbookData.length > 1) {
const maxIndex = orderbookData.length - 1;
@@ -253,28 +282,6 @@ export const updateCompactedRows = (
return data;
};
export const mapMarketData = (
data: Pick<
MarketData,
| 'staticMidPrice'
| 'bestStaticBidPrice'
| 'bestStaticOfferPrice'
| 'indicativePrice'
> | null,
resolution: number
) => ({
staticMidPrice:
data?.staticMidPrice && getPriceLevel(data?.staticMidPrice, resolution),
bestStaticBidPrice:
data?.bestStaticBidPrice &&
getPriceLevel(data?.bestStaticBidPrice, resolution),
bestStaticOfferPrice:
data?.bestStaticOfferPrice &&
getPriceLevel(data?.bestStaticOfferPrice, resolution),
indicativePrice:
data?.indicativePrice && getPriceLevel(data?.indicativePrice, resolution),
});
/**
* Updates raw data with new data received from subscription - mutates input
* @param levels
@@ -283,7 +290,8 @@ export const mapMarketData = (
*/
export const updateLevels = (
draft: PriceLevelFieldsFragment[],
updates: (PriceLevelFieldsFragment | PriceLevelFieldsFragment)[]
updates: (PriceLevelFieldsFragment | PriceLevelFieldsFragment)[],
ascending = true
) => {
const levels = [...draft];
updates.forEach((update) => {
@@ -295,8 +303,10 @@ export const updateLevels = (
levels[index] = update;
}
} else if (update.volume !== '0') {
index = levels.findIndex(
(level) => BigInt(level.price) > BigInt(update.price)
index = levels.findIndex((level) =>
ascending
? BigInt(level.price) > BigInt(update.price)
: BigInt(level.price) < BigInt(update.price)
);
if (index !== -1) {
levels.splice(index, 0, update);
@@ -346,22 +356,20 @@ export const generateMockData = ({
numberOfOrders: '',
}));
const rows = compactRows(sell, buy, resolution);
const marketTradingMode =
overlap > 0
? Schema.MarketTradingMode.TRADING_MODE_BATCH_AUCTION
: Schema.MarketTradingMode.TRADING_MODE_CONTINUOUS;
return {
rows,
resolution,
indicativeVolume: indicativeVolume?.toString(),
marketTradingMode:
overlap > 0
? Schema.MarketTradingMode.TRADING_MODE_BATCH_AUCTION
: Schema.MarketTradingMode.TRADING_MODE_CONTINUOUS,
...mapMarketData(
{
staticMidPrice: '',
bestStaticBidPrice: bestStaticBidPrice.toString(),
bestStaticOfferPrice: bestStaticOfferPrice.toString(),
indicativePrice: indicativePrice?.toString() ?? '',
},
resolution
),
marketTradingMode,
midPrice: ((bestStaticBidPrice + bestStaticOfferPrice) / 2).toString(),
bestStaticBidPrice: bestStaticBidPrice.toString(),
bestStaticOfferPrice: bestStaticOfferPrice.toString(),
indicativePrice: indicativePrice
? getPriceLevel(indicativePrice.toString(), resolution)
: undefined,
};
};
@@ -1,3 +1,4 @@
import React from 'react';
import throttle from 'lodash/throttle';
import { AsyncRenderer } from '@vegaprotocol/ui-toolkit';
import { Orderbook } from './orderbook';
@@ -8,12 +9,14 @@ import type { MarketData } from '@vegaprotocol/market-list';
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import type {
MarketDepthUpdateSubscription,
MarketDepthQuery,
PriceLevelFieldsFragment,
} from './__generated__/MarketDepth';
import {
compactRows,
updateCompactedRows,
mapMarketData,
getMidPrice,
getPriceLevel,
} from './orderbook-data';
import type { OrderbookData } from './orderbook-data';
import { usePersistedOrderStore } from '@vegaprotocol/orders';
@@ -31,6 +34,7 @@ export const OrderbookManager = ({ marketId }: OrderbookManagerProps) => {
});
const dataRef = useRef<OrderbookData>({ rows: null });
const marketDataRef = useRef<MarketData | null>(null);
const rawDataRef = useRef<MarketDepthQuery['market'] | null>(null);
const deltaRef = useRef<{
sell: PriceLevelFieldsFragment[];
buy: PriceLevelFieldsFragment[];
@@ -42,7 +46,17 @@ export const OrderbookManager = ({ marketId }: OrderbookManagerProps) => {
throttle(() => {
dataRef.current = {
...marketDataRef.current,
...mapMarketData(marketDataRef.current, resolutionRef.current),
indicativePrice: marketDataRef.current?.indicativePrice
? getPriceLevel(
marketDataRef.current.indicativePrice,
resolutionRef.current
)
: undefined,
midPrice: getMidPrice(
rawDataRef.current?.depth.sell,
rawDataRef.current?.depth.buy,
resolution
),
rows:
deltaRef.current.buy.length || deltaRef.current.sell.length
? updateCompactedRows(
@@ -56,14 +70,16 @@ export const OrderbookManager = ({ marketId }: OrderbookManagerProps) => {
deltaRef.current.buy = [];
deltaRef.current.sell = [];
setOrderbookData(dataRef.current);
}, 1000)
}, 250)
);
const update = useCallback(
({
delta: deltas,
data: rawData,
}: {
delta?: MarketDepthUpdateSubscription['marketsDepthUpdate'];
data?: MarketDepthQuery['market'];
}) => {
if (!dataRef.current.rows) {
return false;
@@ -78,6 +94,7 @@ export const OrderbookManager = ({ marketId }: OrderbookManagerProps) => {
if (delta.buy) {
deltaRef.current.buy.push(...delta.buy);
}
rawDataRef.current = rawData;
updateOrderbookData.current();
}
return true;
@@ -134,9 +151,14 @@ export const OrderbookManager = ({ marketId }: OrderbookManagerProps) => {
}
dataRef.current = {
...marketDataRef.current,
...mapMarketData(marketDataRef.current, resolution),
indicativePrice: getPriceLevel(
marketDataRef.current.indicativePrice,
resolution
),
midPrice: getMidPrice(data.depth.sell, data.depth.buy, resolution),
rows: compactRows(data.depth.sell, data.depth.buy, resolution),
};
rawDataRef.current = data;
setOrderbookData(dataRef.current);
return () => {
+11 -11
View File
@@ -92,7 +92,7 @@ describe('Orderbook', () => {
expect(result.getByTestId('scroll').scrollTop).toBe(90 * rowHeight);
});
it('should should keep price it the middle', async () => {
it('should keep price it the middle', async () => {
window.innerHeight = 11 * rowHeight;
const result = render(
<Orderbook
@@ -106,7 +106,7 @@ describe('Orderbook', () => {
await waitFor(() => screen.getByTestId(`bid-vol-${params.midPrice}`));
const scrollElement = result.getByTestId('scroll');
expect(scrollElement.scrollTop).toBe(91 * rowHeight);
scrollElement.scrollTop = 92 * rowHeight;
scrollElement.scrollTop = 92 * rowHeight + 0.01;
fireEvent.scroll(scrollElement);
result.rerender(
<Orderbook
@@ -121,10 +121,10 @@ describe('Orderbook', () => {
/>
);
await waitFor(() => screen.getByTestId(`bid-vol-${params.midPrice}`));
expect(result.getByTestId('scroll').scrollTop).toBe(91 * rowHeight);
expect(result.getByTestId('scroll').scrollTop).toBe(91 * rowHeight + 0.01);
});
it('should should get back to mid price on click', async () => {
it('should get back to mid price on click', async () => {
window.innerHeight = 11 * rowHeight;
const result = render(
<Orderbook
@@ -138,15 +138,15 @@ describe('Orderbook', () => {
await waitFor(() => screen.getByTestId(`bid-vol-${params.midPrice}`));
const scrollElement = result.getByTestId('scroll');
expect(scrollElement.scrollTop).toBe(91 * rowHeight);
scrollElement.scrollTop = 0;
scrollElement.scrollTop = 1;
fireEvent.scroll(scrollElement);
expect(result.getByTestId('scroll').scrollTop).toBe(0);
expect(result.getByTestId('scroll').scrollTop).toBe(1);
const scrollToMidPriceButton = result.getByTestId('scroll-to-midprice');
fireEvent.click(scrollToMidPriceButton);
expect(result.getByTestId('scroll').scrollTop).toBe(91 * rowHeight);
expect(result.getByTestId('scroll').scrollTop).toBe(91 * rowHeight + 1);
});
it('should should get back to mid price on resolution change', async () => {
it('should get back to mid price on resolution change', async () => {
window.innerHeight = 11 * rowHeight;
const result = render(
<Orderbook
@@ -160,9 +160,9 @@ describe('Orderbook', () => {
await waitFor(() => screen.getByTestId(`bid-vol-${params.midPrice}`));
const scrollElement = result.getByTestId('scroll');
expect(scrollElement.scrollTop).toBe(91 * rowHeight);
scrollElement.scrollTop = 0;
scrollElement.scrollTop = 1;
fireEvent.scroll(scrollElement);
expect(result.getByTestId('scroll').scrollTop).toBe(0);
expect(result.getByTestId('scroll').scrollTop).toBe(1);
const resolutionSelect = result.getByTestId(
'resolution'
) as HTMLSelectElement;
@@ -181,6 +181,6 @@ describe('Orderbook', () => {
onResolutionChange={onResolutionChange}
/>
);
expect(result.getByTestId('scroll').scrollTop).toBe(5 * rowHeight);
expect(result.getByTestId('scroll').scrollTop).toBe(6 * rowHeight);
});
});
+125 -157
View File
@@ -1,15 +1,7 @@
import styles from './orderbook.module.scss';
import colors from 'tailwindcss/colors';
import {
useEffect,
useLayoutEffect,
useRef,
useState,
useMemo,
useCallback,
Fragment,
} from 'react';
import { useEffect, useRef, useState, useCallback, Fragment } from 'react';
import classNames from 'classnames';
import {
@@ -21,7 +13,7 @@ import {
} from '@vegaprotocol/react-helpers';
import * as Schema from '@vegaprotocol/types';
import { OrderbookRow } from './orderbook-row';
import { createRow, getPriceLevel } from './orderbook-data';
import { createRow } from './orderbook-data';
import { Checkbox, Icon, Splash } from '@vegaprotocol/ui-toolkit';
import type { OrderbookData, OrderbookRowData } from './orderbook-data';
@@ -36,7 +28,7 @@ interface OrderbookProps extends OrderbookData {
const HorizontalLine = ({ top, testId }: { top: string; testId: string }) => (
<div
className="absolute border-b border-default inset-x-0"
className="absolute border-b border-default inset-x-0 hidden"
style={{ top }}
data-testid={testId}
/>
@@ -97,7 +89,12 @@ const getRowsToRender = (
};
// 17px of row height plus 5px gap
export const gridGap = 5;
export const rowHeight = 22;
// top padding to make space for header
const headerPadding = 30;
// bottom padding to make space for footer
const footerPadding = 25;
// buffer size in rows
const bufferSize = 30;
// margin size in px, when reached scrollOffset will be updated
@@ -112,30 +109,30 @@ const getBestStaticBidPriceLinePosition = (
rows: OrderbookRowData[] | null
) => {
let bestStaticBidPriceLinePosition = '';
if (maxPriceLevel !== '0' && minPriceLevel !== '0') {
if (
bestStaticBidPrice &&
BigInt(bestStaticBidPrice) < BigInt(maxPriceLevel) &&
BigInt(bestStaticBidPrice) > BigInt(minPriceLevel)
) {
if (fillGaps) {
if (
rows?.length &&
bestStaticBidPrice &&
BigInt(bestStaticBidPrice) < BigInt(maxPriceLevel) &&
BigInt(bestStaticBidPrice) > BigInt(minPriceLevel)
) {
if (fillGaps) {
bestStaticBidPriceLinePosition = (
((BigInt(maxPriceLevel) - BigInt(bestStaticBidPrice)) /
BigInt(resolution)) *
BigInt(rowHeight) +
BigInt(headerPadding) -
BigInt(3)
).toString();
} else {
const index = rows?.findIndex(
(row) => BigInt(row.price) <= BigInt(bestStaticBidPrice)
);
if (index !== undefined && index !== -1) {
bestStaticBidPriceLinePosition = (
((BigInt(maxPriceLevel) - BigInt(bestStaticBidPrice)) /
BigInt(resolution) +
BigInt(1)) *
BigInt(rowHeight) +
BigInt(1)
index * rowHeight +
headerPadding -
3
).toString();
} else {
const index = rows?.findIndex(
(row) => BigInt(row.price) <= BigInt(bestStaticBidPrice)
);
if (index !== undefined && index !== -1) {
bestStaticBidPriceLinePosition = (
(index + 1) * rowHeight +
1
).toString();
}
}
}
}
@@ -151,6 +148,7 @@ const getBestStaticOfferPriceLinePosition = (
) => {
let bestStaticOfferPriceLinePosition = '';
if (
rows?.length &&
bestStaticOfferPrice &&
BigInt(bestStaticOfferPrice) <= BigInt(maxPriceLevel) &&
BigInt(bestStaticOfferPrice) > BigInt(minPriceLevel)
@@ -159,9 +157,10 @@ const getBestStaticOfferPriceLinePosition = (
bestStaticOfferPriceLinePosition = (
((BigInt(maxPriceLevel) - BigInt(bestStaticOfferPrice)) /
BigInt(resolution) +
BigInt(2)) *
BigInt(1)) *
BigInt(rowHeight) +
BigInt(1)
BigInt(headerPadding) -
BigInt(3)
).toString();
} else {
const index = rows?.findIndex(
@@ -169,8 +168,9 @@ const getBestStaticOfferPriceLinePosition = (
);
if (index !== undefined && index !== -1) {
bestStaticOfferPriceLinePosition = (
(index + 2) * rowHeight +
1
(index + 1) * rowHeight +
headerPadding -
3
).toString();
}
}
@@ -187,7 +187,7 @@ const OrderbookDebugInfo = ({
bestStaticOfferPrice,
maxPriceLevel,
minPriceLevel,
resolution,
midPrice,
}: {
decimalPlaces: number;
numberOfRows: number;
@@ -198,7 +198,7 @@ const OrderbookDebugInfo = ({
bestStaticOfferPrice?: string;
maxPriceLevel: string;
minPriceLevel: string;
resolution: number;
midPrice?: string;
}) => (
<Fragment>
<div
@@ -247,16 +247,7 @@ const OrderbookDebugInfo = ({
decimalPlaces
),
midPrice: addDecimalsFixedFormatNumber(
(bestStaticOfferPrice &&
bestStaticBidPrice &&
getPriceLevel(
BigInt(bestStaticOfferPrice) +
(BigInt(bestStaticBidPrice) -
BigInt(bestStaticOfferPrice)) /
BigInt(2),
resolution
)) ??
'0',
midPrice ?? '0',
decimalPlaces
),
},
@@ -270,6 +261,7 @@ const OrderbookDebugInfo = ({
export const Orderbook = ({
rows,
midPrice,
bestStaticBidPrice,
bestStaticOfferPrice,
marketTradingMode,
@@ -295,21 +287,36 @@ export const Orderbook = ({
// price level which is rendered in center of viewport, need to preserve price level when rows will be added or removed
// if undefined then we render mid price in center
const priceInCenter = useRef<string>();
// by default mid price is rendered in center - view locked on mid price
const [lockOnMidPrice, setLockOnMidPrice] = useState(true);
const resolutionRef = useRef(resolution);
const [viewportHeight, setViewportHeight] = useState(window.innerHeight);
// show price levels with no orders, can lead to enormous number of rows
const [fillGaps, setFillGaps] = useState(!!initialFillGaps);
const numberOfRows = useMemo(
() => (fillGaps ? getNumberOfRows(rows, resolution) : rows?.length ?? 0),
[rows, resolution, fillGaps]
);
const maxPriceLevel = rows?.[0]?.price ?? '0';
const minPriceLevel = (
fillGaps
? BigInt(maxPriceLevel) - BigInt(Math.floor(numberOfRows * resolution))
: BigInt(rows?.[rows.length - 1]?.price ?? '0')
).toString();
const [debug, setDebug] = useState(false);
const numberOfRows = fillGaps
? getNumberOfRows(rows, resolution)
: rows?.length ?? 0;
const maxPriceLevel = rows?.[0]?.price ?? '0';
const minPriceLevel = rows?.[rows.length - 1]?.price ?? '0';
let offset = Math.max(0, Math.round(scrollOffset / rowHeight));
const prependingBufferSize = Math.min(bufferSize, offset);
offset -= prependingBufferSize;
const viewportSize = Math.round(viewportHeight / rowHeight);
const limit = Math.min(
prependingBufferSize + viewportSize + bufferSize,
numberOfRows - offset
);
const data = fillGaps
? getRowsToRender(rows, resolution, offset, limit)
: rows?.slice(offset, offset + limit) ?? [];
const paddingTop = offset * rowHeight + headerPadding;
const paddingBottom =
(numberOfRows - offset - limit) * rowHeight + footerPadding;
const updateScrollOffset = useCallback(
(scrollTop: number) => {
if (Math.abs(scrollOffset - scrollTop) > marginSize) {
@@ -318,23 +325,35 @@ export const Orderbook = ({
},
[scrollOffset]
);
const onScroll = useCallback(
(event: React.UIEvent<HTMLDivElement>) => {
const { scrollTop } = event.currentTarget;
const { scrollTop, scrollHeight, clientHeight } = event.currentTarget;
updateScrollOffset(scrollTop);
if (scrollTop === scrollTopRef.current) {
return;
} else if ((scrollTop - scrollTopRef.current) % rowHeight === 0) {
if (scrollElement.current) {
scrollElement.current.scrollTop = scrollTopRef.current;
}
return;
}
if (scrollTop === 0 || scrollHeight === clientHeight + scrollTop) {
priceInCenter.current = undefined;
} else {
// top offset in rows to row in the middle
const offsetTop = Math.floor(
(scrollTop +
Math.floor((viewportHeight - footerPadding - headerPadding) / 2)) /
rowHeight
);
priceInCenter.current = fillGaps
? (
BigInt(maxPriceLevel) -
BigInt(offsetTop) * BigInt(resolution)
).toString()
: rows?.[Math.min(offsetTop, rows.length - 1)].price.toString();
}
const offsetTop = Math.floor(
(scrollTop + Math.floor(viewportHeight / 2)) / rowHeight
);
priceInCenter.current = fillGaps
? (
BigInt(resolution) + // extra row on very top - sticky header
BigInt(maxPriceLevel) -
BigInt(offsetTop) * BigInt(resolution)
).toString()
: rows?.[Math.min(offsetTop, rows.length - 1)]?.price?.toString();
if (lockOnMidPrice) {
setLockOnMidPrice(false);
}
@@ -361,24 +380,22 @@ export const Orderbook = ({
(Number(
(BigInt(maxPriceLevel) - BigInt(price)) / BigInt(resolution)
) +
1) * // add one row for sticky header
rowHeight +
rowHeight / 2 -
(viewportHeight % rowHeight);
1) *
rowHeight;
} else if (rows) {
const index = rows.findIndex(
(row) => BigInt(row.price) <= BigInt(price)
);
if (index !== -1) {
scrollTop =
index * rowHeight + rowHeight / 2 - (viewportHeight % rowHeight);
if (
price === rows[index].price ||
index === 0 ||
BigInt(rows[index].price) - BigInt(price) <
BigInt(price) - BigInt(rows[index - 1].price)
) {
scrollTop += rowHeight;
scrollTop = rowHeight * (index + 1);
if (index !== 0) {
const diffToCurrentRow =
BigInt(price) - BigInt(rows[index].price);
const diffToPreviousRow =
BigInt(rows[index - 1].price) - BigInt(price);
if (diffToPreviousRow < diffToCurrentRow) {
scrollTop -= rowHeight;
}
}
}
}
@@ -389,7 +406,13 @@ export const Orderbook = ({
(scrollTopRef.current % rowHeight) - (scrollTop % rowHeight);
const priceCenterScrollOffset = Math.max(
0,
Math.min(scrollTop, numberOfRows * rowHeight - viewportHeight)
Math.min(
scrollTop,
numberOfRows * rowHeight +
headerPadding +
footerPadding +
-viewportHeight
)
);
if (scrollTopRef.current !== priceCenterScrollOffset) {
updateScrollOffset(priceCenterScrollOffset);
@@ -410,72 +433,31 @@ export const Orderbook = ({
);
const scrollToMidPrice = useCallback(() => {
if (!bestStaticOfferPrice || !bestStaticBidPrice) {
if (!midPrice) {
return;
}
priceInCenter.current = undefined;
let midPrice = getPriceLevel(
BigInt(bestStaticOfferPrice) +
(BigInt(bestStaticBidPrice) - BigInt(bestStaticOfferPrice)) / BigInt(2),
resolution
);
if (BigInt(midPrice) > BigInt(maxPriceLevel)) {
midPrice = maxPriceLevel;
} else {
if (BigInt(midPrice) < BigInt(minPriceLevel)) {
midPrice = minPriceLevel.toString();
}
}
scrollToPrice(midPrice);
setLockOnMidPrice(true);
}, [
bestStaticOfferPrice,
bestStaticBidPrice,
scrollToPrice,
resolution,
maxPriceLevel,
minPriceLevel,
]);
}, [midPrice, scrollToPrice]);
// adjust scroll position to keep selected price in center
useLayoutEffect(() => {
useEffect(() => {
if (priceInCenter.current) {
scrollToPrice(priceInCenter.current);
} else if (lockOnMidPrice && midPrice) {
scrollToPrice(midPrice);
}
}, [midPrice, scrollToPrice, lockOnMidPrice]);
useEffect(() => {
if (resolutionRef.current !== resolution) {
priceInCenter.current = undefined;
resolutionRef.current = resolution;
setLockOnMidPrice(true);
}
if (priceInCenter.current) {
scrollToPrice(priceInCenter.current);
} else {
scrollToMidPrice();
}
}, [scrollToMidPrice, scrollToPrice, resolution]);
}, [resolution]);
// handles window resize
useEffect(() => {
function handleResize() {
if (rootElement.current) {
setViewportHeight(
rootElement.current.clientHeight || window.innerHeight
);
}
}
window.addEventListener('resize', handleResize);
handleResize();
return () => window.removeEventListener('resize', handleResize);
}, []);
// sets the correct width of header and footer
useLayoutEffect(() => {
if (
!gridElement.current ||
!headerElement.current ||
!footerElement.current
) {
return;
}
const gridWidth = gridElement.current.clientWidth;
headerElement.current.style.width = `${gridWidth}px`;
footerElement.current.style.width = `${gridWidth}px`;
}, [headerElement, footerElement, gridElement]);
// handles resizing of the Allotment.Pane (x-axis)
// adjusts the header and footer width
const gridResizeHandler: ResizeObserverCallback = useCallback(
@@ -509,20 +491,6 @@ export const Orderbook = ({
useResizeObserver(gridElement.current, gridResizeHandler);
useResizeObserver(rootElement.current, rootElementResizeHandler);
let offset = Math.max(0, Math.round(scrollOffset / rowHeight));
const prependingBufferSize = Math.min(bufferSize, offset);
offset -= prependingBufferSize;
const viewportSize = Math.round(viewportHeight / rowHeight);
const limit = Math.min(
prependingBufferSize + viewportSize + bufferSize,
numberOfRows - offset
);
const data = fillGaps
? getRowsToRender(rows, resolution, offset, limit)
: rows?.slice(offset, offset + limit) ?? [];
const paddingTop = offset * rowHeight;
const paddingBottom = (numberOfRows - offset - limit) * rowHeight;
const tableBody =
data && data.length !== 0 ? (
<div
@@ -603,16 +571,16 @@ export const Orderbook = ({
</div>
</div>
<div
className={`h-full overflow-auto relative ${styles['scroll']} pt-[26px] pb-[17px]`}
className={`h-full overflow-auto relative ${styles['scroll']}`}
onScroll={onScroll}
ref={scrollElement}
data-testid="scroll"
>
<div
className="relative text-right min-h-full"
className="relative text-right min-h-full overflow-hidden"
style={{
paddingTop: paddingTop,
paddingBottom: paddingBottom,
paddingTop,
paddingBottom,
background: tableBody ? gradientStyles : 'none',
}}
ref={gridElement}
@@ -685,7 +653,7 @@ export const Orderbook = ({
{debug && (
<OrderbookDebugInfo
decimalPlaces={decimalPlaces}
resolution={resolution}
midPrice={midPrice}
numberOfRows={numberOfRows}
viewportHeight={viewportHeight}
lockOnMidPrice={lockOnMidPrice}