Compare commits
14
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b9908003ba | ||
|
|
1f9af79215 | ||
|
|
e31e40a7ef | ||
|
|
81f6b3cc15 | ||
|
|
8adb208a91 | ||
|
|
545c3a17d2 | ||
|
|
ad76988f63 | ||
|
|
d2bbd7e1fc | ||
|
|
fb1ebb3bdf | ||
|
|
b2489537ef | ||
|
|
382a7ea411 | ||
|
|
1ae2b4c153 | ||
|
|
e8875e817e | ||
|
|
4a01c880f3 |
@@ -8,6 +8,7 @@ import { Main } from './components/main';
|
||||
import { TendermintWebsocketProvider } from './contexts/websocket/tendermint-websocket-provider';
|
||||
import type { InMemoryCacheConfig } from '@apollo/client';
|
||||
import { Footer } from './components/footer/footer';
|
||||
import { AnnouncementBanner, ExternalLink } from '@vegaprotocol/ui-toolkit';
|
||||
|
||||
function App() {
|
||||
const [menuOpen, setMenuOpen] = useState(false);
|
||||
@@ -40,6 +41,15 @@ function App() {
|
||||
return (
|
||||
<TendermintWebsocketProvider>
|
||||
<NetworkLoader cache={cacheConfig}>
|
||||
<AnnouncementBanner>
|
||||
<div className="font-alpha calt uppercase text-center text-lg text-white">
|
||||
<span className="pr-4">The Mainnet sims are live!</span>
|
||||
<ExternalLink href="https://fairground.wtf/">
|
||||
Come help stress test the network
|
||||
</ExternalLink>
|
||||
</div>
|
||||
</AnnouncementBanner>
|
||||
|
||||
<div className={layoutClasses}>
|
||||
<Header menuOpen={menuOpen} setMenuOpen={setMenuOpen} />
|
||||
<Nav menuOpen={menuOpen} />
|
||||
|
||||
@@ -71,7 +71,7 @@
|
||||
"tranche_end": "2023-12-05T00:00:00.000Z",
|
||||
"total_added": "86666.297",
|
||||
"total_removed": "0",
|
||||
"locked_amount": "72952.7900446204316134035",
|
||||
"locked_amount": "72537.154027859368347703",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "86666.297",
|
||||
@@ -137,7 +137,7 @@
|
||||
"tranche_end": "2023-06-01T00:00:00.000Z",
|
||||
"total_added": "2500",
|
||||
"total_removed": "0",
|
||||
"locked_amount": "1651.7141712454215",
|
||||
"locked_amount": "1627.66919134106625",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "2500",
|
||||
@@ -485,7 +485,7 @@
|
||||
"tranche_end": "2023-08-01T00:00:00.000Z",
|
||||
"total_added": "37500",
|
||||
"total_removed": "0",
|
||||
"locked_amount": "37500",
|
||||
"locked_amount": "37188.0491290669125",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "7500",
|
||||
@@ -538,7 +538,7 @@
|
||||
"tranche_end": "2023-12-05T00:00:00.000Z",
|
||||
"total_added": "129999.45",
|
||||
"total_removed": "0",
|
||||
"locked_amount": "72886.229802976735885635",
|
||||
"locked_amount": "72470.97300178364112351",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "129999.45",
|
||||
@@ -604,7 +604,7 @@
|
||||
"tranche_end": "2023-09-03T00:00:00.000Z",
|
||||
"total_added": "62600",
|
||||
"total_removed": "0",
|
||||
"locked_amount": "36744.44920091324704",
|
||||
"locked_amount": "36444.23082825976788",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "10000",
|
||||
@@ -797,7 +797,7 @@
|
||||
"tranche_end": "2023-09-17T00:00:00.000Z",
|
||||
"total_added": "5000",
|
||||
"total_removed": "0",
|
||||
"locked_amount": "3126.64098173516",
|
||||
"locked_amount": "3102.661878488077",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "5000",
|
||||
@@ -1008,7 +1008,7 @@
|
||||
"tranche_end": "2023-04-05T00:00:00.000Z",
|
||||
"total_added": "97499.58",
|
||||
"total_removed": "0",
|
||||
"locked_amount": "14716.8034002088306102026",
|
||||
"locked_amount": "14309.4751611591714224046",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "97499.58",
|
||||
@@ -1041,7 +1041,7 @@
|
||||
"tranche_end": "2023-04-05T00:00:00.000Z",
|
||||
"total_added": "135173.4239508",
|
||||
"total_removed": "98230.390980249184455396",
|
||||
"locked_amount": "20115.329497972601377405487628",
|
||||
"locked_amount": "19558.58211747851579304108696",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "135173.4239508",
|
||||
@@ -1087,7 +1087,7 @@
|
||||
"tranche_end": "2023-04-05T00:00:00.000Z",
|
||||
"total_added": "32499.86",
|
||||
"total_removed": "0",
|
||||
"locked_amount": "6191.105044866968902606",
|
||||
"locked_amount": "6019.749088881220610994",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "32499.86",
|
||||
@@ -1120,7 +1120,7 @@
|
||||
"tranche_end": "2023-04-05T00:00:00.000Z",
|
||||
"total_added": "10833.29",
|
||||
"total_removed": "0",
|
||||
"locked_amount": "2015.1446150428923522868",
|
||||
"locked_amount": "1959.369914168368365833",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "10833.29",
|
||||
@@ -1153,7 +1153,7 @@
|
||||
"tranche_end": "2023-04-05T00:00:00.000Z",
|
||||
"total_added": "22749.93",
|
||||
"total_removed": "0",
|
||||
"locked_amount": "7533.0606454515702272178",
|
||||
"locked_amount": "7324.562356528141179063",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "6500",
|
||||
@@ -1291,8 +1291,8 @@
|
||||
"tranche_start": "2022-11-01T00:00:00.000Z",
|
||||
"tranche_end": "2023-05-01T00:00:00.000Z",
|
||||
"total_added": "22500",
|
||||
"total_removed": "3707.308452225",
|
||||
"locked_amount": "11093.96581491712725",
|
||||
"total_removed": "3853.26264195",
|
||||
"locked_amount": "10876.365389042358075",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "7500",
|
||||
@@ -1311,6 +1311,11 @@
|
||||
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
|
||||
"tx": "0x3bd3579f34ddc1eee597ba9b3fbbf18b6c268d085cd299e964b7239b2434fcf3"
|
||||
},
|
||||
{
|
||||
"amount": "145.954189725",
|
||||
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
|
||||
"tx": "0x702e76a6868be327dcce3416fa163759a406b68600063473106afdb555f03db4"
|
||||
},
|
||||
{
|
||||
"amount": "305.3119245",
|
||||
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
|
||||
@@ -1380,6 +1385,12 @@
|
||||
"tranche_id": 33,
|
||||
"tx": "0x3bd3579f34ddc1eee597ba9b3fbbf18b6c268d085cd299e964b7239b2434fcf3"
|
||||
},
|
||||
{
|
||||
"amount": "145.954189725",
|
||||
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
|
||||
"tranche_id": 33,
|
||||
"tx": "0x702e76a6868be327dcce3416fa163759a406b68600063473106afdb555f03db4"
|
||||
},
|
||||
{
|
||||
"amount": "305.3119245",
|
||||
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
|
||||
@@ -1442,8 +1453,8 @@
|
||||
}
|
||||
],
|
||||
"total_tokens": "7500",
|
||||
"withdrawn_tokens": "3707.308452225",
|
||||
"remaining_tokens": "3792.691547775"
|
||||
"withdrawn_tokens": "3853.26264195",
|
||||
"remaining_tokens": "3646.73735805"
|
||||
},
|
||||
{
|
||||
"address": "0x2539b51EbDE65a75672aBcfE9439a706a99D18D1",
|
||||
@@ -1468,7 +1479,7 @@
|
||||
"tranche_end": "2023-06-02T00:00:00.000Z",
|
||||
"total_added": "1939928.38",
|
||||
"total_removed": "928642.9598472029154",
|
||||
"locked_amount": "644400.581592751114092338",
|
||||
"locked_amount": "635097.0330095579571042166",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "1852091.69",
|
||||
@@ -1820,7 +1831,7 @@
|
||||
"tranche_end": "2023-02-01T00:00:00.000Z",
|
||||
"total_added": "42500",
|
||||
"total_removed": "24434.0787288",
|
||||
"locked_amount": "56.5415534420304905",
|
||||
"locked_amount": "0",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "12500",
|
||||
@@ -33429,7 +33440,7 @@
|
||||
"tranche_end": "2023-06-05T00:00:00.000Z",
|
||||
"total_added": "3732368.4671",
|
||||
"total_removed": "589730.8667090699299",
|
||||
"locked_amount": "1014720.66218397269192838495",
|
||||
"locked_amount": "1000424.34716318809300120353",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "1998.95815",
|
||||
@@ -34743,8 +34754,8 @@
|
||||
"tranche_start": "2022-06-05T00:00:00.000Z",
|
||||
"tranche_end": "2023-12-05T00:00:00.000Z",
|
||||
"total_added": "15870102.715470999700000001",
|
||||
"total_removed": "551396.87737862765567952",
|
||||
"locked_amount": "8897821.9024516211694110022588123882238443",
|
||||
"total_removed": "553068.12935216697830452",
|
||||
"locked_amount": "8847128.0872990775682462500282478416234118",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "16249.93",
|
||||
@@ -35273,6 +35284,16 @@
|
||||
"user": "0xE6CacAE56Cca8dFdB7910b5A13578719D4E57DA0",
|
||||
"tx": "0xeb5f2401c9402d58fa4a7549ce2045a48a2c3b90ec6e812ca2f0059f1275e213"
|
||||
},
|
||||
{
|
||||
"amount": "1102.899782241316125",
|
||||
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
|
||||
"tx": "0x6a0a62bebbd8dbc54bb67ab842f6fc121d8ca4fab087e2779a089007ce6b0b20"
|
||||
},
|
||||
{
|
||||
"amount": "568.3521912980065",
|
||||
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
|
||||
"tx": "0x374feedb0a834a85c280bc2d0d021b727c4d7b303ac035bca381e1f1e7cd662e"
|
||||
},
|
||||
{
|
||||
"amount": "858.360074993579125",
|
||||
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
|
||||
@@ -36862,6 +36883,18 @@
|
||||
"tranche_id": 2,
|
||||
"tx": "0x6c49f9f742a84f7889b90e6f978f3fb1f642ea447f737f348b3fac91716b9717"
|
||||
},
|
||||
{
|
||||
"amount": "1102.899782241316125",
|
||||
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
|
||||
"tranche_id": 2,
|
||||
"tx": "0x6a0a62bebbd8dbc54bb67ab842f6fc121d8ca4fab087e2779a089007ce6b0b20"
|
||||
},
|
||||
{
|
||||
"amount": "568.3521912980065",
|
||||
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
|
||||
"tranche_id": 2,
|
||||
"tx": "0x374feedb0a834a85c280bc2d0d021b727c4d7b303ac035bca381e1f1e7cd662e"
|
||||
},
|
||||
{
|
||||
"amount": "858.360074993579125",
|
||||
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
|
||||
@@ -37950,8 +37983,8 @@
|
||||
}
|
||||
],
|
||||
"total_tokens": "259998.8875",
|
||||
"withdrawn_tokens": "113142.013429259527625",
|
||||
"remaining_tokens": "146856.874070740472375"
|
||||
"withdrawn_tokens": "114813.26540279885025",
|
||||
"remaining_tokens": "145185.62209720114975"
|
||||
},
|
||||
{
|
||||
"address": "0x89051CAb67Bc7F8CC44F7e270c6EDaf1EC57676c",
|
||||
@@ -40122,8 +40155,8 @@
|
||||
"tranche_start": "2021-11-05T00:00:00.000Z",
|
||||
"tranche_end": "2023-05-05T00:00:00.000Z",
|
||||
"total_added": "14597706.0446472999",
|
||||
"total_removed": "3709441.39326687814680893",
|
||||
"locked_amount": "2492966.831482503696801792718375116",
|
||||
"total_removed": "3711762.46803043411726343",
|
||||
"locked_amount": "2446166.93445800685782748312503739",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "129284.449",
|
||||
@@ -40337,6 +40370,16 @@
|
||||
"user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b",
|
||||
"tx": "0xc8aae35d9d474b83dd5524de9db59f994fd77040397c00a1a4cf30a5c9315826"
|
||||
},
|
||||
{
|
||||
"amount": "1535.17244264446063625",
|
||||
"user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b",
|
||||
"tx": "0x7d23b7beca87dac22f754a5d6b367079b813549a87bb547a4c3e08046fc23846"
|
||||
},
|
||||
{
|
||||
"amount": "785.90232091150981825",
|
||||
"user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b",
|
||||
"tx": "0x0552a16f5a65f3ea47db290b80f164d6553717dd735e73bb0429b81e17bbbb3e"
|
||||
},
|
||||
{
|
||||
"amount": "8950.14985089483210984",
|
||||
"user": "0x66827bCD635f2bB1779d68c46aEB16541bCA6ba8",
|
||||
@@ -43144,6 +43187,18 @@
|
||||
"tranche_id": 3,
|
||||
"tx": "0xc8aae35d9d474b83dd5524de9db59f994fd77040397c00a1a4cf30a5c9315826"
|
||||
},
|
||||
{
|
||||
"amount": "1535.17244264446063625",
|
||||
"user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b",
|
||||
"tranche_id": 3,
|
||||
"tx": "0x7d23b7beca87dac22f754a5d6b367079b813549a87bb547a4c3e08046fc23846"
|
||||
},
|
||||
{
|
||||
"amount": "785.90232091150981825",
|
||||
"user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b",
|
||||
"tranche_id": 3,
|
||||
"tx": "0x0552a16f5a65f3ea47db290b80f164d6553717dd735e73bb0429b81e17bbbb3e"
|
||||
},
|
||||
{
|
||||
"amount": "1192.05386354121365675",
|
||||
"user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b",
|
||||
@@ -45534,8 +45589,8 @@
|
||||
}
|
||||
],
|
||||
"total_tokens": "359123.469575",
|
||||
"withdrawn_tokens": "296283.340785656990038",
|
||||
"remaining_tokens": "62840.128789343009962"
|
||||
"withdrawn_tokens": "298604.4155492129604925",
|
||||
"remaining_tokens": "60519.0540257870395075"
|
||||
},
|
||||
{
|
||||
"address": "0xBdd412797c1B78535Afc5F71503b91fAbD0160fB",
|
||||
@@ -46845,8 +46900,8 @@
|
||||
"tranche_start": "2021-10-05T00:00:00.000Z",
|
||||
"tranche_end": "2023-04-05T00:00:00.000Z",
|
||||
"total_added": "5778205.3912159303",
|
||||
"total_removed": "2645595.987385819383764789",
|
||||
"locked_amount": "668082.868916425011638062631735106",
|
||||
"total_removed": "2720584.477569657792162642",
|
||||
"locked_amount": "649591.946558625470488911658628152",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "552496.6455",
|
||||
@@ -47000,6 +47055,26 @@
|
||||
"user": "0xafa64cCa337eFEE0AD827F6C2684e69275226e90",
|
||||
"tx": "0x687f74292db1a9d0c79bedca56eb5dd57d5d338a2d536b355cd2d1022dc4889b"
|
||||
},
|
||||
{
|
||||
"amount": "52477.45638404822374",
|
||||
"user": "0x83b1a48376E045D26420200345414e6b93066396",
|
||||
"tx": "0x8ef3d71e781d7e16dc337620776e044a6e914dc3074109837336941b634ffd61"
|
||||
},
|
||||
{
|
||||
"amount": "18757.845956872719119701",
|
||||
"user": "0xC24da173A250e9Ca5c54870639EbE5f88be5102d",
|
||||
"tx": "0xcdaa0ab119158f2731c0cb7a799ccf34558ac31b643729af102801152247b2d2"
|
||||
},
|
||||
{
|
||||
"amount": "1.402852498117196502",
|
||||
"user": "0xC24da173A250e9Ca5c54870639EbE5f88be5102d",
|
||||
"tx": "0xfae37b3119bf4c1718b13a78a1a3d7fd25c760691f6cc9d52eba3b2ac3d04ec5"
|
||||
},
|
||||
{
|
||||
"amount": "3751.78499041934834165",
|
||||
"user": "0x6ae83EAB68b7112BaD5AfD72d6B24546AbFF137D",
|
||||
"tx": "0x47eb4c897187630f558845180b019d4c5c1816efd91abf3db4964ed14eea5efd"
|
||||
},
|
||||
{
|
||||
"amount": "3634.58269967002683",
|
||||
"user": "0xafa64cCa337eFEE0AD827F6C2684e69275226e90",
|
||||
@@ -48019,6 +48094,12 @@
|
||||
}
|
||||
],
|
||||
"withdrawals": [
|
||||
{
|
||||
"amount": "3751.78499041934834165",
|
||||
"user": "0x6ae83EAB68b7112BaD5AfD72d6B24546AbFF137D",
|
||||
"tranche_id": 4,
|
||||
"tx": "0x47eb4c897187630f558845180b019d4c5c1816efd91abf3db4964ed14eea5efd"
|
||||
},
|
||||
{
|
||||
"amount": "3672.33735125894336105",
|
||||
"user": "0x6ae83EAB68b7112BaD5AfD72d6B24546AbFF137D",
|
||||
@@ -48099,8 +48180,8 @@
|
||||
}
|
||||
],
|
||||
"total_tokens": "92082.572555",
|
||||
"withdrawn_tokens": "77880.2646886437186477",
|
||||
"remaining_tokens": "14202.3078663562813523"
|
||||
"withdrawn_tokens": "81632.04967906306698935",
|
||||
"remaining_tokens": "10450.52287593693301065"
|
||||
},
|
||||
{
|
||||
"address": "0x1dC9B91DE003fd503F25cB5d114cf0fc68F7aFe6",
|
||||
@@ -48128,6 +48209,18 @@
|
||||
}
|
||||
],
|
||||
"withdrawals": [
|
||||
{
|
||||
"amount": "18757.845956872719119701",
|
||||
"user": "0xC24da173A250e9Ca5c54870639EbE5f88be5102d",
|
||||
"tranche_id": 4,
|
||||
"tx": "0xcdaa0ab119158f2731c0cb7a799ccf34558ac31b643729af102801152247b2d2"
|
||||
},
|
||||
{
|
||||
"amount": "1.402852498117196502",
|
||||
"user": "0xC24da173A250e9Ca5c54870639EbE5f88be5102d",
|
||||
"tranche_id": 4,
|
||||
"tx": "0xfae37b3119bf4c1718b13a78a1a3d7fd25c760691f6cc9d52eba3b2ac3d04ec5"
|
||||
},
|
||||
{
|
||||
"amount": "18362.008706688844354071",
|
||||
"user": "0xC24da173A250e9Ca5c54870639EbE5f88be5102d",
|
||||
@@ -48226,8 +48319,8 @@
|
||||
}
|
||||
],
|
||||
"total_tokens": "460415.072915097",
|
||||
"withdrawn_tokens": "389402.491279555911199683",
|
||||
"remaining_tokens": "71012.581635541088800317"
|
||||
"withdrawn_tokens": "408161.740088926747515886",
|
||||
"remaining_tokens": "52253.332826170252484114"
|
||||
},
|
||||
{
|
||||
"address": "0xd66e4853c0880df150e7329974715BFC8d2da47D",
|
||||
@@ -48296,6 +48389,12 @@
|
||||
}
|
||||
],
|
||||
"withdrawals": [
|
||||
{
|
||||
"amount": "52477.45638404822374",
|
||||
"user": "0x83b1a48376E045D26420200345414e6b93066396",
|
||||
"tranche_id": 4,
|
||||
"tx": "0x8ef3d71e781d7e16dc337620776e044a6e914dc3074109837336941b634ffd61"
|
||||
},
|
||||
{
|
||||
"amount": "78729.23106762397314",
|
||||
"user": "0x83b1a48376E045D26420200345414e6b93066396",
|
||||
@@ -48352,8 +48451,8 @@
|
||||
}
|
||||
],
|
||||
"total_tokens": "1104995.291",
|
||||
"withdrawn_tokens": "926865.56004188459769",
|
||||
"remaining_tokens": "178129.73095811540231"
|
||||
"withdrawn_tokens": "979343.01642593282143",
|
||||
"remaining_tokens": "125652.27457406717857"
|
||||
},
|
||||
{
|
||||
"address": "0x5565d64f29Ea17355106DF3bA5903Eb793B3e139",
|
||||
@@ -48762,8 +48861,8 @@
|
||||
"tranche_start": "2022-06-05T00:00:00.000Z",
|
||||
"tranche_end": "2023-06-05T00:00:00.000Z",
|
||||
"total_added": "472355.6199999996",
|
||||
"total_removed": "31901.3891584672685",
|
||||
"locked_amount": "160788.274335342977388499684830024",
|
||||
"total_removed": "31923.1301021472685",
|
||||
"locked_amount": "158522.95647737743991233846067988",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "3000",
|
||||
@@ -55397,6 +55496,11 @@
|
||||
"user": "0x479F059c46c6873C6B970A92911084b3eA036d56",
|
||||
"tx": "0x36fe6a597bd0be532236bf13e1e2575fbb56536666438d07a006a9df0c9b0a13"
|
||||
},
|
||||
{
|
||||
"amount": "21.74094368",
|
||||
"user": "0x7C8D2D8BcFffcD48dBcb65C5Bb7588B66dcD22bb",
|
||||
"tx": "0x2883648c0fa0f5a27defb406ab22bc0120d43e3a98b16fd7a97d99f9e79dc24c"
|
||||
},
|
||||
{
|
||||
"amount": "78.261187214",
|
||||
"user": "0x479F059c46c6873C6B970A92911084b3eA036d56",
|
||||
@@ -69238,6 +69342,12 @@
|
||||
}
|
||||
],
|
||||
"withdrawals": [
|
||||
{
|
||||
"amount": "21.74094368",
|
||||
"user": "0x7C8D2D8BcFffcD48dBcb65C5Bb7588B66dcD22bb",
|
||||
"tranche_id": 5,
|
||||
"tx": "0x2883648c0fa0f5a27defb406ab22bc0120d43e3a98b16fd7a97d99f9e79dc24c"
|
||||
},
|
||||
{
|
||||
"amount": "15.426179608",
|
||||
"user": "0x7C8D2D8BcFffcD48dBcb65C5Bb7588B66dcD22bb",
|
||||
@@ -69270,8 +69380,8 @@
|
||||
}
|
||||
],
|
||||
"total_tokens": "400",
|
||||
"withdrawn_tokens": "243.804249112",
|
||||
"remaining_tokens": "156.195750888"
|
||||
"withdrawn_tokens": "265.545192792",
|
||||
"remaining_tokens": "134.454807208"
|
||||
},
|
||||
{
|
||||
"address": "0xc56F9f1d5f124655FA9CB8f85C7701E13a2fBF4D",
|
||||
@@ -77283,7 +77393,7 @@
|
||||
"tranche_start": "2021-12-05T00:00:00.000Z",
|
||||
"tranche_end": "2022-06-05T00:00:00.000Z",
|
||||
"total_added": "171288.42",
|
||||
"total_removed": "64476.1049690697989",
|
||||
"total_removed": "64726.1049690697989",
|
||||
"locked_amount": "0",
|
||||
"deposits": [
|
||||
{
|
||||
@@ -81518,6 +81628,11 @@
|
||||
"user": "0x0428D82D3C4d8C616Dac3862E9Fc88af9A294b83",
|
||||
"tx": "0x01dfb567e73d420d9370ba42ec1399e159e56fb50457cbe1b2d28e20da2f1dc9"
|
||||
},
|
||||
{
|
||||
"amount": "250",
|
||||
"user": "0x68091918EaDB98f54E8fC179870aB6F755736174",
|
||||
"tx": "0x6957558e2c90b17cd1ef8673ca4a6c949dfcdf0772e3b580e7dda2655b647c64"
|
||||
},
|
||||
{
|
||||
"amount": "250",
|
||||
"user": "0xbd09687340A09BeB0B5EE0D3C2bCa8d78eBF6E63",
|
||||
@@ -98012,10 +98127,17 @@
|
||||
"tx": "0xe32a466fc780a0fb3fd84a804f622931ebfaf3f428bff0dc6d141270410e75f8"
|
||||
}
|
||||
],
|
||||
"withdrawals": [],
|
||||
"withdrawals": [
|
||||
{
|
||||
"amount": "250",
|
||||
"user": "0x68091918EaDB98f54E8fC179870aB6F755736174",
|
||||
"tranche_id": 6,
|
||||
"tx": "0x6957558e2c90b17cd1ef8673ca4a6c949dfcdf0772e3b580e7dda2655b647c64"
|
||||
}
|
||||
],
|
||||
"total_tokens": "250",
|
||||
"withdrawn_tokens": "0",
|
||||
"remaining_tokens": "250"
|
||||
"withdrawn_tokens": "250",
|
||||
"remaining_tokens": "0"
|
||||
},
|
||||
{
|
||||
"address": "0x0E39d235cf6874F22a13c55C19500343B371000f",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# App configuration variables
|
||||
NX_VEGA_ENV=TESTNET
|
||||
NX_VEGA_CONFIG_URL=https://static.vega.xyz/assets/testnet-network.json
|
||||
NX_VEGA_URL=https://api.n10.testnet.vega.xyz/graphql
|
||||
NX_VEGA_URL=https://api.n08.testnet.vega.xyz/graphql
|
||||
NX_VEGA_NETWORKS='{"DEVNET":"https://dev.token.vega.xyz","STAGNET3":"https://stagnet3.token.vega.xyz","TESTNET":"https://token.fairground.wtf","MAINNET":"https://token.vega.xyz"}'
|
||||
NX_ETHEREUM_PROVIDER_URL=https://sepolia.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8
|
||||
NX_ETHERSCAN_URL=https://sepolia.etherscan.io
|
||||
|
||||
@@ -12,5 +12,10 @@ export default {
|
||||
moduleNameMapper: {
|
||||
'^d3-(.*)$': `d3-$1/dist/d3-$1`,
|
||||
},
|
||||
collectCoverageFrom: ['**/*.{ts,tsx}', '!**/node_modules/**'],
|
||||
collectCoverageFrom: [
|
||||
'**/*.{ts,tsx}',
|
||||
'!**/node_modules/**',
|
||||
'!**/__generated__/**',
|
||||
'!**/__generated___/**',
|
||||
],
|
||||
};
|
||||
|
||||
@@ -2,20 +2,24 @@ import * as Sentry from '@sentry/react';
|
||||
import { toBigNum } from '@vegaprotocol/react-helpers';
|
||||
import { useEthereumConfig } from '@vegaprotocol/web3';
|
||||
import { useWeb3React } from '@web3-react/core';
|
||||
import React from 'react';
|
||||
import { useEffect } from 'react';
|
||||
|
||||
import { useAppState } from '../../contexts/app-state/app-state-context';
|
||||
import { useContracts } from '../../contexts/contracts/contracts-context';
|
||||
import { useGetAssociationBreakdown } from '../../hooks/use-get-association-breakdown';
|
||||
import { useGetUserTrancheBalances } from '../../hooks/use-get-user-tranche-balances';
|
||||
import { useBalances } from '../../lib/balances/balances-store';
|
||||
import type { ReactElement } from 'react';
|
||||
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||
import { useListenForStakingEvents as useListenForAssociationEvents } from '../../hooks/use-listen-for-staking-events';
|
||||
|
||||
interface BalanceManagerProps {
|
||||
children: React.ReactElement;
|
||||
children: ReactElement;
|
||||
}
|
||||
|
||||
export const BalanceManager = ({ children }: BalanceManagerProps) => {
|
||||
const contracts = useContracts();
|
||||
const { pubKey } = useVegaWallet();
|
||||
const { account } = useWeb3React();
|
||||
const {
|
||||
appState: { decimals },
|
||||
@@ -23,6 +27,20 @@ export const BalanceManager = ({ children }: BalanceManagerProps) => {
|
||||
const { updateBalances: updateStoreBalances } = useBalances();
|
||||
const { config } = useEthereumConfig();
|
||||
|
||||
const numberOfConfirmations = config?.confirmations || 0;
|
||||
|
||||
useListenForAssociationEvents(
|
||||
contracts?.staking.contract,
|
||||
pubKey,
|
||||
numberOfConfirmations
|
||||
);
|
||||
|
||||
useListenForAssociationEvents(
|
||||
contracts?.vesting.contract,
|
||||
pubKey,
|
||||
numberOfConfirmations
|
||||
);
|
||||
|
||||
const getUserTrancheBalances = useGetUserTrancheBalances(
|
||||
account || '',
|
||||
contracts?.vesting
|
||||
@@ -34,7 +52,7 @@ export const BalanceManager = ({ children }: BalanceManagerProps) => {
|
||||
);
|
||||
|
||||
// update balances on connect to Ethereum
|
||||
React.useEffect(() => {
|
||||
useEffect(() => {
|
||||
const updateBalances = async () => {
|
||||
if (!account || !config) return;
|
||||
try {
|
||||
@@ -75,13 +93,13 @@ export const BalanceManager = ({ children }: BalanceManagerProps) => {
|
||||
]);
|
||||
|
||||
// This use effect hook is very expensive and is kept separate to prevent expensive reloading of data.
|
||||
React.useEffect(() => {
|
||||
useEffect(() => {
|
||||
if (account) {
|
||||
getUserTrancheBalances();
|
||||
}
|
||||
}, [account, getUserTrancheBalances]);
|
||||
|
||||
React.useEffect(() => {
|
||||
useEffect(() => {
|
||||
if (account) {
|
||||
getAssociationBreakdown();
|
||||
}
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
import { Networks, useEnvironment } from '@vegaprotocol/environment';
|
||||
import { ViewingAsBanner } from '@vegaprotocol/ui-toolkit';
|
||||
import {
|
||||
ViewingAsBanner,
|
||||
AnnouncementBanner,
|
||||
ExternalLink,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||
import React from 'react';
|
||||
|
||||
@@ -15,6 +19,14 @@ export function TemplateSidebar({ children, sidebar }: TemplateSidebarProps) {
|
||||
const { isReadOnly, pubKey, disconnect } = useVegaWallet();
|
||||
return (
|
||||
<>
|
||||
<AnnouncementBanner>
|
||||
<div className="font-alpha calt uppercase text-center text-lg text-white">
|
||||
<span className="pr-4">The Mainnet sims are live!</span>
|
||||
<ExternalLink href="https://fairground.wtf/">
|
||||
Come help stress test the network
|
||||
</ExternalLink>
|
||||
</div>
|
||||
</AnnouncementBanner>
|
||||
<Nav navbarTheme={VEGA_ENV === Networks.TESTNET ? 'yellow' : 'dark'} />
|
||||
{isReadOnly ? (
|
||||
<ViewingAsBanner pubKey={pubKey} disconnect={disconnect} />
|
||||
|
||||
@@ -25,6 +25,8 @@ import { usePollForDelegations } from './hooks';
|
||||
import { useVegaWallet, useVegaWalletDialogStore } from '@vegaprotocol/wallet';
|
||||
import { Button, ButtonLink } from '@vegaprotocol/ui-toolkit';
|
||||
import { toBigNum } from '@vegaprotocol/react-helpers';
|
||||
import { usePendingBalancesStore } from '../../hooks/use-pending-balances-manager';
|
||||
import { StakingEventType } from '../../hooks/use-get-association-breakdown';
|
||||
|
||||
export const VegaWallet = () => {
|
||||
const { t } = useTranslation();
|
||||
@@ -119,6 +121,10 @@ interface VegaWalletConnectedProps {
|
||||
}
|
||||
|
||||
const VegaWalletConnected = ({ vegaKeys }: VegaWalletConnectedProps) => {
|
||||
const pendingBalances = usePendingBalancesStore(
|
||||
(state) => state.pendingBalances
|
||||
);
|
||||
|
||||
const { t } = useTranslation();
|
||||
const {
|
||||
appDispatch,
|
||||
@@ -126,6 +132,27 @@ const VegaWalletConnected = ({ vegaKeys }: VegaWalletConnectedProps) => {
|
||||
} = useAppState();
|
||||
const { delegations, currentStakeAvailable, delegatedNodes, accounts } =
|
||||
usePollForDelegations();
|
||||
const amountRemoved = BigNumber.sum.apply(null, [
|
||||
new BigNumber(0),
|
||||
...pendingBalances
|
||||
.filter(({ event }) => event === StakingEventType.Stake_Removed)
|
||||
.map(({ args }) => toBigNum(args?.[1].toString(), decimals)),
|
||||
]);
|
||||
|
||||
const amountAdded = BigNumber.sum.apply(null, [
|
||||
new BigNumber(0),
|
||||
...pendingBalances
|
||||
.filter(({ event }) => event === StakingEventType.Stake_Deposited)
|
||||
.map(({ args }) => toBigNum(args?.[1].toString(), decimals)),
|
||||
]);
|
||||
const totalPending = React.useMemo(
|
||||
() => amountRemoved.plus(amountAdded),
|
||||
[amountAdded, amountRemoved]
|
||||
);
|
||||
const pendingStakeAmount = React.useMemo(
|
||||
() => currentStakeAvailable.plus(amountAdded).minus(amountRemoved),
|
||||
[amountAdded, amountRemoved, currentStakeAvailable]
|
||||
);
|
||||
|
||||
const unstaked = React.useMemo(() => {
|
||||
const totalDelegated = delegations.reduce<BigNumber>(
|
||||
@@ -161,6 +188,26 @@ const VegaWalletConnected = ({ vegaKeys }: VegaWalletConnectedProps) => {
|
||||
symbol="VEGA"
|
||||
balance={currentStakeAvailable}
|
||||
/>
|
||||
{totalPending.eq(0) ? null : (
|
||||
<>
|
||||
<WalletCardAsset
|
||||
image={vegaWhite}
|
||||
decimals={decimals}
|
||||
name="VEGA"
|
||||
subheading={t('Pending association')}
|
||||
symbol="VEGA"
|
||||
balance={totalPending}
|
||||
/>
|
||||
<WalletCardAsset
|
||||
image={vegaWhite}
|
||||
decimals={decimals}
|
||||
name="VEGA"
|
||||
subheading={t('Total associated after pending')}
|
||||
symbol="VEGA"
|
||||
balance={pendingStakeAmount}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
<div data-testid="vega-wallet-balance-unstaked">
|
||||
<WalletCardRow label={t('unstaked')} value={unstaked} />
|
||||
</div>
|
||||
|
||||
@@ -11,6 +11,11 @@ import { useAppState } from '../contexts/app-state/app-state-context';
|
||||
import BigNumber from 'bignumber.js';
|
||||
import { useBalances } from '../lib/balances/balances-store';
|
||||
|
||||
export enum StakingEventType {
|
||||
Stake_Removed = 'Stake_Removed',
|
||||
Stake_Deposited = 'Stake_Deposited',
|
||||
}
|
||||
|
||||
export function useGetAssociationBreakdown(
|
||||
ethAddress: string,
|
||||
staking: StakingBridge,
|
||||
@@ -63,8 +68,8 @@ function combineStakeEventsByVegaKey(
|
||||
const res = events.reduce((obj, e) => {
|
||||
const vegaKey = e.args?.vega_public_key;
|
||||
const amount = parseEventAmount(e, decimals);
|
||||
const isDeposit = e.event === 'Stake_Deposited';
|
||||
const isRemove = e.event === 'Stake_Removed';
|
||||
const isDeposit = e.event === StakingEventType.Stake_Deposited;
|
||||
const isRemove = e.event === StakingEventType.Stake_Removed;
|
||||
|
||||
if (!isDeposit && !isRemove) return obj;
|
||||
|
||||
|
||||
@@ -0,0 +1,102 @@
|
||||
import { useListenForPendingEthEvents } from './use-listen-for-pending-eth-events';
|
||||
import { renderHook, cleanup, waitFor } from '@testing-library/react';
|
||||
import type { Contract, EventFilter, Event } from 'ethers';
|
||||
|
||||
let contract: Contract;
|
||||
let filter: EventFilter;
|
||||
let addPendingTxs: (event: Event[]) => void;
|
||||
let removePendingTx: (event: Event) => void;
|
||||
let resetPendingTxs: () => void;
|
||||
|
||||
beforeEach(() => {
|
||||
contract = {
|
||||
on: jest.fn(),
|
||||
off: jest.fn(),
|
||||
queryFilter: jest.fn().mockResolvedValue([]),
|
||||
} as unknown as Contract;
|
||||
filter = {} as EventFilter;
|
||||
addPendingTxs = jest.fn();
|
||||
removePendingTx = jest.fn();
|
||||
resetPendingTxs = jest.fn();
|
||||
});
|
||||
|
||||
jest.mock('@web3-react/core', () => ({
|
||||
useWeb3React: () => ({
|
||||
provider: {
|
||||
getBlockNumber: jest.fn().mockResolvedValue(1),
|
||||
},
|
||||
}),
|
||||
}));
|
||||
|
||||
describe('useListenForPendingEthEvents', () => {
|
||||
it('listens for events on contract', async () => {
|
||||
renderHook(() =>
|
||||
useListenForPendingEthEvents(
|
||||
1,
|
||||
contract,
|
||||
filter,
|
||||
addPendingTxs,
|
||||
removePendingTx,
|
||||
resetPendingTxs
|
||||
)
|
||||
);
|
||||
|
||||
expect(contract.on).toHaveBeenCalledWith(filter, expect.any(Function));
|
||||
cleanup();
|
||||
expect(contract.off).toHaveBeenCalledWith(filter, expect.any(Function));
|
||||
});
|
||||
|
||||
it('waits for correct number of confirmations before removing tx from pending txs', async () => {
|
||||
renderHook(() => {
|
||||
useListenForPendingEthEvents(
|
||||
2,
|
||||
contract,
|
||||
filter,
|
||||
addPendingTxs,
|
||||
removePendingTx,
|
||||
resetPendingTxs
|
||||
);
|
||||
});
|
||||
|
||||
const listener = (contract.on as jest.Mock).mock.calls[0][1];
|
||||
const event = {
|
||||
getTransaction: jest.fn().mockResolvedValue({
|
||||
wait: jest.fn().mockResolvedValue({}),
|
||||
}),
|
||||
} as unknown as Event;
|
||||
listener(null, null, null, event);
|
||||
|
||||
expect(addPendingTxs).toHaveBeenCalledWith([event]);
|
||||
expect(removePendingTx).not.toHaveBeenCalled();
|
||||
|
||||
await waitFor(() => {
|
||||
expect(removePendingTx).toHaveBeenCalledWith(event);
|
||||
});
|
||||
});
|
||||
|
||||
it('gets existing transactions', async () => {
|
||||
const event = {
|
||||
getTransaction: jest.fn().mockResolvedValue({
|
||||
wait: jest.fn().mockResolvedValue(null),
|
||||
}),
|
||||
} as unknown as Event;
|
||||
|
||||
contract.queryFilter = jest.fn().mockResolvedValue([event]);
|
||||
|
||||
renderHook(() => {
|
||||
useListenForPendingEthEvents(
|
||||
2,
|
||||
contract,
|
||||
filter,
|
||||
addPendingTxs,
|
||||
removePendingTx,
|
||||
resetPendingTxs
|
||||
);
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(contract.queryFilter).toHaveBeenCalledWith(filter, -1);
|
||||
expect(addPendingTxs).toHaveBeenCalledWith([event]);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,103 @@
|
||||
import { useWeb3React } from '@web3-react/core';
|
||||
import { useCallback, useEffect } from 'react';
|
||||
import * as Sentry from '@sentry/react';
|
||||
import type { Contract, Event, EventFilter } from 'ethers';
|
||||
|
||||
export const useListenForPendingEthEvents = (
|
||||
numberOfConfirmations: number,
|
||||
contract: Contract | undefined,
|
||||
filter: EventFilter | null,
|
||||
addPendingTxs: (event: Event[]) => void,
|
||||
removePendingTx: (event: Event) => void,
|
||||
resetPendingTxs: () => void
|
||||
) => {
|
||||
const { provider } = useWeb3React();
|
||||
|
||||
/**
|
||||
* Add listener for the ethereum events on the contract passed in for the filter passed in.
|
||||
* Push the event into the store and wait for the correct number of confirmations before removing it.
|
||||
*/
|
||||
useEffect(() => {
|
||||
if (!contract || !filter) {
|
||||
return;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const listener = async (...args: any[]) => {
|
||||
try {
|
||||
const event = args[3] as Event;
|
||||
addPendingTxs([event]);
|
||||
const tx = await event.getTransaction();
|
||||
await tx.wait(numberOfConfirmations);
|
||||
removePendingTx(event);
|
||||
} catch (e) {
|
||||
Sentry.captureException(`Error listening for pending eth events ${e}`);
|
||||
}
|
||||
};
|
||||
|
||||
contract.on(filter, listener);
|
||||
|
||||
return () => {
|
||||
contract.off(filter, listener);
|
||||
};
|
||||
}, [addPendingTxs, contract, filter, numberOfConfirmations, removePendingTx]);
|
||||
|
||||
/**
|
||||
* Get all transactions that exist on the blockchain but have yet to reach the number of confirmations
|
||||
*/
|
||||
const getExistingTransactions = useCallback(async () => {
|
||||
const blockNumber = (await provider?.getBlockNumber()) || 0;
|
||||
if (!filter || !contract) {
|
||||
return [];
|
||||
}
|
||||
try {
|
||||
return await contract.queryFilter(
|
||||
filter,
|
||||
blockNumber - numberOfConfirmations
|
||||
);
|
||||
} catch (e) {
|
||||
Sentry.captureException(`Error getting existing transactions ${e}`);
|
||||
return [];
|
||||
}
|
||||
}, [contract, filter, numberOfConfirmations, provider]);
|
||||
|
||||
const waitForExistingTransactions = useCallback(
|
||||
(events: Event[], numberOfConfirmations: number) => {
|
||||
events.map(async (event) => {
|
||||
try {
|
||||
const tx = await event.getTransaction();
|
||||
|
||||
await tx.wait(Math.max(numberOfConfirmations, 0));
|
||||
|
||||
removePendingTx(event);
|
||||
} catch (e) {
|
||||
Sentry.captureException(
|
||||
`Error waiting for existing transactions ${e}`
|
||||
);
|
||||
}
|
||||
});
|
||||
},
|
||||
[removePendingTx]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
let cancelled = false;
|
||||
resetPendingTxs();
|
||||
getExistingTransactions().then((events) => {
|
||||
if (!cancelled) {
|
||||
addPendingTxs([...events]);
|
||||
waitForExistingTransactions([...events], numberOfConfirmations);
|
||||
}
|
||||
});
|
||||
|
||||
return () => {
|
||||
cancelled = true;
|
||||
};
|
||||
}, [
|
||||
addPendingTxs,
|
||||
getExistingTransactions,
|
||||
numberOfConfirmations,
|
||||
resetPendingTxs,
|
||||
waitForExistingTransactions,
|
||||
]);
|
||||
};
|
||||
@@ -0,0 +1,56 @@
|
||||
import { useMemo } from 'react';
|
||||
import { usePendingBalancesStore } from './use-pending-balances-manager';
|
||||
import type { Contract } from 'ethers';
|
||||
import { useListenForPendingEthEvents } from './use-listen-for-pending-eth-events';
|
||||
import { prepend0x } from '@vegaprotocol/smart-contracts';
|
||||
|
||||
export const useListenForStakingEvents = (
|
||||
contract: Contract | undefined,
|
||||
vegaPublicKey: string | null,
|
||||
numberOfConfirmations: number
|
||||
) => {
|
||||
const { addPendingTxs, removePendingTx, resetPendingTxs } =
|
||||
usePendingBalancesStore((state) => ({
|
||||
addPendingTxs: state.addPendingTxs,
|
||||
removePendingTx: state.removePendingTx,
|
||||
resetPendingTxs: state.resetPendingTxs,
|
||||
}));
|
||||
const addFilter = useMemo(
|
||||
() =>
|
||||
vegaPublicKey && contract
|
||||
? contract.filters.Stake_Deposited(null, null, prepend0x(vegaPublicKey))
|
||||
: null,
|
||||
[contract, vegaPublicKey]
|
||||
);
|
||||
const removeFilter = useMemo(
|
||||
() =>
|
||||
vegaPublicKey && contract
|
||||
? contract.filters.Stake_Removed(null, null, prepend0x(vegaPublicKey))
|
||||
: null,
|
||||
[contract, vegaPublicKey]
|
||||
);
|
||||
|
||||
/**
|
||||
* Listen for all add stake events
|
||||
*/
|
||||
useListenForPendingEthEvents(
|
||||
numberOfConfirmations,
|
||||
contract,
|
||||
addFilter,
|
||||
addPendingTxs,
|
||||
removePendingTx,
|
||||
resetPendingTxs
|
||||
);
|
||||
|
||||
/**
|
||||
* Listen for all remove stake events
|
||||
*/
|
||||
useListenForPendingEthEvents(
|
||||
numberOfConfirmations,
|
||||
contract,
|
||||
removeFilter,
|
||||
addPendingTxs,
|
||||
removePendingTx,
|
||||
resetPendingTxs
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,76 @@
|
||||
import { act } from '@testing-library/react-hooks';
|
||||
import { usePendingBalancesStore } from './use-pending-balances-manager';
|
||||
import type { Event } from 'ethers';
|
||||
|
||||
afterEach(() => {
|
||||
usePendingBalancesStore.setState((state) => ({
|
||||
...state,
|
||||
pendingBalances: [],
|
||||
}));
|
||||
});
|
||||
|
||||
const event1 = { transactionHash: 'tx1' } as Event;
|
||||
const event2 = { transactionHash: 'tx2' } as Event;
|
||||
|
||||
describe('usePendingBalancesStore', () => {
|
||||
it('should add new events to the pendingBalances state and remove duplicates', () => {
|
||||
const { addPendingTxs } = usePendingBalancesStore.getState();
|
||||
const duplicateEvent = { transactionHash: 'tx1' } as Event;
|
||||
|
||||
act(() => {
|
||||
addPendingTxs([event1, duplicateEvent]);
|
||||
});
|
||||
|
||||
expect(usePendingBalancesStore.getState().pendingBalances).toEqual([
|
||||
event1,
|
||||
]);
|
||||
|
||||
act(() => {
|
||||
addPendingTxs([event2]);
|
||||
});
|
||||
|
||||
expect(usePendingBalancesStore.getState().pendingBalances).toEqual([
|
||||
event1,
|
||||
event2,
|
||||
]);
|
||||
});
|
||||
|
||||
it('should remove a specific event from the pendingBalances state', () => {
|
||||
const { addPendingTxs, removePendingTx } =
|
||||
usePendingBalancesStore.getState();
|
||||
const eventToRemove = { transactionHash: 'tx1' } as Event;
|
||||
|
||||
act(() => {
|
||||
addPendingTxs([eventToRemove, event2]);
|
||||
});
|
||||
expect(usePendingBalancesStore.getState().pendingBalances).toEqual([
|
||||
eventToRemove,
|
||||
event2,
|
||||
]);
|
||||
|
||||
act(() => {
|
||||
removePendingTx(eventToRemove);
|
||||
});
|
||||
expect(usePendingBalancesStore.getState().pendingBalances).toEqual([
|
||||
event2,
|
||||
]);
|
||||
});
|
||||
|
||||
it('should reset the pendingBalances state', () => {
|
||||
const { addPendingTxs, resetPendingTxs } =
|
||||
usePendingBalancesStore.getState();
|
||||
|
||||
act(() => {
|
||||
addPendingTxs([event1, event2]);
|
||||
});
|
||||
expect(usePendingBalancesStore.getState().pendingBalances).toEqual([
|
||||
event1,
|
||||
event2,
|
||||
]);
|
||||
|
||||
act(() => {
|
||||
resetPendingTxs();
|
||||
});
|
||||
expect(usePendingBalancesStore.getState().pendingBalances).toEqual([]);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,35 @@
|
||||
import type { Event } from 'ethers';
|
||||
import uniqBy from 'lodash/uniqBy';
|
||||
|
||||
import create from 'zustand';
|
||||
|
||||
export type PendingTxsStore = {
|
||||
pendingBalances: Event[];
|
||||
addPendingTxs: (event: Event[]) => void;
|
||||
removePendingTx: (event: Event) => void;
|
||||
resetPendingTxs: () => void;
|
||||
};
|
||||
|
||||
export const usePendingBalancesStore = create<PendingTxsStore>((set, get) => ({
|
||||
pendingBalances: [],
|
||||
addPendingTxs: (event: Event[]) => {
|
||||
set({
|
||||
pendingBalances: uniqBy(
|
||||
[...get().pendingBalances, ...event],
|
||||
'transactionHash'
|
||||
),
|
||||
});
|
||||
},
|
||||
removePendingTx: (event: Event) => {
|
||||
set({
|
||||
pendingBalances: [
|
||||
...get().pendingBalances.filter(
|
||||
({ transactionHash }) => transactionHash !== event.transactionHash
|
||||
),
|
||||
],
|
||||
});
|
||||
},
|
||||
resetPendingTxs: () => {
|
||||
set({ pendingBalances: [] });
|
||||
},
|
||||
}));
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Dialog, Icon, Intent } from '@vegaprotocol/ui-toolkit';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { Link, useNavigate } from 'react-router-dom';
|
||||
import Routes from '../../routes';
|
||||
import type { StakeAction } from './staking-form';
|
||||
import { Actions, RemoveType } from './staking-form';
|
||||
@@ -23,6 +23,7 @@ export const StakeSuccess = ({
|
||||
toggleDialog,
|
||||
}: StakeSuccessProps) => {
|
||||
const { t } = useTranslation();
|
||||
const navigate = useNavigate();
|
||||
const isAdd = action === Actions.Add;
|
||||
const title = isAdd
|
||||
? t('stakeAddSuccessTitle', { amount })
|
||||
@@ -44,7 +45,15 @@ export const StakeSuccess = ({
|
||||
<div>
|
||||
<p>{message}</p>
|
||||
<p>
|
||||
<Link className="underline" to={Routes.VALIDATORS}>
|
||||
<Link
|
||||
className="underline"
|
||||
to={Routes.VALIDATORS}
|
||||
onClick={(event) => {
|
||||
event.preventDefault();
|
||||
toggleDialog();
|
||||
setTimeout(() => navigate(Routes.VALIDATORS), 0);
|
||||
}}
|
||||
>
|
||||
{t('backToStaking')}
|
||||
</Link>
|
||||
</p>
|
||||
|
||||
@@ -452,7 +452,7 @@ describe('limit order validations', { tags: '@smoke' }, () => {
|
||||
//7002-SORD-018
|
||||
cy.getByTestId(orderPriceField)
|
||||
.siblings('label')
|
||||
.should('have.text', 'Price (tBTC)');
|
||||
.should('have.text', 'Price (BTC)');
|
||||
});
|
||||
|
||||
it('must see warning when placing an order with expiry date in past', () => {
|
||||
|
||||
@@ -49,7 +49,6 @@ describe('NodeHealth', () => {
|
||||
it.each(cases)(
|
||||
'renders correct text and indicator color for $diff block difference',
|
||||
(elem) => {
|
||||
console.log(elem);
|
||||
render(<NodeHealth blockDiff={elem.diff} openNodeSwitcher={jest.fn()} />);
|
||||
expect(screen.getByTestId('indicator')).toHaveClass(elem.classname);
|
||||
expect(screen.getByText(elem.text)).toBeInTheDocument();
|
||||
|
||||
@@ -1,11 +1,25 @@
|
||||
import type { RefObject } from 'react';
|
||||
import { t } from '@vegaprotocol/react-helpers';
|
||||
import { useMemo } from 'react';
|
||||
import { t, useDataProvider } from '@vegaprotocol/react-helpers';
|
||||
import { TradingModeTooltip } from '@vegaprotocol/deal-ticket';
|
||||
import { useInView } from 'react-intersection-observer';
|
||||
import * as Schema from '@vegaprotocol/types';
|
||||
import { useStaticMarketData } from '@vegaprotocol/market-list';
|
||||
import { HeaderStat } from '../header';
|
||||
import { Tooltip } from '@vegaprotocol/ui-toolkit';
|
||||
import { marketDataProvider } from '@vegaprotocol/market-list';
|
||||
|
||||
// This will cause often re-rendering
|
||||
// Here it may not be a problem because the component is not very complex
|
||||
// In general, we should avoid using this marketData hook without any throttling
|
||||
const useMarketData = (marketId?: string, skip?: boolean) => {
|
||||
const variables = useMemo(() => ({ marketId }), [marketId]);
|
||||
const { data } = useDataProvider({
|
||||
dataProvider: marketDataProvider,
|
||||
variables,
|
||||
skip: skip || !marketId,
|
||||
});
|
||||
return data;
|
||||
};
|
||||
|
||||
const getTradingModeLabel = (
|
||||
tradingMode?: Schema.MarketTradingMode,
|
||||
@@ -35,7 +49,7 @@ export const HeaderStatMarketTradingMode = ({
|
||||
initialTradingMode,
|
||||
initialTrigger,
|
||||
}: HeaderStatMarketTradingModeProps) => {
|
||||
const data = useStaticMarketData(marketId);
|
||||
const data = useMarketData(marketId);
|
||||
const tradingMode = data?.marketTradingMode ?? initialTradingMode;
|
||||
const trigger = data?.trigger ?? initialTrigger;
|
||||
|
||||
@@ -61,7 +75,7 @@ export const MarketTradingMode = ({
|
||||
inViewRoot?: RefObject<Element>;
|
||||
}) => {
|
||||
const [ref, inView] = useInView({ root: inViewRoot?.current });
|
||||
const data = useStaticMarketData(marketId, !inView);
|
||||
const data = useMarketData(marketId, !inView);
|
||||
|
||||
return (
|
||||
<Tooltip
|
||||
|
||||
@@ -151,6 +151,8 @@ const MARKET_B: PartialMarket = {
|
||||
};
|
||||
|
||||
describe('SelectMarket', () => {
|
||||
const table = document.createElement('table');
|
||||
|
||||
it('should render the SelectAllMarketsTableBody', () => {
|
||||
const onSelect = jest.fn();
|
||||
const onCellClick = jest.fn();
|
||||
@@ -162,7 +164,7 @@ describe('SelectMarket', () => {
|
||||
onSelect={onSelect}
|
||||
/>
|
||||
</MemoryRouter>,
|
||||
{ wrapper: MockedProvider }
|
||||
{ wrapper: MockedProvider, container: document.body.appendChild(table) }
|
||||
);
|
||||
expect(screen.getByText('ABCDEF')).toBeTruthy(); // name
|
||||
expect(screen.getByText('25.00%')).toBeTruthy(); // price change
|
||||
|
||||
@@ -59,14 +59,17 @@ describe('AccountManager', () => {
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText('No accounts')).toBeInTheDocument();
|
||||
});
|
||||
expect(mockedUpdate({ data: [] })).toEqual(true);
|
||||
expect(
|
||||
mockedUpdate({ data: [{ party: { id: 't1' } }] as AccountFields[] })
|
||||
).toEqual(false);
|
||||
expect(
|
||||
mockedUpdate({ data: [{ party: { id: 't2' } }] as AccountFields[] })
|
||||
).toEqual(true);
|
||||
expect(mockedUpdate({ data: [] })).toEqual(false);
|
||||
expect(mockedUpdate({ data: [] })).toEqual(true);
|
||||
await act(() => {
|
||||
expect(mockedUpdate({ data: [] })).toEqual(true);
|
||||
|
||||
expect(
|
||||
mockedUpdate({ data: [{ party: { id: 't1' } }] as AccountFields[] })
|
||||
).toEqual(false);
|
||||
expect(
|
||||
mockedUpdate({ data: [{ party: { id: 't2' } }] as AccountFields[] })
|
||||
).toEqual(true);
|
||||
expect(mockedUpdate({ data: [] })).toEqual(false);
|
||||
expect(mockedUpdate({ data: [] })).toEqual(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -16,7 +16,7 @@ export interface DealTicketFeeDetails {
|
||||
label: string;
|
||||
value?: string | number | null;
|
||||
labelDescription?: string | ReactNode;
|
||||
quoteName?: string;
|
||||
symbol?: string;
|
||||
}
|
||||
|
||||
export const DealTicketFeeDetails = ({
|
||||
@@ -27,7 +27,7 @@ export const DealTicketFeeDetails = ({
|
||||
const details = getFeeDetailsValues(feeDetails);
|
||||
return (
|
||||
<div>
|
||||
{details.map(({ label, value, labelDescription, quoteName }) => (
|
||||
{details.map(({ label, value, labelDescription, symbol }) => (
|
||||
<div
|
||||
key={label}
|
||||
className="text-xs mt-2 flex justify-between items-center gap-4 flex-wrap"
|
||||
@@ -39,7 +39,7 @@ export const DealTicketFeeDetails = ({
|
||||
</div>
|
||||
<div className="text-neutral-500 dark:text-neutral-300">{`${
|
||||
value ?? '-'
|
||||
} ${quoteName || ''}`}</div>
|
||||
} ${symbol || ''}`}</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
@@ -15,8 +15,7 @@ export const DealTicketLimitAmount = ({
|
||||
}: DealTicketLimitAmountProps) => {
|
||||
const priceStep = toDecimal(market?.decimalPlaces);
|
||||
const sizeStep = toDecimal(market?.positionDecimalPlaces);
|
||||
const quoteName =
|
||||
market.tradableInstrument.instrument.product.settlementAsset.symbol;
|
||||
const quoteName = market.tradableInstrument.instrument.product.quoteName;
|
||||
|
||||
const renderError = () => {
|
||||
if (sizeError) {
|
||||
|
||||
@@ -19,8 +19,7 @@ export const DealTicketMarketAmount = ({
|
||||
market,
|
||||
sizeError,
|
||||
}: DealTicketMarketAmountProps) => {
|
||||
const quoteName =
|
||||
market.tradableInstrument.instrument.product.settlementAsset.symbol;
|
||||
const quoteName = market.tradableInstrument.instrument.product.quoteName;
|
||||
const sizeStep = toDecimal(market?.positionDecimalPlaces);
|
||||
const price = getMarketPrice(market);
|
||||
|
||||
|
||||
@@ -84,7 +84,7 @@ describe('DealTicket', () => {
|
||||
expect(screen.getByTestId('last-price')).toHaveTextContent(
|
||||
// eslint-disable-next-line
|
||||
`~${addDecimal(market!.data.markPrice, market.decimalPlaces)} ${
|
||||
market.tradableInstrument.instrument.product.settlementAsset.symbol
|
||||
market.tradableInstrument.instrument.product.quoteName
|
||||
}`
|
||||
);
|
||||
});
|
||||
|
||||
@@ -69,12 +69,13 @@ export const useFeeDealTicketDetails = (
|
||||
return null;
|
||||
}, [derivedPrice, order.size, market.decimalPlaces]);
|
||||
|
||||
const quoteName = market.tradableInstrument.instrument.product.quoteName;
|
||||
const symbol =
|
||||
market.tradableInstrument.instrument.product.settlementAsset.symbol;
|
||||
|
||||
return useMemo(() => {
|
||||
return {
|
||||
market,
|
||||
quoteName,
|
||||
symbol,
|
||||
notionalSize,
|
||||
estMargin,
|
||||
estCloseOut,
|
||||
@@ -83,7 +84,7 @@ export const useFeeDealTicketDetails = (
|
||||
};
|
||||
}, [
|
||||
market,
|
||||
quoteName,
|
||||
symbol,
|
||||
notionalSize,
|
||||
estMargin,
|
||||
estCloseOut,
|
||||
@@ -94,7 +95,7 @@ export const useFeeDealTicketDetails = (
|
||||
|
||||
export interface FeeDetails {
|
||||
market: MarketDealTicket;
|
||||
quoteName: string;
|
||||
symbol: string;
|
||||
notionalSize: string | null;
|
||||
estMargin: OrderMargin | null;
|
||||
estCloseOut: string | null;
|
||||
@@ -102,7 +103,7 @@ export interface FeeDetails {
|
||||
}
|
||||
|
||||
export const getFeeDetailsValues = ({
|
||||
quoteName,
|
||||
symbol,
|
||||
notionalSize,
|
||||
estMargin,
|
||||
estCloseOut,
|
||||
@@ -128,7 +129,7 @@ export const getFeeDetailsValues = ({
|
||||
{
|
||||
label: t('Notional'),
|
||||
value: formatValueWithMarketDp(notionalSize),
|
||||
quoteName,
|
||||
symbol,
|
||||
labelDescription: NOTIONAL_SIZE_TOOLTIP_TEXT,
|
||||
},
|
||||
{
|
||||
@@ -146,24 +147,24 @@ export const getFeeDetailsValues = ({
|
||||
<FeesBreakdown
|
||||
fees={estMargin?.fees}
|
||||
feeFactors={market.fees.factors}
|
||||
quoteName={quoteName}
|
||||
symbol={symbol}
|
||||
decimals={assetDecimals}
|
||||
/>
|
||||
</>
|
||||
),
|
||||
quoteName,
|
||||
symbol,
|
||||
},
|
||||
{
|
||||
label: t('Margin'),
|
||||
value:
|
||||
estMargin?.margin && `~${formatValueWithAssetDp(estMargin?.margin)}`,
|
||||
quoteName,
|
||||
symbol,
|
||||
labelDescription: EST_MARGIN_TOOLTIP_TEXT,
|
||||
},
|
||||
{
|
||||
label: t('Liquidation'),
|
||||
value: estCloseOut && `~${formatValueWithMarketDp(estCloseOut)}`,
|
||||
quoteName,
|
||||
symbol: market.tradableInstrument.instrument.product.quoteName,
|
||||
labelDescription: EST_CLOSEOUT_TOOLTIP_TEXT,
|
||||
},
|
||||
];
|
||||
|
||||
@@ -185,7 +185,7 @@ export const DepositForm = ({
|
||||
hasError={Boolean(errors.asset?.message)}
|
||||
>
|
||||
{assets.filter(isAssetTypeERC20).map((a) => (
|
||||
<AssetOption asset={a} />
|
||||
<AssetOption asset={a} key={a.id} />
|
||||
))}
|
||||
</RichSelect>
|
||||
)}
|
||||
|
||||
@@ -82,8 +82,9 @@ describe('FillsTable', () => {
|
||||
liquidityFee: '2',
|
||||
},
|
||||
});
|
||||
|
||||
render(<FillsTable partyId={partyId} rowData={[{ ...buyerFill }]} />);
|
||||
await act(async () => {
|
||||
render(<FillsTable partyId={partyId} rowData={[{ ...buyerFill }]} />);
|
||||
});
|
||||
await waitForGridToBeInTheDOM();
|
||||
await waitForDataToHaveLoaded();
|
||||
|
||||
@@ -119,8 +120,9 @@ describe('FillsTable', () => {
|
||||
liquidityFee: '1',
|
||||
},
|
||||
});
|
||||
|
||||
render(<FillsTable partyId={partyId} rowData={[buyerFill]} />);
|
||||
await act(async () => {
|
||||
render(<FillsTable partyId={partyId} rowData={[buyerFill]} />);
|
||||
});
|
||||
await waitForGridToBeInTheDOM();
|
||||
await waitForDataToHaveLoaded();
|
||||
|
||||
@@ -150,10 +152,13 @@ describe('FillsTable', () => {
|
||||
},
|
||||
aggressor: Schema.Side.SIDE_SELL,
|
||||
});
|
||||
|
||||
const { rerender } = render(
|
||||
<FillsTable partyId={partyId} rowData={[takerFill]} />
|
||||
);
|
||||
let rerenderer: (ui: React.ReactElement) => void;
|
||||
await act(async () => {
|
||||
const { rerender } = render(
|
||||
<FillsTable partyId={partyId} rowData={[takerFill]} />
|
||||
);
|
||||
rerenderer = rerender;
|
||||
});
|
||||
await waitForGridToBeInTheDOM();
|
||||
await waitForDataToHaveLoaded();
|
||||
|
||||
@@ -169,8 +174,9 @@ describe('FillsTable', () => {
|
||||
},
|
||||
aggressor: Schema.Side.SIDE_BUY,
|
||||
});
|
||||
|
||||
rerender(<FillsTable partyId={partyId} rowData={[makerFill]} />);
|
||||
await act(async () => {
|
||||
rerenderer(<FillsTable partyId={partyId} rowData={[makerFill]} />);
|
||||
});
|
||||
await waitForGridToBeInTheDOM();
|
||||
await waitForDataToHaveLoaded();
|
||||
|
||||
@@ -189,7 +195,7 @@ describe('FillsTable', () => {
|
||||
},
|
||||
aggressor: Schema.Side.SIDE_SELL,
|
||||
});
|
||||
act(() => {
|
||||
await act(async () => {
|
||||
render(<FillsTable partyId={partyId} rowData={[takerFill]} />);
|
||||
});
|
||||
await waitForGridToBeInTheDOM();
|
||||
@@ -206,11 +212,12 @@ describe('FillsTable', () => {
|
||||
await waitFor(() => {
|
||||
expect(feeCell).toBeInTheDocument();
|
||||
});
|
||||
act(() => {
|
||||
await act(async () => {
|
||||
userEvent.hover(feeCell as HTMLElement);
|
||||
await new Promise((res) => setTimeout(() => res(true), 1000));
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
await act(async () => {
|
||||
expect(screen.getByTestId('fee-breakdown-tooltip')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -51,7 +51,7 @@ export const FeesBreakdownPercentage = ({
|
||||
export const FeesBreakdown = ({
|
||||
fees,
|
||||
feeFactors,
|
||||
quoteName,
|
||||
symbol,
|
||||
decimals,
|
||||
}: {
|
||||
fees?: {
|
||||
@@ -60,7 +60,7 @@ export const FeesBreakdown = ({
|
||||
makerFee: string;
|
||||
};
|
||||
feeFactors?: Market['fees']['factors'];
|
||||
quoteName?: string;
|
||||
symbol?: string;
|
||||
decimals: number;
|
||||
}) => {
|
||||
if (!fees) return null;
|
||||
@@ -84,7 +84,7 @@ export const FeesBreakdown = ({
|
||||
</dd>
|
||||
)}
|
||||
<dd className="text-right col-span-2">
|
||||
{formatValue(fees.infrastructureFee)} {quoteName || ''}
|
||||
{formatValue(fees.infrastructureFee)} {symbol || ''}
|
||||
</dd>
|
||||
<dt className="col-span-2">{t('Liquidity fee')}</dt>
|
||||
{feeFactors && (
|
||||
@@ -95,7 +95,7 @@ export const FeesBreakdown = ({
|
||||
</dd>
|
||||
)}
|
||||
<dd className="text-right col-span-2">
|
||||
{formatValue(fees.liquidityFee)} {quoteName || ''}
|
||||
{formatValue(fees.liquidityFee)} {symbol || ''}
|
||||
</dd>
|
||||
<dt className="col-span-2">{t('Maker fee')}</dt>
|
||||
{feeFactors && (
|
||||
@@ -106,7 +106,7 @@ export const FeesBreakdown = ({
|
||||
</dd>
|
||||
)}
|
||||
<dd className="text-right col-span-2">
|
||||
{formatValue(fees.makerFee)} {quoteName || ''}
|
||||
{formatValue(fees.makerFee)} {symbol || ''}
|
||||
</dd>
|
||||
<dt className="col-span-2">{t('Total fees')}</dt>
|
||||
{feeFactors && (
|
||||
@@ -115,7 +115,7 @@ export const FeesBreakdown = ({
|
||||
</dd>
|
||||
)}
|
||||
<dd className="text-right col-span-2">
|
||||
{formatValue(totalFees)} {quoteName || ''}
|
||||
{formatValue(totalFees)} {symbol || ''}
|
||||
</dd>
|
||||
</dl>
|
||||
);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import { render, screen, act } from '@testing-library/react';
|
||||
import { OrderListManager } from './order-list-manager';
|
||||
import * as useDataProviderHook from '@vegaprotocol/react-helpers';
|
||||
import type { OrderFieldsFragment } from '../';
|
||||
@@ -19,7 +19,7 @@ const generateJsx = () => {
|
||||
);
|
||||
};
|
||||
|
||||
it('Renders a loading state while awaiting orders', () => {
|
||||
it('Renders a loading state while awaiting orders', async () => {
|
||||
jest.spyOn(useDataProviderHook, 'useDataProvider').mockReturnValue({
|
||||
data: [],
|
||||
loading: true,
|
||||
@@ -29,11 +29,13 @@ it('Renders a loading state while awaiting orders', () => {
|
||||
load: jest.fn(),
|
||||
totalCount: 0,
|
||||
});
|
||||
render(generateJsx());
|
||||
await act(async () => {
|
||||
render(generateJsx());
|
||||
});
|
||||
expect(screen.getByText('Loading...')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('Renders an error state', () => {
|
||||
it('Renders an error state', async () => {
|
||||
const errorMsg = 'Oops! An Error';
|
||||
jest.spyOn(useDataProviderHook, 'useDataProvider').mockReturnValue({
|
||||
data: [],
|
||||
@@ -44,7 +46,9 @@ it('Renders an error state', () => {
|
||||
load: jest.fn(),
|
||||
totalCount: undefined,
|
||||
});
|
||||
render(generateJsx());
|
||||
await act(async () => {
|
||||
render(generateJsx());
|
||||
});
|
||||
expect(
|
||||
screen.getByText(`Something went wrong: ${errorMsg}`)
|
||||
).toBeInTheDocument();
|
||||
@@ -63,6 +67,8 @@ it('Renders the order list if orders provided', async () => {
|
||||
load: jest.fn(),
|
||||
totalCount: undefined,
|
||||
});
|
||||
render(generateJsx());
|
||||
await act(async () => {
|
||||
render(generateJsx());
|
||||
});
|
||||
expect(await screen.findByText('OrderList')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { renderHook } from '@testing-library/react-hooks';
|
||||
import { renderHook, act } from '@testing-library/react-hooks';
|
||||
import type { MockedResponse } from '@apollo/client/testing';
|
||||
import { MockedProvider } from '@apollo/client/testing';
|
||||
import type { ReactNode } from 'react';
|
||||
@@ -33,6 +33,7 @@ const render = (mocks?: MockedResponse[]) => {
|
||||
const wrapper = ({ children }: { children: ReactNode }) => (
|
||||
<MockedProvider mocks={mocks}>{children}</MockedProvider>
|
||||
);
|
||||
|
||||
return renderHook(() => useVegaTransactionUpdater(), { wrapper });
|
||||
};
|
||||
|
||||
@@ -193,19 +194,23 @@ describe('useVegaTransactionManager', () => {
|
||||
it('updates order on OrderBusEvents', async () => {
|
||||
mockTransactionStoreState.mockReturnValue(defaultState);
|
||||
const { waitForNextUpdate } = render([mockedOrderBusEvent]);
|
||||
waitForNextUpdate();
|
||||
await waitForNextTick();
|
||||
expect(updateOrder).toHaveBeenCalledWith(orderBusEvent);
|
||||
await act(async () => {
|
||||
waitForNextUpdate();
|
||||
await waitForNextTick();
|
||||
expect(updateOrder).toHaveBeenCalledWith(orderBusEvent);
|
||||
});
|
||||
});
|
||||
|
||||
it('updates transaction on TransactionResultBusEvents', async () => {
|
||||
mockTransactionStoreState.mockReturnValue(defaultState);
|
||||
const { waitForNextUpdate } = render([mockedTransactionResultBusEvent]);
|
||||
waitForNextUpdate();
|
||||
await waitForNextTick();
|
||||
expect(updateTransactionResult).toHaveBeenCalledWith(
|
||||
transactionResultBusEvent
|
||||
);
|
||||
await act(async () => {
|
||||
waitForNextUpdate();
|
||||
await waitForNextTick();
|
||||
expect(updateTransactionResult).toHaveBeenCalledWith(
|
||||
transactionResultBusEvent
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it('updates withdrawal on WithdrawalBusEvents', async () => {
|
||||
@@ -215,11 +220,13 @@ describe('useVegaTransactionManager', () => {
|
||||
erc20WithdrawalApproval
|
||||
);
|
||||
const { waitForNextUpdate } = render([mockedWithdrawalBusEvent]);
|
||||
waitForNextUpdate();
|
||||
await waitForNextTick();
|
||||
expect(updateWithdrawal).toHaveBeenCalledWith(
|
||||
withdrawalBusEvent,
|
||||
erc20WithdrawalApproval
|
||||
);
|
||||
await act(async () => {
|
||||
waitForNextUpdate();
|
||||
await waitForNextTick();
|
||||
expect(updateWithdrawal).toHaveBeenCalledWith(
|
||||
withdrawalBusEvent,
|
||||
erc20WithdrawalApproval
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { act } from 'react-dom/test-utils';
|
||||
import { act } from '@testing-library/react';
|
||||
const zu = jest.requireActual('zustand'); // if using jest
|
||||
|
||||
// a variable to hold reset functions for all stores declared in the app
|
||||
@@ -6,7 +6,14 @@ const storeResetFns = new Set();
|
||||
|
||||
// when creating a store, we get its initial state, create a reset function and add it in the set
|
||||
export const create = (createState) => {
|
||||
const store = zu.create(createState);
|
||||
let store;
|
||||
if (typeof createState === 'function') {
|
||||
store = zu.create(createState);
|
||||
} else {
|
||||
store = (selector, equalityFn) =>
|
||||
zu.useStore(createState, selector, equalityFn);
|
||||
Object.assign(store, createState);
|
||||
}
|
||||
const initialState = store.getState();
|
||||
storeResetFns.add(() => store.setState(initialState, true));
|
||||
return store;
|
||||
|
||||
@@ -1,4 +1,11 @@
|
||||
import { fireEvent, render, screen } from '@testing-library/react';
|
||||
import {
|
||||
fireEvent,
|
||||
render,
|
||||
screen,
|
||||
act,
|
||||
waitFor,
|
||||
} from '@testing-library/react';
|
||||
import type { RenderResult } from '@testing-library/react';
|
||||
import type { MockedResponse } from '@apollo/client/testing';
|
||||
import { MockedProvider } from '@apollo/client/testing';
|
||||
import { Web3Container } from './web3-container';
|
||||
@@ -63,103 +70,111 @@ jest.mock('@web3-react/core', () => {
|
||||
};
|
||||
});
|
||||
|
||||
function setup(mock = networkParamsQueryMock) {
|
||||
return render(
|
||||
<EnvironmentProvider definitions={mockEnvironment}>
|
||||
<MockedProvider mocks={[mock]}>
|
||||
<Web3Container>
|
||||
<div>
|
||||
<div>Child</div>
|
||||
<div>{mockEthereumConfig.collateral_bridge_contract.address}</div>
|
||||
</div>
|
||||
</Web3Container>
|
||||
</MockedProvider>
|
||||
<Web3ConnectUncontrolledDialog />
|
||||
</EnvironmentProvider>
|
||||
);
|
||||
let renderResults: RenderResult;
|
||||
async function setup(mock = networkParamsQueryMock) {
|
||||
await act(async () => {
|
||||
renderResults = await render(
|
||||
<EnvironmentProvider definitions={mockEnvironment}>
|
||||
<MockedProvider mocks={[mock]}>
|
||||
<Web3Container>
|
||||
<div>
|
||||
<div>Child</div>
|
||||
<div>{mockEthereumConfig.collateral_bridge_contract.address}</div>
|
||||
</div>
|
||||
</Web3Container>
|
||||
</MockedProvider>
|
||||
<Web3ConnectUncontrolledDialog />
|
||||
</EnvironmentProvider>
|
||||
);
|
||||
});
|
||||
return renderResults;
|
||||
}
|
||||
|
||||
it('Prompt to connect opens dialog', async () => {
|
||||
mockHookValue = defaultHookValue;
|
||||
setup();
|
||||
describe('Web3Container', () => {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
it('Prompt to connect opens dialog', async () => {
|
||||
mockHookValue = defaultHookValue;
|
||||
await setup();
|
||||
await waitFor(async () => {
|
||||
expect(
|
||||
await screen.findByText('Connect your Ethereum wallet')
|
||||
).toBeInTheDocument();
|
||||
|
||||
expect(screen.getByText('Loading...')).toBeInTheDocument();
|
||||
expect(
|
||||
await screen.findByText('Connect your Ethereum wallet')
|
||||
).toBeInTheDocument();
|
||||
expect(screen.queryByText('Child')).not.toBeInTheDocument();
|
||||
expect(
|
||||
screen.queryByTestId('web3-connector-list')
|
||||
).not.toBeInTheDocument();
|
||||
await act(() => {
|
||||
fireEvent.click(screen.getByText('Connect'));
|
||||
});
|
||||
expect(screen.getByTestId('web3-connector-list')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
expect(screen.queryByText('Child')).not.toBeInTheDocument();
|
||||
expect(screen.queryByTestId('web3-connector-list')).not.toBeInTheDocument();
|
||||
it('Error message is shown', async () => {
|
||||
const message = 'Opps! An error';
|
||||
mockHookValue = { ...defaultHookValue, error: new Error(message) };
|
||||
await setup();
|
||||
|
||||
fireEvent.click(screen.getByText('Connect'));
|
||||
expect(screen.getByTestId('web3-connector-list')).toBeInTheDocument();
|
||||
});
|
||||
await waitFor(async () => {
|
||||
expect(
|
||||
await screen.findByText(`Something went wrong: ${message}`)
|
||||
).toBeInTheDocument();
|
||||
expect(screen.queryByText('Child')).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
it('Error message is shown', async () => {
|
||||
const message = 'Opps! An error';
|
||||
mockHookValue = { ...defaultHookValue, error: new Error(message) };
|
||||
setup();
|
||||
it('Checks that chain ID matches app ID', async () => {
|
||||
const expectedChainId = 4;
|
||||
mockHookValue = {
|
||||
...defaultHookValue,
|
||||
isActive: true,
|
||||
chainId: expectedChainId,
|
||||
};
|
||||
await setup();
|
||||
expect(
|
||||
await screen.findByText(`This app only works on Sepolia`)
|
||||
).toBeInTheDocument();
|
||||
expect(screen.queryByText('Child')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
expect(screen.getByText('Loading...')).toBeInTheDocument();
|
||||
expect(
|
||||
await screen.findByText(`Something went wrong: ${message}`)
|
||||
).toBeInTheDocument();
|
||||
expect(screen.queryByText('Child')).not.toBeInTheDocument();
|
||||
});
|
||||
it('Passes ethereum config to children', async () => {
|
||||
mockHookValue = {
|
||||
...defaultHookValue,
|
||||
isActive: true,
|
||||
};
|
||||
await setup();
|
||||
expect(
|
||||
await screen.findByText(
|
||||
mockEthereumConfig.collateral_bridge_contract.address
|
||||
)
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('Checks that chain ID matches app ID', async () => {
|
||||
const expectedChainId = 4;
|
||||
mockHookValue = {
|
||||
...defaultHookValue,
|
||||
isActive: true,
|
||||
chainId: expectedChainId,
|
||||
};
|
||||
setup();
|
||||
|
||||
expect(screen.getByText('Loading...')).toBeInTheDocument();
|
||||
expect(
|
||||
await screen.findByText(`This app only works on Sepolia`)
|
||||
).toBeInTheDocument();
|
||||
expect(screen.queryByText('Child')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('Passes ethereum config to children', async () => {
|
||||
mockHookValue = {
|
||||
...defaultHookValue,
|
||||
isActive: true,
|
||||
};
|
||||
setup();
|
||||
|
||||
expect(screen.getByText('Loading...')).toBeInTheDocument();
|
||||
expect(
|
||||
await screen.findByText(
|
||||
mockEthereumConfig.collateral_bridge_contract.address
|
||||
)
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('Shows no config found message if the network parameter doesnt exist', async () => {
|
||||
const mock: MockedResponse<NetworkParamsQuery> = {
|
||||
request: {
|
||||
query: NetworkParamsDocument,
|
||||
},
|
||||
result: {
|
||||
data: {
|
||||
networkParametersConnection: {
|
||||
edges: [
|
||||
{
|
||||
node: {
|
||||
__typename: 'NetworkParameter',
|
||||
key: 'nope',
|
||||
value: 'foo',
|
||||
it('Shows no config found message if the network parameter doesnt exist', async () => {
|
||||
const mock: MockedResponse<NetworkParamsQuery> = {
|
||||
request: {
|
||||
query: NetworkParamsDocument,
|
||||
},
|
||||
result: {
|
||||
data: {
|
||||
networkParametersConnection: {
|
||||
edges: [
|
||||
{
|
||||
node: {
|
||||
__typename: 'NetworkParameter',
|
||||
key: 'nope',
|
||||
value: 'foo',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
setup(mock);
|
||||
expect(screen.getByText('Loading...')).toBeInTheDocument();
|
||||
expect(await screen.findByText('No data')).toBeInTheDocument();
|
||||
};
|
||||
await setup(mock);
|
||||
expect(await screen.findByText('No data')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import { render, screen, act, waitFor } from '@testing-library/react';
|
||||
import { MockedProvider } from '@apollo/client/testing';
|
||||
import type { Account } from '@vegaprotocol/accounts';
|
||||
import { WithdrawFormContainer } from './withdraw-form-container';
|
||||
@@ -95,7 +95,7 @@ describe('WithdrawFormContainer', () => {
|
||||
afterEach(() => {
|
||||
jest.resetAllMocks();
|
||||
});
|
||||
it('should be properly rendered', () => {
|
||||
it('should be properly rendered', async () => {
|
||||
mockData = [
|
||||
{ ...account1 },
|
||||
{ ...account2 },
|
||||
@@ -186,50 +186,68 @@ describe('WithdrawFormContainer', () => {
|
||||
__typename: 'AccountBalance',
|
||||
},
|
||||
];
|
||||
const { container } = render(
|
||||
<MockedProvider>
|
||||
<WithdrawFormContainer {...props} />
|
||||
</MockedProvider>
|
||||
);
|
||||
expect(screen.getByTestId('select-asset')).toBeInTheDocument();
|
||||
const options = container.querySelectorAll('select[name="asset"] option');
|
||||
expect(options).toHaveLength(3);
|
||||
let rendererContainer: Element;
|
||||
await act(() => {
|
||||
const { container } = render(<WithdrawFormContainer {...props} />, {
|
||||
wrapper: MockedProvider,
|
||||
});
|
||||
rendererContainer = container;
|
||||
});
|
||||
await expect(screen.getByTestId('select-asset')).toBeInTheDocument();
|
||||
await waitFor(() => {
|
||||
const options = rendererContainer.querySelectorAll(
|
||||
'select[name="asset"] option'
|
||||
);
|
||||
expect(options).toHaveLength(3);
|
||||
});
|
||||
});
|
||||
|
||||
it('should display no data message', () => {
|
||||
it('should display no data message', async () => {
|
||||
mockData = null;
|
||||
render(
|
||||
<MockedProvider>
|
||||
<WithdrawFormContainer {...props} />
|
||||
</MockedProvider>
|
||||
);
|
||||
await act(() => {
|
||||
render(
|
||||
<MockedProvider>
|
||||
<WithdrawFormContainer {...props} />
|
||||
</MockedProvider>
|
||||
);
|
||||
});
|
||||
expect(
|
||||
screen.getByText('You have no assets to withdraw')
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should filter out zero balance account assets', () => {
|
||||
it('should filter out zero balance account assets', async () => {
|
||||
let rendererContainer: Element;
|
||||
mockData = [{ ...account1 }, { ...account2, balance: '0' }];
|
||||
const { container } = render(
|
||||
<MockedProvider>
|
||||
<WithdrawFormContainer {...props} />
|
||||
</MockedProvider>
|
||||
);
|
||||
await act(() => {
|
||||
const { container } = render(
|
||||
<MockedProvider>
|
||||
<WithdrawFormContainer {...props} />
|
||||
</MockedProvider>
|
||||
);
|
||||
rendererContainer = container;
|
||||
});
|
||||
expect(screen.getByTestId('select-asset')).toBeInTheDocument();
|
||||
const options = container.querySelectorAll('select[name="asset"] option');
|
||||
expect(options).toHaveLength(2);
|
||||
await waitFor(() => {
|
||||
const options = rendererContainer.querySelectorAll(
|
||||
'select[name="asset"] option'
|
||||
);
|
||||
expect(options).toHaveLength(2);
|
||||
});
|
||||
});
|
||||
|
||||
it('when no accounts have a balance should should display no data message', () => {
|
||||
it('when no accounts have a balance should should display no data message', async () => {
|
||||
mockData = [
|
||||
{ ...account1, balance: '0' },
|
||||
{ ...account2, balance: '0' },
|
||||
];
|
||||
render(
|
||||
<MockedProvider>
|
||||
<WithdrawFormContainer {...props} />
|
||||
</MockedProvider>
|
||||
);
|
||||
await act(() => {
|
||||
render(
|
||||
<MockedProvider>
|
||||
<WithdrawFormContainer {...props} />
|
||||
</MockedProvider>
|
||||
);
|
||||
});
|
||||
expect(
|
||||
screen.getByText('You have no assets to withdraw')
|
||||
).toBeInTheDocument();
|
||||
|
||||
@@ -23,13 +23,17 @@ export const WithdrawFormContainer = ({
|
||||
variables,
|
||||
});
|
||||
|
||||
const filteredAsset = data
|
||||
?.filter(
|
||||
(account) =>
|
||||
account.type === Types.AccountType.ACCOUNT_TYPE_GENERAL &&
|
||||
toBigNum(account.balance, account.asset.decimals).isGreaterThan(0)
|
||||
)
|
||||
.map((account) => account.asset);
|
||||
const filteredAsset = useMemo(
|
||||
() =>
|
||||
data
|
||||
?.filter(
|
||||
(account) =>
|
||||
account.type === Types.AccountType.ACCOUNT_TYPE_GENERAL &&
|
||||
toBigNum(account.balance, account.asset.decimals).isGreaterThan(0)
|
||||
)
|
||||
.map((account) => account.asset),
|
||||
[data]
|
||||
);
|
||||
const assets = filteredAsset?.length ? filteredAsset : null;
|
||||
return (
|
||||
<AsyncRenderer
|
||||
|
||||
@@ -92,9 +92,6 @@ export const WithdrawForm = ({
|
||||
}: {
|
||||
field: ControllerRenderProps<FormFields, 'asset'>;
|
||||
}) => {
|
||||
console.log('assets', assets.filter(isAssetTypeERC20));
|
||||
console.log('selected asset', selectedAsset);
|
||||
|
||||
return (
|
||||
<RichSelect
|
||||
data-testid="select-asset"
|
||||
@@ -110,7 +107,7 @@ export const WithdrawForm = ({
|
||||
hasError={Boolean(errors.asset?.message)}
|
||||
>
|
||||
{assets.filter(isAssetTypeERC20).map((a) => (
|
||||
<AssetOption asset={a} />
|
||||
<AssetOption key={a.id} asset={a} />
|
||||
))}
|
||||
</RichSelect>
|
||||
);
|
||||
|
||||
@@ -19195,9 +19195,9 @@ react-refresh@^0.11.0:
|
||||
integrity sha512-F27qZr8uUqwhWZboondsPx8tnC3Ct3SxZA3V5WyEvujRyyNv0VYPhoBg1gZ8/MV5tubQp76Trw8lTv9hzRBa+A==
|
||||
|
||||
react-remove-scroll-bar@^2.3.3:
|
||||
version "2.3.3"
|
||||
resolved "https://registry.yarnpkg.com/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.3.tgz#e291f71b1bb30f5f67f023765b7435f4b2b2cd94"
|
||||
integrity sha512-i9GMNWwpz8XpUpQ6QlevUtFjHGqnPG4Hxs+wlIJntu/xcsZVEpJcIV71K3ZkqNy2q3GfgvkD7y6t/Sv8ofYSbw==
|
||||
version "2.3.4"
|
||||
resolved "https://registry.yarnpkg.com/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.4.tgz#53e272d7a5cb8242990c7f144c44d8bd8ab5afd9"
|
||||
integrity sha512-63C4YQBUt0m6ALadE9XV56hV8BgJWDmmTPY758iIJjfQKt2nYwoUrPk0LXRXcB/yIj82T1/Ixfdpdk68LwIB0A==
|
||||
dependencies:
|
||||
react-style-singleton "^2.2.1"
|
||||
tslib "^2.0.0"
|
||||
|
||||
Reference in New Issue
Block a user