Compare commits

...
Author SHA1 Message Date
Matthew Russell 64bf2b3b52 feat: add check for operation name in apollo client error link 2023-05-18 15:05:32 -07:00
Matthew Russell 28f9e169e7 chore: rename nodes so its more clear what the query is used for 2023-05-18 14:54:13 -07:00
dexturr 162b167860 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-05-18 18:07:36 +00:00
Art a4149cc55e feat(web3): withdrawal details (#3701) 2023-05-18 16:50:15 +02:00
Mikołaj Młodzikowski 4161a74eaf feat(ci): fix release body (#3822) 2023-05-18 14:59:46 +01:00
Mikołaj Młodzikowski 5008e57a56 feat(ci): adjust variables during release pipeline to cover actual tag_name 2023-05-18 15:12:12 +02:00
Mikołaj Młodzikowski 89031bf826 feat(ci): append ipfs related information to release body (#3818) 2023-05-18 14:51:53 +02:00
dexturr 61501d4d14 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-05-18 12:10:20 +00:00
Madalina Raicu f8f3ce7df9 fix: no oracle for this market banner 2023-05-18 13:00:17 +01:00
Maciek f7aab5a78a feat(ui-toolkit,trading): allow toast box configuration (#3811) 2023-05-18 12:53:36 +01:00
Bartłomiej Głownia 2904975fd3 feat(trading): highlight negative oracle statuses in market lists (#3816) 2023-05-18 13:22:54 +02:00
Mikołaj Młodzikowski 4f55c946f0 feat(ci): add pipeline for rollback console deployments (#3817) 2023-05-18 10:11:41 +00:00
Matthew Russell de637fee13 chore(trading): show markets in opening auction in market selector (#3814) 2023-05-18 09:49:44 +01:00
dexturr 69c6db8ec2 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-05-18 06:09:07 +00:00
Matthew Russell 8dc465c1d1 feat(trading): additional row actions for tables (#3600) 2023-05-17 21:05:53 -07:00
dexturr 0be934edca chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-05-18 00:16:39 +00:00
Matthew Russell 653688c1dd fix(governance): clear wallet state if user changes to a non existent party (#3813) 2023-05-17 19:25:14 +00:00
dexturr 34400be18a chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-05-17 18:09:58 +00:00
Sam Keen d4a0a4a42c fix(governance): updated page titles (#3810) 2023-05-17 16:16:19 +00:00
Joe Tsang fabf643ce7 chore(ci): fix nightly test runner (#3804) 2023-05-17 16:21:10 +01:00
Sam Keen 45e210092c fix(governance): ensure no negative overstaked penalty (#3794) 2023-05-17 14:41:55 +00:00
Ciaran McGhie 1dc3b3fd05 fix(ui-toolkit): add keys to healthbar segments (#3797) 2023-05-17 15:14:11 +01:00
Sam Keen 0a80e3e39d fix(governance): avoid unknown pubkey causing validator page error (#3755) 2023-05-17 13:21:00 +00:00
Sam Keen 7b8d9dc94b feat(governance): validator tables refocused on stake (#3780) 2023-05-17 14:14:23 +01:00
Daniel c0320c2468 feat(ci): update DNS for mainnet console when new version is released (#3783) 2023-05-17 14:45:06 +02:00
Sam Keen 11173d6614 fix(governance): point to correct data for displayed LP rewards (#3800) 2023-05-17 12:20:51 +00:00
dexturr e3324331e2 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-05-17 12:11:33 +00:00
Maciek 0a6e3cb6f5 chore(trading): refactor docs links resolver (#3782) 2023-05-17 12:10:31 +02:00
Sam Keen 68ede90609 feat(3779): proposal terms in table, full proposal json, description … (#3793) 2023-05-17 08:23:25 +00:00
daro-maj 09778a5d3f test(trading): pegged orders e2e tests (#3787) 2023-05-17 09:54:19 +02:00
MaciekandMatthew Russell 59993e3ea6 chore(trading): suppress sentry logs when eth fails (#3726)
Co-authored-by: Matthew Russell <mattrussell36@gmail.com>
2023-05-16 23:31:53 -07:00
dexturr 54161a6dba chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-05-17 06:07:24 +00:00
Matthew Russell 26f524ea07 feat(trading): sidebar market list adjustments (#3792) 2023-05-16 18:45:17 -07:00
dexturr db13e6f21d chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-05-17 00:17:38 +00:00
dexturr 4a679813ba chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-05-16 18:07:35 +00:00
Matthew Russell a4279d5b5e feat(trading): sidebar market list (#3776) 2023-05-16 16:57:36 +00:00
m.ray 1a274a67c3 feat(trading): oracle banner check termination and settlement data oracles (#3707) 2023-05-16 15:12:22 +02:00
dexturr 1e9ff31fd7 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-05-16 12:08:54 +00:00
Sam Keen aa2b755b93 fix(governance): reward aggregation for validator types (#3773) 2023-05-16 08:40:36 +01:00
Matthew Russell 4a7296312a fix(smart-contracts,deposits): add different abi for tether (#3775) 2023-05-16 08:40:05 +01:00
dexturr 1418a2259f chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-05-16 06:07:24 +00:00
dexturr 7e5ef0157a chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-05-16 00:14:15 +00:00
dexturr 0cf764f11e chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-05-15 18:09:01 +00:00
Sam Keen 1c626aeeb2 fix(governance): useWeb3ConnectStore instead of app state for eth connect d… (#3750) 2023-05-15 16:21:20 +01:00
Bartłomiej Głownia c4cec47b0b feat(trading): add oracle dialog trigger to oracle banner, display status reas… (#3727) 2023-05-15 16:20:30 +01:00
dexturr 1a67c41bb4 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-05-15 12:08:57 +00:00
Matthew Russell fc66240390 fix(governance): check value of telemetry local storage entry (#3762) 2023-05-15 09:48:42 +01:00
Maciek fc0da98683 fix(trading): match link as active more strictly (#3754) 2023-05-15 10:08:21 +02:00
Maciek 4d0dafb113 fix(trading): match link as active more strictly (#3754) 2023-05-15 10:08:04 +02:00
dexturr eda431be9e chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-05-15 06:07:48 +00:00
dexturr 4ff1749e54 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-05-15 00:17:19 +00:00
dexturr e7a108fd7a chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-05-14 18:07:19 +00:00
dexturr 5d3f2ea7d8 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-05-14 12:08:55 +00:00
dexturr 1ab27574ca chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-05-14 06:09:03 +00:00
dexturr 0ea3c43e78 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-05-14 00:17:56 +00:00
dexturr 9e183568b0 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-05-13 18:08:39 +00:00
dexturr 69fd5b26b6 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-05-13 12:09:50 +00:00
dexturr 2d5b926609 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-05-13 06:07:23 +00:00
dexturr 98a2ba79a8 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-05-13 00:13:37 +00:00
dexturr 0e6e986937 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-05-12 18:07:19 +00:00
Mikołaj Młodzikowski 15d6ea0c05 feat(ci): push directly to main branch on vega.xyz 2023-05-12 17:40:52 +02:00
Mikołaj Młodzikowski 0358ce6293 feat(ci): try to merge to vega.xyz using standard PR 2023-05-12 17:13:33 +02:00
Mikołaj Młodzikowski 0791be4636 feat(ci): push directly to main branch on vega.xyz 2023-05-12 16:40:45 +02:00
Dexter Edwardsandsam-keen 07442848ee fix(governance): claim not encoding ascii correctly (#3747)
Co-authored-by: sam-keen <samuel.kleinmann@gmail.com>
2023-05-12 14:04:06 +00:00
Mikołaj Młodzikowski d5f0bd419e feat(ci): update vega landing page whenever new console is released (#3751) 2023-05-12 15:18:15 +02:00
Sam KeenandBartłomiej Głownia 2c7934a8c7 fix(governance): fix time delay notice for withdrawal of 0.00 assets (#3740)
Co-authored-by: Bartłomiej Głownia <bglownia@gmail.com>
2023-05-12 15:04:53 +02:00
Joe Tsang fae61c28ca chore(cypress): remove cypress project id (#3739) 2023-05-12 13:44:22 +01:00
dexturr fcd4541625 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-05-12 12:10:20 +00:00
Sam KeenandJoe 8e0a92fdec fix(governance): network upgrade proposal e2e test tweak (#3748)
Co-authored-by: Joe <joe@vega.xyz>
2023-05-12 09:58:10 +01:00
dexturr 6e7109ed87 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-05-12 06:09:45 +00:00
dexturr f2c698ce06 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-05-12 00:16:15 +00:00
dexturr ad9a3a3400 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-05-11 18:10:08 +00:00
Sam Keen efeac49d58 feat(governance): change status labels for network upgrade proposals (#3711) 2023-05-11 15:43:27 +00:00
Mikołaj Młodzikowski 2d430710ab feat(ci): use actual secret in the script 2023-05-11 17:31:12 +02:00
Mikołaj Młodzikowski bca1a98985 fix(ci): remove old dockerfile 2023-05-11 17:16:28 +02:00
Edd 8a080d3279 fix(explorer): fix bug where no votes displayed a yes icon (#3699) 2023-05-11 14:55:00 +00:00
Mikołaj Młodzikowski f121836b4e feat(ci): fleek updates (#3724) 2023-05-11 15:42:35 +02:00
Sam Keen b641c82ad8 feat(governance): voting blog link and button title (#3717) 2023-05-11 12:59:28 +00:00
Sam Keen bf73559c30 fix(governance): change proposal sort order (#3716) 2023-05-11 12:39:30 +00:00
dexturr 653cec2592 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-05-11 12:07:57 +00:00
m.ray 579c884a5a feat(trading): show pegged order offset and reference in the order table (#3675) 2023-05-11 12:43:20 +01:00
Bartłomiej Głownia b2279c7e47 feat(trading): hook up new EstimatePosition and EstimateFees api methods (#3634) 2023-05-11 12:58:12 +02:00
Sam Keen a8c17b6807 fix(governance): tranches data (#3708) 2023-05-11 10:22:52 +00:00
Sam Keen b4c7dc6f59 fix(governance): more thorough catch for url in error reporting (#3686) 2023-05-11 10:21:16 +00:00
daro-maj d44392bebf test(trading): show full oracle profile info in markets test (#3695) 2023-05-11 08:18:00 +02:00
dexturr bd679957e2 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-05-11 06:07:33 +00:00
dexturr e30d48555e chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-05-11 00:16:19 +00:00
Sam Keen b4b2416780 fix(governance): my stake share 2 dp (#3685) 2023-05-10 16:23:37 +00:00
Maciek dc7832ac81 chore(candles-chart): fill up missing candles (#3664) 2023-05-10 15:26:27 +00:00
Joe Tsang 91207d31ee chore(governance): fix failing validator tests (#3703) 2023-05-10 15:03:47 +00:00
dexturr 58efb460a6 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-05-10 12:10:00 +00:00
Mikołaj Młodzikowski 044f98777b feat(ci): lint makefile 2023-05-10 13:43:33 +02:00
Mikołaj Młodzikowski 00fdc5e81a fix(ci): preparing dist bash syntax 2023-05-10 13:33:17 +02:00
Mikołaj Młodzikowski adfad3bafb feat(ci): fix typo 2023-05-10 13:21:53 +02:00
dexturr e6ae88905c chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-05-10 06:07:15 +00:00
dexturr 3953ed9953 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-05-10 00:15:02 +00:00
Matthew Russell b48ab58e6a chore: add VEGA_URL for explorer mainnet 2023-05-09 14:06:10 -07:00
Matthew Russell e44cf1ff53 chore(environment,trading): fix unit tests for footer and node switcher (#3682) 2023-05-09 13:39:17 -07:00
Matthew Russell bded1d32ba feat(explorer,governance): always allow selecting a node, add node guard (#3678) 2023-05-09 12:58:09 -07:00
Edd 76ddf45f4c fix(explorer): add envs back to env (#3677) 2023-05-09 12:57:38 -07:00
Dexter Edwards 6a2c9004d8 fix(governance, console, explorer): show vega node in list, cache bust (#3676) 2023-05-09 12:57:11 -07:00
dexturr 77ee88b43e chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-05-09 18:09:50 +00:00
Dexter e4958ce3c0 fix: do not show validators with 0 self stake in list 2023-05-09 18:25:20 +01:00
Mikołaj Młodzikowski ecedb879f8 fix(ci): define which app ipfs hash is set for 2023-05-09 18:47:50 +02:00
m.ray 89700803d6 fix(trading): fix release script for mainnet (#3669) 2023-05-09 16:30:05 +00:00
Mikołaj Młodzikowski 65c9fa5df2 feat(ci): establish build based on docker to produce ipfs-hashes (#3612) 2023-05-09 18:24:39 +02:00
ArtandMadalina Raicu 02399c40fc fix(trading): welcome dialog links (#3656)
Co-authored-by: Madalina Raicu <madalina@raygroup.uk>
2023-05-09 15:43:36 +00:00
Sam Keen 2cb3b55a3b fix(governance): total supply displayed (#3666) 2023-05-09 15:33:07 +00:00
Sam Keen 3acc0e2401 fix(governance): consensus table works without previous epoch (#3663) 2023-05-09 15:14:04 +00:00
Dexter c734556eb4 ci: force build 2023-05-09 16:09:50 +01:00
509 changed files with 11356 additions and 6284 deletions
@@ -0,0 +1,74 @@
name: After Release
on:
release:
types: [published]
jobs:
after-release:
runs-on: ubuntu-22.04
timeout-minutes: 30
steps:
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
- name: Log in to the Container registry (ghcr)
uses: docker/login-action@v2
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Log in to the Container registry (docker hub)
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: resolve ipfs hashes for release
run: |
echo "Tag name: ${{ github.event.release.tag_name }}"
echo "Name: ${{ github.event.release.name }}"
echo "Description: ${{ github.event.release.body }}"
until docker pull vegaprotocol/trading:${{ github.event.release.tag_name }}; do
echo "Image not pushed yet, waiting 60 seconds"
sleep 60
done
docker run --rm vegaprotocol/trading:${{ github.event.release.tag_name }} cat /ipfs-hash > ipfs-hash
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
export PATH="$PATH:$PWD/kubo"
which ipfs
echo IPFS_V0=$(cat ipfs-hash) >> $GITHUB_ENV
echo IPFS_V1=$(ipfs cid format -v 1 -b base32 $(cat ipfs-hash)) >> $GITHUB_ENV
- name: Edit Release
uses: irongut/EditRelease@v1.2.0
with:
token: ${{ secrets.GITHUB_TOKEN }}
id: ${{ github.event.release.id }}
body: |
---
# Deployments
* https://explorer.vega.xyz
* https://governance.vega.xyz
# IPFS releases
Tye IPFS hash of this release of the Trading app is:
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 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/).
Your settings are not remembered across different URLs.
IPFS gateways:
https://${{ env.IPFS_V1 }}.ipfs.dweb.link/
https://${{ env.IPFS_V1 }}.ipfs.cf-ipfs.com/
ipfs://${{ env.IPFS_V0 }}/
+3 -3
View File
@@ -5,6 +5,8 @@ on:
branches: branches:
- release/* - release/*
- develop - develop
tags:
- v*
pull_request: pull_request:
types: types:
- opened - opened
@@ -97,15 +99,13 @@ jobs:
- name: See affected apps - name: See affected apps
run: | run: |
echo ">>>> debug" echo ">>>> debug"
echo "NX Version: $nx_version"
echo "NX_BASE: ${{ env.NX_BASE }}" echo "NX_BASE: ${{ env.NX_BASE }}"
echo "NX_HEAD: ${{ env.NX_HEAD }}" echo "NX_HEAD: ${{ env.NX_HEAD }}"
echo ">>>> eof debug" echo ">>>> eof debug"
affected="$(yarn nx print-affected --base=${{ env.NX_BASE }} --head=${{ env.NX_HEAD }} --select=projects)" affected="$(yarn nx print-affected --base=${{ env.NX_BASE }} --head=${{ env.NX_HEAD }} --select=projects)"
echo -n "Affected projects: $affected"
branch_slug="$(echo ${{ github.head_ref || github.ref_name }} | sed -r s/[^a-zA-Z0-9]+/-/g | sed -r s/^-+\|-+$//g | cut -c 1-50 )" branch_slug="$(echo '${{ github.head_ref || github.ref_name }}' | sed -r s/[^a-zA-Z0-9]+/-/g | sed -r s/^-+\|-+$//g | cut -c 1-50 )"
projects_e2e="" projects_e2e=""
preview_governance="not deployed" preview_governance="not deployed"
preview_trading="not deployed" preview_trading="not deployed"
-1
View File
@@ -33,5 +33,4 @@ jobs:
config: baseUrl=${{ github.event.inputs.url }} config: baseUrl=${{ github.event.inputs.url }}
env: grepTags=@live env: grepTags=@live
env: env:
CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+3 -3
View File
@@ -20,7 +20,7 @@ jobs:
project: ${{ fromJSON(inputs.projects) }} project: ${{ fromJSON(inputs.projects) }}
name: ${{ matrix.project }} name: ${{ matrix.project }}
runs-on: self-hosted-runner runs-on: self-hosted-runner
timeout-minutes: 40 timeout-minutes: 60
steps: steps:
# Checks if skip cache was requested # Checks if skip cache was requested
- name: Set skip-nx-cache flag - name: Set skip-nx-cache flag
@@ -66,7 +66,7 @@ jobs:
###### ######
- name: Run Cypress tests - name: Run Cypress tests
run: yarn nx run ${{ matrix.project }}:e2e ${{ env.SKIP_CACHE }} --record --key ${{ secrets.CYPRESS_RECORD_KEY }} --browser chrome --env.grepTags="${{ inputs.tags }}" run: yarn nx run ${{ matrix.project }}:e2e ${{ env.SKIP_CACHE }} --browser chrome --env.grepTags="${{ inputs.tags }}"
working-directory: frontend-monorepo working-directory: frontend-monorepo
env: env:
CYPRESS_SLACK_WEBHOOK: ${{ secrets.CYPRESS_SLACK_WEBHOOK }} CYPRESS_SLACK_WEBHOOK: ${{ secrets.CYPRESS_SLACK_WEBHOOK }}
@@ -98,4 +98,4 @@ jobs:
if: ${{ failure() }} if: ${{ failure() }}
with: with:
name: test-report-${{ matrix.project }} name: test-report-${{ matrix.project }}
path: frontend-monorepo/apps/trading-e2e/cypress/reports path: frontend-monorepo/apps/${{ matrix.project }}/cypress/reports
+83 -34
View File
@@ -30,13 +30,21 @@ jobs:
- name: Set up Docker Buildx - name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2 uses: docker/setup-buildx-action@v2
- name: Log in to the Container registry - name: Log in to the Container registry (ghcr)
uses: docker/login-action@v2 uses: docker/login-action@v2
with: with:
registry: ghcr.io registry: ghcr.io
username: ${{ github.actor }} username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }} password: ${{ secrets.GITHUB_TOKEN }}
- name: Log in to the Container registry (docker hub)
uses: docker/login-action@v2
if: ${{ startsWith(github.ref, 'refs/tags/v') }}
with:
# registry: registry.hub.docker.com
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Setup node - name: Setup node
uses: actions/setup-node@v3 uses: actions/setup-node@v3
with: with:
@@ -54,30 +62,24 @@ jobs:
- name: Define variables - name: Define variables
run: | run: |
envName='' envName=''
dockerfile="dist.Dockerfile"
if [[ "${{ github.event_name }}" = "push" ]]; then if [[ "${{ github.event_name }}" = "push" ]]; then
domain="vega.rocks" domain="vega.rocks"
if [[ "${{ github.ref }}" =~ .*release/.* ]]; then if [[ "${{ github.ref }}" =~ .*release/.* ]]; then
envName="$(echo ${{ github.ref }} | rev | cut -d '/' -f 1 | rev)" envName="$(echo ${{ github.ref }} | rev | cut -d '/' -f 1 | rev)"
if [[ "${{ github.ref }}" =~ .*mainnet.* ]]; then if [[ "${{ github.ref }}" =~ .*mainnet.* ]]; then
domain="vega.community" domain="vega.community"
if [[ "${{ matrix.app }}" = "trading" ]]; then
dockerfile="ipfs.Dockerfile"
fi
fi fi
elif [[ "${{ github.ref }}" =~ .*develop$ ]]; then elif [[ "${{ github.ref }}" =~ .*develop$ ]]; then
envName="stagnet1" envName="stagnet1"
elif [[ "${{ matrix.app}}" = "trading" ]] && [[ ${{ startsWith(github.ref, 'refs/tags/v') && 'true' || 'false' }} = "true" ]]; then
envName="mainnet"
fi fi
bucketName="${{ matrix.app }}.${envName}.${domain}" bucketName="${{ matrix.app }}.${envName}.${domain}"
echo BUCKET_NAME=${bucketName} >> $GITHUB_ENV echo BUCKET_NAME=${bucketName} >> $GITHUB_ENV
fi fi
nodeVersion=$(cat .nvmrc | head -n 1)
echo ENV_NAME=${envName} >> $GITHUB_ENV echo ENV_NAME=${envName} >> $GITHUB_ENV
echo NODE_VERSION=${nodeVersion} >> $GITHUB_ENV
echo DOCKERFILE=docker/${dockerfile} >> $GITHUB_ENV
- name: Build local dist - name: Build local dist
if: ${{ env.DOCKERFILE != 'docker/ipfs.Dockerfile' }}
run: | run: |
flags="" flags=""
if [[ ! -z "${{ env.ENV_NAME }}" ]]; then if [[ ! -z "${{ env.ENV_NAME }}" ]]; then
@@ -98,62 +100,60 @@ jobs:
- name: Build and export to local Docker - name: Build and export to local Docker
id: docker_build id: docker_build
if: ${{ github.event_name == 'pull_request' || ( env.DOCKERFILE == 'docker/ipfs.Dockerfile' && github.event_name == 'push' ) }}
uses: docker/build-push-action@v3 uses: docker/build-push-action@v3
with: with:
context: . context: .
file: ${{ env.DOCKERFILE }} file: docker/node-outside-docker.Dockerfile
load: true load: true
build-args: | build-args: |
APP=${{ matrix.app }} APP=${{ matrix.app }}
NODE_VERSION=${{ env.NODE_VERSION }}
ENV_NAME=${{ env.ENV_NAME }} ENV_NAME=${{ env.ENV_NAME }}
tags: | tags: |
ghcr.io/vegaprotocol/frontend/${{ matrix.app }}:local ghcr.io/vegaprotocol/frontend/${{ matrix.app }}:local
- name: Image digest - name: Image digest
if: ${{ github.event_name == 'pull_request' || ( env.DOCKERFILE == 'docker/ipfs.Dockerfile' && github.event_name == 'push' ) }} if: ${{ github.event_name == 'pull_request' }}
run: echo ${{ steps.docker_build.outputs.digest }} run: echo ${{ steps.docker_build.outputs.digest }}
- name: Sanity check docker image - name: Sanity check docker image
if: ${{ github.event_name == 'pull_request' || ( env.DOCKERFILE == 'docker/ipfs.Dockerfile' && github.event_name == 'push' ) }}
run: | run: |
echo "Check ipfs-hash" echo "Check ipfs-hash"
if [[ "${{ env.DOCKERFILE }}" = "docker/ipfs.Dockerfile" ]]; then docker run --rm ghcr.io/vegaprotocol/frontend/${{ matrix.app }}:local cat /ipfs-hash
docker run --rm ghcr.io/vegaprotocol/frontend/${{ matrix.app }}:local cat /ipfs-hash docker run --rm ghcr.io/vegaprotocol/frontend/${{ matrix.app }}:local cat /ipfs-hash > ${{ matrix.app }}-ipfs-hash
fi
echo "List html directory" echo "List html directory"
docker run --rm ghcr.io/vegaprotocol/frontend/${{ matrix.app }}:local sh -c 'apk add --update tree; tree .' docker run --rm ghcr.io/vegaprotocol/frontend/${{ matrix.app }}:local sh -c 'apk add --update tree; tree /usr/share/nginx/html'
- name: Copy dist to local filesystem - name: Publish dist as docker image (ghcr)
if: ${{ env.DOCKERFILE == 'docker/ipfs.Dockerfile' && github.event_name == 'push' }}
run: |
docker create --name=dist ghcr.io/vegaprotocol/frontend/${{ matrix.app }}:local
docker cp dist:/usr/share/nginx/html dist
echo "check local dist files"
tree dist/html
mv dist/html dist-result
- name: Publish dist as docker image
uses: docker/build-push-action@v3 uses: docker/build-push-action@v3
if: ${{ github.event_name == 'pull_request' }} if: ${{ github.event_name == 'pull_request' || (matrix.app == 'trading' && github.event_name == 'push' && !startsWith(github.ref, 'refs/tags/v') ) }}
with: with:
context: . context: .
file: ${{ env.DOCKERFILE }} file: docker/node-outside-docker.Dockerfile
push: true push: true
build-args: | build-args: |
APP=${{ matrix.app }} APP=${{ matrix.app }}
NODE_VERSION=${{ env.NODE_VERSION }}
ENV_NAME=${{ env.ENV_NAME }} ENV_NAME=${{ env.ENV_NAME }}
tags: | tags: |
ghcr.io/vegaprotocol/frontend/${{ matrix.app }}:${{ github.event.pull_request.head.sha || github.sha }} ghcr.io/vegaprotocol/frontend/${{ matrix.app }}:${{ github.event.pull_request.head.sha || github.sha }}
- name: Publish dist as docker image (docker hub)
uses: docker/build-push-action@v3
if: ${{ matrix.app == 'trading' && github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v') }}
with:
context: .
file: docker/node-outside-docker.Dockerfile
push: true
build-args: |
APP=${{ matrix.app }}
ENV_NAME=${{ env.ENV_NAME }}
tags: |
vegaprotocol/${{ matrix.app }}:${{ github.ref_name }}
vegaprotocol/${{ matrix.app }}:mainnet
# bucket creation in github.com/vegaprotocol/terraform//frontend # bucket creation in github.com/vegaprotocol/terraform//frontend
- name: Publish dist to s3 - name: Publish dist to s3
uses: jakejarvis/s3-sync-action@master uses: jakejarvis/s3-sync-action@master
if: ${{ github.event_name == 'push' }} if: ${{ github.event_name == 'push' && !startsWith(github.ref, 'refs/tags/v') }}
with: with:
args: --acl private --follow-symlinks --delete args: --acl private --follow-symlinks --delete
env: env:
@@ -169,3 +169,52 @@ jobs:
with: with:
labels: ${{ matrix.app }}-preview labels: ${{ matrix.app }}-preview
number: ${{ github.event.number }} number: ${{ github.event.number }}
- name: Trigger fleek deployment
if: ${{ matrix.app == 'trading' && github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v') }}
run: |
# display info about app
curl -H "Authorization: ${{ secrets.FLEEK_API_KEY }}" \
-H "Content-Type: application/json" \
-d '{"query": "query{getSiteById(siteId:\"f8f2e051-f18e-49e6-b876-0a39369dc0d8\"){id latestDeploy{id status}}}"}' \
https://api.fleek.co/graphql
# trigger new deployment as base image is always set to vegaprotocol/trading:mainnet
curl -H "Authorization: ${{ secrets.FLEEK_API_KEY }}" \
-H "Content-Type: application/json" \
-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
if: ${{ matrix.app == 'trading' && github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v') }}
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
export PATH="$PATH:$PWD/kubo"
which ipfs
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}"
# 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}
+77
View File
@@ -0,0 +1,77 @@
name: 'Rollback console'
on:
workflow_dispatch:
inputs:
version:
description: 'Version that should be set on rollback'
required: true
type: string
jobs:
rollback:
runs-on: ubuntu-22.04
timeout-minutes: 10
steps:
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
- name: Log in to the Container registry (docker hub)
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Retag mainnet
run: |
docker pull vegaprotocol/trading:${{ inputs.version }}
docker tag vegaprotocol/trading:${{ inputs.version }} vegaprotocol/trading:mainnet
docker push vegaprotocol/trading:mainnet
docker run --rm vegaprotocol/trading:mainnet cat /ipfs-hash > ipfs-hash
- name: Trigger fleek deployment
run: |
# display info about app
curl -H "Authorization: ${{ secrets.FLEEK_API_KEY }}" \
-H "Content-Type: application/json" \
-d '{"query": "query{getSiteById(siteId:\"f8f2e051-f18e-49e6-b876-0a39369dc0d8\"){id latestDeploy{id status}}}"}' \
https://api.fleek.co/graphql
# trigger new deployment as base image is always set to vegaprotocol/trading:mainnet
curl -H "Authorization: ${{ secrets.FLEEK_API_KEY }}" \
-H "Content-Type: application/json" \
-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
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
export PATH="$PATH:$PWD/kubo"
which ipfs
new_hash=$(cat 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}"
# 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}
+2 -1
View File
@@ -2,6 +2,7 @@
# compiled output # compiled output
/dist /dist
/dist-result
/tmp /tmp
/out-tsc /out-tsc
/tools/executors/**/*.js /tools/executors/**/*.js
@@ -48,4 +49,4 @@ cypress.env.json
.next .next
#cypress #cypress
/apps/trading-e2e/cypress/reports/ /apps/**/cypress/reports/
+20
View File
@@ -0,0 +1,20 @@
.PHONY: latest-release
latest-release:
gh release list | head -n 1 | awk '{print $1}'
.PHONY: show-latest-release
show-latest-release:
gh release view `gh release list | head -n 1 | awk '{print $1}'`
.PHONY: recalculate-ipfs
recalculate-ipfs:
echo "ipfs hash inside the image"
docker run --rm ${TAG} cat /ipfs-hash
echo "recalculating ipfs hash"
docker run --rm ${TAG} ipfs add -r /usr/share/nginx/html
.PHONY: eject-ipfs-hash
unpack:
docker create --name=dist ${TAG}
docker cp dist:/usr/share/nginx/html dist
docker rm dist
+55 -12
View File
@@ -103,25 +103,68 @@ In CI linting, formatting and also run. These checks can be seen in the [CI work
Visit the [Nx Documentation](https://nx.dev/getting-started/intro) to learn more. Visit the [Nx Documentation](https://nx.dev/getting-started/intro) to learn more.
# Docker & Vegacapsule # 🐋 Hosting a console
## Docker To host a console there are two possible build scenarios for running the frontends: nx performed **outside** or **inside** docker build. For specific build instructions follow [build instructions](#build-instructions).
The [Dockerfile](./dockerfiles) for running the frontends is pretty basic, merely building the application with the APP arg that is passed in and serving the application from [nginx](./nginx/nginx.conf). The only complexity that exists is that there is a script which allows the passing of run time environment variables to the containers. See configuration below for how to do this. In order to run a container on port 3000:
You can build any of the containers locally with the following command:
```bash
docker build --dockerfile dockerfiles/Dockerfile.cra . --build-arg APP=[YOUR APP] --tag=[TAG]
```
In order to run a container:
```bash ```bash
docker run -p 3000:80 [TAG] docker run -p 3000:80 [TAG]
``` ```
Images ending with `.dist` are to pack locally created transpiled HTML files into nginx container for non-compatible with yarn architectures like M1 Mac ## Build instructions
The [`docker`](./docker) subfolder has some docker configurations for easily setting up your own hosted version of console either for the web, or ready for pinning on IPFS
### nx build outside the docker
Packaging prepared dist into [`nginx`](https://hub.docker.com/_/nginx)([server configuration](./nginx/nginx.conf)) docker image involves building the application on docker host machine from source.
As a prerequisite you need to perform build of `dist` directory and move its content for specific application to `dist-result` directory. Use following script to do it with a single command:
```bash
./docker/prepare-dist.sh
```
You can build any of the containers locally with the following command:
```bash
docker build --dockerfile docker/node-outside-docker.Dockerfile . --tag=[TAG]
```
### nx build inside the docker
Using multistage dockerfile dist is compiled using [node](https://hub.docker.com/_/node) image and later packed to nginx as in [dist build](#dist-build) example.
```bash
docker build --build-arg APP=[YOUR APP] --build-arg NODE_VERSION=$(cat .nvmrc) --build-arg ENV_NAME=mainnet -t [TAG] -f docker/node-inside-docker.Dockerfile .
```
### Computing ipfs-hash of the build
At the moment this feature is important only for `trading` (console) releases.
Each docker build finishes with hash calculation for dist directory. Resulting hash is added to file named as `/ipfs-hash`. Once docker image is produced you can run following commad to display ipfs-hash:
```bash
make recalculate-ipfs TAG=vegaprotocol/trading:{YOUR_VERSION}
```
**updating hash:** recompiling dist directory (even if there are no changed to source code) results in different hash computed by ipfs command.
### Verifying ipfs-hash of existing current application version
An IPFS CID will be attached to every [release](https://github.com/vegaprotocol/frontend-monorepo/releases). If you are intending to pin an application on IPFS, you can check that your build matches by running the following steps:
1. Show latest release by runnning: `make latest-release`. You need to configure [`gh`](https://cli.github.com/) for this step to work, otherwise please provide release manually from [github](https://github.com/vegaprotocol/frontend-monorepo/releases) or [dockerhub](https://hub.docker.com/r/vegaprotocol/trading)
2. Set RELEASE environment variable to value that you want to validate: `export RELEASE=$(make latest-release)` or `export RELEASE=vXX.XX.XX`
3. Set TAG environment variable to image that you want to validate: `export TAG=vegaprotocol/trading:$RELEASE`
4. Download docker image with the desired release `docker pull $TAG`.
5. Recalculate hash: `make recalculate-ipfs`
6. You should see exactly same hash produced by ipfs command as one placed with the release notes: `make show-latest-release`
7. If you want to extract dist from docker image to your local filesystem you can run following command: `make unpack`
8. Now `dist` directory contains valid application build. **it is not possible to calculate same ipfs hash on files that are result of copy operation**
## Config ## Config
+2 -1
View File
@@ -1,10 +1,11 @@
const { defineConfig } = require('cypress'); const { defineConfig } = require('cypress');
module.exports = defineConfig({ module.exports = defineConfig({
projectId: 'et4snf', reporter: '../../node_modules/cypress-mochawesome-reporter',
e2e: { e2e: {
setupNodeEvents(on, config) { setupNodeEvents(on, config) {
require('cypress-mochawesome-reporter/plugin')(on);
require('@cypress/grep/src/plugin')(config); require('@cypress/grep/src/plugin')(config);
return config; return config;
}, },
+1
View File
@@ -15,5 +15,6 @@
import '@vegaprotocol/cypress'; import '@vegaprotocol/cypress';
import './common.functions.js'; import './common.functions.js';
import 'cypress-mochawesome-reporter/register';
import registerCypressGrep from '@cypress/grep'; import registerCypressGrep from '@cypress/grep';
registerCypressGrep(); registerCypressGrep();
+1
View File
@@ -13,6 +13,7 @@ NX_ETHERSCAN_URL=https://sepolia.etherscan.io
NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json
NX_VEGA_GOVERNANCE_URL=https://stagnet1.governance.vega.xyz NX_VEGA_GOVERNANCE_URL=https://stagnet1.governance.vega.xyz
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.jsoo NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.jsoo
NX_VEGA_NETWORKS='{"TESTNET":"https://explorer.fairground.wtf","MAINNET":"https://explorer.vega.xyz"}'
# App flags # App flags
NX_EXPLORER_ASSETS=1 NX_EXPLORER_ASSETS=1
+1
View File
@@ -2,6 +2,7 @@
NX_TENDERMINT_URL=https://be.vega.community NX_TENDERMINT_URL=https://be.vega.community
NX_TENDERMINT_WEBSOCKET_URL=wss://be.vega.community/websocket NX_TENDERMINT_WEBSOCKET_URL=wss://be.vega.community/websocket
NX_VEGA_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/networks/master/mainnet1/mainnet1.toml NX_VEGA_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/networks/master/mainnet1/mainnet1.toml
NX_VEGA_URL=https://api.vega.community/graphql
NX_VEGA_ENV=MAINNET NX_VEGA_ENV=MAINNET
NX_BLOCK_EXPLORER=https://be.vega.community/rest/ NX_BLOCK_EXPLORER=https://be.vega.community/rest/
NX_ETHERSCAN_URL=https://etherscan.io NX_ETHERSCAN_URL=https://etherscan.io
+2 -1
View File
@@ -1 +1,2 @@
# .env is stagnet1, so there are no overrides required # .env is stagnet1, so there are no overrides required
NX_VEGA_NETWORKS='{"TESTNET":"https://explorer.fairground.wtf","MAINNET":"https://explorer.vega.xyz","STAGNET3":"https://stagnet3.explorer.vega.xyz"}'
+24 -2
View File
@@ -1,9 +1,18 @@
import { NetworkLoader, useInitializeEnv } from '@vegaprotocol/environment'; import {
AppFailure,
NetworkLoader,
NodeGuard,
NodeSwitcherDialog,
useEnvironment,
useInitializeEnv,
useNodeSwitcherStore,
} from '@vegaprotocol/environment';
import { TendermintWebsocketProvider } from './contexts/websocket/tendermint-websocket-provider'; import { TendermintWebsocketProvider } from './contexts/websocket/tendermint-websocket-provider';
import { Loader, Splash } from '@vegaprotocol/ui-toolkit'; import { Loader, Splash } from '@vegaprotocol/ui-toolkit';
import { DEFAULT_CACHE_CONFIG } from '@vegaprotocol/apollo-client'; import { DEFAULT_CACHE_CONFIG } from '@vegaprotocol/apollo-client';
import { RouterProvider } from 'react-router-dom'; import { RouterProvider } from 'react-router-dom';
import { router } from './routes/router-config'; import { router } from './routes/router-config';
import { t } from '@vegaprotocol/i18n';
const splashLoading = ( const splashLoading = (
<Splash> <Splash>
@@ -12,10 +21,23 @@ const splashLoading = (
); );
function App() { function App() {
const { VEGA_URL } = useEnvironment();
const [nodeSwitcherOpen, setNodeSwitcherOpen] = useNodeSwitcherStore(
(store) => [store.dialogOpen, store.setDialogOpen]
);
return ( return (
<TendermintWebsocketProvider> <TendermintWebsocketProvider>
<NetworkLoader cache={DEFAULT_CACHE_CONFIG}> <NetworkLoader cache={DEFAULT_CACHE_CONFIG}>
<RouterProvider router={router} fallbackElement={splashLoading} /> <NodeGuard
skeleton={<div>{t('Loading')}</div>}
failure={<AppFailure title={t(`Node: ${VEGA_URL} is unsuitable`)} />}
>
<RouterProvider router={router} fallbackElement={splashLoading} />
</NodeGuard>
<NodeSwitcherDialog
open={nodeSwitcherOpen}
setOpen={setNodeSwitcherOpen}
/>
</NetworkLoader> </NetworkLoader>
</TendermintWebsocketProvider> </TendermintWebsocketProvider>
); );
@@ -1,13 +1,19 @@
import { NodeSwitcherDialog, useEnvironment } from '@vegaprotocol/environment'; import {
useEnvironment,
useNodeSwitcherStore,
} from '@vegaprotocol/environment';
import { t } from '@vegaprotocol/i18n'; import { t } from '@vegaprotocol/i18n';
import { useScreenDimensions } from '@vegaprotocol/react-helpers'; import { useScreenDimensions } from '@vegaprotocol/react-helpers';
import { ExternalLink, Link } from '@vegaprotocol/ui-toolkit'; import { ExternalLink, Link } from '@vegaprotocol/ui-toolkit';
import { useMemo, useState } from 'react'; import { useMemo } from 'react';
import { ENV } from '../../config/env'; import { ENV } from '../../config/env';
export const Footer = () => { export const Footer = () => {
const { VEGA_URL, GIT_COMMIT_HASH, GIT_ORIGIN_URL } = useEnvironment(); const { VEGA_URL, GIT_COMMIT_HASH, GIT_ORIGIN_URL } = useEnvironment();
const [nodeSwitcherOpen, setNodeSwitcherOpen] = useState(false); const setNodeSwitcherOpen = useNodeSwitcherStore(
(store) => store.setDialogOpen
);
const { screenSize } = useScreenDimensions(); const { screenSize } = useScreenDimensions();
const showFullFeedbackLabel = useMemo( const showFullFeedbackLabel = useMemo(
() => ['md', 'lg', 'xl', 'xxl', 'xxxl'].includes(screenSize), () => ['md', 'lg', 'xl', 'xxl', 'xxxl'].includes(screenSize),
@@ -15,46 +21,40 @@ export const Footer = () => {
); );
return ( 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 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"> <div className="flex justify-between gap-2 align-middle">
<div className="flex justify-between gap-2 align-middle"> {GIT_COMMIT_HASH && (
{GIT_COMMIT_HASH && ( <div className="content-center flex border-r border-neutral-700 dark:border-neutral-300 pr-4">
<div className="content-center flex border-r border-neutral-700 dark:border-neutral-300 pr-4"> <p data-testid="git-commit-hash">
<p data-testid="git-commit-hash"> {t('Version')}:{' '}
{t('Version')}:{' '} <Link
<Link href={
href={ GIT_ORIGIN_URL
GIT_ORIGIN_URL ? `${GIT_ORIGIN_URL}/commit/${GIT_COMMIT_HASH}`
? `${GIT_ORIGIN_URL}/commit/${GIT_COMMIT_HASH}` : undefined
: undefined }
} target={GIT_ORIGIN_URL ? '_blank' : undefined}
target={GIT_ORIGIN_URL ? '_blank' : undefined} >
> {GIT_COMMIT_HASH}
{GIT_COMMIT_HASH} </Link>
</Link> </p>
</p>
</div>
)}
<div className="content-center flex pl-2 md:border-r border-neutral-700 dark:border-neutral-300 pr-4">
{VEGA_URL && <NodeUrl url={VEGA_URL} />}
<Link className="ml-2" onClick={() => setNodeSwitcherOpen(true)}>
{t('Change')}
</Link>
</div> </div>
)}
<div className="flex pl-2 content-center"> <div className="content-center flex pl-2 md:border-r border-neutral-700 dark:border-neutral-300 pr-4">
<ExternalLink href={ENV.addresses.feedback}> {VEGA_URL && <NodeUrl url={VEGA_URL} />}
{showFullFeedbackLabel ? t('Share your feedback') : t('Feedback')} <Link className="ml-2" onClick={() => setNodeSwitcherOpen(true)}>
</ExternalLink> {t('Change')}
</div> </Link>
</div> </div>
</footer>
<NodeSwitcherDialog <div className="flex pl-2 content-center">
open={nodeSwitcherOpen} <ExternalLink href={ENV.addresses.feedback}>
setOpen={setNodeSwitcherOpen} {showFullFeedbackLabel ? t('Share your feedback') : t('Feedback')}
/> </ExternalLink>
</> </div>
</div>
</footer>
); );
}; };
@@ -3,6 +3,7 @@ import { Header } from './header';
import { MemoryRouter } from 'react-router-dom'; import { MemoryRouter } from 'react-router-dom';
jest.mock('@vegaprotocol/environment', () => ({ jest.mock('@vegaprotocol/environment', () => ({
...jest.requireActual('@vegaprotocol/environment'),
NetworkSwitcher: () => ( NetworkSwitcher: () => (
<div data-testid="network-switcher">NetworkSwitcher</div> <div data-testid="network-switcher">NetworkSwitcher</div>
), ),
@@ -1,8 +1,8 @@
import { t } from '@vegaprotocol/i18n'; import { t } from '@vegaprotocol/i18n';
import type { MarketInfoWithData } from '@vegaprotocol/market-info'; import type { MarketInfoWithData } from '@vegaprotocol/markets';
import { LiquidityInfoPanel } from '@vegaprotocol/market-info';
import { LiquidityMonitoringParametersInfoPanel } from '@vegaprotocol/market-info';
import { import {
LiquidityInfoPanel,
LiquidityMonitoringParametersInfoPanel,
InstrumentInfoPanel, InstrumentInfoPanel,
KeyDetailsInfoPanel, KeyDetailsInfoPanel,
LiquidityPriceRangeInfoPanel, LiquidityPriceRangeInfoPanel,
@@ -12,8 +12,8 @@ import {
RiskModelInfoPanel, RiskModelInfoPanel,
RiskParametersInfoPanel, RiskParametersInfoPanel,
SettlementAssetInfoPanel, SettlementAssetInfoPanel,
} from '@vegaprotocol/market-info'; } from '@vegaprotocol/markets';
import { MarketInfoTable } from '@vegaprotocol/market-info'; import { MarketInfoTable } from '@vegaprotocol/markets';
import type { DataSourceDefinition } from '@vegaprotocol/types'; import type { DataSourceDefinition } from '@vegaprotocol/types';
import isEqual from 'lodash/isEqual'; import isEqual from 'lodash/isEqual';
@@ -1,4 +1,4 @@
import type { MarketFieldsFragment } from '@vegaprotocol/market-list'; import type { MarketFieldsFragment } from '@vegaprotocol/markets';
import { t } from '@vegaprotocol/i18n'; import { t } from '@vegaprotocol/i18n';
import { ButtonLink } from '@vegaprotocol/ui-toolkit'; import { ButtonLink } from '@vegaprotocol/ui-toolkit';
import type { AgGridReact } from 'ag-grid-react'; import type { AgGridReact } from 'ag-grid-react';
@@ -1,3 +1,5 @@
import { Icon } from '@vegaprotocol/ui-toolkit';
// https://github.com/vegaprotocol/vega/blob/develop/core/blockchain/response.go // https://github.com/vegaprotocol/vega/blob/develop/core/blockchain/response.go
export const ErrorCodes = new Map([ export const ErrorCodes = new Map([
[51, 'Transaction failed validation'], [51, 'Transaction failed validation'],
@@ -28,7 +30,11 @@ export const ChainResponseCode = ({
}: ChainResponseCodeProps) => { }: ChainResponseCodeProps) => {
const isSuccess = successCodes.has(code); const isSuccess = successCodes.has(code);
const icon = isSuccess ? '✅' : '❌'; const icon = isSuccess ? (
<Icon name="tick-circle" className="fill-vega-green-550" />
) : (
<Icon name="cross" className="fill-vega-pink-550" />
);
const label = ErrorCodes.get(code) || 'Unknown response code'; const label = ErrorCodes.get(code) || 'Unknown response code';
// Hack for batches with many errors - see https://github.com/vegaprotocol/vega/issues/7245 // Hack for batches with many errors - see https://github.com/vegaprotocol/vega/issues/7245
@@ -36,7 +42,7 @@ export const ChainResponseCode = ({
error && error.length > 100 ? error.replace(/,/g, ',\r\n') : error; error && error.length > 100 ? error.replace(/,/g, ',\r\n') : error;
return ( return (
<div title={`Response code: ${code} - ${label}`} className="inline-block"> <div title={`Response code: ${code} - ${label}`} className=" inline-block">
<span <span
className="mr-2" className="mr-2"
aria-label={isSuccess ? 'Success' : 'Warning'} aria-label={isSuccess ? 'Success' : 'Warning'}
@@ -4,6 +4,7 @@ import type { TendermintBlocksResponse } from '../../../routes/blocks/tendermint
import { TxDetailsShared } from './shared/tx-details-shared'; import { TxDetailsShared } from './shared/tx-details-shared';
import { TableCell, TableRow, TableWithTbody } from '../../table'; import { TableCell, TableRow, TableWithTbody } from '../../table';
import ProposalLink from '../../links/proposal-link/proposal-link'; import ProposalLink from '../../links/proposal-link/proposal-link';
import { VoteIcon } from '../../vote-icon/vote-icon';
interface TxProposalVoteProps { interface TxProposalVoteProps {
txData: BlockExplorerTransactionResult | undefined; txData: BlockExplorerTransactionResult | undefined;
@@ -30,27 +31,22 @@ export const TxProposalVote = ({
return <>{t('Awaiting Block Explorer transaction details')}</>; return <>{t('Awaiting Block Explorer transaction details')}</>;
} }
const vote = txData.command.voteSubmission.value ? '👍' : '👎'; const vote = txData.command.voteSubmission.value === 'VALUE_YES';
return ( return (
<TableWithTbody className="mb-8" allowWrap={true}> <TableWithTbody className="mb-8" allowWrap={true}>
<TxDetailsShared txData={txData} pubKey={pubKey} blockData={blockData} /> <TxDetailsShared txData={txData} pubKey={pubKey} blockData={blockData} />
<TableRow modifier="bordered">
<TableCell>{t('Proposal ID')}</TableCell>
<TableCell>{txData.command.voteSubmission.proposalId}</TableCell>
</TableRow>
<TableRow modifier="bordered"> <TableRow modifier="bordered">
<TableCell>{t('Proposal details')}</TableCell> <TableCell>{t('Proposal details')}</TableCell>
<TableCell> <TableCell>
<ProposalLink id={txData.command.voteSubmission.proposalId} /> <ProposalLink id={txData.command.voteSubmission.proposalId} />
</TableCell> </TableCell>
</TableRow> </TableRow>
<TableRow modifier="bordered">
<TableCell>{t('Proposal')}</TableCell>
<TableCell>{txData.command.voteSubmission.proposalId}</TableCell>
</TableRow>
<TableRow modifier="bordered"> <TableRow modifier="bordered">
<TableCell>{t('Vote')}</TableCell> <TableCell>{t('Vote')}</TableCell>
<TableCell>{vote}</TableCell> <TableCell>
<VoteIcon vote={vote} />
</TableCell>
</TableRow> </TableRow>
</TableWithTbody> </TableWithTbody>
); );
@@ -1,5 +1,6 @@
import { t } from '@vegaprotocol/i18n'; import { t } from '@vegaprotocol/i18n';
import type { components } from '../../../types/explorer'; import type { components } from '../../../types/explorer';
import { VoteIcon } from '../vote-icon/vote-icon';
interface TxOrderTypeProps { interface TxOrderTypeProps {
orderType: string; orderType: string;
@@ -137,12 +138,15 @@ export const TxOrderType = ({ orderType, command }: TxOrderTypeProps) => {
let type = displayString[orderType] || orderType; let type = displayString[orderType] || orderType;
let colours = let colours =
'text-white dark:text-white bg-vega-dark-150 dark:bg-vega-dark-150'; 'text-white dark:text-white bg-vega-dark-150 dark:bg-vega-dark-250';
// This will get unwieldy and should probably produce a different colour of tag // This will get unwieldy and should probably produce a different colour of tag
if (type === 'Chain Event' && !!command?.chainEvent) { if (type === 'Chain Event' && !!command?.chainEvent) {
type = getLabelForChainEvent(command.chainEvent); type = getLabelForChainEvent(command.chainEvent);
colours = 'text-white dark-text-white bg-vega-pink dark:bg-vega-pink'; colours = 'text-white dark-text-white bg-vega-pink dark:bg-vega-pink';
} else if (type === 'Validator Heartbeat') {
colours =
'text-white dark-text-white bg-vega-light-200 dark:bg-vega-dark-100';
} else if (type === 'Proposal' || type === 'Governance Proposal') { } else if (type === 'Proposal' || type === 'Governance Proposal') {
if (command && !!command.proposalSubmission) { if (command && !!command.proposalSubmission) {
type = getLabelForProposal(command.proposalSubmission); type = getLabelForProposal(command.proposalSubmission);
@@ -150,6 +154,16 @@ export const TxOrderType = ({ orderType, command }: TxOrderTypeProps) => {
colours = 'text-black bg-vega-yellow'; colours = 'text-black bg-vega-yellow';
} }
if (type === 'Vote on Proposal') {
return (
<VoteIcon
vote={command?.voteSubmission?.value === 'VALUE_YES'}
yesText="Proposal vote"
noText="Proposal vote"
/>
);
}
if (type === 'Vote on Proposal' || type === 'Vote Submission') { if (type === 'Vote on Proposal' || type === 'Vote Submission') {
colours = 'text-black bg-vega-yellow'; colours = 'text-black bg-vega-yellow';
} }
@@ -98,6 +98,6 @@ describe('Txs infinite list item', () => {
expect(screen.getByTestId('pub-key')).toHaveTextContent('testPubKey'); expect(screen.getByTestId('pub-key')).toHaveTextContent('testPubKey');
expect(screen.getByTestId('tx-type')).toHaveTextContent('testType'); expect(screen.getByTestId('tx-type')).toHaveTextContent('testType');
expect(screen.getByTestId('tx-block')).toHaveTextContent('1'); expect(screen.getByTestId('tx-block')).toHaveTextContent('1');
expect(screen.getByTestId('tx-success')).toHaveTextContent('Success: ✅'); expect(screen.getByTestId('tx-success')).toHaveTextContent('Success');
}); });
}); });
@@ -31,7 +31,7 @@ export const TxsInfiniteListItem = ({
return ( return (
<div <div
data-testid="transaction-row" data-testid="transaction-row"
className="flex items-center h-full border-t border-neutral-600 dark:border-neutral-800 txs-infinite-list-item grid grid-cols-10 py-2" className="flex items-center h-full border-t border-neutral-600 dark:border-neutral-800 txs-infinite-list-item grid grid-cols-10"
> >
<div <div
className="text-sm col-span-10 md:col-span-3 leading-none" className="text-sm col-span-10 md:col-span-3 leading-none"
@@ -83,7 +83,7 @@ export const TxsInfiniteListItem = ({
data-testid="tx-success" data-testid="tx-success"
> >
<span className="md:hidden uppercase text-vega-dark-300"> <span className="md:hidden uppercase text-vega-dark-300">
Success:&nbsp; Success&nbsp;
</span> </span>
{isNumber(code) ? ( {isNumber(code) ? (
<ChainResponseCode code={code} hideLabel={true} /> <ChainResponseCode code={code} hideLabel={true} />
@@ -0,0 +1,37 @@
import { render } from '@testing-library/react';
import { VoteIcon } from './vote-icon';
describe('Vote TX icon', () => {
it('should use the text For by default for yes votes', () => {
const yes = render(<VoteIcon vote={true} />);
expect(yes.getByTestId('label')).toHaveTextContent('For');
});
it('should use the yesText for yes votes if specified', () => {
const yes = render(<VoteIcon vote={true} yesText="Test" />);
expect(yes.getByTestId('label')).toHaveTextContent('Test');
});
it('should display the tick icon for yes votes', () => {
const no = render(<VoteIcon vote={true} />);
expect(no.getByRole('img')).toHaveAttribute(
'aria-label',
'tick-circle icon'
);
});
it('should use the text Against by default for no votes', () => {
const no = render(<VoteIcon vote={false} />);
expect(no.getByTestId('label')).toHaveTextContent('Against');
});
it('should use the noText for no votes if specified', () => {
const no = render(<VoteIcon vote={false} noText="Test" />);
expect(no.getByTestId('label')).toHaveTextContent('Test');
});
it('should display the delete icon for no votes', () => {
const no = render(<VoteIcon vote={false} />);
expect(no.getByRole('img')).toHaveAttribute('aria-label', 'delete icon');
});
});
@@ -0,0 +1,40 @@
import { Icon } from '@vegaprotocol/ui-toolkit';
import type { IconName } from '@vegaprotocol/ui-toolkit';
export interface VoteIconProps {
// True is a yes vote, false is undefined or no vorte
vote: boolean;
// Defaults to 'For', but can be any text
yesText?: string;
// Defaults to 'Against', but can be any text
noText?: string;
}
/**
* Displays a lozenge with an icon representing the way a user voted for a proposal.
* The yes and no text can be overridden
*
* @returns
*/
export function VoteIcon({
vote,
yesText = 'For',
noText = 'Against',
}: VoteIconProps) {
const label = vote ? yesText : noText;
const bg = vote ? 'bg-vega-green-550' : 'bg-vega-pink-550';
const icon: IconName = vote ? 'tick-circle' : 'delete';
const fill = vote ? 'vega-green-300' : 'vega-pink-300';
const text = vote ? 'vega-green-200' : 'vega-pink-200';
return (
<div
className={`voteicon inline-block my-1 py-1 px-2 py rounded-md text-white leading-one sm align-top ${bg}`}
>
<Icon name={icon} size={3} className={`mr-2 p-0 fill-${fill}`} />
<span className={`text-base text-${text}`} data-testid="label">
{label}
</span>
</div>
);
}
@@ -8,7 +8,7 @@ import { useScrollToLocation } from '../../hooks/scroll-to-location';
import { useDocumentTitle } from '../../hooks/use-document-title'; import { useDocumentTitle } from '../../hooks/use-document-title';
import compact from 'lodash/compact'; import compact from 'lodash/compact';
import { JsonViewerDialog } from '../../components/dialogs/json-viewer-dialog'; import { JsonViewerDialog } from '../../components/dialogs/json-viewer-dialog';
import { marketInfoWithDataProvider } from '@vegaprotocol/market-info'; import { marketInfoWithDataProvider } from '@vegaprotocol/markets';
import { PageTitle } from '../../components/page-helpers/page-title'; import { PageTitle } from '../../components/page-helpers/page-title';
export const MarketPage = () => { export const MarketPage = () => {
@@ -1,6 +1,6 @@
import { useScrollToLocation } from '../../hooks/scroll-to-location'; import { useScrollToLocation } from '../../hooks/scroll-to-location';
import { useDocumentTitle } from '../../hooks/use-document-title'; import { useDocumentTitle } from '../../hooks/use-document-title';
import { marketsProvider } from '@vegaprotocol/market-list'; import { marketsProvider } from '@vegaprotocol/markets';
import { RouteTitle } from '../../components/route-title'; import { RouteTitle } from '../../components/route-title';
import { AsyncRenderer } from '@vegaprotocol/ui-toolkit'; import { AsyncRenderer } from '@vegaprotocol/ui-toolkit';
import { t } from '@vegaprotocol/i18n'; import { t } from '@vegaprotocol/i18n';
+7 -5
View File
@@ -32,9 +32,9 @@
line-height: calc(min(var(--ag-line-height, 26px), 26px) - 4px); line-height: calc(min(var(--ag-line-height, 26px), 26px) - 4px);
} }
.vega-ag-grid .ag-row { .vega-ag-grid .ag-row,
border-width: 1px 0; .vega-ag-grid .ag-cell {
border-bottom: 1px solid transparent; border-width: 0;
} }
/* Light variables */ /* Light variables */
@@ -46,7 +46,6 @@
--ag-header-column-separator-color: theme(colors.neutral[300]); --ag-header-column-separator-color: theme(colors.neutral[300]);
--ag-row-border-color: theme(colors.white); --ag-row-border-color: theme(colors.white);
--ag-row-hover-color: theme(colors.neutral[100]); --ag-row-hover-color: theme(colors.neutral[100]);
--ag-font-size: 12px;
} }
/* Dark variables */ /* Dark variables */
@@ -58,5 +57,8 @@
--ag-header-column-separator-color: theme(colors.neutral[600]); --ag-header-column-separator-color: theme(colors.neutral[600]);
--ag-row-border-color: theme(colors.black); --ag-row-border-color: theme(colors.black);
--ag-row-hover-color: theme(colors.neutral[800]); --ag-row-hover-color: theme(colors.neutral[800]);
--ag-font-size: 12px; }
.voteicon svg {
vertical-align: baseline;
} }
+2 -1
View File
@@ -1,10 +1,11 @@
const { defineConfig } = require('cypress'); const { defineConfig } = require('cypress');
module.exports = defineConfig({ module.exports = defineConfig({
projectId: 'et4snf', reporter: '../../node_modules/cypress-mochawesome-reporter',
e2e: { e2e: {
setupNodeEvents(on, config) { setupNodeEvents(on, config) {
require('cypress-mochawesome-reporter/plugin')(on);
require('@cypress/grep/src/plugin')(config); require('@cypress/grep/src/plugin')(config);
return config; return config;
}, },
@@ -7,6 +7,7 @@ import {
waitForSpinner, waitForSpinner,
navigateTo, navigateTo,
navigation, navigation,
turnTelemetryOff,
} from '../../support/common.functions'; } from '../../support/common.functions';
import { import {
clickOnValidatorFromList, clickOnValidatorFromList,
@@ -67,6 +68,7 @@ context(
'teardown wallet & drill into a specific validator', 'teardown wallet & drill into a specific validator',
function () { function () {
cy.clearLocalStorage(); cy.clearLocalStorage();
turnTelemetryOff();
cy.reload(); cy.reload();
waitForSpinner(); waitForSpinner();
cy.connectVegaWallet(); cy.connectVegaWallet();
@@ -252,10 +254,10 @@ context(
waitForBeginningOfEpoch(); waitForBeginningOfEpoch();
cy.getByTestId(stakeValidatorListStakePercentage).should( cy.getByTestId(stakeValidatorListStakePercentage).should(
'have.text', 'have.text',
'100%' '50.02%'
); );
navigateTo(navigation.validators); navigateTo(navigation.validators);
validateValidatorListTotalStakeAndShare('0', '2.00', '100.00%'); validateValidatorListTotalStakeAndShare('0', '3,002.00', '50.02%');
} }
); );
@@ -109,7 +109,7 @@ context(
); );
cy.getByTestId('protocol-upgrade-proposal-status').should( cy.getByTestId('protocol-upgrade-proposal-status').should(
'have.text', 'have.text',
'Approved ' 'Approved by validators '
); );
}); });
}); });
@@ -136,7 +136,7 @@ context(
); );
cy.getByTestId('protocol-upgrade-state').should( cy.getByTestId('protocol-upgrade-state').should(
'have.text', 'have.text',
'Approved' 'Approved by validators'
); );
cy.getByTestId('protocol-upgrade-release-tag').should( cy.getByTestId('protocol-upgrade-release-tag').should(
'have.text', 'have.text',
@@ -63,7 +63,6 @@ context(
}); });
it('should have option to go to last and newest page', function () { it('should have option to go to last and newest page', function () {
waitForBeginningOfEpoch();
cy.getByTestId('goto-last-page').click(); cy.getByTestId('goto-last-page').click();
cy.getByTestId('epoch-total-rewards-table') cy.getByTestId('epoch-total-rewards-table')
.last() .last()
@@ -29,7 +29,7 @@ const performancePenaltyToolTip = '[data-testid="performance-penalty-tooltip"]';
const overstakedPenaltyToolTip = '[data-testid="overstaked-penalty-tooltip"]'; const overstakedPenaltyToolTip = '[data-testid="overstaked-penalty-tooltip"]';
const totalPenaltyToolTip = '[data-testid="total-penalty-tooltip"]'; const totalPenaltyToolTip = '[data-testid="total-penalty-tooltip"]';
const epochCountDown = '[data-testid="epoch-countdown"]'; const epochCountDown = '[data-testid="epoch-countdown"]';
const stakeNumberRegex = /^\d*\.?\d*$/; const stakeNumberRegex = /^\d{1,3}(,\d{3})*(\.\d+)?$/;
context('Validators Page - verify elements on page', function () { context('Validators Page - verify elements on page', function () {
before('navigate to validators page', function () { before('navigate to validators page', function () {
@@ -84,13 +84,13 @@ context('Validators Page - verify elements on page', function () {
cy.get(stakedByOperatorToolTip) cy.get(stakedByOperatorToolTip)
.invoke('text') .invoke('text')
.should('contain', 'Staked by operator: 0.00'); .should('contain', 'Staked by operator: 3,000.00');
cy.get(stakedByDelegatesToolTip) cy.get(stakedByDelegatesToolTip)
.invoke('text') .invoke('text')
.should('contain', 'Staked by delegates: 0.00'); .should('contain', 'Staked by delegates: 0.00');
cy.get(totalStakedToolTip) cy.get(totalStakedToolTip)
.invoke('text') .invoke('text')
.should('contain', 'Total stake: 0.00'); .should('contain', 'Total stake: 3,000.00');
}); });
it('Should be able to see validator normalised voting power', function () { it('Should be able to see validator normalised voting power', function () {
@@ -106,10 +106,10 @@ context('Validators Page - verify elements on page', function () {
cy.get(unnormalisedVotingPowerToolTip) cy.get(unnormalisedVotingPowerToolTip)
.invoke('text') .invoke('text')
.should('contain', 'Unnormalised voting power: 0.00%'); .should('contain', 'Unnormalised voting power: 20.00%');
cy.get(normalisedVotingPowerToolTip) cy.get(normalisedVotingPowerToolTip)
.invoke('text') .invoke('text')
.should('contain', 'Normalised voting power: 0.10%'); .should('contain', 'Normalised voting power: 50.00%');
}); });
// 2002-SINC-018 // 2002-SINC-018
@@ -126,13 +126,13 @@ context('Validators Page - verify elements on page', function () {
cy.get(performancePenaltyToolTip) cy.get(performancePenaltyToolTip)
.invoke('text') .invoke('text')
.should('contain', 'Performance penalty: 100.00%'); .should('contain', 'Performance penalty: 0.00%');
cy.get(overstakedPenaltyToolTip) cy.get(overstakedPenaltyToolTip)
.invoke('text') .invoke('text')
.should('contain', 'Overstaked penalty:'); // value not asserted due to #2886 .should('contain', 'Overstaked penalty: 60.00%'); // value not asserted due to #2886
cy.get(totalPenaltyToolTip) cy.get(totalPenaltyToolTip)
.invoke('text') .invoke('text')
.should('contain', 'Total penalties: 0.00%'); .should('contain', 'Total penalties: 60.00%');
}); });
it('Should be able to see validator pending stake', function () { it('Should be able to see validator pending stake', function () {
+3
View File
@@ -8,6 +8,7 @@ import './wallet-eth.functions.ts';
import './wallet-teardown.functions.ts'; import './wallet-teardown.functions.ts';
import './wallet-vega.functions.ts'; import './wallet-vega.functions.ts';
import './proposal.functions.ts'; import './proposal.functions.ts';
import 'cypress-mochawesome-reporter/register';
import registerCypressGrep from '@cypress/grep'; import registerCypressGrep from '@cypress/grep';
import { aliasGQLQuery } from '@vegaprotocol/cypress'; import { aliasGQLQuery } from '@vegaprotocol/cypress';
import { chainIdQuery, statisticsQuery } from '@vegaprotocol/mock'; import { chainIdQuery, statisticsQuery } from '@vegaprotocol/mock';
@@ -33,4 +34,6 @@ before(() => {
aliasGQLQuery(req, 'ChainId', chainIdQuery()); aliasGQLQuery(req, 'ChainId', chainIdQuery());
aliasGQLQuery(req, 'Statistics', statisticsQuery()); aliasGQLQuery(req, 'Statistics', statisticsQuery());
}); });
// Self stake validators so they are displayed
cy.validatorsSelfDelegate();
}); });
@@ -3,7 +3,6 @@ import {
StakingBridge, StakingBridge,
Token, Token,
TokenVesting, TokenVesting,
TokenFaucetable,
CollateralBridge, CollateralBridge,
} from '@vegaprotocol/smart-contracts'; } from '@vegaprotocol/smart-contracts';
import { ethers, Wallet } from 'ethers'; import { ethers, Wallet } from 'ethers';
@@ -41,7 +40,7 @@ export async function depositAsset(
decimalPlaces: number decimalPlaces: number
) { ) {
// Approve asset // Approve asset
const faucet = new TokenFaucetable(assetEthAddress, signer); const faucet = new Token(assetEthAddress, signer);
cy.wrap( cy.wrap(
faucet.approve(Erc20BridgeAddress, amount + '0'.repeat(decimalPlaces + 1)), faucet.approve(Erc20BridgeAddress, amount + '0'.repeat(decimalPlaces + 1)),
{ {
@@ -62,7 +61,7 @@ export async function depositAsset(
} }
export async function faucetAsset(assetEthAddress: string) { export async function faucetAsset(assetEthAddress: string) {
const faucet = new TokenFaucetable(assetEthAddress, signer); const faucet = new Token(assetEthAddress, signer);
await promiseWithTimeout(faucet.faucet(), 10 * 60 * 1000, 'faucet asset'); await promiseWithTimeout(faucet.faucet(), 10 * 60 * 1000, 'faucet asset');
} }
+6
View File
@@ -0,0 +1,6 @@
function ReactMarkdown({ children }) {
// eslint-disable-next-line react/jsx-no-useless-fragment
return <>{children}</>;
}
export default ReactMarkdown;
+9 -3
View File
@@ -58,9 +58,15 @@ export const AppLoader = ({ children }: { children: React.ReactElement }) => {
token.decimals(), token.decimals(),
]); ]);
const totalSupply = toBigNum(supply, decimals); const totalSupply = toBigNum(supply.toString(), decimals);
const totalWallet = toBigNum(totalAssociatedWallet, decimals); const totalWallet = toBigNum(
const totalVesting = toBigNum(totalAssociatedVesting, decimals); totalAssociatedWallet.toString(),
decimals
);
const totalVesting = toBigNum(
totalAssociatedVesting.toString(),
decimals
);
appDispatch({ appDispatch({
type: AppStateActionType.SET_TOKEN, type: AppStateActionType.SET_TOKEN,
+94 -29
View File
@@ -2,7 +2,6 @@ import './i18n';
import React, { useEffect } from 'react'; import React, { useEffect } from 'react';
import * as Sentry from '@sentry/react'; import * as Sentry from '@sentry/react';
import { Integrations } from '@sentry/tracing';
import { BrowserRouter as Router, useLocation } from 'react-router-dom'; import { BrowserRouter as Router, useLocation } from 'react-router-dom';
import { AppLoader } from './app-loader'; import { AppLoader } from './app-loader';
import { NetworkInfo } from '@vegaprotocol/network-info'; import { NetworkInfo } from '@vegaprotocol/network-info';
@@ -17,6 +16,7 @@ import { AppStateProvider } from './contexts/app-state/app-state-provider';
import { ContractsProvider } from './contexts/contracts/contracts-provider'; import { ContractsProvider } from './contexts/contracts/contracts-provider';
import { AppRouter } from './routes'; import { AppRouter } from './routes';
import type { EthereumConfig } from '@vegaprotocol/web3'; import type { EthereumConfig } from '@vegaprotocol/web3';
import { WithdrawalApprovalDialogContainer } from '@vegaprotocol/web3';
import { import {
createConnectors, createConnectors,
useEthTransactionManager, useEthTransactionManager,
@@ -37,10 +37,14 @@ import {
useEnvironment, useEnvironment,
NetworkLoader, NetworkLoader,
useInitializeEnv, useInitializeEnv,
NodeGuard,
AppFailure,
NodeSwitcherDialog,
useNodeSwitcherStore,
} from '@vegaprotocol/environment'; } from '@vegaprotocol/environment';
import { ENV } from './config'; import { ENV } from './config';
import type { InMemoryCacheConfig } from '@apollo/client'; import type { InMemoryCacheConfig } from '@apollo/client';
import { WithdrawalDialog } from '@vegaprotocol/withdraws'; import { CreateWithdrawalDialog } from '@vegaprotocol/withdraws';
import { SplashLoader } from './components/splash-loader'; import { SplashLoader } from './components/splash-loader';
import { ToastsManager } from './toasts-manager'; import { ToastsManager } from './toasts-manager';
import { import {
@@ -48,6 +52,8 @@ import {
TELEMETRY_ON, TELEMETRY_ON,
} from './components/telemetry-dialog/telemetry-dialog'; } from './components/telemetry-dialog/telemetry-dialog';
import { useLocalStorage } from '@vegaprotocol/react-helpers'; import { useLocalStorage } from '@vegaprotocol/react-helpers';
import { useTranslation } from 'react-i18next';
import { isPartyNotFoundError } from './lib/party';
const cache: InMemoryCacheConfig = { const cache: InMemoryCacheConfig = {
typePolicies: { typePolicies: {
@@ -153,7 +159,8 @@ const Web3Container = ({
<InitializeHandlers /> <InitializeHandlers />
<VegaWalletDialogs /> <VegaWalletDialogs />
<TransactionModal /> <TransactionModal />
<WithdrawalDialog /> <CreateWithdrawalDialog />
<WithdrawalApprovalDialogContainer />
<TelemetryDialog /> <TelemetryDialog />
</> </>
</BalanceManager> </BalanceManager>
@@ -179,32 +186,82 @@ const ScrollToTop = () => {
return null; return null;
}; };
const removeQueryParams = (url: string) => {
return url.split('?')[0];
};
const AppContainer = () => { const AppContainer = () => {
const { config, loading, error } = useEthereumConfig(); const { config, loading, error } = useEthereumConfig();
const { VEGA_ENV, GIT_COMMIT_HASH, GIT_BRANCH, ETHEREUM_PROVIDER_URL } = const {
useEnvironment(); VEGA_ENV,
VEGA_URL,
GIT_COMMIT_HASH,
GIT_BRANCH,
ETHEREUM_PROVIDER_URL,
} = useEnvironment();
const [telemetryOn] = useLocalStorage(TELEMETRY_ON); const [telemetryOn] = useLocalStorage(TELEMETRY_ON);
const { t } = useTranslation();
const [nodeSwitcherOpen, setNodeSwitcher] = useNodeSwitcherStore((store) => [
store.dialogOpen,
store.setDialogOpen,
]);
useEffect(() => { useEffect(() => {
if (ENV.dsn && telemetryOn) { if (ENV.dsn && telemetryOn === 'true') {
Sentry.init({ Sentry.init({
dsn: ENV.dsn, dsn: ENV.dsn,
integrations: [new Integrations.BrowserTracing()],
tracesSampleRate: 0.1, tracesSampleRate: 0.1,
enabled: true, enabled: true,
environment: VEGA_ENV, environment: VEGA_ENV,
release: GIT_COMMIT_HASH, release: GIT_COMMIT_HASH,
beforeSend(event) { beforeSend(event, hint) {
if (event.request?.url?.includes('/claim?')) { const error = hint?.originalException;
return { const errorIsString = typeof error === 'string';
...event, const errorIsObject = error instanceof Error;
request: { const requestUrl = event.request?.url;
...event.request, const transaction = event.transaction;
url: event.request?.url.split('?')[0],
}, if (
}; (errorIsString && isPartyNotFoundError({ message: error })) ||
(errorIsObject && isPartyNotFoundError(error))
) {
// This error is caused by a pubkey making an API request before
// it has interacted with the chain. This isn't needed in Sentry.
return null;
} }
return event;
const updatedRequest =
requestUrl && requestUrl.includes('/claim?')
? { ...event.request, url: removeQueryParams(requestUrl) }
: event.request;
const updatedTransaction =
transaction && transaction.includes('/claim?')
? removeQueryParams(transaction)
: transaction;
const updatedBreadcrumbs = event.breadcrumbs?.map((breadcrumb) => {
if (
breadcrumb.type === 'navigation' &&
breadcrumb.data?.to?.includes('/claim?')
) {
return {
...breadcrumb,
data: {
...breadcrumb.data,
to: removeQueryParams(breadcrumb.data.to),
},
};
}
return breadcrumb;
});
return {
...event,
request: updatedRequest,
transaction: updatedTransaction,
breadcrumbs: updatedBreadcrumbs ?? event.breadcrumbs,
};
}, },
}); });
Sentry.setTag('branch', GIT_BRANCH); Sentry.setTag('branch', GIT_BRANCH);
@@ -219,21 +276,29 @@ const AppContainer = () => {
<ScrollToTop /> <ScrollToTop />
<AppStateProvider> <AppStateProvider>
<div className="grid min-h-full text-white"> <div className="grid min-h-full text-white">
<AsyncRenderer<EthereumConfig | null> <NodeGuard
loading={loading} skeleton={<div>{t('Loading')}</div>}
data={config} failure={
error={error} <AppFailure title={t('NodeUnsuitable', { url: VEGA_URL })} />
render={(cnf) =>
cnf && (
<Web3Container
chainId={Number(cnf.chain_id)}
providerUrl={ETHEREUM_PROVIDER_URL}
/>
)
} }
/> >
<AsyncRenderer<EthereumConfig | null>
loading={loading}
data={config}
error={error}
render={(cnf) =>
cnf && (
<Web3Container
chainId={Number(cnf.chain_id)}
providerUrl={ETHEREUM_PROVIDER_URL}
/>
)
}
/>
</NodeGuard>
</div> </div>
</AppStateProvider> </AppStateProvider>
<NodeSwitcherDialog open={nodeSwitcherOpen} setOpen={setNodeSwitcher} />
</Router> </Router>
); );
}; };
@@ -1,23 +1,14 @@
import { Button } from '@vegaprotocol/ui-toolkit'; import { Button } from '@vegaprotocol/ui-toolkit';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { useWeb3ConnectStore } from '@vegaprotocol/web3';
import {
AppStateActionType,
useAppState,
} from '../../contexts/app-state/app-state-context';
export const EthConnectPrompt = () => { export const EthConnectPrompt = () => {
const { t } = useTranslation(); const { t } = useTranslation();
const { appDispatch } = useAppState(); const { open } = useWeb3ConnectStore();
return ( return (
<Button <Button
variant="default" variant="default"
onClick={() => onClick={open}
appDispatch({
type: AppStateActionType.SET_ETH_WALLET_OVERLAY,
isOpen: true,
})
}
fill={true} fill={true}
data-testid="connect-to-eth-btn" data-testid="connect-to-eth-btn"
> >
@@ -2,10 +2,7 @@ import { Button } from '@vegaprotocol/ui-toolkit';
import { useWeb3React } from '@web3-react/core'; import { useWeb3React } from '@web3-react/core';
import type { ReactElement } from 'react'; import type { ReactElement } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { import { useWeb3ConnectStore } from '@vegaprotocol/web3';
AppStateActionType,
useAppState,
} from '../../contexts/app-state/app-state-context';
export const EthWalletContainer = ({ export const EthWalletContainer = ({
children, children,
@@ -14,21 +11,12 @@ export const EthWalletContainer = ({
}) => { }) => {
const { account } = useWeb3React(); const { account } = useWeb3React();
const { t } = useTranslation(); const { t } = useTranslation();
const { appDispatch } = useAppState(); const { open } = useWeb3ConnectStore();
if (!account) { if (!account) {
return ( return (
<div className="w-full text-center"> <div className="w-full text-center">
<Button <Button onClick={open}>{t('connectEthWallet')}</Button>
onClick={() =>
appDispatch({
type: AppStateActionType.SET_ETH_WALLET_OVERLAY,
isOpen: true,
})
}
>
{t('connectEthWallet')}
</Button>
</div> </div>
); );
} }
@@ -27,7 +27,11 @@ import {
import { Loader } from '@vegaprotocol/ui-toolkit'; import { Loader } from '@vegaprotocol/ui-toolkit';
import colors from 'tailwindcss/colors'; import colors from 'tailwindcss/colors';
import { useBalances } from '../../lib/balances/balances-store'; import { useBalances } from '../../lib/balances/balances-store';
import { useEthereumConfig, useWeb3Disconnect } from '@vegaprotocol/web3'; import {
useEthereumConfig,
useWeb3ConnectStore,
useWeb3Disconnect,
} from '@vegaprotocol/web3';
import { getChainName } from '@vegaprotocol/web3'; import { getChainName } from '@vegaprotocol/web3';
const removeLeadingAddressSymbol = (key: string) => { const removeLeadingAddressSymbol = (key: string) => {
@@ -189,6 +193,7 @@ export const EthWallet = () => {
const pendingTxs = usePendingTransactions(); const pendingTxs = usePendingTransactions();
const disconnect = useWeb3Disconnect(connector); const disconnect = useWeb3Disconnect(connector);
const { config } = useEthereumConfig(); const { config } = useEthereumConfig();
const { open } = useWeb3ConnectStore();
return ( return (
<WalletCard> <WalletCard>
@@ -233,12 +238,7 @@ export const EthWallet = () => {
) : ( ) : (
<Button <Button
fill={true} fill={true}
onClick={() => onClick={open}
appDispatch({
type: AppStateActionType.SET_ETH_WALLET_OVERLAY,
isOpen: true,
})
}
data-testid="connect-to-eth-wallet-button" data-testid="connect-to-eth-wallet-button"
> >
{t('connectEthWalletToAssociate')} {t('connectEthWalletToAssociate')}
@@ -1,6 +1,6 @@
import { Link } from '@vegaprotocol/ui-toolkit'; import { Link } from '@vegaprotocol/ui-toolkit';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { ExternalLinks } from '@vegaprotocol/utils'; import { ExternalLinks } from '@vegaprotocol/environment';
export const DownloadWalletPrompt = () => { export const DownloadWalletPrompt = () => {
const { t } = useTranslation(); const { t } = useTranslation();
@@ -26,6 +26,7 @@ import type {
WalletDelegationFieldsFragment, WalletDelegationFieldsFragment,
} from './__generated__/Delegations'; } from './__generated__/Delegations';
import { DelegationsDocument } from './__generated__/Delegations'; import { DelegationsDocument } from './__generated__/Delegations';
import { isPartyNotFoundError } from '../../lib/party';
export const usePollForDelegations = () => { export const usePollForDelegations = () => {
const { token: vegaToken } = useContracts(); const { token: vegaToken } = useContracts();
@@ -75,6 +76,7 @@ export const usePollForDelegations = () => {
}) })
.then((res) => { .then((res) => {
if (!mounted) return; if (!mounted) return;
const canonisedDelegations = removePaginationWrapper( const canonisedDelegations = removePaginationWrapper(
res.data.party?.delegationsConnection?.edges res.data.party?.delegationsConnection?.edges
); );
@@ -203,6 +205,15 @@ export const usePollForDelegations = () => {
setDelegatedNodes(delegatedAmounts); setDelegatedNodes(delegatedAmounts);
}) })
.catch((err: Error) => { .catch((err: Error) => {
// if party isn't found, dont log to Sentry, just clear state as, user
// will not have any delagations or accounts
if (isPartyNotFoundError(err)) {
setDelegations([]);
setAccounts([]);
setDelegatedNodes([]);
setCurrentStakeAvailable(new BigNumber(0));
return;
}
Sentry.captureException(err); Sentry.captureException(err);
// If query fails stop interval. Its almost certain that the query // If query fails stop interval. Its almost certain that the query
// will just continue to fail // will just continue to fail
@@ -25,20 +25,26 @@ export function Web3Connector({
connectors, connectors,
chainId, chainId,
}: Web3ConnectorProps) { }: Web3ConnectorProps) {
const { appState, appDispatch } = useAppState(); const { open, close, isOpen } = useWeb3ConnectStore();
const setDialogOpen = useCallback( const setDialogOpen = useCallback(
(isOpen: boolean) => { (isOpen: boolean) => {
appDispatch({ type: AppStateActionType.SET_ETH_WALLET_OVERLAY, isOpen }); if (isOpen) {
open();
} else {
close();
}
}, },
[appDispatch] [open, close]
); );
const appChainId = Number(chainId); const appChainId = Number(chainId);
return ( return (
<> <>
<Web3Content appChainId={appChainId}>{children}</Web3Content> <Web3Content appChainId={appChainId}>{children}</Web3Content>
<Web3ConnectDialog <Web3ConnectDialog
connectors={connectors} connectors={connectors}
dialogOpen={appState.ethConnectOverlay} dialogOpen={isOpen}
setDialogOpen={setDialogOpen} setDialogOpen={setDialogOpen}
desiredChainId={appChainId} desiredChainId={appChainId}
/> />
@@ -34,9 +34,6 @@ export interface AppState {
/** Whether or not the manage VEGA wallet overlay is open */ /** Whether or not the manage VEGA wallet overlay is open */
vegaWalletManageOverlay: boolean; vegaWalletManageOverlay: boolean;
/** Whether or not the connect to Ethereum wallet overlay is open */
ethConnectOverlay: boolean;
/** Whether or not the transaction modal is open */ /** Whether or not the transaction modal is open */
transactionOverlay: boolean; transactionOverlay: boolean;
/** /**
@@ -57,7 +54,6 @@ export enum AppStateActionType {
REFRESH_BALANCES, REFRESH_BALANCES,
SET_VEGA_WALLET_OVERLAY, SET_VEGA_WALLET_OVERLAY,
SET_VEGA_WALLET_MANAGE_OVERLAY, SET_VEGA_WALLET_MANAGE_OVERLAY,
SET_ETH_WALLET_OVERLAY,
SET_DRAWER, SET_DRAWER,
REFRESH_ASSOCIATED_BALANCES, REFRESH_ASSOCIATED_BALANCES,
SET_ASSOCIATION_BREAKDOWN, SET_ASSOCIATION_BREAKDOWN,
@@ -81,10 +77,6 @@ export type AppStateAction =
type: AppStateActionType.SET_VEGA_WALLET_MANAGE_OVERLAY; type: AppStateActionType.SET_VEGA_WALLET_MANAGE_OVERLAY;
isOpen: boolean; isOpen: boolean;
} }
| {
type: AppStateActionType.SET_ETH_WALLET_OVERLAY;
isOpen: boolean;
}
| { | {
type: AppStateActionType.SET_DRAWER; type: AppStateActionType.SET_DRAWER;
isOpen: boolean; isOpen: boolean;
@@ -16,7 +16,6 @@ const initialAppState: AppState = {
totalSupply: new BigNumber(0), totalSupply: new BigNumber(0),
vegaWalletOverlay: false, vegaWalletOverlay: false,
vegaWalletManageOverlay: false, vegaWalletManageOverlay: false,
ethConnectOverlay: false,
transactionOverlay: false, transactionOverlay: false,
bannerMessage: '', bannerMessage: '',
disconnectNotice: false, disconnectNotice: false,
@@ -45,12 +44,6 @@ function appStateReducer(state: AppState, action: AppStateAction): AppState {
vegaWalletOverlay: action.isOpen ? false : state.vegaWalletOverlay, vegaWalletOverlay: action.isOpen ? false : state.vegaWalletOverlay,
}; };
} }
case AppStateActionType.SET_ETH_WALLET_OVERLAY: {
return {
...state,
ethConnectOverlay: action.isOpen,
};
}
case AppStateActionType.SET_DRAWER: { case AppStateActionType.SET_DRAWER: {
return { return {
...state, ...state,
@@ -202,6 +202,7 @@
"tokenVotes": "Token votes", "tokenVotes": "Token votes",
"liquidityVotes": "Liquidity votes", "liquidityVotes": "Liquidity votes",
"castYourVote": "Cast your vote", "castYourVote": "Cast your vote",
"yourVote": "Your vote",
"for": "For", "for": "For",
"against": "Against", "against": "Against",
"majorityRequired": "Majority Required", "majorityRequired": "Majority Required",
@@ -593,7 +594,9 @@
"numberOfAgainstVotes": "Number of votes against", "numberOfAgainstVotes": "Number of votes against",
"yesPercentage": "Yes percentage", "yesPercentage": "Yes percentage",
"noPercentage": "No percentage", "noPercentage": "No percentage",
"proposalTerms": "Proposal terms", "proposalJson": "Full proposal JSON",
"proposalDetails": "Proposal details",
"proposalDescription": "Description",
"currentlySetTo": "Currently expected to ", "currentlySetTo": "Currently expected to ",
"currently": "currently", "currently": "currently",
"finalOutcomeMayDiffer": "Final outcome may differ", "finalOutcomeMayDiffer": "Final outcome may differ",
@@ -787,9 +790,9 @@
"homeVegaTokenButtonText": "Manage tokens", "homeVegaTokenButtonText": "Manage tokens",
"downloadProposalJson": "Download proposal as JSON", "downloadProposalJson": "Download proposal as JSON",
"networkUpgrade": "Network Upgrade", "networkUpgrade": "Network Upgrade",
"PROTOCOL_UPGRADE_PROPOSAL_STATUS_APPROVED": "Approved", "PROTOCOL_UPGRADE_PROPOSAL_STATUS_APPROVED": "Approved by validators",
"PROTOCOL_UPGRADE_PROPOSAL_STATUS_PENDING": "Pending", "PROTOCOL_UPGRADE_PROPOSAL_STATUS_PENDING": "Waiting for validator votes",
"PROTOCOL_UPGRADE_PROPOSAL_STATUS_REJECTED": "Rejected", "PROTOCOL_UPGRADE_PROPOSAL_STATUS_REJECTED": "Declined by validators",
"PROTOCOL_UPGRADE_PROPOSAL_STATUS_UNSPECIFIED": "Unspecified", "PROTOCOL_UPGRADE_PROPOSAL_STATUS_UNSPECIFIED": "Unspecified",
"vegaRelease{release}": "Vega Release {{release}}", "vegaRelease{release}": "Vega Release {{release}}",
"upgradeBlockHeight": "Upgrade block height", "upgradeBlockHeight": "Upgrade block height",
@@ -811,5 +814,6 @@
"OptOutOfTelemetry": "You can opt out any time via settings", "OptOutOfTelemetry": "You can opt out any time via settings",
"NoThanks": "No thanks", "NoThanks": "No thanks",
"ShareData": "Share data", "ShareData": "Share data",
"ContinueSharingData": "Continue sharing data" "ContinueSharingData": "Continue sharing data",
"NodeUnsuitable": "Node: {{url}} is unsuitable"
} }
+8
View File
@@ -0,0 +1,8 @@
export const PARTY_NOT_FOUND = 'failed to get party for ID';
export const isPartyNotFoundError = (error: { message: string }) => {
if (error.message.includes(PARTY_NOT_FOUND)) {
return true;
}
return false;
};
@@ -50,7 +50,22 @@ export const useTranches = create<TranchesStore>()((set) => ({
?.map((t) => { ?.map((t) => {
const tranche_progress = const tranche_progress =
t.duration !== 0 ? (now - t.cliff_start) / t.duration : 0; t.duration !== 0 ? (now - t.cliff_start) / t.duration : 0;
const lockedDecimal = tranche_progress < 0 ? 1 : 1 - tranche_progress; let lockedDecimal;
if (t.duration !== 0) {
if (tranche_progress < 0) {
lockedDecimal = 1;
} else {
lockedDecimal = 1 - tranche_progress;
}
} else {
if (now < t.cliff_start) {
lockedDecimal = 1;
} else {
lockedDecimal = 0;
}
}
const clampedLockedDecimal = Math.max(0, Math.min(1, lockedDecimal));
return { return {
tranche_id: t.tranche_id, tranche_id: t.tranche_id,
tranche_start: secondsToDate(t.cliff_start), tranche_start: secondsToDate(t.cliff_start),
@@ -60,7 +75,7 @@ export const useTranches = create<TranchesStore>()((set) => ({
toBigNum(t.current_balance, decimals) toBigNum(t.current_balance, decimals)
), ),
locked_amount: toBigNum(t.initial_balance, decimals).times( locked_amount: toBigNum(t.initial_balance, decimals).times(
lockedDecimal clampedLockedDecimal
), ),
users: t.users, users: t.users,
}; };
+2 -1
View File
@@ -12,7 +12,8 @@ import { useRefreshAfterEpoch } from '../../hooks/use-refresh-after-epoch';
import { ProposalsListItem } from '../proposals/components/proposals-list-item'; import { ProposalsListItem } from '../proposals/components/proposals-list-item';
import { ProtocolUpgradeProposalsListItem } from '../proposals/components/protocol-upgrade-proposals-list-item/protocol-upgrade-proposals-list-item'; import { ProtocolUpgradeProposalsListItem } from '../proposals/components/protocol-upgrade-proposals-list-item/protocol-upgrade-proposals-list-item';
import Routes from '../routes'; import Routes from '../routes';
import { ExternalLinks, removePaginationWrapper } from '@vegaprotocol/utils'; import { ExternalLinks } from '@vegaprotocol/environment';
import { removePaginationWrapper } from '@vegaprotocol/utils';
import { useNodesQuery } from '../staking/home/__generated__/Nodes'; import { useNodesQuery } from '../staking/home/__generated__/Nodes';
import { useProposalsQuery } from '../proposals/proposals/__generated__/Proposals'; import { useProposalsQuery } from '../proposals/proposals/__generated__/Proposals';
import { import {
@@ -53,7 +53,6 @@ const mockAppState: AppState = {
totalSupply: new BigNumber(65000000), totalSupply: new BigNumber(65000000),
vegaWalletOverlay: false, vegaWalletOverlay: false,
vegaWalletManageOverlay: false, vegaWalletManageOverlay: false,
ethConnectOverlay: false,
transactionOverlay: false, transactionOverlay: false,
bannerMessage: '', bannerMessage: '',
disconnectNotice: false, disconnectNotice: false,
@@ -0,0 +1 @@
export { ProposalDescription } from './proposal-description';
@@ -0,0 +1,51 @@
import ReactMarkdown from 'react-markdown';
import classnames from 'classnames';
import { useState } from 'react';
import { useTranslation } from 'react-i18next';
import { Icon, RoundedWrapper } from '@vegaprotocol/ui-toolkit';
import { SubHeading } from '../../../../components/heading';
export const ProposalDescription = ({
description,
}: {
description: string;
}) => {
const { t } = useTranslation();
const [showDescription, setShowDescription] = useState(false);
const showDescriptionIconClasses = classnames('mb-4', {
'rotate-180': showDescription,
});
return (
<section data-testid="proposal-description">
<button
onClick={() => setShowDescription(!showDescription)}
data-testid="proposal-description-toggle"
>
<div className="flex items-center gap-3">
<SubHeading title={t('proposalDescription')} />
<div className={showDescriptionIconClasses}>
<Icon name="chevron-down" size={8} />
</div>
</div>
</button>
{showDescription && (
<RoundedWrapper paddingBottom={true} marginBottomLarge={true}>
<div className="p-2">
<ReactMarkdown
className="react-markdown-container"
/* Prevents HTML embedded in the description from rendering */
skipHtml={true}
/* Stops users embedding images which could be used for tracking */
disallowedElements={['img']}
linkTarget="_blank"
>
{description}
</ReactMarkdown>
</div>
</RoundedWrapper>
)}
</section>
);
};
@@ -206,28 +206,6 @@ describe('Proposal header', () => {
).not.toBeInTheDocument(); ).not.toBeInTheDocument();
}); });
it('Renders Freeform proposal - long rationale (105 chars) - details', () => {
renderComponent(
generateProposal({
id: 'long',
rationale: {
title: '0x0',
description:
'Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Aenean dolor.',
},
terms: {
change: {
__typename: 'NewFreeform',
},
},
}),
false
);
expect(screen.getByTestId('proposal-description')).toHaveTextContent(
/Class aptent/
);
});
// Remove once proposals have rationale and re-enable above tests // Remove once proposals have rationale and re-enable above tests
it('Renders Freeform proposal - id for title', () => { it('Renders Freeform proposal - id for title', () => {
renderComponent( renderComponent(
@@ -5,7 +5,6 @@ import { Heading, SubHeading } from '../../../../components/heading';
import type { ReactNode } from 'react'; import type { ReactNode } from 'react';
import type { ProposalFieldsFragment } from '../../proposals/__generated__/Proposals'; import type { ProposalFieldsFragment } from '../../proposals/__generated__/Proposals';
import type { ProposalQuery } from '../../proposal/__generated__/Proposal'; import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
import ReactMarkdown from 'react-markdown';
import { truncateMiddle } from '../../../../lib/truncate-middle'; import { truncateMiddle } from '../../../../lib/truncate-middle';
import { CurrentProposalState } from '../current-proposal-state'; import { CurrentProposalState } from '../current-proposal-state';
import { ProposalInfoLabel } from '../proposal-info-label'; import { ProposalInfoLabel } from '../proposal-info-label';
@@ -23,12 +22,7 @@ export const ProposalHeader = ({
let details: ReactNode; let details: ReactNode;
let proposalType = ''; let proposalType = '';
let title = proposal?.rationale.title.trim(); const title = proposal?.rationale.title.trim();
let description = proposal?.rationale.description.trim();
if (title?.length === 0 && description && description.length > 0) {
title = description;
description = '';
}
const titleContent = shorten(title ?? '', 100); const titleContent = shorten(title ?? '', 100);
@@ -145,23 +139,6 @@ export const ProposalHeader = ({
{details} {details}
</div> </div>
)} )}
{description && !isListItem && (
<div data-testid="proposal-description">
{/*<div className="uppercase mr-2">{t('ProposalDescription')}:</div>*/}
<SubHeading title={t('ProposalDescription')} />
<ReactMarkdown
className="react-markdown-container"
/* Prevents HTML embedded in the description from rendering */
skipHtml={true}
/* Stops users embedding images which could be used for tracking */
disallowedElements={['img']}
linkTarget="_blank"
>
{description}
</ReactMarkdown>
</div>
)}
</> </>
); );
}; };
@@ -0,0 +1 @@
export { ProposalJson } from './proposal-json';
@@ -1,15 +1,15 @@
import classnames from 'classnames';
import { useState } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { Icon, SyntaxHighlighter } from '@vegaprotocol/ui-toolkit'; import { Icon, SyntaxHighlighter } from '@vegaprotocol/ui-toolkit';
import { SubHeading } from '../../../../components/heading'; import { SubHeading } from '../../../../components/heading';
import type { PartialDeep } from 'type-fest'; import type { ProposalFieldsFragment } from '../../proposals/__generated__/Proposals';
import type * as Schema from '@vegaprotocol/types'; import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
import { useState } from 'react';
import classnames from 'classnames';
export const ProposalTermsJson = ({ export const ProposalJson = ({
terms, proposal,
}: { }: {
terms: PartialDeep<Schema.ProposalTerms>; proposal: ProposalFieldsFragment | ProposalQuery['proposal'];
}) => { }) => {
const { t } = useTranslation(); const { t } = useTranslation();
const [showDetails, setShowDetails] = useState(false); const [showDetails, setShowDetails] = useState(false);
@@ -18,20 +18,20 @@ export const ProposalTermsJson = ({
}); });
return ( return (
<section> <section data-testid="proposal-json">
<button <button
onClick={() => setShowDetails(!showDetails)} onClick={() => setShowDetails(!showDetails)}
data-testid="proposal-terms-toggle" data-testid="proposal-json-toggle"
> >
<div className="flex items-center gap-3"> <div className="flex items-center gap-3">
<SubHeading title={t('proposalTerms')} /> <SubHeading title={t('proposalJson')} />
<div className={showDetailsIconClasses}> <div className={showDetailsIconClasses}>
<Icon name="chevron-down" size={8} /> <Icon name="chevron-down" size={8} />
</div> </div>
</div> </div>
</button> </button>
{showDetails && <SyntaxHighlighter data={terms} />} {showDetails && <SyntaxHighlighter data={proposal} />}
</section> </section>
); );
}; };
@@ -1 +0,0 @@
export { ProposalTermsJson } from './proposal-terms-json';
@@ -0,0 +1 @@
export { ProposalTerms } from './proposal-terms';
@@ -0,0 +1,142 @@
import {
RoundedWrapper,
KeyValueTable,
KeyValueTableRow,
Icon,
} from '@vegaprotocol/ui-toolkit';
import { BigNumber } from '../../../../lib/bignumber';
import { SubHeading } from '../../../../components/heading';
import { useTranslation } from 'react-i18next';
import { useState } from 'react';
import classnames from 'classnames';
interface ProposalTermsProps {
data: Record<string, unknown>;
}
const getParsedValue = (value: unknown) => {
if (typeof value === 'string') {
try {
// Check if value is a number string - if so use bignumber to maintain precision
if (/^\d+(\.\d+)?$/.test(value)) {
return new BigNumber(value).toString();
} else {
// This would convert, for example, a JSON object ('{"key":"value"}')
// into an actual JS object ({key: "value"})
return JSON.parse(value);
}
} catch (error) {
return value;
}
} else {
return value;
}
};
const RenderKeyValue = ({
title,
value,
}: {
title: string;
value: string | number;
}) => (
<KeyValueTable>
<KeyValueTableRow>
{title}
{value}
</KeyValueTableRow>
</KeyValueTable>
);
const RenderArray = ({ title, array }: { title: string; array: unknown[] }) => {
if (array.every((item) => typeof item === 'string')) {
return <RenderKeyValue title={title} value={array.join(', ')} />;
} else {
return (
<div>
<div className="mb-2">{title}</div>
{array.map((item, index) => (
<div key={index}>
<ProposalTermsRenderer data={item as Record<string, unknown>} />
</div>
))}
</div>
);
}
};
// Working with 'unknown' type as a proposal's terms can be in many shapes
const RenderTerm = ({ title, value }: { title: string; value: unknown }) => {
const parsedValue = getParsedValue(value);
if (parsedValue === null || typeof parsedValue === 'boolean') {
return <RenderKeyValue title={title} value={String(parsedValue)} />;
} else if (
typeof parsedValue === 'string' ||
typeof parsedValue === 'number'
) {
return <RenderKeyValue title={title} value={parsedValue} />;
} else if (typeof parsedValue === 'object') {
if (Array.isArray(parsedValue)) {
return <RenderArray title={title} array={parsedValue} />;
} else {
return (
<div>
<div className="my-2">{title}</div>
<ProposalTermsRenderer
data={parsedValue as Record<string, unknown>}
/>
</div>
);
}
} else if (parsedValue === undefined) {
return <span>{'undefined'}</span>;
} else {
return <span>{String(parsedValue)}</span>;
}
};
const ProposalTermsRenderer = ({ data }: ProposalTermsProps) => {
return (
<RoundedWrapper paddingBottom={true}>
{Object.keys(data)
.filter(
(key) =>
!['__typename', 'closingDatetime', 'enactmentDatetime'].includes(
key
)
)
.map((key, index) => (
<div key={index}>
<RenderTerm title={key} value={data[key]} />
</div>
))}
</RoundedWrapper>
);
};
export const ProposalTerms = ({ data }: ProposalTermsProps) => {
const { t } = useTranslation();
const [showTerms, setShowTerms] = useState(false);
const showTermsIconClasses = classnames('mb-4', {
'rotate-180': showTerms,
});
return (
<section data-testid="proposal-terms">
<button
onClick={() => setShowTerms(!showTerms)}
data-testid="proposal-terms-toggle"
>
<div className="flex items-center gap-3">
<SubHeading title={t('proposalDetails')} />
<div className={showTermsIconClasses}>
<Icon name="chevron-down" size={8} />
</div>
</div>
</button>
{showTerms && <ProposalTermsRenderer data={data} />}
</section>
);
};
@@ -25,8 +25,11 @@ jest.mock('../proposal-detail-header/proposal-header', () => ({
jest.mock('../proposal-change-table', () => ({ jest.mock('../proposal-change-table', () => ({
ProposalChangeTable: () => <div data-testid="proposal-change-table"></div>, ProposalChangeTable: () => <div data-testid="proposal-change-table"></div>,
})); }));
jest.mock('../proposal-terms-json', () => ({ jest.mock('../proposal-json', () => ({
ProposalTermsJson: () => <div data-testid="proposal-terms-json"></div>, ProposalJson: () => <div data-testid="proposal-json"></div>,
}));
jest.mock('../proposal-terms/proposal-terms', () => ({
ProposalTerms: () => <div data-testid="proposal-terms"></div>,
})); }));
jest.mock('../proposal-votes-table', () => ({ jest.mock('../proposal-votes-table', () => ({
ProposalVotesTable: () => <div data-testid="proposal-votes-table"></div>, ProposalVotesTable: () => <div data-testid="proposal-votes-table"></div>,
@@ -49,7 +52,8 @@ it('renders each section', async () => {
render(<Proposal proposal={proposal as ProposalQuery['proposal']} />); render(<Proposal proposal={proposal as ProposalQuery['proposal']} />);
expect(await screen.findByTestId('proposal-header')).toBeInTheDocument(); expect(await screen.findByTestId('proposal-header')).toBeInTheDocument();
expect(screen.getByTestId('proposal-change-table')).toBeInTheDocument(); expect(screen.getByTestId('proposal-change-table')).toBeInTheDocument();
expect(screen.getByTestId('proposal-terms-json')).toBeInTheDocument(); expect(screen.getByTestId('proposal-json')).toBeInTheDocument();
expect(screen.getByTestId('proposal-terms')).toBeInTheDocument();
expect(screen.getByTestId('proposal-votes-table')).toBeInTheDocument(); expect(screen.getByTestId('proposal-votes-table')).toBeInTheDocument();
expect(screen.getByTestId('proposal-vote-details')).toBeInTheDocument(); expect(screen.getByTestId('proposal-vote-details')).toBeInTheDocument();
expect(screen.queryByTestId('proposal-list-asset')).not.toBeInTheDocument(); expect(screen.queryByTestId('proposal-list-asset')).not.toBeInTheDocument();
@@ -6,8 +6,10 @@ import { AsyncRenderer, RoundedWrapper } from '@vegaprotocol/ui-toolkit';
import { ProposalHeader } from '../proposal-detail-header/proposal-header'; import { ProposalHeader } from '../proposal-detail-header/proposal-header';
import type { ProposalFieldsFragment } from '../../proposals/__generated__/Proposals'; import type { ProposalFieldsFragment } from '../../proposals/__generated__/Proposals';
import type { ProposalQuery } from '../../proposal/__generated__/Proposal'; import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
import { ProposalDescription } from '../proposal-description';
import { ProposalChangeTable } from '../proposal-change-table'; import { ProposalChangeTable } from '../proposal-change-table';
import { ProposalTermsJson } from '../proposal-terms-json'; import { ProposalJson } from '../proposal-json';
import { ProposalTerms } from '../proposal-terms';
import { ProposalVotesTable } from '../proposal-votes-table'; import { ProposalVotesTable } from '../proposal-votes-table';
import { VoteDetails } from '../vote-details'; import { VoteDetails } from '../vote-details';
import { ListAsset } from '../list-asset'; import { ListAsset } from '../list-asset';
@@ -20,7 +22,7 @@ export enum ProposalType {
PROPOSAL_NETWORK_PARAMETER = 'PROPOSAL_NETWORK_PARAMETER', PROPOSAL_NETWORK_PARAMETER = 'PROPOSAL_NETWORK_PARAMETER',
PROPOSAL_FREEFORM = 'PROPOSAL_FREEFORM', PROPOSAL_FREEFORM = 'PROPOSAL_FREEFORM',
} }
interface ProposalProps { export interface ProposalProps {
proposal: ProposalFieldsFragment | ProposalQuery['proposal']; proposal: ProposalFieldsFragment | ProposalQuery['proposal'];
} }
@@ -78,9 +80,11 @@ export const Proposal = ({ proposal }: ProposalProps) => {
<AsyncRenderer data={params} loading={loading} error={error}> <AsyncRenderer data={params} loading={loading} error={error}>
<section data-testid="proposal"> <section data-testid="proposal">
<ProposalHeader proposal={proposal} isListItem={false} /> <ProposalHeader proposal={proposal} isListItem={false} />
<div className="my-10"> <div className="my-10">
<ProposalChangeTable proposal={proposal} /> <ProposalChangeTable proposal={proposal} />
</div> </div>
{proposal.terms.change.__typename === 'NewAsset' && {proposal.terms.change.__typename === 'NewAsset' &&
proposal.terms.change.source.__typename === 'ERC20' && proposal.terms.change.source.__typename === 'ERC20' &&
proposal.id ? ( proposal.id ? (
@@ -90,7 +94,20 @@ export const Proposal = ({ proposal }: ProposalProps) => {
lifetimeLimit={proposal.terms.change.source.lifetimeLimit} lifetimeLimit={proposal.terms.change.source.lifetimeLimit}
/> />
) : null} ) : null}
<div className="mb-12">
<div className="mb-4">
<ProposalDescription description={proposal.rationale.description} />
</div>
<div className="mb-4">
<ProposalTerms data={proposal.terms} />
</div>
<div className="mb-6">
<ProposalJson proposal={proposal} />
</div>
<div className="mb-10">
<RoundedWrapper paddingBottom={true}> <RoundedWrapper paddingBottom={true}>
<VoteDetails <VoteDetails
proposal={proposal} proposal={proposal}
@@ -102,10 +119,10 @@ export const Proposal = ({ proposal }: ProposalProps) => {
/> />
</RoundedWrapper> </RoundedWrapper>
</div> </div>
<div className="mb-4"> <div className="mb-4">
<ProposalVotesTable proposal={proposal} proposalType={proposalType} /> <ProposalVotesTable proposal={proposal} proposalType={proposalType} />
</div> </div>
<ProposalTermsJson terms={proposal.terms} />
</section> </section>
</AsyncRenderer> </AsyncRenderer>
); );
@@ -24,7 +24,6 @@ const openProposalClosesNextMonth = generateProposal({
}, },
terms: { terms: {
closingDatetime: nextMonth.toString(), closingDatetime: nextMonth.toString(),
enactmentDatetime: nextMonth.toString(),
}, },
}); });
@@ -36,7 +35,6 @@ const openProposalClosesNextWeek = generateProposal({
}, },
terms: { terms: {
closingDatetime: nextWeek.toString(), closingDatetime: nextWeek.toString(),
enactmentDatetime: nextWeek.toString(),
}, },
}); });
@@ -45,7 +43,6 @@ const enactedProposalClosedLastWeek = generateProposal({
state: ProposalState.STATE_ENACTED, state: ProposalState.STATE_ENACTED,
terms: { terms: {
closingDatetime: lastWeek.toString(), closingDatetime: lastWeek.toString(),
enactmentDatetime: lastWeek.toString(),
}, },
}); });
@@ -54,7 +51,6 @@ const failedProposalClosedLastMonth = generateProposal({
state: ProposalState.STATE_FAILED, state: ProposalState.STATE_FAILED,
terms: { terms: {
closingDatetime: lastMonth.toString(), closingDatetime: lastMonth.toString(),
enactmentDatetime: lastMonth.toString(),
}, },
}); });
@@ -8,12 +8,11 @@ import { ProposalsListFilter } from '../proposals-list-filter';
import Routes from '../../../routes'; import Routes from '../../../routes';
import { Button, VegaIcon, VegaIconNames } from '@vegaprotocol/ui-toolkit'; import { Button, VegaIcon, VegaIconNames } from '@vegaprotocol/ui-toolkit';
import { Link } from 'react-router-dom'; import { Link } from 'react-router-dom';
import { createDocsLinks, ExternalLinks } from '@vegaprotocol/utils';
import { ExternalLink } from '@vegaprotocol/ui-toolkit'; import { ExternalLink } from '@vegaprotocol/ui-toolkit';
import type { ProposalQuery } from '../../proposal/__generated__/Proposal'; import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
import type { ProposalFieldsFragment } from '../../proposals/__generated__/Proposals'; import type { ProposalFieldsFragment } from '../../proposals/__generated__/Proposals';
import type { ProtocolUpgradeProposalFieldsFragment } from '@vegaprotocol/proposals'; import type { ProtocolUpgradeProposalFieldsFragment } from '@vegaprotocol/proposals';
import { useEnvironment } from '@vegaprotocol/environment'; import { DocsLinks, ExternalLinks } from '@vegaprotocol/environment';
interface ProposalsListProps { interface ProposalsListProps {
proposals: Array<ProposalFieldsFragment | ProposalQuery['proposal']>; proposals: Array<ProposalFieldsFragment | ProposalQuery['proposal']>;
@@ -36,7 +35,6 @@ export const ProposalsList = ({
protocolUpgradeProposals, protocolUpgradeProposals,
lastBlockHeight, lastBlockHeight,
}: ProposalsListProps) => { }: ProposalsListProps) => {
const { VEGA_DOCS_URL } = useEnvironment();
const { t } = useTranslation(); const { t } = useTranslation();
const [filterString, setFilterString] = useState(''); const [filterString, setFilterString] = useState('');
const sortedProposals = proposals.reduce( const sortedProposals = proposals.reduce(
@@ -83,9 +81,9 @@ export const ProposalsList = ({
marginBottom={false} marginBottom={false}
title={t('pageTitleProposals')} title={t('pageTitleProposals')}
/> />
{VEGA_DOCS_URL && ( {DocsLinks && (
<div className="xs:justify-self-end" data-testid="new-proposal-link"> <div className="xs:justify-self-end" data-testid="new-proposal-link">
<ExternalLink href={createDocsLinks(VEGA_DOCS_URL).PROPOSALS_GUIDE}> <ExternalLink href={DocsLinks.PROPOSALS_GUIDE}>
<Button variant="primary" size="sm"> <Button variant="primary" size="sm">
<div className="flex items-center gap-1"> <div className="flex items-center gap-1">
{t('NewProposal')} {t('NewProposal')}
@@ -2,9 +2,10 @@ import { render, screen } from '@testing-library/react';
import { ProposalFormTerms } from './proposal-form-terms'; import { ProposalFormTerms } from './proposal-form-terms';
jest.mock('@vegaprotocol/environment', () => ({ jest.mock('@vegaprotocol/environment', () => ({
useEnvironment: () => ({ ...jest.requireActual('@vegaprotocol/environment'),
VEGA_DOCS_URL: 'https://docs.vega.xyz', DocsLinks: {
}), PROPOSALS_GUIDE: 'https://docs.vega.xyz/tutorials/proposals',
},
})); }));
const renderComponent = () => { const renderComponent = () => {
@@ -5,8 +5,7 @@ import {
ExternalLink, ExternalLink,
TextArea, TextArea,
} from '@vegaprotocol/ui-toolkit'; } from '@vegaprotocol/ui-toolkit';
import { createDocsLinks } from '@vegaprotocol/utils'; import { DocsLinks } from '@vegaprotocol/environment';
import { useEnvironment } from '@vegaprotocol/environment';
import type { UseFormRegisterReturn } from 'react-hook-form'; import type { UseFormRegisterReturn } from 'react-hook-form';
interface ProposalFormTermsProps { interface ProposalFormTermsProps {
@@ -22,23 +21,20 @@ export const ProposalFormTerms = ({
labelOverride, labelOverride,
docsLink, docsLink,
}: ProposalFormTermsProps) => { }: ProposalFormTermsProps) => {
const { VEGA_DOCS_URL } = useEnvironment();
const { t } = useTranslation(); const { t } = useTranslation();
return ( return (
<FormGroup <FormGroup
label={labelOverride || t('ProposalTerms')} label={labelOverride || t('ProposalTerms')}
labelFor="proposal-terms" labelFor="proposal-terms"
> >
{docsLink && VEGA_DOCS_URL && ( {docsLink && DocsLinks && (
<div className="mt-[-4px] mb-2 text-sm font-light"> <div className="mt-[-4px] mb-2 text-sm font-light">
<span className="mr-1">{t('ProposalTermsText')}</span> <span className="mr-1">{t('ProposalTermsText')}</span>
<ExternalLink <ExternalLink
href={`${ href={`${DocsLinks.PROPOSALS_GUIDE}${docsLink}`}
createDocsLinks(VEGA_DOCS_URL).PROPOSALS_GUIDE
}${docsLink}`}
target="_blank" target="_blank"
> >
{`${createDocsLinks(VEGA_DOCS_URL).PROPOSALS_GUIDE}${docsLink}`} {`${DocsLinks.PROPOSALS_GUIDE}${docsLink}`}
</ExternalLink> </ExternalLink>
</div> </div>
)} )}
@@ -32,7 +32,9 @@ describe('ProtocolUpgradeProposalDetailInfo', () => {
it('should render the state', () => { it('should render the state', () => {
const { getByTestId } = renderComponent(); const { getByTestId } = renderComponent();
expect(getByTestId('protocol-upgrade-state')).toHaveTextContent('Pending'); expect(getByTestId('protocol-upgrade-state')).toHaveTextContent(
'Waiting for validator votes'
);
}); });
it('should render the vega release tag', () => { it('should render the vega release tag', () => {
@@ -26,28 +26,34 @@ describe('ProtocolUpgradeProposalsListItem', () => {
status: status:
ProtocolUpgradeProposalStatus.PROTOCOL_UPGRADE_PROPOSAL_STATUS_REJECTED, ProtocolUpgradeProposalStatus.PROTOCOL_UPGRADE_PROPOSAL_STATUS_REJECTED,
icon: 'protocol-upgrade-proposal-status-icon-rejected', icon: 'protocol-upgrade-proposal-status-icon-rejected',
text: 'Declined by validators',
}, },
{ {
status: status:
ProtocolUpgradeProposalStatus.PROTOCOL_UPGRADE_PROPOSAL_STATUS_PENDING, ProtocolUpgradeProposalStatus.PROTOCOL_UPGRADE_PROPOSAL_STATUS_PENDING,
icon: 'protocol-upgrade-proposal-status-icon-pending', icon: 'protocol-upgrade-proposal-status-icon-pending',
text: 'Waiting for validator votes',
}, },
{ {
status: status:
ProtocolUpgradeProposalStatus.PROTOCOL_UPGRADE_PROPOSAL_STATUS_APPROVED, ProtocolUpgradeProposalStatus.PROTOCOL_UPGRADE_PROPOSAL_STATUS_APPROVED,
icon: 'protocol-upgrade-proposal-status-icon-approved', icon: 'protocol-upgrade-proposal-status-icon-approved',
text: 'Approved by validators',
}, },
{ {
status: status:
ProtocolUpgradeProposalStatus.PROTOCOL_UPGRADE_PROPOSAL_STATUS_UNSPECIFIED, ProtocolUpgradeProposalStatus.PROTOCOL_UPGRADE_PROPOSAL_STATUS_UNSPECIFIED,
icon: 'protocol-upgrade-proposal-status-icon-unspecified', icon: 'protocol-upgrade-proposal-status-icon-unspecified',
text: 'Unspecified',
}, },
]; ];
statuses.forEach(({ status, icon }) => { statuses.forEach(({ status, icon, text }) => {
renderComponent({ ...proposal, status }); renderComponent({ ...proposal, status });
const statusIcon = screen.getByTestId(icon); const statusIcon = screen.getByTestId(icon);
const textContent = screen.getByText(text);
expect(statusIcon).toBeInTheDocument(); expect(statusIcon).toBeInTheDocument();
expect(textContent).toBeInTheDocument();
}); });
}); });
@@ -111,7 +111,9 @@ describe('Vote buttons', () => {
</AppStateProvider> </AppStateProvider>
); );
expect( expect(
screen.getByText('You need some VEGA tokens to participate in governance') screen.getByText(
'You need some VEGA tokens to participate in governance.'
)
).toBeTruthy(); ).toBeTruthy();
}); });
@@ -2,7 +2,12 @@ import { format } from 'date-fns';
import React, { useMemo } from 'react'; import React, { useMemo } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { useVegaWallet, useVegaWalletDialogStore } from '@vegaprotocol/wallet'; import { useVegaWallet, useVegaWalletDialogStore } from '@vegaprotocol/wallet';
import { AsyncRenderer, Button, ButtonLink } from '@vegaprotocol/ui-toolkit'; import {
AsyncRenderer,
Button,
ButtonLink,
ExternalLink,
} from '@vegaprotocol/ui-toolkit';
import { addDecimal, toBigNum } from '@vegaprotocol/utils'; import { addDecimal, toBigNum } from '@vegaprotocol/utils';
import { ProposalState, VoteValue } from '@vegaprotocol/types'; import { ProposalState, VoteValue } from '@vegaprotocol/types';
import { import {
@@ -161,7 +166,12 @@ export const VoteButtons = ({
{changeVote || (voteState === VoteState.NotCast && proposalVotable) ? ( {changeVote || (voteState === VoteState.NotCast && proposalVotable) ? (
<> <>
{currentStakeAvailable.isLessThanOrEqualTo(0) && ( {currentStakeAvailable.isLessThanOrEqualTo(0) && (
<p data-testid="no-stake-available">{t('noGovernanceTokens')}</p> <>
<p data-testid="no-stake-available">{t('noGovernanceTokens')}.</p>
<ExternalLink href="https://blog.vega.xyz/how-to-vote-on-vega-2195d1e52ec5">
{t('findOutMoreAboutHowToVote')}
</ExternalLink>
</>
)} )}
<div className="flex gap-4" data-testid="vote-buttons"> <div className="flex gap-4" data-testid="vote-buttons">
@@ -1,6 +1,6 @@
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { formatDistanceToNow } from 'date-fns'; import { formatDistanceToNow } from 'date-fns';
import { RoundedWrapper, Icon } from '@vegaprotocol/ui-toolkit'; import { RoundedWrapper, Icon, ExternalLink } from '@vegaprotocol/ui-toolkit';
import { useVegaWallet } from '@vegaprotocol/wallet'; import { useVegaWallet } from '@vegaprotocol/wallet';
import { ProposalState } from '@vegaprotocol/types'; import { ProposalState } from '@vegaprotocol/types';
import { useVoteSubmit, VoteProgress } from '@vegaprotocol/proposals'; import { useVoteSubmit, VoteProgress } from '@vegaprotocol/proposals';
@@ -202,7 +202,12 @@ export const VoteDetails = ({
)} )}
<section className="mt-10"> <section className="mt-10">
<SubHeading title={t('castYourVote')} /> {proposal?.state === ProposalState.STATE_OPEN ? (
<SubHeading title={t('castYourVote')} />
) : (
<SubHeading title={t('yourVote')} />
)}
{pubKey ? ( {pubKey ? (
proposal && ( proposal && (
<VoteButtonsContainer <VoteButtonsContainer
@@ -224,6 +229,9 @@ export const VoteDetails = ({
<Icon name={'info-sign'} /> <Icon name={'info-sign'} />
<div>{t('connectAVegaWalletToVote')}</div> <div>{t('connectAVegaWalletToVote')}</div>
</div> </div>
<ExternalLink href="https://blog.vega.xyz/how-to-vote-on-vega-2195d1e52ec5">
{t('findOutMoreAboutHowToVote')}
</ExternalLink>
</div> </div>
<ConnectToVega /> <ConnectToVega />
</RoundedWrapper> </RoundedWrapper>
@@ -16,7 +16,6 @@ const mockAppState: AppState = {
totalSupply: mockTotalSupply, totalSupply: mockTotalSupply,
vegaWalletOverlay: false, vegaWalletOverlay: false,
vegaWalletManageOverlay: false, vegaWalletManageOverlay: false,
ethConnectOverlay: false,
transactionOverlay: false, transactionOverlay: false,
bannerMessage: '', bannerMessage: '',
disconnectNotice: false, disconnectNotice: false,
@@ -20,8 +20,11 @@ import { useProtocolUpgradeProposalsQuery } from '@vegaprotocol/proposals';
const orderByDate = (arr: ProposalFieldsFragment[]) => const orderByDate = (arr: ProposalFieldsFragment[]) =>
orderBy( orderBy(
arr, arr,
[(p) => new Date(p?.terms?.closingDatetime).getTime(), (p) => p.id], [
['desc', 'desc'] (p) => new Date(p?.terms?.closingDatetime).getTime(),
(p) => new Date(p?.datetime).getTime(),
],
['asc', 'asc']
); );
const orderByUpgradeBlockHeight = ( const orderByUpgradeBlockHeight = (
@@ -10,9 +10,10 @@ import type { MockedResponse } from '@apollo/client/testing';
import { NetworkParamsDocument } from '@vegaprotocol/network-parameters'; import { NetworkParamsDocument } from '@vegaprotocol/network-parameters';
jest.mock('@vegaprotocol/environment', () => ({ jest.mock('@vegaprotocol/environment', () => ({
useEnvironment: () => ({ ...jest.requireActual('@vegaprotocol/environment'),
VEGA_DOCS_URL: 'https://docs.vega.xyz', DocsLinks: {
}), PROPOSALS_GUIDE: 'https://docs.vega.xyz/tutorials/proposals',
},
})); }));
const updateMarketNetworkParamsQueryMock: MockedResponse<NetworkParamsQuery> = { const updateMarketNetworkParamsQueryMock: MockedResponse<NetworkParamsQuery> = {
@@ -5,7 +5,7 @@ import {
useProposalSubmit, useProposalSubmit,
deadlineToRoundedHours, deadlineToRoundedHours,
} from '@vegaprotocol/proposals'; } from '@vegaprotocol/proposals';
import { useEnvironment } from '@vegaprotocol/environment'; import { useEnvironment, DocsLinks } from '@vegaprotocol/environment';
import { import {
ProposalFormDescription, ProposalFormDescription,
ProposalFormSubheader, ProposalFormSubheader,
@@ -17,7 +17,6 @@ import {
import { ProposalMinRequirements } from '../../components/shared'; import { ProposalMinRequirements } from '../../components/shared';
import { AsyncRenderer, ExternalLink } from '@vegaprotocol/ui-toolkit'; import { AsyncRenderer, ExternalLink } from '@vegaprotocol/ui-toolkit';
import { Heading } from '../../../../components/heading'; import { Heading } from '../../../../components/heading';
import { createDocsLinks } from '@vegaprotocol/utils';
import { import {
NetworkParams, NetworkParams,
useNetworkParams, useNetworkParams,
@@ -42,7 +41,7 @@ export const ProposeFreeform = () => {
NetworkParams.governance_proposal_freeform_minProposerBalance, NetworkParams.governance_proposal_freeform_minProposerBalance,
NetworkParams.spam_protection_proposal_min_tokens, NetworkParams.spam_protection_proposal_min_tokens,
]); ]);
const { VEGA_DOCS_URL, VEGA_EXPLORER_URL } = useEnvironment(); const { VEGA_EXPLORER_URL } = useEnvironment();
const { t } = useTranslation(); const { t } = useTranslation();
const { const {
register, register,
@@ -111,17 +110,13 @@ export const ProposeFreeform = () => {
userAction={ProposalUserAction.CREATE} userAction={ProposalUserAction.CREATE}
/> />
{VEGA_DOCS_URL && ( {DocsLinks && (
<p className="text-sm" data-testid="proposal-docs-link"> <p className="text-sm" data-testid="proposal-docs-link">
<span className="mr-1">{t('ProposalTermsText')}</span> <span className="mr-1">{t('ProposalTermsText')}</span>
<ExternalLink <ExternalLink
href={`${ href={`${DocsLinks.PROPOSALS_GUIDE}${DOCS_LINK}`}
createDocsLinks(VEGA_DOCS_URL).PROPOSALS_GUIDE
}${DOCS_LINK}`}
target="_blank" target="_blank"
>{`${ >{`${DocsLinks.PROPOSALS_GUIDE}${DOCS_LINK}`}</ExternalLink>
createDocsLinks(VEGA_DOCS_URL).PROPOSALS_GUIDE
}${DOCS_LINK}`}</ExternalLink>
</p> </p>
)} )}
@@ -10,9 +10,10 @@ import { NetworkParamsDocument } from '@vegaprotocol/network-parameters';
import type { MockedResponse } from '@apollo/client/testing'; import type { MockedResponse } from '@apollo/client/testing';
jest.mock('@vegaprotocol/environment', () => ({ jest.mock('@vegaprotocol/environment', () => ({
useEnvironment: () => ({ ...jest.requireActual('@vegaprotocol/environment'),
VEGA_DOCS_URL: 'https://docs.vega.xyz', DocsLinks: {
}), PROPOSALS_GUIDE: 'https://docs.vega.xyz/tutorials/proposals',
},
})); }));
const updateMarketNetworkParamsQueryMock: MockedResponse<NetworkParamsQuery> = { const updateMarketNetworkParamsQueryMock: MockedResponse<NetworkParamsQuery> = {
@@ -1,10 +1,7 @@
import { useState } from 'react'; import { useState } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { useForm } from 'react-hook-form'; import { useForm } from 'react-hook-form';
import { import { suitableForSyntaxHighlighter } from '@vegaprotocol/utils';
createDocsLinks,
suitableForSyntaxHighlighter,
} from '@vegaprotocol/utils';
import { useNetworkParams } from '@vegaprotocol/network-parameters'; import { useNetworkParams } from '@vegaprotocol/network-parameters';
import { import {
getClosingTimestamp, getClosingTimestamp,
@@ -12,7 +9,7 @@ import {
useProposalSubmit, useProposalSubmit,
doesValueEquateToParam, doesValueEquateToParam,
} from '@vegaprotocol/proposals'; } from '@vegaprotocol/proposals';
import { useEnvironment } from '@vegaprotocol/environment'; import { useEnvironment, DocsLinks } from '@vegaprotocol/environment';
import { import {
ProposalFormDescription, ProposalFormDescription,
ProposalFormSubheader, ProposalFormSubheader,
@@ -84,7 +81,7 @@ export const ProposeNetworkParameter = () => {
loading: networkParamsLoading, loading: networkParamsLoading,
error: networkParamsError, error: networkParamsError,
} = useNetworkParams(); } = useNetworkParams();
const { VEGA_EXPLORER_URL, VEGA_DOCS_URL } = useEnvironment(); const { VEGA_EXPLORER_URL } = useEnvironment();
const { t } = useTranslation(); const { t } = useTranslation();
const { const {
register, register,
@@ -175,17 +172,13 @@ export const ProposeNetworkParameter = () => {
userAction={ProposalUserAction.CREATE} userAction={ProposalUserAction.CREATE}
/> />
{VEGA_DOCS_URL && ( {DocsLinks && (
<p className="text-sm" data-testid="proposal-docs-link"> <p className="text-sm" data-testid="proposal-docs-link">
<span className="mr-1">{t('ProposalTermsText')}</span> <span className="mr-1">{t('ProposalTermsText')}</span>
<ExternalLink <ExternalLink
href={`${ href={`${DocsLinks.PROPOSALS_GUIDE}${DOCS_LINK}`}
createDocsLinks(VEGA_DOCS_URL).PROPOSALS_GUIDE
}${DOCS_LINK}`}
target="_blank" target="_blank"
>{`${ >{`${DocsLinks.PROPOSALS_GUIDE}${DOCS_LINK}`}</ExternalLink>
createDocsLinks(VEGA_DOCS_URL).PROPOSALS_GUIDE
}${DOCS_LINK}`}</ExternalLink>
</p> </p>
)} )}
@@ -10,9 +10,10 @@ import type { MockedResponse } from '@apollo/client/testing';
import { NetworkParamsDocument } from '@vegaprotocol/network-parameters'; import { NetworkParamsDocument } from '@vegaprotocol/network-parameters';
jest.mock('@vegaprotocol/environment', () => ({ jest.mock('@vegaprotocol/environment', () => ({
useEnvironment: () => ({ ...jest.requireActual('@vegaprotocol/environment'),
VEGA_DOCS_URL: 'https://docs.vega.xyz', DocsLinks: {
}), PROPOSALS_GUIDE: 'https://docs.vega.xyz/tutorials/proposals',
},
})); }));
const newAssetNetworkParamsQueryMock: MockedResponse<NetworkParamsQuery> = { const newAssetNetworkParamsQueryMock: MockedResponse<NetworkParamsQuery> = {
@@ -7,8 +7,8 @@ import {
useProposalSubmit, useProposalSubmit,
doesValueEquateToParam, doesValueEquateToParam,
} from '@vegaprotocol/proposals'; } from '@vegaprotocol/proposals';
import { useEnvironment } from '@vegaprotocol/environment'; import { useEnvironment, DocsLinks } from '@vegaprotocol/environment';
import { createDocsLinks, validateJson } from '@vegaprotocol/utils'; import { validateJson } from '@vegaprotocol/utils';
import { import {
NetworkParams, NetworkParams,
useNetworkParams, useNetworkParams,
@@ -53,7 +53,7 @@ export const ProposeNewAsset = () => {
NetworkParams.governance_proposal_asset_minProposerBalance, NetworkParams.governance_proposal_asset_minProposerBalance,
NetworkParams.spam_protection_proposal_min_tokens, NetworkParams.spam_protection_proposal_min_tokens,
]); ]);
const { VEGA_EXPLORER_URL, VEGA_DOCS_URL } = useEnvironment(); const { VEGA_EXPLORER_URL } = useEnvironment();
const { t } = useTranslation(); const { t } = useTranslation();
const { const {
register, register,
@@ -142,17 +142,13 @@ export const ProposeNewAsset = () => {
userAction={ProposalUserAction.CREATE} userAction={ProposalUserAction.CREATE}
/> />
{VEGA_DOCS_URL && ( {DocsLinks && (
<p className="text-sm" data-testid="proposal-docs-link"> <p className="text-sm" data-testid="proposal-docs-link">
<span className="mr-1">{t('ProposalTermsText')}</span> <span className="mr-1">{t('ProposalTermsText')}</span>
<ExternalLink <ExternalLink
href={`${ href={`${DocsLinks.PROPOSALS_GUIDE}${DOCS_LINK}`}
createDocsLinks(VEGA_DOCS_URL).PROPOSALS_GUIDE
}${DOCS_LINK}`}
target="_blank" target="_blank"
>{`${ >{`${DocsLinks.PROPOSALS_GUIDE}${DOCS_LINK}`}</ExternalLink>
createDocsLinks(VEGA_DOCS_URL).PROPOSALS_GUIDE
}${DOCS_LINK}`}</ExternalLink>
</p> </p>
)} )}
@@ -10,9 +10,10 @@ import type { NetworkParamsQuery } from '@vegaprotocol/network-parameters';
import { NetworkParamsDocument } from '@vegaprotocol/network-parameters'; import { NetworkParamsDocument } from '@vegaprotocol/network-parameters';
jest.mock('@vegaprotocol/environment', () => ({ jest.mock('@vegaprotocol/environment', () => ({
useEnvironment: () => ({ ...jest.requireActual('@vegaprotocol/environment'),
VEGA_DOCS_URL: 'https://docs.vega.xyz', DocsLinks: {
}), PROPOSALS_GUIDE: 'https://docs.vega.xyz/tutorials/proposals',
},
})); }));
const newMarketNetworkParamsQueryMock: MockedResponse<NetworkParamsQuery> = { const newMarketNetworkParamsQueryMock: MockedResponse<NetworkParamsQuery> = {
@@ -6,8 +6,8 @@ import {
useProposalSubmit, useProposalSubmit,
doesValueEquateToParam, doesValueEquateToParam,
} from '@vegaprotocol/proposals'; } from '@vegaprotocol/proposals';
import { useEnvironment } from '@vegaprotocol/environment'; import { useEnvironment, DocsLinks } from '@vegaprotocol/environment';
import { createDocsLinks, validateJson } from '@vegaprotocol/utils'; import { validateJson } from '@vegaprotocol/utils';
import { import {
NetworkParams, NetworkParams,
useNetworkParams, useNetworkParams,
@@ -51,7 +51,7 @@ export const ProposeNewMarket = () => {
NetworkParams.governance_proposal_market_minProposerBalance, NetworkParams.governance_proposal_market_minProposerBalance,
NetworkParams.spam_protection_proposal_min_tokens, NetworkParams.spam_protection_proposal_min_tokens,
]); ]);
const { VEGA_EXPLORER_URL, VEGA_DOCS_URL } = useEnvironment(); const { VEGA_EXPLORER_URL } = useEnvironment();
const { t } = useTranslation(); const { t } = useTranslation();
const { const {
register, register,
@@ -132,17 +132,13 @@ export const ProposeNewMarket = () => {
userAction={ProposalUserAction.CREATE} userAction={ProposalUserAction.CREATE}
/> />
{VEGA_DOCS_URL && ( {DocsLinks && (
<p className="text-sm" data-testid="proposal-docs-link"> <p className="text-sm" data-testid="proposal-docs-link">
<span className="mr-1">{t('ProposalTermsText')}</span> <span className="mr-1">{t('ProposalTermsText')}</span>
<ExternalLink <ExternalLink
href={`${ href={`${DocsLinks.PROPOSALS_GUIDE}${DOCS_LINK}`}
createDocsLinks(VEGA_DOCS_URL).PROPOSALS_GUIDE
}${DOCS_LINK}`}
target="_blank" target="_blank"
>{`${ >{`${DocsLinks.PROPOSALS_GUIDE}${DOCS_LINK}`}</ExternalLink>
createDocsLinks(VEGA_DOCS_URL).PROPOSALS_GUIDE
}${DOCS_LINK}`}</ExternalLink>
</p> </p>
)} )}
@@ -2,12 +2,11 @@ import Routes from '../../routes';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { Link } from 'react-router-dom'; import { Link } from 'react-router-dom';
import { ExternalLink } from '@vegaprotocol/ui-toolkit'; import { ExternalLink } from '@vegaprotocol/ui-toolkit';
import { useEnvironment } from '@vegaprotocol/environment'; import { useEnvironment, DocsLinks } from '@vegaprotocol/environment';
import { Heading } from '../../../components/heading'; import { Heading } from '../../../components/heading';
import { createDocsLinks } from '@vegaprotocol/utils';
export const Propose = () => { export const Propose = () => {
const { VEGA_DOCS_URL, VEGA_EXPLORER_URL } = useEnvironment(); const { VEGA_EXPLORER_URL } = useEnvironment();
const { t } = useTranslation(); const { t } = useTranslation();
return ( return (
@@ -15,14 +14,11 @@ export const Propose = () => {
<section className="pb-6"> <section className="pb-6">
<Heading title={t('NewProposal')} /> <Heading title={t('NewProposal')} />
<div className="text-sm"> <div className="text-sm">
{VEGA_DOCS_URL && ( {DocsLinks && (
<p> <p>
<span className="mr-1">{t('ProposalTermsText')}</span> <span className="mr-1">{t('ProposalTermsText')}</span>
<ExternalLink <ExternalLink href={DocsLinks.PROPOSALS_GUIDE} target="_blank">
href={createDocsLinks(VEGA_DOCS_URL).PROPOSALS_GUIDE} {DocsLinks.PROPOSALS_GUIDE}
target="_blank"
>
{createDocsLinks(VEGA_DOCS_URL).PROPOSALS_GUIDE}
</ExternalLink> </ExternalLink>
</p> </p>
)} )}
@@ -1,6 +1,6 @@
import { useForm } from 'react-hook-form'; import { useForm } from 'react-hook-form';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { useEnvironment } from '@vegaprotocol/environment'; import { useEnvironment, DocsLinks } from '@vegaprotocol/environment';
import { Heading } from '../../../../components/heading'; import { Heading } from '../../../../components/heading';
import { import {
AsyncRenderer, AsyncRenderer,
@@ -9,7 +9,7 @@ import {
InputError, InputError,
TextArea, TextArea,
} from '@vegaprotocol/ui-toolkit'; } from '@vegaprotocol/ui-toolkit';
import { createDocsLinks, validateJson } from '@vegaprotocol/utils'; import { validateJson } from '@vegaprotocol/utils';
import { import {
NetworkParams, NetworkParams,
useNetworkParams, useNetworkParams,
@@ -39,7 +39,7 @@ export const ProposeRaw = () => {
NetworkParams.governance_proposal_freeform_minProposerBalance, NetworkParams.governance_proposal_freeform_minProposerBalance,
NetworkParams.spam_protection_proposal_min_tokens, NetworkParams.spam_protection_proposal_min_tokens,
]); ]);
const { VEGA_EXPLORER_URL, VEGA_DOCS_URL } = useEnvironment(); const { VEGA_EXPLORER_URL } = useEnvironment();
const { t } = useTranslation(); const { t } = useTranslation();
const { const {
register, register,
@@ -79,14 +79,11 @@ export const ProposeRaw = () => {
spamProtectionMin={params.spam_protection_proposal_min_tokens} spamProtectionMin={params.spam_protection_proposal_min_tokens}
/> />
{VEGA_DOCS_URL && ( {DocsLinks && (
<p className="text-sm" data-testid="proposal-docs-link"> <p className="text-sm" data-testid="proposal-docs-link">
<span className="mr-1">{t('ProposalTermsText')}</span> <span className="mr-1">{t('ProposalTermsText')}</span>
<ExternalLink <ExternalLink href={DocsLinks.PROPOSALS_GUIDE} target="_blank">
href={createDocsLinks(VEGA_DOCS_URL).PROPOSALS_GUIDE} {DocsLinks.PROPOSALS_GUIDE}
target="_blank"
>
{createDocsLinks(VEGA_DOCS_URL).PROPOSALS_GUIDE}
</ExternalLink> </ExternalLink>
</p> </p>
)} )}
@@ -10,9 +10,10 @@ import type { MockedResponse } from '@apollo/client/testing';
import { NetworkParamsDocument } from '@vegaprotocol/network-parameters'; import { NetworkParamsDocument } from '@vegaprotocol/network-parameters';
jest.mock('@vegaprotocol/environment', () => ({ jest.mock('@vegaprotocol/environment', () => ({
useEnvironment: () => ({ ...jest.requireActual('@vegaprotocol/environment'),
VEGA_DOCS_URL: 'https://docs.vega.xyz', DocsLinks: {
}), PROPOSALS_GUIDE: 'https://docs.vega.xyz/tutorials/proposals',
},
})); }));
const updateAssetNetworkParamsQueryMock: MockedResponse<NetworkParamsQuery> = { const updateAssetNetworkParamsQueryMock: MockedResponse<NetworkParamsQuery> = {
@@ -6,8 +6,8 @@ import {
useProposalSubmit, useProposalSubmit,
doesValueEquateToParam, doesValueEquateToParam,
} from '@vegaprotocol/proposals'; } from '@vegaprotocol/proposals';
import { useEnvironment } from '@vegaprotocol/environment'; import { useEnvironment, DocsLinks } from '@vegaprotocol/environment';
import { createDocsLinks, validateJson } from '@vegaprotocol/utils'; import { validateJson } from '@vegaprotocol/utils';
import { import {
NetworkParams, NetworkParams,
useNetworkParams, useNetworkParams,
@@ -51,7 +51,7 @@ export const ProposeUpdateAsset = () => {
NetworkParams.governance_proposal_updateAsset_minProposerBalance, NetworkParams.governance_proposal_updateAsset_minProposerBalance,
NetworkParams.spam_protection_proposal_min_tokens, NetworkParams.spam_protection_proposal_min_tokens,
]); ]);
const { VEGA_EXPLORER_URL, VEGA_DOCS_URL } = useEnvironment(); const { VEGA_EXPLORER_URL } = useEnvironment();
const { t } = useTranslation(); const { t } = useTranslation();
const { const {
register, register,
@@ -132,17 +132,13 @@ export const ProposeUpdateAsset = () => {
userAction={ProposalUserAction.CREATE} userAction={ProposalUserAction.CREATE}
/> />
{VEGA_DOCS_URL && ( {DocsLinks && (
<p className="text-sm" data-testid="proposal-docs-link"> <p className="text-sm" data-testid="proposal-docs-link">
<span className="mr-1">{t('ProposalTermsText')}</span> <span className="mr-1">{t('ProposalTermsText')}</span>
<ExternalLink <ExternalLink
href={`${ href={`${DocsLinks.PROPOSALS_GUIDE}${DOCS_LINK}`}
createDocsLinks(VEGA_DOCS_URL).PROPOSALS_GUIDE
}${DOCS_LINK}`}
target="_blank" target="_blank"
>{`${ >{`${DocsLinks.PROPOSALS_GUIDE}${DOCS_LINK}`}</ExternalLink>
createDocsLinks(VEGA_DOCS_URL).PROPOSALS_GUIDE
}${DOCS_LINK}`}</ExternalLink>
</p> </p>
)} )}
@@ -7,8 +7,8 @@ import {
getEnactmentTimestamp, getEnactmentTimestamp,
useProposalSubmit, useProposalSubmit,
} from '@vegaprotocol/proposals'; } from '@vegaprotocol/proposals';
import { useEnvironment } from '@vegaprotocol/environment'; import { useEnvironment, DocsLinks } from '@vegaprotocol/environment';
import { createDocsLinks, validateJson } from '@vegaprotocol/utils'; import { validateJson } from '@vegaprotocol/utils';
import { import {
NetworkParams, NetworkParams,
useNetworkParams, useNetworkParams,
@@ -98,7 +98,7 @@ export const ProposeUpdateMarket = () => {
const [selectedMarket, setSelectedMarket] = useState<string | undefined>( const [selectedMarket, setSelectedMarket] = useState<string | undefined>(
undefined undefined
); );
const { VEGA_EXPLORER_URL, VEGA_DOCS_URL } = useEnvironment(); const { VEGA_EXPLORER_URL } = useEnvironment();
const { t } = useTranslation(); const { t } = useTranslation();
const { const {
register, register,
@@ -181,17 +181,13 @@ export const ProposeUpdateMarket = () => {
userAction={ProposalUserAction.CREATE} userAction={ProposalUserAction.CREATE}
/> />
{VEGA_DOCS_URL && ( {DocsLinks && (
<p className="text-sm" data-testid="proposal-docs-link"> <p className="text-sm" data-testid="proposal-docs-link">
<span className="mr-1">{t('ProposalTermsText')}</span> <span className="mr-1">{t('ProposalTermsText')}</span>
<ExternalLink <ExternalLink
href={`${ href={`${DocsLinks.PROPOSALS_GUIDE}${DOCS_LINK}`}
createDocsLinks(VEGA_DOCS_URL).PROPOSALS_GUIDE
}${DOCS_LINK}`}
target="_blank" target="_blank"
>{`${ >{`${DocsLinks.PROPOSALS_GUIDE}${DOCS_LINK}`}</ExternalLink>
createDocsLinks(VEGA_DOCS_URL).PROPOSALS_GUIDE
}${DOCS_LINK}`}</ExternalLink>
</p> </p>
)} )}
@@ -54,22 +54,16 @@ export const TrancheItem = ({
{formatNumber(total, 2)} {formatNumber(total, 2)}
</span> </span>
</div> </div>
<table className="w-full"> <div className="grid grid-cols-2 my-2">
<tbody> <div>
<tr> <span>{t('Starts unlocking')}:</span>{' '}
<td>{t('Starts unlocking')}</td> <span>{format(tranche.tranche_start, DATE_FORMAT_LONG)}</span>
<td className="text-right"> </div>
{format(tranche.tranche_start, DATE_FORMAT_LONG)} <div className="justify-self-end">
</td> <span>{t('Fully unlocked')}:</span>{' '}
</tr> <span>{format(tranche.tranche_end, DATE_FORMAT_LONG)}</span>
<tr> </div>
<td>{t('Fully unlocked')}</td> </div>
<td className="text-right">
{format(tranche.tranche_end, DATE_FORMAT_LONG)}
</td>
</tr>
</tbody>
</table>
<LockedProgress <LockedProgress
locked={locked} locked={locked}
unlocked={unlocked} unlocked={unlocked}
@@ -25,7 +25,7 @@ const mockData = {
amount: '0', amount: '0',
percentageOfTotal: '0', percentageOfTotal: '0',
}, },
ACCOUNT_TYPE_FEES_LIQUIDITY: { ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES: {
amount: '0', amount: '0',
percentageOfTotal: '0', percentageOfTotal: '0',
}, },
@@ -55,7 +55,9 @@ describe('EpochIndividualRewardsTable', () => {
expect( expect(
getByTestId('ACCOUNT_TYPE_REWARD_MAKER_RECEIVED_FEES') getByTestId('ACCOUNT_TYPE_REWARD_MAKER_RECEIVED_FEES')
).toBeInTheDocument(); ).toBeInTheDocument();
expect(getByTestId('ACCOUNT_TYPE_FEES_LIQUIDITY')).toBeInTheDocument(); expect(
getByTestId('ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES')
).toBeInTheDocument();
expect( expect(
getByTestId('ACCOUNT_TYPE_REWARD_MARKET_PROPOSERS') getByTestId('ACCOUNT_TYPE_REWARD_MARKET_PROPOSERS')
).toBeInTheDocument(); ).toBeInTheDocument();
@@ -24,7 +24,7 @@ describe('generateEpochIndividualRewardsList', () => {
}; };
const reward3: RewardFieldsFragment = { const reward3: RewardFieldsFragment = {
rewardType: AccountType.ACCOUNT_TYPE_FEES_LIQUIDITY, rewardType: AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES,
amount: '200', amount: '200',
percentageOfTotal: '0.2', percentageOfTotal: '0.2',
receivedAt: new Date(), receivedAt: new Date(),
@@ -34,7 +34,7 @@ describe('generateEpochIndividualRewardsList', () => {
}; };
const reward4: RewardFieldsFragment = { const reward4: RewardFieldsFragment = {
rewardType: AccountType.ACCOUNT_TYPE_FEES_LIQUIDITY, rewardType: AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES,
amount: '100', amount: '100',
percentageOfTotal: '0.1', percentageOfTotal: '0.1',
receivedAt: new Date(), receivedAt: new Date(),
@@ -44,7 +44,7 @@ describe('generateEpochIndividualRewardsList', () => {
}; };
const reward5: RewardFieldsFragment = { const reward5: RewardFieldsFragment = {
rewardType: AccountType.ACCOUNT_TYPE_FEES_LIQUIDITY, rewardType: AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES,
amount: '150', amount: '150',
percentageOfTotal: '0.15', percentageOfTotal: '0.15',
receivedAt: new Date(), receivedAt: new Date(),
@@ -105,7 +105,7 @@ describe('generateEpochIndividualRewardsList', () => {
amount: '0', amount: '0',
percentageOfTotal: '0', percentageOfTotal: '0',
}, },
[AccountType.ACCOUNT_TYPE_FEES_LIQUIDITY]: { [AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES]: {
amount: '0', amount: '0',
percentageOfTotal: '0', percentageOfTotal: '0',
}, },
@@ -172,7 +172,7 @@ describe('generateEpochIndividualRewardsList', () => {
amount: '0', amount: '0',
percentageOfTotal: '0', percentageOfTotal: '0',
}, },
[AccountType.ACCOUNT_TYPE_FEES_LIQUIDITY]: { [AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES]: {
amount: '200', amount: '200',
percentageOfTotal: '0.2', percentageOfTotal: '0.2',
}, },
@@ -202,7 +202,7 @@ describe('generateEpochIndividualRewardsList', () => {
amount: '0', amount: '0',
percentageOfTotal: '0', percentageOfTotal: '0',
}, },
[AccountType.ACCOUNT_TYPE_FEES_LIQUIDITY]: { [AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES]: {
amount: '0', amount: '0',
percentageOfTotal: '0', percentageOfTotal: '0',
}, },
@@ -237,7 +237,7 @@ describe('generateEpochIndividualRewardsList', () => {
amount: '0', amount: '0',
percentageOfTotal: '0', percentageOfTotal: '0',
}, },
[AccountType.ACCOUNT_TYPE_FEES_LIQUIDITY]: { [AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES]: {
amount: '100', amount: '100',
percentageOfTotal: '0.1', percentageOfTotal: '0.1',
}, },
@@ -285,7 +285,7 @@ describe('generateEpochIndividualRewardsList', () => {
amount: '0', amount: '0',
percentageOfTotal: '0', percentageOfTotal: '0',
}, },
[AccountType.ACCOUNT_TYPE_FEES_LIQUIDITY]: { [AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES]: {
amount: '150', amount: '150',
percentageOfTotal: '0.15', percentageOfTotal: '0.15',
}, },
@@ -320,7 +320,7 @@ describe('generateEpochIndividualRewardsList', () => {
amount: '0', amount: '0',
percentageOfTotal: '0', percentageOfTotal: '0',
}, },
[AccountType.ACCOUNT_TYPE_FEES_LIQUIDITY]: { [AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES]: {
amount: '200', amount: '200',
percentageOfTotal: '0.2', percentageOfTotal: '0.2',
}, },
@@ -350,7 +350,7 @@ describe('generateEpochIndividualRewardsList', () => {
amount: '0', amount: '0',
percentageOfTotal: '0', percentageOfTotal: '0',
}, },
[AccountType.ACCOUNT_TYPE_FEES_LIQUIDITY]: { [AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES]: {
amount: '0', amount: '0',
percentageOfTotal: '0', percentageOfTotal: '0',
}, },
@@ -395,7 +395,7 @@ describe('generateEpochIndividualRewardsList', () => {
amount: '0', amount: '0',
percentageOfTotal: '0', percentageOfTotal: '0',
}, },
[AccountType.ACCOUNT_TYPE_FEES_LIQUIDITY]: { [AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES]: {
amount: '100', amount: '100',
percentageOfTotal: '0.1', percentageOfTotal: '0.1',
}, },
@@ -79,7 +79,24 @@ export const generateEpochIndividualRewardsList = ({
epoch?.rewards.push(asset); epoch?.rewards.push(asset);
} }
asset.rewardTypes[rewardType] = { amount, percentageOfTotal }; if (!asset.rewardTypes[rewardType]) {
asset.rewardTypes[rewardType] = { amount, percentageOfTotal };
} else {
const previousAmount = asset.rewardTypes[rewardType]?.amount;
const previousPercentageOfTotal =
asset.rewardTypes[rewardType]?.percentageOfTotal;
asset.rewardTypes[rewardType] = {
amount: previousAmount
? new BigNumber(previousAmount).plus(amount).toString()
: amount,
percentageOfTotal: previousPercentageOfTotal
? new BigNumber(previousPercentageOfTotal)
.plus(percentageOfTotal)
.toString()
: percentageOfTotal,
};
}
// totalAmount is the sum of all rewardTypes amounts // totalAmount is the sum of all rewardTypes amounts
asset.totalAmount = Object.values(asset.rewardTypes).reduce( asset.totalAmount = Object.values(asset.rewardTypes).reduce(
@@ -29,7 +29,7 @@ const rewardsList = [
amount: '0', amount: '0',
}, },
{ {
rewardType: AccountType.ACCOUNT_TYPE_FEES_LIQUIDITY, rewardType: AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES,
amount: '0', amount: '0',
}, },
{ {
@@ -78,7 +78,9 @@ describe('EpochTotalRewardsTable', () => {
expect( expect(
getByTestId('ACCOUNT_TYPE_REWARD_MAKER_RECEIVED_FEES') getByTestId('ACCOUNT_TYPE_REWARD_MAKER_RECEIVED_FEES')
).toBeInTheDocument(); ).toBeInTheDocument();
expect(getByTestId('ACCOUNT_TYPE_FEES_LIQUIDITY')).toBeInTheDocument(); expect(
getByTestId('ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES')
).toBeInTheDocument();
expect( expect(
getByTestId('ACCOUNT_TYPE_REWARD_MARKET_PROPOSERS') getByTestId('ACCOUNT_TYPE_REWARD_MARKET_PROPOSERS')
).toBeInTheDocument(); ).toBeInTheDocument();
@@ -162,9 +162,10 @@ describe('generateEpochAssetRewardsList', () => {
}, },
], ],
[ [
AccountType.ACCOUNT_TYPE_FEES_LIQUIDITY, AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES,
{ {
rewardType: AccountType.ACCOUNT_TYPE_FEES_LIQUIDITY, rewardType:
AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES,
amount: '0', amount: '0',
}, },
], ],
@@ -227,7 +228,7 @@ describe('generateEpochAssetRewardsList', () => {
node: { node: {
epoch: 2, epoch: 2,
assetId: '1', assetId: '1',
rewardType: AccountType.ACCOUNT_TYPE_FEES_LIQUIDITY, rewardType: AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES,
amount: '5', amount: '5',
}, },
}, },
@@ -287,9 +288,10 @@ describe('generateEpochAssetRewardsList', () => {
}, },
], ],
[ [
AccountType.ACCOUNT_TYPE_FEES_LIQUIDITY, AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES,
{ {
rewardType: AccountType.ACCOUNT_TYPE_FEES_LIQUIDITY, rewardType:
AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES,
amount: '0', amount: '0',
}, },
], ],
@@ -351,9 +353,10 @@ describe('generateEpochAssetRewardsList', () => {
}, },
], ],
[ [
AccountType.ACCOUNT_TYPE_FEES_LIQUIDITY, AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES,
{ {
rewardType: AccountType.ACCOUNT_TYPE_FEES_LIQUIDITY, rewardType:
AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES,
amount: '5', amount: '5',
}, },
], ],
@@ -416,7 +419,7 @@ describe('generateEpochAssetRewardsList', () => {
node: { node: {
epoch: 2, epoch: 2,
assetId: '1', assetId: '1',
rewardType: AccountType.ACCOUNT_TYPE_FEES_LIQUIDITY, rewardType: AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES,
amount: '6', amount: '6',
}, },
}, },
@@ -424,7 +427,7 @@ describe('generateEpochAssetRewardsList', () => {
node: { node: {
epoch: 2, epoch: 2,
assetId: '1', assetId: '1',
rewardType: AccountType.ACCOUNT_TYPE_FEES_LIQUIDITY, rewardType: AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES,
amount: '27', amount: '27',
}, },
}, },
@@ -497,9 +500,10 @@ describe('generateEpochAssetRewardsList', () => {
}, },
], ],
[ [
AccountType.ACCOUNT_TYPE_FEES_LIQUIDITY, AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES,
{ {
rewardType: AccountType.ACCOUNT_TYPE_FEES_LIQUIDITY, rewardType:
AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES,
amount: '33', amount: '33',
}, },
], ],
@@ -561,9 +565,10 @@ describe('generateEpochAssetRewardsList', () => {
}, },
], ],
[ [
AccountType.ACCOUNT_TYPE_FEES_LIQUIDITY, AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES,
{ {
rewardType: AccountType.ACCOUNT_TYPE_FEES_LIQUIDITY, rewardType:
AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES,
amount: '0', amount: '0',
}, },
], ],
@@ -637,9 +642,10 @@ describe('generateEpochAssetRewardsList', () => {
}, },
], ],
[ [
AccountType.ACCOUNT_TYPE_FEES_LIQUIDITY, AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES,
{ {
rewardType: AccountType.ACCOUNT_TYPE_FEES_LIQUIDITY, rewardType:
AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES,
amount: '0', amount: '0',
}, },
], ],
@@ -11,7 +11,6 @@ import {
ExternalLink, ExternalLink,
} from '@vegaprotocol/ui-toolkit'; } from '@vegaprotocol/ui-toolkit';
import { useVegaWallet } from '@vegaprotocol/wallet'; import { useVegaWallet } from '@vegaprotocol/wallet';
import { createDocsLinks } from '@vegaprotocol/utils';
import { import {
useNetworkParams, useNetworkParams,
NetworkParams, NetworkParams,
@@ -21,7 +20,7 @@ import { EpochCountdown } from '../../../components/epoch-countdown';
import { Heading, SubHeading } from '../../../components/heading'; import { Heading, SubHeading } from '../../../components/heading';
import { EpochIndividualRewards } from '../epoch-individual-rewards/epoch-individual-rewards'; import { EpochIndividualRewards } from '../epoch-individual-rewards/epoch-individual-rewards';
import { useRefreshAfterEpoch } from '../../../hooks/use-refresh-after-epoch'; import { useRefreshAfterEpoch } from '../../../hooks/use-refresh-after-epoch';
import { useEnvironment } from '@vegaprotocol/environment'; import { DocsLinks } from '@vegaprotocol/environment';
import { ConnectToSeeRewards } from '../connect-to-see-rewards'; import { ConnectToSeeRewards } from '../connect-to-see-rewards';
import { EpochTotalRewards } from '../epoch-total-rewards/epoch-total-rewards'; import { EpochTotalRewards } from '../epoch-total-rewards/epoch-total-rewards';
@@ -29,7 +28,6 @@ type RewardsView = 'total' | 'individual';
export const RewardsPage = () => { export const RewardsPage = () => {
const { t } = useTranslation(); const { t } = useTranslation();
const { VEGA_DOCS_URL } = useEnvironment();
const { pubKey, pubKeys } = useVegaWallet(); const { pubKey, pubKeys } = useVegaWallet();
const [toggleRewardsView, setToggleRewardsView] = const [toggleRewardsView, setToggleRewardsView] =
useState<RewardsView>('total'); useState<RewardsView>('total');
@@ -68,9 +66,9 @@ export const RewardsPage = () => {
<Heading title={t('pageTitleRewards')} /> <Heading title={t('pageTitleRewards')} />
<p className="mb-12"> <p className="mb-12">
{t('rewardsIntro')}{' '} {t('rewardsIntro')}{' '}
{VEGA_DOCS_URL && ( {DocsLinks && (
<ExternalLink <ExternalLink
href={createDocsLinks(VEGA_DOCS_URL).REWARDS_GUIDE} href={DocsLinks.REWARDS_GUIDE}
target="_blank" target="_blank"
data-testid="rewards-guide-link" data-testid="rewards-guide-link"
className="text-white" className="text-white"
@@ -26,7 +26,7 @@ export const RowAccountTypes = {
columnTitle: 'rewardsColPriceMakingHeader', columnTitle: 'rewardsColPriceMakingHeader',
description: 'rewardsColPriceMakingTooltip', description: 'rewardsColPriceMakingTooltip',
}, },
[AccountType.ACCOUNT_TYPE_FEES_LIQUIDITY]: { [AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES]: {
columnTitle: 'rewardsColLiquidityProvisionHeader', columnTitle: 'rewardsColLiquidityProvisionHeader',
description: 'rewardsColLiquidityProvisionTooltip', description: 'rewardsColLiquidityProvisionTooltip',
}, },
+7 -4
View File
@@ -229,7 +229,7 @@ const redirects = [
const routerConfig = [ const routerConfig = [
{ {
path: Routes.HOME, path: Routes.HOME,
element: <LazyHome name="Token" />, element: <LazyHome name="Home" />,
}, },
{ {
path: Routes.PROPOSALS, path: Routes.PROPOSALS,
@@ -291,7 +291,7 @@ const routerConfig = [
// Not lazy as loaded when a user first hits the site // Not lazy as loaded when a user first hits the site
children: [ children: [
{ {
element: <Home name="Home" />, element: <Home name="Token" />,
index: true, index: true,
}, },
{ {
@@ -325,8 +325,11 @@ const routerConfig = [
}, },
], ],
}, },
{ path: 'associate', element: <LazyStakingAssociate /> }, { path: 'associate', element: <LazyStakingAssociate name="Associate" /> },
{ path: 'disassociate', element: <LazyStakingDisassociate /> }, {
path: 'disassociate',
element: <LazyStakingDisassociate name="Disassociate" />,
},
], ],
}, },
{ {
@@ -1,12 +1,14 @@
import { useEthereumConfig } from '@vegaprotocol/web3'; import { useEthereumConfig } from '@vegaprotocol/web3';
import { StakingWalletsContainer } from './components/staking-wallets-container/staking-wallets-container'; import { StakingWalletsContainer } from './components/staking-wallets-container';
import { AssociatePage } from './associate-page'; import { AssociatePage } from './associate-page';
import { AssociatePageNoVega } from './associate-page-no-vega'; import { AssociatePageNoVega } from './associate-page-no-vega';
import { Heading } from '../../../components/heading'; import { Heading } from '../../../components/heading';
import React from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { useDocumentTitle } from '../../../hooks/use-document-title';
import type { RouteChildProps } from '../../index';
export const AssociateContainer = () => { export const AssociateContainer = ({ name }: RouteChildProps) => {
useDocumentTitle(name);
const { t } = useTranslation(); const { t } = useTranslation();
const { config } = useEthereumConfig(); const { config } = useEthereumConfig();
@@ -39,7 +39,7 @@ jest.mock('@vegaprotocol/wallet', () => ({
})); }));
const renderComponent = () => { const renderComponent = () => {
return render(<Disassociate />); return render(<Disassociate name="Disassociate" />);
}; };
describe('Disassociate', () => { describe('Disassociate', () => {
@@ -3,10 +3,12 @@ import { useWeb3React } from '@web3-react/core';
import { EthConnectPrompt } from '../../../components/eth-connect-prompt'; import { EthConnectPrompt } from '../../../components/eth-connect-prompt';
import { DisassociatePage } from './components/disassociate-page'; import { DisassociatePage } from './components/disassociate-page';
import { Heading } from '../../../components/heading'; import { Heading } from '../../../components/heading';
import React from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { useDocumentTitle } from '../../../hooks/use-document-title';
import type { RouteChildProps } from '../../index';
export const DisassociateContainer = () => { export const DisassociateContainer = ({ name }: RouteChildProps) => {
useDocumentTitle(name);
const { t } = useTranslation(); const { t } = useTranslation();
const { account } = useWeb3React(); const { account } = useWeb3React();
const { pubKey } = useVegaWallet(); const { pubKey } = useVegaWallet();
@@ -1,14 +1,12 @@
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { EpochData } from './epoch-data'; import { EpochData } from './epoch-data';
import { useEnvironment } from '@vegaprotocol/environment'; import { DocsLinks } from '@vegaprotocol/environment';
import { ExternalLink } from '@vegaprotocol/ui-toolkit'; import { ExternalLink } from '@vegaprotocol/ui-toolkit';
import { createDocsLinks } from '@vegaprotocol/utils';
import { Heading } from '../../../components/heading'; import { Heading } from '../../../components/heading';
import React from 'react'; import React from 'react';
export const Staking = () => { export const Staking = () => {
const { t } = useTranslation(); const { t } = useTranslation();
const { VEGA_DOCS_URL } = useEnvironment();
return ( return (
<> <>
@@ -16,9 +14,9 @@ export const Staking = () => {
<section> <section>
<p className="mb-12"> <p className="mb-12">
{t('stakingIntro')}{' '} {t('stakingIntro')}{' '}
{VEGA_DOCS_URL && ( {DocsLinks && (
<ExternalLink <ExternalLink
href={createDocsLinks(VEGA_DOCS_URL).STAKING_GUIDE} href={DocsLinks.STAKING_GUIDE}
target="_blank" target="_blank"
data-testid="staking-guide-link" data-testid="staking-guide-link"
className="text-white" className="text-white"

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