Compare commits

..
Author SHA1 Message Date
Matthew Russell a5b97721f0 feat: change default protocol countdown mode as thats what all apps use 2023-07-31 16:04:46 +01:00
Matthew Russell ecfc364e4c chore: fix console.log errors 2023-07-31 15:26:47 +01:00
Matthew Russell 7b566fa927 chore: re add protocol upgrade notice 2023-07-31 15:03:09 +01:00
Matthew Russell a3376fb858 chore: add missing button attributes 2023-07-31 14:37:57 +01:00
Matthew Russell 29e01b6417 chore: remove stray log 2023-07-31 14:35:46 +01:00
Matthew Russell 7032ff1a4d chore: add console rule 2023-07-31 14:35:18 +01:00
Matthew Russell 61572ce170 test: fix incorrect test id for proposals actions 2023-07-31 13:07:58 +01:00
Matthew Russell 5b6be64695 fix: add market-actions to col id check for cell click 2023-07-31 11:50:23 +01:00
Matthew Russell 19fdf3d516 fix: incorrect test ids for actions dropdowns 2023-07-31 11:45:39 +01:00
Matthew Russell 16e2cb96f7 chore: delete network switcher tests now covered by console-test 2023-07-31 11:37:25 +01:00
Matthew Russell 7581b51e0d fix: table dropdown button element and alignment 2023-07-27 13:11:43 +01:00
Matthew Russell b325628acc chore: update text in test for connect button 2023-07-27 13:01:02 +01:00
Matthew Russell 7aa66d733d chore: add testid for network switcher trigger 2023-07-27 12:59:14 +01:00
Matthew Russell bd3dc2c262 fix: default text color 2023-07-27 12:03:01 +01:00
Matthew Russell e6f991b4a9 feat: add group to not show active border in sub nav link 2023-07-27 11:50:56 +01:00
Matthew Russell 730a352f0c feat: add trading dropdown for wallet 2023-07-27 09:51:49 +01:00
Matthew Russell 9a846191cb feat: adjust styles to accomodate fairground 2023-07-27 09:51:49 +01:00
Matthew Russell b31a0bcb11 feat: changes to support testing with sim, delete cypress version 2023-07-27 09:51:49 +01:00
Matthew Russell d56d534669 chore: add missing prop for market selector tests 2023-07-27 09:51:49 +01:00
Matthew Russell 28318c4847 test: add unit test for navbar 2023-07-27 09:51:49 +01:00
Matthew Russell 97499a9adc feat: split navbar into two lists to enable better spacing 2023-07-27 09:51:49 +01:00
Matthew Russell ddff88a446 chore: reduce sidebar width, tidyup comments/classes 2023-07-27 09:51:49 +01:00
Matthew Russell 2b016b473c feat: add external link icons 2023-07-27 09:51:49 +01:00
Matthew Russell e21466973b feat: use trading button tidy mobile buttons 2023-07-27 09:51:49 +01:00
Matthew Russell d28d92a768 chore: remove divider inside network trigger 2023-07-27 09:51:49 +01:00
Matthew Russell e8ca2d5d1b feat: make market selector close on click 2023-07-27 09:51:49 +01:00
Matthew Russell 52f18a6f1d feat: mobile improvements 2023-07-27 09:51:49 +01:00
Matthew Russell 5b4485d1f0 feat: hide header for small screens 2023-07-27 09:51:49 +01:00
Matthew Russell aefdf6e6ef feat: add routes for nav header 2023-07-27 09:51:49 +01:00
Matthew Russell d7cfa3800a feat: refactor navbar for trading 2023-07-27 09:51:49 +01:00
458 changed files with 6968 additions and 15423 deletions
-28
View File
@@ -1,28 +0,0 @@
---
name: Feature Epic
about: A template to capture and scope user requirements, high level process, and basic mockups for an upcoming feature as part of the initial core spec review process.
title: 'FEATURE EPIC: '
labels: feature-epic
---
## Core Feature
<Name>
## Tasks
- [ ] Define high level requirements
- [ ] Create basic mockups
- [ ] Update "API Requirements" in core spec
- [ ] Update "User-Interface Spec" in relevant front end repo
- [ ] Create detailed user stories using normal template
## High Level Requirements
## Basic Mockups
## Link to API Requirements in Core spec
## Link to User Interface Specs
## Linked User Stories
@@ -7,7 +7,7 @@ on:
jobs:
after-release:
runs-on: ubuntu-22.04
timeout-minutes: 45
timeout-minutes: 30
steps:
- name: Checkout
uses: actions/checkout@v3
@@ -30,20 +30,18 @@ jobs:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Wait for publish to complete
uses: lewagon/wait-on-check-action@v1.3.1
with:
ref: ${{ github.event.release.tag_name }}
check-name: '(CD) publish dist / trading'
repo-token: ${{ secrets.GITHUB_TOKEN }}
wait-interval: 10
- name: resolve ipfs hashes for release
run: |
echo "Name: ${{ github.event.release.name }}"
echo "Description: ${{ github.event.release.body }}"
echo "Tag: ${{ github.event.release.tag_name }}"
docker run --rm vegaprotocol/trading:mainnet cat /ipfs-hash > ipfs-hash
commit="$(git rev-list -n 1 ${{ github.event.release.tag_name }})"
echo "Commit: $commit"
until docker pull vegaprotocol/trading:$commit; do
echo "Image not pushed yet, waiting 60 seconds"
sleep 60
done
docker run --rm vegaprotocol/trading:$commit cat /ipfs-hash > ipfs-hash
curl -L https://dist.ipfs.tech/kubo/v0.20.0/kubo_v0.20.0_linux-amd64.tar.gz -o kubo.tgz
tar -xzf kubo.tgz
export PATH="$PATH:$PWD/kubo"
+1 -10
View File
@@ -120,15 +120,6 @@ jobs:
preview_explorer: ${{ env.PREVIEW_EXPLORER }}
preview_tools: ${{ env.PREVIEW_TOOLS }}
console-e2e:
needs: lint-test-build
name: '(CI) console python'
uses: ./.github/workflows/console-test-run.yml
secrets: inherit
if: ${{ contains(fromJSON(needs.lint-test-build.outputs.projects), 'trading') && github.event_name == 'pull_request' }}
with:
github-sha: ${{ github.event.pull_request.head.sha || github.sha }}
cypress:
needs: lint-test-build
name: '(CI) cypress'
@@ -137,7 +128,7 @@ jobs:
secrets: inherit
with:
projects: ${{ needs.lint-test-build.outputs.projects-e2e }}
tags: '@smoke'
tags: '@smoke @regression'
publish-dist:
needs: lint-test-build
+27 -110
View File
@@ -1,138 +1,55 @@
name: (CI) Console tests
name: console-test-run
on:
workflow_call:
inputs:
github-sha:
required: true
type: string
jobs:
run-tests:
name: run-tests
runs-on: 8-cores
timeout-minutes: 20
console-test:
timeout-minutes: 5
runs-on: self-hosted-runner
steps:
#----------------------------------------------
# check-out frontend-monorepo
#----------------------------------------------
- name: Checkout console test repo
uses: actions/checkout@v3
with:
ref: ${{ inputs.github-sha }}
#----------------------------------------------
# cache node modules
#----------------------------------------------
- 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-
#----------------------------------------------
# setup node
#----------------------------------------------
- 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
#----------------------------------------------
# install deps if cache missing
#----------------------------------------------
- name: yarn install
if: steps.cache.outputs.cache-hit != 'true'
run: yarn install --pure-lockfile
#----------------------------------------------
# build trading
#----------------------------------------------
- name: Build affected spec
run: |
yarn env-cmd -f ./apps/trading/.env.stagnet1 yarn nx export trading
#----------------------------------------------
# run trading server
#----------------------------------------------
- name: Run trading server
run: |
docker run -d -p 80:4200 -v $PWD/docker/nginx.conf:/etc/nginx/conf.d/default.conf -v $PWD/dist/apps/trading/exported:/usr/share/nginx/html nginx:1.23-alpine@sha256:6318314189b40e73145a48060bff4783a116c34cc7241532d0d94198fb2c9629
sleep 5
docker ps
#----------------------------------------------
# check if container persists between runs
#----------------------------------------------
- name: Check server
run: |
docker ps
#----------------------------------------------
# check-out tests repo
#----------------------------------------------
- name: Checkout console test repo
uses: actions/checkout@v3
with:
repository: vegaprotocol/console-test
path: './console-test'
#----------------------------------------------
# set-up python
#----------------------------------------------
- name: Set up python
id: setup-python
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.10.11'
#----------------------------------------------
# ----- install & configure poetry -----
#----------------------------------------------
- name: Install Poetry
uses: snok/install-poetry@v1
with:
virtualenvs-create: true
virtualenvs-in-project: true
virtualenvs-path: console-test/.venv
#----------------------------------------------
# load cached venv if cache exists
#----------------------------------------------
- name: Load cached venv
id: cached-poetry-dependencies
uses: actions/cache@v3
with:
path: console-test/.venv
key: venv-${{ runner.os }}-${{ steps.setup-python.outputs.python-version }}-${{ hashFiles('**/poetry.lock') }}
#----------------------------------------------
# install dependencies if cache does not exist
#----------------------------------------------
- name: Install dependencies
run: |
poetry install --no-root
working-directory: ./console-test
if: steps.cached-poetry-dependencies.outputs.cache-hit != 'true'
run: poetry install --no-interaction --no-root
#----------------------------------------------
# install vega binaries
#----------------------------------------------
- name: Install vega binaries
- name: load Binaries
run: |
poetry run python -m vega_sim.tools.load_binaries
working-directory: ./console-test
run: poetry run python -m vega_sim.tools.load_binaries --force
#----------------------------------------------
# install playwright
#----------------------------------------------
- name: pull console
run: |
poetry run docker pull ghcr.io/vegaprotocol/frontend/trading:${{ inputs.github-sha }}
- name: Update container_name in config.py
run: |
sed -i "s/container_name = \".*\"/container_name = \"vegaprotocol\/frontend\/trading:${{ inputs.github-sha }}\"/g" config.py
- name: install playwright
run: poetry run playwright install
working-directory: ./console-test
#----------------------------------------------
# run tests
#----------------------------------------------
- name: Run tests
- name: run tests
run: poetry run pytest --numprocesses auto
working-directory: ./console-test
run: poetry run pytest -s --numprocesses auto
- name: Check files
run: |
ls -al .
ls -al console-test
#----------------------------------------------
# upload traces
#----------------------------------------------
- name: Upload Playwright Trace
uses: actions/upload-artifact@v3
if: always()
+1 -1
View File
@@ -20,7 +20,7 @@ jobs:
project: ${{ fromJSON(inputs.projects) }}
name: ${{ matrix.project }}
runs-on: self-hosted-runner
timeout-minutes: 120
timeout-minutes: 100
steps:
# Checks if skip cache was requested
- name: Set skip-nx-cache flag
+4 -14
View File
@@ -29,12 +29,6 @@ jobs:
echo IS_TESTNET_RELEASE=false >> $GITHUB_ENV
echo IS_IPFS_RELEASE=false >> $GITHUB_ENV
echo IS_S3_RELEASE=false >> $GITHUB_ENV
echo IS_DEV_IMAGE=false >> $GITHUB_ENV
- name: Is dev image
if: ${{ contains(github.ref, 'develop') && github.event_name == 'push' && matrix.app == 'trading' }}
run: |
echo IS_DEV_IMAGE=true >> $GITHUB_ENV
- name: Is PR
if: ${{ github.event_name == 'pull_request' }}
@@ -42,7 +36,7 @@ jobs:
echo IS_PR=true >> $GITHUB_ENV
- name: Is mainnet release
if: ${{ contains(github.ref, 'release/mainnet') && !contains(github.ref, 'mirror') }}
if: ${{ contains(github.ref, 'release/mainnnet') && !contains(github.ref, 'mirror') }}
run: |
echo IS_MAINNET_RELEASE=true >> $GITHUB_ENV
@@ -81,7 +75,7 @@ jobs:
- name: Log in to the Container registry (docker hub)
uses: docker/login-action@v2
if: ${{ env.IS_IPFS_RELEASE == 'true' || env.IS_DEV_IMAGE == 'true' }}
if: ${{ env.IS_IPFS_RELEASE == 'true' }}
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
@@ -124,12 +118,8 @@ jobs:
elif [ "${{ matrix.app }}" = "ui-toolkit" ]; then
NODE_ENV=production yarn nx run ui-toolkit:build-storybook
DIST_LOCATION=dist/storybook/ui-toolkit
elif [ "${{ matrix.app }}" = "static" ]; then
yarn nx build static || (yarn install && yarn nx build static)
else
$envCmd yarn nx build ${{ matrix.app }} || (yarn install && $envCmd yarn nx build ${{ matrix.app }})
fi
if [[ -z "$DIST_LOCATION" ]]; then
DIST_LOCATION=dist/apps/${{ matrix.app }}
fi
mv $DIST_LOCATION dist-result
@@ -179,7 +169,7 @@ jobs:
uses: docker/build-push-action@v3
continue-on-error: true
id: dockerhub-push
if: ${{ env.IS_IPFS_RELEASE == 'true' || env.IS_DEV_IMAGE == 'true' }}
if: ${{ env.IS_IPFS_RELEASE == 'true' }}
with:
context: .
file: docker/node-outside-docker.Dockerfile
@@ -189,7 +179,7 @@ jobs:
ENV_NAME=${{ env.ENV_NAME }}
tags: |
vegaprotocol/${{ matrix.app }}:${{ github.sha }}
vegaprotocol/${{ matrix.app }}:${{ env.IS_MAINNET_RELEASE == 'true' && 'mainnet' || env.IS_TESTNET_RELEASE == 'true' && 'testnet' || env.IS_DEV_IMAGE == 'true' && 'develop' || '' }}
vegaprotocol/${{ matrix.app }}:${{ env.IS_MAINNET_RELEASE == 'true' && 'mainnet' || env.IS_TESTNET_RELEASE == 'true' && 'testnet' || '' }}
- name: Publish dist as docker image (ghcr - retry)
uses: docker/build-push-action@v3
-1
View File
@@ -23,7 +23,6 @@ module.exports = defineConfig({
viewportWidth: 1440,
viewportHeight: 900,
testIsolation: false,
experimentalMemoryManagement: true,
},
env: {
environment: 'CUSTOM',
+6 -50
View File
@@ -1,8 +1,8 @@
import { createSuccessorMarketProposal } from '../support/governance.functions';
context('Market page', { tags: '@regression' }, function () {
describe('Verify elements on page', function () {
const marketHeaders = 'markets-heading';
const createdMarketId =
'2eab0e66545a789047561bc5a2e5cbc3b19eb708da41104e3cac2474ee36c4d4';
before('Create market', function () {
cy.visit('/');
@@ -11,7 +11,7 @@ context('Market page', { tags: '@regression' }, function () {
beforeEach('Get market id', function () {
cy.navigate_to('markets');
cy.get('[col-id="id"]').last().invoke('text').as('createdMarketId');
cy.get('[col-id="id"]').eq(1).invoke('text').as('createdMarketId');
});
it('Market displayed on market page', function () {
@@ -106,7 +106,6 @@ context('Market page', { tags: '@regression' }, function () {
// Able to view Json
cy.contains('View JSON').click();
cy.get('.language-json').should('exist');
cy.getByTestId('icon-cross').click();
});
// Skipping due to resize observer loop limit error
@@ -114,60 +113,17 @@ context('Market page', { tags: '@regression' }, function () {
cy.common_switch_to_mobile_and_click_toggle();
cy.navigate_to('markets', true);
cy.getByTestId(marketHeaders).should('be.visible');
cy.get(`[row-id="${this.createdMarketId}"]`)
cy.get(`[row-id="${createdMarketId}"]`)
.should('be.visible')
.within(() => {
cy.get_element_by_col_id('code').should('have.text', 'TEST.24h');
cy.get_element_by_col_id('name').should('have.text', 'Test market 1');
cy.get_element_by_col_id('state').should('have.text', 'Pending');
cy.get_element_by_col_id('asset').should('have.text', 'fUSDC');
cy.get_element_by_col_id('id').should(
'have.text',
this.createdMarketId
);
cy.get_element_by_col_id('id').should('have.text', createdMarketId);
cy.get_element_by_col_id('actions')
.find('a')
.should('have.attr', 'href', `/markets/${this.createdMarketId}`);
});
});
it('Able to go to market details page for successor market', function () {
const successionLineItem = 'succession-line-item';
const successionLineMarketId = 'succession-line-item-market-id';
createSuccessorMarketProposal(this.createdMarketId);
cy.navigate_to('markets');
cy.reload();
cy.contains('Token test market', { timeout: 8000 }).should('be.visible');
cy.get('[row-index="0"]')
.invoke('attr', 'row-id')
.as('successorMarketId');
cy.contains('Token test market').click();
cy.getByTestId(marketHeaders).should('have.text', 'Token test market');
cy.validate_proposal_change_type('Triggering Ratio', 'Added');
cy.validate_element_from_table('Triggering Ratio', '0.7');
cy.validate_proposal_change_type('Time Window', 'Added');
cy.validate_element_from_table('Time Window', '3,600');
cy.validate_proposal_change_type('Scaling Factor', 'Added');
cy.validate_element_from_table('Scaling Factor', '10');
cy.getByTestId(successionLineItem)
.first()
.within(() => {
cy.contains('Test market 1');
cy.getByTestId(successionLineMarketId).should(
'have.text',
this.createdMarketId
);
});
cy.getByTestId(successionLineItem)
.eq(1)
.within(() => {
cy.contains('Token test market');
cy.getByTestId(successionLineMarketId).should(
'have.text',
this.successorMarketId
);
.should('have.attr', 'href', `/markets/${createdMarketId}`);
});
});
});
@@ -1,49 +0,0 @@
context('Proposal page', { tags: '@smoke' }, function () {
describe('Verify elements on page', function () {
const proposalHeading = 'proposals-heading';
const dateTimeRegex =
/(\d{1,2})\/(\d{1,2})\/(\d{4}), (\d{1,2}):(\d{1,2}):(\d{1,2})/gm;
const proposalTitle = 'Add Lorem Ipsum market';
before('Create market proposal', function () {
cy.visit('/');
cy.createMarket();
});
it('Able to view proposal', function () {
cy.navigate_to('governanceProposals');
cy.getByTestId(proposalHeading).should('be.visible');
cy.contains(proposalTitle)
.parent()
.parent()
.parent()
.within(() => {
cy.get_element_by_col_id('title').should('have.text', proposalTitle);
cy.get_element_by_col_id('type').should('have.text', 'NewMarket');
cy.get_element_by_col_id('state').should('have.text', 'Enacted');
cy.getByTestId('vote-progress').should('be.visible');
cy.get('[col-id="cDate"]')
.invoke('text')
.should('match', dateTimeRegex);
cy.get('[col-id="eDate"]')
.invoke('text')
.should('match', dateTimeRegex);
cy.getByTestId('external-link')
.should('have.attr', 'href')
.and('contains', 'https://governance.fairground.wtf/proposals/');
cy.contains('View terms').should('exist').click();
});
cy.getByTestId('dialog-title').should('have.text', proposalTitle);
cy.get('.language-json').should('exist');
});
it.skip('Proposal page displayed on mobile', function () {
cy.common_switch_to_mobile_and_click_toggle();
cy.navigate_to('governanceProposals', true);
cy.getByTestId(proposalHeading).should('be.visible');
cy.get('[row-index="0"]').within(() => {
cy.get_element_by_col_id('title').should('have.text', proposalTitle);
});
});
});
});
@@ -7,11 +7,7 @@ context('Validator page', { tags: '@smoke' }, function () {
it('should be able to see validator tiles', function () {
cy.getNodes().then((nodes) => {
nodes.forEach((node) => {
if (node.rankingScore.performanceScore > 0) {
cy.get(`[validator-id="${node.id}"]`).should('be.visible');
} else {
cy.get(`[validator-id="${node.id}"]`).should('not.exist');
}
cy.get(`[validator-id="${node.id}"]`).should('be.visible');
});
});
});
@@ -127,10 +127,3 @@ Cypress.Commands.add(
.should('have.text', tableRowValue);
}
);
Cypress.Commands.add(
'validate_proposal_change_type',
(tableRowName, changeType) => {
cy.contains(tableRowName).siblings().should('have.text', changeType);
}
);
@@ -1,130 +0,0 @@
export function createSuccessorMarketProposal(parentMarketId) {
cy.VegaWalletSubmitProposal(getSuccessorTxBody(parentMarketId));
}
function getSuccessorTxBody(parentMarketId) {
return {
proposalSubmission: {
rationale: {
title: 'Test successor market proposal details',
description: 'E2E test for successor market',
},
terms: {
newMarket: {
changes: {
decimalPlaces: '5',
positionDecimalPlaces: '5',
linearSlippageFactor: '0.001',
quadraticSlippageFactor: '0',
lpPriceRange: '10',
instrument: {
name: 'Token test market',
code: 'TEST.24h',
future: {
settlementAsset:
'816af99af60d684502a40824758f6b5377e6af48e50a9ee8ef478ecb879ea8bc',
quoteName: 'fUSDC',
dataSourceSpecForSettlementData: {
external: {
oracle: {
signers: [
{
pubKey: {
key: '70d14a321e02e71992fd115563df765000ccc4775cbe71a0e2f9ff5a3b9dc680',
},
},
],
filters: [
{
key: {
name: 'prices.BTC.value',
type: 'TYPE_INTEGER',
numberDecimalPlaces: '0',
},
conditions: [
{
operator: 'OPERATOR_GREATER_THAN',
value: '0',
},
],
},
],
},
},
},
dataSourceSpecForTradingTermination: {
external: {
oracle: {
signers: [
{
pubKey: {
key: '70d14a321e02e71992fd115563df765000ccc4775cbe71a0e2f9ff5a3b9dc680',
},
},
],
filters: [
{
key: {
name: 'trading.terminated.ETH5',
type: 'TYPE_BOOLEAN',
},
conditions: [
{
operator: 'OPERATOR_EQUALS',
value: 'true',
},
],
},
],
},
},
},
dataSourceSpecBinding: {
settlementDataProperty: 'prices.BTC.value',
tradingTerminationProperty: 'trading.terminated.ETH5',
},
},
},
metadata: [
'sector:food',
'sector:materials',
'source:docs.vega.xyz',
],
priceMonitoringParameters: {
triggers: [
{
horizon: '43200',
probability: '0.9999999',
auctionExtension: '600',
},
],
},
liquidityMonitoringParameters: {
targetStakeParameters: {
timeWindow: '3600',
scalingFactor: 10,
},
triggeringRatio: '0.7',
auctionExtension: '1',
},
logNormal: {
tau: 0.0001140771161,
riskAversionParameter: 0.01,
params: {
mu: 0,
r: 0.016,
sigma: 0.5,
},
},
successor: {
parentMarketId: parentMarketId,
insurancePoolFraction: '0.75',
},
},
},
closingTimestamp: 1695666618,
enactmentTimestamp: 1695666618,
},
},
};
}
+2 -3
View File
@@ -1,4 +1,6 @@
# App configuration variables
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
@@ -6,6 +8,3 @@ NX_VEGA_EXPLORER_URL=/
# App flags
NX_EXPLORER_TXS_LIST=0
NX_TENDERMINT_URL=http://localhost:26617
NX_TENDERMINT_WEBSOCKET_URL=wss://localhost:26617/websocket
+2 -3
View File
@@ -1,4 +1,6 @@
# App configuration variables
NX_TENDERMINT_URL=https://tm.be.devnet1.vega.xyz/
NX_TENDERMINT_WEBSOCKET_URL=wss://be.devnet1.vega.xyz/websocket
NX_VEGA_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/networks-internal/main/devnet1/vegawallet-devnet1.toml
NX_VEGA_ENV=DEVNET
NX_BLOCK_EXPLORER=https://be.devnet1.vega.xyz/rest
@@ -8,6 +10,3 @@ NX_VEGA_URL=https://api.devnet1.vega.xyz/graphql
NX_VEGA_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/networks-internal/main/devnet1/vegawallet-devnet1.toml
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json
NX_VEGA_EXPLORER_URL=/
NX_TENDERMINT_URL=https://tm.be.devnet1.vega.xyz/
NX_TENDERMINT_WEBSOCKET_URL=wss://be.devnet1.vega.xyz/websocket
+2 -3
View File
@@ -1,4 +1,6 @@
# App configuration variables
NX_TENDERMINT_URL=https://be.vega.community
NX_TENDERMINT_WEBSOCKET_URL=wss://be.vega.community/websocket
NX_SENTRY_DSN=https://b3a56b03eda842faad731f3ea9dfd1bc@o286262.ingest.sentry.io/6242427
NX_VEGA_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/networks/master/mainnet1/mainnet1.toml
NX_VEGA_URL=https://api.vega.community/graphql
@@ -9,6 +11,3 @@ NX_VEGA_GOVERNANCE_URL=https://governance.vega.xyz
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/mainnet/announcements.json
NX_VEGA_EXPLORER_URL=https://explorer.vega.xyz/
NX_VEGA_CONSOLE_URL=https://console.vega.xyz
NX_TENDERMINT_URL=https://be.vega.community
NX_TENDERMINT_WEBSOCKET_URL=wss://be.vega.community/websocket
+3 -4
View File
@@ -1,4 +1,6 @@
# App configuration variables
NX_TENDERMINT_URL=https://be.mainnet-mirror.vega.rocks
NX_TENDERMINT_WEBSOCKET_URL=wss://be.mainnet-mirror.vega.rocks/websocket
NX_SENTRY_DSN=https://4b8c8a8ba07742648aa4dfe1b8d17e40@o286262.ingest.sentry.io/5882996
NX_VEGA_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/networks-internal/main/mainnet-mirror/vegawallet-mainnet-mirror.toml
NX_VEGA_URL=https://api.mainnet-mirror.vega.rocks/graphql
@@ -8,7 +10,4 @@ NX_ETHERSCAN_URL=https://sepolia.etherscan.io
NX_VEGA_GOVERNANCE_URL=https://governance.mainnet-mirror.vega.rocks
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/mainnet/announcements.json
NX_VEGA_EXPLORER_URL=https://explorer.mainnet-mirror.vega.rocks/
NX_VEGA_CONSOLE_URL=https://console.mainnet-mirror.vega.rocks
NX_TENDERMINT_URL=https://be.mainnet-mirror.vega.rocks
NX_TENDERMINT_WEBSOCKET_URL=wss://be.mainnet-mirror.vega.rocks/websocket
NX_VEGA_CONSOLE_URL=https://console.mainnet-mirror.vega.rocks
+2 -3
View File
@@ -1,5 +1,7 @@
# App configuration variables
NX_TENDERMINT_URL=https://tm.be.testnet.vega.xyz
NX_BLOCK_EXPLORER=https://be.testnet.vega.xyz/rest
NX_TENDERMINT_WEBSOCKET_URL=wss://be.testnet.vega.xyz/websocket
NX_VEGA_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/networks-internal/main/fairground/vegawallet-fairground.toml
NX_VEGA_ENV=TESTNET
NX_VEGA_URL=https://api.n07.testnet.vega.xyz/graphql
@@ -9,6 +11,3 @@ NX_VEGA_GOVERNANCE_URL=https://governance.fairground.wtf
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json
NX_VEGA_EXPLORER_URL=https://explorer.fairground.wtf
NX_VEGA_CONSOLE_URL=https://console.fairground.wtf
NX_TENDERMINT_URL=https://tm.be.testnet.vega.xyz
NX_TENDERMINT_WEBSOCKET_URL=wss://be.testnet.vega.xyz/websocket
+2 -3
View File
@@ -7,10 +7,9 @@ NX_ETHEREUM_PROVIDER_URL=https://sepolia.infura.io/v3/4f846e79e13f44d1b51bbd7ed9
NX_ETHERSCAN_URL=https://sepolia.etherscan.io
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json
NX_TENDERMINT_URL=https://tm.be.validators-testnet.vega.rocks
NX_BLOCK_EXPLORER=https://be.validators-testnet.vega.rocks/rest
NX_TENDERMINT_WEBSOCKET_URL=wss://be.validators-testnet.vega.xyz/websocket
NX_VEGA_GOVERNANCE_URL=https://governance.validators-testnet.vega.rocks
NX_VEGA_EXPLORER_URL=https://explorer.validators-testnet.vega.rocks/
NX_VEGA_CONSOLE_URL=https://trading.validators-testnet.vega.rocks/
NX_TENDERMINT_URL=https://tm.be.validators-testnet.vega.rocks
NX_TENDERMINT_WEBSOCKET_URL=wss://be.validators-testnet.vega.xyz/websocket
@@ -23,7 +23,7 @@ export const Footer = () => {
);
return (
<footer className="grid grid-cols-[1fr_auto] items-center text-xs md:text-md lg:flex md:col-span-2 px-4 pt-2 pb-3 gap-4 border-t border-vega-light-200 dark:border-vega-dark-200">
<footer className="grid grid-rows-2 lg:grid-cols-[1fr_auto] text-xs md:text-md lg:flex md:col-span-2 px-4 py-2 gap-4 border-t border-vega-light-200 dark:border-vega-dark-200">
<div className="flex justify-between gap-2 align-middle">
{GIT_COMMIT_HASH && (
<div className="content-center flex border-r border-neutral-700 dark:border-neutral-300 pr-4">
@@ -43,12 +43,9 @@ export const Footer = () => {
</div>
)}
<div className="content-center flex pr-4 md:border-r border-neutral-700 dark:border-neutral-300">
<span className="pr-2">{VEGA_URL && <NodeUrl url={VEGA_URL} />}</span>
<Link
className="ml-2 underline-offset-4"
onClick={() => setNodeSwitcherOpen(true)}
>
<div className="content-center flex pl-2 md:border-r border-neutral-700 dark:border-neutral-300 pr-4">
{VEGA_URL && <NodeUrl url={VEGA_URL} />}
<Link className="ml-2" onClick={() => setNodeSwitcherOpen(true)}>
{t('Change')}
</Link>
</div>
@@ -62,10 +59,7 @@ export const Footer = () => {
) : null}
</div>
<div className="pl-2 align-center lg:align-right lg:flex lg:justify-end gap-2 align-middle lg:max-w-xs lg:ml-auto">
<RouteLink
to={`/${Routes.DISCLAIMER}`}
className="underline underline-offset-4"
>
<RouteLink to={`/${Routes.DISCLAIMER}`} className="underline">
Disclaimer
</RouteLink>
</div>
@@ -1,9 +1,6 @@
import { t } from '@vegaprotocol/i18n';
import type { MarketInfoWithData } from '@vegaprotocol/markets';
import {
PriceMonitoringBoundsInfoPanel,
SuccessionLineInfoPanel,
} from '@vegaprotocol/markets';
import { PriceMonitoringBoundsInfoPanel } from '@vegaprotocol/markets';
import {
LiquidityInfoPanel,
LiquidityMonitoringParametersInfoPanel,
@@ -106,8 +103,6 @@ export const MarketDetails = ({ market }: { market: MarketInfoWithData }) => {
<OracleInfoPanel market={market} type="settlementData" />
</>
)}
<h2 className={`${headerClassName} mb-4`}>{t('Succession line')}</h2>
<SuccessionLineInfoPanel market={market} />
</div>
);
};
@@ -10,8 +10,6 @@ import Hash from '../../links/hash';
import { t } from '@vegaprotocol/i18n';
import { ProposalSignatureBundleNewAsset } from './proposal/signature-bundle-new';
import { ProposalSignatureBundleUpdateAsset } from './proposal/signature-bundle-update';
import { MarketLink } from '../../links';
import { formatNumber } from '@vegaprotocol/utils';
export type Proposal = components['schemas']['v1ProposalSubmission'];
export type ProposalTerms = components['schemas']['vegaProposalTerms'];
@@ -55,11 +53,7 @@ export function proposalTypeLabel(terms?: ProposalTerms): string {
} else if (has(terms, 'updateAsset')) {
return t('Update asset proposal');
} else if (has(terms, 'newMarket')) {
if (terms?.newMarket?.changes?.successor) {
return t('Successor market proposal');
} else {
return t('New market proposal');
}
return t('New market proposal');
} else if (has(terms, 'updateMarket')) {
return t('Update market proposal');
} else if (has(terms, 'updateNetworkParameter')) {
@@ -91,13 +85,6 @@ export const TxProposal = ({ txData, pubKey, blockData }: TxProposalProps) => {
}
const tx = proposal.terms?.newAsset || proposal.terms?.updateAsset;
const isSuccessorMarketProposal =
proposal?.terms?.newMarket?.changes?.successor;
const parentMarketId =
isSuccessorMarketProposal &&
proposal?.terms.newMarket?.changes?.successor?.parentMarketId;
const insurancePoolFraction =
proposal?.terms?.newMarket?.changes?.successor?.insurancePoolFraction;
// This component is not rendered if no bundle is required
const SignatureBundleComponent = proposal.terms?.newAsset
@@ -124,30 +111,6 @@ export const TxProposal = ({ txData, pubKey, blockData }: TxProposalProps) => {
<Hash text={deterministicId} />
</TableCell>
</TableRow>
{isSuccessorMarketProposal ? (
<>
{parentMarketId ? (
<TableRow modifier="bordered">
<TableCell>{t('Previous market')}</TableCell>
<TableCell>
<MarketLink
id={
proposal.terms?.newMarket.changes.successor.parentMarketId
}
/>
</TableCell>
</TableRow>
) : null}
{insurancePoolFraction ? (
<TableRow modifier="bordered">
<TableCell>{t('Insurance pool fraction')}</TableCell>
<TableCell>
{formatNumber(Number(insurancePoolFraction) * 100, 0)}%
</TableCell>
</TableRow>
) : null}
</>
) : null}
</TableWithTbody>
<ProposalSummary
id={deterministicId}
@@ -10,6 +10,7 @@ import {
DropdownMenuTrigger,
DropdownMenuSubContent,
Icon,
Button,
} from '@vegaprotocol/ui-toolkit';
import type { Dispatch, SetStateAction } from 'react';
import { FilterLabel } from './tx-filter-label';
@@ -99,13 +100,15 @@ export const TxsFilter = ({ filters, setFilters }: TxFilterProps) => {
<DropdownMenu
modal={false}
trigger={
<DropdownMenuTrigger>
<FilterLabel filters={filters} />
<DropdownMenuTrigger className="ml-0">
<Button size="xs" data-testid="filter-trigger">
<FilterLabel filters={filters} />
</Button>
</DropdownMenuTrigger>
}
>
<DropdownMenuContent>
{filters.size > 1 ? null : (
{filters.size > 0 ? null : (
<>
<DropdownMenuCheckboxItem
onCheckedChange={() => setFilters(new Set(AllFilterOptions))}
@@ -44,7 +44,7 @@ export const TxsListNavigation = ({
</Button>
<Button
size="xs"
disabled={!hasMoreTxs}
disabled={!hasMoreTxs || loading}
onClick={() => {
nextPage();
}}
@@ -78,11 +78,7 @@ export function getLabelForProposal(
} else if (proposal.terms?.updateAsset) {
return t('Proposal: Update asset');
} else if (proposal.terms?.newMarket) {
if (proposal.terms?.newMarket.changes?.successor) {
return t('Proposal: Successor market');
} else {
return t('Proposal: New market');
}
return t('Proposal: New market');
} else if (proposal.terms?.updateMarket) {
return t('Proposal: Update market');
} else if (proposal.terms?.updateNetworkParameter) {
@@ -86,18 +86,14 @@ describe('Txs infinite list item', () => {
render(
<MockedProvider>
<MemoryRouter>
<table>
<tbody>
<TxsInfiniteListItem
type="testType"
submitter="testPubKey"
hash="testTxHash"
block="1"
code={0}
command={{}}
/>
</tbody>
</table>
<TxsInfiniteListItem
type="testType"
submitter="testPubKey"
hash="testTxHash"
block="1"
code={0}
command={{}}
/>
</MemoryRouter>
</MockedProvider>
);
@@ -14,9 +14,9 @@ const DEFAULT_TRUNCATE_LENGTH = 7;
export function getIdTruncateLength(screen: Screen): number {
if (['xxxl', 'xxl'].includes(screen)) {
return 32;
return 64;
} else if (['xl', 'lg', 'md'].includes(screen)) {
return 16;
return 32;
}
return DEFAULT_TRUNCATE_LENGTH;
}
@@ -5,17 +5,11 @@ import type { BlockExplorerTransactionResult } from '../../routes/types/block-ex
import { Side } from '@vegaprotocol/types';
import { MockedProvider } from '@apollo/client/testing';
const generateHash = (): string =>
Array.from(
{ length: 64 },
() => '0123456789ABCDEF'[Math.floor(Math.random() * 16)]
).join('');
const generateTxs = (number: number): BlockExplorerTransactionResult[] => {
return Array.from(Array(number)).map((_) => ({
block: '87901',
index: 2,
hash: generateHash(),
hash: '0F8B98DA0923A50786B852D9CA11E051CACC4C733E1DB93D535C7D81DBD10F6F',
submitter:
'4b782482f587d291e8614219eb9a5ee9280fa2c58982dee71d976782a9be1964',
type: 'Submit Order',
@@ -2,7 +2,7 @@ import { Table, TableRow } from '../table';
import { t } from '@vegaprotocol/i18n';
import { useFetch } from '@vegaprotocol/react-helpers';
import type { BlockExplorerTransactions } from '../../routes/types/block-explorer-response';
import { getTxsDataUrl } from '../../hooks/get-txs-data-url';
import { getTxsDataUrl } from '../../hooks/use-txs-data';
import { AsyncRenderer, Loader } from '@vegaprotocol/ui-toolkit';
import EmptyList from '../empty-list/empty-list';
import { TxsInfiniteListItem } from './txs-infinite-list-item';
@@ -14,7 +14,7 @@ interface TxsPerBlockProps {
export const TxsPerBlock = ({ blockHeight, txCount }: TxsPerBlockProps) => {
const filters = `filters[block.height]=${blockHeight}`;
const url = getTxsDataUrl({ filters, count: txCount });
const url = getTxsDataUrl({ limit: txCount.toString(), filters });
const {
state: { data, loading, error },
} = useFetch<BlockExplorerTransactions>(url);
@@ -1,59 +0,0 @@
import { DATA_SOURCES } from '../config';
type IGetTxsDataFirstPage = {
baseUrl?: string;
count?: number;
party?: string;
filters?: string;
};
interface IGetTxsDataPrevious extends IGetTxsDataFirstPage {
before: string;
}
interface IGetTxsDataNext extends IGetTxsDataFirstPage {
after: string;
}
type IGetTxsDataUrl =
| IGetTxsDataPrevious
| IGetTxsDataNext
| IGetTxsDataFirstPage;
export const BE_TXS_PER_REQUEST = 25;
/**
* Properly encodes the filters and parameters for a request to the block explorer
* API for transactions. As the API uses a slightly less common format for encoding
* filters, some of it is more manual than you might expect.
*
* @param params An object containing the pagination and filters
* @returns string URL to call
*/
export const getTxsDataUrl = (params: IGetTxsDataUrl) => {
const baseUrl =
params.baseUrl || `${DATA_SOURCES.blockExplorerUrl}/transactions`;
const url = new URL(baseUrl);
const count = `${params.count || BE_TXS_PER_REQUEST}`;
if ('before' in params && params.before?.length > 0) {
url.searchParams.append('last', count);
url.searchParams.append('before', params.before);
} else if ('after' in params && params.after?.length > 0) {
url.searchParams.append('first', count);
url.searchParams.append('after', params.after);
} else {
url.searchParams.append('first', count);
}
// Hacky fix for param as array
let urlAsString = url.toString();
if (params.filters && params.filters?.length > 0) {
urlAsString += '&' + params.filters.replaceAll(' ', '%20');
}
if (params.party && params.party?.length > 0) {
urlAsString += `&filters[tx.submitter]=${params.party}`;
}
return urlAsString;
};
@@ -1,48 +0,0 @@
import { getTxsDataUrl } from './get-txs-data-url'; // import the function to be tested
describe('getTxsDataUrl', () => {
it('should return the correct URL without filters and party', () => {
const params = {
count: 10,
baseUrl: 'https://example.com/transactions',
};
const expectedUrl = 'https://example.com/transactions?first=10';
expect(getTxsDataUrl(params)).toEqual(expectedUrl);
});
it('should return the correct URL with "before" in params', () => {
const params = {
count: 5,
before: '100.1',
baseUrl: 'https://example.com/transactions',
};
const expectedUrl = 'https://example.com/transactions?last=5&before=100.1';
expect(getTxsDataUrl(params)).toEqual(expectedUrl);
});
it('should return the correct URL with "after" in params', () => {
const params = {
count: 5,
after: '222.1',
baseUrl: 'https://example.com/transactions',
};
const expectedUrl = 'https://example.com/transactions?first=5&after=222.1';
expect(getTxsDataUrl(params)).toEqual(expectedUrl);
});
it('should return the correct URL with filters and party', () => {
const params = {
count: 10,
filters: 'filters[cmd.type]=Made Up Transaction',
party: '1234',
baseUrl: 'https://example.com/transactions',
};
const expectedUrl =
'https://example.com/transactions?first=10&filters[cmd.type]=Made%20Up%20Transaction&filters[tx.submitter]=1234';
expect(getTxsDataUrl(params)).toEqual(expectedUrl);
});
});
+92 -103
View File
@@ -1,141 +1,130 @@
import { useSearchParams } from 'react-router-dom';
import type { URLSearchParamsInit } from 'react-router-dom';
import { useCallback } from 'react';
import { useCallback, useEffect, useState } from 'react';
import { useFetch } from '@vegaprotocol/react-helpers';
import type {
BlockExplorerTransactionResult,
BlockExplorerTransactions,
} from '../routes/types/block-explorer-response';
import { DATA_SOURCES } from '../config';
import isNumber from 'lodash/isNumber';
import { AllFilterOptions } from '../components/txs/tx-filter';
import type { FilterOption } from '../components/txs/tx-filter';
import { BE_TXS_PER_REQUEST, getTxsDataUrl } from './get-txs-data-url';
export function getTypeFilters(filters?: Set<FilterOption>) {
if (!filters) {
return '';
} else if (filters.size > 1) {
return '';
}
const forcedSingleFilter = Array.from(filters)[0];
return `filters[cmd.type]=${forcedSingleFilter}`;
export interface TxsStateProps {
txsData: BlockExplorerTransactionResult[];
hasMoreTxs: boolean;
cursor: string;
previousCursors: string[];
hasPreviousPage: boolean;
}
export interface IUseTxsData {
count?: number;
before?: string;
after?: string;
party?: string;
filters?: Set<FilterOption>;
limit: number;
filters?: string;
}
export const useTxsData = ({
count = 25,
before,
after,
filters,
party,
}: IUseTxsData) => {
const [, setSearchParams] = useSearchParams();
let hasMoreTxs = true;
let txsData: BlockExplorerTransactionResult[] = [];
interface IGetTxsDataUrl {
limit: string;
filters?: string;
}
const url = getTxsDataUrl({
filters: getTypeFilters(filters),
count,
before,
after,
party,
export const getTxsDataUrl = ({ limit, filters }: IGetTxsDataUrl) => {
const url = new URL(`${DATA_SOURCES.blockExplorerUrl}/transactions`);
if (limit) {
url.searchParams.append('limit', limit);
}
// Hacky fix for param as array
let urlAsString = url.toString();
if (filters) {
urlAsString += '&' + filters.replace(' ', '%20');
}
return urlAsString;
};
export const useTxsData = ({ limit, filters }: IUseTxsData) => {
const [
{ txsData, hasMoreTxs, cursor, previousCursors, hasPreviousPage },
setTxsState,
] = useState<TxsStateProps>({
txsData: [],
hasMoreTxs: false,
previousCursors: [],
cursor: '',
hasPreviousPage: false,
});
const url = getTxsDataUrl({ limit: limit.toString(), filters });
const {
state: { data, error, loading },
refetch,
} = useFetch<BlockExplorerTransactions>(url, {}, true);
if (!loading && data && isNumber(data.transactions.length)) {
hasMoreTxs = data.transactions.length >= count;
txsData = data.transactions;
}
useEffect(() => {
if (!loading && data && isNumber(data.transactions.length)) {
setTxsState((prev) => {
return {
...prev,
txsData: data.transactions,
hasMoreTxs: data.transactions.length >= limit,
cursor: data?.transactions.at(-1)?.cursor || '',
};
});
}
}, [loading, setTxsState, data, limit]);
const nextPage = useCallback(() => {
const after = data?.transactions.at(-1)?.cursor || '';
const params: URLSearchParamsInit = { after };
if (filters) {
params.filters = Array.from(filters).join(',');
}
setSearchParams(params);
}, [filters, data, setSearchParams]);
const c = data?.transactions.at(0)?.cursor;
const newPreviousCursors = c ? [...previousCursors, c] : previousCursors;
setTxsState((prev) => ({
...prev,
hasPreviousPage: true,
previousCursors: newPreviousCursors,
}));
return refetch({
limit,
before: cursor,
});
}, [data, previousCursors, cursor, limit, refetch]);
const previousPage = useCallback(() => {
const before = data?.transactions[0]?.cursor || '';
const params: URLSearchParamsInit = { before };
if (filters && filters.size > 0 && filters.size === 1) {
params.filters = Array.from(filters)[0];
}
setSearchParams(params);
}, [filters, data, setSearchParams]);
const previousCursor = [...previousCursors].pop();
const newPreviousCursors = previousCursors.slice(0, -1);
setTxsState((prev) => ({
...prev,
hasPreviousPage: newPreviousCursors.length > 0,
previousCursors: newPreviousCursors,
}));
return refetch({
limit,
before: previousCursor,
});
}, [previousCursors, limit, refetch]);
const refreshTxs = useCallback(async () => {
const params: URLSearchParamsInit = {};
if (filters && filters.size > 0 && filters.size === 1) {
params.filters = Array.from(filters)[0];
}
setSearchParams(params);
setTxsState(() => ({
txsData: [],
cursor: '',
previousCursors: [],
hasMoreTxs: false,
hasPreviousPage: false,
}));
refetch({ count: BE_TXS_PER_REQUEST });
}, [setSearchParams, refetch, filters]);
const updateFilters = useCallback(
(newFilters: Set<FilterOption>) => {
const params: URLSearchParamsInit = {};
if (newFilters && newFilters.size === 1) {
params.filters = Array.from(newFilters)[0];
}
setSearchParams(params);
},
[setSearchParams]
);
refetch({ limit });
}, [setTxsState, limit, refetch, filters]); // eslint-disable-line react-hooks/exhaustive-deps
return {
updateFilters,
txsData,
loading,
error,
hasMoreTxs,
hasPreviousPage,
previousCursors,
cursor,
refreshTxs,
nextPage,
previousPage,
};
};
/**
* Returns a Set of filters based on the URLSearchParams, or
* defaults to all.
* @param params
* @returns Set
*/
export function getInitialFilters(params: URLSearchParams): Set<FilterOption> {
const defaultFilters = new Set(AllFilterOptions);
const p = params.get('filters');
if (!p) {
return defaultFilters;
}
const filters = new Set<FilterOption>();
p.split(',').forEach((f) => {
if (AllFilterOptions.includes(f as FilterOption)) {
filters.add(f as FilterOption);
}
});
if (filters.size === 0) {
return defaultFilters;
}
return filters;
}
@@ -204,7 +204,6 @@ describe('Block', () => {
(useFetch as jest.Mock).mockReturnValue({
state: { data: createBlockResponse(1), loading: false, error: null },
});
render(renderComponent(1));
await waitFor(() => screen.getByTestId('block-header'));
expect(screen.getByTestId('previous-block-button')).toHaveAttribute(
-9
View File
@@ -22,11 +22,6 @@ import { Footer } from '../components/footer/footer';
import { Header } from '../components/header';
import { Routes } from './route-names';
import { useExplorerNodeNamesLazyQuery } from './validators/__generated__/NodeNames';
import {
ProtocolUpgradeCountdownMode,
ProtocolUpgradeInProgressNotification,
ProtocolUpgradeProposalNotification,
} from '@vegaprotocol/proposals';
const DialogsContainer = () => {
const { isOpen, id, trigger, asJson, setOpen } = useAssetDetailsDialogStore();
@@ -67,10 +62,6 @@ export const Layout = () => {
/>
)}
<Header />
<ProtocolUpgradeProposalNotification
mode={ProtocolUpgradeCountdownMode.IN_ESTIMATED_TIME_REMAINING}
/>
<ProtocolUpgradeInProgressNotification />
</div>
<div className={fixedWidthClasses}>
<main className="p-4">
@@ -15,13 +15,9 @@ import { PartyBlockAccounts } from './components/party-block-accounts';
import { isValidPartyId } from './components/party-id-error';
import { useDataProvider } from '@vegaprotocol/data-provider';
import { TxsListNavigation } from '../../../components/txs/tx-list-navigation';
import type { FilterOption } from '../../../components/txs/tx-filter';
import { AllFilterOptions, TxsFilter } from '../../../components/txs/tx-filter';
import { useSearchParams } from 'react-router-dom';
const Party = () => {
const [params] = useSearchParams();
const [filters, setFilters] = useState(new Set(AllFilterOptions));
const { party } = useParams<{ party: string }>();
@@ -31,21 +27,24 @@ const Party = () => {
const partyId = toNonHex(party ? party : '');
const { isMobile } = useScreenDimensions();
const visibleChars = useMemo(() => (isMobile ? 10 : 14), [isMobile]);
const baseFilters = `filters[tx.submitter]=${partyId}`;
const f =
filters && filters.size === 1
? `${baseFilters}&filters[cmd.type]=${Array.from(filters)[0]}`
: baseFilters;
const {
hasMoreTxs,
nextPage,
refreshTxs,
previousPage,
error,
refreshTxs,
loading,
txsData,
hasMoreTxs,
updateFilters,
hasPreviousPage,
} = useTxsData({
filters: filters.size === 1 ? filters : undefined,
before: params.get('before') || undefined,
after: !params.get('before') ? params.get('after') || undefined : undefined,
party: partyId,
limit: 25,
filters: f,
});
const variables = useMemo(() => ({ partyId }), [partyId]);
@@ -103,21 +102,15 @@ const Party = () => {
refreshTxs={refreshTxs}
nextPage={nextPage}
previousPage={previousPage}
hasPreviousPage={true}
hasPreviousPage={hasPreviousPage}
loading={loading}
hasMoreTxs={hasMoreTxs}
>
<TxsFilter
filters={filters}
setFilters={(f) => {
setFilters(f);
updateFilters(f as Set<FilterOption>);
}}
/>
<TxsFilter filters={filters} setFilters={setFilters} />
</TxsListNavigation>
{!error && txsData ? (
<TxsInfiniteList
hasMoreTxs={true}
hasMoreTxs={hasMoreTxs}
areTxsLoading={loading}
txs={txsData}
loadMoreTxs={nextPage}
+16 -19
View File
@@ -1,14 +1,14 @@
import { t } from '@vegaprotocol/i18n';
import { RouteTitle } from '../../../components/route-title';
import { TxsInfiniteList } from '../../../components/txs';
import { useTxsData, getInitialFilters } from '../../../hooks/use-txs-data';
import { useTxsData } from '../../../hooks/use-txs-data';
import { useDocumentTitle } from '../../../hooks/use-document-title';
import { useState } from 'react';
import { TxsFilter } from '../../../components/txs/tx-filter';
import type { FilterOption } from '../../../components/txs/tx-filter';
import { AllFilterOptions, TxsFilter } from '../../../components/txs/tx-filter';
import { TxsListNavigation } from '../../../components/txs/tx-list-navigation';
import { useSearchParams } from 'react-router-dom';
const BE_TXS_PER_REQUEST = 25;
export const TxsList = () => {
useDocumentTitle(['Transactions']);
@@ -27,22 +27,25 @@ export const TxsList = () => {
* @returns {JSX.Element} Transaction List and controls
*/
export const TxsListFiltered = () => {
const [params] = useSearchParams();
const [filters, setFilters] = useState(getInitialFilters(params));
const [filters, setFilters] = useState(new Set(AllFilterOptions));
const f =
filters && filters.size === 1
? `filters[cmd.type]=${Array.from(filters)[0]}`
: '';
const {
hasMoreTxs,
nextPage,
previousPage,
error,
refreshTxs,
loading,
txsData,
hasMoreTxs,
updateFilters,
hasPreviousPage,
} = useTxsData({
filters: filters.size === 1 ? filters : undefined,
before: params.get('before') || undefined,
after: !params.get('before') ? params.get('after') || undefined : undefined,
limit: BE_TXS_PER_REQUEST,
filters: f,
});
return (
@@ -51,17 +54,11 @@ export const TxsListFiltered = () => {
refreshTxs={refreshTxs}
nextPage={nextPage}
previousPage={previousPage}
hasPreviousPage={true}
hasPreviousPage={hasPreviousPage}
loading={loading}
hasMoreTxs={hasMoreTxs}
>
<TxsFilter
filters={filters}
setFilters={(f) => {
setFilters(f);
updateFilters(f as Set<FilterOption>);
}}
/>
<TxsFilter filters={filters} setFilters={setFilters} />
</TxsListNavigation>
<TxsInfiniteList
hasFilters={filters.size > 0}
@@ -15,9 +15,6 @@ query ExplorerNodes {
stakedByDelegates
stakedTotal
pendingStake
rankingScore {
performanceScore
}
epochData {
total
offline
@@ -6,7 +6,7 @@ const defaultOptions = {} as const;
export type ExplorerNodesQueryVariables = Types.Exact<{ [key: string]: never; }>;
export type ExplorerNodesQuery = { __typename?: 'Query', nodesConnection: { __typename?: 'NodesConnection', edges?: Array<{ __typename?: 'NodeEdge', node: { __typename?: 'Node', id: string, name: string, infoUrl: string, avatarUrl?: string | null, pubkey: string, tmPubkey: string, ethereumAddress: string, location: string, status: Types.NodeStatus, stakedByOperator: string, stakedByDelegates: string, stakedTotal: string, pendingStake: string, rankingScore: { __typename?: 'RankingScore', performanceScore: string }, epochData?: { __typename?: 'EpochData', total: number, offline: number, online: number } | null } } | null> | null } };
export type ExplorerNodesQuery = { __typename?: 'Query', nodesConnection: { __typename?: 'NodesConnection', edges?: Array<{ __typename?: 'NodeEdge', node: { __typename?: 'Node', id: string, name: string, infoUrl: string, avatarUrl?: string | null, pubkey: string, tmPubkey: string, ethereumAddress: string, location: string, status: Types.NodeStatus, stakedByOperator: string, stakedByDelegates: string, stakedTotal: string, pendingStake: string, epochData?: { __typename?: 'EpochData', total: number, offline: number, online: number } | null } } | null> | null } };
export const ExplorerNodesDocument = gql`
@@ -27,9 +27,6 @@ export const ExplorerNodesDocument = gql`
stakedByDelegates
stakedTotal
pendingStake
rankingScore {
performanceScore
}
epochData {
total
offline
@@ -91,15 +91,7 @@ export const ValidatorsPage = () => {
const { data: tmData } = useTendermintValidators(5000);
const { data, loading, error, refetch } = useExplorerNodesQuery();
const validators = compact(
data?.nodesConnection.edges
?.map((e) => e?.node)
.filter(
(node) =>
node?.rankingScore?.performanceScore &&
new BigNumber(node.rankingScore.performanceScore).isGreaterThan(0)
)
);
const validators = compact(data?.nodesConnection.edges?.map((e) => e?.node));
// voting power
const powers = compact(tmData?.result.validators).map(
Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 547 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

-20
View File
@@ -1,20 +0,0 @@
{
"short_name": "Mainnet Stats",
"name": "Vega Mainnet statistics",
"icons": [
{
"src": "favicon.ico",
"sizes": "64x64 32x32 24x24 16x16",
"type": "image/x-icon"
},
{
"src": "logo192.png",
"type": "image/png",
"sizes": "192x192"
}
],
"start_url": ".",
"display": "standalone",
"theme_color": "#000000",
"background_color": "#ffffff"
}
-3
View File
@@ -1,3 +0,0 @@
# https://www.robotstxt.org/robotstxt.html
User-agent: *
Disallow:
+10 -26
View File
@@ -1,39 +1,23 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta charset="utf-8" />
<link
rel="icon"
type="image/x-icon"
href="https://static.vega.xyz/favicon.ico"
/>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta name="description" content="Vega Protocol - VEGA Explorer" />
<meta name="og:type" content="website" />
<meta name="og:url" content="https://explorer.vega.xyz/" />
<meta name="og:title" content="Vega Protocol - Explorer" />
<meta name="og:site_name" content="Vega Protocol - Explorer" />
<meta name="og:image" content="https://static.vega.xyz/favicon.ico" />
<meta name="twitter:card" content="https://static.vega.xyz/favicon.ico" />
<meta name="twitter:title" content="Vega Protocol - Explorer" />
<meta name="twitter:description" content="Vega Protocol - Explorer" />
<meta name="twitter:image" content="https://static.vega.xyz/favicon.ico" />
<meta name="twitter:image:alt" content="VEGA logo" />
<meta name="twitter:site" content="@vegaprotocol" />
<meta name="description" content="Vega Protocol - Explorer" />
<link rel="apple-touch-icon" href="assets/apple-touch-icon.png" />
<link rel="manifest" href="assets/manifest.json" />
<title>Explorer</title>
<base href="/" />
<link
rel="preload"
href="https://static.vega.xyz/AlphaLyrae-Medium.woff2"
as="font"
type="font/woff2"
crossorigin="anonymous"
/>
<title>VEGA Explorer</title>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link
rel="icon"
type="image/x-icon"
href="https://static.vega.xyz/favicon.ico"
/>
<link rel="stylesheet" href="https://static.vega.xyz/fonts.css" />
<script src="./assets/env-config.js"></script>
</head>
<body class="dark:bg-black h-full w-full">
-1
View File
@@ -18,7 +18,6 @@ NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/annou
NX_GITHUB_FEEDBACK_URL=https://github.com/vegaprotocol/feedback/discussions
NX_WALLETCONNECT_PROJECT_ID=fe8091dc35738863e509fc4947525c72
NX_VEGA_REST_URL=http://localhost:3008/api/v2/
NX_SUCCESSOR_MARKETS=true
#Test configuration variables
CYPRESS_FAIRGROUND=false
-1
View File
@@ -28,7 +28,6 @@ module.exports = defineConfig({
numTestsKeptInMemory: 5,
downloadsFolder: 'cypress/downloads',
testIsolation: false,
experimentalMemoryManagement: true,
},
env: {
ethProviderUrl: 'http://localhost:8545/',
@@ -1,107 +0,0 @@
{
"changes": {
"decimalPlaces": "5",
"positionDecimalPlaces": "5",
"linearSlippageFactor": "0.001",
"quadraticSlippageFactor": "0",
"lpPriceRange": "10",
"instrument": {
"name": "Token test market",
"code": "Token.24h",
"future": {
"settlementAsset": "816af99af60d684502a40824758f6b5377e6af48e50a9ee8ef478ecb879ea8bc",
"quoteName": "fUSDC",
"dataSourceSpecForSettlementData": {
"external": {
"oracle": {
"signers": [
{
"pubKey": {
"key": "70d14a321e02e71992fd115563df765000ccc4775cbe71a0e2f9ff5a3b9dc680"
}
}
],
"filters": [
{
"key": {
"name": "prices.BTC.value",
"type": "TYPE_INTEGER",
"numberDecimalPlaces": "0"
},
"conditions": [
{
"operator": "OPERATOR_GREATER_THAN",
"value": "0"
}
]
}
]
}
}
},
"dataSourceSpecForTradingTermination": {
"external": {
"oracle": {
"signers": [
{
"pubKey": {
"key": "70d14a321e02e71992fd115563df765000ccc4775cbe71a0e2f9ff5a3b9dc680"
}
}
],
"filters": [
{
"key": {
"name": "trading.terminated.ETH5",
"type": "TYPE_BOOLEAN"
},
"conditions": [
{
"operator": "OPERATOR_EQUALS",
"value": "true"
}
]
}
]
}
}
},
"dataSourceSpecBinding": {
"settlementDataProperty": "prices.BTC.value",
"tradingTerminationProperty": "trading.terminated.ETH5"
}
}
},
"metadata": ["sector:food", "sector:materials", "source:docs.vega.xyz"],
"priceMonitoringParameters": {
"triggers": [
{
"horizon": "43200",
"probability": "0.9999999",
"auctionExtension": "600"
}
]
},
"liquidityMonitoringParameters": {
"targetStakeParameters": {
"timeWindow": "3600",
"scalingFactor": 10
},
"triggeringRatio": "0.7",
"auctionExtension": "1"
},
"logNormal": {
"tau": 0.0001140771161,
"riskAversionParameter": 0.001,
"params": {
"mu": 0,
"r": 0.016,
"sigma": 0.8
}
},
"successor": {
"parentMarketId": "",
"insurancePoolFraction": "0.75"
}
}
}
@@ -11,9 +11,7 @@ import {
getDateFormatForSpecifiedDays,
getProposalFromTitle,
getProposalInformationFromTable,
proposalChangeType,
submitUniqueRawProposal,
validateProposalDetailsDiff,
voteForProposal,
} from '../../../../governance-e2e/src/support/governance.functions';
import {
@@ -28,9 +26,7 @@ import {
} from '../../support/wallet-functions';
import type { testFreeformProposal } from '../../support/common-interfaces';
import { formatDateWithLocalTimezone } from '@vegaprotocol/utils';
import { createSuccessorMarketProposalTxBody } from '../../support/proposal.functions';
const proposalListItem = '[data-testid="proposals-list-item"]';
const proposalVoteProgressForPercentage =
'vote-progress-indicator-percentage-for';
const proposalVoteProgressAgainstPercentage =
@@ -46,7 +42,6 @@ const viewProposalButton = 'view-proposal-btn';
const proposalDescriptionToggle = 'proposal-description-toggle';
const voteBreakdownToggle = 'vote-breakdown-toggle';
const proposalTermsToggle = 'proposal-json-toggle';
const marketDataToggle = 'proposal-market-data-toggle';
describe(
'Governance flow for proposal details',
@@ -55,12 +50,12 @@ describe(
before('connect wallets and set approval limit', function () {
cy.visit('/');
ethereumWalletConnect();
// cy.associateTokensToVegaWallet('1');
});
beforeEach('visit proposals tab', function () {
cy.clearLocalStorage();
turnTelemetryOff();
cy.mockChainId();
cy.reload();
waitForSpinner();
cy.connectVegaWallet();
@@ -223,33 +218,17 @@ describe(
getProposalInformationFromTable('Tokens for proposal')
.should('have.text', (1).toFixed(2))
.and('be.visible');
navigateTo(navigation.proposals);
cy.get<testFreeformProposal>('@rawProposal').then((rawProposal) => {
getProposalFromTitle(rawProposal.rationale.title).within(() => {
// 3002-PROP-021
cy.getByTestId('user-voted-yes').should('exist');
cy.getByTestId(viewProposalButton).click();
});
});
cy.getByTestId(changeVoteButton).should('be.visible').click();
voteForProposal('against');
cy.getByTestId(proposalVoteProgressAgainstPercentage)
.contains('100.00%')
.and('be.visible');
cy.getByTestId(voteBreakdownToggle).click();
getProposalInformationFromTable('Tokens against proposal')
.should('have.text', (1).toFixed(2))
.and('be.visible');
getProposalInformationFromTable('Number of voting parties')
.should('have.text', '1')
.and('be.visible');
navigateTo(navigation.proposals);
cy.get<testFreeformProposal>('@rawProposal').then((rawProposal) => {
getProposalFromTitle(rawProposal.rationale.title).within(() => {
cy.getByTestId('user-voted-no').should('exist');
cy.getByTestId(viewProposalButton).click();
});
});
});
// 3001-VOTE-042, 3001-VOTE-057, 3001-VOTE-058, 3001-VOTE-059, 3001-VOTE-060
@@ -359,98 +338,5 @@ describe(
switchVegaWalletPubKey();
stakingPageDisassociateAllTokens();
});
it('Able to see successor market details with new and updated values', function () {
cy.createMarket();
cy.reload();
waitForSpinner();
cy.getByTestId('closed-proposals').within(() => {
cy.contains('Add Lorem Ipsum market')
.parentsUntil(proposalListItem)
.last()
.within(() => {
cy.getByTestId(viewProposalButton).click();
});
});
getProposalInformationFromTable('ID')
.invoke('text')
.as('parentMarketId')
.then(() => {
cy.VegaWalletSubmitProposal(
createSuccessorMarketProposalTxBody(this.parentMarketId)
);
});
navigateTo(navigation.proposals);
cy.reload();
getProposalFromTitle('Test successor market proposal details').within(
() => cy.getByTestId(viewProposalButton).click()
);
// #3003-PMAN-010
cy.getByTestId(proposalTermsToggle).click();
cy.get('.language-json').within(() => {
cy.get('.hljs-attr').should('contain.text', 'parentMarketId');
cy.get('.hljs-string').should('contain.text', this.parentMarketId);
cy.get('.hljs-attr').should('contain.text', 'insurancePoolFraction');
cy.get('.hljs-string').should('contain.text', '0.75');
});
// 3003-PMAN-011 3003-PMAN-012
cy.getByTestId(marketDataToggle).click();
cy.getByTestId('proposal-market-data').within(() => {
cy.contains('Key details').click();
validateProposalDetailsDiff(
'Name',
proposalChangeType.UPDATED,
'Token test market',
'Test market 1'
);
validateProposalDetailsDiff(
'Parent Market ID',
proposalChangeType.ADDED,
this.parentMarketId
);
validateProposalDetailsDiff(
'Insurance Pool Fraction',
proposalChangeType.ADDED,
'0.75'
);
validateProposalDetailsDiff(
'Trading Mode',
proposalChangeType.UPDATED,
'No trading',
'Opening auction'
);
cy.contains('Instrument').click();
validateProposalDetailsDiff(
'Market Name',
proposalChangeType.UPDATED,
'Token test market',
'Test market 1'
);
cy.contains('Metadata').click();
validateProposalDetailsDiff(
'Sector',
proposalChangeType.UPDATED,
'materials',
'tech'
);
});
// 3003-PMAN-011
cy.get('.underline').contains('Parent Market ID').realHover();
cy.getByTestId('tooltip-content', { timeout: 8000 }).should(
'contain.text',
'The ID of the market this market succeeds.'
);
cy.get('.underline')
.contains('Insurance Pool Fraction')
.realMouseUp()
.realHover();
cy.getByTestId('tooltip-content', { timeout: 8000 }).should(
'contain.text',
'The fraction of the insurance pool balance that is carried over from the parent market to the successor.'
);
});
}
);
@@ -26,8 +26,6 @@ const votesTable = 'votes-table';
const openProposals = 'open-proposals';
const proposalVoteProgressForPercentage =
'vote-progress-indicator-percentage-for';
const majorityVoteReached = 'majority-reached';
const minParticipationReached = 'participation-reached';
const proposalTimeout = { timeout: 8000 };
context(
@@ -42,7 +40,6 @@ context(
beforeEach('visit proposals', function () {
cy.clearLocalStorage();
turnTelemetryOff();
cy.mockChainId();
cy.reload();
waitForSpinner();
cy.connectVegaWallet();
@@ -75,7 +72,7 @@ context(
});
});
// 3001-VOTE-020 3001-VOTE-021 3001-VOTE-046 3001-VOTE-044 3001-VOTE-074 3001-VOTE-074
// 3001-VOTE-046 3001-VOTE-044 3001-VOTE-074 3001-VOTE-074
it('Able to enact proposal by voting', function () {
const proposalTitle = 'Add New proposal with short enactment';
const proposalTx = createUpdateNetworkProposalTxBody();
@@ -88,18 +85,10 @@ context(
cy.contains(proposalTitle)
.parentsUntil(proposalListItem)
.last()
.within(() => {
// 3001-VOTE-019 time to vote is highlighted red
cy.getByTestId('vote-details')
.find('span')
.should('have.class', 'text-vega-pink');
cy.getByTestId(viewProposalButton).click();
});
.within(() => cy.getByTestId(viewProposalButton).click());
});
cy.getByTestId(proposalStatus).should('have.text', 'Open');
voteForProposal('for');
cy.getByTestId(majorityVoteReached).should('exist');
cy.getByTestId(minParticipationReached).should('exist');
cy.getByTestId(proposalStatus, proposalTimeout)
.should('have.text', 'Passed')
.then(() => {
@@ -115,14 +104,6 @@ context(
cy.getByTestId(proposalVoteProgressForPercentage)
.contains('100.00%')
.and('be.visible');
navigateTo(navigation.proposals);
cy.contains(proposalTitle)
.parentsUntil(proposalListItem)
.last()
.within(() => {
cy.getByTestId(majorityVoteReached).should('exist');
cy.getByTestId(minParticipationReached).should('exist');
});
});
// 3001-VOTE-047
@@ -82,7 +82,6 @@ context(
cy.clearLocalStorage();
turnTelemetryOff();
cy.reload();
cy.mockChainId();
waitForSpinner();
cy.connectVegaWallet();
ethereumWalletConnect();
@@ -74,7 +74,6 @@ context(
beforeEach('visit governance tab', function () {
cy.clearLocalStorage();
turnTelemetryOff();
cy.mockChainId();
cy.reload();
waitForSpinner();
cy.connectVegaWallet();
@@ -221,7 +220,7 @@ context(
function () {
const proposalTitle = 'Test new market proposal';
goToMakeNewProposal(governanceProposalType.NEW_MARKET);
cy.getByTestId(newProposalTitle).type(proposalTitle);
cy.getByTestId(newProposalTitle).type('Test new market proposal');
cy.getByTestId(newProposalDescription).type('E2E test for proposals');
cy.fixture('/proposals/new-market').then((newMarketProposal) => {
const newMarketPayload = JSON.stringify(newMarketProposal);
@@ -607,57 +606,6 @@ context(
});
});
it('able to submit successor market proposal', function () {
const proposalTitle = 'Test successor market proposal';
cy.createMarket();
cy.reload();
waitForSpinner();
cy.getByTestId('closed-proposals').within(() => {
cy.contains('Add Lorem Ipsum market')
.parentsUntil(proposalListItem)
.last()
.within(() => {
cy.getByTestId(viewProposalBtn).click();
});
});
getProposalInformationFromTable('ID').invoke('text').as('parentMarketId');
goToMakeNewProposal(governanceProposalType.NEW_MARKET);
cy.getByTestId(newProposalTitle).type(proposalTitle);
cy.getByTestId(newProposalDescription).type(
'E2E test for successor market'
);
cy.fixture('/proposals/successor-market').then((newMarketProposal) => {
newMarketProposal.changes.successor.parentMarketId =
this.parentMarketId;
const newMarketPayload = JSON.stringify(newMarketProposal);
cy.getByTestId(newProposalTerms).type(newMarketPayload, {
parseSpecialCharSequences: false,
delay: 2,
});
});
cy.getByTestId(proposalDownloadBtn)
.should('be.visible')
.click()
.then(() => {
cy.wrap(
getDownloadedProposalJsonPath('vega-new-market-proposal-')
).then((filePath) => {
goToMakeNewProposal(governanceProposalType.RAW);
submitUniqueRawProposal({ proposalBody: filePath });
});
});
navigateTo(navigation.proposals);
getProposalFromTitle(proposalTitle).within(() => {
// 3003-PMAN-008
cy.getByTestId('proposal-successor-info')
.should('have.text', 'Successor market to: TEST.24h')
.find('a')
.should('have.attr', 'href')
.and('contain', this.parentMarketId);
});
});
after('Disassociate from second wallet key if present', function () {
cy.reload();
waitForSpinner();
@@ -694,7 +642,6 @@ context(
.getByTestId('key-value-table-row')
.contains(heading)
.parent()
.parent()
.siblings();
}
}
@@ -42,7 +42,6 @@ describe('Governance flow for proposal list', { tags: '@slow' }, function () {
cy.clearLocalStorage();
turnTelemetryOff();
cy.reload();
cy.mockChainId();
waitForSpinner();
cy.connectVegaWallet();
ethereumWalletConnect();
@@ -50,8 +49,8 @@ describe('Governance flow for proposal list', { tags: '@slow' }, function () {
navigateTo(navigation.proposals);
});
// 3001-VOTE-018
it('Newly created proposals list - proposals closest to closing date appear higher in list', function () {
// 3001-VOTE-005
const proposalDays = [364, 50, 2];
for (let index = 0; index < proposalDays.length; index++) {
goToMakeNewProposal(governanceProposalType.RAW);
@@ -121,24 +120,16 @@ describe('Governance flow for proposal list', { tags: '@slow' }, function () {
submitUniqueRawProposal({ proposalTitle: proposalTitle });
getProposalFromTitle(proposalTitle).within(() => {
// 3001-VOTE-039
cy.getByTestId('participation-not-reached').should(
cy.getByTestId(voteStatus).should(
'have.text',
'Min. participation not reached'
'Participation not reached'
);
cy.getByTestId(viewProposalButton).click();
});
voteForProposal('for');
navigateTo(navigation.proposals);
getProposalFromTitle(proposalTitle).within(() => {
cy.getByTestId(voteStatus).should(
'have.text',
'Currently expected to pass'
);
cy.getByTestId('user-voted-yes').should('exist');
cy.getByTestId('participation-reached').should(
'have.text',
'Min. participation reached'
);
cy.getByTestId(voteStatus).should('have.text', 'Set to pass');
cy.getByTestId(viewProposalButton).click();
});
cy.getByTestId(voteBreakDownToggle).click();
@@ -26,7 +26,6 @@ context('rewards - flow', { tags: '@slow' }, function () {
before('set up environment to allow rewards', function () {
cy.clearLocalStorage();
turnTelemetryOff();
cy.mockChainId();
cy.visit('/');
waitForSpinner();
ethereumWalletConnect();
@@ -97,14 +96,12 @@ context('rewards - flow', { tags: '@slow' }, function () {
.within(() => {
cy.get('h2').first().should('contain.text', 'EPOCH');
cy.getByTestId('individual-rewards-asset').should('have.text', 'Vega');
cy.getByTestId('ACCOUNT_TYPE_GLOBAL_REWARD', rewardsTimeOut).should(
'contain.text',
'0.4415'
);
cy.getByTestId('ACCOUNT_TYPE_FEES_INFRASTRUCTURE').should(
'contain.text',
'0.0004'
);
cy.getByTestId('ACCOUNT_TYPE_GLOBAL_REWARD', rewardsTimeOut)
.should('contain.text', '0.4415')
.and('contain.text', '(44.15%)');
cy.getByTestId('ACCOUNT_TYPE_FEES_INFRASTRUCTURE')
.should('contain.text', '0.0004')
.and('contain.text', '(44.15%)');
cy.getByTestId('total').should('have.text', '0.4419');
});
});
@@ -25,7 +25,6 @@ import {
vegaWalletSetSpecifiedApprovalAmount,
vegaWalletTeardown,
} from '../../support/wallet-functions';
const stakeValidatorListTotalStake = 'total-stake';
const stakeValidatorListTotalShare = 'total-stake-share';
const stakeValidatorListStakePercentage = 'stake-percentage';
@@ -68,7 +67,6 @@ context(
function () {
cy.clearLocalStorage();
turnTelemetryOff();
cy.mockChainId();
// Go to homepage to allow wallet teardown without epoch timer refreshing page
navigateTo(navigation.home);
vegaWalletTeardown();
@@ -56,7 +56,6 @@ context(
function () {
cy.clearLocalStorage();
turnTelemetryOff();
cy.mockChainId();
cy.reload();
waitForSpinner();
cy.connectVegaWallet();
@@ -352,12 +351,6 @@ context(
);
stakingPageDisassociateAllTokens();
});
it('Able to associate over 1 million tokens', function () {
cy.get(ethWalletAssociateButton).click();
stakingPageAssociateTokens('2000000', { approve: true });
stakingPageDisassociateAllTokens();
});
});
}
);
@@ -29,7 +29,6 @@ const toastPanel = 'toast-panel';
const toastClose = 'toast-close';
const withdrawalDialogContent = 'dialog-content';
const toastCompleteWithdrawal = 'toast-complete-withdrawal';
const scrollBar = '.ag-body-horizontal-scroll-viewport';
const usdtName = 'USDC (local)';
const usdcEthAddress = '0x1b8a1B6CBE5c93609b46D1829Cc7f3Cb8eeE23a0';
const usdcSymbol = 'tUSDC';
@@ -51,7 +50,6 @@ context(
beforeEach('Navigate to withdrawal page', function () {
cy.clearLocalStorage();
turnTelemetryOff();
cy.mockChainId();
cy.reload();
waitForSpinner();
navigateTo(navigation.withdraw);
@@ -98,8 +96,7 @@ context(
});
});
// eslint-disable-next-line
it.skip(
it(
'Able to withdraw asset: -eth wallet connected -withdraw funds button',
{ tags: '@smoke' },
function () {
@@ -200,17 +197,20 @@ context(
);
cy.getByTestId(toastClose).click();
});
cy.get("[row-id='0']").within(() => {
cy.get(tableAssetSymbol).should('have.text', usdcSymbol);
cy.get(tableAmount).should('have.text', '110.00');
cy.get(tableReceiverAddress)
.find('a')
.should('have.attr', 'href')
.and('contain', 'https://sepolia.etherscan.io/address/');
cy.get(tableCreatedTimeStamp).should('not.be.empty');
});
cy.get(tableTxHash)
.eq(1)
.should('have.text', 'Complete withdrawal')
.parent()
.within(() => {
cy.get(tableAssetSymbol).should('have.text', usdcSymbol);
cy.get(tableAmount).should('have.text', '110.00');
cy.get(tableReceiverAddress)
.find('a')
.should('have.attr', 'href')
.and('contain', 'https://sepolia.etherscan.io/address/');
cy.get(tableCreatedTimeStamp).should('not.be.empty');
});
ethereumWalletConnect();
cy.get(scrollBar).scrollTo('right');
cy.getByTestId(completeWithdrawalButton).first().click();
cy.getByTestId(toast)
.last(txTimeout)
@@ -17,7 +17,6 @@ const governanceDocsUrl = 'https://vega.xyz/governance';
const networkUpgradeProposalListItem = 'protocol-upgrade-proposals-list-item';
const closedProposals = 'closed-proposals';
const closedProposalToggle = 'closed-proposals-toggle-networkUpgrades';
const protocolUpgradeTime = 'protocol-upgrade-time';
context(
'Governance Page - verify elements on page',
@@ -175,7 +174,6 @@ context(
});
});
// 3009-NTWU-003 3009-NTWU-004 3009-NTWU-007
it('should see details of network upgrade proposal', function () {
mockNetworkUpgradeProposal();
navigateTo(navigation.proposals);
@@ -230,8 +228,7 @@ context(
cy.getByTestId(closedProposalToggle).should('not.exist');
});
// 3009-NTWU-001 3009-NTWU-002 3009-NTWU-006 3009-NTWU-009
it.skip('should display network upgrade banner with estimate', function () {
it('should display network upgrade banner with estimate', function () {
mockNetworkUpgradeProposal();
cy.visit('/');
cy.getByTestId('banners').within(() => {
@@ -262,18 +259,10 @@ context(
.as('displayedEstimate');
cy.get('@displayedEstimate').then((estimateText) => {
// Estimated time should automatically update every second
cy.getByTestId(protocolUpgradeTime)
cy.getByTestId('protocol-upgrade-time')
.invoke('text')
.should('not.eq', estimateText);
});
// time estimate on proposal detail
cy.getByTestId('protocol-upgrade-proposal').within(() => {
cy.get('@displayedEstimate').then((estimateText) => {
cy.getByTestId(protocolUpgradeTime)
.invoke('text')
.should('not.eq', estimateText);
});
});
});
}
);
@@ -11,7 +11,6 @@ import {
waitForBeginningOfEpoch,
} from '../../support/staking.functions';
import { previousEpochData } from '../../fixtures/mocks/previous-epoch';
import { chainIdQuery, statisticsQuery } from '@vegaprotocol/mock';
const guideLink = 'staking-guide-link';
const validatorTitle = 'validator-node-title';
@@ -34,22 +33,11 @@ const overstakedPenaltyToolTip = 'overstaked-penalty-tooltip';
const multisigPenaltyToolTip = 'multisig-error-tooltip';
const epochCountDown = 'epoch-countdown';
const stakeNumberRegex = /^\d{1,3}(,\d{3})*(\.\d+)?$/;
const txTimeout = Cypress.env('txTimeout');
context('Validators Page - verify elements on page', function () {
before('navigate to validators page', () => {
cy.mockGQL((req) => {
aliasGQLQuery(req, 'ChainId', chainIdQuery());
aliasGQLQuery(req, 'Statistics', statisticsQuery());
});
before('navigate to validators page', function () {
cy.visit('/validators');
});
beforeEach(() => {
cy.mockGQL((req) => {
aliasGQLQuery(req, 'ChainId', chainIdQuery());
aliasGQLQuery(req, 'Statistics', statisticsQuery());
});
});
describe('with wallets disconnected', { tags: '@smoke' }, function () {
it('Should have validators tab highlighted', function () {
@@ -189,11 +177,6 @@ context('Validators Page - verify elements on page', function () {
{ tags: '@smoke' },
function () {
before('connect wallets and click on validator', function () {
cy.mockGQL((req) => {
aliasGQLQuery(req, 'ChainId', chainIdQuery());
aliasGQLQuery(req, 'Statistics', statisticsQuery());
});
cy.visit('/validators');
cy.connectVegaWallet();
clickOnValidatorFromList(0);
});
@@ -280,7 +263,7 @@ context('Validators Page - verify elements on page', function () {
cy.getByTestId(epochCountDown).within(() => {
cy.get(epochTitle).should('not.be.empty');
cy.get(nextEpochInfo, txTimeout).should('contain.text', 'Next epoch');
cy.get(nextEpochInfo).should('contain.text', 'Next epoch');
});
});
}
@@ -4,8 +4,6 @@ import {
vegaWalletFaucetAssetsWithoutCheck,
vegaWalletTeardown,
} from '../../support/wallet-functions';
import { aliasGQLQuery } from '@vegaprotocol/cypress';
import { chainIdQuery, statisticsQuery } from '@vegaprotocol/mock';
const walletContainer = 'aside [data-testid="vega-wallet"]';
const walletHeader = '[data-testid="wallet-header"] h1';
@@ -16,6 +14,10 @@ const dialogHeader = 'dialog-title';
const walletDialogHeader = 'wallet-dialog-title';
const connectorsList = 'connectors-list';
const dialogCloseBtn = 'dialog-close';
const restConnectorForm = 'rest-connector-form';
const restWallet = '#wallet';
const restPassphrase = '#passphrase';
const restConnectBtn = '[type="submit"]';
const accountNo = 'vega-account-truncated';
const currencyTitle = 'currency-title';
const currencyValue = 'currency-value';
@@ -67,7 +69,7 @@ context(
cy.get(dialog).within(() => {
cy.getByTestId(walletDialogHeader)
.should('be.visible')
.and('have.text', 'Get a Vega wallet');
.and('have.text', 'Connect');
});
});
@@ -75,7 +77,10 @@ context(
cy.getByTestId(connectorsList).within(() => {
cy.getByTestId('connector-jsonRpc')
.should('be.visible')
.and('have.text', 'Use the Desktop App/CLI');
.and('have.text', 'Connect Vega wallet');
cy.getByTestId('connector-rest')
.should('be.visible')
.and('have.text', 'Hosted Fairground wallet');
});
});
@@ -86,14 +91,48 @@ context(
});
});
describe('when rest connector form opened', function () {
before('click hosted wallet app button', function () {
cy.getByTestId(connectorsList).within(() => {
cy.getByTestId('connector-rest').click();
});
});
// 0002-WCON-002
it('should have wallet field visible', function () {
cy.getByTestId(restConnectorForm).within(() => {
cy.get(restWallet).should('be.visible');
});
});
it('should have password field visible', function () {
cy.getByTestId(restConnectorForm).within(() => {
cy.get(restPassphrase).should('be.visible');
});
});
it('should have connect button visible', function () {
cy.getByTestId(restConnectorForm).within(() => {
cy.get(restConnectBtn)
.should('be.visible')
.and('have.text', 'Connect');
});
});
it('should have close button visible', function () {
cy.get(dialog).within(() => {
cy.getByTestId(dialogCloseBtn).should('be.visible');
});
});
after('close dialog', function () {
cy.getByTestId(dialogCloseBtn).click().should('not.exist');
});
});
describe('when vega wallet connected', function () {
before('connect vega wallet', function () {
cy.mockGQL((req) => {
aliasGQLQuery(req, 'ChainId', chainIdQuery());
aliasGQLQuery(req, 'Statistics', statisticsQuery());
});
cy.visit('/');
cy.wait('@ChainId');
cy.connectVegaWallet();
vegaWalletTeardown();
});
@@ -276,10 +315,6 @@ context(
];
before('faucet assets to connected vega wallet', function () {
cy.mockGQL((req) => {
aliasGQLQuery(req, 'ChainId', chainIdQuery());
aliasGQLQuery(req, 'Statistics', statisticsQuery());
});
for (const { id, amount } of assets) {
vegaWalletFaucetAssetsWithoutCheck(id, amount, vegaWalletPublicKey);
}
@@ -304,7 +339,7 @@ context(
cy.getByTestId(vegaWalletCurrencyTitle)
.contains(name)
.parent()
.siblings(txTimeout)
.siblings()
.should((elementAmount) => {
const displayedAmount = parseFloat(elementAmount.text());
expect(displayedAmount).be.gte(expectedAmount);
@@ -107,7 +107,7 @@ export function dissociateFromSecondWalletKey() {
cy.getByTestId('vega-in-wallet')
.first()
.within(() => {
cy.getByTestId('eth-wallet-associated-balances', txTimeout)
cy.getByTestId('eth-wallet-associated-balances')
.last()
.within(() => {
cy.getByTestId('associated-key')
@@ -232,23 +232,6 @@ export function getDownloadedProposalJsonPath(proposalType: string) {
return filepath;
}
export function validateProposalDetailsDiff(
RowName: string,
changeType: proposalChangeType,
newValue: string,
oldValue?: string
) {
cy.contains(RowName)
.parentsUntil(proposalInformationTableRows)
.parent()
.first()
.within(() => {
cy.contains(changeType).should('be.visible');
cy.contains(newValue).should('be.visible');
if (oldValue) cy.contains(oldValue).should('have.class', 'line-through');
});
}
function getFormattedTime() {
const now = new Date();
const day = now.getDate().toString().padStart(2, '0');
@@ -269,8 +252,3 @@ export enum governanceProposalType {
FREEFORM = 'Freeform',
RAW = 'raw proposal',
}
export enum proposalChangeType {
UPDATED = 'Updated',
ADDED = 'Added',
}
@@ -211,143 +211,6 @@ export function createNewMarketProposalTxBody(): ProposalSubmissionBody {
};
}
// Requires cy.createMarket() to be run to set up parent market
export function createSuccessorMarketProposalTxBody(
parentMarketId: string
): ProposalSubmissionBody {
const MIN_CLOSE_SEC = 10000;
const MIN_ENACT_SEC = 10000;
const closingDate = addSeconds(new Date(), MIN_CLOSE_SEC);
const enactmentDate = addSeconds(closingDate, MIN_ENACT_SEC);
const closingTimestamp = millisecondsToSeconds(closingDate.getTime());
const enactmentTimestamp = millisecondsToSeconds(enactmentDate.getTime());
return {
proposalSubmission: {
rationale: {
title: 'Test successor market proposal details',
description: 'E2E test for successor market',
},
terms: {
newMarket: {
changes: {
decimalPlaces: '5',
positionDecimalPlaces: '5',
linearSlippageFactor: '0.001',
quadraticSlippageFactor: '0',
lpPriceRange: '10',
instrument: {
name: 'Token test market',
code: 'TEST.24h',
future: {
settlementAsset:
'816af99af60d684502a40824758f6b5377e6af48e50a9ee8ef478ecb879ea8bc',
quoteName: 'fUSDC',
dataSourceSpecForSettlementData: {
external: {
oracle: {
signers: [
{
pubKey: {
key: '70d14a321e02e71992fd115563df765000ccc4775cbe71a0e2f9ff5a3b9dc680',
},
},
],
filters: [
{
key: {
name: 'prices.BTC.value',
type: 'TYPE_INTEGER' as const,
numberDecimalPlaces: '0',
},
conditions: [
{
operator: 'OPERATOR_GREATER_THAN' as const,
value: '0',
},
],
},
],
},
},
},
dataSourceSpecForTradingTermination: {
external: {
oracle: {
signers: [
{
pubKey: {
key: '70d14a321e02e71992fd115563df765000ccc4775cbe71a0e2f9ff5a3b9dc680',
},
},
],
filters: [
{
key: {
name: 'trading.terminated.ETH5',
type: 'TYPE_BOOLEAN' as const,
},
conditions: [
{
operator: 'OPERATOR_EQUALS' as const,
value: 'true',
},
],
},
],
},
},
},
dataSourceSpecBinding: {
settlementDataProperty: 'prices.BTC.value',
tradingTerminationProperty: 'trading.terminated.ETH5',
},
},
},
metadata: [
'sector:food',
'sector:materials',
'source:docs.vega.xyz',
],
priceMonitoringParameters: {
triggers: [
{
horizon: '43200',
probability: '0.9999999',
auctionExtension: '600',
},
],
},
liquidityMonitoringParameters: {
targetStakeParameters: {
timeWindow: '3600',
scalingFactor: 10,
},
triggeringRatio: '0.7',
auctionExtension: '1',
},
logNormal: {
tau: 0.0001140771161,
riskAversionParameter: 0.01,
params: {
mu: 0,
r: 0.016,
sigma: 0.5,
},
},
successor: {
parentMarketId: parentMarketId,
insurancePoolFraction: '0.75',
},
},
},
closingTimestamp,
enactmentTimestamp,
},
},
};
}
export function mockNetworkUpgradeProposal() {
cy.mockGQL((req) => {
aliasGQLQuery(req, 'Nodes', nodeData);
@@ -78,22 +78,13 @@ export function stakingPageAssociateTokens(
}
cy.get(tokenAmountInputBox, epochTimeout).type(amount);
if (approve) {
cy.getByTestId('wallet-associate').then((walletAssociateField) => {
if (
walletAssociateField.find('[data-testid="token-input-approve-button"]')
.length
) {
cy.get(tokenInputApprove, txTimeout).should('be.enabled').click();
cy.contains(
'Approve $VEGA Tokens for staking on Vega',
txTimeout
).should('be.visible');
cy.contains(
'Approve $VEGA Tokens for staking on Vega',
txTimeout
).should('not.exist');
}
});
cy.get(tokenInputApprove, txTimeout).should('be.enabled').click();
cy.contains('Approve $VEGA Tokens for staking on Vega').should(
'be.visible'
);
cy.contains('Approve $VEGA Tokens for staking on Vega', txTimeout).should(
'not.exist'
);
}
cy.get(tokenSubmitButton, txTimeout).should('be.enabled').click();
+1 -5
View File
@@ -9,7 +9,6 @@ NX_VEGA_NETWORKS='{"DEVNET":"https://dev.governance.vega.xyz","STAGNET1":"https:
NX_GITHUB_FEEDBACK_URL=https://github.com/vegaprotocol/feedback/discussions
NX_VEGA_WALLET_URL=http://localhost:1789
NX_VEGA_EXPLORER_URL=https://explorer.stagnet1.vega.rocks
NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json
NX_HOSTED_WALLET_URL=https://wallet.testnet.vega.xyz
NX_VEGA_DOCS_URL=https://docs.vega.xyz/testnet
@@ -22,12 +21,9 @@ NX_VEGA_REST_URL=https://api.n00.stagnet1.vega.xyz/api/v2/
NX_TENDERMINT_URL=https://tm.n01.stagnet1.vega.rocks
NX_TENDERMINT_WEBSOCKET_URL=wss://tm.n01.stagnet1.vega.xyz/websocket
NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/pl/firefox/addon/vega-wallet
#Test configuration variables
CYPRESS_FAIRGROUND=false
LC_ALL="en_US.UTF-8"
# Cosmic elevator flags
NX_SUCCESSOR_MARKETS=true
NX_SUCCESSOR_MARKETS=true
@@ -1 +0,0 @@
export * from './json-diff';
@@ -1,30 +0,0 @@
import { render, screen, cleanup } from '@testing-library/react';
import { JsonDiff } from './json-diff';
describe('JsonDiff', () => {
afterEach(cleanup);
it('renders without crashing', () => {
render(<JsonDiff left={{}} right={{}} />);
expect(screen.getByTestId('json-diff')).toBeInTheDocument();
});
it('shows the correct message when both objects are identical', () => {
render(<JsonDiff left={{}} right={{}} />);
expect(screen.getByText('Data is identical')).toBeInTheDocument();
});
it('does not show the "identical" message when both objects are not identical', () => {
render(
<JsonDiff
left={{
name: 'test',
}}
right={{
name: 'test2',
}}
/>
);
expect(screen.queryByText('Data is identical')).not.toBeInTheDocument();
});
});
@@ -1,46 +0,0 @@
import { useState, useEffect } from 'react';
import { useTranslation } from 'react-i18next';
import { formatters, create } from 'jsondiffpatch';
import 'jsondiffpatch/dist/formatters-styles/html.css';
import 'jsondiffpatch/dist/formatters-styles/annotated.css';
export type JsonValue =
| string
| number
| boolean
| null
| JsonValue[]
| { [key: string]: JsonValue };
interface JsonDiffProps {
left: JsonValue;
right: JsonValue;
objectHash?: (obj: unknown) => string | undefined;
}
export const JsonDiff = ({ right, left, objectHash }: JsonDiffProps) => {
const { t } = useTranslation();
const [html, setHtml] = useState<string | undefined>();
useEffect(() => {
const delta = create({
objectHash,
}).diff(left, right);
if (delta) {
const deltaHtml = formatters.html.format(delta, left);
formatters.html.hideUnchanged();
setHtml(deltaHtml);
} else {
setHtml(undefined);
}
}, [right, left, objectHash]);
return html ? (
<div data-testid="json-diff" dangerouslySetInnerHTML={{ __html: html }} />
) : (
<p data-testid="json-diff">{t('dataIsIdentical')}</p>
);
};
@@ -6,7 +6,6 @@ import { Nav } from '../nav';
import { Networks, useEnvironment } from '@vegaprotocol/environment';
import {
ProtocolUpgradeCountdownMode,
ProtocolUpgradeInProgressNotification,
ProtocolUpgradeProposalNotification,
} from '@vegaprotocol/proposals';
import { ViewingAsBanner } from '@vegaprotocol/ui-toolkit';
@@ -50,7 +49,6 @@ const NotificationsContainer = () => {
<ProtocolUpgradeProposalNotification
mode={ProtocolUpgradeCountdownMode.IN_ESTIMATED_TIME_REMAINING}
/>
<ProtocolUpgradeInProgressNotification />
{isReadOnly ? (
<ViewingAsBanner pubKey={pubKey} disconnect={disconnect} />
) : null}
@@ -1,8 +1,4 @@
import {
VegaConnectDialog,
VegaManageDialog,
ViewAsDialog,
} from '@vegaprotocol/wallet';
import { VegaConnectDialog, VegaManageDialog } from '@vegaprotocol/wallet';
import {
AppStateActionType,
useAppState,
@@ -29,8 +25,6 @@ export const VegaWalletDialogs = () => {
})
}
/>
<ViewAsDialog connector={Connectors.view} />
</>
);
};
@@ -0,0 +1,17 @@
import { Link } from '@vegaprotocol/ui-toolkit';
import { useTranslation } from 'react-i18next';
import { ExternalLinks } from '@vegaprotocol/environment';
export const DownloadWalletPrompt = () => {
const { t } = useTranslation();
return (
<>
<h3 className="mt-4 mb-2">{t('getWallet')}</h3>
<p>
<Link className="text-neutral-500" href={ExternalLinks.VEGA_WALLET_URL}>
{t('getWalletLink')}
</Link>
</p>
</>
);
};
@@ -1,26 +0,0 @@
import { ButtonLink, Link } from '@vegaprotocol/ui-toolkit';
import { useTranslation } from 'react-i18next';
import { ExternalLinks } from '@vegaprotocol/environment';
import { useViewAsDialog } from '@vegaprotocol/wallet';
export const VegaWalletPrompt = () => {
const { t } = useTranslation();
const setViewAsDialog = useViewAsDialog((state) => state.setOpen);
return (
<>
<h3 className="mt-4 mb-2">{t('getWallet')}</h3>
<div className="flex flex-row gap-4">
<Link className="text-neutral-500" href={ExternalLinks.VEGA_WALLET_URL}>
{t('getWalletLink')}
</Link>
<ButtonLink
className="text-neutral-500"
data-testid="view-as-user"
onClick={() => setViewAsDialog(true)}
>
{t('viewAsParty')}
</ButtonLink>
</div>
</>
);
};
@@ -20,7 +20,7 @@ import {
WalletCardHeader,
WalletCardRow,
} from '../wallet-card';
import { VegaWalletPrompt } from './vega-wallet-prompt';
import { DownloadWalletPrompt } from './download-wallet-prompt';
import { usePollForDelegations } from './hooks';
import { useVegaWallet, useVegaWalletDialogStore } from '@vegaprotocol/wallet';
import { Button, ButtonLink } from '@vegaprotocol/ui-toolkit';
@@ -85,7 +85,7 @@ const VegaWalletNotConnected = () => {
>
{t('connectVegaWalletToUseAssociated')}
</Button>
<VegaWalletPrompt />
<DownloadWalletPrompt />
</>
);
};
@@ -626,8 +626,7 @@
"status-tendermint": "Consensus",
"status-ersatz": "Standby",
"status-pending": "Candidate",
"ersatzDescription1": "To be promoted, a standby validator must have more than the lowest consensus stake, plus a bonus given to existing validators, and only one standby validator can be promoted per epoch. Currently this requires a minimum of",
"ersatzDescription2": "stake assuming no performance penalty incurred.",
"ersatzDescription": "To be promoted, a standby validator must have more than the lowest consensus stake, plus a bonus given to existing validators. This currently requires a minimum of {{stakeNeededForPromotion}} stake assuming no penalties. Only one validator per epoch can be promoted.",
"pendingDescription1": "Anyone can",
"pendingDescriptionLinkText": "set up and run a node on Vega",
"pendingDescription2": ". A node can move from being a candidate into standby based on how much nomination it attracts, assuming it has proven reliability by sending heartbeats to the network.",
@@ -785,7 +784,6 @@
"PerformancePenaltyDescription": "Performance score is a measure of how often a validator proposed blocks in the last epoch relative to how many they should be expected to propose based on their voting power. Performance penalty is applied for having a performance score of less than 1",
"UnnormalisedVotingPowerDescription": "The voting power of the validator based on their final validator score after all penalties have been applied",
"NormalisedVotingPowerDescription": "The voting power of the validator, adjusted to ensure all validator scores sum to 1, used for distribution of rewards",
"NonConsensusVotingPowerDescription": "The voting power of the validator. Only consensus validators have voting power",
"Score": "Score",
"performancePenalty": "Performance penalty",
"overstaked": "Overstaked",
@@ -855,8 +853,5 @@
"consensusNodes": "consensus nodes",
"activeNodes": "active nodes",
"Estimated time to upgrade": "Estimated time to upgrade",
"Upgraded at": "Upgraded at",
"dataIsIdentical": "Data is identical",
"updatesToMarket": "Updates to market",
"viewAsParty": "View as party"
"Upgraded at": "Upgraded at"
}
+13 -20
View File
@@ -1,8 +1,8 @@
<!DOCTYPE html>
<html lang="en" class="dark bg-black w-full h-full">
<head>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta charset="utf-8" />
<link rel="stylesheet" href="//static.vega.xyz/fonts.css" />
<link
rel="icon"
type="image/x-icon"
@@ -10,42 +10,35 @@
/>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta name="description" content="Vega Protocol - VEGA Governance" />
<meta name="og:type" content="website" />
<meta name="og:url" content="https://governance.vega.xyz/" />
<meta name="og:title" content="Vega Protocol - Governance" />
<meta name="og:site_name" content="Vega Protocol - Governance" />
<meta name="og:image" content="https://static.vega.xyz/favicon.ico" />
<meta name="twitter:card" content="https://static.vega.xyz/favicon.ico" />
<meta name="twitter:title" content="Vega Protocol - Governance" />
<meta name="twitter:description" content="Vega Protocol - Governance" />
<meta name="twitter:image" content="https://static.vega.xyz/favicon.ico" />
<meta name="twitter:image:alt" content="VEGA logo" />
<meta name="twitter:site" content="@vegaprotocol" />
<meta name="description" content="Vega Protocol - Governance" />
<meta name="description" content="Vega Protocol - VEGA Token Vesting" />
<link rel="apple-touch-icon" href="assets/apple-touch-icon.png" />
<!--
manifest.json provides metadata used when your web app is installed on a
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
-->
<link rel="manifest" href="assets/manifest.json" />
<link
rel="preload"
href="https://static.vega.xyz/AlphaLyrae-Medium.woff2"
as="font"
type="font/woff2"
crossorigin="anonymous"
/>
<title>VEGA Governance</title>
<title>Vega Governance dApp</title>
<script src="./assets/env-config.js"></script>
</head>
<body class="h-full">
<noscript> You need to enable JavaScript to run this app. </noscript>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root" class="h-full"></div>
<!-- This HTML file is a template.
<!--
This HTML file is a template.
If you open it directly in the browser, you will see an empty page.
You can add webfonts, meta tags, or analytics to this file.
The build step will place the bundled scripts into the <body> tag.
To begin the development, run `npm start` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`. -->
To create a production bundle, use `npm run build` or `yarn build`.
-->
</body>
</html>
@@ -1,4 +1,5 @@
import {
RestConnector,
JsonRpcConnector,
ViewConnector,
InjectedConnector,
@@ -7,11 +8,13 @@ import {
const urlParams = new URLSearchParams(window.location.search);
export const injected = new InjectedConnector();
export const rest = new RestConnector();
export const jsonRpc = new JsonRpcConnector();
export const view = new ViewConnector(urlParams.get('address'));
export const Connectors = {
injected,
rest,
jsonRpc,
view,
};
@@ -24,7 +24,6 @@ import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
import type { MockedResponse } from '@apollo/client/testing';
import { FLAGS } from '@vegaprotocol/environment';
import { BrowserRouter } from 'react-router-dom';
import { VoteState } from '../vote-details/use-user-vote';
jest.mock('@vegaprotocol/proposals', () => ({
...jest.requireActual('@vegaprotocol/proposals'),
@@ -37,8 +36,7 @@ jest.mock('@vegaprotocol/proposals', () => ({
const renderComponent = (
proposal: ProposalQuery['proposal'],
isListItem = true,
mocks: MockedResponse[] = [],
voteState?: VoteState
mocks: MockedResponse[] = []
) =>
render(
<AppStateProvider>
@@ -49,7 +47,6 @@ const renderComponent = (
proposal={proposal}
isListItem={isListItem}
networkParams={mockNetworkParams}
voteState={voteState}
/>
</VegaWalletContext.Provider>
</MockedProvider>
@@ -389,15 +386,10 @@ describe('Proposal header', () => {
closingDatetime: nextWeek.toString(),
},
});
renderComponent(
proposal,
true,
[
// @ts-ignore generateProposal always creates an id
createUserVoteQueryMock(proposal.id, VoteValue.VALUE_NO),
],
VoteState.No
);
renderComponent(proposal, true, [
// @ts-ignore generateProposal always creates an id
createUserVoteQueryMock(proposal.id, VoteValue.VALUE_NO),
]);
expect(await screen.findByTestId('user-voted-no')).toBeInTheDocument();
});
@@ -408,15 +400,10 @@ describe('Proposal header', () => {
closingDatetime: nextWeek.toString(),
},
});
renderComponent(
proposal,
true,
[
// @ts-ignore generateProposal always creates an id
createUserVoteQueryMock(proposal.id, VoteValue.VALUE_YES),
],
VoteState.Yes
);
renderComponent(proposal, true, [
// @ts-ignore generateProposal always creates an id
createUserVoteQueryMock(proposal.id, VoteValue.VALUE_YES),
]);
expect(await screen.findByTestId('user-voted-yes')).toBeInTheDocument();
});
});
@@ -8,26 +8,25 @@ import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
import { truncateMiddle } from '../../../../lib/truncate-middle';
import { CurrentProposalState } from '../current-proposal-state';
import { ProposalInfoLabel } from '../proposal-info-label';
import { useUserVote } from '../vote-details/use-user-vote';
import { ProposalVotingStatus } from '../proposal-voting-status';
import type { NetworkParamsResult } from '@vegaprotocol/network-parameters';
import { useSuccessorMarketProposalDetails } from '@vegaprotocol/proposals';
import { FLAGS } from '@vegaprotocol/environment';
import Routes from '../../../routes';
import { Link } from 'react-router-dom';
import type { VoteState } from '../vote-details/use-user-vote';
export const ProposalHeader = ({
proposal,
networkParams,
isListItem = true,
voteState,
}: {
proposal: ProposalFieldsFragment | ProposalQuery['proposal'];
networkParams: Partial<NetworkParamsResult>;
isListItem?: boolean;
voteState?: VoteState | null;
}) => {
const { t } = useTranslation();
const { voteState } = useUserVote(proposal?.id);
const change = proposal?.terms.change;
let details: ReactNode;
@@ -1 +0,0 @@
export * from './proposal-market-changes';
@@ -1,91 +0,0 @@
import { render, fireEvent } from '@testing-library/react';
import {
ProposalMarketChanges,
applyImmutableKeysFromEarlierVersion,
} from './proposal-market-changes';
import type { JsonValue } from '../../../../components/json-diff';
describe('applyImmutableKeysFromEarlierVersion', () => {
it('returns an empty object if any argument is not an object or null', () => {
const earlierVersion: JsonValue = null;
const updatedVersion: JsonValue = null;
expect(
applyImmutableKeysFromEarlierVersion(earlierVersion, updatedVersion)
).toEqual({});
});
it('overrides updatedVersion with values from earlierVersion for immutable keys', () => {
const earlierVersion: JsonValue = {
decimalPlaces: 2,
positionDecimalPlaces: 3,
instrument: {
name: 'Instrument1',
future: {
settlementAsset: 'Asset1',
},
},
};
const updatedVersion: JsonValue = {
decimalPlaces: 3, // should be overridden by 2
positionDecimalPlaces: 4, // should be overridden by 3
instrument: {
name: 'Instrument2', // should be overridden by 'Instrument1'
future: {
settlementAsset: 'Asset2', // should be overridden by 'Asset1'
},
},
};
const expected: JsonValue = {
decimalPlaces: 2,
positionDecimalPlaces: 3,
instrument: {
name: 'Instrument1',
future: {
settlementAsset: 'Asset1',
},
},
};
expect(
applyImmutableKeysFromEarlierVersion(earlierVersion, updatedVersion)
).toEqual(expected);
});
});
describe('ProposalMarketChanges', () => {
it('renders correctly', () => {
const { getByTestId } = render(
<ProposalMarketChanges
originalProposal={{}}
latestEnactedProposal={{}}
updatedProposal={{}}
/>
);
expect(getByTestId('proposal-market-changes')).toBeInTheDocument();
});
it('JsonDiff is not visible when showChanges is false', () => {
const { queryByTestId } = render(
<ProposalMarketChanges
originalProposal={{}}
latestEnactedProposal={{}}
updatedProposal={{}}
/>
);
expect(queryByTestId('json-diff')).not.toBeInTheDocument();
});
it('JsonDiff is visible when showChanges is true', async () => {
const { getByTestId } = render(
<ProposalMarketChanges
originalProposal={{}}
latestEnactedProposal={{}}
updatedProposal={{}}
/>
);
fireEvent.click(getByTestId('proposal-market-changes-toggle'));
expect(getByTestId('json-diff')).toBeInTheDocument();
});
});
@@ -1,86 +0,0 @@
import cloneDeep from 'lodash/cloneDeep';
import set from 'lodash/set';
import get from 'lodash/get';
import { JsonDiff } from '../../../../components/json-diff';
import { useTranslation } from 'react-i18next';
import { useState } from 'react';
import { CollapsibleToggle } from '../../../../components/collapsible-toggle';
import { SubHeading } from '../../../../components/heading';
import type { JsonValue } from '../../../../components/json-diff';
const immutableKeys = [
'decimalPlaces',
'positionDecimalPlaces',
'instrument.name',
'instrument.future.settlementAsset',
];
export const applyImmutableKeysFromEarlierVersion = (
earlierVersion: JsonValue,
updatedVersion: JsonValue
) => {
if (
typeof earlierVersion !== 'object' ||
earlierVersion === null ||
typeof updatedVersion !== 'object' ||
updatedVersion === null
) {
// If either version is not an object or is null, return null or throw an error
return {};
}
const updatedVersionCopy = cloneDeep(updatedVersion);
// Overwrite the immutable keys in the updatedVersionCopy with the earlier values
immutableKeys.forEach((key) => {
set(updatedVersionCopy, key, get(earlierVersion, key));
});
return updatedVersionCopy;
};
interface ProposalMarketChangesProps {
originalProposal: JsonValue;
latestEnactedProposal: JsonValue | undefined;
updatedProposal: JsonValue;
}
export const ProposalMarketChanges = ({
originalProposal,
latestEnactedProposal,
updatedProposal,
}: ProposalMarketChangesProps) => {
const { t } = useTranslation();
const [showChanges, setShowChanges] = useState(false);
return (
<section data-testid="proposal-market-changes">
<CollapsibleToggle
toggleState={showChanges}
setToggleState={setShowChanges}
dataTestId={'proposal-market-changes-toggle'}
>
<SubHeading title={t('updatesToMarket')} />
</CollapsibleToggle>
{showChanges && (
<div className="mb-6">
<JsonDiff
left={latestEnactedProposal || originalProposal}
right={
latestEnactedProposal
? applyImmutableKeysFromEarlierVersion(
latestEnactedProposal,
updatedProposal
)
: applyImmutableKeysFromEarlierVersion(
originalProposal,
updatedProposal
)
}
/>
</div>
)}
</section>
);
};
@@ -25,7 +25,7 @@ import {
} from '@vegaprotocol/ui-toolkit';
import { SubHeading } from '../../../../components/heading';
import { CollapsibleToggle } from '../../../../components/collapsible-toggle';
import type { MarketInfo } from '@vegaprotocol/markets';
import type { MarketInfoWithData } from '@vegaprotocol/markets';
import type { DataSourceDefinition } from '@vegaprotocol/types';
import { create } from 'zustand';
@@ -45,10 +45,8 @@ export const useMarketDataDialogStore = create<MarketDataDialogState>(
export const ProposalMarketData = ({
marketData,
parentMarketData,
}: {
marketData: MarketInfo;
parentMarketData?: MarketInfo;
marketData: MarketInfoWithData;
}) => {
const { t } = useTranslation();
const { isOpen, open, close } = useMarketDataDialogStore();
@@ -60,21 +58,8 @@ export const ProposalMarketData = ({
const settlementData = marketData.tradableInstrument.instrument.product
.dataSourceSpecForSettlementData.data as DataSourceDefinition;
const parentSettlementData =
parentMarketData?.tradableInstrument.instrument?.product
?.dataSourceSpecForSettlementData?.data;
const terminationData = marketData.tradableInstrument.instrument.product
.dataSourceSpecForTradingTermination.data as DataSourceDefinition;
const parentTerminationData =
parentMarketData?.tradableInstrument.instrument?.product
?.dataSourceSpecForTradingTermination?.data;
const isParentSettlementDataEqual =
parentSettlementData !== undefined &&
isEqual(settlementData, parentSettlementData);
const isParentTerminationDataEqual =
parentTerminationData !== undefined &&
isEqual(terminationData, parentTerminationData);
const getSigners = (data: DataSourceDefinition) => {
if (data.sourceType.__typename === 'DataSourceDefinitionExternal') {
@@ -112,22 +97,12 @@ export const ProposalMarketData = ({
<AccordionItem
itemId="key-details"
title={t('Key details')}
content={
<KeyDetailsInfoPanel
market={marketData}
parentMarket={parentMarketData}
/>
}
content={<KeyDetailsInfoPanel market={marketData} />}
/>
<AccordionItem
itemId="instrument"
title={t('Instrument')}
content={
<InstrumentInfoPanel
market={marketData}
parentMarket={parentMarketData}
/>
}
content={<InstrumentInfoPanel market={marketData} />}
/>
{isEqual(
getSigners(settlementData),
@@ -140,11 +115,6 @@ export const ProposalMarketData = ({
<OracleInfoPanel
market={marketData}
type="settlementData"
parentMarket={
isParentSettlementDataEqual
? undefined
: parentMarketData
}
/>
}
/>
@@ -157,11 +127,6 @@ export const ProposalMarketData = ({
<OracleInfoPanel
market={marketData}
type="settlementData"
parentMarket={
isParentSettlementDataEqual
? undefined
: parentMarketData
}
/>
}
/>
@@ -170,21 +135,11 @@ export const ProposalMarketData = ({
itemId="termination-oracle"
title={t('Termination Oracle')}
content={
<OracleInfoPanel
market={marketData}
type="termination"
parentMarket={
isParentTerminationDataEqual
? undefined
: parentMarketData
}
/>
<OracleInfoPanel market={marketData} type="termination" />
}
/>
</>
)}
{/*Note: successor markets will not differ in their settlement*/}
{/*assets, so no need to pass in parent market data for comparison.*/}
<AccordionItem
itemId="settlement-asset"
title={t('Settlement asset')}
@@ -193,42 +148,22 @@ export const ProposalMarketData = ({
<AccordionItem
itemId="metadata"
title={t('Metadata')}
content={
<MetadataInfoPanel
market={marketData}
parentMarket={parentMarketData}
/>
}
content={<MetadataInfoPanel market={marketData} />}
/>
<AccordionItem
itemId="risk-model"
title={t('Risk model')}
content={
<RiskModelInfoPanel
market={marketData}
parentMarket={parentMarketData}
/>
}
content={<RiskModelInfoPanel market={marketData} />}
/>
<AccordionItem
itemId="risk-parameters"
title={t('Risk parameters')}
content={
<RiskParametersInfoPanel
market={marketData}
parentMarket={parentMarketData}
/>
}
content={<RiskParametersInfoPanel market={marketData} />}
/>
<AccordionItem
itemId="risk-factors"
title={t('Risk factors')}
content={
<RiskFactorsInfoPanel
market={marketData}
parentMarket={parentMarketData}
/>
}
content={<RiskFactorsInfoPanel market={marketData} />}
/>
{(
marketData.priceMonitoringSettings?.parameters?.triggers || []
@@ -239,7 +174,6 @@ export const ProposalMarketData = ({
content={
<PriceMonitoringBoundsInfoPanel
market={marketData}
parentMarket={parentMarketData}
triggerIndex={triggerIndex}
/>
}
@@ -249,21 +183,13 @@ export const ProposalMarketData = ({
itemId="liqudity-monitoring-parameters"
title={t('Liquidity monitoring parameters')}
content={
<LiquidityMonitoringParametersInfoPanel
market={marketData}
parentMarket={parentMarketData}
/>
<LiquidityMonitoringParametersInfoPanel market={marketData} />
}
/>
<AccordionItem
itemId="liquidity-price-range"
title={t('Liquidity price range')}
content={
<LiquidityPriceRangeInfoPanel
market={marketData}
parentMarket={parentMarketData}
/>
}
content={<LiquidityPriceRangeInfoPanel market={marketData} />}
/>
</Accordion>
</div>
@@ -274,7 +200,7 @@ export const ProposalMarketData = ({
title={marketData.tradableInstrument.instrument.code}
open={isOpen}
onChange={(isOpen) => (isOpen ? open() : close())}
size="large"
size="medium"
dataTestId="market-json-dialog"
>
<CopyWithTooltip text={JSON.stringify(marketData)}>
@@ -1,6 +1,4 @@
import { MemoryRouter } from 'react-router-dom';
import { MockedProvider } from '@apollo/client/testing';
import { VegaWalletProvider } from '@vegaprotocol/wallet';
import { render, screen } from '@testing-library/react';
import { generateProposal } from '../../test-helpers/generate-proposals';
import { Proposal } from './proposal';
@@ -46,15 +44,11 @@ jest.mock('../list-asset', () => ({
const renderComponent = (proposal: ProposalQuery['proposal']) => {
render(
<MemoryRouter>
<MockedProvider>
<VegaWalletProvider>
<Proposal
restData={{}}
proposal={proposal as ProposalQuery['proposal']}
networkParams={mockNetworkParams}
/>
</VegaWalletProvider>
</MockedProvider>
<Proposal
restData={{}}
proposal={proposal as ProposalQuery['proposal']}
networkParams={mockNetworkParams}
/>
</MemoryRouter>
);
};
@@ -13,14 +13,11 @@ import Routes from '../../../routes';
import { ProposalMarketData } from '../proposal-market-data';
import type { ProposalFieldsFragment } from '../../proposals/__generated__/Proposals';
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
import type { MarketInfo } from '@vegaprotocol/markets';
import type { MarketInfoWithData } from '@vegaprotocol/markets';
import type { AssetQuery } from '@vegaprotocol/assets';
import { removePaginationWrapper } from '@vegaprotocol/utils';
import { ProposalState } from '@vegaprotocol/types';
import { ProposalMarketChanges } from '../proposal-market-changes';
import type { NetworkParamsResult } from '@vegaprotocol/network-parameters';
import { useVoteSubmit } from '@vegaprotocol/proposals';
import { useUserVote } from '../vote-details/use-user-vote';
export enum ProposalType {
PROPOSAL_NEW_MARKET = 'PROPOSAL_NEW_MARKET',
@@ -33,15 +30,10 @@ export enum ProposalType {
export interface ProposalProps {
proposal: ProposalFieldsFragment | ProposalQuery['proposal'];
networkParams: Partial<NetworkParamsResult>;
newMarketData?: MarketInfo | null;
parentMarketData?: MarketInfo | null;
newMarketData?: MarketInfoWithData | null;
assetData?: AssetQuery | null;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
restData: any;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
originalMarketProposalRestData?: any;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
mostRecentlyEnactedAssociatedMarketProposal?: any;
}
export const Proposal = ({
@@ -49,14 +41,9 @@ export const Proposal = ({
networkParams,
restData,
newMarketData,
parentMarketData,
assetData,
originalMarketProposalRestData,
mostRecentlyEnactedAssociatedMarketProposal,
}: ProposalProps) => {
const { t } = useTranslation();
const { submit, Dialog, finalizedVote } = useVoteSubmit();
const { voteState, voteDatetime } = useUserVote(proposal?.id, finalizedVote);
if (!proposal) {
return null;
@@ -136,12 +123,10 @@ export const Proposal = ({
</div>
)}
</div>
<ProposalHeader
proposal={proposal}
isListItem={false}
networkParams={networkParams}
voteState={voteState}
/>
<div id="details">
@@ -165,28 +150,7 @@ export const Proposal = ({
{newMarketData && (
<div className="mb-4">
<ProposalMarketData
marketData={newMarketData}
parentMarketData={parentMarketData ? parentMarketData : undefined}
/>
</div>
)}
{proposal.terms.change.__typename === 'UpdateMarket' && (
<div className="mb-4">
<ProposalMarketChanges
originalProposal={
originalMarketProposalRestData?.data?.proposal?.terms?.newMarket
?.changes || {}
}
latestEnactedProposal={
mostRecentlyEnactedAssociatedMarketProposal?.node?.proposal
?.terms?.updateMarket?.changes || {}
}
updatedProposal={
restData?.data?.proposal?.terms?.updateMarket?.changes || {}
}
/>
<ProposalMarketData marketData={newMarketData} />
</div>
)}
@@ -213,10 +177,6 @@ export const Proposal = ({
spamProtectionMinTokens={
networkParams?.spam_protection_voting_min_tokens
}
submit={submit}
dialog={Dialog}
voteState={voteState}
voteDatetime={voteDatetime}
/>
</RoundedWrapper>
</div>
@@ -1,7 +1,6 @@
import { RoundedWrapper } from '@vegaprotocol/ui-toolkit';
import { ProposalHeader } from '../proposal-detail-header/proposal-header';
import { ProposalsListItemDetails } from './proposals-list-item-details';
import { useUserVote } from '../vote-details/use-user-vote';
import type { ProposalFieldsFragment } from '../../proposals/__generated__/Proposals';
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
import type { NetworkParamsResult } from '@vegaprotocol/network-parameters';
@@ -15,17 +14,12 @@ export const ProposalsListItem = ({
proposal,
networkParams,
}: ProposalsListItemProps) => {
const { voteState } = useUserVote(proposal?.id);
if (!proposal || !proposal.id || !networkParams) return null;
return (
<li id={proposal.id} data-testid="proposals-list-item">
<RoundedWrapper paddingBottom={true} heightFull={true}>
<ProposalHeader
proposal={proposal}
networkParams={networkParams}
voteState={voteState}
/>
<ProposalHeader proposal={proposal} networkParams={networkParams} />
<ProposalsListItemDetails proposal={proposal} />
</RoundedWrapper>
</li>
@@ -188,7 +188,11 @@ export const VoteButtons = ({
(voteState === VoteState.Yes || voteState === VoteState.No) && (
<p data-testid="you-voted">
<span>{t('youVoted')}:</span>{' '}
<span className="text-white font-bold">
<span
className={
voteState === VoteState.Yes ? 'text-success' : 'text-danger'
}
>
{t(`voteState_${voteState}`)}
</span>{' '}
{voteDatetime ? (
@@ -3,29 +3,23 @@ import { formatDistanceToNow } from 'date-fns';
import { RoundedWrapper, Icon, ExternalLink } from '@vegaprotocol/ui-toolkit';
import { useVegaWallet } from '@vegaprotocol/wallet';
import { ProposalState } from '@vegaprotocol/types';
import { VoteProgress } from '@vegaprotocol/proposals';
import { useVoteSubmit, VoteProgress } from '@vegaprotocol/proposals';
import { formatNumber } from '../../../../lib/format-number';
import { ConnectToVega } from '../../../../components/connect-to-vega';
import { useVoteInformation } from '../../hooks';
import { useUserVote } from './use-user-vote';
import { CurrentProposalStatus } from '../current-proposal-status';
import { VoteButtonsContainer } from './vote-buttons';
import { SubHeading } from '../../../../components/heading';
import { ProposalType } from '../proposal/proposal';
import type { VoteValue } from '@vegaprotocol/types';
import type { DialogProps } from '@vegaprotocol/wallet';
import type { ProposalFieldsFragment } from '../../proposals/__generated__/Proposals';
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
import type { VoteState } from './use-user-vote';
interface VoteDetailsProps {
proposal: ProposalFieldsFragment | ProposalQuery['proposal'];
minVoterBalance: string | null | undefined;
spamProtectionMinTokens: string | null | undefined;
proposalType: ProposalType | null;
submit: (voteValue: VoteValue, proposalId: string | null) => Promise<void>;
dialog: (props: DialogProps) => JSX.Element;
voteState: VoteState | null;
voteDatetime: Date | null;
}
export const VoteDetails = ({
@@ -33,10 +27,6 @@ export const VoteDetails = ({
minVoterBalance,
spamProtectionMinTokens,
proposalType,
submit,
dialog,
voteState,
voteDatetime,
}: VoteDetailsProps) => {
const { pubKey } = useVegaWallet();
const {
@@ -58,7 +48,8 @@ export const VoteDetails = ({
} = useVoteInformation({ proposal });
const { t } = useTranslation();
const { submit, Dialog, finalizedVote } = useVoteSubmit();
const { voteState, voteDatetime } = useUserVote(proposal?.id, finalizedVote);
const defaultDecimals = 2;
const daysLeft = t('daysLeft', {
daysLeft: formatDistanceToNow(new Date(proposal?.terms.closingDatetime)),
@@ -228,7 +219,7 @@ export const VoteDetails = ({
spamProtectionMinTokens={spamProtectionMinTokens}
className="flex"
submit={submit}
dialog={dialog}
dialog={Dialog}
/>
)
) : (
@@ -1,5 +1,5 @@
import { AsyncRenderer } from '@vegaprotocol/ui-toolkit';
import { useEffect, useState } from 'react';
import { useEffect } from 'react';
import { useParams } from 'react-router-dom';
import { Proposal } from '../components/proposal';
@@ -8,23 +8,15 @@ import { useProposalQuery } from './__generated__/Proposal';
import { useFetch } from '@vegaprotocol/react-helpers';
import { ENV } from '../../../config';
import { useDataProvider } from '@vegaprotocol/data-provider';
import { marketInfoProvider } from '@vegaprotocol/markets';
import { marketInfoWithDataProvider } from '@vegaprotocol/markets';
import { useAssetQuery } from '@vegaprotocol/assets';
import {
NetworkParams,
useNetworkParams,
} from '@vegaprotocol/network-parameters';
import { useParentMarketIdQuery } from '@vegaprotocol/markets';
import { FLAGS } from '@vegaprotocol/environment';
import { useSuccessorMarketProposalDetails } from '@vegaprotocol/proposals';
export const ProposalContainer = () => {
const [
mostRecentlyEnactedAssociatedMarketProposal,
setMostRecentlyEnactedAssociatedMarketProposal,
] = useState(undefined);
const params = useParams<{ proposalId: string }>();
const {
params: networkParams,
loading: networkParamsLoading,
@@ -45,11 +37,9 @@ export const ProposalContainer = () => {
NetworkParams.governance_proposal_updateNetParam_requiredMajority,
NetworkParams.governance_proposal_freeform_requiredMajority,
]);
const {
state: { data: restData, loading: restLoading, error: restError },
state: { data: restData },
} = useFetch(`${ENV.rest}governance?proposalId=${params.proposalId}`);
const { data, loading, error, refetch } = useProposalQuery({
fetchPolicy: 'network-only',
errorPolicy: 'ignore',
@@ -57,45 +47,12 @@ export const ProposalContainer = () => {
skip: !params.proposalId,
});
const successor = useSuccessorMarketProposalDetails(params.proposalId);
const isSuccessor = !!successor?.parentMarketId || !!successor.code;
const {
state: {
data: originalMarketProposalRestData,
loading: originalMarketProposalRestLoading,
error: originalMarketProposalRestError,
},
} = useFetch(
`${ENV.rest}governance?proposalId=${
data?.proposal?.terms.change.__typename === 'UpdateMarket' &&
data?.proposal.terms.change.marketId
}`,
undefined,
true,
data?.proposal?.terms.change.__typename !== 'UpdateMarket'
);
const {
state: {
data: previouslyEnactedMarketProposalsRestData,
loading: previouslyEnactedMarketProposalsRestLoading,
error: previouslyEnactedMarketProposalsRestError,
},
} = useFetch(
`${ENV.rest}governances?proposalState=STATE_ENACTED&proposalType=TYPE_UPDATE_MARKET`,
undefined,
true,
data?.proposal?.terms.change.__typename !== 'UpdateMarket'
);
const {
data: newMarketData,
loading: newMarketLoading,
error: newMarketError,
} = useDataProvider({
dataProvider: marketInfoProvider,
dataProvider: marketInfoWithDataProvider,
skipUpdates: true,
variables: {
marketId: data?.proposal?.id || '',
@@ -103,33 +60,6 @@ export const ProposalContainer = () => {
},
});
const {
data: parentMarketId,
loading: parentMarketIdLoading,
error: parentMarketIdError,
} = useParentMarketIdQuery({
variables: {
marketId: newMarketData?.id || '',
},
skip: !FLAGS.SUCCESSOR_MARKETS || !isSuccessor || !newMarketData?.id,
});
const {
data: parentMarketData,
loading: parentMarketLoading,
error: parentMarketError,
} = useDataProvider({
dataProvider: marketInfoProvider,
skipUpdates: true,
variables: {
marketId: parentMarketId?.market?.parentMarketID || '',
skip:
!FLAGS.SUCCESSOR_MARKETS ||
!isSuccessor ||
!parentMarketId?.market?.parentMarketID,
},
});
const {
data: assetData,
loading: assetLoading,
@@ -149,39 +79,6 @@ export const ProposalContainer = () => {
),
});
useEffect(() => {
if (
previouslyEnactedMarketProposalsRestData &&
data?.proposal?.terms.change.__typename === 'UpdateMarket'
) {
const change = data?.proposal?.terms?.change as { marketId: string };
const filteredProposals =
// @ts-ignore rest data is not typed
previouslyEnactedMarketProposalsRestData.connection.edges.filter(
// @ts-ignore rest data is not typed
({ node }) =>
node?.proposal?.terms?.updateMarket?.marketId === change.marketId
);
const sortedProposals = filteredProposals.sort(
// @ts-ignore rest data is not typed
(a, b) =>
new Date(a?.node?.terms?.enactmentTimestamp).getTime() -
new Date(b?.node?.terms?.enactmentTimestamp).getTime()
);
setMostRecentlyEnactedAssociatedMarketProposal(
sortedProposals[sortedProposals.length - 1]
);
}
}, [
previouslyEnactedMarketProposalsRestData,
params.proposalId,
data?.proposal?.terms.change.__typename,
data?.proposal?.terms.change,
]);
useEffect(() => {
const interval = setInterval(refetch, 2000);
return () => clearInterval(interval);
@@ -190,44 +87,14 @@ export const ProposalContainer = () => {
return (
<AsyncRenderer
loading={
loading ||
newMarketLoading ||
assetLoading ||
networkParamsLoading ||
parentMarketIdLoading ||
parentMarketLoading ||
(restLoading ? (restLoading as boolean) : false) ||
(originalMarketProposalRestLoading
? (originalMarketProposalRestLoading as boolean)
: false) ||
(previouslyEnactedMarketProposalsRestLoading
? (previouslyEnactedMarketProposalsRestLoading as boolean)
: false)
}
error={
error ||
newMarketError ||
assetError ||
networkParamsError ||
parentMarketIdError ||
parentMarketError ||
restError ||
originalMarketProposalRestError ||
previouslyEnactedMarketProposalsRestError
loading || newMarketLoading || assetLoading || networkParamsLoading
}
error={error || newMarketError || assetError || networkParamsError}
data={{
...data,
...networkParams,
...(newMarketData ? { newMarketData } : {}),
...(parentMarketData ? { parentMarketData } : {}),
...(assetData ? { assetData } : {}),
...(restData ? { restData } : {}),
...(originalMarketProposalRestData
? { originalMarketProposalRestData }
: {}),
...(previouslyEnactedMarketProposalsRestData
? { previouslyEnactedMarketProposalsRestData }
: {}),
}}
>
{data?.proposal ? (
@@ -236,12 +103,7 @@ export const ProposalContainer = () => {
networkParams={networkParams}
restData={restData}
newMarketData={newMarketData}
parentMarketData={parentMarketData}
assetData={assetData}
originalMarketProposalRestData={originalMarketProposalRestData}
mostRecentlyEnactedAssociatedMarketProposal={
mostRecentlyEnactedAssociatedMarketProposal
}
/>
) : (
<ProposalNotFound />
@@ -5,6 +5,7 @@ import {
RewardsTable,
} from '../shared-rewards-table-assets/shared-rewards-table-assets';
import type { EpochIndividualReward } from './generate-epoch-individual-rewards-list';
import { useTranslation } from 'react-i18next';
interface EpochIndividualRewardsGridProps {
data: EpochIndividualReward;
@@ -21,11 +22,14 @@ interface RewardItemProps {
const DisplayReward = ({
reward,
decimals,
percentageOfTotal,
}: {
reward: string;
decimals: number;
percentageOfTotal?: string;
}) => {
const { t } = useTranslation();
if (Number(reward) === 0) {
return <span className="text-vega-dark-300">-</span>;
}
@@ -35,12 +39,23 @@ const DisplayReward = ({
description={
<div className="flex flex-col items-start">
<span>{formatNumber(toBigNum(reward, decimals), decimals)}</span>
{percentageOfTotal && (
<span className="text-vega-dark-300">
({percentageOfTotal}% {t('ofTotalDistributed')})
</span>
)}
</div>
}
>
<button>
<div className="flex flex-col items-start">
<span>{formatNumber(toBigNum(reward, decimals), 4)}</span>
{percentageOfTotal && (
<span className="text-vega-dark-300">
({formatNumber(percentageOfTotal, 4).toString()}
%)
</span>
)}
</div>
</button>
</Tooltip>
@@ -168,6 +168,17 @@ describe('generateEpochIndividualRewardsList', () => {
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,
epochRewardSummaries: [],
});
expect(result[0].rewards[0].totalAmount).toEqual('200');
});
it('returns data in the expected shape', () => {
// Just sanity checking the whole structure here
const rewards = [reward1, reward2, reward3, reward4];
@@ -447,4 +458,69 @@ describe('generateEpochIndividualRewardsList', () => {
},
]);
});
it('correctly calculates the percentage of two or more rewards by referencing the total rewards amount', () => {
const result = generateEpochIndividualRewardsList({
rewards: [
// reward1 is 100 usd, which is 10% of the total rewards amount
reward1,
{
rewardType: AccountType.ACCOUNT_TYPE_GLOBAL_REWARD,
amount: '200',
percentageOfTotal: '0.2',
receivedAt: new Date(),
asset: { id: 'usd', symbol: 'USD', name: 'USD', decimals: 6 },
party: { id: 'blah' },
epoch: { id: '1' },
},
],
epochId: 1,
epochRewardSummaries: [
{
__typename: 'EpochRewardSummary',
epoch: 1,
assetId: 'usd',
amount: '1000',
rewardType: AccountType.ACCOUNT_TYPE_GLOBAL_REWARD,
},
],
});
expect(result[0]).toEqual({
epoch: 1,
rewards: [
{
asset: 'USD',
decimals: 6,
totalAmount: '300',
rewardTypes: {
[AccountType.ACCOUNT_TYPE_FEES_INFRASTRUCTURE]: {
amount: '0',
percentageOfTotal: '0',
},
[AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES]: {
amount: '0',
percentageOfTotal: '0',
},
[AccountType.ACCOUNT_TYPE_GLOBAL_REWARD]: {
amount: '300',
percentageOfTotal: '30',
},
[AccountType.ACCOUNT_TYPE_REWARD_MAKER_PAID_FEES]: {
amount: '0',
percentageOfTotal: '0',
},
[AccountType.ACCOUNT_TYPE_REWARD_MAKER_RECEIVED_FEES]: {
amount: '0',
percentageOfTotal: '0',
},
[AccountType.ACCOUNT_TYPE_REWARD_MARKET_PROPOSERS]: {
amount: '0',
percentageOfTotal: '0',
},
},
},
],
});
});
});
@@ -59,6 +59,7 @@ export const generateEpochIndividualRewardsList = ({
const epochIndividualRewards = rewards.reduce((acc, reward) => {
const epochId = reward.epoch.id;
const assetName = reward.asset.name;
const assetId = reward.asset.id;
const assetDecimals = reward.asset.decimals;
const rewardType = reward.rewardType;
const amount = reward.amount;
@@ -76,6 +77,13 @@ export const generateEpochIndividualRewardsList = ({
const epoch = acc.get(epochId);
// matchingTotalReward is the total awarded for all users for the reward type in the epoch of the asset
const matchingTotalRewardAmount = epochRewardSummaries.find(
(summary) =>
summary.epoch === Number(epochId) &&
summary.assetId === assetId &&
summary.rewardType === rewardType
)?.amount;
let asset = epoch?.rewards.find((r) => r.asset === assetName);
if (!asset) {
@@ -98,7 +106,14 @@ export const generateEpochIndividualRewardsList = ({
asset.rewardTypes[rewardType] = {
amount: newAmount,
percentageOfTotal: percentageOfTotal,
percentageOfTotal: matchingTotalRewardAmount
? new BigNumber(newAmount)
.dividedBy(matchingTotalRewardAmount)
.multipliedBy(100)
.toString()
: // this should never be reached, if there's an individual reward there should
// always be a reward total from the api too, but set it as a fallback just in case
percentageOfTotal,
};
}
@@ -8,10 +8,9 @@ import { TxState } from '../../../hooks/transaction-reducer';
import { useTransaction } from '../../../hooks/use-transaction';
import { BigNumber } from '../../../lib/bignumber';
import { AssociateInfo } from './associate-info';
import { toBigNum } from '@vegaprotocol/utils';
import { removeDecimal, toBigNum } from '@vegaprotocol/utils';
import type { EthereumConfig } from '@vegaprotocol/web3';
import { useBalances } from '../../../lib/balances/balances-store';
import { MaxUint256 } from '@ethersproject/constants';
export const WalletAssociate = ({
perform,
@@ -43,7 +42,7 @@ export const WalletAssociate = ({
} = useTransaction(() => {
return token.approve(
ethereumConfig.staking_bridge_contract.address,
MaxUint256.toString()
removeDecimal('1000000', decimals).toString()
);
});
@@ -389,10 +389,10 @@ export const ConsensusValidatorsTable = ({
},
{
field: ValidatorFields.NORMALISED_VOTING_POWER,
headerName: t('votingPower').toString(),
headerName: t(ValidatorFields.NORMALISED_VOTING_POWER).toString(),
headerTooltip: t('NormalisedVotingPowerDescription').toString(),
cellRenderer: VotingPowerRenderer,
width: 120,
width: 200,
},
{
field: ValidatorFields.TOTAL_PENALTIES,
@@ -20,7 +20,6 @@ import {
TotalStakeRenderer,
StakeShareRenderer,
PendingStakeRenderer,
VotingPowerRenderer,
} from './shared';
import type { AgGridReact } from 'ag-grid-react';
import type { ColDef } from 'ag-grid-community';
@@ -40,6 +39,7 @@ interface StandbyPendingValidatorsTableProps extends ValidatorsTableProps {
export const StandbyPendingValidatorsTable = ({
data,
previousEpochData,
totalStake,
stakeNeededForPromotion,
stakeNeededForPromotionDescription,
validatorsView,
@@ -132,8 +132,6 @@ export const StandbyPendingValidatorsTable = ({
name,
},
[ValidatorFields.STAKE]: stakedTotal,
[ValidatorFields.NORMALISED_VOTING_POWER]: '0%',
[ValidatorFields.UNNORMALISED_VOTING_POWER]: '0%',
[ValidatorFields.STAKE_NEEDED_FOR_PROMOTION]:
individualStakeNeededForPromotion || null,
[ValidatorFields.STAKE_NEEDED_FOR_PROMOTION_DESCRIPTION]:
@@ -225,14 +223,7 @@ export const StandbyPendingValidatorsTable = ({
headerName: t(ValidatorFields.STAKE_SHARE).toString(),
headerTooltip: t('StakeShareDescription').toString(),
cellRenderer: StakeShareRenderer,
width: 120,
},
{
field: ValidatorFields.NORMALISED_VOTING_POWER,
headerName: t('votingPower').toString(),
headerTooltip: t('NonConsensusVotingPowerDescription').toString(),
cellRenderer: VotingPowerRenderer,
width: 120,
width: 100,
},
// {
// field: ValidatorFields.STAKE_NEEDED_FOR_PROMOTION,
@@ -17,10 +17,6 @@ import type { PreviousEpochQuery } from '../../__generated__/PreviousEpoch';
import type { StakingQuery } from '../../__generated__/Staking';
import type { StakingDelegationFieldsFragment } from '../../__generated__/Staking';
import type { ValidatorWithUserData } from './shared';
import {
NetworkParams,
useNetworkParams,
} from '@vegaprotocol/network-parameters';
export interface ValidatorsTableProps {
nodesData: NodesQuery | undefined;
@@ -45,9 +41,6 @@ export const ValidatorTables = ({
const {
appState: { decimals },
} = useAppState();
const { params } = useNetworkParams([
NetworkParams.network_validators_incumbentBonus,
]);
const [validatorsView, setValidatorsView] = useState<ValidatorsView>('all');
const totalStake = useMemo(
@@ -59,10 +52,6 @@ export const ValidatorTables = ({
() => userStakingData?.party?.stakingSummary.currentStakeAvailable || '0',
[userStakingData?.party?.stakingSummary.currentStakeAvailable]
);
const incumbentBonus = useMemo(
() => new BigNumber(params?.network_validators_incumbentBonus),
[params?.network_validators_incumbentBonus]
);
let stakeNeededForPromotion = undefined;
let delegations: StakingDelegationFieldsFragment[] | undefined = undefined;
@@ -137,23 +126,28 @@ export const ValidatorTables = ({
consensusValidators.length &&
(standbyValidators.length || pendingValidators.length)
) {
const lowestConsensusStake = consensusValidators.reduce(
const lowestRankingConsensusScore = consensusValidators.reduce(
(lowest: ValidatorWithUserData, validator: ValidatorWithUserData) => {
if (Number(validator.stakedTotal) < Number(lowest.stakedTotal)) {
if (
Number(validator.rankingScore.rankingScore) <
Number(lowest.rankingScore.rankingScore)
) {
lowest = validator;
}
return lowest;
}
).stakedTotal;
).rankingScore.rankingScore;
const lowestRankingBigNum = toBigNum(lowestConsensusStake, decimals);
const lowestRankingBigNum = toBigNum(lowestRankingConsensusScore, 0);
const consensusStakedTotal = consensusValidators.reduce((acc, cur) => {
return acc.plus(toBigNum(cur.stakedTotal, decimals));
}, new BigNumber(0));
stakeNeededForPromotion = formatNumber(
lowestRankingBigNum.multipliedBy(incumbentBonus.plus(1)),
lowestRankingBigNum.times(consensusStakedTotal),
2
).toString();
}
return (
<section data-testid="validator-tables">
<div className="grid w-full justify-end">
@@ -193,18 +187,12 @@ export const ValidatorTables = ({
<div className="mb-10">
<SubHeading title={t('status-ersatz')} />
<p>
<span>
<Trans i18nKey="ersatzDescription1" />
</span>
<span className="text-white font-bold">
{' '}
{stakeNeededForPromotion}{' '}
</span>
<span>
<Trans i18nKey="ersatzDescription2" />
</span>
<Trans
i18nKey="ersatzDescription"
values={{
stakeNeededForPromotion,
}}
/>
</p>
<StandbyPendingValidatorsTable
data={standbyValidators}
+1 -1
View File
@@ -68,7 +68,7 @@ export const calculateOverstakedPenalty = (nodeId: string, nodes: Node[]) => {
}
const penalty = new BigNumber(1)
.minus(
new BigNumber(node.rewardScore?.rawValidatorScore || 1).dividedBy(tts)
new BigNumber(node.rewardScore?.rawValidatorScore || 0).dividedBy(tts)
)
.times(100);
return penalty.isLessThan(0) ? new BigNumber(0) : penalty;
+1 -31
View File
@@ -62,7 +62,7 @@
cursor: pointer;
}
/* Styles required to (effectively) un-override the
/* Styles required to (effectively) un-override the
* reset styles so that the Proposal description fields
* render as you'd expect them to.
*
@@ -97,33 +97,3 @@
.react-markdown-container ul li {
list-style: circle;
}
.jsondiffpatch-delta,
.jsondiffpatch-delta pre {
font-family: 'Roboto Mono', monospace !important;
}
.jsondiffpatch-delta pre {
padding: 0 0.25em !important;
}
.jsondiffpatch-added .jsondiffpatch-property-name,
.jsondiffpatch-added .jsondiffpatch-value pre,
.jsondiffpatch-modified .jsondiffpatch-right-value pre,
.jsondiffpatch-textdiff-added {
background: theme(colors.vega.green[650]) !important;
color: theme(colors.white) !important;
}
.jsondiffpatch-deleted .jsondiffpatch-property-name,
.jsondiffpatch-deleted pre,
.jsondiffpatch-modified .jsondiffpatch-left-value pre,
.jsondiffpatch-textdiff-deleted {
background: theme(colors.vega.pink[650]) !important;
color: theme(colors.white) !important;
}
.jsondiffpatch-moved .jsondiffpatch-moved-destination {
background: theme(colors.vega.yellow[350]) !important;
color: theme(colors.vega.dark[200]) !important;
}
@@ -57,7 +57,7 @@ const ROWS = [
export const HealthDialog = ({ onChange, isOpen }: HealthDialogProps) => {
return (
<Dialog size="large" open={isOpen} onChange={onChange}>
<Dialog size="medium" open={isOpen} onChange={onChange}>
<h1 className="text-2xl mb-5 pr-2 font-medium font-alpha uppercase">
{t('Health')}
</h1>
-1
View File
@@ -26,7 +26,6 @@ module.exports = defineConfig({
requestTimeout: 20000,
retries: 1,
testIsolation: false,
experimentalMemoryManagement: true,
},
env: {
ETHERSCAN_URL: 'https://sepolia.etherscan.io',
@@ -0,0 +1,177 @@
// #region consts
const assetColId = '[col-id="asset.symbol"]';
const assetDetailsDialog = 'dialog-content';
const assetRow = 'key-value-table-row';
const contractAddress = '7_value';
const dialogCloseBtn = 'close-asset-details-dialog';
const dialogCloseX = 'dialog-close';
const dialogTitle = 'dialog-title';
const indicesWithLabelTooltips = [4, 5, 6, 7, 8, 9, 11, 12, 13, 14];
const indicesWithValueTooltips = [1, 6];
const labelValueToolTipPairs = [
{
label: 'ID',
value: 'asset-id',
},
{
label: 'Type',
value: 'ERC20',
valueToolTip: 'An asset originated from an Ethereum ERC20 Token',
},
{
label: 'Name',
value: 'Euro',
},
{
label: 'Symbol',
value: 'tEURO',
},
{
label: 'Decimals',
value: '5',
labelTooltip: 'Number of decimal / precision handled by this asset',
},
{
label: 'Quantum',
value: '0.00001',
labelTooltip: 'The minimum economically meaningful amount of the asset',
},
{
label: 'Status',
value: 'Enabled',
labelTooltip: 'The status of the asset in the Vega network',
valueToolTip: 'Asset can be used on the Vega network',
},
{
label: 'Contract address',
value: '0x0158031158Bb4dF2AD02eAA31e8963E84EA978a4 ',
labelTooltip:
'The address of the contract for the token, on the ethereum network',
},
{
label: 'Withdrawal threshold',
value: '0.0005',
labelTooltip:
"The maximum you can withdraw instantly. There's no limit on the size of a withdrawal, but all withdrawals over the threshold will have a delay time added to them",
},
{
label: 'Lifetime limit',
value: '1,230.00',
labelTooltip:
'The lifetime deposit limit per address. Note: this is a temporary measure that can be changed or removed through governance',
},
{ label: '', value: '' },
{
label: 'Infrastructure fee account balance',
value: '0.00001',
labelTooltip: 'The infrastructure fee account in this asset',
},
{
label: 'Global reward pool account balance',
value: '0.00002',
labelTooltip: 'The global rewards acquired in this asset',
},
{
label: 'Maker paid fees account balance',
value: '0.00003',
labelTooltip:
'The rewards acquired based on the fees paid to makers in this asset',
},
{
label: 'Maker received fees account balance',
value: '0.00004',
labelTooltip:
'The rewards acquired based on fees received for being a maker on trades',
},
{
label: 'Liquidity provision fee reward account balance',
value: '0.00005',
labelTooltip:
'The rewards acquired based on the liquidity provision fees in this asset',
},
{
label: 'Market proposer reward account balance',
value: '0.00006',
labelTooltip:
'The rewards acquired based on the market proposer reward in this asset',
},
];
//endregion
beforeEach(() => {
cy.mockTradingPage();
cy.mockSubscription();
cy.setVegaWallet();
});
const visitPortfolioAndClickAsset = (assetName: string) => {
cy.visit('/#/portfolio');
cy.get(assetColId).contains(assetName).click();
};
const testTooltip = (index: number, testId: string, tooltip: string) => {
cy.getByTestId(`${index}_${testId}`).realHover();
cy.get('[role="tooltip"]').find('div').should('have.text', tooltip);
cy.getByTestId(dialogTitle).click();
};
describe('assets', { tags: '@smoke', testIsolation: true }, () => {
it('asset details', () => {
visitPortfolioAndClickAsset('tBTC');
cy.getByTestId(assetRow).each((element, index) => {
if (index === 10) {
return;
}
const { label, value, labelTooltip, valueToolTip } =
labelValueToolTipPairs[index];
// 6501-ASSE-001
// 6501-ASSE-002
// 6501-ASSE-003
// 6501-ASSE-004
// 6501-ASSE-005
// 6501-ASSE-006
// 6501-ASSE-007
// 6501-ASSE-008
// 6501-ASSE-009
// 6501-ASSE-010
// 6501-ASSE-011
cy.getByTestId(`${index}_label`).should('have.text', label);
cy.getByTestId(`${index}_value`).should('have.text', value);
// 6501-ASSE-012
if (indicesWithLabelTooltips.includes(index)) {
if (labelTooltip) {
testTooltip(index, 'label', labelTooltip);
}
}
if (indicesWithValueTooltips.includes(index)) {
if (valueToolTip) {
testTooltip(index, 'value', valueToolTip);
}
}
});
// 6501-ASSE-013
cy.getByTestId(dialogCloseX).click();
cy.document().then((doc) => {
expect(doc.querySelector(assetDetailsDialog)).to.not.exist;
});
});
it('ERC20 Contract address', () => {
visitPortfolioAndClickAsset('tBTC');
cy.getByTestId(contractAddress).within(() => {
// 6501-ASSE-014
cy.getByTestId('external-link')
.should('have.attr', 'target', '_blank')
.should('have.text', '0x0158031158Bb4dF2AD02eAA31e8963E84EA978a4');
});
// 6501-ASSE-013
cy.getByTestId(dialogCloseBtn).click();
cy.document().then((doc) => {
expect(doc.querySelector(assetDetailsDialog)).to.not.exist;
});
});
});
@@ -0,0 +1,15 @@
describe('charts', { tags: '@smoke' }, () => {
before(() => {
cy.mockTradingPage();
cy.mockSubscription();
cy.visit('/#/markets/market-0');
cy.wait('@Markets');
cy.getByTestId('Depth').click();
});
it('can see market depth chart', () => {
// 6006-DEPC-001
cy.getByTestId('tab-depth').should('be.visible');
cy.get('.depth-chart-module_canvas__260De').should('be.visible');
});
});

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