Compare commits
208
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9679e5cbbc | ||
|
|
1756ed0986 | ||
|
|
ae57bd92f4 | ||
|
|
115b642140 | ||
|
|
ce3da97a8a | ||
|
|
cabd99d3ef | ||
|
|
597e07608f | ||
|
|
e451dc54b3 | ||
|
|
9703c3b7a6 | ||
|
|
c22b6f3ce9 | ||
|
|
c740c11eaa | ||
|
|
8187af05f5 | ||
|
|
ec2b85d74a | ||
|
|
d1fabb31a3 | ||
|
|
94f045a60b | ||
|
|
1af375bab8 | ||
|
|
6cc7d89967 | ||
|
|
cac20e7eed | ||
|
|
bd55eb5254 | ||
|
|
5a48ba4a33 | ||
|
|
d1fd9184ce | ||
|
|
13b3479661 | ||
|
|
ba22df9802 | ||
|
|
df3fe272a3 | ||
|
|
e0e48c70a2 | ||
|
|
8f29824f9e | ||
|
|
cf9bc272b9 | ||
|
|
c4635249be | ||
|
|
461383b3d9 | ||
|
|
fc3c73ad24 | ||
|
|
035e02ac1e | ||
|
|
78b9a923b6 | ||
|
|
1d2e00e4fa | ||
|
|
0467be10f0 | ||
|
|
1027827576 | ||
|
|
0089920d4c | ||
|
|
35896a9cf5 | ||
|
|
554ff2d2de | ||
|
|
9ee4a48e6f | ||
|
|
549f697cfa | ||
|
|
5a90ebe12e | ||
|
|
6be117086a | ||
|
|
2fed35eb1d | ||
|
|
2170e23f76 | ||
|
|
03a1c3adad | ||
|
|
79c2622d97 | ||
|
|
2bac18c7b4 | ||
|
|
9c6dd9f254 | ||
|
|
a675576a52 | ||
|
|
98d248e02e | ||
|
|
c8d55c6293 | ||
|
|
3467feb3e5 | ||
|
|
bdf1663709 | ||
|
|
d775573543 | ||
|
|
c0fdc8d570 | ||
|
|
a9ebb36b9b | ||
|
|
f6c0082f2d | ||
|
|
931ad75cc9 | ||
|
|
f25a58a3c3 | ||
|
|
37340b4dc3 | ||
|
|
17ba3eb351 | ||
|
|
8f966a4ba6 | ||
|
|
cc7f403718 | ||
|
|
9184c2374c | ||
|
|
37247a504a | ||
|
|
2e7a6a6458 | ||
|
|
bf6c13f523 | ||
|
|
a6f3a08d2f | ||
|
|
f13e456a29 | ||
|
|
5cab040189 | ||
|
|
e47b868c53 | ||
|
|
b6286cd0f3 | ||
|
|
f66e976b66 | ||
|
|
866e7232db | ||
|
|
5a8ff90890 | ||
|
|
7b8f654906 | ||
|
|
d6f39049bd | ||
|
|
1d06be8f4e | ||
|
|
5eba8fe28f | ||
|
|
2ba0e9a1b2 | ||
|
|
43d3754c64 | ||
|
|
aae5c44fa4 | ||
|
|
878bed9c7a | ||
|
|
6f9f432c90 | ||
|
|
3f01b93159 | ||
|
|
473c244d7b | ||
|
|
d6a32f5090 | ||
|
|
71540a90fb | ||
|
|
cefdbe6a3c | ||
|
|
3928dd5c0e | ||
|
|
1521bab4c4 | ||
|
|
b3036d520f | ||
|
|
429a5a23d3 | ||
|
|
5b02fd5d54 | ||
|
|
b8309a76e7 | ||
|
|
e8ae085c06 | ||
|
|
fdcd24847c | ||
|
|
e054db39b5 | ||
|
|
a01e48d508 | ||
|
|
0d96c487d9 | ||
|
|
011e4e4cec | ||
|
|
d6e2432955 | ||
|
|
18c034b910 | ||
|
|
849cbb43c4 | ||
|
|
cd34cb181f | ||
|
|
97b3f4decf | ||
|
|
0465fb8ca9 | ||
|
|
22af89e78c | ||
|
|
adfd2f41b6 | ||
|
|
d9b1aa6da3 | ||
|
|
205f369cdc | ||
|
|
9f63fde123 | ||
|
|
f2c70e6da4 | ||
|
|
e8ff1b0b60 | ||
|
|
f5989b0022 | ||
|
|
653ca034f2 | ||
|
|
ef25552311 | ||
|
|
0a8439d125 | ||
|
|
702ea0c6b4 | ||
|
|
0f2b86770a | ||
|
|
cd6d350047 | ||
|
|
c5a06c57ac | ||
|
|
5bb31fa38d | ||
|
|
fd8f38803c | ||
|
|
a22f8f0ef1 | ||
|
|
c2d5c71f35 | ||
|
|
f88c35648e | ||
|
|
348c61530e | ||
|
|
f364dabe2f | ||
|
|
1ae1fdff5e | ||
|
|
3ed615c36e | ||
|
|
6b50816234 | ||
|
|
f5e67a0c2d | ||
|
|
e4d4646878 | ||
|
|
bbd1536c18 | ||
|
|
f8444c0c88 | ||
|
|
b1adf776c0 | ||
|
|
ad898ab0e8 | ||
|
|
5b0fffc5cb | ||
|
|
a9dbc0f6eb | ||
|
|
a71786817f | ||
|
|
9ee0cb7b65 | ||
|
|
8c4aa41240 | ||
|
|
91ac977704 | ||
|
|
1190033776 | ||
|
|
b99e2235b4 | ||
|
|
002bc4f188 | ||
|
|
0c2af8c75f | ||
|
|
a305c5b8a9 | ||
|
|
4c3ceb94f3 | ||
|
|
2215864d0c | ||
|
|
589785ce09 | ||
|
|
babd99088d | ||
|
|
d155909fe4 | ||
|
|
1d3298cdc9 | ||
|
|
c2baaa2dd9 | ||
|
|
e2c4c4a841 | ||
|
|
7a89dc984d | ||
|
|
19580fb9ce | ||
|
|
0721099d7d | ||
|
|
9ee2810c51 | ||
|
|
9f8f99b50b | ||
|
|
3208f762fd | ||
|
|
7a06c946d1 | ||
|
|
70e172045d | ||
|
|
06c540666e | ||
|
|
f81503b8f7 | ||
|
|
1699e33cbd | ||
|
|
5280b79927 | ||
|
|
34526d527e | ||
|
|
7b9aae700d | ||
|
|
e9cb2ad217 | ||
|
|
6dd4536fdd | ||
|
|
522eb521b9 | ||
|
|
3aa0e974c2 | ||
|
|
a0eb381ab1 | ||
|
|
a51ab4d98d | ||
|
|
67643afd74 | ||
|
|
145539e0c0 | ||
|
|
48109b584c | ||
|
|
a4512e6064 | ||
|
|
45adac2932 | ||
|
|
fff8fdd78b | ||
|
|
079d8bc48d | ||
|
|
9658363b56 | ||
|
|
e24289330e | ||
|
|
0fbd2bfa72 | ||
|
|
43ef4fad6f | ||
|
|
828dcf6e93 | ||
|
|
4a583ccf09 | ||
|
|
8bcad28f50 | ||
|
|
b375681076 | ||
|
|
8a2db407cf | ||
|
|
491844d3fd | ||
|
|
f1f51c5ecd | ||
|
|
9bc78f8ccd | ||
|
|
a03031a283 | ||
|
|
b499c293e3 | ||
|
|
65747e8d26 | ||
|
|
9a84eda21b | ||
|
|
fba98f2fd0 | ||
|
|
3e32d0f396 | ||
|
|
b75d189054 | ||
|
|
afa627864c | ||
|
|
b13a866b2a | ||
|
|
c65f07eee6 | ||
|
|
5bd18cc127 | ||
|
|
ff3519279d |
@@ -14,8 +14,7 @@ What we need to achieve and who for
|
||||
|
||||
## Tasks
|
||||
|
||||
- [ ] What do we need to do first
|
||||
- [ ] and then what?
|
||||
- [ ] Etc.
|
||||
- [ ]
|
||||
- [ ]
|
||||
|
||||
## Additional details / background info
|
||||
|
||||
@@ -22,11 +22,14 @@ So that
|
||||
|
||||
## Tasks
|
||||
|
||||
- [ ] Explore and sketch
|
||||
- [ ] UX (if needed)
|
||||
- [ ] Design (if needed)
|
||||
- [ ] Team and stakeholder review
|
||||
- [ ] Visual Design
|
||||
- [ ] Team review
|
||||
- [ ] Etc.
|
||||
- [ ] Specs reviewed and created or adjusted
|
||||
- [ ] Implementation
|
||||
- [ ] Testing (unit and/or e2e)
|
||||
- [ ] Code review
|
||||
- [ ] QA review
|
||||
|
||||
## Sketch
|
||||
|
||||
|
||||
@@ -9,6 +9,11 @@ jobs:
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
|
||||
@@ -27,14 +32,16 @@ jobs:
|
||||
|
||||
- name: resolve ipfs hashes for release
|
||||
run: |
|
||||
echo "Tag name: ${{ github.event.release.tag_name }}"
|
||||
echo "Name: ${{ github.event.release.name }}"
|
||||
echo "Description: ${{ github.event.release.body }}"
|
||||
until docker pull vegaprotocol/trading:${{ github.event.release.tag_name }}; do
|
||||
echo "Tag: ${{ github.event.release.tag_name }}"
|
||||
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"
|
||||
sleep 60
|
||||
done
|
||||
docker run --rm vegaprotocol/trading:${{ github.event.release.tag_name }} cat /ipfs-hash > ipfs-hash
|
||||
docker run --rm vegaprotocol/trading:$commit cat /ipfs-hash > ipfs-hash
|
||||
curl -L https://dist.ipfs.tech/kubo/v0.20.0/kubo_v0.20.0_linux-amd64.tar.gz -o kubo.tgz
|
||||
tar -xzf kubo.tgz
|
||||
export PATH="$PATH:$PWD/kubo"
|
||||
@@ -48,19 +55,20 @@ jobs:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
id: ${{ github.event.release.id }}
|
||||
body: |
|
||||
---
|
||||
|
||||
___
|
||||
|
||||
# Deployments
|
||||
* https://explorer.vega.xyz
|
||||
* https://governance.vega.xyz
|
||||
|
||||
# IPFS releases
|
||||
Tye IPFS hash of this release of the Trading app is:
|
||||
The IPFS hash of this release of the Trading app is:
|
||||
|
||||
CIDv0: ${{ env.IPFS_V0 }}
|
||||
CIDv1: ${{ env.IPFS_V1 }}
|
||||
|
||||
You can always access the latest IPFS release by visiting [vega.trading](https://vega.trading).
|
||||
You can always access the latest IPFS release by visiting [console.vega.xyz](https://console.vega.xyz).
|
||||
|
||||
You can also access Trading directly from an IPFS gateway.
|
||||
BEWARE: The Trading interface uses [localStorage](https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage) to remember your settings, such as which tokens you have imported. You should always use an IPFS gateway that enforces [origin separation](https://ipfs.github.io/public-gateway-checker/).
|
||||
|
||||
@@ -5,8 +5,7 @@ on:
|
||||
branches:
|
||||
- release/*
|
||||
- develop
|
||||
tags:
|
||||
- v*
|
||||
- main
|
||||
pull_request:
|
||||
types:
|
||||
- opened
|
||||
@@ -98,44 +97,79 @@ jobs:
|
||||
# See affected apps
|
||||
- name: See affected apps
|
||||
run: |
|
||||
affected="$(yarn nx print-affected --base=${{ env.NX_BASE }} --head=${{ env.NX_HEAD }} --select=projects)"
|
||||
branch_slug="$(echo '${{ github.head_ref || github.ref_name }}' | sed -r s/[^a-zA-Z0-9]+/-/g | sed -r s/^-+\|-+$//g | cut -c 1-50 )"
|
||||
|
||||
echo ">>>> debug"
|
||||
echo "NX_BASE: ${{ env.NX_BASE }}"
|
||||
echo "NX_HEAD: ${{ env.NX_HEAD }}"
|
||||
echo "Affected: ${affected}"
|
||||
echo "Branch slug: ${branch_slug}"
|
||||
echo ">>>> eof debug"
|
||||
|
||||
affected="$(yarn nx print-affected --base=${{ env.NX_BASE }} --head=${{ env.NX_HEAD }} --select=projects)"
|
||||
|
||||
branch_slug="$(echo '${{ github.head_ref || github.ref_name }}' | sed -r s/[^a-zA-Z0-9]+/-/g | sed -r s/^-+\|-+$//g | cut -c 1-50 )"
|
||||
projects_e2e=""
|
||||
preview_governance="not deployed"
|
||||
preview_trading="not deployed"
|
||||
preview_explorer="not deployed"
|
||||
preview_tools="not deployed"
|
||||
if echo "$affected" | grep -q governance; then
|
||||
echo "Governance is affected"
|
||||
projects_e2e+='"governance-e2e" '
|
||||
preview_governance=$(printf "https://%s.%s.vega.rocks" "governance" "$branch_slug")
|
||||
fi
|
||||
if echo "$affected" | grep -q trading; then
|
||||
echo "Trading is affected"
|
||||
projects_e2e+='"trading-e2e" '
|
||||
preview_trading=$(printf "https://%s.%s.vega.rocks" "trading" "$branch_slug")
|
||||
fi
|
||||
if echo "$affected" | grep -q explorer; then
|
||||
echo "Explorer is affected"
|
||||
projects_e2e+='"explorer-e2e" '
|
||||
preview_explorer=$(printf "https://%s.%s.vega.rocks" "explorer" "$branch_slug")
|
||||
fi
|
||||
if [[ -z "$projects_e2e" ]]; then
|
||||
projects_e2e+='"governance-e2e" "trading-e2e" "explorer-e2e" '
|
||||
preview_governance=$(printf "https://%s.%s.vega.rocks" "governance" "$branch_slug")
|
||||
preview_trading=$(printf "https://%s.%s.vega.rocks" "trading" "$branch_slug")
|
||||
preview_explorer=$(printf "https://%s.%s.vega.rocks" "explorer" "$branch_slug")
|
||||
else
|
||||
if [[ $affected == *"governance"* ]]; then
|
||||
projects_e2e+='"governance-e2e" '
|
||||
preview_governance=$(printf "https://%s.%s.vega.rocks" "governance" "$branch_slug")
|
||||
fi
|
||||
projects="$(echo $projects_e2e | sed 's|-e2e||g')"
|
||||
if [[ "${{ github.event_name }}" = "pull_request" ]]; then
|
||||
if echo "$affected" | grep -q multisig-signer; then
|
||||
echo "Tools are affected"
|
||||
# tools are only applicable to check previews or deploy from develop to mainnet
|
||||
echo "Deploying tools on preview"
|
||||
preview_tools=$(printf "https://%s.%s.vega.rocks" "tools" "$branch_slug")
|
||||
projects+=' "multisig-signer" '
|
||||
fi
|
||||
if [[ $affected == *"trading"* ]]; then
|
||||
projects_e2e+='"trading-e2e" '
|
||||
preview_trading=$(printf "https://%s.%s.vega.rocks" "trading" "$branch_slug")
|
||||
elif [[ "${{ github.ref }}" =~ .*develop$ ]]; then
|
||||
if echo "$affected" | grep -q multisig-signer; then
|
||||
echo "Tools are affected"
|
||||
# tools are only applicable to check previews or deploy from develop to mainnet
|
||||
echo "Deploying tools on s3"
|
||||
projects+=' "multisig-signer" '
|
||||
fi
|
||||
if [[ $affected == *"explorer"* ]]; then
|
||||
projects_e2e+='"explorer-e2e" '
|
||||
preview_explorer=$(printf "https://%s.%s.vega.rocks" "explorer" "$branch_slug")
|
||||
if echo "$affected" | grep -q static; then
|
||||
echo "static is affected"
|
||||
echo "Deploying static on s3"
|
||||
projects+=' "static" '
|
||||
fi
|
||||
if echo "$affected" | grep -q ui-toolkit; then
|
||||
echo "ui-toolkit is affected"
|
||||
echo "Deploying ui-toolkit on s3"
|
||||
projects+=' "ui-toolkit" '
|
||||
fi
|
||||
fi
|
||||
|
||||
projects_e2e=${projects_e2e%?}
|
||||
projects_e2e=[${projects_e2e// /,}]
|
||||
projects=[${projects// /,}]
|
||||
echo PROJECTS_E2E=$projects_e2e >> $GITHUB_ENV
|
||||
echo PROJECTS=$(echo $projects_e2e | sed 's|-e2e||g') >> $GITHUB_ENV
|
||||
echo PROJECTS=$projects >> $GITHUB_ENV
|
||||
echo PREVIEW_GOVERNANCE=$preview_governance >> $GITHUB_ENV
|
||||
echo PREVIEW_TRADING=$preview_trading >> $GITHUB_ENV
|
||||
echo PREVIEW_EXPLORER=$preview_explorer >> $GITHUB_ENV
|
||||
echo PREVIEW_TOOLS=$preview_tools >> $GITHUB_ENV
|
||||
|
||||
outputs:
|
||||
projects: ${{ env.PROJECTS }}
|
||||
@@ -143,11 +177,12 @@ jobs:
|
||||
preview_governance: ${{ env.PREVIEW_GOVERNANCE }}
|
||||
preview_trading: ${{ env.PREVIEW_TRADING }}
|
||||
preview_explorer: ${{ env.PREVIEW_EXPLORER }}
|
||||
preview_tools: ${{ env.PREVIEW_TOOLS }}
|
||||
|
||||
cypress:
|
||||
needs: lint-test-build
|
||||
name: '(CI) cypress'
|
||||
if: ${{ needs.lint-test-build.outputs.projects != '[]' }}
|
||||
if: ${{ needs.lint-test-build.outputs.projects-e2e != '[]' }}
|
||||
uses: ./.github/workflows/cypress-run.yml
|
||||
secrets: inherit
|
||||
with:
|
||||
@@ -169,6 +204,7 @@ jobs:
|
||||
- publish-dist
|
||||
- lint-test-build
|
||||
if: ${{ github.event_name == 'pull_request' }}
|
||||
timeout-minutes: 60
|
||||
name: '(CD) comment preview links'
|
||||
steps:
|
||||
- name: Find Comment
|
||||
@@ -178,6 +214,35 @@ jobs:
|
||||
issue-number: ${{ github.event.pull_request.number }}
|
||||
body-includes: Previews
|
||||
|
||||
- name: Wait for deployments
|
||||
run: |
|
||||
# https://stackoverflow.com/questions/3183444/check-for-valid-link-url
|
||||
regex='(https?|ftp|file)://[-[:alnum:]\+&@#/%?=~_|!:,.;]*[-[:alnum:]\+&@#/%=~_|]'
|
||||
if [[ "${{ needs.lint-test-build.outputs.preview_governance }}" =~ $regex ]]; then
|
||||
until curl -L --fail "${{ needs.lint-test-build.outputs.preview_governance }}"; do
|
||||
echo "waiting for governance preview"
|
||||
sleep 5
|
||||
done
|
||||
fi
|
||||
if [[ "${{ needs.lint-test-build.outputs.preview_explorer }}" =~ $regex ]]; then
|
||||
until curl -L --fail "${{ needs.lint-test-build.outputs.preview_explorer }}"; do
|
||||
echo "waiting for explorer preview"
|
||||
sleep 5
|
||||
done
|
||||
fi
|
||||
if [[ "${{ needs.lint-test-build.outputs.preview_trading }}" =~ $regex ]]; then
|
||||
until curl -L --fail "${{ needs.lint-test-build.outputs.preview_trading }}"; do
|
||||
echo "waiting for trading preview"
|
||||
sleep 5
|
||||
done
|
||||
fi
|
||||
if [[ "${{ needs.lint-test-build.outputs.preview_tools }}" =~ $regex ]]; then
|
||||
until curl -L --fail "${{ needs.lint-test-build.outputs.preview_tools }}"; do
|
||||
echo "waiting for tools preview"
|
||||
sleep 5
|
||||
done
|
||||
fi
|
||||
|
||||
- name: Create comment
|
||||
uses: peter-evans/create-or-update-comment@v3
|
||||
if: ${{ steps.fc.outputs.comment-id == 0 }}
|
||||
@@ -188,16 +253,11 @@ jobs:
|
||||
* governance: ${{ needs.lint-test-build.outputs.preview_governance }}
|
||||
* explorer: ${{ needs.lint-test-build.outputs.preview_explorer }}
|
||||
* trading: ${{ needs.lint-test-build.outputs.preview_trading }}
|
||||
|
||||
cypress-check:
|
||||
name: '(CI) cypress - check'
|
||||
runs-on: ubuntu-latest
|
||||
needs: cypress
|
||||
steps:
|
||||
- run: echo Done!
|
||||
* tools: ${{ needs.lint-test-build.outputs.preview_tools }}
|
||||
|
||||
# Report single result at the end, to avoid mess with required checks in PR
|
||||
cypress-result:
|
||||
cypress-check:
|
||||
name: '(CI) cypress - check'
|
||||
if: ${{ always() }}
|
||||
needs: cypress
|
||||
runs-on: ubuntu-22.04
|
||||
|
||||
@@ -20,7 +20,7 @@ jobs:
|
||||
project: ${{ fromJSON(inputs.projects) }}
|
||||
name: ${{ matrix.project }}
|
||||
runs-on: self-hosted-runner
|
||||
timeout-minutes: 60
|
||||
timeout-minutes: 100
|
||||
steps:
|
||||
# Checks if skip cache was requested
|
||||
- name: Set skip-nx-cache flag
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
name: Generate tranches
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 */6 * * *'
|
||||
|
||||
jobs:
|
||||
master:
|
||||
name: Generate Queries
|
||||
runs-on: ubuntu-22.04
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
token: ${{ secrets.VEGA_CI_BOT_GITHUB_TOKEN }}
|
||||
fetch-depth: 0
|
||||
- name: Use Node.js 16
|
||||
id: Node
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: 16.15.1
|
||||
- name: Install root dependencies
|
||||
run: yarn install
|
||||
- name: Generate queries
|
||||
run: node ./scripts/get-tranches.js
|
||||
- uses: stefanzweifel/git-auto-commit-action@v4
|
||||
with:
|
||||
commit_message: 'chore: update tranches'
|
||||
commit_options: '--no-verify --signoff'
|
||||
skip_fetch: true
|
||||
skip_checkout: true
|
||||
@@ -31,6 +31,7 @@ jobs:
|
||||
uses: docker/setup-buildx-action@v2
|
||||
|
||||
- name: Log in to the Container registry (ghcr)
|
||||
if: ${{ github.event_name == 'pull_request' }}
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
registry: ghcr.io
|
||||
@@ -39,7 +40,7 @@ jobs:
|
||||
|
||||
- name: Log in to the Container registry (docker hub)
|
||||
uses: docker/login-action@v2
|
||||
if: ${{ startsWith(github.ref, 'refs/tags/v') }}
|
||||
if: ${{ matrix.app == 'trading' && github.event_name == 'push' && ( endsWith(github.ref, 'main') || endsWith(github.ref, 'release/testnet') ) }}
|
||||
with:
|
||||
# registry: registry.hub.docker.com
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
@@ -59,40 +60,68 @@ jobs:
|
||||
key: ${{ runner.os }}-cache-node-modules-${{ hashFiles('yarn.lock') }}
|
||||
|
||||
# https://docs.github.com/en/actions/learn-github-actions/contexts
|
||||
- name: Define variables
|
||||
- name: Define dist variables
|
||||
if: ${{ github.event_name == 'push' }}
|
||||
run: |
|
||||
envName=''
|
||||
domain="vega.rocks"
|
||||
if [[ "${{ github.event_name }}" = "push" ]]; then
|
||||
if [[ "${{ github.ref }}" =~ .*release/.* ]]; then
|
||||
envName="$(echo ${{ github.ref }} | rev | cut -d '/' -f 1 | rev)"
|
||||
elif [[ "${{ github.ref }}" =~ .*develop$ ]]; then
|
||||
envName="stagnet1"
|
||||
elif [[ "${{ matrix.app}}" = "trading" ]] && [[ ${{ startsWith(github.ref, 'refs/tags/v') && 'true' || 'false' }} = "true" ]]; then
|
||||
bucketName=''
|
||||
|
||||
if [[ "${{ github.ref }}" =~ .*release/.* ]]; then
|
||||
envName="$(echo ${{ github.ref }} | rev | cut -d '/' -f 1 | rev)"
|
||||
elif [[ "${{ github.ref }}" =~ .*develop$ ]]; then
|
||||
envName="stagnet1"
|
||||
if [[ "${{ matrix.app }}" = "multisig-signer" ]]; then
|
||||
envName="mainnet"
|
||||
elif [[ "${{ github.ref }}" =~ .*main$ ]]; then
|
||||
bucketName="tools.vega.xyz"
|
||||
fi
|
||||
if [[ "${{ matrix.app }}" = "static" ]]; then
|
||||
envName="mainnet"
|
||||
bucketName="static.vega.xyz"
|
||||
fi
|
||||
if [[ "${envName}" = "mainnet" ]]; then
|
||||
domain="vega.xyz"
|
||||
if [[ "${{ matrix.app }}" = "ui-toolkit" ]]; then
|
||||
envName="mainnet"
|
||||
bucketName="ui.vega.rocks"
|
||||
fi
|
||||
bucketName="${{ matrix.app }}.${envName}.${domain}"
|
||||
echo BUCKET_NAME=${bucketName} >> $GITHUB_ENV
|
||||
elif [[ "${{ github.ref }}" =~ .*main$ ]]; then
|
||||
envName="mainnet"
|
||||
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}"
|
||||
fi
|
||||
|
||||
echo "bucket name: ${bucketName}"
|
||||
echo "env name: ${envName}"
|
||||
|
||||
echo BUCKET_NAME=${bucketName} >> $GITHUB_ENV
|
||||
echo ENV_NAME=${envName} >> $GITHUB_ENV
|
||||
|
||||
- name: Build local dist
|
||||
run: |
|
||||
flags=""
|
||||
if [[ ! -z "${{ env.ENV_NAME }}" ]]; then
|
||||
if [[ "${{ env.ENV_NAME }}" != "ops-vega" ]]; then
|
||||
flags="--env=${{ env.ENV_NAME }}"
|
||||
fi
|
||||
flags="--env=${{ env.ENV_NAME }}"
|
||||
fi
|
||||
|
||||
if [ "${{ matrix.app }}" = "trading" ]; then
|
||||
yarn nx export trading $flags || (yarn install && yarn nx export trading $flags)
|
||||
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
|
||||
yarn nx build ${{ matrix.app }} $flags || (yarn install && yarn nx build ${{ matrix.app }} $flags)
|
||||
DIST_LOCATION=dist/apps/${{ matrix.app }}
|
||||
@@ -127,7 +156,9 @@ jobs:
|
||||
|
||||
- name: Publish dist as docker image (ghcr)
|
||||
uses: docker/build-push-action@v3
|
||||
if: ${{ github.event_name == 'pull_request' || (matrix.app == 'trading' && github.event_name == 'push' && !startsWith(github.ref, 'refs/tags/v') ) }}
|
||||
continue-on-error: true
|
||||
id: ghcr-push
|
||||
if: ${{ github.event_name == 'pull_request' }}
|
||||
with:
|
||||
context: .
|
||||
file: docker/node-outside-docker.Dockerfile
|
||||
@@ -140,7 +171,9 @@ jobs:
|
||||
|
||||
- name: Publish dist as docker image (docker hub)
|
||||
uses: docker/build-push-action@v3
|
||||
if: ${{ matrix.app == 'trading' && github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v') }}
|
||||
continue-on-error: true
|
||||
id: dockerhub-push
|
||||
if: ${{ matrix.app == 'trading' && github.event_name == 'push' && ( endsWith(github.ref, 'main') || endsWith(github.ref, 'release/testnet') ) }}
|
||||
with:
|
||||
context: .
|
||||
file: docker/node-outside-docker.Dockerfile
|
||||
@@ -149,13 +182,41 @@ jobs:
|
||||
APP=${{ matrix.app }}
|
||||
ENV_NAME=${{ env.ENV_NAME }}
|
||||
tags: |
|
||||
vegaprotocol/${{ matrix.app }}:${{ github.ref_name }}
|
||||
vegaprotocol/${{ matrix.app }}:mainnet
|
||||
vegaprotocol/${{ matrix.app }}:${{ github.sha }}
|
||||
vegaprotocol/${{ matrix.app }}:${{ endsWith(github.ref, 'main') && 'mainnet' || endsWith(github.ref, 'release/testnet') && 'testnet' || '' }}
|
||||
|
||||
- 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
|
||||
- name: Publish dist to s3
|
||||
uses: jakejarvis/s3-sync-action@master
|
||||
if: ${{ github.event_name == 'push' && !startsWith(github.ref, 'refs/tags/v') }}
|
||||
# s3 releases are not happening for trading on mainnet - it's IPFS
|
||||
if: ${{ github.event_name == 'push' && ( matrix.app != 'trading' || (matrix.app == 'trading' && !( endsWith(github.ref, 'main') || endsWith(github.ref, 'release/testnet') ) ) ) }}
|
||||
with:
|
||||
args: --acl private --follow-symlinks --delete
|
||||
env:
|
||||
@@ -173,22 +234,85 @@ jobs:
|
||||
number: ${{ github.event.number }}
|
||||
|
||||
- name: Trigger fleek deployment
|
||||
if: ${{ matrix.app == 'trading' && github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v') }}
|
||||
# release to ipfs happens only on mainnet (represented by main branch) for trading
|
||||
if: ${{ matrix.app == 'trading' && github.event_name == 'push' && ( endsWith(github.ref, 'main') || endsWith(github.ref, 'release/testnet') ) }}
|
||||
run: |
|
||||
# display info about app
|
||||
curl -H "Authorization: ${{ secrets.FLEEK_API_KEY }}" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"query": "query{getSiteById(siteId:\"f8f2e051-f18e-49e6-b876-0a39369dc0d8\"){id latestDeploy{id status}}}"}' \
|
||||
https://api.fleek.co/graphql
|
||||
if echo ${{ github.ref }} | grep -q main; then
|
||||
# display info about app
|
||||
curl --fail -H "Authorization: ${{ secrets.FLEEK_API_KEY }}" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"query": "query{getSiteById(siteId:\"f8f2e051-f18e-49e6-b876-0a39369dc0d8\"){id latestDeploy{id status}}}"}' \
|
||||
https://api.fleek.co/graphql
|
||||
|
||||
# trigger new deployment as base image is always set to vegaprotocol/trading:mainnet
|
||||
curl -H "Authorization: ${{ secrets.FLEEK_API_KEY }}" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"query": "mutation{triggerDeploy(siteId:\"f8f2e051-f18e-49e6-b876-0a39369dc0d8\"){id status}}"}' \
|
||||
https://api.fleek.co/graphql
|
||||
# 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:\"f8f2e051-f18e-49e6-b876-0a39369dc0d8\"){id status}}"}' \
|
||||
https://api.fleek.co/graphql
|
||||
|
||||
- name: Update vega.trading DNS to redirect to the new console
|
||||
if: ${{ matrix.app == 'trading' && github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v') }}
|
||||
elif echo ${{ github.ref }} | grep -q release/testnet; then
|
||||
# display info about app
|
||||
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') }}
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
repository: 'vegaprotocol/ipfs-redirect'
|
||||
path: 'ipfs-redirect'
|
||||
fetch-depth: '0'
|
||||
token: ${{ secrets.VEGA_CI_BOT_GITHUB_TOKEN }}
|
||||
|
||||
- name: Update console.vega.xyz DNS to redirect to the new console
|
||||
if: ${{ matrix.app == 'trading' && github.event_name == 'push' && endsWith(github.ref, 'main') }}
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.VEGA_CI_BOT_GITHUB_TOKEN }}
|
||||
run: |
|
||||
curl -L https://dist.ipfs.tech/kubo/v0.20.0/kubo_v0.20.0_linux-amd64.tar.gz -o kubo.tgz
|
||||
tar -xzf kubo.tgz
|
||||
export PATH="$PATH:$PWD/kubo"
|
||||
which ipfs
|
||||
new_hash=$(cat ${{ matrix.app }}-ipfs-hash)
|
||||
new_cid=$(ipfs cid format -v 1 -b base32 $new_hash)
|
||||
|
||||
ls -al ipfs-redirect
|
||||
|
||||
echo $new_hash > ipfs-redirect/cidv0.txt
|
||||
echo $new_cid > ipfs-redirect/cidv1.txt
|
||||
|
||||
(
|
||||
cd ipfs-redirect
|
||||
|
||||
git status
|
||||
cat .git/config
|
||||
git config --global user.email "vega-ci-bot@vega.xyz"
|
||||
git config --global user.name "vega-ci-bot"
|
||||
|
||||
branch_name="update-hash-${{ github.ref }}"
|
||||
git checkout -b "$branch_name"
|
||||
commit_msg="Automated hash update from ${{ github.ref }}"
|
||||
git add cidv0.txt cidv1.txt
|
||||
git commit -m "$commit_msg"
|
||||
git push -u origin "$branch_name"
|
||||
pr_url="$(gh pr create --title "${commit_msg}" --body 'automated pull request to update CIDs')"
|
||||
echo $pr_url
|
||||
# once auto merge get's enabled on documentation repo let's do follow up
|
||||
sleep 5
|
||||
gh pr merge "${pr_url}" --delete-branch --squash --admin
|
||||
)
|
||||
|
||||
- name: Update console.fairground.wtf DNS to redirect to the new console
|
||||
if: ${{ matrix.app == 'trading' && github.event_name == 'push' && endsWith(github.ref, 'testnet') }}
|
||||
run: |
|
||||
curl -L https://dist.ipfs.tech/kubo/v0.20.0/kubo_v0.20.0_linux-amd64.tar.gz -o kubo.tgz
|
||||
tar -xzf kubo.tgz
|
||||
@@ -198,21 +322,34 @@ jobs:
|
||||
new_cid=$(ipfs cid format -v 1 -b base32 $new_hash)
|
||||
|
||||
# Generate console URL
|
||||
|
||||
new_console_url_type=ipfs
|
||||
# new_console_url_type=ipns
|
||||
|
||||
new_console_url_domain=cf-ipfs.com
|
||||
# new_console_url_domain=dweb.link
|
||||
new_console_url="https://${new_cid}.${new_console_url_type}.${new_console_url_domain}/"
|
||||
|
||||
new_console_url="http://${new_cid}.${new_console_url_type}.${new_console_url_domain}/"
|
||||
echo "new_console_url=${new_console_url}"
|
||||
|
||||
# Update record in DNSimple
|
||||
# docs: https://developer.dnsimple.com/v2/zones/records/#updateZoneRecord
|
||||
dnsimple_account_id=84895
|
||||
dnsimple_zone_name=vega.trading
|
||||
dnsimple_record_id=44409591
|
||||
# see: https://dnsimple.com/a/84895/domains/vega.trading/records/44409591/edit
|
||||
dnsimple_zone_name=fairground.wtf
|
||||
dnsimple_record_id=45300615
|
||||
# see: https://dnsimple.com/a/84895/domains/fairground.wtf/records/45300615/edit
|
||||
curl --fail -H 'Authorization: Bearer ${{ secrets.DNSIMPLE_API_TOKEN }}' \
|
||||
-H 'Accept: application/json' \
|
||||
-H 'Content-Type: application/json' \
|
||||
-X PATCH \
|
||||
-d "{
|
||||
\"content\": \"${new_console_url}\"
|
||||
}" \
|
||||
https://api.dnsimple.com/v2/${dnsimple_account_id}/zones/${dnsimple_zone_name}/records/${dnsimple_record_id}
|
||||
|
||||
curl -H 'Authorization: Bearer ${{ secrets.DNSIMPLE_API_TOKEN }}' \
|
||||
# trading.fairground.wtf
|
||||
dnsimple_record_id=45302531
|
||||
curl --fail -H 'Authorization: Bearer ${{ secrets.DNSIMPLE_API_TOKEN }}' \
|
||||
-H 'Accept: application/json' \
|
||||
-H 'Content-Type: application/json' \
|
||||
-X PATCH \
|
||||
|
||||
@@ -43,7 +43,17 @@ jobs:
|
||||
-d '{"query": "mutation{triggerDeploy(siteId:\"f8f2e051-f18e-49e6-b876-0a39369dc0d8\"){id status}}"}' \
|
||||
https://api.fleek.co/graphql
|
||||
|
||||
- name: Update vega.trading DNS to redirect to the new console
|
||||
- name: Check out ipfs-redirect
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
repository: 'vegaprotocol/ipfs-redirect'
|
||||
path: 'ipfs-redirect'
|
||||
fetch-depth: '0'
|
||||
token: ${{ secrets.VEGA_CI_BOT_GITHUB_TOKEN }}
|
||||
|
||||
- name: Update console.vega.xyz DNS to redirect to the new console
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.VEGA_CI_BOT_GITHUB_TOKEN }}
|
||||
run: |
|
||||
curl -L https://dist.ipfs.tech/kubo/v0.20.0/kubo_v0.20.0_linux-amd64.tar.gz -o kubo.tgz
|
||||
tar -xzf kubo.tgz
|
||||
@@ -51,27 +61,24 @@ jobs:
|
||||
which ipfs
|
||||
new_hash=$(cat ipfs-hash)
|
||||
new_cid=$(ipfs cid format -v 1 -b base32 $new_hash)
|
||||
git config --global user.email "vega-ci-bot@vega.xyz"
|
||||
git config --global user.name "vega-ci-bot"
|
||||
echo $new_hash > ipfs-redirect/cidv0.txt
|
||||
echo $new_cid > ipfs-redirect/cidv1.txt
|
||||
|
||||
# Generate console URL
|
||||
new_console_url_type=ipfs
|
||||
# new_console_url_type=ipns
|
||||
new_console_url_domain=cf-ipfs.com
|
||||
# new_console_url_domain=dweb.link
|
||||
new_console_url="https://${new_cid}.${new_console_url_type}.${new_console_url_domain}/"
|
||||
echo "new_console_url=${new_console_url}"
|
||||
(
|
||||
cd ipfs-redirect
|
||||
|
||||
# Update record in DNSimple
|
||||
# docs: https://developer.dnsimple.com/v2/zones/records/#updateZoneRecord
|
||||
dnsimple_account_id=84895
|
||||
dnsimple_zone_name=vega.trading
|
||||
dnsimple_record_id=44409591
|
||||
# see: https://dnsimple.com/a/84895/domains/vega.trading/records/44409591/edit
|
||||
|
||||
curl -H 'Authorization: Bearer ${{ secrets.DNSIMPLE_API_TOKEN }}' \
|
||||
-H 'Accept: application/json' \
|
||||
-H 'Content-Type: application/json' \
|
||||
-X PATCH \
|
||||
-d "{
|
||||
\"content\": \"${new_console_url}\"
|
||||
}" \
|
||||
https://api.dnsimple.com/v2/${dnsimple_account_id}/zones/${dnsimple_zone_name}/records/${dnsimple_record_id}
|
||||
git status
|
||||
branch_name="rollback-to-$new_hash"
|
||||
git checkout -b "$branch_name"
|
||||
commit_msg="hash rollback to $new_hash"
|
||||
git add cidv0.txt cidv1.txt
|
||||
git commit -m "$commit_msg"
|
||||
git push -u origin "$branch_name" --force-with-lease
|
||||
pr_url="$(gh pr create --title "${commit_msg}" --body 'automated pull request to update CIDs')"
|
||||
echo $pr_url
|
||||
# once auto merge get's enabled on documentation repo let's do follow up
|
||||
sleep 5
|
||||
gh pr merge "${pr_url}" --delete-branch --squash --admin
|
||||
)
|
||||
|
||||
@@ -50,3 +50,5 @@ cypress.env.json
|
||||
|
||||
#cypress
|
||||
/apps/**/cypress/reports/
|
||||
/apps/**/cypress/downloads/
|
||||
/apps/**/fixtures/wallet/node**
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
NX_CHAIN_EXPLORER_URL=https://explorer.vega.trading/.netlify/functions/chain-explorer-api
|
||||
NX_TENDERMINT_URL=http://localhost:26617
|
||||
NX_TENDERMINT_WEBSOCKET_URL=wss://localhost:26617/websocket
|
||||
NX_VEGA_URL=http://localhost:3008/graphql
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
# App configuration variables
|
||||
NX_CHAIN_EXPLORER_URL=https://explorer.vega.trading/.netlify/functions/chain-explorer-api
|
||||
NX_TENDERMINT_URL=https://n04.d.vega.xyz/tm
|
||||
NX_TENDERMINT_WEBSOCKET_URL=wss://n04.d.vega.xyz/tm/websocket
|
||||
NX_VEGA_URL=https://api.n04.d.vega.xyz/graphql
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
# App configuration variables
|
||||
NX_CHAIN_EXPLORER_URL=https://explorer.vega.trading/.netlify/functions/chain-explorer-api
|
||||
NX_TENDERMINT_URL=https://mainnet-observer-proxy01.ops.vega.xyz/
|
||||
NX_TENDERMINT_WEBSOCKET_URL=wss://mainnet-observer-proxy01.ops.vega.xyz/websocket
|
||||
NX_VEGA_URL=https://api.vega.community/graphql
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
# App configuration variables
|
||||
NX_CHAIN_EXPLORER_URL=https://explorer.vega.trading/.netlify/functions/chain-explorer-api
|
||||
NX_TENDERMINT_URL=https://tm.n07.testnet.vega.xyz/tm
|
||||
NX_TENDERMINT_WEBSOCKET_URL=wss://lb.testnet.vega.xyz/tm/websocket
|
||||
NX_VEGA_URL=https://api.n07.testnet.vega.xyz/graphql
|
||||
|
||||
+5
-4
@@ -3,18 +3,19 @@ NX_ETHERSCAN_URL=https://sepolia.etherscan.io
|
||||
NX_HOSTED_WALLET_URL=https://wallet.testnet.vega.xyz
|
||||
NX_VEGA_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/networks-internal/main/stagnet1/vegawallet-stagnet1.toml
|
||||
NX_VEGA_ENV=STAGNET1
|
||||
NX_VEGA_EXPLORER_URL=https://stagnet1.explorer.vega.xyz
|
||||
NX_VEGA_TOKEN_URL=https://stagnet1.governance.vega.xyz
|
||||
NX_VEGA_EXPLORER_URL=https://explorer.stagnet1.vega.rocks
|
||||
NX_VEGA_TOKEN_URL=https://governance.stagnet1.vega.rocks
|
||||
NX_VEGA_WALLET_URL=http://localhost:1789
|
||||
NX_TENDERMINT_URL=https://tm.n01.stagnet1.vega.xyz
|
||||
NX_TENDERMINT_WEBSOCKET_URL=wss://tm.n01.stagnet1.vega.xyz/websocket
|
||||
NX_BLOCK_EXPLORER=https://be.stagnet1.vega.xyz/rest
|
||||
NX_ETHERSCAN_URL=https://sepolia.etherscan.io
|
||||
NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json
|
||||
NX_VEGA_GOVERNANCE_URL=https://stagnet1.governance.vega.xyz
|
||||
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.jsoo
|
||||
NX_VEGA_GOVERNANCE_URL=https://governance.stagnet1.vega.rocks
|
||||
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json
|
||||
NX_VEGA_NETWORKS='{"TESTNET":"https://explorer.fairground.wtf","MAINNET":"https://explorer.vega.xyz"}'
|
||||
NX_GITHUB_FEEDBACK_URL=https://github.com/vegaprotocol/feedback/discussions
|
||||
NX_VEGA_REPO_URL=https://github.com/vegaprotocol/vega/releases/tag/
|
||||
|
||||
# App flags
|
||||
NX_EXPLORER_ASSETS=1
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
# App configuration variables
|
||||
NX_TENDERMINT_URL=https://be.vega.community
|
||||
NX_TENDERMINT_WEBSOCKET_URL=wss://be.vega.community/websocket
|
||||
NX_SENTRY_DSN=https://b3a56b03eda842faad731f3ea9dfd1bc@o286262.ingest.sentry.io/6242427
|
||||
NX_VEGA_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/networks/master/mainnet1/mainnet1.toml
|
||||
NX_VEGA_URL=https://api.vega.community/graphql
|
||||
NX_VEGA_ENV=MAINNET
|
||||
NX_BLOCK_EXPLORER=https://be.vega.community/rest/
|
||||
NX_BLOCK_EXPLORER=https://be.vega.community/rest
|
||||
NX_ETHERSCAN_URL=https://etherscan.io
|
||||
NX_VEGA_GOVERNANCE_URL=https://governance.vega.xyz
|
||||
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/mainnet/announcements.json
|
||||
NX_VEGA_EXPLORER_URL=https://explorer.vega.xyz/
|
||||
NX_VEGA_CONSOLE_URL=https://console.vega.xyz
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
# .env is stagnet1, so there are no overrides required
|
||||
NX_VEGA_NETWORKS='{"TESTNET":"https://explorer.fairground.wtf","MAINNET":"https://explorer.vega.xyz","STAGNET3":"https://stagnet3.explorer.vega.xyz"}'
|
||||
NX_VEGA_NETWORKS='{"TESTNET":"https://explorer.fairground.wtf","MAINNET":"https://explorer.vega.xyz","STAGNET1":"https://stagnet1.explorer.vega.xyz"}'
|
||||
|
||||
@@ -10,3 +10,4 @@ NX_ETHERSCAN_URL=https://sepolia.etherscan.io
|
||||
NX_VEGA_GOVERNANCE_URL=https://governance.fairground.wtf
|
||||
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json
|
||||
NX_VEGA_EXPLORER_URL=https://explorer.fairground.wtf
|
||||
NX_VEGA_CONSOLE_URL=https://console.fairground.wtf
|
||||
|
||||
@@ -7,8 +7,9 @@ NX_ETHEREUM_PROVIDER_URL=https://sepolia.infura.io/v3/4f846e79e13f44d1b51bbd7ed9
|
||||
NX_ETHERSCAN_URL=https://sepolia.etherscan.io
|
||||
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json
|
||||
|
||||
NX_VEGA_GOVERNANCE_URL=https://validator-testnet.governance.vega.xyz
|
||||
NX_TENDERMINT_URL=https://tm-be.validators-testnet.vega.rocks
|
||||
NX_TENDERMINT_URL=https://tm.be.validators-testnet.vega.rocks
|
||||
NX_BLOCK_EXPLORER=https://be.validators-testnet.vega.rocks/rest
|
||||
NX_TENDERMINT_WEBSOCKET_URL=wss://be.validators-testnet.vega.xyz/websocket
|
||||
NX_VEGA_EXPLORER_URL=https://validator-testnet.explorer.vega.xyz/
|
||||
NX_VEGA_GOVERNANCE_URL=https://governance.validators-testnet.vega.rocks
|
||||
NX_VEGA_EXPLORER_URL=https://explorer.validators-testnet.vega.rocks/
|
||||
NX_VEGA_CONSOLE_URL=https://trading.validators-testnet.vega.rocks/
|
||||
@@ -70,7 +70,7 @@
|
||||
"executor": "@nrwl/workspace:run-commands",
|
||||
"options": {
|
||||
"commands": [
|
||||
"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"
|
||||
"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"
|
||||
]
|
||||
}
|
||||
},
|
||||
|
||||
@@ -13,6 +13,7 @@ import { DEFAULT_CACHE_CONFIG } from '@vegaprotocol/apollo-client';
|
||||
import { RouterProvider } from 'react-router-dom';
|
||||
import { router } from './routes/router-config';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { Suspense } from 'react';
|
||||
|
||||
const splashLoading = (
|
||||
<Splash>
|
||||
@@ -32,7 +33,9 @@ function App() {
|
||||
skeleton={<div>{t('Loading')}</div>}
|
||||
failure={<AppFailure title={t(`Node: ${VEGA_URL} is unsuitable`)} />}
|
||||
>
|
||||
<RouterProvider router={router} fallbackElement={splashLoading} />
|
||||
<Suspense fallback={splashLoading}>
|
||||
<RouterProvider router={router} fallbackElement={splashLoading} />
|
||||
</Suspense>
|
||||
</NodeGuard>
|
||||
<NodeSwitcherDialog
|
||||
open={nodeSwitcherOpen}
|
||||
|
||||
@@ -1,29 +1,13 @@
|
||||
import WS from 'jest-websocket-mock';
|
||||
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 { render, screen } from '@testing-library/react';
|
||||
import { BlocksRefetch } from './blocks-refetch';
|
||||
|
||||
const BlocksRefetchInWebsocketProvider = ({
|
||||
callback,
|
||||
mocketLocation,
|
||||
}: {
|
||||
callback: () => null;
|
||||
mocketLocation: string;
|
||||
}) => {
|
||||
const contextShape = useWebSocket(mocketLocation);
|
||||
|
||||
return (
|
||||
<TendermintWebsocketContext.Provider value={{ ...contextShape }}>
|
||||
<BlocksRefetch refetch={callback} />
|
||||
</TendermintWebsocketContext.Provider>
|
||||
);
|
||||
return <BlocksRefetch refetch={callback} />;
|
||||
};
|
||||
|
||||
describe('Blocks refetch', () => {
|
||||
@@ -32,111 +16,8 @@ describe('Blocks refetch', () => {
|
||||
const mocket = new WS(mocketLocation, { jsonProtocol: true });
|
||||
new WebSocket(mocketLocation);
|
||||
|
||||
render(
|
||||
<BlocksRefetchInWebsocketProvider
|
||||
callback={() => null}
|
||||
mocketLocation={mocketLocation}
|
||||
/>
|
||||
);
|
||||
await mocket.connected;
|
||||
expect(screen.getByTestId('new-blocks')).toHaveTextContent('new blocks');
|
||||
render(<BlocksRefetchInWebsocketProvider callback={() => null} />);
|
||||
expect(screen.getByTestId('refresh')).toBeInTheDocument();
|
||||
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,36 +1,19 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import { useTendermintWebsocket } from '../../hooks/use-tendermint-websocket';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { ButtonLink } from '@vegaprotocol/ui-toolkit';
|
||||
import { Button, Icon } from '@vegaprotocol/ui-toolkit';
|
||||
|
||||
interface BlocksRefetchProps {
|
||||
refetch: () => void;
|
||||
}
|
||||
|
||||
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 = () => {
|
||||
refetch();
|
||||
setBlocksToLoad(0);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="mb-4">
|
||||
<span data-testid="new-blocks">{blocksToLoad} new blocks - </span>
|
||||
<ButtonLink onClick={refresh} data-testid="refresh">
|
||||
{t('refresh to see latest')}
|
||||
</ButtonLink>
|
||||
</div>
|
||||
<Button onClick={refresh} data-testid="refresh" size="xs">
|
||||
<Icon name="refresh" className="!align-baseline mr-2" size={3} />
|
||||
{t('Load new')}
|
||||
</Button>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -4,9 +4,11 @@ import {
|
||||
} from '@vegaprotocol/environment';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { useScreenDimensions } from '@vegaprotocol/react-helpers';
|
||||
import { ExternalLink, Link } from '@vegaprotocol/ui-toolkit';
|
||||
import { Link, ExternalLink } from '@vegaprotocol/ui-toolkit';
|
||||
import { useMemo } from 'react';
|
||||
import { ENV } from '../../config/env';
|
||||
import { Routes } from '../../routes/route-names';
|
||||
import { Link as RouteLink } from 'react-router-dom';
|
||||
|
||||
export const Footer = () => {
|
||||
const { VEGA_URL, GIT_COMMIT_HASH, GIT_ORIGIN_URL } = useEnvironment();
|
||||
@@ -21,7 +23,7 @@ export const Footer = () => {
|
||||
);
|
||||
|
||||
return (
|
||||
<footer className="grid grid-rows-2 grid-cols-[1fr_auto] text-xs md:text-md md:flex md:col-span-2 px-4 py-2 gap-4 border-t border-vega-light-200 dark:border-vega-dark-200">
|
||||
<footer className="grid grid-rows-2 lg:grid-cols-[1fr_auto] text-xs md:text-md lg:flex md:col-span-2 px-4 py-2 gap-4 border-t border-vega-light-200 dark:border-vega-dark-200">
|
||||
<div className="flex justify-between gap-2 align-middle">
|
||||
{GIT_COMMIT_HASH && (
|
||||
<div className="content-center flex border-r border-neutral-700 dark:border-neutral-300 pr-4">
|
||||
@@ -56,11 +58,16 @@ export const Footer = () => {
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
<div className="pl-2 align-center lg:align-right lg:flex lg:justify-end gap-2 align-middle lg:max-w-xs lg:ml-auto">
|
||||
<RouteLink to={`/${Routes.DISCLAIMER}`} className="underline">
|
||||
Disclaimer
|
||||
</RouteLink>
|
||||
</div>
|
||||
</footer>
|
||||
);
|
||||
};
|
||||
|
||||
const NodeUrl = ({ url }: { url: string }) => {
|
||||
export const NodeUrl = ({ url }: { url: string }) => {
|
||||
// get base url from api url, api sub domain
|
||||
const urlObj = new URL(url);
|
||||
const nodeUrl = urlObj.origin.replace(/^[^.]+\./g, '');
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import type { MarketInfoWithData } from '@vegaprotocol/markets';
|
||||
import { PriceMonitoringBoundsInfoPanel } from '@vegaprotocol/markets';
|
||||
import {
|
||||
LiquidityInfoPanel,
|
||||
LiquidityMonitoringParametersInfoPanel,
|
||||
@@ -39,133 +40,69 @@ export const MarketDetails = ({ market }: { market: MarketInfoWithData }) => {
|
||||
return [];
|
||||
};
|
||||
|
||||
const oraclePanels = isEqual(
|
||||
const showTwoOracles = isEqual(
|
||||
getSigners(settlementData),
|
||||
getSigners(terminationData)
|
||||
)
|
||||
? [
|
||||
{
|
||||
title: t('Settlement Oracle'),
|
||||
content: (
|
||||
<OracleInfoPanel
|
||||
noBorder={false}
|
||||
market={market}
|
||||
type="settlementData"
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: t('Termination Oracle'),
|
||||
content: (
|
||||
<OracleInfoPanel
|
||||
noBorder={false}
|
||||
market={market}
|
||||
type="termination"
|
||||
/>
|
||||
),
|
||||
},
|
||||
]
|
||||
: [
|
||||
{
|
||||
title: t('Oracle'),
|
||||
content: (
|
||||
<OracleInfoPanel
|
||||
noBorder={false}
|
||||
market={market}
|
||||
type="settlementData"
|
||||
/>
|
||||
),
|
||||
},
|
||||
];
|
||||
);
|
||||
|
||||
const 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
|
||||
}
|
||||
/>
|
||||
</>
|
||||
),
|
||||
})),
|
||||
{
|
||||
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,
|
||||
];
|
||||
const headerClassName = 'font-alpha calt text-xl mt-4 border-b-2 pb-2';
|
||||
|
||||
return (
|
||||
<>
|
||||
{panels.map((p) => (
|
||||
<div key={p.title} className="mb-3">
|
||||
<h2 className="font-alpha calt text-xl">{p.title}</h2>
|
||||
{p.content}
|
||||
</div>
|
||||
<div>
|
||||
<h2 className={headerClassName}>{t('Key details')}</h2>
|
||||
<KeyDetailsInfoPanel market={market} />
|
||||
<h2 className={headerClassName}>{t('Instrument')}</h2>
|
||||
<InstrumentInfoPanel market={market} />
|
||||
<h2 className={headerClassName}>{t('Settlement asset')}</h2>
|
||||
<SettlementAssetInfoPanel market={market} />
|
||||
<h2 className={headerClassName}>{t('Metadata')}</h2>
|
||||
<MetadataInfoPanel market={market} />
|
||||
<h2 className={headerClassName}>{t('Risk model')}</h2>
|
||||
<RiskModelInfoPanel market={market} />
|
||||
<h2 className={headerClassName}>{t('Risk parameters')}</h2>
|
||||
<RiskParametersInfoPanel market={market} />
|
||||
<h2 className={headerClassName}>{t('Risk factors')}</h2>
|
||||
<RiskFactorsInfoPanel market={market} />
|
||||
{(market.data?.priceMonitoringBounds || []).map((trigger, i) => (
|
||||
<>
|
||||
<h2 className={headerClassName}>
|
||||
{t('Price monitoring bounds %s', [(i + 1).toString()])}
|
||||
</h2>
|
||||
<PriceMonitoringBoundsInfoPanel
|
||||
market={market}
|
||||
triggerIndex={i + 1}
|
||||
/>
|
||||
</>
|
||||
))}
|
||||
</>
|
||||
{(market.priceMonitoringSettings?.parameters?.triggers || []).map(
|
||||
(trigger, i) => (
|
||||
<>
|
||||
<h2 className={headerClassName}>
|
||||
{t('Price monitoring settings %s', [(i + 1).toString()])}
|
||||
</h2>
|
||||
<MarketInfoTable data={trigger} key={i} />
|
||||
</>
|
||||
)
|
||||
)}
|
||||
<h2 className={headerClassName}>{t('Liquidity monitoring')}</h2>
|
||||
<LiquidityMonitoringParametersInfoPanel market={market} />
|
||||
<h2 className={headerClassName}>{t('Liquidity')}</h2>
|
||||
<LiquidityInfoPanel market={market} />
|
||||
<h2 className={headerClassName}>{t('Liquidity price range')}</h2>
|
||||
<LiquidityPriceRangeInfoPanel market={market} />
|
||||
{showTwoOracles ? (
|
||||
<>
|
||||
<h2 className={headerClassName}>{t('Settlement oracle')}</h2>
|
||||
<OracleInfoPanel market={market} type="settlementData" />
|
||||
<h2 className={headerClassName}>{t('Termination oracle')}</h2>
|
||||
<OracleInfoPanel market={market} type="termination" />
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<h2 className={headerClassName}>{t('Oracle')}</h2>
|
||||
<OracleInfoPanel market={market} type="settlementData" />
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -77,22 +77,24 @@ export const MarketsTable = ({ data }: MarketsTableProps) => {
|
||||
hide={window.innerWidth <= BREAKPOINT_MD}
|
||||
valueGetter={({
|
||||
data,
|
||||
}: VegaValueGetterParams<MarketFieldsFragment, 'state'>) => {
|
||||
}: VegaValueGetterParams<MarketFieldsFragment>) => {
|
||||
return data?.state ? MarketStateMapping[data?.state] : '-';
|
||||
}}
|
||||
/>
|
||||
<AgGridColumn
|
||||
colId="asset"
|
||||
headerName={t('Settlement asset')}
|
||||
field="tradableInstrument.instrument.product.settlementAsset"
|
||||
field="tradableInstrument.instrument.product.settlementAsset.symbol"
|
||||
hide={window.innerWidth <= BREAKPOINT_MD}
|
||||
cellRenderer={({
|
||||
value,
|
||||
data,
|
||||
}: VegaICellRendererParams<
|
||||
MarketFieldsFragment,
|
||||
'tradableInstrument.instrument.product.settlementAsset'
|
||||
>) =>
|
||||
value ? (
|
||||
'tradableInstrument.instrument.product.settlementAsset.symbol'
|
||||
>) => {
|
||||
const value =
|
||||
data?.tradableInstrument.instrument.product.settlementAsset;
|
||||
return value ? (
|
||||
<ButtonLink
|
||||
onClick={(e) => {
|
||||
openAssetDetailsDialog(value.id, e.target as HTMLElement);
|
||||
@@ -102,8 +104,8 @@ export const MarketsTable = ({ data }: MarketsTableProps) => {
|
||||
</ButtonLink>
|
||||
) : (
|
||||
''
|
||||
)
|
||||
}
|
||||
);
|
||||
}}
|
||||
/>
|
||||
<AgGridColumn
|
||||
flex={2}
|
||||
|
||||
+6
-4
@@ -5,12 +5,13 @@ export const ErrorCodes = new Map([
|
||||
[51, 'Transaction failed validation'],
|
||||
[60, 'Transaction could not be decoded'],
|
||||
[70, 'Error'],
|
||||
[71, 'Partial success/error'],
|
||||
[80, 'Unknown command'],
|
||||
[89, 'Rejected as spam'],
|
||||
[0, 'Success'],
|
||||
]);
|
||||
|
||||
export const successCodes = new Set([0]);
|
||||
export const successCodes = new Set([0, 71]);
|
||||
|
||||
interface ChainResponseCodeProps {
|
||||
code: number;
|
||||
@@ -29,11 +30,12 @@ export const ChainResponseCode = ({
|
||||
error,
|
||||
}: ChainResponseCodeProps) => {
|
||||
const isSuccess = successCodes.has(code);
|
||||
|
||||
const successColour =
|
||||
code === 71 ? 'fill-vega-orange' : 'fill-vega-green-600';
|
||||
const icon = isSuccess ? (
|
||||
<Icon name="tick-circle" className="fill-vega-green-550" />
|
||||
<Icon name="tick-circle" className={successColour} />
|
||||
) : (
|
||||
<Icon name="cross" className="fill-vega-pink-550" />
|
||||
<Icon name="cross" className="fill-vega-pink-600" />
|
||||
);
|
||||
const label = ErrorCodes.get(code) || 'Unknown response code';
|
||||
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
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>
|
||||
<code className="bg-vega-light-150 px-2 rounded-md capitalize">
|
||||
{Array.from(filters)[0]}
|
||||
</code>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,164 @@
|
||||
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,6 +160,7 @@ export const TxOrderType = ({ orderType, command }: TxOrderTypeProps) => {
|
||||
vote={command?.voteSubmission?.value === 'VALUE_YES'}
|
||||
yesText="Proposal vote"
|
||||
noText="Proposal vote"
|
||||
useVoteColour={false}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React from 'react';
|
||||
import React, { useEffect, useRef } from 'react';
|
||||
import { FixedSizeList as List } from 'react-window';
|
||||
import InfiniteLoader from 'react-window-infinite-loader';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
@@ -69,6 +69,17 @@ export const TxsInfiniteList = ({
|
||||
}: TxsInfiniteListProps) => {
|
||||
const { screenSize } = useScreenDimensions();
|
||||
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 (!areTxsLoading) {
|
||||
@@ -110,6 +121,7 @@ export const TxsInfiniteList = ({
|
||||
isItemLoaded={isItemLoaded}
|
||||
itemCount={itemCount}
|
||||
loadMoreItems={loadMoreItems}
|
||||
ref={infiniteLoaderRef}
|
||||
>
|
||||
{({ onItemsRendered, ref }) => (
|
||||
<List
|
||||
|
||||
@@ -34,4 +34,17 @@ describe('Vote TX icon', () => {
|
||||
const no = render(<VoteIcon vote={false} />);
|
||||
expect(no.getByRole('img')).toHaveAttribute('aria-label', 'delete icon');
|
||||
});
|
||||
|
||||
it('useVoteColour prop can be used to override coloured background', () => {
|
||||
const no = render(<VoteIcon vote={false} />);
|
||||
expect(no.container.children[0]).toHaveClass('bg-vega-pink-550');
|
||||
|
||||
const monochromeNo = render(
|
||||
<VoteIcon vote={false} useVoteColour={false} />
|
||||
);
|
||||
expect(monochromeNo.container.children[0]).not.toHaveClass(
|
||||
'bg-vega-pink-550'
|
||||
);
|
||||
expect(monochromeNo.container.children[0]).toHaveClass('bg-vega-dark-200');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -8,6 +8,32 @@ export interface VoteIconProps {
|
||||
yesText?: string;
|
||||
// Defaults to 'Against', but can be any text
|
||||
noText?: string;
|
||||
// If set to false the background will not be coloured
|
||||
useVoteColour?: boolean;
|
||||
}
|
||||
|
||||
function getBgColour(useVoteColour: boolean, vote: boolean) {
|
||||
if (useVoteColour === false) {
|
||||
return 'bg-vega-dark-200';
|
||||
}
|
||||
|
||||
return vote ? 'bg-vega-green-550' : 'bg-vega-pink-550';
|
||||
}
|
||||
|
||||
function getFillColour(useVoteColour: boolean, vote: boolean) {
|
||||
if (useVoteColour === false) {
|
||||
return 'white';
|
||||
}
|
||||
|
||||
return vote ? 'vega-green-300' : 'vega-pink-300';
|
||||
}
|
||||
|
||||
function getTextColour(useVoteColour: boolean, vote: boolean) {
|
||||
if (useVoteColour === false) {
|
||||
return 'white';
|
||||
}
|
||||
|
||||
return vote ? 'vega-green-200' : 'vega-pink-200';
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -18,14 +44,15 @@ export interface VoteIconProps {
|
||||
*/
|
||||
export function VoteIcon({
|
||||
vote,
|
||||
useVoteColour = true,
|
||||
yesText = 'For',
|
||||
noText = 'Against',
|
||||
}: VoteIconProps) {
|
||||
const label = vote ? yesText : noText;
|
||||
const bg = vote ? 'bg-vega-green-550' : 'bg-vega-pink-550';
|
||||
const icon: IconName = vote ? 'tick-circle' : 'delete';
|
||||
const fill = vote ? 'vega-green-300' : 'vega-pink-300';
|
||||
const text = vote ? 'vega-green-200' : 'vega-pink-200';
|
||||
const bg = getBgColour(useVoteColour, vote);
|
||||
const fill = getFillColour(useVoteColour, vote);
|
||||
const text = getTextColour(useVoteColour, vote);
|
||||
|
||||
return (
|
||||
<div
|
||||
|
||||
@@ -33,7 +33,7 @@ export const getTxsDataUrl = ({ limit, filters }: IGetTxsDataUrl) => {
|
||||
// Hacky fix for param as array
|
||||
let urlAsString = url.toString();
|
||||
if (filters) {
|
||||
urlAsString += '&' + filters;
|
||||
urlAsString += '&' + filters.replace(' ', '%20');
|
||||
}
|
||||
|
||||
return urlAsString;
|
||||
@@ -65,6 +65,14 @@ export const useTxsData = ({ limit, filters }: IUseTxsData) => {
|
||||
}
|
||||
}, [setTxsState, data]);
|
||||
|
||||
useEffect(() => {
|
||||
setTxsState((prev) => ({
|
||||
txsData: [],
|
||||
hasMoreTxs: true,
|
||||
lastCursor: '',
|
||||
}));
|
||||
}, [filters]);
|
||||
|
||||
const loadTxs = useCallback(() => {
|
||||
return refetch({
|
||||
limit: limit,
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
import { render } from '@testing-library/react';
|
||||
import { OracleDetailsType } from './oracle-details-type';
|
||||
import type { SourceTypeName } from './oracle-details-type';
|
||||
import { OracleDetailsType, isInternalSourceType } from './oracle-details-type';
|
||||
import type { SourceType } from './oracle';
|
||||
import { PropertyKeyType } from '@vegaprotocol/types';
|
||||
|
||||
function renderComponent(type: SourceTypeName) {
|
||||
return <OracleDetailsType type={type} />;
|
||||
function renderComponent(type: SourceType) {
|
||||
return <OracleDetailsType sourceType={type} />;
|
||||
}
|
||||
|
||||
function renderWrappedComponent(type: SourceTypeName) {
|
||||
function renderWrappedComponent(type: SourceType) {
|
||||
return (
|
||||
<table>
|
||||
<tbody>{renderComponent(type)}</tbody>
|
||||
@@ -14,19 +15,53 @@ function renderWrappedComponent(type: SourceTypeName) {
|
||||
);
|
||||
}
|
||||
|
||||
function mock(name: string): SourceType {
|
||||
return {
|
||||
sourceType: {
|
||||
filters: [
|
||||
{
|
||||
__typename: 'Filter',
|
||||
key: {
|
||||
name,
|
||||
type: PropertyKeyType.TYPE_STRING,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
describe('Oracle type view', () => {
|
||||
it('Renders nothing when type is null', () => {
|
||||
const res = render(renderComponent(null as unknown as SourceTypeName));
|
||||
const res = render(renderComponent(null as unknown as SourceType));
|
||||
expect(res.container).toBeEmptyDOMElement();
|
||||
});
|
||||
|
||||
it('Renders Internal time for internal sources', () => {
|
||||
const res = render(renderWrappedComponent('DataSourceDefinitionInternal'));
|
||||
expect(res.getByText('Internal time')).toBeInTheDocument();
|
||||
it('Renders Internal time for internal sources - timestamp', () => {
|
||||
const s = mock('vegaprotocol.builtin.timestamp');
|
||||
expect(isInternalSourceType(s)).toEqual(true);
|
||||
const res = render(renderWrappedComponent(s));
|
||||
expect(res.getByText('Internal data')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('Renders Internal time for internal sources - potential future types', () => {
|
||||
const s = mock('vegaprotocol.builtin.boolean');
|
||||
expect(isInternalSourceType(s)).toEqual(true);
|
||||
const res = render(renderWrappedComponent(s));
|
||||
expect(res.getByText('Internal data')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('Renders External data otherwise - prices.external.whatever', () => {
|
||||
const s = mock('prices.external.whatever');
|
||||
expect(isInternalSourceType(s)).toEqual(false);
|
||||
const res = render(renderWrappedComponent(s));
|
||||
expect(res.getByText('External data')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('Renders External data otherwise', () => {
|
||||
const res = render(renderWrappedComponent('DataSourceDefinitionExternal'));
|
||||
const s = mock('prices.external.vegaprotocol.builtin.');
|
||||
expect(isInternalSourceType(s)).toEqual(false);
|
||||
const res = render(renderWrappedComponent(s));
|
||||
expect(res.getByText('External data')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,27 +1,51 @@
|
||||
import { TableRow, TableCell, TableHeader } from '../../../components/table';
|
||||
import type { SourceType } from './oracle';
|
||||
|
||||
export type SourceTypeName = SourceType['__typename'] | undefined;
|
||||
/**
|
||||
* Basic function to determine if a source is internal or external.
|
||||
*
|
||||
* This should be distinguishable using __typename, but the type is incorrectly
|
||||
* reported at the moment, so instead we check the filters.
|
||||
*
|
||||
* @param s SourceType
|
||||
* @returns boolean True if the source is internal
|
||||
*/
|
||||
export function isInternalSourceType(s: SourceType) {
|
||||
if ('filters' in s.sourceType) {
|
||||
const filters = s.sourceType.filters;
|
||||
|
||||
if (filters) {
|
||||
return (
|
||||
filters?.filter((f) => {
|
||||
return f.key.name?.indexOf('vegaprotocol.builtin.') === 0;
|
||||
}).length > 0
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
interface OracleDetailsTypeProps {
|
||||
type: SourceTypeName;
|
||||
sourceType: SourceType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders a a single table row for the Oracle Details view that shows
|
||||
* if the oracle is using the internal time oracle or external data
|
||||
*/
|
||||
export function OracleDetailsType({ type }: OracleDetailsTypeProps) {
|
||||
if (!type) {
|
||||
export function OracleDetailsType({ sourceType }: OracleDetailsTypeProps) {
|
||||
if (!sourceType) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const isInternal = isInternalSourceType(sourceType);
|
||||
|
||||
return (
|
||||
<TableRow modifier="bordered">
|
||||
<TableHeader scope="row">Type</TableHeader>
|
||||
<TableCell modifier="bordered">
|
||||
{type === 'DataSourceDefinitionInternal'
|
||||
? 'Internal time'
|
||||
: 'External data'}
|
||||
{isInternal ? 'Internal data' : 'External data'}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
);
|
||||
|
||||
@@ -53,7 +53,7 @@ export const OracleDetails = ({
|
||||
<OracleLink id={id} />
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
<OracleDetailsType type={sourceType.__typename} />
|
||||
<OracleDetailsType sourceType={sourceType} />
|
||||
<OracleSigners sourceType={sourceType} />
|
||||
<OracleMarkets id={id} />
|
||||
<TableRow modifier="bordered">
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { RouteTitle } from '../../components/route-title';
|
||||
|
||||
export const Disclaimer = () => {
|
||||
return (
|
||||
<section>
|
||||
<div className="px-40 max-sm:px-0 max-md:px-10 mb-4 max-w-5xl">
|
||||
<RouteTitle data-testid="disclaimer-header">
|
||||
{t('Disclaimer')}
|
||||
</RouteTitle>
|
||||
<p className="mt-3">
|
||||
The Vega Block Explorer is an application that allows users to, among
|
||||
other things, browse through blocks, view wallet addresses, network
|
||||
hashrate, transaction data and other key information on the Vega
|
||||
blockchain. It is free, public and open source software. Software
|
||||
upgrades may contain bugs or security vulnerabilities that might
|
||||
result in loss of functionality.
|
||||
</p>
|
||||
<p className="mt-3">
|
||||
The Vega Block Explorer uses data from nodes on the Vega Blockchain.
|
||||
The developers of the Vega Block Explorer do not operate or run the
|
||||
Vega Blockchain or any other blockchain.
|
||||
</p>
|
||||
<p className="mt-3 font-semibold">
|
||||
The Vega Block Explorer is provided “as is”. The developers of the
|
||||
Vega Block Explorer make no representations or warranties of any kind,
|
||||
whether express or implied, statutory or otherwise regarding the Vega
|
||||
Block Explorer. They disclaim all warranties of merchantability,
|
||||
quality, fitness for purpose. They disclaim all warranties that the
|
||||
Vega Block Explorer is free of harmful components or errors.
|
||||
</p>
|
||||
<p className="mt-3 font-bold">
|
||||
No developer of the Vega Block Explorer accepts any responsibility
|
||||
for, or liability to users in connection with their use of the Vega
|
||||
Block Explorer.
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
@@ -10,4 +10,5 @@ export const Routes = {
|
||||
MARKETS: 'markets',
|
||||
ORACLES: 'oracles',
|
||||
NETWORK_PARAMETERS: 'network-parameters',
|
||||
DISCLAIMER: 'disclaimer',
|
||||
};
|
||||
|
||||
@@ -29,6 +29,7 @@ import { AssetLink, MarketLink } from '../components/links';
|
||||
import { truncateMiddle } from '@vegaprotocol/ui-toolkit';
|
||||
import { remove0x } from '@vegaprotocol/utils';
|
||||
import { PartyAccountsByAsset } from './parties/id/accounts';
|
||||
import { Disclaimer } from './pages/disclaimer';
|
||||
|
||||
export type Navigable = {
|
||||
path: string;
|
||||
@@ -335,6 +336,17 @@ export const routerConfig: Route[] = [
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: Routes.DISCLAIMER,
|
||||
element: <Disclaimer />,
|
||||
handle: {
|
||||
name: t('Disclaimer'),
|
||||
text: t('Disclaimer'),
|
||||
breadcrumb: () => (
|
||||
<Link to={Routes.DISCLAIMER}>{t('Disclaimer')}</Link>
|
||||
),
|
||||
},
|
||||
},
|
||||
...partiesRoutes,
|
||||
...assetsRoutes,
|
||||
...genesisRoutes,
|
||||
|
||||
@@ -5,17 +5,43 @@ import { TxsInfiniteList } from '../../../components/txs';
|
||||
import { useTxsData } from '../../../hooks/use-txs-data';
|
||||
import { useDocumentTitle } from '../../../hooks/use-document-title';
|
||||
|
||||
const BE_TXS_PER_REQUEST = 20;
|
||||
import { useState } from 'react';
|
||||
import { AllFilterOptions, TxsFilter } from '../../../components/txs/tx-filter';
|
||||
|
||||
const BE_TXS_PER_REQUEST = 15;
|
||||
|
||||
export const TxsList = () => {
|
||||
useDocumentTitle(['Transactions']);
|
||||
|
||||
const { hasMoreTxs, loadTxs, error, txsData, refreshTxs, loading } =
|
||||
useTxsData({ limit: BE_TXS_PER_REQUEST });
|
||||
return (
|
||||
<section className="md:p-2 lg:p-4 xl:p-6">
|
||||
<section className="md:p-2 lg:p-4 xl:p-6 relative">
|
||||
<RouteTitle>{t('Transactions')}</RouteTitle>
|
||||
<BlocksRefetch refetch={refreshTxs} />
|
||||
<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 } =
|
||||
useTxsData({
|
||||
limit: BE_TXS_PER_REQUEST,
|
||||
filters: f,
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
<menu className="mb-2">
|
||||
<BlocksRefetch refetch={refreshTxs} />
|
||||
<TxsFilter filters={filters} setFilters={setFilters} />
|
||||
</menu>
|
||||
|
||||
<TxsInfiniteList
|
||||
hasMoreTxs={hasMoreTxs}
|
||||
areTxsLoading={loading}
|
||||
@@ -24,6 +50,6 @@ export const TxsList = () => {
|
||||
error={error}
|
||||
className="mb-28"
|
||||
/>
|
||||
</section>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
+428
-421
File diff suppressed because it is too large
Load Diff
@@ -26,6 +26,7 @@ module.exports = defineConfig({
|
||||
viewportWidth: 1440,
|
||||
viewportHeight: 900,
|
||||
numTestsKeptInMemory: 5,
|
||||
downloadsFolder: 'cypress/downloads',
|
||||
testIsolation: false,
|
||||
},
|
||||
env: {
|
||||
@@ -48,7 +49,7 @@ module.exports = defineConfig({
|
||||
vegaTokenContractAddress: '0xF41bD86d462D36b997C0bbb4D97a0a3382f205B7',
|
||||
vegaTokenAddress: '0x67175Da1D5e966e40D11c4B2519392B2058373de',
|
||||
txTimeout: { timeout: 70000 },
|
||||
epochTimeout: { timeout: 6000 },
|
||||
epochTimeout: { timeout: 10000 },
|
||||
blockConfirmations: 3,
|
||||
grepTags: '@regression @smoke @slow',
|
||||
grepFilterSpecs: true,
|
||||
|
||||
@@ -14,9 +14,16 @@ import {
|
||||
submitUniqueRawProposal,
|
||||
voteForProposal,
|
||||
} from '../../../../governance-e2e/src/support/governance.functions';
|
||||
import { ensureSpecifiedUnstakedTokensAreAssociated } from '../../../../governance-e2e/src/support/staking.functions';
|
||||
import {
|
||||
ensureSpecifiedUnstakedTokensAreAssociated,
|
||||
stakingPageAssociateTokens,
|
||||
stakingPageDisassociateAllTokens,
|
||||
} from '../../../../governance-e2e/src/support/staking.functions';
|
||||
import { ethereumWalletConnect } from '../../../../governance-e2e/src/support/wallet-eth.functions';
|
||||
import { vegaWalletSetSpecifiedApprovalAmount } from '../../../../governance-e2e/src/support/wallet-teardown.functions';
|
||||
import {
|
||||
switchVegaWalletPubKey,
|
||||
vegaWalletSetSpecifiedApprovalAmount,
|
||||
} from '../../support/wallet-functions';
|
||||
import type { testFreeformProposal } from '../../support/common-interfaces';
|
||||
import { formatDateWithLocalTimezone } from '@vegaprotocol/utils';
|
||||
|
||||
@@ -33,8 +40,9 @@ const proposalDetailsTitle = '[data-testid="proposal-title"]';
|
||||
const proposalDetailsDescription = '[data-testid="proposal-description"]';
|
||||
const openProposals = '[data-testid="open-proposals"]';
|
||||
const viewProposalButton = '[data-testid="view-proposal-btn"]';
|
||||
const proposalDescriptionToggle = 'proposal-description-toggle';
|
||||
const voteBreakdownToggle = 'vote-breakdown-toggle';
|
||||
const proposalTermsToggle = 'proposal-terms-toggle';
|
||||
const proposalTermsToggle = 'proposal-json-toggle';
|
||||
|
||||
describe(
|
||||
'Governance flow for proposal details',
|
||||
@@ -43,7 +51,7 @@ describe(
|
||||
before('connect wallets and set approval limit', function () {
|
||||
cy.visit('/');
|
||||
ethereumWalletConnect();
|
||||
cy.associateTokensToVegaWallet('1');
|
||||
// cy.associateTokensToVegaWallet('1');
|
||||
});
|
||||
|
||||
beforeEach('visit proposals tab', function () {
|
||||
@@ -73,16 +81,28 @@ describe(
|
||||
'contain.text',
|
||||
rawProposal.rationale.title
|
||||
);
|
||||
cy.getByTestId(proposalDescriptionToggle).click();
|
||||
cy.getByTestId('proposal-description-toggle');
|
||||
cy.get(proposalDetailsDescription)
|
||||
.find('p')
|
||||
.should('have.text', proposalDescription);
|
||||
});
|
||||
// 3001-VOTE-008
|
||||
getProposalInformationFromTable('ID')
|
||||
.invoke('text')
|
||||
.should('not.be.empty')
|
||||
.and('have.length', 64);
|
||||
// 3001-VOTE-009
|
||||
getProposalInformationFromTable('Proposed by')
|
||||
.invoke('text')
|
||||
.should('not.be.empty')
|
||||
.and('have.length', 64);
|
||||
cy.getByTestId(proposalTermsToggle).click();
|
||||
// 3001-VOTE-052
|
||||
// 3001-VOTE-052 3001-VOTE-010
|
||||
cy.get('code.language-json')
|
||||
.should('exist')
|
||||
.within(() => {
|
||||
cy.get('.hljs-string').eq(0).should('have.text', '"ProposalTerms"');
|
||||
cy.get('.hljs-attr').eq(0).should('have.text', '"id"');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -90,8 +110,6 @@ describe(
|
||||
it('Newly created freeform proposal details - shows proposed and closing dates', function () {
|
||||
const proposalTitle = generateFreeFormProposalTitle();
|
||||
const proposalTimeStamp = createTenDigitUnixTimeStampForSpecifiedDays(3);
|
||||
// const currentDate = new Date(createTenDigitUnixTimeStampForSpecifiedDays(0) * 1000)
|
||||
// const proposedDate = new Date(currentDate.getTime() + 60000)
|
||||
|
||||
submitUniqueRawProposal({
|
||||
proposalTitle: proposalTitle,
|
||||
@@ -109,16 +127,9 @@ describe(
|
||||
closingDate
|
||||
);
|
||||
});
|
||||
cy.wrap(
|
||||
formatDateWithLocalTimezone(
|
||||
new Date(createTenDigitUnixTimeStampForSpecifiedDays(0) * 1000)
|
||||
)
|
||||
).then((proposalDate) => {
|
||||
getProposalInformationFromTable('Proposed on').should(
|
||||
'have.text',
|
||||
proposalDate
|
||||
);
|
||||
});
|
||||
getProposalInformationFromTable('Proposed on')
|
||||
.invoke('text')
|
||||
.should('not.be.empty');
|
||||
});
|
||||
|
||||
it('Newly created proposal details - shows default status set to fail', function () {
|
||||
@@ -293,5 +304,33 @@ describe(
|
||||
.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,8 +14,9 @@ import {
|
||||
createUpdateNetworkProposalTxBody,
|
||||
createFreeFormProposalTxBody,
|
||||
} from '../../support/proposal.functions';
|
||||
import { ensureSpecifiedUnstakedTokensAreAssociated } from '../../support/staking.functions';
|
||||
import { ethereumWalletConnect } from '../../support/wallet-eth.functions';
|
||||
import { vegaWalletSetSpecifiedApprovalAmount } from '../../support/wallet-teardown.functions';
|
||||
import { vegaWalletSetSpecifiedApprovalAmount } from '../../support/wallet-functions';
|
||||
|
||||
const proposalListItem = '[data-testid="proposals-list-item"]';
|
||||
const closedProposals = '[data-testid="closed-proposals"]';
|
||||
@@ -43,6 +44,7 @@ context(
|
||||
waitForSpinner();
|
||||
cy.connectVegaWallet();
|
||||
ethereumWalletConnect();
|
||||
ensureSpecifiedUnstakedTokensAreAssociated('1');
|
||||
navigateTo(navigation.proposals);
|
||||
});
|
||||
|
||||
|
||||
@@ -11,7 +11,6 @@ import {
|
||||
governanceProposalType,
|
||||
submitUniqueRawProposal,
|
||||
voteForProposal,
|
||||
waitForProposalSubmitted,
|
||||
waitForProposalSync,
|
||||
} from '../../support/governance.functions';
|
||||
|
||||
@@ -33,7 +32,7 @@ import {
|
||||
import {
|
||||
vegaWalletSetSpecifiedApprovalAmount,
|
||||
vegaWalletTeardown,
|
||||
} from '../../support/wallet-teardown.functions';
|
||||
} from '../../support/wallet-functions';
|
||||
import { ethereumWalletConnect } from '../../support/wallet-eth.functions';
|
||||
import type { testFreeformProposal } from '../../support/common-interfaces';
|
||||
|
||||
@@ -46,11 +45,7 @@ const connectToVegaWalletButton = '[data-testid="connect-to-vega-wallet-btn"]';
|
||||
const newProposalSubmitButton = '[data-testid="proposal-submit"]';
|
||||
const viewProposalButton = '[data-testid="view-proposal-btn"]';
|
||||
const rawProposalData = '[data-testid="proposal-data"]';
|
||||
const minVoteButton = '[data-testid="min-vote"]';
|
||||
const maxVoteButton = '[data-testid="max-vote"]';
|
||||
const voteButtons = '[data-testid="vote-buttons"]';
|
||||
const votingDate = '[data-testid="voting-date"]';
|
||||
const voteTwoMinExtraNote = '[data-testid="voting-2-mins-extra"]';
|
||||
const rejectProposalsLink = '[href="/proposals/rejected"]';
|
||||
const feedbackError = '[data-testid="Error"]';
|
||||
const noOpenProposals = '[data-testid="no-open-proposals"]';
|
||||
@@ -106,8 +101,7 @@ context(
|
||||
.and('have.text', 'There are no enacted or rejected proposals');
|
||||
});
|
||||
|
||||
// 3002-PROP-002
|
||||
// 3002-PROP-003
|
||||
// 3002-PROP-002 3002-PROP-003 3001-VOTE-012 3007-PNE-020 3004-PMAC-004 3005-PASN-004 3008-PFRO-016 3003-PMAN-004
|
||||
it('Proposal form - shows how many vega tokens are required to make a proposal', function () {
|
||||
// 3002-PROP-005
|
||||
goToMakeNewProposal(governanceProposalType.NEW_MARKET);
|
||||
@@ -116,23 +110,14 @@ context(
|
||||
).should('be.visible');
|
||||
});
|
||||
|
||||
// Skipping as currently unable to propose using forms other than raw
|
||||
// 3002-PROP-011
|
||||
it.skip('Able to submit a valid freeform proposal - with minimum required tokens associated', function () {
|
||||
goToMakeNewProposal(governanceProposalType.FREEFORM);
|
||||
cy.get(minVoteButton).should('be.visible'); // 3002-PROP-008
|
||||
cy.get(maxVoteButton).should('be.visible');
|
||||
cy.get(votingDate).should('not.be.empty');
|
||||
cy.get(voteTwoMinExtraNote).should(
|
||||
'contain.text',
|
||||
'we add 2 minutes of extra time'
|
||||
);
|
||||
enterUniqueFreeFormProposalBody('50', generateFreeFormProposalTitle());
|
||||
// 3002-PROP-012
|
||||
// 3002-PROP-016
|
||||
waitForProposalSubmitted();
|
||||
// 3002-PROP-011 3008-PFRO-005
|
||||
it('Able to submit a valid freeform proposal - with minimum required tokens associated', function () {
|
||||
ensureSpecifiedUnstakedTokensAreAssociated('1');
|
||||
verifyUnstakedBalance(1);
|
||||
createRawProposal();
|
||||
});
|
||||
|
||||
// 3008-PFRO-002 3008-PFRO-004
|
||||
it('Able to submit a valid freeform proposal - with minimum required tokens associated - but also staked', function () {
|
||||
ensureSpecifiedUnstakedTokensAreAssociated('2');
|
||||
verifyUnstakedBalance(2);
|
||||
@@ -148,10 +133,6 @@ context(
|
||||
it.skip('Creating a proposal - proposal rejected - when closing time sooner than system default', function () {
|
||||
goToMakeNewProposal(governanceProposalType.FREEFORM);
|
||||
enterUniqueFreeFormProposalBody('0.1', generateFreeFormProposalTitle());
|
||||
|
||||
cy.contains('Awaiting network confirmation', epochTimeout).should(
|
||||
'not.exist'
|
||||
);
|
||||
cy.get('input:invalid')
|
||||
.invoke('prop', 'validationMessage')
|
||||
.should('equal', 'Value must be greater than or equal to 1.');
|
||||
@@ -280,6 +261,20 @@ context(
|
||||
closeDialog();
|
||||
});
|
||||
|
||||
// 3007-PNE-022 3007-PNE-023 3004-PMAC-006 3004-PMAC-007 3005-PASN-006 3005-PASN-007
|
||||
// 3006-PASC-006 3006-PASC-007 3008-PFRO-018 3008-PFRO-019 3003-PMAN-006 3003-PMAN-007
|
||||
it('Unable to submit proposal without valid json', function () {
|
||||
goToMakeNewProposal(governanceProposalType.RAW);
|
||||
cy.get(newProposalSubmitButton).click();
|
||||
cy.getByTestId('input-error-text').should('have.text', 'Required');
|
||||
cy.get(rawProposalData).type('Not a valid json string');
|
||||
cy.get(newProposalSubmitButton).click();
|
||||
cy.getByTestId('input-error-text').should(
|
||||
'have.text',
|
||||
'Must be valid JSON'
|
||||
);
|
||||
});
|
||||
|
||||
// 1005-PROP-009
|
||||
it('Unable to vote on a freeform proposal - when some but not enough vega associated', function () {
|
||||
const proposalTitle = generateFreeFormProposalTitle();
|
||||
|
||||
@@ -1,24 +1,33 @@
|
||||
import {
|
||||
closeDialog,
|
||||
dissociateFromSecondWalletKey,
|
||||
navigateTo,
|
||||
navigation,
|
||||
turnTelemetryOff,
|
||||
waitForSpinner,
|
||||
} from '../../support/common.functions';
|
||||
import {
|
||||
getDownloadedProposalJsonPath,
|
||||
submitUniqueRawProposal,
|
||||
} from '../../support/governance.functions';
|
||||
import {
|
||||
getProposalInformationFromTable,
|
||||
goToMakeNewProposal,
|
||||
governanceProposalType,
|
||||
voteForProposal,
|
||||
waitForProposalSubmitted,
|
||||
} from '../../support/governance.functions';
|
||||
import { ensureSpecifiedUnstakedTokensAreAssociated } from '../../support/staking.functions';
|
||||
import {
|
||||
ensureSpecifiedUnstakedTokensAreAssociated,
|
||||
stakingPageAssociateTokens,
|
||||
stakingPageDisassociateAllTokens,
|
||||
} from '../../support/staking.functions';
|
||||
import { ethereumWalletConnect } from '../../support/wallet-eth.functions';
|
||||
import {
|
||||
switchVegaWalletPubKey,
|
||||
vegaWalletFaucetAssetsWithoutCheck,
|
||||
vegaWalletSetSpecifiedApprovalAmount,
|
||||
vegaWalletTeardown,
|
||||
} from '../../support/wallet-teardown.functions';
|
||||
import { vegaWalletFaucetAssetsWithoutCheck } from '../../support/wallet-vega.functions';
|
||||
} from '../../support/wallet-functions';
|
||||
|
||||
const proposalListItem = '[data-testid="proposals-list-item"]';
|
||||
const openProposals = '[data-testid="open-proposals"]';
|
||||
@@ -26,15 +35,11 @@ const proposalType = '[data-testid="proposal-type"]';
|
||||
const proposalDetails = '[data-testid="proposal-details"]';
|
||||
const newProposalSubmitButton = '[data-testid="proposal-submit"]';
|
||||
const proposalVoteDeadline = '[data-testid="proposal-vote-deadline"]';
|
||||
const proposalValidationDeadline =
|
||||
'[data-testid="proposal-validation-deadline"]';
|
||||
const proposalParameterSelect = '[data-testid="proposal-parameter-select"]';
|
||||
const proposalMarketSelect = '[data-testid="proposal-market-select"]';
|
||||
const newProposalTitle = '[data-testid="proposal-title"]';
|
||||
const newProposalDescription = '[data-testid="proposal-description"]';
|
||||
const newProposalTerms = '[data-testid="proposal-terms"]';
|
||||
const currentParameterValue =
|
||||
'[data-testid="selected-proposal-param-current-value"]';
|
||||
const newProposedParameterValue =
|
||||
'[data-testid="selected-proposal-param-new-value"]';
|
||||
const minVoteDeadline = '[data-testid="min-vote"]';
|
||||
@@ -54,11 +59,10 @@ const proposalTermsSection = 'proposal';
|
||||
const vegaWalletPublicKey = Cypress.env('vegaWalletPublicKey');
|
||||
const fUSDCId =
|
||||
'816af99af60d684502a40824758f6b5377e6af48e50a9ee8ef478ecb879ea8bc';
|
||||
const epochTimeout = Cypress.env('epochTimeout');
|
||||
const proposalTimeout = { timeout: 14000 };
|
||||
|
||||
// 3001-VOTE-007
|
||||
context.skip(
|
||||
context(
|
||||
'Governance flow - form validations for different governance proposals',
|
||||
{ tags: '@slow' },
|
||||
function () {
|
||||
@@ -79,28 +83,10 @@ context.skip(
|
||||
navigateTo(navigation.proposals);
|
||||
});
|
||||
|
||||
it('Able to submit valid update network parameter proposal', function () {
|
||||
goToMakeNewProposal(governanceProposalType.NETWORK_PARAMETER);
|
||||
// 3002-PROP-006
|
||||
cy.get(newProposalTitle).type('Test update network parameter proposal');
|
||||
// 3002-PROP-007
|
||||
cy.get(newProposalDescription).type('E2E test for proposals');
|
||||
|
||||
cy.get(proposalParameterSelect).find('option').should('have.length', 117);
|
||||
cy.get(proposalParameterSelect).select(
|
||||
// 3007-PNEC-002
|
||||
'governance_proposal_asset_minEnact'
|
||||
);
|
||||
cy.get(currentParameterValue).should('have.value', '2s');
|
||||
cy.get(newProposedParameterValue).type('5s'); // 3007-PNEC-003
|
||||
cy.get(newProposalSubmitButton).should('be.visible').click();
|
||||
waitForProposalSubmitted();
|
||||
});
|
||||
|
||||
it('Unable to submit network parameter with missing/invalid fields', function () {
|
||||
navigateTo(navigation.proposals);
|
||||
goToMakeNewProposal(governanceProposalType.NETWORK_PARAMETER);
|
||||
cy.get(newProposalSubmitButton).should('be.visible').click();
|
||||
cy.get(proposalDownloadBtn).click();
|
||||
cy.get(inputError).should('have.length', 3);
|
||||
cy.get(newProposalTitle).type(
|
||||
'Invalid update network parameter proposal'
|
||||
@@ -111,62 +97,78 @@ context.skip(
|
||||
);
|
||||
cy.get(newProposedParameterValue).type('0');
|
||||
cy.get(proposalVoteDeadline).clear().type('0');
|
||||
cy.get(newProposalSubmitButton).click();
|
||||
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'
|
||||
);
|
||||
});
|
||||
|
||||
it('Able to download network param proposal json', function () {
|
||||
const downloadFolder = './cypress/downloads/';
|
||||
goToMakeNewProposal(governanceProposalType.NETWORK_PARAMETER);
|
||||
cy.log('Download proposal file');
|
||||
cy.get(proposalDownloadBtn)
|
||||
.should('be.visible')
|
||||
.click()
|
||||
.then(() => {
|
||||
const filename =
|
||||
downloadFolder +
|
||||
'vega-network-param-proposal-' +
|
||||
getFormattedTime() +
|
||||
'.json';
|
||||
cy.readFile(filename, proposalTimeout)
|
||||
.its('terms.updateNetworkParameter')
|
||||
.should('exist');
|
||||
cy.wrap(
|
||||
getDownloadedProposalJsonPath('vega-network-param-proposal-')
|
||||
).then((filePath) => {
|
||||
goToMakeNewProposal(governanceProposalType.RAW);
|
||||
submitUniqueRawProposal({ proposalBody: filePath, submit: false });
|
||||
});
|
||||
});
|
||||
cy.get(newProposalSubmitButton).click();
|
||||
validateDialogContentMsg('PROPOSAL_ERROR_CLOSE_TIME_TOO_SOON');
|
||||
});
|
||||
|
||||
// 3007-PNEC-001 3007-PNEC-003
|
||||
it('Able to download and submit network param proposal', function () {
|
||||
goToMakeNewProposal(governanceProposalType.NETWORK_PARAMETER);
|
||||
// 3007-PNEC-006
|
||||
cy.get(newProposalTitle)
|
||||
.siblings()
|
||||
.should('contain.text', '(100 characters or less)');
|
||||
// 3007-PNEC-004 3007-PNEC-005
|
||||
cy.get(newProposalTitle).type('Test update network parameter proposal');
|
||||
// 3007-PNEC-009
|
||||
cy.get(newProposalDescription)
|
||||
.siblings()
|
||||
.should('contain.text', '(20,000 characters or less)');
|
||||
// 3007-PNEC-007 3007-PNEC-008
|
||||
cy.get(newProposalDescription).type('E2E test for downloading proposals');
|
||||
// 3007-PNEC-010
|
||||
cy.get(proposalParameterSelect).select(
|
||||
'governance_proposal_asset_minClose'
|
||||
);
|
||||
// 3007-PNEC-011
|
||||
cy.get(newProposedParameterValue).type('10s');
|
||||
|
||||
// 3007-PNEC-012
|
||||
cy.get(proposalVoteDeadline).clear().type('2');
|
||||
// 3007-PNEC-013 3007-PNEC-014
|
||||
cy.getByTestId('voting-date').invoke('text').should('not.be.empty');
|
||||
// 3007-PNEC-015
|
||||
cy.get(maxEnactDeadline).click();
|
||||
// 3007-PNEC-016
|
||||
cy.getByTestId('enactment-date').invoke('text').should('not.be.empty');
|
||||
// 3007-PNEC-017
|
||||
cy.contains(
|
||||
'Time till enactment (must be equal to or after vote close)'
|
||||
).should('be.visible');
|
||||
// 3007-PNE-018
|
||||
cy.log('Download updated proposal file');
|
||||
cy.get(proposalDownloadBtn)
|
||||
.should('be.visible')
|
||||
.click()
|
||||
.then(() => {
|
||||
const filename =
|
||||
downloadFolder +
|
||||
'vega-network-param-proposal-' +
|
||||
getFormattedTime() +
|
||||
'.json';
|
||||
cy.get(proposalDownloadBtn).should('be.visible').click();
|
||||
cy.readFile(filename, proposalTimeout).then((jsonFile) => {
|
||||
cy.wrap(jsonFile)
|
||||
.its('rationale.description')
|
||||
.should('eq', 'E2E test for downloading proposals');
|
||||
cy.wrap(jsonFile)
|
||||
.its('terms.updateNetworkParameter.changes.key')
|
||||
.should('eq', 'governance.proposal.asset.minClose');
|
||||
cy.wrap(jsonFile)
|
||||
.its('terms.updateNetworkParameter.changes.value')
|
||||
.should('eq', '10s');
|
||||
cy.wrap(
|
||||
getDownloadedProposalJsonPath('vega-network-param-proposal-')
|
||||
).then((filePath) => {
|
||||
cy.readFile(String(filePath), { timeout: 14000 }).then(
|
||||
(jsonFile) => {
|
||||
cy.wrap(jsonFile)
|
||||
.its('rationale.description')
|
||||
.should('eq', 'E2E test for downloading proposals');
|
||||
cy.wrap(jsonFile)
|
||||
.its('terms.updateNetworkParameter.changes.key')
|
||||
.should('eq', 'governance.proposal.asset.minClose');
|
||||
cy.wrap(jsonFile)
|
||||
.its('terms.updateNetworkParameter.changes.value')
|
||||
.should('eq', '10s');
|
||||
}
|
||||
);
|
||||
// 3007-PNE-019
|
||||
goToMakeNewProposal(governanceProposalType.RAW);
|
||||
submitUniqueRawProposal({ proposalBody: filePath });
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -186,38 +188,58 @@ context.skip(
|
||||
'have.text',
|
||||
'Proposal will fail if enactment is earlier than the voting deadline'
|
||||
);
|
||||
cy.get(proposalDownloadBtn)
|
||||
.should('be.visible')
|
||||
.click()
|
||||
.then(() => {
|
||||
cy.wrap(
|
||||
getDownloadedProposalJsonPath('vega-network-param-proposal-')
|
||||
).then((filePath) => {
|
||||
goToMakeNewProposal(governanceProposalType.RAW);
|
||||
submitUniqueRawProposal({ proposalBody: filePath, submit: false });
|
||||
});
|
||||
});
|
||||
cy.get(newProposalSubmitButton).click();
|
||||
cy.get(feedbackError).should(
|
||||
'have.text',
|
||||
validateFeedBackMsg(
|
||||
'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
|
||||
it('Able to submit valid new market proposal', function () {
|
||||
goToMakeNewProposal(governanceProposalType.NEW_MARKET);
|
||||
cy.get(newProposalTitle).type('Test new market proposal');
|
||||
cy.get(newProposalDescription).type('E2E test for proposals');
|
||||
cy.fixture('/proposals/new-market').then((newMarketProposal) => {
|
||||
const newMarketPayload = JSON.stringify(newMarketProposal);
|
||||
cy.get(newProposalTerms).type(newMarketPayload, {
|
||||
parseSpecialCharSequences: false,
|
||||
delay: 2,
|
||||
it(
|
||||
'Able to submit valid new market proposal',
|
||||
{ tags: '@smoke' },
|
||||
function () {
|
||||
goToMakeNewProposal(governanceProposalType.NEW_MARKET);
|
||||
cy.get(newProposalTitle).type('Test new market proposal');
|
||||
cy.get(newProposalDescription).type('E2E test for proposals');
|
||||
cy.fixture('/proposals/new-market').then((newMarketProposal) => {
|
||||
const newMarketPayload = JSON.stringify(newMarketProposal);
|
||||
cy.get(newProposalTerms).type(newMarketPayload, {
|
||||
parseSpecialCharSequences: false,
|
||||
delay: 2,
|
||||
});
|
||||
});
|
||||
});
|
||||
cy.get(newProposalSubmitButton).should('be.visible').click();
|
||||
waitForProposalSubmitted();
|
||||
});
|
||||
cy.get(proposalDownloadBtn)
|
||||
.should('be.visible')
|
||||
.click()
|
||||
.then(() => {
|
||||
cy.wrap(
|
||||
getDownloadedProposalJsonPath('vega-new-market-proposal-')
|
||||
).then((filePath) => {
|
||||
goToMakeNewProposal(governanceProposalType.RAW);
|
||||
submitUniqueRawProposal({ proposalBody: filePath }); // 3003-PMAN-003
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
it('Unable to submit new market proposal with missing/invalid fields', function () {
|
||||
const errorMsg =
|
||||
'Invalid params: the transaction does not use a valid Vega command: unknown field "invalid" in vega.NewMarket';
|
||||
|
||||
goToMakeNewProposal(governanceProposalType.NEW_MARKET);
|
||||
cy.get(newProposalSubmitButton).should('be.visible').click();
|
||||
cy.get(proposalDownloadBtn).should('be.visible').click();
|
||||
cy.get(inputError).should('have.length', 3);
|
||||
cy.get(newProposalTitle).type('Test new market proposal');
|
||||
cy.get(newProposalDescription).type('E2E test for proposals');
|
||||
@@ -229,14 +251,27 @@ context.skip(
|
||||
delay: 2,
|
||||
});
|
||||
});
|
||||
cy.get(proposalDownloadBtn)
|
||||
.should('be.visible')
|
||||
.click()
|
||||
.then(() => {
|
||||
cy.wrap(
|
||||
getDownloadedProposalJsonPath('vega-new-market-proposal-')
|
||||
).then((filePath) => {
|
||||
goToMakeNewProposal(governanceProposalType.RAW);
|
||||
submitUniqueRawProposal({ proposalBody: filePath, submit: false });
|
||||
});
|
||||
});
|
||||
cy.get(newProposalSubmitButton).should('be.visible').click();
|
||||
cy.contains('Transaction failed', proposalTimeout).should('be.visible');
|
||||
cy.get(feedbackError).should('have.text', errorMsg);
|
||||
validateFeedBackMsg(errorMsg);
|
||||
});
|
||||
|
||||
// Will fail if run after 'Able to submit update market proposal and vote for proposal'
|
||||
// 3002-PROP-022
|
||||
it('Unable to submit update market proposal without equity-like share in the market', function () {
|
||||
switchVegaWalletPubKey();
|
||||
stakingPageAssociateTokens('1');
|
||||
goToMakeNewProposal(governanceProposalType.UPDATE_MARKET);
|
||||
cy.get(newProposalTitle).type('Test update market proposal - rejected');
|
||||
cy.get(newProposalDescription).type('E2E test for proposals');
|
||||
@@ -248,12 +283,24 @@ context.skip(
|
||||
delay: 2,
|
||||
});
|
||||
});
|
||||
cy.get(proposalDownloadBtn)
|
||||
.should('be.visible')
|
||||
.click()
|
||||
.then(() => {
|
||||
cy.wrap(
|
||||
getDownloadedProposalJsonPath('vega-update-market-proposal-')
|
||||
).then((filePath) => {
|
||||
goToMakeNewProposal(governanceProposalType.RAW);
|
||||
submitUniqueRawProposal({ proposalBody: filePath, submit: false });
|
||||
});
|
||||
});
|
||||
cy.get(newProposalSubmitButton).should('be.visible').click();
|
||||
cy.contains('Proposal rejected', proposalTimeout).should('be.visible');
|
||||
cy.getByTestId('dialog-content')
|
||||
.find('p')
|
||||
.should('have.text', 'PROPOSAL_ERROR_INSUFFICIENT_EQUITY_LIKE_SHARE');
|
||||
ensureSpecifiedUnstakedTokensAreAssociated('1');
|
||||
validateDialogContentMsg('PROPOSAL_ERROR_INSUFFICIENT_EQUITY_LIKE_SHARE');
|
||||
closeDialog();
|
||||
ethereumWalletConnect();
|
||||
stakingPageDisassociateAllTokens();
|
||||
switchVegaWalletPubKey();
|
||||
});
|
||||
|
||||
// 3002-PROP-020
|
||||
@@ -275,15 +322,25 @@ context.skip(
|
||||
delay: 2,
|
||||
});
|
||||
});
|
||||
cy.get(proposalDownloadBtn)
|
||||
.should('be.visible')
|
||||
.click()
|
||||
.then(() => {
|
||||
cy.wrap(
|
||||
getDownloadedProposalJsonPath('vega-update-market-proposal-')
|
||||
).then((filePath) => {
|
||||
goToMakeNewProposal(governanceProposalType.RAW);
|
||||
submitUniqueRawProposal({ proposalBody: filePath, submit: false });
|
||||
});
|
||||
});
|
||||
cy.get(newProposalSubmitButton).should('be.visible').click();
|
||||
cy.contains('Transaction failed', proposalTimeout).should('be.visible');
|
||||
cy.get(feedbackError).should(
|
||||
'have.text',
|
||||
validateFeedBackMsg(
|
||||
'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
|
||||
// 3001-VOTE-092 3004-PMAC-001 3004-PMAC-003
|
||||
it('Able to submit update market proposal and vote for proposal', function () {
|
||||
vegaWalletFaucetAssetsWithoutCheck(
|
||||
fUSDCId,
|
||||
@@ -313,11 +370,20 @@ context.skip(
|
||||
delay: 2,
|
||||
});
|
||||
});
|
||||
cy.get(newProposalSubmitButton).should('be.visible').click();
|
||||
waitForProposalSubmitted();
|
||||
cy.get(proposalDownloadBtn)
|
||||
.should('be.visible')
|
||||
.click()
|
||||
.then(() => {
|
||||
cy.wrap(
|
||||
getDownloadedProposalJsonPath('vega-update-market-proposal-')
|
||||
).then((filePath) => {
|
||||
goToMakeNewProposal(governanceProposalType.RAW);
|
||||
submitUniqueRawProposal({ proposalBody: filePath });
|
||||
});
|
||||
});
|
||||
navigateTo(navigation.proposals);
|
||||
cy.get('@EnactedMarketId').then((marketId) => {
|
||||
cy.contains(String(marketId))
|
||||
cy.contains(String(marketId).slice(0, 6))
|
||||
.parentsUntil(proposalListItem)
|
||||
.last()
|
||||
.within(() => {
|
||||
@@ -341,12 +407,13 @@ context.skip(
|
||||
'contain.text',
|
||||
'Currently expected to pass'
|
||||
);
|
||||
cy.getByTestId('vote-breakdown-toggle').click();
|
||||
getProposalInformationFromTable('Expected to pass')
|
||||
.contains('👍 by token vote')
|
||||
.should('be.visible');
|
||||
});
|
||||
|
||||
// 3001-VOTE-026 3001-VOTE-027 3001-VOTE-028 3001-VOTE-095 3001-VOTE-096 3005-PASN-001
|
||||
// 3001-VOTE-026 3001-VOTE-027 3001-VOTE-028 3001-VOTE-095 3001-VOTE-096 3005-PASN-001
|
||||
it('Able to submit new asset proposal using min deadlines', function () {
|
||||
const proposalTitle = 'Test new asset proposal';
|
||||
goToMakeNewProposal(governanceProposalType.NEW_ASSET);
|
||||
@@ -362,25 +429,23 @@ context.skip(
|
||||
cy.get(minVoteDeadline).click();
|
||||
cy.get(minValidationDeadline).click();
|
||||
cy.get(minEnactDeadline).click();
|
||||
cy.get(proposalDownloadBtn)
|
||||
.should('be.visible')
|
||||
.click()
|
||||
.then(() => {
|
||||
cy.wrap(
|
||||
getDownloadedProposalJsonPath('vega-new-asset-proposal-')
|
||||
).then((filePath) => {
|
||||
goToMakeNewProposal(governanceProposalType.RAW);
|
||||
submitUniqueRawProposal({ proposalBody: filePath, submit: false }); // 3005-PASN-003
|
||||
});
|
||||
});
|
||||
cy.get(newProposalSubmitButton).should('be.visible').click();
|
||||
cy.contains('Awaiting network confirmation', epochTimeout).should(
|
||||
'be.visible'
|
||||
);
|
||||
cy.contains('Proposal waiting for node vote', proposalTimeout).should(
|
||||
'be.visible'
|
||||
);
|
||||
closeDialog();
|
||||
cy.get(newProposalSubmitButton).should('be.visible').click();
|
||||
// cannot submit a proposal with ERC20 address already in use
|
||||
cy.contains('Proposal rejected', proposalTimeout).should('be.visible');
|
||||
cy.getByTestId('dialog-content')
|
||||
.last()
|
||||
.within(() => {
|
||||
cy.get('p').should(
|
||||
'have.text',
|
||||
'PROPOSAL_ERROR_ERC20_ADDRESS_ALREADY_IN_USE'
|
||||
);
|
||||
});
|
||||
validateDialogContentMsg('PROPOSAL_ERROR_ERC20_ADDRESS_ALREADY_IN_USE');
|
||||
closeDialog();
|
||||
navigateTo(navigation.proposals);
|
||||
cy.contains(proposalTitle)
|
||||
@@ -389,6 +454,7 @@ context.skip(
|
||||
.within(() => {
|
||||
cy.getByTestId(viewProposalBtn).click();
|
||||
});
|
||||
cy.getByTestId('proposal-terms-toggle').click();
|
||||
cy.getByTestId(proposalTermsSection).within(() => {
|
||||
cy.contains('USDT Coin').should('be.visible');
|
||||
cy.contains('USDT').should('be.visible');
|
||||
@@ -397,15 +463,8 @@ context.skip(
|
||||
|
||||
it('Unable to submit new asset proposal with missing/invalid fields', function () {
|
||||
goToMakeNewProposal(governanceProposalType.NEW_ASSET);
|
||||
cy.get(newProposalSubmitButton).should('be.visible').click();
|
||||
cy.get(proposalDownloadBtn).should('be.visible').click();
|
||||
cy.get(inputError).should('have.length', 3);
|
||||
cy.get(newProposalTitle).type('Invalid new asset proposal');
|
||||
cy.get(newProposalDescription).type('Invalid E2E test for proposals');
|
||||
cy.get(proposalValidationDeadline).clear().type('2');
|
||||
cy.get(newProposalSubmitButton).click();
|
||||
cy.contains('Awaiting network confirmation', epochTimeout).should(
|
||||
'not.exist'
|
||||
);
|
||||
});
|
||||
|
||||
it('Able to submit update asset proposal using min deadline', function () {
|
||||
@@ -416,8 +475,17 @@ context.skip(
|
||||
enterUpdateAssetProposalDetails();
|
||||
cy.get(minVoteDeadline).click();
|
||||
cy.get(minEnactDeadline).click();
|
||||
cy.get(newProposalSubmitButton).should('be.visible').click();
|
||||
waitForProposalSubmitted();
|
||||
cy.get(proposalDownloadBtn)
|
||||
.should('be.visible')
|
||||
.click()
|
||||
.then(() => {
|
||||
cy.wrap(
|
||||
getDownloadedProposalJsonPath('vega-update-asset-proposal-')
|
||||
).then((filePath) => {
|
||||
goToMakeNewProposal(governanceProposalType.RAW);
|
||||
submitUniqueRawProposal({ proposalBody: filePath });
|
||||
});
|
||||
});
|
||||
navigateTo(navigation.proposals);
|
||||
cy.get(openProposals).within(() => {
|
||||
cy.get(proposalType)
|
||||
@@ -425,7 +493,7 @@ context.skip(
|
||||
.parentsUntil(proposalListItem)
|
||||
.last()
|
||||
.within(() => {
|
||||
cy.get(proposalDetails).should('contain.text', assetId); // 3001-VOTE-029
|
||||
cy.get(proposalDetails).should('contain.text', assetId.slice(0, 6)); // 3001-VOTE-029
|
||||
cy.getByTestId(viewProposalBtn).click();
|
||||
});
|
||||
});
|
||||
@@ -433,37 +501,97 @@ context.skip(
|
||||
.invoke('text')
|
||||
.should('not.be.empty');
|
||||
// 3001-VOTE-030 3001-VOTE-031
|
||||
cy.getByTestId(proposalTermsSection).within(() => {
|
||||
cy.contains('UpdateAsset').should('be.visible');
|
||||
cy.contains('UpdateERC20').should('be.visible');
|
||||
cy.contains('"lifetimeLimit": "10"').should('be.visible');
|
||||
cy.getByTestId('proposal-terms-toggle').click();
|
||||
cy.getByTestId('proposal-terms').within(() => {
|
||||
getProposalInformationFromTable('assetId').should('have.text', assetId);
|
||||
getProposalInformationFromTable('lifetimeLimit').should(
|
||||
'have.text',
|
||||
'10'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
// 3006-PASC-001 3006-PASC-003
|
||||
it('Able to submit update asset proposal using max deadline', function () {
|
||||
goToMakeNewProposal(governanceProposalType.UPDATE_ASSET);
|
||||
enterUpdateAssetProposalDetails();
|
||||
cy.get(maxVoteDeadline).click();
|
||||
cy.get(maxEnactDeadline).click();
|
||||
cy.get(newProposalSubmitButton).should('be.visible').click();
|
||||
waitForProposalSubmitted();
|
||||
cy.get(proposalDownloadBtn)
|
||||
.should('be.visible')
|
||||
.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 () {
|
||||
goToMakeNewProposal(governanceProposalType.UPDATE_ASSET);
|
||||
cy.get(newProposalSubmitButton).should('be.visible').click();
|
||||
cy.get(proposalDownloadBtn).should('be.visible').click();
|
||||
cy.get(inputError).should('have.length', 3);
|
||||
});
|
||||
|
||||
function getFormattedTime() {
|
||||
const now = new Date();
|
||||
const day = now.getDate().toString().padStart(2, '0');
|
||||
const month = now.toLocaleString('en-US', { month: 'short' });
|
||||
const year = now.getFullYear().toString();
|
||||
const hours = now.getHours().toString().padStart(2, '0');
|
||||
const minutes = now.getMinutes().toString().padStart(2, '0');
|
||||
it('Able to download and submit freeform proposal', function () {
|
||||
goToMakeNewProposal(governanceProposalType.FREEFORM);
|
||||
// 3008-PFRO-006
|
||||
cy.get(newProposalTitle)
|
||||
.siblings()
|
||||
.should('contain.text', '(100 characters or less)'); // 3008-PFRO-007
|
||||
// 3008-PFRO-005
|
||||
cy.get(newProposalTitle).type('Test freeform proposal form');
|
||||
// 3008-PFRO-009
|
||||
cy.get(newProposalDescription)
|
||||
.siblings()
|
||||
.should('contain.text', '(20,000 characters or less)'); // 3008-PFRO-010
|
||||
// 3008-PFRO-008 3002-PROP-012 3002-PROP-016
|
||||
cy.get(newProposalDescription).type(
|
||||
'E2E test for downloading freeform proposal'
|
||||
);
|
||||
// 3008-PFRO-012
|
||||
cy.get(minVoteDeadline).should('exist'); // 3002-PROP-008
|
||||
cy.get(maxVoteDeadline).should('exist');
|
||||
// 3008-PFRO-011
|
||||
cy.get(proposalVoteDeadline).clear().type('2');
|
||||
// 3008-PFRO-013 3008-PFRO-014
|
||||
cy.getByTestId('voting-date').invoke('text').should('not.be.empty');
|
||||
// 3008-PFRO-015
|
||||
cy.log('Download updated proposal file');
|
||||
cy.get(proposalDownloadBtn)
|
||||
.should('be.visible')
|
||||
.click()
|
||||
.then(() => {
|
||||
cy.wrap(
|
||||
getDownloadedProposalJsonPath('vega-freeform-proposal-')
|
||||
).then((filePath) => {
|
||||
// 3008-PFRO-019
|
||||
goToMakeNewProposal(governanceProposalType.RAW);
|
||||
submitUniqueRawProposal({ proposalBody: filePath });
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
return `${day}-${month}-${year}-${hours}-${minutes}`;
|
||||
after('Disassociate from second wallet key if present', function () {
|
||||
cy.reload();
|
||||
waitForSpinner();
|
||||
ethereumWalletConnect();
|
||||
dissociateFromSecondWalletKey();
|
||||
});
|
||||
|
||||
function validateDialogContentMsg(expectedMsg: string) {
|
||||
cy.getByTestId('dialog-content')
|
||||
.last()
|
||||
.within(() => {
|
||||
cy.get('p').should('have.text', expectedMsg);
|
||||
});
|
||||
}
|
||||
|
||||
function validateFeedBackMsg(expectedMsg: string) {
|
||||
cy.get(feedbackError).should('have.text', expectedMsg);
|
||||
}
|
||||
|
||||
function enterUpdateAssetProposalDetails() {
|
||||
|
||||
@@ -21,7 +21,7 @@ import {
|
||||
} from '../../support/governance.functions';
|
||||
import { ensureSpecifiedUnstakedTokensAreAssociated } from '../../support/staking.functions';
|
||||
import { ethereumWalletConnect } from '../../support/wallet-eth.functions';
|
||||
import { vegaWalletSetSpecifiedApprovalAmount } from '../../support/wallet-teardown.functions';
|
||||
import { vegaWalletSetSpecifiedApprovalAmount } from '../../support/wallet-functions';
|
||||
|
||||
const proposalListItem = 'proposals-list-item';
|
||||
const openProposals = '[data-testid="open-proposals"]';
|
||||
@@ -86,11 +86,12 @@ describe('Governance flow for proposal list', { tags: '@slow' }, function () {
|
||||
});
|
||||
|
||||
it('Newly created proposals list - shows title and portion of summary', function () {
|
||||
const proposalPath = '/proposals/new-market-raw.json';
|
||||
const enactmentTimestamp = createTenDigitUnixTimeStampForSpecifiedDays(3);
|
||||
const proposalPath = 'src/fixtures/proposals/new-market-raw.json';
|
||||
const proposalTimestamp = createTenDigitUnixTimeStampForSpecifiedDays(3);
|
||||
submitUniqueRawProposal({
|
||||
proposalBody: proposalPath,
|
||||
enactmentTimestamp: enactmentTimestamp,
|
||||
enactmentTimestamp: proposalTimestamp,
|
||||
closingTimestamp: proposalTimestamp,
|
||||
}); // 3001-VOTE-052
|
||||
// 3001-VOTE-008
|
||||
// 3001-VOTE-034
|
||||
|
||||
@@ -7,7 +7,6 @@ import {
|
||||
import {
|
||||
clickOnValidatorFromList,
|
||||
closeStakingDialog,
|
||||
stakingPageAssociateTokens,
|
||||
stakingValidatorPageAddStake,
|
||||
waitForBeginningOfEpoch,
|
||||
} from '../../support/staking.functions';
|
||||
@@ -15,7 +14,7 @@ import { ethereumWalletConnect } from '../../support/wallet-eth.functions';
|
||||
import {
|
||||
depositAsset,
|
||||
vegaWalletTeardown,
|
||||
} from '../../support/wallet-teardown.functions';
|
||||
} from '../../support/wallet-functions';
|
||||
|
||||
const vegaAssetAddress = '0x67175Da1D5e966e40D11c4B2519392B2058373de';
|
||||
const vegaWalletUnstakedBalance =
|
||||
@@ -30,20 +29,22 @@ context('rewards - flow', { tags: '@slow' }, function () {
|
||||
turnTelemetryOff();
|
||||
cy.visit('/');
|
||||
waitForSpinner();
|
||||
depositAsset(vegaAssetAddress, '1000', 18);
|
||||
cy.validatorsSelfDelegate();
|
||||
ethereumWalletConnect();
|
||||
cy.connectVegaWallet();
|
||||
cy.VegaWalletTopUpRewardsPool(30, 200);
|
||||
navigateTo(navigation.validators);
|
||||
depositAsset(vegaAssetAddress, '1000', 18);
|
||||
cy.getByTestId('currency-title', txTimeout).should(
|
||||
'contain.text',
|
||||
'Collateral'
|
||||
);
|
||||
vegaWalletTeardown();
|
||||
stakingPageAssociateTokens('6000');
|
||||
cy.associateTokensToVegaWallet('6000');
|
||||
navigateTo(navigation.validators);
|
||||
cy.VegaWalletTopUpRewardsPool();
|
||||
cy.get(vegaWalletUnstakedBalance, txTimeout).should(
|
||||
'contain',
|
||||
'6,000.0',
|
||||
txTimeout
|
||||
);
|
||||
cy.get('button').contains('Select a validator to nominate').click();
|
||||
clickOnValidatorFromList(0);
|
||||
stakingValidatorPageAddStake('3000');
|
||||
closeStakingDialog();
|
||||
|
||||
@@ -25,7 +25,7 @@ import { ethereumWalletConnect } from '../../support/wallet-eth.functions';
|
||||
import {
|
||||
vegaWalletSetSpecifiedApprovalAmount,
|
||||
vegaWalletTeardown,
|
||||
} from '../../support/wallet-teardown.functions';
|
||||
} from '../../support/wallet-functions';
|
||||
const stakeValidatorListTotalStake = 'total-stake';
|
||||
const stakeValidatorListTotalShare = 'total-stake-share';
|
||||
const stakeValidatorListStakePercentage = 'stake-percentage';
|
||||
@@ -54,7 +54,7 @@ context(
|
||||
'Staking Tab - with eth and vega wallets connected',
|
||||
{ tags: '@slow' },
|
||||
function () {
|
||||
// 2001-STKE-002, 2001-STKE-032
|
||||
// 1002-STKE-002, 1002-STKE-032
|
||||
before('visit staking tab and connect vega wallet', function () {
|
||||
cy.visit('/');
|
||||
ethereumWalletConnect();
|
||||
@@ -77,25 +77,41 @@ 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 () {
|
||||
ensureSpecifiedUnstakedTokensAreAssociated('3');
|
||||
verifyUnstakedBalance(3.0);
|
||||
verifyEthWalletTotalAssociatedBalance('3.0');
|
||||
verifyEthWalletAssociatedBalance('3.0');
|
||||
cy.get('button').contains('Select a validator to nominate').click();
|
||||
// 2001-STKE-031
|
||||
// 1002-STKE-031
|
||||
clickOnValidatorFromList(0);
|
||||
// 2001-STKE-033, 2001-STKE-034, 2001-STKE-037
|
||||
// 1002-STKE-033, 1002-STKE-034, 1002-STKE-037
|
||||
stakingValidatorPageAddStake('2');
|
||||
verifyUnstakedBalance(1.0);
|
||||
// 2001-STKE-039
|
||||
// 1002-STKE-039
|
||||
verifyStakedBalance(2.0);
|
||||
verifyNextEpochValue(2.0); // 2001-STKE-016 2001-STKE-038
|
||||
verifyThisEpochValue(2.0); // 2001-STKE-013
|
||||
verifyNextEpochValue(2.0); // 1002-STKE-016 1002-STKE-038
|
||||
verifyThisEpochValue(2.0); // 1002-STKE-013
|
||||
closeStakingDialog();
|
||||
navigateTo(navigation.validators);
|
||||
|
||||
// 2002-SINC-007
|
||||
// 2002-SINC-007 1002-STKE-015 1002-STKE-017 1002-STKE-052
|
||||
validateValidatorListTotalStakeAndShare('0', '3,002.00', '50.02%');
|
||||
});
|
||||
|
||||
@@ -109,6 +125,7 @@ context(
|
||||
cy.getByTestId(userStake, epochTimeout)
|
||||
.first()
|
||||
.should('have.text', '2.00');
|
||||
waitForBeginningOfEpoch();
|
||||
cy.getByTestId('total-stake').first().realHover();
|
||||
cy.getByTestId('staked-by-user-tooltip')
|
||||
.first()
|
||||
@@ -217,7 +234,7 @@ context(
|
||||
});
|
||||
});
|
||||
|
||||
// 2001-STKE-041
|
||||
// 1002-STKE-041 1002-STKE-053
|
||||
it(
|
||||
'Able to remove part of a stake against a validator',
|
||||
{ tags: '@smoke' },
|
||||
@@ -230,11 +247,11 @@ context(
|
||||
verifyUnstakedBalance(1.0);
|
||||
closeStakingDialog();
|
||||
navigateTo(navigation.validators);
|
||||
// 2001-STKE-040
|
||||
// 1002-STKE-040
|
||||
clickOnValidatorFromList(0);
|
||||
// 2001-STKE-044, 2001-STKE-048
|
||||
// 1002-STKE-044, 1002-STKE-048
|
||||
stakingValidatorPageRemoveStake('1');
|
||||
// 2001-STKE-049
|
||||
// 1002-STKE-049
|
||||
verifyNextEpochValue(2.0);
|
||||
verifyUnstakedBalance(2.0);
|
||||
verifyStakedBalance(2.0);
|
||||
@@ -254,7 +271,7 @@ context(
|
||||
}
|
||||
);
|
||||
|
||||
// 2001-STKE-045
|
||||
// 1002-STKE-045
|
||||
it('Able to remove a full stake against a validator', function () {
|
||||
stakingPageAssociateTokens('3');
|
||||
verifyUnstakedBalance(3.0);
|
||||
@@ -302,6 +319,7 @@ context(
|
||||
.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 () {
|
||||
stakingPageAssociateTokens('3');
|
||||
verifyUnstakedBalance(3.0);
|
||||
@@ -379,6 +397,9 @@ context(
|
||||
});
|
||||
|
||||
it('Disassociating some tokens - prioritizes unstaked tokens', function () {
|
||||
vegaWalletSetSpecifiedApprovalAmount('1000');
|
||||
cy.reload();
|
||||
ethereumWalletConnect();
|
||||
stakingPageAssociateTokens('3');
|
||||
verifyUnstakedBalance(3.0);
|
||||
cy.get('button').contains('Select a validator to nominate').click();
|
||||
@@ -401,7 +422,7 @@ context(
|
||||
});
|
||||
|
||||
it('Associating wallet tokens - when some already staked - auto stakes tokens to staked validator', function () {
|
||||
// 2001-STKE-004
|
||||
// 1002-STKE-004
|
||||
stakingPageAssociateTokens('3');
|
||||
verifyUnstakedBalance(3.0);
|
||||
cy.get('button').contains('Select a validator to nominate').click();
|
||||
@@ -415,7 +436,7 @@ context(
|
||||
});
|
||||
|
||||
it('Associating vesting contract tokens - when some already staked - auto stakes tokens to staked validator', function () {
|
||||
// 2001-STKE-004
|
||||
// 1002-STKE-004
|
||||
stakingPageAssociateTokens('3', { type: 'contract' });
|
||||
verifyUnstakedBalance(3.0);
|
||||
cy.get('button').contains('Select a validator to nominate').click();
|
||||
@@ -429,7 +450,7 @@ context(
|
||||
});
|
||||
|
||||
it('Associating vesting contract tokens - when wallet tokens already staked - auto stakes tokens to staked validator', function () {
|
||||
// 2001-STKE-004
|
||||
// 1002-STKE-004
|
||||
stakingPageAssociateTokens('3', { type: 'wallet' });
|
||||
verifyUnstakedBalance(3.0);
|
||||
cy.get('button').contains('Select a validator to nominate').click();
|
||||
@@ -443,7 +464,7 @@ context(
|
||||
});
|
||||
|
||||
it('Associating tokens - with multiple validators already staked - auto stakes to staked validators - abiding by existing stake ratio', function () {
|
||||
// 2001-STKE-004
|
||||
// 1002-STKE-004
|
||||
stakingPageAssociateTokens('6');
|
||||
verifyUnstakedBalance(6.0);
|
||||
cy.get('button').contains('Select a validator to nominate').click();
|
||||
@@ -485,6 +506,7 @@ context(
|
||||
});
|
||||
|
||||
afterEach('Teardown Wallet', function () {
|
||||
navigateTo(navigation.home);
|
||||
vegaWalletTeardown();
|
||||
});
|
||||
|
||||
@@ -495,7 +517,7 @@ context(
|
||||
}
|
||||
|
||||
function verifyThisEpochValue(amount: number) {
|
||||
cy.getByTestId('stake-this-epoch', epochTimeout) // 2001-STKE-013
|
||||
cy.getByTestId('stake-this-epoch', epochTimeout) // 1002-STKE-013
|
||||
.contains(amount, epochTimeout)
|
||||
.should('be.visible');
|
||||
}
|
||||
|
||||
@@ -14,11 +14,12 @@ import {
|
||||
} from '../../support/staking.functions';
|
||||
import { ethereumWalletConnect } from '../../support/wallet-eth.functions';
|
||||
import {
|
||||
switchVegaWalletPubKey,
|
||||
vegaWalletAssociate,
|
||||
vegaWalletDisassociate,
|
||||
vegaWalletSetSpecifiedApprovalAmount,
|
||||
vegaWalletTeardown,
|
||||
} from '../../support/wallet-teardown.functions';
|
||||
} from '../../support/wallet-functions';
|
||||
|
||||
const ethWalletContainer = '[data-testid="ethereum-wallet"]';
|
||||
const vegaWalletAssociatedBalance = '[data-testid="currency-value"]';
|
||||
@@ -38,7 +39,7 @@ const associatedKey = '[data-testid="associated-key"]';
|
||||
const associatedAmount = '[data-testid="associated-amount"]';
|
||||
const associateCompleteText = '[data-testid="transaction-complete-body"]';
|
||||
const disassociationWarning = '[data-testid="disassociation-warning"]';
|
||||
const vegaWallet = '[data-testid="vega-wallet"]';
|
||||
const vegaWallet = 'aside [data-testid="vega-wallet"]';
|
||||
|
||||
context(
|
||||
'Token association flow - with eth and vega wallets connected',
|
||||
@@ -64,46 +65,38 @@ context(
|
||||
}
|
||||
);
|
||||
|
||||
it('Able to associate tokens - from wallet', function () {
|
||||
//1004-ASSO-003
|
||||
//1004-ASSO-005
|
||||
//1004-ASSO-009
|
||||
//1004-ASSO-030
|
||||
//1004-ASSO-012
|
||||
//1004-ASSO-013
|
||||
//1004-ASSO-014
|
||||
//1004-ASSO-015
|
||||
//1004-ASSO-030
|
||||
//0005-ETXN-006
|
||||
//0005-ETXN-003
|
||||
//0005-ETXN-005
|
||||
stakingPageAssociateTokens('2', { skipConfirmation: true });
|
||||
|
||||
cy.getByTestId('currency-title', txTimeout).should(
|
||||
'have.length.above',
|
||||
6
|
||||
);
|
||||
validateWalletCurrency('Associated', '0.00');
|
||||
validateWalletCurrency('Pending association', '2.00');
|
||||
validateWalletCurrency('Total associated after pending', '2.00');
|
||||
cy.getByTestId('currency-title', txTimeout).should('have.length', 6);
|
||||
|
||||
// 0005-ETXN-002
|
||||
verifyEthWalletAssociatedBalance('2.0');
|
||||
|
||||
verifyEthWalletTotalAssociatedBalance('2.0');
|
||||
|
||||
cy.get(vegaWallet)
|
||||
.first()
|
||||
.within(() => {
|
||||
it(
|
||||
'Able to associate tokens - from wallet',
|
||||
{ tags: '@smoke' },
|
||||
function () {
|
||||
//1004-ASSO-003
|
||||
//1004-ASSO-005
|
||||
//1004-ASSO-009
|
||||
//1004-ASSO-030
|
||||
//1004-ASSO-012
|
||||
//1004-ASSO-013
|
||||
//1004-ASSO-014
|
||||
//1004-ASSO-015
|
||||
//1004-ASSO-030
|
||||
//0005-ETXN-006
|
||||
//0005-ETXN-003
|
||||
//0005-ETXN-005
|
||||
stakingPageAssociateTokens('2', { skipConfirmation: true });
|
||||
validateWalletCurrency('Associated', '0.00');
|
||||
validateWalletCurrency('Pending association', '2.00');
|
||||
validateWalletCurrency('Total associated after pending', '2.00');
|
||||
// 0005-ETXN-002
|
||||
verifyEthWalletAssociatedBalance('2.0');
|
||||
verifyEthWalletTotalAssociatedBalance('2.0');
|
||||
cy.get(vegaWallet).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);
|
||||
}
|
||||
);
|
||||
|
||||
it('Able to disassociate all associated tokens - manually', function () {
|
||||
// 1004-ASSO-025
|
||||
@@ -116,15 +109,11 @@ context(
|
||||
verifyEthWalletAssociatedBalance('2.0');
|
||||
verifyEthWalletTotalAssociatedBalance('6,002.00');
|
||||
cy.get('button').contains('Select a validator to nominate').click();
|
||||
cy.getByTestId('epoch-countdown').should('be.visible');
|
||||
stakingPageDisassociateTokens('2');
|
||||
cy.getByTestId('currency-title', txTimeout).should(
|
||||
'have.length.above',
|
||||
6
|
||||
);
|
||||
validateWalletCurrency('Associated', '2.00');
|
||||
validateWalletCurrency('Pending association', '2.00');
|
||||
validateWalletCurrency('Total associated after pending', '0.00');
|
||||
cy.getByTestId('currency-title', txTimeout).should('have.length', 6);
|
||||
cy.get(
|
||||
'[data-testid="eth-wallet-associated-balances"]:visible',
|
||||
txTimeout
|
||||
@@ -137,38 +126,26 @@ context(
|
||||
stakingPageAssociateTokens('1001', { approve: true });
|
||||
verifyEthWalletAssociatedBalance('1,001.00');
|
||||
verifyEthWalletTotalAssociatedBalance('7,001.00');
|
||||
cy.get(vegaWallet)
|
||||
.last()
|
||||
.within(() => {
|
||||
cy.get(vegaWalletAssociatedBalance, txTimeout).should(
|
||||
'contain',
|
||||
'1,001.00'
|
||||
);
|
||||
});
|
||||
cy.get(vegaWallet).within(() => {
|
||||
cy.get(vegaWalletAssociatedBalance, txTimeout).should(
|
||||
'contain',
|
||||
'1,001.00'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it('Able to disassociate a partial amount of tokens currently associated', function () {
|
||||
stakingPageAssociateTokens('2');
|
||||
cy.get(vegaWallet)
|
||||
.first()
|
||||
.within(() => {
|
||||
cy.get(vegaWalletAssociatedBalance, txTimeout).should(
|
||||
'contain',
|
||||
2.0
|
||||
);
|
||||
});
|
||||
|
||||
cy.get(vegaWallet).within(() => {
|
||||
cy.get(vegaWalletAssociatedBalance, txTimeout).should('contain', 2.0);
|
||||
});
|
||||
cy.get('button').contains('Select a validator to nominate').click();
|
||||
cy.getByTestId('epoch-countdown').should('be.visible');
|
||||
stakingPageDisassociateTokens('1');
|
||||
verifyEthWalletAssociatedBalance('1.0');
|
||||
cy.get(vegaWallet)
|
||||
.first()
|
||||
.within(() => {
|
||||
cy.get(vegaWalletAssociatedBalance, txTimeout).should(
|
||||
'contain',
|
||||
1.0
|
||||
);
|
||||
});
|
||||
cy.get(vegaWallet).within(() => {
|
||||
cy.get(vegaWalletAssociatedBalance, txTimeout).should('contain', 1.0);
|
||||
});
|
||||
});
|
||||
|
||||
it('Able to disassociate all tokens - using max', function () {
|
||||
@@ -176,15 +153,11 @@ context(
|
||||
const warningText =
|
||||
'Warning: Any tokens that have been nominated to a node will sacrifice rewards they are due for the current epoch. If you do not wish to sacrifice these, you should remove stake from a node at the end of an epoch before disassociation.';
|
||||
stakingPageAssociateTokens('2');
|
||||
cy.get(vegaWallet)
|
||||
.first()
|
||||
.within(() => {
|
||||
cy.get(vegaWalletAssociatedBalance, txTimeout).should(
|
||||
'contain',
|
||||
2.0
|
||||
);
|
||||
});
|
||||
cy.get(vegaWallet).within(() => {
|
||||
cy.get(vegaWalletAssociatedBalance, txTimeout).should('contain', 2.0);
|
||||
});
|
||||
cy.get('button').contains('Select a validator to nominate').click();
|
||||
cy.getByTestId('epoch-countdown').should('be.visible');
|
||||
cy.get(ethWalletDissociateButton).click();
|
||||
cy.get(disassociationWarning).should('contain', warningText);
|
||||
stakingPageDisassociateAllTokens();
|
||||
@@ -202,14 +175,9 @@ context(
|
||||
'not.exist'
|
||||
);
|
||||
});
|
||||
cy.get(vegaWallet)
|
||||
.first()
|
||||
.within(() => {
|
||||
cy.get(vegaWalletAssociatedBalance, txTimeout).should(
|
||||
'contain',
|
||||
0.0
|
||||
);
|
||||
});
|
||||
cy.get(vegaWallet).within(() => {
|
||||
cy.get(vegaWalletAssociatedBalance, txTimeout).should('contain', 0.0);
|
||||
});
|
||||
});
|
||||
|
||||
it('Able to associate and disassociate vesting contract tokens', function () {
|
||||
@@ -224,38 +192,22 @@ context(
|
||||
type: 'contract',
|
||||
skipConfirmation: true,
|
||||
});
|
||||
|
||||
cy.getByTestId('currency-title', txTimeout).should(
|
||||
'have.length.above',
|
||||
6
|
||||
);
|
||||
validateWalletCurrency('Associated', '0.00');
|
||||
validateWalletCurrency('Pending association', '2.00');
|
||||
validateWalletCurrency('Total associated after pending', '2.00');
|
||||
cy.getByTestId('currency-title', txTimeout).should('have.length', 6);
|
||||
verifyEthWalletAssociatedBalance('2.0');
|
||||
verifyEthWalletTotalAssociatedBalance('2.0');
|
||||
cy.get(vegaWallet)
|
||||
.first()
|
||||
.within(() => {
|
||||
cy.get(vegaWalletAssociatedBalance, txTimeout).should(
|
||||
'contain',
|
||||
2.0
|
||||
);
|
||||
});
|
||||
cy.get(vegaWallet).within(() => {
|
||||
cy.get(vegaWalletAssociatedBalance, txTimeout).should('contain', 2.0);
|
||||
});
|
||||
cy.get(vegaWalletUnstakedBalance, txTimeout).should('contain', 2.0);
|
||||
stakingPageDisassociateTokens('1', {
|
||||
type: 'contract',
|
||||
skipConfirmation: true,
|
||||
});
|
||||
cy.getByTestId('currency-title', txTimeout).should(
|
||||
'have.length.above',
|
||||
6
|
||||
);
|
||||
validateWalletCurrency('Associated', '2.00');
|
||||
validateWalletCurrency('Pending association', '1.00');
|
||||
validateWalletCurrency('Total associated after pending', '1.00');
|
||||
cy.getByTestId('currency-title', txTimeout).should('have.length', 6);
|
||||
verifyEthWalletAssociatedBalance('1.0');
|
||||
verifyEthWalletTotalAssociatedBalance('1.0');
|
||||
});
|
||||
@@ -267,6 +219,7 @@ context(
|
||||
// 1004-ASSO-022
|
||||
stakingPageAssociateTokens('21', { type: 'wallet' });
|
||||
cy.get('button').contains('Select a validator to nominate').click();
|
||||
cy.getByTestId('epoch-countdown').should('be.visible');
|
||||
stakingPageAssociateTokens('37', { type: 'contract' });
|
||||
cy.get(vestingContractSection)
|
||||
.first()
|
||||
@@ -286,28 +239,18 @@ context(
|
||||
);
|
||||
cy.get(associatedAmount, txTimeout).should('contain', 21);
|
||||
});
|
||||
cy.get(vegaWallet)
|
||||
.first()
|
||||
.within(() => {
|
||||
cy.get(vegaWalletAssociatedBalance, txTimeout).should(
|
||||
'contain',
|
||||
58
|
||||
);
|
||||
});
|
||||
cy.get(vegaWallet).within(() => {
|
||||
cy.get(vegaWalletAssociatedBalance, txTimeout).should('contain', 58);
|
||||
});
|
||||
stakingPageDisassociateTokens('6', { type: 'contract' });
|
||||
cy.get(vestingContractSection)
|
||||
.first()
|
||||
.within(() => {
|
||||
cy.get(associatedAmount, txTimeout).should('contain', 31);
|
||||
});
|
||||
cy.get(vegaWallet)
|
||||
.first()
|
||||
.within(() => {
|
||||
cy.get(vegaWalletAssociatedBalance, txTimeout).should(
|
||||
'contain',
|
||||
52
|
||||
);
|
||||
});
|
||||
cy.get(vegaWallet).within(() => {
|
||||
cy.get(vegaWalletAssociatedBalance, txTimeout).should('contain', 52);
|
||||
});
|
||||
navigateTo(navigation.validators);
|
||||
stakingPageDisassociateTokens('9', { type: 'wallet' });
|
||||
cy.get(vegaInWalletSection)
|
||||
@@ -315,14 +258,9 @@ context(
|
||||
.within(() => {
|
||||
cy.get(associatedAmount, txTimeout).should('contain', 12);
|
||||
});
|
||||
cy.get(vegaWallet)
|
||||
.first()
|
||||
.within(() => {
|
||||
cy.get(vegaWalletAssociatedBalance, txTimeout).should(
|
||||
'contain',
|
||||
43
|
||||
);
|
||||
});
|
||||
cy.get(vegaWallet).within(() => {
|
||||
cy.get(vegaWalletAssociatedBalance, txTimeout).should('contain', 43);
|
||||
});
|
||||
});
|
||||
|
||||
it('Not able to associate more tokens than owned', function () {
|
||||
@@ -339,14 +277,9 @@ context(
|
||||
// 1004-ASSO-004
|
||||
it('Pending association outside of app is shown', function () {
|
||||
vegaWalletAssociate('2');
|
||||
cy.getByTestId('currency-title', txTimeout).should(
|
||||
'have.length.above',
|
||||
6
|
||||
);
|
||||
validateWalletCurrency('Associated', '0.00');
|
||||
validateWalletCurrency('Pending association', '2.00');
|
||||
validateWalletCurrency('Total associated after pending', '2.00');
|
||||
cy.getByTestId('currency-title', txTimeout).should('have.length', 6);
|
||||
validateWalletCurrency('Associated', '2.00');
|
||||
});
|
||||
|
||||
@@ -355,14 +288,9 @@ context(
|
||||
cy.wrap(validateWalletCurrency('Associated', '2.00')).then(() => {
|
||||
vegaWalletDisassociate('2');
|
||||
});
|
||||
cy.getByTestId('currency-title', txTimeout).should(
|
||||
'have.length.above',
|
||||
6
|
||||
);
|
||||
validateWalletCurrency('Associated', '2.00');
|
||||
validateWalletCurrency('Pending association', '2.00');
|
||||
validateWalletCurrency('Total associated after pending', '0.00');
|
||||
cy.getByTestId('currency-title', txTimeout).should('have.length', 6);
|
||||
validateWalletCurrency('Associated', '0.00');
|
||||
});
|
||||
|
||||
@@ -374,21 +302,15 @@ context(
|
||||
Cypress.env('vegaWalletPublicKey')
|
||||
);
|
||||
|
||||
cy.get('[data-testid="manage-vega-wallet"]:visible').click();
|
||||
cy.get('[data-testid="select-keypair-button"]').eq(0).click();
|
||||
switchVegaWalletPubKey();
|
||||
cy.get(connectedVegaKey).should(
|
||||
'have.text',
|
||||
Cypress.env('vegaWalletPublicKey2')
|
||||
);
|
||||
stakingPageAssociateTokens('2');
|
||||
cy.get(vegaWallet)
|
||||
.first()
|
||||
.within(() => {
|
||||
cy.get(vegaWalletAssociatedBalance, txTimeout).should(
|
||||
'contain',
|
||||
2.0
|
||||
);
|
||||
});
|
||||
cy.get(vegaWallet).within(() => {
|
||||
cy.get(vegaWalletAssociatedBalance, txTimeout).should('contain', 2.0);
|
||||
});
|
||||
cy.get(associateCompleteText).should(
|
||||
'have.text',
|
||||
`Vega key ${Cypress.env(
|
||||
|
||||
@@ -5,7 +5,7 @@ import {
|
||||
waitForSpinner,
|
||||
} from '../../support/common.functions';
|
||||
import { ethereumWalletConnect } from '../../support/wallet-eth.functions';
|
||||
import { depositAsset } from '../../support/wallet-teardown.functions';
|
||||
import { depositAsset } from '../../support/wallet-functions';
|
||||
|
||||
const withdraw = 'withdraw';
|
||||
const withdrawalForm = 'withdraw-form';
|
||||
@@ -15,13 +15,7 @@ const balanceAvailable = 'BALANCE_AVAILABLE_value';
|
||||
const withdrawalThreshold = 'WITHDRAWAL_THRESHOLD_value';
|
||||
const delayTime = 'DELAY_TIME_value';
|
||||
const submitWithdrawalButton = 'submit-withdrawal';
|
||||
const dialogTitle = 'dialog-title';
|
||||
const dialogClose = 'dialog-close';
|
||||
const txExplorerLink = 'tx-block-explorer';
|
||||
const withdrawalAssetSymbol = 'withdrawal-asset-symbol';
|
||||
const withdrawalAmount = 'withdrawal-amount';
|
||||
const withdrawalRecipient = 'withdrawal-recipient';
|
||||
const withdrawFundsButton = 'withdraw-funds';
|
||||
const completeWithdrawalButton = 'complete-withdrawal';
|
||||
const tableTxHash = '[col-id="txHash"]';
|
||||
const tableAssetSymbol = '[col-id="asset.symbol"]';
|
||||
@@ -30,14 +24,16 @@ const tableReceiverAddress = '[col-id="details.receiverAddress"]';
|
||||
const tableWithdrawnTimeStamp = '[col-id="withdrawnTimestamp"]';
|
||||
const tableWithdrawnStatus = '[col-id="status"]';
|
||||
const tableCreatedTimeStamp = '[col-id="createdTimestamp"]';
|
||||
const toastContent = 'toast-content';
|
||||
const toast = 'toast';
|
||||
const toastPanel = 'toast-panel';
|
||||
const toastClose = 'toast-close';
|
||||
const withdrawalDialogContent = 'dialog-content';
|
||||
const toastCompleteWithdrawal = 'toast-complete-withdrawal';
|
||||
const usdtName = 'USDC (local)';
|
||||
const usdcEthAddress = '0x1b8a1B6CBE5c93609b46D1829Cc7f3Cb8eeE23a0';
|
||||
const usdcSymbol = 'tUSDC';
|
||||
const usdtSelectValue =
|
||||
'993ed98f4f770d91a796faab1738551193ba45c62341d20597df70fea6704ede';
|
||||
const truncatedWithdrawalEthAddress = '0xEe7D…22d94F';
|
||||
const formValidationError = 'input-error-text';
|
||||
const txTimeout = Cypress.env('txTimeout');
|
||||
|
||||
@@ -59,6 +55,10 @@ context(
|
||||
navigateTo(navigation.withdraw);
|
||||
cy.connectVegaWallet();
|
||||
ethereumWalletConnect();
|
||||
cy.getByTestId('currency-title', txTimeout).should(
|
||||
'contain.text',
|
||||
usdtName
|
||||
);
|
||||
});
|
||||
|
||||
it('Able to open withdrawal form with vega wallet connected', function () {
|
||||
@@ -73,7 +73,10 @@ context(
|
||||
it('Unable to submit withdrawal with invalid fields', function () {
|
||||
cy.getByTestId(withdraw).should('be.visible').click();
|
||||
cy.getByTestId(withdrawalForm, txTimeout).within(() => {
|
||||
cy.get('select').select(usdtSelectValue, { force: true });
|
||||
cy.getByTestId('select-asset').click();
|
||||
cy.get('select')
|
||||
.select(usdtSelectValue, { force: true })
|
||||
.should('have.value', usdtSelectValue);
|
||||
cy.getByTestId(balanceAvailable, txTimeout).should('exist');
|
||||
cy.getByTestId(submitWithdrawalButton).click();
|
||||
cy.getByTestId(formValidationError).should('have.length', 1);
|
||||
@@ -93,62 +96,75 @@ context(
|
||||
});
|
||||
});
|
||||
|
||||
it('Able to withdraw asset: -eth wallet connected -withdraw funds button', function () {
|
||||
// fill in withdrawal form
|
||||
cy.getByTestId(withdraw).should('be.visible').click();
|
||||
cy.getByTestId(withdrawalForm, txTimeout).within(() => {
|
||||
cy.get('select').select(usdtSelectValue, { force: true });
|
||||
cy.getByTestId(balanceAvailable, txTimeout).should('exist');
|
||||
cy.getByTestId(withdrawalThreshold).should(
|
||||
'have.text',
|
||||
'100,000.00000T'
|
||||
);
|
||||
cy.getByTestId(delayTime).should('have.text', 'None');
|
||||
cy.getByTestId(amountInput).click().type('120');
|
||||
cy.getByTestId(submitWithdrawalButton).click();
|
||||
});
|
||||
|
||||
cy.contains('Awaiting network confirmation').should('be.visible');
|
||||
// assert withdrawal request
|
||||
cy.getByTestId(dialogTitle, txTimeout).should(
|
||||
'have.text',
|
||||
'Transaction complete'
|
||||
);
|
||||
cy.getByTestId(txExplorerLink)
|
||||
.should('have.attr', 'href')
|
||||
.and('contain', '/txs/');
|
||||
cy.getByTestId(withdrawalAssetSymbol).should('have.text', usdcSymbol);
|
||||
cy.getByTestId(withdrawalAmount).should('have.text', '120.00');
|
||||
cy.getByTestId(withdrawalRecipient)
|
||||
.should('have.text', truncatedWithdrawalEthAddress)
|
||||
.and('have.attr', 'href')
|
||||
.and('contain', `/address/${Cypress.env('ethWalletPublicKey')}`);
|
||||
cy.getByTestId(withdrawFundsButton).click();
|
||||
// withdrawal complete
|
||||
cy.getByTestId(dialogTitle, txTimeout).should(
|
||||
'have.text',
|
||||
'Withdraw asset complete'
|
||||
);
|
||||
cy.getByTestId(dialogClose).click();
|
||||
// withdrawal history for complete withdrawal displayed
|
||||
cy.get(tableWithdrawnStatus)
|
||||
.eq(1, txTimeout)
|
||||
.should('have.text', 'Completed')
|
||||
.parent()
|
||||
.within(() => {
|
||||
cy.get(tableAssetSymbol).should('have.text', usdcSymbol);
|
||||
cy.get(tableAmount).should('have.text', '120.00');
|
||||
cy.get(tableReceiverAddress)
|
||||
.find('a')
|
||||
.should('have.attr', 'href')
|
||||
.and('contain', 'https://sepolia.etherscan.io/address/');
|
||||
cy.get(tableWithdrawnTimeStamp).should('not.be.empty');
|
||||
cy.get(tableTxHash)
|
||||
.find('a')
|
||||
.should('have.attr', 'href')
|
||||
.and('contain', 'https://sepolia.etherscan.io/tx/');
|
||||
it(
|
||||
'Able to withdraw asset: -eth wallet connected -withdraw funds button',
|
||||
{ tags: '@smoke' },
|
||||
function () {
|
||||
// fill in withdrawal form
|
||||
cy.getByTestId(withdraw).should('be.visible').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(delayTime).should('have.text', 'None');
|
||||
cy.getByTestId(amountInput).click().type('120');
|
||||
cy.getByTestId(submitWithdrawalButton).click();
|
||||
});
|
||||
});
|
||||
// assert withdrawal request
|
||||
cy.getByTestId(toast)
|
||||
.first(txTimeout)
|
||||
.should('contain.text', 'Funds unlocked')
|
||||
.within(() => {
|
||||
cy.getByTestId('external-link').should('exist');
|
||||
cy.getByTestId(toastPanel).should(
|
||||
'contain.text',
|
||||
'Withdraw 120.00 tUSDC'
|
||||
);
|
||||
cy.getByTestId(toastCompleteWithdrawal).click();
|
||||
cy.getByTestId(toastClose).click();
|
||||
});
|
||||
// withdrawal complete
|
||||
cy.getByTestId(toast)
|
||||
.first(txTimeout)
|
||||
.should('contain.text', 'The withdrawal has been approved.')
|
||||
.within(() => {
|
||||
cy.getByTestId(toastPanel).should(
|
||||
'contain.text',
|
||||
'Withdraw 120.00 tUSDC'
|
||||
);
|
||||
});
|
||||
cy.getByTestId(toast)
|
||||
.last(txTimeout)
|
||||
.should('contain.text', 'Transaction confirmed')
|
||||
.within(() => {
|
||||
cy.getByTestId('external-link').should('exist');
|
||||
});
|
||||
// withdrawal history for complete withdrawal displayed
|
||||
cy.get(tableWithdrawnStatus)
|
||||
.eq(1, txTimeout)
|
||||
.should('have.text', 'Completed')
|
||||
.parent()
|
||||
.within(() => {
|
||||
cy.get(tableAssetSymbol).should('have.text', usdcSymbol);
|
||||
cy.get(tableAmount).should('have.text', '120.00');
|
||||
cy.get(tableReceiverAddress)
|
||||
.find('a')
|
||||
.should('have.attr', 'href')
|
||||
.and('contain', 'https://sepolia.etherscan.io/address/');
|
||||
cy.get(tableWithdrawnTimeStamp).should('not.be.empty');
|
||||
cy.get(tableTxHash)
|
||||
.find('a')
|
||||
.should('have.attr', 'href')
|
||||
.and('contain', 'https://sepolia.etherscan.io/tx/');
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
it('Able to withdraw asset: -eth wallet not connected', function () {
|
||||
const ethWalletAddress = Cypress.env('ethWalletPublicKey');
|
||||
@@ -157,7 +173,10 @@ context(
|
||||
// fill in withdrawal form
|
||||
cy.getByTestId(withdraw).should('be.visible').click();
|
||||
cy.getByTestId(withdrawalForm, txTimeout).within(() => {
|
||||
cy.get('select').select(usdtSelectValue, { force: true });
|
||||
cy.getByTestId('select-asset').click();
|
||||
cy.get('select')
|
||||
.select(usdtSelectValue, { force: true })
|
||||
.should('have.value', usdtSelectValue);
|
||||
cy.getByTestId(ethAddressInput).should('be.empty');
|
||||
cy.getByTestId(amountInput).click().type('110');
|
||||
cy.getByTestId(submitWithdrawalButton).click();
|
||||
@@ -168,13 +187,17 @@ context(
|
||||
cy.getByTestId(submitWithdrawalButton).click();
|
||||
});
|
||||
|
||||
cy.contains('Awaiting network confirmation').should('be.visible');
|
||||
// assert withdrawal request
|
||||
cy.getByTestId(dialogTitle, txTimeout).should(
|
||||
'have.text',
|
||||
'Transaction complete'
|
||||
);
|
||||
cy.getByTestId(dialogClose).click();
|
||||
cy.getByTestId(toast)
|
||||
.first(txTimeout)
|
||||
.should('contain.text', 'Funds unlocked')
|
||||
.within(() => {
|
||||
cy.getByTestId('external-link').should('exist');
|
||||
cy.getByTestId(toastPanel).should(
|
||||
'contain.text',
|
||||
'Withdraw 110.00 tUSDC'
|
||||
);
|
||||
cy.getByTestId(toastClose).click();
|
||||
});
|
||||
cy.get(tableTxHash)
|
||||
.eq(1)
|
||||
.should('have.text', 'Complete withdrawal')
|
||||
@@ -189,28 +212,78 @@ context(
|
||||
cy.get(tableCreatedTimeStamp).should('not.be.empty');
|
||||
});
|
||||
ethereumWalletConnect();
|
||||
cy.getByTestId(completeWithdrawalButton).click();
|
||||
cy.getByTestId(toastContent)
|
||||
.last()
|
||||
cy.getByTestId(completeWithdrawalButton).first().click();
|
||||
cy.getByTestId(toast)
|
||||
.last(txTimeout)
|
||||
.should('contain.text', 'Awaiting confirmation')
|
||||
.within(() => {
|
||||
cy.getByTestId('external-link').should('exist');
|
||||
});
|
||||
cy.getByTestId(toastContent)
|
||||
.first()
|
||||
cy.getByTestId(toast)
|
||||
.first(txTimeout)
|
||||
.should('contain.text', 'The withdrawal has been approved.')
|
||||
.within(() => {
|
||||
cy.getByTestId(toastPanel).should('contain.text', '110.00', 'tUSDC');
|
||||
});
|
||||
cy.getByTestId(toastContent)
|
||||
.last()
|
||||
cy.getByTestId(toast)
|
||||
.last(txTimeout)
|
||||
.should('contain.text', 'Transaction confirmed')
|
||||
.within(() => {
|
||||
cy.getByTestId('external-link').should('exist');
|
||||
});
|
||||
});
|
||||
|
||||
it('Unable to withdraw asset on pub key view', function () {
|
||||
it('Should be able to see withdrawal details from toast', function () {
|
||||
cy.getByTestId(withdraw).click();
|
||||
cy.getByTestId(withdrawalForm, txTimeout).within(() => {
|
||||
cy.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 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.`;
|
||||
|
||||
@@ -221,13 +294,17 @@ context(
|
||||
cy.connectPublicKey(vegaWalletPubKey);
|
||||
cy.getByTestId(withdraw).should('be.visible').click();
|
||||
cy.getByTestId(withdrawalForm, txTimeout).within(() => {
|
||||
cy.get('select').select(usdtSelectValue, { force: true });
|
||||
cy.getByTestId('select-asset').click();
|
||||
cy.get('select')
|
||||
.select(usdtSelectValue, { force: true })
|
||||
.should('have.value', usdtSelectValue);
|
||||
cy.getByTestId(balanceAvailable, txTimeout).should('exist');
|
||||
cy.getByTestId(amountInput).click().type('100');
|
||||
cy.pause();
|
||||
cy.getByTestId(submitWithdrawalButton).click();
|
||||
});
|
||||
|
||||
cy.getByTestId('dialog-content')
|
||||
cy.getByTestId(withdrawalDialogContent)
|
||||
.last()
|
||||
.within(() => {
|
||||
cy.get('h1').should('have.text', 'Transaction failed');
|
||||
|
||||
@@ -11,7 +11,6 @@ import {
|
||||
import { mockNetworkUpgradeProposal } from '../../support/proposal.functions';
|
||||
|
||||
const proposalDocumentationLink = '[data-testid="proposal-documentation-link"]';
|
||||
const newProposalLink = '[data-testid="new-proposal-link"]';
|
||||
const governanceDocsUrl = 'https://vega.xyz/governance';
|
||||
const connectToVegaWalletButton = '[data-testid="connect-to-vega-wallet-btn"]';
|
||||
|
||||
@@ -32,6 +31,14 @@ context(
|
||||
verifyPageHeader('Proposals');
|
||||
});
|
||||
|
||||
// 3002-PROP-023 3004-PMAC-002 3005-PASN-002 3006-PASC-002 3007-PNEC-002 3008-PFRO-003
|
||||
it('should have button for link to more information on proposals', function () {
|
||||
const proposalsUrl = 'https://docs.vega.xyz/mainnet/tutorials/proposals';
|
||||
cy.getByTestId('new-proposal-link')
|
||||
.find('a')
|
||||
.should('have.attr', 'href', proposalsUrl);
|
||||
});
|
||||
|
||||
it('should be able to see a working link for - find out more about Vega governance', function () {
|
||||
// 3001-VOTE-001
|
||||
cy.get(proposalDocumentationLink)
|
||||
@@ -54,17 +61,62 @@ context(
|
||||
});
|
||||
});
|
||||
|
||||
it.skip('should be able to see button for - new proposal', function () {
|
||||
// 3001-VOTE-002
|
||||
cy.get(newProposalLink)
|
||||
.should('be.visible')
|
||||
.and('have.text', 'New proposal')
|
||||
.and('have.attr', 'href')
|
||||
.and('equal', '/proposals/propose');
|
||||
// 3007-PNE-021
|
||||
it('should have documentation links for network parameter proposal', function () {
|
||||
goToMakeNewProposal(governanceProposalType.NETWORK_PARAMETER);
|
||||
cy.getByTestId('proposal-docs-link')
|
||||
.find('a')
|
||||
.should('have.attr', 'href')
|
||||
.and('contain', '/tutorials/proposals/network-parameter-proposal');
|
||||
});
|
||||
|
||||
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);
|
||||
// 3003-PMAN-002 3003-PMAN-005
|
||||
it('should have documentation links for new market proposal', function () {
|
||||
goToMakeNewProposal(governanceProposalType.NEW_MARKET);
|
||||
cy.getByTestId('proposal-docs-link')
|
||||
.find('a')
|
||||
.should('have.attr', 'href')
|
||||
.and('contain', '/tutorials/proposals/new-market-proposal');
|
||||
});
|
||||
|
||||
// 3004-PMAC-005
|
||||
it('should have documentation links for update market proposal', function () {
|
||||
goToMakeNewProposal(governanceProposalType.UPDATE_MARKET);
|
||||
cy.getByTestId('proposal-docs-link')
|
||||
.find('a')
|
||||
.should('have.attr', 'href')
|
||||
.and('contain', '/tutorials/proposals/update-market-proposal');
|
||||
});
|
||||
|
||||
// 3005-PASN-002 005-PASN-005
|
||||
it('should have documentation links for new asset proposal', function () {
|
||||
goToMakeNewProposal(governanceProposalType.NEW_ASSET);
|
||||
cy.getByTestId('proposal-docs-link')
|
||||
.find('a')
|
||||
.should('have.attr', 'href')
|
||||
.and('contain', '/tutorials/proposals/new-asset-proposal');
|
||||
});
|
||||
|
||||
// 3006-PASC-002 3006-PASC-005
|
||||
it('should have documentation links for update asset proposal', function () {
|
||||
goToMakeNewProposal(governanceProposalType.UPDATE_ASSET);
|
||||
cy.getByTestId('proposal-docs-link')
|
||||
.find('a')
|
||||
.should('have.attr', 'href')
|
||||
.and('contain', '/tutorials/proposals/update-asset-proposal');
|
||||
});
|
||||
|
||||
// 3008-PFRO-003 3008-PFRO-017
|
||||
it('should have documentation links for freeform proposal', function () {
|
||||
goToMakeNewProposal(governanceProposalType.FREEFORM);
|
||||
cy.getByTestId('proposal-docs-link')
|
||||
.find('a')
|
||||
.should('have.attr', 'href')
|
||||
.and('contain', '/tutorials/proposals/freeform-proposal');
|
||||
});
|
||||
|
||||
it('should be able to see a connect wallet button - if vega wallet disconnected and user is submitting new proposal', function () {
|
||||
goToMakeNewProposal(governanceProposalType.RAW);
|
||||
cy.get(connectToVegaWalletButton)
|
||||
.should('be.visible')
|
||||
.and('have.text', 'Connect Vega wallet');
|
||||
@@ -128,6 +180,7 @@ context(
|
||||
.first()
|
||||
.find('[data-testid="view-proposal-btn"]')
|
||||
.click();
|
||||
cy.url().should('contain', '/protocol-upgrades/v1');
|
||||
cy.getByTestId('protocol-upgrade-proposal').within(() => {
|
||||
cy.get('h1').should('have.text', 'Vega Release v1');
|
||||
cy.getByTestId('protocol-upgrade-block-height').should(
|
||||
|
||||
@@ -11,7 +11,7 @@ import {
|
||||
goToMakeNewProposal,
|
||||
governanceProposalType,
|
||||
} from '../../support/governance.functions';
|
||||
import { vegaWalletFaucetAssetsWithoutCheck } from '../../support/wallet-vega.functions';
|
||||
import { vegaWalletFaucetAssetsWithoutCheck } from '../../support/wallet-functions';
|
||||
|
||||
const vegaWalletPubKey = Cypress.env('vegaWalletPublicKey2');
|
||||
const vegaPubkeyTruncated = Cypress.env('vegaWalletPublicKey2Short');
|
||||
|
||||
@@ -5,7 +5,10 @@ import {
|
||||
verifyPageHeader,
|
||||
verifyTabHighlighted,
|
||||
} from '../../support/common.functions';
|
||||
import { clickOnValidatorFromList } from '../../support/staking.functions';
|
||||
import {
|
||||
clickOnValidatorFromList,
|
||||
waitForBeginningOfEpoch,
|
||||
} from '../../support/staking.functions';
|
||||
|
||||
const guideLink = '[data-testid="staking-guide-link"]';
|
||||
const validatorTitle = '[data-testid="validator-node-title"]';
|
||||
@@ -37,206 +40,213 @@ context('Validators Page - verify elements on page', function () {
|
||||
});
|
||||
|
||||
describe('with wallets disconnected', { tags: '@smoke' }, function () {
|
||||
describe('description section', function () {
|
||||
it('Should have validators tab highlighted', function () {
|
||||
verifyTabHighlighted(navigation.validators);
|
||||
});
|
||||
|
||||
it('Should have validators ON VEGA header visible', function () {
|
||||
verifyPageHeader('Validators');
|
||||
});
|
||||
|
||||
it('Should have Staking Guide link visible', function () {
|
||||
// 2001-STKE-003
|
||||
cy.get(guideLink)
|
||||
.should('be.visible')
|
||||
.and('have.text', 'Read more about staking on Vega')
|
||||
.and(
|
||||
'have.attr',
|
||||
'href',
|
||||
'https://docs.vega.xyz/mainnet/concepts/vega-chain/#staking-on-vega'
|
||||
);
|
||||
});
|
||||
it('Should have validators tab highlighted', function () {
|
||||
verifyTabHighlighted(navigation.validators);
|
||||
});
|
||||
describe(
|
||||
'Should be able to see validator list from the staking page',
|
||||
{ tags: '@regression' },
|
||||
function () {
|
||||
// 2001-STKE-050
|
||||
it('Should be able to see validator names', function () {
|
||||
cy.get('[col-id="validator"] > div > span')
|
||||
.should('have.length.at.least', 1)
|
||||
.each(($name) => {
|
||||
cy.wrap($name).should('not.be.empty');
|
||||
});
|
||||
});
|
||||
|
||||
it('Should be able to see validator stake', function () {
|
||||
cy.getByTestId('total-stake')
|
||||
.should('have.length.at.least', 1)
|
||||
.each(($stake) => {
|
||||
cy.wrap($stake).should('not.be.empty');
|
||||
});
|
||||
});
|
||||
it('Should have validators ON VEGA header visible', function () {
|
||||
verifyPageHeader('Validators');
|
||||
});
|
||||
|
||||
it('Should be able to see validator stake tooltip', function () {
|
||||
cy.getByTestId('total-stake').first().realHover();
|
||||
it('Should have Staking Guide link visible', function () {
|
||||
// 1002-STKE-003
|
||||
cy.get(guideLink)
|
||||
.should('be.visible')
|
||||
.and('have.text', 'Read more about staking on Vega')
|
||||
.and(
|
||||
'have.attr',
|
||||
'href',
|
||||
'https://docs.vega.xyz/mainnet/concepts/vega-chain/#staking-on-vega'
|
||||
);
|
||||
});
|
||||
|
||||
cy.get(stakedByOperatorToolTip)
|
||||
.invoke('text')
|
||||
.should('contain', 'Staked by operator: 3,000.00');
|
||||
cy.get(stakedByDelegatesToolTip)
|
||||
.invoke('text')
|
||||
.should('contain', 'Staked by delegates: 0.00');
|
||||
cy.get(totalStakedToolTip)
|
||||
.invoke('text')
|
||||
.should('contain', 'Total stake: 3,000.00');
|
||||
});
|
||||
|
||||
it('Should be able to see validator normalised voting power', function () {
|
||||
cy.getByTestId('normalised-voting-power')
|
||||
.should('have.length.at.least', 1)
|
||||
.each(($vPower) => {
|
||||
cy.wrap($vPower).should('not.be.empty');
|
||||
});
|
||||
});
|
||||
|
||||
it('Should be able to see validator normalised voting power tooltip', function () {
|
||||
cy.getByTestId('normalised-voting-power').first().realHover();
|
||||
|
||||
cy.get(unnormalisedVotingPowerToolTip)
|
||||
.invoke('text')
|
||||
.should('contain', 'Unnormalised voting power: 20.00%');
|
||||
cy.get(normalisedVotingPowerToolTip)
|
||||
.invoke('text')
|
||||
.should('contain', 'Normalised voting power: 50.00%');
|
||||
});
|
||||
|
||||
// 2002-SINC-018
|
||||
it('Should be able to see validator total penalties', function () {
|
||||
cy.getByTestId('total-penalty')
|
||||
.should('have.length.at.least', 1)
|
||||
.each(($penalties) => {
|
||||
cy.wrap($penalties).should('contain.text', '0%');
|
||||
});
|
||||
});
|
||||
|
||||
it('Should be able to see validator penalties tooltip', function () {
|
||||
cy.getByTestId('total-penalty').realHover();
|
||||
|
||||
cy.get(performancePenaltyToolTip)
|
||||
.invoke('text')
|
||||
.should('contain', 'Performance penalty: 0.00%');
|
||||
cy.get(overstakedPenaltyToolTip)
|
||||
.invoke('text')
|
||||
.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 () {
|
||||
cy.getByTestId('total-pending-stake')
|
||||
.should('have.length.at.least', 1)
|
||||
.each(($pendingStake) => {
|
||||
cy.wrap($pendingStake).should('contain.text', '0.00');
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
// 2001-STKE-050
|
||||
describe(
|
||||
'Should be able to see static information about a validator',
|
||||
{ tags: '@smoke' },
|
||||
function () {
|
||||
before('connect wallets and click on validator', function () {
|
||||
cy.connectVegaWallet();
|
||||
clickOnValidatorFromList(0);
|
||||
});
|
||||
|
||||
// 2001-STKE-006
|
||||
it('Should be able to see validator name', function () {
|
||||
cy.get(validatorTitle).should('not.be.empty');
|
||||
});
|
||||
|
||||
// 2001-STKE-007
|
||||
it('Should be able to see validator id', function () {
|
||||
cy.get(validatorId).should('not.be.empty');
|
||||
});
|
||||
|
||||
// 2001-STKE-008
|
||||
it('Should be able to see validator public key', function () {
|
||||
cy.get(validatorPubKey).should('not.be.empty');
|
||||
});
|
||||
|
||||
// 2001-STKE-010
|
||||
it('Should be able to see Ethereum address', function () {
|
||||
cy.get(ethAddressLink)
|
||||
.should('not.be.empty')
|
||||
.and('have.attr', 'href');
|
||||
});
|
||||
// TODO validators missing url for more information about them 2001-STKE-09
|
||||
|
||||
it('Should be able to see validator status', function () {
|
||||
cy.get(validatorStatus).should('have.text', 'Consensus');
|
||||
});
|
||||
|
||||
// 2001-STKE-012
|
||||
it('Should be able to see total stake', function () {
|
||||
cy.get(totalStake).invoke('text').should('match', stakeNumberRegex);
|
||||
});
|
||||
|
||||
it('Should be able to see pending stake', function () {
|
||||
cy.get(pendingStake).invoke('text').should('match', stakeNumberRegex);
|
||||
});
|
||||
|
||||
it('Should be able to see staked by operator', function () {
|
||||
cy.get(stakedByOperator)
|
||||
.invoke('text')
|
||||
.should('match', stakeNumberRegex);
|
||||
});
|
||||
|
||||
it('Should be able to see staked by delegates', function () {
|
||||
cy.get(stakedByDelegates)
|
||||
.invoke('text')
|
||||
.should('match', stakeNumberRegex);
|
||||
});
|
||||
|
||||
// 2001-STKE-051
|
||||
it('Should be able to see stake share in percentage', function () {
|
||||
cy.get(stakeShare)
|
||||
.invoke('text')
|
||||
.then(($stakePercentage) => {
|
||||
// The pattern must start at a word boundary (\b).
|
||||
// The pattern cannot be immediately preceded by a dot ((?<!\.)).
|
||||
// The pattern can be one of the following:
|
||||
// A percentage value of zero (0%), or
|
||||
// A non-zero percentage value that can be:
|
||||
// A single digit (\d) between 0 and 9, or
|
||||
// A two-digit number between 0 and 99 (\d{1,2}), or
|
||||
// The number 100.
|
||||
// The pattern can optionally include a decimal point and one or more digits after the decimal point ((?:(?<!100)\.\d+)?). However, if the number is 100, it cannot have a decimal point.
|
||||
// The pattern must end with a percentage sign (%).
|
||||
|
||||
cy.wrap($stakePercentage).should(
|
||||
'match',
|
||||
/\b(?<!\.)(?:0+(?:\.0+)?%|(?:\d|\d{1,2}|100)(?:(?<!100)\.\d+)?)%/
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
// 2001-STKE-011 2002-SINC-001 2002-SINC-002
|
||||
it('Should be able to see epoch information', function () {
|
||||
const epochTitle = 'h3';
|
||||
const nextEpochInfo = 'p';
|
||||
|
||||
cy.get(epochCountDown).within(() => {
|
||||
cy.get(epochTitle).should('not.be.empty');
|
||||
cy.get(nextEpochInfo).should('contain.text', 'Next epoch');
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
||||
// 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(
|
||||
'Should be able to see validator list from the staking page',
|
||||
{ tags: '@regression' },
|
||||
function () {
|
||||
// 1002-STKE-050
|
||||
it('Should be able to see validator names', function () {
|
||||
cy.get('[col-id="validator"] > div > span')
|
||||
.should('have.length.at.least', 1)
|
||||
.each(($name) => {
|
||||
cy.wrap($name).should('not.be.empty');
|
||||
});
|
||||
});
|
||||
|
||||
it('Should be able to see validator stake', function () {
|
||||
cy.getByTestId('total-stake')
|
||||
.should('have.length.at.least', 1)
|
||||
.each(($stake) => {
|
||||
cy.wrap($stake).should('not.be.empty');
|
||||
});
|
||||
});
|
||||
|
||||
it('Should be able to see validator stake tooltip', function () {
|
||||
waitForBeginningOfEpoch();
|
||||
cy.getByTestId('total-stake').first().realHover();
|
||||
|
||||
cy.get(stakedByOperatorToolTip)
|
||||
.invoke('text')
|
||||
.should('contain', 'Staked by operator: 3,000.00');
|
||||
cy.get(stakedByDelegatesToolTip)
|
||||
.invoke('text')
|
||||
.should('contain', 'Staked by delegates: 0.00');
|
||||
cy.get(totalStakedToolTip)
|
||||
.invoke('text')
|
||||
.should('contain', 'Total stake: 3,000.00');
|
||||
});
|
||||
|
||||
it('Should be able to see validator normalised voting power', function () {
|
||||
cy.getByTestId('normalised-voting-power')
|
||||
.should('have.length.at.least', 1)
|
||||
.each(($vPower) => {
|
||||
cy.wrap($vPower).should('not.be.empty');
|
||||
});
|
||||
});
|
||||
|
||||
it('Should be able to see validator normalised voting power tooltip', function () {
|
||||
waitForBeginningOfEpoch();
|
||||
cy.getByTestId('normalised-voting-power').first().realHover();
|
||||
|
||||
cy.get(unnormalisedVotingPowerToolTip)
|
||||
.invoke('text')
|
||||
.should('contain', 'Unnormalised voting power: 20.00%');
|
||||
cy.get(normalisedVotingPowerToolTip)
|
||||
.invoke('text')
|
||||
.should('contain', 'Normalised voting power: 50.00%');
|
||||
});
|
||||
|
||||
// 2002-SINC-018
|
||||
it('Should be able to see validator total penalties', function () {
|
||||
cy.getByTestId('total-penalty')
|
||||
.should('have.length.at.least', 1)
|
||||
.each(($penalties) => {
|
||||
cy.wrap($penalties).should('contain.text', '0%');
|
||||
});
|
||||
});
|
||||
|
||||
it('Should be able to see validator penalties tooltip', function () {
|
||||
waitForBeginningOfEpoch();
|
||||
cy.getByTestId('total-penalty').realHover();
|
||||
|
||||
cy.get(performancePenaltyToolTip)
|
||||
.invoke('text')
|
||||
.should('contain', 'Performance penalty: 0.00%');
|
||||
cy.get(overstakedPenaltyToolTip)
|
||||
.invoke('text')
|
||||
.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 () {
|
||||
cy.getByTestId('total-pending-stake')
|
||||
.should('have.length.at.least', 1)
|
||||
.each(($pendingStake) => {
|
||||
cy.wrap($pendingStake).should('contain.text', '0.00');
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
// 1002-STKE-050
|
||||
describe(
|
||||
'Should be able to see static information about a validator',
|
||||
{ tags: '@smoke' },
|
||||
function () {
|
||||
before('connect wallets and click on validator', function () {
|
||||
cy.connectVegaWallet();
|
||||
clickOnValidatorFromList(0);
|
||||
});
|
||||
|
||||
// 1002-STKE-006
|
||||
it('Should be able to see validator name', function () {
|
||||
cy.get(validatorTitle).should('not.be.empty');
|
||||
});
|
||||
|
||||
// 1002-STKE-007
|
||||
it('Should be able to see validator id', function () {
|
||||
cy.get(validatorId).should('not.be.empty');
|
||||
});
|
||||
|
||||
// 1002-STKE-008
|
||||
it('Should be able to see validator public key', function () {
|
||||
cy.get(validatorPubKey).should('not.be.empty');
|
||||
});
|
||||
|
||||
// 1002-STKE-010
|
||||
it('Should be able to see Ethereum address', function () {
|
||||
cy.get(ethAddressLink).should('not.be.empty').and('have.attr', 'href');
|
||||
});
|
||||
// TODO validators missing url for more information about them 1002-STKE-09
|
||||
|
||||
it('Should be able to see validator status', function () {
|
||||
cy.get(validatorStatus).should('have.text', 'Consensus');
|
||||
});
|
||||
|
||||
// 1002-STKE-012
|
||||
it('Should be able to see total stake', function () {
|
||||
cy.get(totalStake).invoke('text').should('match', stakeNumberRegex);
|
||||
});
|
||||
|
||||
it('Should be able to see pending stake', function () {
|
||||
cy.get(pendingStake).invoke('text').should('match', stakeNumberRegex);
|
||||
});
|
||||
|
||||
it('Should be able to see staked by operator', function () {
|
||||
cy.get(stakedByOperator)
|
||||
.invoke('text')
|
||||
.should('match', stakeNumberRegex);
|
||||
});
|
||||
|
||||
it('Should be able to see staked by delegates', function () {
|
||||
cy.get(stakedByDelegates)
|
||||
.invoke('text')
|
||||
.should('match', stakeNumberRegex);
|
||||
});
|
||||
|
||||
// 1002-STKE-051
|
||||
it('Should be able to see stake share in percentage', function () {
|
||||
cy.get(stakeShare)
|
||||
.invoke('text')
|
||||
.then(($stakePercentage) => {
|
||||
// The pattern must start at a word boundary (\b).
|
||||
// The pattern cannot be immediately preceded by a dot ((?<!\.)).
|
||||
// The pattern can be one of the following:
|
||||
// A percentage value of zero (0%), or
|
||||
// A non-zero percentage value that can be:
|
||||
// A single digit (\d) between 0 and 9, or
|
||||
// A two-digit number between 0 and 99 (\d{1,2}), or
|
||||
// The number 100.
|
||||
// The pattern can optionally include a decimal point and one or more digits after the decimal point ((?:(?<!100)\.\d+)?). However, if the number is 100, it cannot have a decimal point.
|
||||
// The pattern must end with a percentage sign (%).
|
||||
|
||||
cy.wrap($stakePercentage).should(
|
||||
'match',
|
||||
/\b(?<!\.)(?:0+(?:\.0+)?%|(?:\d|\d{1,2}|100)(?:(?<!100)\.\d+)?)%/
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
// 1002-STKE-011 2002-SINC-001 2002-SINC-002
|
||||
it('Should be able to see epoch information', function () {
|
||||
const epochTitle = 'h3';
|
||||
const nextEpochInfo = 'p';
|
||||
|
||||
cy.get(epochCountDown).within(() => {
|
||||
cy.get(epochTitle).should('not.be.empty');
|
||||
cy.get(nextEpochInfo).should('contain.text', 'Next epoch');
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import { truncateByChars } from '@vegaprotocol/utils';
|
||||
import { waitForSpinner } from '../../support/common.functions';
|
||||
import { vegaWalletTeardown } from '../../support/wallet-teardown.functions';
|
||||
import { vegaWalletFaucetAssetsWithoutCheck } from '../../support/wallet-vega.functions';
|
||||
import {
|
||||
vegaWalletFaucetAssetsWithoutCheck,
|
||||
vegaWalletTeardown,
|
||||
} from '../../support/wallet-functions';
|
||||
|
||||
const walletContainer = 'aside [data-testid="vega-wallet"]';
|
||||
const walletHeader = '[data-testid="wallet-header"] h1';
|
||||
@@ -337,6 +339,7 @@ context(
|
||||
.parent()
|
||||
.siblings()
|
||||
.invoke('text')
|
||||
.should('have.length.at.least', 4)
|
||||
.then(parseFloat)
|
||||
.should('be.gte', parseFloat(expectedAmount));
|
||||
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
import { stakingPageDisassociateAllTokens } from './staking.functions';
|
||||
|
||||
const tokenDropDown = 'state-trigger';
|
||||
const txTimeout = Cypress.env('txTimeout');
|
||||
|
||||
export enum navigation {
|
||||
section = 'nav',
|
||||
home = '[href="/"]',
|
||||
vesting = '[href="/token/redeem"]',
|
||||
validators = '[href="/validators"]',
|
||||
rewards = '[href="/rewards"]',
|
||||
@@ -18,6 +21,7 @@ export function convertTokenValueToNumber(subject: string) {
|
||||
}
|
||||
|
||||
const topLevelRoutes = [
|
||||
navigation.home,
|
||||
navigation.proposals,
|
||||
navigation.validators,
|
||||
navigation.rewards,
|
||||
@@ -97,3 +101,25 @@ export function turnTelemetryOff() {
|
||||
win.localStorage.setItem('vega_telemetry_on', 'false')
|
||||
);
|
||||
}
|
||||
|
||||
export function dissociateFromSecondWalletKey() {
|
||||
const secondWalletKey = Cypress.env('vegaWalletPublicKey2Short');
|
||||
cy.getByTestId('vega-in-wallet')
|
||||
.first()
|
||||
.within(() => {
|
||||
cy.getByTestId('eth-wallet-associated-balances')
|
||||
.last()
|
||||
.within(() => {
|
||||
cy.getByTestId('associated-key')
|
||||
.invoke('text')
|
||||
.as('associatedPubKey');
|
||||
});
|
||||
});
|
||||
cy.get('@associatedPubKey').then((associatedPubKey) => {
|
||||
if (associatedPubKey == secondWalletKey) {
|
||||
cy.get('[data-testid="manage-vega-wallet"]:visible').click();
|
||||
cy.get('[data-testid="select-keypair-button"]').eq(0).click();
|
||||
stakingPageDisassociateAllTokens();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -59,11 +59,11 @@ export function submitUniqueRawProposal(proposalFields: {
|
||||
submit?: boolean;
|
||||
}) {
|
||||
goToMakeNewProposal(governanceProposalType.RAW);
|
||||
let proposalBodyPath = '/proposals/raw.json';
|
||||
let proposalBodyPath = 'src/fixtures/proposals/raw.json';
|
||||
if (proposalFields.proposalBody) {
|
||||
proposalBodyPath = proposalFields.proposalBody;
|
||||
}
|
||||
cy.fixture(proposalBodyPath).then((rawProposal) => {
|
||||
cy.readFile(proposalBodyPath).then((rawProposal) => {
|
||||
if (proposalFields.proposalTitle) {
|
||||
rawProposal.rationale.title = proposalFields.proposalTitle;
|
||||
cy.wrap(proposalFields.proposalTitle).as('proposalTitle');
|
||||
@@ -73,7 +73,10 @@ export function submitUniqueRawProposal(proposalFields: {
|
||||
}
|
||||
if (proposalFields.closingTimestamp) {
|
||||
rawProposal.terms.closingTimestamp = proposalFields.closingTimestamp;
|
||||
} else {
|
||||
} else if (
|
||||
!proposalFields.closingTimestamp &&
|
||||
!proposalFields.proposalBody
|
||||
) {
|
||||
const minTimeStamp = createTenDigitUnixTimeStampForSpecifiedDays(2);
|
||||
rawProposal.terms.closingTimestamp = minTimeStamp;
|
||||
}
|
||||
@@ -106,7 +109,7 @@ export function enterUniqueFreeFormProposalBody(
|
||||
'this is a e2e freeform proposal description'
|
||||
);
|
||||
cy.get(proposalVoteDeadline).clear().click().type(timestamp);
|
||||
cy.getByTestId('proposal-submit').should('be.visible').click();
|
||||
cy.getByTestId('proposal-download-json').should('be.visible').click();
|
||||
}
|
||||
|
||||
export function getProposalFromTitle(proposalTitle: string) {
|
||||
@@ -188,6 +191,7 @@ export function goToMakeNewProposal(proposalType: governanceProposalType) {
|
||||
}
|
||||
}
|
||||
|
||||
// 3001-VOTE-013 3001-VOTE-014
|
||||
export function waitForProposalSubmitted() {
|
||||
cy.contains('Awaiting network confirmation', epochTimeout).should(
|
||||
'be.visible'
|
||||
@@ -222,6 +226,23 @@ export function createFreeformProposal(proposalTitle: string) {
|
||||
navigateTo(navigation.proposals);
|
||||
}
|
||||
|
||||
export function getDownloadedProposalJsonPath(proposalType: string) {
|
||||
const downloadPath = './cypress/downloads/';
|
||||
const filepath = downloadPath + proposalType + getFormattedTime() + '.json';
|
||||
return filepath;
|
||||
}
|
||||
|
||||
function getFormattedTime() {
|
||||
const now = new Date();
|
||||
const day = now.getDate().toString().padStart(2, '0');
|
||||
const month = now.toLocaleString('en-US', { month: 'short' });
|
||||
const year = now.getFullYear().toString();
|
||||
const hours = now.getHours().toString().padStart(2, '0');
|
||||
const minutes = now.getMinutes().toString().padStart(2, '0');
|
||||
|
||||
return `${day}-${month}-${year}-${hours}-${minutes}`;
|
||||
}
|
||||
|
||||
export enum governanceProposalType {
|
||||
NETWORK_PARAMETER = 'Network parameter',
|
||||
NEW_MARKET = 'New market',
|
||||
|
||||
@@ -5,8 +5,7 @@ import './common.functions.ts';
|
||||
import './staking.functions.ts';
|
||||
import './governance.functions.ts';
|
||||
import './wallet-eth.functions.ts';
|
||||
import './wallet-teardown.functions.ts';
|
||||
import './wallet-vega.functions.ts';
|
||||
import './wallet-functions.ts';
|
||||
import './proposal.functions.ts';
|
||||
import 'cypress-mochawesome-reporter/register';
|
||||
import registerCypressGrep from '@cypress/grep';
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { closeDialog } from './common.functions';
|
||||
import { vegaWalletTeardown } from './wallet-teardown.functions';
|
||||
import { vegaWalletTeardown } from './wallet-functions';
|
||||
|
||||
const tokenAmountInputBox = '[data-testid="token-amount-input"]';
|
||||
const tokenSubmitButton = '[data-testid="token-input-submit-button"]';
|
||||
@@ -221,7 +221,7 @@ export function ensureSpecifiedUnstakedTokensAreAssociated(
|
||||
}
|
||||
|
||||
export function closeStakingDialog() {
|
||||
cy.getByTestId('dialog-title').should(
|
||||
cy.getByTestId('dialog-title', txTimeout).should(
|
||||
'contain.text',
|
||||
'At the beginning of the next epoch'
|
||||
);
|
||||
@@ -236,8 +236,8 @@ export function validateWalletCurrency(
|
||||
currencyTitle: string,
|
||||
expectedAmount: string
|
||||
) {
|
||||
cy.get("[data-testid='currency-title']")
|
||||
.contains(currencyTitle)
|
||||
cy.get("[data-testid='currency-title']", txTimeout)
|
||||
.contains(currencyTitle, txTimeout)
|
||||
.parent()
|
||||
.parent()
|
||||
.within(() => {
|
||||
|
||||
@@ -5,7 +5,7 @@ const capsuleWalletConnectButton = '[data-testid="web3-connector-Unknown"]';
|
||||
|
||||
export function ethereumWalletConnect() {
|
||||
cy.highlight('Connecting Eth Wallet');
|
||||
cy.get(connectToEthButton).within(() => {
|
||||
cy.get(connectToEthButton, { timeout: 60000 }).within(() => {
|
||||
cy.contains('Connect Ethereum wallet to associate $VEGA')
|
||||
.should('be.visible')
|
||||
.click();
|
||||
|
||||
+49
-34
@@ -19,7 +19,7 @@ const ethStakingBridgeContractAddress = Cypress.env(
|
||||
);
|
||||
const ethProviderUrl = Cypress.env('ethProviderUrl');
|
||||
const getAccount = (number = 0) => `m/44'/60'/0'/0/${number}`;
|
||||
const transactionTimeout = 100000;
|
||||
const transactionTimeout = { timeout: 100000, log: false };
|
||||
const Erc20BridgeAddress = '0x9708FF7510D4A7B9541e1699d15b53Ecb1AFDc54';
|
||||
|
||||
const provider = new ethers.providers.JsonRpcProvider({ url: ethProviderUrl });
|
||||
@@ -43,10 +43,7 @@ export async function depositAsset(
|
||||
const faucet = new Token(assetEthAddress, signer);
|
||||
cy.wrap(
|
||||
faucet.approve(Erc20BridgeAddress, amount + '0'.repeat(decimalPlaces + 1)),
|
||||
{
|
||||
timeout: transactionTimeout,
|
||||
log: false,
|
||||
}
|
||||
transactionTimeout
|
||||
).then(() => {
|
||||
const collateralBridge = new CollateralBridge(Erc20BridgeAddress, signer);
|
||||
cy.wrap(
|
||||
@@ -55,7 +52,7 @@ export async function depositAsset(
|
||||
amount + '0'.repeat(decimalPlaces),
|
||||
'0x' + vegaWalletPubKey
|
||||
),
|
||||
{ timeout: transactionTimeout, log: false }
|
||||
transactionTimeout
|
||||
);
|
||||
});
|
||||
}
|
||||
@@ -79,13 +76,13 @@ export async function vegaWalletTeardown() {
|
||||
}
|
||||
});
|
||||
cy.get(vegaWalletContainer).within(() => {
|
||||
cy.get(associatedAmountInWallet, {
|
||||
timeout: transactionTimeout,
|
||||
})
|
||||
.should('have.length', 1, { timeout: transactionTimeout })
|
||||
.contains('0.00', {
|
||||
timeout: transactionTimeout,
|
||||
});
|
||||
cy.get(associatedAmountInWallet, transactionTimeout).should(
|
||||
'have.length',
|
||||
1
|
||||
);
|
||||
cy.get(associatedAmountInWallet)
|
||||
.first(transactionTimeout)
|
||||
.should('have.text', '0.00');
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -109,7 +106,7 @@ async function vegaWalletTeardownStaking(stakingBridgeContract: StakingBridge) {
|
||||
cy.highlight('Tearing down staking tokens from vega wallet if present');
|
||||
cy.wrap(
|
||||
stakingBridgeContract.stake_balance(ethWalletPubKey, vegaWalletPubKey),
|
||||
{ timeout: transactionTimeout }
|
||||
transactionTimeout
|
||||
).then((stakeBalance) => {
|
||||
if (Number(stakeBalance) != 0) {
|
||||
cy.get(vegaWalletContainer).within(() => {
|
||||
@@ -122,31 +119,25 @@ async function vegaWalletTeardownStaking(stakingBridgeContract: StakingBridge) {
|
||||
String(stakeBalance),
|
||||
vegaWalletPubKey
|
||||
),
|
||||
{ timeout: transactionTimeout }
|
||||
transactionTimeout
|
||||
);
|
||||
cy.wrap(
|
||||
vestingContract.stake_balance(ethWalletPubKey, vegaWalletPubKey),
|
||||
{
|
||||
timeout: transactionTimeout,
|
||||
log: false,
|
||||
}
|
||||
transactionTimeout
|
||||
).then((vestingAmount) => {
|
||||
if (Number(vestingAmount) != 0) {
|
||||
cy.contains('Associated', {
|
||||
timeout: transactionTimeout,
|
||||
})
|
||||
cy.contains('Associated', transactionTimeout)
|
||||
.parent()
|
||||
.parent()
|
||||
.within(() => {
|
||||
cy.getByTestId('currency-value', {
|
||||
timeout: transactionTimeout,
|
||||
})
|
||||
.should('have.length', 1)
|
||||
cy.getByTestId('currency-value', transactionTimeout)
|
||||
.first()
|
||||
.invoke('text')
|
||||
.as('displayedAmount');
|
||||
cy.get('@displayedAmount', {
|
||||
timeout: transactionTimeout,
|
||||
}).should('not.eq', $associatedAmount);
|
||||
cy.get('@displayedAmount', transactionTimeout).should(
|
||||
'not.eq',
|
||||
$associatedAmount
|
||||
);
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -158,14 +149,14 @@ async function vegaWalletTeardownStaking(stakingBridgeContract: StakingBridge) {
|
||||
|
||||
async function vegaWalletTeardownVesting(vestingContract: TokenVesting) {
|
||||
cy.highlight('Tearing down vesting tokens from vega wallet if present');
|
||||
cy.wrap(vestingContract.stake_balance(ethWalletPubKey, vegaWalletPubKey), {
|
||||
timeout: transactionTimeout,
|
||||
log: false,
|
||||
}).then((vestingAmount) => {
|
||||
cy.wrap(
|
||||
vestingContract.stake_balance(ethWalletPubKey, vegaWalletPubKey),
|
||||
transactionTimeout
|
||||
).then((vestingAmount) => {
|
||||
if (Number(vestingAmount) != 0) {
|
||||
cy.wrap(
|
||||
vestingContract.remove_stake(String(vestingAmount), vegaWalletPubKey),
|
||||
{ timeout: transactionTimeout }
|
||||
transactionTimeout
|
||||
);
|
||||
}
|
||||
});
|
||||
@@ -182,3 +173,27 @@ export async function vegaWalletDisassociate(amount: string) {
|
||||
amount = amount + '0'.repeat(18);
|
||||
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();
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
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'
|
||||
);
|
||||
});
|
||||
}
|
||||
@@ -5,10 +5,11 @@ NX_VEGA_URL=https://api.n00.stagnet1.vega.xyz/graphql
|
||||
NX_ETHEREUM_PROVIDER_URL=https://sepolia.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8
|
||||
NX_ETHERSCAN_URL=https://sepolia.etherscan.io
|
||||
NX_FAIRGROUND=false
|
||||
NX_VEGA_NETWORKS='{"DEVNET":"https://dev.governance.vega.xyz","STAGNET1":"https://stagnet1.governance.vega.xyz","TESTNET":"https://governance.fairground.wtf","MAINNET":"https://governance.vega.xyz"}'
|
||||
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_GITHUB_FEEDBACK_URL=https://github.com/vegaprotocol/feedback/discussions
|
||||
NX_VEGA_WALLET_URL=http://localhost:1789
|
||||
NX_VEGA_EXPLORER_URL=https://stagnet1.explorer.vega.xyz
|
||||
NX_VEGA_EXPLORER_URL=https://explorer.stagnet1.vega.rocks
|
||||
|
||||
NX_HOSTED_WALLET_URL=https://wallet.testnet.vega.xyz
|
||||
NX_VEGA_DOCS_URL=https://docs.vega.xyz/testnet
|
||||
NX_DELEGATIONS_PAGINATION=50
|
||||
|
||||
@@ -6,9 +6,10 @@ NX_VEGA_NETWORKS='{"DEVNET":"https://dev.governance.vega.xyz","TESTNET":"https:/
|
||||
NX_ETHEREUM_PROVIDER_URL=https://sepolia.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8
|
||||
NX_ETHERSCAN_URL=https://sepolia.etherscan.io
|
||||
NX_GITHUB_FEEDBACK_URL=https://github.com/vegaprotocol/feedback/discussions
|
||||
NX_VEGA_EXPLORER_URL=https://dev.explorer.vega.xyz
|
||||
NX_VEGA_EXPLORER_URL=#
|
||||
NX_VEGA_DOCS_URL=https://docs.vega.xyz/testnet
|
||||
NX_DELEGATIONS_PAGINATION=50
|
||||
NX_TRANCHES_SERVICE_URL=https://tranches-devnet1-k8s.ops.vega.xyz
|
||||
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json
|
||||
NX_VEGA_REST_URL=https://api.n00.devnet1.vega.xyz/api/v2/
|
||||
NX_SENTRY_DSN=https://4b8c8a8ba07742648aa4dfe1b8d17e40@o286262.ingest.sentry.io/5882996
|
||||
|
||||
@@ -3,9 +3,10 @@ NX_VEGA_ENV=MAINNET
|
||||
NX_VEGA_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/networks/master/mainnet1/mainnet1.toml
|
||||
NX_VEGA_URL=https://api.vega.community/graphql
|
||||
NX_VEGA_NETWORKS='{"DEVNET":"https://dev.governance.vega.xyz","TESTNET":"https://governance.fairground.wtf","MAINNET":"https://governance.vega.xyz"}'
|
||||
NX_ETHEREUM_PROVIDER_URL=https://mainnet.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8
|
||||
NX_ETHEREUM_PROVIDER_URL=https://mainnet.infura.io/v3/3ba145ccc2884bcd91213d8dc989ca76
|
||||
NX_ETHERSCAN_URL=https://etherscan.io
|
||||
NX_GITHUB_FEEDBACK_URL=https://github.com/vegaprotocol/feedback/discussions
|
||||
NX_SENTRY_DSN=https://4b8c8a8ba07742648aa4dfe1b8d17e40@o286262.ingest.sentry.io/5882996
|
||||
NX_VEGA_EXPLORER_URL=https://explorer.vega.xyz
|
||||
NX_VEGA_DOCS_URL=https://docs.vega.xyz/mainnet
|
||||
NX_SENTRY_DSN=https://4b8c8a8ba07742648aa4dfe1b8d17e40:87edc2605e544f888305d7fc4a9141bd@o286262.ingest.sentry.io/5882996
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
# App configuration variables
|
||||
NX_VEGA_URL=https://api.n00.stagnet1.vega.xyz/graphql
|
||||
NX_VEGA_ENV=STAGNET1
|
||||
NX_VEGA_NETWORKS='{"DEVNET":"https://dev.governance.vega.xyz","STAGNET1":"https://stagnet1.governance.vega.xyz","TESTNET":"https://governance.fairground.wtf","MAINNET":"https://governance.vega.xyz"}'
|
||||
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_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/networks-internal/main/stagnet1/vegawallet-stagnet1.toml
|
||||
NX_VEGA_EXPLORER_URL=https://stagnet1.explorer.vega.xyz
|
||||
NX_VEGA_EXPLORER_URL=https://explorer.stagnet1.vega.rocks
|
||||
NX_VEGA_DOCS_URL=https://docs.vega.xyz/testnet
|
||||
NX_DELEGATIONS_PAGINATION=50
|
||||
NX_TRANCHES_SERVICE_URL=https://tranches-stagnet1-k8s.ops.vega.xyz
|
||||
|
||||
@@ -12,4 +12,5 @@ NX_HOSTED_WALLET_URL=https://wallet.testnet.vega.xyz
|
||||
NX_DELEGATIONS_PAGINATION=50
|
||||
NX_TRANCHES_SERVICE_URL=https://tranches-testnet-k8s.ops.vega.xyz
|
||||
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json
|
||||
NX_VEGA_REST_URL=https://api.n07.testnet.vega.xyz/api/v2/
|
||||
NX_VEGA_REST_URL=https://api.n07.testnet.vega.xyz/api/v2/
|
||||
NX_SENTRY_DSN=https://4b8c8a8ba07742648aa4dfe1b8d17e40@o286262.ingest.sentry.io/5882996
|
||||
|
||||
@@ -7,6 +7,7 @@ NX_VEGA_NETWORKS='{"DEVNET":"https://dev.governance.vega.xyz","TESTNET":"https:/
|
||||
NX_ETHEREUM_PROVIDER_URL=https://sepolia.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8
|
||||
NX_ETHERSCAN_URL=https://sepolia.etherscan.io
|
||||
NX_GITHUB_FEEDBACK_URL=https://github.com/vegaprotocol/feedback/discussions
|
||||
NX_VEGA_EXPLORER_URL=https://dev.explorer.vega.xyz
|
||||
NX_VEGA_EXPLORER_URL=https://explorer.validators-testnet.vega.rocks/
|
||||
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_VEGA_REST_URL=https://api-validators-testnet.vega.rocks/api/v2/
|
||||
NX_SENTRY_DSN=https://4b8c8a8ba07742648aa4dfe1b8d17e40@o286262.ingest.sentry.io/5882996
|
||||
@@ -0,0 +1 @@
|
||||
export * from './multisig-incorrect-notice';
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
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();
|
||||
});
|
||||
});
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
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>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,40 @@
|
||||
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,9 +4,11 @@ import {
|
||||
useAppState,
|
||||
} from '../../contexts/app-state/app-state-context';
|
||||
import { Connectors } from '../../lib/vega-connectors';
|
||||
import { RiskMessage } from './risk-message';
|
||||
|
||||
export const VegaWalletDialogs = () => {
|
||||
const { appState, appDispatch } = useAppState();
|
||||
|
||||
return (
|
||||
<>
|
||||
<VegaConnectDialog
|
||||
@@ -17,6 +19,7 @@ export const VegaWalletDialogs = () => {
|
||||
isOpen: open,
|
||||
})
|
||||
}
|
||||
riskMessage={<RiskMessage />}
|
||||
/>
|
||||
|
||||
<VegaManageDialog
|
||||
|
||||
@@ -3,32 +3,32 @@ import { usePendingBalancesStore } from './use-pending-balances-manager';
|
||||
import type { Contract } from 'ethers';
|
||||
import { useListenForPendingEthEvents } from './use-listen-for-pending-eth-events';
|
||||
import { prepend0x } from '@vegaprotocol/smart-contracts';
|
||||
import { useWeb3React } from '@web3-react/core';
|
||||
|
||||
export const useListenForStakingEvents = (
|
||||
contract: Contract | undefined,
|
||||
vegaPublicKey: string | null,
|
||||
numberOfConfirmations: number
|
||||
) => {
|
||||
const { account } = useWeb3React();
|
||||
const { addPendingTxs, removePendingTx, resetPendingTxs } =
|
||||
usePendingBalancesStore((state) => ({
|
||||
addPendingTxs: state.addPendingTxs,
|
||||
removePendingTx: state.removePendingTx,
|
||||
resetPendingTxs: state.resetPendingTxs,
|
||||
}));
|
||||
const addFilter = useMemo(
|
||||
() =>
|
||||
vegaPublicKey && contract
|
||||
? contract.filters.Stake_Deposited(null, null, prepend0x(vegaPublicKey))
|
||||
: null,
|
||||
[contract, vegaPublicKey]
|
||||
);
|
||||
const removeFilter = useMemo(
|
||||
() =>
|
||||
vegaPublicKey && contract
|
||||
? contract.filters.Stake_Removed(null, null, prepend0x(vegaPublicKey))
|
||||
: null,
|
||||
[contract, vegaPublicKey]
|
||||
);
|
||||
const addFilter = useMemo(() => {
|
||||
if (!account || !vegaPublicKey || !contract) return null;
|
||||
return contract.filters.Stake_Deposited(
|
||||
null,
|
||||
null,
|
||||
prepend0x(vegaPublicKey)
|
||||
);
|
||||
}, [contract, vegaPublicKey, account]);
|
||||
const removeFilter = useMemo(() => {
|
||||
if (!account || !vegaPublicKey || !contract) return null;
|
||||
return contract.filters.Stake_Removed(null, null, prepend0x(vegaPublicKey));
|
||||
}, [contract, vegaPublicKey, account]);
|
||||
|
||||
/**
|
||||
* Listen for all add stake events
|
||||
|
||||
@@ -116,7 +116,7 @@
|
||||
"Showing tranches with <{{trancheMinimum}} VEGA, click to hide these tranches": "Showing tranches with ≤{{trancheMinimum}} $VEGA, click to hide these tranches",
|
||||
"Not showing tranches with <{{trancheMinimum}} VEGA, click to show all tranches": "Not showing tranches with ≤{{trancheMinimum}} $VEGA, click to show all tranches",
|
||||
"the holder": "the holder",
|
||||
"We couldn't seem to load your data.": "We couldn't seem to load your data.",
|
||||
"Your data couldn't be loaded": "Your data couldn't be loaded",
|
||||
"Vesting VEGA": "Vesting VEGA",
|
||||
"All the tokens in this tranche are locked and can not be redeemed yet.": "All the tokens in this tranche are locked and can not be redeemed yet.",
|
||||
"Redeem unlocked VEGA from tranche {{id}}": "Redeem unlocked $VEGA from tranche {{id}}",
|
||||
@@ -596,6 +596,8 @@
|
||||
"noPercentage": "No percentage",
|
||||
"proposalJson": "Full proposal JSON",
|
||||
"proposalDetails": "Proposal details",
|
||||
"marketSpecification": "Market specification",
|
||||
"viewMarketJson": "View market JSON",
|
||||
"proposalDescription": "Description",
|
||||
"currentlySetTo": "Currently expected to ",
|
||||
"currently": "currently",
|
||||
@@ -728,8 +730,12 @@
|
||||
"ThisWillSetEnactmentDeadlineTo": "This will set the enactment date to",
|
||||
"ThisWillSetValidationDeadlineTo": "This will set the validation deadline to",
|
||||
"Hours": "hours",
|
||||
"ThisWillAdd2MinutesToAllowTimeToConfirmInWallet": "Note: we add 2 minutes of extra time when you choose the minimum value. This gives you time to confirm the proposal in your wallet.",
|
||||
"ProposalWillFailIfEnactmentIsEarlierThanVotingDeadline": "Proposal will fail if enactment is earlier than the voting deadline",
|
||||
"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.",
|
||||
"ProposalWillFailIfEnactmentIsEarlierThanVotingDeadline": "The 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",
|
||||
"MarketName": "Market name",
|
||||
"MarketCode": "Market code",
|
||||
@@ -815,5 +821,16 @@
|
||||
"NoThanks": "No thanks",
|
||||
"ShareData": "Share 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"
|
||||
}
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
import classnames from 'classnames';
|
||||
|
||||
export const collapsibleToggleStyles = (toggleState: boolean) =>
|
||||
classnames('mb-4 transition-transform ease-in-out duration-300', {
|
||||
'rotate-180': toggleState,
|
||||
});
|
||||
@@ -0,0 +1,70 @@
|
||||
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,
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,42 @@
|
||||
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,
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,21 @@
|
||||
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;
|
||||
@@ -209,7 +209,10 @@ const GovernanceHome = ({ name }: RouteChildProps) => {
|
||||
[proposalsData]
|
||||
);
|
||||
|
||||
const sortedProposals = useMemo(() => orderByDate(proposals), [proposals]);
|
||||
const sortedProposals = useMemo(
|
||||
() => orderByDate(proposals).reverse(),
|
||||
[proposals]
|
||||
);
|
||||
|
||||
const protocolUpgradeProposals = useMemo(
|
||||
() =>
|
||||
|
||||
+2
-5
@@ -1,9 +1,9 @@
|
||||
import ReactMarkdown from 'react-markdown';
|
||||
import classnames from 'classnames';
|
||||
import { useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Icon, RoundedWrapper } from '@vegaprotocol/ui-toolkit';
|
||||
import { SubHeading } from '../../../../components/heading';
|
||||
import { collapsibleToggleStyles } from '../../../../lib/collapsible-toggle-styles';
|
||||
|
||||
export const ProposalDescription = ({
|
||||
description,
|
||||
@@ -12,9 +12,6 @@ export const ProposalDescription = ({
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const [showDescription, setShowDescription] = useState(false);
|
||||
const showDescriptionIconClasses = classnames('mb-4', {
|
||||
'rotate-180': showDescription,
|
||||
});
|
||||
|
||||
return (
|
||||
<section data-testid="proposal-description">
|
||||
@@ -24,7 +21,7 @@ export const ProposalDescription = ({
|
||||
>
|
||||
<div className="flex items-center gap-3">
|
||||
<SubHeading title={t('proposalDescription')} />
|
||||
<div className={showDescriptionIconClasses}>
|
||||
<div className={collapsibleToggleStyles(showDescription)}>
|
||||
<Icon name="chevron-down" size={8} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
+1
-1
@@ -124,7 +124,7 @@ describe('Proposal header', () => {
|
||||
})
|
||||
);
|
||||
expect(screen.getByTestId('proposal-title')).toHaveTextContent(
|
||||
'Unknown proposal'
|
||||
'New asset proposal'
|
||||
);
|
||||
expect(screen.getByTestId('proposal-type')).toHaveTextContent('New asset');
|
||||
expect(screen.getByTestId('proposal-details')).toHaveTextContent(
|
||||
|
||||
+13
-2
@@ -21,6 +21,7 @@ export const ProposalHeader = ({
|
||||
|
||||
let details: ReactNode;
|
||||
let proposalType = '';
|
||||
let fallbackTitle = '';
|
||||
|
||||
const title = proposal?.rationale.title.trim();
|
||||
|
||||
@@ -29,6 +30,7 @@ export const ProposalHeader = ({
|
||||
switch (change?.__typename) {
|
||||
case 'NewMarket': {
|
||||
proposalType = 'NewMarket';
|
||||
fallbackTitle = t('NewMarketProposal');
|
||||
details = (
|
||||
<>
|
||||
<span>
|
||||
@@ -50,6 +52,7 @@ export const ProposalHeader = ({
|
||||
}
|
||||
case 'UpdateMarket': {
|
||||
proposalType = 'UpdateMarket';
|
||||
fallbackTitle = t('UpdateMarketProposal');
|
||||
details = (
|
||||
<>
|
||||
<span>{t('Market change')}:</span>{' '}
|
||||
@@ -60,6 +63,7 @@ export const ProposalHeader = ({
|
||||
}
|
||||
case 'NewAsset': {
|
||||
proposalType = 'NewAsset';
|
||||
fallbackTitle = t('NewAssetProposal');
|
||||
details = (
|
||||
<>
|
||||
<span>{t('Symbol')}:</span> <Lozenge>{change.symbol}.</Lozenge>{' '}
|
||||
@@ -81,6 +85,7 @@ export const ProposalHeader = ({
|
||||
}
|
||||
case 'UpdateNetworkParameter': {
|
||||
proposalType = 'NetworkParameter';
|
||||
fallbackTitle = t('NetworkParameterProposal');
|
||||
details = (
|
||||
<>
|
||||
<span>{t('Change')}:</span>{' '}
|
||||
@@ -95,11 +100,13 @@ export const ProposalHeader = ({
|
||||
}
|
||||
case 'NewFreeform': {
|
||||
proposalType = 'Freeform';
|
||||
fallbackTitle = t('FreeformProposal');
|
||||
details = <span />;
|
||||
break;
|
||||
}
|
||||
case 'UpdateAsset': {
|
||||
proposalType = 'UpdateAsset';
|
||||
fallbackTitle = t('UpdateAssetProposal');
|
||||
details = (
|
||||
<>
|
||||
<span>{t('AssetID')}:</span>{' '}
|
||||
@@ -115,10 +122,14 @@ export const ProposalHeader = ({
|
||||
<div data-testid="proposal-title">
|
||||
{isListItem ? (
|
||||
<header>
|
||||
<SubHeading title={titleContent || t('Unknown proposal')} />
|
||||
<SubHeading
|
||||
title={titleContent || fallbackTitle || t('Unknown proposal')}
|
||||
/>
|
||||
</header>
|
||||
) : (
|
||||
<Heading title={titleContent || t('Unknown proposal')} />
|
||||
<Heading
|
||||
title={titleContent || fallbackTitle || t('Unknown proposal')}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import classnames from 'classnames';
|
||||
import { useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Icon, SyntaxHighlighter } from '@vegaprotocol/ui-toolkit';
|
||||
import { SubHeading } from '../../../../components/heading';
|
||||
import { collapsibleToggleStyles } from '../../../../lib/collapsible-toggle-styles';
|
||||
import type { ProposalFieldsFragment } from '../../proposals/__generated__/Proposals';
|
||||
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
|
||||
|
||||
@@ -13,9 +13,6 @@ export const ProposalJson = ({
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const [showDetails, setShowDetails] = useState(false);
|
||||
const showDetailsIconClasses = classnames('mb-4', {
|
||||
'rotate-180': showDetails,
|
||||
});
|
||||
|
||||
return (
|
||||
<section data-testid="proposal-json">
|
||||
@@ -25,7 +22,7 @@ export const ProposalJson = ({
|
||||
>
|
||||
<div className="flex items-center gap-3">
|
||||
<SubHeading title={t('proposalJson')} />
|
||||
<div className={showDetailsIconClasses}>
|
||||
<div className={collapsibleToggleStyles(showDetails)}>
|
||||
<Icon name="chevron-down" size={8} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
export * from './proposal-market-data';
|
||||
+222
@@ -0,0 +1,222 @@
|
||||
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>
|
||||
);
|
||||
};
|
||||
+2
-6
@@ -1,4 +1,3 @@
|
||||
import classnames from 'classnames';
|
||||
import { useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import {
|
||||
@@ -13,6 +12,7 @@ import { SubHeading } from '../../../../components/heading';
|
||||
import { useVoteInformation } from '../../hooks';
|
||||
import { useAppState } from '../../../../contexts/app-state/app-state-context';
|
||||
import { ProposalType } from '../proposal/proposal';
|
||||
import { collapsibleToggleStyles } from '../../../../lib/collapsible-toggle-styles';
|
||||
import type { ProposalFieldsFragment } from '../../proposals/__generated__/Proposals';
|
||||
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
|
||||
|
||||
@@ -57,10 +57,6 @@ export const ProposalVotesTable = ({
|
||||
? t('byTokenVote')
|
||||
: t('byLiquidityVote');
|
||||
|
||||
const showDetailsIconClasses = classnames('mb-4', {
|
||||
'rotate-180': showDetails,
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
<button
|
||||
@@ -69,7 +65,7 @@ export const ProposalVotesTable = ({
|
||||
>
|
||||
<div className="flex items-center gap-3">
|
||||
<SubHeading title={t('voteBreakdown')} />
|
||||
<div className={showDetailsIconClasses}>
|
||||
<div className={collapsibleToggleStyles(showDetails)}>
|
||||
<Icon name="chevron-down" size={8} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { MemoryRouter } from 'react-router-dom';
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import { generateProposal } from '../../test-helpers/generate-proposals';
|
||||
import { Proposal } from './proposal';
|
||||
@@ -28,9 +29,6 @@ jest.mock('../proposal-change-table', () => ({
|
||||
jest.mock('../proposal-json', () => ({
|
||||
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', () => ({
|
||||
ProposalVotesTable: () => <div data-testid="proposal-votes-table"></div>,
|
||||
}));
|
||||
@@ -41,23 +39,38 @@ jest.mock('../list-asset', () => ({
|
||||
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 () => {
|
||||
const proposal = generateProposal();
|
||||
render(
|
||||
<Proposal restData={{}} proposal={proposal as ProposalQuery['proposal']} />
|
||||
);
|
||||
renderComponent(proposal);
|
||||
|
||||
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 () => {
|
||||
const proposal = generateProposal();
|
||||
render(
|
||||
<Proposal restData={{}} proposal={proposal as ProposalQuery['proposal']} />
|
||||
);
|
||||
renderComponent(proposal);
|
||||
|
||||
expect(await screen.findByTestId('proposal-header')).toBeInTheDocument();
|
||||
expect(screen.getByTestId('proposal-change-table')).toBeInTheDocument();
|
||||
expect(screen.getByTestId('proposal-json')).toBeInTheDocument();
|
||||
expect(screen.getByTestId('proposal-terms')).toBeInTheDocument();
|
||||
expect(screen.getByTestId('proposal-votes-table')).toBeInTheDocument();
|
||||
expect(screen.getByTestId('proposal-vote-details')).toBeInTheDocument();
|
||||
expect(screen.queryByTestId('proposal-list-asset')).not.toBeInTheDocument();
|
||||
@@ -80,8 +93,7 @@ it('renders whitelist section if proposal is new asset and source is erc20', asy
|
||||
},
|
||||
},
|
||||
});
|
||||
render(
|
||||
<Proposal restData={{}} proposal={proposal as ProposalQuery['proposal']} />
|
||||
);
|
||||
renderComponent(proposal);
|
||||
|
||||
expect(screen.getByTestId('proposal-list-asset')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
@@ -1,18 +1,22 @@
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Link } from 'react-router-dom';
|
||||
import {
|
||||
NetworkParams,
|
||||
useNetworkParams,
|
||||
} from '@vegaprotocol/network-parameters';
|
||||
import { AsyncRenderer, RoundedWrapper } from '@vegaprotocol/ui-toolkit';
|
||||
import { AsyncRenderer, Icon, RoundedWrapper } from '@vegaprotocol/ui-toolkit';
|
||||
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 { ProposalChangeTable } from '../proposal-change-table';
|
||||
import { ProposalJson } from '../proposal-json';
|
||||
import { ProposalTerms } from '../proposal-terms';
|
||||
import { ProposalVotesTable } from '../proposal-votes-table';
|
||||
import { VoteDetails } from '../vote-details';
|
||||
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 {
|
||||
PROPOSAL_NEW_MARKET = 'PROPOSAL_NEW_MARKET',
|
||||
@@ -24,11 +28,17 @@ export enum ProposalType {
|
||||
}
|
||||
export interface ProposalProps {
|
||||
proposal: ProposalFieldsFragment | ProposalQuery['proposal'];
|
||||
newMarketData?: MarketInfoWithData | null;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
restData: any;
|
||||
}
|
||||
|
||||
export const Proposal = ({ proposal, restData }: ProposalProps) => {
|
||||
export const Proposal = ({
|
||||
proposal,
|
||||
restData,
|
||||
newMarketData,
|
||||
}: ProposalProps) => {
|
||||
const { t } = useTranslation();
|
||||
const { params, loading, error } = useNetworkParams([
|
||||
NetworkParams.governance_proposal_market_minVoterBalance,
|
||||
NetworkParams.governance_proposal_updateMarket_minVoterBalance,
|
||||
@@ -81,52 +91,69 @@ export const Proposal = ({ proposal, restData }: ProposalProps) => {
|
||||
return (
|
||||
<AsyncRenderer data={params} loading={loading} error={error}>
|
||||
<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} />
|
||||
|
||||
<div className="my-10">
|
||||
<ProposalChangeTable proposal={proposal} />
|
||||
</div>
|
||||
<div id="details">
|
||||
<div className="my-10">
|
||||
<ProposalChangeTable proposal={proposal} />
|
||||
</div>
|
||||
|
||||
{proposal.terms.change.__typename === 'NewAsset' &&
|
||||
proposal.terms.change.source.__typename === 'ERC20' &&
|
||||
proposal.id ? (
|
||||
<ListAsset
|
||||
assetId={proposal.id}
|
||||
withdrawalThreshold={proposal.terms.change.source.withdrawThreshold}
|
||||
lifetimeLimit={proposal.terms.change.source.lifetimeLimit}
|
||||
/>
|
||||
) : null}
|
||||
{proposal.terms.change.__typename === 'NewAsset' &&
|
||||
proposal.terms.change.source.__typename === 'ERC20' &&
|
||||
proposal.id ? (
|
||||
<ListAsset
|
||||
assetId={proposal.id}
|
||||
withdrawalThreshold={
|
||||
proposal.terms.change.source.withdrawThreshold
|
||||
}
|
||||
lifetimeLimit={proposal.terms.change.source.lifetimeLimit}
|
||||
/>
|
||||
) : null}
|
||||
|
||||
<div className="mb-4">
|
||||
<ProposalDescription description={proposal.rationale.description} />
|
||||
</div>
|
||||
<div className="mb-4">
|
||||
<ProposalDescription description={proposal.rationale.description} />
|
||||
</div>
|
||||
|
||||
{proposal.terms.change.__typename !== 'NewMarket' &&
|
||||
proposal.terms.change.__typename !== 'UpdateMarket' && (
|
||||
{newMarketData && (
|
||||
<div className="mb-4">
|
||||
<ProposalTerms data={proposal.terms} />
|
||||
<ProposalMarketData marketData={newMarketData} />
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="mb-6">
|
||||
<ProposalJson proposal={restData?.data?.proposal} />
|
||||
<div className="mb-6">
|
||||
<ProposalJson proposal={restData?.data?.proposal} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mb-10">
|
||||
<RoundedWrapper paddingBottom={true}>
|
||||
<VoteDetails
|
||||
<div id="voting">
|
||||
<div className="mb-10">
|
||||
<RoundedWrapper paddingBottom={true}>
|
||||
<VoteDetails
|
||||
proposal={proposal}
|
||||
proposalType={proposalType}
|
||||
minVoterBalance={minVoterBalance}
|
||||
spamProtectionMinTokens={
|
||||
params?.spam_protection_voting_min_tokens
|
||||
}
|
||||
/>
|
||||
</RoundedWrapper>
|
||||
</div>
|
||||
|
||||
<div className="mb-4">
|
||||
<ProposalVotesTable
|
||||
proposal={proposal}
|
||||
proposalType={proposalType}
|
||||
minVoterBalance={minVoterBalance}
|
||||
spamProtectionMinTokens={
|
||||
params?.spam_protection_voting_min_tokens
|
||||
}
|
||||
/>
|
||||
</RoundedWrapper>
|
||||
</div>
|
||||
|
||||
<div className="mb-4">
|
||||
<ProposalVotesTable proposal={proposal} proposalType={proposalType} />
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</AsyncRenderer>
|
||||
|
||||
-23
@@ -115,29 +115,6 @@ describe('Proposals list', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('Orders proposals correctly by closingDateTime', () => {
|
||||
render(
|
||||
renderComponent([
|
||||
failedProposalClosedLastMonth,
|
||||
openProposalClosesNextMonth,
|
||||
openProposalClosesNextWeek,
|
||||
enactedProposalClosedLastWeek,
|
||||
])
|
||||
);
|
||||
const openProposals = within(screen.getByTestId('open-proposals'));
|
||||
const closedProposals = within(screen.getByTestId('closed-proposals'));
|
||||
const openProposalsItems = openProposals.getAllByTestId(
|
||||
'proposals-list-item'
|
||||
);
|
||||
const closedProposalsItems = closedProposals.getAllByTestId(
|
||||
'proposals-list-item'
|
||||
);
|
||||
expect(openProposalsItems[0]).toHaveAttribute('id', 'proposal2');
|
||||
expect(openProposalsItems[1]).toHaveAttribute('id', 'proposal1');
|
||||
expect(closedProposalsItems[0]).toHaveAttribute('id', 'proposal3');
|
||||
expect(closedProposalsItems[1]).toHaveAttribute('id', 'proposal4');
|
||||
});
|
||||
|
||||
it('Displays info on no proposals', () => {
|
||||
render(renderComponent([]));
|
||||
expect(screen.queryByTestId('open-proposals')).not.toBeInTheDocument();
|
||||
|
||||
@@ -35,7 +35,11 @@ export const orderByDate = (arr: ProposalFieldsFragment[]) =>
|
||||
orderBy(
|
||||
arr,
|
||||
[
|
||||
(p) => new Date(p?.terms?.closingDatetime).getTime(),
|
||||
(p) =>
|
||||
p?.terms?.enactmentDatetime
|
||||
? new Date(p?.terms?.enactmentDatetime).getTime()
|
||||
: // has to be defaulted to 0 because new Date(null).getTime() -> NaN which is first when ordered
|
||||
new Date(p?.terms?.closingDatetime || 0).getTime(),
|
||||
(p) => new Date(p?.datetime).getTime(),
|
||||
],
|
||||
['asc', 'asc']
|
||||
|
||||
+42
-2
@@ -146,7 +146,7 @@ describe('Proposal form vote, validation and enactment deadline', () => {
|
||||
it('should show the correct datetimes', () => {
|
||||
renderComponent();
|
||||
// Should be adding 2 mins to the vote deadline as the minimum is set by
|
||||
// default, and we add 2 mins for wallet confirmation
|
||||
// default, and 2 mins are added for wallet confirmation
|
||||
expect(screen.getByTestId('voting-date')).toHaveTextContent(
|
||||
'2022-01-01T01:02:00.000Z'
|
||||
);
|
||||
@@ -224,7 +224,47 @@ describe('Proposal form vote, validation and enactment deadline', () => {
|
||||
expect(
|
||||
screen.getByTestId('enactment-before-voting-deadline')
|
||||
).toHaveTextContent(
|
||||
'Proposal will fail if enactment is earlier than the voting deadline'
|
||||
'The proposal will fail if enactment is earlier than the voting deadline'
|
||||
);
|
||||
});
|
||||
|
||||
it('displays error text if the vote deadline is set earlier than the minimum allowed', () => {
|
||||
renderComponent();
|
||||
const voteDeadlineInput = screen.getByTestId('proposal-vote-deadline');
|
||||
fireEvent.change(voteDeadlineInput, { target: { value: 0.01 } });
|
||||
expect(screen.getByTestId('voting-less-than-min')).toHaveTextContent(
|
||||
'The proposal will fail if voting deadline is below the minimum'
|
||||
);
|
||||
});
|
||||
|
||||
it('displays error text if the vote deadline is set later than the maximum allowed', () => {
|
||||
renderComponent();
|
||||
const voteDeadlineInput = screen.getByTestId('proposal-vote-deadline');
|
||||
fireEvent.change(voteDeadlineInput, { target: { value: 100000 } });
|
||||
expect(screen.getByTestId('voting-greater-than-max')).toHaveTextContent(
|
||||
'The proposal will fail if voting deadline is above the maximum'
|
||||
);
|
||||
});
|
||||
|
||||
it('displays error text if the enactment deadline is set earlier than the minimum allowed', () => {
|
||||
renderComponent();
|
||||
const enactmentDeadlineInput = screen.getByTestId(
|
||||
'proposal-enactment-deadline'
|
||||
);
|
||||
fireEvent.change(enactmentDeadlineInput, { target: { value: 0.01 } });
|
||||
expect(screen.getByTestId('enactment-less-than-min')).toHaveTextContent(
|
||||
'The proposal will fail if enactment deadline is below the minimum'
|
||||
);
|
||||
});
|
||||
|
||||
it('displays error text if the enactment deadline is set later than the maximum allowed', () => {
|
||||
renderComponent();
|
||||
const enactmentDeadlineInput = screen.getByTestId(
|
||||
'proposal-enactment-deadline'
|
||||
);
|
||||
fireEvent.change(enactmentDeadlineInput, { target: { value: 100000 } });
|
||||
expect(screen.getByTestId('enactment-greater-than-max')).toHaveTextContent(
|
||||
'The proposal will fail if enactment deadline is above the maximum'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
+62
-22
@@ -220,21 +220,41 @@ const EnactmentForm = ({
|
||||
<span data-testid="enactment-date" className="pl-2">
|
||||
{getDateTimeFormat().format(deadlineDates.enactment)}
|
||||
</span>
|
||||
{deadlines.enactment === minEnactmentHours && (
|
||||
<span
|
||||
data-testid="enactment-2-mins-extra"
|
||||
className="block mt-4 font-light"
|
||||
>
|
||||
{t('ThisWillAdd2MinutesToAllowTimeToConfirmInWallet')}
|
||||
</span>
|
||||
)}
|
||||
{deadlines.enactment && deadlines.enactment < deadlines.vote && (
|
||||
<span
|
||||
data-testid="enactment-before-voting-deadline"
|
||||
className="block mt-4 text-vega-pink"
|
||||
>
|
||||
{t('ProposalWillFailIfEnactmentIsEarlierThanVotingDeadline')}
|
||||
</span>
|
||||
{deadlines.enactment && (
|
||||
<>
|
||||
{deadlines.enactment === minEnactmentHours && (
|
||||
<span
|
||||
data-testid="enactment-2-mins-extra"
|
||||
className="block mt-4 font-light"
|
||||
>
|
||||
{t('ThisWillAdd2MinutesToAllowTimeToConfirmInWallet')}
|
||||
</span>
|
||||
)}
|
||||
{deadlines.enactment < deadlines.vote && (
|
||||
<span
|
||||
data-testid="enactment-before-voting-deadline"
|
||||
className="block mt-4 text-vega-pink"
|
||||
>
|
||||
{t('ProposalWillFailIfEnactmentIsEarlierThanVotingDeadline')}
|
||||
</span>
|
||||
)}
|
||||
{deadlines.enactment < minEnactmentHours && (
|
||||
<span
|
||||
data-testid="enactment-less-than-min"
|
||||
className="block mt-4 text-vega-pink"
|
||||
>
|
||||
{t('ProposalWillFailIfEnactmentIsBelowTheMinimumDeadline')}
|
||||
</span>
|
||||
)}
|
||||
{deadlines.enactment > maxEnactmentHours && (
|
||||
<span
|
||||
data-testid="enactment-greater-than-max"
|
||||
className="block mt-4 text-vega-pink"
|
||||
>
|
||||
{t('ProposalWillFailIfEnactmentIsAboveTheMaximumDeadline')}
|
||||
</span>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</p>
|
||||
)}
|
||||
@@ -500,13 +520,33 @@ export function ProposalFormVoteAndEnactmentDeadline({
|
||||
<span data-testid="voting-date" className="pl-2">
|
||||
{getDateTimeFormat().format(deadlineDates.vote)}
|
||||
</span>
|
||||
{deadlines.vote === minVoteHours && (
|
||||
<span
|
||||
data-testid="voting-2-mins-extra"
|
||||
className="block mt-4 font-light"
|
||||
>
|
||||
{t('ThisWillAdd2MinutesToAllowTimeToConfirmInWallet')}
|
||||
</span>
|
||||
{deadlines.vote && (
|
||||
<>
|
||||
{deadlines.vote === minVoteHours && (
|
||||
<span
|
||||
data-testid="voting-2-mins-extra"
|
||||
className="block mt-4 font-light"
|
||||
>
|
||||
{t('ThisWillAdd2MinutesToAllowTimeToConfirmInWallet')}
|
||||
</span>
|
||||
)}
|
||||
{deadlines.vote < minVoteHours && (
|
||||
<span
|
||||
data-testid="voting-less-than-min"
|
||||
className="block mt-4 text-vega-pink"
|
||||
>
|
||||
{t('ProposalWillFailIfVotingIsBelowTheMinimumDeadline')}
|
||||
</span>
|
||||
)}
|
||||
{deadlines.vote > maxVoteHours && (
|
||||
<span
|
||||
data-testid="voting-greater-than-max"
|
||||
className="block mt-4 text-vega-pink"
|
||||
>
|
||||
{t('ProposalWillFailIfVotingIsAboveTheMaximumDeadline')}
|
||||
</span>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</p>
|
||||
)}
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
import { captureMessage } from '@sentry/minimal';
|
||||
|
||||
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||
import { VoteValue } from '@vegaprotocol/types';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useUserVoteQuery } from './__generated__/Vote';
|
||||
import type { FinalizedVote } from '@vegaprotocol/proposals';
|
||||
import { removePaginationWrapper } from '@vegaprotocol/utils';
|
||||
import type { FinalizedVote } from '@vegaprotocol/proposals';
|
||||
|
||||
export enum VoteState {
|
||||
NotCast = 'NotCast',
|
||||
@@ -45,21 +44,23 @@ export function useUserVote(
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (finalizedVote?.vote.value) {
|
||||
if (finalizedVote?.vote.value && finalizedVote.pubKey === pubKey) {
|
||||
setUserVote(finalizedVote);
|
||||
} else if (data?.party?.votesConnection?.edges) {
|
||||
// This sets the vote (if any) when the user first loads the page
|
||||
setUserVote(
|
||||
removePaginationWrapper(data?.party?.votesConnection?.edges).find(
|
||||
({ proposalId: pId }) => proposalId === pId
|
||||
)
|
||||
);
|
||||
} else if (data?.party?.votesConnection?.edges && pubKey) {
|
||||
const vote = removePaginationWrapper(
|
||||
data?.party?.votesConnection?.edges
|
||||
).find(({ proposalId: pId }) => proposalId === pId);
|
||||
|
||||
if (vote) {
|
||||
setUserVote({ ...vote, pubKey });
|
||||
}
|
||||
}
|
||||
}, [
|
||||
finalizedVote?.vote.value,
|
||||
data?.party?.votesConnection?.edges,
|
||||
finalizedVote,
|
||||
proposalId,
|
||||
pubKey,
|
||||
]);
|
||||
|
||||
// If user vote changes update the vote state
|
||||
@@ -94,6 +95,8 @@ export function useUserVote(
|
||||
return {
|
||||
voteState,
|
||||
userVote,
|
||||
voteDatetime: userVote ? new Date(userVote.vote.datetime) : null,
|
||||
voteDatetime: userVote?.vote?.datetime
|
||||
? new Date(userVote.vote.datetime)
|
||||
: null,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -7,6 +7,8 @@ import { ProposalNotFound } from '../components/proposal-not-found';
|
||||
import { useProposalQuery } from './__generated__/Proposal';
|
||||
import { useFetch } from '@vegaprotocol/react-helpers';
|
||||
import { ENV } from '../../../config';
|
||||
import { useDataProvider } from '@vegaprotocol/data-provider';
|
||||
import { marketInfoWithDataProvider } from '@vegaprotocol/markets';
|
||||
|
||||
export const ProposalContainer = () => {
|
||||
const params = useParams<{ proposalId: string }>();
|
||||
@@ -20,15 +22,36 @@ export const ProposalContainer = () => {
|
||||
skip: !params.proposalId,
|
||||
});
|
||||
|
||||
const {
|
||||
data: newMarketData,
|
||||
loading: newMarketLoading,
|
||||
error: newMarketError,
|
||||
} = useDataProvider({
|
||||
dataProvider: marketInfoWithDataProvider,
|
||||
skipUpdates: true,
|
||||
variables: {
|
||||
marketId: data?.proposal?.id || '',
|
||||
skip: !data?.proposal?.id,
|
||||
},
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
const interval = setInterval(refetch, 1000);
|
||||
const interval = setInterval(refetch, 2000);
|
||||
return () => clearInterval(interval);
|
||||
}, [refetch]);
|
||||
|
||||
return (
|
||||
<AsyncRenderer loading={loading} error={error} data={data}>
|
||||
<AsyncRenderer
|
||||
loading={loading || newMarketLoading}
|
||||
error={error || newMarketError}
|
||||
data={newMarketData ? { newMarketData, data } : data}
|
||||
>
|
||||
{data?.proposal ? (
|
||||
<Proposal proposal={data.proposal} restData={restData} />
|
||||
<Proposal
|
||||
proposal={data.proposal}
|
||||
restData={restData}
|
||||
newMarketData={newMarketData}
|
||||
/>
|
||||
) : (
|
||||
<ProposalNotFound />
|
||||
)}
|
||||
|
||||
@@ -49,6 +49,7 @@ export const ProposeFreeform = () => {
|
||||
formState: { errors },
|
||||
setValue,
|
||||
watch,
|
||||
trigger,
|
||||
} = useForm<FreeformProposalFormFields>();
|
||||
const { finalizedProposal, submit, Dialog } = useProposalSubmit();
|
||||
|
||||
@@ -85,7 +86,13 @@ export const ProposeFreeform = () => {
|
||||
await submit(assembleProposal(fields));
|
||||
};
|
||||
|
||||
const viewJson = () => {
|
||||
const viewJson = async () => {
|
||||
const isValid = await trigger();
|
||||
|
||||
if (!isValid) {
|
||||
return;
|
||||
}
|
||||
|
||||
const formData = watch();
|
||||
downloadJson(
|
||||
JSON.stringify(assembleProposal(formData)),
|
||||
|
||||
+8
-1
@@ -89,6 +89,7 @@ export const ProposeNetworkParameter = () => {
|
||||
formState: { errors },
|
||||
setValue,
|
||||
watch,
|
||||
trigger,
|
||||
} = useForm<NetworkParameterProposalFormFields>();
|
||||
const { finalizedProposal, submit, Dialog } = useProposalSubmit();
|
||||
|
||||
@@ -148,7 +149,13 @@ export const ProposeNetworkParameter = () => {
|
||||
await submit(assembleProposal(fields));
|
||||
};
|
||||
|
||||
const viewJson = () => {
|
||||
const viewJson = async () => {
|
||||
const isValid = await trigger();
|
||||
|
||||
if (!isValid) {
|
||||
return;
|
||||
}
|
||||
|
||||
const formData = watch();
|
||||
downloadJson(
|
||||
JSON.stringify(assembleProposal(formData)),
|
||||
|
||||
@@ -61,6 +61,7 @@ export const ProposeNewAsset = () => {
|
||||
formState: { errors },
|
||||
setValue,
|
||||
watch,
|
||||
trigger,
|
||||
} = useForm<NewAssetProposalFormFields>();
|
||||
const { finalizedProposal, submit, Dialog } = useProposalSubmit();
|
||||
|
||||
@@ -117,7 +118,13 @@ export const ProposeNewAsset = () => {
|
||||
await submit(assembleProposal(fields));
|
||||
};
|
||||
|
||||
const viewJson = () => {
|
||||
const viewJson = async () => {
|
||||
const isValid = await trigger();
|
||||
|
||||
if (!isValid) {
|
||||
return;
|
||||
}
|
||||
|
||||
const formData = watch();
|
||||
downloadJson(
|
||||
JSON.stringify(assembleProposal(formData)),
|
||||
|
||||
@@ -59,6 +59,7 @@ export const ProposeNewMarket = () => {
|
||||
formState: { errors },
|
||||
setValue,
|
||||
watch,
|
||||
trigger,
|
||||
} = useForm<NewMarketProposalFormFields>();
|
||||
const { finalizedProposal, submit, Dialog } = useProposalSubmit();
|
||||
|
||||
@@ -107,7 +108,13 @@ export const ProposeNewMarket = () => {
|
||||
await submit(assembleProposal(fields));
|
||||
};
|
||||
|
||||
const viewJson = () => {
|
||||
const viewJson = async () => {
|
||||
const isValid = await trigger();
|
||||
|
||||
if (!isValid) {
|
||||
return;
|
||||
}
|
||||
|
||||
const formData = watch();
|
||||
downloadJson(
|
||||
JSON.stringify(assembleProposal(formData)),
|
||||
|
||||
@@ -59,6 +59,7 @@ export const ProposeUpdateAsset = () => {
|
||||
formState: { errors },
|
||||
setValue,
|
||||
watch,
|
||||
trigger,
|
||||
} = useForm<UpdateAssetProposalFormFields>();
|
||||
const { finalizedProposal, submit, Dialog } = useProposalSubmit();
|
||||
|
||||
@@ -107,7 +108,13 @@ export const ProposeUpdateAsset = () => {
|
||||
await submit(assembleProposal(fields));
|
||||
};
|
||||
|
||||
const viewJson = () => {
|
||||
const viewJson = async () => {
|
||||
const isValid = await trigger();
|
||||
|
||||
if (!isValid) {
|
||||
return;
|
||||
}
|
||||
|
||||
const formData = watch();
|
||||
downloadJson(
|
||||
JSON.stringify(assembleProposal(formData)),
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user