Compare commits

...
45 changed files with 692 additions and 317 deletions
+3 -3
View File
@@ -5,6 +5,8 @@ on:
branches: branches:
- release/* - release/*
- develop - develop
tags:
- v*
pull_request: pull_request:
types: types:
- opened - opened
@@ -97,15 +99,13 @@ jobs:
- name: See affected apps - name: See affected apps
run: | run: |
echo ">>>> debug" echo ">>>> debug"
echo "NX Version: $nx_version"
echo "NX_BASE: ${{ env.NX_BASE }}" echo "NX_BASE: ${{ env.NX_BASE }}"
echo "NX_HEAD: ${{ env.NX_HEAD }}" echo "NX_HEAD: ${{ env.NX_HEAD }}"
echo ">>>> eof debug" echo ">>>> eof debug"
affected="$(yarn nx print-affected --base=${{ env.NX_BASE }} --head=${{ env.NX_HEAD }} --select=projects)" affected="$(yarn nx print-affected --base=${{ env.NX_BASE }} --head=${{ env.NX_HEAD }} --select=projects)"
echo -n "Affected projects: $affected"
branch_slug="$(echo ${{ github.head_ref || github.ref_name }} | sed -r s/[^a-zA-Z0-9]+/-/g | sed -r s/^-+\|-+$//g | cut -c 1-50 )" branch_slug="$(echo '${{ github.head_ref || github.ref_name }}' | sed -r s/[^a-zA-Z0-9]+/-/g | sed -r s/^-+\|-+$//g | cut -c 1-50 )"
projects_e2e="" projects_e2e=""
preview_governance="not deployed" preview_governance="not deployed"
preview_trading="not deployed" preview_trading="not deployed"
+39 -34
View File
@@ -30,13 +30,21 @@ jobs:
- name: Set up Docker Buildx - name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2 uses: docker/setup-buildx-action@v2
- name: Log in to the Container registry - name: Log in to the Container registry (ghcr)
uses: docker/login-action@v2 uses: docker/login-action@v2
with: with:
registry: ghcr.io registry: ghcr.io
username: ${{ github.actor }} username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }} password: ${{ secrets.GITHUB_TOKEN }}
- name: Log in to the Container registry (docker hub)
uses: docker/login-action@v2
if: ${{ startsWith(github.ref, 'refs/tags/v') }}
with:
# registry: registry.hub.docker.com
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Setup node - name: Setup node
uses: actions/setup-node@v3 uses: actions/setup-node@v3
with: with:
@@ -54,30 +62,24 @@ jobs:
- name: Define variables - name: Define variables
run: | run: |
envName='' envName=''
dockerfile="dist.Dockerfile"
if [[ "${{ github.event_name }}" = "push" ]]; then if [[ "${{ github.event_name }}" = "push" ]]; then
domain="vega.rocks" domain="vega.rocks"
if [[ "${{ github.ref }}" =~ .*release/.* ]]; then if [[ "${{ github.ref }}" =~ .*release/.* ]]; then
envName="$(echo ${{ github.ref }} | rev | cut -d '/' -f 1 | rev)" envName="$(echo ${{ github.ref }} | rev | cut -d '/' -f 1 | rev)"
if [[ "${{ github.ref }}" =~ .*mainnet.* ]]; then if [[ "${{ github.ref }}" =~ .*mainnet.* ]]; then
domain="vega.community" domain="vega.community"
if [[ "${{ matrix.app }}" = "trading" ]]; then
dockerfile="ipfs.Dockerfile"
fi
fi fi
elif [[ "${{ github.ref }}" =~ .*develop$ ]]; then elif [[ "${{ github.ref }}" =~ .*develop$ ]]; then
envName="stagnet1" envName="stagnet1"
elif [[ "${{ matrix.app}}" = "trading" ]] && [[ ${{ startsWith(github.ref, 'refs/tags/v') && 'true' || 'false' }} = "true" ]]; then
envName="mainnet"
fi fi
bucketName="${{ matrix.app }}.${envName}.${domain}" bucketName="${{ matrix.app }}.${envName}.${domain}"
echo BUCKET_NAME=${bucketName} >> $GITHUB_ENV echo BUCKET_NAME=${bucketName} >> $GITHUB_ENV
fi fi
nodeVersion=$(cat .nvmrc | head -n 1)
echo ENV_NAME=${envName} >> $GITHUB_ENV echo ENV_NAME=${envName} >> $GITHUB_ENV
echo NODE_VERSION=${nodeVersion} >> $GITHUB_ENV
echo DOCKERFILE=docker/${dockerfile} >> $GITHUB_ENV
- name: Build local dist - name: Build local dist
if: ${{ env.DOCKERFILE != 'docker/ipfs.Dockerfile' }}
run: | run: |
flags="" flags=""
if [[ ! -z "${{ env.ENV_NAME }}" ]]; then if [[ ! -z "${{ env.ENV_NAME }}" ]]; then
@@ -98,62 +100,59 @@ jobs:
- name: Build and export to local Docker - name: Build and export to local Docker
id: docker_build id: docker_build
if: ${{ github.event_name == 'pull_request' || ( env.DOCKERFILE == 'docker/ipfs.Dockerfile' && github.event_name == 'push' ) }}
uses: docker/build-push-action@v3 uses: docker/build-push-action@v3
with: with:
context: . context: .
file: ${{ env.DOCKERFILE }} file: docker/node-outside-docker.Dockerfile
load: true load: true
build-args: | build-args: |
APP=${{ matrix.app }} APP=${{ matrix.app }}
NODE_VERSION=${{ env.NODE_VERSION }}
ENV_NAME=${{ env.ENV_NAME }} ENV_NAME=${{ env.ENV_NAME }}
tags: | tags: |
ghcr.io/vegaprotocol/frontend/${{ matrix.app }}:local ghcr.io/vegaprotocol/frontend/${{ matrix.app }}:local
- name: Image digest - name: Image digest
if: ${{ github.event_name == 'pull_request' || ( env.DOCKERFILE == 'docker/ipfs.Dockerfile' && github.event_name == 'push' ) }} if: ${{ github.event_name == 'pull_request' }}
run: echo ${{ steps.docker_build.outputs.digest }} run: echo ${{ steps.docker_build.outputs.digest }}
- name: Sanity check docker image - name: Sanity check docker image
if: ${{ github.event_name == 'pull_request' || ( env.DOCKERFILE == 'docker/ipfs.Dockerfile' && github.event_name == 'push' ) }}
run: | run: |
echo "Check ipfs-hash" echo "Check ipfs-hash"
if [[ "${{ env.DOCKERFILE }}" = "docker/ipfs.Dockerfile" ]]; then docker run --rm ghcr.io/vegaprotocol/frontend/${{ matrix.app }}:local cat /ipfs-hash
docker run --rm ghcr.io/vegaprotocol/frontend/${{ matrix.app }}:local cat /ipfs-hash docker run --rm ghcr.io/vegaprotocol/frontend/${{ matrix.app }}:local cat /ipfs-hash > ${{ matirx.app }}-ipfs-hash
fi
echo "List html directory" echo "List html directory"
docker run --rm ghcr.io/vegaprotocol/frontend/${{ matrix.app }}:local sh -c 'apk add --update tree; tree .' docker run --rm ghcr.io/vegaprotocol/frontend/${{ matrix.app }}:local sh -c 'apk add --update tree; tree /usr/share/nginx/html'
- name: Copy dist to local filesystem - name: Publish dist as docker image (ghcr)
if: ${{ env.DOCKERFILE == 'docker/ipfs.Dockerfile' && github.event_name == 'push' }}
run: |
docker create --name=dist ghcr.io/vegaprotocol/frontend/${{ matrix.app }}:local
docker cp dist:/usr/share/nginx/html dist
echo "check local dist files"
tree dist/html
mv dist/html dist-result
- name: Publish dist as docker image
uses: docker/build-push-action@v3 uses: docker/build-push-action@v3
if: ${{ github.event_name == 'pull_request' }} if: ${{ github.event_name == 'pull_request' || (matrix.app == 'trading' && github.event_name == 'push' && !startsWith(github.ref, 'refs/tags/v') ) }}
with: with:
context: . context: .
file: ${{ env.DOCKERFILE }} file: docker/node-outside-docker.Dockerfile
push: true push: true
build-args: | build-args: |
APP=${{ matrix.app }} APP=${{ matrix.app }}
NODE_VERSION=${{ env.NODE_VERSION }}
ENV_NAME=${{ env.ENV_NAME }} ENV_NAME=${{ env.ENV_NAME }}
tags: | tags: |
ghcr.io/vegaprotocol/frontend/${{ matrix.app }}:${{ github.event.pull_request.head.sha || github.sha }} ghcr.io/vegaprotocol/frontend/${{ matrix.app }}:${{ github.event.pull_request.head.sha || github.sha }}
- name: Publish dist as docker image (docker hub)
uses: docker/build-push-action@v3
if: ${{ matrix.app == 'trading' && github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v') }}
with:
context: .
file: docker/node-outside-docker.Dockerfile
push: true
build-args: |
APP=${{ matrix.app }}
ENV_NAME=${{ env.ENV_NAME }}
tags: |
vegaprotocol/${{ matrix.app }}:${{ github.ref_name }}
# bucket creation in github.com/vegaprotocol/terraform//frontend # bucket creation in github.com/vegaprotocol/terraform//frontend
- name: Publish dist to s3 - name: Publish dist to s3
uses: jakejarvis/s3-sync-action@master uses: jakejarvis/s3-sync-action@master
if: ${{ github.event_name == 'push' }} if: ${{ github.event_name == 'push' && !startsWith(github.ref, 'refs/tags/v') }}
with: with:
args: --acl private --follow-symlinks --delete args: --acl private --follow-symlinks --delete
env: env:
@@ -169,3 +168,9 @@ jobs:
with: with:
labels: ${{ matrix.app }}-preview labels: ${{ matrix.app }}-preview
number: ${{ github.event.number }} number: ${{ github.event.number }}
- name: Add ipfs hash to release
uses: softprops/action-gh-release@v1
if: ${{ matrix.app == 'trading' && github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v') }}
with:
files: ${{ matrix.app }}-ipfs-hash
+1
View File
@@ -2,6 +2,7 @@
# compiled output # compiled output
/dist /dist
/dist-result
/tmp /tmp
/out-tsc /out-tsc
/tools/executors/**/*.js /tools/executors/**/*.js
+20
View File
@@ -0,0 +1,20 @@
.PHONY: latest-release
latest-release:
gh release list | head -n 1 | awk '{print $1}'
.PHONY: show-latest-release
show-latest-release:
gh release view `gh release list | head -n 1 | awk '{print $1}'`
.PHONY: recalculate-ipfs
recalculate-ipfs:
echo "ipfs hash inside the image"
docker run --rm ${TAG} cat /ipfs-hash
echo "recalculating ipfs hash"
docker run --rm ${TAG} ipfs add -rw /usr/share/nginx/html
.PHONY: eject-ipfs-hash
unpack:
docker create --name=dist ${TAG}
docker cp dist:/usr/share/nginx/html dist
docker rm dist
+55 -12
View File
@@ -103,25 +103,68 @@ In CI linting, formatting and also run. These checks can be seen in the [CI work
Visit the [Nx Documentation](https://nx.dev/getting-started/intro) to learn more. Visit the [Nx Documentation](https://nx.dev/getting-started/intro) to learn more.
# Docker & Vegacapsule # 🐋 Hosting a console
## Docker To host a console there are two possible build scenarios for running the frontends: nx performed **outside** or **inside** docker build. For specific build instructions follow [build instructions](#build-instructions).
The [Dockerfile](./dockerfiles) for running the frontends is pretty basic, merely building the application with the APP arg that is passed in and serving the application from [nginx](./nginx/nginx.conf). The only complexity that exists is that there is a script which allows the passing of run time environment variables to the containers. See configuration below for how to do this. In order to run a container on port 3000:
You can build any of the containers locally with the following command:
```bash
docker build --dockerfile dockerfiles/Dockerfile.cra . --build-arg APP=[YOUR APP] --tag=[TAG]
```
In order to run a container:
```bash ```bash
docker run -p 3000:80 [TAG] docker run -p 3000:80 [TAG]
``` ```
Images ending with `.dist` are to pack locally created transpiled HTML files into nginx container for non-compatible with yarn architectures like M1 Mac ## Build instructions
The [`docker`](./docker) subfolder has some docker configurations for easily setting up your own hosted version of console either for the web, or ready for pinning on IPFS
### nx build outside the docker
Packaging prepared dist into [`nginx`](https://hub.docker.com/_/nginx)([server configuration](./nginx/nginx.conf)) docker image involves building the application on docker host machine from source.
As a prerequisite you need to perform build of `dist` directory and move its content for specific application to `dist-result` directory. Use following script to do it with a single command:
```bash
./docker/prepare-dist.sh
```
You can build any of the containers locally with the following command:
```bash
docker build --dockerfile docker/node-outside-docker.Dockerfile . --tag=[TAG]
```
### nx build inside the docker
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) example.
```bash
docker build --build-arg APP=[YOUR APP] --build-arg NODE_VERSION=$(cat .nvmrc) --build-arg ENV_NAME=mainnet -t [TAG] -f docker/node-inside-docker.Dockerfile .
```
### Computing ipfs-hash of the build
At the moment this feature is important only for `trading` (console) releases.
Each docker build finishes with hash calculation for dist directory. Resulting hash is added to file named as `/ipfs-hash`. Once docker image is produced you can run following commad to display ipfs-hash:
```bash
make recalculate-ipfs TAG=vegaprotocol/trading:{YOUR_VERSION}
```
**updating hash:** recompiling dist directory (even if there are no changed to source code) results in different hash computed by ipfs command.
### Verifying ipfs-hash of existing current application version
An IPFS CID will be attached to every [release](https://github.com/vegaprotocol/frontend-monorepo/releases). If you are intending to pin an application on IPFS, you can check that your build matches by running the following steps:
1. Show latest release by runnning: `make latest-release`. You need to configure [`gh`](https://cli.github.com/) for this step to work, otherwise please provide release manually from [github](https://github.com/vegaprotocol/frontend-monorepo/releases) or [dockerhub](https://hub.docker.com/r/vegaprotocol/trading)
2. Set RELEASE environment variable to value that you want to validate: `export RELEASE=$(make latest-release)` or `export RELEASE=vXX.XX.XX`
3. Set TAG environment variable to image that you want to validate: `export TAG=vegaprotocol/trading:$RELEASE`
4. Download docker image with the desired release `docker pull $TAG`.
5. Recalculate hash: `make recalculate-ipfs`
6. You should see exactly same hash produced by ipfs command as one placed with the release notes: `make show-latest-release`
7. If you want to extract dist from docker image to your local filesystem you can run following command: `make unpack`
8. Now `dist` directory contains valid application build. **it is not possible to calculate same ipfs hash on files that are result of copy operation**
## Config ## Config
+1
View File
@@ -13,6 +13,7 @@ NX_ETHERSCAN_URL=https://sepolia.etherscan.io
NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json
NX_VEGA_GOVERNANCE_URL=https://stagnet1.governance.vega.xyz NX_VEGA_GOVERNANCE_URL=https://stagnet1.governance.vega.xyz
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.jsoo NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.jsoo
NX_VEGA_NETWORKS='{"TESTNET":"https://explorer.fairground.wtf","MAINNET":"https://explorer.vega.xyz"}'
# App flags # App flags
NX_EXPLORER_ASSETS=1 NX_EXPLORER_ASSETS=1
+1
View File
@@ -1 +1,2 @@
# .env is stagnet1, so there are no overrides required # .env is stagnet1, so there are no overrides required
NX_VEGA_NETWORKS='{"TESTNET":"https://explorer.fairground.wtf","MAINNET":"https://explorer.vega.xyz","STAGNET3":"https://stagnet3.explorer.vega.xyz"}'
+24 -2
View File
@@ -1,9 +1,18 @@
import { NetworkLoader, useInitializeEnv } from '@vegaprotocol/environment'; import {
AppFailure,
NetworkLoader,
NodeGuard,
NodeSwitcherDialog,
useEnvironment,
useInitializeEnv,
useNodeSwitcherStore,
} from '@vegaprotocol/environment';
import { TendermintWebsocketProvider } from './contexts/websocket/tendermint-websocket-provider'; import { TendermintWebsocketProvider } from './contexts/websocket/tendermint-websocket-provider';
import { Loader, Splash } from '@vegaprotocol/ui-toolkit'; import { Loader, Splash } from '@vegaprotocol/ui-toolkit';
import { DEFAULT_CACHE_CONFIG } from '@vegaprotocol/apollo-client'; import { DEFAULT_CACHE_CONFIG } from '@vegaprotocol/apollo-client';
import { RouterProvider } from 'react-router-dom'; import { RouterProvider } from 'react-router-dom';
import { router } from './routes/router-config'; import { router } from './routes/router-config';
import { t } from '@vegaprotocol/i18n';
const splashLoading = ( const splashLoading = (
<Splash> <Splash>
@@ -12,10 +21,23 @@ const splashLoading = (
); );
function App() { function App() {
const { VEGA_URL } = useEnvironment();
const [nodeSwitcherOpen, setNodeSwitcherOpen] = useNodeSwitcherStore(
(store) => [store.dialogOpen, store.setDialogOpen]
);
return ( return (
<TendermintWebsocketProvider> <TendermintWebsocketProvider>
<NetworkLoader cache={DEFAULT_CACHE_CONFIG}> <NetworkLoader cache={DEFAULT_CACHE_CONFIG}>
<RouterProvider router={router} fallbackElement={splashLoading} /> <NodeGuard
skeleton={<div>{t('Loading')}</div>}
failure={<AppFailure title={t(`Node: ${VEGA_URL} is unsuitable`)} />}
>
<RouterProvider router={router} fallbackElement={splashLoading} />
</NodeGuard>
<NodeSwitcherDialog
open={nodeSwitcherOpen}
setOpen={setNodeSwitcherOpen}
/>
</NetworkLoader> </NetworkLoader>
</TendermintWebsocketProvider> </TendermintWebsocketProvider>
); );
@@ -1,13 +1,19 @@
import { NodeSwitcherDialog, useEnvironment } from '@vegaprotocol/environment'; import {
useEnvironment,
useNodeSwitcherStore,
} from '@vegaprotocol/environment';
import { t } from '@vegaprotocol/i18n'; import { t } from '@vegaprotocol/i18n';
import { useScreenDimensions } from '@vegaprotocol/react-helpers'; import { useScreenDimensions } from '@vegaprotocol/react-helpers';
import { ExternalLink, Link } from '@vegaprotocol/ui-toolkit'; import { ExternalLink, Link } from '@vegaprotocol/ui-toolkit';
import { useMemo, useState } from 'react'; import { useMemo } from 'react';
import { ENV } from '../../config/env'; import { ENV } from '../../config/env';
export const Footer = () => { export const Footer = () => {
const { VEGA_URL, GIT_COMMIT_HASH, GIT_ORIGIN_URL } = useEnvironment(); const { VEGA_URL, GIT_COMMIT_HASH, GIT_ORIGIN_URL } = useEnvironment();
const [nodeSwitcherOpen, setNodeSwitcherOpen] = useState(false); const setNodeSwitcherOpen = useNodeSwitcherStore(
(store) => store.setDialogOpen
);
const { screenSize } = useScreenDimensions(); const { screenSize } = useScreenDimensions();
const showFullFeedbackLabel = useMemo( const showFullFeedbackLabel = useMemo(
() => ['md', 'lg', 'xl', 'xxl', 'xxxl'].includes(screenSize), () => ['md', 'lg', 'xl', 'xxl', 'xxxl'].includes(screenSize),
@@ -15,46 +21,40 @@ export const Footer = () => {
); );
return ( return (
<> <footer className="grid grid-rows-2 grid-cols-[1fr_auto] text-xs md:text-md md:flex md:col-span-2 px-4 py-2 gap-4 border-t border-vega-light-200 dark:border-vega-dark-200">
<footer className="grid grid-rows-2 grid-cols-[1fr_auto] text-xs md:text-md md:flex md:col-span-2 px-4 py-2 gap-4 border-t border-vega-light-200 dark:border-vega-dark-200"> <div className="flex justify-between gap-2 align-middle">
<div className="flex justify-between gap-2 align-middle"> {GIT_COMMIT_HASH && (
{GIT_COMMIT_HASH && ( <div className="content-center flex border-r border-neutral-700 dark:border-neutral-300 pr-4">
<div className="content-center flex border-r border-neutral-700 dark:border-neutral-300 pr-4"> <p data-testid="git-commit-hash">
<p data-testid="git-commit-hash"> {t('Version')}:{' '}
{t('Version')}:{' '} <Link
<Link href={
href={ GIT_ORIGIN_URL
GIT_ORIGIN_URL ? `${GIT_ORIGIN_URL}/commit/${GIT_COMMIT_HASH}`
? `${GIT_ORIGIN_URL}/commit/${GIT_COMMIT_HASH}` : undefined
: undefined }
} target={GIT_ORIGIN_URL ? '_blank' : undefined}
target={GIT_ORIGIN_URL ? '_blank' : undefined} >
> {GIT_COMMIT_HASH}
{GIT_COMMIT_HASH} </Link>
</Link> </p>
</p>
</div>
)}
<div className="content-center flex pl-2 md:border-r border-neutral-700 dark:border-neutral-300 pr-4">
{VEGA_URL && <NodeUrl url={VEGA_URL} />}
<Link className="ml-2" onClick={() => setNodeSwitcherOpen(true)}>
{t('Change')}
</Link>
</div> </div>
)}
<div className="flex pl-2 content-center"> <div className="content-center flex pl-2 md:border-r border-neutral-700 dark:border-neutral-300 pr-4">
<ExternalLink href={ENV.addresses.feedback}> {VEGA_URL && <NodeUrl url={VEGA_URL} />}
{showFullFeedbackLabel ? t('Share your feedback') : t('Feedback')} <Link className="ml-2" onClick={() => setNodeSwitcherOpen(true)}>
</ExternalLink> {t('Change')}
</div> </Link>
</div> </div>
</footer>
<NodeSwitcherDialog <div className="flex pl-2 content-center">
open={nodeSwitcherOpen} <ExternalLink href={ENV.addresses.feedback}>
setOpen={setNodeSwitcherOpen} {showFullFeedbackLabel ? t('Share your feedback') : t('Feedback')}
/> </ExternalLink>
</> </div>
</div>
</footer>
); );
}; };
+9 -3
View File
@@ -58,9 +58,15 @@ export const AppLoader = ({ children }: { children: React.ReactElement }) => {
token.decimals(), token.decimals(),
]); ]);
const totalSupply = toBigNum(supply, decimals); const totalSupply = toBigNum(supply.toString(), decimals);
const totalWallet = toBigNum(totalAssociatedWallet, decimals); const totalWallet = toBigNum(
const totalVesting = toBigNum(totalAssociatedVesting, decimals); totalAssociatedWallet.toString(),
decimals
);
const totalVesting = toBigNum(
totalAssociatedVesting.toString(),
decimals
);
appDispatch({ appDispatch({
type: AppStateActionType.SET_TOKEN, type: AppStateActionType.SET_TOKEN,
+37 -14
View File
@@ -37,6 +37,10 @@ import {
useEnvironment, useEnvironment,
NetworkLoader, NetworkLoader,
useInitializeEnv, useInitializeEnv,
NodeGuard,
AppFailure,
NodeSwitcherDialog,
useNodeSwitcherStore,
} from '@vegaprotocol/environment'; } from '@vegaprotocol/environment';
import { ENV } from './config'; import { ENV } from './config';
import type { InMemoryCacheConfig } from '@apollo/client'; import type { InMemoryCacheConfig } from '@apollo/client';
@@ -48,6 +52,7 @@ import {
TELEMETRY_ON, TELEMETRY_ON,
} from './components/telemetry-dialog/telemetry-dialog'; } from './components/telemetry-dialog/telemetry-dialog';
import { useLocalStorage } from '@vegaprotocol/react-helpers'; import { useLocalStorage } from '@vegaprotocol/react-helpers';
import { useTranslation } from 'react-i18next';
const cache: InMemoryCacheConfig = { const cache: InMemoryCacheConfig = {
typePolicies: { typePolicies: {
@@ -181,9 +186,19 @@ const ScrollToTop = () => {
const AppContainer = () => { const AppContainer = () => {
const { config, loading, error } = useEthereumConfig(); const { config, loading, error } = useEthereumConfig();
const { VEGA_ENV, GIT_COMMIT_HASH, GIT_BRANCH, ETHEREUM_PROVIDER_URL } = const {
useEnvironment(); VEGA_ENV,
VEGA_URL,
GIT_COMMIT_HASH,
GIT_BRANCH,
ETHEREUM_PROVIDER_URL,
} = useEnvironment();
const [telemetryOn] = useLocalStorage(TELEMETRY_ON); const [telemetryOn] = useLocalStorage(TELEMETRY_ON);
const { t } = useTranslation();
const [nodeSwitcherOpen, setNodeSwitcher] = useNodeSwitcherStore((store) => [
store.dialogOpen,
store.setDialogOpen,
]);
useEffect(() => { useEffect(() => {
if (ENV.dsn && telemetryOn) { if (ENV.dsn && telemetryOn) {
@@ -219,21 +234,29 @@ const AppContainer = () => {
<ScrollToTop /> <ScrollToTop />
<AppStateProvider> <AppStateProvider>
<div className="grid min-h-full text-white"> <div className="grid min-h-full text-white">
<AsyncRenderer<EthereumConfig | null> <NodeGuard
loading={loading} skeleton={<div>{t('Loading')}</div>}
data={config} failure={
error={error} <AppFailure title={t('NodeUnsuitable', { url: VEGA_URL })} />
render={(cnf) =>
cnf && (
<Web3Container
chainId={Number(cnf.chain_id)}
providerUrl={ETHEREUM_PROVIDER_URL}
/>
)
} }
/> >
<AsyncRenderer<EthereumConfig | null>
loading={loading}
data={config}
error={error}
render={(cnf) =>
cnf && (
<Web3Container
chainId={Number(cnf.chain_id)}
providerUrl={ETHEREUM_PROVIDER_URL}
/>
)
}
/>
</NodeGuard>
</div> </div>
</AppStateProvider> </AppStateProvider>
<NodeSwitcherDialog open={nodeSwitcherOpen} setOpen={setNodeSwitcher} />
</Router> </Router>
); );
}; };
@@ -811,5 +811,6 @@
"OptOutOfTelemetry": "You can opt out any time via settings", "OptOutOfTelemetry": "You can opt out any time via settings",
"NoThanks": "No thanks", "NoThanks": "No thanks",
"ShareData": "Share data", "ShareData": "Share data",
"ContinueSharingData": "Continue sharing data" "ContinueSharingData": "Continue sharing data",
"NodeUnsuitable": "Node: {{url}} is unsuitable"
} }
@@ -34,6 +34,7 @@ import {
formatNumberPercentage, formatNumberPercentage,
toBigNum, toBigNum,
} from '@vegaprotocol/utils'; } from '@vegaprotocol/utils';
import { VALIDATOR_LOGO_MAP } from './logo-map';
interface CanonisedConsensusNodeProps { interface CanonisedConsensusNodeProps {
id: string; id: string;
@@ -131,7 +132,7 @@ export const ConsensusValidatorsTable = ({
const gridRef = useRef<AgGridReact | null>(null); const gridRef = useRef<AgGridReact | null>(null);
const nodes = useMemo(() => { const nodes = useMemo(() => {
if (!data || !previousEpochData) return []; if (!data) return [];
let canonisedNodes = data let canonisedNodes = data
.sort((a, b) => { .sort((a, b) => {
const aVotingPower = new BigNumber(a.rankingScore.votingPower); const aVotingPower = new BigNumber(a.rankingScore.votingPower);
@@ -161,6 +162,11 @@ export const ConsensusValidatorsTable = ({
pendingUserStake, pendingUserStake,
userStakeShare, userStakeShare,
}) => { }) => {
const logo = VALIDATOR_LOGO_MAP[id]
? VALIDATOR_LOGO_MAP[id]
: avatarUrl
? avatarUrl
: null;
const { const {
rawValidatorScore: previousEpochValidatorScore, rawValidatorScore: previousEpochValidatorScore,
performanceScore: previousEpochPerformanceScore, performanceScore: previousEpochPerformanceScore,
@@ -171,7 +177,7 @@ export const ConsensusValidatorsTable = ({
id, id,
[ValidatorFields.RANKING_INDEX]: votingPowerRanking, [ValidatorFields.RANKING_INDEX]: votingPowerRanking,
[ValidatorFields.VALIDATOR]: { [ValidatorFields.VALIDATOR]: {
avatarUrl, avatarUrl: logo,
name, name,
}, },
[ValidatorFields.STAKE]: stakedTotal, [ValidatorFields.STAKE]: stakedTotal,
@@ -0,0 +1,14 @@
export const VALIDATOR_LOGO_MAP: { [key: string]: string } = {
'55504e9bfd914a7bbefa342c82f59a2f4dee344e5b6863a14c02a812f4fbde32':
'https://pbs.twimg.com/profile_images/1586047492629712897/ZVMWBE94_400x400.jpg',
efbdf943443bd7595e83b0d7e88f37b7932d487d1b94aab3d004997273bb43fc:
'https://pbs.twimg.com/profile_images/1026823609979949057/3e-LCHHm_400x400.jpg',
'126751c5830b50d39eb85412fb2964f46338cce6946ff455b73f1b1be3f5e8cc':
'https://pbs.twimg.com/profile_images/1228627868542029824/9aoaLiIx_400x400.jpg',
'43697a3e911d8b70c0ce672adde17a5c38ca8f6a0486bf85ed0546e1b9a82887':
'https://pbs.twimg.com/profile_images/1352167987478843392/XzX82gIb_400x400.jpg',
ac735acc9ab11cf1d8c59c2df2107e00092b4ac96451cb137a1629af5b66242a:
'https://pbs.twimg.com/profile_images/1159254945885016064/vhhp1wL4_400x400.jpg',
'74023df02b8afc9eaf3e3e2e8b07eab1d2122ac3e74b1b0222daf4af565ad3dd':
'https://cdn4.telegram-cdn.org/file/n_OJ6upnglNG95E7u5FyfLmqqBUhf25LqtyZD0UM27gMM_WWi8rywrp9JaAA83quP7vx-WU9Q86quvIRdk9wuUrC2tdD08cYxah4dXHa4OgphFg7Vm5UYFjuGxuHn916OIlzIuVOjJWK_nricGdlPcZQK_T4kBJ_5v4pp94j76_DQ1eSy8DjUxnchZxqpGFv-lGN8U4SjRMK0RC_6DusNaRYEn9Ni2ZQHig7nMiqud-nkK5RgUrClfXFwyhoMFVfhHpGUzwpSv1pSEVLXVh7E1mVmWYrW9hzdQGGrouWCGRqtf_hRa9enS2fv0P9mAB23BcURWuLOVtMdJTF4wfc-w.jpg',
};
@@ -16,6 +16,7 @@ import type { PreviousEpochQuery } from '../../__generated__/PreviousEpoch';
import { useAppState } from '../../../../contexts/app-state/app-state-context'; import { useAppState } from '../../../../contexts/app-state/app-state-context';
import type { StakingDelegationFieldsFragment } from '../../__generated__/Staking'; import type { StakingDelegationFieldsFragment } from '../../__generated__/Staking';
import type { ValidatorsView } from './validator-tables'; import type { ValidatorsView } from './validator-tables';
import { VALIDATOR_LOGO_MAP } from './logo-map';
export enum ValidatorFields { export enum ValidatorFields {
RANKING_INDEX = 'rankingIndex', RANKING_INDEX = 'rankingIndex',
@@ -102,13 +103,18 @@ interface ValidatorRendererProps {
export const ValidatorRenderer = ({ data }: ValidatorRendererProps) => { export const ValidatorRenderer = ({ data }: ValidatorRendererProps) => {
const { t } = useTranslation(); const { t } = useTranslation();
const { avatarUrl, name } = data.validator; const { avatarUrl, name } = data.validator;
const logo = VALIDATOR_LOGO_MAP[data.id]
? VALIDATOR_LOGO_MAP[data.id]
: avatarUrl
? avatarUrl
: null;
return ( return (
<div className="w-[238px] grid grid-cols-[1fr_auto] gap-2 items-center"> <div className="w-[238px] grid grid-cols-[1fr_auto] gap-2 items-center">
<span className="flex overflow-hidden"> <span className="flex overflow-hidden">
{avatarUrl && ( {logo && (
<img <img
className="h-6 w-6 rounded-full mr-2" className="h-6 w-6 rounded-full mr-2"
src={avatarUrl} src={logo}
alt={`Avatar icon for ${name}`} alt={`Avatar icon for ${name}`}
onError={(e) => (e.currentTarget.style.display = 'none')} onError={(e) => (e.currentTarget.style.display = 'none')}
/> />
@@ -78,37 +78,38 @@ export const ValidatorTables = ({
const nextDelegation = delegations?.find( const nextDelegation = delegations?.find(
(d) => d.node.id === validatorId && d.epoch === Number(epochId) + 1 (d) => d.node.id === validatorId && d.epoch === Number(epochId) + 1
); );
if (validator.stakedByOperator !== '0') {
switch (validator.rankingScore?.status) { switch (validator.rankingScore?.status) {
case Schema.ValidatorStatus.VALIDATOR_NODE_STATUS_TENDERMINT: case Schema.ValidatorStatus.VALIDATOR_NODE_STATUS_TENDERMINT:
acc.consensusValidators.push( acc.consensusValidators.push(
addUserDataToValidator( addUserDataToValidator(
validator, validator,
currentDelegation, currentDelegation,
nextDelegation, nextDelegation,
currentUserStakeAvailable currentUserStakeAvailable
) )
); );
break; break;
case Schema.ValidatorStatus.VALIDATOR_NODE_STATUS_ERSATZ: case Schema.ValidatorStatus.VALIDATOR_NODE_STATUS_ERSATZ:
acc.standbyValidators.push( acc.standbyValidators.push(
addUserDataToValidator( addUserDataToValidator(
validator, validator,
currentDelegation, currentDelegation,
nextDelegation, nextDelegation,
currentUserStakeAvailable currentUserStakeAvailable
) )
); );
break; break;
case Schema.ValidatorStatus.VALIDATOR_NODE_STATUS_PENDING: case Schema.ValidatorStatus.VALIDATOR_NODE_STATUS_PENDING:
acc.pendingValidators.push( acc.pendingValidators.push(
addUserDataToValidator( addUserDataToValidator(
validator, validator,
currentDelegation, currentDelegation,
nextDelegation, nextDelegation,
currentUserStakeAvailable currentUserStakeAvailable
) )
); );
}
} }
return acc; return acc;
}, },
+81 -26
View File
@@ -49,7 +49,7 @@
"tranche_end": "2023-05-20T00:00:00.000Z", "tranche_end": "2023-05-20T00:00:00.000Z",
"total_added": "19242.125", "total_added": "19242.125",
"total_removed": "1523.8177488329475", "total_removed": "1523.8177488329475",
"locked_amount": "6891.9471601080244395475", "locked_amount": "6570.0201729841825026125",
"deposits": [ "deposits": [
{ {
"amount": "188", "amount": "188",
@@ -4861,7 +4861,7 @@
"tranche_end": "2023-12-05T00:00:00.000Z", "tranche_end": "2023-12-05T00:00:00.000Z",
"total_added": "86666.297", "total_added": "86666.297",
"total_removed": "0", "total_removed": "0",
"locked_amount": "49802.2753121154213651374", "locked_amount": "49683.1009092296761910951",
"deposits": [ "deposits": [
{ {
"amount": "86666.297", "amount": "86666.297",
@@ -4927,7 +4927,7 @@
"tranche_end": "2023-06-01T00:00:00.000Z", "tranche_end": "2023-06-01T00:00:00.000Z",
"total_added": "2500", "total_added": "2500",
"total_removed": "0", "total_removed": "0",
"locked_amount": "312.4325905575906", "locked_amount": "305.538226241351125",
"deposits": [ "deposits": [
{ {
"amount": "2500", "amount": "2500",
@@ -4960,7 +4960,7 @@
"tranche_end": "2023-11-01T00:00:00.000Z", "tranche_end": "2023-11-01T00:00:00.000Z",
"total_added": "15000.000000000000015", "total_added": "15000.000000000000015",
"total_removed": "0", "total_removed": "0",
"locked_amount": "14327.0455917874395143270455917874395", "locked_amount": "14286.1290383454105142861290383454105",
"deposits": [ "deposits": [
{ {
"amount": "1.5e-14", "amount": "1.5e-14",
@@ -5048,7 +5048,7 @@
"tranche_end": "2023-09-01T00:00:00.000Z", "tranche_end": "2023-09-01T00:00:00.000Z",
"total_added": "17500", "total_added": "17500",
"total_removed": "0", "total_removed": "0",
"locked_amount": "10913.25608896940375", "locked_amount": "10865.52010995370325",
"deposits": [ "deposits": [
{ {
"amount": "12500", "amount": "12500",
@@ -5315,7 +5315,7 @@
"tranche_end": "2023-08-01T00:00:00.000Z", "tranche_end": "2023-08-01T00:00:00.000Z",
"total_added": "37500", "total_added": "37500",
"total_removed": "18077.0118744", "total_removed": "18077.0118744",
"locked_amount": "17350.5026089625535", "locked_amount": "17246.515788060160125",
"deposits": [ "deposits": [
{ {
"amount": "7500", "amount": "7500",
@@ -5734,7 +5734,7 @@
"tranche_end": "2023-12-05T00:00:00.000Z", "tranche_end": "2023-12-05T00:00:00.000Z",
"total_added": "129999.45", "total_added": "129999.45",
"total_removed": "0", "total_removed": "0",
"locked_amount": "49756.837002255673748325", "locked_amount": "49637.7713310174637779075",
"deposits": [ "deposits": [
{ {
"amount": "129999.45", "amount": "129999.45",
@@ -5767,7 +5767,7 @@
"tranche_end": "2024-04-01T00:00:00.000Z", "tranche_end": "2024-04-01T00:00:00.000Z",
"total_added": "54144.7663", "total_added": "54144.7663",
"total_removed": "0", "total_removed": "0",
"locked_amount": "48485.46842731635829207292", "locked_amount": "48411.21764968406449080437",
"deposits": [ "deposits": [
{ {
"amount": "54144.7663", "amount": "54144.7663",
@@ -5800,7 +5800,7 @@
"tranche_end": "2023-09-03T00:00:00.000Z", "tranche_end": "2023-09-03T00:00:00.000Z",
"total_added": "62600", "total_added": "62600",
"total_removed": "0", "total_removed": "0",
"locked_amount": "20022.5830035514954", "locked_amount": "19936.502048452562182",
"deposits": [ "deposits": [
{ {
"amount": "10000", "amount": "10000",
@@ -5993,7 +5993,7 @@
"tranche_end": "2023-09-17T00:00:00.000Z", "tranche_end": "2023-09-17T00:00:00.000Z",
"total_added": "5000", "total_added": "5000",
"total_removed": "0", "total_removed": "0",
"locked_amount": "1791.028665651953", "locked_amount": "1784.15319000507335",
"deposits": [ "deposits": [
{ {
"amount": "5000", "amount": "5000",
@@ -7062,7 +7062,7 @@
"tranche_end": "2023-06-02T00:00:00.000Z", "tranche_end": "2023-06-02T00:00:00.000Z",
"total_added": "1939928.38", "total_added": "1939928.38",
"total_removed": "1709370.7872515768348", "total_removed": "1709370.7872515768348",
"locked_amount": "126202.1342632826843379468", "locked_amount": "123534.5481966065072434858",
"deposits": [ "deposits": [
{ {
"amount": "1852091.69", "amount": "1852091.69",
@@ -40934,7 +40934,7 @@
"tranche_end": "2023-06-05T00:00:00.000Z", "tranche_end": "2023-06-05T00:00:00.000Z",
"total_added": "3732368.4671", "total_added": "3732368.4671",
"total_removed": "715655.108029600523393", "total_removed": "715655.108029600523393",
"locked_amount": "218430.066175628697078272948", "locked_amount": "214330.915145972181008792886",
"deposits": [ "deposits": [
{ {
"amount": "1998.95815", "amount": "1998.95815",
@@ -42326,8 +42326,8 @@
"tranche_start": "2022-06-05T00:00:00.000Z", "tranche_start": "2022-06-05T00:00:00.000Z",
"tranche_end": "2023-12-05T00:00:00.000Z", "tranche_end": "2023-12-05T00:00:00.000Z",
"total_added": "15870102.715470999700000001", "total_added": "15870102.715470999700000001",
"total_removed": "871680.07831804700259352", "total_removed": "872635.89843522227071852",
"locked_amount": "6074226.5757489411253544394273819693295485", "locked_amount": "6059691.24938843042043456221192312067585835",
"deposits": [ "deposits": [
{ {
"amount": "16249.93", "amount": "16249.93",
@@ -42836,6 +42836,11 @@
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b", "user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
"tx": "0x625e0c8fb8d5e2dd7fef0359bb396571f7d8aed05a601ddf0f6e6577edb26a13" "tx": "0x625e0c8fb8d5e2dd7fef0359bb396571f7d8aed05a601ddf0f6e6577edb26a13"
}, },
{
"amount": "955.820117175268125",
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
"tx": "0x4301fcb38d16c1f6aa4a93a18a277a4e77eb3b4565bf98ca6218f6598b78b90b"
},
{ {
"amount": "10150.87581603206683", "amount": "10150.87581603206683",
"user": "0xe3eB4CF43C072658401996b71D43eE26E573562D", "user": "0xe3eB4CF43C072658401996b71D43eE26E573562D",
@@ -45010,6 +45015,12 @@
"tranche_id": 2, "tranche_id": 2,
"tx": "0x625e0c8fb8d5e2dd7fef0359bb396571f7d8aed05a601ddf0f6e6577edb26a13" "tx": "0x625e0c8fb8d5e2dd7fef0359bb396571f7d8aed05a601ddf0f6e6577edb26a13"
}, },
{
"amount": "955.820117175268125",
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
"tranche_id": 2,
"tx": "0x4301fcb38d16c1f6aa4a93a18a277a4e77eb3b4565bf98ca6218f6598b78b90b"
},
{ {
"amount": "913.910324501590625", "amount": "913.910324501590625",
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b", "user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
@@ -46566,8 +46577,8 @@
} }
], ],
"total_tokens": "259998.8875", "total_tokens": "259998.8875",
"withdrawn_tokens": "159590.522104386423875", "withdrawn_tokens": "160546.342221561692",
"remaining_tokens": "100408.365395613576125" "remaining_tokens": "99452.545278438308"
}, },
{ {
"address": "0x89051CAb67Bc7F8CC44F7e270c6EDaf1EC57676c", "address": "0x89051CAb67Bc7F8CC44F7e270c6EDaf1EC57676c",
@@ -48948,7 +48959,7 @@
"tranche_start": "2021-11-05T00:00:00.000Z", "tranche_start": "2021-11-05T00:00:00.000Z",
"tranche_end": "2023-05-05T00:00:00.000Z", "tranche_end": "2023-05-05T00:00:00.000Z",
"total_added": "14597706.0446472999", "total_added": "14597706.0446472999",
"total_removed": "6170228.182342542771392682", "total_removed": "6246751.954791069035514289",
"locked_amount": "0", "locked_amount": "0",
"deposits": [ "deposits": [
{ {
@@ -49158,6 +49169,11 @@
} }
], ],
"withdrawals": [ "withdrawals": [
{
"amount": "76523.772448526264121607",
"user": "0xfc3b2D0b548d3edBb512CeE0Bb79Fb7FaD50AaF3",
"tx": "0x5c4a7a8fbab9c7c5899835a06397ea9f9dfbc434376dd0515482b8ba83944d67"
},
{ {
"amount": "1257.7360413991436375", "amount": "1257.7360413991436375",
"user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b", "user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b",
@@ -56357,6 +56373,12 @@
} }
], ],
"withdrawals": [ "withdrawals": [
{
"amount": "76523.772448526264121607",
"user": "0xfc3b2D0b548d3edBb512CeE0Bb79Fb7FaD50AaF3",
"tranche_id": 3,
"tx": "0x5c4a7a8fbab9c7c5899835a06397ea9f9dfbc434376dd0515482b8ba83944d67"
},
{ {
"amount": "466184.354416277260082874", "amount": "466184.354416277260082874",
"user": "0xfc3b2D0b548d3edBb512CeE0Bb79Fb7FaD50AaF3", "user": "0xfc3b2D0b548d3edBb512CeE0Bb79Fb7FaD50AaF3",
@@ -56371,8 +56393,8 @@
} }
], ],
"total_tokens": "575536.79731735", "total_tokens": "575536.79731735",
"withdrawn_tokens": "499013.024868823735878393", "withdrawn_tokens": "575536.79731735",
"remaining_tokens": "76523.772448526264121607" "remaining_tokens": "0"
}, },
{ {
"address": "0xaAeD573103e3f981867C4cd31c6674DE1c8a13c3", "address": "0xaAeD573103e3f981867C4cd31c6674DE1c8a13c3",
@@ -56864,7 +56886,7 @@
"tranche_start": "2021-10-05T00:00:00.000Z", "tranche_start": "2021-10-05T00:00:00.000Z",
"tranche_end": "2023-04-05T00:00:00.000Z", "tranche_end": "2023-04-05T00:00:00.000Z",
"total_added": "5778205.3912159303", "total_added": "5778205.3912159303",
"total_removed": "3480901.398845846498418769", "total_removed": "3492469.400132244009296769",
"locked_amount": "0", "locked_amount": "0",
"deposits": [ "deposits": [
{ {
@@ -57009,6 +57031,11 @@
} }
], ],
"withdrawals": [ "withdrawals": [
{
"amount": "11568.001286397510878",
"user": "0x759C9ABABA492500c4c730bEB568B5b851Dec2c7",
"tx": "0xd4191eb657ad26e08faddde326514247ae4a88e8aefcc7592590f368010c4046"
},
{ {
"amount": "58898.87608135458795", "amount": "58898.87608135458795",
"user": "0x83b1a48376E045D26420200345414e6b93066396", "user": "0x83b1a48376E045D26420200345414e6b93066396",
@@ -57979,6 +58006,12 @@
} }
], ],
"withdrawals": [ "withdrawals": [
{
"amount": "11568.001286397510878",
"user": "0x759C9ABABA492500c4c730bEB568B5b851Dec2c7",
"tranche_id": 4,
"tx": "0xd4191eb657ad26e08faddde326514247ae4a88e8aefcc7592590f368010c4046"
},
{ {
"amount": "209431.056913602489122", "amount": "209431.056913602489122",
"user": "0x759C9ABABA492500c4c730bEB568B5b851Dec2c7", "user": "0x759C9ABABA492500c4c730bEB568B5b851Dec2c7",
@@ -57987,8 +58020,8 @@
} }
], ],
"total_tokens": "220999.0582", "total_tokens": "220999.0582",
"withdrawn_tokens": "209431.056913602489122", "withdrawn_tokens": "220999.0582",
"remaining_tokens": "11568.001286397510878" "remaining_tokens": "0"
}, },
{ {
"address": "0x9058e12e2F32cB1cD4D3123359963D77786477FC", "address": "0x9058e12e2F32cB1cD4D3123359963D77786477FC",
@@ -59069,8 +59102,8 @@
"tranche_start": "2022-06-05T00:00:00.000Z", "tranche_start": "2022-06-05T00:00:00.000Z",
"tranche_end": "2023-06-05T00:00:00.000Z", "tranche_end": "2023-06-05T00:00:00.000Z",
"total_added": "472355.6199999996", "total_added": "472355.6199999996",
"total_removed": "44479.0535455583416", "total_removed": "44544.1737890903416",
"locked_amount": "34611.49258501772999518148756976", "locked_amount": "33961.958672622378193436739320136",
"deposits": [ "deposits": [
{ {
"amount": "3000", "amount": "3000",
@@ -65694,6 +65727,11 @@
"user": "0x0436E2E4EA467f9958403dc56635404EE1bb1E94", "user": "0x0436E2E4EA467f9958403dc56635404EE1bb1E94",
"tx": "0x97f5b5ec5ac839a8d182b9267b5d052cb70f9a18b648874337e3d5360b21a5fb" "tx": "0x97f5b5ec5ac839a8d182b9267b5d052cb70f9a18b648874337e3d5360b21a5fb"
}, },
{
"amount": "65.120243532",
"user": "0x3F9E884B459a6AaC3f66b72555611AdE68a7F472",
"tx": "0x4697229c7f272bd3fc06ecdc1b15143d28e20083e15f1b3cadb3fc80e80af180"
},
{ {
"amount": "182.662252664", "amount": "182.662252664",
"user": "0xb1169C6daAc76bAcaf0D8f87641Fc38fbabe569F", "user": "0xb1169C6daAc76bAcaf0D8f87641Fc38fbabe569F",
@@ -86287,6 +86325,12 @@
} }
], ],
"withdrawals": [ "withdrawals": [
{
"amount": "65.120243532",
"user": "0x3F9E884B459a6AaC3f66b72555611AdE68a7F472",
"tranche_id": 5,
"tx": "0x4697229c7f272bd3fc06ecdc1b15143d28e20083e15f1b3cadb3fc80e80af180"
},
{ {
"amount": "120.24984145", "amount": "120.24984145",
"user": "0x3F9E884B459a6AaC3f66b72555611AdE68a7F472", "user": "0x3F9E884B459a6AaC3f66b72555611AdE68a7F472",
@@ -86295,8 +86339,8 @@
} }
], ],
"total_tokens": "200", "total_tokens": "200",
"withdrawn_tokens": "120.24984145", "withdrawn_tokens": "185.370084982",
"remaining_tokens": "79.75015855" "remaining_tokens": "14.629915018"
}, },
{ {
"address": "0x8c951C54F9cd08Cf81F770248e835D7A4F491a46", "address": "0x8c951C54F9cd08Cf81F770248e835D7A4F491a46",
@@ -144401,6 +144445,11 @@
} }
], ],
"withdrawals": [ "withdrawals": [
{
"amount": "0",
"user": "0x759C9ABABA492500c4c730bEB568B5b851Dec2c7",
"tx": "0x7b20f640f6947ca553bb2ffb572e316f155781b051b198911b234c6c40e79eaa"
},
{ {
"amount": "0", "amount": "0",
"user": "0x759C9ABABA492500c4c730bEB568B5b851Dec2c7", "user": "0x759C9ABABA492500c4c730bEB568B5b851Dec2c7",
@@ -144696,6 +144745,12 @@
} }
], ],
"withdrawals": [ "withdrawals": [
{
"amount": "0",
"user": "0x759C9ABABA492500c4c730bEB568B5b851Dec2c7",
"tranche_id": 9,
"tx": "0x7b20f640f6947ca553bb2ffb572e316f155781b051b198911b234c6c40e79eaa"
},
{ {
"amount": "0", "amount": "0",
"user": "0x759C9ABABA492500c4c730bEB568B5b851Dec2c7", "user": "0x759C9ABABA492500c4c730bEB568B5b851Dec2c7",
@@ -12,6 +12,7 @@ import {
createMarketsDataFragment, createMarketsDataFragment,
assetQuery, assetQuery,
networkParamsQuery, networkParamsQuery,
nodeGuardQuery,
} from '@vegaprotocol/mock'; } from '@vegaprotocol/mock';
import { import {
addDecimalsFormatNumber, addDecimalsFormatNumber,
@@ -158,6 +159,7 @@ describe('Closed markets', { tags: '@smoke' }, () => {
cy.mockGQL((req) => { cy.mockGQL((req) => {
aliasGQLQuery(req, 'ChainId', chainIdQuery()); aliasGQLQuery(req, 'ChainId', chainIdQuery());
aliasGQLQuery(req, 'Statistics', statisticsQuery()); aliasGQLQuery(req, 'Statistics', statisticsQuery());
aliasGQLQuery(req, 'NodeGuard', nodeGuardQuery());
aliasGQLQuery(req, 'NetworkParams', networkParamsQuery()); aliasGQLQuery(req, 'NetworkParams', networkParamsQuery());
aliasGQLQuery( aliasGQLQuery(
req, req,
+2
View File
@@ -20,6 +20,7 @@ import {
marketsDataQuery, marketsDataQuery,
marketsQuery, marketsQuery,
networkParamsQuery, networkParamsQuery,
nodeGuardQuery,
ordersQuery, ordersQuery,
positionsQuery, positionsQuery,
proposalListQuery, proposalListQuery,
@@ -82,6 +83,7 @@ const mockTradingPage = (
) => { ) => {
aliasGQLQuery(req, 'ChainId', chainIdQuery()); aliasGQLQuery(req, 'ChainId', chainIdQuery());
aliasGQLQuery(req, 'Statistics', statisticsQuery()); aliasGQLQuery(req, 'Statistics', statisticsQuery());
aliasGQLQuery(req, 'NodeGuard', nodeGuardQuery());
aliasGQLQuery( aliasGQLQuery(
req, req,
'Markets', 'Markets',
@@ -1,5 +1,6 @@
import type { InMemoryCacheConfig } from '@apollo/client'; import type { InMemoryCacheConfig } from '@apollo/client';
import { import {
AppFailure,
NetworkLoader, NetworkLoader,
NodeGuard, NodeGuard,
useEnvironment, useEnvironment,
@@ -9,7 +10,6 @@ import { MaintenancePage } from '@vegaprotocol/ui-toolkit';
import { VegaWalletProvider } from '@vegaprotocol/wallet'; import { VegaWalletProvider } from '@vegaprotocol/wallet';
import dynamic from 'next/dynamic'; import dynamic from 'next/dynamic';
import type { ReactNode } from 'react'; import type { ReactNode } from 'react';
import { AppFailure } from './app-failure';
import { Web3Provider } from './web3-provider'; import { Web3Provider } from './web3-provider';
export const DynamicLoader = dynamic(() => import('../preloader/preloader'), { export const DynamicLoader = dynamic(() => import('../preloader/preloader'), {
@@ -1,3 +1,2 @@
export * from './app-failure';
export * from './app-loader'; export * from './app-loader';
export * from './web3-provider'; export * from './web3-provider';
@@ -4,18 +4,15 @@ import { NodeHealth, NodeUrl, HealthIndicator } from './footer';
import { MockedProvider } from '@apollo/client/testing'; import { MockedProvider } from '@apollo/client/testing';
import { Intent } from '@vegaprotocol/ui-toolkit'; import { Intent } from '@vegaprotocol/ui-toolkit';
const mockSetNodeSwitcher = jest.fn();
jest.mock('@vegaprotocol/environment', () => ({ jest.mock('@vegaprotocol/environment', () => ({
...jest.requireActual('@vegaprotocol/environment'), ...jest.requireActual('@vegaprotocol/environment'),
useEnvironment: jest.fn().mockImplementation(() => ({ useEnvironment: jest.fn().mockImplementation(() => ({
VEGA_URL: 'https://vega-url.wtf', VEGA_URL: 'https://vega-url.wtf',
VEGA_INCIDENT_URL: 'https://blog.vega.community', VEGA_INCIDENT_URL: 'https://blog.vega.community',
})), })),
})); useNodeSwitcherStore: jest.fn(() => mockSetNodeSwitcher),
const mockSetNodeSwitcher = jest.fn();
jest.mock('../../stores', () => ({
...jest.requireActual('../../stores'),
useGlobalStore: () => mockSetNodeSwitcher,
})); }));
describe('NodeHealth', () => { describe('NodeHealth', () => {
+6 -5
View File
@@ -1,11 +1,14 @@
import { useCallback } from 'react'; import { useCallback } from 'react';
import { useEnvironment, useNodeHealth } from '@vegaprotocol/environment'; import {
useEnvironment,
useNodeHealth,
useNodeSwitcherStore,
} from '@vegaprotocol/environment';
import { t } from '@vegaprotocol/i18n'; import { t } from '@vegaprotocol/i18n';
import type { Intent } from '@vegaprotocol/ui-toolkit'; import type { Intent } from '@vegaprotocol/ui-toolkit';
import { Indicator, ExternalLink } from '@vegaprotocol/ui-toolkit'; import { Indicator, ExternalLink } from '@vegaprotocol/ui-toolkit';
import classNames from 'classnames'; import classNames from 'classnames';
import type { ButtonHTMLAttributes, ReactNode } from 'react'; import type { ButtonHTMLAttributes, ReactNode } from 'react';
import { useGlobalStore } from '../../stores';
export const Footer = () => { export const Footer = () => {
return ( return (
@@ -20,9 +23,7 @@ export const Footer = () => {
export const NodeHealth = () => { export const NodeHealth = () => {
const { VEGA_URL, VEGA_INCIDENT_URL } = useEnvironment(); const { VEGA_URL, VEGA_INCIDENT_URL } = useEnvironment();
const setNodeSwitcher = useGlobalStore( const setNodeSwitcher = useNodeSwitcherStore((store) => store.setDialogOpen);
(store) => (open: boolean) => store.update({ nodeSwitcherDialog: open })
);
const { datanodeBlockHeight, text, intent } = useNodeHealth(); const { datanodeBlockHeight, text, intent } = useNodeHealth();
const onClick = useCallback(() => { const onClick = useCallback(() => {
setNodeSwitcher(true); setNodeSwitcher(true);
@@ -7,7 +7,13 @@ import {
} from '@vegaprotocol/ui-toolkit'; } from '@vegaprotocol/ui-toolkit';
import { RISK_ACCEPTED_KEY } from '../constants'; import { RISK_ACCEPTED_KEY } from '../constants';
import { TelemetryApproval } from './telemetry-approval'; import { TelemetryApproval } from './telemetry-approval';
import { Networks, useEnvironment } from '@vegaprotocol/environment'; import {
DOCS_VEGA_WALLET,
GET_VEGA_WALLET_URL,
Networks,
useDocsLink,
useEnvironment,
} from '@vegaprotocol/environment';
import { useLocalStorage } from '@vegaprotocol/react-helpers'; import { useLocalStorage } from '@vegaprotocol/react-helpers';
interface Props { interface Props {
@@ -46,8 +52,9 @@ export const RiskNoticeDialog = ({ onClose, network }: Props) => {
}; };
const TestnetContent = ({ network }: { network: Networks }) => { const TestnetContent = ({ network }: { network: Networks }) => {
const { VEGA_DOCS_URL, GITHUB_FEEDBACK_URL, VEGA_WALLET_URL } = const { GITHUB_FEEDBACK_URL } = useEnvironment();
useEnvironment(); const docsLink = useDocsLink();
return ( return (
<> <>
<p className="mb-4"> <p className="mb-4">
@@ -62,17 +69,17 @@ const TestnetContent = ({ network }: { network: Networks }) => {
[network] [network]
)} )}
</p> </p>
{VEGA_DOCS_URL && GITHUB_FEEDBACK_URL && VEGA_WALLET_URL && ( {GITHUB_FEEDBACK_URL && GET_VEGA_WALLET_URL && docsLink && (
<ul className="list-disc pl-4"> <ul className="list-disc pl-4">
<li className="mb-1"> <li className="mb-1">
<Link href={VEGA_WALLET_URL} target="_blank"> <Link href={GET_VEGA_WALLET_URL} target="_blank">
<span className="underline">{t('Learn about Vega wallet')}</span>{' '} <span className="underline">{t('Get a Vega Wallet')}</span>{' '}
<VegaIcon name={VegaIconNames.OPEN_EXTERNAL} /> <VegaIcon name={VegaIconNames.OPEN_EXTERNAL} />
</Link> </Link>
</li> </li>
<li className="mb-1"> <li className="mb-1">
<Link href={VEGA_DOCS_URL} target="_blank"> <Link href={docsLink(DOCS_VEGA_WALLET)} target="_blank">
<span className="underline">{t('View documentation')}</span>{' '} <span className="underline">{t('Learn about Vega Wallet')}</span>{' '}
<VegaIcon name={VegaIconNames.OPEN_EXTERNAL} /> <VegaIcon name={VegaIconNames.OPEN_EXTERNAL} />
</Link> </Link>
</li> </li>
+6 -6
View File
@@ -21,9 +21,10 @@ import {
NodeSwitcherDialog, NodeSwitcherDialog,
useEnvironment, useEnvironment,
useInitializeEnv, useInitializeEnv,
useNodeSwitcherStore,
} from '@vegaprotocol/environment'; } from '@vegaprotocol/environment';
import './styles.css'; import './styles.css';
import { useGlobalStore, usePageTitleStore } from '../stores'; import { usePageTitleStore } from '../stores';
import { Footer } from '../components/footer'; import { Footer } from '../components/footer';
import DialogsContainer from './dialogs-container'; import DialogsContainer from './dialogs-container';
import ToastsManager from './toasts-manager'; import ToastsManager from './toasts-manager';
@@ -115,11 +116,10 @@ function AppBody({ Component }: AppProps) {
function VegaTradingApp(props: AppProps) { function VegaTradingApp(props: AppProps) {
const status = useEnvironment((store) => store.status); const status = useEnvironment((store) => store.status);
const { nodeSwitcherOpen, setNodeSwitcher } = useGlobalStore((store) => ({ const [nodeSwitcherOpen, setNodeSwitcher] = useNodeSwitcherStore((store) => [
nodeSwitcherOpen: store.nodeSwitcherDialog, store.dialogOpen,
setNodeSwitcher: (open: boolean) => store.setDialogOpen,
store.update({ nodeSwitcherDialog: open }), ]);
}));
useInitializeEnv(); useInitializeEnv();
-2
View File
@@ -3,7 +3,6 @@ import { create } from 'zustand';
import produce from 'immer'; import produce from 'immer';
interface GlobalStore { interface GlobalStore {
nodeSwitcherDialog: boolean;
marketId: string | null; marketId: string | null;
update: (store: Partial<Omit<GlobalStore, 'update'>>) => void; update: (store: Partial<Omit<GlobalStore, 'update'>>) => void;
shouldDisplayWelcomeDialog: boolean; shouldDisplayWelcomeDialog: boolean;
@@ -15,7 +14,6 @@ interface PageTitleStore {
} }
export const useGlobalStore = create<GlobalStore>()((set) => ({ export const useGlobalStore = create<GlobalStore>()((set) => ({
nodeSwitcherDialog: false,
marketId: LocalStorage.getItem('marketId') || null, marketId: LocalStorage.getItem('marketId') || null,
shouldDisplayWelcomeDialog: false, shouldDisplayWelcomeDialog: false,
update: (newState) => { update: (newState) => {
+31
View File
@@ -0,0 +1,31 @@
# Build container
ARG NODE_VERSION
FROM --platform=amd64 node:${NODE_VERSION}-alpine3.16 as build
WORKDIR /app
# Argument to allow building of different apps
ARG APP
ARG ENV_NAME=""
RUN apk add --update --no-cache \
python3==3.10.11-r0 \
make==4.3-r0 \
gcc==11.2.1_git20220219-r2 \
g++==11.2.1_git20220219-r2
COPY . ./
RUN yarn --network-timeout 100000 --pure-lockfile
# work around for different build process in trading
RUN sh docker/docker-build.sh
# Server environment
# if this fails you need to docker pull nginx:1.23-alpine and pin new SHA
# this is to ensure that we run always same version of alpine to make sure ipfs is indempotent
FROM --platform=amd64 nginx:1.23-alpine@sha256:6318314189b40e73145a48060bff4783a116c34cc7241532d0d94198fb2c9629
# configuration of system
EXPOSE 80
# Copy dist
COPY docker/nginx.conf /etc/nginx/conf.d/default.conf
RUN rm -rf /usr/share/nginx/html/*
COPY --from=build /app/dist/apps/${APP}/* /usr/share/nginx/html
RUN apk add --no-cache go-ipfs==0.16.0-r6 \
&& ipfs init \
&& echo "$(ipfs add -rwQ /usr/share/nginx/html)" > /ipfs-hash \
&& echo "ipfs hash of this build: $(cat /ipfs-hash)"
+10
View File
@@ -0,0 +1,10 @@
FROM --platform=amd64 nginx:1.23-alpine@sha256:6318314189b40e73145a48060bff4783a116c34cc7241532d0d94198fb2c9629
EXPOSE 80
COPY docker/nginx.conf /etc/nginx/conf.d/default.conf
RUN rm -rf /usr/share/nginx/html/*
COPY ./dist-result/ /usr/share/nginx/html/
RUN apk add --no-cache go-ipfs==0.16.0-r6 \
&& ipfs init \
&& echo "$(ipfs add -rwQ /usr/share/nginx/html)" > /ipfs-hash \
&& echo "ipfs hash of this build: $(cat /ipfs-hash)"
+13
View File
@@ -0,0 +1,13 @@
#!/bin/bash -e
yarn --pure-lockfile
app={$1:-trading}
flags="--env=${$2:-mainnet}"
yarn install
if [ "${app}" = "trading" ]; then
yarn nx export trading $flags
DIST_LOCATION=dist/apps/trading/exported
else
yarn nx build ${app} $flags
DIST_LOCATION=dist/apps/${app}
fi
cp -r $DIST_LOCATION dist-result
+1
View File
@@ -7,6 +7,7 @@ export * from '../candles-chart/src/lib/chart.mock';
export * from '../deal-ticket/src/hooks/estimate-order.mock'; export * from '../deal-ticket/src/hooks/estimate-order.mock';
export * from '../deposits/src/lib/deposit.mock'; export * from '../deposits/src/lib/deposit.mock';
export * from '../environment/src/utils/node.mock'; export * from '../environment/src/utils/node.mock';
export * from '../environment/src/components/node-guard/node-guard.mock';
export * from '../fills/src/lib/fills.mock'; export * from '../fills/src/lib/fills.mock';
export * from '../proposals/src/lib/proposals-data-provider/proposals.mock'; export * from '../proposals/src/lib/proposals-data-provider/proposals.mock';
export * from '../ledger/src/lib/ledger-entries.mock'; export * from '../ledger/src/lib/ledger-entries.mock';
@@ -1,6 +1,6 @@
import { t } from '@vegaprotocol/i18n'; import { t } from '@vegaprotocol/i18n';
import { Button } from '@vegaprotocol/ui-toolkit'; import { Button } from '@vegaprotocol/ui-toolkit';
import { useGlobalStore } from '../../stores'; import { useNodeSwitcherStore } from '../../hooks/use-node-switcher-store';
export const AppFailure = ({ export const AppFailure = ({
title, title,
@@ -9,17 +9,13 @@ export const AppFailure = ({
title: string; title: string;
error?: string | null; error?: string | null;
}) => { }) => {
const { setNodeSwitcher } = useGlobalStore((store) => ({ const setNodeSwitcher = useNodeSwitcherStore((store) => store.setDialogOpen);
nodeSwitcherOpen: store.nodeSwitcherDialog,
setNodeSwitcher: (open: boolean) =>
store.update({ nodeSwitcherDialog: open }),
}));
const nonIdealWrapperClasses = const nonIdealWrapperClasses =
'h-full min-h-screen flex items-center justify-center'; 'h-full min-h-screen flex items-center justify-center';
return ( return (
<div className={nonIdealWrapperClasses}> <div className={nonIdealWrapperClasses}>
<div className="text-center"> <div className="text-center">
<h1 className="text-xl mb-4">{title}</h1> <p className="text-xl mb-4">{title}</p>
{error && <p className="text-sm mb-8">{error}</p>} {error && <p className="text-sm mb-8">{error}</p>}
<Button onClick={() => setNodeSwitcher(true)}> <Button onClick={() => setNodeSwitcher(true)}>
{t('Change node')} {t('Change node')}
@@ -0,0 +1 @@
export * from './app-failure';
+1
View File
@@ -1,3 +1,4 @@
export * from './app-failure';
export * from './network-loader'; export * from './network-loader';
export * from './network-switcher'; export * from './network-switcher';
export * from './node-guard'; export * from './node-guard';
@@ -0,0 +1,11 @@
query NodeGuard {
lastBlockHeight
networkParametersConnection {
edges {
node {
key
value
}
}
}
}
@@ -0,0 +1,51 @@
import * as Types from '@vegaprotocol/types';
import { gql } from '@apollo/client';
import * as Apollo from '@apollo/client';
const defaultOptions = {} as const;
export type NodeGuardQueryVariables = Types.Exact<{ [key: string]: never; }>;
export type NodeGuardQuery = { __typename?: 'Query', lastBlockHeight: string, networkParametersConnection: { __typename?: 'NetworkParametersConnection', edges?: Array<{ __typename?: 'NetworkParameterEdge', node: { __typename?: 'NetworkParameter', key: string, value: string } } | null> | null } };
export const NodeGuardDocument = gql`
query NodeGuard {
lastBlockHeight
networkParametersConnection {
edges {
node {
key
value
}
}
}
}
`;
/**
* __useNodeGuardQuery__
*
* To run a query within a React component, call `useNodeGuardQuery` and pass it any options that fit your needs.
* When your component renders, `useNodeGuardQuery` returns an object from Apollo Client that contains loading, error, and data properties
* you can use to render your UI.
*
* @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
*
* @example
* const { data, loading, error } = useNodeGuardQuery({
* variables: {
* },
* });
*/
export function useNodeGuardQuery(baseOptions?: Apollo.QueryHookOptions<NodeGuardQuery, NodeGuardQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useQuery<NodeGuardQuery, NodeGuardQueryVariables>(NodeGuardDocument, options);
}
export function useNodeGuardLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<NodeGuardQuery, NodeGuardQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useLazyQuery<NodeGuardQuery, NodeGuardQueryVariables>(NodeGuardDocument, options);
}
export type NodeGuardQueryHookResult = ReturnType<typeof useNodeGuardQuery>;
export type NodeGuardLazyQueryHookResult = ReturnType<typeof useNodeGuardLazyQuery>;
export type NodeGuardQueryResult = Apollo.QueryResult<NodeGuardQuery, NodeGuardQueryVariables>;
@@ -0,0 +1,53 @@
import type { NodeGuardQuery } from './__generated__/NodeGuard';
import merge from 'lodash/merge';
import type { PartialDeep } from 'type-fest';
export const nodeGuardQuery = (
override?: PartialDeep<NodeGuardQuery>
): NodeGuardQuery => {
const defaultResult: NodeGuardQuery = {
lastBlockHeight: '11',
networkParametersConnection: {
__typename: 'NetworkParametersConnection',
edges: [
{
__typename: 'NetworkParameterEdge',
node: {
__typename: 'NetworkParameter' as const,
key: 'governance.proposal.market.requiredMajority',
value: '0.66',
},
},
{
__typename: 'NetworkParameterEdge',
node: {
__typename: 'NetworkParameter' as const,
key: 'blockchains.ethereumConfig',
value: JSON.stringify({
network_id: '3',
chain_id: '3',
collateral_bridge_contract: {
address: '0x7fe27d970bc8Afc3B11Cc8d9737bfB66B1efd799',
},
multisig_control_contract: {
address: '0x6eBc32d66277D94DB8FF2ccF86E36f37F29a52D3',
deployment_block_height: 12341882,
},
staking_bridge_contract: {
address: '0xFFb0A0d4806502ceF491aF1141f66669A1Bd0D03',
deployment_block_height: 11177313,
},
token_vesting_contract: {
address: '0x680fF88252FA7071CAce7398e77872d54D781d0B',
deployment_block_height: 11177353,
},
confirmations: 3,
}),
},
},
],
},
};
return merge(defaultResult, override);
};
@@ -1,5 +1,5 @@
import type { ReactNode } from 'react'; import type { ReactNode } from 'react';
import { useStatisticsQuery } from '../../utils/__generated__/Node'; import { useNodeGuardQuery } from './__generated__/NodeGuard';
export const NodeGuard = ({ export const NodeGuard = ({
children, children,
@@ -10,14 +10,19 @@ export const NodeGuard = ({
failure: ReactNode; failure: ReactNode;
skeleton: ReactNode; skeleton: ReactNode;
}) => { }) => {
const { error, loading } = useStatisticsQuery(); const { data, error, loading } = useNodeGuardQuery();
const wrapperClasses = 'h-full min-h-screen flex items-center justify-center'; const wrapperClasses =
'h-full min-h-screen flex items-center justify-center text-black dark:text-white';
if (loading) { if (loading) {
return <div className={wrapperClasses}>{skeleton}</div>; return <div className={wrapperClasses}>{skeleton}</div>;
} }
if (error) { // It is possible for nodes to have a functioning datanode, but not return
// any net params. The app cannot safely function without net params
const netParamEdges = data?.networkParametersConnection.edges;
if (error || !netParamEdges || !netParamEdges.length) {
return <div className={wrapperClasses}>{failure}</div>; return <div className={wrapperClasses}>{failure}</div>;
} }
@@ -96,13 +96,13 @@ describe('RowData', () => {
mockHeaders(props.url); mockHeaders(props.url);
render(renderComponent(props, statsQueryMock, subMock)); render(renderComponent(props, statsQueryMock, subMock));
// radio should be disabled until query resolves // radio should be enabled until query resolves
expect( expect(
screen.getByRole('radio', { screen.getByRole('radio', {
checked: false, checked: false,
name: props.url, name: props.url,
}) })
).toBeDisabled(); ).toBeEnabled();
expect(screen.getByTestId('response-time-cell')).toHaveTextContent( expect(screen.getByTestId('response-time-cell')).toHaveTextContent(
'Checking' 'Checking'
); );
@@ -124,7 +124,7 @@ describe('RowData', () => {
}); });
}); });
it('radio button disabled if query fails', async () => { it('radio button still enabled if query fails', async () => {
mockHeaders(props.url, {}); mockHeaders(props.url, {});
const failedQueryMock: MockedResponse<StatisticsQuery> = { const failedQueryMock: MockedResponse<StatisticsQuery> = {
@@ -149,7 +149,7 @@ describe('RowData', () => {
checked: false, checked: false,
name: props.url, name: props.url,
}) })
).toBeDisabled(); ).toBeEnabled();
expect(screen.getByTestId('response-time-cell')).toHaveTextContent( expect(screen.getByTestId('response-time-cell')).toHaveTextContent(
'Checking' 'Checking'
); );
@@ -170,7 +170,7 @@ describe('RowData', () => {
checked: false, checked: false,
name: props.url, name: props.url,
}) })
).toBeDisabled(); ).toBeEnabled();
}); });
}); });
@@ -207,19 +207,6 @@ describe('RowData', () => {
expect(screen.getByTestId('block-height-cell')).toHaveClass('text-danger'); expect(screen.getByTestId('block-height-cell')).toHaveClass('text-danger');
}); });
it('disables radio button if url is invalid', () => {
mockHeaders(props.url, { blockHeight: 100 });
render(renderComponent(props, statsQueryMock, subMock));
expect(
screen.getByRole('radio', {
checked: false,
name: props.url,
})
).toBeDisabled();
});
it('doesnt render the radio if its the custom row', () => { it('doesnt render the radio if its the custom row', () => {
render( render(
renderComponent( renderComponent(
@@ -124,29 +124,6 @@ export const RowData = ({
return false; return false;
}; };
const getIsNodeDisabled = () => {
if (!isValidUrl(url)) {
return true;
}
// if still waiting or query errored disable node
if (loading || error) {
return true;
}
if (subLoading || subError) {
return true;
}
// if we are still waiting for a header entry for this
// url disable the node
if (!headers) {
return true;
}
return false;
};
const getSubFailed = ( const getSubFailed = (
subError: ApolloError | undefined, subError: ApolloError | undefined,
subFailed: boolean subFailed: boolean
@@ -160,12 +137,7 @@ export const RowData = ({
<> <>
{id !== CUSTOM_NODE_KEY && ( {id !== CUSTOM_NODE_KEY && (
<div className="break-all" data-testid="node"> <div className="break-all" data-testid="node">
<Radio <Radio id={`node-url-${id}`} value={url} label={url} />
id={`node-url-${id}`}
value={url}
label={url}
disabled={getIsNodeDisabled()}
/>
</div> </div>
)} )}
<LayoutCell <LayoutCell
+1
View File
@@ -1,5 +1,6 @@
export * from './use-environment'; export * from './use-environment';
export * from './use-links'; export * from './use-links';
export * from './use-node-health'; export * from './use-node-health';
export * from './use-node-switcher-store';
export * from './use-vega-releases'; export * from './use-vega-releases';
export * from './use-vega-release'; export * from './use-vega-release';
@@ -15,6 +15,7 @@ import { Networks } from '../types';
import { compileErrors } from '../utils/compile-errors'; import { compileErrors } from '../utils/compile-errors';
import { envSchema } from '../utils/validate-environment'; import { envSchema } from '../utils/validate-environment';
import { tomlConfigSchema } from '../utils/validate-configuration'; import { tomlConfigSchema } from '../utils/validate-configuration';
import uniq from 'lodash/uniq';
type Client = ReturnType<typeof createClient>; type Client = ReturnType<typeof createClient>;
type ClientCollection = { type ClientCollection = {
@@ -32,7 +33,8 @@ type Actions = {
export type Env = Environment & EnvState; export type Env = Environment & EnvState;
export type EnvStore = Env & Actions; export type EnvStore = Env & Actions;
export const STORAGE_KEY = 'vega_url'; const VERSION = 1;
export const STORAGE_KEY = `vega_url_${VERSION}`;
const SUBSCRIPTION_TIMEOUT = 3000; const SUBSCRIPTION_TIMEOUT = 3000;
export const useEnvironment = create<EnvStore>()((set, get) => ({ export const useEnvironment = create<EnvStore>()((set, get) => ({
@@ -69,7 +71,10 @@ export const useEnvironment = create<EnvStore>()((set, get) => ({
let nodes: string[] | undefined; let nodes: string[] | undefined;
try { try {
nodes = await fetchConfig(state.VEGA_CONFIG_URL); nodes = await fetchConfig(state.VEGA_CONFIG_URL);
set({ nodes }); const enrichedNodes = uniq(
[...nodes, state.VEGA_URL, storedUrl].filter(Boolean) as string[]
);
set({ nodes: enrichedNodes });
} catch (err) { } catch (err) {
console.warn(`Could not fetch node config from ${state.VEGA_CONFIG_URL}`); console.warn(`Could not fetch node config from ${state.VEGA_CONFIG_URL}`);
} }
+16
View File
@@ -84,6 +84,16 @@ export const useEtherscanLink = () => {
return link; return link;
}; };
export const useDocsLink = () => {
const { VEGA_DOCS_URL } = useEnvironment();
const baseUrl = trim(VEGA_DOCS_URL, '/');
const link = useCallback(
(url?: string) => `${baseUrl}/${trim(url, '/') || ''}`,
[baseUrl]
);
return link;
};
// Vega blog // Vega blog
export const BLOG = 'https://blog.vega.xyz/'; export const BLOG = 'https://blog.vega.xyz/';
@@ -125,3 +135,9 @@ export const CONSOLE_MARKET = '/markets/:marketId';
export const CONSOLE_MARKETS = '/markets/all'; export const CONSOLE_MARKETS = '/markets/all';
export const CONSOLE_PORTFOLIO = '/portfolio'; export const CONSOLE_PORTFOLIO = '/portfolio';
export const CONSOLE_LIQUIDITY = 'liquidity/:marketId'; export const CONSOLE_LIQUIDITY = 'liquidity/:marketId';
// Vega XYZ
export const GET_VEGA_WALLET_URL = 'https://vega.xyz/wallet/';
// Docs
export const DOCS_VEGA_WALLET = '/tools/vega-wallet';
@@ -0,0 +1,11 @@
import { create } from 'zustand';
export const useNodeSwitcherStore = create<{
dialogOpen: boolean;
setDialogOpen: (isOpen: boolean) => void;
}>()((set) => ({
dialogOpen: false,
setDialogOpen: (isOpen) => {
set({ dialogOpen: isOpen });
},
}));
+55 -56
View File
@@ -1,7 +1,11 @@
import { Fragment, useState } from 'react'; import { Fragment } from 'react';
import { t } from '@vegaprotocol/i18n'; import { t } from '@vegaprotocol/i18n';
import { Link, Lozenge } from '@vegaprotocol/ui-toolkit'; import { Link, Lozenge } from '@vegaprotocol/ui-toolkit';
import { NodeSwitcherDialog, useEnvironment } from '@vegaprotocol/environment'; import {
NodeSwitcherDialog,
useEnvironment,
useNodeSwitcherStore,
} from '@vegaprotocol/environment';
const getFeedbackLinks = (gitOriginUrl?: string) => const getFeedbackLinks = (gitOriginUrl?: string) =>
[ [
@@ -19,64 +23,59 @@ export const NetworkInfo = () => {
GITHUB_FEEDBACK_URL, GITHUB_FEEDBACK_URL,
ETHEREUM_PROVIDER_URL, ETHEREUM_PROVIDER_URL,
} = useEnvironment(); } = useEnvironment();
const [nodeSwitcherOpen, setNodeSwitcherOpen] = useState(false);
const setNodeSwitcher = useNodeSwitcherStore((store) => store.setDialogOpen);
const feedbackLinks = getFeedbackLinks(GITHUB_FEEDBACK_URL); const feedbackLinks = getFeedbackLinks(GITHUB_FEEDBACK_URL);
return ( return (
<> <div data-testid="git-info">
<div data-testid="git-info"> <p data-testid="git-network-data" className="mb-2">
<p data-testid="git-network-data" className="mb-2"> {t('Reading network data from')}{' '}
{t('Reading network data from')}{' '} <Lozenge className="bg-neutral-300 dark:bg-neutral-700">
<Lozenge className="bg-neutral-300 dark:bg-neutral-700"> {VEGA_URL}
{VEGA_URL} </Lozenge>
</Lozenge> . <Link onClick={() => setNodeSwitcher(true)}>{t('Edit')}</Link>
. <Link onClick={() => setNodeSwitcherOpen(true)}>{t('Edit')}</Link> </p>
<p data-testid="git-eth-data" className="mb-2 break-all">
{t('Reading Ethereum data from')}{' '}
<Lozenge className="bg-neutral-300 dark:bg-neutral-700">
{ETHEREUM_PROVIDER_URL}
</Lozenge>
.{' '}
</p>
{GIT_COMMIT_HASH && (
<p data-testid="git-commit-hash" className="mb-2">
{t('Version/commit hash')}:{' '}
<Link
href={
GIT_ORIGIN_URL
? `${GIT_ORIGIN_URL}/commit/${GIT_COMMIT_HASH}`
: undefined
}
target={GIT_ORIGIN_URL ? '_blank' : undefined}
>
{GIT_COMMIT_HASH}
</Link>
</p> </p>
<p data-testid="git-eth-data" className="mb-2 break-all"> )}
{t('Reading Ethereum data from')}{' '} {feedbackLinks.length > 0 && (
<Lozenge className="bg-neutral-300 dark:bg-neutral-700"> <p>
{ETHEREUM_PROVIDER_URL} {t('Known issues and feedback on')}{' '}
</Lozenge> {feedbackLinks.map(({ name, url }, index) => (
.{' '} <Fragment key={index}>
<Link key={index} href={url}>
{name}
</Link>
{feedbackLinks.length > 1 &&
index < feedbackLinks.length - 2 &&
','}
{feedbackLinks.length > 1 &&
index === feedbackLinks.length - 1 &&
`, ${t('and')} `}
</Fragment>
))}
</p> </p>
{GIT_COMMIT_HASH && ( )}
<p data-testid="git-commit-hash" className="mb-2"> </div>
{t('Version/commit hash')}:{' '}
<Link
href={
GIT_ORIGIN_URL
? `${GIT_ORIGIN_URL}/commit/${GIT_COMMIT_HASH}`
: undefined
}
target={GIT_ORIGIN_URL ? '_blank' : undefined}
>
{GIT_COMMIT_HASH}
</Link>
</p>
)}
{feedbackLinks.length > 0 && (
<p>
{t('Known issues and feedback on')}{' '}
{feedbackLinks.map(({ name, url }, index) => (
<Fragment key={index}>
<Link key={index} href={url}>
{name}
</Link>
{feedbackLinks.length > 1 &&
index < feedbackLinks.length - 2 &&
','}
{feedbackLinks.length > 1 &&
index === feedbackLinks.length - 1 &&
`, ${t('and')} `}
</Fragment>
))}
</p>
)}
</div>
<NodeSwitcherDialog
open={nodeSwitcherOpen}
setOpen={setNodeSwitcherOpen}
/>
</>
); );
}; };
+1 -1
View File
@@ -68,7 +68,7 @@ inquirer
}) })
.then(() => { .then(() => {
for (const env of answers.envs) { for (const env of answers.envs) {
const branch = env === 'mainnet' ? 'master' : `release/${env}`; const branch = env === 'mainnet' ? 'main' : `release/${env}`;
release(answers.tag, branch); release(answers.tag, branch);
} }
execSync('git checkout develop'); execSync('git checkout develop');