Compare commits
62
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bf391bd5ef | ||
|
|
d77a8792fc | ||
|
|
64adba4eec | ||
|
|
964deb2f23 | ||
|
|
6669125dd3 | ||
|
|
129b6c4e89 | ||
|
|
ca418cabfe | ||
|
|
192af844c4 | ||
|
|
6a841f226a | ||
|
|
76426baa2a | ||
|
|
6fdac2419c | ||
|
|
87807d2088 | ||
|
|
a5f2533a66 | ||
|
|
1e9251c9c0 | ||
|
|
2a27e124d7 | ||
|
|
4ee85720ab | ||
|
|
a19da4c408 | ||
|
|
65ad4fffda | ||
|
|
11317cdb2e | ||
|
|
c98870f27d | ||
|
|
c0c56e4c48 | ||
|
|
a9ca215276 | ||
|
|
61a9eb2f5c | ||
|
|
bb47747501 | ||
|
|
5827b87f89 | ||
|
|
19b95832c4 | ||
|
|
48e4ab53a1 | ||
|
|
cefcff040f | ||
|
|
c3ef639daf | ||
|
|
b6052bc3e5 | ||
|
|
6e8894936e | ||
|
|
66f55855ae | ||
|
|
5725b824ad | ||
|
|
9344a2abc0 | ||
|
|
fae66ca4ce | ||
|
|
31524ac7a7 | ||
|
|
ea3e5a7651 | ||
|
|
b062339682 | ||
|
|
824bcf89bd | ||
|
|
f315917094 | ||
|
|
8e898cfd78 | ||
|
|
f377e07996 | ||
|
|
090d340364 | ||
|
|
6d32fa7362 | ||
|
|
1a7682c3c9 | ||
|
|
3294c0cabe | ||
|
|
c10084d441 | ||
|
|
96658134ee | ||
|
|
a070504d2e | ||
|
|
f891caf08b | ||
|
|
69bdc637e5 | ||
|
|
78add88014 | ||
|
|
02c425f304 | ||
|
|
06769f25cf | ||
|
|
b3014bb98a | ||
|
|
132f2e4b2b | ||
|
|
374890dc08 | ||
|
|
7f95ba577e | ||
|
|
2f7e53e282 | ||
|
|
568dcd5305 | ||
|
|
9605b0e7fa | ||
|
|
0776332ba7 |
+2
-1
@@ -73,7 +73,8 @@
|
||||
"error",
|
||||
{
|
||||
"prefer": "type-imports",
|
||||
"disallowTypeAnnotations": true
|
||||
"disallowTypeAnnotations": true,
|
||||
"fixStyle": "inline-type-imports"
|
||||
}
|
||||
],
|
||||
"curly": ["error", "multi-line"]
|
||||
|
||||
@@ -34,7 +34,7 @@ jobs:
|
||||
${{ runner.os }}-cache-node-modules-
|
||||
|
||||
- name: Setup node
|
||||
uses: actions/setup-node@v3
|
||||
uses: actions/setup-node@v4
|
||||
if: steps.cache.outputs.cache-hit != 'true'
|
||||
with:
|
||||
node-version-file: '.nvmrc'
|
||||
@@ -57,7 +57,7 @@ jobs:
|
||||
ref: ${{ github.event.pull_request.head.sha || github.sha }}
|
||||
|
||||
- name: Setup node
|
||||
uses: actions/setup-node@v3
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: '.nvmrc'
|
||||
# https://stackoverflow.com/questions/61010294/how-to-cache-yarn-packages-in-github-actions
|
||||
@@ -96,7 +96,7 @@ jobs:
|
||||
ref: ${{ github.event.pull_request.head.sha || github.sha }}
|
||||
|
||||
- name: Setup node
|
||||
uses: actions/setup-node@v3
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: '.nvmrc'
|
||||
cache: yarn
|
||||
@@ -128,7 +128,7 @@ jobs:
|
||||
ref: ${{ github.event.pull_request.head.sha || github.sha }}
|
||||
|
||||
- name: Setup node
|
||||
uses: actions/setup-node@v3
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: '.nvmrc'
|
||||
cache: yarn
|
||||
@@ -182,7 +182,7 @@ jobs:
|
||||
run: |
|
||||
if [[ "${{ github.base_ref }}" == "develop" ]]; then
|
||||
echo "e2e-needed=true" >> $GITHUB_OUTPUT
|
||||
elif [[ "${{ github.base_ref }}" == "main" ]]; then
|
||||
elif [[ "${{ github.base_ref }}" == "main" ]]; then
|
||||
echo "e2e-needed=true" >> $GITHUB_OUTPUT
|
||||
elif [[ "${{ github.event_name }}" == "push" && "${{ github.ref_name }}" == *"release/"* ]]; then
|
||||
echo "e2e-needed=true" >> $GITHUB_OUTPUT
|
||||
@@ -205,7 +205,7 @@ jobs:
|
||||
|
||||
console-e2e:
|
||||
needs: [build-sources, check-e2e-needed]
|
||||
name: '(CI) console python'
|
||||
name: '(CI) trading e2e python'
|
||||
uses: ./.github/workflows/console-test-run.yml
|
||||
secrets: inherit
|
||||
if: needs.check-e2e-needed.outputs.run-tests == 'true' && contains(needs.build-sources.outputs.projects, 'trading')
|
||||
|
||||
@@ -10,7 +10,7 @@ on:
|
||||
inputs:
|
||||
console-test-branch:
|
||||
type: choice
|
||||
description: 'main: v0.72.14, develop: v0.73.0-preview7'
|
||||
description: 'main: v0.73.5, develop: v0.73.5'
|
||||
options:
|
||||
- main
|
||||
- develop
|
||||
@@ -32,9 +32,9 @@ jobs:
|
||||
# cache node modules
|
||||
#----------------------------------------------
|
||||
- name: setup node
|
||||
uses: actions/setup-node@v3
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '16'
|
||||
node-version-file: '.nvmrc'
|
||||
cache: yarn
|
||||
|
||||
- name: Cache node modules
|
||||
@@ -153,25 +153,19 @@ jobs:
|
||||
run: |
|
||||
docker load --input /tmp/console-image.tar
|
||||
docker image ls -a
|
||||
|
||||
#----------------------------------------------
|
||||
# check-out tests repo
|
||||
# check-out frontend-monorepo
|
||||
#----------------------------------------------
|
||||
- name: Checkout console test repo
|
||||
- name: Checkout frontend-monorepo
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
repository: vegaprotocol/console-test
|
||||
ref: ${{ needs.console-test-branch.outputs.console-branch }}
|
||||
|
||||
- name: Load console test envs
|
||||
id: console-test-env
|
||||
uses: falti/dotenv-action@v1.0.4
|
||||
with:
|
||||
path: '.env.${{ needs.console-test-branch.outputs.console-branch }}'
|
||||
export-variables: true
|
||||
keys-case: upper
|
||||
log-variables: true
|
||||
|
||||
ref: ${{ inputs.github-sha || github.sha }}
|
||||
#----------------------------------------------
|
||||
# get vega version
|
||||
#----------------------------------------------
|
||||
- name: Set VEGA_VERSION from .env
|
||||
id: set_vega_version
|
||||
run: echo "VEGA_VERSION=$(grep VEGA_VERSION apps/trading/e2e/.env | cut -d '=' -f2)" >> $GITHUB_ENV
|
||||
#----------------------------------------------
|
||||
# ----- Setup python -----
|
||||
#----------------------------------------------
|
||||
@@ -194,22 +188,25 @@ jobs:
|
||||
#----------------------------------------------
|
||||
- name: Install dependencies
|
||||
run: poetry install --no-interaction --no-root
|
||||
working-directory: apps/trading/e2e
|
||||
#----------------------------------------------
|
||||
# install vega binaries
|
||||
#----------------------------------------------
|
||||
- name: Install vega binaries
|
||||
run: poetry run python -m vega_sim.tools.load_binaries --force --version ${{ env.VEGA_VERSION }}
|
||||
working-directory: apps/trading/e2e
|
||||
#----------------------------------------------
|
||||
# install playwright
|
||||
# install playwrightworking-directory: apps/trading/e2e
|
||||
#----------------------------------------------
|
||||
- name: install playwright
|
||||
run: poetry run playwright install --with-deps chromium
|
||||
working-directory: apps/trading/e2e
|
||||
#----------------------------------------------
|
||||
# run tests
|
||||
#----------------------------------------------
|
||||
- name: Run tests
|
||||
run: CONSOLE_IMAGE_NAME=ci/trading:local poetry run pytest -v -s --numprocesses 4 --dist loadfile --durations=15
|
||||
|
||||
working-directory: apps/trading/e2e
|
||||
#----------------------------------------------
|
||||
# upload traces
|
||||
#----------------------------------------------
|
||||
@@ -218,7 +215,7 @@ jobs:
|
||||
if: always()
|
||||
with:
|
||||
name: playwright-trace
|
||||
path: ./traces/
|
||||
path: apps/trading/e2e/traces/
|
||||
retention-days: 15
|
||||
#----------------------------------------------
|
||||
# ----- upload logs -----
|
||||
|
||||
@@ -18,11 +18,11 @@ jobs:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Use Node.js 16
|
||||
- name: Use Node.js 20
|
||||
id: Node
|
||||
uses: actions/setup-node@v2
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 16.15.1
|
||||
node-version-file: '.nvmrc'
|
||||
|
||||
- name: Run Cypress tests
|
||||
uses: cypress-io/github-action@v4
|
||||
|
||||
@@ -57,6 +57,14 @@ jobs:
|
||||
path: './frontend-monorepo'
|
||||
ref: ${{ github.event.pull_request.head.sha || github.sha }}
|
||||
|
||||
- name: Setup node
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: './frontend-monorepo/.nvmrc'
|
||||
# https://stackoverflow.com/questions/61010294/how-to-cache-yarn-packages-in-github-actions
|
||||
cache: yarn
|
||||
cache-dependency-path: './frontend-monorepo/yarn.lock'
|
||||
|
||||
# Restore node_modules from cache if possible
|
||||
- name: Restore node_modules from cache
|
||||
id: cache-node-modules
|
||||
|
||||
@@ -15,7 +15,7 @@ jobs:
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Setup node
|
||||
uses: actions/setup-node@v3
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: '.nvmrc'
|
||||
# https://stackoverflow.com/questions/61010294/how-to-cache-yarn-packages-in-github-actions
|
||||
|
||||
@@ -18,13 +18,13 @@ jobs:
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Setup node
|
||||
uses: actions/setup-node@v3
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 16
|
||||
node-version-file: '.nvmrc'
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
rm package.json
|
||||
rm package.json
|
||||
npm install --no-save @commitlint/cli @commitlint/config-conventional @commitlint/config-nx-scopes nx
|
||||
|
||||
- name: Check PR title
|
||||
|
||||
@@ -93,7 +93,7 @@ jobs:
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
||||
- name: Setup node
|
||||
uses: actions/setup-node@v3
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: '.nvmrc'
|
||||
# https://stackoverflow.com/questions/61010294/how-to-cache-yarn-packages-in-github-actions
|
||||
|
||||
@@ -29,7 +29,7 @@ jobs:
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Setup node
|
||||
uses: actions/setup-node@v3
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: '.nvmrc'
|
||||
# https://stackoverflow.com/questions/61010294/how-to-cache-yarn-packages-in-github-actions
|
||||
|
||||
+10
-2
@@ -48,8 +48,16 @@ cypress.env.json
|
||||
# Next.js
|
||||
.next
|
||||
|
||||
#cypress
|
||||
# cypress
|
||||
/apps/**/cypress/reports/
|
||||
/apps/**/cypress/downloads/
|
||||
/apps/**/fixtures/wallet/node**
|
||||
.nx/
|
||||
|
||||
# apps/trading/e2e
|
||||
__pycache__/
|
||||
apps/trading/e2e/logs/
|
||||
apps/trading/e2e/.pytest_cache/
|
||||
apps/trading/e2e/traces/
|
||||
|
||||
.nx/cache
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
# Add files here to ignore them from prettier formatting
|
||||
|
||||
/dist
|
||||
/dist-result
|
||||
/coverage
|
||||
__generated__
|
||||
__generated___
|
||||
@@ -8,3 +9,15 @@ __generated___
|
||||
apps/static/src/assets/devnet-tranches.json
|
||||
apps/static/src/assets/mainnet-tranches.json
|
||||
apps/static/src/assets/testnet-tranches.json
|
||||
|
||||
/apps/**/cypress/reports/
|
||||
/apps/**/cypress/downloads/
|
||||
|
||||
/.nx/cache
|
||||
|
||||
# apps/trading/e2e
|
||||
__pycache__/
|
||||
apps/trading/e2e/logs/
|
||||
apps/trading/e2e/.pytest_cache/
|
||||
apps/trading/e2e/traces/
|
||||
.pytest_cache/
|
||||
|
||||
@@ -1,2 +1,4 @@
|
||||
* @vegaprotocol/frontend
|
||||
apps/ @vegaprotocol/frontend-qa
|
||||
libs/ @vegaprotocol/frontend-qa
|
||||
*.graphql @vegaprotocol/core
|
||||
|
||||
@@ -136,7 +136,7 @@ The [`docker`](./docker) subfolder has some docker configurations for easily set
|
||||
Using multistage dockerfile dist is compiled using [node](https://hub.docker.com/_/node) image and later packed to nginx as in [dist build](#dist-build). The multistage builds ensures consistent CPU architecture and build toolchains are used so that the result will be identical.
|
||||
|
||||
```bash
|
||||
docker build --build-arg APP=[YOUR APP] --build-arg NODE_VERSION=16.5.1 --build-arg ENV_NAME=mainnet -t [TAG] -f docker/node-inside-docker.Dockerfile .
|
||||
docker build --build-arg APP=[YOUR APP] --build-arg NODE_VERSION=20.9.1 --build-arg ENV_NAME=mainnet -t [TAG] -f docker/node-inside-docker.Dockerfile .
|
||||
```
|
||||
|
||||
### Computing ipfs-hash of the build
|
||||
|
||||
@@ -4,7 +4,9 @@
|
||||
"overrides": [
|
||||
{
|
||||
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
|
||||
"rules": {}
|
||||
"rules": {
|
||||
"cypress/unsafe-to-chain-command": 0
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
}
|
||||
},
|
||||
"lint": {
|
||||
"executor": "@nx/linter:eslint",
|
||||
"executor": "@nx/eslint:lint",
|
||||
"outputs": ["{options.outputFile}"],
|
||||
"options": {
|
||||
"lintFilePatterns": ["apps/explorer-e2e/**/*.{js,ts}"]
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
[[redirects]]
|
||||
from = "/*"
|
||||
to = "/index.html"
|
||||
status = 200
|
||||
@@ -53,7 +53,7 @@
|
||||
}
|
||||
},
|
||||
"lint": {
|
||||
"executor": "@nx/linter:eslint",
|
||||
"executor": "@nx/eslint:lint",
|
||||
"outputs": ["{options.outputFile}"],
|
||||
"options": {
|
||||
"lintFilePatterns": ["apps/explorer/**/*.{ts,tsx,js,jsx}"]
|
||||
@@ -63,14 +63,7 @@
|
||||
"executor": "@nx/jest:jest",
|
||||
"outputs": ["{workspaceRoot}/coverage/apps/explorer"],
|
||||
"options": {
|
||||
"jestConfig": "apps/explorer/jest.config.ts",
|
||||
"passWithNoTests": true
|
||||
},
|
||||
"configurations": {
|
||||
"ci": {
|
||||
"ci": true,
|
||||
"codeCoverage": true
|
||||
}
|
||||
"jestConfig": "apps/explorer/jest.config.ts"
|
||||
}
|
||||
},
|
||||
"generate-types": {
|
||||
@@ -81,15 +74,6 @@
|
||||
]
|
||||
}
|
||||
},
|
||||
"build-netlify": {
|
||||
"executor": "nx:run-commands",
|
||||
"options": {
|
||||
"commands": [
|
||||
"cp apps/explorer/netlify.toml netlify.toml",
|
||||
"nx build explorer"
|
||||
]
|
||||
}
|
||||
},
|
||||
"build-spec": {
|
||||
"executor": "nx:run-commands",
|
||||
"outputs": [],
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import '../i18n';
|
||||
import {
|
||||
NetworkLoader,
|
||||
NodeFailure,
|
||||
@@ -28,20 +29,24 @@ function App() {
|
||||
);
|
||||
return (
|
||||
<TendermintWebsocketProvider>
|
||||
<NetworkLoader cache={DEFAULT_CACHE_CONFIG}>
|
||||
<NodeGuard
|
||||
skeleton={<div>{t('Loading')}</div>}
|
||||
failure={<NodeFailure title={t(`Node: ${VEGA_URL} is unsuitable`)} />}
|
||||
>
|
||||
<Suspense fallback={splashLoading}>
|
||||
<RouterProvider router={router} fallbackElement={splashLoading} />
|
||||
</Suspense>
|
||||
</NodeGuard>
|
||||
<NodeSwitcherDialog
|
||||
open={nodeSwitcherOpen}
|
||||
setOpen={setNodeSwitcherOpen}
|
||||
/>
|
||||
</NetworkLoader>
|
||||
<Suspense fallback={splashLoading}>
|
||||
<NetworkLoader cache={DEFAULT_CACHE_CONFIG}>
|
||||
<NodeGuard
|
||||
skeleton={<div>{t('Loading')}</div>}
|
||||
failure={
|
||||
<NodeFailure title={t(`Node: ${VEGA_URL} is unsuitable`)} />
|
||||
}
|
||||
>
|
||||
<Suspense fallback={splashLoading}>
|
||||
<RouterProvider router={router} fallbackElement={splashLoading} />
|
||||
</Suspense>
|
||||
</NodeGuard>
|
||||
<NodeSwitcherDialog
|
||||
open={nodeSwitcherOpen}
|
||||
setOpen={setNodeSwitcherOpen}
|
||||
/>
|
||||
</NetworkLoader>
|
||||
</Suspense>
|
||||
</TendermintWebsocketProvider>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useAssetDataProvider } from '@vegaprotocol/assets';
|
||||
import { addDecimalsFormatNumber } from '@vegaprotocol/utils';
|
||||
import { addDecimalsFixedFormatNumber } from '@vegaprotocol/utils';
|
||||
import { AssetLink } from '../links';
|
||||
|
||||
export type AssetBalanceProps = {
|
||||
@@ -23,12 +23,12 @@ const AssetBalance = ({
|
||||
|
||||
const label =
|
||||
!loading && asset && asset.decimals
|
||||
? addDecimalsFormatNumber(price, asset.decimals)
|
||||
? addDecimalsFixedFormatNumber(price, asset.decimals)
|
||||
: price;
|
||||
|
||||
return (
|
||||
<div className="inline-block">
|
||||
<span>{label}</span>{' '}
|
||||
<span className="font-mono">{label}</span>{' '}
|
||||
{showAssetLink && asset?.id ? (
|
||||
<AssetLink showAssetSymbol={showAssetSymbol} assetId={assetId} />
|
||||
) : null}
|
||||
|
||||
@@ -1,20 +1,25 @@
|
||||
import { useMemo } from 'react';
|
||||
import type { AssetFieldsFragment } from '@vegaprotocol/assets';
|
||||
import { AssetTypeMapping, AssetStatusMapping } from '@vegaprotocol/assets';
|
||||
import {
|
||||
useAssetTypeMapping,
|
||||
useAssetStatusMapping,
|
||||
type AssetFieldsFragment,
|
||||
} from '@vegaprotocol/assets';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { ButtonLink } from '@vegaprotocol/ui-toolkit';
|
||||
import type { AgGridReact } from 'ag-grid-react';
|
||||
import { type AgGridReact } from 'ag-grid-react';
|
||||
import { AgGrid } from '@vegaprotocol/datagrid';
|
||||
import type { VegaICellRendererParams } from '@vegaprotocol/datagrid';
|
||||
import { type VegaICellRendererParams } from '@vegaprotocol/datagrid';
|
||||
import { useRef, useLayoutEffect } from 'react';
|
||||
import { BREAKPOINT_MD } from '../../config/breakpoints';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import type { RowClickedEvent, ColDef } from 'ag-grid-community';
|
||||
import { type RowClickedEvent, ColDef } from 'ag-grid-community';
|
||||
|
||||
type AssetsTableProps = {
|
||||
data: AssetFieldsFragment[] | null;
|
||||
};
|
||||
export const AssetsTable = ({ data }: AssetsTableProps) => {
|
||||
const assetTypeMapping = useAssetTypeMapping();
|
||||
const assetStatusMapping = useAssetStatusMapping();
|
||||
const navigate = useNavigate();
|
||||
const ref = useRef<AgGridReact>(null);
|
||||
const showColumnsOnDesktop = () => {
|
||||
@@ -47,14 +52,14 @@ export const AssetsTable = ({ data }: AssetsTableProps) => {
|
||||
field: 'source.__typename',
|
||||
hide: window.innerWidth < BREAKPOINT_MD,
|
||||
valueFormatter: ({ value }: { value?: string }) =>
|
||||
value ? AssetTypeMapping[value].value : '',
|
||||
value ? assetTypeMapping[value].value : '',
|
||||
},
|
||||
{
|
||||
headerName: t('Status'),
|
||||
field: 'status',
|
||||
hide: window.innerWidth < BREAKPOINT_MD,
|
||||
valueFormatter: ({ value }: { value?: string }) =>
|
||||
value ? AssetStatusMapping[value].value : '',
|
||||
value ? assetStatusMapping[value].value : '',
|
||||
},
|
||||
{
|
||||
colId: 'actions',
|
||||
@@ -69,7 +74,7 @@ export const AssetsTable = ({ data }: AssetsTableProps) => {
|
||||
}: VegaICellRendererParams<AssetFieldsFragment, 'id'>) =>
|
||||
value ? (
|
||||
<ButtonLink
|
||||
onClick={(e) => {
|
||||
onClick={() => {
|
||||
navigate(value);
|
||||
}}
|
||||
>
|
||||
@@ -80,7 +85,7 @@ export const AssetsTable = ({ data }: AssetsTableProps) => {
|
||||
),
|
||||
},
|
||||
],
|
||||
[navigate]
|
||||
[navigate, assetStatusMapping, assetTypeMapping]
|
||||
);
|
||||
|
||||
return (
|
||||
|
||||
@@ -91,6 +91,6 @@ describe('Market link component', () => {
|
||||
};
|
||||
|
||||
const res = render(renderComponent('123', [mock]));
|
||||
expect(await res.findByText('123')).toBeInTheDocument();
|
||||
expect(await res.findByTitle('123')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -2,18 +2,18 @@ import { useMemo } from 'react';
|
||||
import { getAsset, type MarketFieldsFragment } from '@vegaprotocol/markets';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { ButtonLink } from '@vegaprotocol/ui-toolkit';
|
||||
import type { AgGridReact } from 'ag-grid-react';
|
||||
import type { ColDef } from 'ag-grid-community';
|
||||
import { type AgGridReact } from 'ag-grid-react';
|
||||
import { type ColDef } from 'ag-grid-community';
|
||||
import { AgGrid } from '@vegaprotocol/datagrid';
|
||||
import type {
|
||||
VegaICellRendererParams,
|
||||
VegaValueGetterParams,
|
||||
import {
|
||||
type VegaICellRendererParams,
|
||||
type VegaValueGetterParams,
|
||||
} from '@vegaprotocol/datagrid';
|
||||
import { useRef, useLayoutEffect } from 'react';
|
||||
import { BREAKPOINT_MD } from '../../config/breakpoints';
|
||||
import { MarketStateMapping } from '@vegaprotocol/types';
|
||||
import { useAssetDetailsDialogStore } from '@vegaprotocol/assets';
|
||||
import type { RowClickedEvent } from 'ag-grid-community';
|
||||
import { type RowClickedEvent } from 'ag-grid-community';
|
||||
import { Link, useNavigate } from 'react-router-dom';
|
||||
|
||||
type MarketsTableProps = {
|
||||
|
||||
@@ -17,7 +17,7 @@ function renderAmendOrderDetails(
|
||||
mocks: MockedResponse[]
|
||||
) {
|
||||
return render(
|
||||
<MockedProvider mocks={mocks} addTypename={false}>
|
||||
<MockedProvider mocks={mocks}>
|
||||
<MemoryRouter>
|
||||
<AmendOrderDetails version={version} id={id} amend={amend} />
|
||||
</MemoryRouter>
|
||||
@@ -44,18 +44,18 @@ function renderExistingAmend(
|
||||
orderByID: {
|
||||
__typename: 'Order',
|
||||
id: '123',
|
||||
type: 'GTT',
|
||||
type: Schema.OrderType.TYPE_LIMIT,
|
||||
status: Schema.OrderStatus.STATUS_ACTIVE,
|
||||
version: version,
|
||||
version: version ? version.toString() : '',
|
||||
createdAt: '123',
|
||||
updatedAt: '456',
|
||||
expiresAt: '789',
|
||||
timeInForce: Schema.OrderTimeInForce.TIME_IN_FORCE_GTC,
|
||||
price: '200',
|
||||
side: 'BUY',
|
||||
side: Schema.Side.SIDE_BUY,
|
||||
peggedOrder: null,
|
||||
remaining: '99',
|
||||
rejectionReason: 'rejection',
|
||||
rejectionReason: Schema.OrderRejectionReason.ORDER_ERROR_NOT_FOUND,
|
||||
reference: '123',
|
||||
size: '100',
|
||||
party: {
|
||||
@@ -65,18 +65,15 @@ function renderExistingAmend(
|
||||
market: {
|
||||
__typename: 'Market',
|
||||
id: '789',
|
||||
state: 'STATUS_ACTIVE',
|
||||
state: Schema.MarketState.STATE_ACTIVE,
|
||||
positionDecimalPlaces: 2,
|
||||
decimalPlaces: '5',
|
||||
decimalPlaces: 0,
|
||||
tradableInstrument: {
|
||||
instrument: {
|
||||
name: 'test',
|
||||
product: {
|
||||
__typename: 'Future',
|
||||
quoteName: '123',
|
||||
settlementAsset: {
|
||||
decimals: 8,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -97,18 +94,18 @@ function renderExistingAmend(
|
||||
orderByID: {
|
||||
__typename: 'Order',
|
||||
id: '123',
|
||||
type: 'GTT',
|
||||
type: Schema.OrderType.TYPE_LIMIT,
|
||||
status: Schema.OrderStatus.STATUS_ACTIVE,
|
||||
version: 100,
|
||||
version: '100',
|
||||
createdAt: '123',
|
||||
updatedAt: '456',
|
||||
expiresAt: '789',
|
||||
timeInForce: Schema.OrderTimeInForce.TIME_IN_FORCE_GTC,
|
||||
peggedOrder: null,
|
||||
price: '200',
|
||||
side: 'BUY',
|
||||
side: Schema.Side.SIDE_BUY,
|
||||
remaining: '99',
|
||||
rejectionReason: 'rejection',
|
||||
rejectionReason: Schema.OrderRejectionReason.ORDER_ERROR_NOT_FOUND,
|
||||
reference: '123',
|
||||
size: '200',
|
||||
party: {
|
||||
@@ -117,19 +114,16 @@ function renderExistingAmend(
|
||||
},
|
||||
market: {
|
||||
__typename: 'Market',
|
||||
id: 'amend-to-order-latest-version',
|
||||
state: 'STATUS_ACTIVE',
|
||||
id: '8888',
|
||||
state: Schema.MarketState.STATE_ACTIVE,
|
||||
positionDecimalPlaces: 2,
|
||||
decimalPlaces: '5',
|
||||
decimalPlaces: 0,
|
||||
tradableInstrument: {
|
||||
instrument: {
|
||||
name: 'amend-to-order-latest-version-test',
|
||||
product: {
|
||||
__typename: 'Future',
|
||||
quoteName: '123',
|
||||
settlementAsset: {
|
||||
decimals: 8,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -142,13 +136,13 @@ function renderExistingAmend(
|
||||
request: {
|
||||
query: ExplorerMarketDocument,
|
||||
variables: {
|
||||
id: '789',
|
||||
id: '8888',
|
||||
},
|
||||
},
|
||||
result: {
|
||||
data: {
|
||||
market: {
|
||||
id: '789',
|
||||
id: '8888',
|
||||
decimalPlaces: 5,
|
||||
positionDecimalPlaces: 2,
|
||||
state: 'irrelevant-test-data',
|
||||
@@ -225,12 +219,10 @@ describe('Amend order details', () => {
|
||||
|
||||
it('Fetches latest version when version is not specified', async () => {
|
||||
const amend: Amend = {
|
||||
price: '-7879',
|
||||
price: '123',
|
||||
};
|
||||
|
||||
const res = renderExistingAmend('123', undefined, amend);
|
||||
expect(
|
||||
await res.findByText('amend-to-order-latest-version')
|
||||
).toBeInTheDocument();
|
||||
expect(await res.findByText('test-label')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -46,10 +46,10 @@ const AmendOrderDetails = ({ id, version, amend }: AmendOrderDetailsProps) => {
|
||||
<div className={wrapperClasses}>
|
||||
<div className="mb-12 lg:mb-0">
|
||||
<div className="relative block rounded-lg px-3 py-6 md:px-6 lg:-mr-7">
|
||||
<h2 className="text-3xl font-bold mb-4 display-5">
|
||||
<h2 className="display-5 mb-4 text-3xl font-bold">
|
||||
{t('Order not found')}
|
||||
</h2>
|
||||
<p className="text-gray-500 mb-12">
|
||||
<p className="mb-12 text-gray-500">
|
||||
{t('No order created from this transaction')}
|
||||
</p>
|
||||
</div>
|
||||
@@ -68,24 +68,24 @@ const AmendOrderDetails = ({ id, version, amend }: AmendOrderDetailsProps) => {
|
||||
<div className={wrapperClasses}>
|
||||
<div className="mb-12 lg:mb-0">
|
||||
<div className="relative block px-3 py-6 md:px-6 lg:-mr-7">
|
||||
<h2 className="text-3xl font-bold mb-4 display-5">
|
||||
<h2 className="display-5 mb-4 text-3xl font-bold">
|
||||
{t('Edits to ')}
|
||||
{sideText[o.side]}
|
||||
{t(' order')}
|
||||
</h2>
|
||||
<p className="text-gray-500 mb-4">
|
||||
<div className="mb-4 text-gray-500">
|
||||
In <MarketLink id={o.market.id} />, updated at{' '}
|
||||
<Time date={o.updatedAt} />.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="grid md:grid-cols-4 gap-x-6">
|
||||
<div className="grid gap-x-6 md:grid-cols-4">
|
||||
{amend.sizeDelta && amend.sizeDelta !== '0' ? (
|
||||
<div className="mb-12 md:mb-0">
|
||||
<h2 className="text-2xl font-bold text-dark mb-4">
|
||||
<h2 className="text-dark mb-4 text-2xl font-bold">
|
||||
{t('New size')}
|
||||
</h2>
|
||||
<h5
|
||||
className={`text-lg font-medium text-gray-500 mb-0 capitalize ${getSideDeltaColour(
|
||||
className={`mb-0 text-lg font-medium capitalize text-gray-500 ${getSideDeltaColour(
|
||||
amend.sizeDelta
|
||||
)}`}
|
||||
>
|
||||
@@ -96,10 +96,10 @@ const AmendOrderDetails = ({ id, version, amend }: AmendOrderDetailsProps) => {
|
||||
|
||||
{amend.price && amend.price !== '0' ? (
|
||||
<div className="">
|
||||
<h2 className="text-2xl font-bold text-dark mb-4">
|
||||
<h2 className="text-dark mb-4 text-2xl font-bold">
|
||||
{t('New price')}
|
||||
</h2>
|
||||
<h5 className="text-lg font-medium text-gray-500 mb-0">
|
||||
<h5 className="mb-0 text-lg font-medium text-gray-500">
|
||||
<PriceInMarket price={amend.price} marketId={o.market.id} />
|
||||
</h5>
|
||||
</div>
|
||||
@@ -108,10 +108,10 @@ const AmendOrderDetails = ({ id, version, amend }: AmendOrderDetailsProps) => {
|
||||
{amend.peggedReference &&
|
||||
amend.peggedReference !== 'PEGGED_REFERENCE_UNSPECIFIED' ? (
|
||||
<div className="">
|
||||
<h2 className="text-2xl font-bold text-dark mb-4">
|
||||
<h2 className="text-dark mb-4 text-2xl font-bold">
|
||||
{t('New reference')}
|
||||
</h2>
|
||||
<h5 className="text-lg font-medium text-gray-500 mb-0">
|
||||
<h5 className="mb-0 text-lg font-medium text-gray-500">
|
||||
{peggedReference[amend.peggedReference]}
|
||||
</h5>
|
||||
</div>
|
||||
@@ -119,10 +119,10 @@ const AmendOrderDetails = ({ id, version, amend }: AmendOrderDetailsProps) => {
|
||||
|
||||
{amend.peggedOffset ? (
|
||||
<div className="">
|
||||
<h2 className="text-2xl font-bold text-dark mb-4">
|
||||
<h2 className="text-dark mb-4 text-2xl font-bold">
|
||||
{t('New offset')}
|
||||
</h2>
|
||||
<h5 className="text-lg font-medium text-gray-500 mb-0">
|
||||
<h5 className="mb-0 text-lg font-medium text-gray-500">
|
||||
{amend.peggedOffset}
|
||||
</h5>
|
||||
</div>
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import type { ProposalListFieldsFragment } from '@vegaprotocol/proposals';
|
||||
import { type ProposalListFieldsFragment } from '@vegaprotocol/proposals';
|
||||
import { VoteProgress } from '@vegaprotocol/proposals';
|
||||
import type { AgGridReact } from 'ag-grid-react';
|
||||
import { type AgGridReact } from 'ag-grid-react';
|
||||
import { ExternalLink } from '@vegaprotocol/ui-toolkit';
|
||||
import { AgGrid } from '@vegaprotocol/datagrid';
|
||||
import type {
|
||||
VegaICellRendererParams,
|
||||
VegaValueFormatterParams,
|
||||
import {
|
||||
type VegaICellRendererParams,
|
||||
type VegaValueFormatterParams,
|
||||
} from '@vegaprotocol/datagrid';
|
||||
import { useLayoutEffect, useMemo, useRef, useState } from 'react';
|
||||
import type { RowClickedEvent, ColDef } from 'ag-grid-community';
|
||||
import { type RowClickedEvent, ColDef } from 'ag-grid-community';
|
||||
import { getDateTimeFormat } from '@vegaprotocol/utils';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import {
|
||||
@@ -105,7 +105,7 @@ export const ProposalsTable = ({ data }: ProposalsTableProps) => {
|
||||
? new BigNumber(0)
|
||||
: yesTokens.multipliedBy(100).dividedBy(totalTokensVoted);
|
||||
return (
|
||||
<div className="flex items-center justify-center h-full pt-2 uppercase">
|
||||
<div className="flex h-full items-center justify-center pt-2 uppercase">
|
||||
<VoteProgress
|
||||
threshold={requiredMajorityPercentage}
|
||||
progress={yesPercentage}
|
||||
|
||||
@@ -15,6 +15,7 @@ import isUndefined from 'lodash/isUndefined';
|
||||
import type { BlockExplorerTransactionResult } from '../../../../routes/types/block-explorer-response';
|
||||
import { TxDetailsChainEventWithdrawal } from './tx-erc20-withdrawal';
|
||||
import { TxDetailsChainEventErc20AssetDelist } from './tx-erc20-asset-delist';
|
||||
import { TxDetailsContractCall } from './tx-contract-call';
|
||||
|
||||
interface ChainEventProps {
|
||||
txData: BlockExplorerTransactionResult | undefined;
|
||||
@@ -38,7 +39,7 @@ export const ChainEvent = ({ txData }: ChainEventProps) => {
|
||||
return null;
|
||||
}
|
||||
|
||||
const { builtin, erc20, erc20Multisig, stakingEvent } =
|
||||
const { builtin, erc20, erc20Multisig, stakingEvent, contractCall } =
|
||||
txData.command.chainEvent;
|
||||
|
||||
// Builtin Asset events
|
||||
@@ -140,6 +141,10 @@ export const ChainEvent = ({ txData }: ChainEventProps) => {
|
||||
}
|
||||
}
|
||||
|
||||
if (contractCall) {
|
||||
return <TxDetailsContractCall contractCall={contractCall} />;
|
||||
}
|
||||
|
||||
// If we hit this return, tx-shared-details should give a basic overview
|
||||
return null;
|
||||
};
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
import { decodeEthCallResult } from './tx-contract-call';
|
||||
import { base64 } from 'ethers/lib/utils';
|
||||
import { defaultAbiCoder } from '@ethersproject/abi';
|
||||
import { BigNumber } from '@ethersproject/bignumber';
|
||||
|
||||
describe('decodeEthCallResult', () => {
|
||||
it('should decode contractData correctly (mocked)', () => {
|
||||
const mockContractData = base64.encode(
|
||||
defaultAbiCoder.encode(['int256'], [BigNumber.from(123)])
|
||||
);
|
||||
const result = decodeEthCallResult(mockContractData);
|
||||
expect(result).toBe('123');
|
||||
});
|
||||
|
||||
it('should decode contractData correctly (known data)', () => {
|
||||
const mockContractData = 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADH8cueyY=';
|
||||
const result = decodeEthCallResult(mockContractData);
|
||||
expect(result).toBe('3435020581670');
|
||||
});
|
||||
|
||||
it('should return "-" when an error occurs', () => {
|
||||
const mockContractData = 'invalid_data';
|
||||
const result = decodeEthCallResult(mockContractData);
|
||||
expect(result).toBe('-');
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,88 @@
|
||||
import { TableCell, TableRow } from '../../../table';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import {
|
||||
EthExplorerLink,
|
||||
EthExplorerLinkTypes,
|
||||
} from '../../../links/eth-explorer-link/eth-explorer-link';
|
||||
import type { components } from '../../../../../types/explorer';
|
||||
import { defaultAbiCoder, base64 } from 'ethers/lib/utils';
|
||||
import { BigNumber } from 'ethers';
|
||||
import OracleLink from '../../../links/oracle-link/oracle-link';
|
||||
import { useExplorerOracleSpecByIdQuery } from '../../../../routes/oracles/__generated__/Oracles';
|
||||
import { OracleEthSource } from '../../../../routes/oracles/components/oracle-eth-source';
|
||||
|
||||
/**
|
||||
* Decodes the b64/ABIcoded result from an eth cal
|
||||
* @param data
|
||||
* @returns
|
||||
*/
|
||||
export function decodeEthCallResult(contractData: string): string {
|
||||
try {
|
||||
const rawResult = defaultAbiCoder.decode(
|
||||
['int256'],
|
||||
base64.decode(contractData)
|
||||
);
|
||||
|
||||
// Finally, convert the resulting BigNumber in to a string
|
||||
const res = BigNumber.from(rawResult[0]).toString();
|
||||
return res;
|
||||
} catch (e) {
|
||||
return '-';
|
||||
}
|
||||
}
|
||||
|
||||
interface TxDetailsContractCallProps {
|
||||
contractCall: components['schemas']['vegaEthContractCallEvent'];
|
||||
}
|
||||
|
||||
export const TxDetailsContractCall = ({
|
||||
contractCall,
|
||||
}: TxDetailsContractCallProps) => {
|
||||
const { data } = useExplorerOracleSpecByIdQuery({
|
||||
variables: {
|
||||
id: contractCall.specId || '1',
|
||||
},
|
||||
});
|
||||
|
||||
if (!contractCall || !contractCall.result) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{contractCall.specId && (
|
||||
<TableRow modifier="bordered">
|
||||
<TableCell>{t('Oracle')}</TableCell>
|
||||
<TableCell>
|
||||
<OracleLink
|
||||
id={contractCall.specId}
|
||||
hasSeenOracleReports={true}
|
||||
status={data?.oracleSpec?.dataSourceSpec.spec.status || '-'}
|
||||
/>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
)}
|
||||
{contractCall.blockHeight && (
|
||||
<TableRow modifier="bordered">
|
||||
<TableCell>{t('ETH block')}</TableCell>
|
||||
<TableCell>
|
||||
<EthExplorerLink
|
||||
id={contractCall.blockHeight}
|
||||
type={EthExplorerLinkTypes.block}
|
||||
/>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
)}
|
||||
{data?.oracleSpec?.dataSourceSpec && (
|
||||
<OracleEthSource
|
||||
sourceType={data.oracleSpec.dataSourceSpec.spec.data.sourceType}
|
||||
/>
|
||||
)}
|
||||
|
||||
<TableRow modifier="bordered">
|
||||
<TableCell>{t('Result')}</TableCell>
|
||||
<TableCell>{decodeEthCallResult(contractCall.result)}</TableCell>
|
||||
</TableRow>
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -1,51 +1,11 @@
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { TxDetailsShared } from './shared/tx-details-shared';
|
||||
import { TableWithTbody } from '../../table';
|
||||
import { defaultAbiCoder, base64 } from 'ethers/lib/utils';
|
||||
import { ChainEvent } from './chain-events';
|
||||
import { BigNumber } from 'ethers';
|
||||
|
||||
import type { AbiType } from '../../../lib/encoders/abis/abi-types';
|
||||
import type { BlockExplorerTransactionResult } from '../../../routes/types/block-explorer-response';
|
||||
import type { TendermintBlocksResponse } from '../../../routes/blocks/tendermint-blocks-response';
|
||||
|
||||
interface AbiOutput {
|
||||
type: AbiType;
|
||||
internalType: AbiType;
|
||||
name: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes the b64/ABIcoded result from an eth cal
|
||||
* @param data
|
||||
* @returns
|
||||
*/
|
||||
export function decodeEthCallResult(
|
||||
data: BlockExplorerTransactionResult
|
||||
): string {
|
||||
const ethResult = data.command.chainEvent?.contractCall.result;
|
||||
|
||||
try {
|
||||
// Decode the result string: base64 => uint8array
|
||||
const data = base64.decode(ethResult);
|
||||
|
||||
// Parse the escaped ABI in to an object
|
||||
const abi = JSON.parse(
|
||||
'[{"inputs":[],"name":"latestAnswer","outputs":[{"internalType":"int256","name":"","type":"int256"}],"stateMutability":"view","type":"function"}]'
|
||||
);
|
||||
// Pull the expected types out of the Oracles ABI
|
||||
const types: AbiType[] = abi[0].outputs.map((o: AbiOutput) => o.type);
|
||||
|
||||
const rawResult = defaultAbiCoder.decode(types, data);
|
||||
|
||||
// Finally, convert the resulting BigNumber in to a string
|
||||
const res = BigNumber.from(rawResult[0]).toString();
|
||||
return res;
|
||||
} catch (e) {
|
||||
return '-';
|
||||
}
|
||||
}
|
||||
|
||||
interface TxDetailsChainEventProps {
|
||||
txData: BlockExplorerTransactionResult | undefined;
|
||||
pubKey: string | undefined;
|
||||
|
||||
@@ -3,8 +3,8 @@ import { DATA_SOURCES } from '../../../config';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { useFetch } from '@vegaprotocol/react-helpers';
|
||||
import { TxDetailsOrder } from './tx-order';
|
||||
import type { BlockExplorerTransactionResult } from '../../../routes/types/block-explorer-response';
|
||||
import type { TendermintBlocksResponse } from '../../../routes/blocks/tendermint-blocks-response';
|
||||
import { type BlockExplorerTransactionResult } from '../../../routes/types/block-explorer-response';
|
||||
import { type TendermintBlocksResponse } from '../../../routes/blocks/tendermint-blocks-response';
|
||||
import { TxDetailsHeartbeat } from './tx-hearbeat';
|
||||
import { TxDetailsGeneric } from './tx-generic';
|
||||
import { TxDetailsBatch } from './tx-batch';
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Table, TableRow } from '../table';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { useFetch } from '@vegaprotocol/react-helpers';
|
||||
import type { BlockExplorerTransactions } from '../../routes/types/block-explorer-response';
|
||||
import { type BlockExplorerTransactions } from '../../routes/types/block-explorer-response';
|
||||
import { getTxsDataUrl } from '../../hooks/get-txs-data-url';
|
||||
import { AsyncRenderer, Loader } from '@vegaprotocol/ui-toolkit';
|
||||
import EmptyList from '../empty-list/empty-list';
|
||||
@@ -22,7 +22,7 @@ export const TxsPerBlock = ({ blockHeight, txCount }: TxsPerBlockProps) => {
|
||||
return (
|
||||
<AsyncRenderer data={data} error={error} loading={!!loading}>
|
||||
{data && data.transactions.length > 0 ? (
|
||||
<div className="overflow-x-auto whitespace-nowrap mb-28">
|
||||
<div className="mb-28 overflow-x-auto whitespace-nowrap">
|
||||
<Table>
|
||||
<thead>
|
||||
<TableRow modifier="bordered" className="font-mono">
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import { useSearchParams } from 'react-router-dom';
|
||||
import type { URLSearchParamsInit } from 'react-router-dom';
|
||||
import { type URLSearchParamsInit } from 'react-router-dom';
|
||||
import { useCallback } from 'react';
|
||||
import { useFetch } from '@vegaprotocol/react-helpers';
|
||||
import type {
|
||||
BlockExplorerTransactionResult,
|
||||
BlockExplorerTransactions,
|
||||
import {
|
||||
type BlockExplorerTransactionResult,
|
||||
type BlockExplorerTransactions,
|
||||
} from '../routes/types/block-explorer-response';
|
||||
import isNumber from 'lodash/isNumber';
|
||||
import { AllFilterOptions } from '../components/txs/tx-filter';
|
||||
import type { FilterOption } from '../components/txs/tx-filter';
|
||||
import { type FilterOption } from '../components/txs/tx-filter';
|
||||
import { BE_TXS_PER_REQUEST, getTxsDataUrl } from './get-txs-data-url';
|
||||
|
||||
export function getTypeFilters(filters?: Set<FilterOption>) {
|
||||
|
||||
@@ -9,11 +9,13 @@ import { JsonViewerDialog } from '../../components/dialogs/json-viewer-dialog';
|
||||
import { useState } from 'react';
|
||||
import { PageTitle } from '../../components/page-helpers/page-title';
|
||||
|
||||
type Params = { assetId: string };
|
||||
|
||||
export const AssetPage = () => {
|
||||
useDocumentTitle(['Assets']);
|
||||
useScrollToLocation();
|
||||
|
||||
const { assetId } = useParams<{ assetId: string }>();
|
||||
const { assetId } = useParams<Params>();
|
||||
const { data, loading, error } = useAssetDataProvider(assetId || '');
|
||||
|
||||
const title = data ? data.name : error ? t('Asset not found') : '';
|
||||
@@ -41,7 +43,7 @@ export const AssetPage = () => {
|
||||
loading={loading}
|
||||
error={error}
|
||||
>
|
||||
<div className="h-full relative">
|
||||
<div className="relative h-full">
|
||||
<AssetDetailsTable asset={data as AssetFieldsFragment} />
|
||||
</div>
|
||||
</AsyncRenderer>
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { useCallback, useState } from 'react';
|
||||
import { DATA_SOURCES } from '../../../config';
|
||||
import type {
|
||||
BlockMeta,
|
||||
TendermintBlockchainResponse,
|
||||
import {
|
||||
type BlockMeta,
|
||||
type TendermintBlockchainResponse,
|
||||
} from '../tendermint-blockchain-response';
|
||||
import { RouteTitle } from '../../../components/route-title';
|
||||
import { BlocksRefetch } from '../../../components/blocks';
|
||||
|
||||
@@ -17,8 +17,10 @@ import { NodeLink } from '../../../components/links';
|
||||
import { useDocumentTitle } from '../../../hooks/use-document-title';
|
||||
import EmptyList from '../../../components/empty-list/empty-list';
|
||||
|
||||
type Params = { block: string };
|
||||
|
||||
const Block = () => {
|
||||
const { block } = useParams<{ block: string }>();
|
||||
const { block } = useParams<Params>();
|
||||
useDocumentTitle(['Blocks', `Block #${block}`]);
|
||||
const {
|
||||
state: { data: blockData, loading, error },
|
||||
@@ -29,7 +31,7 @@ const Block = () => {
|
||||
<RouteTitle data-testid="block-header">{t(`BLOCK ${block}`)}</RouteTitle>
|
||||
<AsyncRenderer data={blockData} error={error} loading={!!loading}>
|
||||
<>
|
||||
<div className="grid grid-cols-2 gap-2 mb-8">
|
||||
<div className="mb-8 grid grid-cols-2 gap-2">
|
||||
<Link
|
||||
data-testid="previous-block"
|
||||
to={`/${Routes.BLOCKS}/${Number(block) - 1}`}
|
||||
|
||||
@@ -3,7 +3,7 @@ import { useFetch } from '@vegaprotocol/react-helpers';
|
||||
import { RouteTitle } from '../../components/route-title';
|
||||
import { AsyncRenderer, SyntaxHighlighter } from '@vegaprotocol/ui-toolkit';
|
||||
import { DATA_SOURCES } from '../../config';
|
||||
import type { TendermintGenesisResponse } from './tendermint-genesis-response';
|
||||
import { type TendermintGenesisResponse } from './tendermint-genesis-response';
|
||||
import { useDocumentTitle } from '../../hooks/use-document-title';
|
||||
|
||||
const Genesis = () => {
|
||||
|
||||
@@ -11,10 +11,12 @@ import { JsonViewerDialog } from '../../components/dialogs/json-viewer-dialog';
|
||||
import { marketInfoWithDataProvider } from '@vegaprotocol/markets';
|
||||
import { PageTitle } from '../../components/page-helpers/page-title';
|
||||
|
||||
type Params = { marketId: string };
|
||||
|
||||
export const MarketPage = () => {
|
||||
useScrollToLocation();
|
||||
|
||||
const { marketId } = useParams<{ marketId: string }>();
|
||||
const { marketId } = useParams<Params>();
|
||||
|
||||
const { data, loading, error } = useDataProvider({
|
||||
dataProvider: marketInfoWithDataProvider,
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import { getNodes } from '@vegaprotocol/utils';
|
||||
import { MarketLink } from '../../../components/links';
|
||||
import { TableRow, TableCell, TableHeader } from '../../../components/table';
|
||||
import type { ExplorerOracleForMarketsMarketFragment } from '../__generated__/OraclesForMarkets';
|
||||
import { useExplorerOracleFormMarketsQuery } from '../__generated__/OraclesForMarkets';
|
||||
import {
|
||||
useExplorerOracleFormMarketsQuery,
|
||||
type ExplorerOracleForMarketsMarketFragment,
|
||||
} from '../__generated__/OraclesForMarkets';
|
||||
|
||||
interface OracleMarketsProps {
|
||||
id: string;
|
||||
|
||||
@@ -9,8 +9,10 @@ import { AsyncRenderer, SyntaxHighlighter } from '@vegaprotocol/ui-toolkit';
|
||||
import filter from 'recursive-key-filter';
|
||||
import { TruncateInline } from '../../../components/truncate/truncate';
|
||||
|
||||
type Params = { id: string };
|
||||
|
||||
export const Oracle = () => {
|
||||
const { id } = useParams<{ id: string }>();
|
||||
const { id } = useParams<Params>();
|
||||
|
||||
useDocumentTitle(['Oracle', `Oracle #${truncateByChars(id || '1', 5, 5)}`]);
|
||||
|
||||
|
||||
@@ -6,8 +6,10 @@ import { useDocumentTitle } from '../../../../hooks/use-document-title';
|
||||
|
||||
import { PartyAccounts } from '../components/party-accounts';
|
||||
|
||||
type Params = { party: string };
|
||||
|
||||
const PartyAccountsByAsset = () => {
|
||||
const { party } = useParams<{ party: string }>();
|
||||
const { party } = useParams<Params>();
|
||||
|
||||
useDocumentTitle(['Public keys', party || '-']);
|
||||
const partyId = toNonHex(party ? party : '');
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { AccountManager } from '@vegaprotocol/accounts';
|
||||
import { useCallback } from 'react';
|
||||
import { useAssetDetailsDialogStore } from '@vegaprotocol/assets';
|
||||
import { useExplorerPartyAssetsQuery } from '../__generated__/Party-assets';
|
||||
import { AssetLink, MarketLink } from '../../../../components/links';
|
||||
import AssetBalance from '../../../../components/asset-balance/asset-balance';
|
||||
import { AccountTypeMapping } from '@vegaprotocol/types';
|
||||
|
||||
interface PartyAccountsProps {
|
||||
partyId: string;
|
||||
@@ -12,21 +13,71 @@ interface PartyAccountsProps {
|
||||
* appearing first and... tbd
|
||||
*/
|
||||
export const PartyAccounts = ({ partyId }: PartyAccountsProps) => {
|
||||
const { open: openAssetDetailsDialog } = useAssetDetailsDialogStore();
|
||||
const onClickAsset = useCallback(
|
||||
(assetId?: string) => {
|
||||
assetId && openAssetDetailsDialog(assetId);
|
||||
},
|
||||
[openAssetDetailsDialog]
|
||||
);
|
||||
const { data } = useExplorerPartyAssetsQuery({
|
||||
variables: { partyId },
|
||||
});
|
||||
|
||||
const party = data?.partiesConnection?.edges[0]?.node;
|
||||
const accounts =
|
||||
party?.accountsConnection?.edges?.filter((edge) => edge?.node) || [];
|
||||
|
||||
return (
|
||||
<div className="block min-h-44 h-60 4 w-full border-red-800 relative">
|
||||
<AccountManager
|
||||
partyId={partyId}
|
||||
onClickAsset={onClickAsset}
|
||||
isReadOnly={true}
|
||||
/>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th className="text-right px-4">Balance</th>
|
||||
<th className="text-left px-4">Type</th>
|
||||
<th className="text-left px-4">Market</th>
|
||||
<th className="text-left px-4">Asset</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{accounts
|
||||
.sort((a, b) => {
|
||||
// Sort by asset id, then market id, with general accounts first
|
||||
if (!a) {
|
||||
return 1;
|
||||
}
|
||||
if (!b) {
|
||||
return -1;
|
||||
}
|
||||
if (a.node.asset.id !== b.node.asset.id) {
|
||||
return a.node.asset.id.localeCompare(b.node.asset.id);
|
||||
}
|
||||
if (a.node.type === 'ACCOUNT_TYPE_GENERAL') return -1;
|
||||
if (b.node.type === 'ACCOUNT_TYPE_GENERAL') return 1;
|
||||
if (a.node.market && b.node.market) {
|
||||
return a.node.market.id.localeCompare(b.node.market.id);
|
||||
} else {
|
||||
return a.node.type.localeCompare(b.node.type);
|
||||
}
|
||||
})
|
||||
.map((e) => {
|
||||
if (!e) return null;
|
||||
const { type, asset, balance, market } = e.node;
|
||||
|
||||
return (
|
||||
<tr className="border-t border-neutral-300 dark:border-neutral-600">
|
||||
<td className="px-4 text-right">
|
||||
<AssetBalance
|
||||
assetId={asset.id}
|
||||
price={balance}
|
||||
showAssetSymbol={true}
|
||||
/>
|
||||
</td>
|
||||
<td className="px-4">{AccountTypeMapping[type]}</td>
|
||||
<td className="px-4">
|
||||
{market?.id ? <MarketLink id={market.id} /> : '-'}
|
||||
</td>
|
||||
<td className="px-4">
|
||||
<AssetLink assetId={asset.id} />
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
})}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -19,11 +19,13 @@ import type { FilterOption } from '../../../components/txs/tx-filter';
|
||||
import { AllFilterOptions, TxsFilter } from '../../../components/txs/tx-filter';
|
||||
import { useSearchParams } from 'react-router-dom';
|
||||
|
||||
type Params = { party: string };
|
||||
|
||||
const Party = () => {
|
||||
const [params] = useSearchParams();
|
||||
|
||||
const [filters, setFilters] = useState(new Set(AllFilterOptions));
|
||||
const { party } = useParams<{ party: string }>();
|
||||
const { party } = useParams<Params>();
|
||||
|
||||
useDocumentTitle(['Public keys', party || '-']);
|
||||
const navigate = useNavigate();
|
||||
@@ -60,7 +62,7 @@ const Party = () => {
|
||||
|
||||
if (!isValidPartyId(partyId)) {
|
||||
return (
|
||||
<div className="max-w-sm mx-auto">
|
||||
<div className="mx-auto max-w-sm">
|
||||
<Notification
|
||||
message={t('Invalid party ID')}
|
||||
intent={Intent.Danger}
|
||||
@@ -84,7 +86,7 @@ const Party = () => {
|
||||
truncateEnd={visibleChars}
|
||||
/>
|
||||
|
||||
<div className="grid md:grid-flow-col grid-flow-row md:space-x-4 grid-cols-1 md:grid-cols-2 w-full">
|
||||
<div className="grid w-full grid-flow-row grid-cols-1 md:grid-flow-col md:grid-cols-2 md:space-x-4">
|
||||
<PartyBlockAccounts
|
||||
accountError={AccountError}
|
||||
accountLoading={AccountLoading}
|
||||
|
||||
@@ -4,13 +4,15 @@ import { useFetch } from '@vegaprotocol/react-helpers';
|
||||
import { DATA_SOURCES } from '../../../config';
|
||||
import { RenderFetched } from '../../../components/render-fetched';
|
||||
import { TxDetails } from './tx-details';
|
||||
import type { BlockExplorerTransaction } from '../../../routes/types/block-explorer-response';
|
||||
import { type BlockExplorerTransaction } from '../../../routes/types/block-explorer-response';
|
||||
import { toNonHex } from '../../../components/search/detect-search';
|
||||
import { PageHeader } from '../../../components/page-header';
|
||||
import { useDocumentTitle } from '../../../hooks/use-document-title';
|
||||
|
||||
type Params = { txHash: string };
|
||||
|
||||
const Tx = () => {
|
||||
const { txHash } = useParams<{ txHash: string }>();
|
||||
const { txHash } = useParams<Params>();
|
||||
const hash = txHash ? toNonHex(txHash) : '';
|
||||
let errorMessage: string | undefined = undefined;
|
||||
|
||||
|
||||
@@ -3,6 +3,9 @@
|
||||
// expect(element).toHaveTextContent(/react/i)
|
||||
// learn more: https://github.com/testing-library/jest-dom
|
||||
import '@testing-library/jest-dom';
|
||||
import { locales } from '@vegaprotocol/i18n';
|
||||
import i18n from 'i18next';
|
||||
import { initReactI18next } from 'react-i18next';
|
||||
|
||||
Object.defineProperty(window, 'ResizeObserver', {
|
||||
writable: false,
|
||||
@@ -13,3 +16,14 @@ Object.defineProperty(window, 'ResizeObserver', {
|
||||
disconnect: jest.fn(),
|
||||
})),
|
||||
});
|
||||
|
||||
// Set up i18n instance so that components have the correct default
|
||||
// en translations
|
||||
i18n.use(initReactI18next).init({
|
||||
// we init with resources
|
||||
resources: locales,
|
||||
fallbackLng: 'en',
|
||||
nsSeparator: false,
|
||||
ns: ['explorer'],
|
||||
defaultNS: 'explorer',
|
||||
});
|
||||
|
||||
Symlink
+1
@@ -0,0 +1 @@
|
||||
../../../../libs/i18n/src/locales
|
||||
@@ -0,0 +1,45 @@
|
||||
import type { Module } from 'i18next';
|
||||
import i18n from 'i18next';
|
||||
import HttpBackend from 'i18next-http-backend';
|
||||
import LocizeBackend from 'i18next-locize-backend';
|
||||
import LanguageDetector from 'i18next-browser-languagedetector';
|
||||
import { initReactI18next } from 'react-i18next';
|
||||
|
||||
const isInDev = process.env.NODE_ENV === 'development';
|
||||
const useLocize = isInDev && !!process.env.NX_USE_LOCIZE;
|
||||
|
||||
const backend = useLocize
|
||||
? {
|
||||
projectId: '96ac1231-4bdd-455a-b9d7-f5322a2e7430',
|
||||
apiKey: process.env.NX_LOCIZE_API_KEY,
|
||||
referenceLng: 'en',
|
||||
}
|
||||
: {
|
||||
loadPath: '/assets/locales/{{lng}}/{{ns}}.json',
|
||||
};
|
||||
|
||||
const Backend: Module = useLocize ? LocizeBackend : HttpBackend;
|
||||
|
||||
i18n
|
||||
.use(Backend)
|
||||
.use(LanguageDetector)
|
||||
.use(initReactI18next)
|
||||
.init({
|
||||
lng: 'en',
|
||||
fallbackLng: 'en',
|
||||
supportedLngs: ['en'],
|
||||
load: 'languageOnly',
|
||||
debug: isInDev,
|
||||
// have a common namespace used around the full app
|
||||
ns: ['explorer'],
|
||||
defaultNS: 'explorer',
|
||||
keySeparator: false, // we use content as keys
|
||||
nsSeparator: false,
|
||||
backend,
|
||||
saveMissing: useLocize && !!process.env.NX_LOCIZE_API_KEY,
|
||||
interpolation: {
|
||||
escapeValue: false,
|
||||
},
|
||||
});
|
||||
|
||||
export default i18n;
|
||||
@@ -2,7 +2,11 @@
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "../../dist/out-tsc",
|
||||
"types": ["node"]
|
||||
"types": [
|
||||
"node",
|
||||
"@nx/react/typings/cssmodule.d.ts",
|
||||
"@nx/react/typings/image.d.ts"
|
||||
]
|
||||
},
|
||||
"files": [
|
||||
"../../node_modules/@nx/react/typings/cssmodule.d.ts",
|
||||
|
||||
@@ -3,7 +3,13 @@
|
||||
"compilerOptions": {
|
||||
"outDir": "../../dist/out-tsc",
|
||||
"module": "commonjs",
|
||||
"types": ["jest", "node", "@testing-library/jest-dom"]
|
||||
"types": [
|
||||
"jest",
|
||||
"node",
|
||||
"@testing-library/jest-dom",
|
||||
"@nx/react/typings/cssmodule.d.ts",
|
||||
"@nx/react/typings/image.d.ts"
|
||||
]
|
||||
},
|
||||
"include": [
|
||||
"**/*.test.ts",
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
{
|
||||
"extends": ["plugin:cypress/recommended", "../../.eslintrc.json"],
|
||||
"ignorePatterns": ["!**/*"],
|
||||
"ignorePatterns": ["!**/*", "cypress"],
|
||||
"overrides": [
|
||||
{
|
||||
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
|
||||
"rules": {}
|
||||
"rules": {
|
||||
"cypress/unsafe-to-chain-command": 0
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
}
|
||||
},
|
||||
"lint": {
|
||||
"executor": "@nx/linter:eslint",
|
||||
"executor": "@nx/eslint:lint",
|
||||
"outputs": ["{options.outputFile}"],
|
||||
"options": {
|
||||
"lintFilePatterns": ["apps/governance-e2e/**/*.{js,ts}"]
|
||||
|
||||
@@ -31,7 +31,7 @@ import {
|
||||
switchVegaWalletPubKey,
|
||||
vegaWalletSetSpecifiedApprovalAmount,
|
||||
} from '../../support/wallet-functions';
|
||||
import type { testFreeformProposal } from '../../support/common-interfaces';
|
||||
import { type testFreeformProposal } from '../../support/common-interfaces';
|
||||
import { formatDateWithLocalTimezone } from '@vegaprotocol/utils';
|
||||
import {
|
||||
createGovernanceTransferProposalTxBody,
|
||||
@@ -61,6 +61,7 @@ const marketProposalType = 'proposal-type';
|
||||
describe(
|
||||
'Governance flow for proposal details',
|
||||
{ tags: '@slow' },
|
||||
// @ts-ignore clash between jest and cypress
|
||||
function () {
|
||||
before('connect wallets and set approval limit', function () {
|
||||
cy.visit('/');
|
||||
@@ -78,6 +79,7 @@ describe(
|
||||
getProposalInformationFromTable('ID').invoke('text').as('parentMarketId');
|
||||
});
|
||||
|
||||
// @ts-ignore clash between jest and cypress
|
||||
beforeEach('visit proposals tab', function () {
|
||||
cy.clearLocalStorage();
|
||||
turnTelemetryOff();
|
||||
@@ -323,6 +325,7 @@ describe(
|
||||
});
|
||||
});
|
||||
cy.VegaWalletSubmitProposal(
|
||||
// @ts-ignore this is any
|
||||
createSuccessorMarketProposalTxBody(this.parentMarketId)
|
||||
);
|
||||
navigateTo(navigation.proposals);
|
||||
@@ -334,6 +337,7 @@ describe(
|
||||
cy.getByTestId(proposalTermsToggle).click();
|
||||
cy.get('.language-json').within(() => {
|
||||
cy.get('.hljs-attr').should('contain.text', 'parentMarketId');
|
||||
// @ts-ignore this is any
|
||||
cy.get('.hljs-string').should('contain.text', this.parentMarketId);
|
||||
cy.get('.hljs-attr').should('contain.text', 'insurancePoolFraction');
|
||||
cy.get('.hljs-string').should('contain.text', '0.75');
|
||||
@@ -352,6 +356,7 @@ describe(
|
||||
validateProposalDetailsDiff(
|
||||
'Parent Market ID',
|
||||
proposalChangeType.ADDED,
|
||||
// @ts-ignore this is any
|
||||
this.parentMarketId
|
||||
);
|
||||
validateProposalDetailsDiff(
|
||||
@@ -450,6 +455,7 @@ describe(
|
||||
const closingTimestamp = createTenDigitUnixTimeStampForSpecifiedDays(2);
|
||||
submitUniqueRawProposal({
|
||||
proposalBody: proposalPath,
|
||||
// @ts-ignore this is any
|
||||
updateMarketId: this.parentMarketId,
|
||||
enactmentTimestamp: enactmentTimestamp,
|
||||
closingTimestamp: closingTimestamp,
|
||||
@@ -466,6 +472,7 @@ describe(
|
||||
cy.getByTestId('proposal-update-market-state').within(() => {
|
||||
getProposalInformationFromTable('Market ID')
|
||||
.invoke('text')
|
||||
// @ts-ignore this is any
|
||||
.and('eq', this.parentMarketId);
|
||||
});
|
||||
});
|
||||
@@ -476,6 +483,7 @@ describe(
|
||||
const closingTimestamp = createTenDigitUnixTimeStampForSpecifiedDays(2);
|
||||
submitUniqueRawProposal({
|
||||
proposalBody: proposalPath,
|
||||
// @ts-ignore this is any
|
||||
updateMarketId: this.parentMarketId,
|
||||
enactmentTimestamp: enactmentTimestamp,
|
||||
closingTimestamp: closingTimestamp,
|
||||
@@ -489,6 +497,7 @@ describe(
|
||||
cy.getByTestId('proposal-update-market-state').within(() => {
|
||||
getProposalInformationFromTable('Market ID')
|
||||
.invoke('text')
|
||||
// @ts-ignore this is any
|
||||
.and('eq', this.parentMarketId);
|
||||
});
|
||||
});
|
||||
@@ -499,6 +508,7 @@ describe(
|
||||
const closingTimestamp = createTenDigitUnixTimeStampForSpecifiedDays(2);
|
||||
submitUniqueRawProposal({
|
||||
proposalBody: proposalPath,
|
||||
// @ts-ignore this is any
|
||||
updateMarketId: this.parentMarketId,
|
||||
enactmentTimestamp: enactmentTimestamp,
|
||||
closingTimestamp: closingTimestamp,
|
||||
@@ -518,6 +528,7 @@ describe(
|
||||
cy.getByTestId('proposal-update-market-state').within(() => {
|
||||
getProposalInformationFromTable('Market ID')
|
||||
.invoke('text')
|
||||
// @ts-ignore this is any
|
||||
.and('eq', this.parentMarketId);
|
||||
getProposalDetailsValue('Termination Price').should(
|
||||
'contain.text',
|
||||
|
||||
@@ -37,6 +37,7 @@ context(
|
||||
vegaWalletSetSpecifiedApprovalAmount('1000');
|
||||
});
|
||||
|
||||
// @ts-ignore clash between jest and cypress
|
||||
beforeEach('visit proposals', function () {
|
||||
cy.clearLocalStorage();
|
||||
turnTelemetryOff();
|
||||
|
||||
@@ -34,7 +34,7 @@ import {
|
||||
vegaWalletTeardown,
|
||||
} from '../../support/wallet-functions';
|
||||
import { ethereumWalletConnect } from '../../support/wallet-eth.functions';
|
||||
import type { testFreeformProposal } from '../../support/common-interfaces';
|
||||
import { type testFreeformProposal } from '../../support/common-interfaces';
|
||||
|
||||
const vegaWalletStakedBalances = 'vega-wallet-balance-staked-validators';
|
||||
const vegaWalletAssociatedBalance = 'associated-amount';
|
||||
@@ -78,6 +78,7 @@ context(
|
||||
vegaWalletSetSpecifiedApprovalAmount('1000');
|
||||
});
|
||||
|
||||
// @ts-ignore clash between jest and cypress
|
||||
beforeEach('visit governance tab', function () {
|
||||
cy.clearLocalStorage();
|
||||
turnTelemetryOff();
|
||||
@@ -301,6 +302,7 @@ context(
|
||||
cy.getByTestId(voteButtons).should('not.exist');
|
||||
cy.getByTestId('min-proposal-requirements').should(
|
||||
'have.text',
|
||||
// @ts-ignore this is any
|
||||
`You must have at least ${this.minVoterBalance} VEGA associated to vote on this proposal`
|
||||
);
|
||||
});
|
||||
|
||||
@@ -70,6 +70,7 @@ context(
|
||||
vegaWalletSetSpecifiedApprovalAmount('1000');
|
||||
});
|
||||
|
||||
// @ts-ignore clash between jest and cypress
|
||||
beforeEach('visit governance tab', function () {
|
||||
cy.clearLocalStorage();
|
||||
turnTelemetryOff();
|
||||
@@ -216,6 +217,7 @@ context(
|
||||
// 3003-PMAN-001
|
||||
it(
|
||||
'Able to submit valid new market proposal',
|
||||
// @ts-ignore clash between jest and cypress
|
||||
{ tags: '@smoke' },
|
||||
function () {
|
||||
const proposalTitle = 'Test new market proposal';
|
||||
@@ -631,6 +633,7 @@ context(
|
||||
);
|
||||
cy.fixture('/proposals/successor-market').then((newMarketProposal) => {
|
||||
newMarketProposal.changes.successor.parentMarketId =
|
||||
// @ts-ignore clash between jest and cypress
|
||||
this.parentMarketId;
|
||||
const newMarketPayload = JSON.stringify(newMarketProposal);
|
||||
cy.getByTestId(newProposalTerms).type(newMarketPayload, {
|
||||
@@ -658,6 +661,7 @@ context(
|
||||
.should('have.text', 'Successor market to: TEST.24h')
|
||||
.find('a')
|
||||
.should('have.attr', 'href')
|
||||
// @ts-ignore clash between jest and cypress
|
||||
.and('contain', this.parentMarketId);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { testFreeformProposal } from '../../support/common-interfaces';
|
||||
import { type testFreeformProposal } from '../../support/common-interfaces';
|
||||
import {
|
||||
navigateTo,
|
||||
navigation,
|
||||
@@ -33,12 +33,14 @@ const voteMajorityNotMet = 'token-majority-not-met';
|
||||
const voteMajorityMet = 'token-majority-met';
|
||||
const votesForPercentage = 'votes-for-percentage';
|
||||
|
||||
// @ts-ignore clash between jest and cypress
|
||||
describe('Governance flow for proposal list', { tags: '@slow' }, function () {
|
||||
before('connect wallets and set approval limit', function () {
|
||||
vegaWalletSetSpecifiedApprovalAmount('1000');
|
||||
cy.visit('/');
|
||||
});
|
||||
|
||||
// @ts-ignore clash between jest and cypress
|
||||
beforeEach('visit proposals tab', function () {
|
||||
cy.clearLocalStorage();
|
||||
turnTelemetryOff();
|
||||
@@ -106,6 +108,7 @@ describe('Governance flow for proposal list', { tags: '@slow' }, function () {
|
||||
it('Newly created proposals list - shows open proposals in an open state', function () {
|
||||
// 3001-VOTE-004
|
||||
// 3001-VOTE-035
|
||||
// @ts-ignore clash between jest and cypress
|
||||
createRawProposal(this.minProposerBalance);
|
||||
cy.get<testFreeformProposal>('@rawProposal').then((rawProposal) => {
|
||||
getProposalFromTitle(rawProposal.rationale.title).within(() => {
|
||||
|
||||
@@ -64,6 +64,7 @@ context(
|
||||
|
||||
describe('Eth wallet - contains VEGA tokens', function () {
|
||||
beforeEach(
|
||||
// @ts-ignore clash between jest and cypress
|
||||
'teardown wallet & drill into a specific validator',
|
||||
function () {
|
||||
cy.clearLocalStorage();
|
||||
@@ -237,6 +238,7 @@ context(
|
||||
// 1002-STKE-041 1002-STKE-053
|
||||
it(
|
||||
'Able to remove part of a stake against a validator',
|
||||
// @ts-ignore clash between jest and cypress
|
||||
{ tags: '@smoke' },
|
||||
function () {
|
||||
ensureSpecifiedUnstakedTokensAreAssociated('4');
|
||||
|
||||
@@ -52,6 +52,7 @@ context(
|
||||
|
||||
describe('Eth wallet - contains VEGA tokens', function () {
|
||||
beforeEach(
|
||||
// @ts-ignore clash between jest and cypress
|
||||
'teardown wallet & drill into a specific validator',
|
||||
function () {
|
||||
cy.clearLocalStorage();
|
||||
@@ -67,6 +68,7 @@ context(
|
||||
|
||||
it(
|
||||
'Able to associate tokens - from wallet',
|
||||
// @ts-ignore clash between jest and cypress
|
||||
{ tags: '@smoke' },
|
||||
function () {
|
||||
//1004-ASSO-003
|
||||
|
||||
@@ -48,6 +48,7 @@ context(
|
||||
depositAsset(usdcEthAddress, '1000', 5);
|
||||
});
|
||||
|
||||
// @ts-ignore clash between jest and cypress
|
||||
beforeEach('Navigate to withdrawal page', function () {
|
||||
cy.clearLocalStorage();
|
||||
turnTelemetryOff();
|
||||
@@ -101,6 +102,7 @@ context(
|
||||
// eslint-disable-next-line
|
||||
it.skip(
|
||||
'Able to withdraw asset: -eth wallet connected -withdraw funds button',
|
||||
// @ts-ignore clash between jest and cypress
|
||||
{ tags: '@smoke' },
|
||||
function () {
|
||||
// fill in withdrawal form
|
||||
|
||||
@@ -25,6 +25,7 @@ context('View functionality with public key', { tags: '@smoke' }, function () {
|
||||
);
|
||||
});
|
||||
|
||||
// @ts-ignore clash between jest and cypress
|
||||
beforeEach('visit home page', function () {
|
||||
cy.clearLocalStorage();
|
||||
turnTelemetryOff();
|
||||
|
||||
@@ -52,6 +52,7 @@ context(
|
||||
.invoke('text')
|
||||
.then(($newPageNumber) => {
|
||||
const newPageNumber = Number($newPageNumber.slice(5));
|
||||
// @ts-ignore clash between jest and cypress
|
||||
expect(newPageNumber).to.be.greaterThan(currentPageNumber);
|
||||
cy.getByTestId('goto-previous-page').click();
|
||||
cy.getByTestId('page-info').should(
|
||||
|
||||
@@ -44,6 +44,7 @@ context('Validators Page - verify elements on page', function () {
|
||||
cy.mockChainId();
|
||||
});
|
||||
|
||||
// @ts-ignore clash between jest and cypress
|
||||
describe('with wallets disconnected', { tags: '@smoke' }, function () {
|
||||
it('Should have validators tab highlighted', function () {
|
||||
verifyTabHighlighted(navigation.validators);
|
||||
@@ -76,6 +77,7 @@ context('Validators Page - verify elements on page', function () {
|
||||
describe(
|
||||
'Should be able to see validator list from the staking page',
|
||||
{ tags: '@regression' },
|
||||
// @ts-ignore clash between jest and cypress
|
||||
function () {
|
||||
// 1002-STKE-050
|
||||
it('Should be able to see validator names', function () {
|
||||
@@ -180,6 +182,7 @@ context('Validators Page - verify elements on page', function () {
|
||||
describe(
|
||||
'Should be able to see static information about a validator',
|
||||
{ tags: '@smoke' },
|
||||
// @ts-ignore clash between jest and cypress
|
||||
function () {
|
||||
before('connect wallets and click on validator', function () {
|
||||
cy.mockChainId();
|
||||
|
||||
@@ -198,6 +198,7 @@ context(
|
||||
});
|
||||
})
|
||||
.then(function () {
|
||||
// @ts-ignore clash between jest and cypress
|
||||
expect(parseFloat(this.value).toFixed(1)).to.equal(
|
||||
(Math.round((this.locked + this.unlocked) * 100) / 100).toFixed(
|
||||
1
|
||||
@@ -270,6 +271,7 @@ context(
|
||||
});
|
||||
})
|
||||
.then(function () {
|
||||
// @ts-ignore clash between jest and cypress
|
||||
expect(this.value).to.equal(this.locked + this.unlocked);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -105,6 +105,7 @@ context(
|
||||
// 0002-WCON-008
|
||||
it(
|
||||
'should have truncated account number visible',
|
||||
// @ts-ignore clash between jest and cypress
|
||||
{ tags: '@smoke' },
|
||||
function () {
|
||||
cy.get(walletContainer).within(() => {
|
||||
@@ -125,6 +126,7 @@ context(
|
||||
|
||||
it(
|
||||
'should have Vega Associated currency value visible',
|
||||
// @ts-ignore clash between jest and cypress
|
||||
{ tags: '@smoke' },
|
||||
function () {
|
||||
cy.get(walletContainer).within(() => {
|
||||
@@ -135,14 +137,19 @@ context(
|
||||
}
|
||||
);
|
||||
|
||||
it('should have Unstaked value visible', { tags: '@smoke' }, function () {
|
||||
cy.get(walletContainer).within(() => {
|
||||
cy.get(vegaUnstaked)
|
||||
.should('be.visible')
|
||||
.invoke('text')
|
||||
.and('not.be.empty');
|
||||
});
|
||||
});
|
||||
it(
|
||||
'should have Unstaked value visible',
|
||||
// @ts-ignore clash between jest and cypress
|
||||
{ tags: '@smoke' },
|
||||
function () {
|
||||
cy.get(walletContainer).within(() => {
|
||||
cy.get(vegaUnstaked)
|
||||
.should('be.visible')
|
||||
.invoke('text')
|
||||
.and('not.be.empty');
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
it('should have Governance button visible', function () {
|
||||
cy.get(walletContainer).within(() => {
|
||||
@@ -295,13 +302,17 @@ context(
|
||||
|
||||
cy.getByTestId(vegaWalletCurrencyTitle)
|
||||
.contains(name)
|
||||
.parent()
|
||||
.siblings(txTimeout)
|
||||
.should((elementAmount) => {
|
||||
const displayedAmount = parseFloat(elementAmount.text());
|
||||
expect(displayedAmount).be.gte(expectedAmount);
|
||||
.parent() // back to currency-title
|
||||
.parent() // back to container
|
||||
.within(() => {
|
||||
cy.get(
|
||||
'[data-account-type="account_type_general"] [data-value]'
|
||||
).should((elementAmount) => {
|
||||
const displayedAmount = parseFloat(elementAmount.text());
|
||||
// @ts-ignore clash between jest and cypress
|
||||
expect(displayedAmount).be.gte(expectedAmount);
|
||||
});
|
||||
});
|
||||
|
||||
cy.getByTestId(vegaWalletCurrencyTitle)
|
||||
.contains(name)
|
||||
.parent()
|
||||
|
||||
@@ -256,10 +256,7 @@ export function validateWalletCurrency(
|
||||
.parent()
|
||||
.parent()
|
||||
.within(() => {
|
||||
cy.getByTestId('currency-value', txTimeout).should(
|
||||
'have.text',
|
||||
expectedAmount
|
||||
);
|
||||
cy.get('[data-value]', txTimeout).should('have.text', expectedAmount);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -49,10 +49,6 @@ There are a few different configuration options offered for this app:
|
||||
| `NX_ETH_WALLET_MNEMONIC` (optional) | The mnemonic to be used to sign transactions with in browser |
|
||||
| `NX_LOCAL_PROVIDER_URL` (optional) | The local node to use to send transaction to when signing in browser |
|
||||
|
||||
## Example configs:
|
||||
|
||||
For example configurations, check out our [netlify.toml](./netlify.toml).
|
||||
|
||||
## Testing
|
||||
|
||||
To run the minimal set of unit tests, run the following:
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
[[redirects]]
|
||||
from = "/*"
|
||||
to = "/index.html"
|
||||
status = 200
|
||||
@@ -56,7 +56,7 @@
|
||||
}
|
||||
},
|
||||
"lint": {
|
||||
"executor": "@nx/linter:eslint",
|
||||
"executor": "@nx/eslint:lint",
|
||||
"outputs": ["{options.outputFile}"],
|
||||
"options": {
|
||||
"lintFilePatterns": ["apps/governance/**/*.{ts,tsx,js,jsx}"]
|
||||
@@ -66,23 +66,7 @@
|
||||
"executor": "@nx/jest:jest",
|
||||
"outputs": ["{workspaceRoot}/coverage/apps/governance"],
|
||||
"options": {
|
||||
"jestConfig": "apps/governance/jest.config.ts",
|
||||
"passWithNoTests": true
|
||||
},
|
||||
"configurations": {
|
||||
"ci": {
|
||||
"ci": true,
|
||||
"codeCoverage": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"build-netlify": {
|
||||
"executor": "nx:run-commands",
|
||||
"options": {
|
||||
"commands": [
|
||||
"cp apps/governance/netlify.toml netlify.toml",
|
||||
"nx build token"
|
||||
]
|
||||
"jestConfig": "apps/governance/jest.config.ts"
|
||||
}
|
||||
},
|
||||
"build-spec": {
|
||||
|
||||
@@ -4,7 +4,7 @@ import { Splash } from '@vegaprotocol/ui-toolkit';
|
||||
import { useVegaWallet, useEagerConnect } from '@vegaprotocol/wallet';
|
||||
import { FLAGS, useEnvironment } from '@vegaprotocol/environment';
|
||||
import { useWeb3React } from '@web3-react/core';
|
||||
import React from 'react';
|
||||
import React, { Suspense } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { SplashError } from './components/splash-error';
|
||||
@@ -164,13 +164,14 @@ export const AppLoader = ({ children }: { children: React.ReactElement }) => {
|
||||
);
|
||||
}
|
||||
|
||||
if (!loaded) {
|
||||
return (
|
||||
<Splash>
|
||||
<SplashLoader />
|
||||
</Splash>
|
||||
);
|
||||
}
|
||||
const loading = (
|
||||
<Splash>
|
||||
<SplashLoader />
|
||||
</Splash>
|
||||
);
|
||||
|
||||
return children;
|
||||
if (!loaded) {
|
||||
return loading;
|
||||
}
|
||||
return <Suspense fallback={loading}>{children}</Suspense>;
|
||||
};
|
||||
|
||||
@@ -42,6 +42,7 @@ import {
|
||||
useNodeSwitcherStore,
|
||||
DocsLinks,
|
||||
NodeFailure,
|
||||
AppLoader as Loader,
|
||||
} from '@vegaprotocol/environment';
|
||||
import { ENV } from './config';
|
||||
import type { InMemoryCacheConfig } from '@apollo/client';
|
||||
@@ -352,9 +353,11 @@ function App() {
|
||||
useInitializeEnv();
|
||||
|
||||
return (
|
||||
<NetworkLoader cache={cache}>
|
||||
<AppContainer />
|
||||
</NetworkLoader>
|
||||
<React.Suspense fallback={<Loader />}>
|
||||
<NetworkLoader cache={cache}>
|
||||
<AppContainer />
|
||||
</NetworkLoader>
|
||||
</React.Suspense>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
+1
@@ -0,0 +1 @@
|
||||
../../../../libs/i18n/src/locales
|
||||
@@ -10,7 +10,7 @@ import noIcon from '../../images/token-no-icon.png';
|
||||
import vegaBlack from '../../images/vega_black.png';
|
||||
import vegaVesting from '../../images/vega_vesting.png';
|
||||
import { BigNumber } from '../../lib/bignumber';
|
||||
import type { WalletCardAssetProps } from '../wallet-card';
|
||||
import { type WalletCardAssetProps } from '../wallet-card';
|
||||
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||
import { useContracts } from '../../contexts/contracts/contracts-context';
|
||||
import * as Schema from '@vegaprotocol/types';
|
||||
@@ -21,12 +21,12 @@ import {
|
||||
toBigNum,
|
||||
} from '@vegaprotocol/utils';
|
||||
import { useAppState } from '../../contexts/app-state/app-state-context';
|
||||
import type {
|
||||
DelegationsQuery,
|
||||
DelegationsQueryVariables,
|
||||
WalletDelegationFieldsFragment,
|
||||
import {
|
||||
DelegationsDocument,
|
||||
type DelegationsQuery,
|
||||
type DelegationsQueryVariables,
|
||||
type WalletDelegationFieldsFragment,
|
||||
} from './__generated__/Delegations';
|
||||
import { DelegationsDocument } from './__generated__/Delegations';
|
||||
import { isPartyNotFoundError } from '../../lib/party';
|
||||
|
||||
export const usePollForDelegations = () => {
|
||||
@@ -44,6 +44,7 @@ export const usePollForDelegations = () => {
|
||||
const [delegatedNodes, setDelegatedNodes] = React.useState<
|
||||
{
|
||||
nodeId: string;
|
||||
// eslint-disable-next-line
|
||||
name: string;
|
||||
hasStakePending: boolean;
|
||||
currentEpochStake?: BigNumber;
|
||||
@@ -113,6 +114,16 @@ export const usePollForDelegations = () => {
|
||||
isAssetTypeERC20(a.asset) &&
|
||||
a.asset.source.contractAddress === vegaToken.address;
|
||||
|
||||
const isVesting =
|
||||
a.type === Schema.AccountType.ACCOUNT_TYPE_VESTED_REWARDS ||
|
||||
a.type === Schema.AccountType.ACCOUNT_TYPE_VESTING_REWARDS;
|
||||
|
||||
let icon = noIcon;
|
||||
if (isVega) {
|
||||
if (isVesting) icon = vegaVesting;
|
||||
else icon = vegaBlack;
|
||||
}
|
||||
|
||||
return {
|
||||
isVega,
|
||||
name: a.asset.name,
|
||||
@@ -123,14 +134,7 @@ export const usePollForDelegations = () => {
|
||||
balance: new BigNumber(
|
||||
addDecimal(a.balance, a.asset.decimals)
|
||||
),
|
||||
image: isVega
|
||||
? vegaBlack
|
||||
: a.type ===
|
||||
Schema.AccountType.ACCOUNT_TYPE_VESTED_REWARDS ||
|
||||
a.type ===
|
||||
Schema.AccountType.ACCOUNT_TYPE_VESTING_REWARDS
|
||||
? vegaVesting
|
||||
: noIcon,
|
||||
image: icon,
|
||||
border: isVega,
|
||||
address: isAssetTypeERC20(a.asset)
|
||||
? a.asset.source.contractAddress
|
||||
|
||||
@@ -11,7 +11,10 @@ import { BigNumber } from '../../lib/bignumber';
|
||||
import { truncateMiddle } from '../../lib/truncate-middle';
|
||||
import Routes from '../../routes/routes';
|
||||
import { BulletHeader } from '../bullet-header';
|
||||
import type { WalletCardAssetProps } from '../wallet-card';
|
||||
import type {
|
||||
WalletCardAssetProps,
|
||||
WalletCardAssetWithMultipleBalancesProps,
|
||||
} from '../wallet-card';
|
||||
import {
|
||||
WalletCard,
|
||||
WalletCardActions,
|
||||
@@ -27,6 +30,7 @@ import { Button, ButtonLink } from '@vegaprotocol/ui-toolkit';
|
||||
import { toBigNum } from '@vegaprotocol/utils';
|
||||
import { usePendingBalancesStore } from '../../hooks/use-pending-balances-manager';
|
||||
import { StakingEventType } from '../../hooks/use-get-association-breakdown';
|
||||
import omit from 'lodash/omit';
|
||||
|
||||
export const VegaWallet = () => {
|
||||
const { t } = useTranslation();
|
||||
@@ -99,12 +103,29 @@ const VegaWalletAssetList = ({ accounts }: VegaWalletAssetsListProps) => {
|
||||
if (!accounts.length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const groupedByAsset = accounts.reduce((all, a) => {
|
||||
const foundIndex = all.findIndex((acc) => acc.assetId === a.assetId);
|
||||
if (foundIndex > -1) {
|
||||
const found = all[foundIndex];
|
||||
all[foundIndex] = {
|
||||
...found,
|
||||
balances: [...found.balances, { balance: a.balance, type: a.type }],
|
||||
};
|
||||
return all;
|
||||
}
|
||||
const acc = {
|
||||
...omit(a, 'balance', 'type'),
|
||||
balances: [{ balance: a.balance, type: a.type }],
|
||||
};
|
||||
return [...all, acc];
|
||||
}, [] as WalletCardAssetWithMultipleBalancesProps[]);
|
||||
return (
|
||||
<>
|
||||
<WalletCardHeader>
|
||||
<BulletHeader tag="h2">{t('assets')}</BulletHeader>
|
||||
</WalletCardHeader>
|
||||
{accounts.map((a, i) => (
|
||||
{groupedByAsset.map((a, i) => (
|
||||
<WalletCardAsset key={i} {...a} />
|
||||
))}
|
||||
</>
|
||||
@@ -182,6 +203,7 @@ const VegaWalletConnected = ({ vegaKeys }: VegaWalletConnectedProps) => {
|
||||
subheading={t('Associated')}
|
||||
symbol="VEGA"
|
||||
balance={currentStakeAvailable}
|
||||
allowZeroBalance={true}
|
||||
/>
|
||||
{totalPending.eq(0) ? null : (
|
||||
<>
|
||||
@@ -192,6 +214,7 @@ const VegaWalletConnected = ({ vegaKeys }: VegaWalletConnectedProps) => {
|
||||
subheading={t('Pending association')}
|
||||
symbol="VEGA"
|
||||
balance={totalPending}
|
||||
allowZeroBalance={true}
|
||||
/>
|
||||
<WalletCardAsset
|
||||
image={vegaWhite}
|
||||
@@ -200,6 +223,7 @@ const VegaWalletConnected = ({ vegaKeys }: VegaWalletConnectedProps) => {
|
||||
subheading={t('Total associated after pending')}
|
||||
symbol="VEGA"
|
||||
balance={pendingStakeAmount}
|
||||
allowZeroBalance={true}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
|
||||
@@ -103,7 +103,7 @@ export const WalletCardActions = ({
|
||||
return <div className="flex justify-end gap-2 mb-4">{children}</div>;
|
||||
};
|
||||
|
||||
export interface WalletCardAssetProps {
|
||||
export type WalletCardAssetProps = {
|
||||
image: string;
|
||||
name: string;
|
||||
symbol: string;
|
||||
@@ -113,42 +113,61 @@ export interface WalletCardAssetProps {
|
||||
border?: boolean;
|
||||
subheading?: string;
|
||||
type?: Schema.AccountType;
|
||||
}
|
||||
allowZeroBalance?: boolean;
|
||||
};
|
||||
|
||||
export type WalletCardAssetWithMultipleBalancesProps = Omit<
|
||||
WalletCardAssetProps,
|
||||
'balance' | 'type'
|
||||
> & {
|
||||
balances: { balance: BigNumber; type?: Schema.AccountType }[];
|
||||
};
|
||||
|
||||
export const WalletCardAsset = ({
|
||||
image,
|
||||
name,
|
||||
symbol,
|
||||
balance,
|
||||
decimals,
|
||||
assetId,
|
||||
border,
|
||||
subheading,
|
||||
type,
|
||||
}: WalletCardAssetProps) => {
|
||||
const [integers, decimalsPlaces, separator] = useNumberParts(
|
||||
balance,
|
||||
decimals
|
||||
);
|
||||
const { t } = useTranslation();
|
||||
const consoleLink = useLinks(DApp.Console);
|
||||
const transferAssetLink = (assetId: string) =>
|
||||
consoleLink(CONSOLE_TRANSFER_ASSET.replace(':assetId', assetId));
|
||||
const { param: baseRate } = useNetworkParam('rewards_vesting_baseRate');
|
||||
allowZeroBalance = false,
|
||||
...props
|
||||
}: WalletCardAssetProps | WalletCardAssetWithMultipleBalancesProps) => {
|
||||
const balance = 'balance' in props ? props.balance : undefined;
|
||||
const type = 'type' in props ? props.type : undefined;
|
||||
const balances =
|
||||
'balances' in props
|
||||
? props.balances
|
||||
: balance
|
||||
? [{ balance, type }]
|
||||
: undefined;
|
||||
|
||||
const isRedeemable =
|
||||
type === Schema.AccountType.ACCOUNT_TYPE_VESTED_REWARDS && assetId;
|
||||
const values =
|
||||
balances &&
|
||||
balances.length > 0 &&
|
||||
balances
|
||||
.filter((b) => allowZeroBalance || !b.balance.isZero())
|
||||
.sort((a, b) => {
|
||||
const order = [
|
||||
Schema.AccountType.ACCOUNT_TYPE_VESTING_REWARDS,
|
||||
Schema.AccountType.ACCOUNT_TYPE_VESTED_REWARDS,
|
||||
Schema.AccountType.ACCOUNT_TYPE_GENERAL,
|
||||
undefined,
|
||||
];
|
||||
return order.indexOf(a.type) - order.indexOf(b.type);
|
||||
})
|
||||
.map(({ balance, type }, i) => (
|
||||
<CurrencyValue
|
||||
key={i}
|
||||
balance={balance}
|
||||
decimals={decimals}
|
||||
type={type}
|
||||
assetId={assetId}
|
||||
/>
|
||||
));
|
||||
|
||||
const accountTypeTooltip = useMemo(() => {
|
||||
if (type === Schema.AccountType.ACCOUNT_TYPE_VESTED_REWARDS) {
|
||||
return t('VestedRewardsTooltip');
|
||||
}
|
||||
if (type === Schema.AccountType.ACCOUNT_TYPE_VESTING_REWARDS && baseRate) {
|
||||
return t('VestingRewardsTooltip', { baseRate });
|
||||
}
|
||||
|
||||
return null;
|
||||
}, [baseRate, t, type]);
|
||||
if (!values || values.length === 0) return;
|
||||
|
||||
return (
|
||||
<div className="flex flex-nowrap gap-2 mt-2 mb-4">
|
||||
@@ -169,35 +188,92 @@ export const WalletCardAsset = ({
|
||||
{subheading || symbol}
|
||||
</div>
|
||||
</div>
|
||||
{type ? (
|
||||
<div className="mb-[2px] flex gap-2 items-baseline">
|
||||
<Tooltip description={accountTypeTooltip}>
|
||||
<span className="px-2 py-1 leading-none text-xs bg-vega-cdark-700 rounded">
|
||||
{Schema.AccountTypeMapping[type]}
|
||||
</span>
|
||||
</Tooltip>
|
||||
{isRedeemable ? (
|
||||
<Tooltip description={t('RedeemRewardsTooltip')}>
|
||||
<AnchorButton
|
||||
variant="primary"
|
||||
size="xs"
|
||||
href={transferAssetLink(assetId)}
|
||||
target="_blank"
|
||||
className="px-2 py-1 leading-none text-xs bg-vega-yellow text-black rounded"
|
||||
>
|
||||
{t('Redeem')}
|
||||
</AnchorButton>
|
||||
</Tooltip>
|
||||
) : null}
|
||||
</div>
|
||||
) : null}
|
||||
<div className="basis-full font-mono" data-testid="currency-value">
|
||||
<span>
|
||||
{integers}
|
||||
{separator}
|
||||
</span>
|
||||
<span className="text-neutral-400">{decimalsPlaces}</span>
|
||||
{values}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const useAccountTypeTooltip = (type?: Schema.AccountType) => {
|
||||
const { t } = useTranslation();
|
||||
const { param: baseRate } = useNetworkParam('rewards_vesting_baseRate');
|
||||
const accountTypeTooltip = useMemo(() => {
|
||||
if (type === Schema.AccountType.ACCOUNT_TYPE_VESTED_REWARDS) {
|
||||
return t('VestedRewardsTooltip');
|
||||
}
|
||||
if (type === Schema.AccountType.ACCOUNT_TYPE_VESTING_REWARDS && baseRate) {
|
||||
return t('VestingRewardsTooltip', { baseRate });
|
||||
}
|
||||
|
||||
return null;
|
||||
}, [baseRate, t, type]);
|
||||
|
||||
return accountTypeTooltip;
|
||||
};
|
||||
|
||||
const CurrencyValue = ({
|
||||
balance,
|
||||
decimals,
|
||||
type,
|
||||
assetId,
|
||||
}: {
|
||||
balance: BigNumber;
|
||||
decimals: number;
|
||||
type?: Schema.AccountType;
|
||||
assetId?: string;
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const consoleLink = useLinks(DApp.Console);
|
||||
const transferAssetLink = (assetId: string) =>
|
||||
consoleLink(CONSOLE_TRANSFER_ASSET.replace(':assetId', assetId));
|
||||
|
||||
const [integers, decimalsPlaces, separator] = useNumberParts(
|
||||
balance,
|
||||
decimals
|
||||
);
|
||||
const accountTypeTooltip = useAccountTypeTooltip(type);
|
||||
|
||||
const accountType = type && (
|
||||
<Tooltip description={accountTypeTooltip}>
|
||||
<span className="px-2 py-1 leading-none text-xs bg-vega-cdark-700 rounded">
|
||||
{Schema.AccountTypeMapping[type]}
|
||||
</span>
|
||||
</Tooltip>
|
||||
);
|
||||
const isRedeemable =
|
||||
type === Schema.AccountType.ACCOUNT_TYPE_VESTED_REWARDS && assetId;
|
||||
const redeemBtn = isRedeemable ? (
|
||||
<Tooltip description={t('RedeemRewardsTooltip')}>
|
||||
<AnchorButton
|
||||
variant="primary"
|
||||
size="xs"
|
||||
href={transferAssetLink(assetId)}
|
||||
target="_blank"
|
||||
className="px-2 py-1 leading-none text-xs bg-vega-yellow text-black rounded"
|
||||
>
|
||||
{t('Redeem')}
|
||||
</AnchorButton>
|
||||
</Tooltip>
|
||||
) : null;
|
||||
|
||||
return (
|
||||
<div
|
||||
className="basis-full font-mono mb-1"
|
||||
data-account-type={type?.toLowerCase() || 'unspecified'}
|
||||
data-testid="currency-value"
|
||||
>
|
||||
{type && (
|
||||
<div data-type className="flex gap-1">
|
||||
{accountType}
|
||||
{redeemBtn}
|
||||
</div>
|
||||
)}
|
||||
<div data-value>
|
||||
<span>
|
||||
{integers}
|
||||
{separator}
|
||||
</span>
|
||||
<span className="text-neutral-400">{decimalsPlaces}</span>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -9,7 +9,7 @@ import { useWeb3React } from '@web3-react/core';
|
||||
import React from 'react';
|
||||
|
||||
import { SplashLoader } from '../../components/splash-loader';
|
||||
import type { ContractsContextShape } from './contracts-context';
|
||||
import { type ContractsContextShape } from './contracts-context';
|
||||
import { ContractsContext } from './contracts-context';
|
||||
import { createDefaultProvider } from '../../lib/web3-connectors';
|
||||
import { useEthereumConfig } from '@vegaprotocol/web3';
|
||||
|
||||
@@ -1,29 +1,42 @@
|
||||
import type { Module } from 'i18next';
|
||||
import i18n from 'i18next';
|
||||
import HttpBackend from 'i18next-http-backend';
|
||||
import LocizeBackend from 'i18next-locize-backend';
|
||||
import LanguageDetector from 'i18next-browser-languagedetector';
|
||||
import { initReactI18next } from 'react-i18next';
|
||||
|
||||
import dev from './translations/dev.json';
|
||||
const isInDev = process.env.NODE_ENV === 'development';
|
||||
const useLocize = isInDev && !!process.env.NX_USE_LOCIZE;
|
||||
|
||||
const backend = useLocize
|
||||
? {
|
||||
projectId: '96ac1231-4bdd-455a-b9d7-f5322a2e7430',
|
||||
apiKey: process.env.NX_LOCIZE_API_KEY,
|
||||
referenceLng: 'en',
|
||||
}
|
||||
: {
|
||||
loadPath: '/assets/locales/{{lng}}/{{ns}}.json',
|
||||
};
|
||||
|
||||
const Backend: Module = useLocize ? LocizeBackend : HttpBackend;
|
||||
|
||||
i18n
|
||||
.use(Backend)
|
||||
.use(LanguageDetector)
|
||||
.use(initReactI18next)
|
||||
.init({
|
||||
// we init with resources
|
||||
resources: {
|
||||
en: {
|
||||
translations: {
|
||||
...dev,
|
||||
},
|
||||
},
|
||||
},
|
||||
lng: undefined,
|
||||
lng: 'en',
|
||||
fallbackLng: 'en',
|
||||
debug: true,
|
||||
supportedLngs: ['en'],
|
||||
load: 'languageOnly',
|
||||
debug: isInDev,
|
||||
// have a common namespace used around the full app
|
||||
ns: ['translations'],
|
||||
defaultNS: 'translations',
|
||||
ns: ['governance'],
|
||||
defaultNS: 'governance',
|
||||
keySeparator: false, // we use content as keys
|
||||
|
||||
nsSeparator: false,
|
||||
backend,
|
||||
saveMissing: useLocize && !!process.env.NX_LOCIZE_API_KEY,
|
||||
interpolation: {
|
||||
escapeValue: false,
|
||||
},
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { CollateralBridge } from '@vegaprotocol/smart-contracts';
|
||||
import { type CollateralBridge } from '@vegaprotocol/smart-contracts';
|
||||
import * as Schema from '@vegaprotocol/types';
|
||||
import { Button } from '@vegaprotocol/ui-toolkit';
|
||||
import { useBridgeContract, useEthereumTransaction } from '@vegaprotocol/web3';
|
||||
@@ -88,7 +88,7 @@ export const ListAsset = ({
|
||||
assetData.erc20ListAssetBundle;
|
||||
return (
|
||||
<div className="mb-8">
|
||||
<h3 className="text-xl mb-2">{t('ListAsset')}</h3>
|
||||
<h3 className="mb-2 text-xl">{t('ListAsset')}</h3>
|
||||
<p className="pr-8">{t('ListAssetDescription')}</p>
|
||||
<EthWalletContainer>
|
||||
<Button
|
||||
|
||||
+3
-2
@@ -1,7 +1,7 @@
|
||||
import {
|
||||
getProposalDialogIcon,
|
||||
getProposalDialogIntent,
|
||||
getProposalDialogTitle,
|
||||
useGetProposalDialogTitle,
|
||||
} from '@vegaprotocol/proposals';
|
||||
import type { ProposalEventFieldsFragment } from '@vegaprotocol/proposals';
|
||||
import type { DialogProps } from '@vegaprotocol/proposals';
|
||||
@@ -15,6 +15,7 @@ export const ProposalFormTransactionDialog = ({
|
||||
finalizedProposal,
|
||||
TransactionDialog,
|
||||
}: ProposalFormTransactionDialogProps) => {
|
||||
const title = useGetProposalDialogTitle(finalizedProposal?.state);
|
||||
// Render a custom complete UI if the proposal was rejected otherwise
|
||||
// pass undefined so that the default vega transaction dialog UI gets used
|
||||
const completeContent = finalizedProposal?.rejectionReason ? (
|
||||
@@ -24,7 +25,7 @@ export const ProposalFormTransactionDialog = ({
|
||||
return (
|
||||
<div data-testid="proposal-transaction-dialog">
|
||||
<TransactionDialog
|
||||
title={getProposalDialogTitle(finalizedProposal?.state)}
|
||||
title={title}
|
||||
intent={getProposalDialogIntent(finalizedProposal?.state)}
|
||||
icon={getProposalDialogIcon(finalizedProposal?.state)}
|
||||
content={{
|
||||
|
||||
+20
-1
@@ -8,7 +8,7 @@ import {
|
||||
networkParamsQueryMock,
|
||||
nextWeek,
|
||||
} from '../../test-helpers/mocks';
|
||||
import { VoteBreakdown } from './vote-breakdown';
|
||||
import { CompactVotes, VoteBreakdown } from './vote-breakdown';
|
||||
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
|
||||
import type { MockedResponse } from '@apollo/client/testing';
|
||||
import {
|
||||
@@ -346,3 +346,22 @@ describe('VoteBreakdown', () => {
|
||||
expect(style.width).toBe(`${expectedProgress}%`);
|
||||
});
|
||||
});
|
||||
|
||||
describe('CompactVotes', () => {
|
||||
it.each([
|
||||
[0, '0'],
|
||||
[1, '1'],
|
||||
[12, '12'],
|
||||
[123, '123'],
|
||||
[1234, '1.2K'],
|
||||
[12345, '12.3K'],
|
||||
[123456, '123.5K'],
|
||||
[1234567, '1.2M'],
|
||||
[12345678, '12.3M'],
|
||||
[123456789, '123.5M'],
|
||||
[1234567890, '1.2B'],
|
||||
])('compacts %s to %s', (input, output) => {
|
||||
const { getByTestId } = render(<CompactVotes number={BigNumber(input)} />);
|
||||
expect(getByTestId('compact-number').textContent).toBe(output);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -3,11 +3,21 @@ import BigNumber from 'bignumber.js';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useVoteInformation } from '../../hooks';
|
||||
import { Icon, Tooltip } from '@vegaprotocol/ui-toolkit';
|
||||
import { formatNumber, toBigNum } from '@vegaprotocol/utils';
|
||||
import { formatNumber } from '@vegaprotocol/utils';
|
||||
import { ProposalState } from '@vegaprotocol/types';
|
||||
import type { ReactNode } from 'react';
|
||||
import type { ProposalFieldsFragment } from '../../proposals/__generated__/Proposals';
|
||||
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
|
||||
import { CompactNumber } from '@vegaprotocol/react-helpers';
|
||||
|
||||
export const CompactVotes = ({ number }: { number: BigNumber }) => (
|
||||
<CompactNumber
|
||||
number={number}
|
||||
decimals={number.isGreaterThan(1000) ? 1 : 0}
|
||||
compactAbove={1000}
|
||||
compactDisplay="short"
|
||||
/>
|
||||
);
|
||||
|
||||
interface VoteBreakdownProps {
|
||||
proposal: ProposalFieldsFragment | ProposalQuery['proposal'];
|
||||
@@ -198,10 +208,7 @@ export const VoteBreakdown = ({ proposal }: VoteBreakdownProps) => {
|
||||
)}
|
||||
>
|
||||
<button>
|
||||
{yesEquityLikeShareWeight
|
||||
.dividedBy(toBigNum(10 ** 6, 0))
|
||||
.toFixed(1)}
|
||||
M
|
||||
<CompactVotes number={yesEquityLikeShareWeight} />
|
||||
</button>
|
||||
</Tooltip>
|
||||
<span>
|
||||
@@ -226,10 +233,7 @@ export const VoteBreakdown = ({ proposal }: VoteBreakdownProps) => {
|
||||
)}
|
||||
>
|
||||
<button>
|
||||
{noEquityLikeShareWeight
|
||||
.dividedBy(toBigNum(10 ** 6, 0))
|
||||
.toFixed(1)}
|
||||
M
|
||||
<CompactVotes number={noEquityLikeShareWeight} />
|
||||
</button>
|
||||
</Tooltip>
|
||||
<span>
|
||||
@@ -279,10 +283,7 @@ export const VoteBreakdown = ({ proposal }: VoteBreakdownProps) => {
|
||||
)}
|
||||
>
|
||||
<button>
|
||||
{totalEquityLikeShareWeight
|
||||
.dividedBy(toBigNum(10 ** 6, 0))
|
||||
.toFixed(1)}
|
||||
M
|
||||
<CompactVotes number={totalEquityLikeShareWeight} />
|
||||
</button>
|
||||
</Tooltip>
|
||||
<span>
|
||||
@@ -321,7 +322,7 @@ export const VoteBreakdown = ({ proposal }: VoteBreakdownProps) => {
|
||||
<span>{t('tokenVotesFor')}:</span>
|
||||
<Tooltip description={formatNumber(yesTokens, defaultDP)}>
|
||||
<button data-testid="num-votes-for">
|
||||
{yesTokens.dividedBy(toBigNum(10 ** 6, 0)).toFixed(1)}M
|
||||
<CompactVotes number={yesTokens} />
|
||||
</button>
|
||||
</Tooltip>
|
||||
<span>
|
||||
@@ -341,7 +342,7 @@ export const VoteBreakdown = ({ proposal }: VoteBreakdownProps) => {
|
||||
<span>{t('tokenVotesAgainst')}:</span>
|
||||
<Tooltip description={formatNumber(noTokens, defaultDP)}>
|
||||
<button data-testid="num-votes-against">
|
||||
{noTokens.dividedBy(toBigNum(10 ** 6, 0)).toFixed(1)}M
|
||||
<CompactVotes number={noTokens} />
|
||||
</button>
|
||||
</Tooltip>
|
||||
<span>
|
||||
@@ -384,7 +385,7 @@ export const VoteBreakdown = ({ proposal }: VoteBreakdownProps) => {
|
||||
<span>{t('totalTokensVoted')}:</span>
|
||||
<Tooltip description={formatNumber(totalTokensVoted, defaultDP)}>
|
||||
<button data-testid="total-voted">
|
||||
{totalTokensVoted.dividedBy(toBigNum(10 ** 6, 0)).toFixed(1)}M
|
||||
<CompactVotes number={totalTokensVoted} />
|
||||
</button>
|
||||
</Tooltip>
|
||||
<span data-testid="total-voted-percentage">
|
||||
|
||||
@@ -4,7 +4,7 @@ import { VoteValue } from '@vegaprotocol/types';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useUserVoteQuery } from './__generated__/Vote';
|
||||
import { removePaginationWrapper } from '@vegaprotocol/utils';
|
||||
import type { FinalizedVote } from '@vegaprotocol/proposals';
|
||||
import { type FinalizedVote } from '@vegaprotocol/proposals';
|
||||
|
||||
export enum VoteState {
|
||||
NotCast = 'NotCast',
|
||||
|
||||
@@ -5,15 +5,17 @@ import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { SplashLoader } from '../../../components/splash-loader';
|
||||
import { ProposalsList } from '../components/proposals-list';
|
||||
import { useProposalsQuery } from './__generated__/Proposals';
|
||||
import { getNodes, removePaginationWrapper } from '@vegaprotocol/utils';
|
||||
import {
|
||||
ProposalState,
|
||||
ProtocolUpgradeProposalStatus,
|
||||
} from '@vegaprotocol/types';
|
||||
import type { NodeConnection, NodeEdge } from '@vegaprotocol/utils';
|
||||
import type { ProposalFieldsFragment } from './__generated__/Proposals';
|
||||
import type { ProtocolUpgradeProposalFieldsFragment } from '@vegaprotocol/proposals';
|
||||
import { type NodeConnection, type NodeEdge } from '@vegaprotocol/utils';
|
||||
import {
|
||||
useProposalsQuery,
|
||||
type ProposalFieldsFragment,
|
||||
} from './__generated__/Proposals';
|
||||
import { type ProtocolUpgradeProposalFieldsFragment } from '@vegaprotocol/proposals';
|
||||
import { useProtocolUpgradeProposalsQuery } from '@vegaprotocol/proposals';
|
||||
import { FLAGS } from '@vegaprotocol/environment';
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ import {
|
||||
doesValueEquateToParam,
|
||||
} from '@vegaprotocol/proposals';
|
||||
import { useEnvironment, DocsLinks } from '@vegaprotocol/environment';
|
||||
import { validateJson } from '@vegaprotocol/utils';
|
||||
import { useValidateJson } from '@vegaprotocol/utils';
|
||||
import {
|
||||
NetworkParams,
|
||||
useNetworkParams,
|
||||
@@ -41,6 +41,7 @@ export interface NewAssetProposalFormFields {
|
||||
const DOCS_LINK = '/new-asset-proposal';
|
||||
|
||||
export const ProposeNewAsset = () => {
|
||||
const validateJson = useValidateJson();
|
||||
const {
|
||||
params,
|
||||
loading: networkParamsLoading,
|
||||
|
||||
@@ -7,7 +7,7 @@ import {
|
||||
doesValueEquateToParam,
|
||||
} from '@vegaprotocol/proposals';
|
||||
import { useEnvironment, DocsLinks } from '@vegaprotocol/environment';
|
||||
import { validateJson } from '@vegaprotocol/utils';
|
||||
import { useValidateJson } from '@vegaprotocol/utils';
|
||||
import {
|
||||
NetworkParams,
|
||||
useNetworkParams,
|
||||
@@ -39,6 +39,7 @@ export interface NewMarketProposalFormFields {
|
||||
const DOCS_LINK = '/new-market-proposal';
|
||||
|
||||
export const ProposeNewMarket = () => {
|
||||
const validateJson = useValidateJson();
|
||||
const {
|
||||
params,
|
||||
loading: networkParamsLoading,
|
||||
|
||||
@@ -14,7 +14,7 @@ import {
|
||||
RoundedWrapper,
|
||||
TextArea,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import { validateJson } from '@vegaprotocol/utils';
|
||||
import { useValidateJson } from '@vegaprotocol/utils';
|
||||
import {
|
||||
NetworkParams,
|
||||
useNetworkParams,
|
||||
@@ -31,6 +31,7 @@ export interface RawProposalFormFields {
|
||||
}
|
||||
|
||||
export const ProposeRaw = () => {
|
||||
const validateJson = useValidateJson();
|
||||
const {
|
||||
params,
|
||||
loading: networkParamsLoading,
|
||||
|
||||
@@ -7,7 +7,7 @@ import {
|
||||
doesValueEquateToParam,
|
||||
} from '@vegaprotocol/proposals';
|
||||
import { useEnvironment, DocsLinks } from '@vegaprotocol/environment';
|
||||
import { validateJson } from '@vegaprotocol/utils';
|
||||
import { useValidateJson } from '@vegaprotocol/utils';
|
||||
import {
|
||||
NetworkParams,
|
||||
useNetworkParams,
|
||||
@@ -39,6 +39,7 @@ export interface UpdateAssetProposalFormFields {
|
||||
const DOCS_LINK = '/update-asset-proposal';
|
||||
|
||||
export const ProposeUpdateAsset = () => {
|
||||
const validateJson = useValidateJson();
|
||||
const {
|
||||
params,
|
||||
loading: networkParamsLoading,
|
||||
|
||||
+3
-2
@@ -8,7 +8,7 @@ import {
|
||||
useProposalSubmit,
|
||||
} from '@vegaprotocol/proposals';
|
||||
import { useEnvironment, DocsLinks } from '@vegaprotocol/environment';
|
||||
import { validateJson } from '@vegaprotocol/utils';
|
||||
import { useValidateJson } from '@vegaprotocol/utils';
|
||||
import {
|
||||
NetworkParams,
|
||||
useNetworkParams,
|
||||
@@ -53,6 +53,7 @@ export interface UpdateMarketProposalFormFields {
|
||||
const DOCS_LINK = '/update-market-proposal';
|
||||
|
||||
export const ProposeUpdateMarket = () => {
|
||||
const validateJson = useValidateJson();
|
||||
const {
|
||||
params,
|
||||
loading: networkParamsLoading,
|
||||
@@ -260,7 +261,7 @@ export const ProposeUpdateMarket = () => {
|
||||
</FormGroup>
|
||||
|
||||
{selectedMarket && (
|
||||
<div className="mt-[-20px] mb-6">
|
||||
<div className="mb-6 mt-[-20px]">
|
||||
<KeyValueTable data-testid="update-market-details">
|
||||
<KeyValueTableRow>
|
||||
{t('MarketName')}
|
||||
|
||||
@@ -5,9 +5,9 @@ import * as faker from 'faker';
|
||||
import isArray from 'lodash/isArray';
|
||||
import mergeWith from 'lodash/mergeWith';
|
||||
|
||||
import type { PartialDeep } from 'type-fest';
|
||||
import type { ProposalQuery } from '../proposal/__generated__/Proposal';
|
||||
import type { ProtocolUpgradeProposalFieldsFragment } from '@vegaprotocol/proposals';
|
||||
import { type PartialDeep } from 'type-fest';
|
||||
import { type ProposalQuery } from '../proposal/__generated__/Proposal';
|
||||
import { type ProtocolUpgradeProposalFieldsFragment } from '@vegaprotocol/proposals';
|
||||
|
||||
export function generateProtocolUpgradeProposal(
|
||||
override: PartialDeep<ProtocolUpgradeProposalFieldsFragment> = {}
|
||||
|
||||
@@ -22,11 +22,13 @@ interface UserBalances {
|
||||
balance: BigNumber;
|
||||
}
|
||||
|
||||
type Params = { address: string };
|
||||
|
||||
export const RedemptionInformation = () => {
|
||||
const { t } = useTranslation();
|
||||
const navigate = useNavigate();
|
||||
const tranches = useTranches((state) => state.tranches);
|
||||
const { address } = useParams<{ address: string }>();
|
||||
const { address } = useParams<Params>();
|
||||
const [userBalances, setUserBalances] = useState<null | UserBalances>();
|
||||
const getUsersBalances = useGetUserBalances(address);
|
||||
useEffect(() => {
|
||||
@@ -84,7 +86,7 @@ export const RedemptionInformation = () => {
|
||||
i18nKey="noVestingTokens"
|
||||
components={{
|
||||
tranchesLink: (
|
||||
<Link className="underline text-white" to={Routes.SUPPLY} />
|
||||
<Link className="text-white underline" to={Routes.SUPPLY} />
|
||||
),
|
||||
}}
|
||||
/>
|
||||
@@ -160,7 +162,7 @@ export const RedemptionInformation = () => {
|
||||
intent={Intent.Warning}
|
||||
>
|
||||
<p>{t('Find out more about Staking.')}</p>
|
||||
<Link to={Routes.VALIDATORS} className="underline text-white">
|
||||
<Link to={Routes.VALIDATORS} className="text-white underline">
|
||||
{t('Stake VEGA tokens')}
|
||||
</Link>
|
||||
</Callout>
|
||||
|
||||
@@ -21,9 +21,10 @@ import RoutesConfig from '../routes';
|
||||
interface FormFields {
|
||||
address: string;
|
||||
}
|
||||
type Params = { address: string };
|
||||
|
||||
const RedemptionRouter = () => {
|
||||
const { address } = useParams<{ address: string }>();
|
||||
const { address } = useParams<Params>();
|
||||
const navigate = useNavigate();
|
||||
const { t } = useTranslation();
|
||||
const validatePubkey = useCallback(
|
||||
@@ -89,7 +90,7 @@ const RedemptionRouter = () => {
|
||||
{t('View connected Eth Wallet')}
|
||||
</Button>
|
||||
)}
|
||||
<p className="py-4 flex justify-center">{t('OR')}</p>
|
||||
<p className="flex justify-center py-4">{t('OR')}</p>
|
||||
<form
|
||||
onSubmit={handleSubmit(onSubmit)}
|
||||
data-testid="view-connector-form"
|
||||
|
||||
@@ -21,6 +21,8 @@ import { EthConnectPrompt } from '../../../components/eth-connect-prompt';
|
||||
import { useUserTrancheBalances } from '../hooks';
|
||||
import { useAppState } from '../../../contexts/app-state/app-state-context';
|
||||
|
||||
type Params = { id: string };
|
||||
|
||||
export const RedeemFromTranche = () => {
|
||||
const { account: address } = useWeb3React();
|
||||
const { vesting } = useContracts();
|
||||
@@ -34,7 +36,7 @@ export const RedeemFromTranche = () => {
|
||||
tranches: state.tranches,
|
||||
getTranches: state.getTranches,
|
||||
}));
|
||||
const { id } = useParams<{ id: string }>();
|
||||
const { id } = useParams<Params>();
|
||||
const numberId = Number(id);
|
||||
const tranche = React.useMemo(
|
||||
() => tranches?.find(({ tranche_id }) => tranche_id === numberId) || null,
|
||||
@@ -86,7 +88,7 @@ export const RedeemFromTranche = () => {
|
||||
i18nKey="noVestingTokens"
|
||||
components={{
|
||||
tranchesLink: (
|
||||
<Link className="underline text-white" to={Routes.SUPPLY} />
|
||||
<Link className="text-white underline" to={Routes.SUPPLY} />
|
||||
),
|
||||
}}
|
||||
/>
|
||||
@@ -128,13 +130,13 @@ export const RedeemFromTranche = () => {
|
||||
components={{
|
||||
stakingLink: (
|
||||
<Link
|
||||
className="underline text-white"
|
||||
className="text-white underline"
|
||||
to={Routes.VALIDATORS}
|
||||
/>
|
||||
),
|
||||
governanceLink: (
|
||||
<Link
|
||||
className="underline text-white"
|
||||
className="text-white underline"
|
||||
to={Routes.PROPOSALS}
|
||||
/>
|
||||
),
|
||||
|
||||
@@ -11,12 +11,12 @@ import { useTransaction } from '../../../hooks/use-transaction';
|
||||
import { useAppState } from '../../../contexts/app-state/app-state-context';
|
||||
import { removeDecimal, removePaginationWrapper } from '@vegaprotocol/utils';
|
||||
import * as Schema from '@vegaprotocol/types';
|
||||
import type {
|
||||
LinkingsFieldsFragment,
|
||||
PartyStakeLinkingsQuery,
|
||||
PartyStakeLinkingsQueryVariables,
|
||||
import {
|
||||
PartyStakeLinkingsDocument,
|
||||
type LinkingsFieldsFragment,
|
||||
type PartyStakeLinkingsQuery,
|
||||
type PartyStakeLinkingsQueryVariables,
|
||||
} from './__generated__/PartyStakeLinkings';
|
||||
import { PartyStakeLinkingsDocument } from './__generated__/PartyStakeLinkings';
|
||||
|
||||
export const useAddStake = (
|
||||
address: string,
|
||||
|
||||
+7
-7
@@ -26,9 +26,9 @@ import {
|
||||
ValidatorRenderer,
|
||||
VotingPowerRenderer,
|
||||
} from './shared';
|
||||
import type { AgGridReact } from 'ag-grid-react';
|
||||
import type { ColDef, RowHeightParams } from 'ag-grid-community';
|
||||
import type { ValidatorsTableProps } from './shared';
|
||||
import { type AgGridReact } from 'ag-grid-react';
|
||||
import { type ColDef, type RowHeightParams } from 'ag-grid-community';
|
||||
import { type ValidatorsTableProps } from './shared';
|
||||
import {
|
||||
formatNumber,
|
||||
formatNumberPercentage,
|
||||
@@ -83,19 +83,19 @@ const TopThirdCellRenderer = (
|
||||
e.preventDefault();
|
||||
setHideTopThird(false);
|
||||
}}
|
||||
className="grid grid-cols-[60px_1fr] w-full h-full py-4 px-0 text-sm text-white text-center overflow-scroll"
|
||||
className="grid h-full w-full grid-cols-[60px_1fr] overflow-scroll px-0 py-4 text-center text-sm text-white"
|
||||
>
|
||||
<div className="px-3 text-xs text-left">
|
||||
<div className="px-3 text-left text-xs">
|
||||
{params?.data?.rankingDisplay}
|
||||
</div>
|
||||
<div className="px-3 whitespace-normal">
|
||||
<div className="whitespace-normal px-3">
|
||||
<div className="mb-4">
|
||||
<Button
|
||||
data-testid="show-all-validators"
|
||||
rightIcon={
|
||||
<Icon
|
||||
name="arrow-right"
|
||||
className="mr-2 align-text-top fill-current"
|
||||
className="mr-2 fill-current align-text-top"
|
||||
/>
|
||||
}
|
||||
className="inline-flex items-center"
|
||||
|
||||
+3
-3
@@ -22,9 +22,9 @@ import {
|
||||
PendingStakeRenderer,
|
||||
VotingPowerRenderer,
|
||||
} from './shared';
|
||||
import type { AgGridReact } from 'ag-grid-react';
|
||||
import type { ColDef } from 'ag-grid-community';
|
||||
import type { ValidatorsTableProps } from './shared';
|
||||
import { type AgGridReact } from 'ag-grid-react';
|
||||
import { type ColDef } from 'ag-grid-community';
|
||||
import { type ValidatorsTableProps } from './shared';
|
||||
import {
|
||||
formatNumber,
|
||||
formatNumberPercentage,
|
||||
|
||||
@@ -28,12 +28,14 @@ interface StakingNodeProps {
|
||||
previousEpochData?: PreviousEpochQuery;
|
||||
}
|
||||
|
||||
type Params = { node: string };
|
||||
|
||||
export const StakingNode = ({ data, previousEpochData }: StakingNodeProps) => {
|
||||
const { pubKey: vegaKey } = useVegaWallet();
|
||||
const {
|
||||
appState: { decimals },
|
||||
} = useAppState();
|
||||
const { node } = useParams<{ node: string }>();
|
||||
const { node } = useParams<Params>();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const { nodeInfo, currentEpoch, delegations } = React.useMemo(
|
||||
|
||||
@@ -16,10 +16,12 @@ import Routes from '../routes';
|
||||
import { TrancheLabel } from './tranche-label';
|
||||
import { useTranches } from '../../lib/tranches/tranches-store';
|
||||
|
||||
type Params = { trancheId: string; address: string };
|
||||
|
||||
export const Tranche = () => {
|
||||
const tranches = useTranches((state) => state.tranches);
|
||||
const { t } = useTranslation();
|
||||
const { trancheId } = useParams<{ trancheId: string; address: string }>();
|
||||
const { trancheId } = useParams<Params>();
|
||||
const { chainId } = useWeb3React();
|
||||
const tranche = tranches?.find(
|
||||
(tranche) => trancheId && parseInt(trancheId) === tranche.tranche_id
|
||||
@@ -41,7 +43,7 @@ export const Tranche = () => {
|
||||
}
|
||||
/>
|
||||
<div
|
||||
className="flex justify-between gap-x-4 py-2 px-4"
|
||||
className="flex justify-between gap-x-4 px-4 py-2"
|
||||
data-testid="redeemed-tranche-tokens"
|
||||
>
|
||||
<span>{t('alreadyRedeemed')}</span>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
// expect(element).toHaveTextContent(/react/i)
|
||||
// learn more: https://github.com/testing-library/jest-dom
|
||||
import '@testing-library/jest-dom';
|
||||
import dev from './i18n/translations/dev.json';
|
||||
import { locales } from '@vegaprotocol/i18n';
|
||||
import i18n from 'i18next';
|
||||
import { initReactI18next } from 'react-i18next';
|
||||
import ResizeObserver from 'resize-observer-polyfill';
|
||||
@@ -12,16 +12,10 @@ import ResizeObserver from 'resize-observer-polyfill';
|
||||
// en translations
|
||||
i18n.use(initReactI18next).init({
|
||||
// we init with resources
|
||||
resources: {
|
||||
en: {
|
||||
translations: {
|
||||
...dev,
|
||||
},
|
||||
},
|
||||
},
|
||||
resources: locales,
|
||||
fallbackLng: 'en',
|
||||
ns: ['translations'],
|
||||
defaultNS: 'translations',
|
||||
ns: ['governance'],
|
||||
defaultNS: 'governance',
|
||||
});
|
||||
|
||||
global.ResizeObserver = ResizeObserver;
|
||||
|
||||
@@ -2,7 +2,11 @@
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "../../dist/out-tsc",
|
||||
"types": ["node"]
|
||||
"types": [
|
||||
"node",
|
||||
"@nx/react/typings/cssmodule.d.ts",
|
||||
"@nx/react/typings/image.d.ts"
|
||||
]
|
||||
},
|
||||
"files": [
|
||||
"../../node_modules/@nx/react/typings/cssmodule.d.ts",
|
||||
|
||||
@@ -3,7 +3,13 @@
|
||||
"compilerOptions": {
|
||||
"outDir": "../../dist/out-tsc",
|
||||
"module": "commonjs",
|
||||
"types": ["jest", "node", "@testing-library/jest-dom"]
|
||||
"types": [
|
||||
"jest",
|
||||
"node",
|
||||
"@testing-library/jest-dom",
|
||||
"@nx/react/typings/cssmodule.d.ts",
|
||||
"@nx/react/typings/image.d.ts"
|
||||
]
|
||||
},
|
||||
"include": [
|
||||
"**/*.test.ts",
|
||||
|
||||
@@ -3,20 +3,20 @@
|
||||
|
||||
# Netlify Environment Variables
|
||||
# https://www.netlify.com/docs/continuous-deployment/#environment-variables
|
||||
NX_VERSION=$npm_package_version
|
||||
NX_REPOSITORY_URL=$REPOSITORY_URL
|
||||
NX_BRANCH=$BRANCH
|
||||
NX_PULL_REQUEST=$PULL_REQUEST
|
||||
NX_HEAD=$HEAD
|
||||
NX_COMMIT_REF=$COMMIT_REF
|
||||
NX_CONTEXT=$CONTEXT
|
||||
NX_REVIEW_ID=$REVIEW_ID
|
||||
NX_INCOMING_HOOK_TITLE=$INCOMING_HOOK_TITLE
|
||||
NX_INCOMING_HOOK_URL=$INCOMING_HOOK_URL
|
||||
NX_INCOMING_HOOK_BODY=$INCOMING_HOOK_BODY
|
||||
NX_URL=$URL
|
||||
NX_DEPLOY_URL=$DEPLOY_URL
|
||||
NX_DEPLOY_PRIME_URL=$DEPLOY_PRIME_URL
|
||||
NX_VERSION=\$npm_package_version
|
||||
NX_REPOSITORY_URL=\$REPOSITORY_URL
|
||||
NX_BRANCH=\$BRANCH
|
||||
NX_PULL_REQUEST=\$PULL_REQUEST
|
||||
NX_HEAD=\$HEAD
|
||||
NX_COMMIT_REF=\$COMMIT_REF
|
||||
NX_CONTEXT=\$CONTEXT
|
||||
NX_REVIEW_ID=\$REVIEW_ID
|
||||
NX_INCOMING_HOOK_TITLE=\$INCOMING_HOOK_TITLE
|
||||
NX_INCOMING_HOOK_URL=\$INCOMING_HOOK_URL
|
||||
NX_INCOMING_HOOK_BODY=\$INCOMING_HOOK_BODY
|
||||
NX_URL=\$URL
|
||||
NX_DEPLOY_URL=\$DEPLOY_URL
|
||||
NX_DEPLOY_PRIME_URL=\$DEPLOY_PRIME_URL
|
||||
NX_VEGA_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/networks-internal/main/fairground/vegawallet-fairground.toml
|
||||
NX_VEGA_ENV = 'TESTNET'
|
||||
NX_VEGA_URL="https://api.n07.testnet.vega.xyz/graphql"
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user