Compare commits

..
Author SHA1 Message Date
asiaznik 809bdc5e6c chore: focus on toggle (2562) 2023-01-19 16:27:33 +01:00
Art 5ccef2de5e feat: deposit and deal ticket transaction stores and toasts (#2495) 2023-01-19 13:10:52 +01:00
mattrussell36 4ce2924380 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-01-19 12:07:41 +00:00
Dexter Edwards f5b345636c feat: view apps as any pub key (#2548) 2023-01-19 11:16:21 +00:00
Matthew Russell c533c584da chore: tidy market setup scripts (#2657) 2023-01-19 09:52:38 +00:00
mattrussell36 c1ebda9274 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-01-19 06:06:49 +00:00
mattrussell36 1b2c0e8f8b chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-01-19 00:12:28 +00:00
Eddandm.ray df6b9023f1 feat(explorer): add liquidity provision submission and amend view (#2663)
Co-authored-by: m.ray <16125548+MadalinaRaicu@users.noreply.github.com>
2023-01-18 19:13:39 +00:00
mattrussell36 0702a97b6d chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-01-18 18:08:06 +00:00
m.ray c552a22af8 feat(#2560): show market code and name in the trading header (#2659) 2023-01-18 17:39:55 +00:00
m.rayandmacqbat fbdcd04096 chore(#2618): rename fields in ledger entry query (#2658) (#2660)
Co-authored-by: macqbat <maciek@vegaprotocol.io>
2023-01-18 13:14:06 +00:00
mattrussell36 89d0723eb3 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-01-18 12:07:58 +00:00
mattrussell36 f163a270d1 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-01-18 06:06:09 +00:00
mattrussell36 e04314608f chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-01-18 00:12:54 +00:00
mattrussell36 61c91c3542 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-01-17 18:06:53 +00:00
m.ray 2a9f3c7568 fix: liquidation est. should update on price change (#2649)
* fix: liquidation close out price needs to update when price updates on limit orders

* fix(#2624): add test case on limit sell order
2023-01-17 16:31:39 +00:00
Joe Tsang 2640b7d77d chore: update proposal detail tests (#2648) 2023-01-17 16:18:51 +00:00
Radosław Szpiech 2b229cd1e7 chore: add package json to hash (#2651) 2023-01-17 17:01:01 +01:00
Radosław Szpiech d098ee671e chore: add job name to log file name (#2650) 2023-01-17 16:47:03 +01:00
2c3c685522 feat(#2255): capsule tests for trading (#2498)
* feat: improve proposal types, add faucet for trading asset

* chore: yarn lock update

* fix: capsule tests to avoid jsx import

* chore: throw error so that ethereum setup failure is caught

* chore: org files and add logging

* chore: revert current trading-e2e .env file

* chore: add .env.capsule for trading tests on capsule

* chore: remove unused nx command

* chore: remove try catchs and allow errors to be caught by cypress

* chore: add slow tag for capsule test

* feat: move create market setup to libs/cypress

* chore: clearly separate vars meant for capsule with vars meant for non-capsule

* feat: remove hardcoded urls and use config obj

* chore: fix missing comma in genesis.tmpl

* chore: added limit order edit price test

* chore: remove encoding of transaction, update proposal tx trigger ratio to string

* chore: fix date and add date verification for update order

* chore: formatting fix

* chore: adjust trading app tests and workflows to be run in capsule in ci (#2574)

* chore: adjust trading app tests and workflows to be run in capsule in ci

* chore: fix token tests

* chore: increase timeout as it fails in ci

* chore: move market check to before function

* chore: add capsule logs to trading

* chore: increase proposal close time, also break up script for better debugging

* chore: bump up ethereum account setup timeout

* chore: suppress fetch logs to help with debugging

* chore: revert cy wraps now with suppressed logging

* chore: decrease tick amount

* chore: promise with timeout for better debugging

* chore: bump individual timeouts

* chore: bump timeout for tx wait promises

* chore: minor fix

* fix: leave Promise<any>

* fix: leave Promise<any>

* chore: fix home fail

Co-authored-by: Joe <joe@vega.xyz>
Co-authored-by: Dariusz Majcherczyk <dariusz.majcherczyk@gmail.com>
Co-authored-by: Radosław Szpiech <szpiechrados@gmail.com>
Co-authored-by: Madalina Raicu <madalina@raygroup.uk>
2023-01-17 16:33:43 +01:00
Sam Keen b96e6ee9ce feat(2633): remove unneeded values in validator detail view (#2644)
* feat(2633): remove overstaked amount

* feat(2633): remove performance score
2023-01-17 15:01:51 +00:00
Bartłomiej Głownia 1a6266e2ec chore: fix data and no data message update issues in trading data grids (#2620) 2023-01-17 15:01:24 +00:00
m.rayandRado c70ec7676e fix(#2569): market id copy update (#2627)
* fix(#2569): market id copy update

* fix: enable cell text selection on ag-grid by default

* chore: fix partially filled order test

Co-authored-by: Rado <szpiechrados@gmail.com>
2023-01-17 13:59:34 +00:00
macqbat 5e97391d53 chore(2559): filter ledger entries by account type (#2631)
* chore: ledger entries - add account type filters

* chore: ledger entries filtering - add int test

* chore: ledger entries filtering - fix linter fails

* chore: ledger entries filtering - fix int test failing

* chore: ledger entries filtering - fix int test failing
2023-01-17 13:44:23 +00:00
mattrussell36 8eebd814ae chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-01-17 12:08:17 +00:00
Ciaran McGhie bd754d1103 chore: 0 decimal places for % target stake column (#2588) 2023-01-17 11:10:20 +00:00
m.ray e362d06b5a fix: asset dropdown list (#2622)
* fix(#2556): asset dropdown list fix

* fix: add ID to market list and make cell copyable

* fix: revert market list table
2023-01-17 10:02:46 +00:00
macqbatandMatthew Russell 45a4dd7009 feat: mobile navbar on Console (#2547)
* feat: mobile navbar on Console

* feat: mobile navbar on Console - adjust unit test

* feat: mobile navbar on Console - adjust unit test

* feat: mobile navbar on Console - adjust themes

* feat: mobile navbar on Console - add some unit tests

* feat: mobile navbar on Console - refactor solution

* feat: mobile navbar on Console - adjust int tests

* feat: mobile navbar on Console - adjust styling

* feat: mobile navbar on Console - move close button into the drawer

* feat: mobile navbar on Console - adjust int tests

* chore: close drawe after navigation

* chore: mobile navbar on Console - adjust unit tests

* chore: mobile navbar on Console - adjust unit tests

Co-authored-by: Matthew Russell <mattrussell36@gmail.com>
2023-01-17 10:59:12 +01:00
Edd ff53e5e841 feat(explorer): batch market instructions (#2617)
* feat(explorer): add basic table for all types in a batch
* feat(explorer): use marketlink for market ids in batch
* feat(explorer): add ordersummary
* feat(explorer): improve caching with some slight overfetching and cache config
2023-01-17 09:51:42 +00:00
Sam Keen cd0ed05eb8 feat(2629): validator table - adjust the overstaked amount calculation (#2630) 2023-01-17 09:01:44 +00:00
mattrussell36 89f41a4500 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-01-17 06:08:44 +00:00
mattrussell36 38fdc91444 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-01-17 00:12:39 +00:00
mattrussell36 f8f523eb9c chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-01-16 18:08:44 +00:00
Bartłomiej Głownia 5a0da4e158 feat: update market data components when they are in view (#2607) 2023-01-16 17:51:30 +00:00
candida-d 4f7b589cbd fix: make validator text terminology match (#2626) 2023-01-16 17:36:40 +00:00
m.rayandcandida-d e5bf54ca02 fix: market expiry & trading live copy (#2619)
* fix(#2602): market expiry tooltip

* fix(#2612): update live trading copy

* fix: show - liq. supplied and fix lint issue

* fix: on-chain functionality tooltip

* Update apps/trading/components/welcome-dialog/welcome-notice-dialog.tsx

Co-authored-by: candida-d <62548908+candida-d@users.noreply.github.com>

* fix: propose a market link

Co-authored-by: candida-d <62548908+candida-d@users.noreply.github.com>
2023-01-16 17:36:06 +00:00
Edd 866a11fd89 feat(explorer): bring back oracles view (#2551)
* build(explorer): enable oracles view by default

* feat(explorer): restore and update oracles view

* feat(explorer): dumb and in need of refactor oracles tables

* feat(oracles): disable key view and rejig table

* feat(oracles): filter all JSON views to remove typename

* chore(explorer): oracles component refactor

* test(explorer): add tests for oracle markets component

* test(explorer): add tests for oracle signers component
2023-01-16 14:03:02 +00:00
Matthew Russell b10effa3c7 chore: add link to etherscan for asset dialog contract address (#2561)
* chore: add link to etherscan for asset dialog contract address

* chore: use useEtherscanLink hook

* chore: fix unused import
2023-01-16 13:11:51 +00:00
Sam Keen 1f1ca510f4 feat(2432): minor proposal detail display improvements (#2616)
* feat(2432): minor proposal detail display improvements

* feat(2432): unit tests updated
2023-01-16 12:35:10 +00:00
Bartłomiej Głownia 06151c0592 feat: add marketId to orders query (#2536)
* feat: add marketId to orders query

* feat: move filters handling from useOrderListData to ordersProvider
2023-01-16 12:26:50 +00:00
mattrussell36 12a799a2fd chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-01-16 12:08:50 +00:00
mattrussell36 e91a37684a chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-01-16 06:06:37 +00:00
mattrussell36 b4a0e74457 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-01-16 00:12:40 +00:00
mattrussell36 11ba5e733e chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-01-15 18:07:24 +00:00
mattrussell36 315c463eec chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-01-15 12:06:07 +00:00
mattrussell36 62bfbd6822 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-01-15 06:07:31 +00:00
mattrussell36 290c2e3d42 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-01-15 00:10:47 +00:00
mattrussell36 c67daf885c chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-01-14 18:08:19 +00:00
mattrussell36 c3944128eb chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-01-14 12:08:40 +00:00
mattrussell36 c63d2a9bc6 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-01-14 06:06:06 +00:00
mattrussell36 e00998f9e3 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-01-14 00:10:11 +00:00
mattrussell36 61a5098776 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-01-13 18:06:05 +00:00
Matthew Russell 9203c81084 chore: fix governance app link for mirror env 2023-01-13 08:17:56 -08:00
m.rayandMatthew Russell 8864696783 chore: merge release/testnet back in develop (#2613)
* chore: add mirror config for trading

* chore: fix url for mainnet mirror in network config

* fix: remove link if no market id

Co-authored-by: Matthew Russell <mattrussell36@gmail.com>
2023-01-13 14:10:31 +00:00
mattrussell36 99d338f50a chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-01-13 12:06:53 +00:00
macqbat 2b28ff6c18 chore: update fills tab - add tooltip with fees breakdown (#2606)
* chore: update fills tab - add tooltip with fees breakdown

* chore: update fills tab - fix failing unit test

* chore: update fills tab - add unit test

* chore: update fills tab - adjust int test
2023-01-13 10:44:01 +00:00
Edd 6d3795f1f4 build(explorer): checkout mirror conf from mirror release branch 2023-01-13 10:21:56 +00:00
mattrussell36 c0b84a88d3 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-01-13 06:07:37 +00:00
mattrussell36 5b0d799a00 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-01-13 00:11:40 +00:00
Matthew Russell 9e6ecbc558 chore: fix url for mainnet mirror in network config 2023-01-12 13:03:08 -08:00
mattrussell36 c3de7f12f0 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-01-12 18:06:47 +00:00
Joe Tsang 473fdf8466 test(2578): download proposal json (#2596)
* chore: add test for downloading proposal json

* chore: remove seconds from json file names
2023-01-12 14:36:09 +00:00
Sam Keen 47c0ec01eb feat(2601): token homepage proposals should have their width halved on larger breakpoints (#2603) 2023-01-12 14:18:17 +00:00
mattrussell36 6410cb54b6 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-01-12 12:06:50 +00:00
Dexter EdwardsandJoe b8ff3d3050 feat(2093): token sub nav (#2549)
* feat: implement subnav using exisitng DD component

* chore: make nav as per design

* chore: fix conflicts

* style: make css for trigger match designs

* chore: add border to subnav card

* chore: reogranise files

* chore: remove unused imports

* fix: minor rendering errors

* test: add first tests

* chore: fix failing tests from nav update

* test: add unit tests for nav

* style: lint

* style: make navbar theme work properly

* style: lint

* chore: remove unnecessary click on header

* fix: move route config to ts only file

* test: tidy up test logic

* feat: add nav drop down to ui toolkit

* fix: broken routing in places

* style: lint

Co-authored-by: Joe <joe@vega.xyz>
2023-01-12 11:45:22 +00:00
Dexter d474a502a8 ci: force build 2023-01-12 11:20:31 +00:00
macqbat 06e1b3663d chore: volume in chart enabled by default (#2577)
* chore: volume in chart enabled by default

* chore: volume in chart enabled by default - add unit test
2023-01-12 11:49:57 +01:00
Sam Keen 38ff2c24e4 fix(2590): download JSON for some proposal types not working if proposal terms is empty (#2591) 2023-01-12 09:40:31 +00:00
mattrussell36 2689bbdcd2 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-01-12 06:07:49 +00:00
mattrussell36 c7b4465adf chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-01-12 00:12:29 +00:00
mattrussell36 3f6913ab39 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-01-11 18:08:00 +00:00
m.ray 31ffa306eb fix(#2554): close out formatting (#2584)
* fix(#2530): fix use order close out - formatting to be done in component

* fix: format number in console-lite
2023-01-11 17:10:35 +00:00
Radosław Szpiech 13ab03c459 chore: remove liquidity-provision-dashobard-e2e as it is not maintained (#2589) 2023-01-11 18:02:16 +01:00
Radosław Szpiech dd2e3a6556 chore: remove stats and stats-e2e by burning them to the scorched ground (#2586) 2023-01-11 17:45:08 +01:00
Bartłomiej Głownia 4608683bde feat(2408): trading data grid snags (#2513)
* feat: trading data grid snags

* feat: fix e2e tests, fix use order list data avoid rerender condition
2023-01-11 15:43:52 +01:00
mattrussell36 1f96ccea68 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-01-11 12:08:56 +00:00
mattrussell36 c055be1146 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-01-11 06:06:35 +00:00
mattrussell36 a316eaae9d chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-01-11 00:12:29 +00:00
EddandMatthew Russell 996c4ac4d5 chore: regenerate types from version 66 (#2568)
* chore: regenerate types from version 66

* chore: fix transfer type mappings

* chore: update triggering ratio to use strings

* chore: regen explorer types

* build(explorer): fix linting error in regenerated explorer types

* chore: remove expiry fields from tests and fix imports to use updated generated code

Co-authored-by: Matthew Russell <mattrussell36@gmail.com>
2023-01-10 14:21:54 -08:00
mattrussell36 560e4a4040 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-01-10 18:06:26 +00:00
Matthew Russell d98ce18f9e chore: rename candles tab to chart (#2558) 2023-01-10 16:59:08 +01:00
m.ray 0fd1739ce5 fix(#2553): show rejection reason & orderbook fixed dp (#2563)
* fix: orderbook fixed dp - and show rejection reason

* fix: show rejection reason in order list

* fix: fix parked rejection reason test

* fix: remove not covered part
2023-01-10 13:41:37 +00:00
mattrussell36 cf43566bde chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-01-10 12:06:41 +00:00
mattrussell36 24a9712e91 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-01-10 06:08:21 +00:00
mattrussell36 03f5b5e78b chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-01-10 00:12:45 +00:00
Matthew Russell 9552fbc033 chore: change text for TRANSFER_TYPE_WIN (#2522) 2023-01-09 10:55:48 -08:00
mattrussell36 74a4a60195 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-01-09 18:06:34 +00:00
m.ray ef35999091 fix(#2532): show why market is in liquidity monitoring auction when liquidity supplied is > target (#2543)
* fix: add sufficient liquidity still auction message

* fix: add sufficient liquidity still auction message

* fix(#2532): update message

* fix(#2532): update liquidity data provider

* fix: lint issue

* fix: try update test
2023-01-09 14:39:03 +00:00
mattrussell36 cba44d7eb1 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-01-09 12:08:53 +00:00
Sam Keen c5b72e5a64 feat(2211): download proposal forms data as json (#2521)
* feat(2211): view proposal forms data as json

* feat(2211): removed the vega wallet barrier to viewing proposal forms

* feat(2211): tweaked the json viewing to download the proposal form json, with a useful title (proposal title and readable datetime)

* feat(2211): removed unwanted NX_SENTRY_DSN config from dev environments

* feat(2211): unit test for download-json.ts

* feat(2211): unit test for proposal-form-download-json.spec.tsx

* feat(2211): simplified unit test for proposal-form-download-json.spec.tsx

* feat(2211): removed unneeded unit test

* feat(2211): revoked change that was causing test failures
2023-01-09 09:48:47 +00:00
mattrussell36 863c288e0d chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-01-09 06:08:54 +00:00
mattrussell36 a97512cc7b chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-01-09 00:12:55 +00:00
mattrussell36 0884654c1e chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-01-08 18:07:31 +00:00
mattrussell36 5b4440f767 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-01-08 12:08:35 +00:00
mattrussell36 cfbea6e894 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-01-08 06:07:45 +00:00
mattrussell36 774a7d697f chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-01-08 00:13:00 +00:00
mattrussell36 dbb08acba3 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-01-07 18:07:40 +00:00
mattrussell36 6db201c1de chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-01-07 12:06:13 +00:00
mattrussell36 54cd8324ed chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-01-07 06:07:19 +00:00
mattrussell36 562e2b5cb2 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-01-07 00:10:12 +00:00
mattrussell36 ca2647d4d9 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-01-06 18:06:07 +00:00
m.rayandMatthew Russell bbdc64582a feat(#2465): vega-pink across frontend-monorepo (#2476)
* feat(#2465): change red to vega pink

* feat(#2465): change red to vega pink part 2

* fix: update vega.red to vega.pink

* feat: color the charts

* feat: color the charts

* feat: color the charts

* fix: use dark pink and green

* fix: use dark pink and green

* feat(#2467): color long/short toggle and place order button

* fix: colors wick showing within candle

* fix: remove opacity from chart

* fix: use vega dark pink and green for vol cell

* fix: toggle and button colors

* feat(#2465): toggle peer checked text white

* fix: add liquidity supplied gap-2 in tooltip data grid

* fix: add indicator temporarily

* chore: update colors

* chore: update from x-dark to vega-x

* fix: rename symbols

* chore: update sell candles to only use stroke as they are solidly filled

* fix: remove use state

* fix: remove network account types

Co-authored-by: Matthew Russell <mattrussell36@gmail.com>
2023-01-06 16:58:55 +00:00
m.rayandRado dabae31bfd fix(#2538): stake to ccy volume renaming (#2546)
* fix(#2538): stakeToCcyVolume param

* chore: fix for network params query error

Co-authored-by: Rado <szpiechrados@gmail.com>
2023-01-06 15:35:32 +00:00
Radosław Szpiech 5b42a6ad35 chore: update market.liquidity.stakeToCcyVolume in genesis file (#2545) 2023-01-06 15:21:35 +01:00
mattrussell36 870ae13b4c chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-01-06 12:07:43 +00:00
mattrussell36 4c32a21f28 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-01-06 06:08:04 +00:00
mattrussell36 2ea603e59e chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-01-06 00:10:00 +00:00
mattrussell36 3e485e3841 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-01-05 18:06:39 +00:00
daro-maj 7766b3f118 chore: update token e2e readme (#2504)
* chore: update readme for token-e2e tests setup

* chore: formatting fix

* chore: fix style

* chore: update readme for explorer-e2e tests

* chore: rework
2023-01-05 18:00:29 +01:00
Ciaran McGhie 8db3b01284 feat: add market close datetime to lp dashboard (#2529)
* feat: add proposal data from governance to market liquidity provider

We need data from this API to be able to tell when a market will close,
and when opening auctions will end.

* feat: add closing time to lp-dashboard market list page

Adds a 'closing time' column to the market list page on the dashboard.
2023-01-05 15:28:30 +00:00
Bartłomiej Głownia 4fbd2e5eba feat(#2323): don't allow cancel all if there are no cancellable orders (#2472) 2023-01-05 13:11:58 +00:00
Edd 53d3d5ac1c feat(explorer): improve empty states and loading indicators (#2496)
* feat(explorer): initial empty list component on block txs list

* feat(explorer): messages formatting

* chore(explorer): update generated types

* feat(explorer): empty state for markets and assets and governance and blocks and txs

* feat(explorer): use loader rather than the word loading

* feat(explorer): use loader rather than the word loading in more places

* feat(explorer): empty state appears below headers in more pages

* feat(explorer): txs per block update

* feat(explorer): update tests to match new messages

* test(explorer): add test for loading and empty states for assets

* test(explorer): add test for loading and empty states for more routes

* test(explorer): change loading detector
2023-01-05 13:10:42 +00:00
Matthew RussellandMadalina Raicu 35de756120 fix(#2515): use accounts nested under LP provision (#2523)
* fix: use accounts nested under lp provision

* fix: add getLiquidityProvision test

Co-authored-by: Madalina Raicu <madalina@raygroup.uk>
2023-01-05 13:09:45 +00:00
mattrussell36 f89cf31982 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-01-05 12:06:22 +00:00
mattrussell36 8976a40dc8 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-01-05 06:07:37 +00:00
mattrussell36 7669aeb218 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-01-05 00:10:03 +00:00
mattrussell36 6e996221b2 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-01-04 18:08:45 +00:00
botond 117c0c1306 fix: wallet connect save url to config (#2518) 2023-01-04 16:34:35 +00:00
daro-majandm.ray e56d7bc2d5 chore: cover ac 7003-MORD-016 by e2e test (#2514)
* chore: cover ac 7003-MORD-016 by e2e test

* chore: formatting fix

* Update apps/trading-e2e/src/integration/trading-orders.cy.ts

Co-authored-by: m.ray <16125548+MadalinaRaicu@users.noreply.github.com>

Co-authored-by: m.ray <16125548+MadalinaRaicu@users.noreply.github.com>
2023-01-04 16:04:28 +01:00
Dexter 85ec66a9e2 chore: update mirror URL 2023-01-04 14:31:28 +00:00
macqbat ba14ead8fc feat(1784): maintenance page (#2505)
* feat: maintance page

* feat: maintenance page - add styling and animations

* feat: maintenance page - add skip of node inits

* feat: maintenance page - add unit test

* feat: maintenance page - add unit test
2023-01-04 13:45:50 +01:00
Art 1211d27011 fix: error message consitency (2486) (#2507)
fix: error message consitency (2486)
2023-01-04 13:25:11 +01:00
mattrussell36 904a1b7805 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-01-04 12:08:46 +00:00
Dexter Edwards 66a8c659b4 chore: sentry dsn (#2499)
* chore: update readme to be correct

* chore: add sentry DSN to env files

* chore: use correct dsn
2023-01-04 09:16:37 +00:00
mattrussell36 f1a437500e chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-01-04 06:08:37 +00:00
mattrussell36 29ced0b460 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-01-04 00:10:49 +00:00
Art e45265ac71 chore: chain id to name (714) (#2503) 2023-01-03 14:54:54 -08:00
Art cc3c339cc3 fix: asset dialog overflow (2488) (#2497) 2023-01-03 10:37:23 -08:00
mattrussell36 6f95989569 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-01-03 18:07:04 +00:00
Radosław Szpiech 14f0c5a5fb chore: change connect vega wallet to set vega wallet config in local storage (#2502) 2023-01-03 14:56:33 +01:00
mattrussell36 0167a42990 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-01-03 12:06:25 +00:00
Joe Tsang 1d65c4791c chore: skip flow test (#2500) 2023-01-03 11:32:39 +00:00
mattrussell36 8a1fe6e5b1 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-01-03 06:08:12 +00:00
mattrussell36 437f7ef807 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-01-03 00:12:00 +00:00
mattrussell36 2b3683d4a4 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-01-02 18:07:24 +00:00
macqbat 6c84153cdb chore: redirect to last visited market instead first from the list (#2489)
* chore: redirect to last visited market instead first from the list

* chore: redirect to last visited market instead first from the list - add int tests

* feat: redirect to last visited market instead first from the list - refactor solution

* feat: redirect to last visited market instead first from the list - fix failing int test

* feat: redirect to last visited market instead first from the list - fix failing int test

* feat: redirect to last visited market instead first from the list - fix failing int test

* feat: redirect to last visited market instead first from the list - use immer in globalStore

* chore: redirect to last visited market - improve use of zustand
2023-01-02 17:01:06 +01:00
mattrussell36 ccce5a2848 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-01-02 12:08:44 +00:00
mattrussell36 5673620fbd chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-01-02 06:08:34 +00:00
mattrussell36 41d6940b89 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-01-02 00:12:58 +00:00
mattrussell36 84150bec8c chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-01-01 18:08:33 +00:00
mattrussell36 5a311d14c9 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-01-01 12:08:31 +00:00
mattrussell36 0f152a4b30 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-01-01 06:05:51 +00:00
mattrussell36 45d8b537a3 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-01-01 00:14:06 +00:00
mattrussell36 dc41be5715 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2022-12-31 18:08:31 +00:00
mattrussell36 a85cf66419 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2022-12-31 12:06:36 +00:00
mattrussell36 6e82e917ac chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2022-12-31 06:06:27 +00:00
mattrussell36 8dfdccd4aa chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2022-12-31 00:09:38 +00:00
Matthew Russell 82833cc06a chore: fix and improve types for market proposal (#2493)
* chore: fix and improve types for market proposal

* chore: remove unused type definition
2022-12-30 10:05:48 -08:00
botondandMatthew Russell aac3799b14 chore: add wallet client (#2462)
* chore: migrate to wallet client

* fix: bump client with new esm release version

* fix: add new no client error

* fix: bump wallet client to 0.1.2 and remove empty params from method calls

* fix: bump wallet client

* fix: format

* fix: reset client on url change

* fix: trading-deal-ticket tests after wallet client added

* fix: amend and cancel order tests, global connect tests

* chore: fix typescript error

* fix: connect wallet before mobile view test for console-lite

Co-authored-by: Matthew Russell <mattrussell36@gmail.com>
2022-12-30 10:05:27 -08:00
mattrussell36 0d1c9ff0f1 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2022-12-30 12:07:23 +00:00
Edd f8eec8a056 fix(explorer): fix resizing callout (#2492)
* fix(explorer): prevent layout thrashing on stats page
2022-12-30 11:40:47 +00:00
Edd ee6f26d473 feat(explorer): use block height filter 2022-12-30 09:53:07 +00:00
mattrussell36 de62d2d972 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2022-12-30 06:08:01 +00:00
Matthew Russell 955e233c46 fix: dark class being removed on token due to theme switcher changes (#2484)
* fix: dark class being removed on token due to theme switcher changes

* chore: check for window before reading from document classlist
2022-12-29 21:01:37 -08:00
Matthew RussellandEdd 0bafa4c7d5 chore: add readmes for token and explorer e2e (#2485)
* chore: add readmes for token and explorer e2e

* fix: typo fix in explorer readme

* fix: typo fix in token readme

Co-authored-by: Edd <edd@vega.xyz>
2022-12-29 20:48:04 -08:00
mattrussell36 045529dc38 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2022-12-30 00:11:08 +00:00
mattrussell36 5ca3e4d90f chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2022-12-29 18:08:27 +00:00
mattrussell36 9048d41f72 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2022-12-29 12:08:48 +00:00
mattrussell36 d731c4797c chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2022-12-29 06:08:36 +00:00
mattrussell36 4a931ca134 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2022-12-29 00:11:03 +00:00
mattrussell36 3398b15d8a chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2022-12-28 18:08:02 +00:00
m.rayandMatthew Russell 1e98ecbd21 feat(#2367): liquidity supplied info in market header (#2481)
* feat(#2367): market liquidity supplied in trade market header

* feat(#2467): calculate LP status

* fix: add LP view link

* fix: add LP view link

* feat(#2367): show liquidity supplied percentage

* feat(#2367): show liquidity supplied percentage

* fix: liquidity-utils test needs big number

* feat(#2456): liquidity status marker

* feat(#2456): liquidity indicator

* feat(#2367): update props lp

* fix: use market data directly

* feat(#2367): move data grid in react-helpers

* feat(#2367): move data grid in react-helpers

* fix: indicator commented

* chore: remove unnecessary styles

* test: update test name

Co-authored-by: Matthew Russell <mattrussell36@gmail.com>
2022-12-28 09:37:19 -08:00
daro-maj a9c7c2bb46 chore: cover7003 mord e2e tests (#2487)
* chore:  tests will be added after the proper implementation is prepared

* chore: formatting fix
2022-12-28 15:18:06 +01:00
mattrussell36 ba2c71de67 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2022-12-28 12:06:41 +00:00
551 changed files with 15055 additions and 7096 deletions
@@ -1,6 +1,3 @@
inputs:
passphrase:
description: 'Wallet password'
outputs:
token:
description: 'api-token of wallet'
@@ -12,8 +12,6 @@ on:
options:
- console-lite-e2e
- explorer-e2e
- liquidity-provision-dashboard-e2e
- stats-e2e
- token-e2e
- trading-e2e
tags:
@@ -12,6 +12,6 @@ jobs:
uses: ./.github/workflows/tests-dispatcher.yml
secrets: inherit
with:
project: '[console-lite-e2e, explorer-e2e, liquidity-provision-dashboard-e2e, stats-e2e, token-e2e, trading-e2e]'
project: '[console-lite-e2e, explorer-e2e, token-e2e, trading-e2e]'
tags: --env.grepTags '[ @smoke, @regression, @slow ]'
night-run: true
+1 -1
View File
@@ -29,7 +29,7 @@ jobs:
path: |
frontend-monorepo/node_modules
/home/runner/.cache/Cypress
key: node_modules_cypress-${{ hashFiles('frontend-monorepo/yarn.lock') }}
key: node_modules_cypress-${{ hashFiles('frontend-monorepo/yarn.lock', 'frontend-monorepo/package.json') }}
# Install frontend dependencies
- name: Install root dependencies
@@ -36,23 +36,22 @@ jobs:
path: |
frontend-monorepo/node_modules
/home/runner/.cache/Cypress
key: node_modules_cypress-${{ hashFiles('frontend-monorepo/yarn.lock') }}
key: node_modules_cypress-${{ hashFiles('frontend-monorepo/yarn.lock', 'frontend-monorepo/package.json') }}
# Install frontend dependencies
- name: Install root dependencies
run: yarn install --frozen-lockfile
working-directory: frontend-monorepo
######
## Setup a Vega wallet for our user
######
- name: Set up Vegawallet
- name: Run Vegacapsule network
uses: ./frontend-monorepo/.github/actions/run-vegacapsule
- name: Set up Vegawallet for capsule
id: setup-vega
uses: ./frontend-monorepo/.github/actions/setup-vegawallet
with:
recovery: ${{ secrets.TRADING_TEST_VEGA_WALLET_RECOVERY }}
passphrase: ${{ secrets.CYPRESS_TRADING_TEST_VEGA_WALLET_PASSPHRASE }}
uses: ./frontend-monorepo/.github/actions/setup-vegawallet-docker
# To make sure that all Cypress binaries are installed properly
- name: Install cypress bins
@@ -63,7 +62,6 @@ jobs:
run: npx nx run console-lite-e2e:e2e ${{ inputs.skip-cache }} --record --key ${{ secrets.CYPRESS_RECORD_KEY }} --browser chrome ${{ inputs.tags }}
working-directory: frontend-monorepo
env:
CYPRESS_TRADING_TEST_VEGA_WALLET_PASSPHRASE: ${{ secrets.CYPRESS_TRADING_TEST_VEGA_WALLET_PASSPHRASE }}
CYPRESS_SLACK_WEBHOOK: ${{ secrets.CYPRESS_SLACK_WEBHOOK }}
CYPRESS_ETH_WALLET_MNEMONIC: ${{ secrets.CYPRESS_ETH_WALLET_MNEMONIC }}
CYPRESS_VEGA_WALLET_API_TOKEN: ${{ steps.setup-vega.outputs.token }}
+5 -12
View File
@@ -44,29 +44,23 @@ jobs:
path: |
frontend-monorepo/node_modules
/home/runner/.cache/Cypress
key: node_modules_cypress-${{ hashFiles('frontend-monorepo/yarn.lock') }}
key: node_modules_cypress-${{ hashFiles('frontend-monorepo/yarn.lock', 'frontend-monorepo/package.json') }}
# Install frontend dependencies
- name: Install root dependencies
run: yarn install --frozen-lockfile
working-directory: frontend-monorepo
#######
## Build and run Vegacapsule network
#######
- name: Build and run Vegacapsule network
uses: ./frontend-monorepo/.github/actions/run-vegacapsule
######
## Setup a Vega wallet for our user
######
- name: Set up Vegawallet for docker
- name: Run Vegacapsule network
uses: ./frontend-monorepo/.github/actions/run-vegacapsule
- name: Set up Vegawallet for capsule
id: setup-vega
uses: ./frontend-monorepo/.github/actions/setup-vegawallet-docker
with:
passphrase: ${{ secrets.CYPRESS_TRADING_TEST_VEGA_WALLET_PASSPHRASE }}
######
## Run some tests
@@ -81,7 +75,6 @@ jobs:
run: npx nx run explorer-e2e:e2e ${{ inputs.skip-cache }} --record --key ${{ secrets.CYPRESS_RECORD_KEY }} --browser chrome ${{ inputs.tags }}
working-directory: frontend-monorepo
env:
CYPRESS_TRADING_TEST_VEGA_WALLET_PASSPHRASE: ${{ secrets.CYPRESS_TRADING_TEST_VEGA_WALLET_PASSPHRASE }}
CYPRESS_SLACK_WEBHOOK: ${{ secrets.CYPRESS_SLACK_WEBHOOK }}
CYPRESS_ETH_WALLET_MNEMONIC: ${{ secrets.CYPRESS_ETH_WALLET_MNEMONIC }}
CYPRESS_NIGHTLY_RUN: ${{ inputs.night-run }}
@@ -1,47 +0,0 @@
name: Cypress - liquidity provision dashboard
on:
workflow_call:
inputs:
trigger:
required: true
type: string
default: 'false'
jobs:
liquidity-provision-dashboard-e2e:
timeout-minutes: 10
if: ${{ inputs.trigger == 'true' }}
runs-on: self-hosted
steps:
# Checkout front ends
- name: Checkout frontend mono repo
uses: actions/checkout@v3
with:
fetch-depth: 0
path: './frontend-monorepo'
# Restore node_modules from cache if possible
- name: Restore node_modules from cache
uses: actions/cache@v3
with:
path: |
frontend-monorepo/node_modules
/home/runner/.cache/Cypress
key: node_modules_cypress-${{ hashFiles('frontend-monorepo/yarn.lock') }}
# Install frontend dependencies
- name: Install root dependencies
run: yarn install --frozen-lockfile
working-directory: frontend-monorepo
# To make sure that all Cypress binaries are installed properly
- name: Install cypress bins
run: yarn cypress install
working-directory: frontend-monorepo
- name: Run Cypress tests
run: npx nx run liquidity-provision-dashboard-e2e:e2e --record --key ${{ secrets.CYPRESS_RECORD_KEY }} --browser chrome
working-directory: frontend-monorepo
env:
CYPRESS_SLACK_WEBHOOK: ${{ secrets.CYPRESS_SLACK_WEBHOOK }}
-47
View File
@@ -1,47 +0,0 @@
name: Cypress - stats
on:
workflow_call:
inputs:
trigger:
required: true
type: string
default: 'false'
jobs:
stats-e2e:
runs-on: self-hosted
if: ${{ inputs.trigger == 'true' }}
timeout-minutes: 10
steps:
# Checkout front ends
- name: Checkout frontend mono repo
uses: actions/checkout@v3
with:
fetch-depth: 0
path: './frontend-monorepo'
# Restore node_modules from cache if possible
- name: Restore node_modules from cache
uses: actions/cache@v3
with:
path: |
frontend-monorepo/node_modules
/home/runner/.cache/Cypress
key: node_modules_cypress-${{ hashFiles('frontend-monorepo/yarn.lock') }}
# Install frontend dependencies
- name: Install root dependencies
run: yarn install --frozen-lockfile
working-directory: frontend-monorepo
# To make sure that all Cypress binaries are installed properly
- name: Install cypress bins
run: yarn cypress install
working-directory: frontend-monorepo
- name: Run Cypress tests
run: npx nx run stats-e2e:e2e --record --key ${{ secrets.CYPRESS_RECORD_KEY }} --browser chrome
working-directory: frontend-monorepo
env:
CYPRESS_SLACK_WEBHOOK: ${{ secrets.CYPRESS_SLACK_WEBHOOK }}
+6 -13
View File
@@ -40,29 +40,23 @@ jobs:
path: |
frontend-monorepo/node_modules
/home/runner/.cache/Cypress
key: node_modules_cypress-${{ hashFiles('frontend-monorepo/yarn.lock') }}
key: node_modules_cypress-${{ hashFiles('frontend-monorepo/yarn.lock', 'frontend-monorepo/package.json') }}
# Install frontend dependencies
- name: Install root dependencies
run: yarn install --frozen-lockfile
working-directory: frontend-monorepo
#######
## Build and run Vegacapsule network
#######
- name: Build and run Vegacapsule network
uses: ./frontend-monorepo/.github/actions/run-vegacapsule
######
## Setup a Vega wallet for our user
######
- name: Set up Vegawallet for docker
- name: Run Vegacapsule network
uses: ./frontend-monorepo/.github/actions/run-vegacapsule
- name: Set up Vegawallet for capsule
id: setup-vega
uses: ./frontend-monorepo/.github/actions/setup-vegawallet-docker
with:
passphrase: ${{ secrets.CYPRESS_TRADING_TEST_VEGA_WALLET_PASSPHRASE }}
######
## Run some tests
@@ -77,7 +71,6 @@ jobs:
run: npx nx run token-e2e:e2e ${{ inputs.skip-cache }} --record --key ${{ secrets.CYPRESS_RECORD_KEY }} --browser chrome ${{ inputs.tags }}
working-directory: frontend-monorepo
env:
CYPRESS_TRADING_TEST_VEGA_WALLET_PASSPHRASE: ${{ secrets.CYPRESS_TRADING_TEST_VEGA_WALLET_PASSPHRASE }}
CYPRESS_SLACK_WEBHOOK: ${{ secrets.CYPRESS_SLACK_WEBHOOK }}
CYPRESS_ETH_WALLET_MNEMONIC: ${{ secrets.CYPRESS_ETH_WALLET_MNEMONIC }}
CYPRESS_VEGA_WALLET_API_TOKEN: ${{ steps.setup-vega.outputs.token }}
@@ -97,5 +90,5 @@ jobs:
- uses: actions/upload-artifact@v2
if: ${{ always() }}
with:
name: logs
name: logs-token
path: /home/runner/.vegacapsule/testnet/logs
+23 -7
View File
@@ -37,7 +37,7 @@ jobs:
path: |
frontend-monorepo/node_modules
/home/runner/.cache/Cypress
key: node_modules_cypress-${{ hashFiles('frontend-monorepo/yarn.lock') }}
key: node_modules_cypress-${{ hashFiles('frontend-monorepo/yarn.lock', 'frontend-monorepo/package.json') }}
# Install frontend dependencies
- name: Install root dependencies
@@ -48,12 +48,12 @@ jobs:
## Setup a Vega wallet for our user
######
- name: Set up Vegawallet
- name: Run Vegacapsule network
uses: ./frontend-monorepo/.github/actions/run-vegacapsule
- name: Set up Vegawallet for capsule
id: setup-vega
uses: ./frontend-monorepo/.github/actions/setup-vegawallet
with:
recovery: ${{ secrets.TRADING_TEST_VEGA_WALLET_RECOVERY }}
passphrase: ${{ secrets.CYPRESS_TRADING_TEST_VEGA_WALLET_PASSPHRASE }}
uses: ./frontend-monorepo/.github/actions/setup-vegawallet-docker
# To make sure that all Cypress binaries are installed properly
- name: Install cypress bins
@@ -64,7 +64,23 @@ jobs:
run: npx nx run trading-e2e:e2e ${{ inputs.skip-cache }} --record --key ${{ secrets.CYPRESS_RECORD_KEY }} --browser chrome ${{ inputs.tags }}
working-directory: frontend-monorepo
env:
CYPRESS_TRADING_TEST_VEGA_WALLET_PASSPHRASE: ${{ secrets.CYPRESS_TRADING_TEST_VEGA_WALLET_PASSPHRASE }}
CYPRESS_SLACK_WEBHOOK: ${{ secrets.CYPRESS_SLACK_WEBHOOK }}
CYPRESS_ETH_WALLET_MNEMONIC: ${{ secrets.CYPRESS_ETH_WALLET_MNEMONIC }}
CYPRESS_VEGA_WALLET_API_TOKEN: ${{ steps.setup-vega.outputs.token }}
######
## Upload logs
######
- name: Rename files to allow archive
if: ${{ always() }}
run: |
while read -r file; do
mv "${file}" "$(echo ${file} | sed 's|:|-|g')"
done< <(find /home/runner/.vegacapsule/testnet/logs -type f)
- uses: actions/upload-artifact@v2
if: ${{ always() }}
with:
name: logs-trading
path: /home/runner/.vegacapsule/testnet/logs
-12
View File
@@ -32,18 +32,6 @@ jobs:
tags: ${{ inputs.tags }}
night-run: ${{ inputs.night-run }}
run-liquidity-e2e:
uses: ./.github/workflows/cypress-liquidity-provision-dashboard-e2e.yml
secrets: inherit
with:
trigger: ${{ contains(inputs.project, 'liquidity-provision-dashboard-e2e') || contains(inputs.project, 'liquidity-provision-dashboard') }}
run-stats-e2e:
uses: ./.github/workflows/cypress-stats-e2e.yml
secrets: inherit
with:
trigger: ${{ contains(inputs.project, 'stats-e2e') || contains(inputs.project, 'stats') }}
run-token-e2e:
uses: ./.github/workflows/cypress-token-e2e.yml
secrets: inherit
-4
View File
@@ -22,10 +22,6 @@ The utility dApp for interacting with the Vega token and using its' utility. Thi
The block explorer for the Vega network, showing details of raw chain states and the state of markets on the Vega network.
### [Stats](./apps/stats)
An application for the status of the Vega network. Showing block height and other network activity.
### [Static](./apps/static)
Hosting for static content being shared across apps, for example fonts.
+7 -6
View File
@@ -1,10 +1,11 @@
# App configuration variables
NX_VEGA_CONFIG_URL=https://static.vega.xyz/assets/stagnet3-network.json
NX_VEGA_URL=https://api.stagnet3.vega.xyz/graphql
NX_VEGA_ENV=STAGNET3
NX_ETHEREUM_PROVIDER_URL=https://sepolia.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8
NX_ETHEREUM_PROVIDER_URL=http://localhost:8545
NX_ETHERSCAN_URL=https://sepolia.etherscan.io
NX_VEGA_CONFIG_URL=''
NX_VEGA_ENV=CUSTOM
NX_VEGA_EXPLORER_URL=https://stagnet3.explorer.vega.xyz
NX_VEGA_URL=http://localhost:3028/query
NX_VEGA_WALLET_URL=http://localhost:1789
CYPRESS_VEGA_ENV=STAGNET3
CYPRESS_VEGA_ENV=CUSTOM
CYPRESS_VEGA_URL=http://localhost:3028/query
CYPRESS_VEGA_WALLET_API_TOKEN=
+11
View File
@@ -0,0 +1,11 @@
NX_ETHEREUM_PROVIDER_URL=http://localhost:8545
NX_ETHERSCAN_URL=https://sepolia.etherscan.io
NX_VEGA_CONFIG_URL=''
NX_VEGA_ENV=CUSTOM
NX_VEGA_EXPLORER_URL=https://stagnet3.explorer.vega.xyz
NX_VEGA_URL=http://localhost:3028/query
NX_VEGA_WALLET_URL=http://localhost:1789
CYPRESS_VEGA_ENV=CUSTOM
CYPRESS_VEGA_URL=http://localhost:3028/query
CYPRESS_VEGA_WALLET_API_TOKEN=
@@ -1,7 +1,11 @@
# App configuration variables
NX_VEGA_CONFIG_URL=https://static.vega.xyz/assets/stagnet3-network.json
NX_VEGA_URL=https://api.stagnet3.vega.xyz/graphql
NX_VEGA_ENV=STAGNET3
NX_ETHEREUM_PROVIDER_URL=https://sepolia.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8
NX_ETHERSCAN_URL=https://sepolia.etherscan.io
NX_VEGA_EXPLORER_URL=https://staging2.explorer.vega.xyz
NX_VEGA_CONFIG_URL=https://static.vega.xyz/assets/stagnet3-network.json
NX_VEGA_ENV=STAGNET3
NX_VEGA_EXPLORER_URL=https://stagnet3.explorer.vega.xyz
NX_VEGA_URL=https://api.stagnet3.vega.xyz/graphql
NX_VEGA_WALLET_URL=http://localhost:1789
CYPRESS_VEGA_ENV=STAGNET3
CYPRESS_VEGA_URL=https://api.stagnet3.vega.xyz/graphql
CYPRESS_VEGA_WALLET_API_TOKEN=
-16
View File
@@ -1,7 +1,6 @@
const { defineConfig } = require('cypress');
module.exports = defineConfig({
projectId: 'et4snf',
e2e: {
setupNodeEvents(on, config) {
require('@cypress/grep/src/plugin')(config);
@@ -23,21 +22,6 @@ module.exports = defineConfig({
viewportHeight: 900,
},
env: {
ETHEREUM_PROVIDER_URL:
'https://sepolia.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8',
VEGA_PUBLIC_KEY:
'47836c253520d2661bf5bed6339c0de08fd02cf5d4db0efee3b4373f20c7d278',
VEGA_PUBLIC_KEY2:
'1a18cdcaaa4f44a57b35a4e9b77e0701c17a476f2b407620f8c17371740cf2e4',
TRUNCATED_VEGA_PUBLIC_KEY: '47836c…c7d278',
TRUNCATED_VEGA_PUBLIC_KEY2: '1a18cd…0cf2e4',
ETHEREUM_WALLET_ADDRESS: '0x265Cc6d39a1B53d0d92068443009eE7410807158',
ETHERSCAN_URL: 'https://sepolia.etherscan.io',
tsConfig: 'tsconfig.json',
TAGS: 'not @todo and not @ignore and not @manual',
TRADING_TEST_VEGA_WALLET_PASSPHRASE: '123',
ETH_WALLET_MNEMONIC:
'ugly gallery notice network true range brave clarify flat logic someone chunk',
grepTags: '@regression @smoke @slow',
grepFilterSpecs: true,
grepOmitFiltered: true,
@@ -4,8 +4,8 @@ const marketName = 'ACTIVE MARKET';
describe('market selector', { tags: '@smoke' }, () => {
beforeEach(() => {
cy.mockConsole();
cy.setVegaWallet();
cy.visit(`/trading/${marketId}`);
cy.connectVegaWallet();
cy.wait('@Markets');
});
@@ -67,6 +67,7 @@ describe('market selector', { tags: '@smoke' }, () => {
it('mobile view', () => {
cy.viewport('iphone-xr');
cy.visit(`/trading/${marketId}`);
cy.connectVegaWallet();
cy.get('[role="dialog"]').should('not.exist');
cy.getByTestId('arrow-button').click();
cy.get('[role="dialog"]').should('be.visible');
@@ -30,7 +30,6 @@ describe('Market trade with wallet disconnected', { tags: '@smoke' }, () => {
cy.mockConsole();
cy.visit(`/trading/${marketId}`);
cy.wait('@Market');
console.log('marketId', marketId);
});
it('should not display steps', () => {
cy.getByTestId('trading-connect-wallet')
@@ -51,8 +50,8 @@ describe('Market trade', { tags: '@regression' }, () => {
cy.mockGQL((req) => {
aliasGQLQuery(req, 'Market', marketQuery(marketOverride));
});
cy.setVegaWallet();
cy.visit(`/trading/${marketId}`);
cy.connectVegaWallet();
cy.wait('@Market');
});
@@ -14,6 +14,7 @@ import {
describe('Portfolio page - wallet', { tags: '@smoke' }, () => {
it('button for wallet connect should work', () => {
cy.mockConsole();
cy.visit('/');
cy.get('[href="/portfolio"]').eq(0).click();
cy.getByTestId('trading-connect-wallet').should('be.visible');
@@ -34,11 +35,11 @@ describe('Portfolio page tabs', { tags: '@smoke' }, () => {
aliasGQLQuery(req, 'Accounts', accountsQuery());
aliasGQLQuery(req, 'Assets', assetsQuery());
});
cy.setVegaWallet();
});
it('certain tabs should exist', () => {
cy.visit('/portfolio');
cy.connectVegaWallet();
cy.getByTestId('assets').click();
cy.location('pathname').should('eq', '/portfolio/assets');
@@ -57,27 +58,16 @@ describe('Portfolio page tabs', { tags: '@smoke' }, () => {
});
describe('Assets view', () => {
before(() => {
cy.mockGQL((req) => {
aliasGQLQuery(req, 'ChainId', chainIdQuery());
aliasGQLQuery(req, 'Statistics', statisticsQuery());
aliasGQLQuery(req, 'Positions', positionsQuery());
aliasGQLQuery(req, 'Margins', marginsQuery());
aliasGQLQuery(req, 'Markets', marketsQuery());
aliasGQLQuery(req, 'MarketsData', marketsDataQuery());
aliasGQLQuery(req, 'Accounts', accountsQuery());
aliasGQLQuery(req, 'Assets', assetsQuery());
});
beforeEach(() => {
cy.mockConsole();
cy.setVegaWallet();
cy.visit('/portfolio/assets');
cy.connectVegaWallet();
});
it('data should be properly rendered', () => {
cy.get('.ag-center-cols-container .ag-row').should('have.length', 5);
cy.get(
'.ag-center-cols-container [row-id="ACCOUNT_TYPE_GENERAL-asset-id-null"]'
)
.find('button')
cy.get('[role="gridcell"][col-id="account-asset"] button')
.contains('tEURO')
.click();
cy.getByTestId('dialog-title').should(
'have.text',
@@ -99,8 +89,8 @@ describe('Portfolio page tabs', { tags: '@smoke' }, () => {
aliasGQLQuery(req, 'Accounts', accountsQuery());
aliasGQLQuery(req, 'Assets', assetsQuery());
});
cy.setVegaWallet();
cy.visit('/portfolio/positions');
cy.connectVegaWallet();
});
it('data should be properly rendered', () => {
@@ -116,8 +106,8 @@ describe('Portfolio page tabs', { tags: '@smoke' }, () => {
aliasGQLQuery(req, 'Orders', ordersQuery());
aliasGQLQuery(req, 'Markets', marketsQuery());
});
cy.setVegaWallet();
cy.visit('/portfolio/orders');
cy.connectVegaWallet();
});
it('data should be properly rendered', () => {
@@ -134,8 +124,8 @@ describe('Portfolio page tabs', { tags: '@smoke' }, () => {
aliasGQLQuery(req, 'Markets', marketsQuery());
aliasGQLQuery(req, 'Fills', fillsQuery());
});
cy.setVegaWallet();
cy.visit('/portfolio/fills');
cy.connectVegaWallet();
});
it('data should be properly rendered', () => {
@@ -161,8 +151,8 @@ describe('Portfolio page tabs', { tags: '@smoke' }, () => {
aliasGQLQuery(req, 'Margins', marginsQuery());
aliasGQLQuery(req, 'MarketsData', marketsDataQuery());
});
cy.setVegaWallet();
cy.visit('/portfolio');
cy.connectVegaWallet();
});
it('"No data to display" should be always displayed', () => {
@@ -13,6 +13,7 @@ import {
marketsCandlesQuery,
marketsDataQuery,
marketsQuery,
networkParamsQuery,
ordersQuery,
positionsQuery,
statisticsQuery,
@@ -47,6 +48,7 @@ const mockPage = (req: CyHttpMessages.IncomingHttpRequest) => {
aliasGQLQuery(req, 'SimpleMarkets', marketsQuery());
aliasGQLQuery(req, 'Orders', ordersQuery());
aliasGQLQuery(req, 'Fills', fillsQuery());
aliasGQLQuery(req, 'NetworkParams', networkParamsQuery());
};
export const addMockConsole = () => {
@@ -1,5 +1,4 @@
import { useParams } from 'react-router-dom';
import { DealTicketManager } from '@vegaprotocol/deal-ticket';
import { Loader, Splash } from '@vegaprotocol/ui-toolkit';
import { t, useDataProvider } from '@vegaprotocol/react-helpers';
import { useVegaWallet } from '@vegaprotocol/wallet';
@@ -50,10 +49,10 @@ export const DealTicketContainer = () => {
);
const container = (
<DealTicketManager market={data}>
<>
{loading ? loader : balance}
<DealTicketSteps market={data} />
</DealTicketManager>
</>
);
return (
@@ -8,7 +8,6 @@ import {
useOrderMargin,
useMaximumPositionSize,
useCalculateSlippage,
validateAmount,
} from '@vegaprotocol/deal-ticket';
import { InputError } from '@vegaprotocol/ui-toolkit';
import { BigNumber } from 'bignumber.js';
@@ -23,6 +22,7 @@ import {
addDecimalsFormatNumber,
addDecimal,
formatNumber,
validateAmount,
} from '@vegaprotocol/react-helpers';
import {
useOrderSubmit,
@@ -83,10 +83,11 @@ export const DealTicketSteps = ({ market }: DealTicketMarketProps) => {
order,
});
const estCloseOut = useOrderCloseOut({
const closeOut = useOrderCloseOut({
order,
market,
});
const estCloseOut = closeOut && formatNumber(closeOut, market.decimalPlaces);
const slippage = useCalculateSlippage({ marketId: market.id, order });
const [slippageValue, setSlippageValue] = useState(
slippage ? parseFloat(slippage) : 0
@@ -1,13 +1,12 @@
import type { ReactNode } from 'react';
import type { FieldErrors } from 'react-hook-form';
import { useMemo } from 'react';
import { t, toDecimal } from '@vegaprotocol/react-helpers';
import { DataGrid, t, toDecimal } from '@vegaprotocol/react-helpers';
import { useVegaWallet } from '@vegaprotocol/wallet';
import * as Schema from '@vegaprotocol/types';
import type { OrderSubmissionBody } from '@vegaprotocol/wallet';
import { Tooltip } from '@vegaprotocol/ui-toolkit';
import {
MarketDataGrid,
compileGridData,
MarginWarning,
isMarketInAuction,
@@ -217,7 +216,7 @@ export const useOrderValidation = ({
{t('This market is in auction until it reaches')}{' '}
<Tooltip
description={
<MarketDataGrid grid={compileGridData(market)} />
<DataGrid grid={compileGridData(market, market.data)} />
}
>
<span>{t('sufficient liquidity')}</span>
@@ -241,7 +240,7 @@ export const useOrderValidation = ({
{t('This market is in auction due to')}{' '}
<Tooltip
description={
<MarketDataGrid grid={compileGridData(market)} />
<DataGrid grid={compileGridData(market, market.data)} />
}
>
<span>{t('high price volatility')}</span>
@@ -282,7 +281,7 @@ export const useOrderValidation = ({
{t('This market is in auction until it reaches')}{' '}
<Tooltip
description={
<MarketDataGrid grid={compileGridData(market)} />
<DataGrid grid={compileGridData(market, market.data)} />
}
>
<span>{t('sufficient liquidity')}</span>
@@ -308,7 +307,7 @@ export const useOrderValidation = ({
{t('This market is in auction due to')}{' '}
<Tooltip
description={
<MarketDataGrid grid={compileGridData(market)} />
<DataGrid grid={compileGridData(market, market.data)} />
}
>
<span>{t('high price volatility')}</span>
@@ -33,19 +33,17 @@ const AccountsManager = () => {
update,
variables,
});
const getRows = async ({
successCallback,
startRow,
endRow,
}: IGetRowsParams) => {
const rowsThisBlock = dataRef.current
? dataRef.current.slice(startRow, endRow)
: [];
const lastRow = dataRef.current?.length ?? -1;
successCallback(rowsThisBlock, lastRow);
};
const getRows = useCallback(
async ({ successCallback, startRow, endRow }: IGetRowsParams) => {
const rowsThisBlock = dataRef.current
? dataRef.current.slice(startRow, endRow)
: [];
const lastRow = dataRef.current ? dataRef.current.length : 0;
successCallback(rowsThisBlock, lastRow);
},
[]
);
const { columnDefs, defaultColDef } = useAccountColumnDefinitions();
console.log(data, loading);
return (
<>
<AsyncRenderer
@@ -13,7 +13,7 @@ import useColumnDefinitions from './use-column-definitions';
const Positions = () => {
const gridRef = useRef<AgGridReact | null>(null);
const { partyId } = useOutletContext<{ partyId: string }>();
const { data, error, loading } = usePositionsData(partyId, gridRef);
const { data, error, loading, getRows } = usePositionsData(partyId, gridRef);
const { columnDefs, defaultColDef } = useColumnDefinitions();
return (
<AsyncRenderer
@@ -29,7 +29,8 @@ const Positions = () => {
columnDefs={columnDefs}
defaultColDef={defaultColDef}
getRowId={getRowId}
rowData={data || undefined}
rowModelType="infinite"
datasource={{ getRows }}
components={{ PriceFlashCell }}
/>
</AsyncRenderer>
@@ -1,27 +0,0 @@
/* tslint:disable */
/* eslint-disable */
// @generated
// This file was automatically generated and should not be edited.
// ====================================================
// GraphQL subscription operation: CandleLive
// ====================================================
export interface CandleLive_candles {
__typename: "Candle";
/**
* Close price (uint64)
*/
close: string;
}
export interface CandleLive {
/**
* Subscribe to the candles updates
*/
candles: CandleLive_candles;
}
export interface CandleLiveVariables {
marketId: string;
}
@@ -1,43 +0,0 @@
import * as Types from '@vegaprotocol/types';
import { gql } from '@apollo/client';
import * as Apollo from '@apollo/client';
const defaultOptions = {} as const;
export type CandleLiveSubscriptionVariables = Types.Exact<{
marketId: Types.Scalars['ID'];
}>;
export type CandleLiveSubscription = { __typename?: 'Subscription', candles: { __typename?: 'Candle', close: string } };
export const CandleLiveDocument = gql`
subscription CandleLive($marketId: ID!) {
candles(marketId: $marketId, interval: INTERVAL_I1H) {
close
}
}
`;
/**
* __useCandleLiveSubscription__
*
* To run a query within a React component, call `useCandleLiveSubscription` and pass it any options that fit your needs.
* When your component renders, `useCandleLiveSubscription` returns an object from Apollo Client that contains loading, error, and data properties
* you can use to render your UI.
*
* @param baseOptions options that will be passed into the subscription, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
*
* @example
* const { data, loading, error } = useCandleLiveSubscription({
* variables: {
* marketId: // value for 'marketId'
* },
* });
*/
export function useCandleLiveSubscription(baseOptions: Apollo.SubscriptionHookOptions<CandleLiveSubscription, CandleLiveSubscriptionVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useSubscription<CandleLiveSubscription, CandleLiveSubscriptionVariables>(CandleLiveDocument, options);
}
export type CandleLiveSubscriptionHookResult = ReturnType<typeof useCandleLiveSubscription>;
export type CandleLiveSubscriptionResult = Apollo.SubscriptionResult<CandleLiveSubscription>;
+1
View File
@@ -18,3 +18,4 @@ NX_EXPLORER_PARTIES=1
NX_EXPLORER_VALIDATORS=1
CYPRESS_VEGA_WALLET_API_TOKEN=
CYPRESS_VEGA_URL=http://localhost:3028/query
+1
View File
@@ -4,6 +4,7 @@ NX_TENDERMINT_URL=https://n04.d.vega.xyz/tm
NX_TENDERMINT_WEBSOCKET_URL=wss://n04.d.vega.xyz/tm/websocket
NX_VEGA_URL=https://api.n04.d.vega.xyz/graphql
NX_VEGA_ENV=DEVNET
NX_BLOCK_EXPLORER=https://be.devnet1.vega.xyz/rest
# App flags
NX_EXPLORER_ASSETS=1
+1
View File
@@ -4,6 +4,7 @@ NX_TENDERMINT_URL=https://mainnet-observer-proxy01.ops.vega.xyz/
NX_TENDERMINT_WEBSOCKET_URL=wss://mainnet-observer-proxy01.ops.vega.xyz/websocket
NX_VEGA_URL=https://api.vega.xyz/query
NX_VEGA_ENV=MAINNET
NX_BLOCK_EXPLORER=https://be.explorer.vega.xyz/rest
# App flags
NX_EXPLORER_ASSETS=1
+1
View File
@@ -4,6 +4,7 @@ NX_TENDERMINT_URL=https://tm.n00.stagnet3.vega.xyz
NX_TENDERMINT_WEBSOCKET_URL=wss://tm.n00.stagnet3.vega.xyz/websocket
NX_VEGA_URL=https://api.stagnet3.vega.xyz/graphql
NX_VEGA_ENV=STAGNET3
NX_BLOCK_EXPLORER=https://be.stagnet3.vega.xyz/rest
# App flags
NX_EXPLORER_ASSETS=1
+1
View File
@@ -4,6 +4,7 @@ NX_TENDERMINT_URL=https://tm.n07.testnet.vega.xyz/tm
NX_TENDERMINT_WEBSOCKET_URL=wss://lb.testnet.vega.xyz/tm/websocket
NX_VEGA_URL=https://api.n11.testnet.vega.xyz/graphql
NX_VEGA_ENV=TESTNET
NX_BLOCK_EXPLORER=https://be.testnet.vega.xyz/rest
# App flags
NX_EXPLORER_ASSETS=1
+39
View File
@@ -0,0 +1,39 @@
# Vega Explorer E2E tests
To run the UI automation tests with Vega Capsule, run:
```bash
yarn nx run explorer-e2e:e2e
```
To open Cypress and run in interactive mode, run:
```bash
yarn nx run explorer-e2e:e2e --watch
```
## Vega Capsule Setup
The e2e tests run against a locally running instance of the Vega network, managed and controlled by [Vega Capsule](https://github.com/vegaprotocol/vegacapsule). Vega Capsule will:
- Bootstrap and start up a Vega network
- Start up [Ganache](https://trufflesuite.com/ganache/) for a local Ethereum network
- Install the required Vega smart contracts
- Set up DataNodes with a running GraphQL and REST APIs.
1. Refer to the [Vega Capsule readme](https://github.com/vegaprotocol/vegacapsule#readme) for setting up and running Capsule - follow by Pre-start and Quick Start (points 1-2)
2. Bootstrap with auto-installed dependencies including wallet
```bash
vegacapsule network bootstrap --config-path=../frontend-monorepo/vegacapsule/config.hcl --force
```
### Troubleshooting
- You may need to run `vegacapsule nodes unsafe-reset-all` to get a clean network state
## Vega Wallet Setup
You can then refer to (or run) `frontend-monorepo/vegacapsule/setup-vegawallet.sh`. This will initialise and configure your wallet to have the correct public keys and network config to run against capsule.
Go to the .env file in `apps/explorer-e2e` and set the `CYPRESS_VEGA_WALLET_API_TOKEN` environment variable by pasting in your wallets long lived api token
@@ -29,7 +29,7 @@
"market.liquidity.maximumLiquidityFeeFactorLevel",
"market.liquidity.minimum.probabilityOfTrading.lpOrders",
"market.liquidity.probabilityOfTrading.tau.scaling",
"market.liquidity.stakeToCcySiskas",
"market.liquidity.stakeToCcyVolume",
"market.liquidity.targetstake.triggering.ratio",
"market.liquidityProvision.minLpStakeQuantumMultiple",
"market.liquidityProvision.shapes.maxSize",
@@ -52,7 +52,8 @@ context('Blocks page', { tags: '@regression' }, function () {
});
});
it('Previous button disabled on first block', function () {
// Skipping - see https://github.com/vegaprotocol/frontend-monorepo/issues/2494
it.skip('Previous button disabled on first block', function () {
cy.get('[data-testid="block-input"]').type('1');
cy.get('[data-testid="go-submit"]').click();
cy.get(previousBlockBtn).find('button').should('be.disabled');
@@ -115,7 +116,7 @@ context('Blocks page', { tags: '@regression' }, function () {
});
function waitForBlocksResponse() {
cy.contains('Loading...').should('not.exist', { timeout: 18000 });
cy.get('[data-testid="loader"]').should('not.exist', { timeout: 18000 });
}
function validateBlocksDisplayed() {
+19 -21
View File
@@ -20,19 +20,18 @@ context('Home Page', function () {
1: 'Height',
2: 'Uptime',
3: 'Total nodes',
4: 'Inactive nodes',
5: 'Total staked',
6: 'Backlog',
7: 'Trades / second',
8: 'Orders / block',
9: 'Orders / second',
10: 'Transactions / block',
11: 'Block time',
12: 'Time',
13: 'App',
14: 'Tendermint',
15: 'Up since',
16: 'Chain ID',
4: 'Total staked',
5: 'Backlog',
6: 'Trades / second',
7: 'Orders / block',
8: 'Orders / second',
9: 'Transactions / block',
10: 'Block time',
11: 'Time',
12: 'App',
13: 'Tendermint',
14: 'Up since',
15: 'Chain ID',
};
cy.get('[data-testid="stats-title"]')
@@ -40,7 +39,7 @@ context('Home Page', function () {
cy.wrap($list).should('have.text', statTitles[index]);
})
.then(($list) => {
cy.wrap($list).should('have.length', 17);
cy.wrap($list).should('have.length', 16);
});
cy.get(statsValue).eq(0).should('have.text', 'CONNECTED');
@@ -50,30 +49,29 @@ context('Home Page', function () {
.invoke('text')
.should('match', /\d+d \d+h \d+m \d+s/i);
cy.get(statsValue).eq(3).should('have.text', '2');
cy.get(statsValue).eq(4).should('have.text', '2');
cy.get(statsValue)
.eq(5)
.eq(4)
.invoke('text')
.should('match', /\d+\.\d\d(?!\d)/i);
cy.get(statsValue).eq(5).should('have.text', '0');
cy.get(statsValue).eq(6).should('have.text', '0');
cy.get(statsValue).eq(7).should('have.text', '0');
cy.get(statsValue).eq(8).should('have.text', '0');
cy.get(statsValue).eq(9).should('have.text', '0');
cy.get(statsValue).eq(9).should('not.be.empty');
cy.get(statsValue).eq(10).should('not.be.empty');
cy.get(statsValue).eq(11).should('not.be.empty');
cy.get(statsValue).eq(12).should('not.be.empty');
if (Cypress.env('NIGHTLY_RUN') != true) {
cy.get(statsValue)
.eq(13)
.eq(12)
.invoke('text')
.should('match', /v\d+\.\d+\.\d+/i);
}
cy.get(statsValue)
.eq(14)
.eq(13)
.invoke('text')
.should('match', /\d+\.\d+\.\d+/i);
cy.get(statsValue).eq(14).should('not.be.empty');
cy.get(statsValue).eq(15).should('not.be.empty');
cy.get(statsValue).eq(16).should('not.be.empty');
});
it('Block height should be updating', function () {
+1 -1
View File
@@ -18,4 +18,4 @@ NX_EXPLORER_PARTIES=1
NX_EXPLORER_VALIDATORS=1
NX_EXPLORER_MARKETS=1
NX_EXPLORER_ORACLES=1
NX_EXPLORER_TXS_LIST=0
NX_EXPLORER_TXS_LIST=1
+20
View File
@@ -0,0 +1,20 @@
# App configuration variables
NX_TENDERMINT_URL=https://tm.be.mainnet-mirror.vega.xyz
NX_TENDERMINT_WEBSOCKET_URL=https://tm.be.mainnet-mirror.vega.xyz
NX_VEGA_CONFIG_URL=https://static.vega.xyz/assets/mirror-network.json
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_VEGA_ENV=MIRROR
NX_GITHUB_FEEDBACK_URL=https://github.com/vegaprotocol/feedback/discussions
NX_BLOCK_EXPLORER=https://be.mainnet-mirror.vega.xyz/rest/
NX_ETHERSCAN_URL=https://sepolia.etherscan.io
# App flags
NX_EXPLORER_ASSETS=1
NX_EXPLORER_GENESIS=1
NX_EXPLORER_GOVERNANCE=1
NX_EXPLORER_NETWORK_PARAMETERS=1
NX_EXPLORER_PARTIES=1
NX_EXPLORER_VALIDATORS=1
NX_EXPLORER_MARKETS=0
NX_EXPLORER_ORACLES=0
NX_EXPLORER_TXS_LIST=1
+1 -1
View File
@@ -70,7 +70,7 @@
"executor": "@nrwl/workspace:run-commands",
"options": {
"commands": [
"npx openapi-typescript https://raw.githubusercontent.com/vegaprotocol/documentation/main/specs/v0.62.1/blockexplorer.openapi.json --output apps/explorer/src/types/explorer.d.ts --immutable-types"
"npx openapi-typescript https://raw.githubusercontent.com/vegaprotocol/documentation/main/specs/v0.66.1/blockexplorer.openapi.json --output apps/explorer/src/types/explorer.d.ts --immutable-types"
]
}
},
+2 -19
View File
@@ -1,23 +1,15 @@
import classnames from 'classnames';
import { useState, useEffect } from 'react';
import * as Sentry from '@sentry/react';
import { BrowserTracing } from '@sentry/tracing';
import { useLocation } from 'react-router-dom';
import {
EnvironmentProvider,
NetworkLoader,
useEnvironment,
} from '@vegaprotocol/environment';
import { EnvironmentProvider, NetworkLoader } from '@vegaprotocol/environment';
import { NetworkInfo } from '@vegaprotocol/network-info';
import { Nav } from './components/nav';
import { Header } from './components/header';
import { Main } from './components/main';
import { TendermintWebsocketProvider } from './contexts/websocket/tendermint-websocket-provider';
import { ENV } from './config/env';
import type { InMemoryCacheConfig } from '@apollo/client';
function App() {
const { VEGA_ENV } = useEnvironment();
const [menuOpen, setMenuOpen] = useState(false);
const location = useLocation();
@@ -26,18 +18,9 @@ function App() {
setMenuOpen(false);
}, [location]);
useEffect(() => {
Sentry.init({
dsn: ENV.dsn,
integrations: [new BrowserTracing()],
tracesSampleRate: 1,
environment: VEGA_ENV,
});
}, [VEGA_ENV]);
const cacheConfig: InMemoryCacheConfig = {
typePolicies: {
Node: {
statistics: {
keyFields: false,
},
},
@@ -18,7 +18,6 @@ const AssetBalance = ({
showAssetLink = true,
}: AssetBalanceProps) => {
const { data } = useExplorerAssetQuery({
fetchPolicy: 'cache-first',
variables: { id: assetId },
});
@@ -59,7 +59,8 @@ describe('Blocks infinite list', () => {
error={undefined}
/>
);
expect(screen.getByText('No items')).toBeInTheDocument();
expect(screen.getByTestId('emptylist')).toBeInTheDocument();
expect(screen.getByText('This chain has 0 blocks')).toBeInTheDocument();
});
it('error is displayed at item level', () => {
@@ -4,6 +4,8 @@ import InfiniteLoader from 'react-window-infinite-loader';
import { t } from '@vegaprotocol/react-helpers';
import type { BlockMeta } from '../../routes/blocks/tendermint-blockchain-response';
import { BlockData } from './block-data';
import EmptyList from '../empty-list/empty-list';
import { Loader } from '@vegaprotocol/ui-toolkit';
interface BlocksInfiniteListProps {
hasMoreBlocks: boolean;
@@ -31,7 +33,16 @@ export const BlocksInfiniteList = ({
className,
}: BlocksInfiniteListProps) => {
if (!blocks) {
return <div>No items</div>;
if (!areBlocksLoading) {
return (
<EmptyList
heading={t('This chain has 0 blocks')}
label={t('Check back soon')}
/>
);
} else {
return <Loader />;
}
}
// If there are more items to be loaded then add an extra row to hold a loading indicator.
@@ -50,7 +61,7 @@ export const BlocksInfiniteList = ({
if (error) {
content = t(`${error}`);
} else if (!isItemLoaded(index)) {
content = t('Loading...');
content = <Loader />;
} else {
content = <BlockData block={blocks[index]} />;
}
@@ -0,0 +1,34 @@
export type EmptyListProps = {
heading?: string;
label?: string;
};
/**
* Renders the empty state from github ticket #1463
*/
const EmptyList = ({ heading, label }: EmptyListProps) => {
return (
<div
className="empty-list w-full items-center h-full align-center"
data-testid="emptylist"
>
<div className="skeleton-list border-dashed border-neutral-800 rounded p-5 w-full border-[1px] grid gap-4 grid-cols-9 grid-rows-1 place-content-around mb-4">
<div className="bg-neutral-900 mr-5 h-3 col-span-5"></div>
<div className="bg-neutral-900 h-3 col-span-1"></div>
</div>
<div className="mt-4">
{heading ? (
<h1 className="font-alpha text-xl uppercase text-center leading-relaxed">
{heading}
</h1>
) : null}
{label ? (
<p className="font-alpha text-gray-500 text-center">{label}</p>
) : null}
</div>
</div>
);
};
export default EmptyList;
@@ -16,7 +16,6 @@ export type AssetLinkProps = Partial<ComponentProps<typeof Link>> & {
*/
const AssetLink = ({ id, ...props }: AssetLinkProps) => {
const { data } = useExplorerAssetQuery({
fetchPolicy: 'cache-first',
variables: { id },
});
@@ -29,7 +29,7 @@ const MarketLink = ({ id, ...props }: MarketLinkProps) => {
label = (
<div title={t('Unknown market')}>
<span role="img" aria-label="Unknown market" className="img">
&nbsp;{t('Invalid market')}
</span>
&nbsp;{id}
</div>
@@ -1,4 +1,3 @@
import React from 'react';
import { Routes } from '../../../routes/route-names';
import { Link } from 'react-router-dom';
@@ -22,8 +22,14 @@ fragment ExplorerDeterministicOrderFields on Order {
tradableInstrument {
instrument {
name
product {
... on Future {
quoteName
}
}
}
}
state
}
}
@@ -3,7 +3,7 @@ import * as Types from '@vegaprotocol/types';
import { gql } from '@apollo/client';
import * as Apollo from '@apollo/client';
const defaultOptions = {} as const;
export type ExplorerDeterministicOrderFieldsFragment = { __typename?: 'Order', id: string, type?: Types.OrderType | null, reference: string, status: Types.OrderStatus, version: string, createdAt: any, updatedAt?: any | null, expiresAt?: any | null, timeInForce: Types.OrderTimeInForce, price: string, side: Types.Side, remaining: string, size: string, rejectionReason?: Types.OrderRejectionReason | null, party: { __typename?: 'Party', id: string }, market: { __typename?: 'Market', id: string, decimalPlaces: number, tradableInstrument: { __typename?: 'TradableInstrument', instrument: { __typename?: 'Instrument', name: string } } } };
export type ExplorerDeterministicOrderFieldsFragment = { __typename?: 'Order', id: string, type?: Types.OrderType | null, reference: string, status: Types.OrderStatus, version: string, createdAt: any, updatedAt?: any | null, expiresAt?: any | null, timeInForce: Types.OrderTimeInForce, price: string, side: Types.Side, remaining: string, size: string, rejectionReason?: Types.OrderRejectionReason | null, party: { __typename?: 'Party', id: string }, market: { __typename?: 'Market', id: string, decimalPlaces: number, state: Types.MarketState, tradableInstrument: { __typename?: 'TradableInstrument', instrument: { __typename?: 'Instrument', name: string, product: { __typename?: 'Future', quoteName: string } } } } };
export type ExplorerDeterministicOrderQueryVariables = Types.Exact<{
orderId: Types.Scalars['ID'];
@@ -11,7 +11,7 @@ export type ExplorerDeterministicOrderQueryVariables = Types.Exact<{
}>;
export type ExplorerDeterministicOrderQuery = { __typename?: 'Query', orderByID: { __typename?: 'Order', id: string, type?: Types.OrderType | null, reference: string, status: Types.OrderStatus, version: string, createdAt: any, updatedAt?: any | null, expiresAt?: any | null, timeInForce: Types.OrderTimeInForce, price: string, side: Types.Side, remaining: string, size: string, rejectionReason?: Types.OrderRejectionReason | null, party: { __typename?: 'Party', id: string }, market: { __typename?: 'Market', id: string, decimalPlaces: number, tradableInstrument: { __typename?: 'TradableInstrument', instrument: { __typename?: 'Instrument', name: string } } } } };
export type ExplorerDeterministicOrderQuery = { __typename?: 'Query', orderByID: { __typename?: 'Order', id: string, type?: Types.OrderType | null, reference: string, status: Types.OrderStatus, version: string, createdAt: any, updatedAt?: any | null, expiresAt?: any | null, timeInForce: Types.OrderTimeInForce, price: string, side: Types.Side, remaining: string, size: string, rejectionReason?: Types.OrderRejectionReason | null, party: { __typename?: 'Party', id: string }, market: { __typename?: 'Market', id: string, decimalPlaces: number, state: Types.MarketState, tradableInstrument: { __typename?: 'TradableInstrument', instrument: { __typename?: 'Instrument', name: string, product: { __typename?: 'Future', quoteName: string } } } } } };
export const ExplorerDeterministicOrderFieldsFragmentDoc = gql`
fragment ExplorerDeterministicOrderFields on Order {
@@ -38,8 +38,14 @@ export const ExplorerDeterministicOrderFieldsFragmentDoc = gql`
tradableInstrument {
instrument {
name
product {
... on Future {
quoteName
}
}
}
}
state
}
}
`;
@@ -60,10 +60,15 @@ function renderExistingAmend(id: string, version: number, amend: Amend) {
market: {
__typename: 'Market',
id: '789',
state: 'STATUS_ACTIVE',
decimalPlaces: '5',
tradableInstrument: {
instrument: {
name: 'test',
product: {
__typename: 'Future',
quoteName: '123',
},
},
},
},
@@ -1,5 +1,6 @@
import { t } from '@vegaprotocol/react-helpers';
import type * as Schema from '@vegaprotocol/types';
import type { components } from '../../../../types/explorer';
export interface DeterministicOrderDetailsProps {
id: string;
@@ -19,7 +20,8 @@ export const statusText: Record<Schema.OrderStatus, string> = {
STATUS_STOPPED: t('Stopped'),
};
export const sideText: Record<Schema.Side, string> = {
export const sideText: Record<components['schemas']['vegaSide'], string> = {
SIDE_UNSPECIFIED: t('Unspecified'),
SIDE_BUY: t('Buy'),
SIDE_SELL: t('Sell'),
};
@@ -0,0 +1,85 @@
import type { MockedResponse } from '@apollo/client/testing';
import { MockedProvider } from '@apollo/client/testing';
import OrderSummary from './order-summary';
import type { OrderSummaryModifier } from './order-summary';
import { render } from '@testing-library/react';
import { ExplorerDeterministicOrderDocument } from '../order-details/__generated__/Order';
const mock = {
request: {
query: ExplorerDeterministicOrderDocument,
variables: {
orderId: '123',
},
},
result: {
data: {
orderByID: {
__typename: 'Order',
id: '123',
type: 'GTC',
status: 'OPEN',
version: '1',
createdAt: 'Tue, Jan 10, 2023 3:35',
expiresAt: 'Tue, Jan 10, 2023 3:35',
updatedAt: 'Tue, Jan 10, 2023 3:35',
rejectionReason: null,
reference: null,
timeInForce: 'GTC',
price: '333',
side: 'SIDE_BUY',
remaining: '100',
size: '100',
party: {
id: '456',
},
market: {
__typename: 'Market',
id: '789',
state: 'STATE_ACTIVE',
decimalPlaces: 2,
tradableInstrument: {
instrument: {
name: 'TEST',
product: {
__typename: 'Future',
quoteName: '123',
},
},
},
},
},
},
},
};
function renderComponent(
id: string,
mocks?: MockedResponse[],
modifier?: OrderSummaryModifier
) {
return render(
<MockedProvider mocks={mocks}>
<OrderSummary id={id} modifier={modifier} />
</MockedProvider>
);
}
describe('Order Summary component', () => {
it('side, size are present', async () => {
const res = renderComponent(mock.result.data.orderByID.id, [mock]);
expect(await res.findByText('Buy')).toBeInTheDocument();
expect(await res.findByText('100')).toBeInTheDocument();
// Note: Market is not mocked so the PriceInMarket component is not rendering in this test - hence no price formatting
});
it('Cancelled modifier add strikethrough', async () => {
const res = renderComponent(
mock.result.data.orderByID.id,
[mock],
'cancelled'
);
const buy = await res.findByText('Buy');
expect(buy.parentElement).toHaveClass('line-through');
});
});
@@ -0,0 +1,50 @@
import { useExplorerDeterministicOrderQuery } from '../order-details/__generated__/Order';
import PriceInMarket from '../price-in-market/price-in-market';
import { sideText } from '../order-details/lib/order-labels';
// Note: Edited has no style currently
export type OrderSummaryModifier = 'cancelled' | 'edited';
/**
* Provides Tailwind classnames to apply to order
* @param modifier
* @returns string
*/
export function getClassName(modifier?: OrderSummaryModifier) {
if (modifier === 'cancelled') {
return 'line-through';
}
return undefined;
}
export interface OrderSummaryProps {
id: string;
modifier?: OrderSummaryModifier;
}
/**
* This component renders the *current* details for an order, like OrderDetails but much
* more compact. It is equivalent to OrderTxSummary, but operates on an order rather than
* the transaction that creates an order
*/
const OrderSummary = ({ id, modifier }: OrderSummaryProps) => {
const { data, error } = useExplorerDeterministicOrderQuery({
variables: { orderId: id },
});
if (error || !data || (data && !data.orderByID)) {
return <div data-testid="order-summary">-</div>;
}
const order = data.orderByID;
return (
<div data-testid="order-summary" className={getClassName(modifier)}>
<span>{sideText[order.side]}</span>&nbsp;
<span>{order.size}</span>&nbsp;<i>@</i>&nbsp;
<PriceInMarket marketId={order.market.id} price={order.price} />
</div>
);
};
export default OrderSummary;
@@ -0,0 +1,119 @@
import type { MockedResponse } from '@apollo/client/testing';
import type { components } from '../../../types/explorer';
import type { UnknownObject } from '../nested-data-list';
import { MockedProvider } from '@apollo/client/testing';
import OrderTxSummary from './order-tx-summary';
import { render } from '@testing-library/react';
import { ExplorerMarketDocument } from '../links/market-link/__generated__/Market';
type Order = components['schemas']['v1OrderSubmission'];
function renderComponent(order: Order, mocks?: MockedResponse[]) {
return render(
<MockedProvider mocks={mocks}>
<OrderTxSummary order={order} />
</MockedProvider>
);
}
describe('Order TX Summary component', () => {
it('Renders nothing if the order passed is somehow null', () => {
const res = renderComponent({} as UnknownObject as Order);
expect(res.queryByTestId('order-summary')).not.toBeInTheDocument();
});
it('Renders nothing if the order passed lacks a side', () => {
const o: Order = {
marketId: '123',
price: '100',
type: 'TYPE_LIMIT',
size: '10',
};
const res = renderComponent(o);
expect(res.queryByTestId('order-summary')).not.toBeInTheDocument();
});
it('Renders nothing if the order passed lacks a price', () => {
const o: Order = {
marketId: '123',
side: 'SIDE_BUY',
type: 'TYPE_LIMIT',
size: '10',
};
const res = renderComponent(o);
expect(res.queryByTestId('order-summary')).not.toBeInTheDocument();
});
it('Renders nothing if the order has an unspecified side', () => {
const o: Order = {
marketId: '123',
side: 'SIDE_UNSPECIFIED',
type: 'TYPE_LIMIT',
size: '10',
price: '10',
};
const res = renderComponent(o);
expect(res.queryByTestId('order-summary')).not.toBeInTheDocument();
});
it('Renders nothing if the order has an unspecified market', () => {
const o: Order = {
side: 'SIDE_BUY',
type: 'TYPE_LIMIT',
size: '10',
price: '10',
};
const res = renderComponent(o);
expect(res.queryByTestId('order-summary')).not.toBeInTheDocument();
});
it('side, size and price in market if all details are present', async () => {
const o: Order = {
marketId: '123',
side: 'SIDE_BUY',
type: 'TYPE_LIMIT',
size: '10',
price: '333',
};
const mock = {
request: {
query: ExplorerMarketDocument,
variables: {
id: '123',
},
},
result: {
data: {
market: {
id: '123',
decimalPlaces: 2,
state: 'irrelevant-test-data',
tradableInstrument: {
instrument: {
name: 'TEST',
product: {
__typename: 'Future',
quoteName: 'TEST',
},
},
},
},
},
},
};
const res = renderComponent(o, [mock]);
expect(res.queryByTestId('order-summary')).toBeInTheDocument();
expect(res.getByText('Buy')).toBeInTheDocument();
expect(res.getByText('10')).toBeInTheDocument();
// Initially renders price alone
expect(res.getByText('333')).toBeInTheDocument();
// After fetch renders formatted price and asset quotename
expect(await res.findByText('3.33')).toBeInTheDocument();
expect(await res.findByText('TEST')).toBeInTheDocument();
});
});
@@ -0,0 +1,41 @@
import type { components } from '../../../types/explorer';
import PriceInMarket from '../price-in-market/price-in-market';
import { sideText } from '../order-details/lib/order-labels';
export type OrderSummaryProps = {
order: components['schemas']['v1OrderSubmission'];
};
/**
* Shows a brief, relatively plaintext summary of an order transaction
* showing the price, correctly formatted. Created for the Batch Submission
* list, should be kept compact.
*
* Market name is expected to be listed elsewhere, so is not included
*/
const OrderTxSummary = ({ order }: OrderSummaryProps) => {
// Render nothing if the Order Submission doesn't look right
if (
!order ||
!order.marketId ||
!order.price ||
!order.side ||
order.side === 'SIDE_UNSPECIFIED'
) {
return null;
}
return (
<div data-testid="order-summary">
<span>{sideText[order.side]}</span>&nbsp;
<span>{order.size}</span>&nbsp;<i className="text-xs">@</i>&nbsp;
<PriceInMarket
marketId={order.marketId}
price={order.price}
></PriceInMarket>
</div>
);
};
export default OrderTxSummary;
@@ -15,6 +15,7 @@ export type PriceInMarketProps = {
const PriceInMarket = ({ marketId, price }: PriceInMarketProps) => {
const { data } = useExplorerMarketQuery({
variables: { id: marketId },
fetchPolicy: 'cache-first',
});
let label = price;
@@ -37,9 +38,9 @@ const PriceInMarket = ({ marketId, price }: PriceInMarketProps) => {
);
} else {
return (
<div className="inline-block">
<label>
<span>{label}</span> <span>{suffix}</span>
</div>
</label>
);
}
};
@@ -0,0 +1,29 @@
import type { BatchInstruction } from '../../../../routes/types/block-explorer-response';
import { TxOrderType } from '../../tx-order-type';
import { MarketLink } from '../../../links';
import OrderSummary from '../../../order-summary/order-summary';
interface BatchAmendProps {
index: number;
submission: BatchInstruction;
}
/**
* Table row for a single amendment in a batch submission
*/
export const BatchAmend = ({ index, submission }: BatchAmendProps) => {
return (
<tr key={`amend-${index}`}>
<td>{index}</td>
<td>
<TxOrderType orderType={'OrderAmendment'} />
</td>
<td>
<OrderSummary id={submission.orderId} modifier="edited" />
</td>
<td>
<MarketLink id={submission.marketId} />
</td>
</tr>
);
};
@@ -0,0 +1,29 @@
import type { BatchCancellationInstruction } from '../../../../routes/types/block-explorer-response';
import { TxOrderType } from '../../tx-order-type';
import { MarketLink } from '../../../links';
import OrderSummary from '../../../order-summary/order-summary';
interface BatchCancelProps {
index: number;
submission: BatchCancellationInstruction;
}
/**
* Table row for a single cancellation in a batch submission
*/
export const BatchCancel = ({ index, submission }: BatchCancelProps) => {
return (
<tr>
<td>{index}</td>
<td>
<TxOrderType orderType={'OrderCancellation'} />
</td>
<td>
<OrderSummary id={submission.orderId} modifier="cancelled" />
</td>
<td>
<MarketLink id={submission.marketId} />
</td>
</tr>
);
};
@@ -0,0 +1,29 @@
import type { BatchInstruction } from '../../../../routes/types/block-explorer-response';
import { TxOrderType } from '../../tx-order-type';
import { MarketLink } from '../../../links';
import OrderTxSummary from '../../../order-summary/order-tx-summary';
interface BatchOrderProps {
index: number;
submission: BatchInstruction;
}
/**
* Table row for a single order in a batch submission
*/
export const BatchOrder = ({ index, submission }: BatchOrderProps) => {
return (
<tr key={`batch-${index}`}>
<td>{index}</td>
<td>
<TxOrderType orderType={'OrderSubmission'} />
</td>
<td>
<OrderTxSummary order={submission} />
</td>
<td>
<MarketLink id={submission.marketId} />
</td>
</tr>
);
};
@@ -9,6 +9,8 @@ describe('Lib: getBlockTime', () => {
it('Returns a known date string', () => {
const mockBlockTime = '1669223762';
const usRes = getBlockTime(mockBlockTime, 'en-US');
expect(usRes).toEqual('11/23/2022, 5:16:02 PM');
expect(usRes).toContain('11/23/2022');
expect(usRes).toContain('5:16:02');
expect(usRes).toContain('PM');
});
});
@@ -2,7 +2,7 @@
export const ErrorCodes = new Map([
[51, 'Transaction failed validation'],
[60, 'Transaction could not be decoded'],
[70, 'Internal error'],
[70, 'Error'],
[80, 'Unknown command'],
[89, 'Rejected as spam'],
[0, 'Success'],
@@ -31,6 +31,10 @@ export const ChainResponseCode = ({
const icon = isSuccess ? '✅' : '❌';
const label = ErrorCodes.get(code) || 'Unknown response code';
// Hack for batches with many errors - see https://github.com/vegaprotocol/vega/issues/7245
const displayError =
error && error.length > 100 ? error.replace(/,/g, ',\r\n') : error;
return (
<div title={`Response code: ${code} - ${label}`}>
<span
@@ -41,8 +45,8 @@ export const ChainResponseCode = ({
{icon}
</span>
{hideLabel ? null : <span>{label}</span>}
{!hideLabel && !!error ? (
<span className="ml-1">&mdash;&nbsp;{error}</span>
{!hideLabel && !!displayError ? (
<span className="ml-1 whitespace-pre">&mdash;&nbsp;{displayError}</span>
) : null}
</div>
);
@@ -0,0 +1,17 @@
query ExplorerSettlementAssetForMarket($id: ID!) {
market(id: $id) {
id
decimalPlaces
tradableInstrument {
instrument {
product {
... on Future {
settlementAsset {
decimals
}
}
}
}
}
}
}
@@ -0,0 +1,60 @@
import * as Types from '@vegaprotocol/types';
import { gql } from '@apollo/client';
import * as Apollo from '@apollo/client';
const defaultOptions = {} as const;
export type ExplorerSettlementAssetForMarketQueryVariables = Types.Exact<{
id: Types.Scalars['ID'];
}>;
export type ExplorerSettlementAssetForMarketQuery = { __typename?: 'Query', market?: { __typename?: 'Market', id: string, decimalPlaces: number, tradableInstrument: { __typename?: 'TradableInstrument', instrument: { __typename?: 'Instrument', product: { __typename?: 'Future', settlementAsset: { __typename?: 'Asset', decimals: number } } } } } | null };
export const ExplorerSettlementAssetForMarketDocument = gql`
query ExplorerSettlementAssetForMarket($id: ID!) {
market(id: $id) {
id
decimalPlaces
tradableInstrument {
instrument {
product {
... on Future {
settlementAsset {
decimals
}
}
}
}
}
}
}
`;
/**
* __useExplorerSettlementAssetForMarketQuery__
*
* To run a query within a React component, call `useExplorerSettlementAssetForMarketQuery` and pass it any options that fit your needs.
* When your component renders, `useExplorerSettlementAssetForMarketQuery` returns an object from Apollo Client that contains loading, error, and data properties
* you can use to render your UI.
*
* @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
*
* @example
* const { data, loading, error } = useExplorerSettlementAssetForMarketQuery({
* variables: {
* id: // value for 'id'
* },
* });
*/
export function useExplorerSettlementAssetForMarketQuery(baseOptions: Apollo.QueryHookOptions<ExplorerSettlementAssetForMarketQuery, ExplorerSettlementAssetForMarketQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useQuery<ExplorerSettlementAssetForMarketQuery, ExplorerSettlementAssetForMarketQueryVariables>(ExplorerSettlementAssetForMarketDocument, options);
}
export function useExplorerSettlementAssetForMarketLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<ExplorerSettlementAssetForMarketQuery, ExplorerSettlementAssetForMarketQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useLazyQuery<ExplorerSettlementAssetForMarketQuery, ExplorerSettlementAssetForMarketQueryVariables>(ExplorerSettlementAssetForMarketDocument, options);
}
export type ExplorerSettlementAssetForMarketQueryHookResult = ReturnType<typeof useExplorerSettlementAssetForMarketQuery>;
export type ExplorerSettlementAssetForMarketLazyQueryHookResult = ReturnType<typeof useExplorerSettlementAssetForMarketLazyQuery>;
export type ExplorerSettlementAssetForMarketQueryResult = Apollo.QueryResult<ExplorerSettlementAssetForMarketQuery, ExplorerSettlementAssetForMarketQueryVariables>;
@@ -0,0 +1,131 @@
import { MockedProvider } from '@apollo/client/testing';
import { render } from '@testing-library/react';
import { Side } from '@vegaprotocol/types';
import type { LiquidityOrder } from '@vegaprotocol/types';
import { PeggedReference } from '@vegaprotocol/types';
import { LiquidityProvisionDetailsRow } from './liquidity-provision-details-row';
import type { VegaSide } from './liquidity-provision-details-row';
describe('LiquidityProvisionDetails component', () => {
function renderComponent(
order: LiquidityOrder,
side: VegaSide,
normaliseProportionsTo: number,
marketId: string
) {
return render(
<MockedProvider>
<table>
<tbody data-testid="container">
<LiquidityProvisionDetailsRow
order={order}
marketId={marketId}
normaliseProportionsTo={normaliseProportionsTo}
side={side}
/>
</tbody>
</table>
</MockedProvider>
);
}
it('renders null for an order with no proportion', () => {
const mockOrder = {
offset: '1',
reference: PeggedReference.PEGGED_REFERENCE_MID,
};
const res = renderComponent(
mockOrder as LiquidityOrder,
Side.SIDE_BUY,
100,
'123'
);
expect(res.getByTestId('container')).toBeEmptyDOMElement();
});
it('renders null for a null order', () => {
const res = renderComponent(
null as unknown as LiquidityOrder,
Side.SIDE_BUY,
100,
'123'
);
expect(res.getByTestId('container')).toBeEmptyDOMElement();
});
it('renders a row when the order is as expected', () => {
const mockOrder = {
offset: '1',
proportion: 20,
reference: PeggedReference.PEGGED_REFERENCE_MID,
};
const res = renderComponent(
mockOrder as LiquidityOrder,
Side.SIDE_BUY,
100,
'123'
);
// Row test ids and keys are based on the side, reference and proportion
expect(res.getByTestId('SIDE_BUY-20-1')).toBeInTheDocument();
expect(res.getByText('+1')).toBeInTheDocument();
expect(res.getByText('Mid')).toBeInTheDocument();
expect(res.getByText('20%')).toBeInTheDocument();
});
it('normalises offsets when normaliseToProportion is not 100', () => {
const mockOrder = {
offset: '1',
proportion: 20,
reference: PeggedReference.PEGGED_REFERENCE_BEST_BID,
};
const res = renderComponent(
mockOrder as LiquidityOrder,
Side.SIDE_SELL,
50,
'123'
);
// Row test ids and keys are based on the side, reference and proportion - and that proportion is scaled
expect(res.getByTestId('SIDE_SELL-40-1')).toBeInTheDocument();
expect(res.getByText('-1')).toBeInTheDocument();
expect(res.getByText('Best Bid')).toBeInTheDocument();
expect(res.getByText('40% (normalised from: 20%)')).toBeInTheDocument();
});
it('handles a missing offset gracefully (should not happen)', () => {
const mockOrder = {
proportion: 20,
reference: PeggedReference.PEGGED_REFERENCE_BEST_BID,
};
const res = renderComponent(
mockOrder as LiquidityOrder,
Side.SIDE_SELL,
50,
'123'
);
// Row test ids and keys are based on the side, reference and proportion - and that proportion is scaled
expect(res.getByTestId('SIDE_SELL-40-')).toBeInTheDocument();
expect(res.getByText('-')).toBeInTheDocument();
});
it('handles a missing reference gracefully (should not happen)', () => {
const mockOrder = {
offset: '1',
proportion: 20,
};
const res = renderComponent(
mockOrder as LiquidityOrder,
Side.SIDE_SELL,
50,
'123'
);
// Row test ids and keys are based on the side, reference and proportion - and that proportion is scaled
expect(res.getByTestId('SIDE_SELL-40-1')).toBeInTheDocument();
expect(res.getByText('40% (normalised from: 20%)')).toBeInTheDocument();
expect(res.getByText('-')).toBeInTheDocument();
});
});
@@ -0,0 +1,77 @@
import { t } from '@vegaprotocol/react-helpers';
import type { components } from '../../../../../../types/explorer';
import { TableRow } from '../../../../table';
import { LiquidityProvisionOffset } from './liquidity-provision-offset';
export type VegaPeggedReference = components['schemas']['vegaPeggedReference'];
export type VegaSide = components['schemas']['vegaSide'];
export type LiquidityProvisionOrder =
components['schemas']['vegaLiquidityOrder'];
export const LiquidityReferenceLabel: Record<VegaPeggedReference, string> = {
PEGGED_REFERENCE_BEST_ASK: t('Best Ask'),
PEGGED_REFERENCE_BEST_BID: t('Best Bid'),
PEGGED_REFERENCE_MID: t('Mid'),
PEGGED_REFERENCE_UNSPECIFIED: '-',
};
export type LiquidityProvisionDetailsRowProps = {
order?: LiquidityProvisionOrder;
marketId?: string;
side: VegaSide;
// If this is
normaliseProportionsTo: number;
};
/**
*
* Note: offset is formatted by settlement asset on the market, assuming that is available
* Note: Due to the mix of references (MID vs BEST_X), it's not possible to correctly order
* the orders by their actual distance from a midpoint. This would require us knowing
* the best bid (now or at placement) and the mid. Getting the data for *now* would be
* misleading for LP submissions in the past. There is no API for getting <mid />
* at the time of a transaction.
*/
export function LiquidityProvisionDetailsRow({
normaliseProportionsTo,
order,
side,
marketId,
}: LiquidityProvisionDetailsRowProps) {
if (!order || !order.proportion) {
return null;
}
const proportion =
normaliseProportionsTo === 100
? order.proportion
: Math.round((order.proportion / normaliseProportionsTo) * 100);
const key = `${side}-${proportion}-${order.offset ? order.offset : ''}`;
return (
<TableRow modifier="bordered" key={key} data-testid={key}>
<td className="text-right px-2">
{order.offset && marketId ? (
<LiquidityProvisionOffset
offset={order.offset}
side={side}
marketId={marketId}
/>
) : (
'-'
)}
</td>
<td className="text-center">
{order.reference ? LiquidityReferenceLabel[order.reference] : '-'}
</td>
<td className="text-center">
{proportion === order.proportion
? `${proportion}%`
: `${proportion}% (normalised from: ${order.proportion}%)`}{' '}
</td>
</TableRow>
);
}
@@ -0,0 +1,22 @@
import { render } from '@testing-library/react';
import { LiquidityProvisionMid } from './liquidity-provision-mid';
describe('LiquidityProvisionMid component', () => {
function renderComponent() {
return render(
<table>
<tbody data-testid="container">
<LiquidityProvisionMid />
</tbody>
</table>
);
}
it('renders a basic row that spans the whole table', () => {
const res = renderComponent();
const display = res.getByTestId('mid-display');
expect(res.getByTestId('mid')).toBeInTheDocument();
expect(display).toBeInTheDocument();
expect(display).toHaveAttribute('colspan', '3');
});
});
@@ -0,0 +1,17 @@
import { TableRow } from '../../../../table';
/**
* In a LiquidityProvision table, this row is the midpoint. Above our LP orders on the
* buy side, below are LP orders on the sell side. This component simply divides them.
*
* There is no API that can give us the mid price when the order was created, and even
* if there was it isn't clear that would be appropriate for this centre row. So instead
* it's a simple divider.
*/
export function LiquidityProvisionMid() {
return (
<TableRow modifier="bordered" data-testid="mid">
<td data-testid="mid-display" colSpan={3} className="bg-white"></td>
</TableRow>
);
}
@@ -0,0 +1,76 @@
import { MockedProvider } from '@apollo/client/testing';
import type { MockedResponse } from '@apollo/client/testing';
import { render } from '@testing-library/react';
import { ExplorerSettlementAssetForMarketDocument } from '../__generated__/Explorer-settlement-asset';
import type { ExplorerSettlementAssetForMarketQuery } from '../__generated__/Explorer-settlement-asset';
import type { VegaSide } from './liquidity-provision-details-row';
import {
getFormattedOffset,
LiquidityProvisionOffset,
} from './liquidity-provision-offset';
const decimalsMock: ExplorerSettlementAssetForMarketQuery = {
market: {
id: '123',
__typename: 'Market',
decimalPlaces: 5,
tradableInstrument: {
instrument: {
product: {
settlementAsset: {
decimals: 5,
},
},
},
},
},
};
describe('LiquidityProvisionOffset component', () => {
function renderComponent(
offset: string,
side: VegaSide,
marketId: string,
mocks: MockedResponse[]
) {
return render(
<MockedProvider mocks={mocks}>
<LiquidityProvisionOffset
offset={offset}
side={side}
marketId={marketId}
/>
</MockedProvider>
);
}
it('renders a simple row before market data comes in', () => {
const res = renderComponent('1', 'SIDE_BUY', '123', []);
expect(res.getByText('+1')).toBeInTheDocument();
});
it('replaces unformatted with formatted if the market data comes in', () => {
const mock = {
request: {
query: ExplorerSettlementAssetForMarketDocument,
variables: {
id: '123',
},
result: {
data: decimalsMock,
},
},
};
const res = renderComponent('1', 'SIDE_BUY', '123', [mock]);
expect(res.getByText('+1')).toBeInTheDocument();
});
it('getFormattedOffset returns the unformatted offset if there is not enough data', () => {
const res = getFormattedOffset('1', {});
expect(res).toEqual('1');
});
it('getFormattedOffset decimal formats a number if it comes in with market data', () => {
const res = getFormattedOffset('1', decimalsMock);
expect(res).toEqual('0.00001');
});
});
@@ -0,0 +1,61 @@
import { useExplorerSettlementAssetForMarketQuery } from '../__generated__/Explorer-settlement-asset';
import { addDecimalsFormatNumber } from '@vegaprotocol/react-helpers';
import type { ExplorerSettlementAssetForMarketQuery } from '../__generated__/Explorer-settlement-asset';
import type { VegaSide } from './liquidity-provision-details-row';
export type LiquidityProvisionOffsetProps = {
side: VegaSide;
offset: string;
marketId: string;
};
/**
* Correctly formats an LP's offset according to the market settlement decimal places.
* Initially this will appear unformatted, then when the query loads in the proper formatted
* value will be displayed
*
* @see getFormattedOffset
*/
export function LiquidityProvisionOffset({
side,
offset,
marketId,
}: LiquidityProvisionOffsetProps) {
const { data } = useExplorerSettlementAssetForMarketQuery({
variables: {
id: marketId,
},
});
// getFormattedOffset handles missing results/loading states
const formattedOffset = getFormattedOffset(offset, data);
const label = side === 'SIDE_BUY' ? '+' : '-';
const className = side === 'SIDE_BUY' ? 'text-vega-green' : 'text-vega-pink';
return <span className={className}>{`${label}${formattedOffset}`}</span>;
}
/**
* Does the work of formatting the number now we have the settlement decimal places.
* If no market data is assigned (i.e. during loading, or if the market doesn't exist)
* this function will return the unformatted number
*
* @see LiquidityProvisionOffset
* @param data the result of a ExplorerSettlementAssetForMarketQuery
* @param offset the unformatted offset
* @returns string the offset of this lp order formatted with the settlement decimal places
*/
export function getFormattedOffset(
offset: string,
data?: ExplorerSettlementAssetForMarketQuery
) {
const decimals =
data?.market?.tradableInstrument.instrument.product.settlementAsset
.decimals;
if (!decimals) {
return offset;
}
return addDecimalsFormatNumber(offset, decimals);
}
@@ -0,0 +1,209 @@
import { MockedProvider } from '@apollo/client/testing';
import { render } from '@testing-library/react';
import type { LiquidityOrder } from '@vegaprotocol/types';
import { PeggedReference } from '@vegaprotocol/types';
import type { LiquiditySubmission } from '../tx-liquidity-submission';
import {
LiquidityProvisionDetails,
sumProportions,
} from './liquidity-provision-details';
function mockProportion(proportion: number): LiquidityOrder {
return {
proportion,
reference: PeggedReference.PEGGED_REFERENCE_MID,
offset: '1',
};
}
describe('sumProportions function', () => {
it('returns 0 if the side is undefined', () => {
const side: LiquidityOrder[] = undefined as unknown as LiquidityOrder[];
const res = sumProportions(side);
expect(res).toEqual(0);
});
it('returns 0 if the side is empty', () => {
const side: LiquidityOrder[] = [];
const res = sumProportions(side);
expect(res).toEqual(0);
});
it('sums 1 item correctly (under 100%)', () => {
const side: LiquidityOrder[] = [mockProportion(10)];
const res = sumProportions(side);
expect(res).toEqual(10);
});
it('sums 2 item correctly (exactly 100%)', () => {
const side: LiquidityOrder[] = [mockProportion(50), mockProportion(50)];
const res = sumProportions(side);
expect(res).toEqual(100);
});
it('sums 3 item correctly to over 100%', () => {
const side: LiquidityOrder[] = [
mockProportion(20),
mockProportion(40),
mockProportion(50),
];
const res = sumProportions(side);
expect(res).toEqual(110);
});
});
describe('LiquidityProvisionDetails component', () => {
function renderComponent(provision: LiquiditySubmission) {
return render(
<MockedProvider>
<LiquidityProvisionDetails provision={provision} />
</MockedProvider>
);
}
it('handles an LP with no buys or sells by returning empty (should never happen)', () => {
const mock: LiquiditySubmission = {};
const res = renderComponent(mock);
expect(res.container).toBeEmptyDOMElement();
});
it('handles an LP with no sells by just rendering buys', () => {
const mock: LiquiditySubmission = {
marketId: '123',
buys: [
{
offset: '1',
proportion: 50,
reference: PeggedReference.PEGGED_REFERENCE_MID,
},
{
offset: '2',
proportion: 50,
reference: PeggedReference.PEGGED_REFERENCE_MID,
},
],
};
const res = renderComponent(mock);
expect(res.getByText('Price offset')).toBeInTheDocument();
expect(res.getByText('Price reference')).toBeInTheDocument();
expect(res.getByText('Proportion')).toBeInTheDocument();
expect(res.getByTestId('SIDE_BUY-50-1')).toBeInTheDocument();
expect(res.getByTestId('SIDE_BUY-50-2')).toBeInTheDocument();
});
it('handles an LP with no buys by just rendering sells', () => {
const mock: LiquiditySubmission = {
marketId: '123',
sells: [
{
offset: '1',
proportion: 50,
reference: PeggedReference.PEGGED_REFERENCE_MID,
},
{
offset: '2',
proportion: 50,
reference: PeggedReference.PEGGED_REFERENCE_MID,
},
],
};
const res = renderComponent(mock);
expect(res.getByText('Price offset')).toBeInTheDocument();
expect(res.getByText('Price reference')).toBeInTheDocument();
expect(res.getByText('Proportion')).toBeInTheDocument();
expect(res.getByTestId('SIDE_SELL-50-1')).toBeInTheDocument();
expect(res.getByTestId('SIDE_SELL-50-2')).toBeInTheDocument();
});
it('handles an LP with sells by just rendering buys', () => {
const mock: LiquiditySubmission = {
marketId: '123',
buys: [
{
offset: '1',
proportion: 50,
reference: PeggedReference.PEGGED_REFERENCE_MID,
},
{
offset: '2',
proportion: 50,
reference: PeggedReference.PEGGED_REFERENCE_MID,
},
],
};
const res = renderComponent(mock);
expect(res.getByText('Price offset')).toBeInTheDocument();
expect(res.getByText('Price reference')).toBeInTheDocument();
expect(res.getByText('Proportion')).toBeInTheDocument();
expect(res.getByTestId('SIDE_BUY-50-1')).toBeInTheDocument();
expect(res.getByTestId('SIDE_BUY-50-2')).toBeInTheDocument();
});
it('handles an LP with both sides', () => {
const mock: LiquiditySubmission = {
marketId: '123',
buys: [
{
offset: '1',
proportion: 50,
reference: PeggedReference.PEGGED_REFERENCE_MID,
},
{
offset: '2',
proportion: 50,
reference: PeggedReference.PEGGED_REFERENCE_MID,
},
],
sells: [
{
offset: '4',
proportion: 50,
reference: PeggedReference.PEGGED_REFERENCE_MID,
},
{
offset: '2',
proportion: 50,
reference: PeggedReference.PEGGED_REFERENCE_MID,
},
],
};
const res = renderComponent(mock);
expect(res.getByText('Price offset')).toBeInTheDocument();
expect(res.getByText('Price reference')).toBeInTheDocument();
expect(res.getByText('Proportion')).toBeInTheDocument();
expect(res.getByTestId('SIDE_BUY-50-1')).toBeInTheDocument();
expect(res.getByTestId('SIDE_BUY-50-2')).toBeInTheDocument();
expect(res.getByTestId('SIDE_SELL-50-4')).toBeInTheDocument();
expect(res.getByTestId('SIDE_SELL-50-2')).toBeInTheDocument();
});
it('normalises proportions when they do not total 100%', () => {
const mock: LiquiditySubmission = {
marketId: '123',
buys: [
{
offset: '1',
proportion: 25,
reference: PeggedReference.PEGGED_REFERENCE_MID,
},
{
offset: '2',
proportion: 30,
reference: PeggedReference.PEGGED_REFERENCE_MID,
},
],
};
const res = renderComponent(mock);
expect(res.getByText('45% (normalised from: 25%)')).toBeInTheDocument();
expect(res.getByText('55% (normalised from: 30%)')).toBeInTheDocument();
});
});
@@ -0,0 +1,94 @@
import { t } from '@vegaprotocol/react-helpers';
import type { components } from '../../../../../types/explorer';
import type { LiquiditySubmission } from '../tx-liquidity-submission';
import { TableRow } from '../../../table';
import { LiquidityProvisionMid } from './components/liquidity-provision-mid';
import { LiquidityProvisionDetailsRow } from './components/liquidity-provision-details-row';
import { Side } from '@vegaprotocol/types';
export type VegaPeggedReference = components['schemas']['vegaPeggedReference'];
export type LiquidityProvisionOrder =
components['schemas']['vegaLiquidityOrder'];
export const LiquidityReferenceLabel: Record<VegaPeggedReference, string> = {
PEGGED_REFERENCE_BEST_ASK: t('Best Ask'),
PEGGED_REFERENCE_BEST_BID: t('Best Bid'),
PEGGED_REFERENCE_MID: t('Mid'),
PEGGED_REFERENCE_UNSPECIFIED: '-',
};
/**
* Given a side of a liquidity provision order, returns the total
* It should be 100%, but it isn't always and if it isn't the proportion
* reported for each order should be scaled
*
* @returns number
*/
export function sumProportions(
side: LiquiditySubmission['buys'] | LiquiditySubmission['sells']
): number {
if (!side || side.length === 0) {
return 0;
}
return side.reduce((total, o) => total + (o.proportion || 0), 0);
}
export type LiquidityProvisionDetailsProps = {
provision: LiquiditySubmission;
};
/**
* Renders a table displaying all buys and sells in this LP. It is valid for there
* to be no buys or sells.
*
* It might seem logical to turn proportions in to values based on the total commitment
* but based on the current API structure it is awkward, and given that non-LP orders
* will change the amount that is actually deployed vs assigned to a level, we decided
* not to bother going down that route.
*/
export function LiquidityProvisionDetails({
provision,
}: LiquidityProvisionDetailsProps) {
if (!provision.buys?.length && !provision.sells?.length) {
return null;
}
// We need to do some additional calcs if these aren't both 100
const buyTotal = sumProportions(provision.buys);
const sellTotal = sumProportions(provision.sells);
return (
<table>
<thead>
<TableRow modifier="bordered">
<th className="px-2 pb-1">{t('Price offset')}</th>
<th className="px-2 pb-1">{t('Price reference')}</th>
<th className="px-2 pb-1">{t('Proportion')}</th>
</TableRow>
</thead>
<tbody>
{provision.buys?.map((b, i) => (
<LiquidityProvisionDetailsRow
order={b}
marketId={provision.marketId}
side={Side.SIDE_BUY}
key={`SIDE_BUY-${i}`}
normaliseProportionsTo={buyTotal}
/>
))}
<LiquidityProvisionMid />
{provision.sells?.map((s, i) => (
<LiquidityProvisionDetailsRow
order={s}
marketId={provision.marketId}
side={Side.SIDE_SELL}
key={`SIDE_SELL-${i}`}
normaliseProportionsTo={sellTotal}
/>
))}
</tbody>
</table>
);
}
@@ -14,6 +14,12 @@ interface TxDetailsSharedProps {
blockData: TendermintBlocksResponse | undefined;
}
// Applied to all header cells
const sharedHeaderProps = {
// Ensures that multi line contents still have the header aligned to the first line
className: 'align-top',
};
/**
* These rows are shown for every transaction type, providing a consistent set of rows for the top
* of a transaction details row. The order is relatively arbitrary but felt right - it might need to
@@ -34,27 +40,27 @@ export const TxDetailsShared = ({
return (
<>
<TableRow modifier="bordered">
<TableCell>{t('Type')}</TableCell>
<TableCell {...sharedHeaderProps}>{t('Type')}</TableCell>
<TableCell>{txData.type}</TableCell>
</TableRow>
<TableRow modifier="bordered">
<TableCell>{t('Hash')}</TableCell>
<TableCell {...sharedHeaderProps}>{t('Hash')}</TableCell>
<TableCell>
<code>{txData.hash}</code>
</TableCell>
</TableRow>
<TableRow modifier="bordered">
<TableCell>{t('Submitter')}</TableCell>
<TableCell {...sharedHeaderProps}>{t('Submitter')}</TableCell>
<TableCell>{pubKey ? <PartyLink id={pubKey} /> : '-'}</TableCell>
</TableRow>
<TableRow modifier="bordered">
<TableCell>{t('Block')}</TableCell>
<TableCell {...sharedHeaderProps}>{t('Block')}</TableCell>
<TableCell>
<BlockLink height={height} />
</TableCell>
</TableRow>
<TableRow modifier="bordered">
<TableCell>{t('Time')}</TableCell>
<TableCell {...sharedHeaderProps}>{t('Time')}</TableCell>
<TableCell>
{time ? (
<div>
@@ -71,7 +77,7 @@ export const TxDetailsShared = ({
</TableCell>
</TableRow>
<TableRow modifier="bordered">
<TableCell>{t('Response code')}</TableCell>
<TableCell {...sharedHeaderProps}>{t('Response code')}</TableCell>
<TableCell>
<ChainResponseCode code={txData.code} error={txData.error} />
</TableCell>
@@ -1,8 +1,15 @@
import { t } from '@vegaprotocol/react-helpers';
import type { BlockExplorerTransactionResult } from '../../../routes/types/block-explorer-response';
import type {
BatchCancellationInstruction,
BatchInstruction,
BlockExplorerTransactionResult,
} from '../../../routes/types/block-explorer-response';
import type { TendermintBlocksResponse } from '../../../routes/blocks/tendermint-blocks-response';
import { TxDetailsShared } from './shared/tx-details-shared';
import { TableWithTbody, TableRow, TableCell } from '../../table';
import { TableWithTbody, TableRow, TableCell, Table } from '../../table';
import { BatchCancel } from './batch-submission/batch-cancel';
import { BatchAmend } from './batch-submission/batch-amend';
import { BatchOrder } from './batch-submission/batch-order';
interface TxDetailsBatchProps {
txData: BlockExplorerTransactionResult | undefined;
@@ -16,6 +23,11 @@ interface TxDetailsBatchProps {
*
* Design considerations for batch:
* - So far it's very basic details about the size of the batch
*
* Batches are processed in the following order:
* - Cancellations
* - Amends
* - Submissions
*/
export const TxDetailsBatch = ({
txData,
@@ -26,47 +38,80 @@ export const TxDetailsBatch = ({
return <>{t('Awaiting Block Explorer transaction details')}</>;
}
const countSubmissions =
txData.command.batchMarketInstructions.submissions?.length || 0;
const countAmendments =
txData.command.batchMarketInstructions.amendments?.length || 0;
const countCancellations =
txData.command.batchMarketInstructions.cancellations?.length || 0;
const countTotal = countSubmissions + countAmendments + countCancellations;
const submissions: BatchInstruction[] =
txData.command.batchMarketInstructions.submissions;
const countSubmissions = submissions?.length || 0;
const amendments: BatchInstruction[] =
txData.command.batchMarketInstructions.amendments;
const countAmendments = amendments?.length || 0;
const cancellations: BatchCancellationInstruction[] =
txData.command.batchMarketInstructions.cancellations;
const countCancellations = cancellations.length || 0;
const countTotal = countSubmissions + countAmendments + countCancellations;
let index = 0;
return (
<TableWithTbody className="mb-8">
<TxDetailsShared txData={txData} pubKey={pubKey} blockData={blockData} />
<TableRow modifier="bordered">
<TableCell>{t('Batch size')}</TableCell>
<TableCell>
<span>{countTotal}</span>
</TableCell>
</TableRow>
<TableRow modifier="bordered">
<TableCell>
<span className="ml-5">{t('Submissions')}</span>
</TableCell>
<TableCell>
<span>{countSubmissions}</span>
</TableCell>
</TableRow>
<TableRow modifier="bordered">
<TableCell>
<span className="ml-5">{t('Amendments')}</span>
</TableCell>
<TableCell>
<span>{countAmendments}</span>
</TableCell>
</TableRow>
<TableRow modifier="bordered">
<TableCell>
<span className="ml-5">{t('Cancellations')}</span>
</TableCell>
<TableCell>
<span>{countCancellations}</span>
</TableCell>
</TableRow>
</TableWithTbody>
<div key={`tx-${index}`}>
<TableWithTbody className="mb-8">
<TxDetailsShared
txData={txData}
pubKey={pubKey}
blockData={blockData}
/>
<TableRow modifier="bordered">
<TableCell>{t('Batch size')}</TableCell>
<TableCell>
<span>{countTotal}</span>
</TableCell>
</TableRow>
<TableRow modifier="bordered">
<TableCell>
<span className="ml-5">{t('Cancellations')}</span>
</TableCell>
<TableCell>
<span>{countCancellations}</span>
</TableCell>
</TableRow>
<TableRow modifier="bordered">
<TableCell>
<span className="ml-5">{t('Amendments')}</span>
</TableCell>
<TableCell>
<span>{countAmendments}</span>
</TableCell>
</TableRow>
<TableRow modifier="bordered">
<TableCell>
<span className="ml-5">{t('Submissions')}</span>
</TableCell>
<TableCell>
<span>{countSubmissions}</span>
</TableCell>
</TableRow>
</TableWithTbody>
<Table className="max-w-5xl min-w-fit">
<thead>
<TableRow modifier="bordered" className="font-mono">
<th align="left">{t('#')}</th>
<th align="left">{t('Type')}</th>
<th align="left">{t('Order')}</th>
<th align="left">{t('Market')}</th>
</TableRow>
</thead>
<tbody>
{cancellations.map((c) => (
<BatchCancel key={`bc-${index}`} submission={c} index={index++} />
))}
{amendments.map((a) => (
<BatchAmend key={`ba-${index}`} submission={a} index={index++} />
))}
{submissions.map((s) => (
<BatchOrder key={`bo-${index}`} submission={s} index={index++} />
))}
</tbody>
</Table>
</div>
);
};
@@ -5,7 +5,6 @@ import { TxDetailsOrder } from './tx-order';
import type { BlockExplorerTransactionResult } from '../../../routes/types/block-explorer-response';
import type { TendermintBlocksResponse } from '../../../routes/blocks/tendermint-blocks-response';
import { TxDetailsHeartbeat } from './tx-hearbeat';
import { TxDetailsLPAmend } from './tx-lp-amend';
import { TxDetailsGeneric } from './tx-generic';
import { TxDetailsBatch } from './tx-batch';
import { TxDetailsChainEvent } from './tx-chain-event';
@@ -17,6 +16,8 @@ import { TxDetailsOrderAmend } from './tx-order-amend';
import { TxDetailsWithdrawSubmission } from './tx-withdraw-submission';
import { TxDetailsDelegate } from './tx-delegation';
import { TxDetailsUndelegate } from './tx-undelegation';
import { TxDetailsLiquiditySubmission } from './tx-liquidity-submission';
import { TxDetailsLiquidityAmendment } from './tx-liquidity-amend';
interface TxDetailsWrapperProps {
txData: BlockExplorerTransactionResult | undefined;
@@ -45,7 +46,7 @@ export const TxDetailsWrapper = ({
const raw = get(blockData, `result.block.data.txs[${txData.index}]`);
return (
<>
<div key={`txd-${txData.hash}`}>
<section>{child({ txData, pubKey, blockData })}</section>
<details title={t('Decoded transaction')} className="mt-3">
@@ -59,7 +60,7 @@ export const TxDetailsWrapper = ({
<code className="break-all font-mono text-xs">{raw}</code>
</details>
) : null}
</>
</div>
);
};
@@ -83,8 +84,6 @@ function getTransactionComponent(txData?: BlockExplorerTransactionResult) {
return TxDetailsOrderAmend;
case 'Validator Heartbeat':
return TxDetailsHeartbeat;
case 'Amend LiquidityProvision Order':
return TxDetailsLPAmend;
case 'Batch Market Instructions':
return TxDetailsBatch;
case 'Chain Event':
@@ -93,6 +92,10 @@ function getTransactionComponent(txData?: BlockExplorerTransactionResult) {
return TxDetailsNodeVote;
case 'Withdraw':
return TxDetailsWithdrawSubmission;
case 'Liquidity Provision Order':
return TxDetailsLiquiditySubmission;
case 'Amend Liquidity Provision Order':
return TxDetailsLiquidityAmendment;
case 'Delegate':
return TxDetailsDelegate;
case 'Undelegate':
@@ -0,0 +1,73 @@
import { t } from '@vegaprotocol/react-helpers';
import type { BlockExplorerTransactionResult } from '../../../routes/types/block-explorer-response';
import { MarketLink } from '../../links';
import type { TendermintBlocksResponse } from '../../../routes/blocks/tendermint-blocks-response';
import { TxDetailsShared } from './shared/tx-details-shared';
import { TableCell, TableRow, TableWithTbody } from '../../table';
import type { components } from '../../../../types/explorer';
import { LiquidityProvisionDetails } from './liquidity-provision/liquidity-provision-details';
import PriceInMarket from '../../price-in-market/price-in-market';
export type LiquidityAmendment =
components['schemas']['v1LiquidityProvisionAmendment'];
interface TxDetailsLiquidityAmendmentProps {
txData: BlockExplorerTransactionResult | undefined;
pubKey: string | undefined;
blockData: TendermintBlocksResponse | undefined;
}
/**
* An existing liquidity order is being amended. This uses
* exactly the same details as the creation
*/
export const TxDetailsLiquidityAmendment = ({
txData,
pubKey,
blockData,
}: TxDetailsLiquidityAmendmentProps) => {
if (!txData || !txData.command.liquidityProvisionAmendment) {
return <>{t('Awaiting Block Explorer transaction details')}</>;
}
const amendment: LiquidityAmendment =
txData.command.liquidityProvisionAmendment;
const marketId: string = amendment.marketId || '-';
return (
<>
<TableWithTbody className="mb-8">
<TxDetailsShared
txData={txData}
pubKey={pubKey}
blockData={blockData}
/>
<TableRow modifier="bordered">
<TableCell>{t('Market')}</TableCell>
<TableCell>
<MarketLink id={marketId} />
</TableCell>
</TableRow>
{amendment.commitmentAmount ? (
<TableRow modifier="bordered">
<TableCell>{t('Commitment amount')}</TableCell>
<TableCell>
<PriceInMarket
price={amendment.commitmentAmount}
marketId={marketId}
/>
</TableCell>
</TableRow>
) : null}
{amendment.fee ? (
<TableRow modifier="bordered">
<TableCell>{t('Fee')}</TableCell>
<TableCell>{amendment.fee}%</TableCell>
</TableRow>
) : null}
</TableWithTbody>
<LiquidityProvisionDetails provision={amendment} />
</>
);
};
@@ -0,0 +1,72 @@
import { t } from '@vegaprotocol/react-helpers';
import type { BlockExplorerTransactionResult } from '../../../routes/types/block-explorer-response';
import { MarketLink } from '../../links/';
import type { TendermintBlocksResponse } from '../../../routes/blocks/tendermint-blocks-response';
import { TxDetailsShared } from './shared/tx-details-shared';
import { TableCell, TableRow, TableWithTbody } from '../../table';
import type { components } from '../../../../types/explorer';
import { LiquidityProvisionDetails } from './liquidity-provision/liquidity-provision-details';
import PriceInMarket from '../../price-in-market/price-in-market';
export type LiquiditySubmission =
components['schemas']['v1LiquidityProvisionSubmission'];
interface TxDetailsLiquiditySubmissionProps {
txData: BlockExplorerTransactionResult | undefined;
pubKey: string | undefined;
blockData: TendermintBlocksResponse | undefined;
}
/**
* Someone cancelled an order
*/
export const TxDetailsLiquiditySubmission = ({
txData,
pubKey,
blockData,
}: TxDetailsLiquiditySubmissionProps) => {
if (!txData || !txData.command.liquidityProvisionSubmission) {
return <>{t('Awaiting Block Explorer transaction details')}</>;
}
const submission: LiquiditySubmission =
txData.command.liquidityProvisionSubmission;
const marketId: string = submission.marketId || '-';
return (
<>
<TableWithTbody className="mb-8">
<TxDetailsShared
txData={txData}
pubKey={pubKey}
blockData={blockData}
/>
<TableRow modifier="bordered">
<TableCell>{t('Market')}</TableCell>
<TableCell>
<MarketLink id={marketId} />
</TableCell>
</TableRow>
{submission.commitmentAmount ? (
<TableRow modifier="bordered">
<TableCell>{t('Commitment amount')}</TableCell>
<TableCell>
<PriceInMarket
price={submission.commitmentAmount}
marketId={marketId}
/>
</TableCell>
</TableRow>
) : null}
{submission.fee ? (
<TableRow modifier="bordered">
<TableCell>{t('Fee')}</TableCell>
<TableCell>{submission.fee}%</TableCell>
</TableRow>
) : null}
</TableWithTbody>
<LiquidityProvisionDetails provision={submission} />
</>
);
};
@@ -1,41 +0,0 @@
import { t } from '@vegaprotocol/react-helpers';
import type { BlockExplorerTransactionResult } from '../../../routes/types/block-explorer-response';
import { MarketLink } from '../../links/';
import type { TendermintBlocksResponse } from '../../../routes/blocks/tendermint-blocks-response';
import { TxDetailsShared } from './shared/tx-details-shared';
import { TableCell, TableRow, TableWithTbody } from '../../table';
interface TxDetailsOrderProps {
txData: BlockExplorerTransactionResult | undefined;
pubKey: string | undefined;
blockData: TendermintBlocksResponse | undefined;
}
/**
* Specifies changes to the shape of a users Liquidity Commitment order for
* a specific market. So far this only displays the market, which is only
* because it's very easy to do so.
*/
export const TxDetailsLPAmend = ({
txData,
pubKey,
blockData,
}: TxDetailsOrderProps) => {
if (!txData) {
return <>{t('Awaiting Block Explorer transaction details')}</>;
}
const marketId = txData.command.liquidityProvisionAmendment?.marketId || '';
return (
<TableWithTbody className="mb-8">
<TxDetailsShared txData={txData} pubKey={pubKey} blockData={blockData} />
<TableRow modifier="bordered">
<TableCell>{t('Market')}</TableCell>
<TableCell>
<MarketLink id={marketId} />
</TableCell>
</TableRow>
</TableWithTbody>
);
};
@@ -14,8 +14,6 @@ export const methodText: Record<
METHOD_NOW: 'Immediate',
METHOD_UNSPECIFIED: 'Unspecified',
METHOD_AT_END_OF_EPOCH: 'End of epoch',
// This will be removed in a future release
METHOD_IN_ANGER: 'Immediate',
};
interface TxDetailsUndelegateProps {
@@ -2,4 +2,3 @@ export { TxList } from './tx-list';
export { TxOrderType } from './tx-order-type';
export { TxsInfiniteList } from './txs-infinite-list';
export { TxsInfiniteListItem } from './txs-infinite-list-item';
export { TxsStatsInfo } from './txs-stats-info';
@@ -137,8 +137,7 @@ export const TxOrderType = ({ orderType, command }: TxOrderTypeProps) => {
// This will get unwieldy and should probably produce a different colour of tag
if (type === 'Chain Event' && !!command?.chainEvent) {
type = getLabelForChainEvent(command.chainEvent);
colours =
'text-white dark-text-white bg-vega-pink-dark dark:bg-vega-pink-dark';
colours = 'text-white dark-text-white bg-vega-pink dark:bg-vega-pink';
} else if (type === 'Proposal' || type === 'Governance Proposal') {
if (command && !!command.proposalSubmission) {
type = getLabelForProposal(command.proposalSubmission);
@@ -2,6 +2,7 @@ import { TxsInfiniteList } from './txs-infinite-list';
import { render, screen, fireEvent, act } from '@testing-library/react';
import { MemoryRouter } from 'react-router-dom';
import type { BlockExplorerTransactionResult } from '../../routes/types/block-explorer-response';
import { Side } from '@vegaprotocol/types';
const generateTxs = (number: number): BlockExplorerTransactionResult[] => {
return Array.from(Array(number)).map((_) => ({
@@ -24,7 +25,7 @@ const generateTxs = (number: number): BlockExplorerTransactionResult[] => {
'b4d0a070f5cc73a7d53b23d6f63f8cb52e937ed65d2469a3af4cc1e80e155fcf',
price: '14525946',
size: '54',
side: 'SIDE_SELL',
side: Side.SIDE_SELL,
timeInForce: 'TIME_IN_FORCE_GTT',
expiresAt: '1664966445481288736',
type: 'TYPE_LIMIT',
@@ -46,7 +47,10 @@ describe('Txs infinite list', () => {
error={undefined}
/>
);
expect(screen.getByText('No items')).toBeInTheDocument();
expect(screen.getByTestId('emptylist')).toBeInTheDocument();
expect(
screen.getByText('This chain has 0 transactions')
).toBeInTheDocument();
});
it('error is displayed at item level', () => {
@@ -4,6 +4,8 @@ import InfiniteLoader from 'react-window-infinite-loader';
import { t, useScreenDimensions } from '@vegaprotocol/react-helpers';
import { TxsInfiniteListItem } from './txs-infinite-list-item';
import type { BlockExplorerTransactionResult } from '../../routes/types/block-explorer-response';
import EmptyList from '../empty-list/empty-list';
import { Loader } from '@vegaprotocol/ui-toolkit';
interface TxsInfiniteListProps {
hasMoreTxs: boolean;
@@ -29,7 +31,7 @@ const Item = ({ index, style, isLoading, error }: ItemProps) => {
if (error) {
content = t(`Cannot fetch transaction: ${error}`);
} else if (isLoading) {
content = t('Loading...');
content = <Loader />;
} else {
const {
hash,
@@ -68,7 +70,16 @@ export const TxsInfiniteList = ({
const isStacked = ['xs', 'sm', 'md', 'lg'].includes(screenSize);
if (!txs) {
return <div>No items</div>;
if (!areTxsLoading) {
return (
<EmptyList
heading={t('This chain has 0 transactions')}
label={t('Check back soon')}
/>
);
} else {
return <Loader />;
}
}
// If there are more items to be loaded then add an extra row to hold a loading indicator.
@@ -1,5 +1,4 @@
import { Routes } from '../../routes/route-names';
import { DATA_SOURCES } from '../../config';
import { RenderFetched } from '../render-fetched';
import { TruncatedLink } from '../truncate/truncated-link';
import { TxOrderType } from './tx-order-type';
@@ -8,6 +7,9 @@ import { t, useFetch } from '@vegaprotocol/react-helpers';
import type { BlockExplorerTransactions } from '../../routes/types/block-explorer-response';
import isNumber from 'lodash/isNumber';
import { ChainResponseCode } from './details/chain-response-code/chain-reponse.code';
import { getTxsDataUrl } from '../../hooks/use-txs-data';
import { Loader } from '@vegaprotocol/ui-toolkit';
import EmptyList from '../empty-list/empty-list';
interface TxsPerBlockProps {
blockHeight: string;
@@ -17,15 +19,11 @@ interface TxsPerBlockProps {
const truncateLength = 5;
export const TxsPerBlock = ({ blockHeight, txCount }: TxsPerBlockProps) => {
// TODO after https://github.com/vegaprotocol/vega/pull/6958/files is merged and deployed, use filter
// by block height instead
const filters = `filters[block.height]=${blockHeight}`;
const url = getTxsDataUrl({ limit: txCount.toString(), filters });
const {
state: { data, loading, error },
} = useFetch<BlockExplorerTransactions>(
`${
DATA_SOURCES.blockExplorerUrl
}/transactions?before=${blockHeight.toString()}.0&limit=${txCount}`
);
} = useFetch<BlockExplorerTransactions>(url);
return (
<RenderFetched error={error} loading={loading} className="text-body-large">
@@ -88,10 +86,13 @@ export const TxsPerBlock = ({ blockHeight, txCount }: TxsPerBlockProps) => {
</tbody>
</Table>
</div>
) : loading ? (
<Loader />
) : (
<div className="sr-only">
{t(`No transactions in block ${blockHeight}`)}
</div>
<EmptyList
heading={t('No transactions in this block')}
label={t('0 transactions')}
/>
)}
</RenderFetched>
);
@@ -1,77 +0,0 @@
import { t } from '@vegaprotocol/react-helpers';
import { useEffect } from 'react';
import { InfoBlock } from '../../components/info-block';
import { Panel } from '../../components/panel';
import { useExplorerStatsQuery } from './__generated__/Explorer-stats';
import type { ExplorerStatsFieldsFragment } from './__generated__/Explorer-stats';
interface StatsMap {
field: keyof ExplorerStatsFieldsFragment;
label: string;
info: string;
}
export const TXS_STATS_MAP: StatsMap[] = [
{
field: 'averageOrdersPerBlock',
label: t('Orders per block'),
info: t(
'Number of new orders processed in the last block. All orders derived from pegged orders and liquidity commitments count as a single order'
),
},
{
field: 'txPerBlock',
label: t('Transactions per block'),
info: t('Number of transactions processed in the last block'),
},
{
field: 'tradesPerSecond',
label: t('Trades per second'),
info: t('Number of trades processed in the last second'),
},
{
field: 'ordersPerSecond',
label: t('Order per second'),
info: t(
'Number of orders processed in the last second. All orders derived from pegged orders and liquidity commitments count as a single order'
),
},
];
interface TxsStatsInfoProps {
className?: string;
}
export const TxsStatsInfo = ({ className }: TxsStatsInfoProps) => {
const { data, startPolling, stopPolling } = useExplorerStatsQuery();
useEffect(() => {
startPolling(1000);
return () => stopPolling();
});
const gridStyles =
'grid grid-rows-2 gap-4 grid-cols-2 xl:gap-8 xl:grid-rows-1 xl:grid-cols-4';
return (
<Panel className={className}>
<section className={gridStyles}>
{TXS_STATS_MAP.map((field) => {
if (!data?.statistics) {
return null;
}
// Workaround for awkward typing
const title = data.statistics[field.field] || '';
return (
<InfoBlock
subtitle={field.label}
tooltipInfo={field.info}
title={title}
/>
);
})}
</section>
</Panel>
);
};
@@ -84,13 +84,13 @@ export const WithdrawalProgress = ({ id, txStatus }: TxsStatsInfoProps) => {
const classes = {
indicatorFailed:
'rounded-full transition duration-500 ease-in-out h-12 w-12 py-3 border-2 border-red-600 bg-red-600 text-center text-white font-bold leading-5',
'rounded-full transition duration-500 ease-in-out h-12 w-12 py-3 border-2 border-pink-600 bg-vega-pink-600 text-center text-white font-bold leading-5',
textFailed:
'absolute top-0 -ml-10 text-center mt-16 w-32 text-xs font-medium uppercase text-vega-red',
'absolute top-0 -ml-10 text-center mt-16 w-32 text-xs font-medium uppercase text-vega-pink',
indicatorComplete:
'rounded-full transition duration-500 ease-in-out h-12 w-12 py-3 border-2 border-vega-green-dark bg-vega-green-dark text-center text-white leading-5',
'rounded-full transition duration-500 ease-in-out h-12 w-12 py-3 border-2 border-vega-green bg-vega-green text-center text-white leading-5',
textComplete:
'absolute top-0 -ml-10 text-center mt-16 w-32 text-xs font-medium uppercase text-vega-green-dark',
'absolute top-0 -ml-10 text-center mt-16 w-32 text-xs font-medium uppercase text-vega-green',
indicatorIncomplete:
'rounded-full transition duration-500 ease-in-out h-12 w-12 py-3 border-2 border-gray-300 text-center leading-5',
textIncomplete:
@@ -140,7 +140,7 @@ export function WithdrawalProgressSeparator({
return (
<div
className={`flex-auto border-t-2 transition duration-500 ease-in-out ${
isComplete ? 'border-vega-green-dark' : 'border-gray-300'
isComplete ? 'border-vega-green' : 'border-gray-300'
}`}
></div>
);
@@ -0,0 +1,44 @@
import { MockedProvider } from '@apollo/client/testing';
import { render } from '@testing-library/react';
import { MemoryRouter } from 'react-router-dom';
import Assets from './index';
import type { MockedResponse } from '@apollo/client/testing';
import { ExplorerAssetDocument } from '../../components/links/asset-link/__generated__/Asset';
function renderComponent(mock: MockedResponse[]) {
return (
<MemoryRouter>
<MockedProvider mocks={mock}>
<Assets />
</MockedProvider>
</MemoryRouter>
);
}
describe('Assets index', () => {
it('Renders loader when loading', async () => {
const mock = {
request: {
query: ExplorerAssetDocument,
},
result: {
data: {},
},
};
const res = render(renderComponent([mock]));
expect(await res.findByTestId('loader')).toBeInTheDocument();
});
it('Renders EmptyList when loading completes and there are no results', async () => {
const mock = {
request: {
query: ExplorerAssetDocument,
},
result: {
data: {},
},
};
const res = render(renderComponent([mock]));
expect(await res.findByTestId('emptylist')).toBeInTheDocument();
});
});
+16 -7
View File
@@ -2,14 +2,15 @@ import { getNodes, t } from '@vegaprotocol/react-helpers';
import React from 'react';
import { RouteTitle } from '../../components/route-title';
import { SubHeading } from '../../components/sub-heading';
import { SyntaxHighlighter } from '@vegaprotocol/ui-toolkit';
import { Loader, SyntaxHighlighter } from '@vegaprotocol/ui-toolkit';
import { useExplorerAssetsQuery } from './__generated__/Assets';
import type { AssetsFieldsFragment } from './__generated__/Assets';
import { useScrollToLocation } from '../../hooks/scroll-to-location';
import { useDocumentTitle } from '../../hooks/use-document-title';
import EmptyList from '../../components/empty-list/empty-list';
const Assets = () => {
const { data } = useExplorerAssetsQuery();
const { data, loading } = useExplorerAssetsQuery();
useDocumentTitle(['Assets']);
useScrollToLocation();
@@ -17,17 +18,25 @@ const Assets = () => {
const assets = getNodes<AssetsFieldsFragment>(data?.assetsConnection);
if (!assets || assets.length === 0) {
return <section></section>;
if (!loading) {
return (
<section>
<RouteTitle data-testid="assets-header">{t('Assets')}</RouteTitle>
<EmptyList
heading={t('This chain has no assets')}
label={t('0 assets')}
/>
</section>
);
} else {
return <Loader />;
}
}
return (
<section>
<RouteTitle data-testid="assets-header">{t('Assets')}</RouteTitle>
{assets.map((a) => {
if (!a) {
return null;
}
return (
<React.Fragment key={a.id}>
<SubHeading data-testid="asset-header" id={a.id}>
@@ -18,6 +18,7 @@ import { RenderFetched } from '../../../components/render-fetched';
import { t, useFetch } from '@vegaprotocol/react-helpers';
import { NodeLink } from '../../../components/links';
import { useDocumentTitle } from '../../../hooks/use-document-title';
import EmptyList from '../../../components/empty-list/empty-list';
const Block = () => {
const { block } = useParams<{ block: string }>();
@@ -112,7 +113,12 @@ const Block = () => {
blockHeight={blockData.result.block.header.height}
txCount={blockData.result.block.data.txs.length}
/>
) : null}
) : (
<EmptyList
heading={t('This block is empty')}
label={t('0 transactions')}
/>
)}
</>
)}
</>
@@ -1,6 +1,6 @@
import { t, useFetch } from '@vegaprotocol/react-helpers';
import { RouteTitle } from '../../components/route-title';
import { SyntaxHighlighter } from '@vegaprotocol/ui-toolkit';
import { Loader, SyntaxHighlighter } from '@vegaprotocol/ui-toolkit';
import { DATA_SOURCES } from '../../config';
import type { TendermintGenesisResponse } from './tendermint-genesis-response';
import { useDocumentTitle } from '../../hooks/use-document-title';
@@ -9,11 +9,16 @@ const Genesis = () => {
useDocumentTitle(['Genesis']);
const {
state: { data: genesis },
state: { data: genesis, loading },
} = useFetch<TendermintGenesisResponse>(
`${DATA_SOURCES.tendermintUrl}/genesis`
);
if (!genesis?.result.genesis) return null;
if (!genesis?.result.genesis) {
if (loading) {
return <Loader />;
}
return null;
}
return (
<section>
<RouteTitle data-testid="genesis-header">{t('Genesis')}</RouteTitle>
@@ -2,19 +2,35 @@ import { t } from '@vegaprotocol/react-helpers';
import React from 'react';
import { RouteTitle } from '../../components/route-title';
import { SubHeading } from '../../components/sub-heading';
import { SyntaxHighlighter } from '@vegaprotocol/ui-toolkit';
import { Loader, SyntaxHighlighter } from '@vegaprotocol/ui-toolkit';
import { useExplorerProposalsQuery } from './__generated__/Proposals';
import { useDocumentTitle } from '../../hooks/use-document-title';
import EmptyList from '../../components/empty-list/empty-list';
const Governance = () => {
const { data } = useExplorerProposalsQuery({
const { data, loading } = useExplorerProposalsQuery({
errorPolicy: 'ignore',
});
useDocumentTitle();
if (!data || !data.proposalsConnection || !data.proposalsConnection.edges) {
return <section></section>;
if (!loading) {
return (
<section>
<RouteTitle data-testid="governance-header">
{t('Governance Proposals')}
</RouteTitle>
<EmptyList
heading={t('This chain has no proposals')}
label={t('0 proposals')}
/>
</section>
);
} else {
return <Loader />;
}
}
const proposals = data?.proposalsConnection?.edges.map((e) => {
@@ -6,7 +6,7 @@ const defaultOptions = {} as const;
export type ExplorerMarketsQueryVariables = Types.Exact<{ [key: string]: never; }>;
export type ExplorerMarketsQuery = { __typename?: 'Query', marketsConnection?: { __typename?: 'MarketConnection', edges: Array<{ __typename?: 'MarketEdge', node: { __typename?: 'Market', id: string, decimalPlaces: number, tradingMode: Types.MarketTradingMode, state: Types.MarketState, fees: { __typename?: 'Fees', factors: { __typename?: 'FeeFactors', makerFee: string, infrastructureFee: string, liquidityFee: string } }, tradableInstrument: { __typename?: 'TradableInstrument', instrument: { __typename?: 'Instrument', name: string, code: string, metadata: { __typename?: 'InstrumentMetadata', tags?: Array<string> | null }, product: { __typename?: 'Future', settlementAsset: { __typename?: 'Asset', id: string, name: string, decimals: number, globalRewardPoolAccount?: { __typename?: 'AccountBalance', balance: string } | null } } }, riskModel: { __typename?: 'LogNormalRiskModel', tau: number, riskAversionParameter: number, params: { __typename?: 'LogNormalModelParams', r: number, sigma: number, mu: number } } | { __typename?: 'SimpleRiskModel', params: { __typename?: 'SimpleRiskModelParams', factorLong: number, factorShort: number } }, marginCalculator?: { __typename?: 'MarginCalculator', scalingFactors: { __typename?: 'ScalingFactors', searchLevel: number, initialMargin: number, collateralRelease: number } } | null }, openingAuction: { __typename?: 'AuctionDuration', durationSecs: number, volume: number }, priceMonitoringSettings: { __typename?: 'PriceMonitoringSettings', parameters?: { __typename?: 'PriceMonitoringParameters', triggers?: Array<{ __typename?: 'PriceMonitoringTrigger', horizonSecs: number, probability: number, auctionExtensionSecs: number }> | null } | null }, liquidityMonitoringParameters: { __typename?: 'LiquidityMonitoringParameters', triggeringRatio: number, targetStakeParameters: { __typename?: 'TargetStakeParameters', timeWindow: number, scalingFactor: number } }, proposal?: { __typename?: 'Proposal', id?: string | null } | null, accountsConnection?: { __typename?: 'AccountsConnection', edges?: Array<{ __typename?: 'AccountEdge', node: { __typename?: 'AccountBalance', balance: string, type: Types.AccountType, asset: { __typename?: 'Asset', id: string, name: string } } } | null> | null } | null, data?: { __typename?: 'MarketData', markPrice: string, bestBidPrice: string, bestBidVolume: string, bestOfferPrice: string, bestOfferVolume: string, bestStaticBidPrice: string, bestStaticBidVolume: string, bestStaticOfferPrice: string, bestStaticOfferVolume: string, midPrice: string, staticMidPrice: string, timestamp: any, openInterest: string, auctionEnd?: string | null, auctionStart?: string | null, indicativePrice: string, indicativeVolume: string, trigger: Types.AuctionTrigger, extensionTrigger: Types.AuctionTrigger, targetStake?: string | null, suppliedStake?: string | null, marketValueProxy: string, priceMonitoringBounds?: Array<{ __typename?: 'PriceMonitoringBounds', minValidPrice: string, maxValidPrice: string, referencePrice: string, trigger: { __typename?: 'PriceMonitoringTrigger', auctionExtensionSecs: number, probability: number } }> | null, liquidityProviderFeeShare?: Array<{ __typename?: 'LiquidityProviderFeeShare', equityLikeShare: string, averageEntryValuation: string, party: { __typename?: 'Party', id: string } }> | null } | null } }> } | null };
export type ExplorerMarketsQuery = { __typename?: 'Query', marketsConnection?: { __typename?: 'MarketConnection', edges: Array<{ __typename?: 'MarketEdge', node: { __typename?: 'Market', id: string, decimalPlaces: number, tradingMode: Types.MarketTradingMode, state: Types.MarketState, fees: { __typename?: 'Fees', factors: { __typename?: 'FeeFactors', makerFee: string, infrastructureFee: string, liquidityFee: string } }, tradableInstrument: { __typename?: 'TradableInstrument', instrument: { __typename?: 'Instrument', name: string, code: string, metadata: { __typename?: 'InstrumentMetadata', tags?: Array<string> | null }, product: { __typename?: 'Future', settlementAsset: { __typename?: 'Asset', id: string, name: string, decimals: number, globalRewardPoolAccount?: { __typename?: 'AccountBalance', balance: string } | null } } }, riskModel: { __typename?: 'LogNormalRiskModel', tau: number, riskAversionParameter: number, params: { __typename?: 'LogNormalModelParams', r: number, sigma: number, mu: number } } | { __typename?: 'SimpleRiskModel', params: { __typename?: 'SimpleRiskModelParams', factorLong: number, factorShort: number } }, marginCalculator?: { __typename?: 'MarginCalculator', scalingFactors: { __typename?: 'ScalingFactors', searchLevel: number, initialMargin: number, collateralRelease: number } } | null }, openingAuction: { __typename?: 'AuctionDuration', durationSecs: number, volume: number }, priceMonitoringSettings: { __typename?: 'PriceMonitoringSettings', parameters?: { __typename?: 'PriceMonitoringParameters', triggers?: Array<{ __typename?: 'PriceMonitoringTrigger', horizonSecs: number, probability: number, auctionExtensionSecs: number }> | null } | null }, liquidityMonitoringParameters: { __typename?: 'LiquidityMonitoringParameters', triggeringRatio: string, targetStakeParameters: { __typename?: 'TargetStakeParameters', timeWindow: number, scalingFactor: number } }, proposal?: { __typename?: 'Proposal', id?: string | null } | null, accountsConnection?: { __typename?: 'AccountsConnection', edges?: Array<{ __typename?: 'AccountEdge', node: { __typename?: 'AccountBalance', balance: string, type: Types.AccountType, asset: { __typename?: 'Asset', id: string, name: string } } } | null> | null } | null, data?: { __typename?: 'MarketData', markPrice: string, bestBidPrice: string, bestBidVolume: string, bestOfferPrice: string, bestOfferVolume: string, bestStaticBidPrice: string, bestStaticBidVolume: string, bestStaticOfferPrice: string, bestStaticOfferVolume: string, midPrice: string, staticMidPrice: string, timestamp: any, openInterest: string, auctionEnd?: string | null, auctionStart?: string | null, indicativePrice: string, indicativeVolume: string, trigger: Types.AuctionTrigger, extensionTrigger: Types.AuctionTrigger, targetStake?: string | null, suppliedStake?: string | null, marketValueProxy: string, priceMonitoringBounds?: Array<{ __typename?: 'PriceMonitoringBounds', minValidPrice: string, maxValidPrice: string, referencePrice: string, trigger: { __typename?: 'PriceMonitoringTrigger', auctionExtensionSecs: number, probability: number } }> | null, liquidityProviderFeeShare?: Array<{ __typename?: 'LiquidityProviderFeeShare', equityLikeShare: string, averageEntryValuation: string, party: { __typename?: 'Party', id: string } }> | null } | null } }> } | null };
export const ExplorerMarketsDocument = gql`
@@ -0,0 +1,48 @@
import { MockedProvider } from '@apollo/client/testing';
import { render } from '@testing-library/react';
import { MemoryRouter } from 'react-router-dom';
import Markets from './index';
import type { MockedResponse } from '@apollo/client/testing';
import { ExplorerMarketsDocument } from './__generated__/Markets';
function renderComponent(mock: MockedResponse[]) {
return (
<MemoryRouter>
<MockedProvider mocks={mock}>
<Markets />
</MockedProvider>
</MemoryRouter>
);
}
describe('Markets index', () => {
it('Renders loader when loading', async () => {
const mock = {
request: {
query: ExplorerMarketsDocument,
},
result: {
data: {
marketsConnection: [],
},
},
};
const res = render(renderComponent([mock]));
expect(await res.findByTestId('loader')).toBeInTheDocument();
});
it('Renders EmptyList when loading completes and there are no results', async () => {
const mock = {
request: {
query: ExplorerMarketsDocument,
},
result: {
data: {
marketsConnection: [],
},
},
};
const res = render(renderComponent([mock]));
expect(await res.findByTestId('emptylist')).toBeInTheDocument();
});
});
+20 -12
View File
@@ -1,14 +1,15 @@
import React from 'react';
import { SyntaxHighlighter } from '@vegaprotocol/ui-toolkit';
import { Loader, SyntaxHighlighter } from '@vegaprotocol/ui-toolkit';
import { RouteTitle } from '../../components/route-title';
import { SubHeading } from '../../components/sub-heading';
import { t } from '@vegaprotocol/react-helpers';
import { useExplorerMarketsQuery } from './__generated__/Markets';
import { useScrollToLocation } from '../../hooks/scroll-to-location';
import { useDocumentTitle } from '../../hooks/use-document-title';
import EmptyList from '../../components/empty-list/empty-list';
const Markets = () => {
const { data } = useExplorerMarketsQuery();
const { data, loading } = useExplorerMarketsQuery();
useScrollToLocation();
useDocumentTitle(['Markets']);
@@ -19,16 +20,23 @@ const Markets = () => {
<section key="markets">
<RouteTitle data-testid="markets-heading">{t('Markets')}</RouteTitle>
{m
? m.map((e) => (
<React.Fragment key={e.node.id}>
<SubHeading data-testid="markets-header" id={e.node.id}>
{e.node.tradableInstrument.instrument.name}
</SubHeading>
<SyntaxHighlighter data={e.node} />
</React.Fragment>
))
: null}
{m ? (
m.map((e) => (
<React.Fragment key={e.node.id}>
<SubHeading data-testid="markets-header" id={e.node.id}>
{e.node.tradableInstrument.instrument.name}
</SubHeading>
<SyntaxHighlighter data={e.node} />
</React.Fragment>
))
) : loading ? (
<Loader />
) : (
<EmptyList
heading={t('This chain has no markets')}
label={t('0 markets')}
/>
)}
</section>
);
};
@@ -0,0 +1,90 @@
fragment ExplorerOracleDataSource on OracleSpec {
dataSourceSpec {
spec {
id
createdAt
updatedAt
status
data {
sourceType {
... on DataSourceDefinitionInternal {
sourceType {
... on DataSourceSpecConfigurationTime {
conditions {
value
operator
}
}
}
}
... on DataSourceDefinitionExternal {
sourceType {
... on DataSourceSpecConfiguration {
signers {
signer {
... on ETHAddress {
address
}
... on PubKey {
key
}
}
}
filters {
key {
name
type
}
conditions {
value
operator
}
}
}
}
}
}
}
}
}
}
fragment ExplorerOracleDataConnection on OracleSpec {
dataConnection {
edges {
node {
externalData {
data {
signers {
signer {
... on ETHAddress {
address
}
... on PubKey {
key
}
}
}
data {
name
value
}
matchedSpecIds
broadcastAt
}
}
}
}
}
}
query ExplorerOracleSpecs {
oracleSpecsConnection {
edges {
node {
...ExplorerOracleDataSource
...ExplorerOracleDataConnection
}
}
}
}
@@ -0,0 +1,27 @@
fragment ExplorerOracleForMarketsMarket on Market {
id
tradableInstrument {
instrument {
product {
... on Future {
dataSourceSpecForSettlementData {
id
}
dataSourceSpecForTradingTermination {
id
}
}
}
}
}
}
query ExplorerOracleFormMarkets {
marketsConnection {
edges {
node {
...ExplorerOracleForMarketsMarket
}
}
}
}
@@ -0,0 +1,136 @@
import * as Types from '@vegaprotocol/types';
import { gql } from '@apollo/client';
import * as Apollo from '@apollo/client';
const defaultOptions = {} as const;
export type ExplorerOracleDataSourceFragment = { __typename?: 'OracleSpec', dataSourceSpec: { __typename?: 'ExternalDataSourceSpec', spec: { __typename?: 'DataSourceSpec', id: string, createdAt: any, updatedAt?: any | null, status: Types.DataSourceSpecStatus, data: { __typename?: 'DataSourceDefinition', sourceType: { __typename?: 'DataSourceDefinitionExternal', sourceType: { __typename?: 'DataSourceSpecConfiguration', signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null, filters?: Array<{ __typename?: 'Filter', key: { __typename?: 'PropertyKey', name?: string | null, type: Types.PropertyKeyType }, conditions?: Array<{ __typename?: 'Condition', value?: string | null, operator: Types.ConditionOperator }> | null }> | null } } | { __typename?: 'DataSourceDefinitionInternal', sourceType: { __typename?: 'DataSourceSpecConfigurationTime', conditions: Array<{ __typename?: 'Condition', value?: string | null, operator: Types.ConditionOperator } | null> } } } } } };
export type ExplorerOracleDataConnectionFragment = { __typename?: 'OracleSpec', dataConnection: { __typename?: 'OracleDataConnection', edges?: Array<{ __typename?: 'OracleDataEdge', node: { __typename?: 'OracleData', externalData: { __typename?: 'ExternalData', data: { __typename?: 'Data', matchedSpecIds?: Array<string> | null, broadcastAt: any, signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null, data?: Array<{ __typename?: 'Property', name: string, value: string }> | null } } } } | null> | null } };
export type ExplorerOracleSpecsQueryVariables = Types.Exact<{ [key: string]: never; }>;
export type ExplorerOracleSpecsQuery = { __typename?: 'Query', oracleSpecsConnection?: { __typename?: 'OracleSpecsConnection', edges?: Array<{ __typename?: 'OracleSpecEdge', node: { __typename?: 'OracleSpec', dataSourceSpec: { __typename?: 'ExternalDataSourceSpec', spec: { __typename?: 'DataSourceSpec', id: string, createdAt: any, updatedAt?: any | null, status: Types.DataSourceSpecStatus, data: { __typename?: 'DataSourceDefinition', sourceType: { __typename?: 'DataSourceDefinitionExternal', sourceType: { __typename?: 'DataSourceSpecConfiguration', signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null, filters?: Array<{ __typename?: 'Filter', key: { __typename?: 'PropertyKey', name?: string | null, type: Types.PropertyKeyType }, conditions?: Array<{ __typename?: 'Condition', value?: string | null, operator: Types.ConditionOperator }> | null }> | null } } | { __typename?: 'DataSourceDefinitionInternal', sourceType: { __typename?: 'DataSourceSpecConfigurationTime', conditions: Array<{ __typename?: 'Condition', value?: string | null, operator: Types.ConditionOperator } | null> } } } } }, dataConnection: { __typename?: 'OracleDataConnection', edges?: Array<{ __typename?: 'OracleDataEdge', node: { __typename?: 'OracleData', externalData: { __typename?: 'ExternalData', data: { __typename?: 'Data', matchedSpecIds?: Array<string> | null, broadcastAt: any, signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null, data?: Array<{ __typename?: 'Property', name: string, value: string }> | null } } } } | null> | null } } } | null> | null } | null };
export const ExplorerOracleDataSourceFragmentDoc = gql`
fragment ExplorerOracleDataSource on OracleSpec {
dataSourceSpec {
spec {
id
createdAt
updatedAt
status
data {
sourceType {
... on DataSourceDefinitionInternal {
sourceType {
... on DataSourceSpecConfigurationTime {
conditions {
value
operator
}
}
}
}
... on DataSourceDefinitionExternal {
sourceType {
... on DataSourceSpecConfiguration {
signers {
signer {
... on ETHAddress {
address
}
... on PubKey {
key
}
}
}
filters {
key {
name
type
}
conditions {
value
operator
}
}
}
}
}
}
}
}
}
}
`;
export const ExplorerOracleDataConnectionFragmentDoc = gql`
fragment ExplorerOracleDataConnection on OracleSpec {
dataConnection {
edges {
node {
externalData {
data {
signers {
signer {
... on ETHAddress {
address
}
... on PubKey {
key
}
}
}
data {
name
value
}
matchedSpecIds
broadcastAt
}
}
}
}
}
}
`;
export const ExplorerOracleSpecsDocument = gql`
query ExplorerOracleSpecs {
oracleSpecsConnection {
edges {
node {
...ExplorerOracleDataSource
...ExplorerOracleDataConnection
}
}
}
}
${ExplorerOracleDataSourceFragmentDoc}
${ExplorerOracleDataConnectionFragmentDoc}`;
/**
* __useExplorerOracleSpecsQuery__
*
* To run a query within a React component, call `useExplorerOracleSpecsQuery` and pass it any options that fit your needs.
* When your component renders, `useExplorerOracleSpecsQuery` returns an object from Apollo Client that contains loading, error, and data properties
* you can use to render your UI.
*
* @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
*
* @example
* const { data, loading, error } = useExplorerOracleSpecsQuery({
* variables: {
* },
* });
*/
export function useExplorerOracleSpecsQuery(baseOptions?: Apollo.QueryHookOptions<ExplorerOracleSpecsQuery, ExplorerOracleSpecsQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useQuery<ExplorerOracleSpecsQuery, ExplorerOracleSpecsQueryVariables>(ExplorerOracleSpecsDocument, options);
}
export function useExplorerOracleSpecsLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<ExplorerOracleSpecsQuery, ExplorerOracleSpecsQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useLazyQuery<ExplorerOracleSpecsQuery, ExplorerOracleSpecsQueryVariables>(ExplorerOracleSpecsDocument, options);
}
export type ExplorerOracleSpecsQueryHookResult = ReturnType<typeof useExplorerOracleSpecsQuery>;
export type ExplorerOracleSpecsLazyQueryHookResult = ReturnType<typeof useExplorerOracleSpecsLazyQuery>;
export type ExplorerOracleSpecsQueryResult = Apollo.QueryResult<ExplorerOracleSpecsQuery, ExplorerOracleSpecsQueryVariables>;
@@ -0,0 +1,69 @@
import * as Types from '@vegaprotocol/types';
import { gql } from '@apollo/client';
import * as Apollo from '@apollo/client';
const defaultOptions = {} as const;
export type ExplorerOracleForMarketsMarketFragment = { __typename?: 'Market', id: string, tradableInstrument: { __typename?: 'TradableInstrument', instrument: { __typename?: 'Instrument', product: { __typename?: 'Future', dataSourceSpecForSettlementData: { __typename?: 'DataSourceSpec', id: string }, dataSourceSpecForTradingTermination: { __typename?: 'DataSourceSpec', id: string } } } } };
export type ExplorerOracleFormMarketsQueryVariables = Types.Exact<{ [key: string]: never; }>;
export type ExplorerOracleFormMarketsQuery = { __typename?: 'Query', marketsConnection?: { __typename?: 'MarketConnection', edges: Array<{ __typename?: 'MarketEdge', node: { __typename?: 'Market', id: string, tradableInstrument: { __typename?: 'TradableInstrument', instrument: { __typename?: 'Instrument', product: { __typename?: 'Future', dataSourceSpecForSettlementData: { __typename?: 'DataSourceSpec', id: string }, dataSourceSpecForTradingTermination: { __typename?: 'DataSourceSpec', id: string } } } } } }> } | null };
export const ExplorerOracleForMarketsMarketFragmentDoc = gql`
fragment ExplorerOracleForMarketsMarket on Market {
id
tradableInstrument {
instrument {
product {
... on Future {
dataSourceSpecForSettlementData {
id
}
dataSourceSpecForTradingTermination {
id
}
}
}
}
}
}
`;
export const ExplorerOracleFormMarketsDocument = gql`
query ExplorerOracleFormMarkets {
marketsConnection {
edges {
node {
...ExplorerOracleForMarketsMarket
}
}
}
}
${ExplorerOracleForMarketsMarketFragmentDoc}`;
/**
* __useExplorerOracleFormMarketsQuery__
*
* To run a query within a React component, call `useExplorerOracleFormMarketsQuery` and pass it any options that fit your needs.
* When your component renders, `useExplorerOracleFormMarketsQuery` returns an object from Apollo Client that contains loading, error, and data properties
* you can use to render your UI.
*
* @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
*
* @example
* const { data, loading, error } = useExplorerOracleFormMarketsQuery({
* variables: {
* },
* });
*/
export function useExplorerOracleFormMarketsQuery(baseOptions?: Apollo.QueryHookOptions<ExplorerOracleFormMarketsQuery, ExplorerOracleFormMarketsQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useQuery<ExplorerOracleFormMarketsQuery, ExplorerOracleFormMarketsQueryVariables>(ExplorerOracleFormMarketsDocument, options);
}
export function useExplorerOracleFormMarketsLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<ExplorerOracleFormMarketsQuery, ExplorerOracleFormMarketsQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useLazyQuery<ExplorerOracleFormMarketsQuery, ExplorerOracleFormMarketsQueryVariables>(ExplorerOracleFormMarketsDocument, options);
}
export type ExplorerOracleFormMarketsQueryHookResult = ReturnType<typeof useExplorerOracleFormMarketsQuery>;
export type ExplorerOracleFormMarketsLazyQueryHookResult = ReturnType<typeof useExplorerOracleFormMarketsLazyQuery>;
export type ExplorerOracleFormMarketsQueryResult = Apollo.QueryResult<ExplorerOracleFormMarketsQuery, ExplorerOracleFormMarketsQueryVariables>;
@@ -0,0 +1,67 @@
import { render } from '@testing-library/react';
import { OracleData } from './oracle-data';
import type { ExplorerOracleDataConnectionFragment } from '../__generated__/Oracles';
function renderComponent(data: ExplorerOracleDataConnectionFragment) {
return <OracleData data={data} />;
}
describe('Oracle Data view', () => {
it('Renders nothing when data is null', () => {
const res = render(
renderComponent(null as unknown as ExplorerOracleDataConnectionFragment)
);
expect(res.container).toBeEmptyDOMElement();
});
it('Renders nothing when dataConnection is empty', () => {
const res = render(
renderComponent({} as ExplorerOracleDataConnectionFragment)
);
expect(res.container).toBeEmptyDOMElement();
});
it('Renders nothing when dataConnection has no edges', () => {
const res = render(
renderComponent({
dataConnection: {
edges: null,
},
} as ExplorerOracleDataConnectionFragment)
);
expect(res.container).toBeEmptyDOMElement();
});
it('Renders nothing when dataConnection edges is empty', () => {
const res = render(
renderComponent({
dataConnection: {
edges: [],
},
} as ExplorerOracleDataConnectionFragment)
);
expect(res.container).toBeEmptyDOMElement();
});
// This stops short of asserting how the data is presented
// because the current view is pretty rudimentary
it('Renders details component when there is data', () => {
const res = render(
renderComponent({
dataConnection: {
edges: [
{
node: {
externalData: {
data: {
broadcastAt: '2022-01-01',
},
},
},
},
],
},
} as ExplorerOracleDataConnectionFragment)
);
expect(res.getByText('Broadcast data')).toBeInTheDocument();
});
});
@@ -0,0 +1,44 @@
import { t } from '@vegaprotocol/react-helpers';
import { SyntaxHighlighter } from '@vegaprotocol/ui-toolkit';
import filter from 'recursive-key-filter';
import type { ExplorerOracleDataConnectionFragment } from '../__generated__/Oracles';
interface OracleDataTypeProps {
data: ExplorerOracleDataConnectionFragment;
}
/**
* If there is data that has matched this oracle, this view will
* render the data inside a collapsed element so that it can be viewed.
* Currently the data is just rendered as a JSON view, because
* that Does The Job, rather than because it's good.
*/
export function OracleData({ data }: OracleDataTypeProps) {
if (
!data ||
!data.dataConnection ||
!data.dataConnection.edges?.length ||
data.dataConnection.edges.length > 1
) {
return null;
}
return (
<details data-testid="oracle-data">
<summary>{t('Broadcast data')}</summary>
<ul>
{data.dataConnection.edges.map((d) => {
if (!d) {
return null;
}
return (
<li key={d.node.externalData.data.broadcastAt}>
<SyntaxHighlighter data={filter(d, ['__typename'])} />
</li>
);
})}
</ul>
</details>
);
}
@@ -0,0 +1,32 @@
import { render } from '@testing-library/react';
import { OracleDetailsType } from './oracle-details-type';
import type { SourceTypeName } from './oracle-details-type';
function renderComponent(type: SourceTypeName) {
return <OracleDetailsType type={type} />;
}
function renderWrappedComponent(type: SourceTypeName) {
return (
<table>
<tbody>{renderComponent(type)}</tbody>
</table>
);
}
describe('Oracle type view', () => {
it('Renders nothing when type is null', () => {
const res = render(renderComponent(null as unknown as SourceTypeName));
expect(res.container).toBeEmptyDOMElement();
});
it('Renders Internal time for internal sources', () => {
const res = render(renderWrappedComponent('DataSourceDefinitionInternal'));
expect(res.getByText('Internal time')).toBeInTheDocument();
});
it('Renders External data otherwise', () => {
const res = render(renderWrappedComponent('DataSourceDefinitionExternal'));
expect(res.getByText('External data')).toBeInTheDocument();
});
});

Some files were not shown because too many files have changed in this diff Show More