Compare commits

..
Author SHA1 Message Date
Edd f8cb42a38d fix: address feedback 2023-06-15 15:46:43 +01:00
Edd b7ebc7ce12 chore: make filter its own component 2023-06-14 15:24:42 +01:00
Edd 7f58fb7e23 feat(explorer): alphabetise list 2023-06-13 15:49:05 +01:00
Edd 8b0a367cca chore: minor changes 2023-06-13 15:38:51 +01:00
Edd d42786d909 feat(explorer): experimenting with submenus 2023-06-13 15:38:51 +01:00
Edd 68298d9abd chore(explorer): rearrange tx list filtering 2023-06-13 15:38:51 +01:00
Edd 94b1a82201 feat: initial dropdown for filtering transactions by type 2023-06-13 15:38:51 +01:00
Edd a475279615 chore(explorer): form filter 2023-06-13 15:38:51 +01:00
Edd 7db546c2b6 chore(explorer): regenerate types 2023-06-13 15:38:51 +01:00
250 changed files with 3916 additions and 7646 deletions
@@ -9,11 +9,6 @@ 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
@@ -32,16 +27,14 @@ 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 }}"
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
until docker pull vegaprotocol/trading:${{ github.event.release.tag_name }}; do
echo "Image not pushed yet, waiting 60 seconds"
sleep 60
done
docker run --rm vegaprotocol/trading:$commit cat /ipfs-hash > ipfs-hash
docker run --rm vegaprotocol/trading:${{ github.event.release.tag_name }} cat /ipfs-hash > ipfs-hash
curl -L https://dist.ipfs.tech/kubo/v0.20.0/kubo_v0.20.0_linux-amd64.tar.gz -o kubo.tgz
tar -xzf kubo.tgz
export PATH="$PATH:$PWD/kubo"
+21 -46
View File
@@ -6,9 +6,9 @@ on:
- release/*
- develop
- main
# uncomment pull_request and comment pull_request_target to test CI changes against feature branch not target branch (develop)
# pull_request:
pull_request_target:
tags:
- v*
pull_request:
types:
- opened
- ready_for_review
@@ -22,8 +22,6 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v3
with:
ref: ${{ github.event.pull_request.head.sha || github.sha }}
- name: Cache node modules
id: cache
@@ -64,7 +62,6 @@ jobs:
uses: actions/checkout@v3
with:
fetch-depth: 0
ref: ${{ github.event.pull_request.head.sha || github.sha }}
- name: Setup node
uses: actions/setup-node@v3
@@ -112,82 +109,60 @@ jobs:
echo "Branch slug: ${branch_slug}"
echo ">>>> eof debug"
projects_array=()
projects_e2e=""
preview_governance="not deployed"
preview_trading="not deployed"
preview_explorer="not deployed"
preview_tools="not deployed"
# parse if affected is any of three main applications, if none - use all of them
if echo "$affected" | grep -q governance; then
echo "Governance is affected"
projects_array+=("governance")
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_array+=("trading")
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_array+=("explorer")
projects_e2e+='"explorer-e2e" '
preview_explorer=$(printf "https://%s.%s.vega.rocks" "explorer" "$branch_slug")
fi
if [[ ${#projects_array[@]} -eq 0 ]]; then
projects_array=("governance" "trading" "explorer")
if [[ -z "$projects_e2e" ]]; then
projects_e2e+='"governance-e2e" "trading-e2e" "explorer-e2e" '
preview_governance=$(printf "https://%s.%s.vega.rocks" "governance" "$branch_slug")
preview_trading=$(printf "https://%s.%s.vega.rocks" "trading" "$branch_slug")
preview_explorer=$(printf "https://%s.%s.vega.rocks" "explorer" "$branch_slug")
fi
# applications parsed before this loop are applicable for running e2e-tests
projects_e2e_array=()
for project in "${projects_array[@]}"; do
projects_e2e_array+=("${project}-e2e")
done
# all applications below this loop are not applicable for running e2e-test
# check if pull request event to deploy tools
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_array+=("multisig-signer")
projects+=' "multisig-signer" '
fi
# those apps deploy only from develop to mainnet
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_array+=("multisig-signer")
projects+=' "multisig-signer" '
fi
if echo "$affected" | grep -q static; then
echo "static is affected"
echo "Static are affected"
echo "Deploying static on s3"
projects_array+=("static")
fi
if echo "$affected" | grep -q ui-toolkit; then
echo "ui-toolkit is affected"
echo "Deploying ui-toolkit on s3"
projects_array+=("ui-toolkit")
projects+=' "static" '
fi
fi
echo "Projects: ${projects_array[@]}"
echo "Projects E2E: ${projects_e2e_array[@]}"
projects_json=$(jq -M --compact-output --null-input '$ARGS.positional' --args -- "${projects_array[@]}")
projects_e2e_json=$(jq -M --compact-output --null-input '$ARGS.positional' --args -- "${projects_e2e_array[@]}")
echo PROJECTS_E2E=$projects_e2e_json >> $GITHUB_ENV
echo PROJECTS=$projects_json >> $GITHUB_ENV
projects_e2e=${projects_e2e%?}
projects_e2e=[${projects_e2e// /,}]
projects=[${projects// /,}]
echo PROJECTS_E2E=$projects_e2e >> $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
-1
View File
@@ -33,7 +33,6 @@ jobs:
with:
fetch-depth: 0
path: './frontend-monorepo'
ref: ${{ github.event.pull_request.head.sha || github.sha }}
# Restore node_modules from cache if possible
- name: Restore node_modules from cache
-2
View File
@@ -11,8 +11,6 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v3
with:
ref: ${{ github.event.pull_request.head.sha || github.sha }}
- name: Setup node
uses: actions/setup-node@v3
+63 -94
View File
@@ -19,8 +19,6 @@ jobs:
steps:
- name: Check out code
uses: actions/checkout@v3
with:
ref: ${{ github.event.pull_request.head.sha || github.sha }}
- name: Set up QEMU
id: quemu
@@ -33,7 +31,6 @@ 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
@@ -42,7 +39,7 @@ jobs:
- name: Log in to the Container registry (docker hub)
uses: docker/login-action@v2
if: ${{ matrix.app == 'trading' && github.event_name == 'push' && ( endsWith(github.ref, 'main') || endsWith(github.ref, 'release/testnet') ) }}
if: ${{ startsWith(github.ref, 'refs/tags/v') }}
with:
# registry: registry.hub.docker.com
username: ${{ secrets.DOCKERHUB_USERNAME }}
@@ -81,12 +78,10 @@ jobs:
envName="mainnet"
bucketName="static.vega.xyz"
fi
if [[ "${{ matrix.app }}" = "ui-toolkit" ]]; then
envName="mainnet"
bucketName="ui.vega.rocks"
fi
elif [[ "${{ github.ref }}" =~ .*main$ ]]; then
envName="mainnet"
elif [[ "${{ matrix.app}}" = "trading" ]] && [[ "${{ startsWith(github.ref, 'refs/tags/v') && 'true' || 'false' }}" = "true" ]]; then
envName="mainnet"
fi
if [[ "${envName}" = "mainnet" ]]; then
@@ -115,15 +110,14 @@ jobs:
run: |
flags=""
if [[ ! -z "${{ env.ENV_NAME }}" ]]; then
flags="--env=${{ env.ENV_NAME }}"
if [[ "${{ env.ENV_NAME }}" != "ops-vega" ]]; then
flags="--env=${{ env.ENV_NAME }}"
fi
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 }}
@@ -158,9 +152,7 @@ jobs:
- name: Publish dist as docker image (ghcr)
uses: docker/build-push-action@v3
continue-on-error: true
id: ghcr-push
if: ${{ github.event_name == 'pull_request' }}
if: ${{ github.event_name == 'pull_request' || (matrix.app == 'trading' && github.event_name == 'push' && !startsWith(github.ref, 'refs/tags/v') ) }}
with:
context: .
file: docker/node-outside-docker.Dockerfile
@@ -173,9 +165,7 @@ jobs:
- name: Publish dist as docker image (docker hub)
uses: docker/build-push-action@v3
continue-on-error: true
id: dockerhub-push
if: ${{ matrix.app == 'trading' && github.event_name == 'push' && ( endsWith(github.ref, 'main') || endsWith(github.ref, 'release/testnet') ) }}
if: ${{ matrix.app == 'trading' && github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v') }}
with:
context: .
file: docker/node-outside-docker.Dockerfile
@@ -184,41 +174,13 @@ jobs:
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' || '' }}
- 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' || '' }}
vegaprotocol/${{ matrix.app }}:${{ github.ref_name }}
vegaprotocol/${{ matrix.app }}:mainnet
# bucket creation in github.com/vegaprotocol/terraform//frontend
- name: Publish dist to s3
uses: jakejarvis/s3-sync-action@master
# 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') ) ) ) }}
if: ${{ github.event_name == 'push' && !startsWith(github.ref, 'refs/tags/v') && ( matrix.app != 'trading' || (matrix.app == 'trading' && !endsWith(github.ref, 'main') ) ) }}
with:
args: --acl private --follow-symlinks --delete
env:
@@ -236,38 +198,22 @@ jobs:
number: ${{ github.event.number }}
- name: Trigger fleek deployment
# release to ipfs happens only on mainnet (represented by main branch) for trading
if: ${{ matrix.app == 'trading' && github.event_name == 'push' && ( endsWith(github.ref, 'main') || endsWith(github.ref, 'release/testnet') ) }}
if: ${{ matrix.app == 'trading' && github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v') }}
run: |
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
# display info about app
curl -H "Authorization: ${{ secrets.FLEEK_API_KEY }}" \
-H "Content-Type: application/json" \
-d '{"query": "query{getSiteById(siteId:\"f8f2e051-f18e-49e6-b876-0a39369dc0d8\"){id latestDeploy{id status}}}"}' \
https://api.fleek.co/graphql
# trigger new deployment as base image is always set to vegaprotocol/trading:mainnet
curl --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
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
# trigger new deployment as base image is always set to vegaprotocol/trading:mainnet
curl -H "Authorization: ${{ secrets.FLEEK_API_KEY }}" \
-H "Content-Type: application/json" \
-d '{"query": "mutation{triggerDeploy(siteId:\"f8f2e051-f18e-49e6-b876-0a39369dc0d8\"){id status}}"}' \
https://api.fleek.co/graphql
- name: Check out ipfs-redirect
if: ${{ matrix.app == 'trading' && github.event_name == 'push' && ( endsWith(github.ref, 'main') || endsWith(github.ref, 'testnet') ) }}
if: ${{ matrix.app == 'trading' && github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v') }}
uses: actions/checkout@v3
with:
repository: 'vegaprotocol/ipfs-redirect'
@@ -275,12 +221,11 @@ jobs:
fetch-depth: '0'
token: ${{ secrets.VEGA_CI_BOT_GITHUB_TOKEN }}
- name: Update interstitial page to point to the new console
if: ${{ matrix.app == 'trading' && github.event_name == 'push' && ( endsWith(github.ref, 'main') || endsWith(github.ref, 'testnet') ) }}
- name: Update console.vega.xyz DNS to redirect to the new console
if: ${{ matrix.app == 'trading' && github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v') }}
env:
GH_TOKEN: ${{ secrets.VEGA_CI_BOT_GITHUB_TOKEN }}
run: |
# set CID
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"
@@ -288,28 +233,52 @@ jobs:
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
# configure git
git status
cat .git/config
git config --global user.email "vega-ci-bot@vega.xyz"
git config --global user.name "vega-ci-bot"
# update CID files
if echo ${{ github.ref }} | grep -q main; then
echo $new_hash > cidv0-mainnet.txt
echo $new_cid > cidv1-mainnet.txt
git add cidv0-mainnet.txt cidv1-mainnet.txt
elif echo ${{ github.ref }} | grep -q release/testnet; then
echo $new_hash > cidv0-fairground.txt
echo $new_cid > cidv1-fairground.txt
git add cidv0-fairground.txt cidv1-fairground.txt
fi
# create commit
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 "main"
git push -u origin "$branch_name"
pr_url="$(gh pr create --title "${commit_msg}" --body 'automated pull request to update CIDs')"
echo $pr_url
# once auto merge get's enabled on documentation repo let's do follow up
sleep 5
gh pr merge "${pr_url}" --delete-branch --squash --admin
)
# # Generate console URL
# new_console_url_type=ipfs
# # new_console_url_type=ipns
# new_console_url_domain=cf-ipfs.com
# # new_console_url_domain=dweb.link
# new_console_url="https://${new_cid}.${new_console_url_type}.${new_console_url_domain}/"
# echo "new_console_url=${new_console_url}"
# # Update record in DNSimple
# # docs: https://developer.dnsimple.com/v2/zones/records/#updateZoneRecord
# dnsimple_account_id=84895
# dnsimple_zone_name=console.vega.xyz
# dnsimple_record_id=44409591
# # see: https://dnsimple.com/a/84895/domains/console.vega.xyz/records/44409591/edit
# curl -H 'Authorization: Bearer ${{ secrets.DNSIMPLE_API_TOKEN }}' \
# -H 'Accept: application/json' \
# -H 'Content-Type: application/json' \
# -X PATCH \
# -d "{
# \"content\": \"${new_console_url}\"
# }" \
# https://api.dnsimple.com/v2/${dnsimple_account_id}/zones/${dnsimple_zone_name}/records/${dnsimple_record_id}
@@ -77,7 +77,7 @@ export const MarketsTable = ({ data }: MarketsTableProps) => {
hide={window.innerWidth <= BREAKPOINT_MD}
valueGetter={({
data,
}: VegaValueGetterParams<MarketFieldsFragment>) => {
}: VegaValueGetterParams<MarketFieldsFragment, 'state'>) => {
return data?.state ? MarketStateMapping[data?.state] : '-';
}}
/>
+1 -1
View File
@@ -49,7 +49,7 @@ module.exports = defineConfig({
vegaTokenContractAddress: '0xF41bD86d462D36b997C0bbb4D97a0a3382f205B7',
vegaTokenAddress: '0x67175Da1D5e966e40D11c4B2519392B2058373de',
txTimeout: { timeout: 70000 },
epochTimeout: { timeout: 12000 },
epochTimeout: { timeout: 6000 },
blockConfirmations: 3,
grepTags: '@regression @smoke @slow',
grepFilterSpecs: true,
@@ -1,63 +0,0 @@
export const previousEpochData = {
epoch: {
id: '7611',
validatorsConnection: {
edges: [
{
node: {
id: 'cd96782bc0ad5679869cf69fe7838a92212da7f53b4a214bed68067117494122',
stakedTotal: '3154229668720612941799',
rewardScore: {
rawValidatorScore: '0.2',
performanceScore: '1',
multisigScore: '0',
validatorScore: '0.2',
normalisedScore: '0.2007216887087119',
validatorStatus: 'VALIDATOR_NODE_STATUS_TENDERMINT',
__typename: 'RewardScore',
},
rankingScore: {
status: 'VALIDATOR_NODE_STATUS_TENDERMINT',
previousStatus: 'VALIDATOR_NODE_STATUS_TENDERMINT',
rankingScore: '0.211713765544955625',
stakeScore: '0.2016321576618625',
performanceScore: '1',
votingPower: '2007',
__typename: 'RankingScore',
},
__typename: 'Node',
},
__typename: 'NodeEdge',
},
{
node: {
id: '887d936f797a47032eceb572a13b69b581d3b1fa595a7d021a3e3cf2a5d2acfd',
stakedTotal: '3151161904761904764551',
rewardScore: {
rawValidatorScore: '0.2',
performanceScore: '1',
multisigScore: '1',
validatorScore: '0.2',
normalisedScore: '0.2007216887087119',
validatorStatus: 'VALIDATOR_NODE_STATUS_TENDERMINT',
__typename: 'RewardScore',
},
rankingScore: {
status: 'VALIDATOR_NODE_STATUS_TENDERMINT',
previousStatus: 'VALIDATOR_NODE_STATUS_TENDERMINT',
rankingScore: '0.211507855409133255',
stakeScore: '0.2014360527706031',
performanceScore: '1',
votingPower: '2007',
__typename: 'RankingScore',
},
__typename: 'Node',
},
__typename: 'NodeEdge',
},
],
__typename: 'NodesConnection',
},
__typename: 'Epoch',
},
};
@@ -14,16 +14,9 @@ import {
submitUniqueRawProposal,
voteForProposal,
} from '../../../../governance-e2e/src/support/governance.functions';
import {
ensureSpecifiedUnstakedTokensAreAssociated,
stakingPageAssociateTokens,
stakingPageDisassociateAllTokens,
} from '../../../../governance-e2e/src/support/staking.functions';
import { ensureSpecifiedUnstakedTokensAreAssociated } from '../../../../governance-e2e/src/support/staking.functions';
import { ethereumWalletConnect } from '../../../../governance-e2e/src/support/wallet-eth.functions';
import {
switchVegaWalletPubKey,
vegaWalletSetSpecifiedApprovalAmount,
} from '../../support/wallet-functions';
import { vegaWalletSetSpecifiedApprovalAmount } from '../../../../governance-e2e/src/support/wallet-teardown.functions';
import type { testFreeformProposal } from '../../support/common-interfaces';
import { formatDateWithLocalTimezone } from '@vegaprotocol/utils';
@@ -51,7 +44,7 @@ describe(
before('connect wallets and set approval limit', function () {
cy.visit('/');
ethereumWalletConnect();
// cy.associateTokensToVegaWallet('1');
cy.associateTokensToVegaWallet('1');
});
beforeEach('visit proposals tab', function () {
@@ -304,33 +297,5 @@ 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();
});
}
);
@@ -16,7 +16,7 @@ import {
} from '../../support/proposal.functions';
import { ensureSpecifiedUnstakedTokensAreAssociated } from '../../support/staking.functions';
import { ethereumWalletConnect } from '../../support/wallet-eth.functions';
import { vegaWalletSetSpecifiedApprovalAmount } from '../../support/wallet-functions';
import { vegaWalletSetSpecifiedApprovalAmount } from '../../support/wallet-teardown.functions';
const proposalListItem = '[data-testid="proposals-list-item"]';
const closedProposals = '[data-testid="closed-proposals"]';
@@ -89,14 +89,8 @@ context(
});
cy.get(proposalStatus).should('have.text', 'Open');
voteForProposal('for');
cy.get(proposalStatus, proposalTimeout)
.should('have.text', 'Passed')
.then(() => {
cy.get(proposalStatus, proposalTimeout).should(
'have.text',
'Enacted'
);
});
cy.get(proposalStatus, proposalTimeout).should('have.text', 'Passed');
cy.get(proposalStatus, proposalTimeout).should('have.text', 'Enacted');
cy.get(votesTable).within(() => {
cy.contains('Vote passed.').should('be.visible');
cy.contains('Voting has ended.').should('be.visible');
@@ -32,7 +32,7 @@ import {
import {
vegaWalletSetSpecifiedApprovalAmount,
vegaWalletTeardown,
} from '../../support/wallet-functions';
} from '../../support/wallet-teardown.functions';
import { ethereumWalletConnect } from '../../support/wallet-eth.functions';
import type { testFreeformProposal } from '../../support/common-interfaces';
@@ -130,7 +130,7 @@ context(
createRawProposal();
});
it('Creating a proposal - proposal rejected - when closing time sooner than system default', function () {
it.skip('Creating a proposal - proposal rejected - when closing time sooner than system default', function () {
goToMakeNewProposal(governanceProposalType.FREEFORM);
enterUniqueFreeFormProposalBody('0.1', generateFreeFormProposalTitle());
cy.get('input:invalid')
@@ -138,7 +138,7 @@ context(
.should('equal', 'Value must be greater than or equal to 1.');
});
it('Creating a proposal - proposal rejected - when closing time later than system default', function () {
it.skip('Creating a proposal - proposal rejected - when closing time later than system default', function () {
goToMakeNewProposal(governanceProposalType.FREEFORM);
enterUniqueFreeFormProposalBody(
'100000',
@@ -8,7 +8,6 @@ import {
} from '../../support/common.functions';
import {
getDownloadedProposalJsonPath,
getProposalFromTitle,
submitUniqueRawProposal,
} from '../../support/governance.functions';
import {
@@ -24,11 +23,10 @@ import {
} from '../../support/staking.functions';
import { ethereumWalletConnect } from '../../support/wallet-eth.functions';
import {
switchVegaWalletPubKey,
vegaWalletFaucetAssetsWithoutCheck,
vegaWalletSetSpecifiedApprovalAmount,
vegaWalletTeardown,
} from '../../support/wallet-functions';
} from '../../support/wallet-teardown.functions';
import { vegaWalletFaucetAssetsWithoutCheck } from '../../support/wallet-vega.functions';
const proposalListItem = '[data-testid="proposals-list-item"]';
const openProposals = '[data-testid="open-proposals"]';
@@ -56,8 +54,7 @@ const feedbackError = '[data-testid="Error"]';
const viewProposalBtn = 'view-proposal-btn';
const liquidityVoteStatus = 'liquidity-votes-status';
const tokenVoteStatus = 'token-votes-status';
const proposalJsonToggle = 'proposal-json-toggle';
const proposalJsonSection = 'proposal-json';
const proposalTermsSection = 'proposal';
const vegaWalletPublicKey = Cypress.env('vegaWalletPublicKey');
const fUSDCId =
'816af99af60d684502a40824758f6b5377e6af48e50a9ee8ef478ecb879ea8bc';
@@ -188,7 +185,7 @@ context(
cy.get(maxVoteDeadline).click();
cy.get(enactmentDeadlineError).should(
'have.text',
'The proposal will fail if enactment is earlier than the voting deadline'
'Proposal will fail if enactment is earlier than the voting deadline'
);
cy.get(proposalDownloadBtn)
.should('be.visible')
@@ -208,51 +205,29 @@ context(
});
// 3003-PMAN-001
it(
'Able to submit valid new market proposal',
{ tags: '@smoke' },
function () {
const proposalTitle = 'Test new market proposal';
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', 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(proposalDownloadBtn)
.should('be.visible')
.click()
.then(() => {
cy.wrap(
getDownloadedProposalJsonPath('vega-new-market-proposal-')
).then((filePath) => {
goToMakeNewProposal(governanceProposalType.RAW);
submitUniqueRawProposal({ proposalBody: filePath }); // 3003-PMAN-003
});
});
cy.get(proposalDownloadBtn)
.should('be.visible')
.click()
.then(() => {
cy.wrap(
getDownloadedProposalJsonPath('vega-new-market-proposal-')
).then((filePath) => {
goToMakeNewProposal(governanceProposalType.RAW);
submitUniqueRawProposal({ proposalBody: filePath }); // 3003-PMAN-003
});
});
navigateTo(navigation.proposals);
getProposalFromTitle(proposalTitle).within(() =>
cy.getByTestId('view-proposal-btn').click()
);
cy.getByTestId('proposal-market-data').within(() => {
cy.getByTestId('proposal-market-data-toggle').click();
cy.contains('Key details').click();
getMarketProposalDetailsFromTable('Name').should(
'have.text',
'Token test market'
);
cy.contains('Settlement asset').click();
// Settlement asset symbol
cy.getByTestId('3_value').should('have.text', 'fBTC');
cy.contains('Oracle').click();
cy.getByTestId('oracle-spec-links').should('have.attr', 'href');
});
}
);
});
it('Unable to submit new market proposal with missing/invalid fields', function () {
const errorMsg =
@@ -290,7 +265,8 @@ context(
// 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();
cy.get('[data-testid="manage-vega-wallet"]:visible').click();
cy.get('[data-testid="select-keypair-button"]').eq(0).click(); // switch to second wallet pub key
stakingPageAssociateTokens('1');
goToMakeNewProposal(governanceProposalType.UPDATE_MARKET);
cy.get(newProposalTitle).type('Test update market proposal - rejected');
@@ -320,7 +296,8 @@ context(
closeDialog();
ethereumWalletConnect();
stakingPageDisassociateAllTokens();
switchVegaWalletPubKey();
cy.get('[data-testid="manage-vega-wallet"]:visible').click();
cy.get('[data-testid="select-keypair-button"]').eq(0).click();
});
// 3002-PROP-020
@@ -474,8 +451,8 @@ context(
.within(() => {
cy.getByTestId(viewProposalBtn).click();
});
cy.getByTestId(proposalJsonToggle).click();
cy.getByTestId(proposalJsonSection).within(() => {
cy.getByTestId('proposal-terms-toggle').click();
cy.getByTestId(proposalTermsSection).within(() => {
cy.contains('USDT Coin').should('be.visible');
cy.contains('USDT').should('be.visible');
});
@@ -521,11 +498,13 @@ context(
.invoke('text')
.should('not.be.empty');
// 3001-VOTE-030 3001-VOTE-031
cy.getByTestId(proposalJsonToggle).click();
cy.getByTestId(proposalJsonSection).within(() => {
cy.contains(assetId).should('be.visible');
cy.contains('lifetimeLimit').should('be.visible');
cy.contains('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'
);
});
});
@@ -623,13 +602,5 @@ context(
});
});
}
function getMarketProposalDetailsFromTable(heading: string) {
return cy
.getByTestId('key-value-table-row')
.contains(heading)
.parent()
.siblings();
}
}
);
@@ -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-functions';
import { vegaWalletSetSpecifiedApprovalAmount } from '../../support/wallet-teardown.functions';
const proposalListItem = 'proposals-list-item';
const openProposals = '[data-testid="open-proposals"]';
@@ -14,7 +14,7 @@ import { ethereumWalletConnect } from '../../support/wallet-eth.functions';
import {
depositAsset,
vegaWalletTeardown,
} from '../../support/wallet-functions';
} from '../../support/wallet-teardown.functions';
const vegaAssetAddress = '0x67175Da1D5e966e40D11c4B2519392B2058373de';
const vegaWalletUnstakedBalance =
@@ -25,7 +25,7 @@ import { ethereumWalletConnect } from '../../support/wallet-eth.functions';
import {
vegaWalletSetSpecifiedApprovalAmount,
vegaWalletTeardown,
} from '../../support/wallet-functions';
} from '../../support/wallet-teardown.functions';
const stakeValidatorListTotalStake = 'total-stake';
const stakeValidatorListTotalShare = 'total-stake-share';
const stakeValidatorListStakePercentage = 'stake-percentage';
@@ -54,14 +54,16 @@ context(
'Staking Tab - with eth and vega wallets connected',
{ tags: '@slow' },
function () {
// 1002-STKE-002, 1002-STKE-032
// 2001-STKE-002, 2001-STKE-032
before('visit staking tab and connect vega wallet', function () {
cy.visit('/');
ethereumWalletConnect();
// this is a workaround for #2422 which can be removed once issue is resolved
cy.associateTokensToVegaWallet('4');
vegaWalletSetSpecifiedApprovalAmount('1000');
});
describe('Eth wallet - contains VEGA tokens', function () {
describe.skip('Eth wallet - contains VEGA tokens', function () {
beforeEach(
'teardown wallet & drill into a specific validator',
function () {
@@ -75,41 +77,25 @@ 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();
// 1002-STKE-031
// 2001-STKE-031
clickOnValidatorFromList(0);
// 1002-STKE-033, 1002-STKE-034, 1002-STKE-037
// 2001-STKE-033, 2001-STKE-034, 2001-STKE-037
stakingValidatorPageAddStake('2');
verifyUnstakedBalance(1.0);
// 1002-STKE-039
// 2001-STKE-039
verifyStakedBalance(2.0);
verifyNextEpochValue(2.0); // 1002-STKE-016 1002-STKE-038
verifyThisEpochValue(2.0); // 1002-STKE-013
verifyNextEpochValue(2.0); // 2001-STKE-016 2001-STKE-038
verifyThisEpochValue(2.0); // 2001-STKE-013
closeStakingDialog();
navigateTo(navigation.validators);
// 2002-SINC-007 1002-STKE-015 1002-STKE-017 1002-STKE-052
// 2002-SINC-007
validateValidatorListTotalStakeAndShare('0', '3,002.00', '50.02%');
});
@@ -232,7 +218,7 @@ context(
});
});
// 1002-STKE-041 1002-STKE-053
// 2001-STKE-041
it(
'Able to remove part of a stake against a validator',
{ tags: '@smoke' },
@@ -245,11 +231,11 @@ context(
verifyUnstakedBalance(1.0);
closeStakingDialog();
navigateTo(navigation.validators);
// 1002-STKE-040
// 2001-STKE-040
clickOnValidatorFromList(0);
// 1002-STKE-044, 1002-STKE-048
// 2001-STKE-044, 2001-STKE-048
stakingValidatorPageRemoveStake('1');
// 1002-STKE-049
// 2001-STKE-049
verifyNextEpochValue(2.0);
verifyUnstakedBalance(2.0);
verifyStakedBalance(2.0);
@@ -269,7 +255,7 @@ context(
}
);
// 1002-STKE-045
// 2001-STKE-045
it('Able to remove a full stake against a validator', function () {
stakingPageAssociateTokens('3');
verifyUnstakedBalance(3.0);
@@ -317,7 +303,6 @@ 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);
@@ -396,8 +381,6 @@ 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();
@@ -420,7 +403,7 @@ context(
});
it('Associating wallet tokens - when some already staked - auto stakes tokens to staked validator', function () {
// 1002-STKE-004
// 2001-STKE-004
stakingPageAssociateTokens('3');
verifyUnstakedBalance(3.0);
cy.get('button').contains('Select a validator to nominate').click();
@@ -434,7 +417,7 @@ context(
});
it('Associating vesting contract tokens - when some already staked - auto stakes tokens to staked validator', function () {
// 1002-STKE-004
// 2001-STKE-004
stakingPageAssociateTokens('3', { type: 'contract' });
verifyUnstakedBalance(3.0);
cy.get('button').contains('Select a validator to nominate').click();
@@ -448,7 +431,7 @@ context(
});
it('Associating vesting contract tokens - when wallet tokens already staked - auto stakes tokens to staked validator', function () {
// 1002-STKE-004
// 2001-STKE-004
stakingPageAssociateTokens('3', { type: 'wallet' });
verifyUnstakedBalance(3.0);
cy.get('button').contains('Select a validator to nominate').click();
@@ -462,7 +445,7 @@ context(
});
it('Associating tokens - with multiple validators already staked - auto stakes to staked validators - abiding by existing stake ratio', function () {
// 1002-STKE-004
// 2001-STKE-004
stakingPageAssociateTokens('6');
verifyUnstakedBalance(6.0);
cy.get('button').contains('Select a validator to nominate').click();
@@ -515,7 +498,7 @@ context(
}
function verifyThisEpochValue(amount: number) {
cy.getByTestId('stake-this-epoch', epochTimeout) // 1002-STKE-013
cy.getByTestId('stake-this-epoch', epochTimeout) // 2001-STKE-013
.contains(amount, epochTimeout)
.should('be.visible');
}
@@ -14,12 +14,11 @@ import {
} from '../../support/staking.functions';
import { ethereumWalletConnect } from '../../support/wallet-eth.functions';
import {
switchVegaWalletPubKey,
vegaWalletAssociate,
vegaWalletDisassociate,
vegaWalletSetSpecifiedApprovalAmount,
vegaWalletTeardown,
} from '../../support/wallet-functions';
} from '../../support/wallet-teardown.functions';
const ethWalletContainer = '[data-testid="ethereum-wallet"]';
const vegaWalletAssociatedBalance = '[data-testid="currency-value"]';
@@ -65,38 +64,31 @@ context(
}
);
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);
}
);
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 });
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);
});
it('Able to disassociate all associated tokens - manually', function () {
// 1004-ASSO-025
@@ -302,7 +294,8 @@ context(
Cypress.env('vegaWalletPublicKey')
);
switchVegaWalletPubKey();
cy.get('[data-testid="manage-vega-wallet"]:visible').click();
cy.get('[data-testid="select-keypair-button"]').eq(0).click();
cy.get(connectedVegaKey).should(
'have.text',
Cypress.env('vegaWalletPublicKey2')
@@ -5,7 +5,7 @@ import {
waitForSpinner,
} from '../../support/common.functions';
import { ethereumWalletConnect } from '../../support/wallet-eth.functions';
import { depositAsset } from '../../support/wallet-functions';
import { depositAsset } from '../../support/wallet-teardown.functions';
const withdraw = 'withdraw';
const withdrawalForm = 'withdraw-form';
@@ -55,10 +55,6 @@ 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,10 +69,7 @@ context(
it('Unable to submit withdrawal with invalid fields', function () {
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.get('select').select(usdtSelectValue, { force: true });
cy.getByTestId(balanceAvailable, txTimeout).should('exist');
cy.getByTestId(submitWithdrawalButton).click();
cy.getByTestId(formValidationError).should('have.length', 1);
@@ -96,75 +89,68 @@ context(
});
});
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'
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();
});
// 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(delayTime).should('have.text', 'None');
cy.getByTestId(amountInput).click().type('120');
cy.getByTestId(submitWithdrawalButton).click();
cy.getByTestId(toastCompleteWithdrawal).click();
cy.getByTestId(toastClose).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/');
});
}
);
// 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');
@@ -173,10 +159,7 @@ context(
// 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.get('select').select(usdtSelectValue, { force: true });
cy.getByTestId(ethAddressInput).should('be.empty');
cy.getByTestId(amountInput).click().type('110');
cy.getByTestId(submitWithdrawalButton).click();
@@ -236,10 +219,7 @@ context(
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.get('select').select(usdtSelectValue, { force: true });
cy.getByTestId(balanceAvailable, txTimeout).should('exist');
cy.getByTestId(withdrawalThreshold).should(
'have.text',
@@ -294,10 +274,7 @@ context(
cy.connectPublicKey(vegaWalletPubKey);
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.get('select').select(usdtSelectValue, { force: true });
cy.getByTestId(balanceAvailable, txTimeout).should('exist');
cy.getByTestId(amountInput).click().type('100');
cy.pause();
@@ -11,7 +11,7 @@ import {
goToMakeNewProposal,
governanceProposalType,
} from '../../support/governance.functions';
import { vegaWalletFaucetAssetsWithoutCheck } from '../../support/wallet-functions';
import { vegaWalletFaucetAssetsWithoutCheck } from '../../support/wallet-vega.functions';
const vegaWalletPubKey = Cypress.env('vegaWalletPublicKey2');
const vegaPubkeyTruncated = Cypress.env('vegaWalletPublicKey2Short');
@@ -1,16 +1,11 @@
/// <reference types="cypress" />
import { aliasGQLQuery } from '@vegaprotocol/cypress';
import {
navigation,
verifyPageHeader,
verifyTabHighlighted,
} from '../../support/common.functions';
import {
clickOnValidatorFromList,
waitForBeginningOfEpoch,
} from '../../support/staking.functions';
import { previousEpochData } from '../../fixtures/mocks/previous-epoch';
import { clickOnValidatorFromList } from '../../support/staking.functions';
const guideLink = '[data-testid="staking-guide-link"]';
const validatorTitle = '[data-testid="validator-node-title"]';
@@ -32,7 +27,7 @@ const normalisedVotingPowerToolTip =
'[data-testid="normalised-voting-power-tooltip"]';
const performancePenaltyToolTip = '[data-testid="performance-penalty-tooltip"]';
const overstakedPenaltyToolTip = '[data-testid="overstaked-penalty-tooltip"]';
const multisigPenaltyToolTip = '[data-testid="multisig-error-tooltip"]';
const totalPenaltyToolTip = '[data-testid="total-penalty-tooltip"]';
const epochCountDown = '[data-testid="epoch-countdown"]';
const stakeNumberRegex = /^\d{1,3}(,\d{3})*(\.\d+)?$/;
@@ -42,226 +37,206 @@ context('Validators Page - verify elements on page', function () {
});
describe('with wallets disconnected', { tags: '@smoke' }, function () {
it('Should have validators tab highlighted', function () {
verifyTabHighlighted(navigation.validators);
});
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 validators ON VEGA header visible', function () {
verifyPageHeader('Validators');
});
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'
);
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'
);
});
});
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');
});
});
// 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');
});
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 () {
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 () {
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-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
});
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');
});
});
it('Should be able to see multisig error', function () {
cy.mockGQL((req) => {
aliasGQLQuery(req, 'PreviousEpoch', previousEpochData);
});
waitForBeginningOfEpoch();
cy.getByTestId('total-penalty').first().realHover();
cy.get(multisigPenaltyToolTip)
.invoke('text')
.should('contain', 'Multisig penalty: 100%');
cy.getByTestId('total-penalty').eq(1).realHover();
cy.get(multisigPenaltyToolTip)
.invoke('text')
.should('contain', 'Multisig penalty: 100%');
});
}
);
// 1002-STKE-050
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,9 +1,7 @@
import { truncateByChars } from '@vegaprotocol/utils';
import { waitForSpinner } from '../../support/common.functions';
import {
vegaWalletFaucetAssetsWithoutCheck,
vegaWalletTeardown,
} from '../../support/wallet-functions';
import { vegaWalletTeardown } from '../../support/wallet-teardown.functions';
import { vegaWalletFaucetAssetsWithoutCheck } from '../../support/wallet-vega.functions';
const walletContainer = 'aside [data-testid="vega-wallet"]';
const walletHeader = '[data-testid="wallet-header"] h1';
@@ -287,28 +285,28 @@ context(
name: 'USDC (fake)',
symbol: 'fUSDC',
amount: '1000000',
expectedAmount: 10.0,
expectedAmount: '10.00',
},
{
id: '8566db7257222b5b7ef2886394ad28b938b28680a54a169bbc795027b89d6665',
name: 'DAI (fake)',
symbol: 'fDAI',
amount: '200000',
expectedAmount: 2.0,
expectedAmount: '2.00',
},
{
id: '73174a6fb1d5802ba0ac7bd7ab79e0a3a4837b262de0a4e80815a55442692bd0',
name: 'BTC (fake)',
symbol: 'fBTC',
amount: '600000',
expectedAmount: 6.0,
expectedAmount: '6.00',
},
{
id: 'e02d4c15d790d1d2dffaf2dcd1cf06a1fe656656cf4ed18c8ce99f9e83643567',
name: 'EURO (fake)',
symbol: 'fEURO',
amount: '800000',
expectedAmount: 8.0,
expectedAmount: '8.00',
},
];
@@ -319,6 +317,12 @@ context(
cy.reload();
waitForSpinner();
cy.connectVegaWallet();
cy.get(walletContainer).within(() => {
cy.getByTestId('currency-title', txTimeout).should(
'have.length.at.least',
5
);
});
});
for (const { name, symbol, expectedAmount } of assets) {
@@ -332,10 +336,9 @@ context(
.contains(name)
.parent()
.siblings()
.then((elementAmount) => {
const displayedAmount = parseFloat(elementAmount.text());
expect(displayedAmount).be.gte(expectedAmount);
});
.invoke('text')
.then(parseFloat)
.should('be.gte', parseFloat(expectedAmount));
cy.get(vegaWalletCurrencyTitle)
.contains(name)
@@ -37,7 +37,7 @@ export function navigateTo(page: navigation) {
});
} else {
return cy.get(navigation.section, { timeout: 10000 }).within(() => {
cy.get(page).eq(0).click({ force: true });
cy.get(page).eq(0).click();
});
}
}
+2 -1
View File
@@ -5,7 +5,8 @@ import './common.functions.ts';
import './staking.functions.ts';
import './governance.functions.ts';
import './wallet-eth.functions.ts';
import './wallet-functions.ts';
import './wallet-teardown.functions.ts';
import './wallet-vega.functions.ts';
import './proposal.functions.ts';
import 'cypress-mochawesome-reporter/register';
import registerCypressGrep from '@cypress/grep';
@@ -1,5 +1,5 @@
import { closeDialog } from './common.functions';
import { vegaWalletTeardown } from './wallet-functions';
import { vegaWalletTeardown } from './wallet-teardown.functions';
const tokenAmountInputBox = '[data-testid="token-amount-input"]';
const tokenSubmitButton = '[data-testid="token-input-submit-button"]';
@@ -173,27 +173,3 @@ 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();
}
@@ -0,0 +1,18 @@
export function vegaWalletFaucetAssetsWithoutCheck(
asset: string,
amount: string,
vegaWalletPublicKey: string
) {
cy.highlight(`Topping up vega wallet with ${asset}, amount: ${amount}`);
cy.exec(
`curl -X POST -d '{"amount": "${amount}", "asset": "${asset}", "party": "${vegaWalletPublicKey}"}' http://localhost:1790/api/v1/mint`
)
.its('stdout')
.then((response) => {
assert.include(
response,
`"success":true`,
'Ensuring curl command was successfully undertaken'
);
});
}
+2 -11
View File
@@ -596,8 +596,6 @@
"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",
@@ -731,11 +729,7 @@
"ThisWillSetValidationDeadlineTo": "This will set the validation deadline to",
"Hours": "hours",
"ThisWillAdd2MinutesToAllowTimeToConfirmInWallet": "Note: 2 minutes of extra time are added when you choose the minimum value. This gives you time to confirm the proposal in your wallet.",
"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",
"ProposalWillFailIfEnactmentIsEarlierThanVotingDeadline": "Proposal will fail if enactment is earlier than the voting deadline",
"SelectAMarketToChange": "Select a market to change",
"MarketName": "Market name",
"MarketCode": "Market code",
@@ -786,7 +780,6 @@
"performancePenalty": "Performance penalty",
"overstaked": "Overstaked",
"overstakedPenalty": "Overstaked penalty",
"multisigPenalty": "Multisig penalty",
"homeProposalsIntro": "Decisions on the Vega network are on-chain, with tokenholders creating proposals that other tokenholders vote to approve or reject. Network upgrades are proposed and approved by validators.",
"homeProposalsButtonText": "Browse, vote, and propose",
"homeValidatorsIntro": "Vega runs on a delegated proof of stake blockchain, where validators earn fees for validating block transactions. Tokenholders can nominate validators by staking tokens to them.",
@@ -831,7 +824,5 @@
"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"
"learnMore": "Learn more"
}
@@ -1,6 +0,0 @@
import classnames from 'classnames';
export const collapsibleToggleStyles = (toggleState: boolean) =>
classnames('mb-4 transition-transform ease-in-out duration-300', {
'rotate-180': toggleState,
});
@@ -1,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,6 +12,9 @@ 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">
@@ -21,7 +24,7 @@ export const ProposalDescription = ({
>
<div className="flex items-center gap-3">
<SubHeading title={t('proposalDescription')} />
<div className={collapsibleToggleStyles(showDescription)}>
<div className={showDescriptionIconClasses}>
<Icon name="chevron-down" size={8} />
</div>
</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,6 +13,9 @@ 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">
@@ -22,7 +25,7 @@ export const ProposalJson = ({
>
<div className="flex items-center gap-3">
<SubHeading title={t('proposalJson')} />
<div className={collapsibleToggleStyles(showDetails)}>
<div className={showDetailsIconClasses}>
<Icon name="chevron-down" size={8} />
</div>
</div>
@@ -1 +0,0 @@
export * from './proposal-market-data';
@@ -1,222 +0,0 @@
import isEqual from 'lodash/isEqual';
import { useState } from 'react';
import { useTranslation } from 'react-i18next';
import {
InstrumentInfoPanel,
KeyDetailsInfoPanel,
LiquidityMonitoringParametersInfoPanel,
LiquidityPriceRangeInfoPanel,
MetadataInfoPanel,
OracleInfoPanel,
PriceMonitoringBoundsInfoPanel,
RiskFactorsInfoPanel,
RiskModelInfoPanel,
RiskParametersInfoPanel,
SettlementAssetInfoPanel,
} from '@vegaprotocol/markets';
import {
Accordion,
AccordionItem,
Button,
CopyWithTooltip,
Dialog,
Icon,
SyntaxHighlighter,
} from '@vegaprotocol/ui-toolkit';
import { SubHeading } from '../../../../components/heading';
import { collapsibleToggleStyles } from '../../../../lib/collapsible-toggle-styles';
import type { MarketInfoWithData } from '@vegaprotocol/markets';
import type { DataSourceDefinition } from '@vegaprotocol/types';
import { create } from 'zustand';
type MarketDataDialogState = {
isOpen: boolean;
open: () => void;
close: () => void;
};
export const useMarketDataDialogStore = create<MarketDataDialogState>(
(set) => ({
isOpen: false,
open: () => set({ isOpen: true }),
close: () => set({ isOpen: false }),
})
);
export const ProposalMarketData = ({
marketData,
}: {
marketData: MarketInfoWithData;
}) => {
const { t } = useTranslation();
const { isOpen, open, close } = useMarketDataDialogStore();
const [showDetails, setShowDetails] = useState(false);
if (!marketData) {
return null;
}
const settlementData = marketData.tradableInstrument.instrument.product
.dataSourceSpecForSettlementData.data as DataSourceDefinition;
const terminationData = marketData.tradableInstrument.instrument.product
.dataSourceSpecForTradingTermination.data as DataSourceDefinition;
const getSigners = (data: DataSourceDefinition) => {
if (data.sourceType.__typename === 'DataSourceDefinitionExternal') {
const signers = data.sourceType.sourceType.signers || [];
return signers.map(({ signer }) => {
return (
(signer.__typename === 'ETHAddress' && signer.address) ||
(signer.__typename === 'PubKey' && signer.key)
);
});
}
return [];
};
return (
<section className="relative" data-testid="proposal-market-data">
<button
onClick={() => setShowDetails(!showDetails)}
data-testid="proposal-market-data-toggle"
>
<div className="flex items-center gap-3">
<SubHeading title={t('marketSpecification')} />
<div className={collapsibleToggleStyles(showDetails)}>
<Icon name="chevron-down" size={8} />
</div>
</div>
</button>
{showDetails && (
<>
<div className="float-right">
<Button onClick={open} data-testid="view-market-json">
{t('viewMarketJson')}
</Button>
</div>
<div className="mb-10">
<Accordion>
<AccordionItem
itemId="key-details"
title={t('Key details')}
content={<KeyDetailsInfoPanel market={marketData} />}
/>
<AccordionItem
itemId="instrument"
title={t('Instrument')}
content={<InstrumentInfoPanel market={marketData} />}
/>
{isEqual(
getSigners(settlementData),
getSigners(terminationData)
) ? (
<AccordionItem
itemId="oracles"
title={t('Oracle')}
content={
<OracleInfoPanel
market={marketData}
type="settlementData"
/>
}
/>
) : (
<>
<AccordionItem
itemId="settlement-oracle"
title={t('Settlement Oracle')}
content={
<OracleInfoPanel
market={marketData}
type="settlementData"
/>
}
/>
<AccordionItem
itemId="termination-oracle"
title={t('Termination Oracle')}
content={
<OracleInfoPanel market={marketData} type="termination" />
}
/>
</>
)}
<AccordionItem
itemId="settlement-asset"
title={t('Settlement asset')}
content={<SettlementAssetInfoPanel market={marketData} />}
/>
<AccordionItem
itemId="metadata"
title={t('Metadata')}
content={<MetadataInfoPanel market={marketData} />}
/>
<AccordionItem
itemId="risk-model"
title={t('Risk model')}
content={<RiskModelInfoPanel market={marketData} />}
/>
<AccordionItem
itemId="risk-parameters"
title={t('Risk parameters')}
content={<RiskParametersInfoPanel market={marketData} />}
/>
<AccordionItem
itemId="risk-factors"
title={t('Risk factors')}
content={<RiskFactorsInfoPanel market={marketData} />}
/>
{(
marketData.priceMonitoringSettings?.parameters?.triggers || []
).map((_, triggerIndex) => (
<AccordionItem
itemId={`trigger-${triggerIndex}`}
title={t(`Price monitoring bounds ${triggerIndex + 1}`)}
content={
<PriceMonitoringBoundsInfoPanel
market={marketData}
triggerIndex={triggerIndex}
/>
}
/>
))}
<AccordionItem
itemId="liqudity-monitoring-parameters"
title={t('Liquidity monitoring parameters')}
content={
<LiquidityMonitoringParametersInfoPanel market={marketData} />
}
/>
<AccordionItem
itemId="liquidity-price-range"
title={t('Liquidity price range')}
content={<LiquidityPriceRangeInfoPanel market={marketData} />}
/>
</Accordion>
</div>
</>
)}
<Dialog
title={marketData.tradableInstrument.instrument.code}
open={isOpen}
onChange={(isOpen) => (isOpen ? open() : close())}
size="medium"
dataTestId="market-json-dialog"
>
<CopyWithTooltip text={JSON.stringify(marketData)}>
<button className="bg-vega-dark-100 rounded-sm py-2 px-3 mb-4 text-white">
<span>
<Icon name="duplicate" />
</span>
<span className="ml-2">Copy</span>
</button>
</CopyWithTooltip>
<SyntaxHighlighter data={marketData} />
</Dialog>
</section>
);
};
@@ -1,3 +1,4 @@
import classnames from 'classnames';
import { useState } from 'react';
import { useTranslation } from 'react-i18next';
import {
@@ -12,7 +13,6 @@ 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,6 +57,10 @@ export const ProposalVotesTable = ({
? t('byTokenVote')
: t('byLiquidityVote');
const showDetailsIconClasses = classnames('mb-4', {
'rotate-180': showDetails,
});
return (
<>
<button
@@ -65,7 +69,7 @@ export const ProposalVotesTable = ({
>
<div className="flex items-center gap-3">
<SubHeading title={t('voteBreakdown')} />
<div className={collapsibleToggleStyles(showDetails)}>
<div className={showDetailsIconClasses}>
<Icon name="chevron-down" size={8} />
</div>
</div>
@@ -1,4 +1,3 @@
import { MemoryRouter } from 'react-router-dom';
import { render, screen } from '@testing-library/react';
import { generateProposal } from '../../test-helpers/generate-proposals';
import { Proposal } from './proposal';
@@ -29,6 +28,9 @@ 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>,
}));
@@ -39,38 +41,23 @@ 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();
renderComponent(proposal);
render(
<Proposal restData={{}} proposal={proposal as ProposalQuery['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();
renderComponent(proposal);
render(
<Proposal restData={{}} proposal={proposal as ProposalQuery['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();
@@ -93,7 +80,8 @@ it('renders whitelist section if proposal is new asset and source is erc20', asy
},
},
});
renderComponent(proposal);
render(
<Proposal restData={{}} proposal={proposal as ProposalQuery['proposal']} />
);
expect(screen.getByTestId('proposal-list-asset')).toBeInTheDocument();
});
@@ -1,22 +1,18 @@
import { useTranslation } from 'react-i18next';
import { Link } from 'react-router-dom';
import {
NetworkParams,
useNetworkParams,
} from '@vegaprotocol/network-parameters';
import { AsyncRenderer, Icon, RoundedWrapper } from '@vegaprotocol/ui-toolkit';
import { AsyncRenderer, RoundedWrapper } from '@vegaprotocol/ui-toolkit';
import { ProposalHeader } from '../proposal-detail-header/proposal-header';
import 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',
@@ -28,17 +24,11 @@ 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,
newMarketData,
}: ProposalProps) => {
const { t } = useTranslation();
export const Proposal = ({ proposal, restData }: ProposalProps) => {
const { params, loading, error } = useNetworkParams([
NetworkParams.governance_proposal_market_minVoterBalance,
NetworkParams.governance_proposal_updateMarket_minVoterBalance,
@@ -91,69 +81,53 @@ export const Proposal = ({
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 id="details">
<div className="my-10">
<ProposalChangeTable proposal={proposal} />
</div>
<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>
{newMarketData && (
{proposal.terms.change.__typename !== 'NewMarket' &&
proposal.terms.change.__typename !== 'UpdateMarket' &&
proposal.terms.change.__typename !== 'NewFreeform' && (
<div className="mb-4">
<ProposalMarketData marketData={newMarketData} />
<ProposalTerms data={proposal.terms} />
</div>
)}
<div className="mb-6">
<ProposalJson proposal={restData?.data?.proposal} />
</div>
<div className="mb-6">
<ProposalJson proposal={restData?.data?.proposal} />
</div>
<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
<div className="mb-10">
<RoundedWrapper paddingBottom={true}>
<VoteDetails
proposal={proposal}
proposalType={proposalType}
minVoterBalance={minVoterBalance}
spamProtectionMinTokens={
params?.spam_protection_voting_min_tokens
}
/>
</div>
</RoundedWrapper>
</div>
<div className="mb-4">
<ProposalVotesTable proposal={proposal} proposalType={proposalType} />
</div>
</section>
</AsyncRenderer>
@@ -224,47 +224,7 @@ describe('Proposal form vote, validation and enactment deadline', () => {
expect(
screen.getByTestId('enactment-before-voting-deadline')
).toHaveTextContent(
'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'
'Proposal will fail if enactment is earlier than the voting deadline'
);
});
});
@@ -220,41 +220,21 @@ const EnactmentForm = ({
<span data-testid="enactment-date" className="pl-2">
{getDateTimeFormat().format(deadlineDates.enactment)}
</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>
)}
</>
{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>
)}
</p>
)}
@@ -520,33 +500,13 @@ export function ProposalFormVoteAndEnactmentDeadline({
<span data-testid="voting-date" className="pl-2">
{getDateTimeFormat().format(deadlineDates.vote)}
</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>
)}
</>
{deadlines.vote === minVoteHours && (
<span
data-testid="voting-2-mins-extra"
className="block mt-4 font-light"
>
{t('ThisWillAdd2MinutesToAllowTimeToConfirmInWallet')}
</span>
)}
</p>
)}
@@ -1,10 +1,11 @@
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 { removePaginationWrapper } from '@vegaprotocol/utils';
import type { FinalizedVote } from '@vegaprotocol/proposals';
import { removePaginationWrapper } from '@vegaprotocol/utils';
export enum VoteState {
NotCast = 'NotCast',
@@ -44,23 +45,21 @@ export function useUserVote(
);
useEffect(() => {
if (finalizedVote?.vote.value && finalizedVote.pubKey === pubKey) {
if (finalizedVote?.vote.value) {
setUserVote(finalizedVote);
} else if (data?.party?.votesConnection?.edges && pubKey) {
const vote = removePaginationWrapper(
data?.party?.votesConnection?.edges
).find(({ proposalId: pId }) => proposalId === pId);
if (vote) {
setUserVote({ ...vote, pubKey });
}
} 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
)
);
}
}, [
finalizedVote?.vote.value,
data?.party?.votesConnection?.edges,
finalizedVote,
proposalId,
pubKey,
]);
// If user vote changes update the vote state
@@ -95,8 +94,6 @@ export function useUserVote(
return {
voteState,
userVote,
voteDatetime: userVote?.vote?.datetime
? new Date(userVote.vote.datetime)
: null,
voteDatetime: userVote ? new Date(userVote.vote.datetime) : null,
};
}
@@ -7,8 +7,6 @@ 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 }>();
@@ -22,36 +20,15 @@ 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, 2000);
const interval = setInterval(refetch, 1000);
return () => clearInterval(interval);
}, [refetch]);
return (
<AsyncRenderer
loading={loading || newMarketLoading}
error={error || newMarketError}
data={newMarketData ? { newMarketData, data } : data}
>
<AsyncRenderer loading={loading} error={error} data={data}>
{data?.proposal ? (
<Proposal
proposal={data.proposal}
restData={restData}
newMarketData={newMarketData}
/>
<Proposal proposal={data.proposal} restData={restData} />
) : (
<ProposalNotFound />
)}
@@ -48,22 +48,15 @@ export const EpochIndividualRewards = ({
return removePaginationWrapper(data.party.rewardsConnection.edges);
}, [data]);
const epochRewardSummaries = useMemo(() => {
if (!data?.epochRewardSummaries) return [];
return removePaginationWrapper(data.epochRewardSummaries.edges);
}, [data]);
const epochIndividualRewardSummaries = useMemo(() => {
if (!data?.party) return [];
return generateEpochIndividualRewardsList({
rewards,
epochId,
epochRewardSummaries,
page,
size: EPOCHS_PAGE_SIZE,
});
}, [data?.party, epochId, epochRewardSummaries, page, rewards]);
}, [data?.party, epochId, page, rewards]);
const refetchData = useCallback(
async (toPage?: number) => {
@@ -65,11 +65,7 @@ describe('generateEpochIndividualRewardsList', () => {
it('should return an empty array if no rewards are provided', () => {
expect(
generateEpochIndividualRewardsList({
rewards: [],
epochId: 1,
epochRewardSummaries: [],
})
generateEpochIndividualRewardsList({ rewards: [], epochId: 1 })
).toEqual([
{
epoch: 1,
@@ -82,7 +78,6 @@ describe('generateEpochIndividualRewardsList', () => {
const result = generateEpochIndividualRewardsList({
rewards: [rewardWrongType],
epochId: 1,
epochRewardSummaries: [],
});
expect(result).toEqual([
@@ -97,15 +92,6 @@ describe('generateEpochIndividualRewardsList', () => {
const result = generateEpochIndividualRewardsList({
rewards: [reward1],
epochId: 1,
epochRewardSummaries: [
{
__typename: 'EpochRewardSummary',
epoch: 1,
assetId: 'usd',
amount: '100000',
rewardType: AccountType.ACCOUNT_TYPE_GLOBAL_REWARD,
},
],
});
expect(result[0]).toEqual({
@@ -148,11 +134,7 @@ describe('generateEpochIndividualRewardsList', () => {
it('should return an array sorted by epoch descending', () => {
const rewards = [reward1, reward2, reward3, reward4];
const result1 = generateEpochIndividualRewardsList({
rewards,
epochId: 2,
epochRewardSummaries: [],
});
const result1 = generateEpochIndividualRewardsList({ rewards, epochId: 2 });
expect(result1[0].epoch).toEqual(2);
expect(result1[1].epoch).toEqual(1);
@@ -161,7 +143,6 @@ describe('generateEpochIndividualRewardsList', () => {
const result2 = generateEpochIndividualRewardsList({
rewards: reorderedRewards,
epochId: 2,
epochRewardSummaries: [],
});
expect(result2[0].epoch).toEqual(2);
@@ -170,11 +151,7 @@ describe('generateEpochIndividualRewardsList', () => {
it('correctly calculates the total value of rewards for an asset', () => {
const rewards = [reward1, reward4];
const result = generateEpochIndividualRewardsList({
rewards,
epochId: 1,
epochRewardSummaries: [],
});
const result = generateEpochIndividualRewardsList({ rewards, epochId: 1 });
expect(result[0].rewards[0].totalAmount).toEqual('200');
});
@@ -182,11 +159,7 @@ describe('generateEpochIndividualRewardsList', () => {
it('returns data in the expected shape', () => {
// Just sanity checking the whole structure here
const rewards = [reward1, reward2, reward3, reward4];
const result = generateEpochIndividualRewardsList({
rewards,
epochId: 2,
epochRewardSummaries: [],
});
const result = generateEpochIndividualRewardsList({ rewards, epochId: 2 });
expect(result).toEqual([
{
@@ -300,7 +273,6 @@ describe('generateEpochIndividualRewardsList', () => {
const resultPageOne = generateEpochIndividualRewardsList({
rewards,
epochId: 3,
epochRewardSummaries: [],
page: 1,
size: 2,
});
@@ -414,7 +386,6 @@ describe('generateEpochIndividualRewardsList', () => {
const resultPageTwo = generateEpochIndividualRewardsList({
rewards,
epochId: 3,
epochRewardSummaries: [],
page: 2,
size: 2,
});
@@ -458,69 +429,4 @@ describe('generateEpochIndividualRewardsList', () => {
},
]);
});
it('correctly calculates the percentage of two or more rewards by referencing the total rewards amount', () => {
const result = generateEpochIndividualRewardsList({
rewards: [
// reward1 is 100 usd, which is 10% of the total rewards amount
reward1,
{
rewardType: AccountType.ACCOUNT_TYPE_GLOBAL_REWARD,
amount: '200',
percentageOfTotal: '0.2',
receivedAt: new Date(),
asset: { id: 'usd', symbol: 'USD', name: 'USD', decimals: 6 },
party: { id: 'blah' },
epoch: { id: '1' },
},
],
epochId: 1,
epochRewardSummaries: [
{
__typename: 'EpochRewardSummary',
epoch: 1,
assetId: 'usd',
amount: '1000',
rewardType: AccountType.ACCOUNT_TYPE_GLOBAL_REWARD,
},
],
});
expect(result[0]).toEqual({
epoch: 1,
rewards: [
{
asset: 'USD',
decimals: 6,
totalAmount: '300',
rewardTypes: {
[AccountType.ACCOUNT_TYPE_FEES_INFRASTRUCTURE]: {
amount: '0',
percentageOfTotal: '0',
},
[AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES]: {
amount: '0',
percentageOfTotal: '0',
},
[AccountType.ACCOUNT_TYPE_GLOBAL_REWARD]: {
amount: '300',
percentageOfTotal: '30',
},
[AccountType.ACCOUNT_TYPE_REWARD_MAKER_PAID_FEES]: {
amount: '0',
percentageOfTotal: '0',
},
[AccountType.ACCOUNT_TYPE_REWARD_MAKER_RECEIVED_FEES]: {
amount: '0',
percentageOfTotal: '0',
},
[AccountType.ACCOUNT_TYPE_REWARD_MARKET_PROPOSERS]: {
amount: '0',
percentageOfTotal: '0',
},
},
},
],
});
});
});
@@ -1,9 +1,6 @@
import { BigNumber } from '../../../lib/bignumber';
import { RowAccountTypes } from '../shared-rewards-table-assets/shared-rewards-table-assets';
import type {
EpochRewardSummaryFieldsFragment,
RewardFieldsFragment,
} from '../home/__generated__/Rewards';
import type { RewardFieldsFragment } from '../home/__generated__/Rewards';
import type { AccountType } from '@vegaprotocol/types';
import { calculateEpochOffset } from '../../../lib/epoch-pagination';
@@ -35,13 +32,11 @@ const emptyRowAccountTypes = accountTypes.map((type) => [
export const generateEpochIndividualRewardsList = ({
rewards,
epochId,
epochRewardSummaries,
page = 1,
size = 10,
}: {
rewards: RewardFieldsFragment[];
epochId: number;
epochRewardSummaries: EpochRewardSummaryFieldsFragment[];
page?: number;
size?: number;
}) => {
@@ -59,7 +54,6 @@ export const generateEpochIndividualRewardsList = ({
const epochIndividualRewards = rewards.reduce((acc, reward) => {
const epochId = reward.epoch.id;
const assetName = reward.asset.name;
const assetId = reward.asset.id;
const assetDecimals = reward.asset.decimals;
const rewardType = reward.rewardType;
const amount = reward.amount;
@@ -76,14 +70,6 @@ export const generateEpochIndividualRewardsList = ({
const epoch = acc.get(epochId);
// matchingTotalReward is the total awarded for all users for the reward type in the epoch of the asset
const matchingTotalRewardAmount = epochRewardSummaries.find(
(summary) =>
summary.epoch === Number(epochId) &&
summary.assetId === assetId &&
summary.rewardType === rewardType
)?.amount;
let asset = epoch?.rewards.find((r) => r.asset === assetName);
if (!asset) {
@@ -100,24 +86,22 @@ export const generateEpochIndividualRewardsList = ({
asset.rewardTypes[rewardType] = { amount, percentageOfTotal };
} else {
const previousAmount = asset.rewardTypes[rewardType]?.amount;
const newAmount = previousAmount
? new BigNumber(previousAmount).plus(amount).toString()
: amount;
const previousPercentageOfTotal =
asset.rewardTypes[rewardType]?.percentageOfTotal;
asset.rewardTypes[rewardType] = {
amount: newAmount,
percentageOfTotal: matchingTotalRewardAmount
? new BigNumber(newAmount)
.dividedBy(matchingTotalRewardAmount)
.multipliedBy(100)
amount: previousAmount
? new BigNumber(previousAmount).plus(amount).toString()
: amount,
percentageOfTotal: previousPercentageOfTotal
? new BigNumber(previousPercentageOfTotal)
.plus(percentageOfTotal)
.toString()
: // this should never be reached, if there's an individual reward there should
// always be a reward total from the api too, but set it as a fallback just in case
percentageOfTotal,
: percentageOfTotal,
};
}
// totalAmount is the sum of all individual rewardTypes amounts
// totalAmount is the sum of all rewardTypes amounts
asset.totalAmount = Object.values(asset.rewardTypes).reduce(
(sum, rewardType) => {
return new BigNumber(sum).plus(rewardType.amount).toString();
@@ -22,13 +22,6 @@ fragment DelegationFields on Delegation {
epoch
}
fragment EpochRewardSummaryFields on EpochRewardSummary {
epoch
assetId
amount
rewardType
}
query Rewards(
$partyId: ID!
$fromEpoch: Int
@@ -57,16 +50,13 @@ query Rewards(
}
}
}
epochRewardSummaries(
filter: { fromEpoch: $fromEpoch, toEpoch: $toEpoch }
pagination: $rewardsPagination
) {
edges {
node {
...EpochRewardSummaryFields
}
}
}
}
fragment EpochRewardSummaryFields on EpochRewardSummary {
epoch
assetId
amount
rewardType
}
query EpochAssetsRewards(
@@ -7,8 +7,6 @@ export type RewardFieldsFragment = { __typename?: 'Reward', rewardType: Types.Ac
export type DelegationFieldsFragment = { __typename?: 'Delegation', amount: string, epoch: number };
export type EpochRewardSummaryFieldsFragment = { __typename?: 'EpochRewardSummary', epoch: number, assetId: string, amount: string, rewardType: Types.AccountType };
export type RewardsQueryVariables = Types.Exact<{
partyId: Types.Scalars['ID'];
fromEpoch?: Types.InputMaybe<Types.Scalars['Int']>;
@@ -18,7 +16,9 @@ export type RewardsQueryVariables = Types.Exact<{
}>;
export type RewardsQuery = { __typename?: 'Query', party?: { __typename?: 'Party', id: string, rewardsConnection?: { __typename?: 'RewardsConnection', edges?: Array<{ __typename?: 'RewardEdge', node: { __typename?: 'Reward', rewardType: Types.AccountType, amount: string, percentageOfTotal: string, receivedAt: any, asset: { __typename?: 'Asset', id: string, symbol: string, name: string, decimals: number }, party: { __typename?: 'Party', id: string }, epoch: { __typename?: 'Epoch', id: string } } } | null> | null } | null, delegationsConnection?: { __typename?: 'DelegationsConnection', edges?: Array<{ __typename?: 'DelegationEdge', node: { __typename?: 'Delegation', amount: string, epoch: number } } | null> | null } | null } | null, epochRewardSummaries?: { __typename?: 'EpochRewardSummaryConnection', edges?: Array<{ __typename?: 'EpochRewardSummaryEdge', node: { __typename?: 'EpochRewardSummary', epoch: number, assetId: string, amount: string, rewardType: Types.AccountType } } | null> | null } | null };
export type RewardsQuery = { __typename?: 'Query', party?: { __typename?: 'Party', id: string, rewardsConnection?: { __typename?: 'RewardsConnection', edges?: Array<{ __typename?: 'RewardEdge', node: { __typename?: 'Reward', rewardType: Types.AccountType, amount: string, percentageOfTotal: string, receivedAt: any, asset: { __typename?: 'Asset', id: string, symbol: string, name: string, decimals: number }, party: { __typename?: 'Party', id: string }, epoch: { __typename?: 'Epoch', id: string } } } | null> | null } | null, delegationsConnection?: { __typename?: 'DelegationsConnection', edges?: Array<{ __typename?: 'DelegationEdge', node: { __typename?: 'Delegation', amount: string, epoch: number } } | null> | null } | null } | null };
export type EpochRewardSummaryFieldsFragment = { __typename?: 'EpochRewardSummary', epoch: number, assetId: string, amount: string, rewardType: Types.AccountType };
export type EpochAssetsRewardsQueryVariables = Types.Exact<{
epochRewardSummariesFilter?: Types.InputMaybe<Types.RewardSummaryFilter>;
@@ -102,20 +102,9 @@ export const RewardsDocument = gql`
}
}
}
epochRewardSummaries(
filter: {fromEpoch: $fromEpoch, toEpoch: $toEpoch}
pagination: $rewardsPagination
) {
edges {
node {
...EpochRewardSummaryFields
}
}
}
}
${RewardFieldsFragmentDoc}
${DelegationFieldsFragmentDoc}
${EpochRewardSummaryFieldsFragmentDoc}`;
${DelegationFieldsFragmentDoc}`;
/**
* __useRewardsQuery__
@@ -36,7 +36,6 @@ import {
toBigNum,
} from '@vegaprotocol/utils';
import { VALIDATOR_LOGO_MAP } from './logo-map';
import { getMultisigStatusInfo } from '../../../../lib/get-multisig-status-info';
interface CanonisedConsensusNodeProps {
id: string;
@@ -138,10 +137,6 @@ export const ConsensusValidatorsTable = ({
[totalStake]
);
const multisigStatus = previousEpochData
? getMultisigStatusInfo(previousEpochData)
: undefined;
const allNodesInPreviousEpoch = removePaginationWrapper(
previousEpochData?.epoch.validatorsConnection?.edges
);
@@ -228,8 +223,6 @@ export const ConsensusValidatorsTable = ({
[ValidatorFields.USER_STAKE_SHARE]: userStakeShare
? formatNumberPercentage(new BigNumber(userStakeShare), 2)
: undefined,
[ValidatorFields.MULTISIG_ERROR]:
multisigStatus?.showMultisigStatusError,
};
}
);
@@ -339,7 +332,6 @@ export const ConsensusValidatorsTable = ({
data,
decimals,
hideTopThird,
multisigStatus?.showMultisigStatusError,
previousEpochData,
thirdOfTotalStake,
validatorsView,
@@ -39,7 +39,6 @@ export enum ValidatorFields {
STAKED_BY_USER = 'stakedByUser',
PENDING_USER_STAKE = 'pendingUserStake',
USER_STAKE_SHARE = 'userStakeShare',
MULTISIG_ERROR = 'multisigError',
}
export const addUserDataToValidator = (
@@ -327,7 +326,6 @@ interface TotalPenaltiesRendererProps {
overstakedAmount: string;
overstakingPenalty: string;
totalPenalties: string;
multisigError?: boolean;
};
}
@@ -346,11 +344,10 @@ export const TotalPenaltiesRenderer = ({
<div data-testid="overstaked-penalty-tooltip">
{t('overstakedPenalty')}: {data.overstakingPenalty}
</div>
{data.multisigError && (
<div data-testid="multisig-error-tooltip">
{t('multisigPenalty')}: 100%
</div>
)}
<div data-testid="total-penalty-tooltip">
{t('totalPenalties')}:{' '}
<span className="font-bold">{data.totalPenalties}</span>
</div>
</>
}
>
@@ -107,7 +107,7 @@ export const StakingNode = ({ data, previousEpochData }: StakingNodeProps) => {
<div className="flex items-center gap-1">
<Icon name={'chevron-left'} />
<Link className="underline" to={Routes.VALIDATORS}>
{t('AllValidators')}
{t('All validators')}
</Link>
</div>
<Heading
@@ -7,30 +7,31 @@ import { AsyncRenderer } from '@vegaprotocol/ui-toolkit';
import {
getFeeLevels,
sumLiquidityCommitted,
marketLiquidityDataProvider,
lpAggregatedDataProvider,
} from '@vegaprotocol/liquidity';
import { marketWithDataProvider } from '@vegaprotocol/markets';
import type { MarketWithData } from '@vegaprotocol/markets';
import type { MarketLpQuery } from '@vegaprotocol/liquidity';
import { Market } from './market';
import { Header } from './header';
import { LPProvidersGrid } from './providers';
const formatMarket = (market: MarketWithData) => {
const formatMarket = (data: MarketLpQuery) => {
return {
name: market?.tradableInstrument.instrument.name,
name: data?.market?.tradableInstrument.instrument.name,
symbol:
market?.tradableInstrument.instrument.product.settlementAsset.symbol,
data?.market?.tradableInstrument.instrument.product.settlementAsset
.symbol,
settlementAsset:
market?.tradableInstrument.instrument.product.settlementAsset,
targetStake: market?.data?.targetStake,
tradingMode: market?.data?.marketTradingMode,
trigger: market?.data?.trigger,
data?.market?.tradableInstrument.instrument.product.settlementAsset,
targetStake: data?.market?.data?.targetStake,
tradingMode: data?.market?.data?.marketTradingMode,
trigger: data?.market?.data?.trigger,
};
};
export const lpDataProvider = makeDerivedDataProvider(
[marketWithDataProvider, lpAggregatedDataProvider],
[marketLiquidityDataProvider, lpAggregatedDataProvider],
([market, lpAggregatedData]) => ({
market: { ...formatMarket(market) },
liquidityProviders: lpAggregatedData || [],
-1
View File
@@ -14,7 +14,6 @@ NX_VEGA_WALLET_URL=http://localhost:1789
NX_ETH_LOCAL_PROVIDER_URL=http://localhost:8545/
NX_ETH_WALLET_MNEMONIC="ozone access unlock valid olympic save include omit supply green clown session"
NX_WALLETCONNECT_PROJECT_ID=fe8091dc35738863e509fc4947525c72
NX_SENTRY_DSN=https://dummy@o999999.ingest.sentry.io/9999999
# Expose some env vars to cypress environment for market setup
CYPRESS_ETH_WALLET_MNEMONIC=ozone access unlock valid olympic save include omit supply green clown session
@@ -1,5 +1,5 @@
// #region consts
const assetColId = '[col-id="asset.symbol"]';
const asset = 'asset';
const assetDetailsDialog = 'dialog-content';
const assetRow = 'key-value-table-row';
const contractAddress = '7_value';
@@ -108,7 +108,7 @@ beforeEach(() => {
const visitPortfolioAndClickAsset = (assetName: string) => {
cy.visit('/#/portfolio');
cy.get(assetColId).contains(assetName).click();
cy.getByTestId(asset).contains(assetName).click();
};
const testTooltip = (index: number, testId: string, tooltip: string) => {
@@ -1,15 +0,0 @@
describe('charts', { tags: '@smoke' }, () => {
before(() => {
cy.mockTradingPage();
cy.mockSubscription();
cy.visit('/#/markets/market-0');
cy.wait('@Markets');
cy.getByTestId('Depth').click();
});
it('can see market depth chart', () => {
// 6006-DEPC-001
cy.getByTestId('tab-depth').should('be.visible');
cy.get('.depth-chart-module_canvas__260De').should('be.visible');
});
});
@@ -450,17 +450,3 @@ describe('Closed markets', { tags: '@smoke' }, () => {
.should('have.text', 'View on Explorer');
});
});
describe('no closed markets', { tags: '@smoke', testIsolation: true }, () => {
before(() => {
cy.mockTradingPage();
cy.mockSubscription();
cy.visit('/#/markets/all');
cy.get('[data-testid="Closed markets"]').click();
});
it('can see no markets message', () => {
// 6001-MARK-034
cy.getByTestId('tab-closed-markets').should('contain.text', 'No markets');
});
});
@@ -1,8 +1,10 @@
const dialogContent = 'dialog-content';
const nodeHealth = 'node-health';
const statusIncidentsLink = 'footer [data-testid=external-link]';
describe('home', { tags: '@regression' }, () => {
before(() => {
cy.clearAllLocalStorage();
cy.mockTradingPage();
cy.mockSubscription();
cy.visit('/');
@@ -74,14 +76,23 @@ describe('home', { tags: '@regression' }, () => {
cy.visit('/');
});
// 0006-NETW-002
// 0006-NETW-003
// 0006-NETW-011
it('switch to fairground network and check status & incidents link', () => {
// 0006-NETW-002
// 0006-NETW-003
cy.getByTestId('navigation')
.find('[data-testid="network-switcher"]')
.should('have.text', 'Custom')
.click();
cy.getByTestId('network-item').contains('Fairground testnet');
cy.getByTestId('network-item').contains('Fairground testnet').click();
cy.get('[aria-haspopup="menu"]').should('contain.text', 'Fairground');
cy.url().should('include', 'fairground.wtf');
cy.contains('Continue').click();
cy.get(statusIncidentsLink)
.children('span')
.should('have.text', 'Mainnet status & incidents');
cy.get(statusIncidentsLink)
.should('have.attr', 'href')
.and('contain', 'https://blog.vega.xyz/tagged/vega-incident-reports');
});
});
});
@@ -1,10 +1,7 @@
import { aliasGQLQuery } from '@vegaprotocol/cypress';
import type { MarketsQuery } from '@vegaprotocol/markets';
import * as Schema from '@vegaprotocol/types';
const rowSelector =
'[data-testid="tab-all-markets"] .ag-center-cols-container .ag-row';
const colInstrumentCode = '[col-id="tradableInstrument.instrument.code"]';
describe('markets all table', { tags: '@smoke' }, () => {
beforeEach(() => {
@@ -63,7 +60,7 @@ describe('markets all table', { tags: '@smoke' }, () => {
// 6001-MARK-035
cy.get(rowSelector)
.first()
.find(colInstrumentCode)
.find('[col-id="tradableInstrument.instrument.code"]')
.should('have.text', 'SOLUSD');
// 6001-MARK-036
@@ -158,7 +155,6 @@ describe('markets all table', { tags: '@smoke' }, () => {
});
it('able to open and sort full market list - market page', () => {
// 6001-MARK-064
const ExpectedSortedMarkets = [
'AAPL.MF21',
'BTCUSD.MF21',
@@ -171,38 +167,8 @@ describe('markets all table', { tags: '@smoke' }, () => {
cy.get('.ag-header-cell-label').contains('Market').click(); // sort by market name
for (let i = 0; i < ExpectedSortedMarkets.length; i++) {
cy.get(`[row-index=${i}]`)
.find(colInstrumentCode)
.find('[col-id="tradableInstrument.instrument.code"]')
.should('have.text', ExpectedSortedMarkets[i]);
}
});
it('can drag and drop columns', () => {
// 6001-MARK-065
cy.get('.ag-overlay-loading-wrapper').should('not.be.visible');
cy.get(colInstrumentCode)
.realMouseDown()
.realMouseMove(700, 15)
.realMouseUp();
cy.get(colInstrumentCode).should(($element) => {
const attributeValue = $element.attr('aria-colindex');
expect(attributeValue).not.to.equal('1');
});
});
});
describe('no all markets', { tags: '@smoke', testIsolation: true }, () => {
before(() => {
cy.mockTradingPage();
const markets: MarketsQuery = {};
cy.mockGQL((req) => {
aliasGQLQuery(req, 'Markets', markets);
});
cy.mockSubscription();
cy.visit('/#/markets/all');
});
it('can see no markets message', () => {
// 6001-MARK-048
cy.getByTestId('tab-all-markets').should('contain.text', 'No markets');
});
});
@@ -182,7 +182,7 @@ describe('market info is displayed', { tags: '@smoke' }, () => {
.contains('Liquidity monitoring parameters')
.click();
validateMarketDataRow(0, 'Triggering Ratio', '0.7');
validateMarketDataRow(0, 'Triggering Ratio', '0');
validateMarketDataRow(1, 'Time Window', '3,600');
validateMarketDataRow(2, 'Scaling Factor', '10');
});
@@ -1,328 +0,0 @@
import { checkSorting } from '@vegaprotocol/cypress';
import * as Schema from '@vegaprotocol/types';
const liquidityTab = 'Liquidity';
const rowSelector =
'[data-testid="tab-liquidity"] .ag-center-cols-container .ag-row';
const rowSelectorLiquidityActive =
'[data-testid="tab-active"] .ag-center-cols-container .ag-row';
const rowSelectorLiquidityInactive =
'[data-testid="tab-inactive"] .ag-center-cols-container .ag-row';
const marketSummaryBlock = 'header-summary';
const itemValue = 'item-value';
const itemHeader = 'item-header';
const colCommitmentAmount = '[col-id="commitmentAmount"]';
const colAverageEntryValuation = '[col-id="averageEntryValuation"]';
const colEquityLikeShare = '[col-id="equityLikeShare"]';
const colFee = '[col-id="fee"]';
const colCommitmentAmount_1 = '[col-id="commitmentAmount_1"]';
const colBalance = '[col-id="balance"]';
const colStatus = '[col-id="status"]';
const colCreatedAt = '[col-id="createdAt"] button';
const colUpdatedAt = '[col-id="updatedAt"] button';
const headers = [
'Party',
'Commitment (tDAI)',
'Share',
'Proposed fee',
'Market valuation at entry',
'Obligation',
'Supplied',
'Status',
'Created',
'Updated',
];
describe('liquidity table - trading', { tags: '@smoke' }, () => {
before(() => {
cy.mockSubscription();
cy.mockTradingPage(
Schema.MarketState.STATE_ACTIVE,
Schema.MarketTradingMode.TRADING_MODE_MONITORING_AUCTION,
Schema.AuctionTrigger.AUCTION_TRIGGER_LIQUIDITY_TARGET_NOT_MET
);
cy.visit('/#/markets/market-0');
cy.wait('@MarketData');
cy.getByTestId(liquidityTab).click();
cy.wait('@LiquidityProvisions');
});
it('can see table headers', () => {
// 5002-LIQP-001
cy.getByTestId('tab-liquidity').within(($headers) => {
cy.wrap($headers)
.get('.ag-header-cell-text')
.each(($header, i) => {
cy.wrap($header).should('have.text', headers[i]);
});
});
});
it('renders liquidity table correctly', () => {
// 5002-LIQP-002
cy.get(rowSelector)
.first()
.find('[col-id="party.id"]')
.should(
'have.text',
'69464e35bcb8e8a2900ca0f87acaf252d50cf2ab2fc73694845a16b7c8a0dc6f'
);
cy.get(rowSelector)
.first()
.find(colCommitmentAmount)
.should('have.text', '4,000.00');
cy.get(rowSelector)
.first()
.find(colEquityLikeShare)
.should('have.text', '100.00%');
cy.get(rowSelector).first().find(colFee).should('have.text', '0.09%');
// 5002-LIQP-013
cy.get(rowSelector)
.first()
.find(colAverageEntryValuation)
.should('have.text', '685,852.93692');
cy.get(rowSelector)
.first()
.find(colCommitmentAmount_1)
.should('have.text', '4,000.00');
cy.get(rowSelector)
.first()
.find(colBalance)
.scrollIntoView()
.should('have.text', '4,000.00');
cy.get(rowSelector).first().find(colStatus).should('have.text', 'Active');
cy.get(rowSelector).first().find(colCreatedAt).should('not.be.empty');
cy.get(rowSelector).first().find(colUpdatedAt).should('not.be.empty');
});
it.skip('liquidity status column should be sorted properly', () => {
// 5002-LIQP-003
const liquidityColDefault = ['Active', 'Pending'];
const liquidityColAsc = ['Active', 'Pending'];
const liquidityColDesc = ['Pending', 'Active'];
checkSorting(
'status',
liquidityColDefault,
liquidityColAsc,
liquidityColDesc
);
});
});
describe('liquidity table view', { tags: '@smoke' }, () => {
before(() => {
cy.mockSubscription();
cy.mockTradingPage(
Schema.MarketState.STATE_ACTIVE,
Schema.MarketTradingMode.TRADING_MODE_MONITORING_AUCTION,
Schema.AuctionTrigger.AUCTION_TRIGGER_LIQUIDITY_TARGET_NOT_MET
);
cy.visit('/#/liquidity/market-0');
cy.wait('@LiquidityProvisions');
});
it('can see header title', () => {
// 5002-LIQP-004
// 5002-LIQP-005
cy.getByTestId('header-title')
.should('contain.text', 'BTCUSD.MF21 liquidity provision')
.and('contain.text', 'Go to trading');
});
it('can see target stake', () => {
// 5002-LIQP-006
cy.getByTestId(marketSummaryBlock).within(() => {
cy.getByTestId('target-stake').within(() => {
cy.getByTestId(itemHeader).should('have.text', 'Target stake');
cy.getByTestId(itemValue).should('have.text', '10.00 tDAI').realHover();
});
});
cy.getByTestId('tooltip-content').should(
'contain.text',
`The market's liquidity requirement which is derived from the maximum open interest observed over a rolling time window.The market's liquidity requirement which is derived from the maximum open interest observed over a rolling time window.`
);
});
it('can see supplied stake', () => {
// 5002-LIQP-007
cy.getByTestId(marketSummaryBlock).within(() => {
cy.getByTestId('supplied-stake').within(() => {
cy.getByTestId(itemHeader).should('have.text', 'Supplied stake');
cy.getByTestId(itemValue).should('have.text', '0.01 tDAI').realHover();
});
});
cy.getByTestId('tooltip-content').should(
'contain.text',
'The current amount of liquidity supplied for this market.'
);
});
it('can see liquidity supplied', () => {
// 5002-LIQP-008
cy.getByTestId(marketSummaryBlock).within(() => {
cy.getByTestId('liquidity-supplied').within(() => {
cy.getByTestId(itemHeader).should('have.text', 'Liquidity supplied');
cy.getByTestId('indicator').should('be.visible');
cy.getByTestId(itemValue).should('have.text', '0.10%').realHover();
});
});
});
it('can see market id', () => {
// 5002-LIQP-009
cy.getByTestId(marketSummaryBlock).within(() => {
cy.getByTestId('liquidity-market-id').within(() => {
cy.getByTestId(itemHeader).should('have.text', 'Market ID');
cy.getByTestId(itemValue).should('have.text', 'market-0');
});
});
});
it('can see market id', () => {
// 5002-LIQP-010
cy.getByTestId(marketSummaryBlock).within(() => {
cy.getByTestId('liquidity-learn-more').within(() => {
cy.getByTestId(itemHeader).should('have.text', 'Learn more');
cy.getByTestId(itemValue).should('have.text', 'Providing liquidity');
cy.getByTestId('external-link')
.should('have.attr', 'href')
.and(
'include',
'https://docs.vega.xyz/testnet/concepts/liquidity/provision'
);
});
});
});
describe('liquidity table view', { tags: '@smoke' }, () => {
it('can see table headers', () => {
cy.getByTestId('tab-active').within(($headers) => {
cy.wrap($headers)
.get('.ag-header-cell-text')
.each(($header, i) => {
cy.wrap($header).should('have.text', headers[i]);
});
});
});
it('renders liquidity active table correctly', () => {
// 5002-LIQP-011
cy.get(rowSelectorLiquidityActive)
.first()
.find('[col-id="party.id"]')
.should(
'have.text',
'69464e35bcb8e8a2900ca0f87acaf252d50cf2ab2fc73694845a16b7c8a0dc6f'
);
cy.get(rowSelectorLiquidityActive)
.first()
.find(colCommitmentAmount)
.should('have.text', '4,000.00');
cy.get(rowSelectorLiquidityActive)
.first()
.find(colEquityLikeShare)
.should('have.text', '100.00%');
cy.get(rowSelectorLiquidityActive)
.first()
.find(colFee)
.should('have.text', '0.09%');
// 5002-LIQP-013
cy.get(rowSelectorLiquidityActive)
.first()
.find(colAverageEntryValuation)
.should('have.text', '685,852.93692');
cy.get(rowSelectorLiquidityActive)
.first()
.find(colCommitmentAmount_1)
.should('have.text', '4,000.00');
cy.get(rowSelectorLiquidityActive)
.first()
.find(colBalance)
.should('have.text', '4,000.00');
cy.get(rowSelectorLiquidityActive)
.first()
.find(colStatus)
.should('have.text', 'Active');
cy.get(rowSelectorLiquidityActive)
.first()
.find(colCreatedAt)
.should('not.be.empty');
cy.get(rowSelectorLiquidityActive)
.first()
.find(colUpdatedAt)
.should('not.be.empty');
});
it('renders liquidity inactive table correctly', () => {
// 5002-LIQP-012
cy.getByTestId('Inactive').click();
cy.get(rowSelectorLiquidityInactive)
.first()
.find('[col-id="party.id"]')
.should(
'have.text',
'cc464e35bcb8e8a2900ca0f87acaf252d50cf2ab2fc73694845a16b7c8a0dc6f'
);
cy.get(rowSelectorLiquidityInactive)
.first()
.find(colCommitmentAmount)
.should('have.text', '4,000.00');
cy.get(rowSelectorLiquidityInactive)
.first()
.find(colEquityLikeShare)
.should('have.text', '100.00%');
cy.get(rowSelectorLiquidityInactive)
.first()
.find(colFee)
.should('have.text', '0.40%');
cy.get(rowSelectorLiquidityInactive)
.first()
.find(colAverageEntryValuation)
.should('have.text', '685,852.93692');
cy.get(rowSelectorLiquidityInactive)
.first()
.find(colCommitmentAmount_1)
.should('have.text', '4,000.00');
cy.get(rowSelectorLiquidityInactive)
.first()
.find(colBalance)
.should('have.text', '2,000.00');
cy.get(rowSelectorLiquidityInactive)
.first()
.find(colStatus)
.should('have.text', 'Pending');
cy.get(rowSelectorLiquidityInactive)
.first()
.find(colCreatedAt)
.should('not.be.empty');
cy.get(rowSelectorLiquidityInactive)
.first()
.find(colUpdatedAt)
.should('not.be.empty');
});
});
});
@@ -25,7 +25,6 @@ describe('markets selector', { tags: '@smoke' }, () => {
cy.wait('@MarketsCandles');
});
// 6001-MARK-066
it('can toggle the sidebar', () => {
cy.getByTestId('market-selector').should('be.visible');
cy.getByTestId('sidebar-toggle').click();
@@ -68,7 +67,6 @@ describe('markets selector', { tags: '@smoke' }, () => {
.each((item, i) => {
const market = data[i];
// 6001-MARK-021
// 6001-MARK-022
expect(item.find('h3').text()).equals(market.code);
expect(
item.find('[data-testid="market-selector-data-row"]').eq(0).text()
@@ -86,19 +84,8 @@ describe('markets selector', { tags: '@smoke' }, () => {
});
});
it('can see all markets link', () => {
// 6001-MARK-026
cy.getByTestId('market-selector').within(() => {
cy.getByTestId('all-markets-link')
.should('be.visible')
.and('have.text', 'All markets')
.and('have.attr', 'href')
.and('contain', '#/markets/all');
});
});
// 6001-MARK-27
it('can use the filter options', () => {
// 6001-MARK-027
// product type
cy.getByTestId('product-Spot').click();
cy.getByTestId(list).contains('Spot markets coming soon.');
@@ -107,7 +94,7 @@ describe('markets selector', { tags: '@smoke' }, () => {
cy.getByTestId('product-Future').click();
cy.getByTestId(list).find('a').should('have.length', 4);
// 6001-MARK-029
// 6001-MARK-29
cy.getByTestId(searchInput).clear().type('btc');
cy.getByTestId(list).find('a').should('have.length', 2);
cy.getByTestId(list).find('a').eq(1).contains('BTCUSD.MF21');
@@ -116,29 +103,4 @@ describe('markets selector', { tags: '@smoke' }, () => {
cy.getByTestId(searchInput).clear();
cy.getByTestId(list).find('a').should('have.length', 4);
});
it('can sort by by top gaining and top losing market', () => {
// 6001-MARK-030
// 6001-MARK-031
// 6001-MARK-032
// 6001-MARK-033
cy.getByTestId(' sort-trigger').click();
cy.getByTestId('sort-item-Gained')
.contains('Top gaining')
.should('be.visible');
cy.getByTestId('sort-item-Lost')
.contains('Top losing')
.should('be.visible');
cy.getByTestId('sort-item-New')
.contains('New markets')
.should('be.visible');
});
it('can filter by settlement asset', () => {
// 6001-MARK-028
cy.getByTestId('asset-trigger').click();
cy.getByTestId('asset-id-asset-3').contains('tBTC').click();
cy.getByTestId(list).find('a').should('have.length', 1);
cy.getByTestId(list).find('a').eq(0).contains('ETHBTC.QM21');
});
});
@@ -1,16 +1,16 @@
import { aliasGQLQuery, checkSorting } from '@vegaprotocol/cypress';
import type { ProposalsListQuery } from '@vegaprotocol/proposals';
import { checkSorting } from '@vegaprotocol/cypress';
const rowSelector =
'[data-testid="tab-proposed-markets"] .ag-center-cols-container .ag-row';
const colMarketId = '[col-id="market"]';
describe('markets proposed table', { tags: '@smoke' }, () => {
before(() => {
cy.mockTradingPage();
cy.mockSubscription();
cy.visit('/#/markets/all');
cy.get('[data-testid="Proposed markets"]').click();
beforeEach(() => {
cy.clearLocalStorage().then(() => {
cy.mockTradingPage();
cy.mockSubscription();
cy.visit('/#/markets/all');
cy.get('[data-testid="Proposed markets"]').click();
});
});
it('can see table headers', () => {
@@ -35,7 +35,10 @@ describe('markets proposed table', { tags: '@smoke' }, () => {
it('renders markets correctly', () => {
// 6001-MARK-049
cy.get(rowSelector).first().find(colMarketId).should('have.text', 'ETHUSD');
cy.get(rowSelector)
.first()
.find('[col-id="market"]')
.should('have.text', 'ETHUSD');
// 6001-MARK-050
cy.get(rowSelector)
@@ -116,7 +119,6 @@ describe('markets proposed table', { tags: '@smoke' }, () => {
);
});
it('proposed markets tab should be sorted properly', () => {
// 6001-MARK-062
cy.get('[data-testid="Proposed markets"]').click({ force: true });
const marketColDefault = [
'ETHUSD',
@@ -194,31 +196,4 @@ describe('markets proposed table', { tags: '@smoke' }, () => {
];
checkSorting('state', stateColDefault, stateColAsc, stateColDesc);
});
it('can drag and drop columns', () => {
// 6001-MARK-063
cy.get(colMarketId).realMouseDown().realMouseMove(700, 15).realMouseUp();
cy.get(colMarketId).should(($element) => {
const attributeValue = $element.attr('aria-colindex');
expect(attributeValue).not.to.equal('1');
});
});
});
describe('no markets proposed', { tags: '@smoke', testIsolation: true }, () => {
before(() => {
cy.mockTradingPage();
const proposal: ProposalsListQuery = {};
cy.mockGQL((req) => {
aliasGQLQuery(req, 'ProposalsList', proposal);
});
cy.mockSubscription();
cy.visit('/#/markets/all');
cy.get('[data-testid="Proposed markets"]').click();
});
it('can see no markets message', () => {
// 6001-MARK-061
cy.getByTestId('tab-proposed-markets').should('contain.text', 'No markets');
});
});
@@ -1,102 +0,0 @@
const orderbookTab = 'Orderbook';
const orderbookTable = 'tab-orderbook';
const askPrice = 'price-9894585';
const bidPrice = 'price-9889001';
const askVolume = 'ask-vol-9894585';
const bidVolume = 'bid-vol-9889001';
const askCumulative = 'cumulative-vol-9894585';
const bidCumulative = 'cumulative-vol-9889001';
const midPrice = 'middle-mark-price-4612690000';
const priceResolution = 'resolution';
const dealTicketPrice = 'order-price';
const resPrice = 'price-990';
describe('order book', { tags: '@smoke' }, () => {
before(() => {
cy.mockTradingPage();
cy.mockSubscription();
cy.visit('/#/markets/market-0');
cy.wait('@Markets');
});
beforeEach(() => {
cy.mockTradingPage();
});
it('show order book', () => {
// 6003-ORDB-001
// 6003-ORDB-002
cy.getByTestId(orderbookTab).click();
cy.getByTestId(orderbookTable).should('be.visible');
cy.getByTestId(orderbookTable).should('not.be.empty');
});
it('show orders prices', () => {
// 6003-ORDB-003
cy.getByTestId(askPrice).should('have.text', '98.94585');
cy.getByTestId(bidPrice).should('have.text', '98.89001');
});
it('show prices volumes', () => {
// 6003-ORDB-004
cy.getByTestId(askVolume).should('have.text', '1');
cy.getByTestId(bidVolume).should('have.text', '1');
});
it('show prices cumulative volumes', () => {
// 6003-ORDB-005
cy.getByTestId(askCumulative).should('have.text', '39');
cy.getByTestId(bidCumulative).should('have.text', '7');
});
it('show mid price', () => {
// 6003-ORDB-006
cy.getByTestId(midPrice).should('have.text', '46,126.90');
});
it('sort prices descending', () => {
// 6003-ORDB-007
const prices: number[] = [];
cy.getByTestId(orderbookTable).within(() => {
cy.get('[data-testid*=price]')
.each(($el) => {
prices.push(Number($el.text()));
})
.then(() => {
expect(prices).to.deep.equal(prices.sort((a, b) => b - a));
});
});
});
it('copy price to deal ticket form', () => {
// 6003-ORDB-009
cy.getByTestId(askPrice).click();
cy.getByTestId(dealTicketPrice).should('have.value', '98.94585');
});
it('change price resolution', () => {
// 6003-ORDB-008
const resolutions = [
'0.00000',
'0.0000',
'0.000',
'0.00',
'0.0',
'0',
'10',
'100',
'1,000',
'10,000',
];
cy.getByTestId(priceResolution)
.find('option')
.each(($el, index) => {
expect($el.text()).to.equal(resolutions[index]);
});
cy.getByTestId(priceResolution).select('0.0');
cy.getByTestId(resPrice).should('have.text', '99.0');
cy.getByTestId(askPrice).should('not.exist');
cy.getByTestId(bidPrice).should('not.exist');
});
});
@@ -20,7 +20,6 @@ describe('accounts', { tags: '@smoke' }, () => {
// 7001-COLL-006
// 7001-COLL-007
// 1003-TRAN-001
// 7001-COLL-012
const tradingAccountRowId = '[row-id="t-0"]';
cy.getByTestId('Collateral').click();
@@ -35,23 +34,28 @@ describe('accounts', { tags: '@smoke' }, () => {
cy.getByTestId('tab-accounts')
.get(tradingAccountRowId)
.find('[col-id="used"]')
.should('have.text', '1.01' + '1.00%');
.should('have.text', '1.010.00%');
cy.getByTestId('tab-accounts')
.get(tradingAccountRowId)
.find('[col-id="available"]')
.should('have.text', '100.00');
.should('have.text', '100,000.00');
cy.getByTestId('tab-accounts')
.get(tradingAccountRowId)
.find('[col-id="total"]')
.should('have.text', '101.01');
.should('have.text', '100,001.01');
cy.getByTestId('tab-accounts')
.get(tradingAccountRowId)
.find('[col-id="accounts-actions"]')
.should('have.text', '');
cy.getByTestId('tab-accounts')
.get(tradingAccountRowId)
.find('[col-id="total"]')
.should('have.text', '100,001.01');
cy.getByTestId('tab-accounts')
.get('[col-id="accounts-actions"]')
.find('[data-testid="dropdown-menu"]')
@@ -97,7 +101,7 @@ describe('accounts', { tags: '@smoke' }, () => {
'Liquidity provision fee reward account balance',
'Market proposer reward account balance',
];
cy.get('[col-id="asset.symbol"]').contains('tEURO').click();
cy.getByTestId('asset').contains('tEURO').click();
cy.get('[data-testid$="_label"]').should('have.length', 16);
cy.get('[data-testid$="_label"]').each((element, index) => {
cy.wrap(element).should('have.text', titles[index]);
@@ -108,8 +112,8 @@ describe('accounts', { tags: '@smoke' }, () => {
it('should open usage breakdown dialog when clicked on used', () => {
// 7001-COLL-009
cy.get('[col-id="used"]').contains('1.01').click();
const headers = ['Market', 'Account type', 'Balance', 'Margin health'];
cy.getByTestId('breakdown').contains('1.01').click();
const headers = ['Market', 'Account type', 'Balance'];
cy.getByTestId('usage-breakdown').within(($headers) => {
cy.wrap($headers)
.get('.ag-header-cell-text')
@@ -128,14 +132,12 @@ describe('accounts', { tags: '@smoke' }, () => {
cy.wrap(btn).click();
});
}
cy.contains('Loading...').should('not.exist');
});
// 7001-COLL-010
it('sorting by asset', () => {
cy.getByTestId('Collateral').click();
const marketsSortedDefault = ['tBTC', 'tEURO', 'tDAI', 'tBTC'];
const marketsSortedAsc = ['tBTC', 'tBTC', 'tDAI', 'tEURO'];
const marketsSortedDesc = Array.from(marketsSortedAsc).reverse();
const marketsSortedDesc = ['tEURO', 'tDAI', 'tBTC', 'tBTC'];
checkSorting(
'asset.symbol',
marketsSortedDefault,
@@ -158,8 +160,12 @@ describe('accounts', { tags: '@smoke' }, () => {
'1,000.00002',
'1,000.01',
];
const marketsSortedDesc = Array.from(marketsSortedAsc).reverse();
const marketsSortedDesc = [
'1,000.01',
'1,000.00002',
'1,000.00001',
'1,000.00',
];
checkSorting(
'total',
marketsSortedDefault,
@@ -170,22 +176,24 @@ describe('accounts', { tags: '@smoke' }, () => {
it('sorting by used', () => {
cy.getByTestId('Collateral').click();
// concat actual value with percentage value
// as cypress will pick up the entire cell contes
// textContent
const marketsSortedDefault = [
'0.00' + '0.00%',
'0.01' + '0.00%',
'0.00' + '0.00%',
'0.00' + '0.00%',
'0.000.00%',
'0.010.00%',
'0.000.00%',
'0.000.00%',
];
const marketsSortedAsc = [
'0.00' + '0.00%',
'0.00' + '0.00%',
'0.00' + '0.00%',
'0.01' + '0.00%',
'0.000.00%',
'0.000.00%',
'0.000.00%',
'0.010.00%',
];
const marketsSortedDesc = [
'0.010.00%',
'0.000.00%',
'0.000.00%',
'0.000.00%',
];
const marketsSortedDesc = Array.from(marketsSortedAsc).reverse();
checkSorting(
'used',
marketsSortedDefault,
@@ -194,24 +202,29 @@ describe('accounts', { tags: '@smoke' }, () => {
);
});
it('sorting by available', () => {
it('sorting by total', () => {
cy.getByTestId('Collateral').click();
const marketsSortedDefault = [
'1,000.00002',
'1,000.00',
'1,000.01',
'1,000.00',
'1,000.00001',
];
const marketsSortedAsc = [
'1,000.00',
'1,000.00',
'1,000.00001',
'1,000.00002',
'1,000.01',
];
const marketsSortedDesc = [
'1,000.01',
'1,000.00002',
'1,000.00001',
'1,000.00',
];
const marketsSortedDesc = Array.from(marketsSortedAsc).reverse();
checkSorting(
'available',
'total',
marketsSortedDefault,
marketsSortedAsc,
marketsSortedDesc
@@ -81,7 +81,7 @@ describe(
cy.visit('/#/markets/market-0');
});
it('must display that market is not accepting orders', function () {
cy.getByTestId('deal-ticket-error-message-summary').should(
cy.getByTestId('dealticket-error-message-summary').should(
'have.text',
`This market is ${marketState
.split('_')
@@ -45,7 +45,7 @@ describe('deal ticker order validation', { tags: '@smoke' }, () => {
cy.getByTestId(placeOrderBtn).click();
cy.getByTestId('deal-ticket-error-message-expiry').should(
cy.getByTestId('dealticket-error-message-expiry').should(
'have.text',
'The expiry date that you have entered appears to be in the past'
);
@@ -57,7 +57,7 @@ describe('deal ticker order validation', { tags: '@smoke' }, () => {
cy.getByTestId(orderTIFDropDown).select('TIME_IN_FORCE_GTC');
cy.getByTestId(orderSizeField).clear().type('1');
cy.getByTestId(orderPriceField).clear().type('1.123456');
cy.getByTestId('deal-ticket-error-message-price-limit').should(
cy.getByTestId('dealticket-error-message-price-limit').should(
'have.text',
'Price accepts up to 5 decimal places'
);
@@ -79,7 +79,7 @@ describe('deal ticker order validation', { tags: '@smoke' }, () => {
cy.getByTestId(orderSizeField).clear().type('1.234');
// 7002-SORD-060
cy.getByTestId(placeOrderBtn).should('be.enabled');
cy.getByTestId('deal-ticket-error-message-size-market').should(
cy.getByTestId('dealticket-error-message-size-market').should(
'have.text',
'Size must be whole numbers for this market'
);
@@ -88,7 +88,7 @@ describe('deal ticker order validation', { tags: '@smoke' }, () => {
it('must warn if order size is set to 0', function () {
cy.getByTestId(orderSizeField).clear().type('0');
cy.getByTestId(placeOrderBtn).should('be.enabled');
cy.getByTestId('deal-ticket-error-message-size-market').should(
cy.getByTestId('dealticket-error-message-size-market').should(
'have.text',
'Size cannot be lower than 1'
);
@@ -96,29 +96,15 @@ describe('deal ticker order validation', { tags: '@smoke' }, () => {
it('must have total margin available', () => {
// 7001-COLL-011
cy.getByTestId('deal-ticket-fee-total-margin-available').within(() => {
cy.get('[data-state="closed"]').should(
'have.text',
'Total margin available100.01 tDAI'
);
});
});
it('must have current margin allocation', () => {
cy.getByTestId('deal-ticket-fee-current-margin-allocation').within(() => {
cy.get('[data-state="closed"]:first').should(
'have.text',
'Current margin allocation'
);
});
});
it('should open usage breakdown dialog when clicked on current margin allocation', () => {
cy.getByTestId('deal-ticket-fee-current-margin-allocation').within(() => {
cy.get('button').click();
});
cy.getByTestId('usage-breakdown').should('exist');
cy.getByTestId('dialog-close').click();
cy.getByTestId('tab-ticket')
.find('.text-xs')
.eq(5)
.within(() => {
cy.get('[data-state="closed"]').should(
'have.text',
'Total margin available100,000.01 tDAI'
);
});
});
});
});
@@ -1,10 +1,6 @@
import * as Schema from '@vegaprotocol/types';
import { aliasGQLQuery } from '@vegaprotocol/cypress';
import {
accountsQuery,
amendGeneralAccountBalance,
amendMarginAccountBalance,
} from '@vegaprotocol/mock';
import { accountsQuery, amendGeneralAccountBalance } from '@vegaprotocol/mock';
import type { OrderSubmission } from '@vegaprotocol/wallet';
import { createOrder } from '../support/create-order';
@@ -16,9 +12,8 @@ describe(
beforeEach(() => {
cy.setVegaWallet();
cy.mockTradingPage();
let accounts = accountsQuery();
accounts = amendMarginAccountBalance(accounts, 'market-0', '1000');
accounts = amendGeneralAccountBalance(accounts, 'market-0', '0');
const accounts = accountsQuery();
amendGeneralAccountBalance(accounts, 'market-0', '0');
cy.mockGQL((req) => {
aliasGQLQuery(req, 'Accounts', accounts);
});
@@ -28,15 +23,10 @@ describe(
});
it('should show an error if your balance is zero', () => {
const accounts = accountsQuery();
amendMarginAccountBalance(accounts, 'market-0', '0');
cy.mockGQL((req) => {
aliasGQLQuery(req, 'Accounts', accounts);
});
// 7002-SORD-060
cy.getByTestId('place-order').should('be.enabled');
// 7002-SORD-003
cy.getByTestId('deal-ticket-error-message-zero-balance').should(
cy.getByTestId('dealticket-error-message-zero-balance').should(
'have.text',
'You need ' +
'tDAI' +
@@ -50,9 +40,8 @@ describe(
beforeEach(() => {
cy.setVegaWallet();
cy.mockTradingPage();
let accounts = accountsQuery();
accounts = amendMarginAccountBalance(accounts, 'market-0', '1000');
accounts = amendGeneralAccountBalance(accounts, 'market-0', '1');
const accounts = accountsQuery();
amendGeneralAccountBalance(accounts, 'market-0', '1');
cy.mockGQL((req) => {
aliasGQLQuery(req, 'Accounts', accounts);
});
@@ -65,11 +54,11 @@ describe(
// 7002-SORD-003
// warning should show immediately
cy.getByTestId('deal-ticket-warning-margin').should(
cy.getByTestId('dealticket-warning-margin').should(
'contain.text',
'You may not have enough margin available to open this position'
);
cy.getByTestId('deal-ticket-warning-margin').should(
cy.getByTestId('dealticket-warning-margin').should(
'contain.text',
'You may not have enough margin available to open this position. 5.00 tDAI is currently required. You have only 0.01001 tDAI available.'
);
@@ -37,7 +37,7 @@ describe('suspended market validation', { tags: '@regression' }, () => {
// 7002-SORD-060
cy.getByTestId(placeOrderBtn).should('be.enabled');
cy.getByTestId(placeOrderBtn).click();
cy.getByTestId('deal-ticket-error-message-type').should(
cy.getByTestId('dealticket-error-message-type').should(
'have.text',
'This market is in auction until it reaches sufficient liquidity. Only limit orders are permitted when market is in auction'
);
@@ -48,7 +48,7 @@ describe('suspended market validation', { tags: '@regression' }, () => {
cy.getByTestId(orderPriceField).clear().type('0.1');
cy.getByTestId(orderSizeField).clear().type('1');
cy.getByTestId(placeOrderBtn).should('be.enabled');
cy.getByTestId('deal-ticket-warning-auction').should(
cy.getByTestId('dealticket-warning-auction').should(
'have.text',
'Any orders placed now will not trade until the auction ends'
);
@@ -60,7 +60,7 @@ describe('suspended market validation', { tags: '@regression' }, () => {
TIFlist.filter((item) => item.code === 'FOK')[0].value
);
cy.getByTestId(placeOrderBtn).should('be.enabled');
cy.getByTestId('deal-ticket-error-message-tif').should(
cy.getByTestId('dealticket-error-message-tif').should(
'have.text',
'This market is in auction until it reaches sufficient liquidity. Until the auction ends, you can only place GFA, GTT, or GTC limit orders'
);
@@ -5,10 +5,6 @@ import {
toggleMarket,
} from '../support/deal-ticket';
const tooltipContent = 'tooltip-content';
const reduceOnly = 'reduce-only';
const postOnly = 'post-only';
describe('time in force validation', { tags: '@smoke' }, () => {
before(() => {
cy.setVegaWallet();
@@ -126,18 +122,13 @@ describe('time in force validation', { tags: '@smoke' }, () => {
// 7002-SORD-026
it(`post and reduce order market for ${tif.code}`, function () {
// 7003-SORD-054
// 7003-SORD-055
// 7003-SORD-056
// 7003-SORD-057
cy.getByTestId(orderTIFDropDown).select(tif.value);
cy.get(`[data-testid=${orderTIFDropDown}] option:selected`).should(
'have.text',
tif.text
);
cy.getByTestId(postOnly).should('be.disabled');
cy.getByTestId(reduceOnly).should('be.enabled');
cy.getByTestId('post-only').should('be.disabled');
cy.getByTestId('reduce-only').should('be.enabled');
});
});
});
@@ -153,33 +144,14 @@ describe('time in force validation', { tags: '@smoke' }, () => {
validTIFLimit.forEach((tif) => {
it(`post and reduce order for limit ${tif.code}`, function () {
// 7003-SORD-054
// 7003-SORD-055
// 7003-SORD-056
// 7003-SORD-057
cy.getByTestId(orderTIFDropDown).select(tif.value);
cy.get(`[data-testid=${orderTIFDropDown}] option:selected`).should(
'have.text',
tif.text
);
cy.getByTestId(postOnly).should('be.enabled');
cy.getByTestId(reduceOnly).should('be.disabled');
cy.getByTestId('post-only').should('be.enabled');
cy.getByTestId('reduce-only').should('be.disabled');
});
});
it(`can see explanation of what post only and reduce only is/does`, function () {
// 7003-SORD-058
cy.get('[for="post-only"]').should('have.text', 'Post only').realHover();
cy.getByTestId(tooltipContent).should(
'contain.text',
`"Post only" will ensure the order is not filled immediately but is placed on the order book as a passive order. When the order is processed it is either stopped (if it would not be filled immediately), or placed in the order book as a passive order until the price taker matches with it.`
);
cy.get('[for="reduce-only"]')
.should('have.text', 'Reduce only')
.realHover();
cy.getByTestId(tooltipContent).should(
'contain.text',
`"Reduce only" can be used only with non-persistent orders, such as "Fill or Kill" or "Immediate or Cancel".`
);
});
});
});
@@ -1,4 +1,5 @@
import { checkSorting, aliasGQLQuery } from '@vegaprotocol/cypress';
import { checkSorting } from '@vegaprotocol/cypress';
import { aliasGQLQuery } from '@vegaprotocol/cypress';
import { marketsDataQuery } from '@vegaprotocol/mock';
import { positionsQuery } from '@vegaprotocol/mock';
@@ -14,12 +15,13 @@ const toastContent = 'toast-content';
const tooltipContent = 'tooltip-content';
// #endregion
beforeEach(() => {
cy.mockTradingPage();
cy.mockSubscription();
cy.setVegaWallet();
});
describe('positions', { tags: '@smoke', testIsolation: true }, () => {
beforeEach(() => {
cy.mockTradingPage();
cy.mockSubscription();
cy.setVegaWallet();
});
it('renders positions on trading page', () => {
visitAndClickPositions();
// 7004-POSI-001
@@ -62,14 +64,7 @@ describe('positions', { tags: '@smoke', testIsolation: true }, () => {
);
});
});
describe('positions', { tags: '@regression', testIsolation: true }, () => {
beforeEach(() => {
cy.mockTradingPage();
cy.mockSubscription();
cy.setVegaWallet();
});
it('rows should be displayed despite errors', () => {
const errors = [
{
@@ -169,9 +164,8 @@ describe('positions', { tags: '@regression', testIsolation: true }, () => {
);
});
// let elementWidth: number;
it('Resize column', () => {
let elementWidth: number;
visitAndClickPositions();
cy.get('.ag-overlay-loading-wrapper').should('not.be.visible');
cy.get('.ag-header-container').within(() => {
@@ -186,33 +180,29 @@ describe('positions', { tags: '@regression', testIsolation: true }, () => {
cy.get(`[col-id="marketName"]`)
.invoke('width')
.should('be.greaterThan', 250);
});
cy.get(`[col-id="marketName"]`)
.invoke('width')
.then((width) => {
elementWidth = width as number;
})
.then(() => {
let localStorageCopy: Record<string, string>;
cy.window().then((win) => {
localStorageCopy = { ...win.localStorage };
});
// This test depends on the previous one
it('Has persisted column widths', () => {
const width = 400;
cy.reload();
cy.window().then((win) => {
Object.keys(localStorageCopy).forEach((key) => {
win.localStorage.setItem(key, localStorageCopy[key]);
});
});
cy.window().then((win) => {
win.localStorage.setItem(
'vega_positions_store',
JSON.stringify({
state: {
gridStore: {
columnState: [{ colId: 'marketName', width }],
},
},
})
);
});
visitAndClickPositions();
// 7004-POSI-012
cy.get('.ag-center-cols-container .ag-row')
.first()
.find('[col-id="marketName"]')
.invoke('outerWidth')
.should('equal', width);
// 7004-POSI-012
cy.get('[col-id="marketName"]')
.invoke('width')
.should('equal', elementWidth);
});
});
it('Scroll horizontally', () => {
@@ -301,43 +291,48 @@ describe('positions', { tags: '@regression', testIsolation: true }, () => {
cy.getByTestId(dialogContent).should('be.visible');
});
});
function validatePositionsDisplayed(multiKey = false) {
cy.getByTestId('tab-positions').should('be.visible');
cy.getByTestId('tab-positions')
.get('.ag-center-cols-container .ag-row')
.first()
.within(() => {
cy.get('[col-id="marketName"]')
.should('be.visible')
.invoke('text')
.should('not.be.empty');
cy.getByTestId('tab-positions').within(() => {
cy.get('[col-id="marketName"]')
.should('be.visible')
.each(($marketSymbol) => {
cy.wrap($marketSymbol).invoke('text').should('not.be.empty');
});
cy.get('[col-id="openVolume"]').should('not.be.empty');
// includes average entry price, mark price, realised PNL & leverage
cy.getByTestId('flash-cell').should('not.be.empty');
if (!multiKey) {
cy.get('[col-id="currentLeverage"]').should('contain.text', '2,767.3');
cy.get('[col-id="marginAccountBalance"]') // margin allocated
.should('contain.text', '0.01');
cy.get('.ag-center-cols-container [col-id="openVolume"]').each(
($openVolume) => {
cy.wrap($openVolume).invoke('text').should('not.be.empty');
}
);
cy.get('[col-id="unrealisedPNL"]').should('not.be.empty');
cy.get('[col-id="notional"]').should('contain.text', '276,761.40348'); // Total tDAI position
cy.get('[col-id="realisedPNL"]').should('contain.text', '2.30'); // Total Realised PNL
cy.get('[col-id="unrealisedPNL"]').should('contain.text', '8.95'); // Total Unrealised PNL
// includes average entry price, mark price, realised PNL & leverage
cy.getByTestId('flash-cell').each(($prices) => {
cy.wrap($prices).invoke('text').should('not.be.empty');
});
cy.get('.ag-header-row [col-id="notional"]')
.should('contain.text', 'Notional')
.realHover();
cy.get('.ag-popup').should('contain.text', 'Mark price x open volume');
if (!multiKey) {
cy.get('[col-id="currentLeverage"]').should('contain.text', '2.846.1');
cy.get('[col-id="marginAccountBalance"]') // margin allocated
.should('contain.text', '0.01');
}
cy.get('[col-id="unrealisedPNL"]').each(($unrealisedPnl) => {
cy.wrap($unrealisedPnl).invoke('text').should('not.be.empty');
});
cy.get('[col-id="notional"]').should('contain.text', '276,761.40348'); // Total tDAI position
cy.get('[col-id="realisedPNL"]').should('contain.text', '2.30'); // Total Realised PNL
cy.get('[col-id="unrealisedPNL"]').should('contain.text', '8.95'); // Total Unrealised PNL
cy.get('.ag-header-row [col-id="notional"]')
.should('contain.text', 'Notional')
.realHover();
cy.get('.ag-popup').should('contain.text', 'Mark price x open volume');
});
cy.getByTestId('close-position').should('be.visible').and('have.length', 3);
}
function assertPNLColor(
pnlSelector: string,
positiveClass: string,
@@ -359,7 +354,6 @@ function assertPNLColor(
}
});
}
function visitAndClickPositions() {
cy.visit('/#/markets/market-0');
cy.getByTestId(positions).click();
@@ -1,79 +1,32 @@
const colHeader = '.ag-header-cell-text';
const colIdPrice = '[col-id=price]';
const colIdSize = '[col-id=size]';
const colIdCreatedAt = '[col-id=createdAt]';
const tradesTab = 'Trades';
const tradesTable = 'tab-trades';
beforeEach(() => {
cy.mockTradingPage();
cy.mockSubscription();
cy.visit('/#/markets/market-0');
});
describe('trades', { tags: '@smoke' }, () => {
beforeEach(() => {
cy.mockTradingPage();
cy.mockSubscription();
});
before(() => {
cy.mockTradingPage();
cy.mockSubscription();
cy.visit('/#/markets/market-0');
cy.getByTestId(tradesTab).click();
});
const colIdPrice = 'price';
const colIdSize = 'size';
const colIdCreatedAt = 'createdAt';
it('show trades', () => {
// 6005-THIS-001
// 6005-THIS-002
cy.getByTestId(tradesTab).should('be.visible');
cy.getByTestId(tradesTable).should('be.visible');
cy.getByTestId(tradesTable).should('not.be.empty');
});
it('renders trades', () => {
cy.getByTestId('Trades').click();
cy.getByTestId('tab-trades').should('be.visible');
it('show trades prices', () => {
// 6005-THIS-003
cy.get(`${colIdPrice} ${colHeader}`).first().should('have.text', 'Price');
cy.get(colIdPrice).each(($tradePrice) => {
cy.get(`[col-id=${colIdPrice}]`).each(($tradePrice) => {
cy.wrap($tradePrice).invoke('text').should('not.be.empty');
});
});
it('show trades sizes', () => {
// 6005-THIS-004
cy.get(`${colIdSize} ${colHeader}`).first().should('have.text', 'Size');
cy.get(colIdSize).each(($tradeSize) => {
cy.get(`[col-id=${colIdSize}]`).each(($tradeSize) => {
cy.wrap($tradeSize).invoke('text').should('not.be.empty');
});
});
it('show trades date and time', () => {
// 6005-THIS-005
cy.get(`${colIdCreatedAt} ${colHeader}`).should('have.text', 'Created at');
const dateTimeRegex =
/(\d{1,2})\/(\d{1,2})\/(\d{4}), (\d{1,2}):(\d{1,2}):(\d{1,2})/gm;
cy.get(colIdCreatedAt).each(($tradeDateTime, index) => {
cy.get(`[col-id=${colIdCreatedAt}]`).each(($tradeDateTime, index) => {
if (index != 0) {
//ignore header
cy.wrap($tradeDateTime).invoke('text').should('match', dateTimeRegex);
}
});
});
it('trades are sorted descending by datetime', () => {
// 6005-THIS-006
const dateTimes: Date[] = [];
cy.get(colIdCreatedAt)
.each(($tradeDateTime, index) => {
if (index != 0) {
//ignore header
dateTimes.push(new Date($tradeDateTime.text()));
}
})
.then(() => {
expect(dateTimes).to.deep.equal(
dateTimes.sort((a, b) => b.getTime() - a.getTime())
);
});
});
it('copy price to deal ticket form', () => {
// 6005-THIS-007
cy.get(colIdPrice).last().click();
cy.getByTestId('order-price').should('have.value', '171.16898');
});
});
-8
View File
@@ -31,8 +31,6 @@ import {
protocolUpgradeProposalsQuery,
blockStatisticsQuery,
networkParamQuery,
liquidityProvisionsQuery,
liquidityProviderFeeShareQuery,
} from '@vegaprotocol/mock';
import type { PartialDeep } from 'type-fest';
import type { MarketDataQuery, MarketsQuery } from '@vegaprotocol/markets';
@@ -160,12 +158,6 @@ const mockTradingPage = (
);
aliasGQLQuery(req, 'Trades', tradesQuery());
aliasGQLQuery(req, 'Chart', chartQuery());
aliasGQLQuery(req, 'LiquidityProvisions', liquidityProvisionsQuery());
aliasGQLQuery(
req,
'LiquidityProviderFeeShare',
liquidityProviderFeeShareQuery
);
aliasGQLQuery(req, 'Candles', candlesQuery());
aliasGQLQuery(req, 'Withdrawals', withdrawalsQuery());
aliasGQLQuery(req, 'NetworkParams', networkParamsQuery());
+1 -1
View File
@@ -2,7 +2,7 @@ NX_ETHEREUM_PROVIDER_URL=https://sepolia.infura.io/v3/4f846e79e13f44d1b51bbd7ed9
NX_ETHERSCAN_URL=https://sepolia.etherscan.io
NX_GITHUB_FEEDBACK_URL=https://github.com/vegaprotocol/feedback/discussions
NX_HOSTED_WALLET_URL=https://wallet.testnet.vega.xyz
NX_SENTRY_DSN=https://2ffce43721964aafa78277c50654ece4@o286262.ingest.sentry.io/6300613
NX_TRADING_SENTRY_DSN=https://2ffce43721964aafa78277c50654ece4@o286262.ingest.sentry.io/6300613
NX_VEGA_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/networks-internal/main/devnet1/vegawallet-devnet1.toml
NX_VEGA_ENV=DEVNET
NX_VEGA_EXPLORER_URL=https://dev.explorer.vega.xyz
+1 -1
View File
@@ -1,7 +1,7 @@
NX_ETHEREUM_PROVIDER_URL=https://eth-mainnet.gateway.pokt.network/v1/lb/af6a2d529a11f8158bc8ca2a
NX_ETHERSCAN_URL=https://etherscan.io
NX_GITHUB_FEEDBACK_URL=https://github.com/vegaprotocol/feedback/discussions
NX_SENTRY_DSN=https://2ffce43721964aafa78277c50654ece4@o286262.ingest.sentry.io/6300613
NX_TRADING_SENTRY_DSN=https://2ffce43721964aafa78277c50654ece4@o286262.ingest.sentry.io/6300613
NX_VEGA_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/networks/master/mainnet1/mainnet1.toml
NX_VEGA_ENV=MAINNET
NX_VEGA_EXPLORER_URL=https://explorer.vega.xyz
+1 -1
View File
@@ -2,7 +2,7 @@ NX_ETHEREUM_PROVIDER_URL=https://sepolia.infura.io/v3/4f846e79e13f44d1b51bbd7ed9
NX_ETHERSCAN_URL=https://sepolia.etherscan.io
NX_GITHUB_FEEDBACK_URL=https://github.com/vegaprotocol/feedback/discussions
NX_HOSTED_WALLET_URL=https://wallet.testnet.vega.xyz
NX_SENTRY_DSN=https://2ffce43721964aafa78277c50654ece4@o286262.ingest.sentry.io/6300613
NX_TRADING_SENTRY_DSN=https://2ffce43721964aafa78277c50654ece4@o286262.ingest.sentry.io/6300613
NX_VEGA_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/networks-internal/main/stagnet1/vegawallet-stagnet1.toml
NX_VEGA_ENV=STAGNET1
NX_VEGA_EXPLORER_URL=https://explorer.stagnet1.vega.rocks
+1 -1
View File
@@ -2,7 +2,7 @@ NX_ETHEREUM_PROVIDER_URL=https://sepolia.infura.io/v3/4f846e79e13f44d1b51bbd7ed9
NX_ETHERSCAN_URL=https://sepolia.etherscan.io
NX_GITHUB_FEEDBACK_URL=https://github.com/vegaprotocol/feedback/discussions
NX_HOSTED_WALLET_URL=https://wallet.testnet.vega.xyz
NX_SENTRY_DSN=https://2ffce43721964aafa78277c50654ece4@o286262.ingest.sentry.io/6300613
NX_TRADING_SENTRY_DSN=https://2ffce43721964aafa78277c50654ece4@o286262.ingest.sentry.io/6300613
NX_VEGA_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/networks-internal/main/fairground/vegawallet-fairground.toml
NX_VEGA_ENV=TESTNET
NX_VEGA_EXPLORER_URL=https://explorer.fairground.wtf
+1 -1
View File
@@ -2,7 +2,7 @@ NX_ETHEREUM_PROVIDER_URL=https://sepolia.infura.io/v3/4f846e79e13f44d1b51bbd7ed9
NX_ETHERSCAN_URL=https://sepolia.etherscan.io
NX_GITHUB_FEEDBACK_URL=https://github.com/vegaprotocol/feedback/discussions
NX_HOSTED_WALLET_URL=https://wallet.testnet.vega.xyz
NX_SENTRY_DSN=https://2ffce43721964aafa78277c50654ece4@o286262.ingest.sentry.io/6300613
NX_TRADING_SENTRY_DSN=https://2ffce43721964aafa78277c50654ece4@o286262.ingest.sentry.io/6300613
NX_VEGA_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/networks/master/testnet2/testnet2.toml
NX_VEGA_ENV=VALIDATOR_TESTNET
NX_VEGA_EXPLORER_URL=https://explorer.validators-testnet.vega.rocks
@@ -1,5 +1,7 @@
import {
matchFilter,
liquidityProvisionsDataProvider,
LiquidityTable,
lpAggregatedDataProvider,
useCheckLiquidityStatus,
} from '@vegaprotocol/liquidity';
@@ -22,16 +24,18 @@ import {
ExternalLink,
} from '@vegaprotocol/ui-toolkit';
import { useVegaWallet } from '@vegaprotocol/wallet';
import { memo, useEffect, useState } from 'react';
import { memo, useEffect, useRef, useState } from 'react';
import { Header, HeaderStat, HeaderTitle } from '../../components/header';
import type { AgGridReact } from 'ag-grid-react';
import type { Filter } from '@vegaprotocol/liquidity';
import { Link, useParams } from 'react-router-dom';
import { Links, Routes } from '../../pages/client-router';
import { useMarket, useStaticMarketData } from '@vegaprotocol/markets';
import { DocsLinks } from '@vegaprotocol/environment';
import { LiquidityContainer } from '../../components/liquidity-container';
const enum LiquidityTabs {
Active = 'active',
@@ -45,6 +49,62 @@ export const Liquidity = () => {
return <LiquidityViewContainer marketId={marketId} />;
};
const useReloadLiquidityData = (marketId: string | undefined) => {
const { reload } = useDataProvider({
dataProvider: liquidityProvisionsDataProvider,
variables: { marketId: marketId || '' },
update: () => true,
skip: !marketId,
});
useEffect(() => {
const interval = setInterval(reload, 30000);
return () => clearInterval(interval);
}, [reload]);
};
export const LiquidityContainer = ({
marketId,
filter,
}: {
marketId: string | undefined;
filter?: Filter;
}) => {
const gridRef = useRef<AgGridReact | null>(null);
const { data: market } = useMarket(marketId);
// To be removed when liquidityProvision subscriptions are working
useReloadLiquidityData(marketId);
const { data, error } = useDataProvider({
dataProvider: lpAggregatedDataProvider,
variables: { marketId: marketId || '', filter },
skip: !marketId,
});
const assetDecimalPlaces =
market?.tradableInstrument.instrument.product.settlementAsset.decimals || 0;
const symbol =
market?.tradableInstrument.instrument.product.settlementAsset.symbol;
const { params } = useNetworkParams([
NetworkParams.market_liquidity_stakeToCcyVolume,
]);
const stakeToCcyVolume = params.market_liquidity_stakeToCcyVolume;
return (
<div className="h-full relative">
<LiquidityTable
ref={gridRef}
rowData={data}
symbol={symbol}
assetDecimalPlaces={assetDecimalPlaces}
stakeToCcyVolume={stakeToCcyVolume}
overlayNoRowsTemplate={error ? error.message : t('No data')}
/>
</div>
);
};
const LiquidityViewHeader = memo(({ marketId }: { marketId?: string }) => {
const { data: market } = useMarket(marketId);
const { data: marketData } = useStaticMarketData(marketId);
@@ -90,7 +150,6 @@ const LiquidityViewHeader = memo(({ marketId }: { marketId?: string }) => {
<HeaderStat
heading={t('Target stake')}
description={tooltipMapping['targetStake']}
testId="target-stake"
>
<div>
{targetStake
@@ -104,7 +163,6 @@ const LiquidityViewHeader = memo(({ marketId }: { marketId?: string }) => {
<HeaderStat
heading={t('Supplied stake')}
description={tooltipMapping['suppliedStake']}
testId="supplied-stake"
>
<div>
{suppliedStake
@@ -120,10 +178,10 @@ const LiquidityViewHeader = memo(({ marketId }: { marketId?: string }) => {
{formatNumberPercentage(percentage, 2)}
</HeaderStat>
<HeaderStat heading={t('Market ID')} testId="liquidity-market-id">
<HeaderStat heading={t('Market ID')}>
<div className="break-word">{marketId}</div>
</HeaderStat>
<HeaderStat heading={t('Learn more')} testId="liquidity-learn-more">
<HeaderStat heading={t('Learn more')}>
{DocsLinks ? (
<ExternalLink href={DocsLinks.LIQUIDITY}>
{t('Providing liquidity')}
@@ -8,7 +8,7 @@ import {
VegaIconNames,
} from '@vegaprotocol/ui-toolkit';
import type { CSSProperties } from 'react';
import { useCallback, useState, useMemo } from 'react';
import { useState } from 'react';
import { Link } from 'react-router-dom';
import { FixedSizeList } from 'react-window';
import AutoSizer from 'react-virtualized-auto-sizer';
@@ -151,11 +151,7 @@ export const MarketSelector = ({
</div>
<div className="px-4 py-2">
<span className="inline-block border-b border-black dark:border-white">
<Link
to={'/markets/all'}
data-testid="all-markets-link"
className="flex items-center gap-x-2"
>
<Link to={'/markets/all'} className="flex items-center gap-x-2">
{t('All markets')}
<VegaIcon name={VegaIconNames.ARROW_RIGHT} />
</Link>
@@ -184,6 +180,7 @@ const MarketList = ({
if (error) {
return <div>{error.message}</div>;
}
return (
<AutoSizer>
{({ width, height }) => (
@@ -203,29 +200,6 @@ const MarketList = ({
);
};
interface ListItemData {
data: MarketMaybeWithDataAndCandles[];
onSelect?: (marketId: string) => void;
currentMarketId?: string;
}
const ListItem = ({
index,
style,
data,
}: {
index: number;
style: CSSProperties;
data: ListItemData;
}) => (
<MarketSelectorItem
market={data.data[index]}
currentMarketId={data.currentMarketId}
style={style}
onSelect={data.onSelect}
/>
);
const List = ({
data,
loading,
@@ -234,20 +208,28 @@ const List = ({
onSelect,
noItems,
currentMarketId,
}: ListItemData & {
}: {
data: MarketMaybeWithDataAndCandles[];
loading: boolean;
width: number;
height: number;
noItems: string;
onSelect?: (marketId: string) => void;
currentMarketId?: string;
}) => {
const itemKey = useCallback(
(index: number, data: ListItemData) => data.data[index].id,
[]
);
const itemData = useMemo(
() => ({ data, onSelect, currentMarketId }),
[data, onSelect, currentMarketId]
);
const row = ({ index, style }: { index: number; style: CSSProperties }) => {
const market = data[index];
return (
<MarketSelectorItem
market={market}
currentMarketId={currentMarketId}
style={style}
onSelect={onSelect}
/>
);
};
if (!data || loading) {
return (
<div style={{ width, height }}>
@@ -273,13 +255,11 @@ const List = ({
<FixedSizeList
className="virtualized-list"
itemCount={data.length}
itemData={itemData}
itemSize={130}
itemKey={itemKey}
width={width}
height={height}
>
{ListItem}
{row}
</FixedSizeList>
);
};
@@ -19,7 +19,10 @@ import {
VegaIcon,
VegaIconNames,
} from '@vegaprotocol/ui-toolkit';
import { useMarketClickHandler } from '../../lib/hooks/use-market-click-handler';
import {
useMarketClickHandler,
useMarketLiquidityClickHandler,
} from '../../lib/hooks/use-market-click-handler';
import { VegaWalletContainer } from '../../components/vega-wallet-container';
import { HeaderTitle } from '../../components/header';
import {
@@ -46,6 +49,7 @@ const MarketBottomPanel = memo(
const [sizes, handleOnLayoutChange] = usePaneLayout({ id: 'bottom' });
const { screenSize } = useScreenDimensions();
const onMarketClick = useMarketClickHandler(true);
const onOrderTypeClick = useMarketLiquidityClickHandler(true);
return 'xxxl' === screenSize ? (
<ResizableGrid
@@ -65,6 +69,10 @@ const MarketBottomPanel = memo(
<TradingViews.orders.component
marketId={marketId}
filter={Filter.Open}
onMarketClick={onMarketClick}
onOrderTypeClick={onOrderTypeClick}
enforceBottomPlaceholder
storeKey="marketOpenOrders"
/>
</VegaWalletContainer>
</Tab>
@@ -73,6 +81,10 @@ const MarketBottomPanel = memo(
<TradingViews.orders.component
marketId={marketId}
filter={Filter.Closed}
onMarketClick={onMarketClick}
onOrderTypeClick={onOrderTypeClick}
enforceBottomPlaceholder
storeKey="marketClosedOrders"
/>
</VegaWalletContainer>
</Tab>
@@ -81,12 +93,22 @@ const MarketBottomPanel = memo(
<TradingViews.orders.component
marketId={marketId}
filter={Filter.Rejected}
onMarketClick={onMarketClick}
onOrderTypeClick={onOrderTypeClick}
enforceBottomPlaceholder
storeKey="marketRejectOrders"
/>
</VegaWalletContainer>
</Tab>
<Tab id="orders" name={t('All')}>
<VegaWalletContainer>
<TradingViews.orders.component marketId={marketId} />
<TradingViews.orders.component
marketId={marketId}
onMarketClick={onMarketClick}
onOrderTypeClick={onOrderTypeClick}
enforceBottomPlaceholder
storeKey="marketAllOrders"
/>
</VegaWalletContainer>
</Tab>
<Tab id="fills" name={t('Fills')}>
@@ -94,6 +116,7 @@ const MarketBottomPanel = memo(
<TradingViews.fills.component
marketId={marketId}
onMarketClick={onMarketClick}
storeKey="marketFills"
/>
</VegaWalletContainer>
</Tab>
@@ -111,6 +134,8 @@ const MarketBottomPanel = memo(
<VegaWalletContainer>
<TradingViews.positions.component
onMarketClick={onMarketClick}
noBottomPlaceholder
storeKey="marketPositions"
/>
</VegaWalletContainer>
</Tab>
@@ -118,8 +143,9 @@ const MarketBottomPanel = memo(
<VegaWalletContainer>
<TradingViews.collateral.component
pinnedAsset={pinnedAsset}
onMarketClick={onMarketClick}
noBottomPlaceholder
hideButtons
storeKey="marketCollateral"
/>
</VegaWalletContainer>
</Tab>
@@ -132,7 +158,10 @@ const MarketBottomPanel = memo(
<Tabs storageKey="console-trade-grid-bottom">
<Tab id="positions" name={t('Positions')}>
<VegaWalletContainer>
<TradingViews.positions.component onMarketClick={onMarketClick} />
<TradingViews.positions.component
onMarketClick={onMarketClick}
storeKey="marketPositions"
/>
</VegaWalletContainer>
</Tab>
<Tab id="open-orders" name={t('Open')}>
@@ -140,6 +169,10 @@ const MarketBottomPanel = memo(
<TradingViews.orders.component
marketId={marketId}
filter={Filter.Open}
onMarketClick={onMarketClick}
onOrderTypeClick={onOrderTypeClick}
enforceBottomPlaceholder
storeKey="marketOpenOrders"
/>
</VegaWalletContainer>
</Tab>
@@ -148,6 +181,10 @@ const MarketBottomPanel = memo(
<TradingViews.orders.component
marketId={marketId}
filter={Filter.Closed}
onMarketClick={onMarketClick}
onOrderTypeClick={onOrderTypeClick}
enforceBottomPlaceholder
storeKey="marketClosedOrders"
/>
</VegaWalletContainer>
</Tab>
@@ -156,12 +193,22 @@ const MarketBottomPanel = memo(
<TradingViews.orders.component
marketId={marketId}
filter={Filter.Rejected}
onMarketClick={onMarketClick}
onOrderTypeClick={onOrderTypeClick}
enforceBottomPlaceholder
storeKey="marketRejectedOrders"
/>
</VegaWalletContainer>
</Tab>
<Tab id="orders" name={t('All')}>
<VegaWalletContainer>
<TradingViews.orders.component marketId={marketId} />
<TradingViews.orders.component
marketId={marketId}
onMarketClick={onMarketClick}
onOrderTypeClick={onOrderTypeClick}
enforceBottomPlaceholder
storeKey="marketAllOrders"
/>
</VegaWalletContainer>
</Tab>
<Tab id="fills" name={t('Fills')}>
@@ -169,6 +216,7 @@ const MarketBottomPanel = memo(
<TradingViews.fills.component
marketId={marketId}
onMarketClick={onMarketClick}
storeKey="marketFills"
/>
</VegaWalletContainer>
</Tab>
@@ -176,8 +224,8 @@ const MarketBottomPanel = memo(
<VegaWalletContainer>
<TradingViews.collateral.component
pinnedAsset={pinnedAsset}
onMarketClick={onMarketClick}
hideButtons
storeKey="marketCollateral"
/>
</VegaWalletContainer>
</Tab>
@@ -201,7 +249,6 @@ const MainGrid = memo(
const [sizesMiddle, handleOnMiddleLayoutChange] = usePaneLayout({
id: 'middle-1',
});
const onMarketClick = useMarketClickHandler(true);
return (
<ResizableGrid vertical onChange={handleOnLayoutChange}>
@@ -220,7 +267,6 @@ const MainGrid = memo(
<Tab id="ticket" name={t('Ticket')}>
<TradingViews.ticket.component
marketId={marketId}
onMarketClick={onMarketClick}
onClickCollateral={() => navigate('/portfolio')}
/>
</Tab>
@@ -39,7 +39,7 @@ export const TradePanels = ({
}: TradePanelsProps) => {
const [drawerOpen, setDrawerOpen] = useState(false);
const onMarketClick = useMarketClickHandler(true);
const onOrderTypeClick = useMarketLiquidityClickHandler();
const onOrderTypeClick = useMarketLiquidityClickHandler(true);
const [view, setView] = useState<TradingView>('candles');
const renderView = () => {
@@ -1,19 +1,18 @@
import type { ComponentProps } from 'react';
import { Splash } from '@vegaprotocol/ui-toolkit';
import { DealTicketContainer } from '@vegaprotocol/deal-ticket';
import { MarketInfoAccordionContainer } from '@vegaprotocol/markets';
import { OrderbookContainer } from '@vegaprotocol/market-depth';
import { OrderListContainer, Filter } from '@vegaprotocol/orders';
import type { OrderListContainerProps } from '@vegaprotocol/orders';
import { FillsContainer } from '@vegaprotocol/fills';
import { PositionsContainer } from '@vegaprotocol/positions';
import { TradesContainer } from '@vegaprotocol/trades';
import type { ComponentProps } from 'react';
import { DepthChartContainer } from '@vegaprotocol/market-depth';
import { CandlesChartContainer } from '@vegaprotocol/candles-chart';
import { OrderbookContainer } from '@vegaprotocol/market-depth';
import { Filter } from '@vegaprotocol/orders';
import { NO_MARKET } from './constants';
import { FillsContainer } from '../../components/fills-container';
import { PositionsContainer } from '../../components/positions-container';
import { Splash } from '@vegaprotocol/ui-toolkit';
import { AccountsContainer } from '../../components/accounts-container';
import { LiquidityContainer } from '../../components/liquidity-container';
import type { OrderContainerProps } from '../../components/orders-container';
import { OrdersContainer } from '../../components/orders-container';
import { NO_MARKET } from './constants';
import { LiquidityContainer } from '../liquidity/liquidity';
type MarketDependantView =
| typeof CandlesChartContainer
@@ -66,25 +65,25 @@ export const TradingViews = {
positions: { label: 'Positions', component: PositionsContainer },
activeOrders: {
label: 'Active',
component: (props: OrderContainerProps) => (
<OrdersContainer {...props} filter={Filter.Open} />
component: (props: OrderListContainerProps) => (
<OrderListContainer {...props} filter={Filter.Open} />
),
},
closedOrders: {
label: 'Closed',
component: (props: OrderContainerProps) => (
<OrdersContainer {...props} filter={Filter.Closed} />
component: (props: OrderListContainerProps) => (
<OrderListContainer {...props} filter={Filter.Closed} />
),
},
rejectedOrders: {
label: 'Rejected',
component: (props: OrderContainerProps) => (
<OrdersContainer {...props} filter={Filter.Rejected} />
component: (props: OrderListContainerProps) => (
<OrderListContainer {...props} filter={Filter.Rejected} />
),
},
orders: {
label: 'All',
component: OrdersContainer,
component: OrderListContainer,
},
collateral: { label: 'Collateral', component: AccountsContainer },
fills: { label: 'Fills', component: FillsContainer },
@@ -313,6 +313,7 @@ const ClosedMarketsDataGrid = ({
minWidth: 100,
}}
overlayNoRowsTemplate={error ? error.message : t('No markets')}
storeKey="closedMarkets"
/>
);
};
@@ -2,6 +2,7 @@ import { Button } from '@vegaprotocol/ui-toolkit';
import { useDepositDialog, DepositsTable } from '@vegaprotocol/deposits';
import { depositsProvider } from '@vegaprotocol/deposits';
import { t } from '@vegaprotocol/i18n';
import { useBottomPlaceholder } from '@vegaprotocol/datagrid';
import { useDataProvider } from '@vegaprotocol/data-provider';
import { useVegaWallet } from '@vegaprotocol/wallet';
import { useRef } from 'react';
@@ -16,13 +17,17 @@ export const DepositsContainer = () => {
skip: !pubKey,
});
const openDepositDialog = useDepositDialog((state) => state.open);
const bottomPlaceholderProps = useBottomPlaceholder({ gridRef });
return (
<div className="h-full">
<DepositsTable
rowData={data}
ref={gridRef}
overlayNoRowsTemplate={error ? error.message : t('No deposits')}
/>
<div className="h-full relative">
<DepositsTable
rowData={data || []}
ref={gridRef}
{...bottomPlaceholderProps}
overlayNoRowsTemplate={error ? error.message : t('No deposits')}
/>
</div>
{!isReadOnly && (
<div className="h-auto flex justify-end px-[11px] py-2 bottom-0 right-3 absolute dark:bg-black/75 bg-white/75 rounded">
<Button
@@ -1,39 +1,29 @@
import { useEffect } from 'react';
import type { ReactNode } from 'react';
import { LayoutPriority } from 'allotment';
import { titlefy } from '@vegaprotocol/utils';
import { t } from '@vegaprotocol/i18n';
import { useIncompleteWithdrawals } from '@vegaprotocol/withdraws';
import { usePaneLayout } from '@vegaprotocol/react-helpers';
import { PositionsContainer } from '@vegaprotocol/positions';
import { OrderListContainer } from '@vegaprotocol/orders';
import { Tab, LocalStoragePersistTabs as Tabs } from '@vegaprotocol/ui-toolkit';
import { usePageTitleStore } from '../../stores';
import { useMarketClickHandler } from '../../lib/hooks/use-market-click-handler';
import { AccountsContainer } from '../../components/accounts-container';
import { DepositsContainer } from './deposits-container';
import { FillsContainer } from '../../components/fills-container';
import { PositionsContainer } from '../../components/positions-container';
import { WithdrawalsContainer } from './withdrawals-container';
import { OrdersContainer } from '../../components/orders-container';
import { FillsContainer } from '@vegaprotocol/fills';
import type { ReactNode } from 'react';
import { useEffect } from 'react';
import { usePaneLayout } from '@vegaprotocol/react-helpers';
import { VegaWalletContainer } from '../../components/vega-wallet-container';
import { LedgerContainer } from '../../components/ledger-container';
import { DepositsContainer } from './deposits-container';
import { LayoutPriority } from 'allotment';
import { usePageTitleStore } from '../../stores';
import { LedgerContainer } from '@vegaprotocol/ledger';
import { AccountsContainer } from '../../components/accounts-container';
import { AccountHistoryContainer } from './account-history-container';
import {
useMarketClickHandler,
useMarketLiquidityClickHandler,
} from '../../lib/hooks/use-market-click-handler';
import {
ResizableGrid,
ResizableGridPanel,
} from '../../components/resizable-grid';
const WithdrawalsIndicator = () => {
const { ready } = useIncompleteWithdrawals();
if (!ready || ready.length === 0) {
return null;
}
return (
<span className="bg-vega-blue-450 text-white text-[10px] rounded p-[3px] pb-[2px] leading-none">
{ready.length}
</span>
);
};
export const Portfolio = () => {
const { updateTitle } = usePageTitleStore((store) => ({
updateTitle: store.updateTitle,
@@ -44,6 +34,7 @@ export const Portfolio = () => {
}, [updateTitle]);
const onMarketClick = useMarketClickHandler(true);
const onOrderTypeClick = useMarketLiquidityClickHandler(true);
const [sizes, handleOnLayoutChange] = usePaneLayout({ id: 'portfolio' });
const wrapperClasses = 'h-full max-h-full flex flex-col';
return (
@@ -59,17 +50,29 @@ export const Portfolio = () => {
</Tab>
<Tab id="positions" name={t('Positions')}>
<VegaWalletContainer>
<PositionsContainer onMarketClick={onMarketClick} allKeys />
<PositionsContainer
onMarketClick={onMarketClick}
noBottomPlaceholder
storeKey="portfolioPositions"
allKeys
/>
</VegaWalletContainer>
</Tab>
<Tab id="orders" name={t('Orders')}>
<VegaWalletContainer>
<OrdersContainer />
<OrderListContainer
onMarketClick={onMarketClick}
onOrderTypeClick={onOrderTypeClick}
storeKey="portfolioOrders"
/>
</VegaWalletContainer>
</Tab>
<Tab id="fills" name={t('Fills')}>
<VegaWalletContainer>
<FillsContainer onMarketClick={onMarketClick} />
<FillsContainer
onMarketClick={onMarketClick}
storeKey="portfolioFills"
/>
</VegaWalletContainer>
</Tab>
<Tab id="ledger-entries" name={t('Ledger entries')}>
@@ -89,7 +92,7 @@ export const Portfolio = () => {
<Tabs storageKey="console-portfolio-bottom">
<Tab id="collateral" name={t('Collateral')}>
<VegaWalletContainer>
<AccountsContainer />
<AccountsContainer storeKey="portfolioCollateral" />
</VegaWalletContainer>
</Tab>
<Tab id="deposits" name={t('Deposits')}>
@@ -97,11 +100,7 @@ export const Portfolio = () => {
<DepositsContainer />
</VegaWalletContainer>
</Tab>
<Tab
id="withdrawals"
name={t('Withdrawals')}
indicator={<WithdrawalsIndicator />}
>
<Tab id="withdrawals" name={t('Withdrawals')}>
<WithdrawalsContainer />
</Tab>
</Tabs>
@@ -3,7 +3,6 @@ import {
withdrawalProvider,
useWithdrawalDialog,
WithdrawalsTable,
useIncompleteWithdrawals,
} from '@vegaprotocol/withdraws';
import { useVegaWallet } from '@vegaprotocol/wallet';
import { t } from '@vegaprotocol/i18n';
@@ -18,7 +17,6 @@ export const WithdrawalsContainer = () => {
skip: !pubKey,
});
const openWithdrawDialog = useWithdrawalDialog((state) => state.open);
const { ready, delayed } = useIncompleteWithdrawals();
return (
<VegaWalletContainer>
@@ -27,8 +25,6 @@ export const WithdrawalsContainer = () => {
data-testid="withdrawals-history"
rowData={data}
overlayNoRowsTemplate={error ? error.message : t('No withdrawals')}
ready={ready}
delayed={delayed}
/>
</div>
{!isReadOnly && (
@@ -8,20 +8,17 @@ import { useVegaWallet } from '@vegaprotocol/wallet';
import type { PinnedAsset } from '@vegaprotocol/accounts';
import { AccountManager, useTransferDialog } from '@vegaprotocol/accounts';
import { useDepositDialog } from '@vegaprotocol/deposits';
import { create } from 'zustand';
import { persist } from 'zustand/middleware';
import { useDataGridEvents } from '@vegaprotocol/datagrid';
import type { DataGridSlice } from '../../stores/datagrid-store-slice';
import { createDataGridSlice } from '../../stores/datagrid-store-slice';
export const AccountsContainer = ({
pinnedAsset,
hideButtons,
onMarketClick,
noBottomPlaceholder,
storeKey,
}: {
pinnedAsset?: PinnedAsset;
hideButtons?: boolean;
onMarketClick?: (marketId: string, metaKey?: boolean) => void;
noBottomPlaceholder?: boolean;
storeKey?: string;
}) => {
const { pubKey, isReadOnly } = useVegaWallet();
const { open: openAssetDetailsDialog } = useAssetDetailsDialogStore();
@@ -29,12 +26,6 @@ export const AccountsContainer = ({
const openDepositDialog = useDepositDialog((store) => store.open);
const openTransferDialog = useTransferDialog((store) => store.open);
const gridStore = useAccountStore((store) => store.gridStore);
const updateGridStore = useAccountStore((store) => store.updateGridStore);
const gridStoreCallbacks = useDataGridEvents(gridStore, (colState) => {
updateGridStore(colState);
});
const onClickAsset = useCallback(
(assetId?: string) => {
assetId && openAssetDetailsDialog(assetId);
@@ -57,10 +48,10 @@ export const AccountsContainer = ({
onClickAsset={onClickAsset}
onClickWithdraw={openWithdrawalDialog}
onClickDeposit={openDepositDialog}
onMarketClick={onMarketClick}
isReadOnly={isReadOnly}
pinnedAsset={pinnedAsset}
gridProps={gridStoreCallbacks}
noBottomPlaceholder={noBottomPlaceholder}
storeKey={storeKey}
/>
{!isReadOnly && !hideButtons && (
<div className="flex gap-2 justify-end p-2 px-[11px] absolute lg:fixed bottom-0 right-3 dark:bg-black/75 bg-white/75 rounded">
@@ -84,9 +75,3 @@ export const AccountsContainer = ({
</div>
);
};
const useAccountStore = create<DataGridSlice>()(
persist(createDataGridSlice, {
name: 'vega_accounts_store',
})
);
@@ -49,9 +49,6 @@ export const AppLoader = ({ children }: { children: ReactNode }) => {
const cacheConfig: InMemoryCacheConfig = {
typePolicies: {
Statistics: {
merge: true,
},
Account: {
keyFields: false,
fields: {
@@ -83,6 +80,12 @@ const cacheConfig: InMemoryCacheConfig = {
ERC20: {
keyFields: ['contractAddress'],
},
PositionUpdate: {
keyFields: false,
},
AccountUpdate: {
keyFields: false,
},
Party: {
keyFields: false,
},
@@ -92,16 +95,8 @@ const cacheConfig: InMemoryCacheConfig = {
Fees: {
keyFields: false,
},
// The folling types are cached by the data provider and not by apollo
PositionUpdate: {
keyFields: false,
},
TradeUpdate: {
keyFields: false,
},
AccountUpdate: {
keyFields: false,
},
// Don't cache order update as this subscription result gets merged into the main order cache
// We don't need to write these to the cache at all
OrderUpdate: {
keyFields: false,
},
@@ -1,16 +1,13 @@
import type { DefaultWeb3ProviderContextShape } from '@vegaprotocol/web3';
import {
useEthereumConfig,
createConnectors,
Web3Provider as Web3ProviderInternal,
useWeb3ConnectStore,
createDefaultProvider,
} from '@vegaprotocol/web3';
import { AsyncRenderer } from '@vegaprotocol/ui-toolkit';
import { t } from '@vegaprotocol/i18n';
import { useEnvironment } from '@vegaprotocol/environment';
import type { ReactNode } from 'react';
import { useState } from 'react';
import { useEffect } from 'react';
export const Web3Provider = ({ children }: { children: ReactNode }) => {
@@ -20,13 +17,10 @@ export const Web3Provider = ({ children }: { children: ReactNode }) => {
const connectors = useWeb3ConnectStore((store) => store.connectors);
const initializeConnectors = useWeb3ConnectStore((store) => store.initialize);
const [defaultProvider, setDefaultProvider] = useState<
DefaultWeb3ProviderContextShape['provider'] | undefined
>(undefined);
useEffect(() => {
if (config?.chain_id) {
initializeConnectors(
return initializeConnectors(
createConnectors(
ETHEREUM_PROVIDER_URL,
Number(config?.chain_id),
@@ -35,11 +29,6 @@ export const Web3Provider = ({ children }: { children: ReactNode }) => {
),
Number(config.chain_id)
);
const defaultProvider = createDefaultProvider(
ETHEREUM_PROVIDER_URL,
Number(config?.chain_id)
);
setDefaultProvider(defaultProvider);
}
}, [
config?.chain_id,
@@ -60,10 +49,7 @@ export const Web3Provider = ({ children }: { children: ReactNode }) => {
}}
noDataMessage={t('Could not fetch Ethereum configuration')}
>
<Web3ProviderInternal
connectors={connectors}
defaultProvider={defaultProvider}
>
<Web3ProviderInternal connectors={connectors}>
<>{children}</>
</Web3ProviderInternal>
</AsyncRenderer>
@@ -1,49 +0,0 @@
import { t } from '@vegaprotocol/i18n';
import { Splash } from '@vegaprotocol/ui-toolkit';
import { useVegaWallet } from '@vegaprotocol/wallet';
import { FillsManager } from '@vegaprotocol/fills';
import { create } from 'zustand';
import { persist } from 'zustand/middleware';
import { useDataGridEvents } from '@vegaprotocol/datagrid';
import type { DataGridSlice } from '../../stores/datagrid-store-slice';
import { createDataGridSlice } from '../../stores/datagrid-store-slice';
export const FillsContainer = ({
marketId,
onMarketClick,
}: {
marketId?: string;
onMarketClick?: (marketId: string, metaKey?: boolean) => void;
}) => {
const { pubKey } = useVegaWallet();
const gridStore = useFillsStore((store) => store.gridStore);
const updateGridStore = useFillsStore((store) => store.updateGridStore);
const gridStoreCallbacks = useDataGridEvents(gridStore, (colState) => {
updateGridStore(colState);
});
if (!pubKey) {
return (
<Splash>
<p>{t('Please connect Vega wallet')}</p>
</Splash>
);
}
return (
<FillsManager
partyId={pubKey}
marketId={marketId}
onMarketClick={onMarketClick}
gridProps={gridStoreCallbacks}
/>
);
};
const useFillsStore = create<DataGridSlice>()(
persist(createDataGridSlice, {
name: 'vega_fills_store',
})
);
@@ -1 +0,0 @@
export * from './fills-container';
@@ -1 +0,0 @@
export * from './ledger-container';
@@ -1,36 +0,0 @@
import { useDataGridEvents } from '@vegaprotocol/datagrid';
import { t } from '@vegaprotocol/i18n';
import { LedgerManager } from '@vegaprotocol/ledger';
import { Splash } from '@vegaprotocol/ui-toolkit';
import { useVegaWallet } from '@vegaprotocol/wallet';
import type { DataGridSlice } from '../../stores/datagrid-store-slice';
import { createDataGridSlice } from '../../stores/datagrid-store-slice';
import { create } from 'zustand';
import { persist } from 'zustand/middleware';
export const LedgerContainer = () => {
const { pubKey } = useVegaWallet();
const gridStore = useLedgerStore((store) => store.gridStore);
const updateGridStore = useLedgerStore((store) => store.updateGridStore);
const gridStoreCallbacks = useDataGridEvents(gridStore, (colState) => {
updateGridStore(colState);
});
if (!pubKey) {
return (
<Splash>
<p>{t('Please connect Vega wallet')}</p>
</Splash>
);
}
return <LedgerManager partyId={pubKey} gridProps={gridStoreCallbacks} />;
};
const useLedgerStore = create<DataGridSlice>()(
persist(createDataGridSlice, {
name: 'vega_ledger_store',
})
);
@@ -1 +0,0 @@
export * from './liquidity-container';
@@ -1,93 +0,0 @@
import { useDataProvider } from '@vegaprotocol/data-provider';
import { useDataGridEvents } from '@vegaprotocol/datagrid';
import { t } from '@vegaprotocol/i18n';
import {
lpAggregatedDataProvider,
type Filter,
LiquidityTable,
liquidityProvisionsDataProvider,
} from '@vegaprotocol/liquidity';
import { useMarket } from '@vegaprotocol/markets';
import {
NetworkParams,
useNetworkParams,
} from '@vegaprotocol/network-parameters';
import type { AgGridReact } from 'ag-grid-react';
import type { DataGridSlice } from '../../stores/datagrid-store-slice';
import { createDataGridSlice } from '../../stores/datagrid-store-slice';
import { useEffect, useRef } from 'react';
import { create } from 'zustand';
import { persist } from 'zustand/middleware';
export const LiquidityContainer = ({
marketId,
filter,
}: {
marketId: string | undefined;
filter?: Filter;
}) => {
const gridRef = useRef<AgGridReact | null>(null);
const gridStore = useLiquidityStore((store) => store.gridStore);
const updateGridStore = useLiquidityStore((store) => store.updateGridStore);
const gridStoreCallbacks = useDataGridEvents(gridStore, (colState) => {
updateGridStore(colState);
});
const { data: market } = useMarket(marketId);
// To be removed when liquidityProvision subscriptions are working
useReloadLiquidityData(marketId);
const { data, error } = useDataProvider({
dataProvider: lpAggregatedDataProvider,
variables: { marketId: marketId || '', filter },
skip: !marketId,
});
const assetDecimalPlaces =
market?.tradableInstrument.instrument.product.settlementAsset.decimals || 0;
const quantum =
market?.tradableInstrument.instrument.product.settlementAsset.quantum || 0;
const symbol =
market?.tradableInstrument.instrument.product.settlementAsset.symbol;
const { params } = useNetworkParams([
NetworkParams.market_liquidity_stakeToCcyVolume,
]);
const stakeToCcyVolume = params.market_liquidity_stakeToCcyVolume;
return (
<div className="h-full relative">
<LiquidityTable
ref={gridRef}
rowData={data}
symbol={symbol}
assetDecimalPlaces={assetDecimalPlaces}
quantum={quantum}
stakeToCcyVolume={stakeToCcyVolume}
overlayNoRowsTemplate={error ? error.message : t('No data')}
{...gridStoreCallbacks}
/>
</div>
);
};
const useReloadLiquidityData = (marketId: string | undefined) => {
const { reload } = useDataProvider({
dataProvider: liquidityProvisionsDataProvider,
variables: { marketId: marketId || '' },
update: () => true,
skip: !marketId,
});
useEffect(() => {
const interval = setInterval(reload, 30000);
return () => clearInterval(interval);
}, [reload]);
};
const useLiquidityStore = create<DataGridSlice>()(
persist(createDataGridSlice, {
name: 'vega_ledger_store',
})
);
@@ -125,19 +125,14 @@ export const MarketLiquiditySupplied = ({
</KeyValueTableRow>
</KeyValueTable>
<br />
<div className="flex flex-col gap-2">
<Link
href={`/#/liquidity/${marketId}`}
data-testid="view-liquidity-link"
>
{t('View liquidity provision table')}
</Link>
{DocsLinks && (
<ExternalLink href={DocsLinks.LIQUIDITY} className="mt-2">
{t('Learn about providing liquidity')}
</ExternalLink>
)}
</div>
<Link href={`/#/liquidity/${marketId}`} data-testid="view-liquidity-link">
{t('View liquidity provision table')}
</Link>
{DocsLinks && (
<ExternalLink href={DocsLinks.LIQUIDITY} className="mt-2">
{t('Learn about providing liquidity')}
</ExternalLink>
)}
{showMessage && (
<p className="mt-4">
{t(
@@ -1 +0,0 @@
export * from './orders-container';
@@ -1,106 +0,0 @@
import { act, renderHook } from '@testing-library/react';
import {
FilterStatusValue,
STORAGE_KEY,
useOrderListGridState,
} from './orders-container';
import { Filter } from '@vegaprotocol/orders';
import { OrderType } from '@vegaprotocol/types';
describe('useOrderListGridState', () => {
afterAll(() => {
localStorage.clear();
});
const setup = (filter: Filter | undefined) => {
return renderHook(() => useOrderListGridState(filter));
};
it.each(Object.values(Filter))(
'providers correct AgGrid filter for %s',
(filter) => {
const { result } = setup(filter);
expect(typeof result.current.updateGridState).toBe('function');
expect(result.current.gridState).toEqual({
columnState: undefined,
filterModel: {
status: {
value: FilterStatusValue[filter],
},
},
});
}
);
it('provides correct AgGrid filter for all', () => {
const { result } = setup(undefined);
expect(typeof result.current.updateGridState).toBe('function');
expect(result.current.gridState).toEqual({
columnState: undefined,
filterModel: undefined,
});
});
it.each(Object.values(Filter))(
'sets and stores column state and filters for %s',
(filter) => {
const filterModel = {
type: {
value: [OrderType.TYPE_LIMIT],
},
};
const { result } = setup(filter);
act(() => {
result.current.updateGridState(filter, {
filterModel,
});
});
expect(result.current.gridState).toEqual({
columnState: undefined,
filterModel: {
...filterModel,
status: {
value: FilterStatusValue[filter],
},
},
});
const columnState = [{ colId: 'status', width: 200 }];
act(() => {
result.current.updateGridState(filter, {
columnState,
});
});
expect(result.current.gridState).toEqual({
columnState,
filterModel: {
...filterModel,
status: {
value: FilterStatusValue[filter],
},
},
});
const storeKeyMap = {
[Filter.Open]: 'open',
[Filter.Rejected]: 'rejected',
[Filter.Closed]: 'closed',
};
expect(JSON.parse(localStorage.getItem(STORAGE_KEY) || '')).toMatchObject(
{
state: {
[storeKeyMap[filter]]: {
columnState,
filterModel, // no need to check that status is set, hook will return status
},
},
}
);
}
);
});
@@ -1,166 +0,0 @@
import { useDataGridEvents } from '@vegaprotocol/datagrid';
import { t } from '@vegaprotocol/i18n';
import { Filter } from '@vegaprotocol/orders';
import { OrderListManager } from '@vegaprotocol/orders';
import { Splash } from '@vegaprotocol/ui-toolkit';
import { useVegaWallet } from '@vegaprotocol/wallet';
import {
useMarketClickHandler,
useMarketLiquidityClickHandler,
} from '../../lib/hooks/use-market-click-handler';
import { create } from 'zustand';
import { persist } from 'zustand/middleware';
import type { DataGridStore } from '../../stores/datagrid-store-slice';
import { OrderStatus } from '@vegaprotocol/types';
export const FilterStatusValue = {
[Filter.Open]: [OrderStatus.STATUS_ACTIVE, OrderStatus.STATUS_PARKED],
[Filter.Closed]: [
OrderStatus.STATUS_CANCELLED,
OrderStatus.STATUS_EXPIRED,
OrderStatus.STATUS_FILLED,
OrderStatus.STATUS_PARTIALLY_FILLED,
OrderStatus.STATUS_STOPPED,
],
[Filter.Rejected]: [OrderStatus.STATUS_REJECTED],
};
export interface OrderContainerProps {
marketId?: string;
filter?: Filter;
}
export const OrdersContainer = ({ marketId, filter }: OrderContainerProps) => {
const { pubKey, isReadOnly } = useVegaWallet();
const onMarketClick = useMarketClickHandler(true);
const onOrderTypeClick = useMarketLiquidityClickHandler();
const { gridState, updateGridState } = useOrderListGridState(filter);
const gridStoreCallbacks = useDataGridEvents(gridState, (newState) => {
updateGridState(filter, newState);
});
if (!pubKey) {
return <Splash>{t('Please connect Vega wallet')}</Splash>;
}
return (
<OrderListManager
partyId={pubKey}
marketId={marketId}
filter={filter}
onMarketClick={onMarketClick}
onOrderTypeClick={onOrderTypeClick}
isReadOnly={isReadOnly}
gridProps={gridStoreCallbacks}
/>
);
};
export const STORAGE_KEY = 'vega_order_list_store';
const useOrderListStore = create<{
open: DataGridStore;
closed: DataGridStore;
rejected: DataGridStore;
all: DataGridStore;
update: (filter: Filter | undefined, gridStore: DataGridStore) => void;
}>()(
persist(
(set) => ({
open: {},
closed: {},
rejected: {},
all: {},
update: (filter, newStore) => {
switch (filter) {
case Filter.Open: {
set((curr) => ({
open: {
...curr.open,
...newStore,
},
}));
return;
}
case Filter.Closed: {
set((curr) => ({
closed: {
...curr.closed,
...newStore,
},
}));
return;
}
case Filter.Rejected: {
set((curr) => ({
rejected: {
...curr.rejected,
...newStore,
},
}));
return;
}
case undefined: {
set((curr) => ({
all: {
...curr.all,
...newStore,
},
}));
return;
}
}
},
}),
{
name: STORAGE_KEY,
}
)
);
export const useOrderListGridState = (filter: Filter | undefined) => {
const updateGridState = useOrderListStore((store) => store.update);
const gridState = useOrderListStore((store) => {
// Return the column/filter state for the given filter but ensuring that
// each filter controlled by the tab is always applied
switch (filter) {
case Filter.Open: {
return {
columnState: store.open.columnState,
filterModel: {
...store.open.filterModel,
status: {
value: FilterStatusValue[Filter.Open],
},
},
};
}
case Filter.Closed: {
return {
columnState: store.closed.columnState,
filterModel: {
...store.closed.filterModel,
status: {
value: FilterStatusValue[Filter.Closed],
},
},
};
}
case Filter.Rejected: {
return {
columnState: store.rejected.columnState,
filterModel: {
...store.rejected.filterModel,
status: {
value: FilterStatusValue[Filter.Rejected],
},
},
};
}
default: {
return store.all;
}
}
});
return { gridState, updateGridState };
};
@@ -1 +0,0 @@
export * from './positions-container';
@@ -1,25 +1,17 @@
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { render, screen, act } from '@testing-library/react';
import { TelemetryApproval } from './telemetry-approval';
jest.mock('@vegaprotocol/logger', () => ({
SentryInit: () => undefined,
SentryClose: () => undefined,
}));
jest.mock('@vegaprotocol/environment', () => ({
useEnvironment: () => ({ VEGA_ENV: 'test', SENTRY_DSN: 'sentry-dsn' }),
}));
describe('TelemetryApproval', () => {
it('click on checkbox should be properly handled', async () => {
it('click on checkbox should be properly handled', () => {
const helpText = 'My help text';
render(<TelemetryApproval helpText={helpText} />);
expect(screen.getByRole('checkbox')).toHaveAttribute(
'data-state',
'unchecked'
);
await userEvent.click(screen.getByRole('checkbox'));
act(() => {
screen.getByRole('checkbox').click();
});
expect(screen.getByRole('checkbox')).toHaveAttribute(
'data-state',
'checked'
@@ -11,7 +11,6 @@ import { WelcomeNoticeDialog } from './welcome-notice-dialog';
import { useGlobalStore } from '../../stores';
import { useEnvironment } from '@vegaprotocol/environment';
import { Networks } from '@vegaprotocol/environment';
import { isTestEnv } from '@vegaprotocol/utils';
export const WelcomeDialog = () => {
const { VEGA_ENV } = useEnvironment();
@@ -32,7 +31,9 @@ export const WelcomeDialog = () => {
);
const isRiskDialogNeeded =
riskAccepted !== 'true' && VEGA_ENV !== Networks.MAINNET && !isTestEnv();
riskAccepted !== 'true' &&
VEGA_ENV !== Networks.MAINNET &&
!('Cypress' in window);
const isWelcomeDialogNeeded = pathname === '/' || shouldDisplayWelcomeDialog;
+20
View File
@@ -0,0 +1,20 @@
const windowOrDefault = (key: string, defaultValue?: string) => {
if (typeof window !== 'undefined') {
if (window._env_ && window._env_[key]) {
return window._env_[key];
}
}
return defaultValue || '';
};
/**
* Need to have default value as next in-lines environment variables. Cannot figure out dynamic keys.
* So must provide the default with the key so that next can figure it out.
*/
export const ENV = {
envName: windowOrDefault('NX_VEGA_ENV', process.env['NX_VEGA_ENV']),
dsn: windowOrDefault(
'NX_TRADING_SENTRY_DSN',
process.env['NX_TRADING_SENTRY_DSN']
),
};
+1
View File
@@ -0,0 +1 @@
export * from './env';
@@ -20,8 +20,20 @@ export const useMarketClickHandler = (replace = false) => {
);
};
export const useMarketLiquidityClickHandler = () => {
return useCallback((selectedId: string, metaKey?: boolean) => {
window.open(`/#/liquidity/${selectedId}`, metaKey ? '_blank' : '_self');
}, []);
export const useMarketLiquidityClickHandler = (replace = false) => {
const navigate = useNavigate();
const { marketId } = useParams();
const { pathname } = useLocation();
const isLiquidityPage = pathname.match(/^\/liquidity\/(.+)/);
return useCallback(
(selectedId: string, metaKey?: boolean) => {
const link = Links[Routes.LIQUIDITY](selectedId);
if (metaKey) {
window.open(`/#${link}`, '_blank');
} else if (selectedId !== marketId || !isLiquidityPage) {
navigate(link, { replace });
}
},
[navigate, marketId, replace, isLiquidityPage]
);
};

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