Compare commits

..
567 changed files with 159720 additions and 18706 deletions
+3 -2
View File
@@ -14,7 +14,8 @@ What we need to achieve and who for
## Tasks ## Tasks
- [ ] - [ ] What do we need to do first
- [ ] - [ ] and then what?
- [ ] Etc.
## Additional details / background info ## Additional details / background info
+4 -7
View File
@@ -22,14 +22,11 @@ So that
## Tasks ## Tasks
- [ ] UX (if needed) - [ ] Explore and sketch
- [ ] Design (if needed)
- [ ] Team and stakeholder review - [ ] Team and stakeholder review
- [ ] Specs reviewed and created or adjusted - [ ] Visual Design
- [ ] Implementation - [ ] Team review
- [ ] Testing (unit and/or e2e) - [ ] Etc.
- [ ] Code review
- [ ] QA review
## Sketch ## Sketch
@@ -9,11 +9,6 @@ jobs:
runs-on: ubuntu-22.04 runs-on: ubuntu-22.04
timeout-minutes: 30 timeout-minutes: 30
steps: steps:
- name: Checkout
uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Set up Docker Buildx - name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2 uses: docker/setup-buildx-action@v2
@@ -32,16 +27,14 @@ jobs:
- name: resolve ipfs hashes for release - name: resolve ipfs hashes for release
run: | run: |
echo "Tag name: ${{ github.event.release.tag_name }}"
echo "Name: ${{ github.event.release.name }}" echo "Name: ${{ github.event.release.name }}"
echo "Description: ${{ github.event.release.body }}" echo "Description: ${{ github.event.release.body }}"
echo "Tag: ${{ github.event.release.tag_name }}" until docker pull vegaprotocol/trading:${{ github.event.release.tag_name }}; do
commit="$(git rev-list -n 1 ${{ github.event.release.tag_name }})"
echo "Commit: $commit"
until docker pull vegaprotocol/trading:$commit; do
echo "Image not pushed yet, waiting 60 seconds" echo "Image not pushed yet, waiting 60 seconds"
sleep 60 sleep 60
done done
docker run --rm vegaprotocol/trading:$commit cat /ipfs-hash > ipfs-hash docker run --rm vegaprotocol/trading:${{ github.event.release.tag_name }} cat /ipfs-hash > ipfs-hash
curl -L https://dist.ipfs.tech/kubo/v0.20.0/kubo_v0.20.0_linux-amd64.tar.gz -o kubo.tgz 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 tar -xzf kubo.tgz
export PATH="$PATH:$PWD/kubo" export PATH="$PATH:$PWD/kubo"
@@ -55,20 +48,19 @@ jobs:
token: ${{ secrets.GITHUB_TOKEN }} token: ${{ secrets.GITHUB_TOKEN }}
id: ${{ github.event.release.id }} id: ${{ github.event.release.id }}
body: | body: |
---
___
# Deployments # Deployments
* https://explorer.vega.xyz * https://explorer.vega.xyz
* https://governance.vega.xyz * https://governance.vega.xyz
# IPFS releases # 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 }} CIDv0: ${{ env.IPFS_V0 }}
CIDv1: ${{ env.IPFS_V1 }} 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. 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/). 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/).
+30 -112
View File
@@ -5,10 +5,9 @@ on:
branches: branches:
- release/* - release/*
- develop - develop
- main tags:
# uncomment pull_request and comment pull_request_target to test CI changes against feature branch not target branch (develop) - v*
# pull_request: pull_request:
pull_request_target:
types: types:
- opened - opened
- ready_for_review - ready_for_review
@@ -22,8 +21,6 @@ jobs:
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v3 uses: actions/checkout@v3
with:
ref: ${{ github.event.pull_request.head.sha || github.sha }}
- name: Cache node modules - name: Cache node modules
id: cache id: cache
@@ -64,7 +61,6 @@ jobs:
uses: actions/checkout@v3 uses: actions/checkout@v3
with: with:
fetch-depth: 0 fetch-depth: 0
ref: ${{ github.event.pull_request.head.sha || github.sha }}
- name: Setup node - name: Setup node
uses: actions/setup-node@v3 uses: actions/setup-node@v3
@@ -102,96 +98,44 @@ jobs:
# See affected apps # See affected apps
- name: See affected apps - name: See affected apps
run: | 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 ">>>> debug"
echo "NX_BASE: ${{ env.NX_BASE }}" echo "NX_BASE: ${{ env.NX_BASE }}"
echo "NX_HEAD: ${{ env.NX_HEAD }}" echo "NX_HEAD: ${{ env.NX_HEAD }}"
echo "Affected: ${affected}"
echo "Branch slug: ${branch_slug}"
echo ">>>> eof debug" echo ">>>> eof debug"
projects_array=() 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_governance="not deployed"
preview_trading="not deployed" preview_trading="not deployed"
preview_explorer="not deployed" preview_explorer="not deployed"
preview_tools="not deployed" if [[ -z "$projects_e2e" ]]; then
projects_e2e+='"governance-e2e" "trading-e2e" "explorer-e2e" '
# parse if affected is any of three main applications, if none - use all of them
if echo "$affected" | grep -q governance; then
echo "Governance is affected"
projects_array+=("governance")
preview_governance=$(printf "https://%s.%s.vega.rocks" "governance" "$branch_slug")
fi
if echo "$affected" | grep -q trading; then
echo "Trading is affected"
projects_array+=("trading")
preview_trading=$(printf "https://%s.%s.vega.rocks" "trading" "$branch_slug")
fi
if echo "$affected" | grep -q explorer; then
echo "Explorer is affected"
projects_array+=("explorer")
preview_explorer=$(printf "https://%s.%s.vega.rocks" "explorer" "$branch_slug")
fi
if [[ ${#projects_array[@]} -eq 0 ]]; then
projects_array=("governance" "trading" "explorer")
preview_governance=$(printf "https://%s.%s.vega.rocks" "governance" "$branch_slug") preview_governance=$(printf "https://%s.%s.vega.rocks" "governance" "$branch_slug")
preview_trading=$(printf "https://%s.%s.vega.rocks" "trading" "$branch_slug") preview_trading=$(printf "https://%s.%s.vega.rocks" "trading" "$branch_slug")
preview_explorer=$(printf "https://%s.%s.vega.rocks" "explorer" "$branch_slug") preview_explorer=$(printf "https://%s.%s.vega.rocks" "explorer" "$branch_slug")
else
if [[ $affected == *"governance"* ]]; then
projects_e2e+='"governance-e2e" '
preview_governance=$(printf "https://%s.%s.vega.rocks" "governance" "$branch_slug")
fi fi
if [[ $affected == *"trading"* ]]; then
# applications parsed before this loop are applicable for running e2e-tests projects_e2e+='"trading-e2e" '
projects_e2e_array=() preview_trading=$(printf "https://%s.%s.vega.rocks" "trading" "$branch_slug")
for project in "${projects_array[@]}"; do
projects_e2e_array+=("${project}-e2e")
done
# all applications below this loop are not applicable for running e2e-test
# check if pull request event to deploy tools
if [[ "${{ github.event_name }}" = "pull_request" ]]; then
if echo "$affected" | grep -q multisig-signer; then
echo "Tools are affected"
echo "Deploying tools on preview"
preview_tools=$(printf "https://%s.%s.vega.rocks" "tools" "$branch_slug")
projects_array+=("multisig-signer")
fi fi
# those apps deploy only from develop to mainnet if [[ $affected == *"explorer"* ]]; then
elif [[ "${{ github.ref }}" =~ .*develop$ ]]; then projects_e2e+='"explorer-e2e" '
if echo "$affected" | grep -q multisig-signer; then preview_explorer=$(printf "https://%s.%s.vega.rocks" "explorer" "$branch_slug")
echo "Tools are affected"
echo "Deploying tools on s3"
projects_array+=("multisig-signer")
fi
if echo "$affected" | grep -q static; then
echo "static is affected"
echo "Deploying static on s3"
projects_array+=("static")
fi
if echo "$affected" | grep -q ui-toolkit; then
echo "ui-toolkit is affected"
echo "Deploying ui-toolkit on s3"
projects_array+=("ui-toolkit")
fi fi
fi fi
projects_e2e=${projects_e2e%?}
echo "Projects: ${projects_array[@]}" projects_e2e=[${projects_e2e// /,}]
echo "Projects E2E: ${projects_e2e_array[@]}" echo PROJECTS_E2E=$projects_e2e >> $GITHUB_ENV
projects_json=$(jq -M --compact-output --null-input '$ARGS.positional' --args -- "${projects_array[@]}") echo PROJECTS=$(echo $projects_e2e | sed 's|-e2e||g') >> $GITHUB_ENV
projects_e2e_json=$(jq -M --compact-output --null-input '$ARGS.positional' --args -- "${projects_e2e_array[@]}")
echo PROJECTS_E2E=$projects_e2e_json >> $GITHUB_ENV
echo PROJECTS=$projects_json >> $GITHUB_ENV
echo PREVIEW_GOVERNANCE=$preview_governance >> $GITHUB_ENV echo PREVIEW_GOVERNANCE=$preview_governance >> $GITHUB_ENV
echo PREVIEW_TRADING=$preview_trading >> $GITHUB_ENV echo PREVIEW_TRADING=$preview_trading >> $GITHUB_ENV
echo PREVIEW_EXPLORER=$preview_explorer >> $GITHUB_ENV echo PREVIEW_EXPLORER=$preview_explorer >> $GITHUB_ENV
echo PREVIEW_TOOLS=$preview_tools >> $GITHUB_ENV
outputs: outputs:
projects: ${{ env.PROJECTS }} projects: ${{ env.PROJECTS }}
@@ -199,12 +143,11 @@ jobs:
preview_governance: ${{ env.PREVIEW_GOVERNANCE }} preview_governance: ${{ env.PREVIEW_GOVERNANCE }}
preview_trading: ${{ env.PREVIEW_TRADING }} preview_trading: ${{ env.PREVIEW_TRADING }}
preview_explorer: ${{ env.PREVIEW_EXPLORER }} preview_explorer: ${{ env.PREVIEW_EXPLORER }}
preview_tools: ${{ env.PREVIEW_TOOLS }}
cypress: cypress:
needs: lint-test-build needs: lint-test-build
name: '(CI) cypress' name: '(CI) cypress'
if: ${{ needs.lint-test-build.outputs.projects-e2e != '[]' }} if: ${{ needs.lint-test-build.outputs.projects != '[]' }}
uses: ./.github/workflows/cypress-run.yml uses: ./.github/workflows/cypress-run.yml
secrets: inherit secrets: inherit
with: with:
@@ -226,7 +169,6 @@ jobs:
- publish-dist - publish-dist
- lint-test-build - lint-test-build
if: ${{ github.event_name == 'pull_request' }} if: ${{ github.event_name == 'pull_request' }}
timeout-minutes: 60
name: '(CD) comment preview links' name: '(CD) comment preview links'
steps: steps:
- name: Find Comment - name: Find Comment
@@ -236,35 +178,6 @@ jobs:
issue-number: ${{ github.event.pull_request.number }} issue-number: ${{ github.event.pull_request.number }}
body-includes: Previews 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 - name: Create comment
uses: peter-evans/create-or-update-comment@v3 uses: peter-evans/create-or-update-comment@v3
if: ${{ steps.fc.outputs.comment-id == 0 }} if: ${{ steps.fc.outputs.comment-id == 0 }}
@@ -275,11 +188,16 @@ jobs:
* governance: ${{ needs.lint-test-build.outputs.preview_governance }} * governance: ${{ needs.lint-test-build.outputs.preview_governance }}
* explorer: ${{ needs.lint-test-build.outputs.preview_explorer }} * explorer: ${{ needs.lint-test-build.outputs.preview_explorer }}
* trading: ${{ needs.lint-test-build.outputs.preview_trading }} * 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: cypress-check:
name: '(CI) 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() }} if: ${{ always() }}
needs: cypress needs: cypress
runs-on: ubuntu-22.04 runs-on: ubuntu-22.04
+1 -2
View File
@@ -20,7 +20,7 @@ jobs:
project: ${{ fromJSON(inputs.projects) }} project: ${{ fromJSON(inputs.projects) }}
name: ${{ matrix.project }} name: ${{ matrix.project }}
runs-on: self-hosted-runner runs-on: self-hosted-runner
timeout-minutes: 100 timeout-minutes: 60
steps: steps:
# Checks if skip cache was requested # Checks if skip cache was requested
- name: Set skip-nx-cache flag - name: Set skip-nx-cache flag
@@ -33,7 +33,6 @@ jobs:
with: with:
fetch-depth: 0 fetch-depth: 0
path: './frontend-monorepo' path: './frontend-monorepo'
ref: ${{ github.event.pull_request.head.sha || github.sha }}
# Restore node_modules from cache if possible # Restore node_modules from cache if possible
- name: Restore node_modules from cache - name: Restore node_modules from cache
-2
View File
@@ -11,8 +11,6 @@ jobs:
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v3 uses: actions/checkout@v3
with:
ref: ${{ github.event.pull_request.head.sha || github.sha }}
- name: Setup node - name: Setup node
uses: actions/setup-node@v3 uses: actions/setup-node@v3
+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
+41 -136
View File
@@ -19,8 +19,6 @@ jobs:
steps: steps:
- name: Check out code - name: Check out code
uses: actions/checkout@v3 uses: actions/checkout@v3
with:
ref: ${{ github.event.pull_request.head.sha || github.sha }}
- name: Set up QEMU - name: Set up QEMU
id: quemu id: quemu
@@ -33,7 +31,6 @@ jobs:
uses: docker/setup-buildx-action@v2 uses: docker/setup-buildx-action@v2
- name: Log in to the Container registry (ghcr) - name: Log in to the Container registry (ghcr)
if: ${{ github.event_name == 'pull_request' }}
uses: docker/login-action@v2 uses: docker/login-action@v2
with: with:
registry: ghcr.io registry: ghcr.io
@@ -42,7 +39,7 @@ jobs:
- name: Log in to the Container registry (docker hub) - name: Log in to the Container registry (docker hub)
uses: docker/login-action@v2 uses: docker/login-action@v2
if: ${{ matrix.app == 'trading' && github.event_name == 'push' && ( endsWith(github.ref, 'main') || endsWith(github.ref, 'release/testnet') ) }} if: ${{ startsWith(github.ref, 'refs/tags/v') }}
with: with:
# registry: registry.hub.docker.com # registry: registry.hub.docker.com
username: ${{ secrets.DOCKERHUB_USERNAME }} username: ${{ secrets.DOCKERHUB_USERNAME }}
@@ -62,68 +59,38 @@ jobs:
key: ${{ runner.os }}-cache-node-modules-${{ hashFiles('yarn.lock') }} key: ${{ runner.os }}-cache-node-modules-${{ hashFiles('yarn.lock') }}
# https://docs.github.com/en/actions/learn-github-actions/contexts # https://docs.github.com/en/actions/learn-github-actions/contexts
- name: Define dist variables - name: Define variables
if: ${{ github.event_name == 'push' }}
run: | run: |
envName='' envName=''
if [[ "${{ github.event_name }}" = "push" ]]; then
domain="vega.rocks" domain="vega.rocks"
bucketName=''
if [[ "${{ github.ref }}" =~ .*release/.* ]]; then if [[ "${{ github.ref }}" =~ .*release/.* ]]; then
envName="$(echo ${{ github.ref }} | rev | cut -d '/' -f 1 | rev)" envName="$(echo ${{ github.ref }} | rev | cut -d '/' -f 1 | rev)"
if [[ "${{ github.ref }}" =~ .*mainnet.* ]]; then
domain="vega.community"
fi
elif [[ "${{ github.ref }}" =~ .*develop$ ]]; then elif [[ "${{ github.ref }}" =~ .*develop$ ]]; then
envName="stagnet1" envName="stagnet1"
if [[ "${{ matrix.app }}" = "multisig-signer" ]]; then elif [[ "${{ matrix.app}}" = "trading" ]] && [[ ${{ startsWith(github.ref, 'refs/tags/v') && 'true' || 'false' }} = "true" ]]; then
envName="mainnet"
bucketName="tools.vega.xyz"
fi
if [[ "${{ matrix.app }}" = "static" ]]; then
envName="mainnet"
bucketName="static.vega.xyz"
fi
if [[ "${{ matrix.app }}" = "ui-toolkit" ]]; then
envName="mainnet"
bucketName="ui.vega.rocks"
fi
elif [[ "${{ github.ref }}" =~ .*main$ ]]; then
envName="mainnet" envName="mainnet"
fi fi
if [[ "${envName}" = "mainnet" ]]; then
domain="vega.xyz"
if [[ -z "${bucketName}" ]]; then
bucketName="${{ matrix.app }}.${domain}"
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}" bucketName="${{ matrix.app }}.${envName}.${domain}"
fi
echo "bucket name: ${bucketName}"
echo "env name: ${envName}"
echo BUCKET_NAME=${bucketName} >> $GITHUB_ENV echo BUCKET_NAME=${bucketName} >> $GITHUB_ENV
fi
echo ENV_NAME=${envName} >> $GITHUB_ENV echo ENV_NAME=${envName} >> $GITHUB_ENV
- name: Build local dist - name: Build local dist
run: | run: |
flags="" flags=""
if [[ ! -z "${{ env.ENV_NAME }}" ]]; then if [[ ! -z "${{ env.ENV_NAME }}" ]]; then
if [[ "${{ env.ENV_NAME }}" != "ops-vega" ]]; then
flags="--env=${{ env.ENV_NAME }}" flags="--env=${{ env.ENV_NAME }}"
fi fi
fi
if [ "${{ matrix.app }}" = "trading" ]; then if [ "${{ matrix.app }}" = "trading" ]; then
yarn nx export trading $flags || (yarn install && yarn nx export trading $flags) yarn nx export trading $flags || (yarn install && yarn nx export trading $flags)
DIST_LOCATION=dist/apps/trading/exported DIST_LOCATION=dist/apps/trading/exported
elif [ "${{ matrix.app }}" = "ui-toolkit" ]; then
NODE_ENV=production yarn nx run ui-toolkit:build-storybook
DIST_LOCATION=dist/storybook/ui-toolkit
else else
yarn nx build ${{ matrix.app }} $flags || (yarn install && yarn nx build ${{ matrix.app }} $flags) yarn nx build ${{ matrix.app }} $flags || (yarn install && yarn nx build ${{ matrix.app }} $flags)
DIST_LOCATION=dist/apps/${{ matrix.app }} DIST_LOCATION=dist/apps/${{ matrix.app }}
@@ -158,9 +125,7 @@ jobs:
- name: Publish dist as docker image (ghcr) - name: Publish dist as docker image (ghcr)
uses: docker/build-push-action@v3 uses: docker/build-push-action@v3
continue-on-error: true if: ${{ github.event_name == 'pull_request' || (matrix.app == 'trading' && github.event_name == 'push' && !startsWith(github.ref, 'refs/tags/v') ) }}
id: ghcr-push
if: ${{ github.event_name == 'pull_request' }}
with: with:
context: . context: .
file: docker/node-outside-docker.Dockerfile file: docker/node-outside-docker.Dockerfile
@@ -173,9 +138,7 @@ jobs:
- name: Publish dist as docker image (docker hub) - name: Publish dist as docker image (docker hub)
uses: docker/build-push-action@v3 uses: docker/build-push-action@v3
continue-on-error: true if: ${{ matrix.app == 'trading' && github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v') }}
id: dockerhub-push
if: ${{ matrix.app == 'trading' && github.event_name == 'push' && ( endsWith(github.ref, 'main') || endsWith(github.ref, 'release/testnet') ) }}
with: with:
context: . context: .
file: docker/node-outside-docker.Dockerfile file: docker/node-outside-docker.Dockerfile
@@ -184,41 +147,13 @@ jobs:
APP=${{ matrix.app }} APP=${{ matrix.app }}
ENV_NAME=${{ env.ENV_NAME }} ENV_NAME=${{ env.ENV_NAME }}
tags: | tags: |
vegaprotocol/${{ matrix.app }}:${{ github.sha }} vegaprotocol/${{ matrix.app }}:${{ github.ref_name }}
vegaprotocol/${{ matrix.app }}:${{ endsWith(github.ref, 'main') && 'mainnet' || endsWith(github.ref, 'release/testnet') && 'testnet' || '' }} vegaprotocol/${{ matrix.app }}:mainnet
- name: Publish dist as docker image (ghcr - retry)
uses: docker/build-push-action@v3
if: ${{ steps.ghcr-push.outcome == 'failure' }}
with:
context: .
file: docker/node-outside-docker.Dockerfile
push: true
build-args: |
APP=${{ matrix.app }}
ENV_NAME=${{ env.ENV_NAME }}
tags: |
ghcr.io/vegaprotocol/frontend/${{ matrix.app }}:${{ github.event.pull_request.head.sha || github.sha }}
- name: Publish dist as docker image (docker hub - retry)
uses: docker/build-push-action@v3
if: ${{ steps.dockerhub-push.outcome == 'failure' }}
with:
context: .
file: docker/node-outside-docker.Dockerfile
push: true
build-args: |
APP=${{ matrix.app }}
ENV_NAME=${{ env.ENV_NAME }}
tags: |
vegaprotocol/${{ matrix.app }}:${{ github.sha }}
vegaprotocol/${{ matrix.app }}:${{ endsWith(github.ref, 'main') && 'mainnet' || endsWith(github.ref, 'release/testnet') && 'testnet' || '' }}
# bucket creation in github.com/vegaprotocol/terraform//frontend # bucket creation in github.com/vegaprotocol/terraform//frontend
- name: Publish dist to s3 - name: Publish dist to s3
uses: jakejarvis/s3-sync-action@master uses: jakejarvis/s3-sync-action@master
# s3 releases are not happening for trading on mainnet - it's IPFS if: ${{ github.event_name == 'push' && !startsWith(github.ref, 'refs/tags/v') }}
if: ${{ github.event_name == 'push' && ( matrix.app != 'trading' || (matrix.app == 'trading' && !( endsWith(github.ref, 'main') || endsWith(github.ref, 'release/testnet') ) ) ) }}
with: with:
args: --acl private --follow-symlinks --delete args: --acl private --follow-symlinks --delete
env: env:
@@ -236,51 +171,23 @@ jobs:
number: ${{ github.event.number }} number: ${{ github.event.number }}
- name: Trigger fleek deployment - name: Trigger fleek deployment
# release to ipfs happens only on mainnet (represented by main branch) for trading if: ${{ matrix.app == 'trading' && github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v') }}
if: ${{ matrix.app == 'trading' && github.event_name == 'push' && ( endsWith(github.ref, 'main') || endsWith(github.ref, 'release/testnet') ) }}
run: | run: |
if echo ${{ github.ref }} | grep -q main; then
# display info about app # display info about app
curl --fail -H "Authorization: ${{ secrets.FLEEK_API_KEY }}" \ curl -H "Authorization: ${{ secrets.FLEEK_API_KEY }}" \
-H "Content-Type: application/json" \ -H "Content-Type: application/json" \
-d '{"query": "query{getSiteById(siteId:\"f8f2e051-f18e-49e6-b876-0a39369dc0d8\"){id latestDeploy{id status}}}"}' \ -d '{"query": "query{getSiteById(siteId:\"f8f2e051-f18e-49e6-b876-0a39369dc0d8\"){id latestDeploy{id status}}}"}' \
https://api.fleek.co/graphql https://api.fleek.co/graphql
# trigger new deployment as base image is always set to vegaprotocol/trading:mainnet # trigger new deployment as base image is always set to vegaprotocol/trading:mainnet
curl --fail -H "Authorization: ${{ secrets.FLEEK_API_KEY }}" \ curl -H "Authorization: ${{ secrets.FLEEK_API_KEY }}" \
-H "Content-Type: application/json" \ -H "Content-Type: application/json" \
-d '{"query": "mutation{triggerDeploy(siteId:\"f8f2e051-f18e-49e6-b876-0a39369dc0d8\"){id status}}"}' \ -d '{"query": "mutation{triggerDeploy(siteId:\"f8f2e051-f18e-49e6-b876-0a39369dc0d8\"){id status}}"}' \
https://api.fleek.co/graphql https://api.fleek.co/graphql
elif echo ${{ github.ref }} | grep -q release/testnet; then - name: Update vega.trading DNS to redirect to the new console
# display info about app if: ${{ matrix.app == 'trading' && github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v') }}
curl --fail -H "Authorization: ${{ secrets.FLEEK_API_KEY }}" \
-H "Content-Type: application/json" \
-d '{"query": "query{getSiteById(siteId:\"79baaeca-1952-4ae7-a256-f668cfc1d68e\"){id latestDeploy{id status}}}"}' \
https://api.fleek.co/graphql
# trigger new deployment as base image is always set to vegaprotocol/trading:mainnet
curl --fail -H "Authorization: ${{ secrets.FLEEK_API_KEY }}" \
-H "Content-Type: application/json" \
-d '{"query": "mutation{triggerDeploy(siteId:\"79baaeca-1952-4ae7-a256-f668cfc1d68e\"){id status}}"}' \
https://api.fleek.co/graphql
fi
- name: Check out ipfs-redirect
if: ${{ matrix.app == 'trading' && github.event_name == 'push' && ( endsWith(github.ref, 'main') || endsWith(github.ref, 'testnet') ) }}
uses: actions/checkout@v3
with:
repository: 'vegaprotocol/ipfs-redirect'
path: 'ipfs-redirect'
fetch-depth: '0'
token: ${{ secrets.VEGA_CI_BOT_GITHUB_TOKEN }}
- name: Update interstitial page to point to the new console
if: ${{ matrix.app == 'trading' && github.event_name == 'push' && ( endsWith(github.ref, 'main') || endsWith(github.ref, 'testnet') ) }}
env:
GH_TOKEN: ${{ secrets.VEGA_CI_BOT_GITHUB_TOKEN }}
run: | run: |
# set CID
curl -L https://dist.ipfs.tech/kubo/v0.20.0/kubo_v0.20.0_linux-amd64.tar.gz -o kubo.tgz 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 tar -xzf kubo.tgz
export PATH="$PATH:$PWD/kubo" export PATH="$PATH:$PWD/kubo"
@@ -288,28 +195,26 @@ jobs:
new_hash=$(cat ${{ matrix.app }}-ipfs-hash) new_hash=$(cat ${{ matrix.app }}-ipfs-hash)
new_cid=$(ipfs cid format -v 1 -b base32 $new_hash) new_cid=$(ipfs cid format -v 1 -b base32 $new_hash)
( # Generate console URL
cd ipfs-redirect 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}"
# configure git # Update record in DNSimple
git status # docs: https://developer.dnsimple.com/v2/zones/records/#updateZoneRecord
cat .git/config dnsimple_account_id=84895
git config --global user.email "vega-ci-bot@vega.xyz" dnsimple_zone_name=vega.trading
git config --global user.name "vega-ci-bot" dnsimple_record_id=44409591
# see: https://dnsimple.com/a/84895/domains/vega.trading/records/44409591/edit
# update CID files curl -H 'Authorization: Bearer ${{ secrets.DNSIMPLE_API_TOKEN }}' \
if echo ${{ github.ref }} | grep -q main; then -H 'Accept: application/json' \
echo $new_hash > cidv0-mainnet.txt -H 'Content-Type: application/json' \
echo $new_cid > cidv1-mainnet.txt -X PATCH \
git add cidv0-mainnet.txt cidv1-mainnet.txt -d "{
elif echo ${{ github.ref }} | grep -q release/testnet; then \"content\": \"${new_console_url}\"
echo $new_hash > cidv0-fairground.txt }" \
echo $new_cid > cidv1-fairground.txt https://api.dnsimple.com/v2/${dnsimple_account_id}/zones/${dnsimple_zone_name}/records/${dnsimple_record_id}
git add cidv0-fairground.txt cidv1-fairground.txt
fi
# create commit
commit_msg="Automated hash update from ${{ github.ref }}"
git commit -m "$commit_msg"
git push -u origin "main"
)
+23 -30
View File
@@ -43,17 +43,7 @@ jobs:
-d '{"query": "mutation{triggerDeploy(siteId:\"f8f2e051-f18e-49e6-b876-0a39369dc0d8\"){id status}}"}' \ -d '{"query": "mutation{triggerDeploy(siteId:\"f8f2e051-f18e-49e6-b876-0a39369dc0d8\"){id status}}"}' \
https://api.fleek.co/graphql https://api.fleek.co/graphql
- name: Check out ipfs-redirect - name: Update vega.trading DNS to redirect to the new console
uses: actions/checkout@v3
with:
repository: 'vegaprotocol/ipfs-redirect'
path: 'ipfs-redirect'
fetch-depth: '0'
token: ${{ secrets.VEGA_CI_BOT_GITHUB_TOKEN }}
- name: Update console.vega.xyz DNS to redirect to the new console
env:
GH_TOKEN: ${{ secrets.VEGA_CI_BOT_GITHUB_TOKEN }}
run: | run: |
curl -L https://dist.ipfs.tech/kubo/v0.20.0/kubo_v0.20.0_linux-amd64.tar.gz -o kubo.tgz 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 tar -xzf kubo.tgz
@@ -61,24 +51,27 @@ jobs:
which ipfs which ipfs
new_hash=$(cat ipfs-hash) new_hash=$(cat ipfs-hash)
new_cid=$(ipfs cid format -v 1 -b base32 $new_hash) new_cid=$(ipfs cid format -v 1 -b base32 $new_hash)
git config --global user.email "vega-ci-bot@vega.xyz"
git config --global user.name "vega-ci-bot"
echo $new_hash > ipfs-redirect/cidv0.txt
echo $new_cid > ipfs-redirect/cidv1.txt
( # Generate console URL
cd ipfs-redirect 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 # Update record in DNSimple
branch_name="rollback-to-$new_hash" # docs: https://developer.dnsimple.com/v2/zones/records/#updateZoneRecord
git checkout -b "$branch_name" dnsimple_account_id=84895
commit_msg="hash rollback to $new_hash" dnsimple_zone_name=vega.trading
git add cidv0.txt cidv1.txt dnsimple_record_id=44409591
git commit -m "$commit_msg" # see: https://dnsimple.com/a/84895/domains/vega.trading/records/44409591/edit
git push -u origin "$branch_name" --force-with-lease
pr_url="$(gh pr create --title "${commit_msg}" --body 'automated pull request to update CIDs')" curl -H 'Authorization: Bearer ${{ secrets.DNSIMPLE_API_TOKEN }}' \
echo $pr_url -H 'Accept: application/json' \
# once auto merge get's enabled on documentation repo let's do follow up -H 'Content-Type: application/json' \
sleep 5 -X PATCH \
gh pr merge "${pr_url}" --delete-branch --squash --admin -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 #cypress
/apps/**/cypress/reports/ /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_URL=http://localhost:26617
NX_TENDERMINT_WEBSOCKET_URL=wss://localhost:26617/websocket NX_TENDERMINT_WEBSOCKET_URL=wss://localhost:26617/websocket
NX_VEGA_URL=http://localhost:3008/graphql NX_VEGA_URL=http://localhost:3008/graphql
+1
View File
@@ -1,4 +1,5 @@
# App configuration variables # 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_URL=https://n04.d.vega.xyz/tm
NX_TENDERMINT_WEBSOCKET_URL=wss://n04.d.vega.xyz/tm/websocket NX_TENDERMINT_WEBSOCKET_URL=wss://n04.d.vega.xyz/tm/websocket
NX_VEGA_URL=https://api.n04.d.vega.xyz/graphql NX_VEGA_URL=https://api.n04.d.vega.xyz/graphql
+1
View File
@@ -1,4 +1,5 @@
# App configuration variables # 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_URL=https://mainnet-observer-proxy01.ops.vega.xyz/
NX_TENDERMINT_WEBSOCKET_URL=wss://mainnet-observer-proxy01.ops.vega.xyz/websocket NX_TENDERMINT_WEBSOCKET_URL=wss://mainnet-observer-proxy01.ops.vega.xyz/websocket
NX_VEGA_URL=https://api.vega.community/graphql NX_VEGA_URL=https://api.vega.community/graphql
+1
View File
@@ -1,4 +1,5 @@
# App configuration variables # 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_URL=https://tm.n07.testnet.vega.xyz/tm
NX_TENDERMINT_WEBSOCKET_URL=wss://lb.testnet.vega.xyz/tm/websocket NX_TENDERMINT_WEBSOCKET_URL=wss://lb.testnet.vega.xyz/tm/websocket
NX_VEGA_URL=https://api.n07.testnet.vega.xyz/graphql NX_VEGA_URL=https://api.n07.testnet.vega.xyz/graphql
+4 -6
View File
@@ -3,19 +3,17 @@ NX_ETHERSCAN_URL=https://sepolia.etherscan.io
NX_HOSTED_WALLET_URL=https://wallet.testnet.vega.xyz 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_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/networks-internal/main/stagnet1/vegawallet-stagnet1.toml
NX_VEGA_ENV=STAGNET1 NX_VEGA_ENV=STAGNET1
NX_VEGA_EXPLORER_URL=https://explorer.stagnet1.vega.rocks NX_VEGA_EXPLORER_URL=https://stagnet1.explorer.vega.xyz
NX_VEGA_TOKEN_URL=https://governance.stagnet1.vega.rocks NX_VEGA_TOKEN_URL=https://stagnet1.governance.vega.xyz
NX_VEGA_WALLET_URL=http://localhost:1789 NX_VEGA_WALLET_URL=http://localhost:1789
NX_TENDERMINT_URL=https://tm.n01.stagnet1.vega.xyz NX_TENDERMINT_URL=https://tm.n01.stagnet1.vega.xyz
NX_TENDERMINT_WEBSOCKET_URL=wss://tm.n01.stagnet1.vega.xyz/websocket NX_TENDERMINT_WEBSOCKET_URL=wss://tm.n01.stagnet1.vega.xyz/websocket
NX_BLOCK_EXPLORER=https://be.stagnet1.vega.xyz/rest NX_BLOCK_EXPLORER=https://be.stagnet1.vega.xyz/rest
NX_ETHERSCAN_URL=https://sepolia.etherscan.io NX_ETHERSCAN_URL=https://sepolia.etherscan.io
NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json
NX_VEGA_GOVERNANCE_URL=https://governance.stagnet1.vega.rocks NX_VEGA_GOVERNANCE_URL=https://stagnet1.governance.vega.xyz
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json 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_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 # App flags
NX_EXPLORER_ASSETS=1 NX_EXPLORER_ASSETS=1
+1 -3
View File
@@ -1,13 +1,11 @@
# App configuration variables # App configuration variables
NX_TENDERMINT_URL=https://be.vega.community NX_TENDERMINT_URL=https://be.vega.community
NX_TENDERMINT_WEBSOCKET_URL=wss://be.vega.community/websocket NX_TENDERMINT_WEBSOCKET_URL=wss://be.vega.community/websocket
NX_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_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/networks/master/mainnet1/mainnet1.toml
NX_VEGA_URL=https://api.vega.community/graphql NX_VEGA_URL=https://api.vega.community/graphql
NX_VEGA_ENV=MAINNET NX_VEGA_ENV=MAINNET
NX_BLOCK_EXPLORER=https://be.vega.community/rest NX_BLOCK_EXPLORER=https://be.vega.community/rest/
NX_ETHERSCAN_URL=https://etherscan.io NX_ETHERSCAN_URL=https://etherscan.io
NX_VEGA_GOVERNANCE_URL=https://governance.vega.xyz NX_VEGA_GOVERNANCE_URL=https://governance.vega.xyz
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/mainnet/announcements.json NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/mainnet/announcements.json
NX_VEGA_EXPLORER_URL=https://explorer.vega.xyz/ 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 # .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_VEGA_GOVERNANCE_URL=https://governance.fairground.wtf
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json
NX_VEGA_EXPLORER_URL=https://explorer.fairground.wtf 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_ETHERSCAN_URL=https://sepolia.etherscan.io
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json 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_BLOCK_EXPLORER=https://be.validators-testnet.vega.rocks/rest
NX_TENDERMINT_WEBSOCKET_URL=wss://be.validators-testnet.vega.xyz/websocket 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://validator-testnet.explorer.vega.xyz/
NX_VEGA_EXPLORER_URL=https://explorer.validators-testnet.vega.rocks/
NX_VEGA_CONSOLE_URL=https://trading.validators-testnet.vega.rocks/
+1 -1
View File
@@ -70,7 +70,7 @@
"executor": "@nrwl/workspace:run-commands", "executor": "@nrwl/workspace:run-commands",
"options": { "options": {
"commands": [ "commands": [
"npx openapi-typescript https://raw.githubusercontent.com/vegaprotocol/documentation/main/specs/v0.71.4/blockexplorer.openapi.json --output apps/explorer/src/types/explorer.d.ts --immutable-types" "npx openapi-typescript https://raw.githubusercontent.com/vegaprotocol/documentation/main/specs/v0.67.3/blockexplorer.openapi.json --output apps/explorer/src/types/explorer.d.ts --immutable-types"
] ]
} }
}, },
-3
View File
@@ -13,7 +13,6 @@ import { DEFAULT_CACHE_CONFIG } from '@vegaprotocol/apollo-client';
import { RouterProvider } from 'react-router-dom'; import { RouterProvider } from 'react-router-dom';
import { router } from './routes/router-config'; import { router } from './routes/router-config';
import { t } from '@vegaprotocol/i18n'; import { t } from '@vegaprotocol/i18n';
import { Suspense } from 'react';
const splashLoading = ( const splashLoading = (
<Splash> <Splash>
@@ -33,9 +32,7 @@ function App() {
skeleton={<div>{t('Loading')}</div>} skeleton={<div>{t('Loading')}</div>}
failure={<AppFailure title={t(`Node: ${VEGA_URL} is unsuitable`)} />} failure={<AppFailure title={t(`Node: ${VEGA_URL} is unsuitable`)} />}
> >
<Suspense fallback={splashLoading}>
<RouterProvider router={router} fallbackElement={splashLoading} /> <RouterProvider router={router} fallbackElement={splashLoading} />
</Suspense>
</NodeGuard> </NodeGuard>
<NodeSwitcherDialog <NodeSwitcherDialog
open={nodeSwitcherOpen} open={nodeSwitcherOpen}
@@ -1,13 +1,29 @@
import WS from 'jest-websocket-mock'; import WS from 'jest-websocket-mock';
import { render, screen } from '@testing-library/react'; import useWebSocket from 'react-use-websocket';
import {
render,
screen,
fireEvent,
act,
waitFor,
} from '@testing-library/react';
import { TendermintWebsocketContext } from '../../contexts/websocket/tendermint-websocket-context';
import { BlocksRefetch } from './blocks-refetch'; import { BlocksRefetch } from './blocks-refetch';
const BlocksRefetchInWebsocketProvider = ({ const BlocksRefetchInWebsocketProvider = ({
callback, callback,
mocketLocation,
}: { }: {
callback: () => null; callback: () => null;
mocketLocation: string;
}) => { }) => {
return <BlocksRefetch refetch={callback} />; const contextShape = useWebSocket(mocketLocation);
return (
<TendermintWebsocketContext.Provider value={{ ...contextShape }}>
<BlocksRefetch refetch={callback} />
</TendermintWebsocketContext.Provider>
);
}; };
describe('Blocks refetch', () => { describe('Blocks refetch', () => {
@@ -16,8 +32,111 @@ describe('Blocks refetch', () => {
const mocket = new WS(mocketLocation, { jsonProtocol: true }); const mocket = new WS(mocketLocation, { jsonProtocol: true });
new WebSocket(mocketLocation); new WebSocket(mocketLocation);
render(<BlocksRefetchInWebsocketProvider callback={() => null} />); render(
<BlocksRefetchInWebsocketProvider
callback={() => null}
mocketLocation={mocketLocation}
/>
);
await mocket.connected;
expect(screen.getByTestId('new-blocks')).toHaveTextContent('new blocks');
expect(screen.getByTestId('refresh')).toBeInTheDocument(); expect(screen.getByTestId('refresh')).toBeInTheDocument();
mocket.close(); mocket.close();
}); });
it('should initiate callback when the button is clicked', async () => {
const mocketLocation = 'wss:localhost:3003';
const mocket = new WS(mocketLocation, { jsonProtocol: true });
new WebSocket(mocketLocation);
const callback = jest.fn();
render(
<BlocksRefetchInWebsocketProvider
callback={callback}
mocketLocation={mocketLocation}
/>
);
await mocket.connected;
const button = screen.getByTestId('refresh');
act(() => {
fireEvent.click(button);
});
expect(callback.mock.calls.length).toEqual(1);
mocket.close();
});
it('should show new blocks as websocket is correctly updated', async () => {
const mocketLocation = 'wss:localhost:3004';
const mocket = new WS(mocketLocation, { jsonProtocol: true });
new WebSocket(mocketLocation);
render(
<BlocksRefetchInWebsocketProvider
callback={() => null}
mocketLocation={mocketLocation}
/>
);
await mocket.connected;
// Ensuring we send an ID equal to the one the client subscribed with.
await waitFor(() => expect(mocket.messages.length).toEqual(1));
// @ts-ignore id on messages
const id = mocket.messages[0].id;
const newBlockMessage = {
id,
result: {
query: "tm.event = 'NewBlock'",
},
};
expect(screen.getByTestId('new-blocks')).toHaveTextContent('0 new blocks');
act(() => {
mocket.send(newBlockMessage);
});
expect(screen.getByTestId('new-blocks')).toHaveTextContent('1 new blocks');
act(() => {
mocket.send(newBlockMessage);
});
expect(screen.getByTestId('new-blocks')).toHaveTextContent('2 new blocks');
mocket.close();
});
it('will not show new blocks if websocket has wrong ID', async () => {
const mocketLocation = 'wss:localhost:3005';
const mocket = new WS(mocketLocation, { jsonProtocol: true });
new WebSocket(mocketLocation);
render(
<BlocksRefetchInWebsocketProvider
callback={() => null}
mocketLocation={mocketLocation}
/>
);
await mocket.connected;
// Ensuring we send an ID equal to the one the client subscribed with.
await waitFor(() => expect(mocket.messages.length).toEqual(1));
const newBlockMessageBadId = {
id: 'blahblahblah',
result: {
query: "tm.event = 'NewBlock'",
},
};
expect(screen.getByTestId('new-blocks')).toHaveTextContent('0 new blocks');
act(() => {
mocket.send(newBlockMessageBadId);
});
expect(screen.getByTestId('new-blocks')).toHaveTextContent('0 new blocks');
mocket.close();
});
}); });
@@ -1,19 +1,36 @@
import { useState, useEffect } from 'react';
import { useTendermintWebsocket } from '../../hooks/use-tendermint-websocket';
import { t } from '@vegaprotocol/i18n'; import { t } from '@vegaprotocol/i18n';
import { Button, Icon } from '@vegaprotocol/ui-toolkit'; import { ButtonLink } from '@vegaprotocol/ui-toolkit';
interface BlocksRefetchProps { interface BlocksRefetchProps {
refetch: () => void; refetch: () => void;
} }
export const BlocksRefetch = ({ refetch }: BlocksRefetchProps) => { export const BlocksRefetch = ({ refetch }: BlocksRefetchProps) => {
const [blocksToLoad, setBlocksToLoad] = useState<number>(0);
const { messages } = useTendermintWebsocket({
query: "tm.event = 'NewBlock'",
});
useEffect(() => {
if (messages.length > 0) {
setBlocksToLoad((prev) => prev + 1);
}
}, [messages]);
const refresh = () => { const refresh = () => {
refetch(); refetch();
setBlocksToLoad(0);
}; };
return ( return (
<Button onClick={refresh} data-testid="refresh" size="xs"> <div className="mb-4">
<Icon name="refresh" className="!align-baseline mr-2" size={3} /> <span data-testid="new-blocks">{blocksToLoad} new blocks - </span>
{t('Load new')} <ButtonLink onClick={refresh} data-testid="refresh">
</Button> {t('refresh to see latest')}
</ButtonLink>
</div>
); );
}; };
@@ -4,11 +4,9 @@ import {
} from '@vegaprotocol/environment'; } from '@vegaprotocol/environment';
import { t } from '@vegaprotocol/i18n'; import { t } from '@vegaprotocol/i18n';
import { useScreenDimensions } from '@vegaprotocol/react-helpers'; import { useScreenDimensions } from '@vegaprotocol/react-helpers';
import { Link, ExternalLink } from '@vegaprotocol/ui-toolkit'; import { ExternalLink, Link } from '@vegaprotocol/ui-toolkit';
import { useMemo } from 'react'; import { useMemo } from 'react';
import { ENV } from '../../config/env'; import { ENV } from '../../config/env';
import { Routes } from '../../routes/route-names';
import { Link as RouteLink } from 'react-router-dom';
export const Footer = () => { export const Footer = () => {
const { VEGA_URL, GIT_COMMIT_HASH, GIT_ORIGIN_URL } = useEnvironment(); const { VEGA_URL, GIT_COMMIT_HASH, GIT_ORIGIN_URL } = useEnvironment();
@@ -23,7 +21,7 @@ export const Footer = () => {
); );
return ( 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"> <div className="flex justify-between gap-2 align-middle">
{GIT_COMMIT_HASH && ( {GIT_COMMIT_HASH && (
<div className="content-center flex border-r border-neutral-700 dark:border-neutral-300 pr-4"> <div className="content-center flex border-r border-neutral-700 dark:border-neutral-300 pr-4">
@@ -50,26 +48,23 @@ export const Footer = () => {
</Link> </Link>
</div> </div>
{ENV.addresses.feedback ? (
<div className="flex pl-2 content-center"> <div className="flex pl-2 content-center">
<ExternalLink href={ENV.addresses.feedback}> <ExternalLink href={ENV.addresses.feedback}>
{showFullFeedbackLabel ? t('Share your feedback') : t('Feedback')} {showFullFeedbackLabel ? t('Share your feedback') : t('Feedback')}
</ExternalLink> </ExternalLink>
</div> </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> </div>
</footer> </footer>
); );
}; };
export const NodeUrl = ({ url }: { url: string }) => { const NodeUrl = ({ url }: { url: string }) => {
// get base url from api url, api sub domain // get base url from api url, api sub domain
const urlObj = new URL(url); const urlObj = new URL(url);
const nodeUrl = urlObj.origin.replace(/^[^.]+\./g, ''); const nodeUrl = urlObj.origin.replace(/^[^.]+\./g, '');
return <span className="cursor-default">{nodeUrl}</span>; return (
<Link href={'https://' + nodeUrl} target="_blank">
{nodeUrl}
</Link>
);
}; };
@@ -1,6 +1,5 @@
import { t } from '@vegaprotocol/i18n'; import { t } from '@vegaprotocol/i18n';
import type { MarketInfoWithData } from '@vegaprotocol/markets'; import type { MarketInfoWithData } from '@vegaprotocol/markets';
import { PriceMonitoringBoundsInfoPanel } from '@vegaprotocol/markets';
import { import {
LiquidityInfoPanel, LiquidityInfoPanel,
LiquidityMonitoringParametersInfoPanel, LiquidityMonitoringParametersInfoPanel,
@@ -21,10 +20,12 @@ import isEqual from 'lodash/isEqual';
export const MarketDetails = ({ market }: { market: MarketInfoWithData }) => { export const MarketDetails = ({ market }: { market: MarketInfoWithData }) => {
if (!market) return null; if (!market) return null;
const settlementData = market.tradableInstrument.instrument.product const settlementData =
.dataSourceSpecForSettlementData.data as DataSourceDefinition; market.tradableInstrument.instrument.product.dataSourceSpecForSettlementData
const terminationData = market.tradableInstrument.instrument.product .data;
.dataSourceSpecForTradingTermination.data as DataSourceDefinition; const terminationData =
market.tradableInstrument.instrument.product
.dataSourceSpecForTradingTermination.data;
const getSigners = (data: DataSourceDefinition) => { const getSigners = (data: DataSourceDefinition) => {
if (data.sourceType.__typename === 'DataSourceDefinitionExternal') { if (data.sourceType.__typename === 'DataSourceDefinitionExternal') {
@@ -40,69 +41,133 @@ export const MarketDetails = ({ market }: { market: MarketInfoWithData }) => {
return []; return [];
}; };
const showTwoOracles = isEqual( const oraclePanels = isEqual(
getSigners(settlementData), getSigners(settlementData),
getSigners(terminationData) getSigners(terminationData)
); )
? [
const headerClassName = 'font-alpha calt text-xl mt-4 border-b-2 pb-2'; {
title: t('Settlement Oracle'),
return ( content: (
<div> <OracleInfoPanel
<h2 className={headerClassName}>{t('Key details')}</h2> noBorder={false}
<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) => (
<>
<h2 className={headerClassName}>
{t('Price monitoring bounds %s', [(i + 1).toString()])}
</h2>
<PriceMonitoringBoundsInfoPanel
market={market} market={market}
triggerIndex={i + 1} 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 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: (
<>
<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
}
/> />
</> </>
))} ),
{(market.priceMonitoringSettings?.parameters?.triggers || []).map( })),
(trigger, i) => ( {
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 (
<> <>
<h2 className={headerClassName}> {panels.map((p) => (
{t('Price monitoring settings %s', [(i + 1).toString()])} <div key={p.title} className="mb-3">
</h2> <h2 className="font-alpha calt text-xl">{p.title}</h2>
<MarketInfoTable data={trigger} key={i} /> {p.content}
</>
)
)}
<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> </div>
))}
</>
); );
}; };
@@ -77,24 +77,22 @@ export const MarketsTable = ({ data }: MarketsTableProps) => {
hide={window.innerWidth <= BREAKPOINT_MD} hide={window.innerWidth <= BREAKPOINT_MD}
valueGetter={({ valueGetter={({
data, data,
}: VegaValueGetterParams<MarketFieldsFragment>) => { }: VegaValueGetterParams<MarketFieldsFragment, 'state'>) => {
return data?.state ? MarketStateMapping[data?.state] : '-'; return data?.state ? MarketStateMapping[data?.state] : '-';
}} }}
/> />
<AgGridColumn <AgGridColumn
colId="asset" colId="asset"
headerName={t('Settlement asset')} headerName={t('Settlement asset')}
field="tradableInstrument.instrument.product.settlementAsset.symbol" field="tradableInstrument.instrument.product.settlementAsset"
hide={window.innerWidth <= BREAKPOINT_MD} hide={window.innerWidth <= BREAKPOINT_MD}
cellRenderer={({ cellRenderer={({
data, value,
}: VegaICellRendererParams< }: VegaICellRendererParams<
MarketFieldsFragment, MarketFieldsFragment,
'tradableInstrument.instrument.product.settlementAsset.symbol' 'tradableInstrument.instrument.product.settlementAsset'
>) => { >) =>
const value = value ? (
data?.tradableInstrument.instrument.product.settlementAsset;
return value ? (
<ButtonLink <ButtonLink
onClick={(e) => { onClick={(e) => {
openAssetDetailsDialog(value.id, e.target as HTMLElement); openAssetDetailsDialog(value.id, e.target as HTMLElement);
@@ -104,8 +102,8 @@ export const MarketsTable = ({ data }: MarketsTableProps) => {
</ButtonLink> </ButtonLink>
) : ( ) : (
'' ''
); )
}} }
/> />
<AgGridColumn <AgGridColumn
flex={2} flex={2}
@@ -5,13 +5,12 @@ export const ErrorCodes = new Map([
[51, 'Transaction failed validation'], [51, 'Transaction failed validation'],
[60, 'Transaction could not be decoded'], [60, 'Transaction could not be decoded'],
[70, 'Error'], [70, 'Error'],
[71, 'Partial success/error'],
[80, 'Unknown command'], [80, 'Unknown command'],
[89, 'Rejected as spam'], [89, 'Rejected as spam'],
[0, 'Success'], [0, 'Success'],
]); ]);
export const successCodes = new Set([0, 71]); export const successCodes = new Set([0]);
interface ChainResponseCodeProps { interface ChainResponseCodeProps {
code: number; code: number;
@@ -30,12 +29,11 @@ export const ChainResponseCode = ({
error, error,
}: ChainResponseCodeProps) => { }: ChainResponseCodeProps) => {
const isSuccess = successCodes.has(code); const isSuccess = successCodes.has(code);
const successColour =
code === 71 ? 'fill-vega-orange' : 'fill-vega-green-600';
const icon = isSuccess ? ( 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'; const label = ErrorCodes.get(code) || 'Unknown response code';
@@ -1,56 +0,0 @@
import type { Proposal } from './tx-proposal';
import { proposalRequiresSignatureBundle } from './tx-proposal';
describe('proposalRequiresSignatureBundle', () => {
it('should return false for freeform proposals, which do not require a signature bundle to enact', () => {
const mock = {
terms: {
newFreeform: {},
},
};
expect(proposalRequiresSignatureBundle(mock)).toEqual(false);
});
it('should return false for newMarket proposals, which do not require a signature bundle to enact', () => {
const mock = {
terms: {
newMarket: {},
},
};
expect(proposalRequiresSignatureBundle(mock)).toEqual(false);
});
it('should return true for newAsset proposals, which do require a signature bundle to enact', () => {
const mock = {
terms: {
newAsset: {},
},
};
expect(proposalRequiresSignatureBundle(mock)).toEqual(true);
});
it('should return true for updateAsset proposals, which do require a signature bundle to enact', () => {
const mock = {
terms: {
updateAsset: {},
},
};
expect(proposalRequiresSignatureBundle(mock)).toEqual(true);
});
it('should return false when bad data is supplied', () => {
expect(
proposalRequiresSignatureBundle(false as unknown as Proposal)
).toEqual(false);
expect(
proposalRequiresSignatureBundle(undefined as unknown as Proposal)
).toEqual(false);
expect(
proposalRequiresSignatureBundle({ test: false } as unknown as Proposal)
).toEqual(false);
});
});
@@ -28,16 +28,11 @@ interface TxProposalProps {
* @returns boolean True if a signature bundle is required. Used to fetch a signature bundle * @returns boolean True if a signature bundle is required. Used to fetch a signature bundle
*/ */
export function proposalRequiresSignatureBundle(proposal?: Proposal): boolean { export function proposalRequiresSignatureBundle(proposal?: Proposal): boolean {
const proposalsThatRequireBundles = ['newAsset', 'updateAsset'];
if (!proposal?.terms) { if (!proposal?.terms) {
return false; return false;
} }
return !!['newAsset', 'updateAsset'].filter((requiredIfExists) =>
return (
proposalsThatRequireBundles.filter((requiredIfExists) =>
has(proposal.terms, requiredIfExists) has(proposal.terms, requiredIfExists)
).length > 0
); );
} }
@@ -86,7 +81,6 @@ export const TxProposal = ({ txData, pubKey, blockData }: TxProposalProps) => {
const tx = proposal.terms?.newAsset || proposal.terms?.updateAsset; const tx = proposal.terms?.newAsset || proposal.terms?.updateAsset;
// This component is not rendered if no bundle is required
const SignatureBundleComponent = proposal.terms?.newAsset const SignatureBundleComponent = proposal.terms?.newAsset
? ProposalSignatureBundleNewAsset ? ProposalSignatureBundleNewAsset
: ProposalSignatureBundleUpdateAsset; : ProposalSignatureBundleUpdateAsset;
@@ -1,24 +0,0 @@
import { t } from '@vegaprotocol/i18n';
export interface FilterLabelProps {
filters: Set<string>;
}
/**
* Renders the list (currently limited to 1) of filters set by the
* Transaction Filter
*/
export function FilterLabel({ filters }: FilterLabelProps) {
if (!filters || filters.size !== 1) {
return <span className="uppercase">{t('Filter')}</span>;
}
return (
<div>
<span className="uppercase">{t('Filters')}:</span>&nbsp;
<code className="bg-vega-light-150 px-2 rounded-md capitalize">
{Array.from(filters)[0]}
</code>
</div>
);
}
@@ -1,164 +0,0 @@
import { t } from '@vegaprotocol/i18n';
import {
DropdownMenu,
DropdownMenuCheckboxItem,
DropdownMenuContent,
DropdownMenuItemIndicator,
DropdownMenuSeparator,
DropdownMenuSub,
DropdownMenuSubTrigger,
DropdownMenuTrigger,
DropdownMenuSubContent,
Icon,
Button,
} from '@vegaprotocol/ui-toolkit';
import type { Dispatch, SetStateAction } from 'react';
import { FilterLabel } from './tx-filter-label';
// All possible transaction types. Should be generated.
export type FilterOption =
| 'Amend LiquidityProvision Order'
| 'Amend Order'
| 'Batch Market Instructions'
| 'Cancel LiquidityProvision Order'
| 'Cancel Order'
| 'Cancel Transfer Funds'
| 'Chain Event'
| 'Delegate'
| 'Ethereum Key Rotate Submission'
| 'Issue Signatures'
| 'Key Rotate Submission'
| 'Liquidity Provision Order'
| 'Node Signature'
| 'Node Vote'
| 'Proposal'
| 'Protocol Upgrade'
| 'Register new Node'
| 'State Variable Proposal'
| 'Submit Oracle Data'
| 'Submit Order'
| 'Transfer Funds'
| 'Undelegate'
| 'Validator Heartbeat'
| 'Vote on Proposal'
| 'Withdraw';
// Alphabetised list of transaction types to appear at the top level
export const PrimaryFilterOptions: FilterOption[] = [
'Amend LiquidityProvision Order',
'Amend Order',
'Batch Market Instructions',
'Cancel LiquidityProvision Order',
'Cancel Order',
'Cancel Transfer Funds',
'Delegate',
'Liquidity Provision Order',
'Proposal',
'Submit Oracle Data',
'Submit Order',
'Transfer Funds',
'Undelegate',
'Vote on Proposal',
'Withdraw',
];
// Alphabetised list of transaction types to nest under a 'More...' submenu
export const SecondaryFilterOptions: FilterOption[] = [
'Chain Event',
'Ethereum Key Rotate Submission',
'Issue Signatures',
'Key Rotate Submission',
'Node Signature',
'Node Vote',
'Protocol Upgrade',
'Register new Node',
'State Variable Proposal',
'Validator Heartbeat',
];
export const AllFilterOptions: FilterOption[] = [
...PrimaryFilterOptions,
...SecondaryFilterOptions,
];
export interface TxFilterProps {
filters: Set<FilterOption>;
setFilters: Dispatch<SetStateAction<Set<FilterOption>>>;
}
/**
* Renders a structured dropdown menu of all of the available transaction
* types. It allows a user to select one transaction type to view. Later
* it will support multiple selection, but until the API supports that it is
* one or all.
* @param filters null or Set of tranaction types
* @param setFilters A function to update the filters prop
* @returns
*/
export const TxsFilter = ({ filters, setFilters }: TxFilterProps) => {
return (
<DropdownMenu
modal={false}
trigger={
<DropdownMenuTrigger className="ml-2">
<Button size="xs">
<FilterLabel filters={filters} />
</Button>
</DropdownMenuTrigger>
}
>
<DropdownMenuContent>
{filters.size > 1 ? null : (
<>
<DropdownMenuCheckboxItem
onCheckedChange={() => setFilters(new Set(AllFilterOptions))}
>
{t('Clear filters')} <Icon name="cross" />
</DropdownMenuCheckboxItem>
<DropdownMenuSeparator />
</>
)}
{PrimaryFilterOptions.map((f) => (
<DropdownMenuCheckboxItem
key={f}
checked={filters.has(f)}
onCheckedChange={() => {
// NOTE: These act like radio buttons until the API supports multiple filters
setFilters(new Set([f]));
}}
id={`radio-${f}`}
>
{f}
<DropdownMenuItemIndicator>
<Icon name="tick-circle" />
</DropdownMenuItemIndicator>
</DropdownMenuCheckboxItem>
))}
<DropdownMenuSub>
<DropdownMenuSubTrigger>
{t('More Types')}
<Icon name="chevron-right" />
</DropdownMenuSubTrigger>
<DropdownMenuSubContent>
{SecondaryFilterOptions.map((f) => (
<DropdownMenuCheckboxItem
key={f}
checked={filters.has(f)}
onCheckedChange={(checked) => {
// NOTE: These act like radio buttons until the API supports multiple filters
setFilters(new Set([f]));
}}
id={`radio-${f}`}
>
{f}
<DropdownMenuItemIndicator>
<Icon name="tick-circle" className="inline" />
</DropdownMenuItemIndicator>
</DropdownMenuCheckboxItem>
))}
</DropdownMenuSubContent>
</DropdownMenuSub>
</DropdownMenuContent>
</DropdownMenu>
);
};
@@ -160,7 +160,6 @@ export const TxOrderType = ({ orderType, command }: TxOrderTypeProps) => {
vote={command?.voteSubmission?.value === 'VALUE_YES'} vote={command?.voteSubmission?.value === 'VALUE_YES'}
yesText="Proposal vote" yesText="Proposal vote"
noText="Proposal vote" noText="Proposal vote"
useVoteColour={false}
/> />
); );
} }
@@ -1,4 +1,4 @@
import React, { useEffect, useRef } from 'react'; import React from 'react';
import { FixedSizeList as List } from 'react-window'; import { FixedSizeList as List } from 'react-window';
import InfiniteLoader from 'react-window-infinite-loader'; import InfiniteLoader from 'react-window-infinite-loader';
import { t } from '@vegaprotocol/i18n'; import { t } from '@vegaprotocol/i18n';
@@ -69,17 +69,6 @@ export const TxsInfiniteList = ({
}: TxsInfiniteListProps) => { }: TxsInfiniteListProps) => {
const { screenSize } = useScreenDimensions(); const { screenSize } = useScreenDimensions();
const isStacked = ['xs', 'sm'].includes(screenSize); const isStacked = ['xs', 'sm'].includes(screenSize);
const infiniteLoaderRef = useRef<InfiniteLoader>(null);
const hasMountedRef = useRef(false);
useEffect(() => {
if (hasMountedRef.current) {
if (infiniteLoaderRef.current) {
infiniteLoaderRef.current.resetloadMoreItemsCache(true);
}
}
hasMountedRef.current = true;
}, [loadMoreTxs]);
if (!txs) { if (!txs) {
if (!areTxsLoading) { if (!areTxsLoading) {
@@ -121,7 +110,6 @@ export const TxsInfiniteList = ({
isItemLoaded={isItemLoaded} isItemLoaded={isItemLoaded}
itemCount={itemCount} itemCount={itemCount}
loadMoreItems={loadMoreItems} loadMoreItems={loadMoreItems}
ref={infiniteLoaderRef}
> >
{({ onItemsRendered, ref }) => ( {({ onItemsRendered, ref }) => (
<List <List
@@ -34,17 +34,4 @@ describe('Vote TX icon', () => {
const no = render(<VoteIcon vote={false} />); const no = render(<VoteIcon vote={false} />);
expect(no.getByRole('img')).toHaveAttribute('aria-label', 'delete icon'); 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; yesText?: string;
// Defaults to 'Against', but can be any text // Defaults to 'Against', but can be any text
noText?: string; 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({ export function VoteIcon({
vote, vote,
useVoteColour = true,
yesText = 'For', yesText = 'For',
noText = 'Against', noText = 'Against',
}: VoteIconProps) { }: VoteIconProps) {
const label = vote ? yesText : noText; const label = vote ? yesText : noText;
const bg = vote ? 'bg-vega-green-550' : 'bg-vega-pink-550';
const icon: IconName = vote ? 'tick-circle' : 'delete'; const icon: IconName = vote ? 'tick-circle' : 'delete';
const bg = getBgColour(useVoteColour, vote); const fill = vote ? 'vega-green-300' : 'vega-pink-300';
const fill = getFillColour(useVoteColour, vote); const text = vote ? 'vega-green-200' : 'vega-pink-200';
const text = getTextColour(useVoteColour, vote);
return ( return (
<div <div
+1 -9
View File
@@ -33,7 +33,7 @@ export const getTxsDataUrl = ({ limit, filters }: IGetTxsDataUrl) => {
// Hacky fix for param as array // Hacky fix for param as array
let urlAsString = url.toString(); let urlAsString = url.toString();
if (filters) { if (filters) {
urlAsString += '&' + filters.replace(' ', '%20'); urlAsString += '&' + filters;
} }
return urlAsString; return urlAsString;
@@ -65,14 +65,6 @@ export const useTxsData = ({ limit, filters }: IUseTxsData) => {
} }
}, [setTxsState, data]); }, [setTxsState, data]);
useEffect(() => {
setTxsState((prev) => ({
txsData: [],
hasMoreTxs: true,
lastCursor: '',
}));
}, [filters]);
const loadTxs = useCallback(() => { const loadTxs = useCallback(() => {
return refetch({ return refetch({
limit: limit, limit: limit,
@@ -1,13 +1,12 @@
import { render } from '@testing-library/react'; import { render } from '@testing-library/react';
import { OracleDetailsType, isInternalSourceType } from './oracle-details-type'; import { OracleDetailsType } from './oracle-details-type';
import type { SourceType } from './oracle'; import type { SourceTypeName } from './oracle-details-type';
import { PropertyKeyType } from '@vegaprotocol/types';
function renderComponent(type: SourceType) { function renderComponent(type: SourceTypeName) {
return <OracleDetailsType sourceType={type} />; return <OracleDetailsType type={type} />;
} }
function renderWrappedComponent(type: SourceType) { function renderWrappedComponent(type: SourceTypeName) {
return ( return (
<table> <table>
<tbody>{renderComponent(type)}</tbody> <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', () => { describe('Oracle type view', () => {
it('Renders nothing when type is null', () => { 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(); expect(res.container).toBeEmptyDOMElement();
}); });
it('Renders Internal time for internal sources - timestamp', () => { it('Renders Internal time for internal sources', () => {
const s = mock('vegaprotocol.builtin.timestamp'); const res = render(renderWrappedComponent('DataSourceDefinitionInternal'));
expect(isInternalSourceType(s)).toEqual(true); expect(res.getByText('Internal time')).toBeInTheDocument();
const res = render(renderWrappedComponent(s));
expect(res.getByText('Internal data')).toBeInTheDocument();
});
it('Renders Internal time for internal sources - potential future types', () => {
const s = mock('vegaprotocol.builtin.boolean');
expect(isInternalSourceType(s)).toEqual(true);
const res = render(renderWrappedComponent(s));
expect(res.getByText('Internal data')).toBeInTheDocument();
});
it('Renders External data otherwise - prices.external.whatever', () => {
const s = mock('prices.external.whatever');
expect(isInternalSourceType(s)).toEqual(false);
const res = render(renderWrappedComponent(s));
expect(res.getByText('External data')).toBeInTheDocument();
}); });
it('Renders External data otherwise', () => { it('Renders External data otherwise', () => {
const s = mock('prices.external.vegaprotocol.builtin.'); const res = render(renderWrappedComponent('DataSourceDefinitionExternal'));
expect(isInternalSourceType(s)).toEqual(false);
const res = render(renderWrappedComponent(s));
expect(res.getByText('External data')).toBeInTheDocument(); expect(res.getByText('External data')).toBeInTheDocument();
}); });
}); });
@@ -1,51 +1,27 @@
import { TableRow, TableCell, TableHeader } from '../../../components/table'; import { TableRow, TableCell, TableHeader } from '../../../components/table';
import type { SourceType } from './oracle'; import type { SourceType } from './oracle';
/** export type SourceTypeName = SourceType['__typename'] | undefined;
* Basic function to determine if a source is internal or external.
*
* This should be distinguishable using __typename, but the type is incorrectly
* reported at the moment, so instead we check the filters.
*
* @param s SourceType
* @returns boolean True if the source is internal
*/
export function isInternalSourceType(s: SourceType) {
if ('filters' in s.sourceType) {
const filters = s.sourceType.filters;
if (filters) {
return (
filters?.filter((f) => {
return f.key.name?.indexOf('vegaprotocol.builtin.') === 0;
}).length > 0
);
}
}
return false;
}
interface OracleDetailsTypeProps { interface OracleDetailsTypeProps {
sourceType: SourceType; type: SourceTypeName;
} }
/** /**
* Renders a a single table row for the Oracle Details view that shows * 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 * if the oracle is using the internal time oracle or external data
*/ */
export function OracleDetailsType({ sourceType }: OracleDetailsTypeProps) { export function OracleDetailsType({ type }: OracleDetailsTypeProps) {
if (!sourceType) { if (!type) {
return null; return null;
} }
const isInternal = isInternalSourceType(sourceType);
return ( return (
<TableRow modifier="bordered"> <TableRow modifier="bordered">
<TableHeader scope="row">Type</TableHeader> <TableHeader scope="row">Type</TableHeader>
<TableCell modifier="bordered"> <TableCell modifier="bordered">
{isInternal ? 'Internal data' : 'External data'} {type === 'DataSourceDefinitionInternal'
? 'Internal time'
: 'External data'}
</TableCell> </TableCell>
</TableRow> </TableRow>
); );
@@ -53,7 +53,7 @@ export const OracleDetails = ({
<OracleLink id={id} /> <OracleLink id={id} />
</TableCell> </TableCell>
</TableRow> </TableRow>
<OracleDetailsType sourceType={sourceType} /> <OracleDetailsType type={sourceType.__typename} />
<OracleSigners sourceType={sourceType} /> <OracleSigners sourceType={sourceType} />
<OracleMarkets id={id} /> <OracleMarkets id={id} />
<TableRow modifier="bordered"> <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', MARKETS: 'markets',
ORACLES: 'oracles', ORACLES: 'oracles',
NETWORK_PARAMETERS: 'network-parameters', NETWORK_PARAMETERS: 'network-parameters',
DISCLAIMER: 'disclaimer',
}; };
@@ -29,7 +29,6 @@ import { AssetLink, MarketLink } from '../components/links';
import { truncateMiddle } from '@vegaprotocol/ui-toolkit'; import { truncateMiddle } from '@vegaprotocol/ui-toolkit';
import { remove0x } from '@vegaprotocol/utils'; import { remove0x } from '@vegaprotocol/utils';
import { PartyAccountsByAsset } from './parties/id/accounts'; import { PartyAccountsByAsset } from './parties/id/accounts';
import { Disclaimer } from './pages/disclaimer';
export type Navigable = { export type Navigable = {
path: string; 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, ...partiesRoutes,
...assetsRoutes, ...assetsRoutes,
...genesisRoutes, ...genesisRoutes,
@@ -5,43 +5,17 @@ import { TxsInfiniteList } from '../../../components/txs';
import { useTxsData } from '../../../hooks/use-txs-data'; import { useTxsData } from '../../../hooks/use-txs-data';
import { useDocumentTitle } from '../../../hooks/use-document-title'; import { useDocumentTitle } from '../../../hooks/use-document-title';
import { useState } from 'react'; const BE_TXS_PER_REQUEST = 20;
import { AllFilterOptions, TxsFilter } from '../../../components/txs/tx-filter';
const BE_TXS_PER_REQUEST = 15;
export const TxsList = () => { export const TxsList = () => {
useDocumentTitle(['Transactions']); useDocumentTitle(['Transactions']);
return (
<section className="md:p-2 lg:p-4 xl:p-6 relative">
<RouteTitle>{t('Transactions')}</RouteTitle>
<TxsListFiltered />
</section>
);
};
export const TxsListFiltered = () => {
const [filters, setFilters] = useState(new Set(AllFilterOptions));
const f =
filters && filters.size === 1
? `filters[cmd.type]=${Array.from(filters)[0]}`
: '';
const { hasMoreTxs, loadTxs, error, txsData, refreshTxs, loading } = const { hasMoreTxs, loadTxs, error, txsData, refreshTxs, loading } =
useTxsData({ useTxsData({ limit: BE_TXS_PER_REQUEST });
limit: BE_TXS_PER_REQUEST,
filters: f,
});
return ( return (
<> <section className="md:p-2 lg:p-4 xl:p-6">
<menu className="mb-2"> <RouteTitle>{t('Transactions')}</RouteTitle>
<BlocksRefetch refetch={refreshTxs} /> <BlocksRefetch refetch={refreshTxs} />
<TxsFilter filters={filters} setFilters={setFilters} />
</menu>
<TxsInfiniteList <TxsInfiniteList
hasMoreTxs={hasMoreTxs} hasMoreTxs={hasMoreTxs}
areTxsLoading={loading} areTxsLoading={loading}
@@ -50,6 +24,6 @@ export const TxsListFiltered = () => {
error={error} error={error}
className="mb-28" className="mb-28"
/> />
</> </section>
); );
}; };
+421 -428
View File
File diff suppressed because it is too large Load Diff
-1
View File
@@ -17,7 +17,6 @@ NX_TRANCHES_SERVICE_URL=https://tranches-stagnet1-k8s.ops.vega.xyz
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/test/announcements.json NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/test/announcements.json
NX_GITHUB_FEEDBACK_URL=https://github.com/vegaprotocol/feedback/discussions NX_GITHUB_FEEDBACK_URL=https://github.com/vegaprotocol/feedback/discussions
NX_WALLETCONNECT_PROJECT_ID=fe8091dc35738863e509fc4947525c72 NX_WALLETCONNECT_PROJECT_ID=fe8091dc35738863e509fc4947525c72
NX_VEGA_REST_URL=http://localhost:3008/api/v2/
#Test configuration variables #Test configuration variables
CYPRESS_FAIRGROUND=false CYPRESS_FAIRGROUND=false
+1 -2
View File
@@ -26,7 +26,6 @@ module.exports = defineConfig({
viewportWidth: 1440, viewportWidth: 1440,
viewportHeight: 900, viewportHeight: 900,
numTestsKeptInMemory: 5, numTestsKeptInMemory: 5,
downloadsFolder: 'cypress/downloads',
testIsolation: false, testIsolation: false,
}, },
env: { env: {
@@ -49,7 +48,7 @@ module.exports = defineConfig({
vegaTokenContractAddress: '0xF41bD86d462D36b997C0bbb4D97a0a3382f205B7', vegaTokenContractAddress: '0xF41bD86d462D36b997C0bbb4D97a0a3382f205B7',
vegaTokenAddress: '0x67175Da1D5e966e40D11c4B2519392B2058373de', vegaTokenAddress: '0x67175Da1D5e966e40D11c4B2519392B2058373de',
txTimeout: { timeout: 70000 }, txTimeout: { timeout: 70000 },
epochTimeout: { timeout: 12000 }, epochTimeout: { timeout: 6000 },
blockConfirmations: 3, blockConfirmations: 3,
grepTags: '@regression @smoke @slow', grepTags: '@regression @smoke @slow',
grepFilterSpecs: true, grepFilterSpecs: true,
@@ -1,63 +0,0 @@
export const previousEpochData = {
epoch: {
id: '7611',
validatorsConnection: {
edges: [
{
node: {
id: 'cd96782bc0ad5679869cf69fe7838a92212da7f53b4a214bed68067117494122',
stakedTotal: '3154229668720612941799',
rewardScore: {
rawValidatorScore: '0.2',
performanceScore: '1',
multisigScore: '0',
validatorScore: '0.2',
normalisedScore: '0.2007216887087119',
validatorStatus: 'VALIDATOR_NODE_STATUS_TENDERMINT',
__typename: 'RewardScore',
},
rankingScore: {
status: 'VALIDATOR_NODE_STATUS_TENDERMINT',
previousStatus: 'VALIDATOR_NODE_STATUS_TENDERMINT',
rankingScore: '0.211713765544955625',
stakeScore: '0.2016321576618625',
performanceScore: '1',
votingPower: '2007',
__typename: 'RankingScore',
},
__typename: 'Node',
},
__typename: 'NodeEdge',
},
{
node: {
id: '887d936f797a47032eceb572a13b69b581d3b1fa595a7d021a3e3cf2a5d2acfd',
stakedTotal: '3151161904761904764551',
rewardScore: {
rawValidatorScore: '0.2',
performanceScore: '1',
multisigScore: '1',
validatorScore: '0.2',
normalisedScore: '0.2007216887087119',
validatorStatus: 'VALIDATOR_NODE_STATUS_TENDERMINT',
__typename: 'RewardScore',
},
rankingScore: {
status: 'VALIDATOR_NODE_STATUS_TENDERMINT',
previousStatus: 'VALIDATOR_NODE_STATUS_TENDERMINT',
rankingScore: '0.211507855409133255',
stakeScore: '0.2014360527706031',
performanceScore: '1',
votingPower: '2007',
__typename: 'RankingScore',
},
__typename: 'Node',
},
__typename: 'NodeEdge',
},
],
__typename: 'NodesConnection',
},
__typename: 'Epoch',
},
};
@@ -1,113 +0,0 @@
{
"rationale": {
"title": "New Market Proposal E2E submission",
"description": "E2E new market proposal"
},
"terms": {
"newMarket": {
"changes": {
"decimalPlaces": "5",
"positionDecimalPlaces": "5",
"linearSlippageFactor": "0.001",
"quadraticSlippageFactor": "0",
"lpPriceRange": "10",
"instrument": {
"name": "Token test market",
"code": "TEST.24h",
"future": {
"settlementAsset": "73174a6fb1d5802ba0ac7bd7ab79e0a3a4837b262de0a4e80815a55442692bd0",
"quoteName": "fBTC",
"dataSourceSpecForSettlementData": {
"external": {
"oracle": {
"signers": [
{
"pubKey": {
"key": "70d14a321e02e71992fd115563df765000ccc4775cbe71a0e2f9ff5a3b9dc680"
}
}
],
"filters": [
{
"key": {
"name": "prices.ETH.value",
"type": "TYPE_INTEGER",
"numberDecimalPlaces": "0"
},
"conditions": [
{
"operator": "OPERATOR_GREATER_THAN",
"value": "0"
}
]
}
]
}
}
},
"dataSourceSpecForTradingTermination": {
"external": {
"oracle": {
"signers": [
{
"pubKey": {
"key": "70d14a321e02e71992fd115563df765000ccc4775cbe71a0e2f9ff5a3b9dc680"
}
}
],
"filters": [
{
"key": {
"name": "trading.terminated.ETH5",
"type": "TYPE_BOOLEAN"
},
"conditions": [
{
"operator": "OPERATOR_EQUALS",
"value": "true"
}
]
}
]
}
}
},
"dataSourceSpecBinding": {
"settlementDataProperty": "prices.ETH.value",
"tradingTerminationProperty": "trading.terminated.ETH5"
}
}
},
"metadata": ["sector:energy", "sector:tech", "source:docs.vega.xyz"],
"priceMonitoringParameters": {
"triggers": [
{
"horizon": "43200",
"probability": "0.9999999",
"auctionExtension": "600"
}
]
},
"liquidityMonitoringParameters": {
"targetStakeParameters": {
"timeWindow": "3600",
"scalingFactor": 10
},
"triggeringRatio": "0.7",
"auctionExtension": "1"
},
"logNormal": {
"tau": 0.0001140771161,
"riskAversionParameter": 0.01,
"params": {
"mu": 0,
"r": 0.016,
"sigma": 0.5
}
}
}
},
"closingTimestamp": 0,
"enactmentTimestamp": 0
}
}
@@ -7,23 +7,21 @@ import {
import { import {
createRawProposal, createRawProposal,
createTenDigitUnixTimeStampForSpecifiedDays, createTenDigitUnixTimeStampForSpecifiedDays,
enterUniqueFreeFormProposalBody,
generateFreeFormProposalTitle, generateFreeFormProposalTitle,
getDateFormatForSpecifiedDays, getDateFormatForSpecifiedDays,
getProposalFromTitle, getProposalIdFromList,
getProposalInformationFromTable, getProposalInformationFromTable,
submitUniqueRawProposal, getSubmittedProposalFromProposalList,
goToMakeNewProposal,
governanceProposalType,
voteForProposal, voteForProposal,
waitForProposalSubmitted,
waitForProposalSync,
} from '../../../../governance-e2e/src/support/governance.functions'; } from '../../../../governance-e2e/src/support/governance.functions';
import { import { ensureSpecifiedUnstakedTokensAreAssociated } from '../../../../governance-e2e/src/support/staking.functions';
ensureSpecifiedUnstakedTokensAreAssociated,
stakingPageAssociateTokens,
stakingPageDisassociateAllTokens,
} from '../../../../governance-e2e/src/support/staking.functions';
import { ethereumWalletConnect } from '../../../../governance-e2e/src/support/wallet-eth.functions'; import { ethereumWalletConnect } from '../../../../governance-e2e/src/support/wallet-eth.functions';
import { import { vegaWalletSetSpecifiedApprovalAmount } from '../../../../governance-e2e/src/support/wallet-teardown.functions';
switchVegaWalletPubKey,
vegaWalletSetSpecifiedApprovalAmount,
} from '../../support/wallet-functions';
import type { testFreeformProposal } from '../../support/common-interfaces'; import type { testFreeformProposal } from '../../support/common-interfaces';
import { formatDateWithLocalTimezone } from '@vegaprotocol/utils'; import { formatDateWithLocalTimezone } from '@vegaprotocol/utils';
@@ -40,9 +38,6 @@ const proposalDetailsTitle = '[data-testid="proposal-title"]';
const proposalDetailsDescription = '[data-testid="proposal-description"]'; const proposalDetailsDescription = '[data-testid="proposal-description"]';
const openProposals = '[data-testid="open-proposals"]'; const openProposals = '[data-testid="open-proposals"]';
const viewProposalButton = '[data-testid="view-proposal-btn"]'; const viewProposalButton = '[data-testid="view-proposal-btn"]';
const proposalDescriptionToggle = 'proposal-description-toggle';
const voteBreakdownToggle = 'vote-breakdown-toggle';
const proposalTermsToggle = 'proposal-json-toggle';
describe( describe(
'Governance flow for proposal details', 'Governance flow for proposal details',
@@ -51,7 +46,7 @@ describe(
before('connect wallets and set approval limit', function () { before('connect wallets and set approval limit', function () {
cy.visit('/'); cy.visit('/');
ethereumWalletConnect(); ethereumWalletConnect();
// cy.associateTokensToVegaWallet('1'); cy.associateTokensToVegaWallet('1');
}); });
beforeEach('visit proposals tab', function () { beforeEach('visit proposals tab', function () {
@@ -67,69 +62,61 @@ describe(
// 3001-VOTE-050 3001-VOTE-054 3001-VOTE-055 3002-PROP-019 // 3001-VOTE-050 3001-VOTE-054 3001-VOTE-055 3002-PROP-019
it('Newly created raw proposal details - shows proposal title and full description', function () { it('Newly created raw proposal details - shows proposal title and full description', function () {
const proposalDescription =
'I propose that everyone evaluate the following IPFS document and vote Yes if they agree. bafybeigwwctpv37xdcwacqxvekr6e4kaemqsrv34em6glkbiceo3fcy4si';
createRawProposal(); createRawProposal();
cy.get<testFreeformProposal>('@rawProposal').then((rawProposal) => { cy.get<testFreeformProposal>('@rawProposal').then((rawProposal) => {
getProposalIdFromList(rawProposal.rationale.title);
cy.get('@proposalIdText').then((proposalId) => {
cy.get(openProposals).within(() => { cy.get(openProposals).within(() => {
getProposalFromTitle(rawProposal.rationale.title).within(() => { cy.get(`#${proposalId}`).within(() => {
cy.get(viewProposalButton).should('be.visible').click(); cy.get(viewProposalButton).should('be.visible').click();
}); });
}); });
cy.get(proposalDetailsTitle).should(
'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 cy.get(proposalDetailsTitle)
getProposalInformationFromTable('ID') .should('contain', rawProposal.rationale.title)
.invoke('text') .and('be.visible');
.should('not.be.empty') cy.get(proposalDetailsDescription)
.and('have.length', 64); .should('contain', rawProposal.rationale.description)
// 3001-VOTE-009 .and('be.visible');
getProposalInformationFromTable('Proposed by') });
.invoke('text') // 3001-VOTE-052
.should('not.be.empty')
.and('have.length', 64);
cy.getByTestId(proposalTermsToggle).click();
// 3001-VOTE-052 3001-VOTE-010
cy.get('code.language-json') cy.get('code.language-json')
.should('exist') .should('exist')
.within(() => { .within(() => {
cy.get('.hljs-attr').eq(0).should('have.text', '"id"'); cy.get('.hljs-string').eq(0).should('have.text', '"ProposalTerms"');
}); });
}); });
// 3001-VOTE-043 // 3001-VOTE-043
it('Newly created freeform proposal details - shows proposed and closing dates', function () { it('Newly created freeform proposal details - shows proposed and closing dates', function () {
const closingVoteHrs = '72';
const proposalTitle = generateFreeFormProposalTitle(); const proposalTitle = generateFreeFormProposalTitle();
const proposalTimeStamp = createTenDigitUnixTimeStampForSpecifiedDays(3); const proposalTimeStamp = createTenDigitUnixTimeStampForSpecifiedDays(3);
submitUniqueRawProposal({ goToMakeNewProposal(governanceProposalType.FREEFORM);
proposalTitle: proposalTitle, enterUniqueFreeFormProposalBody(closingVoteHrs, proposalTitle);
closingTimestamp: proposalTimeStamp, waitForProposalSubmitted();
}); waitForProposalSync();
navigateTo(navigation.proposals); navigateTo(navigation.proposals);
getProposalFromTitle(proposalTitle).within(() => getSubmittedProposalFromProposalList(proposalTitle).within(() =>
cy.get(viewProposalButton).click() cy.get(viewProposalButton).click()
); );
cy.wrap( cy.wrap(
formatDateWithLocalTimezone(new Date(proposalTimeStamp * 1000)) formatDateWithLocalTimezone(new Date(proposalTimeStamp * 1000))
).then((closingDate) => { ).then((closingDate) => {
getProposalInformationFromTable('Closes on').should( getProposalInformationFromTable('Closes on')
'have.text', .contains(closingDate)
closingDate .should('be.visible');
);
}); });
cy.wrap(
formatDateWithLocalTimezone(
new Date(createTenDigitUnixTimeStampForSpecifiedDays(0) * 1000)
)
).then((proposalDate) => {
getProposalInformationFromTable('Proposed on') getProposalInformationFromTable('Proposed on')
.invoke('text') .contains(proposalDate)
.should('not.be.empty'); .should('be.visible');
});
}); });
it('Newly created proposal details - shows default status set to fail', function () { it('Newly created proposal details - shows default status set to fail', function () {
@@ -138,14 +125,13 @@ describe(
// 3001-VOTE-067 // 3001-VOTE-067
createRawProposal(); createRawProposal();
cy.get<testFreeformProposal>('@rawProposal').then((rawProposal) => { cy.get<testFreeformProposal>('@rawProposal').then((rawProposal) => {
getProposalFromTitle(rawProposal.rationale.title).within(() => getSubmittedProposalFromProposalList(
cy.get(viewProposalButton).click() rawProposal.rationale.title
); ).within(() => cy.get(viewProposalButton).click());
}); });
cy.contains('Participation: Not Met 0.00 0.00%(0.00% Required)').should( cy.contains('Participation: Not Met 0.00 0.00%(0.00% Required)').should(
'be.visible' 'be.visible'
); );
cy.getByTestId(voteBreakdownToggle).click();
getProposalInformationFromTable('Expected to pass') getProposalInformationFromTable('Expected to pass')
.contains('👎') .contains('👎')
.should('be.visible'); .should('be.visible');
@@ -165,9 +151,9 @@ describe(
it('Newly created proposal details - ability to vote for and against proposal - with minimum required tokens associated', function () { it('Newly created proposal details - ability to vote for and against proposal - with minimum required tokens associated', function () {
createRawProposal(); createRawProposal();
cy.get<testFreeformProposal>('@rawProposal').then((rawProposal) => { cy.get<testFreeformProposal>('@rawProposal').then((rawProposal) => {
getProposalFromTitle(rawProposal.rationale.title).within(() => getSubmittedProposalFromProposalList(
cy.get(viewProposalButton).click() rawProposal.rationale.title
); ).within(() => cy.get(viewProposalButton).click());
}); });
// 3001-VOTE-080 // 3001-VOTE-080
cy.getByTestId('vote-buttons').contains('against').should('be.visible'); cy.getByTestId('vote-buttons').contains('against').should('be.visible');
@@ -193,7 +179,6 @@ describe(
cy.get(proposalVoteProgressAgainstTokens) cy.get(proposalVoteProgressAgainstTokens)
.contains('0.00') .contains('0.00')
.and('be.visible'); .and('be.visible');
cy.getByTestId(voteBreakdownToggle).click();
getProposalInformationFromTable('Tokens for proposal') getProposalInformationFromTable('Tokens for proposal')
.should('have.text', (1).toFixed(2)) .should('have.text', (1).toFixed(2))
.and('be.visible'); .and('be.visible');
@@ -235,15 +220,14 @@ describe(
vegaWalletSetSpecifiedApprovalAmount('1000'); vegaWalletSetSpecifiedApprovalAmount('1000');
createRawProposal(); createRawProposal();
cy.get<testFreeformProposal>('@rawProposal').then((rawProposal) => { cy.get<testFreeformProposal>('@rawProposal').then((rawProposal) => {
getProposalFromTitle(rawProposal.rationale.title).within(() => getSubmittedProposalFromProposalList(
cy.get(viewProposalButton).click() rawProposal.rationale.title
); ).within(() => cy.get(viewProposalButton).click());
}); });
voteForProposal('for'); voteForProposal('for');
// 3001-VOTE-079 // 3001-VOTE-079
cy.contains('You voted: For').should('be.visible'); cy.contains('You voted: For').should('be.visible');
cy.get(proposalVoteProgressForTokens).contains('1').and('be.visible'); cy.get(proposalVoteProgressForTokens).contains('1').and('be.visible');
cy.getByTestId(voteBreakdownToggle).click();
getProposalInformationFromTable('Total Supply') getProposalInformationFromTable('Total Supply')
.invoke('text') .invoke('text')
.then((totalSupply) => { .then((totalSupply) => {
@@ -251,15 +235,14 @@ describe(
(Number(totalSupply.replace(/,/g, '')) * 0.001) / (Number(totalSupply.replace(/,/g, '')) * 0.001) /
100 100
).toFixed(2); ).toFixed(2);
ethereumWalletConnect();
ensureSpecifiedUnstakedTokensAreAssociated( ensureSpecifiedUnstakedTokensAreAssociated(
tokensRequiredToAchieveResult tokensRequiredToAchieveResult
); );
navigateTo(navigation.proposals); navigateTo(navigation.proposals);
cy.get<testFreeformProposal>('@rawProposal').then((rawProposal) => { cy.get<testFreeformProposal>('@rawProposal').then((rawProposal) => {
getProposalFromTitle(rawProposal.rationale.title).within(() => getSubmittedProposalFromProposalList(
cy.get(viewProposalButton).click() rawProposal.rationale.title
); ).within(() => cy.get(viewProposalButton).click());
}); });
cy.get(proposalVoteProgressForPercentage) cy.get(proposalVoteProgressForPercentage)
.contains('100.00%') .contains('100.00%')
@@ -276,7 +259,6 @@ describe(
cy.get(proposalVoteProgressAgainstTokens) cy.get(proposalVoteProgressAgainstTokens)
.contains('0.00') .contains('0.00')
.and('be.visible'); .and('be.visible');
cy.getByTestId(voteBreakdownToggle).click();
getProposalInformationFromTable('Total tokens voted percentage') getProposalInformationFromTable('Total tokens voted percentage')
.should('have.text', '0.00%') .should('have.text', '0.00%')
.and('be.visible'); .and('be.visible');
@@ -304,33 +286,5 @@ describe(
.and('be.visible'); .and('be.visible');
}); });
}); });
it('Able to vote for proposal twice by switching public key', function () {
ensureSpecifiedUnstakedTokensAreAssociated('1');
createRawProposal();
cy.get<testFreeformProposal>('@rawProposal').then((rawProposal) => {
getProposalFromTitle(rawProposal.rationale.title).within(() =>
cy.get(viewProposalButton).click()
);
voteForProposal('for');
cy.contains('You voted: For').should('be.visible');
ethereumWalletConnect();
switchVegaWalletPubKey();
stakingPageAssociateTokens('2');
navigateTo(navigation.proposals);
getProposalFromTitle(rawProposal.rationale.title).within(() =>
cy.get(viewProposalButton).click()
);
cy.getByTestId('you-voted').should('not.exist');
voteForProposal('against');
cy.contains('You voted: Against').should('be.visible');
switchVegaWalletPubKey();
cy.get(proposalVoteProgressForTokens).should('contain.text', '1.00');
// Checking vote status for different public keys is displayed correctly
cy.contains('You voted: For').should('be.visible');
});
switchVegaWalletPubKey();
stakingPageDisassociateAllTokens();
});
} }
); );
@@ -14,9 +14,8 @@ import {
createUpdateNetworkProposalTxBody, createUpdateNetworkProposalTxBody,
createFreeFormProposalTxBody, createFreeFormProposalTxBody,
} from '../../support/proposal.functions'; } from '../../support/proposal.functions';
import { ensureSpecifiedUnstakedTokensAreAssociated } from '../../support/staking.functions';
import { ethereumWalletConnect } from '../../support/wallet-eth.functions'; import { ethereumWalletConnect } from '../../support/wallet-eth.functions';
import { vegaWalletSetSpecifiedApprovalAmount } from '../../support/wallet-functions'; import { vegaWalletSetSpecifiedApprovalAmount } from '../../support/wallet-teardown.functions';
const proposalListItem = '[data-testid="proposals-list-item"]'; const proposalListItem = '[data-testid="proposals-list-item"]';
const closedProposals = '[data-testid="closed-proposals"]'; const closedProposals = '[data-testid="closed-proposals"]';
@@ -44,7 +43,6 @@ context(
waitForSpinner(); waitForSpinner();
cy.connectVegaWallet(); cy.connectVegaWallet();
ethereumWalletConnect(); ethereumWalletConnect();
ensureSpecifiedUnstakedTokensAreAssociated('1');
navigateTo(navigation.proposals); navigateTo(navigation.proposals);
}); });
@@ -65,7 +63,9 @@ context(
}); });
}); });
cy.getByTestId('proposal-type').should('have.text', 'New market'); cy.getByTestId('proposal-type').should('have.text', 'New market');
cy.get(proposalStatus).should('have.text', 'Enacted'); getProposalInformationFromTable('State')
.contains('Enacted')
.and('be.visible');
cy.get(votesTable).within(() => { cy.get(votesTable).within(() => {
cy.contains('Vote passed.').should('be.visible'); cy.contains('Vote passed.').should('be.visible');
cy.contains('Voting has ended.').should('be.visible'); cy.contains('Voting has ended.').should('be.visible');
@@ -87,16 +87,16 @@ context(
.last() .last()
.within(() => cy.get(viewProposalButton).click()); .within(() => cy.get(viewProposalButton).click());
}); });
cy.get(proposalStatus).should('have.text', 'Open'); getProposalInformationFromTable('State')
.contains('Open')
.and('be.visible');
voteForProposal('for'); voteForProposal('for');
cy.get(proposalStatus, proposalTimeout) getProposalInformationFromTable('State') // 3001-VOTE-047
.should('have.text', 'Passed') .contains('Passed', proposalTimeout)
.then(() => { .and('be.visible');
cy.get(proposalStatus, proposalTimeout).should( getProposalInformationFromTable('State')
'have.text', .contains('Enacted', proposalTimeout)
'Enacted' .and('be.visible');
);
});
cy.get(votesTable).within(() => { cy.get(votesTable).within(() => {
cy.contains('Vote passed.').should('be.visible'); cy.contains('Vote passed.').should('be.visible');
cy.contains('Voting has ended.').should('be.visible'); cy.contains('Voting has ended.').should('be.visible');
@@ -121,9 +121,13 @@ context(
.last() .last()
.within(() => cy.get(viewProposalButton).click()); .within(() => cy.get(viewProposalButton).click());
}); });
cy.get(proposalStatus).should('have.text', 'Open'); getProposalInformationFromTable('State')
.contains('Open')
.and('be.visible');
voteForProposal('for'); voteForProposal('for');
cy.get(proposalStatus, proposalTimeout).should('have.text', 'Enacted'); getProposalInformationFromTable('State')
.contains('Enacted', proposalTimeout)
.and('be.visible');
}); });
// 3001-VOTE-048 3001-VOTE-049 3001-VOTE-050 // 3001-VOTE-048 3001-VOTE-049 3001-VOTE-050
@@ -140,8 +144,12 @@ context(
.last() .last()
.within(() => cy.get(viewProposalButton).click()); .within(() => cy.get(viewProposalButton).click());
}); });
cy.get(proposalStatus).should('have.text', 'Open'); getProposalInformationFromTable('State')
cy.get(proposalStatus, proposalTimeout).should('have.text', 'Declined'); .contains('Open')
.and('be.visible');
getProposalInformationFromTable('State') // 3001-VOTE-047
.contains('Declined', proposalTimeout)
.and('be.visible');
getProposalInformationFromTable('Rejection reason') getProposalInformationFromTable('Rejection reason')
.contains('PROPOSAL_ERROR_PARTICIPATION_THRESHOLD_NOT_REACHED') .contains('PROPOSAL_ERROR_PARTICIPATION_THRESHOLD_NOT_REACHED')
.and('be.visible'); .and('be.visible');
@@ -5,12 +5,12 @@ import {
enterRawProposalBody, enterRawProposalBody,
enterUniqueFreeFormProposalBody, enterUniqueFreeFormProposalBody,
generateFreeFormProposalTitle, generateFreeFormProposalTitle,
getProposalFromTitle,
getProposalInformationFromTable, getProposalInformationFromTable,
getSubmittedProposalFromProposalList,
goToMakeNewProposal, goToMakeNewProposal,
governanceProposalType, governanceProposalType,
submitUniqueRawProposal,
voteForProposal, voteForProposal,
waitForProposalSubmitted,
waitForProposalSync, waitForProposalSync,
} from '../../support/governance.functions'; } from '../../support/governance.functions';
@@ -32,7 +32,7 @@ import {
import { import {
vegaWalletSetSpecifiedApprovalAmount, vegaWalletSetSpecifiedApprovalAmount,
vegaWalletTeardown, vegaWalletTeardown,
} from '../../support/wallet-functions'; } from '../../support/wallet-teardown.functions';
import { ethereumWalletConnect } from '../../support/wallet-eth.functions'; import { ethereumWalletConnect } from '../../support/wallet-eth.functions';
import type { testFreeformProposal } from '../../support/common-interfaces'; import type { testFreeformProposal } from '../../support/common-interfaces';
@@ -45,7 +45,11 @@ const connectToVegaWalletButton = '[data-testid="connect-to-vega-wallet-btn"]';
const newProposalSubmitButton = '[data-testid="proposal-submit"]'; const newProposalSubmitButton = '[data-testid="proposal-submit"]';
const viewProposalButton = '[data-testid="view-proposal-btn"]'; const viewProposalButton = '[data-testid="view-proposal-btn"]';
const rawProposalData = '[data-testid="proposal-data"]'; 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 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 rejectProposalsLink = '[href="/proposals/rejected"]';
const feedbackError = '[data-testid="Error"]'; const feedbackError = '[data-testid="Error"]';
const noOpenProposals = '[data-testid="no-open-proposals"]'; const noOpenProposals = '[data-testid="no-open-proposals"]';
@@ -77,6 +81,7 @@ context(
}); });
vegaWalletSetSpecifiedApprovalAmount('1000'); vegaWalletSetSpecifiedApprovalAmount('1000');
cy.associateTokensToVegaWallet('1');
}); });
beforeEach('visit governance tab', function () { beforeEach('visit governance tab', function () {
@@ -90,8 +95,7 @@ context(
navigateTo(navigation.proposals); navigateTo(navigation.proposals);
}); });
// Test can only pass if run before other proposal tests. it('Should be able to see that no proposals exist', function () {
it.skip('Should be able to see that no proposals exist', function () {
// 3001-VOTE-003 // 3001-VOTE-003
cy.get(noOpenProposals) cy.get(noOpenProposals)
.should('be.visible') .should('be.visible')
@@ -101,8 +105,9 @@ context(
.and('have.text', 'There are no enacted or rejected proposals'); .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
it('Proposal form - shows how many vega tokens are required to make a proposal', function () { // 3002-PROP-003
it('Submit a proposal form - shows how many vega tokens are required to make a proposal', function () {
// 3002-PROP-005 // 3002-PROP-005
goToMakeNewProposal(governanceProposalType.NEW_MARKET); goToMakeNewProposal(governanceProposalType.NEW_MARKET);
cy.contains( cy.contains(
@@ -110,14 +115,22 @@ context(
).should('be.visible'); ).should('be.visible');
}); });
// 3002-PROP-011 3008-PFRO-005 // 3002-PROP-011
it('Able to submit a valid freeform proposal - with minimum required tokens associated', function () { it('Able to submit a valid freeform proposal - with minimum required tokens associated', function () {
ensureSpecifiedUnstakedTokensAreAssociated('1'); goToMakeNewProposal(governanceProposalType.FREEFORM);
verifyUnstakedBalance(1); cy.get(minVoteButton).should('be.visible'); // 3002-PROP-008
createRawProposal(); 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 () { it('Able to submit a valid freeform proposal - with minimum required tokens associated - but also staked', function () {
ensureSpecifiedUnstakedTokensAreAssociated('2'); ensureSpecifiedUnstakedTokensAreAssociated('2');
verifyUnstakedBalance(2); verifyUnstakedBalance(2);
@@ -127,12 +140,19 @@ context(
closeStakingDialog(); closeStakingDialog();
cy.get(vegaWalletStakedBalances, txTimeout).should('contain', '2'); cy.get(vegaWalletStakedBalances, txTimeout).should('contain', '2');
createRawProposal();
navigateTo(navigation.proposals);
goToMakeNewProposal(governanceProposalType.FREEFORM);
enterUniqueFreeFormProposalBody('50', generateFreeFormProposalTitle());
waitForProposalSubmitted();
}); });
it('Creating a proposal - proposal rejected - when closing time sooner than system default', function () { it('Creating a proposal - proposal rejected - when closing time sooner than system default', function () {
goToMakeNewProposal(governanceProposalType.FREEFORM); goToMakeNewProposal(governanceProposalType.FREEFORM);
enterUniqueFreeFormProposalBody('0.1', generateFreeFormProposalTitle()); enterUniqueFreeFormProposalBody('0.1', generateFreeFormProposalTitle());
cy.contains('Awaiting network confirmation', epochTimeout).should(
'not.exist'
);
cy.get('input:invalid') cy.get('input:invalid')
.invoke('prop', 'validationMessage') .invoke('prop', 'validationMessage')
.should('equal', 'Value must be greater than or equal to 1.'); .should('equal', 'Value must be greater than or equal to 1.');
@@ -165,13 +185,17 @@ context(
navigateTo(navigation.proposals); navigateTo(navigation.proposals);
cy.get(rejectProposalsLink).click(); cy.get(rejectProposalsLink).click();
cy.get<testFreeformProposal>('@rawProposal').then((rawProposal) => { cy.get<testFreeformProposal>('@rawProposal').then((rawProposal) => {
getProposalFromTitle(rawProposal.rationale.title).within(() => { getSubmittedProposalFromProposalList(
rawProposal.rationale.title
).within(() => {
cy.contains('Rejected').should('be.visible'); cy.contains('Rejected').should('be.visible');
cy.contains('Close time too late').should('be.visible'); cy.contains('Close time too late').should('be.visible');
cy.get(viewProposalButton).click(); cy.get(viewProposalButton).click();
}); });
}); });
cy.getByTestId('proposal-status').should('have.text', 'Rejected'); getProposalInformationFromTable('State')
.contains('Rejected')
.and('be.visible');
getProposalInformationFromTable('Rejection reason') getProposalInformationFromTable('Rejection reason')
.contains('PROPOSAL_ERROR_CLOSE_TIME_TOO_LATE') .contains('PROPOSAL_ERROR_CLOSE_TIME_TOO_LATE')
.and('be.visible'); .and('be.visible');
@@ -261,38 +285,23 @@ context(
closeDialog(); 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 // 1005-PROP-009
it('Unable to vote on a freeform proposal - when some but not enough vega associated', function () { it('Unable to vote on a freeform proposal - when some but not enough vega associated', function () {
const proposalTitle = generateFreeFormProposalTitle(); const proposalTitle = generateFreeFormProposalTitle();
ensureSpecifiedUnstakedTokensAreAssociated('1'); ensureSpecifiedUnstakedTokensAreAssociated('1');
submitUniqueRawProposal({ proposalTitle: proposalTitle }); goToMakeNewProposal(governanceProposalType.FREEFORM);
ethereumWalletConnect(); enterUniqueFreeFormProposalBody('50', proposalTitle);
waitForProposalSubmitted();
stakingPageDisassociateTokens('0.0001'); stakingPageDisassociateTokens('0.0001');
cy.get(vegaWallet) cy.get(vegaWallet).within(() => {
.first()
.within(() => {
cy.get(vegaWalletAssociatedBalance, txTimeout).should( cy.get(vegaWalletAssociatedBalance, txTimeout).should(
'contain', 'contain',
'0.9999' '0.9999'
); );
}); });
navigateTo(navigation.proposals); navigateTo(navigation.proposals);
getProposalFromTitle(proposalTitle).within(() => getSubmittedProposalFromProposalList(proposalTitle).within(() =>
cy.get(viewProposalButton).click() cy.get(viewProposalButton).click()
); );
cy.contains('Vote breakdown').should('be.visible', { cy.contains('Vote breakdown').should('be.visible', {
@@ -310,9 +319,9 @@ context(
cy.get('[data-testid="manage-vega-wallet"]:visible').click(); cy.get('[data-testid="manage-vega-wallet"]:visible').click();
cy.get('[data-testid="disconnect"]').click(); cy.get('[data-testid="disconnect"]').click();
cy.get<testFreeformProposal>('@rawProposal').then((rawProposal) => { cy.get<testFreeformProposal>('@rawProposal').then((rawProposal) => {
getProposalFromTitle(rawProposal.rationale.title).within(() => getSubmittedProposalFromProposalList(
cy.get(viewProposalButton).click() rawProposal.rationale.title
); ).within(() => cy.get(viewProposalButton).click());
}); });
// 3001-VOTE-075 // 3001-VOTE-075
// 3001-VOTE-076 // 3001-VOTE-076
@@ -1,34 +1,23 @@
import { import {
closeDialog, closeDialog,
dissociateFromSecondWalletKey,
navigateTo, navigateTo,
navigation, navigation,
turnTelemetryOff, turnTelemetryOff,
waitForSpinner, waitForSpinner,
} from '../../support/common.functions'; } from '../../support/common.functions';
import {
getDownloadedProposalJsonPath,
getProposalFromTitle,
submitUniqueRawProposal,
} from '../../support/governance.functions';
import { import {
getProposalInformationFromTable, getProposalInformationFromTable,
goToMakeNewProposal, goToMakeNewProposal,
governanceProposalType,
voteForProposal, voteForProposal,
waitForProposalSubmitted,
} from '../../support/governance.functions'; } from '../../support/governance.functions';
import { import { ensureSpecifiedUnstakedTokensAreAssociated } from '../../support/staking.functions';
ensureSpecifiedUnstakedTokensAreAssociated,
stakingPageAssociateTokens,
stakingPageDisassociateAllTokens,
} from '../../support/staking.functions';
import { ethereumWalletConnect } from '../../support/wallet-eth.functions'; import { ethereumWalletConnect } from '../../support/wallet-eth.functions';
import { import {
switchVegaWalletPubKey,
vegaWalletFaucetAssetsWithoutCheck,
vegaWalletSetSpecifiedApprovalAmount, vegaWalletSetSpecifiedApprovalAmount,
vegaWalletTeardown, vegaWalletTeardown,
} from '../../support/wallet-functions'; } from '../../support/wallet-teardown.functions';
import { vegaWalletFaucetAssetsWithoutCheck } from '../../support/wallet-vega.functions';
const proposalListItem = '[data-testid="proposals-list-item"]'; const proposalListItem = '[data-testid="proposals-list-item"]';
const openProposals = '[data-testid="open-proposals"]'; const openProposals = '[data-testid="open-proposals"]';
@@ -36,11 +25,15 @@ const proposalType = '[data-testid="proposal-type"]';
const proposalDetails = '[data-testid="proposal-details"]'; const proposalDetails = '[data-testid="proposal-details"]';
const newProposalSubmitButton = '[data-testid="proposal-submit"]'; const newProposalSubmitButton = '[data-testid="proposal-submit"]';
const proposalVoteDeadline = '[data-testid="proposal-vote-deadline"]'; const proposalVoteDeadline = '[data-testid="proposal-vote-deadline"]';
const proposalValidationDeadline =
'[data-testid="proposal-validation-deadline"]';
const proposalParameterSelect = '[data-testid="proposal-parameter-select"]'; const proposalParameterSelect = '[data-testid="proposal-parameter-select"]';
const proposalMarketSelect = '[data-testid="proposal-market-select"]'; const proposalMarketSelect = '[data-testid="proposal-market-select"]';
const newProposalTitle = '[data-testid="proposal-title"]'; const newProposalTitle = '[data-testid="proposal-title"]';
const newProposalDescription = '[data-testid="proposal-description"]'; const newProposalDescription = '[data-testid="proposal-description"]';
const newProposalTerms = '[data-testid="proposal-terms"]'; const newProposalTerms = '[data-testid="proposal-terms"]';
const currentParameterValue =
'[data-testid="selected-proposal-param-current-value"]';
const newProposedParameterValue = const newProposedParameterValue =
'[data-testid="selected-proposal-param-new-value"]'; '[data-testid="selected-proposal-param-new-value"]';
const minVoteDeadline = '[data-testid="min-vote"]'; const minVoteDeadline = '[data-testid="min-vote"]';
@@ -56,13 +49,23 @@ const feedbackError = '[data-testid="Error"]';
const viewProposalBtn = 'view-proposal-btn'; const viewProposalBtn = 'view-proposal-btn';
const liquidityVoteStatus = 'liquidity-votes-status'; const liquidityVoteStatus = 'liquidity-votes-status';
const tokenVoteStatus = 'token-votes-status'; const tokenVoteStatus = 'token-votes-status';
const proposalJsonToggle = 'proposal-json-toggle'; const proposalTermsSection = 'proposal';
const proposalJsonSection = 'proposal-json';
const vegaWalletPublicKey = Cypress.env('vegaWalletPublicKey'); const vegaWalletPublicKey = Cypress.env('vegaWalletPublicKey');
const fUSDCId = const fUSDCId =
'816af99af60d684502a40824758f6b5377e6af48e50a9ee8ef478ecb879ea8bc'; '816af99af60d684502a40824758f6b5377e6af48e50a9ee8ef478ecb879ea8bc';
const epochTimeout = Cypress.env('epochTimeout');
const proposalTimeout = { timeout: 14000 }; const proposalTimeout = { timeout: 14000 };
const governanceProposalType = {
NETWORK_PARAMETER: 'Network parameter',
NEW_MARKET: 'New market',
UPDATE_MARKET: 'Update market',
NEW_ASSET: 'New asset',
UPDATE_ASSET: 'Update asset',
FREEFORM: 'Freeform',
RAW: 'raw proposal',
};
// 3001-VOTE-007 // 3001-VOTE-007
context( context(
'Governance flow - form validations for different governance proposals', 'Governance flow - form validations for different governance proposals',
@@ -85,10 +88,28 @@ context(
navigateTo(navigation.proposals); 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 () { it('Unable to submit network parameter with missing/invalid fields', function () {
navigateTo(navigation.proposals); navigateTo(navigation.proposals);
goToMakeNewProposal(governanceProposalType.NETWORK_PARAMETER); goToMakeNewProposal(governanceProposalType.NETWORK_PARAMETER);
cy.get(proposalDownloadBtn).click(); cy.get(newProposalSubmitButton).should('be.visible').click();
cy.get(inputError).should('have.length', 3); cy.get(inputError).should('have.length', 3);
cy.get(newProposalTitle).type( cy.get(newProposalTitle).type(
'Invalid update network parameter proposal' 'Invalid update network parameter proposal'
@@ -99,64 +120,53 @@ context(
); );
cy.get(newProposedParameterValue).type('0'); cy.get(newProposedParameterValue).type('0');
cy.get(proposalVoteDeadline).clear().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(); 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 network param proposal json', function () {
it('Able to download and submit network param proposal', function () { const downloadFolder = './cypress/downloads/';
goToMakeNewProposal(governanceProposalType.NETWORK_PARAMETER); goToMakeNewProposal(governanceProposalType.NETWORK_PARAMETER);
// 3007-PNEC-006 cy.log('Download proposal file');
cy.get(newProposalTitle) cy.get(proposalDownloadBtn)
.siblings() .should('be.visible')
.should('contain.text', '(100 characters or less)'); .click()
// 3007-PNEC-004 3007-PNEC-005 .then(() => {
cy.get(newProposalTitle).type('Test update network parameter proposal'); const filename =
// 3007-PNEC-009 downloadFolder +
cy.get(newProposalDescription) 'vega-network-param-proposal-' +
.siblings() getFormattedTime() +
.should('contain.text', '(20,000 characters or less)'); '.json';
// 3007-PNEC-007 3007-PNEC-008 cy.readFile(filename, proposalTimeout)
.its('terms.updateNetworkParameter')
.should('exist');
});
cy.get(newProposalDescription).type('E2E test for downloading proposals'); cy.get(newProposalDescription).type('E2E test for downloading proposals');
// 3007-PNEC-010
cy.get(proposalParameterSelect).select( cy.get(proposalParameterSelect).select(
'governance_proposal_asset_minClose' 'governance_proposal_asset_minClose'
); );
// 3007-PNEC-011
cy.get(newProposedParameterValue).type('10s'); 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.log('Download updated proposal file');
cy.get(proposalDownloadBtn) cy.get(proposalDownloadBtn)
.should('be.visible') .should('be.visible')
.click() .click()
.then(() => { .then(() => {
cy.wrap( const filename =
getDownloadedProposalJsonPath('vega-network-param-proposal-') downloadFolder +
).then((filePath) => { 'vega-network-param-proposal-' +
cy.readFile(String(filePath), { timeout: 14000 }).then( getFormattedTime() +
(jsonFile) => { '.json';
cy.get(proposalDownloadBtn).should('be.visible').click();
cy.readFile(filename, proposalTimeout).then((jsonFile) => {
cy.wrap(jsonFile) cy.wrap(jsonFile)
.its('rationale.description') .its('rationale.description')
.should('eq', 'E2E test for downloading proposals'); .should('eq', 'E2E test for downloading proposals');
@@ -166,11 +176,6 @@ context(
cy.wrap(jsonFile) cy.wrap(jsonFile)
.its('terms.updateNetworkParameter.changes.value') .its('terms.updateNetworkParameter.changes.value')
.should('eq', '10s'); .should('eq', '10s');
}
);
// 3007-PNE-019
goToMakeNewProposal(governanceProposalType.RAW);
submitUniqueRawProposal({ proposalBody: filePath });
}); });
}); });
}); });
@@ -188,31 +193,20 @@ context(
cy.get(maxVoteDeadline).click(); cy.get(maxVoteDeadline).click();
cy.get(enactmentDeadlineError).should( cy.get(enactmentDeadlineError).should(
'have.text', 'have.text',
'The proposal will fail if enactment is earlier than the voting deadline' '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(); cy.get(newProposalSubmitButton).click();
validateFeedBackMsg( cy.get(feedbackError).should(
'have.text',
'Invalid params: proposal_submission.terms.closing_timestamp (cannot be after enactment time)' '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 // 3003-PMAN-001
it( it('Able to submit valid new market proposal', function () {
'Able to submit valid new market proposal',
{ tags: '@smoke' },
function () {
const proposalTitle = 'Test new market proposal';
goToMakeNewProposal(governanceProposalType.NEW_MARKET); goToMakeNewProposal(governanceProposalType.NEW_MARKET);
cy.get(newProposalTitle).type('Test new market proposal'); cy.get(newProposalTitle).type('Test new market proposal');
cy.get(newProposalDescription).type('E2E test for proposals'); cy.get(newProposalDescription).type('E2E test for proposals');
@@ -223,43 +217,16 @@ context(
delay: 2, delay: 2,
}); });
}); });
cy.get(proposalDownloadBtn) cy.get(newProposalSubmitButton).should('be.visible').click();
.should('be.visible') waitForProposalSubmitted();
.click()
.then(() => {
cy.wrap(
getDownloadedProposalJsonPath('vega-new-market-proposal-')
).then((filePath) => {
goToMakeNewProposal(governanceProposalType.RAW);
submitUniqueRawProposal({ proposalBody: filePath }); // 3003-PMAN-003
}); });
});
navigateTo(navigation.proposals);
getProposalFromTitle(proposalTitle).within(() =>
cy.getByTestId('view-proposal-btn').click()
);
cy.getByTestId('proposal-market-data').within(() => {
cy.getByTestId('proposal-market-data-toggle').click();
cy.contains('Key details').click();
getMarketProposalDetailsFromTable('Name').should(
'have.text',
'Token test market'
);
cy.contains('Settlement asset').click();
// Settlement asset symbol
cy.getByTestId('3_value').should('have.text', 'fBTC');
cy.contains('Oracle').click();
cy.getByTestId('oracle-spec-links').should('have.attr', 'href');
});
}
);
it('Unable to submit new market proposal with missing/invalid fields', function () { it('Unable to submit new market proposal with missing/invalid fields', function () {
const errorMsg = const errorMsg =
'Invalid params: the transaction does not use a valid Vega command: unknown field "invalid" in vega.NewMarket'; 'Invalid params: the transaction does not use a valid Vega command: unknown field "invalid" in vega.NewMarket';
goToMakeNewProposal(governanceProposalType.NEW_MARKET); 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(inputError).should('have.length', 3);
cy.get(newProposalTitle).type('Test new market proposal'); cy.get(newProposalTitle).type('Test new market proposal');
cy.get(newProposalDescription).type('E2E test for proposals'); cy.get(newProposalDescription).type('E2E test for proposals');
@@ -271,27 +238,14 @@ context(
delay: 2, 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.get(newProposalSubmitButton).should('be.visible').click();
cy.contains('Transaction failed', proposalTimeout).should('be.visible'); 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' // Will fail if run after 'Able to submit update market proposal and vote for proposal'
// 3002-PROP-022 // 3002-PROP-022
it('Unable to submit update market proposal without equity-like share in the market', function () { it('Unable to submit update market proposal without equity-like share in the market', function () {
switchVegaWalletPubKey();
stakingPageAssociateTokens('1');
goToMakeNewProposal(governanceProposalType.UPDATE_MARKET); goToMakeNewProposal(governanceProposalType.UPDATE_MARKET);
cy.get(newProposalTitle).type('Test update market proposal - rejected'); cy.get(newProposalTitle).type('Test update market proposal - rejected');
cy.get(newProposalDescription).type('E2E test for proposals'); cy.get(newProposalDescription).type('E2E test for proposals');
@@ -303,24 +257,12 @@ context(
delay: 2, 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.get(newProposalSubmitButton).should('be.visible').click();
cy.contains('Proposal rejected', proposalTimeout).should('be.visible'); cy.contains('Proposal rejected', proposalTimeout).should('be.visible');
validateDialogContentMsg('PROPOSAL_ERROR_INSUFFICIENT_EQUITY_LIKE_SHARE'); cy.getByTestId('dialog-content')
closeDialog(); .find('p')
ethereumWalletConnect(); .should('have.text', 'PROPOSAL_ERROR_INSUFFICIENT_EQUITY_LIKE_SHARE');
stakingPageDisassociateAllTokens(); ensureSpecifiedUnstakedTokensAreAssociated('1');
switchVegaWalletPubKey();
}); });
// 3002-PROP-020 // 3002-PROP-020
@@ -342,25 +284,15 @@ context(
delay: 2, 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.get(newProposalSubmitButton).should('be.visible').click();
cy.contains('Transaction failed', proposalTimeout).should('be.visible'); 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)' '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 () { it('Able to submit update market proposal and vote for proposal', function () {
vegaWalletFaucetAssetsWithoutCheck( vegaWalletFaucetAssetsWithoutCheck(
fUSDCId, fUSDCId,
@@ -390,20 +322,11 @@ context(
delay: 2, delay: 2,
}); });
}); });
cy.get(proposalDownloadBtn) cy.get(newProposalSubmitButton).should('be.visible').click();
.should('be.visible') waitForProposalSubmitted();
.click()
.then(() => {
cy.wrap(
getDownloadedProposalJsonPath('vega-update-market-proposal-')
).then((filePath) => {
goToMakeNewProposal(governanceProposalType.RAW);
submitUniqueRawProposal({ proposalBody: filePath });
});
});
navigateTo(navigation.proposals); navigateTo(navigation.proposals);
cy.get('@EnactedMarketId').then((marketId) => { cy.get('@EnactedMarketId').then((marketId) => {
cy.contains(String(marketId).slice(0, 6)) cy.contains(String(marketId))
.parentsUntil(proposalListItem) .parentsUntil(proposalListItem)
.last() .last()
.within(() => { .within(() => {
@@ -427,7 +350,6 @@ context(
'contain.text', 'contain.text',
'Currently expected to pass' 'Currently expected to pass'
); );
cy.getByTestId('vote-breakdown-toggle').click();
getProposalInformationFromTable('Expected to pass') getProposalInformationFromTable('Expected to pass')
.contains('👍 by token vote') .contains('👍 by token vote')
.should('be.visible'); .should('be.visible');
@@ -449,23 +371,25 @@ context(
cy.get(minVoteDeadline).click(); cy.get(minVoteDeadline).click();
cy.get(minValidationDeadline).click(); cy.get(minValidationDeadline).click();
cy.get(minEnactDeadline).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.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(); closeDialog();
cy.get(newProposalSubmitButton).should('be.visible').click(); cy.get(newProposalSubmitButton).should('be.visible').click();
// cannot submit a proposal with ERC20 address already in use // cannot submit a proposal with ERC20 address already in use
cy.contains('Proposal rejected', proposalTimeout).should('be.visible'); 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(); closeDialog();
navigateTo(navigation.proposals); navigateTo(navigation.proposals);
cy.contains(proposalTitle) cy.contains(proposalTitle)
@@ -474,8 +398,7 @@ context(
.within(() => { .within(() => {
cy.getByTestId(viewProposalBtn).click(); cy.getByTestId(viewProposalBtn).click();
}); });
cy.getByTestId(proposalJsonToggle).click(); cy.getByTestId(proposalTermsSection).within(() => {
cy.getByTestId(proposalJsonSection).within(() => {
cy.contains('USDT Coin').should('be.visible'); cy.contains('USDT Coin').should('be.visible');
cy.contains('USDT').should('be.visible'); cy.contains('USDT').should('be.visible');
}); });
@@ -483,8 +406,15 @@ context(
it('Unable to submit new asset proposal with missing/invalid fields', function () { it('Unable to submit new asset proposal with missing/invalid fields', function () {
goToMakeNewProposal(governanceProposalType.NEW_ASSET); 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(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 () { it('Able to submit update asset proposal using min deadline', function () {
@@ -495,17 +425,8 @@ context(
enterUpdateAssetProposalDetails(); enterUpdateAssetProposalDetails();
cy.get(minVoteDeadline).click(); cy.get(minVoteDeadline).click();
cy.get(minEnactDeadline).click(); cy.get(minEnactDeadline).click();
cy.get(proposalDownloadBtn) cy.get(newProposalSubmitButton).should('be.visible').click();
.should('be.visible') waitForProposalSubmitted();
.click()
.then(() => {
cy.wrap(
getDownloadedProposalJsonPath('vega-update-asset-proposal-')
).then((filePath) => {
goToMakeNewProposal(governanceProposalType.RAW);
submitUniqueRawProposal({ proposalBody: filePath });
});
});
navigateTo(navigation.proposals); navigateTo(navigation.proposals);
cy.get(openProposals).within(() => { cy.get(openProposals).within(() => {
cy.get(proposalType) cy.get(proposalType)
@@ -513,7 +434,7 @@ context(
.parentsUntil(proposalListItem) .parentsUntil(proposalListItem)
.last() .last()
.within(() => { .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(); cy.getByTestId(viewProposalBtn).click();
}); });
}); });
@@ -521,95 +442,37 @@ context(
.invoke('text') .invoke('text')
.should('not.be.empty'); .should('not.be.empty');
// 3001-VOTE-030 3001-VOTE-031 // 3001-VOTE-030 3001-VOTE-031
cy.getByTestId(proposalJsonToggle).click(); cy.getByTestId(proposalTermsSection).within(() => {
cy.getByTestId(proposalJsonSection).within(() => { cy.contains('UpdateAsset').should('be.visible');
cy.contains(assetId).should('be.visible'); cy.contains('UpdateERC20').should('be.visible');
cy.contains('lifetimeLimit').should('be.visible'); cy.contains('"lifetimeLimit": "10"').should('be.visible');
cy.contains('10').should('be.visible');
}); });
}); });
// 3006-PASC-001 3006-PASC-003
it('Able to submit update asset proposal using max deadline', function () { it('Able to submit update asset proposal using max deadline', function () {
goToMakeNewProposal(governanceProposalType.UPDATE_ASSET); goToMakeNewProposal(governanceProposalType.UPDATE_ASSET);
enterUpdateAssetProposalDetails(); enterUpdateAssetProposalDetails();
cy.get(maxVoteDeadline).click(); cy.get(maxVoteDeadline).click();
cy.get(maxEnactDeadline).click(); cy.get(maxEnactDeadline).click();
cy.get(proposalDownloadBtn) cy.get(newProposalSubmitButton).should('be.visible').click();
.should('be.visible') waitForProposalSubmitted();
.click()
.then(() => {
cy.wrap(
getDownloadedProposalJsonPath('vega-update-asset-proposal-')
).then((filePath) => {
goToMakeNewProposal(governanceProposalType.RAW);
submitUniqueRawProposal({ proposalBody: filePath });
});
});
}); });
it('Unable to submit edit asset proposal with missing/invalid fields', function () { it('Unable to submit edit asset proposal with missing/invalid fields', function () {
goToMakeNewProposal(governanceProposalType.UPDATE_ASSET); 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); cy.get(inputError).should('have.length', 3);
}); });
it('Able to download and submit freeform proposal', function () { function getFormattedTime() {
goToMakeNewProposal(governanceProposalType.FREEFORM); const now = new Date();
// 3008-PFRO-006 const day = now.getDate().toString().padStart(2, '0');
cy.get(newProposalTitle) const month = now.toLocaleString('en-US', { month: 'short' });
.siblings() const year = now.getFullYear().toString();
.should('contain.text', '(100 characters or less)'); // 3008-PFRO-007 const hours = now.getHours().toString().padStart(2, '0');
// 3008-PFRO-005 const minutes = now.getMinutes().toString().padStart(2, '0');
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 });
});
});
});
after('Disassociate from second wallet key if present', function () { return `${day}-${month}-${year}-${hours}-${minutes}`;
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);
} }
function enterUpdateAssetProposalDetails() { function enterUpdateAssetProposalDetails() {
@@ -623,13 +486,5 @@ context(
}); });
}); });
} }
function getMarketProposalDetailsFromTable(heading: string) {
return cy
.getByTestId('key-value-table-row')
.contains(heading)
.parent()
.siblings();
}
} }
); );
@@ -6,31 +6,28 @@ import {
waitForSpinner, waitForSpinner,
} from '../../support/common.functions'; } from '../../support/common.functions';
import { import {
createFreeformProposal,
createRawProposal, createRawProposal,
createTenDigitUnixTimeStampForSpecifiedDays, createTenDigitUnixTimeStampForSpecifiedDays,
enterRawProposalBody, enterRawProposalBody,
generateFreeFormProposalTitle, generateFreeFormProposalTitle,
getProposalFromTitle, getProposalIdFromList,
getProposalInformationFromTable, getProposalInformationFromTable,
getSubmittedProposalFromProposalList,
goToMakeNewProposal, goToMakeNewProposal,
governanceProposalType, governanceProposalType,
submitUniqueRawProposal,
voteForProposal, voteForProposal,
waitForProposalSubmitted, waitForProposalSubmitted,
waitForProposalSync, waitForProposalSync,
} from '../../support/governance.functions'; } from '../../support/governance.functions';
import { ensureSpecifiedUnstakedTokensAreAssociated } from '../../support/staking.functions'; import { ensureSpecifiedUnstakedTokensAreAssociated } from '../../support/staking.functions';
import { ethereumWalletConnect } from '../../support/wallet-eth.functions'; import { ethereumWalletConnect } from '../../support/wallet-eth.functions';
import { vegaWalletSetSpecifiedApprovalAmount } from '../../support/wallet-functions'; import { vegaWalletSetSpecifiedApprovalAmount } from '../../support/wallet-teardown.functions';
const proposalListItem = 'proposals-list-item';
const openProposals = '[data-testid="open-proposals"]'; const openProposals = '[data-testid="open-proposals"]';
const voteStatus = 'vote-status'; const voteStatus = '[data-testid="vote-status"]';
const proposalType = 'proposal-type';
const proposalStatus = 'proposal-status';
const proposalClosingDate = '[data-testid="vote-details"]'; const proposalClosingDate = '[data-testid="vote-details"]';
const viewProposalButton = '[data-testid="view-proposal-btn"]'; const viewProposalButton = '[data-testid="view-proposal-btn"]';
const voteBreakDownToggle = 'vote-breakdown-toggle';
describe('Governance flow for proposal list', { tags: '@slow' }, function () { describe('Governance flow for proposal list', { tags: '@slow' }, function () {
before('connect wallets and set approval limit', function () { before('connect wallets and set approval limit', function () {
@@ -63,12 +60,12 @@ describe('Governance flow for proposal list', { tags: '@slow' }, function () {
navigateTo(navigation.proposals); navigateTo(navigation.proposals);
cy.get(openProposals).within(() => { cy.get(openProposals).within(() => {
cy.get(proposalClosingDate).first().should('contain.text', 'year');
cy.get(proposalClosingDate).should('contain.text', 'months');
cy.get(proposalClosingDate) cy.get(proposalClosingDate)
.first() .last()
.invoke('text') .invoke('text')
.should('match', /days|minutes/); .should('match', /days|minutes/);
cy.get(proposalClosingDate).should('contain.text', 'months');
cy.get(proposalClosingDate).last().should('contain.text', 'year');
}); });
}); });
@@ -76,28 +73,36 @@ describe('Governance flow for proposal list', { tags: '@slow' }, function () {
const proposerId = Cypress.env('vegaWalletPublicKey'); const proposerId = Cypress.env('vegaWalletPublicKey');
const proposalTitle = generateFreeFormProposalTitle(); const proposalTitle = generateFreeFormProposalTitle();
submitUniqueRawProposal({ proposalTitle: proposalTitle }); createFreeformProposal(proposalTitle);
getProposalIdFromList(proposalTitle);
cy.get('@proposalIdText').then((proposalId) => {
cy.get('[data-testid="set-proposals-filter-visible"]').click(); cy.get('[data-testid="set-proposals-filter-visible"]').click();
cy.get('[data-testid="filter-input"]').type(proposerId); cy.get('[data-testid="filter-input"]').type(proposerId);
// cy.get(`#${proposalId}`).should('contain', proposalId); cy.get(`#${proposalId}`).should('contain', proposalId);
cy.contains(proposalTitle).should('be.visible'); });
cy.get('[data-testid="filter-input"]').type('123');
cy.getByTestId(proposalListItem).should('not.exist');
}); });
it('Newly created proposals list - shows title and portion of summary', function () { it('Newly created proposals list - shows title and portion of summary', function () {
const proposalPath = 'src/fixtures/proposals/new-market-raw.json'; createRawProposal(this.minProposerBalance); // 3001-VOTE-052
const proposalTimestamp = createTenDigitUnixTimeStampForSpecifiedDays(3); cy.get<testFreeformProposal>('@rawProposal').then((rawProposal) => {
submitUniqueRawProposal({ getProposalIdFromList(rawProposal.rationale.title);
proposalBody: proposalPath, cy.get('@proposalIdText').then((proposalId) => {
enactmentTimestamp: proposalTimestamp, cy.get(openProposals).within(() => {
closingTimestamp: proposalTimestamp,
}); // 3001-VOTE-052
// 3001-VOTE-008 // 3001-VOTE-008
// 3001-VOTE-034 // 3001-VOTE-034
cy.get(`#${proposalId}`)
// 3001-VOTE-097 // 3001-VOTE-097
cy.contains('New Market Proposal E2E submission'); .should('contain', rawProposal.rationale.title)
cy.contains('Code: TEST.24h. fBTC settled future.').should('be.visible'); .and('be.visible');
cy.get(`#${proposalId}`)
.should(
'contain',
rawProposal.rationale.description.substring(0, 59)
)
.and('be.visible');
});
});
});
}); });
it('Newly created proposals list - shows open proposals in an open state', function () { it('Newly created proposals list - shows open proposals in an open state', function () {
@@ -105,11 +110,23 @@ describe('Governance flow for proposal list', { tags: '@slow' }, function () {
// 3001-VOTE-035 // 3001-VOTE-035
createRawProposal(this.minProposerBalance); createRawProposal(this.minProposerBalance);
cy.get<testFreeformProposal>('@rawProposal').then((rawProposal) => { cy.get<testFreeformProposal>('@rawProposal').then((rawProposal) => {
getProposalFromTitle(rawProposal.rationale.title).within(() => { getSubmittedProposalFromProposalList(rawProposal.rationale.title).within(
cy.get(viewProposalButton).should('be.visible'); () => {
cy.getByTestId(proposalType).should('have.text', 'Freeform'); cy.get(viewProposalButton).should('be.visible').click();
cy.getByTestId(proposalStatus).should('have.text', 'Open'); }
);
cy.get('@proposalIdText').then((proposalId) => {
getProposalInformationFromTable('ID')
.contains(String(proposalId))
.and('be.visible');
}); });
getProposalInformationFromTable('State')
.contains('Open')
.and('be.visible');
getProposalInformationFromTable('Type')
.contains('Freeform')
.and('be.visible');
}); });
}); });
@@ -117,22 +134,18 @@ describe('Governance flow for proposal list', { tags: '@slow' }, function () {
it('Newly created freeform proposals list - shows proposal participation - both met and not', function () { it('Newly created freeform proposals list - shows proposal participation - both met and not', function () {
const proposalTitle = generateFreeFormProposalTitle(); const proposalTitle = generateFreeFormProposalTitle();
submitUniqueRawProposal({ proposalTitle: proposalTitle }); createFreeformProposal(proposalTitle);
getProposalFromTitle(proposalTitle).within(() => { getSubmittedProposalFromProposalList(proposalTitle).within(() => {
// 3001-VOTE-039 // 3001-VOTE-039
cy.getByTestId(voteStatus).should( cy.get(voteStatus).should('have.text', 'Participation not reached');
'have.text',
'Participation not reached'
);
cy.get(viewProposalButton).click(); cy.get(viewProposalButton).click();
}); });
voteForProposal('for'); voteForProposal('for');
navigateTo(navigation.proposals); navigateTo(navigation.proposals);
getProposalFromTitle(proposalTitle).within(() => { getSubmittedProposalFromProposalList(proposalTitle).within(() => {
cy.getByTestId(voteStatus).should('have.text', 'Set to pass'); cy.get(voteStatus).should('have.text', 'Set to pass');
cy.get(viewProposalButton).click(); cy.get(viewProposalButton).click();
}); });
cy.getByTestId(voteBreakDownToggle).click();
getProposalInformationFromTable('Token participation met') getProposalInformationFromTable('Token participation met')
.contains('👍') .contains('👍')
.should('be.visible'); .should('be.visible');
@@ -7,6 +7,7 @@ import {
import { import {
clickOnValidatorFromList, clickOnValidatorFromList,
closeStakingDialog, closeStakingDialog,
stakingPageAssociateTokens,
stakingValidatorPageAddStake, stakingValidatorPageAddStake,
waitForBeginningOfEpoch, waitForBeginningOfEpoch,
} from '../../support/staking.functions'; } from '../../support/staking.functions';
@@ -14,7 +15,7 @@ import { ethereumWalletConnect } from '../../support/wallet-eth.functions';
import { import {
depositAsset, depositAsset,
vegaWalletTeardown, vegaWalletTeardown,
} from '../../support/wallet-functions'; } from '../../support/wallet-teardown.functions';
const vegaAssetAddress = '0x67175Da1D5e966e40D11c4B2519392B2058373de'; const vegaAssetAddress = '0x67175Da1D5e966e40D11c4B2519392B2058373de';
const vegaWalletUnstakedBalance = const vegaWalletUnstakedBalance =
@@ -29,22 +30,20 @@ context('rewards - flow', { tags: '@slow' }, function () {
turnTelemetryOff(); turnTelemetryOff();
cy.visit('/'); cy.visit('/');
waitForSpinner(); waitForSpinner();
depositAsset(vegaAssetAddress, '1000', 18);
cy.validatorsSelfDelegate();
ethereumWalletConnect(); ethereumWalletConnect();
cy.connectVegaWallet(); cy.connectVegaWallet();
depositAsset(vegaAssetAddress, '1000', 18); cy.VegaWalletTopUpRewardsPool(30, 200);
cy.getByTestId('currency-title', txTimeout).should(
'contain.text',
'Collateral'
);
vegaWalletTeardown();
cy.associateTokensToVegaWallet('6000');
navigateTo(navigation.validators); navigateTo(navigation.validators);
cy.VegaWalletTopUpRewardsPool(); vegaWalletTeardown();
stakingPageAssociateTokens('6000');
cy.get(vegaWalletUnstakedBalance, txTimeout).should( cy.get(vegaWalletUnstakedBalance, txTimeout).should(
'contain', 'contain',
'6,000.0', '6,000.0',
txTimeout txTimeout
); );
cy.get('button').contains('Select a validator to nominate').click();
clickOnValidatorFromList(0); clickOnValidatorFromList(0);
stakingValidatorPageAddStake('3000'); stakingValidatorPageAddStake('3000');
closeStakingDialog(); closeStakingDialog();
@@ -25,7 +25,7 @@ import { ethereumWalletConnect } from '../../support/wallet-eth.functions';
import { import {
vegaWalletSetSpecifiedApprovalAmount, vegaWalletSetSpecifiedApprovalAmount,
vegaWalletTeardown, vegaWalletTeardown,
} from '../../support/wallet-functions'; } from '../../support/wallet-teardown.functions';
const stakeValidatorListTotalStake = 'total-stake'; const stakeValidatorListTotalStake = 'total-stake';
const stakeValidatorListTotalShare = 'total-stake-share'; const stakeValidatorListTotalShare = 'total-stake-share';
const stakeValidatorListStakePercentage = 'stake-percentage'; const stakeValidatorListStakePercentage = 'stake-percentage';
@@ -54,10 +54,12 @@ context(
'Staking Tab - with eth and vega wallets connected', 'Staking Tab - with eth and vega wallets connected',
{ tags: '@slow' }, { tags: '@slow' },
function () { function () {
// 1002-STKE-002, 1002-STKE-032 // 2001-STKE-002, 2001-STKE-032
before('visit staking tab and connect vega wallet', function () { before('visit staking tab and connect vega wallet', function () {
cy.visit('/'); cy.visit('/');
ethereumWalletConnect(); ethereumWalletConnect();
// this is a workaround for #2422 which can be removed once issue is resolved
cy.associateTokensToVegaWallet('4');
vegaWalletSetSpecifiedApprovalAmount('1000'); vegaWalletSetSpecifiedApprovalAmount('1000');
}); });
@@ -75,42 +77,26 @@ context(
} }
); );
// 1002-STKE-035 1002-STKE-036
it('Unable to stake against a validator with less than minimum and more than associated amount', function () {
ensureSpecifiedUnstakedTokensAreAssociated('3');
verifyUnstakedBalance(3.0);
verifyEthWalletTotalAssociatedBalance('3.0');
verifyEthWalletAssociatedBalance('3.0');
cy.get('button').contains('Select a validator to nominate').click();
clickOnValidatorFromList(0);
cy.getByTestId(stakeAddStakeRadioButton, epochTimeout).click({
force: true,
});
cy.getByTestId(stakeTokenAmountInputBox).type('0.001');
cy.getByTestId(stakeTokenSubmitButton).should('be.disabled');
cy.getByTestId(stakeTokenAmountInputBox).clear().type('4');
cy.getByTestId(stakeTokenSubmitButton).should('be.disabled');
});
it('Able to stake against a validator - using vega from wallet', function () { it('Able to stake against a validator - using vega from wallet', function () {
ensureSpecifiedUnstakedTokensAreAssociated('3'); ensureSpecifiedUnstakedTokensAreAssociated('3');
verifyUnstakedBalance(3.0); verifyUnstakedBalance(3.0);
verifyEthWalletTotalAssociatedBalance('3.0'); verifyEthWalletTotalAssociatedBalance('3.0');
verifyEthWalletAssociatedBalance('3.0'); verifyEthWalletAssociatedBalance('3.0');
cy.get('button').contains('Select a validator to nominate').click(); cy.get('button').contains('Select a validator to nominate').click();
// 1002-STKE-031 // 2001-STKE-031
clickOnValidatorFromList(0); clickOnValidatorFromList(0);
// 1002-STKE-033, 1002-STKE-034, 1002-STKE-037 // 2001-STKE-033, 2001-STKE-034, 2001-STKE-037
stakingValidatorPageAddStake('2'); stakingValidatorPageAddStake('2');
verifyUnstakedBalance(1.0); verifyUnstakedBalance(1.0);
// 1002-STKE-039 // 2001-STKE-039
verifyStakedBalance(2.0); verifyStakedBalance(2.0);
verifyNextEpochValue(2.0); // 1002-STKE-016 1002-STKE-038 verifyNextEpochValue(2.0); // 2001-STKE-016 2001-STKE-038
verifyThisEpochValue(2.0); // 1002-STKE-013 verifyThisEpochValue(2.0); // 2001-STKE-013
closeStakingDialog(); closeStakingDialog();
navigateTo(navigation.validators); navigateTo(navigation.validators);
// 2002-SINC-007 1002-STKE-015 1002-STKE-017 1002-STKE-052 // 2002-SINC-007
validateValidatorListTotalStakeAndShare('0', '3,002.00', '50.02%'); validateValidatorListTotalStakeAndShare('0', '2.00', '100.00%');
}); });
it('Able to view validators staked by me', function () { it('Able to view validators staked by me', function () {
@@ -123,7 +109,6 @@ context(
cy.getByTestId(userStake, epochTimeout) cy.getByTestId(userStake, epochTimeout)
.first() .first()
.should('have.text', '2.00'); .should('have.text', '2.00');
waitForBeginningOfEpoch();
cy.getByTestId('total-stake').first().realHover(); cy.getByTestId('total-stake').first().realHover();
cy.getByTestId('staked-by-user-tooltip') cy.getByTestId('staked-by-user-tooltip')
.first() .first()
@@ -161,7 +146,7 @@ context(
verifyThisEpochValue(2.0); verifyThisEpochValue(2.0);
closeStakingDialog(); closeStakingDialog();
navigateTo(navigation.validators); navigateTo(navigation.validators);
validateValidatorListTotalStakeAndShare('0', '3,002.00', '50.02%'); validateValidatorListTotalStakeAndShare('0', '2.00', '100.00%');
}); });
it('Able to stake against a validator - using vega from both wallet and vesting contract', function () { it('Able to stake against a validator - using vega from both wallet and vesting contract', function () {
@@ -181,11 +166,10 @@ context(
verifyThisEpochValue(6.0); verifyThisEpochValue(6.0);
closeStakingDialog(); closeStakingDialog();
navigateTo(navigation.validators); navigateTo(navigation.validators);
validateValidatorListTotalStakeAndShare('0', '3,006.00', '50.05%'); validateValidatorListTotalStakeAndShare('0', '6.00', '100.00%');
}); });
it('Able to stake against multiple validators', function () { it('Able to stake against multiple validators', function () {
vegaWalletTeardown();
stakingPageAssociateTokens('5'); stakingPageAssociateTokens('5');
verifyUnstakedBalance(5.0); verifyUnstakedBalance(5.0);
cy.get('button').contains('Select a validator to nominate').click(); cy.get('button').contains('Select a validator to nominate').click();
@@ -213,10 +197,14 @@ context(
.eq(1) .eq(1)
.within(() => { .within(() => {
cy.getByTestId(stakeValidatorListTotalStake) cy.getByTestId(stakeValidatorListTotalStake)
.should('have.text', '3,002.00') .should('have.text', '2.00')
.and('be.visible'); .and('be.visible');
cy.getByTestId(stakeValidatorListTotalShare) cy.getByTestId(stakeValidatorListTotalShare)
.should('have.text', '50.01%') .should('have.text', '66.67%')
.and('be.visible');
cy.getByTestId(stakeValidatorListTotalStake)
.scrollIntoView()
.should('have.text', '2.00')
.and('be.visible'); .and('be.visible');
}); });
cy.get(`[row-id="${1}"]`) cy.get(`[row-id="${1}"]`)
@@ -224,15 +212,19 @@ context(
.within(() => { .within(() => {
cy.getByTestId(stakeValidatorListTotalStake) cy.getByTestId(stakeValidatorListTotalStake)
.scrollIntoView() .scrollIntoView()
.should('have.text', '3,001.00') .should('have.text', '1.00')
.and('be.visible'); .and('be.visible');
cy.getByTestId(stakeValidatorListTotalShare) cy.getByTestId(stakeValidatorListTotalShare)
.should('have.text', '49.99%') .should('have.text', '33.33%')
.and('be.visible');
cy.getByTestId(stakeValidatorListTotalStake)
.scrollIntoView()
.should('have.text', '1.00')
.and('be.visible'); .and('be.visible');
}); });
}); });
// 1002-STKE-041 1002-STKE-053 // 2001-STKE-041
it( it(
'Able to remove part of a stake against a validator', 'Able to remove part of a stake against a validator',
{ tags: '@smoke' }, { tags: '@smoke' },
@@ -245,11 +237,11 @@ context(
verifyUnstakedBalance(1.0); verifyUnstakedBalance(1.0);
closeStakingDialog(); closeStakingDialog();
navigateTo(navigation.validators); navigateTo(navigation.validators);
// 1002-STKE-040 // 2001-STKE-040
clickOnValidatorFromList(0); clickOnValidatorFromList(0);
// 1002-STKE-044, 1002-STKE-048 // 2001-STKE-044, 2001-STKE-048
stakingValidatorPageRemoveStake('1'); stakingValidatorPageRemoveStake('1');
// 1002-STKE-049 // 2001-STKE-049
verifyNextEpochValue(2.0); verifyNextEpochValue(2.0);
verifyUnstakedBalance(2.0); verifyUnstakedBalance(2.0);
verifyStakedBalance(2.0); verifyStakedBalance(2.0);
@@ -269,7 +261,7 @@ context(
} }
); );
// 1002-STKE-045 // 2001-STKE-045
it('Able to remove a full stake against a validator', function () { it('Able to remove a full stake against a validator', function () {
stakingPageAssociateTokens('3'); stakingPageAssociateTokens('3');
verifyUnstakedBalance(3.0); verifyUnstakedBalance(3.0);
@@ -290,7 +282,7 @@ context(
txTimeout txTimeout
); );
navigateTo(navigation.validators); navigateTo(navigation.validators);
validateValidatorListTotalStakeAndShare('0', '3,000.00', '50.00%'); validateValidatorListTotalStakeAndShare('0', '0.00', '0.00%');
cy.getByTestId(userStakeBtn).should('not.exist'); cy.getByTestId(userStakeBtn).should('not.exist');
cy.getByTestId(userStake).should('not.exist'); cy.getByTestId(userStake).should('not.exist');
@@ -317,7 +309,6 @@ context(
.and('be.visible'); .and('be.visible');
}); });
// 1002-STKE-046 1002-STKE-047
it('Unable to remove a stake greater than staked amount next epoch for a validator', function () { it('Unable to remove a stake greater than staked amount next epoch for a validator', function () {
stakingPageAssociateTokens('3'); stakingPageAssociateTokens('3');
verifyUnstakedBalance(3.0); verifyUnstakedBalance(3.0);
@@ -363,7 +354,7 @@ context(
txTimeout txTimeout
); );
navigateTo(navigation.validators); navigateTo(navigation.validators);
validateValidatorListTotalStakeAndShare('0', '3,000.00', '50.00%'); validateValidatorListTotalStakeAndShare('0', '0.00', '0.00%');
}); });
it('Disassociating all vesting contract tokens max - removes all staked tokens', function () { it('Disassociating all vesting contract tokens max - removes all staked tokens', function () {
@@ -391,13 +382,10 @@ context(
txTimeout txTimeout
); );
navigateTo(navigation.validators); navigateTo(navigation.validators);
validateValidatorListTotalStakeAndShare('0', '3,000.00', '50.00%'); validateValidatorListTotalStakeAndShare('0', '0.00', '0.00%');
}); });
it('Disassociating some tokens - prioritizes unstaked tokens', function () { it('Disassociating some tokens - prioritizes unstaked tokens', function () {
vegaWalletSetSpecifiedApprovalAmount('1000');
cy.reload();
ethereumWalletConnect();
stakingPageAssociateTokens('3'); stakingPageAssociateTokens('3');
verifyUnstakedBalance(3.0); verifyUnstakedBalance(3.0);
cy.get('button').contains('Select a validator to nominate').click(); cy.get('button').contains('Select a validator to nominate').click();
@@ -416,11 +404,11 @@ context(
}); });
verifyStakedBalance(2.0); verifyStakedBalance(2.0);
navigateTo(navigation.validators); navigateTo(navigation.validators);
validateValidatorListTotalStakeAndShare('0', '3,002.00', '50.02%'); validateValidatorListTotalStakeAndShare('0', '2.00', '100.00%');
}); });
it('Associating wallet tokens - when some already staked - auto stakes tokens to staked validator', function () { it('Associating wallet tokens - when some already staked - auto stakes tokens to staked validator', function () {
// 1002-STKE-004 // 2001-STKE-004
stakingPageAssociateTokens('3'); stakingPageAssociateTokens('3');
verifyUnstakedBalance(3.0); verifyUnstakedBalance(3.0);
cy.get('button').contains('Select a validator to nominate').click(); cy.get('button').contains('Select a validator to nominate').click();
@@ -434,7 +422,7 @@ context(
}); });
it('Associating vesting contract tokens - when some already staked - auto stakes tokens to staked validator', function () { it('Associating vesting contract tokens - when some already staked - auto stakes tokens to staked validator', function () {
// 1002-STKE-004 // 2001-STKE-004
stakingPageAssociateTokens('3', { type: 'contract' }); stakingPageAssociateTokens('3', { type: 'contract' });
verifyUnstakedBalance(3.0); verifyUnstakedBalance(3.0);
cy.get('button').contains('Select a validator to nominate').click(); cy.get('button').contains('Select a validator to nominate').click();
@@ -448,7 +436,7 @@ context(
}); });
it('Associating vesting contract tokens - when wallet tokens already staked - auto stakes tokens to staked validator', function () { it('Associating vesting contract tokens - when wallet tokens already staked - auto stakes tokens to staked validator', function () {
// 1002-STKE-004 // 2001-STKE-004
stakingPageAssociateTokens('3', { type: 'wallet' }); stakingPageAssociateTokens('3', { type: 'wallet' });
verifyUnstakedBalance(3.0); verifyUnstakedBalance(3.0);
cy.get('button').contains('Select a validator to nominate').click(); cy.get('button').contains('Select a validator to nominate').click();
@@ -462,7 +450,7 @@ context(
}); });
it('Associating tokens - with multiple validators already staked - auto stakes to staked validators - abiding by existing stake ratio', function () { it('Associating tokens - with multiple validators already staked - auto stakes to staked validators - abiding by existing stake ratio', function () {
// 1002-STKE-004 // 2001-STKE-004
stakingPageAssociateTokens('6'); stakingPageAssociateTokens('6');
verifyUnstakedBalance(6.0); verifyUnstakedBalance(6.0);
cy.get('button').contains('Select a validator to nominate').click(); cy.get('button').contains('Select a validator to nominate').click();
@@ -504,7 +492,6 @@ context(
}); });
afterEach('Teardown Wallet', function () { afterEach('Teardown Wallet', function () {
navigateTo(navigation.home);
vegaWalletTeardown(); vegaWalletTeardown();
}); });
@@ -515,7 +502,7 @@ context(
} }
function verifyThisEpochValue(amount: number) { function verifyThisEpochValue(amount: number) {
cy.getByTestId('stake-this-epoch', epochTimeout) // 1002-STKE-013 cy.getByTestId('stake-this-epoch', epochTimeout) // 2001-STKE-013
.contains(amount, epochTimeout) .contains(amount, epochTimeout)
.should('be.visible'); .should('be.visible');
} }
@@ -14,12 +14,11 @@ import {
} from '../../support/staking.functions'; } from '../../support/staking.functions';
import { ethereumWalletConnect } from '../../support/wallet-eth.functions'; import { ethereumWalletConnect } from '../../support/wallet-eth.functions';
import { import {
switchVegaWalletPubKey,
vegaWalletAssociate, vegaWalletAssociate,
vegaWalletDisassociate, vegaWalletDisassociate,
vegaWalletSetSpecifiedApprovalAmount, vegaWalletSetSpecifiedApprovalAmount,
vegaWalletTeardown, vegaWalletTeardown,
} from '../../support/wallet-functions'; } from '../../support/wallet-teardown.functions';
const ethWalletContainer = '[data-testid="ethereum-wallet"]'; const ethWalletContainer = '[data-testid="ethereum-wallet"]';
const vegaWalletAssociatedBalance = '[data-testid="currency-value"]'; const vegaWalletAssociatedBalance = '[data-testid="currency-value"]';
@@ -39,7 +38,7 @@ const associatedKey = '[data-testid="associated-key"]';
const associatedAmount = '[data-testid="associated-amount"]'; const associatedAmount = '[data-testid="associated-amount"]';
const associateCompleteText = '[data-testid="transaction-complete-body"]'; const associateCompleteText = '[data-testid="transaction-complete-body"]';
const disassociationWarning = '[data-testid="disassociation-warning"]'; const disassociationWarning = '[data-testid="disassociation-warning"]';
const vegaWallet = 'aside [data-testid="vega-wallet"]'; const vegaWallet = '[data-testid="vega-wallet"]';
context( context(
'Token association flow - with eth and vega wallets connected', 'Token association flow - with eth and vega wallets connected',
@@ -65,10 +64,7 @@ context(
} }
); );
it( it('Able to associate tokens - from wallet', function () {
'Able to associate tokens - from wallet',
{ tags: '@smoke' },
function () {
//1004-ASSO-003 //1004-ASSO-003
//1004-ASSO-005 //1004-ASSO-005
//1004-ASSO-009 //1004-ASSO-009
@@ -82,21 +78,32 @@ context(
//0005-ETXN-003 //0005-ETXN-003
//0005-ETXN-005 //0005-ETXN-005
stakingPageAssociateTokens('2', { skipConfirmation: true }); stakingPageAssociateTokens('2', { skipConfirmation: true });
cy.getByTestId('currency-title', txTimeout).should(
'have.length.above',
6
);
validateWalletCurrency('Associated', '0.00'); validateWalletCurrency('Associated', '0.00');
validateWalletCurrency('Pending association', '2.00'); validateWalletCurrency('Pending association', '2.00');
validateWalletCurrency('Total associated after pending', '2.00'); validateWalletCurrency('Total associated after pending', '2.00');
cy.getByTestId('currency-title', txTimeout).should('have.length', 6);
// 0005-ETXN-002 // 0005-ETXN-002
verifyEthWalletAssociatedBalance('2.0'); verifyEthWalletAssociatedBalance('2.0');
verifyEthWalletTotalAssociatedBalance('2.0'); verifyEthWalletTotalAssociatedBalance('2.0');
cy.get(vegaWallet).within(() => {
cy.get(vegaWallet)
.first()
.within(() => {
cy.get(vegaWalletAssociatedBalance, txTimeout).should( cy.get(vegaWalletAssociatedBalance, txTimeout).should(
'contain', 'contain',
2.0 2.0
); );
}); });
cy.get(vegaWalletUnstakedBalance, txTimeout).should('contain', 2.0); cy.get(vegaWalletUnstakedBalance, txTimeout).should('contain', 2.0);
} });
);
it('Able to disassociate all associated tokens - manually', function () { it('Able to disassociate all associated tokens - manually', function () {
// 1004-ASSO-025 // 1004-ASSO-025
@@ -104,29 +111,34 @@ context(
// 1004-ASSO-028 // 1004-ASSO-028
// 1004-ASSO-029 // 1004-ASSO-029
// 1004-ASSO-031 // 1004-ASSO-031
vegaWalletTeardown();
stakingPageAssociateTokens('2'); stakingPageAssociateTokens('2');
verifyEthWalletAssociatedBalance('2.0'); verifyEthWalletAssociatedBalance('2.0');
verifyEthWalletTotalAssociatedBalance('6,002.00'); verifyEthWalletTotalAssociatedBalance('2.0');
cy.get('button').contains('Select a validator to nominate').click(); cy.get('button').contains('Select a validator to nominate').click();
cy.getByTestId('epoch-countdown').should('be.visible');
stakingPageDisassociateTokens('2'); stakingPageDisassociateTokens('2');
cy.getByTestId('currency-title', txTimeout).should(
'have.length.above',
6
);
validateWalletCurrency('Associated', '2.00'); validateWalletCurrency('Associated', '2.00');
validateWalletCurrency('Pending association', '2.00'); validateWalletCurrency('Pending association', '2.00');
validateWalletCurrency('Total associated after pending', '0.00'); validateWalletCurrency('Total associated after pending', '0.00');
cy.get( cy.getByTestId('currency-title', txTimeout).should('have.length', 6);
'[data-testid="eth-wallet-associated-balances"]:visible', cy.getByTestId('eth-wallet-associated-balances', txTimeout).should(
txTimeout 'not.exist'
).should('have.length', 2); );
verifyEthWalletTotalAssociatedBalance('6,000.00'); verifyEthWalletTotalAssociatedBalance('0.00');
}); });
it('Able to associate more tokens than the approved amount of 1000 - requires re-approval', function () { it('Able to associate more tokens than the approved amount of 1000 - requires re-approval', function () {
//1004-ASSO-011 //1004-ASSO-011
stakingPageAssociateTokens('1001', { approve: true }); stakingPageAssociateTokens('1001', { approve: true });
verifyEthWalletAssociatedBalance('1,001.00'); verifyEthWalletAssociatedBalance('1,001.00');
verifyEthWalletTotalAssociatedBalance('7,001.00'); verifyEthWalletTotalAssociatedBalance('1,001.00');
cy.get(vegaWallet).within(() => { cy.get(vegaWallet)
.last()
.within(() => {
cy.get(vegaWalletAssociatedBalance, txTimeout).should( cy.get(vegaWalletAssociatedBalance, txTimeout).should(
'contain', 'contain',
'1,001.00' '1,001.00'
@@ -136,15 +148,25 @@ context(
it('Able to disassociate a partial amount of tokens currently associated', function () { it('Able to disassociate a partial amount of tokens currently associated', function () {
stakingPageAssociateTokens('2'); stakingPageAssociateTokens('2');
cy.get(vegaWallet).within(() => { cy.get(vegaWallet)
cy.get(vegaWalletAssociatedBalance, txTimeout).should('contain', 2.0); .first()
.within(() => {
cy.get(vegaWalletAssociatedBalance, txTimeout).should(
'contain',
2.0
);
}); });
cy.get('button').contains('Select a validator to nominate').click(); cy.get('button').contains('Select a validator to nominate').click();
cy.getByTestId('epoch-countdown').should('be.visible');
stakingPageDisassociateTokens('1'); stakingPageDisassociateTokens('1');
verifyEthWalletAssociatedBalance('1.0'); verifyEthWalletAssociatedBalance('1.0');
cy.get(vegaWallet).within(() => { cy.get(vegaWallet)
cy.get(vegaWalletAssociatedBalance, txTimeout).should('contain', 1.0); .first()
.within(() => {
cy.get(vegaWalletAssociatedBalance, txTimeout).should(
'contain',
1.0
);
}); });
}); });
@@ -153,11 +175,15 @@ context(
const warningText = 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.'; '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'); stakingPageAssociateTokens('2');
cy.get(vegaWallet).within(() => { cy.get(vegaWallet)
cy.get(vegaWalletAssociatedBalance, txTimeout).should('contain', 2.0); .first()
.within(() => {
cy.get(vegaWalletAssociatedBalance, txTimeout).should(
'contain',
2.0
);
}); });
cy.get('button').contains('Select a validator to nominate').click(); cy.get('button').contains('Select a validator to nominate').click();
cy.getByTestId('epoch-countdown').should('be.visible');
cy.get(ethWalletDissociateButton).click(); cy.get(ethWalletDissociateButton).click();
cy.get(disassociationWarning).should('contain', warningText); cy.get(disassociationWarning).should('contain', warningText);
stakingPageDisassociateAllTokens(); stakingPageDisassociateAllTokens();
@@ -175,8 +201,13 @@ context(
'not.exist' 'not.exist'
); );
}); });
cy.get(vegaWallet).within(() => { cy.get(vegaWallet)
cy.get(vegaWalletAssociatedBalance, txTimeout).should('contain', 0.0); .first()
.within(() => {
cy.get(vegaWalletAssociatedBalance, txTimeout).should(
'contain',
0.0
);
}); });
}); });
@@ -192,22 +223,38 @@ context(
type: 'contract', type: 'contract',
skipConfirmation: true, skipConfirmation: true,
}); });
cy.getByTestId('currency-title', txTimeout).should(
'have.length.above',
6
);
validateWalletCurrency('Associated', '0.00'); validateWalletCurrency('Associated', '0.00');
validateWalletCurrency('Pending association', '2.00'); validateWalletCurrency('Pending association', '2.00');
validateWalletCurrency('Total associated after pending', '2.00'); validateWalletCurrency('Total associated after pending', '2.00');
cy.getByTestId('currency-title', txTimeout).should('have.length', 6);
verifyEthWalletAssociatedBalance('2.0'); verifyEthWalletAssociatedBalance('2.0');
verifyEthWalletTotalAssociatedBalance('2.0'); verifyEthWalletTotalAssociatedBalance('2.0');
cy.get(vegaWallet).within(() => { cy.get(vegaWallet)
cy.get(vegaWalletAssociatedBalance, txTimeout).should('contain', 2.0); .first()
.within(() => {
cy.get(vegaWalletAssociatedBalance, txTimeout).should(
'contain',
2.0
);
}); });
cy.get(vegaWalletUnstakedBalance, txTimeout).should('contain', 2.0); cy.get(vegaWalletUnstakedBalance, txTimeout).should('contain', 2.0);
stakingPageDisassociateTokens('1', { stakingPageDisassociateTokens('1', {
type: 'contract', type: 'contract',
skipConfirmation: true, skipConfirmation: true,
}); });
cy.getByTestId('currency-title', txTimeout).should(
'have.length.above',
6
);
validateWalletCurrency('Associated', '2.00'); validateWalletCurrency('Associated', '2.00');
validateWalletCurrency('Pending association', '1.00'); validateWalletCurrency('Pending association', '1.00');
validateWalletCurrency('Total associated after pending', '1.00'); validateWalletCurrency('Total associated after pending', '1.00');
cy.getByTestId('currency-title', txTimeout).should('have.length', 6);
verifyEthWalletAssociatedBalance('1.0'); verifyEthWalletAssociatedBalance('1.0');
verifyEthWalletTotalAssociatedBalance('1.0'); verifyEthWalletTotalAssociatedBalance('1.0');
}); });
@@ -219,7 +266,6 @@ context(
// 1004-ASSO-022 // 1004-ASSO-022
stakingPageAssociateTokens('21', { type: 'wallet' }); stakingPageAssociateTokens('21', { type: 'wallet' });
cy.get('button').contains('Select a validator to nominate').click(); cy.get('button').contains('Select a validator to nominate').click();
cy.getByTestId('epoch-countdown').should('be.visible');
stakingPageAssociateTokens('37', { type: 'contract' }); stakingPageAssociateTokens('37', { type: 'contract' });
cy.get(vestingContractSection) cy.get(vestingContractSection)
.first() .first()
@@ -239,8 +285,13 @@ context(
); );
cy.get(associatedAmount, txTimeout).should('contain', 21); cy.get(associatedAmount, txTimeout).should('contain', 21);
}); });
cy.get(vegaWallet).within(() => { cy.get(vegaWallet)
cy.get(vegaWalletAssociatedBalance, txTimeout).should('contain', 58); .first()
.within(() => {
cy.get(vegaWalletAssociatedBalance, txTimeout).should(
'contain',
58
);
}); });
stakingPageDisassociateTokens('6', { type: 'contract' }); stakingPageDisassociateTokens('6', { type: 'contract' });
cy.get(vestingContractSection) cy.get(vestingContractSection)
@@ -248,8 +299,13 @@ context(
.within(() => { .within(() => {
cy.get(associatedAmount, txTimeout).should('contain', 31); cy.get(associatedAmount, txTimeout).should('contain', 31);
}); });
cy.get(vegaWallet).within(() => { cy.get(vegaWallet)
cy.get(vegaWalletAssociatedBalance, txTimeout).should('contain', 52); .first()
.within(() => {
cy.get(vegaWalletAssociatedBalance, txTimeout).should(
'contain',
52
);
}); });
navigateTo(navigation.validators); navigateTo(navigation.validators);
stakingPageDisassociateTokens('9', { type: 'wallet' }); stakingPageDisassociateTokens('9', { type: 'wallet' });
@@ -258,8 +314,13 @@ context(
.within(() => { .within(() => {
cy.get(associatedAmount, txTimeout).should('contain', 12); cy.get(associatedAmount, txTimeout).should('contain', 12);
}); });
cy.get(vegaWallet).within(() => { cy.get(vegaWallet)
cy.get(vegaWalletAssociatedBalance, txTimeout).should('contain', 43); .first()
.within(() => {
cy.get(vegaWalletAssociatedBalance, txTimeout).should(
'contain',
43
);
}); });
}); });
@@ -277,9 +338,14 @@ context(
// 1004-ASSO-004 // 1004-ASSO-004
it('Pending association outside of app is shown', function () { it('Pending association outside of app is shown', function () {
vegaWalletAssociate('2'); vegaWalletAssociate('2');
cy.getByTestId('currency-title', txTimeout).should(
'have.length.above',
6
);
validateWalletCurrency('Associated', '0.00'); validateWalletCurrency('Associated', '0.00');
validateWalletCurrency('Pending association', '2.00'); validateWalletCurrency('Pending association', '2.00');
validateWalletCurrency('Total associated after pending', '2.00'); validateWalletCurrency('Total associated after pending', '2.00');
cy.getByTestId('currency-title', txTimeout).should('have.length', 6);
validateWalletCurrency('Associated', '2.00'); validateWalletCurrency('Associated', '2.00');
}); });
@@ -288,9 +354,14 @@ context(
cy.wrap(validateWalletCurrency('Associated', '2.00')).then(() => { cy.wrap(validateWalletCurrency('Associated', '2.00')).then(() => {
vegaWalletDisassociate('2'); vegaWalletDisassociate('2');
}); });
cy.getByTestId('currency-title', txTimeout).should(
'have.length.above',
6
);
validateWalletCurrency('Associated', '2.00'); validateWalletCurrency('Associated', '2.00');
validateWalletCurrency('Pending association', '2.00'); validateWalletCurrency('Pending association', '2.00');
validateWalletCurrency('Total associated after pending', '0.00'); validateWalletCurrency('Total associated after pending', '0.00');
cy.getByTestId('currency-title', txTimeout).should('have.length', 6);
validateWalletCurrency('Associated', '0.00'); validateWalletCurrency('Associated', '0.00');
}); });
@@ -302,14 +373,20 @@ context(
Cypress.env('vegaWalletPublicKey') Cypress.env('vegaWalletPublicKey')
); );
switchVegaWalletPubKey(); cy.get('[data-testid="manage-vega-wallet"]:visible').click();
cy.get('[data-testid="select-keypair-button"]').eq(0).click();
cy.get(connectedVegaKey).should( cy.get(connectedVegaKey).should(
'have.text', 'have.text',
Cypress.env('vegaWalletPublicKey2') Cypress.env('vegaWalletPublicKey2')
); );
stakingPageAssociateTokens('2'); stakingPageAssociateTokens('2');
cy.get(vegaWallet).within(() => { cy.get(vegaWallet)
cy.get(vegaWalletAssociatedBalance, txTimeout).should('contain', 2.0); .first()
.within(() => {
cy.get(vegaWalletAssociatedBalance, txTimeout).should(
'contain',
2.0
);
}); });
cy.get(associateCompleteText).should( cy.get(associateCompleteText).should(
'have.text', 'have.text',
@@ -5,7 +5,7 @@ import {
waitForSpinner, waitForSpinner,
} from '../../support/common.functions'; } from '../../support/common.functions';
import { ethereumWalletConnect } from '../../support/wallet-eth.functions'; import { ethereumWalletConnect } from '../../support/wallet-eth.functions';
import { depositAsset } from '../../support/wallet-functions'; import { depositAsset } from '../../support/wallet-teardown.functions';
const withdraw = 'withdraw'; const withdraw = 'withdraw';
const withdrawalForm = 'withdraw-form'; const withdrawalForm = 'withdraw-form';
@@ -15,7 +15,13 @@ const balanceAvailable = 'BALANCE_AVAILABLE_value';
const withdrawalThreshold = 'WITHDRAWAL_THRESHOLD_value'; const withdrawalThreshold = 'WITHDRAWAL_THRESHOLD_value';
const delayTime = 'DELAY_TIME_value'; const delayTime = 'DELAY_TIME_value';
const submitWithdrawalButton = 'submit-withdrawal'; const submitWithdrawalButton = 'submit-withdrawal';
const dialogTitle = 'dialog-title';
const dialogClose = 'dialog-close'; 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 completeWithdrawalButton = 'complete-withdrawal';
const tableTxHash = '[col-id="txHash"]'; const tableTxHash = '[col-id="txHash"]';
const tableAssetSymbol = '[col-id="asset.symbol"]'; const tableAssetSymbol = '[col-id="asset.symbol"]';
@@ -24,16 +30,14 @@ const tableReceiverAddress = '[col-id="details.receiverAddress"]';
const tableWithdrawnTimeStamp = '[col-id="withdrawnTimestamp"]'; const tableWithdrawnTimeStamp = '[col-id="withdrawnTimestamp"]';
const tableWithdrawnStatus = '[col-id="status"]'; const tableWithdrawnStatus = '[col-id="status"]';
const tableCreatedTimeStamp = '[col-id="createdTimestamp"]'; const tableCreatedTimeStamp = '[col-id="createdTimestamp"]';
const toast = 'toast'; const toastContent = 'toast-content';
const toastPanel = 'toast-panel'; const toastPanel = 'toast-panel';
const toastClose = 'toast-close';
const withdrawalDialogContent = 'dialog-content';
const toastCompleteWithdrawal = 'toast-complete-withdrawal';
const usdtName = 'USDC (local)'; const usdtName = 'USDC (local)';
const usdcEthAddress = '0x1b8a1B6CBE5c93609b46D1829Cc7f3Cb8eeE23a0'; const usdcEthAddress = '0x1b8a1B6CBE5c93609b46D1829Cc7f3Cb8eeE23a0';
const usdcSymbol = 'tUSDC'; const usdcSymbol = 'tUSDC';
const usdtSelectValue = const usdtSelectValue =
'993ed98f4f770d91a796faab1738551193ba45c62341d20597df70fea6704ede'; '993ed98f4f770d91a796faab1738551193ba45c62341d20597df70fea6704ede';
const truncatedWithdrawalEthAddress = '0xEe7D…22d94F';
const formValidationError = 'input-error-text'; const formValidationError = 'input-error-text';
const txTimeout = Cypress.env('txTimeout'); const txTimeout = Cypress.env('txTimeout');
@@ -43,9 +47,12 @@ context(
function () { function () {
before('visit withdrawals and connect vega wallet', function () { before('visit withdrawals and connect vega wallet', function () {
cy.visit('/'); cy.visit('/');
// When running tests locally, will fail if run without restarting capsule
cy.updateCapsuleMultiSig().then(() => {
ethereumWalletConnect(); ethereumWalletConnect();
depositAsset(usdcEthAddress, '1000', 5); depositAsset(usdcEthAddress, '1000', 5);
}); });
});
beforeEach('Navigate to withdrawal page', function () { beforeEach('Navigate to withdrawal page', function () {
cy.clearLocalStorage(); cy.clearLocalStorage();
@@ -55,10 +62,6 @@ context(
navigateTo(navigation.withdraw); navigateTo(navigation.withdraw);
cy.connectVegaWallet(); cy.connectVegaWallet();
ethereumWalletConnect(); ethereumWalletConnect();
cy.getByTestId('currency-title', txTimeout).should(
'contain.text',
usdtName
);
}); });
it('Able to open withdrawal form with vega wallet connected', function () { it('Able to open withdrawal form with vega wallet connected', function () {
@@ -73,10 +76,7 @@ context(
it('Unable to submit withdrawal with invalid fields', function () { it('Unable to submit withdrawal with invalid fields', function () {
cy.getByTestId(withdraw).should('be.visible').click(); cy.getByTestId(withdraw).should('be.visible').click();
cy.getByTestId(withdrawalForm, txTimeout).within(() => { cy.getByTestId(withdrawalForm, txTimeout).within(() => {
cy.getByTestId('select-asset').click(); cy.get('select').select(usdtSelectValue, { force: true });
cy.get('select')
.select(usdtSelectValue, { force: true })
.should('have.value', usdtSelectValue);
cy.getByTestId(balanceAvailable, txTimeout).should('exist'); cy.getByTestId(balanceAvailable, txTimeout).should('exist');
cy.getByTestId(submitWithdrawalButton).click(); cy.getByTestId(submitWithdrawalButton).click();
cy.getByTestId(formValidationError).should('have.length', 1); cy.getByTestId(formValidationError).should('have.length', 1);
@@ -96,17 +96,11 @@ context(
}); });
}); });
it( it('Able to withdraw asset: -eth wallet connected -withdraw funds button', function () {
'Able to withdraw asset: -eth wallet connected -withdraw funds button',
{ tags: '@smoke' },
function () {
// fill in withdrawal form // fill in withdrawal form
cy.getByTestId(withdraw).should('be.visible').click(); cy.getByTestId(withdraw).should('be.visible').click();
cy.getByTestId(withdrawalForm, txTimeout).within(() => { cy.getByTestId(withdrawalForm, txTimeout).within(() => {
cy.getByTestId('select-asset').click(); cy.get('select').select(usdtSelectValue, { force: true });
cy.get('select')
.select(usdtSelectValue, { force: true })
.should('have.value', usdtSelectValue);
cy.getByTestId(balanceAvailable, txTimeout).should('exist'); cy.getByTestId(balanceAvailable, txTimeout).should('exist');
cy.getByTestId(withdrawalThreshold).should( cy.getByTestId(withdrawalThreshold).should(
'have.text', 'have.text',
@@ -116,35 +110,29 @@ context(
cy.getByTestId(amountInput).click().type('120'); cy.getByTestId(amountInput).click().type('120');
cy.getByTestId(submitWithdrawalButton).click(); cy.getByTestId(submitWithdrawalButton).click();
}); });
cy.contains('Awaiting network confirmation').should('be.visible');
// assert withdrawal request // assert withdrawal request
cy.getByTestId(toast) cy.getByTestId(dialogTitle, txTimeout).should(
.first(txTimeout) 'have.text',
.should('contain.text', 'Funds unlocked') 'Transaction complete'
.within(() => {
cy.getByTestId('external-link').should('exist');
cy.getByTestId(toastPanel).should(
'contain.text',
'Withdraw 120.00 tUSDC'
); );
cy.getByTestId(toastCompleteWithdrawal).click(); cy.getByTestId(txExplorerLink)
cy.getByTestId(toastClose).click(); .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 // withdrawal complete
cy.getByTestId(toast) cy.getByTestId(dialogTitle, txTimeout).should(
.first(txTimeout) 'have.text',
.should('contain.text', 'The withdrawal has been approved.') 'Withdraw asset complete'
.within(() => {
cy.getByTestId(toastPanel).should(
'contain.text',
'Withdraw 120.00 tUSDC'
); );
}); cy.getByTestId(dialogClose).click();
cy.getByTestId(toast)
.last(txTimeout)
.should('contain.text', 'Transaction confirmed')
.within(() => {
cy.getByTestId('external-link').should('exist');
});
// withdrawal history for complete withdrawal displayed // withdrawal history for complete withdrawal displayed
cy.get(tableWithdrawnStatus) cy.get(tableWithdrawnStatus)
.eq(1, txTimeout) .eq(1, txTimeout)
@@ -163,8 +151,7 @@ context(
.should('have.attr', 'href') .should('have.attr', 'href')
.and('contain', 'https://sepolia.etherscan.io/tx/'); .and('contain', 'https://sepolia.etherscan.io/tx/');
}); });
} });
);
it('Able to withdraw asset: -eth wallet not connected', function () { it('Able to withdraw asset: -eth wallet not connected', function () {
const ethWalletAddress = Cypress.env('ethWalletPublicKey'); const ethWalletAddress = Cypress.env('ethWalletPublicKey');
@@ -173,10 +160,7 @@ context(
// fill in withdrawal form // fill in withdrawal form
cy.getByTestId(withdraw).should('be.visible').click(); cy.getByTestId(withdraw).should('be.visible').click();
cy.getByTestId(withdrawalForm, txTimeout).within(() => { cy.getByTestId(withdrawalForm, txTimeout).within(() => {
cy.getByTestId('select-asset').click(); cy.get('select').select(usdtSelectValue, { force: true });
cy.get('select')
.select(usdtSelectValue, { force: true })
.should('have.value', usdtSelectValue);
cy.getByTestId(ethAddressInput).should('be.empty'); cy.getByTestId(ethAddressInput).should('be.empty');
cy.getByTestId(amountInput).click().type('110'); cy.getByTestId(amountInput).click().type('110');
cy.getByTestId(submitWithdrawalButton).click(); cy.getByTestId(submitWithdrawalButton).click();
@@ -187,17 +171,13 @@ context(
cy.getByTestId(submitWithdrawalButton).click(); cy.getByTestId(submitWithdrawalButton).click();
}); });
cy.getByTestId(toast) cy.contains('Awaiting network confirmation').should('be.visible');
.first(txTimeout) // assert withdrawal request
.should('contain.text', 'Funds unlocked') cy.getByTestId(dialogTitle, txTimeout).should(
.within(() => { 'have.text',
cy.getByTestId('external-link').should('exist'); 'Transaction complete'
cy.getByTestId(toastPanel).should(
'contain.text',
'Withdraw 110.00 tUSDC'
); );
cy.getByTestId(toastClose).click(); cy.getByTestId(dialogClose).click();
});
cy.get(tableTxHash) cy.get(tableTxHash)
.eq(1) .eq(1)
.should('have.text', 'Complete withdrawal') .should('have.text', 'Complete withdrawal')
@@ -212,78 +192,28 @@ context(
cy.get(tableCreatedTimeStamp).should('not.be.empty'); cy.get(tableCreatedTimeStamp).should('not.be.empty');
}); });
ethereumWalletConnect(); ethereumWalletConnect();
cy.getByTestId(completeWithdrawalButton).first().click(); cy.getByTestId(completeWithdrawalButton).click();
cy.getByTestId(toast) cy.getByTestId(toastContent)
.last(txTimeout) .last()
.should('contain.text', 'Awaiting confirmation') .should('contain.text', 'Awaiting confirmation')
.within(() => { .within(() => {
cy.getByTestId('external-link').should('exist'); cy.getByTestId('external-link').should('exist');
}); });
cy.getByTestId(toast) cy.getByTestId(toastContent)
.first(txTimeout) .first()
.should('contain.text', 'The withdrawal has been approved.') .should('contain.text', 'The withdrawal has been approved.')
.within(() => { .within(() => {
cy.getByTestId(toastPanel).should('contain.text', '110.00', 'tUSDC'); cy.getByTestId(toastPanel).should('contain.text', '110.00', 'tUSDC');
}); });
cy.getByTestId(toast) cy.getByTestId(toastContent)
.last(txTimeout) .last()
.should('contain.text', 'Transaction confirmed') .should('contain.text', 'Transaction confirmed')
.within(() => { .within(() => {
cy.getByTestId('external-link').should('exist'); cy.getByTestId('external-link').should('exist');
}); });
}); });
it('Should be able to see withdrawal details from toast', function () { it('Unable to withdraw asset on pub key view', function () {
cy.getByTestId(withdraw).click();
cy.getByTestId(withdrawalForm, txTimeout).within(() => {
cy.getByTestId('select-asset').click();
cy.get('select')
.select(usdtSelectValue, { force: true })
.should('have.value', usdtSelectValue);
cy.getByTestId(balanceAvailable, txTimeout).should('exist');
cy.getByTestId(withdrawalThreshold).should(
'have.text',
'100,000.00000T'
);
cy.getByTestId(amountInput).click().type('50');
cy.getByTestId(submitWithdrawalButton).click();
});
cy.getByTestId(toast)
.first(txTimeout)
.should('contain.text', 'Funds unlocked')
.within(() => {
cy.getByTestId('external-link').should('exist');
cy.getByTestId(toastPanel).should(
'contain.text',
'Withdraw 50.00 tUSDC'
);
cy.contains('save your withdrawal details').click();
});
cy.getByTestId(withdrawalDialogContent)
.last()
.within(() => {
cy.getByTestId('assetSource_value').should(
'have.text',
'0x1b8a1B6CBE5c93609b46D1829Cc7f3Cb8eeE23a0'
);
cy.getByTestId('amount_value').should('have.text', '5000000');
cy.getByTestId('nonce_value').invoke('text').should('not.be.empty');
cy.getByTestId('signatures_value')
.invoke('text')
.should('not.be.empty');
cy.getByTestId('targetAddress_value').should(
'have.text',
Cypress.env('ethWalletPublicKey')
);
cy.getByTestId('creation_value')
.invoke('text')
.should('not.be.empty');
});
cy.getByTestId(dialogClose).click();
});
// Skipping test due to bug #3882
it.skip('Unable to withdraw asset on pub key view', function () {
const vegaWalletPubKey = Cypress.env('vegaWalletPublicKey'); const 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.`; 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.`;
@@ -294,17 +224,13 @@ context(
cy.connectPublicKey(vegaWalletPubKey); cy.connectPublicKey(vegaWalletPubKey);
cy.getByTestId(withdraw).should('be.visible').click(); cy.getByTestId(withdraw).should('be.visible').click();
cy.getByTestId(withdrawalForm, txTimeout).within(() => { cy.getByTestId(withdrawalForm, txTimeout).within(() => {
cy.getByTestId('select-asset').click(); cy.get('select').select(usdtSelectValue, { force: true });
cy.get('select')
.select(usdtSelectValue, { force: true })
.should('have.value', usdtSelectValue);
cy.getByTestId(balanceAvailable, txTimeout).should('exist'); cy.getByTestId(balanceAvailable, txTimeout).should('exist');
cy.getByTestId(amountInput).click().type('100'); cy.getByTestId(amountInput).click().type('100');
cy.pause();
cy.getByTestId(submitWithdrawalButton).click(); cy.getByTestId(submitWithdrawalButton).click();
}); });
cy.getByTestId(withdrawalDialogContent) cy.getByTestId('dialog-content')
.last() .last()
.within(() => { .within(() => {
cy.get('h1').should('have.text', 'Transaction failed'); cy.get('h1').should('have.text', 'Transaction failed');
@@ -11,6 +11,7 @@ import {
import { mockNetworkUpgradeProposal } from '../../support/proposal.functions'; import { mockNetworkUpgradeProposal } from '../../support/proposal.functions';
const proposalDocumentationLink = '[data-testid="proposal-documentation-link"]'; const proposalDocumentationLink = '[data-testid="proposal-documentation-link"]';
const newProposalLink = '[data-testid="new-proposal-link"]';
const governanceDocsUrl = 'https://vega.xyz/governance'; const governanceDocsUrl = 'https://vega.xyz/governance';
const connectToVegaWalletButton = '[data-testid="connect-to-vega-wallet-btn"]'; const connectToVegaWalletButton = '[data-testid="connect-to-vega-wallet-btn"]';
@@ -31,14 +32,6 @@ context(
verifyPageHeader('Proposals'); 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 () { it('should be able to see a working link for - find out more about Vega governance', function () {
// 3001-VOTE-001 // 3001-VOTE-001
cy.get(proposalDocumentationLink) cy.get(proposalDocumentationLink)
@@ -61,62 +54,17 @@ context(
}); });
}); });
// 3007-PNE-021 it.skip('should be able to see button for - new proposal', function () {
it('should have documentation links for network parameter 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); 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) cy.get(connectToVegaWalletButton)
.should('be.visible') .should('be.visible')
.and('have.text', 'Connect Vega wallet'); .and('have.text', 'Connect Vega wallet');
@@ -180,7 +128,6 @@ context(
.first() .first()
.find('[data-testid="view-proposal-btn"]') .find('[data-testid="view-proposal-btn"]')
.click(); .click();
cy.url().should('contain', '/protocol-upgrades/v1');
cy.getByTestId('protocol-upgrade-proposal').within(() => { cy.getByTestId('protocol-upgrade-proposal').within(() => {
cy.get('h1').should('have.text', 'Vega Release v1'); cy.get('h1').should('have.text', 'Vega Release v1');
cy.getByTestId('protocol-upgrade-block-height').should( cy.getByTestId('protocol-upgrade-block-height').should(
@@ -9,9 +9,8 @@ import {
import { import {
enterUniqueFreeFormProposalBody, enterUniqueFreeFormProposalBody,
goToMakeNewProposal, goToMakeNewProposal,
governanceProposalType,
} from '../../support/governance.functions'; } from '../../support/governance.functions';
import { vegaWalletFaucetAssetsWithoutCheck } from '../../support/wallet-functions'; import { vegaWalletFaucetAssetsWithoutCheck } from '../../support/wallet-vega.functions';
const vegaWalletPubKey = Cypress.env('vegaWalletPublicKey2'); const vegaWalletPubKey = Cypress.env('vegaWalletPublicKey2');
const vegaPubkeyTruncated = Cypress.env('vegaWalletPublicKey2Short'); const vegaPubkeyTruncated = Cypress.env('vegaWalletPublicKey2Short');
@@ -51,7 +50,7 @@ context('View functionality with public key', { tags: '@smoke' }, function () {
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.`; 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.`;
navigateTo(navigation.proposals); navigateTo(navigation.proposals);
goToMakeNewProposal(governanceProposalType.FREEFORM); goToMakeNewProposal('Freeform');
enterUniqueFreeFormProposalBody('50', 'pub key proposal test'); enterUniqueFreeFormProposalBody('50', 'pub key proposal test');
cy.getByTestId('dialog-content') cy.getByTestId('dialog-content')
.first() .first()
@@ -1,16 +1,11 @@
/// <reference types="cypress" /> /// <reference types="cypress" />
import { aliasGQLQuery } from '@vegaprotocol/cypress';
import { import {
navigation, navigation,
verifyPageHeader, verifyPageHeader,
verifyTabHighlighted, verifyTabHighlighted,
} from '../../support/common.functions'; } from '../../support/common.functions';
import { import { clickOnValidatorFromList } from '../../support/staking.functions';
clickOnValidatorFromList,
waitForBeginningOfEpoch,
} from '../../support/staking.functions';
import { previousEpochData } from '../../fixtures/mocks/previous-epoch';
const guideLink = '[data-testid="staking-guide-link"]'; const guideLink = '[data-testid="staking-guide-link"]';
const validatorTitle = '[data-testid="validator-node-title"]'; const validatorTitle = '[data-testid="validator-node-title"]';
@@ -32,7 +27,7 @@ const normalisedVotingPowerToolTip =
'[data-testid="normalised-voting-power-tooltip"]'; '[data-testid="normalised-voting-power-tooltip"]';
const performancePenaltyToolTip = '[data-testid="performance-penalty-tooltip"]'; const performancePenaltyToolTip = '[data-testid="performance-penalty-tooltip"]';
const overstakedPenaltyToolTip = '[data-testid="overstaked-penalty-tooltip"]'; const overstakedPenaltyToolTip = '[data-testid="overstaked-penalty-tooltip"]';
const multisigPenaltyToolTip = '[data-testid="multisig-error-tooltip"]'; const totalPenaltyToolTip = '[data-testid="total-penalty-tooltip"]';
const epochCountDown = '[data-testid="epoch-countdown"]'; const epochCountDown = '[data-testid="epoch-countdown"]';
const stakeNumberRegex = /^\d{1,3}(,\d{3})*(\.\d+)?$/; const stakeNumberRegex = /^\d{1,3}(,\d{3})*(\.\d+)?$/;
@@ -42,6 +37,7 @@ context('Validators Page - verify elements on page', function () {
}); });
describe('with wallets disconnected', { tags: '@smoke' }, function () { describe('with wallets disconnected', { tags: '@smoke' }, function () {
describe('description section', function () {
it('Should have validators tab highlighted', function () { it('Should have validators tab highlighted', function () {
verifyTabHighlighted(navigation.validators); verifyTabHighlighted(navigation.validators);
}); });
@@ -51,7 +47,7 @@ context('Validators Page - verify elements on page', function () {
}); });
it('Should have Staking Guide link visible', function () { it('Should have Staking Guide link visible', function () {
// 1002-STKE-003 // 2001-STKE-003
cy.get(guideLink) cy.get(guideLink)
.should('be.visible') .should('be.visible')
.and('have.text', 'Read more about staking on Vega') .and('have.text', 'Read more about staking on Vega')
@@ -61,20 +57,12 @@ context('Validators Page - verify elements on page', function () {
'https://docs.vega.xyz/mainnet/concepts/vega-chain/#staking-on-vega' 'https://docs.vega.xyz/mainnet/concepts/vega-chain/#staking-on-vega'
); );
}); });
// 1002-STKE-032
it('Should have button to connect vega wallet in validator page', function () {
clickOnValidatorFromList(0);
cy.getByTestId('connect-to-vega-wallet-btn').should('be.visible');
cy.visit('/validators');
}); });
});
// 1002-STKE-020 1002-STKE-021 1002-STKE-022 1002-STKE-023 1002-STKE-024
describe( describe(
'Should be able to see validator list from the staking page', 'Should be able to see validator list from the staking page',
{ tags: '@regression' }, { tags: '@regression' },
function () { function () {
// 1002-STKE-050 // 2001-STKE-050
it('Should be able to see validator names', function () { it('Should be able to see validator names', function () {
cy.get('[col-id="validator"] > div > span') cy.get('[col-id="validator"] > div > span')
.should('have.length.at.least', 1) .should('have.length.at.least', 1)
@@ -92,7 +80,6 @@ context('Validators Page - verify elements on page', function () {
}); });
it('Should be able to see validator stake tooltip', function () { it('Should be able to see validator stake tooltip', function () {
waitForBeginningOfEpoch();
cy.getByTestId('total-stake').first().realHover(); cy.getByTestId('total-stake').first().realHover();
cy.get(stakedByOperatorToolTip) cy.get(stakedByOperatorToolTip)
@@ -115,7 +102,6 @@ context('Validators Page - verify elements on page', function () {
}); });
it('Should be able to see validator normalised voting power tooltip', function () { it('Should be able to see validator normalised voting power tooltip', function () {
waitForBeginningOfEpoch();
cy.getByTestId('normalised-voting-power').first().realHover(); cy.getByTestId('normalised-voting-power').first().realHover();
cy.get(unnormalisedVotingPowerToolTip) cy.get(unnormalisedVotingPowerToolTip)
@@ -136,7 +122,6 @@ context('Validators Page - verify elements on page', function () {
}); });
it('Should be able to see validator penalties tooltip', function () { it('Should be able to see validator penalties tooltip', function () {
waitForBeginningOfEpoch();
cy.getByTestId('total-penalty').realHover(); cy.getByTestId('total-penalty').realHover();
cy.get(performancePenaltyToolTip) cy.get(performancePenaltyToolTip)
@@ -145,6 +130,9 @@ context('Validators Page - verify elements on page', function () {
cy.get(overstakedPenaltyToolTip) cy.get(overstakedPenaltyToolTip)
.invoke('text') .invoke('text')
.should('contain', 'Overstaked penalty: 60.00%'); // value not asserted due to #2886 .should('contain', 'Overstaked penalty: 60.00%'); // value not asserted due to #2886
cy.get(totalPenaltyToolTip)
.invoke('text')
.should('contain', 'Total penalties: 60.00%');
}); });
it('Should be able to see validator pending stake', function () { it('Should be able to see validator pending stake', function () {
@@ -154,26 +142,10 @@ context('Validators Page - verify elements on page', function () {
cy.wrap($pendingStake).should('contain.text', '0.00'); cy.wrap($pendingStake).should('contain.text', '0.00');
}); });
}); });
it('Should be able to see multisig error', function () {
cy.mockGQL((req) => {
aliasGQLQuery(req, 'PreviousEpoch', previousEpochData);
});
waitForBeginningOfEpoch();
cy.getByTestId('total-penalty').first().realHover();
cy.get(multisigPenaltyToolTip)
.invoke('text')
.should('contain', 'Multisig penalty: 100%');
cy.getByTestId('total-penalty').eq(1).realHover();
cy.get(multisigPenaltyToolTip)
.invoke('text')
.should('contain', 'Multisig penalty: 100%');
});
} }
); );
// 1002-STKE-050 // 2001-STKE-050
describe( describe(
'Should be able to see static information about a validator', 'Should be able to see static information about a validator',
{ tags: '@smoke' }, { tags: '@smoke' },
@@ -183,32 +155,34 @@ context('Validators Page - verify elements on page', function () {
clickOnValidatorFromList(0); clickOnValidatorFromList(0);
}); });
// 1002-STKE-006 // 2001-STKE-006
it('Should be able to see validator name', function () { it('Should be able to see validator name', function () {
cy.get(validatorTitle).should('not.be.empty'); cy.get(validatorTitle).should('not.be.empty');
}); });
// 1002-STKE-007 // 2001-STKE-007
it('Should be able to see validator id', function () { it('Should be able to see validator id', function () {
cy.get(validatorId).should('not.be.empty'); cy.get(validatorId).should('not.be.empty');
}); });
// 1002-STKE-008 // 2001-STKE-008
it('Should be able to see validator public key', function () { it('Should be able to see validator public key', function () {
cy.get(validatorPubKey).should('not.be.empty'); cy.get(validatorPubKey).should('not.be.empty');
}); });
// 1002-STKE-010 // 2001-STKE-010
it('Should be able to see Ethereum address', function () { it('Should be able to see Ethereum address', function () {
cy.get(ethAddressLink).should('not.be.empty').and('have.attr', 'href'); cy.get(ethAddressLink)
.should('not.be.empty')
.and('have.attr', 'href');
}); });
// TODO validators missing url for more information about them 1002-STKE-09 // TODO validators missing url for more information about them 2001-STKE-09
it('Should be able to see validator status', function () { it('Should be able to see validator status', function () {
cy.get(validatorStatus).should('have.text', 'Consensus'); cy.get(validatorStatus).should('have.text', 'Consensus');
}); });
// 1002-STKE-012 // 2001-STKE-012
it('Should be able to see total stake', function () { it('Should be able to see total stake', function () {
cy.get(totalStake).invoke('text').should('match', stakeNumberRegex); cy.get(totalStake).invoke('text').should('match', stakeNumberRegex);
}); });
@@ -229,7 +203,7 @@ context('Validators Page - verify elements on page', function () {
.should('match', stakeNumberRegex); .should('match', stakeNumberRegex);
}); });
// 1002-STKE-051 // 2001-STKE-051
it('Should be able to see stake share in percentage', function () { it('Should be able to see stake share in percentage', function () {
cy.get(stakeShare) cy.get(stakeShare)
.invoke('text') .invoke('text')
@@ -252,7 +226,7 @@ context('Validators Page - verify elements on page', function () {
}); });
}); });
// 1002-STKE-011 2002-SINC-001 2002-SINC-002 // 2001-STKE-011 2002-SINC-001 2002-SINC-002
it('Should be able to see epoch information', function () { it('Should be able to see epoch information', function () {
const epochTitle = 'h3'; const epochTitle = 'h3';
const nextEpochInfo = 'p'; const nextEpochInfo = 'p';
@@ -265,3 +239,4 @@ context('Validators Page - verify elements on page', function () {
} }
); );
}); });
});
@@ -1,9 +1,7 @@
import { truncateByChars } from '@vegaprotocol/utils'; import { truncateByChars } from '@vegaprotocol/utils';
import { waitForSpinner } from '../../support/common.functions'; import { waitForSpinner } from '../../support/common.functions';
import { import { vegaWalletTeardown } from '../../support/wallet-teardown.functions';
vegaWalletFaucetAssetsWithoutCheck, import { vegaWalletFaucetAssetsWithoutCheck } from '../../support/wallet-vega.functions';
vegaWalletTeardown,
} from '../../support/wallet-functions';
const walletContainer = 'aside [data-testid="vega-wallet"]'; const walletContainer = 'aside [data-testid="vega-wallet"]';
const walletHeader = '[data-testid="wallet-header"] h1'; const walletHeader = '[data-testid="wallet-header"] h1';
@@ -287,28 +285,28 @@ context(
name: 'USDC (fake)', name: 'USDC (fake)',
symbol: 'fUSDC', symbol: 'fUSDC',
amount: '1000000', amount: '1000000',
expectedAmount: 10.0, expectedAmount: '10.00',
}, },
{ {
id: '8566db7257222b5b7ef2886394ad28b938b28680a54a169bbc795027b89d6665', id: '8566db7257222b5b7ef2886394ad28b938b28680a54a169bbc795027b89d6665',
name: 'DAI (fake)', name: 'DAI (fake)',
symbol: 'fDAI', symbol: 'fDAI',
amount: '200000', amount: '200000',
expectedAmount: 2.0, expectedAmount: '2.00',
}, },
{ {
id: '73174a6fb1d5802ba0ac7bd7ab79e0a3a4837b262de0a4e80815a55442692bd0', id: '73174a6fb1d5802ba0ac7bd7ab79e0a3a4837b262de0a4e80815a55442692bd0',
name: 'BTC (fake)', name: 'BTC (fake)',
symbol: 'fBTC', symbol: 'fBTC',
amount: '600000', amount: '600000',
expectedAmount: 6.0, expectedAmount: '6.00',
}, },
{ {
id: 'e02d4c15d790d1d2dffaf2dcd1cf06a1fe656656cf4ed18c8ce99f9e83643567', id: 'e02d4c15d790d1d2dffaf2dcd1cf06a1fe656656cf4ed18c8ce99f9e83643567',
name: 'EURO (fake)', name: 'EURO (fake)',
symbol: 'fEURO', symbol: 'fEURO',
amount: '800000', amount: '800000',
expectedAmount: 8.0, expectedAmount: '8.00',
}, },
]; ];
@@ -319,6 +317,12 @@ context(
cy.reload(); cy.reload();
waitForSpinner(); waitForSpinner();
cy.connectVegaWallet(); cy.connectVegaWallet();
cy.get(walletContainer).within(() => {
cy.getByTestId('currency-title', txTimeout).should(
'have.length.at.least',
5
);
});
}); });
for (const { name, symbol, expectedAmount } of assets) { for (const { name, symbol, expectedAmount } of assets) {
@@ -332,10 +336,9 @@ context(
.contains(name) .contains(name)
.parent() .parent()
.siblings() .siblings()
.then((elementAmount) => { .invoke('text')
const displayedAmount = parseFloat(elementAmount.text()); .then(parseFloat)
expect(displayedAmount).be.gte(expectedAmount); .should('be.gte', parseFloat(expectedAmount));
});
cy.get(vegaWalletCurrencyTitle) cy.get(vegaWalletCurrencyTitle)
.contains(name) .contains(name)
@@ -1,11 +1,8 @@
import { stakingPageDisassociateAllTokens } from './staking.functions';
const tokenDropDown = 'state-trigger'; const tokenDropDown = 'state-trigger';
const txTimeout = Cypress.env('txTimeout'); const txTimeout = Cypress.env('txTimeout');
export enum navigation { export enum navigation {
section = 'nav', section = 'nav',
home = '[href="/"]',
vesting = '[href="/token/redeem"]', vesting = '[href="/token/redeem"]',
validators = '[href="/validators"]', validators = '[href="/validators"]',
rewards = '[href="/rewards"]', rewards = '[href="/rewards"]',
@@ -21,7 +18,6 @@ export function convertTokenValueToNumber(subject: string) {
} }
const topLevelRoutes = [ const topLevelRoutes = [
navigation.home,
navigation.proposals, navigation.proposals,
navigation.validators, navigation.validators,
navigation.rewards, navigation.rewards,
@@ -37,7 +33,7 @@ export function navigateTo(page: navigation) {
}); });
} else { } else {
return cy.get(navigation.section, { timeout: 10000 }).within(() => { return cy.get(navigation.section, { timeout: 10000 }).within(() => {
cy.get(page).eq(0).click({ force: true }); cy.get(page).eq(0).click();
}); });
} }
} }
@@ -101,25 +97,3 @@ export function turnTelemetryOff() {
win.localStorage.setItem('vega_telemetry_on', 'false') 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();
}
});
}
@@ -1,12 +1,8 @@
import { format } from 'date-fns'; import { format } from 'date-fns';
import { import { closeDialog, navigateTo, navigation } from './common.functions';
closeDialog,
navigateTo,
navigation,
waitForSpinner,
} from './common.functions';
import { ensureSpecifiedUnstakedTokensAreAssociated } from './staking.functions'; import { ensureSpecifiedUnstakedTokensAreAssociated } from './staking.functions';
const newProposalButton = '[data-testid="new-proposal-link"]';
const proposalInformationTableRows = '[data-testid="key-value-table-row"]'; const proposalInformationTableRows = '[data-testid="key-value-table-row"]';
const proposalListItem = '[data-testid="proposals-list-item"]'; const proposalListItem = '[data-testid="proposals-list-item"]';
const newProposalTitle = '[data-testid="proposal-title"]'; const newProposalTitle = '[data-testid="proposal-title"]';
@@ -50,56 +46,6 @@ export function enterRawProposalBody(timestamp: number) {
}); });
} }
export function submitUniqueRawProposal(proposalFields: {
proposalBody?: string;
proposalTitle?: string;
proposalDescription?: string;
closingTimestamp?: number;
enactmentTimestamp?: number;
submit?: boolean;
}) {
goToMakeNewProposal(governanceProposalType.RAW);
let proposalBodyPath = 'src/fixtures/proposals/raw.json';
if (proposalFields.proposalBody) {
proposalBodyPath = proposalFields.proposalBody;
}
cy.readFile(proposalBodyPath).then((rawProposal) => {
if (proposalFields.proposalTitle) {
rawProposal.rationale.title = proposalFields.proposalTitle;
cy.wrap(proposalFields.proposalTitle).as('proposalTitle');
}
if (proposalFields.proposalDescription) {
rawProposal.rationale.description = proposalFields.proposalDescription;
}
if (proposalFields.closingTimestamp) {
rawProposal.terms.closingTimestamp = proposalFields.closingTimestamp;
} else if (
!proposalFields.closingTimestamp &&
!proposalFields.proposalBody
) {
const minTimeStamp = createTenDigitUnixTimeStampForSpecifiedDays(2);
rawProposal.terms.closingTimestamp = minTimeStamp;
}
if (proposalFields.enactmentTimestamp) {
rawProposal.terms.enactmentTimestamp = proposalFields.enactmentTimestamp;
}
const proposalPayload = JSON.stringify(rawProposal);
cy.get(rawProposalData).type(proposalPayload, {
parseSpecialCharSequences: false,
delay: 2,
});
if (proposalFields.submit !== false) {
cy.get(newProposalSubmitButton).should('be.visible').click();
cy.wrap(rawProposal).as('rawProposal');
waitForProposalSubmitted();
waitForProposalSync();
navigateTo(navigation.proposals);
}
});
}
export function enterUniqueFreeFormProposalBody( export function enterUniqueFreeFormProposalBody(
timestamp: string, timestamp: string,
proposalTitle: string proposalTitle: string
@@ -109,11 +55,7 @@ export function enterUniqueFreeFormProposalBody(
'this is a e2e freeform proposal description' 'this is a e2e freeform proposal description'
); );
cy.get(proposalVoteDeadline).clear().click().type(timestamp); 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) {
return cy.contains(proposalTitle).parentsUntil(proposalListItem).last();
} }
export function getSubmittedProposalFromProposalList(proposalTitle: string) { export function getSubmittedProposalFromProposalList(proposalTitle: string) {
@@ -178,20 +120,15 @@ export function waitForProposalSync() {
}); });
} }
export function goToMakeNewProposal(proposalType: governanceProposalType) { export function goToMakeNewProposal(proposalType: string) {
cy.visit('/proposals/propose'); navigateTo(navigation.proposals);
waitForSpinner(); cy.get(newProposalButton).should('be.visible').click();
cy.url().should('include', '/proposals/propose'); cy.url().should('include', '/proposals/propose');
cy.get(navigation.pageSpinner, { timeout: 20000 }).should('not.exist'); cy.get(navigation.pageSpinner, { timeout: 20000 }).should('not.exist');
if (proposalType == governanceProposalType.RAW) {
cy.get('[href="/proposals/propose/raw"]').click();
} else {
cy.get('li').should('contain.text', proposalType).and('be.visible'); cy.get('li').should('contain.text', proposalType).and('be.visible');
cy.get('li').contains(proposalType).click(); cy.get('li').contains(proposalType).click();
} }
}
// 3001-VOTE-013 3001-VOTE-014
export function waitForProposalSubmitted() { export function waitForProposalSubmitted() {
cy.contains('Awaiting network confirmation', epochTimeout).should( cy.contains('Awaiting network confirmation', epochTimeout).should(
'be.visible' 'be.visible'
@@ -226,29 +163,11 @@ export function createFreeformProposal(proposalTitle: string) {
navigateTo(navigation.proposals); navigateTo(navigation.proposals);
} }
export function getDownloadedProposalJsonPath(proposalType: string) { export const governanceProposalType = {
const downloadPath = './cypress/downloads/'; NETWORK_PARAMETER: 'Network parameter',
const filepath = downloadPath + proposalType + getFormattedTime() + '.json'; NEW_MARKET: 'New market',
return filepath; UPDATE_MARKET: 'Update market',
} NEW_ASSET: 'New asset',
FREEFORM: 'Freeform',
function getFormattedTime() { RAW: 'raw proposal',
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',
UPDATE_MARKET = 'Update market',
NEW_ASSET = 'New asset',
UPDATE_ASSET = 'Update asset',
FREEFORM = 'Freeform',
RAW = 'raw proposal',
}
+2 -1
View File
@@ -5,7 +5,8 @@ import './common.functions.ts';
import './staking.functions.ts'; import './staking.functions.ts';
import './governance.functions.ts'; import './governance.functions.ts';
import './wallet-eth.functions.ts'; import './wallet-eth.functions.ts';
import './wallet-functions.ts'; import './wallet-teardown.functions.ts';
import './wallet-vega.functions.ts';
import './proposal.functions.ts'; import './proposal.functions.ts';
import 'cypress-mochawesome-reporter/register'; import 'cypress-mochawesome-reporter/register';
import registerCypressGrep from '@cypress/grep'; import registerCypressGrep from '@cypress/grep';
@@ -85,132 +85,6 @@ export function createFreeFormProposalTxBody(): ProposalSubmissionBody {
}; };
} }
export function createNewMarketProposalTxBody(): ProposalSubmissionBody {
const MIN_CLOSE_SEC = 5;
const MIN_ENACT_SEC = 7;
const closingDate = addSeconds(new Date(), MIN_CLOSE_SEC);
const enactmentDate = addSeconds(closingDate, MIN_ENACT_SEC);
const closingTimestamp = millisecondsToSeconds(closingDate.getTime());
const enactmentTimestamp = millisecondsToSeconds(enactmentDate.getTime());
return {
proposalSubmission: {
rationale: {
title: 'New Market Proposal E2E submission',
description: 'E2E new market proposal',
},
terms: {
newMarket: {
changes: {
decimalPlaces: '5',
positionDecimalPlaces: '5',
linearSlippageFactor: '0.001',
quadraticSlippageFactor: '0',
lpPriceRange: '10',
instrument: {
name: 'Token test market',
code: 'TEST.24h',
future: {
settlementAsset:
'73174a6fb1d5802ba0ac7bd7ab79e0a3a4837b262de0a4e80815a55442692bd0',
quoteName: 'fBTC',
dataSourceSpecForSettlementData: {
external: {
oracle: {
signers: [
{
pubKey: {
key: '70d14a321e02e71992fd115563df765000ccc4775cbe71a0e2f9ff5a3b9dc680',
},
},
],
filters: [
{
key: {
name: 'prices.ETH.value',
type: 'TYPE_INTEGER' as const,
numberDecimalPlaces: '0',
},
conditions: [
{
operator: 'OPERATOR_GREATER_THAN' as const,
value: '0',
},
],
},
],
},
},
},
dataSourceSpecForTradingTermination: {
external: {
oracle: {
signers: [
{
pubKey: {
key: '70d14a321e02e71992fd115563df765000ccc4775cbe71a0e2f9ff5a3b9dc680',
},
},
],
filters: [
{
key: {
name: 'trading.terminated.ETH5',
type: 'TYPE_BOOLEAN' as const,
},
conditions: [
{
operator: 'OPERATOR_EQUALS' as const,
value: 'true',
},
],
},
],
},
},
},
dataSourceSpecBinding: {
settlementDataProperty: 'prices.ETH.value',
tradingTerminationProperty: 'trading.terminated.ETH5',
},
},
},
metadata: ['sector:energy', 'sector:tech', 'source:docs.vega.xyz'],
priceMonitoringParameters: {
triggers: [
{
horizon: '43200',
probability: '0.9999999',
auctionExtension: '600',
},
],
},
liquidityMonitoringParameters: {
targetStakeParameters: {
timeWindow: '3600',
scalingFactor: 10,
},
triggeringRatio: '0.7',
auctionExtension: '1',
},
logNormal: {
tau: 0.0001140771161,
riskAversionParameter: 0.01,
params: {
mu: 0,
r: 0.016,
sigma: 0.5,
},
},
},
},
closingTimestamp,
enactmentTimestamp,
},
},
};
}
export function mockNetworkUpgradeProposal() { export function mockNetworkUpgradeProposal() {
cy.mockGQL((req) => { cy.mockGQL((req) => {
aliasGQLQuery(req, 'Nodes', nodeData); aliasGQLQuery(req, 'Nodes', nodeData);
@@ -1,5 +1,5 @@
import { closeDialog } from './common.functions'; import { closeDialog } from './common.functions';
import { vegaWalletTeardown } from './wallet-functions'; import { vegaWalletTeardown } from './wallet-teardown.functions';
const tokenAmountInputBox = '[data-testid="token-amount-input"]'; const tokenAmountInputBox = '[data-testid="token-amount-input"]';
const tokenSubmitButton = '[data-testid="token-input-submit-button"]'; const tokenSubmitButton = '[data-testid="token-input-submit-button"]';
@@ -221,7 +221,7 @@ export function ensureSpecifiedUnstakedTokensAreAssociated(
} }
export function closeStakingDialog() { export function closeStakingDialog() {
cy.getByTestId('dialog-title', txTimeout).should( cy.getByTestId('dialog-title').should(
'contain.text', 'contain.text',
'At the beginning of the next epoch' 'At the beginning of the next epoch'
); );
@@ -236,8 +236,8 @@ export function validateWalletCurrency(
currencyTitle: string, currencyTitle: string,
expectedAmount: string expectedAmount: string
) { ) {
cy.get("[data-testid='currency-title']", txTimeout) cy.get("[data-testid='currency-title']")
.contains(currencyTitle, txTimeout) .contains(currencyTitle)
.parent() .parent()
.parent() .parent()
.within(() => { .within(() => {
@@ -5,7 +5,7 @@ const capsuleWalletConnectButton = '[data-testid="web3-connector-Unknown"]';
export function ethereumWalletConnect() { export function ethereumWalletConnect() {
cy.highlight('Connecting Eth Wallet'); cy.highlight('Connecting Eth Wallet');
cy.get(connectToEthButton, { timeout: 60000 }).within(() => { cy.get(connectToEthButton).within(() => {
cy.contains('Connect Ethereum wallet to associate $VEGA') cy.contains('Connect Ethereum wallet to associate $VEGA')
.should('be.visible') .should('be.visible')
.click(); .click();
@@ -19,7 +19,7 @@ const ethStakingBridgeContractAddress = Cypress.env(
); );
const ethProviderUrl = Cypress.env('ethProviderUrl'); const ethProviderUrl = Cypress.env('ethProviderUrl');
const getAccount = (number = 0) => `m/44'/60'/0'/0/${number}`; const getAccount = (number = 0) => `m/44'/60'/0'/0/${number}`;
const transactionTimeout = { timeout: 100000, log: false }; const transactionTimeout = 100000;
const Erc20BridgeAddress = '0x9708FF7510D4A7B9541e1699d15b53Ecb1AFDc54'; const Erc20BridgeAddress = '0x9708FF7510D4A7B9541e1699d15b53Ecb1AFDc54';
const provider = new ethers.providers.JsonRpcProvider({ url: ethProviderUrl }); const provider = new ethers.providers.JsonRpcProvider({ url: ethProviderUrl });
@@ -43,7 +43,10 @@ export async function depositAsset(
const faucet = new Token(assetEthAddress, signer); const faucet = new Token(assetEthAddress, signer);
cy.wrap( cy.wrap(
faucet.approve(Erc20BridgeAddress, amount + '0'.repeat(decimalPlaces + 1)), faucet.approve(Erc20BridgeAddress, amount + '0'.repeat(decimalPlaces + 1)),
transactionTimeout {
timeout: transactionTimeout,
log: false,
}
).then(() => { ).then(() => {
const collateralBridge = new CollateralBridge(Erc20BridgeAddress, signer); const collateralBridge = new CollateralBridge(Erc20BridgeAddress, signer);
cy.wrap( cy.wrap(
@@ -52,7 +55,7 @@ export async function depositAsset(
amount + '0'.repeat(decimalPlaces), amount + '0'.repeat(decimalPlaces),
'0x' + vegaWalletPubKey '0x' + vegaWalletPubKey
), ),
transactionTimeout { timeout: transactionTimeout, log: false }
); );
}); });
} }
@@ -64,6 +67,7 @@ export async function faucetAsset(assetEthAddress: string) {
export async function vegaWalletTeardown() { export async function vegaWalletTeardown() {
cy.get(associatedAmountInWallet) cy.get(associatedAmountInWallet)
.should('be.visible')
.invoke('text') .invoke('text')
.then((associatedAmount) => { .then((associatedAmount) => {
cy.get('body').then(($body) => { cy.get('body').then(($body) => {
@@ -76,13 +80,11 @@ export async function vegaWalletTeardown() {
} }
}); });
cy.get(vegaWalletContainer).within(() => { cy.get(vegaWalletContainer).within(() => {
cy.get(associatedAmountInWallet, transactionTimeout).should( cy.get(associatedAmountInWallet, {
'have.length', timeout: transactionTimeout,
1 }).contains('0.00', {
); timeout: transactionTimeout,
cy.get(associatedAmountInWallet) });
.first(transactionTimeout)
.should('have.text', '0.00');
}); });
}); });
} }
@@ -106,7 +108,7 @@ async function vegaWalletTeardownStaking(stakingBridgeContract: StakingBridge) {
cy.highlight('Tearing down staking tokens from vega wallet if present'); cy.highlight('Tearing down staking tokens from vega wallet if present');
cy.wrap( cy.wrap(
stakingBridgeContract.stake_balance(ethWalletPubKey, vegaWalletPubKey), stakingBridgeContract.stake_balance(ethWalletPubKey, vegaWalletPubKey),
transactionTimeout { timeout: transactionTimeout }
).then((stakeBalance) => { ).then((stakeBalance) => {
if (Number(stakeBalance) != 0) { if (Number(stakeBalance) != 0) {
cy.get(vegaWalletContainer).within(() => { cy.get(vegaWalletContainer).within(() => {
@@ -119,25 +121,31 @@ async function vegaWalletTeardownStaking(stakingBridgeContract: StakingBridge) {
String(stakeBalance), String(stakeBalance),
vegaWalletPubKey vegaWalletPubKey
), ),
transactionTimeout { timeout: transactionTimeout }
); );
cy.wrap( cy.wrap(
vestingContract.stake_balance(ethWalletPubKey, vegaWalletPubKey), vestingContract.stake_balance(ethWalletPubKey, vegaWalletPubKey),
transactionTimeout {
timeout: transactionTimeout,
log: false,
}
).then((vestingAmount) => { ).then((vestingAmount) => {
if (Number(vestingAmount) != 0) { if (Number(vestingAmount) != 0) {
cy.contains('Associated', transactionTimeout) cy.contains('Associated', {
timeout: transactionTimeout,
})
.parent() .parent()
.parent() .parent()
.within(() => { .within(() => {
cy.getByTestId('currency-value', transactionTimeout) cy.getByTestId('currency-value', {
.first() timeout: transactionTimeout,
})
.should('have.length', 1)
.invoke('text') .invoke('text')
.as('displayedAmount'); .as('displayedAmount');
cy.get('@displayedAmount', transactionTimeout).should( cy.get('@displayedAmount', {
'not.eq', timeout: transactionTimeout,
$associatedAmount }).should('not.eq', $associatedAmount);
);
}); });
} }
}); });
@@ -149,14 +157,14 @@ async function vegaWalletTeardownStaking(stakingBridgeContract: StakingBridge) {
async function vegaWalletTeardownVesting(vestingContract: TokenVesting) { async function vegaWalletTeardownVesting(vestingContract: TokenVesting) {
cy.highlight('Tearing down vesting tokens from vega wallet if present'); cy.highlight('Tearing down vesting tokens from vega wallet if present');
cy.wrap( cy.wrap(vestingContract.stake_balance(ethWalletPubKey, vegaWalletPubKey), {
vestingContract.stake_balance(ethWalletPubKey, vegaWalletPubKey), timeout: transactionTimeout,
transactionTimeout log: false,
).then((vestingAmount) => { }).then((vestingAmount) => {
if (Number(vestingAmount) != 0) { if (Number(vestingAmount) != 0) {
cy.wrap( cy.wrap(
vestingContract.remove_stake(String(vestingAmount), vegaWalletPubKey), vestingContract.remove_stake(String(vestingAmount), vegaWalletPubKey),
transactionTimeout { timeout: transactionTimeout }
); );
} }
}); });
@@ -173,27 +181,3 @@ export async function vegaWalletDisassociate(amount: string) {
amount = amount + '0'.repeat(18); amount = amount + '0'.repeat(18);
stakingBridgeContract.remove_stake(amount, vegaWalletPubKey); stakingBridgeContract.remove_stake(amount, vegaWalletPubKey);
} }
export function vegaWalletFaucetAssetsWithoutCheck(
asset: string,
amount: string,
vegaWalletPublicKey: string
) {
cy.highlight(`Topping up vega wallet with ${asset}, amount: ${amount}`);
cy.exec(
`curl -X POST -d '{"amount": "${amount}", "asset": "${asset}", "party": "${vegaWalletPublicKey}"}' http://localhost:1790/api/v1/mint`
)
.its('stdout')
.then((response) => {
assert.include(
response,
`"success":true`,
'Ensuring curl command was successfully undertaken'
);
});
}
export function switchVegaWalletPubKey() {
cy.get('[data-testid="manage-vega-wallet"]:visible').click();
cy.get('[data-testid="select-keypair-button"]').eq(0).click();
}
@@ -0,0 +1,18 @@
export function vegaWalletFaucetAssetsWithoutCheck(
asset: string,
amount: string,
vegaWalletPublicKey: string
) {
cy.highlight(`Topping up vega wallet with ${asset}, amount: ${amount}`);
cy.exec(
`curl -X POST -d '{"amount": "${amount}", "asset": "${asset}", "party": "${vegaWalletPublicKey}"}' http://localhost:1790/api/v1/mint`
)
.its('stdout')
.then((response) => {
assert.include(
response,
`"success":true`,
'Ensuring curl command was successfully undertaken'
);
});
}
+2 -4
View File
@@ -5,18 +5,16 @@ NX_VEGA_URL=https://api.n00.stagnet1.vega.xyz/graphql
NX_ETHEREUM_PROVIDER_URL=https://sepolia.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8 NX_ETHEREUM_PROVIDER_URL=https://sepolia.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8
NX_ETHERSCAN_URL=https://sepolia.etherscan.io NX_ETHERSCAN_URL=https://sepolia.etherscan.io
NX_FAIRGROUND=false 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_GITHUB_FEEDBACK_URL=https://github.com/vegaprotocol/feedback/discussions
NX_VEGA_WALLET_URL=http://localhost:1789 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_HOSTED_WALLET_URL=https://wallet.testnet.vega.xyz
NX_VEGA_DOCS_URL=https://docs.vega.xyz/testnet NX_VEGA_DOCS_URL=https://docs.vega.xyz/testnet
NX_DELEGATIONS_PAGINATION=50 NX_DELEGATIONS_PAGINATION=50
NX_TRANCHES_SERVICE_URL=https://tranches-stagnet1-k8s.ops.vega.xyz NX_TRANCHES_SERVICE_URL=https://tranches-stagnet1-k8s.ops.vega.xyz
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/main/announcements.json NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/main/announcements.json
NX_WALLETCONNECT_PROJECT_ID=fe8091dc35738863e509fc4947525c72 NX_WALLETCONNECT_PROJECT_ID=fe8091dc35738863e509fc4947525c72
NX_VEGA_REST_URL=https://api.n00.stagnet1.vega.xyz/api/v2/
#Test configuration variables #Test configuration variables
CYPRESS_FAIRGROUND=false CYPRESS_FAIRGROUND=false
-1
View File
@@ -17,7 +17,6 @@ NX_VEGA_WALLET_URL=http://localhost:1789
NX_VEGA_DOCS_URL=https://docs.vega.xyz/testnet NX_VEGA_DOCS_URL=https://docs.vega.xyz/testnet
NX_DELEGATIONS_PAGINATION=50 NX_DELEGATIONS_PAGINATION=50
NX_TRANCHES_SERVICE_URL=https://tranches-stagnet1-k8s.ops.vega.xyz NX_TRANCHES_SERVICE_URL=https://tranches-stagnet1-k8s.ops.vega.xyz
NX_VEGA_REST_URL=http://localhost:3008/api/v2/
#Test configuration variables #Test configuration variables
CYPRESS_FAIRGROUND=false CYPRESS_FAIRGROUND=false
+1 -3
View File
@@ -6,10 +6,8 @@ NX_VEGA_NETWORKS='{"DEVNET":"https://dev.governance.vega.xyz","TESTNET":"https:/
NX_ETHEREUM_PROVIDER_URL=https://sepolia.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8 NX_ETHEREUM_PROVIDER_URL=https://sepolia.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8
NX_ETHERSCAN_URL=https://sepolia.etherscan.io NX_ETHERSCAN_URL=https://sepolia.etherscan.io
NX_GITHUB_FEEDBACK_URL=https://github.com/vegaprotocol/feedback/discussions 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_VEGA_DOCS_URL=https://docs.vega.xyz/testnet
NX_DELEGATIONS_PAGINATION=50 NX_DELEGATIONS_PAGINATION=50
NX_TRANCHES_SERVICE_URL=https://tranches-devnet1-k8s.ops.vega.xyz 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_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 -3
View File
@@ -3,14 +3,12 @@ NX_VEGA_ENV=MAINNET
NX_VEGA_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/networks/master/mainnet1/mainnet1.toml NX_VEGA_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/networks/master/mainnet1/mainnet1.toml
NX_VEGA_URL=https://api.vega.community/graphql NX_VEGA_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_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_ETHERSCAN_URL=https://etherscan.io
NX_GITHUB_FEEDBACK_URL=https://github.com/vegaprotocol/feedback/discussions 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_EXPLORER_URL=https://explorer.vega.xyz
NX_VEGA_DOCS_URL=https://docs.vega.xyz/mainnet NX_VEGA_DOCS_URL=https://docs.vega.xyz/mainnet
NX_SENTRY_DSN=https://4b8c8a8ba07742648aa4dfe1b8d17e40:87edc2605e544f888305d7fc4a9141bd@o286262.ingest.sentry.io/5882996 NX_SENTRY_DSN=https://4b8c8a8ba07742648aa4dfe1b8d17e40:87edc2605e544f888305d7fc4a9141bd@o286262.ingest.sentry.io/5882996
NX_DELEGATIONS_PAGINATION=50 NX_DELEGATIONS_PAGINATION=50
NX_TRANCHES_SERVICE_URL=https://tranches-mainnet-k8s.ops.vega.xyz NX_TRANCHES_SERVICE_URL=https://tranches-mainnet-k8s.ops.vega.xyz
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/mainnet/announcements.json NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/mainnet/announcements.json
NX_VEGA_REST_URL=https://api.vega.community/api/v2/
+2 -3
View File
@@ -1,11 +1,10 @@
# App configuration variables # App configuration variables
NX_VEGA_URL=https://api.n00.stagnet1.vega.xyz/graphql NX_VEGA_URL=https://api.n00.stagnet1.vega.xyz/graphql
NX_VEGA_ENV=STAGNET1 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_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_VEGA_DOCS_URL=https://docs.vega.xyz/testnet
NX_DELEGATIONS_PAGINATION=50 NX_DELEGATIONS_PAGINATION=50
NX_TRANCHES_SERVICE_URL=https://tranches-stagnet1-k8s.ops.vega.xyz NX_TRANCHES_SERVICE_URL=https://tranches-stagnet1-k8s.ops.vega.xyz
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json
NX_VEGA_REST_URL=https://api.n00.stagnet1.vega.xyz/api/v2/
-2
View File
@@ -12,5 +12,3 @@ NX_HOSTED_WALLET_URL=https://wallet.testnet.vega.xyz
NX_DELEGATIONS_PAGINATION=50 NX_DELEGATIONS_PAGINATION=50
NX_TRANCHES_SERVICE_URL=https://tranches-testnet-k8s.ops.vega.xyz 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_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
@@ -7,7 +7,5 @@ NX_VEGA_NETWORKS='{"DEVNET":"https://dev.governance.vega.xyz","TESTNET":"https:/
NX_ETHEREUM_PROVIDER_URL=https://sepolia.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8 NX_ETHEREUM_PROVIDER_URL=https://sepolia.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8
NX_ETHERSCAN_URL=https://sepolia.etherscan.io NX_ETHERSCAN_URL=https://sepolia.etherscan.io
NX_GITHUB_FEEDBACK_URL=https://github.com/vegaprotocol/feedback/discussions 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_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
@@ -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, useAppState,
} from '../../contexts/app-state/app-state-context'; } from '../../contexts/app-state/app-state-context';
import { Connectors } from '../../lib/vega-connectors'; import { Connectors } from '../../lib/vega-connectors';
import { RiskMessage } from './risk-message';
export const VegaWalletDialogs = () => { export const VegaWalletDialogs = () => {
const { appState, appDispatch } = useAppState(); const { appState, appDispatch } = useAppState();
return ( return (
<> <>
<VegaConnectDialog <VegaConnectDialog
@@ -19,7 +17,6 @@ export const VegaWalletDialogs = () => {
isOpen: open, isOpen: open,
}) })
} }
riskMessage={<RiskMessage />}
/> />
<VegaManageDialog <VegaManageDialog
-1
View File
@@ -62,7 +62,6 @@ export const ENV = {
ethWalletMnemonic: windowOrDefault('NX_ETH_WALLET_MNEMONIC'), ethWalletMnemonic: windowOrDefault('NX_ETH_WALLET_MNEMONIC'),
localProviderUrl: windowOrDefault('NX_LOCAL_PROVIDER_URL'), localProviderUrl: windowOrDefault('NX_LOCAL_PROVIDER_URL'),
delegationsPagination: windowOrDefault('NX_DELEGATIONS_PAGINATION'), delegationsPagination: windowOrDefault('NX_DELEGATIONS_PAGINATION'),
rest: windowOrDefault('NX_VEGA_REST_URL'),
flags: { flags: {
NETWORK_DOWN: TRUTHY.includes(windowOrDefault('NX_NETWORK_DOWN')), NETWORK_DOWN: TRUTHY.includes(windowOrDefault('NX_NETWORK_DOWN')),
MOCK: TRUTHY.includes(windowOrDefault('NX_MOCKED')), MOCK: TRUTHY.includes(windowOrDefault('NX_MOCKED')),
@@ -3,32 +3,32 @@ import { usePendingBalancesStore } from './use-pending-balances-manager';
import type { Contract } from 'ethers'; import type { Contract } from 'ethers';
import { useListenForPendingEthEvents } from './use-listen-for-pending-eth-events'; import { useListenForPendingEthEvents } from './use-listen-for-pending-eth-events';
import { prepend0x } from '@vegaprotocol/smart-contracts'; import { prepend0x } from '@vegaprotocol/smart-contracts';
import { useWeb3React } from '@web3-react/core';
export const useListenForStakingEvents = ( export const useListenForStakingEvents = (
contract: Contract | undefined, contract: Contract | undefined,
vegaPublicKey: string | null, vegaPublicKey: string | null,
numberOfConfirmations: number numberOfConfirmations: number
) => { ) => {
const { account } = useWeb3React();
const { addPendingTxs, removePendingTx, resetPendingTxs } = const { addPendingTxs, removePendingTx, resetPendingTxs } =
usePendingBalancesStore((state) => ({ usePendingBalancesStore((state) => ({
addPendingTxs: state.addPendingTxs, addPendingTxs: state.addPendingTxs,
removePendingTx: state.removePendingTx, removePendingTx: state.removePendingTx,
resetPendingTxs: state.resetPendingTxs, resetPendingTxs: state.resetPendingTxs,
})); }));
const addFilter = useMemo(() => { const addFilter = useMemo(
if (!account || !vegaPublicKey || !contract) return null; () =>
return contract.filters.Stake_Deposited( vegaPublicKey && contract
null, ? contract.filters.Stake_Deposited(null, null, prepend0x(vegaPublicKey))
null, : null,
prepend0x(vegaPublicKey) [contract, vegaPublicKey]
);
const removeFilter = useMemo(
() =>
vegaPublicKey && contract
? contract.filters.Stake_Removed(null, null, prepend0x(vegaPublicKey))
: null,
[contract, 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]);
/** /**
* Listen for all add stake events * Listen for all add stake events
+4 -22
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", "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", "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", "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", "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.", "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}}", "Redeem unlocked VEGA from tranche {{id}}": "Redeem unlocked $VEGA from tranche {{id}}",
@@ -596,8 +596,6 @@
"noPercentage": "No percentage", "noPercentage": "No percentage",
"proposalJson": "Full proposal JSON", "proposalJson": "Full proposal JSON",
"proposalDetails": "Proposal details", "proposalDetails": "Proposal details",
"marketSpecification": "Market specification",
"viewMarketJson": "View market JSON",
"proposalDescription": "Description", "proposalDescription": "Description",
"currentlySetTo": "Currently expected to ", "currentlySetTo": "Currently expected to ",
"currently": "currently", "currently": "currently",
@@ -730,12 +728,8 @@
"ThisWillSetEnactmentDeadlineTo": "This will set the enactment date to", "ThisWillSetEnactmentDeadlineTo": "This will set the enactment date to",
"ThisWillSetValidationDeadlineTo": "This will set the validation deadline to", "ThisWillSetValidationDeadlineTo": "This will set the validation deadline to",
"Hours": "hours", "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": "The proposal will fail if enactment is earlier than the voting deadline", "ProposalWillFailIfEnactmentIsEarlierThanVotingDeadline": "Proposal will fail if enactment is earlier than the voting deadline",
"ProposalWillFailIfEnactmentIsBelowTheMinimumDeadline": "The proposal will fail if enactment deadline is below the minimum",
"ProposalWillFailIfVotingIsBelowTheMinimumDeadline": "The proposal will fail if voting deadline is below the minimum",
"ProposalWillFailIfEnactmentIsAboveTheMaximumDeadline": "The proposal will fail if enactment deadline is above the maximum",
"ProposalWillFailIfVotingIsAboveTheMaximumDeadline": "The proposal will fail if voting deadline is above the maximum",
"SelectAMarketToChange": "Select a market to change", "SelectAMarketToChange": "Select a market to change",
"MarketName": "Market name", "MarketName": "Market name",
"MarketCode": "Market code", "MarketCode": "Market code",
@@ -786,7 +780,6 @@
"performancePenalty": "Performance penalty", "performancePenalty": "Performance penalty",
"overstaked": "Overstaked", "overstaked": "Overstaked",
"overstakedPenalty": "Overstaked penalty", "overstakedPenalty": "Overstaked penalty",
"multisigPenalty": "Multisig penalty",
"homeProposalsIntro": "Decisions on the Vega network are on-chain, with tokenholders creating proposals that other tokenholders vote to approve or reject. Network upgrades are proposed and approved by validators.", "homeProposalsIntro": "Decisions on the Vega network are on-chain, with tokenholders creating proposals that other tokenholders vote to approve or reject. Network upgrades are proposed and approved by validators.",
"homeProposalsButtonText": "Browse, vote, and propose", "homeProposalsButtonText": "Browse, vote, and propose",
"homeValidatorsIntro": "Vega runs on a delegated proof of stake blockchain, where validators earn fees for validating block transactions. Tokenholders can nominate validators by staking tokens to them.", "homeValidatorsIntro": "Vega runs on a delegated proof of stake blockchain, where validators earn fees for validating block transactions. Tokenholders can nominate validators by staking tokens to them.",
@@ -822,16 +815,5 @@
"NoThanks": "No thanks", "NoThanks": "No thanks",
"ShareData": "Share data", "ShareData": "Share data",
"ContinueSharingData": "Continue sharing data", "ContinueSharingData": "Continue sharing data",
"NodeUnsuitable": "Node: {{url}} is unsuitable", "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",
"AllValidators": "All validators",
"AllProposals": "All proposals"
} }
@@ -1,6 +0,0 @@
import classnames from 'classnames';
export const collapsibleToggleStyles = (toggleState: boolean) =>
classnames('mb-4 transition-transform ease-in-out duration-300', {
'rotate-180': toggleState,
});
@@ -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;
+6 -18
View File
@@ -27,10 +27,6 @@ import type { ProposalFieldsFragment } from '../proposals/proposals/__generated_
import type { NodesFragmentFragment } from '../staking/home/__generated__/Nodes'; import type { NodesFragmentFragment } from '../staking/home/__generated__/Nodes';
import type { ProtocolUpgradeProposalFieldsFragment } from '@vegaprotocol/proposals'; import type { ProtocolUpgradeProposalFieldsFragment } from '@vegaprotocol/proposals';
import { useProtocolUpgradeProposalsQuery } from '@vegaprotocol/proposals'; import { useProtocolUpgradeProposalsQuery } from '@vegaprotocol/proposals';
import {
orderByDate,
orderByUpgradeBlockHeight,
} from '../proposals/components/proposals-list/proposals-list';
const nodesToShow = 6; const nodesToShow = 6;
@@ -204,20 +200,17 @@ const GovernanceHome = ({ name }: RouteChildProps) => {
const proposals = useMemo( const proposals = useMemo(
() => () =>
proposalsData proposalsData
? getNotRejectedProposals(proposalsData.proposalsConnection) ? getNotRejectedProposals<ProposalFieldsFragment>(
proposalsData.proposalsConnection
)
: [], : [],
[proposalsData] [proposalsData]
); );
const sortedProposals = useMemo(
() => orderByDate(proposals).reverse(),
[proposals]
);
const protocolUpgradeProposals = useMemo( const protocolUpgradeProposals = useMemo(
() => () =>
protocolUpgradesData protocolUpgradesData
? getNotRejectedProtocolUpgradeProposals( ? getNotRejectedProtocolUpgradeProposals<ProtocolUpgradeProposalFieldsFragment>(
protocolUpgradesData.protocolUpgradeProposals protocolUpgradesData.protocolUpgradeProposals
).filter( ).filter(
(p) => (p) =>
@@ -228,20 +221,15 @@ const GovernanceHome = ({ name }: RouteChildProps) => {
[protocolUpgradesData] [protocolUpgradesData]
); );
const sortedProtocolUpgradeProposals = useMemo(
() => orderByUpgradeBlockHeight(protocolUpgradeProposals),
[protocolUpgradeProposals]
);
const totalProposalsDesired = 4; const totalProposalsDesired = 4;
const protocolUpgradeProposalsToShow = sortedProtocolUpgradeProposals.slice( const protocolUpgradeProposalsToShow = protocolUpgradeProposals.slice(
0, 0,
totalProposalsDesired totalProposalsDesired
); );
const proposalsToShow = const proposalsToShow =
protocolUpgradeProposalsToShow.length === totalProposalsDesired protocolUpgradeProposalsToShow.length === totalProposalsDesired
? [] ? []
: sortedProposals.slice( : proposals.slice(
0, 0,
totalProposalsDesired - protocolUpgradeProposalsToShow.length totalProposalsDesired - protocolUpgradeProposalsToShow.length
); );
@@ -1,9 +1,9 @@
import ReactMarkdown from 'react-markdown'; import ReactMarkdown from 'react-markdown';
import classnames from 'classnames';
import { useState } from 'react'; import { useState } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { Icon, RoundedWrapper } from '@vegaprotocol/ui-toolkit'; import { Icon, RoundedWrapper } from '@vegaprotocol/ui-toolkit';
import { SubHeading } from '../../../../components/heading'; import { SubHeading } from '../../../../components/heading';
import { collapsibleToggleStyles } from '../../../../lib/collapsible-toggle-styles';
export const ProposalDescription = ({ export const ProposalDescription = ({
description, description,
@@ -12,6 +12,9 @@ export const ProposalDescription = ({
}) => { }) => {
const { t } = useTranslation(); const { t } = useTranslation();
const [showDescription, setShowDescription] = useState(false); const [showDescription, setShowDescription] = useState(false);
const showDescriptionIconClasses = classnames('mb-4', {
'rotate-180': showDescription,
});
return ( return (
<section data-testid="proposal-description"> <section data-testid="proposal-description">
@@ -21,7 +24,7 @@ export const ProposalDescription = ({
> >
<div className="flex items-center gap-3"> <div className="flex items-center gap-3">
<SubHeading title={t('proposalDescription')} /> <SubHeading title={t('proposalDescription')} />
<div className={collapsibleToggleStyles(showDescription)}> <div className={showDescriptionIconClasses}>
<Icon name="chevron-down" size={8} /> <Icon name="chevron-down" size={8} />
</div> </div>
</div> </div>
@@ -124,7 +124,7 @@ describe('Proposal header', () => {
}) })
); );
expect(screen.getByTestId('proposal-title')).toHaveTextContent( expect(screen.getByTestId('proposal-title')).toHaveTextContent(
'New asset proposal' 'Unknown proposal'
); );
expect(screen.getByTestId('proposal-type')).toHaveTextContent('New asset'); expect(screen.getByTestId('proposal-type')).toHaveTextContent('New asset');
expect(screen.getByTestId('proposal-details')).toHaveTextContent( expect(screen.getByTestId('proposal-details')).toHaveTextContent(
@@ -21,7 +21,6 @@ export const ProposalHeader = ({
let details: ReactNode; let details: ReactNode;
let proposalType = ''; let proposalType = '';
let fallbackTitle = '';
const title = proposal?.rationale.title.trim(); const title = proposal?.rationale.title.trim();
@@ -30,7 +29,6 @@ export const ProposalHeader = ({
switch (change?.__typename) { switch (change?.__typename) {
case 'NewMarket': { case 'NewMarket': {
proposalType = 'NewMarket'; proposalType = 'NewMarket';
fallbackTitle = t('NewMarketProposal');
details = ( details = (
<> <>
<span> <span>
@@ -52,7 +50,6 @@ export const ProposalHeader = ({
} }
case 'UpdateMarket': { case 'UpdateMarket': {
proposalType = 'UpdateMarket'; proposalType = 'UpdateMarket';
fallbackTitle = t('UpdateMarketProposal');
details = ( details = (
<> <>
<span>{t('Market change')}:</span>{' '} <span>{t('Market change')}:</span>{' '}
@@ -63,7 +60,6 @@ export const ProposalHeader = ({
} }
case 'NewAsset': { case 'NewAsset': {
proposalType = 'NewAsset'; proposalType = 'NewAsset';
fallbackTitle = t('NewAssetProposal');
details = ( details = (
<> <>
<span>{t('Symbol')}:</span> <Lozenge>{change.symbol}.</Lozenge>{' '} <span>{t('Symbol')}:</span> <Lozenge>{change.symbol}.</Lozenge>{' '}
@@ -85,7 +81,6 @@ export const ProposalHeader = ({
} }
case 'UpdateNetworkParameter': { case 'UpdateNetworkParameter': {
proposalType = 'NetworkParameter'; proposalType = 'NetworkParameter';
fallbackTitle = t('NetworkParameterProposal');
details = ( details = (
<> <>
<span>{t('Change')}:</span>{' '} <span>{t('Change')}:</span>{' '}
@@ -100,13 +95,11 @@ export const ProposalHeader = ({
} }
case 'NewFreeform': { case 'NewFreeform': {
proposalType = 'Freeform'; proposalType = 'Freeform';
fallbackTitle = t('FreeformProposal');
details = <span />; details = <span />;
break; break;
} }
case 'UpdateAsset': { case 'UpdateAsset': {
proposalType = 'UpdateAsset'; proposalType = 'UpdateAsset';
fallbackTitle = t('UpdateAssetProposal');
details = ( details = (
<> <>
<span>{t('AssetID')}:</span>{' '} <span>{t('AssetID')}:</span>{' '}
@@ -122,14 +115,10 @@ export const ProposalHeader = ({
<div data-testid="proposal-title"> <div data-testid="proposal-title">
{isListItem ? ( {isListItem ? (
<header> <header>
<SubHeading <SubHeading title={titleContent || t('Unknown proposal')} />
title={titleContent || fallbackTitle || t('Unknown proposal')}
/>
</header> </header>
) : ( ) : (
<Heading <Heading title={titleContent || t('Unknown proposal')} />
title={titleContent || fallbackTitle || t('Unknown proposal')}
/>
)} )}
</div> </div>
@@ -1,8 +1,8 @@
import classnames from 'classnames';
import { useState } from 'react'; import { useState } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { Icon, SyntaxHighlighter } from '@vegaprotocol/ui-toolkit'; import { Icon, SyntaxHighlighter } from '@vegaprotocol/ui-toolkit';
import { SubHeading } from '../../../../components/heading'; import { SubHeading } from '../../../../components/heading';
import { collapsibleToggleStyles } from '../../../../lib/collapsible-toggle-styles';
import type { ProposalFieldsFragment } from '../../proposals/__generated__/Proposals'; import type { ProposalFieldsFragment } from '../../proposals/__generated__/Proposals';
import type { ProposalQuery } from '../../proposal/__generated__/Proposal'; import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
@@ -13,6 +13,9 @@ export const ProposalJson = ({
}) => { }) => {
const { t } = useTranslation(); const { t } = useTranslation();
const [showDetails, setShowDetails] = useState(false); const [showDetails, setShowDetails] = useState(false);
const showDetailsIconClasses = classnames('mb-4', {
'rotate-180': showDetails,
});
return ( return (
<section data-testid="proposal-json"> <section data-testid="proposal-json">
@@ -22,7 +25,7 @@ export const ProposalJson = ({
> >
<div className="flex items-center gap-3"> <div className="flex items-center gap-3">
<SubHeading title={t('proposalJson')} /> <SubHeading title={t('proposalJson')} />
<div className={collapsibleToggleStyles(showDetails)}> <div className={showDetailsIconClasses}>
<Icon name="chevron-down" size={8} /> <Icon name="chevron-down" size={8} />
</div> </div>
</div> </div>
@@ -1 +0,0 @@
export * from './proposal-market-data';
@@ -1,222 +0,0 @@
import isEqual from 'lodash/isEqual';
import { useState } from 'react';
import { useTranslation } from 'react-i18next';
import {
InstrumentInfoPanel,
KeyDetailsInfoPanel,
LiquidityMonitoringParametersInfoPanel,
LiquidityPriceRangeInfoPanel,
MetadataInfoPanel,
OracleInfoPanel,
PriceMonitoringBoundsInfoPanel,
RiskFactorsInfoPanel,
RiskModelInfoPanel,
RiskParametersInfoPanel,
SettlementAssetInfoPanel,
} from '@vegaprotocol/markets';
import {
Accordion,
AccordionItem,
Button,
CopyWithTooltip,
Dialog,
Icon,
SyntaxHighlighter,
} from '@vegaprotocol/ui-toolkit';
import { SubHeading } from '../../../../components/heading';
import { collapsibleToggleStyles } from '../../../../lib/collapsible-toggle-styles';
import type { MarketInfoWithData } from '@vegaprotocol/markets';
import type { DataSourceDefinition } from '@vegaprotocol/types';
import { create } from 'zustand';
type MarketDataDialogState = {
isOpen: boolean;
open: () => void;
close: () => void;
};
export const useMarketDataDialogStore = create<MarketDataDialogState>(
(set) => ({
isOpen: false,
open: () => set({ isOpen: true }),
close: () => set({ isOpen: false }),
})
);
export const ProposalMarketData = ({
marketData,
}: {
marketData: MarketInfoWithData;
}) => {
const { t } = useTranslation();
const { isOpen, open, close } = useMarketDataDialogStore();
const [showDetails, setShowDetails] = useState(false);
if (!marketData) {
return null;
}
const settlementData = marketData.tradableInstrument.instrument.product
.dataSourceSpecForSettlementData.data as DataSourceDefinition;
const terminationData = marketData.tradableInstrument.instrument.product
.dataSourceSpecForTradingTermination.data as DataSourceDefinition;
const getSigners = (data: DataSourceDefinition) => {
if (data.sourceType.__typename === 'DataSourceDefinitionExternal') {
const signers = data.sourceType.sourceType.signers || [];
return signers.map(({ signer }) => {
return (
(signer.__typename === 'ETHAddress' && signer.address) ||
(signer.__typename === 'PubKey' && signer.key)
);
});
}
return [];
};
return (
<section className="relative" data-testid="proposal-market-data">
<button
onClick={() => setShowDetails(!showDetails)}
data-testid="proposal-market-data-toggle"
>
<div className="flex items-center gap-3">
<SubHeading title={t('marketSpecification')} />
<div className={collapsibleToggleStyles(showDetails)}>
<Icon name="chevron-down" size={8} />
</div>
</div>
</button>
{showDetails && (
<>
<div className="float-right">
<Button onClick={open} data-testid="view-market-json">
{t('viewMarketJson')}
</Button>
</div>
<div className="mb-10">
<Accordion>
<AccordionItem
itemId="key-details"
title={t('Key details')}
content={<KeyDetailsInfoPanel market={marketData} />}
/>
<AccordionItem
itemId="instrument"
title={t('Instrument')}
content={<InstrumentInfoPanel market={marketData} />}
/>
{isEqual(
getSigners(settlementData),
getSigners(terminationData)
) ? (
<AccordionItem
itemId="oracles"
title={t('Oracle')}
content={
<OracleInfoPanel
market={marketData}
type="settlementData"
/>
}
/>
) : (
<>
<AccordionItem
itemId="settlement-oracle"
title={t('Settlement Oracle')}
content={
<OracleInfoPanel
market={marketData}
type="settlementData"
/>
}
/>
<AccordionItem
itemId="termination-oracle"
title={t('Termination Oracle')}
content={
<OracleInfoPanel market={marketData} type="termination" />
}
/>
</>
)}
<AccordionItem
itemId="settlement-asset"
title={t('Settlement asset')}
content={<SettlementAssetInfoPanel market={marketData} />}
/>
<AccordionItem
itemId="metadata"
title={t('Metadata')}
content={<MetadataInfoPanel market={marketData} />}
/>
<AccordionItem
itemId="risk-model"
title={t('Risk model')}
content={<RiskModelInfoPanel market={marketData} />}
/>
<AccordionItem
itemId="risk-parameters"
title={t('Risk parameters')}
content={<RiskParametersInfoPanel market={marketData} />}
/>
<AccordionItem
itemId="risk-factors"
title={t('Risk factors')}
content={<RiskFactorsInfoPanel market={marketData} />}
/>
{(
marketData.priceMonitoringSettings?.parameters?.triggers || []
).map((_, triggerIndex) => (
<AccordionItem
itemId={`trigger-${triggerIndex}`}
title={t(`Price monitoring bounds ${triggerIndex + 1}`)}
content={
<PriceMonitoringBoundsInfoPanel
market={marketData}
triggerIndex={triggerIndex}
/>
}
/>
))}
<AccordionItem
itemId="liqudity-monitoring-parameters"
title={t('Liquidity monitoring parameters')}
content={
<LiquidityMonitoringParametersInfoPanel market={marketData} />
}
/>
<AccordionItem
itemId="liquidity-price-range"
title={t('Liquidity price range')}
content={<LiquidityPriceRangeInfoPanel market={marketData} />}
/>
</Accordion>
</div>
</>
)}
<Dialog
title={marketData.tradableInstrument.instrument.code}
open={isOpen}
onChange={(isOpen) => (isOpen ? open() : close())}
size="medium"
dataTestId="market-json-dialog"
>
<CopyWithTooltip text={JSON.stringify(marketData)}>
<button className="bg-vega-dark-100 rounded-sm py-2 px-3 mb-4 text-white">
<span>
<Icon name="duplicate" />
</span>
<span className="ml-2">Copy</span>
</button>
</CopyWithTooltip>
<SyntaxHighlighter data={marketData} />
</Dialog>
</section>
);
};
@@ -1,3 +1,4 @@
import classnames from 'classnames';
import { useState } from 'react'; import { useState } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { import {
@@ -12,7 +13,6 @@ import { SubHeading } from '../../../../components/heading';
import { useVoteInformation } from '../../hooks'; import { useVoteInformation } from '../../hooks';
import { useAppState } from '../../../../contexts/app-state/app-state-context'; import { useAppState } from '../../../../contexts/app-state/app-state-context';
import { ProposalType } from '../proposal/proposal'; import { ProposalType } from '../proposal/proposal';
import { collapsibleToggleStyles } from '../../../../lib/collapsible-toggle-styles';
import type { ProposalFieldsFragment } from '../../proposals/__generated__/Proposals'; import type { ProposalFieldsFragment } from '../../proposals/__generated__/Proposals';
import type { ProposalQuery } from '../../proposal/__generated__/Proposal'; import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
@@ -57,6 +57,10 @@ export const ProposalVotesTable = ({
? t('byTokenVote') ? t('byTokenVote')
: t('byLiquidityVote'); : t('byLiquidityVote');
const showDetailsIconClasses = classnames('mb-4', {
'rotate-180': showDetails,
});
return ( return (
<> <>
<button <button
@@ -65,7 +69,7 @@ export const ProposalVotesTable = ({
> >
<div className="flex items-center gap-3"> <div className="flex items-center gap-3">
<SubHeading title={t('voteBreakdown')} /> <SubHeading title={t('voteBreakdown')} />
<div className={collapsibleToggleStyles(showDetails)}> <div className={showDetailsIconClasses}>
<Icon name="chevron-down" size={8} /> <Icon name="chevron-down" size={8} />
</div> </div>
</div> </div>
@@ -1,4 +1,3 @@
import { MemoryRouter } from 'react-router-dom';
import { render, screen } from '@testing-library/react'; import { render, screen } from '@testing-library/react';
import { generateProposal } from '../../test-helpers/generate-proposals'; import { generateProposal } from '../../test-helpers/generate-proposals';
import { Proposal } from './proposal'; import { Proposal } from './proposal';
@@ -29,6 +28,9 @@ jest.mock('../proposal-change-table', () => ({
jest.mock('../proposal-json', () => ({ jest.mock('../proposal-json', () => ({
ProposalJson: () => <div data-testid="proposal-json"></div>, ProposalJson: () => <div data-testid="proposal-json"></div>,
})); }));
jest.mock('../proposal-terms/proposal-terms', () => ({
ProposalTerms: () => <div data-testid="proposal-terms"></div>,
}));
jest.mock('../proposal-votes-table', () => ({ jest.mock('../proposal-votes-table', () => ({
ProposalVotesTable: () => <div data-testid="proposal-votes-table"></div>, ProposalVotesTable: () => <div data-testid="proposal-votes-table"></div>,
})); }));
@@ -39,38 +41,19 @@ jest.mock('../list-asset', () => ({
ListAsset: () => <div data-testid="proposal-list-asset"></div>, ListAsset: () => <div data-testid="proposal-list-asset"></div>,
})); }));
const renderComponent = (proposal: ProposalQuery['proposal']) => {
render(
<MemoryRouter>
<Proposal
restData={{}}
proposal={proposal as ProposalQuery['proposal']}
/>
</MemoryRouter>
);
};
it('Renders with data-testid', async () => { it('Renders with data-testid', async () => {
const proposal = generateProposal(); const proposal = generateProposal();
renderComponent(proposal); render(<Proposal proposal={proposal as ProposalQuery['proposal']} />);
expect(await screen.findByTestId('proposal')).toBeInTheDocument(); expect(await screen.findByTestId('proposal')).toBeInTheDocument();
}); });
it('Renders with a link back to "all proposals"', async () => {
const proposal = generateProposal();
renderComponent(proposal);
expect(await screen.findByTestId('all-proposals-link')).toBeInTheDocument();
});
it('renders each section', async () => { it('renders each section', async () => {
const proposal = generateProposal(); const proposal = generateProposal();
renderComponent(proposal); render(<Proposal proposal={proposal as ProposalQuery['proposal']} />);
expect(await screen.findByTestId('proposal-header')).toBeInTheDocument(); expect(await screen.findByTestId('proposal-header')).toBeInTheDocument();
expect(screen.getByTestId('proposal-change-table')).toBeInTheDocument(); expect(screen.getByTestId('proposal-change-table')).toBeInTheDocument();
expect(screen.getByTestId('proposal-json')).toBeInTheDocument(); expect(screen.getByTestId('proposal-json')).toBeInTheDocument();
expect(screen.getByTestId('proposal-terms')).toBeInTheDocument();
expect(screen.getByTestId('proposal-votes-table')).toBeInTheDocument(); expect(screen.getByTestId('proposal-votes-table')).toBeInTheDocument();
expect(screen.getByTestId('proposal-vote-details')).toBeInTheDocument(); expect(screen.getByTestId('proposal-vote-details')).toBeInTheDocument();
expect(screen.queryByTestId('proposal-list-asset')).not.toBeInTheDocument(); expect(screen.queryByTestId('proposal-list-asset')).not.toBeInTheDocument();
@@ -93,7 +76,6 @@ it('renders whitelist section if proposal is new asset and source is erc20', asy
}, },
}, },
}); });
renderComponent(proposal); render(<Proposal proposal={proposal as ProposalQuery['proposal']} />);
expect(screen.getByTestId('proposal-list-asset')).toBeInTheDocument(); expect(screen.getByTestId('proposal-list-asset')).toBeInTheDocument();
}); });
@@ -1,22 +1,18 @@
import { useTranslation } from 'react-i18next';
import { Link } from 'react-router-dom';
import { import {
NetworkParams, NetworkParams,
useNetworkParams, useNetworkParams,
} from '@vegaprotocol/network-parameters'; } from '@vegaprotocol/network-parameters';
import { AsyncRenderer, Icon, RoundedWrapper } from '@vegaprotocol/ui-toolkit'; import { AsyncRenderer, RoundedWrapper } from '@vegaprotocol/ui-toolkit';
import { ProposalHeader } from '../proposal-detail-header/proposal-header'; import { ProposalHeader } from '../proposal-detail-header/proposal-header';
import type { ProposalFieldsFragment } from '../../proposals/__generated__/Proposals';
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
import { ProposalDescription } from '../proposal-description'; import { ProposalDescription } from '../proposal-description';
import { ProposalChangeTable } from '../proposal-change-table'; import { ProposalChangeTable } from '../proposal-change-table';
import { ProposalJson } from '../proposal-json'; import { ProposalJson } from '../proposal-json';
import { ProposalTerms } from '../proposal-terms';
import { ProposalVotesTable } from '../proposal-votes-table'; import { ProposalVotesTable } from '../proposal-votes-table';
import { VoteDetails } from '../vote-details'; import { VoteDetails } from '../vote-details';
import { ListAsset } from '../list-asset'; import { ListAsset } from '../list-asset';
import Routes from '../../../routes';
import { ProposalMarketData } from '../proposal-market-data';
import type { ProposalFieldsFragment } from '../../proposals/__generated__/Proposals';
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
import type { MarketInfoWithData } from '@vegaprotocol/markets';
export enum ProposalType { export enum ProposalType {
PROPOSAL_NEW_MARKET = 'PROPOSAL_NEW_MARKET', PROPOSAL_NEW_MARKET = 'PROPOSAL_NEW_MARKET',
@@ -28,17 +24,9 @@ export enum ProposalType {
} }
export interface ProposalProps { export interface ProposalProps {
proposal: ProposalFieldsFragment | ProposalQuery['proposal']; proposal: ProposalFieldsFragment | ProposalQuery['proposal'];
newMarketData?: MarketInfoWithData | null;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
restData: any;
} }
export const Proposal = ({ export const Proposal = ({ proposal }: ProposalProps) => {
proposal,
restData,
newMarketData,
}: ProposalProps) => {
const { t } = useTranslation();
const { params, loading, error } = useNetworkParams([ const { params, loading, error } = useNetworkParams([
NetworkParams.governance_proposal_market_minVoterBalance, NetworkParams.governance_proposal_market_minVoterBalance,
NetworkParams.governance_proposal_updateMarket_minVoterBalance, NetworkParams.governance_proposal_updateMarket_minVoterBalance,
@@ -91,18 +79,8 @@ export const Proposal = ({
return ( return (
<AsyncRenderer data={params} loading={loading} error={error}> <AsyncRenderer data={params} loading={loading} error={error}>
<section data-testid="proposal"> <section data-testid="proposal">
<div
className="flex items-center gap-1"
data-testid="all-proposals-link"
>
<Icon name={'chevron-left'} />
<Link className="underline" to={Routes.PROPOSALS}>
{t('AllProposals')}
</Link>
</div>
<ProposalHeader proposal={proposal} isListItem={false} /> <ProposalHeader proposal={proposal} isListItem={false} />
<div id="details">
<div className="my-10"> <div className="my-10">
<ProposalChangeTable proposal={proposal} /> <ProposalChangeTable proposal={proposal} />
</div> </div>
@@ -112,9 +90,7 @@ export const Proposal = ({
proposal.id ? ( proposal.id ? (
<ListAsset <ListAsset
assetId={proposal.id} assetId={proposal.id}
withdrawalThreshold={ withdrawalThreshold={proposal.terms.change.source.withdrawThreshold}
proposal.terms.change.source.withdrawThreshold
}
lifetimeLimit={proposal.terms.change.source.lifetimeLimit} lifetimeLimit={proposal.terms.change.source.lifetimeLimit}
/> />
) : null} ) : null}
@@ -123,18 +99,14 @@ export const Proposal = ({
<ProposalDescription description={proposal.rationale.description} /> <ProposalDescription description={proposal.rationale.description} />
</div> </div>
{newMarketData && (
<div className="mb-4"> <div className="mb-4">
<ProposalMarketData marketData={newMarketData} /> <ProposalTerms data={proposal.terms} />
</div> </div>
)}
<div className="mb-6"> <div className="mb-6">
<ProposalJson proposal={restData?.data?.proposal} /> <ProposalJson proposal={proposal} />
</div>
</div> </div>
<div id="voting">
<div className="mb-10"> <div className="mb-10">
<RoundedWrapper paddingBottom={true}> <RoundedWrapper paddingBottom={true}>
<VoteDetails <VoteDetails
@@ -149,11 +121,7 @@ export const Proposal = ({
</div> </div>
<div className="mb-4"> <div className="mb-4">
<ProposalVotesTable <ProposalVotesTable proposal={proposal} proposalType={proposalType} />
proposal={proposal}
proposalType={proposalType}
/>
</div>
</div> </div>
</section> </section>
</AsyncRenderer> </AsyncRenderer>
@@ -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', 'proposal1');
expect(openProposalsItems[1]).toHaveAttribute('id', 'proposal2');
expect(closedProposalsItems[0]).toHaveAttribute('id', 'proposal4');
expect(closedProposalsItems[1]).toHaveAttribute('id', 'proposal3');
});
it('Displays info on no proposals', () => { it('Displays info on no proposals', () => {
render(renderComponent([])); render(renderComponent([]));
expect(screen.queryByTestId('open-proposals')).not.toBeInTheDocument(); expect(screen.queryByTestId('open-proposals')).not.toBeInTheDocument();
@@ -1,6 +1,5 @@
import orderBy from 'lodash/orderBy';
import { isFuture } from 'date-fns'; import { isFuture } from 'date-fns';
import { useState, useMemo } from 'react'; import { useState } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { Heading, SubHeading } from '../../../../components/heading'; import { Heading, SubHeading } from '../../../../components/heading';
import { ProposalsListItem } from '../proposals-list-item'; import { ProposalsListItem } from '../proposals-list-item';
@@ -31,29 +30,6 @@ interface SortedProtocolUpgradeProposalsProps {
closed: ProtocolUpgradeProposalFieldsFragment[]; closed: ProtocolUpgradeProposalFieldsFragment[];
} }
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?.datetime).getTime(),
],
['asc', 'asc']
);
export const orderByUpgradeBlockHeight = (
arr: ProtocolUpgradeProposalFieldsFragment[]
) =>
orderBy(
arr,
[(p) => p?.upgradeBlockHeight, (p) => p.vegaReleaseTag],
['desc', 'desc']
);
export const ProposalsList = ({ export const ProposalsList = ({
proposals, proposals,
protocolUpgradeProposals, protocolUpgradeProposals,
@@ -61,9 +37,7 @@ export const ProposalsList = ({
}: ProposalsListProps) => { }: ProposalsListProps) => {
const { t } = useTranslation(); const { t } = useTranslation();
const [filterString, setFilterString] = useState(''); const [filterString, setFilterString] = useState('');
const sortedProposals = proposals.reduce(
const sortedProposals: SortedProposalsProps = useMemo(() => {
const initialSorting = proposals.reduce(
(acc: SortedProposalsProps, proposal) => { (acc: SortedProposalsProps, proposal) => {
if (isFuture(new Date(proposal?.terms.closingDatetime))) { if (isFuture(new Date(proposal?.terms.closingDatetime))) {
acc.open.push(proposal); acc.open.push(proposal);
@@ -77,23 +51,8 @@ export const ProposalsList = ({
closed: [], closed: [],
} }
); );
return {
open:
initialSorting.open.length > 0
? orderByDate(initialSorting.open as ProposalFieldsFragment[])
: [],
closed:
initialSorting.closed.length > 0
? orderByDate(
initialSorting.closed as ProposalFieldsFragment[]
).reverse()
: [],
};
}, [proposals]);
const sortedProtocolUpgradeProposals: SortedProtocolUpgradeProposalsProps = const sortedProtocolUpgradeProposals = protocolUpgradeProposals.reduce(
useMemo(() => {
const initialSorting = protocolUpgradeProposals.reduce(
(acc: SortedProtocolUpgradeProposalsProps, proposal) => { (acc: SortedProtocolUpgradeProposalsProps, proposal) => {
if (Number(proposal?.upgradeBlockHeight) > Number(lastBlockHeight)) { if (Number(proposal?.upgradeBlockHeight) > Number(lastBlockHeight)) {
acc.open.push(proposal); acc.open.push(proposal);
@@ -107,11 +66,6 @@ export const ProposalsList = ({
closed: [], closed: [],
} }
); );
return {
open: orderByUpgradeBlockHeight(initialSorting.open),
closed: orderByUpgradeBlockHeight(initialSorting.closed).reverse(),
};
}, [protocolUpgradeProposals, lastBlockHeight]);
const filterPredicate = ( const filterPredicate = (
p: ProposalFieldsFragment | ProposalQuery['proposal'] p: ProposalFieldsFragment | ProposalQuery['proposal']

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