Compare commits

...
Author SHA1 Message Date
Matthew Russell 8990c1185c chore: add applyTransaction 2023-05-26 13:07:57 -07:00
Matthew Russell 375f9b07ea chore: simple order list example 2023-05-26 12:15:16 -07:00
m.ray 0f2b86770a fix(trading): show full node URI in trading footer (#3978) 2023-05-26 16:38:02 +00:00
m.ray cd6d350047 chore(trading): update vega.trading to console.vega.xyz (#3969) 2023-05-26 12:08:26 +01:00
Matthew Russell c5a06c57ac chore(trading,governance,explorer): update issue templates to include check in specs repo (#3964) 2023-05-25 22:37:40 -07:00
Matthew Russell 5bb31fa38d chore(trading,governance,explorer): bump to v0.20.14-core-0.71.4 2023-05-25 16:31:35 -07:00
m.ray fd8f38803c chore(trading): bump version v0.20.13-core-0.71.4 (#3968) 2023-05-25 22:01:43 +00:00
Edd a22f8f0ef1 fix(explorer,trading): remove vega.trading links (#3962) 2023-05-25 21:27:51 +00:00
Matthew Russell c2d5c71f35 chore(trading): remove stray log (#3967) 2023-05-25 11:32:31 -07:00
Joe Tsang f88c35648e chore(governance): reward tests passing (#3963) 2023-05-25 18:08:06 +00:00
Mikołaj Młodzikowski 348c61530e feat(ci): implement active checks for previews readiness (#3949) 2023-05-25 14:36:38 +02:00
Bartłomiej Głownia f364dabe2f feat(trading): show positions for all connected keys (#3858) 2023-05-25 11:59:08 +01:00
m.rayandasiaznik 1ae1fdff5e fix(trading): price and volume in the last 24h can be incorrectly reported (#3870)
Co-authored-by: asiaznik <artur@vegaprotocol.io>
2023-05-25 10:39:07 +00:00
m.ray 3ed615c36e fix(oracles): word breakdown oracles (#3946) 2023-05-25 10:07:17 +00:00
Bartłomiej Głownia 6b50816234 fix(orders): re-render last column of orders table on data change (#3927) 2023-05-25 10:37:53 +01:00
Sam Keen f5e67a0c2d fix(governance): rewards decimals set per asset (#3941) 2023-05-25 10:37:40 +01:00
Bartłomiej Głownia e4d4646878 fix(trading): skip market data query if market not present (#3928) 2023-05-25 10:37:33 +01:00
Art bbd1536c18 chore(trading): upgrade banner semver comparison (#3919) 2023-05-25 10:37:13 +01:00
Mikołaj Młodzikowski f8444c0c88 feat(ci): deploy from main branch as well 2023-05-25 10:20:58 +02:00
Mikołaj Młodzikowski b1adf776c0 feat(ci): update rollback flow 2023-05-24 16:05:15 +02:00
Mikołaj Młodzikowski ad898ab0e8 feat(ci): update rollback flow 2023-05-24 15:58:53 +02:00
Maciek 5b0fffc5cb chore(market-depth): replace bid and ask columns with a single volume column when i… (#3930) 2023-05-24 15:55:21 +02:00
Mikołaj Młodzikowski a9dbc0f6eb feat(ci): update rollback flow 2023-05-24 15:53:07 +02:00
Mikołaj Młodzikowski a71786817f fix(ci): pr creation adjust 2023-05-24 13:47:36 +02:00
Vega CI Botandvega-ci-bot 9ee0cb7b65 Automated hash update from refs/tags/v0.20.12-core-0.71.4-4 (#3933)
Co-authored-by: vega-ci-bot <vega-ci-bot@vega.xyz>
2023-05-24 13:46:01 +02:00
Mikołaj Młodzikowski 8c4aa41240 fix(ci): pr creation adjust 2023-05-24 13:37:04 +02:00
Vega CI Botandvega-ci-bot 91ac977704 Automated hash update from refs/tags/v0.20.12-core-0.71.4-3 (#3932)
Co-authored-by: vega-ci-bot <vega-ci-bot@vega.xyz>
2023-05-24 13:34:10 +02:00
Mikołaj Młodzikowski 1190033776 fix(ci): pr creation adjust 2023-05-24 13:24:28 +02:00
Mikołaj Młodzikowski b99e2235b4 fix(ci): pr creation adjust 2023-05-24 13:09:29 +02:00
Mikołaj Młodzikowski 002bc4f188 feat: regenerate hashes on interstitial page (#3926) 2023-05-24 12:36:11 +02:00
Joe Tsang 0c2af8c75f chore(governance): update nightly tests (#3913) 2023-05-24 09:23:31 +01:00
daro-maj a305c5b8a9 test(trading): capsule tests - fix for wallet connection (#3885) 2023-05-23 20:02:29 +02:00
Matthew Russell 4c3ceb94f3 chore: bump app version 2023-05-23 09:58:39 -07:00
Art 2215864d0c fix(withdraws): min withdraw amount (#3916) 2023-05-23 09:52:48 -07:00
Art 589785ce09 fix(withdraws): link to the withdrawal limits (#3914) 2023-05-23 16:39:29 +00:00
Maciek babd99088d fix(trading): make grids more responsive - suppress sizeColumnsToFit on small screens (#3906) 2023-05-23 16:08:55 +00:00
Bartłomiej Głownia d155909fe4 fix(deal-ticket): clear price error on type change (#3908) 2023-05-23 08:53:40 -07:00
Sam Keen 1d3298cdc9 fix(governance): add enactment date to proposal sorting (#3875) 2023-05-23 16:53:01 +01:00
m.ray c2baaa2dd9 fix(trading): console.fairground.wtf links update back (#3907) 2023-05-23 15:49:14 +00:00
Art e2c4c4a841 fix(deposits): validation with exempt (#3909) 2023-05-23 08:44:06 -07:00
Art 7a89dc984d feat(deposits): mark remaining deposit allowance as exempt (#3902) 2023-05-23 15:35:02 +01:00
Mikołaj Młodzikowski 19580fb9ce fix(ci): work around syntax issue on updating release notes 2023-05-23 13:43:32 +02:00
m.rayandEdd 0721099d7d chore(trading): console fairground links (#3896)
Co-authored-by: Edd <edd@vega.xyz>
2023-05-23 11:06:57 +00:00
m.rayandEdd 9ee2810c51 chore(trading): adding mainnet links (#3879)
Co-authored-by: Edd <edd@vega.xyz>
2023-05-23 10:29:09 +00:00
Edd 9f8f99b50b fix(explorer): wrap routes in suspense (#3894) 2023-05-23 11:15:13 +01:00
m.ray 3208f762fd fix(trading): hide download or export all when no entries (#3892) 2023-05-23 09:26:47 +00:00
Mikołaj Młodzikowski 7a06c946d1 feat(ci): support fairground / mainnet variables for s3 and do not deploy trading to s3 bucket on main branch 2023-05-23 11:20:54 +02:00
Maciek 70e172045d fix(trading): improve data count on propsed markets (#3890) 2023-05-23 08:53:15 +00:00
Mikołaj Młodzikowski 06c540666e feat(ci): setup correct domain for fairground deployments 2023-05-23 10:30:35 +02:00
Matthew Russell f81503b8f7 chore: bump app version for trading 2023-05-22 22:20:30 -07:00
Matthew Russell 1699e33cbd fix(trading): redirect to markets all page if no market found (#3888) 2023-05-22 22:02:55 -07:00
Matthew Russell 5280b79927 feat(trading): market selection changes (#3863) 2023-05-22 21:33:16 -07:00
Matthew Russell 34526d527e fix(trading): icon color when navbar is dark (#3887) 2023-05-22 19:07:18 -07:00
Matthew Russell 7b9aae700d chore(trading): dont show upgrade banner unless APP_VERSION is set (#3886) 2023-05-22 14:10:06 -07:00
Matthew Russell e9cb2ad217 chore(ui-toolkit): adjust nav styles and banner (#3865) 2023-05-22 13:20:17 -07:00
Edd 6dd4536fdd fix(explorer): change how internal filters are detected (#3852) 2023-05-22 21:08:57 +01:00
Bartłomiej Głownia 522eb521b9 feat(trading): show positions for all markets (#3868) 2023-05-22 12:55:15 -07:00
Edd 3aa0e974c2 feat(explorer): footer updates (#3884) 2023-05-22 12:54:37 -07:00
Art a0eb381ab1 feat(trading): upgrade banner (#3883) 2023-05-22 12:53:40 -07:00
Matthew Russell a51ab4d98d chore(governance): remove process tranches script (#3864) 2023-05-22 12:52:13 -07:00
Bartłomiej Głownia 67643afd74 fix(trading): positions and markets settlement asset column filter and sort (#3873) 2023-05-22 12:34:05 -07:00
dexturr 145539e0c0 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-05-22 18:07:30 +00:00
161 changed files with 3710 additions and 154280 deletions
+2 -3
View File
@@ -14,8 +14,7 @@ What we need to achieve and who for
## Tasks
- [ ] What do we need to do first
- [ ] and then what?
- [ ] Etc.
- [ ]
- [ ]
## Additional details / background info
+7 -4
View File
@@ -22,11 +22,14 @@ So that
## Tasks
- [ ] Explore and sketch
- [ ] UX (if needed)
- [ ] Design (if needed)
- [ ] Team and stakeholder review
- [ ] Visual Design
- [ ] Team review
- [ ] Etc.
- [ ] Specs reviewed and created or adjusted
- [ ] Implementation
- [ ] Testing (unit and/or e2e)
- [ ] Code review
- [ ] QA review
## Sketch
@@ -48,7 +48,8 @@ jobs:
token: ${{ secrets.GITHUB_TOKEN }}
id: ${{ github.event.release.id }}
body: |
---
___
# Deployments
* https://explorer.vega.xyz
@@ -60,7 +61,7 @@ jobs:
CIDv0: ${{ env.IPFS_V0 }}
CIDv1: ${{ env.IPFS_V1 }}
You can always access the latest IPFS release by visiting [vega.trading](https://vega.trading).
You can always access the latest IPFS release by visiting [console.vega.xyz](https://console.vega.xyz).
You can also access Trading directly from an IPFS gateway.
BEWARE: The Trading interface uses [localStorage](https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage) to remember your settings, such as which tokens you have imported. You should always use an IPFS gateway that enforces [origin separation](https://ipfs.github.io/public-gateway-checker/).
+26 -7
View File
@@ -5,6 +5,7 @@ on:
branches:
- release/*
- develop
- main
tags:
- v*
pull_request:
@@ -170,6 +171,7 @@ jobs:
- publish-dist
- lint-test-build
if: ${{ github.event_name == 'pull_request' }}
timeout-minutes: 60
name: '(CD) comment preview links'
steps:
- name: Find Comment
@@ -179,6 +181,29 @@ jobs:
issue-number: ${{ github.event.pull_request.number }}
body-includes: Previews
- name: Wait for deployments
run: |
# https://stackoverflow.com/questions/3183444/check-for-valid-link-url
regex='(https?|ftp|file)://[-[:alnum:]\+&@#/%?=~_|!:,.;]*[-[:alnum:]\+&@#/%=~_|]'
if [[ "${{ needs.lint-test-build.outputs.preview_governance }}" =~ $regex ]]; then
until curl -L --fail "${{ needs.lint-test-build.outputs.preview_governance }}"; do
echo "waiting for governance preview"
sleep 5
done
fi
if [[ "${{ needs.lint-test-build.outputs.preview_explorer }}" =~ $regex ]]; then
until curl -L --fail "${{ needs.lint-test-build.outputs.preview_explorer }}"; do
echo "waiting for explorer preview"
sleep 5
done
fi
if [[ "${{ needs.lint-test-build.outputs.preview_trading }}" =~ $regex ]]; then
until curl -L --fail "${{ needs.lint-test-build.outputs.preview_trading }}"; do
echo "waiting for trading preview"
sleep 5
done
fi
- name: Create comment
uses: peter-evans/create-or-update-comment@v3
if: ${{ steps.fc.outputs.comment-id == 0 }}
@@ -190,15 +215,9 @@ jobs:
* explorer: ${{ needs.lint-test-build.outputs.preview_explorer }}
* trading: ${{ needs.lint-test-build.outputs.preview_trading }}
# Report single result at the end, to avoid mess with required checks in PR
cypress-check:
name: '(CI) cypress - check'
runs-on: ubuntu-latest
needs: cypress
steps:
- run: echo Done!
# Report single result at the end, to avoid mess with required checks in PR
cypress-result:
if: ${{ always() }}
needs: cypress
runs-on: ubuntu-22.04
-32
View File
@@ -1,32 +0,0 @@
name: Generate tranches
on:
schedule:
- cron: '0 */6 * * *'
jobs:
master:
name: Generate Queries
runs-on: ubuntu-22.04
steps:
- name: Checkout
uses: actions/checkout@v2
with:
token: ${{ secrets.VEGA_CI_BOT_GITHUB_TOKEN }}
fetch-depth: 0
- name: Use Node.js 16
id: Node
uses: actions/setup-node@v2
with:
node-version: 16.15.1
- name: Install root dependencies
run: yarn install
- name: Generate queries
run: node ./scripts/get-tranches.js
- uses: stefanzweifel/git-auto-commit-action@v4
with:
commit_message: 'chore: update tranches'
commit_options: '--no-verify --signoff'
skip_fetch: true
skip_checkout: true
+89 -39
View File
@@ -59,26 +59,39 @@ jobs:
key: ${{ runner.os }}-cache-node-modules-${{ hashFiles('yarn.lock') }}
# https://docs.github.com/en/actions/learn-github-actions/contexts
- name: Define variables
- name: Define dist variables
if: ${{ github.event_name == 'push' }}
run: |
envName=''
domain="vega.rocks"
if [[ "${{ github.event_name }}" = "push" ]]; then
if [[ "${{ github.ref }}" =~ .*release/.* ]]; then
envName="$(echo ${{ github.ref }} | rev | cut -d '/' -f 1 | rev)"
elif [[ "${{ github.ref }}" =~ .*develop$ ]]; then
envName="stagnet1"
elif [[ "${{ matrix.app}}" = "trading" ]] && [[ ${{ startsWith(github.ref, 'refs/tags/v') && 'true' || 'false' }} = "true" ]]; then
envName="mainnet"
elif [[ "${{ github.ref }}" =~ .*main$ ]]; then
envName="mainnet"
fi
if [[ "${envName}" = "mainnet" ]]; then
domain="vega.xyz"
fi
bucketName="${{ matrix.app }}.${envName}.${domain}"
echo BUCKET_NAME=${bucketName} >> $GITHUB_ENV
bucketName=''
if [[ "${{ github.ref }}" =~ .*release/.* ]]; then
envName="$(echo ${{ github.ref }} | rev | cut -d '/' -f 1 | rev)"
elif [[ "${{ github.ref }}" =~ .*develop$ ]]; then
envName="stagnet1"
elif [[ "${{ github.ref }}" =~ .*main$ ]]; then
envName="mainnet"
elif [[ "${{ matrix.app}}" = "trading" ]] && [[ "${{ startsWith(github.ref, 'refs/tags/v') && 'true' || 'false' }}" = "true" ]]; then
envName="mainnet"
fi
if [[ "${envName}" = "mainnet" ]]; then
domain="vega.xyz"
bucketName="${{ matrix.app }}.${domain}"
elif [[ "${envName}" = "testnet" ]]; then
domain="fairground.wtf"
bucketName="${{ matrix.app }}.${domain}"
fi
if [[ -z "${bucketName}" ]]; then
bucketName="${{ matrix.app }}.${envName}.${domain}"
fi
echo "bucket name: ${bucketName}"
echo "env name: ${envName}"
echo BUCKET_NAME=${bucketName} >> $GITHUB_ENV
echo ENV_NAME=${envName} >> $GITHUB_ENV
- name: Build local dist
@@ -155,7 +168,7 @@ jobs:
# bucket creation in github.com/vegaprotocol/terraform//frontend
- name: Publish dist to s3
uses: jakejarvis/s3-sync-action@master
if: ${{ github.event_name == 'push' && !startsWith(github.ref, 'refs/tags/v') }}
if: ${{ github.event_name == 'push' && !startsWith(github.ref, 'refs/tags/v') && ( matrix.app != 'trading' || (matrix.app == 'trading' && !endsWith(github.ref, 'main') ) ) }}
with:
args: --acl private --follow-symlinks --delete
env:
@@ -187,8 +200,19 @@ jobs:
-d '{"query": "mutation{triggerDeploy(siteId:\"f8f2e051-f18e-49e6-b876-0a39369dc0d8\"){id status}}"}' \
https://api.fleek.co/graphql
- name: Update vega.trading DNS to redirect to the new console
- name: Check out ipfs-redirect
if: ${{ matrix.app == 'trading' && github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v') }}
uses: actions/checkout@v3
with:
repository: 'vegaprotocol/ipfs-redirect'
path: 'ipfs-redirect'
fetch-depth: '0'
token: ${{ secrets.VEGA_CI_BOT_GITHUB_TOKEN }}
- name: Update console.vega.xyz DNS to redirect to the new console
if: ${{ matrix.app == 'trading' && github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v') }}
env:
GH_TOKEN: ${{ secrets.VEGA_CI_BOT_GITHUB_TOKEN }}
run: |
curl -L https://dist.ipfs.tech/kubo/v0.20.0/kubo_v0.20.0_linux-amd64.tar.gz -o kubo.tgz
tar -xzf kubo.tgz
@@ -197,26 +221,52 @@ jobs:
new_hash=$(cat ${{ matrix.app }}-ipfs-hash)
new_cid=$(ipfs cid format -v 1 -b base32 $new_hash)
# Generate console URL
new_console_url_type=ipfs
# new_console_url_type=ipns
new_console_url_domain=cf-ipfs.com
# new_console_url_domain=dweb.link
new_console_url="https://${new_cid}.${new_console_url_type}.${new_console_url_domain}/"
echo "new_console_url=${new_console_url}"
ls -al ipfs-redirect
# Update record in DNSimple
# docs: https://developer.dnsimple.com/v2/zones/records/#updateZoneRecord
dnsimple_account_id=84895
dnsimple_zone_name=vega.trading
dnsimple_record_id=44409591
# see: https://dnsimple.com/a/84895/domains/vega.trading/records/44409591/edit
echo $new_hash > ipfs-redirect/cidv0.txt
echo $new_cid > ipfs-redirect/cidv1.txt
curl -H 'Authorization: Bearer ${{ secrets.DNSIMPLE_API_TOKEN }}' \
-H 'Accept: application/json' \
-H 'Content-Type: application/json' \
-X PATCH \
-d "{
\"content\": \"${new_console_url}\"
}" \
https://api.dnsimple.com/v2/${dnsimple_account_id}/zones/${dnsimple_zone_name}/records/${dnsimple_record_id}
(
cd ipfs-redirect
git status
cat .git/config
git config --global user.email "vega-ci-bot@vega.xyz"
git config --global user.name "vega-ci-bot"
branch_name="update-hash-${{ github.ref }}"
git checkout -b "$branch_name"
commit_msg="Automated hash update from ${{ github.ref }}"
git add cidv0.txt cidv1.txt
git commit -m "$commit_msg"
git push -u origin "$branch_name"
pr_url="$(gh pr create --title "${commit_msg}" --body 'automated pull request to update CIDs')"
echo $pr_url
# once auto merge get's enabled on documentation repo let's do follow up
sleep 5
gh pr merge "${pr_url}" --delete-branch --squash --admin
)
# # Generate console URL
# new_console_url_type=ipfs
# # new_console_url_type=ipns
# new_console_url_domain=cf-ipfs.com
# # new_console_url_domain=dweb.link
# new_console_url="https://${new_cid}.${new_console_url_type}.${new_console_url_domain}/"
# echo "new_console_url=${new_console_url}"
# # Update record in DNSimple
# # docs: https://developer.dnsimple.com/v2/zones/records/#updateZoneRecord
# dnsimple_account_id=84895
# dnsimple_zone_name=console.vega.xyz
# dnsimple_record_id=44409591
# # see: https://dnsimple.com/a/84895/domains/console.vega.xyz/records/44409591/edit
# curl -H 'Authorization: Bearer ${{ secrets.DNSIMPLE_API_TOKEN }}' \
# -H 'Accept: application/json' \
# -H 'Content-Type: application/json' \
# -X PATCH \
# -d "{
# \"content\": \"${new_console_url}\"
# }" \
# https://api.dnsimple.com/v2/${dnsimple_account_id}/zones/${dnsimple_zone_name}/records/${dnsimple_record_id}
+30 -23
View File
@@ -43,7 +43,17 @@ jobs:
-d '{"query": "mutation{triggerDeploy(siteId:\"f8f2e051-f18e-49e6-b876-0a39369dc0d8\"){id status}}"}' \
https://api.fleek.co/graphql
- name: Update vega.trading DNS to redirect to the new console
- name: Check out ipfs-redirect
uses: actions/checkout@v3
with:
repository: 'vegaprotocol/ipfs-redirect'
path: 'ipfs-redirect'
fetch-depth: '0'
token: ${{ secrets.VEGA_CI_BOT_GITHUB_TOKEN }}
- name: Update console.vega.xyz DNS to redirect to the new console
env:
GH_TOKEN: ${{ secrets.VEGA_CI_BOT_GITHUB_TOKEN }}
run: |
curl -L https://dist.ipfs.tech/kubo/v0.20.0/kubo_v0.20.0_linux-amd64.tar.gz -o kubo.tgz
tar -xzf kubo.tgz
@@ -51,27 +61,24 @@ jobs:
which ipfs
new_hash=$(cat ipfs-hash)
new_cid=$(ipfs cid format -v 1 -b base32 $new_hash)
git config --global user.email "vega-ci-bot@vega.xyz"
git config --global user.name "vega-ci-bot"
echo $new_hash > ipfs-redirect/cidv0.txt
echo $new_cid > ipfs-redirect/cidv1.txt
# Generate console URL
new_console_url_type=ipfs
# new_console_url_type=ipns
new_console_url_domain=cf-ipfs.com
# new_console_url_domain=dweb.link
new_console_url="https://${new_cid}.${new_console_url_type}.${new_console_url_domain}/"
echo "new_console_url=${new_console_url}"
(
cd ipfs-redirect
# Update record in DNSimple
# docs: https://developer.dnsimple.com/v2/zones/records/#updateZoneRecord
dnsimple_account_id=84895
dnsimple_zone_name=vega.trading
dnsimple_record_id=44409591
# see: https://dnsimple.com/a/84895/domains/vega.trading/records/44409591/edit
curl -H 'Authorization: Bearer ${{ secrets.DNSIMPLE_API_TOKEN }}' \
-H 'Accept: application/json' \
-H 'Content-Type: application/json' \
-X PATCH \
-d "{
\"content\": \"${new_console_url}\"
}" \
https://api.dnsimple.com/v2/${dnsimple_account_id}/zones/${dnsimple_zone_name}/records/${dnsimple_record_id}
git status
branch_name="rollback-to-$new_hash"
git checkout -b "$branch_name"
commit_msg="hash rollback to $new_hash"
git add cidv0.txt cidv1.txt
git commit -m "$commit_msg"
git push -u origin "$branch_name" --force-with-lease
pr_url="$(gh pr create --title "${commit_msg}" --body 'automated pull request to update CIDs')"
echo $pr_url
# once auto merge get's enabled on documentation repo let's do follow up
sleep 5
gh pr merge "${pr_url}" --delete-branch --squash --admin
)
+1 -1
View File
@@ -10,4 +10,4 @@ NX_ETHERSCAN_URL=https://etherscan.io
NX_VEGA_GOVERNANCE_URL=https://governance.vega.xyz
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/mainnet/announcements.json
NX_VEGA_EXPLORER_URL=https://explorer.vega.xyz/
NX_VEGA_CONSOLE_URL=https://vega.trading
NX_VEGA_CONSOLE_URL=https://console.vega.xyz
+4 -1
View File
@@ -13,6 +13,7 @@ import { DEFAULT_CACHE_CONFIG } from '@vegaprotocol/apollo-client';
import { RouterProvider } from 'react-router-dom';
import { router } from './routes/router-config';
import { t } from '@vegaprotocol/i18n';
import { Suspense } from 'react';
const splashLoading = (
<Splash>
@@ -32,7 +33,9 @@ function App() {
skeleton={<div>{t('Loading')}</div>}
failure={<AppFailure title={t(`Node: ${VEGA_URL} is unsuitable`)} />}
>
<RouterProvider router={router} fallbackElement={splashLoading} />
<Suspense fallback={splashLoading}>
<RouterProvider router={router} fallbackElement={splashLoading} />
</Suspense>
</NodeGuard>
<NodeSwitcherDialog
open={nodeSwitcherOpen}
@@ -4,9 +4,11 @@ import {
} from '@vegaprotocol/environment';
import { t } from '@vegaprotocol/i18n';
import { useScreenDimensions } from '@vegaprotocol/react-helpers';
import { ExternalLink, Link } from '@vegaprotocol/ui-toolkit';
import { Link, ExternalLink } from '@vegaprotocol/ui-toolkit';
import { useMemo } from 'react';
import { ENV } from '../../config/env';
import { Routes } from '../../routes/route-names';
import { Link as RouteLink } from 'react-router-dom';
export const Footer = () => {
const { VEGA_URL, GIT_COMMIT_HASH, GIT_ORIGIN_URL } = useEnvironment();
@@ -21,7 +23,7 @@ export const Footer = () => {
);
return (
<footer className="grid grid-rows-2 grid-cols-[1fr_auto] text-xs md:text-md md:flex md:col-span-2 px-4 py-2 gap-4 border-t border-vega-light-200 dark:border-vega-dark-200">
<footer className="grid grid-rows-2 lg:grid-cols-[1fr_auto] text-xs md:text-md lg:flex md:col-span-2 px-4 py-2 gap-4 border-t border-vega-light-200 dark:border-vega-dark-200">
<div className="flex justify-between gap-2 align-middle">
{GIT_COMMIT_HASH && (
<div className="content-center flex border-r border-neutral-700 dark:border-neutral-300 pr-4">
@@ -56,11 +58,16 @@ export const Footer = () => {
</div>
) : null}
</div>
<div className="pl-2 align-center lg:align-right lg:flex lg:justify-end gap-2 align-middle lg:max-w-xs lg:ml-auto">
<RouteLink to={`/${Routes.DISCLAIMER}`} className="underline">
Disclaimer
</RouteLink>
</div>
</footer>
);
};
const NodeUrl = ({ url }: { url: string }) => {
export const NodeUrl = ({ url }: { url: string }) => {
// get base url from api url, api sub domain
const urlObj = new URL(url);
const nodeUrl = urlObj.origin.replace(/^[^.]+\./g, '');
@@ -84,15 +84,17 @@ export const MarketsTable = ({ data }: MarketsTableProps) => {
<AgGridColumn
colId="asset"
headerName={t('Settlement asset')}
field="tradableInstrument.instrument.product.settlementAsset"
field="tradableInstrument.instrument.product.settlementAsset.symbol"
hide={window.innerWidth <= BREAKPOINT_MD}
cellRenderer={({
value,
data,
}: VegaICellRendererParams<
MarketFieldsFragment,
'tradableInstrument.instrument.product.settlementAsset'
>) =>
value ? (
'tradableInstrument.instrument.product.settlementAsset.symbol'
>) => {
const value =
data?.tradableInstrument.instrument.product.settlementAsset;
return value ? (
<ButtonLink
onClick={(e) => {
openAssetDetailsDialog(value.id, e.target as HTMLElement);
@@ -102,8 +104,8 @@ export const MarketsTable = ({ data }: MarketsTableProps) => {
</ButtonLink>
) : (
''
)
}
);
}}
/>
<AgGridColumn
flex={2}
@@ -1,12 +1,13 @@
import { render } from '@testing-library/react';
import { OracleDetailsType } from './oracle-details-type';
import type { SourceTypeName } from './oracle-details-type';
import { OracleDetailsType, isInternalSourceType } from './oracle-details-type';
import type { SourceType } from './oracle';
import { PropertyKeyType } from '@vegaprotocol/types';
function renderComponent(type: SourceTypeName) {
return <OracleDetailsType type={type} />;
function renderComponent(type: SourceType) {
return <OracleDetailsType sourceType={type} />;
}
function renderWrappedComponent(type: SourceTypeName) {
function renderWrappedComponent(type: SourceType) {
return (
<table>
<tbody>{renderComponent(type)}</tbody>
@@ -14,19 +15,53 @@ function renderWrappedComponent(type: SourceTypeName) {
);
}
function mock(name: string): SourceType {
return {
sourceType: {
filters: [
{
__typename: 'Filter',
key: {
name,
type: PropertyKeyType.TYPE_STRING,
},
},
],
},
};
}
describe('Oracle type view', () => {
it('Renders nothing when type is null', () => {
const res = render(renderComponent(null as unknown as SourceTypeName));
const res = render(renderComponent(null as unknown as SourceType));
expect(res.container).toBeEmptyDOMElement();
});
it('Renders Internal time for internal sources', () => {
const res = render(renderWrappedComponent('DataSourceDefinitionInternal'));
expect(res.getByText('Internal time')).toBeInTheDocument();
it('Renders Internal time for internal sources - timestamp', () => {
const s = mock('vegaprotocol.builtin.timestamp');
expect(isInternalSourceType(s)).toEqual(true);
const res = render(renderWrappedComponent(s));
expect(res.getByText('Internal data')).toBeInTheDocument();
});
it('Renders Internal time for internal sources - potential future types', () => {
const s = mock('vegaprotocol.builtin.boolean');
expect(isInternalSourceType(s)).toEqual(true);
const res = render(renderWrappedComponent(s));
expect(res.getByText('Internal data')).toBeInTheDocument();
});
it('Renders External data otherwise - prices.external.whatever', () => {
const s = mock('prices.external.whatever');
expect(isInternalSourceType(s)).toEqual(false);
const res = render(renderWrappedComponent(s));
expect(res.getByText('External data')).toBeInTheDocument();
});
it('Renders External data otherwise', () => {
const res = render(renderWrappedComponent('DataSourceDefinitionExternal'));
const s = mock('prices.external.vegaprotocol.builtin.');
expect(isInternalSourceType(s)).toEqual(false);
const res = render(renderWrappedComponent(s));
expect(res.getByText('External data')).toBeInTheDocument();
});
});
@@ -1,27 +1,51 @@
import { TableRow, TableCell, TableHeader } from '../../../components/table';
import type { SourceType } from './oracle';
export type SourceTypeName = SourceType['__typename'] | undefined;
/**
* Basic function to determine if a source is internal or external.
*
* This should be distinguishable using __typename, but the type is incorrectly
* reported at the moment, so instead we check the filters.
*
* @param s SourceType
* @returns boolean True if the source is internal
*/
export function isInternalSourceType(s: SourceType) {
if ('filters' in s.sourceType) {
const filters = s.sourceType.filters;
if (filters) {
return (
filters?.filter((f) => {
return f.key.name?.indexOf('vegaprotocol.builtin.') === 0;
}).length > 0
);
}
}
return false;
}
interface OracleDetailsTypeProps {
type: SourceTypeName;
sourceType: SourceType;
}
/**
* Renders a a single table row for the Oracle Details view that shows
* if the oracle is using the internal time oracle or external data
*/
export function OracleDetailsType({ type }: OracleDetailsTypeProps) {
if (!type) {
export function OracleDetailsType({ sourceType }: OracleDetailsTypeProps) {
if (!sourceType) {
return null;
}
const isInternal = isInternalSourceType(sourceType);
return (
<TableRow modifier="bordered">
<TableHeader scope="row">Type</TableHeader>
<TableCell modifier="bordered">
{type === 'DataSourceDefinitionInternal'
? 'Internal time'
: 'External data'}
{isInternal ? 'Internal data' : 'External data'}
</TableCell>
</TableRow>
);
@@ -53,7 +53,7 @@ export const OracleDetails = ({
<OracleLink id={id} />
</TableCell>
</TableRow>
<OracleDetailsType type={sourceType.__typename} />
<OracleDetailsType sourceType={sourceType} />
<OracleSigners sourceType={sourceType} />
<OracleMarkets id={id} />
<TableRow modifier="bordered">
@@ -0,0 +1,40 @@
import { t } from '@vegaprotocol/i18n';
import { RouteTitle } from '../../components/route-title';
export const Disclaimer = () => {
return (
<section>
<div className="px-40 max-sm:px-0 max-md:px-10 mb-4 max-w-5xl">
<RouteTitle data-testid="disclaimer-header">
{t('Disclaimer')}
</RouteTitle>
<p className="mt-3">
The Vega Block Explorer is an application that allows users to, among
other things, browse through blocks, view wallet addresses, network
hashrate, transaction data and other key information on the Vega
blockchain. It is free, public and open source software. Software
upgrades may contain bugs or security vulnerabilities that might
result in loss of functionality.
</p>
<p className="mt-3">
The Vega Block Explorer uses data from nodes on the Vega Blockchain.
The developers of the Vega Block Explorer do not operate or run the
Vega Blockchain or any other blockchain.
</p>
<p className="mt-3 font-semibold">
The Vega Block Explorer is provided as is. The developers of the
Vega Block Explorer make no representations or warranties of any kind,
whether express or implied, statutory or otherwise regarding the Vega
Block Explorer. They disclaim all warranties of merchantability,
quality, fitness for purpose. They disclaim all warranties that the
Vega Block Explorer is free of harmful components or errors.
</p>
<p className="mt-3 font-bold">
No developer of the Vega Block Explorer accepts any responsibility
for, or liability to users in connection with their use of the Vega
Block Explorer.
</p>
</div>
</section>
);
};
@@ -10,4 +10,5 @@ export const Routes = {
MARKETS: 'markets',
ORACLES: 'oracles',
NETWORK_PARAMETERS: 'network-parameters',
DISCLAIMER: 'disclaimer',
};
@@ -29,6 +29,7 @@ import { AssetLink, MarketLink } from '../components/links';
import { truncateMiddle } from '@vegaprotocol/ui-toolkit';
import { remove0x } from '@vegaprotocol/utils';
import { PartyAccountsByAsset } from './parties/id/accounts';
import { Disclaimer } from './pages/disclaimer';
export type Navigable = {
path: string;
@@ -335,6 +336,17 @@ export const routerConfig: Route[] = [
},
],
},
{
path: Routes.DISCLAIMER,
element: <Disclaimer />,
handle: {
name: t('Disclaimer'),
text: t('Disclaimer'),
breadcrumb: () => (
<Link to={Routes.DISCLAIMER}>{t('Disclaimer')}</Link>
),
},
},
...partiesRoutes,
...assetsRoutes,
...genesisRoutes,
@@ -33,8 +33,9 @@ const proposalDetailsTitle = '[data-testid="proposal-title"]';
const proposalDetailsDescription = '[data-testid="proposal-description"]';
const openProposals = '[data-testid="open-proposals"]';
const viewProposalButton = '[data-testid="view-proposal-btn"]';
const proposalDescriptionToggle = 'proposal-description-toggle';
const voteBreakdownToggle = 'vote-breakdown-toggle';
const proposalTermsToggle = 'proposal-terms-toggle';
const proposalTermsToggle = 'proposal-json-toggle';
describe(
'Governance flow for proposal details',
@@ -73,6 +74,8 @@ describe(
'contain.text',
rawProposal.rationale.title
);
cy.getByTestId(proposalDescriptionToggle).click();
cy.getByTestId('proposal-description-toggle');
cy.get(proposalDetailsDescription)
.find('p')
.should('have.text', proposalDescription);
@@ -82,7 +85,7 @@ describe(
cy.get('code.language-json')
.should('exist')
.within(() => {
cy.get('.hljs-string').eq(0).should('have.text', '"ProposalTerms"');
cy.get('.hljs-attr').eq(0).should('have.text', '"id"');
});
});
@@ -109,16 +112,9 @@ describe(
closingDate
);
});
cy.wrap(
formatDateWithLocalTimezone(
new Date(createTenDigitUnixTimeStampForSpecifiedDays(0) * 1000)
)
).then((proposalDate) => {
getProposalInformationFromTable('Proposed on').should(
'have.text',
proposalDate
);
});
getProposalInformationFromTable('Proposed on')
.invoke('text')
.should('not.be.empty');
});
it('Newly created proposal details - shows default status set to fail', function () {
@@ -7,7 +7,6 @@ import {
import {
clickOnValidatorFromList,
closeStakingDialog,
stakingPageAssociateTokens,
stakingValidatorPageAddStake,
waitForBeginningOfEpoch,
} from '../../support/staking.functions';
@@ -31,19 +30,17 @@ context('rewards - flow', { tags: '@slow' }, function () {
cy.visit('/');
waitForSpinner();
depositAsset(vegaAssetAddress, '1000', 18);
cy.validatorsSelfDelegate();
ethereumWalletConnect();
cy.connectVegaWallet();
vegaWalletTeardown();
cy.associateTokensToVegaWallet('6000');
cy.VegaWalletTopUpRewardsPool(30, 200);
navigateTo(navigation.validators);
vegaWalletTeardown();
stakingPageAssociateTokens('6000');
cy.get(vegaWalletUnstakedBalance, txTimeout).should(
'contain',
'6,000.0',
txTimeout
);
cy.get('button').contains('Select a validator to nominate').click();
clickOnValidatorFromList(0);
stakingValidatorPageAddStake('3000');
closeStakingDialog();
@@ -24,6 +24,7 @@ const ethWalletContainer = '[data-testid="ethereum-wallet"]';
const vegaWalletAssociatedBalance = '[data-testid="currency-value"]';
const vegaWalletUnstakedBalance =
'[data-testid="vega-wallet-balance-unstaked"]';
const currencyTitle = '[data-testid="currency-title"]:visible';
const txTimeout = Cypress.env('txTimeout');
const vegaWalletPublicKeyShort = Cypress.env('vegaWalletPublicKeyShort');
const ethWalletAssociateButton = '[data-testid="associate-btn"]:visible';
@@ -79,14 +80,11 @@ context(
//0005-ETXN-005
stakingPageAssociateTokens('2', { skipConfirmation: true });
cy.getByTestId('currency-title', txTimeout).should(
'have.length.above',
6
);
cy.get(currencyTitle, txTimeout).should('have.length.above', 4);
validateWalletCurrency('Associated', '0.00');
validateWalletCurrency('Pending association', '2.00');
validateWalletCurrency('Total associated after pending', '2.00');
cy.getByTestId('currency-title', txTimeout).should('have.length', 6);
cy.get(currencyTitle, txTimeout).should('have.length.at.least', 6);
// 0005-ETXN-002
verifyEthWalletAssociatedBalance('2.0');
@@ -117,14 +115,11 @@ context(
verifyEthWalletTotalAssociatedBalance('6,002.00');
cy.get('button').contains('Select a validator to nominate').click();
stakingPageDisassociateTokens('2');
cy.getByTestId('currency-title', txTimeout).should(
'have.length.above',
6
);
cy.get(currencyTitle, txTimeout).should('have.length.above', 4);
validateWalletCurrency('Associated', '2.00');
validateWalletCurrency('Pending association', '2.00');
validateWalletCurrency('Total associated after pending', '0.00');
cy.getByTestId('currency-title', txTimeout).should('have.length', 6);
cy.get(currencyTitle, txTimeout).should('have.length.at.least', 6);
cy.get(
'[data-testid="eth-wallet-associated-balances"]:visible',
txTimeout
@@ -225,14 +220,11 @@ context(
skipConfirmation: true,
});
cy.getByTestId('currency-title', txTimeout).should(
'have.length.above',
6
);
cy.get(currencyTitle, txTimeout).should('have.length.above', 4);
validateWalletCurrency('Associated', '0.00');
validateWalletCurrency('Pending association', '2.00');
validateWalletCurrency('Total associated after pending', '2.00');
cy.getByTestId('currency-title', txTimeout).should('have.length', 6);
cy.get(currencyTitle, txTimeout).should('have.length.at.least', 6);
verifyEthWalletAssociatedBalance('2.0');
verifyEthWalletTotalAssociatedBalance('2.0');
cy.get(vegaWallet)
@@ -248,14 +240,11 @@ context(
type: 'contract',
skipConfirmation: true,
});
cy.getByTestId('currency-title', txTimeout).should(
'have.length.above',
6
);
cy.get(currencyTitle, txTimeout).should('have.length.above', 4);
validateWalletCurrency('Associated', '2.00');
validateWalletCurrency('Pending association', '1.00');
validateWalletCurrency('Total associated after pending', '1.00');
cy.getByTestId('currency-title', txTimeout).should('have.length', 6);
cy.get(currencyTitle, txTimeout).should('have.length.at.least', 6);
verifyEthWalletAssociatedBalance('1.0');
verifyEthWalletTotalAssociatedBalance('1.0');
});
@@ -339,14 +328,11 @@ context(
// 1004-ASSO-004
it('Pending association outside of app is shown', function () {
vegaWalletAssociate('2');
cy.getByTestId('currency-title', txTimeout).should(
'have.length.above',
6
);
cy.get(currencyTitle, txTimeout).should('have.length.above', 4);
validateWalletCurrency('Associated', '0.00');
validateWalletCurrency('Pending association', '2.00');
validateWalletCurrency('Total associated after pending', '2.00');
cy.getByTestId('currency-title', txTimeout).should('have.length', 6);
cy.get(currencyTitle, txTimeout).should('have.length.at.least', 6);
validateWalletCurrency('Associated', '2.00');
});
@@ -355,14 +341,11 @@ context(
cy.wrap(validateWalletCurrency('Associated', '2.00')).then(() => {
vegaWalletDisassociate('2');
});
cy.getByTestId('currency-title', txTimeout).should(
'have.length.above',
6
);
cy.get(currencyTitle, txTimeout).should('have.length.above', 4);
validateWalletCurrency('Associated', '2.00');
validateWalletCurrency('Pending association', '2.00');
validateWalletCurrency('Total associated after pending', '0.00');
cy.getByTestId('currency-title', txTimeout).should('have.length', 6);
cy.get(currencyTitle, txTimeout).should('have.length.at.least', 6);
validateWalletCurrency('Associated', '0.00');
});
@@ -15,13 +15,7 @@ const balanceAvailable = 'BALANCE_AVAILABLE_value';
const withdrawalThreshold = 'WITHDRAWAL_THRESHOLD_value';
const delayTime = 'DELAY_TIME_value';
const submitWithdrawalButton = 'submit-withdrawal';
const dialogTitle = 'dialog-title';
const dialogClose = 'dialog-close';
const txExplorerLink = 'tx-block-explorer';
const withdrawalAssetSymbol = 'withdrawal-asset-symbol';
const withdrawalAmount = 'withdrawal-amount';
const withdrawalRecipient = 'withdrawal-recipient';
const withdrawFundsButton = 'withdraw-funds';
const completeWithdrawalButton = 'complete-withdrawal';
const tableTxHash = '[col-id="txHash"]';
const tableAssetSymbol = '[col-id="asset.symbol"]';
@@ -30,14 +24,16 @@ const tableReceiverAddress = '[col-id="details.receiverAddress"]';
const tableWithdrawnTimeStamp = '[col-id="withdrawnTimestamp"]';
const tableWithdrawnStatus = '[col-id="status"]';
const tableCreatedTimeStamp = '[col-id="createdTimestamp"]';
const toastContent = 'toast-content';
const toast = 'toast';
const toastPanel = 'toast-panel';
const toastClose = 'toast-close';
const withdrawalDialogContent = 'dialog-content';
const toastCompleteWithdrawal = 'toast-complete-withdrawal';
const usdtName = 'USDC (local)';
const usdcEthAddress = '0x1b8a1B6CBE5c93609b46D1829Cc7f3Cb8eeE23a0';
const usdcSymbol = 'tUSDC';
const usdtSelectValue =
'993ed98f4f770d91a796faab1738551193ba45c62341d20597df70fea6704ede';
const truncatedWithdrawalEthAddress = '0xEe7D…22d94F';
const formValidationError = 'input-error-text';
const txTimeout = Cypress.env('txTimeout');
@@ -107,29 +103,35 @@ context(
cy.getByTestId(amountInput).click().type('120');
cy.getByTestId(submitWithdrawalButton).click();
});
cy.contains('Awaiting network confirmation').should('be.visible');
// assert withdrawal request
cy.getByTestId(dialogTitle, txTimeout).should(
'have.text',
'Transaction complete'
);
cy.getByTestId(txExplorerLink)
.should('have.attr', 'href')
.and('contain', '/txs/');
cy.getByTestId(withdrawalAssetSymbol).should('have.text', usdcSymbol);
cy.getByTestId(withdrawalAmount).should('have.text', '120.00');
cy.getByTestId(withdrawalRecipient)
.should('have.text', truncatedWithdrawalEthAddress)
.and('have.attr', 'href')
.and('contain', `/address/${Cypress.env('ethWalletPublicKey')}`);
cy.getByTestId(withdrawFundsButton).click();
cy.getByTestId(toast)
.first(txTimeout)
.should('contain.text', 'Funds unlocked')
.within(() => {
cy.getByTestId('external-link').should('exist');
cy.getByTestId(toastPanel).should(
'contain.text',
'Withdraw 120.00 tUSDC'
);
cy.getByTestId(toastCompleteWithdrawal).click();
cy.getByTestId(toastClose).click();
});
// withdrawal complete
cy.getByTestId(dialogTitle, txTimeout).should(
'have.text',
'Withdraw asset complete'
);
cy.getByTestId(dialogClose).click();
cy.getByTestId(toast)
.first(txTimeout)
.should('contain.text', 'The withdrawal has been approved.')
.within(() => {
cy.getByTestId(toastPanel).should(
'contain.text',
'Withdraw 120.00 tUSDC'
);
});
cy.getByTestId(toast)
.last(txTimeout)
.should('contain.text', 'Transaction confirmed')
.within(() => {
cy.getByTestId('external-link').should('exist');
});
// withdrawal history for complete withdrawal displayed
cy.get(tableWithdrawnStatus)
.eq(1, txTimeout)
@@ -168,13 +170,17 @@ context(
cy.getByTestId(submitWithdrawalButton).click();
});
cy.contains('Awaiting network confirmation').should('be.visible');
// assert withdrawal request
cy.getByTestId(dialogTitle, txTimeout).should(
'have.text',
'Transaction complete'
);
cy.getByTestId(dialogClose).click();
cy.getByTestId(toast)
.first(txTimeout)
.should('contain.text', 'Funds unlocked')
.within(() => {
cy.getByTestId('external-link').should('exist');
cy.getByTestId(toastPanel).should(
'contain.text',
'Withdraw 110.00 tUSDC'
);
cy.getByTestId(toastClose).click();
});
cy.get(tableTxHash)
.eq(1)
.should('have.text', 'Complete withdrawal')
@@ -189,28 +195,75 @@ context(
cy.get(tableCreatedTimeStamp).should('not.be.empty');
});
ethereumWalletConnect();
cy.getByTestId(completeWithdrawalButton).click();
cy.getByTestId(toastContent)
.last()
cy.getByTestId(completeWithdrawalButton).first().click();
cy.getByTestId(toast)
.last(txTimeout)
.should('contain.text', 'Awaiting confirmation')
.within(() => {
cy.getByTestId('external-link').should('exist');
});
cy.getByTestId(toastContent)
.first()
cy.getByTestId(toast)
.first(txTimeout)
.should('contain.text', 'The withdrawal has been approved.')
.within(() => {
cy.getByTestId(toastPanel).should('contain.text', '110.00', 'tUSDC');
});
cy.getByTestId(toastContent)
.last()
cy.getByTestId(toast)
.last(txTimeout)
.should('contain.text', 'Transaction confirmed')
.within(() => {
cy.getByTestId('external-link').should('exist');
});
});
it('Unable to withdraw asset on pub key view', function () {
it('Should be able to see withdrawal details from toast', function () {
cy.getByTestId(withdraw).click();
cy.getByTestId(withdrawalForm, txTimeout).within(() => {
cy.get('select').select(usdtSelectValue, { force: true });
cy.getByTestId(balanceAvailable, txTimeout).should('exist');
cy.getByTestId(withdrawalThreshold).should(
'have.text',
'100,000.00000T'
);
cy.getByTestId(amountInput).click().type('50');
cy.getByTestId(submitWithdrawalButton).click();
});
cy.getByTestId(toast)
.first(txTimeout)
.should('contain.text', 'Funds unlocked')
.within(() => {
cy.getByTestId('external-link').should('exist');
cy.getByTestId(toastPanel).should(
'contain.text',
'Withdraw 50.00 tUSDC'
);
cy.contains('save your withdrawal details').click();
});
cy.getByTestId(withdrawalDialogContent)
.last()
.within(() => {
cy.getByTestId('assetSource_value').should(
'have.text',
'0x1b8a1B6CBE5c93609b46D1829Cc7f3Cb8eeE23a0'
);
cy.getByTestId('amount_value').should('have.text', '5000000');
cy.getByTestId('nonce_value').invoke('text').should('not.be.empty');
cy.getByTestId('signatures_value')
.invoke('text')
.should('not.be.empty');
cy.getByTestId('targetAddress_value').should(
'have.text',
Cypress.env('ethWalletPublicKey')
);
cy.getByTestId('creation_value')
.invoke('text')
.should('not.be.empty');
});
cy.getByTestId(dialogClose).click();
});
// Skipping test due to bug #3882
it.skip('Unable to withdraw asset on pub key view', function () {
const vegaWalletPubKey = Cypress.env('vegaWalletPublicKey');
const expectedErrorTxt = `You are connected in a view only state for public key: ${vegaWalletPubKey}. In order to send transactions you must connect to a real wallet.`;
@@ -224,10 +277,11 @@ context(
cy.get('select').select(usdtSelectValue, { force: true });
cy.getByTestId(balanceAvailable, txTimeout).should('exist');
cy.getByTestId(amountInput).click().type('100');
cy.pause();
cy.getByTestId(submitWithdrawalButton).click();
});
cy.getByTestId('dialog-content')
cy.getByTestId(withdrawalDialogContent)
.last()
.within(() => {
cy.get('h1').should('have.text', 'Transaction failed');
@@ -128,6 +128,7 @@ context(
.first()
.find('[data-testid="view-proposal-btn"]')
.click();
cy.url().should('contain', '/protocol-upgrades/v1');
cy.getByTestId('protocol-upgrade-proposal').within(() => {
cy.get('h1').should('have.text', 'Vega Release v1');
cy.getByTestId('protocol-upgrade-block-height').should(
@@ -221,7 +221,7 @@ export function ensureSpecifiedUnstakedTokensAreAssociated(
}
export function closeStakingDialog() {
cy.getByTestId('dialog-title').should(
cy.getByTestId('dialog-title', txTimeout).should(
'contain.text',
'At the beginning of the next epoch'
);
@@ -5,7 +5,7 @@ const capsuleWalletConnectButton = '[data-testid="web3-connector-Unknown"]';
export function ethereumWalletConnect() {
cy.highlight('Connecting Eth Wallet');
cy.get(connectToEthButton).within(() => {
cy.get(connectToEthButton, { timeout: 60000 }).within(() => {
cy.contains('Connect Ethereum wallet to associate $VEGA')
.should('be.visible')
.click();
@@ -115,29 +115,6 @@ describe('Proposals list', () => {
);
});
it('Orders proposals correctly by closingDateTime', () => {
render(
renderComponent([
failedProposalClosedLastMonth,
openProposalClosesNextMonth,
openProposalClosesNextWeek,
enactedProposalClosedLastWeek,
])
);
const openProposals = within(screen.getByTestId('open-proposals'));
const closedProposals = within(screen.getByTestId('closed-proposals'));
const openProposalsItems = openProposals.getAllByTestId(
'proposals-list-item'
);
const closedProposalsItems = closedProposals.getAllByTestId(
'proposals-list-item'
);
expect(openProposalsItems[0]).toHaveAttribute('id', 'proposal2');
expect(openProposalsItems[1]).toHaveAttribute('id', 'proposal1');
expect(closedProposalsItems[0]).toHaveAttribute('id', 'proposal3');
expect(closedProposalsItems[1]).toHaveAttribute('id', 'proposal4');
});
it('Displays info on no proposals', () => {
render(renderComponent([]));
expect(screen.queryByTestId('open-proposals')).not.toBeInTheDocument();
@@ -35,7 +35,11 @@ export const orderByDate = (arr: ProposalFieldsFragment[]) =>
orderBy(
arr,
[
(p) => new Date(p?.terms?.closingDatetime).getTime(),
(p) =>
p?.terms?.enactmentDatetime
? new Date(p?.terms?.enactmentDatetime).getTime()
: // has to be defaulted to 0 because new Date(null).getTime() -> NaN which is first when ordered
new Date(p?.terms?.closingDatetime || 0).getTime(),
(p) => new Date(p?.datetime).getTime(),
],
['asc', 'asc']
@@ -5,6 +5,9 @@ import { ProtocolUpgradeProposalDetailInfo } from '../components/protocol-upgrad
import { getNormalisedVotingPower } from '../../staking/shared';
import type { NodesFragmentFragment } from '../../staking/home/__generated__/Nodes';
import type { ProtocolUpgradeProposalFieldsFragment } from '@vegaprotocol/proposals';
import { useVegaRelease } from '@vegaprotocol/environment';
import { ExternalLink } from '@vegaprotocol/ui-toolkit';
import { useTranslation } from 'react-i18next';
export interface ProtocolUpgradeProposalProps {
proposal: ProtocolUpgradeProposalFieldsFragment;
@@ -42,6 +45,9 @@ export const ProtocolUpgradeProposal = ({
lastBlockHeight,
consensusValidators,
}: ProtocolUpgradeProposalProps) => {
const { t } = useTranslation();
const releaseInfo = useVegaRelease(proposal.vegaReleaseTag);
const consensusApprovals = useMemo(
() => getConsensusApprovals(consensusValidators || [], proposal),
[consensusValidators, proposal]
@@ -78,6 +84,14 @@ export const ProtocolUpgradeProposal = ({
totalConsensusValidators={consensusValidators.length}
/>
)}
{releaseInfo && releaseInfo.htmlUrl && (
<div className="mb-10">
<ExternalLink href={releaseInfo.htmlUrl}>
{t('Explore release on GitHub')}
</ExternalLink>
</div>
)}
</section>
);
};
@@ -8,6 +8,7 @@ const mockData = {
{
asset: 'tDAI',
totalAmount: '5',
decimals: 6,
rewardTypes: {
ACCOUNT_TYPE_GLOBAL_REWARD: {
amount: '0',
@@ -1,6 +1,5 @@
import { formatNumber, toBigNum } from '@vegaprotocol/utils';
import { Tooltip } from '@vegaprotocol/ui-toolkit';
import { useAppState } from '../../../contexts/app-state/app-state-context';
import {
rowGridItemStyles,
RewardsTable,
@@ -13,7 +12,8 @@ interface EpochIndividualRewardsGridProps {
}
interface RewardItemProps {
value: string;
amount: string;
decimals: number;
percentageOfTotal?: string;
dataTestId: string;
last?: boolean;
@@ -21,15 +21,14 @@ interface RewardItemProps {
const DisplayReward = ({
reward,
decimals,
percentageOfTotal,
}: {
reward: string;
decimals: number;
percentageOfTotal?: string;
}) => {
const { t } = useTranslation();
const {
appState: { decimals },
} = useAppState();
if (Number(reward) === 0) {
return <span className="text-vega-dark-300">-</span>;
@@ -64,7 +63,8 @@ const DisplayReward = ({
};
const RewardItem = ({
value,
amount,
decimals,
percentageOfTotal,
dataTestId,
last,
@@ -72,7 +72,11 @@ const RewardItem = ({
<div data-testid={dataTestId} className={rowGridItemStyles(last)}>
<div className="h-full w-5 absolute right-0 top-0 bg-gradient-to-r from-transparent to-black pointer-events-none" />
<div className="overflow-auto p-5">
<DisplayReward reward={value} percentageOfTotal={percentageOfTotal} />
<DisplayReward
reward={amount}
decimals={decimals}
percentageOfTotal={percentageOfTotal}
/>
</div>
<div className="h-full w-5 absolute left-0 top-0 bg-gradient-to-l from-transparent to-black pointer-events-none" />
</div>
@@ -86,7 +90,7 @@ export const EpochIndividualRewardsTable = ({
dataTestId="epoch-individual-rewards-table"
epoch={Number(data.epoch)}
>
{data.rewards.map(({ asset, rewardTypes, totalAmount }, i) => (
{data.rewards.map(({ asset, rewardTypes, totalAmount, decimals }, i) => (
<div className="contents" key={i}>
<div
data-testid="individual-rewards-asset"
@@ -98,13 +102,19 @@ export const EpochIndividualRewardsTable = ({
([key, { amount, percentageOfTotal }]) => (
<RewardItem
key={key}
value={amount}
amount={amount}
decimals={decimals}
percentageOfTotal={percentageOfTotal}
dataTestId={key}
/>
)
)}
<RewardItem dataTestId="total" value={totalAmount} last={true} />
<RewardItem
dataTestId="total"
amount={totalAmount}
decimals={decimals}
last={true}
/>
</div>
))}
</RewardsTable>
@@ -8,7 +8,7 @@ describe('generateEpochIndividualRewardsList', () => {
amount: '100',
percentageOfTotal: '0.1',
receivedAt: new Date(),
asset: { id: 'usd', symbol: 'USD', name: 'USD' },
asset: { id: 'usd', symbol: 'USD', name: 'USD', decimals: 6 },
party: { id: 'blah' },
epoch: { id: '1' },
};
@@ -18,7 +18,7 @@ describe('generateEpochIndividualRewardsList', () => {
amount: '50',
percentageOfTotal: '0.05',
receivedAt: new Date(),
asset: { id: 'eur', symbol: 'EUR', name: 'EUR' },
asset: { id: 'eur', symbol: 'EUR', name: 'EUR', decimals: 5 },
party: { id: 'blah' },
epoch: { id: '2' },
};
@@ -28,7 +28,7 @@ describe('generateEpochIndividualRewardsList', () => {
amount: '200',
percentageOfTotal: '0.2',
receivedAt: new Date(),
asset: { id: 'gbp', symbol: 'GBP', name: 'GBP' },
asset: { id: 'gbp', symbol: 'GBP', name: 'GBP', decimals: 7 },
party: { id: 'blah' },
epoch: { id: '2' },
};
@@ -38,7 +38,7 @@ describe('generateEpochIndividualRewardsList', () => {
amount: '100',
percentageOfTotal: '0.1',
receivedAt: new Date(),
asset: { id: 'usd', symbol: 'USD', name: 'USD' },
asset: { id: 'usd', symbol: 'USD', name: 'USD', decimals: 6 },
party: { id: 'blah' },
epoch: { id: '1' },
};
@@ -48,7 +48,7 @@ describe('generateEpochIndividualRewardsList', () => {
amount: '150',
percentageOfTotal: '0.15',
receivedAt: new Date(),
asset: { id: 'usd', symbol: 'USD', name: 'USD' },
asset: { id: 'usd', symbol: 'USD', name: 'USD', decimals: 6 },
party: { id: 'blah' },
epoch: { id: '3' },
};
@@ -58,7 +58,7 @@ describe('generateEpochIndividualRewardsList', () => {
amount: '50',
percentageOfTotal: '0.05',
receivedAt: new Date(),
asset: { id: 'eur', symbol: 'EUR', name: 'EUR' },
asset: { id: 'eur', symbol: 'EUR', name: 'EUR', decimals: 5 },
party: { id: 'blah' },
epoch: { id: '2' },
};
@@ -99,6 +99,7 @@ describe('generateEpochIndividualRewardsList', () => {
rewards: [
{
asset: 'USD',
decimals: 6,
totalAmount: '100',
rewardTypes: {
[AccountType.ACCOUNT_TYPE_FEES_INFRASTRUCTURE]: {
@@ -167,6 +168,7 @@ describe('generateEpochIndividualRewardsList', () => {
{
asset: 'GBP',
totalAmount: '200',
decimals: 7,
rewardTypes: {
[AccountType.ACCOUNT_TYPE_FEES_INFRASTRUCTURE]: {
amount: '0',
@@ -197,6 +199,7 @@ describe('generateEpochIndividualRewardsList', () => {
{
asset: 'EUR',
totalAmount: '50',
decimals: 5,
rewardTypes: {
[AccountType.ACCOUNT_TYPE_FEES_INFRASTRUCTURE]: {
amount: '0',
@@ -232,6 +235,7 @@ describe('generateEpochIndividualRewardsList', () => {
{
asset: 'USD',
totalAmount: '200',
decimals: 6,
rewardTypes: {
[AccountType.ACCOUNT_TYPE_FEES_INFRASTRUCTURE]: {
amount: '0',
@@ -279,6 +283,7 @@ describe('generateEpochIndividualRewardsList', () => {
rewards: [
{
asset: 'USD',
decimals: 6,
totalAmount: '150',
rewardTypes: {
[AccountType.ACCOUNT_TYPE_FEES_INFRASTRUCTURE]: {
@@ -315,6 +320,7 @@ describe('generateEpochIndividualRewardsList', () => {
{
asset: 'GBP',
totalAmount: '200',
decimals: 7,
rewardTypes: {
[AccountType.ACCOUNT_TYPE_FEES_INFRASTRUCTURE]: {
amount: '0',
@@ -345,6 +351,7 @@ describe('generateEpochIndividualRewardsList', () => {
{
asset: 'EUR',
totalAmount: '50',
decimals: 5,
rewardTypes: {
[AccountType.ACCOUNT_TYPE_FEES_INFRASTRUCTURE]: {
amount: '0',
@@ -390,6 +397,7 @@ describe('generateEpochIndividualRewardsList', () => {
{
asset: 'USD',
totalAmount: '200',
decimals: 6,
rewardTypes: {
[AccountType.ACCOUNT_TYPE_FEES_INFRASTRUCTURE]: {
amount: '0',
@@ -9,6 +9,7 @@ export interface EpochIndividualReward {
rewards: {
asset: string;
totalAmount: string;
decimals: number;
rewardTypes: {
[key in AccountType]?: {
amount: string;
@@ -53,6 +54,7 @@ export const generateEpochIndividualRewardsList = ({
const epochIndividualRewards = rewards.reduce((acc, reward) => {
const epochId = reward.epoch.id;
const assetName = reward.asset.name;
const assetDecimals = reward.asset.decimals;
const rewardType = reward.rewardType;
const amount = reward.amount;
const percentageOfTotal = reward.percentageOfTotal;
@@ -73,6 +75,7 @@ export const generateEpochIndividualRewardsList = ({
if (!asset) {
asset = {
asset: assetName,
decimals: assetDecimals,
totalAmount: '0',
rewardTypes: Object.fromEntries(emptyRowAccountTypes),
};
@@ -52,6 +52,7 @@ const assetRewards: Map<
assetRewards.set(assetId, {
assetId,
name: 'tDAI TEST',
decimals: 6,
rewards,
totalAmount: '295',
});
@@ -1,6 +1,5 @@
import { formatNumber, toBigNum } from '@vegaprotocol/utils';
import { Tooltip } from '@vegaprotocol/ui-toolkit';
import { useAppState } from '../../../contexts/app-state/app-state-context';
import {
rowGridItemStyles,
RewardsTable,
@@ -12,16 +11,19 @@ interface EpochTotalRewardsGridProps {
}
interface RewardItemProps {
value: string;
amount: string;
decimals: number;
dataTestId: string;
last?: boolean;
}
const DisplayReward = ({ reward }: { reward: string }) => {
const {
appState: { decimals },
} = useAppState();
const DisplayReward = ({
reward,
decimals,
}: {
reward: string;
decimals: number;
}) => {
if (Number(reward) === 0) {
return <span className="text-vega-dark-300">-</span>;
}
@@ -33,11 +35,16 @@ const DisplayReward = ({ reward }: { reward: string }) => {
);
};
const RewardItem = ({ value, dataTestId, last }: RewardItemProps) => (
const RewardItem = ({
amount,
decimals,
dataTestId,
last,
}: RewardItemProps) => (
<div data-testid={dataTestId} className={rowGridItemStyles(last)}>
<div className="h-full w-5 absolute right-0 top-0 bg-gradient-to-r from-transparent to-black pointer-events-none" />
<div className="overflow-auto p-5">
<DisplayReward reward={value} />
<DisplayReward reward={amount} decimals={decimals} />
</div>
<div className="h-full w-5 absolute left-0 top-0 bg-gradient-to-l from-transparent to-black pointer-events-none" />
</div>
@@ -49,15 +56,25 @@ export const EpochTotalRewardsTable = ({
return (
<RewardsTable dataTestId="epoch-total-rewards-table" epoch={data.epoch}>
{Array.from(data.assetRewards.values()).map(
({ name, rewards, totalAmount }, i) => (
({ name, rewards, totalAmount, decimals }, i) => (
<div className="contents" key={i}>
<div data-testid="asset" className={`${rowGridItemStyles()} p-5`}>
{name}
</div>
{Array.from(rewards.values()).map(({ rewardType, amount }, i) => (
<RewardItem key={i} dataTestId={rewardType} value={amount} />
<RewardItem
key={i}
dataTestId={rewardType}
amount={amount}
decimals={decimals}
/>
))}
<RewardItem dataTestId="total" value={totalAmount} last={true} />
<RewardItem
dataTestId="total"
amount={totalAmount}
decimals={decimals}
last={true}
/>
</div>
)
)}
@@ -56,12 +56,14 @@ describe('generateEpochAssetRewardsList', () => {
node: {
id: '1',
name: 'Asset 1',
decimals: 18,
},
},
{
node: {
id: '2',
name: 'Asset 2',
decimals: 6,
},
},
],
@@ -101,6 +103,7 @@ describe('generateEpochAssetRewardsList', () => {
{
node: {
epoch: 1,
decimals: 18,
assetId: '1',
rewardType: AccountType.ACCOUNT_TYPE_GLOBAL_REWARD,
amount: '123',
@@ -128,6 +131,7 @@ describe('generateEpochAssetRewardsList', () => {
'1',
{
assetId: '1',
decimals: 0,
name: '',
rewards: new Map([
[
@@ -196,12 +200,14 @@ describe('generateEpochAssetRewardsList', () => {
node: {
id: '1',
name: 'Asset 1',
decimals: 18,
},
},
{
node: {
id: '2',
name: 'Asset 2',
decimals: 6,
},
},
],
@@ -212,6 +218,7 @@ describe('generateEpochAssetRewardsList', () => {
node: {
epoch: 1,
assetId: '1',
decimals: 18,
rewardType: AccountType.ACCOUNT_TYPE_REWARD_MAKER_PAID_FEES,
amount: '123',
},
@@ -220,6 +227,7 @@ describe('generateEpochAssetRewardsList', () => {
node: {
epoch: 1,
assetId: '1',
decimals: 18,
rewardType: AccountType.ACCOUNT_TYPE_FEES_INFRASTRUCTURE,
amount: '100',
},
@@ -228,6 +236,7 @@ describe('generateEpochAssetRewardsList', () => {
node: {
epoch: 2,
assetId: '1',
decimals: 18,
rewardType: AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES,
amount: '5',
},
@@ -254,6 +263,7 @@ describe('generateEpochAssetRewardsList', () => {
'1',
{
assetId: '1',
decimals: 18,
name: 'Asset 1',
rewards: new Map([
[
@@ -319,6 +329,7 @@ describe('generateEpochAssetRewardsList', () => {
'1',
{
assetId: '1',
decimals: 18,
name: 'Asset 1',
rewards: new Map([
[
@@ -387,12 +398,14 @@ describe('generateEpochAssetRewardsList', () => {
node: {
id: '1',
name: 'Asset 1',
decimals: 18,
},
},
{
node: {
id: '2',
name: 'Asset 2',
decimals: 6,
},
},
],
@@ -403,6 +416,7 @@ describe('generateEpochAssetRewardsList', () => {
node: {
epoch: 1,
assetId: '1',
decimals: 18,
rewardType: AccountType.ACCOUNT_TYPE_REWARD_MAKER_PAID_FEES,
amount: '123',
},
@@ -411,6 +425,7 @@ describe('generateEpochAssetRewardsList', () => {
node: {
epoch: 1,
assetId: '1',
decimals: 18,
rewardType: AccountType.ACCOUNT_TYPE_FEES_INFRASTRUCTURE,
amount: '100',
},
@@ -419,6 +434,7 @@ describe('generateEpochAssetRewardsList', () => {
node: {
epoch: 2,
assetId: '1',
decimals: 18,
rewardType: AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES,
amount: '6',
},
@@ -427,6 +443,7 @@ describe('generateEpochAssetRewardsList', () => {
node: {
epoch: 2,
assetId: '1',
decimals: 18,
rewardType: AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES,
amount: '27',
},
@@ -435,6 +452,7 @@ describe('generateEpochAssetRewardsList', () => {
node: {
epoch: 3,
assetId: '1',
decimals: 18,
rewardType: AccountType.ACCOUNT_TYPE_FEES_INFRASTRUCTURE,
amount: '15',
},
@@ -467,6 +485,7 @@ describe('generateEpochAssetRewardsList', () => {
{
assetId: '1',
name: 'Asset 1',
decimals: 18,
rewards: new Map([
[
AccountType.ACCOUNT_TYPE_GLOBAL_REWARD,
@@ -531,6 +550,7 @@ describe('generateEpochAssetRewardsList', () => {
'1',
{
assetId: '1',
decimals: 18,
name: 'Asset 1',
rewards: new Map([
[
@@ -608,6 +628,7 @@ describe('generateEpochAssetRewardsList', () => {
'1',
{
assetId: '1',
decimals: 18,
name: 'Asset 1',
rewards: new Map([
[
@@ -22,6 +22,7 @@ export type AggregatedEpochRewardSummary = {
name: EpochSummaryWithNamedReward['name'];
rewards: Map<RewardType, RewardItem>;
totalAmount: string;
decimals: number;
};
export type EpochTotalSummary = {
@@ -91,6 +92,7 @@ export const generateEpochTotalRewardsList = ({
assetId: reward.assetId,
name: matchingAsset?.name || '',
rewards: rewards || new Map(emptyRowAccountTypes),
decimals: matchingAsset?.decimals || 0,
totalAmount: (
Number(reward.amount) + Number(assetWithRewards?.totalAmount || 0)
).toString(),
@@ -4,6 +4,7 @@ fragment RewardFields on Reward {
id
symbol
name
decimals
}
party {
id
@@ -67,6 +68,7 @@ query EpochAssetsRewards(
node {
id
name
decimals
}
}
}
@@ -3,7 +3,7 @@ import * as Types from '@vegaprotocol/types';
import { gql } from '@apollo/client';
import * as Apollo from '@apollo/client';
const defaultOptions = {} as const;
export type RewardFieldsFragment = { __typename?: 'Reward', rewardType: Types.AccountType, amount: string, percentageOfTotal: string, receivedAt: any, asset: { __typename?: 'Asset', id: string, symbol: string, name: string }, party: { __typename?: 'Party', id: string }, epoch: { __typename?: 'Epoch', id: string } };
export type RewardFieldsFragment = { __typename?: 'Reward', rewardType: Types.AccountType, amount: string, percentageOfTotal: string, receivedAt: any, asset: { __typename?: 'Asset', id: string, symbol: string, name: string, decimals: number }, party: { __typename?: 'Party', id: string }, epoch: { __typename?: 'Epoch', id: string } };
export type DelegationFieldsFragment = { __typename?: 'Delegation', amount: string, epoch: number };
@@ -16,7 +16,7 @@ export type RewardsQueryVariables = Types.Exact<{
}>;
export type RewardsQuery = { __typename?: 'Query', party?: { __typename?: 'Party', id: string, rewardsConnection?: { __typename?: 'RewardsConnection', edges?: Array<{ __typename?: 'RewardEdge', node: { __typename?: 'Reward', rewardType: Types.AccountType, amount: string, percentageOfTotal: string, receivedAt: any, asset: { __typename?: 'Asset', id: string, symbol: string, name: string }, party: { __typename?: 'Party', id: string }, epoch: { __typename?: 'Epoch', id: string } } } | null> | null } | null, delegationsConnection?: { __typename?: 'DelegationsConnection', edges?: Array<{ __typename?: 'DelegationEdge', node: { __typename?: 'Delegation', amount: string, epoch: number } } | null> | null } | null } | null };
export type RewardsQuery = { __typename?: 'Query', party?: { __typename?: 'Party', id: string, rewardsConnection?: { __typename?: 'RewardsConnection', edges?: Array<{ __typename?: 'RewardEdge', node: { __typename?: 'Reward', rewardType: Types.AccountType, amount: string, percentageOfTotal: string, receivedAt: any, asset: { __typename?: 'Asset', id: string, symbol: string, name: string, decimals: number }, party: { __typename?: 'Party', id: string }, epoch: { __typename?: 'Epoch', id: string } } } | null> | null } | null, delegationsConnection?: { __typename?: 'DelegationsConnection', edges?: Array<{ __typename?: 'DelegationEdge', node: { __typename?: 'Delegation', amount: string, epoch: number } } | null> | null } | null } | null };
export type EpochRewardSummaryFieldsFragment = { __typename?: 'EpochRewardSummary', epoch: number, assetId: string, amount: string, rewardType: Types.AccountType };
@@ -26,7 +26,7 @@ export type EpochAssetsRewardsQueryVariables = Types.Exact<{
}>;
export type EpochAssetsRewardsQuery = { __typename?: 'Query', assetsConnection?: { __typename?: 'AssetsConnection', edges?: Array<{ __typename?: 'AssetEdge', node: { __typename?: 'Asset', id: string, name: string } } | null> | null } | null, epochRewardSummaries?: { __typename?: 'EpochRewardSummaryConnection', edges?: Array<{ __typename?: 'EpochRewardSummaryEdge', node: { __typename?: 'EpochRewardSummary', epoch: number, assetId: string, amount: string, rewardType: Types.AccountType } } | null> | null } | null };
export type EpochAssetsRewardsQuery = { __typename?: 'Query', assetsConnection?: { __typename?: 'AssetsConnection', edges?: Array<{ __typename?: 'AssetEdge', node: { __typename?: 'Asset', id: string, name: string, decimals: number } } | null> | null } | null, epochRewardSummaries?: { __typename?: 'EpochRewardSummaryConnection', edges?: Array<{ __typename?: 'EpochRewardSummaryEdge', node: { __typename?: 'EpochRewardSummary', epoch: number, assetId: string, amount: string, rewardType: Types.AccountType } } | null> | null } | null };
export type EpochFieldsFragment = { __typename?: 'Epoch', id: string, timestamps: { __typename?: 'EpochTimestamps', start?: any | null, end?: any | null, expiry?: any | null } };
@@ -42,6 +42,7 @@ export const RewardFieldsFragmentDoc = gql`
id
symbol
name
decimals
}
party {
id
@@ -143,6 +144,7 @@ export const EpochAssetsRewardsDocument = gql`
node {
id
name
decimals
}
}
}
@@ -71,14 +71,6 @@ export const calculateOverstakedPenalty = (nodeId: string, nodes: Node[]) => {
new BigNumber(node.rewardScore?.rawValidatorScore || 0).dividedBy(tts)
)
.times(100);
console.log(
nodeId,
new BigNumber(node.rewardScore?.rawValidatorScore || 0).toString(),
tts.toString(),
new BigNumber(node.rewardScore?.rawValidatorScore || 0)
.dividedBy(tts)
.toString()
);
return penalty.isLessThan(0) ? new BigNumber(0) : penalty;
};
@@ -6,4 +6,4 @@ NX_VEGA_NETWORKS={\"TESTNET\":\"https://console.fairground.wtf\"}
NX_ETHEREUM_PROVIDER_URL=https://mainnet.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8
NX_ETHERSCAN_URL=https://etherscan.io
NX_VEGA_EXPLORER_URL=https://explorer.vega.xyz
NX_VEGA_CONSOLE_URL=https://vega.trading
NX_VEGA_CONSOLE_URL=https://console.vega.xyz
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -24,7 +24,7 @@ module.exports = defineConfig({
viewportHeight: 900,
responseTimeout: 50000,
requestTimeout: 20000,
retries: 2,
retries: 1,
testIsolation: false,
},
env: {
+10 -10
View File
@@ -50,10 +50,7 @@ describe('capsule - without MultiSign', { tags: '@slow' }, () => {
cy.wrap(markets[0]).as('market');
});
cy.visit('/#/portfolio');
});
beforeEach(() => {
cy.setVegaWallet();
cy.connectVegaWallet();
});
it('can deposit', function () {
@@ -172,7 +169,6 @@ describe('capsule - without MultiSign', { tags: '@slow' }, () => {
'contain.text',
'Error occurredcannot estimate gas'
);
cy.getByTestId(toastCloseBtn).click({ multiple: true });
cy.getByTestId(completeWithdrawalBtn).should(
'contain.text',
'Complete withdrawal'
@@ -376,10 +372,14 @@ describe('capsule', { tags: '@slow', testIsolation: true }, () => {
'contain.text',
'Transaction confirmed'
);
cy.getByTestId(toastContent, txTimeout).should(
'contain.text',
'Funds unlockedYour funds have been unlocked for withdrawalView in block explorerWithdraw 1.00 tBTCComplete withdrawal'
);
cy.getByTestId(toastContent, txTimeout)
.should('contain.text', 'Funds unlocked')
.and('contain.text', 'Your funds have been unlocked for withdrawal.')
.and(
'contain.text',
'View in block explorerYou can save your withdrawal details for extra security.'
)
.and('contain.text', 'Withdraw 1.00 tBTCComplete withdrawal');
cy.get('.ag-center-cols-container')
.find('[col-id="status"]')
@@ -407,7 +407,7 @@ describe('capsule', { tags: '@slow', testIsolation: true }, () => {
});
cy.getByTestId('withdraw-dialog-button').click({ force: true });
cy.getByTestId('BALANCE_AVAILABLE_value').should('have.text', '7.999');
// cy.getByTestId('BALANCE_AVAILABLE_value').should('have.text', '6.999');
});
it('approved amount is less than deposit', function () {
@@ -1,20 +1,16 @@
import { closeWelcomeDialog } from '../support/helpers';
const dialogContent = 'dialog-content';
const nodeHealth = 'node-health';
describe('home', { tags: '@regression' }, () => {
describe.skip('home', { tags: '@regression' }, () => {
before(() => {
cy.clearAllLocalStorage();
cy.mockTradingPage();
cy.mockSubscription();
cy.visit('/');
closeWelcomeDialog();
});
describe('footer', () => {
it.skip('shows current block height', () => {
closeWelcomeDialog();
it('shows current block height', () => {
// 0006-NETW-004
// 0006-NETW-005
// 0006-NETW-008
+3 -97
View File
@@ -1,9 +1,7 @@
import { aliasGQLQuery } from '@vegaprotocol/cypress';
import type { ProposalListFieldsFragment } from '@vegaprotocol/proposals';
import { marketsDataQuery } from '@vegaprotocol/mock';
import * as Schema from '@vegaprotocol/types';
const selectMarketOverlay = 'select-market-list';
const dialogContent = 'dialog-content';
const generateProposal = (code: string): ProposalListFieldsFragment => ({
@@ -110,77 +108,12 @@ describe('home', { tags: '@regression' }, () => {
cy.get('main[data-testid^="/markets/"]');
// Overlay should be shown
cy.getByTestId(selectMarketOverlay).should('exist');
cy.contains('Select a market to get started').should('be.visible');
// I expect the market overlay table to contain at least 3 rows (one header row)
cy.getByTestId(selectMarketOverlay)
.get('table tr')
.then((row) => {
expect(row.length >= 3).to.be.true;
});
// each market shown in overlay table contains content under the last price and change fields
cy.getByTestId(selectMarketOverlay)
.get('table tr')
.each(($element, index) => {
if (index > 0) {
// skip header row
cy.root().within(() => {
cy.getByTestId('price').should('not.be.empty');
});
}
});
cy.getByTestId('welcome-notice-proposed-markets')
.children('div.pt-1.flex.justify-between')
.should('have.length', 3)
.each((item) => {
cy.wrap(item).getByTestId('external-link').should('exist');
});
cy.getByTestId('dialog-close').click();
cy.getByTestId(selectMarketOverlay).should('not.exist');
// the choose market overlay is no longer showing
cy.contains('Select a market to get started').should('not.exist');
cy.contains('Loading...').should('not.exist');
cy.url().should('eq', Cypress.config().baseUrl + '/#/markets/market-0');
});
});
describe('market table should be properly rendered', () => {
it('redirects to a default market with the landing dialog open', () => {
const override = {
marketsConnection: {
edges: [
{
node: {
data: {
markPrice: '46126900581221212121212121212121212121212121212',
},
},
},
],
},
};
// @ts-ignore partial deep check failing
const data = marketsDataQuery(override);
cy.mockGQL((req) => {
aliasGQLQuery(req, 'MarketsData', data);
});
cy.visit('/');
cy.wait('@Markets');
cy.getByTestId(selectMarketOverlay)
.get('table')
.invoke('outerWidth')
.then((value) => {
expect(value).to.be.closeTo(554, 10);
});
});
});
describe('no markets found', () => {
beforeEach(() => {
cy.mockGQL((req) => {
@@ -206,40 +139,13 @@ describe('home', { tags: '@regression' }, () => {
cy.wait('@Markets');
cy.wait('@MarketsData');
});
it('redirects to a the empty market page and displays welcome notice', () => {
cy.url().should('eq', Cypress.config().baseUrl + `/#/markets`);
it('redirects to market/all and displays welcome notice', () => {
cy.url().should('eq', Cypress.config().baseUrl + `/#/markets/all`);
cy.getByTestId('welcome-notice-title').should(
'contain.text',
'Welcome to Console'
);
cy.getByTestId('welcome-notice-proposed-markets').should(
'contain.text',
'AAAZZZ'
);
});
});
describe('no proposal found', () => {
it('there is a link to propose market', () => {
cy.mockGQL((req) => {
aliasGQLQuery(req, 'ProposalsList', {
proposalsConnection: {
__typename: 'ProposalsConnection',
edges: null,
},
});
});
cy.visit('/');
cy.wait('@Markets');
cy.wait('@MarketsData');
cy.getByTestId(selectMarketOverlay)
.get('table tr')
.then((row) => {
expect(row.length >= 3).to.be.true;
});
cy.getByTestId('external-link')
.contains('Propose a market')
.should('exist');
});
});
@@ -1,4 +1,3 @@
const selectMarketOverlay = 'select-market-list';
const marketInfoBtn = 'Info';
const marketInfoSubtitle = 'accordion-title';
const marketSummaryBlock = 'header-summary';
@@ -14,45 +13,6 @@ const itemHeader = 'item-header';
const itemValue = 'item-value';
const marketListContent = 'popover-content';
describe(
'Console - market list - live env',
{ tags: '@live', testIsolation: true },
() => {
beforeEach(() => {
cy.visit('/');
});
it('shows the market list page', () => {
cy.get('main', { timeout: 20000 });
// Overlay should be shown
cy.getByTestId(selectMarketOverlay).should('exist');
cy.contains('Select a market to get started').should('be.visible');
// I expect the market overlay table to contain at least one row
cy.getByTestId(selectMarketOverlay)
.get('table tr')
.should('have.length.greaterThan', 1);
// each market shown in overlay table contains content under the last price and change fields
cy.getByTestId(selectMarketOverlay)
.get('table tr')
.getByTestId('price')
.should('not.be.empty');
});
it('redirects to a default market', () => {
cy.getByTestId('dialog-close').click();
cy.getByTestId(selectMarketOverlay).should('not.exist');
// the choose market overlay is no longer showing
cy.contains('Select a market to get started').should('not.exist');
cy.contains('Loading...').should('not.exist');
cy.getByTestId('popover-trigger').should('not.be.empty');
});
}
);
describe(
'Console - market info - live env',
{ tags: '@live', testIsolation: true },
@@ -55,7 +55,10 @@ describe('market info is displayed', { tags: '@smoke' }, () => {
validateMarketDataRow(3, 'Quote Unit', 'BTC');
});
it('market volume displayed', () => {
// TODO: fix this test
// New volume check logic, added by https://github.com/vegaprotocol/frontend-monorepo/pull/3870 has caused the
// 24hr volume assertion to fail as it now reads 'Unknown'
it.skip('market volume displayed', () => {
cy.getByTestId(marketTitle).contains('Market volume').click();
validateMarketDataRow(0, '24 Hour Volume', '1');
validateMarketDataRow(1, 'Open Interest', '-');
@@ -38,28 +38,28 @@ describe('markets selector', { tags: '@smoke' }, () => {
// TODO: load data from mocks in. Using alias and wrap intermittently fails
const data = [
{
code: 'AAPL.MF21',
name: 'Apple Monthly (30 Jun 2022)',
markPrice: '46,126.90058',
change: '+200.00%',
},
{
code: 'BTCUSD.MF21',
name: 'ACTIVE MARKET',
markPrice: '46,126.90058',
code: 'SOLUSD',
markPrice: '84.41XYZalpha',
change: '+200.00%',
vol: '324h vol',
},
{
code: 'ETHBTC.QM21',
name: 'ETHBTC Quarterly (30 Jun 2022)',
markPrice: '46,126.90058',
markPrice: '46,126.90058tBTC',
change: '+200.00%',
vol: '324h vol',
},
{
code: 'SOLUSD',
name: 'SUSPENDED MARKET',
markPrice: '84.41',
code: 'BTCUSD.MF21',
markPrice: '46,126.90058tDAI',
change: '+200.00%',
vol: '324h vol',
},
{
code: 'AAPL.MF21',
markPrice: '46,126.90058tUSDC',
change: '+200.00%',
vol: '324h vol',
},
];
cy.getByTestId(list)
@@ -68,12 +68,13 @@ describe('markets selector', { tags: '@smoke' }, () => {
const market = data[i];
// 6001-MARK-021
expect(item.find('h3').text()).equals(market.code);
// 6001-MARK-022
expect(item.find('h4').text()).equals(market.name);
expect(
item.find('[data-testid="market-selector-data-row"]').eq(0).text()
).contains(market.vol);
// 6001-MARK-024
expect(item.find('[data-testid="market-item-price"]').text()).equals(
market.markPrice
);
expect(
item.find('[data-testid="market-selector-data-row"]').eq(1).text()
).contains(market.markPrice);
// 6001-MARK-023
expect(item.find('[data-testid="market-item-change"]').text()).equals(
market.change
@@ -96,8 +97,8 @@ describe('markets selector', { tags: '@smoke' }, () => {
// 6001-MARK-29
cy.getByTestId(searchInput).clear().type('btc');
cy.getByTestId(list).find('a').should('have.length', 2);
cy.getByTestId(list).find('a').eq(0).contains('BTCUSD.MF21');
cy.getByTestId(list).find('a').eq(1).contains('ETHBTC.QM21');
cy.getByTestId(list).find('a').eq(1).contains('BTCUSD.MF21');
cy.getByTestId(list).find('a').eq(0).contains('ETHBTC.QM21');
cy.getByTestId(searchInput).clear();
cy.getByTestId(list).find('a').should('have.length', 4);
+5 -17
View File
@@ -3,8 +3,6 @@ import { aliasGQLQuery, checkSorting } from '@vegaprotocol/cypress';
import { marketsQuery } from '@vegaprotocol/mock';
import { getDateTimeFormat } from '@vegaprotocol/utils';
const dialogCloseBtn = 'dialog-close';
describe('markets table', { tags: '@smoke' }, () => {
beforeEach(() => {
cy.clearLocalStorage().then(() => {
@@ -14,20 +12,18 @@ describe('markets table', { tags: '@smoke' }, () => {
Schema.AuctionTrigger.AUCTION_TRIGGER_LIQUIDITY_TARGET_NOT_MET
);
cy.mockSubscription();
cy.visit('/');
cy.wait('@Markets');
cy.wait('@MarketsData');
cy.wait('@MarketsCandles');
cy.visit('/#/markets/all');
});
});
it('renders markets correctly', () => {
cy.wait('@Markets');
cy.wait('@MarketsData');
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('able to open and sort full market list - market page', () => {
@@ -37,9 +33,6 @@ describe('markets table', { tags: '@smoke' }, () => {
'ETHBTC.QM21',
'SOLUSD',
];
cy.getByTestId('view-market-list-link')
.should('have.attr', 'href', '#/markets/all')
.click();
cy.url().should('eq', Cypress.config('baseUrl') + '/#/markets/all');
cy.contains('AAPL.MF21').should('be.visible');
cy.get('.ag-header-cell-label').contains('Market').click(); // sort by market name
@@ -51,10 +44,6 @@ describe('markets table', { tags: '@smoke' }, () => {
});
it('proposed markets tab should be rendered properly', () => {
cy.getByTestId('view-market-list-link')
.should('have.attr', 'href', '#/markets/all')
.click();
cy.get('[data-testid="All markets"]').should(
'have.attr',
'data-state',
@@ -87,8 +76,8 @@ describe('markets table', { tags: '@smoke' }, () => {
`${Cypress.env('VEGA_TOKEN_URL')}/proposals/propose/new-market`
);
});
it('proposed markets tab should be sorted properly', () => {
cy.getByTestId('view-market-list-link').click();
cy.get('[data-testid="Proposed markets"]').click();
const marketColDefault = [
'ETHUSD',
@@ -167,7 +156,7 @@ describe('markets table', { tags: '@smoke' }, () => {
checkSorting('state', stateColDefault, stateColAsc, stateColDesc);
});
it('opening auction subsets should be properly displayed', () => {
it.skip('opening auction subsets should be properly displayed', () => {
cy.mockTradingPage(
Schema.MarketState.STATE_ACTIVE,
Schema.MarketTradingMode.TRADING_MODE_OPENING_AUCTION
@@ -200,7 +189,6 @@ describe('markets table', { tags: '@smoke' }, () => {
});
cy.visit('#/markets/market-0');
cy.url().should('contain', 'market-0');
cy.getByTestId(dialogCloseBtn).click();
cy.getByTestId('item-value').contains('Opening auction').realHover();
cy.getByTestId('opening-auction-sub-status').should(
'contain.text',
@@ -8,9 +8,6 @@ describe('Navbar', { tags: '@smoke' }, () => {
cy.visit('/');
cy.wait('@Markets');
cy.wait('@MarketsData');
cy.wait('@MarketsCandles');
// close welcome dialog
cy.getByTestId('dialog-close').click();
});
const pages = [
@@ -1,12 +1,9 @@
import { closeWelcomeDialog } from '../support/helpers';
describe('Settings page', { tags: '@smoke' }, () => {
beforeEach(() => {
cy.clearLocalStorage().then(() => {
cy.mockTradingPage();
cy.mockSubscription();
cy.visit('/');
closeWelcomeDialog();
cy.get('[aria-label="cog icon"]').click();
});
});
@@ -1,6 +1,7 @@
import { checkSorting } from '@vegaprotocol/cypress';
import { aliasGQLQuery } from '@vegaprotocol/cypress';
import { marketsDataQuery } from '@vegaprotocol/mock';
import { positionsQuery } from '@vegaprotocol/mock';
beforeEach(() => {
cy.mockTradingPage();
@@ -16,9 +17,27 @@ describe('positions', { tags: '@smoke', testIsolation: true }, () => {
});
it('renders positions on portfolio page', () => {
cy.mockGQL((req) => {
const positions = positionsQuery();
if (positions.positions?.edges) {
positions.positions.edges.push(
...positions.positions.edges.map((edge) => ({
...edge,
node: {
...edge.node,
party: {
...edge.node.party,
id: 'vega-1',
},
},
}))
);
}
aliasGQLQuery(req, 'Positions', positions);
});
cy.visit('/#/portfolio');
cy.getByTestId('Positions').click();
validatePositionsDisplayed();
validatePositionsDisplayed(true);
});
describe('renders position among some graphql errors', () => {
it('rows should be displayed despite errors', () => {
@@ -56,7 +75,9 @@ describe('positions', { tags: '@smoke', testIsolation: true }, () => {
cy.getByTestId('tab-positions')
.first()
.within(() => {
cy.get('[row-id="market-2"]')
cy.get(
'[row-id="02eceaba4df2bef76ea10caf728d8a099a2aa846cced25737cccaa9812342f65-market-2"]'
)
.eq(1)
.within(() => {
emptyCells.forEach((cell) => {
@@ -103,9 +124,11 @@ describe('positions', { tags: '@smoke', testIsolation: true }, () => {
const marketsSortedDefault = [
'ACTIVE MARKET',
'Apple Monthly (30 Jun 2022)',
'SUSPENDED MARKET',
];
const marketsSortedAsc = ['ACTIVE MARKET', 'Apple Monthly (30 Jun 2022)'];
const marketsSortedDesc = [
'SUSPENDED MARKET',
'Apple Monthly (30 Jun 2022)',
'ACTIVE MARKET',
];
@@ -119,9 +142,13 @@ describe('positions', { tags: '@smoke', testIsolation: true }, () => {
});
it('sorting by notional', () => {
cy.visit('/#/markets/market-0');
const marketsSortedDefault = ['276,761.40348', '46,126.90058'];
const marketsSortedAsc = ['46,126.90058', '276,761.40348'];
const marketsSortedDesc = ['276,761.40348', '46,126.90058'];
const marketsSortedDefault = [
'276,761.40348',
'46,126.90058',
'1,688.20',
];
const marketsSortedAsc = ['1,688.20', '46,126.90058', '276,761.40348'];
const marketsSortedDesc = ['276,761.40348', '46,126.90058', '1,688.20'];
cy.getByTestId('Positions').click();
checkSorting(
'notional',
@@ -132,9 +159,9 @@ describe('positions', { tags: '@smoke', testIsolation: true }, () => {
});
it('sorting by unrealisedPNL', () => {
cy.visit('/#/markets/market-0');
const marketsSortedDefault = ['8.95', '-0.22519'];
const marketsSortedAsc = ['-0.22519', '8.95'];
const marketsSortedDesc = ['8.95', '-0.22519'];
const marketsSortedDefault = ['8.95', '-0.22519', '8.95'];
const marketsSortedAsc = ['-0.22519', '8.95', '8.95'];
const marketsSortedDesc = ['8.95', '8.95', '-0.22519'];
cy.getByTestId('Positions').click();
checkSorting(
'unrealisedPNL',
@@ -145,7 +172,7 @@ describe('positions', { tags: '@smoke', testIsolation: true }, () => {
});
});
function validatePositionsDisplayed() {
function validatePositionsDisplayed(multiKey = false) {
cy.getByTestId('tab-positions').should('be.visible');
cy.getByTestId('tab-positions').within(() => {
cy.get('[col-id="marketName"]')
@@ -165,10 +192,11 @@ describe('positions', { tags: '@smoke', testIsolation: true }, () => {
cy.wrap($prices).invoke('text').should('not.be.empty');
});
cy.get('[col-id="currentLeverage"]').should('contain.text', '2.846.1');
cy.get('[col-id="marginAccountBalance"]') // margin allocated
.should('contain.text', '0.01');
if (!multiKey) {
cy.get('[col-id="currentLeverage"]').should('contain.text', '2.846.1');
cy.get('[col-id="marginAccountBalance"]') // margin allocated
.should('contain.text', '0.01');
}
cy.get('[col-id="unrealisedPNL"]').each(($unrealisedPnl) => {
cy.wrap($unrealisedPnl).invoke('text').should('not.be.empty');
@@ -184,6 +212,6 @@ describe('positions', { tags: '@smoke', testIsolation: true }, () => {
cy.get('.ag-popup').should('contain.text', 'Mark price x open volume');
});
cy.getByTestId('close-position').should('be.visible').and('have.length', 2);
cy.getByTestId('close-position').should('be.visible').and('have.length', 3);
}
});
-5
View File
@@ -9,8 +9,3 @@ export const selectAsset = (assetIndex: number) => {
// eslint-disable-next-line
cy.wait(100);
};
export const closeWelcomeDialog = () => {
cy.getByTestId('select-market-list').should('exist');
cy.getByTestId('dialog-close').click();
};
+1 -1
View File
@@ -85,7 +85,7 @@ const mockTradingPage = (
trigger?: Schema.AuctionTrigger
) => {
aliasGQLQuery(req, 'ChainId', chainIdQuery());
aliasGQLQuery(req, 'Statistics', statisticsQuery());
aliasGQLQuery(req, 'NodeCheck', statisticsQuery());
aliasGQLQuery(req, 'NodeGuard', nodeGuardQuery());
aliasGQLQuery(
req,
+1 -2
View File
@@ -6,11 +6,10 @@ NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/m
NX_VEGA_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/networks-internal/main/stagnet1/vegawallet-stagnet1.toml
NX_VEGA_ENV=STAGNET1
NX_VEGA_EXPLORER_URL=https://explorer.stagnet1.vega.rocks
NX_VEGA_NETWORKS={\"MAINNET\":\"https://vega.trading\",\"TESTNET\":\"https://console.fairground.wtf\",\"STAGNET1\":\"https://trading.stagnet1.vega.rocks\"}
NX_VEGA_NETWORKS={\"MAINNET\":\"https://console.vega.xyz\",\"TESTNET\":\"https://console.fairground.wtf\",\"STAGNET1\":\"https://trading.stagnet1.vega.rocks\"}
NX_VEGA_TOKEN_URL=https://governance.fairground.wtf
NX_VEGA_WALLET_URL=http://localhost:1789
NX_VEGA_DOCS_URL=https://docs.vega.xyz/testnet
NX_VEGA_REPO_URL=https://github.com/vegaprotocol/vega/releases
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/main/announcements.json
NX_WALLETCONNECT_PROJECT_ID=fe8091dc35738863e509fc4947525c72
+1 -1
View File
@@ -5,7 +5,7 @@ NX_HOSTED_WALLET_URL=https://wallet.testnet.vega.xyz
NX_VEGA_CONFIG_URL=''
NX_VEGA_ENV=CUSTOM
NX_VEGA_EXPLORER_URL=https://explorer.stagnet1.vega.rocks
NX_VEGA_NETWORKS={\"TESTNET\":\"https://console.fairground.wtf\",\"STAGNET1\":\"https://trading.stagnet1.vega.rocks\"}
NX_VEGA_NETWORKS={\"MAINNET\":\"https://console.vega.xyz\",\"TESTNET\":\"https://console.fairground.wtf\",\"STAGNET1\":\"https://trading.stagnet1.vega.rocks\"}
NX_VEGA_TOKEN_URL=https://governance.fairground.wtf
NX_VEGA_URL=http://localhost:3008/graphql
NX_VEGA_WALLET_URL=http://localhost:1789
+1 -1
View File
@@ -6,7 +6,7 @@ NX_TRADING_SENTRY_DSN=https://2ffce43721964aafa78277c50654ece4@o286262.ingest.se
NX_VEGA_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/networks-internal/main/devnet1/vegawallet-devnet1.toml
NX_VEGA_ENV=DEVNET
NX_VEGA_EXPLORER_URL=https://dev.explorer.vega.xyz
NX_VEGA_NETWORKS={\"TESTNET\":\"https://console.fairground.wtf\",\"STAGNET1\":\"https://trading.stagnet1.vega.rocks\"}
NX_VEGA_NETWORKS={\"MAINNET\":\"https://console.vega.xyz\",\"TESTNET\":\"https://console.fairground.wtf\",\"STAGNET1\":\"https://trading.stagnet1.vega.rocks\"}
NX_VEGA_TOKEN_URL=https://governance.fairground.wtf
NX_VEGA_WALLET_URL=http://localhost:1789
NX_VEGA_DOCS_URL=https://docs.vega.xyz/testnet
+4 -2
View File
@@ -5,11 +5,13 @@ NX_TRADING_SENTRY_DSN=https://2ffce43721964aafa78277c50654ece4@o286262.ingest.se
NX_VEGA_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/networks/master/mainnet1/mainnet1.toml
NX_VEGA_ENV=MAINNET
NX_VEGA_EXPLORER_URL=https://explorer.vega.xyz
NX_VEGA_NETWORKS={\"MAINNET\":\"https://vega.trading\",\"TESTNET\":\"https://console.fairground.wtf\",\"STAGNET1\":\"https://trading.stagnet1.vega.rocks\"}
NX_VEGA_NETWORKS={\"MAINNET\":\"https://console.vega.xyz\",\"TESTNET\":\"https://console.fairground.wtf\",\"STAGNET1\":\"https://trading.stagnet1.vega.rocks\"}
NX_VEGA_TOKEN_URL=https://governance.vega.xyz
NX_VEGA_WALLET_URL=http://localhost:1789
NX_VEGA_DOCS_URL=https://docs.vega.xyz/mainnet
NX_VEGA_REPO_URL=https://github.com/vegaprotocol/vega/releases
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/mainnet/announcements.json
NX_VEGA_INCIDENT_URL=https://blog.vega.xyz/tagged/vega-incident-reports
NX_VEGA_CONSOLE_URL=https://vega.trading
NX_VEGA_CONSOLE_URL=https://console.vega.xyz
# TAG name of the current app version - TODO: bump to the latest upon release
NX_APP_VERSION=v0.20.14-core-0.71.4
+1 -1
View File
@@ -6,7 +6,7 @@ NX_TRADING_SENTRY_DSN=https://2ffce43721964aafa78277c50654ece4@o286262.ingest.se
NX_VEGA_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/networks-internal/main/stagnet1/vegawallet-stagnet1.toml
NX_VEGA_ENV=STAGNET1
NX_VEGA_EXPLORER_URL=https://explorer.stagnet1.vega.rocks
NX_VEGA_NETWORKS={\"TESTNET\":\"https://console.fairground.wtf\",\"STAGNET1\":\"https://trading.stagnet1.vega.rocks\"}
NX_VEGA_NETWORKS={\"MAINNET\":\"https://console.vega.xyz\",\"TESTNET\":\"https://console.fairground.wtf\",\"STAGNET1\":\"https://trading.stagnet1.vega.rocks\"}
NX_VEGA_TOKEN_URL=https://governance.stagnet1.vega.rocks
NX_VEGA_WALLET_URL=http://localhost:1789
NX_VEGA_DOCS_URL=https://docs.vega.xyz/testnet
+1 -1
View File
@@ -6,7 +6,7 @@ NX_TRADING_SENTRY_DSN=https://2ffce43721964aafa78277c50654ece4@o286262.ingest.se
NX_VEGA_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/networks-internal/main/fairground/vegawallet-fairground.toml
NX_VEGA_ENV=TESTNET
NX_VEGA_EXPLORER_URL=https://explorer.fairground.wtf
NX_VEGA_NETWORKS={\"TESTNET\":\"https://console.fairground.wtf\",\"STAGNET1\":\"https://trading.stagnet1.vega.rocks\"}
NX_VEGA_NETWORKS={\"MAINNET\":\"https://console.vega.xyz\",\"TESTNET\":\"https://console.fairground.wtf\",\"STAGNET1\":\"https://trading.stagnet1.vega.rocks\"}
NX_VEGA_TOKEN_URL=https://governance.fairground.wtf
NX_VEGA_WALLET_URL=http://localhost:1789
NX_VEGA_DOCS_URL=https://docs.vega.xyz/testnet
+1 -1
View File
@@ -6,7 +6,7 @@ NX_TRADING_SENTRY_DSN=https://2ffce43721964aafa78277c50654ece4@o286262.ingest.se
NX_VEGA_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/networks/master/testnet2/testnet2.toml
NX_VEGA_ENV=VALIDATOR_TESTNET
NX_VEGA_EXPLORER_URL=https://explorer.validators-testnet.vega.rocks
NX_VEGA_NETWORKS={\"STAGNET1\":\"https://trading.stagnet1.vega.rocks\",\"TESTNET\":\"https://console.fairground.wtf\"}
NX_VEGA_NETWORKS={\"MAINNET\":\"https://console.vega.xyz\",\"STAGNET1\":\"https://trading.stagnet1.vega.rocks\",\"TESTNET\":\"https://console.fairground.wtf\"}
NX_VEGA_TOKEN_URL=https://governance.validators-testnet.vega.rocks
NX_VEGA_WALLET_URL=http://localhost:1789
NX_VEGA_DOCS_URL=https://docs.vega.xyz/testnet
+2 -2
View File
@@ -9,7 +9,7 @@ import { useGlobalStore } from '../../stores';
export const Home = () => {
const navigate = useNavigate();
// The default market selected in the platform behind the overlay
// should be the oldest market that is currently trading in us mode(i.e. not in auction).
// should be the oldest market that is currently trading in continuous mode(i.e. not in auction).
const { data, error, loading } = useDataProvider({
dataProvider: marketsWithDataProvider,
variables: undefined,
@@ -30,7 +30,7 @@ export const Home = () => {
replace: true,
});
} else {
navigate(Links[Routes.MARKET]());
navigate(Links[Routes.MARKETS]());
}
}
}, [marketId, data, navigate, update]);
@@ -56,13 +56,7 @@ export const HeaderStats = ({ market }: HeaderStatsProps) => {
decimalPlaces={market?.decimalPlaces}
/>
</HeaderStat>
<HeaderStat
heading={t('Volume (24h)')}
testId="market-volume"
description={t(
'The total number of contracts traded in the last 24 hours.'
)}
>
<HeaderStat heading={t('Volume (24h)')} testId="market-volume">
<Last24hVolume
marketId={market?.id}
positionDecimalPlaces={market?.positionDecimalPlaces}
@@ -22,7 +22,19 @@ describe('MarketSelectorItem', () => {
id: 'market-0',
decimalPlaces: 2,
// @ts-ignore fragment doesn't contain candles
candles: [{ close: '5' }, { close: '10' }],
candles: [
{ close: '5', volume: '50' },
{ close: '10', volume: '50' },
],
tradableInstrument: {
instrument: {
product: {
settlementAsset: {
symbol: 'SYM',
},
},
},
},
});
const marketData: MarketDataUpdateFieldsFragment = {
__typename: 'ObservableMarketData',
@@ -83,6 +95,9 @@ describe('MarketSelectorItem', () => {
};
it('renders market information', async () => {
const symbol =
market.tradableInstrument.instrument.product.settlementAsset.symbol;
renderJsx();
const link = screen.getByRole('link');
@@ -91,7 +106,8 @@ describe('MarketSelectorItem', () => {
expect(link).toHaveClass('ring-1');
expect(screen.getByTestId('market-item-price')).toHaveTextContent('-');
expect(screen.getByTitle('24h vol')).toHaveTextContent('100');
expect(screen.getByTitle(symbol)).toHaveTextContent('-');
// candles are loaded immediately
expect(screen.getByTestId('market-item-change')).toHaveTextContent(
@@ -99,7 +115,7 @@ describe('MarketSelectorItem', () => {
);
await waitFor(() => {
expect(screen.getByTestId('market-item-price')).toHaveTextContent(
expect(screen.getByTitle(symbol)).toHaveTextContent(
addDecimalsFormatNumber(marketData.markPrice, market.decimalPlaces)
);
});
@@ -7,8 +7,14 @@ import {
priceChangePercentage,
} from '@vegaprotocol/utils';
import type { MarketMaybeWithDataAndCandles } from '@vegaprotocol/markets';
import { calcCandleVolume } from '@vegaprotocol/markets';
import { useMarketDataUpdateSubscription } from '@vegaprotocol/markets';
import { Sparkline } from '@vegaprotocol/ui-toolkit';
import {
MarketTradingMode,
MarketTradingModeMapping,
} from '@vegaprotocol/types';
import { t } from '@vegaprotocol/i18n';
export const MarketSelectorItem = ({
market,
@@ -38,13 +44,6 @@ export const MarketSelectorItem = ({
onSelect && onSelect(market.id);
}}
>
<h3>{market.tradableInstrument.instrument.code}</h3>
<h4
title={market.tradableInstrument.instrument.name}
className="text-sm text-vega-light-300 dark:text-vega-dark-300 text-ellipsis whitespace-nowrap overflow-hidden"
>
{market.tradableInstrument.instrument.name}
</h4>
<MarketData market={market} />
</Link>
</div>
@@ -68,30 +67,84 @@ const MarketData = ({ market }: { market: MarketMaybeWithDataAndCandles }) => {
? addDecimalsFormatNumber(market.data.markPrice, market.decimalPlaces)
: '-';
const marketTradingMode = marketData
? marketData.marketTradingMode
: market.tradingMode;
const mode = [
MarketTradingMode.TRADING_MODE_BATCH_AUCTION,
MarketTradingMode.TRADING_MODE_MONITORING_AUCTION,
MarketTradingMode.TRADING_MODE_OPENING_AUCTION,
].includes(marketTradingMode)
? MarketTradingModeMapping[marketTradingMode]
: '';
const instrument = market.tradableInstrument.instrument;
const vol = market.candles ? calcCandleVolume(market.candles) : '0';
const volume =
vol && vol !== '0'
? addDecimalsFormatNumber(vol, market.positionDecimalPlaces)
: '0.00';
return (
<div className="flex flex-nowrap justify-between items-center mt-1">
<div className="w-1/2">
<div
className="text-ellipsis whitespace-nowrap overflow-hidden"
data-testid="market-item-price"
<>
<div className="flex items-end gap-1 mb-1">
<h3
className={classNames(
'overflow-hidden text-ellipsis whitespace-nowrap',
{
'w-1/2': mode, // make space for showing the trading mode
}
)}
>
{price}
</div>
{market.tradableInstrument.instrument.code}
</h3>
{mode && (
<p className="w-1/2 text-xs text-right text-vega-orange-500 dark:text-vega-orange-550">
{mode}
</p>
)}
</div>
<DataRow value={volume} label={t('24h vol')} />
<DataRow
value={price}
label={instrument.product.settlementAsset.symbol}
/>
<div className="relative">
{market.candles && (
<PriceChange candles={market.candles.map((c) => c.close)} />
)}
<div
// absolute so height is not larger than price change value
className="absolute right-0 bottom-0 w-[120px]"
>
{market.candles && (
<Sparkline
width={120}
height={20}
data={market.candles.filter(Boolean).map((c) => Number(c.close))}
/>
)}
</div>
</div>
<div className="w-1/2 max-w-[120px]">
{market.candles ? (
<Sparkline
width={120}
height={20}
data={market.candles.filter(Boolean).map((c) => Number(c.close))}
/>
) : (
'-'
)}
</div>
</>
);
};
const DataRow = ({ value, label }: { value: string; label: string }) => {
return (
<div
className="text-ellipsis whitespace-nowrap overflow-hidden leading-tight"
data-testid="market-selector-data-row"
>
<span title={label} className="text-sm mr-1">
{value}
</span>
<span className="text-xs text-vega-light-300 dark:text-vega-light-300">
{label}
</span>
</div>
);
};
@@ -2,8 +2,8 @@ import classNames from 'classnames';
// Make sure these match the available __typename properties on product
export const Product = {
Spot: 'Spot',
Future: 'Future',
Spot: 'Spot',
Perpetual: 'Perpetual',
} as const;
@@ -12,8 +12,8 @@ export type ProductType = keyof typeof Product;
const ProductTypeMapping: {
[key in ProductType]: string;
} = {
[Product.Spot]: 'Spot',
[Product.Future]: 'Futures',
[Product.Spot]: 'Spot',
[Product.Perpetual]: 'Perpetuals',
};
+17 -10
View File
@@ -339,24 +339,31 @@ export const TradeGrid = ({ market, pinnedAsset }: TradeGridProps) => {
return (
<div className={wrapperClasses}>
<div className="border-b border-r border-default">
<div className="flex gap-2 justify-between items-center px-4 py-2">
<div className="h-full flex gap-2 justify-between items-end px-4 pt-1 pb-3">
<HeaderTitle
primaryContent={market?.tradableInstrument.instrument.code}
secondaryContent={market?.tradableInstrument.instrument.name}
/>
<button
onClick={() => setSidebarOpen((x) => !x)}
className="p-2"
className="flex flex-col items-center text-xs w-12"
data-testid="sidebar-toggle"
>
<span
className={classNames('block', {
'rotate-90 translate-x-1': !sidebarOpen,
'-rotate-90 -translate-x-1': sidebarOpen,
})}
>
<VegaIcon name={VegaIconNames.CHEVRON_UP} />
</span>
{sidebarOpen ? (
<>
<VegaIcon name={VegaIconNames.CHEVRON_UP} />
<span className="text-vega-light-300 dark:text-vega-dark-300">
{t('Close')}
</span>
</>
) : (
<>
<VegaIcon name={VegaIconNames.CHEVRON_DOWN} />
<span className="text-vega-light-300 dark:text-vega-dark-300">
{t('Markets')}
</span>
</>
)}
</button>
</div>
</div>
@@ -315,39 +315,47 @@ describe('useMarketSelectorList', () => {
]);
});
it('sorts albphabetically', () => {
it('sorts by state and volume by default', () => {
const markets = [
createMarketFragment({
id: 'market-0',
tradableInstrument: {
instrument: {
code: 'd',
state: MarketState.STATE_PENDING,
// @ts-ignore candles not on fragment
candles: [
{
volume: '200',
},
},
],
}),
createMarketFragment({
id: 'market-1',
tradableInstrument: {
instrument: {
code: 'b',
state: MarketState.STATE_ACTIVE,
// @ts-ignore candles not on fragment
candles: [
{
volume: '200',
},
},
],
}),
createMarketFragment({
id: 'market-2',
tradableInstrument: {
instrument: {
code: 'a',
state: MarketState.STATE_ACTIVE,
// @ts-ignore candles not on fragment
candles: [
{
volume: '100',
},
},
],
}),
createMarketFragment({
state: MarketState.STATE_PENDING,
id: 'market-3',
tradableInstrument: {
instrument: {
code: 'c',
// @ts-ignore candles not on fragment
candles: [
{
volume: '100',
},
},
],
}),
];
@@ -364,10 +372,10 @@ describe('useMarketSelectorList', () => {
assets: [],
});
expect(result.current.markets).toEqual([
markets[2],
markets[1],
markets[3],
markets[2],
markets[0],
markets[3],
]);
});
@@ -1,11 +1,18 @@
import { useMemo } from 'react';
import orderBy from 'lodash/orderBy';
import { MarketState } from '@vegaprotocol/types';
import { useMarketList } from '@vegaprotocol/markets';
import { calcCandleVolume, useMarketList } from '@vegaprotocol/markets';
import { priceChangePercentage } from '@vegaprotocol/utils';
import type { Filter } from './market-selector';
import { Sort } from './sort-dropdown';
// Used for sort order and filter
const MARKET_TEMPLATE = [
MarketState.STATE_ACTIVE,
MarketState.STATE_SUSPENDED,
MarketState.STATE_PENDING,
];
export const useMarketSelectorList = ({
product,
assets,
@@ -46,7 +53,19 @@ export const useMarketSelectorList = ({
});
if (sort === Sort.None) {
return orderBy(markets, ['tradableInstrument.instrument.code'], ['asc']);
// Sort by market state primarilly and AtoZ secondarilly
return orderBy(
markets,
[
(m) => MARKET_TEMPLATE.indexOf(m.state),
(m) => {
if (!m.candles?.length) return 0;
const vol = calcCandleVolume(m.candles);
return Number(vol || 0);
},
],
['asc', 'desc']
);
}
if (sort === Sort.Gained || sort === Sort.Lost) {
@@ -78,9 +97,5 @@ export const useMarketSelectorList = ({
};
export const isMarketActive = (state: MarketState) => {
return [
MarketState.STATE_ACTIVE,
MarketState.STATE_SUSPENDED,
MarketState.STATE_PENDING,
].includes(state);
return MARKET_TEMPLATE.includes(state);
};
@@ -175,6 +175,10 @@ describe('Closed', () => {
__typename: 'Market',
id: marketId,
},
party: {
__typename: 'Party',
id: pubKey,
},
};
};
const position = createPosition();
@@ -182,18 +186,14 @@ describe('Closed', () => {
request: {
query: PositionsDocument,
variables: {
partyId: pubKey,
partyIds: [pubKey],
},
},
result: {
data: {
party: {
__typename: 'Party',
id: pubKey,
positionsConnection: {
__typename: 'PositionConnection',
edges: [{ __typename: 'PositionEdge', node: position }],
},
positions: {
__typename: 'PositionConnection',
edges: [{ __typename: 'PositionEdge', node: position }],
},
},
},
+5 -6
View File
@@ -64,7 +64,7 @@ export const Closed = () => {
});
const { data: positionData } = usePositionsQuery({
variables: {
partyId: pubKey || '',
partyIds: pubKey ? [pubKey] : [],
},
skip: !pubKey,
});
@@ -72,11 +72,9 @@ export const Closed = () => {
// find a position for each market and add the realised pnl to
// a normalized object
const rowData = compact(marketData).map((market) => {
const position = positionData?.party?.positionsConnection?.edges?.find(
(edge) => {
return edge.node.market.id === market.id;
}
);
const position = positionData?.positions?.edges?.find((edge) => {
return edge.node.market.id === market.id;
});
const instrument = market.tradableInstrument.instrument;
@@ -315,6 +313,7 @@ const ClosedMarketsDataGrid = ({ rowData }: { rowData: Row[] }) => {
getRowId={({ data }) => data.id}
defaultColDef={{
resizable: true,
minWidth: 100,
}}
overlayNoRowsTemplate="No data"
storeKey="closedMarkets"
@@ -54,6 +54,7 @@ export const Portfolio = () => {
onMarketClick={onMarketClick}
noBottomPlaceholder
storeKey="portfolioPositions"
allKeys
/>
</VegaWalletContainer>
</Tab>
@@ -89,6 +89,9 @@ const cacheConfig: InMemoryCacheConfig = {
Party: {
keyFields: false,
},
Position: {
keyFields: ['market', ['id'], 'party', ['id']],
},
Fees: {
keyFields: false,
},
+1
View File
@@ -1 +1,2 @@
export * from './announcement-banner';
export * from './upgrade-banner';
@@ -0,0 +1,84 @@
import { useMemo, useState } from 'react';
import { gt, prerelease } from 'semver';
import {
ReleasesFeed,
useEnvironment,
useReleases,
Networks,
} from '@vegaprotocol/environment';
import { t } from '@vegaprotocol/i18n';
import {
CopyWithTooltip,
ExternalLink,
Intent,
NotificationBanner,
VegaIcon,
VegaIconNames,
} from '@vegaprotocol/ui-toolkit';
// v0.20.12-core-0.71.4 -> v0.20.12
// we need to strip the "core" suffix in order to determine whether a release
// is a pre-release (candidate); example: v.0.21.0-beta.1-core-0.71.4
const parseTagName = (tagName: string) => tagName.replace(/-core-[\d.]+$/i, '');
type UpgradeBannerProps = {
showVersionChange: boolean;
};
export const UpgradeBanner = ({ showVersionChange }: UpgradeBannerProps) => {
const [visible, setVisible] = useState(true);
const { data } = useReleases(ReleasesFeed.FrontEnd);
const { APP_VERSION, VEGA_ENV, VEGA_NETWORKS } = useEnvironment();
const CANONICAL_URL = VEGA_NETWORKS[VEGA_ENV] || 'https://console.vega.xyz';
const newest = useMemo(() => {
if (!APP_VERSION || !data) return undefined;
const newer = data.filter((r) => gt(r.tagName, APP_VERSION));
const valid =
// filter pre-releases on mainnet
VEGA_ENV === Networks.MAINNET
? newer?.filter((r) => !prerelease(parseTagName(r.tagName)))
: newer;
return valid.sort((a, b) => (gt(a.tagName, b.tagName) ? -1 : 1))[0];
}, [APP_VERSION, VEGA_ENV, data]);
if (!visible || !newest) {
return null;
}
return (
<NotificationBanner
intent={Intent.Warning}
onClose={() => {
setVisible(false);
}}
>
<div className="uppercase mb-1">
<ExternalLink href={CANONICAL_URL}>
{t('Upgrade to the latest version of Console')}
</ExternalLink>
</div>
<div data-testid="bookmark-message">
<a
className="underline"
href={newest.htmlUrl}
rel="noreferrer nofollow noopener"
target="_blank"
>
{t("View what's changed")}
</a>{' '}
{t(' or bookmark')}{' '}
<a className="underline" href={CANONICAL_URL}>
{t('console.vega.xyz')}
</a>{' '}
<CopyWithTooltip text={CANONICAL_URL}>
<button title={t('Copy %s', CANONICAL_URL)}>
<span className="sr-only">{t('Copy %s', CANONICAL_URL)}</span>
<VegaIcon size={14} name={VegaIconNames.COPY} />
</button>
</CopyWithTooltip>{' '}
{'to always see the latest version.'}
</div>
</NotificationBanner>
);
};
+1
View File
@@ -1,4 +1,5 @@
import { t } from '@vegaprotocol/i18n';
export const THROTTLE_UPDATE_TIME = 500;
export const RISK_ACCEPTED_KEY = 'vega_risk_accepted';
export const MAINNET_WELCOME_HEADER = t(
@@ -36,11 +36,12 @@ describe('NodeHealth', () => {
describe('NodeUrl', () => {
it('renders correct part of node url', () => {
const node = 'https://api.n99.somenetwork.vega.xyz';
const expectedText = node.split('.').slice(1).join('.');
render(<NodeUrl url={node} />);
expect(screen.getByText(expectedText)).toBeInTheDocument();
expect(
screen.getByText('api.n99.somenetwork.vega.xyz')
).toBeInTheDocument();
});
});
+1 -2
View File
@@ -65,9 +65,8 @@ interface NodeUrlProps {
}
export const NodeUrl = ({ url }: NodeUrlProps) => {
// get base url from api url, api sub domain
const urlObj = new URL(url);
const nodeUrl = urlObj.origin.replace(/^[^.]+\./g, '');
const nodeUrl = urlObj.hostname;
return <span title={t('Connected node')}>{nodeUrl}</span>;
};
+2 -2
View File
@@ -73,10 +73,10 @@ export const HeaderTitle = ({
}) => {
return (
<div className="text-left" data-testid="header-title">
<div className="text-sm md:text-md lg:text-lg whitespace-nowrap leading-4">
<div className="text-sm md:text-md lg:text-lg whitespace-nowrap !leading-[1]">
{primaryContent}
</div>
<div className="text-xs whitespace-nowrap text-neutral-500 dark:text-neutral-400">
<div className="text-xs whitespace-nowrap text-vega-light-300 dark:text-vega-dark-300">
{secondaryContent}
</div>
</div>
+1 -6
View File
@@ -4,15 +4,10 @@ export const WalletIcon = ({ className }: { className?: string }) => {
width="26"
height="18"
viewBox="0 0 26 18"
fill="none"
xmlns="http://www.w3.org/2000/svg"
className={className}
data-testid="wallet-icon"
>
<path
d="M4.77437 17.7499H4.74987C3.6504 17.7368 2.77439 16.8489 2.77439 15.7772V12.8495V12.6343L2.5615 12.6023C1.59672 12.4575 0.849609 11.6116 0.849609 10.6266V7.40064C0.849609 6.39018 1.59509 5.56985 2.56147 5.4249L2.77439 5.39297V5.17767V2.24998C2.77439 1.14102 3.66537 0.25 4.77437 0.25H23.7501C24.8591 0.25 25.7501 1.14098 25.7501 2.24998V15.7499C25.7501 16.8588 24.8591 17.7499 23.7501 17.7499H4.77437ZM4.44917 12.5992H4.19917L4.77441 16.075V16.325H4.77466H23.7502C24.0778 16.325 24.3254 16.0777 24.3254 15.7497V2.24984C24.3254 1.9222 24.0782 1.6746 23.7502 1.6746H4.77441C4.44677 1.6746 4.19917 1.92182 4.19917 2.24984V5.12306V5.37306H4.44917H7.0244C8.51139 5.37306 9.67508 6.56094 9.67508 8.02374V9.94852C9.67508 11.4355 8.4872 12.5992 7.0244 12.5992H4.44917ZM2.84963 6.8253C2.52199 6.8253 2.27439 7.07253 2.27439 7.40054V10.6264C2.27439 10.9541 2.52161 11.2017 2.84962 11.2017L7.02419 11.2019C7.73619 11.2019 8.25009 10.6515 8.25009 9.97598V8.0512C8.25009 7.3392 7.69976 6.8253 7.0242 6.8253H2.84963Z"
stroke="none"
/>
<path d="M4.77437 17.7499H4.74987C3.6504 17.7368 2.77439 16.8489 2.77439 15.7772V12.8495V12.6343L2.5615 12.6023C1.59672 12.4575 0.849609 11.6116 0.849609 10.6266V7.40064C0.849609 6.39018 1.59509 5.56985 2.56147 5.4249L2.77439 5.39297V5.17767V2.24998C2.77439 1.14102 3.66537 0.25 4.77437 0.25H23.7501C24.8591 0.25 25.7501 1.14098 25.7501 2.24998V15.7499C25.7501 16.8588 24.8591 17.7499 23.7501 17.7499H4.77437ZM4.44917 12.5992H4.19917L4.77441 16.075V16.325H4.77466H23.7502C24.0778 16.325 24.3254 16.0777 24.3254 15.7497V2.24984C24.3254 1.9222 24.0782 1.6746 23.7502 1.6746H4.77441C4.44677 1.6746 4.19917 1.92182 4.19917 2.24984V5.12306V5.37306H4.44917H7.0244C8.51139 5.37306 9.67508 6.56094 9.67508 8.02374V9.94852C9.67508 11.4355 8.4872 12.5992 7.0244 12.5992H4.44917ZM2.84963 6.8253C2.52199 6.8253 2.27439 7.07253 2.27439 7.40054V10.6264C2.27439 10.9541 2.52161 11.2017 2.84962 11.2017L7.02419 11.2019C7.73619 11.2019 8.25009 10.6515 8.25009 9.97598V8.0512C8.25009 7.3392 7.69976 6.8253 7.0242 6.8253H2.84963Z" />
</svg>
);
};
@@ -26,7 +26,7 @@ export const MarketMarkPrice = ({
{
dataProvider: marketDataProvider,
variables: { marketId: marketId || '' },
skip: !inView,
skip: !inView || !marketId,
},
THROTTLE_UPDATE_TIME
);
+3 -2
View File
@@ -16,10 +16,11 @@ import {
NavigationItem,
NavigationLink,
ExternalLink,
Icon,
NavigationBreakpoint,
NavigationTrigger,
NavigationContent,
VegaIconNames,
VegaIcon,
} from '@vegaprotocol/ui-toolkit';
import { Links, Routes } from '../../pages/client-router';
@@ -146,7 +147,7 @@ const NavExternalLink = ({
<ExternalLink href={href}>
<span className="flex items-center gap-2">
<span>{children}</span>
<Icon name="arrow-top-right" size={3} />
<VegaIcon name={VegaIconNames.OPEN_EXTERNAL} />
</span>
</ExternalLink>
);
@@ -1,2 +0,0 @@
export * from './select-market-columns';
export * from './select-market-table';
@@ -1,344 +0,0 @@
import type { RefObject, MouseEvent } from 'react';
import {
FeesCell,
calcCandleHigh,
calcCandleLow,
calcCandleVolume,
Last24hPriceChange,
Last24hVolume,
} from '@vegaprotocol/markets';
import { addDecimalsFormatNumber } from '@vegaprotocol/utils';
import { t } from '@vegaprotocol/i18n';
import { PriceCell } from '@vegaprotocol/datagrid';
import { Link as UILink, Sparkline, Tooltip } from '@vegaprotocol/ui-toolkit';
import isNil from 'lodash/isNil';
import type { CandleClose } from '@vegaprotocol/types';
import type { MarketMaybeWithDataAndCandles } from '@vegaprotocol/markets';
import { Link } from 'react-router-dom';
import { MarketMarkPrice } from '../market-mark-price';
import { MarketTradingMode } from '../market-trading-mode';
import { Links, Routes } from '../../pages/client-router';
const ellipsisClasses = 'whitespace-nowrap overflow-hidden text-ellipsis';
export const cellClassNames = `py-1 first:text-left text-right ${ellipsisClasses}`;
const FeesInfo = () => {
return (
<Tooltip
description={
<span>
{t(
'Fees are paid by market takers on aggressive orders only. The fee displayed is made up of:'
)}
<ul className="list-disc ml-4">
<li>{t('An infrastructure fee')}</li>
<li>{t('A liquidity provision fee')}</li>
<li>{t('A maker fee')}</li>
</ul>
</span>
}
>
<span>{t('Taker fee')}</span>
</Tooltip>
);
};
export enum ColumnKind {
Market,
LastPrice,
Change24,
Asset,
ProductType,
Sparkline,
High24,
Low24,
TradingMode,
Volume,
Fee,
Position,
FullName,
}
export interface Column {
kind: ColumnKind;
value: string | React.ReactNode;
className: string;
onlyOnDetailed: boolean;
dataTestId?: string;
}
const headers: Column[] = [
{
kind: ColumnKind.Market,
value: t('Market'),
className: cellClassNames,
onlyOnDetailed: false,
},
{
kind: ColumnKind.ProductType,
value: t('Type'),
className: 'py-2 text-left hidden sm:table-cell',
onlyOnDetailed: false,
},
{
kind: ColumnKind.LastPrice,
value: t('Last price'),
className: cellClassNames,
onlyOnDetailed: false,
},
{
kind: ColumnKind.Change24,
value: t('Change (24h)'),
className: cellClassNames,
onlyOnDetailed: false,
},
{
kind: ColumnKind.Sparkline,
value: t(''),
className: `${cellClassNames} hidden lg:table-cell`,
onlyOnDetailed: false,
},
{
kind: ColumnKind.Asset,
value: t('Settlement asset'),
className: `${cellClassNames} hidden sm:table-cell`,
onlyOnDetailed: false,
},
{
kind: ColumnKind.High24,
value: t('24h High'),
className: `${cellClassNames} hidden xl:table-cell`,
onlyOnDetailed: true,
},
{
kind: ColumnKind.Low24,
value: t('24h Low'),
className: `${cellClassNames} hidden xl:table-cell`,
onlyOnDetailed: true,
},
{
kind: ColumnKind.Volume,
value: t('24h Volume'),
className: `${cellClassNames} hidden lg:table-cell`,
onlyOnDetailed: true,
},
{
kind: ColumnKind.TradingMode,
value: t('Trading mode'),
className: `${cellClassNames} hidden lg:table-cell`,
onlyOnDetailed: true,
},
{
kind: ColumnKind.Fee,
value: <FeesInfo />,
className: `${cellClassNames} hidden xl:table-cell`,
onlyOnDetailed: true,
},
];
export const columnHeadersPositionMarkets: Column[] = [
...headers,
{
kind: ColumnKind.Position,
value: t('Position'),
className: `${cellClassNames} hidden xxl:table-cell`,
onlyOnDetailed: true,
},
];
export const columnHeaders: Column[] = [
...headers,
{
kind: ColumnKind.FullName,
value: t('Full name'),
className: `${cellClassNames} hidden xxl:block`,
onlyOnDetailed: true,
},
];
export type OnCellClickHandler = (
e: MouseEvent,
kind: ColumnKind,
value: string
) => void;
export const columns = (
market: MarketMaybeWithDataAndCandles,
onSelect: (id: string, metaKey?: boolean) => void,
onCellClick: OnCellClickHandler,
inViewRoot?: RefObject<HTMLElement>
) => {
const candlesClose = market.candles
?.map((candle) => candle?.close)
.filter((c: string | undefined): c is CandleClose => !isNil(c));
const candleLow = market.candles && calcCandleLow(market.candles);
const candleHigh = market.candles && calcCandleHigh(market.candles);
const candleVolume = market.candles && calcCandleVolume(market.candles);
const selectMarketColumns: Column[] = [
{
kind: ColumnKind.Market,
value: (
<Link
to={Links[Routes.MARKET](market.id)}
data-testid={`market-link-${market.id}`}
onClick={(e) => {
e.preventDefault();
e.stopPropagation();
onSelect(market.id, e.metaKey || e.ctrlKey);
}}
>
<UILink>{market.tradableInstrument.instrument.code}</UILink>
</Link>
),
className: `${cellClassNames} max-w-[110px]`,
onlyOnDetailed: false,
},
{
kind: ColumnKind.ProductType,
value: market.tradableInstrument.instrument.product.__typename,
className: `py-2 text-left hidden sm:table-cell max-w-[50px] ${ellipsisClasses}`,
onlyOnDetailed: false,
},
{
kind: ColumnKind.LastPrice,
value: (
<MarketMarkPrice
marketId={market.id}
decimalPlaces={market?.decimalPlaces}
initialValue={market.data?.markPrice}
inViewRoot={inViewRoot}
asPriceCell
/>
),
className: `${cellClassNames} max-w-[100px]`,
onlyOnDetailed: false,
},
{
kind: ColumnKind.Change24,
value: (
<Last24hPriceChange
marketId={market.id}
decimalPlaces={market?.decimalPlaces}
inViewRoot={inViewRoot}
initialValue={candlesClose}
/>
),
className: `${cellClassNames} max-w-[150px]`,
onlyOnDetailed: false,
},
{
kind: ColumnKind.Sparkline,
value: market.candles && (
<Sparkline
width={100}
height={20}
muted={false}
data={candlesClose?.map((c: string) => Number(c)) || []}
/>
),
className: `${cellClassNames} hidden lg:table-cell max-w-[80px]`,
onlyOnDetailed: false && candlesClose,
},
{
kind: ColumnKind.Asset,
value: (
<button
data-dialog-trigger
className="inline underline"
onClick={(e) => {
e.stopPropagation();
onCellClick(
e,
ColumnKind.Asset,
market.tradableInstrument.instrument.product.settlementAsset.id
);
}}
>
{market.tradableInstrument.instrument.product.settlementAsset.symbol}
</button>
),
dataTestId: 'settlement-asset',
className: `${cellClassNames} hidden sm:table-cell max-w-[100px]`,
onlyOnDetailed: false,
},
{
kind: ColumnKind.High24,
value: candleHigh ? (
<PriceCell
value={Number(candleHigh)}
valueFormatted={addDecimalsFormatNumber(
candleHigh.toString(),
market.decimalPlaces,
2
)}
/>
) : (
'-'
),
className: `${cellClassNames} hidden xl:table-cell font-mono`,
onlyOnDetailed: true,
},
{
kind: ColumnKind.Low24,
value: candleLow ? (
<PriceCell
value={Number(candleLow)}
valueFormatted={addDecimalsFormatNumber(
candleLow.toString(),
market.decimalPlaces,
2
)}
/>
) : (
'-'
),
className: `${cellClassNames} hidden xl:table-cell font-mono`,
onlyOnDetailed: true,
},
{
kind: ColumnKind.Volume,
value: (
<Last24hVolume
marketId={market.id}
positionDecimalPlaces={market.positionDecimalPlaces}
initialValue={candleVolume}
inViewRoot={inViewRoot}
formatDecimals={2}
/>
),
className: `${cellClassNames} hidden lg:table-cell font-mono`,
onlyOnDetailed: true,
dataTestId: 'market-volume',
},
{
kind: ColumnKind.TradingMode,
value: (
<MarketTradingMode
marketId={market?.id}
inViewRoot={inViewRoot}
initialTradingMode={market.tradingMode}
initialTrigger={market.data?.trigger}
/>
),
className: `${cellClassNames} hidden lg:table-cell`,
onlyOnDetailed: true,
dataTestId: 'trading-mode-col',
},
{
kind: ColumnKind.Fee,
value: <FeesCell feeFactors={market.fees.factors} />,
className: `${cellClassNames} hidden xl:table-cell font-mono`,
onlyOnDetailed: true,
dataTestId: 'taker-fee',
},
{
kind: ColumnKind.FullName,
value: market.tradableInstrument.instrument.name,
className: `${cellClassNames} hidden xxl:block`,
onlyOnDetailed: true,
dataTestId: 'market-name',
},
];
return selectMarketColumns;
};
@@ -1,63 +0,0 @@
import { columnHeaders } from './select-market-columns';
import classNames from 'classnames';
import type { Column } from './select-market-columns';
export const SelectMarketTableHeader = ({
detailed = false,
headers = columnHeaders,
}) => {
return (
<tr className="sticky top-0 z-10 border-b border-default bg-inherit">
{headers.map(({ kind, value, className, onlyOnDetailed }) => {
const thClass = classNames(
'font-normal text-neutral-500 dark:text-neutral-400',
className
);
if (!onlyOnDetailed || detailed === onlyOnDetailed) {
return (
<th key={kind} className={thClass}>
{value}
</th>
);
}
return null;
})}
</tr>
);
};
export const SelectMarketTableRow = ({
detailed = false,
columns,
onSelect,
marketId,
}: {
detailed?: boolean;
columns: Column[];
onSelect: (id: string, metaKey?: boolean) => void;
marketId: string;
}) => {
return (
<tr
className={`hover:bg-neutral-200 dark:hover:bg-neutral-700 cursor-pointer relative h-[34px]`}
onClick={(ev) => {
onSelect(marketId, ev.metaKey || ev.ctrlKey);
}}
data-testid={`market-link-${marketId}`}
>
{columns.map(({ kind, value, className, dataTestId, onlyOnDetailed }) => {
if (!onlyOnDetailed || detailed === onlyOnDetailed) {
const tdClass = classNames(className);
return (
<td key={kind} data-testid={dataTestId} className={tdClass}>
{value}
</td>
);
}
return null;
})}
</tr>
);
};
@@ -58,7 +58,7 @@ const MobileWalletButton = ({
? 'hidden'
: isYellow
? 'fill-black'
: 'fill-black dark:fill-white';
: 'fill-white';
const [container, setContainer] = useState<HTMLElement | null>(null);
const walletButton = (
@@ -1,11 +0,0 @@
import { Networks, useEnvironment } from '@vegaprotocol/environment';
import * as constants from '../constants';
export const WelcomeDialogHeader = () => {
const { VEGA_ENV } = useEnvironment();
const header =
VEGA_ENV === Networks.MAINNET
? constants.MAINNET_WELCOME_HEADER
: constants.TESTNET_WELCOME_HEADER;
return <h1 className="mb-6 p-4 text-center text-2xl">{header}</h1>;
};
@@ -8,7 +8,6 @@ import { activeMarketsProvider } from '@vegaprotocol/markets';
import * as constants from '../constants';
import { RiskNoticeDialog } from './risk-notice-dialog';
import { WelcomeNoticeDialog } from './welcome-notice-dialog';
import { WelcomeLandingDialog } from './welcome-landing-dialog';
import { useGlobalStore } from '../../stores';
import { useEnvironment } from '@vegaprotocol/environment';
import { Networks } from '@vegaprotocol/environment';
@@ -43,6 +42,7 @@ export const WelcomeDialog = () => {
shouldDisplayWelcomeDialog: isRiskDialogNeeded,
});
}, [update, isRiskDialogNeeded]);
if (isRiskDialogNeeded) {
dialogContent = (
<RiskNoticeDialog onClose={onCloseDialog} network={VEGA_ENV} />
@@ -52,9 +52,6 @@ export const WelcomeDialog = () => {
} else if (isWelcomeDialogNeeded && data?.length === 0) {
dialogContent = <WelcomeNoticeDialog />;
onClose = onCloseDialog;
} else if (isWelcomeDialogNeeded && (data?.length || 0) > 0) {
dialogContent = <WelcomeLandingDialog onClose={onCloseDialog} />;
onClose = onCloseDialog;
} else {
dialogContent = null as React.ReactNode;
}
@@ -1,258 +0,0 @@
import { fireEvent, render, screen } from '@testing-library/react';
import { MemoryRouter } from 'react-router-dom';
import { MockedProvider } from '@apollo/client/testing';
import * as Schema from '@vegaprotocol/types';
import type {
MarketMaybeWithCandles,
MarketMaybeWithData,
MarketData,
} from '@vegaprotocol/markets';
import { SelectMarketLandingTable } from './welcome-landing-dialog';
const mockMarketClickHandler = jest.fn();
jest.mock('../../lib/hooks/use-market-click-handler', () => ({
useMarketClickHandler: () => mockMarketClickHandler,
}));
type Market = MarketMaybeWithCandles & MarketMaybeWithData;
type PartialMarket = Partial<
Omit<Market, 'data'> & { data: Partial<MarketData> }
>;
const MARKET_A: PartialMarket = {
__typename: 'Market',
id: '1',
decimalPlaces: 2,
tradingMode: Schema.MarketTradingMode.TRADING_MODE_CONTINUOUS,
tradableInstrument: {
__typename: 'TradableInstrument',
instrument: {
__typename: 'Instrument',
id: '1',
code: 'ABCDEF',
name: 'ABCDEF 1-Day',
product: {
__typename: 'Future',
quoteName: 'ABCDEF',
settlementAsset: {
__typename: 'Asset',
id: 'asset-ABC',
name: 'asset-ABC',
decimals: 2,
symbol: 'ABC',
},
dataSourceSpecForTradingTermination: {
__typename: 'DataSourceSpec',
id: 'oracleId',
data: {
__typename: 'DataSourceDefinition',
sourceType: {
__typename: 'DataSourceDefinitionExternal',
sourceType: {
__typename: 'DataSourceSpecConfiguration',
},
},
},
},
dataSourceSpecForSettlementData: {
__typename: 'DataSourceSpec',
id: 'oracleId',
data: {
__typename: 'DataSourceDefinition',
sourceType: {
__typename: 'DataSourceDefinitionExternal',
sourceType: {
__typename: 'DataSourceSpecConfiguration',
},
},
},
},
dataSourceSpecBinding: {
__typename: 'DataSourceSpecToFutureBinding',
tradingTerminationProperty: 'trading-termination-property',
settlementDataProperty: 'settlement-data-property',
},
},
metadata: {
__typename: 'InstrumentMetadata',
tags: ['ABC', 'DEF'],
},
},
},
fees: {
__typename: 'Fees',
factors: {
__typename: 'FeeFactors',
infrastructureFee: '0.01',
liquidityFee: '0.01',
makerFee: '0.01',
},
},
data: {
__typename: 'MarketData',
market: {
__typename: 'Market',
id: '1',
},
markPrice: '90',
trigger: Schema.AuctionTrigger.AUCTION_TRIGGER_OPENING,
marketState: Schema.MarketState.STATE_PENDING,
marketTradingMode: Schema.MarketTradingMode.TRADING_MODE_OPENING_AUCTION,
indicativeVolume: '1000',
},
candles: [
{
__typename: 'Candle',
high: '100',
low: '10',
open: '10',
close: '80',
volume: '1000',
periodStart: '2022-11-01T15:49:00Z',
},
{
__typename: 'Candle',
high: '10',
low: '1',
open: '1',
close: '100',
volume: '1000',
periodStart: '2022-11-01T15:50:00Z',
},
],
};
const MARKET_B: PartialMarket = {
__typename: 'Market',
id: '2',
decimalPlaces: 2,
positionDecimalPlaces: 0,
tradingMode: Schema.MarketTradingMode.TRADING_MODE_CONTINUOUS,
tradableInstrument: {
__typename: 'TradableInstrument',
instrument: {
__typename: 'Instrument',
id: '2',
code: 'XYZ',
name: 'XYZ 1-Day',
product: {
__typename: 'Future',
quoteName: 'XYZ',
settlementAsset: {
__typename: 'Asset',
id: 'asset-XYZ',
name: 'asset-XYZ',
decimals: 2,
symbol: 'XYZ',
},
dataSourceSpecForTradingTermination: {
__typename: 'DataSourceSpec',
id: 'oracleId',
data: {
__typename: 'DataSourceDefinition',
sourceType: {
__typename: 'DataSourceDefinitionExternal',
sourceType: {
__typename: 'DataSourceSpecConfiguration',
},
},
},
},
dataSourceSpecForSettlementData: {
__typename: 'DataSourceSpec',
id: 'oracleId',
data: {
__typename: 'DataSourceDefinition',
sourceType: {
__typename: 'DataSourceDefinitionExternal',
sourceType: {
__typename: 'DataSourceSpecConfiguration',
},
},
},
},
dataSourceSpecBinding: {
__typename: 'DataSourceSpecToFutureBinding',
tradingTerminationProperty: 'trading-termination-property',
settlementDataProperty: 'settlement-data-property',
},
},
metadata: {
__typename: 'InstrumentMetadata',
tags: ['XYZ'],
},
},
},
fees: {
__typename: 'Fees',
factors: {
__typename: 'FeeFactors',
infrastructureFee: '0.01',
liquidityFee: '0.01',
makerFee: '0.01',
},
},
data: {
__typename: 'MarketData',
market: {
__typename: 'Market',
id: '2',
},
markPrice: '123.123',
trigger: Schema.AuctionTrigger.AUCTION_TRIGGER_OPENING,
marketState: Schema.MarketState.STATE_PENDING,
marketTradingMode: Schema.MarketTradingMode.TRADING_MODE_OPENING_AUCTION,
indicativeVolume: '2000',
},
candles: [
{
__typename: 'Candle',
high: '100',
low: '10',
open: '10',
close: '80',
volume: '1000',
periodStart: '2022-11-01T15:49:00Z',
},
],
};
describe('WelcomeLandingDialog', () => {
it('should call onSelect callback on SelectMarketLandingTable', () => {
const onClose = jest.fn();
render(
<MemoryRouter>
<SelectMarketLandingTable
markets={[MARKET_A as Market, MARKET_B as Market]}
onClose={onClose}
/>
</MemoryRouter>,
{ wrapper: MockedProvider }
);
fireEvent.click(screen.getAllByTestId(`market-link-1`)[0]);
expect(onClose).toHaveBeenCalled();
fireEvent.click(screen.getAllByTestId(`market-link-2`)[0]);
expect(onClose).toHaveBeenCalled();
});
it('should not call onClose when metaKey is held', () => {
const onClose = jest.fn();
render(
<MemoryRouter>
<SelectMarketLandingTable
markets={[MARKET_A as Market, MARKET_B as Market]}
onClose={onClose}
/>
</MemoryRouter>,
{ wrapper: MockedProvider }
);
fireEvent.click(screen.getAllByTestId(`market-link-1`)[0], {
metaKey: true,
});
expect(mockMarketClickHandler).toHaveBeenCalled();
expect(onClose).not.toHaveBeenCalled();
fireEvent.click(screen.getAllByTestId(`market-link-1`)[0]);
expect(onClose).toHaveBeenCalled();
});
});
@@ -1,130 +0,0 @@
import React, { useCallback } from 'react';
import { useMarketList } from '@vegaprotocol/markets';
import { t } from '@vegaprotocol/i18n';
import { useAssetDetailsDialogStore } from '@vegaprotocol/assets';
import { Link as UILink, TinyScroll } from '@vegaprotocol/ui-toolkit';
import type { OnCellClickHandler } from '../select-market';
import type { MarketMaybeWithDataAndCandles } from '@vegaprotocol/markets';
import {
ColumnKind,
columns,
SelectMarketTableHeader,
SelectMarketTableRow,
} from '../select-market';
import { WelcomeDialogHeader } from './welcome-dialog-header';
import { Link } from 'react-router-dom';
import { ProposedMarkets } from './proposed-markets';
import { Links, Routes } from '../../pages/client-router';
import { useMarketClickHandler } from '../../lib/hooks/use-market-click-handler';
import { TelemetryApproval } from './telemetry-approval';
import { Networks, useEnvironment } from '@vegaprotocol/environment';
export const SelectMarketLandingTable = ({
markets,
onClose,
}: {
markets: MarketMaybeWithDataAndCandles[] | null;
onClose: () => void;
}) => {
const onSelect = useMarketClickHandler();
const onSelectMarket = useCallback(
(id: string, metaKey?: boolean) => {
onSelect(id, metaKey);
if (!metaKey) {
onClose();
}
},
[onSelect, onClose]
);
const { open: openAssetDetailsDialog } = useAssetDetailsDialogStore();
const onCellClick = useCallback<OnCellClickHandler>(
(e, kind, value) => {
if (value && kind === ColumnKind.Asset) {
openAssetDetailsDialog(value, e.target as HTMLElement);
}
},
[openAssetDetailsDialog]
);
const showProposed = (markets?.length || 0) <= 5;
return (
<>
<TinyScroll
className="max-h-[60vh] overflow-x-auto -mr-4 pr-4"
data-testid="select-market-list"
>
<p className="text-neutral-500 dark:text-neutral-400 mb-4">
{t('Select a market to get started...')}
</p>
<table className="text-sm relative h-full min-w-full whitespace-nowrap">
<thead className="sticky top-0 z-10 bg-white dark:bg-black">
<SelectMarketTableHeader />
</thead>
<tbody>
{markets?.map((market, i) => (
<SelectMarketTableRow
marketId={market.id}
key={i}
detailed={false}
onSelect={onSelectMarket}
columns={columns(market, onSelectMarket, onCellClick)}
/>
))}
</tbody>
</table>
</TinyScroll>
<div className="mt-4 text-md">
<Link
to={Links[Routes.MARKETS]()}
data-testid="view-market-list-link"
onClick={() => onClose()}
>
<UILink className="text-sm underline">
{'Or view full market list'}
</UILink>
</Link>
</div>
{showProposed && <ProposedMarkets />}
</>
);
};
interface LandingDialogContainerProps {
onClose: () => void;
}
export const WelcomeLandingDialog = ({
onClose,
}: LandingDialogContainerProps) => {
const { data, loading, error } = useMarketList();
const { VEGA_ENV } = useEnvironment();
const isMainnet = VEGA_ENV === Networks.MAINNET;
if (error) {
return (
<div className="flex justify-center items-center">
<p className="my-8">{t('Failed to load markets')}</p>
</div>
);
}
if (loading) {
return (
<div className="flex justify-center items-center">
<p className="my-8">{t('Loading...')}</p>
</div>
);
}
return (
<>
<WelcomeDialogHeader />
<SelectMarketLandingTable markets={data} onClose={onClose} />
{isMainnet && (
<TelemetryApproval
helpText={t(
'Help identify bugs and improve the service by sharing anonymous usage data. You can change this in your settings at any time.'
)}
/>
)}
</>
);
};
+2 -1
View File
@@ -31,7 +31,7 @@ import ToastsManager from './toasts-manager';
import { HashRouter, useLocation, useSearchParams } from 'react-router-dom';
import { Connectors } from '../lib/vega-connectors';
import { ViewingBanner } from '../components/viewing-banner';
import { AnnouncementBanner } from '../components/banner';
import { AnnouncementBanner, UpgradeBanner } from '../components/banner';
import { AppLoader, DynamicLoader } from '../components/app-loader';
import { Navbar } from '../components/navbar';
import { ENV } from '../lib/config';
@@ -99,6 +99,7 @@ function AppBody({ Component }: AppProps) {
mode={ProtocolUpgradeCountdownMode.IN_ESTIMATED_TIME_REMAINING}
/>
<ViewingBanner />
<UpgradeBanner showVersionChange={true} />
</div>
<main data-testid={location.pathname}>
<Component />
+2 -11
View File
@@ -1,10 +1,9 @@
import { useRef, useMemo, memo, useCallback } from 'react';
import { useRef, useMemo, memo } from 'react';
import { t } from '@vegaprotocol/i18n';
import { useBottomPlaceholder } from '@vegaprotocol/datagrid';
import { useDataProvider } from '@vegaprotocol/data-provider';
import { AsyncRenderer } from '@vegaprotocol/ui-toolkit';
import type { AgGridReact } from 'ag-grid-react';
import type { AccountFields } from './accounts-data-provider';
import { aggregatedAccountsDataProvider } from './accounts-data-provider';
import type { PinnedAsset } from './accounts-table';
import { AccountTable } from './accounts-table';
@@ -36,16 +35,8 @@ export const AccountManager = ({
dataProvider: aggregatedAccountsDataProvider,
variables,
});
const setId = useCallback(
(data: AccountFields, id: string) => ({
...data,
asset: { ...data.asset, id },
}),
[]
);
const bottomPlaceholderProps = useBottomPlaceholder<AccountFields>({
const bottomPlaceholderProps = useBottomPlaceholder({
gridRef,
setId,
disabled: noBottomPlaceholder,
});
+5 -1
View File
@@ -154,7 +154,11 @@ export const AccountTable = forwardRef<AgGridReact, AccountTableProps>(
{...props}
style={{ width: '100%', height: '100%' }}
overlayNoRowsTemplate={t('No accounts')}
getRowId={({ data }: { data: AccountFields }) => data.asset.id}
getRowId={({
data,
}: {
data: AccountFields & { isLastPlaceholder?: boolean; id?: string };
}) => (data.isLastPlaceholder && data.id ? data.id : data.asset.id)}
ref={ref}
tooltipShowDelay={500}
rowData={props.rowData?.filter(
+11 -10
View File
@@ -2,9 +2,10 @@ import { useEffect, useState } from 'react';
import type { AppNameType, Announcement } from './schema';
import { useAnnouncement } from './hooks/use-announcement';
import {
Icon,
AnnouncementBanner as Banner,
ExternalLink,
VegaIcon,
VegaIconNames,
} from '@vegaprotocol/ui-toolkit';
export type AnnouncementBannerProps = {
@@ -75,22 +76,22 @@ export const AnnouncementBanner = ({
return (
<Banner className="relative px-10">
<button
className="absolute left-0 top-0 w-10 h-full flex items-center justify-center"
data-testid="app-announcement-close"
onClick={() => setVisible(false)}
>
<Icon name="cross" className="w-6 h-6" ariaLabel="dismiss" />
</button>
<div
data-testid="app-announcement"
className="relative font-alpha flex gap-2 justify-center calt uppercase text-center text-lg text-white"
className="relative font-alpha flex gap-2 justify-center text-center text-lg text-white"
>
<span className="pr-4">{data.text}</span>
<span>{data.text}</span>{' '}
{data.urlText && data.url && (
<ExternalLink href={data.url}>{data.urlText}</ExternalLink>
)}
</div>
<button
className="absolute right-0 top-0 p-4 w-10 h-full flex items-center justify-center text-white"
data-testid="app-announcement-close"
onClick={() => setVisible(false)}
>
<VegaIcon name={VegaIconNames.CROSS} size={24} />
</button>
</Banner>
);
};
+54 -24
View File
@@ -164,7 +164,9 @@ interface DataProviderParams<
Data,
SubscriptionData,
Delta,
Variables extends OperationVariables | undefined = undefined
Variables extends OperationVariables | undefined = undefined,
SubscriptionVariables extends OperationVariables | undefined = Variables,
QueryVariables extends OperationVariables | undefined = Variables
> {
query: Query<QueryData>;
subscriptionQuery?: Query<SubscriptionData>;
@@ -181,6 +183,10 @@ interface DataProviderParams<
resetDelay?: number;
additionalContext?: Record<string, unknown>;
errorPolicyGuard?: (graphqlErrors: GraphQLErrors) => boolean;
getQueryVariables?: (variables: Variables) => QueryVariables;
getSubscriptionVariables?: (
variables: Variables
) => SubscriptionVariables | SubscriptionVariables[];
}
/**
@@ -199,7 +205,9 @@ function makeDataProviderInternal<
Data,
SubscriptionData,
Delta,
Variables extends OperationVariables | undefined = undefined
Variables extends OperationVariables | undefined = undefined,
QueryVariables extends OperationVariables | undefined = Variables,
SubscriptionVariables extends OperationVariables | undefined = Variables
>({
query,
subscriptionQuery,
@@ -211,12 +219,16 @@ function makeDataProviderInternal<
resetDelay,
additionalContext,
errorPolicyGuard,
getQueryVariables,
getSubscriptionVariables,
}: DataProviderParams<
QueryData,
Data,
SubscriptionData,
Delta,
Variables
Variables,
QueryVariables,
SubscriptionVariables
>): Subscribe<Data, Delta, Variables> {
// list of callbacks passed through subscribe call
const callbacks: UpdateCallback<Data, Delta>[] = [];
@@ -230,7 +242,7 @@ function makeDataProviderInternal<
let loading = true;
let loaded = false;
let client: ApolloClient<object>;
let subscription: Subscription | undefined;
let subscription: Subscription[] | undefined;
let pageInfo: PageInfo | null = null;
let totalCount: number | undefined;
@@ -263,7 +275,7 @@ function makeDataProviderInternal<
.query<QueryData>({
query,
variables: {
...variables,
...(getQueryVariables ? getQueryVariables(variables) : variables),
...(pagination && {
// let the variables pagination be prior to provider param
pagination: {
@@ -343,6 +355,33 @@ function makeDataProviderInternal<
}
};
const subscriptionSubscribe = () => {
if (!subscriptionQuery || !getDelta || !update) {
return;
}
const subscriptionVariables = getSubscriptionVariables
? getSubscriptionVariables(variables)
: variables;
subscription = ([] as (OperationVariables | undefined)[])
.concat(subscriptionVariables)
.map((variables) =>
client
.subscribe<SubscriptionData>({
query: subscriptionQuery,
variables,
fetchPolicy,
})
.subscribe(onNext, onError)
);
};
const subscriptionUnsubscribe = () => {
if (subscription) {
subscription.forEach((subscription) => subscription.unsubscribe());
}
subscription = undefined;
};
const initialFetch = async (isUpdate = false) => {
if (!client) {
return;
@@ -392,10 +431,7 @@ function makeDataProviderInternal<
}
// if error will occur data provider stops subscription
error = e as Error;
if (subscription) {
subscription.unsubscribe();
}
subscription = undefined;
subscriptionUnsubscribe();
} finally {
loading = false;
notifyAll({ isUpdate });
@@ -439,10 +475,7 @@ function makeDataProviderInternal<
const onError = (e: Error) => {
error = e;
if (subscription) {
subscription.unsubscribe();
subscription = undefined;
}
subscriptionUnsubscribe();
notifyAll();
};
@@ -460,13 +493,7 @@ function makeDataProviderInternal<
return;
}
if (subscriptionQuery && getDelta && update) {
subscription = client
.subscribe<SubscriptionData>({
query: subscriptionQuery,
variables,
fetchPolicy,
})
.subscribe(onNext, onError);
subscriptionSubscribe();
}
await initialFetch();
};
@@ -475,8 +502,7 @@ function makeDataProviderInternal<
if (!subscription) {
return;
}
subscription.unsubscribe();
subscription = undefined;
subscriptionUnsubscribe();
data = null;
error = undefined;
loading = false;
@@ -590,14 +616,18 @@ export function makeDataProvider<
Data,
SubscriptionData,
Delta,
Variables extends OperationVariables | undefined = undefined
Variables extends OperationVariables | undefined = undefined,
SubscriptionVariables extends OperationVariables | undefined = Variables,
QueryVariables extends OperationVariables | undefined = Variables
>(
params: DataProviderParams<
QueryData,
Data,
SubscriptionData,
Delta,
Variables
Variables,
SubscriptionVariables,
QueryVariables
>
): Subscribe<Data, Delta, Variables> {
const getInstance = memoize<Data, Delta, Variables>(() =>
@@ -6,6 +6,7 @@ import type {
} from 'ag-grid-community';
import { renderHook, act, waitFor } from '@testing-library/react';
import { useColumnSizes } from './use-column-sizes';
import * as reactHelpers from '@vegaprotocol/react-helpers';
const mockApis = {
api: {
@@ -49,6 +50,9 @@ describe('UseColumnSizes hook', () => {
});
it('onGridSizeChanged should call setSize', async () => {
jest
.spyOn(reactHelpers, 'useScreenDimensions')
.mockReturnValue({ screenSize: 'xxl' });
const { result } = renderHook(() =>
useColumnSizes({ storeKey, props: {} })
);
@@ -95,6 +99,7 @@ describe('UseColumnSizes hook', () => {
it('onGridReady should call setSizes', async () => {
const props = { onGridReady: jest.fn() };
const { result } = renderHook(() => useColumnSizes({ storeKey, props }));
const obTest = { cool: 1, ...mockApis };
await act(() => {
@@ -8,6 +8,7 @@ import type { AgGridReactProps, AgReactUiProps } from 'ag-grid-react';
import { create } from 'zustand';
import { persist } from 'zustand/middleware';
import { immer } from 'zustand/middleware/immer';
import { useScreenDimensions } from '@vegaprotocol/react-helpers';
const STORAGE_KEY = 'vega_columns_sizes_store';
@@ -93,11 +94,12 @@ export const useColumnSizes = ({
},
[valueSetter, storeKey, parentOnColumnResized]
);
const { screenSize } = useScreenDimensions();
const largeScreen = ['xl', 'xxl', 'xxxl'].includes(screenSize);
const setSizes = useCallback(
(apiEvent: GridReadyEvent | GridSizeChangedEvent) => {
if (!storeKey || !Object.keys(sizes).length || !widthRef.current) {
apiEvent?.api.sizeColumnsToFit();
largeScreen && apiEvent?.api.sizeColumnsToFit();
} else {
const recalculatedSizes = recalculateSizes(sizes);
const newSizes = Object.entries(recalculatedSizes).map(
@@ -109,7 +111,7 @@ export const useColumnSizes = ({
apiEvent.columnApi.setColumnWidths(newSizes);
}
},
[storeKey, recalculateSizes, sizes]
[storeKey, recalculateSizes, sizes, largeScreen]
);
const onGridReady = useCallback(
@@ -25,7 +25,7 @@ export const PriceChangeCell = memo(
ref={ref}
className={`${signedNumberCssClass(
change
)} flex items-center gap-2 justify-end font-mono text-ui-small`}
)} flex items-center gap-2 font-mono text-ui-small`}
>
<Arrow value={change} />
<span data-testid="price-change-percentage">
@@ -4,41 +4,34 @@ import type { AgGridReact } from 'ag-grid-react';
import type { IsFullWidthRowParams, RowHeightParams } from 'ag-grid-community';
const NO_HOVER_CSS_RULE = { 'no-hover': 'data?.isLastPlaceholder' };
const ROW_ID = 'bottomPlaceholder';
const ROW_ID = 'bottom-placeholder';
const fullWidthCellRenderer = () => null;
const isFullWidthRow = (params: IsFullWidthRowParams) =>
params.rowNode.data?.isLastPlaceholder;
interface Props<T> {
interface Props {
gridRef: RefObject<AgGridReact>;
setId?: (data: T, id: string) => T;
disabled?: boolean;
}
// eslint-disable-next-line @typescript-eslint/ban-types
export const useBottomPlaceholder = <T extends {}>({
gridRef,
setId,
disabled,
}: Props<T>) => {
export const useBottomPlaceholder = ({ gridRef, disabled }: Props) => {
const onBodyScrollEnd = useCallback(() => {
const rowCont = gridRef.current?.api.getDisplayedRowCount() ?? 0;
if (rowCont) {
const lastRow = gridRef.current?.api.getDisplayedRowAtIndex(rowCont - 1);
if (lastRow && lastRow.data) {
const placeholderRow = setId
? setId({ ...lastRow.data, isLastPlaceholder: true }, ROW_ID)
: {
...lastRow.data,
isLastPlaceholder: true,
id: ROW_ID,
};
const placeholderRow = {
...lastRow.data,
isLastPlaceholder: true,
id: ROW_ID,
};
const transaction = gridRef.current?.api.getRowNode(ROW_ID)
? { update: [placeholderRow] }
: { add: [placeholderRow] };
gridRef.current?.api.applyTransaction(transaction);
}
}
}, [gridRef, setId]);
}, [gridRef]);
const onRowsChanged = useCallback(() => {
const placeholderNode = gridRef.current?.api.getRowNode(ROW_ID);
@@ -304,7 +304,7 @@ export const DealTicket = ({
: order.postOnly,
expiresAt: undefined,
});
clearErrors('expiresAt');
clearErrors(['expiresAt', 'price']);
}}
market={market}
marketData={marketData}
@@ -58,6 +58,7 @@ beforeEach(() => {
max: new BigNumber(20),
allowance: new BigNumber(30),
deposited: new BigNumber(10),
exempt: false,
};
props = {
assets: [asset],
@@ -287,6 +288,7 @@ describe('Deposit form', () => {
balance,
max,
deposited,
exempt: false,
}}
selectedAsset={asset}
/>

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