Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e2665ed055 |
@@ -4,54 +4,14 @@ on:
|
||||
push:
|
||||
branches:
|
||||
- release/*
|
||||
- develop
|
||||
pull_request:
|
||||
types:
|
||||
- opened
|
||||
- ready_for_review
|
||||
- reopened
|
||||
- edited
|
||||
- synchronize
|
||||
- ready_for_review
|
||||
jobs:
|
||||
node-modules:
|
||||
runs-on: ubuntu-22.04
|
||||
name: 'Cache yarn modules'
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Cache node modules
|
||||
id: cache
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: node_modules
|
||||
key: ${{ runner.os }}-cache-node-modules-${{ hashFiles('yarn.lock') }}
|
||||
# comment out "restore-keys" if you need to rebuild yarn from 0
|
||||
restore-keys: |
|
||||
${{ runner.os }}-cache-node-modules-
|
||||
|
||||
- name: Setup node
|
||||
uses: actions/setup-node@v3
|
||||
if: steps.cache.outputs.cache-hit != 'true'
|
||||
with:
|
||||
node-version-file: '.nvmrc'
|
||||
# https://stackoverflow.com/questions/61010294/how-to-cache-yarn-packages-in-github-actions
|
||||
cache: yarn
|
||||
|
||||
- name: yarn install
|
||||
if: steps.cache.outputs.cache-hit != 'true'
|
||||
run: yarn install --pure-lockfile
|
||||
|
||||
lint-pr-title:
|
||||
needs: node-modules
|
||||
if: ${{ github.event_name == 'pull_request' }}
|
||||
name: Verify PR title
|
||||
uses: ./.github/workflows/lint-pr.yml
|
||||
secrets: inherit
|
||||
|
||||
lint-test-build:
|
||||
timeout-minutes: 35
|
||||
needs: node-modules
|
||||
runs-on: ubuntu-22.04
|
||||
name: '(CI) lint + unit test + build'
|
||||
steps:
|
||||
@@ -67,11 +27,8 @@ jobs:
|
||||
# https://stackoverflow.com/questions/61010294/how-to-cache-yarn-packages-in-github-actions
|
||||
cache: yarn
|
||||
|
||||
- name: Cache node modules
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: node_modules
|
||||
key: ${{ runner.os }}-cache-node-modules-${{ hashFiles('yarn.lock') }}
|
||||
- name: Install root dependencies
|
||||
run: yarn install --frozen-lockfile
|
||||
|
||||
- name: Derive appropriate SHAs for base and head for `nx affected` commands
|
||||
uses: nrwl/nx-set-shas@v3
|
||||
@@ -91,7 +48,7 @@ jobs:
|
||||
run: yarn nx affected:test
|
||||
|
||||
- name: Build affected
|
||||
run: yarn nx affected:build || (yarn install && yarn nx affected:build)
|
||||
run: yarn nx affected:build
|
||||
|
||||
# See affected apps
|
||||
- name: See affected apps
|
||||
@@ -138,43 +95,6 @@ jobs:
|
||||
with:
|
||||
projects: ${{ needs.lint-test-build.outputs.projects }}
|
||||
|
||||
dist-check:
|
||||
runs-on: ubuntu-latest
|
||||
needs: publish-dist
|
||||
if: ${{ github.event_name == 'pull_request' }}
|
||||
name: '(CD) comment preview links'
|
||||
steps:
|
||||
- name: Find Comment
|
||||
uses: peter-evans/find-comment@v2
|
||||
id: fc
|
||||
with:
|
||||
issue-number: ${{ github.event.pull_request.number }}
|
||||
body-includes: Previews
|
||||
|
||||
- name: Inject slug/short variables
|
||||
if: ${{ steps.fc.outputs.comment-id == 0 }}
|
||||
uses: rlespinasse/github-slug-action@v4
|
||||
with:
|
||||
prefix: CI_
|
||||
|
||||
- name: Create comment
|
||||
if: ${{ steps.fc.outputs.comment-id == 0 }}
|
||||
uses: peter-evans/create-or-update-comment@v3
|
||||
with:
|
||||
issue-number: ${{ github.event.pull_request.number }}
|
||||
body: |
|
||||
Previews
|
||||
- explorer https://explorer.${{ env.CI_GITHUB_REF_NAME }}.vega.rocks
|
||||
- trading https://trading.${{ env.CI_GITHUB_REF_NAME }}.vega.rocks
|
||||
- governance https://governance.${{ env.CI_GITHUB_REF_NAME }}.vega.rocks
|
||||
|
||||
cypress-check:
|
||||
name: '(CI) cypress - check'
|
||||
runs-on: ubuntu-latest
|
||||
needs: cypress
|
||||
steps:
|
||||
- run: echo Done!
|
||||
|
||||
# Report single result at the end, to avoid mess with required checks in PR
|
||||
cypress-result:
|
||||
if: ${{ always() }}
|
||||
|
||||
@@ -20,7 +20,7 @@ jobs:
|
||||
project: ${{ fromJSON(inputs.projects) }}
|
||||
name: ${{ matrix.project }}
|
||||
runs-on: self-hosted-runner
|
||||
timeout-minutes: 40
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
# Checks if skip cache was requested
|
||||
- name: Set skip-nx-cache flag
|
||||
@@ -91,7 +91,5 @@ jobs:
|
||||
- uses: actions/upload-artifact@v3
|
||||
if: ${{ failure() }}
|
||||
with:
|
||||
name: test-report-and-logs-${{ matrix.project }}
|
||||
path: |
|
||||
/home/runner/.vegacapsule/testnet/logs
|
||||
frontend-monorepo/apps/trading-e2e/cypress/reports
|
||||
name: logs-${{ matrix.project }}
|
||||
path: /home/runner/.vegacapsule/testnet/logs
|
||||
|
||||
@@ -2,11 +2,15 @@
|
||||
name: Verify PR title
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
|
||||
pull_request:
|
||||
types:
|
||||
- opened
|
||||
- ready_for_review
|
||||
- reopened
|
||||
- edited
|
||||
- synchronize
|
||||
jobs:
|
||||
lint_pr:
|
||||
timeout-minutes: 10
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Checkout
|
||||
@@ -19,11 +23,8 @@ jobs:
|
||||
# https://stackoverflow.com/questions/61010294/how-to-cache-yarn-packages-in-github-actions
|
||||
cache: yarn
|
||||
|
||||
- name: Cache node modules
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: node_modules
|
||||
key: ${{ runner.os }}-cache-node-modules-${{ hashFiles('yarn.lock') }}
|
||||
- name: Install root dependencies
|
||||
run: yarn install --frozen-lockfile
|
||||
|
||||
- name: Check PR title
|
||||
run: echo "${{ github.event.pull_request.title }}" | npx commitlint --config ./commitlint.config-ci.js
|
||||
@@ -15,7 +15,6 @@ jobs:
|
||||
app: ${{ fromJSON(inputs.projects) }}
|
||||
name: ${{ matrix.app }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 25
|
||||
steps:
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v3
|
||||
@@ -37,131 +36,66 @@ jobs:
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Setup node
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version-file: '.nvmrc'
|
||||
# https://stackoverflow.com/questions/61010294/how-to-cache-yarn-packages-in-github-actions
|
||||
cache: yarn
|
||||
|
||||
- name: Cache node modules
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: node_modules
|
||||
key: ${{ runner.os }}-cache-node-modules-${{ hashFiles('yarn.lock') }}
|
||||
|
||||
# https://docs.github.com/en/actions/learn-github-actions/contexts
|
||||
- name: Define variables
|
||||
- name: Check node version
|
||||
id: tags
|
||||
run: |
|
||||
envName=''
|
||||
dockerfile="dist.Dockerfile"
|
||||
if [[ "${{ github.event_name }}" = "push" ]]; then
|
||||
domain="vega.rocks"
|
||||
if [[ "${{ github.ref }}" =~ .*release/.* ]]; then
|
||||
envName="$(echo ${{ github.ref }} | rev | cut -d '/' -f 1 | rev)"
|
||||
if [[ "${{ github.ref }}" =~ .*mainnet.* ]]; then
|
||||
domain="vega.community"
|
||||
if [[ "${{ matrix.app }}" = "trading" ]]; then
|
||||
dockerfile="ipfs.Dockerfile"
|
||||
fi
|
||||
fi
|
||||
elif [[ "${{ github.ref }}" =~ .*develop$ ]]; then
|
||||
envName="stagnet3"
|
||||
fi
|
||||
bucketName="${{ matrix.app }}.${envName}.${domain}"
|
||||
echo BUCKET_NAME=${bucketName} >> $GITHUB_ENV
|
||||
fi
|
||||
nodeVersion=$(cat .nvmrc | head -n 1)
|
||||
echo ENV_NAME=${envName} >> $GITHUB_ENV
|
||||
echo NODE_VERSION=${nodeVersion} >> $GITHUB_ENV
|
||||
echo DOCKERFILE=docker/${dockerfile} >> $GITHUB_ENV
|
||||
echo ::set-output name=nodeVersion::${nodeVersion}
|
||||
|
||||
- name: Build local dist
|
||||
if: ${{ env.DOCKERFILE != 'docker/ipfs.Dockerfile' }}
|
||||
run: |
|
||||
flags=""
|
||||
if [[ ! -z "${{ env.ENV_NAME }}" ]]; then
|
||||
if [[ "${{ env.ENV_NAME }}" != "ops-vega" ]]; then
|
||||
flags="--env=${{ env.ENV_NAME }}"
|
||||
fi
|
||||
if [[ "${{ github.event_name }}" = "push" ]]; then
|
||||
envName="$(echo ${{ github.ref }} | rev | cut -d '/' -f 1 | rev)"
|
||||
bucketName="${{ github.event.repository.name }}-$envName"
|
||||
echo ::set-output name=bucketName::${bucketName}
|
||||
echo ::set-output name=envName::${envName}
|
||||
fi
|
||||
|
||||
if [ "${{ matrix.app }}" = "trading" ]; then
|
||||
yarn nx export trading $flags || (yarn install && yarn nx export trading $flags)
|
||||
DIST_LOCATION=dist/apps/trading/exported
|
||||
else
|
||||
yarn nx build ${{ matrix.app }} $flags || (yarn install && yarn nx build ${{ matrix.app }} $flags)
|
||||
DIST_LOCATION=dist/apps/${{ matrix.app }}
|
||||
fi
|
||||
mv $DIST_LOCATION dist-result
|
||||
tree dist-result
|
||||
|
||||
- name: Build and export to local Docker
|
||||
id: docker_build
|
||||
if: ${{ github.event_name == 'pull_request' || ( env.DOCKERFILE == 'docker/ipfs.Dockerfile' && github.event_name == 'push' ) }}
|
||||
uses: docker/build-push-action@v3
|
||||
with:
|
||||
context: .
|
||||
file: ${{ env.DOCKERFILE }}
|
||||
load: true
|
||||
build-args: |
|
||||
APP=${{ matrix.app }}
|
||||
NODE_VERSION=${{ env.NODE_VERSION }}
|
||||
ENV_NAME=${{ env.ENV_NAME }}
|
||||
NODE_VERSION=${{ steps.tags.outputs.nodeVersion }}
|
||||
ENV_NAME=${{ steps.tags.outputs.envName || '' }}
|
||||
tags: |
|
||||
ghcr.io/vegaprotocol/frontend/${{ matrix.app }}:local
|
||||
|
||||
- name: Image digest
|
||||
if: ${{ github.event_name == 'pull_request' || ( env.DOCKERFILE == 'docker/ipfs.Dockerfile' && github.event_name == 'push' ) }}
|
||||
run: echo ${{ steps.docker_build.outputs.digest }}
|
||||
|
||||
- name: Sanity check docker image
|
||||
if: ${{ github.event_name == 'pull_request' || ( env.DOCKERFILE == 'docker/ipfs.Dockerfile' && github.event_name == 'push' ) }}
|
||||
run: |
|
||||
echo "Check ipfs-hash"
|
||||
if [[ "${{ env.DOCKERFILE }}" = "docker/ipfs.Dockerfile" ]]; then
|
||||
docker run --rm ghcr.io/vegaprotocol/frontend/${{ matrix.app }}:local cat /ipfs-hash
|
||||
fi
|
||||
docker run --rm ghcr.io/vegaprotocol/frontend/${{ matrix.app }}:local cat ipfs-hash
|
||||
|
||||
echo "List html directory"
|
||||
docker run --rm ghcr.io/vegaprotocol/frontend/${{ matrix.app }}:local sh -c 'apk add --update tree; tree .'
|
||||
docker run --rm ghcr.io/vegaprotocol/frontend/${{ matrix.app }}:local ls -lah
|
||||
|
||||
- name: Copy dist to local filesystem
|
||||
if: ${{ env.DOCKERFILE == 'docker/ipfs.Dockerfile' && github.event_name == 'push' }}
|
||||
run: |
|
||||
echo "Copy dist to local filesystem"
|
||||
docker create --name=dist ghcr.io/vegaprotocol/frontend/${{ matrix.app }}:local
|
||||
docker cp dist:/usr/share/nginx/html dist
|
||||
|
||||
echo "check local dist files"
|
||||
tree dist/html
|
||||
mv dist/html dist-result
|
||||
echo "Check local dist"
|
||||
ls -al dist
|
||||
|
||||
- name: Publish dist as docker image
|
||||
uses: docker/build-push-action@v3
|
||||
if: ${{ github.event_name == 'pull_request' }}
|
||||
with:
|
||||
context: .
|
||||
file: ${{ env.DOCKERFILE }}
|
||||
push: true
|
||||
build-args: |
|
||||
APP=${{ matrix.app }}
|
||||
NODE_VERSION=${{ env.NODE_VERSION }}
|
||||
ENV_NAME=${{ env.ENV_NAME }}
|
||||
NODE_VERSION=${{ steps.tags.outputs.nodeVersion }}
|
||||
tags: |
|
||||
ghcr.io/vegaprotocol/frontend/${{ matrix.app }}:${{ github.event.pull_request.head.sha || github.sha }}
|
||||
|
||||
# bucket creation in github.com/vegaprotocol/terraform//frontend
|
||||
- name: Publish dist to s3
|
||||
uses: jakejarvis/s3-sync-action@master
|
||||
if: ${{ github.event_name == 'push' }}
|
||||
with:
|
||||
args: --acl private --follow-symlinks --delete
|
||||
env:
|
||||
AWS_S3_BUCKET: ${{ env.BUCKET_NAME }}
|
||||
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
|
||||
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
|
||||
AWS_REGION: 'eu-west-1'
|
||||
SOURCE_DIR: 'dist-result'
|
||||
# - uses: shallwefootball/s3-upload-action@master
|
||||
# if: ${{ github.event_name == 'push' }}
|
||||
# name: Upload dist S3
|
||||
# with:
|
||||
# aws_key_id: ${{ secrets.AWS_KEY_ID }}
|
||||
# aws_secret_access_key: ${{ secrets.AWS_SECRET_ACCESS_KEY}}
|
||||
# aws_bucket: ${{ steps.tags.outputs.bucketName }}
|
||||
# source_dir: 'dist'
|
||||
|
||||
- name: Add preview label
|
||||
uses: actions-ecosystem/action-add-labels@v1
|
||||
@@ -169,3 +103,6 @@ jobs:
|
||||
with:
|
||||
labels: ${{ matrix.app }}-preview
|
||||
number: ${{ github.event.number }}
|
||||
|
||||
- name: Image digest
|
||||
run: echo ${{ steps.docker_build.outputs.digest }}
|
||||
|
||||
@@ -46,6 +46,3 @@ cypress.env.json
|
||||
|
||||
# Next.js
|
||||
.next
|
||||
|
||||
#cypress
|
||||
/apps/trading-e2e/cypress/reports/
|
||||
|
||||
@@ -13,7 +13,7 @@ RUN apk add --update --no-cache \
|
||||
COPY . ./
|
||||
RUN yarn --network-timeout 100000 --pure-lockfile
|
||||
# work around for different build process in trading
|
||||
RUN sh docker/docker-build.sh
|
||||
RUN sh ./docker-build.sh
|
||||
|
||||
# Server environment
|
||||
# if this fails you need to docker pull nginx:1.23-alpine and pin new SHA
|
||||
@@ -23,7 +23,6 @@ FROM --platform=amd64 nginx:1.23-alpine@sha256:6318314189b40e73145a48060bff4783a
|
||||
EXPOSE 80
|
||||
# Copy dist
|
||||
WORKDIR /usr/share/nginx/html
|
||||
COPY docker/nginx.conf /etc/nginx/conf.d/default.conf
|
||||
RUN rm -rf /usr/share/nginx/html/*
|
||||
COPY --from=build /app/dist/apps/${APP}/* /usr/share/nginx/html
|
||||
RUN apk add --no-cache go-ipfs; ipfs init && echo "$(ipfs add -rQ .)" > /ipfs-hash; apk del go-ipfs
|
||||
COPY nginx/nginx.conf /etc/nginx/conf.d/default.conf
|
||||
COPY --from=build /app/dist/apps/${APP} /usr/share/nginx/html
|
||||
RUN apk add --no-cache go-ipfs; ipfs init && echo "$(ipfs add -rQ .)" > ipfs-hash; apk del go-ipfs
|
||||
@@ -21,7 +21,6 @@ module.exports = defineConfig({
|
||||
chromeWebSecurity: false,
|
||||
viewportWidth: 1440,
|
||||
viewportHeight: 900,
|
||||
testIsolation: false,
|
||||
},
|
||||
env: {
|
||||
environment: 'CUSTOM',
|
||||
|
||||
@@ -3,7 +3,7 @@ NX_TENDERMINT_URL=http://localhost:26617
|
||||
NX_TENDERMINT_WEBSOCKET_URL=wss://localhost:26617/websocket
|
||||
NX_VEGA_ENV=CUSTOM
|
||||
NX_ETHERSCAN_URL=https://sepolia.etherscan.io
|
||||
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/test/announcements.json
|
||||
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/main/announcements.json
|
||||
|
||||
# App flags
|
||||
NX_EXPLORER_ASSETS=1
|
||||
|
||||
@@ -9,5 +9,5 @@ NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/annou
|
||||
|
||||
NX_VEGA_GOVERNANCE_URL=https://validator-testnet.governance.vega.xyz
|
||||
NX_TENDERMINT_URL=https://tm-be.validators-testnet.vega.rocks
|
||||
NX_BLOCK_EXPLORER=https://be.validators-testnet.vega.rocks/rest
|
||||
NX_BLOCK_EXPLORER=https://be.validators-testnet.vega.rocks/
|
||||
NX_TENDERMINT_WEBSOCKET_URL=wss://be.validators-testnet.vega.xyz/websocket
|
||||
|
||||
@@ -4,4 +4,4 @@ NX_TENDERMINT_WEBSOCKET_URL=wss://localhost:26607/websocket
|
||||
NX_VEGA_ENV=CUSTOM
|
||||
NX_BLOCK_EXPLORER=
|
||||
NX_ETHERSCAN_URL=https://sepolia.etherscan.io
|
||||
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/test/announcements.json
|
||||
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/main/announcements.json
|
||||
|
||||
@@ -10,7 +10,7 @@ export const Footer = () => {
|
||||
const [nodeSwitcherOpen, setNodeSwitcherOpen] = useState(false);
|
||||
const { screenSize } = useScreenDimensions();
|
||||
const showFullFeedbackLabel = useMemo(
|
||||
() => ['md', 'lg', 'xl', 'xxl', 'xxxl'].includes(screenSize),
|
||||
() => ['lg', 'xl', 'xxl', 'xxxl'].includes(screenSize),
|
||||
[screenSize]
|
||||
);
|
||||
|
||||
|
||||
@@ -60,11 +60,9 @@ export const MarketDetails = ({ market }: { market: MarketInfoWithData }) => {
|
||||
<>
|
||||
<MarketInfoTable
|
||||
noBorder={false}
|
||||
data={{
|
||||
maxValidPrice: trigger.maxValidPrice,
|
||||
minValidPrice: trigger.minValidPrice,
|
||||
}}
|
||||
data={trigger}
|
||||
decimalPlaces={market.decimalPlaces}
|
||||
omits={['referencePrice', '__typename']}
|
||||
/>
|
||||
<MarketInfoTable
|
||||
noBorder={false}
|
||||
|
||||
@@ -38,16 +38,14 @@ const DialogsContainer = () => {
|
||||
export const Layout = () => {
|
||||
const isHome = Boolean(useMatch(Routes.HOME));
|
||||
const { ANNOUNCEMENTS_CONFIG_URL } = useEnvironment();
|
||||
const fixedWidthClasses = 'w-full max-w-[1500px] mx-auto';
|
||||
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
className={classNames(
|
||||
'min-h-screen',
|
||||
'max-w-[1500px] min-h-[100vh]',
|
||||
'mx-auto my-0',
|
||||
'grid grid-rows-[auto_1fr_auto] grid-cols-1',
|
||||
'border-vega-light-200 dark:border-vega-dark-200',
|
||||
'border-vega-light-200 dark:border-vega-dark-200 lg:border-l lg:border-r',
|
||||
'antialiased text-black dark:text-white',
|
||||
'overflow-hidden relative'
|
||||
)}
|
||||
@@ -61,13 +59,13 @@ export const Layout = () => {
|
||||
)}
|
||||
<Header />
|
||||
</div>
|
||||
<div className={fixedWidthClasses}>
|
||||
<div>
|
||||
<main className="p-4">
|
||||
{!isHome && <BreadcrumbsContainer className="mb-4" />}
|
||||
<Outlet />
|
||||
</main>
|
||||
</div>
|
||||
<div className={fixedWidthClasses}>
|
||||
<div>
|
||||
<Footer />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -10,13 +10,11 @@ NX_ETHEREUM_CHAIN_ID=1440
|
||||
NX_ETH_URL_CONNECT=1
|
||||
NX_ETH_WALLET_MNEMONIC=ozone access unlock valid olympic save include omit supply green clown session
|
||||
NX_LOCAL_PROVIDER_URL=http://localhost:8545/
|
||||
NX_ETH_LOCAL_PROVIDER_URL=http://localhost:8545/
|
||||
NX_VEGA_WALLET_URL=http://localhost:1789
|
||||
NX_VEGA_DOCS_URL=https://docs.vega.xyz/mainnet
|
||||
NX_TRANCHES_SERVICE_URL=https://tranches-stagnet3-k8s.ops.vega.xyz
|
||||
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/test/announcements.json
|
||||
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json
|
||||
NX_GITHUB_FEEDBACK_URL=https://github.com/vegaprotocol/feedback/discussions
|
||||
NX_WALLETCONNECT_PROJECT_ID=fe8091dc35738863e509fc4947525c72
|
||||
|
||||
#Test configuration variables
|
||||
CYPRESS_FAIRGROUND=false
|
||||
|
||||
@@ -25,7 +25,6 @@ module.exports = defineConfig({
|
||||
viewportWidth: 1440,
|
||||
viewportHeight: 900,
|
||||
numTestsKeptInMemory: 5,
|
||||
testIsolation: false,
|
||||
},
|
||||
env: {
|
||||
ethProviderUrl: 'http://localhost:8545/',
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
export const upgradeProposalsData = {
|
||||
lastBlockHeight: '2014133',
|
||||
protocolUpgradeProposals: {
|
||||
edges: [
|
||||
{
|
||||
node: {
|
||||
upgradeBlockHeight: '2015942',
|
||||
vegaReleaseTag: 'v1',
|
||||
approvers: [
|
||||
'02a6531716b7a6d82779b7793c3ad2fcb47290ea2ff0912c56f61219bd9675ff',
|
||||
'121934387281812a2d5e6913e5d57c0f85a8f169e2752347ee2e23b52d46623c',
|
||||
'65c80e2f5f84e2109eec30810f137ba04cbbecaba8f27706c146cc6c6f90db29',
|
||||
'bd6339d2428c79ac3bc9011771236d17bac92bcb1806423388d52fb440043aef',
|
||||
],
|
||||
status: 'PROTOCOL_UPGRADE_PROPOSAL_STATUS_APPROVED',
|
||||
__typename: 'ProtocolUpgradeProposal',
|
||||
},
|
||||
__typename: 'ProtocolUpgradeProposalEdge',
|
||||
},
|
||||
{
|
||||
node: {
|
||||
upgradeBlockHeight: '1955065',
|
||||
vegaReleaseTag: 'v0.71.0+dev-12156-bca1d57e',
|
||||
approvers: [
|
||||
'02a6531716b7a6d82779b7793c3ad2fcb47290ea2ff0912c56f61219bd9675ff',
|
||||
'121934387281812a2d5e6913e5d57c0f85a8f169e2752347ee2e23b52d46623c',
|
||||
'65c80e2f5f84e2109eec30810f137ba04cbbecaba8f27706c146cc6c6f90db29',
|
||||
'bd6339d2428c79ac3bc9011771236d17bac92bcb1806423388d52fb440043aef',
|
||||
],
|
||||
status: 'PROTOCOL_UPGRADE_PROPOSAL_STATUS_APPROVED',
|
||||
__typename: 'ProtocolUpgradeProposal',
|
||||
},
|
||||
__typename: 'ProtocolUpgradeProposalEdge',
|
||||
},
|
||||
],
|
||||
__typename: 'ProtocolUpgradeProposalConnection',
|
||||
},
|
||||
};
|
||||
@@ -1,117 +0,0 @@
|
||||
export const nodeData = {
|
||||
epoch: {
|
||||
id: '204731',
|
||||
timestamps: {
|
||||
start: '2023-04-14T09:30:09.452005Z',
|
||||
end: null,
|
||||
expiry: '2023-04-14T09:31:09.452005Z',
|
||||
__typename: 'EpochTimestamps',
|
||||
},
|
||||
__typename: 'Epoch',
|
||||
},
|
||||
nodesConnection: {
|
||||
edges: [
|
||||
{
|
||||
node: {
|
||||
avatarUrl:
|
||||
'https://www.gravatar.com/avatar/y2hwc4xjds7zvlam3y4in94q2rcdimsn?d=identicon',
|
||||
id: 'f337b5cc50c49a928e49129a2eca62277a81b4b7336b6e4131f8f0431e8db029',
|
||||
name: 'lovely-fisherman',
|
||||
pubkey:
|
||||
'02a6531716b7a6d82779b7793c3ad2fcb47290ea2ff0912c56f61219bd9675ff',
|
||||
stakedByOperator: '3000000000000000000000',
|
||||
stakedByDelegates: '0',
|
||||
stakedTotal: '3000000000000000000000',
|
||||
pendingStake: '0',
|
||||
rankingScore: {
|
||||
rankingScore: '0.4999166805532412',
|
||||
stakeScore: '0.2499583402766206',
|
||||
performanceScore: '1',
|
||||
votingPower: '2499',
|
||||
status: 'VALIDATOR_NODE_STATUS_TENDERMINT',
|
||||
__typename: 'RankingScore',
|
||||
},
|
||||
__typename: 'Node',
|
||||
},
|
||||
__typename: 'NodeEdge',
|
||||
},
|
||||
{
|
||||
node: {
|
||||
avatarUrl:
|
||||
'https://www.gravatar.com/avatar/g1tw70qnoo0tf8ror30jefwhae92zy16?d=identicon',
|
||||
id: 'cdadcf885556e372b6b39679d6ad46854d1d9c1e982da7ff2929544df01f9088',
|
||||
name: 'magnificent-door',
|
||||
pubkey:
|
||||
'121934387281812a2d5e6913e5d57c0f85a8f169e2752347ee2e23b52d46623c',
|
||||
stakedByOperator: '3000000000000000000000',
|
||||
stakedByDelegates: '0',
|
||||
stakedTotal: '3000000000000000000000',
|
||||
pendingStake: '0',
|
||||
rankingScore: {
|
||||
rankingScore: '0.4999166805532412',
|
||||
stakeScore: '0.2499583402766206',
|
||||
performanceScore: '1',
|
||||
votingPower: '2499',
|
||||
status: 'VALIDATOR_NODE_STATUS_TENDERMINT',
|
||||
__typename: 'RankingScore',
|
||||
},
|
||||
__typename: 'Node',
|
||||
},
|
||||
__typename: 'NodeEdge',
|
||||
},
|
||||
{
|
||||
node: {
|
||||
avatarUrl:
|
||||
'https://www.gravatar.com/avatar/s2e7hp7soq5zdhxeap7p21onetsjiy6w?d=identicon',
|
||||
id: '9a13de2548fef30d928e71e76004c98ec7ad9d8fc3ddcd16ddee210b28ea4cfc',
|
||||
name: 'helpful-tree',
|
||||
pubkey:
|
||||
'bd6339d2428c79ac3bc9011771236d17bac92bcb1806423388d52fb440043aef',
|
||||
stakedByOperator: '3000000000000000000000',
|
||||
stakedByDelegates: '2000000000000000000',
|
||||
stakedTotal: '3002000000000000000000',
|
||||
pendingStake: '0',
|
||||
rankingScore: {
|
||||
rankingScore: '0.5002499583402766',
|
||||
stakeScore: '0.2501249791701383',
|
||||
performanceScore: '1',
|
||||
votingPower: '2501',
|
||||
status: 'VALIDATOR_NODE_STATUS_TENDERMINT',
|
||||
__typename: 'RankingScore',
|
||||
},
|
||||
__typename: 'Node',
|
||||
},
|
||||
__typename: 'NodeEdge',
|
||||
},
|
||||
{
|
||||
node: {
|
||||
avatarUrl:
|
||||
'https://www.gravatar.com/avatar/3gop683kg2cvd8rpv286hzagqt3yjsdq?d=identicon',
|
||||
id: '93ac271ab95038333587e47d11461e6edc0126e6e77156281c545f4c5650a6d0',
|
||||
name: 'easy-cookie',
|
||||
pubkey:
|
||||
'65c80e2f5f84e2109eec30810f137ba04cbbecaba8f27706c146cc6c6f90db29',
|
||||
stakedByOperator: '3000000000000000000000',
|
||||
stakedByDelegates: '0',
|
||||
stakedTotal: '3000000000000000000000',
|
||||
pendingStake: '0',
|
||||
rankingScore: {
|
||||
rankingScore: '0.4999166805532412',
|
||||
stakeScore: '0.2499583402766206',
|
||||
performanceScore: '1',
|
||||
votingPower: '2499',
|
||||
status: 'VALIDATOR_NODE_STATUS_TENDERMINT',
|
||||
__typename: 'RankingScore',
|
||||
},
|
||||
__typename: 'Node',
|
||||
},
|
||||
__typename: 'NodeEdge',
|
||||
},
|
||||
],
|
||||
__typename: 'NodesConnection',
|
||||
},
|
||||
nodeData: {
|
||||
stakedTotal: '12002000000000000000000',
|
||||
__typename: 'NodeData',
|
||||
},
|
||||
};
|
||||
@@ -1,325 +0,0 @@
|
||||
export const proposalsData = {
|
||||
proposalsConnection: {
|
||||
edges: [
|
||||
{
|
||||
node: {
|
||||
id: 'e8ba9d268e12514644fd1fc7ff289292f4ce6489cc32cc73133aea52c04aef89',
|
||||
rationale: {
|
||||
title: 'Add asset Wrapped Ether',
|
||||
description: 'Proposal to add asset WETH to Vega network',
|
||||
__typename: 'ProposalRationale',
|
||||
},
|
||||
reference: '',
|
||||
state: 'STATE_OPEN',
|
||||
datetime: '2026-12-01T11:41:28.654288Z',
|
||||
rejectionReason: null,
|
||||
party: {
|
||||
id: '69464e35bcb8e8a2900ca0f87acaf252d50cf2ab2fc73694845a16b7c8a0dc6f',
|
||||
__typename: 'Party',
|
||||
},
|
||||
errorDetails: null,
|
||||
terms: {
|
||||
closingDatetime: '2026-12-01T11:45:33Z',
|
||||
enactmentDatetime: '2026-12-01T11:45:43Z',
|
||||
change: {
|
||||
name: 'Wrapped Ether',
|
||||
symbol: 'WETH',
|
||||
decimals: 18,
|
||||
quantum: '0.0008',
|
||||
source: {
|
||||
contractAddress: '0x9B18C6CaD886D5653783E2B25759124760F4407F',
|
||||
withdrawThreshold: '1',
|
||||
lifetimeLimit: '400000000000000000',
|
||||
__typename: 'ERC20',
|
||||
},
|
||||
__typename: 'NewAsset',
|
||||
},
|
||||
__typename: 'ProposalTerms',
|
||||
},
|
||||
votes: {
|
||||
yes: {
|
||||
totalTokens: '0',
|
||||
totalNumber: '0',
|
||||
totalEquityLikeShareWeight: '0',
|
||||
__typename: 'ProposalVoteSide',
|
||||
},
|
||||
no: {
|
||||
totalTokens: '0',
|
||||
totalNumber: '0',
|
||||
totalEquityLikeShareWeight: '0',
|
||||
__typename: 'ProposalVoteSide',
|
||||
},
|
||||
__typename: 'ProposalVotes',
|
||||
},
|
||||
__typename: 'Proposal',
|
||||
},
|
||||
__typename: 'ProposalEdge',
|
||||
},
|
||||
{
|
||||
node: {
|
||||
id: 'd848fc7881f13d366df5f61ab139d5fcfa72bf838151bb51b54381870e357931',
|
||||
rationale: {
|
||||
title: 'Add asset Dai Stablecoin',
|
||||
description: 'Proposal to add asset DAI to Vega network',
|
||||
__typename: 'ProposalRationale',
|
||||
},
|
||||
reference: '',
|
||||
state: 'STATE_ENACTED',
|
||||
datetime: '2022-11-29T15:49:57.80978Z',
|
||||
rejectionReason: null,
|
||||
party: {
|
||||
id: '69464e35bcb8e8a2900ca0f87acaf252d50cf2ab2fc73694845a16b7c8a0dc6f',
|
||||
__typename: 'Party',
|
||||
},
|
||||
errorDetails: null,
|
||||
terms: {
|
||||
closingDatetime: '2023-04-13T15:52:58Z',
|
||||
enactmentDatetime: '2023-04-13T15:53:08Z',
|
||||
change: {
|
||||
name: 'Dai Stablecoin',
|
||||
symbol: 'DAI',
|
||||
decimals: 18,
|
||||
quantum: '1',
|
||||
source: {
|
||||
contractAddress: '0xad018fB8ec00bfd622B91C83E684a6AC7bB8fbA4',
|
||||
withdrawThreshold: '1',
|
||||
lifetimeLimit: '500000000000000000000',
|
||||
__typename: 'ERC20',
|
||||
},
|
||||
__typename: 'NewAsset',
|
||||
},
|
||||
__typename: 'ProposalTerms',
|
||||
},
|
||||
votes: {
|
||||
yes: {
|
||||
totalTokens: '0',
|
||||
totalNumber: '0',
|
||||
totalEquityLikeShareWeight: '0',
|
||||
__typename: 'ProposalVoteSide',
|
||||
},
|
||||
no: {
|
||||
totalTokens: '0',
|
||||
totalNumber: '0',
|
||||
totalEquityLikeShareWeight: '0',
|
||||
__typename: 'ProposalVoteSide',
|
||||
},
|
||||
__typename: 'ProposalVotes',
|
||||
},
|
||||
__typename: 'Proposal',
|
||||
},
|
||||
__typename: 'ProposalEdge',
|
||||
},
|
||||
{
|
||||
node: {
|
||||
id: 'ccbd651b4a1167fd73c4a0340ac759fa0a31ca487ad46a13254b741ad71947ed',
|
||||
rationale: {
|
||||
title: 'New DAI market',
|
||||
description: 'New DAI market',
|
||||
__typename: 'ProposalRationale',
|
||||
},
|
||||
reference: '0VFQusmmESdrP5GuL8naB6lxfoE3RPGaEeo7abdN',
|
||||
state: 'STATE_ENACTED',
|
||||
datetime: '2022-11-26T19:36:19.26034Z',
|
||||
rejectionReason: null,
|
||||
party: {
|
||||
id: '69464e35bcb8e8a2900ca0f87acaf252d50cf2ab2fc73694845a16b7c8a0dc6f',
|
||||
__typename: 'Party',
|
||||
},
|
||||
errorDetails: null,
|
||||
terms: {
|
||||
closingDatetime: '2022-11-26T19:36:42Z',
|
||||
enactmentDatetime: '2023-03-22T13:57:37Z',
|
||||
change: {
|
||||
instrument: {
|
||||
name: 'UNIDAI Monthly (Dec 2022)',
|
||||
code: 'UNIDAI.MF21',
|
||||
futureProduct: {
|
||||
settlementAsset: { symbol: 'tDAI', __typename: 'Asset' },
|
||||
__typename: 'FutureProduct',
|
||||
},
|
||||
__typename: 'InstrumentConfiguration',
|
||||
},
|
||||
__typename: 'NewMarket',
|
||||
},
|
||||
__typename: 'ProposalTerms',
|
||||
},
|
||||
votes: {
|
||||
yes: {
|
||||
totalTokens: '0',
|
||||
totalNumber: '0',
|
||||
totalEquityLikeShareWeight: '0',
|
||||
__typename: 'ProposalVoteSide',
|
||||
},
|
||||
no: {
|
||||
totalTokens: '0',
|
||||
totalNumber: '0',
|
||||
totalEquityLikeShareWeight: '0',
|
||||
__typename: 'ProposalVoteSide',
|
||||
},
|
||||
__typename: 'ProposalVotes',
|
||||
},
|
||||
__typename: 'Proposal',
|
||||
},
|
||||
__typename: 'ProposalEdge',
|
||||
},
|
||||
{
|
||||
node: {
|
||||
id: 'bc70383f0e9515b15542cf4c63590cd2ca46b3363ba7c4a72af0e62112b3951b',
|
||||
rationale: {
|
||||
title: 'USDC-III',
|
||||
description: 'USDC-III D List test',
|
||||
__typename: 'ProposalRationale',
|
||||
},
|
||||
reference: '',
|
||||
state: 'STATE_ENACTED',
|
||||
datetime: '2022-11-22T15:17:28.829605Z',
|
||||
rejectionReason: null,
|
||||
party: {
|
||||
id: '69464e35bcb8e8a2900ca0f87acaf252d50cf2ab2fc73694845a16b7c8a0dc6f',
|
||||
__typename: 'Party',
|
||||
},
|
||||
errorDetails: null,
|
||||
terms: {
|
||||
closingDatetime: '2022-11-22T15:18:56Z',
|
||||
enactmentDatetime: '2022-11-22T15:19:06Z',
|
||||
change: {
|
||||
name: 'USDC-III',
|
||||
symbol: 'USDC-III',
|
||||
decimals: 18,
|
||||
quantum: '1',
|
||||
source: {
|
||||
contractAddress: '0x1F1A067aEC530b66BA5128C9Db76825eC22c3C6b',
|
||||
withdrawThreshold: '100000000000000000000000000',
|
||||
lifetimeLimit: '1000000000000000000000000000',
|
||||
__typename: 'ERC20',
|
||||
},
|
||||
__typename: 'NewAsset',
|
||||
},
|
||||
__typename: 'ProposalTerms',
|
||||
},
|
||||
votes: {
|
||||
yes: {
|
||||
totalTokens: '0',
|
||||
totalNumber: '0',
|
||||
totalEquityLikeShareWeight: '0',
|
||||
__typename: 'ProposalVoteSide',
|
||||
},
|
||||
no: {
|
||||
totalTokens: '0',
|
||||
totalNumber: '0',
|
||||
totalEquityLikeShareWeight: '0',
|
||||
__typename: 'ProposalVoteSide',
|
||||
},
|
||||
__typename: 'ProposalVotes',
|
||||
},
|
||||
__typename: 'Proposal',
|
||||
},
|
||||
__typename: 'ProposalEdge',
|
||||
},
|
||||
{
|
||||
node: {
|
||||
id: '9d9b2a9d0179d0e4ccb317f6c4a5db0b905d893190bfb5e5499985ef313281c8',
|
||||
rationale: {
|
||||
title: 'New BTC market',
|
||||
description: 'New BTC market',
|
||||
__typename: 'ProposalRationale',
|
||||
},
|
||||
reference: 'AXeRWS3TvLBFDgWOSHQpKFJf3NTbnWK6310q02fZ',
|
||||
state: 'STATE_ENACTED',
|
||||
datetime: '2022-11-26T19:36:19.26034Z',
|
||||
rejectionReason: null,
|
||||
party: {
|
||||
id: '69464e35bcb8e8a2900ca0f87acaf252d50cf2ab2fc73694845a16b7c8a0dc6f',
|
||||
__typename: 'Party',
|
||||
},
|
||||
errorDetails: null,
|
||||
terms: {
|
||||
closingDatetime: '2022-11-26T19:36:42Z',
|
||||
enactmentDatetime: '2023-03-22T13:57:37Z',
|
||||
change: {
|
||||
instrument: {
|
||||
name: 'ETHBTC Quarterly (Feb 2023)',
|
||||
code: 'ETHBTC.QM21',
|
||||
futureProduct: {
|
||||
settlementAsset: { symbol: 'tBTC', __typename: 'Asset' },
|
||||
__typename: 'FutureProduct',
|
||||
},
|
||||
__typename: 'InstrumentConfiguration',
|
||||
},
|
||||
__typename: 'NewMarket',
|
||||
},
|
||||
__typename: 'ProposalTerms',
|
||||
},
|
||||
votes: {
|
||||
yes: {
|
||||
totalTokens: '0',
|
||||
totalNumber: '0',
|
||||
totalEquityLikeShareWeight: '0',
|
||||
__typename: 'ProposalVoteSide',
|
||||
},
|
||||
no: {
|
||||
totalTokens: '0',
|
||||
totalNumber: '0',
|
||||
totalEquityLikeShareWeight: '0',
|
||||
__typename: 'ProposalVoteSide',
|
||||
},
|
||||
__typename: 'ProposalVotes',
|
||||
},
|
||||
__typename: 'Proposal',
|
||||
},
|
||||
__typename: 'ProposalEdge',
|
||||
},
|
||||
{
|
||||
node: {
|
||||
id: '9c48796e7988769ededc2b2b02220b00e93f65f23e8141bf1fd23a6983d95943',
|
||||
rationale: {
|
||||
title: 'Update governance.proposal.asset.requiredMajority',
|
||||
description:
|
||||
'Proposal to update governance.proposal.asset.requiredMajority to 300}',
|
||||
__typename: 'ProposalRationale',
|
||||
},
|
||||
reference: '',
|
||||
state: 'STATE_ENACTED',
|
||||
datetime: '2022-11-22T13:22:52.370655Z',
|
||||
rejectionReason: null,
|
||||
party: {
|
||||
id: '69464e35bcb8e8a2900ca0f87acaf252d50cf2ab2fc73694845a16b7c8a0dc6f',
|
||||
__typename: 'Party',
|
||||
},
|
||||
errorDetails: null,
|
||||
terms: {
|
||||
closingDatetime: '2022-11-22T13:27:13Z',
|
||||
enactmentDatetime: '2022-11-22T13:27:33Z',
|
||||
change: {
|
||||
networkParameter: {
|
||||
key: 'governance.proposal.asset.requiredParticipation',
|
||||
value: '0.000001',
|
||||
__typename: 'NetworkParameter',
|
||||
},
|
||||
__typename: 'UpdateNetworkParameter',
|
||||
},
|
||||
__typename: 'ProposalTerms',
|
||||
},
|
||||
votes: {
|
||||
yes: {
|
||||
totalTokens: '0',
|
||||
totalNumber: '0',
|
||||
totalEquityLikeShareWeight: '0',
|
||||
__typename: 'ProposalVoteSide',
|
||||
},
|
||||
no: {
|
||||
totalTokens: '0',
|
||||
totalNumber: '0',
|
||||
totalEquityLikeShareWeight: '0',
|
||||
__typename: 'ProposalVoteSide',
|
||||
},
|
||||
__typename: 'ProposalVotes',
|
||||
},
|
||||
__typename: 'Proposal',
|
||||
},
|
||||
__typename: 'ProposalEdge',
|
||||
},
|
||||
],
|
||||
__typename: 'ProposalsConnection',
|
||||
},
|
||||
};
|
||||
@@ -44,8 +44,7 @@ describe(
|
||||
function () {
|
||||
before('connect wallets and set approval limit', function () {
|
||||
cy.visit('/');
|
||||
ethereumWalletConnect();
|
||||
cy.associateTokensToVegaWallet('1');
|
||||
vegaWalletSetSpecifiedApprovalAmount('1000');
|
||||
});
|
||||
|
||||
beforeEach('visit proposals tab', function () {
|
||||
@@ -214,7 +213,6 @@ describe(
|
||||
|
||||
// 3001-VOTE-042, 3001-VOTE-057, 3001-VOTE-058, 3001-VOTE-059, 3001-VOTE-060
|
||||
it('Newly created proposal details - ability to increase associated tokens - by voting again after association', function () {
|
||||
vegaWalletSetSpecifiedApprovalAmount('1000');
|
||||
createRawProposal();
|
||||
cy.get<testFreeformProposal>('@rawProposal').then((rawProposal) => {
|
||||
getSubmittedProposalFromProposalList(rawProposal.rationale.title)
|
||||
|
||||
@@ -13,6 +13,7 @@ import {
|
||||
createUpdateNetworkProposalTxBody,
|
||||
createFreeFormProposalTxBody,
|
||||
} from '../../support/proposal.functions';
|
||||
import { ensureSpecifiedUnstakedTokensAreAssociated } from '../../support/staking.functions';
|
||||
import { ethereumWalletConnect } from '../../support/wallet-eth.functions';
|
||||
import { vegaWalletSetSpecifiedApprovalAmount } from '../../support/wallet-teardown.functions';
|
||||
|
||||
@@ -32,6 +33,10 @@ context(
|
||||
before('Connect wallets and set approval', function () {
|
||||
cy.visit('/');
|
||||
vegaWalletSetSpecifiedApprovalAmount('1000');
|
||||
cy.connectVegaWallet();
|
||||
ethereumWalletConnect();
|
||||
ensureSpecifiedUnstakedTokensAreAssociated('1');
|
||||
cy.clearLocalStorage();
|
||||
});
|
||||
|
||||
beforeEach('visit proposals', function () {
|
||||
@@ -109,7 +114,7 @@ context(
|
||||
navigateTo(navigation.proposals);
|
||||
cy.reload();
|
||||
waitForSpinner();
|
||||
cy.get(openProposals, { timeout: 6000 }).within(() => {
|
||||
cy.get(openProposals).within(() => {
|
||||
cy.contains(proposalTitle)
|
||||
.parentsUntil('[data-testid="proposals-list-item"]')
|
||||
.within(() => cy.get(viewProposalButton).click());
|
||||
|
||||
@@ -232,7 +232,7 @@ context(
|
||||
|
||||
it('Unable to create a freeform proposal - when json parent section contains unexpected field', function () {
|
||||
const errorMsg =
|
||||
'Invalid params: the transaction does not use a valid Vega command: unknown field "unexpected" in vega.commands.v1.ProposalSubmission';
|
||||
'Invalid params: the transaction does not use a valid Vega command: unknown field unexpected" in vega.commands.v1.ProposalSubmission';
|
||||
|
||||
// 3001-VOTE-038 3002-PROP-013 3002-PROP-014
|
||||
goToMakeNewProposal(governanceProposalType.RAW);
|
||||
@@ -313,7 +313,7 @@ context(
|
||||
|
||||
it('Unable to vote on a proposal - when vega wallet disconnected - option to connect from within', function () {
|
||||
createRawProposal();
|
||||
cy.get('[data-testid="manage-vega-wallet"]:visible').click();
|
||||
cy.get('[data-testid="manage-vega-wallet"]').click();
|
||||
cy.get('[data-testid="disconnect"]').click();
|
||||
cy.get<testFreeformProposal>('@rawProposal').then((rawProposal) => {
|
||||
getSubmittedProposalFromProposalList(
|
||||
|
||||
@@ -218,7 +218,7 @@ context(
|
||||
|
||||
it('Unable to submit new market proposal with missing/invalid fields', function () {
|
||||
const errorMsg =
|
||||
'Invalid params: the transaction does not use a valid Vega command: unknown field "invalid" in vega.NewMarket';
|
||||
'Invalid params: the transaction is not a valid Vega command: unknown field "filters" in vega.DataSourceDefinition';
|
||||
|
||||
goToMakeNewProposal(governanceProposalType.NEW_MARKET);
|
||||
cy.get(newProposalSubmitButton).should('be.visible').click();
|
||||
@@ -436,7 +436,7 @@ context(
|
||||
});
|
||||
});
|
||||
|
||||
it('Able to submit update asset proposal using max deadline', function () {
|
||||
it.only('Able to submit update asset proposal using max deadline', function () {
|
||||
goToMakeNewProposal(governanceProposalType.UPDATE_ASSET);
|
||||
enterUpdateAssetProposalDetails();
|
||||
cy.get(maxVoteDeadline).click();
|
||||
|
||||
@@ -12,6 +12,7 @@ import {
|
||||
generateFreeFormProposalTitle,
|
||||
getProposalIdFromList,
|
||||
getProposalInformationFromTable,
|
||||
getSortOrderOfSuppliedArray,
|
||||
getSubmittedProposalFromProposalList,
|
||||
goToMakeNewProposal,
|
||||
governanceProposalType,
|
||||
@@ -23,9 +24,9 @@ import { ensureSpecifiedUnstakedTokensAreAssociated } from '../../support/stakin
|
||||
import { ethereumWalletConnect } from '../../support/wallet-eth.functions';
|
||||
import { vegaWalletSetSpecifiedApprovalAmount } from '../../support/wallet-teardown.functions';
|
||||
|
||||
const proposalDetailsTitle = '[data-testid="proposal-title"]';
|
||||
const openProposals = '[data-testid="open-proposals"]';
|
||||
const voteStatus = '[data-testid="vote-status"]';
|
||||
const proposalClosingDate = '[data-testid="vote-details"]';
|
||||
const viewProposalButton = '[data-testid="view-proposal-btn"]';
|
||||
|
||||
describe('Governance flow for proposal list', { tags: '@slow' }, function () {
|
||||
@@ -44,8 +45,16 @@ describe('Governance flow for proposal list', { tags: '@slow' }, function () {
|
||||
});
|
||||
|
||||
it('Newly created proposals list - proposals closest to closing date appear higher in list', function () {
|
||||
const minCloseDays = 2;
|
||||
const maxCloseDays = 3;
|
||||
|
||||
// 3001-VOTE-005
|
||||
const proposalDays = [364, 50, 2];
|
||||
const proposalDays = [
|
||||
minCloseDays + 1,
|
||||
maxCloseDays,
|
||||
minCloseDays + 3,
|
||||
minCloseDays + 2,
|
||||
];
|
||||
for (let index = 0; index < proposalDays.length; index++) {
|
||||
goToMakeNewProposal(governanceProposalType.RAW);
|
||||
enterRawProposalBody(
|
||||
@@ -55,12 +64,19 @@ describe('Governance flow for proposal list', { tags: '@slow' }, function () {
|
||||
waitForProposalSync();
|
||||
}
|
||||
|
||||
const arrayOfProposals: string[] = [];
|
||||
|
||||
navigateTo(navigation.proposals);
|
||||
cy.get(openProposals).within(() => {
|
||||
cy.get(proposalClosingDate).first().should('contain.text', 'year');
|
||||
cy.get(proposalClosingDate).should('contain.text', 'months');
|
||||
cy.get(proposalClosingDate).last().should('contain.text', 'days');
|
||||
});
|
||||
cy.get(proposalDetailsTitle)
|
||||
.each((proposalTitleElement) => {
|
||||
arrayOfProposals.push(proposalTitleElement.text());
|
||||
})
|
||||
.then(() => {
|
||||
cy.wrap(getSortOrderOfSuppliedArray(arrayOfProposals)).should(
|
||||
'equal',
|
||||
'descending'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it('Newly created proposals list - able to filter by proposerID to show it in list', function () {
|
||||
|
||||
@@ -40,22 +40,18 @@ const stakeAddStakeRadioButton = 'add-stake-radio';
|
||||
const stakeMaximumTokens = 'token-amount-use-maximum';
|
||||
const vegaWalletAssociatedBalance = 'currency-value';
|
||||
const vegaWalletStakedBalances = 'vega-wallet-balance-staked-validators';
|
||||
const ethWallet = 'ethereum-wallet';
|
||||
const ethWalletContainer = 'ethereum-wallet';
|
||||
const vegaWallet = 'vega-wallet';
|
||||
const vegaWalletPublicKeyShort = Cypress.env('vegaWalletPublicKeyShort');
|
||||
const txTimeout = Cypress.env('txTimeout');
|
||||
const epochTimeout = Cypress.env('epochTimeout');
|
||||
|
||||
const getEthereumWallet = () => cy.get(`[data-testid="${ethWallet}"]:visible`);
|
||||
const getVegaWallet = () => cy.get(`[data-testid="${vegaWallet}"]:visible`);
|
||||
|
||||
context(
|
||||
'Staking Tab - with eth and vega wallets connected',
|
||||
{ tags: '@slow' },
|
||||
function () {
|
||||
// 2001-STKE-002, 2001-STKE-032
|
||||
before('visit staking tab and connect vega wallet', function () {
|
||||
cy.clearAllLocalStorage();
|
||||
cy.visit('/');
|
||||
ethereumWalletConnect();
|
||||
// this is a workaround for #2422 which can be removed once issue is resolved
|
||||
@@ -191,35 +187,31 @@ context(
|
||||
.should('contain', 1.0, txTimeout);
|
||||
closeStakingDialog();
|
||||
navigateTo(navigation.validators);
|
||||
cy.get(`[row-id="${0}"]`)
|
||||
.eq(1)
|
||||
.within(() => {
|
||||
cy.getByTestId(stakeValidatorListTotalStake)
|
||||
.should('have.text', '2.00')
|
||||
.and('be.visible');
|
||||
cy.getByTestId(stakeValidatorListTotalShare)
|
||||
.should('have.text', '66.67%')
|
||||
.and('be.visible');
|
||||
cy.getByTestId(stakeValidatorListTotalStake)
|
||||
.scrollIntoView()
|
||||
.should('have.text', '2.00')
|
||||
.and('be.visible');
|
||||
});
|
||||
cy.get(`[row-id="${1}"]`)
|
||||
.eq(1)
|
||||
.within(() => {
|
||||
cy.getByTestId(stakeValidatorListTotalStake)
|
||||
.scrollIntoView()
|
||||
.should('have.text', '1.00')
|
||||
.and('be.visible');
|
||||
cy.getByTestId(stakeValidatorListTotalShare)
|
||||
.should('have.text', '33.33%')
|
||||
.and('be.visible');
|
||||
cy.getByTestId(stakeValidatorListTotalStake)
|
||||
.scrollIntoView()
|
||||
.should('have.text', '1.00')
|
||||
.and('be.visible');
|
||||
});
|
||||
cy.get(`[row-id="${0}"]`).within(() => {
|
||||
cy.getByTestId(stakeValidatorListTotalStake)
|
||||
.should('have.text', '2.00')
|
||||
.and('be.visible');
|
||||
cy.getByTestId(stakeValidatorListTotalShare)
|
||||
.should('have.text', '66.67%')
|
||||
.and('be.visible');
|
||||
cy.getByTestId(stakeValidatorListTotalStake)
|
||||
.scrollIntoView()
|
||||
.should('have.text', '2.00')
|
||||
.and('be.visible');
|
||||
});
|
||||
cy.get(`[row-id="${1}"]`).within(() => {
|
||||
cy.getByTestId(stakeValidatorListTotalStake)
|
||||
.scrollIntoView()
|
||||
.should('have.text', '1.00')
|
||||
.and('be.visible');
|
||||
cy.getByTestId(stakeValidatorListTotalShare)
|
||||
.should('have.text', '33.33%')
|
||||
.and('be.visible');
|
||||
cy.getByTestId(stakeValidatorListTotalStake)
|
||||
.scrollIntoView()
|
||||
.should('have.text', '1.00')
|
||||
.and('be.visible');
|
||||
});
|
||||
});
|
||||
|
||||
// 2001-STKE-041
|
||||
@@ -337,11 +329,11 @@ context(
|
||||
verifyStakedBalance(2.0);
|
||||
closeStakingDialog();
|
||||
stakingPageDisassociateAllTokens();
|
||||
getEthereumWallet().within(() => {
|
||||
cy.getByTestId(ethWalletContainer).within(() => {
|
||||
cy.contains(vegaWalletPublicKeyShort, txTimeout).should('not.exist');
|
||||
});
|
||||
verifyEthWalletTotalAssociatedBalance('0.0');
|
||||
getVegaWallet().within(() => {
|
||||
cy.getByTestId(vegaWallet).within(() => {
|
||||
cy.getByTestId(vegaWalletAssociatedBalance, txTimeout).should(
|
||||
'contain',
|
||||
'0.00'
|
||||
@@ -365,11 +357,11 @@ context(
|
||||
verifyStakedBalance(2.0);
|
||||
closeStakingDialog();
|
||||
stakingPageDisassociateAllTokens('contract');
|
||||
getEthereumWallet().within(() => {
|
||||
cy.getByTestId(ethWalletContainer).within(() => {
|
||||
cy.contains(vegaWalletPublicKeyShort, txTimeout).should('not.exist');
|
||||
});
|
||||
verifyEthWalletTotalAssociatedBalance('0.0');
|
||||
getVegaWallet().within(() => {
|
||||
cy.getByTestId(vegaWallet).within(() => {
|
||||
cy.getByTestId(vegaWalletAssociatedBalance, txTimeout).should(
|
||||
'contain',
|
||||
'0.00'
|
||||
@@ -394,7 +386,7 @@ context(
|
||||
closeStakingDialog();
|
||||
stakingPageDisassociateTokens('1');
|
||||
verifyEthWalletTotalAssociatedBalance('2.0');
|
||||
getVegaWallet().within(() => {
|
||||
cy.getByTestId(vegaWallet).within(() => {
|
||||
cy.getByTestId(vegaWalletAssociatedBalance, txTimeout).should(
|
||||
'contain',
|
||||
'2.00'
|
||||
@@ -461,7 +453,7 @@ context(
|
||||
verifyUnstakedBalance(0.0);
|
||||
closeStakingDialog();
|
||||
stakingPageAssociateTokens('6');
|
||||
getVegaWallet().within(() => {
|
||||
cy.getByTestId(vegaWallet).within(() => {
|
||||
cy.getByTestId(vegaWalletAssociatedBalance, txTimeout).should(
|
||||
'contain',
|
||||
'12.00'
|
||||
|
||||
@@ -25,11 +25,11 @@ const vegaWalletUnstakedBalance =
|
||||
'[data-testid="vega-wallet-balance-unstaked"]';
|
||||
const txTimeout = Cypress.env('txTimeout');
|
||||
const vegaWalletPublicKeyShort = Cypress.env('vegaWalletPublicKeyShort');
|
||||
const ethWalletAssociateButton = '[data-testid="associate-btn"]:visible';
|
||||
const ethWalletAssociateButton = '[data-testid="associate-btn"]';
|
||||
const associateWalletRadioButton = '[data-testid="associate-radio-wallet"]';
|
||||
const tokenAmountInputBox = '[data-testid="token-amount-input"]';
|
||||
const tokenSubmitButton = '[data-testid="token-input-submit-button"]';
|
||||
const ethWalletDissociateButton = '[href="/token/disassociate"]:visible';
|
||||
const ethWalletDissociateButton = '[href="/token/disassociate"]';
|
||||
const vestingContractSection = '[data-testid="vega-in-vesting-contract"]';
|
||||
const vegaInWalletSection = '[data-testid="vega-in-wallet"]';
|
||||
const connectedVegaKey = '[data-testid="connected-vega-key"]';
|
||||
@@ -78,12 +78,12 @@ context(
|
||||
|
||||
cy.getByTestId('currency-title', txTimeout).should(
|
||||
'have.length.above',
|
||||
6
|
||||
3
|
||||
);
|
||||
validateWalletCurrency('Associated', '0.00');
|
||||
validateWalletCurrency('Pending association', '2.00');
|
||||
validateWalletCurrency('Total associated after pending', '2.00');
|
||||
cy.getByTestId('currency-title', txTimeout).should('have.length', 6);
|
||||
cy.getByTestId('currency-title', txTimeout).should('have.length', 3);
|
||||
|
||||
// 0005-ETXN-002
|
||||
verifyEthWalletAssociatedBalance('2.0');
|
||||
@@ -111,12 +111,12 @@ context(
|
||||
stakingPageDisassociateTokens('2');
|
||||
cy.getByTestId('currency-title', txTimeout).should(
|
||||
'have.length.above',
|
||||
6
|
||||
3
|
||||
);
|
||||
validateWalletCurrency('Associated', '2.00');
|
||||
validateWalletCurrency('Pending association', '2.00');
|
||||
validateWalletCurrency('Total associated after pending', '0.00');
|
||||
cy.getByTestId('currency-title', txTimeout).should('have.length', 6);
|
||||
cy.getByTestId('currency-title', txTimeout).should('have.length', 3);
|
||||
cy.getByTestId('eth-wallet-associated-balances', txTimeout).should(
|
||||
'not.exist'
|
||||
);
|
||||
@@ -192,12 +192,12 @@ context(
|
||||
|
||||
cy.getByTestId('currency-title', txTimeout).should(
|
||||
'have.length.above',
|
||||
6
|
||||
3
|
||||
);
|
||||
validateWalletCurrency('Associated', '0.00');
|
||||
validateWalletCurrency('Pending association', '2.00');
|
||||
validateWalletCurrency('Total associated after pending', '2.00');
|
||||
cy.getByTestId('currency-title', txTimeout).should('have.length', 6);
|
||||
cy.getByTestId('currency-title', txTimeout).should('have.length', 3);
|
||||
verifyEthWalletAssociatedBalance('2.0');
|
||||
verifyEthWalletTotalAssociatedBalance('2.0');
|
||||
cy.get(vegaWallet).within(() => {
|
||||
@@ -210,12 +210,12 @@ context(
|
||||
});
|
||||
cy.getByTestId('currency-title', txTimeout).should(
|
||||
'have.length.above',
|
||||
6
|
||||
3
|
||||
);
|
||||
validateWalletCurrency('Associated', '2.00');
|
||||
validateWalletCurrency('Pending association', '1.00');
|
||||
validateWalletCurrency('Total associated after pending', '1.00');
|
||||
cy.getByTestId('currency-title', txTimeout).should('have.length', 6);
|
||||
cy.getByTestId('currency-title', txTimeout).should('have.length', 3);
|
||||
verifyEthWalletAssociatedBalance('1.0');
|
||||
verifyEthWalletTotalAssociatedBalance('1.0');
|
||||
});
|
||||
@@ -266,7 +266,7 @@ context(
|
||||
// 1004-ASSO-008
|
||||
// 1004-ASSO-010
|
||||
// No warning visible as described in AC, but the button is disabled
|
||||
cy.get(ethWalletAssociateButton).click();
|
||||
cy.get(ethWalletAssociateButton).first().click();
|
||||
cy.get(associateWalletRadioButton, { timeout: 30000 }).click();
|
||||
cy.get(tokenSubmitButton, txTimeout).should('be.disabled'); // button disabled with no input
|
||||
cy.get(tokenAmountInputBox, { timeout: 10000 }).type('6500000');
|
||||
@@ -278,12 +278,12 @@ context(
|
||||
vegaWalletAssociate('2');
|
||||
cy.getByTestId('currency-title', txTimeout).should(
|
||||
'have.length.above',
|
||||
6
|
||||
3
|
||||
);
|
||||
validateWalletCurrency('Associated', '0.00');
|
||||
validateWalletCurrency('Pending association', '2.00');
|
||||
validateWalletCurrency('Total associated after pending', '2.00');
|
||||
cy.getByTestId('currency-title', txTimeout).should('have.length', 6);
|
||||
cy.getByTestId('currency-title', txTimeout).should('have.length', 3);
|
||||
validateWalletCurrency('Associated', '2.00');
|
||||
});
|
||||
|
||||
@@ -294,24 +294,24 @@ context(
|
||||
});
|
||||
cy.getByTestId('currency-title', txTimeout).should(
|
||||
'have.length.above',
|
||||
6
|
||||
3
|
||||
);
|
||||
validateWalletCurrency('Associated', '2.00');
|
||||
validateWalletCurrency('Pending association', '2.00');
|
||||
validateWalletCurrency('Total associated after pending', '0.00');
|
||||
cy.getByTestId('currency-title', txTimeout).should('have.length', 6);
|
||||
cy.getByTestId('currency-title', txTimeout).should('have.length', 3);
|
||||
validateWalletCurrency('Associated', '0.00');
|
||||
});
|
||||
|
||||
it('Able to associate tokens to different public key of connected vega wallet', function () {
|
||||
cy.get(ethWalletAssociateButton).click();
|
||||
cy.get(ethWalletAssociateButton).first().click();
|
||||
cy.get(associateWalletRadioButton).click();
|
||||
cy.get(connectedVegaKey).should(
|
||||
'have.text',
|
||||
Cypress.env('vegaWalletPublicKey')
|
||||
);
|
||||
|
||||
cy.get('[data-testid="manage-vega-wallet"]:visible').click();
|
||||
cy.get('[data-testid="manage-vega-wallet"]').click();
|
||||
cy.get('[data-testid="select-keypair-button"]').eq(0).click();
|
||||
cy.get(connectedVegaKey).should(
|
||||
'have.text',
|
||||
|
||||
@@ -17,11 +17,9 @@ context('Home Page - verify elements on page', { tags: '@smoke' }, function () {
|
||||
|
||||
it('should display announcement banner', function () {
|
||||
cy.getByTestId('app-announcement')
|
||||
.should('contain.text', 'TEST ANNOUNCEMENT!')
|
||||
.should('be.visible')
|
||||
.within(() => {
|
||||
cy.getByTestId('external-link')
|
||||
.should('have.attr', 'href', 'https://fairground.wtf')
|
||||
.and('have.text', 'CLICK LINK');
|
||||
cy.getByTestId('external-link').should('exist');
|
||||
});
|
||||
cy.getByTestId('app-announcement-close').should('be.visible').click();
|
||||
cy.getByTestId('app-announcement').should('not.exist');
|
||||
|
||||
@@ -3,14 +3,11 @@ import {
|
||||
navigation,
|
||||
verifyPageHeader,
|
||||
verifyTabHighlighted,
|
||||
waitForSpinner,
|
||||
} from '../../support/common.functions';
|
||||
import {
|
||||
goToMakeNewProposal,
|
||||
governanceProposalType,
|
||||
} from '../../support/governance.functions';
|
||||
import { mockNetworkUpgradeProposal } from '../../support/proposal.functions';
|
||||
|
||||
const proposalDocumentationLink = '[data-testid="proposal-documentation-link"]';
|
||||
const newProposalButton = '[data-testid="new-proposal-link"]';
|
||||
const newProposalLink = '[data-testid="new-proposal-link"]';
|
||||
const governanceDocsUrl = 'https://vega.xyz/governance';
|
||||
const connectToVegaWalletButton = '[data-testid="connect-to-vega-wallet-btn"]';
|
||||
@@ -24,140 +21,56 @@ context(
|
||||
navigateTo(navigation.proposals);
|
||||
});
|
||||
|
||||
it('should have governance tab highlighted', function () {
|
||||
verifyTabHighlighted(navigation.proposals);
|
||||
});
|
||||
|
||||
it('should have GOVERNANCE header visible', function () {
|
||||
verifyPageHeader('Proposals');
|
||||
});
|
||||
|
||||
it('should be able to see a working link for - find out more about Vega governance', function () {
|
||||
// 3001-VOTE-001
|
||||
cy.get(proposalDocumentationLink)
|
||||
.should('be.visible')
|
||||
.and('have.text', 'Find out more about Vega governance')
|
||||
.and('have.attr', 'href')
|
||||
.and('equal', governanceDocsUrl);
|
||||
|
||||
// 3002-PROP-001
|
||||
cy.request(governanceDocsUrl)
|
||||
.its('body')
|
||||
.then((body) => {
|
||||
if (!body.includes('Govern the network')) {
|
||||
assert.include(
|
||||
body,
|
||||
'Govern the network',
|
||||
`Checking that governance link destination includes 'Govern the network' text`
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('should be able to see button for - new proposal', function () {
|
||||
// 3001-VOTE-002
|
||||
cy.get(newProposalLink)
|
||||
.should('be.visible')
|
||||
.and('have.text', 'New proposal')
|
||||
.and('have.attr', 'href')
|
||||
.and('equal', '/proposals/propose');
|
||||
});
|
||||
|
||||
it('should be able to see a connect wallet button - if vega wallet disconnected and user is submitting new proposal', function () {
|
||||
goToMakeNewProposal(governanceProposalType.NETWORK_PARAMETER);
|
||||
cy.get(connectToVegaWalletButton)
|
||||
.should('be.visible')
|
||||
.and('have.text', 'Connect Vega wallet');
|
||||
});
|
||||
|
||||
it('should see open network upgrade proposal on homepage', function () {
|
||||
mockNetworkUpgradeProposal();
|
||||
cy.visit('/');
|
||||
cy.getByTestId('home-proposal-list').within(() => {
|
||||
cy.getByTestId('protocol-upgrade-proposals-list-item').should('exist');
|
||||
cy.getByTestId('protocol-upgrade-proposal-title').should(
|
||||
'have.text',
|
||||
'Vega release v1'
|
||||
);
|
||||
describe('with no network change proposals', function () {
|
||||
it('should have governance tab highlighted', function () {
|
||||
verifyTabHighlighted(navigation.proposals);
|
||||
});
|
||||
});
|
||||
|
||||
it('should see network upgrade proposals in proposals list', function () {
|
||||
mockNetworkUpgradeProposal();
|
||||
navigateTo(navigation.proposals);
|
||||
cy.getByTestId('open-proposals').within(() => {
|
||||
cy.get('li')
|
||||
.eq(0)
|
||||
.should(
|
||||
'have.attr',
|
||||
'data-testid',
|
||||
'protocol-upgrade-proposals-list-item'
|
||||
)
|
||||
.within(() => {
|
||||
cy.get('h2').should('have.text', 'Vega release v1');
|
||||
cy.getByTestId('protocol-upgrade-proposal-type').should(
|
||||
'have.text',
|
||||
'Network Upgrade'
|
||||
);
|
||||
cy.getByTestId('protocol-upgrade-proposal-release-tag').should(
|
||||
'have.text',
|
||||
'Vega release tagv1'
|
||||
);
|
||||
cy.getByTestId('protocol-upgrade-proposal-block-height').should(
|
||||
'have.text',
|
||||
'Upgrade block height2015942'
|
||||
);
|
||||
cy.getByTestId('protocol-upgrade-proposal-status').should(
|
||||
'have.text',
|
||||
'Approved'
|
||||
);
|
||||
it('should have GOVERNANCE header visible', function () {
|
||||
verifyPageHeader('Proposals');
|
||||
});
|
||||
|
||||
it('should be able to see a working link for - find out more about Vega governance', function () {
|
||||
// 3001-VOTE-001
|
||||
cy.get(proposalDocumentationLink)
|
||||
.should('be.visible')
|
||||
.and('have.text', 'Find out more about Vega governance')
|
||||
.and('have.attr', 'href')
|
||||
.and('equal', governanceDocsUrl);
|
||||
|
||||
// 3002-PROP-001
|
||||
cy.request(governanceDocsUrl)
|
||||
.its('body')
|
||||
.then((body) => {
|
||||
if (!body.includes('Govern the network')) {
|
||||
assert.include(
|
||||
body,
|
||||
'Govern the network',
|
||||
`Checking that governance link destination includes 'Govern the network' text`
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
cy.getByTestId('closed-proposals').within(() => {
|
||||
cy.getByTestId('protocol-upgrade-proposals-list-item').should(
|
||||
'have.length',
|
||||
1
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it('should see details of network upgrade proposal', function () {
|
||||
mockNetworkUpgradeProposal();
|
||||
navigateTo(navigation.proposals);
|
||||
cy.getByTestId('protocol-upgrade-proposals-list-item')
|
||||
.first()
|
||||
.find('[data-testid="view-proposal-btn"]')
|
||||
.click();
|
||||
cy.getByTestId('protocol-upgrade-proposal').within(() => {
|
||||
cy.get('h1').should('have.text', 'Vega Release v1');
|
||||
cy.getByTestId('protocol-upgrade-block-height').should(
|
||||
'have.text',
|
||||
'2015942 (currently 2014133)'
|
||||
);
|
||||
cy.getByTestId('protocol-upgrade-state').should(
|
||||
'have.text',
|
||||
'Approved'
|
||||
);
|
||||
cy.getByTestId('protocol-upgrade-release-tag').should(
|
||||
'have.text',
|
||||
'v1'
|
||||
);
|
||||
cy.getByTestId('protocol-upgrade-approval-status')
|
||||
.should('contain.text', '99.98% approval (% validator voting power)')
|
||||
.and('contain.text', '(67% voting power required)');
|
||||
cy.get('h2').should('contain.text', 'Approvers (4/4 validators)');
|
||||
cy.getByTestId('validator-name')
|
||||
.should('have.length', 4)
|
||||
.each(($validator) => {
|
||||
cy.wrap($validator).find('a').should('have.attr', 'href');
|
||||
});
|
||||
cy.getByTestId('validator-voting-power').each(
|
||||
($validatorVotingPower) => {
|
||||
cy.wrap($validatorVotingPower)
|
||||
.invoke('text')
|
||||
.should('contain', '%');
|
||||
}
|
||||
);
|
||||
it('should be able to see button for - new proposal', function () {
|
||||
// 3001-VOTE-002
|
||||
cy.get(newProposalLink)
|
||||
.should('be.visible')
|
||||
.and('have.text', 'New proposal')
|
||||
.and('have.attr', 'href')
|
||||
.and('equal', '/proposals/propose');
|
||||
});
|
||||
|
||||
// Skipping this test for now, the new proposal button no longer takes a user directly
|
||||
// to a proposal form, instead it takes them to a page where they can select a proposal type.
|
||||
// Keeping this test here for now as it can be repurposed to test the new proposal forms.
|
||||
it.skip('should be able to see a connect wallet button - if vega wallet disconnected and new proposal button selected', function () {
|
||||
cy.get(newProposalButton).should('be.visible').click();
|
||||
cy.get(connectToVegaWalletButton)
|
||||
.should('be.visible')
|
||||
.and('have.text', 'Connect Vega wallet');
|
||||
navigateTo(navigation.proposals);
|
||||
waitForSpinner();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -45,12 +45,10 @@ context('View functionality with public key', { tags: '@smoke' }, function () {
|
||||
navigateTo(navigation.proposals);
|
||||
goToMakeNewProposal('Freeform');
|
||||
enterUniqueFreeFormProposalBody('50', 'pub key proposal test');
|
||||
cy.getByTestId('dialog-content')
|
||||
.first()
|
||||
.within(() => {
|
||||
cy.get('h1').should('have.text', 'Transaction failed');
|
||||
cy.getByTestId('Error').should('have.text', expectedErrorTxt);
|
||||
});
|
||||
cy.getByTestId('dialog-content').within(() => {
|
||||
cy.get('h1').should('have.text', 'Transaction failed');
|
||||
cy.getByTestId('Error').should('have.text', expectedErrorTxt);
|
||||
});
|
||||
});
|
||||
|
||||
it('Able to disconnect via banner', function () {
|
||||
|
||||
@@ -3,7 +3,6 @@ import {
|
||||
navigation,
|
||||
verifyPageHeader,
|
||||
} from '../../support/common.functions';
|
||||
import { waitForBeginningOfEpoch } from '../../support/staking.functions';
|
||||
|
||||
const viewToggle = '[data-testid="epoch-reward-view-toggle-total"]';
|
||||
const warning = '[data-testid="callout"]';
|
||||
@@ -38,41 +37,6 @@ context(
|
||||
it('should have toggle for seeing total vs individual rewards', function () {
|
||||
cy.get(viewToggle).should('be.visible');
|
||||
});
|
||||
|
||||
// Skipping due to bug #3471 causing flaky failuress
|
||||
it.skip('should have option to view go to next and previous page', function () {
|
||||
waitForBeginningOfEpoch();
|
||||
cy.getByTestId('page-info')
|
||||
.should('contain.text', 'Page ')
|
||||
.invoke('text')
|
||||
.then(($currentPage) => {
|
||||
const currentPageNumber = Number($currentPage.slice(5));
|
||||
cy.getByTestId('goto-next-page').click();
|
||||
cy.getByTestId('page-info')
|
||||
.invoke('text')
|
||||
.then(($newPageNumber) => {
|
||||
const newPageNumber = Number($newPageNumber.slice(5));
|
||||
expect(newPageNumber).to.be.greaterThan(currentPageNumber);
|
||||
cy.getByTestId('goto-previous-page').click();
|
||||
cy.getByTestId('page-info').should(
|
||||
'contain.text',
|
||||
$currentPage
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should have option to go to last and newest page', function () {
|
||||
waitForBeginningOfEpoch();
|
||||
cy.getByTestId('goto-last-page').click();
|
||||
cy.getByTestId('epoch-total-rewards-table')
|
||||
.last()
|
||||
.find('h2')
|
||||
.first()
|
||||
.should('have.text', 'EPOCH 1');
|
||||
cy.getByTestId('goto-first-page').click();
|
||||
cy.get('h2').should('not.contain.text', 'EPOCH 1');
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
/// <reference types="cypress" />
|
||||
|
||||
import {
|
||||
navigateTo,
|
||||
navigation,
|
||||
verifyPageHeader,
|
||||
verifyTabHighlighted,
|
||||
@@ -33,8 +34,8 @@ const stakeNumberRegex = /^\d*\.?\d*$/;
|
||||
|
||||
context('Validators Page - verify elements on page', function () {
|
||||
before('navigate to validators page', function () {
|
||||
cy.clearAllLocalStorage();
|
||||
cy.visit('/validators');
|
||||
cy.visit('/');
|
||||
navigateTo(navigation.validators);
|
||||
});
|
||||
|
||||
describe('with wallets disconnected', { tags: '@smoke' }, function () {
|
||||
|
||||
@@ -43,25 +43,23 @@ context(
|
||||
|
||||
// 1005-VEST-020 1005-VEST-021
|
||||
it('Tokens in vesting contract for eth wallet is displayed on wallet window', function () {
|
||||
cy.get('[data-testid="vega-in-vesting-contract"]:visible').within(
|
||||
() => {
|
||||
cy.getByTestId('currency-title')
|
||||
.should('contain.text', 'VEGA')
|
||||
.and('contain.text', 'In vesting contract');
|
||||
cy.get('[data-testid="currency-value"]:visible').should(
|
||||
'have.text',
|
||||
lockedTokensInVestingContract
|
||||
);
|
||||
cy.get('[data-testid="currency-locked"]:visible').should(
|
||||
'have.text',
|
||||
lockedTokensInVestingContract
|
||||
);
|
||||
cy.get('[data-testid="currency-unlocked"]:visible').should(
|
||||
'have.text',
|
||||
'0.00'
|
||||
);
|
||||
}
|
||||
);
|
||||
cy.getByTestId('vega-in-vesting-contract').within(() => {
|
||||
cy.getByTestId('currency-title')
|
||||
.should('contain.text', 'VEGA')
|
||||
.and('contain.text', 'In vesting contract');
|
||||
cy.get('[data-testid="currency-value"]:visible').should(
|
||||
'have.text',
|
||||
lockedTokensInVestingContract
|
||||
);
|
||||
cy.get('[data-testid="currency-locked"]:visible').should(
|
||||
'have.text',
|
||||
lockedTokensInVestingContract
|
||||
);
|
||||
cy.get('[data-testid="currency-unlocked"]:visible').should(
|
||||
'have.text',
|
||||
'0.00'
|
||||
);
|
||||
});
|
||||
});
|
||||
// 1005-VEST-022 1005-VEST-023
|
||||
it('Tokens amount displayed in vesting page', function () {
|
||||
|
||||
@@ -17,7 +17,7 @@ const vegaInWallet = '[data-testid="vega-in-wallet"]:visible';
|
||||
const progressBar = '[data-testid="progress-bar"]:visible';
|
||||
const currencyLocked = '[data-testid="currency-locked"]:visible';
|
||||
const currencyUnlocked = '[data-testid="currency-unlocked"]:visible';
|
||||
const dialog = '[role="dialog"]:visible';
|
||||
const dialog = '[role="dialog"]';
|
||||
const dialogHeader = '[data-testid="dialog-title"]';
|
||||
const dialogCloseBtn = '[data-testid="dialog-close"]';
|
||||
|
||||
@@ -68,10 +68,8 @@ context(
|
||||
it('should have connector list visible', function () {
|
||||
const connectList = [
|
||||
'Unknown',
|
||||
'MetaMask',
|
||||
'Coinbase',
|
||||
'MetaMask, Brave or other injected web wallet',
|
||||
'WalletConnect',
|
||||
'WalletConnect Legacy',
|
||||
];
|
||||
cy.get(connectorList).within(() => {
|
||||
cy.get('button').each(($btn, i) => {
|
||||
|
||||
@@ -7,7 +7,7 @@ const walletContainer = 'aside [data-testid="vega-wallet"]';
|
||||
const walletHeader = '[data-testid="wallet-header"] h1';
|
||||
const connectButton = '[data-testid="connect-vega-wallet"]';
|
||||
const getVegaLink = '[data-testid="link"]';
|
||||
const dialog = '[role="dialog"]:visible';
|
||||
const dialog = '[role="dialog"]';
|
||||
const dialogHeader = '[data-testid="dialog-title"]';
|
||||
const walletDialogHeader = '[data-testid="wallet-dialog-title"]';
|
||||
const connectorsList = '[data-testid="connectors-list"]';
|
||||
@@ -35,7 +35,6 @@ context(
|
||||
{ tags: '@regression' },
|
||||
() => {
|
||||
before('visit token home page', () => {
|
||||
cy.clearAllLocalStorage();
|
||||
cy.visit('/');
|
||||
cy.get(walletContainer, { timeout: 60000 }).should('be.visible');
|
||||
});
|
||||
@@ -334,8 +333,10 @@ context(
|
||||
.parent()
|
||||
.siblings()
|
||||
.invoke('text')
|
||||
.then(parseFloat)
|
||||
.should('be.gte', parseFloat(expectedAmount));
|
||||
.then((el) => {
|
||||
const value = parseFloat(el);
|
||||
cy.wrap(value).should('be.gte', parseFloat(expectedAmount));
|
||||
});
|
||||
|
||||
cy.get(vegaWalletCurrencyTitle)
|
||||
.contains(id)
|
||||
|
||||
@@ -12,7 +12,6 @@ context(
|
||||
{ tags: '@smoke' },
|
||||
function () {
|
||||
before('navigate to withdrawals page', function () {
|
||||
cy.clearAllLocalStorage();
|
||||
cy.visit('/');
|
||||
navigateTo(navigation.withdraw);
|
||||
});
|
||||
|
||||
@@ -26,11 +26,9 @@ const topLevelRoutes = [
|
||||
export function navigateTo(page: navigation) {
|
||||
if (!topLevelRoutes.includes(page)) {
|
||||
cy.getByTestId(tokenDropDown, { timeout: 10000 }).eq(0).click();
|
||||
cy.getByTestId('token-dropdown')
|
||||
.first()
|
||||
.within(() => {
|
||||
cy.get(page).eq(0).click();
|
||||
});
|
||||
cy.getByTestId('token-dropdown').within(() => {
|
||||
cy.get(page).eq(0).click();
|
||||
});
|
||||
} else {
|
||||
return cy.get(navigation.section, { timeout: 10000 }).within(() => {
|
||||
cy.get(page).eq(0).click();
|
||||
|
||||
@@ -150,11 +150,22 @@ export function waitForProposalSync() {
|
||||
});
|
||||
}
|
||||
|
||||
export function getSortOrderOfSuppliedArray(suppliedArray: string[]) {
|
||||
const tempArray = [];
|
||||
for (let index = 1; index < suppliedArray.length; index++) {
|
||||
tempArray.push(
|
||||
suppliedArray[index - 1].localeCompare(suppliedArray[index])
|
||||
);
|
||||
}
|
||||
if (tempArray.every((n) => n <= 0)) return 'ascending';
|
||||
else if (tempArray.every((n) => n >= 0)) return 'descending';
|
||||
else return 'unsorted';
|
||||
}
|
||||
|
||||
export function goToMakeNewProposal(proposalType: string) {
|
||||
navigateTo(navigation.proposals);
|
||||
cy.get(newProposalButton).should('be.visible').click();
|
||||
cy.url().should('include', '/proposals/propose');
|
||||
cy.get(navigation.pageSpinner, { timeout: 20000 }).should('not.exist');
|
||||
cy.get('li').should('contain.text', proposalType).and('be.visible');
|
||||
cy.get('li').contains(proposalType).click();
|
||||
}
|
||||
|
||||
@@ -1,9 +1,5 @@
|
||||
import { addSeconds, millisecondsToSeconds } from 'date-fns';
|
||||
import type { ProposalSubmissionBody } from '@vegaprotocol/wallet';
|
||||
import { aliasGQLQuery } from '@vegaprotocol/cypress';
|
||||
import { upgradeProposalsData } from '../fixtures/mocks/network-upgrade';
|
||||
import { proposalsData } from '../fixtures/mocks/proposals';
|
||||
import { nodeData } from '../fixtures/mocks/nodes';
|
||||
|
||||
export function createUpdateNetworkProposalTxBody(): ProposalSubmissionBody {
|
||||
const MIN_CLOSE_SEC = 5;
|
||||
@@ -84,11 +80,3 @@ export function createFreeFormProposalTxBody(): ProposalSubmissionBody {
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function mockNetworkUpgradeProposal() {
|
||||
cy.mockGQL((req) => {
|
||||
aliasGQLQuery(req, 'Nodes', nodeData);
|
||||
aliasGQLQuery(req, 'Proposals', proposalsData);
|
||||
aliasGQLQuery(req, 'ProtocolUpgrades', upgradeProposalsData);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -184,18 +184,16 @@ export function validateValidatorListTotalStakeAndShare(
|
||||
) {
|
||||
cy.contains('Loading...', epochTimeout).should('not.exist');
|
||||
waitForBeginningOfEpoch();
|
||||
cy.get(`[row-id="${positionOnList}"]:visible`)
|
||||
.eq(1)
|
||||
.within(() => {
|
||||
cy.getByTestId(stakeValidatorListTotalStake, epochTimeout).should(
|
||||
'have.text',
|
||||
expectedTotalStake
|
||||
);
|
||||
cy.getByTestId(stakeValidatorListTotalShare, epochTimeout).should(
|
||||
'have.text',
|
||||
expectedTotalShare
|
||||
);
|
||||
});
|
||||
cy.get(`[row-id="${positionOnList}"]`).within(() => {
|
||||
cy.getByTestId(stakeValidatorListTotalStake, epochTimeout).should(
|
||||
'have.text',
|
||||
expectedTotalStake
|
||||
);
|
||||
cy.getByTestId(stakeValidatorListTotalShare, epochTimeout).should(
|
||||
'have.text',
|
||||
expectedTotalShare
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
export function ensureSpecifiedUnstakedTokensAreAssociated(
|
||||
@@ -220,15 +218,13 @@ export function ensureSpecifiedUnstakedTokensAreAssociated(
|
||||
}
|
||||
|
||||
export function closeStakingDialog() {
|
||||
cy.get('[data-testid="dialog-title"]:visible').should(
|
||||
cy.getByTestId('dialog-title').should(
|
||||
'contain.text',
|
||||
'At the beginning of the next epoch'
|
||||
);
|
||||
cy.get('[data-testid="dialog-content"]:visible')
|
||||
.first()
|
||||
.within(() => {
|
||||
cy.get('a').should('have.text', 'Back to Staking').click();
|
||||
});
|
||||
cy.getByTestId('dialog-content').within(() => {
|
||||
cy.get('a').should('have.text', 'Back to Staking').click();
|
||||
});
|
||||
}
|
||||
|
||||
export function validateWalletCurrency(
|
||||
|
||||
@@ -8,8 +8,7 @@ import {
|
||||
} from '@vegaprotocol/smart-contracts';
|
||||
import { ethers, Wallet } from 'ethers';
|
||||
|
||||
const associatedAmountInWallet = '[data-testid="associated-amount"]:visible';
|
||||
const vegaWalletContainer = 'aside [data-testid="vega-wallet"]:visible';
|
||||
const vegaWalletContainer = 'aside [data-testid="vega-wallet"]';
|
||||
const vegaWalletMnemonic = Cypress.env('vegaWalletMnemonic');
|
||||
const vegaWalletPubKey = Cypress.env('vegaWalletPublicKey');
|
||||
const vegaTokenContractAddress = Cypress.env('vegaTokenContractAddress');
|
||||
@@ -60,7 +59,7 @@ export async function faucetAsset(assetEthAddress: string) {
|
||||
}
|
||||
|
||||
export async function vegaWalletTeardown() {
|
||||
cy.get(associatedAmountInWallet)
|
||||
cy.get('[data-testid="associated-amount"]')
|
||||
.should('be.visible')
|
||||
.invoke('text')
|
||||
.then((associatedAmount) => {
|
||||
@@ -69,12 +68,12 @@ export async function vegaWalletTeardown() {
|
||||
$body.find('[data-testid="eth-wallet-associated-balances"]').length ||
|
||||
associatedAmount != '0.00'
|
||||
) {
|
||||
vegaWalletTeardownStaking(stakingBridgeContract);
|
||||
vegaWalletTeardownVesting(vestingContract);
|
||||
vegaWalletTeardownStaking(stakingBridgeContract);
|
||||
}
|
||||
});
|
||||
cy.get(vegaWalletContainer).within(() => {
|
||||
cy.get(associatedAmountInWallet, {
|
||||
cy.getByTestId('associated-amount', {
|
||||
timeout: transactionTimeout,
|
||||
}).contains('0.00', {
|
||||
timeout: transactionTimeout,
|
||||
@@ -91,7 +90,7 @@ export async function vegaWalletSetSpecifiedApprovalAmount(
|
||||
await promiseWithTimeout(
|
||||
token.approve(
|
||||
ethStakingBridgeContractAddress,
|
||||
resetAmount + '0'.repeat(18)
|
||||
resetAmount.concat('000000000000000000')
|
||||
),
|
||||
10 * 60 * 1000,
|
||||
'set approval amount'
|
||||
@@ -105,25 +104,13 @@ async function vegaWalletTeardownStaking(stakingBridgeContract: StakingBridge) {
|
||||
{ timeout: transactionTimeout, log: false }
|
||||
).then((stakeBalance) => {
|
||||
if (Number(stakeBalance) != 0) {
|
||||
cy.get(vegaWalletContainer).within(() => {
|
||||
cy.getByTestId('currency-value')
|
||||
.invoke('text')
|
||||
.then(($associatedAmount) => {
|
||||
cy.wrap(
|
||||
stakingBridgeContract.remove_stake(
|
||||
String(stakeBalance),
|
||||
vegaWalletPubKey
|
||||
),
|
||||
{ timeout: transactionTimeout, log: false }
|
||||
);
|
||||
cy.get("[data-testid='currency-value']")
|
||||
.first()
|
||||
.invoke('text', {
|
||||
timeout: transactionTimeout,
|
||||
})
|
||||
.should('not.eq', $associatedAmount);
|
||||
});
|
||||
});
|
||||
cy.wrap(
|
||||
stakingBridgeContract.remove_stake(
|
||||
String(stakeBalance),
|
||||
vegaWalletPubKey
|
||||
),
|
||||
{ timeout: transactionTimeout, log: false }
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -135,6 +122,9 @@ async function vegaWalletTeardownVesting(vestingContract: TokenVesting) {
|
||||
log: false,
|
||||
}).then((vestingAmount) => {
|
||||
if (Number(vestingAmount) != 0) {
|
||||
// Wait needed to allow time for ganache to process tx for stakingBridgeContract.remove_stake
|
||||
// eslint-disable-next-line cypress/no-unnecessary-waiting
|
||||
cy.wait(1000);
|
||||
cy.wrap(
|
||||
vestingContract.remove_stake(String(vestingAmount), vegaWalletPubKey),
|
||||
{ timeout: transactionTimeout, log: false }
|
||||
|
||||
@@ -14,7 +14,6 @@ NX_VEGA_DOCS_URL=https://docs.vega.xyz/testnet
|
||||
NX_DELEGATIONS_PAGINATION=50
|
||||
NX_TRANCHES_SERVICE_URL=https://tranches-stagnet3-k8s.ops.vega.xyz
|
||||
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/main/announcements.json
|
||||
NX_WALLETCONNECT_PROJECT_ID=fe8091dc35738863e509fc4947525c72
|
||||
|
||||
#Test configuration variables
|
||||
CYPRESS_FAIRGROUND=false
|
||||
|
||||
@@ -5,7 +5,7 @@ NX_ETHERSCAN_URL=https://sepolia.etherscan.io
|
||||
NX_FAIRGROUND=false
|
||||
NX_VEGA_NETWORKS='{"DEVNET":"https://dev.token.vega.xyz","STAGNET3":"https://stagnet3.token.vega.xyz","TESTNET":"https://token.fairground.wtf","MAINNET":"https://token.vega.xyz"}'
|
||||
NX_GITHUB_FEEDBACK_URL=https://github.com/vegaprotocol/feedback/discussions
|
||||
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/test/announcements.json
|
||||
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json
|
||||
NX_VEGA_CONFIG_URL=''
|
||||
|
||||
NX_VEGA_URL=http://localhost:3028/query
|
||||
@@ -20,4 +20,3 @@ NX_TRANCHES_SERVICE_URL=https://tranches-stagnet3-k8s.ops.vega.xyz
|
||||
|
||||
#Test configuration variables
|
||||
CYPRESS_FAIRGROUND=false
|
||||
|
||||
|
||||
@@ -8,4 +8,3 @@ 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_VEGA_EXPLORER_URL=https://dev.explorer.vega.xyz
|
||||
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json
|
||||
|
||||
@@ -17,12 +17,6 @@ import { AppStateProvider } from './contexts/app-state/app-state-provider';
|
||||
import { ContractsProvider } from './contexts/contracts/contracts-provider';
|
||||
import { AppRouter } from './routes';
|
||||
import type { EthereumConfig } from '@vegaprotocol/web3';
|
||||
import {
|
||||
createConnectors,
|
||||
useEthTransactionManager,
|
||||
useEthWithdrawApprovalsManager,
|
||||
useWeb3ConnectStore,
|
||||
} from '@vegaprotocol/web3';
|
||||
import { Web3Provider } from '@vegaprotocol/web3';
|
||||
import { VegaWalletDialogs } from './components/vega-wallet-dialogs';
|
||||
import { VegaWalletProvider } from '@vegaprotocol/wallet';
|
||||
@@ -33,10 +27,10 @@ import {
|
||||
NetworkLoader,
|
||||
useInitializeEnv,
|
||||
} from '@vegaprotocol/environment';
|
||||
import { ENV } from './config';
|
||||
import { createConnectors } from './lib/web3-connectors';
|
||||
import { ENV } from './config/env';
|
||||
import type { InMemoryCacheConfig } from '@apollo/client';
|
||||
import { WithdrawalDialog } from '@vegaprotocol/withdraws';
|
||||
import { SplashLoader } from './components/splash-loader';
|
||||
|
||||
const cache: InMemoryCacheConfig = {
|
||||
typePolicies: {
|
||||
@@ -76,52 +70,20 @@ const cache: InMemoryCacheConfig = {
|
||||
|
||||
const Web3Container = ({
|
||||
chainId,
|
||||
providerUrl,
|
||||
}: {
|
||||
chainId: number;
|
||||
providerUrl: string;
|
||||
}) => {
|
||||
const InitializeHandlers = () => {
|
||||
useEthTransactionManager();
|
||||
useEthWithdrawApprovalsManager();
|
||||
return null;
|
||||
};
|
||||
const [connectors, initializeConnectors] = useWeb3ConnectStore((store) => [
|
||||
store.connectors,
|
||||
store.initialize,
|
||||
]);
|
||||
const { ETHEREUM_PROVIDER_URL, ETH_LOCAL_PROVIDER_URL, ETH_WALLET_MNEMONIC } =
|
||||
useEnvironment();
|
||||
useEffect(() => {
|
||||
if (chainId) {
|
||||
return initializeConnectors(
|
||||
createConnectors(
|
||||
ETHEREUM_PROVIDER_URL,
|
||||
Number(chainId),
|
||||
ETH_LOCAL_PROVIDER_URL,
|
||||
ETH_WALLET_MNEMONIC
|
||||
),
|
||||
Number(chainId)
|
||||
);
|
||||
}
|
||||
}, [
|
||||
chainId,
|
||||
ETHEREUM_PROVIDER_URL,
|
||||
initializeConnectors,
|
||||
ETH_LOCAL_PROVIDER_URL,
|
||||
ETH_WALLET_MNEMONIC,
|
||||
]);
|
||||
const sideBar = React.useMemo(() => {
|
||||
return [<EthWallet />, <VegaWallet />];
|
||||
}, []);
|
||||
|
||||
if (connectors.length === 0) {
|
||||
// Prevent loading when the connectors are not initialized
|
||||
return <SplashLoader />;
|
||||
}
|
||||
|
||||
const Connectors = React.useMemo(() => {
|
||||
return createConnectors(providerUrl, Number(chainId));
|
||||
}, [chainId, providerUrl]);
|
||||
return (
|
||||
<Web3Provider connectors={connectors}>
|
||||
<Web3Connector connectors={connectors} chainId={Number(chainId)}>
|
||||
<Web3Provider connectors={Connectors}>
|
||||
<Web3Connector connectors={Connectors} chainId={Number(chainId)}>
|
||||
<VegaWalletProvider>
|
||||
<ContractsProvider>
|
||||
<AppLoader>
|
||||
@@ -135,7 +97,6 @@ const Web3Container = ({
|
||||
<NetworkInfo />
|
||||
</footer>
|
||||
</AppLayout>
|
||||
<InitializeHandlers />
|
||||
<VegaWalletDialogs />
|
||||
<TransactionModal />
|
||||
<WithdrawalDialog />
|
||||
|
||||
@@ -26,7 +26,6 @@ import {
|
||||
import { Loader } from '@vegaprotocol/ui-toolkit';
|
||||
import colors from 'tailwindcss/colors';
|
||||
import { useBalances } from '../../lib/balances/balances-store';
|
||||
import { useWeb3Disconnect } from '@vegaprotocol/web3';
|
||||
|
||||
const removeLeadingAddressSymbol = (key: string) => {
|
||||
if (key && key.length > 2 && key.slice(0, 2) === '0x') {
|
||||
@@ -185,7 +184,6 @@ export const EthWallet = () => {
|
||||
const { appDispatch } = useAppState();
|
||||
const { account, connector } = useWeb3React();
|
||||
const pendingTxs = usePendingTransactions();
|
||||
const disconnect = useWeb3Disconnect(connector);
|
||||
|
||||
return (
|
||||
<WalletCard>
|
||||
@@ -241,7 +239,7 @@ export const EthWallet = () => {
|
||||
<div className="flex justify-end">
|
||||
<button
|
||||
className="underline"
|
||||
onClick={() => disconnect()}
|
||||
onClick={() => connector.deactivate()}
|
||||
data-testid="disconnect-from-eth-wallet-button"
|
||||
>
|
||||
{t('disconnect')}
|
||||
|
||||
@@ -1,19 +1,15 @@
|
||||
import classNames from 'classnames';
|
||||
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||
import type { ReactNode } from 'react';
|
||||
import { AnnouncementBanner } from '@vegaprotocol/announcements';
|
||||
import { Nav } from '../nav';
|
||||
import { Networks, useEnvironment } from '@vegaprotocol/environment';
|
||||
import React from 'react';
|
||||
|
||||
interface AppLayoutProps {
|
||||
children: ReactNode;
|
||||
}
|
||||
export const AppLayout = ({ children }: AppLayoutProps) => {
|
||||
const { VEGA_ENV, ANNOUNCEMENTS_CONFIG_URL } = useEnvironment();
|
||||
const { isReadOnly } = useVegaWallet();
|
||||
const AppLayoutClasses = classNames(
|
||||
'app w-full max-w-[1500px] mx-auto grid',
|
||||
'app w-full max-w-[1500px] mx-auto grid min-h-full',
|
||||
'border-neutral-700 lg:border-l lg:border-r',
|
||||
'lg:text-body-large',
|
||||
{
|
||||
'grid-rows-[repeat(2,min-content)_1fr_min-content]': !isReadOnly,
|
||||
@@ -21,18 +17,5 @@ export const AppLayout = ({ children }: AppLayoutProps) => {
|
||||
}
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="min-h-full">
|
||||
<div className="lg:text-body-large">
|
||||
{ANNOUNCEMENTS_CONFIG_URL && (
|
||||
<AnnouncementBanner
|
||||
app="governance"
|
||||
configUrl={ANNOUNCEMENTS_CONFIG_URL}
|
||||
/>
|
||||
)}
|
||||
<Nav theme={VEGA_ENV === Networks.TESTNET ? 'yellow' : 'dark'} />
|
||||
</div>
|
||||
<div className={AppLayoutClasses}>{children}</div>
|
||||
</div>
|
||||
);
|
||||
return <div className={AppLayoutClasses}>{children}</div>;
|
||||
};
|
||||
|
||||
@@ -1,16 +1,28 @@
|
||||
import { Networks, useEnvironment } from '@vegaprotocol/environment';
|
||||
import { ViewingAsBanner } from '@vegaprotocol/ui-toolkit';
|
||||
import { AnnouncementBanner } from '@vegaprotocol/announcements';
|
||||
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||
import React from 'react';
|
||||
|
||||
import { Nav } from '../nav';
|
||||
|
||||
export interface TemplateSidebarProps {
|
||||
children: React.ReactNode;
|
||||
sidebar: React.ReactNode[];
|
||||
}
|
||||
|
||||
export function TemplateSidebar({ children, sidebar }: TemplateSidebarProps) {
|
||||
const { VEGA_ENV, ANNOUNCEMENTS_CONFIG_URL } = useEnvironment();
|
||||
const { isReadOnly, pubKey, disconnect } = useVegaWallet();
|
||||
return (
|
||||
<>
|
||||
{ANNOUNCEMENTS_CONFIG_URL && (
|
||||
<AnnouncementBanner
|
||||
app="governance"
|
||||
configUrl={ANNOUNCEMENTS_CONFIG_URL}
|
||||
/>
|
||||
)}
|
||||
<Nav theme={VEGA_ENV === Networks.TESTNET ? 'yellow' : 'dark'} />
|
||||
{isReadOnly ? (
|
||||
<ViewingAsBanner pubKey={pubKey} disconnect={disconnect} />
|
||||
) : null}
|
||||
|
||||
@@ -1,10 +1,5 @@
|
||||
import { Button, Splash } from '@vegaprotocol/ui-toolkit';
|
||||
import {
|
||||
getChainName,
|
||||
useWeb3ConnectStore,
|
||||
useWeb3Disconnect,
|
||||
Web3ConnectDialog,
|
||||
} from '@vegaprotocol/web3';
|
||||
import { getChainName, Web3ConnectDialog } from '@vegaprotocol/web3';
|
||||
import { useWeb3React } from '@web3-react/core';
|
||||
import type { ReactElement } from 'react';
|
||||
import { useCallback, useEffect } from 'react';
|
||||
@@ -56,9 +51,7 @@ interface Web3ContentProps {
|
||||
}
|
||||
|
||||
export const Web3Content = ({ children, appChainId }: Web3ContentProps) => {
|
||||
const { connector, chainId } = useWeb3React();
|
||||
const error = useWeb3ConnectStore((store) => store.error);
|
||||
const disconnect = useWeb3Disconnect(connector);
|
||||
const { error, connector, chainId } = useWeb3React();
|
||||
|
||||
useEffect(() => {
|
||||
if (connector?.connectEagerly) {
|
||||
@@ -74,7 +67,7 @@ export const Web3Content = ({ children, appChainId }: Web3ContentProps) => {
|
||||
<Splash>
|
||||
<div className="flex flex-col items-center gap-12">
|
||||
<p className="text-white">Something went wrong: {error.message}</p>
|
||||
<Button onClick={() => disconnect()}>Disconnect</Button>
|
||||
<Button onClick={() => connector.deactivate()}>Disconnect</Button>
|
||||
</div>
|
||||
</Splash>
|
||||
);
|
||||
@@ -87,7 +80,7 @@ export const Web3Content = ({ children, appChainId }: Web3ContentProps) => {
|
||||
<p className="text-white">
|
||||
This app only works on {getChainName(appChainId)}
|
||||
</p>
|
||||
<Button onClick={() => disconnect()}>Disconnect</Button>
|
||||
<Button onClick={() => connector.deactivate()}>Disconnect</Button>
|
||||
</div>
|
||||
</Splash>
|
||||
);
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import type { ObservableQuery } from '@apollo/client';
|
||||
import { useEffect } from 'react';
|
||||
|
||||
export const useRefreshAfterEpoch = (
|
||||
epochExpiry: string | undefined,
|
||||
refetch: () => void
|
||||
refetch: ObservableQuery['refetch']
|
||||
) => {
|
||||
return useEffect(() => {
|
||||
const epochInterval = setInterval(() => {
|
||||
|
||||
@@ -443,8 +443,8 @@
|
||||
"rewardType": "Reward type",
|
||||
"rewardsAndFeesReceived": "Rewards and fees received",
|
||||
"ThisDoesNotIncludeFeesReceivedForMakersOrLiquidityProviders": "This does not include fees received for makers or liquidity providers",
|
||||
"totalDistributed": "Total distributed",
|
||||
"earnedByMe": "Earned by me",
|
||||
"totalDistributed": "TOTAL DISTRIBUTED",
|
||||
"earnedByMe": "EARNED BY ME",
|
||||
"noRewardsHaveBeenDistributedYet": "NO REWARDS HAVE BEEN DISTRIBUTED YET",
|
||||
"rewardsColAssetHeader": "ASSET",
|
||||
"rewardsColStakingHeader": "STAKING",
|
||||
@@ -595,7 +595,6 @@
|
||||
"noPercentage": "No percentage",
|
||||
"proposalTerms": "Proposal terms",
|
||||
"currentlySetTo": "Currently expected to ",
|
||||
"currently": "currently",
|
||||
"finalOutcomeMayDiffer": "Final outcome may differ",
|
||||
"votingPower": "Voting power",
|
||||
"normalisedVotingPower": "Normalised voting power",
|
||||
@@ -768,7 +767,7 @@
|
||||
"performancePenalty": "Performance penalty",
|
||||
"overstaked": "Overstaked",
|
||||
"overstakedPenalty": "Overstaked penalty",
|
||||
"homeProposalsIntro": "Decisions on the Vega network are on-chain, with tokenholders creating proposals that other tokenholders vote to approve or reject. Network upgrades are proposed and approved by validators.",
|
||||
"homeProposalsIntro": "Decisions on the Vega network are on-chain, with tokenholders creating proposals that other tokenholders vote to approve or reject.",
|
||||
"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.",
|
||||
"homeValidatorsButtonText": "Browse, and stake",
|
||||
@@ -776,19 +775,5 @@
|
||||
"homeRewardsButtonText": "See rewards",
|
||||
"homeVegaTokenIntro": "VEGA Token is a governance asset used to make and vote on proposals, and nominate validators.",
|
||||
"homeVegaTokenButtonText": "Manage tokens",
|
||||
"downloadProposalJson": "Download proposal as JSON",
|
||||
"networkUpgrade": "Network Upgrade",
|
||||
"PROTOCOL_UPGRADE_PROPOSAL_STATUS_APPROVED": "Approved",
|
||||
"PROTOCOL_UPGRADE_PROPOSAL_STATUS_PENDING": "Pending",
|
||||
"PROTOCOL_UPGRADE_PROPOSAL_STATUS_REJECTED": "Rejected",
|
||||
"PROTOCOL_UPGRADE_PROPOSAL_STATUS_UNSPECIFIED": "Unspecified",
|
||||
"vegaRelease{release}": "Vega Release {{release}}",
|
||||
"upgradeBlockHeight": "Upgrade block height",
|
||||
"vegaReleaseTag": "Vega release tag",
|
||||
"approvalStatus": "Approval status",
|
||||
"approvers": "Approvers",
|
||||
"approval (% validator voting power)": "approval (% validator voting power)",
|
||||
"67% voting power required": "67% voting power required",
|
||||
"Token": "Token",
|
||||
"associateVegaNow": "Associate $VEGA now"
|
||||
"downloadProposalJson": "Download proposal as JSON"
|
||||
}
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
export const calculateEpochOffset = ({
|
||||
epochId,
|
||||
page,
|
||||
size,
|
||||
}: {
|
||||
epochId: number;
|
||||
page: number;
|
||||
size: number;
|
||||
}) => {
|
||||
// offset the epoch by the current page number times the page size while making sure it doesn't go below the minimum epoch value
|
||||
return {
|
||||
fromEpoch: Math.max(0, epochId - size * page) + 1,
|
||||
toEpoch: epochId - size * page + size,
|
||||
};
|
||||
};
|
||||
@@ -1,17 +1,20 @@
|
||||
import { ethers } from 'ethers';
|
||||
import type { Web3ReactHooks } from '@web3-react/core';
|
||||
import { initializeConnector } from '@web3-react/core';
|
||||
import { MetaMask } from '@web3-react/metamask';
|
||||
import { WalletConnect } from '@web3-react/walletconnect';
|
||||
import type { Connector } from '@web3-react/types';
|
||||
import { ENV } from '../config/env';
|
||||
import {
|
||||
initializeMetaMaskConnector,
|
||||
initializeWalletConnector,
|
||||
initializeWalletConnectLegacyConnector,
|
||||
initializeCoinbaseConnector,
|
||||
WALLETCONNECT_PROJECT_ID,
|
||||
initializeUrlConnector,
|
||||
} from '@vegaprotocol/web3';
|
||||
import { UrlConnector } from '@vegaprotocol/web3';
|
||||
|
||||
initializeUrlConnector(ENV.localProviderUrl, ENV.ethWalletMnemonic);
|
||||
const [metamask, metamaskHooks] = initializeConnector<MetaMask>(
|
||||
(actions) => new MetaMask(actions)
|
||||
);
|
||||
|
||||
const [urlConnector, urlHooks] = initializeConnector<UrlConnector>(
|
||||
(actions) =>
|
||||
new UrlConnector(actions, ENV.localProviderUrl, ENV.ethWalletMnemonic)
|
||||
);
|
||||
|
||||
export const createDefaultProvider = (providerUrl: string, chainId: number) => {
|
||||
return new ethers.providers.JsonRpcProvider(providerUrl, chainId);
|
||||
@@ -21,14 +24,19 @@ export const createConnectors = (providerUrl: string, chainId: number) => {
|
||||
if (isNaN(chainId)) {
|
||||
throw new Error('Invalid Ethereum chain ID for environment');
|
||||
}
|
||||
|
||||
const [walletconnect, walletconnectHooks] =
|
||||
initializeConnector<WalletConnect>(
|
||||
(actions) =>
|
||||
new WalletConnect(actions, {
|
||||
rpc: {
|
||||
[chainId]: providerUrl,
|
||||
},
|
||||
}),
|
||||
[chainId]
|
||||
);
|
||||
return [
|
||||
ENV.urlConnect
|
||||
? initializeUrlConnector(ENV.localProviderUrl, ENV.ethWalletMnemonic)
|
||||
: null,
|
||||
initializeMetaMaskConnector(),
|
||||
initializeCoinbaseConnector(providerUrl),
|
||||
initializeWalletConnector(WALLETCONNECT_PROJECT_ID, chainId, providerUrl),
|
||||
initializeWalletConnectLegacyConnector(chainId, providerUrl),
|
||||
].filter(Boolean) as unknown as [Connector, Web3ReactHooks][];
|
||||
ENV.urlConnect ? [urlConnector, urlHooks] : null,
|
||||
[metamask, metamaskHooks],
|
||||
[walletconnect, walletconnectHooks],
|
||||
].filter(Boolean) as [Connector, Web3ReactHooks][];
|
||||
};
|
||||
|
||||
@@ -10,31 +10,23 @@ import {
|
||||
import { useDocumentTitle } from '../../hooks/use-document-title';
|
||||
import { useRefreshAfterEpoch } from '../../hooks/use-refresh-after-epoch';
|
||||
import { ProposalsListItem } from '../proposals/components/proposals-list-item';
|
||||
import { ProtocolUpgradeProposalsListItem } from '../proposals/components/protocol-upgrade-proposals-list-item/protocol-upgrade-proposals-list-item';
|
||||
import Routes from '../routes';
|
||||
import { ExternalLinks, removePaginationWrapper } from '@vegaprotocol/utils';
|
||||
import { useNodesQuery } from '../staking/home/__generated__/Nodes';
|
||||
import { useProposalsQuery } from '../proposals/proposals/__generated__/Proposals';
|
||||
import { useProtocolUpgradesQuery } from '../proposals/protocol-upgrade/__generated__/ProtocolUpgradeProposals';
|
||||
import {
|
||||
getNotRejectedProposals,
|
||||
getNotRejectedProtocolUpgradeProposals,
|
||||
} from '../proposals/proposals/proposals-container';
|
||||
import { getNotRejectedProposals } from '../proposals/proposals/proposals-container';
|
||||
import { Heading } from '../../components/heading';
|
||||
import * as Schema from '@vegaprotocol/types';
|
||||
import type { RouteChildProps } from '..';
|
||||
import type { ProposalFieldsFragment } from '../proposals/proposals/__generated__/Proposals';
|
||||
import type { NodesFragmentFragment } from '../staking/home/__generated__/Nodes';
|
||||
import type { ProtocolUpgradeProposalFieldsFragment } from '../proposals/protocol-upgrade/__generated__/ProtocolUpgradeProposals';
|
||||
|
||||
const nodesToShow = 6;
|
||||
|
||||
const HomeProposals = ({
|
||||
proposals,
|
||||
protocolUpgradeProposals,
|
||||
}: {
|
||||
proposals: ProposalFieldsFragment[];
|
||||
protocolUpgradeProposals: ProtocolUpgradeProposalFieldsFragment[];
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
@@ -55,10 +47,6 @@ const HomeProposals = ({
|
||||
data-testid="home-proposal-list"
|
||||
className="grid md:grid-cols-2 lg:grid-cols-1 xl:grid-cols-2 gap-6"
|
||||
>
|
||||
{protocolUpgradeProposals.map((proposal, index) => (
|
||||
<ProtocolUpgradeProposalsListItem key={index} proposal={proposal} />
|
||||
))}
|
||||
|
||||
{proposals.map((proposal) => (
|
||||
<ProposalsListItem key={proposal.id} proposal={proposal} />
|
||||
))}
|
||||
@@ -119,7 +107,20 @@ const HomeNodes = ({
|
||||
|
||||
{trimmedActiveNodes.map(({ id, avatarUrl, name }) => (
|
||||
<div key={id} data-testid="validators" className="col-span-2">
|
||||
<ValidatorDetailsLink id={id} avatarUrl={avatarUrl} name={name} />
|
||||
<Link to={`${Routes.VALIDATORS}/${id}`}>
|
||||
<RoundedWrapper paddingBottom={true} border={false}>
|
||||
<div className="flex items-center justify-center m-[-1rem] p-4 bg-neutral-900 hover:bg-neutral-800">
|
||||
{avatarUrl && (
|
||||
<img
|
||||
className="h-6 w-6 rounded-full mr-2"
|
||||
src={avatarUrl}
|
||||
alt={`Avatar icon for ${name}`}
|
||||
/>
|
||||
)}
|
||||
<span className="text-sm">{name}</span>
|
||||
</div>
|
||||
</RoundedWrapper>
|
||||
</Link>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
@@ -135,35 +136,6 @@ const HomeNodes = ({
|
||||
);
|
||||
};
|
||||
|
||||
interface ValidatorDetailsLinkProps {
|
||||
id: string;
|
||||
avatarUrl: string | null | undefined;
|
||||
name: string;
|
||||
}
|
||||
|
||||
export const ValidatorDetailsLink = ({
|
||||
id,
|
||||
avatarUrl,
|
||||
name,
|
||||
}: ValidatorDetailsLinkProps) => {
|
||||
return (
|
||||
<Link to={`${Routes.VALIDATORS}/${id}`}>
|
||||
<RoundedWrapper paddingBottom={true} border={false}>
|
||||
<div className="flex items-center justify-center m-[-1rem] p-3 bg-neutral-900 hover:bg-neutral-800">
|
||||
{avatarUrl && (
|
||||
<img
|
||||
className="h-6 w-6 rounded-full mr-2"
|
||||
src={avatarUrl}
|
||||
alt={`Avatar icon for ${name}`}
|
||||
/>
|
||||
)}
|
||||
<span className="text-sm">{name}</span>
|
||||
</div>
|
||||
</RoundedWrapper>
|
||||
</Link>
|
||||
);
|
||||
};
|
||||
|
||||
const GovernanceHome = ({ name }: RouteChildProps) => {
|
||||
useDocumentTitle(name);
|
||||
const { t } = useTranslation();
|
||||
@@ -177,16 +149,6 @@ const GovernanceHome = ({ name }: RouteChildProps) => {
|
||||
errorPolicy: 'ignore',
|
||||
});
|
||||
|
||||
const {
|
||||
data: protocolUpgradesData,
|
||||
loading: protocolUpgradesLoading,
|
||||
error: protocolUpgradesError,
|
||||
} = useProtocolUpgradesQuery({
|
||||
pollInterval: 5000,
|
||||
fetchPolicy: 'network-only',
|
||||
errorPolicy: 'ignore',
|
||||
});
|
||||
|
||||
const {
|
||||
data: validatorsData,
|
||||
error: validatorsError,
|
||||
@@ -201,38 +163,11 @@ const GovernanceHome = ({ name }: RouteChildProps) => {
|
||||
proposalsData
|
||||
? getNotRejectedProposals<ProposalFieldsFragment>(
|
||||
proposalsData.proposalsConnection
|
||||
)
|
||||
).slice(0, 3)
|
||||
: [],
|
||||
[proposalsData]
|
||||
);
|
||||
|
||||
const protocolUpgradeProposals = useMemo(
|
||||
() =>
|
||||
protocolUpgradesData
|
||||
? getNotRejectedProtocolUpgradeProposals<ProtocolUpgradeProposalFieldsFragment>(
|
||||
protocolUpgradesData.protocolUpgradeProposals
|
||||
).filter(
|
||||
(p) =>
|
||||
Number(p.upgradeBlockHeight) >
|
||||
Number(protocolUpgradesData.lastBlockHeight)
|
||||
)
|
||||
: [],
|
||||
[protocolUpgradesData]
|
||||
);
|
||||
|
||||
const totalProposalsDesired = 4;
|
||||
const protocolUpgradeProposalsToShow = protocolUpgradeProposals.slice(
|
||||
0,
|
||||
totalProposalsDesired
|
||||
);
|
||||
const proposalsToShow =
|
||||
protocolUpgradeProposalsToShow.length === totalProposalsDesired
|
||||
? []
|
||||
: proposals.slice(
|
||||
0,
|
||||
totalProposalsDesired - protocolUpgradeProposalsToShow.length
|
||||
);
|
||||
|
||||
const activeNodes = removePaginationWrapper(
|
||||
validatorsData?.nodesConnection.edges
|
||||
);
|
||||
@@ -247,14 +182,11 @@ const GovernanceHome = ({ name }: RouteChildProps) => {
|
||||
|
||||
return (
|
||||
<AsyncRenderer
|
||||
loading={proposalsLoading || protocolUpgradesLoading || validatorsLoading}
|
||||
error={proposalsError || protocolUpgradesError || validatorsError}
|
||||
data={proposalsData && protocolUpgradesData && validatorsData}
|
||||
loading={proposalsLoading || validatorsLoading}
|
||||
error={proposalsError || validatorsError}
|
||||
data={proposalsData && validatorsData}
|
||||
>
|
||||
<HomeProposals
|
||||
proposals={proposalsToShow}
|
||||
protocolUpgradeProposals={protocolUpgradeProposalsToShow}
|
||||
/>
|
||||
<HomeProposals proposals={proposals} />
|
||||
|
||||
<HomeNodes
|
||||
activeNodes={activeNodes}
|
||||
|
||||
+1
-1
@@ -63,7 +63,7 @@ const renderComponent = (proposals: ProposalQuery['proposal'][]) => (
|
||||
<MockedProvider mocks={[networkParamsQueryMock]}>
|
||||
<AppStateProvider>
|
||||
<VegaWalletContext.Provider value={mockWalletContext}>
|
||||
<ProposalsList proposals={proposals} protocolUpgradeProposals={[]} />
|
||||
<ProposalsList proposals={proposals} />
|
||||
</VegaWalletContext.Provider>
|
||||
</AppStateProvider>
|
||||
</MockedProvider>
|
||||
|
||||
@@ -3,21 +3,17 @@ import { useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Heading, SubHeading } from '../../../../components/heading';
|
||||
import { ProposalsListItem } from '../proposals-list-item';
|
||||
import { ProtocolUpgradeProposalsListItem } from '../protocol-upgrade-proposals-list-item/protocol-upgrade-proposals-list-item';
|
||||
import { ProposalsListFilter } from '../proposals-list-filter';
|
||||
import Routes from '../../../routes';
|
||||
import { Button } from '@vegaprotocol/ui-toolkit';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { ExternalLinks } from '@vegaprotocol/utils';
|
||||
import { ExternalLink } from '@vegaprotocol/ui-toolkit';
|
||||
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
|
||||
import type { ProposalFieldsFragment } from '../../proposals/__generated__/Proposals';
|
||||
import type { ProtocolUpgradeProposalFieldsFragment } from '../../protocol-upgrade/__generated__/ProtocolUpgradeProposals';
|
||||
import { ExternalLinks } from '@vegaprotocol/utils';
|
||||
import { ExternalLink } from '@vegaprotocol/ui-toolkit';
|
||||
|
||||
interface ProposalsListProps {
|
||||
proposals: Array<ProposalFieldsFragment | ProposalQuery['proposal']>;
|
||||
protocolUpgradeProposals: ProtocolUpgradeProposalFieldsFragment[];
|
||||
lastBlockHeight?: string;
|
||||
}
|
||||
|
||||
interface SortedProposalsProps {
|
||||
@@ -25,16 +21,7 @@ interface SortedProposalsProps {
|
||||
closed: Array<ProposalFieldsFragment | ProposalQuery['proposal']>;
|
||||
}
|
||||
|
||||
interface SortedProtocolUpgradeProposalsProps {
|
||||
open: ProtocolUpgradeProposalFieldsFragment[];
|
||||
closed: ProtocolUpgradeProposalFieldsFragment[];
|
||||
}
|
||||
|
||||
export const ProposalsList = ({
|
||||
proposals,
|
||||
protocolUpgradeProposals,
|
||||
lastBlockHeight,
|
||||
}: ProposalsListProps) => {
|
||||
export const ProposalsList = ({ proposals }: ProposalsListProps) => {
|
||||
const { t } = useTranslation();
|
||||
const [filterString, setFilterString] = useState('');
|
||||
const sortedProposals = proposals.reduce(
|
||||
@@ -52,21 +39,6 @@ export const ProposalsList = ({
|
||||
}
|
||||
);
|
||||
|
||||
const sortedProtocolUpgradeProposals = protocolUpgradeProposals.reduce(
|
||||
(acc: SortedProtocolUpgradeProposalsProps, proposal) => {
|
||||
if (Number(proposal?.upgradeBlockHeight) > Number(lastBlockHeight)) {
|
||||
acc.open.push(proposal);
|
||||
} else {
|
||||
acc.closed.push(proposal);
|
||||
}
|
||||
return acc;
|
||||
},
|
||||
{
|
||||
open: [],
|
||||
closed: [],
|
||||
}
|
||||
);
|
||||
|
||||
const filterPredicate = (
|
||||
p: ProposalFieldsFragment | ProposalQuery['proposal']
|
||||
) =>
|
||||
@@ -93,7 +65,7 @@ export const ProposalsList = ({
|
||||
</div>
|
||||
<p className="mb-8">
|
||||
{t(
|
||||
`The Vega network is governed by the community. View active proposals, vote on them or propose changes to the network. Network upgrades are proposed and approved by validators.`
|
||||
`The Vega network is governed by the community. View active proposals, vote on them or propose changes to the network.`
|
||||
)}{' '}
|
||||
<ExternalLink
|
||||
data-testid="proposal-documentation-link"
|
||||
@@ -108,15 +80,8 @@ export const ProposalsList = ({
|
||||
)}
|
||||
<section className="-mx-4 p-4 mb-8 bg-neutral-800">
|
||||
<SubHeading title={t('openProposals')} />
|
||||
{sortedProposals.open.length > 0 ||
|
||||
sortedProtocolUpgradeProposals.open.length > 0 ? (
|
||||
{sortedProposals.open.length > 0 ? (
|
||||
<ul data-testid="open-proposals">
|
||||
{sortedProtocolUpgradeProposals.open.map((proposal) => (
|
||||
<ProtocolUpgradeProposalsListItem
|
||||
key={proposal.upgradeBlockHeight}
|
||||
proposal={proposal}
|
||||
/>
|
||||
))}
|
||||
{sortedProposals.open.filter(filterPredicate).map((proposal) => (
|
||||
<ProposalsListItem key={proposal?.id} proposal={proposal} />
|
||||
))}
|
||||
@@ -129,16 +94,8 @@ export const ProposalsList = ({
|
||||
</section>
|
||||
<section>
|
||||
<SubHeading title={t('closedProposals')} />
|
||||
{sortedProposals.closed.length > 0 ||
|
||||
sortedProtocolUpgradeProposals.closed.length > 0 ? (
|
||||
{sortedProposals.closed.length > 0 ? (
|
||||
<ul data-testid="closed-proposals">
|
||||
{sortedProtocolUpgradeProposals.closed.map((proposal) => (
|
||||
<ProtocolUpgradeProposalsListItem
|
||||
key={proposal.upgradeBlockHeight}
|
||||
proposal={proposal}
|
||||
/>
|
||||
))}
|
||||
|
||||
{sortedProposals.closed.filter(filterPredicate).map((proposal) => (
|
||||
<ProposalsListItem key={proposal?.id} proposal={proposal} />
|
||||
))}
|
||||
|
||||
-1
@@ -1 +0,0 @@
|
||||
export * from './protocol-update-proposal-detail-approvals';
|
||||
-81
@@ -1,81 +0,0 @@
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import {
|
||||
RoundedWrapper,
|
||||
KeyValueTable,
|
||||
KeyValueTableRow,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import { SubHeading } from '../../../../components/heading';
|
||||
import { ValidatorDetailsLink } from '../../../home';
|
||||
import { getNormalisedVotingPower } from '../../../staking/shared';
|
||||
import type { NodesFragmentFragment } from '../../../staking/home/__generated__/Nodes';
|
||||
|
||||
export interface ProtocolUpdateProposalDetailApprovalsProps {
|
||||
totalConsensusValidators: number;
|
||||
// Consensus validators that have approved the proposal
|
||||
consensusApprovals: NodesFragmentFragment[];
|
||||
consensusApprovalsVotingPowerPercentage: string;
|
||||
}
|
||||
|
||||
export const ProtocolUpdateProposalDetailApprovals = ({
|
||||
totalConsensusValidators,
|
||||
consensusApprovals,
|
||||
consensusApprovalsVotingPowerPercentage,
|
||||
}: ProtocolUpdateProposalDetailApprovalsProps) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="mb-10">
|
||||
<SubHeading title={t('approvalStatus')} />
|
||||
<RoundedWrapper marginBottomLarge={true} paddingBottom={true}>
|
||||
<KeyValueTable data-testid="protocol-upgrade-approval-status">
|
||||
<KeyValueTableRow noBorder={true}>
|
||||
<span>{`${consensusApprovalsVotingPowerPercentage} ${t(
|
||||
'approval (% validator voting power)'
|
||||
)}`}</span>
|
||||
<span>({t('67% voting power required')})</span>
|
||||
</KeyValueTableRow>
|
||||
</KeyValueTable>
|
||||
</RoundedWrapper>
|
||||
</div>
|
||||
|
||||
<div className="mb-10">
|
||||
<SubHeading
|
||||
title={`${t('approvers')} (${
|
||||
consensusApprovals.length
|
||||
}/${totalConsensusValidators} validators)`}
|
||||
/>
|
||||
<RoundedWrapper marginBottomLarge={true} paddingBottom={true}>
|
||||
<KeyValueTable data-testid="protocol-upgrade-approvers">
|
||||
<KeyValueTableRow>
|
||||
<div className="mb-2">{t('validator')}</div>
|
||||
<div className="text-white mb-2">{t('votingPower')}</div>
|
||||
</KeyValueTableRow>
|
||||
|
||||
{consensusApprovals.map((validator, index) => (
|
||||
<KeyValueTableRow
|
||||
noBorder={index === consensusApprovals?.length - 1}
|
||||
key={validator.pubkey}
|
||||
>
|
||||
<div className="-mb-3 mt-1" data-testid={'validator-name'}>
|
||||
<ValidatorDetailsLink
|
||||
id={validator.id}
|
||||
avatarUrl={validator.avatarUrl}
|
||||
name={validator.name}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<span data-testid="validator-voting-power">
|
||||
{getNormalisedVotingPower(
|
||||
validator.rankingScore.votingPower,
|
||||
2
|
||||
)}
|
||||
</span>
|
||||
</KeyValueTableRow>
|
||||
))}
|
||||
</KeyValueTable>
|
||||
</RoundedWrapper>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
-86
@@ -1,86 +0,0 @@
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import { BrowserRouter } from 'react-router-dom';
|
||||
import * as Schema from '@vegaprotocol/types';
|
||||
import { ProtocolUpdateProposalDetailApprovals } from './protocol-update-proposal-detail-approvals';
|
||||
import { getNormalisedVotingPower } from '../../../staking/shared';
|
||||
import type { ProtocolUpdateProposalDetailApprovalsProps } from './protocol-update-proposal-detail-approvals';
|
||||
import type { NodesFragmentFragment } from '../../../staking/home/__generated__/Nodes';
|
||||
|
||||
describe('ProtocolUpdateProposalDetailApprovals', () => {
|
||||
const mockValidators: NodesFragmentFragment[] = [
|
||||
{
|
||||
id: 'ccc022b7e63a4d0a6d3a193c3940c88574060e58a184964c994998d86835a1b4',
|
||||
name: 'Marvin',
|
||||
avatarUrl:
|
||||
'https://upload.wikimedia.org/wikipedia/en/2/25/Marvin-TV-3.jpg',
|
||||
pubkey:
|
||||
'6abc23391a9f888ab240415bf63d6844b03fc360be822f4a1d2cd832d87b2917',
|
||||
stakedTotal: '14182454495731682635157',
|
||||
stakedByOperator: '1000000000000000000000',
|
||||
stakedByDelegates: '13182454495731682635157',
|
||||
pendingStake: '0',
|
||||
rankingScore: {
|
||||
rankingScore: '0.67845061012234727427532760837568',
|
||||
stakeScore: '0.3392701644525644',
|
||||
performanceScore: '0.9998677767864936',
|
||||
votingPower: '3500',
|
||||
status: Schema.ValidatorStatus.VALIDATOR_NODE_STATUS_TENDERMINT,
|
||||
__typename: 'RankingScore',
|
||||
},
|
||||
__typename: 'Node',
|
||||
},
|
||||
];
|
||||
|
||||
const defaultProps = {
|
||||
totalConsensusValidators: 3,
|
||||
consensusApprovals: mockValidators,
|
||||
consensusApprovalsVotingPowerPercentage: '75%',
|
||||
};
|
||||
|
||||
const renderComponent = (
|
||||
props: Partial<ProtocolUpdateProposalDetailApprovalsProps> = {}
|
||||
) => {
|
||||
const mergedProps = {
|
||||
...defaultProps,
|
||||
...props,
|
||||
};
|
||||
return render(
|
||||
<BrowserRouter>
|
||||
<ProtocolUpdateProposalDetailApprovals {...mergedProps} />
|
||||
</BrowserRouter>
|
||||
);
|
||||
};
|
||||
|
||||
it('renders without crashing', () => {
|
||||
renderComponent();
|
||||
expect(
|
||||
screen.getByTestId('protocol-upgrade-approval-status')
|
||||
).toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByTestId('protocol-upgrade-approvers')
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders the correct number of consensus approvers', () => {
|
||||
renderComponent();
|
||||
const validators = screen.getAllByTestId(/validator-name/);
|
||||
expect(validators.length).toBe(mockValidators.length);
|
||||
});
|
||||
|
||||
it('renders the correct approval status with the required percentage', () => {
|
||||
renderComponent();
|
||||
expect(screen.getByText(/75% approval/i)).toBeInTheDocument();
|
||||
expect(screen.getByText(/67% voting power required/i)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders the correct voting power for each validator', () => {
|
||||
renderComponent();
|
||||
mockValidators.forEach((validator) => {
|
||||
const normalisedVotingPower = getNormalisedVotingPower(
|
||||
validator.rankingScore.votingPower,
|
||||
2
|
||||
);
|
||||
expect(screen.getByText(normalisedVotingPower)).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
});
|
||||
-1
@@ -1 +0,0 @@
|
||||
export * from './protocol-upgrade-proposal-detail-header';
|
||||
-18
@@ -1,18 +0,0 @@
|
||||
import { render } from '@testing-library/react';
|
||||
import { ProtocolUpgradeProposalDetailHeader } from './protocol-upgrade-proposal-detail-header';
|
||||
|
||||
describe('ProtocolUpgradeProposalDetailHeader', () => {
|
||||
it('should render successfully', () => {
|
||||
const { baseElement } = render(
|
||||
<ProtocolUpgradeProposalDetailHeader releaseTag="v1.0.0" />
|
||||
);
|
||||
expect(baseElement).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should render the release tag', () => {
|
||||
const { getByText } = render(
|
||||
<ProtocolUpgradeProposalDetailHeader releaseTag="v1.0.0" />
|
||||
);
|
||||
expect(getByText('Vega Release v1.0.0')).toBeTruthy();
|
||||
});
|
||||
});
|
||||
-21
@@ -1,21 +0,0 @@
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Intent, Lozenge } from '@vegaprotocol/ui-toolkit';
|
||||
import { Heading } from '../../../../components/heading';
|
||||
|
||||
export interface ProtocolUpgradeProposalDetailHeaderProps {
|
||||
releaseTag: string;
|
||||
}
|
||||
|
||||
export const ProtocolUpgradeProposalDetailHeader = ({
|
||||
releaseTag,
|
||||
}: ProtocolUpgradeProposalDetailHeaderProps) => {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<>
|
||||
<Heading title={t('vegaRelease{release}', { release: releaseTag })} />
|
||||
<div className="mb-10">
|
||||
<Lozenge variant={Intent.Success}>{t('networkUpgrade')}</Lozenge>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
-1
@@ -1 +0,0 @@
|
||||
export * from './protocol-upgrade-proposal-detail-info';
|
||||
-44
@@ -1,44 +0,0 @@
|
||||
// jest tests for the protocol upgrade proposal detail info component
|
||||
|
||||
import { render } from '@testing-library/react';
|
||||
import { ProtocolUpgradeProposalStatus } from '@vegaprotocol/types';
|
||||
import { ProtocolUpgradeProposalDetailInfo } from './protocol-upgrade-proposal-detail-info';
|
||||
|
||||
const renderComponent = () =>
|
||||
render(
|
||||
<ProtocolUpgradeProposalDetailInfo
|
||||
proposal={{
|
||||
vegaReleaseTag: 'v0.1.234',
|
||||
status:
|
||||
ProtocolUpgradeProposalStatus.PROTOCOL_UPGRADE_PROPOSAL_STATUS_PENDING,
|
||||
upgradeBlockHeight: '12345',
|
||||
approvers: [],
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
||||
describe('ProtocolUpgradeProposalDetailInfo', () => {
|
||||
it('should render successfully', () => {
|
||||
const { baseElement } = renderComponent();
|
||||
expect(baseElement).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should render the upgrade block height', () => {
|
||||
const { getByTestId } = renderComponent();
|
||||
expect(getByTestId('protocol-upgrade-block-height')).toHaveTextContent(
|
||||
'12345'
|
||||
);
|
||||
});
|
||||
|
||||
it('should render the state', () => {
|
||||
const { getByTestId } = renderComponent();
|
||||
expect(getByTestId('protocol-upgrade-state')).toHaveTextContent('Pending');
|
||||
});
|
||||
|
||||
it('should render the vega release tag', () => {
|
||||
const { getByTestId } = renderComponent();
|
||||
expect(getByTestId('protocol-upgrade-release-tag')).toHaveTextContent(
|
||||
'v0.1.234'
|
||||
);
|
||||
});
|
||||
});
|
||||
-58
@@ -1,58 +0,0 @@
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import {
|
||||
KeyValueTable,
|
||||
KeyValueTableRow,
|
||||
RoundedWrapper,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import { SubHeading } from '../../../../components/heading';
|
||||
import type { ProtocolUpgradeProposalFieldsFragment } from '../../protocol-upgrade/__generated__/ProtocolUpgradeProposals';
|
||||
|
||||
export interface ProtocolUpgradeProposalDetailInfoProps {
|
||||
proposal: ProtocolUpgradeProposalFieldsFragment;
|
||||
lastBlockHeight?: string;
|
||||
}
|
||||
|
||||
export const ProtocolUpgradeProposalDetailInfo = ({
|
||||
proposal,
|
||||
lastBlockHeight,
|
||||
}: ProtocolUpgradeProposalDetailInfoProps) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<div className="mb-10">
|
||||
<SubHeading title={t('proposal')} />
|
||||
<RoundedWrapper marginBottomLarge={true} paddingBottom={true}>
|
||||
<KeyValueTable data-testid="protocol-upgrade-proposal-details">
|
||||
<KeyValueTableRow>
|
||||
<span className="uppercase">{t('upgradeBlockHeight')}</span>
|
||||
|
||||
<span data-testid="protocol-upgrade-block-height">
|
||||
{proposal.upgradeBlockHeight}{' '}
|
||||
{lastBlockHeight && (
|
||||
<>
|
||||
({t('currently')} {lastBlockHeight})
|
||||
</>
|
||||
)}
|
||||
</span>
|
||||
</KeyValueTableRow>
|
||||
|
||||
<KeyValueTableRow>
|
||||
<span className="uppercase">{t('state')}</span>
|
||||
|
||||
<span data-testid="protocol-upgrade-state">
|
||||
{t(`${proposal.status}`)}
|
||||
</span>
|
||||
</KeyValueTableRow>
|
||||
|
||||
<KeyValueTableRow noBorder={true}>
|
||||
<span className="uppercase">{t('vegaReleaseTag')}</span>
|
||||
|
||||
<span data-testid="protocol-upgrade-release-tag">
|
||||
{proposal.vegaReleaseTag}
|
||||
</span>
|
||||
</KeyValueTableRow>
|
||||
</KeyValueTable>
|
||||
</RoundedWrapper>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
-69
@@ -1,69 +0,0 @@
|
||||
import React from 'react';
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import { BrowserRouter } from 'react-router-dom';
|
||||
import { ProtocolUpgradeProposalsListItem } from './protocol-upgrade-proposals-list-item';
|
||||
import { ProtocolUpgradeProposalStatus } from '@vegaprotocol/types';
|
||||
import type { ProtocolUpgradeProposalFieldsFragment } from '../../protocol-upgrade/__generated__/ProtocolUpgradeProposals';
|
||||
|
||||
const proposal = {
|
||||
status:
|
||||
ProtocolUpgradeProposalStatus.PROTOCOL_UPGRADE_PROPOSAL_STATUS_PENDING,
|
||||
vegaReleaseTag: 'v1.0.0',
|
||||
upgradeBlockHeight: '12345',
|
||||
} as ProtocolUpgradeProposalFieldsFragment;
|
||||
|
||||
const renderComponent = (proposal: ProtocolUpgradeProposalFieldsFragment) =>
|
||||
render(
|
||||
<BrowserRouter>
|
||||
<ProtocolUpgradeProposalsListItem proposal={proposal} />
|
||||
</BrowserRouter>
|
||||
);
|
||||
|
||||
describe('ProtocolUpgradeProposalsListItem', () => {
|
||||
it('renders the correct status icon for each proposal status', () => {
|
||||
const statuses = [
|
||||
{
|
||||
status:
|
||||
ProtocolUpgradeProposalStatus.PROTOCOL_UPGRADE_PROPOSAL_STATUS_REJECTED,
|
||||
icon: 'protocol-upgrade-proposal-status-icon-rejected',
|
||||
},
|
||||
{
|
||||
status:
|
||||
ProtocolUpgradeProposalStatus.PROTOCOL_UPGRADE_PROPOSAL_STATUS_PENDING,
|
||||
icon: 'protocol-upgrade-proposal-status-icon-pending',
|
||||
},
|
||||
{
|
||||
status:
|
||||
ProtocolUpgradeProposalStatus.PROTOCOL_UPGRADE_PROPOSAL_STATUS_APPROVED,
|
||||
icon: 'protocol-upgrade-proposal-status-icon-approved',
|
||||
},
|
||||
{
|
||||
status:
|
||||
ProtocolUpgradeProposalStatus.PROTOCOL_UPGRADE_PROPOSAL_STATUS_UNSPECIFIED,
|
||||
icon: 'protocol-upgrade-proposal-status-icon-unspecified',
|
||||
},
|
||||
];
|
||||
|
||||
statuses.forEach(({ status, icon }) => {
|
||||
renderComponent({ ...proposal, status });
|
||||
const statusIcon = screen.getByTestId(icon);
|
||||
expect(statusIcon).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
it('renders the correct Vega release tag', () => {
|
||||
renderComponent(proposal);
|
||||
const releaseTag = screen.getByTestId(
|
||||
'protocol-upgrade-proposal-release-tag'
|
||||
);
|
||||
expect(releaseTag).toHaveTextContent(proposal.vegaReleaseTag);
|
||||
});
|
||||
|
||||
it('renders the correct upgrade block height', () => {
|
||||
renderComponent(proposal);
|
||||
const blockHeight = screen.getByTestId(
|
||||
'protocol-upgrade-proposal-block-height'
|
||||
);
|
||||
expect(blockHeight).toHaveTextContent(proposal.upgradeBlockHeight);
|
||||
});
|
||||
});
|
||||
-124
@@ -1,124 +0,0 @@
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Link } from 'react-router-dom';
|
||||
import {
|
||||
Button,
|
||||
Icon,
|
||||
Intent,
|
||||
Lozenge,
|
||||
RoundedWrapper,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import { stripFullStops } from '@vegaprotocol/utils';
|
||||
import { ProtocolUpgradeProposalStatus } from '@vegaprotocol/types';
|
||||
import { SubHeading } from '../../../../components/heading';
|
||||
import type { ReactNode } from 'react';
|
||||
import type { ProtocolUpgradeProposalFieldsFragment } from '../../protocol-upgrade/__generated__/ProtocolUpgradeProposals';
|
||||
import Routes from '../../../routes';
|
||||
|
||||
interface ProtocolProposalsListItemProps {
|
||||
proposal: ProtocolUpgradeProposalFieldsFragment;
|
||||
}
|
||||
|
||||
export const ProtocolUpgradeProposalsListItem = ({
|
||||
proposal,
|
||||
}: ProtocolProposalsListItemProps) => {
|
||||
const { t } = useTranslation();
|
||||
if (!proposal || !proposal.upgradeBlockHeight) return null;
|
||||
|
||||
let proposalStatusIcon: ReactNode;
|
||||
|
||||
switch (proposal.status) {
|
||||
case ProtocolUpgradeProposalStatus.PROTOCOL_UPGRADE_PROPOSAL_STATUS_REJECTED:
|
||||
proposalStatusIcon = (
|
||||
<div data-testid="protocol-upgrade-proposal-status-icon-rejected">
|
||||
<Icon name={'cross'} />
|
||||
</div>
|
||||
);
|
||||
break;
|
||||
case ProtocolUpgradeProposalStatus.PROTOCOL_UPGRADE_PROPOSAL_STATUS_PENDING:
|
||||
proposalStatusIcon = (
|
||||
<div data-testid="protocol-upgrade-proposal-status-icon-pending">
|
||||
<Icon name={'time'} />
|
||||
</div>
|
||||
);
|
||||
break;
|
||||
case ProtocolUpgradeProposalStatus.PROTOCOL_UPGRADE_PROPOSAL_STATUS_APPROVED:
|
||||
proposalStatusIcon = (
|
||||
<div data-testid="protocol-upgrade-proposal-status-icon-approved">
|
||||
<Icon name={'tick'} />
|
||||
</div>
|
||||
);
|
||||
break;
|
||||
case ProtocolUpgradeProposalStatus.PROTOCOL_UPGRADE_PROPOSAL_STATUS_UNSPECIFIED:
|
||||
proposalStatusIcon = (
|
||||
<div data-testid="protocol-upgrade-proposal-status-icon-unspecified">
|
||||
<Icon name={'disable'} />
|
||||
</div>
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
return (
|
||||
<li
|
||||
id={proposal.upgradeBlockHeight}
|
||||
data-testid="protocol-upgrade-proposals-list-item"
|
||||
>
|
||||
<RoundedWrapper paddingBottom={true} heightFull={true}>
|
||||
<div
|
||||
data-testid="protocol-upgrade-proposal-title"
|
||||
className="text-sm mb-2"
|
||||
>
|
||||
<SubHeading title={`Vega release ${proposal.vegaReleaseTag}`} />
|
||||
</div>
|
||||
|
||||
<div className="text-sm">
|
||||
<div
|
||||
data-testid="protocol-upgrade-proposal-type"
|
||||
className="flex items-center gap-2 mb-4"
|
||||
>
|
||||
<Lozenge variant={Intent.Success}>{t('networkUpgrade')}</Lozenge>
|
||||
</div>
|
||||
|
||||
<div
|
||||
data-testid="protocol-upgrade-proposal-release-tag"
|
||||
className="mb-2"
|
||||
>
|
||||
<span className="pr-2">{t('vegaReleaseTag')}</span>
|
||||
<Lozenge>{proposal.vegaReleaseTag}</Lozenge>
|
||||
</div>
|
||||
|
||||
<div
|
||||
data-testid="protocol-upgrade-proposal-block-height"
|
||||
className="mb-2"
|
||||
>
|
||||
<span className="pr-2">{t('upgradeBlockHeight')}</span>
|
||||
<Lozenge>{proposal.upgradeBlockHeight}</Lozenge>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-[1fr_auto] mt-3 items-start gap-2">
|
||||
<div className="col-start-1 row-start-1 text-white">
|
||||
<div
|
||||
data-testid="protocol-upgrade-proposal-status"
|
||||
className="flex items-center gap-2"
|
||||
>
|
||||
<span>{t(`${proposal.status}`)}</span>
|
||||
<span>{proposalStatusIcon}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="col-start-2 row-start-2 justify-self-end">
|
||||
<Link
|
||||
to={`${Routes.PROPOSALS}/protocol-upgrade/${stripFullStops(
|
||||
proposal.vegaReleaseTag
|
||||
)}`}
|
||||
>
|
||||
<Button data-testid="view-proposal-btn" size="sm">
|
||||
{t('View')}
|
||||
</Button>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</RoundedWrapper>
|
||||
</li>
|
||||
);
|
||||
};
|
||||
@@ -5,18 +5,13 @@ import { useTranslation } from 'react-i18next';
|
||||
import { SplashLoader } from '../../../components/splash-loader';
|
||||
import { ProposalsList } from '../components/proposals-list';
|
||||
import { useProposalsQuery } from './__generated__/Proposals';
|
||||
import type { ProposalFieldsFragment } from './__generated__/Proposals';
|
||||
import type { NodeConnection, NodeEdge } from '@vegaprotocol/utils';
|
||||
import { getNodes } from '@vegaprotocol/utils';
|
||||
import flow from 'lodash/flow';
|
||||
import {
|
||||
ProposalState,
|
||||
ProtocolUpgradeProposalStatus,
|
||||
} from '@vegaprotocol/types';
|
||||
import type { NodeConnection, NodeEdge } from '@vegaprotocol/utils';
|
||||
import type { ProposalFieldsFragment } from './__generated__/Proposals';
|
||||
import type { ProtocolUpgradeProposalFieldsFragment } from '../protocol-upgrade/__generated__/ProtocolUpgradeProposals';
|
||||
import { ProposalState } from '@vegaprotocol/types';
|
||||
|
||||
import orderBy from 'lodash/orderBy';
|
||||
import { useProtocolUpgradesQuery } from '../protocol-upgrade/__generated__/ProtocolUpgradeProposals';
|
||||
|
||||
const orderByDate = (arr: ProposalFieldsFragment[]) =>
|
||||
orderBy(
|
||||
@@ -25,15 +20,6 @@ const orderByDate = (arr: ProposalFieldsFragment[]) =>
|
||||
['desc', 'desc']
|
||||
);
|
||||
|
||||
const orderByUpgradeBlockHeight = (
|
||||
arr: ProtocolUpgradeProposalFieldsFragment[]
|
||||
) =>
|
||||
orderBy(
|
||||
arr,
|
||||
[(p) => p?.upgradeBlockHeight, (p) => p.vegaReleaseTag],
|
||||
['desc', 'desc']
|
||||
);
|
||||
|
||||
export function getNotRejectedProposals<T extends ProposalFieldsFragment>(
|
||||
data?: NodeConnection<NodeEdge<T>> | null
|
||||
): T[] {
|
||||
@@ -46,21 +32,6 @@ export function getNotRejectedProposals<T extends ProposalFieldsFragment>(
|
||||
])(data);
|
||||
}
|
||||
|
||||
export function getNotRejectedProtocolUpgradeProposals<
|
||||
T extends ProtocolUpgradeProposalFieldsFragment
|
||||
>(data?: NodeConnection<NodeEdge<T>> | null): T[] {
|
||||
return flow([
|
||||
(data) =>
|
||||
getNodes<ProtocolUpgradeProposalFieldsFragment>(data, (p) =>
|
||||
p
|
||||
? p.status !==
|
||||
ProtocolUpgradeProposalStatus.PROTOCOL_UPGRADE_PROPOSAL_STATUS_REJECTED
|
||||
: false
|
||||
),
|
||||
orderByUpgradeBlockHeight,
|
||||
])(data);
|
||||
}
|
||||
|
||||
export const ProposalsContainer = () => {
|
||||
const { t } = useTranslation();
|
||||
const { data, loading, error } = useProposalsQuery({
|
||||
@@ -69,16 +40,6 @@ export const ProposalsContainer = () => {
|
||||
errorPolicy: 'ignore',
|
||||
});
|
||||
|
||||
const {
|
||||
data: protocolUpgradesData,
|
||||
loading: protocolUpgradesLoading,
|
||||
error: protocolUpgradesError,
|
||||
} = useProtocolUpgradesQuery({
|
||||
pollInterval: 5000,
|
||||
fetchPolicy: 'network-only',
|
||||
errorPolicy: 'ignore',
|
||||
});
|
||||
|
||||
const proposals = useMemo(
|
||||
() =>
|
||||
getNotRejectedProposals<ProposalFieldsFragment>(
|
||||
@@ -87,25 +48,15 @@ export const ProposalsContainer = () => {
|
||||
[data]
|
||||
);
|
||||
|
||||
const protocolUpgradeProposals = useMemo(
|
||||
() =>
|
||||
protocolUpgradesData
|
||||
? getNotRejectedProtocolUpgradeProposals<ProtocolUpgradeProposalFieldsFragment>(
|
||||
protocolUpgradesData.protocolUpgradeProposals
|
||||
)
|
||||
: [],
|
||||
[protocolUpgradesData]
|
||||
);
|
||||
|
||||
if (error || protocolUpgradesError) {
|
||||
if (error) {
|
||||
return (
|
||||
<Callout intent={Intent.Danger} title={t('Something went wrong')}>
|
||||
<pre>{error?.message || protocolUpgradesError?.message}</pre>
|
||||
<pre>{error.message}</pre>
|
||||
</Callout>
|
||||
);
|
||||
}
|
||||
|
||||
if (loading || protocolUpgradesLoading) {
|
||||
if (loading) {
|
||||
return (
|
||||
<Splash>
|
||||
<SplashLoader />
|
||||
@@ -113,11 +64,5 @@ export const ProposalsContainer = () => {
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<ProposalsList
|
||||
proposals={proposals}
|
||||
protocolUpgradeProposals={protocolUpgradeProposals}
|
||||
lastBlockHeight={protocolUpgradesData?.lastBlockHeight}
|
||||
/>
|
||||
);
|
||||
return <ProposalsList proposals={proposals} />;
|
||||
};
|
||||
|
||||
-17
@@ -1,17 +0,0 @@
|
||||
fragment ProtocolUpgradeProposalFields on ProtocolUpgradeProposal {
|
||||
upgradeBlockHeight
|
||||
vegaReleaseTag
|
||||
approvers
|
||||
status
|
||||
}
|
||||
|
||||
query ProtocolUpgrades {
|
||||
lastBlockHeight
|
||||
protocolUpgradeProposals {
|
||||
edges {
|
||||
node {
|
||||
...ProtocolUpgradeProposalFields
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Generated
-59
@@ -1,59 +0,0 @@
|
||||
import * as Types from '@vegaprotocol/types';
|
||||
|
||||
import { gql } from '@apollo/client';
|
||||
import * as Apollo from '@apollo/client';
|
||||
const defaultOptions = {} as const;
|
||||
export type ProtocolUpgradeProposalFieldsFragment = { __typename?: 'ProtocolUpgradeProposal', upgradeBlockHeight: string, vegaReleaseTag: string, approvers: Array<string>, status: Types.ProtocolUpgradeProposalStatus };
|
||||
|
||||
export type ProtocolUpgradesQueryVariables = Types.Exact<{ [key: string]: never; }>;
|
||||
|
||||
|
||||
export type ProtocolUpgradesQuery = { __typename?: 'Query', lastBlockHeight: string, protocolUpgradeProposals?: { __typename?: 'ProtocolUpgradeProposalConnection', edges?: Array<{ __typename?: 'ProtocolUpgradeProposalEdge', node: { __typename?: 'ProtocolUpgradeProposal', upgradeBlockHeight: string, vegaReleaseTag: string, approvers: Array<string>, status: Types.ProtocolUpgradeProposalStatus } }> | null } | null };
|
||||
|
||||
export const ProtocolUpgradeProposalFieldsFragmentDoc = gql`
|
||||
fragment ProtocolUpgradeProposalFields on ProtocolUpgradeProposal {
|
||||
upgradeBlockHeight
|
||||
vegaReleaseTag
|
||||
approvers
|
||||
status
|
||||
}
|
||||
`;
|
||||
export const ProtocolUpgradesDocument = gql`
|
||||
query ProtocolUpgrades {
|
||||
lastBlockHeight
|
||||
protocolUpgradeProposals {
|
||||
edges {
|
||||
node {
|
||||
...ProtocolUpgradeProposalFields
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
${ProtocolUpgradeProposalFieldsFragmentDoc}`;
|
||||
|
||||
/**
|
||||
* __useProtocolUpgradesQuery__
|
||||
*
|
||||
* To run a query within a React component, call `useProtocolUpgradesQuery` and pass it any options that fit your needs.
|
||||
* When your component renders, `useProtocolUpgradesQuery` returns an object from Apollo Client that contains loading, error, and data properties
|
||||
* you can use to render your UI.
|
||||
*
|
||||
* @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
|
||||
*
|
||||
* @example
|
||||
* const { data, loading, error } = useProtocolUpgradesQuery({
|
||||
* variables: {
|
||||
* },
|
||||
* });
|
||||
*/
|
||||
export function useProtocolUpgradesQuery(baseOptions?: Apollo.QueryHookOptions<ProtocolUpgradesQuery, ProtocolUpgradesQueryVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return Apollo.useQuery<ProtocolUpgradesQuery, ProtocolUpgradesQueryVariables>(ProtocolUpgradesDocument, options);
|
||||
}
|
||||
export function useProtocolUpgradesLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<ProtocolUpgradesQuery, ProtocolUpgradesQueryVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return Apollo.useLazyQuery<ProtocolUpgradesQuery, ProtocolUpgradesQueryVariables>(ProtocolUpgradesDocument, options);
|
||||
}
|
||||
export type ProtocolUpgradesQueryHookResult = ReturnType<typeof useProtocolUpgradesQuery>;
|
||||
export type ProtocolUpgradesLazyQueryHookResult = ReturnType<typeof useProtocolUpgradesLazyQuery>;
|
||||
export type ProtocolUpgradesQueryResult = Apollo.QueryResult<ProtocolUpgradesQuery, ProtocolUpgradesQueryVariables>;
|
||||
@@ -1 +0,0 @@
|
||||
export { ProtocolUpgradeProposalContainer as default } from './protocol-upgrade-proposal-container';
|
||||
-78
@@ -1,78 +0,0 @@
|
||||
import { useEffect, useMemo } from 'react';
|
||||
import { useParams } from 'react-router-dom';
|
||||
import { AsyncRenderer } from '@vegaprotocol/ui-toolkit';
|
||||
import { removePaginationWrapper, stripFullStops } from '@vegaprotocol/utils';
|
||||
import * as Schema from '@vegaprotocol/types';
|
||||
|
||||
import { ProtocolUpgradeProposal } from './protocol-upgrade-proposal';
|
||||
import { ProposalNotFound } from '../components/proposal-not-found';
|
||||
import { useProtocolUpgradesQuery } from './__generated__/ProtocolUpgradeProposals';
|
||||
import { useNodesQuery } from '../../staking/home/__generated__/Nodes';
|
||||
import { useRefreshAfterEpoch } from '../../../hooks/use-refresh-after-epoch';
|
||||
|
||||
export const ProtocolUpgradeProposalContainer = () => {
|
||||
const params = useParams<{ proposalReleaseTag: string }>();
|
||||
|
||||
const { data, loading, error, refetch } = useProtocolUpgradesQuery({
|
||||
fetchPolicy: 'network-only',
|
||||
errorPolicy: 'ignore',
|
||||
skip: !params.proposalReleaseTag,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
const interval = setInterval(refetch, 1000);
|
||||
return () => clearInterval(interval);
|
||||
}, [refetch]);
|
||||
|
||||
const {
|
||||
data: nodesData,
|
||||
error: nodesError,
|
||||
loading: nodesLoading,
|
||||
refetch: nodesRefetch,
|
||||
} = useNodesQuery();
|
||||
|
||||
useRefreshAfterEpoch(nodesData?.epoch.timestamps.expiry, nodesRefetch);
|
||||
|
||||
const protocolUpgradeProposal = useMemo(() => {
|
||||
if (!data?.protocolUpgradeProposals) return null;
|
||||
|
||||
const proposals = removePaginationWrapper(
|
||||
data.protocolUpgradeProposals.edges
|
||||
);
|
||||
|
||||
return proposals.find(
|
||||
({ vegaReleaseTag }) =>
|
||||
params.proposalReleaseTag &&
|
||||
params.proposalReleaseTag === stripFullStops(vegaReleaseTag)
|
||||
);
|
||||
}, [data, params.proposalReleaseTag]);
|
||||
|
||||
const consensusValidators = useMemo(() => {
|
||||
if (!nodesData?.nodesConnection.edges) return null;
|
||||
|
||||
const nodes = removePaginationWrapper(nodesData.nodesConnection.edges);
|
||||
|
||||
return nodes.filter(
|
||||
({ rankingScore: { status } }) =>
|
||||
status === Schema.ValidatorStatus.VALIDATOR_NODE_STATUS_TENDERMINT
|
||||
);
|
||||
}, [nodesData]);
|
||||
|
||||
return (
|
||||
<AsyncRenderer
|
||||
loading={loading || nodesLoading}
|
||||
error={error || nodesError}
|
||||
data={{ ...data, ...nodesData }}
|
||||
>
|
||||
{protocolUpgradeProposal ? (
|
||||
<ProtocolUpgradeProposal
|
||||
proposal={protocolUpgradeProposal}
|
||||
lastBlockHeight={data?.lastBlockHeight}
|
||||
consensusValidators={consensusValidators}
|
||||
/>
|
||||
) : (
|
||||
<ProposalNotFound />
|
||||
)}
|
||||
</AsyncRenderer>
|
||||
);
|
||||
};
|
||||
-134
@@ -1,134 +0,0 @@
|
||||
import { render } from '@testing-library/react';
|
||||
import * as Schema from '@vegaprotocol/types';
|
||||
import {
|
||||
ProtocolUpgradeProposal,
|
||||
getConsensusApprovals,
|
||||
getConsensusApprovalsVotingPower,
|
||||
getConsensusApprovalsVotingPowerPercentage,
|
||||
} from './protocol-upgrade-proposal';
|
||||
import { ProtocolUpgradeProposalStatus } from '@vegaprotocol/types';
|
||||
import { getNormalisedVotingPower } from '../../staking/shared';
|
||||
import type { NodesFragmentFragment } from '../../staking/home/__generated__/Nodes';
|
||||
import type { ProtocolUpgradeProposalFieldsFragment } from './__generated__/ProtocolUpgradeProposals';
|
||||
|
||||
const mockProposal: ProtocolUpgradeProposalFieldsFragment = {
|
||||
vegaReleaseTag: 'v0.1.234',
|
||||
status:
|
||||
ProtocolUpgradeProposalStatus.PROTOCOL_UPGRADE_PROPOSAL_STATUS_PENDING,
|
||||
upgradeBlockHeight: '12345',
|
||||
approvers: [],
|
||||
};
|
||||
|
||||
const mockConsensusValidators: NodesFragmentFragment[] = [
|
||||
{
|
||||
id: 'ccc022b7e63a4d0a6d3a193c3940c88574060e58a184964c994998d86835a1b4',
|
||||
name: 'Marvin',
|
||||
avatarUrl: 'https://upload.wikimedia.org/wikipedia/en/2/25/Marvin-TV-3.jpg',
|
||||
pubkey: '6abc23391a9f888ab240415bf63d6844b03fc360be822f4a1d2cd832d87b2917',
|
||||
stakedTotal: '14182454495731682635157',
|
||||
stakedByOperator: '1000000000000000000000',
|
||||
stakedByDelegates: '13182454495731682635157',
|
||||
pendingStake: '0',
|
||||
rankingScore: {
|
||||
rankingScore: '0.67845061012234727427532760837568',
|
||||
stakeScore: '0.3392701644525644',
|
||||
performanceScore: '0.9998677767864936',
|
||||
votingPower: '3500',
|
||||
status: Schema.ValidatorStatus.VALIDATOR_NODE_STATUS_TENDERMINT,
|
||||
__typename: 'RankingScore',
|
||||
},
|
||||
__typename: 'Node',
|
||||
},
|
||||
{
|
||||
id: 'ccc022b7e63a4d0a6d3a193c3940c88574060e58a184964c994998d86835a1b5',
|
||||
name: 'Barvin',
|
||||
avatarUrl: 'https://upload.wikimedia.org/wikipedia/en/2/25/Barvin-TV-3.jpg',
|
||||
pubkey: '6abc23391a9f888ab240415bf63d6844b03fc360be822f4a1d2cd832d87b291b',
|
||||
stakedTotal: '1418245449573168263',
|
||||
stakedByOperator: '100000000000000000',
|
||||
stakedByDelegates: '1318245449573168263',
|
||||
pendingStake: '0',
|
||||
rankingScore: {
|
||||
rankingScore: '0.5',
|
||||
stakeScore: '0.3',
|
||||
performanceScore: '0.98',
|
||||
votingPower: '2500',
|
||||
status: Schema.ValidatorStatus.VALIDATOR_NODE_STATUS_TENDERMINT,
|
||||
__typename: 'RankingScore',
|
||||
},
|
||||
__typename: 'Node',
|
||||
},
|
||||
];
|
||||
|
||||
const renderComponent = () =>
|
||||
render(
|
||||
<ProtocolUpgradeProposal proposal={mockProposal} consensusValidators={[]} />
|
||||
);
|
||||
|
||||
describe('ProtocolUpgradeProposal', () => {
|
||||
it('should render successfully', () => {
|
||||
const { baseElement } = renderComponent();
|
||||
expect(baseElement).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
describe('getConsensusApprovals', () => {
|
||||
it('returns an empty array when there are no matching approvers', () => {
|
||||
const result = getConsensusApprovals(mockConsensusValidators, {
|
||||
...mockProposal,
|
||||
approvers: [],
|
||||
});
|
||||
expect(result).toEqual([]);
|
||||
});
|
||||
|
||||
it('returns the correct consensus approvals when there are matching approvers', () => {
|
||||
const expectedApprovals = mockConsensusValidators[1];
|
||||
|
||||
const result = getConsensusApprovals(mockConsensusValidators, {
|
||||
...mockProposal,
|
||||
approvers: [
|
||||
'6abc23391a9f888ab240415bf63d6844b03fc360be822f4a1d2cd832d87b291b',
|
||||
],
|
||||
});
|
||||
|
||||
expect(result.length).toEqual(1);
|
||||
expect(result[0].id).toEqual(expectedApprovals.id);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getConsensusApprovalsVotingPower', () => {
|
||||
it('returns 0 when the input array is empty', () => {
|
||||
const result = getConsensusApprovalsVotingPower([]);
|
||||
expect(result).toBe(0);
|
||||
});
|
||||
|
||||
it('returns the correct sum of voting power for a given array of consensus validators', () => {
|
||||
const expectedSum = mockConsensusValidators.reduce(
|
||||
(acc, curr) => acc + Number(curr.rankingScore.votingPower),
|
||||
0
|
||||
);
|
||||
|
||||
const result = getConsensusApprovalsVotingPower(mockConsensusValidators);
|
||||
expect(result).toBe(expectedSum);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getConsensusApprovalsVotingPowerPercentage', () => {
|
||||
it('returns "0%" when the input value is 0 or less', () => {
|
||||
const resultZero = getConsensusApprovalsVotingPowerPercentage(0);
|
||||
const resultNegative = getConsensusApprovalsVotingPowerPercentage(-10);
|
||||
expect(resultZero).toBe('0%');
|
||||
expect(resultNegative).toBe('0%');
|
||||
});
|
||||
|
||||
it('returns the correct percentage string when the input value is greater than 0', () => {
|
||||
const inputValue = 12345;
|
||||
const expectedPercentage = getNormalisedVotingPower(
|
||||
inputValue.toString(),
|
||||
2
|
||||
);
|
||||
|
||||
const result = getConsensusApprovalsVotingPowerPercentage(inputValue);
|
||||
expect(result).toBe(expectedPercentage);
|
||||
});
|
||||
});
|
||||
@@ -1,83 +0,0 @@
|
||||
import { useMemo } from 'react';
|
||||
import { ProtocolUpgradeProposalDetailHeader } from '../components/protocol-upgrade-proposal-detail-header';
|
||||
import { ProtocolUpdateProposalDetailApprovals } from '../components/protocol-upgrade-proposal-detail-approvals';
|
||||
import { ProtocolUpgradeProposalDetailInfo } from '../components/protocol-upgrade-proposal-detail-info';
|
||||
import { getNormalisedVotingPower } from '../../staking/shared';
|
||||
import type { ProtocolUpgradeProposalFieldsFragment } from './__generated__/ProtocolUpgradeProposals';
|
||||
import type { NodesFragmentFragment } from '../../staking/home/__generated__/Nodes';
|
||||
|
||||
export interface ProtocolUpgradeProposalProps {
|
||||
proposal: ProtocolUpgradeProposalFieldsFragment;
|
||||
consensusValidators: NodesFragmentFragment[] | null;
|
||||
lastBlockHeight?: string;
|
||||
}
|
||||
|
||||
export const getConsensusApprovals = (
|
||||
consensusValidators: NodesFragmentFragment[],
|
||||
proposal: ProtocolUpgradeProposalFieldsFragment
|
||||
) =>
|
||||
consensusValidators?.filter(({ pubkey }) =>
|
||||
proposal.approvers?.includes(pubkey)
|
||||
);
|
||||
|
||||
export const getConsensusApprovalsVotingPower = (
|
||||
consensusValidators: NodesFragmentFragment[]
|
||||
) =>
|
||||
consensusValidators?.reduce<number>(
|
||||
(acc: number, curr: NodesFragmentFragment) => {
|
||||
return acc + Number(curr.rankingScore.votingPower);
|
||||
},
|
||||
0
|
||||
);
|
||||
|
||||
export const getConsensusApprovalsVotingPowerPercentage = (
|
||||
consensusApprovalsVotingPower: number
|
||||
) =>
|
||||
consensusApprovalsVotingPower > 0
|
||||
? getNormalisedVotingPower(consensusApprovalsVotingPower.toString(), 2)
|
||||
: '0%';
|
||||
|
||||
export const ProtocolUpgradeProposal = ({
|
||||
proposal,
|
||||
lastBlockHeight,
|
||||
consensusValidators,
|
||||
}: ProtocolUpgradeProposalProps) => {
|
||||
const consensusApprovals = useMemo(
|
||||
() => getConsensusApprovals(consensusValidators || [], proposal),
|
||||
[consensusValidators, proposal]
|
||||
);
|
||||
|
||||
const consensusApprovalsVotingPower = useMemo(
|
||||
() => getConsensusApprovalsVotingPower(consensusApprovals || []),
|
||||
[consensusApprovals]
|
||||
);
|
||||
|
||||
const consensusApprovalsVotingPowerPercentage = useMemo(
|
||||
() =>
|
||||
getConsensusApprovalsVotingPowerPercentage(consensusApprovalsVotingPower),
|
||||
[consensusApprovalsVotingPower]
|
||||
);
|
||||
|
||||
return (
|
||||
<section data-testid="protocol-upgrade-proposal">
|
||||
<ProtocolUpgradeProposalDetailHeader
|
||||
releaseTag={proposal.vegaReleaseTag}
|
||||
/>
|
||||
|
||||
<ProtocolUpgradeProposalDetailInfo
|
||||
proposal={proposal}
|
||||
lastBlockHeight={lastBlockHeight}
|
||||
/>
|
||||
|
||||
{consensusValidators && consensusApprovals && (
|
||||
<ProtocolUpdateProposalDetailApprovals
|
||||
consensusApprovals={consensusApprovals}
|
||||
consensusApprovalsVotingPowerPercentage={
|
||||
consensusApprovalsVotingPowerPercentage
|
||||
}
|
||||
totalConsensusValidators={consensusValidators.length}
|
||||
/>
|
||||
)}
|
||||
</section>
|
||||
);
|
||||
};
|
||||
+1
-1
@@ -3,7 +3,7 @@ import { AppStateProvider } from '../../../contexts/app-state/app-state-provider
|
||||
import { EpochIndividualRewardsTable } from './epoch-individual-rewards-table';
|
||||
|
||||
const mockData = {
|
||||
epoch: 4441,
|
||||
epoch: '4441',
|
||||
rewards: [
|
||||
{
|
||||
asset: 'tDAI',
|
||||
|
||||
+21
-68
@@ -1,43 +1,32 @@
|
||||
import { useMemo, useEffect, useState, useCallback } from 'react';
|
||||
import { useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { AsyncRenderer, Pagination } from '@vegaprotocol/ui-toolkit';
|
||||
import { AsyncRenderer } from '@vegaprotocol/ui-toolkit';
|
||||
import { removePaginationWrapper } from '@vegaprotocol/utils';
|
||||
import type { EpochFieldsFragment } from '../home/__generated__/Rewards';
|
||||
import { useRewardsQuery } from '../home/__generated__/Rewards';
|
||||
import { ENV } from '../../../config';
|
||||
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||
import { EpochIndividualRewardsTable } from './epoch-individual-rewards-table';
|
||||
import { generateEpochIndividualRewardsList } from './generate-epoch-individual-rewards-list';
|
||||
import { calculateEpochOffset } from '../../../lib/epoch-pagination';
|
||||
|
||||
const EPOCHS_PAGE_SIZE = 10;
|
||||
|
||||
type EpochTotalRewardsProps = {
|
||||
currentEpoch: EpochFieldsFragment;
|
||||
};
|
||||
|
||||
export const EpochIndividualRewards = ({
|
||||
currentEpoch,
|
||||
}: EpochTotalRewardsProps) => {
|
||||
// we start from the previous epoch when displaying rewards data, because the current one has no calculated data while ongoing
|
||||
const epochId = Number(currentEpoch.id) - 1;
|
||||
const totalPages = Math.ceil(epochId / EPOCHS_PAGE_SIZE);
|
||||
const [page, setPage] = useState(1);
|
||||
export const EpochIndividualRewards = () => {
|
||||
const { t } = useTranslation();
|
||||
const { pubKey } = useVegaWallet();
|
||||
const { delegationsPagination } = ENV;
|
||||
|
||||
const { data, loading, error, refetch } = useRewardsQuery({
|
||||
notifyOnNetworkStatusChange: true,
|
||||
const { data, loading, error } = useRewardsQuery({
|
||||
variables: {
|
||||
partyId: pubKey || '',
|
||||
fromEpoch: epochId - EPOCHS_PAGE_SIZE,
|
||||
toEpoch: epochId,
|
||||
delegationsPagination: delegationsPagination
|
||||
? {
|
||||
first: Number(delegationsPagination),
|
||||
}
|
||||
: undefined,
|
||||
// we can use the same value for rewardsPagination as delegationsPagination
|
||||
rewardsPagination: delegationsPagination
|
||||
? {
|
||||
first: Number(delegationsPagination),
|
||||
}
|
||||
: undefined,
|
||||
},
|
||||
skip: !pubKey,
|
||||
});
|
||||
@@ -50,37 +39,8 @@ export const EpochIndividualRewards = ({
|
||||
|
||||
const epochIndividualRewardSummaries = useMemo(() => {
|
||||
if (!data?.party) return [];
|
||||
return generateEpochIndividualRewardsList({
|
||||
rewards,
|
||||
epochId,
|
||||
page,
|
||||
size: EPOCHS_PAGE_SIZE,
|
||||
});
|
||||
}, [data?.party, epochId, page, rewards]);
|
||||
|
||||
const refetchData = useCallback(
|
||||
async (toPage?: number) => {
|
||||
const targetPage = toPage ?? page;
|
||||
await refetch({
|
||||
partyId: pubKey || '',
|
||||
...calculateEpochOffset({ epochId, page, size: EPOCHS_PAGE_SIZE }),
|
||||
delegationsPagination: delegationsPagination
|
||||
? {
|
||||
first: Number(delegationsPagination),
|
||||
}
|
||||
: undefined,
|
||||
});
|
||||
setPage(targetPage);
|
||||
},
|
||||
[epochId, page, refetch, delegationsPagination, pubKey]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
// when the epoch changes, we want to refetch the data to update the current page
|
||||
if (data) {
|
||||
refetchData();
|
||||
}
|
||||
}, [epochId, data, refetchData]);
|
||||
return generateEpochIndividualRewardsList(rewards);
|
||||
}, [data?.party, rewards]);
|
||||
|
||||
return (
|
||||
<AsyncRenderer
|
||||
@@ -93,24 +53,17 @@ export const EpochIndividualRewards = ({
|
||||
{t('Connected Vega key')}:{' '}
|
||||
<span className="text-white">{pubKey}</span>
|
||||
</p>
|
||||
{epochIndividualRewardSummaries.map(
|
||||
(epochIndividualRewardSummary) => (
|
||||
<EpochIndividualRewardsTable
|
||||
data={epochIndividualRewardSummary}
|
||||
/>
|
||||
{epochIndividualRewardSummaries.length ? (
|
||||
epochIndividualRewardSummaries.map(
|
||||
(epochIndividualRewardSummary) => (
|
||||
<EpochIndividualRewardsTable
|
||||
data={epochIndividualRewardSummary}
|
||||
/>
|
||||
)
|
||||
)
|
||||
) : (
|
||||
<p>{t('noRewards')}</p>
|
||||
)}
|
||||
<Pagination
|
||||
isLoading={loading}
|
||||
hasPrevPage={page > 1}
|
||||
hasNextPage={page < totalPages}
|
||||
onBack={() => refetchData(page - 1)}
|
||||
onNext={() => refetchData(page + 1)}
|
||||
onFirst={() => refetchData(1)}
|
||||
onLast={() => refetchData(totalPages)}
|
||||
>
|
||||
{t('Page')} {page}
|
||||
</Pagination>
|
||||
</div>
|
||||
)}
|
||||
/>
|
||||
|
||||
+15
-204
@@ -43,16 +43,6 @@ describe('generateEpochIndividualRewardsList', () => {
|
||||
epoch: { id: '1' },
|
||||
};
|
||||
|
||||
const reward5: RewardFieldsFragment = {
|
||||
rewardType: AccountType.ACCOUNT_TYPE_FEES_LIQUIDITY,
|
||||
amount: '150',
|
||||
percentageOfTotal: '0.15',
|
||||
receivedAt: new Date(),
|
||||
asset: { id: 'usd', symbol: 'USD', name: 'USD' },
|
||||
party: { id: 'blah' },
|
||||
epoch: { id: '3' },
|
||||
};
|
||||
|
||||
const rewardWrongType: RewardFieldsFragment = {
|
||||
rewardType: AccountType.ACCOUNT_TYPE_INSURANCE,
|
||||
amount: '50',
|
||||
@@ -64,38 +54,20 @@ describe('generateEpochIndividualRewardsList', () => {
|
||||
};
|
||||
|
||||
it('should return an empty array if no rewards are provided', () => {
|
||||
expect(
|
||||
generateEpochIndividualRewardsList({ rewards: [], epochId: 1 })
|
||||
).toEqual([
|
||||
{
|
||||
epoch: 1,
|
||||
rewards: [],
|
||||
},
|
||||
]);
|
||||
expect(generateEpochIndividualRewardsList([])).toEqual([]);
|
||||
});
|
||||
|
||||
it('should filter out any rewards of the wrong type', () => {
|
||||
const result = generateEpochIndividualRewardsList({
|
||||
rewards: [rewardWrongType],
|
||||
epochId: 1,
|
||||
});
|
||||
const result = generateEpochIndividualRewardsList([rewardWrongType]);
|
||||
|
||||
expect(result).toEqual([
|
||||
{
|
||||
epoch: 1,
|
||||
rewards: [],
|
||||
},
|
||||
]);
|
||||
expect(result).toEqual([]);
|
||||
});
|
||||
|
||||
it('should return reward in the correct format', () => {
|
||||
const result = generateEpochIndividualRewardsList({
|
||||
rewards: [reward1],
|
||||
epochId: 1,
|
||||
});
|
||||
const result = generateEpochIndividualRewardsList([reward1]);
|
||||
|
||||
expect(result[0]).toEqual({
|
||||
epoch: 1,
|
||||
epoch: '1',
|
||||
rewards: [
|
||||
{
|
||||
asset: 'USD',
|
||||
@@ -133,24 +105,21 @@ describe('generateEpochIndividualRewardsList', () => {
|
||||
|
||||
it('should return an array sorted by epoch descending', () => {
|
||||
const rewards = [reward1, reward2, reward3, reward4];
|
||||
const result1 = generateEpochIndividualRewardsList({ rewards, epochId: 2 });
|
||||
const result1 = generateEpochIndividualRewardsList(rewards);
|
||||
|
||||
expect(result1[0].epoch).toEqual(2);
|
||||
expect(result1[1].epoch).toEqual(1);
|
||||
expect(result1[0].epoch).toEqual('2');
|
||||
expect(result1[1].epoch).toEqual('1');
|
||||
|
||||
const reorderedRewards = [reward4, reward3, reward2, reward1];
|
||||
const result2 = generateEpochIndividualRewardsList({
|
||||
rewards: reorderedRewards,
|
||||
epochId: 2,
|
||||
});
|
||||
const result2 = generateEpochIndividualRewardsList(reorderedRewards);
|
||||
|
||||
expect(result2[0].epoch).toEqual(2);
|
||||
expect(result2[1].epoch).toEqual(1);
|
||||
expect(result2[0].epoch).toEqual('2');
|
||||
expect(result2[1].epoch).toEqual('1');
|
||||
});
|
||||
|
||||
it('correctly calculates the total value of rewards for an asset', () => {
|
||||
const rewards = [reward1, reward4];
|
||||
const result = generateEpochIndividualRewardsList({ rewards, epochId: 1 });
|
||||
const result = generateEpochIndividualRewardsList(rewards);
|
||||
|
||||
expect(result[0].rewards[0].totalAmount).toEqual('200');
|
||||
});
|
||||
@@ -158,11 +127,11 @@ 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 });
|
||||
const result = generateEpochIndividualRewardsList(rewards);
|
||||
|
||||
expect(result).toEqual([
|
||||
{
|
||||
epoch: 2,
|
||||
epoch: '2',
|
||||
rewards: [
|
||||
{
|
||||
asset: 'GBP',
|
||||
@@ -227,165 +196,7 @@ describe('generateEpochIndividualRewardsList', () => {
|
||||
],
|
||||
},
|
||||
{
|
||||
epoch: 1,
|
||||
rewards: [
|
||||
{
|
||||
asset: 'USD',
|
||||
totalAmount: '200',
|
||||
rewardTypes: {
|
||||
[AccountType.ACCOUNT_TYPE_FEES_INFRASTRUCTURE]: {
|
||||
amount: '0',
|
||||
percentageOfTotal: '0',
|
||||
},
|
||||
[AccountType.ACCOUNT_TYPE_FEES_LIQUIDITY]: {
|
||||
amount: '100',
|
||||
percentageOfTotal: '0.1',
|
||||
},
|
||||
[AccountType.ACCOUNT_TYPE_GLOBAL_REWARD]: {
|
||||
amount: '100',
|
||||
percentageOfTotal: '0.1',
|
||||
},
|
||||
[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',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('returns data correctly for the requested epoch range', () => {
|
||||
const rewards = [reward1, reward2, reward3, reward4, reward5];
|
||||
const resultPageOne = generateEpochIndividualRewardsList({
|
||||
rewards,
|
||||
epochId: 3,
|
||||
page: 1,
|
||||
size: 2,
|
||||
});
|
||||
|
||||
expect(resultPageOne).toEqual([
|
||||
{
|
||||
epoch: 3,
|
||||
rewards: [
|
||||
{
|
||||
asset: 'USD',
|
||||
totalAmount: '150',
|
||||
rewardTypes: {
|
||||
[AccountType.ACCOUNT_TYPE_FEES_INFRASTRUCTURE]: {
|
||||
amount: '0',
|
||||
percentageOfTotal: '0',
|
||||
},
|
||||
[AccountType.ACCOUNT_TYPE_FEES_LIQUIDITY]: {
|
||||
amount: '150',
|
||||
percentageOfTotal: '0.15',
|
||||
},
|
||||
[AccountType.ACCOUNT_TYPE_GLOBAL_REWARD]: {
|
||||
amount: '0',
|
||||
percentageOfTotal: '0',
|
||||
},
|
||||
[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',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
epoch: 2,
|
||||
rewards: [
|
||||
{
|
||||
asset: 'GBP',
|
||||
totalAmount: '200',
|
||||
rewardTypes: {
|
||||
[AccountType.ACCOUNT_TYPE_FEES_INFRASTRUCTURE]: {
|
||||
amount: '0',
|
||||
percentageOfTotal: '0',
|
||||
},
|
||||
[AccountType.ACCOUNT_TYPE_FEES_LIQUIDITY]: {
|
||||
amount: '200',
|
||||
percentageOfTotal: '0.2',
|
||||
},
|
||||
[AccountType.ACCOUNT_TYPE_GLOBAL_REWARD]: {
|
||||
amount: '0',
|
||||
percentageOfTotal: '0',
|
||||
},
|
||||
[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',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
asset: 'EUR',
|
||||
totalAmount: '50',
|
||||
rewardTypes: {
|
||||
[AccountType.ACCOUNT_TYPE_FEES_INFRASTRUCTURE]: {
|
||||
amount: '0',
|
||||
percentageOfTotal: '0',
|
||||
},
|
||||
[AccountType.ACCOUNT_TYPE_FEES_LIQUIDITY]: {
|
||||
amount: '0',
|
||||
percentageOfTotal: '0',
|
||||
},
|
||||
[AccountType.ACCOUNT_TYPE_GLOBAL_REWARD]: {
|
||||
amount: '50',
|
||||
percentageOfTotal: '0.05',
|
||||
},
|
||||
[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',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
]);
|
||||
|
||||
const resultPageTwo = generateEpochIndividualRewardsList({
|
||||
rewards,
|
||||
epochId: 3,
|
||||
page: 2,
|
||||
size: 2,
|
||||
});
|
||||
|
||||
expect(resultPageTwo).toEqual([
|
||||
{
|
||||
epoch: 1,
|
||||
epoch: '1',
|
||||
rewards: [
|
||||
{
|
||||
asset: 'USD',
|
||||
|
||||
+11
-30
@@ -2,10 +2,9 @@ import { BigNumber } from '../../../lib/bignumber';
|
||||
import { RowAccountTypes } from '../shared-rewards-table-assets/shared-rewards-table-assets';
|
||||
import type { RewardFieldsFragment } from '../home/__generated__/Rewards';
|
||||
import type { AccountType } from '@vegaprotocol/types';
|
||||
import { calculateEpochOffset } from '../../../lib/epoch-pagination';
|
||||
|
||||
export interface EpochIndividualReward {
|
||||
epoch: number;
|
||||
epoch: string;
|
||||
rewards: {
|
||||
asset: string;
|
||||
totalAmount: string;
|
||||
@@ -28,29 +27,11 @@ const emptyRowAccountTypes = accountTypes.map((type) => [
|
||||
},
|
||||
]);
|
||||
|
||||
export const generateEpochIndividualRewardsList = ({
|
||||
rewards,
|
||||
epochId,
|
||||
page = 1,
|
||||
size = 10,
|
||||
}: {
|
||||
rewards: RewardFieldsFragment[];
|
||||
epochId: number;
|
||||
page?: number;
|
||||
size?: number;
|
||||
}) => {
|
||||
const map: Map<string, EpochIndividualReward> = new Map();
|
||||
const { fromEpoch, toEpoch } = calculateEpochOffset({ epochId, page, size });
|
||||
|
||||
for (let i = toEpoch; i >= fromEpoch; i--) {
|
||||
map.set(i.toString(), {
|
||||
epoch: i,
|
||||
rewards: [],
|
||||
});
|
||||
}
|
||||
|
||||
export const generateEpochIndividualRewardsList = (
|
||||
rewards: RewardFieldsFragment[]
|
||||
) => {
|
||||
// We take the rewards and aggregate them by epoch and asset.
|
||||
const epochIndividualRewards = rewards.reduce((acc, reward) => {
|
||||
const epochIndividualRewards = rewards.reduce((map, reward) => {
|
||||
const epochId = reward.epoch.id;
|
||||
const assetName = reward.asset.name;
|
||||
const rewardType = reward.rewardType;
|
||||
@@ -59,14 +40,14 @@ export const generateEpochIndividualRewardsList = ({
|
||||
|
||||
// if the rewardType is not of a type we display in the table, we skip it.
|
||||
if (!accountTypes.includes(rewardType)) {
|
||||
return acc;
|
||||
return map;
|
||||
}
|
||||
|
||||
if (!acc.has(epochId)) {
|
||||
return acc;
|
||||
if (!map.has(epochId)) {
|
||||
map.set(epochId, { epoch: epochId, rewards: [] });
|
||||
}
|
||||
|
||||
const epoch = acc.get(epochId);
|
||||
const epoch = map.get(epochId);
|
||||
|
||||
let asset = epoch?.rewards.find((r) => r.asset === assetName);
|
||||
|
||||
@@ -95,8 +76,8 @@ export const generateEpochIndividualRewardsList = ({
|
||||
});
|
||||
}
|
||||
|
||||
return acc;
|
||||
}, map);
|
||||
return map;
|
||||
}, new Map<string, EpochIndividualReward>());
|
||||
|
||||
return Array.from(epochIndividualRewards.values()).sort(
|
||||
(a, b) => Number(b.epoch) - Number(a.epoch)
|
||||
|
||||
+34
-54
@@ -1,64 +1,44 @@
|
||||
import { render } from '@testing-library/react';
|
||||
import { AppStateProvider } from '../../../contexts/app-state/app-state-provider';
|
||||
import { EpochTotalRewardsTable } from './epoch-total-rewards-table';
|
||||
import type {
|
||||
AggregatedEpochRewardSummary,
|
||||
RewardType,
|
||||
RewardItem,
|
||||
} from './generate-epoch-total-rewards-list';
|
||||
import { AccountType } from '@vegaprotocol/types';
|
||||
|
||||
const assetId =
|
||||
'b340c130096819428a62e5df407fd6abe66e444b89ad64f670beb98621c9c663';
|
||||
|
||||
const rewardsList = [
|
||||
{
|
||||
rewardType: AccountType.ACCOUNT_TYPE_GLOBAL_REWARD,
|
||||
amount: '0',
|
||||
},
|
||||
{
|
||||
rewardType: AccountType.ACCOUNT_TYPE_FEES_INFRASTRUCTURE,
|
||||
amount: '295',
|
||||
},
|
||||
{
|
||||
rewardType: AccountType.ACCOUNT_TYPE_REWARD_MAKER_PAID_FEES,
|
||||
amount: '0',
|
||||
},
|
||||
{
|
||||
rewardType: AccountType.ACCOUNT_TYPE_REWARD_MAKER_RECEIVED_FEES,
|
||||
amount: '0',
|
||||
},
|
||||
{
|
||||
rewardType: AccountType.ACCOUNT_TYPE_FEES_LIQUIDITY,
|
||||
amount: '0',
|
||||
},
|
||||
{
|
||||
rewardType: AccountType.ACCOUNT_TYPE_REWARD_MARKET_PROPOSERS,
|
||||
amount: '0',
|
||||
},
|
||||
];
|
||||
|
||||
const rewards: Map<RewardType, RewardItem> = new Map();
|
||||
|
||||
rewardsList.forEach((r) => {
|
||||
rewards.set(r.rewardType, r);
|
||||
});
|
||||
|
||||
const assetRewards: Map<
|
||||
AggregatedEpochRewardSummary['assetId'],
|
||||
AggregatedEpochRewardSummary
|
||||
> = new Map();
|
||||
|
||||
assetRewards.set(assetId, {
|
||||
assetId,
|
||||
name: 'tDAI TEST',
|
||||
rewards,
|
||||
totalAmount: '295',
|
||||
});
|
||||
|
||||
const mockData = {
|
||||
epoch: 4431,
|
||||
assetRewards,
|
||||
assetRewards: [
|
||||
{
|
||||
assetId:
|
||||
'b340c130096819428a62e5df407fd6abe66e444b89ad64f670beb98621c9c663',
|
||||
name: 'tDAI TEST',
|
||||
rewards: [
|
||||
{
|
||||
rewardType: AccountType.ACCOUNT_TYPE_GLOBAL_REWARD,
|
||||
amount: '0',
|
||||
},
|
||||
{
|
||||
rewardType: AccountType.ACCOUNT_TYPE_FEES_INFRASTRUCTURE,
|
||||
amount: '295',
|
||||
},
|
||||
{
|
||||
rewardType: AccountType.ACCOUNT_TYPE_REWARD_MAKER_PAID_FEES,
|
||||
amount: '0',
|
||||
},
|
||||
{
|
||||
rewardType: AccountType.ACCOUNT_TYPE_REWARD_MAKER_RECEIVED_FEES,
|
||||
amount: '0',
|
||||
},
|
||||
{
|
||||
rewardType: AccountType.ACCOUNT_TYPE_FEES_LIQUIDITY,
|
||||
amount: '0',
|
||||
},
|
||||
{
|
||||
rewardType: AccountType.ACCOUNT_TYPE_REWARD_MARKET_PROPOSERS,
|
||||
amount: '0',
|
||||
},
|
||||
],
|
||||
totalAmount: '295',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
describe('EpochTotalRewardsTable', () => {
|
||||
|
||||
+10
-12
@@ -48,19 +48,17 @@ export const EpochTotalRewardsTable = ({
|
||||
}: EpochTotalRewardsGridProps) => {
|
||||
return (
|
||||
<RewardsTable dataTestId="epoch-total-rewards-table" epoch={data.epoch}>
|
||||
{Array.from(data.assetRewards.values()).map(
|
||||
({ name, rewards, totalAmount }, i) => (
|
||||
<div className="contents" key={i}>
|
||||
<div data-testid="asset" className={`${rowGridItemStyles()} p-5`}>
|
||||
{name}
|
||||
</div>
|
||||
{Array.from(rewards.values()).map(({ rewardType, amount }, i) => (
|
||||
<RewardItem key={i} dataTestId={rewardType} value={amount} />
|
||||
))}
|
||||
<RewardItem dataTestId="total" value={totalAmount} last={true} />
|
||||
{data.assetRewards.map(({ name, rewards, totalAmount }, i) => (
|
||||
<div className="contents" key={i}>
|
||||
<div data-testid="asset" className={`${rowGridItemStyles()} p-5`}>
|
||||
{name}
|
||||
</div>
|
||||
)
|
||||
)}
|
||||
{rewards.map(({ rewardType, amount }, i) => (
|
||||
<RewardItem key={i} dataTestId={rewardType} value={amount} />
|
||||
))}
|
||||
<RewardItem dataTestId="total" value={totalAmount} last={true} />
|
||||
</div>
|
||||
))}
|
||||
</RewardsTable>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,62 +1,21 @@
|
||||
import { useState, useCallback, useEffect } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { AsyncRenderer, Pagination } from '@vegaprotocol/ui-toolkit';
|
||||
import type { EpochFieldsFragment } from '../home/__generated__/Rewards';
|
||||
import { AsyncRenderer } from '@vegaprotocol/ui-toolkit';
|
||||
import { useEpochAssetsRewardsQuery } from '../home/__generated__/Rewards';
|
||||
import { useRefreshAfterEpoch } from '../../../hooks/use-refresh-after-epoch';
|
||||
import { generateEpochTotalRewardsList } from './generate-epoch-total-rewards-list';
|
||||
import { NoRewards } from '../no-rewards';
|
||||
import { EpochTotalRewardsTable } from './epoch-total-rewards-table';
|
||||
import { calculateEpochOffset } from '../../../lib/epoch-pagination';
|
||||
|
||||
const EPOCHS_PAGE_SIZE = 10;
|
||||
|
||||
type EpochTotalRewardsProps = {
|
||||
currentEpoch: EpochFieldsFragment;
|
||||
};
|
||||
|
||||
export const EpochTotalRewards = ({ currentEpoch }: EpochTotalRewardsProps) => {
|
||||
// we start from the previous epoch when displaying rewards data, because the current one has no calculated data while ongoing
|
||||
const epochId = Number(currentEpoch.id) - 1;
|
||||
const totalPages = Math.ceil(epochId / EPOCHS_PAGE_SIZE);
|
||||
const { t } = useTranslation();
|
||||
const [page, setPage] = useState(1);
|
||||
export const EpochTotalRewards = () => {
|
||||
const { data, loading, error, refetch } = useEpochAssetsRewardsQuery({
|
||||
notifyOnNetworkStatusChange: true,
|
||||
variables: {
|
||||
epochRewardSummariesFilter: {
|
||||
fromEpoch: epochId - EPOCHS_PAGE_SIZE,
|
||||
epochRewardSummariesPagination: {
|
||||
first: 10,
|
||||
},
|
||||
},
|
||||
});
|
||||
useRefreshAfterEpoch(data?.epoch.timestamps.expiry, refetch);
|
||||
|
||||
const refetchData = useCallback(
|
||||
async (toPage?: number) => {
|
||||
const targetPage = toPage ?? page;
|
||||
await refetch({
|
||||
epochRewardSummariesFilter: calculateEpochOffset({
|
||||
epochId,
|
||||
page: targetPage,
|
||||
size: EPOCHS_PAGE_SIZE,
|
||||
}),
|
||||
});
|
||||
setPage(targetPage);
|
||||
},
|
||||
[epochId, page, refetch]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
// when the epoch changes, we want to refetch the data to update the current page
|
||||
if (data) {
|
||||
refetchData();
|
||||
}
|
||||
}, [epochId, data, refetchData]);
|
||||
|
||||
const epochTotalRewardSummaries =
|
||||
generateEpochTotalRewardsList({
|
||||
data,
|
||||
epochId,
|
||||
page,
|
||||
size: EPOCHS_PAGE_SIZE,
|
||||
}) || [];
|
||||
const epochTotalRewardSummaries = generateEpochTotalRewardsList(data) || [];
|
||||
|
||||
return (
|
||||
<AsyncRenderer
|
||||
@@ -68,22 +27,15 @@ export const EpochTotalRewards = ({ currentEpoch }: EpochTotalRewardsProps) => {
|
||||
className="max-w-full overflow-auto"
|
||||
data-testid="epoch-rewards-total"
|
||||
>
|
||||
{Array.from(epochTotalRewardSummaries.values()).map(
|
||||
(epochTotalSummary, index) => (
|
||||
<EpochTotalRewardsTable data={epochTotalSummary} key={index} />
|
||||
)
|
||||
{epochTotalRewardSummaries.length === 0 ? (
|
||||
<NoRewards />
|
||||
) : (
|
||||
<>
|
||||
{epochTotalRewardSummaries.map((epochTotalSummary, index) => (
|
||||
<EpochTotalRewardsTable data={epochTotalSummary} key={index} />
|
||||
))}
|
||||
</>
|
||||
)}
|
||||
<Pagination
|
||||
isLoading={loading}
|
||||
hasPrevPage={page > 1}
|
||||
hasNextPage={page < totalPages}
|
||||
onBack={() => refetchData(page - 1)}
|
||||
onNext={() => refetchData(page + 1)}
|
||||
onFirst={() => refetchData(1)}
|
||||
onLast={() => refetchData(totalPages)}
|
||||
>
|
||||
{t('Page')} {page}
|
||||
</Pagination>
|
||||
</div>
|
||||
)}
|
||||
/>
|
||||
|
||||
+117
-521
@@ -3,23 +3,13 @@ import { AccountType } from '@vegaprotocol/types';
|
||||
|
||||
describe('generateEpochAssetRewardsList', () => {
|
||||
it('should return an empty array if data is undefined', () => {
|
||||
const result = generateEpochTotalRewardsList({ epochId: 1 });
|
||||
const result = generateEpochTotalRewardsList(undefined);
|
||||
|
||||
expect(result).toEqual(
|
||||
new Map([
|
||||
[
|
||||
'1',
|
||||
{
|
||||
epoch: 1,
|
||||
assetRewards: new Map(),
|
||||
},
|
||||
],
|
||||
])
|
||||
);
|
||||
expect(result).toEqual([]);
|
||||
});
|
||||
|
||||
it('should return an empty map if empty data is provided', () => {
|
||||
const data = {
|
||||
it('should return an empty array if empty data is provided', () => {
|
||||
const epochData = {
|
||||
assetsConnection: {
|
||||
edges: [],
|
||||
},
|
||||
@@ -33,23 +23,13 @@ describe('generateEpochAssetRewardsList', () => {
|
||||
},
|
||||
};
|
||||
|
||||
const result = generateEpochTotalRewardsList({ data, epochId: 1 });
|
||||
const result = generateEpochTotalRewardsList(epochData);
|
||||
|
||||
expect(result).toEqual(
|
||||
new Map([
|
||||
[
|
||||
'1',
|
||||
{
|
||||
epoch: 1,
|
||||
assetRewards: new Map(),
|
||||
},
|
||||
],
|
||||
])
|
||||
);
|
||||
expect(result).toEqual([]);
|
||||
});
|
||||
|
||||
it('should return an empty map if no epochRewardSummaries are provided', () => {
|
||||
const data = {
|
||||
it('should return an empty array if no epochRewardSummaries are provided', () => {
|
||||
const epochData = {
|
||||
assetsConnection: {
|
||||
edges: [
|
||||
{
|
||||
@@ -76,23 +56,13 @@ describe('generateEpochAssetRewardsList', () => {
|
||||
},
|
||||
};
|
||||
|
||||
const result = generateEpochTotalRewardsList({ data, epochId: 1 });
|
||||
const result = generateEpochTotalRewardsList(epochData);
|
||||
|
||||
expect(result).toEqual(
|
||||
new Map([
|
||||
[
|
||||
'1',
|
||||
{
|
||||
epoch: 1,
|
||||
assetRewards: new Map(),
|
||||
},
|
||||
],
|
||||
])
|
||||
);
|
||||
expect(result).toEqual([]);
|
||||
});
|
||||
|
||||
it('should return a map of unnamed assets if no asset names are provided (should not happen)', () => {
|
||||
const data = {
|
||||
it('should return an array of unnamed assets if no asset names are provided (should not happen)', () => {
|
||||
const epochData = {
|
||||
assetsConnection: {
|
||||
edges: [],
|
||||
},
|
||||
@@ -115,80 +85,50 @@ describe('generateEpochAssetRewardsList', () => {
|
||||
},
|
||||
};
|
||||
|
||||
const result = generateEpochTotalRewardsList({ data, epochId: 1 });
|
||||
const result = generateEpochTotalRewardsList(epochData);
|
||||
|
||||
expect(result).toEqual(
|
||||
new Map([
|
||||
[
|
||||
'1',
|
||||
expect(result).toEqual([
|
||||
{
|
||||
epoch: 1,
|
||||
assetRewards: [
|
||||
{
|
||||
epoch: 1,
|
||||
assetRewards: new Map([
|
||||
[
|
||||
'1',
|
||||
{
|
||||
assetId: '1',
|
||||
name: '',
|
||||
rewards: new Map([
|
||||
[
|
||||
AccountType.ACCOUNT_TYPE_GLOBAL_REWARD,
|
||||
{
|
||||
rewardType: AccountType.ACCOUNT_TYPE_GLOBAL_REWARD,
|
||||
amount: '123',
|
||||
},
|
||||
],
|
||||
[
|
||||
AccountType.ACCOUNT_TYPE_FEES_INFRASTRUCTURE,
|
||||
{
|
||||
rewardType:
|
||||
AccountType.ACCOUNT_TYPE_FEES_INFRASTRUCTURE,
|
||||
amount: '0',
|
||||
},
|
||||
],
|
||||
[
|
||||
AccountType.ACCOUNT_TYPE_REWARD_MAKER_PAID_FEES,
|
||||
{
|
||||
rewardType:
|
||||
AccountType.ACCOUNT_TYPE_REWARD_MAKER_PAID_FEES,
|
||||
amount: '0',
|
||||
},
|
||||
],
|
||||
[
|
||||
AccountType.ACCOUNT_TYPE_REWARD_MAKER_RECEIVED_FEES,
|
||||
{
|
||||
rewardType:
|
||||
AccountType.ACCOUNT_TYPE_REWARD_MAKER_RECEIVED_FEES,
|
||||
amount: '0',
|
||||
},
|
||||
],
|
||||
[
|
||||
AccountType.ACCOUNT_TYPE_FEES_LIQUIDITY,
|
||||
{
|
||||
rewardType: AccountType.ACCOUNT_TYPE_FEES_LIQUIDITY,
|
||||
amount: '0',
|
||||
},
|
||||
],
|
||||
[
|
||||
AccountType.ACCOUNT_TYPE_REWARD_MARKET_PROPOSERS,
|
||||
{
|
||||
rewardType:
|
||||
AccountType.ACCOUNT_TYPE_REWARD_MARKET_PROPOSERS,
|
||||
amount: '0',
|
||||
},
|
||||
],
|
||||
]),
|
||||
totalAmount: '123',
|
||||
},
|
||||
],
|
||||
]),
|
||||
assetId: '1',
|
||||
name: '',
|
||||
rewards: [
|
||||
{
|
||||
rewardType: AccountType.ACCOUNT_TYPE_GLOBAL_REWARD,
|
||||
amount: '123',
|
||||
},
|
||||
{
|
||||
rewardType: AccountType.ACCOUNT_TYPE_FEES_INFRASTRUCTURE,
|
||||
amount: '0',
|
||||
},
|
||||
{
|
||||
rewardType: AccountType.ACCOUNT_TYPE_REWARD_MAKER_PAID_FEES,
|
||||
amount: '0',
|
||||
},
|
||||
{
|
||||
rewardType: AccountType.ACCOUNT_TYPE_REWARD_MAKER_RECEIVED_FEES,
|
||||
amount: '0',
|
||||
},
|
||||
{
|
||||
rewardType: AccountType.ACCOUNT_TYPE_FEES_LIQUIDITY,
|
||||
amount: '0',
|
||||
},
|
||||
{
|
||||
rewardType: AccountType.ACCOUNT_TYPE_REWARD_MARKET_PROPOSERS,
|
||||
amount: '0',
|
||||
},
|
||||
],
|
||||
totalAmount: '123',
|
||||
},
|
||||
],
|
||||
])
|
||||
);
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should return the aggregated epoch summaries', () => {
|
||||
const data = {
|
||||
it('should return an array of aggregated epoch summaries', () => {
|
||||
const epochData = {
|
||||
assetsConnection: {
|
||||
edges: [
|
||||
{
|
||||
@@ -240,425 +180,81 @@ describe('generateEpochAssetRewardsList', () => {
|
||||
},
|
||||
};
|
||||
|
||||
const result = generateEpochTotalRewardsList({ data, epochId: 2 });
|
||||
const result = generateEpochTotalRewardsList(epochData);
|
||||
|
||||
expect(result).toEqual(
|
||||
new Map([
|
||||
[
|
||||
'1',
|
||||
expect(result).toEqual([
|
||||
{
|
||||
epoch: 1,
|
||||
assetRewards: [
|
||||
{
|
||||
epoch: 1,
|
||||
assetRewards: new Map([
|
||||
[
|
||||
'1',
|
||||
{
|
||||
assetId: '1',
|
||||
name: 'Asset 1',
|
||||
rewards: new Map([
|
||||
[
|
||||
AccountType.ACCOUNT_TYPE_GLOBAL_REWARD,
|
||||
{
|
||||
rewardType: AccountType.ACCOUNT_TYPE_GLOBAL_REWARD,
|
||||
amount: '0',
|
||||
},
|
||||
],
|
||||
[
|
||||
AccountType.ACCOUNT_TYPE_FEES_INFRASTRUCTURE,
|
||||
{
|
||||
rewardType:
|
||||
AccountType.ACCOUNT_TYPE_FEES_INFRASTRUCTURE,
|
||||
amount: '100',
|
||||
},
|
||||
],
|
||||
[
|
||||
AccountType.ACCOUNT_TYPE_REWARD_MAKER_PAID_FEES,
|
||||
{
|
||||
rewardType:
|
||||
AccountType.ACCOUNT_TYPE_REWARD_MAKER_PAID_FEES,
|
||||
amount: '123',
|
||||
},
|
||||
],
|
||||
[
|
||||
AccountType.ACCOUNT_TYPE_REWARD_MAKER_RECEIVED_FEES,
|
||||
{
|
||||
rewardType:
|
||||
AccountType.ACCOUNT_TYPE_REWARD_MAKER_RECEIVED_FEES,
|
||||
amount: '0',
|
||||
},
|
||||
],
|
||||
[
|
||||
AccountType.ACCOUNT_TYPE_FEES_LIQUIDITY,
|
||||
{
|
||||
rewardType: AccountType.ACCOUNT_TYPE_FEES_LIQUIDITY,
|
||||
amount: '0',
|
||||
},
|
||||
],
|
||||
[
|
||||
AccountType.ACCOUNT_TYPE_REWARD_MARKET_PROPOSERS,
|
||||
{
|
||||
rewardType:
|
||||
AccountType.ACCOUNT_TYPE_REWARD_MARKET_PROPOSERS,
|
||||
amount: '0',
|
||||
},
|
||||
],
|
||||
]),
|
||||
totalAmount: '223',
|
||||
},
|
||||
],
|
||||
]),
|
||||
},
|
||||
],
|
||||
[
|
||||
'2',
|
||||
{
|
||||
epoch: 2,
|
||||
assetRewards: new Map([
|
||||
[
|
||||
'1',
|
||||
{
|
||||
assetId: '1',
|
||||
name: 'Asset 1',
|
||||
rewards: new Map([
|
||||
[
|
||||
AccountType.ACCOUNT_TYPE_GLOBAL_REWARD,
|
||||
{
|
||||
rewardType: AccountType.ACCOUNT_TYPE_GLOBAL_REWARD,
|
||||
amount: '0',
|
||||
},
|
||||
],
|
||||
[
|
||||
AccountType.ACCOUNT_TYPE_FEES_INFRASTRUCTURE,
|
||||
{
|
||||
rewardType:
|
||||
AccountType.ACCOUNT_TYPE_FEES_INFRASTRUCTURE,
|
||||
amount: '0',
|
||||
},
|
||||
],
|
||||
[
|
||||
AccountType.ACCOUNT_TYPE_REWARD_MAKER_PAID_FEES,
|
||||
{
|
||||
rewardType:
|
||||
AccountType.ACCOUNT_TYPE_REWARD_MAKER_PAID_FEES,
|
||||
amount: '0',
|
||||
},
|
||||
],
|
||||
[
|
||||
AccountType.ACCOUNT_TYPE_REWARD_MAKER_RECEIVED_FEES,
|
||||
{
|
||||
rewardType:
|
||||
AccountType.ACCOUNT_TYPE_REWARD_MAKER_RECEIVED_FEES,
|
||||
amount: '0',
|
||||
},
|
||||
],
|
||||
[
|
||||
AccountType.ACCOUNT_TYPE_FEES_LIQUIDITY,
|
||||
{
|
||||
rewardType: AccountType.ACCOUNT_TYPE_FEES_LIQUIDITY,
|
||||
amount: '5',
|
||||
},
|
||||
],
|
||||
[
|
||||
AccountType.ACCOUNT_TYPE_REWARD_MARKET_PROPOSERS,
|
||||
{
|
||||
rewardType:
|
||||
AccountType.ACCOUNT_TYPE_REWARD_MARKET_PROPOSERS,
|
||||
amount: '0',
|
||||
},
|
||||
],
|
||||
]),
|
||||
totalAmount: '5',
|
||||
},
|
||||
],
|
||||
]),
|
||||
},
|
||||
],
|
||||
])
|
||||
);
|
||||
});
|
||||
|
||||
it('should return the requested range for aggregated epoch summaries', () => {
|
||||
const data = {
|
||||
assetsConnection: {
|
||||
edges: [
|
||||
{
|
||||
node: {
|
||||
id: '1',
|
||||
name: 'Asset 1',
|
||||
},
|
||||
},
|
||||
{
|
||||
node: {
|
||||
id: '2',
|
||||
name: 'Asset 2',
|
||||
},
|
||||
assetId: '1',
|
||||
name: 'Asset 1',
|
||||
rewards: [
|
||||
{
|
||||
rewardType: AccountType.ACCOUNT_TYPE_GLOBAL_REWARD,
|
||||
amount: '0',
|
||||
},
|
||||
{
|
||||
rewardType: AccountType.ACCOUNT_TYPE_FEES_INFRASTRUCTURE,
|
||||
amount: '100',
|
||||
},
|
||||
{
|
||||
rewardType: AccountType.ACCOUNT_TYPE_REWARD_MAKER_PAID_FEES,
|
||||
amount: '123',
|
||||
},
|
||||
{
|
||||
rewardType: AccountType.ACCOUNT_TYPE_REWARD_MAKER_RECEIVED_FEES,
|
||||
amount: '0',
|
||||
},
|
||||
{
|
||||
rewardType: AccountType.ACCOUNT_TYPE_FEES_LIQUIDITY,
|
||||
amount: '0',
|
||||
},
|
||||
{
|
||||
rewardType: AccountType.ACCOUNT_TYPE_REWARD_MARKET_PROPOSERS,
|
||||
amount: '0',
|
||||
},
|
||||
],
|
||||
totalAmount: '223',
|
||||
},
|
||||
],
|
||||
},
|
||||
epochRewardSummaries: {
|
||||
edges: [
|
||||
{
|
||||
epoch: 2,
|
||||
assetRewards: [
|
||||
{
|
||||
node: {
|
||||
epoch: 1,
|
||||
assetId: '1',
|
||||
rewardType: AccountType.ACCOUNT_TYPE_REWARD_MAKER_PAID_FEES,
|
||||
amount: '123',
|
||||
},
|
||||
},
|
||||
{
|
||||
node: {
|
||||
epoch: 1,
|
||||
assetId: '1',
|
||||
rewardType: AccountType.ACCOUNT_TYPE_FEES_INFRASTRUCTURE,
|
||||
amount: '100',
|
||||
},
|
||||
},
|
||||
{
|
||||
node: {
|
||||
epoch: 2,
|
||||
assetId: '1',
|
||||
rewardType: AccountType.ACCOUNT_TYPE_FEES_LIQUIDITY,
|
||||
amount: '6',
|
||||
},
|
||||
},
|
||||
{
|
||||
node: {
|
||||
epoch: 2,
|
||||
assetId: '1',
|
||||
rewardType: AccountType.ACCOUNT_TYPE_FEES_LIQUIDITY,
|
||||
amount: '27',
|
||||
},
|
||||
},
|
||||
{
|
||||
node: {
|
||||
epoch: 3,
|
||||
assetId: '1',
|
||||
rewardType: AccountType.ACCOUNT_TYPE_FEES_INFRASTRUCTURE,
|
||||
amount: '15',
|
||||
},
|
||||
assetId: '1',
|
||||
name: 'Asset 1',
|
||||
rewards: [
|
||||
{
|
||||
rewardType: AccountType.ACCOUNT_TYPE_GLOBAL_REWARD,
|
||||
amount: '0',
|
||||
},
|
||||
{
|
||||
rewardType: AccountType.ACCOUNT_TYPE_FEES_INFRASTRUCTURE,
|
||||
amount: '0',
|
||||
},
|
||||
{
|
||||
rewardType: AccountType.ACCOUNT_TYPE_REWARD_MAKER_PAID_FEES,
|
||||
amount: '0',
|
||||
},
|
||||
{
|
||||
rewardType: AccountType.ACCOUNT_TYPE_REWARD_MAKER_RECEIVED_FEES,
|
||||
amount: '0',
|
||||
},
|
||||
{
|
||||
rewardType: AccountType.ACCOUNT_TYPE_FEES_LIQUIDITY,
|
||||
amount: '5',
|
||||
},
|
||||
{
|
||||
rewardType: AccountType.ACCOUNT_TYPE_REWARD_MARKET_PROPOSERS,
|
||||
amount: '0',
|
||||
},
|
||||
],
|
||||
totalAmount: '5',
|
||||
},
|
||||
],
|
||||
},
|
||||
epoch: {
|
||||
timestamps: {
|
||||
expiry: null,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const resultPageOne = generateEpochTotalRewardsList({
|
||||
data,
|
||||
epochId: 3,
|
||||
page: 1,
|
||||
size: 2,
|
||||
});
|
||||
|
||||
expect(resultPageOne).toEqual(
|
||||
new Map([
|
||||
[
|
||||
'2',
|
||||
{
|
||||
epoch: 2,
|
||||
assetRewards: new Map([
|
||||
[
|
||||
'1',
|
||||
{
|
||||
assetId: '1',
|
||||
name: 'Asset 1',
|
||||
rewards: new Map([
|
||||
[
|
||||
AccountType.ACCOUNT_TYPE_GLOBAL_REWARD,
|
||||
{
|
||||
rewardType: AccountType.ACCOUNT_TYPE_GLOBAL_REWARD,
|
||||
amount: '0',
|
||||
},
|
||||
],
|
||||
[
|
||||
AccountType.ACCOUNT_TYPE_FEES_INFRASTRUCTURE,
|
||||
{
|
||||
rewardType:
|
||||
AccountType.ACCOUNT_TYPE_FEES_INFRASTRUCTURE,
|
||||
amount: '0',
|
||||
},
|
||||
],
|
||||
[
|
||||
AccountType.ACCOUNT_TYPE_REWARD_MAKER_PAID_FEES,
|
||||
{
|
||||
rewardType:
|
||||
AccountType.ACCOUNT_TYPE_REWARD_MAKER_PAID_FEES,
|
||||
amount: '0',
|
||||
},
|
||||
],
|
||||
[
|
||||
AccountType.ACCOUNT_TYPE_REWARD_MAKER_RECEIVED_FEES,
|
||||
{
|
||||
rewardType:
|
||||
AccountType.ACCOUNT_TYPE_REWARD_MAKER_RECEIVED_FEES,
|
||||
amount: '0',
|
||||
},
|
||||
],
|
||||
[
|
||||
AccountType.ACCOUNT_TYPE_FEES_LIQUIDITY,
|
||||
{
|
||||
rewardType: AccountType.ACCOUNT_TYPE_FEES_LIQUIDITY,
|
||||
amount: '33',
|
||||
},
|
||||
],
|
||||
[
|
||||
AccountType.ACCOUNT_TYPE_REWARD_MARKET_PROPOSERS,
|
||||
{
|
||||
rewardType:
|
||||
AccountType.ACCOUNT_TYPE_REWARD_MARKET_PROPOSERS,
|
||||
amount: '0',
|
||||
},
|
||||
],
|
||||
]),
|
||||
totalAmount: '33',
|
||||
},
|
||||
],
|
||||
]),
|
||||
},
|
||||
],
|
||||
[
|
||||
'3',
|
||||
{
|
||||
epoch: 3,
|
||||
assetRewards: new Map([
|
||||
[
|
||||
'1',
|
||||
{
|
||||
assetId: '1',
|
||||
name: 'Asset 1',
|
||||
rewards: new Map([
|
||||
[
|
||||
AccountType.ACCOUNT_TYPE_GLOBAL_REWARD,
|
||||
{
|
||||
rewardType: AccountType.ACCOUNT_TYPE_GLOBAL_REWARD,
|
||||
amount: '0',
|
||||
},
|
||||
],
|
||||
[
|
||||
AccountType.ACCOUNT_TYPE_FEES_INFRASTRUCTURE,
|
||||
{
|
||||
rewardType:
|
||||
AccountType.ACCOUNT_TYPE_FEES_INFRASTRUCTURE,
|
||||
amount: '15',
|
||||
},
|
||||
],
|
||||
[
|
||||
AccountType.ACCOUNT_TYPE_REWARD_MAKER_PAID_FEES,
|
||||
{
|
||||
rewardType:
|
||||
AccountType.ACCOUNT_TYPE_REWARD_MAKER_PAID_FEES,
|
||||
amount: '0',
|
||||
},
|
||||
],
|
||||
[
|
||||
AccountType.ACCOUNT_TYPE_REWARD_MAKER_RECEIVED_FEES,
|
||||
{
|
||||
rewardType:
|
||||
AccountType.ACCOUNT_TYPE_REWARD_MAKER_RECEIVED_FEES,
|
||||
amount: '0',
|
||||
},
|
||||
],
|
||||
[
|
||||
AccountType.ACCOUNT_TYPE_FEES_LIQUIDITY,
|
||||
{
|
||||
rewardType: AccountType.ACCOUNT_TYPE_FEES_LIQUIDITY,
|
||||
amount: '0',
|
||||
},
|
||||
],
|
||||
[
|
||||
AccountType.ACCOUNT_TYPE_REWARD_MARKET_PROPOSERS,
|
||||
{
|
||||
rewardType:
|
||||
AccountType.ACCOUNT_TYPE_REWARD_MARKET_PROPOSERS,
|
||||
amount: '0',
|
||||
},
|
||||
],
|
||||
]),
|
||||
totalAmount: '15',
|
||||
},
|
||||
],
|
||||
]),
|
||||
},
|
||||
],
|
||||
])
|
||||
);
|
||||
|
||||
const resultPageTwo = generateEpochTotalRewardsList({
|
||||
data,
|
||||
epochId: 3,
|
||||
page: 2,
|
||||
size: 2,
|
||||
});
|
||||
|
||||
expect(resultPageTwo).toEqual(
|
||||
new Map([
|
||||
[
|
||||
'1',
|
||||
{
|
||||
epoch: 1,
|
||||
assetRewards: new Map([
|
||||
[
|
||||
'1',
|
||||
{
|
||||
assetId: '1',
|
||||
name: 'Asset 1',
|
||||
rewards: new Map([
|
||||
[
|
||||
AccountType.ACCOUNT_TYPE_GLOBAL_REWARD,
|
||||
{
|
||||
rewardType: AccountType.ACCOUNT_TYPE_GLOBAL_REWARD,
|
||||
amount: '0',
|
||||
},
|
||||
],
|
||||
[
|
||||
AccountType.ACCOUNT_TYPE_FEES_INFRASTRUCTURE,
|
||||
{
|
||||
rewardType:
|
||||
AccountType.ACCOUNT_TYPE_FEES_INFRASTRUCTURE,
|
||||
amount: '100',
|
||||
},
|
||||
],
|
||||
[
|
||||
AccountType.ACCOUNT_TYPE_REWARD_MAKER_PAID_FEES,
|
||||
{
|
||||
rewardType:
|
||||
AccountType.ACCOUNT_TYPE_REWARD_MAKER_PAID_FEES,
|
||||
amount: '123',
|
||||
},
|
||||
],
|
||||
[
|
||||
AccountType.ACCOUNT_TYPE_REWARD_MAKER_RECEIVED_FEES,
|
||||
{
|
||||
rewardType:
|
||||
AccountType.ACCOUNT_TYPE_REWARD_MAKER_RECEIVED_FEES,
|
||||
amount: '0',
|
||||
},
|
||||
],
|
||||
[
|
||||
AccountType.ACCOUNT_TYPE_FEES_LIQUIDITY,
|
||||
{
|
||||
rewardType: AccountType.ACCOUNT_TYPE_FEES_LIQUIDITY,
|
||||
amount: '0',
|
||||
},
|
||||
],
|
||||
[
|
||||
AccountType.ACCOUNT_TYPE_REWARD_MARKET_PROPOSERS,
|
||||
{
|
||||
rewardType:
|
||||
AccountType.ACCOUNT_TYPE_REWARD_MARKET_PROPOSERS,
|
||||
amount: '0',
|
||||
},
|
||||
],
|
||||
]),
|
||||
totalAmount: '223',
|
||||
},
|
||||
],
|
||||
]),
|
||||
},
|
||||
],
|
||||
])
|
||||
);
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
+104
-74
@@ -5,97 +5,127 @@ import type {
|
||||
import { removePaginationWrapper } from '@vegaprotocol/utils';
|
||||
import { RowAccountTypes } from '../shared-rewards-table-assets/shared-rewards-table-assets';
|
||||
import type { AccountType } from '@vegaprotocol/types';
|
||||
import { calculateEpochOffset } from '../../../lib/epoch-pagination';
|
||||
import { BigNumber } from '../../../lib/bignumber';
|
||||
|
||||
interface EpochSummaryWithNamedReward extends EpochRewardSummaryFieldsFragment {
|
||||
name: string;
|
||||
}
|
||||
|
||||
export type RewardType = EpochRewardSummaryFieldsFragment['rewardType'];
|
||||
export type RewardItem = Pick<
|
||||
EpochRewardSummaryFieldsFragment,
|
||||
'rewardType' | 'amount'
|
||||
>;
|
||||
|
||||
export type AggregatedEpochRewardSummary = {
|
||||
export interface AggregatedEpochRewardSummary {
|
||||
assetId: EpochRewardSummaryFieldsFragment['assetId'];
|
||||
name: EpochSummaryWithNamedReward['name'];
|
||||
rewards: Map<RewardType, RewardItem>;
|
||||
rewards: {
|
||||
rewardType: EpochRewardSummaryFieldsFragment['rewardType'];
|
||||
amount: EpochRewardSummaryFieldsFragment['amount'];
|
||||
}[];
|
||||
totalAmount: string;
|
||||
};
|
||||
}
|
||||
|
||||
export type EpochTotalSummary = {
|
||||
export interface EpochTotalSummary {
|
||||
epoch: EpochRewardSummaryFieldsFragment['epoch'];
|
||||
assetRewards: Map<
|
||||
EpochRewardSummaryFieldsFragment['assetId'],
|
||||
AggregatedEpochRewardSummary
|
||||
>;
|
||||
};
|
||||
assetRewards: AggregatedEpochRewardSummary[];
|
||||
}
|
||||
|
||||
const emptyRowAccountTypes: Map<RewardType, RewardItem> = new Map();
|
||||
const emptyRowAccountTypes = Object.keys(RowAccountTypes).map((type) => ({
|
||||
rewardType: type as AccountType,
|
||||
amount: '0',
|
||||
}));
|
||||
|
||||
Object.keys(RowAccountTypes).forEach((type) => {
|
||||
emptyRowAccountTypes.set(type as AccountType, {
|
||||
rewardType: type as AccountType,
|
||||
amount: '0',
|
||||
});
|
||||
});
|
||||
|
||||
export const generateEpochTotalRewardsList = ({
|
||||
data,
|
||||
epochId,
|
||||
page = 1,
|
||||
size = 10,
|
||||
}: {
|
||||
data?: EpochAssetsRewardsQuery | undefined;
|
||||
epochId: number;
|
||||
page?: number;
|
||||
size?: number;
|
||||
}) => {
|
||||
export const generateEpochTotalRewardsList = (
|
||||
epochData: EpochAssetsRewardsQuery | undefined
|
||||
) => {
|
||||
const epochRewardSummaries = removePaginationWrapper(
|
||||
data?.epochRewardSummaries?.edges
|
||||
epochData?.epochRewardSummaries?.edges
|
||||
);
|
||||
|
||||
const assets = removePaginationWrapper(data?.assetsConnection?.edges);
|
||||
const assets = removePaginationWrapper(epochData?.assetsConnection?.edges);
|
||||
|
||||
const map: Map<string, EpochTotalSummary> = new Map();
|
||||
const { fromEpoch, toEpoch } = calculateEpochOffset({ epochId, page, size });
|
||||
// Because the epochRewardSummaries don't have the asset name, we need to find it in the assets list
|
||||
const epochSummariesWithNamedReward: EpochSummaryWithNamedReward[] =
|
||||
epochRewardSummaries.map((epochReward) => ({
|
||||
...epochReward,
|
||||
name:
|
||||
assets.find((asset) => asset.id === epochReward.assetId)?.name || '',
|
||||
}));
|
||||
|
||||
for (let i = toEpoch; i >= fromEpoch; i--) {
|
||||
map.set(i.toString(), {
|
||||
epoch: i,
|
||||
assetRewards: new Map(),
|
||||
// Aggregating the epoch summaries by epoch number
|
||||
const aggregatedEpochSummariesByEpochNumber =
|
||||
epochSummariesWithNamedReward.reduce((acc, epochReward) => {
|
||||
const epoch = epochReward.epoch;
|
||||
const epochSummaryIndex = acc.findIndex(
|
||||
(epochSummary) => epochSummary[0].epoch === epoch
|
||||
);
|
||||
|
||||
if (epochSummaryIndex === -1) {
|
||||
acc.push([epochReward]);
|
||||
} else {
|
||||
acc[epochSummaryIndex].push(epochReward);
|
||||
}
|
||||
|
||||
return acc;
|
||||
}, [] as EpochSummaryWithNamedReward[][]);
|
||||
|
||||
// Now aggregate the array of arrays of epoch summaries by asset rewards.
|
||||
const epochTotalRewards: EpochTotalSummary[] =
|
||||
aggregatedEpochSummariesByEpochNumber.map((epochSummaries) => {
|
||||
const assetRewards = epochSummaries.reduce((acc, epochSummary) => {
|
||||
const assetRewardIndex = acc.findIndex(
|
||||
(assetReward) =>
|
||||
assetReward.assetId === epochSummary.assetId &&
|
||||
assetReward.name === epochSummary.name
|
||||
);
|
||||
|
||||
if (assetRewardIndex === -1) {
|
||||
acc.push({
|
||||
assetId: epochSummary.assetId,
|
||||
name: epochSummary.name,
|
||||
rewards: [
|
||||
...emptyRowAccountTypes.map((emptyRowAccountType) => {
|
||||
if (
|
||||
emptyRowAccountType.rewardType === epochSummary.rewardType
|
||||
) {
|
||||
return {
|
||||
rewardType: epochSummary.rewardType,
|
||||
amount: epochSummary.amount,
|
||||
};
|
||||
} else {
|
||||
return emptyRowAccountType;
|
||||
}
|
||||
}),
|
||||
],
|
||||
totalAmount: epochSummary.amount,
|
||||
});
|
||||
} else {
|
||||
acc[assetRewardIndex].rewards = acc[assetRewardIndex].rewards.map(
|
||||
(reward) => {
|
||||
if (reward.rewardType === epochSummary.rewardType) {
|
||||
return {
|
||||
rewardType: epochSummary.rewardType,
|
||||
amount: (
|
||||
Number(reward.amount) + Number(epochSummary.amount)
|
||||
).toString(),
|
||||
};
|
||||
} else {
|
||||
return reward;
|
||||
}
|
||||
}
|
||||
);
|
||||
acc[assetRewardIndex].totalAmount = (
|
||||
Number(acc[assetRewardIndex].totalAmount) +
|
||||
Number(epochSummary.amount)
|
||||
).toString();
|
||||
}
|
||||
|
||||
return acc;
|
||||
}, [] as AggregatedEpochRewardSummary[]);
|
||||
|
||||
return {
|
||||
epoch: epochSummaries[0].epoch,
|
||||
assetRewards: assetRewards.sort((a, b) => {
|
||||
return new BigNumber(b.totalAmount).comparedTo(a.totalAmount);
|
||||
}),
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
return epochRewardSummaries.reduce((acc, reward) => {
|
||||
const epoch = acc.get(reward.epoch.toString());
|
||||
|
||||
if (epoch) {
|
||||
const matchingAsset = assets.find((asset) => asset.id === reward.assetId);
|
||||
const assetWithRewards = epoch.assetRewards.get(reward.assetId);
|
||||
|
||||
const rewards =
|
||||
assetWithRewards?.rewards || new Map(emptyRowAccountTypes);
|
||||
const rewardItem = rewards?.get(reward.rewardType);
|
||||
const amount = (
|
||||
(Number(rewardItem?.amount) || 0) + Number(reward.amount)
|
||||
).toString();
|
||||
|
||||
rewards?.set(reward.rewardType, {
|
||||
rewardType: reward.rewardType,
|
||||
amount,
|
||||
});
|
||||
|
||||
epoch.assetRewards.set(reward.assetId, {
|
||||
assetId: reward.assetId,
|
||||
name: matchingAsset?.name || '',
|
||||
rewards: rewards || new Map(emptyRowAccountTypes),
|
||||
totalAmount: (
|
||||
Number(reward.amount) + Number(assetWithRewards?.totalAmount || 0)
|
||||
).toString(),
|
||||
});
|
||||
}
|
||||
return acc;
|
||||
}, map);
|
||||
return epochTotalRewards;
|
||||
};
|
||||
|
||||
@@ -23,18 +23,12 @@ fragment DelegationFields on Delegation {
|
||||
|
||||
query Rewards(
|
||||
$partyId: ID!
|
||||
$fromEpoch: Int
|
||||
$toEpoch: Int
|
||||
$rewardsPagination: Pagination
|
||||
$delegationsPagination: Pagination
|
||||
$rewardsPagination: Pagination
|
||||
) {
|
||||
party(id: $partyId) {
|
||||
id
|
||||
rewardsConnection(
|
||||
fromEpoch: $fromEpoch
|
||||
toEpoch: $toEpoch
|
||||
pagination: $rewardsPagination
|
||||
) {
|
||||
rewardsConnection(pagination: $rewardsPagination) {
|
||||
edges {
|
||||
node {
|
||||
...RewardFields
|
||||
@@ -49,6 +43,14 @@ query Rewards(
|
||||
}
|
||||
}
|
||||
}
|
||||
epoch {
|
||||
id
|
||||
timestamps {
|
||||
start
|
||||
end
|
||||
expiry
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fragment EpochRewardSummaryFields on EpochRewardSummary {
|
||||
@@ -58,10 +60,7 @@ fragment EpochRewardSummaryFields on EpochRewardSummary {
|
||||
rewardType
|
||||
}
|
||||
|
||||
query EpochAssetsRewards(
|
||||
$epochRewardSummariesFilter: RewardSummaryFilter
|
||||
$epochRewardSummariesPagination: Pagination
|
||||
) {
|
||||
query EpochAssetsRewards($epochRewardSummariesPagination: Pagination) {
|
||||
assetsConnection {
|
||||
edges {
|
||||
node {
|
||||
@@ -70,16 +69,18 @@ query EpochAssetsRewards(
|
||||
}
|
||||
}
|
||||
}
|
||||
epochRewardSummaries(
|
||||
filter: $epochRewardSummariesFilter
|
||||
pagination: $epochRewardSummariesPagination
|
||||
) {
|
||||
epochRewardSummaries(pagination: $epochRewardSummariesPagination) {
|
||||
edges {
|
||||
node {
|
||||
...EpochRewardSummaryFields
|
||||
}
|
||||
}
|
||||
}
|
||||
epoch {
|
||||
timestamps {
|
||||
expiry
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fragment EpochFields on Epoch {
|
||||
|
||||
+21
-21
@@ -9,24 +9,21 @@ export type DelegationFieldsFragment = { __typename?: 'Delegation', amount: stri
|
||||
|
||||
export type RewardsQueryVariables = Types.Exact<{
|
||||
partyId: Types.Scalars['ID'];
|
||||
fromEpoch?: Types.InputMaybe<Types.Scalars['Int']>;
|
||||
toEpoch?: Types.InputMaybe<Types.Scalars['Int']>;
|
||||
rewardsPagination?: Types.InputMaybe<Types.Pagination>;
|
||||
delegationsPagination?: Types.InputMaybe<Types.Pagination>;
|
||||
rewardsPagination?: Types.InputMaybe<Types.Pagination>;
|
||||
}>;
|
||||
|
||||
|
||||
export type RewardsQuery = { __typename?: 'Query', party?: { __typename?: 'Party', id: string, rewardsConnection?: { __typename?: 'RewardsConnection', edges?: Array<{ __typename?: 'RewardEdge', node: { __typename?: 'Reward', rewardType: Types.AccountType, amount: string, percentageOfTotal: string, receivedAt: any, asset: { __typename?: 'Asset', id: string, symbol: string, name: string }, party: { __typename?: 'Party', id: string }, epoch: { __typename?: 'Epoch', id: string } } } | null> | null } | null, delegationsConnection?: { __typename?: 'DelegationsConnection', edges?: Array<{ __typename?: 'DelegationEdge', node: { __typename?: 'Delegation', amount: string, epoch: number } } | null> | null } | null } | null };
|
||||
export type RewardsQuery = { __typename?: 'Query', party?: { __typename?: 'Party', id: string, rewardsConnection?: { __typename?: 'RewardsConnection', edges?: Array<{ __typename?: 'RewardEdge', node: { __typename?: 'Reward', rewardType: Types.AccountType, amount: string, percentageOfTotal: string, receivedAt: any, asset: { __typename?: 'Asset', id: string, symbol: string, name: string }, party: { __typename?: 'Party', id: string }, epoch: { __typename?: 'Epoch', id: string } } } | null> | null } | null, delegationsConnection?: { __typename?: 'DelegationsConnection', edges?: Array<{ __typename?: 'DelegationEdge', node: { __typename?: 'Delegation', amount: string, epoch: number } } | null> | null } | null } | null, epoch: { __typename?: 'Epoch', id: string, timestamps: { __typename?: 'EpochTimestamps', start?: any | null, end?: any | null, expiry?: any | 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>;
|
||||
epochRewardSummariesPagination?: Types.InputMaybe<Types.Pagination>;
|
||||
}>;
|
||||
|
||||
|
||||
export type EpochAssetsRewardsQuery = { __typename?: 'Query', assetsConnection?: { __typename?: 'AssetsConnection', edges?: Array<{ __typename?: 'AssetEdge', node: { __typename?: 'Asset', id: string, name: string } } | null> | null } | null, epochRewardSummaries?: { __typename?: 'EpochRewardSummaryConnection', edges?: Array<{ __typename?: 'EpochRewardSummaryEdge', node: { __typename?: 'EpochRewardSummary', epoch: number, assetId: string, amount: string, rewardType: Types.AccountType } } | null> | null } | null };
|
||||
export type EpochAssetsRewardsQuery = { __typename?: 'Query', assetsConnection?: { __typename?: 'AssetsConnection', edges?: Array<{ __typename?: 'AssetEdge', node: { __typename?: 'Asset', id: string, name: string } } | null> | null } | null, epochRewardSummaries?: { __typename?: 'EpochRewardSummaryConnection', edges?: Array<{ __typename?: 'EpochRewardSummaryEdge', node: { __typename?: 'EpochRewardSummary', epoch: number, assetId: string, amount: string, rewardType: Types.AccountType } } | null> | null } | null, epoch: { __typename?: 'Epoch', timestamps: { __typename?: 'EpochTimestamps', expiry?: any | null } } };
|
||||
|
||||
export type EpochFieldsFragment = { __typename?: 'Epoch', id: string, timestamps: { __typename?: 'EpochTimestamps', start?: any | null, end?: any | null, expiry?: any | null } };
|
||||
|
||||
@@ -79,14 +76,10 @@ export const EpochFieldsFragmentDoc = gql`
|
||||
}
|
||||
`;
|
||||
export const RewardsDocument = gql`
|
||||
query Rewards($partyId: ID!, $fromEpoch: Int, $toEpoch: Int, $rewardsPagination: Pagination, $delegationsPagination: Pagination) {
|
||||
query Rewards($partyId: ID!, $delegationsPagination: Pagination, $rewardsPagination: Pagination) {
|
||||
party(id: $partyId) {
|
||||
id
|
||||
rewardsConnection(
|
||||
fromEpoch: $fromEpoch
|
||||
toEpoch: $toEpoch
|
||||
pagination: $rewardsPagination
|
||||
) {
|
||||
rewardsConnection(pagination: $rewardsPagination) {
|
||||
edges {
|
||||
node {
|
||||
...RewardFields
|
||||
@@ -101,6 +94,14 @@ export const RewardsDocument = gql`
|
||||
}
|
||||
}
|
||||
}
|
||||
epoch {
|
||||
id
|
||||
timestamps {
|
||||
start
|
||||
end
|
||||
expiry
|
||||
}
|
||||
}
|
||||
}
|
||||
${RewardFieldsFragmentDoc}
|
||||
${DelegationFieldsFragmentDoc}`;
|
||||
@@ -118,10 +119,8 @@ ${DelegationFieldsFragmentDoc}`;
|
||||
* const { data, loading, error } = useRewardsQuery({
|
||||
* variables: {
|
||||
* partyId: // value for 'partyId'
|
||||
* fromEpoch: // value for 'fromEpoch'
|
||||
* toEpoch: // value for 'toEpoch'
|
||||
* rewardsPagination: // value for 'rewardsPagination'
|
||||
* delegationsPagination: // value for 'delegationsPagination'
|
||||
* rewardsPagination: // value for 'rewardsPagination'
|
||||
* },
|
||||
* });
|
||||
*/
|
||||
@@ -137,7 +136,7 @@ export type RewardsQueryHookResult = ReturnType<typeof useRewardsQuery>;
|
||||
export type RewardsLazyQueryHookResult = ReturnType<typeof useRewardsLazyQuery>;
|
||||
export type RewardsQueryResult = Apollo.QueryResult<RewardsQuery, RewardsQueryVariables>;
|
||||
export const EpochAssetsRewardsDocument = gql`
|
||||
query EpochAssetsRewards($epochRewardSummariesFilter: RewardSummaryFilter, $epochRewardSummariesPagination: Pagination) {
|
||||
query EpochAssetsRewards($epochRewardSummariesPagination: Pagination) {
|
||||
assetsConnection {
|
||||
edges {
|
||||
node {
|
||||
@@ -146,16 +145,18 @@ export const EpochAssetsRewardsDocument = gql`
|
||||
}
|
||||
}
|
||||
}
|
||||
epochRewardSummaries(
|
||||
filter: $epochRewardSummariesFilter
|
||||
pagination: $epochRewardSummariesPagination
|
||||
) {
|
||||
epochRewardSummaries(pagination: $epochRewardSummariesPagination) {
|
||||
edges {
|
||||
node {
|
||||
...EpochRewardSummaryFields
|
||||
}
|
||||
}
|
||||
}
|
||||
epoch {
|
||||
timestamps {
|
||||
expiry
|
||||
}
|
||||
}
|
||||
}
|
||||
${EpochRewardSummaryFieldsFragmentDoc}`;
|
||||
|
||||
@@ -171,7 +172,6 @@ export const EpochAssetsRewardsDocument = gql`
|
||||
* @example
|
||||
* const { data, loading, error } = useEpochAssetsRewardsQuery({
|
||||
* variables: {
|
||||
* epochRewardSummariesFilter: // value for 'epochRewardSummariesFilter'
|
||||
* epochRewardSummariesPagination: // value for 'epochRewardSummariesPagination'
|
||||
* },
|
||||
* });
|
||||
|
||||
@@ -114,7 +114,7 @@ export const RewardsPage = () => {
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="w-[360px]">
|
||||
<div className="w-[440px]">
|
||||
<Toggle
|
||||
name="epoch-reward-view-toggle"
|
||||
toggles={[
|
||||
@@ -136,15 +136,11 @@ export const RewardsPage = () => {
|
||||
</section>
|
||||
|
||||
{toggleRewardsView === 'total' ? (
|
||||
epochData?.epoch ? (
|
||||
<EpochTotalRewards currentEpoch={epochData?.epoch} />
|
||||
) : null
|
||||
<EpochTotalRewards />
|
||||
) : (
|
||||
<section>
|
||||
{pubKey && pubKeys?.length ? (
|
||||
epochData?.epoch ? (
|
||||
<EpochIndividualRewards currentEpoch={epochData?.epoch} />
|
||||
) : null
|
||||
<EpochIndividualRewards />
|
||||
) : (
|
||||
<ConnectToSeeRewards />
|
||||
)}
|
||||
|
||||
@@ -109,13 +109,6 @@ const LazyProposal = React.lazy(
|
||||
)
|
||||
);
|
||||
|
||||
const LazyProtocolUpgradeProposal = React.lazy(
|
||||
() =>
|
||||
import(
|
||||
/* webpackChunkName: "route-governance-protocol-upgrade-proposal", webpackPrefetch: true */ './proposals/protocol-upgrade'
|
||||
)
|
||||
);
|
||||
|
||||
const LazyProposalsList = React.lazy(
|
||||
() =>
|
||||
import(
|
||||
@@ -264,10 +257,6 @@ const routerConfig = [
|
||||
},
|
||||
{ path: 'proposals', element: <LazyProposalsList /> },
|
||||
{ path: ':proposalId', element: <LazyProposal /> },
|
||||
{
|
||||
path: 'protocol-upgrade/:proposalReleaseTag',
|
||||
element: <LazyProtocolUpgradeProposal />,
|
||||
},
|
||||
{ path: 'rejected', element: <LazyRejectedProposalsList /> },
|
||||
],
|
||||
},
|
||||
|
||||
@@ -7,10 +7,9 @@ query PreviousEpoch($epochId: ID) {
|
||||
id
|
||||
rewardScore {
|
||||
rawValidatorScore
|
||||
performanceScore
|
||||
}
|
||||
rankingScore {
|
||||
stakeScore
|
||||
performanceScore
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ export type PreviousEpochQueryVariables = Types.Exact<{
|
||||
}>;
|
||||
|
||||
|
||||
export type PreviousEpochQuery = { __typename?: 'Query', epoch: { __typename?: 'Epoch', id: string, validatorsConnection?: { __typename?: 'NodesConnection', edges?: Array<{ __typename?: 'NodeEdge', node: { __typename?: 'Node', id: string, rewardScore?: { __typename?: 'RewardScore', rawValidatorScore: string, performanceScore: string } | null, rankingScore: { __typename?: 'RankingScore', stakeScore: string } } } | null> | null } | null } };
|
||||
export type PreviousEpochQuery = { __typename?: 'Query', epoch: { __typename?: 'Epoch', id: string, validatorsConnection?: { __typename?: 'NodesConnection', edges?: Array<{ __typename?: 'NodeEdge', node: { __typename?: 'Node', id: string, rewardScore?: { __typename?: 'RewardScore', rawValidatorScore: string } | null, rankingScore: { __typename?: 'RankingScore', performanceScore: string } } } | null> | null } | null } };
|
||||
|
||||
|
||||
export const PreviousEpochDocument = gql`
|
||||
@@ -21,10 +21,9 @@ export const PreviousEpochDocument = gql`
|
||||
id
|
||||
rewardScore {
|
||||
rawValidatorScore
|
||||
performanceScore
|
||||
}
|
||||
rankingScore {
|
||||
stakeScore
|
||||
performanceScore
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+3
-6
@@ -81,10 +81,9 @@ const MOCK_PREVIOUS_EPOCH: PreviousEpochQuery = {
|
||||
id: 'ccc022b7e63a4d0a6d3a193c3940c88574060e58a184964c994998d86835a1b4',
|
||||
rewardScore: {
|
||||
rawValidatorScore: '0.25',
|
||||
performanceScore: '0.9998677767864936',
|
||||
},
|
||||
rankingScore: {
|
||||
stakeScore: '0.2499583402766206',
|
||||
performanceScore: '0.9998677767864936',
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -93,10 +92,9 @@ const MOCK_PREVIOUS_EPOCH: PreviousEpochQuery = {
|
||||
id: '966438c6bffac737cfb08173ffcb3f393c4692b099ad80cb45a82e2dc0a8cf99',
|
||||
rewardScore: {
|
||||
rawValidatorScore: '0.3',
|
||||
performanceScore: '1',
|
||||
},
|
||||
rankingScore: {
|
||||
stakeScore: '0.25',
|
||||
performanceScore: '1',
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -105,10 +103,9 @@ const MOCK_PREVIOUS_EPOCH: PreviousEpochQuery = {
|
||||
id: '12c81b738e8051152e1afe44376ec37bca9216466e6d44cdd772194bad0ada81',
|
||||
rewardScore: {
|
||||
rawValidatorScore: '0.35',
|
||||
performanceScore: '0.999629748500531',
|
||||
},
|
||||
rankingScore: {
|
||||
stakeScore: '0.2312',
|
||||
performanceScore: '0.999629748500531',
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
+20
-16
@@ -10,6 +10,7 @@ import {
|
||||
getFormattedPerformanceScore,
|
||||
getLastEpochScoreAndPerformance,
|
||||
getNormalisedVotingPower,
|
||||
getOverstakedAmount,
|
||||
getOverstakingPenalty,
|
||||
getPerformancePenalty,
|
||||
getTotalPenalties,
|
||||
@@ -51,6 +52,7 @@ interface CanonisedConsensusNodeProps {
|
||||
[ValidatorFields.STAKED_BY_OPERATOR]: string;
|
||||
[ValidatorFields.PERFORMANCE_SCORE]: string;
|
||||
[ValidatorFields.PERFORMANCE_PENALTY]: string;
|
||||
[ValidatorFields.OVERSTAKED_AMOUNT]: string;
|
||||
[ValidatorFields.OVERSTAKING_PENALTY]: string;
|
||||
[ValidatorFields.TOTAL_PENALTIES]: string;
|
||||
[ValidatorFields.PENDING_STAKE]: string;
|
||||
@@ -162,11 +164,14 @@ export const ConsensusValidatorsTable = ({
|
||||
pendingUserStake,
|
||||
userStakeShare,
|
||||
}) => {
|
||||
const {
|
||||
rawValidatorScore: previousEpochValidatorScore,
|
||||
performanceScore: previousEpochPerformanceScore,
|
||||
stakeScore: previousEpochStakeScore,
|
||||
} = getLastEpochScoreAndPerformance(previousEpochData, id);
|
||||
const { rawValidatorScore, performanceScore } =
|
||||
getLastEpochScoreAndPerformance(previousEpochData, id);
|
||||
|
||||
const overstakedAmount = getOverstakedAmount(
|
||||
rawValidatorScore,
|
||||
stakedTotal,
|
||||
totalStake
|
||||
);
|
||||
|
||||
return {
|
||||
id,
|
||||
@@ -179,7 +184,7 @@ export const ConsensusValidatorsTable = ({
|
||||
[ValidatorFields.NORMALISED_VOTING_POWER]:
|
||||
getNormalisedVotingPower(votingPower),
|
||||
[ValidatorFields.UNNORMALISED_VOTING_POWER]:
|
||||
getUnnormalisedVotingPower(previousEpochValidatorScore),
|
||||
getUnnormalisedVotingPower(rawValidatorScore),
|
||||
[ValidatorFields.STAKE_SHARE]: stakedTotalPercentage(stakeScore),
|
||||
[ValidatorFields.STAKED_BY_DELEGATES]: formatNumber(
|
||||
toBigNum(stakedByDelegates, decimals),
|
||||
@@ -189,19 +194,18 @@ export const ConsensusValidatorsTable = ({
|
||||
toBigNum(stakedByOperator, decimals),
|
||||
2
|
||||
),
|
||||
[ValidatorFields.PERFORMANCE_SCORE]: getFormattedPerformanceScore(
|
||||
previousEpochPerformanceScore
|
||||
).toString(),
|
||||
[ValidatorFields.PERFORMANCE_PENALTY]: getPerformancePenalty(
|
||||
previousEpochPerformanceScore
|
||||
),
|
||||
[ValidatorFields.PERFORMANCE_SCORE]:
|
||||
getFormattedPerformanceScore(performanceScore).toString(),
|
||||
[ValidatorFields.PERFORMANCE_PENALTY]:
|
||||
getPerformancePenalty(performanceScore),
|
||||
[ValidatorFields.OVERSTAKED_AMOUNT]: overstakedAmount.toString(),
|
||||
[ValidatorFields.OVERSTAKING_PENALTY]: getOverstakingPenalty(
|
||||
previousEpochValidatorScore,
|
||||
previousEpochStakeScore
|
||||
overstakedAmount,
|
||||
totalStake
|
||||
),
|
||||
[ValidatorFields.TOTAL_PENALTIES]: getTotalPenalties(
|
||||
previousEpochValidatorScore,
|
||||
previousEpochPerformanceScore,
|
||||
rawValidatorScore,
|
||||
performanceScore,
|
||||
stakedTotal,
|
||||
totalStake
|
||||
),
|
||||
|
||||
+19
-19
@@ -7,6 +7,7 @@ import { BigNumber } from '../../../../lib/bignumber';
|
||||
import {
|
||||
getFormattedPerformanceScore,
|
||||
getLastEpochScoreAndPerformance,
|
||||
getOverstakedAmount,
|
||||
getOverstakingPenalty,
|
||||
getPerformancePenalty,
|
||||
getTotalPenalties,
|
||||
@@ -81,21 +82,21 @@ export const StandbyPendingValidatorsTable = ({
|
||||
pendingUserStake,
|
||||
userStakeShare,
|
||||
}) => {
|
||||
const {
|
||||
rawValidatorScore: previousEpochValidatorScore,
|
||||
performanceScore: previousEpochPerformanceScore,
|
||||
stakeScore: previousEpochStakeScore,
|
||||
} = getLastEpochScoreAndPerformance(previousEpochData, id);
|
||||
const { rawValidatorScore, performanceScore } =
|
||||
getLastEpochScoreAndPerformance(previousEpochData, id);
|
||||
|
||||
const overstakedAmount = getOverstakedAmount(
|
||||
rawValidatorScore,
|
||||
stakedTotal,
|
||||
totalStake
|
||||
);
|
||||
let individualStakeNeededForPromotion,
|
||||
individualStakeNeededForPromotionDescription;
|
||||
|
||||
if (stakeNeededForPromotion && previousEpochPerformanceScore) {
|
||||
if (stakeNeededForPromotion && performanceScore) {
|
||||
const stakedTotalBigNum = new BigNumber(stakedTotal);
|
||||
const stakeNeededBigNum = new BigNumber(stakeNeededForPromotion);
|
||||
const performanceScoreBigNum = new BigNumber(
|
||||
previousEpochPerformanceScore
|
||||
);
|
||||
const performanceScoreBigNum = new BigNumber(performanceScore);
|
||||
|
||||
const calc = stakeNeededBigNum
|
||||
.dividedBy(performanceScoreBigNum)
|
||||
@@ -141,19 +142,18 @@ export const StandbyPendingValidatorsTable = ({
|
||||
toBigNum(stakedByOperator, decimals),
|
||||
2
|
||||
),
|
||||
[ValidatorFields.PERFORMANCE_SCORE]: getFormattedPerformanceScore(
|
||||
previousEpochPerformanceScore
|
||||
).toString(),
|
||||
[ValidatorFields.PERFORMANCE_PENALTY]: getPerformancePenalty(
|
||||
previousEpochPerformanceScore
|
||||
),
|
||||
[ValidatorFields.PERFORMANCE_SCORE]:
|
||||
getFormattedPerformanceScore(performanceScore).toString(),
|
||||
[ValidatorFields.PERFORMANCE_PENALTY]:
|
||||
getPerformancePenalty(performanceScore),
|
||||
[ValidatorFields.OVERSTAKED_AMOUNT]: overstakedAmount.toString(),
|
||||
[ValidatorFields.OVERSTAKING_PENALTY]: getOverstakingPenalty(
|
||||
previousEpochValidatorScore,
|
||||
previousEpochStakeScore
|
||||
overstakedAmount,
|
||||
totalStake
|
||||
),
|
||||
[ValidatorFields.TOTAL_PENALTIES]: getTotalPenalties(
|
||||
previousEpochValidatorScore,
|
||||
previousEpochPerformanceScore,
|
||||
rawValidatorScore,
|
||||
performanceScore,
|
||||
stakedTotal,
|
||||
totalStake
|
||||
),
|
||||
|
||||
@@ -155,16 +155,16 @@ export const ValidatorTables = ({
|
||||
return (
|
||||
<section data-testid="validator-tables">
|
||||
<div className="grid w-full justify-end">
|
||||
<div className="w-[340px]">
|
||||
<div className="w-[400px]">
|
||||
<Toggle
|
||||
name="validators-view-toggle"
|
||||
toggles={[
|
||||
{
|
||||
label: t('All validators'),
|
||||
label: t('ALL VALIDATORS'),
|
||||
value: 'all',
|
||||
},
|
||||
{
|
||||
label: t('Staked by me'),
|
||||
label: t('STAKED BY ME'),
|
||||
value: 'myStake',
|
||||
},
|
||||
]}
|
||||
|
||||
@@ -13,8 +13,6 @@ import { StakingFormTxStatuses } from './staking-form-tx-statuses';
|
||||
import {
|
||||
ButtonLink,
|
||||
FormGroup,
|
||||
Intent,
|
||||
Notification,
|
||||
Radio,
|
||||
RadioGroup,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
@@ -31,7 +29,6 @@ import type {
|
||||
DelegateSubmissionBody,
|
||||
UndelegateSubmissionBody,
|
||||
} from '@vegaprotocol/wallet';
|
||||
import Routes from '../../routes';
|
||||
|
||||
export enum FormState {
|
||||
Default,
|
||||
@@ -194,65 +191,51 @@ export const StakingForm = ({
|
||||
}, [isDialogVisible]);
|
||||
|
||||
return (
|
||||
<div className="my-8">
|
||||
<>
|
||||
<SubHeading title={t('Manage your stake')} />
|
||||
{formState === FormState.Default &&
|
||||
availableStakeToAdd.isEqualTo(0) &&
|
||||
availableStakeToRemove.isEqualTo(0) && (
|
||||
<div className="mb-4">
|
||||
{lien.isGreaterThan(0) ? (
|
||||
<Notification
|
||||
intent={Intent.Warning}
|
||||
message={t('stakeNodeWrongVegaKey')}
|
||||
/>
|
||||
<span className="text-vega-pink">
|
||||
{t('stakeNodeWrongVegaKey')}
|
||||
</span>
|
||||
) : (
|
||||
<Notification
|
||||
message={t('stakeNodeNone')}
|
||||
intent={Intent.Warning}
|
||||
buttonProps={{
|
||||
text: t('associateVegaNow'),
|
||||
action: () => navigate(Routes.ASSOCIATE),
|
||||
className: 'py-1',
|
||||
size: 'sm',
|
||||
}}
|
||||
/>
|
||||
<span className="text-vega-orange">{t('stakeNodeNone')}</span>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="mb-8">
|
||||
<FormGroup
|
||||
label={t('Select if you want to add or remove stake')}
|
||||
labelFor="radio-stake-options"
|
||||
hideLabel={true}
|
||||
<FormGroup
|
||||
label={t('Select if you want to add or remove stake')}
|
||||
labelFor="radio-stake-options"
|
||||
hideLabel={true}
|
||||
>
|
||||
<RadioGroup
|
||||
name="radio-stake-options"
|
||||
onChange={(value) => {
|
||||
// @ts-ignore value does exist on target
|
||||
setAction(value);
|
||||
navigate(`?action=${value}`, {
|
||||
replace: true,
|
||||
});
|
||||
}}
|
||||
value={action}
|
||||
>
|
||||
<RadioGroup
|
||||
name="radio-stake-options"
|
||||
onChange={(value) => {
|
||||
// @ts-ignore value does exist on target
|
||||
setAction(value);
|
||||
navigate(`?action=${value}`, {
|
||||
replace: true,
|
||||
});
|
||||
}}
|
||||
value={action}
|
||||
>
|
||||
<Radio
|
||||
disabled={availableStakeToAdd.isEqualTo(0)}
|
||||
value={Actions.Add}
|
||||
label="Add"
|
||||
id="add-stake-radio"
|
||||
/>
|
||||
<Radio
|
||||
disabled={availableStakeToRemove.isEqualTo(0)}
|
||||
value={Actions.Remove}
|
||||
label="Remove"
|
||||
id="remove-stake-radio"
|
||||
/>
|
||||
</RadioGroup>
|
||||
</FormGroup>
|
||||
</div>
|
||||
|
||||
<Radio
|
||||
disabled={availableStakeToAdd.isEqualTo(0)}
|
||||
value={Actions.Add}
|
||||
label="Add"
|
||||
id="add-stake-radio"
|
||||
/>
|
||||
<Radio
|
||||
disabled={availableStakeToRemove.isEqualTo(0)}
|
||||
value={Actions.Remove}
|
||||
label="Remove"
|
||||
id="remove-stake-radio"
|
||||
/>
|
||||
</RadioGroup>
|
||||
</FormGroup>
|
||||
{action !== undefined && (
|
||||
// eslint-disable-next-line
|
||||
<>
|
||||
@@ -331,6 +314,6 @@ export const StakingForm = ({
|
||||
toggleDialog={toggleDialog}
|
||||
error={error}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -20,6 +20,7 @@ import { SubHeading } from '../../../components/heading';
|
||||
import {
|
||||
getLastEpochScoreAndPerformance,
|
||||
getNormalisedVotingPower,
|
||||
getOverstakedAmount,
|
||||
getOverstakingPenalty,
|
||||
getPerformancePenalty,
|
||||
getTotalPenalties,
|
||||
@@ -74,9 +75,15 @@ export const ValidatorTable = ({
|
||||
|
||||
const stakedOnNode = toBigNum(node.stakedTotal, decimals);
|
||||
|
||||
const { rawValidatorScore, performanceScore, stakeScore } =
|
||||
const { rawValidatorScore, performanceScore } =
|
||||
getLastEpochScoreAndPerformance(previousEpochData, node.id);
|
||||
|
||||
const overstakedAmount = getOverstakedAmount(
|
||||
rawValidatorScore,
|
||||
stakedTotal,
|
||||
node.stakedTotal
|
||||
);
|
||||
|
||||
const stakePercentage = getStakePercentage(total, stakedOnNode);
|
||||
|
||||
const totalPenaltiesAmount = getTotalPenalties(
|
||||
@@ -238,7 +245,7 @@ export const ValidatorTable = ({
|
||||
|
||||
<Tooltip description={t('OverstakedPenaltyDescription')}>
|
||||
<span data-testid="overstaking-penalty">
|
||||
{getOverstakingPenalty(rawValidatorScore, stakeScore)}
|
||||
{getOverstakingPenalty(overstakedAmount, node.stakedTotal)}
|
||||
</span>
|
||||
</Tooltip>
|
||||
</KeyValueTableRow>
|
||||
|
||||
@@ -4,6 +4,7 @@ import {
|
||||
getNormalisedVotingPower,
|
||||
getUnnormalisedVotingPower,
|
||||
getOverstakingPenalty,
|
||||
getOverstakedAmount,
|
||||
getFormattedPerformanceScore,
|
||||
getPerformancePenalty,
|
||||
getTotalPenalties,
|
||||
@@ -21,10 +22,9 @@ describe('getLastEpochScoreAndPerformance', () => {
|
||||
id: '0x123',
|
||||
rewardScore: {
|
||||
rawValidatorScore: '0.25',
|
||||
performanceScore: '0.75',
|
||||
},
|
||||
rankingScore: {
|
||||
stakeScore: '0.25',
|
||||
performanceScore: '0.75',
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -33,10 +33,9 @@ describe('getLastEpochScoreAndPerformance', () => {
|
||||
id: '0x234',
|
||||
rewardScore: {
|
||||
rawValidatorScore: '0.35',
|
||||
performanceScore: '0.85',
|
||||
},
|
||||
rankingScore: {
|
||||
stakeScore: '0.25',
|
||||
performanceScore: '0.85',
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -51,14 +50,12 @@ describe('getLastEpochScoreAndPerformance', () => {
|
||||
).toEqual({
|
||||
rawValidatorScore: '0.25',
|
||||
performanceScore: '0.75',
|
||||
stakeScore: '0.25',
|
||||
});
|
||||
expect(
|
||||
getLastEpochScoreAndPerformance(mockPreviousEpochData, '0x234')
|
||||
).toEqual({
|
||||
rawValidatorScore: '0.35',
|
||||
performanceScore: '0.85',
|
||||
stakeScore: '0.25',
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -82,34 +79,40 @@ describe('getUnnormalisedVotingPower', () => {
|
||||
});
|
||||
|
||||
describe('getOverstakingPenalty', () => {
|
||||
it('returns "0%" when both arguments are null or undefined', () => {
|
||||
expect(getOverstakingPenalty(null, null)).toBe('0%');
|
||||
expect(getOverstakingPenalty(undefined, undefined)).toBe('0%');
|
||||
expect(getOverstakingPenalty(null, undefined)).toBe('0%');
|
||||
expect(getOverstakingPenalty(undefined, null)).toBe('0%');
|
||||
it('should return the overstaking penalty', () => {
|
||||
expect(
|
||||
getOverstakingPenalty(new BigNumber(100), Number(1000).toString())
|
||||
).toEqual('10.00%');
|
||||
expect(
|
||||
getOverstakingPenalty(new BigNumber(500), Number(2000).toString())
|
||||
).toEqual('25.00%');
|
||||
});
|
||||
});
|
||||
|
||||
describe('getOverstakedAmount', () => {
|
||||
it('should return the overstaked amount', () => {
|
||||
expect(
|
||||
// If a validator score is 0, any amount staked on the node is considered overstaked
|
||||
getOverstakedAmount('0', Number(100).toString(), Number(20).toString())
|
||||
).toEqual(new BigNumber(20));
|
||||
expect(
|
||||
getOverstakedAmount('0.05', Number(100).toString(), Number(20).toString())
|
||||
).toEqual(new BigNumber(15));
|
||||
expect(
|
||||
getOverstakedAmount('0.1', Number(100).toString(), Number(20).toString())
|
||||
).toEqual(new BigNumber(10));
|
||||
expect(
|
||||
getOverstakedAmount('0.15', Number(100).toString(), Number(20).toString())
|
||||
).toEqual(new BigNumber(5));
|
||||
expect(
|
||||
getOverstakedAmount('0.2', Number(100).toString(), Number(20).toString())
|
||||
).toEqual(new BigNumber(0));
|
||||
});
|
||||
|
||||
it('returns "0%" when one argument is null or undefined', () => {
|
||||
expect(getOverstakingPenalty('10', null)).toBe('0%');
|
||||
expect(getOverstakingPenalty(null, '20')).toBe('0%');
|
||||
expect(getOverstakingPenalty('10', undefined)).toBe('0%');
|
||||
expect(getOverstakingPenalty(undefined, '20')).toBe('0%');
|
||||
});
|
||||
|
||||
it('returns "0%" when validatorScore or stakeScore is zero', () => {
|
||||
expect(getOverstakingPenalty('0', '20')).toBe('0%');
|
||||
expect(getOverstakingPenalty('10', '0')).toBe('0%');
|
||||
});
|
||||
|
||||
it('returns the correct overstaking penalty', () => {
|
||||
expect(getOverstakingPenalty('0.18', '0.2')).toBe('10.00%');
|
||||
expect(getOverstakingPenalty('0.2', '0.2')).toBe('0.00%');
|
||||
expect(getOverstakingPenalty('0.04', '0.2')).toBe('80.00%');
|
||||
});
|
||||
|
||||
it('handles string numbers with decimals', () => {
|
||||
expect(getOverstakingPenalty('7.5', '15')).toBe('50.00%');
|
||||
expect(getOverstakingPenalty('12.5', '25')).toBe('50.00%');
|
||||
it('should return 0 if the overstaked amount is negative', () => {
|
||||
expect(
|
||||
getOverstakedAmount('0.8', Number(100).toString(), Number(20).toString())
|
||||
).toEqual(new BigNumber(0));
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -15,13 +15,12 @@ export const getLastEpochScoreAndPerformance = (
|
||||
|
||||
return {
|
||||
rawValidatorScore: validator?.rewardScore?.rawValidatorScore,
|
||||
performanceScore: validator?.rewardScore?.performanceScore,
|
||||
stakeScore: validator?.rankingScore?.stakeScore,
|
||||
performanceScore: validator?.rankingScore?.performanceScore,
|
||||
};
|
||||
};
|
||||
|
||||
export const getNormalisedVotingPower = (votingPower: string, decimals = 2) =>
|
||||
formatNumberPercentage(new BigNumber(votingPower).dividedBy(100), decimals);
|
||||
export const getNormalisedVotingPower = (votingPower: string) =>
|
||||
formatNumberPercentage(new BigNumber(votingPower).dividedBy(100), 2);
|
||||
|
||||
export const getUnnormalisedVotingPower = (
|
||||
validatorScore: string | null | undefined
|
||||
@@ -43,26 +42,31 @@ export const getPerformancePenalty = (performanceScore?: string) =>
|
||||
2
|
||||
);
|
||||
|
||||
export const getOverstakingPenalty = (
|
||||
export const getOverstakedAmount = (
|
||||
validatorScore: string | null | undefined,
|
||||
stakeScore: string | null | undefined
|
||||
totalStake: string,
|
||||
stakedOnNode: string
|
||||
) => {
|
||||
if (!validatorScore || !stakeScore) {
|
||||
return '0%';
|
||||
}
|
||||
const toReturn = validatorScore
|
||||
? new BigNumber(stakedOnNode).minus(
|
||||
new BigNumber(validatorScore).times(new BigNumber(totalStake))
|
||||
)
|
||||
: new BigNumber(0);
|
||||
|
||||
return toReturn.isNegative() ? new BigNumber(0) : toReturn;
|
||||
};
|
||||
|
||||
export const getOverstakingPenalty = (
|
||||
overstakedAmount: BigNumber,
|
||||
stakedOnNode: string
|
||||
) => {
|
||||
// avoid division by zero
|
||||
if (
|
||||
new BigNumber(validatorScore).isZero() ||
|
||||
new BigNumber(stakeScore).isZero()
|
||||
) {
|
||||
return '0%';
|
||||
if (new BigNumber(stakedOnNode).isZero() || overstakedAmount.isZero()) {
|
||||
return '0';
|
||||
}
|
||||
|
||||
return formatNumberPercentage(
|
||||
new BigNumber(1)
|
||||
.minus(new BigNumber(validatorScore).dividedBy(new BigNumber(stakeScore)))
|
||||
.times(100),
|
||||
overstakedAmount.dividedBy(new BigNumber(stakedOnNode)).times(100),
|
||||
2
|
||||
);
|
||||
};
|
||||
|
||||
@@ -2,4 +2,4 @@ NX_VEGA_URL=https://api.stagnet3.vega.xyz/graphql
|
||||
NX_VEGA_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/networks-internal/main/stagnet3/vegawallet-stagnet3.toml
|
||||
NX_VEGA_NETWORKS='{"TESTNET":"https://multisig-signer.fairground.wtf","MAINNET":"https://multisig-signer.vega.xyz"}'
|
||||
NX_VEGA_ENV=STAGNET3
|
||||
NX_WALLETCONNECT_PROJECT_ID=fe8091dc35738863e509fc4947525c72
|
||||
|
||||
|
||||
@@ -9,7 +9,6 @@ import {
|
||||
} from '@vegaprotocol/environment';
|
||||
import { AsyncRenderer, Button, Lozenge } from '@vegaprotocol/ui-toolkit';
|
||||
import type { EthereumConfig } from '@vegaprotocol/web3';
|
||||
import { useWeb3Disconnect } from '@vegaprotocol/web3';
|
||||
import { useEthereumConfig, Web3Provider } from '@vegaprotocol/web3';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { ENV } from './config/env';
|
||||
@@ -35,7 +34,6 @@ const pageWrapperClasses = classnames(
|
||||
|
||||
const ConnectedApp = ({ config }: { config: EthereumConfig | null }) => {
|
||||
const { account, connector } = useWeb3React();
|
||||
const disconnect = useWeb3Disconnect(connector);
|
||||
return (
|
||||
<main className="w-full max-w-3xl px-5 justify-self-center">
|
||||
<h1>{t('Multisig signer')}</h1>
|
||||
@@ -43,7 +41,7 @@ const ConnectedApp = ({ config }: { config: EthereumConfig | null }) => {
|
||||
<p>
|
||||
Connected to Eth wallet: <Lozenge>{account}</Lozenge>
|
||||
</p>
|
||||
<Button onClick={() => disconnect()}>Disconnect</Button>
|
||||
<Button onClick={() => connector.deactivate()}>Disconnect</Button>
|
||||
</div>
|
||||
<ContractDetails config={config} />
|
||||
<h2>{t('Add or remove signer')}</h2>
|
||||
|
||||
@@ -1,10 +1,5 @@
|
||||
import { useEnvironment } from '@vegaprotocol/environment';
|
||||
import {
|
||||
getChainName,
|
||||
useEthereumConfig,
|
||||
useWeb3ConnectStore,
|
||||
useWeb3Disconnect,
|
||||
} from '@vegaprotocol/web3';
|
||||
import { getChainName, useEthereumConfig } from '@vegaprotocol/web3';
|
||||
import { Button, Splash, AsyncRenderer } from '@vegaprotocol/ui-toolkit';
|
||||
import { Web3ConnectDialog } from '@vegaprotocol/web3';
|
||||
import { useWeb3React } from '@web3-react/core';
|
||||
@@ -60,9 +55,7 @@ export const Web3Content = ({
|
||||
appChainId,
|
||||
setDialogOpen,
|
||||
}: Web3ContentProps) => {
|
||||
const { connector, chainId } = useWeb3React();
|
||||
const error = useWeb3ConnectStore((store) => store.error);
|
||||
const disconnect = useWeb3Disconnect(connector);
|
||||
const { error, connector, chainId } = useWeb3React();
|
||||
|
||||
useEffect(() => {
|
||||
if (connector?.connectEagerly) {
|
||||
@@ -78,7 +71,7 @@ export const Web3Content = ({
|
||||
<Splash>
|
||||
<div className="flex flex-col items-center gap-12">
|
||||
<p className="text-white">Something went wrong: {error.message}</p>
|
||||
<Button onClick={() => disconnect()}>Disconnect</Button>
|
||||
<Button onClick={() => connector.deactivate()}>Disconnect</Button>
|
||||
</div>
|
||||
</Splash>
|
||||
);
|
||||
@@ -91,7 +84,7 @@ export const Web3Content = ({
|
||||
<p className="text-white">
|
||||
This app only works on {getChainName(appChainId)}
|
||||
</p>
|
||||
<Button onClick={() => disconnect()}>Disconnect</Button>
|
||||
<Button onClick={() => connector.deactivate()}>Disconnect</Button>
|
||||
</div>
|
||||
</Splash>
|
||||
);
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import { ethers } from 'ethers';
|
||||
import type { Web3ReactHooks } from '@web3-react/core';
|
||||
import { initializeConnector } from '@web3-react/core';
|
||||
import { MetaMask } from '@web3-react/metamask';
|
||||
import { WalletConnect } from '@web3-react/walletconnect';
|
||||
import type { Connector } from '@web3-react/types';
|
||||
import {
|
||||
WALLETCONNECT_PROJECT_ID,
|
||||
initializeCoinbaseConnector,
|
||||
initializeMetaMaskConnector,
|
||||
initializeWalletConnectLegacyConnector,
|
||||
initializeWalletConnector,
|
||||
} from '@vegaprotocol/web3';
|
||||
|
||||
const [metamask, metamaskHooks] = initializeConnector<MetaMask>(
|
||||
(actions) => new MetaMask(actions)
|
||||
);
|
||||
|
||||
export const createDefaultProvider = (providerUrl: string, chainId: number) => {
|
||||
return new ethers.providers.JsonRpcProvider(providerUrl, chainId);
|
||||
@@ -17,11 +17,18 @@ export const createConnectors = (providerUrl: string, chainId: number) => {
|
||||
if (isNaN(chainId)) {
|
||||
throw new Error('Invalid Ethereum chain ID for environment');
|
||||
}
|
||||
|
||||
const [walletconnect, walletconnectHooks] =
|
||||
initializeConnector<WalletConnect>(
|
||||
(actions) =>
|
||||
new WalletConnect(actions, {
|
||||
rpc: {
|
||||
[chainId]: providerUrl,
|
||||
},
|
||||
}),
|
||||
[chainId]
|
||||
);
|
||||
return [
|
||||
initializeMetaMaskConnector(),
|
||||
initializeCoinbaseConnector(providerUrl),
|
||||
initializeWalletConnector(WALLETCONNECT_PROJECT_ID, chainId, providerUrl),
|
||||
initializeWalletConnectLegacyConnector(chainId, providerUrl),
|
||||
].filter(Boolean) as unknown as [Connector, Web3ReactHooks][];
|
||||
[metamask, metamaskHooks],
|
||||
[walletconnect, walletconnectHooks],
|
||||
] as [Connector, Web3ReactHooks][];
|
||||
};
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user