Compare commits
1 Commits
develop
...
fix/5774-f
Author | SHA1 | Date | |
---|---|---|---|
|
dac1ed19a4 |
@ -4,5 +4,6 @@ tmp/*
|
|||||||
.dockerignore
|
.dockerignore
|
||||||
dockerfiles
|
dockerfiles
|
||||||
node_modules
|
node_modules
|
||||||
|
.git
|
||||||
.github
|
.github
|
||||||
.vscode
|
.vscode
|
||||||
|
3
.github/workflows/ci-cd-trigger.yml
vendored
3
.github/workflows/ci-cd-trigger.yml
vendored
@ -196,9 +196,9 @@ jobs:
|
|||||||
cypress:
|
cypress:
|
||||||
needs: [build-sources, check-e2e-needed]
|
needs: [build-sources, check-e2e-needed]
|
||||||
name: '(CI) cypress'
|
name: '(CI) cypress'
|
||||||
|
if: ${{ needs.check-e2e-needed.outputs.run-tests == 'true' }}
|
||||||
uses: ./.github/workflows/cypress-run.yml
|
uses: ./.github/workflows/cypress-run.yml
|
||||||
secrets: inherit
|
secrets: inherit
|
||||||
if: needs.check-e2e-needed.outputs.run-tests == 'true' && (contains(needs.build-sources.outputs.projects, 'governance') || contains(needs.build-sources.outputs.projects, 'explorer'))
|
|
||||||
with:
|
with:
|
||||||
projects: ${{ needs.build-sources.outputs.projects-e2e }}
|
projects: ${{ needs.build-sources.outputs.projects-e2e }}
|
||||||
tags: '@smoke'
|
tags: '@smoke'
|
||||||
@ -287,7 +287,6 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- run: |
|
- run: |
|
||||||
result="${{ needs.cypress.result }}"
|
result="${{ needs.cypress.result }}"
|
||||||
echo "Result: $result"
|
|
||||||
if [[ $result == "success" || $result == "skipped" ]]; then
|
if [[ $result == "success" || $result == "skipped" ]]; then
|
||||||
exit 0
|
exit 0
|
||||||
else
|
else
|
||||||
|
36
.github/workflows/cypress-live-test.yml
vendored
Normal file
36
.github/workflows/cypress-live-test.yml
vendored
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
name: Cypress Console tests -- live environment
|
||||||
|
|
||||||
|
# This workflow runs using provided url
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
url:
|
||||||
|
description: 'Url'
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
cypress-run:
|
||||||
|
name: Run Cypress Trading tests -- live environment
|
||||||
|
runs-on: ubuntu-22.04
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- name: Use Node.js 20
|
||||||
|
id: Node
|
||||||
|
uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version-file: '.nvmrc'
|
||||||
|
|
||||||
|
- name: Run Cypress tests
|
||||||
|
uses: cypress-io/github-action@v4
|
||||||
|
with:
|
||||||
|
browser: chrome
|
||||||
|
record: true
|
||||||
|
project: ./apps/trading-e2e
|
||||||
|
config: baseUrl=${{ github.event.inputs.url }}
|
||||||
|
env: grepTags=@live
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
1
.github/workflows/cypress-manual-trigger.yml
vendored
1
.github/workflows/cypress-manual-trigger.yml
vendored
@ -12,6 +12,7 @@ on:
|
|||||||
options:
|
options:
|
||||||
- explorer-e2e
|
- explorer-e2e
|
||||||
- governance-e2e
|
- governance-e2e
|
||||||
|
- trading-e2e
|
||||||
tags:
|
tags:
|
||||||
description: 'Test tags to run'
|
description: 'Test tags to run'
|
||||||
required: true
|
required: true
|
||||||
|
2
.github/workflows/cypress-nightly.yml
vendored
2
.github/workflows/cypress-nightly.yml
vendored
@ -10,5 +10,5 @@ jobs:
|
|||||||
uses: ./.github/workflows/cypress-run.yml
|
uses: ./.github/workflows/cypress-run.yml
|
||||||
secrets: inherit
|
secrets: inherit
|
||||||
with:
|
with:
|
||||||
projects: '["explorer-e2e","governance-e2e"]'
|
projects: '["explorer-e2e","governance-e2e","trading-e2e"]'
|
||||||
tags: '@smoke @regression @slow'
|
tags: '@smoke @regression @slow'
|
||||||
|
4
.github/workflows/lint-pr.yml
vendored
4
.github/workflows/lint-pr.yml
vendored
@ -25,7 +25,7 @@ jobs:
|
|||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: |
|
run: |
|
||||||
rm package.json
|
rm package.json
|
||||||
npm install --no-save @commitlint/cli@16.3.0 @commitlint/config-conventional@18.6.1 @commitlint/config-nx-scopes@18.6.1 nx@17.1.2
|
npm install --no-save @commitlint/cli @commitlint/config-conventional @commitlint/config-nx-scopes nx
|
||||||
|
|
||||||
- name: Check PR title
|
- name: Check PR title
|
||||||
run: echo "${{ github.event.pull_request.title }}" | npx @commitlint/cli@16.3.0 --config ./commitlint.config-ci.js
|
run: echo "${{ github.event.pull_request.title }}" | npx commitlint --config ./commitlint.config-ci.js
|
||||||
|
@ -1,28 +0,0 @@
|
|||||||
# path to a directory with all packages
|
|
||||||
storage: ../tmp/local-registry/storage
|
|
||||||
|
|
||||||
# a list of other known repositories we can talk to
|
|
||||||
uplinks:
|
|
||||||
npmjs:
|
|
||||||
url: https://registry.yarnpkg.com
|
|
||||||
maxage: 60m
|
|
||||||
|
|
||||||
packages:
|
|
||||||
'**':
|
|
||||||
# give all users (including non-authenticated users) full access
|
|
||||||
# because it is a local registry
|
|
||||||
access: $all
|
|
||||||
publish: $all
|
|
||||||
unpublish: $all
|
|
||||||
|
|
||||||
# if package is not available locally, proxy requests to npm registry
|
|
||||||
proxy: npmjs
|
|
||||||
|
|
||||||
# log settings
|
|
||||||
logs:
|
|
||||||
type: stdout
|
|
||||||
format: pretty
|
|
||||||
level: warn
|
|
||||||
|
|
||||||
publish:
|
|
||||||
allow_offline: true # set offline to true to allow publish offline
|
|
@ -1,7 +1,7 @@
|
|||||||
import { getNewAssetTxBody } from '../support/governance.functions';
|
import { getNewAssetTxBody } from '../support/governance.functions';
|
||||||
|
|
||||||
context('Proposal page', { tags: '@smoke' }, function () {
|
context('Proposal page', { tags: '@smoke' }, function () {
|
||||||
describe.skip('Verify elements on page', function () {
|
describe('Verify elements on page', function () {
|
||||||
const proposalHeading = 'proposals-heading';
|
const proposalHeading = 'proposals-heading';
|
||||||
const dateTimeRegex =
|
const dateTimeRegex =
|
||||||
/(\d{1,2})\/(\d{1,2})\/(\d{4}), (\d{1,2}):(\d{1,2}):(\d{1,2})/gm;
|
/(\d{1,2})\/(\d{1,2})\/(\d{4}), (\d{1,2}):(\d{1,2}):(\d{1,2})/gm;
|
||||||
|
@ -1,9 +1,7 @@
|
|||||||
const { join } = require('path');
|
const { join } = require('path');
|
||||||
const { createGlobPatternsForDependencies } = require('@nx/react/tailwind');
|
const { createGlobPatternsForDependencies } = require('@nx/react/tailwind');
|
||||||
const { theme } = require('../../libs/tailwindcss-config/src/theme');
|
const theme = require('../../libs/tailwindcss-config/src/theme');
|
||||||
const {
|
const vegaCustomClasses = require('../../libs/tailwindcss-config/src/vega-custom-classes');
|
||||||
vegaCustomClasses,
|
|
||||||
} = require('../../libs/tailwindcss-config/src/vega-custom-classes');
|
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
content: [
|
content: [
|
||||||
|
@ -7,7 +7,6 @@ import {
|
|||||||
navigateTo,
|
navigateTo,
|
||||||
navigation,
|
navigation,
|
||||||
turnTelemetryOff,
|
turnTelemetryOff,
|
||||||
setRiskAccepted,
|
|
||||||
} from '../../support/common.functions';
|
} from '../../support/common.functions';
|
||||||
import {
|
import {
|
||||||
clickOnValidatorFromList,
|
clickOnValidatorFromList,
|
||||||
@ -58,7 +57,6 @@ context(
|
|||||||
// 1002-STKE-002, 1002-STKE-032
|
// 1002-STKE-002, 1002-STKE-032
|
||||||
before('visit staking tab and connect vega wallet', function () {
|
before('visit staking tab and connect vega wallet', function () {
|
||||||
cy.visit('/');
|
cy.visit('/');
|
||||||
setRiskAccepted();
|
|
||||||
ethereumWalletConnect();
|
ethereumWalletConnect();
|
||||||
cy.connectVegaWallet();
|
cy.connectVegaWallet();
|
||||||
vegaWalletSetSpecifiedApprovalAmount('1000');
|
vegaWalletSetSpecifiedApprovalAmount('1000');
|
||||||
|
@ -5,7 +5,6 @@ import {
|
|||||||
navigateTo,
|
navigateTo,
|
||||||
navigation,
|
navigation,
|
||||||
turnTelemetryOff,
|
turnTelemetryOff,
|
||||||
setRiskAccepted,
|
|
||||||
} from '../../support/common.functions';
|
} from '../../support/common.functions';
|
||||||
import {
|
import {
|
||||||
stakingPageAssociateTokens,
|
stakingPageAssociateTokens,
|
||||||
@ -58,7 +57,6 @@ context(
|
|||||||
function () {
|
function () {
|
||||||
cy.clearLocalStorage();
|
cy.clearLocalStorage();
|
||||||
turnTelemetryOff();
|
turnTelemetryOff();
|
||||||
setRiskAccepted();
|
|
||||||
cy.mockChainId();
|
cy.mockChainId();
|
||||||
cy.reload();
|
cy.reload();
|
||||||
waitForSpinner();
|
waitForSpinner();
|
||||||
|
@ -7,7 +7,7 @@ context('Home Page - verify elements on page', { tags: '@smoke' }, function () {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('Links and buttons', function () {
|
describe('Links and buttons', function () {
|
||||||
it('should have link for proposal page', function () {
|
it.skip('should have link for proposal page', function () {
|
||||||
cy.getByTestId('home-proposals').within(() => {
|
cy.getByTestId('home-proposals').within(() => {
|
||||||
cy.get('[href="/proposals"]')
|
cy.get('[href="/proposals"]')
|
||||||
.should('exist')
|
.should('exist')
|
||||||
@ -51,7 +51,7 @@ context('Home Page - verify elements on page', { tags: '@smoke' }, function () {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should have external link for governance', function () {
|
it.skip('should have external link for governance', function () {
|
||||||
cy.getByTestId('home-proposals').within(() => {
|
cy.getByTestId('home-proposals').within(() => {
|
||||||
cy.getByTestId('external-link')
|
cy.getByTestId('external-link')
|
||||||
.should('have.attr', 'href')
|
.should('have.attr', 'href')
|
||||||
@ -59,7 +59,7 @@ context('Home Page - verify elements on page', { tags: '@smoke' }, function () {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should have link for validator page', function () {
|
it.skip('should have link for validator page', function () {
|
||||||
cy.getByTestId('home-validators').within(() => {
|
cy.getByTestId('home-validators').within(() => {
|
||||||
cy.get('[href="/validators"]')
|
cy.get('[href="/validators"]')
|
||||||
.first()
|
.first()
|
||||||
@ -68,7 +68,7 @@ context('Home Page - verify elements on page', { tags: '@smoke' }, function () {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should have external link for validators', function () {
|
it.skip('should have external link for validators', function () {
|
||||||
cy.getByTestId('home-validators').within(() => {
|
cy.getByTestId('home-validators').within(() => {
|
||||||
cy.getByTestId('external-link')
|
cy.getByTestId('external-link')
|
||||||
.should('have.attr', 'href')
|
.should('have.attr', 'href')
|
||||||
@ -101,7 +101,7 @@ context('Home Page - verify elements on page', { tags: '@smoke' }, function () {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should have link for rewards page', function () {
|
it.skip('should have link for rewards page', function () {
|
||||||
cy.getByTestId('home-rewards').within(() => {
|
cy.getByTestId('home-rewards').within(() => {
|
||||||
cy.get('[href="/rewards"]')
|
cy.get('[href="/rewards"]')
|
||||||
.first()
|
.first()
|
||||||
@ -110,7 +110,7 @@ context('Home Page - verify elements on page', { tags: '@smoke' }, function () {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should have link for withdrawal page', function () {
|
it.skip('should have link for withdrawal page', function () {
|
||||||
cy.getByTestId('home-vega-token').within(() => {
|
cy.getByTestId('home-vega-token').within(() => {
|
||||||
cy.get('[href="/token/withdraw"]')
|
cy.get('[href="/token/withdraw"]')
|
||||||
.first()
|
.first()
|
||||||
@ -132,7 +132,7 @@ context('Home Page - verify elements on page', { tags: '@smoke' }, function () {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// 0006-NETW-003 0006-NETW-008 0006-NETW-009 0006-NETW-010 0006-NETW-012 0006-NETW-013 0006-NETW-017 0006-NETW-018 0006-NETW-019 0006-NETW-020
|
// 0006-NETW-003 0006-NETW-008 0006-NETW-009 0006-NETW-010 0006-NETW-012 0006-NETW-013 0006-NETW-017 0006-NETW-018 0006-NETW-019 0006-NETW-020
|
||||||
it('should have option to switch to different network node', function () {
|
it.skip('should have option to switch to different network node', function () {
|
||||||
cy.getByTestId('git-network-data').within(() => {
|
cy.getByTestId('git-network-data').within(() => {
|
||||||
cy.getByTestId('link').click();
|
cy.getByTestId('link').click();
|
||||||
});
|
});
|
||||||
@ -153,13 +153,13 @@ context('Home Page - verify elements on page', { tags: '@smoke' }, function () {
|
|||||||
.invoke('text')
|
.invoke('text')
|
||||||
.should('not.eq', currentBlockHeight);
|
.should('not.eq', currentBlockHeight);
|
||||||
});
|
});
|
||||||
cy.getByTestId('subscription-cell').should('be.be.visible');
|
cy.getByTestId('subscription-cell').should('have.text', 'Yes');
|
||||||
});
|
});
|
||||||
cy.getByTestId('connect').should('be.disabled');
|
cy.getByTestId('connect').should('be.disabled');
|
||||||
cy.getByTestId('node-url-custom').click({ force: true });
|
cy.getByTestId('node-url-custom').click({ force: true });
|
||||||
cy.get('input').should('exist');
|
cy.get('input').should('exist');
|
||||||
cy.getByTestId('connect').should('be.disabled');
|
cy.getByTestId('connect').should('be.disabled');
|
||||||
cy.getByTestId('dialog-close').click();
|
cy.getByTestId('icon-cross').click();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should display eth data', function () {
|
it('should display eth data', function () {
|
||||||
|
@ -33,12 +33,12 @@ context(
|
|||||||
verifyTabHighlighted(navigation.proposals);
|
verifyTabHighlighted(navigation.proposals);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should have GOVERNANCE header visible', function () {
|
it.skip('should have GOVERNANCE header visible', function () {
|
||||||
verifyPageHeader('Proposals');
|
verifyPageHeader('Proposals');
|
||||||
});
|
});
|
||||||
|
|
||||||
// 3002-PROP-023 3004-PMAC-002 3005-PASN-002 3006-PASC-002 3007-PNEC-002 3008-PFRO-003
|
// 3002-PROP-023 3004-PMAC-002 3005-PASN-002 3006-PASC-002 3007-PNEC-002 3008-PFRO-003
|
||||||
it('new proposal page should have button for link to more information on proposals', function () {
|
it.skip('new proposal page should have button for link to more information on proposals', function () {
|
||||||
cy.getByTestId('new-proposal-link').click();
|
cy.getByTestId('new-proposal-link').click();
|
||||||
cy.url().should('include', '/proposals/propose/raw');
|
cy.url().should('include', '/proposals/propose/raw');
|
||||||
cy.contains('To see Explorer data on proposals visit').within(() => {
|
cy.contains('To see Explorer data on proposals visit').within(() => {
|
||||||
@ -73,7 +73,7 @@ context(
|
|||||||
navigateTo(navigation.proposals);
|
navigateTo(navigation.proposals);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should be able to see a working link for - find out more about Vega governance', function () {
|
it.skip('should be able to see a working link for - find out more about Vega governance', function () {
|
||||||
// 3001-VOTE-001 // 3002-PROP-001
|
// 3001-VOTE-001 // 3002-PROP-001
|
||||||
cy.getByTestId(proposalDocumentationLink)
|
cy.getByTestId(proposalDocumentationLink)
|
||||||
.should('be.visible')
|
.should('be.visible')
|
||||||
|
@ -34,10 +34,16 @@ context('View functionality with public key', { tags: '@smoke' }, function () {
|
|||||||
cy.connectPublicKey(vegaWalletPubKey);
|
cy.connectPublicKey(vegaWalletPubKey);
|
||||||
});
|
});
|
||||||
|
|
||||||
it.skip('Able to connect public key via wallet and view assets in wallet', function () {
|
it('Able to connect public key using url', function () {
|
||||||
|
cy.getByTestId('exit-view').click();
|
||||||
|
cy.visit(`/?address=${vegaWalletPubKey}`);
|
||||||
|
verifyConnectedToPubKey();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Able to connect public key via wallet and view assets in wallet', function () {
|
||||||
verifyConnectedToPubKey();
|
verifyConnectedToPubKey();
|
||||||
cy.getByTestId('currency-title', { timeout: 10000 })
|
cy.getByTestId('currency-title', { timeout: 10000 })
|
||||||
.should('have.length.at.least', 2)
|
.should('have.length.at.least', 4)
|
||||||
.and('contain.text', 'USDC (fake)');
|
.and('contain.text', 'USDC (fake)');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -3,7 +3,6 @@
|
|||||||
import { aliasGQLQuery } from '@vegaprotocol/cypress';
|
import { aliasGQLQuery } from '@vegaprotocol/cypress';
|
||||||
import {
|
import {
|
||||||
navigation,
|
navigation,
|
||||||
setRiskAccepted,
|
|
||||||
verifyPageHeader,
|
verifyPageHeader,
|
||||||
verifyTabHighlighted,
|
verifyTabHighlighted,
|
||||||
} from '../../support/common.functions';
|
} from '../../support/common.functions';
|
||||||
@ -47,11 +46,11 @@ context('Validators Page - verify elements on page', function () {
|
|||||||
|
|
||||||
// @ts-ignore clash between jest and cypress
|
// @ts-ignore clash between jest and cypress
|
||||||
describe('with wallets disconnected', { tags: '@smoke' }, function () {
|
describe('with wallets disconnected', { tags: '@smoke' }, function () {
|
||||||
it('Should have validators tab highlighted', function () {
|
it.skip('Should have validators tab highlighted', function () {
|
||||||
verifyTabHighlighted(navigation.validators);
|
verifyTabHighlighted(navigation.validators);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Should have validators ON VEGA header visible', function () {
|
it.skip('Should have validators ON VEGA header visible', function () {
|
||||||
verifyPageHeader('Validators');
|
verifyPageHeader('Validators');
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -188,13 +187,12 @@ context('Validators Page - verify elements on page', function () {
|
|||||||
before('connect wallets and click on validator', function () {
|
before('connect wallets and click on validator', function () {
|
||||||
cy.mockChainId();
|
cy.mockChainId();
|
||||||
cy.visit('/validators');
|
cy.visit('/validators');
|
||||||
setRiskAccepted();
|
|
||||||
cy.connectVegaWallet();
|
cy.connectVegaWallet();
|
||||||
clickOnValidatorFromList(0);
|
clickOnValidatorFromList(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
// 1002-STKE-006
|
// 1002-STKE-006
|
||||||
it('Should be able to see validator name', function () {
|
it.skip('Should be able to see validator name', function () {
|
||||||
cy.getByTestId(validatorTitle).should('not.be.empty');
|
cy.getByTestId(validatorTitle).should('not.be.empty');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1,8 +1,5 @@
|
|||||||
import { truncateByChars } from '@vegaprotocol/utils';
|
import { truncateByChars } from '@vegaprotocol/utils';
|
||||||
import {
|
import { waitForSpinner } from '../../support/common.functions';
|
||||||
setRiskAccepted,
|
|
||||||
waitForSpinner,
|
|
||||||
} from '../../support/common.functions';
|
|
||||||
import {
|
import {
|
||||||
vegaWalletFaucetAssetsWithoutCheck,
|
vegaWalletFaucetAssetsWithoutCheck,
|
||||||
vegaWalletTeardown,
|
vegaWalletTeardown,
|
||||||
@ -14,6 +11,7 @@ const connectButton = 'connect-vega-wallet';
|
|||||||
const getVegaLink = 'link';
|
const getVegaLink = 'link';
|
||||||
const dialog = '[role="dialog"]:visible';
|
const dialog = '[role="dialog"]:visible';
|
||||||
const dialogHeader = 'dialog-title';
|
const dialogHeader = 'dialog-title';
|
||||||
|
const walletDialogHeader = 'wallet-dialog-title';
|
||||||
const connectorsList = 'connectors-list';
|
const connectorsList = 'connectors-list';
|
||||||
const dialogCloseBtn = 'dialog-close';
|
const dialogCloseBtn = 'dialog-close';
|
||||||
const accountNo = 'vega-account-truncated';
|
const accountNo = 'vega-account-truncated';
|
||||||
@ -36,7 +34,6 @@ context(
|
|||||||
() => {
|
() => {
|
||||||
before('visit token home page', () => {
|
before('visit token home page', () => {
|
||||||
cy.visit('/');
|
cy.visit('/');
|
||||||
setRiskAccepted();
|
|
||||||
cy.get(walletContainer, { timeout: 60000 }).should('be.visible');
|
cy.get(walletContainer, { timeout: 60000 }).should('be.visible');
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -66,12 +63,17 @@ context(
|
|||||||
|
|
||||||
it('should have Connect Vega header visible', () => {
|
it('should have Connect Vega header visible', () => {
|
||||||
cy.get(dialog).within(() => {
|
cy.get(dialog).within(() => {
|
||||||
cy.getByTestId(connectorsList)
|
cy.getByTestId(walletDialogHeader)
|
||||||
.should('be.visible')
|
.should('be.visible')
|
||||||
.and(
|
.and('have.text', 'Get a Vega wallet');
|
||||||
'have.text',
|
});
|
||||||
'Get the Vega WalletGet MetaMask>_Command Line WalletView as public key'
|
});
|
||||||
);
|
|
||||||
|
it('should have jsonRpc and hosted connection options visible on list', function () {
|
||||||
|
cy.getByTestId(connectorsList).within(() => {
|
||||||
|
cy.getByTestId('connector-jsonRpc')
|
||||||
|
.should('be.visible')
|
||||||
|
.and('have.text', 'Use the Desktop App/CLI');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -86,6 +88,7 @@ context(
|
|||||||
before('connect vega wallet', function () {
|
before('connect vega wallet', function () {
|
||||||
cy.mockChainId();
|
cy.mockChainId();
|
||||||
cy.visit('/');
|
cy.visit('/');
|
||||||
|
cy.wait('@ChainId');
|
||||||
cy.connectVegaWallet();
|
cy.connectVegaWallet();
|
||||||
vegaWalletTeardown();
|
vegaWalletTeardown();
|
||||||
});
|
});
|
||||||
|
@ -102,12 +102,6 @@ export function turnTelemetryOff() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function setRiskAccepted() {
|
|
||||||
cy.window().then((win) =>
|
|
||||||
win.localStorage.setItem('vega_wallet_risk_accepted', 'true')
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function dissociateFromSecondWalletKey() {
|
export function dissociateFromSecondWalletKey() {
|
||||||
const secondWalletKey = Cypress.env('vegaWalletPublicKey2Short');
|
const secondWalletKey = Cypress.env('vegaWalletPublicKey2Short');
|
||||||
cy.getByTestId('vega-in-wallet')
|
cy.getByTestId('vega-in-wallet')
|
||||||
|
@ -14,6 +14,7 @@ NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/m
|
|||||||
|
|
||||||
NX_HOSTED_WALLET_URL=https://wallet.testnet.vega.xyz
|
NX_HOSTED_WALLET_URL=https://wallet.testnet.vega.xyz
|
||||||
NX_VEGA_DOCS_URL=https://docs.vega.xyz/testnet
|
NX_VEGA_DOCS_URL=https://docs.vega.xyz/testnet
|
||||||
|
NX_DELEGATIONS_PAGINATION=50
|
||||||
NX_TRANCHES_SERVICE_URL=https://tranches-stagnet1-k8s.ops.vega.xyz
|
NX_TRANCHES_SERVICE_URL=https://tranches-stagnet1-k8s.ops.vega.xyz
|
||||||
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/main/announcements.json
|
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/main/announcements.json
|
||||||
NX_WALLETCONNECT_PROJECT_ID=fe8091dc35738863e509fc4947525c72
|
NX_WALLETCONNECT_PROJECT_ID=fe8091dc35738863e509fc4947525c72
|
||||||
@ -23,7 +24,7 @@ NX_TENDERMINT_URL=https://tm.n01.stagnet1.vega.rocks
|
|||||||
NX_TENDERMINT_WEBSOCKET_URL=wss://tm.n01.stagnet1.vega.xyz/websocket
|
NX_TENDERMINT_WEBSOCKET_URL=wss://tm.n01.stagnet1.vega.xyz/websocket
|
||||||
|
|
||||||
NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn
|
NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn
|
||||||
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/en-GB/firefox/addon/vega-wallet-beta/
|
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/firefox/addon/vega-wallet-fairground
|
||||||
|
|
||||||
#Test configuration variables
|
#Test configuration variables
|
||||||
CYPRESS_FAIRGROUND=false
|
CYPRESS_FAIRGROUND=false
|
||||||
@ -32,7 +33,7 @@ LC_ALL="en_US.UTF-8"
|
|||||||
# Cosmic elevator flags
|
# Cosmic elevator flags
|
||||||
NX_SUCCESSOR_MARKETS=true
|
NX_SUCCESSOR_MARKETS=true
|
||||||
NX_METAMASK_SNAPS=true
|
NX_METAMASK_SNAPS=true
|
||||||
NX_PRODUCT_PERPETUALS=true
|
NX_PRODUCT_PERPETUALS=false
|
||||||
NX_UPDATE_MARKET_STATE=true
|
NX_UPDATE_MARKET_STATE=false
|
||||||
NX_REFERRALS=true
|
NX_REFERRALS=true
|
||||||
NX_GOVERNANCE_TRANSFERS=true
|
NX_GOVERNANCE_TRANSFERS=false
|
||||||
|
@ -15,11 +15,12 @@ NX_ETH_WALLET_MNEMONIC=ozone access unlock valid olympic save include omit suppl
|
|||||||
NX_LOCAL_PROVIDER_URL=http://localhost:8545/
|
NX_LOCAL_PROVIDER_URL=http://localhost:8545/
|
||||||
NX_VEGA_WALLET_URL=http://localhost:1789
|
NX_VEGA_WALLET_URL=http://localhost:1789
|
||||||
NX_VEGA_DOCS_URL=https://docs.vega.xyz/testnet
|
NX_VEGA_DOCS_URL=https://docs.vega.xyz/testnet
|
||||||
|
NX_DELEGATIONS_PAGINATION=50
|
||||||
NX_TRANCHES_SERVICE_URL=https://tranches-stagnet1-k8s.ops.vega.xyz
|
NX_TRANCHES_SERVICE_URL=https://tranches-stagnet1-k8s.ops.vega.xyz
|
||||||
NX_VEGA_REST_URL=http://localhost:3008/api/v2/
|
NX_VEGA_REST_URL=http://localhost:3008/api/v2/
|
||||||
|
|
||||||
NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn
|
NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn
|
||||||
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/en-GB/firefox/addon/vega-wallet-beta/
|
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/firefox/addon/vega-wallet-fairground
|
||||||
|
|
||||||
NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json
|
NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json
|
||||||
|
|
||||||
|
@ -8,13 +8,14 @@ NX_ETHERSCAN_URL=https://sepolia.etherscan.io
|
|||||||
NX_GITHUB_FEEDBACK_URL=https://github.com/vegaprotocol/feedback/discussions
|
NX_GITHUB_FEEDBACK_URL=https://github.com/vegaprotocol/feedback/discussions
|
||||||
NX_VEGA_EXPLORER_URL=#
|
NX_VEGA_EXPLORER_URL=#
|
||||||
NX_VEGA_DOCS_URL=https://docs.vega.xyz/testnet
|
NX_VEGA_DOCS_URL=https://docs.vega.xyz/testnet
|
||||||
|
NX_DELEGATIONS_PAGINATION=50
|
||||||
NX_TRANCHES_SERVICE_URL=https://tranches-devnet1-k8s.ops.vega.xyz
|
NX_TRANCHES_SERVICE_URL=https://tranches-devnet1-k8s.ops.vega.xyz
|
||||||
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json
|
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json
|
||||||
NX_VEGA_REST_URL=https://api.n00.devnet1.vega.xyz/api/v2/
|
NX_VEGA_REST_URL=https://api.n00.devnet1.vega.xyz/api/v2/
|
||||||
NX_SENTRY_DSN=https://4b8c8a8ba07742648aa4dfe1b8d17e40@o286262.ingest.sentry.io/5882996
|
NX_SENTRY_DSN=https://4b8c8a8ba07742648aa4dfe1b8d17e40@o286262.ingest.sentry.io/5882996
|
||||||
|
|
||||||
NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn
|
NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn
|
||||||
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/en-GB/firefox/addon/vega-wallet-beta/
|
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/firefox/addon/vega-wallet-fairground
|
||||||
|
|
||||||
NX_TENDERMINT_URL=https://tm.be.devnet1.vega.xyz/
|
NX_TENDERMINT_URL=https://tm.be.devnet1.vega.xyz/
|
||||||
NX_TENDERMINT_WEBSOCKET_URL=wss://be.devnet1.vega.xyz/websocket
|
NX_TENDERMINT_WEBSOCKET_URL=wss://be.devnet1.vega.xyz/websocket
|
||||||
|
@ -10,6 +10,7 @@ NX_SENTRY_DSN=https://4b8c8a8ba07742648aa4dfe1b8d17e40@o286262.ingest.sentry.io/
|
|||||||
NX_VEGA_EXPLORER_URL=https://explorer.vega.xyz
|
NX_VEGA_EXPLORER_URL=https://explorer.vega.xyz
|
||||||
NX_VEGA_DOCS_URL=https://docs.vega.xyz/mainnet
|
NX_VEGA_DOCS_URL=https://docs.vega.xyz/mainnet
|
||||||
NX_SENTRY_DSN=https://4b8c8a8ba07742648aa4dfe1b8d17e40:87edc2605e544f888305d7fc4a9141bd@o286262.ingest.sentry.io/5882996
|
NX_SENTRY_DSN=https://4b8c8a8ba07742648aa4dfe1b8d17e40:87edc2605e544f888305d7fc4a9141bd@o286262.ingest.sentry.io/5882996
|
||||||
|
NX_DELEGATIONS_PAGINATION=50
|
||||||
NX_TRANCHES_SERVICE_URL=https://tranches-mainnet-k8s.ops.vega.xyz
|
NX_TRANCHES_SERVICE_URL=https://tranches-mainnet-k8s.ops.vega.xyz
|
||||||
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/mainnet/announcements.json
|
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/mainnet/announcements.json
|
||||||
NX_VEGA_REST_URL=https://api.vega.community/api/v2/
|
NX_VEGA_REST_URL=https://api.vega.community/api/v2/
|
||||||
|
@ -9,6 +9,7 @@ NX_GITHUB_FEEDBACK_URL=https://github.com/vegaprotocol/feedback/discussions
|
|||||||
NX_SENTRY_DSN=https://4b8c8a8ba07742648aa4dfe1b8d17e40@o286262.ingest.sentry.io/5882996
|
NX_SENTRY_DSN=https://4b8c8a8ba07742648aa4dfe1b8d17e40@o286262.ingest.sentry.io/5882996
|
||||||
NX_VEGA_EXPLORER_URL=https://explorer.mainnet-mirror.vega.rocks
|
NX_VEGA_EXPLORER_URL=https://explorer.mainnet-mirror.vega.rocks
|
||||||
NX_VEGA_DOCS_URL=https://docs.vega.xyz/mainnet
|
NX_VEGA_DOCS_URL=https://docs.vega.xyz/mainnet
|
||||||
|
NX_DELEGATIONS_PAGINATION=50
|
||||||
NX_TRANCHES_SERVICE_URL=https://tranches-mainnet-mirror-k8s.ops.vega.xyz
|
NX_TRANCHES_SERVICE_URL=https://tranches-mainnet-mirror-k8s.ops.vega.xyz
|
||||||
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/mainnet/announcements.json
|
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/mainnet/announcements.json
|
||||||
NX_VEGA_REST_URL=https://api.mainnet-mirror.vega.rocks/api/v2/
|
NX_VEGA_REST_URL=https://api.mainnet-mirror.vega.rocks/api/v2/
|
||||||
|
@ -5,6 +5,7 @@ NX_VEGA_NETWORKS='{"DEVNET":"https://dev.governance.vega.xyz","STAGNET1":"https:
|
|||||||
NX_VEGA_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/networks-internal/main/stagnet1/vegawallet-stagnet1.toml
|
NX_VEGA_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/networks-internal/main/stagnet1/vegawallet-stagnet1.toml
|
||||||
NX_VEGA_EXPLORER_URL=https://explorer.stagnet1.vega.rocks
|
NX_VEGA_EXPLORER_URL=https://explorer.stagnet1.vega.rocks
|
||||||
NX_VEGA_DOCS_URL=https://docs.vega.xyz/testnet
|
NX_VEGA_DOCS_URL=https://docs.vega.xyz/testnet
|
||||||
|
NX_DELEGATIONS_PAGINATION=50
|
||||||
NX_TRANCHES_SERVICE_URL=https://tranches-stagnet1-k8s.ops.vega.xyz
|
NX_TRANCHES_SERVICE_URL=https://tranches-stagnet1-k8s.ops.vega.xyz
|
||||||
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json
|
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json
|
||||||
NX_VEGA_REST_URL=https://api.n00.stagnet1.vega.xyz/api/v2/
|
NX_VEGA_REST_URL=https://api.n00.stagnet1.vega.xyz/api/v2/
|
||||||
@ -12,7 +13,7 @@ NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/m
|
|||||||
NX_WALLETCONNECT_PROJECT_ID=fe8091dc35738863e509fc4947525c72
|
NX_WALLETCONNECT_PROJECT_ID=fe8091dc35738863e509fc4947525c72
|
||||||
|
|
||||||
NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn
|
NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn
|
||||||
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/en-GB/firefox/addon/vega-wallet-beta/
|
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/firefox/addon/vega-wallet-fairground
|
||||||
|
|
||||||
NX_TENDERMINT_URL=https://tm.n01.stagnet1.vega.rocks
|
NX_TENDERMINT_URL=https://tm.n01.stagnet1.vega.rocks
|
||||||
NX_TENDERMINT_WEBSOCKET_URL=wss://tm.n01.stagnet1.vega.xyz/websocket
|
NX_TENDERMINT_WEBSOCKET_URL=wss://tm.n01.stagnet1.vega.xyz/websocket
|
||||||
|
@ -9,6 +9,7 @@ NX_GITHUB_FEEDBACK_URL=https://github.com/vegaprotocol/feedback/discussions
|
|||||||
NX_VEGA_EXPLORER_URL=https://explorer.fairground.wtf
|
NX_VEGA_EXPLORER_URL=https://explorer.fairground.wtf
|
||||||
NX_VEGA_DOCS_URL=https://docs.vega.xyz/testnet
|
NX_VEGA_DOCS_URL=https://docs.vega.xyz/testnet
|
||||||
NX_HOSTED_WALLET_URL=https://wallet.testnet.vega.xyz
|
NX_HOSTED_WALLET_URL=https://wallet.testnet.vega.xyz
|
||||||
|
NX_DELEGATIONS_PAGINATION=50
|
||||||
NX_TRANCHES_SERVICE_URL=https://tranches-testnet-k8s.ops.vega.xyz
|
NX_TRANCHES_SERVICE_URL=https://tranches-testnet-k8s.ops.vega.xyz
|
||||||
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json
|
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json
|
||||||
NX_VEGA_REST_URL=https://api.n07.testnet.vega.xyz/api/v2/
|
NX_VEGA_REST_URL=https://api.n07.testnet.vega.xyz/api/v2/
|
||||||
@ -17,7 +18,7 @@ NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/m
|
|||||||
NX_WALLETCONNECT_PROJECT_ID=fe8091dc35738863e509fc4947525c72
|
NX_WALLETCONNECT_PROJECT_ID=fe8091dc35738863e509fc4947525c72
|
||||||
|
|
||||||
NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn
|
NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn
|
||||||
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/en-GB/firefox/addon/vega-wallet-beta/
|
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/firefox/addon/vega-wallet-fairground
|
||||||
|
|
||||||
NX_TENDERMINT_URL=https://tm.be.testnet.vega.xyz
|
NX_TENDERMINT_URL=https://tm.be.testnet.vega.xyz
|
||||||
NX_TENDERMINT_WEBSOCKET_URL=wss://be.testnet.vega.xyz/websocket
|
NX_TENDERMINT_WEBSOCKET_URL=wss://be.testnet.vega.xyz/websocket
|
||||||
|
@ -14,7 +14,7 @@ NX_SENTRY_DSN=https://4b8c8a8ba07742648aa4dfe1b8d17e40@o286262.ingest.sentry.io/
|
|||||||
NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json
|
NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json
|
||||||
|
|
||||||
NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn
|
NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn
|
||||||
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/en-GB/firefox/addon/vega-wallet-beta/
|
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/firefox/addon/vega-wallet-fairground
|
||||||
|
|
||||||
NX_TENDERMINT_URL=https://tm.be.validators-testnet.vega.rocks
|
NX_TENDERMINT_URL=https://tm.be.validators-testnet.vega.rocks
|
||||||
NX_TENDERMINT_WEBSOCKET_URL=wss://be.validators-testnet.vega.
|
NX_TENDERMINT_WEBSOCKET_URL=wss://be.validators-testnet.vega.
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import * as Sentry from '@sentry/react';
|
import * as Sentry from '@sentry/react';
|
||||||
import { toBigNum } from '@vegaprotocol/utils';
|
import { toBigNum } from '@vegaprotocol/utils';
|
||||||
import { Splash } from '@vegaprotocol/ui-toolkit';
|
import { Splash } from '@vegaprotocol/ui-toolkit';
|
||||||
import { useVegaWallet, useEagerConnect } from '@vegaprotocol/wallet-react';
|
import { useVegaWallet, useEagerConnect } from '@vegaprotocol/wallet';
|
||||||
import { useFeatureFlags, useEnvironment } from '@vegaprotocol/environment';
|
import { useFeatureFlags, useEnvironment } from '@vegaprotocol/environment';
|
||||||
import { useWeb3React } from '@web3-react/core';
|
import { useWeb3React } from '@web3-react/core';
|
||||||
import React, { Suspense } from 'react';
|
import React, { Suspense } from 'react';
|
||||||
@ -15,6 +15,20 @@ import {
|
|||||||
} from './contexts/app-state/app-state-context';
|
} from './contexts/app-state/app-state-context';
|
||||||
import { useContracts } from './contexts/contracts/contracts-context';
|
import { useContracts } from './contexts/contracts/contracts-context';
|
||||||
import { useRefreshAssociatedBalances } from './hooks/use-refresh-associated-balances';
|
import { useRefreshAssociatedBalances } from './hooks/use-refresh-associated-balances';
|
||||||
|
import { useConnectors } from './lib/vega-connectors';
|
||||||
|
import { useSearchParams } from 'react-router-dom';
|
||||||
|
|
||||||
|
const useVegaWalletEagerConnect = () => {
|
||||||
|
const connectors = useConnectors();
|
||||||
|
const vegaConnecting = useEagerConnect(connectors);
|
||||||
|
const { pubKey, connect } = useVegaWallet();
|
||||||
|
const [searchParams] = useSearchParams();
|
||||||
|
const [query] = React.useState(searchParams.get('address'));
|
||||||
|
if (query && !pubKey) {
|
||||||
|
connect(connectors.view);
|
||||||
|
}
|
||||||
|
return vegaConnecting;
|
||||||
|
};
|
||||||
|
|
||||||
export const AppLoader = ({ children }: { children: React.ReactElement }) => {
|
export const AppLoader = ({ children }: { children: React.ReactElement }) => {
|
||||||
const featureFlags = useFeatureFlags((state) => state.flags);
|
const featureFlags = useFeatureFlags((state) => state.flags);
|
||||||
@ -26,9 +40,9 @@ export const AppLoader = ({ children }: { children: React.ReactElement }) => {
|
|||||||
const { token, staking, vesting } = useContracts();
|
const { token, staking, vesting } = useContracts();
|
||||||
const setAssociatedBalances = useRefreshAssociatedBalances();
|
const setAssociatedBalances = useRefreshAssociatedBalances();
|
||||||
const [balancesLoaded, setBalancesLoaded] = React.useState(false);
|
const [balancesLoaded, setBalancesLoaded] = React.useState(false);
|
||||||
const vegaWalletStatus = useEagerConnect();
|
const vegaConnecting = useVegaWalletEagerConnect();
|
||||||
|
|
||||||
const loaded = balancesLoaded && vegaWalletStatus !== 'connecting';
|
const loaded = balancesLoaded && !vegaConnecting;
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
const run = async () => {
|
const run = async () => {
|
||||||
@ -169,5 +183,3 @@ export const AppLoader = ({ children }: { children: React.ReactElement }) => {
|
|||||||
}
|
}
|
||||||
return <Suspense fallback={loading}>{children}</Suspense>;
|
return <Suspense fallback={loading}>{children}</Suspense>;
|
||||||
};
|
};
|
||||||
|
|
||||||
AppLoader.displayName = 'AppLoader';
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import './i18n';
|
import './i18n';
|
||||||
|
|
||||||
import React, { useEffect } from 'react';
|
import React, { useEffect } from 'react';
|
||||||
|
import * as Sentry from '@sentry/react';
|
||||||
import { BrowserRouter as Router, useLocation } from 'react-router-dom';
|
import { BrowserRouter as Router, useLocation } from 'react-router-dom';
|
||||||
import { AppLoader } from './app-loader';
|
import { AppLoader } from './app-loader';
|
||||||
import { NetworkInfo } from '@vegaprotocol/network-info';
|
import { NetworkInfo } from '@vegaprotocol/network-info';
|
||||||
@ -25,7 +26,7 @@ import {
|
|||||||
} from '@vegaprotocol/web3';
|
} from '@vegaprotocol/web3';
|
||||||
import { Web3Provider } from '@vegaprotocol/web3';
|
import { Web3Provider } from '@vegaprotocol/web3';
|
||||||
import { VegaWalletDialogs } from './components/vega-wallet-dialogs';
|
import { VegaWalletDialogs } from './components/vega-wallet-dialogs';
|
||||||
import { WalletProvider } from '@vegaprotocol/wallet-react';
|
import { VegaWalletProvider, useChainId } from '@vegaprotocol/wallet';
|
||||||
import {
|
import {
|
||||||
useVegaTransactionManager,
|
useVegaTransactionManager,
|
||||||
useVegaTransactionUpdater,
|
useVegaTransactionUpdater,
|
||||||
@ -35,30 +36,32 @@ import { useEthereumConfig } from '@vegaprotocol/web3';
|
|||||||
import {
|
import {
|
||||||
useEnvironment,
|
useEnvironment,
|
||||||
NetworkLoader,
|
NetworkLoader,
|
||||||
|
useInitializeEnv,
|
||||||
NodeGuard,
|
NodeGuard,
|
||||||
NodeSwitcherDialog,
|
NodeSwitcherDialog,
|
||||||
useNodeSwitcherStore,
|
useNodeSwitcherStore,
|
||||||
|
DocsLinks,
|
||||||
NodeFailure,
|
NodeFailure,
|
||||||
AppLoader as Loader,
|
AppLoader as Loader,
|
||||||
useInitializeEnv,
|
|
||||||
} from '@vegaprotocol/environment';
|
} from '@vegaprotocol/environment';
|
||||||
|
import { ENV } from './config';
|
||||||
import type { InMemoryCacheConfig } from '@apollo/client';
|
import type { InMemoryCacheConfig } from '@apollo/client';
|
||||||
import { CreateWithdrawalDialog } from '@vegaprotocol/withdraws';
|
import { CreateWithdrawalDialog } from '@vegaprotocol/withdraws';
|
||||||
import { SplashLoader } from './components/splash-loader';
|
import { SplashLoader } from './components/splash-loader';
|
||||||
import { ToastsManager } from './toasts-manager';
|
import { ToastsManager } from './toasts-manager';
|
||||||
import { TelemetryDialog } from './components/telemetry-dialog/telemetry-dialog';
|
import {
|
||||||
|
TelemetryDialog,
|
||||||
|
TELEMETRY_ON,
|
||||||
|
} from './components/telemetry-dialog/telemetry-dialog';
|
||||||
|
import { useLocalStorage } from '@vegaprotocol/react-helpers';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { useSentryInit } from './hooks/use-sentry-init';
|
import { isPartyNotFoundError } from './lib/party';
|
||||||
import { useVegaWalletConfig } from './hooks/use-vega-wallet-config';
|
|
||||||
|
|
||||||
const cache: InMemoryCacheConfig = {
|
const cache: InMemoryCacheConfig = {
|
||||||
typePolicies: {
|
typePolicies: {
|
||||||
Account: {
|
Account: {
|
||||||
keyFields: false,
|
keyFields: false,
|
||||||
},
|
},
|
||||||
Instrument: {
|
|
||||||
keyFields: ['code'],
|
|
||||||
},
|
|
||||||
Delegation: {
|
Delegation: {
|
||||||
keyFields: false,
|
keyFields: false,
|
||||||
// Only get full updates
|
// Only get full updates
|
||||||
@ -98,12 +101,32 @@ const Web3Container = ({
|
|||||||
/** Ethereum provider url */
|
/** Ethereum provider url */
|
||||||
providerUrl: string;
|
providerUrl: string;
|
||||||
}) => {
|
}) => {
|
||||||
|
const InitializeHandlers = () => {
|
||||||
|
useVegaTransactionManager();
|
||||||
|
useVegaTransactionUpdater();
|
||||||
|
useEthTransactionManager();
|
||||||
|
useEthTransactionUpdater();
|
||||||
|
useEthWithdrawApprovalsManager();
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
const [connectors, initializeConnectors] = useWeb3ConnectStore((store) => [
|
const [connectors, initializeConnectors] = useWeb3ConnectStore((store) => [
|
||||||
store.connectors,
|
store.connectors,
|
||||||
store.initialize,
|
store.initialize,
|
||||||
]);
|
]);
|
||||||
const { ETHEREUM_PROVIDER_URL, ETH_LOCAL_PROVIDER_URL, ETH_WALLET_MNEMONIC } =
|
const {
|
||||||
useEnvironment();
|
ETHEREUM_PROVIDER_URL,
|
||||||
|
ETH_LOCAL_PROVIDER_URL,
|
||||||
|
ETH_WALLET_MNEMONIC,
|
||||||
|
VEGA_ENV,
|
||||||
|
VEGA_URL,
|
||||||
|
VEGA_EXPLORER_URL,
|
||||||
|
CHROME_EXTENSION_URL,
|
||||||
|
MOZILLA_EXTENSION_URL,
|
||||||
|
VEGA_WALLET_URL,
|
||||||
|
} = useEnvironment();
|
||||||
|
|
||||||
|
const vegaChainId = useChainId(VEGA_URL);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (chainId) {
|
if (chainId) {
|
||||||
@ -124,31 +147,50 @@ const Web3Container = ({
|
|||||||
ETH_LOCAL_PROVIDER_URL,
|
ETH_LOCAL_PROVIDER_URL,
|
||||||
ETH_WALLET_MNEMONIC,
|
ETH_WALLET_MNEMONIC,
|
||||||
]);
|
]);
|
||||||
|
const sideBar = React.useMemo(() => {
|
||||||
|
return [<EthWallet />, <VegaWallet />];
|
||||||
|
}, []);
|
||||||
|
|
||||||
const vegaWalletConfig = useVegaWalletConfig();
|
if (connectors.length === 0) {
|
||||||
|
|
||||||
if (!vegaWalletConfig || connectors.length === 0) {
|
|
||||||
// Prevent loading when the connectors are not initialized
|
// Prevent loading when the connectors are not initialized
|
||||||
return <SplashLoader />;
|
return <SplashLoader />;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
!VEGA_URL ||
|
||||||
|
!VEGA_WALLET_URL ||
|
||||||
|
!VEGA_EXPLORER_URL ||
|
||||||
|
!DocsLinks ||
|
||||||
|
!CHROME_EXTENSION_URL ||
|
||||||
|
!MOZILLA_EXTENSION_URL ||
|
||||||
|
!vegaChainId
|
||||||
|
) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Web3Provider connectors={connectors}>
|
<Web3Provider connectors={connectors}>
|
||||||
<Web3Connector connectors={connectors} chainId={Number(chainId)}>
|
<Web3Connector connectors={connectors} chainId={Number(chainId)}>
|
||||||
<WalletProvider config={vegaWalletConfig}>
|
<VegaWalletProvider
|
||||||
|
config={{
|
||||||
|
network: VEGA_ENV,
|
||||||
|
vegaUrl: VEGA_URL,
|
||||||
|
chainId: vegaChainId,
|
||||||
|
vegaWalletServiceUrl: VEGA_WALLET_URL,
|
||||||
|
links: {
|
||||||
|
explorer: VEGA_EXPLORER_URL,
|
||||||
|
concepts: DocsLinks?.VEGA_WALLET_CONCEPTS_URL,
|
||||||
|
chromeExtensionUrl: CHROME_EXTENSION_URL,
|
||||||
|
mozillaExtensionUrl: MOZILLA_EXTENSION_URL,
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
>
|
||||||
<ContractsProvider>
|
<ContractsProvider>
|
||||||
<AppLoader>
|
<AppLoader>
|
||||||
<BalanceManager>
|
<BalanceManager>
|
||||||
<>
|
<>
|
||||||
<AppLayout>
|
<AppLayout>
|
||||||
<TemplateSidebar
|
<TemplateSidebar sidebar={sideBar}>
|
||||||
sidebar={
|
|
||||||
<>
|
|
||||||
<EthWallet />
|
|
||||||
<VegaWallet />
|
|
||||||
</>
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<AppRouter />
|
<AppRouter />
|
||||||
</TemplateSidebar>
|
</TemplateSidebar>
|
||||||
<footer className="p-4 break-all border-t border-neutral-700">
|
<footer className="p-4 break-all border-t border-neutral-700">
|
||||||
@ -166,7 +208,7 @@ const Web3Container = ({
|
|||||||
</BalanceManager>
|
</BalanceManager>
|
||||||
</AppLoader>
|
</AppLoader>
|
||||||
</ContractsProvider>
|
</ContractsProvider>
|
||||||
</WalletProvider>
|
</VegaWalletProvider>
|
||||||
</Web3Connector>
|
</Web3Connector>
|
||||||
</Web3Provider>
|
</Web3Provider>
|
||||||
);
|
);
|
||||||
@ -186,9 +228,20 @@ const ScrollToTop = () => {
|
|||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const removeQueryParams = (url: string) => {
|
||||||
|
return url.split('?')[0];
|
||||||
|
};
|
||||||
|
|
||||||
const AppContainer = () => {
|
const AppContainer = () => {
|
||||||
const { config, loading, error } = useEthereumConfig();
|
const { config, loading, error } = useEthereumConfig();
|
||||||
const { VEGA_URL, ETHEREUM_PROVIDER_URL } = useEnvironment();
|
const {
|
||||||
|
VEGA_ENV,
|
||||||
|
VEGA_URL,
|
||||||
|
GIT_COMMIT_HASH,
|
||||||
|
GIT_BRANCH,
|
||||||
|
ETHEREUM_PROVIDER_URL,
|
||||||
|
} = useEnvironment();
|
||||||
|
const [telemetryOn] = useLocalStorage(TELEMETRY_ON);
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [nodeSwitcherOpen, setNodeSwitcher] = useNodeSwitcherStore((store) => [
|
const [nodeSwitcherOpen, setNodeSwitcher] = useNodeSwitcherStore((store) => [
|
||||||
store.dialogOpen,
|
store.dialogOpen,
|
||||||
@ -198,7 +251,70 @@ const AppContainer = () => {
|
|||||||
// Hacky skip all the loading & web3 init for geo restricted users
|
// Hacky skip all the loading & web3 init for geo restricted users
|
||||||
const isRestricted = document?.location?.pathname?.includes('/restricted');
|
const isRestricted = document?.location?.pathname?.includes('/restricted');
|
||||||
|
|
||||||
useSentryInit();
|
useEffect(() => {
|
||||||
|
if (ENV.dsn && telemetryOn === 'true') {
|
||||||
|
Sentry.init({
|
||||||
|
dsn: ENV.dsn,
|
||||||
|
tracesSampleRate: 0.1,
|
||||||
|
enabled: true,
|
||||||
|
environment: VEGA_ENV,
|
||||||
|
release: GIT_COMMIT_HASH,
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
const updatedRequest =
|
||||||
|
requestUrl && requestUrl.includes('/claim?')
|
||||||
|
? { ...event.request, url: removeQueryParams(requestUrl) }
|
||||||
|
: event.request;
|
||||||
|
|
||||||
|
const updatedTransaction =
|
||||||
|
transaction && transaction.includes('/claim?')
|
||||||
|
? removeQueryParams(transaction)
|
||||||
|
: transaction;
|
||||||
|
|
||||||
|
const updatedBreadcrumbs = event.breadcrumbs?.map((breadcrumb) => {
|
||||||
|
if (
|
||||||
|
breadcrumb.type === 'navigation' &&
|
||||||
|
breadcrumb.data?.to?.includes('/claim?')
|
||||||
|
) {
|
||||||
|
return {
|
||||||
|
...breadcrumb,
|
||||||
|
data: {
|
||||||
|
...breadcrumb.data,
|
||||||
|
to: removeQueryParams(breadcrumb.data.to),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return breadcrumb;
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
...event,
|
||||||
|
request: updatedRequest,
|
||||||
|
transaction: updatedTransaction,
|
||||||
|
breadcrumbs: updatedBreadcrumbs ?? event.breadcrumbs,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
Sentry.setTag('branch', GIT_BRANCH);
|
||||||
|
Sentry.setTag('commit', GIT_COMMIT_HASH);
|
||||||
|
} else {
|
||||||
|
Sentry.close();
|
||||||
|
}
|
||||||
|
}, [GIT_COMMIT_HASH, GIT_BRANCH, VEGA_ENV, telemetryOn]);
|
||||||
|
|
||||||
if (isRestricted) {
|
if (isRestricted) {
|
||||||
return (
|
return (
|
||||||
@ -240,15 +356,6 @@ const AppContainer = () => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const InitializeHandlers = () => {
|
|
||||||
useVegaTransactionManager();
|
|
||||||
useVegaTransactionUpdater();
|
|
||||||
useEthTransactionManager();
|
|
||||||
useEthTransactionUpdater();
|
|
||||||
useEthWithdrawApprovalsManager();
|
|
||||||
return null;
|
|
||||||
};
|
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
useInitializeEnv();
|
useInitializeEnv();
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@ import { useGetAssociationBreakdown } from '../../hooks/use-get-association-brea
|
|||||||
import { useGetUserBalances } from '../../hooks/use-get-user-balances';
|
import { useGetUserBalances } from '../../hooks/use-get-user-balances';
|
||||||
import { useBalances } from '../../lib/balances/balances-store';
|
import { useBalances } from '../../lib/balances/balances-store';
|
||||||
import type { ReactElement } from 'react';
|
import type { ReactElement } from 'react';
|
||||||
import { useVegaWallet } from '@vegaprotocol/wallet-react';
|
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||||
import { useListenForStakingEvents as useListenForAssociationEvents } from '../../hooks/use-listen-for-staking-events';
|
import { useListenForStakingEvents as useListenForAssociationEvents } from '../../hooks/use-listen-for-staking-events';
|
||||||
import { useTranches } from '../../lib/tranches/tranches-store';
|
import { useTranches } from '../../lib/tranches/tranches-store';
|
||||||
import { useUserTrancheBalances } from '../../routes/redemption/hooks';
|
import { useUserTrancheBalances } from '../../routes/redemption/hooks';
|
||||||
|
@ -15,16 +15,19 @@ export const CollapsibleToggle = ({
|
|||||||
dataTestId,
|
dataTestId,
|
||||||
children,
|
children,
|
||||||
}: CollapsibleToggleProps) => {
|
}: CollapsibleToggleProps) => {
|
||||||
const classes = classnames('transition-transform ease-in-out duration-300', {
|
const classes = classnames(
|
||||||
'rotate-180': toggleState,
|
'mb-4 transition-transform ease-in-out duration-300',
|
||||||
});
|
{
|
||||||
|
'rotate-180': toggleState,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<button
|
<button
|
||||||
onClick={() => setToggleState(!toggleState)}
|
onClick={() => setToggleState(!toggleState)}
|
||||||
data-testid={dataTestId}
|
data-testid={dataTestId}
|
||||||
>
|
>
|
||||||
<div className="flex items-baseline gap-3">
|
<div className="flex items-center gap-3">
|
||||||
{children}
|
{children}
|
||||||
<div className={classes} data-testid="toggle-icon-wrapper">
|
<div className={classes} data-testid="toggle-icon-wrapper">
|
||||||
<VegaIcon name={VegaIconNames.CHEVRON_DOWN} size={20} />
|
<VegaIcon name={VegaIconNames.CHEVRON_DOWN} size={20} />
|
||||||
|
@ -1,13 +1,18 @@
|
|||||||
import { Button } from '@vegaprotocol/ui-toolkit';
|
import { Button } from '@vegaprotocol/ui-toolkit';
|
||||||
|
import React from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { useDialogStore } from '@vegaprotocol/wallet-react';
|
import { useVegaWalletDialogStore } from '@vegaprotocol/wallet';
|
||||||
|
|
||||||
export const ConnectToVega = () => {
|
export const ConnectToVega = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const openVegaWalletDialog = useDialogStore((store) => store.open);
|
const { openVegaWalletDialog } = useVegaWalletDialogStore((store) => ({
|
||||||
|
openVegaWalletDialog: store.openVegaWalletDialog,
|
||||||
|
}));
|
||||||
return (
|
return (
|
||||||
<Button
|
<Button
|
||||||
onClick={openVegaWalletDialog}
|
onClick={() => {
|
||||||
|
openVegaWalletDialog();
|
||||||
|
}}
|
||||||
data-testid="connect-to-vega-wallet-btn"
|
data-testid="connect-to-vega-wallet-btn"
|
||||||
variant="primary"
|
variant="primary"
|
||||||
>
|
>
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { type ReactNode } from 'react';
|
|
||||||
|
|
||||||
interface HeadingProps {
|
interface HeadingProps {
|
||||||
title?: ReactNode;
|
title?: string;
|
||||||
centerContent?: boolean;
|
centerContent?: boolean;
|
||||||
marginTop?: boolean;
|
marginTop?: boolean;
|
||||||
marginBottom?: boolean;
|
marginBottom?: boolean;
|
||||||
|
@ -1,44 +0,0 @@
|
|||||||
import {
|
|
||||||
useLinks,
|
|
||||||
DApp,
|
|
||||||
CONSOLE_REWARDS_PAGE,
|
|
||||||
} from '@vegaprotocol/environment';
|
|
||||||
import {
|
|
||||||
ExternalLink,
|
|
||||||
Intent,
|
|
||||||
NotificationBanner,
|
|
||||||
VegaIcon,
|
|
||||||
VegaIconNames,
|
|
||||||
} from '@vegaprotocol/ui-toolkit';
|
|
||||||
import { Trans } from 'react-i18next';
|
|
||||||
import { useMatch } from 'react-router-dom';
|
|
||||||
import Routes from '../../routes/routes';
|
|
||||||
import { type ReactNode } from 'react';
|
|
||||||
|
|
||||||
const ConsoleRewardsLink = ({ children }: { children: ReactNode }) => {
|
|
||||||
const consoleLink = useLinks(DApp.Console);
|
|
||||||
return (
|
|
||||||
<ExternalLink
|
|
||||||
href={consoleLink(CONSOLE_REWARDS_PAGE)}
|
|
||||||
className="underline inline-flex gap-1 items-center"
|
|
||||||
title="Rewards in Console"
|
|
||||||
>
|
|
||||||
<span>{children}</span>
|
|
||||||
<VegaIcon size={12} name={VegaIconNames.OPEN_EXTERNAL} />
|
|
||||||
</ExternalLink>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const RewardsMovedNotification = () => {
|
|
||||||
const onRewardsPage = useMatch(Routes.REWARDS);
|
|
||||||
if (!onRewardsPage) return null;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<NotificationBanner intent={Intent.Warning}>
|
|
||||||
<Trans
|
|
||||||
i18nKey="rewardsMovedNotification"
|
|
||||||
components={[<ConsoleRewardsLink>Console</ConsoleRewardsLink>]}
|
|
||||||
/>
|
|
||||||
</NotificationBanner>
|
|
||||||
);
|
|
||||||
};
|
|
@ -1,5 +1,5 @@
|
|||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { useVegaWallet } from '@vegaprotocol/wallet-react';
|
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||||
import type { ReactNode } from 'react';
|
import type { ReactNode } from 'react';
|
||||||
import { AnnouncementBanner } from '@vegaprotocol/announcements';
|
import { AnnouncementBanner } from '@vegaprotocol/announcements';
|
||||||
import { Nav } from '../nav';
|
import { Nav } from '../nav';
|
||||||
@ -10,7 +10,6 @@ import {
|
|||||||
ProtocolUpgradeProposalNotification,
|
ProtocolUpgradeProposalNotification,
|
||||||
} from '@vegaprotocol/proposals';
|
} from '@vegaprotocol/proposals';
|
||||||
import { ViewingAsBanner } from '@vegaprotocol/ui-toolkit';
|
import { ViewingAsBanner } from '@vegaprotocol/ui-toolkit';
|
||||||
import { RewardsMovedNotification } from '../notifications/rewards-moved-notification';
|
|
||||||
|
|
||||||
interface AppLayoutProps {
|
interface AppLayoutProps {
|
||||||
children: ReactNode;
|
children: ReactNode;
|
||||||
@ -46,10 +45,8 @@ export const AppLayout = ({ children }: AppLayoutProps) => {
|
|||||||
|
|
||||||
const NotificationsContainer = () => {
|
const NotificationsContainer = () => {
|
||||||
const { isReadOnly, pubKey, disconnect } = useVegaWallet();
|
const { isReadOnly, pubKey, disconnect } = useVegaWallet();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div data-testid="banners">
|
<div data-testid="banners">
|
||||||
<RewardsMovedNotification />
|
|
||||||
<ProtocolUpgradeProposalNotification
|
<ProtocolUpgradeProposalNotification
|
||||||
mode={ProtocolUpgradeCountdownMode.IN_ESTIMATED_TIME_REMAINING}
|
mode={ProtocolUpgradeCountdownMode.IN_ESTIMATED_TIME_REMAINING}
|
||||||
/>
|
/>
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import { Children, type ReactNode } from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
export interface TemplateSidebarProps {
|
export interface TemplateSidebarProps {
|
||||||
children: ReactNode;
|
children: React.ReactNode;
|
||||||
sidebar: ReactNode;
|
sidebar: React.ReactNode[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export function TemplateSidebar({ children, sidebar }: TemplateSidebarProps) {
|
export function TemplateSidebar({ children, sidebar }: TemplateSidebarProps) {
|
||||||
@ -12,9 +12,9 @@ export function TemplateSidebar({ children, sidebar }: TemplateSidebarProps) {
|
|||||||
{children}
|
{children}
|
||||||
</main>
|
</main>
|
||||||
<aside className="col-start-2 row-start-1 row-span-2 hidden lg:block p-4 bg-banner bg-contain border-l border-neutral-700">
|
<aside className="col-start-2 row-start-1 row-span-2 hidden lg:block p-4 bg-banner bg-contain border-l border-neutral-700">
|
||||||
{Children.map(sidebar, (child, i) => (
|
{sidebar.map((Component, i) => (
|
||||||
<section className="mb-4 last:mb-0" key={i}>
|
<section className="mb-4 last:mb-0" key={i}>
|
||||||
{child}
|
{Component}
|
||||||
</section>
|
</section>
|
||||||
))}
|
))}
|
||||||
</aside>
|
</aside>
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { Button } from '@vegaprotocol/ui-toolkit';
|
import { Button } from '@vegaprotocol/ui-toolkit';
|
||||||
import { useVegaWallet, useDialogStore } from '@vegaprotocol/wallet-react';
|
import { useVegaWallet, useVegaWalletDialogStore } from '@vegaprotocol/wallet';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
@ -10,7 +10,9 @@ interface VegaWalletContainerProps {
|
|||||||
export const VegaWalletContainer = ({ children }: VegaWalletContainerProps) => {
|
export const VegaWalletContainer = ({ children }: VegaWalletContainerProps) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { pubKey } = useVegaWallet();
|
const { pubKey } = useVegaWallet();
|
||||||
const openVegaWalletDialog = useDialogStore((store) => store.open);
|
const { openVegaWalletDialog } = useVegaWalletDialogStore((store) => ({
|
||||||
|
openVegaWalletDialog: store.openVegaWalletDialog,
|
||||||
|
}));
|
||||||
|
|
||||||
if (!pubKey) {
|
if (!pubKey) {
|
||||||
return (
|
return (
|
||||||
|
@ -9,7 +9,7 @@ import Routes from '../../routes/routes';
|
|||||||
export const RiskMessage = () => {
|
export const RiskMessage = () => {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="bg-vega-light-100 dark:bg-vega-dark-100 p-6">
|
<div className="bg-vega-light-100 dark:bg-vega-dark-100 p-6 mb-6">
|
||||||
<ul className="list-[square] ml-4">
|
<ul className="list-[square] ml-4">
|
||||||
<li>
|
<li>
|
||||||
{t(
|
{t(
|
||||||
@ -23,7 +23,7 @@ export const RiskMessage = () => {
|
|||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<p>
|
<p className="mb-8">
|
||||||
{t(
|
{t(
|
||||||
'By using the Vega Governance App, you acknowledge that you have read and understood the'
|
'By using the Vega Governance App, you acknowledge that you have read and understood the'
|
||||||
)}{' '}
|
)}{' '}
|
||||||
|
@ -1,39 +1,25 @@
|
|||||||
import {
|
import {
|
||||||
ConnectDialogWithRiskAck,
|
VegaConnectDialog,
|
||||||
useDialogStore,
|
VegaManageDialog,
|
||||||
} from '@vegaprotocol/wallet-react';
|
ViewAsDialog,
|
||||||
|
} from '@vegaprotocol/wallet';
|
||||||
import {
|
import {
|
||||||
AppStateActionType,
|
AppStateActionType,
|
||||||
useAppState,
|
useAppState,
|
||||||
} from '../../contexts/app-state/app-state-context';
|
} from '../../contexts/app-state/app-state-context';
|
||||||
|
import { useConnectors } from '../../lib/vega-connectors';
|
||||||
import { RiskMessage } from './risk-message';
|
import { RiskMessage } from './risk-message';
|
||||||
import { VegaManageDialog } from '../manage-dialog';
|
|
||||||
import { useLocalStorage } from '@vegaprotocol/react-helpers';
|
|
||||||
import { Networks, useEnvironment } from '@vegaprotocol/environment';
|
|
||||||
|
|
||||||
export const VegaWalletDialogs = () => {
|
export const VegaWalletDialogs = () => {
|
||||||
const { VEGA_ENV } = useEnvironment();
|
|
||||||
const { appState, appDispatch } = useAppState();
|
const { appState, appDispatch } = useAppState();
|
||||||
const [riskAccepted, setRiskAccepted] = useLocalStorage(
|
const connectors = useConnectors();
|
||||||
'vega_wallet_risk_accepted'
|
|
||||||
);
|
|
||||||
const vegaWalletDialogOpen = useDialogStore((store) => store.isOpen);
|
|
||||||
const setVegaWalletDialog = useDialogStore((store) => store.set);
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<ConnectDialogWithRiskAck
|
<VegaConnectDialog
|
||||||
open={vegaWalletDialogOpen}
|
connectors={connectors}
|
||||||
onChange={setVegaWalletDialog}
|
riskMessage={<RiskMessage />}
|
||||||
riskAccepted={
|
|
||||||
VEGA_ENV === Networks.TESTNET ? riskAccepted === 'true' : true
|
|
||||||
}
|
|
||||||
riskAckContent={<RiskMessage />}
|
|
||||||
onRiskAccepted={() => setRiskAccepted('true')}
|
|
||||||
onRiskRejected={() => {
|
|
||||||
setRiskAccepted('false');
|
|
||||||
setVegaWalletDialog(false);
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<VegaManageDialog
|
<VegaManageDialog
|
||||||
dialogOpen={appState.vegaWalletManageOverlay}
|
dialogOpen={appState.vegaWalletManageOverlay}
|
||||||
setDialogOpen={(open) =>
|
setDialogOpen={(open) =>
|
||||||
@ -43,6 +29,8 @@ export const VegaWalletDialogs = () => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<ViewAsDialog connector={connectors.view} />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -4,13 +4,14 @@ import keyBy from 'lodash/keyBy';
|
|||||||
import uniq from 'lodash/uniq';
|
import uniq from 'lodash/uniq';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { ENV } from '../../config';
|
||||||
|
|
||||||
import noIcon from '../../images/token-no-icon.png';
|
import noIcon from '../../images/token-no-icon.png';
|
||||||
import vegaBlack from '../../images/vega_black.png';
|
import vegaBlack from '../../images/vega_black.png';
|
||||||
import vegaVesting from '../../images/vega_vesting.png';
|
import vegaVesting from '../../images/vega_vesting.png';
|
||||||
import { BigNumber } from '../../lib/bignumber';
|
import { BigNumber } from '../../lib/bignumber';
|
||||||
import { type WalletCardAssetProps } from '../wallet-card';
|
import { type WalletCardAssetProps } from '../wallet-card';
|
||||||
import { useVegaWallet } from '@vegaprotocol/wallet-react';
|
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||||
import { useContracts } from '../../contexts/contracts/contracts-context';
|
import { useContracts } from '../../contexts/contracts/contracts-context';
|
||||||
import * as Schema from '@vegaprotocol/types';
|
import * as Schema from '@vegaprotocol/types';
|
||||||
import {
|
import {
|
||||||
@ -36,6 +37,7 @@ export const usePollForDelegations = () => {
|
|||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { pubKey } = useVegaWallet();
|
const { pubKey } = useVegaWallet();
|
||||||
const client = useApolloClient();
|
const client = useApolloClient();
|
||||||
|
const { delegationsPagination } = ENV;
|
||||||
const [delegations, setDelegations] = React.useState<
|
const [delegations, setDelegations] = React.useState<
|
||||||
WalletDelegationFieldsFragment[]
|
WalletDelegationFieldsFragment[]
|
||||||
>([]);
|
>([]);
|
||||||
@ -66,9 +68,11 @@ export const usePollForDelegations = () => {
|
|||||||
query: DelegationsDocument,
|
query: DelegationsDocument,
|
||||||
variables: {
|
variables: {
|
||||||
partyId: pubKey,
|
partyId: pubKey,
|
||||||
delegationsPagination: {
|
delegationsPagination: delegationsPagination
|
||||||
first: 50,
|
? {
|
||||||
},
|
first: Number(delegationsPagination),
|
||||||
|
}
|
||||||
|
: undefined,
|
||||||
},
|
},
|
||||||
fetchPolicy: 'network-only',
|
fetchPolicy: 'network-only',
|
||||||
})
|
})
|
||||||
@ -232,14 +236,14 @@ export const usePollForDelegations = () => {
|
|||||||
// will just continue to fail
|
// will just continue to fail
|
||||||
clearInterval(interval);
|
clearInterval(interval);
|
||||||
});
|
});
|
||||||
}, 20000);
|
}, 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
clearInterval(interval);
|
clearInterval(interval);
|
||||||
mounted = false;
|
mounted = false;
|
||||||
};
|
};
|
||||||
}, [client, decimals, pubKey, t, vegaToken.address]);
|
}, [delegationsPagination, client, decimals, pubKey, t, vegaToken.address]);
|
||||||
|
|
||||||
return { delegations, currentStakeAvailable, delegatedNodes, accounts };
|
return { delegations, currentStakeAvailable, delegatedNodes, accounts };
|
||||||
};
|
};
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
import { ButtonLink, Link } from '@vegaprotocol/ui-toolkit';
|
import { ButtonLink, Link } from '@vegaprotocol/ui-toolkit';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { ExternalLinks } from '@vegaprotocol/environment';
|
import { ExternalLinks } from '@vegaprotocol/environment';
|
||||||
import { useConnect } from '@vegaprotocol/wallet-react';
|
import { useViewAsDialog } from '@vegaprotocol/wallet';
|
||||||
|
|
||||||
export const VegaWalletPrompt = () => {
|
export const VegaWalletPrompt = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { connect } = useConnect();
|
const setViewAsDialog = useViewAsDialog((state) => state.setOpen);
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<h3 className="mt-4 mb-2">{t('getWallet')}</h3>
|
<h3 className="mt-4 mb-2">{t('getWallet')}</h3>
|
||||||
@ -16,7 +16,7 @@ export const VegaWalletPrompt = () => {
|
|||||||
<ButtonLink
|
<ButtonLink
|
||||||
className="text-neutral-500"
|
className="text-neutral-500"
|
||||||
data-testid="view-as-user"
|
data-testid="view-as-user"
|
||||||
onClick={() => connect('viewParty')}
|
onClick={() => setViewAsDialog(true)}
|
||||||
>
|
>
|
||||||
{t('viewAsParty')}
|
{t('viewAsParty')}
|
||||||
</ButtonLink>
|
</ButtonLink>
|
||||||
|
@ -25,7 +25,7 @@ import {
|
|||||||
} from '../wallet-card';
|
} from '../wallet-card';
|
||||||
import { VegaWalletPrompt } from './vega-wallet-prompt';
|
import { VegaWalletPrompt } from './vega-wallet-prompt';
|
||||||
import { usePollForDelegations } from './hooks';
|
import { usePollForDelegations } from './hooks';
|
||||||
import { useVegaWallet, useDialogStore } from '@vegaprotocol/wallet-react';
|
import { useVegaWallet, useVegaWalletDialogStore } from '@vegaprotocol/wallet';
|
||||||
import { Button, ButtonLink } from '@vegaprotocol/ui-toolkit';
|
import { Button, ButtonLink } from '@vegaprotocol/ui-toolkit';
|
||||||
import { toBigNum } from '@vegaprotocol/utils';
|
import { toBigNum } from '@vegaprotocol/utils';
|
||||||
import { usePendingBalancesStore } from '../../hooks/use-pending-balances-manager';
|
import { usePendingBalancesStore } from '../../hooks/use-pending-balances-manager';
|
||||||
@ -34,17 +34,16 @@ import omit from 'lodash/omit';
|
|||||||
|
|
||||||
export const VegaWallet = () => {
|
export const VegaWallet = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { status, pubKey, pubKeys } = useVegaWallet();
|
const { pubKey, pubKeys } = useVegaWallet();
|
||||||
const pubKeyObj = useMemo(() => {
|
const pubKeyObj = useMemo(() => {
|
||||||
return pubKeys?.find((pk) => pk.publicKey === pubKey);
|
return pubKeys?.find((pk) => pk.publicKey === pubKey);
|
||||||
}, [pubKey, pubKeys]);
|
}, [pubKey, pubKeys]);
|
||||||
|
|
||||||
const child =
|
const child = !pubKeys ? (
|
||||||
status === 'connected' ? (
|
<VegaWalletNotConnected />
|
||||||
<VegaWalletConnected vegaKeys={pubKeys.map((pk) => pk.publicKey)} />
|
) : (
|
||||||
) : (
|
<VegaWalletConnected vegaKeys={pubKeys.map((pk) => pk.publicKey)} />
|
||||||
<VegaWalletNotConnected />
|
);
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section className="vega-wallet" data-testid="vega-wallet">
|
<section className="vega-wallet" data-testid="vega-wallet">
|
||||||
@ -76,7 +75,9 @@ export const VegaWallet = () => {
|
|||||||
|
|
||||||
const VegaWalletNotConnected = () => {
|
const VegaWalletNotConnected = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const openVegaWalletDialog = useDialogStore((store) => store.open);
|
const { openVegaWalletDialog } = useVegaWalletDialogStore((store) => ({
|
||||||
|
openVegaWalletDialog: store.openVegaWalletDialog,
|
||||||
|
}));
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Button
|
<Button
|
||||||
|
@ -65,6 +65,7 @@ export const ENV = {
|
|||||||
docsUrl: windowOrDefault('NX_VEGA_DOCS_URL'),
|
docsUrl: windowOrDefault('NX_VEGA_DOCS_URL'),
|
||||||
ethWalletMnemonic: windowOrDefault('NX_ETH_WALLET_MNEMONIC'),
|
ethWalletMnemonic: windowOrDefault('NX_ETH_WALLET_MNEMONIC'),
|
||||||
localProviderUrl: windowOrDefault('NX_LOCAL_PROVIDER_URL'),
|
localProviderUrl: windowOrDefault('NX_LOCAL_PROVIDER_URL'),
|
||||||
|
delegationsPagination: windowOrDefault('NX_DELEGATIONS_PAGINATION'),
|
||||||
rest: windowOrDefault('NX_VEGA_REST_URL'),
|
rest: windowOrDefault('NX_VEGA_REST_URL'),
|
||||||
addresses:
|
addresses:
|
||||||
ContractAddresses[(envName === 'local' ? 'CUSTOM' : envName) as Networks],
|
ContractAddresses[(envName === 'local' ? 'CUSTOM' : envName) as Networks],
|
||||||
|
@ -111,4 +111,3 @@ export const ContractsProvider = ({ children }: { children: JSX.Element }) => {
|
|||||||
</ContractsContext.Provider>
|
</ContractsContext.Provider>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
ContractsProvider.displayName = 'ContractsProvider';
|
|
||||||
|
@ -7,7 +7,7 @@ import { useWeb3React } from '@web3-react/core';
|
|||||||
|
|
||||||
export const useListenForStakingEvents = (
|
export const useListenForStakingEvents = (
|
||||||
contract: Contract | undefined,
|
contract: Contract | undefined,
|
||||||
vegaPublicKey: string | undefined,
|
vegaPublicKey: string | null,
|
||||||
numberOfConfirmations: number
|
numberOfConfirmations: number
|
||||||
) => {
|
) => {
|
||||||
const { account } = useWeb3React();
|
const { account } = useWeb3React();
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import * as Sentry from '@sentry/react';
|
import * as Sentry from '@sentry/react';
|
||||||
import { toBigNum } from '@vegaprotocol/utils';
|
import { toBigNum } from '@vegaprotocol/utils';
|
||||||
import { useVegaWallet } from '@vegaprotocol/wallet-react';
|
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||||
import { useEthereumConfig } from '@vegaprotocol/web3';
|
import { useEthereumConfig } from '@vegaprotocol/web3';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
|
@ -1,81 +0,0 @@
|
|||||||
import { useEffect } from 'react';
|
|
||||||
import * as Sentry from '@sentry/react';
|
|
||||||
import { useLocalStorage } from '@vegaprotocol/react-helpers';
|
|
||||||
import { TELEMETRY_ON } from '../components/telemetry-dialog/telemetry-dialog';
|
|
||||||
import { useEnvironment } from '@vegaprotocol/environment';
|
|
||||||
import { ENV } from '../config';
|
|
||||||
import { isPartyNotFoundError } from '../lib/party';
|
|
||||||
|
|
||||||
export const useSentryInit = () => {
|
|
||||||
const { VEGA_ENV, GIT_COMMIT_HASH, GIT_BRANCH } = useEnvironment();
|
|
||||||
const [telemetryOn] = useLocalStorage(TELEMETRY_ON);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (ENV.dsn && telemetryOn === 'true') {
|
|
||||||
Sentry.init({
|
|
||||||
dsn: ENV.dsn,
|
|
||||||
tracesSampleRate: 0.1,
|
|
||||||
enabled: true,
|
|
||||||
environment: VEGA_ENV,
|
|
||||||
release: GIT_COMMIT_HASH,
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
const updatedRequest =
|
|
||||||
requestUrl && requestUrl.includes('/claim?')
|
|
||||||
? { ...event.request, url: removeQueryParams(requestUrl) }
|
|
||||||
: event.request;
|
|
||||||
|
|
||||||
const updatedTransaction =
|
|
||||||
transaction && transaction.includes('/claim?')
|
|
||||||
? removeQueryParams(transaction)
|
|
||||||
: transaction;
|
|
||||||
|
|
||||||
const updatedBreadcrumbs = event.breadcrumbs?.map((breadcrumb) => {
|
|
||||||
if (
|
|
||||||
breadcrumb.type === 'navigation' &&
|
|
||||||
breadcrumb.data?.to?.includes('/claim?')
|
|
||||||
) {
|
|
||||||
return {
|
|
||||||
...breadcrumb,
|
|
||||||
data: {
|
|
||||||
...breadcrumb.data,
|
|
||||||
to: removeQueryParams(breadcrumb.data.to),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return breadcrumb;
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
|
||||||
...event,
|
|
||||||
request: updatedRequest,
|
|
||||||
transaction: updatedTransaction,
|
|
||||||
breadcrumbs: updatedBreadcrumbs ?? event.breadcrumbs,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
});
|
|
||||||
Sentry.setTag('branch', GIT_BRANCH);
|
|
||||||
Sentry.setTag('commit', GIT_COMMIT_HASH);
|
|
||||||
} else {
|
|
||||||
Sentry.close();
|
|
||||||
}
|
|
||||||
}, [GIT_COMMIT_HASH, GIT_BRANCH, VEGA_ENV, telemetryOn]);
|
|
||||||
};
|
|
||||||
|
|
||||||
const removeQueryParams = (url: string) => {
|
|
||||||
return url.split('?')[0];
|
|
||||||
};
|
|
@ -1,41 +0,0 @@
|
|||||||
import { useMemo } from 'react';
|
|
||||||
import {
|
|
||||||
InjectedConnector,
|
|
||||||
JsonRpcConnector,
|
|
||||||
SnapConnector,
|
|
||||||
ViewPartyConnector,
|
|
||||||
createConfig,
|
|
||||||
fairground,
|
|
||||||
stagnet,
|
|
||||||
mainnet,
|
|
||||||
} from '@vegaprotocol/wallet';
|
|
||||||
import { useEnvironment } from '@vegaprotocol/environment';
|
|
||||||
|
|
||||||
export const useVegaWalletConfig = () => {
|
|
||||||
const { VEGA_ENV, VEGA_URL, VEGA_WALLET_URL } = useEnvironment();
|
|
||||||
return useMemo(() => {
|
|
||||||
if (!VEGA_ENV || !VEGA_URL || !VEGA_WALLET_URL) return;
|
|
||||||
|
|
||||||
const injected = new InjectedConnector();
|
|
||||||
|
|
||||||
const jsonRpc = new JsonRpcConnector({
|
|
||||||
url: VEGA_WALLET_URL,
|
|
||||||
});
|
|
||||||
|
|
||||||
const snap = new SnapConnector({
|
|
||||||
node: new URL(VEGA_URL).origin,
|
|
||||||
snapId: 'npm:@vegaprotocol/snap',
|
|
||||||
version: '1.0.1',
|
|
||||||
});
|
|
||||||
|
|
||||||
const viewParty = new ViewPartyConnector();
|
|
||||||
|
|
||||||
const config = createConfig({
|
|
||||||
chains: [mainnet, fairground, stagnet],
|
|
||||||
defaultChainId: fairground.id,
|
|
||||||
connectors: [injected, snap, jsonRpc, viewParty],
|
|
||||||
});
|
|
||||||
|
|
||||||
return config;
|
|
||||||
}, [VEGA_ENV, VEGA_URL, VEGA_WALLET_URL]);
|
|
||||||
};
|
|
@ -31,7 +31,7 @@ i18n
|
|||||||
load: 'languageOnly',
|
load: 'languageOnly',
|
||||||
debug: isInDev,
|
debug: isInDev,
|
||||||
// have a common namespace used around the full app
|
// have a common namespace used around the full app
|
||||||
ns: ['governance', 'wallet', 'wallet-react'],
|
ns: ['governance'],
|
||||||
defaultNS: 'governance',
|
defaultNS: 'governance',
|
||||||
keySeparator: false, // we use content as keys
|
keySeparator: false, // we use content as keys
|
||||||
nsSeparator: false,
|
nsSeparator: false,
|
||||||
|
@ -20,7 +20,6 @@ describe('getMultisigStatus', () => {
|
|||||||
expect(result).toEqual({
|
expect(result).toEqual({
|
||||||
multisigStatus: MultisigStatus.noNodes,
|
multisigStatus: MultisigStatus.noNodes,
|
||||||
showMultisigStatusError: true,
|
showMultisigStatusError: true,
|
||||||
zeroScoreNodes: [],
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -36,7 +35,6 @@ describe('getMultisigStatus', () => {
|
|||||||
expect(result).toEqual({
|
expect(result).toEqual({
|
||||||
multisigStatus: MultisigStatus.correct,
|
multisigStatus: MultisigStatus.correct,
|
||||||
showMultisigStatusError: false,
|
showMultisigStatusError: false,
|
||||||
zeroScoreNodes: [],
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -52,22 +50,6 @@ describe('getMultisigStatus', () => {
|
|||||||
expect(result).toEqual({
|
expect(result).toEqual({
|
||||||
multisigStatus: MultisigStatus.nodeNeedsRemoving,
|
multisigStatus: MultisigStatus.nodeNeedsRemoving,
|
||||||
showMultisigStatusError: true,
|
showMultisigStatusError: true,
|
||||||
zeroScoreNodes: [
|
|
||||||
{
|
|
||||||
id: '1',
|
|
||||||
rewardScore: {
|
|
||||||
multisigScore: '0',
|
|
||||||
},
|
|
||||||
stakedTotal: '1000',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '2',
|
|
||||||
rewardScore: {
|
|
||||||
multisigScore: '0',
|
|
||||||
},
|
|
||||||
stakedTotal: '1000',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -83,15 +65,6 @@ describe('getMultisigStatus', () => {
|
|||||||
expect(result).toEqual({
|
expect(result).toEqual({
|
||||||
multisigStatus: MultisigStatus.nodeNeedsAdding,
|
multisigStatus: MultisigStatus.nodeNeedsAdding,
|
||||||
showMultisigStatusError: true,
|
showMultisigStatusError: true,
|
||||||
zeroScoreNodes: [
|
|
||||||
{
|
|
||||||
id: '1',
|
|
||||||
rewardScore: {
|
|
||||||
multisigScore: '0',
|
|
||||||
},
|
|
||||||
stakedTotal: '1000',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,8 +1,5 @@
|
|||||||
import { removePaginationWrapper } from '@vegaprotocol/utils';
|
import { removePaginationWrapper } from '@vegaprotocol/utils';
|
||||||
import type {
|
import type { PreviousEpochQuery } from '../routes/staking/__generated__/PreviousEpoch';
|
||||||
PreviousEpochQuery,
|
|
||||||
ValidatorNodeFragment,
|
|
||||||
} from '../routes/staking/__generated__/PreviousEpoch';
|
|
||||||
|
|
||||||
export enum MultisigStatus {
|
export enum MultisigStatus {
|
||||||
'correct' = 'correct',
|
'correct' = 'correct',
|
||||||
@ -20,15 +17,12 @@ export const getMultisigStatusInfo = (
|
|||||||
previousEpochData?.epoch.validatorsConnection?.edges
|
previousEpochData?.epoch.validatorsConnection?.edges
|
||||||
);
|
);
|
||||||
|
|
||||||
const zeroScore = (node: ValidatorNodeFragment) =>
|
const hasZero = allNodesInPreviousEpoch.some(
|
||||||
Number(node.rewardScore?.multisigScore) === 0;
|
(node) => Number(node?.rewardScore?.multisigScore) === 0
|
||||||
const oneScore = (node: ValidatorNodeFragment) =>
|
);
|
||||||
Number(node.rewardScore?.multisigScore) === 1;
|
const hasOne = allNodesInPreviousEpoch.some(
|
||||||
|
(node) => Number(node?.rewardScore?.multisigScore) === 1
|
||||||
const hasZero = allNodesInPreviousEpoch.some(zeroScore);
|
);
|
||||||
const hasOne = allNodesInPreviousEpoch.some(oneScore);
|
|
||||||
|
|
||||||
const zeroScoreNodes = allNodesInPreviousEpoch.filter(zeroScore);
|
|
||||||
|
|
||||||
if (hasZero && hasOne) {
|
if (hasZero && hasOne) {
|
||||||
// If any individual node has 0 it means that node is missing from the multisig and needs to be added
|
// If any individual node has 0 it means that node is missing from the multisig and needs to be added
|
||||||
@ -44,6 +38,5 @@ export const getMultisigStatusInfo = (
|
|||||||
return {
|
return {
|
||||||
showMultisigStatusError: status !== MultisigStatus.correct,
|
showMultisigStatusError: status !== MultisigStatus.correct,
|
||||||
multisigStatus: status,
|
multisigStatus: status,
|
||||||
zeroScoreNodes,
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
30
apps/governance/src/lib/vega-connectors.ts
Normal file
30
apps/governance/src/lib/vega-connectors.ts
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
import { useFeatureFlags } from '@vegaprotocol/environment';
|
||||||
|
import { useMemo } from 'react';
|
||||||
|
import {
|
||||||
|
JsonRpcConnector,
|
||||||
|
ViewConnector,
|
||||||
|
InjectedConnector,
|
||||||
|
SnapConnector,
|
||||||
|
DEFAULT_SNAP_ID,
|
||||||
|
} from '@vegaprotocol/wallet';
|
||||||
|
|
||||||
|
const urlParams = new URLSearchParams(window.location.search);
|
||||||
|
|
||||||
|
export const jsonRpc = new JsonRpcConnector();
|
||||||
|
export const injected = new InjectedConnector();
|
||||||
|
export const view = new ViewConnector(urlParams.get('address'));
|
||||||
|
|
||||||
|
export const snap = new SnapConnector(DEFAULT_SNAP_ID);
|
||||||
|
|
||||||
|
export const useConnectors = () => {
|
||||||
|
const featureFlags = useFeatureFlags((state) => state.flags);
|
||||||
|
return useMemo(
|
||||||
|
() => ({
|
||||||
|
injected,
|
||||||
|
jsonRpc,
|
||||||
|
view,
|
||||||
|
snap: featureFlags.METAMASK_SNAPS ? snap : undefined,
|
||||||
|
}),
|
||||||
|
[featureFlags.METAMASK_SNAPS]
|
||||||
|
);
|
||||||
|
};
|
@ -5,6 +5,7 @@ import {
|
|||||||
ProposalState,
|
ProposalState,
|
||||||
VoteValue,
|
VoteValue,
|
||||||
} from '@vegaprotocol/types';
|
} from '@vegaprotocol/types';
|
||||||
|
import { VegaWalletContext } from '@vegaprotocol/wallet';
|
||||||
import { AppStateProvider } from '../../../../contexts/app-state/app-state-provider';
|
import { AppStateProvider } from '../../../../contexts/app-state/app-state-provider';
|
||||||
import {
|
import {
|
||||||
generateNoVotes,
|
generateNoVotes,
|
||||||
@ -15,7 +16,9 @@ import { ProposalHeader, NewTransferSummary } from './proposal-header';
|
|||||||
import {
|
import {
|
||||||
lastWeek,
|
lastWeek,
|
||||||
nextWeek,
|
nextWeek,
|
||||||
|
mockWalletContext,
|
||||||
createUserVoteQueryMock,
|
createUserVoteQueryMock,
|
||||||
|
networkParamsQueryMock,
|
||||||
} from '../../test-helpers/mocks';
|
} from '../../test-helpers/mocks';
|
||||||
import { BrowserRouter } from 'react-router-dom';
|
import { BrowserRouter } from 'react-router-dom';
|
||||||
import { VoteState } from '../vote-details/use-user-vote';
|
import { VoteState } from '../vote-details/use-user-vote';
|
||||||
@ -42,12 +45,14 @@ const renderComponent = (
|
|||||||
render(
|
render(
|
||||||
<AppStateProvider>
|
<AppStateProvider>
|
||||||
<BrowserRouter>
|
<BrowserRouter>
|
||||||
<MockedProvider mocks={mocks}>
|
<MockedProvider mocks={[networkParamsQueryMock, ...mocks]}>
|
||||||
<ProposalHeader
|
<VegaWalletContext.Provider value={mockWalletContext}>
|
||||||
proposal={proposal}
|
<ProposalHeader
|
||||||
isListItem={isListItem}
|
proposal={proposal}
|
||||||
voteState={voteState}
|
isListItem={isListItem}
|
||||||
/>
|
voteState={voteState}
|
||||||
|
/>
|
||||||
|
</VegaWalletContext.Provider>
|
||||||
</MockedProvider>
|
</MockedProvider>
|
||||||
</BrowserRouter>
|
</BrowserRouter>
|
||||||
</AppStateProvider>
|
</AppStateProvider>
|
||||||
@ -155,7 +160,7 @@ describe('Proposal header', () => {
|
|||||||
screen.queryByTestId('proposal-description')
|
screen.queryByTestId('proposal-description')
|
||||||
).not.toBeInTheDocument();
|
).not.toBeInTheDocument();
|
||||||
expect(screen.getByTestId('proposal-details')).toHaveTextContent(
|
expect(screen.getByTestId('proposal-details')).toHaveTextContent(
|
||||||
/Update to market: MarketId/
|
'Update to market ID: MarketId'
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -36,9 +36,6 @@ import { type Proposal, type BatchProposal } from '../../types';
|
|||||||
import { type ProposalTermsFieldsFragment } from '../../__generated__/Proposals';
|
import { type ProposalTermsFieldsFragment } from '../../__generated__/Proposals';
|
||||||
import { differenceInHours, format, formatDistanceToNowStrict } from 'date-fns';
|
import { differenceInHours, format, formatDistanceToNowStrict } from 'date-fns';
|
||||||
import { DATE_FORMAT_DETAILED } from '../../../../lib/date-formats';
|
import { DATE_FORMAT_DETAILED } from '../../../../lib/date-formats';
|
||||||
import { MarketName } from '../proposal/market-name';
|
|
||||||
import { Indicator } from '../proposal/indicator';
|
|
||||||
import { type ProposalNode } from '../proposal/proposal-utils';
|
|
||||||
|
|
||||||
const ProposalTypeTags = ({
|
const ProposalTypeTags = ({
|
||||||
proposal,
|
proposal,
|
||||||
@ -55,8 +52,11 @@ const ProposalTypeTags = ({
|
|||||||
|
|
||||||
if (proposal.__typename === 'BatchProposal') {
|
if (proposal.__typename === 'BatchProposal') {
|
||||||
return (
|
return (
|
||||||
<div data-testid="proposal-type">
|
<div data-testid="proposal-type" className="flex gap-1">
|
||||||
<ProposalInfoLabel variant="secondary">BatchProposal</ProposalInfoLabel>
|
{proposal.subProposals?.map((subProposal, i) => {
|
||||||
|
if (!subProposal?.terms) return null;
|
||||||
|
return <ProposalTypeTag key={i} terms={subProposal.terms} />;
|
||||||
|
})}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -138,77 +138,50 @@ const ProposalDetails = ({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
case 'UpdateMarketState': {
|
case 'UpdateMarketState': {
|
||||||
const marketPageLink = consoleLink(
|
|
||||||
CONSOLE_MARKET_PAGE.replace(':marketId', terms.change.market.id)
|
|
||||||
);
|
|
||||||
return (
|
return (
|
||||||
<span>
|
<span>
|
||||||
{featureFlags.UPDATE_MARKET_STATE &&
|
{featureFlags.UPDATE_MARKET_STATE &&
|
||||||
terms.change?.market?.id &&
|
terms.change?.market?.id &&
|
||||||
terms.change.updateType ? (
|
terms.change.updateType ? (
|
||||||
<>
|
<>
|
||||||
<span>{t(terms.change.updateType)}: </span>
|
{t(terms.change.updateType)}:{' '}
|
||||||
<span className="inline-flex gap-2">
|
{truncateMiddle(terms.change.market.id)}
|
||||||
<span className="break-all">
|
|
||||||
<MarketName marketId={terms.change.market.id} />
|
|
||||||
</span>
|
|
||||||
<span className="inline-flex items-end gap-0">
|
|
||||||
<CopyWithTooltip
|
|
||||||
text={terms.change.market.id}
|
|
||||||
description={t('copyId')}
|
|
||||||
>
|
|
||||||
<button className="inline-block px-1">
|
|
||||||
<VegaIcon size={20} name={VegaIconNames.COPY} />
|
|
||||||
</button>
|
|
||||||
</CopyWithTooltip>
|
|
||||||
<Tooltip description={t('OpenInConsole')} align="center">
|
|
||||||
<Link
|
|
||||||
className="inline-block px-1"
|
|
||||||
to={marketPageLink}
|
|
||||||
target="_blank"
|
|
||||||
>
|
|
||||||
<VegaIcon
|
|
||||||
size={20}
|
|
||||||
name={VegaIconNames.OPEN_EXTERNAL}
|
|
||||||
/>
|
|
||||||
</Link>
|
|
||||||
</Tooltip>
|
|
||||||
</span>
|
|
||||||
</span>
|
|
||||||
</>
|
</>
|
||||||
) : null}
|
) : null}
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
case 'UpdateMarket': {
|
case 'UpdateMarket': {
|
||||||
const marketPageLink = consoleLink(
|
|
||||||
CONSOLE_MARKET_PAGE.replace(':marketId', terms.change.marketId)
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<span>{t('UpdateToMarket')}: </span>
|
<span>{t('UpdateToMarket')}:</span>{' '}
|
||||||
<span className="inline-flex items-start gap-2">
|
<span className="inline-flex items-start gap-2">
|
||||||
<span className="break-all">
|
<span className="break-all">{terms.change.marketId} </span>
|
||||||
<MarketName marketId={terms.change.marketId} />
|
|
||||||
</span>
|
|
||||||
<span className="inline-flex items-end gap-0">
|
<span className="inline-flex items-end gap-0">
|
||||||
<CopyWithTooltip
|
<CopyWithTooltip
|
||||||
text={terms.change.marketId}
|
text={terms.change.marketId}
|
||||||
description={t('copyId')}
|
description={t('copyToClipboard')}
|
||||||
>
|
>
|
||||||
<button className="inline-block px-1">
|
<button className="inline-block px-1">
|
||||||
<VegaIcon size={20} name={VegaIconNames.COPY} />
|
<VegaIcon size={20} name={VegaIconNames.COPY} />
|
||||||
</button>
|
</button>
|
||||||
</CopyWithTooltip>
|
</CopyWithTooltip>
|
||||||
<Tooltip description={t('OpenInConsole')} align="center">
|
<Tooltip description={t('OpenInConsole')} align="center">
|
||||||
<Link
|
<button
|
||||||
className="inline-block px-1"
|
className="inline-block px-1"
|
||||||
target="_blank"
|
onClick={() => {
|
||||||
to={marketPageLink}
|
const marketPageLink = consoleLink(
|
||||||
|
CONSOLE_MARKET_PAGE.replace(
|
||||||
|
':marketId',
|
||||||
|
// @ts-ignore ts doesn't like this field even though its already a string above???
|
||||||
|
terms.change.marketId
|
||||||
|
)
|
||||||
|
);
|
||||||
|
window.open(marketPageLink, '_blank');
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<VegaIcon size={20} name={VegaIconNames.OPEN_EXTERNAL} />
|
<VegaIcon size={20} name={VegaIconNames.OPEN_EXTERNAL} />
|
||||||
</Link>
|
</button>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
@ -251,7 +224,7 @@ const ProposalDetails = ({
|
|||||||
}}
|
}}
|
||||||
components={{
|
components={{
|
||||||
// @ts-ignore children passed by i18next
|
// @ts-ignore children passed by i18next
|
||||||
lozenge: <Lozenge className="text-xs" />,
|
lozenge: <Lozenge />,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
@ -313,18 +286,12 @@ const ProposalDetails = ({
|
|||||||
{proposal.subProposals.map((p, i) => {
|
{proposal.subProposals.map((p, i) => {
|
||||||
if (!p?.terms) return null;
|
if (!p?.terms) return null;
|
||||||
return (
|
return (
|
||||||
<li
|
<li key={i}>
|
||||||
key={i}
|
<div>{renderDetails(p.terms)}</div>
|
||||||
className="grid grid-cols-[40px_minmax(0,1fr)] grid-rows-1 gap-3 items-center"
|
<SubProposalStateText
|
||||||
>
|
state={proposal.state}
|
||||||
<Indicator indicator={i + 1} />
|
enactmentDatetime={p.terms.enactmentDatetime}
|
||||||
<span>
|
/>
|
||||||
<div>{renderDetails(p.terms)}</div>
|
|
||||||
<SubProposalStateText
|
|
||||||
state={proposal.state}
|
|
||||||
enactmentDatetime={p.terms.enactmentDatetime}
|
|
||||||
/>
|
|
||||||
</span>
|
|
||||||
</li>
|
</li>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
@ -541,12 +508,10 @@ const BatchProposalStateText = ({
|
|||||||
|
|
||||||
export const ProposalHeader = ({
|
export const ProposalHeader = ({
|
||||||
proposal,
|
proposal,
|
||||||
restData,
|
|
||||||
isListItem = true,
|
isListItem = true,
|
||||||
voteState,
|
voteState,
|
||||||
}: {
|
}: {
|
||||||
proposal: Proposal | BatchProposal;
|
proposal: Proposal | BatchProposal;
|
||||||
restData?: ProposalNode | null;
|
|
||||||
isListItem?: boolean;
|
isListItem?: boolean;
|
||||||
voteState?: VoteState | null;
|
voteState?: VoteState | null;
|
||||||
}) => {
|
}) => {
|
||||||
@ -598,7 +563,7 @@ export const ProposalHeader = ({
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<ProposalDetails proposal={proposal} />
|
<ProposalDetails proposal={proposal} />
|
||||||
<VoteBreakdown proposal={proposal} restData={restData} />
|
<VoteBreakdown proposal={proposal} />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -3,8 +3,16 @@ import { useTranslation } from 'react-i18next';
|
|||||||
import { SyntaxHighlighter } from '@vegaprotocol/ui-toolkit';
|
import { SyntaxHighlighter } from '@vegaprotocol/ui-toolkit';
|
||||||
import { SubHeading } from '../../../../components/heading';
|
import { SubHeading } from '../../../../components/heading';
|
||||||
import { CollapsibleToggle } from '../../../../components/collapsible-toggle';
|
import { CollapsibleToggle } from '../../../../components/collapsible-toggle';
|
||||||
|
import {
|
||||||
|
type BatchProposalFieldsFragment,
|
||||||
|
type ProposalFieldsFragment,
|
||||||
|
} from '../../__generated__/Proposals';
|
||||||
|
|
||||||
export const ProposalJson = ({ proposal }: { proposal?: unknown }) => {
|
export const ProposalJson = ({
|
||||||
|
proposal,
|
||||||
|
}: {
|
||||||
|
proposal: ProposalFieldsFragment | BatchProposalFieldsFragment;
|
||||||
|
}) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [showDetails, setShowDetails] = useState(false);
|
const [showDetails, setShowDetails] = useState(false);
|
||||||
|
|
||||||
@ -18,7 +26,7 @@ export const ProposalJson = ({ proposal }: { proposal?: unknown }) => {
|
|||||||
<SubHeading title={t('proposalJson')} />
|
<SubHeading title={t('proposalJson')} />
|
||||||
</CollapsibleToggle>
|
</CollapsibleToggle>
|
||||||
|
|
||||||
{showDetails && <SyntaxHighlighter size="smaller" data={proposal} />}
|
{showDetails && <SyntaxHighlighter data={proposal} />}
|
||||||
</section>
|
</section>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -5,11 +5,6 @@ import {
|
|||||||
} from './proposal-market-changes';
|
} from './proposal-market-changes';
|
||||||
import type { JsonValue } from '../../../../components/json-diff';
|
import type { JsonValue } from '../../../../components/json-diff';
|
||||||
|
|
||||||
jest.mock('../proposal/market-name.tsx', () => ({
|
|
||||||
...jest.requireActual('../proposal/market-name.tsx'),
|
|
||||||
MarketName: jest.fn(),
|
|
||||||
}));
|
|
||||||
|
|
||||||
describe('applyImmutableKeysFromEarlierVersion', () => {
|
describe('applyImmutableKeysFromEarlierVersion', () => {
|
||||||
it('returns an empty object if any argument is not an object or null', () => {
|
it('returns an empty object if any argument is not an object or null', () => {
|
||||||
const earlierVersion: JsonValue = null;
|
const earlierVersion: JsonValue = null;
|
||||||
@ -62,21 +57,21 @@ describe('applyImmutableKeysFromEarlierVersion', () => {
|
|||||||
describe('ProposalMarketChanges', () => {
|
describe('ProposalMarketChanges', () => {
|
||||||
it('renders correctly', () => {
|
it('renders correctly', () => {
|
||||||
const { getByTestId } = render(
|
const { getByTestId } = render(
|
||||||
<ProposalMarketChanges marketId="market-id" updateProposalNode={null} />
|
<ProposalMarketChanges marketId="market-id" updatedProposal={{}} />
|
||||||
);
|
);
|
||||||
expect(getByTestId('proposal-market-changes')).toBeInTheDocument();
|
expect(getByTestId('proposal-market-changes')).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('JsonDiff is not visible when showChanges is false', () => {
|
it('JsonDiff is not visible when showChanges is false', () => {
|
||||||
const { queryByTestId } = render(
|
const { queryByTestId } = render(
|
||||||
<ProposalMarketChanges marketId="market-id" updateProposalNode={null} />
|
<ProposalMarketChanges marketId="market-id" updatedProposal={{}} />
|
||||||
);
|
);
|
||||||
expect(queryByTestId('json-diff')).not.toBeInTheDocument();
|
expect(queryByTestId('json-diff')).not.toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('JsonDiff is visible when showChanges is true', async () => {
|
it('JsonDiff is visible when showChanges is true', async () => {
|
||||||
const { getByTestId } = render(
|
const { getByTestId } = render(
|
||||||
<ProposalMarketChanges marketId="market-id" updateProposalNode={null} />
|
<ProposalMarketChanges marketId="market-id" updatedProposal={{}} />
|
||||||
);
|
);
|
||||||
fireEvent.click(getByTestId('proposal-market-changes-toggle'));
|
fireEvent.click(getByTestId('proposal-market-changes-toggle'));
|
||||||
expect(getByTestId('json-diff')).toBeInTheDocument();
|
expect(getByTestId('json-diff')).toBeInTheDocument();
|
||||||
|
@ -1,24 +1,14 @@
|
|||||||
import cloneDeep from 'lodash/cloneDeep';
|
import cloneDeep from 'lodash/cloneDeep';
|
||||||
import set from 'lodash/set';
|
import set from 'lodash/set';
|
||||||
import get from 'lodash/get';
|
import get from 'lodash/get';
|
||||||
import compact from 'lodash/compact';
|
import { JsonDiff } from '../../../../components/json-diff';
|
||||||
import orderBy from 'lodash/orderBy';
|
|
||||||
import { JsonDiff, type JsonValue } from '../../../../components/json-diff';
|
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { CollapsibleToggle } from '../../../../components/collapsible-toggle';
|
import { CollapsibleToggle } from '../../../../components/collapsible-toggle';
|
||||||
import { SubHeading } from '../../../../components/heading';
|
import { SubHeading } from '../../../../components/heading';
|
||||||
import {
|
import type { JsonValue } from '../../../../components/json-diff';
|
||||||
useFetchProposal,
|
import { useFetch } from '@vegaprotocol/react-helpers';
|
||||||
useFetchProposals,
|
import { ENV } from '../../../../config';
|
||||||
flatten,
|
|
||||||
isBatchProposalNode,
|
|
||||||
isSingleProposalNode,
|
|
||||||
type ProposalNode,
|
|
||||||
type SingleProposalData,
|
|
||||||
type SubProposalData,
|
|
||||||
} from '../proposal/proposal-utils';
|
|
||||||
import { MarketName } from '../proposal/market-name';
|
|
||||||
|
|
||||||
const immutableKeys = [
|
const immutableKeys = [
|
||||||
'decimalPlaces',
|
'decimalPlaces',
|
||||||
@ -28,8 +18,8 @@ const immutableKeys = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
export const applyImmutableKeysFromEarlierVersion = (
|
export const applyImmutableKeysFromEarlierVersion = (
|
||||||
earlierVersion: unknown,
|
earlierVersion: JsonValue,
|
||||||
updatedVersion: unknown
|
updatedVersion: JsonValue
|
||||||
) => {
|
) => {
|
||||||
if (
|
if (
|
||||||
typeof earlierVersion !== 'object' ||
|
typeof earlierVersion !== 'object' ||
|
||||||
@ -45,8 +35,7 @@ export const applyImmutableKeysFromEarlierVersion = (
|
|||||||
|
|
||||||
// Overwrite the immutable keys in the updatedVersionCopy with the earlier values
|
// Overwrite the immutable keys in the updatedVersionCopy with the earlier values
|
||||||
immutableKeys.forEach((key) => {
|
immutableKeys.forEach((key) => {
|
||||||
const earlier = get(earlierVersion, key);
|
set(updatedVersionCopy, key, get(earlierVersion, key));
|
||||||
if (earlier) set(updatedVersionCopy, key, earlier);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return updatedVersionCopy;
|
return updatedVersionCopy;
|
||||||
@ -54,84 +43,49 @@ export const applyImmutableKeysFromEarlierVersion = (
|
|||||||
|
|
||||||
interface ProposalMarketChangesProps {
|
interface ProposalMarketChangesProps {
|
||||||
marketId: string;
|
marketId: string;
|
||||||
/** This are the changes from proposal */
|
updatedProposal: JsonValue;
|
||||||
updateProposalNode: ProposalNode | null;
|
|
||||||
indicator?: number;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ProposalMarketChanges = ({
|
export const ProposalMarketChanges = ({
|
||||||
marketId,
|
marketId,
|
||||||
updateProposalNode,
|
updatedProposal,
|
||||||
indicator,
|
|
||||||
}: ProposalMarketChangesProps) => {
|
}: ProposalMarketChangesProps) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [showChanges, setShowChanges] = useState(false);
|
const [showChanges, setShowChanges] = useState(false);
|
||||||
|
|
||||||
const { data: originalProposalData } = useFetchProposal({
|
const {
|
||||||
proposalId: marketId,
|
state: { data },
|
||||||
});
|
} = useFetch(`${ENV.rest}governance?proposalId=${marketId}`, undefined, true);
|
||||||
|
|
||||||
const { data: enactedProposalsData } = useFetchProposals({
|
const {
|
||||||
proposalState: 'STATE_ENACTED',
|
state: { data: enactedProposalData },
|
||||||
proposalType: 'TYPE_UPDATE_MARKET',
|
} = useFetch(
|
||||||
});
|
`${ENV.rest}governances?proposalState=STATE_ENACTED&proposalType=TYPE_UPDATE_MARKET`,
|
||||||
|
undefined,
|
||||||
let updateProposal: SingleProposalData | SubProposalData | undefined;
|
true
|
||||||
if (isBatchProposalNode(updateProposalNode)) {
|
|
||||||
updateProposal = updateProposalNode.proposals.find(
|
|
||||||
(p, i) =>
|
|
||||||
p.terms.updateMarket?.marketId === marketId &&
|
|
||||||
(indicator != null ? i === indicator - 1 : true)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (isSingleProposalNode(updateProposalNode)) {
|
|
||||||
updateProposal = updateProposalNode.proposal;
|
|
||||||
}
|
|
||||||
|
|
||||||
// this should get the proposal before the current one
|
|
||||||
const enactedUpdateMarketProposals = orderBy(
|
|
||||||
compact(
|
|
||||||
flatten(enactedProposalsData).filter((enacted) => {
|
|
||||||
const related = enacted.terms.updateMarket?.marketId === marketId;
|
|
||||||
const notCurrent =
|
|
||||||
enacted.id !== updateProposal?.id ||
|
|
||||||
('batchId' in enacted && enacted.batchId !== updateProposal.id);
|
|
||||||
const beforeCurrent =
|
|
||||||
Number(enacted.terms.enactmentTimestamp) <
|
|
||||||
Number(updateProposal?.terms.enactmentTimestamp);
|
|
||||||
return related && notCurrent && beforeCurrent;
|
|
||||||
})
|
|
||||||
),
|
|
||||||
[(proposal) => Number(proposal.terms.enactmentTimestamp)],
|
|
||||||
'desc'
|
|
||||||
);
|
);
|
||||||
|
|
||||||
const latestEnactedProposal =
|
// @ts-ignore no types here :-/
|
||||||
enactedUpdateMarketProposals.length > 0
|
const enacted = enactedProposalData?.connection?.edges
|
||||||
? enactedUpdateMarketProposals[0]
|
.filter(
|
||||||
: undefined;
|
// @ts-ignore no type here
|
||||||
|
({ node }) => node?.proposal?.terms?.updateMarket?.marketId === marketId
|
||||||
|
)
|
||||||
|
// @ts-ignore no type here
|
||||||
|
.sort((a, b) => {
|
||||||
|
return (
|
||||||
|
new Date(a?.node?.terms?.enactmentTimestamp).getTime() -
|
||||||
|
new Date(b?.node?.terms?.enactmentTimestamp).getTime()
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
let originalProposal;
|
const latestEnactedProposal = enacted?.length
|
||||||
if (isBatchProposalNode(originalProposalData)) {
|
? enacted[enacted.length - 1]
|
||||||
originalProposal = originalProposalData.proposals.find(
|
: undefined;
|
||||||
(proposal) => proposal.id === marketId && proposal.terms.newMarket != null
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (isSingleProposalNode(originalProposalData)) {
|
|
||||||
originalProposal = originalProposalData.proposal;
|
|
||||||
}
|
|
||||||
|
|
||||||
// LEFT SIDE: update market proposal enacted just before this one
|
const originalProposal =
|
||||||
// or original new market proposal
|
// @ts-ignore no types with useFetch TODO: check this is good
|
||||||
const left =
|
data?.data?.proposal?.terms?.newMarket?.changes;
|
||||||
latestEnactedProposal?.terms.updateMarket?.changes ||
|
|
||||||
originalProposal?.terms.newMarket?.changes;
|
|
||||||
|
|
||||||
// RIGHT SIDE: this update market proposal
|
|
||||||
const right = applyImmutableKeysFromEarlierVersion(
|
|
||||||
left,
|
|
||||||
updateProposal?.terms.updateMarket?.changes
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section data-testid="proposal-market-changes">
|
<section data-testid="proposal-market-changes">
|
||||||
@ -140,18 +94,25 @@ export const ProposalMarketChanges = ({
|
|||||||
setToggleState={setShowChanges}
|
setToggleState={setShowChanges}
|
||||||
dataTestId={'proposal-market-changes-toggle'}
|
dataTestId={'proposal-market-changes-toggle'}
|
||||||
>
|
>
|
||||||
<SubHeading
|
<SubHeading title={t('updatesToMarket')} />
|
||||||
title={
|
|
||||||
<>
|
|
||||||
{t('UpdateToMarket')}: <MarketName marketId={marketId} truncate />
|
|
||||||
</>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</CollapsibleToggle>
|
</CollapsibleToggle>
|
||||||
|
|
||||||
{showChanges && (
|
{showChanges && (
|
||||||
<div className="mb-6 bg-vega-cdark-900 p-2 rounded-lg">
|
<div className="mb-6">
|
||||||
<JsonDiff left={left as JsonValue} right={right as JsonValue} />
|
<JsonDiff
|
||||||
|
left={latestEnactedProposal || originalProposal}
|
||||||
|
right={
|
||||||
|
latestEnactedProposal
|
||||||
|
? applyImmutableKeysFromEarlierVersion(
|
||||||
|
latestEnactedProposal,
|
||||||
|
updatedProposal
|
||||||
|
)
|
||||||
|
: applyImmutableKeysFromEarlierVersion(
|
||||||
|
originalProposal,
|
||||||
|
updatedProposal
|
||||||
|
)
|
||||||
|
}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</section>
|
</section>
|
||||||
|
@ -2,11 +2,6 @@ import { fireEvent, render, screen } from '@testing-library/react';
|
|||||||
import { ProposalUpdateMarketState } from './proposal-update-market-state';
|
import { ProposalUpdateMarketState } from './proposal-update-market-state';
|
||||||
import { MarketUpdateType } from '@vegaprotocol/types';
|
import { MarketUpdateType } from '@vegaprotocol/types';
|
||||||
|
|
||||||
jest.mock('../proposal/market-name.tsx', () => ({
|
|
||||||
...jest.requireActual('../proposal/market-name.tsx'),
|
|
||||||
MarketName: jest.fn(),
|
|
||||||
}));
|
|
||||||
|
|
||||||
describe('<ProposalUpdateMarketState />', () => {
|
describe('<ProposalUpdateMarketState />', () => {
|
||||||
const suspendProposal = {
|
const suspendProposal = {
|
||||||
__typename: 'UpdateMarketState' as const,
|
__typename: 'UpdateMarketState' as const,
|
||||||
|
@ -9,8 +9,6 @@ import { useState } from 'react';
|
|||||||
import { CollapsibleToggle } from '../../../../components/collapsible-toggle';
|
import { CollapsibleToggle } from '../../../../components/collapsible-toggle';
|
||||||
import { SubHeading } from '../../../../components/heading';
|
import { SubHeading } from '../../../../components/heading';
|
||||||
import { type UpdateMarketStatesFragment } from '../../__generated__/Proposals';
|
import { type UpdateMarketStatesFragment } from '../../__generated__/Proposals';
|
||||||
import { MarketUpdateTypeMapping } from '@vegaprotocol/types';
|
|
||||||
import { MarketName } from '../proposal/market-name';
|
|
||||||
|
|
||||||
interface ProposalUpdateMarketStateProps {
|
interface ProposalUpdateMarketStateProps {
|
||||||
change: UpdateMarketStatesFragment | null;
|
change: UpdateMarketStatesFragment | null;
|
||||||
@ -21,18 +19,16 @@ export const ProposalUpdateMarketState = ({
|
|||||||
}: ProposalUpdateMarketStateProps) => {
|
}: ProposalUpdateMarketStateProps) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [showDetails, setShowDetails] = useState(false);
|
const [showDetails, setShowDetails] = useState(false);
|
||||||
|
let market;
|
||||||
|
let isTerminate = false;
|
||||||
|
|
||||||
if (!change || change.__typename !== 'UpdateMarketState') {
|
if (!change) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const market = change?.market;
|
if (change.__typename === 'UpdateMarketState') {
|
||||||
const isTerminate =
|
market = change?.market;
|
||||||
change?.updateType === 'MARKET_STATE_UPDATE_TYPE_TERMINATE';
|
isTerminate = change?.updateType === 'MARKET_STATE_UPDATE_TYPE_TERMINATE';
|
||||||
|
|
||||||
let toggleTitle = t(change.updateType);
|
|
||||||
if (toggleTitle.length === 0) {
|
|
||||||
toggleTitle = t('MarketDetails');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -42,13 +38,7 @@ export const ProposalUpdateMarketState = ({
|
|||||||
setToggleState={setShowDetails}
|
setToggleState={setShowDetails}
|
||||||
dataTestId="proposal-market-data-toggle"
|
dataTestId="proposal-market-data-toggle"
|
||||||
>
|
>
|
||||||
<SubHeading
|
<SubHeading title={t('MarketDetails')} />
|
||||||
title={
|
|
||||||
<>
|
|
||||||
{toggleTitle}: <MarketName marketId={market?.id} />
|
|
||||||
</>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</CollapsibleToggle>
|
</CollapsibleToggle>
|
||||||
|
|
||||||
{showDetails && (
|
{showDetails && (
|
||||||
@ -59,12 +49,6 @@ export const ProposalUpdateMarketState = ({
|
|||||||
{t('marketId')}
|
{t('marketId')}
|
||||||
{market?.id}
|
{market?.id}
|
||||||
</KeyValueTableRow>
|
</KeyValueTableRow>
|
||||||
<KeyValueTableRow>
|
|
||||||
{t('State')}
|
|
||||||
<span className="bg-vega-green-650 px-1">
|
|
||||||
{MarketUpdateTypeMapping[change.updateType]}
|
|
||||||
</span>
|
|
||||||
</KeyValueTableRow>
|
|
||||||
<KeyValueTableRow>
|
<KeyValueTableRow>
|
||||||
{t('marketName')}
|
{t('marketName')}
|
||||||
{market?.tradableInstrument?.instrument?.name}
|
{market?.tradableInstrument?.instrument?.name}
|
||||||
|
@ -1,46 +0,0 @@
|
|||||||
import classNames from 'classnames';
|
|
||||||
|
|
||||||
// rainbow-ish order
|
|
||||||
const COLOURS = ['red', 'pink', 'orange', 'yellow', 'green', 'blue', 'purple'];
|
|
||||||
|
|
||||||
const getColour = (indicator: number, max = COLOURS.length) => {
|
|
||||||
const available =
|
|
||||||
max < COLOURS.length ? COLOURS.slice(COLOURS.length - max) : COLOURS;
|
|
||||||
const tiers = Object.keys(available).length;
|
|
||||||
let index = Math.abs(indicator - 1);
|
|
||||||
if (indicator >= tiers) {
|
|
||||||
index = index % tiers;
|
|
||||||
}
|
|
||||||
return available[index];
|
|
||||||
};
|
|
||||||
|
|
||||||
export const getStyle = (indicator: number, max = COLOURS.length) =>
|
|
||||||
classNames({
|
|
||||||
'bg-vega-yellow-400 before:bg-vega-yellow-400':
|
|
||||||
'yellow' === getColour(indicator, max),
|
|
||||||
'bg-vega-green-400 before:bg-vega-green-400':
|
|
||||||
'green' === getColour(indicator, max),
|
|
||||||
'bg-vega-blue-400 before:bg-vega-blue-400':
|
|
||||||
'blue' === getColour(indicator, max),
|
|
||||||
'bg-vega-purple-400 before:bg-vega-purple-400':
|
|
||||||
'purple' === getColour(indicator, max),
|
|
||||||
'bg-vega-pink-400 before:bg-vega-pink-400':
|
|
||||||
'pink' === getColour(indicator, max),
|
|
||||||
'bg-vega-orange-400 before:bg-vega-orange-400':
|
|
||||||
'orange' === getColour(indicator, max),
|
|
||||||
'bg-vega-red-400 before:bg-vega-red-400':
|
|
||||||
'red' === getColour(indicator, max),
|
|
||||||
'bg-vega-clight-600 before:bg-vega-clight-600':
|
|
||||||
'none' === getColour(indicator, max),
|
|
||||||
});
|
|
||||||
|
|
||||||
export const getIndicatorStyle = (indicator: number) =>
|
|
||||||
classNames(
|
|
||||||
'rounded-sm text-black inline-block px-1 py-1 font-alpha calt h-8 w-7 text-center',
|
|
||||||
'text-border-1',
|
|
||||||
getStyle(indicator),
|
|
||||||
// Comment below if you want to remove the "chevron"
|
|
||||||
'relative mr-[11px] z-1',
|
|
||||||
'before:absolute before:z-0 before:top-1 before:right-[-11px] before:rounded-sm',
|
|
||||||
"before:w-[22.62px] before:h-[22.62px] before:rotate-45 before:content-['']"
|
|
||||||
);
|
|
@ -1,9 +0,0 @@
|
|||||||
import { getIndicatorStyle } from './colours';
|
|
||||||
|
|
||||||
export const Indicator = ({ indicator }: { indicator: number }) => (
|
|
||||||
<div className={getIndicatorStyle(indicator)}>
|
|
||||||
<span className="absolute top-0 left-0 p-1 w-full text-center z-1">
|
|
||||||
{indicator}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
);
|
|
@ -1,21 +0,0 @@
|
|||||||
import { useMarketInfoQuery } from '@vegaprotocol/markets';
|
|
||||||
import { truncateMiddle } from '@vegaprotocol/ui-toolkit';
|
|
||||||
|
|
||||||
export const MarketName = ({
|
|
||||||
marketId,
|
|
||||||
truncate,
|
|
||||||
}: {
|
|
||||||
marketId?: string;
|
|
||||||
truncate?: boolean;
|
|
||||||
}) => {
|
|
||||||
const { data } = useMarketInfoQuery({
|
|
||||||
variables: {
|
|
||||||
marketId: marketId || '',
|
|
||||||
},
|
|
||||||
skip: !marketId,
|
|
||||||
});
|
|
||||||
|
|
||||||
const id = truncate ? truncateMiddle(marketId || '') : marketId;
|
|
||||||
|
|
||||||
return <span>{data?.market?.tradableInstrument.instrument.code || id}</span>;
|
|
||||||
};
|
|
@ -1,4 +1,3 @@
|
|||||||
import { Trans, useTranslation } from 'react-i18next';
|
|
||||||
import { type ProposalTermsFieldsFragment } from '../../__generated__/Proposals';
|
import { type ProposalTermsFieldsFragment } from '../../__generated__/Proposals';
|
||||||
import { type Proposal, type BatchProposal } from '../../types';
|
import { type Proposal, type BatchProposal } from '../../types';
|
||||||
import { ListAsset } from '../list-asset';
|
import { ListAsset } from '../list-asset';
|
||||||
@ -13,29 +12,21 @@ import {
|
|||||||
import { ProposalUpdateBenefitTiers } from '../proposal-update-benefit-tiers';
|
import { ProposalUpdateBenefitTiers } from '../proposal-update-benefit-tiers';
|
||||||
import { ProposalUpdateMarketState } from '../proposal-update-market-state';
|
import { ProposalUpdateMarketState } from '../proposal-update-market-state';
|
||||||
import { ProposalVolumeDiscountProgramDetails } from '../proposal-volume-discount-program-details';
|
import { ProposalVolumeDiscountProgramDetails } from '../proposal-volume-discount-program-details';
|
||||||
import { type ProposalNode } from './proposal-utils';
|
|
||||||
import { Lozenge } from '@vegaprotocol/ui-toolkit';
|
|
||||||
import { Indicator } from './indicator';
|
|
||||||
import { SubHeading } from '../../../../components/heading';
|
|
||||||
|
|
||||||
export const ProposalChangeDetails = ({
|
export const ProposalChangeDetails = ({
|
||||||
proposal,
|
proposal,
|
||||||
terms,
|
terms,
|
||||||
restData,
|
restData,
|
||||||
indicator,
|
|
||||||
}: {
|
}: {
|
||||||
proposal: Proposal | BatchProposal;
|
proposal: Proposal | BatchProposal;
|
||||||
terms: ProposalTermsFieldsFragment;
|
terms: ProposalTermsFieldsFragment;
|
||||||
restData: ProposalNode | null;
|
// eslint-disable-next-line
|
||||||
indicator?: number;
|
restData: any;
|
||||||
}) => {
|
}) => {
|
||||||
const { t } = useTranslation();
|
|
||||||
let details = null;
|
|
||||||
|
|
||||||
switch (terms.change.__typename) {
|
switch (terms.change.__typename) {
|
||||||
case 'NewAsset': {
|
case 'NewAsset': {
|
||||||
if (proposal.id && terms.change.source.__typename === 'ERC20') {
|
if (proposal.id && terms.change.source.__typename === 'ERC20') {
|
||||||
details = (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<ListAsset
|
<ListAsset
|
||||||
assetId={proposal.id}
|
assetId={proposal.id}
|
||||||
@ -46,64 +37,62 @@ export const ProposalChangeDetails = ({
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
break;
|
return null;
|
||||||
}
|
}
|
||||||
case 'UpdateAsset': {
|
case 'UpdateAsset': {
|
||||||
if (proposal.id) {
|
if (proposal.id) {
|
||||||
details = (
|
return (
|
||||||
<ProposalAssetDetails
|
<ProposalAssetDetails
|
||||||
change={terms.change}
|
change={terms.change}
|
||||||
assetId={terms.change.assetId}
|
assetId={terms.change.assetId}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
break;
|
return null;
|
||||||
}
|
}
|
||||||
case 'NewMarket': {
|
case 'NewMarket': {
|
||||||
if (proposal.id) {
|
if (proposal.id) {
|
||||||
details = <ProposalMarketData proposalId={proposal.id} />;
|
return <ProposalMarketData proposalId={proposal.id} />;
|
||||||
}
|
}
|
||||||
break;
|
return null;
|
||||||
}
|
}
|
||||||
case 'UpdateMarket': {
|
case 'UpdateMarket': {
|
||||||
if (proposal.id) {
|
if (proposal.id) {
|
||||||
details = (
|
return (
|
||||||
<div className="flex flex-col gap-4">
|
<div className="flex flex-col gap-4">
|
||||||
<ProposalMarketData proposalId={proposal.id} />
|
<ProposalMarketData proposalId={proposal.id} />
|
||||||
<ProposalMarketChanges
|
<ProposalMarketChanges
|
||||||
indicator={indicator}
|
|
||||||
marketId={terms.change.marketId}
|
marketId={terms.change.marketId}
|
||||||
updateProposalNode={restData}
|
updatedProposal={
|
||||||
|
restData?.data?.proposal?.terms?.updateMarket?.changes
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
return null;
|
||||||
}
|
}
|
||||||
case 'NewTransfer': {
|
case 'NewTransfer': {
|
||||||
if (proposal.id) {
|
if (proposal.id) {
|
||||||
details = <ProposalTransferDetails proposalId={proposal.id} />;
|
return <ProposalTransferDetails proposalId={proposal.id} />;
|
||||||
}
|
}
|
||||||
break;
|
return null;
|
||||||
}
|
}
|
||||||
case 'CancelTransfer': {
|
case 'CancelTransfer': {
|
||||||
if (proposal.id) {
|
if (proposal.id) {
|
||||||
details = <ProposalCancelTransferDetails proposalId={proposal.id} />;
|
return <ProposalCancelTransferDetails proposalId={proposal.id} />;
|
||||||
}
|
}
|
||||||
break;
|
return null;
|
||||||
}
|
}
|
||||||
case 'UpdateMarketState': {
|
case 'UpdateMarketState': {
|
||||||
details = <ProposalUpdateMarketState change={terms.change} />;
|
return <ProposalUpdateMarketState change={terms.change} />;
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
case 'UpdateReferralProgram': {
|
case 'UpdateReferralProgram': {
|
||||||
details = <ProposalReferralProgramDetails change={terms.change} />;
|
return <ProposalReferralProgramDetails change={terms.change} />;
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
case 'UpdateVolumeDiscountProgram': {
|
case 'UpdateVolumeDiscountProgram': {
|
||||||
details = <ProposalVolumeDiscountProgramDetails change={terms.change} />;
|
return <ProposalVolumeDiscountProgramDetails change={terms.change} />;
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
case 'UpdateNetworkParameter': {
|
case 'UpdateNetworkParameter': {
|
||||||
if (
|
if (
|
||||||
@ -111,48 +100,18 @@ export const ProposalChangeDetails = ({
|
|||||||
terms.change.networkParameter.key ===
|
terms.change.networkParameter.key ===
|
||||||
'rewards.activityStreak.benefitTiers'
|
'rewards.activityStreak.benefitTiers'
|
||||||
) {
|
) {
|
||||||
details = <ProposalUpdateBenefitTiers change={terms.change} />;
|
return <ProposalUpdateBenefitTiers change={terms.change} />;
|
||||||
} else {
|
|
||||||
details = (
|
|
||||||
<div className="mb-4">
|
|
||||||
<SubHeading title={t(terms.change.__typename as string)} />
|
|
||||||
<span>
|
|
||||||
<Trans
|
|
||||||
i18nKey="Change <lozenge>{{key}}</lozenge> to <lozenge>{{value}}</lozenge>"
|
|
||||||
values={{
|
|
||||||
key: terms.change.networkParameter.key,
|
|
||||||
value: terms.change.networkParameter.value,
|
|
||||||
}}
|
|
||||||
components={{
|
|
||||||
// @ts-ignore children passed by i18next
|
|
||||||
lozenge: <Lozenge />,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
return null;
|
||||||
}
|
}
|
||||||
case 'NewFreeform':
|
case 'NewFreeform':
|
||||||
case 'NewSpotMarket':
|
case 'NewSpotMarket':
|
||||||
case 'UpdateSpotMarket':
|
case 'UpdateSpotMarket': {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
default: {
|
default: {
|
||||||
break;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (indicator != null && details != null) {
|
|
||||||
details = (
|
|
||||||
<div className="grid grid-cols-[40px_minmax(0,1fr)] grid-rows-1 gap-3 mb-3">
|
|
||||||
<div className="w-10">
|
|
||||||
<Indicator indicator={indicator} />
|
|
||||||
</div>
|
|
||||||
<div>{details}</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return details;
|
|
||||||
};
|
};
|
||||||
|
@ -1,283 +0,0 @@
|
|||||||
import compact from 'lodash/compact';
|
|
||||||
import { ENV } from '../../../../config';
|
|
||||||
import { useEffect, useState } from 'react';
|
|
||||||
|
|
||||||
type Maybe<T> = T | null | undefined;
|
|
||||||
|
|
||||||
type ProposalState =
|
|
||||||
| 'STATE_UNSPECIFIED'
|
|
||||||
| 'STATE_FAILED'
|
|
||||||
| 'STATE_OPEN'
|
|
||||||
| 'STATE_PASSED'
|
|
||||||
| 'STATE_REJECTED'
|
|
||||||
| 'STATE_DECLINED'
|
|
||||||
| 'STATE_ENACTED'
|
|
||||||
| 'STATE_WAITING_FOR_NODE_VOTE';
|
|
||||||
|
|
||||||
type ProposalType =
|
|
||||||
| 'TYPE_UNSPECIFIED'
|
|
||||||
| 'TYPE_ALL'
|
|
||||||
| 'TYPE_NEW_MARKET'
|
|
||||||
| 'TYPE_UPDATE_MARKET'
|
|
||||||
| 'TYPE_NETWORK_PARAMETERS'
|
|
||||||
| 'TYPE_NEW_ASSET'
|
|
||||||
| 'TYPE_NEW_FREE_FORM'
|
|
||||||
| 'TYPE_UPDATE_ASSET'
|
|
||||||
| 'TYPE_NEW_SPOT_MARKET'
|
|
||||||
| 'TYPE_UPDATE_SPOT_MARKET'
|
|
||||||
| 'TYPE_NEW_TRANSFER'
|
|
||||||
| 'TYPE_CANCEL_TRANSFER'
|
|
||||||
| 'TYPE_UPDATE_MARKET_STATE'
|
|
||||||
| 'TYPE_UPDATE_REFERRAL_PROGRAM'
|
|
||||||
| 'TYPE_UPDATE_VOLUME_DISCOUNT_PROGRAM';
|
|
||||||
|
|
||||||
type ProposalNodeType = 'TYPE_SINGLE_OR_UNSPECIFIED' | 'TYPE_BATCH';
|
|
||||||
|
|
||||||
type ProposalData = {
|
|
||||||
id: string;
|
|
||||||
rationale: {
|
|
||||||
description: string;
|
|
||||||
title: string;
|
|
||||||
};
|
|
||||||
state: ProposalState;
|
|
||||||
timestamp: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
type Terms = {
|
|
||||||
cancelTransfer?: { changes: unknown };
|
|
||||||
enactmentTimestamp: string;
|
|
||||||
newAsset?: { changes: unknown };
|
|
||||||
newFreeform: object;
|
|
||||||
newMarket?: { changes: unknown };
|
|
||||||
newSpotMarket?: { changes: unknown };
|
|
||||||
newTransfer?: { changes: unknown };
|
|
||||||
updateAsset?: { assetId: string; changes: unknown };
|
|
||||||
updateMarket?: { marketId: string; changes: unknown };
|
|
||||||
updateMarketState?: {
|
|
||||||
changes: {
|
|
||||||
marketId: string;
|
|
||||||
price: string;
|
|
||||||
updateType:
|
|
||||||
| 'MARKET_STATE_UPDATE_TYPE_UNSPECIFIED'
|
|
||||||
| 'MARKET_STATE_UPDATE_TYPE_TERMINATE'
|
|
||||||
| 'MARKET_STATE_UPDATE_TYPE_SUSPEND'
|
|
||||||
| 'MARKET_STATE_UPDATE_TYPE_RESUME';
|
|
||||||
};
|
|
||||||
};
|
|
||||||
updateNetworkParameter?: { changes: unknown };
|
|
||||||
updateReferralProgram?: { changes: unknown };
|
|
||||||
updateSpotMarket?: { marketId: string; changes: unknown };
|
|
||||||
updateVolumeDiscountProgram?: { changes: unknown };
|
|
||||||
};
|
|
||||||
|
|
||||||
export type SingleProposalData = ProposalData & {
|
|
||||||
terms: Terms & {
|
|
||||||
closingTimestamp: string;
|
|
||||||
validationTimestamp: string;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
type BatchProposalData = ProposalData & {
|
|
||||||
batchTerms: {
|
|
||||||
changes: Terms[];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
export type SubProposalData = SingleProposalData & {
|
|
||||||
batchId: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type ProposalNode = {
|
|
||||||
proposal: ProposalData;
|
|
||||||
proposalType: ProposalNodeType;
|
|
||||||
proposals: SubProposalData[];
|
|
||||||
yes?: [
|
|
||||||
{
|
|
||||||
partyId: string;
|
|
||||||
elsPerMarket?: [
|
|
||||||
{
|
|
||||||
marketId: string;
|
|
||||||
els: string;
|
|
||||||
}
|
|
||||||
];
|
|
||||||
}
|
|
||||||
];
|
|
||||||
no?: [
|
|
||||||
{
|
|
||||||
partyId: string;
|
|
||||||
elsPerMarket?: [
|
|
||||||
{
|
|
||||||
marketId: string;
|
|
||||||
els: string;
|
|
||||||
}
|
|
||||||
];
|
|
||||||
}
|
|
||||||
];
|
|
||||||
};
|
|
||||||
|
|
||||||
type SingleProposalNode = ProposalNode & {
|
|
||||||
proposal: SingleProposalData;
|
|
||||||
proposalType: 'TYPE_SINGLE_OR_UNSPECIFIED';
|
|
||||||
proposals: [];
|
|
||||||
};
|
|
||||||
|
|
||||||
type BatchProposalNode = ProposalNode & {
|
|
||||||
proposal: BatchProposalData;
|
|
||||||
proposalType: 'TYPE_BATCH';
|
|
||||||
};
|
|
||||||
|
|
||||||
export const isProposalNode = (node: unknown): node is ProposalNode =>
|
|
||||||
Boolean(
|
|
||||||
typeof node === 'object' &&
|
|
||||||
node &&
|
|
||||||
'proposal' in node &&
|
|
||||||
typeof node.proposal === 'object' &&
|
|
||||||
node?.proposal &&
|
|
||||||
'id' in node.proposal &&
|
|
||||||
node?.proposal?.id
|
|
||||||
);
|
|
||||||
|
|
||||||
export const isSingleProposalNode = (
|
|
||||||
node: Maybe<ProposalNode>
|
|
||||||
): node is SingleProposalNode =>
|
|
||||||
Boolean(
|
|
||||||
node &&
|
|
||||||
node?.proposalType === 'TYPE_SINGLE_OR_UNSPECIFIED' &&
|
|
||||||
node?.proposal
|
|
||||||
);
|
|
||||||
|
|
||||||
export const isBatchProposalNode = (
|
|
||||||
node: Maybe<ProposalNode>
|
|
||||||
): node is BatchProposalNode =>
|
|
||||||
Boolean(
|
|
||||||
node &&
|
|
||||||
node?.proposalType === 'TYPE_BATCH' &&
|
|
||||||
node?.proposal &&
|
|
||||||
'batchTerms' in node.proposal &&
|
|
||||||
node?.proposals?.length > 0
|
|
||||||
);
|
|
||||||
|
|
||||||
// this includes also batch proposals with `updateMarket`s 👍
|
|
||||||
const PROPOSALS_ENDPOINT = `${ENV.rest}governances?proposalState=:proposalState&proposalType=:proposalType`;
|
|
||||||
|
|
||||||
// this can be queried also by sub proposal id as `proposalId` and it will
|
|
||||||
// return full batch proposal data with all of its sub proposals including
|
|
||||||
// the requested one inside `proposals` array.
|
|
||||||
const PROPOSAL_ENDPOINT = `${ENV.rest}governance?proposalId=:proposalId`;
|
|
||||||
|
|
||||||
export const getProposals = async ({
|
|
||||||
proposalState,
|
|
||||||
proposalType,
|
|
||||||
}: {
|
|
||||||
proposalState: ProposalState;
|
|
||||||
proposalType: ProposalType;
|
|
||||||
}) => {
|
|
||||||
try {
|
|
||||||
const response = await fetch(
|
|
||||||
PROPOSALS_ENDPOINT.replace(':proposalState', proposalState).replace(
|
|
||||||
':proposalType',
|
|
||||||
proposalType
|
|
||||||
)
|
|
||||||
);
|
|
||||||
if (response.ok) {
|
|
||||||
const data = await response.json();
|
|
||||||
if (
|
|
||||||
data &&
|
|
||||||
'connection' in data &&
|
|
||||||
data.connection &&
|
|
||||||
'edges' in data.connection &&
|
|
||||||
data.connection.edges?.length > 0
|
|
||||||
) {
|
|
||||||
const nodes = compact(
|
|
||||||
data.connection.edges.map((e: { node?: object }) => e?.node)
|
|
||||||
).filter(isProposalNode);
|
|
||||||
|
|
||||||
return nodes;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch {
|
|
||||||
// NOOP - ignore errors
|
|
||||||
}
|
|
||||||
|
|
||||||
return [];
|
|
||||||
};
|
|
||||||
|
|
||||||
export const getProposal = async ({ proposalId }: { proposalId: string }) => {
|
|
||||||
try {
|
|
||||||
const response = await fetch(
|
|
||||||
PROPOSAL_ENDPOINT.replace(':proposalId', proposalId)
|
|
||||||
);
|
|
||||||
if (response.ok) {
|
|
||||||
const data = await response.json();
|
|
||||||
if (data && 'data' in data && isProposalNode(data.data)) {
|
|
||||||
return data.data as ProposalNode;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
// NOOP - ignore errors
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const useFetchProposal = ({ proposalId }: { proposalId?: string }) => {
|
|
||||||
const [data, setData] = useState<ProposalNode | null>(null);
|
|
||||||
const [loading, setLoading] = useState<boolean>(false);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const cb = async () => {
|
|
||||||
if (!proposalId) return;
|
|
||||||
|
|
||||||
setLoading(true);
|
|
||||||
const data = await getProposal({ proposalId });
|
|
||||||
setLoading(false);
|
|
||||||
if (data) {
|
|
||||||
setData(data);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
cb();
|
|
||||||
}, [proposalId]);
|
|
||||||
|
|
||||||
return { data, loading };
|
|
||||||
};
|
|
||||||
|
|
||||||
export const useFetchProposals = ({
|
|
||||||
proposalState,
|
|
||||||
proposalType,
|
|
||||||
}: {
|
|
||||||
proposalState: ProposalState;
|
|
||||||
proposalType: ProposalType;
|
|
||||||
}) => {
|
|
||||||
const [data, setData] = useState<ProposalNode[]>([]);
|
|
||||||
const [loading, setLoading] = useState<boolean>(false);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const cb = async () => {
|
|
||||||
setLoading(true);
|
|
||||||
const data = await getProposals({ proposalState, proposalType });
|
|
||||||
setLoading(false);
|
|
||||||
if (data) {
|
|
||||||
setData(data);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
cb();
|
|
||||||
}, [proposalState, proposalType]);
|
|
||||||
|
|
||||||
return { data, loading };
|
|
||||||
};
|
|
||||||
|
|
||||||
export const flatten = (
|
|
||||||
nodes: ProposalNode[]
|
|
||||||
): (SingleProposalData | SubProposalData)[] => {
|
|
||||||
const flattenNodes = [];
|
|
||||||
for (const node of nodes) {
|
|
||||||
if (isSingleProposalNode(node)) {
|
|
||||||
flattenNodes.push(node.proposal);
|
|
||||||
}
|
|
||||||
if (isBatchProposalNode(node)) {
|
|
||||||
for (const sub of node.proposals) {
|
|
||||||
flattenNodes.push(sub);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return flattenNodes;
|
|
||||||
};
|
|
@ -1,12 +1,12 @@
|
|||||||
import { MemoryRouter } from 'react-router-dom';
|
import { MemoryRouter } from 'react-router-dom';
|
||||||
import { MockedProvider } from '@apollo/client/testing';
|
import { MockedProvider } from '@apollo/client/testing';
|
||||||
|
import { VegaWalletProvider } from '@vegaprotocol/wallet';
|
||||||
|
import { type VegaWalletConfig } from '@vegaprotocol/wallet';
|
||||||
import { render, screen } from '@testing-library/react';
|
import { render, screen } from '@testing-library/react';
|
||||||
import { generateProposal } from '../../test-helpers/generate-proposals';
|
import { generateProposal } from '../../test-helpers/generate-proposals';
|
||||||
import { Proposal } from './proposal';
|
import { Proposal } from './proposal';
|
||||||
import { ProposalState } from '@vegaprotocol/types';
|
import { ProposalState } from '@vegaprotocol/types';
|
||||||
import { type Proposal as IProposal } from '../../types';
|
import { type Proposal as IProposal } from '../../types';
|
||||||
import { AppStateProvider } from '../../../../contexts/app-state/app-state-provider';
|
|
||||||
import { MockedWalletProvider } from '@vegaprotocol/wallet-react/testing';
|
|
||||||
|
|
||||||
jest.mock('@vegaprotocol/network-parameters', () => ({
|
jest.mock('@vegaprotocol/network-parameters', () => ({
|
||||||
...jest.requireActual('@vegaprotocol/network-parameters'),
|
...jest.requireActual('@vegaprotocol/network-parameters'),
|
||||||
@ -24,44 +24,45 @@ jest.mock('@vegaprotocol/network-parameters', () => ({
|
|||||||
error: null,
|
error: null,
|
||||||
})),
|
})),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
jest.mock('../proposal-detail-header/proposal-header', () => ({
|
jest.mock('../proposal-detail-header/proposal-header', () => ({
|
||||||
ProposalHeader: () => <div data-testid="proposal-header"></div>,
|
ProposalHeader: () => <div data-testid="proposal-header"></div>,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
jest.mock('../proposal-change-table', () => ({
|
jest.mock('../proposal-change-table', () => ({
|
||||||
ProposalChangeTable: () => <div data-testid="proposal-change-table"></div>,
|
ProposalChangeTable: () => <div data-testid="proposal-change-table"></div>,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
jest.mock('../proposal-json', () => ({
|
jest.mock('../proposal-json', () => ({
|
||||||
ProposalJson: () => <div data-testid="proposal-json"></div>,
|
ProposalJson: () => <div data-testid="proposal-json"></div>,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
jest.mock('../list-asset', () => ({
|
jest.mock('../list-asset', () => ({
|
||||||
ListAsset: () => <div data-testid="proposal-list-asset"></div>,
|
ListAsset: () => <div data-testid="proposal-list-asset"></div>,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
jest.mock('../list-asset', () => ({
|
|
||||||
ListAsset: () => <div data-testid="proposal-list-asset"></div>,
|
|
||||||
}));
|
|
||||||
|
|
||||||
jest.mock('../vote-details', () => ({
|
|
||||||
UserVote: () => <div data-testid="user-vote"></div>,
|
|
||||||
}));
|
|
||||||
|
|
||||||
jest.mock('./proposal-change-details', () => ({
|
jest.mock('./proposal-change-details', () => ({
|
||||||
ProposalChangeDetails: () => <div data-testid="proposal-change-details" />,
|
ProposalChangeDetails: () => (
|
||||||
|
<div data-testid="proposal-change-details"></div>
|
||||||
|
),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
const vegaWalletConfig: VegaWalletConfig = {
|
||||||
|
network: 'TESTNET',
|
||||||
|
vegaUrl: 'https://vega.xyz',
|
||||||
|
vegaWalletServiceUrl: 'https://wallet.vega.xyz',
|
||||||
|
links: {
|
||||||
|
explorer: 'explorer',
|
||||||
|
concepts: 'concepts',
|
||||||
|
chromeExtensionUrl: 'chrome',
|
||||||
|
mozillaExtensionUrl: 'mozilla',
|
||||||
|
},
|
||||||
|
chainId: 'VEGA_CHAIN_ID',
|
||||||
|
};
|
||||||
|
|
||||||
const renderComponent = (proposal: IProposal) => {
|
const renderComponent = (proposal: IProposal) => {
|
||||||
return render(
|
render(
|
||||||
<MemoryRouter>
|
<MemoryRouter>
|
||||||
<MockedProvider>
|
<MockedProvider>
|
||||||
<MockedWalletProvider>
|
<VegaWalletProvider config={vegaWalletConfig}>
|
||||||
<AppStateProvider>
|
<Proposal restData={{}} proposal={proposal} />
|
||||||
<Proposal restData={null} proposal={proposal} />
|
</VegaWalletProvider>
|
||||||
</AppStateProvider>
|
|
||||||
</MockedWalletProvider>
|
|
||||||
</MockedProvider>
|
</MockedProvider>
|
||||||
</MemoryRouter>
|
</MemoryRouter>
|
||||||
);
|
);
|
||||||
|
@ -8,22 +8,20 @@ import { ProposalJson } from '../proposal-json';
|
|||||||
import { UserVote } from '../vote-details';
|
import { UserVote } from '../vote-details';
|
||||||
import Routes from '../../../routes';
|
import Routes from '../../../routes';
|
||||||
import { ProposalState } from '@vegaprotocol/types';
|
import { ProposalState } from '@vegaprotocol/types';
|
||||||
import { type ProposalNode } from './proposal-utils';
|
|
||||||
import { useVoteSubmit } from '@vegaprotocol/proposals';
|
import { useVoteSubmit } from '@vegaprotocol/proposals';
|
||||||
import { useUserVote } from '../vote-details/use-user-vote';
|
import { useUserVote } from '../vote-details/use-user-vote';
|
||||||
import { type Proposal as IProposal, type BatchProposal } from '../../types';
|
import { type Proposal as IProposal, type BatchProposal } from '../../types';
|
||||||
import { ProposalChangeDetails } from './proposal-change-details';
|
import { ProposalChangeDetails } from './proposal-change-details';
|
||||||
import { type JsonValue } from 'type-fest';
|
|
||||||
|
|
||||||
export interface ProposalProps {
|
export interface ProposalProps {
|
||||||
proposal: IProposal | BatchProposal;
|
proposal: IProposal | BatchProposal;
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
restData: ProposalNode | null;
|
restData: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const Proposal = ({ proposal, restData }: ProposalProps) => {
|
export const Proposal = ({ proposal, restData }: ProposalProps) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { submit, finalizedVote, transaction } = useVoteSubmit();
|
const { submit, Dialog, finalizedVote, transaction } = useVoteSubmit();
|
||||||
const { voteState, voteDatetime } = useUserVote(proposal?.id, finalizedVote);
|
const { voteState, voteDatetime } = useUserVote(proposal?.id, finalizedVote);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -48,7 +46,6 @@ export const Proposal = ({ proposal, restData }: ProposalProps) => {
|
|||||||
|
|
||||||
<ProposalHeader
|
<ProposalHeader
|
||||||
proposal={proposal}
|
proposal={proposal}
|
||||||
restData={restData}
|
|
||||||
isListItem={false}
|
isListItem={false}
|
||||||
voteState={voteState}
|
voteState={voteState}
|
||||||
/>
|
/>
|
||||||
@ -61,7 +58,7 @@ export const Proposal = ({ proposal, restData }: ProposalProps) => {
|
|||||||
<ProposalDescription description={proposal.rationale.description} />
|
<ProposalDescription description={proposal.rationale.description} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="mb-4 flex flex-col gap-0">
|
<div className="mb-4">
|
||||||
{proposal.__typename === 'Proposal' ? (
|
{proposal.__typename === 'Proposal' ? (
|
||||||
<ProposalChangeDetails
|
<ProposalChangeDetails
|
||||||
proposal={proposal}
|
proposal={proposal}
|
||||||
@ -73,7 +70,6 @@ export const Proposal = ({ proposal, restData }: ProposalProps) => {
|
|||||||
if (!p?.terms) return null;
|
if (!p?.terms) return null;
|
||||||
return (
|
return (
|
||||||
<ProposalChangeDetails
|
<ProposalChangeDetails
|
||||||
indicator={i + 1}
|
|
||||||
key={i}
|
key={i}
|
||||||
proposal={proposal}
|
proposal={proposal}
|
||||||
terms={p.terms}
|
terms={p.terms}
|
||||||
@ -89,6 +85,7 @@ export const Proposal = ({ proposal, restData }: ProposalProps) => {
|
|||||||
<UserVote
|
<UserVote
|
||||||
proposal={proposal}
|
proposal={proposal}
|
||||||
submit={submit}
|
submit={submit}
|
||||||
|
dialog={Dialog}
|
||||||
transaction={transaction}
|
transaction={transaction}
|
||||||
voteState={voteState}
|
voteState={voteState}
|
||||||
voteDatetime={voteDatetime}
|
voteDatetime={voteDatetime}
|
||||||
@ -97,7 +94,7 @@ export const Proposal = ({ proposal, restData }: ProposalProps) => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="mb-6">
|
<div className="mb-6">
|
||||||
<ProposalJson proposal={restData?.proposal as unknown as JsonValue} />
|
<ProposalJson proposal={restData?.data?.proposal} />
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
);
|
);
|
||||||
|
@ -1,14 +1,15 @@
|
|||||||
import { BrowserRouter as Router } from 'react-router-dom';
|
import { BrowserRouter as Router } from 'react-router-dom';
|
||||||
|
import { VegaWalletContext } from '@vegaprotocol/wallet';
|
||||||
import { render, screen } from '@testing-library/react';
|
import { render, screen } from '@testing-library/react';
|
||||||
import { AppStateProvider } from '../../../../contexts/app-state/app-state-provider';
|
|
||||||
import { ProposalsListItemDetails } from './proposals-list-item-details';
|
import { ProposalsListItemDetails } from './proposals-list-item-details';
|
||||||
|
import { mockWalletContext } from '../../test-helpers/mocks';
|
||||||
|
|
||||||
const renderComponent = (id: string) =>
|
const renderComponent = (id: string) =>
|
||||||
render(
|
render(
|
||||||
<Router>
|
<Router>
|
||||||
<AppStateProvider>
|
<VegaWalletContext.Provider value={mockWalletContext}>
|
||||||
<ProposalsListItemDetails id={id} />
|
<ProposalsListItemDetails id={id} />
|
||||||
</AppStateProvider>
|
</VegaWalletContext.Provider>
|
||||||
</Router>
|
</Router>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -206,7 +206,7 @@ export const ProposalsList = ({
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<section className="-mx-4 p-4 mb-8 bg-vega-cdark-900 rounded-l-sm">
|
<section className="-mx-4 p-4 mb-8 bg-vega-dark-100">
|
||||||
<SubHeading title={t('openProposals')} />
|
<SubHeading title={t('openProposals')} />
|
||||||
|
|
||||||
{sortedProposals.open.length > 0 ||
|
{sortedProposals.open.length > 0 ||
|
||||||
|
@ -1,32 +1,25 @@
|
|||||||
import { act, render, screen } from '@testing-library/react';
|
import { render, screen } from '@testing-library/react';
|
||||||
import {
|
import { VegaWalletContext } from '@vegaprotocol/wallet';
|
||||||
MockedWalletProvider,
|
|
||||||
mockConfig,
|
|
||||||
} from '@vegaprotocol/wallet-react/testing';
|
|
||||||
import { AppStateProvider } from '../../../../contexts/app-state/app-state-provider';
|
import { AppStateProvider } from '../../../../contexts/app-state/app-state-provider';
|
||||||
import { ProposalFormSubmit } from './proposal-form-submit';
|
import { ProposalFormSubmit } from './proposal-form-submit';
|
||||||
|
import type { VegaWalletContextShape } from '@vegaprotocol/wallet';
|
||||||
|
|
||||||
const renderComponent = (isSubmitting: boolean) => {
|
const renderComponent = (
|
||||||
|
context: VegaWalletContextShape,
|
||||||
|
isSubmitting: boolean
|
||||||
|
) => {
|
||||||
render(
|
render(
|
||||||
<MockedWalletProvider>
|
<AppStateProvider>
|
||||||
<AppStateProvider>
|
<VegaWalletContext.Provider value={context}>
|
||||||
<ProposalFormSubmit isSubmitting={isSubmitting} />
|
<ProposalFormSubmit isSubmitting={isSubmitting} />
|
||||||
</AppStateProvider>
|
</VegaWalletContext.Provider>
|
||||||
</MockedWalletProvider>
|
</AppStateProvider>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
describe('Proposal Form Submit', () => {
|
describe('Proposal Form Submit', () => {
|
||||||
const pubKey = { publicKey: '123456__123456', name: 'test' };
|
|
||||||
|
|
||||||
afterEach(() => {
|
|
||||||
act(() => {
|
|
||||||
mockConfig.reset();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should display connection message and button if wallet not connected', () => {
|
it('should display connection message and button if wallet not connected', () => {
|
||||||
renderComponent(false);
|
renderComponent({ pubKey: null } as VegaWalletContextShape, false);
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
screen.getByText('Connect your wallet to submit a proposal')
|
screen.getByText('Connect your wallet to submit a proposal')
|
||||||
@ -37,22 +30,28 @@ describe('Proposal Form Submit', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should display submit button if wallet is connected', () => {
|
it('should display submit button if wallet is connected', () => {
|
||||||
mockConfig.store.setState({
|
const pubKey = { publicKey: '123456__123456', name: 'test' };
|
||||||
pubKey: pubKey.publicKey,
|
renderComponent(
|
||||||
keys: [pubKey],
|
{
|
||||||
});
|
pubKey: pubKey.publicKey,
|
||||||
renderComponent(false);
|
pubKeys: [pubKey],
|
||||||
|
} as VegaWalletContextShape,
|
||||||
|
false
|
||||||
|
);
|
||||||
expect(screen.getByTestId('proposal-submit')).toHaveTextContent(
|
expect(screen.getByTestId('proposal-submit')).toHaveTextContent(
|
||||||
'Submit proposal'
|
'Submit proposal'
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should display submitting button text if wallet is connected and submitting', () => {
|
it('should display submitting button text if wallet is connected and submitting', () => {
|
||||||
mockConfig.store.setState({
|
const pubKey = { publicKey: '123456__123456', name: 'test' };
|
||||||
pubKey: pubKey.publicKey,
|
renderComponent(
|
||||||
keys: [pubKey],
|
{
|
||||||
});
|
pubKey: pubKey.publicKey,
|
||||||
renderComponent(true);
|
pubKeys: [pubKey],
|
||||||
|
} as VegaWalletContextShape,
|
||||||
|
true
|
||||||
|
);
|
||||||
expect(screen.getByTestId('proposal-submit')).toHaveTextContent(
|
expect(screen.getByTestId('proposal-submit')).toHaveTextContent(
|
||||||
'Submitting proposal'
|
'Submitting proposal'
|
||||||
);
|
);
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { Button } from '@vegaprotocol/ui-toolkit';
|
import { Button } from '@vegaprotocol/ui-toolkit';
|
||||||
import { useVegaWallet } from '@vegaprotocol/wallet-react';
|
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||||
import { VegaWalletContainer } from '../../../../components/vega-wallet-container';
|
import { VegaWalletContainer } from '../../../../components/vega-wallet-container';
|
||||||
|
|
||||||
interface ProposalFormSubmitProps {
|
interface ProposalFormSubmitProps {
|
||||||
|
@ -1,24 +1,19 @@
|
|||||||
import {
|
import {
|
||||||
VegaTransactionDialog,
|
|
||||||
getProposalDialogIcon,
|
getProposalDialogIcon,
|
||||||
getProposalDialogIntent,
|
getProposalDialogIntent,
|
||||||
useGetProposalDialogTitle,
|
useGetProposalDialogTitle,
|
||||||
} from '@vegaprotocol/proposals';
|
} from '@vegaprotocol/proposals';
|
||||||
import type {
|
import type { ProposalEventFieldsFragment } from '@vegaprotocol/proposals';
|
||||||
ProposalEventFieldsFragment,
|
import type { DialogProps } from '@vegaprotocol/proposals';
|
||||||
VegaTxState,
|
|
||||||
} from '@vegaprotocol/proposals';
|
|
||||||
|
|
||||||
interface ProposalFormTransactionDialogProps {
|
interface ProposalFormTransactionDialogProps {
|
||||||
finalizedProposal: ProposalEventFieldsFragment | null;
|
finalizedProposal: ProposalEventFieldsFragment | null;
|
||||||
transaction: VegaTxState;
|
TransactionDialog: (props: DialogProps) => JSX.Element;
|
||||||
onChange: (open: boolean) => void;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ProposalFormTransactionDialog = ({
|
export const ProposalFormTransactionDialog = ({
|
||||||
finalizedProposal,
|
finalizedProposal,
|
||||||
transaction,
|
TransactionDialog,
|
||||||
onChange,
|
|
||||||
}: ProposalFormTransactionDialogProps) => {
|
}: ProposalFormTransactionDialogProps) => {
|
||||||
const title = useGetProposalDialogTitle(finalizedProposal?.state);
|
const title = useGetProposalDialogTitle(finalizedProposal?.state);
|
||||||
// Render a custom complete UI if the proposal was rejected otherwise
|
// Render a custom complete UI if the proposal was rejected otherwise
|
||||||
@ -29,16 +24,13 @@ export const ProposalFormTransactionDialog = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div data-testid="proposal-transaction-dialog">
|
<div data-testid="proposal-transaction-dialog">
|
||||||
<VegaTransactionDialog
|
<TransactionDialog
|
||||||
title={title}
|
title={title}
|
||||||
intent={getProposalDialogIntent(finalizedProposal?.state)}
|
intent={getProposalDialogIntent(finalizedProposal?.state)}
|
||||||
icon={getProposalDialogIcon(finalizedProposal?.state)}
|
icon={getProposalDialogIcon(finalizedProposal?.state)}
|
||||||
content={{
|
content={{
|
||||||
Complete: completeContent,
|
Complete: completeContent,
|
||||||
}}
|
}}
|
||||||
transaction={transaction}
|
|
||||||
isOpen={transaction.dialogOpen}
|
|
||||||
onChange={onChange}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
import { render, screen } from '@testing-library/react';
|
import { render, screen } from '@testing-library/react';
|
||||||
import { BrowserRouter as Router } from 'react-router-dom';
|
import { BrowserRouter as Router } from 'react-router-dom';
|
||||||
import { MockedProvider } from '@apollo/client/testing';
|
import { MockedProvider } from '@apollo/client/testing';
|
||||||
|
import { VegaWalletContext } from '@vegaprotocol/wallet';
|
||||||
import {
|
import {
|
||||||
lastWeek,
|
lastWeek,
|
||||||
|
mockWalletContext,
|
||||||
networkParamsQueryMock,
|
networkParamsQueryMock,
|
||||||
nextWeek,
|
nextWeek,
|
||||||
} from '../../test-helpers/mocks';
|
} from '../../test-helpers/mocks';
|
||||||
@ -46,7 +48,9 @@ const renderComponent = (
|
|||||||
render(
|
render(
|
||||||
<Router>
|
<Router>
|
||||||
<MockedProvider mocks={mocks}>
|
<MockedProvider mocks={mocks}>
|
||||||
<VoteBreakdown proposal={proposal} />
|
<VegaWalletContext.Provider value={mockWalletContext}>
|
||||||
|
<VoteBreakdown proposal={proposal} />
|
||||||
|
</VegaWalletContext.Provider>
|
||||||
</MockedProvider>
|
</MockedProvider>
|
||||||
</Router>
|
</Router>
|
||||||
);
|
);
|
||||||
@ -56,7 +60,6 @@ describe('VoteBreakdown', () => {
|
|||||||
jest.useFakeTimers();
|
jest.useFakeTimers();
|
||||||
jest.setSystemTime(0);
|
jest.setSystemTime(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
afterAll(() => {
|
afterAll(() => {
|
||||||
jest.useRealTimers();
|
jest.useRealTimers();
|
||||||
});
|
});
|
||||||
|
@ -15,9 +15,6 @@ import {
|
|||||||
type VoteFieldsFragment,
|
type VoteFieldsFragment,
|
||||||
} from '../../__generated__/Proposals';
|
} from '../../__generated__/Proposals';
|
||||||
import { useBatchVoteInformation } from '../../hooks/use-vote-information';
|
import { useBatchVoteInformation } from '../../hooks/use-vote-information';
|
||||||
import { MarketName } from '../proposal/market-name';
|
|
||||||
import { Indicator } from '../proposal/indicator';
|
|
||||||
import { type ProposalNode } from '../proposal/proposal-utils';
|
|
||||||
|
|
||||||
export const CompactVotes = ({ number }: { number: BigNumber }) => (
|
export const CompactVotes = ({ number }: { number: BigNumber }) => (
|
||||||
<CompactNumber
|
<CompactNumber
|
||||||
@ -42,9 +39,8 @@ const VoteProgress = ({
|
|||||||
children,
|
children,
|
||||||
}: VoteProgressProps) => {
|
}: VoteProgressProps) => {
|
||||||
const containerClasses = classNames(
|
const containerClasses = classNames(
|
||||||
'relative h-2 rounded-md overflow-hidden',
|
'relative h-10 rounded-md border border-vega-dark-300 overflow-hidden',
|
||||||
// 'border border-vega-dark-300',
|
colourfulBg ? 'bg-vega-pink' : 'bg-vega-dark-400'
|
||||||
colourfulBg ? 'bg-vega-red' : 'bg-vega-dark-200'
|
|
||||||
);
|
);
|
||||||
|
|
||||||
const progressClasses = classNames(
|
const progressClasses = classNames(
|
||||||
@ -53,19 +49,17 @@ const VoteProgress = ({
|
|||||||
);
|
);
|
||||||
|
|
||||||
const textClasses = classNames(
|
const textClasses = classNames(
|
||||||
'w-full flex items-center justify-start text-white text-sm pb-1'
|
'absolute top-0 left-0 w-full h-full flex items-center justify-start px-3 text-black'
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div className={containerClasses}>
|
||||||
|
<div
|
||||||
|
className={progressClasses}
|
||||||
|
style={{ width: `${percentageFor}%` }}
|
||||||
|
data-testid={testId}
|
||||||
|
/>
|
||||||
<div className={textClasses}>{children}</div>
|
<div className={textClasses}>{children}</div>
|
||||||
<div className={containerClasses}>
|
|
||||||
<div
|
|
||||||
className={progressClasses}
|
|
||||||
style={{ width: `${percentageFor}%` }}
|
|
||||||
data-testid={testId}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@ -84,22 +78,14 @@ const Status = ({ reached, threshold, text, testId }: StatusProps) => {
|
|||||||
<div data-testid={testId}>
|
<div data-testid={testId}>
|
||||||
{reached ? (
|
{reached ? (
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<VegaIcon
|
<VegaIcon name={VegaIconNames.TICK} size={20} />
|
||||||
name={VegaIconNames.TICK}
|
|
||||||
className="text-vega-green"
|
|
||||||
size={20}
|
|
||||||
/>
|
|
||||||
<span>
|
<span>
|
||||||
{threshold.toString()}% {text} {t('met')}
|
{threshold.toString()}% {text} {t('met')}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<VegaIcon
|
<VegaIcon name={VegaIconNames.CROSS} size={20} />
|
||||||
name={VegaIconNames.CROSS}
|
|
||||||
className="text-vega-red"
|
|
||||||
size={20}
|
|
||||||
/>
|
|
||||||
<span>
|
<span>
|
||||||
{threshold.toString()}% {text} {t('not met')}
|
{threshold.toString()}% {text} {t('not met')}
|
||||||
</span>
|
</span>
|
||||||
@ -111,64 +97,24 @@ const Status = ({ reached, threshold, text, testId }: StatusProps) => {
|
|||||||
|
|
||||||
export const VoteBreakdown = ({
|
export const VoteBreakdown = ({
|
||||||
proposal,
|
proposal,
|
||||||
restData,
|
|
||||||
}: {
|
}: {
|
||||||
proposal: Proposal | BatchProposal;
|
proposal: Proposal | BatchProposal;
|
||||||
restData?: ProposalNode | null;
|
|
||||||
}) => {
|
}) => {
|
||||||
if (proposal.__typename === 'Proposal') {
|
if (proposal.__typename === 'Proposal') {
|
||||||
return <VoteBreakdownNormal proposal={proposal} />;
|
return <VoteBreakdownNormal proposal={proposal} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (proposal.__typename === 'BatchProposal') {
|
if (proposal.__typename === 'BatchProposal') {
|
||||||
return <VoteBreakdownBatch proposal={proposal} restData={restData} />;
|
return <VoteBreakdownBatch proposal={proposal} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
|
|
||||||
const VoteBreakdownBatch = ({
|
const VoteBreakdownBatch = ({ proposal }: { proposal: BatchProposal }) => {
|
||||||
proposal,
|
|
||||||
restData,
|
|
||||||
}: {
|
|
||||||
proposal: BatchProposal;
|
|
||||||
restData?: ProposalNode | null;
|
|
||||||
}) => {
|
|
||||||
const [fullBreakdown, setFullBreakdown] = useState(false);
|
const [fullBreakdown, setFullBreakdown] = useState(false);
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const yesELS =
|
|
||||||
restData?.yes?.reduce((all, y) => {
|
|
||||||
if (y.elsPerMarket) {
|
|
||||||
y.elsPerMarket.forEach((item) => {
|
|
||||||
const share = Number(item.els);
|
|
||||||
if (all[item.marketId]) {
|
|
||||||
all[item.marketId].push(share);
|
|
||||||
} else {
|
|
||||||
all[item.marketId] = [share];
|
|
||||||
}
|
|
||||||
return all;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return all;
|
|
||||||
}, {} as Record<string, number[]>) || {};
|
|
||||||
|
|
||||||
const noELS =
|
|
||||||
restData?.no?.reduce((all, y) => {
|
|
||||||
if (y.elsPerMarket) {
|
|
||||||
y.elsPerMarket.forEach((item) => {
|
|
||||||
const share = Number(item.els);
|
|
||||||
if (all[item.marketId]) {
|
|
||||||
all[item.marketId].push(share);
|
|
||||||
} else {
|
|
||||||
all[item.marketId] = [share];
|
|
||||||
}
|
|
||||||
return all;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return all;
|
|
||||||
}, {} as Record<string, number[]>) || {};
|
|
||||||
|
|
||||||
const voteInfo = useBatchVoteInformation({
|
const voteInfo = useBatchVoteInformation({
|
||||||
terms: compact(
|
terms: compact(
|
||||||
proposal.subProposals ? proposal.subProposals.map((p) => p?.terms) : []
|
proposal.subProposals ? proposal.subProposals.map((p) => p?.terms) : []
|
||||||
@ -205,7 +151,7 @@ const VoteBreakdownBatch = ({
|
|||||||
<p className="flex gap-2 m-0 items-center">
|
<p className="flex gap-2 m-0 items-center">
|
||||||
<VegaIcon
|
<VegaIcon
|
||||||
name={VegaIconNames.CROSS}
|
name={VegaIconNames.CROSS}
|
||||||
className="text-vega-red"
|
className="text-vega-pink"
|
||||||
size={20}
|
size={20}
|
||||||
/>
|
/>
|
||||||
{t(
|
{t(
|
||||||
@ -230,13 +176,10 @@ const VoteBreakdownBatch = ({
|
|||||||
if (!p?.terms) return null;
|
if (!p?.terms) return null;
|
||||||
return (
|
return (
|
||||||
<VoteBreakdownBatchSubProposal
|
<VoteBreakdownBatchSubProposal
|
||||||
indicator={i + 1}
|
|
||||||
key={i}
|
key={i}
|
||||||
proposal={proposal}
|
proposal={proposal}
|
||||||
votes={proposal.votes}
|
votes={proposal.votes}
|
||||||
terms={p.terms}
|
terms={p.terms}
|
||||||
yesELS={yesELS}
|
|
||||||
noELS={noELS}
|
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
@ -270,7 +213,7 @@ const VoteBreakdownBatch = ({
|
|||||||
<p className="flex gap-2 m-0 items-center">
|
<p className="flex gap-2 m-0 items-center">
|
||||||
<VegaIcon
|
<VegaIcon
|
||||||
name={VegaIconNames.CROSS}
|
name={VegaIconNames.CROSS}
|
||||||
className="text-vega-red"
|
className="text-vega-pink"
|
||||||
size={20}
|
size={20}
|
||||||
/>
|
/>
|
||||||
{t('Proposal failed: {{count}} of {{total}} proposals passed', {
|
{t('Proposal failed: {{count}} of {{total}} proposals passed', {
|
||||||
@ -292,13 +235,10 @@ const VoteBreakdownBatch = ({
|
|||||||
if (!p?.terms) return null;
|
if (!p?.terms) return null;
|
||||||
return (
|
return (
|
||||||
<VoteBreakdownBatchSubProposal
|
<VoteBreakdownBatchSubProposal
|
||||||
indicator={i + 1}
|
|
||||||
key={i}
|
key={i}
|
||||||
proposal={proposal}
|
proposal={proposal}
|
||||||
votes={proposal.votes}
|
votes={proposal.votes}
|
||||||
terms={p.terms}
|
terms={p.terms}
|
||||||
yesELS={yesELS}
|
|
||||||
noELS={noELS}
|
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
@ -315,62 +255,28 @@ const VoteBreakdownBatchSubProposal = ({
|
|||||||
proposal,
|
proposal,
|
||||||
votes,
|
votes,
|
||||||
terms,
|
terms,
|
||||||
indicator,
|
|
||||||
yesELS,
|
|
||||||
noELS,
|
|
||||||
}: {
|
}: {
|
||||||
proposal: BatchProposal;
|
proposal: BatchProposal;
|
||||||
votes: VoteFieldsFragment;
|
votes: VoteFieldsFragment;
|
||||||
terms: ProposalTermsFieldsFragment;
|
terms: ProposalTermsFieldsFragment;
|
||||||
indicator?: number;
|
|
||||||
yesELS: Record<string, number[]>;
|
|
||||||
noELS: Record<string, number[]>;
|
|
||||||
}) => {
|
}) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
const voteInfo = useVoteInformation({
|
||||||
|
votes,
|
||||||
|
terms,
|
||||||
|
});
|
||||||
|
|
||||||
const isProposalOpen = proposal?.state === ProposalState.STATE_OPEN;
|
const isProposalOpen = proposal?.state === ProposalState.STATE_OPEN;
|
||||||
const isUpdateMarket = terms?.change?.__typename === 'UpdateMarket';
|
const isUpdateMarket = terms?.change?.__typename === 'UpdateMarket';
|
||||||
|
|
||||||
let marketId = undefined;
|
|
||||||
if (terms?.change?.__typename === 'UpdateMarket') {
|
|
||||||
marketId = terms.change.marketId;
|
|
||||||
}
|
|
||||||
if (terms?.change?.__typename === 'UpdateMarketState') {
|
|
||||||
marketId = terms.change.market.id;
|
|
||||||
}
|
|
||||||
|
|
||||||
const voteInfo = useVoteInformation({
|
|
||||||
votes,
|
|
||||||
terms,
|
|
||||||
// yes votes ELS for this specific proposal (market)
|
|
||||||
yesELS: marketId ? yesELS[marketId] : undefined,
|
|
||||||
// no votes ELS for this specific proposal (market)
|
|
||||||
noELS: marketId ? noELS[marketId] : undefined,
|
|
||||||
});
|
|
||||||
|
|
||||||
const marketName = marketId ? (
|
|
||||||
<>
|
|
||||||
: <MarketName marketId={marketId} />
|
|
||||||
</>
|
|
||||||
) : null;
|
|
||||||
|
|
||||||
const indicatorElement = indicator && <Indicator indicator={indicator} />;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="mb-6">
|
<div>
|
||||||
<div className="flex items-center gap-3 mb-3">
|
<h4>{t(terms.change.__typename)}</h4>
|
||||||
{indicatorElement}
|
<VoteBreakDownUI
|
||||||
<h4>
|
voteInfo={voteInfo}
|
||||||
{t(terms.change.__typename)} {marketName}
|
isProposalOpen={isProposalOpen}
|
||||||
</h4>
|
isUpdateMarket={isUpdateMarket}
|
||||||
</div>
|
/>
|
||||||
<div className="rounded-sm bg-vega-dark-100 p-3">
|
|
||||||
<VoteBreakDownUI
|
|
||||||
voteInfo={voteInfo}
|
|
||||||
isProposalOpen={isProposalOpen}
|
|
||||||
isUpdateMarket={isUpdateMarket}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@ -385,13 +291,11 @@ const VoteBreakdownNormal = ({ proposal }: { proposal: Proposal }) => {
|
|||||||
const isUpdateMarket = proposal?.terms?.change?.__typename === 'UpdateMarket';
|
const isUpdateMarket = proposal?.terms?.change?.__typename === 'UpdateMarket';
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="mb-6">
|
<VoteBreakDownUI
|
||||||
<VoteBreakDownUI
|
voteInfo={voteInfo}
|
||||||
voteInfo={voteInfo}
|
isProposalOpen={isProposalOpen}
|
||||||
isProposalOpen={isProposalOpen}
|
isUpdateMarket={isUpdateMarket}
|
||||||
isUpdateMarket={isUpdateMarket}
|
/>
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -418,6 +322,7 @@ const VoteBreakDownUI = ({
|
|||||||
noPercentage,
|
noPercentage,
|
||||||
noLPPercentage,
|
noLPPercentage,
|
||||||
yesPercentage,
|
yesPercentage,
|
||||||
|
yesLPPercentage,
|
||||||
yesTokens,
|
yesTokens,
|
||||||
noTokens,
|
noTokens,
|
||||||
totalEquityLikeShareWeight,
|
totalEquityLikeShareWeight,
|
||||||
@ -430,7 +335,6 @@ const VoteBreakDownUI = ({
|
|||||||
majorityLPMet,
|
majorityLPMet,
|
||||||
willPassByTokenVote,
|
willPassByTokenVote,
|
||||||
willPassByLPVote,
|
willPassByLPVote,
|
||||||
lpVoteWeight,
|
|
||||||
} = voteInfo;
|
} = voteInfo;
|
||||||
|
|
||||||
const participationThresholdProgress = BigNumber.min(
|
const participationThresholdProgress = BigNumber.min(
|
||||||
@ -455,13 +359,13 @@ const VoteBreakDownUI = ({
|
|||||||
'flex justify-between flex-wrap gap-6'
|
'flex justify-between flex-wrap gap-6'
|
||||||
);
|
);
|
||||||
const sectionClasses = classNames('min-w-[300px] flex-1 flex-grow');
|
const sectionClasses = classNames('min-w-[300px] flex-1 flex-grow');
|
||||||
const headingClasses = classNames('mb-2 text-sm text-white font-bold');
|
const headingClasses = classNames('mb-2 text-vega-dark-400');
|
||||||
const progressDetailsClasses = classNames(
|
const progressDetailsClasses = classNames(
|
||||||
'flex justify-between flex-wrap mt-2 text-sm'
|
'flex justify-between flex-wrap mt-2 text-sm'
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div className="mb-6">
|
||||||
{isProposalOpen && (
|
{isProposalOpen && (
|
||||||
<div
|
<div
|
||||||
data-testid="vote-status"
|
data-testid="vote-status"
|
||||||
@ -478,7 +382,7 @@ const VoteBreakDownUI = ({
|
|||||||
<VegaIcon
|
<VegaIcon
|
||||||
name={VegaIconNames.CROSS}
|
name={VegaIconNames.CROSS}
|
||||||
size={20}
|
size={20}
|
||||||
className="text-vega-red"
|
className="text-vega-pink"
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</span>
|
</span>
|
||||||
@ -494,13 +398,103 @@ const VoteBreakDownUI = ({
|
|||||||
<p className="m-0">
|
<p className="m-0">
|
||||||
<Trans
|
<Trans
|
||||||
i18nKey={'Currently expected to <0>fail</0>'}
|
i18nKey={'Currently expected to <0>fail</0>'}
|
||||||
components={[<span className="text-vega-red" />]}
|
components={[<span className="text-vega-pink" />]}
|
||||||
/>
|
/>
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{isUpdateMarket && (
|
||||||
|
<div className="mb-4">
|
||||||
|
<h3 className={headingClasses}>{t('liquidityProviderVote')}</h3>
|
||||||
|
<div className={sectionWrapperClasses}>
|
||||||
|
<section
|
||||||
|
className={sectionClasses}
|
||||||
|
data-testid="lp-majority-breakdown"
|
||||||
|
>
|
||||||
|
<VoteProgress
|
||||||
|
percentageFor={yesLPPercentage}
|
||||||
|
colourfulBg={true}
|
||||||
|
testId="lp-majority-progress"
|
||||||
|
>
|
||||||
|
<Status
|
||||||
|
reached={majorityLPMet}
|
||||||
|
threshold={requiredMajorityLPPercentage}
|
||||||
|
text={t('majorityThreshold')}
|
||||||
|
testId={
|
||||||
|
majorityLPMet ? 'lp-majority-met' : 'lp-majority-not-met'
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</VoteProgress>
|
||||||
|
|
||||||
|
<div className={progressDetailsClasses}>
|
||||||
|
<div className="flex items-center gap-1">
|
||||||
|
<span>{t('liquidityProviderVotesFor')}:</span>
|
||||||
|
<Tooltip
|
||||||
|
description={
|
||||||
|
<span>{yesLPPercentage.toFixed(defaultDP)}%</span>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<button>{yesLPPercentage.toFixed(1)}%</button>
|
||||||
|
</Tooltip>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex items-center gap-1">
|
||||||
|
<span>{t('liquidityProviderVotesAgainst')}:</span>
|
||||||
|
<span>
|
||||||
|
<Tooltip
|
||||||
|
description={
|
||||||
|
<span>{noLPPercentage.toFixed(defaultDP)}%</span>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<button>{noLPPercentage.toFixed(1)}%</button>
|
||||||
|
</Tooltip>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section
|
||||||
|
className={sectionClasses}
|
||||||
|
data-testid="lp-participation-breakdown"
|
||||||
|
>
|
||||||
|
<VoteProgress
|
||||||
|
percentageFor={
|
||||||
|
lpParticipationThresholdProgress || new BigNumber(0)
|
||||||
|
}
|
||||||
|
testId="lp-participation-progress"
|
||||||
|
>
|
||||||
|
<Status
|
||||||
|
reached={participationLPMet}
|
||||||
|
threshold={requiredParticipationLP || new BigNumber(1)}
|
||||||
|
text={t('participationThreshold')}
|
||||||
|
testId={
|
||||||
|
participationLPMet
|
||||||
|
? 'lp-participation-met'
|
||||||
|
: 'lp-participation-not-met'
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</VoteProgress>
|
||||||
|
|
||||||
|
<div className="flex mt-2 text-sm">
|
||||||
|
<div className="flex items-center gap-1">
|
||||||
|
<span>{t('totalLiquidityProviderTokensVoted')}:</span>
|
||||||
|
<Tooltip
|
||||||
|
description={formatNumber(
|
||||||
|
totalEquityLikeShareWeight,
|
||||||
|
defaultDP
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<span>{totalEquityLikeShareWeight.toFixed(1)}%</span>
|
||||||
|
</Tooltip>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
{isUpdateMarket && <h3 className={headingClasses}>{t('tokenVote')}</h3>}
|
{isUpdateMarket && <h3 className={headingClasses}>{t('tokenVote')}</h3>}
|
||||||
<div className={sectionWrapperClasses}>
|
<div className={sectionWrapperClasses}>
|
||||||
<section
|
<section
|
||||||
@ -600,97 +594,6 @@ const VoteBreakDownUI = ({
|
|||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/** Liquidity provider vote */}
|
|
||||||
{isUpdateMarket && (
|
|
||||||
<div className="mt-3">
|
|
||||||
<h3 className={headingClasses}>{t('liquidityProviderVote')}</h3>
|
|
||||||
<div className={sectionWrapperClasses}>
|
|
||||||
<section
|
|
||||||
className={sectionClasses}
|
|
||||||
data-testid="lp-majority-breakdown"
|
|
||||||
>
|
|
||||||
<VoteProgress
|
|
||||||
percentageFor={lpVoteWeight}
|
|
||||||
colourfulBg={true}
|
|
||||||
testId="lp-majority-progress"
|
|
||||||
>
|
|
||||||
<Status
|
|
||||||
reached={majorityLPMet}
|
|
||||||
threshold={requiredMajorityLPPercentage}
|
|
||||||
text={t('majorityThreshold')}
|
|
||||||
testId={
|
|
||||||
majorityLPMet ? 'lp-majority-met' : 'lp-majority-not-met'
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</VoteProgress>
|
|
||||||
|
|
||||||
<div className={progressDetailsClasses}>
|
|
||||||
<div className="flex items-center gap-1">
|
|
||||||
<span>{t('liquidityProviderVotesFor')}:</span>
|
|
||||||
<Tooltip
|
|
||||||
description={
|
|
||||||
<span>{lpVoteWeight.toFixed(defaultDP)}%</span>
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<button>{lpVoteWeight.toFixed(1)}%</button>
|
|
||||||
</Tooltip>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="flex items-center gap-1">
|
|
||||||
<span>{t('liquidityProviderVotesAgainst')}:</span>
|
|
||||||
<span>
|
|
||||||
<Tooltip
|
|
||||||
description={
|
|
||||||
<span>{noLPPercentage.toFixed(defaultDP)}%</span>
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<button>{noLPPercentage.toFixed(1)}%</button>
|
|
||||||
</Tooltip>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section
|
|
||||||
className={sectionClasses}
|
|
||||||
data-testid="lp-participation-breakdown"
|
|
||||||
>
|
|
||||||
<VoteProgress
|
|
||||||
percentageFor={
|
|
||||||
lpParticipationThresholdProgress || new BigNumber(0)
|
|
||||||
}
|
|
||||||
testId="lp-participation-progress"
|
|
||||||
>
|
|
||||||
<Status
|
|
||||||
reached={participationLPMet}
|
|
||||||
threshold={requiredParticipationLP || new BigNumber(1)}
|
|
||||||
text={t('participationThreshold')}
|
|
||||||
testId={
|
|
||||||
participationLPMet
|
|
||||||
? 'lp-participation-met'
|
|
||||||
: 'lp-participation-not-met'
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</VoteProgress>
|
|
||||||
|
|
||||||
<div className="flex mt-2 text-sm">
|
|
||||||
<div className="flex items-center gap-1">
|
|
||||||
<span>{t('totalLiquidityProviderTokensVoted')}:</span>
|
|
||||||
<Tooltip
|
|
||||||
description={formatNumber(
|
|
||||||
totalEquityLikeShareWeight,
|
|
||||||
defaultDP
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
<span>{totalEquityLikeShareWeight.toFixed(1)}%</span>
|
|
||||||
</Tooltip>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { captureMessage } from '@sentry/minimal';
|
import { captureMessage } from '@sentry/minimal';
|
||||||
import { useVegaWallet } from '@vegaprotocol/wallet-react';
|
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||||
import { VoteValue } from '@vegaprotocol/types';
|
import { VoteValue } from '@vegaprotocol/types';
|
||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import { useUserVoteQuery } from './__generated__/Vote';
|
import { useUserVoteQuery } from './__generated__/Vote';
|
||||||
|
@ -1,19 +1,20 @@
|
|||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { Icon, ExternalLink } from '@vegaprotocol/ui-toolkit';
|
import { Icon, ExternalLink } from '@vegaprotocol/ui-toolkit';
|
||||||
import { useVegaWallet } from '@vegaprotocol/wallet-react';
|
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||||
import { ProposalState } from '@vegaprotocol/types';
|
import { ProposalState } from '@vegaprotocol/types';
|
||||||
import { ConnectToVega } from '../../../../components/connect-to-vega';
|
import { ConnectToVega } from '../../../../components/connect-to-vega';
|
||||||
import { VoteButtonsContainer } from './vote-buttons';
|
import { VoteButtonsContainer } from './vote-buttons';
|
||||||
import { SubHeading } from '../../../../components/heading';
|
import { SubHeading } from '../../../../components/heading';
|
||||||
import { type VoteValue } from '@vegaprotocol/types';
|
import { type VoteValue } from '@vegaprotocol/types';
|
||||||
import { type VegaTxState } from '@vegaprotocol/proposals';
|
import { type DialogProps, type VegaTxState } from '@vegaprotocol/proposals';
|
||||||
import { type VoteState } from './use-user-vote';
|
import { type VoteState } from './use-user-vote';
|
||||||
import { type Proposal, type BatchProposal } from '../../types';
|
import { type Proposal, type BatchProposal } from '../../types';
|
||||||
|
|
||||||
interface UserVoteProps {
|
interface UserVoteProps {
|
||||||
proposal: Proposal | BatchProposal;
|
proposal: Proposal | BatchProposal;
|
||||||
transaction: VegaTxState;
|
transaction: VegaTxState | null;
|
||||||
submit: (voteValue: VoteValue, proposalId: string | null) => Promise<void>;
|
submit: (voteValue: VoteValue, proposalId: string | null) => Promise<void>;
|
||||||
|
dialog: (props: DialogProps) => JSX.Element;
|
||||||
voteState: VoteState | null;
|
voteState: VoteState | null;
|
||||||
voteDatetime: Date | null;
|
voteDatetime: Date | null;
|
||||||
}
|
}
|
||||||
@ -22,6 +23,7 @@ export const UserVote = ({
|
|||||||
proposal,
|
proposal,
|
||||||
submit,
|
submit,
|
||||||
transaction,
|
transaction,
|
||||||
|
dialog,
|
||||||
voteState,
|
voteState,
|
||||||
voteDatetime,
|
voteDatetime,
|
||||||
}: UserVoteProps) => {
|
}: UserVoteProps) => {
|
||||||
@ -54,6 +56,7 @@ export const UserVote = ({
|
|||||||
className="flex"
|
className="flex"
|
||||||
submit={submit}
|
submit={submit}
|
||||||
transaction={transaction}
|
transaction={transaction}
|
||||||
|
dialog={dialog}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
) : (
|
) : (
|
||||||
|
@ -2,20 +2,33 @@ import { render, screen } from '@testing-library/react';
|
|||||||
import { VoteTransactionDialog } from './vote-transaction-dialog';
|
import { VoteTransactionDialog } from './vote-transaction-dialog';
|
||||||
import { VoteState } from './use-user-vote';
|
import { VoteState } from './use-user-vote';
|
||||||
import { VegaTxStatus } from '@vegaprotocol/proposals';
|
import { VegaTxStatus } from '@vegaprotocol/proposals';
|
||||||
import { ConnectorErrors, unknownError } from '@vegaprotocol/wallet';
|
|
||||||
|
|
||||||
describe('VoteTransactionDialog', () => {
|
describe('VoteTransactionDialog', () => {
|
||||||
|
const mockTransactionDialog = jest.fn(({ title, content }) => (
|
||||||
|
<div>
|
||||||
|
<div>{title}</div>
|
||||||
|
<div>{content?.Complete}</div>
|
||||||
|
</div>
|
||||||
|
));
|
||||||
|
|
||||||
|
it('renders without crashing', () => {
|
||||||
|
render(
|
||||||
|
<VoteTransactionDialog
|
||||||
|
voteState={VoteState.Yes}
|
||||||
|
transaction={null}
|
||||||
|
TransactionDialog={mockTransactionDialog}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(screen.getByTestId('vote-transaction-dialog')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
it('renders with txRequested title when voteState is Requested', () => {
|
it('renders with txRequested title when voteState is Requested', () => {
|
||||||
render(
|
render(
|
||||||
<VoteTransactionDialog
|
<VoteTransactionDialog
|
||||||
voteState={VoteState.Requested}
|
voteState={VoteState.Requested}
|
||||||
transaction={{
|
transaction={null}
|
||||||
error: null,
|
TransactionDialog={mockTransactionDialog}
|
||||||
txHash: null,
|
|
||||||
signature: null,
|
|
||||||
status: VegaTxStatus.Requested,
|
|
||||||
dialogOpen: true,
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -26,13 +39,8 @@ describe('VoteTransactionDialog', () => {
|
|||||||
render(
|
render(
|
||||||
<VoteTransactionDialog
|
<VoteTransactionDialog
|
||||||
voteState={VoteState.Pending}
|
voteState={VoteState.Pending}
|
||||||
transaction={{
|
transaction={null}
|
||||||
error: null,
|
TransactionDialog={mockTransactionDialog}
|
||||||
txHash: null,
|
|
||||||
signature: null,
|
|
||||||
status: VegaTxStatus.Pending,
|
|
||||||
dialogOpen: true,
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -43,13 +51,8 @@ describe('VoteTransactionDialog', () => {
|
|||||||
render(
|
render(
|
||||||
<VoteTransactionDialog
|
<VoteTransactionDialog
|
||||||
voteState={VoteState.Yes} // or any other state other than Requested or Pending
|
voteState={VoteState.Yes} // or any other state other than Requested or Pending
|
||||||
transaction={{
|
transaction={null}
|
||||||
error: null,
|
TransactionDialog={mockTransactionDialog}
|
||||||
txHash: null,
|
|
||||||
signature: null,
|
|
||||||
status: VegaTxStatus.Complete,
|
|
||||||
dialogOpen: true,
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -62,18 +65,17 @@ describe('VoteTransactionDialog', () => {
|
|||||||
<VoteTransactionDialog
|
<VoteTransactionDialog
|
||||||
voteState={VoteState.Failed}
|
voteState={VoteState.Failed}
|
||||||
transaction={{
|
transaction={{
|
||||||
error: unknownError(),
|
error: { message: 'Custom error test message', name: 'blah' },
|
||||||
txHash: null,
|
txHash: null,
|
||||||
signature: null,
|
signature: null,
|
||||||
status: VegaTxStatus.Error,
|
status: VegaTxStatus.Error,
|
||||||
dialogOpen: true,
|
dialogOpen: false,
|
||||||
}}
|
}}
|
||||||
|
TransactionDialog={mockTransactionDialog}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(
|
expect(screen.getByText('Custom error test message')).toBeInTheDocument();
|
||||||
screen.getByText(ConnectorErrors.unknown.message)
|
|
||||||
).toBeInTheDocument();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('renders default error message when voteState is failed and no error message exists on the tx', () => {
|
it('renders default error message when voteState is failed and no error message exists on the tx', () => {
|
||||||
@ -84,9 +86,10 @@ describe('VoteTransactionDialog', () => {
|
|||||||
error: null,
|
error: null,
|
||||||
txHash: null,
|
txHash: null,
|
||||||
signature: null,
|
signature: null,
|
||||||
status: VegaTxStatus.Complete,
|
status: VegaTxStatus.Error,
|
||||||
dialogOpen: true,
|
dialogOpen: false,
|
||||||
}}
|
}}
|
||||||
|
TransactionDialog={mockTransactionDialog}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -97,13 +100,8 @@ describe('VoteTransactionDialog', () => {
|
|||||||
render(
|
render(
|
||||||
<VoteTransactionDialog
|
<VoteTransactionDialog
|
||||||
voteState={VoteState.Yes}
|
voteState={VoteState.Yes}
|
||||||
transaction={{
|
transaction={null}
|
||||||
error: null,
|
TransactionDialog={mockTransactionDialog}
|
||||||
txHash: null,
|
|
||||||
signature: null,
|
|
||||||
status: VegaTxStatus.Default,
|
|
||||||
dialogOpen: true,
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -1,77 +1,120 @@
|
|||||||
import { act, fireEvent, render, screen } from '@testing-library/react';
|
import { fireEvent, render, screen } from '@testing-library/react';
|
||||||
import BigNumber from 'bignumber.js';
|
import BigNumber from 'bignumber.js';
|
||||||
import { VoteButtons, type VoteButtonsProps } from './vote-buttons';
|
import { VoteButtons } from './vote-buttons';
|
||||||
import { VoteState } from './use-user-vote';
|
import { VoteState } from './use-user-vote';
|
||||||
import { ProposalState } from '@vegaprotocol/types';
|
import { ProposalState } from '@vegaprotocol/types';
|
||||||
|
import { VegaWalletContext } from '@vegaprotocol/wallet';
|
||||||
|
import type { VegaWalletContextShape } from '@vegaprotocol/wallet';
|
||||||
|
import { mockWalletContext } from '../../test-helpers/mocks';
|
||||||
import { AppStateProvider } from '../../../../contexts/app-state/app-state-provider';
|
import { AppStateProvider } from '../../../../contexts/app-state/app-state-provider';
|
||||||
import { MockedProvider } from '@apollo/react-testing';
|
import { MockedProvider } from '@apollo/react-testing';
|
||||||
import { VegaTxStatus } from '@vegaprotocol/proposals';
|
|
||||||
import {
|
|
||||||
MockedWalletProvider,
|
|
||||||
mockConfig,
|
|
||||||
} from '@vegaprotocol/wallet-react/testing';
|
|
||||||
|
|
||||||
describe('Vote buttons', () => {
|
describe('Vote buttons', () => {
|
||||||
const key = { publicKey: '0x123', name: 'key 1' };
|
it('should render successfully', () => {
|
||||||
const transaction = {
|
const { baseElement } = render(
|
||||||
status: VegaTxStatus.Default,
|
|
||||||
error: null,
|
|
||||||
txHash: null,
|
|
||||||
signature: null,
|
|
||||||
dialogOpen: false,
|
|
||||||
};
|
|
||||||
const props = {
|
|
||||||
voteState: VoteState.NotCast,
|
|
||||||
voteDatetime: null,
|
|
||||||
proposalState: ProposalState.STATE_OPEN,
|
|
||||||
proposalId: null,
|
|
||||||
minVoterBalance: null,
|
|
||||||
spamProtectionMinTokens: null,
|
|
||||||
currentStakeAvailable: new BigNumber(1),
|
|
||||||
submit: () => Promise.resolve(),
|
|
||||||
transaction,
|
|
||||||
};
|
|
||||||
|
|
||||||
const renderComponent = (testProps?: Partial<VoteButtonsProps>) => {
|
|
||||||
return render(
|
|
||||||
<AppStateProvider>
|
<AppStateProvider>
|
||||||
<MockedProvider>
|
<MockedProvider>
|
||||||
<MockedWalletProvider>
|
<VegaWalletContext.Provider value={mockWalletContext}>
|
||||||
<VoteButtons {...props} {...testProps} />
|
<VoteButtons
|
||||||
</MockedWalletProvider>
|
voteState={VoteState.NotCast}
|
||||||
|
voteDatetime={null}
|
||||||
|
proposalState={ProposalState.STATE_OPEN}
|
||||||
|
proposalId={null}
|
||||||
|
minVoterBalance={null}
|
||||||
|
spamProtectionMinTokens={null}
|
||||||
|
currentStakeAvailable={new BigNumber(1)}
|
||||||
|
dialog={() => <div>Blah</div>}
|
||||||
|
submit={() => Promise.resolve()}
|
||||||
|
transaction={null}
|
||||||
|
/>
|
||||||
|
</VegaWalletContext.Provider>
|
||||||
</MockedProvider>
|
</MockedProvider>
|
||||||
</AppStateProvider>
|
</AppStateProvider>
|
||||||
);
|
);
|
||||||
};
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
mockConfig.store.setState({ pubKey: key.publicKey, keys: [key] });
|
|
||||||
});
|
|
||||||
|
|
||||||
afterEach(() => {
|
|
||||||
act(() => {
|
|
||||||
mockConfig.reset();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should render successfully', () => {
|
|
||||||
const { baseElement } = renderComponent();
|
|
||||||
expect(baseElement).toBeTruthy();
|
expect(baseElement).toBeTruthy();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should explain that voting is closed if the proposal is not open', () => {
|
it('should explain that voting is closed if the proposal is not open', () => {
|
||||||
renderComponent({ proposalState: ProposalState.STATE_PASSED });
|
render(
|
||||||
|
<AppStateProvider>
|
||||||
|
<MockedProvider>
|
||||||
|
<VegaWalletContext.Provider value={mockWalletContext}>
|
||||||
|
<VoteButtons
|
||||||
|
voteState={VoteState.NotCast}
|
||||||
|
voteDatetime={null}
|
||||||
|
proposalState={ProposalState.STATE_PASSED}
|
||||||
|
proposalId={null}
|
||||||
|
minVoterBalance={null}
|
||||||
|
spamProtectionMinTokens={null}
|
||||||
|
currentStakeAvailable={new BigNumber(1)}
|
||||||
|
dialog={() => <div>Blah</div>}
|
||||||
|
submit={() => Promise.resolve()}
|
||||||
|
transaction={null}
|
||||||
|
/>
|
||||||
|
</VegaWalletContext.Provider>
|
||||||
|
</MockedProvider>
|
||||||
|
</AppStateProvider>
|
||||||
|
);
|
||||||
expect(screen.getByText('Voting has ended.')).toBeTruthy();
|
expect(screen.getByText('Voting has ended.')).toBeTruthy();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should provide a connect wallet prompt if no pubkey', () => {
|
it('should provide a connect wallet prompt if no pubkey', () => {
|
||||||
mockConfig.reset();
|
const mockWalletNoPubKeyContext = {
|
||||||
renderComponent();
|
pubKey: null,
|
||||||
|
pubKeys: [],
|
||||||
|
isReadOnly: false,
|
||||||
|
sendTx: jest.fn().mockReturnValue(Promise.resolve(null)),
|
||||||
|
connect: jest.fn(),
|
||||||
|
disconnect: jest.fn(),
|
||||||
|
selectPubKey: jest.fn(),
|
||||||
|
connector: null,
|
||||||
|
} as unknown as VegaWalletContextShape;
|
||||||
|
|
||||||
|
render(
|
||||||
|
<AppStateProvider>
|
||||||
|
<MockedProvider>
|
||||||
|
<VegaWalletContext.Provider value={mockWalletNoPubKeyContext}>
|
||||||
|
<VoteButtons
|
||||||
|
voteState={VoteState.NotCast}
|
||||||
|
voteDatetime={null}
|
||||||
|
proposalState={ProposalState.STATE_OPEN}
|
||||||
|
proposalId={null}
|
||||||
|
minVoterBalance={null}
|
||||||
|
spamProtectionMinTokens={null}
|
||||||
|
currentStakeAvailable={new BigNumber(1)}
|
||||||
|
dialog={() => <div>Blah</div>}
|
||||||
|
submit={() => Promise.resolve()}
|
||||||
|
transaction={null}
|
||||||
|
/>
|
||||||
|
</VegaWalletContext.Provider>
|
||||||
|
</MockedProvider>
|
||||||
|
</AppStateProvider>
|
||||||
|
);
|
||||||
|
|
||||||
expect(screen.getByTestId('connect-wallet')).toBeTruthy();
|
expect(screen.getByTestId('connect-wallet')).toBeTruthy();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should tell the user they need tokens if their current stake is 0', () => {
|
it('should tell the user they need tokens if their current stake is 0', () => {
|
||||||
renderComponent({ currentStakeAvailable: new BigNumber(0) });
|
render(
|
||||||
|
<AppStateProvider>
|
||||||
|
<MockedProvider>
|
||||||
|
<VegaWalletContext.Provider value={mockWalletContext}>
|
||||||
|
<VoteButtons
|
||||||
|
voteState={VoteState.NotCast}
|
||||||
|
voteDatetime={null}
|
||||||
|
proposalState={ProposalState.STATE_OPEN}
|
||||||
|
proposalId={null}
|
||||||
|
minVoterBalance={null}
|
||||||
|
spamProtectionMinTokens={null}
|
||||||
|
currentStakeAvailable={new BigNumber(0)}
|
||||||
|
dialog={() => <div>Blah</div>}
|
||||||
|
submit={() => Promise.resolve()}
|
||||||
|
transaction={null}
|
||||||
|
/>
|
||||||
|
</VegaWalletContext.Provider>
|
||||||
|
</MockedProvider>
|
||||||
|
</AppStateProvider>
|
||||||
|
);
|
||||||
expect(
|
expect(
|
||||||
screen.getByText(
|
screen.getByText(
|
||||||
'You need some VEGA tokens to participate in governance.'
|
'You need some VEGA tokens to participate in governance.'
|
||||||
@ -80,10 +123,26 @@ describe('Vote buttons', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should tell the user of the minimum requirements if they have some, but not enough tokens', () => {
|
it('should tell the user of the minimum requirements if they have some, but not enough tokens', () => {
|
||||||
renderComponent({
|
render(
|
||||||
minVoterBalance: '2000000000000000000',
|
<AppStateProvider>
|
||||||
spamProtectionMinTokens: '1000000000000000000',
|
<MockedProvider>
|
||||||
});
|
<VegaWalletContext.Provider value={mockWalletContext}>
|
||||||
|
<VoteButtons
|
||||||
|
voteState={VoteState.NotCast}
|
||||||
|
voteDatetime={null}
|
||||||
|
proposalState={ProposalState.STATE_OPEN}
|
||||||
|
proposalId={null}
|
||||||
|
minVoterBalance="2000000000000000000"
|
||||||
|
spamProtectionMinTokens="1000000000000000000"
|
||||||
|
currentStakeAvailable={new BigNumber(1)}
|
||||||
|
dialog={() => <div>Blah</div>}
|
||||||
|
submit={() => Promise.resolve()}
|
||||||
|
transaction={null}
|
||||||
|
/>
|
||||||
|
</VegaWalletContext.Provider>
|
||||||
|
</MockedProvider>
|
||||||
|
</AppStateProvider>
|
||||||
|
);
|
||||||
expect(
|
expect(
|
||||||
screen.getByText(
|
screen.getByText(
|
||||||
'You must have at least 2 VEGA associated to vote on this proposal'
|
'You must have at least 2 VEGA associated to vote on this proposal'
|
||||||
@ -92,23 +151,51 @@ describe('Vote buttons', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should show you voted if vote state is correct, and if the proposal is still open, it will display a change vote button', () => {
|
it('should show you voted if vote state is correct, and if the proposal is still open, it will display a change vote button', () => {
|
||||||
renderComponent({
|
render(
|
||||||
voteState: VoteState.Yes,
|
<AppStateProvider>
|
||||||
minVoterBalance: '2000000000000000000',
|
<MockedProvider>
|
||||||
spamProtectionMinTokens: '1000000000000000000',
|
<VegaWalletContext.Provider value={mockWalletContext}>
|
||||||
currentStakeAvailable: new BigNumber(10),
|
<VoteButtons
|
||||||
});
|
voteState={VoteState.Yes}
|
||||||
|
voteDatetime={null}
|
||||||
|
proposalState={ProposalState.STATE_OPEN}
|
||||||
|
proposalId={null}
|
||||||
|
minVoterBalance="2000000000000000000"
|
||||||
|
spamProtectionMinTokens="1000000000000000000"
|
||||||
|
currentStakeAvailable={new BigNumber(10)}
|
||||||
|
dialog={() => <div>Blah</div>}
|
||||||
|
submit={() => Promise.resolve()}
|
||||||
|
transaction={null}
|
||||||
|
/>
|
||||||
|
</VegaWalletContext.Provider>
|
||||||
|
</MockedProvider>
|
||||||
|
</AppStateProvider>
|
||||||
|
);
|
||||||
expect(screen.getByTestId('you-voted')).toBeInTheDocument();
|
expect(screen.getByTestId('you-voted')).toBeInTheDocument();
|
||||||
expect(screen.getByTestId('change-vote-button')).toBeInTheDocument();
|
expect(screen.getByTestId('change-vote-button')).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should allow you to change your vote', () => {
|
it('should allow you to change your vote', () => {
|
||||||
renderComponent({
|
render(
|
||||||
voteState: VoteState.No,
|
<AppStateProvider>
|
||||||
minVoterBalance: '2000000000000000000',
|
<MockedProvider>
|
||||||
spamProtectionMinTokens: '1000000000000000000',
|
<VegaWalletContext.Provider value={mockWalletContext}>
|
||||||
currentStakeAvailable: new BigNumber(10),
|
<VoteButtons
|
||||||
});
|
voteState={VoteState.No}
|
||||||
|
voteDatetime={null}
|
||||||
|
proposalState={ProposalState.STATE_OPEN}
|
||||||
|
proposalId={null}
|
||||||
|
minVoterBalance="2000000000000000000"
|
||||||
|
spamProtectionMinTokens="1000000000000000000"
|
||||||
|
currentStakeAvailable={new BigNumber(10)}
|
||||||
|
dialog={() => <div>Blah</div>}
|
||||||
|
submit={() => Promise.resolve()}
|
||||||
|
transaction={null}
|
||||||
|
/>
|
||||||
|
</VegaWalletContext.Provider>
|
||||||
|
</MockedProvider>
|
||||||
|
</AppStateProvider>
|
||||||
|
);
|
||||||
fireEvent.click(screen.getByTestId('change-vote-button'));
|
fireEvent.click(screen.getByTestId('change-vote-button'));
|
||||||
expect(screen.getByTestId('vote-buttons')).toBeInTheDocument();
|
expect(screen.getByTestId('vote-buttons')).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { format } from 'date-fns';
|
import { format } from 'date-fns';
|
||||||
import React, { useMemo } from 'react';
|
import React, { useMemo } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { useVegaWallet, useDialogStore } from '@vegaprotocol/wallet-react';
|
import { useVegaWallet, useVegaWalletDialogStore } from '@vegaprotocol/wallet';
|
||||||
import {
|
import {
|
||||||
AsyncRenderer,
|
AsyncRenderer,
|
||||||
Button,
|
Button,
|
||||||
@ -17,7 +17,7 @@ import { VoteState } from './use-user-vote';
|
|||||||
import { ProposalMinRequirements, ProposalUserAction } from '../shared';
|
import { ProposalMinRequirements, ProposalUserAction } from '../shared';
|
||||||
import { VoteTransactionDialog } from './vote-transaction-dialog';
|
import { VoteTransactionDialog } from './vote-transaction-dialog';
|
||||||
import { useVoteButtonsQuery } from './__generated__/Stake';
|
import { useVoteButtonsQuery } from './__generated__/Stake';
|
||||||
import type { VegaTxState } from '@vegaprotocol/proposals';
|
import type { DialogProps, VegaTxState } from '@vegaprotocol/proposals';
|
||||||
import { filterAcceptableGraphqlErrors } from '../../../../lib/party';
|
import { filterAcceptableGraphqlErrors } from '../../../../lib/party';
|
||||||
import {
|
import {
|
||||||
NetworkParams,
|
NetworkParams,
|
||||||
@ -32,7 +32,8 @@ interface VoteButtonsContainerProps {
|
|||||||
proposalId: string | null;
|
proposalId: string | null;
|
||||||
proposalState: ProposalState;
|
proposalState: ProposalState;
|
||||||
submit: (voteValue: VoteValue, proposalId: string | null) => Promise<void>;
|
submit: (voteValue: VoteValue, proposalId: string | null) => Promise<void>;
|
||||||
transaction: VegaTxState;
|
transaction: VegaTxState | null;
|
||||||
|
dialog: (props: DialogProps) => JSX.Element;
|
||||||
className?: string;
|
className?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -135,16 +136,17 @@ export const VoteButtonsContainer = (props: VoteButtonsContainerProps) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export interface VoteButtonsProps {
|
interface VoteButtonsProps {
|
||||||
voteState: VoteState | null;
|
voteState: VoteState | null;
|
||||||
voteDatetime: Date | null;
|
voteDatetime: Date | null;
|
||||||
proposalState: ProposalState;
|
|
||||||
proposalId: string | null;
|
proposalId: string | null;
|
||||||
|
proposalState: ProposalState;
|
||||||
|
submit: (voteValue: VoteValue, proposalId: string | null) => Promise<void>;
|
||||||
|
transaction: VegaTxState | null;
|
||||||
|
dialog: (props: DialogProps) => JSX.Element;
|
||||||
currentStakeAvailable: BigNumber;
|
currentStakeAvailable: BigNumber;
|
||||||
minVoterBalance: string | null;
|
minVoterBalance: string | null;
|
||||||
spamProtectionMinTokens: string | null;
|
spamProtectionMinTokens: string | null;
|
||||||
submit: (voteValue: VoteValue, proposalId: string | null) => Promise<void>;
|
|
||||||
transaction: VegaTxState;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const VoteButtons = ({
|
export const VoteButtons = ({
|
||||||
@ -157,10 +159,13 @@ export const VoteButtons = ({
|
|||||||
spamProtectionMinTokens,
|
spamProtectionMinTokens,
|
||||||
submit,
|
submit,
|
||||||
transaction,
|
transaction,
|
||||||
|
dialog: Dialog,
|
||||||
}: VoteButtonsProps) => {
|
}: VoteButtonsProps) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { pubKey } = useVegaWallet();
|
const { pubKey } = useVegaWallet();
|
||||||
const openVegaWalletDialog = useDialogStore((store) => store.open);
|
const { openVegaWalletDialog } = useVegaWalletDialogStore((store) => ({
|
||||||
|
openVegaWalletDialog: store.openVegaWalletDialog,
|
||||||
|
}));
|
||||||
const [changeVote, setChangeVote] = React.useState(false);
|
const [changeVote, setChangeVote] = React.useState(false);
|
||||||
const proposalVotable = useMemo(
|
const proposalVotable = useMemo(
|
||||||
() =>
|
() =>
|
||||||
@ -179,7 +184,11 @@ export const VoteButtons = ({
|
|||||||
if (!pubKey) {
|
if (!pubKey) {
|
||||||
return (
|
return (
|
||||||
<div data-testid="connect-wallet">
|
<div data-testid="connect-wallet">
|
||||||
<ButtonLink onClick={openVegaWalletDialog}>
|
<ButtonLink
|
||||||
|
onClick={() => {
|
||||||
|
openVegaWalletDialog();
|
||||||
|
}}
|
||||||
|
>
|
||||||
{t('connectVegaWallet')}
|
{t('connectVegaWallet')}
|
||||||
</ButtonLink>{' '}
|
</ButtonLink>{' '}
|
||||||
{t('toVote')}
|
{t('toVote')}
|
||||||
@ -292,7 +301,11 @@ export const VoteButtons = ({
|
|||||||
</p>
|
</p>
|
||||||
)
|
)
|
||||||
)}
|
)}
|
||||||
<VoteTransactionDialog voteState={voteState} transaction={transaction} />
|
<VoteTransactionDialog
|
||||||
|
voteState={voteState}
|
||||||
|
transaction={transaction}
|
||||||
|
TransactionDialog={Dialog}
|
||||||
|
/>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -1,13 +1,11 @@
|
|||||||
import { t } from '@vegaprotocol/i18n';
|
import { t } from '@vegaprotocol/i18n';
|
||||||
import { VoteState } from './use-user-vote';
|
import { VoteState } from './use-user-vote';
|
||||||
import {
|
import type { DialogProps, VegaTxState } from '@vegaprotocol/proposals';
|
||||||
VegaTransactionDialog,
|
|
||||||
type VegaTxState,
|
|
||||||
} from '@vegaprotocol/proposals';
|
|
||||||
|
|
||||||
interface VoteTransactionDialogProps {
|
interface VoteTransactionDialogProps {
|
||||||
voteState: VoteState;
|
voteState: VoteState;
|
||||||
transaction: VegaTxState;
|
transaction: VegaTxState | null;
|
||||||
|
TransactionDialog: (props: DialogProps) => JSX.Element;
|
||||||
}
|
}
|
||||||
|
|
||||||
const dialogTitle = (voteState: VoteState): string | undefined => {
|
const dialogTitle = (voteState: VoteState): string | undefined => {
|
||||||
@ -24,23 +22,22 @@ const dialogTitle = (voteState: VoteState): string | undefined => {
|
|||||||
export const VoteTransactionDialog = ({
|
export const VoteTransactionDialog = ({
|
||||||
voteState,
|
voteState,
|
||||||
transaction,
|
transaction,
|
||||||
|
TransactionDialog,
|
||||||
}: VoteTransactionDialogProps) => {
|
}: VoteTransactionDialogProps) => {
|
||||||
// Render a custom message if the voting fails otherwise
|
// Render a custom message if the voting fails otherwise
|
||||||
// pass undefined so that the default vega transaction dialog UI gets used
|
// pass undefined so that the default vega transaction dialog UI gets used
|
||||||
const customMessage =
|
const customMessage =
|
||||||
voteState === VoteState.Failed ? (
|
voteState === VoteState.Failed ? (
|
||||||
<p>{transaction.error?.message || t('voteError')}</p>
|
<p>{transaction?.error?.message || t('voteError')}</p>
|
||||||
) : undefined;
|
) : undefined;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div data-testid="vote-transaction-dialog">
|
<div data-testid="vote-transaction-dialog">
|
||||||
<VegaTransactionDialog
|
<TransactionDialog
|
||||||
title={dialogTitle(voteState)}
|
title={dialogTitle(voteState)}
|
||||||
transaction={transaction}
|
|
||||||
content={{
|
content={{
|
||||||
Complete: customMessage,
|
Complete: customMessage,
|
||||||
}}
|
}}
|
||||||
isOpen={transaction.dialogOpen}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -273,74 +273,4 @@ describe('use-vote-information', () => {
|
|||||||
expect(current?.willPassByTokenVote).toEqual(false);
|
expect(current?.willPassByTokenVote).toEqual(false);
|
||||||
expect(current?.willPassByLPVote).toEqual(true);
|
expect(current?.willPassByLPVote).toEqual(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('mainnet recreation: only yes LP votes equal passing', () => {
|
|
||||||
const yesVotes = 0;
|
|
||||||
const noVotes = 70;
|
|
||||||
const yesEquityLikeShareWeight = '0.21';
|
|
||||||
const noEquityLikeShareWeight = '0';
|
|
||||||
const fixedTokenValue = 1000000000000000000;
|
|
||||||
|
|
||||||
const proposal = generateProposal({
|
|
||||||
terms: {
|
|
||||||
change: {
|
|
||||||
__typename: 'UpdateMarket',
|
|
||||||
marketId: '12345',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
votes: {
|
|
||||||
__typename: 'ProposalVotes',
|
|
||||||
yes: generateYesVotes(
|
|
||||||
yesVotes,
|
|
||||||
fixedTokenValue,
|
|
||||||
yesEquityLikeShareWeight
|
|
||||||
),
|
|
||||||
no: generateNoVotes(noVotes, fixedTokenValue, noEquityLikeShareWeight),
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const {
|
|
||||||
result: { current },
|
|
||||||
} = renderHook(() =>
|
|
||||||
useVoteInformation({ terms: proposal.terms, votes: proposal.votes })
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(current?.willPassByTokenVote).toEqual(false);
|
|
||||||
expect(current?.willPassByLPVote).toEqual(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('mainnet recreation: mixed yes and no LP votes equal failing', () => {
|
|
||||||
const yesVotes = 0;
|
|
||||||
const noVotes = 70;
|
|
||||||
const yesEquityLikeShareWeight = '0.21';
|
|
||||||
const noEquityLikeShareWeight = '0.22';
|
|
||||||
const fixedTokenValue = 1000000000000000000;
|
|
||||||
|
|
||||||
const proposal = generateProposal({
|
|
||||||
terms: {
|
|
||||||
change: {
|
|
||||||
__typename: 'UpdateMarket',
|
|
||||||
marketId: '12345',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
votes: {
|
|
||||||
__typename: 'ProposalVotes',
|
|
||||||
yes: generateYesVotes(
|
|
||||||
yesVotes,
|
|
||||||
fixedTokenValue,
|
|
||||||
yesEquityLikeShareWeight
|
|
||||||
),
|
|
||||||
no: generateNoVotes(noVotes, fixedTokenValue, noEquityLikeShareWeight),
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const {
|
|
||||||
result: { current },
|
|
||||||
} = renderHook(() =>
|
|
||||||
useVoteInformation({ terms: proposal.terms, votes: proposal.votes })
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(current?.willPassByTokenVote).toEqual(false);
|
|
||||||
expect(current?.willPassByLPVote).toEqual(false);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
@ -8,18 +8,13 @@ import {
|
|||||||
type VoteFieldsFragment,
|
type VoteFieldsFragment,
|
||||||
} from '../__generated__/Proposals';
|
} from '../__generated__/Proposals';
|
||||||
import { type ProposalChangeType } from '../types';
|
import { type ProposalChangeType } from '../types';
|
||||||
import sum from 'lodash/sum';
|
|
||||||
|
|
||||||
export const useVoteInformation = ({
|
export const useVoteInformation = ({
|
||||||
votes,
|
votes,
|
||||||
terms,
|
terms,
|
||||||
yesELS,
|
|
||||||
noELS,
|
|
||||||
}: {
|
}: {
|
||||||
votes: VoteFieldsFragment;
|
votes: VoteFieldsFragment;
|
||||||
terms: ProposalTermsFieldsFragment;
|
terms: ProposalTermsFieldsFragment;
|
||||||
yesELS?: number[];
|
|
||||||
noELS?: number[];
|
|
||||||
}) => {
|
}) => {
|
||||||
const {
|
const {
|
||||||
appState: { totalSupply, decimals },
|
appState: { totalSupply, decimals },
|
||||||
@ -36,9 +31,7 @@ export const useVoteInformation = ({
|
|||||||
paramsForChange,
|
paramsForChange,
|
||||||
votes,
|
votes,
|
||||||
totalSupply,
|
totalSupply,
|
||||||
decimals,
|
decimals
|
||||||
yesELS,
|
|
||||||
noELS
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -79,11 +72,7 @@ const getVoteData = (
|
|||||||
},
|
},
|
||||||
votes: ProposalFieldsFragment['votes'],
|
votes: ProposalFieldsFragment['votes'],
|
||||||
totalSupply: BigNumber,
|
totalSupply: BigNumber,
|
||||||
decimals: number,
|
decimals: number
|
||||||
/** A list of ELS yes votes */
|
|
||||||
yesELS?: number[],
|
|
||||||
/** A list if ELS no votes */
|
|
||||||
noELS?: number[]
|
|
||||||
) => {
|
) => {
|
||||||
const requiredMajorityPercentage = params.requiredMajority
|
const requiredMajorityPercentage = params.requiredMajority
|
||||||
? new BigNumber(params.requiredMajority).times(100)
|
? new BigNumber(params.requiredMajority).times(100)
|
||||||
@ -97,31 +86,17 @@ const getVoteData = (
|
|||||||
addDecimal(votes.no.totalTokens ?? 0, decimals)
|
addDecimal(votes.no.totalTokens ?? 0, decimals)
|
||||||
);
|
);
|
||||||
|
|
||||||
let noEquityLikeShareWeight = !votes.no.totalEquityLikeShareWeight
|
const noEquityLikeShareWeight = !votes.no.totalEquityLikeShareWeight
|
||||||
? new BigNumber(0)
|
? new BigNumber(0)
|
||||||
: new BigNumber(votes.no.totalEquityLikeShareWeight).times(100);
|
: new BigNumber(votes.no.totalEquityLikeShareWeight).times(100);
|
||||||
// there's no meaningful `totalEquityLikeShareWeight` in batch proposals,
|
|
||||||
// it has to be deduced from `elsPerMarket` of `no` votes of given proposal
|
|
||||||
// data. (by REST DATA)
|
|
||||||
if (noELS != null) {
|
|
||||||
const noTotalELS = sum(noELS);
|
|
||||||
noEquityLikeShareWeight = new BigNumber(noTotalELS).times(100);
|
|
||||||
}
|
|
||||||
|
|
||||||
const yesTokens = new BigNumber(
|
const yesTokens = new BigNumber(
|
||||||
addDecimal(votes.yes.totalTokens ?? 0, decimals)
|
addDecimal(votes.yes.totalTokens ?? 0, decimals)
|
||||||
);
|
);
|
||||||
|
|
||||||
let yesEquityLikeShareWeight = !votes.yes.totalEquityLikeShareWeight
|
const yesEquityLikeShareWeight = !votes.yes.totalEquityLikeShareWeight
|
||||||
? new BigNumber(0)
|
? new BigNumber(0)
|
||||||
: new BigNumber(votes.yes.totalEquityLikeShareWeight).times(100);
|
: new BigNumber(votes.yes.totalEquityLikeShareWeight).times(100);
|
||||||
// there's no meaningful `totalEquityLikeShareWeight` in batch proposals,
|
|
||||||
// it has to be deduced from `elsPerMarket` of `yes` votes of given proposal
|
|
||||||
// data. (by REST DATA)
|
|
||||||
if (noELS != null) {
|
|
||||||
const yesTotalELS = sum(yesELS);
|
|
||||||
yesEquityLikeShareWeight = new BigNumber(yesTotalELS).times(100);
|
|
||||||
}
|
|
||||||
|
|
||||||
const totalTokensVoted = yesTokens.plus(noTokens);
|
const totalTokensVoted = yesTokens.plus(noTokens);
|
||||||
|
|
||||||
@ -148,19 +123,15 @@ const getVoteData = (
|
|||||||
totalSupply.multipliedBy(params.requiredParticipation)
|
totalSupply.multipliedBy(params.requiredParticipation)
|
||||||
);
|
);
|
||||||
|
|
||||||
const lpVoteWeight = yesEquityLikeShareWeight
|
|
||||||
.dividedBy(totalEquityLikeShareWeight)
|
|
||||||
.multipliedBy(100);
|
|
||||||
|
|
||||||
const participationLPMet = params.requiredParticipationLP
|
const participationLPMet = params.requiredParticipationLP
|
||||||
? lpVoteWeight.isGreaterThan(params.requiredParticipationLP)
|
? totalEquityLikeShareWeight.isGreaterThan(params.requiredParticipationLP)
|
||||||
: false;
|
: false;
|
||||||
|
|
||||||
const majorityMet = yesPercentage.isGreaterThanOrEqualTo(
|
const majorityMet = yesPercentage.isGreaterThanOrEqualTo(
|
||||||
requiredMajorityPercentage
|
requiredMajorityPercentage
|
||||||
);
|
);
|
||||||
|
|
||||||
const majorityLPMet = lpVoteWeight.isGreaterThanOrEqualTo(
|
const majorityLPMet = yesLPPercentage.isGreaterThanOrEqualTo(
|
||||||
requiredMajorityLPPercentage
|
requiredMajorityLPPercentage
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -178,12 +149,14 @@ const getVoteData = (
|
|||||||
|
|
||||||
const willPassByLPVote =
|
const willPassByLPVote =
|
||||||
participationLPMet &&
|
participationLPMet &&
|
||||||
lpVoteWeight.isGreaterThanOrEqualTo(requiredMajorityLPPercentage);
|
new BigNumber(yesLPPercentage).isGreaterThanOrEqualTo(
|
||||||
|
requiredMajorityLPPercentage
|
||||||
|
);
|
||||||
|
|
||||||
let willPass = false;
|
let willPass = false;
|
||||||
|
|
||||||
if (changeType === 'UpdateMarket' || changeType === 'UpdateMarketState') {
|
if (changeType === 'UpdateMarket' || changeType === 'UpdateMarketState') {
|
||||||
willPass = willPassByTokenVote || willPassByLPVote;
|
willPass = willPassByTokenVote && willPassByLPVote;
|
||||||
} else {
|
} else {
|
||||||
willPass = willPassByTokenVote;
|
willPass = willPassByTokenVote;
|
||||||
}
|
}
|
||||||
@ -209,7 +182,6 @@ const getVoteData = (
|
|||||||
totalLPTokensPercentage,
|
totalLPTokensPercentage,
|
||||||
willPassByTokenVote,
|
willPassByTokenVote,
|
||||||
willPassByLPVote,
|
willPassByLPVote,
|
||||||
lpVoteWeight: lpVoteWeight.isNaN() ? new BigNumber(0) : lpVoteWeight,
|
|
||||||
yesVotes: new BigNumber(votes.yes.totalNumber ?? 0),
|
yesVotes: new BigNumber(votes.yes.totalNumber ?? 0),
|
||||||
noVotes: new BigNumber(votes.no.totalNumber ?? 0),
|
noVotes: new BigNumber(votes.no.totalNumber ?? 0),
|
||||||
totalVotes: new BigNumber(votes.yes.totalNumber ?? 0).plus(
|
totalVotes: new BigNumber(votes.yes.totalNumber ?? 0).plus(
|
||||||
|
@ -1,16 +1,17 @@
|
|||||||
import { useParams } from 'react-router-dom';
|
import { useParams } from 'react-router-dom';
|
||||||
import { AsyncRenderer } from '@vegaprotocol/ui-toolkit';
|
import { AsyncRenderer } from '@vegaprotocol/ui-toolkit';
|
||||||
|
import { useFetch } from '@vegaprotocol/react-helpers';
|
||||||
|
import { ENV } from '../../../config';
|
||||||
import { Proposal } from '../components/proposal';
|
import { Proposal } from '../components/proposal';
|
||||||
import { ProposalNotFound } from '../components/proposal-not-found';
|
import { ProposalNotFound } from '../components/proposal-not-found';
|
||||||
import { useProposalQuery } from '../__generated__/Proposals';
|
import { useProposalQuery } from '../__generated__/Proposals';
|
||||||
import { useFetchProposal } from '../components/proposal/proposal-utils';
|
|
||||||
|
|
||||||
export const ProposalContainer = () => {
|
export const ProposalContainer = () => {
|
||||||
const params = useParams<{ proposalId: string }>();
|
const params = useParams<{ proposalId: string }>();
|
||||||
|
|
||||||
const { data: restData, loading: restLoading } = useFetchProposal({
|
const {
|
||||||
proposalId: params.proposalId,
|
state: { data: restData, loading: restLoading, error: restError },
|
||||||
});
|
} = useFetch(`${ENV.rest}governance?proposalId=${params.proposalId}`);
|
||||||
|
|
||||||
const { data, loading, error } = useProposalQuery({
|
const { data, loading, error } = useProposalQuery({
|
||||||
fetchPolicy: 'network-only',
|
fetchPolicy: 'network-only',
|
||||||
@ -25,7 +26,7 @@ export const ProposalContainer = () => {
|
|||||||
return (
|
return (
|
||||||
<AsyncRenderer
|
<AsyncRenderer
|
||||||
loading={Boolean(loading || restLoading)}
|
loading={Boolean(loading || restLoading)}
|
||||||
error={error}
|
error={error || restError}
|
||||||
data={{
|
data={{
|
||||||
...data,
|
...data,
|
||||||
...(restData ? { restData } : {}),
|
...(restData ? { restData } : {}),
|
||||||
|
@ -1,12 +1,13 @@
|
|||||||
import { render, screen } from '@testing-library/react';
|
import { render, screen } from '@testing-library/react';
|
||||||
import { ProposeFreeform } from './propose-freeform';
|
import { ProposeFreeform } from './propose-freeform';
|
||||||
import { MockedProvider } from '@apollo/client/testing';
|
import { MockedProvider } from '@apollo/client/testing';
|
||||||
|
import { mockWalletContext } from '../../test-helpers/mocks';
|
||||||
import { AppStateProvider } from '../../../../contexts/app-state/app-state-provider';
|
import { AppStateProvider } from '../../../../contexts/app-state/app-state-provider';
|
||||||
|
import { VegaWalletContext } from '@vegaprotocol/wallet';
|
||||||
import { MemoryRouter as Router } from 'react-router-dom';
|
import { MemoryRouter as Router } from 'react-router-dom';
|
||||||
import type { NetworkParamsQuery } from '@vegaprotocol/network-parameters';
|
import type { NetworkParamsQuery } from '@vegaprotocol/network-parameters';
|
||||||
import type { MockedResponse } from '@apollo/client/testing';
|
import type { MockedResponse } from '@apollo/client/testing';
|
||||||
import { NetworkParamsDocument } from '@vegaprotocol/network-parameters';
|
import { NetworkParamsDocument } from '@vegaprotocol/network-parameters';
|
||||||
import { MockedWalletProvider } from '@vegaprotocol/wallet-react/testing';
|
|
||||||
|
|
||||||
jest.mock('@vegaprotocol/environment', () => ({
|
jest.mock('@vegaprotocol/environment', () => ({
|
||||||
...jest.requireActual('@vegaprotocol/environment'),
|
...jest.requireActual('@vegaprotocol/environment'),
|
||||||
@ -71,19 +72,18 @@ const updateMarketNetworkParamsQueryMock: MockedResponse<NetworkParamsQuery> = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const renderComponent = () => {
|
const renderComponent = () =>
|
||||||
return render(
|
render(
|
||||||
<Router>
|
<Router>
|
||||||
<MockedProvider mocks={[updateMarketNetworkParamsQueryMock]}>
|
<MockedProvider mocks={[updateMarketNetworkParamsQueryMock]}>
|
||||||
<MockedWalletProvider>
|
<AppStateProvider>
|
||||||
<AppStateProvider>
|
<VegaWalletContext.Provider value={mockWalletContext}>
|
||||||
<ProposeFreeform />
|
<ProposeFreeform />
|
||||||
</AppStateProvider>
|
</VegaWalletContext.Provider>
|
||||||
</MockedWalletProvider>
|
</AppStateProvider>
|
||||||
</MockedProvider>
|
</MockedProvider>
|
||||||
</Router>
|
</Router>
|
||||||
);
|
);
|
||||||
};
|
|
||||||
|
|
||||||
// Note: form submission is tested in propose-raw.spec.tsx. Reusable form
|
// Note: form submission is tested in propose-raw.spec.tsx. Reusable form
|
||||||
// components are tested in their own directory.
|
// components are tested in their own directory.
|
||||||
|
@ -51,8 +51,7 @@ export const ProposeFreeform = () => {
|
|||||||
watch,
|
watch,
|
||||||
trigger,
|
trigger,
|
||||||
} = useForm<FreeformProposalFormFields>();
|
} = useForm<FreeformProposalFormFields>();
|
||||||
const { finalizedProposal, transaction, submit, setTransaction } =
|
const { finalizedProposal, submit, Dialog } = useProposalSubmit();
|
||||||
useProposalSubmit();
|
|
||||||
|
|
||||||
const assembleProposal = (fields: FreeformProposalFormFields) => {
|
const assembleProposal = (fields: FreeformProposalFormFields) => {
|
||||||
const isVoteDeadlineAtMinimum =
|
const isVoteDeadlineAtMinimum =
|
||||||
@ -170,8 +169,7 @@ export const ProposeFreeform = () => {
|
|||||||
<ProposalFormDownloadJson downloadJson={viewJson} />
|
<ProposalFormDownloadJson downloadJson={viewJson} />
|
||||||
<ProposalFormTransactionDialog
|
<ProposalFormTransactionDialog
|
||||||
finalizedProposal={finalizedProposal}
|
finalizedProposal={finalizedProposal}
|
||||||
transaction={transaction}
|
TransactionDialog={Dialog}
|
||||||
onChange={(open) => setTransaction({ dialogOpen: open })}
|
|
||||||
/>
|
/>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,12 +1,13 @@
|
|||||||
import { fireEvent, render, screen } from '@testing-library/react';
|
import { fireEvent, render, screen } from '@testing-library/react';
|
||||||
import { ProposeNetworkParameter } from './propose-network-parameter';
|
import { ProposeNetworkParameter } from './propose-network-parameter';
|
||||||
import { MockedProvider } from '@apollo/client/testing';
|
import { MockedProvider } from '@apollo/client/testing';
|
||||||
|
import { mockWalletContext } from '../../test-helpers/mocks';
|
||||||
import { AppStateProvider } from '../../../../contexts/app-state/app-state-provider';
|
import { AppStateProvider } from '../../../../contexts/app-state/app-state-provider';
|
||||||
|
import { VegaWalletContext } from '@vegaprotocol/wallet';
|
||||||
import { MemoryRouter as Router } from 'react-router-dom';
|
import { MemoryRouter as Router } from 'react-router-dom';
|
||||||
import type { NetworkParamsQuery } from '@vegaprotocol/network-parameters';
|
import type { NetworkParamsQuery } from '@vegaprotocol/network-parameters';
|
||||||
import { NetworkParamsDocument } from '@vegaprotocol/network-parameters';
|
import { NetworkParamsDocument } from '@vegaprotocol/network-parameters';
|
||||||
import type { MockedResponse } from '@apollo/client/testing';
|
import type { MockedResponse } from '@apollo/client/testing';
|
||||||
import { MockedWalletProvider } from '@vegaprotocol/wallet-react/testing';
|
|
||||||
|
|
||||||
jest.mock('@vegaprotocol/environment', () => ({
|
jest.mock('@vegaprotocol/environment', () => ({
|
||||||
...jest.requireActual('@vegaprotocol/environment'),
|
...jest.requireActual('@vegaprotocol/environment'),
|
||||||
@ -71,27 +72,26 @@ const updateMarketNetworkParamsQueryMock: MockedResponse<NetworkParamsQuery> = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const renderComponent = () => {
|
const renderComponent = () =>
|
||||||
return render(
|
render(
|
||||||
<Router>
|
<Router>
|
||||||
<MockedProvider mocks={[updateMarketNetworkParamsQueryMock]}>
|
<MockedProvider mocks={[updateMarketNetworkParamsQueryMock]}>
|
||||||
<MockedWalletProvider>
|
<AppStateProvider>
|
||||||
<AppStateProvider>
|
<VegaWalletContext.Provider value={mockWalletContext}>
|
||||||
<ProposeNetworkParameter />
|
<ProposeNetworkParameter />
|
||||||
</AppStateProvider>
|
</VegaWalletContext.Provider>
|
||||||
</MockedWalletProvider>
|
</AppStateProvider>
|
||||||
</MockedProvider>
|
</MockedProvider>
|
||||||
</Router>
|
</Router>
|
||||||
);
|
);
|
||||||
};
|
|
||||||
|
|
||||||
// Note: form submission is tested in propose-raw.spec.tsx. Reusable form
|
// Note: form submission is tested in propose-raw.spec.tsx. Reusable form
|
||||||
// components are tested in their own directory.
|
// components are tested in their own directory.
|
||||||
|
|
||||||
describe('Propose Network Parameter', () => {
|
describe('Propose Network Parameter', () => {
|
||||||
it('should render successfully', () => {
|
it('should render successfully', async () => {
|
||||||
const { baseElement } = renderComponent();
|
const { baseElement } = renderComponent();
|
||||||
expect(baseElement).toBeTruthy();
|
await expect(baseElement).toBeTruthy();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should render the correct title', async () => {
|
it('should render the correct title', async () => {
|
||||||
|
@ -91,8 +91,7 @@ export const ProposeNetworkParameter = () => {
|
|||||||
watch,
|
watch,
|
||||||
trigger,
|
trigger,
|
||||||
} = useForm<NetworkParameterProposalFormFields>();
|
} = useForm<NetworkParameterProposalFormFields>();
|
||||||
const { finalizedProposal, transaction, submit, setTransaction } =
|
const { finalizedProposal, submit, Dialog } = useProposalSubmit();
|
||||||
useProposalSubmit();
|
|
||||||
|
|
||||||
const selectedParamEntry = params
|
const selectedParamEntry = params
|
||||||
? Object.entries(params).find(([key]) => key === selectedNetworkParam)
|
? Object.entries(params).find(([key]) => key === selectedNetworkParam)
|
||||||
@ -313,8 +312,7 @@ export const ProposeNetworkParameter = () => {
|
|||||||
<ProposalFormDownloadJson downloadJson={viewJson} />
|
<ProposalFormDownloadJson downloadJson={viewJson} />
|
||||||
<ProposalFormTransactionDialog
|
<ProposalFormTransactionDialog
|
||||||
finalizedProposal={finalizedProposal}
|
finalizedProposal={finalizedProposal}
|
||||||
transaction={transaction}
|
TransactionDialog={Dialog}
|
||||||
onChange={(open) => setTransaction({ dialogOpen: open })}
|
|
||||||
/>
|
/>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,12 +1,13 @@
|
|||||||
import { MockedProvider } from '@apollo/client/testing';
|
import { MockedProvider } from '@apollo/client/testing';
|
||||||
import { MemoryRouter as Router } from 'react-router-dom';
|
import { MemoryRouter as Router } from 'react-router-dom';
|
||||||
import { render, screen } from '@testing-library/react';
|
import { render, screen } from '@testing-library/react';
|
||||||
|
import { VegaWalletContext } from '@vegaprotocol/wallet';
|
||||||
import { AppStateProvider } from '../../../../contexts/app-state/app-state-provider';
|
import { AppStateProvider } from '../../../../contexts/app-state/app-state-provider';
|
||||||
|
import { mockWalletContext } from '../../test-helpers/mocks';
|
||||||
import { ProposeNewAsset } from './propose-new-asset';
|
import { ProposeNewAsset } from './propose-new-asset';
|
||||||
import type { NetworkParamsQuery } from '@vegaprotocol/network-parameters';
|
import type { NetworkParamsQuery } from '@vegaprotocol/network-parameters';
|
||||||
import type { MockedResponse } from '@apollo/client/testing';
|
import type { MockedResponse } from '@apollo/client/testing';
|
||||||
import { NetworkParamsDocument } from '@vegaprotocol/network-parameters';
|
import { NetworkParamsDocument } from '@vegaprotocol/network-parameters';
|
||||||
import { MockedWalletProvider } from '@vegaprotocol/wallet-react/testing';
|
|
||||||
|
|
||||||
jest.mock('@vegaprotocol/environment', () => ({
|
jest.mock('@vegaprotocol/environment', () => ({
|
||||||
...jest.requireActual('@vegaprotocol/environment'),
|
...jest.requireActual('@vegaprotocol/environment'),
|
||||||
@ -71,27 +72,26 @@ const newAssetNetworkParamsQueryMock: MockedResponse<NetworkParamsQuery> = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const renderComponent = () => {
|
const renderComponent = () =>
|
||||||
return render(
|
render(
|
||||||
<Router>
|
<Router>
|
||||||
<MockedProvider mocks={[newAssetNetworkParamsQueryMock]}>
|
<MockedProvider mocks={[newAssetNetworkParamsQueryMock]}>
|
||||||
<MockedWalletProvider>
|
<AppStateProvider>
|
||||||
<AppStateProvider>
|
<VegaWalletContext.Provider value={mockWalletContext}>
|
||||||
<ProposeNewAsset />
|
<ProposeNewAsset />
|
||||||
</AppStateProvider>
|
</VegaWalletContext.Provider>
|
||||||
</MockedWalletProvider>
|
</AppStateProvider>
|
||||||
</MockedProvider>
|
</MockedProvider>
|
||||||
</Router>
|
</Router>
|
||||||
);
|
);
|
||||||
};
|
|
||||||
|
|
||||||
// Note: form submission is tested in propose-raw.spec.tsx. Reusable form
|
// Note: form submission is tested in propose-raw.spec.tsx. Reusable form
|
||||||
// components are tested in their own directory.
|
// components are tested in their own directory.
|
||||||
|
|
||||||
describe('Propose New Asset', () => {
|
describe('Propose New Asset', () => {
|
||||||
it('should render successfully', () => {
|
it('should render successfully', async () => {
|
||||||
const { baseElement } = renderComponent();
|
const { baseElement } = renderComponent();
|
||||||
expect(baseElement).toBeTruthy();
|
await expect(baseElement).toBeTruthy();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should render the title', async () => {
|
it('should render the title', async () => {
|
||||||
|
@ -64,8 +64,7 @@ export const ProposeNewAsset = () => {
|
|||||||
watch,
|
watch,
|
||||||
trigger,
|
trigger,
|
||||||
} = useForm<NewAssetProposalFormFields>();
|
} = useForm<NewAssetProposalFormFields>();
|
||||||
const { finalizedProposal, transaction, submit, setTransaction } =
|
const { finalizedProposal, submit, Dialog } = useProposalSubmit();
|
||||||
useProposalSubmit();
|
|
||||||
|
|
||||||
const assembleProposal = (fields: NewAssetProposalFormFields) => {
|
const assembleProposal = (fields: NewAssetProposalFormFields) => {
|
||||||
const isVoteDeadlineAtMinimum = doesValueEquateToParam(
|
const isVoteDeadlineAtMinimum = doesValueEquateToParam(
|
||||||
@ -233,8 +232,7 @@ export const ProposeNewAsset = () => {
|
|||||||
<ProposalFormDownloadJson downloadJson={viewJson} />
|
<ProposalFormDownloadJson downloadJson={viewJson} />
|
||||||
<ProposalFormTransactionDialog
|
<ProposalFormTransactionDialog
|
||||||
finalizedProposal={finalizedProposal}
|
finalizedProposal={finalizedProposal}
|
||||||
transaction={transaction}
|
TransactionDialog={Dialog}
|
||||||
onChange={(open) => setTransaction({ dialogOpen: open })}
|
|
||||||
/>
|
/>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,12 +1,13 @@
|
|||||||
import { render, screen } from '@testing-library/react';
|
import { render, screen } from '@testing-library/react';
|
||||||
import { ProposeNewMarket } from './propose-new-market';
|
import { ProposeNewMarket } from './propose-new-market';
|
||||||
import { MockedProvider } from '@apollo/client/testing';
|
import { MockedProvider } from '@apollo/client/testing';
|
||||||
|
import { mockWalletContext } from '../../test-helpers/mocks';
|
||||||
import { AppStateProvider } from '../../../../contexts/app-state/app-state-provider';
|
import { AppStateProvider } from '../../../../contexts/app-state/app-state-provider';
|
||||||
|
import { VegaWalletContext } from '@vegaprotocol/wallet';
|
||||||
import { BrowserRouter as Router } from 'react-router-dom';
|
import { BrowserRouter as Router } from 'react-router-dom';
|
||||||
import type { MockedResponse } from '@apollo/client/testing';
|
import type { MockedResponse } from '@apollo/client/testing';
|
||||||
import type { NetworkParamsQuery } from '@vegaprotocol/network-parameters';
|
import type { NetworkParamsQuery } from '@vegaprotocol/network-parameters';
|
||||||
import { NetworkParamsDocument } from '@vegaprotocol/network-parameters';
|
import { NetworkParamsDocument } from '@vegaprotocol/network-parameters';
|
||||||
import { MockedWalletProvider } from '@vegaprotocol/wallet-react/testing';
|
|
||||||
|
|
||||||
jest.mock('@vegaprotocol/environment', () => ({
|
jest.mock('@vegaprotocol/environment', () => ({
|
||||||
...jest.requireActual('@vegaprotocol/environment'),
|
...jest.requireActual('@vegaprotocol/environment'),
|
||||||
@ -71,27 +72,26 @@ const newMarketNetworkParamsQueryMock: MockedResponse<NetworkParamsQuery> = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const renderComponent = () => {
|
const renderComponent = () =>
|
||||||
return render(
|
render(
|
||||||
<Router>
|
<Router>
|
||||||
<MockedProvider mocks={[newMarketNetworkParamsQueryMock]}>
|
<MockedProvider mocks={[newMarketNetworkParamsQueryMock]}>
|
||||||
<MockedWalletProvider>
|
<AppStateProvider>
|
||||||
<AppStateProvider>
|
<VegaWalletContext.Provider value={mockWalletContext}>
|
||||||
<ProposeNewMarket />
|
<ProposeNewMarket />
|
||||||
</AppStateProvider>
|
</VegaWalletContext.Provider>
|
||||||
</MockedWalletProvider>
|
</AppStateProvider>
|
||||||
</MockedProvider>
|
</MockedProvider>
|
||||||
</Router>
|
</Router>
|
||||||
);
|
);
|
||||||
};
|
|
||||||
|
|
||||||
// Note: form submission is tested in propose-raw.spec.tsx. Reusable form
|
// Note: form submission is tested in propose-raw.spec.tsx. Reusable form
|
||||||
// components are tested in their own directory.
|
// components are tested in their own directory.
|
||||||
|
|
||||||
describe('Propose New Market', () => {
|
describe('Propose New Market', () => {
|
||||||
it('should render successfully', () => {
|
it('should render successfully', async () => {
|
||||||
const { baseElement } = renderComponent();
|
const { baseElement } = renderComponent();
|
||||||
expect(baseElement).toBeTruthy();
|
await expect(baseElement).toBeTruthy();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should render the form components', async () => {
|
it('should render the form components', async () => {
|
||||||
|
@ -62,8 +62,7 @@ export const ProposeNewMarket = () => {
|
|||||||
watch,
|
watch,
|
||||||
trigger,
|
trigger,
|
||||||
} = useForm<NewMarketProposalFormFields>();
|
} = useForm<NewMarketProposalFormFields>();
|
||||||
const { finalizedProposal, transaction, submit, setTransaction } =
|
const { finalizedProposal, submit, Dialog } = useProposalSubmit();
|
||||||
useProposalSubmit();
|
|
||||||
|
|
||||||
const assembleProposal = (fields: NewMarketProposalFormFields) => {
|
const assembleProposal = (fields: NewMarketProposalFormFields) => {
|
||||||
const isVoteDeadlineAtMinimum = doesValueEquateToParam(
|
const isVoteDeadlineAtMinimum = doesValueEquateToParam(
|
||||||
@ -215,8 +214,7 @@ export const ProposeNewMarket = () => {
|
|||||||
<ProposalFormDownloadJson downloadJson={viewJson} />
|
<ProposalFormDownloadJson downloadJson={viewJson} />
|
||||||
<ProposalFormTransactionDialog
|
<ProposalFormTransactionDialog
|
||||||
finalizedProposal={finalizedProposal}
|
finalizedProposal={finalizedProposal}
|
||||||
transaction={transaction}
|
TransactionDialog={Dialog}
|
||||||
onChange={(open) => setTransaction({ dialogOpen: open })}
|
|
||||||
/>
|
/>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
@ -3,6 +3,8 @@ import type { MockedResponse } from '@apollo/client/testing';
|
|||||||
import { addHours, getTime } from 'date-fns';
|
import { addHours, getTime } from 'date-fns';
|
||||||
import { AppStateProvider } from '../../../../contexts/app-state/app-state-provider';
|
import { AppStateProvider } from '../../../../contexts/app-state/app-state-provider';
|
||||||
import { MockedProvider } from '@apollo/client/testing';
|
import { MockedProvider } from '@apollo/client/testing';
|
||||||
|
import type { VegaWalletContextShape } from '@vegaprotocol/wallet';
|
||||||
|
import { VegaWalletContext } from '@vegaprotocol/wallet';
|
||||||
import * as Schema from '@vegaprotocol/types';
|
import * as Schema from '@vegaprotocol/types';
|
||||||
import { ProposeRaw } from './propose-raw';
|
import { ProposeRaw } from './propose-raw';
|
||||||
import { ProposalEventDocument } from '@vegaprotocol/proposals';
|
import { ProposalEventDocument } from '@vegaprotocol/proposals';
|
||||||
@ -10,11 +12,6 @@ import type { ProposalEventSubscription } from '@vegaprotocol/proposals';
|
|||||||
|
|
||||||
import type { NetworkParamsQuery } from '@vegaprotocol/network-parameters';
|
import type { NetworkParamsQuery } from '@vegaprotocol/network-parameters';
|
||||||
import { NetworkParamsDocument } from '@vegaprotocol/network-parameters';
|
import { NetworkParamsDocument } from '@vegaprotocol/network-parameters';
|
||||||
import {
|
|
||||||
MockedWalletProvider,
|
|
||||||
mockConfig,
|
|
||||||
} from '@vegaprotocol/wallet-react/testing';
|
|
||||||
import { userRejectedError } from '@vegaprotocol/wallet';
|
|
||||||
|
|
||||||
const paramsDelay = 20;
|
const paramsDelay = 20;
|
||||||
|
|
||||||
@ -106,15 +103,23 @@ describe('Raw proposal form', () => {
|
|||||||
},
|
},
|
||||||
delay: 300,
|
delay: 300,
|
||||||
};
|
};
|
||||||
const setup = () => {
|
const setup = (mockSendTx = jest.fn()) => {
|
||||||
return render(
|
return render(
|
||||||
<AppStateProvider>
|
<AppStateProvider>
|
||||||
<MockedProvider
|
<MockedProvider
|
||||||
mocks={[rawProposalNetworkParamsQueryMock, mockProposalEvent]}
|
mocks={[rawProposalNetworkParamsQueryMock, mockProposalEvent]}
|
||||||
>
|
>
|
||||||
<MockedWalletProvider>
|
<VegaWalletContext.Provider
|
||||||
|
value={
|
||||||
|
{
|
||||||
|
pubKey,
|
||||||
|
sendTx: mockSendTx,
|
||||||
|
links: { explorer: 'explorer' },
|
||||||
|
} as unknown as VegaWalletContextShape
|
||||||
|
}
|
||||||
|
>
|
||||||
<ProposeRaw />
|
<ProposeRaw />
|
||||||
</MockedWalletProvider>
|
</VegaWalletContext.Provider>
|
||||||
</MockedProvider>
|
</MockedProvider>
|
||||||
</AppStateProvider>
|
</AppStateProvider>
|
||||||
);
|
);
|
||||||
@ -122,22 +127,15 @@ describe('Raw proposal form', () => {
|
|||||||
|
|
||||||
beforeAll(() => {
|
beforeAll(() => {
|
||||||
jest.useFakeTimers();
|
jest.useFakeTimers();
|
||||||
mockConfig.store.setState({ status: 'connected', pubKey: '0x123' });
|
|
||||||
});
|
});
|
||||||
|
|
||||||
afterAll(() => {
|
afterAll(() => {
|
||||||
jest.useRealTimers();
|
jest.useRealTimers();
|
||||||
mockConfig.reset();
|
|
||||||
});
|
|
||||||
|
|
||||||
afterEach(() => {
|
|
||||||
jest.clearAllMocks();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('handles validation', async () => {
|
it('handles validation', async () => {
|
||||||
const mockSendTx = jest.spyOn(mockConfig, 'sendTransaction');
|
const mockSendTx = jest.fn().mockReturnValue(Promise.resolve());
|
||||||
|
setup(mockSendTx);
|
||||||
setup();
|
|
||||||
|
|
||||||
expect(await screen.findByTestId('proposal-submit')).toBeTruthy();
|
expect(await screen.findByTestId('proposal-submit')).toBeTruthy();
|
||||||
await act(async () => {
|
await act(async () => {
|
||||||
@ -164,25 +162,20 @@ describe('Raw proposal form', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('sends the transaction', async () => {
|
it('sends the transaction', async () => {
|
||||||
const mockSendTx = jest
|
const mockSendTx = jest.fn().mockReturnValue(
|
||||||
.spyOn(mockConfig, 'sendTransaction')
|
new Promise((resolve) => {
|
||||||
.mockReturnValue(
|
setTimeout(
|
||||||
new Promise((resolve) => {
|
() =>
|
||||||
setTimeout(
|
resolve({
|
||||||
() =>
|
transactionHash: 'tx-hash',
|
||||||
resolve({
|
signature:
|
||||||
transactionHash: 'tx-hash',
|
'cfe592d169f87d0671dd447751036d0dddc165b9c4b65e5a5060e2bbadd1aa726d4cbe9d3c3b327bcb0bff4f83999592619a2493f9bbd251fae99ce7ce766909',
|
||||||
signature:
|
}),
|
||||||
'cfe592d169f87d0671dd447751036d0dddc165b9c4b65e5a5060e2bbadd1aa726d4cbe9d3c3b327bcb0bff4f83999592619a2493f9bbd251fae99ce7ce766909',
|
100
|
||||||
sentAt: new Date().toISOString(),
|
);
|
||||||
receivedAt: new Date().toISOString(),
|
})
|
||||||
}),
|
);
|
||||||
100
|
setup(mockSendTx);
|
||||||
);
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
setup();
|
|
||||||
|
|
||||||
await act(async () => {
|
await act(async () => {
|
||||||
jest.advanceTimersByTime(paramsDelay);
|
jest.advanceTimersByTime(paramsDelay);
|
||||||
@ -213,12 +206,8 @@ describe('Raw proposal form', () => {
|
|||||||
fireEvent.click(screen.getByTestId('proposal-submit'));
|
fireEvent.click(screen.getByTestId('proposal-submit'));
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(mockSendTx).toHaveBeenCalledWith({
|
expect(mockSendTx).toHaveBeenCalledWith(pubKey, {
|
||||||
publicKey: pubKey,
|
proposalSubmission: JSON.parse(inputJSON),
|
||||||
sendingMode: 'TYPE_SYNC',
|
|
||||||
transaction: {
|
|
||||||
proposalSubmission: JSON.parse(inputJSON),
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(screen.getByTestId('dialog-title')).toHaveTextContent(
|
expect(screen.getByTestId('dialog-title')).toHaveTextContent(
|
||||||
@ -243,12 +232,12 @@ describe('Raw proposal form', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('can be rejected by the user', async () => {
|
it('can be rejected by the user', async () => {
|
||||||
jest.spyOn(mockConfig, 'sendTransaction').mockReturnValue(
|
const mockSendTx = jest.fn().mockReturnValue(
|
||||||
new Promise((_, reject) => {
|
new Promise((resolve) => {
|
||||||
setTimeout(() => reject(userRejectedError()), 100);
|
setTimeout(() => resolve(null), 100);
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
setup();
|
setup(mockSendTx);
|
||||||
|
|
||||||
await act(async () => {
|
await act(async () => {
|
||||||
jest.advanceTimersByTime(paramsDelay);
|
jest.advanceTimersByTime(paramsDelay);
|
||||||
|
@ -52,8 +52,7 @@ export const ProposeRaw = () => {
|
|||||||
handleSubmit,
|
handleSubmit,
|
||||||
formState: { isSubmitting, errors },
|
formState: { isSubmitting, errors },
|
||||||
} = useForm<RawProposalFormFields>();
|
} = useForm<RawProposalFormFields>();
|
||||||
const { finalizedProposal, transaction, submit, setTransaction } =
|
const { finalizedProposal, submit, Dialog } = useProposalSubmit();
|
||||||
useProposalSubmit();
|
|
||||||
|
|
||||||
const hasError = Boolean(errors.rawProposalData?.message);
|
const hasError = Boolean(errors.rawProposalData?.message);
|
||||||
|
|
||||||
@ -153,8 +152,7 @@ export const ProposeRaw = () => {
|
|||||||
<ProposalFormSubmit isSubmitting={isSubmitting} />
|
<ProposalFormSubmit isSubmitting={isSubmitting} />
|
||||||
<ProposalFormTransactionDialog
|
<ProposalFormTransactionDialog
|
||||||
finalizedProposal={finalizedProposal}
|
finalizedProposal={finalizedProposal}
|
||||||
transaction={transaction}
|
TransactionDialog={Dialog}
|
||||||
onChange={(open) => setTransaction({ dialogOpen: open })}
|
|
||||||
/>
|
/>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,12 +1,13 @@
|
|||||||
import { MockedProvider } from '@apollo/client/testing';
|
import { MockedProvider } from '@apollo/client/testing';
|
||||||
import { MemoryRouter as Router } from 'react-router-dom';
|
import { MemoryRouter as Router } from 'react-router-dom';
|
||||||
import { render, screen } from '@testing-library/react';
|
import { render, screen } from '@testing-library/react';
|
||||||
|
import { VegaWalletContext } from '@vegaprotocol/wallet';
|
||||||
import { AppStateProvider } from '../../../../contexts/app-state/app-state-provider';
|
import { AppStateProvider } from '../../../../contexts/app-state/app-state-provider';
|
||||||
|
import { mockWalletContext } from '../../test-helpers/mocks';
|
||||||
import { ProposeUpdateAsset } from './propose-update-asset';
|
import { ProposeUpdateAsset } from './propose-update-asset';
|
||||||
import type { NetworkParamsQuery } from '@vegaprotocol/network-parameters';
|
import type { NetworkParamsQuery } from '@vegaprotocol/network-parameters';
|
||||||
import type { MockedResponse } from '@apollo/client/testing';
|
import type { MockedResponse } from '@apollo/client/testing';
|
||||||
import { NetworkParamsDocument } from '@vegaprotocol/network-parameters';
|
import { NetworkParamsDocument } from '@vegaprotocol/network-parameters';
|
||||||
import { MockedWalletProvider } from '@vegaprotocol/wallet-react/testing';
|
|
||||||
|
|
||||||
jest.mock('@vegaprotocol/environment', () => ({
|
jest.mock('@vegaprotocol/environment', () => ({
|
||||||
...jest.requireActual('@vegaprotocol/environment'),
|
...jest.requireActual('@vegaprotocol/environment'),
|
||||||
@ -71,27 +72,26 @@ const updateAssetNetworkParamsQueryMock: MockedResponse<NetworkParamsQuery> = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const renderComponent = () => {
|
const renderComponent = () =>
|
||||||
return render(
|
render(
|
||||||
<Router>
|
<Router>
|
||||||
<MockedProvider mocks={[updateAssetNetworkParamsQueryMock]}>
|
<MockedProvider mocks={[updateAssetNetworkParamsQueryMock]}>
|
||||||
<MockedWalletProvider>
|
<AppStateProvider>
|
||||||
<AppStateProvider>
|
<VegaWalletContext.Provider value={mockWalletContext}>
|
||||||
<ProposeUpdateAsset />
|
<ProposeUpdateAsset />
|
||||||
</AppStateProvider>
|
</VegaWalletContext.Provider>
|
||||||
</MockedWalletProvider>
|
</AppStateProvider>
|
||||||
</MockedProvider>
|
</MockedProvider>
|
||||||
</Router>
|
</Router>
|
||||||
);
|
);
|
||||||
};
|
|
||||||
|
|
||||||
// Note: form submission is tested in propose-raw.spec.tsx. Reusable form
|
// Note: form submission is tested in propose-raw.spec.tsx. Reusable form
|
||||||
// components are tested in their own directory.
|
// components are tested in their own directory.
|
||||||
|
|
||||||
describe('Propose Update Asset', () => {
|
describe('Propose Update Asset', () => {
|
||||||
it('should render successfully', () => {
|
it('should render successfully', async () => {
|
||||||
const { baseElement } = renderComponent();
|
const { baseElement } = renderComponent();
|
||||||
expect(baseElement).toBeTruthy();
|
await expect(baseElement).toBeTruthy();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should render the title', async () => {
|
it('should render the title', async () => {
|
||||||
|
@ -62,8 +62,7 @@ export const ProposeUpdateAsset = () => {
|
|||||||
watch,
|
watch,
|
||||||
trigger,
|
trigger,
|
||||||
} = useForm<UpdateAssetProposalFormFields>();
|
} = useForm<UpdateAssetProposalFormFields>();
|
||||||
const { finalizedProposal, transaction, submit, setTransaction } =
|
const { finalizedProposal, submit, Dialog } = useProposalSubmit();
|
||||||
useProposalSubmit();
|
|
||||||
|
|
||||||
const assembleProposal = (fields: UpdateAssetProposalFormFields) => {
|
const assembleProposal = (fields: UpdateAssetProposalFormFields) => {
|
||||||
const isVoteDeadlineAtMinimum = doesValueEquateToParam(
|
const isVoteDeadlineAtMinimum = doesValueEquateToParam(
|
||||||
@ -219,8 +218,7 @@ export const ProposeUpdateAsset = () => {
|
|||||||
<ProposalFormDownloadJson downloadJson={viewJson} />
|
<ProposalFormDownloadJson downloadJson={viewJson} />
|
||||||
<ProposalFormTransactionDialog
|
<ProposalFormTransactionDialog
|
||||||
finalizedProposal={finalizedProposal}
|
finalizedProposal={finalizedProposal}
|
||||||
transaction={transaction}
|
TransactionDialog={Dialog}
|
||||||
onChange={(open) => setTransaction({ dialogOpen: open })}
|
|
||||||
/>
|
/>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
@ -2,14 +2,15 @@ import type { MockedResponse } from '@apollo/client/testing';
|
|||||||
import { MockedProvider } from '@apollo/client/testing';
|
import { MockedProvider } from '@apollo/client/testing';
|
||||||
import { MemoryRouter as Router } from 'react-router-dom';
|
import { MemoryRouter as Router } from 'react-router-dom';
|
||||||
import { fireEvent, render, screen, waitFor } from '@testing-library/react';
|
import { fireEvent, render, screen, waitFor } from '@testing-library/react';
|
||||||
|
import { VegaWalletContext } from '@vegaprotocol/wallet';
|
||||||
import { AppStateProvider } from '../../../../contexts/app-state/app-state-provider';
|
import { AppStateProvider } from '../../../../contexts/app-state/app-state-provider';
|
||||||
|
import { mockWalletContext } from '../../test-helpers/mocks';
|
||||||
import { ProposeUpdateMarket } from './propose-update-market';
|
import { ProposeUpdateMarket } from './propose-update-market';
|
||||||
import type { NetworkParamsQuery } from '@vegaprotocol/network-parameters';
|
import type { NetworkParamsQuery } from '@vegaprotocol/network-parameters';
|
||||||
import { NetworkParamsDocument } from '@vegaprotocol/network-parameters';
|
import { NetworkParamsDocument } from '@vegaprotocol/network-parameters';
|
||||||
import type { ProposalMarketsQueryQuery } from './__generated__/UpdateMarket';
|
import type { ProposalMarketsQueryQuery } from './__generated__/UpdateMarket';
|
||||||
import { ProposalMarketsQueryDocument } from './__generated__/UpdateMarket';
|
import { ProposalMarketsQueryDocument } from './__generated__/UpdateMarket';
|
||||||
import { ProposalState } from '@vegaprotocol/types';
|
import { ProposalState } from '@vegaprotocol/types';
|
||||||
import { MockedWalletProvider } from '@vegaprotocol/wallet-react/testing';
|
|
||||||
|
|
||||||
const updateMarketNetworkParamsQueryMock: MockedResponse<NetworkParamsQuery> = {
|
const updateMarketNetworkParamsQueryMock: MockedResponse<NetworkParamsQuery> = {
|
||||||
request: {
|
request: {
|
||||||
@ -216,30 +217,29 @@ const marketQueryMock: MockedResponse<ProposalMarketsQueryQuery> = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const renderComponent = () => {
|
const renderComponent = () =>
|
||||||
return render(
|
render(
|
||||||
<MockedProvider
|
<MockedProvider
|
||||||
mocks={[updateMarketNetworkParamsQueryMock, marketQueryMock]}
|
mocks={[updateMarketNetworkParamsQueryMock, marketQueryMock]}
|
||||||
addTypename={false}
|
addTypename={false}
|
||||||
>
|
>
|
||||||
<Router>
|
<Router>
|
||||||
<MockedWalletProvider>
|
<AppStateProvider>
|
||||||
<AppStateProvider>
|
<VegaWalletContext.Provider value={mockWalletContext}>
|
||||||
<ProposeUpdateMarket />
|
<ProposeUpdateMarket />
|
||||||
</AppStateProvider>
|
</VegaWalletContext.Provider>
|
||||||
</MockedWalletProvider>
|
</AppStateProvider>
|
||||||
</Router>
|
</Router>
|
||||||
</MockedProvider>
|
</MockedProvider>
|
||||||
);
|
);
|
||||||
};
|
|
||||||
|
|
||||||
// Note: form submission is tested in propose-raw.spec.tsx. Reusable form
|
// Note: form submission is tested in propose-raw.spec.tsx. Reusable form
|
||||||
// components are tested in their own directory.
|
// components are tested in their own directory.
|
||||||
|
|
||||||
describe('Propose Update Market', () => {
|
describe('Propose Update Market', () => {
|
||||||
it('should render successfully', () => {
|
it('should render successfully', async () => {
|
||||||
const { baseElement } = renderComponent();
|
const { baseElement } = renderComponent();
|
||||||
expect(baseElement).toBeTruthy();
|
await expect(baseElement).toBeTruthy();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should render the title', async () => {
|
it('should render the title', async () => {
|
||||||
|
@ -109,8 +109,7 @@ export const ProposeUpdateMarket = () => {
|
|||||||
watch,
|
watch,
|
||||||
trigger,
|
trigger,
|
||||||
} = useForm<UpdateMarketProposalFormFields>();
|
} = useForm<UpdateMarketProposalFormFields>();
|
||||||
const { finalizedProposal, transaction, submit, setTransaction } =
|
const { finalizedProposal, submit, Dialog } = useProposalSubmit();
|
||||||
useProposalSubmit();
|
|
||||||
|
|
||||||
const assembleProposal = (fields: UpdateMarketProposalFormFields) => {
|
const assembleProposal = (fields: UpdateMarketProposalFormFields) => {
|
||||||
const isVoteDeadlineAtMinimum = doesValueEquateToParam(
|
const isVoteDeadlineAtMinimum = doesValueEquateToParam(
|
||||||
@ -324,8 +323,7 @@ export const ProposeUpdateMarket = () => {
|
|||||||
<ProposalFormDownloadJson downloadJson={viewJson} />
|
<ProposalFormDownloadJson downloadJson={viewJson} />
|
||||||
<ProposalFormTransactionDialog
|
<ProposalFormTransactionDialog
|
||||||
finalizedProposal={finalizedProposal}
|
finalizedProposal={finalizedProposal}
|
||||||
transaction={transaction}
|
TransactionDialog={Dialog}
|
||||||
onChange={(open) => setTransaction({ dialogOpen: open })}
|
|
||||||
/>
|
/>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,17 +1,28 @@
|
|||||||
import { NetworkParamsDocument } from '@vegaprotocol/network-parameters';
|
import { NetworkParamsDocument } from '@vegaprotocol/network-parameters';
|
||||||
import type { MockedResponse } from '@apollo/client/testing';
|
import type { MockedResponse } from '@apollo/client/testing';
|
||||||
import type { NetworkParamsQuery } from '@vegaprotocol/network-parameters';
|
import type { NetworkParamsQuery } from '@vegaprotocol/network-parameters';
|
||||||
|
import type { PubKey, VegaWalletContextShape } from '@vegaprotocol/wallet';
|
||||||
import type { VoteValue } from '@vegaprotocol/types';
|
import type { VoteValue } from '@vegaprotocol/types';
|
||||||
import type { UserVoteQuery } from '../components/vote-details/__generated__/Vote';
|
import type { UserVoteQuery } from '../components/vote-details/__generated__/Vote';
|
||||||
import { UserVoteDocument } from '../components/vote-details/__generated__/Vote';
|
import { UserVoteDocument } from '../components/vote-details/__generated__/Vote';
|
||||||
import faker from 'faker';
|
import faker from 'faker';
|
||||||
import { type Key } from '@vegaprotocol/wallet';
|
|
||||||
|
|
||||||
export const mockPubkey: Key = {
|
export const mockPubkey: PubKey = {
|
||||||
publicKey: '0x123',
|
publicKey: '0x123',
|
||||||
name: 'test key 1',
|
name: 'test key 1',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const mockWalletContext = {
|
||||||
|
pubKey: mockPubkey.publicKey,
|
||||||
|
pubKeys: [mockPubkey],
|
||||||
|
isReadOnly: false,
|
||||||
|
sendTx: jest.fn().mockReturnValue(Promise.resolve(null)),
|
||||||
|
connect: jest.fn(),
|
||||||
|
disconnect: jest.fn(),
|
||||||
|
selectPubKey: jest.fn(),
|
||||||
|
connector: null,
|
||||||
|
} as unknown as VegaWalletContextShape;
|
||||||
|
|
||||||
const mockEthereumConfig = {
|
const mockEthereumConfig = {
|
||||||
network_id: '3',
|
network_id: '3',
|
||||||
chain_id: '3',
|
chain_id: '3',
|
||||||
|
@ -1,11 +1,13 @@
|
|||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { useDialogStore } from '@vegaprotocol/wallet-react';
|
import { useVegaWalletDialogStore } from '@vegaprotocol/wallet';
|
||||||
import { Button } from '@vegaprotocol/ui-toolkit';
|
import { Button } from '@vegaprotocol/ui-toolkit';
|
||||||
import { SubHeading } from '../../components/heading';
|
import { SubHeading } from '../../components/heading';
|
||||||
|
|
||||||
export const ConnectToSeeRewards = () => {
|
export const ConnectToSeeRewards = () => {
|
||||||
const openVegaWalletDialog = useDialogStore((store) => store.open);
|
const { openVegaWalletDialog } = useVegaWalletDialogStore((store) => ({
|
||||||
|
openVegaWalletDialog: store.openVegaWalletDialog,
|
||||||
|
}));
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const classes = classNames(
|
const classes = classNames(
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
import { useMemo, useState } from 'react';
|
import { useMemo, useEffect, useState, useCallback, useRef } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { AsyncRenderer, Pagination } from '@vegaprotocol/ui-toolkit';
|
import { AsyncRenderer, Pagination } from '@vegaprotocol/ui-toolkit';
|
||||||
import { removePaginationWrapper } from '@vegaprotocol/utils';
|
import { removePaginationWrapper } from '@vegaprotocol/utils';
|
||||||
import type { EpochFieldsFragment } from '../home/__generated__/Rewards';
|
import type { EpochFieldsFragment } from '../home/__generated__/Rewards';
|
||||||
import { useRewardsQuery } from '../home/__generated__/Rewards';
|
import { useRewardsQuery } from '../home/__generated__/Rewards';
|
||||||
import { useVegaWallet } from '@vegaprotocol/wallet-react';
|
import { ENV } from '../../../config';
|
||||||
|
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||||
import { EpochIndividualRewardsTable } from './epoch-individual-rewards-table';
|
import { EpochIndividualRewardsTable } from './epoch-individual-rewards-table';
|
||||||
import { generateEpochIndividualRewardsList } from './generate-epoch-individual-rewards-list';
|
import { generateEpochIndividualRewardsList } from './generate-epoch-individual-rewards-list';
|
||||||
import { calculateEpochOffset } from '../../../lib/epoch-pagination';
|
import { calculateEpochOffset } from '../../../lib/epoch-pagination';
|
||||||
@ -26,16 +27,22 @@ export const EpochIndividualRewards = ({
|
|||||||
const [page, setPage] = useState(1);
|
const [page, setPage] = useState(1);
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { pubKey } = useVegaWallet();
|
const { pubKey } = useVegaWallet();
|
||||||
|
const { delegationsPagination } = ENV;
|
||||||
const { param: marketCreationQuantumMultiple } = useNetworkParam(
|
const { param: marketCreationQuantumMultiple } = useNetworkParam(
|
||||||
'rewards_marketCreationQuantumMultiple'
|
'rewards_marketCreationQuantumMultiple'
|
||||||
);
|
);
|
||||||
|
|
||||||
const { data, loading, error } = useRewardsQuery({
|
const { data, loading, error, refetch } = useRewardsQuery({
|
||||||
notifyOnNetworkStatusChange: true,
|
notifyOnNetworkStatusChange: true,
|
||||||
variables: {
|
variables: {
|
||||||
partyId: pubKey || '',
|
partyId: pubKey || '',
|
||||||
...calculateEpochOffset({ epochId, page, size: EPOCHS_PAGE_SIZE }),
|
fromEpoch: epochId - EPOCHS_PAGE_SIZE,
|
||||||
delegationsPagination: { first: 50 },
|
toEpoch: epochId,
|
||||||
|
delegationsPagination: delegationsPagination
|
||||||
|
? {
|
||||||
|
first: Number(delegationsPagination),
|
||||||
|
}
|
||||||
|
: undefined,
|
||||||
},
|
},
|
||||||
skip: !pubKey,
|
skip: !pubKey,
|
||||||
});
|
});
|
||||||
@ -63,6 +70,36 @@ export const EpochIndividualRewards = ({
|
|||||||
});
|
});
|
||||||
}, [data?.party, epochId, epochRewardSummaries, page, rewards]);
|
}, [data?.party, epochId, epochRewardSummaries, page, rewards]);
|
||||||
|
|
||||||
|
const refetchData = useCallback(
|
||||||
|
async (toPage?: number) => {
|
||||||
|
const targetPage = toPage ?? page;
|
||||||
|
await refetch({
|
||||||
|
partyId: pubKey || '',
|
||||||
|
...calculateEpochOffset({ epochId, page, size: EPOCHS_PAGE_SIZE }),
|
||||||
|
delegationsPagination: delegationsPagination
|
||||||
|
? {
|
||||||
|
first: Number(delegationsPagination),
|
||||||
|
}
|
||||||
|
: undefined,
|
||||||
|
});
|
||||||
|
setPage(targetPage);
|
||||||
|
},
|
||||||
|
[epochId, page, refetch, delegationsPagination, pubKey]
|
||||||
|
);
|
||||||
|
|
||||||
|
const prevEpochIdRef = useRef<number | null>(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (prevEpochIdRef.current === null) {
|
||||||
|
prevEpochIdRef.current = epochId;
|
||||||
|
} else if (epochId !== prevEpochIdRef.current) {
|
||||||
|
// When the epoch changes, we want to refetch the data to update the current page
|
||||||
|
refetchData();
|
||||||
|
}
|
||||||
|
|
||||||
|
prevEpochIdRef.current = epochId;
|
||||||
|
}, [epochId, refetchData]);
|
||||||
|
|
||||||
// Workarounds for the error handling of AsyncRenderer
|
// Workarounds for the error handling of AsyncRenderer
|
||||||
const filteredErrors = filterAcceptableGraphqlErrors(error);
|
const filteredErrors = filterAcceptableGraphqlErrors(error);
|
||||||
const filteredData = data || [];
|
const filteredData = data || [];
|
||||||
@ -94,10 +131,10 @@ export const EpochIndividualRewards = ({
|
|||||||
isLoading={loading}
|
isLoading={loading}
|
||||||
hasPrevPage={page > 1}
|
hasPrevPage={page > 1}
|
||||||
hasNextPage={page < totalPages}
|
hasNextPage={page < totalPages}
|
||||||
onBack={() => setPage((x) => x - 1)}
|
onBack={() => refetchData(page - 1)}
|
||||||
onNext={() => setPage((x) => x + 1)}
|
onNext={() => refetchData(page + 1)}
|
||||||
onFirst={() => setPage(1)}
|
onFirst={() => refetchData(1)}
|
||||||
onLast={() => setPage(totalPages)}
|
onLast={() => refetchData(totalPages)}
|
||||||
>
|
>
|
||||||
{t('Page')} {page}
|
{t('Page')} {page}
|
||||||
</Pagination>
|
</Pagination>
|
||||||
|
@ -23,6 +23,36 @@ describe('generateEpochIndividualRewardsList', () => {
|
|||||||
epoch: { id: '2' },
|
epoch: { id: '2' },
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const reward3: RewardFieldsFragment = {
|
||||||
|
rewardType: AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES,
|
||||||
|
amount: '200',
|
||||||
|
percentageOfTotal: '0.2',
|
||||||
|
receivedAt: new Date(),
|
||||||
|
asset: { id: 'gbp', symbol: 'GBP', name: 'GBP', decimals: 7 },
|
||||||
|
party: { id: 'blah' },
|
||||||
|
epoch: { id: '2' },
|
||||||
|
};
|
||||||
|
|
||||||
|
const reward4: RewardFieldsFragment = {
|
||||||
|
rewardType: AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES,
|
||||||
|
amount: '100',
|
||||||
|
percentageOfTotal: '0.1',
|
||||||
|
receivedAt: new Date(),
|
||||||
|
asset: { id: 'usd', symbol: 'USD', name: 'USD', decimals: 6 },
|
||||||
|
party: { id: 'blah' },
|
||||||
|
epoch: { id: '1' },
|
||||||
|
};
|
||||||
|
|
||||||
|
const reward5: RewardFieldsFragment = {
|
||||||
|
rewardType: AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES,
|
||||||
|
amount: '150',
|
||||||
|
percentageOfTotal: '0.15',
|
||||||
|
receivedAt: new Date(),
|
||||||
|
asset: { id: 'usd', symbol: 'USD', name: 'USD', decimals: 6 },
|
||||||
|
party: { id: 'blah' },
|
||||||
|
epoch: { id: '3' },
|
||||||
|
};
|
||||||
|
|
||||||
const rewardWrongType: RewardFieldsFragment = {
|
const rewardWrongType: RewardFieldsFragment = {
|
||||||
rewardType: AccountType.ACCOUNT_TYPE_INSURANCE,
|
rewardType: AccountType.ACCOUNT_TYPE_INSURANCE,
|
||||||
amount: '50',
|
amount: '50',
|
||||||
@ -90,10 +120,26 @@ describe('generateEpochIndividualRewardsList', () => {
|
|||||||
amount: '0',
|
amount: '0',
|
||||||
percentageOfTotal: '0',
|
percentageOfTotal: '0',
|
||||||
},
|
},
|
||||||
|
[AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES]: {
|
||||||
|
amount: '0',
|
||||||
|
percentageOfTotal: '0',
|
||||||
|
},
|
||||||
[AccountType.ACCOUNT_TYPE_GLOBAL_REWARD]: {
|
[AccountType.ACCOUNT_TYPE_GLOBAL_REWARD]: {
|
||||||
amount: '100',
|
amount: '100',
|
||||||
percentageOfTotal: '0.1',
|
percentageOfTotal: '0.1',
|
||||||
},
|
},
|
||||||
|
[AccountType.ACCOUNT_TYPE_REWARD_MAKER_PAID_FEES]: {
|
||||||
|
amount: '0',
|
||||||
|
percentageOfTotal: '0',
|
||||||
|
},
|
||||||
|
[AccountType.ACCOUNT_TYPE_REWARD_MAKER_RECEIVED_FEES]: {
|
||||||
|
amount: '0',
|
||||||
|
percentageOfTotal: '0',
|
||||||
|
},
|
||||||
|
[AccountType.ACCOUNT_TYPE_REWARD_MARKET_PROPOSERS]: {
|
||||||
|
amount: '0',
|
||||||
|
percentageOfTotal: '0',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@ -101,7 +147,7 @@ describe('generateEpochIndividualRewardsList', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should return an array sorted by epoch descending', () => {
|
it('should return an array sorted by epoch descending', () => {
|
||||||
const rewards = [reward1, reward2];
|
const rewards = [reward1, reward2, reward3, reward4];
|
||||||
const result1 = generateEpochIndividualRewardsList({
|
const result1 = generateEpochIndividualRewardsList({
|
||||||
rewards,
|
rewards,
|
||||||
epochId: 2,
|
epochId: 2,
|
||||||
@ -111,7 +157,7 @@ describe('generateEpochIndividualRewardsList', () => {
|
|||||||
expect(result1[0].epoch).toEqual(2);
|
expect(result1[0].epoch).toEqual(2);
|
||||||
expect(result1[1].epoch).toEqual(1);
|
expect(result1[1].epoch).toEqual(1);
|
||||||
|
|
||||||
const reorderedRewards = [reward2, reward1];
|
const reorderedRewards = [reward4, reward3, reward2, reward1];
|
||||||
const result2 = generateEpochIndividualRewardsList({
|
const result2 = generateEpochIndividualRewardsList({
|
||||||
rewards: reorderedRewards,
|
rewards: reorderedRewards,
|
||||||
epochId: 2,
|
epochId: 2,
|
||||||
@ -124,7 +170,7 @@ describe('generateEpochIndividualRewardsList', () => {
|
|||||||
|
|
||||||
it('returns data in the expected shape', () => {
|
it('returns data in the expected shape', () => {
|
||||||
// Just sanity checking the whole structure here
|
// Just sanity checking the whole structure here
|
||||||
const rewards = [reward1, reward2];
|
const rewards = [reward1, reward2, reward3, reward4];
|
||||||
const result = generateEpochIndividualRewardsList({
|
const result = generateEpochIndividualRewardsList({
|
||||||
rewards,
|
rewards,
|
||||||
epochId: 2,
|
epochId: 2,
|
||||||
@ -135,6 +181,37 @@ describe('generateEpochIndividualRewardsList', () => {
|
|||||||
{
|
{
|
||||||
epoch: 2,
|
epoch: 2,
|
||||||
rewards: [
|
rewards: [
|
||||||
|
{
|
||||||
|
asset: 'GBP',
|
||||||
|
totalAmount: '200',
|
||||||
|
decimals: 7,
|
||||||
|
rewardTypes: {
|
||||||
|
[AccountType.ACCOUNT_TYPE_FEES_INFRASTRUCTURE]: {
|
||||||
|
amount: '0',
|
||||||
|
percentageOfTotal: '0',
|
||||||
|
},
|
||||||
|
[AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES]: {
|
||||||
|
amount: '200',
|
||||||
|
percentageOfTotal: '0.2',
|
||||||
|
},
|
||||||
|
[AccountType.ACCOUNT_TYPE_GLOBAL_REWARD]: {
|
||||||
|
amount: '0',
|
||||||
|
percentageOfTotal: '0',
|
||||||
|
},
|
||||||
|
[AccountType.ACCOUNT_TYPE_REWARD_MAKER_PAID_FEES]: {
|
||||||
|
amount: '0',
|
||||||
|
percentageOfTotal: '0',
|
||||||
|
},
|
||||||
|
[AccountType.ACCOUNT_TYPE_REWARD_MAKER_RECEIVED_FEES]: {
|
||||||
|
amount: '0',
|
||||||
|
percentageOfTotal: '0',
|
||||||
|
},
|
||||||
|
[AccountType.ACCOUNT_TYPE_REWARD_MARKET_PROPOSERS]: {
|
||||||
|
amount: '0',
|
||||||
|
percentageOfTotal: '0',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
asset: 'EUR',
|
asset: 'EUR',
|
||||||
totalAmount: '50',
|
totalAmount: '50',
|
||||||
@ -144,10 +221,26 @@ describe('generateEpochIndividualRewardsList', () => {
|
|||||||
amount: '0',
|
amount: '0',
|
||||||
percentageOfTotal: '0',
|
percentageOfTotal: '0',
|
||||||
},
|
},
|
||||||
|
[AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES]: {
|
||||||
|
amount: '0',
|
||||||
|
percentageOfTotal: '0',
|
||||||
|
},
|
||||||
[AccountType.ACCOUNT_TYPE_GLOBAL_REWARD]: {
|
[AccountType.ACCOUNT_TYPE_GLOBAL_REWARD]: {
|
||||||
amount: '50',
|
amount: '50',
|
||||||
percentageOfTotal: '0.05',
|
percentageOfTotal: '0.05',
|
||||||
},
|
},
|
||||||
|
[AccountType.ACCOUNT_TYPE_REWARD_MAKER_PAID_FEES]: {
|
||||||
|
amount: '0',
|
||||||
|
percentageOfTotal: '0',
|
||||||
|
},
|
||||||
|
[AccountType.ACCOUNT_TYPE_REWARD_MAKER_RECEIVED_FEES]: {
|
||||||
|
amount: '0',
|
||||||
|
percentageOfTotal: '0',
|
||||||
|
},
|
||||||
|
[AccountType.ACCOUNT_TYPE_REWARD_MARKET_PROPOSERS]: {
|
||||||
|
amount: '0',
|
||||||
|
percentageOfTotal: '0',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@ -157,17 +250,33 @@ describe('generateEpochIndividualRewardsList', () => {
|
|||||||
rewards: [
|
rewards: [
|
||||||
{
|
{
|
||||||
asset: 'USD',
|
asset: 'USD',
|
||||||
totalAmount: '100',
|
totalAmount: '200',
|
||||||
decimals: 6,
|
decimals: 6,
|
||||||
rewardTypes: {
|
rewardTypes: {
|
||||||
[AccountType.ACCOUNT_TYPE_FEES_INFRASTRUCTURE]: {
|
[AccountType.ACCOUNT_TYPE_FEES_INFRASTRUCTURE]: {
|
||||||
amount: '0',
|
amount: '0',
|
||||||
percentageOfTotal: '0',
|
percentageOfTotal: '0',
|
||||||
},
|
},
|
||||||
|
[AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES]: {
|
||||||
|
amount: '100',
|
||||||
|
percentageOfTotal: '0.1',
|
||||||
|
},
|
||||||
[AccountType.ACCOUNT_TYPE_GLOBAL_REWARD]: {
|
[AccountType.ACCOUNT_TYPE_GLOBAL_REWARD]: {
|
||||||
amount: '100',
|
amount: '100',
|
||||||
percentageOfTotal: '0.1',
|
percentageOfTotal: '0.1',
|
||||||
},
|
},
|
||||||
|
[AccountType.ACCOUNT_TYPE_REWARD_MAKER_PAID_FEES]: {
|
||||||
|
amount: '0',
|
||||||
|
percentageOfTotal: '0',
|
||||||
|
},
|
||||||
|
[AccountType.ACCOUNT_TYPE_REWARD_MAKER_RECEIVED_FEES]: {
|
||||||
|
amount: '0',
|
||||||
|
percentageOfTotal: '0',
|
||||||
|
},
|
||||||
|
[AccountType.ACCOUNT_TYPE_REWARD_MARKET_PROPOSERS]: {
|
||||||
|
amount: '0',
|
||||||
|
percentageOfTotal: '0',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@ -176,7 +285,7 @@ describe('generateEpochIndividualRewardsList', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('returns data correctly for the requested epoch range', () => {
|
it('returns data correctly for the requested epoch range', () => {
|
||||||
const rewards = [reward1, reward2];
|
const rewards = [reward1, reward2, reward3, reward4, reward5];
|
||||||
const resultPageOne = generateEpochIndividualRewardsList({
|
const resultPageOne = generateEpochIndividualRewardsList({
|
||||||
rewards,
|
rewards,
|
||||||
epochId: 3,
|
epochId: 3,
|
||||||
@ -188,11 +297,74 @@ describe('generateEpochIndividualRewardsList', () => {
|
|||||||
expect(resultPageOne).toEqual([
|
expect(resultPageOne).toEqual([
|
||||||
{
|
{
|
||||||
epoch: 3,
|
epoch: 3,
|
||||||
rewards: [],
|
rewards: [
|
||||||
|
{
|
||||||
|
asset: 'USD',
|
||||||
|
decimals: 6,
|
||||||
|
totalAmount: '150',
|
||||||
|
rewardTypes: {
|
||||||
|
[AccountType.ACCOUNT_TYPE_FEES_INFRASTRUCTURE]: {
|
||||||
|
amount: '0',
|
||||||
|
percentageOfTotal: '0',
|
||||||
|
},
|
||||||
|
[AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES]: {
|
||||||
|
amount: '150',
|
||||||
|
percentageOfTotal: '0.15',
|
||||||
|
},
|
||||||
|
[AccountType.ACCOUNT_TYPE_GLOBAL_REWARD]: {
|
||||||
|
amount: '0',
|
||||||
|
percentageOfTotal: '0',
|
||||||
|
},
|
||||||
|
[AccountType.ACCOUNT_TYPE_REWARD_MAKER_PAID_FEES]: {
|
||||||
|
amount: '0',
|
||||||
|
percentageOfTotal: '0',
|
||||||
|
},
|
||||||
|
[AccountType.ACCOUNT_TYPE_REWARD_MAKER_RECEIVED_FEES]: {
|
||||||
|
amount: '0',
|
||||||
|
percentageOfTotal: '0',
|
||||||
|
},
|
||||||
|
[AccountType.ACCOUNT_TYPE_REWARD_MARKET_PROPOSERS]: {
|
||||||
|
amount: '0',
|
||||||
|
percentageOfTotal: '0',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
epoch: 2,
|
epoch: 2,
|
||||||
rewards: [
|
rewards: [
|
||||||
|
{
|
||||||
|
asset: 'GBP',
|
||||||
|
totalAmount: '200',
|
||||||
|
decimals: 7,
|
||||||
|
rewardTypes: {
|
||||||
|
[AccountType.ACCOUNT_TYPE_FEES_INFRASTRUCTURE]: {
|
||||||
|
amount: '0',
|
||||||
|
percentageOfTotal: '0',
|
||||||
|
},
|
||||||
|
[AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES]: {
|
||||||
|
amount: '200',
|
||||||
|
percentageOfTotal: '0.2',
|
||||||
|
},
|
||||||
|
[AccountType.ACCOUNT_TYPE_GLOBAL_REWARD]: {
|
||||||
|
amount: '0',
|
||||||
|
percentageOfTotal: '0',
|
||||||
|
},
|
||||||
|
[AccountType.ACCOUNT_TYPE_REWARD_MAKER_PAID_FEES]: {
|
||||||
|
amount: '0',
|
||||||
|
percentageOfTotal: '0',
|
||||||
|
},
|
||||||
|
[AccountType.ACCOUNT_TYPE_REWARD_MAKER_RECEIVED_FEES]: {
|
||||||
|
amount: '0',
|
||||||
|
percentageOfTotal: '0',
|
||||||
|
},
|
||||||
|
[AccountType.ACCOUNT_TYPE_REWARD_MARKET_PROPOSERS]: {
|
||||||
|
amount: '0',
|
||||||
|
percentageOfTotal: '0',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
asset: 'EUR',
|
asset: 'EUR',
|
||||||
totalAmount: '50',
|
totalAmount: '50',
|
||||||
@ -202,10 +374,26 @@ describe('generateEpochIndividualRewardsList', () => {
|
|||||||
amount: '0',
|
amount: '0',
|
||||||
percentageOfTotal: '0',
|
percentageOfTotal: '0',
|
||||||
},
|
},
|
||||||
|
[AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES]: {
|
||||||
|
amount: '0',
|
||||||
|
percentageOfTotal: '0',
|
||||||
|
},
|
||||||
[AccountType.ACCOUNT_TYPE_GLOBAL_REWARD]: {
|
[AccountType.ACCOUNT_TYPE_GLOBAL_REWARD]: {
|
||||||
amount: '50',
|
amount: '50',
|
||||||
percentageOfTotal: '0.05',
|
percentageOfTotal: '0.05',
|
||||||
},
|
},
|
||||||
|
[AccountType.ACCOUNT_TYPE_REWARD_MAKER_PAID_FEES]: {
|
||||||
|
amount: '0',
|
||||||
|
percentageOfTotal: '0',
|
||||||
|
},
|
||||||
|
[AccountType.ACCOUNT_TYPE_REWARD_MAKER_RECEIVED_FEES]: {
|
||||||
|
amount: '0',
|
||||||
|
percentageOfTotal: '0',
|
||||||
|
},
|
||||||
|
[AccountType.ACCOUNT_TYPE_REWARD_MARKET_PROPOSERS]: {
|
||||||
|
amount: '0',
|
||||||
|
percentageOfTotal: '0',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@ -226,17 +414,33 @@ describe('generateEpochIndividualRewardsList', () => {
|
|||||||
rewards: [
|
rewards: [
|
||||||
{
|
{
|
||||||
asset: 'USD',
|
asset: 'USD',
|
||||||
totalAmount: '100',
|
totalAmount: '200',
|
||||||
decimals: 6,
|
decimals: 6,
|
||||||
rewardTypes: {
|
rewardTypes: {
|
||||||
[AccountType.ACCOUNT_TYPE_FEES_INFRASTRUCTURE]: {
|
[AccountType.ACCOUNT_TYPE_FEES_INFRASTRUCTURE]: {
|
||||||
amount: '0',
|
amount: '0',
|
||||||
percentageOfTotal: '0',
|
percentageOfTotal: '0',
|
||||||
},
|
},
|
||||||
|
[AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES]: {
|
||||||
|
amount: '100',
|
||||||
|
percentageOfTotal: '0.1',
|
||||||
|
},
|
||||||
[AccountType.ACCOUNT_TYPE_GLOBAL_REWARD]: {
|
[AccountType.ACCOUNT_TYPE_GLOBAL_REWARD]: {
|
||||||
amount: '100',
|
amount: '100',
|
||||||
percentageOfTotal: '0.1',
|
percentageOfTotal: '0.1',
|
||||||
},
|
},
|
||||||
|
[AccountType.ACCOUNT_TYPE_REWARD_MAKER_PAID_FEES]: {
|
||||||
|
amount: '0',
|
||||||
|
percentageOfTotal: '0',
|
||||||
|
},
|
||||||
|
[AccountType.ACCOUNT_TYPE_REWARD_MAKER_RECEIVED_FEES]: {
|
||||||
|
amount: '0',
|
||||||
|
percentageOfTotal: '0',
|
||||||
|
},
|
||||||
|
[AccountType.ACCOUNT_TYPE_REWARD_MARKET_PROPOSERS]: {
|
||||||
|
amount: '0',
|
||||||
|
percentageOfTotal: '0',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { useState } from 'react';
|
import { useState, useCallback, useEffect } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { AsyncRenderer, Pagination } from '@vegaprotocol/ui-toolkit';
|
import { AsyncRenderer, Pagination } from '@vegaprotocol/ui-toolkit';
|
||||||
import type { EpochFieldsFragment } from '../home/__generated__/Rewards';
|
import type { EpochFieldsFragment } from '../home/__generated__/Rewards';
|
||||||
@ -23,17 +23,37 @@ export const EpochTotalRewards = ({ currentEpoch }: EpochTotalRewardsProps) => {
|
|||||||
'rewards_marketCreationQuantumMultiple'
|
'rewards_marketCreationQuantumMultiple'
|
||||||
);
|
);
|
||||||
const [page, setPage] = useState(1);
|
const [page, setPage] = useState(1);
|
||||||
const { data, loading, error } = useEpochAssetsRewardsQuery({
|
const { data, loading, error, refetch } = useEpochAssetsRewardsQuery({
|
||||||
notifyOnNetworkStatusChange: true,
|
notifyOnNetworkStatusChange: true,
|
||||||
variables: {
|
variables: {
|
||||||
epochRewardSummariesFilter: calculateEpochOffset({
|
epochRewardSummariesFilter: {
|
||||||
epochId,
|
fromEpoch: epochId - EPOCHS_PAGE_SIZE,
|
||||||
page: page,
|
},
|
||||||
size: EPOCHS_PAGE_SIZE,
|
|
||||||
}),
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const refetchData = useCallback(
|
||||||
|
async (toPage?: number) => {
|
||||||
|
const targetPage = toPage ?? page;
|
||||||
|
await refetch({
|
||||||
|
epochRewardSummariesFilter: calculateEpochOffset({
|
||||||
|
epochId,
|
||||||
|
page: targetPage,
|
||||||
|
size: EPOCHS_PAGE_SIZE,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
setPage(targetPage);
|
||||||
|
},
|
||||||
|
[epochId, page, refetch]
|
||||||
|
);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
// when the epoch changes, we want to refetch the data to update the current page
|
||||||
|
if (data) {
|
||||||
|
refetchData();
|
||||||
|
}
|
||||||
|
}, [epochId, data, refetchData]);
|
||||||
|
|
||||||
const epochTotalRewardSummaries =
|
const epochTotalRewardSummaries =
|
||||||
generateEpochTotalRewardsList({
|
generateEpochTotalRewardsList({
|
||||||
data,
|
data,
|
||||||
@ -65,10 +85,10 @@ export const EpochTotalRewards = ({ currentEpoch }: EpochTotalRewardsProps) => {
|
|||||||
isLoading={loading}
|
isLoading={loading}
|
||||||
hasPrevPage={page > 1}
|
hasPrevPage={page > 1}
|
||||||
hasNextPage={page < totalPages}
|
hasNextPage={page < totalPages}
|
||||||
onBack={() => setPage((x) => x - 1)}
|
onBack={() => refetchData(page - 1)}
|
||||||
onNext={() => setPage((x) => x + 1)}
|
onNext={() => refetchData(page + 1)}
|
||||||
onFirst={() => setPage(1)}
|
onFirst={() => refetchData(1)}
|
||||||
onLast={() => setPage(totalPages)}
|
onLast={() => refetchData(totalPages)}
|
||||||
>
|
>
|
||||||
{t('Page')} {page}
|
{t('Page')} {page}
|
||||||
</Pagination>
|
</Pagination>
|
||||||
|
@ -149,6 +149,38 @@ describe('generateEpochAssetRewardsList', () => {
|
|||||||
amount: '0',
|
amount: '0',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
[
|
||||||
|
AccountType.ACCOUNT_TYPE_REWARD_MAKER_PAID_FEES,
|
||||||
|
{
|
||||||
|
rewardType:
|
||||||
|
AccountType.ACCOUNT_TYPE_REWARD_MAKER_PAID_FEES,
|
||||||
|
amount: '0',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
AccountType.ACCOUNT_TYPE_REWARD_MAKER_RECEIVED_FEES,
|
||||||
|
{
|
||||||
|
rewardType:
|
||||||
|
AccountType.ACCOUNT_TYPE_REWARD_MAKER_RECEIVED_FEES,
|
||||||
|
amount: '0',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES,
|
||||||
|
{
|
||||||
|
rewardType:
|
||||||
|
AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES,
|
||||||
|
amount: '0',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
AccountType.ACCOUNT_TYPE_REWARD_MARKET_PROPOSERS,
|
||||||
|
{
|
||||||
|
rewardType:
|
||||||
|
AccountType.ACCOUNT_TYPE_REWARD_MARKET_PROPOSERS,
|
||||||
|
amount: '0',
|
||||||
|
},
|
||||||
|
],
|
||||||
]),
|
]),
|
||||||
totalAmount: '123',
|
totalAmount: '123',
|
||||||
},
|
},
|
||||||
@ -259,8 +291,40 @@ describe('generateEpochAssetRewardsList', () => {
|
|||||||
amount: '100',
|
amount: '100',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
[
|
||||||
|
AccountType.ACCOUNT_TYPE_REWARD_MAKER_PAID_FEES,
|
||||||
|
{
|
||||||
|
rewardType:
|
||||||
|
AccountType.ACCOUNT_TYPE_REWARD_MAKER_PAID_FEES,
|
||||||
|
amount: '123',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
AccountType.ACCOUNT_TYPE_REWARD_MAKER_RECEIVED_FEES,
|
||||||
|
{
|
||||||
|
rewardType:
|
||||||
|
AccountType.ACCOUNT_TYPE_REWARD_MAKER_RECEIVED_FEES,
|
||||||
|
amount: '0',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES,
|
||||||
|
{
|
||||||
|
rewardType:
|
||||||
|
AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES,
|
||||||
|
amount: '0',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
AccountType.ACCOUNT_TYPE_REWARD_MARKET_PROPOSERS,
|
||||||
|
{
|
||||||
|
rewardType:
|
||||||
|
AccountType.ACCOUNT_TYPE_REWARD_MARKET_PROPOSERS,
|
||||||
|
amount: '0',
|
||||||
|
},
|
||||||
|
],
|
||||||
]),
|
]),
|
||||||
totalAmount: '100',
|
totalAmount: '223',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
]),
|
]),
|
||||||
@ -270,7 +334,66 @@ describe('generateEpochAssetRewardsList', () => {
|
|||||||
'2',
|
'2',
|
||||||
{
|
{
|
||||||
epoch: 2,
|
epoch: 2,
|
||||||
assetRewards: new Map(),
|
assetRewards: new Map([
|
||||||
|
[
|
||||||
|
'1',
|
||||||
|
{
|
||||||
|
assetId: '1',
|
||||||
|
decimals: 18,
|
||||||
|
name: 'Asset 1',
|
||||||
|
rewards: new Map([
|
||||||
|
[
|
||||||
|
AccountType.ACCOUNT_TYPE_GLOBAL_REWARD,
|
||||||
|
{
|
||||||
|
rewardType: AccountType.ACCOUNT_TYPE_GLOBAL_REWARD,
|
||||||
|
amount: '0',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
AccountType.ACCOUNT_TYPE_FEES_INFRASTRUCTURE,
|
||||||
|
{
|
||||||
|
rewardType:
|
||||||
|
AccountType.ACCOUNT_TYPE_FEES_INFRASTRUCTURE,
|
||||||
|
amount: '0',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
AccountType.ACCOUNT_TYPE_REWARD_MAKER_PAID_FEES,
|
||||||
|
{
|
||||||
|
rewardType:
|
||||||
|
AccountType.ACCOUNT_TYPE_REWARD_MAKER_PAID_FEES,
|
||||||
|
amount: '0',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
AccountType.ACCOUNT_TYPE_REWARD_MAKER_RECEIVED_FEES,
|
||||||
|
{
|
||||||
|
rewardType:
|
||||||
|
AccountType.ACCOUNT_TYPE_REWARD_MAKER_RECEIVED_FEES,
|
||||||
|
amount: '0',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES,
|
||||||
|
{
|
||||||
|
rewardType:
|
||||||
|
AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES,
|
||||||
|
amount: '5',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
AccountType.ACCOUNT_TYPE_REWARD_MARKET_PROPOSERS,
|
||||||
|
{
|
||||||
|
rewardType:
|
||||||
|
AccountType.ACCOUNT_TYPE_REWARD_MARKET_PROPOSERS,
|
||||||
|
amount: '0',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
]),
|
||||||
|
totalAmount: '5',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
]),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
])
|
])
|
||||||
@ -366,7 +489,66 @@ describe('generateEpochAssetRewardsList', () => {
|
|||||||
'2',
|
'2',
|
||||||
{
|
{
|
||||||
epoch: 2,
|
epoch: 2,
|
||||||
assetRewards: new Map(),
|
assetRewards: new Map([
|
||||||
|
[
|
||||||
|
'1',
|
||||||
|
{
|
||||||
|
assetId: '1',
|
||||||
|
name: 'Asset 1',
|
||||||
|
decimals: 18,
|
||||||
|
rewards: new Map([
|
||||||
|
[
|
||||||
|
AccountType.ACCOUNT_TYPE_GLOBAL_REWARD,
|
||||||
|
{
|
||||||
|
rewardType: AccountType.ACCOUNT_TYPE_GLOBAL_REWARD,
|
||||||
|
amount: '0',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
AccountType.ACCOUNT_TYPE_FEES_INFRASTRUCTURE,
|
||||||
|
{
|
||||||
|
rewardType:
|
||||||
|
AccountType.ACCOUNT_TYPE_FEES_INFRASTRUCTURE,
|
||||||
|
amount: '0',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
AccountType.ACCOUNT_TYPE_REWARD_MAKER_PAID_FEES,
|
||||||
|
{
|
||||||
|
rewardType:
|
||||||
|
AccountType.ACCOUNT_TYPE_REWARD_MAKER_PAID_FEES,
|
||||||
|
amount: '0',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
AccountType.ACCOUNT_TYPE_REWARD_MAKER_RECEIVED_FEES,
|
||||||
|
{
|
||||||
|
rewardType:
|
||||||
|
AccountType.ACCOUNT_TYPE_REWARD_MAKER_RECEIVED_FEES,
|
||||||
|
amount: '0',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES,
|
||||||
|
{
|
||||||
|
rewardType:
|
||||||
|
AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES,
|
||||||
|
amount: '33',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
AccountType.ACCOUNT_TYPE_REWARD_MARKET_PROPOSERS,
|
||||||
|
{
|
||||||
|
rewardType:
|
||||||
|
AccountType.ACCOUNT_TYPE_REWARD_MARKET_PROPOSERS,
|
||||||
|
amount: '0',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
]),
|
||||||
|
totalAmount: '33',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
]),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
@ -396,6 +578,38 @@ describe('generateEpochAssetRewardsList', () => {
|
|||||||
amount: '15',
|
amount: '15',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
[
|
||||||
|
AccountType.ACCOUNT_TYPE_REWARD_MAKER_PAID_FEES,
|
||||||
|
{
|
||||||
|
rewardType:
|
||||||
|
AccountType.ACCOUNT_TYPE_REWARD_MAKER_PAID_FEES,
|
||||||
|
amount: '0',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
AccountType.ACCOUNT_TYPE_REWARD_MAKER_RECEIVED_FEES,
|
||||||
|
{
|
||||||
|
rewardType:
|
||||||
|
AccountType.ACCOUNT_TYPE_REWARD_MAKER_RECEIVED_FEES,
|
||||||
|
amount: '0',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES,
|
||||||
|
{
|
||||||
|
rewardType:
|
||||||
|
AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES,
|
||||||
|
amount: '0',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
AccountType.ACCOUNT_TYPE_REWARD_MARKET_PROPOSERS,
|
||||||
|
{
|
||||||
|
rewardType:
|
||||||
|
AccountType.ACCOUNT_TYPE_REWARD_MARKET_PROPOSERS,
|
||||||
|
amount: '0',
|
||||||
|
},
|
||||||
|
],
|
||||||
]),
|
]),
|
||||||
totalAmount: '15',
|
totalAmount: '15',
|
||||||
},
|
},
|
||||||
@ -442,8 +656,40 @@ describe('generateEpochAssetRewardsList', () => {
|
|||||||
amount: '100',
|
amount: '100',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
[
|
||||||
|
AccountType.ACCOUNT_TYPE_REWARD_MAKER_PAID_FEES,
|
||||||
|
{
|
||||||
|
rewardType:
|
||||||
|
AccountType.ACCOUNT_TYPE_REWARD_MAKER_PAID_FEES,
|
||||||
|
amount: '123',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
AccountType.ACCOUNT_TYPE_REWARD_MAKER_RECEIVED_FEES,
|
||||||
|
{
|
||||||
|
rewardType:
|
||||||
|
AccountType.ACCOUNT_TYPE_REWARD_MAKER_RECEIVED_FEES,
|
||||||
|
amount: '0',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES,
|
||||||
|
{
|
||||||
|
rewardType:
|
||||||
|
AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES,
|
||||||
|
amount: '0',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
AccountType.ACCOUNT_TYPE_REWARD_MARKET_PROPOSERS,
|
||||||
|
{
|
||||||
|
rewardType:
|
||||||
|
AccountType.ACCOUNT_TYPE_REWARD_MARKET_PROPOSERS,
|
||||||
|
amount: '0',
|
||||||
|
},
|
||||||
|
],
|
||||||
]),
|
]),
|
||||||
totalAmount: '100',
|
totalAmount: '223',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
]),
|
]),
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user