Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f784bd552c |
@@ -1,74 +0,0 @@
|
|||||||
name: After Release
|
|
||||||
|
|
||||||
on:
|
|
||||||
release:
|
|
||||||
types: [published]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
after-release:
|
|
||||||
runs-on: ubuntu-22.04
|
|
||||||
timeout-minutes: 30
|
|
||||||
steps:
|
|
||||||
- name: Set up Docker Buildx
|
|
||||||
uses: docker/setup-buildx-action@v2
|
|
||||||
|
|
||||||
- name: Log in to the Container registry (ghcr)
|
|
||||||
uses: docker/login-action@v2
|
|
||||||
with:
|
|
||||||
registry: ghcr.io
|
|
||||||
username: ${{ github.actor }}
|
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
|
|
||||||
- name: Log in to the Container registry (docker hub)
|
|
||||||
uses: docker/login-action@v2
|
|
||||||
with:
|
|
||||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
|
||||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
|
||||||
|
|
||||||
- name: resolve ipfs hashes for release
|
|
||||||
run: |
|
|
||||||
echo "Tag name: ${{ github.event.release.tag_name }}"
|
|
||||||
echo "Name: ${{ github.event.release.name }}"
|
|
||||||
echo "Description: ${{ github.event.release.body }}"
|
|
||||||
until docker pull vegaprotocol/trading:${{ github.event.release.tag_name }}; do
|
|
||||||
echo "Image not pushed yet, waiting 60 seconds"
|
|
||||||
sleep 60
|
|
||||||
done
|
|
||||||
docker run --rm vegaprotocol/trading:${{ github.event.release.tag_name }} cat /ipfs-hash > ipfs-hash
|
|
||||||
curl -L https://dist.ipfs.tech/kubo/v0.20.0/kubo_v0.20.0_linux-amd64.tar.gz -o kubo.tgz
|
|
||||||
tar -xzf kubo.tgz
|
|
||||||
export PATH="$PATH:$PWD/kubo"
|
|
||||||
which ipfs
|
|
||||||
echo IPFS_V0=$(cat ipfs-hash) >> $GITHUB_ENV
|
|
||||||
echo IPFS_V1=$(ipfs cid format -v 1 -b base32 $(cat ipfs-hash)) >> $GITHUB_ENV
|
|
||||||
|
|
||||||
- name: Edit Release
|
|
||||||
uses: irongut/EditRelease@v1.2.0
|
|
||||||
with:
|
|
||||||
token: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
id: ${{ github.event.release.id }}
|
|
||||||
body: |
|
|
||||||
---
|
|
||||||
|
|
||||||
# Deployments
|
|
||||||
* https://explorer.vega.xyz
|
|
||||||
* https://governance.vega.xyz
|
|
||||||
|
|
||||||
# IPFS releases
|
|
||||||
Tye IPFS hash of this release of the Trading app is:
|
|
||||||
|
|
||||||
CIDv0: ${{ env.IPFS_V0 }}
|
|
||||||
CIDv1: ${{ env.IPFS_V1 }}
|
|
||||||
|
|
||||||
You can always access the latest IPFS release by visiting [vega.trading](https://vega.trading).
|
|
||||||
|
|
||||||
You can also access Trading directly from an IPFS gateway.
|
|
||||||
BEWARE: The Trading interface uses [localStorage](https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage) to remember your settings, such as which tokens you have imported. You should always use an IPFS gateway that enforces [origin separation](https://ipfs.github.io/public-gateway-checker/).
|
|
||||||
|
|
||||||
Your settings are not remembered across different URLs.
|
|
||||||
|
|
||||||
IPFS gateways:
|
|
||||||
|
|
||||||
https://${{ env.IPFS_V1 }}.ipfs.dweb.link/
|
|
||||||
https://${{ env.IPFS_V1 }}.ipfs.cf-ipfs.com/
|
|
||||||
ipfs://${{ env.IPFS_V0 }}/
|
|
||||||
@@ -33,4 +33,5 @@ jobs:
|
|||||||
config: baseUrl=${{ github.event.inputs.url }}
|
config: baseUrl=${{ github.event.inputs.url }}
|
||||||
env: grepTags=@live
|
env: grepTags=@live
|
||||||
env:
|
env:
|
||||||
|
CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }}
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ jobs:
|
|||||||
project: ${{ fromJSON(inputs.projects) }}
|
project: ${{ fromJSON(inputs.projects) }}
|
||||||
name: ${{ matrix.project }}
|
name: ${{ matrix.project }}
|
||||||
runs-on: self-hosted-runner
|
runs-on: self-hosted-runner
|
||||||
timeout-minutes: 60
|
timeout-minutes: 40
|
||||||
steps:
|
steps:
|
||||||
# Checks if skip cache was requested
|
# Checks if skip cache was requested
|
||||||
- name: Set skip-nx-cache flag
|
- name: Set skip-nx-cache flag
|
||||||
@@ -66,7 +66,7 @@ jobs:
|
|||||||
######
|
######
|
||||||
|
|
||||||
- name: Run Cypress tests
|
- name: Run Cypress tests
|
||||||
run: yarn nx run ${{ matrix.project }}:e2e ${{ env.SKIP_CACHE }} --browser chrome --env.grepTags="${{ inputs.tags }}"
|
run: yarn nx run ${{ matrix.project }}:e2e ${{ env.SKIP_CACHE }} --record --key ${{ secrets.CYPRESS_RECORD_KEY }} --browser chrome --env.grepTags="${{ inputs.tags }}"
|
||||||
working-directory: frontend-monorepo
|
working-directory: frontend-monorepo
|
||||||
env:
|
env:
|
||||||
CYPRESS_SLACK_WEBHOOK: ${{ secrets.CYPRESS_SLACK_WEBHOOK }}
|
CYPRESS_SLACK_WEBHOOK: ${{ secrets.CYPRESS_SLACK_WEBHOOK }}
|
||||||
@@ -98,4 +98,4 @@ jobs:
|
|||||||
if: ${{ failure() }}
|
if: ${{ failure() }}
|
||||||
with:
|
with:
|
||||||
name: test-report-${{ matrix.project }}
|
name: test-report-${{ matrix.project }}
|
||||||
path: frontend-monorepo/apps/${{ matrix.project }}/cypress/reports
|
path: frontend-monorepo/apps/trading-e2e/cypress/reports
|
||||||
|
|||||||
@@ -119,7 +119,7 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
echo "Check ipfs-hash"
|
echo "Check ipfs-hash"
|
||||||
docker run --rm ghcr.io/vegaprotocol/frontend/${{ matrix.app }}:local cat /ipfs-hash
|
docker run --rm ghcr.io/vegaprotocol/frontend/${{ matrix.app }}:local cat /ipfs-hash
|
||||||
docker run --rm ghcr.io/vegaprotocol/frontend/${{ matrix.app }}:local cat /ipfs-hash > ${{ matrix.app }}-ipfs-hash
|
docker run --rm ghcr.io/vegaprotocol/frontend/${{ matrix.app }}:local cat /ipfs-hash > ${{ matirx.app }}-ipfs-hash
|
||||||
echo "List html directory"
|
echo "List html directory"
|
||||||
docker run --rm ghcr.io/vegaprotocol/frontend/${{ matrix.app }}:local sh -c 'apk add --update tree; tree /usr/share/nginx/html'
|
docker run --rm ghcr.io/vegaprotocol/frontend/${{ matrix.app }}:local sh -c 'apk add --update tree; tree /usr/share/nginx/html'
|
||||||
|
|
||||||
@@ -148,7 +148,6 @@ jobs:
|
|||||||
ENV_NAME=${{ env.ENV_NAME }}
|
ENV_NAME=${{ env.ENV_NAME }}
|
||||||
tags: |
|
tags: |
|
||||||
vegaprotocol/${{ matrix.app }}:${{ github.ref_name }}
|
vegaprotocol/${{ matrix.app }}:${{ github.ref_name }}
|
||||||
vegaprotocol/${{ matrix.app }}:mainnet
|
|
||||||
|
|
||||||
# bucket creation in github.com/vegaprotocol/terraform//frontend
|
# bucket creation in github.com/vegaprotocol/terraform//frontend
|
||||||
- name: Publish dist to s3
|
- name: Publish dist to s3
|
||||||
@@ -170,51 +169,8 @@ jobs:
|
|||||||
labels: ${{ matrix.app }}-preview
|
labels: ${{ matrix.app }}-preview
|
||||||
number: ${{ github.event.number }}
|
number: ${{ github.event.number }}
|
||||||
|
|
||||||
- name: Trigger fleek deployment
|
- name: Add ipfs hash to release
|
||||||
|
uses: softprops/action-gh-release@v1
|
||||||
if: ${{ matrix.app == 'trading' && github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v') }}
|
if: ${{ matrix.app == 'trading' && github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v') }}
|
||||||
run: |
|
with:
|
||||||
# display info about app
|
files: ${{ matrix.app }}-ipfs-hash
|
||||||
curl -H "Authorization: ${{ secrets.FLEEK_API_KEY }}" \
|
|
||||||
-H "Content-Type: application/json" \
|
|
||||||
-d '{"query": "query{getSiteById(siteId:\"f8f2e051-f18e-49e6-b876-0a39369dc0d8\"){id latestDeploy{id status}}}"}' \
|
|
||||||
https://api.fleek.co/graphql
|
|
||||||
|
|
||||||
# trigger new deployment as base image is always set to vegaprotocol/trading:mainnet
|
|
||||||
curl -H "Authorization: ${{ secrets.FLEEK_API_KEY }}" \
|
|
||||||
-H "Content-Type: application/json" \
|
|
||||||
-d '{"query": "mutation{triggerDeploy(siteId:\"f8f2e051-f18e-49e6-b876-0a39369dc0d8\"){id status}}"}' \
|
|
||||||
https://api.fleek.co/graphql
|
|
||||||
|
|
||||||
- name: Update vega.trading DNS to redirect to the new console
|
|
||||||
if: ${{ matrix.app == 'trading' && github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v') }}
|
|
||||||
run: |
|
|
||||||
curl -L https://dist.ipfs.tech/kubo/v0.20.0/kubo_v0.20.0_linux-amd64.tar.gz -o kubo.tgz
|
|
||||||
tar -xzf kubo.tgz
|
|
||||||
export PATH="$PATH:$PWD/kubo"
|
|
||||||
which ipfs
|
|
||||||
new_hash=$(cat ${{ matrix.app }}-ipfs-hash)
|
|
||||||
new_cid=$(ipfs cid format -v 1 -b base32 $new_hash)
|
|
||||||
|
|
||||||
# Generate console URL
|
|
||||||
new_console_url_type=ipfs
|
|
||||||
# new_console_url_type=ipns
|
|
||||||
new_console_url_domain=cf-ipfs.com
|
|
||||||
# new_console_url_domain=dweb.link
|
|
||||||
new_console_url="https://${new_cid}.${new_console_url_type}.${new_console_url_domain}/"
|
|
||||||
echo "new_console_url=${new_console_url}"
|
|
||||||
|
|
||||||
# Update record in DNSimple
|
|
||||||
# docs: https://developer.dnsimple.com/v2/zones/records/#updateZoneRecord
|
|
||||||
dnsimple_account_id=84895
|
|
||||||
dnsimple_zone_name=vega.trading
|
|
||||||
dnsimple_record_id=44409591
|
|
||||||
# see: https://dnsimple.com/a/84895/domains/vega.trading/records/44409591/edit
|
|
||||||
|
|
||||||
curl -H 'Authorization: Bearer ${{ secrets.DNSIMPLE_API_TOKEN }}' \
|
|
||||||
-H 'Accept: application/json' \
|
|
||||||
-H 'Content-Type: application/json' \
|
|
||||||
-X PATCH \
|
|
||||||
-d "{
|
|
||||||
\"content\": \"${new_console_url}\"
|
|
||||||
}" \
|
|
||||||
https://api.dnsimple.com/v2/${dnsimple_account_id}/zones/${dnsimple_zone_name}/records/${dnsimple_record_id}
|
|
||||||
|
|||||||
@@ -1,77 +0,0 @@
|
|||||||
name: 'Rollback console'
|
|
||||||
|
|
||||||
on:
|
|
||||||
workflow_dispatch:
|
|
||||||
inputs:
|
|
||||||
version:
|
|
||||||
description: 'Version that should be set on rollback'
|
|
||||||
required: true
|
|
||||||
type: string
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
rollback:
|
|
||||||
runs-on: ubuntu-22.04
|
|
||||||
timeout-minutes: 10
|
|
||||||
steps:
|
|
||||||
- name: Set up Docker Buildx
|
|
||||||
uses: docker/setup-buildx-action@v2
|
|
||||||
|
|
||||||
- name: Log in to the Container registry (docker hub)
|
|
||||||
uses: docker/login-action@v2
|
|
||||||
with:
|
|
||||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
|
||||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
|
||||||
|
|
||||||
- name: Retag mainnet
|
|
||||||
run: |
|
|
||||||
docker pull vegaprotocol/trading:${{ inputs.version }}
|
|
||||||
docker tag vegaprotocol/trading:${{ inputs.version }} vegaprotocol/trading:mainnet
|
|
||||||
docker push vegaprotocol/trading:mainnet
|
|
||||||
docker run --rm vegaprotocol/trading:mainnet cat /ipfs-hash > ipfs-hash
|
|
||||||
|
|
||||||
- name: Trigger fleek deployment
|
|
||||||
run: |
|
|
||||||
# display info about app
|
|
||||||
curl -H "Authorization: ${{ secrets.FLEEK_API_KEY }}" \
|
|
||||||
-H "Content-Type: application/json" \
|
|
||||||
-d '{"query": "query{getSiteById(siteId:\"f8f2e051-f18e-49e6-b876-0a39369dc0d8\"){id latestDeploy{id status}}}"}' \
|
|
||||||
https://api.fleek.co/graphql
|
|
||||||
|
|
||||||
# trigger new deployment as base image is always set to vegaprotocol/trading:mainnet
|
|
||||||
curl -H "Authorization: ${{ secrets.FLEEK_API_KEY }}" \
|
|
||||||
-H "Content-Type: application/json" \
|
|
||||||
-d '{"query": "mutation{triggerDeploy(siteId:\"f8f2e051-f18e-49e6-b876-0a39369dc0d8\"){id status}}"}' \
|
|
||||||
https://api.fleek.co/graphql
|
|
||||||
|
|
||||||
- name: Update vega.trading DNS to redirect to the new console
|
|
||||||
run: |
|
|
||||||
curl -L https://dist.ipfs.tech/kubo/v0.20.0/kubo_v0.20.0_linux-amd64.tar.gz -o kubo.tgz
|
|
||||||
tar -xzf kubo.tgz
|
|
||||||
export PATH="$PATH:$PWD/kubo"
|
|
||||||
which ipfs
|
|
||||||
new_hash=$(cat ipfs-hash)
|
|
||||||
new_cid=$(ipfs cid format -v 1 -b base32 $new_hash)
|
|
||||||
|
|
||||||
# Generate console URL
|
|
||||||
new_console_url_type=ipfs
|
|
||||||
# new_console_url_type=ipns
|
|
||||||
new_console_url_domain=cf-ipfs.com
|
|
||||||
# new_console_url_domain=dweb.link
|
|
||||||
new_console_url="https://${new_cid}.${new_console_url_type}.${new_console_url_domain}/"
|
|
||||||
echo "new_console_url=${new_console_url}"
|
|
||||||
|
|
||||||
# Update record in DNSimple
|
|
||||||
# docs: https://developer.dnsimple.com/v2/zones/records/#updateZoneRecord
|
|
||||||
dnsimple_account_id=84895
|
|
||||||
dnsimple_zone_name=vega.trading
|
|
||||||
dnsimple_record_id=44409591
|
|
||||||
# see: https://dnsimple.com/a/84895/domains/vega.trading/records/44409591/edit
|
|
||||||
|
|
||||||
curl -H 'Authorization: Bearer ${{ secrets.DNSIMPLE_API_TOKEN }}' \
|
|
||||||
-H 'Accept: application/json' \
|
|
||||||
-H 'Content-Type: application/json' \
|
|
||||||
-X PATCH \
|
|
||||||
-d "{
|
|
||||||
\"content\": \"${new_console_url}\"
|
|
||||||
}" \
|
|
||||||
https://api.dnsimple.com/v2/${dnsimple_account_id}/zones/${dnsimple_zone_name}/records/${dnsimple_record_id}
|
|
||||||
+1
-1
@@ -49,4 +49,4 @@ cypress.env.json
|
|||||||
.next
|
.next
|
||||||
|
|
||||||
#cypress
|
#cypress
|
||||||
/apps/**/cypress/reports/
|
/apps/trading-e2e/cypress/reports/
|
||||||
|
|||||||
@@ -1,20 +1,20 @@
|
|||||||
.PHONY: latest-release
|
.PHONY: latest-release
|
||||||
latest-release:
|
latest-release:
|
||||||
gh release list | head -n 1 | awk '{print $1}'
|
gh release list | head -n 1 | awk '{print $1}'
|
||||||
|
|
||||||
.PHONY: show-latest-release
|
.PHONY: show-latest-release
|
||||||
show-latest-release:
|
show-latest-release:
|
||||||
gh release view `gh release list | head -n 1 | awk '{print $1}'`
|
gh release view `gh release list | head -n 1 | awk '{print $1}'`
|
||||||
|
|
||||||
.PHONY: recalculate-ipfs
|
.PHONY: recalculate-ipfs
|
||||||
recalculate-ipfs:
|
recalculate-ipfs:
|
||||||
echo "ipfs hash inside the image"
|
echo "ipfs hash inside the image"
|
||||||
docker run --rm ${TAG} cat /ipfs-hash
|
docker run --rm ${TAG} cat /ipfs-hash
|
||||||
echo "recalculating ipfs hash"
|
echo "recalculating ipfs hash"
|
||||||
docker run --rm ${TAG} ipfs add -r /usr/share/nginx/html
|
docker run --rm ${TAG} ipfs add -rw /usr/share/nginx/html
|
||||||
|
|
||||||
.PHONY: eject-ipfs-hash
|
.PHONY: eject-ipfs-hash
|
||||||
unpack:
|
unpack:
|
||||||
docker create --name=dist ${TAG}
|
docker create --name=dist ${TAG}
|
||||||
docker cp dist:/usr/share/nginx/html dist
|
docker cp dist:/usr/share/nginx/html dist
|
||||||
docker rm dist
|
docker rm dist
|
||||||
|
|||||||
@@ -1,11 +1,10 @@
|
|||||||
const { defineConfig } = require('cypress');
|
const { defineConfig } = require('cypress');
|
||||||
|
|
||||||
module.exports = defineConfig({
|
module.exports = defineConfig({
|
||||||
reporter: '../../node_modules/cypress-mochawesome-reporter',
|
projectId: 'et4snf',
|
||||||
|
|
||||||
e2e: {
|
e2e: {
|
||||||
setupNodeEvents(on, config) {
|
setupNodeEvents(on, config) {
|
||||||
require('cypress-mochawesome-reporter/plugin')(on);
|
|
||||||
require('@cypress/grep/src/plugin')(config);
|
require('@cypress/grep/src/plugin')(config);
|
||||||
return config;
|
return config;
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -15,6 +15,5 @@
|
|||||||
|
|
||||||
import '@vegaprotocol/cypress';
|
import '@vegaprotocol/cypress';
|
||||||
import './common.functions.js';
|
import './common.functions.js';
|
||||||
import 'cypress-mochawesome-reporter/register';
|
|
||||||
import registerCypressGrep from '@cypress/grep';
|
import registerCypressGrep from '@cypress/grep';
|
||||||
registerCypressGrep();
|
registerCypressGrep();
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
NX_TENDERMINT_URL=https://be.vega.community
|
NX_TENDERMINT_URL=https://be.vega.community
|
||||||
NX_TENDERMINT_WEBSOCKET_URL=wss://be.vega.community/websocket
|
NX_TENDERMINT_WEBSOCKET_URL=wss://be.vega.community/websocket
|
||||||
NX_VEGA_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/networks/master/mainnet1/mainnet1.toml
|
NX_VEGA_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/networks/master/mainnet1/mainnet1.toml
|
||||||
NX_VEGA_URL=https://api.vega.community/graphql
|
|
||||||
NX_VEGA_ENV=MAINNET
|
NX_VEGA_ENV=MAINNET
|
||||||
NX_BLOCK_EXPLORER=https://be.vega.community/rest/
|
NX_BLOCK_EXPLORER=https://be.vega.community/rest/
|
||||||
NX_ETHERSCAN_URL=https://etherscan.io
|
NX_ETHERSCAN_URL=https://etherscan.io
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ import { Header } from './header';
|
|||||||
import { MemoryRouter } from 'react-router-dom';
|
import { MemoryRouter } from 'react-router-dom';
|
||||||
|
|
||||||
jest.mock('@vegaprotocol/environment', () => ({
|
jest.mock('@vegaprotocol/environment', () => ({
|
||||||
...jest.requireActual('@vegaprotocol/environment'),
|
|
||||||
NetworkSwitcher: () => (
|
NetworkSwitcher: () => (
|
||||||
<div data-testid="network-switcher">NetworkSwitcher</div>
|
<div data-testid="network-switcher">NetworkSwitcher</div>
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import { t } from '@vegaprotocol/i18n';
|
import { t } from '@vegaprotocol/i18n';
|
||||||
import type { MarketInfoWithData } from '@vegaprotocol/markets';
|
import type { MarketInfoWithData } from '@vegaprotocol/market-info';
|
||||||
|
import { LiquidityInfoPanel } from '@vegaprotocol/market-info';
|
||||||
|
import { LiquidityMonitoringParametersInfoPanel } from '@vegaprotocol/market-info';
|
||||||
import {
|
import {
|
||||||
LiquidityInfoPanel,
|
|
||||||
LiquidityMonitoringParametersInfoPanel,
|
|
||||||
InstrumentInfoPanel,
|
InstrumentInfoPanel,
|
||||||
KeyDetailsInfoPanel,
|
KeyDetailsInfoPanel,
|
||||||
LiquidityPriceRangeInfoPanel,
|
LiquidityPriceRangeInfoPanel,
|
||||||
@@ -12,8 +12,8 @@ import {
|
|||||||
RiskModelInfoPanel,
|
RiskModelInfoPanel,
|
||||||
RiskParametersInfoPanel,
|
RiskParametersInfoPanel,
|
||||||
SettlementAssetInfoPanel,
|
SettlementAssetInfoPanel,
|
||||||
} from '@vegaprotocol/markets';
|
} from '@vegaprotocol/market-info';
|
||||||
import { MarketInfoTable } from '@vegaprotocol/markets';
|
import { MarketInfoTable } from '@vegaprotocol/market-info';
|
||||||
import type { DataSourceDefinition } from '@vegaprotocol/types';
|
import type { DataSourceDefinition } from '@vegaprotocol/types';
|
||||||
import isEqual from 'lodash/isEqual';
|
import isEqual from 'lodash/isEqual';
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import type { MarketFieldsFragment } from '@vegaprotocol/markets';
|
import type { MarketFieldsFragment } from '@vegaprotocol/market-list';
|
||||||
import { t } from '@vegaprotocol/i18n';
|
import { t } from '@vegaprotocol/i18n';
|
||||||
import { ButtonLink } from '@vegaprotocol/ui-toolkit';
|
import { ButtonLink } from '@vegaprotocol/ui-toolkit';
|
||||||
import type { AgGridReact } from 'ag-grid-react';
|
import type { AgGridReact } from 'ag-grid-react';
|
||||||
|
|||||||
+2
-8
@@ -1,5 +1,3 @@
|
|||||||
import { Icon } from '@vegaprotocol/ui-toolkit';
|
|
||||||
|
|
||||||
// https://github.com/vegaprotocol/vega/blob/develop/core/blockchain/response.go
|
// https://github.com/vegaprotocol/vega/blob/develop/core/blockchain/response.go
|
||||||
export const ErrorCodes = new Map([
|
export const ErrorCodes = new Map([
|
||||||
[51, 'Transaction failed validation'],
|
[51, 'Transaction failed validation'],
|
||||||
@@ -30,11 +28,7 @@ export const ChainResponseCode = ({
|
|||||||
}: ChainResponseCodeProps) => {
|
}: ChainResponseCodeProps) => {
|
||||||
const isSuccess = successCodes.has(code);
|
const isSuccess = successCodes.has(code);
|
||||||
|
|
||||||
const icon = isSuccess ? (
|
const icon = isSuccess ? '✅' : '❌';
|
||||||
<Icon name="tick-circle" className="fill-vega-green-550" />
|
|
||||||
) : (
|
|
||||||
<Icon name="cross" className="fill-vega-pink-550" />
|
|
||||||
);
|
|
||||||
const label = ErrorCodes.get(code) || 'Unknown response code';
|
const label = ErrorCodes.get(code) || 'Unknown response code';
|
||||||
|
|
||||||
// Hack for batches with many errors - see https://github.com/vegaprotocol/vega/issues/7245
|
// Hack for batches with many errors - see https://github.com/vegaprotocol/vega/issues/7245
|
||||||
@@ -42,7 +36,7 @@ export const ChainResponseCode = ({
|
|||||||
error && error.length > 100 ? error.replace(/,/g, ',\r\n') : error;
|
error && error.length > 100 ? error.replace(/,/g, ',\r\n') : error;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div title={`Response code: ${code} - ${label}`} className=" inline-block">
|
<div title={`Response code: ${code} - ${label}`} className="inline-block">
|
||||||
<span
|
<span
|
||||||
className="mr-2"
|
className="mr-2"
|
||||||
aria-label={isSuccess ? 'Success' : 'Warning'}
|
aria-label={isSuccess ? 'Success' : 'Warning'}
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import type { TendermintBlocksResponse } from '../../../routes/blocks/tendermint
|
|||||||
import { TxDetailsShared } from './shared/tx-details-shared';
|
import { TxDetailsShared } from './shared/tx-details-shared';
|
||||||
import { TableCell, TableRow, TableWithTbody } from '../../table';
|
import { TableCell, TableRow, TableWithTbody } from '../../table';
|
||||||
import ProposalLink from '../../links/proposal-link/proposal-link';
|
import ProposalLink from '../../links/proposal-link/proposal-link';
|
||||||
import { VoteIcon } from '../../vote-icon/vote-icon';
|
|
||||||
|
|
||||||
interface TxProposalVoteProps {
|
interface TxProposalVoteProps {
|
||||||
txData: BlockExplorerTransactionResult | undefined;
|
txData: BlockExplorerTransactionResult | undefined;
|
||||||
@@ -31,22 +30,27 @@ export const TxProposalVote = ({
|
|||||||
return <>{t('Awaiting Block Explorer transaction details')}</>;
|
return <>{t('Awaiting Block Explorer transaction details')}</>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const vote = txData.command.voteSubmission.value === 'VALUE_YES';
|
const vote = txData.command.voteSubmission.value ? '👍' : '👎';
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<TableWithTbody className="mb-8" allowWrap={true}>
|
<TableWithTbody className="mb-8" allowWrap={true}>
|
||||||
<TxDetailsShared txData={txData} pubKey={pubKey} blockData={blockData} />
|
<TxDetailsShared txData={txData} pubKey={pubKey} blockData={blockData} />
|
||||||
|
<TableRow modifier="bordered">
|
||||||
|
<TableCell>{t('Proposal ID')}</TableCell>
|
||||||
|
<TableCell>{txData.command.voteSubmission.proposalId}</TableCell>
|
||||||
|
</TableRow>
|
||||||
<TableRow modifier="bordered">
|
<TableRow modifier="bordered">
|
||||||
<TableCell>{t('Proposal details')}</TableCell>
|
<TableCell>{t('Proposal details')}</TableCell>
|
||||||
<TableCell>
|
<TableCell>
|
||||||
<ProposalLink id={txData.command.voteSubmission.proposalId} />
|
<ProposalLink id={txData.command.voteSubmission.proposalId} />
|
||||||
</TableCell>
|
</TableCell>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
|
<TableRow modifier="bordered">
|
||||||
|
<TableCell>{t('Proposal')}</TableCell>
|
||||||
|
<TableCell>{txData.command.voteSubmission.proposalId}</TableCell>
|
||||||
|
</TableRow>
|
||||||
<TableRow modifier="bordered">
|
<TableRow modifier="bordered">
|
||||||
<TableCell>{t('Vote')}</TableCell>
|
<TableCell>{t('Vote')}</TableCell>
|
||||||
<TableCell>
|
<TableCell>{vote}</TableCell>
|
||||||
<VoteIcon vote={vote} />
|
|
||||||
</TableCell>
|
|
||||||
</TableRow>
|
</TableRow>
|
||||||
</TableWithTbody>
|
</TableWithTbody>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import { t } from '@vegaprotocol/i18n';
|
import { t } from '@vegaprotocol/i18n';
|
||||||
import type { components } from '../../../types/explorer';
|
import type { components } from '../../../types/explorer';
|
||||||
import { VoteIcon } from '../vote-icon/vote-icon';
|
|
||||||
|
|
||||||
interface TxOrderTypeProps {
|
interface TxOrderTypeProps {
|
||||||
orderType: string;
|
orderType: string;
|
||||||
@@ -138,15 +137,12 @@ export const TxOrderType = ({ orderType, command }: TxOrderTypeProps) => {
|
|||||||
let type = displayString[orderType] || orderType;
|
let type = displayString[orderType] || orderType;
|
||||||
|
|
||||||
let colours =
|
let colours =
|
||||||
'text-white dark:text-white bg-vega-dark-150 dark:bg-vega-dark-250';
|
'text-white dark:text-white bg-vega-dark-150 dark:bg-vega-dark-150';
|
||||||
|
|
||||||
// This will get unwieldy and should probably produce a different colour of tag
|
// This will get unwieldy and should probably produce a different colour of tag
|
||||||
if (type === 'Chain Event' && !!command?.chainEvent) {
|
if (type === 'Chain Event' && !!command?.chainEvent) {
|
||||||
type = getLabelForChainEvent(command.chainEvent);
|
type = getLabelForChainEvent(command.chainEvent);
|
||||||
colours = 'text-white dark-text-white bg-vega-pink dark:bg-vega-pink';
|
colours = 'text-white dark-text-white bg-vega-pink dark:bg-vega-pink';
|
||||||
} else if (type === 'Validator Heartbeat') {
|
|
||||||
colours =
|
|
||||||
'text-white dark-text-white bg-vega-light-200 dark:bg-vega-dark-100';
|
|
||||||
} else if (type === 'Proposal' || type === 'Governance Proposal') {
|
} else if (type === 'Proposal' || type === 'Governance Proposal') {
|
||||||
if (command && !!command.proposalSubmission) {
|
if (command && !!command.proposalSubmission) {
|
||||||
type = getLabelForProposal(command.proposalSubmission);
|
type = getLabelForProposal(command.proposalSubmission);
|
||||||
@@ -154,16 +150,6 @@ export const TxOrderType = ({ orderType, command }: TxOrderTypeProps) => {
|
|||||||
colours = 'text-black bg-vega-yellow';
|
colours = 'text-black bg-vega-yellow';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (type === 'Vote on Proposal') {
|
|
||||||
return (
|
|
||||||
<VoteIcon
|
|
||||||
vote={command?.voteSubmission?.value === 'VALUE_YES'}
|
|
||||||
yesText="Proposal vote"
|
|
||||||
noText="Proposal vote"
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (type === 'Vote on Proposal' || type === 'Vote Submission') {
|
if (type === 'Vote on Proposal' || type === 'Vote Submission') {
|
||||||
colours = 'text-black bg-vega-yellow';
|
colours = 'text-black bg-vega-yellow';
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -98,6 +98,6 @@ describe('Txs infinite list item', () => {
|
|||||||
expect(screen.getByTestId('pub-key')).toHaveTextContent('testPubKey');
|
expect(screen.getByTestId('pub-key')).toHaveTextContent('testPubKey');
|
||||||
expect(screen.getByTestId('tx-type')).toHaveTextContent('testType');
|
expect(screen.getByTestId('tx-type')).toHaveTextContent('testType');
|
||||||
expect(screen.getByTestId('tx-block')).toHaveTextContent('1');
|
expect(screen.getByTestId('tx-block')).toHaveTextContent('1');
|
||||||
expect(screen.getByTestId('tx-success')).toHaveTextContent('Success');
|
expect(screen.getByTestId('tx-success')).toHaveTextContent('Success: ✅');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ export const TxsInfiniteListItem = ({
|
|||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
data-testid="transaction-row"
|
data-testid="transaction-row"
|
||||||
className="flex items-center h-full border-t border-neutral-600 dark:border-neutral-800 txs-infinite-list-item grid grid-cols-10"
|
className="flex items-center h-full border-t border-neutral-600 dark:border-neutral-800 txs-infinite-list-item grid grid-cols-10 py-2"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className="text-sm col-span-10 md:col-span-3 leading-none"
|
className="text-sm col-span-10 md:col-span-3 leading-none"
|
||||||
@@ -83,7 +83,7 @@ export const TxsInfiniteListItem = ({
|
|||||||
data-testid="tx-success"
|
data-testid="tx-success"
|
||||||
>
|
>
|
||||||
<span className="md:hidden uppercase text-vega-dark-300">
|
<span className="md:hidden uppercase text-vega-dark-300">
|
||||||
Success
|
Success:
|
||||||
</span>
|
</span>
|
||||||
{isNumber(code) ? (
|
{isNumber(code) ? (
|
||||||
<ChainResponseCode code={code} hideLabel={true} />
|
<ChainResponseCode code={code} hideLabel={true} />
|
||||||
|
|||||||
@@ -1,37 +0,0 @@
|
|||||||
import { render } from '@testing-library/react';
|
|
||||||
import { VoteIcon } from './vote-icon';
|
|
||||||
|
|
||||||
describe('Vote TX icon', () => {
|
|
||||||
it('should use the text For by default for yes votes', () => {
|
|
||||||
const yes = render(<VoteIcon vote={true} />);
|
|
||||||
expect(yes.getByTestId('label')).toHaveTextContent('For');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should use the yesText for yes votes if specified', () => {
|
|
||||||
const yes = render(<VoteIcon vote={true} yesText="Test" />);
|
|
||||||
expect(yes.getByTestId('label')).toHaveTextContent('Test');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should display the tick icon for yes votes', () => {
|
|
||||||
const no = render(<VoteIcon vote={true} />);
|
|
||||||
expect(no.getByRole('img')).toHaveAttribute(
|
|
||||||
'aria-label',
|
|
||||||
'tick-circle icon'
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should use the text Against by default for no votes', () => {
|
|
||||||
const no = render(<VoteIcon vote={false} />);
|
|
||||||
expect(no.getByTestId('label')).toHaveTextContent('Against');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should use the noText for no votes if specified', () => {
|
|
||||||
const no = render(<VoteIcon vote={false} noText="Test" />);
|
|
||||||
expect(no.getByTestId('label')).toHaveTextContent('Test');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should display the delete icon for no votes', () => {
|
|
||||||
const no = render(<VoteIcon vote={false} />);
|
|
||||||
expect(no.getByRole('img')).toHaveAttribute('aria-label', 'delete icon');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
import { Icon } from '@vegaprotocol/ui-toolkit';
|
|
||||||
import type { IconName } from '@vegaprotocol/ui-toolkit';
|
|
||||||
|
|
||||||
export interface VoteIconProps {
|
|
||||||
// True is a yes vote, false is undefined or no vorte
|
|
||||||
vote: boolean;
|
|
||||||
// Defaults to 'For', but can be any text
|
|
||||||
yesText?: string;
|
|
||||||
// Defaults to 'Against', but can be any text
|
|
||||||
noText?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Displays a lozenge with an icon representing the way a user voted for a proposal.
|
|
||||||
* The yes and no text can be overridden
|
|
||||||
*
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
export function VoteIcon({
|
|
||||||
vote,
|
|
||||||
yesText = 'For',
|
|
||||||
noText = 'Against',
|
|
||||||
}: VoteIconProps) {
|
|
||||||
const label = vote ? yesText : noText;
|
|
||||||
const bg = vote ? 'bg-vega-green-550' : 'bg-vega-pink-550';
|
|
||||||
const icon: IconName = vote ? 'tick-circle' : 'delete';
|
|
||||||
const fill = vote ? 'vega-green-300' : 'vega-pink-300';
|
|
||||||
const text = vote ? 'vega-green-200' : 'vega-pink-200';
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
className={`voteicon inline-block my-1 py-1 px-2 py rounded-md text-white leading-one sm align-top ${bg}`}
|
|
||||||
>
|
|
||||||
<Icon name={icon} size={3} className={`mr-2 p-0 fill-${fill}`} />
|
|
||||||
<span className={`text-base text-${text}`} data-testid="label">
|
|
||||||
{label}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -8,7 +8,7 @@ import { useScrollToLocation } from '../../hooks/scroll-to-location';
|
|||||||
import { useDocumentTitle } from '../../hooks/use-document-title';
|
import { useDocumentTitle } from '../../hooks/use-document-title';
|
||||||
import compact from 'lodash/compact';
|
import compact from 'lodash/compact';
|
||||||
import { JsonViewerDialog } from '../../components/dialogs/json-viewer-dialog';
|
import { JsonViewerDialog } from '../../components/dialogs/json-viewer-dialog';
|
||||||
import { marketInfoWithDataProvider } from '@vegaprotocol/markets';
|
import { marketInfoWithDataProvider } from '@vegaprotocol/market-info';
|
||||||
import { PageTitle } from '../../components/page-helpers/page-title';
|
import { PageTitle } from '../../components/page-helpers/page-title';
|
||||||
|
|
||||||
export const MarketPage = () => {
|
export const MarketPage = () => {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { useScrollToLocation } from '../../hooks/scroll-to-location';
|
import { useScrollToLocation } from '../../hooks/scroll-to-location';
|
||||||
import { useDocumentTitle } from '../../hooks/use-document-title';
|
import { useDocumentTitle } from '../../hooks/use-document-title';
|
||||||
import { marketsProvider } from '@vegaprotocol/markets';
|
import { marketsProvider } from '@vegaprotocol/market-list';
|
||||||
import { RouteTitle } from '../../components/route-title';
|
import { RouteTitle } from '../../components/route-title';
|
||||||
import { AsyncRenderer } from '@vegaprotocol/ui-toolkit';
|
import { AsyncRenderer } from '@vegaprotocol/ui-toolkit';
|
||||||
import { t } from '@vegaprotocol/i18n';
|
import { t } from '@vegaprotocol/i18n';
|
||||||
|
|||||||
@@ -32,9 +32,9 @@
|
|||||||
line-height: calc(min(var(--ag-line-height, 26px), 26px) - 4px);
|
line-height: calc(min(var(--ag-line-height, 26px), 26px) - 4px);
|
||||||
}
|
}
|
||||||
|
|
||||||
.vega-ag-grid .ag-row,
|
.vega-ag-grid .ag-row {
|
||||||
.vega-ag-grid .ag-cell {
|
border-width: 1px 0;
|
||||||
border-width: 0;
|
border-bottom: 1px solid transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Light variables */
|
/* Light variables */
|
||||||
@@ -46,6 +46,7 @@
|
|||||||
--ag-header-column-separator-color: theme(colors.neutral[300]);
|
--ag-header-column-separator-color: theme(colors.neutral[300]);
|
||||||
--ag-row-border-color: theme(colors.white);
|
--ag-row-border-color: theme(colors.white);
|
||||||
--ag-row-hover-color: theme(colors.neutral[100]);
|
--ag-row-hover-color: theme(colors.neutral[100]);
|
||||||
|
--ag-font-size: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Dark variables */
|
/* Dark variables */
|
||||||
@@ -57,8 +58,5 @@
|
|||||||
--ag-header-column-separator-color: theme(colors.neutral[600]);
|
--ag-header-column-separator-color: theme(colors.neutral[600]);
|
||||||
--ag-row-border-color: theme(colors.black);
|
--ag-row-border-color: theme(colors.black);
|
||||||
--ag-row-hover-color: theme(colors.neutral[800]);
|
--ag-row-hover-color: theme(colors.neutral[800]);
|
||||||
}
|
--ag-font-size: 12px;
|
||||||
|
|
||||||
.voteicon svg {
|
|
||||||
vertical-align: baseline;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,10 @@
|
|||||||
const { defineConfig } = require('cypress');
|
const { defineConfig } = require('cypress');
|
||||||
|
|
||||||
module.exports = defineConfig({
|
module.exports = defineConfig({
|
||||||
reporter: '../../node_modules/cypress-mochawesome-reporter',
|
projectId: 'et4snf',
|
||||||
|
|
||||||
e2e: {
|
e2e: {
|
||||||
setupNodeEvents(on, config) {
|
setupNodeEvents(on, config) {
|
||||||
require('cypress-mochawesome-reporter/plugin')(on);
|
|
||||||
require('@cypress/grep/src/plugin')(config);
|
require('@cypress/grep/src/plugin')(config);
|
||||||
return config;
|
return config;
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import {
|
|||||||
waitForSpinner,
|
waitForSpinner,
|
||||||
navigateTo,
|
navigateTo,
|
||||||
navigation,
|
navigation,
|
||||||
turnTelemetryOff,
|
|
||||||
} from '../../support/common.functions';
|
} from '../../support/common.functions';
|
||||||
import {
|
import {
|
||||||
clickOnValidatorFromList,
|
clickOnValidatorFromList,
|
||||||
@@ -68,7 +67,6 @@ context(
|
|||||||
'teardown wallet & drill into a specific validator',
|
'teardown wallet & drill into a specific validator',
|
||||||
function () {
|
function () {
|
||||||
cy.clearLocalStorage();
|
cy.clearLocalStorage();
|
||||||
turnTelemetryOff();
|
|
||||||
cy.reload();
|
cy.reload();
|
||||||
waitForSpinner();
|
waitForSpinner();
|
||||||
cy.connectVegaWallet();
|
cy.connectVegaWallet();
|
||||||
@@ -254,10 +252,10 @@ context(
|
|||||||
waitForBeginningOfEpoch();
|
waitForBeginningOfEpoch();
|
||||||
cy.getByTestId(stakeValidatorListStakePercentage).should(
|
cy.getByTestId(stakeValidatorListStakePercentage).should(
|
||||||
'have.text',
|
'have.text',
|
||||||
'50.02%'
|
'100%'
|
||||||
);
|
);
|
||||||
navigateTo(navigation.validators);
|
navigateTo(navigation.validators);
|
||||||
validateValidatorListTotalStakeAndShare('0', '3,002.00', '50.02%');
|
validateValidatorListTotalStakeAndShare('0', '2.00', '100.00%');
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -109,7 +109,7 @@ context(
|
|||||||
);
|
);
|
||||||
cy.getByTestId('protocol-upgrade-proposal-status').should(
|
cy.getByTestId('protocol-upgrade-proposal-status').should(
|
||||||
'have.text',
|
'have.text',
|
||||||
'Approved by validators '
|
'Approved '
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -136,7 +136,7 @@ context(
|
|||||||
);
|
);
|
||||||
cy.getByTestId('protocol-upgrade-state').should(
|
cy.getByTestId('protocol-upgrade-state').should(
|
||||||
'have.text',
|
'have.text',
|
||||||
'Approved by validators'
|
'Approved'
|
||||||
);
|
);
|
||||||
cy.getByTestId('protocol-upgrade-release-tag').should(
|
cy.getByTestId('protocol-upgrade-release-tag').should(
|
||||||
'have.text',
|
'have.text',
|
||||||
|
|||||||
@@ -63,6 +63,7 @@ context(
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should have option to go to last and newest page', function () {
|
it('should have option to go to last and newest page', function () {
|
||||||
|
waitForBeginningOfEpoch();
|
||||||
cy.getByTestId('goto-last-page').click();
|
cy.getByTestId('goto-last-page').click();
|
||||||
cy.getByTestId('epoch-total-rewards-table')
|
cy.getByTestId('epoch-total-rewards-table')
|
||||||
.last()
|
.last()
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ const performancePenaltyToolTip = '[data-testid="performance-penalty-tooltip"]';
|
|||||||
const overstakedPenaltyToolTip = '[data-testid="overstaked-penalty-tooltip"]';
|
const overstakedPenaltyToolTip = '[data-testid="overstaked-penalty-tooltip"]';
|
||||||
const totalPenaltyToolTip = '[data-testid="total-penalty-tooltip"]';
|
const totalPenaltyToolTip = '[data-testid="total-penalty-tooltip"]';
|
||||||
const epochCountDown = '[data-testid="epoch-countdown"]';
|
const epochCountDown = '[data-testid="epoch-countdown"]';
|
||||||
const stakeNumberRegex = /^\d{1,3}(,\d{3})*(\.\d+)?$/;
|
const stakeNumberRegex = /^\d*\.?\d*$/;
|
||||||
|
|
||||||
context('Validators Page - verify elements on page', function () {
|
context('Validators Page - verify elements on page', function () {
|
||||||
before('navigate to validators page', function () {
|
before('navigate to validators page', function () {
|
||||||
@@ -84,13 +84,13 @@ context('Validators Page - verify elements on page', function () {
|
|||||||
|
|
||||||
cy.get(stakedByOperatorToolTip)
|
cy.get(stakedByOperatorToolTip)
|
||||||
.invoke('text')
|
.invoke('text')
|
||||||
.should('contain', 'Staked by operator: 3,000.00');
|
.should('contain', 'Staked by operator: 0.00');
|
||||||
cy.get(stakedByDelegatesToolTip)
|
cy.get(stakedByDelegatesToolTip)
|
||||||
.invoke('text')
|
.invoke('text')
|
||||||
.should('contain', 'Staked by delegates: 0.00');
|
.should('contain', 'Staked by delegates: 0.00');
|
||||||
cy.get(totalStakedToolTip)
|
cy.get(totalStakedToolTip)
|
||||||
.invoke('text')
|
.invoke('text')
|
||||||
.should('contain', 'Total stake: 3,000.00');
|
.should('contain', 'Total stake: 0.00');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Should be able to see validator normalised voting power', function () {
|
it('Should be able to see validator normalised voting power', function () {
|
||||||
@@ -106,10 +106,10 @@ context('Validators Page - verify elements on page', function () {
|
|||||||
|
|
||||||
cy.get(unnormalisedVotingPowerToolTip)
|
cy.get(unnormalisedVotingPowerToolTip)
|
||||||
.invoke('text')
|
.invoke('text')
|
||||||
.should('contain', 'Unnormalised voting power: 20.00%');
|
.should('contain', 'Unnormalised voting power: 0.00%');
|
||||||
cy.get(normalisedVotingPowerToolTip)
|
cy.get(normalisedVotingPowerToolTip)
|
||||||
.invoke('text')
|
.invoke('text')
|
||||||
.should('contain', 'Normalised voting power: 50.00%');
|
.should('contain', 'Normalised voting power: 0.10%');
|
||||||
});
|
});
|
||||||
|
|
||||||
// 2002-SINC-018
|
// 2002-SINC-018
|
||||||
@@ -126,13 +126,13 @@ context('Validators Page - verify elements on page', function () {
|
|||||||
|
|
||||||
cy.get(performancePenaltyToolTip)
|
cy.get(performancePenaltyToolTip)
|
||||||
.invoke('text')
|
.invoke('text')
|
||||||
.should('contain', 'Performance penalty: 0.00%');
|
.should('contain', 'Performance penalty: 100.00%');
|
||||||
cy.get(overstakedPenaltyToolTip)
|
cy.get(overstakedPenaltyToolTip)
|
||||||
.invoke('text')
|
.invoke('text')
|
||||||
.should('contain', 'Overstaked penalty: 60.00%'); // value not asserted due to #2886
|
.should('contain', 'Overstaked penalty:'); // value not asserted due to #2886
|
||||||
cy.get(totalPenaltyToolTip)
|
cy.get(totalPenaltyToolTip)
|
||||||
.invoke('text')
|
.invoke('text')
|
||||||
.should('contain', 'Total penalties: 60.00%');
|
.should('contain', 'Total penalties: 0.00%');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Should be able to see validator pending stake', function () {
|
it('Should be able to see validator pending stake', function () {
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ import './wallet-eth.functions.ts';
|
|||||||
import './wallet-teardown.functions.ts';
|
import './wallet-teardown.functions.ts';
|
||||||
import './wallet-vega.functions.ts';
|
import './wallet-vega.functions.ts';
|
||||||
import './proposal.functions.ts';
|
import './proposal.functions.ts';
|
||||||
import 'cypress-mochawesome-reporter/register';
|
|
||||||
import registerCypressGrep from '@cypress/grep';
|
import registerCypressGrep from '@cypress/grep';
|
||||||
import { aliasGQLQuery } from '@vegaprotocol/cypress';
|
import { aliasGQLQuery } from '@vegaprotocol/cypress';
|
||||||
import { chainIdQuery, statisticsQuery } from '@vegaprotocol/mock';
|
import { chainIdQuery, statisticsQuery } from '@vegaprotocol/mock';
|
||||||
@@ -34,6 +33,4 @@ before(() => {
|
|||||||
aliasGQLQuery(req, 'ChainId', chainIdQuery());
|
aliasGQLQuery(req, 'ChainId', chainIdQuery());
|
||||||
aliasGQLQuery(req, 'Statistics', statisticsQuery());
|
aliasGQLQuery(req, 'Statistics', statisticsQuery());
|
||||||
});
|
});
|
||||||
// Self stake validators so they are displayed
|
|
||||||
cy.validatorsSelfDelegate();
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import {
|
|||||||
StakingBridge,
|
StakingBridge,
|
||||||
Token,
|
Token,
|
||||||
TokenVesting,
|
TokenVesting,
|
||||||
|
TokenFaucetable,
|
||||||
CollateralBridge,
|
CollateralBridge,
|
||||||
} from '@vegaprotocol/smart-contracts';
|
} from '@vegaprotocol/smart-contracts';
|
||||||
import { ethers, Wallet } from 'ethers';
|
import { ethers, Wallet } from 'ethers';
|
||||||
@@ -40,7 +41,7 @@ export async function depositAsset(
|
|||||||
decimalPlaces: number
|
decimalPlaces: number
|
||||||
) {
|
) {
|
||||||
// Approve asset
|
// Approve asset
|
||||||
const faucet = new Token(assetEthAddress, signer);
|
const faucet = new TokenFaucetable(assetEthAddress, signer);
|
||||||
cy.wrap(
|
cy.wrap(
|
||||||
faucet.approve(Erc20BridgeAddress, amount + '0'.repeat(decimalPlaces + 1)),
|
faucet.approve(Erc20BridgeAddress, amount + '0'.repeat(decimalPlaces + 1)),
|
||||||
{
|
{
|
||||||
@@ -61,7 +62,7 @@ export async function depositAsset(
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function faucetAsset(assetEthAddress: string) {
|
export async function faucetAsset(assetEthAddress: string) {
|
||||||
const faucet = new Token(assetEthAddress, signer);
|
const faucet = new TokenFaucetable(assetEthAddress, signer);
|
||||||
await promiseWithTimeout(faucet.faucet(), 10 * 60 * 1000, 'faucet asset');
|
await promiseWithTimeout(faucet.faucet(), 10 * 60 * 1000, 'faucet asset');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
-6
@@ -1,6 +0,0 @@
|
|||||||
function ReactMarkdown({ children }) {
|
|
||||||
// eslint-disable-next-line react/jsx-no-useless-fragment
|
|
||||||
return <>{children}</>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default ReactMarkdown;
|
|
||||||
+15
-57
@@ -2,6 +2,7 @@ import './i18n';
|
|||||||
|
|
||||||
import React, { useEffect } from 'react';
|
import React, { useEffect } from 'react';
|
||||||
import * as Sentry from '@sentry/react';
|
import * as Sentry from '@sentry/react';
|
||||||
|
import { Integrations } from '@sentry/tracing';
|
||||||
import { BrowserRouter as Router, useLocation } from 'react-router-dom';
|
import { BrowserRouter as Router, useLocation } from 'react-router-dom';
|
||||||
import { AppLoader } from './app-loader';
|
import { AppLoader } from './app-loader';
|
||||||
import { NetworkInfo } from '@vegaprotocol/network-info';
|
import { NetworkInfo } from '@vegaprotocol/network-info';
|
||||||
@@ -16,7 +17,6 @@ import { AppStateProvider } from './contexts/app-state/app-state-provider';
|
|||||||
import { ContractsProvider } from './contexts/contracts/contracts-provider';
|
import { ContractsProvider } from './contexts/contracts/contracts-provider';
|
||||||
import { AppRouter } from './routes';
|
import { AppRouter } from './routes';
|
||||||
import type { EthereumConfig } from '@vegaprotocol/web3';
|
import type { EthereumConfig } from '@vegaprotocol/web3';
|
||||||
import { WithdrawalApprovalDialogContainer } from '@vegaprotocol/web3';
|
|
||||||
import {
|
import {
|
||||||
createConnectors,
|
createConnectors,
|
||||||
useEthTransactionManager,
|
useEthTransactionManager,
|
||||||
@@ -44,7 +44,7 @@ import {
|
|||||||
} from '@vegaprotocol/environment';
|
} from '@vegaprotocol/environment';
|
||||||
import { ENV } from './config';
|
import { ENV } from './config';
|
||||||
import type { InMemoryCacheConfig } from '@apollo/client';
|
import type { InMemoryCacheConfig } from '@apollo/client';
|
||||||
import { CreateWithdrawalDialog } from '@vegaprotocol/withdraws';
|
import { WithdrawalDialog } from '@vegaprotocol/withdraws';
|
||||||
import { SplashLoader } from './components/splash-loader';
|
import { SplashLoader } from './components/splash-loader';
|
||||||
import { ToastsManager } from './toasts-manager';
|
import { ToastsManager } from './toasts-manager';
|
||||||
import {
|
import {
|
||||||
@@ -53,7 +53,6 @@ import {
|
|||||||
} from './components/telemetry-dialog/telemetry-dialog';
|
} from './components/telemetry-dialog/telemetry-dialog';
|
||||||
import { useLocalStorage } from '@vegaprotocol/react-helpers';
|
import { useLocalStorage } from '@vegaprotocol/react-helpers';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { isPartyNotFoundError } from './lib/party';
|
|
||||||
|
|
||||||
const cache: InMemoryCacheConfig = {
|
const cache: InMemoryCacheConfig = {
|
||||||
typePolicies: {
|
typePolicies: {
|
||||||
@@ -159,8 +158,7 @@ const Web3Container = ({
|
|||||||
<InitializeHandlers />
|
<InitializeHandlers />
|
||||||
<VegaWalletDialogs />
|
<VegaWalletDialogs />
|
||||||
<TransactionModal />
|
<TransactionModal />
|
||||||
<CreateWithdrawalDialog />
|
<WithdrawalDialog />
|
||||||
<WithdrawalApprovalDialogContainer />
|
|
||||||
<TelemetryDialog />
|
<TelemetryDialog />
|
||||||
</>
|
</>
|
||||||
</BalanceManager>
|
</BalanceManager>
|
||||||
@@ -186,10 +184,6 @@ const ScrollToTop = () => {
|
|||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
|
|
||||||
const removeQueryParams = (url: string) => {
|
|
||||||
return url.split('?')[0];
|
|
||||||
};
|
|
||||||
|
|
||||||
const AppContainer = () => {
|
const AppContainer = () => {
|
||||||
const { config, loading, error } = useEthereumConfig();
|
const { config, loading, error } = useEthereumConfig();
|
||||||
const {
|
const {
|
||||||
@@ -207,61 +201,25 @@ const AppContainer = () => {
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (ENV.dsn && telemetryOn === 'true') {
|
if (ENV.dsn && telemetryOn) {
|
||||||
Sentry.init({
|
Sentry.init({
|
||||||
dsn: ENV.dsn,
|
dsn: ENV.dsn,
|
||||||
|
integrations: [new Integrations.BrowserTracing()],
|
||||||
tracesSampleRate: 0.1,
|
tracesSampleRate: 0.1,
|
||||||
enabled: true,
|
enabled: true,
|
||||||
environment: VEGA_ENV,
|
environment: VEGA_ENV,
|
||||||
release: GIT_COMMIT_HASH,
|
release: GIT_COMMIT_HASH,
|
||||||
beforeSend(event, hint) {
|
beforeSend(event) {
|
||||||
const error = hint?.originalException;
|
if (event.request?.url?.includes('/claim?')) {
|
||||||
const errorIsString = typeof error === 'string';
|
return {
|
||||||
const errorIsObject = error instanceof Error;
|
...event,
|
||||||
const requestUrl = event.request?.url;
|
request: {
|
||||||
const transaction = event.transaction;
|
...event.request,
|
||||||
|
url: event.request?.url.split('?')[0],
|
||||||
if (
|
},
|
||||||
(errorIsString && isPartyNotFoundError({ message: error })) ||
|
};
|
||||||
(errorIsObject && isPartyNotFoundError(error))
|
|
||||||
) {
|
|
||||||
// This error is caused by a pubkey making an API request before
|
|
||||||
// it has interacted with the chain. This isn't needed in Sentry.
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
return event;
|
||||||
const updatedRequest =
|
|
||||||
requestUrl && requestUrl.includes('/claim?')
|
|
||||||
? { ...event.request, url: removeQueryParams(requestUrl) }
|
|
||||||
: event.request;
|
|
||||||
|
|
||||||
const updatedTransaction =
|
|
||||||
transaction && transaction.includes('/claim?')
|
|
||||||
? removeQueryParams(transaction)
|
|
||||||
: transaction;
|
|
||||||
|
|
||||||
const updatedBreadcrumbs = event.breadcrumbs?.map((breadcrumb) => {
|
|
||||||
if (
|
|
||||||
breadcrumb.type === 'navigation' &&
|
|
||||||
breadcrumb.data?.to?.includes('/claim?')
|
|
||||||
) {
|
|
||||||
return {
|
|
||||||
...breadcrumb,
|
|
||||||
data: {
|
|
||||||
...breadcrumb.data,
|
|
||||||
to: removeQueryParams(breadcrumb.data.to),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return breadcrumb;
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
|
||||||
...event,
|
|
||||||
request: updatedRequest,
|
|
||||||
transaction: updatedTransaction,
|
|
||||||
breadcrumbs: updatedBreadcrumbs ?? event.breadcrumbs,
|
|
||||||
};
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
Sentry.setTag('branch', GIT_BRANCH);
|
Sentry.setTag('branch', GIT_BRANCH);
|
||||||
|
|||||||
@@ -1,14 +1,23 @@
|
|||||||
import { Button } from '@vegaprotocol/ui-toolkit';
|
import { Button } from '@vegaprotocol/ui-toolkit';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { useWeb3ConnectStore } from '@vegaprotocol/web3';
|
|
||||||
|
import {
|
||||||
|
AppStateActionType,
|
||||||
|
useAppState,
|
||||||
|
} from '../../contexts/app-state/app-state-context';
|
||||||
|
|
||||||
export const EthConnectPrompt = () => {
|
export const EthConnectPrompt = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { open } = useWeb3ConnectStore();
|
const { appDispatch } = useAppState();
|
||||||
return (
|
return (
|
||||||
<Button
|
<Button
|
||||||
variant="default"
|
variant="default"
|
||||||
onClick={open}
|
onClick={() =>
|
||||||
|
appDispatch({
|
||||||
|
type: AppStateActionType.SET_ETH_WALLET_OVERLAY,
|
||||||
|
isOpen: true,
|
||||||
|
})
|
||||||
|
}
|
||||||
fill={true}
|
fill={true}
|
||||||
data-testid="connect-to-eth-btn"
|
data-testid="connect-to-eth-btn"
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -2,7 +2,10 @@ import { Button } from '@vegaprotocol/ui-toolkit';
|
|||||||
import { useWeb3React } from '@web3-react/core';
|
import { useWeb3React } from '@web3-react/core';
|
||||||
import type { ReactElement } from 'react';
|
import type { ReactElement } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { useWeb3ConnectStore } from '@vegaprotocol/web3';
|
import {
|
||||||
|
AppStateActionType,
|
||||||
|
useAppState,
|
||||||
|
} from '../../contexts/app-state/app-state-context';
|
||||||
|
|
||||||
export const EthWalletContainer = ({
|
export const EthWalletContainer = ({
|
||||||
children,
|
children,
|
||||||
@@ -11,12 +14,21 @@ export const EthWalletContainer = ({
|
|||||||
}) => {
|
}) => {
|
||||||
const { account } = useWeb3React();
|
const { account } = useWeb3React();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { open } = useWeb3ConnectStore();
|
const { appDispatch } = useAppState();
|
||||||
|
|
||||||
if (!account) {
|
if (!account) {
|
||||||
return (
|
return (
|
||||||
<div className="w-full text-center">
|
<div className="w-full text-center">
|
||||||
<Button onClick={open}>{t('connectEthWallet')}</Button>
|
<Button
|
||||||
|
onClick={() =>
|
||||||
|
appDispatch({
|
||||||
|
type: AppStateActionType.SET_ETH_WALLET_OVERLAY,
|
||||||
|
isOpen: true,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{t('connectEthWallet')}
|
||||||
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,11 +27,7 @@ import {
|
|||||||
import { Loader } from '@vegaprotocol/ui-toolkit';
|
import { Loader } from '@vegaprotocol/ui-toolkit';
|
||||||
import colors from 'tailwindcss/colors';
|
import colors from 'tailwindcss/colors';
|
||||||
import { useBalances } from '../../lib/balances/balances-store';
|
import { useBalances } from '../../lib/balances/balances-store';
|
||||||
import {
|
import { useEthereumConfig, useWeb3Disconnect } from '@vegaprotocol/web3';
|
||||||
useEthereumConfig,
|
|
||||||
useWeb3ConnectStore,
|
|
||||||
useWeb3Disconnect,
|
|
||||||
} from '@vegaprotocol/web3';
|
|
||||||
import { getChainName } from '@vegaprotocol/web3';
|
import { getChainName } from '@vegaprotocol/web3';
|
||||||
|
|
||||||
const removeLeadingAddressSymbol = (key: string) => {
|
const removeLeadingAddressSymbol = (key: string) => {
|
||||||
@@ -193,7 +189,6 @@ export const EthWallet = () => {
|
|||||||
const pendingTxs = usePendingTransactions();
|
const pendingTxs = usePendingTransactions();
|
||||||
const disconnect = useWeb3Disconnect(connector);
|
const disconnect = useWeb3Disconnect(connector);
|
||||||
const { config } = useEthereumConfig();
|
const { config } = useEthereumConfig();
|
||||||
const { open } = useWeb3ConnectStore();
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<WalletCard>
|
<WalletCard>
|
||||||
@@ -238,7 +233,12 @@ export const EthWallet = () => {
|
|||||||
) : (
|
) : (
|
||||||
<Button
|
<Button
|
||||||
fill={true}
|
fill={true}
|
||||||
onClick={open}
|
onClick={() =>
|
||||||
|
appDispatch({
|
||||||
|
type: AppStateActionType.SET_ETH_WALLET_OVERLAY,
|
||||||
|
isOpen: true,
|
||||||
|
})
|
||||||
|
}
|
||||||
data-testid="connect-to-eth-wallet-button"
|
data-testid="connect-to-eth-wallet-button"
|
||||||
>
|
>
|
||||||
{t('connectEthWalletToAssociate')}
|
{t('connectEthWalletToAssociate')}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Link } from '@vegaprotocol/ui-toolkit';
|
import { Link } from '@vegaprotocol/ui-toolkit';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { ExternalLinks } from '@vegaprotocol/environment';
|
import { ExternalLinks } from '@vegaprotocol/utils';
|
||||||
|
|
||||||
export const DownloadWalletPrompt = () => {
|
export const DownloadWalletPrompt = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|||||||
@@ -26,7 +26,6 @@ import type {
|
|||||||
WalletDelegationFieldsFragment,
|
WalletDelegationFieldsFragment,
|
||||||
} from './__generated__/Delegations';
|
} from './__generated__/Delegations';
|
||||||
import { DelegationsDocument } from './__generated__/Delegations';
|
import { DelegationsDocument } from './__generated__/Delegations';
|
||||||
import { isPartyNotFoundError } from '../../lib/party';
|
|
||||||
|
|
||||||
export const usePollForDelegations = () => {
|
export const usePollForDelegations = () => {
|
||||||
const { token: vegaToken } = useContracts();
|
const { token: vegaToken } = useContracts();
|
||||||
@@ -76,7 +75,6 @@ export const usePollForDelegations = () => {
|
|||||||
})
|
})
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
|
|
||||||
const canonisedDelegations = removePaginationWrapper(
|
const canonisedDelegations = removePaginationWrapper(
|
||||||
res.data.party?.delegationsConnection?.edges
|
res.data.party?.delegationsConnection?.edges
|
||||||
);
|
);
|
||||||
@@ -205,15 +203,6 @@ export const usePollForDelegations = () => {
|
|||||||
setDelegatedNodes(delegatedAmounts);
|
setDelegatedNodes(delegatedAmounts);
|
||||||
})
|
})
|
||||||
.catch((err: Error) => {
|
.catch((err: Error) => {
|
||||||
// if party isn't found, dont log to Sentry, just clear state as, user
|
|
||||||
// will not have any delagations or accounts
|
|
||||||
if (isPartyNotFoundError(err)) {
|
|
||||||
setDelegations([]);
|
|
||||||
setAccounts([]);
|
|
||||||
setDelegatedNodes([]);
|
|
||||||
setCurrentStakeAvailable(new BigNumber(0));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Sentry.captureException(err);
|
Sentry.captureException(err);
|
||||||
// If query fails stop interval. Its almost certain that the query
|
// If query fails stop interval. Its almost certain that the query
|
||||||
// will just continue to fail
|
// will just continue to fail
|
||||||
|
|||||||
@@ -25,26 +25,20 @@ export function Web3Connector({
|
|||||||
connectors,
|
connectors,
|
||||||
chainId,
|
chainId,
|
||||||
}: Web3ConnectorProps) {
|
}: Web3ConnectorProps) {
|
||||||
const { open, close, isOpen } = useWeb3ConnectStore();
|
const { appState, appDispatch } = useAppState();
|
||||||
|
|
||||||
const setDialogOpen = useCallback(
|
const setDialogOpen = useCallback(
|
||||||
(isOpen: boolean) => {
|
(isOpen: boolean) => {
|
||||||
if (isOpen) {
|
appDispatch({ type: AppStateActionType.SET_ETH_WALLET_OVERLAY, isOpen });
|
||||||
open();
|
|
||||||
} else {
|
|
||||||
close();
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
[open, close]
|
[appDispatch]
|
||||||
);
|
);
|
||||||
|
|
||||||
const appChainId = Number(chainId);
|
const appChainId = Number(chainId);
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Web3Content appChainId={appChainId}>{children}</Web3Content>
|
<Web3Content appChainId={appChainId}>{children}</Web3Content>
|
||||||
<Web3ConnectDialog
|
<Web3ConnectDialog
|
||||||
connectors={connectors}
|
connectors={connectors}
|
||||||
dialogOpen={isOpen}
|
dialogOpen={appState.ethConnectOverlay}
|
||||||
setDialogOpen={setDialogOpen}
|
setDialogOpen={setDialogOpen}
|
||||||
desiredChainId={appChainId}
|
desiredChainId={appChainId}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -34,6 +34,9 @@ export interface AppState {
|
|||||||
/** Whether or not the manage VEGA wallet overlay is open */
|
/** Whether or not the manage VEGA wallet overlay is open */
|
||||||
vegaWalletManageOverlay: boolean;
|
vegaWalletManageOverlay: boolean;
|
||||||
|
|
||||||
|
/** Whether or not the connect to Ethereum wallet overlay is open */
|
||||||
|
ethConnectOverlay: boolean;
|
||||||
|
|
||||||
/** Whether or not the transaction modal is open */
|
/** Whether or not the transaction modal is open */
|
||||||
transactionOverlay: boolean;
|
transactionOverlay: boolean;
|
||||||
/**
|
/**
|
||||||
@@ -54,6 +57,7 @@ export enum AppStateActionType {
|
|||||||
REFRESH_BALANCES,
|
REFRESH_BALANCES,
|
||||||
SET_VEGA_WALLET_OVERLAY,
|
SET_VEGA_WALLET_OVERLAY,
|
||||||
SET_VEGA_WALLET_MANAGE_OVERLAY,
|
SET_VEGA_WALLET_MANAGE_OVERLAY,
|
||||||
|
SET_ETH_WALLET_OVERLAY,
|
||||||
SET_DRAWER,
|
SET_DRAWER,
|
||||||
REFRESH_ASSOCIATED_BALANCES,
|
REFRESH_ASSOCIATED_BALANCES,
|
||||||
SET_ASSOCIATION_BREAKDOWN,
|
SET_ASSOCIATION_BREAKDOWN,
|
||||||
@@ -77,6 +81,10 @@ export type AppStateAction =
|
|||||||
type: AppStateActionType.SET_VEGA_WALLET_MANAGE_OVERLAY;
|
type: AppStateActionType.SET_VEGA_WALLET_MANAGE_OVERLAY;
|
||||||
isOpen: boolean;
|
isOpen: boolean;
|
||||||
}
|
}
|
||||||
|
| {
|
||||||
|
type: AppStateActionType.SET_ETH_WALLET_OVERLAY;
|
||||||
|
isOpen: boolean;
|
||||||
|
}
|
||||||
| {
|
| {
|
||||||
type: AppStateActionType.SET_DRAWER;
|
type: AppStateActionType.SET_DRAWER;
|
||||||
isOpen: boolean;
|
isOpen: boolean;
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ const initialAppState: AppState = {
|
|||||||
totalSupply: new BigNumber(0),
|
totalSupply: new BigNumber(0),
|
||||||
vegaWalletOverlay: false,
|
vegaWalletOverlay: false,
|
||||||
vegaWalletManageOverlay: false,
|
vegaWalletManageOverlay: false,
|
||||||
|
ethConnectOverlay: false,
|
||||||
transactionOverlay: false,
|
transactionOverlay: false,
|
||||||
bannerMessage: '',
|
bannerMessage: '',
|
||||||
disconnectNotice: false,
|
disconnectNotice: false,
|
||||||
@@ -44,6 +45,12 @@ function appStateReducer(state: AppState, action: AppStateAction): AppState {
|
|||||||
vegaWalletOverlay: action.isOpen ? false : state.vegaWalletOverlay,
|
vegaWalletOverlay: action.isOpen ? false : state.vegaWalletOverlay,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
case AppStateActionType.SET_ETH_WALLET_OVERLAY: {
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
ethConnectOverlay: action.isOpen,
|
||||||
|
};
|
||||||
|
}
|
||||||
case AppStateActionType.SET_DRAWER: {
|
case AppStateActionType.SET_DRAWER: {
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
|
|||||||
@@ -202,7 +202,6 @@
|
|||||||
"tokenVotes": "Token votes",
|
"tokenVotes": "Token votes",
|
||||||
"liquidityVotes": "Liquidity votes",
|
"liquidityVotes": "Liquidity votes",
|
||||||
"castYourVote": "Cast your vote",
|
"castYourVote": "Cast your vote",
|
||||||
"yourVote": "Your vote",
|
|
||||||
"for": "For",
|
"for": "For",
|
||||||
"against": "Against",
|
"against": "Against",
|
||||||
"majorityRequired": "Majority Required",
|
"majorityRequired": "Majority Required",
|
||||||
@@ -594,9 +593,7 @@
|
|||||||
"numberOfAgainstVotes": "Number of votes against",
|
"numberOfAgainstVotes": "Number of votes against",
|
||||||
"yesPercentage": "Yes percentage",
|
"yesPercentage": "Yes percentage",
|
||||||
"noPercentage": "No percentage",
|
"noPercentage": "No percentage",
|
||||||
"proposalJson": "Full proposal JSON",
|
"proposalTerms": "Proposal terms",
|
||||||
"proposalDetails": "Proposal details",
|
|
||||||
"proposalDescription": "Description",
|
|
||||||
"currentlySetTo": "Currently expected to ",
|
"currentlySetTo": "Currently expected to ",
|
||||||
"currently": "currently",
|
"currently": "currently",
|
||||||
"finalOutcomeMayDiffer": "Final outcome may differ",
|
"finalOutcomeMayDiffer": "Final outcome may differ",
|
||||||
@@ -790,9 +787,9 @@
|
|||||||
"homeVegaTokenButtonText": "Manage tokens",
|
"homeVegaTokenButtonText": "Manage tokens",
|
||||||
"downloadProposalJson": "Download proposal as JSON",
|
"downloadProposalJson": "Download proposal as JSON",
|
||||||
"networkUpgrade": "Network Upgrade",
|
"networkUpgrade": "Network Upgrade",
|
||||||
"PROTOCOL_UPGRADE_PROPOSAL_STATUS_APPROVED": "Approved by validators",
|
"PROTOCOL_UPGRADE_PROPOSAL_STATUS_APPROVED": "Approved",
|
||||||
"PROTOCOL_UPGRADE_PROPOSAL_STATUS_PENDING": "Waiting for validator votes",
|
"PROTOCOL_UPGRADE_PROPOSAL_STATUS_PENDING": "Pending",
|
||||||
"PROTOCOL_UPGRADE_PROPOSAL_STATUS_REJECTED": "Declined by validators",
|
"PROTOCOL_UPGRADE_PROPOSAL_STATUS_REJECTED": "Rejected",
|
||||||
"PROTOCOL_UPGRADE_PROPOSAL_STATUS_UNSPECIFIED": "Unspecified",
|
"PROTOCOL_UPGRADE_PROPOSAL_STATUS_UNSPECIFIED": "Unspecified",
|
||||||
"vegaRelease{release}": "Vega Release {{release}}",
|
"vegaRelease{release}": "Vega Release {{release}}",
|
||||||
"upgradeBlockHeight": "Upgrade block height",
|
"upgradeBlockHeight": "Upgrade block height",
|
||||||
|
|||||||
@@ -1,8 +0,0 @@
|
|||||||
export const PARTY_NOT_FOUND = 'failed to get party for ID';
|
|
||||||
|
|
||||||
export const isPartyNotFoundError = (error: { message: string }) => {
|
|
||||||
if (error.message.includes(PARTY_NOT_FOUND)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
@@ -50,22 +50,7 @@ export const useTranches = create<TranchesStore>()((set) => ({
|
|||||||
?.map((t) => {
|
?.map((t) => {
|
||||||
const tranche_progress =
|
const tranche_progress =
|
||||||
t.duration !== 0 ? (now - t.cliff_start) / t.duration : 0;
|
t.duration !== 0 ? (now - t.cliff_start) / t.duration : 0;
|
||||||
let lockedDecimal;
|
const lockedDecimal = tranche_progress < 0 ? 1 : 1 - tranche_progress;
|
||||||
if (t.duration !== 0) {
|
|
||||||
if (tranche_progress < 0) {
|
|
||||||
lockedDecimal = 1;
|
|
||||||
} else {
|
|
||||||
lockedDecimal = 1 - tranche_progress;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (now < t.cliff_start) {
|
|
||||||
lockedDecimal = 1;
|
|
||||||
} else {
|
|
||||||
lockedDecimal = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const clampedLockedDecimal = Math.max(0, Math.min(1, lockedDecimal));
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
tranche_id: t.tranche_id,
|
tranche_id: t.tranche_id,
|
||||||
tranche_start: secondsToDate(t.cliff_start),
|
tranche_start: secondsToDate(t.cliff_start),
|
||||||
@@ -75,7 +60,7 @@ export const useTranches = create<TranchesStore>()((set) => ({
|
|||||||
toBigNum(t.current_balance, decimals)
|
toBigNum(t.current_balance, decimals)
|
||||||
),
|
),
|
||||||
locked_amount: toBigNum(t.initial_balance, decimals).times(
|
locked_amount: toBigNum(t.initial_balance, decimals).times(
|
||||||
clampedLockedDecimal
|
lockedDecimal
|
||||||
),
|
),
|
||||||
users: t.users,
|
users: t.users,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -12,8 +12,7 @@ import { useRefreshAfterEpoch } from '../../hooks/use-refresh-after-epoch';
|
|||||||
import { ProposalsListItem } from '../proposals/components/proposals-list-item';
|
import { ProposalsListItem } from '../proposals/components/proposals-list-item';
|
||||||
import { ProtocolUpgradeProposalsListItem } from '../proposals/components/protocol-upgrade-proposals-list-item/protocol-upgrade-proposals-list-item';
|
import { ProtocolUpgradeProposalsListItem } from '../proposals/components/protocol-upgrade-proposals-list-item/protocol-upgrade-proposals-list-item';
|
||||||
import Routes from '../routes';
|
import Routes from '../routes';
|
||||||
import { ExternalLinks } from '@vegaprotocol/environment';
|
import { ExternalLinks, removePaginationWrapper } from '@vegaprotocol/utils';
|
||||||
import { removePaginationWrapper } from '@vegaprotocol/utils';
|
|
||||||
import { useNodesQuery } from '../staking/home/__generated__/Nodes';
|
import { useNodesQuery } from '../staking/home/__generated__/Nodes';
|
||||||
import { useProposalsQuery } from '../proposals/proposals/__generated__/Proposals';
|
import { useProposalsQuery } from '../proposals/proposals/__generated__/Proposals';
|
||||||
import {
|
import {
|
||||||
|
|||||||
@@ -53,6 +53,7 @@ const mockAppState: AppState = {
|
|||||||
totalSupply: new BigNumber(65000000),
|
totalSupply: new BigNumber(65000000),
|
||||||
vegaWalletOverlay: false,
|
vegaWalletOverlay: false,
|
||||||
vegaWalletManageOverlay: false,
|
vegaWalletManageOverlay: false,
|
||||||
|
ethConnectOverlay: false,
|
||||||
transactionOverlay: false,
|
transactionOverlay: false,
|
||||||
bannerMessage: '',
|
bannerMessage: '',
|
||||||
disconnectNotice: false,
|
disconnectNotice: false,
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
export { ProposalDescription } from './proposal-description';
|
|
||||||
-51
@@ -1,51 +0,0 @@
|
|||||||
import ReactMarkdown from 'react-markdown';
|
|
||||||
import classnames from 'classnames';
|
|
||||||
import { useState } from 'react';
|
|
||||||
import { useTranslation } from 'react-i18next';
|
|
||||||
import { Icon, RoundedWrapper } from '@vegaprotocol/ui-toolkit';
|
|
||||||
import { SubHeading } from '../../../../components/heading';
|
|
||||||
|
|
||||||
export const ProposalDescription = ({
|
|
||||||
description,
|
|
||||||
}: {
|
|
||||||
description: string;
|
|
||||||
}) => {
|
|
||||||
const { t } = useTranslation();
|
|
||||||
const [showDescription, setShowDescription] = useState(false);
|
|
||||||
const showDescriptionIconClasses = classnames('mb-4', {
|
|
||||||
'rotate-180': showDescription,
|
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
|
||||||
<section data-testid="proposal-description">
|
|
||||||
<button
|
|
||||||
onClick={() => setShowDescription(!showDescription)}
|
|
||||||
data-testid="proposal-description-toggle"
|
|
||||||
>
|
|
||||||
<div className="flex items-center gap-3">
|
|
||||||
<SubHeading title={t('proposalDescription')} />
|
|
||||||
<div className={showDescriptionIconClasses}>
|
|
||||||
<Icon name="chevron-down" size={8} />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</button>
|
|
||||||
|
|
||||||
{showDescription && (
|
|
||||||
<RoundedWrapper paddingBottom={true} marginBottomLarge={true}>
|
|
||||||
<div className="p-2">
|
|
||||||
<ReactMarkdown
|
|
||||||
className="react-markdown-container"
|
|
||||||
/* Prevents HTML embedded in the description from rendering */
|
|
||||||
skipHtml={true}
|
|
||||||
/* Stops users embedding images which could be used for tracking */
|
|
||||||
disallowedElements={['img']}
|
|
||||||
linkTarget="_blank"
|
|
||||||
>
|
|
||||||
{description}
|
|
||||||
</ReactMarkdown>
|
|
||||||
</div>
|
|
||||||
</RoundedWrapper>
|
|
||||||
)}
|
|
||||||
</section>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
+22
@@ -206,6 +206,28 @@ describe('Proposal header', () => {
|
|||||||
).not.toBeInTheDocument();
|
).not.toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('Renders Freeform proposal - long rationale (105 chars) - details', () => {
|
||||||
|
renderComponent(
|
||||||
|
generateProposal({
|
||||||
|
id: 'long',
|
||||||
|
rationale: {
|
||||||
|
title: '0x0',
|
||||||
|
description:
|
||||||
|
'Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Aenean dolor.',
|
||||||
|
},
|
||||||
|
terms: {
|
||||||
|
change: {
|
||||||
|
__typename: 'NewFreeform',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
false
|
||||||
|
);
|
||||||
|
expect(screen.getByTestId('proposal-description')).toHaveTextContent(
|
||||||
|
/Class aptent/
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
// Remove once proposals have rationale and re-enable above tests
|
// Remove once proposals have rationale and re-enable above tests
|
||||||
it('Renders Freeform proposal - id for title', () => {
|
it('Renders Freeform proposal - id for title', () => {
|
||||||
renderComponent(
|
renderComponent(
|
||||||
|
|||||||
+24
-1
@@ -5,6 +5,7 @@ import { Heading, SubHeading } from '../../../../components/heading';
|
|||||||
import type { ReactNode } from 'react';
|
import type { ReactNode } from 'react';
|
||||||
import type { ProposalFieldsFragment } from '../../proposals/__generated__/Proposals';
|
import type { ProposalFieldsFragment } from '../../proposals/__generated__/Proposals';
|
||||||
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
|
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
|
||||||
|
import ReactMarkdown from 'react-markdown';
|
||||||
import { truncateMiddle } from '../../../../lib/truncate-middle';
|
import { truncateMiddle } from '../../../../lib/truncate-middle';
|
||||||
import { CurrentProposalState } from '../current-proposal-state';
|
import { CurrentProposalState } from '../current-proposal-state';
|
||||||
import { ProposalInfoLabel } from '../proposal-info-label';
|
import { ProposalInfoLabel } from '../proposal-info-label';
|
||||||
@@ -22,7 +23,12 @@ export const ProposalHeader = ({
|
|||||||
let details: ReactNode;
|
let details: ReactNode;
|
||||||
let proposalType = '';
|
let proposalType = '';
|
||||||
|
|
||||||
const title = proposal?.rationale.title.trim();
|
let title = proposal?.rationale.title.trim();
|
||||||
|
let description = proposal?.rationale.description.trim();
|
||||||
|
if (title?.length === 0 && description && description.length > 0) {
|
||||||
|
title = description;
|
||||||
|
description = '';
|
||||||
|
}
|
||||||
|
|
||||||
const titleContent = shorten(title ?? '', 100);
|
const titleContent = shorten(title ?? '', 100);
|
||||||
|
|
||||||
@@ -139,6 +145,23 @@ export const ProposalHeader = ({
|
|||||||
{details}
|
{details}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{description && !isListItem && (
|
||||||
|
<div data-testid="proposal-description">
|
||||||
|
{/*<div className="uppercase mr-2">{t('ProposalDescription')}:</div>*/}
|
||||||
|
<SubHeading title={t('ProposalDescription')} />
|
||||||
|
<ReactMarkdown
|
||||||
|
className="react-markdown-container"
|
||||||
|
/* Prevents HTML embedded in the description from rendering */
|
||||||
|
skipHtml={true}
|
||||||
|
/* Stops users embedding images which could be used for tracking */
|
||||||
|
disallowedElements={['img']}
|
||||||
|
linkTarget="_blank"
|
||||||
|
>
|
||||||
|
{description}
|
||||||
|
</ReactMarkdown>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
export { ProposalJson } from './proposal-json';
|
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export { ProposalTermsJson } from './proposal-terms-json';
|
||||||
+11
-11
@@ -1,15 +1,15 @@
|
|||||||
import classnames from 'classnames';
|
|
||||||
import { useState } from 'react';
|
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { Icon, SyntaxHighlighter } from '@vegaprotocol/ui-toolkit';
|
import { Icon, SyntaxHighlighter } from '@vegaprotocol/ui-toolkit';
|
||||||
import { SubHeading } from '../../../../components/heading';
|
import { SubHeading } from '../../../../components/heading';
|
||||||
import type { ProposalFieldsFragment } from '../../proposals/__generated__/Proposals';
|
import type { PartialDeep } from 'type-fest';
|
||||||
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
|
import type * as Schema from '@vegaprotocol/types';
|
||||||
|
import { useState } from 'react';
|
||||||
|
import classnames from 'classnames';
|
||||||
|
|
||||||
export const ProposalJson = ({
|
export const ProposalTermsJson = ({
|
||||||
proposal,
|
terms,
|
||||||
}: {
|
}: {
|
||||||
proposal: ProposalFieldsFragment | ProposalQuery['proposal'];
|
terms: PartialDeep<Schema.ProposalTerms>;
|
||||||
}) => {
|
}) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [showDetails, setShowDetails] = useState(false);
|
const [showDetails, setShowDetails] = useState(false);
|
||||||
@@ -18,20 +18,20 @@ export const ProposalJson = ({
|
|||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section data-testid="proposal-json">
|
<section>
|
||||||
<button
|
<button
|
||||||
onClick={() => setShowDetails(!showDetails)}
|
onClick={() => setShowDetails(!showDetails)}
|
||||||
data-testid="proposal-json-toggle"
|
data-testid="proposal-terms-toggle"
|
||||||
>
|
>
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex items-center gap-3">
|
||||||
<SubHeading title={t('proposalJson')} />
|
<SubHeading title={t('proposalTerms')} />
|
||||||
<div className={showDetailsIconClasses}>
|
<div className={showDetailsIconClasses}>
|
||||||
<Icon name="chevron-down" size={8} />
|
<Icon name="chevron-down" size={8} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
{showDetails && <SyntaxHighlighter data={proposal} />}
|
{showDetails && <SyntaxHighlighter data={terms} />}
|
||||||
</section>
|
</section>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@@ -1 +0,0 @@
|
|||||||
export { ProposalTerms } from './proposal-terms';
|
|
||||||
@@ -1,142 +0,0 @@
|
|||||||
import {
|
|
||||||
RoundedWrapper,
|
|
||||||
KeyValueTable,
|
|
||||||
KeyValueTableRow,
|
|
||||||
Icon,
|
|
||||||
} from '@vegaprotocol/ui-toolkit';
|
|
||||||
import { BigNumber } from '../../../../lib/bignumber';
|
|
||||||
import { SubHeading } from '../../../../components/heading';
|
|
||||||
import { useTranslation } from 'react-i18next';
|
|
||||||
import { useState } from 'react';
|
|
||||||
import classnames from 'classnames';
|
|
||||||
|
|
||||||
interface ProposalTermsProps {
|
|
||||||
data: Record<string, unknown>;
|
|
||||||
}
|
|
||||||
|
|
||||||
const getParsedValue = (value: unknown) => {
|
|
||||||
if (typeof value === 'string') {
|
|
||||||
try {
|
|
||||||
// Check if value is a number string - if so use bignumber to maintain precision
|
|
||||||
if (/^\d+(\.\d+)?$/.test(value)) {
|
|
||||||
return new BigNumber(value).toString();
|
|
||||||
} else {
|
|
||||||
// This would convert, for example, a JSON object ('{"key":"value"}')
|
|
||||||
// into an actual JS object ({key: "value"})
|
|
||||||
return JSON.parse(value);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const RenderKeyValue = ({
|
|
||||||
title,
|
|
||||||
value,
|
|
||||||
}: {
|
|
||||||
title: string;
|
|
||||||
value: string | number;
|
|
||||||
}) => (
|
|
||||||
<KeyValueTable>
|
|
||||||
<KeyValueTableRow>
|
|
||||||
{title}
|
|
||||||
{value}
|
|
||||||
</KeyValueTableRow>
|
|
||||||
</KeyValueTable>
|
|
||||||
);
|
|
||||||
|
|
||||||
const RenderArray = ({ title, array }: { title: string; array: unknown[] }) => {
|
|
||||||
if (array.every((item) => typeof item === 'string')) {
|
|
||||||
return <RenderKeyValue title={title} value={array.join(', ')} />;
|
|
||||||
} else {
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<div className="mb-2">{title}</div>
|
|
||||||
{array.map((item, index) => (
|
|
||||||
<div key={index}>
|
|
||||||
<ProposalTermsRenderer data={item as Record<string, unknown>} />
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Working with 'unknown' type as a proposal's terms can be in many shapes
|
|
||||||
const RenderTerm = ({ title, value }: { title: string; value: unknown }) => {
|
|
||||||
const parsedValue = getParsedValue(value);
|
|
||||||
|
|
||||||
if (parsedValue === null || typeof parsedValue === 'boolean') {
|
|
||||||
return <RenderKeyValue title={title} value={String(parsedValue)} />;
|
|
||||||
} else if (
|
|
||||||
typeof parsedValue === 'string' ||
|
|
||||||
typeof parsedValue === 'number'
|
|
||||||
) {
|
|
||||||
return <RenderKeyValue title={title} value={parsedValue} />;
|
|
||||||
} else if (typeof parsedValue === 'object') {
|
|
||||||
if (Array.isArray(parsedValue)) {
|
|
||||||
return <RenderArray title={title} array={parsedValue} />;
|
|
||||||
} else {
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<div className="my-2">{title}</div>
|
|
||||||
<ProposalTermsRenderer
|
|
||||||
data={parsedValue as Record<string, unknown>}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} else if (parsedValue === undefined) {
|
|
||||||
return <span>{'undefined'}</span>;
|
|
||||||
} else {
|
|
||||||
return <span>{String(parsedValue)}</span>;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const ProposalTermsRenderer = ({ data }: ProposalTermsProps) => {
|
|
||||||
return (
|
|
||||||
<RoundedWrapper paddingBottom={true}>
|
|
||||||
{Object.keys(data)
|
|
||||||
.filter(
|
|
||||||
(key) =>
|
|
||||||
!['__typename', 'closingDatetime', 'enactmentDatetime'].includes(
|
|
||||||
key
|
|
||||||
)
|
|
||||||
)
|
|
||||||
.map((key, index) => (
|
|
||||||
<div key={index}>
|
|
||||||
<RenderTerm title={key} value={data[key]} />
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</RoundedWrapper>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const ProposalTerms = ({ data }: ProposalTermsProps) => {
|
|
||||||
const { t } = useTranslation();
|
|
||||||
const [showTerms, setShowTerms] = useState(false);
|
|
||||||
const showTermsIconClasses = classnames('mb-4', {
|
|
||||||
'rotate-180': showTerms,
|
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
|
||||||
<section data-testid="proposal-terms">
|
|
||||||
<button
|
|
||||||
onClick={() => setShowTerms(!showTerms)}
|
|
||||||
data-testid="proposal-terms-toggle"
|
|
||||||
>
|
|
||||||
<div className="flex items-center gap-3">
|
|
||||||
<SubHeading title={t('proposalDetails')} />
|
|
||||||
<div className={showTermsIconClasses}>
|
|
||||||
<Icon name="chevron-down" size={8} />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</button>
|
|
||||||
|
|
||||||
{showTerms && <ProposalTermsRenderer data={data} />}
|
|
||||||
</section>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@@ -25,11 +25,8 @@ jest.mock('../proposal-detail-header/proposal-header', () => ({
|
|||||||
jest.mock('../proposal-change-table', () => ({
|
jest.mock('../proposal-change-table', () => ({
|
||||||
ProposalChangeTable: () => <div data-testid="proposal-change-table"></div>,
|
ProposalChangeTable: () => <div data-testid="proposal-change-table"></div>,
|
||||||
}));
|
}));
|
||||||
jest.mock('../proposal-json', () => ({
|
jest.mock('../proposal-terms-json', () => ({
|
||||||
ProposalJson: () => <div data-testid="proposal-json"></div>,
|
ProposalTermsJson: () => <div data-testid="proposal-terms-json"></div>,
|
||||||
}));
|
|
||||||
jest.mock('../proposal-terms/proposal-terms', () => ({
|
|
||||||
ProposalTerms: () => <div data-testid="proposal-terms"></div>,
|
|
||||||
}));
|
}));
|
||||||
jest.mock('../proposal-votes-table', () => ({
|
jest.mock('../proposal-votes-table', () => ({
|
||||||
ProposalVotesTable: () => <div data-testid="proposal-votes-table"></div>,
|
ProposalVotesTable: () => <div data-testid="proposal-votes-table"></div>,
|
||||||
@@ -52,8 +49,7 @@ it('renders each section', async () => {
|
|||||||
render(<Proposal proposal={proposal as ProposalQuery['proposal']} />);
|
render(<Proposal proposal={proposal as ProposalQuery['proposal']} />);
|
||||||
expect(await screen.findByTestId('proposal-header')).toBeInTheDocument();
|
expect(await screen.findByTestId('proposal-header')).toBeInTheDocument();
|
||||||
expect(screen.getByTestId('proposal-change-table')).toBeInTheDocument();
|
expect(screen.getByTestId('proposal-change-table')).toBeInTheDocument();
|
||||||
expect(screen.getByTestId('proposal-json')).toBeInTheDocument();
|
expect(screen.getByTestId('proposal-terms-json')).toBeInTheDocument();
|
||||||
expect(screen.getByTestId('proposal-terms')).toBeInTheDocument();
|
|
||||||
expect(screen.getByTestId('proposal-votes-table')).toBeInTheDocument();
|
expect(screen.getByTestId('proposal-votes-table')).toBeInTheDocument();
|
||||||
expect(screen.getByTestId('proposal-vote-details')).toBeInTheDocument();
|
expect(screen.getByTestId('proposal-vote-details')).toBeInTheDocument();
|
||||||
expect(screen.queryByTestId('proposal-list-asset')).not.toBeInTheDocument();
|
expect(screen.queryByTestId('proposal-list-asset')).not.toBeInTheDocument();
|
||||||
|
|||||||
@@ -6,10 +6,8 @@ import { AsyncRenderer, RoundedWrapper } from '@vegaprotocol/ui-toolkit';
|
|||||||
import { ProposalHeader } from '../proposal-detail-header/proposal-header';
|
import { ProposalHeader } from '../proposal-detail-header/proposal-header';
|
||||||
import type { ProposalFieldsFragment } from '../../proposals/__generated__/Proposals';
|
import type { ProposalFieldsFragment } from '../../proposals/__generated__/Proposals';
|
||||||
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
|
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
|
||||||
import { ProposalDescription } from '../proposal-description';
|
|
||||||
import { ProposalChangeTable } from '../proposal-change-table';
|
import { ProposalChangeTable } from '../proposal-change-table';
|
||||||
import { ProposalJson } from '../proposal-json';
|
import { ProposalTermsJson } from '../proposal-terms-json';
|
||||||
import { ProposalTerms } from '../proposal-terms';
|
|
||||||
import { ProposalVotesTable } from '../proposal-votes-table';
|
import { ProposalVotesTable } from '../proposal-votes-table';
|
||||||
import { VoteDetails } from '../vote-details';
|
import { VoteDetails } from '../vote-details';
|
||||||
import { ListAsset } from '../list-asset';
|
import { ListAsset } from '../list-asset';
|
||||||
@@ -22,7 +20,7 @@ export enum ProposalType {
|
|||||||
PROPOSAL_NETWORK_PARAMETER = 'PROPOSAL_NETWORK_PARAMETER',
|
PROPOSAL_NETWORK_PARAMETER = 'PROPOSAL_NETWORK_PARAMETER',
|
||||||
PROPOSAL_FREEFORM = 'PROPOSAL_FREEFORM',
|
PROPOSAL_FREEFORM = 'PROPOSAL_FREEFORM',
|
||||||
}
|
}
|
||||||
export interface ProposalProps {
|
interface ProposalProps {
|
||||||
proposal: ProposalFieldsFragment | ProposalQuery['proposal'];
|
proposal: ProposalFieldsFragment | ProposalQuery['proposal'];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -80,11 +78,9 @@ export const Proposal = ({ proposal }: ProposalProps) => {
|
|||||||
<AsyncRenderer data={params} loading={loading} error={error}>
|
<AsyncRenderer data={params} loading={loading} error={error}>
|
||||||
<section data-testid="proposal">
|
<section data-testid="proposal">
|
||||||
<ProposalHeader proposal={proposal} isListItem={false} />
|
<ProposalHeader proposal={proposal} isListItem={false} />
|
||||||
|
|
||||||
<div className="my-10">
|
<div className="my-10">
|
||||||
<ProposalChangeTable proposal={proposal} />
|
<ProposalChangeTable proposal={proposal} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{proposal.terms.change.__typename === 'NewAsset' &&
|
{proposal.terms.change.__typename === 'NewAsset' &&
|
||||||
proposal.terms.change.source.__typename === 'ERC20' &&
|
proposal.terms.change.source.__typename === 'ERC20' &&
|
||||||
proposal.id ? (
|
proposal.id ? (
|
||||||
@@ -94,20 +90,7 @@ export const Proposal = ({ proposal }: ProposalProps) => {
|
|||||||
lifetimeLimit={proposal.terms.change.source.lifetimeLimit}
|
lifetimeLimit={proposal.terms.change.source.lifetimeLimit}
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
|
<div className="mb-12">
|
||||||
<div className="mb-4">
|
|
||||||
<ProposalDescription description={proposal.rationale.description} />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="mb-4">
|
|
||||||
<ProposalTerms data={proposal.terms} />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="mb-6">
|
|
||||||
<ProposalJson proposal={proposal} />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="mb-10">
|
|
||||||
<RoundedWrapper paddingBottom={true}>
|
<RoundedWrapper paddingBottom={true}>
|
||||||
<VoteDetails
|
<VoteDetails
|
||||||
proposal={proposal}
|
proposal={proposal}
|
||||||
@@ -119,10 +102,10 @@ export const Proposal = ({ proposal }: ProposalProps) => {
|
|||||||
/>
|
/>
|
||||||
</RoundedWrapper>
|
</RoundedWrapper>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="mb-4">
|
<div className="mb-4">
|
||||||
<ProposalVotesTable proposal={proposal} proposalType={proposalType} />
|
<ProposalVotesTable proposal={proposal} proposalType={proposalType} />
|
||||||
</div>
|
</div>
|
||||||
|
<ProposalTermsJson terms={proposal.terms} />
|
||||||
</section>
|
</section>
|
||||||
</AsyncRenderer>
|
</AsyncRenderer>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ const openProposalClosesNextMonth = generateProposal({
|
|||||||
},
|
},
|
||||||
terms: {
|
terms: {
|
||||||
closingDatetime: nextMonth.toString(),
|
closingDatetime: nextMonth.toString(),
|
||||||
|
enactmentDatetime: nextMonth.toString(),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -35,6 +36,7 @@ const openProposalClosesNextWeek = generateProposal({
|
|||||||
},
|
},
|
||||||
terms: {
|
terms: {
|
||||||
closingDatetime: nextWeek.toString(),
|
closingDatetime: nextWeek.toString(),
|
||||||
|
enactmentDatetime: nextWeek.toString(),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -43,6 +45,7 @@ const enactedProposalClosedLastWeek = generateProposal({
|
|||||||
state: ProposalState.STATE_ENACTED,
|
state: ProposalState.STATE_ENACTED,
|
||||||
terms: {
|
terms: {
|
||||||
closingDatetime: lastWeek.toString(),
|
closingDatetime: lastWeek.toString(),
|
||||||
|
enactmentDatetime: lastWeek.toString(),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -51,6 +54,7 @@ const failedProposalClosedLastMonth = generateProposal({
|
|||||||
state: ProposalState.STATE_FAILED,
|
state: ProposalState.STATE_FAILED,
|
||||||
terms: {
|
terms: {
|
||||||
closingDatetime: lastMonth.toString(),
|
closingDatetime: lastMonth.toString(),
|
||||||
|
enactmentDatetime: lastMonth.toString(),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -8,11 +8,12 @@ import { ProposalsListFilter } from '../proposals-list-filter';
|
|||||||
import Routes from '../../../routes';
|
import Routes from '../../../routes';
|
||||||
import { Button, VegaIcon, VegaIconNames } from '@vegaprotocol/ui-toolkit';
|
import { Button, VegaIcon, VegaIconNames } from '@vegaprotocol/ui-toolkit';
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
|
import { createDocsLinks, ExternalLinks } from '@vegaprotocol/utils';
|
||||||
import { ExternalLink } from '@vegaprotocol/ui-toolkit';
|
import { ExternalLink } from '@vegaprotocol/ui-toolkit';
|
||||||
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
|
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
|
||||||
import type { ProposalFieldsFragment } from '../../proposals/__generated__/Proposals';
|
import type { ProposalFieldsFragment } from '../../proposals/__generated__/Proposals';
|
||||||
import type { ProtocolUpgradeProposalFieldsFragment } from '@vegaprotocol/proposals';
|
import type { ProtocolUpgradeProposalFieldsFragment } from '@vegaprotocol/proposals';
|
||||||
import { DocsLinks, ExternalLinks } from '@vegaprotocol/environment';
|
import { useEnvironment } from '@vegaprotocol/environment';
|
||||||
|
|
||||||
interface ProposalsListProps {
|
interface ProposalsListProps {
|
||||||
proposals: Array<ProposalFieldsFragment | ProposalQuery['proposal']>;
|
proposals: Array<ProposalFieldsFragment | ProposalQuery['proposal']>;
|
||||||
@@ -35,6 +36,7 @@ export const ProposalsList = ({
|
|||||||
protocolUpgradeProposals,
|
protocolUpgradeProposals,
|
||||||
lastBlockHeight,
|
lastBlockHeight,
|
||||||
}: ProposalsListProps) => {
|
}: ProposalsListProps) => {
|
||||||
|
const { VEGA_DOCS_URL } = useEnvironment();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [filterString, setFilterString] = useState('');
|
const [filterString, setFilterString] = useState('');
|
||||||
const sortedProposals = proposals.reduce(
|
const sortedProposals = proposals.reduce(
|
||||||
@@ -81,9 +83,9 @@ export const ProposalsList = ({
|
|||||||
marginBottom={false}
|
marginBottom={false}
|
||||||
title={t('pageTitleProposals')}
|
title={t('pageTitleProposals')}
|
||||||
/>
|
/>
|
||||||
{DocsLinks && (
|
{VEGA_DOCS_URL && (
|
||||||
<div className="xs:justify-self-end" data-testid="new-proposal-link">
|
<div className="xs:justify-self-end" data-testid="new-proposal-link">
|
||||||
<ExternalLink href={DocsLinks.PROPOSALS_GUIDE}>
|
<ExternalLink href={createDocsLinks(VEGA_DOCS_URL).PROPOSALS_GUIDE}>
|
||||||
<Button variant="primary" size="sm">
|
<Button variant="primary" size="sm">
|
||||||
<div className="flex items-center gap-1">
|
<div className="flex items-center gap-1">
|
||||||
{t('NewProposal')}
|
{t('NewProposal')}
|
||||||
|
|||||||
+3
-4
@@ -2,10 +2,9 @@ import { render, screen } from '@testing-library/react';
|
|||||||
import { ProposalFormTerms } from './proposal-form-terms';
|
import { ProposalFormTerms } from './proposal-form-terms';
|
||||||
|
|
||||||
jest.mock('@vegaprotocol/environment', () => ({
|
jest.mock('@vegaprotocol/environment', () => ({
|
||||||
...jest.requireActual('@vegaprotocol/environment'),
|
useEnvironment: () => ({
|
||||||
DocsLinks: {
|
VEGA_DOCS_URL: 'https://docs.vega.xyz',
|
||||||
PROPOSALS_GUIDE: 'https://docs.vega.xyz/tutorials/proposals',
|
}),
|
||||||
},
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const renderComponent = () => {
|
const renderComponent = () => {
|
||||||
|
|||||||
@@ -5,7 +5,8 @@ import {
|
|||||||
ExternalLink,
|
ExternalLink,
|
||||||
TextArea,
|
TextArea,
|
||||||
} from '@vegaprotocol/ui-toolkit';
|
} from '@vegaprotocol/ui-toolkit';
|
||||||
import { DocsLinks } from '@vegaprotocol/environment';
|
import { createDocsLinks } from '@vegaprotocol/utils';
|
||||||
|
import { useEnvironment } from '@vegaprotocol/environment';
|
||||||
import type { UseFormRegisterReturn } from 'react-hook-form';
|
import type { UseFormRegisterReturn } from 'react-hook-form';
|
||||||
|
|
||||||
interface ProposalFormTermsProps {
|
interface ProposalFormTermsProps {
|
||||||
@@ -21,20 +22,23 @@ export const ProposalFormTerms = ({
|
|||||||
labelOverride,
|
labelOverride,
|
||||||
docsLink,
|
docsLink,
|
||||||
}: ProposalFormTermsProps) => {
|
}: ProposalFormTermsProps) => {
|
||||||
|
const { VEGA_DOCS_URL } = useEnvironment();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
return (
|
return (
|
||||||
<FormGroup
|
<FormGroup
|
||||||
label={labelOverride || t('ProposalTerms')}
|
label={labelOverride || t('ProposalTerms')}
|
||||||
labelFor="proposal-terms"
|
labelFor="proposal-terms"
|
||||||
>
|
>
|
||||||
{docsLink && DocsLinks && (
|
{docsLink && VEGA_DOCS_URL && (
|
||||||
<div className="mt-[-4px] mb-2 text-sm font-light">
|
<div className="mt-[-4px] mb-2 text-sm font-light">
|
||||||
<span className="mr-1">{t('ProposalTermsText')}</span>
|
<span className="mr-1">{t('ProposalTermsText')}</span>
|
||||||
<ExternalLink
|
<ExternalLink
|
||||||
href={`${DocsLinks.PROPOSALS_GUIDE}${docsLink}`}
|
href={`${
|
||||||
|
createDocsLinks(VEGA_DOCS_URL).PROPOSALS_GUIDE
|
||||||
|
}${docsLink}`}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
>
|
>
|
||||||
{`${DocsLinks.PROPOSALS_GUIDE}${docsLink}`}
|
{`${createDocsLinks(VEGA_DOCS_URL).PROPOSALS_GUIDE}${docsLink}`}
|
||||||
</ExternalLink>
|
</ExternalLink>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|||||||
+1
-3
@@ -32,9 +32,7 @@ describe('ProtocolUpgradeProposalDetailInfo', () => {
|
|||||||
|
|
||||||
it('should render the state', () => {
|
it('should render the state', () => {
|
||||||
const { getByTestId } = renderComponent();
|
const { getByTestId } = renderComponent();
|
||||||
expect(getByTestId('protocol-upgrade-state')).toHaveTextContent(
|
expect(getByTestId('protocol-upgrade-state')).toHaveTextContent('Pending');
|
||||||
'Waiting for validator votes'
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should render the vega release tag', () => {
|
it('should render the vega release tag', () => {
|
||||||
|
|||||||
+1
-7
@@ -26,34 +26,28 @@ describe('ProtocolUpgradeProposalsListItem', () => {
|
|||||||
status:
|
status:
|
||||||
ProtocolUpgradeProposalStatus.PROTOCOL_UPGRADE_PROPOSAL_STATUS_REJECTED,
|
ProtocolUpgradeProposalStatus.PROTOCOL_UPGRADE_PROPOSAL_STATUS_REJECTED,
|
||||||
icon: 'protocol-upgrade-proposal-status-icon-rejected',
|
icon: 'protocol-upgrade-proposal-status-icon-rejected',
|
||||||
text: 'Declined by validators',
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
status:
|
status:
|
||||||
ProtocolUpgradeProposalStatus.PROTOCOL_UPGRADE_PROPOSAL_STATUS_PENDING,
|
ProtocolUpgradeProposalStatus.PROTOCOL_UPGRADE_PROPOSAL_STATUS_PENDING,
|
||||||
icon: 'protocol-upgrade-proposal-status-icon-pending',
|
icon: 'protocol-upgrade-proposal-status-icon-pending',
|
||||||
text: 'Waiting for validator votes',
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
status:
|
status:
|
||||||
ProtocolUpgradeProposalStatus.PROTOCOL_UPGRADE_PROPOSAL_STATUS_APPROVED,
|
ProtocolUpgradeProposalStatus.PROTOCOL_UPGRADE_PROPOSAL_STATUS_APPROVED,
|
||||||
icon: 'protocol-upgrade-proposal-status-icon-approved',
|
icon: 'protocol-upgrade-proposal-status-icon-approved',
|
||||||
text: 'Approved by validators',
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
status:
|
status:
|
||||||
ProtocolUpgradeProposalStatus.PROTOCOL_UPGRADE_PROPOSAL_STATUS_UNSPECIFIED,
|
ProtocolUpgradeProposalStatus.PROTOCOL_UPGRADE_PROPOSAL_STATUS_UNSPECIFIED,
|
||||||
icon: 'protocol-upgrade-proposal-status-icon-unspecified',
|
icon: 'protocol-upgrade-proposal-status-icon-unspecified',
|
||||||
text: 'Unspecified',
|
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
statuses.forEach(({ status, icon, text }) => {
|
statuses.forEach(({ status, icon }) => {
|
||||||
renderComponent({ ...proposal, status });
|
renderComponent({ ...proposal, status });
|
||||||
const statusIcon = screen.getByTestId(icon);
|
const statusIcon = screen.getByTestId(icon);
|
||||||
const textContent = screen.getByText(text);
|
|
||||||
expect(statusIcon).toBeInTheDocument();
|
expect(statusIcon).toBeInTheDocument();
|
||||||
expect(textContent).toBeInTheDocument();
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -111,9 +111,7 @@ describe('Vote buttons', () => {
|
|||||||
</AppStateProvider>
|
</AppStateProvider>
|
||||||
);
|
);
|
||||||
expect(
|
expect(
|
||||||
screen.getByText(
|
screen.getByText('You need some VEGA tokens to participate in governance')
|
||||||
'You need some VEGA tokens to participate in governance.'
|
|
||||||
)
|
|
||||||
).toBeTruthy();
|
).toBeTruthy();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -2,12 +2,7 @@ import { format } from 'date-fns';
|
|||||||
import React, { useMemo } from 'react';
|
import React, { useMemo } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { useVegaWallet, useVegaWalletDialogStore } from '@vegaprotocol/wallet';
|
import { useVegaWallet, useVegaWalletDialogStore } from '@vegaprotocol/wallet';
|
||||||
import {
|
import { AsyncRenderer, Button, ButtonLink } from '@vegaprotocol/ui-toolkit';
|
||||||
AsyncRenderer,
|
|
||||||
Button,
|
|
||||||
ButtonLink,
|
|
||||||
ExternalLink,
|
|
||||||
} from '@vegaprotocol/ui-toolkit';
|
|
||||||
import { addDecimal, toBigNum } from '@vegaprotocol/utils';
|
import { addDecimal, toBigNum } from '@vegaprotocol/utils';
|
||||||
import { ProposalState, VoteValue } from '@vegaprotocol/types';
|
import { ProposalState, VoteValue } from '@vegaprotocol/types';
|
||||||
import {
|
import {
|
||||||
@@ -166,12 +161,7 @@ export const VoteButtons = ({
|
|||||||
{changeVote || (voteState === VoteState.NotCast && proposalVotable) ? (
|
{changeVote || (voteState === VoteState.NotCast && proposalVotable) ? (
|
||||||
<>
|
<>
|
||||||
{currentStakeAvailable.isLessThanOrEqualTo(0) && (
|
{currentStakeAvailable.isLessThanOrEqualTo(0) && (
|
||||||
<>
|
<p data-testid="no-stake-available">{t('noGovernanceTokens')}</p>
|
||||||
<p data-testid="no-stake-available">{t('noGovernanceTokens')}.</p>
|
|
||||||
<ExternalLink href="https://blog.vega.xyz/how-to-vote-on-vega-2195d1e52ec5">
|
|
||||||
{t('findOutMoreAboutHowToVote')}
|
|
||||||
</ExternalLink>
|
|
||||||
</>
|
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div className="flex gap-4" data-testid="vote-buttons">
|
<div className="flex gap-4" data-testid="vote-buttons">
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { formatDistanceToNow } from 'date-fns';
|
import { formatDistanceToNow } from 'date-fns';
|
||||||
import { RoundedWrapper, Icon, ExternalLink } from '@vegaprotocol/ui-toolkit';
|
import { RoundedWrapper, Icon } from '@vegaprotocol/ui-toolkit';
|
||||||
import { useVegaWallet } from '@vegaprotocol/wallet';
|
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||||
import { ProposalState } from '@vegaprotocol/types';
|
import { ProposalState } from '@vegaprotocol/types';
|
||||||
import { useVoteSubmit, VoteProgress } from '@vegaprotocol/proposals';
|
import { useVoteSubmit, VoteProgress } from '@vegaprotocol/proposals';
|
||||||
@@ -202,12 +202,7 @@ export const VoteDetails = ({
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
<section className="mt-10">
|
<section className="mt-10">
|
||||||
{proposal?.state === ProposalState.STATE_OPEN ? (
|
<SubHeading title={t('castYourVote')} />
|
||||||
<SubHeading title={t('castYourVote')} />
|
|
||||||
) : (
|
|
||||||
<SubHeading title={t('yourVote')} />
|
|
||||||
)}
|
|
||||||
|
|
||||||
{pubKey ? (
|
{pubKey ? (
|
||||||
proposal && (
|
proposal && (
|
||||||
<VoteButtonsContainer
|
<VoteButtonsContainer
|
||||||
@@ -229,9 +224,6 @@ export const VoteDetails = ({
|
|||||||
<Icon name={'info-sign'} />
|
<Icon name={'info-sign'} />
|
||||||
<div>{t('connectAVegaWalletToVote')}</div>
|
<div>{t('connectAVegaWalletToVote')}</div>
|
||||||
</div>
|
</div>
|
||||||
<ExternalLink href="https://blog.vega.xyz/how-to-vote-on-vega-2195d1e52ec5">
|
|
||||||
{t('findOutMoreAboutHowToVote')}
|
|
||||||
</ExternalLink>
|
|
||||||
</div>
|
</div>
|
||||||
<ConnectToVega />
|
<ConnectToVega />
|
||||||
</RoundedWrapper>
|
</RoundedWrapper>
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ const mockAppState: AppState = {
|
|||||||
totalSupply: mockTotalSupply,
|
totalSupply: mockTotalSupply,
|
||||||
vegaWalletOverlay: false,
|
vegaWalletOverlay: false,
|
||||||
vegaWalletManageOverlay: false,
|
vegaWalletManageOverlay: false,
|
||||||
|
ethConnectOverlay: false,
|
||||||
transactionOverlay: false,
|
transactionOverlay: false,
|
||||||
bannerMessage: '',
|
bannerMessage: '',
|
||||||
disconnectNotice: false,
|
disconnectNotice: false,
|
||||||
|
|||||||
@@ -20,11 +20,8 @@ import { useProtocolUpgradeProposalsQuery } from '@vegaprotocol/proposals';
|
|||||||
const orderByDate = (arr: ProposalFieldsFragment[]) =>
|
const orderByDate = (arr: ProposalFieldsFragment[]) =>
|
||||||
orderBy(
|
orderBy(
|
||||||
arr,
|
arr,
|
||||||
[
|
[(p) => new Date(p?.terms?.closingDatetime).getTime(), (p) => p.id],
|
||||||
(p) => new Date(p?.terms?.closingDatetime).getTime(),
|
['desc', 'desc']
|
||||||
(p) => new Date(p?.datetime).getTime(),
|
|
||||||
],
|
|
||||||
['asc', 'asc']
|
|
||||||
);
|
);
|
||||||
|
|
||||||
const orderByUpgradeBlockHeight = (
|
const orderByUpgradeBlockHeight = (
|
||||||
|
|||||||
@@ -10,10 +10,9 @@ import type { MockedResponse } from '@apollo/client/testing';
|
|||||||
import { NetworkParamsDocument } from '@vegaprotocol/network-parameters';
|
import { NetworkParamsDocument } from '@vegaprotocol/network-parameters';
|
||||||
|
|
||||||
jest.mock('@vegaprotocol/environment', () => ({
|
jest.mock('@vegaprotocol/environment', () => ({
|
||||||
...jest.requireActual('@vegaprotocol/environment'),
|
useEnvironment: () => ({
|
||||||
DocsLinks: {
|
VEGA_DOCS_URL: 'https://docs.vega.xyz',
|
||||||
PROPOSALS_GUIDE: 'https://docs.vega.xyz/tutorials/proposals',
|
}),
|
||||||
},
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const updateMarketNetworkParamsQueryMock: MockedResponse<NetworkParamsQuery> = {
|
const updateMarketNetworkParamsQueryMock: MockedResponse<NetworkParamsQuery> = {
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import {
|
|||||||
useProposalSubmit,
|
useProposalSubmit,
|
||||||
deadlineToRoundedHours,
|
deadlineToRoundedHours,
|
||||||
} from '@vegaprotocol/proposals';
|
} from '@vegaprotocol/proposals';
|
||||||
import { useEnvironment, DocsLinks } from '@vegaprotocol/environment';
|
import { useEnvironment } from '@vegaprotocol/environment';
|
||||||
import {
|
import {
|
||||||
ProposalFormDescription,
|
ProposalFormDescription,
|
||||||
ProposalFormSubheader,
|
ProposalFormSubheader,
|
||||||
@@ -17,6 +17,7 @@ import {
|
|||||||
import { ProposalMinRequirements } from '../../components/shared';
|
import { ProposalMinRequirements } from '../../components/shared';
|
||||||
import { AsyncRenderer, ExternalLink } from '@vegaprotocol/ui-toolkit';
|
import { AsyncRenderer, ExternalLink } from '@vegaprotocol/ui-toolkit';
|
||||||
import { Heading } from '../../../../components/heading';
|
import { Heading } from '../../../../components/heading';
|
||||||
|
import { createDocsLinks } from '@vegaprotocol/utils';
|
||||||
import {
|
import {
|
||||||
NetworkParams,
|
NetworkParams,
|
||||||
useNetworkParams,
|
useNetworkParams,
|
||||||
@@ -41,7 +42,7 @@ export const ProposeFreeform = () => {
|
|||||||
NetworkParams.governance_proposal_freeform_minProposerBalance,
|
NetworkParams.governance_proposal_freeform_minProposerBalance,
|
||||||
NetworkParams.spam_protection_proposal_min_tokens,
|
NetworkParams.spam_protection_proposal_min_tokens,
|
||||||
]);
|
]);
|
||||||
const { VEGA_EXPLORER_URL } = useEnvironment();
|
const { VEGA_DOCS_URL, VEGA_EXPLORER_URL } = useEnvironment();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const {
|
const {
|
||||||
register,
|
register,
|
||||||
@@ -110,13 +111,17 @@ export const ProposeFreeform = () => {
|
|||||||
userAction={ProposalUserAction.CREATE}
|
userAction={ProposalUserAction.CREATE}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{DocsLinks && (
|
{VEGA_DOCS_URL && (
|
||||||
<p className="text-sm" data-testid="proposal-docs-link">
|
<p className="text-sm" data-testid="proposal-docs-link">
|
||||||
<span className="mr-1">{t('ProposalTermsText')}</span>
|
<span className="mr-1">{t('ProposalTermsText')}</span>
|
||||||
<ExternalLink
|
<ExternalLink
|
||||||
href={`${DocsLinks.PROPOSALS_GUIDE}${DOCS_LINK}`}
|
href={`${
|
||||||
|
createDocsLinks(VEGA_DOCS_URL).PROPOSALS_GUIDE
|
||||||
|
}${DOCS_LINK}`}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
>{`${DocsLinks.PROPOSALS_GUIDE}${DOCS_LINK}`}</ExternalLink>
|
>{`${
|
||||||
|
createDocsLinks(VEGA_DOCS_URL).PROPOSALS_GUIDE
|
||||||
|
}${DOCS_LINK}`}</ExternalLink>
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|||||||
+3
-4
@@ -10,10 +10,9 @@ import { NetworkParamsDocument } from '@vegaprotocol/network-parameters';
|
|||||||
import type { MockedResponse } from '@apollo/client/testing';
|
import type { MockedResponse } from '@apollo/client/testing';
|
||||||
|
|
||||||
jest.mock('@vegaprotocol/environment', () => ({
|
jest.mock('@vegaprotocol/environment', () => ({
|
||||||
...jest.requireActual('@vegaprotocol/environment'),
|
useEnvironment: () => ({
|
||||||
DocsLinks: {
|
VEGA_DOCS_URL: 'https://docs.vega.xyz',
|
||||||
PROPOSALS_GUIDE: 'https://docs.vega.xyz/tutorials/proposals',
|
}),
|
||||||
},
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const updateMarketNetworkParamsQueryMock: MockedResponse<NetworkParamsQuery> = {
|
const updateMarketNetworkParamsQueryMock: MockedResponse<NetworkParamsQuery> = {
|
||||||
|
|||||||
+13
-6
@@ -1,7 +1,10 @@
|
|||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { useForm } from 'react-hook-form';
|
import { useForm } from 'react-hook-form';
|
||||||
import { suitableForSyntaxHighlighter } from '@vegaprotocol/utils';
|
import {
|
||||||
|
createDocsLinks,
|
||||||
|
suitableForSyntaxHighlighter,
|
||||||
|
} from '@vegaprotocol/utils';
|
||||||
import { useNetworkParams } from '@vegaprotocol/network-parameters';
|
import { useNetworkParams } from '@vegaprotocol/network-parameters';
|
||||||
import {
|
import {
|
||||||
getClosingTimestamp,
|
getClosingTimestamp,
|
||||||
@@ -9,7 +12,7 @@ import {
|
|||||||
useProposalSubmit,
|
useProposalSubmit,
|
||||||
doesValueEquateToParam,
|
doesValueEquateToParam,
|
||||||
} from '@vegaprotocol/proposals';
|
} from '@vegaprotocol/proposals';
|
||||||
import { useEnvironment, DocsLinks } from '@vegaprotocol/environment';
|
import { useEnvironment } from '@vegaprotocol/environment';
|
||||||
import {
|
import {
|
||||||
ProposalFormDescription,
|
ProposalFormDescription,
|
||||||
ProposalFormSubheader,
|
ProposalFormSubheader,
|
||||||
@@ -81,7 +84,7 @@ export const ProposeNetworkParameter = () => {
|
|||||||
loading: networkParamsLoading,
|
loading: networkParamsLoading,
|
||||||
error: networkParamsError,
|
error: networkParamsError,
|
||||||
} = useNetworkParams();
|
} = useNetworkParams();
|
||||||
const { VEGA_EXPLORER_URL } = useEnvironment();
|
const { VEGA_EXPLORER_URL, VEGA_DOCS_URL } = useEnvironment();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const {
|
const {
|
||||||
register,
|
register,
|
||||||
@@ -172,13 +175,17 @@ export const ProposeNetworkParameter = () => {
|
|||||||
userAction={ProposalUserAction.CREATE}
|
userAction={ProposalUserAction.CREATE}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{DocsLinks && (
|
{VEGA_DOCS_URL && (
|
||||||
<p className="text-sm" data-testid="proposal-docs-link">
|
<p className="text-sm" data-testid="proposal-docs-link">
|
||||||
<span className="mr-1">{t('ProposalTermsText')}</span>
|
<span className="mr-1">{t('ProposalTermsText')}</span>
|
||||||
<ExternalLink
|
<ExternalLink
|
||||||
href={`${DocsLinks.PROPOSALS_GUIDE}${DOCS_LINK}`}
|
href={`${
|
||||||
|
createDocsLinks(VEGA_DOCS_URL).PROPOSALS_GUIDE
|
||||||
|
}${DOCS_LINK}`}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
>{`${DocsLinks.PROPOSALS_GUIDE}${DOCS_LINK}`}</ExternalLink>
|
>{`${
|
||||||
|
createDocsLinks(VEGA_DOCS_URL).PROPOSALS_GUIDE
|
||||||
|
}${DOCS_LINK}`}</ExternalLink>
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|||||||
@@ -10,10 +10,9 @@ import type { MockedResponse } from '@apollo/client/testing';
|
|||||||
import { NetworkParamsDocument } from '@vegaprotocol/network-parameters';
|
import { NetworkParamsDocument } from '@vegaprotocol/network-parameters';
|
||||||
|
|
||||||
jest.mock('@vegaprotocol/environment', () => ({
|
jest.mock('@vegaprotocol/environment', () => ({
|
||||||
...jest.requireActual('@vegaprotocol/environment'),
|
useEnvironment: () => ({
|
||||||
DocsLinks: {
|
VEGA_DOCS_URL: 'https://docs.vega.xyz',
|
||||||
PROPOSALS_GUIDE: 'https://docs.vega.xyz/tutorials/proposals',
|
}),
|
||||||
},
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const newAssetNetworkParamsQueryMock: MockedResponse<NetworkParamsQuery> = {
|
const newAssetNetworkParamsQueryMock: MockedResponse<NetworkParamsQuery> = {
|
||||||
|
|||||||
@@ -7,8 +7,8 @@ import {
|
|||||||
useProposalSubmit,
|
useProposalSubmit,
|
||||||
doesValueEquateToParam,
|
doesValueEquateToParam,
|
||||||
} from '@vegaprotocol/proposals';
|
} from '@vegaprotocol/proposals';
|
||||||
import { useEnvironment, DocsLinks } from '@vegaprotocol/environment';
|
import { useEnvironment } from '@vegaprotocol/environment';
|
||||||
import { validateJson } from '@vegaprotocol/utils';
|
import { createDocsLinks, validateJson } from '@vegaprotocol/utils';
|
||||||
import {
|
import {
|
||||||
NetworkParams,
|
NetworkParams,
|
||||||
useNetworkParams,
|
useNetworkParams,
|
||||||
@@ -53,7 +53,7 @@ export const ProposeNewAsset = () => {
|
|||||||
NetworkParams.governance_proposal_asset_minProposerBalance,
|
NetworkParams.governance_proposal_asset_minProposerBalance,
|
||||||
NetworkParams.spam_protection_proposal_min_tokens,
|
NetworkParams.spam_protection_proposal_min_tokens,
|
||||||
]);
|
]);
|
||||||
const { VEGA_EXPLORER_URL } = useEnvironment();
|
const { VEGA_EXPLORER_URL, VEGA_DOCS_URL } = useEnvironment();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const {
|
const {
|
||||||
register,
|
register,
|
||||||
@@ -142,13 +142,17 @@ export const ProposeNewAsset = () => {
|
|||||||
userAction={ProposalUserAction.CREATE}
|
userAction={ProposalUserAction.CREATE}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{DocsLinks && (
|
{VEGA_DOCS_URL && (
|
||||||
<p className="text-sm" data-testid="proposal-docs-link">
|
<p className="text-sm" data-testid="proposal-docs-link">
|
||||||
<span className="mr-1">{t('ProposalTermsText')}</span>
|
<span className="mr-1">{t('ProposalTermsText')}</span>
|
||||||
<ExternalLink
|
<ExternalLink
|
||||||
href={`${DocsLinks.PROPOSALS_GUIDE}${DOCS_LINK}`}
|
href={`${
|
||||||
|
createDocsLinks(VEGA_DOCS_URL).PROPOSALS_GUIDE
|
||||||
|
}${DOCS_LINK}`}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
>{`${DocsLinks.PROPOSALS_GUIDE}${DOCS_LINK}`}</ExternalLink>
|
>{`${
|
||||||
|
createDocsLinks(VEGA_DOCS_URL).PROPOSALS_GUIDE
|
||||||
|
}${DOCS_LINK}`}</ExternalLink>
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|||||||
@@ -10,10 +10,9 @@ import type { NetworkParamsQuery } from '@vegaprotocol/network-parameters';
|
|||||||
import { NetworkParamsDocument } from '@vegaprotocol/network-parameters';
|
import { NetworkParamsDocument } from '@vegaprotocol/network-parameters';
|
||||||
|
|
||||||
jest.mock('@vegaprotocol/environment', () => ({
|
jest.mock('@vegaprotocol/environment', () => ({
|
||||||
...jest.requireActual('@vegaprotocol/environment'),
|
useEnvironment: () => ({
|
||||||
DocsLinks: {
|
VEGA_DOCS_URL: 'https://docs.vega.xyz',
|
||||||
PROPOSALS_GUIDE: 'https://docs.vega.xyz/tutorials/proposals',
|
}),
|
||||||
},
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const newMarketNetworkParamsQueryMock: MockedResponse<NetworkParamsQuery> = {
|
const newMarketNetworkParamsQueryMock: MockedResponse<NetworkParamsQuery> = {
|
||||||
|
|||||||
@@ -6,8 +6,8 @@ import {
|
|||||||
useProposalSubmit,
|
useProposalSubmit,
|
||||||
doesValueEquateToParam,
|
doesValueEquateToParam,
|
||||||
} from '@vegaprotocol/proposals';
|
} from '@vegaprotocol/proposals';
|
||||||
import { useEnvironment, DocsLinks } from '@vegaprotocol/environment';
|
import { useEnvironment } from '@vegaprotocol/environment';
|
||||||
import { validateJson } from '@vegaprotocol/utils';
|
import { createDocsLinks, validateJson } from '@vegaprotocol/utils';
|
||||||
import {
|
import {
|
||||||
NetworkParams,
|
NetworkParams,
|
||||||
useNetworkParams,
|
useNetworkParams,
|
||||||
@@ -51,7 +51,7 @@ export const ProposeNewMarket = () => {
|
|||||||
NetworkParams.governance_proposal_market_minProposerBalance,
|
NetworkParams.governance_proposal_market_minProposerBalance,
|
||||||
NetworkParams.spam_protection_proposal_min_tokens,
|
NetworkParams.spam_protection_proposal_min_tokens,
|
||||||
]);
|
]);
|
||||||
const { VEGA_EXPLORER_URL } = useEnvironment();
|
const { VEGA_EXPLORER_URL, VEGA_DOCS_URL } = useEnvironment();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const {
|
const {
|
||||||
register,
|
register,
|
||||||
@@ -132,13 +132,17 @@ export const ProposeNewMarket = () => {
|
|||||||
userAction={ProposalUserAction.CREATE}
|
userAction={ProposalUserAction.CREATE}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{DocsLinks && (
|
{VEGA_DOCS_URL && (
|
||||||
<p className="text-sm" data-testid="proposal-docs-link">
|
<p className="text-sm" data-testid="proposal-docs-link">
|
||||||
<span className="mr-1">{t('ProposalTermsText')}</span>
|
<span className="mr-1">{t('ProposalTermsText')}</span>
|
||||||
<ExternalLink
|
<ExternalLink
|
||||||
href={`${DocsLinks.PROPOSALS_GUIDE}${DOCS_LINK}`}
|
href={`${
|
||||||
|
createDocsLinks(VEGA_DOCS_URL).PROPOSALS_GUIDE
|
||||||
|
}${DOCS_LINK}`}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
>{`${DocsLinks.PROPOSALS_GUIDE}${DOCS_LINK}`}</ExternalLink>
|
>{`${
|
||||||
|
createDocsLinks(VEGA_DOCS_URL).PROPOSALS_GUIDE
|
||||||
|
}${DOCS_LINK}`}</ExternalLink>
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|||||||
@@ -2,11 +2,12 @@ import Routes from '../../routes';
|
|||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
import { ExternalLink } from '@vegaprotocol/ui-toolkit';
|
import { ExternalLink } from '@vegaprotocol/ui-toolkit';
|
||||||
import { useEnvironment, DocsLinks } from '@vegaprotocol/environment';
|
import { useEnvironment } from '@vegaprotocol/environment';
|
||||||
import { Heading } from '../../../components/heading';
|
import { Heading } from '../../../components/heading';
|
||||||
|
import { createDocsLinks } from '@vegaprotocol/utils';
|
||||||
|
|
||||||
export const Propose = () => {
|
export const Propose = () => {
|
||||||
const { VEGA_EXPLORER_URL } = useEnvironment();
|
const { VEGA_DOCS_URL, VEGA_EXPLORER_URL } = useEnvironment();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -14,11 +15,14 @@ export const Propose = () => {
|
|||||||
<section className="pb-6">
|
<section className="pb-6">
|
||||||
<Heading title={t('NewProposal')} />
|
<Heading title={t('NewProposal')} />
|
||||||
<div className="text-sm">
|
<div className="text-sm">
|
||||||
{DocsLinks && (
|
{VEGA_DOCS_URL && (
|
||||||
<p>
|
<p>
|
||||||
<span className="mr-1">{t('ProposalTermsText')}</span>
|
<span className="mr-1">{t('ProposalTermsText')}</span>
|
||||||
<ExternalLink href={DocsLinks.PROPOSALS_GUIDE} target="_blank">
|
<ExternalLink
|
||||||
{DocsLinks.PROPOSALS_GUIDE}
|
href={createDocsLinks(VEGA_DOCS_URL).PROPOSALS_GUIDE}
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
{createDocsLinks(VEGA_DOCS_URL).PROPOSALS_GUIDE}
|
||||||
</ExternalLink>
|
</ExternalLink>
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { useForm } from 'react-hook-form';
|
import { useForm } from 'react-hook-form';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { useEnvironment, DocsLinks } from '@vegaprotocol/environment';
|
import { useEnvironment } from '@vegaprotocol/environment';
|
||||||
import { Heading } from '../../../../components/heading';
|
import { Heading } from '../../../../components/heading';
|
||||||
import {
|
import {
|
||||||
AsyncRenderer,
|
AsyncRenderer,
|
||||||
@@ -9,7 +9,7 @@ import {
|
|||||||
InputError,
|
InputError,
|
||||||
TextArea,
|
TextArea,
|
||||||
} from '@vegaprotocol/ui-toolkit';
|
} from '@vegaprotocol/ui-toolkit';
|
||||||
import { validateJson } from '@vegaprotocol/utils';
|
import { createDocsLinks, validateJson } from '@vegaprotocol/utils';
|
||||||
import {
|
import {
|
||||||
NetworkParams,
|
NetworkParams,
|
||||||
useNetworkParams,
|
useNetworkParams,
|
||||||
@@ -39,7 +39,7 @@ export const ProposeRaw = () => {
|
|||||||
NetworkParams.governance_proposal_freeform_minProposerBalance,
|
NetworkParams.governance_proposal_freeform_minProposerBalance,
|
||||||
NetworkParams.spam_protection_proposal_min_tokens,
|
NetworkParams.spam_protection_proposal_min_tokens,
|
||||||
]);
|
]);
|
||||||
const { VEGA_EXPLORER_URL } = useEnvironment();
|
const { VEGA_EXPLORER_URL, VEGA_DOCS_URL } = useEnvironment();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const {
|
const {
|
||||||
register,
|
register,
|
||||||
@@ -79,11 +79,14 @@ export const ProposeRaw = () => {
|
|||||||
spamProtectionMin={params.spam_protection_proposal_min_tokens}
|
spamProtectionMin={params.spam_protection_proposal_min_tokens}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{DocsLinks && (
|
{VEGA_DOCS_URL && (
|
||||||
<p className="text-sm" data-testid="proposal-docs-link">
|
<p className="text-sm" data-testid="proposal-docs-link">
|
||||||
<span className="mr-1">{t('ProposalTermsText')}</span>
|
<span className="mr-1">{t('ProposalTermsText')}</span>
|
||||||
<ExternalLink href={DocsLinks.PROPOSALS_GUIDE} target="_blank">
|
<ExternalLink
|
||||||
{DocsLinks.PROPOSALS_GUIDE}
|
href={createDocsLinks(VEGA_DOCS_URL).PROPOSALS_GUIDE}
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
{createDocsLinks(VEGA_DOCS_URL).PROPOSALS_GUIDE}
|
||||||
</ExternalLink>
|
</ExternalLink>
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
|
|||||||
+3
-4
@@ -10,10 +10,9 @@ import type { MockedResponse } from '@apollo/client/testing';
|
|||||||
import { NetworkParamsDocument } from '@vegaprotocol/network-parameters';
|
import { NetworkParamsDocument } from '@vegaprotocol/network-parameters';
|
||||||
|
|
||||||
jest.mock('@vegaprotocol/environment', () => ({
|
jest.mock('@vegaprotocol/environment', () => ({
|
||||||
...jest.requireActual('@vegaprotocol/environment'),
|
useEnvironment: () => ({
|
||||||
DocsLinks: {
|
VEGA_DOCS_URL: 'https://docs.vega.xyz',
|
||||||
PROPOSALS_GUIDE: 'https://docs.vega.xyz/tutorials/proposals',
|
}),
|
||||||
},
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const updateAssetNetworkParamsQueryMock: MockedResponse<NetworkParamsQuery> = {
|
const updateAssetNetworkParamsQueryMock: MockedResponse<NetworkParamsQuery> = {
|
||||||
|
|||||||
@@ -6,8 +6,8 @@ import {
|
|||||||
useProposalSubmit,
|
useProposalSubmit,
|
||||||
doesValueEquateToParam,
|
doesValueEquateToParam,
|
||||||
} from '@vegaprotocol/proposals';
|
} from '@vegaprotocol/proposals';
|
||||||
import { useEnvironment, DocsLinks } from '@vegaprotocol/environment';
|
import { useEnvironment } from '@vegaprotocol/environment';
|
||||||
import { validateJson } from '@vegaprotocol/utils';
|
import { createDocsLinks, validateJson } from '@vegaprotocol/utils';
|
||||||
import {
|
import {
|
||||||
NetworkParams,
|
NetworkParams,
|
||||||
useNetworkParams,
|
useNetworkParams,
|
||||||
@@ -51,7 +51,7 @@ export const ProposeUpdateAsset = () => {
|
|||||||
NetworkParams.governance_proposal_updateAsset_minProposerBalance,
|
NetworkParams.governance_proposal_updateAsset_minProposerBalance,
|
||||||
NetworkParams.spam_protection_proposal_min_tokens,
|
NetworkParams.spam_protection_proposal_min_tokens,
|
||||||
]);
|
]);
|
||||||
const { VEGA_EXPLORER_URL } = useEnvironment();
|
const { VEGA_EXPLORER_URL, VEGA_DOCS_URL } = useEnvironment();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const {
|
const {
|
||||||
register,
|
register,
|
||||||
@@ -132,13 +132,17 @@ export const ProposeUpdateAsset = () => {
|
|||||||
userAction={ProposalUserAction.CREATE}
|
userAction={ProposalUserAction.CREATE}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{DocsLinks && (
|
{VEGA_DOCS_URL && (
|
||||||
<p className="text-sm" data-testid="proposal-docs-link">
|
<p className="text-sm" data-testid="proposal-docs-link">
|
||||||
<span className="mr-1">{t('ProposalTermsText')}</span>
|
<span className="mr-1">{t('ProposalTermsText')}</span>
|
||||||
<ExternalLink
|
<ExternalLink
|
||||||
href={`${DocsLinks.PROPOSALS_GUIDE}${DOCS_LINK}`}
|
href={`${
|
||||||
|
createDocsLinks(VEGA_DOCS_URL).PROPOSALS_GUIDE
|
||||||
|
}${DOCS_LINK}`}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
>{`${DocsLinks.PROPOSALS_GUIDE}${DOCS_LINK}`}</ExternalLink>
|
>{`${
|
||||||
|
createDocsLinks(VEGA_DOCS_URL).PROPOSALS_GUIDE
|
||||||
|
}${DOCS_LINK}`}</ExternalLink>
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|||||||
+10
-6
@@ -7,8 +7,8 @@ import {
|
|||||||
getEnactmentTimestamp,
|
getEnactmentTimestamp,
|
||||||
useProposalSubmit,
|
useProposalSubmit,
|
||||||
} from '@vegaprotocol/proposals';
|
} from '@vegaprotocol/proposals';
|
||||||
import { useEnvironment, DocsLinks } from '@vegaprotocol/environment';
|
import { useEnvironment } from '@vegaprotocol/environment';
|
||||||
import { validateJson } from '@vegaprotocol/utils';
|
import { createDocsLinks, validateJson } from '@vegaprotocol/utils';
|
||||||
import {
|
import {
|
||||||
NetworkParams,
|
NetworkParams,
|
||||||
useNetworkParams,
|
useNetworkParams,
|
||||||
@@ -98,7 +98,7 @@ export const ProposeUpdateMarket = () => {
|
|||||||
const [selectedMarket, setSelectedMarket] = useState<string | undefined>(
|
const [selectedMarket, setSelectedMarket] = useState<string | undefined>(
|
||||||
undefined
|
undefined
|
||||||
);
|
);
|
||||||
const { VEGA_EXPLORER_URL } = useEnvironment();
|
const { VEGA_EXPLORER_URL, VEGA_DOCS_URL } = useEnvironment();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const {
|
const {
|
||||||
register,
|
register,
|
||||||
@@ -181,13 +181,17 @@ export const ProposeUpdateMarket = () => {
|
|||||||
userAction={ProposalUserAction.CREATE}
|
userAction={ProposalUserAction.CREATE}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{DocsLinks && (
|
{VEGA_DOCS_URL && (
|
||||||
<p className="text-sm" data-testid="proposal-docs-link">
|
<p className="text-sm" data-testid="proposal-docs-link">
|
||||||
<span className="mr-1">{t('ProposalTermsText')}</span>
|
<span className="mr-1">{t('ProposalTermsText')}</span>
|
||||||
<ExternalLink
|
<ExternalLink
|
||||||
href={`${DocsLinks.PROPOSALS_GUIDE}${DOCS_LINK}`}
|
href={`${
|
||||||
|
createDocsLinks(VEGA_DOCS_URL).PROPOSALS_GUIDE
|
||||||
|
}${DOCS_LINK}`}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
>{`${DocsLinks.PROPOSALS_GUIDE}${DOCS_LINK}`}</ExternalLink>
|
>{`${
|
||||||
|
createDocsLinks(VEGA_DOCS_URL).PROPOSALS_GUIDE
|
||||||
|
}${DOCS_LINK}`}</ExternalLink>
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|||||||
@@ -54,16 +54,22 @@ export const TrancheItem = ({
|
|||||||
{formatNumber(total, 2)}
|
{formatNumber(total, 2)}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="grid grid-cols-2 my-2">
|
<table className="w-full">
|
||||||
<div>
|
<tbody>
|
||||||
<span>{t('Starts unlocking')}:</span>{' '}
|
<tr>
|
||||||
<span>{format(tranche.tranche_start, DATE_FORMAT_LONG)}</span>
|
<td>{t('Starts unlocking')}</td>
|
||||||
</div>
|
<td className="text-right">
|
||||||
<div className="justify-self-end">
|
{format(tranche.tranche_start, DATE_FORMAT_LONG)}
|
||||||
<span>{t('Fully unlocked')}:</span>{' '}
|
</td>
|
||||||
<span>{format(tranche.tranche_end, DATE_FORMAT_LONG)}</span>
|
</tr>
|
||||||
</div>
|
<tr>
|
||||||
</div>
|
<td>{t('Fully unlocked')}</td>
|
||||||
|
<td className="text-right">
|
||||||
|
{format(tranche.tranche_end, DATE_FORMAT_LONG)}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
<LockedProgress
|
<LockedProgress
|
||||||
locked={locked}
|
locked={locked}
|
||||||
unlocked={unlocked}
|
unlocked={unlocked}
|
||||||
|
|||||||
+2
-4
@@ -25,7 +25,7 @@ const mockData = {
|
|||||||
amount: '0',
|
amount: '0',
|
||||||
percentageOfTotal: '0',
|
percentageOfTotal: '0',
|
||||||
},
|
},
|
||||||
ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES: {
|
ACCOUNT_TYPE_FEES_LIQUIDITY: {
|
||||||
amount: '0',
|
amount: '0',
|
||||||
percentageOfTotal: '0',
|
percentageOfTotal: '0',
|
||||||
},
|
},
|
||||||
@@ -55,9 +55,7 @@ describe('EpochIndividualRewardsTable', () => {
|
|||||||
expect(
|
expect(
|
||||||
getByTestId('ACCOUNT_TYPE_REWARD_MAKER_RECEIVED_FEES')
|
getByTestId('ACCOUNT_TYPE_REWARD_MAKER_RECEIVED_FEES')
|
||||||
).toBeInTheDocument();
|
).toBeInTheDocument();
|
||||||
expect(
|
expect(getByTestId('ACCOUNT_TYPE_FEES_LIQUIDITY')).toBeInTheDocument();
|
||||||
getByTestId('ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES')
|
|
||||||
).toBeInTheDocument();
|
|
||||||
expect(
|
expect(
|
||||||
getByTestId('ACCOUNT_TYPE_REWARD_MARKET_PROPOSERS')
|
getByTestId('ACCOUNT_TYPE_REWARD_MARKET_PROPOSERS')
|
||||||
).toBeInTheDocument();
|
).toBeInTheDocument();
|
||||||
|
|||||||
+11
-11
@@ -24,7 +24,7 @@ describe('generateEpochIndividualRewardsList', () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const reward3: RewardFieldsFragment = {
|
const reward3: RewardFieldsFragment = {
|
||||||
rewardType: AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES,
|
rewardType: AccountType.ACCOUNT_TYPE_FEES_LIQUIDITY,
|
||||||
amount: '200',
|
amount: '200',
|
||||||
percentageOfTotal: '0.2',
|
percentageOfTotal: '0.2',
|
||||||
receivedAt: new Date(),
|
receivedAt: new Date(),
|
||||||
@@ -34,7 +34,7 @@ describe('generateEpochIndividualRewardsList', () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const reward4: RewardFieldsFragment = {
|
const reward4: RewardFieldsFragment = {
|
||||||
rewardType: AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES,
|
rewardType: AccountType.ACCOUNT_TYPE_FEES_LIQUIDITY,
|
||||||
amount: '100',
|
amount: '100',
|
||||||
percentageOfTotal: '0.1',
|
percentageOfTotal: '0.1',
|
||||||
receivedAt: new Date(),
|
receivedAt: new Date(),
|
||||||
@@ -44,7 +44,7 @@ describe('generateEpochIndividualRewardsList', () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const reward5: RewardFieldsFragment = {
|
const reward5: RewardFieldsFragment = {
|
||||||
rewardType: AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES,
|
rewardType: AccountType.ACCOUNT_TYPE_FEES_LIQUIDITY,
|
||||||
amount: '150',
|
amount: '150',
|
||||||
percentageOfTotal: '0.15',
|
percentageOfTotal: '0.15',
|
||||||
receivedAt: new Date(),
|
receivedAt: new Date(),
|
||||||
@@ -105,7 +105,7 @@ describe('generateEpochIndividualRewardsList', () => {
|
|||||||
amount: '0',
|
amount: '0',
|
||||||
percentageOfTotal: '0',
|
percentageOfTotal: '0',
|
||||||
},
|
},
|
||||||
[AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES]: {
|
[AccountType.ACCOUNT_TYPE_FEES_LIQUIDITY]: {
|
||||||
amount: '0',
|
amount: '0',
|
||||||
percentageOfTotal: '0',
|
percentageOfTotal: '0',
|
||||||
},
|
},
|
||||||
@@ -172,7 +172,7 @@ describe('generateEpochIndividualRewardsList', () => {
|
|||||||
amount: '0',
|
amount: '0',
|
||||||
percentageOfTotal: '0',
|
percentageOfTotal: '0',
|
||||||
},
|
},
|
||||||
[AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES]: {
|
[AccountType.ACCOUNT_TYPE_FEES_LIQUIDITY]: {
|
||||||
amount: '200',
|
amount: '200',
|
||||||
percentageOfTotal: '0.2',
|
percentageOfTotal: '0.2',
|
||||||
},
|
},
|
||||||
@@ -202,7 +202,7 @@ describe('generateEpochIndividualRewardsList', () => {
|
|||||||
amount: '0',
|
amount: '0',
|
||||||
percentageOfTotal: '0',
|
percentageOfTotal: '0',
|
||||||
},
|
},
|
||||||
[AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES]: {
|
[AccountType.ACCOUNT_TYPE_FEES_LIQUIDITY]: {
|
||||||
amount: '0',
|
amount: '0',
|
||||||
percentageOfTotal: '0',
|
percentageOfTotal: '0',
|
||||||
},
|
},
|
||||||
@@ -237,7 +237,7 @@ describe('generateEpochIndividualRewardsList', () => {
|
|||||||
amount: '0',
|
amount: '0',
|
||||||
percentageOfTotal: '0',
|
percentageOfTotal: '0',
|
||||||
},
|
},
|
||||||
[AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES]: {
|
[AccountType.ACCOUNT_TYPE_FEES_LIQUIDITY]: {
|
||||||
amount: '100',
|
amount: '100',
|
||||||
percentageOfTotal: '0.1',
|
percentageOfTotal: '0.1',
|
||||||
},
|
},
|
||||||
@@ -285,7 +285,7 @@ describe('generateEpochIndividualRewardsList', () => {
|
|||||||
amount: '0',
|
amount: '0',
|
||||||
percentageOfTotal: '0',
|
percentageOfTotal: '0',
|
||||||
},
|
},
|
||||||
[AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES]: {
|
[AccountType.ACCOUNT_TYPE_FEES_LIQUIDITY]: {
|
||||||
amount: '150',
|
amount: '150',
|
||||||
percentageOfTotal: '0.15',
|
percentageOfTotal: '0.15',
|
||||||
},
|
},
|
||||||
@@ -320,7 +320,7 @@ describe('generateEpochIndividualRewardsList', () => {
|
|||||||
amount: '0',
|
amount: '0',
|
||||||
percentageOfTotal: '0',
|
percentageOfTotal: '0',
|
||||||
},
|
},
|
||||||
[AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES]: {
|
[AccountType.ACCOUNT_TYPE_FEES_LIQUIDITY]: {
|
||||||
amount: '200',
|
amount: '200',
|
||||||
percentageOfTotal: '0.2',
|
percentageOfTotal: '0.2',
|
||||||
},
|
},
|
||||||
@@ -350,7 +350,7 @@ describe('generateEpochIndividualRewardsList', () => {
|
|||||||
amount: '0',
|
amount: '0',
|
||||||
percentageOfTotal: '0',
|
percentageOfTotal: '0',
|
||||||
},
|
},
|
||||||
[AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES]: {
|
[AccountType.ACCOUNT_TYPE_FEES_LIQUIDITY]: {
|
||||||
amount: '0',
|
amount: '0',
|
||||||
percentageOfTotal: '0',
|
percentageOfTotal: '0',
|
||||||
},
|
},
|
||||||
@@ -395,7 +395,7 @@ describe('generateEpochIndividualRewardsList', () => {
|
|||||||
amount: '0',
|
amount: '0',
|
||||||
percentageOfTotal: '0',
|
percentageOfTotal: '0',
|
||||||
},
|
},
|
||||||
[AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES]: {
|
[AccountType.ACCOUNT_TYPE_FEES_LIQUIDITY]: {
|
||||||
amount: '100',
|
amount: '100',
|
||||||
percentageOfTotal: '0.1',
|
percentageOfTotal: '0.1',
|
||||||
},
|
},
|
||||||
|
|||||||
+1
-18
@@ -79,24 +79,7 @@ export const generateEpochIndividualRewardsList = ({
|
|||||||
epoch?.rewards.push(asset);
|
epoch?.rewards.push(asset);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!asset.rewardTypes[rewardType]) {
|
asset.rewardTypes[rewardType] = { amount, percentageOfTotal };
|
||||||
asset.rewardTypes[rewardType] = { amount, percentageOfTotal };
|
|
||||||
} else {
|
|
||||||
const previousAmount = asset.rewardTypes[rewardType]?.amount;
|
|
||||||
const previousPercentageOfTotal =
|
|
||||||
asset.rewardTypes[rewardType]?.percentageOfTotal;
|
|
||||||
|
|
||||||
asset.rewardTypes[rewardType] = {
|
|
||||||
amount: previousAmount
|
|
||||||
? new BigNumber(previousAmount).plus(amount).toString()
|
|
||||||
: amount,
|
|
||||||
percentageOfTotal: previousPercentageOfTotal
|
|
||||||
? new BigNumber(previousPercentageOfTotal)
|
|
||||||
.plus(percentageOfTotal)
|
|
||||||
.toString()
|
|
||||||
: percentageOfTotal,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// totalAmount is the sum of all rewardTypes amounts
|
// totalAmount is the sum of all rewardTypes amounts
|
||||||
asset.totalAmount = Object.values(asset.rewardTypes).reduce(
|
asset.totalAmount = Object.values(asset.rewardTypes).reduce(
|
||||||
|
|||||||
+2
-4
@@ -29,7 +29,7 @@ const rewardsList = [
|
|||||||
amount: '0',
|
amount: '0',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
rewardType: AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES,
|
rewardType: AccountType.ACCOUNT_TYPE_FEES_LIQUIDITY,
|
||||||
amount: '0',
|
amount: '0',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -78,9 +78,7 @@ describe('EpochTotalRewardsTable', () => {
|
|||||||
expect(
|
expect(
|
||||||
getByTestId('ACCOUNT_TYPE_REWARD_MAKER_RECEIVED_FEES')
|
getByTestId('ACCOUNT_TYPE_REWARD_MAKER_RECEIVED_FEES')
|
||||||
).toBeInTheDocument();
|
).toBeInTheDocument();
|
||||||
expect(
|
expect(getByTestId('ACCOUNT_TYPE_FEES_LIQUIDITY')).toBeInTheDocument();
|
||||||
getByTestId('ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES')
|
|
||||||
).toBeInTheDocument();
|
|
||||||
expect(
|
expect(
|
||||||
getByTestId('ACCOUNT_TYPE_REWARD_MARKET_PROPOSERS')
|
getByTestId('ACCOUNT_TYPE_REWARD_MARKET_PROPOSERS')
|
||||||
).toBeInTheDocument();
|
).toBeInTheDocument();
|
||||||
|
|||||||
+15
-21
@@ -162,10 +162,9 @@ describe('generateEpochAssetRewardsList', () => {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES,
|
AccountType.ACCOUNT_TYPE_FEES_LIQUIDITY,
|
||||||
{
|
{
|
||||||
rewardType:
|
rewardType: AccountType.ACCOUNT_TYPE_FEES_LIQUIDITY,
|
||||||
AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES,
|
|
||||||
amount: '0',
|
amount: '0',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@@ -228,7 +227,7 @@ describe('generateEpochAssetRewardsList', () => {
|
|||||||
node: {
|
node: {
|
||||||
epoch: 2,
|
epoch: 2,
|
||||||
assetId: '1',
|
assetId: '1',
|
||||||
rewardType: AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES,
|
rewardType: AccountType.ACCOUNT_TYPE_FEES_LIQUIDITY,
|
||||||
amount: '5',
|
amount: '5',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -288,10 +287,9 @@ describe('generateEpochAssetRewardsList', () => {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES,
|
AccountType.ACCOUNT_TYPE_FEES_LIQUIDITY,
|
||||||
{
|
{
|
||||||
rewardType:
|
rewardType: AccountType.ACCOUNT_TYPE_FEES_LIQUIDITY,
|
||||||
AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES,
|
|
||||||
amount: '0',
|
amount: '0',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@@ -353,10 +351,9 @@ describe('generateEpochAssetRewardsList', () => {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES,
|
AccountType.ACCOUNT_TYPE_FEES_LIQUIDITY,
|
||||||
{
|
{
|
||||||
rewardType:
|
rewardType: AccountType.ACCOUNT_TYPE_FEES_LIQUIDITY,
|
||||||
AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES,
|
|
||||||
amount: '5',
|
amount: '5',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@@ -419,7 +416,7 @@ describe('generateEpochAssetRewardsList', () => {
|
|||||||
node: {
|
node: {
|
||||||
epoch: 2,
|
epoch: 2,
|
||||||
assetId: '1',
|
assetId: '1',
|
||||||
rewardType: AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES,
|
rewardType: AccountType.ACCOUNT_TYPE_FEES_LIQUIDITY,
|
||||||
amount: '6',
|
amount: '6',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -427,7 +424,7 @@ describe('generateEpochAssetRewardsList', () => {
|
|||||||
node: {
|
node: {
|
||||||
epoch: 2,
|
epoch: 2,
|
||||||
assetId: '1',
|
assetId: '1',
|
||||||
rewardType: AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES,
|
rewardType: AccountType.ACCOUNT_TYPE_FEES_LIQUIDITY,
|
||||||
amount: '27',
|
amount: '27',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -500,10 +497,9 @@ describe('generateEpochAssetRewardsList', () => {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES,
|
AccountType.ACCOUNT_TYPE_FEES_LIQUIDITY,
|
||||||
{
|
{
|
||||||
rewardType:
|
rewardType: AccountType.ACCOUNT_TYPE_FEES_LIQUIDITY,
|
||||||
AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES,
|
|
||||||
amount: '33',
|
amount: '33',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@@ -565,10 +561,9 @@ describe('generateEpochAssetRewardsList', () => {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES,
|
AccountType.ACCOUNT_TYPE_FEES_LIQUIDITY,
|
||||||
{
|
{
|
||||||
rewardType:
|
rewardType: AccountType.ACCOUNT_TYPE_FEES_LIQUIDITY,
|
||||||
AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES,
|
|
||||||
amount: '0',
|
amount: '0',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@@ -642,10 +637,9 @@ describe('generateEpochAssetRewardsList', () => {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES,
|
AccountType.ACCOUNT_TYPE_FEES_LIQUIDITY,
|
||||||
{
|
{
|
||||||
rewardType:
|
rewardType: AccountType.ACCOUNT_TYPE_FEES_LIQUIDITY,
|
||||||
AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES,
|
|
||||||
amount: '0',
|
amount: '0',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import {
|
|||||||
ExternalLink,
|
ExternalLink,
|
||||||
} from '@vegaprotocol/ui-toolkit';
|
} from '@vegaprotocol/ui-toolkit';
|
||||||
import { useVegaWallet } from '@vegaprotocol/wallet';
|
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||||
|
import { createDocsLinks } from '@vegaprotocol/utils';
|
||||||
import {
|
import {
|
||||||
useNetworkParams,
|
useNetworkParams,
|
||||||
NetworkParams,
|
NetworkParams,
|
||||||
@@ -20,7 +21,7 @@ import { EpochCountdown } from '../../../components/epoch-countdown';
|
|||||||
import { Heading, SubHeading } from '../../../components/heading';
|
import { Heading, SubHeading } from '../../../components/heading';
|
||||||
import { EpochIndividualRewards } from '../epoch-individual-rewards/epoch-individual-rewards';
|
import { EpochIndividualRewards } from '../epoch-individual-rewards/epoch-individual-rewards';
|
||||||
import { useRefreshAfterEpoch } from '../../../hooks/use-refresh-after-epoch';
|
import { useRefreshAfterEpoch } from '../../../hooks/use-refresh-after-epoch';
|
||||||
import { DocsLinks } from '@vegaprotocol/environment';
|
import { useEnvironment } from '@vegaprotocol/environment';
|
||||||
import { ConnectToSeeRewards } from '../connect-to-see-rewards';
|
import { ConnectToSeeRewards } from '../connect-to-see-rewards';
|
||||||
import { EpochTotalRewards } from '../epoch-total-rewards/epoch-total-rewards';
|
import { EpochTotalRewards } from '../epoch-total-rewards/epoch-total-rewards';
|
||||||
|
|
||||||
@@ -28,6 +29,7 @@ type RewardsView = 'total' | 'individual';
|
|||||||
|
|
||||||
export const RewardsPage = () => {
|
export const RewardsPage = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
const { VEGA_DOCS_URL } = useEnvironment();
|
||||||
const { pubKey, pubKeys } = useVegaWallet();
|
const { pubKey, pubKeys } = useVegaWallet();
|
||||||
const [toggleRewardsView, setToggleRewardsView] =
|
const [toggleRewardsView, setToggleRewardsView] =
|
||||||
useState<RewardsView>('total');
|
useState<RewardsView>('total');
|
||||||
@@ -66,9 +68,9 @@ export const RewardsPage = () => {
|
|||||||
<Heading title={t('pageTitleRewards')} />
|
<Heading title={t('pageTitleRewards')} />
|
||||||
<p className="mb-12">
|
<p className="mb-12">
|
||||||
{t('rewardsIntro')}{' '}
|
{t('rewardsIntro')}{' '}
|
||||||
{DocsLinks && (
|
{VEGA_DOCS_URL && (
|
||||||
<ExternalLink
|
<ExternalLink
|
||||||
href={DocsLinks.REWARDS_GUIDE}
|
href={createDocsLinks(VEGA_DOCS_URL).REWARDS_GUIDE}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
data-testid="rewards-guide-link"
|
data-testid="rewards-guide-link"
|
||||||
className="text-white"
|
className="text-white"
|
||||||
|
|||||||
+1
-1
@@ -26,7 +26,7 @@ export const RowAccountTypes = {
|
|||||||
columnTitle: 'rewardsColPriceMakingHeader',
|
columnTitle: 'rewardsColPriceMakingHeader',
|
||||||
description: 'rewardsColPriceMakingTooltip',
|
description: 'rewardsColPriceMakingTooltip',
|
||||||
},
|
},
|
||||||
[AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES]: {
|
[AccountType.ACCOUNT_TYPE_FEES_LIQUIDITY]: {
|
||||||
columnTitle: 'rewardsColLiquidityProvisionHeader',
|
columnTitle: 'rewardsColLiquidityProvisionHeader',
|
||||||
description: 'rewardsColLiquidityProvisionTooltip',
|
description: 'rewardsColLiquidityProvisionTooltip',
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -229,7 +229,7 @@ const redirects = [
|
|||||||
const routerConfig = [
|
const routerConfig = [
|
||||||
{
|
{
|
||||||
path: Routes.HOME,
|
path: Routes.HOME,
|
||||||
element: <LazyHome name="Home" />,
|
element: <LazyHome name="Token" />,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: Routes.PROPOSALS,
|
path: Routes.PROPOSALS,
|
||||||
@@ -291,7 +291,7 @@ const routerConfig = [
|
|||||||
// Not lazy as loaded when a user first hits the site
|
// Not lazy as loaded when a user first hits the site
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
element: <Home name="Token" />,
|
element: <Home name="Home" />,
|
||||||
index: true,
|
index: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -325,11 +325,8 @@ const routerConfig = [
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{ path: 'associate', element: <LazyStakingAssociate name="Associate" /> },
|
{ path: 'associate', element: <LazyStakingAssociate /> },
|
||||||
{
|
{ path: 'disassociate', element: <LazyStakingDisassociate /> },
|
||||||
path: 'disassociate',
|
|
||||||
element: <LazyStakingDisassociate name="Disassociate" />,
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,14 +1,12 @@
|
|||||||
import { useEthereumConfig } from '@vegaprotocol/web3';
|
import { useEthereumConfig } from '@vegaprotocol/web3';
|
||||||
import { StakingWalletsContainer } from './components/staking-wallets-container';
|
import { StakingWalletsContainer } from './components/staking-wallets-container/staking-wallets-container';
|
||||||
import { AssociatePage } from './associate-page';
|
import { AssociatePage } from './associate-page';
|
||||||
import { AssociatePageNoVega } from './associate-page-no-vega';
|
import { AssociatePageNoVega } from './associate-page-no-vega';
|
||||||
import { Heading } from '../../../components/heading';
|
import { Heading } from '../../../components/heading';
|
||||||
|
import React from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { useDocumentTitle } from '../../../hooks/use-document-title';
|
|
||||||
import type { RouteChildProps } from '../../index';
|
|
||||||
|
|
||||||
export const AssociateContainer = ({ name }: RouteChildProps) => {
|
export const AssociateContainer = () => {
|
||||||
useDocumentTitle(name);
|
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { config } = useEthereumConfig();
|
const { config } = useEthereumConfig();
|
||||||
|
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ jest.mock('@vegaprotocol/wallet', () => ({
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
const renderComponent = () => {
|
const renderComponent = () => {
|
||||||
return render(<Disassociate name="Disassociate" />);
|
return render(<Disassociate />);
|
||||||
};
|
};
|
||||||
|
|
||||||
describe('Disassociate', () => {
|
describe('Disassociate', () => {
|
||||||
|
|||||||
@@ -3,12 +3,10 @@ import { useWeb3React } from '@web3-react/core';
|
|||||||
import { EthConnectPrompt } from '../../../components/eth-connect-prompt';
|
import { EthConnectPrompt } from '../../../components/eth-connect-prompt';
|
||||||
import { DisassociatePage } from './components/disassociate-page';
|
import { DisassociatePage } from './components/disassociate-page';
|
||||||
import { Heading } from '../../../components/heading';
|
import { Heading } from '../../../components/heading';
|
||||||
|
import React from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { useDocumentTitle } from '../../../hooks/use-document-title';
|
|
||||||
import type { RouteChildProps } from '../../index';
|
|
||||||
|
|
||||||
export const DisassociateContainer = ({ name }: RouteChildProps) => {
|
export const DisassociateContainer = () => {
|
||||||
useDocumentTitle(name);
|
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { account } = useWeb3React();
|
const { account } = useWeb3React();
|
||||||
const { pubKey } = useVegaWallet();
|
const { pubKey } = useVegaWallet();
|
||||||
|
|||||||
@@ -1,12 +1,14 @@
|
|||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { EpochData } from './epoch-data';
|
import { EpochData } from './epoch-data';
|
||||||
import { DocsLinks } from '@vegaprotocol/environment';
|
import { useEnvironment } from '@vegaprotocol/environment';
|
||||||
import { ExternalLink } from '@vegaprotocol/ui-toolkit';
|
import { ExternalLink } from '@vegaprotocol/ui-toolkit';
|
||||||
|
import { createDocsLinks } from '@vegaprotocol/utils';
|
||||||
import { Heading } from '../../../components/heading';
|
import { Heading } from '../../../components/heading';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
export const Staking = () => {
|
export const Staking = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
const { VEGA_DOCS_URL } = useEnvironment();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@@ -14,9 +16,9 @@ export const Staking = () => {
|
|||||||
<section>
|
<section>
|
||||||
<p className="mb-12">
|
<p className="mb-12">
|
||||||
{t('stakingIntro')}{' '}
|
{t('stakingIntro')}{' '}
|
||||||
{DocsLinks && (
|
{VEGA_DOCS_URL && (
|
||||||
<ExternalLink
|
<ExternalLink
|
||||||
href={DocsLinks.STAKING_GUIDE}
|
href={createDocsLinks(VEGA_DOCS_URL).STAKING_GUIDE}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
data-testid="staking-guide-link"
|
data-testid="staking-guide-link"
|
||||||
className="text-white"
|
className="text-white"
|
||||||
|
|||||||
+59
-70
@@ -131,25 +131,20 @@ export const ConsensusValidatorsTable = ({
|
|||||||
|
|
||||||
const gridRef = useRef<AgGridReact | null>(null);
|
const gridRef = useRef<AgGridReact | null>(null);
|
||||||
|
|
||||||
const thirdOfTotalStake = useMemo(
|
|
||||||
() => new BigNumber(totalStake).dividedBy(3),
|
|
||||||
[totalStake]
|
|
||||||
);
|
|
||||||
|
|
||||||
const nodes = useMemo(() => {
|
const nodes = useMemo(() => {
|
||||||
if (!data) return [];
|
if (!data) return [];
|
||||||
let canonisedNodes = data
|
let canonisedNodes = data
|
||||||
.sort((a, b) => {
|
.sort((a, b) => {
|
||||||
const aStakedTotal = new BigNumber(a.stakedTotal);
|
const aVotingPower = new BigNumber(a.rankingScore.votingPower);
|
||||||
const bStakedTotal = new BigNumber(b.stakedTotal);
|
const bVotingPower = new BigNumber(b.rankingScore.votingPower);
|
||||||
return bStakedTotal.minus(aStakedTotal).toNumber();
|
return bVotingPower.minus(aVotingPower).toNumber();
|
||||||
})
|
})
|
||||||
.map((node, index) => {
|
.map((node, index) => {
|
||||||
const stakedTotalRanking = index + 1;
|
const votingPowerRanking = index + 1;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...node,
|
...node,
|
||||||
stakedTotalRanking,
|
votingPowerRanking,
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
.map(
|
.map(
|
||||||
@@ -162,7 +157,7 @@ export const ConsensusValidatorsTable = ({
|
|||||||
stakedTotal,
|
stakedTotal,
|
||||||
rankingScore: { stakeScore, votingPower },
|
rankingScore: { stakeScore, votingPower },
|
||||||
pendingStake,
|
pendingStake,
|
||||||
stakedTotalRanking,
|
votingPowerRanking,
|
||||||
stakedByUser,
|
stakedByUser,
|
||||||
pendingUserStake,
|
pendingUserStake,
|
||||||
userStakeShare,
|
userStakeShare,
|
||||||
@@ -180,7 +175,7 @@ export const ConsensusValidatorsTable = ({
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
id,
|
id,
|
||||||
[ValidatorFields.RANKING_INDEX]: stakedTotalRanking,
|
[ValidatorFields.RANKING_INDEX]: votingPowerRanking,
|
||||||
[ValidatorFields.VALIDATOR]: {
|
[ValidatorFields.VALIDATOR]: {
|
||||||
avatarUrl: logo,
|
avatarUrl: logo,
|
||||||
name,
|
name,
|
||||||
@@ -221,7 +216,7 @@ export const ConsensusValidatorsTable = ({
|
|||||||
: undefined,
|
: undefined,
|
||||||
[ValidatorFields.PENDING_USER_STAKE]: pendingUserStake,
|
[ValidatorFields.PENDING_USER_STAKE]: pendingUserStake,
|
||||||
[ValidatorFields.USER_STAKE_SHARE]: userStakeShare
|
[ValidatorFields.USER_STAKE_SHARE]: userStakeShare
|
||||||
? formatNumberPercentage(new BigNumber(userStakeShare), 2)
|
? formatNumberPercentage(new BigNumber(userStakeShare))
|
||||||
: undefined,
|
: undefined,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -241,12 +236,16 @@ export const ConsensusValidatorsTable = ({
|
|||||||
return canonisedNodes;
|
return canonisedNodes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The point of identifying and hiding the group that could halt the network
|
||||||
|
// is that we assume the top 1/3 of stake is held by considerably less than
|
||||||
|
// 1/3 of the validators and we really want people not to stake any more to
|
||||||
|
// that group, because we want to make it require as many difference
|
||||||
|
// validators to collude as possible to halt the network, so we hide them.
|
||||||
const { topThird, remaining } = canonisedNodes.reduce(
|
const { topThird, remaining } = canonisedNodes.reduce(
|
||||||
(acc, node) => {
|
(acc, node) => {
|
||||||
if (acc.cumulativeScore.isLessThan(thirdOfTotalStake)) {
|
if (acc.cumulativeScore < 100 / 3) {
|
||||||
const prev = acc.cumulativeScore;
|
acc.cumulativeScore += parseFloat(
|
||||||
acc.cumulativeScore = prev.plus(
|
node[ValidatorFields.NORMALISED_VOTING_POWER]
|
||||||
new BigNumber(node[ValidatorFields.STAKE])
|
|
||||||
);
|
);
|
||||||
acc.topThird.push(node);
|
acc.topThird.push(node);
|
||||||
return acc;
|
return acc;
|
||||||
@@ -254,70 +253,61 @@ export const ConsensusValidatorsTable = ({
|
|||||||
acc.remaining.push(node);
|
acc.remaining.push(node);
|
||||||
return acc;
|
return acc;
|
||||||
},
|
},
|
||||||
{ topThird: [], remaining: [], cumulativeScore: new BigNumber(0) } as {
|
{ topThird: [], remaining: [], cumulativeScore: 0 } as {
|
||||||
topThird: CanonisedConsensusNodeProps[];
|
topThird: CanonisedConsensusNodeProps[];
|
||||||
remaining: CanonisedConsensusNodeProps[];
|
remaining: CanonisedConsensusNodeProps[];
|
||||||
cumulativeScore: BigNumber;
|
cumulativeScore: number;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
// We need to combine the top third of validators into a single node, this
|
// We need to combine the top third of validators into a single node, this
|
||||||
// way the combined values can be passed to AG grid so that the combined cell's
|
// way the combined values can be passed to AG grid so that the combined cell's
|
||||||
// values are correct for ordering.
|
// values are correct for ordering.
|
||||||
const combinedTopThird = topThird.reduce(
|
const combinedTopThird = topThird.reduce((acc, node) => {
|
||||||
(acc, node) => {
|
const {
|
||||||
const {
|
[ValidatorFields.STAKE]: stake,
|
||||||
[ValidatorFields.STAKE]: stake,
|
[ValidatorFields.STAKE_SHARE]: stakeShare,
|
||||||
[ValidatorFields.STAKE_SHARE]: stakeShare,
|
[ValidatorFields.PENDING_STAKE]: pendingStake,
|
||||||
[ValidatorFields.PENDING_STAKE]: pendingStake,
|
[ValidatorFields.NORMALISED_VOTING_POWER]: normalisedVotingPower,
|
||||||
[ValidatorFields.NORMALISED_VOTING_POWER]: normalisedVotingPower,
|
[ValidatorFields.TOTAL_PENALTIES]: totalPenalties,
|
||||||
[ValidatorFields.TOTAL_PENALTIES]: totalPenalties,
|
} = node;
|
||||||
} = node;
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
[ValidatorFields.STAKE]: accStake,
|
[ValidatorFields.STAKE]: accStake,
|
||||||
[ValidatorFields.STAKE_SHARE]: accStakeShare,
|
[ValidatorFields.STAKE_SHARE]: accStakeShare,
|
||||||
[ValidatorFields.PENDING_STAKE]: accPendingStake,
|
[ValidatorFields.PENDING_STAKE]: accPendingStake,
|
||||||
[ValidatorFields.NORMALISED_VOTING_POWER]: accNormalisedVotingPower,
|
[ValidatorFields.NORMALISED_VOTING_POWER]: accNormalisedVotingPower,
|
||||||
[ValidatorFields.TOTAL_PENALTIES]: accTotalPenalties,
|
[ValidatorFields.TOTAL_PENALTIES]: accTotalPenalties,
|
||||||
} = acc;
|
} = acc;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...acc,
|
...acc,
|
||||||
[ValidatorFields.STAKE]: new BigNumber(accStake)
|
[ValidatorFields.STAKE]: toBigNum(accStake, decimals)
|
||||||
.plus(new BigNumber(stake))
|
.plus(toBigNum(stake, decimals))
|
||||||
.toString(),
|
.toString(),
|
||||||
[ValidatorFields.STAKE_SHARE]: formatNumberPercentage(
|
[ValidatorFields.STAKE_SHARE]: formatNumberPercentage(
|
||||||
new BigNumber(parseFloat(accStakeShare)).plus(
|
new BigNumber(parseFloat(accStakeShare)).plus(
|
||||||
new BigNumber(parseFloat(stakeShare))
|
new BigNumber(parseFloat(stakeShare))
|
||||||
),
|
|
||||||
2
|
|
||||||
),
|
),
|
||||||
[ValidatorFields.PENDING_STAKE]: toBigNum(accPendingStake, decimals)
|
2
|
||||||
.plus(toBigNum(pendingStake, decimals))
|
),
|
||||||
.toString(),
|
[ValidatorFields.PENDING_STAKE]: toBigNum(accPendingStake, decimals)
|
||||||
[ValidatorFields.NORMALISED_VOTING_POWER]: formatNumberPercentage(
|
.plus(toBigNum(pendingStake, decimals))
|
||||||
new BigNumber(parseFloat(accNormalisedVotingPower)).plus(
|
.toString(),
|
||||||
new BigNumber(parseFloat(normalisedVotingPower))
|
[ValidatorFields.NORMALISED_VOTING_POWER]: formatNumberPercentage(
|
||||||
),
|
new BigNumber(parseFloat(accNormalisedVotingPower)).plus(
|
||||||
2
|
new BigNumber(parseFloat(normalisedVotingPower))
|
||||||
),
|
),
|
||||||
[ValidatorFields.TOTAL_PENALTIES]: formatNumberPercentage(
|
2
|
||||||
new BigNumber(parseFloat(accTotalPenalties)).plus(
|
),
|
||||||
new BigNumber(parseFloat(totalPenalties))
|
[ValidatorFields.TOTAL_PENALTIES]: formatNumberPercentage(
|
||||||
),
|
new BigNumber(parseFloat(accTotalPenalties)).plus(
|
||||||
2
|
new BigNumber(parseFloat(totalPenalties))
|
||||||
),
|
),
|
||||||
};
|
2
|
||||||
},
|
),
|
||||||
{
|
};
|
||||||
[ValidatorFields.STAKE]: '0',
|
});
|
||||||
[ValidatorFields.STAKE_SHARE]: '0',
|
|
||||||
[ValidatorFields.PENDING_STAKE]: '0',
|
|
||||||
[ValidatorFields.NORMALISED_VOTING_POWER]: '0',
|
|
||||||
[ValidatorFields.TOTAL_PENALTIES]: '0',
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
@@ -332,7 +322,6 @@ export const ConsensusValidatorsTable = ({
|
|||||||
decimals,
|
decimals,
|
||||||
hideTopThird,
|
hideTopThird,
|
||||||
previousEpochData,
|
previousEpochData,
|
||||||
thirdOfTotalStake,
|
|
||||||
totalStake,
|
totalStake,
|
||||||
validatorsView,
|
validatorsView,
|
||||||
]);
|
]);
|
||||||
@@ -363,7 +352,6 @@ export const ConsensusValidatorsTable = ({
|
|||||||
headerTooltip: t('StakeDescription').toString(),
|
headerTooltip: t('StakeDescription').toString(),
|
||||||
cellRenderer: TotalStakeRenderer,
|
cellRenderer: TotalStakeRenderer,
|
||||||
width: 120,
|
width: 120,
|
||||||
sort: 'desc',
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
field: ValidatorFields.PENDING_STAKE,
|
field: ValidatorFields.PENDING_STAKE,
|
||||||
@@ -385,6 +373,7 @@ export const ConsensusValidatorsTable = ({
|
|||||||
headerTooltip: t('NormalisedVotingPowerDescription').toString(),
|
headerTooltip: t('NormalisedVotingPowerDescription').toString(),
|
||||||
cellRenderer: VotingPowerRenderer,
|
cellRenderer: VotingPowerRenderer,
|
||||||
width: 200,
|
width: 200,
|
||||||
|
sort: 'desc',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
field: ValidatorFields.TOTAL_PENALTIES,
|
field: ValidatorFields.TOTAL_PENALTIES,
|
||||||
|
|||||||
+9
-9
@@ -57,16 +57,16 @@ export const StandbyPendingValidatorsTable = ({
|
|||||||
|
|
||||||
return data
|
return data
|
||||||
.sort((a, b) => {
|
.sort((a, b) => {
|
||||||
const aStakedTotal = new BigNumber(a.stakedTotal);
|
const aVotingPower = new BigNumber(a.rankingScore.votingPower);
|
||||||
const bStakedTotal = new BigNumber(b.stakedTotal);
|
const bVotingPower = new BigNumber(b.rankingScore.votingPower);
|
||||||
return bStakedTotal.minus(aStakedTotal).toNumber();
|
return bVotingPower.minus(aVotingPower).toNumber();
|
||||||
})
|
})
|
||||||
.map((node, index) => {
|
.map((node, index) => {
|
||||||
const stakedTotalRanking = index + 1;
|
const votingPowerRanking = index + 1;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...node,
|
...node,
|
||||||
stakedTotalRanking,
|
votingPowerRanking,
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
.map(
|
.map(
|
||||||
@@ -79,7 +79,7 @@ export const StandbyPendingValidatorsTable = ({
|
|||||||
stakedTotal,
|
stakedTotal,
|
||||||
rankingScore: { stakeScore },
|
rankingScore: { stakeScore },
|
||||||
pendingStake,
|
pendingStake,
|
||||||
stakedTotalRanking,
|
votingPowerRanking,
|
||||||
stakedByUser,
|
stakedByUser,
|
||||||
pendingUserStake,
|
pendingUserStake,
|
||||||
userStakeShare,
|
userStakeShare,
|
||||||
@@ -125,7 +125,7 @@ export const StandbyPendingValidatorsTable = ({
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
id,
|
id,
|
||||||
[ValidatorFields.RANKING_INDEX]: stakedTotalRanking,
|
[ValidatorFields.RANKING_INDEX]: votingPowerRanking,
|
||||||
[ValidatorFields.VALIDATOR]: {
|
[ValidatorFields.VALIDATOR]: {
|
||||||
avatarUrl,
|
avatarUrl,
|
||||||
name,
|
name,
|
||||||
@@ -166,7 +166,7 @@ export const StandbyPendingValidatorsTable = ({
|
|||||||
: undefined,
|
: undefined,
|
||||||
[ValidatorFields.PENDING_USER_STAKE]: pendingUserStake,
|
[ValidatorFields.PENDING_USER_STAKE]: pendingUserStake,
|
||||||
[ValidatorFields.USER_STAKE_SHARE]: userStakeShare
|
[ValidatorFields.USER_STAKE_SHARE]: userStakeShare
|
||||||
? formatNumberPercentage(new BigNumber(userStakeShare), 2)
|
? formatNumberPercentage(new BigNumber(userStakeShare))
|
||||||
: undefined,
|
: undefined,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -210,7 +210,6 @@ export const StandbyPendingValidatorsTable = ({
|
|||||||
headerTooltip: t('StakeDescription').toString(),
|
headerTooltip: t('StakeDescription').toString(),
|
||||||
cellRenderer: TotalStakeRenderer,
|
cellRenderer: TotalStakeRenderer,
|
||||||
width: 120,
|
width: 120,
|
||||||
sort: 'desc',
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
field: ValidatorFields.PENDING_STAKE,
|
field: ValidatorFields.PENDING_STAKE,
|
||||||
@@ -234,6 +233,7 @@ export const StandbyPendingValidatorsTable = ({
|
|||||||
}),
|
}),
|
||||||
cellRenderer: StakeNeededForPromotionRenderer,
|
cellRenderer: StakeNeededForPromotionRenderer,
|
||||||
width: 210,
|
width: 210,
|
||||||
|
sort: 'asc',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
field: ValidatorFields.TOTAL_PENALTIES,
|
field: ValidatorFields.TOTAL_PENALTIES,
|
||||||
|
|||||||
@@ -2,8 +2,12 @@ import { useMemo, useState } from 'react';
|
|||||||
import { Trans, useTranslation } from 'react-i18next';
|
import { Trans, useTranslation } from 'react-i18next';
|
||||||
import BigNumber from 'bignumber.js';
|
import BigNumber from 'bignumber.js';
|
||||||
import * as Schema from '@vegaprotocol/types';
|
import * as Schema from '@vegaprotocol/types';
|
||||||
import { removePaginationWrapper, toBigNum } from '@vegaprotocol/utils';
|
import {
|
||||||
import { DocsLinks } from '@vegaprotocol/environment';
|
createDocsLinks,
|
||||||
|
removePaginationWrapper,
|
||||||
|
toBigNum,
|
||||||
|
} from '@vegaprotocol/utils';
|
||||||
|
import { useEnvironment } from '@vegaprotocol/environment';
|
||||||
import { Link as UTLink, Toggle } from '@vegaprotocol/ui-toolkit';
|
import { Link as UTLink, Toggle } from '@vegaprotocol/ui-toolkit';
|
||||||
|
|
||||||
import { formatNumber } from '../../../../lib/format-number';
|
import { formatNumber } from '../../../../lib/format-number';
|
||||||
@@ -38,6 +42,7 @@ export const ValidatorTables = ({
|
|||||||
previousEpochData,
|
previousEpochData,
|
||||||
}: ValidatorsTableProps) => {
|
}: ValidatorsTableProps) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
const { VEGA_DOCS_URL } = useEnvironment();
|
||||||
const {
|
const {
|
||||||
appState: { decimals },
|
appState: { decimals },
|
||||||
} = useAppState();
|
} = useAppState();
|
||||||
@@ -208,12 +213,12 @@ export const ValidatorTables = ({
|
|||||||
<>
|
<>
|
||||||
<SubHeading title={t('status-pending')} />
|
<SubHeading title={t('status-pending')} />
|
||||||
<p>
|
<p>
|
||||||
{DocsLinks && (
|
{VEGA_DOCS_URL && (
|
||||||
<>
|
<>
|
||||||
<span>{t('pendingDescription1')} </span>
|
<span>{t('pendingDescription1')} </span>
|
||||||
<span>
|
<span>
|
||||||
<UTLink
|
<UTLink
|
||||||
href={DocsLinks.STAKING_GUIDE}
|
href={createDocsLinks(VEGA_DOCS_URL).STAKING_GUIDE}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
data-testid="validator-forum-link"
|
data-testid="validator-forum-link"
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ import { usePreviousEpochQuery } from '../__generated__/PreviousEpoch';
|
|||||||
import type { ReactElement } from 'react';
|
import type { ReactElement } from 'react';
|
||||||
import type { StakingQuery } from '../__generated__/Staking';
|
import type { StakingQuery } from '../__generated__/Staking';
|
||||||
import type { PreviousEpochQuery } from '../__generated__/PreviousEpoch';
|
import type { PreviousEpochQuery } from '../__generated__/PreviousEpoch';
|
||||||
import { isPartyNotFoundError } from '../../../lib/party';
|
|
||||||
|
|
||||||
// TODO should only request a single node. When migrating from deprecated APIs we should address this.
|
// TODO should only request a single node. When migrating from deprecated APIs we should address this.
|
||||||
|
|
||||||
@@ -29,7 +28,6 @@ export const NodeContainer = ({
|
|||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { pubKey } = useVegaWallet();
|
const { pubKey } = useVegaWallet();
|
||||||
const { delegationsPagination } = ENV;
|
const { delegationsPagination } = ENV;
|
||||||
|
|
||||||
const { data, loading, error, refetch } = useStakingQuery({
|
const { data, loading, error, refetch } = useStakingQuery({
|
||||||
variables: {
|
variables: {
|
||||||
partyId: pubKey || '',
|
partyId: pubKey || '',
|
||||||
@@ -39,9 +37,7 @@ export const NodeContainer = ({
|
|||||||
}
|
}
|
||||||
: undefined,
|
: undefined,
|
||||||
},
|
},
|
||||||
errorPolicy: 'all',
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const { data: previousEpochData } = usePreviousEpochQuery({
|
const { data: previousEpochData } = usePreviousEpochQuery({
|
||||||
variables: {
|
variables: {
|
||||||
epochId: (Number(data?.epoch.id) - 1).toString(),
|
epochId: (Number(data?.epoch.id) - 1).toString(),
|
||||||
@@ -51,7 +47,7 @@ export const NodeContainer = ({
|
|||||||
|
|
||||||
useRefreshAfterEpoch(data?.epoch.timestamps.expiry, refetch);
|
useRefreshAfterEpoch(data?.epoch.timestamps.expiry, refetch);
|
||||||
|
|
||||||
if (error && !isPartyNotFoundError(error)) {
|
if (error) {
|
||||||
return (
|
return (
|
||||||
<Callout intent={Intent.Danger} title={t('Something went wrong')}>
|
<Callout intent={Intent.Danger} title={t('Something went wrong')}>
|
||||||
<pre>
|
<pre>
|
||||||
|
|||||||
@@ -1,11 +1,7 @@
|
|||||||
import React, { useMemo } from 'react';
|
import React, { useMemo } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import {
|
import { useEnvironment } from '@vegaprotocol/environment';
|
||||||
useEnvironment,
|
import { createDocsLinks, ExternalLinks, toBigNum } from '@vegaprotocol/utils';
|
||||||
DocsLinks,
|
|
||||||
ExternalLinks,
|
|
||||||
} from '@vegaprotocol/environment';
|
|
||||||
import { toBigNum } from '@vegaprotocol/utils';
|
|
||||||
import * as Schema from '@vegaprotocol/types';
|
import * as Schema from '@vegaprotocol/types';
|
||||||
import {
|
import {
|
||||||
Link as UTLink,
|
Link as UTLink,
|
||||||
@@ -68,7 +64,7 @@ export const ValidatorTable = ({
|
|||||||
stakedTotal,
|
stakedTotal,
|
||||||
previousEpochData,
|
previousEpochData,
|
||||||
}: ValidatorTableProps) => {
|
}: ValidatorTableProps) => {
|
||||||
const { ETHERSCAN_URL } = useEnvironment();
|
const { ETHERSCAN_URL, VEGA_DOCS_URL } = useEnvironment();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const {
|
const {
|
||||||
appState: { decimals },
|
appState: { decimals },
|
||||||
@@ -94,9 +90,9 @@ export const ValidatorTable = ({
|
|||||||
<>
|
<>
|
||||||
<p className="mb-12">
|
<p className="mb-12">
|
||||||
{t('validatorFormIntro')}{' '}
|
{t('validatorFormIntro')}{' '}
|
||||||
{DocsLinks && (
|
{VEGA_DOCS_URL && (
|
||||||
<ExternalLink
|
<ExternalLink
|
||||||
href={DocsLinks.STAKING_GUIDE}
|
href={createDocsLinks(VEGA_DOCS_URL).STAKING_GUIDE}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
data-testid="validator-table-staking-guide-link"
|
data-testid="validator-table-staking-guide-link"
|
||||||
className="text-white"
|
className="text-white"
|
||||||
|
|||||||
@@ -60,14 +60,9 @@ export const getOverstakingPenalty = (
|
|||||||
}
|
}
|
||||||
|
|
||||||
return formatNumberPercentage(
|
return formatNumberPercentage(
|
||||||
BigNumber.max(
|
new BigNumber(1)
|
||||||
new BigNumber(1)
|
.minus(new BigNumber(validatorScore).dividedBy(new BigNumber(stakeScore)))
|
||||||
.minus(
|
.times(100),
|
||||||
new BigNumber(validatorScore).dividedBy(new BigNumber(stakeScore))
|
|
||||||
)
|
|
||||||
.times(100),
|
|
||||||
new BigNumber(0)
|
|
||||||
),
|
|
||||||
2
|
2
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -3,8 +3,7 @@ import { Trans, useTranslation } from 'react-i18next';
|
|||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
|
|
||||||
import { Heading, SubHeading } from '../../components/heading';
|
import { Heading, SubHeading } from '../../components/heading';
|
||||||
import { ExternalLinks } from '@vegaprotocol/environment';
|
import { ExternalLinks, toBigNum } from '@vegaprotocol/utils';
|
||||||
import { toBigNum } from '@vegaprotocol/utils';
|
|
||||||
import { useAppState } from '../../contexts/app-state/app-state-context';
|
import { useAppState } from '../../contexts/app-state/app-state-context';
|
||||||
import { useDocumentTitle } from '../../hooks/use-document-title';
|
import { useDocumentTitle } from '../../hooks/use-document-title';
|
||||||
import type { RouteChildProps } from '..';
|
import type { RouteChildProps } from '..';
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user