Compare commits

...
Author SHA1 Message Date
Madalina Raicu 9c93793a93 fix: format error labels 2022-08-15 22:40:55 +01:00
Madalina Raicu e265a14e69 fix: order feedback format 2022-08-15 10:52:54 +01:00
Madalina Raicu 3189fc1b37 feat: add toast component + orders updates 2022-08-15 10:52:22 +01:00
Madalina Raicu 9715b681ea Merge branch 'master' of github.com:vegaprotocol/frontend-monorepo 2022-08-11 13:39:03 +01:00
botond 3e223c24a7 Fix/954: Token & Explorer decimals (#1006)
* fix: token governance percentage displays

* fix: add percentage displays to selected network params

* fix: format

* fix: majority rule and proposal required percentage defaults

* fix: format
2022-08-11 13:14:46 +01:00
mattrussell36 51d6dd00a9 chore: update tranches
Signed-off-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2022-08-11 12:05:15 +00:00
m.ray 0523b56e39 feat: market list mega dropdown (rich popover) (#889)
* feat: use MarketList query only

* fix: remove Market.ts from index

* feat: 30 refactor dialog, market list, change query

* feat: #30 add indicativeVolume, total fees, tooltip, large dialog, tooltip accepts html description

* fix: #30 total fees display in tooltip

* fix: #30 toggle title on dialog open

* fix: #30 fix order, price, high, low utils

* fix: #30 fix test for market utils

* feat: #30 add popover with markets to select

* feat: #30 storybook popover

* feat: #30 remove border on trigger and add some other classes

* fix:  #30 fix format check with format:write

* feat: #30 add tooltip on taker fee

* feat: #30 add tooltip on taker fee

* fix: #30 format on select market list

* fix: #30 remove unknown cast in test mock data

* fix: #30 show markets where you have open positions

* fix: #30 double check if open positions

* fix: #30 dialog has only small/large sizes

* feat: #30 add border on trigger and change padding and no wrap

* fix: #30 if fees or factors are not found

* fix: #30 remove markets.cy tests as markets page is now gone

* fix #30 remove view full market list test

* fix: #30 add rotating arrow on market title

* fix: #30 add ease-in-out on popover

* fix: #30 add ease-in-out on popover

* fix: #30 align select a market table

* fix: #30 select a market title

* fix: #30 select a market title

* fix: #30 fix any validateDOMnesting issues

* fix: #30 show loading market data

* fix: #30 add list of header columns

* fix: #30 add list of header columns

* fix: #30 small refactoring after review

* fix: #30 update bold undreline class names

* fix: #30 add large-mobile size

* feat: #30 refactor select markets tables to render array of columns

* fix: #30 remove size from select market dialog

* fix: #30 add extra file for columns

* fix: #30 update formtting

* fix: #30 make sure popup closes on same market navigation

* fix: rename market-utils, add calcCandle methods, store market id on select

* fix: useMemo ondata and marketPositionData + orderbook stories fix

* feat: #30 add open volume positions

* fix: add market summary back

* fix: update formatting

* fix: use currentcolor on arrow

* fix: create all markets page

* fix: add overflow-y auto

* fix: enlarge select market to get started dialog

* fix: revert markets container

* fix: use query to fix flickering on position markets

* fix: edit unordered list in tooltips

* fix: fix tooltip table

* fix: fix home.cy.ts

* chore: skip /markets tests
2022-08-11 12:56:35 +01:00
Sam Keen 1be1a78a69 Feat/537: remove old contract for collateral bridge (#998)
* Feat/537: Removed old contract and branching logic for collateral bridge

* Feat/537: Renamed all 'new' name instances in functions, files and types. Regenerated types.

* Feat/537: Added 'creation' field to withdraw-dialog.spec.tsx test
2022-08-11 11:33:45 +01:00
Joe Tsang 1c4231f6fc test: remove eth wallet test (#989) 2022-08-11 11:27:45 +01:00
Joe Tsang 22fc0e7ac1 Test/625 market info (#999)
* test: assert edit button

* test: assert edit order btn

* test: update market info mock

* test: tests for market info
2022-08-11 11:26:26 +01:00
botondandPeteWilliams 8f0898385f Chore/953: Rename amount to size (#983)
* chore: change amount label to size

* chore: colour-coding size cell

* feat: add colors to the order table size column

* fix: format

* fix: broken fills tests

* fix: lint

* fix: simple market test imports

Co-authored-by: PeteWilliams <me@petewilliams.info>
2022-08-11 09:10:20 +01:00
AndyWhiteVega db3aa7c17b Test/884 token e2e adjust staking flow tests to new implementation of validators table (#990)
* test: flake fixes since ui revamp

* test: lint

* test: fix broken locator since locator revamp

* test: unrelated flake preventing PR - fix
2022-08-11 09:34:29 +02:00
mattrussell36 416805c2a2 chore: update tranches
Signed-off-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2022-08-11 06:04:31 +00:00
mattrussell36 f28ef02c54 chore: update tranches
Signed-off-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2022-08-11 00:09:33 +00:00
mattrussell36 41f869c6c6 chore: update tranches
Signed-off-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2022-08-10 18:04:10 +00:00
Elmar b75ed62072 Feat/348 select size of trade (#943)
* feat(ui-toolkit): add slider to ui-toolkit

* feat(console-lite): add deal ticket size to use new slider

* feat(console-lite): add use-maximum-position-size hook

* feat(console-lite): add e2e tests

* feat(console-lite): add position size value after selection

* fix(console-lite): remove lingering console log

* fix(console-lite): fix linting errors

* fix(console-lite): fix cypress config with wrong app specified

* fix(console-lite): fix react hooks bug after upgrade to react 18

* feat(console-lite): add proportional size selector and size amount input

* fix(console-lite): add missing env variables

* feat(console-lite): add missing tests and fix broken one after input button for size

* fix(console-lite): fix async error for max trade size
2022-08-10 16:28:03 +01:00
Madalina Raicu c7c42045a5 Merge branch 'master' of github.com:vegaprotocol/frontend-monorepo 2022-08-10 16:06:50 +01:00
m.ray 1a1ab4db65 feat: reselct field so that GTC is default on limit (#995)
* feat: reselct field so that GTC is default on limit

* feat: set value when switching order type

* fix: deal ticket test

* fix: revert TIF selector file
2022-08-10 13:45:36 +01:00
Sam Keen 24a20f48e1 Feat/589: Reimplement theme switcher in storybook (#988)
* Feat/589: Reimplement theme switcher in storybook

* Feat/589: Setting a higher level 'dark' class when dark theme selected

* Feat/589: Removed class test
2022-08-10 13:24:51 +01:00
mattrussell36 c1019c0ffb chore: update tranches
Signed-off-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2022-08-10 12:05:08 +00:00
Sam Keen 079a4b7fa7 Feat/980: New Asset proposals should require validationTimestamp (#981) 2022-08-10 12:28:40 +01:00
Madalina Raicu fbecf0bffd Merge 2022-08-10 12:22:40 +01:00
Bartłomiej Głownia f732e66546 Feat/986: extract routes from routes, lazy load all child routes 2022-08-10 09:50:15 +01:00
mattrussell36 541396eb76 chore: update tranches
Signed-off-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2022-08-10 06:05:15 +00:00
mattrussell36 3f06f56724 chore: update tranches
Signed-off-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2022-08-10 00:09:08 +00:00
mattrussell36 8a175bf2d7 chore: update tranches
Signed-off-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2022-08-09 18:04:36 +00:00
Dexter EdwardsandEdd eed6a83572 chore: show total supply on governance page (#967)
* chore: show total supply on governance page

* Update apps/token/src/i18n/translations/dev.json

Co-authored-by: Edd <edd@vega.xyz>

* Update apps/token/src/i18n/translations/dev.json

Co-authored-by: Edd <edd@vega.xyz>
2022-08-09 16:54:41 +01:00
botond 25c3ac8b4a Fix/945: Orderbook memory leak (#978)
* fix: cancel throttle on unmount

* fix: format

* fix: lint
2022-08-09 14:31:56 +01:00
m.ray 9a2ac6a8ac chore: fix failing test on proposal-list.spec (#985)
* chore: add moduleNameMapper for d3 in token jest.config.js

* remove extra line of transform

* fix: use @testing-library/react not react-hooks
2022-08-09 14:25:47 +01:00
Madalina Raicu 0413ef13f3 Merge branch 'master' of github.com:vegaprotocol/frontend-monorepo 2022-08-09 14:09:28 +01:00
mattrussell36 dee486e3aa chore: update tranches
Signed-off-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2022-08-09 12:05:17 +00:00
Madalina Raicu d28d238574 Merge branch 'master' of github.com:vegaprotocol/frontend-monorepo 2022-08-09 12:22:22 +01:00
Joe Tsang 67315d611b Test/fix nightly run (#979)
* fix: change test command for night run

* chore: try command in capsule run

* chore: correct command

* chore: change checkout to master

* chore: revert changes

* chore: fix typo
2022-08-09 12:18:13 +01:00
macqbatandmaciek 8a03ca5cfe feat: [console-lite] - add 'proto' portfolio view (#962)
* feat: [console-lite] - add proto portfolio view

* feat: [console-lite] - add proto portfolio view - mount diff in types

* feat: [console-lite] - add proto portfolio view - add unit test

* feat: [console-lite] - add proto portfolio view - add int test

Co-authored-by: maciek <maciek@vegaprotocol.io>
2022-08-09 12:10:55 +02:00
Sam Keen 4cd980f6bf Feat/971: Add link to the make proposal form (#972)
* Feat/971: Add link to the make proposal form

* Feat/971: Basic render test for the page title and new proposal link
2022-08-09 11:07:12 +01:00
71ede25339 chore: upgrade to React 18 (#952)
* chore: upgrade react only

* chore: import renderHook from testing-library/react

* chore: add @babel/runtime to fix tests

* fix: fix some of the tests

* fix: fix some of the tests

* fix: fix tests failing on not being wrapped in act

* fix: fix tests in use-environment

* fix:  fix @types/react issue

* fix: fix formatting

* fix: remove unsued method

* fix: callout not accepting react node and root element null check

* fix: main.tsx stats null check

* fix: implicit any type fixes

* Update libs/environment/src/hooks/use-nodes.spec.tsx

* fix:  import act from testing-lib

* fix:  add strict mode back

* fix:  fix formatting issues

* fix: add babel deps for storybook

* Update tsconfig.json (#970)

* Update tsconfig.json

* feat: [console-lite] - add missing types in few places

Co-authored-by: maciek <maciek@vegaprotocol.io>

* chore(#952): remove any from useDataProvider hook

Co-authored-by: macqbat <kubat.maciek@gmail.com>
Co-authored-by: maciek <maciek@vegaprotocol.io>
Co-authored-by: Bartłomiej Głownia <bglownia@gmail.com>
2022-08-09 10:43:11 +01:00
mattrussell36 80f6725a9a chore: update tranches
Signed-off-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2022-08-09 06:05:05 +00:00
mattrussell36 1d726ba9e3 chore: update tranches
Signed-off-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2022-08-09 00:09:37 +00:00
m.ray ba4954b52a feat: multi open accordion (#963)
* feat: 744 multi open accordion but not using pixelated icon anymore

* fix: fix size of the icon and color

* fix: remove styled stitches

* fix: remove styled stitches
2022-08-08 19:09:46 +01:00
mattrussell36 f614ec9211 chore: update tranches
Signed-off-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2022-08-08 18:05:03 +00:00
Joe Tsang c375f74906 Test/fix ci test failures (#973)
* fix: failing node switcher test

* fix: failing staking tests in CI
2022-08-08 15:53:18 +01:00
mattrussell36 df8fed2af2 chore: update tranches
Signed-off-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2022-08-08 12:05:40 +00:00
botond 1c9cd3aa2a Feat/815 auction conditions (#955)
* feat: add tooltip for market trading modes

* fix: format

* fix: remove log

* fix: typo
2022-08-08 11:19:57 +01:00
Dexter Edwards 2e644de413 fix: provider sometimes not having signer (#964) 2022-08-08 10:16:10 +00:00
Dexter Edwards 838c6c45c7 Release/token (#948)
* Revert "feat: show correct reward type (#930)"

This reverts commit 8644eac2be.

* fix: incorrect decimals in pending stake

* fix: use old bridge
2022-08-08 09:42:56 +01:00
mattrussell36 b733de5996 chore: update tranches
Signed-off-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2022-08-08 06:05:01 +00:00
mattrussell36 20cb4677ee chore: update tranches
Signed-off-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2022-08-08 00:09:05 +00:00
mattrussell36 4cc3bbae6f chore: update tranches
Signed-off-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2022-08-07 18:05:12 +00:00
mattrussell36 6ba8926ff7 chore: update tranches
Signed-off-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2022-08-07 12:05:08 +00:00
mattrussell36 a8565f8d2f chore: update tranches
Signed-off-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2022-08-07 06:04:55 +00:00
mattrussell36 1843b59304 chore: update tranches
Signed-off-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2022-08-07 00:10:07 +00:00
madalinaraicu 937f4beeb0 Merge branch 'master' of github.com:vegaprotocol/frontend-monorepo 2022-08-06 19:05:16 +01:00
mattrussell36 460e3c03c5 chore: update tranches
Signed-off-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2022-08-06 18:04:18 +00:00
mattrussell36 3c09dce870 chore: update tranches
Signed-off-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2022-08-06 12:05:27 +00:00
mattrussell36 3347e168a4 chore: update tranches
Signed-off-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2022-08-06 06:05:38 +00:00
mattrussell36 057e0ad947 chore: update tranches
Signed-off-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2022-08-05 18:04:20 +00:00
Joe TsangandEdd 181819fb24 Test/868 staking ACs (#939)
* test: staking validation tests

* chore: standardise codes

* chore: standardise codes

* test: additional ACs added and view tests mostly done

* chore: update codes

* test: staking tests passing

* chore: remove comments and formatting

* test: fix staking view tests

* fix: empty string assertion

* fix: lint

* chore: remove unnecessary assertions

Co-authored-by: Edd <edd@vega.xyz>
2022-08-05 17:59:44 +01:00
PeteWilliams f46d7e4617 feat: add resizable panels to portfolio page (#888)
* chore: fix padding issues

* chore: add resizable panels to portfolio page

* chore: padding tweak

* chore: adding resizable panels component
2022-08-05 14:24:46 +01:00
mattrussell36 0a88087f47 chore: update tranches
Signed-off-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2022-08-05 12:05:34 +00:00
Sam KeenandMatthew Russell c40d71e1ed feat (#804): simple proposal form (#942)
* Feat/804: Routing and basic page setup

* Feat/804: Type definition for proposals - awaiting asset docs/changes

* Feat/804: New proposals lib

* Feat/804: Propose form and page

* Feat/804: Removing dud copied unit tests for now

* Feat/804: Added types for new asset proposal

* feat: handle new error types returned from wallet for proposals

* chore: rename lib to governance

* feat: move usage of hook into form component

* feat: some adjustments and test coverage

* chore: tidy up, remove tailwind config

Co-authored-by: Matthew Russell <mattrussell36@gmail.com>
2022-08-05 12:01:46 +01:00
mattrussell36 4c7ff15f23 chore: update tranches
Signed-off-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2022-08-05 06:05:11 +00:00
mattrussell36 4c900ffb31 chore: update tranches
Signed-off-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2022-08-05 00:10:28 +00:00
mattrussell36 a2f219f2dd chore: update tranches
Signed-off-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2022-08-04 18:04:58 +00:00
botond eaad3fa26c feat(#521): pnl tooltip (#947)
* feat: add header tooltip for pnl column and fix tests

* fix: format of course
2022-08-04 17:27:31 +01:00
mattrussell36 4ee12dfa68 chore: update tranches
Signed-off-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2022-08-04 12:05:36 +00:00
macqbatandmaciek f0e37e4a2f feat: [console-lite] - fixes in order close out calcs (#934)
* feat: [console-lite] - fixes in order close out calcs

* feat: [console-lite] - fixes in order close out calcs - adjust int test

* feat: [console-lite] - fixes in order close out calcs - adjust int test

* feat: [console-lite] - fixes in order close out calcs - adjust int test

* feat: [console-lite] - fixes in order close out calcs - add dash instead 0

* feat: [console-lite] - fixes in order close out calcs - add dash instead 0

Co-authored-by: maciek <maciek@vegaprotocol.io>
2022-08-04 13:21:28 +02:00
botond 45d05b38f9 fix: disallow updating custom node before validation (#931)
* fix: disallow updating custom node before validation

* fix: format

* fix: lint

* feat: add node switcher component tests

* fix: environment filter
2022-08-04 10:14:49 +01:00
mattrussell36 ab0b762cd2 chore: update tranches
Signed-off-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2022-08-04 06:05:22 +00:00
mattrussell36 33820440da chore: update tranches
Signed-off-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2022-08-04 00:09:42 +00:00
mattrussell36 0f0f0a35cb chore: update tranches
Signed-off-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2022-08-03 18:04:54 +00:00
Artur 53c72b0eae fix: walletconnect does not work (#935)
* fix: walletconnect does not work

* fix: walletconnect does not work

added buffer

* chore: moved global from env-config.js
2022-08-03 14:56:40 +01:00
Matthew Russell b57b12a798 fix: formatting on governance.proposal.market network params (#937) 2022-08-03 14:19:12 +01:00
mattrussell36 a1d3094fc8 chore: update tranches
Signed-off-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2022-08-03 12:05:27 +00:00
Dexter Edwards 33a044e6ad fix: governance page crashing when a freeform proposal exists (#941) 2022-08-03 13:00:41 +01:00
mattrussell36 f11403b1af chore: update tranches
Signed-off-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2022-08-03 06:04:48 +00:00
mattrussell36 b9436877e5 chore: update tranches
Signed-off-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2022-08-03 00:08:51 +00:00
mattrussell36 d496758478 chore: update tranches
Signed-off-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2022-08-02 18:04:56 +00:00
mattrussell36 5f8002fb27 chore: update tranches
Signed-off-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2022-08-02 12:05:06 +00:00
Matthew Russell 85d838b9a6 Task/remove vegawallet service (#926)
* feat: improve error handling

* chore: lint

* fix: cypress test incorrect assertion
2022-08-02 07:37:46 +01:00
mattrussell36 459defddd2 chore: update tranches
Signed-off-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2022-08-02 06:05:31 +00:00
mattrussell36 52632c19d0 chore: update tranches
Signed-off-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2022-08-02 00:09:13 +00:00
mattrussell36 48c5b5cc4c chore: update tranches
Signed-off-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2022-08-01 18:05:43 +00:00
Dexter Edwards 8644eac2be feat: show correct reward type (#930) 2022-08-01 17:35:09 +01:00
asiaznik 92febfde6e fix(790): gap between orderbook headers and tabs (#929) 2022-08-01 16:49:31 +02:00
Sam Keen 76b3cdb312 fix/904: Validators sorting (#913) 2022-08-01 15:09:37 +01:00
mattrussell36 b751c5c27e chore: update tranches
Signed-off-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2022-08-01 12:05:05 +00:00
botond b1ded718a1 fix: make check link unclickable while loading (#928)
* fix: make check link unclickable while loading

* fix: format
2022-08-01 12:59:48 +01:00
macqbatandmaciek 7b4e618689 feat: [console-lite] - fixes in order margin calcs (#891)
* feat: [console-lite] - fixes in order margin calcs

* feat: [console-lite] - fixes in order margin calcs - fix decimals

* feat: [console-lite] - fixes in order margin calcs - add unit tests

* feat: [console-lite] - fixes in order margin calcs - improve cals

* feat: [console-lite] - fixes in order margin calcs - improve cals

* feat: [console-lite] - fixes in order margin calcs - more improvements

Co-authored-by: maciek <maciek@vegaprotocol.io>
2022-08-01 10:32:18 +01:00
Matthew Russell 4269060c9c Task/Remove vegawallet service api client (#916)
* chore: remove generated vegawallet client code and implement in rest connector

* feat: add zod validation

* feat: handle specific auth/token delete case

* feat: make withdraw dialog match vega tx dialog

* fix: response stub to be right shape, add content type to requests

* chore: revert unrelated classname change
2022-08-01 09:21:31 +01:00
mattrussell36 76efc3f68b chore: update tranches
Signed-off-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2022-08-01 06:11:59 +00:00
mattrussell36 32b9fae28e chore: update tranches
Signed-off-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2022-08-01 00:09:39 +00:00
mattrussell36 2ad3e68118 chore: update tranches
Signed-off-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2022-07-31 18:04:52 +00:00
mattrussell36 a505c6219a chore: update tranches
Signed-off-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2022-07-31 12:05:31 +00:00
mattrussell36 497f129097 chore: update tranches
Signed-off-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2022-07-31 06:04:28 +00:00
mattrussell36 a4d5f132a4 chore: update tranches
Signed-off-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2022-07-31 00:10:03 +00:00
mattrussell36 9e4b1c0bfc chore: update tranches
Signed-off-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2022-07-30 18:04:47 +00:00
mattrussell36 457843723b chore: update tranches
Signed-off-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2022-07-30 12:05:07 +00:00
mattrussell36 53e3326a6a chore: update tranches
Signed-off-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2022-07-30 06:05:12 +00:00
mattrussell36 4f73fdfbdf chore: update tranches
Signed-off-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2022-07-30 00:09:39 +00:00
mattrussell36 db51edca0a chore: update tranches
Signed-off-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2022-07-29 18:04:53 +00:00
Matthew Russell 5000474e0a Fix/eager connection (#923)
* fix: eager connect

* chore: add comment for eslint disable
2022-07-29 18:55:03 +01:00
Matthew Russell 7e715d08ec fix: text and theme icon colors (#920) 2022-07-29 17:50:32 +01:00
Sam Keen a4a1c20c7c Fix/687: Jump to block input should not allow negative block numbers or 0 (#918)
* Fix/687: Jump to block input should not allow negative block numbers

* Fix/687: Jump to block input should have a minimum of 1
2022-07-29 16:20:29 +01:00
Eddandsam-keen b9d57efacf Feat/893 anchor per network param (#907)
* feat: highlight network param targeted by anchor

* Feat/893: Highlighted key-value row colours

* Feat/893: Removed unneeded important bangs

Co-authored-by: sam-keen <samuel.kleinmann@gmail.com>
2022-07-29 13:06:49 +01:00
mattrussell36 e1c532cc05 chore: update tranches
Signed-off-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2022-07-29 12:05:28 +00:00
Radosław SzpiechandRado e026fa6244 test: add night runs again (#917)
Co-authored-by: Rado <rado@vegaprotocol.io>
2022-07-29 13:00:34 +02:00
Radosław SzpiechandRado 5f29d0ccb3 Remove nightly runs (#906)
* test: maybe passing true as string would work

* test: lets just create copy of nightly run but with manual trigger

* test: change to boolean true

* test: just remove old flows and create new ones - monkey would be better dev than me if it works

* test: forgot to delete workflows:

* test: yeah remove all of workflows because github does not differentiate removing from renaming

Co-authored-by: Rado <rado@vegaprotocol.io>
2022-07-29 12:10:15 +02:00
Sam Keen 394c38c601 Feat/633: Oracles MVP in block explorer (#871)
* frontend-monorepo-633: Oracles in block explorer - initial oracles page and router setup

* frontend-monorepo-633: Oracles in block explorer - added generated types file

* Feat/633: Cleanup for MVP1

* Feat/633: Cleanup of route name
2022-07-29 11:01:11 +01:00
Sam Keen 3c8a0b182f Fix/900: Validator list - pending stake formatted as per current stake (#901) 2022-07-29 10:08:02 +01:00
mattrussell36 cc6e62cff5 chore: update tranches
Signed-off-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2022-07-29 06:05:29 +00:00
mattrussell36 855934d639 chore: update tranches
Signed-off-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2022-07-29 00:09:24 +00:00
mattrussell36 48d69ed0cc chore: update tranches
Signed-off-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2022-07-28 18:05:02 +00:00
328 changed files with 14157 additions and 8455 deletions
@@ -129,12 +129,12 @@ jobs:
run: yarn install
- name: Run Cypress tests
run: yarn nx run ${{inputs.project}}:e2e --record --key ${{ secrets.CYPRESS_RECORD_KEY }} --browser chrome
run: yarn nx run ${{ github.event.inputs.project }}:e2e --record --key ${{ secrets.CYPRESS_RECORD_KEY }} --browser chrome
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.CYPESS_ETH_WALLET_MNEMONIC }}
CYPRESS_INCLUDE_FLOWS: ${{inputs.includeFlows}}
CYPRESS_INCLUDE_FLOWS: ${{ github.event.inputs.includeFlows }}
######
## Upload logs
@@ -1,4 +1,4 @@
name: Capsule tests -- nightly
name: Capsule tests -- night run
# This workflow runs the frontend tests against latest develop of the core to preempt breaking changes
@@ -106,7 +106,7 @@ jobs:
- name: Checkout frontend mono repo
uses: actions/checkout@v2
with:
ref: ${{ github.event.pull_request.head.ref }}
ref: master
fetch-depth: 0
- name: Install root dependencies
@@ -1,133 +0,0 @@
name: Capsule tests -- nightly test copy
# This workflow runs the frontend tests against latest develop of the core to preempt breaking changes
on: workflow_dispatch
jobs:
nightly:
name: Run capsule tests -- nightly
runs-on: ubuntu-latest
env:
GO111MODULE: 'on'
steps:
#######
## Setup langs
#######
- name: Set up Go
uses: actions/setup-go@v3
id: go
with:
go-version: 1.18
- name: Set up Node 16
uses: actions/setup-node@v2
id: npm
with:
node-version: 16
#######
## Checkout capsule
#######
# Checkout capsule to build local network
- name: Checkout capsule
uses: actions/checkout@v2
with:
repository: vegaprotocol/vegacapsule
ref: v0.2.1
token: ${{ secrets.VEGA_CI_BOT_GITHUB_TOKEN }}
path: './capsule'
#######
## Build binaries
#######
- name: Build capsule
run: go install
working-directory: capsule
- name: Set GOBIN
run: echo GOBIN=$(go env GOPATH)/bin >> $GITHUB_ENV
- name: Install binaries
run: vegacapsule install-bins
env:
GITHUB_TOKEN: ${{ secrets.VEGA_CI_BOT_GITHUB_TOKEN }}
GOBIN: ${{ env.GOBIN }}
- name: Checkout Vega
uses: actions/checkout@v2
with:
repository: vegaprotocol/vega
ref: tm35.9
token: ${{ secrets.VEGA_CI_BOT_GITHUB_TOKEN }}
path: './vega'
- name: Install binary from Vega repo
run: go install ./cmd/vega
working-directory: vega
######
## Start capsule
######
- name: Login to docker
run: echo -n ${{ secrets.VEGA_CI_BOT_GITHUB_TOKEN }} | docker login https://ghcr.io -u vega-ci-bot --password-stdin
- name: Start nomad
run: vegacapsule nomad &
- name: Bootstrap network
run: vegacapsule network bootstrap --config-path=./net_confs/config.hcl --force
working-directory: capsule
######
## Setup a Vega wallet for our user
######
- name: Create passphrase
run: echo "${{ secrets.CYPRESS_TRADING_TEST_VEGA_WALLET_PASSPHRASE }}" > ./passphrase
- name: Create recovery
run: echo "${{ secrets.TRADING_TEST_VEGA_WALLET_RECOVERY }}" > ./recovery
- name: Initialize wallet
run: vegawallet init -f --home ~/.vegacapsule/testnet/wallet
- name: Import wallet
run: vegawallet import -w UI_Trading_Test --recovery-phrase-file ./recovery -p ./passphrase --home ~/.vegacapsule/testnet/wallet
- name: Create public key 2
run: vegawallet key generate -w UI_Trading_Test -p ./passphrase --home ~/.vegacapsule/testnet/wallet
- name: Start service
run: vegawallet service run --network DV --automatic-consent --home ~/.vegacapsule/testnet/wallet &
######
## Run some tests
######
# Checkout front ends
- name: Checkout frontend mono repo
uses: actions/checkout@v2
with:
ref: ${{ github.event.pull_request.head.ref }}
fetch-depth: 0
- name: Install root dependencies
run: yarn install
- name: Run Cypress tests
run: yarn nx run-many --target=e2e --all --record --key ${{ secrets.CYPRESS_RECORD_KEY }} --browser chrome
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.CYPESS_ETH_WALLET_MNEMONIC }}
CYPRESS_NIGHTLY_RUN: true
CYPRESS_INCLUDE_FLOWS: 'true'
######
## Upload logs
######
- name: Logs
if: ${{ always() }}
run: vegacapsule network logs > vega-capsule-logs.txt
- uses: actions/upload-artifact@v2
with:
name: logs
path: ./vega-capsule-logs.txt
+1 -1
View File
@@ -21,7 +21,7 @@
# IDE - VSCode
.vscode/*
!.vscode/settings.json
.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
+1
View File
@@ -12,6 +12,7 @@ NX_EXPLORER_ASSETS=1
NX_EXPLORER_GENESIS=1
NX_EXPLORER_GOVERNANCE=1
NX_EXPLORER_MARKETS=1
NX_EXPLORER_ORACLES=1
NX_EXPLORER_TXS_LIST=0
NX_EXPLORER_NETWORK_PARAMETERS=1
NX_EXPLORER_PARTIES=1
+1
View File
@@ -11,6 +11,7 @@ NX_EXPLORER_ASSETS=1
NX_EXPLORER_GENESIS=1
NX_EXPLORER_GOVERNANCE=1
NX_EXPLORER_MARKETS=1
NX_EXPLORER_ORACLES=1
NX_EXPLORER_TXS_LIST=1
NX_EXPLORER_NETWORK_PARAMETERS=1
NX_EXPLORER_PARTIES=1
+1
View File
@@ -11,6 +11,7 @@ NX_EXPLORER_ASSETS=1
NX_EXPLORER_GENESIS=1
NX_EXPLORER_GOVERNANCE=1
NX_EXPLORER_MARKETS=1
NX_EXPLORER_ORACLES=1
NX_EXPLORER_TXS_LIST=1
NX_EXPLORER_NETWORK_PARAMETERS=1
NX_EXPLORER_PARTIES=1
+1
View File
@@ -11,6 +11,7 @@ NX_EXPLORER_ASSETS=1
NX_EXPLORER_GENESIS=1
NX_EXPLORER_GOVERNANCE=1
NX_EXPLORER_MARKETS=1
NX_EXPLORER_ORACLES=1
NX_EXPLORER_TXS_LIST=1
NX_EXPLORER_NETWORK_PARAMETERS=1
NX_EXPLORER_PARTIES=1
+1
View File
@@ -11,6 +11,7 @@ NX_EXPLORER_ASSETS=1
NX_EXPLORER_GENESIS=1
NX_EXPLORER_GOVERNANCE=1
NX_EXPLORER_MARKETS=1
NX_EXPLORER_ORACLES=1
NX_EXPLORER_TXS_LIST=1
NX_EXPLORER_NETWORK_PARAMETERS=1
NX_EXPLORER_PARTIES=1
@@ -64,20 +64,21 @@ context('Node switcher', function () {
cy.getByTestId('node-url-custom').click();
cy.getByTestId(customNodeBtn).within(() => {
cy.get('input').clear().type('https://n04.d.vega.xyz/query');
cy.get('input').clear().type('https://api.token.vega.xyz/query');
cy.getByTestId('link').click();
});
validateNodeError(errorTypeTxt, nodeErrorTxt);
});
afterEach('Close node switcher', function () {
cy.getByTestId(closeDialogBtn).click();
cy.getByTestId(closeDialogBtn).should('be.enabled').click();
});
function validateNodeError(errortype, errorMsg) {
cy.getByTestId(nodeErrorType).should('have.text', errortype, {
timeout: 1000,
});
cy.getByTestId(nodeErrorType, { timeout: 10000 }).should(
'have.text',
errortype
);
cy.getByTestId(nodeErrorMsg).should('have.text', errorMsg);
}
});
+1
View File
@@ -23,4 +23,5 @@ NX_EXPLORER_NETWORK_PARAMETERS=1
NX_EXPLORER_PARTIES=1
NX_EXPLORER_VALIDATORS=1
NX_EXPLORER_MARKETS=1
NX_EXPLORER_ORACLES=1
NX_EXPLORER_TXS_LIST=1
+1
View File
@@ -13,6 +13,7 @@ NX_EXPLORER_ASSETS=1
NX_EXPLORER_GENESIS=1
NX_EXPLORER_GOVERNANCE=1
NX_EXPLORER_MARKETS=1
NX_EXPLORER_ORACLES=1
NX_EXPLORER_TXS_LIST=1
NX_EXPLORER_NETWORK_PARAMETERS=1
NX_EXPLORER_PARTIES=1
+1
View File
@@ -58,6 +58,7 @@ There are a few different configuration options offered for this app:
| `NX_EXPLORER_GENESIS` | Enable the genesis page for the explorer |
| `NX_EXPLORER_GOVERNANCE` | Enable the governance page for the explorer |
| `NX_EXPLORER_MARKETS` | Enable the markets page for the explorer |
| `NX_EXPLORER_ORACLES` | Enable the oracles page for the explorer |
| `NX_EXPLORER_TXS_LIST` | Enable the transactions list page for the explorer |
| `NX_EXPLORER_NETWORK_PARAMETERS` | Enable the network parameters page for the explorer |
| `NX_EXPLORER_PARTIES` | Enable the parties page for the explorer |
@@ -29,6 +29,7 @@ export const JumpToBlock = () => {
inputType="number"
inputName="blockNumber"
submitHandler={handleSubmit}
inputMin={1}
/>
);
};
@@ -9,6 +9,8 @@ interface JumpToProps {
inputType: HTMLInputTypeAttribute;
inputName: string;
submitHandler: (arg0: SyntheticEvent) => void;
inputMin?: string | number;
inputMax?: string | number;
}
export const JumpTo = ({
@@ -18,6 +20,8 @@ export const JumpTo = ({
inputType,
inputName,
submitHandler,
inputMin,
inputMax,
}: JumpToProps) => {
return (
<form onSubmit={submitHandler}>
@@ -35,6 +39,8 @@ export const JumpTo = ({
name={inputName}
placeholder={placeholder}
className="max-w-[200px]"
min={inputMin}
max={inputMax}
/>
<Button
data-testid="go-submit"
+1
View File
@@ -24,6 +24,7 @@ export const ENV = {
genesis: truthy.includes(windowOrDefault('NX_EXPLORER_GENESIS')),
governance: truthy.includes(windowOrDefault('NX_EXPLORER_GOVERNANCE')),
markets: truthy.includes(windowOrDefault('NX_EXPLORER_MARKETS')),
oracles: truthy.includes(windowOrDefault('NX_EXPLORER_ORACLES')),
txsList: truthy.includes(windowOrDefault('NX_EXPLORER_TXS_LIST')),
networkParameters: truthy.includes(
windowOrDefault('NX_EXPLORER_NETWORK_PARAMETERS')
@@ -24,10 +24,10 @@ describe('NetworkParametersTable', () => {
);
const rows = screen.getAllByTestId('key-value-table-row');
expect(rows[0].children[0]).toHaveTextContent(
'Market Fee Factors Infrastructure Fee'
'market.fee.factors.infrastructureFee'
);
expect(rows[1].children[0]).toHaveTextContent(
'Market Liquidity Provision Min Lp Stake Quantum Multiple'
'market.liquidityProvision.minLpStakeQuantumMultiple'
);
expect(rows[0].children[1]).toHaveTextContent('0.0005');
expect(rows[1].children[1]).toHaveTextContent('1');
@@ -54,10 +54,10 @@ describe('NetworkParametersTable', () => {
);
const rows = screen.getAllByTestId('key-value-table-row');
expect(rows[0].children[0]).toHaveTextContent(
'Market Fee Factors Infrastructure Fee'
'market.fee.factors.infrastructureFee'
);
expect(rows[1].children[0]).toHaveTextContent(
'Market Liquidity Provision Min Lp Stake Quantum Multiple'
'market.liquidityProvision.minLpStakeQuantumMultiple'
);
expect(rows[0].children[1]).toHaveTextContent('0.0005');
expect(rows[1].children[1]).toHaveTextContent('1');
@@ -16,7 +16,22 @@ import type {
NetworkParametersQuery_networkParameters,
} from './__generated__/NetworkParametersQuery';
import orderBy from 'lodash/orderBy';
import startCase from 'lodash/startCase';
const PERCENTAGE_PARAMS = [
'governance.proposal.asset.requiredMajority',
'governance.proposal.asset.requiredParticipation',
'governance.proposal.freeform.requiredMajority',
'governance.proposal.freeform.requiredParticipation',
'governance.proposal.market.requiredMajority',
'governance.proposal.market.requiredParticipation',
'governance.proposal.updateMarket.requiredMajority',
'governance.proposal.updateMarket.requiredMajorityLP',
'governance.proposal.updateMarket.requiredParticipation',
'governance.proposal.updateMarket.requiredParticipationLP',
'governance.proposal.updateNetParam.requiredMajority',
'governance.proposal.updateNetParam.requiredParticipation',
'validators.vote.required',
];
const BIG_NUMBER_PARAMS = [
'spam.protection.delegation.min.tokens',
@@ -34,22 +49,35 @@ const BIG_NUMBER_PARAMS = [
'spam.protection.proposal.min.tokens',
'governance.proposal.updateMarket.minProposerBalance',
'governance.proposal.asset.minProposerBalance',
'governance.proposal.market.minProposerBalance',
'governance.proposal.market.minVoterBalance',
];
export const renderRow = ({
key,
value,
}: NetworkParametersQuery_networkParameters) => {
export const NetworkParameterRow = ({
row: { key, value },
}: {
row: NetworkParametersQuery_networkParameters;
}) => {
const isSyntaxRow = isJsonObject(value);
return (
<KeyValueTableRow key={key} inline={!isSyntaxRow}>
{startCase(key)}
<KeyValueTableRow
key={key}
inline={!isSyntaxRow}
id={key}
className={
'group target:bg-vega-pink target:text-white dark:target:bg-vega-yellow dark:target:text-black'
}
>
{key}
{isSyntaxRow ? (
<SyntaxHighlighter data={JSON.parse(value)} />
) : isNaN(Number(value)) ? (
value
) : BIG_NUMBER_PARAMS.includes(key) ? (
addDecimalsFormatNumber(Number(value), 18)
) : PERCENTAGE_PARAMS.includes(key) ? (
`${formatNumber(Number(value) * 100, 0)}%`
) : (
formatNumber(Number(value), 4)
)}
@@ -103,7 +131,9 @@ export const NetworkParametersTable = ({
);
return (
<KeyValueTable data-testid="parameters">
{(ascParams || []).map((row) => renderRow(row))}
{(ascParams || []).map((row) => (
<NetworkParameterRow key={row.key} row={row} />
))}
</KeyValueTable>
);
}}
@@ -0,0 +1,97 @@
/* tslint:disable */
/* eslint-disable */
// @generated
// This file was automatically generated and should not be edited.
import { OracleSpecStatus, PropertyKeyType, ConditionOperator } from "@vegaprotocol/types";
// ====================================================
// GraphQL query operation: OracleSpecs
// ====================================================
export interface OracleSpecs_oracleSpecs_filters_key {
__typename: "PropertyKey";
/**
* name is the name of the property.
*/
name: string | null;
/**
* type is the type of the property.
*/
type: PropertyKeyType;
}
export interface OracleSpecs_oracleSpecs_filters_conditions {
__typename: "Condition";
/**
* value is used by the comparator.
*/
value: string | null;
/**
* comparator is the type of comparison to make on the value.
*/
operator: ConditionOperator;
}
export interface OracleSpecs_oracleSpecs_filters {
__typename: "Filter";
/**
* key is the oracle data property key targeted by the filter.
*/
key: OracleSpecs_oracleSpecs_filters_key;
/**
* conditions are the conditions that should be matched by the data to be
* considered of interest.
*/
conditions: OracleSpecs_oracleSpecs_filters_conditions[] | null;
}
export interface OracleSpecs_oracleSpecs_data {
__typename: "OracleData";
/**
* pubKeys is the list of public keys that signed the data
*/
pubKeys: string[] | null;
}
export interface OracleSpecs_oracleSpecs {
__typename: "OracleSpec";
/**
* status describes the status of the oracle spec
*/
status: OracleSpecStatus;
/**
* id is a hash generated from the OracleSpec data.
*/
id: string;
/**
* RFC3339Nano creation date time
*/
createdAt: string;
/**
* RFC3339Nano last updated timestamp
*/
updatedAt: string | null;
/**
* pubKeys is the list of authorized public keys that signed the data for this
* oracle. All the public keys in the oracle data should be contained in these
* public keys.
*/
pubKeys: string[] | null;
/**
* filters describes which oracle data are considered of interest or not for
* the product (or the risk model).
*/
filters: OracleSpecs_oracleSpecs_filters[] | null;
/**
* data list all the oracle data broadcast to this spec
*/
data: OracleSpecs_oracleSpecs_data[];
}
export interface OracleSpecs {
/**
* All registered oracle specs
*/
oracleSpecs: OracleSpecs_oracleSpecs[] | null;
}
@@ -0,0 +1,53 @@
import { gql, useQuery } from '@apollo/client';
import type { OracleSpecs as OracleSpecsQuery } from './__generated__/OracleSpecs';
import React from 'react';
import { SyntaxHighlighter } from '@vegaprotocol/ui-toolkit';
import { RouteTitle } from '../../components/route-title';
import { t } from '@vegaprotocol/react-helpers';
import { SubHeading } from '../../components/sub-heading';
const ORACLE_SPECS_QUERY = gql`
query OracleSpecs {
oracleSpecs {
status
id
createdAt
updatedAt
pubKeys
filters {
key {
name
type
}
conditions {
value
operator
}
}
data {
pubKeys
}
}
}
`;
const Oracles = () => {
const { data } = useQuery<OracleSpecsQuery>(ORACLE_SPECS_QUERY);
return (
<section>
<RouteTitle data-testid="oracle-specs-heading">{t('Oracles')}</RouteTitle>
{data?.oracleSpecs
? data.oracleSpecs.map((o) => (
<React.Fragment key={o.id}>
<SubHeading>{o.id}</SubHeading>
<SyntaxHighlighter data={o} />
</React.Fragment>
))
: null}
</section>
);
};
export default Oracles;
@@ -8,5 +8,6 @@ export const Routes = {
GENESIS: 'genesis',
GOVERNANCE: 'governance',
MARKETS: 'markets',
ORACLES: 'oracles',
NETWORK_PARAMETERS: 'network-parameters',
};
@@ -3,6 +3,7 @@ import BlockPage from './blocks';
import Governance from './governance';
import Home from './home';
import Markets from './markets';
import Oracles from './oracles';
import Party from './parties';
import { Parties } from './parties/home';
import { Party as PartySingle } from './parties/id';
@@ -84,6 +85,17 @@ const marketsRoutes = flags.markets
]
: [];
const oraclesRoutes = flags.oracles
? [
{
path: Routes.ORACLES,
name: 'Oracles',
text: t('Oracles'),
element: <Oracles />,
},
]
: [];
const networkParametersRoutes = flags.networkParameters
? [
{
@@ -154,6 +166,7 @@ const routerConfig = [
...genesisRoutes,
...governanceRoutes,
...marketsRoutes,
...oraclesRoutes,
...networkParametersRoutes,
...validators,
];
+6 -5
View File
@@ -1,12 +1,12 @@
import * as Sentry from '@sentry/react';
import { BrowserTracing } from '@sentry/tracing';
import { StrictMode } from 'react';
import * as ReactDOM from 'react-dom';
import { createRoot } from 'react-dom/client';
import { BrowserRouter } from 'react-router-dom';
import './styles.css';
import App from './app/app';
import { ENV } from './app/config/env';
import { StrictMode } from 'react';
const { dsn } = ENV;
@@ -19,12 +19,13 @@ if (dsn) {
environment: ENV.envName,
});
}
const rootElement = document.getElementById('root');
const root = rootElement && createRoot(rootElement);
ReactDOM.render(
root?.render(
<StrictMode>
<BrowserRouter>
<App />
</BrowserRouter>
</StrictMode>,
document.getElementById('root')
</StrictMode>
);
@@ -12,8 +12,9 @@ module.exports = defineConfig({
supportFile: './src/support/index.ts',
video: true,
videoUploadOnPasses: false,
videosFolder: '../../dist/cypress/apps/explorer-e2e/videos',
screenshotsFolder: '../../dist/cypress/apps/explorer-e2e/screenshots',
videosFolder: '../../dist/cypress/apps/simple-trading-app-e2e/videos',
screenshotsFolder:
'../../dist/cypress/apps/simple-trading-app-e2e/screenshots',
chromeWebSecurity: false,
viewportWidth: 1440,
viewportHeight: 900,
@@ -5,18 +5,9 @@ import { generateMarketTags } from '../support/mocks/generate-market-tags';
import { generateMarketPositions } from '../support/mocks/generate-market-positions';
import { generateEstimateOrder } from '../support/mocks/generate-estimate-order';
import { generatePartyBalance } from '../support/mocks/generate-party-balance';
const connectVegaWallet = () => {
const form = 'rest-connector-form';
const walletName = Cypress.env('TRADING_TEST_VEGA_WALLET_NAME');
const walletPassphrase = Cypress.env('TRADING_TEST_VEGA_WALLET_PASSPHRASE');
cy.getByTestId('connect-vega-wallet').click();
cy.getByTestId('connectors-list').find('button').click();
cy.getByTestId(form).find('#wallet').click().type(walletName);
cy.getByTestId(form).find('#passphrase').click().type(walletPassphrase);
cy.getByTestId('rest-connector-form').find('button[type=submit]').click();
};
import { generatePartyMarketData } from '../support/mocks/generate-party-market-data';
import { generateMarketMarkPrice } from '../support/mocks/generate-market-mark-price';
import { connectVegaWallet } from '../support/connect-wallet';
describe('Market trade', () => {
let markets;
@@ -28,6 +19,8 @@ describe('Market trade', () => {
aliasQuery(req, 'MarketPositions', generateMarketPositions());
aliasQuery(req, 'EstimateOrder', generateEstimateOrder());
aliasQuery(req, 'PartyBalanceQuery', generatePartyBalance());
aliasQuery(req, 'PartyMarketData', generatePartyMarketData());
aliasQuery(req, 'MarketMarkPrice', generateMarketMarkPrice());
});
cy.visit('/markets');
cy.wait('@SimpleMarkets').then((response) => {
@@ -36,6 +29,7 @@ describe('Market trade', () => {
}
});
});
it('side selector should work well', () => {
if (markets?.length) {
cy.visit(`/trading/${markets[0].id}`);
@@ -53,7 +47,7 @@ describe('Market trade', () => {
}
});
it('mobile view should work well', () => {
it('side selector mobile view should work well', () => {
if (markets?.length) {
cy.viewport('iphone-xr');
cy.visit(`/trading/${markets[0].id}`);
@@ -85,6 +79,79 @@ describe('Market trade', () => {
}
});
it('size slider should work well', () => {
if (markets?.length) {
cy.visit(`/trading/${markets[1].id}`);
connectVegaWallet();
cy.get('#step-1-control [aria-label^="Selected value"]').click();
cy.get('button[aria-label="Open short position"]').click();
cy.get('#step-2-control').click();
cy.get('#step-2-panel')
.find('dd')
.eq(0)
.find('button')
.should('have.text', '1');
cy.get('#step-2-panel').find('[role="slider"]').type('{rightarrow}');
cy.get('#step-2-panel')
.find('dd')
.eq(0)
.find('button')
.should('have.text', '2');
}
});
it('percentage selection should work well', () => {
if (markets?.length) {
cy.visit(`/trading/${markets[1].id}`);
connectVegaWallet();
cy.get('#step-1-control [aria-label^="Selected value"]').click();
cy.get('button[aria-label="Open short position"]').click();
cy.get('#step-2-control').click();
cy.get('#step-2-panel')
.find('dd')
.eq(0)
.find('button')
.should('have.text', '1');
cy.getByTestId('percentage-selector')
.find('button')
.contains('Max')
.click();
cy.get('#step-2-panel')
.find('dd')
.eq(0)
.find('button')
.should('have.text', '21');
}
});
it('size input should work well', () => {
if (markets?.length) {
cy.visit(`/trading/${markets[1].id}`);
connectVegaWallet();
cy.get('#step-1-control [aria-label^="Selected value"]').click();
cy.get('button[aria-label="Open short position"]').click();
cy.get('#step-2-control').click();
cy.get('#step-2-panel')
.find('dd')
.eq(0)
.find('button')
.should('have.text', '1');
cy.get('#step-2-panel').find('dd').eq(0).find('button').click();
cy.get('#step-2-panel')
.find('dd')
.eq(0)
.find('input')
.type('{backspace}2');
cy.get('#step-2-panel').find('dd').eq(0).find('button').click();
cy.get('#step-2-panel')
.find('dd')
.eq(0)
.find('button')
.should('have.text', '2');
}
});
it('order review should display proper calculations', () => {
if (markets?.length) {
cy.visit(`/trading/${markets[0].id}`);
@@ -94,7 +161,7 @@ describe('Market trade', () => {
.find('dl')
.eq(1)
.find('dd div')
.should('have.text', '3.44055');
.should('have.text', '25.78726');
cy.getByTestId('key-value-table')
.find('dl')
.eq(2)
@@ -104,7 +171,7 @@ describe('Market trade', () => {
.find('dl')
.eq(3)
.find('dd div')
.should('have.text', '-785.81045');
.should('have.text', ' - ');
cy.getByTestId('place-order').click();
cy.getByTestId('dialog-title').should(
'have.text',
@@ -0,0 +1,34 @@
import {
connectVegaWallet,
disconnectVegaWallet,
} from '../support/connect-wallet';
describe('Portfolio page', () => {
afterEach(() => {
disconnectVegaWallet();
});
it('button for wallet connect should work', () => {
cy.visit('/');
cy.get('[href="/portfolio"]').eq(0).click();
cy.getByTestId('connect-vega-wallet-text').should('be.visible');
connectVegaWallet();
cy.getByTestId('connect-vega-wallet-text').should('not.exist');
});
it('certain tabs should exist', () => {
cy.visit('/portfolio');
connectVegaWallet();
cy.getByTestId('Assets').should('exist');
cy.getByTestId('tab-assets').should('exist');
cy.getByTestId('Positions').click();
cy.getByTestId('tab-positions').should('exist');
cy.getByTestId('Orders').click();
cy.getByTestId('tab-orders').should('exist');
cy.getByTestId('Fills').click();
cy.getByTestId('tab-fills').should('exist');
});
});
@@ -0,0 +1,16 @@
export const connectVegaWallet = () => {
const form = 'rest-connector-form';
const walletName = Cypress.env('TRADING_TEST_VEGA_WALLET_NAME');
const walletPassphrase = Cypress.env('TRADING_TEST_VEGA_WALLET_PASSPHRASE');
cy.getByTestId('connect-vega-wallet').click();
cy.getByTestId('connectors-list').find('button').click();
cy.getByTestId(form).find('#wallet').click().type(walletName);
cy.getByTestId(form).find('#passphrase').click().type(walletPassphrase);
cy.getByTestId('rest-connector-form').find('button[type=submit]').click();
};
export const disconnectVegaWallet = () => {
cy.getByTestId('connect-vega-wallet').click();
cy.getByTestId('disconnect').click();
};
@@ -0,0 +1,9 @@
export const generateMarketMarkPrice = () => {
return {
market: {
decimalPlaces: 5,
data: { markPrice: '692748', __typename: 'MarketData' },
__typename: 'Market',
},
};
};
@@ -2,6 +2,30 @@ export const generateMarketPositions = () => {
return {
party: {
id: '2e1ef32e5804e14232406aebaad719087d326afa5c648b7824d0823d8a46c8d1',
accounts: [
{
type: 'General',
asset: {
decimals: 5,
},
balance: '400000000000000000000',
market: {
id: '2751c508f9759761f912890f37fb3f97a00300bf7685c02a56a86e05facfe221',
__typename: 'Market',
},
},
{
type: 'Margin',
asset: {
decimals: 5,
},
balance: '265329',
market: {
id: 'first-btcusd-id',
__typename: 'Market',
},
},
],
positionsConnection: {
edges: [
{
@@ -9,10 +33,6 @@ export const generateMarketPositions = () => {
openVolume: '3',
market: {
id: '2751c508f9759761f912890f37fb3f97a00300bf7685c02a56a86e05facfe221',
accounts: [
{ balance: '0', __typename: 'Account' },
{ balance: '0', __typename: 'Account' },
],
__typename: 'Market',
},
__typename: 'Position',
@@ -24,10 +44,6 @@ export const generateMarketPositions = () => {
openVolume: '12',
market: {
id: 'first-btcusd-id',
accounts: [
{ balance: '10', __typename: 'Account' },
{ balance: '15', __typename: 'Account' },
],
__typename: 'Market',
},
__typename: 'Position',
@@ -4,6 +4,7 @@ export const generatePartyBalance = () => {
accounts: [
{
balance: '88474051',
type: 'General',
asset: {
id: '6d9d35f657589e40ddfb448b7ad4a7463b66efb307527fedd2aa7df1bbd5ea61',
symbol: 'tDAI',
@@ -15,6 +16,7 @@ export const generatePartyBalance = () => {
},
{
balance: '100000000',
type: 'General',
asset: {
id: '8b52d4a3a4b0ffe733cddbc2b67be273816cfeb6ca4c8b339bac03ffba08e4e4',
symbol: 'tEURO',
@@ -26,6 +28,7 @@ export const generatePartyBalance = () => {
},
{
balance: '3412867',
type: 'General',
asset: {
id: '6d9d35f657589e40ddfb448b7ad4a7463b66efb307527fedd2aa7df1bbd5ea61',
symbol: 'tDAI',
@@ -37,6 +40,7 @@ export const generatePartyBalance = () => {
},
{
balance: '70007',
type: 'General',
asset: {
id: '6d9d35f657589e40ddfb448b7ad4a7463b66efb307527fedd2aa7df1bbd5ea61',
symbol: 'tDAI',
@@ -0,0 +1,19 @@
export const generatePartyMarketData = () => {
return {
party: {
id: '2e1ef32e5804e14232406aebaad719087d326afa5c648b7824d0823d8a46c8d1',
accounts: [
{
type: 'General',
balance: '1200000',
asset: { id: 'fBTC', decimals: 5, __typename: 'Asset' },
market: null,
__typename: 'Account',
},
],
marginsConnection: { edges: null, __typename: 'MarginConnection' },
positionsConnection: { edges: null, __typename: 'PositionConnection' },
__typename: 'Party',
},
};
};
+4 -1
View File
@@ -17,8 +17,11 @@ NX_INCOMING_HOOK_BODY=$INCOMING_HOOK_BODY
NX_URL=$URL
NX_DEPLOY_URL=$DEPLOY_URL
NX_DEPLOY_PRIME_URL=$DEPLOY_PRIME_URL
NX_VEGA_CONFIG_URL="https://static.vega.xyz/assets/testnet-network.json"
NX_VEGA_ENV = 'TESTNET'
NX_VEGA_URL="https://lb.testnet.vega.xyz/query"
NX_VEGA_WALLET_URL=http://localhost:1789/api/v1
NX_ETHEREUM_PROVIDER_URL=https://ropsten.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8
NX_ETHERSCAN_URL=https://ropsten.etherscan.io
NX_VEGA_NETWORKS={\"MAINNET\":\"https://alpha.console.vega.xyz\"}
NX_VEGA_EXPLORER_URL=https://explorer.fairground.wtf
+4
View File
@@ -3,3 +3,7 @@ NX_VEGA_CONFIG_URL=https://static.vega.xyz/assets/devnet-network.json
NX_VEGA_URL=https://n04.d.vega.xyz/query
NX_VEGA_ENV=DEVNET
NX_VEGA_REST=https://n04.d.vega.xyz/datanode/rest
NX_VEGA_NETWORKS={\"MAINNET\":\"https://alpha.console.vega.xyz\"}
NX_ETHEREUM_PROVIDER_URL=https://ropsten.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8
NX_ETHERSCAN_URL=https://ropsten.etherscan.io
NX_VEGA_EXPLORER_URL=https://dev.explorer.vega.xyz
+4
View File
@@ -3,3 +3,7 @@ NX_VEGA_CONFIG_URL=https://static.vega.xyz/assets/mainnet-network.json
NX_VEGA_URL=https://api.token.vega.xyz/query
NX_VEGA_ENV=MAINNET
NX_VEGA_REST=https://api.token.vega.xyz/
NX_VEGA_NETWORKS='{\"MAINNET\":\"https://alpha.console.vega.xyz\"}'
NX_ETHEREUM_PROVIDER_URL=https://mainnet.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8
NX_ETHERSCAN_URL=https://etherscan.io
NX_VEGA_EXPLORER_URL=https://explorer.vega.xyz
+4
View File
@@ -3,3 +3,7 @@ NX_VEGA_CONFIG_URL=https://static.vega.xyz/assets/stagnet1-network.json
NX_VEGA_URL=https://n03.s.vega.xyz/query
NX_VEGA_ENV=STAGNET
NX_VEGA_REST=https://n03.s.vega.xyz/datanode/rest
NX_VEGA_NETWORKS='{\"MAINNET\":\"https://alpha.console.vega.xyz\"}'
NX_ETHEREUM_PROVIDER_URL=https://ropsten.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8
NX_ETHERSCAN_URL=https://ropsten.etherscan.io
NX_VEGA_EXPLORER_URL=https://staging.explorer.vega.xyz
+4
View File
@@ -3,3 +3,7 @@ NX_VEGA_CONFIG_URL=https://static.vega.xyz/assets/stagnet2-network.json
NX_VEGA_URL=https://n03.stagnet2.vega.xyz/query
NX_VEGA_ENV=STAGNET2
NX_VEGA_REST=https://n01.stagnet2.vega.xyz/datanode/rest
NX_VEGA_NETWORKS='{\"MAINNET\":\"https://alpha.console.vega.xyz\"}'
NX_ETHEREUM_PROVIDER_URL=https://ropsten.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8
NX_ETHERSCAN_URL=https://ropsten.etherscan.io
NX_VEGA_EXPLORER_URL=https://staging2.explorer.vega.xyz
+4
View File
@@ -3,3 +3,7 @@ NX_VEGA_CONFIG_URL=https://static.vega.xyz/assets/testnet-network.json
NX_VEGA_URL=https://lb.testnet.vega.xyz/query
NX_VEGA_ENV=TESTNET
NX_VEGA_REST=https://lb.testnet.vega.xyz/datanode/rest
NX_VEGA_NETWORKS='{\"MAINNET\":\"https://alpha.console.vega.xyz\"}'
NX_ETHEREUM_PROVIDER_URL=https://ropsten.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8
NX_ETHERSCAN_URL=https://ropsten.etherscan.io
NX_VEGA_EXPLORER_URL=https://explorer.fairground.wtf
+22 -28
View File
@@ -1,8 +1,9 @@
import { useState, useEffect } from 'react';
import { useLocation } from 'react-router-dom';
import { createClient } from './lib/apollo-client';
import { ThemeContext } from '@vegaprotocol/react-helpers';
import { useThemeSwitcher } from '@vegaprotocol/react-helpers';
import { EnvironmentProvider, NetworkLoader } from '@vegaprotocol/environment';
import { createClient } from './lib/apollo-client';
import {
VegaConnectDialog,
VegaManageDialog,
@@ -13,14 +14,12 @@ import '../styles.scss';
import { AppLoader } from './components/app-loader';
import Header from './components/header';
import { Main } from './components/main';
import { useLocation } from 'react-router-dom';
import LocalContext from './context/local-context';
import useLocalValues from './hooks/use-local-values';
function App() {
const localValues = useLocalValues();
const [theme, toggleTheme] = useThemeSwitcher();
const [vegaWallet, setVegaWallet] = useState({
connect: false,
manage: false,
});
const [menuOpen, setMenuOpen] = useState(false);
const onToggle = () => setMenuOpen(!menuOpen);
@@ -36,28 +35,23 @@ function App() {
<ThemeContext.Provider value={theme}>
<NetworkLoader createClient={createClient}>
<VegaWalletProvider>
<AppLoader>
<div className="max-h-full min-h-full dark:bg-lite-black dark:text-white-60 bg-white text-black-60 grid grid-rows-[min-content,1fr]">
<Header
setVegaWallet={setVegaWallet}
toggleTheme={toggleTheme}
/>
<Main isMenuOpen={menuOpen} onToggle={onToggle} />
<VegaConnectDialog
connectors={Connectors}
dialogOpen={vegaWallet.connect}
setDialogOpen={(open) =>
setVegaWallet((x) => ({ ...x, connect: open }))
}
/>
<VegaManageDialog
dialogOpen={vegaWallet.manage}
setDialogOpen={(open) =>
setVegaWallet((x) => ({ ...x, manage: open }))
}
/>
</div>
</AppLoader>
<LocalContext.Provider value={localValues}>
<AppLoader>
<div className="max-h-full min-h-full dark:bg-lite-black dark:text-white-60 bg-white text-black-60 grid grid-rows-[min-content,1fr]">
<Header toggleTheme={toggleTheme} />
<Main isMenuOpen={menuOpen} onToggle={onToggle} />
<VegaConnectDialog
connectors={Connectors}
dialogOpen={localValues.vegaWalletDialog.connect}
setDialogOpen={localValues.vegaWalletDialog.setConnect}
/>
<VegaManageDialog
dialogOpen={localValues.vegaWalletDialog.manage}
setDialogOpen={localValues.vegaWalletDialog.setManage}
/>
</div>
</AppLoader>
</LocalContext.Provider>
</VegaWalletProvider>
</NetworkLoader>
</ThemeContext.Provider>
@@ -3,6 +3,8 @@
// @generated
// This file was automatically generated and should not be edited.
import { AccountType } from "@vegaprotocol/types";
// ====================================================
// GraphQL query operation: PartyBalanceQuery
// ====================================================
@@ -29,6 +31,10 @@ export interface PartyBalanceQuery_party_accounts_asset {
export interface PartyBalanceQuery_party_accounts {
__typename: "Account";
/**
* Account type (General, Margin, etc)
*/
type: AccountType;
/**
* Balance as string - current account balance (approx. as balances can be updated several times per second)
*/
@@ -5,6 +5,7 @@ import type {
PartyBalanceQuery_party_accounts_asset,
} from './__generated__/PartyBalanceQuery';
import { DealTicketBalance } from './deal-ticket-balance';
import { AccountType } from '@vegaprotocol/types';
const tDAI: PartyBalanceQuery_party_accounts_asset = {
__typename: 'Asset',
@@ -17,6 +18,7 @@ const tDAI: PartyBalanceQuery_party_accounts_asset = {
const accounts: PartyBalanceQuery_party_accounts[] = [
{
__typename: 'Account',
type: AccountType.General,
balance: '1000000',
asset: tDAI,
},
@@ -4,6 +4,7 @@ import type { DealTicketQuery_market_tradableInstrument_instrument_product_settl
import type { PartyBalanceQuery_party_accounts } from './__generated__/PartyBalanceQuery';
import { useSettlementAccount } from '../../hooks/use-settlement-account';
import { addDecimalsFormatNumber, t } from '@vegaprotocol/react-helpers';
import { AccountType } from '@vegaprotocol/types';
interface DealTicketBalanceProps {
settlementAsset: DealTicketQuery_market_tradableInstrument_instrument_product_settlementAsset;
@@ -20,7 +21,11 @@ export const DealTicketBalance = ({
}: DealTicketBalanceProps) => {
const settlementAssetId = settlementAsset?.id;
const settlementAssetSymbol = settlementAsset?.symbol;
const settlementAccount = useSettlementAccount(settlementAssetId, accounts);
const settlementAccount = useSettlementAccount(
settlementAssetId,
accounts,
AccountType.General
);
const formatedNumber =
settlementAccount?.balance &&
settlementAccount.asset.decimals &&
@@ -17,6 +17,7 @@ const PARTY_BALANCE_QUERY = gql`
query PartyBalanceQuery($partyId: ID!) {
party(id: $partyId) {
accounts {
type
balance
asset {
id
@@ -0,0 +1,201 @@
import React, { useCallback, useState } from 'react';
import { t } from '@vegaprotocol/react-helpers';
import {
SliderRoot,
SliderThumb,
SliderTrack,
SliderRange,
Button,
Input,
FormGroup,
} from '@vegaprotocol/ui-toolkit';
import { BigNumber } from 'bignumber.js';
interface DealTicketSizeProps {
step: number;
min: number;
max: number;
value: number;
onValueChange: (value: number[]) => void;
name: string;
quoteName: string;
price: string;
estCloseOut: string;
estMargin: string;
positionDecimalPlaces: number;
}
const getSizeLabel = (value: number): string => {
const MIN_LABEL = 'Min';
const MAX_LABEL = 'Max';
if (value === 0) {
return MIN_LABEL;
} else if (value === 100) {
return MAX_LABEL;
}
return `${value}%`;
};
export const DealTicketSize = ({
value,
step,
min,
max,
price,
quoteName,
onValueChange,
estCloseOut,
positionDecimalPlaces,
}: DealTicketSizeProps) => {
const sizeRatios = [0, 25, 50, 75, 100];
const [inputValue, setInputValue] = useState(value);
const [isInputVisible, setIsInputVisible] = useState(false);
const onInputValueChange = useCallback(
(event: React.ChangeEvent<HTMLInputElement>) => {
const value = parseFloat(event.target.value);
const isLessThanMin = value < min;
const isMoreThanMax = value > max;
if (value) {
if (isLessThanMin) {
onValueChange([min]);
} else if (isMoreThanMax) {
onValueChange([max]);
} else {
onValueChange([value]);
}
}
setInputValue(value);
},
[min, max, onValueChange, setInputValue]
);
const onButtonValueChange = useCallback(
(size: number) => {
if (isInputVisible) {
setIsInputVisible(false);
}
const newVal = new BigNumber(size)
.decimalPlaces(positionDecimalPlaces)
.toNumber();
onValueChange([newVal]);
setInputValue(newVal);
},
[isInputVisible, onValueChange, positionDecimalPlaces]
);
const toggleInput = useCallback(() => {
setIsInputVisible(!isInputVisible);
}, [isInputVisible]);
const onInputEnter = useCallback(
(event: React.KeyboardEvent) => {
if (event.key === 'Enter') {
event.stopPropagation();
toggleInput();
}
},
[toggleInput]
);
return max === 0 ? (
<p>Not enough balance to trade</p>
) : (
<div>
<div className="flex justify-between text-black dark:text-white mb-8">
<span>{min}</span>
<span>{max}</span>
</div>
<SliderRoot
className="mb-8"
value={[value]}
onValueChange={onValueChange}
step={step}
min={min}
max={max}
>
<SliderTrack className="bg-lightGrey dark:bg-offBlack">
<SliderRange className="!bg-black dark:!bg-white" />
</SliderTrack>
<SliderThumb />
</SliderRoot>
<div
data-testid="percentage-selector"
className="flex w-full justify-between text-black dark:text-white mb-32"
>
{sizeRatios.map((size, index) => {
const proportionalSize = size ? (size / 100) * max : min;
return (
<Button
variant="inline-link"
className="no-underline !text-blue"
onClick={() => onButtonValueChange(proportionalSize)}
key={index}
>
{getSizeLabel(size)}
</Button>
);
})}
</div>
<dl className="text-black dark:text-white">
<div className="flex items-center justify-between mb-8">
<dt>
<span>{t('Size')}</span>
&nbsp;
<small>({quoteName})</small>
</dt>
<dd className="flex justify-end w-full">
<FormGroup
className="mb-0 flex items-center"
labelClassName="mr-8 sr-only"
label="Enter Size"
labelFor="trade-size-input"
>
{isInputVisible ? (
<>
<Input
id="input-order-size-market"
type="number"
step={step}
min={min}
max={max}
className="w-full"
value={inputValue}
onKeyDown={onInputEnter}
onChange={onInputValueChange}
/>
<Button
variant="inline-link"
className="no-underline !text-blue"
onClick={toggleInput}
>
{t('set')}
</Button>
</>
) : (
<Button
variant="inline-link"
className="no-underline !text-blue"
onClick={toggleInput}
>
{value}
</Button>
)}
</FormGroup>
</dd>
</div>
<div className="flex justify-between mb-8">
<dt>{t('Est. price')}</dt>
<dd>{price}</dd>
</div>
<div className="flex justify-between">
<dt>{t('Est. close out')}</dt>
<dd>{estCloseOut}</dd>
</div>
</dl>
</div>
);
};
@@ -1,29 +1,34 @@
import React, { useCallback } from 'react';
import React, { useCallback, useEffect, useState } from 'react';
import { useNavigate } from 'react-router-dom';
import { useForm, Controller } from 'react-hook-form';
import { Stepper } from '../stepper';
import type { DealTicketQuery_market } from '@vegaprotocol/deal-ticket';
import { InputError } from '@vegaprotocol/ui-toolkit';
import { BigNumber } from 'bignumber.js';
import {
DealTicketAmount,
getDialogTitle,
getDialogIntent,
getDialogIcon,
getOrderDialogTitle,
getOrderDialogIntent,
getOrderDialogIcon,
MarketSelector,
} from '@vegaprotocol/deal-ticket';
import type { Order } from '@vegaprotocol/orders';
import { VegaTxStatus } from '@vegaprotocol/wallet';
import { useVegaWallet, VegaTxStatus } from '@vegaprotocol/wallet';
import { t, addDecimal, toDecimal } from '@vegaprotocol/react-helpers';
import {
getDefaultOrder,
useOrderValidation,
useOrderSubmit,
OrderFeedback,
validateSize,
} from '@vegaprotocol/orders';
import { DealTicketSize } from './deal-ticket-size';
import MarketNameRenderer from '../simple-market-list/simple-market-renderer';
import SideSelector, { SIDE_NAMES } from './side-selector';
import ReviewTrade from './review-trade';
import type { PartyBalanceQuery } from './__generated__/PartyBalanceQuery';
import useOrderCloseOut from '../../hooks/use-order-closeout';
import useOrderMargin from '../../hooks/use-order-margin';
import useMaximumPositionSize from '../../hooks/use-maximum-position-size';
interface DealTicketMarketProps {
market: DealTicketQuery_market;
@@ -36,28 +41,55 @@ export const DealTicketSteps = ({
}: DealTicketMarketProps) => {
const navigate = useNavigate();
const setMarket = useCallback(
(marketId) => {
(marketId: string) => {
navigate(`/trading/${marketId}`);
},
[navigate]
);
const {
register,
control,
handleSubmit,
watch,
setValue,
formState: { errors },
} = useForm<Order>({
mode: 'onChange',
defaultValues: getDefaultOrder(market),
});
const [max, setMax] = useState<number | null>(null);
const step = toDecimal(market.positionDecimalPlaces);
const orderType = watch('type');
const orderTimeInForce = watch('timeInForce');
const orderSide = watch('side');
const orderSize = watch('size');
const order = watch();
const estCloseOut = useOrderCloseOut({ order, market, partyData });
const { keypair } = useVegaWallet();
const estMargin = useOrderMargin({
order,
market,
partyId: keypair?.pub || '',
});
const maxTrade = useMaximumPositionSize({
partyId: keypair?.pub || '',
accounts: partyData?.party?.accounts || [],
marketId: market.id,
settlementAssetId:
market.tradableInstrument.instrument.product.settlementAsset.id,
price: market?.depth?.lastTrade?.price,
order,
});
useEffect(() => {
setMax(
new BigNumber(maxTrade)
.decimalPlaces(market.positionDecimalPlaces)
.toNumber()
);
}, [maxTrade, market.positionDecimalPlaces]);
const { message: invalidText, isDisabled } = useOrderValidation({
step,
@@ -70,6 +102,16 @@ export const DealTicketSteps = ({
const { submit, transaction, finalizedOrder, TransactionDialog } =
useOrderSubmit(market);
const onSizeChange = (value: number[]) => {
const newVal = new BigNumber(value[0])
.decimalPlaces(market.positionDecimalPlaces)
.toString();
const isValid = validateSize(step)(newVal);
if (isValid !== 'step') {
setValue('size', newVal);
}
};
const transactionStatus =
transaction.status === VegaTxStatus.Requested ||
transaction.status === VegaTxStatus.Pending
@@ -112,19 +154,29 @@ export const DealTicketSteps = ({
},
{
label: t('Choose Position Size'),
component: (
<DealTicketAmount
orderType={orderType}
step={step}
register={register}
price={
market.depth.lastTrade
? addDecimal(market.depth.lastTrade.price, market.decimalPlaces)
: undefined
}
quoteName={market.tradableInstrument.instrument.product.quoteName}
/>
),
component:
max !== null ? (
<DealTicketSize
step={step}
min={step}
max={max}
onValueChange={onSizeChange}
value={new BigNumber(orderSize).toNumber()}
name="size"
price={
market.depth.lastTrade
? addDecimal(market.depth.lastTrade.price, market.decimalPlaces)
: ''
}
positionDecimalPlaces={market.positionDecimalPlaces}
quoteName={market.tradableInstrument.instrument.product.quoteName}
estCloseOut={estCloseOut}
estMargin={estMargin || ' - '}
/>
) : (
'loading...'
),
value: orderSize,
},
{
label: t('Review Trade'),
@@ -140,12 +192,13 @@ export const DealTicketSteps = ({
isDisabled={isDisabled}
transactionStatus={transactionStatus}
order={order}
partyData={partyData}
estCloseOut={estCloseOut}
estMargin={estMargin || ' - '}
/>
<TransactionDialog
title={getDialogTitle(finalizedOrder?.status)}
intent={getDialogIntent(finalizedOrder?.status)}
icon={getDialogIcon(finalizedOrder?.status)}
title={getOrderDialogTitle(finalizedOrder?.status)}
intent={getOrderDialogIntent(finalizedOrder?.status)}
icon={getOrderDialogIcon(finalizedOrder?.status)}
>
<OrderFeedback transaction={transaction} order={finalizedOrder} />
</TransactionDialog>
@@ -1 +1,2 @@
export { DealTicketContainer } from './deal-ticket-container';
export * from './deal-ticket-container';
export * from './deal-ticket-size';
@@ -10,17 +10,14 @@ import classNames from 'classnames';
import type { DealTicketQuery_market } from '@vegaprotocol/deal-ticket';
import type { Order } from '@vegaprotocol/orders';
import { SIDE_NAMES } from './side-selector';
import { useVegaWallet, VegaWalletOrderSide } from '@vegaprotocol/wallet';
import { VegaWalletOrderSide } from '@vegaprotocol/wallet';
import SimpleMarketExpires from '../simple-market-list/simple-market-expires';
import { gql, useQuery } from '@apollo/client';
import type {
MarketTags,
MarketTagsVariables,
} from './__generated__/MarketTags';
import useOrderMargin from '../../hooks/use-order-margin';
import useOrderCloseOut from '../../hooks/use-order-closeout';
import { IconNames } from '@blueprintjs/icons';
import type { PartyBalanceQuery } from './__generated__/PartyBalanceQuery';
export const MARKET_TAGS_QUERY = gql`
query MarketTags($marketId: ID!) {
@@ -41,7 +38,8 @@ interface Props {
isDisabled: boolean;
transactionStatus?: string;
order: Order;
partyData?: PartyBalanceQuery;
estCloseOut: string;
estMargin: string;
}
export default ({
@@ -49,21 +47,16 @@ export default ({
market,
order,
transactionStatus,
partyData,
estCloseOut,
estMargin,
}: Props) => {
const { keypair } = useVegaWallet();
const { data: tagsData } = useQuery<MarketTags, MarketTagsVariables>(
MARKET_TAGS_QUERY,
{
variables: { marketId: market.id },
}
);
const estMargin = useOrderMargin({
order,
market,
partyId: keypair?.pub || '',
});
const estCloseOut = useOrderCloseOut({ order, market, partyData });
return (
<div className="mb-8 text-black dark:text-white">
<KeyValueTable>
@@ -1,18 +1,16 @@
import React from 'react';
import React, { useContext } from 'react';
import { ThemeSwitcher } from '@vegaprotocol/ui-toolkit';
import Logo from './logo';
import { VegaWalletConnectButton } from '../vega-wallet-connect-button';
type WalletParams = {
connect: boolean;
manage: boolean;
};
import LocalContext from '../../context/local-context';
interface Props {
setVegaWallet: (func: (wParams: WalletParams) => WalletParams) => void;
toggleTheme: () => void;
}
const Header = ({ setVegaWallet, toggleTheme }: Props) => {
const Header = ({ toggleTheme }: Props) => {
const {
vegaWalletDialog: { setConnect, setManage },
} = useContext(LocalContext);
return (
<div
className="flex items-stretch pr-16 py-16 bg-black text-white-60"
@@ -21,12 +19,8 @@ const Header = ({ setVegaWallet, toggleTheme }: Props) => {
<Logo />
<div className="flex items-center gap-4 ml-auto mr-8 relative z-10">
<VegaWalletConnectButton
setConnectDialog={(open) =>
setVegaWallet((x) => ({ ...x, connect: open }))
}
setManageDialog={(open) =>
setVegaWallet((x) => ({ ...x, manage: open }))
}
setConnectDialog={setConnect}
setManageDialog={setManage}
/>
<ThemeSwitcher
onToggle={toggleTheme}
@@ -1,13 +1,49 @@
import { useContext } from 'react';
import { AccountsContainer } from '@vegaprotocol/accounts';
import { t } from '@vegaprotocol/react-helpers';
import { useVegaWallet } from '@vegaprotocol/wallet';
import { Button, Splash, Tabs, Tab } from '@vegaprotocol/ui-toolkit';
import LocalContext from '../../context/local-context';
import { OrderListContainer } from '@vegaprotocol/orders';
import { PositionsContainer } from '@vegaprotocol/positions';
import { FillsContainer } from '@vegaprotocol/fills';
export const Portfolio = () => {
const { keypair } = useVegaWallet();
const {
vegaWalletDialog: { setConnect },
} = useContext(LocalContext);
if (!keypair) {
return (
<Splash>
<div className="text-center">
<p className="mb-12" data-testid="connect-vega-wallet-text">
{t('Connect your Vega wallet')}
</p>
<Button
onClick={() => setConnect(true)}
data-testid="vega-wallet-connect"
>
{t('Connect')}
</Button>
</div>
</Splash>
);
}
return (
<section>
<h2 className="text-lg mb-16">{t('Assets')}</h2>
<div className="h-screen">
<Tabs>
<Tab id="assets" name={t('Assets')}>
<AccountsContainer />
</div>
</section>
</Tab>
<Tab id="positions" name={t('Positions')}>
<PositionsContainer />
</Tab>
<Tab id="orders" name={t('Orders')}>
<OrderListContainer />
</Tab>
<Tab id="fills" name={t('Fills')}>
<FillsContainer />
</Tab>
</Tabs>
);
};
@@ -1,4 +1,4 @@
import React from 'react';
import * as React from 'react';
import { act } from 'react-dom/test-utils';
import {
render,
@@ -60,6 +60,7 @@ describe('SimpleMarketList', () => {
};
await act(async () => {
render(
// @ts-ignore different versions of react types in apollo and app
<MockedProvider mocks={[mocks, filterMock]}>
<SimpleMarketList />
</MockedProvider>,
@@ -129,6 +130,7 @@ describe('SimpleMarketList', () => {
};
await act(async () => {
render(
// @ts-ignore different versions of react types in apollo and app
<MockedProvider mocks={[mocks, filterMock]}>
<SimpleMarketList />
</MockedProvider>,
@@ -25,6 +25,14 @@ import SimpleMarketToolbar from './simple-market-toolbar';
import type { SimpleMarkets_markets } from './__generated__/SimpleMarkets';
import type { SimpleMarketDataSub_marketData } from './__generated__/SimpleMarketDataSub';
import { IS_MARKET_TRADABLE } from '../../constants';
import type {
CellKeyDownEvent,
FullWidthCellKeyDownEvent,
} from 'ag-grid-community/dist/lib/events';
import type {
GetRowIdParams,
TabToNextCellParams,
} from 'ag-grid-community/dist/lib/entities/iCallbackParams';
export type SimpleMarketsType = SimpleMarkets_markets & {
percentChange?: number | '-';
@@ -84,7 +92,7 @@ const SimpleMarketList = () => {
const { columnDefs, defaultColDef } = useColumnDefinitions({ isMobile });
const getRowId = useCallback(({ data }) => data.id, []);
const getRowId = useCallback(({ data }: GetRowIdParams) => data.id, []);
const handleRowClicked = useCallback(
({ data }: { data: SimpleMarketsType }) => {
@@ -95,7 +103,7 @@ const SimpleMarketList = () => {
[navigate]
);
const onTabToNextCell = useCallback((params) => {
const onTabToNextCell = useCallback((params: TabToNextCellParams) => {
const {
api,
previousCellPosition: { rowIndex },
@@ -108,7 +116,11 @@ const SimpleMarketList = () => {
}, []);
const onCellKeyDown = useCallback(
(params) => {
(
params: (CellKeyDownEvent | FullWidthCellKeyDownEvent) & {
event: KeyboardEvent;
}
) => {
const { event: { key = '' } = {}, data } = params;
if (key === 'Enter') {
handleRowClicked({ data });
@@ -7,6 +7,7 @@ import type { MockedResponse } from '@apollo/client/testing';
import type { MarketFilters } from './__generated__/MarketFilters';
import { FILTERS_QUERY } from './data-provider';
import filterData from './mocks/market-filters.json';
import { act } from 'react-dom/test-utils';
describe('SimpleMarketToolbar', () => {
const filterMock: MockedResponse<MarketFilters> = {
@@ -58,65 +59,74 @@ describe('SimpleMarketToolbar', () => {
});
it('should be properly rendered', async () => {
render(
<MockedProvider mocks={[filterMock]} addTypename={false}>
<WrappedCompForTest />
</MockedProvider>,
{ wrapper: BrowserRouter }
);
await waitFor(() => {
expect(screen.getByText('Future')).toBeInTheDocument();
});
fireEvent.click(screen.getByText('Future'));
await waitFor(() => {
expect(screen.getByTestId('market-products-menu').children).toHaveLength(
3
act(async () => {
render(
// @ts-ignore different versions of react types in apollo and app
<MockedProvider mocks={[filterMock]} addTypename={false}>
<WrappedCompForTest />
</MockedProvider>,
{ wrapper: BrowserRouter }
);
expect(screen.getByTestId('market-assets-menu').children).toHaveLength(6);
});
fireEvent.click(screen.getByTestId('state-trigger'));
waitFor(() => {
expect(screen.getByRole('menu')).toBeInTheDocument();
expect(screen.getByRole('menu').children).toHaveLength(10);
await waitFor(() => {
expect(screen.getByText('Future')).toBeInTheDocument();
});
fireEvent.click(screen.getByText('Future'));
await waitFor(() => {
expect(
screen.getByTestId('market-products-menu').children
).toHaveLength(3);
expect(screen.getByTestId('market-assets-menu').children).toHaveLength(
6
);
});
fireEvent.click(screen.getByTestId('state-trigger'));
waitFor(() => {
expect(screen.getByRole('menu')).toBeInTheDocument();
expect(screen.getByRole('menu').children).toHaveLength(10);
});
});
});
it('navigation should work well', async () => {
render(
<MockedProvider mocks={[filterMock]} addTypename={false}>
<WrappedCompForTest />
</MockedProvider>,
{ wrapper: BrowserRouter }
);
await waitFor(() => {
expect(screen.getByText('Future')).toBeInTheDocument();
});
fireEvent.click(screen.getByText('Future'));
await waitFor(() => {
expect(screen.getByTestId('location-display')).toHaveTextContent(
'/markets/Active/Future'
act(async () => {
render(
// @ts-ignore different versions of react types in apollo and app
<MockedProvider mocks={[filterMock]} addTypename={false}>
<WrappedCompForTest />
</MockedProvider>,
{ wrapper: BrowserRouter }
);
});
fireEvent.click(
screen
.getByTestId('market-assets-menu')
.children[5].querySelector('a') as HTMLAnchorElement
);
await waitFor(() => {
expect(screen.getByTestId('location-display')).toHaveTextContent(
'/markets/Active/Future/tEURO'
);
});
await waitFor(() => {
expect(screen.getByText('Future')).toBeInTheDocument();
});
fireEvent.click(screen.getByText('Future'));
fireEvent.click(screen.getByTestId('state-trigger'));
waitFor(() => {
expect(screen.getByRole('menu')).toBeInTheDocument();
fireEvent.click(screen.getByText('Pending'));
expect(screen.getByTestId('location-display')).toHaveTextContent(
'/markets/Pending/Future/tEURO'
await waitFor(() => {
expect(screen.getByTestId('location-display')).toHaveTextContent(
'/markets/Active/Future'
);
});
fireEvent.click(
screen
.getByTestId('market-assets-menu')
.children[5].querySelector('a') as HTMLAnchorElement
);
await waitFor(() => {
expect(screen.getByTestId('location-display')).toHaveTextContent(
'/markets/Active/Future/tEURO'
);
});
fireEvent.click(screen.getByTestId('state-trigger'));
waitFor(() => {
expect(screen.getByRole('menu')).toBeInTheDocument();
fireEvent.click(screen.getByText('Pending'));
expect(screen.getByTestId('location-display')).toHaveTextContent(
'/markets/Pending/Future/tEURO'
);
});
});
});
});
@@ -0,0 +1,16 @@
import { createContext } from 'react';
export interface VegaWalletDialogState {
connect: boolean;
manage: boolean;
setConnect: (isOpen: boolean) => void;
setManage: (isOpen: boolean) => void;
}
export interface LocalValues {
vegaWalletDialog: VegaWalletDialogState;
}
const LocalContext = createContext<LocalValues>({} as LocalValues);
export default LocalContext;
@@ -0,0 +1,52 @@
/* tslint:disable */
/* eslint-disable */
// @generated
// This file was automatically generated and should not be edited.
// ====================================================
// GraphQL query operation: MarketMarkPrice
// ====================================================
export interface MarketMarkPrice_market_data {
__typename: "MarketData";
/**
* the mark price (actually an unsigned int)
*/
markPrice: string;
}
export interface MarketMarkPrice_market {
__typename: "Market";
/**
* decimalPlaces indicates the number of decimal places that an integer must be shifted by in order to get a correct
* number denominated in the currency of the Market. (uint64)
*
* Examples:
* Currency Balance decimalPlaces Real Balance
* GBP 100 0 GBP 100
* GBP 100 2 GBP 1.00
* GBP 100 4 GBP 0.01
* GBP 1 4 GBP 0.0001 ( 0.01p )
*
* GBX (pence) 100 0 GBP 1.00 (100p )
* GBX (pence) 100 2 GBP 0.01 ( 1p )
* GBX (pence) 100 4 GBP 0.0001 ( 0.01p )
* GBX (pence) 1 4 GBP 0.000001 ( 0.0001p)
*/
decimalPlaces: number;
/**
* marketData for the given market
*/
data: MarketMarkPrice_market_data | null;
}
export interface MarketMarkPrice {
/**
* An instrument that is trading on the VEGA network
*/
market: MarketMarkPrice_market | null;
}
export interface MarketMarkPriceVariables {
marketId: string;
}
@@ -0,0 +1,118 @@
/* tslint:disable */
/* eslint-disable */
// @generated
// This file was automatically generated and should not be edited.
import { AccountType } from "@vegaprotocol/types";
// ====================================================
// GraphQL query operation: PartyMarketData
// ====================================================
export interface PartyMarketData_party_accounts_asset {
__typename: "Asset";
/**
* The id of the asset
*/
id: string;
/**
* The precision of the asset
*/
decimals: number;
}
export interface PartyMarketData_party_accounts_market {
__typename: "Market";
/**
* Market ID
*/
id: string;
}
export interface PartyMarketData_party_accounts {
__typename: "Account";
/**
* Account type (General, Margin, etc)
*/
type: AccountType;
/**
* Balance as string - current account balance (approx. as balances can be updated several times per second)
*/
balance: string;
/**
* Asset, the 'currency'
*/
asset: PartyMarketData_party_accounts_asset;
/**
* Market (only relevant to margin accounts)
*/
market: PartyMarketData_party_accounts_market | null;
}
export interface PartyMarketData_party_marginsConnection_edges_node_market {
__typename: "Market";
/**
* Market ID
*/
id: string;
}
export interface PartyMarketData_party_marginsConnection_edges_node {
__typename: "MarginLevels";
/**
* market in which the margin is required for this party
*/
market: PartyMarketData_party_marginsConnection_edges_node_market;
/**
* this is the minimal margin required for a party to place a new order on the network (unsigned int actually)
*/
initialLevel: string;
/**
* minimal margin for the position to be maintained in the network (unsigned int actually)
*/
maintenanceLevel: string;
/**
* if the margin is between maintenance and search, the network will initiate a collateral search (unsigned int actually)
*/
searchLevel: string;
}
export interface PartyMarketData_party_marginsConnection_edges {
__typename: "MarginEdge";
node: PartyMarketData_party_marginsConnection_edges_node;
}
export interface PartyMarketData_party_marginsConnection {
__typename: "MarginConnection";
/**
* The margin levels in this connection
*/
edges: PartyMarketData_party_marginsConnection_edges[] | null;
}
export interface PartyMarketData_party {
__typename: "Party";
/**
* Party identifier
*/
id: string;
/**
* Collateral accounts relating to a party
*/
accounts: PartyMarketData_party_accounts[] | null;
/**
* Margin level for a market
*/
marginsConnection: PartyMarketData_party_marginsConnection;
}
export interface PartyMarketData {
/**
* An entity that is trading on the VEGA network
*/
party: PartyMarketData_party | null;
}
export interface PartyMarketDataVariables {
partyId: string;
}
@@ -3,16 +3,46 @@
// @generated
// This file was automatically generated and should not be edited.
import { AccountType } from "@vegaprotocol/types";
// ====================================================
// GraphQL query operation: MarketPositions
// ====================================================
export interface MarketPositions_party_positionsConnection_edges_node_market_accounts {
export interface MarketPositions_party_accounts_asset {
__typename: "Asset";
/**
* The precision of the asset
*/
decimals: number;
}
export interface MarketPositions_party_accounts_market {
__typename: "Market";
/**
* Market ID
*/
id: string;
}
export interface MarketPositions_party_accounts {
__typename: "Account";
/**
* Account type (General, Margin, etc)
*/
type: AccountType;
/**
* Balance as string - current account balance (approx. as balances can be updated several times per second)
*/
balance: string;
/**
* Asset, the 'currency'
*/
asset: MarketPositions_party_accounts_asset;
/**
* Market (only relevant to margin accounts)
*/
market: MarketPositions_party_accounts_market | null;
}
export interface MarketPositions_party_positionsConnection_edges_node_market {
@@ -21,10 +51,6 @@ export interface MarketPositions_party_positionsConnection_edges_node_market {
* Market ID
*/
id: string;
/**
* Get account for a party or market
*/
accounts: MarketPositions_party_positionsConnection_edges_node_market_accounts[] | null;
}
export interface MarketPositions_party_positionsConnection_edges_node {
@@ -58,6 +84,10 @@ export interface MarketPositions_party {
* Party identifier
*/
id: string;
/**
* Collateral accounts relating to a party
*/
accounts: MarketPositions_party_accounts[] | null;
/**
* Trading positions relating to a party
*/
@@ -0,0 +1,17 @@
import { renderHook, act } from '@testing-library/react';
import useLocalValues from './use-local-values';
describe('local values hook', () => {
it('state of wallet dialog should be properly handled', () => {
const { result } = renderHook(() => useLocalValues());
expect(result.current.vegaWalletDialog).toBeDefined();
expect(result.current.vegaWalletDialog.manage).toBe(false);
expect(result.current.vegaWalletDialog.connect).toBe(false);
act(() => {
result.current.vegaWalletDialog.setConnect(true);
result.current.vegaWalletDialog.setManage(true);
});
expect(result.current.vegaWalletDialog.manage).toBe(true);
expect(result.current.vegaWalletDialog.connect).toBe(true);
});
});
@@ -0,0 +1,15 @@
import { useMemo, useState } from 'react';
import type { LocalValues } from '../context/local-context';
const useLocalValues = () => {
const [connect, setConnect] = useState<boolean>(false);
const [manage, setManage] = useState<boolean>(false);
return useMemo<LocalValues>(
() => ({
vegaWalletDialog: { connect, manage, setConnect, setManage },
}),
[connect, manage, setConnect, setManage]
);
};
export default useLocalValues;
@@ -0,0 +1,38 @@
import { gql, useQuery } from '@apollo/client';
import { useMemo, useRef } from 'react';
import type {
MarketMarkPrice,
MarketMarkPriceVariables,
} from './__generated__/MarketMarkPrice';
const MARKET_MARK_PRICE = gql`
query MarketMarkPrice($marketId: ID!) {
market(id: $marketId) {
decimalPlaces
data {
markPrice
}
}
}
`;
export default (marketId: string) => {
const memoRef = useRef<MarketMarkPrice | null>(null);
const { data } = useQuery<MarketMarkPrice, MarketMarkPriceVariables>(
MARKET_MARK_PRICE,
{
pollInterval: 5000,
variables: { marketId },
skip: !marketId,
}
);
return useMemo(() => {
if (
data &&
data.market?.data?.markPrice !== memoRef.current?.market?.data?.markPrice
) {
memoRef.current = data;
}
return memoRef.current;
}, [data, memoRef]);
};
@@ -0,0 +1,133 @@
import { renderHook } from '@testing-library/react';
import useMarketPositions from './use-market-positions';
let mockNotEmptyData = {
party: {
accounts: [
{
balance: '50001000000',
asset: {
decimals: 5,
},
market: {
id: 'marketId',
},
},
{
balance: '700000000000000000000000000000',
asset: {
decimals: 5,
},
market: {
id: 'someOtherMarketId',
},
},
],
positionsConnection: {
edges: [
{
node: {
openVolume: '100002',
market: {
id: 'marketId',
},
},
},
{
node: {
openVolume: '3',
market: {
id: 'someOtherMarketId',
},
},
},
],
},
},
};
jest.mock('@apollo/client', () => ({
...jest.requireActual('@apollo/client'),
useQuery: jest.fn(() => ({ data: mockNotEmptyData })),
}));
describe('useOrderPosition Hook', () => {
afterEach(() => {
jest.clearAllMocks();
});
it('should return proper positive value', () => {
const { result } = renderHook(() =>
useMarketPositions({ marketId: 'marketId', partyId: 'partyId' })
);
expect(result.current?.openVolume.toNumber()).toEqual(100002);
expect(result.current?.balance.toString()).toEqual('50001000000');
});
it('if balance equal 0 return null', () => {
mockNotEmptyData = {
party: {
accounts: [
{
balance: '0',
asset: {
decimals: 5,
},
market: {
id: 'marketId',
},
},
],
positionsConnection: {
edges: [
{
node: {
openVolume: '2',
market: {
id: 'marketId',
},
},
},
],
},
},
};
const { result } = renderHook(() =>
useMarketPositions({ marketId: 'marketId', partyId: 'partyId' })
);
expect(result.current).toBeNull();
});
it('if no markets return null', () => {
mockNotEmptyData = {
party: {
accounts: [
{
balance: '33330',
asset: {
decimals: 5,
},
market: {
id: 'otherMarketId',
},
},
],
positionsConnection: {
edges: [
{
node: {
openVolume: '2',
market: {
id: 'otherMarketId',
},
},
},
],
},
},
};
const { result } = renderHook(() =>
useMarketPositions({ marketId: 'marketId', partyId: 'partyId' })
);
expect(result.current).toBeNull();
});
});
@@ -9,15 +9,22 @@ const MARKET_POSITIONS_QUERY = gql`
query MarketPositions($partyId: ID!) {
party(id: $partyId) {
id
accounts {
type
balance
asset {
decimals
}
market {
id
}
}
positionsConnection {
edges {
node {
openVolume
market {
id
accounts {
balance
}
}
}
}
@@ -31,40 +38,40 @@ interface Props {
partyId: string;
}
type PositionMargin = {
export type PositionMargin = {
openVolume: BigNumber;
balanceSum: BigNumber;
balance: BigNumber;
balanceDecimals?: number;
} | null;
export default ({ marketId, partyId }: Props): PositionMargin => {
const { data } = useQuery<MarketPositions, MarketPositionsVariables>(
MARKET_POSITIONS_QUERY,
{
pollInterval: 15000,
pollInterval: 5000,
variables: { partyId },
skip: !partyId,
}
);
const markets =
data?.party?.positionsConnection?.edges
?.filter((nodes) => nodes.node.market.id === marketId)
.map((nodes) => nodes.node) || [];
const account = data?.party?.accounts?.find(
(nodes) => nodes.market?.id === marketId
);
return markets.length
? markets.reduce(
(agg, item) => {
const balance = item.market.accounts?.reduce(
(acagg, account) => acagg.plus(account.balance || 0),
new BigNumber(0)
);
if (balance) {
agg.balanceSum = agg.balanceSum.plus(balance);
agg.openVolume = agg.openVolume.plus(item.openVolume);
}
return agg;
},
{ openVolume: new BigNumber(0), balanceSum: new BigNumber(0) }
)
: null;
if (account) {
const balance = new BigNumber(account.balance || 0);
const openVolume = new BigNumber(
data?.party?.positionsConnection?.edges?.find(
(nodes) => nodes.node.market.id === marketId
)?.node.openVolume || 0
);
if (!balance.isZero() && !openVolume.isZero()) {
return {
balance,
balanceDecimals: account?.asset.decimals,
openVolume,
};
}
}
return null;
};
@@ -0,0 +1,117 @@
import { renderHook } from '@testing-library/react';
import useMaximumPositionSize from './use-maximum-position-size';
import type { PartyBalanceQuery_party_accounts } from '../components/deal-ticket/__generated__/PartyBalanceQuery';
import { AccountType } from '@vegaprotocol/types';
import type { PositionMargin } from './use-market-positions';
import { BigNumber } from 'bignumber.js';
import {
VegaWalletOrderSide,
VegaWalletOrderTimeInForce,
VegaWalletOrderType,
} from '@vegaprotocol/wallet';
const defaultMockMarketPositions = {
openVolume: new BigNumber(1),
balance: new BigNumber(100000),
};
let mockMarketPositions: PositionMargin | null = defaultMockMarketPositions;
const mockAccount: PartyBalanceQuery_party_accounts = {
__typename: 'Account',
type: AccountType.General,
balance: '200000',
asset: {
__typename: 'Asset',
id: '5cfa87844724df6069b94e4c8a6f03af21907d7bc251593d08e4251043ee9f7c',
symbol: 'tBTC',
name: 'tBTC TEST',
decimals: 5,
},
};
const mockOrder = {
type: VegaWalletOrderType.Market,
size: '1',
side: VegaWalletOrderSide.Buy,
timeInForce: VegaWalletOrderTimeInForce.IOC,
};
jest.mock('./use-settlement-account', () => {
return {
useSettlementAccount: jest.fn(() => mockAccount),
};
});
jest.mock('./use-market-positions', () => jest.fn(() => mockMarketPositions));
describe('useMaximumPositionSize Hook', () => {
it('should return correct size when no open positions', () => {
mockMarketPositions = null;
const price = '50';
const expected = 4000;
const { result } = renderHook(() =>
useMaximumPositionSize({
marketId: '',
partyId: '',
price,
settlementAssetId: '',
order: mockOrder,
accounts: [mockAccount],
})
);
expect(result.current).toBe(expected);
});
it('should return correct size when open positions and same side', () => {
const price = '50';
mockMarketPositions = defaultMockMarketPositions;
const expected = 3999;
const { result } = renderHook(() =>
useMaximumPositionSize({
marketId: '',
partyId: '',
price,
settlementAssetId: '',
order: mockOrder,
accounts: [mockAccount],
})
);
expect(result.current).toBe(expected);
});
it('should return correct size when open positions and opposite side', () => {
const price = '50';
mockOrder.side = VegaWalletOrderSide.Sell;
mockMarketPositions = defaultMockMarketPositions;
const expected = 4001;
const { result } = renderHook(() =>
useMaximumPositionSize({
marketId: '',
partyId: '',
price,
settlementAssetId: '',
order: mockOrder,
accounts: [mockAccount],
})
);
expect(result.current).toBe(expected);
});
it('should return zero if no account balance', () => {
mockAccount.balance = '0';
const price = '50';
mockMarketPositions = defaultMockMarketPositions;
const expected = 0;
const { result } = renderHook(() =>
useMaximumPositionSize({
marketId: '',
partyId: '',
price,
settlementAssetId: '',
order: mockOrder,
accounts: [],
})
);
expect(result.current).toBe(expected);
});
});
@@ -0,0 +1,61 @@
import useMarketPositions from './use-market-positions';
import type { Order } from '@vegaprotocol/orders';
import type { PartyBalanceQuery_party_accounts } from '../components/deal-ticket/__generated__/PartyBalanceQuery';
import { useSettlementAccount } from './use-settlement-account';
import { AccountType } from '@vegaprotocol/types';
import { VegaWalletOrderSide } from '@vegaprotocol/wallet';
import { BigNumber } from 'bignumber.js';
interface Props {
partyId: string;
accounts: PartyBalanceQuery_party_accounts[];
marketId: string;
price?: string;
settlementAssetId: string;
order: Order;
}
const getSize = (balance: string, price: string) =>
new BigNumber(balance).dividedBy(new BigNumber(price));
export default ({
marketId,
accounts,
partyId,
price,
settlementAssetId,
order,
}: Props): number => {
const settlementAccount = useSettlementAccount(
settlementAssetId,
accounts,
AccountType.General
);
const marketPositions = useMarketPositions({ marketId: marketId, partyId });
if (
!settlementAccount?.balance ||
new BigNumber(settlementAccount?.balance || 0).isZero()
) {
return 0;
}
const size = getSize(settlementAccount.balance, price || '');
if (!marketPositions) {
return size.toNumber() || 0;
}
const isSameSide =
(marketPositions.openVolume.isPositive() &&
order.side === VegaWalletOrderSide.Buy) ||
(marketPositions.openVolume.isNegative() &&
order.side === VegaWalletOrderSide.Sell);
const adjustedForVolume = new BigNumber(size)[isSameSide ? 'minus' : 'plus'](
marketPositions.openVolume
);
return adjustedForVolume.isNegative() ? 0 : adjustedForVolume.toNumber();
};
@@ -0,0 +1,99 @@
import * as React from 'react';
import { renderHook } from '@testing-library/react';
import { MockedProvider } from '@apollo/client/testing';
import useOrderCloseOut from './use-order-closeout';
import type { Order } from '@vegaprotocol/orders';
import type { DealTicketQuery_market } from '@vegaprotocol/deal-ticket';
import type { PartyBalanceQuery } from '../components/deal-ticket/__generated__/PartyBalanceQuery';
jest.mock('@vegaprotocol/wallet', () => ({
...jest.requireActual('@vegaprotocol/wallet'),
useVegaWallet: jest.fn().mockReturnValue('wallet-pub-key'),
}));
describe('useOrderCloseOut Hook', () => {
const order = { size: '2', side: 'SIDE_BUY' };
const market = {
decimalPlaces: 5,
depth: {
lastTrade: {
price: '1000000',
},
},
tradableInstrument: {
instrument: {
product: {
settlementAsset: {
id: 'assetId',
},
},
},
},
};
const partyData = {
party: {
accounts: [
{
balance: '200000',
asset: {
id: 'assetId',
decimals: 5,
},
},
],
},
};
it('return proper buy value', () => {
const { result } = renderHook(
() =>
useOrderCloseOut({
order: order as Order,
market: market as DealTicketQuery_market,
partyData: partyData as PartyBalanceQuery,
}),
{
wrapper: ({ children }: { children: React.ReactNode }) => (
// @ts-ignore different versions of react types in apollo and app
<MockedProvider mocks={[]}>{children}</MockedProvider>
),
}
);
expect(result.current).toEqual(' - ');
});
it('return proper sell value', () => {
const { result } = renderHook(
() =>
useOrderCloseOut({
order: { ...order, side: 'SIDE_SELL' } as Order,
market: market as DealTicketQuery_market,
partyData: partyData as PartyBalanceQuery,
}),
{
wrapper: ({ children }: { children: React.ReactNode }) => (
// @ts-ignore different versions of react types in apollo and app
<MockedProvider mocks={[]}>{children}</MockedProvider>
),
}
);
expect(result.current).toEqual('1.00000');
});
it('return proper empty value', () => {
const { result } = renderHook(
() =>
useOrderCloseOut({
order: { ...order, side: 'SIDE_SELL' } as Order,
market: market as DealTicketQuery_market,
}),
{
wrapper: ({ children }: { children: React.ReactNode }) => (
// @ts-ignore different versions of react types in apollo and app
<MockedProvider mocks={[]}>{children}</MockedProvider>
),
}
);
expect(result.current).toEqual('0.00000');
});
});
@@ -3,8 +3,46 @@ import type { Order } from '@vegaprotocol/orders';
import type { DealTicketQuery_market } from '@vegaprotocol/deal-ticket';
import type { PartyBalanceQuery } from '../components/deal-ticket/__generated__/PartyBalanceQuery';
import { useSettlementAccount } from './use-settlement-account';
import { VegaWalletOrderSide } from '@vegaprotocol/wallet';
import { useVegaWallet, VegaWalletOrderSide } from '@vegaprotocol/wallet';
import { addDecimal, formatNumber } from '@vegaprotocol/react-helpers';
import { gql, useQuery } from '@apollo/client';
import useMarketPositions from './use-market-positions';
import useMarketData from './use-market-data';
import type {
PartyMarketData,
PartyMarketDataVariables,
} from './__generated__/PartyMarketData';
const CLOSEOUT_PRICE_QUERY = gql`
query PartyMarketData($partyId: ID!) {
party(id: $partyId) {
id
accounts {
type
balance
asset {
id
decimals
}
market {
id
}
}
marginsConnection {
edges {
node {
market {
id
}
initialLevel
maintenanceLevel
searchLevel
}
}
}
}
}
`;
interface Props {
order: Order;
@@ -13,25 +51,65 @@ interface Props {
}
const useOrderCloseOut = ({ order, market, partyData }: Props): string => {
const { keypair } = useVegaWallet();
const account = useSettlementAccount(
market.tradableInstrument.instrument.product.settlementAsset.id,
partyData?.party?.accounts || []
);
if (account?.balance && market.depth.lastTrade) {
const price = new BigNumber(
addDecimal(market.depth.lastTrade.price, market.decimalPlaces)
);
const balance = new BigNumber(
addDecimal(account.balance, account.asset.decimals)
);
const { size, side } = order;
const bigOne = new BigNumber(1);
return formatNumber(
side === VegaWalletOrderSide.Buy
? bigOne.minus(balance.div(price.times(size))).times(price)
: bigOne.plus(balance.div(price.times(size))).times(price),
const { data } = useQuery<PartyMarketData, PartyMarketDataVariables>(
CLOSEOUT_PRICE_QUERY,
{
pollInterval: 5000,
variables: { partyId: keypair?.pub || '' },
skip: !keypair?.pub,
}
);
const markPriceData = useMarketData(market.id);
const marketPositions = useMarketPositions({
marketId: market.id,
partyId: keypair?.pub || '',
});
const marginMaintenanceLevel = new BigNumber(
addDecimal(
data?.party?.marginsConnection.edges?.find(
(nodes) => nodes.node.market.id === market.id
)?.node.maintenanceLevel || 0,
market.decimalPlaces
);
)
);
const positionAccount = data?.party?.accounts?.find(
(account) => account.market?.id === market.id
);
const positionAccountBalance = new BigNumber(
addDecimal(
positionAccount?.balance || 0,
positionAccount?.asset?.decimals || 0
)
);
const generalAccountBalance = new BigNumber(
addDecimal(account?.balance || 0, account?.asset.decimals || 0)
);
const volume = new BigNumber(
addDecimal(
marketPositions?.openVolume.toNumber() || 0,
market.positionDecimalPlaces
)
)[order.side === VegaWalletOrderSide.Buy ? 'plus' : 'minus'](order.size);
const markPrice = new BigNumber(
addDecimal(
markPriceData?.market?.data?.markPrice || 0,
markPriceData?.market?.decimalPlaces || 0
)
);
// regarding formula (marginMaintenanceLevel - positionAccountBalance - generalAccountBalance) / volume + markPrice
const marginDifference = marginMaintenanceLevel
.minus(positionAccountBalance)
.minus(generalAccountBalance);
const closeOut = marginDifference.div(volume).plus(markPrice);
if (closeOut.isPositive()) {
return formatNumber(closeOut, market.decimalPlaces);
}
return ' - ';
};
@@ -0,0 +1,106 @@
import { renderHook } from '@testing-library/react';
import { useQuery } from '@apollo/client';
import { BigNumber } from 'bignumber.js';
import type { Order } from '@vegaprotocol/orders';
import type { DealTicketQuery_market } from '@vegaprotocol/deal-ticket';
import type { PositionMargin } from './use-market-positions';
import useOrderMargin from './use-order-margin';
let mockEstimateData = {
estimateOrder: {
marginLevels: {
initialLevel: '200000',
},
},
};
jest.mock('@apollo/client', () => ({
...jest.requireActual('@apollo/client'),
useQuery: jest.fn(() => ({ data: mockEstimateData })),
}));
let mockMarketPositions: PositionMargin = {
openVolume: new BigNumber(1),
balance: new BigNumber(100000),
};
jest.mock('./use-market-positions', () => jest.fn(() => mockMarketPositions));
describe('useOrderMargin Hook', () => {
const order = {
size: '2',
side: 'SIDE_BUY',
timeInForce: 'TIME_IN_FORCE_IOC',
type: 'TYPE_MARKET',
};
const market = {
id: 'marketId',
depth: {
lastTrade: {
price: '1000000',
},
},
};
const partyId = 'partyId';
afterEach(() => {
jest.clearAllMocks();
});
it('margin should be properly calculated', () => {
const { result } = renderHook(() =>
useOrderMargin({
order: order as Order,
market: market as DealTicketQuery_market,
partyId,
})
);
expect(result.current).toEqual('100000');
const calledSize = new BigNumber(mockMarketPositions?.openVolume || 0)
.plus(order.size)
.toString();
expect((useQuery as jest.Mock).mock.calls[1][1].variables.size).toEqual(
calledSize
);
});
it('if there is no positions initialMargin should not be subtracted', () => {
mockMarketPositions = null;
const { result } = renderHook(() =>
useOrderMargin({
order: order as Order,
market: market as DealTicketQuery_market,
partyId,
})
);
expect(result.current).toEqual('200000');
expect((useQuery as jest.Mock).mock.calls[1][1].variables.size).toEqual(
order.size
);
});
it('if api fails, should return empty value', () => {
mockEstimateData = {
estimateOrder: {
marginLevels: {
initialLevel: '',
},
},
};
const { result } = renderHook(() =>
useOrderMargin({
order: order as Order,
market: market as DealTicketQuery_market,
partyId,
})
);
expect(result.current).toEqual(' - ');
const calledSize = new BigNumber(mockMarketPositions?.openVolume || 0)
.plus(order.size)
.toString();
expect((useQuery as jest.Mock).mock.calls[1][1].variables.size).toEqual(
calledSize
);
});
});
@@ -1,3 +1,4 @@
import { BigNumber } from 'bignumber.js';
import type { Order } from '@vegaprotocol/orders';
import { gql, useQuery } from '@apollo/client';
import type {
@@ -11,9 +12,9 @@ import {
VegaWalletOrderTimeInForce,
VegaWalletOrderType,
} from '@vegaprotocol/wallet';
import { addDecimal, formatNumber } from '@vegaprotocol/react-helpers';
import { addDecimal, removeDecimal } from '@vegaprotocol/react-helpers';
import useMarketPositions from './use-market-positions';
import { BigNumber } from 'bignumber.js';
import useMarketData from './use-market-data';
export const ESTIMATE_ORDER_QUERY = gql`
query EstimateOrder(
@@ -66,43 +67,43 @@ const types: Record<VegaWalletOrderType, OrderType> = {
const useOrderMargin = ({ order, market, partyId }: Props) => {
const marketPositions = useMarketPositions({ marketId: market.id, partyId });
console.log('marketPositions', marketPositions);
console.log(
'marketPositions.openVolume',
marketPositions?.openVolume.toNumber()
);
console.log(
'marketPositions.balanceSum',
marketPositions?.balanceSum.toNumber()
);
const markPriceData = useMarketData(market.id);
const { data } = useQuery<EstimateOrder, EstimateOrderVariables>(
ESTIMATE_ORDER_QUERY,
{
variables: {
marketId: market.id,
partyId,
price: market.depth.lastTrade?.price,
size: order.size + (marketPositions?.openVolume.toNumber() || 0),
price: markPriceData?.market?.data?.markPrice || '',
size: removeDecimal(
BigNumber.maximum(
0,
new BigNumber(marketPositions?.openVolume || 0)[
order.side === VegaWalletOrderSide.Buy ? 'plus' : 'minus'
](order.size)
).toString(),
market.positionDecimalPlaces
),
side: order.side === VegaWalletOrderSide.Buy ? Side.Buy : Side.Sell,
timeInForce: times[order.timeInForce],
type: types[order.type],
},
skip: !partyId || !market.id || !order.size,
skip:
!partyId ||
!market.id ||
!order.size ||
!markPriceData?.market?.data?.markPrice,
}
);
if (data?.estimateOrder.marginLevels.initialLevel) {
return formatNumber(
Math.max(
return addDecimal(
BigNumber.maximum(
0,
new BigNumber(
addDecimal(
data.estimateOrder.marginLevels.initialLevel,
market.decimalPlaces
)
new BigNumber(data.estimateOrder.marginLevels.initialLevel).minus(
marketPositions?.balance || 0
)
.minus(marketPositions?.balanceSum.toNumber() || 0)
.toNumber()
),
).toString(),
market.decimalPlaces
);
}
@@ -1,12 +1,14 @@
import { renderHook } from '@testing-library/react-hooks';
import { renderHook } from '@testing-library/react';
import { useSettlementAccount } from './use-settlement-account';
import type { PartyBalanceQuery_party_accounts } from '../components/deal-ticket/__generated__/PartyBalanceQuery';
import { AccountType } from '@vegaprotocol/types';
describe('useSettlementAccount Hook', () => {
it('should filter accounts by settlementAssetId', () => {
const accounts: PartyBalanceQuery_party_accounts[] = [
{
__typename: 'Account',
type: AccountType.General,
balance: '2000000000000000000000',
asset: {
__typename: 'Asset',
@@ -18,6 +20,7 @@ describe('useSettlementAccount Hook', () => {
},
{
__typename: 'Account',
type: AccountType.General,
balance: '1000000000',
asset: {
__typename: 'Asset',
@@ -29,6 +32,19 @@ describe('useSettlementAccount Hook', () => {
},
{
__typename: 'Account',
type: AccountType.General,
balance: '5000000000000000000',
asset: {
__typename: 'Asset',
id: 'fc7fd956078fb1fc9db5c19b88f0874c4299b2a7639ad05a47a28c0aef291b55',
symbol: 'VEGA',
name: 'Vega (testnet)',
decimals: 18,
},
},
{
__typename: 'Account',
type: AccountType.Margin,
balance: '5000000000000000000',
asset: {
__typename: 'Asset',
@@ -39,14 +55,23 @@ describe('useSettlementAccount Hook', () => {
},
},
];
const settlementAssetId =
const tDAI =
'6d9d35f657589e40ddfb448b7ad4a7463b66efb307527fedd2aa7df1bbd5ea61';
const vega =
'fc7fd956078fb1fc9db5c19b88f0874c4299b2a7639ad05a47a28c0aef291b55';
const { result } = renderHook(() =>
useSettlementAccount(settlementAssetId, accounts)
const { result: resultDai } = renderHook(() =>
useSettlementAccount(tDAI, accounts)
);
expect(result.current?.balance).toBe(accounts[1].balance);
expect(result.current?.asset).toEqual(accounts[1].asset);
expect(resultDai.current?.balance).toBe(accounts[1].balance);
expect(resultDai.current?.asset).toEqual(accounts[1].asset);
const { result: resultVega } = renderHook(() =>
useSettlementAccount(vega, accounts, AccountType.Margin)
);
expect(resultVega.current?.balance).toBe(accounts[3].balance);
expect(resultVega.current?.asset).toEqual(accounts[3].asset);
});
it('should return null if no accounts', () => {
@@ -1,12 +1,20 @@
import type { PartyBalanceQuery_party_accounts } from '../components/deal-ticket/__generated__/PartyBalanceQuery';
import type { AccountType } from '@vegaprotocol/types';
import { useMemo } from 'react';
export const useSettlementAccount = (
settlementAssetId: string,
accounts: PartyBalanceQuery_party_accounts[]
accounts: PartyBalanceQuery_party_accounts[],
type?: AccountType
): PartyBalanceQuery_party_accounts | null => {
const callback = () =>
accounts.find((account) => account.asset.id === settlementAssetId);
const account = useMemo(callback, [accounts, settlementAssetId]);
accounts.find((account) => {
if (type) {
return account.asset.id === settlementAssetId && account.type === type;
}
return account.asset.id === settlementAssetId;
});
const account = useMemo(callback, [accounts, settlementAssetId, type]);
return account as PartyBalanceQuery_party_accounts;
};
@@ -23,6 +23,9 @@ export function createClient(base?: string) {
const cache = new InMemoryCache({
typePolicies: {
Party: {
keyFields: false,
},
Query: {},
Account: {
keyFields: false,
+5 -4
View File
@@ -1,14 +1,15 @@
import { StrictMode } from 'react';
import * as ReactDOM from 'react-dom';
import { createRoot } from 'react-dom/client';
import { BrowserRouter } from 'react-router-dom';
import App from './app/app';
const rootElement = document.getElementById('root');
const root = rootElement && createRoot(rootElement);
ReactDOM.render(
root?.render(
<StrictMode>
<BrowserRouter>
<App />
</BrowserRouter>
</StrictMode>,
document.getElementById('root')
</StrictMode>
);
+3 -1
View File
@@ -4,7 +4,9 @@
"outDir": "../../dist/out-tsc",
"module": "commonjs",
"types": ["jest", "node", "@testing-library/jest-dom"],
"jsx": "react"
"jsx": "react",
"allowSyntheticDefaultImports": true,
"esModuleInterop": true
},
"include": [
"**/*.test.ts",
File diff suppressed because it is too large Load Diff
@@ -38,7 +38,7 @@
"tranche_end": "2022-11-26T13:48:10.000Z",
"total_added": "100",
"total_removed": "0",
"locked_amount": "33.170119863013703",
"locked_amount": "29.334677828513445",
"deposits": [
{
"amount": "100",
@@ -242,7 +242,7 @@
"tranche_end": "2022-10-12T00:53:20.000Z",
"total_added": "1100",
"total_removed": "673.04388635",
"locked_amount": "227.633273084728555",
"locked_amount": "185.443410705225717",
"deposits": [
{
"amount": "1000",
+1 -1
View File
@@ -69,7 +69,7 @@
"tranche_end": "2022-10-12T00:53:20.000Z",
"total_added": "1010.000000000000000001",
"total_removed": "668.4622323651",
"locked_amount": "209.00876458650434070020693937087772707",
"locked_amount": "170.27076801116179470016858491882293247",
"deposits": [
{
"amount": "1000",
+8 -6
View File
@@ -1,14 +1,16 @@
import React from 'react';
import ReactDOM from 'react-dom';
import React, { StrictMode } from 'react';
import './styles/styles.css';
import App from './app';
import reportWebVitals from './report-web-vitals';
import { createRoot } from 'react-dom/client';
ReactDOM.render(
<React.StrictMode>
const rootElement = document.getElementById('root');
const root = rootElement && createRoot(rootElement);
root?.render(
<StrictMode>
<App />
</React.StrictMode>,
document.getElementById('root')
</StrictMode>
);
// If you want to start measuring performance in your app, pass a function
-1
View File
@@ -3,7 +3,6 @@ NX_VEGA_ENV=TESTNET
NX_ETHEREUM_PROVIDER_URL=http://localhost:8545
NX_ETHERSCAN_URL=https://ropsten.etherscan.io
NX_FAIRGROUND=false
NX_IS_NEW_BRIDGE_CONTRACT=true
NX_VEGA_NETWORKS='{"DEVNET":"https://dev.token.vega.xyz","STAGNET":"https://dev.token.vega.xyz","STAGNET2":"staging2.token.vega.xyz","TESTNET":"token.fairground.wtf","MAINNET":"token.vega.xyz"}'
NX_VEGA_URL=http://localhost:3028/query
+2
View File
@@ -40,5 +40,7 @@ module.exports = defineConfig({
vegaWalletPublicKeyShort: '02ecea…2f65',
vegaTokenContractAddress: '0xF41bD86d462D36b997C0bbb4D97a0a3382f205B7',
vegaTokenAddress: '0x67175Da1D5e966e40D11c4B2519392B2058373de',
epochTimeout: { timeout: 10000 },
txTimeout: { timeout: 40000 },
},
});
@@ -1,6 +1,7 @@
/// <reference types="cypress" />
const stakeValidatorList = '[data-testid="node-list-item-name"]';
const stakeValidatorWithinList = '[data-testid="node-list-item"]';
const stakeValidatorListTotalStake = '[col-id="totalStakeThisEpoch"]';
const stakeValidatorListTotalShare = '[col-id="share"]';
const stakeValidatorListValidatorStake = '[col-id="validatorStake"]';
const stakeRemoveStakeRadioButton = '[data-testid="remove-stake-radio"]';
const stakeTokenAmountInputBox = '[data-testid="token-amount-input"]';
const stakeTokenSubmitButton = '[data-testid="token-input-submit-button"]';
@@ -8,6 +9,8 @@ const stakeNextEpochValue = '[data-testid="stake-next-epoch"]';
const stakeThisEpochValue = '[data-testid="stake-this-epoch"]';
const stakeAddStakeRadioButton = '[data-testid="add-stake-radio"]';
const stakeMaximumTokens = '[data-testid="token-amount-use-maximum"]';
const totalStake = '[data-testid="total-stake"]';
const stakeShare = '[data-testid="stake-percentage"]';
const vegaWalletPublicKeyShort = Cypress.env('vegaWalletPublicKeyShort');
const vegaWalletAssociatedBalance = '[data-testid="currency-value"]';
const vegaWalletUnstakedBalance =
@@ -22,11 +25,12 @@ const ethWalletAssociatedBalances =
'[data-testid="eth-wallet-associated-balances"]';
const ethWalletTotalAssociatedBalance = '[data-testid="currency-locked"]';
const ethWalletContainer = '[data-testid="ethereum-wallet"]';
const txTimeout = { timeout: 40000 };
const epochTimeout = { timeout: 10000 };
const partValidatorId = '…';
const txTimeout = Cypress.env('txTimeout');
const epochTimeout = Cypress.env('epochTimeout');
// Tests skipped because of change of the UI of data nodes
context.skip('Staking Tab - with eth and vega wallets connected', function () {
context('Staking Tab - with eth and vega wallets connected', function () {
// 1002-STKE-002, 1002-STKE-032
before('visit staking tab and connect vega wallet', function () {
cy.vega_wallet_import();
cy.visit('/');
@@ -38,8 +42,6 @@ context.skip('Staking Tab - with eth and vega wallets connected', function () {
cy.ethereum_wallet_connect();
cy.navigate_to('staking');
cy.wait_for_spinner();
cy.get(stakeValidatorList).first().invoke('text').as('validatorName');
cy.get(stakeValidatorList).last().invoke('text').as('otherValidatorName');
});
describe('Eth wallet - contains VEGA tokens', function () {
@@ -48,9 +50,11 @@ context.skip('Staking Tab - with eth and vega wallets connected', function () {
function () {
cy.vega_wallet_teardown();
cy.navigate_to('staking');
cy.wait_for_spinner();
}
);
// 1002-STKE-004
it('Able to stake against a validator', function () {
cy.staking_page_associate_tokens('3');
@@ -71,13 +75,16 @@ context.skip('Staking Tab - with eth and vega wallets connected', function () {
cy.get('button').contains('Select a validator to nominate').click();
cy.get(stakeValidatorList).contains(this.validatorName).click();
// 1002-STKE-031
cy.click_on_validator_from_list(0);
// 1002-STKE-033, 1002-STKE-034, 1002-STKE-037
cy.staking_validator_page_add_stake('2');
// 1002-STKE-038
cy.get(vegaWalletNextEpochBalances, txTimeout)
.should('contain', 2.0, txTimeout)
.and('contain', this.validatorName)
.and('contain', partValidatorId)
.and('contain', 'Next epoch');
cy.get(vegaWalletUnstakedBalance, txTimeout).should(
@@ -86,30 +93,25 @@ context.skip('Staking Tab - with eth and vega wallets connected', function () {
txTimeout
);
// 1002-STKE-039
cy.get(vegaWalletStakedBalances, txTimeout)
.should('contain', 2.0, txTimeout)
.and('contain', this.validatorName);
.and('contain', partValidatorId);
cy.get(stakeNextEpochValue, epochTimeout)
cy.get(stakeNextEpochValue, epochTimeout) // 1002-STKE-016
.contains(2.0, epochTimeout)
.should('be.visible');
cy.get(stakeThisEpochValue, epochTimeout)
cy.get(stakeThisEpochValue, epochTimeout) // 1002-STKE-013
.contains(2.0, epochTimeout)
.should('be.visible');
cy.navigate_to('staking');
cy.get(stakeValidatorWithinList, epochTimeout)
.contains(this.validatorName)
.parent()
.contains('Total stake')
.parent()
.should('contain', '2.0')
.and('contain', '100%');
cy.validate_validator_list_total_stake_and_share('0', '', '2.00', '100%');
});
it('Able to stake against mulitple validators', function () {
it('Able to stake against multiple validators', function () {
cy.staking_page_associate_tokens('5');
cy.get(vegaWalletUnstakedBalance, txTimeout).should(
@@ -119,30 +121,29 @@ context.skip('Staking Tab - with eth and vega wallets connected', function () {
);
cy.get('button').contains('Select a validator to nominate').click();
cy.get(stakeValidatorList).contains(this.validatorName).click();
cy.click_on_validator_from_list(0);
cy.staking_validator_page_add_stake('2');
cy.get(vegaWalletStakedBalances, txTimeout)
.contains(this.validatorName, txTimeout)
.parent()
.should('contain', 2.0, txTimeout);
cy.navigate_to('staking');
cy.get(stakeValidatorList).contains(this.otherValidatorName).click();
cy.click_on_validator_from_list(1);
cy.staking_validator_page_add_stake('1');
cy.get(vegaWalletStakedBalances, txTimeout)
.should('have.length', 2, txTimeout)
.contains(this.otherValidatorName, txTimeout)
.parent()
.should('contain', 1.0, txTimeout);
.eq(0)
.should('contain', 2.0, txTimeout);
cy.get(vegaWalletStakedBalances, txTimeout)
.contains(this.validatorName, txTimeout)
.parent()
.should('contain', 2.0, txTimeout);
.eq(1)
.should('contain', 1.0, txTimeout);
cy.get(vegaWalletUnstakedBalance, txTimeout).should(
'contain',
@@ -152,23 +153,35 @@ context.skip('Staking Tab - with eth and vega wallets connected', function () {
cy.navigate_to('staking');
cy.get(stakeValidatorWithinList, epochTimeout)
.contains(this.validatorName)
.parent()
.contains('Total stake')
.parent()
.should('contain', '2.0')
.and('contain', '66.67%');
cy.get(`[row-id="${0}"]`).within(() => {
cy.get(stakeValidatorListTotalStake)
.should('have.text', '2.00')
.and('be.visible');
cy.get(stakeValidatorListTotalShare)
.should('have.text', '66.67%')
.and('be.visible');
cy.get(stakeValidatorListValidatorStake)
.scrollIntoView()
.should('have.text', '2.00')
.and('be.visible');
});
cy.get(stakeValidatorWithinList, epochTimeout)
.contains(this.otherValidatorName)
.parent()
.contains('Total stake')
.parent()
.should('contain', '1.0')
.and('contain', '33.33%');
cy.get(`[row-id="${1}"]`).within(() => {
cy.get(stakeValidatorListTotalStake)
.scrollIntoView()
.should('have.text', '1.00')
.and('be.visible');
cy.get(stakeValidatorListTotalShare)
.should('have.text', '33.33%')
.and('be.visible');
cy.get(stakeValidatorListValidatorStake)
.scrollIntoView()
.should('have.text', '1.00')
.and('be.visible');
});
});
// 1002-STKE-041
it('Able to remove part of a stake against a validator', function () {
cy.staking_page_associate_tokens('4');
@@ -179,7 +192,8 @@ context.skip('Staking Tab - with eth and vega wallets connected', function () {
);
cy.get('button').contains('Select a validator to nominate').click();
cy.get(stakeValidatorList).contains(this.validatorName).click();
cy.click_on_validator_from_list(0);
cy.staking_validator_page_add_stake('3');
@@ -187,10 +201,11 @@ context.skip('Staking Tab - with eth and vega wallets connected', function () {
.contains(3.0, epochTimeout)
.should('be.visible');
cy.get(vegaWalletNextEpochBalances, txTimeout)
.should('contain', 3.0, txTimeout)
.and('contain', this.validatorName)
.and('contain', 'Next epoch');
cy.get(vegaWalletNextEpochBalances, txTimeout).should(
'contain',
3.0,
txTimeout
);
cy.get(vegaWalletUnstakedBalance, txTimeout).should(
'contain',
@@ -199,22 +214,24 @@ context.skip('Staking Tab - with eth and vega wallets connected', function () {
);
cy.navigate_to('staking');
cy.get(stakeValidatorList).contains(this.validatorName).click();
// 1002-STKE-040
cy.click_on_validator_from_list(0);
// 1002-STKE-044, 1002-STKE-048
cy.staking_validator_page_remove_stake('1');
cy.get(stakeNextEpochValue, epochTimeout)
.contains(2.0, epochTimeout)
.should('be.visible');
// 1002-STKE-049
cy.get(stakeNextEpochValue, epochTimeout).contains(2.0, epochTimeout);
cy.get(vegaWalletNextEpochBalances, txTimeout)
.should('contain', 2.0, txTimeout)
.and('contain', this.validatorName)
.and('contain', 'Next epoch');
cy.get(vegaWalletNextEpochBalances, txTimeout).should(
'contain',
2.0,
txTimeout
);
cy.get(vegaWalletThisEpochBalances, txTimeout)
.should('contain', 3.0, txTimeout)
.and('contain', this.validatorName)
.and('contain', partValidatorId)
.and('contain', 'This Epoch');
cy.get(vegaWalletUnstakedBalance, txTimeout).should(
@@ -223,9 +240,11 @@ context.skip('Staking Tab - with eth and vega wallets connected', function () {
txTimeout
);
cy.get(vegaWalletStakedBalances, txTimeout)
.should('contain', 2.0, txTimeout)
.and('contain', this.validatorName);
cy.get(vegaWalletStakedBalances, txTimeout).should(
'contain',
2.0,
txTimeout
);
cy.get(stakeNextEpochValue, epochTimeout)
.contains(2.0, epochTimeout)
@@ -235,15 +254,13 @@ context.skip('Staking Tab - with eth and vega wallets connected', function () {
.contains(2.0, epochTimeout)
.should('be.visible');
cy.get(totalStake).should('have.text', '2');
cy.get(stakeShare).should('have.text', '100%');
cy.navigate_to('staking');
cy.get(stakeValidatorWithinList, epochTimeout)
.contains(this.validatorName)
.parent()
.contains('Total stake')
.parent()
.should('contain', '2.0')
.and('contain', '100%');
cy.validate_validator_list_total_stake_and_share('0', '', '2.00', '100%');
});
it('Able to remove a full stake against a validator', function () {
@@ -257,14 +274,15 @@ context.skip('Staking Tab - with eth and vega wallets connected', function () {
cy.get('button').contains('Select a validator to nominate').click();
cy.get(stakeValidatorList).contains(this.validatorName).click();
cy.click_on_validator_from_list(0);
cy.staking_validator_page_add_stake('1');
cy.get(vegaWalletNextEpochBalances, txTimeout)
.should('contain', 1.0, txTimeout)
.and('contain', this.validatorName)
.and('contain', 'Next epoch');
cy.get(vegaWalletNextEpochBalances, txTimeout).should(
'contain',
1.0,
txTimeout
);
cy.get(vegaWalletUnstakedBalance, txTimeout).should(
'contain',
@@ -274,7 +292,7 @@ context.skip('Staking Tab - with eth and vega wallets connected', function () {
cy.navigate_to('staking');
cy.get(stakeValidatorList).contains(this.validatorName).click();
cy.click_on_validator_from_list('0');
cy.staking_validator_page_remove_stake('1');
@@ -282,15 +300,17 @@ context.skip('Staking Tab - with eth and vega wallets connected', function () {
.contains(0.0, epochTimeout)
.should('be.visible');
cy.get(vegaWalletThisEpochBalances, txTimeout)
.should('contain', 1.0, txTimeout)
.and('contain', this.validatorName)
.and('contain', 'This Epoch');
cy.get(vegaWalletThisEpochBalances, txTimeout).should(
'contain',
1.0,
txTimeout
);
cy.get(vegaWalletNextEpochBalances, txTimeout)
.should('contain', 0.0, txTimeout)
.and('contain', this.validatorName)
.and('contain', 'Next epoch');
cy.get(vegaWalletNextEpochBalances, txTimeout).should(
'contain',
0.0,
txTimeout
);
cy.get(vegaWalletUnstakedBalance, txTimeout).should(
'contain',
@@ -307,17 +327,12 @@ context.skip('Staking Tab - with eth and vega wallets connected', function () {
.should('be.visible');
cy.get(vegaWalletStakedBalances, txTimeout)
.contains(this.validatorName, txTimeout)
.contains(partValidatorId, txTimeout)
.should('not.exist', txTimeout);
cy.navigate_to('staking');
cy.get(stakeValidatorWithinList, epochTimeout)
.contains(this.validatorName)
.parent()
.contains('Total stake')
.parent()
.should('contain', '0.0');
cy.validate_validator_list_total_stake_and_share('0', '', '0.00', '-');
});
it('Unable to remove a stake with a negative value for a validator', function () {
@@ -331,7 +346,7 @@ context.skip('Staking Tab - with eth and vega wallets connected', function () {
cy.get('button').contains('Select a validator to nominate').click();
cy.get(stakeValidatorList).contains(this.validatorName).click();
cy.click_on_validator_from_list(0);
cy.staking_validator_page_add_stake('2');
@@ -339,10 +354,11 @@ context.skip('Staking Tab - with eth and vega wallets connected', function () {
.contains(2.0, epochTimeout)
.should('be.visible');
cy.get(vegaWalletNextEpochBalances, txTimeout)
.should('contain', 2.0, txTimeout)
.and('contain', this.validatorName)
.and('contain', 'Next epoch');
cy.get(vegaWalletNextEpochBalances, txTimeout).should(
'contain',
2.0,
txTimeout
);
cy.get(vegaWalletUnstakedBalance, txTimeout).should(
'contain',
@@ -352,9 +368,9 @@ context.skip('Staking Tab - with eth and vega wallets connected', function () {
cy.navigate_to('staking');
cy.get(stakeValidatorList).contains(this.validatorName).click();
cy.click_on_validator_from_list(0);
cy.get(stakeRemoveStakeRadioButton).click({ force: true });
cy.get(stakeRemoveStakeRadioButton, txTimeout).click();
cy.get(stakeTokenAmountInputBox).type('-0.1');
@@ -377,7 +393,7 @@ context.skip('Staking Tab - with eth and vega wallets connected', function () {
cy.get('button').contains('Select a validator to nominate').click();
cy.get(stakeValidatorList).contains(this.validatorName).click();
cy.click_on_validator_from_list(0);
cy.staking_validator_page_add_stake('2');
@@ -387,8 +403,7 @@ context.skip('Staking Tab - with eth and vega wallets connected', function () {
cy.get(vegaWalletNextEpochBalances, txTimeout)
.should('contain', 2.0, txTimeout)
.and('contain', this.validatorName)
.and('contain', 'Next epoch');
.and('contain', partValidatorId);
cy.get(vegaWalletUnstakedBalance, txTimeout).should(
'contain',
@@ -398,9 +413,9 @@ context.skip('Staking Tab - with eth and vega wallets connected', function () {
cy.navigate_to('staking');
cy.get(stakeValidatorList).contains(this.validatorName).click();
cy.click_on_validator_from_list(0);
cy.get(stakeRemoveStakeRadioButton).click({ force: true });
cy.get(stakeRemoveStakeRadioButton).click();
cy.get(stakeTokenAmountInputBox).type(4);
@@ -412,7 +427,7 @@ context.skip('Staking Tab - with eth and vega wallets connected', function () {
.and('be.visible');
});
it.skip('Disassociating all tokens max - removes all staked tokens', function () {
it('Disassociating all tokens max - removes all staked tokens', function () {
cy.staking_page_associate_tokens('3');
cy.get(vegaWalletUnstakedBalance, txTimeout).should(
@@ -423,7 +438,7 @@ context.skip('Staking Tab - with eth and vega wallets connected', function () {
cy.get('button').contains('Select a validator to nominate').click();
cy.get(stakeValidatorList).contains(this.validatorName).click();
cy.click_on_validator_from_list('1');
cy.staking_validator_page_add_stake('2');
@@ -433,9 +448,11 @@ context.skip('Staking Tab - with eth and vega wallets connected', function () {
txTimeout
);
cy.get(vegaWalletStakedBalances, txTimeout)
.should('contain', 2.0, txTimeout)
.and('contain', this.validatorName);
cy.get(vegaWalletStakedBalances, txTimeout).should(
'contain',
2.0,
txTimeout
);
cy.navigate_to('staking');
@@ -457,18 +474,14 @@ context.skip('Staking Tab - with eth and vega wallets connected', function () {
txTimeout
);
cy.get(vegaWalletStakedBalances, txTimeout)
.contains(this.validatorName, txTimeout)
.should('not.exist', txTimeout);
cy.get(vegaWalletStakedBalances, txTimeout).should(
'not.exist',
txTimeout
);
cy.navigate_to('staking');
cy.get(stakeValidatorWithinList, epochTimeout)
.contains(this.validatorName)
.parent()
.contains('Total stake')
.parent()
.should('contain', '0.0');
cy.validate_validator_list_total_stake_and_share('0', '', '0.00', '-');
});
it('Disassociating some tokens - prioritizes unstaked tokens', function () {
@@ -481,7 +494,7 @@ context.skip('Staking Tab - with eth and vega wallets connected', function () {
);
cy.get('button').contains('Select a validator to nominate').click();
cy.get(stakeValidatorList).contains(this.validatorName).click();
cy.click_on_validator_from_list(0);
cy.staking_validator_page_add_stake('2');
cy.get(vegaWalletUnstakedBalance, txTimeout).should(
@@ -490,9 +503,11 @@ context.skip('Staking Tab - with eth and vega wallets connected', function () {
txTimeout
);
cy.get(vegaWalletStakedBalances, txTimeout)
.should('contain', 2.0, txTimeout)
.and('contain', this.validatorName);
cy.get(vegaWalletStakedBalances, txTimeout).should(
'contain',
2.0,
txTimeout
);
cy.navigate_to('staking');
@@ -510,20 +525,14 @@ context.skip('Staking Tab - with eth and vega wallets connected', function () {
cy.get(vegaWalletStakedBalances, txTimeout)
.should('contain', 2.0, txTimeout)
.and('contain', this.validatorName);
.and('contain', partValidatorId);
cy.navigate_to('staking');
cy.get(stakeValidatorWithinList, epochTimeout)
.contains(this.validatorName)
.parent()
.contains('Total stake')
.parent()
.should('contain', '2.0')
.and('contain', '100%');
cy.validate_validator_list_total_stake_and_share('0', '', '2.00', '100%');
});
it('Selecting use maximum where tokens are allready staked - suggests the unstaked token amount', function () {
it('Selecting use maximum where tokens are already staked - suggests the unstaked token amount', function () {
cy.staking_page_associate_tokens('3');
cy.get(vegaWalletUnstakedBalance, txTimeout).should(
@@ -533,7 +542,7 @@ context.skip('Staking Tab - with eth and vega wallets connected', function () {
);
cy.get('button').contains('Select a validator to nominate').click();
cy.get(stakeValidatorList).contains(this.validatorName).click();
cy.click_on_validator_from_list(0);
cy.staking_validator_page_add_stake('2');
@@ -545,9 +554,9 @@ context.skip('Staking Tab - with eth and vega wallets connected', function () {
cy.navigate_to('staking');
cy.get(stakeValidatorList).contains(this.otherValidatorName).click();
cy.click_on_validator_from_list(0);
cy.get(stakeAddStakeRadioButton).click({ force: true });
cy.get(stakeAddStakeRadioButton).click();
cy.get(stakeMaximumTokens, { timeout: 60000 }).click();
@@ -1,5 +1,18 @@
/// <reference types="cypress" />
const guideLink = '[data-testid="staking-guide-link"]';
const validators = '[data-testid="validators-grid"]';
const validatorTitle = '[data-testid="validator-node-title"]';
const validatorId = '[data-testid="validator-id"]';
const validatorPubKey = '[data-testid="validator-public-key"]';
const ethAddressLink = '[data-testid="link"]';
const totalStake = '[data-testid="total-stake"]';
const pendingStake = '[data-testid="pending-stake"]';
const stakedByOperator = '[data-testid="staked-by-operator"]';
const stakedByDelegates = '[data-testid="staked-by-delegates"]';
const stakeShare = '[data-testid="stake-percentage"]';
const epochCountDown = '[data-testid="epoch-countdown"]';
const stakeNumberRegex = /^\d*\.?\d*$/;
const ownStake = '[data-testid="own-stake"]';
const nominatedStake = '[data-testid="nominated-stake"]';
context('Staking Page - verify elements on page', function () {
before('navigate to staking page', function () {
@@ -17,6 +30,7 @@ context('Staking Page - verify elements on page', function () {
});
it('should have Staking Guide link visible', function () {
// 1002-STKE-003
cy.get(guideLink)
.should('be.visible')
.and('have.text', 'Read more about staking on Vega')
@@ -27,9 +41,168 @@ context('Staking Page - verify elements on page', function () {
);
});
});
describe('validators section', function () {
it('should be visible', function () {
cy.get(validators).should('be.visible');
describe('Should be able to see validator list from the staking page', function () {
// 1002-STKE-050
it('Should be able to see validator names', function () {
cy.get('[col-id="validator"]')
.should('have.length.at.least', 1)
.each(($name) => {
cy.wrap($name).should('not.be.empty');
});
});
it('Should be able to see validator status', function () {
cy.get('[col-id="status"]')
.should('have.length.at.least', 1)
.each(($status) => {
cy.wrap($status).should('not.be.empty');
});
});
it('Should be able to see total stake for this epoch', function () {
cy.get('[col-id="totalStakeThisEpoch"]')
.should('have.length.at.least', 1)
.each(($totalStaked) => {
cy.wrap($totalStaked).should('not.be.empty');
});
});
it('Should be able to see validator staked for this epoch', function () {
cy.get('[col-id="validatorStake"]')
.should('have.length.at.least', 1)
.each(($validatorStake) => {
cy.wrap($validatorStake).should('not.be.empty');
});
});
it('Should be able to see validator staked for next epoch', function () {
cy.get('[col-id="pendingStake"]')
.should('have.length.at.least', 1)
.each(($pendingStake) => {
cy.wrap($pendingStake).should('not.be.empty');
});
});
// 1002-STKE-021
it('Should be able to see validator ranking score', function () {
cy.get('.ag-body-horizontal-scroll-viewport').scrollTo('right');
cy.get('[col-id="rankingScore"]')
.should('have.length.at.least', 1)
.each(($rankingScore) => {
cy.wrap($rankingScore).should('not.be.empty');
});
});
// 1002-STKE-022
it('Should be able to see validator stake score', function () {
cy.get('[col-id="stakeScore"]')
.should('have.length.at.least', 1)
.each(($stakeScore) => {
cy.wrap($stakeScore).should('not.be.empty');
});
});
// 1002-STKE-023
it('Should be able to see validator performance score', function () {
cy.get('[col-id="performanceScore"]')
.should('have.length.at.least', 1)
.each(($performanceScore) => {
cy.wrap($performanceScore).should('not.be.empty');
});
});
// 1002-STKE-024
it('Should be able to see validator voting power score', function () {
cy.get('[col-id="votingPower"]')
.should('have.length.at.least', 1)
.each(($votingPower) => {
cy.wrap($votingPower).should('not.be.empty');
});
});
});
});
// 1002-STKE-050
describe('Should be able to see static information about a validator', function () {
before('connect wallets and click on validator', function () {
cy.vega_wallet_import();
cy.vega_wallet_connect();
cy.click_on_validator_from_list(0);
});
// 1002-STKE-005
it('Should be able to see validator name', function () {
cy.get(validatorTitle).should('not.be.empty');
});
// 1002-STKE-007
it('Should be able to see validator id', function () {
cy.get(validatorId).should('not.be.empty');
});
// 1002-STKE-008
it('Should be able to see validator public key', function () {
cy.get(validatorPubKey).should('not.be.empty');
});
// 1002-STKE-010
it('Should be able to see Ethereum address', function () {
cy.get(ethAddressLink).should('not.be.empty').and('have.attr', 'href');
});
// TODO validators missing url for more information about them 1002-STKE-09
// 1002-STKE-012
it('Should be able to see total stake', function () {
cy.get(totalStake).invoke('text').should('match', stakeNumberRegex);
});
it('Should be able to see pending stake', function () {
cy.get(pendingStake).invoke('text').should('match', stakeNumberRegex);
});
it('Should be able to see staked by operator', function () {
cy.get(stakedByOperator).invoke('text').should('match', stakeNumberRegex);
});
it('Should be able to see staked by delegates', function () {
cy.get(stakedByDelegates)
.invoke('text')
.should('match', stakeNumberRegex);
});
// 1002-STKE-051
it('Should be able to see stake share in percentage', function () {
cy.get(stakeShare)
.invoke('text')
.then(($stakePercentage) => {
if ($stakePercentage != '-') {
cy.wrap($stakePercentage).should(
'match',
/\b(?<!\.)(?!0+(?:\.0+)?%)(?:\d|[1-9]\d|100)(?:(?<!100)\.\d+)?%/
);
}
});
});
// 1002-STKE-013
it('Should be able to see own stake this epoch', function () {
cy.get(ownStake).invoke('text').should('match', stakeNumberRegex);
});
// 1002-STKE-014
it('Should be able to see nominated stake this epoch', function () {
cy.get(nominatedStake).invoke('text').should('match', stakeNumberRegex);
});
// 1002-STKE-011
it('should be able to see epoch information', function () {
const epochTitle = 'h3';
const nextEpochInfo = 'p';
cy.get(epochCountDown).within(() => {
cy.get(epochTitle).should('not.be.empty');
cy.get(nextEpochInfo).should('contain.text', 'Next epoch');
});
});
});
@@ -14,7 +14,7 @@ const navigation = {
rewards: '[href="/rewards"]',
withdraw: '[href="/withdraw"]',
governance: '[href="/governance"]',
pageSpinner: 'splash-loader',
pageSpinner: '[data-testid="splash-loader"]',
};
Cypress.Commands.add('navigate_to', (page) => {
@@ -7,6 +7,11 @@ const ethWalletAssociateButton = '[href="/staking/associate"]';
const ethWalletDissociateButton = '[href="/staking/disassociate"]';
const associateWalletRadioButton = '[data-testid="associate-radio-wallet"]';
const stakeMaximumTokens = '[data-testid="token-amount-use-maximum"]';
const stakeValidatorListPendingStake = '[col-id="pendingStake"]';
const stakeValidatorListTotalStake = '[col-id="totalStakeThisEpoch"]';
const stakeValidatorListTotalShare = '[col-id="share"]';
const stakeValidatorListName = '[col-id="validator"]';
const txTimeout = Cypress.env('txTimeout');
Cypress.Commands.add('wait_for_begining_of_epoch', () => {
cy.contains('Waiting for next epoch to start', { timeout: 10000 }).should(
@@ -17,7 +22,7 @@ Cypress.Commands.add('wait_for_begining_of_epoch', () => {
Cypress.Commands.add('staking_validator_page_add_stake', (stake) => {
cy.highlight(`Adding a stake of ${stake}`);
cy.get(addStakeRadioButton).click({ force: true });
cy.get(addStakeRadioButton, { timeout: 8000 }).click({ force: true });
cy.get(tokenAmountInputBox).type(stake);
cy.wait_for_begining_of_epoch();
cy.get(tokenSubmitButton, { timeout: 8000 })
@@ -29,7 +34,7 @@ Cypress.Commands.add('staking_validator_page_add_stake', (stake) => {
Cypress.Commands.add('staking_validator_page_remove_stake', (stake) => {
cy.highlight(`Removing a stake of ${stake}`);
cy.get(removeStakeRadioButton).click({ force: true });
cy.get(removeStakeRadioButton, { timeout: 8000 }).click();
cy.get(tokenAmountInputBox).type(stake);
cy.wait_for_begining_of_epoch();
cy.get(tokenSubmitButton)
@@ -47,9 +52,7 @@ Cypress.Commands.add(
cy.get(associateWalletRadioButton, { timeout: 30000 }).click();
cy.get(tokenAmountInputBox, { timeout: 10000 }).type(amount);
if (approve) {
cy.get(tokenInputApprove, { timeout: 40000 })
.should('be.enabled')
.click();
cy.get(tokenInputApprove, txTimeout).should('be.enabled').click();
cy.contains('Approve $VEGA Tokens for staking on Vega').should(
'be.visible'
);
@@ -57,7 +60,7 @@ Cypress.Commands.add(
timeout: 40000,
}).should('not.exist');
}
cy.get(tokenSubmitButton, { timeout: 40000 }).should('be.enabled').click();
cy.get(tokenSubmitButton, txTimeout).should('be.enabled').click();
cy.contains('can now participate in governance and nominate a validator', {
timeout: 60000,
}).should('be.visible');
@@ -70,7 +73,7 @@ Cypress.Commands.add('staking_page_disassociate_tokens', (amount) => {
cy.get(associateWalletRadioButton, { timeout: 30000 }).click();
cy.get(tokenAmountInputBox, { timeout: 10000 }).type(amount);
cy.get(tokenSubmitButton, { timeout: 40000 }).should('be.enabled').click();
cy.get(tokenSubmitButton, txTimeout).should('be.enabled').click();
cy.contains(`${amount} $VEGA tokens have been returned to Ethereum wallet`, {
timeout: 60000,
}).should('be.visible');
@@ -86,3 +89,45 @@ Cypress.Commands.add('staking_page_disassociate_all_tokens', () => {
timeout: 60000,
}).should('be.visible');
});
Cypress.Commands.add(
'click_on_validator_from_list',
(validatorNumber, validatorName = null) => {
cy.contains('Waiting for next epoch to start').should('not.exist');
// below is to ensure validator list is shown
cy.get(stakeValidatorListName, { timeout: 10000 }).should('exist');
cy.get(stakeValidatorListPendingStake, txTimeout).should(
'not.contain',
'2,000,000,000,000,000,000.00' // number due to bug #936
);
if (validatorName) {
cy.contains(validatorName).click();
} else {
cy.get(`[row-id="${validatorNumber}"]`)
.find(stakeValidatorListName)
.click();
}
}
);
Cypress.Commands.add(
'validate_validator_list_total_stake_and_share',
(
positionOnList,
expectedValidatorName,
expectedTotalStake,
expectedTotalShare
) => {
cy.get(`[row-id="${positionOnList}"]`).within(() => {
cy.get(stakeValidatorListName).should('have.text', expectedValidatorName);
cy.get(stakeValidatorListTotalStake).should(
'have.text',
expectedTotalStake
);
cy.get(stakeValidatorListTotalShare).should(
'have.text',
expectedTotalShare
);
});
}
);
@@ -5,6 +5,7 @@ import {
} from '@vegaprotocol/smart-contracts';
import { ethers, Wallet } from 'ethers';
const vegaWalletContainer = '[data-testid="vega-wallet"]';
const vegaWalletAssociatedBalance = '[data-testid="currency-value"]';
const vegaWalletMnemonic = Cypress.env('vegaWalletMnemonic');
const vegaWalletPubKey = Cypress.env('vegaWalletPublicKey');
@@ -40,14 +41,24 @@ before('Vega wallet teardown prep', function () {
});
Cypress.Commands.add('vega_wallet_teardown', function () {
cy.vega_wallet_teardown_staking(this.stakingBridgeContract);
cy.vega_wallet_teardown_vesting(this.vestingContract);
cy.get(vegaWalletContainer).within(() => {
cy.get(vegaWalletAssociatedBalance)
.invoke('text')
.then((balance) => {
if (balance != '0.000000000000000000') {
cy.vega_wallet_teardown_staking(this.stakingBridgeContract);
cy.vega_wallet_teardown_vesting(this.vestingContract);
}
});
});
cy.get(vegaWalletAssociatedBalance, { timeout: transactionTimeout }).should(
'contain',
'0.000000000000000000',
{ timeout: transactionTimeout }
);
cy.get(vegaWalletContainer).within(() => {
cy.get(vegaWalletAssociatedBalance, { timeout: transactionTimeout }).should(
'contain',
'0.000000000000000000',
{ timeout: transactionTimeout }
);
});
});
Cypress.Commands.add(
-1
View File
@@ -5,7 +5,6 @@ NX_VEGA_URL=https://lb.testnet.vega.xyz/query
NX_ETHEREUM_PROVIDER_URL=https://ropsten.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8
NX_ETHERSCAN_URL=https://ropsten.etherscan.io
NX_FAIRGROUND=false
NX_IS_NEW_BRIDGE_CONTRACT=true
NX_VEGA_NETWORKS='{"DEVNET":"https://dev.token.vega.xyz","STAGNET":"https://dev.token.vega.xyz","STAGNET2":"staging2.token.vega.xyz","TESTNET":"token.fairground.wtf","MAINNET":"token.vega.xyz"}'
NX_GITHUB_FEEDBACK_URL=https://github.com/vegaprotocol/feedback/discussions
NX_VEGA_WALLET_URL=http://localhost:1789/api/v1
-1
View File
@@ -3,7 +3,6 @@ NX_VEGA_ENV=TESTNET
NX_ETHEREUM_PROVIDER_URL=http://localhost:8545
NX_ETHERSCAN_URL=https://ropsten.etherscan.io
NX_FAIRGROUND=false
NX_IS_NEW_BRIDGE_CONTRACT=true
NX_VEGA_NETWORKS='{"DEVNET":"https://dev.token.vega.xyz","STAGNET":"https://dev.token.vega.xyz","STAGNET2":"staging2.token.vega.xyz","TESTNET":"token.fairground.wtf","MAINNET":"token.vega.xyz"}'
NX_GITHUB_FEEDBACK_URL=https://github.com/vegaprotocol/feedback/discussions
+3
View File
@@ -8,4 +8,7 @@ module.exports = {
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'],
coverageDirectory: '../../coverage/apps/token',
setupFilesAfterEnv: ['./src/setup-tests.ts'],
moduleNameMapper: {
'^d3-(.*)$': `d3-$1/dist/d3-$1`,
},
};
@@ -13,7 +13,7 @@ import vegaVesting from '../../images/vega_vesting.png';
import vegaWhite from '../../images/vega_white.png';
import { BigNumber } from '../../lib/bignumber';
import { truncateMiddle } from '../../lib/truncate-middle';
import { Routes } from '../../routes/router-config';
import Routes from '../../routes/routes';
import { LockedProgress } from '../locked-progress';
import {
WalletCard,
+10 -3
View File
@@ -1,12 +1,19 @@
export interface HeadingProps {
import classNames from 'classnames';
interface HeadingProps {
title?: string;
centerContent?: boolean;
}
export const Heading = ({ title }: HeadingProps) => {
export const Heading = ({ title, centerContent = true }: HeadingProps) => {
if (!title) return null;
return (
<header className="my-0 mx-auto">
<header
className={classNames('my-0', {
'mx-auto': centerContent,
})}
>
<h1 className="font-alpha calt">{title}</h1>
</header>
);
+1 -1
View File
@@ -13,7 +13,7 @@ import {
AppStateActionType,
useAppState,
} from '../../contexts/app-state/app-state-context';
import { Routes } from '../../routes/router-config';
import Routes from '../../routes/routes';
import { EthWallet } from '../eth-wallet';
import { VegaWallet } from '../vega-wallet';
@@ -25,7 +25,7 @@ export const SplashLoader = ({ text = 'Loading' }: { text?: string }) => {
);
})}
</div>
<div>{text}</div>
<div className="text-white">{text}</div>
</div>
);
};
@@ -9,7 +9,7 @@ import {
import vegaWhite from '../../images/vega_white.png';
import { BigNumber } from '../../lib/bignumber';
import { truncateMiddle } from '../../lib/truncate-middle';
import { Routes } from '../../routes/router-config';
import Routes from '../../routes/routes';
import { BulletHeader } from '../bullet-header';
import type { WalletCardAssetProps } from '../wallet-card';
import {
@@ -62,6 +62,9 @@ export const Web3Content = ({ children, appChainId }: Web3ContentProps) => {
if (connector?.connectEagerly) {
connector.connectEagerly();
}
// wallet connect doesnt handle connectEagerly being called when connector is also in the
// deps array.
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [connector]);
if (error) {
-3
View File
@@ -66,9 +66,6 @@ export const ENV = {
MOCK: TRUTHY.includes(windowOrDefault('NX_MOCKED')),
FAIRGROUND: TRUTHY.includes(windowOrDefault('NX_FAIRGROUND')),
NETWORK_LIMITS: TRUTHY.includes(windowOrDefault('NX_NETWORK_LIMITS')),
USE_NEW_BRIDGE_CONTRACT: TRUTHY.includes(
process.env['NX_IS_NEW_BRIDGE_CONTRACT'] as string
),
},
addresses:
ContractAddresses[(envName === 'local' ? 'CUSTOM' : envName) as Networks],
+4
View File
@@ -17,5 +17,9 @@ export const NetworkParams = {
'governance.proposal.updateNetParam.requiredMajority',
GOV_UPDATE_NET_PARAM_REQUIRED_PARTICIPATION:
'governance.proposal.updateNetParam.requiredParticipation',
GOV_FREEFORM_REQUIRED_PARTICIPATION:
'governance.proposal.freeform.requiredParticipation',
GOV_FREEFORM_REQUIRED_MAJORITY:
'governance.proposal.freeform.requiredMajority',
VALIDATOR_DELEGATION_MIN_AMOUNT: 'validators.delegation.minAmount',
};
@@ -30,6 +30,7 @@ export const ContractsProvider = ({ children }: { children: JSX.Element }) => {
// contracts so that we can sign transactions, otherwise use the provider for just
// reading data
React.useEffect(() => {
let cancelled = false;
const run = async () => {
let signer = null;
@@ -38,7 +39,6 @@ export const ContractsProvider = ({ children }: { children: JSX.Element }) => {
ETHEREUM_PROVIDER_URL,
Number(config.chain_id)
);
const provider = activeProvider ? activeProvider : defaultProvider;
if (
@@ -55,23 +55,28 @@ export const ContractsProvider = ({ children }: { children: JSX.Element }) => {
signer || provider
);
const vegaAddress = await staking.staking_token();
setContracts({
token: new Token(vegaAddress, signer || provider),
staking: new StakingBridge(
config.staking_bridge_contract.address,
signer || provider
),
vesting: new TokenVesting(
config.token_vesting_contract.address,
signer || provider
),
claim: new Claim(ENV.addresses.claimAddress, signer || provider),
});
if (!cancelled) {
setContracts({
token: new Token(vegaAddress, signer || provider),
staking: new StakingBridge(
config.staking_bridge_contract.address,
signer || provider
),
vesting: new TokenVesting(
config.token_vesting_contract.address,
signer || provider
),
claim: new Claim(ENV.addresses.claimAddress, signer || provider),
});
}
}
}
};
run();
return () => {
// TODO: hacky quick fix for release to prevent race condition, find a better fix for this.
cancelled = true;
};
}, [activeProvider, account, config, VEGA_ENV, ETHEREUM_PROVIDER_URL]);
if (!contracts) {
+7 -1
View File
@@ -157,6 +157,9 @@
"Once unlocked they can be redeemed from the contract so that you can transfer them between wallets.": "Once unlocked they can be redeemed from the contract so that you can transfer them between wallets.",
"Tokens are held in different <trancheLink>Tranches</trancheLink>. Each tranche has its own schedule for how the tokens are unlocked.": "Tokens are held in different <trancheLink>Tranches</trancheLink>. Each tranche has its own schedule for how the tokens are unlocked.",
"proposals": "Proposals",
"proposal": "Proposal",
"submitting": "Submitting",
"submit": "Submit",
"proposedEnactment": "Proposed enactment",
"Enacted": "Enacted",
"enactedOn": "Enacted on",
@@ -620,5 +623,8 @@
"InvalidAssetDetails": "Invalid asset details",
"FilterProposals": "Filter proposals",
"FilterProposalsDescription": "Filter by proposal ID or proposer ID",
"Freeform proposal": "Freeform proposal"
"Freeform proposal": "Freeform proposal",
"NewProposal": "New proposal",
"MinProposalRequirements": "You must have at least 1 VEGA associated to make a proposal",
"totalSupply": "Total Supply"
}
+8 -6
View File
@@ -2,12 +2,12 @@ import './styles.css';
import * as Sentry from '@sentry/react';
import { Integrations } from '@sentry/tracing';
import React from 'react';
import ReactDOM from 'react-dom';
import { createRoot } from 'react-dom/client';
import App from './app';
import reportWebVitals from './report-web-vitals';
import { ENV } from './config/env';
import { StrictMode } from 'react';
const dsn = ENV.dsn || false;
const environment = ENV.envName || 'local';
@@ -38,11 +38,13 @@ if (dsn) {
Sentry.setTag('commit', commit);
}
ReactDOM.render(
<React.StrictMode>
const rootElement = document.getElementById('root');
const root = rootElement && createRoot(rootElement);
root?.render(
<StrictMode>
<App />
</React.StrictMode>,
document.getElementById('root')
</StrictMode>
);
// If you want to start measuring performance in your app, pass a function
+5
View File
@@ -5,3 +5,8 @@
*/
import 'core-js/stable';
import 'regenerator-runtime/runtime';
// eslint-disable-next-line @typescript-eslint/no-var-requires
window.Buffer = require('buffer/').Buffer;
// eslint-disable-next-line no-undef, no-global-assign, no-native-reassign
window.global = globalThis;
@@ -14,7 +14,7 @@ import {
TransactionActionType,
TxState,
} from '../../../hooks/transaction-reducer';
import { Routes } from '../../router-config';
import Routes from '../../routes';
export interface ICountry {
name: string;
+1 -1
View File
@@ -5,7 +5,7 @@ import { Link as RouteLink } from 'react-router-dom';
import type { BigNumber } from '../../lib/bignumber';
import { formatNumber } from '../../lib/format-number';
import { Routes } from '../router-config';
import Routes from '../routes';
export const Complete = ({
address,
+1 -1
View File
@@ -29,7 +29,7 @@ const ClaimIndex = ({ name }: RouteChildProps) => {
if (error) {
return (
<Callout intent={Intent.Danger} title={t('errorLoadingTranches')}>
{error}
{error.message}
</Callout>
);
}

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