Compare commits
12
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
83123ac3bd | ||
|
|
fe234c2f18 | ||
|
|
115bc67260 | ||
|
|
8e82bf43fb | ||
|
|
4bb57e9c47 | ||
|
|
ce6d4cb35d | ||
|
|
647f04656f | ||
|
|
67186bf4c0 | ||
|
|
96007bd230 | ||
|
|
554d9ecb06 | ||
|
|
a12a5b3a15 | ||
|
|
a79e2f85bc |
@@ -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:
|
||||
|
||||
@@ -2761,7 +2761,7 @@
|
||||
"tranche_end": "2023-12-05T00:00:00.000Z",
|
||||
"total_added": "86666.297",
|
||||
"total_removed": "0",
|
||||
"locked_amount": "68204.1112467141064238038",
|
||||
"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": "1376.99811762311775",
|
||||
"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": "33407.2091774094525",
|
||||
"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": "68141.88357697181733279",
|
||||
"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": "33314.42734652460852",
|
||||
"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": "2852.677574835109",
|
||||
"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": "10063.041708339958997166",
|
||||
"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": "13754.440703626382165312324964",
|
||||
"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": "4233.3478672634979601984",
|
||||
"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": "1377.9136513589326159943",
|
||||
"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": "5150.948980235602753755",
|
||||
"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": "8607.861418047881625",
|
||||
"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": "538106.703967371859419281",
|
||||
"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": "851383.979871343316981060205",
|
||||
"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": "8318640.5141883996928739572247338928521222",
|
||||
"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": "1958270.581387242551207619820565376",
|
||||
"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": "456821.14668779032274974044808737",
|
||||
"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": "134906.65832892426701505075291732",
|
||||
"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",
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { act } from '@testing-library/react-hooks';
|
||||
import { act } from '@testing-library/react';
|
||||
import { usePendingBalancesStore } from './use-pending-balances-manager';
|
||||
import type { Event } from 'ethers';
|
||||
|
||||
|
||||
@@ -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');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -27,12 +27,8 @@ fragment UpdateNetworkParameterProposal on Proposal {
|
||||
}
|
||||
|
||||
subscription OnUpdateNetworkParameters {
|
||||
busEvents(types: [Proposal], batchSize: 0) {
|
||||
event {
|
||||
... on Proposal {
|
||||
...UpdateNetworkParameterProposal
|
||||
}
|
||||
}
|
||||
proposals {
|
||||
...UpdateNetworkParameterProposal
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ export type UpdateNetworkParameterProposalFragment = { __typename?: 'Proposal',
|
||||
export type OnUpdateNetworkParametersSubscriptionVariables = Types.Exact<{ [key: string]: never; }>;
|
||||
|
||||
|
||||
export type OnUpdateNetworkParametersSubscription = { __typename?: 'Subscription', busEvents?: Array<{ __typename?: 'BusEvent', event: { __typename?: 'AccountEvent' } | { __typename?: 'Asset' } | { __typename?: 'AuctionEvent' } | { __typename?: 'Deposit' } | { __typename?: 'LiquidityProvision' } | { __typename?: 'LossSocialization' } | { __typename?: 'MarginLevels' } | { __typename?: 'Market' } | { __typename?: 'MarketData' } | { __typename?: 'MarketEvent' } | { __typename?: 'MarketTick' } | { __typename?: 'NodeSignature' } | { __typename?: 'OracleSpec' } | { __typename?: 'Order' } | { __typename?: 'Party' } | { __typename?: 'PositionResolution' } | { __typename?: 'Proposal', id?: string | null, state: Types.ProposalState, datetime: any, terms: { __typename?: 'ProposalTerms', enactmentDatetime?: any | null, change: { __typename?: 'NewAsset' } | { __typename?: 'NewFreeform' } | { __typename?: 'NewMarket' } | { __typename?: 'UpdateAsset' } | { __typename?: 'UpdateMarket' } | { __typename?: 'UpdateNetworkParameter', networkParameter: { __typename?: 'NetworkParameter', key: string, value: string } } } } | { __typename?: 'RiskFactor' } | { __typename?: 'SettleDistressed' } | { __typename?: 'SettlePosition' } | { __typename?: 'TimeUpdate' } | { __typename?: 'Trade' } | { __typename?: 'TransactionResult' } | { __typename?: 'TransferResponses' } | { __typename?: 'Vote' } | { __typename?: 'Withdrawal' } }> | null };
|
||||
export type OnUpdateNetworkParametersSubscription = { __typename?: 'Subscription', proposals: { __typename?: 'Proposal', id?: string | null, state: Types.ProposalState, datetime: any, terms: { __typename?: 'ProposalTerms', enactmentDatetime?: any | null, change: { __typename?: 'NewAsset' } | { __typename?: 'NewFreeform' } | { __typename?: 'NewMarket' } | { __typename?: 'UpdateAsset' } | { __typename?: 'UpdateMarket' } | { __typename?: 'UpdateNetworkParameter', networkParameter: { __typename?: 'NetworkParameter', key: string, value: string } } } } };
|
||||
|
||||
export type ProposalOfMarketQueryVariables = Types.Exact<{
|
||||
marketId: Types.Scalars['ID'];
|
||||
@@ -83,12 +83,8 @@ export type ProposalEventSubscriptionHookResult = ReturnType<typeof useProposalE
|
||||
export type ProposalEventSubscriptionResult = Apollo.SubscriptionResult<ProposalEventSubscription>;
|
||||
export const OnUpdateNetworkParametersDocument = gql`
|
||||
subscription OnUpdateNetworkParameters {
|
||||
busEvents(types: [Proposal], batchSize: 0) {
|
||||
event {
|
||||
... on Proposal {
|
||||
...UpdateNetworkParameterProposal
|
||||
}
|
||||
}
|
||||
proposals {
|
||||
...UpdateNetworkParameterProposal
|
||||
}
|
||||
}
|
||||
${UpdateNetworkParameterProposalFragmentDoc}`;
|
||||
|
||||
@@ -7,11 +7,16 @@ import type { Toast } from '@vegaprotocol/ui-toolkit';
|
||||
import { ToastHeading } from '@vegaprotocol/ui-toolkit';
|
||||
import { useToasts } from '@vegaprotocol/ui-toolkit';
|
||||
import { ExternalLink, Intent } from '@vegaprotocol/ui-toolkit';
|
||||
import compact from 'lodash/compact';
|
||||
import { useCallback } from 'react';
|
||||
import type { UpdateNetworkParameterProposalFragment } from './__generated__/Proposal';
|
||||
import { useOnUpdateNetworkParametersSubscription } from './__generated__/Proposal';
|
||||
|
||||
export const PROPOSAL_STATES_TO_TOAST = [
|
||||
ProposalState.STATE_DECLINED,
|
||||
ProposalState.STATE_ENACTED,
|
||||
ProposalState.STATE_OPEN,
|
||||
ProposalState.STATE_PASSED,
|
||||
];
|
||||
const CLOSE_AFTER = 5000;
|
||||
type Proposal = UpdateNetworkParameterProposalFragment;
|
||||
|
||||
@@ -75,26 +80,16 @@ export const useUpdateNetworkParametersToasts = () => {
|
||||
[remove]
|
||||
);
|
||||
|
||||
useOnUpdateNetworkParametersSubscription({
|
||||
onData: (options) => {
|
||||
const events = compact(options.data.data?.busEvents);
|
||||
if (!events || events.length === 0) return;
|
||||
const validProposals = events
|
||||
.filter(
|
||||
(ev) =>
|
||||
ev.event.__typename === 'Proposal' &&
|
||||
ev.event.terms.__typename === 'ProposalTerms' &&
|
||||
ev.event.terms.change.__typename === 'UpdateNetworkParameter' &&
|
||||
[
|
||||
ProposalState.STATE_DECLINED,
|
||||
ProposalState.STATE_ENACTED,
|
||||
ProposalState.STATE_OPEN,
|
||||
ProposalState.STATE_PASSED,
|
||||
].includes(ev.event.state)
|
||||
)
|
||||
.map((ev) => ev.event as Proposal);
|
||||
if (validProposals.length < 5) {
|
||||
validProposals.forEach((p) => setToast(fromProposal(p)));
|
||||
return useOnUpdateNetworkParametersSubscription({
|
||||
onData: ({ data }) => {
|
||||
// note proposals is poorly named, it is actually a single proposal
|
||||
const proposal = data.data?.proposals;
|
||||
if (!proposal) return;
|
||||
if (proposal.terms.change.__typename !== 'UpdateNetworkParameter') return;
|
||||
|
||||
// if one of the following states show a toast
|
||||
if (PROPOSAL_STATES_TO_TOAST.includes(proposal.state)) {
|
||||
setToast(fromProposal(proposal));
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
+106
-63
@@ -1,16 +1,19 @@
|
||||
import merge from 'lodash/merge';
|
||||
import type { MockedResponse } from '@apollo/client/testing';
|
||||
import { MockedProvider } from '@apollo/client/testing';
|
||||
import { renderHook } from '@testing-library/react-hooks';
|
||||
import { ProposalState } from '@vegaprotocol/types';
|
||||
import type { ReactNode } from 'react';
|
||||
import { useUpdateNetworkParametersToasts } from './use-update-network-paramaters-toasts';
|
||||
import {
|
||||
PROPOSAL_STATES_TO_TOAST,
|
||||
useUpdateNetworkParametersToasts,
|
||||
} from './use-update-network-paramaters-toasts';
|
||||
import type {
|
||||
UpdateNetworkParameterProposalFragment,
|
||||
OnUpdateNetworkParametersSubscription,
|
||||
} from './__generated__/Proposal';
|
||||
import { OnUpdateNetworkParametersDocument } from './__generated__/Proposal';
|
||||
import { useToasts } from '@vegaprotocol/ui-toolkit';
|
||||
import { waitFor } from '@testing-library/react';
|
||||
import { waitFor, renderHook } from '@testing-library/react';
|
||||
|
||||
const render = (mocks?: MockedResponse[]) => {
|
||||
const wrapper = ({ children }: { children: ReactNode }) => (
|
||||
@@ -42,56 +45,6 @@ const generateUpdateNetworkParametersProposal = (
|
||||
},
|
||||
});
|
||||
|
||||
const mockedWrongEvent: MockedResponse<OnUpdateNetworkParametersSubscription> =
|
||||
{
|
||||
request: {
|
||||
query: OnUpdateNetworkParametersDocument,
|
||||
},
|
||||
result: {
|
||||
data: {
|
||||
__typename: 'Subscription',
|
||||
busEvents: [
|
||||
{
|
||||
__typename: 'BusEvent',
|
||||
event: {
|
||||
__typename: 'Asset',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const mockedEmptyEvent: MockedResponse<OnUpdateNetworkParametersSubscription> =
|
||||
{
|
||||
request: {
|
||||
query: OnUpdateNetworkParametersDocument,
|
||||
},
|
||||
result: {
|
||||
data: {
|
||||
__typename: 'Subscription',
|
||||
busEvents: [],
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const mockedEvent: MockedResponse<OnUpdateNetworkParametersSubscription> = {
|
||||
request: {
|
||||
query: OnUpdateNetworkParametersDocument,
|
||||
},
|
||||
result: {
|
||||
data: {
|
||||
__typename: 'Subscription',
|
||||
busEvents: [
|
||||
{
|
||||
__typename: 'BusEvent',
|
||||
event: generateUpdateNetworkParametersProposal('abc.def', '123.456'),
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const INITIAL = useToasts.getState();
|
||||
|
||||
const clear = () => {
|
||||
@@ -102,23 +55,113 @@ describe('useUpdateNetworkParametersToasts', () => {
|
||||
beforeEach(clear);
|
||||
afterAll(clear);
|
||||
|
||||
it('returns toast for update network parameters bus event', async () => {
|
||||
render([mockedEvent]);
|
||||
await waitFor(() => {
|
||||
expect(useToasts.getState().count).toBe(1);
|
||||
});
|
||||
});
|
||||
it.each(PROPOSAL_STATES_TO_TOAST)(
|
||||
'toasts for %s network param proposals',
|
||||
async (state) => {
|
||||
const mockOpenProposal: MockedResponse<OnUpdateNetworkParametersSubscription> =
|
||||
{
|
||||
request: {
|
||||
query: OnUpdateNetworkParametersDocument,
|
||||
},
|
||||
result: {
|
||||
data: {
|
||||
proposals: generateUpdateNetworkParametersProposal(
|
||||
'abc.def',
|
||||
'123.456',
|
||||
state
|
||||
),
|
||||
},
|
||||
},
|
||||
};
|
||||
const { result } = render([mockOpenProposal]);
|
||||
expect(result.current.loading).toBe(true);
|
||||
await waitFor(() => {
|
||||
expect(result.current.loading).toBe(false);
|
||||
expect(useToasts.getState().count).toBe(1);
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
it('does not return toast for empty event', async () => {
|
||||
render([mockedEmptyEvent]);
|
||||
const IGNORE_STATES = Object.keys(ProposalState).filter((state) => {
|
||||
return !PROPOSAL_STATES_TO_TOAST.includes(state as ProposalState);
|
||||
}) as ProposalState[];
|
||||
it.each(IGNORE_STATES)('does not toast for %s proposals', async (state) => {
|
||||
const mockFailedProposal: MockedResponse<OnUpdateNetworkParametersSubscription> =
|
||||
{
|
||||
request: {
|
||||
query: OnUpdateNetworkParametersDocument,
|
||||
},
|
||||
result: {
|
||||
data: {
|
||||
proposals: generateUpdateNetworkParametersProposal(
|
||||
'abc.def',
|
||||
'123.456',
|
||||
state
|
||||
),
|
||||
},
|
||||
},
|
||||
};
|
||||
const { result } = render([mockFailedProposal]);
|
||||
expect(result.current.loading).toBe(true);
|
||||
await waitFor(() => {
|
||||
expect(result.current.loading).toBe(false);
|
||||
expect(useToasts.getState().count).toBe(0);
|
||||
});
|
||||
});
|
||||
|
||||
it('does not return toast for wrong event', async () => {
|
||||
render([mockedWrongEvent]);
|
||||
it('does not return toast for empty propsal', async () => {
|
||||
const error = console.error;
|
||||
console.error = () => {
|
||||
/* no op */
|
||||
};
|
||||
const mockEmptyProposal: MockedResponse<OnUpdateNetworkParametersSubscription> =
|
||||
{
|
||||
request: {
|
||||
query: OnUpdateNetworkParametersDocument,
|
||||
},
|
||||
result: {
|
||||
data: {
|
||||
proposals:
|
||||
undefined as unknown as UpdateNetworkParameterProposalFragment,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const { result } = render([mockEmptyProposal]);
|
||||
expect(result.current.loading).toBe(true);
|
||||
await waitFor(() => {
|
||||
expect(result.current.loading).toBe(false);
|
||||
expect(useToasts.getState().count).toBe(0);
|
||||
});
|
||||
console.error = error;
|
||||
});
|
||||
|
||||
it('does not return toast for wrong proposal type', async () => {
|
||||
const wrongProposalType = merge(
|
||||
generateUpdateNetworkParametersProposal('a', 'b'),
|
||||
{
|
||||
terms: {
|
||||
change: {
|
||||
__typename: 'NewMarket',
|
||||
},
|
||||
},
|
||||
}
|
||||
);
|
||||
const mockWrongProposalType: MockedResponse<OnUpdateNetworkParametersSubscription> =
|
||||
{
|
||||
request: {
|
||||
query: OnUpdateNetworkParametersDocument,
|
||||
},
|
||||
result: {
|
||||
data: {
|
||||
proposals: wrongProposalType,
|
||||
},
|
||||
},
|
||||
};
|
||||
const { result } = render([mockWrongProposalType]);
|
||||
expect(result.current.loading).toBe(true);
|
||||
await waitFor(() => {
|
||||
expect(result.current.loading).toBe(false);
|
||||
expect(useToasts.getState().count).toBe(0);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 () => {
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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}
|
||||
|
||||
Generated
+67
-53
@@ -344,58 +344,12 @@ export type BusEvent = {
|
||||
|
||||
/** Event types */
|
||||
export enum BusEventType {
|
||||
/** An account has been updated */
|
||||
Account = 'Account',
|
||||
/** An asset has been created or update */
|
||||
Asset = 'Asset',
|
||||
/** A market has either entered or exited auction */
|
||||
Auction = 'Auction',
|
||||
/** Collateral has deposited in to this Vega network via the bridge */
|
||||
Deposit = 'Deposit',
|
||||
/** A liquidity commitment change occurred */
|
||||
LiquidityProvision = 'LiquidityProvision',
|
||||
/** A position has been closed without sufficient insurance pool balance to cover it */
|
||||
LossSocialization = 'LossSocialization',
|
||||
/** Margin levels have changed for a position */
|
||||
MarginLevels = 'MarginLevels',
|
||||
/** Constant for market events - mainly used for logging */
|
||||
Market = 'Market',
|
||||
/** A new market has been created */
|
||||
MarketCreated = 'MarketCreated',
|
||||
/** Market data has been updated */
|
||||
MarketData = 'MarketData',
|
||||
/** A market has progressed by one tick */
|
||||
MarketTick = 'MarketTick',
|
||||
/** A market has been updated */
|
||||
MarketUpdated = 'MarketUpdated',
|
||||
/** Validator node signatures for an event */
|
||||
NodeSignature = 'NodeSignature',
|
||||
/** An oracle spec has been registered */
|
||||
OracleSpec = 'OracleSpec',
|
||||
/** An order has been created or updated */
|
||||
Order = 'Order',
|
||||
/** A party has been updated */
|
||||
Party = 'Party',
|
||||
/** A position resolution event has occurred */
|
||||
PositionResolution = 'PositionResolution',
|
||||
/** A governance proposal has been created or updated */
|
||||
Proposal = 'Proposal',
|
||||
/** A risk factor adjustment was made */
|
||||
RiskFactor = 'RiskFactor',
|
||||
/** A distressed position has been settled */
|
||||
SettleDistressed = 'SettleDistressed',
|
||||
/** A position has been settled */
|
||||
SettlePosition = 'SettlePosition',
|
||||
/** Vega Time has changed */
|
||||
TimeUpdate = 'TimeUpdate',
|
||||
/** A trade has been created */
|
||||
Trade = 'Trade',
|
||||
/** The results from processing at transaction */
|
||||
TransactionResult = 'TransactionResult',
|
||||
/** A balance has been transferred between accounts */
|
||||
TransferResponses = 'TransferResponses',
|
||||
/** A vote has been placed on a governance proposal */
|
||||
Vote = 'Vote',
|
||||
/** Collateral has been withdrawn from this Vega network via the bridge */
|
||||
Withdrawal = 'Withdrawal'
|
||||
}
|
||||
@@ -1014,7 +968,7 @@ export type EthereumKeyRotationsConnection = {
|
||||
};
|
||||
|
||||
/** Union type for wrapped events in stream PROPOSAL is mapped to governance data, something to keep in mind */
|
||||
export type Event = AccountEvent | Asset | AuctionEvent | Deposit | LiquidityProvision | LossSocialization | MarginLevels | Market | MarketData | MarketEvent | MarketTick | NodeSignature | OracleSpec | Order | Party | PositionResolution | Proposal | RiskFactor | SettleDistressed | SettlePosition | TimeUpdate | Trade | TransactionResult | TransferResponses | Vote | Withdrawal;
|
||||
export type Event = Deposit | TimeUpdate | TransactionResult | Withdrawal;
|
||||
|
||||
export type ExternalData = {
|
||||
__typename?: 'ExternalData';
|
||||
@@ -1100,8 +1054,6 @@ export type FutureProduct = {
|
||||
/** A segment of data node history */
|
||||
export type HistorySegment = {
|
||||
__typename?: 'HistorySegment';
|
||||
/** Chain ID of the history segment */
|
||||
chainID: Scalars['String'];
|
||||
/** From block height of the history segment */
|
||||
fromHeight: Scalars['Int'];
|
||||
/** ID of the history segment */
|
||||
@@ -1225,6 +1177,8 @@ export type LedgerEntryFilter = {
|
||||
/** Configuration of a market liquidity monitoring parameters */
|
||||
export type LiquidityMonitoringParameters = {
|
||||
__typename?: 'LiquidityMonitoringParameters';
|
||||
/** Specifies by how many seconds an auction should be extended if leaving the auction were to trigger a liquidity auction */
|
||||
auctionExtensionSecs: Scalars['Int'];
|
||||
/** Specifies parameters related to target stake calculation */
|
||||
targetStakeParameters: TargetStakeParameters;
|
||||
/** Specifies the triggering ratio for entering liquidity auction */
|
||||
@@ -1489,6 +1443,8 @@ export type Market = {
|
||||
fees: Fees;
|
||||
/** Market ID */
|
||||
id: Scalars['ID'];
|
||||
/** Linear slippage factor is used to cap the slippage component of maintainence margin - it is applied to the slippage volume */
|
||||
linearSlippageFactor: Scalars['String'];
|
||||
/** Liquidity monitoring parameters for the market */
|
||||
liquidityMonitoringParameters: LiquidityMonitoringParameters;
|
||||
/** The list of the liquidity provision commitments for this market */
|
||||
@@ -1515,6 +1471,8 @@ export type Market = {
|
||||
priceMonitoringSettings: PriceMonitoringSettings;
|
||||
/** The proposal that initiated this market */
|
||||
proposal?: Maybe<Proposal>;
|
||||
/** Quadratic slippage factor is used to cap the slippage component of maintainence margin - it is applied to the square of the slippage volume */
|
||||
quadraticSlippageFactor: Scalars['String'];
|
||||
/** Risk factors for the market */
|
||||
riskFactors?: Maybe<RiskFactor>;
|
||||
/** Current state of the market */
|
||||
@@ -1870,10 +1828,20 @@ export type NewMarket = {
|
||||
decimalPlaces: Scalars['Int'];
|
||||
/** New market instrument configuration */
|
||||
instrument: InstrumentConfiguration;
|
||||
/** Linear slippage factor is used to cap the slippage component of maintainence margin - it is applied to the slippage volume */
|
||||
linearSlippageFactor: Scalars['String'];
|
||||
/** Liquidity monitoring parameters */
|
||||
liquidityMonitoringParameters: LiquidityMonitoringParameters;
|
||||
/** Liquidity Provision order price range */
|
||||
lpPriceRange: Scalars['String'];
|
||||
/** Metadata for this instrument, tags */
|
||||
metadata?: Maybe<Array<Scalars['String']>>;
|
||||
/** Decimal places for order sizes, sets what size the smallest order / position on the market can be */
|
||||
positionDecimalPlaces: Scalars['Int'];
|
||||
/** Price monitoring parameters */
|
||||
priceMonitoringParameters: PriceMonitoringParameters;
|
||||
/** Quadratic slippage factor is used to cap the slippage component of maintainence margin - it is applied to the square of the slippage volume */
|
||||
quadraticSlippageFactor: Scalars['String'];
|
||||
/** New market risk configuration */
|
||||
riskParameters: RiskModel;
|
||||
};
|
||||
@@ -2519,7 +2487,10 @@ export type Party = {
|
||||
marginsConnection?: Maybe<MarginConnection>;
|
||||
/** Orders relating to a party */
|
||||
ordersConnection?: Maybe<OrderConnection>;
|
||||
/** Trading positions relating to a party */
|
||||
/**
|
||||
* Trading positions relating to a party
|
||||
* @deprecated Use root positions query instead of sub-query
|
||||
*/
|
||||
positionsConnection?: Maybe<PositionConnection>;
|
||||
/** All governance proposals in the Vega network */
|
||||
proposalsConnection?: Maybe<ProposalsConnection>;
|
||||
@@ -2610,7 +2581,9 @@ export type PartyrewardSummariesArgs = {
|
||||
/** Represents a party on Vega, could be an ethereum wallet address in the future */
|
||||
export type PartyrewardsConnectionArgs = {
|
||||
assetId?: InputMaybe<Scalars['ID']>;
|
||||
fromEpoch?: InputMaybe<Scalars['Int']>;
|
||||
pagination?: InputMaybe<Pagination>;
|
||||
toEpoch?: InputMaybe<Scalars['Int']>;
|
||||
};
|
||||
|
||||
|
||||
@@ -2707,6 +2680,8 @@ export type Position = {
|
||||
__typename?: 'Position';
|
||||
/** Average entry price for this position */
|
||||
averageEntryPrice: Scalars['String'];
|
||||
/** The total amount of profit and loss that was not transferred due to loss socialisation */
|
||||
lossSocializationAmount: Scalars['String'];
|
||||
/** Margins of the party for the given position */
|
||||
marginsConnection?: Maybe<MarginConnection>;
|
||||
/** Market relating to this position */
|
||||
@@ -2715,6 +2690,8 @@ export type Position = {
|
||||
openVolume: Scalars['String'];
|
||||
/** The party holding this position */
|
||||
party: Party;
|
||||
/** Enum set if the position was closed out or orders were removed because party was distressed */
|
||||
positionStatus: PositionStatus;
|
||||
/** Realised Profit and Loss (int64) */
|
||||
realisedPNL: Scalars['String'];
|
||||
/** Unrealised Profit and Loss (int64) */
|
||||
@@ -2764,6 +2741,16 @@ export type PositionResolution = {
|
||||
marketId: Scalars['ID'];
|
||||
};
|
||||
|
||||
/** Position status can change if a position is distressed */
|
||||
export enum PositionStatus {
|
||||
/** The position was distressed, and had to be closed out entirely - orders were removed from the book, and the open volume was closed out by the network */
|
||||
POSITION_STATUS_CLOSED_OUT = 'POSITION_STATUS_CLOSED_OUT',
|
||||
/** The position was distressed, but removing open orders from the book brought the margin level back to a point where the open position could be maintained */
|
||||
POSITION_STATUS_ORDERS_CLOSED = 'POSITION_STATUS_ORDERS_CLOSED',
|
||||
/** The position is either healthy, or if closed out, was closed out normally */
|
||||
POSITION_STATUS_UNSPECIFIED = 'POSITION_STATUS_UNSPECIFIED'
|
||||
}
|
||||
|
||||
/**
|
||||
* An individual party at any point in time is considered net long or net short. This refers to their Open Volume,
|
||||
* calculated using FIFO. This volume is signed as either negative for LONG positions and positive for SHORT positions. A
|
||||
@@ -2788,6 +2775,12 @@ export type PositionUpdate = {
|
||||
updatedAt?: Maybe<Scalars['Timestamp']>;
|
||||
};
|
||||
|
||||
/** Filter to apply to the positions connection query */
|
||||
export type PositionsFilter = {
|
||||
marketIds?: InputMaybe<Array<Scalars['ID']>>;
|
||||
partyIds?: InputMaybe<Array<Scalars['ID']>>;
|
||||
};
|
||||
|
||||
/** Represents a price on either the buy or sell side and all the orders at that price */
|
||||
export type PriceLevel = {
|
||||
__typename?: 'PriceLevel';
|
||||
@@ -2826,7 +2819,7 @@ export type PriceMonitoringSettings = {
|
||||
parameters?: Maybe<PriceMonitoringParameters>;
|
||||
};
|
||||
|
||||
/** PriceMonitoringParameters holds together price projection horizon τ, probability level p, and auction extension duration */
|
||||
/** PriceMonitoringTrigger holds together price projection horizon τ, probability level p, and auction extension duration */
|
||||
export type PriceMonitoringTrigger = {
|
||||
__typename?: 'PriceMonitoringTrigger';
|
||||
/**
|
||||
@@ -3281,6 +3274,8 @@ export type Query = {
|
||||
partiesConnection?: Maybe<PartyConnection>;
|
||||
/** An entity that is trading on the Vega network */
|
||||
party?: Maybe<Party>;
|
||||
/** Fetch all positions */
|
||||
positions?: Maybe<PositionConnection>;
|
||||
/** A governance proposal located by either its ID or reference. If both are set, ID is used. */
|
||||
proposal?: Maybe<Proposal>;
|
||||
/** All governance proposals in the Vega network */
|
||||
@@ -3348,9 +3343,8 @@ export type QueryepochArgs = {
|
||||
|
||||
/** Queries allow a caller to read data and filter data via GraphQL. */
|
||||
export type QueryepochRewardSummariesArgs = {
|
||||
fromEpoch?: InputMaybe<Scalars['Int']>;
|
||||
filter?: InputMaybe<RewardSummaryFilter>;
|
||||
pagination?: InputMaybe<Pagination>;
|
||||
toEpoch?: InputMaybe<Scalars['Int']>;
|
||||
};
|
||||
|
||||
|
||||
@@ -3453,6 +3447,7 @@ export type QuerymarketArgs = {
|
||||
/** Queries allow a caller to read data and filter data via GraphQL. */
|
||||
export type QuerymarketsConnectionArgs = {
|
||||
id?: InputMaybe<Scalars['ID']>;
|
||||
includeSettled?: InputMaybe<Scalars['Boolean']>;
|
||||
pagination?: InputMaybe<Pagination>;
|
||||
};
|
||||
|
||||
@@ -3546,6 +3541,13 @@ export type QuerypartyArgs = {
|
||||
};
|
||||
|
||||
|
||||
/** Queries allow a caller to read data and filter data via GraphQL. */
|
||||
export type QuerypositionsArgs = {
|
||||
filter?: InputMaybe<PositionsFilter>;
|
||||
pagination?: InputMaybe<Pagination>;
|
||||
};
|
||||
|
||||
|
||||
/** Queries allow a caller to read data and filter data via GraphQL. */
|
||||
export type QueryproposalArgs = {
|
||||
id?: InputMaybe<Scalars['ID']>;
|
||||
@@ -3698,6 +3700,14 @@ export type RewardSummaryEdge = {
|
||||
node: RewardSummary;
|
||||
};
|
||||
|
||||
/** Filter for historical reward summary queries */
|
||||
export type RewardSummaryFilter = {
|
||||
assetIds?: InputMaybe<Array<Scalars['ID']>>;
|
||||
fromEpoch?: InputMaybe<Scalars['Int']>;
|
||||
marketIds?: InputMaybe<Array<Scalars['ID']>>;
|
||||
toEpoch?: InputMaybe<Scalars['Int']>;
|
||||
};
|
||||
|
||||
/** Connection type for retrieving cursor-based paginated rewards information */
|
||||
export type RewardsConnection = {
|
||||
__typename?: 'RewardsConnection';
|
||||
@@ -3894,6 +3904,10 @@ export type Statistics = {
|
||||
chainVersion: Scalars['String'];
|
||||
/** RFC3339Nano current time (real) */
|
||||
currentTime: Scalars['Timestamp'];
|
||||
/** Total number of events on the last block */
|
||||
eventCount: Scalars['String'];
|
||||
/** The number of events per second on the last block */
|
||||
eventsPerSecond: Scalars['String'];
|
||||
/** RFC3339Nano genesis time of the chain */
|
||||
genesisTime: Scalars['Timestamp'];
|
||||
/** Number of orders per seconds */
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import { renderHook, act } from '@testing-library/react-hooks';
|
||||
import { renderHook, waitFor } from '@testing-library/react';
|
||||
import type { MockedResponse } from '@apollo/client/testing';
|
||||
import { MockedProvider } from '@apollo/client/testing';
|
||||
import type { ReactNode } from 'react';
|
||||
import { useVegaTransactionUpdater } from './use-vega-transaction-updater';
|
||||
import waitForNextTick from 'flush-promises';
|
||||
import {
|
||||
OrderTxUpdateDocument,
|
||||
TransactionEventDocument,
|
||||
@@ -112,14 +111,13 @@ const mockedTransactionResultBusEvent: MockedResponse<TransactionEventSubscripti
|
||||
data: {
|
||||
busEvents: [
|
||||
{
|
||||
type: BusEventType.Order,
|
||||
type: BusEventType.TransactionResult,
|
||||
event: transactionResultBusEvent,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const withdrawalBusEvent: WithdrawalBusEventFieldsFragment = {
|
||||
id: '2fca514cebf9f465ae31ecb4c5721e3a6f5f260425ded887ca50ba15b81a5d50',
|
||||
status: WithdrawalStatus.STATUS_OPEN,
|
||||
@@ -167,20 +165,16 @@ const mockedWithdrawalBusEvent: MockedResponse<WithdrawalBusEventSubscription> =
|
||||
describe('useVegaTransactionManager', () => {
|
||||
it('updates order on OrderTxUpdate', async () => {
|
||||
mockTransactionStoreState.mockReturnValue(defaultState);
|
||||
const { waitForNextUpdate } = render([mockedOrderUpdate]);
|
||||
await act(async () => {
|
||||
waitForNextUpdate();
|
||||
await waitForNextTick();
|
||||
render([mockedOrderUpdate]);
|
||||
await waitFor(() => {
|
||||
expect(updateOrder).toHaveBeenCalledWith(orderUpdate);
|
||||
});
|
||||
});
|
||||
|
||||
it('updates transaction on TransactionResultBusEvents', async () => {
|
||||
mockTransactionStoreState.mockReturnValue(defaultState);
|
||||
const { waitForNextUpdate } = render([mockedTransactionResultBusEvent]);
|
||||
await act(async () => {
|
||||
waitForNextUpdate();
|
||||
await waitForNextTick();
|
||||
render([mockedTransactionResultBusEvent]);
|
||||
await waitFor(() => {
|
||||
expect(updateTransactionResult).toHaveBeenCalledWith(
|
||||
transactionResultBusEvent
|
||||
);
|
||||
@@ -193,10 +187,8 @@ describe('useVegaTransactionManager', () => {
|
||||
mockWaitForWithdrawalApproval.mockResolvedValueOnce(
|
||||
erc20WithdrawalApproval
|
||||
);
|
||||
const { waitForNextUpdate } = render([mockedWithdrawalBusEvent]);
|
||||
await act(async () => {
|
||||
waitForNextUpdate();
|
||||
await waitForNextTick();
|
||||
render([mockedWithdrawalBusEvent]);
|
||||
await waitFor(() => {
|
||||
expect(updateWithdrawal).toHaveBeenCalledWith(
|
||||
withdrawalBusEvent,
|
||||
erc20WithdrawalApproval
|
||||
|
||||
@@ -38,9 +38,9 @@ export const useVegaTransactionUpdater = () => {
|
||||
result.data?.busEvents?.forEach((event) => {
|
||||
if (event.event.__typename === 'Withdrawal') {
|
||||
const withdrawal = event.event;
|
||||
waitForWithdrawalApproval(withdrawal.id, client).then((approval) =>
|
||||
updateWithdrawal(withdrawal, approval)
|
||||
);
|
||||
waitForWithdrawalApproval(withdrawal.id, client).then((approval) => {
|
||||
updateWithdrawal(withdrawal, approval);
|
||||
});
|
||||
}
|
||||
}),
|
||||
});
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import { renderHook } from '@testing-library/react-hooks';
|
||||
import { renderHook, waitFor } from '@testing-library/react';
|
||||
import type { MockedResponse } from '@apollo/client/testing';
|
||||
import { MockedProvider } from '@apollo/client/testing';
|
||||
import type { ReactNode } from 'react';
|
||||
import { useEthTransactionUpdater } from './use-ethereum-transaction-updater';
|
||||
import waitForNextTick from 'flush-promises';
|
||||
import {
|
||||
DepositBusEventDocument,
|
||||
VegaWalletContext,
|
||||
@@ -77,9 +76,9 @@ const mockedDepositBusEvent: MockedResponse<DepositBusEventSubscription> = {
|
||||
describe('useEthTransactionUpdater', () => {
|
||||
it('updates deposit on DepositBusEvents', async () => {
|
||||
mockTransactionStoreState.mockReturnValue(defaultState);
|
||||
const { waitForNextUpdate } = render([mockedDepositBusEvent]);
|
||||
waitForNextUpdate();
|
||||
await waitForNextTick();
|
||||
expect(updateDeposit).toHaveBeenCalledWith(depositBusEvent);
|
||||
render([mockedDepositBusEvent]);
|
||||
await waitFor(() => {
|
||||
expect(updateDeposit).toHaveBeenCalledWith(depositBusEvent);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -128,7 +128,6 @@
|
||||
"@svgr/webpack": "^5.4.0",
|
||||
"@testing-library/jest-dom": "^5.16.2",
|
||||
"@testing-library/react": "13.3.0",
|
||||
"@testing-library/react-hooks": "^8.0.1",
|
||||
"@testing-library/user-event": "^14.4.1",
|
||||
"@types/classnames": "^2.3.1",
|
||||
"@types/faker": "^5.5.8",
|
||||
|
||||
@@ -6364,14 +6364,6 @@
|
||||
lodash "^4.17.15"
|
||||
redent "^3.0.0"
|
||||
|
||||
"@testing-library/react-hooks@^8.0.1":
|
||||
version "8.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@testing-library/react-hooks/-/react-hooks-8.0.1.tgz#0924bbd5b55e0c0c0502d1754657ada66947ca12"
|
||||
integrity sha512-Aqhl2IVmLt8IovEVarNDFuJDVWVvhnr9/GCU6UUnrYXwgDFF9h2L2o2P9KBni1AST5sT6riAyoukFLyjQUgD/g==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.12.5"
|
||||
react-error-boundary "^3.1.0"
|
||||
|
||||
"@testing-library/react@13.3.0":
|
||||
version "13.3.0"
|
||||
resolved "https://registry.yarnpkg.com/@testing-library/react/-/react-13.3.0.tgz#bf298bfbc5589326bbcc8052b211f3bb097a97c5"
|
||||
@@ -19130,13 +19122,6 @@ react-element-to-jsx-string@^14.3.4:
|
||||
is-plain-object "5.0.0"
|
||||
react-is "17.0.2"
|
||||
|
||||
react-error-boundary@^3.1.0:
|
||||
version "3.1.4"
|
||||
resolved "https://registry.yarnpkg.com/react-error-boundary/-/react-error-boundary-3.1.4.tgz#255db92b23197108757a888b01e5b729919abde0"
|
||||
integrity sha512-uM9uPzZJTF6wRQORmSrvOIgt4lJ9MC1sNgEOj2XGsDTRE4kmpWxg7ENK9EWNKJRMAOY9z0MuF4yIfl6gp4sotA==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.12.5"
|
||||
|
||||
react-hook-form@^7.27.0:
|
||||
version "7.37.0"
|
||||
resolved "https://registry.yarnpkg.com/react-hook-form/-/react-hook-form-7.37.0.tgz#4d1738f092d3d8a3ade34ee892d97350b1032b19"
|
||||
|
||||
Reference in New Issue
Block a user