Compare commits

..
Author SHA1 Message Date
Dariusz Majcherczyk 6334b689b9 test: move closeWelcomeDialog 2023-05-19 19:04:53 +02:00
Dariusz Majcherczyk 7b06d8a770 test: added clearAllLocalStorage 2023-05-19 19:02:53 +02:00
Dariusz Majcherczyk 4ae721ebae test: skip one test 2023-05-19 17:56:17 +02:00
Dariusz Majcherczyk 11f9fcb307 test: skip node tests 2023-05-19 17:21:16 +02:00
372 changed files with 154723 additions and 8372 deletions
+3 -2
View File
@@ -14,7 +14,8 @@ What we need to achieve and who for
## Tasks
- [ ]
- [ ]
- [ ] What do we need to do first
- [ ] and then what?
- [ ] Etc.
## Additional details / background info
+4 -7
View File
@@ -22,14 +22,11 @@ So that
## Tasks
- [ ] UX (if needed)
- [ ] Design (if needed)
- [ ] Explore and sketch
- [ ] Team and stakeholder review
- [ ] Specs reviewed and created or adjusted
- [ ] Implementation
- [ ] Testing (unit and/or e2e)
- [ ] Code review
- [ ] QA review
- [ ] Visual Design
- [ ] Team review
- [ ] Etc.
## Sketch
@@ -48,20 +48,19 @@ jobs:
token: ${{ secrets.GITHUB_TOKEN }}
id: ${{ github.event.release.id }}
body: |
___
---
# Deployments
* https://explorer.vega.xyz
* https://governance.vega.xyz
# IPFS releases
The IPFS hash of this release of the Trading app is:
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 [console.vega.xyz](https://console.vega.xyz).
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/).
+23 -72
View File
@@ -5,7 +5,6 @@ on:
branches:
- release/*
- develop
- main
tags:
- v*
pull_request:
@@ -99,66 +98,44 @@ jobs:
# See affected apps
- name: See affected apps
run: |
affected="$(yarn nx print-affected --base=${{ env.NX_BASE }} --head=${{ env.NX_HEAD }} --select=projects)"
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 )"
echo ">>>> debug"
echo "NX_BASE: ${{ env.NX_BASE }}"
echo "NX_HEAD: ${{ env.NX_HEAD }}"
echo "Affected: ${affected}"
echo "Branch slug: ${branch_slug}"
echo ">>>> eof debug"
affected="$(yarn nx print-affected --base=${{ env.NX_BASE }} --head=${{ env.NX_HEAD }} --select=projects)"
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=""
preview_governance="not deployed"
preview_trading="not deployed"
preview_explorer="not deployed"
preview_tools="not deployed"
if echo "$affected" | grep -q governance; then
echo "Governance is affected"
projects_e2e+='"governance-e2e" '
preview_governance=$(printf "https://%s.%s.vega.rocks" "governance" "$branch_slug")
fi
if echo "$affected" | grep -q trading; then
echo "Trading is affected"
projects_e2e+='"trading-e2e" '
preview_trading=$(printf "https://%s.%s.vega.rocks" "trading" "$branch_slug")
fi
if echo "$affected" | grep -q explorer; then
echo "Explorer is affected"
projects_e2e+='"explorer-e2e" '
preview_explorer=$(printf "https://%s.%s.vega.rocks" "explorer" "$branch_slug")
fi
if [[ -z "$projects_e2e" ]]; then
projects_e2e+='"governance-e2e" "trading-e2e" "explorer-e2e" '
preview_governance=$(printf "https://%s.%s.vega.rocks" "governance" "$branch_slug")
preview_trading=$(printf "https://%s.%s.vega.rocks" "trading" "$branch_slug")
preview_explorer=$(printf "https://%s.%s.vega.rocks" "explorer" "$branch_slug")
fi
projects="$(echo $projects_e2e | sed 's|-e2e||g')"
if echo "$affected" | grep -q multisig-signer; then
echo "Tools are affected"
# tools are only applicable to check previews or deploy from develop to mainnet
if [[ "${{ github.event_name }}" = "pull_request" ]]; then
echo "Deploying tools on preview"
preview_tools=$(printf "https://%s.%s.vega.rocks" "tools" "$branch_slug")
projects+=' "multisig-signer" '
else
if [[ $affected == *"governance"* ]]; then
projects_e2e+='"governance-e2e" '
preview_governance=$(printf "https://%s.%s.vega.rocks" "governance" "$branch_slug")
fi
if [[ "${{ github.ref }}" =~ .*develop$ ]]; then
echo "Deploying tools on s3"
projects+=' "multisig-signer" '
if [[ $affected == *"trading"* ]]; then
projects_e2e+='"trading-e2e" '
preview_trading=$(printf "https://%s.%s.vega.rocks" "trading" "$branch_slug")
fi
if [[ $affected == *"explorer"* ]]; then
projects_e2e+='"explorer-e2e" '
preview_explorer=$(printf "https://%s.%s.vega.rocks" "explorer" "$branch_slug")
fi
fi
projects_e2e=${projects_e2e%?}
projects_e2e=[${projects_e2e// /,}]
projects=${projects%?}
projects=[${projects// /,}]
echo PROJECTS_E2E=$projects_e2e >> $GITHUB_ENV
echo PROJECTS=$projects >> $GITHUB_ENV
echo PROJECTS=$(echo $projects_e2e | sed 's|-e2e||g') >> $GITHUB_ENV
echo PREVIEW_GOVERNANCE=$preview_governance >> $GITHUB_ENV
echo PREVIEW_TRADING=$preview_trading >> $GITHUB_ENV
echo PREVIEW_EXPLORER=$preview_explorer >> $GITHUB_ENV
echo PREVIEW_TOOLS=$preview_tools >> $GITHUB_ENV
outputs:
projects: ${{ env.PROJECTS }}
@@ -166,12 +143,11 @@ jobs:
preview_governance: ${{ env.PREVIEW_GOVERNANCE }}
preview_trading: ${{ env.PREVIEW_TRADING }}
preview_explorer: ${{ env.PREVIEW_EXPLORER }}
preview_tools: ${{ env.PREVIEW_TOOLS }}
cypress:
needs: lint-test-build
name: '(CI) cypress'
if: ${{ needs.lint-test-build.outputs.projects-e2e != '[]' }}
if: ${{ needs.lint-test-build.outputs.projects != '[]' }}
uses: ./.github/workflows/cypress-run.yml
secrets: inherit
with:
@@ -193,7 +169,6 @@ jobs:
- publish-dist
- lint-test-build
if: ${{ github.event_name == 'pull_request' }}
timeout-minutes: 60
name: '(CD) comment preview links'
steps:
- name: Find Comment
@@ -203,35 +178,6 @@ jobs:
issue-number: ${{ github.event.pull_request.number }}
body-includes: Previews
- name: Wait for deployments
run: |
# https://stackoverflow.com/questions/3183444/check-for-valid-link-url
regex='(https?|ftp|file)://[-[:alnum:]\+&@#/%?=~_|!:,.;]*[-[:alnum:]\+&@#/%=~_|]'
if [[ "${{ needs.lint-test-build.outputs.preview_governance }}" =~ $regex ]]; then
until curl -L --fail "${{ needs.lint-test-build.outputs.preview_governance }}"; do
echo "waiting for governance preview"
sleep 5
done
fi
if [[ "${{ needs.lint-test-build.outputs.preview_explorer }}" =~ $regex ]]; then
until curl -L --fail "${{ needs.lint-test-build.outputs.preview_explorer }}"; do
echo "waiting for explorer preview"
sleep 5
done
fi
if [[ "${{ needs.lint-test-build.outputs.preview_trading }}" =~ $regex ]]; then
until curl -L --fail "${{ needs.lint-test-build.outputs.preview_trading }}"; do
echo "waiting for trading preview"
sleep 5
done
fi
if [[ "${{ needs.lint-test-build.outputs.preview_tools }}" =~ $regex ]]; then
until curl -L --fail "${{ needs.lint-test-build.outputs.preview_tools }}"; do
echo "waiting for tools preview"
sleep 5
done
fi
- name: Create comment
uses: peter-evans/create-or-update-comment@v3
if: ${{ steps.fc.outputs.comment-id == 0 }}
@@ -242,11 +188,16 @@ jobs:
* governance: ${{ needs.lint-test-build.outputs.preview_governance }}
* explorer: ${{ needs.lint-test-build.outputs.preview_explorer }}
* trading: ${{ needs.lint-test-build.outputs.preview_trading }}
* tools: ${{ needs.lint-test-build.outputs.preview_tools }}
# Report single result at the end, to avoid mess with required checks in PR
cypress-check:
name: '(CI) cypress - check'
runs-on: ubuntu-latest
needs: cypress
steps:
- run: echo Done!
# Report single result at the end, to avoid mess with required checks in PR
cypress-result:
if: ${{ always() }}
needs: cypress
runs-on: ubuntu-22.04
+1 -1
View File
@@ -20,7 +20,7 @@ jobs:
project: ${{ fromJSON(inputs.projects) }}
name: ${{ matrix.project }}
runs-on: self-hosted-runner
timeout-minutes: 100
timeout-minutes: 60
steps:
# Checks if skip cache was requested
- name: Set skip-nx-cache flag
+32
View File
@@ -0,0 +1,32 @@
name: Generate tranches
on:
schedule:
- cron: '0 */6 * * *'
jobs:
master:
name: Generate Queries
runs-on: ubuntu-22.04
steps:
- name: Checkout
uses: actions/checkout@v2
with:
token: ${{ secrets.VEGA_CI_BOT_GITHUB_TOKEN }}
fetch-depth: 0
- name: Use Node.js 16
id: Node
uses: actions/setup-node@v2
with:
node-version: 16.15.1
- name: Install root dependencies
run: yarn install
- name: Generate queries
run: node ./scripts/get-tranches.js
- uses: stefanzweifel/git-auto-commit-action@v4
with:
commit_message: 'chore: update tranches'
commit_options: '--no-verify --signoff'
skip_fetch: true
skip_checkout: true
+35 -93
View File
@@ -59,47 +59,26 @@ jobs:
key: ${{ runner.os }}-cache-node-modules-${{ hashFiles('yarn.lock') }}
# https://docs.github.com/en/actions/learn-github-actions/contexts
- name: Define dist variables
if: ${{ github.event_name == 'push' }}
- name: Define variables
run: |
envName=''
domain="vega.rocks"
bucketName=''
if [[ "${{ github.ref }}" =~ .*release/.* ]]; then
envName="$(echo ${{ github.ref }} | rev | cut -d '/' -f 1 | rev)"
elif [[ "${{ github.ref }}" =~ .*develop$ ]]; then
envName="stagnet1"
if [[ "${{ matrix.app }}" = "multisig-signer" ]]; then
if [[ "${{ github.event_name }}" = "push" ]]; then
if [[ "${{ github.ref }}" =~ .*release/.* ]]; then
envName="$(echo ${{ github.ref }} | rev | cut -d '/' -f 1 | rev)"
elif [[ "${{ github.ref }}" =~ .*develop$ ]]; then
envName="stagnet1"
elif [[ "${{ matrix.app}}" = "trading" ]] && [[ ${{ startsWith(github.ref, 'refs/tags/v') && 'true' || 'false' }} = "true" ]]; then
envName="mainnet"
elif [[ "${{ github.ref }}" =~ .*main$ ]]; then
envName="mainnet"
bucketName="tools.vega.xyz"
fi
elif [[ "${{ github.ref }}" =~ .*main$ ]]; then
envName="mainnet"
elif [[ "${{ matrix.app}}" = "trading" ]] && [[ "${{ startsWith(github.ref, 'refs/tags/v') && 'true' || 'false' }}" = "true" ]]; then
envName="mainnet"
fi
if [[ "${envName}" = "mainnet" ]]; then
domain="vega.xyz"
if [[ -z "${bucketName}" ]]; then
bucketName="${{ matrix.app }}.${domain}"
if [[ "${envName}" = "mainnet" ]]; then
domain="vega.xyz"
fi
elif [[ "${envName}" = "testnet" ]]; then
domain="fairground.wtf"
if [[ -z "${bucketName}" ]]; then
bucketName="${{ matrix.app }}.${domain}"
fi
fi
if [[ -z "${bucketName}" ]]; then
bucketName="${{ matrix.app }}.${envName}.${domain}"
echo BUCKET_NAME=${bucketName} >> $GITHUB_ENV
fi
echo "bucket name: ${bucketName}"
echo "env name: ${envName}"
echo BUCKET_NAME=${bucketName} >> $GITHUB_ENV
echo ENV_NAME=${envName} >> $GITHUB_ENV
- name: Build local dist
@@ -176,7 +155,7 @@ jobs:
# bucket creation in github.com/vegaprotocol/terraform//frontend
- name: Publish dist to s3
uses: jakejarvis/s3-sync-action@master
if: ${{ github.event_name == 'push' && !startsWith(github.ref, 'refs/tags/v') && ( matrix.app != 'trading' || (matrix.app == 'trading' && !endsWith(github.ref, 'main') ) ) }}
if: ${{ github.event_name == 'push' && !startsWith(github.ref, 'refs/tags/v') }}
with:
args: --acl private --follow-symlinks --delete
env:
@@ -208,19 +187,8 @@ jobs:
-d '{"query": "mutation{triggerDeploy(siteId:\"f8f2e051-f18e-49e6-b876-0a39369dc0d8\"){id status}}"}' \
https://api.fleek.co/graphql
- name: Check out ipfs-redirect
- 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') }}
uses: actions/checkout@v3
with:
repository: 'vegaprotocol/ipfs-redirect'
path: 'ipfs-redirect'
fetch-depth: '0'
token: ${{ secrets.VEGA_CI_BOT_GITHUB_TOKEN }}
- name: Update console.vega.xyz DNS to redirect to the new console
if: ${{ matrix.app == 'trading' && github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v') }}
env:
GH_TOKEN: ${{ secrets.VEGA_CI_BOT_GITHUB_TOKEN }}
run: |
curl -L https://dist.ipfs.tech/kubo/v0.20.0/kubo_v0.20.0_linux-amd64.tar.gz -o kubo.tgz
tar -xzf kubo.tgz
@@ -229,52 +197,26 @@ jobs:
new_hash=$(cat ${{ matrix.app }}-ipfs-hash)
new_cid=$(ipfs cid format -v 1 -b base32 $new_hash)
ls -al ipfs-redirect
# 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}"
echo $new_hash > ipfs-redirect/cidv0.txt
echo $new_cid > ipfs-redirect/cidv1.txt
# 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
(
cd ipfs-redirect
git status
cat .git/config
git config --global user.email "vega-ci-bot@vega.xyz"
git config --global user.name "vega-ci-bot"
branch_name="update-hash-${{ github.ref }}"
git checkout -b "$branch_name"
commit_msg="Automated hash update from ${{ github.ref }}"
git add cidv0.txt cidv1.txt
git commit -m "$commit_msg"
git push -u origin "$branch_name"
pr_url="$(gh pr create --title "${commit_msg}" --body 'automated pull request to update CIDs')"
echo $pr_url
# once auto merge get's enabled on documentation repo let's do follow up
sleep 5
gh pr merge "${pr_url}" --delete-branch --squash --admin
)
# # Generate console URL
# new_console_url_type=ipfs
# # new_console_url_type=ipns
# new_console_url_domain=cf-ipfs.com
# # new_console_url_domain=dweb.link
# new_console_url="https://${new_cid}.${new_console_url_type}.${new_console_url_domain}/"
# echo "new_console_url=${new_console_url}"
# # Update record in DNSimple
# # docs: https://developer.dnsimple.com/v2/zones/records/#updateZoneRecord
# dnsimple_account_id=84895
# dnsimple_zone_name=console.vega.xyz
# dnsimple_record_id=44409591
# # see: https://dnsimple.com/a/84895/domains/console.vega.xyz/records/44409591/edit
# curl -H 'Authorization: Bearer ${{ secrets.DNSIMPLE_API_TOKEN }}' \
# -H 'Accept: application/json' \
# -H 'Content-Type: application/json' \
# -X PATCH \
# -d "{
# \"content\": \"${new_console_url}\"
# }" \
# https://api.dnsimple.com/v2/${dnsimple_account_id}/zones/${dnsimple_zone_name}/records/${dnsimple_record_id}
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}
+23 -30
View File
@@ -43,17 +43,7 @@ jobs:
-d '{"query": "mutation{triggerDeploy(siteId:\"f8f2e051-f18e-49e6-b876-0a39369dc0d8\"){id status}}"}' \
https://api.fleek.co/graphql
- name: Check out ipfs-redirect
uses: actions/checkout@v3
with:
repository: 'vegaprotocol/ipfs-redirect'
path: 'ipfs-redirect'
fetch-depth: '0'
token: ${{ secrets.VEGA_CI_BOT_GITHUB_TOKEN }}
- name: Update console.vega.xyz DNS to redirect to the new console
env:
GH_TOKEN: ${{ secrets.VEGA_CI_BOT_GITHUB_TOKEN }}
- 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
@@ -61,24 +51,27 @@ jobs:
which ipfs
new_hash=$(cat ipfs-hash)
new_cid=$(ipfs cid format -v 1 -b base32 $new_hash)
git config --global user.email "vega-ci-bot@vega.xyz"
git config --global user.name "vega-ci-bot"
echo $new_hash > ipfs-redirect/cidv0.txt
echo $new_cid > ipfs-redirect/cidv1.txt
(
cd ipfs-redirect
# 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}"
git status
branch_name="rollback-to-$new_hash"
git checkout -b "$branch_name"
commit_msg="hash rollback to $new_hash"
git add cidv0.txt cidv1.txt
git commit -m "$commit_msg"
git push -u origin "$branch_name" --force-with-lease
pr_url="$(gh pr create --title "${commit_msg}" --body 'automated pull request to update CIDs')"
echo $pr_url
# once auto merge get's enabled on documentation repo let's do follow up
sleep 5
gh pr merge "${pr_url}" --delete-branch --squash --admin
)
# 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
View File
@@ -50,5 +50,3 @@ cypress.env.json
#cypress
/apps/**/cypress/reports/
/apps/**/cypress/downloads/
/apps/**/fixtures/wallet/node**
+1
View File
@@ -1,3 +1,4 @@
NX_CHAIN_EXPLORER_URL=https://explorer.vega.trading/.netlify/functions/chain-explorer-api
NX_TENDERMINT_URL=http://localhost:26617
NX_TENDERMINT_WEBSOCKET_URL=wss://localhost:26617/websocket
NX_VEGA_URL=http://localhost:3008/graphql
+1
View File
@@ -1,4 +1,5 @@
# App configuration variables
NX_CHAIN_EXPLORER_URL=https://explorer.vega.trading/.netlify/functions/chain-explorer-api
NX_TENDERMINT_URL=https://n04.d.vega.xyz/tm
NX_TENDERMINT_WEBSOCKET_URL=wss://n04.d.vega.xyz/tm/websocket
NX_VEGA_URL=https://api.n04.d.vega.xyz/graphql
+1
View File
@@ -1,4 +1,5 @@
# App configuration variables
NX_CHAIN_EXPLORER_URL=https://explorer.vega.trading/.netlify/functions/chain-explorer-api
NX_TENDERMINT_URL=https://mainnet-observer-proxy01.ops.vega.xyz/
NX_TENDERMINT_WEBSOCKET_URL=wss://mainnet-observer-proxy01.ops.vega.xyz/websocket
NX_VEGA_URL=https://api.vega.community/graphql
+1
View File
@@ -1,4 +1,5 @@
# App configuration variables
NX_CHAIN_EXPLORER_URL=https://explorer.vega.trading/.netlify/functions/chain-explorer-api
NX_TENDERMINT_URL=https://tm.n07.testnet.vega.xyz/tm
NX_TENDERMINT_WEBSOCKET_URL=wss://lb.testnet.vega.xyz/tm/websocket
NX_VEGA_URL=https://api.n07.testnet.vega.xyz/graphql
+4 -5
View File
@@ -3,19 +3,18 @@ NX_ETHERSCAN_URL=https://sepolia.etherscan.io
NX_HOSTED_WALLET_URL=https://wallet.testnet.vega.xyz
NX_VEGA_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/networks-internal/main/stagnet1/vegawallet-stagnet1.toml
NX_VEGA_ENV=STAGNET1
NX_VEGA_EXPLORER_URL=https://explorer.stagnet1.vega.rocks
NX_VEGA_TOKEN_URL=https://governance.stagnet1.vega.rocks
NX_VEGA_EXPLORER_URL=https://stagnet1.explorer.vega.xyz
NX_VEGA_TOKEN_URL=https://stagnet1.governance.vega.xyz
NX_VEGA_WALLET_URL=http://localhost:1789
NX_TENDERMINT_URL=https://tm.n01.stagnet1.vega.xyz
NX_TENDERMINT_WEBSOCKET_URL=wss://tm.n01.stagnet1.vega.xyz/websocket
NX_BLOCK_EXPLORER=https://be.stagnet1.vega.xyz/rest
NX_ETHERSCAN_URL=https://sepolia.etherscan.io
NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json
NX_VEGA_GOVERNANCE_URL=https://governance.stagnet1.vega.rocks
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json
NX_VEGA_GOVERNANCE_URL=https://stagnet1.governance.vega.xyz
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"}'
NX_GITHUB_FEEDBACK_URL=https://github.com/vegaprotocol/feedback/discussions
NX_VEGA_REPO_URL=https://github.com/vegaprotocol/vega/releases/tag/
# App flags
NX_EXPLORER_ASSETS=1
-2
View File
@@ -1,7 +1,6 @@
# App configuration variables
NX_TENDERMINT_URL=https://be.vega.community
NX_TENDERMINT_WEBSOCKET_URL=wss://be.vega.community/websocket
NX_SENTRY_DSN=https://b3a56b03eda842faad731f3ea9dfd1bc@o286262.ingest.sentry.io/6242427
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
@@ -10,4 +9,3 @@ NX_ETHERSCAN_URL=https://etherscan.io
NX_VEGA_GOVERNANCE_URL=https://governance.vega.xyz
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/mainnet/announcements.json
NX_VEGA_EXPLORER_URL=https://explorer.vega.xyz/
NX_VEGA_CONSOLE_URL=https://console.vega.xyz
+1 -1
View File
@@ -1,2 +1,2 @@
# .env is stagnet1, so there are no overrides required
NX_VEGA_NETWORKS='{"TESTNET":"https://explorer.fairground.wtf","MAINNET":"https://explorer.vega.xyz","STAGNET1":"https://stagnet1.explorer.vega.xyz"}'
NX_VEGA_NETWORKS='{"TESTNET":"https://explorer.fairground.wtf","MAINNET":"https://explorer.vega.xyz","STAGNET3":"https://stagnet3.explorer.vega.xyz"}'
-1
View File
@@ -10,4 +10,3 @@ NX_ETHERSCAN_URL=https://sepolia.etherscan.io
NX_VEGA_GOVERNANCE_URL=https://governance.fairground.wtf
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json
NX_VEGA_EXPLORER_URL=https://explorer.fairground.wtf
NX_VEGA_CONSOLE_URL=https://console.fairground.wtf
@@ -7,9 +7,8 @@ NX_ETHEREUM_PROVIDER_URL=https://sepolia.infura.io/v3/4f846e79e13f44d1b51bbd7ed9
NX_ETHERSCAN_URL=https://sepolia.etherscan.io
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json
NX_TENDERMINT_URL=https://tm.be.validators-testnet.vega.rocks
NX_VEGA_GOVERNANCE_URL=https://validator-testnet.governance.vega.xyz
NX_TENDERMINT_URL=https://tm-be.validators-testnet.vega.rocks
NX_BLOCK_EXPLORER=https://be.validators-testnet.vega.rocks/rest
NX_TENDERMINT_WEBSOCKET_URL=wss://be.validators-testnet.vega.xyz/websocket
NX_VEGA_GOVERNANCE_URL=https://governance.validators-testnet.vega.rocks
NX_VEGA_EXPLORER_URL=https://explorer.validators-testnet.vega.rocks/
NX_VEGA_CONSOLE_URL=https://trading.validators-testnet.vega.rocks/
NX_VEGA_EXPLORER_URL=https://validator-testnet.explorer.vega.xyz/
+1 -4
View File
@@ -13,7 +13,6 @@ import { DEFAULT_CACHE_CONFIG } from '@vegaprotocol/apollo-client';
import { RouterProvider } from 'react-router-dom';
import { router } from './routes/router-config';
import { t } from '@vegaprotocol/i18n';
import { Suspense } from 'react';
const splashLoading = (
<Splash>
@@ -33,9 +32,7 @@ function App() {
skeleton={<div>{t('Loading')}</div>}
failure={<AppFailure title={t(`Node: ${VEGA_URL} is unsuitable`)} />}
>
<Suspense fallback={splashLoading}>
<RouterProvider router={router} fallbackElement={splashLoading} />
</Suspense>
<RouterProvider router={router} fallbackElement={splashLoading} />
</NodeGuard>
<NodeSwitcherDialog
open={nodeSwitcherOpen}
@@ -4,11 +4,9 @@ import {
} from '@vegaprotocol/environment';
import { t } from '@vegaprotocol/i18n';
import { useScreenDimensions } from '@vegaprotocol/react-helpers';
import { Link, ExternalLink } from '@vegaprotocol/ui-toolkit';
import { ExternalLink, Link } from '@vegaprotocol/ui-toolkit';
import { useMemo } from 'react';
import { ENV } from '../../config/env';
import { Routes } from '../../routes/route-names';
import { Link as RouteLink } from 'react-router-dom';
export const Footer = () => {
const { VEGA_URL, GIT_COMMIT_HASH, GIT_ORIGIN_URL } = useEnvironment();
@@ -23,7 +21,7 @@ export const Footer = () => {
);
return (
<footer className="grid grid-rows-2 lg:grid-cols-[1fr_auto] text-xs md:text-md lg:flex md:col-span-2 px-4 py-2 gap-4 border-t border-vega-light-200 dark:border-vega-dark-200">
<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">
{GIT_COMMIT_HASH && (
<div className="content-center flex border-r border-neutral-700 dark:border-neutral-300 pr-4">
@@ -58,16 +56,11 @@ export const Footer = () => {
</div>
) : null}
</div>
<div className="pl-2 align-center lg:align-right lg:flex lg:justify-end gap-2 align-middle lg:max-w-xs lg:ml-auto">
<RouteLink to={`/${Routes.DISCLAIMER}`} className="underline">
Disclaimer
</RouteLink>
</div>
</footer>
);
};
export const NodeUrl = ({ url }: { url: string }) => {
const NodeUrl = ({ url }: { url: string }) => {
// get base url from api url, api sub domain
const urlObj = new URL(url);
const nodeUrl = urlObj.origin.replace(/^[^.]+\./g, '');
@@ -1,6 +1,5 @@
import { t } from '@vegaprotocol/i18n';
import type { MarketInfoWithData } from '@vegaprotocol/markets';
import { PriceMonitoringBoundsInfoPanel } from '@vegaprotocol/markets';
import {
LiquidityInfoPanel,
LiquidityMonitoringParametersInfoPanel,
@@ -40,69 +39,133 @@ export const MarketDetails = ({ market }: { market: MarketInfoWithData }) => {
return [];
};
const showTwoOracles = isEqual(
const oraclePanels = isEqual(
getSigners(settlementData),
getSigners(terminationData)
);
)
? [
{
title: t('Settlement Oracle'),
content: (
<OracleInfoPanel
noBorder={false}
market={market}
type="settlementData"
/>
),
},
{
title: t('Termination Oracle'),
content: (
<OracleInfoPanel
noBorder={false}
market={market}
type="termination"
/>
),
},
]
: [
{
title: t('Oracle'),
content: (
<OracleInfoPanel
noBorder={false}
market={market}
type="settlementData"
/>
),
},
];
const headerClassName = 'font-alpha calt text-xl mt-4 border-b-2 pb-2';
return (
<div>
<h2 className={headerClassName}>{t('Key details')}</h2>
<KeyDetailsInfoPanel market={market} />
<h2 className={headerClassName}>{t('Instrument')}</h2>
<InstrumentInfoPanel market={market} />
<h2 className={headerClassName}>{t('Settlement asset')}</h2>
<SettlementAssetInfoPanel market={market} />
<h2 className={headerClassName}>{t('Metadata')}</h2>
<MetadataInfoPanel market={market} />
<h2 className={headerClassName}>{t('Risk model')}</h2>
<RiskModelInfoPanel market={market} />
<h2 className={headerClassName}>{t('Risk parameters')}</h2>
<RiskParametersInfoPanel market={market} />
<h2 className={headerClassName}>{t('Risk factors')}</h2>
<RiskFactorsInfoPanel market={market} />
{(market.data?.priceMonitoringBounds || []).map((trigger, i) => (
const panels = [
{
title: t('Key details'),
content: <KeyDetailsInfoPanel noBorder={false} market={market} />,
},
{
title: t('Instrument'),
content: <InstrumentInfoPanel noBorder={false} market={market} />,
},
{
title: t('Settlement asset'),
content: <SettlementAssetInfoPanel market={market} noBorder={false} />,
},
{
title: t('Metadata'),
content: <MetadataInfoPanel noBorder={false} market={market} />,
},
{
title: t('Risk model'),
content: <RiskModelInfoPanel noBorder={false} market={market} />,
},
{
title: t('Risk parameters'),
content: <RiskParametersInfoPanel noBorder={false} market={market} />,
},
{
title: t('Risk factors'),
content: <RiskFactorsInfoPanel noBorder={false} market={market} />,
},
...(market.priceMonitoringSettings?.parameters?.triggers || []).map(
(trigger, i) => ({
title: t(`Price monitoring trigger ${i + 1}`),
content: <MarketInfoTable noBorder={false} data={trigger} />,
})
),
...(market.data?.priceMonitoringBounds || []).map((trigger, i) => ({
title: t(`Price monitoring bound ${i + 1}`),
content: (
<>
<h2 className={headerClassName}>
{t('Price monitoring bounds %s', [(i + 1).toString()])}
</h2>
<PriceMonitoringBoundsInfoPanel
market={market}
triggerIndex={i + 1}
<MarketInfoTable
noBorder={false}
data={{
maxValidPrice: trigger.maxValidPrice,
minValidPrice: trigger.minValidPrice,
}}
decimalPlaces={market.decimalPlaces}
/>
<MarketInfoTable
noBorder={false}
data={{ referencePrice: trigger.referencePrice }}
decimalPlaces={
market.tradableInstrument.instrument.product.settlementAsset
.decimals
}
/>
</>
),
})),
{
title: t('Liquidity monitoring parameters'),
content: (
<LiquidityMonitoringParametersInfoPanel
noBorder={false}
market={market}
/>
),
},
{
title: t('Liquidity'),
content: <LiquidityInfoPanel market={market} noBorder={false} />,
},
{
title: t('Liquidity price range'),
content: (
<LiquidityPriceRangeInfoPanel market={market} noBorder={false} />
),
},
...oraclePanels,
];
return (
<>
{panels.map((p) => (
<div key={p.title} className="mb-3">
<h2 className="font-alpha calt text-xl">{p.title}</h2>
{p.content}
</div>
))}
{(market.priceMonitoringSettings?.parameters?.triggers || []).map(
(trigger, i) => (
<>
<h2 className={headerClassName}>
{t('Price monitoring settings %s', [(i + 1).toString()])}
</h2>
<MarketInfoTable data={trigger} key={i} />
</>
)
)}
<h2 className={headerClassName}>{t('Liquidity monitoring')}</h2>
<LiquidityMonitoringParametersInfoPanel market={market} />
<h2 className={headerClassName}>{t('Liquidity')}</h2>
<LiquidityInfoPanel market={market} />
<h2 className={headerClassName}>{t('Liquidity price range')}</h2>
<LiquidityPriceRangeInfoPanel market={market} />
{showTwoOracles ? (
<>
<h2 className={headerClassName}>{t('Settlement oracle')}</h2>
<OracleInfoPanel market={market} type="settlementData" />
<h2 className={headerClassName}>{t('Termination oracle')}</h2>
<OracleInfoPanel market={market} type="termination" />
</>
) : (
<>
<h2 className={headerClassName}>{t('Oracle')}</h2>
<OracleInfoPanel market={market} type="settlementData" />
</>
)}
</div>
</>
);
};
@@ -84,17 +84,15 @@ export const MarketsTable = ({ data }: MarketsTableProps) => {
<AgGridColumn
colId="asset"
headerName={t('Settlement asset')}
field="tradableInstrument.instrument.product.settlementAsset.symbol"
field="tradableInstrument.instrument.product.settlementAsset"
hide={window.innerWidth <= BREAKPOINT_MD}
cellRenderer={({
data,
value,
}: VegaICellRendererParams<
MarketFieldsFragment,
'tradableInstrument.instrument.product.settlementAsset.symbol'
>) => {
const value =
data?.tradableInstrument.instrument.product.settlementAsset;
return value ? (
'tradableInstrument.instrument.product.settlementAsset'
>) =>
value ? (
<ButtonLink
onClick={(e) => {
openAssetDetailsDialog(value.id, e.target as HTMLElement);
@@ -104,8 +102,8 @@ export const MarketsTable = ({ data }: MarketsTableProps) => {
</ButtonLink>
) : (
''
);
}}
)
}
/>
<AgGridColumn
flex={2}
@@ -5,13 +5,12 @@ export const ErrorCodes = new Map([
[51, 'Transaction failed validation'],
[60, 'Transaction could not be decoded'],
[70, 'Error'],
[71, 'Partial success/error'],
[80, 'Unknown command'],
[89, 'Rejected as spam'],
[0, 'Success'],
]);
export const successCodes = new Set([0, 71]);
export const successCodes = new Set([0]);
interface ChainResponseCodeProps {
code: number;
@@ -30,12 +29,11 @@ export const ChainResponseCode = ({
error,
}: ChainResponseCodeProps) => {
const isSuccess = successCodes.has(code);
const successColour =
code === 71 ? 'fill-vega-orange' : 'fill-vega-green-600';
const icon = isSuccess ? (
<Icon name="tick-circle" className={successColour} />
<Icon name="tick-circle" className="fill-vega-green-550" />
) : (
<Icon name="cross" className="fill-vega-pink-600" />
<Icon name="cross" className="fill-vega-pink-550" />
);
const label = ErrorCodes.get(code) || 'Unknown response code';
@@ -160,7 +160,6 @@ export const TxOrderType = ({ orderType, command }: TxOrderTypeProps) => {
vote={command?.voteSubmission?.value === 'VALUE_YES'}
yesText="Proposal vote"
noText="Proposal vote"
useVoteColour={false}
/>
);
}
@@ -34,17 +34,4 @@ describe('Vote TX icon', () => {
const no = render(<VoteIcon vote={false} />);
expect(no.getByRole('img')).toHaveAttribute('aria-label', 'delete icon');
});
it('useVoteColour prop can be used to override coloured background', () => {
const no = render(<VoteIcon vote={false} />);
expect(no.container.children[0]).toHaveClass('bg-vega-pink-550');
const monochromeNo = render(
<VoteIcon vote={false} useVoteColour={false} />
);
expect(monochromeNo.container.children[0]).not.toHaveClass(
'bg-vega-pink-550'
);
expect(monochromeNo.container.children[0]).toHaveClass('bg-vega-dark-200');
});
});
@@ -8,32 +8,6 @@ export interface VoteIconProps {
yesText?: string;
// Defaults to 'Against', but can be any text
noText?: string;
// If set to false the background will not be coloured
useVoteColour?: boolean;
}
function getBgColour(useVoteColour: boolean, vote: boolean) {
if (useVoteColour === false) {
return 'bg-vega-dark-200';
}
return vote ? 'bg-vega-green-550' : 'bg-vega-pink-550';
}
function getFillColour(useVoteColour: boolean, vote: boolean) {
if (useVoteColour === false) {
return 'white';
}
return vote ? 'vega-green-300' : 'vega-pink-300';
}
function getTextColour(useVoteColour: boolean, vote: boolean) {
if (useVoteColour === false) {
return 'white';
}
return vote ? 'vega-green-200' : 'vega-pink-200';
}
/**
@@ -44,15 +18,14 @@ function getTextColour(useVoteColour: boolean, vote: boolean) {
*/
export function VoteIcon({
vote,
useVoteColour = true,
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 bg = getBgColour(useVoteColour, vote);
const fill = getFillColour(useVoteColour, vote);
const text = getTextColour(useVoteColour, vote);
const fill = vote ? 'vega-green-300' : 'vega-pink-300';
const text = vote ? 'vega-green-200' : 'vega-pink-200';
return (
<div
@@ -1,13 +1,12 @@
import { render } from '@testing-library/react';
import { OracleDetailsType, isInternalSourceType } from './oracle-details-type';
import type { SourceType } from './oracle';
import { PropertyKeyType } from '@vegaprotocol/types';
import { OracleDetailsType } from './oracle-details-type';
import type { SourceTypeName } from './oracle-details-type';
function renderComponent(type: SourceType) {
return <OracleDetailsType sourceType={type} />;
function renderComponent(type: SourceTypeName) {
return <OracleDetailsType type={type} />;
}
function renderWrappedComponent(type: SourceType) {
function renderWrappedComponent(type: SourceTypeName) {
return (
<table>
<tbody>{renderComponent(type)}</tbody>
@@ -15,53 +14,19 @@ function renderWrappedComponent(type: SourceType) {
);
}
function mock(name: string): SourceType {
return {
sourceType: {
filters: [
{
__typename: 'Filter',
key: {
name,
type: PropertyKeyType.TYPE_STRING,
},
},
],
},
};
}
describe('Oracle type view', () => {
it('Renders nothing when type is null', () => {
const res = render(renderComponent(null as unknown as SourceType));
const res = render(renderComponent(null as unknown as SourceTypeName));
expect(res.container).toBeEmptyDOMElement();
});
it('Renders Internal time for internal sources - timestamp', () => {
const s = mock('vegaprotocol.builtin.timestamp');
expect(isInternalSourceType(s)).toEqual(true);
const res = render(renderWrappedComponent(s));
expect(res.getByText('Internal data')).toBeInTheDocument();
});
it('Renders Internal time for internal sources - potential future types', () => {
const s = mock('vegaprotocol.builtin.boolean');
expect(isInternalSourceType(s)).toEqual(true);
const res = render(renderWrappedComponent(s));
expect(res.getByText('Internal data')).toBeInTheDocument();
});
it('Renders External data otherwise - prices.external.whatever', () => {
const s = mock('prices.external.whatever');
expect(isInternalSourceType(s)).toEqual(false);
const res = render(renderWrappedComponent(s));
expect(res.getByText('External data')).toBeInTheDocument();
it('Renders Internal time for internal sources', () => {
const res = render(renderWrappedComponent('DataSourceDefinitionInternal'));
expect(res.getByText('Internal time')).toBeInTheDocument();
});
it('Renders External data otherwise', () => {
const s = mock('prices.external.vegaprotocol.builtin.');
expect(isInternalSourceType(s)).toEqual(false);
const res = render(renderWrappedComponent(s));
const res = render(renderWrappedComponent('DataSourceDefinitionExternal'));
expect(res.getByText('External data')).toBeInTheDocument();
});
});
@@ -1,51 +1,27 @@
import { TableRow, TableCell, TableHeader } from '../../../components/table';
import type { SourceType } from './oracle';
/**
* Basic function to determine if a source is internal or external.
*
* This should be distinguishable using __typename, but the type is incorrectly
* reported at the moment, so instead we check the filters.
*
* @param s SourceType
* @returns boolean True if the source is internal
*/
export function isInternalSourceType(s: SourceType) {
if ('filters' in s.sourceType) {
const filters = s.sourceType.filters;
if (filters) {
return (
filters?.filter((f) => {
return f.key.name?.indexOf('vegaprotocol.builtin.') === 0;
}).length > 0
);
}
}
return false;
}
export type SourceTypeName = SourceType['__typename'] | undefined;
interface OracleDetailsTypeProps {
sourceType: SourceType;
type: SourceTypeName;
}
/**
* Renders a a single table row for the Oracle Details view that shows
* if the oracle is using the internal time oracle or external data
*/
export function OracleDetailsType({ sourceType }: OracleDetailsTypeProps) {
if (!sourceType) {
export function OracleDetailsType({ type }: OracleDetailsTypeProps) {
if (!type) {
return null;
}
const isInternal = isInternalSourceType(sourceType);
return (
<TableRow modifier="bordered">
<TableHeader scope="row">Type</TableHeader>
<TableCell modifier="bordered">
{isInternal ? 'Internal data' : 'External data'}
{type === 'DataSourceDefinitionInternal'
? 'Internal time'
: 'External data'}
</TableCell>
</TableRow>
);
@@ -53,7 +53,7 @@ export const OracleDetails = ({
<OracleLink id={id} />
</TableCell>
</TableRow>
<OracleDetailsType sourceType={sourceType} />
<OracleDetailsType type={sourceType.__typename} />
<OracleSigners sourceType={sourceType} />
<OracleMarkets id={id} />
<TableRow modifier="bordered">
@@ -1,40 +0,0 @@
import { t } from '@vegaprotocol/i18n';
import { RouteTitle } from '../../components/route-title';
export const Disclaimer = () => {
return (
<section>
<div className="px-40 max-sm:px-0 max-md:px-10 mb-4 max-w-5xl">
<RouteTitle data-testid="disclaimer-header">
{t('Disclaimer')}
</RouteTitle>
<p className="mt-3">
The Vega Block Explorer is an application that allows users to, among
other things, browse through blocks, view wallet addresses, network
hashrate, transaction data and other key information on the Vega
blockchain. It is free, public and open source software. Software
upgrades may contain bugs or security vulnerabilities that might
result in loss of functionality.
</p>
<p className="mt-3">
The Vega Block Explorer uses data from nodes on the Vega Blockchain.
The developers of the Vega Block Explorer do not operate or run the
Vega Blockchain or any other blockchain.
</p>
<p className="mt-3 font-semibold">
The Vega Block Explorer is provided as is. The developers of the
Vega Block Explorer make no representations or warranties of any kind,
whether express or implied, statutory or otherwise regarding the Vega
Block Explorer. They disclaim all warranties of merchantability,
quality, fitness for purpose. They disclaim all warranties that the
Vega Block Explorer is free of harmful components or errors.
</p>
<p className="mt-3 font-bold">
No developer of the Vega Block Explorer accepts any responsibility
for, or liability to users in connection with their use of the Vega
Block Explorer.
</p>
</div>
</section>
);
};
@@ -10,5 +10,4 @@ export const Routes = {
MARKETS: 'markets',
ORACLES: 'oracles',
NETWORK_PARAMETERS: 'network-parameters',
DISCLAIMER: 'disclaimer',
};
@@ -29,7 +29,6 @@ import { AssetLink, MarketLink } from '../components/links';
import { truncateMiddle } from '@vegaprotocol/ui-toolkit';
import { remove0x } from '@vegaprotocol/utils';
import { PartyAccountsByAsset } from './parties/id/accounts';
import { Disclaimer } from './pages/disclaimer';
export type Navigable = {
path: string;
@@ -336,17 +335,6 @@ export const routerConfig: Route[] = [
},
],
},
{
path: Routes.DISCLAIMER,
element: <Disclaimer />,
handle: {
name: t('Disclaimer'),
text: t('Disclaimer'),
breadcrumb: () => (
<Link to={Routes.DISCLAIMER}>{t('Disclaimer')}</Link>
),
},
},
...partiesRoutes,
...assetsRoutes,
...genesisRoutes,
-1
View File
@@ -26,7 +26,6 @@ module.exports = defineConfig({
viewportWidth: 1440,
viewportHeight: 900,
numTestsKeptInMemory: 5,
downloadsFolder: 'cypress/downloads',
testIsolation: false,
},
env: {
@@ -33,9 +33,8 @@ const proposalDetailsTitle = '[data-testid="proposal-title"]';
const proposalDetailsDescription = '[data-testid="proposal-description"]';
const openProposals = '[data-testid="open-proposals"]';
const viewProposalButton = '[data-testid="view-proposal-btn"]';
const proposalDescriptionToggle = 'proposal-description-toggle';
const voteBreakdownToggle = 'vote-breakdown-toggle';
const proposalTermsToggle = 'proposal-json-toggle';
const proposalTermsToggle = 'proposal-terms-toggle';
describe(
'Governance flow for proposal details',
@@ -74,28 +73,16 @@ describe(
'contain.text',
rawProposal.rationale.title
);
cy.getByTestId(proposalDescriptionToggle).click();
cy.getByTestId('proposal-description-toggle');
cy.get(proposalDetailsDescription)
.find('p')
.should('have.text', proposalDescription);
});
// 3001-VOTE-008
getProposalInformationFromTable('ID')
.invoke('text')
.should('not.be.empty')
.and('have.length', 64);
// 3001-VOTE-009
getProposalInformationFromTable('Proposed by')
.invoke('text')
.should('not.be.empty')
.and('have.length', 64);
cy.getByTestId(proposalTermsToggle).click();
// 3001-VOTE-052 3001-VOTE-010
// 3001-VOTE-052
cy.get('code.language-json')
.should('exist')
.within(() => {
cy.get('.hljs-attr').eq(0).should('have.text', '"id"');
cy.get('.hljs-string').eq(0).should('have.text', '"ProposalTerms"');
});
});
@@ -103,6 +90,8 @@ describe(
it('Newly created freeform proposal details - shows proposed and closing dates', function () {
const proposalTitle = generateFreeFormProposalTitle();
const proposalTimeStamp = createTenDigitUnixTimeStampForSpecifiedDays(3);
// const currentDate = new Date(createTenDigitUnixTimeStampForSpecifiedDays(0) * 1000)
// const proposedDate = new Date(currentDate.getTime() + 60000)
submitUniqueRawProposal({
proposalTitle: proposalTitle,
@@ -120,9 +109,16 @@ describe(
closingDate
);
});
getProposalInformationFromTable('Proposed on')
.invoke('text')
.should('not.be.empty');
cy.wrap(
formatDateWithLocalTimezone(
new Date(createTenDigitUnixTimeStampForSpecifiedDays(0) * 1000)
)
).then((proposalDate) => {
getProposalInformationFromTable('Proposed on').should(
'have.text',
proposalDate
);
});
});
it('Newly created proposal details - shows default status set to fail', function () {
@@ -14,7 +14,6 @@ import {
createUpdateNetworkProposalTxBody,
createFreeFormProposalTxBody,
} from '../../support/proposal.functions';
import { ensureSpecifiedUnstakedTokensAreAssociated } from '../../support/staking.functions';
import { ethereumWalletConnect } from '../../support/wallet-eth.functions';
import { vegaWalletSetSpecifiedApprovalAmount } from '../../support/wallet-teardown.functions';
@@ -44,7 +43,6 @@ context(
waitForSpinner();
cy.connectVegaWallet();
ethereumWalletConnect();
ensureSpecifiedUnstakedTokensAreAssociated('1');
navigateTo(navigation.proposals);
});
@@ -11,6 +11,7 @@ import {
governanceProposalType,
submitUniqueRawProposal,
voteForProposal,
waitForProposalSubmitted,
waitForProposalSync,
} from '../../support/governance.functions';
@@ -45,7 +46,11 @@ const connectToVegaWalletButton = '[data-testid="connect-to-vega-wallet-btn"]';
const newProposalSubmitButton = '[data-testid="proposal-submit"]';
const viewProposalButton = '[data-testid="view-proposal-btn"]';
const rawProposalData = '[data-testid="proposal-data"]';
const minVoteButton = '[data-testid="min-vote"]';
const maxVoteButton = '[data-testid="max-vote"]';
const voteButtons = '[data-testid="vote-buttons"]';
const votingDate = '[data-testid="voting-date"]';
const voteTwoMinExtraNote = '[data-testid="voting-2-mins-extra"]';
const rejectProposalsLink = '[href="/proposals/rejected"]';
const feedbackError = '[data-testid="Error"]';
const noOpenProposals = '[data-testid="no-open-proposals"]';
@@ -101,7 +106,8 @@ context(
.and('have.text', 'There are no enacted or rejected proposals');
});
// 3002-PROP-002 3002-PROP-003 3001-VOTE-012 3007-PNE-020 3004-PMAC-004 3005-PASN-004 3008-PFRO-016 3003-PMAN-004
// 3002-PROP-002
// 3002-PROP-003
it('Proposal form - shows how many vega tokens are required to make a proposal', function () {
// 3002-PROP-005
goToMakeNewProposal(governanceProposalType.NEW_MARKET);
@@ -110,14 +116,23 @@ context(
).should('be.visible');
});
// 3002-PROP-011 3008-PFRO-005
it('Able to submit a valid freeform proposal - with minimum required tokens associated', function () {
ensureSpecifiedUnstakedTokensAreAssociated('1');
verifyUnstakedBalance(1);
createRawProposal();
// Skipping as currently unable to propose using forms other than raw
// 3002-PROP-011
it.skip('Able to submit a valid freeform proposal - with minimum required tokens associated', function () {
goToMakeNewProposal(governanceProposalType.FREEFORM);
cy.get(minVoteButton).should('be.visible'); // 3002-PROP-008
cy.get(maxVoteButton).should('be.visible');
cy.get(votingDate).should('not.be.empty');
cy.get(voteTwoMinExtraNote).should(
'contain.text',
'we add 2 minutes of extra time'
);
enterUniqueFreeFormProposalBody('50', generateFreeFormProposalTitle());
// 3002-PROP-012
// 3002-PROP-016
waitForProposalSubmitted();
});
// 3008-PFRO-002 3008-PFRO-004
it('Able to submit a valid freeform proposal - with minimum required tokens associated - but also staked', function () {
ensureSpecifiedUnstakedTokensAreAssociated('2');
verifyUnstakedBalance(2);
@@ -133,6 +148,10 @@ context(
it.skip('Creating a proposal - proposal rejected - when closing time sooner than system default', function () {
goToMakeNewProposal(governanceProposalType.FREEFORM);
enterUniqueFreeFormProposalBody('0.1', generateFreeFormProposalTitle());
cy.contains('Awaiting network confirmation', epochTimeout).should(
'not.exist'
);
cy.get('input:invalid')
.invoke('prop', 'validationMessage')
.should('equal', 'Value must be greater than or equal to 1.');
@@ -261,20 +280,6 @@ context(
closeDialog();
});
// 3007-PNE-022 3007-PNE-023 3004-PMAC-006 3004-PMAC-007 3005-PASN-006 3005-PASN-007
// 3006-PASC-006 3006-PASC-007 3008-PFRO-018 3008-PFRO-019 3003-PMAN-006 3003-PMAN-007
it('Unable to submit proposal without valid json', function () {
goToMakeNewProposal(governanceProposalType.RAW);
cy.get(newProposalSubmitButton).click();
cy.getByTestId('input-error-text').should('have.text', 'Required');
cy.get(rawProposalData).type('Not a valid json string');
cy.get(newProposalSubmitButton).click();
cy.getByTestId('input-error-text').should(
'have.text',
'Must be valid JSON'
);
});
// 1005-PROP-009
it('Unable to vote on a freeform proposal - when some but not enough vega associated', function () {
const proposalTitle = generateFreeFormProposalTitle();
@@ -1,26 +1,18 @@
import {
closeDialog,
dissociateFromSecondWalletKey,
navigateTo,
navigation,
turnTelemetryOff,
waitForSpinner,
} from '../../support/common.functions';
import {
getDownloadedProposalJsonPath,
submitUniqueRawProposal,
} from '../../support/governance.functions';
import {
getProposalInformationFromTable,
goToMakeNewProposal,
governanceProposalType,
voteForProposal,
waitForProposalSubmitted,
} from '../../support/governance.functions';
import {
ensureSpecifiedUnstakedTokensAreAssociated,
stakingPageAssociateTokens,
stakingPageDisassociateAllTokens,
} from '../../support/staking.functions';
import { ensureSpecifiedUnstakedTokensAreAssociated } from '../../support/staking.functions';
import { ethereumWalletConnect } from '../../support/wallet-eth.functions';
import {
vegaWalletSetSpecifiedApprovalAmount,
@@ -34,11 +26,15 @@ const proposalType = '[data-testid="proposal-type"]';
const proposalDetails = '[data-testid="proposal-details"]';
const newProposalSubmitButton = '[data-testid="proposal-submit"]';
const proposalVoteDeadline = '[data-testid="proposal-vote-deadline"]';
const proposalValidationDeadline =
'[data-testid="proposal-validation-deadline"]';
const proposalParameterSelect = '[data-testid="proposal-parameter-select"]';
const proposalMarketSelect = '[data-testid="proposal-market-select"]';
const newProposalTitle = '[data-testid="proposal-title"]';
const newProposalDescription = '[data-testid="proposal-description"]';
const newProposalTerms = '[data-testid="proposal-terms"]';
const currentParameterValue =
'[data-testid="selected-proposal-param-current-value"]';
const newProposedParameterValue =
'[data-testid="selected-proposal-param-new-value"]';
const minVoteDeadline = '[data-testid="min-vote"]';
@@ -58,10 +54,11 @@ const proposalTermsSection = 'proposal';
const vegaWalletPublicKey = Cypress.env('vegaWalletPublicKey');
const fUSDCId =
'816af99af60d684502a40824758f6b5377e6af48e50a9ee8ef478ecb879ea8bc';
const epochTimeout = Cypress.env('epochTimeout');
const proposalTimeout = { timeout: 14000 };
// 3001-VOTE-007
context(
context.skip(
'Governance flow - form validations for different governance proposals',
{ tags: '@slow' },
function () {
@@ -82,10 +79,28 @@ context(
navigateTo(navigation.proposals);
});
it('Able to submit valid update network parameter proposal', function () {
goToMakeNewProposal(governanceProposalType.NETWORK_PARAMETER);
// 3002-PROP-006
cy.get(newProposalTitle).type('Test update network parameter proposal');
// 3002-PROP-007
cy.get(newProposalDescription).type('E2E test for proposals');
cy.get(proposalParameterSelect).find('option').should('have.length', 117);
cy.get(proposalParameterSelect).select(
// 3007-PNEC-002
'governance_proposal_asset_minEnact'
);
cy.get(currentParameterValue).should('have.value', '2s');
cy.get(newProposedParameterValue).type('5s'); // 3007-PNEC-003
cy.get(newProposalSubmitButton).should('be.visible').click();
waitForProposalSubmitted();
});
it('Unable to submit network parameter with missing/invalid fields', function () {
navigateTo(navigation.proposals);
goToMakeNewProposal(governanceProposalType.NETWORK_PARAMETER);
cy.get(proposalDownloadBtn).click();
cy.get(newProposalSubmitButton).should('be.visible').click();
cy.get(inputError).should('have.length', 3);
cy.get(newProposalTitle).type(
'Invalid update network parameter proposal'
@@ -96,78 +111,62 @@ context(
);
cy.get(newProposedParameterValue).type('0');
cy.get(proposalVoteDeadline).clear().type('0');
cy.get(proposalDownloadBtn)
.click()
.then(() => {
cy.wrap(
getDownloadedProposalJsonPath('vega-network-param-proposal-')
).then((filePath) => {
goToMakeNewProposal(governanceProposalType.RAW);
submitUniqueRawProposal({ proposalBody: filePath, submit: false });
});
});
cy.get(newProposalSubmitButton).click();
validateDialogContentMsg('PROPOSAL_ERROR_CLOSE_TIME_TOO_SOON');
cy.contains('Awaiting network confirmation', epochTimeout).should(
'not.exist'
);
cy.get(proposalVoteDeadline).clear().type('9000');
cy.get(newProposalSubmitButton).click();
cy.contains('Awaiting network confirmation', epochTimeout).should(
'not.exist'
);
});
// 3007-PNEC-001 3007-PNEC-003
it('Able to download and submit network param proposal', function () {
it('Able to download network param proposal json', function () {
const downloadFolder = './cypress/downloads/';
goToMakeNewProposal(governanceProposalType.NETWORK_PARAMETER);
// 3007-PNEC-006
cy.get(newProposalTitle)
.siblings()
.should('contain.text', '(100 characters or less)');
// 3007-PNEC-004 3007-PNEC-005
cy.get(newProposalTitle).type('Test update network parameter proposal');
// 3007-PNEC-009
cy.get(newProposalDescription)
.siblings()
.should('contain.text', '(20,000 characters or less)');
// 3007-PNEC-007 3007-PNEC-008
cy.log('Download proposal file');
cy.get(proposalDownloadBtn)
.should('be.visible')
.click()
.then(() => {
const filename =
downloadFolder +
'vega-network-param-proposal-' +
getFormattedTime() +
'.json';
cy.readFile(filename, proposalTimeout)
.its('terms.updateNetworkParameter')
.should('exist');
});
cy.get(newProposalDescription).type('E2E test for downloading proposals');
// 3007-PNEC-010
cy.get(proposalParameterSelect).select(
'governance_proposal_asset_minClose'
);
// 3007-PNEC-011
cy.get(newProposedParameterValue).type('10s');
// 3007-PNEC-012
cy.get(proposalVoteDeadline).clear().type('2');
// 3007-PNEC-013 3007-PNEC-014
cy.getByTestId('voting-date').invoke('text').should('not.be.empty');
// 3007-PNEC-015
cy.get(maxEnactDeadline).click();
// 3007-PNEC-016
cy.getByTestId('enactment-date').invoke('text').should('not.be.empty');
// 3007-PNEC-017
cy.contains(
'Time till enactment (must be equal to or after vote close)'
).should('be.visible');
// 3007-PNE-018
cy.log('Download updated proposal file');
cy.get(proposalDownloadBtn)
.should('be.visible')
.click()
.then(() => {
cy.wrap(
getDownloadedProposalJsonPath('vega-network-param-proposal-')
).then((filePath) => {
cy.readFile(String(filePath), { timeout: 14000 }).then(
(jsonFile) => {
cy.wrap(jsonFile)
.its('rationale.description')
.should('eq', 'E2E test for downloading proposals');
cy.wrap(jsonFile)
.its('terms.updateNetworkParameter.changes.key')
.should('eq', 'governance.proposal.asset.minClose');
cy.wrap(jsonFile)
.its('terms.updateNetworkParameter.changes.value')
.should('eq', '10s');
}
);
// 3007-PNE-019
goToMakeNewProposal(governanceProposalType.RAW);
submitUniqueRawProposal({ proposalBody: filePath });
const filename =
downloadFolder +
'vega-network-param-proposal-' +
getFormattedTime() +
'.json';
cy.get(proposalDownloadBtn).should('be.visible').click();
cy.readFile(filename, proposalTimeout).then((jsonFile) => {
cy.wrap(jsonFile)
.its('rationale.description')
.should('eq', 'E2E test for downloading proposals');
cy.wrap(jsonFile)
.its('terms.updateNetworkParameter.changes.key')
.should('eq', 'governance.proposal.asset.minClose');
cy.wrap(jsonFile)
.its('terms.updateNetworkParameter.changes.value')
.should('eq', '10s');
});
});
});
@@ -187,21 +186,14 @@ context(
'have.text',
'Proposal will fail if enactment is earlier than the voting deadline'
);
cy.get(proposalDownloadBtn)
.should('be.visible')
.click()
.then(() => {
cy.wrap(
getDownloadedProposalJsonPath('vega-network-param-proposal-')
).then((filePath) => {
goToMakeNewProposal(governanceProposalType.RAW);
submitUniqueRawProposal({ proposalBody: filePath, submit: false });
});
});
cy.get(newProposalSubmitButton).click();
validateFeedBackMsg(
cy.get(feedbackError).should(
'have.text',
'Invalid params: proposal_submission.terms.closing_timestamp (cannot be after enactment time)'
);
closeDialog();
cy.get(minVoteDeadline).click();
cy.get(enactmentDeadlineError).should('not.exist');
});
// 3003-PMAN-001
@@ -216,17 +208,8 @@ context(
delay: 2,
});
});
cy.get(proposalDownloadBtn)
.should('be.visible')
.click()
.then(() => {
cy.wrap(
getDownloadedProposalJsonPath('vega-new-market-proposal-')
).then((filePath) => {
goToMakeNewProposal(governanceProposalType.RAW);
submitUniqueRawProposal({ proposalBody: filePath }); // 3003-PMAN-003
});
});
cy.get(newProposalSubmitButton).should('be.visible').click();
waitForProposalSubmitted();
});
it('Unable to submit new market proposal with missing/invalid fields', function () {
@@ -234,7 +217,7 @@ context(
'Invalid params: the transaction does not use a valid Vega command: unknown field "invalid" in vega.NewMarket';
goToMakeNewProposal(governanceProposalType.NEW_MARKET);
cy.get(proposalDownloadBtn).should('be.visible').click();
cy.get(newProposalSubmitButton).should('be.visible').click();
cy.get(inputError).should('have.length', 3);
cy.get(newProposalTitle).type('Test new market proposal');
cy.get(newProposalDescription).type('E2E test for proposals');
@@ -246,28 +229,14 @@ context(
delay: 2,
});
});
cy.get(proposalDownloadBtn)
.should('be.visible')
.click()
.then(() => {
cy.wrap(
getDownloadedProposalJsonPath('vega-new-market-proposal-')
).then((filePath) => {
goToMakeNewProposal(governanceProposalType.RAW);
submitUniqueRawProposal({ proposalBody: filePath, submit: false });
});
});
cy.get(newProposalSubmitButton).should('be.visible').click();
cy.contains('Transaction failed', proposalTimeout).should('be.visible');
validateFeedBackMsg(errorMsg);
cy.get(feedbackError).should('have.text', errorMsg);
});
// Will fail if run after 'Able to submit update market proposal and vote for proposal'
// 3002-PROP-022
it('Unable to submit update market proposal without equity-like share in the market', function () {
cy.get('[data-testid="manage-vega-wallet"]:visible').click();
cy.get('[data-testid="select-keypair-button"]').eq(0).click(); // switch to second wallet pub key
stakingPageAssociateTokens('1');
goToMakeNewProposal(governanceProposalType.UPDATE_MARKET);
cy.get(newProposalTitle).type('Test update market proposal - rejected');
cy.get(newProposalDescription).type('E2E test for proposals');
@@ -279,25 +248,12 @@ context(
delay: 2,
});
});
cy.get(proposalDownloadBtn)
.should('be.visible')
.click()
.then(() => {
cy.wrap(
getDownloadedProposalJsonPath('vega-update-market-proposal-')
).then((filePath) => {
goToMakeNewProposal(governanceProposalType.RAW);
submitUniqueRawProposal({ proposalBody: filePath, submit: false });
});
});
cy.get(newProposalSubmitButton).should('be.visible').click();
cy.contains('Proposal rejected', proposalTimeout).should('be.visible');
validateDialogContentMsg('PROPOSAL_ERROR_INSUFFICIENT_EQUITY_LIKE_SHARE');
closeDialog();
ethereumWalletConnect();
stakingPageDisassociateAllTokens();
cy.get('[data-testid="manage-vega-wallet"]:visible').click();
cy.get('[data-testid="select-keypair-button"]').eq(0).click();
cy.getByTestId('dialog-content')
.find('p')
.should('have.text', 'PROPOSAL_ERROR_INSUFFICIENT_EQUITY_LIKE_SHARE');
ensureSpecifiedUnstakedTokensAreAssociated('1');
});
// 3002-PROP-020
@@ -319,25 +275,15 @@ context(
delay: 2,
});
});
cy.get(proposalDownloadBtn)
.should('be.visible')
.click()
.then(() => {
cy.wrap(
getDownloadedProposalJsonPath('vega-update-market-proposal-')
).then((filePath) => {
goToMakeNewProposal(governanceProposalType.RAW);
submitUniqueRawProposal({ proposalBody: filePath, submit: false });
});
});
cy.get(newProposalSubmitButton).should('be.visible').click();
cy.contains('Transaction failed', proposalTimeout).should('be.visible');
validateFeedBackMsg(
cy.get(feedbackError).should(
'have.text',
'Network error: the network blocked the transaction through the spam protection: party has insufficient associated governance tokens in their staking account to submit proposal request (ABCI code 89)'
);
});
// 3001-VOTE-092 3004-PMAC-001 3004-PMAC-003
// 3001-VOTE-092 3004-PMAC-001
it('Able to submit update market proposal and vote for proposal', function () {
vegaWalletFaucetAssetsWithoutCheck(
fUSDCId,
@@ -367,20 +313,11 @@ context(
delay: 2,
});
});
cy.get(proposalDownloadBtn)
.should('be.visible')
.click()
.then(() => {
cy.wrap(
getDownloadedProposalJsonPath('vega-update-market-proposal-')
).then((filePath) => {
goToMakeNewProposal(governanceProposalType.RAW);
submitUniqueRawProposal({ proposalBody: filePath });
});
});
cy.get(newProposalSubmitButton).should('be.visible').click();
waitForProposalSubmitted();
navigateTo(navigation.proposals);
cy.get('@EnactedMarketId').then((marketId) => {
cy.contains(String(marketId).slice(0, 6))
cy.contains(String(marketId))
.parentsUntil(proposalListItem)
.last()
.within(() => {
@@ -404,13 +341,12 @@ context(
'contain.text',
'Currently expected to pass'
);
cy.getByTestId('vote-breakdown-toggle').click();
getProposalInformationFromTable('Expected to pass')
.contains('👍 by token vote')
.should('be.visible');
});
// 3001-VOTE-026 3001-VOTE-027 3001-VOTE-028 3001-VOTE-095 3001-VOTE-096 3005-PASN-001
// 3001-VOTE-026 3001-VOTE-027 3001-VOTE-028 3001-VOTE-095 3001-VOTE-096 3005-PASN-001
it('Able to submit new asset proposal using min deadlines', function () {
const proposalTitle = 'Test new asset proposal';
goToMakeNewProposal(governanceProposalType.NEW_ASSET);
@@ -426,23 +362,25 @@ context(
cy.get(minVoteDeadline).click();
cy.get(minValidationDeadline).click();
cy.get(minEnactDeadline).click();
cy.get(proposalDownloadBtn)
.should('be.visible')
.click()
.then(() => {
cy.wrap(
getDownloadedProposalJsonPath('vega-new-asset-proposal-')
).then((filePath) => {
goToMakeNewProposal(governanceProposalType.RAW);
submitUniqueRawProposal({ proposalBody: filePath, submit: false }); // 3005-PASN-003
});
});
cy.get(newProposalSubmitButton).should('be.visible').click();
cy.contains('Awaiting network confirmation', epochTimeout).should(
'be.visible'
);
cy.contains('Proposal waiting for node vote', proposalTimeout).should(
'be.visible'
);
closeDialog();
cy.get(newProposalSubmitButton).should('be.visible').click();
// cannot submit a proposal with ERC20 address already in use
cy.contains('Proposal rejected', proposalTimeout).should('be.visible');
validateDialogContentMsg('PROPOSAL_ERROR_ERC20_ADDRESS_ALREADY_IN_USE');
cy.getByTestId('dialog-content')
.last()
.within(() => {
cy.get('p').should(
'have.text',
'PROPOSAL_ERROR_ERC20_ADDRESS_ALREADY_IN_USE'
);
});
closeDialog();
navigateTo(navigation.proposals);
cy.contains(proposalTitle)
@@ -451,7 +389,6 @@ context(
.within(() => {
cy.getByTestId(viewProposalBtn).click();
});
cy.getByTestId('proposal-terms-toggle').click();
cy.getByTestId(proposalTermsSection).within(() => {
cy.contains('USDT Coin').should('be.visible');
cy.contains('USDT').should('be.visible');
@@ -460,8 +397,15 @@ context(
it('Unable to submit new asset proposal with missing/invalid fields', function () {
goToMakeNewProposal(governanceProposalType.NEW_ASSET);
cy.get(proposalDownloadBtn).should('be.visible').click();
cy.get(newProposalSubmitButton).should('be.visible').click();
cy.get(inputError).should('have.length', 3);
cy.get(newProposalTitle).type('Invalid new asset proposal');
cy.get(newProposalDescription).type('Invalid E2E test for proposals');
cy.get(proposalValidationDeadline).clear().type('2');
cy.get(newProposalSubmitButton).click();
cy.contains('Awaiting network confirmation', epochTimeout).should(
'not.exist'
);
});
it('Able to submit update asset proposal using min deadline', function () {
@@ -472,17 +416,8 @@ context(
enterUpdateAssetProposalDetails();
cy.get(minVoteDeadline).click();
cy.get(minEnactDeadline).click();
cy.get(proposalDownloadBtn)
.should('be.visible')
.click()
.then(() => {
cy.wrap(
getDownloadedProposalJsonPath('vega-update-asset-proposal-')
).then((filePath) => {
goToMakeNewProposal(governanceProposalType.RAW);
submitUniqueRawProposal({ proposalBody: filePath });
});
});
cy.get(newProposalSubmitButton).should('be.visible').click();
waitForProposalSubmitted();
navigateTo(navigation.proposals);
cy.get(openProposals).within(() => {
cy.get(proposalType)
@@ -490,7 +425,7 @@ context(
.parentsUntil(proposalListItem)
.last()
.within(() => {
cy.get(proposalDetails).should('contain.text', assetId.slice(0, 6)); // 3001-VOTE-029
cy.get(proposalDetails).should('contain.text', assetId); // 3001-VOTE-029
cy.getByTestId(viewProposalBtn).click();
});
});
@@ -498,97 +433,37 @@ context(
.invoke('text')
.should('not.be.empty');
// 3001-VOTE-030 3001-VOTE-031
cy.getByTestId('proposal-terms-toggle').click();
cy.getByTestId('proposal-terms').within(() => {
getProposalInformationFromTable('assetId').should('have.text', assetId);
getProposalInformationFromTable('lifetimeLimit').should(
'have.text',
'10'
);
cy.getByTestId(proposalTermsSection).within(() => {
cy.contains('UpdateAsset').should('be.visible');
cy.contains('UpdateERC20').should('be.visible');
cy.contains('"lifetimeLimit": "10"').should('be.visible');
});
});
// 3006-PASC-001 3006-PASC-003
it('Able to submit update asset proposal using max deadline', function () {
goToMakeNewProposal(governanceProposalType.UPDATE_ASSET);
enterUpdateAssetProposalDetails();
cy.get(maxVoteDeadline).click();
cy.get(maxEnactDeadline).click();
cy.get(proposalDownloadBtn)
.should('be.visible')
.click()
.then(() => {
cy.wrap(
getDownloadedProposalJsonPath('vega-update-asset-proposal-')
).then((filePath) => {
goToMakeNewProposal(governanceProposalType.RAW);
submitUniqueRawProposal({ proposalBody: filePath });
});
});
cy.get(newProposalSubmitButton).should('be.visible').click();
waitForProposalSubmitted();
});
it('Unable to submit edit asset proposal with missing/invalid fields', function () {
goToMakeNewProposal(governanceProposalType.UPDATE_ASSET);
cy.get(proposalDownloadBtn).should('be.visible').click();
cy.get(newProposalSubmitButton).should('be.visible').click();
cy.get(inputError).should('have.length', 3);
});
it('Able to download and submit freeform proposal', function () {
goToMakeNewProposal(governanceProposalType.FREEFORM);
// 3008-PFRO-006
cy.get(newProposalTitle)
.siblings()
.should('contain.text', '(100 characters or less)'); // 3008-PFRO-007
// 3008-PFRO-005
cy.get(newProposalTitle).type('Test freeform proposal form');
// 3008-PFRO-009
cy.get(newProposalDescription)
.siblings()
.should('contain.text', '(20,000 characters or less)'); // 3008-PFRO-010
// 3008-PFRO-008 3002-PROP-012 3002-PROP-016
cy.get(newProposalDescription).type(
'E2E test for downloading freeform proposal'
);
// 3008-PFRO-012
cy.get(minVoteDeadline).should('exist'); // 3002-PROP-008
cy.get(maxVoteDeadline).should('exist');
// 3008-PFRO-011
cy.get(proposalVoteDeadline).clear().type('2');
// 3008-PFRO-013 3008-PFRO-014
cy.getByTestId('voting-date').invoke('text').should('not.be.empty');
// 3008-PFRO-015
cy.log('Download updated proposal file');
cy.get(proposalDownloadBtn)
.should('be.visible')
.click()
.then(() => {
cy.wrap(
getDownloadedProposalJsonPath('vega-freeform-proposal-')
).then((filePath) => {
// 3008-PFRO-019
goToMakeNewProposal(governanceProposalType.RAW);
submitUniqueRawProposal({ proposalBody: filePath });
});
});
});
function getFormattedTime() {
const now = new Date();
const day = now.getDate().toString().padStart(2, '0');
const month = now.toLocaleString('en-US', { month: 'short' });
const year = now.getFullYear().toString();
const hours = now.getHours().toString().padStart(2, '0');
const minutes = now.getMinutes().toString().padStart(2, '0');
after('Disassociate from second wallet key if present', function () {
cy.reload();
waitForSpinner();
ethereumWalletConnect();
dissociateFromSecondWalletKey();
});
function validateDialogContentMsg(expectedMsg: string) {
cy.getByTestId('dialog-content')
.last()
.within(() => {
cy.get('p').should('have.text', expectedMsg);
});
}
function validateFeedBackMsg(expectedMsg: string) {
cy.get(feedbackError).should('have.text', expectedMsg);
return `${day}-${month}-${year}-${hours}-${minutes}`;
}
function enterUpdateAssetProposalDetails() {
@@ -86,12 +86,11 @@ describe('Governance flow for proposal list', { tags: '@slow' }, function () {
});
it('Newly created proposals list - shows title and portion of summary', function () {
const proposalPath = 'src/fixtures/proposals/new-market-raw.json';
const proposalTimestamp = createTenDigitUnixTimeStampForSpecifiedDays(3);
const proposalPath = '/proposals/new-market-raw.json';
const enactmentTimestamp = createTenDigitUnixTimeStampForSpecifiedDays(3);
submitUniqueRawProposal({
proposalBody: proposalPath,
enactmentTimestamp: proposalTimestamp,
closingTimestamp: proposalTimestamp,
enactmentTimestamp: enactmentTimestamp,
}); // 3001-VOTE-052
// 3001-VOTE-008
// 3001-VOTE-034
@@ -7,6 +7,7 @@ import {
import {
clickOnValidatorFromList,
closeStakingDialog,
stakingPageAssociateTokens,
stakingValidatorPageAddStake,
waitForBeginningOfEpoch,
} from '../../support/staking.functions';
@@ -29,22 +30,20 @@ context('rewards - flow', { tags: '@slow' }, function () {
turnTelemetryOff();
cy.visit('/');
waitForSpinner();
depositAsset(vegaAssetAddress, '1000', 18);
cy.validatorsSelfDelegate();
ethereumWalletConnect();
cy.connectVegaWallet();
depositAsset(vegaAssetAddress, '1000', 18);
cy.getByTestId('currency-title', txTimeout).should(
'contain.text',
'Collateral'
);
vegaWalletTeardown();
cy.associateTokensToVegaWallet('6000');
cy.VegaWalletTopUpRewardsPool(30, 200);
navigateTo(navigation.validators);
cy.VegaWalletTopUpRewardsPool();
vegaWalletTeardown();
stakingPageAssociateTokens('6000');
cy.get(vegaWalletUnstakedBalance, txTimeout).should(
'contain',
'6,000.0',
txTimeout
);
cy.get('button').contains('Select a validator to nominate').click();
clickOnValidatorFromList(0);
stakingValidatorPageAddStake('3000');
closeStakingDialog();
@@ -109,7 +109,6 @@ context(
cy.getByTestId(userStake, epochTimeout)
.first()
.should('have.text', '2.00');
waitForBeginningOfEpoch();
cy.getByTestId('total-stake').first().realHover();
cy.getByTestId('staked-by-user-tooltip')
.first()
@@ -380,7 +379,6 @@ context(
});
it('Disassociating some tokens - prioritizes unstaked tokens', function () {
vegaWalletSetSpecifiedApprovalAmount('1000');
stakingPageAssociateTokens('3');
verifyUnstakedBalance(3.0);
cy.get('button').contains('Select a validator to nominate').click();
@@ -487,7 +485,6 @@ context(
});
afterEach('Teardown Wallet', function () {
navigateTo(navigation.home);
vegaWalletTeardown();
});
@@ -38,7 +38,7 @@ const associatedKey = '[data-testid="associated-key"]';
const associatedAmount = '[data-testid="associated-amount"]';
const associateCompleteText = '[data-testid="transaction-complete-body"]';
const disassociationWarning = '[data-testid="disassociation-warning"]';
const vegaWallet = 'aside [data-testid="vega-wallet"]';
const vegaWallet = '[data-testid="vega-wallet"]';
context(
'Token association flow - with eth and vega wallets connected',
@@ -78,15 +78,30 @@ context(
//0005-ETXN-003
//0005-ETXN-005
stakingPageAssociateTokens('2', { skipConfirmation: true });
cy.getByTestId('currency-title', txTimeout).should(
'have.length.above',
6
);
validateWalletCurrency('Associated', '0.00');
validateWalletCurrency('Pending association', '2.00');
validateWalletCurrency('Total associated after pending', '2.00');
cy.getByTestId('currency-title', txTimeout).should('have.length', 6);
// 0005-ETXN-002
verifyEthWalletAssociatedBalance('2.0');
verifyEthWalletTotalAssociatedBalance('2.0');
cy.get(vegaWallet).within(() => {
cy.get(vegaWalletAssociatedBalance, txTimeout).should('contain', 2.0);
});
cy.get(vegaWallet)
.first()
.within(() => {
cy.get(vegaWalletAssociatedBalance, txTimeout).should(
'contain',
2.0
);
});
cy.get(vegaWalletUnstakedBalance, txTimeout).should('contain', 2.0);
});
@@ -101,11 +116,15 @@ context(
verifyEthWalletAssociatedBalance('2.0');
verifyEthWalletTotalAssociatedBalance('6,002.00');
cy.get('button').contains('Select a validator to nominate').click();
cy.getByTestId('epoch-countdown').should('be.visible');
stakingPageDisassociateTokens('2');
cy.getByTestId('currency-title', txTimeout).should(
'have.length.above',
6
);
validateWalletCurrency('Associated', '2.00');
validateWalletCurrency('Pending association', '2.00');
validateWalletCurrency('Total associated after pending', '0.00');
cy.getByTestId('currency-title', txTimeout).should('have.length', 6);
cy.get(
'[data-testid="eth-wallet-associated-balances"]:visible',
txTimeout
@@ -118,26 +137,38 @@ context(
stakingPageAssociateTokens('1001', { approve: true });
verifyEthWalletAssociatedBalance('1,001.00');
verifyEthWalletTotalAssociatedBalance('7,001.00');
cy.get(vegaWallet).within(() => {
cy.get(vegaWalletAssociatedBalance, txTimeout).should(
'contain',
'1,001.00'
);
});
cy.get(vegaWallet)
.last()
.within(() => {
cy.get(vegaWalletAssociatedBalance, txTimeout).should(
'contain',
'1,001.00'
);
});
});
it('Able to disassociate a partial amount of tokens currently associated', function () {
stakingPageAssociateTokens('2');
cy.get(vegaWallet).within(() => {
cy.get(vegaWalletAssociatedBalance, txTimeout).should('contain', 2.0);
});
cy.get(vegaWallet)
.first()
.within(() => {
cy.get(vegaWalletAssociatedBalance, txTimeout).should(
'contain',
2.0
);
});
cy.get('button').contains('Select a validator to nominate').click();
cy.getByTestId('epoch-countdown').should('be.visible');
stakingPageDisassociateTokens('1');
verifyEthWalletAssociatedBalance('1.0');
cy.get(vegaWallet).within(() => {
cy.get(vegaWalletAssociatedBalance, txTimeout).should('contain', 1.0);
});
cy.get(vegaWallet)
.first()
.within(() => {
cy.get(vegaWalletAssociatedBalance, txTimeout).should(
'contain',
1.0
);
});
});
it('Able to disassociate all tokens - using max', function () {
@@ -145,11 +176,15 @@ context(
const warningText =
'Warning: Any tokens that have been nominated to a node will sacrifice rewards they are due for the current epoch. If you do not wish to sacrifice these, you should remove stake from a node at the end of an epoch before disassociation.';
stakingPageAssociateTokens('2');
cy.get(vegaWallet).within(() => {
cy.get(vegaWalletAssociatedBalance, txTimeout).should('contain', 2.0);
});
cy.get(vegaWallet)
.first()
.within(() => {
cy.get(vegaWalletAssociatedBalance, txTimeout).should(
'contain',
2.0
);
});
cy.get('button').contains('Select a validator to nominate').click();
cy.getByTestId('epoch-countdown').should('be.visible');
cy.get(ethWalletDissociateButton).click();
cy.get(disassociationWarning).should('contain', warningText);
stakingPageDisassociateAllTokens();
@@ -167,9 +202,14 @@ context(
'not.exist'
);
});
cy.get(vegaWallet).within(() => {
cy.get(vegaWalletAssociatedBalance, txTimeout).should('contain', 0.0);
});
cy.get(vegaWallet)
.first()
.within(() => {
cy.get(vegaWalletAssociatedBalance, txTimeout).should(
'contain',
0.0
);
});
});
it('Able to associate and disassociate vesting contract tokens', function () {
@@ -184,22 +224,38 @@ context(
type: 'contract',
skipConfirmation: true,
});
cy.getByTestId('currency-title', txTimeout).should(
'have.length.above',
6
);
validateWalletCurrency('Associated', '0.00');
validateWalletCurrency('Pending association', '2.00');
validateWalletCurrency('Total associated after pending', '2.00');
cy.getByTestId('currency-title', txTimeout).should('have.length', 6);
verifyEthWalletAssociatedBalance('2.0');
verifyEthWalletTotalAssociatedBalance('2.0');
cy.get(vegaWallet).within(() => {
cy.get(vegaWalletAssociatedBalance, txTimeout).should('contain', 2.0);
});
cy.get(vegaWallet)
.first()
.within(() => {
cy.get(vegaWalletAssociatedBalance, txTimeout).should(
'contain',
2.0
);
});
cy.get(vegaWalletUnstakedBalance, txTimeout).should('contain', 2.0);
stakingPageDisassociateTokens('1', {
type: 'contract',
skipConfirmation: true,
});
cy.getByTestId('currency-title', txTimeout).should(
'have.length.above',
6
);
validateWalletCurrency('Associated', '2.00');
validateWalletCurrency('Pending association', '1.00');
validateWalletCurrency('Total associated after pending', '1.00');
cy.getByTestId('currency-title', txTimeout).should('have.length', 6);
verifyEthWalletAssociatedBalance('1.0');
verifyEthWalletTotalAssociatedBalance('1.0');
});
@@ -211,7 +267,6 @@ context(
// 1004-ASSO-022
stakingPageAssociateTokens('21', { type: 'wallet' });
cy.get('button').contains('Select a validator to nominate').click();
cy.getByTestId('epoch-countdown').should('be.visible');
stakingPageAssociateTokens('37', { type: 'contract' });
cy.get(vestingContractSection)
.first()
@@ -231,18 +286,28 @@ context(
);
cy.get(associatedAmount, txTimeout).should('contain', 21);
});
cy.get(vegaWallet).within(() => {
cy.get(vegaWalletAssociatedBalance, txTimeout).should('contain', 58);
});
cy.get(vegaWallet)
.first()
.within(() => {
cy.get(vegaWalletAssociatedBalance, txTimeout).should(
'contain',
58
);
});
stakingPageDisassociateTokens('6', { type: 'contract' });
cy.get(vestingContractSection)
.first()
.within(() => {
cy.get(associatedAmount, txTimeout).should('contain', 31);
});
cy.get(vegaWallet).within(() => {
cy.get(vegaWalletAssociatedBalance, txTimeout).should('contain', 52);
});
cy.get(vegaWallet)
.first()
.within(() => {
cy.get(vegaWalletAssociatedBalance, txTimeout).should(
'contain',
52
);
});
navigateTo(navigation.validators);
stakingPageDisassociateTokens('9', { type: 'wallet' });
cy.get(vegaInWalletSection)
@@ -250,9 +315,14 @@ context(
.within(() => {
cy.get(associatedAmount, txTimeout).should('contain', 12);
});
cy.get(vegaWallet).within(() => {
cy.get(vegaWalletAssociatedBalance, txTimeout).should('contain', 43);
});
cy.get(vegaWallet)
.first()
.within(() => {
cy.get(vegaWalletAssociatedBalance, txTimeout).should(
'contain',
43
);
});
});
it('Not able to associate more tokens than owned', function () {
@@ -269,9 +339,14 @@ context(
// 1004-ASSO-004
it('Pending association outside of app is shown', function () {
vegaWalletAssociate('2');
cy.getByTestId('currency-title', txTimeout).should(
'have.length.above',
6
);
validateWalletCurrency('Associated', '0.00');
validateWalletCurrency('Pending association', '2.00');
validateWalletCurrency('Total associated after pending', '2.00');
cy.getByTestId('currency-title', txTimeout).should('have.length', 6);
validateWalletCurrency('Associated', '2.00');
});
@@ -280,9 +355,14 @@ context(
cy.wrap(validateWalletCurrency('Associated', '2.00')).then(() => {
vegaWalletDisassociate('2');
});
cy.getByTestId('currency-title', txTimeout).should(
'have.length.above',
6
);
validateWalletCurrency('Associated', '2.00');
validateWalletCurrency('Pending association', '2.00');
validateWalletCurrency('Total associated after pending', '0.00');
cy.getByTestId('currency-title', txTimeout).should('have.length', 6);
validateWalletCurrency('Associated', '0.00');
});
@@ -301,9 +381,14 @@ context(
Cypress.env('vegaWalletPublicKey2')
);
stakingPageAssociateTokens('2');
cy.get(vegaWallet).within(() => {
cy.get(vegaWalletAssociatedBalance, txTimeout).should('contain', 2.0);
});
cy.get(vegaWallet)
.first()
.within(() => {
cy.get(vegaWalletAssociatedBalance, txTimeout).should(
'contain',
2.0
);
});
cy.get(associateCompleteText).should(
'have.text',
`Vega key ${Cypress.env(
@@ -15,7 +15,13 @@ const balanceAvailable = 'BALANCE_AVAILABLE_value';
const withdrawalThreshold = 'WITHDRAWAL_THRESHOLD_value';
const delayTime = 'DELAY_TIME_value';
const submitWithdrawalButton = 'submit-withdrawal';
const dialogTitle = 'dialog-title';
const dialogClose = 'dialog-close';
const txExplorerLink = 'tx-block-explorer';
const withdrawalAssetSymbol = 'withdrawal-asset-symbol';
const withdrawalAmount = 'withdrawal-amount';
const withdrawalRecipient = 'withdrawal-recipient';
const withdrawFundsButton = 'withdraw-funds';
const completeWithdrawalButton = 'complete-withdrawal';
const tableTxHash = '[col-id="txHash"]';
const tableAssetSymbol = '[col-id="asset.symbol"]';
@@ -24,16 +30,14 @@ const tableReceiverAddress = '[col-id="details.receiverAddress"]';
const tableWithdrawnTimeStamp = '[col-id="withdrawnTimestamp"]';
const tableWithdrawnStatus = '[col-id="status"]';
const tableCreatedTimeStamp = '[col-id="createdTimestamp"]';
const toast = 'toast';
const toastContent = 'toast-content';
const toastPanel = 'toast-panel';
const toastClose = 'toast-close';
const withdrawalDialogContent = 'dialog-content';
const toastCompleteWithdrawal = 'toast-complete-withdrawal';
const usdtName = 'USDC (local)';
const usdcEthAddress = '0x1b8a1B6CBE5c93609b46D1829Cc7f3Cb8eeE23a0';
const usdcSymbol = 'tUSDC';
const usdtSelectValue =
'993ed98f4f770d91a796faab1738551193ba45c62341d20597df70fea6704ede';
const truncatedWithdrawalEthAddress = '0xEe7D…22d94F';
const formValidationError = 'input-error-text';
const txTimeout = Cypress.env('txTimeout');
@@ -103,35 +107,29 @@ context(
cy.getByTestId(amountInput).click().type('120');
cy.getByTestId(submitWithdrawalButton).click();
});
cy.contains('Awaiting network confirmation').should('be.visible');
// assert withdrawal request
cy.getByTestId(toast)
.first(txTimeout)
.should('contain.text', 'Funds unlocked')
.within(() => {
cy.getByTestId('external-link').should('exist');
cy.getByTestId(toastPanel).should(
'contain.text',
'Withdraw 120.00 tUSDC'
);
cy.getByTestId(toastCompleteWithdrawal).click();
cy.getByTestId(toastClose).click();
});
cy.getByTestId(dialogTitle, txTimeout).should(
'have.text',
'Transaction complete'
);
cy.getByTestId(txExplorerLink)
.should('have.attr', 'href')
.and('contain', '/txs/');
cy.getByTestId(withdrawalAssetSymbol).should('have.text', usdcSymbol);
cy.getByTestId(withdrawalAmount).should('have.text', '120.00');
cy.getByTestId(withdrawalRecipient)
.should('have.text', truncatedWithdrawalEthAddress)
.and('have.attr', 'href')
.and('contain', `/address/${Cypress.env('ethWalletPublicKey')}`);
cy.getByTestId(withdrawFundsButton).click();
// withdrawal complete
cy.getByTestId(toast)
.first(txTimeout)
.should('contain.text', 'The withdrawal has been approved.')
.within(() => {
cy.getByTestId(toastPanel).should(
'contain.text',
'Withdraw 120.00 tUSDC'
);
});
cy.getByTestId(toast)
.last(txTimeout)
.should('contain.text', 'Transaction confirmed')
.within(() => {
cy.getByTestId('external-link').should('exist');
});
cy.getByTestId(dialogTitle, txTimeout).should(
'have.text',
'Withdraw asset complete'
);
cy.getByTestId(dialogClose).click();
// withdrawal history for complete withdrawal displayed
cy.get(tableWithdrawnStatus)
.eq(1, txTimeout)
@@ -170,17 +168,13 @@ context(
cy.getByTestId(submitWithdrawalButton).click();
});
cy.getByTestId(toast)
.first(txTimeout)
.should('contain.text', 'Funds unlocked')
.within(() => {
cy.getByTestId('external-link').should('exist');
cy.getByTestId(toastPanel).should(
'contain.text',
'Withdraw 110.00 tUSDC'
);
cy.getByTestId(toastClose).click();
});
cy.contains('Awaiting network confirmation').should('be.visible');
// assert withdrawal request
cy.getByTestId(dialogTitle, txTimeout).should(
'have.text',
'Transaction complete'
);
cy.getByTestId(dialogClose).click();
cy.get(tableTxHash)
.eq(1)
.should('have.text', 'Complete withdrawal')
@@ -195,75 +189,28 @@ context(
cy.get(tableCreatedTimeStamp).should('not.be.empty');
});
ethereumWalletConnect();
cy.getByTestId(completeWithdrawalButton).first().click();
cy.getByTestId(toast)
.last(txTimeout)
cy.getByTestId(completeWithdrawalButton).click();
cy.getByTestId(toastContent)
.last()
.should('contain.text', 'Awaiting confirmation')
.within(() => {
cy.getByTestId('external-link').should('exist');
});
cy.getByTestId(toast)
.first(txTimeout)
cy.getByTestId(toastContent)
.first()
.should('contain.text', 'The withdrawal has been approved.')
.within(() => {
cy.getByTestId(toastPanel).should('contain.text', '110.00', 'tUSDC');
});
cy.getByTestId(toast)
.last(txTimeout)
cy.getByTestId(toastContent)
.last()
.should('contain.text', 'Transaction confirmed')
.within(() => {
cy.getByTestId('external-link').should('exist');
});
});
it('Should be able to see withdrawal details from toast', function () {
cy.getByTestId(withdraw).click();
cy.getByTestId(withdrawalForm, txTimeout).within(() => {
cy.get('select').select(usdtSelectValue, { force: true });
cy.getByTestId(balanceAvailable, txTimeout).should('exist');
cy.getByTestId(withdrawalThreshold).should(
'have.text',
'100,000.00000T'
);
cy.getByTestId(amountInput).click().type('50');
cy.getByTestId(submitWithdrawalButton).click();
});
cy.getByTestId(toast)
.first(txTimeout)
.should('contain.text', 'Funds unlocked')
.within(() => {
cy.getByTestId('external-link').should('exist');
cy.getByTestId(toastPanel).should(
'contain.text',
'Withdraw 50.00 tUSDC'
);
cy.contains('save your withdrawal details').click();
});
cy.getByTestId(withdrawalDialogContent)
.last()
.within(() => {
cy.getByTestId('assetSource_value').should(
'have.text',
'0x1b8a1B6CBE5c93609b46D1829Cc7f3Cb8eeE23a0'
);
cy.getByTestId('amount_value').should('have.text', '5000000');
cy.getByTestId('nonce_value').invoke('text').should('not.be.empty');
cy.getByTestId('signatures_value')
.invoke('text')
.should('not.be.empty');
cy.getByTestId('targetAddress_value').should(
'have.text',
Cypress.env('ethWalletPublicKey')
);
cy.getByTestId('creation_value')
.invoke('text')
.should('not.be.empty');
});
cy.getByTestId(dialogClose).click();
});
// Skipping test due to bug #3882
it.skip('Unable to withdraw asset on pub key view', function () {
it('Unable to withdraw asset on pub key view', function () {
const vegaWalletPubKey = Cypress.env('vegaWalletPublicKey');
const expectedErrorTxt = `You are connected in a view only state for public key: ${vegaWalletPubKey}. In order to send transactions you must connect to a real wallet.`;
@@ -277,11 +224,10 @@ context(
cy.get('select').select(usdtSelectValue, { force: true });
cy.getByTestId(balanceAvailable, txTimeout).should('exist');
cy.getByTestId(amountInput).click().type('100');
cy.pause();
cy.getByTestId(submitWithdrawalButton).click();
});
cy.getByTestId(withdrawalDialogContent)
cy.getByTestId('dialog-content')
.last()
.within(() => {
cy.get('h1').should('have.text', 'Transaction failed');
@@ -11,6 +11,7 @@ import {
import { mockNetworkUpgradeProposal } from '../../support/proposal.functions';
const proposalDocumentationLink = '[data-testid="proposal-documentation-link"]';
const newProposalLink = '[data-testid="new-proposal-link"]';
const governanceDocsUrl = 'https://vega.xyz/governance';
const connectToVegaWalletButton = '[data-testid="connect-to-vega-wallet-btn"]';
@@ -31,14 +32,6 @@ context(
verifyPageHeader('Proposals');
});
// 3002-PROP-023 3004-PMAC-002 3005-PASN-002 3006-PASC-002 3007-PNEC-002 3008-PFRO-003
it('should have button for link to more information on proposals', function () {
const proposalsUrl = 'https://docs.vega.xyz/mainnet/tutorials/proposals';
cy.getByTestId('new-proposal-link')
.find('a')
.should('have.attr', 'href', proposalsUrl);
});
it('should be able to see a working link for - find out more about Vega governance', function () {
// 3001-VOTE-001
cy.get(proposalDocumentationLink)
@@ -61,62 +54,17 @@ context(
});
});
// 3007-PNE-021
it('should have documentation links for network parameter proposal', function () {
it.skip('should be able to see button for - new proposal', function () {
// 3001-VOTE-002
cy.get(newProposalLink)
.should('be.visible')
.and('have.text', 'New proposal')
.and('have.attr', 'href')
.and('equal', '/proposals/propose');
});
it.skip('should be able to see a connect wallet button - if vega wallet disconnected and user is submitting new proposal', function () {
goToMakeNewProposal(governanceProposalType.NETWORK_PARAMETER);
cy.getByTestId('proposal-docs-link')
.find('a')
.should('have.attr', 'href')
.and('contain', '/tutorials/proposals/network-parameter-proposal');
});
// 3003-PMAN-002 3003-PMAN-005
it('should have documentation links for new market proposal', function () {
goToMakeNewProposal(governanceProposalType.NEW_MARKET);
cy.getByTestId('proposal-docs-link')
.find('a')
.should('have.attr', 'href')
.and('contain', '/tutorials/proposals/new-market-proposal');
});
// 3004-PMAC-005
it('should have documentation links for update market proposal', function () {
goToMakeNewProposal(governanceProposalType.UPDATE_MARKET);
cy.getByTestId('proposal-docs-link')
.find('a')
.should('have.attr', 'href')
.and('contain', '/tutorials/proposals/update-market-proposal');
});
// 3005-PASN-002 005-PASN-005
it('should have documentation links for new asset proposal', function () {
goToMakeNewProposal(governanceProposalType.NEW_ASSET);
cy.getByTestId('proposal-docs-link')
.find('a')
.should('have.attr', 'href')
.and('contain', '/tutorials/proposals/new-asset-proposal');
});
// 3006-PASC-002 3006-PASC-005
it('should have documentation links for update asset proposal', function () {
goToMakeNewProposal(governanceProposalType.UPDATE_ASSET);
cy.getByTestId('proposal-docs-link')
.find('a')
.should('have.attr', 'href')
.and('contain', '/tutorials/proposals/update-asset-proposal');
});
// 3008-PFRO-003 3008-PFRO-017
it('should have documentation links for freeform proposal', function () {
goToMakeNewProposal(governanceProposalType.FREEFORM);
cy.getByTestId('proposal-docs-link')
.find('a')
.should('have.attr', 'href')
.and('contain', '/tutorials/proposals/freeform-proposal');
});
it('should be able to see a connect wallet button - if vega wallet disconnected and user is submitting new proposal', function () {
goToMakeNewProposal(governanceProposalType.RAW);
cy.get(connectToVegaWalletButton)
.should('be.visible')
.and('have.text', 'Connect Vega wallet');
@@ -180,7 +128,6 @@ context(
.first()
.find('[data-testid="view-proposal-btn"]')
.click();
cy.url().should('contain', '/protocol-upgrades/v1');
cy.getByTestId('protocol-upgrade-proposal').within(() => {
cy.get('h1').should('have.text', 'Vega Release v1');
cy.getByTestId('protocol-upgrade-block-height').should(
@@ -1,11 +1,8 @@
import { stakingPageDisassociateAllTokens } from './staking.functions';
const tokenDropDown = 'state-trigger';
const txTimeout = Cypress.env('txTimeout');
export enum navigation {
section = 'nav',
home = '[href="/"]',
vesting = '[href="/token/redeem"]',
validators = '[href="/validators"]',
rewards = '[href="/rewards"]',
@@ -21,7 +18,6 @@ export function convertTokenValueToNumber(subject: string) {
}
const topLevelRoutes = [
navigation.home,
navigation.proposals,
navigation.validators,
navigation.rewards,
@@ -101,25 +97,3 @@ export function turnTelemetryOff() {
win.localStorage.setItem('vega_telemetry_on', 'false')
);
}
export function dissociateFromSecondWalletKey() {
const secondWalletKey = Cypress.env('vegaWalletPublicKey2Short');
cy.getByTestId('vega-in-wallet')
.first()
.within(() => {
cy.getByTestId('eth-wallet-associated-balances')
.last()
.within(() => {
cy.getByTestId('associated-key')
.invoke('text')
.as('associatedPubKey');
});
});
cy.get('@associatedPubKey').then((associatedPubKey) => {
if (associatedPubKey == secondWalletKey) {
cy.get('[data-testid="manage-vega-wallet"]:visible').click();
cy.get('[data-testid="select-keypair-button"]').eq(0).click();
stakingPageDisassociateAllTokens();
}
});
}
@@ -59,11 +59,11 @@ export function submitUniqueRawProposal(proposalFields: {
submit?: boolean;
}) {
goToMakeNewProposal(governanceProposalType.RAW);
let proposalBodyPath = 'src/fixtures/proposals/raw.json';
let proposalBodyPath = '/proposals/raw.json';
if (proposalFields.proposalBody) {
proposalBodyPath = proposalFields.proposalBody;
}
cy.readFile(proposalBodyPath).then((rawProposal) => {
cy.fixture(proposalBodyPath).then((rawProposal) => {
if (proposalFields.proposalTitle) {
rawProposal.rationale.title = proposalFields.proposalTitle;
cy.wrap(proposalFields.proposalTitle).as('proposalTitle');
@@ -73,10 +73,7 @@ export function submitUniqueRawProposal(proposalFields: {
}
if (proposalFields.closingTimestamp) {
rawProposal.terms.closingTimestamp = proposalFields.closingTimestamp;
} else if (
!proposalFields.closingTimestamp &&
!proposalFields.proposalBody
) {
} else {
const minTimeStamp = createTenDigitUnixTimeStampForSpecifiedDays(2);
rawProposal.terms.closingTimestamp = minTimeStamp;
}
@@ -109,7 +106,7 @@ export function enterUniqueFreeFormProposalBody(
'this is a e2e freeform proposal description'
);
cy.get(proposalVoteDeadline).clear().click().type(timestamp);
cy.getByTestId('proposal-download-json').should('be.visible').click();
cy.getByTestId('proposal-submit').should('be.visible').click();
}
export function getProposalFromTitle(proposalTitle: string) {
@@ -191,7 +188,6 @@ export function goToMakeNewProposal(proposalType: governanceProposalType) {
}
}
// 3001-VOTE-013 3001-VOTE-014
export function waitForProposalSubmitted() {
cy.contains('Awaiting network confirmation', epochTimeout).should(
'be.visible'
@@ -226,23 +222,6 @@ export function createFreeformProposal(proposalTitle: string) {
navigateTo(navigation.proposals);
}
export function getDownloadedProposalJsonPath(proposalType: string) {
const downloadPath = './cypress/downloads/';
const filepath = downloadPath + proposalType + getFormattedTime() + '.json';
return filepath;
}
function getFormattedTime() {
const now = new Date();
const day = now.getDate().toString().padStart(2, '0');
const month = now.toLocaleString('en-US', { month: 'short' });
const year = now.getFullYear().toString();
const hours = now.getHours().toString().padStart(2, '0');
const minutes = now.getMinutes().toString().padStart(2, '0');
return `${day}-${month}-${year}-${hours}-${minutes}`;
}
export enum governanceProposalType {
NETWORK_PARAMETER = 'Network parameter',
NEW_MARKET = 'New market',
@@ -221,7 +221,7 @@ export function ensureSpecifiedUnstakedTokensAreAssociated(
}
export function closeStakingDialog() {
cy.getByTestId('dialog-title', txTimeout).should(
cy.getByTestId('dialog-title').should(
'contain.text',
'At the beginning of the next epoch'
);
@@ -236,8 +236,8 @@ export function validateWalletCurrency(
currencyTitle: string,
expectedAmount: string
) {
cy.get("[data-testid='currency-title']", txTimeout)
.contains(currencyTitle, txTimeout)
cy.get("[data-testid='currency-title']")
.contains(currencyTitle)
.parent()
.parent()
.within(() => {
@@ -5,7 +5,7 @@ const capsuleWalletConnectButton = '[data-testid="web3-connector-Unknown"]';
export function ethereumWalletConnect() {
cy.highlight('Connecting Eth Wallet');
cy.get(connectToEthButton, { timeout: 60000 }).within(() => {
cy.get(connectToEthButton).within(() => {
cy.contains('Connect Ethereum wallet to associate $VEGA')
.should('be.visible')
.click();
@@ -19,7 +19,7 @@ const ethStakingBridgeContractAddress = Cypress.env(
);
const ethProviderUrl = Cypress.env('ethProviderUrl');
const getAccount = (number = 0) => `m/44'/60'/0'/0/${number}`;
const transactionTimeout = { timeout: 100000, log: false };
const transactionTimeout = 100000;
const Erc20BridgeAddress = '0x9708FF7510D4A7B9541e1699d15b53Ecb1AFDc54';
const provider = new ethers.providers.JsonRpcProvider({ url: ethProviderUrl });
@@ -43,7 +43,10 @@ export async function depositAsset(
const faucet = new Token(assetEthAddress, signer);
cy.wrap(
faucet.approve(Erc20BridgeAddress, amount + '0'.repeat(decimalPlaces + 1)),
transactionTimeout
{
timeout: transactionTimeout,
log: false,
}
).then(() => {
const collateralBridge = new CollateralBridge(Erc20BridgeAddress, signer);
cy.wrap(
@@ -52,7 +55,7 @@ export async function depositAsset(
amount + '0'.repeat(decimalPlaces),
'0x' + vegaWalletPubKey
),
transactionTimeout
{ timeout: transactionTimeout, log: false }
);
});
}
@@ -76,13 +79,13 @@ export async function vegaWalletTeardown() {
}
});
cy.get(vegaWalletContainer).within(() => {
cy.get(associatedAmountInWallet, transactionTimeout).should(
'have.length',
1
);
cy.get(associatedAmountInWallet)
.first(transactionTimeout)
.should('have.text', '0.00');
cy.get(associatedAmountInWallet, {
timeout: transactionTimeout,
})
.should('have.length', 1, { timeout: transactionTimeout })
.contains('0.00', {
timeout: transactionTimeout,
});
});
});
}
@@ -106,7 +109,7 @@ async function vegaWalletTeardownStaking(stakingBridgeContract: StakingBridge) {
cy.highlight('Tearing down staking tokens from vega wallet if present');
cy.wrap(
stakingBridgeContract.stake_balance(ethWalletPubKey, vegaWalletPubKey),
transactionTimeout
{ timeout: transactionTimeout }
).then((stakeBalance) => {
if (Number(stakeBalance) != 0) {
cy.get(vegaWalletContainer).within(() => {
@@ -119,25 +122,31 @@ async function vegaWalletTeardownStaking(stakingBridgeContract: StakingBridge) {
String(stakeBalance),
vegaWalletPubKey
),
transactionTimeout
{ timeout: transactionTimeout }
);
cy.wrap(
vestingContract.stake_balance(ethWalletPubKey, vegaWalletPubKey),
transactionTimeout
{
timeout: transactionTimeout,
log: false,
}
).then((vestingAmount) => {
if (Number(vestingAmount) != 0) {
cy.contains('Associated', transactionTimeout)
cy.contains('Associated', {
timeout: transactionTimeout,
})
.parent()
.parent()
.within(() => {
cy.getByTestId('currency-value', transactionTimeout)
.first()
cy.getByTestId('currency-value', {
timeout: transactionTimeout,
})
.should('have.length', 1)
.invoke('text')
.as('displayedAmount');
cy.get('@displayedAmount', transactionTimeout).should(
'not.eq',
$associatedAmount
);
cy.get('@displayedAmount', {
timeout: transactionTimeout,
}).should('not.eq', $associatedAmount);
});
}
});
@@ -149,14 +158,14 @@ async function vegaWalletTeardownStaking(stakingBridgeContract: StakingBridge) {
async function vegaWalletTeardownVesting(vestingContract: TokenVesting) {
cy.highlight('Tearing down vesting tokens from vega wallet if present');
cy.wrap(
vestingContract.stake_balance(ethWalletPubKey, vegaWalletPubKey),
transactionTimeout
).then((vestingAmount) => {
cy.wrap(vestingContract.stake_balance(ethWalletPubKey, vegaWalletPubKey), {
timeout: transactionTimeout,
log: false,
}).then((vestingAmount) => {
if (Number(vestingAmount) != 0) {
cy.wrap(
vestingContract.remove_stake(String(vestingAmount), vegaWalletPubKey),
transactionTimeout
{ timeout: transactionTimeout }
);
}
});
+2 -3
View File
@@ -5,11 +5,10 @@ NX_VEGA_URL=https://api.n00.stagnet1.vega.xyz/graphql
NX_ETHEREUM_PROVIDER_URL=https://sepolia.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8
NX_ETHERSCAN_URL=https://sepolia.etherscan.io
NX_FAIRGROUND=false
NX_VEGA_NETWORKS='{"DEVNET":"https://dev.governance.vega.xyz","STAGNET1":"https://governance.stagnet1.vega.rocks","TESTNET":"https://governance.fairground.wtf","MAINNET":"https://governance.vega.xyz"}'
NX_VEGA_NETWORKS='{"DEVNET":"https://dev.governance.vega.xyz","STAGNET1":"https://stagnet1.governance.vega.xyz","TESTNET":"https://governance.fairground.wtf","MAINNET":"https://governance.vega.xyz"}'
NX_GITHUB_FEEDBACK_URL=https://github.com/vegaprotocol/feedback/discussions
NX_VEGA_WALLET_URL=http://localhost:1789
NX_VEGA_EXPLORER_URL=https://explorer.stagnet1.vega.rocks
NX_VEGA_EXPLORER_URL=https://stagnet1.explorer.vega.xyz
NX_HOSTED_WALLET_URL=https://wallet.testnet.vega.xyz
NX_VEGA_DOCS_URL=https://docs.vega.xyz/testnet
NX_DELEGATIONS_PAGINATION=50
+1 -2
View File
@@ -6,10 +6,9 @@ NX_VEGA_NETWORKS='{"DEVNET":"https://dev.governance.vega.xyz","TESTNET":"https:/
NX_ETHEREUM_PROVIDER_URL=https://sepolia.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8
NX_ETHERSCAN_URL=https://sepolia.etherscan.io
NX_GITHUB_FEEDBACK_URL=https://github.com/vegaprotocol/feedback/discussions
NX_VEGA_EXPLORER_URL=#
NX_VEGA_EXPLORER_URL=https://dev.explorer.vega.xyz
NX_VEGA_DOCS_URL=https://docs.vega.xyz/testnet
NX_DELEGATIONS_PAGINATION=50
NX_TRANCHES_SERVICE_URL=https://tranches-devnet1-k8s.ops.vega.xyz
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json
NX_VEGA_REST_URL=https://api.n00.devnet1.vega.xyz/api/v2/
NX_SENTRY_DSN=https://4b8c8a8ba07742648aa4dfe1b8d17e40@o286262.ingest.sentry.io/5882996
+1 -2
View File
@@ -3,10 +3,9 @@ NX_VEGA_ENV=MAINNET
NX_VEGA_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/networks/master/mainnet1/mainnet1.toml
NX_VEGA_URL=https://api.vega.community/graphql
NX_VEGA_NETWORKS='{"DEVNET":"https://dev.governance.vega.xyz","TESTNET":"https://governance.fairground.wtf","MAINNET":"https://governance.vega.xyz"}'
NX_ETHEREUM_PROVIDER_URL=https://mainnet.infura.io/v3/3ba145ccc2884bcd91213d8dc989ca76
NX_ETHEREUM_PROVIDER_URL=https://mainnet.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8
NX_ETHERSCAN_URL=https://etherscan.io
NX_GITHUB_FEEDBACK_URL=https://github.com/vegaprotocol/feedback/discussions
NX_SENTRY_DSN=https://4b8c8a8ba07742648aa4dfe1b8d17e40@o286262.ingest.sentry.io/5882996
NX_VEGA_EXPLORER_URL=https://explorer.vega.xyz
NX_VEGA_DOCS_URL=https://docs.vega.xyz/mainnet
NX_SENTRY_DSN=https://4b8c8a8ba07742648aa4dfe1b8d17e40:87edc2605e544f888305d7fc4a9141bd@o286262.ingest.sentry.io/5882996
+2 -2
View File
@@ -1,9 +1,9 @@
# App configuration variables
NX_VEGA_URL=https://api.n00.stagnet1.vega.xyz/graphql
NX_VEGA_ENV=STAGNET1
NX_VEGA_NETWORKS='{"DEVNET":"https://dev.governance.vega.xyz","STAGNET1":"https://governance.stagnet1.vega.rocks","TESTNET":"https://governance.fairground.wtf","MAINNET":"https://governance.vega.xyz"}'
NX_VEGA_NETWORKS='{"DEVNET":"https://dev.governance.vega.xyz","STAGNET1":"https://stagnet1.governance.vega.xyz","TESTNET":"https://governance.fairground.wtf","MAINNET":"https://governance.vega.xyz"}'
NX_VEGA_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/networks-internal/main/stagnet1/vegawallet-stagnet1.toml
NX_VEGA_EXPLORER_URL=https://explorer.stagnet1.vega.rocks
NX_VEGA_EXPLORER_URL=https://stagnet1.explorer.vega.xyz
NX_VEGA_DOCS_URL=https://docs.vega.xyz/testnet
NX_DELEGATIONS_PAGINATION=50
NX_TRANCHES_SERVICE_URL=https://tranches-stagnet1-k8s.ops.vega.xyz
+1 -2
View File
@@ -12,5 +12,4 @@ NX_HOSTED_WALLET_URL=https://wallet.testnet.vega.xyz
NX_DELEGATIONS_PAGINATION=50
NX_TRANCHES_SERVICE_URL=https://tranches-testnet-k8s.ops.vega.xyz
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json
NX_VEGA_REST_URL=https://api.n07.testnet.vega.xyz/api/v2/
NX_SENTRY_DSN=https://4b8c8a8ba07742648aa4dfe1b8d17e40@o286262.ingest.sentry.io/5882996
NX_VEGA_REST_URL=https://api.n07.testnet.vega.xyz/api/v2/
@@ -7,7 +7,6 @@ NX_VEGA_NETWORKS='{"DEVNET":"https://dev.governance.vega.xyz","TESTNET":"https:/
NX_ETHEREUM_PROVIDER_URL=https://sepolia.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8
NX_ETHERSCAN_URL=https://sepolia.etherscan.io
NX_GITHUB_FEEDBACK_URL=https://github.com/vegaprotocol/feedback/discussions
NX_VEGA_EXPLORER_URL=https://explorer.validators-testnet.vega.rocks/
NX_VEGA_EXPLORER_URL=https://dev.explorer.vega.xyz
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json
NX_VEGA_REST_URL=https://api-validators-testnet.vega.rocks/api/v2/
NX_SENTRY_DSN=https://4b8c8a8ba07742648aa4dfe1b8d17e40@o286262.ingest.sentry.io/5882996
NX_VEGA_REST_URL=https://api-validators-testnet.vega.rocks/api/v2/
@@ -1 +0,0 @@
export * from './multisig-incorrect-notice';
@@ -1,52 +0,0 @@
import { render, screen } from '@testing-library/react';
import { useEthereumConfig } from '@vegaprotocol/web3';
import { useEnvironment } from '@vegaprotocol/environment';
import { MultisigIncorrectNotice } from './multisig-incorrect-notice';
jest.mock('@vegaprotocol/web3', () => ({
useEthereumConfig: jest.fn(),
}));
jest.mock('@vegaprotocol/environment', () => ({
useEnvironment: jest.fn(),
}));
describe('MultisigIncorrectNotice', () => {
it('renders correctly when config is provided', () => {
(useEthereumConfig as jest.Mock).mockReturnValue({
config: {
multisig_control_contract: {
address: '0x1234',
},
},
});
(useEnvironment as unknown as jest.Mock).mockReturnValue({
ETHERSCAN_URL: 'https://etherscan.io',
});
render(<MultisigIncorrectNotice />);
expect(screen.getByTestId('multisig-contract-link')).toHaveAttribute(
'href',
'https://etherscan.io/address/0x1234'
);
expect(screen.getByTestId('multisig-contract-link')).toHaveAttribute(
'title',
'0x1234'
);
expect(
screen.getByTestId('multisig-validators-learn-more')
).toBeInTheDocument();
});
it('does not render when config is not provided', () => {
(useEthereumConfig as jest.Mock).mockReturnValue({
config: null,
});
const { container } = render(<MultisigIncorrectNotice />);
expect(container.firstChild).toBeNull();
});
});
@@ -1,49 +0,0 @@
import { useTranslation } from 'react-i18next';
import { Callout, Intent, Link } from '@vegaprotocol/ui-toolkit';
import { useEthereumConfig } from '@vegaprotocol/web3';
import { useEnvironment, DocsLinks } from '@vegaprotocol/environment';
import type { EthereumConfig } from '@vegaprotocol/web3';
export const MultisigIncorrectNotice = () => {
const { t } = useTranslation();
const { config } = useEthereumConfig();
const { ETHERSCAN_URL } = useEnvironment();
if (!config) {
return null;
}
const contract = config[
'multisig_control_contract' as keyof EthereumConfig
] as {
address: string;
};
return (
<div className="mb-10">
<Callout intent={Intent.Warning}>
<div>
<Link
title={contract.address}
href={`${ETHERSCAN_URL}/address/${contract.address}`}
target="_blank"
data-testid="multisig-contract-link"
>
{t('multisigContractLink')}
</Link>{' '}
{t('multisigContractIncorrect')}
</div>
<div className="mt-2">
<Link
href={DocsLinks?.VALIDATOR_SCORES_REWARDS}
target="_blank"
data-testid="multisig-validators-learn-more"
>
{t('learnMore')}
</Link>
</div>
</Callout>
</div>
);
};
@@ -1,40 +0,0 @@
import { t } from '@vegaprotocol/i18n';
import {
ExternalLink,
VegaIcon,
VegaIconNames,
} from '@vegaprotocol/ui-toolkit';
import Routes from '../../routes/routes';
export const RiskMessage = () => {
return (
<>
<div className="bg-vega-light-100 dark:bg-vega-dark-100 p-6 mb-6">
<ul className="list-[square] ml-4">
<li>
{t(
'You may encounter bugs, loss of functionality or loss of assets using the App.'
)}
</li>
<li>
{t(
'No party accepts any liability for any losses whatsoever related to its use.'
)}
</li>
</ul>
</div>
<p className="mb-8">
{t(
'By using the Vega Governance App, you acknowledge that you have read and understood the'
)}{' '}
<ExternalLink href={Routes.DISCLAIMER} className="underline">
<span className="flex items-center gap-1">
<span>{t('Vega Governance Disclaimer')}</span>
<VegaIcon name={VegaIconNames.OPEN_EXTERNAL} />
</span>
</ExternalLink>
.
</p>
</>
);
};
@@ -4,11 +4,9 @@ import {
useAppState,
} from '../../contexts/app-state/app-state-context';
import { Connectors } from '../../lib/vega-connectors';
import { RiskMessage } from './risk-message';
export const VegaWalletDialogs = () => {
const { appState, appDispatch } = useAppState();
return (
<>
<VegaConnectDialog
@@ -19,7 +17,6 @@ export const VegaWalletDialogs = () => {
isOpen: open,
})
}
riskMessage={<RiskMessage />}
/>
<VegaManageDialog
@@ -3,32 +3,32 @@ import { usePendingBalancesStore } from './use-pending-balances-manager';
import type { Contract } from 'ethers';
import { useListenForPendingEthEvents } from './use-listen-for-pending-eth-events';
import { prepend0x } from '@vegaprotocol/smart-contracts';
import { useWeb3React } from '@web3-react/core';
export const useListenForStakingEvents = (
contract: Contract | undefined,
vegaPublicKey: string | null,
numberOfConfirmations: number
) => {
const { account } = useWeb3React();
const { addPendingTxs, removePendingTx, resetPendingTxs } =
usePendingBalancesStore((state) => ({
addPendingTxs: state.addPendingTxs,
removePendingTx: state.removePendingTx,
resetPendingTxs: state.resetPendingTxs,
}));
const addFilter = useMemo(() => {
if (!account || !vegaPublicKey || !contract) return null;
return contract.filters.Stake_Deposited(
null,
null,
prepend0x(vegaPublicKey)
);
}, [contract, vegaPublicKey, account]);
const removeFilter = useMemo(() => {
if (!account || !vegaPublicKey || !contract) return null;
return contract.filters.Stake_Removed(null, null, prepend0x(vegaPublicKey));
}, [contract, vegaPublicKey, account]);
const addFilter = useMemo(
() =>
vegaPublicKey && contract
? contract.filters.Stake_Deposited(null, null, prepend0x(vegaPublicKey))
: null,
[contract, vegaPublicKey]
);
const removeFilter = useMemo(
() =>
vegaPublicKey && contract
? contract.filters.Stake_Removed(null, null, prepend0x(vegaPublicKey))
: null,
[contract, vegaPublicKey]
);
/**
* Listen for all add stake events
+3 -12
View File
@@ -116,7 +116,7 @@
"Showing tranches with <{{trancheMinimum}} VEGA, click to hide these tranches": "Showing tranches with ≤{{trancheMinimum}} $VEGA, click to hide these tranches",
"Not showing tranches with <{{trancheMinimum}} VEGA, click to show all tranches": "Not showing tranches with ≤{{trancheMinimum}} $VEGA, click to show all tranches",
"the holder": "the holder",
"Your data couldn't be loaded": "Your data couldn't be loaded",
"We couldn't seem to load your data.": "We couldn't seem to load your data.",
"Vesting VEGA": "Vesting VEGA",
"All the tokens in this tranche are locked and can not be redeemed yet.": "All the tokens in this tranche are locked and can not be redeemed yet.",
"Redeem unlocked VEGA from tranche {{id}}": "Redeem unlocked $VEGA from tranche {{id}}",
@@ -728,7 +728,7 @@
"ThisWillSetEnactmentDeadlineTo": "This will set the enactment date to",
"ThisWillSetValidationDeadlineTo": "This will set the validation deadline to",
"Hours": "hours",
"ThisWillAdd2MinutesToAllowTimeToConfirmInWallet": "Note: 2 minutes of extra time are added when you choose the minimum value. This gives you time to confirm the proposal in your wallet.",
"ThisWillAdd2MinutesToAllowTimeToConfirmInWallet": "Note: we add 2 minutes of extra time when you choose the minimum value. This gives you time to confirm the proposal in your wallet.",
"ProposalWillFailIfEnactmentIsEarlierThanVotingDeadline": "Proposal will fail if enactment is earlier than the voting deadline",
"SelectAMarketToChange": "Select a market to change",
"MarketName": "Market name",
@@ -815,14 +815,5 @@
"NoThanks": "No thanks",
"ShareData": "Share data",
"ContinueSharingData": "Continue sharing data",
"NodeUnsuitable": "Node: {{url}} is unsuitable",
"Disclaimer": "Disclaimer",
"disclaimer1": "The Vega Governance App allows the Vega network to arrive at on-chain decisions, where tokenholders can create proposals that other tokenholders can vote to approve or reject. Vega supports on-chain proposals for creating markets and assets, and changing network parameters, markets and assets. Vega also supports freeform proposals for community suggestions that will not be enacted on-chain.",
"disclaimer2": "The Vega Governance App is free, public and open source software. Software upgrades may contain bugs or security vulnerabilities that might result in loss of functionality.",
"disclaimer3": "The Vega Governance App uses data obtained from nodes on the Vega Blockchain. The developers of the Vega Governance App do not operate or run the Vega Blockchain or any other blockchain.",
"disclaimer4": "The Vega Governance App is provided “as is”. The developers of the Vega Governance App make no representations or warranties of any kind, whether express or implied, statutory or otherwise regarding the Vega Governance App. They disclaim all warranties of merchantability, quality, fitness for purpose. They disclaim all warranties that the Vega Governance App is free of harmful components or errors.",
"disclaimer5": "No developer of the Vega Governance App accepts any responsibility for, or liability to users in connection with their use of the Vega Governance App.",
"multisigContractLink": "Ethereum Multisig Contract",
"multisigContractIncorrect": "is incorrectly configured. Validator and delegator rewards will be penalised until this is resolved.",
"learnMore": "Learn more"
"NodeUnsuitable": "Node: {{url}} is unsuitable"
}
@@ -1,70 +0,0 @@
import {
getMultisigStatusInfo,
MultisigStatus,
} from './get-multisig-status-info';
import type { PreviousEpochQuery } from '../routes/staking/__generated__/PreviousEpoch';
const createNode = (id: string, multisigScore: string) => ({
node: {
id,
stakedTotal: '1000',
rewardScore: { multisigScore },
},
});
describe('getMultisigStatus', () => {
it('should return MultisigStatus.noNodes when no nodes are present', () => {
const result = getMultisigStatusInfo({
epoch: { id: '1', validatorsConnection: { edges: [] } },
} as PreviousEpochQuery);
expect(result).toEqual({
multisigStatus: MultisigStatus.noNodes,
showMultisigStatusError: true,
});
});
it('should return MultisigStatus.correct when all nodes have multisigScore of 1', () => {
const result = getMultisigStatusInfo({
epoch: {
id: '1',
validatorsConnection: {
edges: [createNode('1', '1'), createNode('2', '1')],
},
},
} as PreviousEpochQuery);
expect(result).toEqual({
multisigStatus: MultisigStatus.correct,
showMultisigStatusError: false,
});
});
it('should return MultisigStatus.nodeNeedsRemoving when all nodes have multisigScore of 0', () => {
const result = getMultisigStatusInfo({
epoch: {
id: '1',
validatorsConnection: {
edges: [createNode('1', '0'), createNode('2', '0')],
},
},
} as PreviousEpochQuery);
expect(result).toEqual({
multisigStatus: MultisigStatus.nodeNeedsRemoving,
showMultisigStatusError: true,
});
});
it('should return MultisigStatus.nodeNeedsAdding when some nodes have multisigScore of 0 and others have 1', () => {
const result = getMultisigStatusInfo({
epoch: {
id: '1',
validatorsConnection: {
edges: [createNode('1', '0'), createNode('2', '1')],
},
},
} as PreviousEpochQuery);
expect(result).toEqual({
multisigStatus: MultisigStatus.nodeNeedsAdding,
showMultisigStatusError: true,
});
});
});
@@ -1,42 +0,0 @@
import { removePaginationWrapper } from '@vegaprotocol/utils';
import type { PreviousEpochQuery } from '../routes/staking/__generated__/PreviousEpoch';
export enum MultisigStatus {
'correct' = 'correct',
'nodeNeedsAdding' = 'nodeNeedsAdding',
'nodeNeedsRemoving' = 'nodeNeedsRemoving ',
'noNodes' = 'noNodes',
}
export const getMultisigStatusInfo = (
previousEpochData: PreviousEpochQuery
) => {
let status = MultisigStatus.noNodes;
const allNodesInPreviousEpoch = removePaginationWrapper(
previousEpochData?.epoch.validatorsConnection?.edges
);
const hasZero = allNodesInPreviousEpoch.some(
(node) => Number(node?.rewardScore?.multisigScore) === 0
);
const hasOne = allNodesInPreviousEpoch.some(
(node) => Number(node?.rewardScore?.multisigScore) === 1
);
if (hasZero && hasOne) {
// If any individual node has 0 it means that node is missing from the multisig and needs to be added
status = MultisigStatus.nodeNeedsAdding;
} else if (hasZero) {
// If all nodes have 0 it means there is an incorrect address in the multisig that needs to be removed
status = MultisigStatus.nodeNeedsRemoving;
} else if (allNodesInPreviousEpoch.length > 0) {
// If all nodes have 1 it means the multisig is correct
status = MultisigStatus.correct;
}
return {
showMultisigStatusError: status !== MultisigStatus.correct,
multisigStatus: status,
};
};
@@ -1,21 +0,0 @@
import type { RouteChildProps } from '..';
import { useDocumentTitle } from '../../hooks/use-document-title';
import { Heading } from '../../components/heading';
import { useTranslation } from 'react-i18next';
const Disclaimer = ({ name }: RouteChildProps) => {
useDocumentTitle(name);
const { t } = useTranslation();
return (
<>
<Heading title={t('Disclaimer')} />
<p className="mb-6 mt-10">{t('disclaimer1')}</p>
<p className="mb-6">{t('disclaimer2')}</p>
<p className="mb-6">{t('disclaimer3')}</p>
<p className="mb-8">{t('disclaimer4')}</p>
<p className="mb-8">{t('disclaimer5')}</p>
</>
);
};
export default Disclaimer;
+1 -4
View File
@@ -209,10 +209,7 @@ const GovernanceHome = ({ name }: RouteChildProps) => {
[proposalsData]
);
const sortedProposals = useMemo(
() => orderByDate(proposals).reverse(),
[proposals]
);
const sortedProposals = useMemo(() => orderByDate(proposals), [proposals]);
const protocolUpgradeProposals = useMemo(
() =>
@@ -124,7 +124,7 @@ describe('Proposal header', () => {
})
);
expect(screen.getByTestId('proposal-title')).toHaveTextContent(
'New asset proposal'
'Unknown proposal'
);
expect(screen.getByTestId('proposal-type')).toHaveTextContent('New asset');
expect(screen.getByTestId('proposal-details')).toHaveTextContent(
@@ -21,7 +21,6 @@ export const ProposalHeader = ({
let details: ReactNode;
let proposalType = '';
let fallbackTitle = '';
const title = proposal?.rationale.title.trim();
@@ -30,7 +29,6 @@ export const ProposalHeader = ({
switch (change?.__typename) {
case 'NewMarket': {
proposalType = 'NewMarket';
fallbackTitle = t('NewMarketProposal');
details = (
<>
<span>
@@ -52,7 +50,6 @@ export const ProposalHeader = ({
}
case 'UpdateMarket': {
proposalType = 'UpdateMarket';
fallbackTitle = t('UpdateMarketProposal');
details = (
<>
<span>{t('Market change')}:</span>{' '}
@@ -63,7 +60,6 @@ export const ProposalHeader = ({
}
case 'NewAsset': {
proposalType = 'NewAsset';
fallbackTitle = t('NewAssetProposal');
details = (
<>
<span>{t('Symbol')}:</span> <Lozenge>{change.symbol}.</Lozenge>{' '}
@@ -85,7 +81,6 @@ export const ProposalHeader = ({
}
case 'UpdateNetworkParameter': {
proposalType = 'NetworkParameter';
fallbackTitle = t('NetworkParameterProposal');
details = (
<>
<span>{t('Change')}:</span>{' '}
@@ -100,13 +95,11 @@ export const ProposalHeader = ({
}
case 'NewFreeform': {
proposalType = 'Freeform';
fallbackTitle = t('FreeformProposal');
details = <span />;
break;
}
case 'UpdateAsset': {
proposalType = 'UpdateAsset';
fallbackTitle = t('UpdateAssetProposal');
details = (
<>
<span>{t('AssetID')}:</span>{' '}
@@ -122,14 +115,10 @@ export const ProposalHeader = ({
<div data-testid="proposal-title">
{isListItem ? (
<header>
<SubHeading
title={titleContent || fallbackTitle || t('Unknown proposal')}
/>
<SubHeading title={titleContent || t('Unknown proposal')} />
</header>
) : (
<Heading
title={titleContent || fallbackTitle || t('Unknown proposal')}
/>
<Heading title={titleContent || t('Unknown proposal')} />
)}
</div>
@@ -115,6 +115,29 @@ describe('Proposals list', () => {
);
});
it('Orders proposals correctly by closingDateTime', () => {
render(
renderComponent([
failedProposalClosedLastMonth,
openProposalClosesNextMonth,
openProposalClosesNextWeek,
enactedProposalClosedLastWeek,
])
);
const openProposals = within(screen.getByTestId('open-proposals'));
const closedProposals = within(screen.getByTestId('closed-proposals'));
const openProposalsItems = openProposals.getAllByTestId(
'proposals-list-item'
);
const closedProposalsItems = closedProposals.getAllByTestId(
'proposals-list-item'
);
expect(openProposalsItems[0]).toHaveAttribute('id', 'proposal2');
expect(openProposalsItems[1]).toHaveAttribute('id', 'proposal1');
expect(closedProposalsItems[0]).toHaveAttribute('id', 'proposal3');
expect(closedProposalsItems[1]).toHaveAttribute('id', 'proposal4');
});
it('Displays info on no proposals', () => {
render(renderComponent([]));
expect(screen.queryByTestId('open-proposals')).not.toBeInTheDocument();
@@ -35,11 +35,7 @@ export const orderByDate = (arr: ProposalFieldsFragment[]) =>
orderBy(
arr,
[
(p) =>
p?.terms?.enactmentDatetime
? new Date(p?.terms?.enactmentDatetime).getTime()
: // has to be defaulted to 0 because new Date(null).getTime() -> NaN which is first when ordered
new Date(p?.terms?.closingDatetime || 0).getTime(),
(p) => new Date(p?.terms?.closingDatetime).getTime(),
(p) => new Date(p?.datetime).getTime(),
],
['asc', 'asc']
@@ -146,7 +146,7 @@ describe('Proposal form vote, validation and enactment deadline', () => {
it('should show the correct datetimes', () => {
renderComponent();
// Should be adding 2 mins to the vote deadline as the minimum is set by
// default, and 2 mins are added for wallet confirmation
// default, and we add 2 mins for wallet confirmation
expect(screen.getByTestId('voting-date')).toHaveTextContent(
'2022-01-01T01:02:00.000Z'
);
@@ -49,7 +49,6 @@ export const ProposeFreeform = () => {
formState: { errors },
setValue,
watch,
trigger,
} = useForm<FreeformProposalFormFields>();
const { finalizedProposal, submit, Dialog } = useProposalSubmit();
@@ -86,13 +85,7 @@ export const ProposeFreeform = () => {
await submit(assembleProposal(fields));
};
const viewJson = async () => {
const isValid = await trigger();
if (!isValid) {
return;
}
const viewJson = () => {
const formData = watch();
downloadJson(
JSON.stringify(assembleProposal(formData)),
@@ -89,7 +89,6 @@ export const ProposeNetworkParameter = () => {
formState: { errors },
setValue,
watch,
trigger,
} = useForm<NetworkParameterProposalFormFields>();
const { finalizedProposal, submit, Dialog } = useProposalSubmit();
@@ -149,13 +148,7 @@ export const ProposeNetworkParameter = () => {
await submit(assembleProposal(fields));
};
const viewJson = async () => {
const isValid = await trigger();
if (!isValid) {
return;
}
const viewJson = () => {
const formData = watch();
downloadJson(
JSON.stringify(assembleProposal(formData)),
@@ -61,7 +61,6 @@ export const ProposeNewAsset = () => {
formState: { errors },
setValue,
watch,
trigger,
} = useForm<NewAssetProposalFormFields>();
const { finalizedProposal, submit, Dialog } = useProposalSubmit();
@@ -118,13 +117,7 @@ export const ProposeNewAsset = () => {
await submit(assembleProposal(fields));
};
const viewJson = async () => {
const isValid = await trigger();
if (!isValid) {
return;
}
const viewJson = () => {
const formData = watch();
downloadJson(
JSON.stringify(assembleProposal(formData)),
@@ -59,7 +59,6 @@ export const ProposeNewMarket = () => {
formState: { errors },
setValue,
watch,
trigger,
} = useForm<NewMarketProposalFormFields>();
const { finalizedProposal, submit, Dialog } = useProposalSubmit();
@@ -108,13 +107,7 @@ export const ProposeNewMarket = () => {
await submit(assembleProposal(fields));
};
const viewJson = async () => {
const isValid = await trigger();
if (!isValid) {
return;
}
const viewJson = () => {
const formData = watch();
downloadJson(
JSON.stringify(assembleProposal(formData)),
@@ -59,7 +59,6 @@ export const ProposeUpdateAsset = () => {
formState: { errors },
setValue,
watch,
trigger,
} = useForm<UpdateAssetProposalFormFields>();
const { finalizedProposal, submit, Dialog } = useProposalSubmit();
@@ -108,13 +107,7 @@ export const ProposeUpdateAsset = () => {
await submit(assembleProposal(fields));
};
const viewJson = async () => {
const isValid = await trigger();
if (!isValid) {
return;
}
const viewJson = () => {
const formData = watch();
downloadJson(
JSON.stringify(assembleProposal(formData)),
@@ -106,7 +106,6 @@ export const ProposeUpdateMarket = () => {
formState: { errors },
setValue,
watch,
trigger,
} = useForm<UpdateMarketProposalFormFields>();
const { finalizedProposal, submit, Dialog } = useProposalSubmit();
@@ -158,13 +157,7 @@ export const ProposeUpdateMarket = () => {
await submit(assembleProposal(fields));
};
const viewJson = async () => {
const isValid = await trigger();
if (!isValid) {
return;
}
const viewJson = () => {
const formData = watch();
downloadJson(
JSON.stringify(assembleProposal(formData)),
@@ -5,9 +5,6 @@ import { ProtocolUpgradeProposalDetailInfo } from '../components/protocol-upgrad
import { getNormalisedVotingPower } from '../../staking/shared';
import type { NodesFragmentFragment } from '../../staking/home/__generated__/Nodes';
import type { ProtocolUpgradeProposalFieldsFragment } from '@vegaprotocol/proposals';
import { useVegaRelease } from '@vegaprotocol/environment';
import { ExternalLink } from '@vegaprotocol/ui-toolkit';
import { useTranslation } from 'react-i18next';
export interface ProtocolUpgradeProposalProps {
proposal: ProtocolUpgradeProposalFieldsFragment;
@@ -45,9 +42,6 @@ export const ProtocolUpgradeProposal = ({
lastBlockHeight,
consensusValidators,
}: ProtocolUpgradeProposalProps) => {
const { t } = useTranslation();
const releaseInfo = useVegaRelease(proposal.vegaReleaseTag);
const consensusApprovals = useMemo(
() => getConsensusApprovals(consensusValidators || [], proposal),
[consensusValidators, proposal]
@@ -84,14 +78,6 @@ export const ProtocolUpgradeProposal = ({
totalConsensusValidators={consensusValidators.length}
/>
)}
{releaseInfo && releaseInfo.htmlUrl && (
<div className="mb-10">
<ExternalLink href={releaseInfo.htmlUrl}>
{t('Explore release on GitHub')}
</ExternalLink>
</div>
)}
</section>
);
};
@@ -8,7 +8,6 @@ const mockData = {
{
asset: 'tDAI',
totalAmount: '5',
decimals: 6,
rewardTypes: {
ACCOUNT_TYPE_GLOBAL_REWARD: {
amount: '0',
@@ -1,5 +1,6 @@
import { formatNumber, toBigNum } from '@vegaprotocol/utils';
import { Tooltip } from '@vegaprotocol/ui-toolkit';
import { useAppState } from '../../../contexts/app-state/app-state-context';
import {
rowGridItemStyles,
RewardsTable,
@@ -12,8 +13,7 @@ interface EpochIndividualRewardsGridProps {
}
interface RewardItemProps {
amount: string;
decimals: number;
value: string;
percentageOfTotal?: string;
dataTestId: string;
last?: boolean;
@@ -21,14 +21,15 @@ interface RewardItemProps {
const DisplayReward = ({
reward,
decimals,
percentageOfTotal,
}: {
reward: string;
decimals: number;
percentageOfTotal?: string;
}) => {
const { t } = useTranslation();
const {
appState: { decimals },
} = useAppState();
if (Number(reward) === 0) {
return <span className="text-vega-dark-300">-</span>;
@@ -63,8 +64,7 @@ const DisplayReward = ({
};
const RewardItem = ({
amount,
decimals,
value,
percentageOfTotal,
dataTestId,
last,
@@ -72,11 +72,7 @@ const RewardItem = ({
<div data-testid={dataTestId} className={rowGridItemStyles(last)}>
<div className="h-full w-5 absolute right-0 top-0 bg-gradient-to-r from-transparent to-black pointer-events-none" />
<div className="overflow-auto p-5">
<DisplayReward
reward={amount}
decimals={decimals}
percentageOfTotal={percentageOfTotal}
/>
<DisplayReward reward={value} percentageOfTotal={percentageOfTotal} />
</div>
<div className="h-full w-5 absolute left-0 top-0 bg-gradient-to-l from-transparent to-black pointer-events-none" />
</div>
@@ -90,7 +86,7 @@ export const EpochIndividualRewardsTable = ({
dataTestId="epoch-individual-rewards-table"
epoch={Number(data.epoch)}
>
{data.rewards.map(({ asset, rewardTypes, totalAmount, decimals }, i) => (
{data.rewards.map(({ asset, rewardTypes, totalAmount }, i) => (
<div className="contents" key={i}>
<div
data-testid="individual-rewards-asset"
@@ -102,19 +98,13 @@ export const EpochIndividualRewardsTable = ({
([key, { amount, percentageOfTotal }]) => (
<RewardItem
key={key}
amount={amount}
decimals={decimals}
value={amount}
percentageOfTotal={percentageOfTotal}
dataTestId={key}
/>
)
)}
<RewardItem
dataTestId="total"
amount={totalAmount}
decimals={decimals}
last={true}
/>
<RewardItem dataTestId="total" value={totalAmount} last={true} />
</div>
))}
</RewardsTable>
@@ -8,7 +8,7 @@ describe('generateEpochIndividualRewardsList', () => {
amount: '100',
percentageOfTotal: '0.1',
receivedAt: new Date(),
asset: { id: 'usd', symbol: 'USD', name: 'USD', decimals: 6 },
asset: { id: 'usd', symbol: 'USD', name: 'USD' },
party: { id: 'blah' },
epoch: { id: '1' },
};
@@ -18,7 +18,7 @@ describe('generateEpochIndividualRewardsList', () => {
amount: '50',
percentageOfTotal: '0.05',
receivedAt: new Date(),
asset: { id: 'eur', symbol: 'EUR', name: 'EUR', decimals: 5 },
asset: { id: 'eur', symbol: 'EUR', name: 'EUR' },
party: { id: 'blah' },
epoch: { id: '2' },
};
@@ -28,7 +28,7 @@ describe('generateEpochIndividualRewardsList', () => {
amount: '200',
percentageOfTotal: '0.2',
receivedAt: new Date(),
asset: { id: 'gbp', symbol: 'GBP', name: 'GBP', decimals: 7 },
asset: { id: 'gbp', symbol: 'GBP', name: 'GBP' },
party: { id: 'blah' },
epoch: { id: '2' },
};
@@ -38,7 +38,7 @@ describe('generateEpochIndividualRewardsList', () => {
amount: '100',
percentageOfTotal: '0.1',
receivedAt: new Date(),
asset: { id: 'usd', symbol: 'USD', name: 'USD', decimals: 6 },
asset: { id: 'usd', symbol: 'USD', name: 'USD' },
party: { id: 'blah' },
epoch: { id: '1' },
};
@@ -48,7 +48,7 @@ describe('generateEpochIndividualRewardsList', () => {
amount: '150',
percentageOfTotal: '0.15',
receivedAt: new Date(),
asset: { id: 'usd', symbol: 'USD', name: 'USD', decimals: 6 },
asset: { id: 'usd', symbol: 'USD', name: 'USD' },
party: { id: 'blah' },
epoch: { id: '3' },
};
@@ -58,7 +58,7 @@ describe('generateEpochIndividualRewardsList', () => {
amount: '50',
percentageOfTotal: '0.05',
receivedAt: new Date(),
asset: { id: 'eur', symbol: 'EUR', name: 'EUR', decimals: 5 },
asset: { id: 'eur', symbol: 'EUR', name: 'EUR' },
party: { id: 'blah' },
epoch: { id: '2' },
};
@@ -99,7 +99,6 @@ describe('generateEpochIndividualRewardsList', () => {
rewards: [
{
asset: 'USD',
decimals: 6,
totalAmount: '100',
rewardTypes: {
[AccountType.ACCOUNT_TYPE_FEES_INFRASTRUCTURE]: {
@@ -168,7 +167,6 @@ describe('generateEpochIndividualRewardsList', () => {
{
asset: 'GBP',
totalAmount: '200',
decimals: 7,
rewardTypes: {
[AccountType.ACCOUNT_TYPE_FEES_INFRASTRUCTURE]: {
amount: '0',
@@ -199,7 +197,6 @@ describe('generateEpochIndividualRewardsList', () => {
{
asset: 'EUR',
totalAmount: '50',
decimals: 5,
rewardTypes: {
[AccountType.ACCOUNT_TYPE_FEES_INFRASTRUCTURE]: {
amount: '0',
@@ -235,7 +232,6 @@ describe('generateEpochIndividualRewardsList', () => {
{
asset: 'USD',
totalAmount: '200',
decimals: 6,
rewardTypes: {
[AccountType.ACCOUNT_TYPE_FEES_INFRASTRUCTURE]: {
amount: '0',
@@ -283,7 +279,6 @@ describe('generateEpochIndividualRewardsList', () => {
rewards: [
{
asset: 'USD',
decimals: 6,
totalAmount: '150',
rewardTypes: {
[AccountType.ACCOUNT_TYPE_FEES_INFRASTRUCTURE]: {
@@ -320,7 +315,6 @@ describe('generateEpochIndividualRewardsList', () => {
{
asset: 'GBP',
totalAmount: '200',
decimals: 7,
rewardTypes: {
[AccountType.ACCOUNT_TYPE_FEES_INFRASTRUCTURE]: {
amount: '0',
@@ -351,7 +345,6 @@ describe('generateEpochIndividualRewardsList', () => {
{
asset: 'EUR',
totalAmount: '50',
decimals: 5,
rewardTypes: {
[AccountType.ACCOUNT_TYPE_FEES_INFRASTRUCTURE]: {
amount: '0',
@@ -397,7 +390,6 @@ describe('generateEpochIndividualRewardsList', () => {
{
asset: 'USD',
totalAmount: '200',
decimals: 6,
rewardTypes: {
[AccountType.ACCOUNT_TYPE_FEES_INFRASTRUCTURE]: {
amount: '0',
@@ -9,7 +9,6 @@ export interface EpochIndividualReward {
rewards: {
asset: string;
totalAmount: string;
decimals: number;
rewardTypes: {
[key in AccountType]?: {
amount: string;
@@ -54,7 +53,6 @@ export const generateEpochIndividualRewardsList = ({
const epochIndividualRewards = rewards.reduce((acc, reward) => {
const epochId = reward.epoch.id;
const assetName = reward.asset.name;
const assetDecimals = reward.asset.decimals;
const rewardType = reward.rewardType;
const amount = reward.amount;
const percentageOfTotal = reward.percentageOfTotal;
@@ -75,7 +73,6 @@ export const generateEpochIndividualRewardsList = ({
if (!asset) {
asset = {
asset: assetName,
decimals: assetDecimals,
totalAmount: '0',
rewardTypes: Object.fromEntries(emptyRowAccountTypes),
};
@@ -52,7 +52,6 @@ const assetRewards: Map<
assetRewards.set(assetId, {
assetId,
name: 'tDAI TEST',
decimals: 6,
rewards,
totalAmount: '295',
});
@@ -1,5 +1,6 @@
import { formatNumber, toBigNum } from '@vegaprotocol/utils';
import { Tooltip } from '@vegaprotocol/ui-toolkit';
import { useAppState } from '../../../contexts/app-state/app-state-context';
import {
rowGridItemStyles,
RewardsTable,
@@ -11,19 +12,16 @@ interface EpochTotalRewardsGridProps {
}
interface RewardItemProps {
amount: string;
decimals: number;
value: string;
dataTestId: string;
last?: boolean;
}
const DisplayReward = ({
reward,
decimals,
}: {
reward: string;
decimals: number;
}) => {
const DisplayReward = ({ reward }: { reward: string }) => {
const {
appState: { decimals },
} = useAppState();
if (Number(reward) === 0) {
return <span className="text-vega-dark-300">-</span>;
}
@@ -35,16 +33,11 @@ const DisplayReward = ({
);
};
const RewardItem = ({
amount,
decimals,
dataTestId,
last,
}: RewardItemProps) => (
const RewardItem = ({ value, dataTestId, last }: RewardItemProps) => (
<div data-testid={dataTestId} className={rowGridItemStyles(last)}>
<div className="h-full w-5 absolute right-0 top-0 bg-gradient-to-r from-transparent to-black pointer-events-none" />
<div className="overflow-auto p-5">
<DisplayReward reward={amount} decimals={decimals} />
<DisplayReward reward={value} />
</div>
<div className="h-full w-5 absolute left-0 top-0 bg-gradient-to-l from-transparent to-black pointer-events-none" />
</div>
@@ -56,25 +49,15 @@ export const EpochTotalRewardsTable = ({
return (
<RewardsTable dataTestId="epoch-total-rewards-table" epoch={data.epoch}>
{Array.from(data.assetRewards.values()).map(
({ name, rewards, totalAmount, decimals }, i) => (
({ name, rewards, totalAmount }, i) => (
<div className="contents" key={i}>
<div data-testid="asset" className={`${rowGridItemStyles()} p-5`}>
{name}
</div>
{Array.from(rewards.values()).map(({ rewardType, amount }, i) => (
<RewardItem
key={i}
dataTestId={rewardType}
amount={amount}
decimals={decimals}
/>
<RewardItem key={i} dataTestId={rewardType} value={amount} />
))}
<RewardItem
dataTestId="total"
amount={totalAmount}
decimals={decimals}
last={true}
/>
<RewardItem dataTestId="total" value={totalAmount} last={true} />
</div>
)
)}
@@ -56,14 +56,12 @@ describe('generateEpochAssetRewardsList', () => {
node: {
id: '1',
name: 'Asset 1',
decimals: 18,
},
},
{
node: {
id: '2',
name: 'Asset 2',
decimals: 6,
},
},
],
@@ -103,7 +101,6 @@ describe('generateEpochAssetRewardsList', () => {
{
node: {
epoch: 1,
decimals: 18,
assetId: '1',
rewardType: AccountType.ACCOUNT_TYPE_GLOBAL_REWARD,
amount: '123',
@@ -131,7 +128,6 @@ describe('generateEpochAssetRewardsList', () => {
'1',
{
assetId: '1',
decimals: 0,
name: '',
rewards: new Map([
[
@@ -200,14 +196,12 @@ describe('generateEpochAssetRewardsList', () => {
node: {
id: '1',
name: 'Asset 1',
decimals: 18,
},
},
{
node: {
id: '2',
name: 'Asset 2',
decimals: 6,
},
},
],
@@ -218,7 +212,6 @@ describe('generateEpochAssetRewardsList', () => {
node: {
epoch: 1,
assetId: '1',
decimals: 18,
rewardType: AccountType.ACCOUNT_TYPE_REWARD_MAKER_PAID_FEES,
amount: '123',
},
@@ -227,7 +220,6 @@ describe('generateEpochAssetRewardsList', () => {
node: {
epoch: 1,
assetId: '1',
decimals: 18,
rewardType: AccountType.ACCOUNT_TYPE_FEES_INFRASTRUCTURE,
amount: '100',
},
@@ -236,7 +228,6 @@ describe('generateEpochAssetRewardsList', () => {
node: {
epoch: 2,
assetId: '1',
decimals: 18,
rewardType: AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES,
amount: '5',
},
@@ -263,7 +254,6 @@ describe('generateEpochAssetRewardsList', () => {
'1',
{
assetId: '1',
decimals: 18,
name: 'Asset 1',
rewards: new Map([
[
@@ -329,7 +319,6 @@ describe('generateEpochAssetRewardsList', () => {
'1',
{
assetId: '1',
decimals: 18,
name: 'Asset 1',
rewards: new Map([
[
@@ -398,14 +387,12 @@ describe('generateEpochAssetRewardsList', () => {
node: {
id: '1',
name: 'Asset 1',
decimals: 18,
},
},
{
node: {
id: '2',
name: 'Asset 2',
decimals: 6,
},
},
],
@@ -416,7 +403,6 @@ describe('generateEpochAssetRewardsList', () => {
node: {
epoch: 1,
assetId: '1',
decimals: 18,
rewardType: AccountType.ACCOUNT_TYPE_REWARD_MAKER_PAID_FEES,
amount: '123',
},
@@ -425,7 +411,6 @@ describe('generateEpochAssetRewardsList', () => {
node: {
epoch: 1,
assetId: '1',
decimals: 18,
rewardType: AccountType.ACCOUNT_TYPE_FEES_INFRASTRUCTURE,
amount: '100',
},
@@ -434,7 +419,6 @@ describe('generateEpochAssetRewardsList', () => {
node: {
epoch: 2,
assetId: '1',
decimals: 18,
rewardType: AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES,
amount: '6',
},
@@ -443,7 +427,6 @@ describe('generateEpochAssetRewardsList', () => {
node: {
epoch: 2,
assetId: '1',
decimals: 18,
rewardType: AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES,
amount: '27',
},
@@ -452,7 +435,6 @@ describe('generateEpochAssetRewardsList', () => {
node: {
epoch: 3,
assetId: '1',
decimals: 18,
rewardType: AccountType.ACCOUNT_TYPE_FEES_INFRASTRUCTURE,
amount: '15',
},
@@ -485,7 +467,6 @@ describe('generateEpochAssetRewardsList', () => {
{
assetId: '1',
name: 'Asset 1',
decimals: 18,
rewards: new Map([
[
AccountType.ACCOUNT_TYPE_GLOBAL_REWARD,
@@ -550,7 +531,6 @@ describe('generateEpochAssetRewardsList', () => {
'1',
{
assetId: '1',
decimals: 18,
name: 'Asset 1',
rewards: new Map([
[
@@ -628,7 +608,6 @@ describe('generateEpochAssetRewardsList', () => {
'1',
{
assetId: '1',
decimals: 18,
name: 'Asset 1',
rewards: new Map([
[
@@ -22,7 +22,6 @@ export type AggregatedEpochRewardSummary = {
name: EpochSummaryWithNamedReward['name'];
rewards: Map<RewardType, RewardItem>;
totalAmount: string;
decimals: number;
};
export type EpochTotalSummary = {
@@ -92,7 +91,6 @@ export const generateEpochTotalRewardsList = ({
assetId: reward.assetId,
name: matchingAsset?.name || '',
rewards: rewards || new Map(emptyRowAccountTypes),
decimals: matchingAsset?.decimals || 0,
totalAmount: (
Number(reward.amount) + Number(assetWithRewards?.totalAmount || 0)
).toString(),
@@ -4,7 +4,6 @@ fragment RewardFields on Reward {
id
symbol
name
decimals
}
party {
id
@@ -68,7 +67,6 @@ query EpochAssetsRewards(
node {
id
name
decimals
}
}
}
@@ -3,7 +3,7 @@ import * as Types from '@vegaprotocol/types';
import { gql } from '@apollo/client';
import * as Apollo from '@apollo/client';
const defaultOptions = {} as const;
export type RewardFieldsFragment = { __typename?: 'Reward', rewardType: Types.AccountType, amount: string, percentageOfTotal: string, receivedAt: any, asset: { __typename?: 'Asset', id: string, symbol: string, name: string, decimals: number }, party: { __typename?: 'Party', id: string }, epoch: { __typename?: 'Epoch', id: string } };
export type RewardFieldsFragment = { __typename?: 'Reward', rewardType: Types.AccountType, amount: string, percentageOfTotal: string, receivedAt: any, asset: { __typename?: 'Asset', id: string, symbol: string, name: string }, party: { __typename?: 'Party', id: string }, epoch: { __typename?: 'Epoch', id: string } };
export type DelegationFieldsFragment = { __typename?: 'Delegation', amount: string, epoch: number };
@@ -16,7 +16,7 @@ export type RewardsQueryVariables = Types.Exact<{
}>;
export type RewardsQuery = { __typename?: 'Query', party?: { __typename?: 'Party', id: string, rewardsConnection?: { __typename?: 'RewardsConnection', edges?: Array<{ __typename?: 'RewardEdge', node: { __typename?: 'Reward', rewardType: Types.AccountType, amount: string, percentageOfTotal: string, receivedAt: any, asset: { __typename?: 'Asset', id: string, symbol: string, name: string, decimals: number }, party: { __typename?: 'Party', id: string }, epoch: { __typename?: 'Epoch', id: string } } } | null> | null } | null, delegationsConnection?: { __typename?: 'DelegationsConnection', edges?: Array<{ __typename?: 'DelegationEdge', node: { __typename?: 'Delegation', amount: string, epoch: number } } | null> | null } | null } | null };
export type RewardsQuery = { __typename?: 'Query', party?: { __typename?: 'Party', id: string, rewardsConnection?: { __typename?: 'RewardsConnection', edges?: Array<{ __typename?: 'RewardEdge', node: { __typename?: 'Reward', rewardType: Types.AccountType, amount: string, percentageOfTotal: string, receivedAt: any, asset: { __typename?: 'Asset', id: string, symbol: string, name: string }, party: { __typename?: 'Party', id: string }, epoch: { __typename?: 'Epoch', id: string } } } | null> | null } | null, delegationsConnection?: { __typename?: 'DelegationsConnection', edges?: Array<{ __typename?: 'DelegationEdge', node: { __typename?: 'Delegation', amount: string, epoch: number } } | null> | null } | null } | null };
export type EpochRewardSummaryFieldsFragment = { __typename?: 'EpochRewardSummary', epoch: number, assetId: string, amount: string, rewardType: Types.AccountType };
@@ -26,7 +26,7 @@ export type EpochAssetsRewardsQueryVariables = Types.Exact<{
}>;
export type EpochAssetsRewardsQuery = { __typename?: 'Query', assetsConnection?: { __typename?: 'AssetsConnection', edges?: Array<{ __typename?: 'AssetEdge', node: { __typename?: 'Asset', id: string, name: string, decimals: number } } | null> | null } | null, epochRewardSummaries?: { __typename?: 'EpochRewardSummaryConnection', edges?: Array<{ __typename?: 'EpochRewardSummaryEdge', node: { __typename?: 'EpochRewardSummary', epoch: number, assetId: string, amount: string, rewardType: Types.AccountType } } | null> | null } | null };
export type EpochAssetsRewardsQuery = { __typename?: 'Query', assetsConnection?: { __typename?: 'AssetsConnection', edges?: Array<{ __typename?: 'AssetEdge', node: { __typename?: 'Asset', id: string, name: string } } | null> | null } | null, epochRewardSummaries?: { __typename?: 'EpochRewardSummaryConnection', edges?: Array<{ __typename?: 'EpochRewardSummaryEdge', node: { __typename?: 'EpochRewardSummary', epoch: number, assetId: string, amount: string, rewardType: Types.AccountType } } | null> | null } | null };
export type EpochFieldsFragment = { __typename?: 'Epoch', id: string, timestamps: { __typename?: 'EpochTimestamps', start?: any | null, end?: any | null, expiry?: any | null } };
@@ -42,7 +42,6 @@ export const RewardFieldsFragmentDoc = gql`
id
symbol
name
decimals
}
party {
id
@@ -144,7 +143,6 @@ export const EpochAssetsRewardsDocument = gql`
node {
id
name
decimals
}
}
}
@@ -23,9 +23,6 @@ import { useRefreshAfterEpoch } from '../../../hooks/use-refresh-after-epoch';
import { DocsLinks } from '@vegaprotocol/environment';
import { ConnectToSeeRewards } from '../connect-to-see-rewards';
import { EpochTotalRewards } from '../epoch-total-rewards/epoch-total-rewards';
import { usePreviousEpochQuery } from '../../staking/__generated__/PreviousEpoch';
import { getMultisigStatusInfo } from '../../../lib/get-multisig-status-info';
import { MultisigIncorrectNotice } from '../../../components/multisig-incorrect-notice';
type RewardsView = 'total' | 'individual';
@@ -44,25 +41,12 @@ export const RewardsPage = () => {
useRefreshAfterEpoch(epochData?.epoch.timestamps.expiry, refetch);
const { data: previousEpochData } = usePreviousEpochQuery({
variables: {
epochId: (Number(epochData?.epoch.id) - 1).toString(),
},
skip: !epochData?.epoch.id,
});
const multisigStatus = previousEpochData
? getMultisigStatusInfo(previousEpochData)
: undefined;
const {
params,
loading: paramsLoading,
error: paramsError,
} = useNetworkParams([NetworkParams.reward_staking_delegation_payoutDelay]);
console.log('params', params);
const payoutDuration = useMemo(() => {
if (!params) {
return 0;
@@ -94,18 +78,14 @@ export const RewardsPage = () => {
)}
</p>
{multisigStatus?.showMultisigStatusError ? (
<MultisigIncorrectNotice />
) : null}
{!multisigStatus?.showMultisigStatusError && payoutDuration ? (
{payoutDuration ? (
<div className="my-8">
<Callout
title={t('rewardsCallout', {
duration: formatDistance(new Date(0), payoutDuration),
})}
headingLevel={3}
intent={Intent.Primary}
intent={Intent.Warning}
>
<p className="mb-0">{t('rewardsCalloutDetail')}</p>
</Callout>
@@ -207,13 +207,6 @@ const LazyWithdrawals = React.lazy(
)
);
const LazyDisclaimer = React.lazy(
() =>
import(
/* webpackChunkName: "route-disclaimer", webpackPrefetch: true */ './disclaimer'
)
);
const redirects = [
{
path: Routes.VALIDATORS,
@@ -356,10 +349,6 @@ const routerConfig = [
path: Routes.CONTRACTS,
element: <LazyContracts name="Contracts" />,
},
{
path: Routes.DISCLAIMER,
element: <LazyDisclaimer name="Disclaimer" />,
},
{
path: '*',
// Not lazy as loaded when a user first hits the site
-1
View File
@@ -15,7 +15,6 @@ const Routes = {
SUPPLY: '/token/tranches',
ASSOCIATE: '/token/associate',
DISASSOCIATE: '/token/disassociate',
DISCLAIMER: '/disclaimer',
};
export default Routes;
@@ -5,22 +5,12 @@ query PreviousEpoch($epochId: ID) {
edges {
node {
id
stakedTotal
rewardScore {
rawValidatorScore
performanceScore
multisigScore
validatorScore
normalisedScore
validatorStatus
}
rankingScore {
status
previousStatus
rankingScore
stakeScore
performanceScore
votingPower
}
}
}
@@ -8,7 +8,7 @@ export type PreviousEpochQueryVariables = Types.Exact<{
}>;
export type PreviousEpochQuery = { __typename?: 'Query', epoch: { __typename?: 'Epoch', id: string, validatorsConnection?: { __typename?: 'NodesConnection', edges?: Array<{ __typename?: 'NodeEdge', node: { __typename?: 'Node', id: string, stakedTotal: string, rewardScore?: { __typename?: 'RewardScore', rawValidatorScore: string, performanceScore: string, multisigScore: string, validatorScore: string, normalisedScore: string, validatorStatus: Types.ValidatorStatus } | null, rankingScore: { __typename?: 'RankingScore', status: Types.ValidatorStatus, previousStatus: Types.ValidatorStatus, rankingScore: string, stakeScore: string, performanceScore: string, votingPower: string } } } | null> | null } | null } };
export type PreviousEpochQuery = { __typename?: 'Query', epoch: { __typename?: 'Epoch', id: string, validatorsConnection?: { __typename?: 'NodesConnection', edges?: Array<{ __typename?: 'NodeEdge', node: { __typename?: 'Node', id: string, rewardScore?: { __typename?: 'RewardScore', rawValidatorScore: string, performanceScore: string } | null, rankingScore: { __typename?: 'RankingScore', stakeScore: string } } } | null> | null } | null } };
export const PreviousEpochDocument = gql`
@@ -19,22 +19,12 @@ export const PreviousEpochDocument = gql`
edges {
node {
id
stakedTotal
rewardScore {
rawValidatorScore
performanceScore
multisigScore
validatorScore
normalisedScore
validatorStatus
}
rankingScore {
status
previousStatus
rankingScore
stakeScore
performanceScore
votingPower
}
}
}
@@ -7,8 +7,6 @@ import { ValidatorTables } from './validator-tables';
import { useRefreshAfterEpoch } from '../../../hooks/use-refresh-after-epoch';
import { useVegaWallet } from '@vegaprotocol/wallet';
import { ENV } from '../../../config';
import { getMultisigStatusInfo } from '../../../lib/get-multisig-status-info';
import { MultisigIncorrectNotice } from '../../../components/multisig-incorrect-notice';
export const EpochData = () => {
// errorPolicy due to vegaprotocol/vega issue 5898
@@ -48,20 +46,12 @@ export const EpochData = () => {
userStakingRefetch();
});
const multisigStatus = previousEpochData
? getMultisigStatusInfo(previousEpochData)
: undefined;
return (
<AsyncRenderer
loading={nodesLoading || userStakingLoading}
error={nodesError || userStakingError}
data={nodesData}
>
{multisigStatus?.showMultisigStatusError ? (
<MultisigIncorrectNotice />
) : null}
{nodesData?.epoch &&
nodesData.epoch.timestamps.start &&
nodesData?.epoch.timestamps.expiry && (
@@ -79,72 +79,36 @@ const MOCK_PREVIOUS_EPOCH: PreviousEpochQuery = {
{
node: {
id: 'ccc022b7e63a4d0a6d3a193c3940c88574060e58a184964c994998d86835a1b4',
stakedTotal: '14182454495731682635157',
rewardScore: {
rawValidatorScore: '0.25',
performanceScore: '0.9998677767864936',
multisigScore: '',
validatorScore: '',
normalisedScore: '',
validatorStatus:
Schema.ValidatorStatus.VALIDATOR_NODE_STATUS_TENDERMINT,
},
rankingScore: {
stakeScore: '0.2499583402766206',
performanceScore: '0.9998677767864936',
status: Schema.ValidatorStatus.VALIDATOR_NODE_STATUS_TENDERMINT,
previousStatus:
Schema.ValidatorStatus.VALIDATOR_NODE_STATUS_TENDERMINT,
rankingScore: '',
votingPower: '',
},
},
},
{
node: {
id: '966438c6bffac737cfb08173ffcb3f393c4692b099ad80cb45a82e2dc0a8cf99',
stakedTotal: '9618711883996159534058',
rewardScore: {
rawValidatorScore: '0.3',
performanceScore: '1',
multisigScore: '',
validatorScore: '0.31067',
normalisedScore: '',
validatorStatus:
Schema.ValidatorStatus.VALIDATOR_NODE_STATUS_TENDERMINT,
},
rankingScore: {
stakeScore: '0.25',
performanceScore: '0.9998677767864936',
status: Schema.ValidatorStatus.VALIDATOR_NODE_STATUS_TENDERMINT,
previousStatus:
Schema.ValidatorStatus.VALIDATOR_NODE_STATUS_TENDERMINT,
rankingScore: '',
votingPower: '',
},
},
},
{
node: {
id: '12c81b738e8051152e1afe44376ec37bca9216466e6d44cdd772194bad0ada81',
stakedTotal: '4041343338923442976709',
rewardScore: {
rawValidatorScore: '0.35',
performanceScore: '0.999629748500531',
multisigScore: '',
validatorScore: '',
normalisedScore: '',
validatorStatus:
Schema.ValidatorStatus.VALIDATOR_NODE_STATUS_TENDERMINT,
},
rankingScore: {
stakeScore: '0.2312',
performanceScore: '0.9998677767864936',
status: Schema.ValidatorStatus.VALIDATOR_NODE_STATUS_TENDERMINT,
previousStatus:
Schema.ValidatorStatus.VALIDATOR_NODE_STATUS_TENDERMINT,
rankingScore: '',
votingPower: '',
},
},
},
@@ -7,12 +7,12 @@ import { AgGridLazy as AgGrid } from '@vegaprotocol/datagrid';
import { useAppState } from '../../../../contexts/app-state/app-state-context';
import { BigNumber } from '../../../../lib/bignumber';
import {
calculateOverallPenalty,
calculateOverstakedPenalty,
calculatesPerformancePenalty,
getFormattedPerformanceScore,
getLastEpochScoreAndPerformance,
getNormalisedVotingPower,
getOverstakingPenalty,
getPerformancePenalty,
getTotalPenalties,
getUnnormalisedVotingPower,
} from '../../shared';
import {
@@ -32,7 +32,6 @@ import type { ValidatorsTableProps } from './shared';
import {
formatNumber,
formatNumberPercentage,
removePaginationWrapper,
toBigNum,
} from '@vegaprotocol/utils';
import { VALIDATOR_LOGO_MAP } from './logo-map';
@@ -137,10 +136,6 @@ export const ConsensusValidatorsTable = ({
[totalStake]
);
const allNodesInPreviousEpoch = removePaginationWrapper(
previousEpochData?.epoch.validatorsConnection?.edges
);
const nodes = useMemo(() => {
if (!data) return [];
let canonisedNodes = data
@@ -165,7 +160,7 @@ export const ConsensusValidatorsTable = ({
stakedByDelegates,
stakedByOperator,
stakedTotal,
rankingScore: { stakeScore, votingPower, performanceScore },
rankingScore: { stakeScore, votingPower },
pendingStake,
stakedTotalRanking,
stakedByUser,
@@ -177,8 +172,11 @@ export const ConsensusValidatorsTable = ({
: avatarUrl
? avatarUrl
: null;
const { rawValidatorScore: previousEpochValidatorScore } =
getLastEpochScoreAndPerformance(previousEpochData, id);
const {
rawValidatorScore: previousEpochValidatorScore,
performanceScore: previousEpochPerformanceScore,
stakeScore: previousEpochStakeScore,
} = getLastEpochScoreAndPerformance(previousEpochData, id);
return {
id,
@@ -201,19 +199,21 @@ export const ConsensusValidatorsTable = ({
toBigNum(stakedByOperator, decimals),
2
),
[ValidatorFields.PERFORMANCE_SCORE]:
getFormattedPerformanceScore(performanceScore).toString(),
[ValidatorFields.PERFORMANCE_PENALTY]: formatNumberPercentage(
calculatesPerformancePenalty(performanceScore),
2
[ValidatorFields.PERFORMANCE_SCORE]: getFormattedPerformanceScore(
previousEpochPerformanceScore
).toString(),
[ValidatorFields.PERFORMANCE_PENALTY]: getPerformancePenalty(
previousEpochPerformanceScore
),
[ValidatorFields.OVERSTAKING_PENALTY]: formatNumberPercentage(
calculateOverstakedPenalty(id, allNodesInPreviousEpoch),
2
[ValidatorFields.OVERSTAKING_PENALTY]: getOverstakingPenalty(
previousEpochValidatorScore,
previousEpochStakeScore
),
[ValidatorFields.TOTAL_PENALTIES]: formatNumberPercentage(
calculateOverallPenalty(id, allNodesInPreviousEpoch),
2
[ValidatorFields.TOTAL_PENALTIES]: getTotalPenalties(
previousEpochValidatorScore,
previousEpochPerformanceScore,
stakedTotal,
totalStake
),
[ValidatorFields.PENDING_STAKE]: pendingStake,
[ValidatorFields.STAKED_BY_USER]: stakedByUser
@@ -328,12 +328,12 @@ export const ConsensusValidatorsTable = ({
...remaining,
];
}, [
allNodesInPreviousEpoch,
data,
decimals,
hideTopThird,
previousEpochData,
thirdOfTotalStake,
totalStake,
validatorsView,
]);
@@ -5,14 +5,15 @@ import { AgGridLazy as AgGrid } from '@vegaprotocol/datagrid';
import { useAppState } from '../../../../contexts/app-state/app-state-context';
import { BigNumber } from '../../../../lib/bignumber';
import {
calculatesPerformancePenalty,
calculateOverallPenalty,
calculateOverstakedPenalty,
getFormattedPerformanceScore,
getLastEpochScoreAndPerformance,
getOverstakingPenalty,
getPerformancePenalty,
getTotalPenalties,
} from '../../shared';
import {
defaultColDef,
StakeNeededForPromotionRenderer,
stakedTotalPercentage,
ValidatorFields,
ValidatorRenderer,
@@ -27,7 +28,6 @@ import type { ValidatorsTableProps } from './shared';
import {
formatNumber,
formatNumberPercentage,
removePaginationWrapper,
toBigNum,
} from '@vegaprotocol/utils';
@@ -52,10 +52,6 @@ export const StandbyPendingValidatorsTable = ({
const gridRef = useRef<AgGridReact | null>(null);
const allNodesInPreviousEpoch = removePaginationWrapper(
previousEpochData?.epoch.validatorsConnection?.edges
);
let nodes = useMemo(() => {
if (!data) return [];
@@ -81,15 +77,18 @@ export const StandbyPendingValidatorsTable = ({
stakedByDelegates,
stakedByOperator,
stakedTotal,
rankingScore: { stakeScore, performanceScore },
rankingScore: { stakeScore },
pendingStake,
stakedTotalRanking,
stakedByUser,
pendingUserStake,
userStakeShare,
}) => {
const { performanceScore: previousEpochPerformanceScore } =
getLastEpochScoreAndPerformance(previousEpochData, id);
const {
rawValidatorScore: previousEpochValidatorScore,
performanceScore: previousEpochPerformanceScore,
stakeScore: previousEpochStakeScore,
} = getLastEpochScoreAndPerformance(previousEpochData, id);
let individualStakeNeededForPromotion,
individualStakeNeededForPromotionDescription;
@@ -145,19 +144,21 @@ export const StandbyPendingValidatorsTable = ({
toBigNum(stakedByOperator, decimals),
2
),
[ValidatorFields.PERFORMANCE_SCORE]:
getFormattedPerformanceScore(performanceScore).toString(),
[ValidatorFields.PERFORMANCE_PENALTY]: formatNumberPercentage(
calculatesPerformancePenalty(performanceScore),
2
[ValidatorFields.PERFORMANCE_SCORE]: getFormattedPerformanceScore(
previousEpochPerformanceScore
).toString(),
[ValidatorFields.PERFORMANCE_PENALTY]: getPerformancePenalty(
previousEpochPerformanceScore
),
[ValidatorFields.OVERSTAKING_PENALTY]: formatNumberPercentage(
calculateOverstakedPenalty(id, allNodesInPreviousEpoch),
2
[ValidatorFields.OVERSTAKING_PENALTY]: getOverstakingPenalty(
previousEpochValidatorScore,
previousEpochStakeScore
),
[ValidatorFields.TOTAL_PENALTIES]: formatNumberPercentage(
calculateOverallPenalty(id, allNodesInPreviousEpoch),
2
[ValidatorFields.TOTAL_PENALTIES]: getTotalPenalties(
previousEpochValidatorScore,
previousEpochPerformanceScore,
stakedTotal,
totalStake
),
[ValidatorFields.PENDING_STAKE]: pendingStake,
[ValidatorFields.STAKED_BY_USER]: stakedByUser
@@ -171,13 +172,13 @@ export const StandbyPendingValidatorsTable = ({
}
);
}, [
allNodesInPreviousEpoch,
data,
decimals,
previousEpochData,
stakeNeededForPromotion,
stakeNeededForPromotionDescription,
t,
totalStake,
]);
if (validatorsView === 'myStake') {
@@ -225,21 +226,21 @@ export const StandbyPendingValidatorsTable = ({
cellRenderer: StakeShareRenderer,
width: 100,
},
// {
// field: ValidatorFields.STAKE_NEEDED_FOR_PROMOTION,
// headerName: t(ValidatorFields.STAKE_NEEDED_FOR_PROMOTION).toString(),
// headerTooltip: t(stakeNeededForPromotionDescription, {
// prefix: t('The'),
// }),
// cellRenderer: StakeNeededForPromotionRenderer,
// width: 210,
// },
{
field: ValidatorFields.STAKE_NEEDED_FOR_PROMOTION,
headerName: t(ValidatorFields.STAKE_NEEDED_FOR_PROMOTION).toString(),
headerTooltip: t(stakeNeededForPromotionDescription, {
prefix: t('The'),
}),
cellRenderer: StakeNeededForPromotionRenderer,
width: 210,
},
{
field: ValidatorFields.TOTAL_PENALTIES,
headerName: t(ValidatorFields.TOTAL_PENALTIES).toString(),
headerTooltip: t('TotalPenaltiesDescription').toString(),
cellRenderer: TotalPenaltiesRenderer,
width: 120 + 210,
width: 120,
},
],
[]
@@ -1,15 +1,11 @@
import { useMemo } from 'react';
import React, { useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import {
useEnvironment,
DocsLinks,
ExternalLinks,
} from '@vegaprotocol/environment';
import {
formatNumberPercentage,
removePaginationWrapper,
toBigNum,
} from '@vegaprotocol/utils';
import { toBigNum } from '@vegaprotocol/utils';
import * as Schema from '@vegaprotocol/types';
import {
Link as UTLink,
@@ -28,11 +24,11 @@ import { SubHeading } from '../../../components/heading';
import {
getLastEpochScoreAndPerformance,
getNormalisedVotingPower,
getOverstakingPenalty,
getPerformancePenalty,
getTotalPenalties,
getUnnormalisedVotingPower,
getStakePercentage,
calculatesPerformancePenalty,
calculateOverstakedPenalty,
calculateOverallPenalty,
} from '../shared';
import type { ReactNode } from 'react';
import type { StakingNodeFieldsFragment } from '../__generated__/Staking';
@@ -82,27 +78,17 @@ export const ValidatorTable = ({
const stakedOnNode = toBigNum(node.stakedTotal, decimals);
const { rawValidatorScore } = getLastEpochScoreAndPerformance(
previousEpochData,
node.id
);
const { rawValidatorScore, performanceScore, stakeScore } =
getLastEpochScoreAndPerformance(previousEpochData, node.id);
const stakePercentage = getStakePercentage(total, stakedOnNode);
const penalties = useMemo(() => {
const allNodesInPreviousEpoch = removePaginationWrapper(
previousEpochData?.epoch.validatorsConnection?.edges
);
return {
// current epoch
performance: calculatesPerformancePenalty(
node.rankingScore.performanceScore
),
// previous epoch
overstaked: calculateOverstakedPenalty(node.id, allNodesInPreviousEpoch),
overall: calculateOverallPenalty(node.id, allNodesInPreviousEpoch),
};
}, [node, previousEpochData?.epoch.validatorsConnection?.edges]);
const totalPenaltiesAmount = getTotalPenalties(
rawValidatorScore,
performanceScore,
stakedOnNode.toString(),
total.toString()
);
return (
<>
@@ -256,7 +242,7 @@ export const ValidatorTable = ({
<Tooltip description={t('OverstakedPenaltyDescription')}>
<span data-testid="overstaking-penalty">
{formatNumberPercentage(penalties.overstaked, 2)}
{getOverstakingPenalty(rawValidatorScore, stakeScore)}
</span>
</Tooltip>
</KeyValueTableRow>
@@ -265,7 +251,7 @@ export const ValidatorTable = ({
<Tooltip description={t('PerformancePenaltyDescription')}>
<span data-testid="performance-penalty">
{formatNumberPercentage(penalties.performance, 2)}
{getPerformancePenalty(performanceScore)}
</span>
</Tooltip>
</KeyValueTableRow>
@@ -274,7 +260,7 @@ export const ValidatorTable = ({
<strong>{t('TOTAL PENALTIES')}</strong>
</span>
<span data-testid="total-penalties">
<strong>{formatNumberPercentage(penalties.overall, 2)}</strong>
<strong>{totalPenaltiesAmount}</strong>
</span>
</KeyValueTableRow>
</KeyValueTable>
@@ -9,7 +9,6 @@ import {
getTotalPenalties,
getStakePercentage,
} from './shared';
import * as Schema from '@vegaprotocol/types';
describe('getLastEpochScoreAndPerformance', () => {
const mockPreviousEpochData = {
@@ -20,48 +19,24 @@ describe('getLastEpochScoreAndPerformance', () => {
{
node: {
id: '0x123',
stakedTotal: '',
rewardScore: {
rawValidatorScore: '0.25',
performanceScore: '0.75',
multisigScore: '',
validatorScore: '',
normalisedScore: '',
validatorStatus:
Schema.ValidatorStatus.VALIDATOR_NODE_STATUS_TENDERMINT,
},
rankingScore: {
stakeScore: '0.25',
performanceScore: '0.75',
status: Schema.ValidatorStatus.VALIDATOR_NODE_STATUS_TENDERMINT,
previousStatus:
Schema.ValidatorStatus.VALIDATOR_NODE_STATUS_TENDERMINT,
rankingScore: '',
votingPower: '',
},
},
},
{
node: {
id: '0x234',
stakedTotal: '',
rewardScore: {
rawValidatorScore: '0.35',
performanceScore: '0.85',
multisigScore: '',
validatorScore: '',
normalisedScore: '',
validatorStatus:
Schema.ValidatorStatus.VALIDATOR_NODE_STATUS_TENDERMINT,
},
rankingScore: {
stakeScore: '0.25',
performanceScore: '0.85',
status: Schema.ValidatorStatus.VALIDATOR_NODE_STATUS_TENDERMINT,
previousStatus:
Schema.ValidatorStatus.VALIDATOR_NODE_STATUS_TENDERMINT,
rankingScore: '',
votingPower: '',
},
},
},
+1 -81
View File
@@ -4,86 +4,6 @@ import {
} from '@vegaprotocol/utils';
import type { PreviousEpochQuery } from './__generated__/PreviousEpoch';
import { BigNumber } from '../../lib/bignumber';
import type { LastArrayElement } from 'type-fest';
type Node = NonNullable<
LastArrayElement<
NonNullable<
NonNullable<PreviousEpochQuery['epoch']['validatorsConnection']>['edges']
>
>
>['node'];
/**
* Calculates theoretical stake score for a given node
* @param nodeId Id of a node for which a score is calculated
* @param nodes A collection of all nodes
* @returns Theoretical stake score for given node based on the staked total
* of all node of the same type (status)
*/
const calculateTheoreticalStakeScore = (nodeId: string, nodes: Node[]) => {
const node = nodes.find((n) => n.id === nodeId);
if (!node) {
return new BigNumber(0);
}
const all = nodes
.filter((n) => n.rankingScore.status === node.rankingScore.status)
.map((n) => new BigNumber(n.stakedTotal));
const sumOfSameType = all.reduce((acc, a) => acc.plus(a), new BigNumber(0));
if (sumOfSameType.isZero()) {
return new BigNumber(0);
}
return new BigNumber(node.stakedTotal).dividedBy(sumOfSameType);
};
/**
* Calculates overall penalty for a given node
* @param nodeId Id of a node for which a penalty is calculated
* @param nodes A collection of all nodes - needed to calculate theoretical stake score
* @returns %
*/
export const calculateOverallPenalty = (nodeId: string, nodes: Node[]) => {
const node = nodes.find((n) => n.id === nodeId);
const tts = calculateTheoreticalStakeScore(nodeId, nodes);
if (!node || tts.isZero()) {
return new BigNumber(0);
}
const penalty = new BigNumber(1)
.minus(new BigNumber(node.rewardScore?.validatorScore || 0).dividedBy(tts))
.times(100);
return penalty.isLessThan(0) ? new BigNumber(0) : penalty;
};
/**
* Calculates over-staked penalty for a given node
* @param nodeId Id of a node for which a penalty is calculated
* @param nodes A collection of all nodes - needed to calculate theoretical stake score
* @returns %
*/
export const calculateOverstakedPenalty = (nodeId: string, nodes: Node[]) => {
const node = nodes.find((n) => n.id === nodeId);
const tts = calculateTheoreticalStakeScore(nodeId, nodes);
if (!node || tts.isZero()) {
return new BigNumber(0);
}
const penalty = new BigNumber(1)
.minus(
new BigNumber(node.rewardScore?.rawValidatorScore || 0).dividedBy(tts)
)
.times(100);
return penalty.isLessThan(0) ? new BigNumber(0) : penalty;
};
/**
* Calculates performance penalty based on the given performance score.
* @returns %
*/
export const calculatesPerformancePenalty = (performanceScore: string) => {
const penalty = new BigNumber(1)
.minus(new BigNumber(performanceScore))
.times(100);
return penalty.isLessThan(0) ? new BigNumber(0) : penalty;
};
export const getLastEpochScoreAndPerformance = (
previousEpochData: PreviousEpochQuery | undefined,
@@ -95,7 +15,7 @@ export const getLastEpochScoreAndPerformance = (
return {
rawValidatorScore: validator?.rewardScore?.rawValidatorScore,
performanceScore: validator?.rankingScore?.performanceScore,
performanceScore: validator?.rewardScore?.performanceScore,
stakeScore: validator?.rankingScore?.stakeScore,
};
};
+1 -2
View File
@@ -23,6 +23,5 @@ NX_VEGA_URL="https://api.n07.testnet.vega.xyz/graphql"
NX_VEGA_WALLET_URL=http://localhost:1789
NX_ETHEREUM_PROVIDER_URL=https://sepolia.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8
NX_ETHERSCAN_URL=https://sepolia.etherscan.io
NX_VEGA_NETWORKS={\"TESTNET\":\"https://console.fairground.wtf\",\"STAGNET1\":\"https://trading.stagnet1.vega.rocks\"}
NX_VEGA_NETWORKS={\"TESTNET\":\"https://console.fairground.wtf\",\"STAGNET1\":\"https://stagnet1.console.vega.xyz\"}
NX_VEGA_EXPLORER_URL=https://explorer.fairground.wtf
NX_VEGA_CONSOLE_URL=https://console.fairground.wtf

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