Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
719441a86c | ||
|
|
af78f56d13 | ||
|
|
d08bd4fce1 | ||
|
|
a0b648d9b6 | ||
|
|
0d0ca097ed | ||
|
|
a4149cc55e | ||
|
|
4161a74eaf | ||
|
|
5008e57a56 | ||
|
|
89031bf826 | ||
|
|
61501d4d14 | ||
|
|
f8f3ce7df9 | ||
|
|
f7aab5a78a | ||
|
|
2904975fd3 | ||
|
|
4f55c946f0 | ||
|
|
de637fee13 | ||
|
|
69c6db8ec2 | ||
|
|
8dc465c1d1 | ||
|
|
0be934edca | ||
|
|
653688c1dd | ||
|
|
34400be18a | ||
|
|
d4a0a4a42c | ||
|
|
fabf643ce7 | ||
|
|
45e210092c | ||
|
|
1dc3b3fd05 | ||
|
|
0a80e3e39d | ||
|
|
7b8d9dc94b | ||
|
|
c0320c2468 | ||
|
|
11173d6614 | ||
|
|
e3324331e2 | ||
|
|
0a6e3cb6f5 | ||
|
|
68ede90609 | ||
|
|
09778a5d3f | ||
|
|
59993e3ea6 | ||
|
|
54161a6dba | ||
|
|
26f524ea07 | ||
|
|
db13e6f21d | ||
|
|
4a679813ba | ||
|
|
a4279d5b5e | ||
|
|
1a274a67c3 | ||
|
|
1e9ff31fd7 | ||
|
|
aa2b755b93 | ||
|
|
4a7296312a | ||
|
|
1418a2259f | ||
|
|
7e5ef0157a | ||
|
|
0cf764f11e | ||
|
|
1c626aeeb2 | ||
|
|
c4cec47b0b | ||
|
|
1a67c41bb4 | ||
|
|
fc66240390 | ||
|
|
fc0da98683 | ||
|
|
4d0dafb113 | ||
|
|
eda431be9e | ||
|
|
4ff1749e54 | ||
|
|
e7a108fd7a | ||
|
|
5d3f2ea7d8 | ||
|
|
1ab27574ca | ||
|
|
0ea3c43e78 | ||
|
|
9e183568b0 | ||
|
|
69fd5b26b6 | ||
|
|
2d5b926609 | ||
|
|
98a2ba79a8 | ||
|
|
0e6e986937 | ||
|
|
15d6ea0c05 | ||
|
|
0358ce6293 | ||
|
|
0791be4636 | ||
|
|
07442848ee | ||
|
|
d5f0bd419e | ||
|
|
2c7934a8c7 | ||
|
|
fae61c28ca | ||
|
|
fcd4541625 | ||
|
|
8e0a92fdec | ||
|
|
6e7109ed87 | ||
|
|
f2c698ce06 | ||
|
|
ad9a3a3400 | ||
|
|
efeac49d58 | ||
|
|
2d430710ab | ||
|
|
bca1a98985 | ||
|
|
8a080d3279 | ||
|
|
f121836b4e | ||
|
|
b641c82ad8 | ||
|
|
bf73559c30 | ||
|
|
653cec2592 | ||
|
|
579c884a5a | ||
|
|
b2279c7e47 | ||
|
|
a8c17b6807 | ||
|
|
b4c7dc6f59 | ||
|
|
d44392bebf | ||
|
|
bd679957e2 | ||
|
|
e30d48555e | ||
|
|
b4b2416780 |
@@ -0,0 +1,74 @@
|
||||
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,5 +33,4 @@ jobs:
|
||||
config: baseUrl=${{ github.event.inputs.url }}
|
||||
env: grepTags=@live
|
||||
env:
|
||||
CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }}
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
@@ -20,7 +20,7 @@ jobs:
|
||||
project: ${{ fromJSON(inputs.projects) }}
|
||||
name: ${{ matrix.project }}
|
||||
runs-on: self-hosted-runner
|
||||
timeout-minutes: 40
|
||||
timeout-minutes: 60
|
||||
steps:
|
||||
# Checks if skip cache was requested
|
||||
- name: Set skip-nx-cache flag
|
||||
@@ -66,7 +66,7 @@ jobs:
|
||||
######
|
||||
|
||||
- name: Run Cypress tests
|
||||
run: yarn nx run ${{ matrix.project }}:e2e ${{ env.SKIP_CACHE }} --record --key ${{ secrets.CYPRESS_RECORD_KEY }} --browser chrome --env.grepTags="${{ inputs.tags }}"
|
||||
run: yarn nx run ${{ matrix.project }}:e2e ${{ env.SKIP_CACHE }} --browser chrome --env.grepTags="${{ inputs.tags }}"
|
||||
working-directory: frontend-monorepo
|
||||
env:
|
||||
CYPRESS_SLACK_WEBHOOK: ${{ secrets.CYPRESS_SLACK_WEBHOOK }}
|
||||
@@ -98,4 +98,4 @@ jobs:
|
||||
if: ${{ failure() }}
|
||||
with:
|
||||
name: test-report-${{ matrix.project }}
|
||||
path: frontend-monorepo/apps/trading-e2e/cypress/reports
|
||||
path: frontend-monorepo/apps/${{ matrix.project }}/cypress/reports
|
||||
|
||||
@@ -148,6 +148,7 @@ jobs:
|
||||
ENV_NAME=${{ env.ENV_NAME }}
|
||||
tags: |
|
||||
vegaprotocol/${{ matrix.app }}:${{ github.ref_name }}
|
||||
vegaprotocol/${{ matrix.app }}:mainnet
|
||||
|
||||
# bucket creation in github.com/vegaprotocol/terraform//frontend
|
||||
- name: Publish dist to s3
|
||||
@@ -169,8 +170,51 @@ jobs:
|
||||
labels: ${{ matrix.app }}-preview
|
||||
number: ${{ github.event.number }}
|
||||
|
||||
- name: Add ipfs hash to release
|
||||
uses: softprops/action-gh-release@v1
|
||||
- name: Trigger fleek deployment
|
||||
if: ${{ matrix.app == 'trading' && github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v') }}
|
||||
with:
|
||||
files: ${{ matrix.app }}-ipfs-hash
|
||||
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
|
||||
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}
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
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
|
||||
|
||||
#cypress
|
||||
/apps/trading-e2e/cypress/reports/
|
||||
/apps/**/cypress/reports/
|
||||
|
||||
@@ -11,7 +11,7 @@ recalculate-ipfs:
|
||||
echo "ipfs hash inside the image"
|
||||
docker run --rm ${TAG} cat /ipfs-hash
|
||||
echo "recalculating ipfs hash"
|
||||
docker run --rm ${TAG} ipfs add -rw /usr/share/nginx/html
|
||||
docker run --rm ${TAG} ipfs add -r /usr/share/nginx/html
|
||||
|
||||
.PHONY: eject-ipfs-hash
|
||||
unpack:
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
const { defineConfig } = require('cypress');
|
||||
|
||||
module.exports = defineConfig({
|
||||
projectId: 'et4snf',
|
||||
reporter: '../../node_modules/cypress-mochawesome-reporter',
|
||||
|
||||
e2e: {
|
||||
setupNodeEvents(on, config) {
|
||||
require('cypress-mochawesome-reporter/plugin')(on);
|
||||
require('@cypress/grep/src/plugin')(config);
|
||||
return config;
|
||||
},
|
||||
|
||||
@@ -15,5 +15,6 @@
|
||||
|
||||
import '@vegaprotocol/cypress';
|
||||
import './common.functions.js';
|
||||
import 'cypress-mochawesome-reporter/register';
|
||||
import registerCypressGrep from '@cypress/grep';
|
||||
registerCypressGrep();
|
||||
|
||||
@@ -3,6 +3,7 @@ import { Header } from './header';
|
||||
import { MemoryRouter } from 'react-router-dom';
|
||||
|
||||
jest.mock('@vegaprotocol/environment', () => ({
|
||||
...jest.requireActual('@vegaprotocol/environment'),
|
||||
NetworkSwitcher: () => (
|
||||
<div data-testid="network-switcher">NetworkSwitcher</div>
|
||||
),
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import type { MarketInfoWithData } from '@vegaprotocol/market-info';
|
||||
import { LiquidityInfoPanel } from '@vegaprotocol/market-info';
|
||||
import { LiquidityMonitoringParametersInfoPanel } from '@vegaprotocol/market-info';
|
||||
import type { MarketInfoWithData } from '@vegaprotocol/markets';
|
||||
import {
|
||||
LiquidityInfoPanel,
|
||||
LiquidityMonitoringParametersInfoPanel,
|
||||
InstrumentInfoPanel,
|
||||
KeyDetailsInfoPanel,
|
||||
LiquidityPriceRangeInfoPanel,
|
||||
@@ -12,8 +12,8 @@ import {
|
||||
RiskModelInfoPanel,
|
||||
RiskParametersInfoPanel,
|
||||
SettlementAssetInfoPanel,
|
||||
} from '@vegaprotocol/market-info';
|
||||
import { MarketInfoTable } from '@vegaprotocol/market-info';
|
||||
} from '@vegaprotocol/markets';
|
||||
import { MarketInfoTable } from '@vegaprotocol/markets';
|
||||
import type { DataSourceDefinition } from '@vegaprotocol/types';
|
||||
import isEqual from 'lodash/isEqual';
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { MarketFieldsFragment } from '@vegaprotocol/market-list';
|
||||
import type { MarketFieldsFragment } from '@vegaprotocol/markets';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { ButtonLink } from '@vegaprotocol/ui-toolkit';
|
||||
import type { AgGridReact } from 'ag-grid-react';
|
||||
|
||||
+8
-2
@@ -1,3 +1,5 @@
|
||||
import { Icon } from '@vegaprotocol/ui-toolkit';
|
||||
|
||||
// https://github.com/vegaprotocol/vega/blob/develop/core/blockchain/response.go
|
||||
export const ErrorCodes = new Map([
|
||||
[51, 'Transaction failed validation'],
|
||||
@@ -28,7 +30,11 @@ export const ChainResponseCode = ({
|
||||
}: ChainResponseCodeProps) => {
|
||||
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';
|
||||
|
||||
// Hack for batches with many errors - see https://github.com/vegaprotocol/vega/issues/7245
|
||||
@@ -36,7 +42,7 @@ export const ChainResponseCode = ({
|
||||
error && error.length > 100 ? error.replace(/,/g, ',\r\n') : error;
|
||||
|
||||
return (
|
||||
<div title={`Response code: ${code} - ${label}`} className="inline-block">
|
||||
<div title={`Response code: ${code} - ${label}`} className=" inline-block">
|
||||
<span
|
||||
className="mr-2"
|
||||
aria-label={isSuccess ? 'Success' : 'Warning'}
|
||||
|
||||
@@ -4,6 +4,7 @@ import type { TendermintBlocksResponse } from '../../../routes/blocks/tendermint
|
||||
import { TxDetailsShared } from './shared/tx-details-shared';
|
||||
import { TableCell, TableRow, TableWithTbody } from '../../table';
|
||||
import ProposalLink from '../../links/proposal-link/proposal-link';
|
||||
import { VoteIcon } from '../../vote-icon/vote-icon';
|
||||
|
||||
interface TxProposalVoteProps {
|
||||
txData: BlockExplorerTransactionResult | undefined;
|
||||
@@ -30,27 +31,22 @@ export const TxProposalVote = ({
|
||||
return <>{t('Awaiting Block Explorer transaction details')}</>;
|
||||
}
|
||||
|
||||
const vote = txData.command.voteSubmission.value ? '👍' : '👎';
|
||||
const vote = txData.command.voteSubmission.value === 'VALUE_YES';
|
||||
|
||||
return (
|
||||
<TableWithTbody className="mb-8" allowWrap={true}>
|
||||
<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">
|
||||
<TableCell>{t('Proposal details')}</TableCell>
|
||||
<TableCell>
|
||||
<ProposalLink id={txData.command.voteSubmission.proposalId} />
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
<TableRow modifier="bordered">
|
||||
<TableCell>{t('Proposal')}</TableCell>
|
||||
<TableCell>{txData.command.voteSubmission.proposalId}</TableCell>
|
||||
</TableRow>
|
||||
<TableRow modifier="bordered">
|
||||
<TableCell>{t('Vote')}</TableCell>
|
||||
<TableCell>{vote}</TableCell>
|
||||
<TableCell>
|
||||
<VoteIcon vote={vote} />
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
</TableWithTbody>
|
||||
);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import type { components } from '../../../types/explorer';
|
||||
import { VoteIcon } from '../vote-icon/vote-icon';
|
||||
|
||||
interface TxOrderTypeProps {
|
||||
orderType: string;
|
||||
@@ -137,12 +138,15 @@ export const TxOrderType = ({ orderType, command }: TxOrderTypeProps) => {
|
||||
let type = displayString[orderType] || orderType;
|
||||
|
||||
let colours =
|
||||
'text-white dark:text-white bg-vega-dark-150 dark:bg-vega-dark-150';
|
||||
'text-white dark:text-white bg-vega-dark-150 dark:bg-vega-dark-250';
|
||||
|
||||
// This will get unwieldy and should probably produce a different colour of tag
|
||||
if (type === 'Chain Event' && !!command?.chainEvent) {
|
||||
type = getLabelForChainEvent(command.chainEvent);
|
||||
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') {
|
||||
if (command && !!command.proposalSubmission) {
|
||||
type = getLabelForProposal(command.proposalSubmission);
|
||||
@@ -150,6 +154,16 @@ export const TxOrderType = ({ orderType, command }: TxOrderTypeProps) => {
|
||||
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') {
|
||||
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('tx-type')).toHaveTextContent('testType');
|
||||
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 (
|
||||
<div
|
||||
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 py-2"
|
||||
className="flex items-center h-full border-t border-neutral-600 dark:border-neutral-800 txs-infinite-list-item grid grid-cols-10"
|
||||
>
|
||||
<div
|
||||
className="text-sm col-span-10 md:col-span-3 leading-none"
|
||||
@@ -83,7 +83,7 @@ export const TxsInfiniteListItem = ({
|
||||
data-testid="tx-success"
|
||||
>
|
||||
<span className="md:hidden uppercase text-vega-dark-300">
|
||||
Success:
|
||||
Success
|
||||
</span>
|
||||
{isNumber(code) ? (
|
||||
<ChainResponseCode code={code} hideLabel={true} />
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
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');
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,40 @@
|
||||
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 compact from 'lodash/compact';
|
||||
import { JsonViewerDialog } from '../../components/dialogs/json-viewer-dialog';
|
||||
import { marketInfoWithDataProvider } from '@vegaprotocol/market-info';
|
||||
import { marketInfoWithDataProvider } from '@vegaprotocol/markets';
|
||||
import { PageTitle } from '../../components/page-helpers/page-title';
|
||||
|
||||
export const MarketPage = () => {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useScrollToLocation } from '../../hooks/scroll-to-location';
|
||||
import { useDocumentTitle } from '../../hooks/use-document-title';
|
||||
import { marketsProvider } from '@vegaprotocol/market-list';
|
||||
import { marketsProvider } from '@vegaprotocol/markets';
|
||||
import { RouteTitle } from '../../components/route-title';
|
||||
import { AsyncRenderer } from '@vegaprotocol/ui-toolkit';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
|
||||
@@ -32,9 +32,9 @@
|
||||
line-height: calc(min(var(--ag-line-height, 26px), 26px) - 4px);
|
||||
}
|
||||
|
||||
.vega-ag-grid .ag-row {
|
||||
border-width: 1px 0;
|
||||
border-bottom: 1px solid transparent;
|
||||
.vega-ag-grid .ag-row,
|
||||
.vega-ag-grid .ag-cell {
|
||||
border-width: 0;
|
||||
}
|
||||
|
||||
/* Light variables */
|
||||
@@ -46,7 +46,6 @@
|
||||
--ag-header-column-separator-color: theme(colors.neutral[300]);
|
||||
--ag-row-border-color: theme(colors.white);
|
||||
--ag-row-hover-color: theme(colors.neutral[100]);
|
||||
--ag-font-size: 12px;
|
||||
}
|
||||
|
||||
/* Dark variables */
|
||||
@@ -58,5 +57,8 @@
|
||||
--ag-header-column-separator-color: theme(colors.neutral[600]);
|
||||
--ag-row-border-color: theme(colors.black);
|
||||
--ag-row-hover-color: theme(colors.neutral[800]);
|
||||
--ag-font-size: 12px;
|
||||
}
|
||||
|
||||
.voteicon svg {
|
||||
vertical-align: baseline;
|
||||
}
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
const { defineConfig } = require('cypress');
|
||||
|
||||
module.exports = defineConfig({
|
||||
projectId: 'et4snf',
|
||||
reporter: '../../node_modules/cypress-mochawesome-reporter',
|
||||
|
||||
e2e: {
|
||||
setupNodeEvents(on, config) {
|
||||
require('cypress-mochawesome-reporter/plugin')(on);
|
||||
require('@cypress/grep/src/plugin')(config);
|
||||
return config;
|
||||
},
|
||||
|
||||
@@ -57,7 +57,6 @@ context(
|
||||
// 2001-STKE-002, 2001-STKE-032
|
||||
before('visit staking tab and connect vega wallet', function () {
|
||||
cy.visit('/');
|
||||
cy.validatorsSelfDelegate();
|
||||
ethereumWalletConnect();
|
||||
// this is a workaround for #2422 which can be removed once issue is resolved
|
||||
cy.associateTokensToVegaWallet('4');
|
||||
|
||||
@@ -109,7 +109,7 @@ context(
|
||||
);
|
||||
cy.getByTestId('protocol-upgrade-proposal-status').should(
|
||||
'have.text',
|
||||
'Approved '
|
||||
'Approved by validators '
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -136,7 +136,7 @@ context(
|
||||
);
|
||||
cy.getByTestId('protocol-upgrade-state').should(
|
||||
'have.text',
|
||||
'Approved'
|
||||
'Approved by validators'
|
||||
);
|
||||
cy.getByTestId('protocol-upgrade-release-tag').should(
|
||||
'have.text',
|
||||
|
||||
@@ -63,7 +63,6 @@ context(
|
||||
});
|
||||
|
||||
it('should have option to go to last and newest page', function () {
|
||||
waitForBeginningOfEpoch();
|
||||
cy.getByTestId('goto-last-page').click();
|
||||
cy.getByTestId('epoch-total-rewards-table')
|
||||
.last()
|
||||
|
||||
@@ -31,9 +31,6 @@ const totalPenaltyToolTip = '[data-testid="total-penalty-tooltip"]';
|
||||
const epochCountDown = '[data-testid="epoch-countdown"]';
|
||||
const stakeNumberRegex = /^\d{1,3}(,\d{3})*(\.\d+)?$/;
|
||||
|
||||
// If running locally, validators need to have self-stake to be displayed
|
||||
// Run cy.validatorsSelfDelegate() in before hook
|
||||
|
||||
context('Validators Page - verify elements on page', function () {
|
||||
before('navigate to validators page', function () {
|
||||
cy.visit('/validators');
|
||||
|
||||
@@ -8,6 +8,7 @@ import './wallet-eth.functions.ts';
|
||||
import './wallet-teardown.functions.ts';
|
||||
import './wallet-vega.functions.ts';
|
||||
import './proposal.functions.ts';
|
||||
import 'cypress-mochawesome-reporter/register';
|
||||
import registerCypressGrep from '@cypress/grep';
|
||||
import { aliasGQLQuery } from '@vegaprotocol/cypress';
|
||||
import { chainIdQuery, statisticsQuery } from '@vegaprotocol/mock';
|
||||
@@ -33,4 +34,6 @@ before(() => {
|
||||
aliasGQLQuery(req, 'ChainId', chainIdQuery());
|
||||
aliasGQLQuery(req, 'Statistics', statisticsQuery());
|
||||
});
|
||||
// Self stake validators so they are displayed
|
||||
cy.validatorsSelfDelegate();
|
||||
});
|
||||
|
||||
@@ -3,7 +3,6 @@ import {
|
||||
StakingBridge,
|
||||
Token,
|
||||
TokenVesting,
|
||||
TokenFaucetable,
|
||||
CollateralBridge,
|
||||
} from '@vegaprotocol/smart-contracts';
|
||||
import { ethers, Wallet } from 'ethers';
|
||||
@@ -41,7 +40,7 @@ export async function depositAsset(
|
||||
decimalPlaces: number
|
||||
) {
|
||||
// Approve asset
|
||||
const faucet = new TokenFaucetable(assetEthAddress, signer);
|
||||
const faucet = new Token(assetEthAddress, signer);
|
||||
cy.wrap(
|
||||
faucet.approve(Erc20BridgeAddress, amount + '0'.repeat(decimalPlaces + 1)),
|
||||
{
|
||||
@@ -62,7 +61,7 @@ export async function depositAsset(
|
||||
}
|
||||
|
||||
export async function faucetAsset(assetEthAddress: string) {
|
||||
const faucet = new TokenFaucetable(assetEthAddress, signer);
|
||||
const faucet = new Token(assetEthAddress, signer);
|
||||
await promiseWithTimeout(faucet.faucet(), 10 * 60 * 1000, 'faucet asset');
|
||||
}
|
||||
|
||||
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
function ReactMarkdown({ children }) {
|
||||
// eslint-disable-next-line react/jsx-no-useless-fragment
|
||||
return <>{children}</>;
|
||||
}
|
||||
|
||||
export default ReactMarkdown;
|
||||
+57
-15
@@ -2,7 +2,6 @@ import './i18n';
|
||||
|
||||
import React, { useEffect } from 'react';
|
||||
import * as Sentry from '@sentry/react';
|
||||
import { Integrations } from '@sentry/tracing';
|
||||
import { BrowserRouter as Router, useLocation } from 'react-router-dom';
|
||||
import { AppLoader } from './app-loader';
|
||||
import { NetworkInfo } from '@vegaprotocol/network-info';
|
||||
@@ -17,6 +16,7 @@ import { AppStateProvider } from './contexts/app-state/app-state-provider';
|
||||
import { ContractsProvider } from './contexts/contracts/contracts-provider';
|
||||
import { AppRouter } from './routes';
|
||||
import type { EthereumConfig } from '@vegaprotocol/web3';
|
||||
import { WithdrawalApprovalDialogContainer } from '@vegaprotocol/web3';
|
||||
import {
|
||||
createConnectors,
|
||||
useEthTransactionManager,
|
||||
@@ -44,7 +44,7 @@ import {
|
||||
} from '@vegaprotocol/environment';
|
||||
import { ENV } from './config';
|
||||
import type { InMemoryCacheConfig } from '@apollo/client';
|
||||
import { WithdrawalDialog } from '@vegaprotocol/withdraws';
|
||||
import { CreateWithdrawalDialog } from '@vegaprotocol/withdraws';
|
||||
import { SplashLoader } from './components/splash-loader';
|
||||
import { ToastsManager } from './toasts-manager';
|
||||
import {
|
||||
@@ -53,6 +53,7 @@ import {
|
||||
} from './components/telemetry-dialog/telemetry-dialog';
|
||||
import { useLocalStorage } from '@vegaprotocol/react-helpers';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { isPartyNotFoundError } from './lib/party';
|
||||
|
||||
const cache: InMemoryCacheConfig = {
|
||||
typePolicies: {
|
||||
@@ -158,7 +159,8 @@ const Web3Container = ({
|
||||
<InitializeHandlers />
|
||||
<VegaWalletDialogs />
|
||||
<TransactionModal />
|
||||
<WithdrawalDialog />
|
||||
<CreateWithdrawalDialog />
|
||||
<WithdrawalApprovalDialogContainer />
|
||||
<TelemetryDialog />
|
||||
</>
|
||||
</BalanceManager>
|
||||
@@ -184,6 +186,10 @@ const ScrollToTop = () => {
|
||||
return null;
|
||||
};
|
||||
|
||||
const removeQueryParams = (url: string) => {
|
||||
return url.split('?')[0];
|
||||
};
|
||||
|
||||
const AppContainer = () => {
|
||||
const { config, loading, error } = useEthereumConfig();
|
||||
const {
|
||||
@@ -201,25 +207,61 @@ const AppContainer = () => {
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
if (ENV.dsn && telemetryOn) {
|
||||
if (ENV.dsn && telemetryOn === 'true') {
|
||||
Sentry.init({
|
||||
dsn: ENV.dsn,
|
||||
integrations: [new Integrations.BrowserTracing()],
|
||||
tracesSampleRate: 0.1,
|
||||
enabled: true,
|
||||
environment: VEGA_ENV,
|
||||
release: GIT_COMMIT_HASH,
|
||||
beforeSend(event) {
|
||||
if (event.request?.url?.includes('/claim?')) {
|
||||
return {
|
||||
...event,
|
||||
request: {
|
||||
...event.request,
|
||||
url: event.request?.url.split('?')[0],
|
||||
},
|
||||
};
|
||||
beforeSend(event, hint) {
|
||||
const error = hint?.originalException;
|
||||
const errorIsString = typeof error === 'string';
|
||||
const errorIsObject = error instanceof Error;
|
||||
const requestUrl = event.request?.url;
|
||||
const transaction = event.transaction;
|
||||
|
||||
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);
|
||||
|
||||
@@ -1,23 +1,14 @@
|
||||
import { Button } from '@vegaprotocol/ui-toolkit';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import {
|
||||
AppStateActionType,
|
||||
useAppState,
|
||||
} from '../../contexts/app-state/app-state-context';
|
||||
import { useWeb3ConnectStore } from '@vegaprotocol/web3';
|
||||
|
||||
export const EthConnectPrompt = () => {
|
||||
const { t } = useTranslation();
|
||||
const { appDispatch } = useAppState();
|
||||
const { open } = useWeb3ConnectStore();
|
||||
return (
|
||||
<Button
|
||||
variant="default"
|
||||
onClick={() =>
|
||||
appDispatch({
|
||||
type: AppStateActionType.SET_ETH_WALLET_OVERLAY,
|
||||
isOpen: true,
|
||||
})
|
||||
}
|
||||
onClick={open}
|
||||
fill={true}
|
||||
data-testid="connect-to-eth-btn"
|
||||
>
|
||||
|
||||
@@ -2,10 +2,7 @@ import { Button } from '@vegaprotocol/ui-toolkit';
|
||||
import { useWeb3React } from '@web3-react/core';
|
||||
import type { ReactElement } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import {
|
||||
AppStateActionType,
|
||||
useAppState,
|
||||
} from '../../contexts/app-state/app-state-context';
|
||||
import { useWeb3ConnectStore } from '@vegaprotocol/web3';
|
||||
|
||||
export const EthWalletContainer = ({
|
||||
children,
|
||||
@@ -14,21 +11,12 @@ export const EthWalletContainer = ({
|
||||
}) => {
|
||||
const { account } = useWeb3React();
|
||||
const { t } = useTranslation();
|
||||
const { appDispatch } = useAppState();
|
||||
const { open } = useWeb3ConnectStore();
|
||||
|
||||
if (!account) {
|
||||
return (
|
||||
<div className="w-full text-center">
|
||||
<Button
|
||||
onClick={() =>
|
||||
appDispatch({
|
||||
type: AppStateActionType.SET_ETH_WALLET_OVERLAY,
|
||||
isOpen: true,
|
||||
})
|
||||
}
|
||||
>
|
||||
{t('connectEthWallet')}
|
||||
</Button>
|
||||
<Button onClick={open}>{t('connectEthWallet')}</Button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -27,7 +27,11 @@ import {
|
||||
import { Loader } from '@vegaprotocol/ui-toolkit';
|
||||
import colors from 'tailwindcss/colors';
|
||||
import { useBalances } from '../../lib/balances/balances-store';
|
||||
import { useEthereumConfig, useWeb3Disconnect } from '@vegaprotocol/web3';
|
||||
import {
|
||||
useEthereumConfig,
|
||||
useWeb3ConnectStore,
|
||||
useWeb3Disconnect,
|
||||
} from '@vegaprotocol/web3';
|
||||
import { getChainName } from '@vegaprotocol/web3';
|
||||
|
||||
const removeLeadingAddressSymbol = (key: string) => {
|
||||
@@ -189,6 +193,7 @@ export const EthWallet = () => {
|
||||
const pendingTxs = usePendingTransactions();
|
||||
const disconnect = useWeb3Disconnect(connector);
|
||||
const { config } = useEthereumConfig();
|
||||
const { open } = useWeb3ConnectStore();
|
||||
|
||||
return (
|
||||
<WalletCard>
|
||||
@@ -233,12 +238,7 @@ export const EthWallet = () => {
|
||||
) : (
|
||||
<Button
|
||||
fill={true}
|
||||
onClick={() =>
|
||||
appDispatch({
|
||||
type: AppStateActionType.SET_ETH_WALLET_OVERLAY,
|
||||
isOpen: true,
|
||||
})
|
||||
}
|
||||
onClick={open}
|
||||
data-testid="connect-to-eth-wallet-button"
|
||||
>
|
||||
{t('connectEthWalletToAssociate')}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Link } from '@vegaprotocol/ui-toolkit';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { ExternalLinks } from '@vegaprotocol/utils';
|
||||
import { ExternalLinks } from '@vegaprotocol/environment';
|
||||
|
||||
export const DownloadWalletPrompt = () => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
@@ -26,6 +26,7 @@ import type {
|
||||
WalletDelegationFieldsFragment,
|
||||
} from './__generated__/Delegations';
|
||||
import { DelegationsDocument } from './__generated__/Delegations';
|
||||
import { isPartyNotFoundError } from '../../lib/party';
|
||||
|
||||
export const usePollForDelegations = () => {
|
||||
const { token: vegaToken } = useContracts();
|
||||
@@ -75,6 +76,7 @@ export const usePollForDelegations = () => {
|
||||
})
|
||||
.then((res) => {
|
||||
if (!mounted) return;
|
||||
|
||||
const canonisedDelegations = removePaginationWrapper(
|
||||
res.data.party?.delegationsConnection?.edges
|
||||
);
|
||||
@@ -203,6 +205,15 @@ export const usePollForDelegations = () => {
|
||||
setDelegatedNodes(delegatedAmounts);
|
||||
})
|
||||
.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);
|
||||
// If query fails stop interval. Its almost certain that the query
|
||||
// will just continue to fail
|
||||
|
||||
@@ -25,20 +25,26 @@ export function Web3Connector({
|
||||
connectors,
|
||||
chainId,
|
||||
}: Web3ConnectorProps) {
|
||||
const { appState, appDispatch } = useAppState();
|
||||
const { open, close, isOpen } = useWeb3ConnectStore();
|
||||
|
||||
const setDialogOpen = useCallback(
|
||||
(isOpen: boolean) => {
|
||||
appDispatch({ type: AppStateActionType.SET_ETH_WALLET_OVERLAY, isOpen });
|
||||
if (isOpen) {
|
||||
open();
|
||||
} else {
|
||||
close();
|
||||
}
|
||||
},
|
||||
[appDispatch]
|
||||
[open, close]
|
||||
);
|
||||
|
||||
const appChainId = Number(chainId);
|
||||
return (
|
||||
<>
|
||||
<Web3Content appChainId={appChainId}>{children}</Web3Content>
|
||||
<Web3ConnectDialog
|
||||
connectors={connectors}
|
||||
dialogOpen={appState.ethConnectOverlay}
|
||||
dialogOpen={isOpen}
|
||||
setDialogOpen={setDialogOpen}
|
||||
desiredChainId={appChainId}
|
||||
/>
|
||||
|
||||
@@ -34,9 +34,6 @@ export interface AppState {
|
||||
/** Whether or not the manage VEGA wallet overlay is open */
|
||||
vegaWalletManageOverlay: boolean;
|
||||
|
||||
/** Whether or not the connect to Ethereum wallet overlay is open */
|
||||
ethConnectOverlay: boolean;
|
||||
|
||||
/** Whether or not the transaction modal is open */
|
||||
transactionOverlay: boolean;
|
||||
/**
|
||||
@@ -57,7 +54,6 @@ export enum AppStateActionType {
|
||||
REFRESH_BALANCES,
|
||||
SET_VEGA_WALLET_OVERLAY,
|
||||
SET_VEGA_WALLET_MANAGE_OVERLAY,
|
||||
SET_ETH_WALLET_OVERLAY,
|
||||
SET_DRAWER,
|
||||
REFRESH_ASSOCIATED_BALANCES,
|
||||
SET_ASSOCIATION_BREAKDOWN,
|
||||
@@ -81,10 +77,6 @@ export type AppStateAction =
|
||||
type: AppStateActionType.SET_VEGA_WALLET_MANAGE_OVERLAY;
|
||||
isOpen: boolean;
|
||||
}
|
||||
| {
|
||||
type: AppStateActionType.SET_ETH_WALLET_OVERLAY;
|
||||
isOpen: boolean;
|
||||
}
|
||||
| {
|
||||
type: AppStateActionType.SET_DRAWER;
|
||||
isOpen: boolean;
|
||||
|
||||
@@ -16,7 +16,6 @@ const initialAppState: AppState = {
|
||||
totalSupply: new BigNumber(0),
|
||||
vegaWalletOverlay: false,
|
||||
vegaWalletManageOverlay: false,
|
||||
ethConnectOverlay: false,
|
||||
transactionOverlay: false,
|
||||
bannerMessage: '',
|
||||
disconnectNotice: false,
|
||||
@@ -45,12 +44,6 @@ function appStateReducer(state: AppState, action: AppStateAction): AppState {
|
||||
vegaWalletOverlay: action.isOpen ? false : state.vegaWalletOverlay,
|
||||
};
|
||||
}
|
||||
case AppStateActionType.SET_ETH_WALLET_OVERLAY: {
|
||||
return {
|
||||
...state,
|
||||
ethConnectOverlay: action.isOpen,
|
||||
};
|
||||
}
|
||||
case AppStateActionType.SET_DRAWER: {
|
||||
return {
|
||||
...state,
|
||||
|
||||
@@ -202,6 +202,7 @@
|
||||
"tokenVotes": "Token votes",
|
||||
"liquidityVotes": "Liquidity votes",
|
||||
"castYourVote": "Cast your vote",
|
||||
"yourVote": "Your vote",
|
||||
"for": "For",
|
||||
"against": "Against",
|
||||
"majorityRequired": "Majority Required",
|
||||
@@ -593,7 +594,9 @@
|
||||
"numberOfAgainstVotes": "Number of votes against",
|
||||
"yesPercentage": "Yes percentage",
|
||||
"noPercentage": "No percentage",
|
||||
"proposalTerms": "Proposal terms",
|
||||
"proposalJson": "Full proposal JSON",
|
||||
"proposalDetails": "Proposal details",
|
||||
"proposalDescription": "Description",
|
||||
"currentlySetTo": "Currently expected to ",
|
||||
"currently": "currently",
|
||||
"finalOutcomeMayDiffer": "Final outcome may differ",
|
||||
@@ -787,9 +790,9 @@
|
||||
"homeVegaTokenButtonText": "Manage tokens",
|
||||
"downloadProposalJson": "Download proposal as JSON",
|
||||
"networkUpgrade": "Network Upgrade",
|
||||
"PROTOCOL_UPGRADE_PROPOSAL_STATUS_APPROVED": "Approved",
|
||||
"PROTOCOL_UPGRADE_PROPOSAL_STATUS_PENDING": "Pending",
|
||||
"PROTOCOL_UPGRADE_PROPOSAL_STATUS_REJECTED": "Rejected",
|
||||
"PROTOCOL_UPGRADE_PROPOSAL_STATUS_APPROVED": "Approved by validators",
|
||||
"PROTOCOL_UPGRADE_PROPOSAL_STATUS_PENDING": "Waiting for validator votes",
|
||||
"PROTOCOL_UPGRADE_PROPOSAL_STATUS_REJECTED": "Declined by validators",
|
||||
"PROTOCOL_UPGRADE_PROPOSAL_STATUS_UNSPECIFIED": "Unspecified",
|
||||
"vegaRelease{release}": "Vega Release {{release}}",
|
||||
"upgradeBlockHeight": "Upgrade block height",
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
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,7 +50,22 @@ export const useTranches = create<TranchesStore>()((set) => ({
|
||||
?.map((t) => {
|
||||
const tranche_progress =
|
||||
t.duration !== 0 ? (now - t.cliff_start) / t.duration : 0;
|
||||
const lockedDecimal = tranche_progress < 0 ? 1 : 1 - tranche_progress;
|
||||
let lockedDecimal;
|
||||
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 {
|
||||
tranche_id: t.tranche_id,
|
||||
tranche_start: secondsToDate(t.cliff_start),
|
||||
@@ -60,7 +75,7 @@ export const useTranches = create<TranchesStore>()((set) => ({
|
||||
toBigNum(t.current_balance, decimals)
|
||||
),
|
||||
locked_amount: toBigNum(t.initial_balance, decimals).times(
|
||||
lockedDecimal
|
||||
clampedLockedDecimal
|
||||
),
|
||||
users: t.users,
|
||||
};
|
||||
|
||||
@@ -12,7 +12,8 @@ import { useRefreshAfterEpoch } from '../../hooks/use-refresh-after-epoch';
|
||||
import { ProposalsListItem } from '../proposals/components/proposals-list-item';
|
||||
import { ProtocolUpgradeProposalsListItem } from '../proposals/components/protocol-upgrade-proposals-list-item/protocol-upgrade-proposals-list-item';
|
||||
import Routes from '../routes';
|
||||
import { ExternalLinks, removePaginationWrapper } from '@vegaprotocol/utils';
|
||||
import { ExternalLinks } from '@vegaprotocol/environment';
|
||||
import { removePaginationWrapper } from '@vegaprotocol/utils';
|
||||
import { useNodesQuery } from '../staking/home/__generated__/Nodes';
|
||||
import { useProposalsQuery } from '../proposals/proposals/__generated__/Proposals';
|
||||
import {
|
||||
|
||||
@@ -53,7 +53,6 @@ const mockAppState: AppState = {
|
||||
totalSupply: new BigNumber(65000000),
|
||||
vegaWalletOverlay: false,
|
||||
vegaWalletManageOverlay: false,
|
||||
ethConnectOverlay: false,
|
||||
transactionOverlay: false,
|
||||
bannerMessage: '',
|
||||
disconnectNotice: false,
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
export { ProposalDescription } from './proposal-description';
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
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,28 +206,6 @@ describe('Proposal header', () => {
|
||||
).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
|
||||
it('Renders Freeform proposal - id for title', () => {
|
||||
renderComponent(
|
||||
|
||||
+1
-24
@@ -5,7 +5,6 @@ import { Heading, SubHeading } from '../../../../components/heading';
|
||||
import type { ReactNode } from 'react';
|
||||
import type { ProposalFieldsFragment } from '../../proposals/__generated__/Proposals';
|
||||
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
|
||||
import ReactMarkdown from 'react-markdown';
|
||||
import { truncateMiddle } from '../../../../lib/truncate-middle';
|
||||
import { CurrentProposalState } from '../current-proposal-state';
|
||||
import { ProposalInfoLabel } from '../proposal-info-label';
|
||||
@@ -23,12 +22,7 @@ export const ProposalHeader = ({
|
||||
let details: ReactNode;
|
||||
let proposalType = '';
|
||||
|
||||
let title = proposal?.rationale.title.trim();
|
||||
let description = proposal?.rationale.description.trim();
|
||||
if (title?.length === 0 && description && description.length > 0) {
|
||||
title = description;
|
||||
description = '';
|
||||
}
|
||||
const title = proposal?.rationale.title.trim();
|
||||
|
||||
const titleContent = shorten(title ?? '', 100);
|
||||
|
||||
@@ -145,23 +139,6 @@ export const ProposalHeader = ({
|
||||
{details}
|
||||
</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>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
export { ProposalJson } from './proposal-json';
|
||||
+11
-11
@@ -1,15 +1,15 @@
|
||||
import classnames from 'classnames';
|
||||
import { useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Icon, SyntaxHighlighter } from '@vegaprotocol/ui-toolkit';
|
||||
import { SubHeading } from '../../../../components/heading';
|
||||
import type { PartialDeep } from 'type-fest';
|
||||
import type * as Schema from '@vegaprotocol/types';
|
||||
import { useState } from 'react';
|
||||
import classnames from 'classnames';
|
||||
import type { ProposalFieldsFragment } from '../../proposals/__generated__/Proposals';
|
||||
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
|
||||
|
||||
export const ProposalTermsJson = ({
|
||||
terms,
|
||||
export const ProposalJson = ({
|
||||
proposal,
|
||||
}: {
|
||||
terms: PartialDeep<Schema.ProposalTerms>;
|
||||
proposal: ProposalFieldsFragment | ProposalQuery['proposal'];
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const [showDetails, setShowDetails] = useState(false);
|
||||
@@ -18,20 +18,20 @@ export const ProposalTermsJson = ({
|
||||
});
|
||||
|
||||
return (
|
||||
<section>
|
||||
<section data-testid="proposal-json">
|
||||
<button
|
||||
onClick={() => setShowDetails(!showDetails)}
|
||||
data-testid="proposal-terms-toggle"
|
||||
data-testid="proposal-json-toggle"
|
||||
>
|
||||
<div className="flex items-center gap-3">
|
||||
<SubHeading title={t('proposalTerms')} />
|
||||
<SubHeading title={t('proposalJson')} />
|
||||
<div className={showDetailsIconClasses}>
|
||||
<Icon name="chevron-down" size={8} />
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
|
||||
{showDetails && <SyntaxHighlighter data={terms} />}
|
||||
{showDetails && <SyntaxHighlighter data={proposal} />}
|
||||
</section>
|
||||
);
|
||||
};
|
||||
@@ -1 +0,0 @@
|
||||
export { ProposalTermsJson } from './proposal-terms-json';
|
||||
@@ -0,0 +1 @@
|
||||
export { ProposalTerms } from './proposal-terms';
|
||||
@@ -0,0 +1,142 @@
|
||||
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,8 +25,11 @@ jest.mock('../proposal-detail-header/proposal-header', () => ({
|
||||
jest.mock('../proposal-change-table', () => ({
|
||||
ProposalChangeTable: () => <div data-testid="proposal-change-table"></div>,
|
||||
}));
|
||||
jest.mock('../proposal-terms-json', () => ({
|
||||
ProposalTermsJson: () => <div data-testid="proposal-terms-json"></div>,
|
||||
jest.mock('../proposal-json', () => ({
|
||||
ProposalJson: () => <div data-testid="proposal-json"></div>,
|
||||
}));
|
||||
jest.mock('../proposal-terms/proposal-terms', () => ({
|
||||
ProposalTerms: () => <div data-testid="proposal-terms"></div>,
|
||||
}));
|
||||
jest.mock('../proposal-votes-table', () => ({
|
||||
ProposalVotesTable: () => <div data-testid="proposal-votes-table"></div>,
|
||||
@@ -49,7 +52,8 @@ it('renders each section', async () => {
|
||||
render(<Proposal proposal={proposal as ProposalQuery['proposal']} />);
|
||||
expect(await screen.findByTestId('proposal-header')).toBeInTheDocument();
|
||||
expect(screen.getByTestId('proposal-change-table')).toBeInTheDocument();
|
||||
expect(screen.getByTestId('proposal-terms-json')).toBeInTheDocument();
|
||||
expect(screen.getByTestId('proposal-json')).toBeInTheDocument();
|
||||
expect(screen.getByTestId('proposal-terms')).toBeInTheDocument();
|
||||
expect(screen.getByTestId('proposal-votes-table')).toBeInTheDocument();
|
||||
expect(screen.getByTestId('proposal-vote-details')).toBeInTheDocument();
|
||||
expect(screen.queryByTestId('proposal-list-asset')).not.toBeInTheDocument();
|
||||
|
||||
@@ -6,8 +6,10 @@ import { AsyncRenderer, RoundedWrapper } from '@vegaprotocol/ui-toolkit';
|
||||
import { ProposalHeader } from '../proposal-detail-header/proposal-header';
|
||||
import type { ProposalFieldsFragment } from '../../proposals/__generated__/Proposals';
|
||||
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
|
||||
import { ProposalDescription } from '../proposal-description';
|
||||
import { ProposalChangeTable } from '../proposal-change-table';
|
||||
import { ProposalTermsJson } from '../proposal-terms-json';
|
||||
import { ProposalJson } from '../proposal-json';
|
||||
import { ProposalTerms } from '../proposal-terms';
|
||||
import { ProposalVotesTable } from '../proposal-votes-table';
|
||||
import { VoteDetails } from '../vote-details';
|
||||
import { ListAsset } from '../list-asset';
|
||||
@@ -20,7 +22,7 @@ export enum ProposalType {
|
||||
PROPOSAL_NETWORK_PARAMETER = 'PROPOSAL_NETWORK_PARAMETER',
|
||||
PROPOSAL_FREEFORM = 'PROPOSAL_FREEFORM',
|
||||
}
|
||||
interface ProposalProps {
|
||||
export interface ProposalProps {
|
||||
proposal: ProposalFieldsFragment | ProposalQuery['proposal'];
|
||||
}
|
||||
|
||||
@@ -78,9 +80,11 @@ export const Proposal = ({ proposal }: ProposalProps) => {
|
||||
<AsyncRenderer data={params} loading={loading} error={error}>
|
||||
<section data-testid="proposal">
|
||||
<ProposalHeader proposal={proposal} isListItem={false} />
|
||||
|
||||
<div className="my-10">
|
||||
<ProposalChangeTable proposal={proposal} />
|
||||
</div>
|
||||
|
||||
{proposal.terms.change.__typename === 'NewAsset' &&
|
||||
proposal.terms.change.source.__typename === 'ERC20' &&
|
||||
proposal.id ? (
|
||||
@@ -90,7 +94,20 @@ export const Proposal = ({ proposal }: ProposalProps) => {
|
||||
lifetimeLimit={proposal.terms.change.source.lifetimeLimit}
|
||||
/>
|
||||
) : 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}>
|
||||
<VoteDetails
|
||||
proposal={proposal}
|
||||
@@ -102,10 +119,10 @@ export const Proposal = ({ proposal }: ProposalProps) => {
|
||||
/>
|
||||
</RoundedWrapper>
|
||||
</div>
|
||||
|
||||
<div className="mb-4">
|
||||
<ProposalVotesTable proposal={proposal} proposalType={proposalType} />
|
||||
</div>
|
||||
<ProposalTermsJson terms={proposal.terms} />
|
||||
</section>
|
||||
</AsyncRenderer>
|
||||
);
|
||||
|
||||
@@ -8,12 +8,11 @@ import { ProposalsListFilter } from '../proposals-list-filter';
|
||||
import Routes from '../../../routes';
|
||||
import { Button, VegaIcon, VegaIconNames } from '@vegaprotocol/ui-toolkit';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { createDocsLinks, ExternalLinks } from '@vegaprotocol/utils';
|
||||
import { ExternalLink } from '@vegaprotocol/ui-toolkit';
|
||||
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
|
||||
import type { ProposalFieldsFragment } from '../../proposals/__generated__/Proposals';
|
||||
import type { ProtocolUpgradeProposalFieldsFragment } from '@vegaprotocol/proposals';
|
||||
import { useEnvironment } from '@vegaprotocol/environment';
|
||||
import { DocsLinks, ExternalLinks } from '@vegaprotocol/environment';
|
||||
|
||||
interface ProposalsListProps {
|
||||
proposals: Array<ProposalFieldsFragment | ProposalQuery['proposal']>;
|
||||
@@ -36,7 +35,6 @@ export const ProposalsList = ({
|
||||
protocolUpgradeProposals,
|
||||
lastBlockHeight,
|
||||
}: ProposalsListProps) => {
|
||||
const { VEGA_DOCS_URL } = useEnvironment();
|
||||
const { t } = useTranslation();
|
||||
const [filterString, setFilterString] = useState('');
|
||||
const sortedProposals = proposals.reduce(
|
||||
@@ -83,9 +81,9 @@ export const ProposalsList = ({
|
||||
marginBottom={false}
|
||||
title={t('pageTitleProposals')}
|
||||
/>
|
||||
{VEGA_DOCS_URL && (
|
||||
{DocsLinks && (
|
||||
<div className="xs:justify-self-end" data-testid="new-proposal-link">
|
||||
<ExternalLink href={createDocsLinks(VEGA_DOCS_URL).PROPOSALS_GUIDE}>
|
||||
<ExternalLink href={DocsLinks.PROPOSALS_GUIDE}>
|
||||
<Button variant="primary" size="sm">
|
||||
<div className="flex items-center gap-1">
|
||||
{t('NewProposal')}
|
||||
|
||||
+4
-3
@@ -2,9 +2,10 @@ import { render, screen } from '@testing-library/react';
|
||||
import { ProposalFormTerms } from './proposal-form-terms';
|
||||
|
||||
jest.mock('@vegaprotocol/environment', () => ({
|
||||
useEnvironment: () => ({
|
||||
VEGA_DOCS_URL: 'https://docs.vega.xyz',
|
||||
}),
|
||||
...jest.requireActual('@vegaprotocol/environment'),
|
||||
DocsLinks: {
|
||||
PROPOSALS_GUIDE: 'https://docs.vega.xyz/tutorials/proposals',
|
||||
},
|
||||
}));
|
||||
|
||||
const renderComponent = () => {
|
||||
|
||||
@@ -5,8 +5,7 @@ import {
|
||||
ExternalLink,
|
||||
TextArea,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import { createDocsLinks } from '@vegaprotocol/utils';
|
||||
import { useEnvironment } from '@vegaprotocol/environment';
|
||||
import { DocsLinks } from '@vegaprotocol/environment';
|
||||
import type { UseFormRegisterReturn } from 'react-hook-form';
|
||||
|
||||
interface ProposalFormTermsProps {
|
||||
@@ -22,23 +21,20 @@ export const ProposalFormTerms = ({
|
||||
labelOverride,
|
||||
docsLink,
|
||||
}: ProposalFormTermsProps) => {
|
||||
const { VEGA_DOCS_URL } = useEnvironment();
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<FormGroup
|
||||
label={labelOverride || t('ProposalTerms')}
|
||||
labelFor="proposal-terms"
|
||||
>
|
||||
{docsLink && VEGA_DOCS_URL && (
|
||||
{docsLink && DocsLinks && (
|
||||
<div className="mt-[-4px] mb-2 text-sm font-light">
|
||||
<span className="mr-1">{t('ProposalTermsText')}</span>
|
||||
<ExternalLink
|
||||
href={`${
|
||||
createDocsLinks(VEGA_DOCS_URL).PROPOSALS_GUIDE
|
||||
}${docsLink}`}
|
||||
href={`${DocsLinks.PROPOSALS_GUIDE}${docsLink}`}
|
||||
target="_blank"
|
||||
>
|
||||
{`${createDocsLinks(VEGA_DOCS_URL).PROPOSALS_GUIDE}${docsLink}`}
|
||||
{`${DocsLinks.PROPOSALS_GUIDE}${docsLink}`}
|
||||
</ExternalLink>
|
||||
</div>
|
||||
)}
|
||||
|
||||
+3
-1
@@ -32,7 +32,9 @@ describe('ProtocolUpgradeProposalDetailInfo', () => {
|
||||
|
||||
it('should render the state', () => {
|
||||
const { getByTestId } = renderComponent();
|
||||
expect(getByTestId('protocol-upgrade-state')).toHaveTextContent('Pending');
|
||||
expect(getByTestId('protocol-upgrade-state')).toHaveTextContent(
|
||||
'Waiting for validator votes'
|
||||
);
|
||||
});
|
||||
|
||||
it('should render the vega release tag', () => {
|
||||
|
||||
+7
-1
@@ -26,28 +26,34 @@ describe('ProtocolUpgradeProposalsListItem', () => {
|
||||
status:
|
||||
ProtocolUpgradeProposalStatus.PROTOCOL_UPGRADE_PROPOSAL_STATUS_REJECTED,
|
||||
icon: 'protocol-upgrade-proposal-status-icon-rejected',
|
||||
text: 'Declined by validators',
|
||||
},
|
||||
{
|
||||
status:
|
||||
ProtocolUpgradeProposalStatus.PROTOCOL_UPGRADE_PROPOSAL_STATUS_PENDING,
|
||||
icon: 'protocol-upgrade-proposal-status-icon-pending',
|
||||
text: 'Waiting for validator votes',
|
||||
},
|
||||
{
|
||||
status:
|
||||
ProtocolUpgradeProposalStatus.PROTOCOL_UPGRADE_PROPOSAL_STATUS_APPROVED,
|
||||
icon: 'protocol-upgrade-proposal-status-icon-approved',
|
||||
text: 'Approved by validators',
|
||||
},
|
||||
{
|
||||
status:
|
||||
ProtocolUpgradeProposalStatus.PROTOCOL_UPGRADE_PROPOSAL_STATUS_UNSPECIFIED,
|
||||
icon: 'protocol-upgrade-proposal-status-icon-unspecified',
|
||||
text: 'Unspecified',
|
||||
},
|
||||
];
|
||||
|
||||
statuses.forEach(({ status, icon }) => {
|
||||
statuses.forEach(({ status, icon, text }) => {
|
||||
renderComponent({ ...proposal, status });
|
||||
const statusIcon = screen.getByTestId(icon);
|
||||
const textContent = screen.getByText(text);
|
||||
expect(statusIcon).toBeInTheDocument();
|
||||
expect(textContent).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -111,7 +111,9 @@ describe('Vote buttons', () => {
|
||||
</AppStateProvider>
|
||||
);
|
||||
expect(
|
||||
screen.getByText('You need some VEGA tokens to participate in governance')
|
||||
screen.getByText(
|
||||
'You need some VEGA tokens to participate in governance.'
|
||||
)
|
||||
).toBeTruthy();
|
||||
});
|
||||
|
||||
|
||||
@@ -2,7 +2,12 @@ import { format } from 'date-fns';
|
||||
import React, { useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useVegaWallet, useVegaWalletDialogStore } from '@vegaprotocol/wallet';
|
||||
import { AsyncRenderer, Button, ButtonLink } from '@vegaprotocol/ui-toolkit';
|
||||
import {
|
||||
AsyncRenderer,
|
||||
Button,
|
||||
ButtonLink,
|
||||
ExternalLink,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import { addDecimal, toBigNum } from '@vegaprotocol/utils';
|
||||
import { ProposalState, VoteValue } from '@vegaprotocol/types';
|
||||
import {
|
||||
@@ -161,7 +166,12 @@ export const VoteButtons = ({
|
||||
{changeVote || (voteState === VoteState.NotCast && proposalVotable) ? (
|
||||
<>
|
||||
{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">
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { formatDistanceToNow } from 'date-fns';
|
||||
import { RoundedWrapper, Icon } from '@vegaprotocol/ui-toolkit';
|
||||
import { RoundedWrapper, Icon, ExternalLink } from '@vegaprotocol/ui-toolkit';
|
||||
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||
import { ProposalState } from '@vegaprotocol/types';
|
||||
import { useVoteSubmit, VoteProgress } from '@vegaprotocol/proposals';
|
||||
@@ -202,7 +202,12 @@ export const VoteDetails = ({
|
||||
)}
|
||||
|
||||
<section className="mt-10">
|
||||
<SubHeading title={t('castYourVote')} />
|
||||
{proposal?.state === ProposalState.STATE_OPEN ? (
|
||||
<SubHeading title={t('castYourVote')} />
|
||||
) : (
|
||||
<SubHeading title={t('yourVote')} />
|
||||
)}
|
||||
|
||||
{pubKey ? (
|
||||
proposal && (
|
||||
<VoteButtonsContainer
|
||||
@@ -224,6 +229,9 @@ export const VoteDetails = ({
|
||||
<Icon name={'info-sign'} />
|
||||
<div>{t('connectAVegaWalletToVote')}</div>
|
||||
</div>
|
||||
<ExternalLink href="https://blog.vega.xyz/how-to-vote-on-vega-2195d1e52ec5">
|
||||
{t('findOutMoreAboutHowToVote')}
|
||||
</ExternalLink>
|
||||
</div>
|
||||
<ConnectToVega />
|
||||
</RoundedWrapper>
|
||||
|
||||
@@ -16,7 +16,6 @@ const mockAppState: AppState = {
|
||||
totalSupply: mockTotalSupply,
|
||||
vegaWalletOverlay: false,
|
||||
vegaWalletManageOverlay: false,
|
||||
ethConnectOverlay: false,
|
||||
transactionOverlay: false,
|
||||
bannerMessage: '',
|
||||
disconnectNotice: false,
|
||||
|
||||
@@ -10,9 +10,10 @@ import type { MockedResponse } from '@apollo/client/testing';
|
||||
import { NetworkParamsDocument } from '@vegaprotocol/network-parameters';
|
||||
|
||||
jest.mock('@vegaprotocol/environment', () => ({
|
||||
useEnvironment: () => ({
|
||||
VEGA_DOCS_URL: 'https://docs.vega.xyz',
|
||||
}),
|
||||
...jest.requireActual('@vegaprotocol/environment'),
|
||||
DocsLinks: {
|
||||
PROPOSALS_GUIDE: 'https://docs.vega.xyz/tutorials/proposals',
|
||||
},
|
||||
}));
|
||||
|
||||
const updateMarketNetworkParamsQueryMock: MockedResponse<NetworkParamsQuery> = {
|
||||
|
||||
@@ -5,7 +5,7 @@ import {
|
||||
useProposalSubmit,
|
||||
deadlineToRoundedHours,
|
||||
} from '@vegaprotocol/proposals';
|
||||
import { useEnvironment } from '@vegaprotocol/environment';
|
||||
import { useEnvironment, DocsLinks } from '@vegaprotocol/environment';
|
||||
import {
|
||||
ProposalFormDescription,
|
||||
ProposalFormSubheader,
|
||||
@@ -17,7 +17,6 @@ import {
|
||||
import { ProposalMinRequirements } from '../../components/shared';
|
||||
import { AsyncRenderer, ExternalLink } from '@vegaprotocol/ui-toolkit';
|
||||
import { Heading } from '../../../../components/heading';
|
||||
import { createDocsLinks } from '@vegaprotocol/utils';
|
||||
import {
|
||||
NetworkParams,
|
||||
useNetworkParams,
|
||||
@@ -42,7 +41,7 @@ export const ProposeFreeform = () => {
|
||||
NetworkParams.governance_proposal_freeform_minProposerBalance,
|
||||
NetworkParams.spam_protection_proposal_min_tokens,
|
||||
]);
|
||||
const { VEGA_DOCS_URL, VEGA_EXPLORER_URL } = useEnvironment();
|
||||
const { VEGA_EXPLORER_URL } = useEnvironment();
|
||||
const { t } = useTranslation();
|
||||
const {
|
||||
register,
|
||||
@@ -111,17 +110,13 @@ export const ProposeFreeform = () => {
|
||||
userAction={ProposalUserAction.CREATE}
|
||||
/>
|
||||
|
||||
{VEGA_DOCS_URL && (
|
||||
{DocsLinks && (
|
||||
<p className="text-sm" data-testid="proposal-docs-link">
|
||||
<span className="mr-1">{t('ProposalTermsText')}</span>
|
||||
<ExternalLink
|
||||
href={`${
|
||||
createDocsLinks(VEGA_DOCS_URL).PROPOSALS_GUIDE
|
||||
}${DOCS_LINK}`}
|
||||
href={`${DocsLinks.PROPOSALS_GUIDE}${DOCS_LINK}`}
|
||||
target="_blank"
|
||||
>{`${
|
||||
createDocsLinks(VEGA_DOCS_URL).PROPOSALS_GUIDE
|
||||
}${DOCS_LINK}`}</ExternalLink>
|
||||
>{`${DocsLinks.PROPOSALS_GUIDE}${DOCS_LINK}`}</ExternalLink>
|
||||
</p>
|
||||
)}
|
||||
|
||||
|
||||
+4
-3
@@ -10,9 +10,10 @@ import { NetworkParamsDocument } from '@vegaprotocol/network-parameters';
|
||||
import type { MockedResponse } from '@apollo/client/testing';
|
||||
|
||||
jest.mock('@vegaprotocol/environment', () => ({
|
||||
useEnvironment: () => ({
|
||||
VEGA_DOCS_URL: 'https://docs.vega.xyz',
|
||||
}),
|
||||
...jest.requireActual('@vegaprotocol/environment'),
|
||||
DocsLinks: {
|
||||
PROPOSALS_GUIDE: 'https://docs.vega.xyz/tutorials/proposals',
|
||||
},
|
||||
}));
|
||||
|
||||
const updateMarketNetworkParamsQueryMock: MockedResponse<NetworkParamsQuery> = {
|
||||
|
||||
+6
-13
@@ -1,10 +1,7 @@
|
||||
import { useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import {
|
||||
createDocsLinks,
|
||||
suitableForSyntaxHighlighter,
|
||||
} from '@vegaprotocol/utils';
|
||||
import { suitableForSyntaxHighlighter } from '@vegaprotocol/utils';
|
||||
import { useNetworkParams } from '@vegaprotocol/network-parameters';
|
||||
import {
|
||||
getClosingTimestamp,
|
||||
@@ -12,7 +9,7 @@ import {
|
||||
useProposalSubmit,
|
||||
doesValueEquateToParam,
|
||||
} from '@vegaprotocol/proposals';
|
||||
import { useEnvironment } from '@vegaprotocol/environment';
|
||||
import { useEnvironment, DocsLinks } from '@vegaprotocol/environment';
|
||||
import {
|
||||
ProposalFormDescription,
|
||||
ProposalFormSubheader,
|
||||
@@ -84,7 +81,7 @@ export const ProposeNetworkParameter = () => {
|
||||
loading: networkParamsLoading,
|
||||
error: networkParamsError,
|
||||
} = useNetworkParams();
|
||||
const { VEGA_EXPLORER_URL, VEGA_DOCS_URL } = useEnvironment();
|
||||
const { VEGA_EXPLORER_URL } = useEnvironment();
|
||||
const { t } = useTranslation();
|
||||
const {
|
||||
register,
|
||||
@@ -175,17 +172,13 @@ export const ProposeNetworkParameter = () => {
|
||||
userAction={ProposalUserAction.CREATE}
|
||||
/>
|
||||
|
||||
{VEGA_DOCS_URL && (
|
||||
{DocsLinks && (
|
||||
<p className="text-sm" data-testid="proposal-docs-link">
|
||||
<span className="mr-1">{t('ProposalTermsText')}</span>
|
||||
<ExternalLink
|
||||
href={`${
|
||||
createDocsLinks(VEGA_DOCS_URL).PROPOSALS_GUIDE
|
||||
}${DOCS_LINK}`}
|
||||
href={`${DocsLinks.PROPOSALS_GUIDE}${DOCS_LINK}`}
|
||||
target="_blank"
|
||||
>{`${
|
||||
createDocsLinks(VEGA_DOCS_URL).PROPOSALS_GUIDE
|
||||
}${DOCS_LINK}`}</ExternalLink>
|
||||
>{`${DocsLinks.PROPOSALS_GUIDE}${DOCS_LINK}`}</ExternalLink>
|
||||
</p>
|
||||
)}
|
||||
|
||||
|
||||
@@ -10,9 +10,10 @@ import type { MockedResponse } from '@apollo/client/testing';
|
||||
import { NetworkParamsDocument } from '@vegaprotocol/network-parameters';
|
||||
|
||||
jest.mock('@vegaprotocol/environment', () => ({
|
||||
useEnvironment: () => ({
|
||||
VEGA_DOCS_URL: 'https://docs.vega.xyz',
|
||||
}),
|
||||
...jest.requireActual('@vegaprotocol/environment'),
|
||||
DocsLinks: {
|
||||
PROPOSALS_GUIDE: 'https://docs.vega.xyz/tutorials/proposals',
|
||||
},
|
||||
}));
|
||||
|
||||
const newAssetNetworkParamsQueryMock: MockedResponse<NetworkParamsQuery> = {
|
||||
|
||||
@@ -7,8 +7,8 @@ import {
|
||||
useProposalSubmit,
|
||||
doesValueEquateToParam,
|
||||
} from '@vegaprotocol/proposals';
|
||||
import { useEnvironment } from '@vegaprotocol/environment';
|
||||
import { createDocsLinks, validateJson } from '@vegaprotocol/utils';
|
||||
import { useEnvironment, DocsLinks } from '@vegaprotocol/environment';
|
||||
import { validateJson } from '@vegaprotocol/utils';
|
||||
import {
|
||||
NetworkParams,
|
||||
useNetworkParams,
|
||||
@@ -53,7 +53,7 @@ export const ProposeNewAsset = () => {
|
||||
NetworkParams.governance_proposal_asset_minProposerBalance,
|
||||
NetworkParams.spam_protection_proposal_min_tokens,
|
||||
]);
|
||||
const { VEGA_EXPLORER_URL, VEGA_DOCS_URL } = useEnvironment();
|
||||
const { VEGA_EXPLORER_URL } = useEnvironment();
|
||||
const { t } = useTranslation();
|
||||
const {
|
||||
register,
|
||||
@@ -142,17 +142,13 @@ export const ProposeNewAsset = () => {
|
||||
userAction={ProposalUserAction.CREATE}
|
||||
/>
|
||||
|
||||
{VEGA_DOCS_URL && (
|
||||
{DocsLinks && (
|
||||
<p className="text-sm" data-testid="proposal-docs-link">
|
||||
<span className="mr-1">{t('ProposalTermsText')}</span>
|
||||
<ExternalLink
|
||||
href={`${
|
||||
createDocsLinks(VEGA_DOCS_URL).PROPOSALS_GUIDE
|
||||
}${DOCS_LINK}`}
|
||||
href={`${DocsLinks.PROPOSALS_GUIDE}${DOCS_LINK}`}
|
||||
target="_blank"
|
||||
>{`${
|
||||
createDocsLinks(VEGA_DOCS_URL).PROPOSALS_GUIDE
|
||||
}${DOCS_LINK}`}</ExternalLink>
|
||||
>{`${DocsLinks.PROPOSALS_GUIDE}${DOCS_LINK}`}</ExternalLink>
|
||||
</p>
|
||||
)}
|
||||
|
||||
|
||||
@@ -10,9 +10,10 @@ import type { NetworkParamsQuery } from '@vegaprotocol/network-parameters';
|
||||
import { NetworkParamsDocument } from '@vegaprotocol/network-parameters';
|
||||
|
||||
jest.mock('@vegaprotocol/environment', () => ({
|
||||
useEnvironment: () => ({
|
||||
VEGA_DOCS_URL: 'https://docs.vega.xyz',
|
||||
}),
|
||||
...jest.requireActual('@vegaprotocol/environment'),
|
||||
DocsLinks: {
|
||||
PROPOSALS_GUIDE: 'https://docs.vega.xyz/tutorials/proposals',
|
||||
},
|
||||
}));
|
||||
|
||||
const newMarketNetworkParamsQueryMock: MockedResponse<NetworkParamsQuery> = {
|
||||
|
||||
@@ -6,8 +6,8 @@ import {
|
||||
useProposalSubmit,
|
||||
doesValueEquateToParam,
|
||||
} from '@vegaprotocol/proposals';
|
||||
import { useEnvironment } from '@vegaprotocol/environment';
|
||||
import { createDocsLinks, validateJson } from '@vegaprotocol/utils';
|
||||
import { useEnvironment, DocsLinks } from '@vegaprotocol/environment';
|
||||
import { validateJson } from '@vegaprotocol/utils';
|
||||
import {
|
||||
NetworkParams,
|
||||
useNetworkParams,
|
||||
@@ -51,7 +51,7 @@ export const ProposeNewMarket = () => {
|
||||
NetworkParams.governance_proposal_market_minProposerBalance,
|
||||
NetworkParams.spam_protection_proposal_min_tokens,
|
||||
]);
|
||||
const { VEGA_EXPLORER_URL, VEGA_DOCS_URL } = useEnvironment();
|
||||
const { VEGA_EXPLORER_URL } = useEnvironment();
|
||||
const { t } = useTranslation();
|
||||
const {
|
||||
register,
|
||||
@@ -132,17 +132,13 @@ export const ProposeNewMarket = () => {
|
||||
userAction={ProposalUserAction.CREATE}
|
||||
/>
|
||||
|
||||
{VEGA_DOCS_URL && (
|
||||
{DocsLinks && (
|
||||
<p className="text-sm" data-testid="proposal-docs-link">
|
||||
<span className="mr-1">{t('ProposalTermsText')}</span>
|
||||
<ExternalLink
|
||||
href={`${
|
||||
createDocsLinks(VEGA_DOCS_URL).PROPOSALS_GUIDE
|
||||
}${DOCS_LINK}`}
|
||||
href={`${DocsLinks.PROPOSALS_GUIDE}${DOCS_LINK}`}
|
||||
target="_blank"
|
||||
>{`${
|
||||
createDocsLinks(VEGA_DOCS_URL).PROPOSALS_GUIDE
|
||||
}${DOCS_LINK}`}</ExternalLink>
|
||||
>{`${DocsLinks.PROPOSALS_GUIDE}${DOCS_LINK}`}</ExternalLink>
|
||||
</p>
|
||||
)}
|
||||
|
||||
|
||||
@@ -2,12 +2,11 @@ import Routes from '../../routes';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { ExternalLink } from '@vegaprotocol/ui-toolkit';
|
||||
import { useEnvironment } from '@vegaprotocol/environment';
|
||||
import { useEnvironment, DocsLinks } from '@vegaprotocol/environment';
|
||||
import { Heading } from '../../../components/heading';
|
||||
import { createDocsLinks } from '@vegaprotocol/utils';
|
||||
|
||||
export const Propose = () => {
|
||||
const { VEGA_DOCS_URL, VEGA_EXPLORER_URL } = useEnvironment();
|
||||
const { VEGA_EXPLORER_URL } = useEnvironment();
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
@@ -15,14 +14,11 @@ export const Propose = () => {
|
||||
<section className="pb-6">
|
||||
<Heading title={t('NewProposal')} />
|
||||
<div className="text-sm">
|
||||
{VEGA_DOCS_URL && (
|
||||
{DocsLinks && (
|
||||
<p>
|
||||
<span className="mr-1">{t('ProposalTermsText')}</span>
|
||||
<ExternalLink
|
||||
href={createDocsLinks(VEGA_DOCS_URL).PROPOSALS_GUIDE}
|
||||
target="_blank"
|
||||
>
|
||||
{createDocsLinks(VEGA_DOCS_URL).PROPOSALS_GUIDE}
|
||||
<ExternalLink href={DocsLinks.PROPOSALS_GUIDE} target="_blank">
|
||||
{DocsLinks.PROPOSALS_GUIDE}
|
||||
</ExternalLink>
|
||||
</p>
|
||||
)}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useEnvironment } from '@vegaprotocol/environment';
|
||||
import { useEnvironment, DocsLinks } from '@vegaprotocol/environment';
|
||||
import { Heading } from '../../../../components/heading';
|
||||
import {
|
||||
AsyncRenderer,
|
||||
@@ -9,7 +9,7 @@ import {
|
||||
InputError,
|
||||
TextArea,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import { createDocsLinks, validateJson } from '@vegaprotocol/utils';
|
||||
import { validateJson } from '@vegaprotocol/utils';
|
||||
import {
|
||||
NetworkParams,
|
||||
useNetworkParams,
|
||||
@@ -39,7 +39,7 @@ export const ProposeRaw = () => {
|
||||
NetworkParams.governance_proposal_freeform_minProposerBalance,
|
||||
NetworkParams.spam_protection_proposal_min_tokens,
|
||||
]);
|
||||
const { VEGA_EXPLORER_URL, VEGA_DOCS_URL } = useEnvironment();
|
||||
const { VEGA_EXPLORER_URL } = useEnvironment();
|
||||
const { t } = useTranslation();
|
||||
const {
|
||||
register,
|
||||
@@ -79,14 +79,11 @@ export const ProposeRaw = () => {
|
||||
spamProtectionMin={params.spam_protection_proposal_min_tokens}
|
||||
/>
|
||||
|
||||
{VEGA_DOCS_URL && (
|
||||
{DocsLinks && (
|
||||
<p className="text-sm" data-testid="proposal-docs-link">
|
||||
<span className="mr-1">{t('ProposalTermsText')}</span>
|
||||
<ExternalLink
|
||||
href={createDocsLinks(VEGA_DOCS_URL).PROPOSALS_GUIDE}
|
||||
target="_blank"
|
||||
>
|
||||
{createDocsLinks(VEGA_DOCS_URL).PROPOSALS_GUIDE}
|
||||
<ExternalLink href={DocsLinks.PROPOSALS_GUIDE} target="_blank">
|
||||
{DocsLinks.PROPOSALS_GUIDE}
|
||||
</ExternalLink>
|
||||
</p>
|
||||
)}
|
||||
|
||||
+4
-3
@@ -10,9 +10,10 @@ import type { MockedResponse } from '@apollo/client/testing';
|
||||
import { NetworkParamsDocument } from '@vegaprotocol/network-parameters';
|
||||
|
||||
jest.mock('@vegaprotocol/environment', () => ({
|
||||
useEnvironment: () => ({
|
||||
VEGA_DOCS_URL: 'https://docs.vega.xyz',
|
||||
}),
|
||||
...jest.requireActual('@vegaprotocol/environment'),
|
||||
DocsLinks: {
|
||||
PROPOSALS_GUIDE: 'https://docs.vega.xyz/tutorials/proposals',
|
||||
},
|
||||
}));
|
||||
|
||||
const updateAssetNetworkParamsQueryMock: MockedResponse<NetworkParamsQuery> = {
|
||||
|
||||
@@ -6,8 +6,8 @@ import {
|
||||
useProposalSubmit,
|
||||
doesValueEquateToParam,
|
||||
} from '@vegaprotocol/proposals';
|
||||
import { useEnvironment } from '@vegaprotocol/environment';
|
||||
import { createDocsLinks, validateJson } from '@vegaprotocol/utils';
|
||||
import { useEnvironment, DocsLinks } from '@vegaprotocol/environment';
|
||||
import { validateJson } from '@vegaprotocol/utils';
|
||||
import {
|
||||
NetworkParams,
|
||||
useNetworkParams,
|
||||
@@ -51,7 +51,7 @@ export const ProposeUpdateAsset = () => {
|
||||
NetworkParams.governance_proposal_updateAsset_minProposerBalance,
|
||||
NetworkParams.spam_protection_proposal_min_tokens,
|
||||
]);
|
||||
const { VEGA_EXPLORER_URL, VEGA_DOCS_URL } = useEnvironment();
|
||||
const { VEGA_EXPLORER_URL } = useEnvironment();
|
||||
const { t } = useTranslation();
|
||||
const {
|
||||
register,
|
||||
@@ -132,17 +132,13 @@ export const ProposeUpdateAsset = () => {
|
||||
userAction={ProposalUserAction.CREATE}
|
||||
/>
|
||||
|
||||
{VEGA_DOCS_URL && (
|
||||
{DocsLinks && (
|
||||
<p className="text-sm" data-testid="proposal-docs-link">
|
||||
<span className="mr-1">{t('ProposalTermsText')}</span>
|
||||
<ExternalLink
|
||||
href={`${
|
||||
createDocsLinks(VEGA_DOCS_URL).PROPOSALS_GUIDE
|
||||
}${DOCS_LINK}`}
|
||||
href={`${DocsLinks.PROPOSALS_GUIDE}${DOCS_LINK}`}
|
||||
target="_blank"
|
||||
>{`${
|
||||
createDocsLinks(VEGA_DOCS_URL).PROPOSALS_GUIDE
|
||||
}${DOCS_LINK}`}</ExternalLink>
|
||||
>{`${DocsLinks.PROPOSALS_GUIDE}${DOCS_LINK}`}</ExternalLink>
|
||||
</p>
|
||||
)}
|
||||
|
||||
|
||||
+6
-10
@@ -7,8 +7,8 @@ import {
|
||||
getEnactmentTimestamp,
|
||||
useProposalSubmit,
|
||||
} from '@vegaprotocol/proposals';
|
||||
import { useEnvironment } from '@vegaprotocol/environment';
|
||||
import { createDocsLinks, validateJson } from '@vegaprotocol/utils';
|
||||
import { useEnvironment, DocsLinks } from '@vegaprotocol/environment';
|
||||
import { validateJson } from '@vegaprotocol/utils';
|
||||
import {
|
||||
NetworkParams,
|
||||
useNetworkParams,
|
||||
@@ -98,7 +98,7 @@ export const ProposeUpdateMarket = () => {
|
||||
const [selectedMarket, setSelectedMarket] = useState<string | undefined>(
|
||||
undefined
|
||||
);
|
||||
const { VEGA_EXPLORER_URL, VEGA_DOCS_URL } = useEnvironment();
|
||||
const { VEGA_EXPLORER_URL } = useEnvironment();
|
||||
const { t } = useTranslation();
|
||||
const {
|
||||
register,
|
||||
@@ -181,17 +181,13 @@ export const ProposeUpdateMarket = () => {
|
||||
userAction={ProposalUserAction.CREATE}
|
||||
/>
|
||||
|
||||
{VEGA_DOCS_URL && (
|
||||
{DocsLinks && (
|
||||
<p className="text-sm" data-testid="proposal-docs-link">
|
||||
<span className="mr-1">{t('ProposalTermsText')}</span>
|
||||
<ExternalLink
|
||||
href={`${
|
||||
createDocsLinks(VEGA_DOCS_URL).PROPOSALS_GUIDE
|
||||
}${DOCS_LINK}`}
|
||||
href={`${DocsLinks.PROPOSALS_GUIDE}${DOCS_LINK}`}
|
||||
target="_blank"
|
||||
>{`${
|
||||
createDocsLinks(VEGA_DOCS_URL).PROPOSALS_GUIDE
|
||||
}${DOCS_LINK}`}</ExternalLink>
|
||||
>{`${DocsLinks.PROPOSALS_GUIDE}${DOCS_LINK}`}</ExternalLink>
|
||||
</p>
|
||||
)}
|
||||
|
||||
|
||||
@@ -54,22 +54,16 @@ export const TrancheItem = ({
|
||||
{formatNumber(total, 2)}
|
||||
</span>
|
||||
</div>
|
||||
<table className="w-full">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>{t('Starts unlocking')}</td>
|
||||
<td className="text-right">
|
||||
{format(tranche.tranche_start, DATE_FORMAT_LONG)}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{t('Fully unlocked')}</td>
|
||||
<td className="text-right">
|
||||
{format(tranche.tranche_end, DATE_FORMAT_LONG)}
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div className="grid grid-cols-2 my-2">
|
||||
<div>
|
||||
<span>{t('Starts unlocking')}:</span>{' '}
|
||||
<span>{format(tranche.tranche_start, DATE_FORMAT_LONG)}</span>
|
||||
</div>
|
||||
<div className="justify-self-end">
|
||||
<span>{t('Fully unlocked')}:</span>{' '}
|
||||
<span>{format(tranche.tranche_end, DATE_FORMAT_LONG)}</span>
|
||||
</div>
|
||||
</div>
|
||||
<LockedProgress
|
||||
locked={locked}
|
||||
unlocked={unlocked}
|
||||
|
||||
+4
-2
@@ -25,7 +25,7 @@ const mockData = {
|
||||
amount: '0',
|
||||
percentageOfTotal: '0',
|
||||
},
|
||||
ACCOUNT_TYPE_FEES_LIQUIDITY: {
|
||||
ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES: {
|
||||
amount: '0',
|
||||
percentageOfTotal: '0',
|
||||
},
|
||||
@@ -55,7 +55,9 @@ describe('EpochIndividualRewardsTable', () => {
|
||||
expect(
|
||||
getByTestId('ACCOUNT_TYPE_REWARD_MAKER_RECEIVED_FEES')
|
||||
).toBeInTheDocument();
|
||||
expect(getByTestId('ACCOUNT_TYPE_FEES_LIQUIDITY')).toBeInTheDocument();
|
||||
expect(
|
||||
getByTestId('ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES')
|
||||
).toBeInTheDocument();
|
||||
expect(
|
||||
getByTestId('ACCOUNT_TYPE_REWARD_MARKET_PROPOSERS')
|
||||
).toBeInTheDocument();
|
||||
|
||||
+11
-11
@@ -24,7 +24,7 @@ describe('generateEpochIndividualRewardsList', () => {
|
||||
};
|
||||
|
||||
const reward3: RewardFieldsFragment = {
|
||||
rewardType: AccountType.ACCOUNT_TYPE_FEES_LIQUIDITY,
|
||||
rewardType: AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES,
|
||||
amount: '200',
|
||||
percentageOfTotal: '0.2',
|
||||
receivedAt: new Date(),
|
||||
@@ -34,7 +34,7 @@ describe('generateEpochIndividualRewardsList', () => {
|
||||
};
|
||||
|
||||
const reward4: RewardFieldsFragment = {
|
||||
rewardType: AccountType.ACCOUNT_TYPE_FEES_LIQUIDITY,
|
||||
rewardType: AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES,
|
||||
amount: '100',
|
||||
percentageOfTotal: '0.1',
|
||||
receivedAt: new Date(),
|
||||
@@ -44,7 +44,7 @@ describe('generateEpochIndividualRewardsList', () => {
|
||||
};
|
||||
|
||||
const reward5: RewardFieldsFragment = {
|
||||
rewardType: AccountType.ACCOUNT_TYPE_FEES_LIQUIDITY,
|
||||
rewardType: AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES,
|
||||
amount: '150',
|
||||
percentageOfTotal: '0.15',
|
||||
receivedAt: new Date(),
|
||||
@@ -105,7 +105,7 @@ describe('generateEpochIndividualRewardsList', () => {
|
||||
amount: '0',
|
||||
percentageOfTotal: '0',
|
||||
},
|
||||
[AccountType.ACCOUNT_TYPE_FEES_LIQUIDITY]: {
|
||||
[AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES]: {
|
||||
amount: '0',
|
||||
percentageOfTotal: '0',
|
||||
},
|
||||
@@ -172,7 +172,7 @@ describe('generateEpochIndividualRewardsList', () => {
|
||||
amount: '0',
|
||||
percentageOfTotal: '0',
|
||||
},
|
||||
[AccountType.ACCOUNT_TYPE_FEES_LIQUIDITY]: {
|
||||
[AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES]: {
|
||||
amount: '200',
|
||||
percentageOfTotal: '0.2',
|
||||
},
|
||||
@@ -202,7 +202,7 @@ describe('generateEpochIndividualRewardsList', () => {
|
||||
amount: '0',
|
||||
percentageOfTotal: '0',
|
||||
},
|
||||
[AccountType.ACCOUNT_TYPE_FEES_LIQUIDITY]: {
|
||||
[AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES]: {
|
||||
amount: '0',
|
||||
percentageOfTotal: '0',
|
||||
},
|
||||
@@ -237,7 +237,7 @@ describe('generateEpochIndividualRewardsList', () => {
|
||||
amount: '0',
|
||||
percentageOfTotal: '0',
|
||||
},
|
||||
[AccountType.ACCOUNT_TYPE_FEES_LIQUIDITY]: {
|
||||
[AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES]: {
|
||||
amount: '100',
|
||||
percentageOfTotal: '0.1',
|
||||
},
|
||||
@@ -285,7 +285,7 @@ describe('generateEpochIndividualRewardsList', () => {
|
||||
amount: '0',
|
||||
percentageOfTotal: '0',
|
||||
},
|
||||
[AccountType.ACCOUNT_TYPE_FEES_LIQUIDITY]: {
|
||||
[AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES]: {
|
||||
amount: '150',
|
||||
percentageOfTotal: '0.15',
|
||||
},
|
||||
@@ -320,7 +320,7 @@ describe('generateEpochIndividualRewardsList', () => {
|
||||
amount: '0',
|
||||
percentageOfTotal: '0',
|
||||
},
|
||||
[AccountType.ACCOUNT_TYPE_FEES_LIQUIDITY]: {
|
||||
[AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES]: {
|
||||
amount: '200',
|
||||
percentageOfTotal: '0.2',
|
||||
},
|
||||
@@ -350,7 +350,7 @@ describe('generateEpochIndividualRewardsList', () => {
|
||||
amount: '0',
|
||||
percentageOfTotal: '0',
|
||||
},
|
||||
[AccountType.ACCOUNT_TYPE_FEES_LIQUIDITY]: {
|
||||
[AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES]: {
|
||||
amount: '0',
|
||||
percentageOfTotal: '0',
|
||||
},
|
||||
@@ -395,7 +395,7 @@ describe('generateEpochIndividualRewardsList', () => {
|
||||
amount: '0',
|
||||
percentageOfTotal: '0',
|
||||
},
|
||||
[AccountType.ACCOUNT_TYPE_FEES_LIQUIDITY]: {
|
||||
[AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES]: {
|
||||
amount: '100',
|
||||
percentageOfTotal: '0.1',
|
||||
},
|
||||
|
||||
+18
-1
@@ -79,7 +79,24 @@ export const generateEpochIndividualRewardsList = ({
|
||||
epoch?.rewards.push(asset);
|
||||
}
|
||||
|
||||
asset.rewardTypes[rewardType] = { amount, percentageOfTotal };
|
||||
if (!asset.rewardTypes[rewardType]) {
|
||||
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
|
||||
asset.totalAmount = Object.values(asset.rewardTypes).reduce(
|
||||
|
||||
+4
-2
@@ -29,7 +29,7 @@ const rewardsList = [
|
||||
amount: '0',
|
||||
},
|
||||
{
|
||||
rewardType: AccountType.ACCOUNT_TYPE_FEES_LIQUIDITY,
|
||||
rewardType: AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES,
|
||||
amount: '0',
|
||||
},
|
||||
{
|
||||
@@ -78,7 +78,9 @@ describe('EpochTotalRewardsTable', () => {
|
||||
expect(
|
||||
getByTestId('ACCOUNT_TYPE_REWARD_MAKER_RECEIVED_FEES')
|
||||
).toBeInTheDocument();
|
||||
expect(getByTestId('ACCOUNT_TYPE_FEES_LIQUIDITY')).toBeInTheDocument();
|
||||
expect(
|
||||
getByTestId('ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES')
|
||||
).toBeInTheDocument();
|
||||
expect(
|
||||
getByTestId('ACCOUNT_TYPE_REWARD_MARKET_PROPOSERS')
|
||||
).toBeInTheDocument();
|
||||
|
||||
+21
-15
@@ -162,9 +162,10 @@ describe('generateEpochAssetRewardsList', () => {
|
||||
},
|
||||
],
|
||||
[
|
||||
AccountType.ACCOUNT_TYPE_FEES_LIQUIDITY,
|
||||
AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES,
|
||||
{
|
||||
rewardType: AccountType.ACCOUNT_TYPE_FEES_LIQUIDITY,
|
||||
rewardType:
|
||||
AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES,
|
||||
amount: '0',
|
||||
},
|
||||
],
|
||||
@@ -227,7 +228,7 @@ describe('generateEpochAssetRewardsList', () => {
|
||||
node: {
|
||||
epoch: 2,
|
||||
assetId: '1',
|
||||
rewardType: AccountType.ACCOUNT_TYPE_FEES_LIQUIDITY,
|
||||
rewardType: AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES,
|
||||
amount: '5',
|
||||
},
|
||||
},
|
||||
@@ -287,9 +288,10 @@ describe('generateEpochAssetRewardsList', () => {
|
||||
},
|
||||
],
|
||||
[
|
||||
AccountType.ACCOUNT_TYPE_FEES_LIQUIDITY,
|
||||
AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES,
|
||||
{
|
||||
rewardType: AccountType.ACCOUNT_TYPE_FEES_LIQUIDITY,
|
||||
rewardType:
|
||||
AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES,
|
||||
amount: '0',
|
||||
},
|
||||
],
|
||||
@@ -351,9 +353,10 @@ describe('generateEpochAssetRewardsList', () => {
|
||||
},
|
||||
],
|
||||
[
|
||||
AccountType.ACCOUNT_TYPE_FEES_LIQUIDITY,
|
||||
AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES,
|
||||
{
|
||||
rewardType: AccountType.ACCOUNT_TYPE_FEES_LIQUIDITY,
|
||||
rewardType:
|
||||
AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES,
|
||||
amount: '5',
|
||||
},
|
||||
],
|
||||
@@ -416,7 +419,7 @@ describe('generateEpochAssetRewardsList', () => {
|
||||
node: {
|
||||
epoch: 2,
|
||||
assetId: '1',
|
||||
rewardType: AccountType.ACCOUNT_TYPE_FEES_LIQUIDITY,
|
||||
rewardType: AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES,
|
||||
amount: '6',
|
||||
},
|
||||
},
|
||||
@@ -424,7 +427,7 @@ describe('generateEpochAssetRewardsList', () => {
|
||||
node: {
|
||||
epoch: 2,
|
||||
assetId: '1',
|
||||
rewardType: AccountType.ACCOUNT_TYPE_FEES_LIQUIDITY,
|
||||
rewardType: AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES,
|
||||
amount: '27',
|
||||
},
|
||||
},
|
||||
@@ -497,9 +500,10 @@ describe('generateEpochAssetRewardsList', () => {
|
||||
},
|
||||
],
|
||||
[
|
||||
AccountType.ACCOUNT_TYPE_FEES_LIQUIDITY,
|
||||
AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES,
|
||||
{
|
||||
rewardType: AccountType.ACCOUNT_TYPE_FEES_LIQUIDITY,
|
||||
rewardType:
|
||||
AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES,
|
||||
amount: '33',
|
||||
},
|
||||
],
|
||||
@@ -561,9 +565,10 @@ describe('generateEpochAssetRewardsList', () => {
|
||||
},
|
||||
],
|
||||
[
|
||||
AccountType.ACCOUNT_TYPE_FEES_LIQUIDITY,
|
||||
AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES,
|
||||
{
|
||||
rewardType: AccountType.ACCOUNT_TYPE_FEES_LIQUIDITY,
|
||||
rewardType:
|
||||
AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES,
|
||||
amount: '0',
|
||||
},
|
||||
],
|
||||
@@ -637,9 +642,10 @@ describe('generateEpochAssetRewardsList', () => {
|
||||
},
|
||||
],
|
||||
[
|
||||
AccountType.ACCOUNT_TYPE_FEES_LIQUIDITY,
|
||||
AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES,
|
||||
{
|
||||
rewardType: AccountType.ACCOUNT_TYPE_FEES_LIQUIDITY,
|
||||
rewardType:
|
||||
AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES,
|
||||
amount: '0',
|
||||
},
|
||||
],
|
||||
|
||||
@@ -11,7 +11,6 @@ import {
|
||||
ExternalLink,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||
import { createDocsLinks } from '@vegaprotocol/utils';
|
||||
import {
|
||||
useNetworkParams,
|
||||
NetworkParams,
|
||||
@@ -21,7 +20,7 @@ import { EpochCountdown } from '../../../components/epoch-countdown';
|
||||
import { Heading, SubHeading } from '../../../components/heading';
|
||||
import { EpochIndividualRewards } from '../epoch-individual-rewards/epoch-individual-rewards';
|
||||
import { useRefreshAfterEpoch } from '../../../hooks/use-refresh-after-epoch';
|
||||
import { useEnvironment } from '@vegaprotocol/environment';
|
||||
import { DocsLinks } from '@vegaprotocol/environment';
|
||||
import { ConnectToSeeRewards } from '../connect-to-see-rewards';
|
||||
import { EpochTotalRewards } from '../epoch-total-rewards/epoch-total-rewards';
|
||||
|
||||
@@ -29,7 +28,6 @@ type RewardsView = 'total' | 'individual';
|
||||
|
||||
export const RewardsPage = () => {
|
||||
const { t } = useTranslation();
|
||||
const { VEGA_DOCS_URL } = useEnvironment();
|
||||
const { pubKey, pubKeys } = useVegaWallet();
|
||||
const [toggleRewardsView, setToggleRewardsView] =
|
||||
useState<RewardsView>('total');
|
||||
@@ -68,9 +66,9 @@ export const RewardsPage = () => {
|
||||
<Heading title={t('pageTitleRewards')} />
|
||||
<p className="mb-12">
|
||||
{t('rewardsIntro')}{' '}
|
||||
{VEGA_DOCS_URL && (
|
||||
{DocsLinks && (
|
||||
<ExternalLink
|
||||
href={createDocsLinks(VEGA_DOCS_URL).REWARDS_GUIDE}
|
||||
href={DocsLinks.REWARDS_GUIDE}
|
||||
target="_blank"
|
||||
data-testid="rewards-guide-link"
|
||||
className="text-white"
|
||||
|
||||
+1
-1
@@ -26,7 +26,7 @@ export const RowAccountTypes = {
|
||||
columnTitle: 'rewardsColPriceMakingHeader',
|
||||
description: 'rewardsColPriceMakingTooltip',
|
||||
},
|
||||
[AccountType.ACCOUNT_TYPE_FEES_LIQUIDITY]: {
|
||||
[AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES]: {
|
||||
columnTitle: 'rewardsColLiquidityProvisionHeader',
|
||||
description: 'rewardsColLiquidityProvisionTooltip',
|
||||
},
|
||||
|
||||
@@ -229,7 +229,7 @@ const redirects = [
|
||||
const routerConfig = [
|
||||
{
|
||||
path: Routes.HOME,
|
||||
element: <LazyHome name="Token" />,
|
||||
element: <LazyHome name="Home" />,
|
||||
},
|
||||
{
|
||||
path: Routes.PROPOSALS,
|
||||
@@ -291,7 +291,7 @@ const routerConfig = [
|
||||
// Not lazy as loaded when a user first hits the site
|
||||
children: [
|
||||
{
|
||||
element: <Home name="Home" />,
|
||||
element: <Home name="Token" />,
|
||||
index: true,
|
||||
},
|
||||
{
|
||||
@@ -325,8 +325,11 @@ const routerConfig = [
|
||||
},
|
||||
],
|
||||
},
|
||||
{ path: 'associate', element: <LazyStakingAssociate /> },
|
||||
{ path: 'disassociate', element: <LazyStakingDisassociate /> },
|
||||
{ path: 'associate', element: <LazyStakingAssociate name="Associate" /> },
|
||||
{
|
||||
path: 'disassociate',
|
||||
element: <LazyStakingDisassociate name="Disassociate" />,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
import { useEthereumConfig } from '@vegaprotocol/web3';
|
||||
import { StakingWalletsContainer } from './components/staking-wallets-container/staking-wallets-container';
|
||||
import { StakingWalletsContainer } from './components/staking-wallets-container';
|
||||
import { AssociatePage } from './associate-page';
|
||||
import { AssociatePageNoVega } from './associate-page-no-vega';
|
||||
import { Heading } from '../../../components/heading';
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useDocumentTitle } from '../../../hooks/use-document-title';
|
||||
import type { RouteChildProps } from '../../index';
|
||||
|
||||
export const AssociateContainer = () => {
|
||||
export const AssociateContainer = ({ name }: RouteChildProps) => {
|
||||
useDocumentTitle(name);
|
||||
const { t } = useTranslation();
|
||||
const { config } = useEthereumConfig();
|
||||
|
||||
|
||||
@@ -39,7 +39,7 @@ jest.mock('@vegaprotocol/wallet', () => ({
|
||||
}));
|
||||
|
||||
const renderComponent = () => {
|
||||
return render(<Disassociate />);
|
||||
return render(<Disassociate name="Disassociate" />);
|
||||
};
|
||||
|
||||
describe('Disassociate', () => {
|
||||
|
||||
@@ -3,10 +3,12 @@ import { useWeb3React } from '@web3-react/core';
|
||||
import { EthConnectPrompt } from '../../../components/eth-connect-prompt';
|
||||
import { DisassociatePage } from './components/disassociate-page';
|
||||
import { Heading } from '../../../components/heading';
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useDocumentTitle } from '../../../hooks/use-document-title';
|
||||
import type { RouteChildProps } from '../../index';
|
||||
|
||||
export const DisassociateContainer = () => {
|
||||
export const DisassociateContainer = ({ name }: RouteChildProps) => {
|
||||
useDocumentTitle(name);
|
||||
const { t } = useTranslation();
|
||||
const { account } = useWeb3React();
|
||||
const { pubKey } = useVegaWallet();
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { EpochData } from './epoch-data';
|
||||
import { useEnvironment } from '@vegaprotocol/environment';
|
||||
import { DocsLinks } from '@vegaprotocol/environment';
|
||||
import { ExternalLink } from '@vegaprotocol/ui-toolkit';
|
||||
import { createDocsLinks } from '@vegaprotocol/utils';
|
||||
import { Heading } from '../../../components/heading';
|
||||
import React from 'react';
|
||||
|
||||
export const Staking = () => {
|
||||
const { t } = useTranslation();
|
||||
const { VEGA_DOCS_URL } = useEnvironment();
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -16,9 +14,9 @@ export const Staking = () => {
|
||||
<section>
|
||||
<p className="mb-12">
|
||||
{t('stakingIntro')}{' '}
|
||||
{VEGA_DOCS_URL && (
|
||||
{DocsLinks && (
|
||||
<ExternalLink
|
||||
href={createDocsLinks(VEGA_DOCS_URL).STAKING_GUIDE}
|
||||
href={DocsLinks.STAKING_GUIDE}
|
||||
target="_blank"
|
||||
data-testid="staking-guide-link"
|
||||
className="text-white"
|
||||
|
||||
+70
-59
@@ -131,20 +131,25 @@ export const ConsensusValidatorsTable = ({
|
||||
|
||||
const gridRef = useRef<AgGridReact | null>(null);
|
||||
|
||||
const thirdOfTotalStake = useMemo(
|
||||
() => new BigNumber(totalStake).dividedBy(3),
|
||||
[totalStake]
|
||||
);
|
||||
|
||||
const nodes = useMemo(() => {
|
||||
if (!data) return [];
|
||||
let canonisedNodes = data
|
||||
.sort((a, b) => {
|
||||
const aVotingPower = new BigNumber(a.rankingScore.votingPower);
|
||||
const bVotingPower = new BigNumber(b.rankingScore.votingPower);
|
||||
return bVotingPower.minus(aVotingPower).toNumber();
|
||||
const aStakedTotal = new BigNumber(a.stakedTotal);
|
||||
const bStakedTotal = new BigNumber(b.stakedTotal);
|
||||
return bStakedTotal.minus(aStakedTotal).toNumber();
|
||||
})
|
||||
.map((node, index) => {
|
||||
const votingPowerRanking = index + 1;
|
||||
const stakedTotalRanking = index + 1;
|
||||
|
||||
return {
|
||||
...node,
|
||||
votingPowerRanking,
|
||||
stakedTotalRanking,
|
||||
};
|
||||
})
|
||||
.map(
|
||||
@@ -157,7 +162,7 @@ export const ConsensusValidatorsTable = ({
|
||||
stakedTotal,
|
||||
rankingScore: { stakeScore, votingPower },
|
||||
pendingStake,
|
||||
votingPowerRanking,
|
||||
stakedTotalRanking,
|
||||
stakedByUser,
|
||||
pendingUserStake,
|
||||
userStakeShare,
|
||||
@@ -175,7 +180,7 @@ export const ConsensusValidatorsTable = ({
|
||||
|
||||
return {
|
||||
id,
|
||||
[ValidatorFields.RANKING_INDEX]: votingPowerRanking,
|
||||
[ValidatorFields.RANKING_INDEX]: stakedTotalRanking,
|
||||
[ValidatorFields.VALIDATOR]: {
|
||||
avatarUrl: logo,
|
||||
name,
|
||||
@@ -216,7 +221,7 @@ export const ConsensusValidatorsTable = ({
|
||||
: undefined,
|
||||
[ValidatorFields.PENDING_USER_STAKE]: pendingUserStake,
|
||||
[ValidatorFields.USER_STAKE_SHARE]: userStakeShare
|
||||
? formatNumberPercentage(new BigNumber(userStakeShare))
|
||||
? formatNumberPercentage(new BigNumber(userStakeShare), 2)
|
||||
: undefined,
|
||||
};
|
||||
}
|
||||
@@ -236,16 +241,12 @@ export const ConsensusValidatorsTable = ({
|
||||
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(
|
||||
(acc, node) => {
|
||||
if (acc.cumulativeScore < 100 / 3) {
|
||||
acc.cumulativeScore += parseFloat(
|
||||
node[ValidatorFields.NORMALISED_VOTING_POWER]
|
||||
if (acc.cumulativeScore.isLessThan(thirdOfTotalStake)) {
|
||||
const prev = acc.cumulativeScore;
|
||||
acc.cumulativeScore = prev.plus(
|
||||
new BigNumber(node[ValidatorFields.STAKE])
|
||||
);
|
||||
acc.topThird.push(node);
|
||||
return acc;
|
||||
@@ -253,61 +254,70 @@ export const ConsensusValidatorsTable = ({
|
||||
acc.remaining.push(node);
|
||||
return acc;
|
||||
},
|
||||
{ topThird: [], remaining: [], cumulativeScore: 0 } as {
|
||||
{ topThird: [], remaining: [], cumulativeScore: new BigNumber(0) } as {
|
||||
topThird: CanonisedConsensusNodeProps[];
|
||||
remaining: CanonisedConsensusNodeProps[];
|
||||
cumulativeScore: number;
|
||||
cumulativeScore: BigNumber;
|
||||
}
|
||||
);
|
||||
|
||||
// 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
|
||||
// values are correct for ordering.
|
||||
const combinedTopThird = topThird.reduce((acc, node) => {
|
||||
const {
|
||||
[ValidatorFields.STAKE]: stake,
|
||||
[ValidatorFields.STAKE_SHARE]: stakeShare,
|
||||
[ValidatorFields.PENDING_STAKE]: pendingStake,
|
||||
[ValidatorFields.NORMALISED_VOTING_POWER]: normalisedVotingPower,
|
||||
[ValidatorFields.TOTAL_PENALTIES]: totalPenalties,
|
||||
} = node;
|
||||
const combinedTopThird = topThird.reduce(
|
||||
(acc, node) => {
|
||||
const {
|
||||
[ValidatorFields.STAKE]: stake,
|
||||
[ValidatorFields.STAKE_SHARE]: stakeShare,
|
||||
[ValidatorFields.PENDING_STAKE]: pendingStake,
|
||||
[ValidatorFields.NORMALISED_VOTING_POWER]: normalisedVotingPower,
|
||||
[ValidatorFields.TOTAL_PENALTIES]: totalPenalties,
|
||||
} = node;
|
||||
|
||||
const {
|
||||
[ValidatorFields.STAKE]: accStake,
|
||||
[ValidatorFields.STAKE_SHARE]: accStakeShare,
|
||||
[ValidatorFields.PENDING_STAKE]: accPendingStake,
|
||||
[ValidatorFields.NORMALISED_VOTING_POWER]: accNormalisedVotingPower,
|
||||
[ValidatorFields.TOTAL_PENALTIES]: accTotalPenalties,
|
||||
} = acc;
|
||||
const {
|
||||
[ValidatorFields.STAKE]: accStake,
|
||||
[ValidatorFields.STAKE_SHARE]: accStakeShare,
|
||||
[ValidatorFields.PENDING_STAKE]: accPendingStake,
|
||||
[ValidatorFields.NORMALISED_VOTING_POWER]: accNormalisedVotingPower,
|
||||
[ValidatorFields.TOTAL_PENALTIES]: accTotalPenalties,
|
||||
} = acc;
|
||||
|
||||
return {
|
||||
...acc,
|
||||
[ValidatorFields.STAKE]: toBigNum(accStake, decimals)
|
||||
.plus(toBigNum(stake, decimals))
|
||||
.toString(),
|
||||
[ValidatorFields.STAKE_SHARE]: formatNumberPercentage(
|
||||
new BigNumber(parseFloat(accStakeShare)).plus(
|
||||
new BigNumber(parseFloat(stakeShare))
|
||||
return {
|
||||
...acc,
|
||||
[ValidatorFields.STAKE]: new BigNumber(accStake)
|
||||
.plus(new BigNumber(stake))
|
||||
.toString(),
|
||||
[ValidatorFields.STAKE_SHARE]: formatNumberPercentage(
|
||||
new BigNumber(parseFloat(accStakeShare)).plus(
|
||||
new BigNumber(parseFloat(stakeShare))
|
||||
),
|
||||
2
|
||||
),
|
||||
2
|
||||
),
|
||||
[ValidatorFields.PENDING_STAKE]: toBigNum(accPendingStake, decimals)
|
||||
.plus(toBigNum(pendingStake, decimals))
|
||||
.toString(),
|
||||
[ValidatorFields.NORMALISED_VOTING_POWER]: formatNumberPercentage(
|
||||
new BigNumber(parseFloat(accNormalisedVotingPower)).plus(
|
||||
new BigNumber(parseFloat(normalisedVotingPower))
|
||||
[ValidatorFields.PENDING_STAKE]: toBigNum(accPendingStake, decimals)
|
||||
.plus(toBigNum(pendingStake, decimals))
|
||||
.toString(),
|
||||
[ValidatorFields.NORMALISED_VOTING_POWER]: formatNumberPercentage(
|
||||
new BigNumber(parseFloat(accNormalisedVotingPower)).plus(
|
||||
new BigNumber(parseFloat(normalisedVotingPower))
|
||||
),
|
||||
2
|
||||
),
|
||||
2
|
||||
),
|
||||
[ValidatorFields.TOTAL_PENALTIES]: formatNumberPercentage(
|
||||
new BigNumber(parseFloat(accTotalPenalties)).plus(
|
||||
new BigNumber(parseFloat(totalPenalties))
|
||||
[ValidatorFields.TOTAL_PENALTIES]: formatNumberPercentage(
|
||||
new BigNumber(parseFloat(accTotalPenalties)).plus(
|
||||
new BigNumber(parseFloat(totalPenalties))
|
||||
),
|
||||
2
|
||||
),
|
||||
2
|
||||
),
|
||||
};
|
||||
});
|
||||
};
|
||||
},
|
||||
{
|
||||
[ValidatorFields.STAKE]: '0',
|
||||
[ValidatorFields.STAKE_SHARE]: '0',
|
||||
[ValidatorFields.PENDING_STAKE]: '0',
|
||||
[ValidatorFields.NORMALISED_VOTING_POWER]: '0',
|
||||
[ValidatorFields.TOTAL_PENALTIES]: '0',
|
||||
}
|
||||
);
|
||||
|
||||
return [
|
||||
{
|
||||
@@ -322,6 +332,7 @@ export const ConsensusValidatorsTable = ({
|
||||
decimals,
|
||||
hideTopThird,
|
||||
previousEpochData,
|
||||
thirdOfTotalStake,
|
||||
totalStake,
|
||||
validatorsView,
|
||||
]);
|
||||
@@ -352,6 +363,7 @@ export const ConsensusValidatorsTable = ({
|
||||
headerTooltip: t('StakeDescription').toString(),
|
||||
cellRenderer: TotalStakeRenderer,
|
||||
width: 120,
|
||||
sort: 'desc',
|
||||
},
|
||||
{
|
||||
field: ValidatorFields.PENDING_STAKE,
|
||||
@@ -373,7 +385,6 @@ export const ConsensusValidatorsTable = ({
|
||||
headerTooltip: t('NormalisedVotingPowerDescription').toString(),
|
||||
cellRenderer: VotingPowerRenderer,
|
||||
width: 200,
|
||||
sort: 'desc',
|
||||
},
|
||||
{
|
||||
field: ValidatorFields.TOTAL_PENALTIES,
|
||||
|
||||
+9
-9
@@ -57,16 +57,16 @@ export const StandbyPendingValidatorsTable = ({
|
||||
|
||||
return data
|
||||
.sort((a, b) => {
|
||||
const aVotingPower = new BigNumber(a.rankingScore.votingPower);
|
||||
const bVotingPower = new BigNumber(b.rankingScore.votingPower);
|
||||
return bVotingPower.minus(aVotingPower).toNumber();
|
||||
const aStakedTotal = new BigNumber(a.stakedTotal);
|
||||
const bStakedTotal = new BigNumber(b.stakedTotal);
|
||||
return bStakedTotal.minus(aStakedTotal).toNumber();
|
||||
})
|
||||
.map((node, index) => {
|
||||
const votingPowerRanking = index + 1;
|
||||
const stakedTotalRanking = index + 1;
|
||||
|
||||
return {
|
||||
...node,
|
||||
votingPowerRanking,
|
||||
stakedTotalRanking,
|
||||
};
|
||||
})
|
||||
.map(
|
||||
@@ -79,7 +79,7 @@ export const StandbyPendingValidatorsTable = ({
|
||||
stakedTotal,
|
||||
rankingScore: { stakeScore },
|
||||
pendingStake,
|
||||
votingPowerRanking,
|
||||
stakedTotalRanking,
|
||||
stakedByUser,
|
||||
pendingUserStake,
|
||||
userStakeShare,
|
||||
@@ -125,7 +125,7 @@ export const StandbyPendingValidatorsTable = ({
|
||||
|
||||
return {
|
||||
id,
|
||||
[ValidatorFields.RANKING_INDEX]: votingPowerRanking,
|
||||
[ValidatorFields.RANKING_INDEX]: stakedTotalRanking,
|
||||
[ValidatorFields.VALIDATOR]: {
|
||||
avatarUrl,
|
||||
name,
|
||||
@@ -166,7 +166,7 @@ export const StandbyPendingValidatorsTable = ({
|
||||
: undefined,
|
||||
[ValidatorFields.PENDING_USER_STAKE]: pendingUserStake,
|
||||
[ValidatorFields.USER_STAKE_SHARE]: userStakeShare
|
||||
? formatNumberPercentage(new BigNumber(userStakeShare))
|
||||
? formatNumberPercentage(new BigNumber(userStakeShare), 2)
|
||||
: undefined,
|
||||
};
|
||||
}
|
||||
@@ -210,6 +210,7 @@ export const StandbyPendingValidatorsTable = ({
|
||||
headerTooltip: t('StakeDescription').toString(),
|
||||
cellRenderer: TotalStakeRenderer,
|
||||
width: 120,
|
||||
sort: 'desc',
|
||||
},
|
||||
{
|
||||
field: ValidatorFields.PENDING_STAKE,
|
||||
@@ -233,7 +234,6 @@ export const StandbyPendingValidatorsTable = ({
|
||||
}),
|
||||
cellRenderer: StakeNeededForPromotionRenderer,
|
||||
width: 210,
|
||||
sort: 'asc',
|
||||
},
|
||||
{
|
||||
field: ValidatorFields.TOTAL_PENALTIES,
|
||||
|
||||
@@ -2,12 +2,8 @@ import { useMemo, useState } from 'react';
|
||||
import { Trans, useTranslation } from 'react-i18next';
|
||||
import BigNumber from 'bignumber.js';
|
||||
import * as Schema from '@vegaprotocol/types';
|
||||
import {
|
||||
createDocsLinks,
|
||||
removePaginationWrapper,
|
||||
toBigNum,
|
||||
} from '@vegaprotocol/utils';
|
||||
import { useEnvironment } from '@vegaprotocol/environment';
|
||||
import { removePaginationWrapper, toBigNum } from '@vegaprotocol/utils';
|
||||
import { DocsLinks } from '@vegaprotocol/environment';
|
||||
import { Link as UTLink, Toggle } from '@vegaprotocol/ui-toolkit';
|
||||
|
||||
import { formatNumber } from '../../../../lib/format-number';
|
||||
@@ -42,7 +38,6 @@ export const ValidatorTables = ({
|
||||
previousEpochData,
|
||||
}: ValidatorsTableProps) => {
|
||||
const { t } = useTranslation();
|
||||
const { VEGA_DOCS_URL } = useEnvironment();
|
||||
const {
|
||||
appState: { decimals },
|
||||
} = useAppState();
|
||||
@@ -213,12 +208,12 @@ export const ValidatorTables = ({
|
||||
<>
|
||||
<SubHeading title={t('status-pending')} />
|
||||
<p>
|
||||
{VEGA_DOCS_URL && (
|
||||
{DocsLinks && (
|
||||
<>
|
||||
<span>{t('pendingDescription1')} </span>
|
||||
<span>
|
||||
<UTLink
|
||||
href={createDocsLinks(VEGA_DOCS_URL).STAKING_GUIDE}
|
||||
href={DocsLinks.STAKING_GUIDE}
|
||||
target="_blank"
|
||||
data-testid="validator-forum-link"
|
||||
>
|
||||
|
||||
@@ -9,6 +9,7 @@ import { usePreviousEpochQuery } from '../__generated__/PreviousEpoch';
|
||||
import type { ReactElement } from 'react';
|
||||
import type { StakingQuery } from '../__generated__/Staking';
|
||||
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.
|
||||
|
||||
@@ -28,6 +29,7 @@ export const NodeContainer = ({
|
||||
const { t } = useTranslation();
|
||||
const { pubKey } = useVegaWallet();
|
||||
const { delegationsPagination } = ENV;
|
||||
|
||||
const { data, loading, error, refetch } = useStakingQuery({
|
||||
variables: {
|
||||
partyId: pubKey || '',
|
||||
@@ -37,7 +39,9 @@ export const NodeContainer = ({
|
||||
}
|
||||
: undefined,
|
||||
},
|
||||
errorPolicy: 'all',
|
||||
});
|
||||
|
||||
const { data: previousEpochData } = usePreviousEpochQuery({
|
||||
variables: {
|
||||
epochId: (Number(data?.epoch.id) - 1).toString(),
|
||||
@@ -47,7 +51,7 @@ export const NodeContainer = ({
|
||||
|
||||
useRefreshAfterEpoch(data?.epoch.timestamps.expiry, refetch);
|
||||
|
||||
if (error) {
|
||||
if (error && !isPartyNotFoundError(error)) {
|
||||
return (
|
||||
<Callout intent={Intent.Danger} title={t('Something went wrong')}>
|
||||
<pre>
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
import React, { useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useEnvironment } from '@vegaprotocol/environment';
|
||||
import { createDocsLinks, ExternalLinks, toBigNum } from '@vegaprotocol/utils';
|
||||
import {
|
||||
useEnvironment,
|
||||
DocsLinks,
|
||||
ExternalLinks,
|
||||
} from '@vegaprotocol/environment';
|
||||
import { toBigNum } from '@vegaprotocol/utils';
|
||||
import * as Schema from '@vegaprotocol/types';
|
||||
import {
|
||||
Link as UTLink,
|
||||
@@ -64,7 +68,7 @@ export const ValidatorTable = ({
|
||||
stakedTotal,
|
||||
previousEpochData,
|
||||
}: ValidatorTableProps) => {
|
||||
const { ETHERSCAN_URL, VEGA_DOCS_URL } = useEnvironment();
|
||||
const { ETHERSCAN_URL } = useEnvironment();
|
||||
const { t } = useTranslation();
|
||||
const {
|
||||
appState: { decimals },
|
||||
@@ -90,9 +94,9 @@ export const ValidatorTable = ({
|
||||
<>
|
||||
<p className="mb-12">
|
||||
{t('validatorFormIntro')}{' '}
|
||||
{VEGA_DOCS_URL && (
|
||||
{DocsLinks && (
|
||||
<ExternalLink
|
||||
href={createDocsLinks(VEGA_DOCS_URL).STAKING_GUIDE}
|
||||
href={DocsLinks.STAKING_GUIDE}
|
||||
target="_blank"
|
||||
data-testid="validator-table-staking-guide-link"
|
||||
className="text-white"
|
||||
|
||||
@@ -60,9 +60,14 @@ export const getOverstakingPenalty = (
|
||||
}
|
||||
|
||||
return formatNumberPercentage(
|
||||
new BigNumber(1)
|
||||
.minus(new BigNumber(validatorScore).dividedBy(new BigNumber(stakeScore)))
|
||||
.times(100),
|
||||
BigNumber.max(
|
||||
new BigNumber(1)
|
||||
.minus(
|
||||
new BigNumber(validatorScore).dividedBy(new BigNumber(stakeScore))
|
||||
)
|
||||
.times(100),
|
||||
new BigNumber(0)
|
||||
),
|
||||
2
|
||||
);
|
||||
};
|
||||
|
||||
@@ -3,7 +3,8 @@ import { Trans, useTranslation } from 'react-i18next';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
import { Heading, SubHeading } from '../../components/heading';
|
||||
import { ExternalLinks, toBigNum } from '@vegaprotocol/utils';
|
||||
import { ExternalLinks } from '@vegaprotocol/environment';
|
||||
import { toBigNum } from '@vegaprotocol/utils';
|
||||
import { useAppState } from '../../contexts/app-state/app-state-context';
|
||||
import { useDocumentTitle } from '../../hooks/use-document-title';
|
||||
import type { RouteChildProps } from '..';
|
||||
|
||||
@@ -40,9 +40,9 @@
|
||||
line-height: calc(min(var(--ag-line-height, 26px), 26px) - 4px);
|
||||
}
|
||||
|
||||
.vega-ag-grid .ag-row {
|
||||
border-width: 1px 0;
|
||||
border-bottom: 1px solid transparent;
|
||||
.vega-ag-grid .ag-row,
|
||||
.vega-ag-grid .ag-cell {
|
||||
border-width: 0;
|
||||
}
|
||||
|
||||
/* Dark variables */
|
||||
@@ -54,7 +54,6 @@
|
||||
--ag-header-column-separator-color: theme(colors.neutral[600]);
|
||||
--ag-row-border-color: theme(colors.black);
|
||||
--ag-row-hover-color: theme(colors.neutral[800]);
|
||||
--ag-font-size: 12px;
|
||||
}
|
||||
|
||||
.validators-table .ag-theme-balham-dark .ag-body-horizontal-scroll {
|
||||
|
||||
+2
-2
@@ -10,8 +10,8 @@ import {
|
||||
displayChange,
|
||||
} from '@vegaprotocol/liquidity';
|
||||
|
||||
import type { Candle } from '@vegaprotocol/market-list';
|
||||
import { marketCandlesProvider } from '@vegaprotocol/market-list';
|
||||
import type { Candle } from '@vegaprotocol/markets';
|
||||
import { marketCandlesProvider } from '@vegaprotocol/markets';
|
||||
|
||||
const THROTTLE_UPDATE_TIME = 500;
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user