Compare commits

..
Author SHA1 Message Date
Daniel cb7c4a7b5e feat(ci): fix condition for tools define-dist-variables (#5542) 2023-12-22 17:27:10 +00:00
Daniel 9fd945dc17 feat(ci): allow both validators-testnet and validator-testnet for env name (#5540) 2023-12-22 17:07:53 +00:00
Jeremy Letang 9fbb7a13e6 Merge pull request #5526 from vegaprotocol/5409-funding-countdown
feat(trading): poll market info, and last fundingPeriod
2023-12-22 15:21:40 +01:00
Dariusz Majcherczyk 1874095222 feat: skip test 2023-12-22 14:36:38 +01:00
Dariusz Majcherczyk e459e92127 feat: update sim version 2023-12-22 13:58:20 +01:00
Bartłomiej Głownia 4dd65b4923 feat: rollback using market.data as a source of current fundingPeriod startTime 2023-12-22 12:01:32 +01:00
Bartłomiej Głownia 5d792d2458 feat: pool market info, use market data as source of current funding period startTime 2023-12-21 16:42:25 +00:00
Ben 59b2f75b13 chore(trading): update market-sim (#5535) 2023-12-21 13:51:34 +00:00
ArtandMatthew Russell 95775679ca fix(trading): reward pot total value (#5531)
Co-authored-by: Matthew Russell <mattrussell36@gmail.com>
2023-12-21 13:41:37 +00:00
m.ray 4cffee29f8 chore(trading): rollback current traded volume change (#5527) 2023-12-21 10:38:09 +00:00
Edd bd70b0c233 fix(governance): fix lp vote counts (#5521) 2023-12-21 09:30:23 +00:00
Ben 145792f216 chore(trading): static vega port trading e2e (#5520) 2023-12-20 15:49:10 +00:00
m.ray 10add7c236 fix(trading): update copy on tooltips fills update (#5512) 2023-12-20 08:12:35 +00:00
Edd 362a2031c7 fix(explorer): pass through tendermint error responses from usefetch (#5507) 2023-12-20 08:08:56 +00:00
Edd 5aaeb87059 feat(governance): enable snaps (#5511) 2023-12-19 18:38:49 +00:00
Matthew Russell d240dddab5 fix(trading): alpha lyrae font (#5518) 2023-12-19 16:52:56 +00:00
Ben 0af1d0ba82 chore(trading): fees e2e tests (#5496) 2023-12-19 12:22:15 +00:00
Bartłomiej Głownia 665ab6693a feat(trading): show commission earned on referrer stats from last 30 days (#5467) 2023-12-18 15:33:40 +01:00
Ben 902ee2696c chore(trading): black formatting and removed double fixtures (#5506) 2023-12-18 10:54:40 +00:00
Art 3beeb0140c feat(wallet): disconnect if wallet unreachable (#5501) 2023-12-18 10:39:18 +00:00
m.ray 4b208e90bf fix(trading): internal timestamp date unix formatting (#5505) 2023-12-14 15:10:17 +00:00
Bartłomiej Głownia ec837854cf chore(trading): avoid portfolio and market page re-rendering on sidebar view change (#5499) 2023-12-14 14:12:19 +00:00
Bartłomiej Głownia 8b94a75ba4 feat(positions): improve tooltips (#5481) 2023-12-13 14:36:57 +00:00
Ben 08c57b6759 chore(trading): run against local console (#5500) 2023-12-13 12:46:16 +00:00
Ben bb2184498f chore(trading): update vega version (#5498) 2023-12-13 11:16:52 +00:00
Bartłomiej Głownia 8b07ce3024 fix(trading): fix tabs covered by orderbook splash (#5497) 2023-12-13 09:10:54 +00:00
Bartłomiej Głownia 8a110584dd feat(trading): improve desposit and withraw asset selection (#5493) 2023-12-13 08:47:22 +00:00
Bartłomiej Głownia b82615a3d8 fix(trading): do not add candles if they are older than requested date range (#5484) 2023-12-13 08:06:02 +00:00
Matthew Russell f178b85846 feat(trading): trading view (#5348) 2023-12-12 17:33:41 -08:00
Bartłomiej Głownia 0796f2b31f feat(environment): users controlled feature flags (#5425) 2023-12-12 13:53:20 +01:00
dalebennett1992 67be224138 Merge pull request #5492 from vegaprotocol/test/referrals2
test: add referrals tests
2023-12-12 10:33:57 +00:00
dalebennett1992 51c426ef4b test: test ids 2023-12-12 10:16:47 +00:00
dalebennett1992 fa28d31ef3 test: add referrals tests 2023-12-12 10:15:20 +00:00
m.ray 201a586b05 chore(trading): enable close position (#5490) 2023-12-11 15:51:22 +00:00
m.ray 7d96d9bcd1 chore(trading): update env trading (#5478) 2023-12-08 15:44:55 +00:00
Art 345be81142 fix(trading): rewordings in referrals, qusd tooltip, onboarding (#5477) 2023-12-08 14:48:12 +00:00
Matthew Russell 2221e9faca fix(trading): referrals page final commission value and onboarding dialog (#5474) 2023-12-08 09:14:59 +00:00
Ben 7c0139e107 chore(trading): e2e test fix (#5466) 2023-12-07 11:12:56 +00:00
Witoldandbwallacee a157ff1e05 fix(trading): amend formula for max leverage calc (#5462)
Co-authored-by: bwallacee <ben@vega.xyz>
2023-12-06 09:58:59 -08:00
Art 2e9fa0c52e chore(trading): referrals text and ui tweaks, bug fixes (#5408) 2023-12-06 17:51:39 +00:00
Ben c0790c1e93 chore(trading): update market-sim (#5463) 2023-12-06 16:31:44 +00:00
Bartłomiej Głownia 13c1044f8e chore(i18n): sync json translation files with locize (#5444) 2023-12-06 14:58:17 +00:00
Matthew Russell cf9f313e4c feat(trading): error boundaries for panes, sidebars, pages (#5438) 2023-12-06 13:31:40 +00:00
Art f56d34fe6e fix(environment): node switcher on mobile (#5455) 2023-12-06 13:31:08 +00:00
m.rayandcandida-d 1379366caa chore(trading): update LP table tooltips (#5442)
Co-authored-by: candida-d <62548908+candida-d@users.noreply.github.com>
2023-12-06 13:30:22 +00:00
Art 8958c12fe3 fix(governance): markdown link styling, referral details type error, contracts provider error (#5460) 2023-12-06 14:18:44 +01:00
m.ray fef13874db chore(trading): update generic data provider (#5431) 2023-12-06 12:32:51 +00:00
Edd 40f02ecf89 feat(explorer): add type details to orders in tx list and details (#5432) 2023-12-06 10:45:35 +00:00
Bartłomiej Głownia 41cd6b1455 fix(trading): avoid funding LineChart recreate instance on FundingContainer re-render (#5454) 2023-12-06 10:37:44 +00:00
m.rayandMatthew Russell 3cd393dac0 fix(trading): rewards page updates (#5437)
Co-authored-by: Matthew Russell <mattrussell36@gmail.com>
2023-12-05 20:05:45 -08:00
Matthew Russell a52e60d6a2 Merge pull request #5453 from vegaprotocol/chore/sync-main
chore(trading): sync main
2023-12-05 12:35:31 -08:00
Matthew Russell bc13f1b359 Merge branch 'main' into chore/sync-main 2023-12-05 12:01:32 -08:00
m.ray eb81f4ae44 fix(trading): required vol shown if current is zero (#5449) 2023-12-05 11:36:25 -08:00
m.rayandMatthew Russell 51ab02a2e2 fix(trading): missing party ID when there is no SLA data (#5446)
Co-authored-by: Matthew Russell <mattrussell36@gmail.com>
2023-12-05 11:36:02 -08:00
Ben ffada1b93d feat(trading): auction uncross fees test (#5445) 2023-12-05 15:33:03 +00:00
BenandMatthew Russell 9dda3f712b chore(trading): market python tests to jest (#5346)
Co-authored-by: Matthew Russell <mattrussell36@gmail.com>
2023-12-05 10:29:01 +00:00
m.ray df20dbeee0 chore(trading): revert moving rewards container to portfolio (#5441) 2023-12-05 10:19:42 +00:00
m.ray cdfd8a2d00 fix(trading): revert disabling sortable as it breaks view (#5435) 2023-12-04 18:12:13 +00:00
m.ray 1e5c523bc4 chore(trading): disable trades table sorting (#5423) 2023-12-04 15:40:32 +00:00
m.ray 37cd69ba6e chore(trading): move rewards to portfolio part 1 (#5402) 2023-12-04 15:40:14 +00:00
Ben 2c11045dd9 feat(trading): perp market tests (#5426) 2023-12-04 14:29:00 +00:00
ArtandMadalina Raicu 9aef41a119 fix(governance): update asset proposal (#5417)
Co-authored-by: Madalina Raicu <madalina@raygroup.uk>
2023-12-04 15:20:07 +01:00
m.ray 80ab8821d0 fix(trading): live time fraction zero redundant check (#5420) 2023-12-02 11:36:58 +00:00
m.ray 8a3657a9b9 fix(trading): live time fraction zero (#5419) 2023-12-02 11:06:20 +00:00
Art 7100b0e9fc chore(accounts): no assets avaiable in transfer form (#5358) 2023-12-01 17:05:22 +00:00
614a83b7d6 chore(trading): merge main back in develop (fees discounts, discount stats from prev epoch) (#5415)
Co-authored-by: Bartłomiej Głownia <bglownia@gmail.com>
Co-authored-by: asiaznik <artur@vegaprotocol.io>
2023-12-01 17:03:41 +00:00
m.ray 3dc77b0eff chore(trading): add close position button back to console (#5407) 2023-12-01 16:38:32 +00:00
m.ray a59f7dfd29 fix(trading): fills fees maker discounts (#5406) 2023-12-01 16:34:22 +00:00
Bartłomiej Głowniaandasiaznik 61471228aa fix(trading): use discount stats only from previous epoch (#5411)
Co-authored-by: asiaznik <artur@vegaprotocol.io>
2023-12-01 16:34:05 +00:00
Art 127e784ceb chore(trading): don't redirect to inactive markets (#5359) 2023-12-01 14:42:26 +01:00
m.ray 70d748fb15 fix(trading): fills fees fixes for maker (#5405) 2023-12-01 11:23:04 +00:00
EddandMadalina Raicu e06f4818fc feat(explorer): add signature viewer (#5264)
Co-authored-by: Madalina Raicu <madalina@raygroup.uk>
2023-11-30 11:54:18 +00:00
Bartłomiej Głownia 8182da3b31 feat(deal-ticket): fix postOnly mapping in mapFormValuesToOrderSubmission (#5375) 2023-11-30 11:25:25 +01:00
Ben c8c56307bb chore(trading): update vega version (#5396) 2023-11-30 09:52:05 +00:00
Ben a8cd7f157f chore(trading): migrate cypress tests to python (#5367) 2023-11-30 09:42:57 +00:00
Bartłomiej Głownia 4f18caa486 feat(trading): upgrade i18n, fix plurals (#5331) 2023-11-30 07:31:44 +00:00
Matthew Russell 5c7c626bbc Merge pull request #5393 from vegaprotocol/chore/sync-main
chore(trading, datagrid, liquidity, proposals, ui-toolkit): sync main
2023-11-29 18:08:50 -08:00
Matthew Russell e4c4c20631 fix: duplicate props in ag-grid-themed 2023-11-29 14:09:08 -08:00
Matthew Russell 0697302d07 Merge branch 'main' into chore/sync-main 2023-11-29 14:06:45 -08:00
ArtandMadalina Raicu 2d926c0ce0 fix(governance): sensible vote numbers (#5384)
Co-authored-by: Madalina Raicu <madalina@raygroup.uk>
2023-11-29 10:38:41 -08:00
Art f57d6a7c7b fix(trading): missing volume discount program issue, inverted tiers (#5378) 2023-11-29 17:23:14 +00:00
m.ray 15f905046f fix(trading): fees accrued tooltip (#5387) 2023-11-29 17:17:44 +00:00
Bartłomiej Głownia 4f7918f64e feat(trading): remove proposal warning from market header (#5385) 2023-11-29 17:05:53 +00:00
Bartłomiej Głownia 52ab0562b0 feat(trading): refactor ledger export form validation (#5379) 2023-11-29 15:27:00 +00:00
m.ray 4e2b0d1b1d fix(trading): fix ag-grid transparent filters (#5377) 2023-11-29 15:24:37 +00:00
m.ray 0b0bcad9b3 fix(trading): ag-grid compactness adjustment (#5382) 2023-11-29 14:29:02 +00:00
Bartłomiej GłowniaandMatthew Russell bcf17bb34e feat(trading): i18n language switcher (#5320)
Co-authored-by: Matthew Russell <mattrussell36@gmail.com>
2023-11-29 15:16:17 +01:00
Bartłomiej Głownia a2b9b0da05 feat(deal-ticket): enhance for fee discounts (#5353) 2023-11-29 12:59:42 +00:00
Bartłomiej Głownia 7588d0cd11 feat(trading): refactor ledger export form validation (#5362) 2023-11-29 13:28:31 +01:00
m.ray 5ee1748495 fix(trading): ag-grid styling updates and filter fixes (#5368) 2023-11-28 22:13:12 +00:00
Art 73a118978f feat(trading): referral code eligibility (#5325) 2023-11-28 16:08:21 +01:00
m.ray eac26c1966 fix(trading): empty connect wallet dialog (#5356) 2023-11-28 14:43:31 +00:00
Matthew Russell d615587564 Merge pull request #5361 from vegaprotocol/chore/backmerge-main
chore(trading): back merge main
2023-11-27 22:33:19 -08:00
Matthew Russell ba4ce1ce88 fix: botched merge in gitignore 2023-11-27 22:06:46 -08:00
Matthew Russell 3bbacc1aa0 fix: fix broken test after removal of formatting 2023-11-27 21:22:30 -08:00
Matthew Russell de5371435d Merge branch 'main' into chore/backmerge-main 2023-11-27 20:08:08 -08:00
m.rayandMatthew Russell 12cb5e10b6 chore(trading): merge main in develop (#5352)
Co-authored-by: Matthew Russell <mattrussell36@gmail.com>
2023-11-27 15:49:26 -08:00
Art ee2909cc84 fix(wallet): empty connect dialog (#5354) 2023-11-27 16:15:18 +00:00
Bartłomiej Głownia 068d6abf1b feat(trading): show percentage sign on y axis in funding rate chart (#5342) 2023-11-27 13:39:20 +00:00
m.ray e5d4d2b0b8 chore(trading): temporarily disable close position (#5350) 2023-11-27 10:48:29 +00:00
m.ray d4e801cfc6 feat(trading): reset console to default (#5344) 2023-11-27 10:42:47 +00:00
Bartłomiej Głownia 964deb2f23 feat(trading): update title on rewards and fees pages (#5347) 2023-11-27 08:18:59 +00:00
Art 6669125dd3 feat(trading): copy apply referral link from code tile (#5338) 2023-11-24 10:20:37 +00:00
m.ray 129b6c4e89 feat(trading): referral initial state when no sets are available (#5294) 2023-11-23 17:08:26 +00:00
Ben ca418cabfe chore(trading): migrate python tests to jest (#5339) 2023-11-23 15:56:29 +00:00
Ben 192af844c4 chore(trading): fix change keys edge case (#5337) 2023-11-23 09:12:45 +00:00
Ben 6a841f226a chore(ci): reduce number of runners for python tests (#5333) 2023-11-22 16:00:29 +00:00
Ben 76426baa2a chore(ci): create traces dir (#5327) 2023-11-22 15:52:59 +00:00
Ben 6fdac2419c chore(trading): move python stop order tests to jest (#5329) 2023-11-22 15:10:19 +00:00
Art 87807d2088 chore(governance): sensible vote numbers (#5332) 2023-11-22 15:30:16 +01:00
m.rayandbwallacee a5f2533a66 feat(trading): market list snags and disable column removal (#5324)
Co-authored-by: bwallacee <ben@vega.xyz>
2023-11-22 13:51:39 +00:00
Ben 1e9251c9c0 chore(trading): python tests update vega version (#5313) 2023-11-22 09:39:40 +00:00
Ben 2a27e124d7 chore(ci): increase number of tests in parallel (#5322) 2023-11-22 08:17:22 +00:00
Ben 4ee85720ab chore(ci): specific ganache image (#5321) 2023-11-22 08:02:47 +00:00
BenandBartłomiej Głownia a19da4c408 chore(trading): cypress to python tests (#5319)
Co-authored-by: Bartłomiej Głownia <bglownia@gmail.com>
2023-11-21 14:44:41 +00:00
BenandMatthew Russell 65ad4fffda chore(trading): migrate LP cypress tests (#5286)
Co-authored-by: Matthew Russell <mattrussell36@gmail.com>
2023-11-21 14:36:02 +00:00
Bartłomiej GłowniaandMatthew Russell 11317cdb2e feat(withdraws): use i18next (#5285)
Co-authored-by: Matthew Russell <mattrussell36@gmail.com>
2023-11-21 13:49:10 +00:00
Bartłomiej GłowniaandMatthew Russell c98870f27d feat(wallet): use i18next (#5299)
Co-authored-by: Matthew Russell <mattrussell36@gmail.com>
2023-11-21 13:48:53 +00:00
Bartłomiej Głowniaandbwallacee c0c56e4c48 feat(trading): fix markets i18n namespace, add missing label, preload all namespaces (#5312)
Co-authored-by: bwallacee <ben@vega.xyz>
2023-11-21 12:08:33 +01:00
Matthew Russell d9dc43b359 chore(deal-ticket): re-enable margin estimates for perps (#5317) 2023-11-20 13:57:18 -08:00
m.rayandMatthew Russell 06a6fe6d67 fix(accounts): remove account balance formatting in transfer form (#5315)
Co-authored-by: Matthew Russell <mattrussell36@gmail.com>
2023-11-20 13:07:17 -08:00
Bartłomiej GłowniaandMatthew Russell a9ca215276 feat(proposals): use i18next (#5284)
Co-authored-by: Matthew Russell <mattrussell36@gmail.com>
2023-11-19 21:49:39 -08:00
Bartłomiej GłowniaandMatthew Russell 61a9eb2f5c feat(ui-toolkit): use i18next (#5289)
Co-authored-by: Matthew Russell <mattrussell36@gmail.com>
2023-11-19 15:27:52 -08:00
Bartłomiej GłowniaandMatthew Russell bb47747501 feat(utils): use i18next (#5269)
Co-authored-by: Matthew Russell <mattrussell36@gmail.com>
2023-11-19 14:00:45 -08:00
Bartłomiej GłowniaandMatthew Russell 5827b87f89 feat(trades): use i18next (#5268)
Co-authored-by: Matthew Russell <mattrussell36@gmail.com>
2023-11-19 13:51:08 -08:00
Bartłomiej Głownia 19b95832c4 feat(market-depth): use i18next (#5267) 2023-11-19 13:13:03 -08:00
Bartłomiej GłowniaandMatthew Russell 48e4ab53a1 feat(orders): use i18next (#5263)
Co-authored-by: Matthew Russell <mattrussell36@gmail.com>
2023-11-19 13:12:35 -08:00
Bartłomiej Głownia cefcff040f feat(positions): use i18next (#5266) 2023-11-17 19:49:19 -08:00
Bartłomiej Głownia c3ef639daf feat(web3): use i18next (#5256) 2023-11-17 16:44:30 -08:00
Bartłomiej Głownia b6052bc3e5 feat(markets): use i18next (#5255) 2023-11-17 12:01:18 -08:00
Bartłomiej Głownia 6e8894936e feat(ledger): use i18next (#5248) 2023-11-17 11:55:10 -08:00
Bartłomiej Głownia 66f55855ae feat(liquidity): use i18next (#5247) 2023-11-17 11:54:17 -08:00
Matthew Russell 5725b824ad Merge pull request #5309 from vegaprotocol/chore/sync-main
chore(governance,trading): sync with mainnet fixes
2023-11-17 11:51:03 -08:00
Matthew Russell 9344a2abc0 fix: no t function 2023-11-17 11:10:18 -08:00
Matthew Russell fae66ca4ce Merge branch 'main' into chore/sync-main 2023-11-17 10:56:19 -08:00
Matthew Russell 4b7338ed94 chore(deal-ticket,markets): dont show margin estimate for perps (#5305) 2023-11-17 10:33:24 -08:00
Edd 2983c6c6ba feat(governance): enable volume discount update view (#5304) (#5307) 2023-11-17 10:25:30 -08:00
Edd 31524ac7a7 feat(governance): enable volume discount update view (#5304) 2023-11-17 17:29:42 +00:00
Matthew Russellandasiaznik 70f42c1c7e fix(trading): fees page fixes for mainnet (#5303)
Co-authored-by: asiaznik <artur@vegaprotocol.io>
2023-11-17 08:53:21 -08:00
m.ray 2304ce9763 fix(proposals): perpetual proposed markets (#5290) 2023-11-17 12:00:19 +00:00
m.ray 79a16b8562 fix(trading): show deal ticket in sidebar by default (#5292) 2023-11-16 09:08:20 -08:00
Ben ea3e5a7651 chore(trading): update vega-market-sim (#5288) 2023-11-16 15:48:36 +00:00
Ben b062339682 chore(trading): delete duplicate cypress tests (#5287) 2023-11-16 14:22:24 +00:00
Matthew Russell 5661b74082 fix(trading): do not use stakeToCcyVolume to format fees value (#5280) 2023-11-16 09:34:38 +00:00
m.ray 22703d937e fix(trading): fix percentage formatter rounding in liquidity table (#5250) 2023-11-13 16:14:18 +00:00
493 changed files with 12808 additions and 6609 deletions
+1
View File
@@ -77,6 +77,7 @@
"fixStyle": "inline-type-imports"
}
],
"@typescript-eslint/no-useless-constructor": 0,
"curly": ["error", "multi-line"]
}
},
+1
View File
@@ -1,3 +1,4 @@
* text eol=lf
*.png binary
*.ico binary
*.woff2 binary
+2 -2
View File
@@ -10,7 +10,7 @@ on:
inputs:
console-test-branch:
type: choice
description: 'main: v0.72.14, develop: v0.73.4'
description: 'main: v0.73.5, develop: v0.73.5'
options:
- main
- develop
@@ -215,7 +215,7 @@ jobs:
if: always()
with:
name: playwright-trace
path: ./traces/
path: apps/trading/e2e/traces/
retention-days: 15
#----------------------------------------------
# ----- upload logs -----
+1 -2
View File
@@ -59,5 +59,4 @@ apps/trading/e2e/logs/
apps/trading/e2e/.pytest_cache/
apps/trading/e2e/traces/
.nx/cache
.nx/
+3
View File
@@ -1,5 +1,8 @@
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
# Auto-format all files
yarn nx format:write
# Lint all staged files
yarn lint-staged
+3 -3
View File
@@ -1,8 +1,8 @@
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
# Lint all staged files
yarn nx format:check
# Lint all staged files - this brings more value as pre-commit
# yarn nx format:check
# Test all projects with changes
yarn nx affected -t test --exclude trading
# yarn nx affected -t test --exclude trading
+24 -15
View File
@@ -1,3 +1,4 @@
import '../i18n';
import {
NetworkLoader,
NodeFailure,
@@ -11,7 +12,8 @@ import { TendermintWebsocketProvider } from './contexts/websocket/tendermint-web
import { Loader, Splash } from '@vegaprotocol/ui-toolkit';
import { DEFAULT_CACHE_CONFIG } from '@vegaprotocol/apollo-client';
import { RouterProvider } from 'react-router-dom';
import { router } from './routes/router-config';
import { useRouterConfig } from './routes/router-config';
import { createBrowserRouter } from 'react-router-dom';
import { t } from '@vegaprotocol/i18n';
import { Suspense } from 'react';
@@ -28,20 +30,27 @@ function App() {
);
return (
<TendermintWebsocketProvider>
<NetworkLoader cache={DEFAULT_CACHE_CONFIG}>
<NodeGuard
skeleton={<div>{t('Loading')}</div>}
failure={<NodeFailure title={t(`Node: ${VEGA_URL} is unsuitable`)} />}
>
<Suspense fallback={splashLoading}>
<RouterProvider router={router} fallbackElement={splashLoading} />
</Suspense>
</NodeGuard>
<NodeSwitcherDialog
open={nodeSwitcherOpen}
setOpen={setNodeSwitcherOpen}
/>
</NetworkLoader>
<Suspense fallback={splashLoading}>
<NetworkLoader cache={DEFAULT_CACHE_CONFIG}>
<NodeGuard
skeleton={<div>{t('Loading')}</div>}
failure={
<NodeFailure title={t(`Node: ${VEGA_URL} is unsuitable`)} />
}
>
<Suspense fallback={splashLoading}>
<RouterProvider
router={createBrowserRouter(useRouterConfig())}
fallbackElement={splashLoading}
/>
</Suspense>
</NodeGuard>
<NodeSwitcherDialog
open={nodeSwitcherOpen}
setOpen={setNodeSwitcherOpen}
/>
</NetworkLoader>
</Suspense>
</TendermintWebsocketProvider>
);
}
@@ -12,7 +12,8 @@ import { type VegaICellRendererParams } from '@vegaprotocol/datagrid';
import { useRef, useLayoutEffect } from 'react';
import { BREAKPOINT_MD } from '../../config/breakpoints';
import { useNavigate } from 'react-router-dom';
import { type RowClickedEvent, ColDef } from 'ag-grid-community';
import { type ColDef } from 'ag-grid-community';
import type { RowClickedEvent } from 'ag-grid-community';
type AssetsTableProps = {
data: AssetFieldsFragment[] | null;
@@ -14,7 +14,7 @@ import { Routes } from '../../routes/route-names';
import { NetworkSwitcher } from '@vegaprotocol/environment';
import type { Navigable } from '../../routes/router-config';
import { isNavigable } from '../../routes/router-config';
import { routerConfig } from '../../routes/router-config';
import { useRouterConfig } from '../../routes/router-config';
import { useMemo } from 'react';
import compact from 'lodash/compact';
import { Search } from '../search';
@@ -26,6 +26,7 @@ const routeToNavigationItem = (r: Navigable) => (
);
export const Header = () => {
const routerConfig = useRouterConfig();
const isHome = Boolean(useMatch(Routes.HOME));
const pages = routerConfig[0].children || [];
const mainItems = compact(
@@ -6,6 +6,7 @@ import { Time } from '../time';
import { sideText, statusText, tifFull, tifShort } from './lib/order-labels';
import SizeInMarket from '../size-in-market/size-in-market';
import { TxOrderPeggedReference } from '../txs/details/order/tx-order-peg';
import { OrderTypeMapping } from '@vegaprotocol/types';
export interface DeterministicOrderDetailsProps {
id: string;
@@ -69,7 +70,7 @@ const DeterministicOrderDetails = ({
<span className="mx-5 text-base">@</span>
<PriceInMarket price={o.price} marketId={o.market.id} />
</h2>
<p className="text-gray-200">
<p className="text-gray-400 dark:text-gray-600">
In <MarketLink id={o.market.id} /> at <Time date={o.createdAt} />.
</p>
{o.peggedOrder ? (
@@ -83,13 +84,12 @@ const DeterministicOrderDetails = ({
/>
</p>
) : null}
{o.reference ? (
<p className="text-gray-500 mt-4">
<span>{t('Reference')}</span>: {o.reference}
</p>
) : null}
<div className="grid md:grid-cols-4 gap-x-6 mt-4">
<div className="grid md:grid-cols-5 gap-x-6 mt-4">
<div className="mb-12 md:mb-0">
<h2 className="text-2xl font-bold text-dark mb-4">
{t('Status')}
@@ -114,6 +114,16 @@ const DeterministicOrderDetails = ({
{o.version}
</h5>
</div>
{o.type ? (
<div className="">
<h2 className="text-2xl font-bold text-dark mb-4">
{t('Type')}
</h2>
<h5 className="text-lg font-medium text-gray-500 mb-0">
{OrderTypeMapping[o.type]}
</h5>
</div>
) : null}
</div>
</div>
</div>
@@ -8,7 +8,8 @@ import {
type VegaValueFormatterParams,
} from '@vegaprotocol/datagrid';
import { useLayoutEffect, useMemo, useRef, useState } from 'react';
import { type RowClickedEvent, ColDef } from 'ag-grid-community';
import { type ColDef } from 'ag-grid-community';
import type { RowClickedEvent } from 'ag-grid-community';
import { getDateTimeFormat } from '@vegaprotocol/utils';
import { t } from '@vegaprotocol/i18n';
import {
@@ -0,0 +1,59 @@
import { useState } from 'react';
import type { BlockExplorerTransactionResult } from '../../routes/types/block-explorer-response';
import {
CopyWithTooltip,
VegaIcon,
VegaIconNames,
} from '@vegaprotocol/ui-toolkit';
import { t } from '@vegaprotocol/i18n';
interface SignatureProps {
signature: BlockExplorerTransactionResult['signature'];
}
const valueClass =
'font-mono px-2.5 py-0.5 text-xs max-w-[200px] cursor-pointer';
const valueClassClosed = 'text-ellipsis overflow-hidden';
const valueClassOpen = 'break-words text-left';
/**
* Viewer component for a vega signature. Featuers copy and pasting, truncation
*
* @param signature
*/
export const Signature = ({ signature }: SignatureProps) => {
const [isOpen, setIsOpen] = useState(false);
if (!signature || !signature.value || !signature.version || !signature.algo) {
return null;
}
return (
<div className="inline-flex border rounded signature-component relative pr-[20px]">
<span
className="bg-gray-100 px-2.5 py-0.5 text-xs text-gray-500 select-none cursor-default"
title={`Version ${signature.version}`}
>
{signature.algo}
</span>
<div
className={
isOpen
? `${valueClass} ${valueClassOpen}`
: `${valueClass} ${valueClassClosed}`
}
>
<CopyWithTooltip text={signature.value}>
<span title={signature.value}>{signature.value}</span>
</CopyWithTooltip>
</div>
<button
onClick={() => setIsOpen(!isOpen)}
className="absolute top-[-3px] right-0 pr-2"
title={t('Show full signature')}
>
<VegaIcon name={isOpen ? VegaIconNames.EYE_OFF : VegaIconNames.EYE} />
</button>
</div>
);
};
@@ -9,6 +9,7 @@ import { Time } from '../../../time';
import { ChainResponseCode } from '../chain-response-code/chain-reponse.code';
import { TxDataView } from '../../tx-data-view';
import Hash from '../../../links/hash';
import { Signature } from '../../../signature/signature';
interface TxDetailsSharedProps {
txData: BlockExplorerTransactionResult | undefined;
@@ -75,6 +76,12 @@ export const TxDetailsShared = ({
<BlockLink height={height} />
</TableCell>
</TableRow>
<TableRow modifier="bordered">
<TableCell {...sharedHeaderProps}>{t('Signature')}</TableCell>
<TableCell>
<Signature signature={txData.signature} />
</TableCell>
</TableRow>
<TableRow modifier="bordered">
<TableCell {...sharedHeaderProps}>{t('Time')}</TableCell>
<TableCell>
@@ -80,6 +80,12 @@ export function getLabelForOrderType(
if (command.orderSubmission.icebergOpts) {
return 'Iceberg';
}
if (command.orderSubmission.type === 'TYPE_MARKET') {
return 'Market order';
}
if (command.orderSubmission.type === 'TYPE_LIMIT') {
return 'Limit order';
}
}
return 'Order';
}
@@ -98,6 +98,8 @@ describe('TxDetailsTransfer', () => {
},
},
signature: {
version: '1',
algo: 'vega/ed25519',
value:
'610c2e196a7d4fed4413b9e82af267b1ff3e30e943df3a3d28096fd60604d430d752fbaf6dd4f84d496be78885bb6118f40560bff7832c06bd7a3d67b718b700',
},
@@ -20,6 +20,8 @@ const generateTxs = (number: number): BlockExplorerTransactionResult[] => {
'4b782482f587d291e8614219eb9a5ee9280fa2c58982dee71d976782a9be1964',
type: 'Submit Order',
signature: {
version: '1',
algo: 'vega/ed25519',
value: '123',
},
code: 0,
@@ -54,7 +54,7 @@ const Block = () => {
</Button>
</Link>
</div>
{blockData && (
{blockData && 'result' in blockData && (
<>
<TableWithTbody className="mb-8">
<TableRow modifier="bordered">
+292 -289
View File
@@ -18,7 +18,6 @@ import { t } from '@vegaprotocol/i18n';
import { Routes } from './route-names';
import { NetworkParameters } from './network-parameters';
import type { Params, RouteObject } from 'react-router-dom';
import { createBrowserRouter } from 'react-router-dom';
import { Link } from 'react-router-dom';
import { MarketPage, MarketsPage } from './markets';
import type { ReactNode } from 'react';
@@ -29,7 +28,7 @@ import { truncateMiddle } from '@vegaprotocol/ui-toolkit';
import { remove0x } from '@vegaprotocol/utils';
import { PartyAccountsByAsset } from './parties/id/accounts';
import { Disclaimer } from './pages/disclaimer';
import { FLAGS } from '@vegaprotocol/environment';
import { useFeatureFlags } from '@vegaprotocol/environment';
import RestrictedPage from './restricted';
export type Navigable = {
@@ -60,311 +59,315 @@ type Route = RouteItem & {
children?: RouteItem[];
};
const partiesRoutes: Route[] = FLAGS.EXPLORER_PARTIES
? [
{
path: Routes.PARTIES,
element: <Party />,
handle: {
name: t('Parties'),
text: t('Parties'),
breadcrumb: () => <Link to={Routes.PARTIES}>{t('Parties')}</Link>,
},
children: [
{
index: true,
element: <Parties />,
},
{
path: ':party',
element: <Party />,
export const useRouterConfig = () => {
const featureFlags = useFeatureFlags((state) => state.flags);
children: [
{
index: true,
element: <PartySingle />,
handle: {
breadcrumb: (params: Params<string>) => (
<Link to={linkTo(Routes.PARTIES, params.party)}>
{truncateMiddle(params.party as string)}
</Link>
),
const partiesRoutes: Route[] = featureFlags.EXPLORER_PARTIES
? [
{
path: Routes.PARTIES,
element: <Party />,
handle: {
name: t('Parties'),
text: t('Parties'),
breadcrumb: () => <Link to={Routes.PARTIES}>{t('Parties')}</Link>,
},
children: [
{
index: true,
element: <Parties />,
},
{
path: ':party',
element: <Party />,
children: [
{
index: true,
element: <PartySingle />,
handle: {
breadcrumb: (params: Params<string>) => (
<Link to={linkTo(Routes.PARTIES, params.party)}>
{truncateMiddle(params.party as string)}
</Link>
),
},
},
},
{
path: 'assets',
element: <Party />,
handle: {
breadcrumb: (params: Params<string>) => (
<Link to={linkTo(Routes.PARTIES, params.party)}>
{truncateMiddle(params.party as string)}
</Link>
),
},
children: [
{
index: true,
element: <PartyAccountsByAsset />,
handle: {
breadcrumb: () => {
return t('Assets');
{
path: 'assets',
element: <Party />,
handle: {
breadcrumb: (params: Params<string>) => (
<Link to={linkTo(Routes.PARTIES, params.party)}>
{truncateMiddle(params.party as string)}
</Link>
),
},
children: [
{
index: true,
element: <PartyAccountsByAsset />,
handle: {
breadcrumb: () => {
return t('Assets');
},
},
},
},
],
],
},
],
},
],
},
]
: [];
const assetsRoutes: Route[] = featureFlags.EXPLORER_ASSETS
? [
{
path: Routes.ASSETS,
handle: {
name: t('Assets'),
text: t('Assets'),
breadcrumb: () => <Link to={Routes.ASSETS}>{t('Assets')}</Link>,
},
children: [
{
index: true,
element: <AssetsPage />,
},
{
path: ':assetId',
element: <AssetPage />,
handle: {
breadcrumb: (params: Params<string>) => (
<AssetLink assetId={params.assetId as string} />
),
},
],
},
],
},
]
: [];
const assetsRoutes: Route[] = FLAGS.EXPLORER_ASSETS
? [
{
path: Routes.ASSETS,
handle: {
name: t('Assets'),
text: t('Assets'),
breadcrumb: () => <Link to={Routes.ASSETS}>{t('Assets')}</Link>,
},
],
},
children: [
{
index: true,
element: <AssetsPage />,
]
: [];
const genesisRoutes: Route[] = featureFlags.EXPLORER_GENESIS
? [
{
path: Routes.GENESIS,
handle: {
name: t('Genesis'),
text: t('Genesis Parameters'),
breadcrumb: () => (
<Link to={Routes.GENESIS}>{t('Genesis Parameters')}</Link>
),
},
element: <Genesis />,
},
]
: [];
const governanceRoutes: Route[] = featureFlags.EXPLORER_GOVERNANCE
? [
{
path: Routes.GOVERNANCE,
handle: {
name: t('Governance proposals'),
text: t('Governance Proposals'),
breadcrumb: () => (
<Link to={Routes.GOVERNANCE}>{t('Governance Proposals')}</Link>
),
},
element: <Proposals />,
},
]
: [];
const marketsRoutes: Route[] = featureFlags.EXPLORER_MARKETS
? [
{
path: Routes.MARKETS,
handle: {
name: t('Markets'),
text: t('Markets'),
breadcrumb: () => <Link to={Routes.MARKETS}>{t('Markets')}</Link>,
},
children: [
{
index: true,
element: <MarketsPage />,
},
{
path: ':marketId',
element: <MarketPage />,
handle: {
breadcrumb: (params: Params<string>) => (
<MarketLink id={params.marketId as string} />
),
},
},
],
},
]
: [];
const networkParametersRoutes: Route[] =
featureFlags.EXPLORER_NETWORK_PARAMETERS
? [
{
path: ':assetId',
element: <AssetPage />,
path: Routes.NETWORK_PARAMETERS,
handle: {
breadcrumb: (params: Params<string>) => (
<AssetLink assetId={params.assetId as string} />
name: t('NetworkParameters'),
text: t('Network Parameters'),
breadcrumb: () => (
<Link to={Routes.NETWORK_PARAMETERS}>
{t('Network Parameters')}
</Link>
),
},
element: <NetworkParameters />,
},
],
},
]
: [];
]
: [];
const genesisRoutes: Route[] = FLAGS.EXPLORER_GENESIS
? [
{
path: Routes.GENESIS,
handle: {
name: t('Genesis'),
text: t('Genesis Parameters'),
breadcrumb: () => (
<Link to={Routes.GENESIS}>{t('Genesis Parameters')}</Link>
),
},
element: <Genesis />,
},
]
: [];
const governanceRoutes: Route[] = FLAGS.EXPLORER_GOVERNANCE
? [
{
path: Routes.GOVERNANCE,
handle: {
name: t('Governance proposals'),
text: t('Governance Proposals'),
breadcrumb: () => (
<Link to={Routes.GOVERNANCE}>{t('Governance Proposals')}</Link>
),
},
element: <Proposals />,
},
]
: [];
const marketsRoutes: Route[] = FLAGS.EXPLORER_MARKETS
? [
{
path: Routes.MARKETS,
handle: {
name: t('Markets'),
text: t('Markets'),
breadcrumb: () => <Link to={Routes.MARKETS}>{t('Markets')}</Link>,
},
children: [
{
index: true,
element: <MarketsPage />,
const validators: Route[] = featureFlags.EXPLORER_VALIDATORS
? [
{
path: Routes.VALIDATORS,
handle: {
name: t('Validators'),
text: t('Validators'),
breadcrumb: () => (
<Link to={Routes.VALIDATORS}>{t('Validators')}</Link>
),
},
{
path: ':marketId',
element: <MarketPage />,
handle: {
breadcrumb: (params: Params<string>) => (
<MarketLink id={params.marketId as string} />
),
element: <ValidatorsPage />,
},
]
: [];
const linkTo = (...segments: (string | undefined)[]) =>
compact(segments).join('/');
const routerConfig: Route[] = [
{
path: Routes.HOME,
element: <Layout />,
handle: {
name: t('Home'),
text: t('Home'),
breadcrumb: () => <Link to={Routes.HOME}>{t('Home')}</Link>,
},
errorElement: <ErrorBoundary />,
children: [
{
index: true,
element: <Home />,
},
{
path: Routes.TX,
handle: {
name: t('Txs'),
text: t('Transactions'),
breadcrumb: () => <Link to={Routes.TX}>{t('Transactions')}</Link>,
},
children: [
{
path: ':txHash',
element: <Tx />,
handle: {
breadcrumb: (params: Params<string>) => (
<Link to={linkTo(Routes.TX, params.txHash)}>
{truncateMiddle(remove0x(params.txHash as string))}
</Link>
),
},
},
{
index: true,
element: <TxsList />,
},
],
},
{
path: Routes.BLOCKS,
handle: {
name: t('Blocks'),
text: t('Blocks'),
breadcrumb: () => <Link to={Routes.BLOCKS}>{t('Blocks')}</Link>,
},
],
},
]
: [];
const networkParametersRoutes: Route[] = FLAGS.EXPLORER_NETWORK_PARAMETERS
? [
{
path: Routes.NETWORK_PARAMETERS,
handle: {
name: t('NetworkParameters'),
text: t('Network Parameters'),
breadcrumb: () => (
<Link to={Routes.NETWORK_PARAMETERS}>
{t('Network Parameters')}
</Link>
),
element: <BlockPage />,
children: [
{
index: true,
element: <Blocks />,
},
{
path: ':block',
element: <Block />,
handle: {
breadcrumb: (params: Params<string>) => (
<Link to={linkTo(Routes.BLOCKS, params.block)}>
{params.block}
</Link>
),
},
},
],
},
element: <NetworkParameters />,
},
]
: [];
const validators: Route[] = FLAGS.EXPLORER_VALIDATORS
? [
{
path: Routes.VALIDATORS,
handle: {
name: t('Validators'),
text: t('Validators'),
breadcrumb: () => (
<Link to={Routes.VALIDATORS}>{t('Validators')}</Link>
),
{
path: Routes.ORACLES,
handle: {
name: t('Oracles'),
text: t('Oracles'),
breadcrumb: () => <Link to={Routes.ORACLES}>{t('Oracles')}</Link>,
},
element: <OraclePage />,
children: [
{
index: true,
element: <Oracles />,
},
{
path: ':id',
element: <Oracle />,
handle: {
breadcrumb: (params: Params<string>) => (
<Link to={linkTo(Routes.ORACLES, params.id)}>
{truncateMiddle(params.id as string)}
</Link>
),
},
},
],
},
element: <ValidatorsPage />,
},
]
: [];
const linkTo = (...segments: (string | undefined)[]) =>
compact(segments).join('/');
export const routerConfig: Route[] = [
{
path: Routes.HOME,
element: <Layout />,
handle: {
name: t('Home'),
text: t('Home'),
breadcrumb: () => <Link to={Routes.HOME}>{t('Home')}</Link>,
{
path: Routes.DISCLAIMER,
element: <Disclaimer />,
handle: {
name: t('Disclaimer'),
text: t('Disclaimer'),
breadcrumb: () => (
<Link to={Routes.DISCLAIMER}>{t('Disclaimer')}</Link>
),
},
},
...partiesRoutes,
...assetsRoutes,
...genesisRoutes,
...governanceRoutes,
...marketsRoutes,
...networkParametersRoutes,
...validators,
],
},
errorElement: <ErrorBoundary />,
children: [
{
index: true,
element: <Home />,
{
path: Routes.RESTRICTED,
element: <RestrictedPage />,
handle: {
name: t('Restricted'),
text: t('Restricted'),
},
{
path: Routes.TX,
handle: {
name: t('Txs'),
text: t('Transactions'),
breadcrumb: () => <Link to={Routes.TX}>{t('Transactions')}</Link>,
},
children: [
{
path: ':txHash',
element: <Tx />,
handle: {
breadcrumb: (params: Params<string>) => (
<Link to={linkTo(Routes.TX, params.txHash)}>
{truncateMiddle(remove0x(params.txHash as string))}
</Link>
),
},
},
{
index: true,
element: <TxsList />,
},
],
},
{
path: Routes.BLOCKS,
handle: {
name: t('Blocks'),
text: t('Blocks'),
breadcrumb: () => <Link to={Routes.BLOCKS}>{t('Blocks')}</Link>,
},
element: <BlockPage />,
children: [
{
index: true,
element: <Blocks />,
},
{
path: ':block',
element: <Block />,
handle: {
breadcrumb: (params: Params<string>) => (
<Link to={linkTo(Routes.BLOCKS, params.block)}>
{params.block}
</Link>
),
},
},
],
},
{
path: Routes.ORACLES,
handle: {
name: t('Oracles'),
text: t('Oracles'),
breadcrumb: () => <Link to={Routes.ORACLES}>{t('Oracles')}</Link>,
},
element: <OraclePage />,
children: [
{
index: true,
element: <Oracles />,
},
{
path: ':id',
element: <Oracle />,
handle: {
breadcrumb: (params: Params<string>) => (
<Link to={linkTo(Routes.ORACLES, params.id)}>
{truncateMiddle(params.id as string)}
</Link>
),
},
},
],
},
{
path: Routes.DISCLAIMER,
element: <Disclaimer />,
handle: {
name: t('Disclaimer'),
text: t('Disclaimer'),
breadcrumb: () => (
<Link to={Routes.DISCLAIMER}>{t('Disclaimer')}</Link>
),
},
},
...partiesRoutes,
...assetsRoutes,
...genesisRoutes,
...governanceRoutes,
...marketsRoutes,
...networkParametersRoutes,
...validators,
],
},
{
path: Routes.RESTRICTED,
element: <RestrictedPage />,
handle: {
name: t('Restricted'),
text: t('Restricted'),
},
},
];
export const router = createBrowserRouter(routerConfig);
];
return routerConfig;
};
@@ -23,6 +23,8 @@ const txData: BlockExplorerTransactionResult = {
type: 'type',
command: {} as ValidatorHeartbeat,
signature: {
version: '1',
algo: 'vega/ed25519',
value: '123',
},
};
@@ -11,6 +11,8 @@ export interface BlockExplorerTransactionResult {
cursor: string;
command: components['schemas']['blockexplorerv1transaction'];
signature: {
version: string;
algo: string;
value: string;
};
error?: string;
+14
View File
@@ -3,6 +3,9 @@
// expect(element).toHaveTextContent(/react/i)
// learn more: https://github.com/testing-library/jest-dom
import '@testing-library/jest-dom';
import { locales } from '@vegaprotocol/i18n';
import i18n from 'i18next';
import { initReactI18next } from 'react-i18next';
Object.defineProperty(window, 'ResizeObserver', {
writable: false,
@@ -13,3 +16,14 @@ Object.defineProperty(window, 'ResizeObserver', {
disconnect: jest.fn(),
})),
});
// Set up i18n instance so that components have the correct default
// en translations
i18n.use(initReactI18next).init({
// we init with resources
resources: locales,
fallbackLng: 'en',
nsSeparator: false,
ns: ['explorer'],
defaultNS: 'explorer',
});
+1
View File
@@ -0,0 +1 @@
../../../../libs/i18n/src/locales
+45
View File
@@ -0,0 +1,45 @@
import type { Module } from 'i18next';
import i18n from 'i18next';
import HttpBackend from 'i18next-http-backend';
import LocizeBackend from 'i18next-locize-backend';
import LanguageDetector from 'i18next-browser-languagedetector';
import { initReactI18next } from 'react-i18next';
const isInDev = process.env.NODE_ENV === 'development';
const useLocize = isInDev && !!process.env.NX_USE_LOCIZE;
const backend = useLocize
? {
projectId: '96ac1231-4bdd-455a-b9d7-f5322a2e7430',
apiKey: process.env.NX_LOCIZE_API_KEY,
referenceLng: 'en',
}
: {
loadPath: '/assets/locales/{{lng}}/{{ns}}.json',
};
const Backend: Module = useLocize ? LocizeBackend : HttpBackend;
i18n
.use(Backend)
.use(LanguageDetector)
.use(initReactI18next)
.init({
lng: 'en',
fallbackLng: 'en',
supportedLngs: ['en'],
load: 'languageOnly',
debug: isInDev,
// have a common namespace used around the full app
ns: ['explorer'],
defaultNS: 'explorer',
keySeparator: false, // we use content as keys
nsSeparator: false,
backend,
saveMissing: useLocize && !!process.env.NX_LOCIZE_API_KEY,
interpolation: {
escapeValue: false,
},
});
export default i18n;
@@ -133,7 +133,7 @@ export const proposalsData = {
instrument: {
name: 'UNIDAI Monthly (Dec 2022)',
code: 'UNIDAI.MF21',
futureProduct: {
product: {
settlementAsset: { symbol: 'tDAI', __typename: 'Asset' },
__typename: 'FutureProduct',
},
@@ -240,7 +240,7 @@ export const proposalsData = {
instrument: {
name: 'ETHBTC Quarterly (Feb 2023)',
code: 'ETHBTC.QM21',
futureProduct: {
product: {
settlementAsset: { symbol: 'tBTC', __typename: 'Asset' },
__typename: 'FutureProduct',
},
+1 -2
View File
@@ -35,6 +35,5 @@ NX_SUCCESSOR_MARKETS=true
NX_METAMASK_SNAPS=true
NX_PRODUCT_PERPETUALS=false
NX_UPDATE_MARKET_STATE=false
NX_REFERRALS=false
NX_REFERRALS=true
NX_GOVERNANCE_TRANSFERS=false
NX_VOLUME_DISCOUNTS=false
+2 -3
View File
@@ -31,9 +31,8 @@ NX_TENDERMINT_WEBSOCKET_URL=wss://localhost:26617/websocket
CYPRESS_FAIRGROUND=false
# Cosmic elevator flags
NX_SUCCESSOR_MARKETS=false
NX_METAMASK_SNAPS=false
NX_SUCCESSOR_MARKETS=true
NX_METAMASK_SNAPS=true
NX_PRODUCT_PERPETUALS=true
NX_UPDATE_MARKET_STATE=true
NX_REFERRALS=true
NX_VOLUME_DISCOUNTS=true
-1
View File
@@ -28,4 +28,3 @@ NX_METAMASK_SNAPS=true
NX_PRODUCT_PERPETUALS=true
NX_UPDATE_MARKET_STATE=true
NX_REFERRALS=true
NX_VOLUME_DISCOUNTS=true
+5 -6
View File
@@ -22,9 +22,8 @@ NX_TENDERMINT_URL=https://be.vega.community
NX_TENDERMINT_WEBSOCKET_URL=wss://be.vega.community/websocket
# Cosmic elevator flags
NX_SUCCESSOR_MARKETS=false
NX_METAMASK_SNAPS=false
NX_PRODUCT_PERPETUALS=false
NX_UPDATE_MARKET_STATE=false
NX_REFERRALS=false
NX_VOLUME_DISCOUNTS=false
NX_SUCCESSOR_MARKETS=true
NX_METAMASK_SNAPS=true
NX_PRODUCT_PERPETUALS=true
NX_UPDATE_MARKET_STATE=true
NX_REFERRALS=true
+5 -6
View File
@@ -21,9 +21,8 @@ NX_TENDERMINT_URL=https://be.mainnet-mirror.vega.rocks
NX_TENDERMINT_WEBSOCKET_URL=wss://be.mainnet-mirror.vega.rocks/websocket
# Cosmic elevator flags
NX_SUCCESSOR_MARKETS=false
NX_METAMASK_SNAPS=false
NX_PRODUCT_PERPETUALS=false
NX_UPDATE_MARKET_STATE=false
NX_REFERRALS=false
NX_VOLUME_DISCOUNTS=false
NX_SUCCESSOR_MARKETS=true
NX_METAMASK_SNAPS=true
NX_PRODUCT_PERPETUALS=true
NX_UPDATE_MARKET_STATE=true
NX_REFERRALS=true
-1
View File
@@ -25,4 +25,3 @@ NX_PRODUCT_PERPETUALS=true
NX_UPDATE_MARKET_STATE=true
NX_REFERRALS=true
NX_GOVERNANCE_TRANSFERS=true
NX_VOLUME_DISCOUNTS=true
-1
View File
@@ -29,4 +29,3 @@ NX_METAMASK_SNAPS=true
NX_PRODUCT_PERPETUALS=true
NX_UPDATE_MARKET_STATE=true
NX_REFERRALS=true
NX_VOLUME_DISCOUNTS=true
+5 -6
View File
@@ -20,9 +20,8 @@ NX_TENDERMINT_URL=https://tm.be.validators-testnet.vega.rocks
NX_TENDERMINT_WEBSOCKET_URL=wss://be.validators-testnet.vega.
# Cosmic elevator flags
NX_SUCCESSOR_MARKETS=false
NX_METAMASK_SNAPS=false
NX_PRODUCT_PERPETUALS=false
NX_UPDATE_MARKET_STATE=false
NX_REFERRALS=false
NX_VOLUME_DISCOUNTS=false
NX_SUCCESSOR_MARKETS=true
NX_METAMASK_SNAPS=true
NX_PRODUCT_PERPETUALS=true
NX_UPDATE_MARKET_STATE=true
NX_REFERRALS=true
+17 -9
View File
@@ -2,7 +2,7 @@ import * as Sentry from '@sentry/react';
import { toBigNum } from '@vegaprotocol/utils';
import { Splash } from '@vegaprotocol/ui-toolkit';
import { useVegaWallet, useEagerConnect } from '@vegaprotocol/wallet';
import { FLAGS, useEnvironment } from '@vegaprotocol/environment';
import { useFeatureFlags, useEnvironment } from '@vegaprotocol/environment';
import { useWeb3React } from '@web3-react/core';
import React, { Suspense } from 'react';
import { useTranslation } from 'react-i18next';
@@ -15,21 +15,23 @@ import {
} from './contexts/app-state/app-state-context';
import { useContracts } from './contexts/contracts/contracts-context';
import { useRefreshAssociatedBalances } from './hooks/use-refresh-associated-balances';
import { Connectors } from './lib/vega-connectors';
import { useConnectors } from './lib/vega-connectors';
import { useSearchParams } from 'react-router-dom';
const useVegaWalletEagerConnect = () => {
const vegaConnecting = useEagerConnect(Connectors);
const connectors = useConnectors();
const vegaConnecting = useEagerConnect(connectors);
const { pubKey, connect } = useVegaWallet();
const [searchParams] = useSearchParams();
const [query] = React.useState(searchParams.get('address'));
if (query && !pubKey) {
connect(Connectors['view']);
connect(connectors.view);
}
return vegaConnecting;
};
export const AppLoader = ({ children }: { children: React.ReactElement }) => {
const featureFlags = useFeatureFlags((state) => state.flags);
const { t } = useTranslation();
const { account } = useWeb3React();
const { VEGA_URL } = useEnvironment();
@@ -79,10 +81,16 @@ export const AppLoader = ({ children }: { children: React.ReactElement }) => {
}
};
if (!FLAGS.GOVERNANCE_NETWORK_DOWN) {
if (!featureFlags.GOVERNANCE_NETWORK_DOWN) {
run();
}
}, [token, appDispatch, staking, vesting]);
}, [
token,
appDispatch,
staking,
vesting,
featureFlags.GOVERNANCE_NETWORK_DOWN,
]);
React.useEffect(() => {
if (account && pubKey) {
@@ -147,16 +155,16 @@ export const AppLoader = ({ children }: { children: React.ReactElement }) => {
};
// Only begin polling if network limits flag is set, as this is a new API not yet on mainnet 7/3/22
if (FLAGS.GOVERNANCE_NETWORK_LIMITS) {
if (featureFlags.GOVERNANCE_NETWORK_LIMITS) {
getNetworkLimits();
}
return () => {
stopPoll();
};
}, [appDispatch, VEGA_URL, t]);
}, [appDispatch, VEGA_URL, t, featureFlags.GOVERNANCE_NETWORK_LIMITS]);
if (FLAGS.GOVERNANCE_NETWORK_DOWN) {
if (featureFlags.GOVERNANCE_NETWORK_DOWN) {
return (
<Splash>
<SplashError />
@@ -23,10 +23,13 @@ export const Heading = ({
})}
>
<h1
className={classNames('font-alpha calt text-5xl break-words', {
'mt-0': !marginTop,
'mb-0': !marginBottom,
})}
className={classNames(
'font-alpha calt text-5xl [word-break:break-word]',
{
'mt-0': !marginTop,
'mb-0': !marginBottom,
}
)}
>
{title}
</h1>
@@ -7,16 +7,16 @@ import {
AppStateActionType,
useAppState,
} from '../../contexts/app-state/app-state-context';
import { Connectors } from '../../lib/vega-connectors';
import { useConnectors } from '../../lib/vega-connectors';
import { RiskMessage } from './risk-message';
export const VegaWalletDialogs = () => {
const { appState, appDispatch } = useAppState();
const connectors = useConnectors();
return (
<>
<VegaConnectDialog
connectors={Connectors}
connectors={connectors}
riskMessage={<RiskMessage />}
/>
@@ -30,7 +30,7 @@ export const VegaWalletDialogs = () => {
}
/>
<ViewAsDialog connector={Connectors.view} />
<ViewAsDialog connector={connectors.view} />
</>
);
};
@@ -49,12 +49,8 @@ export const ContractsProvider = ({ children }: { children: JSX.Element }) => {
? activeProvider
: defaultProvider;
if (
account &&
activeProvider &&
typeof activeProvider.getSigner === 'function'
) {
signer = provider.getSigner();
if (account && provider && typeof provider.getSigner === 'function') {
signer = provider.getSigner(account);
}
const tokenVestingAddress =
+14 -9
View File
@@ -1,4 +1,5 @@
import { FLAGS } from '@vegaprotocol/environment';
import { useFeatureFlags } from '@vegaprotocol/environment';
import { useMemo } from 'react';
import {
JsonRpcConnector,
ViewConnector,
@@ -13,13 +14,17 @@ export const jsonRpc = new JsonRpcConnector();
export const injected = new InjectedConnector();
export const view = new ViewConnector(urlParams.get('address'));
export const snap = FLAGS.METAMASK_SNAPS
? new SnapConnector(DEFAULT_SNAP_ID)
: undefined;
export const snap = new SnapConnector(DEFAULT_SNAP_ID);
export const Connectors = {
injected,
jsonRpc,
view,
snap,
export const useConnectors = () => {
const featureFlags = useFeatureFlags((state) => state.flags);
return useMemo(
() => ({
injected,
jsonRpc,
view,
snap: featureFlags.METAMASK_SNAPS ? snap : undefined,
}),
[featureFlags.METAMASK_SNAPS]
);
};
+5 -5
View File
@@ -12,7 +12,7 @@ import { useRefreshAfterEpoch } from '../../hooks/use-refresh-after-epoch';
import { ProposalsListItem } from '../proposals/components/proposals-list-item';
import { ProtocolUpgradeProposalsListItem } from '../proposals/components/protocol-upgrade-proposals-list-item/protocol-upgrade-proposals-list-item';
import Routes from '../routes';
import { ExternalLinks, FLAGS } from '@vegaprotocol/environment';
import { ExternalLinks, useFeatureFlags } from '@vegaprotocol/environment';
import { removePaginationWrapper } from '@vegaprotocol/utils';
import { useNodesQuery } from '../staking/home/__generated__/Nodes';
import { useProposalsQuery } from '../proposals/proposals/__generated__/Proposals';
@@ -175,6 +175,7 @@ export const ValidatorDetailsLink = ({
};
const GovernanceHome = ({ name }: RouteChildProps) => {
const featureFlags = useFeatureFlags((state) => state.flags);
useDocumentTitle(name);
const { t } = useTranslation();
const {
@@ -186,10 +187,9 @@ const GovernanceHome = ({ name }: RouteChildProps) => {
fetchPolicy: 'network-only',
errorPolicy: 'ignore',
variables: {
includeNewMarketProductFields: !!FLAGS.PRODUCT_PERPETUALS,
includeUpdateMarketStates: !!FLAGS.UPDATE_MARKET_STATE,
includeUpdateReferralPrograms: !!FLAGS.REFERRALS,
includeUpdateVolumeDiscountPrograms: !!FLAGS.VOLUME_DISCOUNTS,
includeNewMarketProductFields: !!featureFlags.PRODUCT_PERPETUALS,
includeUpdateMarketStates: !!featureFlags.UPDATE_MARKET_STATE,
includeUpdateReferralPrograms: !!featureFlags.REFERRALS,
},
});
@@ -7,8 +7,10 @@ import type { AssetFieldsFragment } from '@vegaprotocol/assets';
export const ProposalAssetDetails = ({
asset,
originalAsset,
}: {
asset: AssetFieldsFragment;
originalAsset?: AssetFieldsFragment;
}) => {
const { t } = useTranslation();
const [showAssetDetails, setShowAssetDetails] = useState(false);
@@ -27,6 +29,7 @@ export const ProposalAssetDetails = ({
<div className="mb-10 pb-4">
<AssetDetailsTable
asset={asset}
originalAsset={originalAsset}
omitRows={[
AssetDetail.STATUS,
AssetDetail.INFRASTRUCTURE_FEE_ACCOUNT_BALANCE,
@@ -19,7 +19,7 @@ import {
mockWalletContext,
createUserVoteQueryMock,
} from '../../test-helpers/mocks';
import { FLAGS } from '@vegaprotocol/environment';
import { useFeatureFlags } from '@vegaprotocol/environment';
import { BrowserRouter } from 'react-router-dom';
import { VoteState } from '../vote-details/use-user-vote';
import { useNewTransferProposalDetails } from '@vegaprotocol/proposals';
@@ -62,8 +62,7 @@ describe('Proposal header', () => {
jest.clearAllMocks();
});
it('Renders New market proposal', () => {
const mockedFlags = jest.mocked(FLAGS);
mockedFlags.SUCCESSOR_MARKETS = true;
useFeatureFlags.setState({ flags: { SUCCESSOR_MARKETS: true } });
renderComponent(
generateProposal({
rationale: {
@@ -77,7 +76,7 @@ describe('Proposal header', () => {
__typename: 'InstrumentConfiguration',
name: 'Some market',
code: 'FX:BTCUSD/DEC99',
futureProduct: {
product: {
__typename: 'FutureProduct',
settlementAsset: {
__typename: 'Asset',
@@ -12,7 +12,7 @@ import {
useNewTransferProposalDetails,
useSuccessorMarketProposalDetails,
} from '@vegaprotocol/proposals';
import { FLAGS } from '@vegaprotocol/environment';
import { useFeatureFlags } from '@vegaprotocol/environment';
import Routes from '../../../routes';
import { Link } from 'react-router-dom';
import type { VoteState } from '../vote-details/use-user-vote';
@@ -28,6 +28,7 @@ export const ProposalHeader = ({
isListItem?: boolean;
voteState?: VoteState | null;
}) => {
const featureFlags = useFeatureFlags((state) => state.flags);
const { t } = useTranslation();
const change = proposal?.terms.change;
@@ -39,25 +40,38 @@ export const ProposalHeader = ({
const titleContent = shorten(title ?? '', 100);
const getAsset = (proposal: ProposalQuery['proposal']) => {
const terms = proposal?.terms;
if (
terms?.change.__typename === 'NewMarket' &&
(terms.change.instrument.product?.__typename === 'FutureProduct' ||
terms.change.instrument.product?.__typename === 'PerpetualProduct')
) {
return terms.change.instrument.product.settlementAsset;
}
return undefined;
};
switch (change?.__typename) {
case 'NewMarket': {
proposalType =
FLAGS.PRODUCT_PERPETUALS && change?.instrument?.product?.__typename
featureFlags.PRODUCT_PERPETUALS &&
change?.instrument?.product?.__typename
? `NewMarket${change?.instrument?.product?.__typename}`
: 'NewMarket';
fallbackTitle = t('NewMarketProposal');
details = (
<>
{FLAGS.SUCCESSOR_MARKETS && (
{featureFlags.SUCCESSOR_MARKETS && (
<SuccessorCode proposalId={proposal?.id} />
)}
<span>
{t('Code')}: {change.instrument.code}.
</span>{' '}
{change.instrument.futureProduct?.settlementAsset.symbol ? (
{proposal?.terms && getAsset(proposal)?.symbol ? (
<>
<span className="font-semibold">
{change.instrument.futureProduct.settlementAsset.symbol}
{getAsset(proposal)?.symbol}
</span>{' '}
{t('settled future')}.
</>
@@ -70,13 +84,13 @@ export const ProposalHeader = ({
}
case 'UpdateMarketState': {
proposalType =
FLAGS.UPDATE_MARKET_STATE && change?.updateType
featureFlags.UPDATE_MARKET_STATE && change?.updateType
? t(change.updateType)
: 'UpdateMarketState';
fallbackTitle = t('UpdateMarketStateProposal');
details = (
<span>
{FLAGS.UPDATE_MARKET_STATE &&
{featureFlags.UPDATE_MARKET_STATE &&
change?.market?.id &&
change.updateType ? (
<>
@@ -165,14 +179,14 @@ export const ProposalHeader = ({
case 'NewTransfer':
proposalType = 'NewTransfer';
fallbackTitle = t('NewTransferProposal');
details = FLAGS.GOVERNANCE_TRANSFERS ? (
details = featureFlags.GOVERNANCE_TRANSFERS ? (
<NewTransferSummary proposalId={proposal?.id} />
) : null;
break;
case 'CancelTransfer':
proposalType = 'CancelTransfer';
fallbackTitle = t('CancelTransferProposal');
details = FLAGS.GOVERNANCE_TRANSFERS ? (
details = featureFlags.GOVERNANCE_TRANSFERS ? (
<CancelTransferSummary proposalId={proposal?.id} />
) : null;
break;
@@ -54,8 +54,8 @@ export const ProposalReferralProgramDetails = ({
return null;
}
const benefitTiers = proposal?.terms?.change?.benefitTiers;
const stakingTiers = proposal?.terms?.change?.stakingTiers;
const benefitTiers = proposal?.terms?.change?.benefitTiers.slice();
const stakingTiers = proposal?.terms?.change?.stakingTiers.slice();
const windowLength = proposal?.terms?.change?.windowLength;
const endOfProgramTimestamp = proposal?.terms?.change?.endOfProgram;
@@ -78,7 +78,7 @@ export const ProposalVolumeDiscountProgramDetails = ({
{t('BenefitTiers')}
</h3>
<KeyValueTable>
{benefitTiers
{[...benefitTiers]
.sort(
(a, b) =>
Number(a.minimumRunningNotionalTakerVolume) -
@@ -26,7 +26,7 @@ import {
ProposalCancelTransferDetails,
ProposalTransferDetails,
} from '../proposal-transfer';
import { FLAGS } from '@vegaprotocol/environment';
import { useFeatureFlags } from '@vegaprotocol/environment';
import { ProposalUpdateBenefitTiers } from '../proposal-update-benefit-tiers';
export interface ProposalProps {
@@ -53,6 +53,7 @@ export const Proposal = ({
originalMarketProposalRestData,
mostRecentlyEnactedAssociatedMarketProposal,
}: ProposalProps) => {
const featureFlags = useFeatureFlags((state) => state.flags);
const { t } = useTranslation();
const { submit, Dialog, finalizedVote, transaction } = useVoteSubmit();
const { voteState, voteDatetime } = useUserVote(proposal?.id, finalizedVote);
@@ -65,10 +66,13 @@ export const Proposal = ({
? removePaginationWrapper(assetData.assetsConnection?.edges)[0]
: undefined;
const originalAsset = asset;
if (proposal.terms.change.__typename === 'UpdateAsset' && asset) {
asset = {
...asset,
quantum: proposal.terms.change.quantum,
source: { ...asset.source },
};
if (asset.source.__typename === 'ERC20') {
@@ -129,7 +133,7 @@ export const Proposal = ({
}
// Show governance transfer details only if the GOVERNANCE_TRANSFERS flag is on.
const governanceTransferDetails = FLAGS.GOVERNANCE_TRANSFERS && (
const governanceTransferDetails = featureFlags.GOVERNANCE_TRANSFERS && (
<>
{proposal.terms.change.__typename === 'NewTransfer' && (
/** Governance New Transfer Details */
@@ -228,7 +232,7 @@ export const Proposal = ({
proposal.terms.change.__typename === 'UpdateAsset') &&
asset && (
<div className="mb-4">
<ProposalAssetDetails asset={asset} />
<ProposalAssetDetails asset={asset} originalAsset={originalAsset} />
</div>
)}
@@ -1,7 +1,7 @@
import {
getProposalDialogIcon,
getProposalDialogIntent,
getProposalDialogTitle,
useGetProposalDialogTitle,
} from '@vegaprotocol/proposals';
import type { ProposalEventFieldsFragment } from '@vegaprotocol/proposals';
import type { DialogProps } from '@vegaprotocol/proposals';
@@ -15,6 +15,7 @@ export const ProposalFormTransactionDialog = ({
finalizedProposal,
TransactionDialog,
}: ProposalFormTransactionDialogProps) => {
const title = useGetProposalDialogTitle(finalizedProposal?.state);
// Render a custom complete UI if the proposal was rejected otherwise
// pass undefined so that the default vega transaction dialog UI gets used
const completeContent = finalizedProposal?.rejectionReason ? (
@@ -24,7 +25,7 @@ export const ProposalFormTransactionDialog = ({
return (
<div data-testid="proposal-transaction-dialog">
<TransactionDialog
title={getProposalDialogTitle(finalizedProposal?.state)}
title={title}
intent={getProposalDialogIntent(finalizedProposal?.state)}
icon={getProposalDialogIcon(finalizedProposal?.state)}
content={{
@@ -8,7 +8,7 @@ import {
networkParamsQueryMock,
nextWeek,
} from '../../test-helpers/mocks';
import { VoteBreakdown } from './vote-breakdown';
import { CompactVotes, VoteBreakdown } from './vote-breakdown';
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
import type { MockedResponse } from '@apollo/client/testing';
import {
@@ -281,8 +281,8 @@ describe('VoteBreakdown', () => {
});
it('Progress bar displays status - LP majority', () => {
const yesVotesLP = 800;
const noVotesLP = 200;
const yesVotesLP = 0.8;
const noVotesLP = 0.2;
const expectedProgress = (yesVotesLP / (yesVotesLP + noVotesLP)) * 100; // 80%
renderComponent(
@@ -346,3 +346,22 @@ describe('VoteBreakdown', () => {
expect(style.width).toBe(`${expectedProgress}%`);
});
});
describe('CompactVotes', () => {
it.each([
[0, '0'],
[1, '1'],
[12, '12'],
[123, '123'],
[1234, '1.2K'],
[12345, '12.3K'],
[123456, '123.5K'],
[1234567, '1.2M'],
[12345678, '12.3M'],
[123456789, '123.5M'],
[1234567890, '1.2B'],
])('compacts %s to %s', (input, output) => {
const { getByTestId } = render(<CompactVotes number={BigNumber(input)} />);
expect(getByTestId('compact-number').textContent).toBe(output);
});
});
@@ -3,11 +3,21 @@ import BigNumber from 'bignumber.js';
import { useTranslation } from 'react-i18next';
import { useVoteInformation } from '../../hooks';
import { Icon, Tooltip } from '@vegaprotocol/ui-toolkit';
import { formatNumber, toBigNum } from '@vegaprotocol/utils';
import { formatNumber } from '@vegaprotocol/utils';
import { ProposalState } from '@vegaprotocol/types';
import type { ReactNode } from 'react';
import type { ProposalFieldsFragment } from '../../proposals/__generated__/Proposals';
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
import { CompactNumber } from '@vegaprotocol/react-helpers';
export const CompactVotes = ({ number }: { number: BigNumber }) => (
<CompactNumber
number={number}
decimals={number.isGreaterThan(1000) ? 1 : 0}
compactAbove={1000}
compactDisplay="short"
/>
);
interface VoteBreakdownProps {
proposal: ProposalFieldsFragment | ProposalQuery['proposal'];
@@ -95,8 +105,6 @@ export const VoteBreakdown = ({ proposal }: VoteBreakdownProps) => {
yesLPPercentage,
yesTokens,
noTokens,
yesEquityLikeShareWeight,
noEquityLikeShareWeight,
totalEquityLikeShareWeight,
requiredMajorityPercentage,
requiredMajorityLPPercentage,
@@ -125,6 +133,7 @@ export const VoteBreakdown = ({ proposal }: VoteBreakdownProps) => {
.multipliedBy(100),
new BigNumber(100)
);
const willPass = willPassByTokenVote || willPassByLPVote;
const updateMarketVotePassMethod = willPassByTokenVote
? t('byTokenVote')
@@ -192,56 +201,24 @@ export const VoteBreakdown = ({ proposal }: VoteBreakdownProps) => {
<div className="flex items-center gap-1">
<span>{t('liquidityProviderVotesFor')}:</span>
<Tooltip
description={formatNumber(
yesEquityLikeShareWeight,
defaultDP
)}
description={
<span>{yesLPPercentage.toFixed(defaultDP)}%</span>
}
>
<button>
{yesEquityLikeShareWeight
.dividedBy(toBigNum(10 ** 6, 0))
.toFixed(1)}
M
</button>
<button>{yesLPPercentage.toFixed(1)}%</button>
</Tooltip>
<span>
(
<Tooltip
description={
<span>{yesLPPercentage.toFixed(defaultDP)}%</span>
}
>
<button>{yesLPPercentage.toFixed(0)}%</button>
</Tooltip>
)
</span>
</div>
<div className="flex items-center gap-1">
<span>{t('liquidityProviderVotesAgainst')}:</span>
<Tooltip
description={formatNumber(
noEquityLikeShareWeight,
defaultDP
)}
>
<button>
{noEquityLikeShareWeight
.dividedBy(toBigNum(10 ** 6, 0))
.toFixed(1)}
M
</button>
</Tooltip>
<span>
(
<Tooltip
description={
<span>{noLPPercentage.toFixed(defaultDP)}%</span>
}
>
<button>{noLPPercentage.toFixed(0)}%</button>
<button>{noLPPercentage.toFixed(1)}%</button>
</Tooltip>
)
</span>
</div>
</div>
@@ -278,16 +255,8 @@ export const VoteBreakdown = ({ proposal }: VoteBreakdownProps) => {
defaultDP
)}
>
<button>
{totalEquityLikeShareWeight
.dividedBy(toBigNum(10 ** 6, 0))
.toFixed(1)}
M
</button>
<span>{totalEquityLikeShareWeight.toFixed(1)}%</span>
</Tooltip>
<span>
({totalEquityLikeShareWeight.toFixed(defaultDP)}%)
</span>
</div>
</div>
</section>
@@ -321,7 +290,7 @@ export const VoteBreakdown = ({ proposal }: VoteBreakdownProps) => {
<span>{t('tokenVotesFor')}:</span>
<Tooltip description={formatNumber(yesTokens, defaultDP)}>
<button data-testid="num-votes-for">
{yesTokens.dividedBy(toBigNum(10 ** 6, 0)).toFixed(1)}M
<CompactVotes number={yesTokens} />
</button>
</Tooltip>
<span>
@@ -341,7 +310,7 @@ export const VoteBreakdown = ({ proposal }: VoteBreakdownProps) => {
<span>{t('tokenVotesAgainst')}:</span>
<Tooltip description={formatNumber(noTokens, defaultDP)}>
<button data-testid="num-votes-against">
{noTokens.dividedBy(toBigNum(10 ** 6, 0)).toFixed(1)}M
<CompactVotes number={noTokens} />
</button>
</Tooltip>
<span>
@@ -384,7 +353,7 @@ export const VoteBreakdown = ({ proposal }: VoteBreakdownProps) => {
<span>{t('totalTokensVoted')}:</span>
<Tooltip description={formatNumber(totalTokensVoted, defaultDP)}>
<button data-testid="total-voted">
{totalTokensVoted.dividedBy(toBigNum(10 ** 6, 0)).toFixed(1)}M
<CompactVotes number={totalTokensVoted} />
</button>
</Tooltip>
<span data-testid="total-voted-percentage">
@@ -54,8 +54,8 @@ describe('use-vote-information', () => {
it('returns all required vote information', () => {
const yesVotes = 40;
const noVotes = 60;
const yesEquityLikeShareWeight = '30';
const noEquityLikeShareWeight = '70';
const yesEquityLikeShareWeight = '0.30';
const noEquityLikeShareWeight = '0.70';
// Note - giving a fixedTokenValue of 1 means a ratio of 1:1 votes to tokens, making sums easier :)
const fixedTokenValue = 1000000000000000000;
@@ -195,10 +195,10 @@ describe('use-vote-information', () => {
});
it('correctly shows whether an update market proposal will pass by token or LP vote - both failing', () => {
const yesVotes = 20;
const noVotes = 70;
const yesEquityLikeShareWeight = '30';
const noEquityLikeShareWeight = '60';
const yesVotes = 0.2;
const noVotes = 0.7;
const yesEquityLikeShareWeight = '0.30';
const noEquityLikeShareWeight = '0.60';
const fixedTokenValue = 1000000000000000000;
const proposal = generateProposal({
@@ -61,7 +61,7 @@ export const useVoteInformation = ({
const noEquityLikeShareWeight = !proposal?.votes.no
.totalEquityLikeShareWeight
? new BigNumber(0)
: new BigNumber(proposal.votes.no.totalEquityLikeShareWeight);
: new BigNumber(proposal.votes.no.totalEquityLikeShareWeight).times(100);
const yesTokens = new BigNumber(
addDecimal(proposal?.votes.yes.totalTokens ?? 0, decimals)
@@ -70,7 +70,7 @@ export const useVoteInformation = ({
const yesEquityLikeShareWeight = !proposal?.votes.yes
.totalEquityLikeShareWeight
? new BigNumber(0)
: new BigNumber(proposal.votes.yes.totalEquityLikeShareWeight);
: new BigNumber(proposal.votes.yes.totalEquityLikeShareWeight).times(100);
const totalTokensVoted = yesTokens.plus(noTokens);
@@ -81,12 +81,7 @@ export const useVoteInformation = ({
const yesPercentage = totalTokensVoted.isZero()
? new BigNumber(0)
: yesTokens.multipliedBy(100).dividedBy(totalTokensVoted);
const yesLPPercentage = totalEquityLikeShareWeight.isZero()
? new BigNumber(0)
: yesEquityLikeShareWeight
.multipliedBy(100)
.dividedBy(totalEquityLikeShareWeight);
const yesLPPercentage = yesEquityLikeShareWeight;
const noPercentage = totalTokensVoted.isZero()
? new BigNumber(0)
@@ -103,9 +98,7 @@ export const useVoteInformation = ({
);
const participationLPMet = requiredParticipationLP
? totalEquityLikeShareWeight.isGreaterThan(
totalSupply.multipliedBy(requiredParticipationLP)
)
? totalEquityLikeShareWeight.isGreaterThan(requiredParticipationLP)
: false;
const majorityMet = yesPercentage.isGreaterThanOrEqualTo(
@@ -120,9 +113,7 @@ export const useVoteInformation = ({
.multipliedBy(100)
.dividedBy(totalSupply);
const totalLPTokensPercentage = totalEquityLikeShareWeight
.multipliedBy(100)
.dividedBy(totalSupply);
const totalLPTokensPercentage = totalEquityLikeShareWeight;
const willPassByTokenVote =
participationMet &&
@@ -84,7 +84,6 @@ query Proposal(
$includeNewMarketProductField: Boolean!
$includeUpdateMarketState: Boolean!
$includeUpdateReferralProgram: Boolean!
$includeUpdateVolumeDiscountProgram: Boolean!
) {
proposal(id: $proposalId) {
id
@@ -104,7 +103,6 @@ query Proposal(
...UpdateMarketState @include(if: $includeUpdateMarketState)
...UpdateReferralProgram @include(if: $includeUpdateReferralProgram)
...UpdateVolumeDiscountProgram
@include(if: $includeUpdateVolumeDiscountProgram)
terms {
closingDatetime
enactmentDatetime
@@ -132,46 +130,25 @@ query Proposal(
instrument {
name
code
futureProduct {
settlementAsset {
id
name
symbol
decimals
quantum
}
quoteName
dataSourceSpecForSettlementData {
sourceType {
... on DataSourceDefinitionInternal {
sourceType {
... on DataSourceSpecConfigurationTime {
conditions {
operator
value
}
}
}
}
... on DataSourceDefinitionExternal {
sourceType {
... on DataSourceSpecConfiguration {
signers {
signer {
... on PubKey {
key
}
... on ETHAddress {
address
}
}
}
filters {
key {
name
type
}
product {
... on FutureProduct {
settlementAsset {
id
name
symbol
decimals
quantum
}
quoteName
dataSourceSpecBinding {
settlementDataProperty
tradingTerminationProperty
}
dataSourceSpecForSettlementData {
sourceType {
... on DataSourceDefinitionInternal {
sourceType {
... on DataSourceSpecConfigurationTime {
conditions {
operator
value
@@ -179,52 +156,44 @@ query Proposal(
}
}
}
... on DataSourceDefinitionExternal {
sourceType {
... on DataSourceSpecConfiguration {
signers {
signer {
... on PubKey {
key
}
... on ETHAddress {
address
}
}
}
filters {
key {
name
type
}
conditions {
operator
value
}
}
}
}
}
}
}
}
# dataSourceSpecForTradingTermination {
# sourceType {
# ... on DataSourceDefinitionInternal {
# sourceType {
# ... on DataSourceSpecConfigurationTime {
# conditions {
# operator
# value
# }
# }
# }
# }
# ... on DataSourceDefinitionExternal {
# sourceType {
# ... on DataSourceSpecConfiguration {
# signers {
# signer {
# ... on PubKey {
# key
# }
# ... on ETHAddress {
# address
# }
# }
# }
# filters {
# key {
# name
# type
# }
# conditions {
# operator
# value
# }
# }
# }
# }
# }
# }
# }
dataSourceSpecBinding {
settlementDataProperty
tradingTerminationProperty
... on PerpetualProduct {
settlementAsset {
id
name
symbol
decimals
quantum
}
quoteName
}
}
}
File diff suppressed because one or more lines are too long
@@ -15,10 +15,11 @@ import {
useNetworkParams,
} from '@vegaprotocol/network-parameters';
import { useParentMarketIdQuery } from '@vegaprotocol/markets';
import { FLAGS } from '@vegaprotocol/environment';
import { useFeatureFlags } from '@vegaprotocol/environment';
import { useSuccessorMarketProposalDetails } from '@vegaprotocol/proposals';
export const ProposalContainer = () => {
const featureFlags = useFeatureFlags((state) => state.flags);
const [
mostRecentlyEnactedAssociatedMarketProposal,
setMostRecentlyEnactedAssociatedMarketProposal,
@@ -59,10 +60,9 @@ export const ProposalContainer = () => {
errorPolicy: 'ignore',
variables: {
proposalId: params.proposalId || '',
includeNewMarketProductField: !!FLAGS.PRODUCT_PERPETUALS,
includeUpdateMarketState: !!FLAGS.UPDATE_MARKET_STATE,
includeUpdateReferralProgram: !!FLAGS.REFERRALS,
includeUpdateVolumeDiscountProgram: !!FLAGS.VOLUME_DISCOUNTS,
includeNewMarketProductField: !!featureFlags.PRODUCT_PERPETUALS,
includeUpdateMarketState: !!featureFlags.UPDATE_MARKET_STATE,
includeUpdateReferralProgram: !!featureFlags.REFERRALS,
},
skip: !params.proposalId,
});
@@ -121,7 +121,7 @@ export const ProposalContainer = () => {
variables: {
marketId: marketData?.id || '',
},
skip: !FLAGS.SUCCESSOR_MARKETS || !isSuccessor || !marketData?.id,
skip: !featureFlags.SUCCESSOR_MARKETS || !isSuccessor || !marketData?.id,
});
const {
@@ -134,7 +134,7 @@ export const ProposalContainer = () => {
variables: {
marketId: parentMarketId?.market?.parentMarketID || '',
skip:
!FLAGS.SUCCESSOR_MARKETS ||
!featureFlags.SUCCESSOR_MARKETS ||
!isSuccessor ||
!parentMarketId?.market?.parentMarketID,
},
@@ -101,9 +101,16 @@ fragment ProposalFields on Proposal {
instrument {
name
code
futureProduct {
settlementAsset {
symbol
product {
... on FutureProduct {
settlementAsset {
symbol
}
}
... on PerpetualProduct {
settlementAsset {
symbol
}
}
}
}
@@ -164,7 +171,6 @@ query Proposals(
$includeNewMarketProductFields: Boolean!
$includeUpdateMarketStates: Boolean!
$includeUpdateReferralPrograms: Boolean!
$includeUpdateVolumeDiscountPrograms: Boolean!
) {
proposalsConnection {
edges {
@@ -174,7 +180,6 @@ query Proposals(
...UpdateMarketStates @include(if: $includeUpdateMarketStates)
...UpdateReferralPrograms @include(if: $includeUpdateReferralPrograms)
...UpdateVolumeDiscountPrograms
@include(if: $includeUpdateVolumeDiscountPrograms)
}
}
}
@@ -11,17 +11,16 @@ export type UpdateReferralProgramsFragment = { __typename?: 'Proposal', terms: {
export type UpdateVolumeDiscountProgramsFragment = { __typename?: 'Proposal', terms: { __typename?: 'ProposalTerms', change: { __typename?: 'CancelTransfer' } | { __typename?: 'NewAsset' } | { __typename?: 'NewFreeform' } | { __typename?: 'NewMarket' } | { __typename?: 'NewSpotMarket' } | { __typename?: 'NewTransfer' } | { __typename?: 'UpdateAsset' } | { __typename?: 'UpdateMarket' } | { __typename?: 'UpdateMarketState' } | { __typename?: 'UpdateNetworkParameter' } | { __typename?: 'UpdateReferralProgram' } | { __typename?: 'UpdateSpotMarket' } | { __typename?: 'UpdateVolumeDiscountProgram', endOfProgramTimestamp: any, windowLength: number, benefitTiers: Array<{ __typename?: 'VolumeBenefitTier', minimumRunningNotionalTakerVolume: string, volumeDiscountFactor: string }> } } };
export type ProposalFieldsFragment = { __typename?: 'Proposal', id?: string | null, reference: string, state: Types.ProposalState, datetime: any, rejectionReason?: Types.ProposalRejectionReason | null, errorDetails?: string | null, rationale: { __typename?: 'ProposalRationale', title: string, description: string }, party: { __typename?: 'Party', id: string }, terms: { __typename?: 'ProposalTerms', closingDatetime: any, enactmentDatetime?: any | null, change: { __typename?: 'CancelTransfer' } | { __typename: 'NewAsset', name: string, symbol: string, decimals: number, quantum: string, source: { __typename?: 'BuiltinAsset', maxFaucetAmountMint: string } | { __typename?: 'ERC20', contractAddress: string, withdrawThreshold: string, lifetimeLimit: string } } | { __typename?: 'NewFreeform' } | { __typename?: 'NewMarket', instrument: { __typename?: 'InstrumentConfiguration', name: string, code: string, futureProduct?: { __typename?: 'FutureProduct', settlementAsset: { __typename?: 'Asset', symbol: string } } | null } } | { __typename?: 'NewSpotMarket' } | { __typename?: 'NewTransfer' } | { __typename?: 'UpdateAsset', quantum: string, assetId: string, source: { __typename?: 'UpdateERC20', lifetimeLimit: string, withdrawThreshold: string } } | { __typename?: 'UpdateMarket', marketId: string } | { __typename?: 'UpdateMarketState' } | { __typename?: 'UpdateNetworkParameter', networkParameter: { __typename?: 'NetworkParameter', key: string, value: string } } | { __typename?: 'UpdateReferralProgram' } | { __typename?: 'UpdateSpotMarket' } | { __typename?: 'UpdateVolumeDiscountProgram' } }, votes: { __typename?: 'ProposalVotes', yes: { __typename?: 'ProposalVoteSide', totalTokens: string, totalNumber: string, totalEquityLikeShareWeight: string }, no: { __typename?: 'ProposalVoteSide', totalTokens: string, totalNumber: string, totalEquityLikeShareWeight: string } } };
export type ProposalFieldsFragment = { __typename?: 'Proposal', id?: string | null, reference: string, state: Types.ProposalState, datetime: any, rejectionReason?: Types.ProposalRejectionReason | null, errorDetails?: string | null, rationale: { __typename?: 'ProposalRationale', title: string, description: string }, party: { __typename?: 'Party', id: string }, terms: { __typename?: 'ProposalTerms', closingDatetime: any, enactmentDatetime?: any | null, change: { __typename?: 'CancelTransfer' } | { __typename: 'NewAsset', name: string, symbol: string, decimals: number, quantum: string, source: { __typename?: 'BuiltinAsset', maxFaucetAmountMint: string } | { __typename?: 'ERC20', contractAddress: string, withdrawThreshold: string, lifetimeLimit: string } } | { __typename?: 'NewFreeform' } | { __typename?: 'NewMarket', instrument: { __typename?: 'InstrumentConfiguration', name: string, code: string, product?: { __typename?: 'FutureProduct', settlementAsset: { __typename?: 'Asset', symbol: string } } | { __typename?: 'PerpetualProduct', settlementAsset: { __typename?: 'Asset', symbol: string } } | { __typename?: 'SpotProduct' } | null } } | { __typename?: 'NewSpotMarket' } | { __typename?: 'NewTransfer' } | { __typename?: 'UpdateAsset', quantum: string, assetId: string, source: { __typename?: 'UpdateERC20', lifetimeLimit: string, withdrawThreshold: string } } | { __typename?: 'UpdateMarket', marketId: string } | { __typename?: 'UpdateMarketState' } | { __typename?: 'UpdateNetworkParameter', networkParameter: { __typename?: 'NetworkParameter', key: string, value: string } } | { __typename?: 'UpdateReferralProgram' } | { __typename?: 'UpdateSpotMarket' } | { __typename?: 'UpdateVolumeDiscountProgram' } }, votes: { __typename?: 'ProposalVotes', yes: { __typename?: 'ProposalVoteSide', totalTokens: string, totalNumber: string, totalEquityLikeShareWeight: string }, no: { __typename?: 'ProposalVoteSide', totalTokens: string, totalNumber: string, totalEquityLikeShareWeight: string } } };
export type ProposalsQueryVariables = Types.Exact<{
includeNewMarketProductFields: Types.Scalars['Boolean'];
includeUpdateMarketStates: Types.Scalars['Boolean'];
includeUpdateReferralPrograms: Types.Scalars['Boolean'];
includeUpdateVolumeDiscountPrograms: Types.Scalars['Boolean'];
}>;
export type ProposalsQuery = { __typename?: 'Query', proposalsConnection?: { __typename?: 'ProposalsConnection', edges?: Array<{ __typename?: 'ProposalEdge', node: { __typename?: 'Proposal', id?: string | null, reference: string, state: Types.ProposalState, datetime: any, rejectionReason?: Types.ProposalRejectionReason | null, errorDetails?: string | null, rationale: { __typename?: 'ProposalRationale', title: string, description: string }, party: { __typename?: 'Party', id: string }, terms: { __typename?: 'ProposalTerms', closingDatetime: any, enactmentDatetime?: any | null, change: { __typename?: 'CancelTransfer' } | { __typename: 'NewAsset', name: string, symbol: string, decimals: number, quantum: string, source: { __typename?: 'BuiltinAsset', maxFaucetAmountMint: string } | { __typename?: 'ERC20', contractAddress: string, withdrawThreshold: string, lifetimeLimit: string } } | { __typename?: 'NewFreeform' } | { __typename?: 'NewMarket', instrument: { __typename?: 'InstrumentConfiguration', name: string, code: string, futureProduct?: { __typename?: 'FutureProduct', settlementAsset: { __typename?: 'Asset', symbol: string } } | null, product?: { __typename: 'FutureProduct' } | { __typename: 'PerpetualProduct' } | { __typename: 'SpotProduct' } | null } } | { __typename?: 'NewSpotMarket' } | { __typename?: 'NewTransfer' } | { __typename?: 'UpdateAsset', quantum: string, assetId: string, source: { __typename?: 'UpdateERC20', lifetimeLimit: string, withdrawThreshold: string } } | { __typename?: 'UpdateMarket', marketId: string } | { __typename?: 'UpdateMarketState', updateType: Types.MarketUpdateType, price?: string | null, market: { __typename?: 'Market', decimalPlaces: number, id: string, tradableInstrument: { __typename?: 'TradableInstrument', instrument: { __typename?: 'Instrument', name: string, code: string, product: { __typename: 'Future', quoteName: string } | { __typename: 'Perpetual', quoteName: string } | { __typename: 'Spot' } } } } } | { __typename?: 'UpdateNetworkParameter', networkParameter: { __typename?: 'NetworkParameter', key: string, value: string } } | { __typename?: 'UpdateReferralProgram', windowLength: number, endOfProgram: any, benefitTiers: Array<{ __typename?: 'BenefitTier', minimumEpochs: number, minimumRunningNotionalTakerVolume: string, referralDiscountFactor: string, referralRewardFactor: string }>, stakingTiers: Array<{ __typename?: 'StakingTier', minimumStakedTokens: string, referralRewardMultiplier: string }> } | { __typename?: 'UpdateSpotMarket' } | { __typename?: 'UpdateVolumeDiscountProgram', endOfProgramTimestamp: any, windowLength: number, benefitTiers: Array<{ __typename?: 'VolumeBenefitTier', minimumRunningNotionalTakerVolume: string, volumeDiscountFactor: string }> } }, votes: { __typename?: 'ProposalVotes', yes: { __typename?: 'ProposalVoteSide', totalTokens: string, totalNumber: string, totalEquityLikeShareWeight: string }, no: { __typename?: 'ProposalVoteSide', totalTokens: string, totalNumber: string, totalEquityLikeShareWeight: string } } } } | null> | null } | null };
export type ProposalsQuery = { __typename?: 'Query', proposalsConnection?: { __typename?: 'ProposalsConnection', edges?: Array<{ __typename?: 'ProposalEdge', node: { __typename?: 'Proposal', id?: string | null, reference: string, state: Types.ProposalState, datetime: any, rejectionReason?: Types.ProposalRejectionReason | null, errorDetails?: string | null, rationale: { __typename?: 'ProposalRationale', title: string, description: string }, party: { __typename?: 'Party', id: string }, terms: { __typename?: 'ProposalTerms', closingDatetime: any, enactmentDatetime?: any | null, change: { __typename?: 'CancelTransfer' } | { __typename: 'NewAsset', name: string, symbol: string, decimals: number, quantum: string, source: { __typename?: 'BuiltinAsset', maxFaucetAmountMint: string } | { __typename?: 'ERC20', contractAddress: string, withdrawThreshold: string, lifetimeLimit: string } } | { __typename?: 'NewFreeform' } | { __typename?: 'NewMarket', instrument: { __typename?: 'InstrumentConfiguration', name: string, code: string, product?: { __typename: 'FutureProduct', settlementAsset: { __typename?: 'Asset', symbol: string } } | { __typename: 'PerpetualProduct', settlementAsset: { __typename?: 'Asset', symbol: string } } | { __typename: 'SpotProduct' } | null } } | { __typename?: 'NewSpotMarket' } | { __typename?: 'NewTransfer' } | { __typename?: 'UpdateAsset', quantum: string, assetId: string, source: { __typename?: 'UpdateERC20', lifetimeLimit: string, withdrawThreshold: string } } | { __typename?: 'UpdateMarket', marketId: string } | { __typename?: 'UpdateMarketState', updateType: Types.MarketUpdateType, price?: string | null, market: { __typename?: 'Market', decimalPlaces: number, id: string, tradableInstrument: { __typename?: 'TradableInstrument', instrument: { __typename?: 'Instrument', name: string, code: string, product: { __typename: 'Future', quoteName: string } | { __typename: 'Perpetual', quoteName: string } | { __typename: 'Spot' } } } } } | { __typename?: 'UpdateNetworkParameter', networkParameter: { __typename?: 'NetworkParameter', key: string, value: string } } | { __typename?: 'UpdateReferralProgram', windowLength: number, endOfProgram: any, benefitTiers: Array<{ __typename?: 'BenefitTier', minimumEpochs: number, minimumRunningNotionalTakerVolume: string, referralDiscountFactor: string, referralRewardFactor: string }>, stakingTiers: Array<{ __typename?: 'StakingTier', minimumStakedTokens: string, referralRewardMultiplier: string }> } | { __typename?: 'UpdateSpotMarket' } | { __typename?: 'UpdateVolumeDiscountProgram', endOfProgramTimestamp: any, windowLength: number, benefitTiers: Array<{ __typename?: 'VolumeBenefitTier', minimumRunningNotionalTakerVolume: string, volumeDiscountFactor: string }> } }, votes: { __typename?: 'ProposalVotes', yes: { __typename?: 'ProposalVoteSide', totalTokens: string, totalNumber: string, totalEquityLikeShareWeight: string }, no: { __typename?: 'ProposalVoteSide', totalTokens: string, totalNumber: string, totalEquityLikeShareWeight: string } } } } | null> | null } | null };
export const NewMarketProductFieldsFragmentDoc = gql`
fragment NewMarketProductFields on Proposal {
@@ -131,9 +130,16 @@ export const ProposalFieldsFragmentDoc = gql`
instrument {
name
code
futureProduct {
settlementAsset {
symbol
product {
... on FutureProduct {
settlementAsset {
symbol
}
}
... on PerpetualProduct {
settlementAsset {
symbol
}
}
}
}
@@ -191,7 +197,7 @@ export const ProposalFieldsFragmentDoc = gql`
}
`;
export const ProposalsDocument = gql`
query Proposals($includeNewMarketProductFields: Boolean!, $includeUpdateMarketStates: Boolean!, $includeUpdateReferralPrograms: Boolean!, $includeUpdateVolumeDiscountPrograms: Boolean!) {
query Proposals($includeNewMarketProductFields: Boolean!, $includeUpdateMarketStates: Boolean!, $includeUpdateReferralPrograms: Boolean!) {
proposalsConnection {
edges {
node {
@@ -199,7 +205,7 @@ export const ProposalsDocument = gql`
...NewMarketProductFields @include(if: $includeNewMarketProductFields)
...UpdateMarketStates @include(if: $includeUpdateMarketStates)
...UpdateReferralPrograms @include(if: $includeUpdateReferralPrograms)
...UpdateVolumeDiscountPrograms @include(if: $includeUpdateVolumeDiscountPrograms)
...UpdateVolumeDiscountPrograms
}
}
}
@@ -225,7 +231,6 @@ ${UpdateVolumeDiscountProgramsFragmentDoc}`;
* includeNewMarketProductFields: // value for 'includeNewMarketProductFields'
* includeUpdateMarketStates: // value for 'includeUpdateMarketStates'
* includeUpdateReferralPrograms: // value for 'includeUpdateReferralPrograms'
* includeUpdateVolumeDiscountPrograms: // value for 'includeUpdateVolumeDiscountPrograms'
* },
* });
*/
@@ -17,7 +17,7 @@ import {
} from './__generated__/Proposals';
import { type ProtocolUpgradeProposalFieldsFragment } from '@vegaprotocol/proposals';
import { useProtocolUpgradeProposalsQuery } from '@vegaprotocol/proposals';
import { FLAGS } from '@vegaprotocol/environment';
import { useFeatureFlags } from '@vegaprotocol/environment';
export function getNotRejectedProposals(data?: ProposalFieldsFragment[]) {
return flow([
@@ -43,16 +43,16 @@ export function getNotRejectedProtocolUpgradeProposals<
}
export const ProposalsContainer = () => {
const featureFlags = useFeatureFlags((state) => state.flags);
const { t } = useTranslation();
const { data, loading, error } = useProposalsQuery({
pollInterval: 5000,
fetchPolicy: 'network-only',
errorPolicy: 'ignore',
variables: {
includeNewMarketProductFields: !!FLAGS.PRODUCT_PERPETUALS,
includeUpdateMarketStates: !!FLAGS.UPDATE_MARKET_STATE,
includeUpdateReferralPrograms: !!FLAGS.REFERRALS,
includeUpdateVolumeDiscountPrograms: !!FLAGS.VOLUME_DISCOUNTS,
includeNewMarketProductFields: !!featureFlags.PRODUCT_PERPETUALS,
includeUpdateMarketStates: !!featureFlags.UPDATE_MARKET_STATE,
includeUpdateReferralPrograms: !!featureFlags.REFERRALS,
},
});
@@ -8,7 +8,7 @@ import {
doesValueEquateToParam,
} from '@vegaprotocol/proposals';
import { useEnvironment, DocsLinks } from '@vegaprotocol/environment';
import { validateJson } from '@vegaprotocol/utils';
import { useValidateJson } from '@vegaprotocol/utils';
import {
NetworkParams,
useNetworkParams,
@@ -41,6 +41,7 @@ export interface NewAssetProposalFormFields {
const DOCS_LINK = '/new-asset-proposal';
export const ProposeNewAsset = () => {
const validateJson = useValidateJson();
const {
params,
loading: networkParamsLoading,
@@ -7,7 +7,7 @@ import {
doesValueEquateToParam,
} from '@vegaprotocol/proposals';
import { useEnvironment, DocsLinks } from '@vegaprotocol/environment';
import { validateJson } from '@vegaprotocol/utils';
import { useValidateJson } from '@vegaprotocol/utils';
import {
NetworkParams,
useNetworkParams,
@@ -39,6 +39,7 @@ export interface NewMarketProposalFormFields {
const DOCS_LINK = '/new-market-proposal';
export const ProposeNewMarket = () => {
const validateJson = useValidateJson();
const {
params,
loading: networkParamsLoading,
@@ -14,7 +14,7 @@ import {
RoundedWrapper,
TextArea,
} from '@vegaprotocol/ui-toolkit';
import { validateJson } from '@vegaprotocol/utils';
import { useValidateJson } from '@vegaprotocol/utils';
import {
NetworkParams,
useNetworkParams,
@@ -31,6 +31,7 @@ export interface RawProposalFormFields {
}
export const ProposeRaw = () => {
const validateJson = useValidateJson();
const {
params,
loading: networkParamsLoading,
@@ -7,7 +7,7 @@ import {
doesValueEquateToParam,
} from '@vegaprotocol/proposals';
import { useEnvironment, DocsLinks } from '@vegaprotocol/environment';
import { validateJson } from '@vegaprotocol/utils';
import { useValidateJson } from '@vegaprotocol/utils';
import {
NetworkParams,
useNetworkParams,
@@ -39,6 +39,7 @@ export interface UpdateAssetProposalFormFields {
const DOCS_LINK = '/update-asset-proposal';
export const ProposeUpdateAsset = () => {
const validateJson = useValidateJson();
const {
params,
loading: networkParamsLoading,
@@ -8,7 +8,7 @@ import {
useProposalSubmit,
} from '@vegaprotocol/proposals';
import { useEnvironment, DocsLinks } from '@vegaprotocol/environment';
import { validateJson } from '@vegaprotocol/utils';
import { useValidateJson } from '@vegaprotocol/utils';
import {
NetworkParams,
useNetworkParams,
@@ -53,6 +53,7 @@ export interface UpdateMarketProposalFormFields {
const DOCS_LINK = '/update-market-proposal';
export const ProposeUpdateMarket = () => {
const validateJson = useValidateJson();
const {
params,
loading: networkParamsLoading,
@@ -260,7 +261,7 @@ export const ProposeUpdateMarket = () => {
</FormGroup>
{selectedMarket && (
<div className="mt-[-20px] mb-6">
<div className="mb-6 mt-[-20px]">
<KeyValueTable data-testid="update-market-details">
<KeyValueTableRow>
{t('MarketName')}
@@ -95,7 +95,7 @@ export const ProtocolUpgradeProposalContainer = () => {
time={
pending && time ? (
convertToCountdownString(time, '0:00:00:00')
) : blockInfo?.result ? (
) : blockInfo && 'result' in blockInfo && blockInfo?.result ? (
<span title={blockInfo.result.block.header.time}>
{formatDateWithLocalTimezone(
new Date(blockInfo.result.block.header.time)
@@ -10,7 +10,7 @@ import { removePaginationWrapper } from '@vegaprotocol/utils';
import flow from 'lodash/flow';
import orderBy from 'lodash/orderBy';
import { ProposalState } from '@vegaprotocol/types';
import { FLAGS } from '@vegaprotocol/environment';
import { useFeatureFlags } from '@vegaprotocol/environment';
const orderByDate = (arr: ProposalFieldsFragment[]) =>
orderBy(
@@ -33,16 +33,16 @@ export function getRejectedProposals(data?: ProposalFieldsFragment[] | null) {
}
export const RejectedProposalsContainer = () => {
const featureFlags = useFeatureFlags((state) => state.flags);
const { t } = useTranslation();
const { data, loading, error } = useProposalsQuery({
pollInterval: 5000,
fetchPolicy: 'network-only',
errorPolicy: 'ignore',
variables: {
includeNewMarketProductFields: !!FLAGS.PRODUCT_PERPETUALS,
includeUpdateMarketStates: !!FLAGS.UPDATE_MARKET_STATE,
includeUpdateReferralPrograms: !!FLAGS.REFERRALS,
includeUpdateVolumeDiscountPrograms: !!FLAGS.VOLUME_DISCOUNTS,
includeNewMarketProductFields: !!featureFlags.PRODUCT_PERPETUALS,
includeUpdateMarketStates: !!featureFlags.UPDATE_MARKET_STATE,
includeUpdateReferralPrograms: !!featureFlags.REFERRALS,
},
});
@@ -116,7 +116,8 @@ export const generateYesVotes = (
fixedTokenValue?: number,
totalEquityLikeShareWeight?: string
): Votes => {
const votes = Array.from(Array(numberOfVotes)).map(() => {
const votes = [];
for (let i = 0; i < numberOfVotes; i++) {
const vote: Vote = {
__typename: 'Vote',
value: Schema.VoteValue.VALUE_YES,
@@ -152,8 +153,9 @@ export const generateYesVotes = (
datetime: faker.date.past().toISOString(),
};
return vote;
});
votes.push(vote);
}
return {
__typename: 'ProposalVoteSide',
totalNumber: votes.length.toString(),
@@ -172,7 +174,8 @@ export const generateNoVotes = (
fixedTokenValue?: number,
totalEquityLikeShareWeight?: string
): Votes => {
const votes = Array.from(Array(numberOfVotes)).map(() => {
const votes = [];
for (let i = 0; i < numberOfVotes; i++) {
const vote: Vote = {
__typename: 'Vote',
value: Schema.VoteValue.VALUE_NO,
@@ -207,8 +210,9 @@ export const generateNoVotes = (
},
datetime: faker.date.past().toISOString(),
};
return vote;
});
votes.push(vote);
}
return {
__typename: 'ProposalVoteSide',
totalNumber: votes.length.toString(),
+4
View File
@@ -104,6 +104,10 @@
list-style: circle;
}
.react-markdown-container a {
text-decoration: underline;
}
.jsondiffpatch-delta,
.jsondiffpatch-delta pre {
font-family: 'Roboto Mono', monospace !important;
@@ -1,204 +0,0 @@
const marketInfoBtn = 'Info';
const marketInfoSubtitle = 'accordion-title';
const marketSummaryBlock = 'header-summary';
const marketExpiry = 'market-expiry';
const marketPrice = 'market-price';
const marketChange = 'market-change';
const marketVolume = 'market-volume';
const marketMode = 'market-trading-mode';
const marketSettlement = 'market-settlement-asset';
const percentageValue = 'price-change-percentage';
const priceChangeValue = 'price-change';
const itemHeader = 'item-header';
const itemValue = 'item-value';
const marketListContent = 'popover-content';
describe(
'Console - market info - live env',
{ tags: '@live', testIsolation: true },
() => {
before(() => {
cy.visit('/');
cy.contains('Loading market data...').should('not.exist');
cy.getByTestId('link').should('be.visible');
cy.getByTestId('dialog-close').click();
cy.getByTestId(marketInfoBtn).click();
});
const titles = ['Market data', 'Market specification', 'Market governance'];
const subtitles = [
'Current fees',
'Market price',
'Market volume',
'Insurance pool',
'Key details',
'Instrument',
'Settlement asset',
'Metadata',
'Risk model',
'Risk parameters',
'Risk factors',
'Price monitoring bounds 1',
'Liquidity monitoring parameters',
'Liquidity',
'Liquidity price range',
'Oracle',
'Proposal',
];
it('market info titles are displayed', () => {
cy.getByTestId('split-view-view')
.find('.text-lg')
.each((element, index) => {
cy.wrap(element).should('have.text', titles[index]);
});
});
it('market info subtitles are displayed', () => {
cy.getByTestId('popover-trigger').click();
cy.contains('Loading market data...').should('not.exist');
cy.contains('[data-testid="link"]', 'AAVEDAI.MF21').click();
cy.getByTestId(marketInfoBtn).click();
cy.getByTestId(marketInfoSubtitle).each((element, index) => {
cy.wrap(element).should('have.text', subtitles[index]);
});
});
it('renders correctly liquidity in trading tab', () => {
cy.getByTestId('Liquidity').click();
cy.contains('Loading').should('not.exist');
cy.contains('Something went wrong').should('not.exist');
cy.contains('Application error').should('not.exist');
cy.getByTestId('tab-liquidity').within(() => {
cy.get('[col-id="partyId"]').eq(1).should('not.be.empty');
});
});
}
);
describe(
'Console - market summary - live env',
{ tags: '@live', testIsolation: true },
() => {
before(() => {
cy.visit('/');
cy.getByTestId('dialog-close').click();
cy.getByTestId(marketSummaryBlock).should('be.visible');
});
it('must display market name', () => {
cy.getByTestId('popover-trigger').should('not.be.empty');
});
it('must see market expiry', () => {
cy.getByTestId(marketSummaryBlock).within(() => {
cy.getByTestId(marketExpiry).within(() => {
cy.getByTestId(itemHeader).should('have.text', 'Expiry');
cy.getByTestId(itemValue).should('not.be.empty');
});
});
});
it('must see market price', () => {
cy.getByTestId(marketSummaryBlock).within(() => {
cy.getByTestId(marketPrice).within(() => {
cy.getByTestId(itemHeader).should('have.text', 'Price');
cy.getByTestId(itemValue).should('not.be.empty');
});
});
});
it('must see market change', () => {
cy.getByTestId(marketSummaryBlock).within(() => {
cy.getByTestId(marketChange).within(() => {
cy.getByTestId(itemHeader).should('have.text', 'Change (24h)');
cy.getByTestId(percentageValue).should('not.be.empty');
cy.getByTestId(priceChangeValue).should('not.be.empty');
});
});
});
it('must see market volume', () => {
cy.getByTestId(marketSummaryBlock).within(() => {
cy.getByTestId(marketVolume).within(() => {
cy.getByTestId(itemHeader).should('have.text', 'Volume (24h)');
cy.getByTestId(itemValue).should('not.be.empty');
});
});
});
it('must see market mode', () => {
cy.getByTestId(marketSummaryBlock).within(() => {
cy.getByTestId(marketMode).within(() => {
cy.getByTestId(itemHeader).should('have.text', 'Trading mode');
cy.getByTestId(itemValue).should('not.be.empty');
});
});
});
it('must see market settlement', () => {
cy.getByTestId(marketSummaryBlock).within(() => {
cy.getByTestId(marketSettlement).within(() => {
cy.getByTestId(itemHeader).should('have.text', 'Settlement asset');
cy.getByTestId(itemValue).should('not.be.empty');
});
});
});
}
);
describe(
'Console - markets table - live env',
{ tags: '@live', testIsolation: true },
() => {
beforeEach(() => {
cy.visit('/');
});
it('renders markets correctly', () => {
cy.get('[data-testid^="market-link-"]').should('not.be.empty');
cy.getByTestId('price').invoke('text').should('not.be.empty');
cy.getByTestId('settlement-asset').should('not.be.empty');
cy.getByTestId('price-change-percentage').should('not.be.empty');
cy.getByTestId('price-change').should('not.be.empty');
cy.getByTestId('sparkline-svg').should('be.visible');
});
it('renders market list drop down', () => {
openMarketDropDown();
cy.getByTestId(marketListContent)
.find('[data-testid="price"]')
.invoke('text')
.should('not.be.empty');
cy.getByTestId(marketListContent)
.find('[data-testid="trading-mode-col"]')
.should('not.be.empty');
cy.getByTestId(marketListContent)
.find('[data-testid="taker-fee"]')
.should('contain.text', '%');
cy.getByTestId(marketListContent)
.find('[data-testid="market-volume"]')
.should('not.be.empty');
cy.getByTestId(marketListContent)
.find('[data-testid="market-name"]')
.should('not.be.empty');
});
it('Able to select market from dropdown', () => {
cy.getByTestId('popover-trigger')
.invoke('text')
.then((marketName) => {
openMarketDropDown();
cy.get('[data-testid^=market-link]').eq(1).click();
cy.getByTestId('popover-trigger').should('not.be.equal', marketName);
});
});
}
);
function openMarketDropDown() {
cy.contains('Loading...').should('not.exist');
cy.getByTestId('link').should('be.visible');
cy.getByTestId('dialog-close').click();
cy.getByTestId('popover-trigger').click();
cy.contains('Loading market data...').should('not.exist');
}
@@ -1,311 +0,0 @@
import { checkSorting } from '@vegaprotocol/cypress';
import * as Schema from '@vegaprotocol/types';
const liquidityTab = 'Liquidity';
const rowSelector =
'[data-testid="tab-liquidity"] .ag-center-cols-container .ag-row';
const rowSelectorLiquidityActive =
'[data-testid="tab-active"] .ag-center-cols-container .ag-row';
const rowSelectorLiquidityInactive =
'[data-testid="tab-inactive"] .ag-center-cols-container .ag-row';
const marketSummaryBlock = 'header-summary';
const itemValue = 'item-value';
const itemHeader = 'item-header';
const colCommitmentAmount = '[col-id="commitmentAmount"]';
const colEquityLikeShare = '[col-id="feeShare.equityLikeShare"]';
const colFee = '[col-id="fee"]';
const colCommitmentAmount_1 = '[col-id="commitmentAmount_1"]';
const colBalance = '[col-id="balance"]';
const colStatus = '[col-id="status"]';
const colCreatedAt = '[col-id="createdAt"] button';
const colUpdatedAt = '[col-id="updatedAt"] button';
const headers = [
'Party',
'Status',
'Commitment (tDAI)',
'Obligation',
'Fee',
'Adjusted stake share',
'Share',
'Live supplied liquidity',
'Fees accrued this epoch',
'Live time on book',
'Live liquidity quality score (%)',
'Last time on the book',
'Last fee penalty',
'Last bond penalty',
'Created',
'Updated',
];
describe('liquidity table - trading', { tags: '@smoke' }, () => {
before(() => {
cy.setOnBoardingViewed();
cy.mockSubscription();
cy.mockTradingPage(
Schema.MarketState.STATE_ACTIVE,
Schema.MarketTradingMode.TRADING_MODE_MONITORING_AUCTION,
Schema.AuctionTrigger.AUCTION_TRIGGER_LIQUIDITY_TARGET_NOT_MET
);
cy.visit('/#/markets/market-0');
cy.wait('@MarketData');
cy.getByTestId(liquidityTab).click();
cy.wait('@LiquidityProvisions');
});
it('can see table headers', () => {
// 5002-LIQP-001
cy.getByTestId('tab-liquidity').within(($headers) => {
cy.wrap($headers)
.get('.ag-header-cell-text')
.each(($header, i) => {
cy.wrap($header).should('have.text', headers[i]);
});
});
});
it('renders liquidity table correctly', () => {
// 5002-LIQP-002
cy.get(rowSelector)
.first()
.find('[col-id="partyId"]')
.should('have.text', '69464e…dc6f');
cy.get(rowSelector)
.first()
.find(colCommitmentAmount)
.should('have.text', '4,000.00');
cy.get(rowSelector)
.first()
.find(colEquityLikeShare)
.should('have.text', '100.00%');
cy.get(rowSelector).first().find(colFee).should('have.text', '0.09%');
cy.get(rowSelector)
.first()
.find(colCommitmentAmount_1)
.should('have.text', '4,000.00');
cy.get(rowSelector)
.first()
.find(colBalance)
.scrollIntoView()
.should('have.text', '4,000.00');
cy.get(rowSelector).first().find(colStatus).should('have.text', 'Active');
cy.get(rowSelector).first().find(colCreatedAt).should('not.be.empty');
cy.get(rowSelector).first().find(colUpdatedAt).should('not.be.empty');
});
it('liquidity status column should be sorted properly', () => {
// 5002-LIQP-003
const liquidityColDefault = ['Active', 'Pending'];
const liquidityColAsc = ['Active', 'Pending'];
const liquidityColDesc = ['Pending', 'Active'];
checkSorting(
'status',
liquidityColDefault,
liquidityColAsc,
liquidityColDesc
);
});
});
describe('liquidity table view', { tags: '@smoke' }, () => {
before(() => {
cy.setOnBoardingViewed();
cy.mockSubscription();
cy.mockTradingPage(
Schema.MarketState.STATE_ACTIVE,
Schema.MarketTradingMode.TRADING_MODE_MONITORING_AUCTION,
Schema.AuctionTrigger.AUCTION_TRIGGER_LIQUIDITY_TARGET_NOT_MET
);
cy.visit('/#/liquidity/market-0');
cy.wait('@LiquidityProvisions');
});
it('can see header title', () => {
// 5002-LIQP-004
// 5002-LIQP-005
cy.getByTestId('header-title').should(
'contain.text',
'BTCUSD.MF21 liquidity provision'
);
});
it('can see target stake', () => {
// 5002-LIQP-006
cy.getByTestId(marketSummaryBlock).within(() => {
cy.getByTestId('target-stake').within(() => {
cy.getByTestId(itemHeader).should('have.text', 'Target stake');
cy.getByTestId(itemValue).should('have.text', '10.00 tDAI').realHover();
});
});
cy.getByTestId('tooltip-content').should(
'contain.text',
`The market's liquidity requirement which is derived from the maximum open interest observed over a rolling time window.The market's liquidity requirement which is derived from the maximum open interest observed over a rolling time window.`
);
});
it('can see supplied stake', () => {
// 5002-LIQP-007
cy.getByTestId(marketSummaryBlock).within(() => {
cy.getByTestId('supplied-stake').within(() => {
cy.getByTestId(itemHeader).should('have.text', 'Supplied stake');
cy.getByTestId(itemValue).should('have.text', '0.01 tDAI').realHover();
});
});
cy.getByTestId('tooltip-content').should(
'contain.text',
'The current amount of liquidity supplied for this market.'
);
});
it('can see liquidity supplied', () => {
// 5002-LIQP-008
cy.getByTestId(marketSummaryBlock).within(() => {
cy.getByTestId('liquidity-supplied').within(() => {
cy.getByTestId(itemHeader).should('have.text', 'Liquidity supplied');
cy.getByTestId('indicator').should('be.visible');
cy.getByTestId(itemValue).should('have.text', ' 0.10%').realHover();
});
});
});
it('can see market id', () => {
// 5002-LIQP-009
cy.getByTestId(marketSummaryBlock).within(() => {
cy.getByTestId('liquidity-market-id').within(() => {
cy.getByTestId(itemHeader).should('have.text', 'Market ID');
cy.getByTestId(itemValue).should('have.text', 'market-0');
});
});
});
it('can see market id', () => {
// 5002-LIQP-010
cy.getByTestId(marketSummaryBlock).within(() => {
cy.getByTestId('liquidity-learn-more').within(() => {
cy.getByTestId(itemHeader).should('have.text', 'Learn more');
cy.getByTestId(itemValue).should('have.text', 'Providing liquidity');
cy.getByTestId('external-link')
.should('have.attr', 'href')
.and(
'include',
'https://docs.vega.xyz/testnet/concepts/liquidity/provision'
);
});
});
});
describe('liquidity table view', { tags: '@smoke' }, () => {
it('can see table headers', () => {
cy.getByTestId('tab-active').within(($headers) => {
cy.wrap($headers)
.get('.ag-header-cell-text')
.each(($header, i) => {
cy.wrap($header).should('have.text', headers[i]);
});
});
});
it('renders liquidity active table correctly', () => {
// 5002-LIQP-011
cy.get(rowSelectorLiquidityActive)
.first()
.find('[col-id="partyId"]')
.should('have.text', '69464e…dc6f');
cy.get(rowSelectorLiquidityActive)
.first()
.find(colCommitmentAmount)
.should('have.text', '4,000.00');
cy.get(rowSelectorLiquidityActive)
.first()
.find(colEquityLikeShare)
.should('have.text', '100.00%');
cy.get(rowSelectorLiquidityActive)
.first()
.find(colFee)
.should('have.text', '0.09%');
cy.get(rowSelectorLiquidityActive)
.first()
.find(colCommitmentAmount_1)
.should('have.text', '4,000.00');
cy.get(rowSelectorLiquidityActive)
.first()
.find(colBalance)
.should('have.text', '4,000.00');
cy.get(rowSelectorLiquidityActive)
.first()
.find(colStatus)
.should('have.text', 'Active');
cy.get(rowSelectorLiquidityActive)
.first()
.find(colCreatedAt)
.should('not.be.empty');
cy.get(rowSelectorLiquidityActive)
.first()
.find(colUpdatedAt)
.should('not.be.empty');
});
it('renders liquidity inactive table correctly', () => {
// 5002-LIQP-012
cy.getByTestId('Inactive').click();
cy.get(rowSelectorLiquidityInactive)
.first()
.find('[col-id="partyId"]')
.should('have.text', 'cc464e…dc6f');
cy.get(rowSelectorLiquidityInactive)
.first()
.find(colCommitmentAmount)
.should('have.text', '4,000.00');
cy.get(rowSelectorLiquidityInactive)
.first()
.find(colEquityLikeShare)
.should('have.text', '100.00%');
cy.get(rowSelectorLiquidityInactive)
.first()
.find(colFee)
.should('have.text', '0.40%');
cy.get(rowSelectorLiquidityInactive)
.first()
.find(colCommitmentAmount_1)
.should('have.text', '4,000.00');
cy.get(rowSelectorLiquidityInactive)
.first()
.find(colBalance)
.should('have.text', '2,000.00');
cy.get(rowSelectorLiquidityInactive)
.first()
.find(colStatus)
.should('have.text', 'Pending');
cy.get(rowSelectorLiquidityInactive)
.first()
.find(colCreatedAt)
.should('not.be.empty');
cy.get(rowSelectorLiquidityInactive)
.first()
.find(colUpdatedAt)
.should('not.be.empty');
});
});
});
@@ -1,47 +0,0 @@
import { aliasGQLQuery } from '@vegaprotocol/cypress';
import { proposalListQuery, marketUpdateProposal } from '@vegaprotocol/mock';
import * as Schema from '@vegaprotocol/types';
const marketSummaryBlock = 'header-summary';
describe('Market proposal notification', { tags: '@smoke' }, () => {
before(() => {
cy.setVegaWallet();
cy.mockTradingPage(
Schema.MarketState.STATE_ACTIVE,
Schema.MarketTradingMode.TRADING_MODE_MONITORING_AUCTION,
Schema.AuctionTrigger.AUCTION_TRIGGER_LIQUIDITY_TARGET_NOT_MET
);
cy.mockGQL((req) => {
aliasGQLQuery(
req,
'ProposalsList',
proposalListQuery({
proposalsConnection: {
edges: [{ node: marketUpdateProposal }],
},
})
);
});
cy.mockSubscription();
cy.visit('/#/markets/market-0');
cy.wait('@MarketData');
cy.getByTestId(marketSummaryBlock).should('be.visible');
});
it('should display market proposal notification if proposal found', () => {
cy.getByTestId(marketSummaryBlock).within(() => {
cy.getByTestId('market-proposal-notification').should(
'contain.text',
'Changes have been proposed for this market'
);
cy.getByTestId('market-proposal-notification').within(() => {
cy.getByTestId('external-link').should(
'have.attr',
'href',
`${Cypress.env('VEGA_TOKEN_URL')}/proposals/123`
);
});
});
});
});
@@ -1,216 +0,0 @@
import * as Schema from '@vegaprotocol/types';
const expirtyTooltip = 'expiry-tooltip';
const externalLink = 'external-link';
const itemHeader = 'item-header';
const itemValue = 'item-value';
const link = 'link';
const liquidityLink = 'view-liquidity-link';
const liquiditySupplied = 'liquidity-supplied';
const liquiditySuppliedTooltip = 'liquidity-supplied-tooltip';
const marketChange = 'market-change';
const marketExpiry = 'market-expiry';
const marketMode = 'market-trading-mode';
const marketName = 'header-title';
const marketPrice = 'market-price';
const marketSettlement = 'market-settlement-asset';
const marketState = 'market-state';
const marketSummaryBlock = 'header-summary';
const marketVolume = 'market-volume';
const percentageValue = 'price-change-percentage';
const priceChangeValue = 'price-change';
const tradingModeTooltip = 'trading-mode-tooltip';
describe('Market trading page', () => {
before(() => {
cy.clearAllLocalStorage();
cy.mockTradingPage(
Schema.MarketState.STATE_ACTIVE,
Schema.MarketTradingMode.TRADING_MODE_MONITORING_AUCTION,
Schema.AuctionTrigger.AUCTION_TRIGGER_LIQUIDITY_TARGET_NOT_MET
);
cy.mockSubscription();
cy.setOnBoardingViewed();
cy.visit('/#/markets/market-0');
cy.wait('@MarketData');
cy.getByTestId(marketSummaryBlock).should('be.visible');
});
describe('Market summary', { tags: '@smoke' }, () => {
// 7002-SORD-001
// 7002-SORD-002
it('must display market name', () => {
// 6002-MDET-001
cy.getByTestId(marketName).should('not.be.empty');
});
it('must see market expiry', () => {
// 6002-MDET-002
cy.getByTestId(marketSummaryBlock).within(() => {
cy.getByTestId(marketExpiry).within(() => {
cy.getByTestId(itemHeader).should('have.text', 'Expiry');
cy.getByTestId(itemValue).should('not.be.empty');
});
});
});
it('must see market price', () => {
// 6002-MDET-003
cy.getByTestId(marketSummaryBlock).within(() => {
cy.getByTestId(marketPrice).within(() => {
cy.getByTestId(itemHeader).should('have.text', 'Mark Price');
cy.getByTestId(itemValue).should('not.be.empty');
});
});
});
it('must see market change', () => {
// 6002-MDET-004
cy.getByTestId(marketSummaryBlock).within(() => {
cy.getByTestId(marketChange).within(() => {
cy.getByTestId(itemHeader).should('have.text', 'Change (24h)');
cy.getByTestId(percentageValue).should('not.be.empty');
cy.getByTestId(priceChangeValue).should('not.be.empty');
});
});
});
it('must see market volume', () => {
// 6002-MDET-005
cy.getByTestId(marketSummaryBlock).within(() => {
cy.getByTestId(marketVolume).within(() => {
cy.getByTestId(itemHeader).should('have.text', 'Volume (24h)');
cy.getByTestId(itemValue).should('not.be.empty');
});
});
});
it('must see market mode', () => {
// 6002-MDET-006
cy.getByTestId(marketSummaryBlock).within(() => {
cy.getByTestId(marketMode).within(() => {
cy.getByTestId(itemHeader).should('have.text', 'Trading mode');
cy.getByTestId(itemValue).should(
'have.text',
'Monitoring auction - liquidity (target not met)'
);
});
});
});
it('must see market status', () => {
// 6002-MDET-007
// 7002-SORD-061
cy.getByTestId(marketSummaryBlock).within(() => {
cy.getByTestId(marketState).within(() => {
cy.getByTestId(itemHeader).should('have.text', 'Status');
cy.getByTestId(itemValue).should('not.be.empty');
});
});
});
it('must see market settlement', () => {
// 6002-MDET-008
cy.getByTestId(marketSummaryBlock).within(() => {
cy.getByTestId(marketSettlement).within(() => {
cy.getByTestId(itemHeader).should('have.text', 'Settlement asset');
cy.getByTestId(itemValue).should('not.be.empty');
});
});
});
it('must see market liquidity supplied', () => {
// 6002-MDET-009
cy.getByTestId(marketSummaryBlock).within(() => {
cy.getByTestId(liquiditySupplied).within(() => {
cy.getByTestId(itemHeader).should('have.text', 'Liquidity supplied');
cy.getByTestId(itemValue).should('not.be.empty');
});
});
});
});
describe('Market tooltips', { tags: '@smoke' }, () => {
it('should see expiry tooltip', () => {
cy.getByTestId(marketSummaryBlock).within(() => {
cy.getByTestId(marketExpiry).within(() => {
cy.getByTestId(itemValue)
.should('have.text', 'Not time-based')
.realHover();
});
});
cy.getByTestId(expirtyTooltip)
.eq(0)
.should(
'contain.text',
'This market expires when triggered by its oracle, not on a set date.'
)
.within(() => {
cy.getByTestId(link)
.should('have.attr', 'href')
.and('include', Cypress.env('EXPLORER_URL'));
});
});
it('should see trading conditions tooltip', () => {
const toolTipLabel = 'tooltip-label';
const toolTipValue = 'tooltip-value';
const auctionToolTipLabels = [
'Auction start',
'Est. auction end',
'Target liquidity',
'Current liquidity',
'Est. uncrossing price',
'Est. uncrossing vol',
];
cy.getByTestId(marketSummaryBlock).within(() => {
cy.getByTestId(marketMode).within(() => {
cy.getByTestId(itemValue)
.should('contain.text', 'Monitoring auction')
.and('contain.text', 'liquidity')
.realHover();
});
});
cy.getByTestId(tradingModeTooltip)
.should(
'contain.text',
'This market is in auction until it reaches sufficient liquidity.'
)
.eq(0)
.within(() => {
cy.getByTestId(externalLink)
.should('have.attr', 'href')
.and('include', Cypress.env('TRADING_MODE_LINK'));
for (let i = 0; i < 6; i++) {
cy.getByTestId(toolTipLabel)
.eq(i)
.should('have.text', auctionToolTipLabels[i]);
cy.getByTestId(toolTipValue).eq(i).should('not.be.empty');
}
});
});
it('should see liquidity supplied tooltip', () => {
cy.getByTestId(marketSummaryBlock).within(() => {
cy.getByTestId(liquiditySupplied).within(() => {
cy.getByTestId(itemValue).realHover();
});
});
cy.getByTestId(liquiditySuppliedTooltip)
.should('contain.text', 'Supplied stake')
.and('contain.text', 'Target stake')
.first()
.within(() => {
cy.getByTestId(liquidityLink).should(
'have.text',
'View liquidity provision table'
);
cy.getByTestId(externalLink).should(
'have.text',
'Learn about providing liquidity'
);
});
});
});
});
@@ -1,87 +0,0 @@
import { aliasGQLQuery } from '@vegaprotocol/cypress';
import {
accountsQuery,
amendGeneralAccountBalance,
amendMarginAccountBalance,
} from '@vegaprotocol/mock';
describe.skip(
'account validation',
{ tags: '@regression', testIsolation: true },
() => {
describe('zero balance error', () => {
beforeEach(() => {
cy.setVegaWallet();
cy.mockTradingPage();
let accounts = accountsQuery();
accounts = amendMarginAccountBalance(accounts, 'market-0', '1000');
accounts = amendGeneralAccountBalance(accounts, 'market-0', '0');
cy.mockGQL((req) => {
aliasGQLQuery(req, 'Accounts', accounts);
});
cy.mockSubscription();
cy.visit('/#/markets/market-0');
cy.wait('@Markets');
});
it('should show an error if your balance is zero', () => {
const accounts = accountsQuery();
amendMarginAccountBalance(accounts, 'market-0', '0');
cy.mockGQL((req) => {
aliasGQLQuery(req, 'Accounts', accounts);
});
// 7002-SORD-060
cy.getByTestId('place-order').should('be.enabled');
// 7002-SORD-003
cy.getByTestId('deal-ticket-error-message-zero-balance').should(
'have.text',
'You need ' +
'tDAI' +
' in your wallet to trade in this market. See all your collateral.Make a deposit'
);
cy.getByTestId('deal-ticket-deposit-dialog-button').should('exist');
});
});
describe('not enough balance warning', () => {
beforeEach(() => {
cy.setVegaWallet();
cy.mockTradingPage();
let accounts = accountsQuery();
accounts = amendMarginAccountBalance(accounts, 'market-0', '1000');
accounts = amendGeneralAccountBalance(accounts, 'market-0', '1');
cy.mockGQL((req) => {
aliasGQLQuery(req, 'Accounts', accounts);
});
cy.mockSubscription();
cy.visit('/#/markets/market-0');
cy.wait('@Markets');
cy.get('[data-testid="deal-ticket-form"]').then(($form) => {
if (!$form.length) {
cy.getByTestId('Order').click();
}
});
});
it('should display info and button for deposit', () => {
// 7002-SORD-003
// warning should show immediately
cy.getByTestId('deal-ticket-warning-margin').should(
'contain.text',
'You may not have enough margin available to open this position'
);
cy.getByTestId('deal-ticket-warning-margin').should(
'contain.text',
'You may not have enough margin available to open this position. 5.00 tDAI is currently required. You have only 0.01001 tDAI available.'
);
cy.getByTestId('deal-ticket-deposit-dialog-button').click();
cy.getByTestId('sidebar-content')
.find('h2')
.eq(0)
.should('have.text', 'Deposit');
});
});
}
);
@@ -1,87 +0,0 @@
import * as Schema from '@vegaprotocol/types';
import { testOrderSubmission } from '../support/order-validation';
import type { OrderSubmission } from '@vegaprotocol/wallet';
import { createOrder } from '../support/create-order';
const displayTomorrow = () => {
const tomorrow = new Date();
tomorrow.setDate(tomorrow.getDate() + 1);
return tomorrow.toISOString().substring(0, 16);
};
describe(
'must submit order for market in batch auction',
{ tags: '@regression' },
() => {
before(() => {
cy.setVegaWallet();
cy.mockTradingPage(
Schema.MarketState.STATE_SUSPENDED,
Schema.MarketTradingMode.TRADING_MODE_BATCH_AUCTION,
Schema.AuctionTrigger.AUCTION_TRIGGER_LIQUIDITY_TARGET_NOT_MET
);
cy.mockSubscription();
cy.visit('/#/markets/market-0');
cy.wait('@Markets');
});
beforeEach(() => {
cy.setVegaWallet();
});
it('successfully places limit buy order', () => {
cy.mockVegaWalletTransaction();
const order: OrderSubmission = {
marketId: 'market-0',
type: Schema.OrderType.TYPE_LIMIT,
side: Schema.Side.SIDE_BUY,
timeInForce: Schema.OrderTimeInForce.TIME_IN_FORCE_GTC,
size: '100',
postOnly: false,
reduceOnly: false,
price: '200',
};
createOrder(order);
testOrderSubmission(order, { price: '20000000' });
});
it('successfully places limit sell order', () => {
cy.mockVegaWalletTransaction();
const order: OrderSubmission = {
marketId: 'market-0',
type: Schema.OrderType.TYPE_LIMIT,
side: Schema.Side.SIDE_SELL,
timeInForce: Schema.OrderTimeInForce.TIME_IN_FORCE_GTC,
size: '100',
postOnly: false,
reduceOnly: false,
price: '50000',
};
createOrder(order);
testOrderSubmission(order, { price: '5000000000' });
});
it('successfully places GTT limit buy order', () => {
cy.mockVegaWalletTransaction();
const order: OrderSubmission = {
marketId: 'market-0',
type: Schema.OrderType.TYPE_LIMIT,
side: Schema.Side.SIDE_SELL,
timeInForce: Schema.OrderTimeInForce.TIME_IN_FORCE_GTT,
size: '100',
postOnly: false,
reduceOnly: false,
price: '1.00',
expiresAt: displayTomorrow(),
};
createOrder(order);
testOrderSubmission(order, {
price: '100000',
postOnly: false,
reduceOnly: false,
expiresAt:
new Date(order.expiresAt as string).getTime().toString() + '000000',
});
});
}
);
@@ -1,85 +0,0 @@
import * as Schema from '@vegaprotocol/types';
import { testOrderSubmission } from '../support/order-validation';
import type { OrderSubmission } from '@vegaprotocol/wallet';
import { createOrder } from '../support/create-order';
const displayTomorrow = () => {
const tomorrow = new Date();
tomorrow.setDate(tomorrow.getDate() + 1);
return tomorrow.toISOString().substring(0, 16);
};
describe(
'must submit order for market in monitoring auction',
{ tags: '@regression' },
() => {
before(() => {
cy.setVegaWallet();
cy.mockTradingPage(
Schema.MarketState.STATE_SUSPENDED,
Schema.MarketTradingMode.TRADING_MODE_MONITORING_AUCTION,
Schema.AuctionTrigger.AUCTION_TRIGGER_LIQUIDITY_TARGET_NOT_MET
);
cy.mockSubscription();
cy.visit('/#/markets/market-0');
cy.wait('@Markets');
});
beforeEach(() => {
cy.setVegaWallet();
});
it('successfully places limit buy order', () => {
cy.mockVegaWalletTransaction();
const order: OrderSubmission = {
marketId: 'market-0',
type: Schema.OrderType.TYPE_LIMIT,
side: Schema.Side.SIDE_BUY,
timeInForce: Schema.OrderTimeInForce.TIME_IN_FORCE_GTC,
size: '100',
postOnly: false,
reduceOnly: false,
price: '200',
};
createOrder(order);
testOrderSubmission(order, { price: '20000000' });
});
it('successfully places limit sell order', () => {
cy.mockVegaWalletTransaction();
const order: OrderSubmission = {
marketId: 'market-0',
type: Schema.OrderType.TYPE_LIMIT,
side: Schema.Side.SIDE_SELL,
timeInForce: Schema.OrderTimeInForce.TIME_IN_FORCE_GTC,
size: '100',
price: '50000',
postOnly: false,
reduceOnly: false,
};
createOrder(order);
testOrderSubmission(order, { price: '5000000000' });
});
it('successfully places GTT limit buy order', () => {
cy.mockVegaWalletTransaction();
const order: OrderSubmission = {
marketId: 'market-0',
type: Schema.OrderType.TYPE_LIMIT,
side: Schema.Side.SIDE_SELL,
timeInForce: Schema.OrderTimeInForce.TIME_IN_FORCE_GTT,
size: '100',
price: '1.00',
expiresAt: displayTomorrow(),
postOnly: false,
reduceOnly: false,
};
createOrder(order);
testOrderSubmission(order, {
price: '100000',
expiresAt:
new Date(order.expiresAt as string).getTime().toString() + '000000',
});
});
}
);
@@ -1,85 +0,0 @@
import * as Schema from '@vegaprotocol/types';
import { testOrderSubmission } from '../support/order-validation';
import type { OrderSubmission } from '@vegaprotocol/wallet';
import { createOrder } from '../support/create-order';
const displayTomorrow = () => {
const tomorrow = new Date();
tomorrow.setDate(tomorrow.getDate() + 1);
return tomorrow.toISOString().substring(0, 16);
};
describe(
'must submit order for market in opening auction',
{ tags: '@regression' },
() => {
before(() => {
cy.setVegaWallet();
cy.mockTradingPage(
Schema.MarketState.STATE_SUSPENDED,
Schema.MarketTradingMode.TRADING_MODE_OPENING_AUCTION,
Schema.AuctionTrigger.AUCTION_TRIGGER_LIQUIDITY_TARGET_NOT_MET
);
cy.mockSubscription();
cy.visit('/#/markets/market-0');
cy.wait('@Markets');
});
beforeEach(() => {
cy.setVegaWallet();
});
it('successfully places limit buy order', () => {
cy.mockVegaWalletTransaction();
const order: OrderSubmission = {
marketId: 'market-0',
type: Schema.OrderType.TYPE_LIMIT,
side: Schema.Side.SIDE_BUY,
timeInForce: Schema.OrderTimeInForce.TIME_IN_FORCE_GTC,
size: '100',
postOnly: false,
reduceOnly: false,
price: '200',
};
createOrder(order);
testOrderSubmission(order, { price: '20000000' });
});
it('successfully places limit sell order', () => {
cy.mockVegaWalletTransaction();
const order: OrderSubmission = {
marketId: 'market-0',
type: Schema.OrderType.TYPE_LIMIT,
side: Schema.Side.SIDE_SELL,
timeInForce: Schema.OrderTimeInForce.TIME_IN_FORCE_GTC,
size: '100',
postOnly: false,
reduceOnly: false,
price: '50000',
};
createOrder(order);
testOrderSubmission(order, { price: '5000000000' });
});
it('successfully places GTT limit buy order', () => {
cy.mockVegaWalletTransaction();
const order: OrderSubmission = {
marketId: 'market-0',
type: Schema.OrderType.TYPE_LIMIT,
side: Schema.Side.SIDE_SELL,
timeInForce: Schema.OrderTimeInForce.TIME_IN_FORCE_GTT,
size: '100',
price: '1.00',
expiresAt: displayTomorrow(),
postOnly: false,
reduceOnly: false,
};
createOrder(order);
testOrderSubmission(order, {
price: '100000',
expiresAt:
new Date(order.expiresAt as string).getTime().toString() + '000000',
});
});
}
);
@@ -1,103 +0,0 @@
import * as Schema from '@vegaprotocol/types';
import type { OrderSubmission } from '@vegaprotocol/wallet';
import { createOrder } from '../support/create-order';
describe(
'vega wallet - prompt',
{ tags: '@regression', testIsolation: true },
() => {
describe('must submit order', { tags: '@smoke' }, () => {
// 7002-SORD-039
beforeEach(() => {
cy.setVegaWallet();
cy.mockTradingPage();
cy.mockSubscription();
cy.visit('/#/markets/market-0');
cy.wait('@Markets');
});
it('must see a prompt to check connected vega wallet to approve transaction', () => {
// 0003-WTXN-002
cy.mockVegaWalletTransaction(1000);
const order: OrderSubmission = {
marketId: 'market-0',
type: Schema.OrderType.TYPE_MARKET,
side: Schema.Side.SIDE_BUY,
timeInForce: Schema.OrderTimeInForce.TIME_IN_FORCE_FOK,
size: '100',
};
createOrder(order);
cy.getByTestId('toast-content').should(
'contain.text',
'Please go to your Vega wallet application and approve or reject the transaction.'
);
});
it('must show error returned by wallet ', () => {
// 0003-WTXN-009
// 0003-WTXN-011
// 0002-WCON-016
// 0003-WTXN-008
//trigger error from the wallet
cy.intercept('POST', 'http://localhost:1789/api/v2/requests', (req) => {
req.on('response', (res) => {
res.send({
jsonrpc: '2.0',
id: '1',
});
});
});
const order: OrderSubmission = {
marketId: 'market-0',
type: Schema.OrderType.TYPE_MARKET,
side: Schema.Side.SIDE_BUY,
timeInForce: Schema.OrderTimeInForce.TIME_IN_FORCE_FOK,
size: '100',
};
createOrder(order);
cy.getByTestId('toast-content').should(
'contain.text',
'The connection to your Vega Wallet has been lost.'
);
cy.getByTestId('connect-vega-wallet').click();
cy.getByTestId('dialog-content').should('be.visible');
});
it('must see that the order was rejected by the connected wallet', () => {
// 0003-WTXN-007
//trigger rejection error from the wallet
cy.intercept('POST', 'http://localhost:1789/api/v2/requests', (req) => {
req.alias = 'client.send_transaction';
req.reply({
statusCode: 400,
body: {
jsonrpc: '2.0',
error: {
code: 3001,
data: 'the user rejected the wallet connection',
message: 'User error',
},
id: '0',
},
});
});
const order: OrderSubmission = {
marketId: 'market-0',
type: Schema.OrderType.TYPE_MARKET,
side: Schema.Side.SIDE_BUY,
timeInForce: Schema.OrderTimeInForce.TIME_IN_FORCE_FOK,
size: '100',
};
createOrder(order);
cy.getByTestId('toast-content').should(
'contain.text',
'Error occurredthe user rejected the wallet connection'
);
});
});
}
);
@@ -26,18 +26,6 @@ describe('ethereum wallet', { tags: '@smoke', testIsolation: true }, () => {
cy.getByTestId('tab-deposits').should('not.be.empty');
});
it.skip('should see QR code modal for WalletConnect', () => {
// 0004-EWAL-003
cy.getByTestId('Deposits').click();
cy.getByTestId('deposit-button').click();
cy.getByTestId('connect-eth-wallet-btn').click();
cy.getByTestId('web3-connector-list').should('exist');
cy.getByTestId('web3-connector-WalletConnect').click();
// testing if exists rather than visible because of the long loading time
cy.get('#w3m-modal').should('exist');
});
it('able to disconnect eth wallet', () => {
// 0004-EWAL-004
// 0004-EWAL-005
@@ -1,91 +0,0 @@
import {
mockConnectWallet,
mockConnectWalletWithUserError,
} from '@vegaprotocol/cypress';
const connectVegaBtn = 'connect-vega-wallet';
const manageVegaBtn = 'manage-vega-wallet';
const dialogContent = 'dialog-content';
describe('connect vega wallet', { tags: '@smoke', testIsolation: true }, () => {
beforeEach(() => {
// Using portfolio page as it requires vega wallet connection
cy.mockTradingPage();
cy.mockSubscription();
cy.setOnBoardingViewed();
cy.visit('/#/portfolio');
cy.get('[data-testid="pathname-/portfolio"]').should('exist');
});
it('can connect', () => {
// 0002-WCON-002
// 0002-WCON-005
// 0002-WCON-007
// 0002-WCON-009
mockConnectWallet();
cy.getByTestId(connectVegaBtn).click();
cy.getByTestId('connectors-list')
.find('[data-testid="connector-jsonRpc"]')
.click();
cy.wait('@walletReq');
cy.getByTestId(dialogContent).should(
'contain.text',
'Approve the connection from your Vega wallet app.'
);
cy.getByTestId(dialogContent).should('not.exist');
cy.getByTestId(manageVegaBtn).should('exist');
});
it('can not connect', () => {
// 0002-WCON-002
// 0002-WCON-005
// 0002-WCON-007
// 0002-WCON-015
mockConnectWalletWithUserError();
cy.getByTestId(connectVegaBtn).click();
cy.getByTestId('connectors-list')
.find('[data-testid="connector-jsonRpc"]')
.click();
cy.getByTestId('dialog-content')
.should('contain.text', 'User error')
.and('contain.text', 'the user rejected the wallet connection');
});
it('can change selected public key and disconnect', () => {
// 0002-WCON-022
// 0002-WCON-023
// 0002-WCON-025
// 0002-WCON-026
// 0002-WCON-021
// 0002-WCON-027
// 0002-WCON-030
// 0002-WCON-029
// 0002-WCON-008
// 0002-WCON-035
// 0002-WCON-014
// 0002-WCON-010
// 0003-WTXN-004
mockConnectWallet();
const key2 = Cypress.env('VEGA_PUBLIC_KEY2');
const truncatedKey2 = Cypress.env('TRUNCATED_VEGA_PUBLIC_KEY2');
cy.connectVegaWallet();
cy.getByTestId('manage-vega-wallet').click();
cy.getByTestId('keypair-list').should('exist');
cy.getByTestId(`key-${key2}`).should('contain.text', truncatedKey2);
cy.getByTestId(`key-${key2}`)
.find('[data-testid="copy-vega-public-key"]')
.should('be.visible');
cy.get(`[data-testid="key-${key2}"] > .mr-2`).click();
cy.getByTestId('keypair-list')
.find('[data-state="checked"]')
.should('be.visible');
cy.getByTestId('disconnect').click();
cy.getByTestId('connect-vega-wallet').should('exist');
cy.getByTestId('manage-vega-wallet').should('not.exist');
cy.getByTestId('connect-vega-wallet').click();
cy.contains('Enter a custom wallet location');
});
});
+2 -2
View File
@@ -22,9 +22,9 @@ NX_WALLETCONNECT_PROJECT_ID=fe8091dc35738863e509fc4947525c72
NX_SUCCESSOR_MARKETS=true
NX_STOP_ORDERS=true
NX_ICEBERG_ORDERS=true
# NX_PRODUCT_PERPETUALS
NX_METAMASK_SNAPS=true
NX_REFERRALS=false
NX_REFERRALS=true
# NX_DISABLE_CLOSE_POSITION=false
NX_TENDERMINT_URL=https://be.vega.community
NX_TENDERMINT_WEBSOCKET_URL=wss://be.vega.community/websocket
+4 -1
View File
@@ -24,4 +24,7 @@ NX_STOP_ORDERS=true
NX_ICEBERG_ORDERS=true
# NX_PRODUCT_PERPETUALS
NX_METAMASK_SNAPS=true
NX_REFERRALS=true
NX_REFERRALS=true
NX_CHARTING_LIBRARY_PATH=https://assets.vega.community/trading-view-bundle/v0.0.1/
NX_CHARTING_LIBRARY_HASH=PDjWaqPFndDp+LCvqbKvntWriaqNzNpZ5i9R/BULzCg=
+3 -1
View File
@@ -23,9 +23,11 @@ NX_WALLETCONNECT_PROJECT_ID=fe8091dc35738863e509fc4947525c72
NX_SUCCESSOR_MARKETS=true
NX_STOP_ORDERS=true
NX_ICEBERG_ORDERS=true
# NX_PRODUCT_PERPETUALS
NX_METAMASK_SNAPS=true
NX_REFERRALS=true
NX_TENDERMINT_URL=https://tm.be.testnet.vega.xyz
NX_TENDERMINT_WEBSOCKET_URL=wss://be.testnet.vega.xyz/websocket
NX_CHARTING_LIBRARY_PATH=https://assets.vega.community/trading-view-bundle/v0.0.1/
NX_CHARTING_LIBRARY_HASH=PDjWaqPFndDp+LCvqbKvntWriaqNzNpZ5i9R/BULzCg=
+19 -4
View File
@@ -1,12 +1,27 @@
import { useEffect } from 'react';
import { titlefy } from '@vegaprotocol/utils';
import { ErrorBoundary } from '../../components/error-boundary';
import { FeesContainer } from '../../components/fees-container';
import { useT } from '../../lib/use-t';
import { usePageTitleStore } from '../../stores';
export const Fees = () => {
const t = useT();
const title = t('Fees');
const { updateTitle } = usePageTitleStore((store) => ({
updateTitle: store.updateTitle,
}));
useEffect(() => {
updateTitle(titlefy([title]));
}, [updateTitle, title]);
return (
<div className="container p-4 mx-auto">
<h1 className="px-4 pb-4 text-2xl">{t('Fees')}</h1>
<FeesContainer />
</div>
<ErrorBoundary feature="fees">
<div className="container p-4 mx-auto">
<h1 className="px-4 pb-4 text-2xl">{title}</h1>
<FeesContainer />
</div>
</ErrorBoundary>
);
};
+2 -25
View File
@@ -1,34 +1,11 @@
import { useEffect } from 'react';
import { useNavigate } from 'react-router-dom';
import { Loader, Splash } from '@vegaprotocol/ui-toolkit';
import { useGlobalStore } from '../../stores';
import { useTopTradedMarkets } from '../../lib/hooks/use-top-traded-markets';
import { Links } from '../../lib/links';
import { useNavigateToLastMarket } from '../../lib/hooks/use-navigate-to-last-market';
// The home pages only purpose is to redirect to the users last market,
// the top traded if they are new, or fall back to the list of markets.
// Thats why we just render a loader here
export const Home = () => {
const navigate = useNavigate();
const { data } = useTopTradedMarkets();
const marketId = useGlobalStore((store) => store.marketId);
useEffect(() => {
if (marketId) {
navigate(Links.MARKET(marketId), {
replace: true,
});
} else if (data) {
const marketDataId = data[0]?.id;
if (marketDataId) {
navigate(Links.MARKET(marketDataId), {
replace: true,
});
} else {
navigate(Links.MARKETS());
}
}
}, [marketId, data, navigate]);
useNavigateToLastMarket();
return (
<Splash>
@@ -6,6 +6,7 @@ import { useEffect, useState } from 'react';
import { useParams } from 'react-router-dom';
import { LiquidityContainer } from '../../components/liquidity-container';
import { useT } from '../../lib/use-t';
import { ErrorBoundary } from '../../components/error-boundary';
const enum LiquidityTabs {
Active = 'active',
@@ -58,19 +59,28 @@ export const LiquidityViewContainer = ({
name={t('My liquidity provision')}
hidden={!pubKey}
>
<LiquidityContainer
marketId={marketId}
filter={{ partyId: pubKey || undefined }}
/>
<ErrorBoundary feature="liquidity-party">
<LiquidityContainer
marketId={marketId}
filter={{ partyId: pubKey || undefined }}
/>
</ErrorBoundary>
</Tab>
<Tab id={LiquidityTabs.Active} name={t('Active')}>
<LiquidityContainer marketId={marketId} filter={{ active: true }} />
<ErrorBoundary feature="liquidity-active">
<LiquidityContainer
marketId={marketId}
filter={{ active: true }}
/>
</ErrorBoundary>
</Tab>
<Tab id={LiquidityTabs.Inactive} name={t('Inactive')}>
<LiquidityContainer
marketId={marketId}
filter={{ active: false }}
/>
<ErrorBoundary feature="liquidity-inactive">
<LiquidityContainer
marketId={marketId}
filter={{ active: false }}
/>
</ErrorBoundary>
</Tab>
</Tabs>
</div>
@@ -1,7 +1,6 @@
import { useAssetDetailsDialogStore } from '@vegaprotocol/assets';
import { DocsLinks, useEnvironment } from '@vegaprotocol/environment';
import { ButtonLink, ExternalLink, Link } from '@vegaprotocol/ui-toolkit';
import { MarketProposalNotification } from '@vegaprotocol/proposals';
import type { Market } from '@vegaprotocol/markets';
import {
addDecimalsFormatNumber,
@@ -145,7 +144,6 @@ export const MarketHeaderStats = ({ market }: MarketHeaderStatsProps) => {
/>
</HeaderStat>
)}
<MarketProposalNotification marketId={market.id} />
</>
);
};
@@ -190,12 +188,11 @@ const useNow = () => {
return now;
};
const useEvery = (marketId: string) => {
const { data: marketTradingMode } = useMarketTradingMode(marketId);
const useEvery = (marketId: string, skip: boolean) => {
const { data: marketInfo } = useDataProvider({
dataProvider: marketInfoProvider,
variables: { marketId },
skip: !marketTradingMode || isMarketInAuction(marketTradingMode),
skip,
});
let every: number | undefined = undefined;
const sourceType =
@@ -213,8 +210,10 @@ const useEvery = (marketId: string) => {
return every;
};
const useStartTime = (marketId: string) => {
const useStartTime = (marketId: string, skip: boolean) => {
const { data: fundingPeriods } = useFundingPeriodsQuery({
pollInterval: 5000,
skip,
variables: {
marketId: marketId,
pagination: { first: 1 },
@@ -248,8 +247,10 @@ const useFormatCountdown = (
export const FundingCountdown = ({ marketId }: { marketId: string }) => {
const now = useNow();
const startTime = useStartTime(marketId);
const every = useEvery(marketId);
const { data: marketTradingMode } = useMarketTradingMode(marketId);
const skip = !marketTradingMode || isMarketInAuction(marketTradingMode);
const startTime = useStartTime(marketId, skip);
const every = useEvery(marketId, skip);
return (
<div data-testid="funding-countdown">
+6 -19
View File
@@ -2,15 +2,13 @@ import React, { useEffect, useMemo } from 'react';
import { addDecimalsFormatNumber, titlefy } from '@vegaprotocol/utils';
import { useScreenDimensions } from '@vegaprotocol/react-helpers';
import { useThrottledDataProvider } from '@vegaprotocol/data-provider';
import { ExternalLink, Loader, Splash } from '@vegaprotocol/ui-toolkit';
import { Link, Loader, Splash } from '@vegaprotocol/ui-toolkit';
import { getAsset, marketDataProvider, useMarket } from '@vegaprotocol/markets';
import { useGlobalStore, usePageTitleStore } from '../../stores';
import { TradeGrid } from './trade-grid';
import { TradePanels } from './trade-panels';
import { useNavigate, useParams } from 'react-router-dom';
import { Links } from '../../lib/links';
import { ViewType, useSidebar } from '../../components/sidebar';
import { useGetCurrentRouteId } from '../../lib/hooks/use-get-current-route-id';
import { useT, ns } from '../../lib/use-t';
import { Trans } from 'react-i18next';
@@ -61,9 +59,6 @@ export const MarketPage = () => {
const t = useT();
const { marketId } = useParams();
const navigate = useNavigate();
const currentRouteId = useGetCurrentRouteId();
const { setViews, getView } = useSidebar();
const view = getView(currentRouteId);
const { screenSize } = useScreenDimensions();
const largeScreen = ['lg', 'xl', 'xxl', 'xxxl'].includes(screenSize);
const update = useGlobalStore((store) => store.update);
@@ -72,20 +67,11 @@ export const MarketPage = () => {
const { data, loading } = useMarket(marketId);
useEffect(() => {
if (data?.id && data.id !== lastMarketId && !closed) {
if (data?.id && data.id !== lastMarketId) {
update({ marketId: data.id });
}
}, [update, lastMarketId, data?.id]);
useEffect(() => {
if (largeScreen && view === undefined) {
setViews(
{ type: closed ? ViewType.Info : ViewType.Order },
currentRouteId
);
}
}, [setViews, view, currentRouteId, largeScreen]);
const pinnedAsset = data && getAsset(data);
const tradeView = useMemo(() => {
@@ -114,15 +100,16 @@ export const MarketPage = () => {
</p>
<p className="justify-center text-sm">
<Trans
defaults="Please choose another market from the <0>market list<0>"
defaults="Please choose another market from the <0>market list</0>"
ns={ns}
components={[
<ExternalLink
<Link
className="underline underline-offset-4 "
onClick={() => navigate(Links.MARKETS())}
key="link"
>
market list
</ExternalLink>,
</Link>,
]}
/>
</p>
+53 -20
View File
@@ -18,8 +18,9 @@ import {
MarketSuccessorProposalBanner,
MarketTerminationBanner,
} from '../../components/market-banner';
import { FLAGS } from '@vegaprotocol/environment';
import { useFeatureFlags } from '@vegaprotocol/environment';
import { useT } from '../../lib/use-t';
import { ErrorBoundary } from '../../components/error-boundary';
interface TradeGridProps {
market: Market | null;
@@ -34,6 +35,7 @@ const MainGrid = memo(
marketId: string;
pinnedAsset?: PinnedAsset;
}) => {
const featureFlags = useFeatureFlags((state) => state.flags);
const t = useT();
const { data: market } = useMarket(marketId);
const [sizes, handleOnLayoutChange] = usePaneLayout({ id: 'top' });
@@ -60,30 +62,40 @@ const MainGrid = memo(
id="chart"
overflowHidden
name={t('Chart')}
menu={<TradingViews.candles.menu />}
menu={<TradingViews.chart.menu />}
>
<TradingViews.candles.component marketId={marketId} />
<ErrorBoundary feature="chart">
<TradingViews.chart.component marketId={marketId} />
</ErrorBoundary>
</Tab>
<Tab id="depth" name={t('Depth')}>
<TradingViews.depth.component marketId={marketId} />
<ErrorBoundary feature="depth">
<TradingViews.depth.component marketId={marketId} />
</ErrorBoundary>
</Tab>
<Tab id="liquidity" name={t('Liquidity')}>
<TradingViews.liquidity.component marketId={marketId} />
<ErrorBoundary feature="liquidity">
<TradingViews.liquidity.component marketId={marketId} />
</ErrorBoundary>
</Tab>
{market &&
market.tradableInstrument.instrument.product.__typename ===
'Perpetual' ? (
<Tab id="funding-history" name={t('Funding history')}>
<TradingViews.funding.component marketId={marketId} />
<ErrorBoundary feature="funding-history">
<TradingViews.funding.component marketId={marketId} />
</ErrorBoundary>
</Tab>
) : null}
{market &&
market.tradableInstrument.instrument.product.__typename ===
'Perpetual' ? (
<Tab id="funding-payments" name={t('Funding payments')}>
<TradingViews.fundingPayments.component
marketId={marketId}
/>
<ErrorBoundary feature="funding-payments">
<TradingViews.fundingPayments.component
marketId={marketId}
/>
</ErrorBoundary>
</Tab>
) : null}
</Tabs>
@@ -96,10 +108,14 @@ const MainGrid = memo(
<TradeGridChild>
<Tabs storageKey="console-trade-grid-main-right">
<Tab id="orderbook" name={t('Orderbook')}>
<TradingViews.orderbook.component marketId={marketId} />
<ErrorBoundary feature="orderbook">
<TradingViews.orderbook.component marketId={marketId} />
</ErrorBoundary>
</Tab>
<Tab id="trades" name={t('Trades')}>
<TradingViews.trades.component marketId={marketId} />
<ErrorBoundary feature="trades">
<TradingViews.trades.component marketId={marketId} />
</ErrorBoundary>
</Tab>
</Tabs>
</TradeGridChild>
@@ -118,31 +134,43 @@ const MainGrid = memo(
name={t('Positions')}
menu={<TradingViews.positions.menu />}
>
<TradingViews.positions.component />
<ErrorBoundary feature="positions">
<TradingViews.positions.component />
</ErrorBoundary>
</Tab>
<Tab
id="open-orders"
name={t('Open')}
menu={<TradingViews.activeOrders.menu />}
>
<TradingViews.activeOrders.component />
<ErrorBoundary feature="activeOrders">
<TradingViews.activeOrders.component />
</ErrorBoundary>
</Tab>
<Tab id="closed-orders" name={t('Closed')}>
<TradingViews.closedOrders.component />
<ErrorBoundary feature="closedOrders">
<TradingViews.closedOrders.component />
</ErrorBoundary>
</Tab>
<Tab id="rejected-orders" name={t('Rejected')}>
<TradingViews.rejectedOrders.component />
<ErrorBoundary feature="rejectedOrders">
<TradingViews.rejectedOrders.component />
</ErrorBoundary>
</Tab>
<Tab
id="orders"
name={t('All')}
menu={<TradingViews.orders.menu />}
>
<TradingViews.orders.component />
<ErrorBoundary feature="orders">
<TradingViews.orders.component />
</ErrorBoundary>
</Tab>
{FLAGS.STOP_ORDERS ? (
{featureFlags.STOP_ORDERS ? (
<Tab id="stop-orders" name={t('Stop orders')}>
<TradingViews.stopOrders.component />
<ErrorBoundary feature="stop-orders">
<TradingViews.stopOrders.component />
</ErrorBoundary>
</Tab>
) : null}
<Tab id="fills" name={t('Fills')}>
@@ -153,7 +181,11 @@ const MainGrid = memo(
name={t('Collateral')}
menu={<TradingViews.collateral.menu />}
>
<TradingViews.collateral.component pinnedAsset={pinnedAsset} />
<ErrorBoundary feature="collateral">
<TradingViews.collateral.component
pinnedAsset={pinnedAsset}
/>
</ErrorBoundary>
</Tab>
</Tabs>
</TradeGridChild>
@@ -165,6 +197,7 @@ const MainGrid = memo(
MainGrid.displayName = 'MainGrid';
export const TradeGrid = ({ market, pinnedAsset }: TradeGridProps) => {
const featureFlags = useFeatureFlags((state) => state.flags);
const wrapperClasses = classNames(
'h-full grid',
'grid-rows-[min-content_1fr]'
@@ -173,7 +206,7 @@ export const TradeGrid = ({ market, pinnedAsset }: TradeGridProps) => {
return (
<div className={wrapperClasses}>
<div>
{FLAGS.SUCCESSOR_MARKETS && (
{featureFlags.SUCCESSOR_MARKETS && (
<>
<MarketSuccessorBanner market={market} />
<MarketSuccessorProposalBanner marketId={market?.id} />
@@ -1,19 +1,20 @@
import type { PinnedAsset } from '@vegaprotocol/accounts';
import type { Market } from '@vegaprotocol/markets';
import { type PinnedAsset } from '@vegaprotocol/accounts';
import { type Market } from '@vegaprotocol/markets';
import { OracleBanner } from '@vegaprotocol/markets';
import type { TradingView } from './trade-views';
import { TradingViews } from './trade-views';
import { useState } from 'react';
import AutoSizer from 'react-virtualized-auto-sizer';
import classNames from 'classnames';
import { Splash } from '@vegaprotocol/ui-toolkit';
import { useT } from '../../lib/use-t';
import {
MarketSuccessorBanner,
MarketSuccessorProposalBanner,
MarketTerminationBanner,
} from '../../components/market-banner';
import { FLAGS } from '@vegaprotocol/environment';
import { useT } from '../../lib/use-t';
import { Splash } from '@vegaprotocol/ui-toolkit';
import { ErrorBoundary } from '../../components/error-boundary';
import { type TradingView } from './trade-views';
import { TradingViews } from './trade-views';
import { useFeatureFlags } from '@vegaprotocol/environment';
interface TradePanelsProps {
market: Market | null;
@@ -21,7 +22,8 @@ interface TradePanelsProps {
}
export const TradePanels = ({ market, pinnedAsset }: TradePanelsProps) => {
const [view, setView] = useState<TradingView>('candles');
const featureFlags = useFeatureFlags((state) => state.flags);
const [view, setView] = useState<TradingView>('chart');
const renderView = () => {
const Component = TradingViews[view].component;
@@ -34,7 +36,11 @@ export const TradePanels = ({ market, pinnedAsset }: TradePanelsProps) => {
// Watch out here, we don't know what component is being rendered
// so watch out for clashes in props
return <Component marketId={market?.id} pinnedAsset={pinnedAsset} />;
return (
<ErrorBoundary feature={view}>
<Component marketId={market?.id} pinnedAsset={pinnedAsset} />;
</ErrorBoundary>
);
};
const renderMenu = () => {
@@ -44,7 +50,7 @@ export const TradePanels = ({ market, pinnedAsset }: TradePanelsProps) => {
const Menu = viewCfg.menu;
return (
<div className="flex gap-1 p-1 bg-vega-clight-800 dark:bg-vega-cdark-800 border-b border-default">
<div className="flex items-center justify-end gap-1 p-1 bg-vega-clight-800 dark:bg-vega-cdark-800 border-b border-default">
<Menu />
</div>
);
@@ -56,7 +62,7 @@ export const TradePanels = ({ market, pinnedAsset }: TradePanelsProps) => {
return (
<div className="h-full grid grid-rows-[min-content_min-content_1fr_min-content]">
<div>
{FLAGS.SUCCESSOR_MARKETS && (
{featureFlags.SUCCESSOR_MARKETS && (
<>
<MarketSuccessorBanner market={market} />
<MarketSuccessorProposalBanner marketId={market?.id} />
@@ -143,7 +149,7 @@ const useViewLabel = (view: TradingView) => {
const t = useT();
const labels = {
candles: t('Candles'),
chart: t('Chart'),
depth: t('Depth'),
liquidity: t('Liquidity'),
funding: t('Funding'),
@@ -1,8 +1,4 @@
import { DepthChartContainer } from '@vegaprotocol/market-depth';
import {
CandlesChartContainer,
CandlesMenu,
} from '@vegaprotocol/candles-chart';
import { Filter, OpenOrdersMenu } from '@vegaprotocol/orders';
import { TradesContainer } from '../../components/trades-container';
import { OrderbookContainer } from '../../components/orderbook-container';
@@ -16,13 +12,14 @@ import { OrdersContainer } from '../../components/orders-container';
import { StopOrdersContainer } from '../../components/stop-orders-container';
import { AccountsMenu } from '../../components/accounts-menu';
import { PositionsMenu } from '../../components/positions-menu';
import { ChartContainer, ChartMenu } from '../../components/chart-container';
export type TradingView = keyof typeof TradingViews;
export const TradingViews = {
candles: {
component: CandlesChartContainer,
menu: CandlesMenu,
chart: {
component: ChartContainer,
menu: ChartMenu,
},
depth: {
component: DepthChartContainer,
@@ -1,4 +1,5 @@
import { act, render, screen, waitFor, within } from '@testing-library/react';
// import userEvent from '@testing-library/user-event';
import { MemoryRouter } from 'react-router-dom';
import { Closed } from './closed';
import { MarketStateMapping, PropertyKeyType } from '@vegaprotocol/types';
@@ -26,6 +27,7 @@ import {
marketsDataQuery,
createMarketsDataFragment,
} from '@vegaprotocol/mock';
import userEvent from '@testing-library/user-event';
describe('Closed', () => {
let originalNow: typeof Date.now;
@@ -168,14 +170,11 @@ describe('Closed', () => {
Date.now = originalNow;
});
// eslint-disable-next-line jest/no-disabled-tests
it.skip('renders correctly formatted and filtered rows', async () => {
const renderComponent = async (mocks: MockedResponse[]) => {
await act(async () => {
render(
<MemoryRouter>
<MockedProvider
mocks={[marketsMock, marketsDataMock, oracleDataMock]}
>
<MockedProvider mocks={mocks}>
<VegaWalletContext.Provider
value={{ pubKey } as VegaWalletContextShape}
>
@@ -185,6 +184,10 @@ describe('Closed', () => {
</MemoryRouter>
);
});
};
it('renders correct headers', async () => {
await renderComponent([marketsMock, marketsDataMock, oracleDataMock]);
const headers = screen.getAllByRole('columnheader');
const expectedHeaders = [
@@ -200,6 +203,10 @@ describe('Closed', () => {
];
expect(headers).toHaveLength(expectedHeaders.length);
expect(headers.map((h) => h.textContent?.trim())).toEqual(expectedHeaders);
});
it('renders correctly formatted and filtered rows', async () => {
await renderComponent([marketsMock, marketsDataMock, oracleDataMock]);
const assetSymbol = getAsset(market).symbol;
@@ -273,21 +280,8 @@ describe('Closed', () => {
},
},
};
await act(async () => {
render(
<MemoryRouter>
<MockedProvider
mocks={[mixedMarketsMock, marketsDataMock, oracleDataMock]}
>
<VegaWalletContext.Provider
value={{ pubKey } as VegaWalletContextShape}
>
<Closed />
</VegaWalletContext.Provider>
</MockedProvider>
</MemoryRouter>
);
});
await renderComponent([mixedMarketsMock, marketsDataMock, oracleDataMock]);
// check that the number of rows in datagrid is 2
const container = within(
@@ -319,8 +313,67 @@ describe('Closed', () => {
);
});
// eslint-disable-next-line jest/no-disabled-tests
it.skip('successor marked should be visible', async () => {
it('display market actions', async () => {
// Use market with a succcessor Id as the actions dropdown will optionally
// show a link to the successor market
const marketsWithSuccessorAndParent = [
{
__typename: 'MarketEdge' as const,
node: createMarketFragment({
id: 'include-0',
state: MarketState.STATE_SETTLED,
successorMarketID: 'successor',
parentMarketID: 'parent',
}),
},
];
const mockWithSuccessorAndParent: MockedResponse<MarketsQuery> = {
request: {
query: MarketsDocument,
},
result: {
data: {
marketsConnection: {
__typename: 'MarketConnection',
edges: marketsWithSuccessorAndParent,
},
},
},
};
await renderComponent([
mockWithSuccessorAndParent,
marketsDataMock,
oracleDataMock,
]);
const actionCell = screen
.getAllByRole('gridcell')
.find((el) => el.getAttribute('col-id') === 'market-actions');
await userEvent.click(
within(actionCell as HTMLElement).getByTestId('dropdown-menu')
);
expect(screen.getByRole('menu')).toBeInTheDocument();
expect(
screen.getByRole('menuitem', { name: 'Copy Market ID' })
).toBeInTheDocument();
expect(
screen.getByRole('menuitem', { name: 'View on Explorer' })
).toBeInTheDocument();
expect(
screen.getByRole('menuitem', { name: 'View settlement asset details' })
).toBeInTheDocument();
expect(
screen.getByRole('menuitem', { name: 'View parent market' })
).toBeInTheDocument();
expect(
screen.getByRole('menuitem', { name: 'View successor market' })
).toBeInTheDocument();
});
it('successor market should be visible', async () => {
const marketsWithSuccessorID = [
{
__typename: 'MarketEdge' as const,
@@ -345,21 +398,11 @@ describe('Closed', () => {
},
};
await act(async () => {
render(
<MemoryRouter>
<MockedProvider
mocks={[mockWithSuccessors, marketsDataMock, oracleDataMock]}
>
<VegaWalletContext.Provider
value={{ pubKey } as VegaWalletContextShape}
>
<Closed />
</VegaWalletContext.Provider>
</MockedProvider>
</MemoryRouter>
);
});
await renderComponent([
mockWithSuccessors,
marketsDataMock,
oracleDataMock,
]);
const container = within(
document.querySelector('.ag-center-cols-container') as HTMLElement
@@ -1,7 +1,14 @@
import type { TypedDataAgGrid } from '@vegaprotocol/datagrid';
import { AgGrid, PriceFlashCell } from '@vegaprotocol/datagrid';
import {
AgGrid,
PriceFlashCell,
useDataGridEvents,
} from '@vegaprotocol/datagrid';
import type { MarketMaybeWithData } from '@vegaprotocol/markets';
import { useColumnDefs } from './use-column-defs';
import type { DataGridStore } from '../../stores/datagrid-store-slice';
import { type StateCreator, create } from 'zustand';
import { persist } from 'zustand/middleware';
export const getRowId = ({ data }: { data: { id: string } }) => data.id;
@@ -18,8 +25,37 @@ const components = {
type Props = TypedDataAgGrid<MarketMaybeWithData>;
export type DataGridSlice = {
gridStore: DataGridStore;
updateGridStore: (gridStore: DataGridStore) => void;
};
export const createDataGridSlice: StateCreator<DataGridSlice> = (set) => ({
gridStore: {},
updateGridStore: (newStore) => {
set((curr) => ({
gridStore: {
...curr.gridStore,
...newStore,
},
}));
},
});
const useMarketsStore = create<DataGridSlice>()(
persist(createDataGridSlice, {
name: 'vega_market_list_store',
})
);
export const MarketListTable = (props: Props) => {
const columnDefs = useColumnDefs();
const gridStore = useMarketsStore((store) => store.gridStore);
const updateGridStore = useMarketsStore((store) => store.updateGridStore);
const gridStoreCallbacks = useDataGridEvents(gridStore, (colState) => {
updateGridStore(colState);
});
return (
<AgGrid
@@ -28,6 +64,7 @@ export const MarketListTable = (props: Props) => {
columnDefs={columnDefs}
components={components}
rowHeight={45}
{...gridStoreCallbacks}
{...props}
/>
);
@@ -15,6 +15,7 @@ import {
useLinks,
} from '@vegaprotocol/environment';
import { useT } from '../../lib/use-t';
import { ErrorBoundary } from '../../components/error-boundary';
export const MarketsPage = () => {
const t = useT();
@@ -34,7 +35,9 @@ export const MarketsPage = () => {
<div className="h-full my-1 border rounded-sm border-default">
<Tabs storageKey="console-markets">
<Tab id="open-markets" name={t('Open markets')}>
<OpenMarkets />
<ErrorBoundary feature="markets-open">
<OpenMarkets />
</ErrorBoundary>
</Tab>
<Tab
id="proposed-markets"
@@ -49,10 +52,14 @@ export const MarketsPage = () => {
</TradingAnchorButton>
}
>
<Proposed />
<ErrorBoundary feature="markets-proposed">
<Proposed />
</ErrorBoundary>
</Tab>
<Tab id="closed-markets" name={t('Closed markets')}>
<Closed />
<ErrorBoundary feature="markets-closed">
<Closed />
</ErrorBoundary>
</Tab>
</Tabs>
</div>
@@ -1,23 +1,33 @@
import { Route, Routes, useParams } from 'react-router-dom';
import { MarketState } from '@vegaprotocol/types';
import { useMarket } from '@vegaprotocol/markets';
import { Route, Routes } from 'react-router-dom';
import { VegaIconNames } from '@vegaprotocol/ui-toolkit';
import {
SidebarButton,
SidebarDivider,
ViewType,
useSidebar,
} from '../../components/sidebar';
import { useGetCurrentRouteId } from '../../lib/hooks/use-get-current-route-id';
import { useT } from '../../lib/use-t';
import { useScreenDimensions } from '@vegaprotocol/react-helpers';
import { useEffect } from 'react';
const ViewInitializer = () => {
const currentRouteId = useGetCurrentRouteId();
const { setViews, getView } = useSidebar();
const view = getView(currentRouteId);
const { screenSize } = useScreenDimensions();
const largeScreen = ['lg', 'xl', 'xxl', 'xxxl'].includes(screenSize);
useEffect(() => {
if (largeScreen && view === undefined) {
setViews({ type: ViewType.Order }, currentRouteId);
}
}, [setViews, view, currentRouteId, largeScreen]);
return null;
};
export const MarketsSidebar = () => {
const t = useT();
const { marketId } = useParams();
const currentRouteId = useGetCurrentRouteId();
const { data } = useMarket(marketId);
const active =
data &&
[MarketState.STATE_ACTIVE, MarketState.STATE_PENDING].includes(data.state);
return (
<>
@@ -44,15 +54,14 @@ export const MarketsSidebar = () => {
path=":marketId"
element={
<>
<ViewInitializer />
<SidebarDivider />
{active && (
<SidebarButton
view={ViewType.Order}
icon={VegaIconNames.TICKET}
tooltip={t('Order')}
routeId={currentRouteId}
/>
)}
<SidebarButton
view={ViewType.Order}
icon={VegaIconNames.TICKET}
tooltip={t('Order')}
routeId={currentRouteId}
/>
<SidebarButton
view={ViewType.Info}
icon={VegaIconNames.BREAKDOWN}
@@ -0,0 +1,145 @@
import { act, render, screen, within } from '@testing-library/react';
import { MemoryRouter } from 'react-router-dom';
import { OpenMarkets } from './open-markets';
import { Interval } from '@vegaprotocol/types';
import type { MockedResponse } from '@apollo/client/testing';
import { MockedProvider } from '@apollo/client/testing';
import type {
MarketsDataQuery,
MarketsQuery,
MarketCandlesQuery,
MarketFieldsFragment,
} from '@vegaprotocol/markets';
import {
MarketsDataDocument,
MarketsDocument,
MarketsCandlesDocument,
} from '@vegaprotocol/markets';
import type { VegaWalletContextShape } from '@vegaprotocol/wallet';
import { VegaWalletContext } from '@vegaprotocol/wallet';
import {
marketsQuery,
marketsDataQuery,
marketsCandlesQuery,
} from '@vegaprotocol/mock';
import userEvent from '@testing-library/user-event';
describe('Open', () => {
let originalNow: typeof Date.now;
const mockNowTimestamp = 1672531200000;
const pubKey = 'pubKey';
const marketsQueryData = marketsQuery();
const marketsMock: MockedResponse<MarketsQuery> = {
request: {
query: MarketsDocument,
},
result: {
data: marketsQueryData,
},
};
const marketsCandlesQueryData = marketsCandlesQuery();
const marketsCandlesMock: MockedResponse<MarketCandlesQuery> = {
request: {
query: MarketsCandlesDocument,
variables: {
interval: Interval.INTERVAL_I1H,
since: '2022-12-31T00:00:00.000Z',
},
},
result: {
data: marketsCandlesQueryData,
},
};
const marketsDataQueryData = marketsDataQuery();
const marketsDataMock: MockedResponse<MarketsDataQuery> = {
request: {
query: MarketsDataDocument,
},
result: {
data: marketsDataQueryData,
},
};
beforeAll(() => {
originalNow = Date.now;
Date.now = jest.fn().mockReturnValue(mockNowTimestamp);
});
afterAll(() => {
Date.now = originalNow;
});
const renderComponent = async () => {
await act(async () => {
render(
<MemoryRouter>
<MockedProvider
mocks={[marketsMock, marketsCandlesMock, marketsDataMock]}
>
<VegaWalletContext.Provider
value={{ pubKey } as VegaWalletContextShape}
>
<OpenMarkets />
</VegaWalletContext.Provider>
</MockedProvider>
</MemoryRouter>
);
});
};
it('renders correct headers', async () => {
await renderComponent();
const headers = screen.getAllByRole('columnheader');
const expectedHeaders = [
'Market',
'Description',
'Settlement asset',
'Trading mode',
'Status',
'Mark price',
'24h volume',
'Open Interest',
'Spread',
'', // Action row
];
expect(headers).toHaveLength(expectedHeaders.length);
expect(headers.map((h) => h.textContent?.trim())).toEqual(expectedHeaders);
});
it('sort columns', async () => {
await renderComponent();
const headers = screen.getAllByRole('columnheader');
const marketHeader = headers.find(
(h) => h.getAttribute('col-id') === 'tradableInstrument.instrument.code'
);
if (!marketHeader) {
throw new Error('No market header found');
}
expect(marketHeader).toHaveAttribute('aria-sort', 'none');
await userEvent.click(within(marketHeader).getByText(/market/i));
// 6001-MARK-064
expect(marketHeader).toHaveAttribute('aria-sort', 'ascending');
});
// eslint-disable-next-line jest/no-disabled-tests, jest/expect-expect
it('renders row', async () => {
await renderComponent();
const container = within(
document.querySelector('.ag-center-cols-container') as HTMLElement
);
const markets = marketsQueryData.marketsConnection?.edges.map(
(e) => e.node
) as MarketFieldsFragment[];
const rows = container.getAllByRole('row');
expect(rows).toHaveLength(markets.length);
});
});
@@ -43,7 +43,7 @@ export const OpenMarkets = () => {
if (!data) return;
// prevent navigating to the market page if any of the below cells are clicked
// event.preventDefault or event.stopPropagation dont seem to apply for aggird
// event.preventDefault or event.stopPropagation do not seem to apply for ag-grid
const colId = column.getColId();
if (
@@ -51,6 +51,29 @@ export const useColumnDefs = () => {
headerName: t('Description'),
field: 'tradableInstrument.instrument.name',
},
{
headerName: t('Settlement asset'),
field: 'tradableInstrument.instrument.product.settlementAsset.symbol',
cellRenderer: ({
data,
}: VegaICellRendererParams<
MarketMaybeWithData,
'tradableInstrument.instrument.product.settlementAsset.symbol'
>) => {
const value = data && getAsset(data);
return value ? (
<ButtonLink
onClick={(e) => {
openAssetDetailsDialog(value.id, e.target as HTMLElement);
}}
>
{value.symbol}
</ButtonLink>
) : (
''
);
},
},
{
headerName: t('Trading mode'),
field: 'tradingMode',
@@ -142,27 +165,21 @@ export const useColumnDefs = () => {
},
},
{
headerName: t('Settlement asset'),
field: 'tradableInstrument.instrument.product.settlementAsset.symbol',
cellRenderer: ({
headerName: t('Open Interest'),
field: 'data.openInterest',
type: 'rightAligned',
valueFormatter: ({
data,
}: VegaICellRendererParams<
}: VegaValueFormatterParams<
MarketMaybeWithData,
'tradableInstrument.instrument.product.settlementAsset.symbol'
>) => {
const value = data && getAsset(data);
return value ? (
<ButtonLink
onClick={(e) => {
openAssetDetailsDialog(value.id, e.target as HTMLElement);
}}
>
{value.symbol}
</ButtonLink>
) : (
''
);
},
'data.openInterest'
>) =>
data?.data?.openInterest === undefined
? '-'
: addDecimalsFormatNumber(
data?.data?.openInterest,
data?.positionDecimalPlaces
),
},
{
headerName: t('Spread'),
@@ -25,6 +25,7 @@ import { DepositsMenu } from '../../components/deposits-menu';
import { WithdrawalsMenu } from '../../components/withdrawals-menu';
import { useGetCurrentRouteId } from '../../lib/hooks/use-get-current-route-id';
import { useT } from '../../lib/use-t';
import { ErrorBoundary } from '../../components/error-boundary';
const WithdrawalsIndicator = () => {
const { ready } = useIncompleteWithdrawals();
@@ -38,11 +39,21 @@ const WithdrawalsIndicator = () => {
);
};
export const Portfolio = () => {
const t = useT();
const SidebarViewInitializer = () => {
const currentRouteId = useGetCurrentRouteId();
const { getView, setViews } = useSidebar();
const view = getView(currentRouteId);
// Make transfer sidebar open by default
useEffect(() => {
if (view === undefined) {
setViews({ type: ViewType.Transfer }, currentRouteId);
}
}, [view, setViews, currentRouteId]);
return null;
};
export const Portfolio = () => {
const t = useT();
const { updateTitle } = usePageTitleStore((store) => ({
updateTitle: store.updateTitle,
@@ -52,17 +63,11 @@ export const Portfolio = () => {
updateTitle(titlefy([t('Portfolio')]));
}, [updateTitle, t]);
// Make transfer sidebar open by default
useEffect(() => {
if (view === undefined) {
setViews({ type: ViewType.Transfer }, currentRouteId);
}
}, [view, setViews, currentRouteId]);
const [sizes, handleOnLayoutChange] = usePaneLayout({ id: 'portfolio' });
const wrapperClasses = 'p-0.5 h-full max-h-full flex flex-col';
return (
<div className={wrapperClasses}>
<SidebarViewInitializer />
<ResizableGrid vertical onChange={handleOnLayoutChange}>
<ResizableGridPanel minSize={75}>
<PortfolioGridChild>
@@ -72,19 +77,29 @@ export const Portfolio = () => {
name={t('Positions')}
menu={<PositionsMenu />}
>
<PositionsContainer allKeys />
<ErrorBoundary feature="portfolio-positions">
<PositionsContainer allKeys />
</ErrorBoundary>
</Tab>
<Tab id="orders" name={t('Orders')}>
<OrdersContainer />
<ErrorBoundary feature="portfolio-orders">
<OrdersContainer />
</ErrorBoundary>
</Tab>
<Tab id="fills" name={t('Fills')}>
<FillsContainer />
<ErrorBoundary feature="portfolio-fills">
<FillsContainer />
</ErrorBoundary>
</Tab>
<Tab id="funding-payments" name={t('Funding payments')}>
<FundingPaymentsContainer />
<ErrorBoundary feature="portfolio-funding-payments">
<FundingPaymentsContainer />
</ErrorBoundary>
</Tab>
<Tab id="ledger-entries" name={t('Ledger entries')}>
<LedgerContainer />
<ErrorBoundary feature="portfolio-ledger">
<LedgerContainer />
</ErrorBoundary>
</Tab>
</Tabs>
</PortfolioGridChild>
@@ -101,10 +116,14 @@ export const Portfolio = () => {
name={t('Collateral')}
menu={<AccountsMenu />}
>
<AccountsContainer />
<ErrorBoundary feature="portfolio-accounts">
<AccountsContainer />
</ErrorBoundary>
</Tab>
<Tab id="deposits" name={t('Deposits')} menu={<DepositsMenu />}>
<DepositsContainer />
<ErrorBoundary feature="portfolio-deposit">
<DepositsContainer />
</ErrorBoundary>
</Tab>
<Tab
id="withdrawals"
@@ -112,7 +131,9 @@ export const Portfolio = () => {
indicator={<WithdrawalsIndicator />}
menu={<WithdrawalsMenu />}
>
<WithdrawalsContainer />
<ErrorBoundary feature="portfolio-deposit">
<WithdrawalsContainer />
</ErrorBoundary>
</Tab>
</Tabs>
</PortfolioGridChild>
@@ -10,18 +10,43 @@ import { useForm } from 'react-hook-form';
import classNames from 'classnames';
import { Navigate, useNavigate, useSearchParams } from 'react-router-dom';
import type { ButtonHTMLAttributes, MouseEventHandler } from 'react';
import { useEffect, useRef, useState } from 'react';
import { useCallback, useEffect, useRef, useState } from 'react';
import { RainbowButton } from './buttons';
import { useVegaWallet, useVegaWalletDialogStore } from '@vegaprotocol/wallet';
import { useReferral } from './hooks/use-referral';
import { useIsInReferralSet, useReferral } from './hooks/use-referral';
import { Routes } from '../../lib/links';
import { useTransactionEventSubscription } from '@vegaprotocol/web3';
import { Statistics, useStats } from './referral-statistics';
import { useReferralProgram } from './hooks/use-referral-program';
import { useT } from '../../lib/use-t';
import { ns, useT } from '../../lib/use-t';
import { useFundsAvailable } from './hooks/use-funds-available';
import { ViewType, useSidebar } from '../../components/sidebar';
import { useGetCurrentRouteId } from '../../lib/hooks/use-get-current-route-id';
import { QUSDTooltip } from './qusd-tooltip';
import { Trans } from 'react-i18next';
const RELOAD_DELAY = 3000;
const SPAM_PROTECTION_ERR = 'SPAM_PROTECTION_ERR';
const SpamProtectionErr = ({
requiredFunds,
}: {
requiredFunds?: string | number | bigint;
}) => {
if (!requiredFunds) return null;
// eslint-disable-next-line react/jsx-no-undef
return (
<Trans
defaults="To protect the network from spam, you must have at least {{requiredFunds}} <0>qUSD</0> of any asset on the network to proceed."
values={{
requiredFunds,
}}
components={[<QUSDTooltip key="qusd" />]}
ns={ns}
/>
);
};
const validateCode = (value: string, t: ReturnType<typeof useT>) => {
const number = +`0x${value}`;
if (!value || value.length !== 64) {
@@ -32,7 +57,23 @@ const validateCode = (value: string, t: ReturnType<typeof useT>) => {
return true;
};
export const ApplyCodeForm = () => {
export const ApplyCodeFormContainer = ({
onSuccess,
}: {
onSuccess?: () => void;
}) => {
const { pubKey } = useVegaWallet();
const isInReferralSet = useIsInReferralSet(pubKey);
// Navigate to the index page when already in the referral set.
if (isInReferralSet) {
return <Navigate to={Routes.REFERRALS} />;
}
return <ApplyCodeForm onSuccess={onSuccess} />;
};
export const ApplyCodeForm = ({ onSuccess }: { onSuccess?: () => void }) => {
const t = useT();
const program = useReferralProgram();
const navigate = useNavigate();
@@ -41,10 +82,15 @@ export const ApplyCodeForm = () => {
);
const [status, setStatus] = useState<
'requested' | 'failed' | 'successful' | null
'requested' | 'no-funds' | 'successful' | null
>(null);
const txHash = useRef<string | null>(null);
const { isReadOnly, pubKey, sendTx } = useVegaWallet();
const { isEligible, requiredFunds } = useFundsAvailable();
const currentRouteId = useGetCurrentRouteId();
const setViews = useSidebar((s) => s.setViews);
const {
register,
handleSubmit,
@@ -55,19 +101,54 @@ export const ApplyCodeForm = () => {
} = useForm();
const [params] = useSearchParams();
const { data: referee } = useReferral({ pubKey, role: 'referee' });
const { data: referrer } = useReferral({ pubKey, role: 'referrer' });
const codeField = watch('code');
const { data: previewData, loading: previewLoading } = useReferral({
code: validateCode(codeField, t) ? codeField : undefined,
});
/**
* Validates if a connected party can apply a code (min funds span protection)
*/
const validateFundsAvailable = useCallback(() => {
if (requiredFunds && !isEligible) {
const err = SPAM_PROTECTION_ERR;
return err;
}
return true;
}, [isEligible, requiredFunds]);
/**
* Validates the set a user tries to apply to.
*/
const validateSet = useCallback(() => {
if (
codeField &&
!previewLoading &&
previewData &&
!previewData.isEligible
) {
return t('The code is no longer valid.');
}
if (codeField && !previewLoading && !previewData) {
return t('The code is invalid');
}
return true;
}, [codeField, previewData, previewLoading, t]);
useEffect(() => {
const code = params.get('code');
if (code) setValue('code', code);
}, [params, setValue]);
useEffect(() => {
const err = validateFundsAvailable();
if (err !== true) {
setStatus('no-funds');
} else {
setStatus(null);
}
}, [isEligible, validateFundsAvailable]);
const onSubmit = ({ code }: FieldValues) => {
if (isReadOnly || !pubKey || !code || code.length === 0) {
return;
@@ -139,21 +220,17 @@ export const ApplyCodeForm = () => {
useEffect(() => {
if (status === 'successful') {
setTimeout(() => {
if (onSuccess) onSuccess();
navigate(Routes.REFERRALS);
}, RELOAD_DELAY);
}
}, [navigate, status]);
// go to main page if the current pubkey is already a referrer or referee
if (referee || referrer) {
return <Navigate to={Routes.REFERRALS} />;
}
}, [navigate, onSuccess, status]);
// show "code applied" message when successfully applied
if (status === 'successful') {
return (
<div className="w-1/2 mx-auto">
<h3 className="mb-5 text-xl text-center uppercase calt flex flex-row gap-2 justify-center items-center">
<div className="mx-auto w-1/2">
<h3 className="calt mb-5 flex flex-row items-center justify-center gap-2 text-center text-xl uppercase">
<span className="text-vega-green-500">
<VegaIcon name={VegaIconNames.TICK} size={20} />
</span>{' '}
@@ -184,6 +261,18 @@ export const ApplyCodeForm = () => {
};
}
if (status === 'no-funds') {
return {
disabled: false,
children: t('Deposit funds'),
type: 'button' as ButtonHTMLAttributes<HTMLButtonElement>['type'],
onClick: ((event) => {
event.preventDefault();
setViews({ type: ViewType.Deposit }, currentRouteId);
}) as MouseEventHandler,
};
}
if (status === 'requested') {
return {
disabled: true,
@@ -205,15 +294,20 @@ export const ApplyCodeForm = () => {
return (
<>
<div className="w-2/3 max-w-md mx-auto bg-vega-clight-800 dark:bg-vega-cdark-800 p-8 rounded-lg">
<h3 className="mb-4 text-2xl text-center calt">
<div
data-testid="referral-apply-code-form"
className="bg-vega-clight-800 dark:bg-vega-cdark-800 mx-auto w-2/3 max-w-md rounded-lg p-8"
>
<h3 className="calt mb-4 text-center text-2xl">
{t('Apply a referral code')}
</h3>
<p className="mb-4 text-center text-base">
{t('Enter a referral code to get trading discounts.')}
{t(
'Apply a referral code to access the discount benefits of the current program.'
)}
</p>
<form
className={classNames('w-full flex flex-col gap-4', {
className={classNames('flex w-full flex-col gap-4', {
'animate-shake': Boolean(errors.code),
})}
onSubmit={handleSubmit(onSubmit)}
@@ -224,31 +318,55 @@ export const ApplyCodeForm = () => {
hasError={Boolean(errors.code)}
{...register('code', {
required: t('You have to provide a code to apply it.'),
validate: (value) => validateCode(value, t),
validate: (value) => {
const codeErr = validateCode(value, t);
if (codeErr !== true) return codeErr;
const fundsErr = validateFundsAvailable();
if (fundsErr !== true) return fundsErr;
return validateSet();
},
})}
placeholder="Enter a code"
className="mb-2 bg-vega-clight-900 dark:bg-vega-cdark-700"
className="bg-vega-clight-900 dark:bg-vega-cdark-700 mb-2"
/>
</label>
<RainbowButton variant="border" {...getButtonProps()} />
</form>
{errors.code && (
<InputError className="break-words overflow-auto">
{errors.code.message?.toString()}
{status === 'no-funds' ? (
<InputError intent="warning" className="overflow-auto break-words">
<span>
<SpamProtectionErr requiredFunds={requiredFunds?.toString()} />
</span>
</InputError>
) : (
errors.code && (
<InputError intent="warning" className="overflow-auto break-words">
{errors.code.message === SPAM_PROTECTION_ERR ? (
<span>
<SpamProtectionErr
requiredFunds={requiredFunds?.toString()}
/>
</span>
) : (
errors.code.message?.toString()
)}
</InputError>
)
)}
</div>
{previewLoading && !previewData ? (
{validateCode(codeField, t) === true && previewLoading && !previewData ? (
<div className="mt-10">
<Loader />
</div>
) : null}
{previewData ? (
{/* TODO: Re-check plural forms once i18n is updated */}
{previewData && previewData.isEligible ? (
<div className="mt-10">
<h2 className="text-2xl mb-5">
<h2 className="mb-5 text-2xl">
{t(
'You are joining the group shown, but will not have access to benefits until you have completed at least %s epochs.',
[nextBenefitTierEpochsValue.toString()]
'youAreJoiningTheGroup',
'You are joining the group shown, but will not have access to benefits until you have completed at least {{count}} epochs.',
{ count: nextBenefitTierEpochsValue }
)}
</h2>
<Statistics data={previewData} program={program} as="referee" />
@@ -6,6 +6,8 @@ export const SKY_BACKGROUND =
'bg-[url(/sky-light.png)] dark:bg-[url(/sky-dark.png)] bg-[40%_0px] bg-[length:1440px] bg-no-repeat bg-local';
// TODO: Update the links to use the correct referral related pages
export const REFERRAL_DOCS_LINK = 'https://docs.vega.xyz/';
export const ABOUT_REFERRAL_DOCS_LINK = 'https://docs.vega.xyz/';
export const REFERRAL_DOCS_LINK =
'https://docs.vega.xyz/mainnet/concepts/trading-on-vega/discounts-rewards#referral-program';
export const ABOUT_REFERRAL_DOCS_LINK =
'https://docs.vega.xyz/mainnet/concepts/trading-on-vega/discounts-rewards#referral-program';
export const DISCLAIMER_REFERRAL_DOCS_LINK = 'https://docs.vega.xyz/';
@@ -19,14 +19,22 @@ import {
import { addDecimalsFormatNumber } from '@vegaprotocol/utils';
import { DApp, TokenStaticLinks, useLinks } from '@vegaprotocol/environment';
import { useStakeAvailable } from './hooks/use-stake-available';
import {
ABOUT_REFERRAL_DOCS_LINK,
DISCLAIMER_REFERRAL_DOCS_LINK,
} from './constants';
import { useReferral } from './hooks/use-referral';
import { ABOUT_REFERRAL_DOCS_LINK } from './constants';
import { useIsInReferralSet, useReferral } from './hooks/use-referral';
import { useT } from '../../lib/use-t';
import { Navigate } from 'react-router-dom';
import { Routes } from '../../lib/links';
import { useReferralProgram } from './hooks/use-referral-program';
export const CreateCodeContainer = () => {
const { pubKey } = useVegaWallet();
const isInReferralSet = useIsInReferralSet(pubKey);
// Navigate to the index page when already in the referral set.
if (isInReferralSet) {
return <Navigate to={Routes.REFERRALS} />;
}
return <CreateCodeForm />;
};
@@ -39,13 +47,16 @@ export const CreateCodeForm = () => {
const { pubKey, isReadOnly } = useVegaWallet();
return (
<div className="w-2/3 max-w-md mx-auto bg-vega-clight-800 dark:bg-vega-cdark-800 p-8 rounded-lg">
<div
data-testid="referral-create-code-form"
className="w-2/3 max-w-md mx-auto bg-vega-clight-800 dark:bg-vega-cdark-800 p-8 rounded-lg"
>
<h3 className="mb-4 text-2xl text-center calt">
{t('Create a referral code')}
</h3>
<p className="mb-4 text-center text-base">
{t(
'Generate a referral code to share with your friends and start earning commission.'
'Generate a referral code to share with your friends and access the commission benefits of the current program.'
)}
</p>
@@ -95,6 +106,8 @@ const CreateCodeDialog = ({
const { stakeAvailable: currentStakeAvailable, requiredStake } =
useStakeAvailable();
const { details: programDetails } = useReferralProgram();
const onSubmit = () => {
if (isReadOnly || !pubKey) {
setErr('Not connected');
@@ -193,12 +206,73 @@ const CreateCodeDialog = ({
);
}
if (!programDetails) {
return (
<div className="flex flex-col gap-4">
{(status === 'idle' || status === 'loading' || status === 'error') && (
<>
{
<p>
{t(
'There is currently no referral program active, are you sure you want to create a code?'
)}
</p>
}
</>
)}
{status === 'success' && code && (
<div className="flex items-center gap-2">
<div className="flex-1 min-w-0 p-2 text-sm rounded bg-vega-clight-700 dark:bg-vega-cdark-700">
<p className="overflow-hidden whitespace-nowrap text-ellipsis">
{code}
</p>
</div>
<CopyWithTooltip text={code}>
<TradingButton
className="text-sm no-underline"
icon={<VegaIcon name={VegaIconNames.COPY} />}
>
<span>{t('Copy')}</span>
</TradingButton>
</CopyWithTooltip>
</div>
)}
<TradingButton
fill={true}
intent={Intent.Primary}
onClick={() => onSubmit()}
{...getButtonProps()}
>
{t('Yes')}
</TradingButton>
{status === 'idle' && (
<TradingButton
fill={true}
intent={Intent.Primary}
onClick={() => {
refetch();
setDialogOpen(false);
}}
>
{t('No')}
</TradingButton>
)}
{err && <InputError>{err}</InputError>}
<div className="flex justify-center pt-5 mt-2 text-sm border-t gap-4 text-default border-default">
<ExternalLink href={ABOUT_REFERRAL_DOCS_LINK}>
{t('About the referral program')}
</ExternalLink>
</div>
</div>
);
}
return (
<div className="flex flex-col gap-4">
{(status === 'idle' || status === 'loading' || status === 'error') && (
<p>
{t(
'Generate a referral code to share with your friends and start earning commission.'
'Generate a referral code to share with your friends and access the commission benefits of the current program.'
)}
</p>
)}
@@ -229,9 +303,6 @@ const CreateCodeDialog = ({
<ExternalLink href={ABOUT_REFERRAL_DOCS_LINK}>
{t('About the referral program')}
</ExternalLink>
<ExternalLink href={DISCLAIMER_REFERRAL_DOCS_LINK}>
{t('Disclaimer')}
</ExternalLink>
</div>
</div>
);
@@ -53,7 +53,7 @@ export const NotFound = () => {
const navigate = useNavigate();
return (
<div className="pt-32">
<LayoutWithSky className="pt-32">
<div
aria-hidden
className="absolute top-64 right-[220px] md:right-[340px] max-sm:hidden"
@@ -75,6 +75,6 @@ export const NotFound = () => {
{t('Go back and try again')}
</RainbowButton>
</p>
</div>
</LayoutWithSky>
);
};
@@ -0,0 +1,20 @@
query FundsAvailable($partyId: ID!) {
party(id: $partyId) {
accountsConnection {
edges {
node {
balance
asset {
decimals
symbol
id
}
}
}
}
}
networkParameter(key: "spam.protection.applyReferral.min.funds") {
key
value
}
}
@@ -11,6 +11,7 @@ query ReferralSetStats($code: ID!, $epoch: Int) {
rewardsMultiplier
rewardsFactorMultiplier
referrerTakerVolume
wasEligible
}
}
}

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