Compare commits

...
Author SHA1 Message Date
Dariusz Majcherczyk 08e6848ef6 test: remove failing wait 2023-05-10 17:33:58 +02:00
Dariusz Majcherczyk 03ba61307f test: remove only 2023-05-10 17:30:48 +02:00
Dariusz Majcherczyk 2a5b17f00d Merge branch 'develop' into test/oraacle-profile-e2e-tests 2023-05-10 17:30:08 +02:00
Maciek dc7832ac81 chore(candles-chart): fill up missing candles (#3664) 2023-05-10 15:26:27 +00:00
Dariusz Majcherczyk ed42ed7f76 test: fix wallet eth tests 2023-05-10 17:23:07 +02:00
Joe Tsang 91207d31ee chore(governance): fix failing validator tests (#3703) 2023-05-10 15:03:47 +00:00
Dariusz Majcherczyk 36e6d665a0 test: fix for api column size error 2023-05-10 15:06:57 +02:00
dexturr 58efb460a6 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-05-10 12:10:00 +00:00
Mikołaj Młodzikowski 044f98777b feat(ci): lint makefile 2023-05-10 13:43:33 +02:00
Mikołaj Młodzikowski 00fdc5e81a fix(ci): preparing dist bash syntax 2023-05-10 13:33:17 +02:00
Mikołaj Młodzikowski adfad3bafb feat(ci): fix typo 2023-05-10 13:21:53 +02:00
Dariusz Majcherczyk dd998e2d7f test: fix for ci 2023-05-10 12:45:41 +02:00
Dariusz Majcherczyk 5468136d31 test: show full oracle profile info in markets e2e 2023-05-10 12:00:17 +02:00
dexturr e6ae88905c chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-05-10 06:07:15 +00:00
dexturr 3953ed9953 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-05-10 00:15:02 +00:00
Matthew Russell b48ab58e6a chore: add VEGA_URL for explorer mainnet 2023-05-09 14:06:10 -07:00
Matthew Russell e44cf1ff53 chore(environment,trading): fix unit tests for footer and node switcher (#3682) 2023-05-09 13:39:17 -07:00
Matthew Russell bded1d32ba feat(explorer,governance): always allow selecting a node, add node guard (#3678) 2023-05-09 12:58:09 -07:00
Edd 76ddf45f4c fix(explorer): add envs back to env (#3677) 2023-05-09 12:57:38 -07:00
Dexter Edwards 6a2c9004d8 fix(governance, console, explorer): show vega node in list, cache bust (#3676) 2023-05-09 12:57:11 -07:00
dexturr 77ee88b43e chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-05-09 18:09:50 +00:00
Dexter e4958ce3c0 fix: do not show validators with 0 self stake in list 2023-05-09 18:25:20 +01:00
Mikołaj Młodzikowski ecedb879f8 fix(ci): define which app ipfs hash is set for 2023-05-09 18:47:50 +02:00
m.ray 89700803d6 fix(trading): fix release script for mainnet (#3669) 2023-05-09 16:30:05 +00:00
Mikołaj Młodzikowski 65c9fa5df2 feat(ci): establish build based on docker to produce ipfs-hashes (#3612) 2023-05-09 18:24:39 +02:00
ArtandMadalina Raicu 02399c40fc fix(trading): welcome dialog links (#3656)
Co-authored-by: Madalina Raicu <madalina@raygroup.uk>
2023-05-09 15:43:36 +00:00
52 changed files with 1140 additions and 343 deletions
+3 -3
View File
@@ -5,6 +5,8 @@ on:
branches:
- release/*
- develop
tags:
- v*
pull_request:
types:
- opened
@@ -97,15 +99,13 @@ jobs:
- name: See affected apps
run: |
echo ">>>> debug"
echo "NX Version: $nx_version"
echo "NX_BASE: ${{ env.NX_BASE }}"
echo "NX_HEAD: ${{ env.NX_HEAD }}"
echo ">>>> eof debug"
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=""
preview_governance="not deployed"
preview_trading="not deployed"
+39 -34
View File
@@ -30,13 +30,21 @@ jobs:
- name: Set up Docker Buildx
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
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Log in to the Container registry (docker hub)
uses: docker/login-action@v2
if: ${{ startsWith(github.ref, 'refs/tags/v') }}
with:
# registry: registry.hub.docker.com
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Setup node
uses: actions/setup-node@v3
with:
@@ -54,30 +62,24 @@ jobs:
- name: Define variables
run: |
envName=''
dockerfile="dist.Dockerfile"
if [[ "${{ github.event_name }}" = "push" ]]; then
domain="vega.rocks"
if [[ "${{ github.ref }}" =~ .*release/.* ]]; then
envName="$(echo ${{ github.ref }} | rev | cut -d '/' -f 1 | rev)"
if [[ "${{ github.ref }}" =~ .*mainnet.* ]]; then
domain="vega.community"
if [[ "${{ matrix.app }}" = "trading" ]]; then
dockerfile="ipfs.Dockerfile"
fi
fi
elif [[ "${{ github.ref }}" =~ .*develop$ ]]; then
envName="stagnet1"
elif [[ "${{ matrix.app}}" = "trading" ]] && [[ ${{ startsWith(github.ref, 'refs/tags/v') && 'true' || 'false' }} = "true" ]]; then
envName="mainnet"
fi
bucketName="${{ matrix.app }}.${envName}.${domain}"
echo BUCKET_NAME=${bucketName} >> $GITHUB_ENV
fi
nodeVersion=$(cat .nvmrc | head -n 1)
echo ENV_NAME=${envName} >> $GITHUB_ENV
echo NODE_VERSION=${nodeVersion} >> $GITHUB_ENV
echo DOCKERFILE=docker/${dockerfile} >> $GITHUB_ENV
- name: Build local dist
if: ${{ env.DOCKERFILE != 'docker/ipfs.Dockerfile' }}
run: |
flags=""
if [[ ! -z "${{ env.ENV_NAME }}" ]]; then
@@ -98,62 +100,59 @@ jobs:
- name: Build and export to local Docker
id: docker_build
if: ${{ github.event_name == 'pull_request' || ( env.DOCKERFILE == 'docker/ipfs.Dockerfile' && github.event_name == 'push' ) }}
uses: docker/build-push-action@v3
with:
context: .
file: ${{ env.DOCKERFILE }}
file: docker/node-outside-docker.Dockerfile
load: true
build-args: |
APP=${{ matrix.app }}
NODE_VERSION=${{ env.NODE_VERSION }}
ENV_NAME=${{ env.ENV_NAME }}
tags: |
ghcr.io/vegaprotocol/frontend/${{ matrix.app }}:local
- 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 }}
- name: Sanity check docker image
if: ${{ github.event_name == 'pull_request' || ( env.DOCKERFILE == 'docker/ipfs.Dockerfile' && github.event_name == 'push' ) }}
run: |
echo "Check ipfs-hash"
if [[ "${{ env.DOCKERFILE }}" = "docker/ipfs.Dockerfile" ]]; then
docker run --rm ghcr.io/vegaprotocol/frontend/${{ matrix.app }}:local cat /ipfs-hash
fi
docker run --rm ghcr.io/vegaprotocol/frontend/${{ matrix.app }}:local cat /ipfs-hash
docker run --rm ghcr.io/vegaprotocol/frontend/${{ matrix.app }}:local cat /ipfs-hash > ${{ matrix.app }}-ipfs-hash
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
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
- name: Publish dist as docker image (ghcr)
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:
context: .
file: ${{ env.DOCKERFILE }}
file: docker/node-outside-docker.Dockerfile
push: true
build-args: |
APP=${{ matrix.app }}
NODE_VERSION=${{ env.NODE_VERSION }}
ENV_NAME=${{ env.ENV_NAME }}
tags: |
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
- name: Publish dist to s3
uses: jakejarvis/s3-sync-action@master
if: ${{ github.event_name == 'push' }}
if: ${{ github.event_name == 'push' && !startsWith(github.ref, 'refs/tags/v') }}
with:
args: --acl private --follow-symlinks --delete
env:
@@ -169,3 +168,9 @@ jobs:
with:
labels: ${{ matrix.app }}-preview
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
/dist
/dist-result
/tmp
/out-tsc
/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.
# 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.
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:
In order to run a container on port 3000:
```bash
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
+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_VEGA_GOVERNANCE_URL=https://stagnet1.governance.vega.xyz
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
NX_EXPLORER_ASSETS=1
+1
View File
@@ -2,6 +2,7 @@
NX_TENDERMINT_URL=https://be.vega.community
NX_TENDERMINT_WEBSOCKET_URL=wss://be.vega.community/websocket
NX_VEGA_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/networks/master/mainnet1/mainnet1.toml
NX_VEGA_URL=https://api.vega.community/graphql
NX_VEGA_ENV=MAINNET
NX_BLOCK_EXPLORER=https://be.vega.community/rest/
NX_ETHERSCAN_URL=https://etherscan.io
+2 -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 { Loader, Splash } from '@vegaprotocol/ui-toolkit';
import { DEFAULT_CACHE_CONFIG } from '@vegaprotocol/apollo-client';
import { RouterProvider } from 'react-router-dom';
import { router } from './routes/router-config';
import { t } from '@vegaprotocol/i18n';
const splashLoading = (
<Splash>
@@ -12,10 +21,23 @@ const splashLoading = (
);
function App() {
const { VEGA_URL } = useEnvironment();
const [nodeSwitcherOpen, setNodeSwitcherOpen] = useNodeSwitcherStore(
(store) => [store.dialogOpen, store.setDialogOpen]
);
return (
<TendermintWebsocketProvider>
<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>
</TendermintWebsocketProvider>
);
@@ -1,13 +1,19 @@
import { NodeSwitcherDialog, useEnvironment } from '@vegaprotocol/environment';
import {
useEnvironment,
useNodeSwitcherStore,
} from '@vegaprotocol/environment';
import { t } from '@vegaprotocol/i18n';
import { useScreenDimensions } from '@vegaprotocol/react-helpers';
import { ExternalLink, Link } from '@vegaprotocol/ui-toolkit';
import { useMemo, useState } from 'react';
import { useMemo } from 'react';
import { ENV } from '../../config/env';
export const Footer = () => {
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 showFullFeedbackLabel = useMemo(
() => ['md', 'lg', 'xl', 'xxl', 'xxxl'].includes(screenSize),
@@ -15,46 +21,40 @@ export const Footer = () => {
);
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">
<div className="flex justify-between gap-2 align-middle">
{GIT_COMMIT_HASH && (
<div className="content-center flex border-r border-neutral-700 dark:border-neutral-300 pr-4">
<p data-testid="git-commit-hash">
{t('Version')}:{' '}
<Link
href={
GIT_ORIGIN_URL
? `${GIT_ORIGIN_URL}/commit/${GIT_COMMIT_HASH}`
: undefined
}
target={GIT_ORIGIN_URL ? '_blank' : undefined}
>
{GIT_COMMIT_HASH}
</Link>
</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>
<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">
{GIT_COMMIT_HASH && (
<div className="content-center flex border-r border-neutral-700 dark:border-neutral-300 pr-4">
<p data-testid="git-commit-hash">
{t('Version')}:{' '}
<Link
href={
GIT_ORIGIN_URL
? `${GIT_ORIGIN_URL}/commit/${GIT_COMMIT_HASH}`
: undefined
}
target={GIT_ORIGIN_URL ? '_blank' : undefined}
>
{GIT_COMMIT_HASH}
</Link>
</p>
</div>
)}
<div className="flex pl-2 content-center">
<ExternalLink href={ENV.addresses.feedback}>
{showFullFeedbackLabel ? t('Share your feedback') : t('Feedback')}
</ExternalLink>
</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>
</footer>
<NodeSwitcherDialog
open={nodeSwitcherOpen}
setOpen={setNodeSwitcherOpen}
/>
</>
<div className="flex pl-2 content-center">
<ExternalLink href={ENV.addresses.feedback}>
{showFullFeedbackLabel ? t('Share your feedback') : t('Feedback')}
</ExternalLink>
</div>
</div>
</footer>
);
};
@@ -7,6 +7,7 @@ import {
waitForSpinner,
navigateTo,
navigation,
turnTelemetryOff,
} from '../../support/common.functions';
import {
clickOnValidatorFromList,
@@ -56,6 +57,7 @@ context(
// 2001-STKE-002, 2001-STKE-032
before('visit staking tab and connect vega wallet', function () {
cy.visit('/');
cy.validatorsSelfDelegate();
ethereumWalletConnect();
// this is a workaround for #2422 which can be removed once issue is resolved
cy.associateTokensToVegaWallet('4');
@@ -67,6 +69,7 @@ context(
'teardown wallet & drill into a specific validator',
function () {
cy.clearLocalStorage();
turnTelemetryOff();
cy.reload();
waitForSpinner();
cy.connectVegaWallet();
@@ -252,10 +255,10 @@ context(
waitForBeginningOfEpoch();
cy.getByTestId(stakeValidatorListStakePercentage).should(
'have.text',
'100%'
'50.02%'
);
navigateTo(navigation.validators);
validateValidatorListTotalStakeAndShare('0', '2.00', '100.00%');
validateValidatorListTotalStakeAndShare('0', '3,002.00', '50.02%');
}
);
@@ -29,7 +29,10 @@ const performancePenaltyToolTip = '[data-testid="performance-penalty-tooltip"]';
const overstakedPenaltyToolTip = '[data-testid="overstaked-penalty-tooltip"]';
const totalPenaltyToolTip = '[data-testid="total-penalty-tooltip"]';
const epochCountDown = '[data-testid="epoch-countdown"]';
const stakeNumberRegex = /^\d*\.?\d*$/;
const stakeNumberRegex = /^\d{1,3}(,\d{3})*(\.\d+)?$/;
// If running locally, validators need to have self-stake to be displayed
// Run cy.validatorsSelfDelegate() in before hook
context('Validators Page - verify elements on page', function () {
before('navigate to validators page', function () {
@@ -84,13 +87,13 @@ context('Validators Page - verify elements on page', function () {
cy.get(stakedByOperatorToolTip)
.invoke('text')
.should('contain', 'Staked by operator: 0.00');
.should('contain', 'Staked by operator: 3,000.00');
cy.get(stakedByDelegatesToolTip)
.invoke('text')
.should('contain', 'Staked by delegates: 0.00');
cy.get(totalStakedToolTip)
.invoke('text')
.should('contain', 'Total stake: 0.00');
.should('contain', 'Total stake: 3,000.00');
});
it('Should be able to see validator normalised voting power', function () {
@@ -106,10 +109,10 @@ context('Validators Page - verify elements on page', function () {
cy.get(unnormalisedVotingPowerToolTip)
.invoke('text')
.should('contain', 'Unnormalised voting power: 0.00%');
.should('contain', 'Unnormalised voting power: 20.00%');
cy.get(normalisedVotingPowerToolTip)
.invoke('text')
.should('contain', 'Normalised voting power: 0.10%');
.should('contain', 'Normalised voting power: 50.00%');
});
// 2002-SINC-018
@@ -126,13 +129,13 @@ context('Validators Page - verify elements on page', function () {
cy.get(performancePenaltyToolTip)
.invoke('text')
.should('contain', 'Performance penalty: 100.00%');
.should('contain', 'Performance penalty: 0.00%');
cy.get(overstakedPenaltyToolTip)
.invoke('text')
.should('contain', 'Overstaked penalty:'); // value not asserted due to #2886
.should('contain', 'Overstaked penalty: 60.00%'); // value not asserted due to #2886
cy.get(totalPenaltyToolTip)
.invoke('text')
.should('contain', 'Total penalties: 0.00%');
.should('contain', 'Total penalties: 60.00%');
});
it('Should be able to see validator pending stake', function () {
+37 -14
View File
@@ -37,6 +37,10 @@ import {
useEnvironment,
NetworkLoader,
useInitializeEnv,
NodeGuard,
AppFailure,
NodeSwitcherDialog,
useNodeSwitcherStore,
} from '@vegaprotocol/environment';
import { ENV } from './config';
import type { InMemoryCacheConfig } from '@apollo/client';
@@ -48,6 +52,7 @@ import {
TELEMETRY_ON,
} from './components/telemetry-dialog/telemetry-dialog';
import { useLocalStorage } from '@vegaprotocol/react-helpers';
import { useTranslation } from 'react-i18next';
const cache: InMemoryCacheConfig = {
typePolicies: {
@@ -181,9 +186,19 @@ const ScrollToTop = () => {
const AppContainer = () => {
const { config, loading, error } = useEthereumConfig();
const { VEGA_ENV, GIT_COMMIT_HASH, GIT_BRANCH, ETHEREUM_PROVIDER_URL } =
useEnvironment();
const {
VEGA_ENV,
VEGA_URL,
GIT_COMMIT_HASH,
GIT_BRANCH,
ETHEREUM_PROVIDER_URL,
} = useEnvironment();
const [telemetryOn] = useLocalStorage(TELEMETRY_ON);
const { t } = useTranslation();
const [nodeSwitcherOpen, setNodeSwitcher] = useNodeSwitcherStore((store) => [
store.dialogOpen,
store.setDialogOpen,
]);
useEffect(() => {
if (ENV.dsn && telemetryOn) {
@@ -219,21 +234,29 @@ const AppContainer = () => {
<ScrollToTop />
<AppStateProvider>
<div className="grid min-h-full text-white">
<AsyncRenderer<EthereumConfig | null>
loading={loading}
data={config}
error={error}
render={(cnf) =>
cnf && (
<Web3Container
chainId={Number(cnf.chain_id)}
providerUrl={ETHEREUM_PROVIDER_URL}
/>
)
<NodeGuard
skeleton={<div>{t('Loading')}</div>}
failure={
<AppFailure title={t('NodeUnsuitable', { url: VEGA_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>
</AppStateProvider>
<NodeSwitcherDialog open={nodeSwitcherOpen} setOpen={setNodeSwitcher} />
</Router>
);
};
@@ -811,5 +811,6 @@
"OptOutOfTelemetry": "You can opt out any time via settings",
"NoThanks": "No thanks",
"ShareData": "Share data",
"ContinueSharingData": "Continue sharing data"
"ContinueSharingData": "Continue sharing data",
"NodeUnsuitable": "Node: {{url}} is unsuitable"
}
@@ -34,6 +34,7 @@ import {
formatNumberPercentage,
toBigNum,
} from '@vegaprotocol/utils';
import { VALIDATOR_LOGO_MAP } from './logo-map';
interface CanonisedConsensusNodeProps {
id: string;
@@ -161,6 +162,11 @@ export const ConsensusValidatorsTable = ({
pendingUserStake,
userStakeShare,
}) => {
const logo = VALIDATOR_LOGO_MAP[id]
? VALIDATOR_LOGO_MAP[id]
: avatarUrl
? avatarUrl
: null;
const {
rawValidatorScore: previousEpochValidatorScore,
performanceScore: previousEpochPerformanceScore,
@@ -171,7 +177,7 @@ export const ConsensusValidatorsTable = ({
id,
[ValidatorFields.RANKING_INDEX]: votingPowerRanking,
[ValidatorFields.VALIDATOR]: {
avatarUrl,
avatarUrl: logo,
name,
},
[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 type { StakingDelegationFieldsFragment } from '../../__generated__/Staking';
import type { ValidatorsView } from './validator-tables';
import { VALIDATOR_LOGO_MAP } from './logo-map';
export enum ValidatorFields {
RANKING_INDEX = 'rankingIndex',
@@ -102,13 +103,18 @@ interface ValidatorRendererProps {
export const ValidatorRenderer = ({ data }: ValidatorRendererProps) => {
const { t } = useTranslation();
const { avatarUrl, name } = data.validator;
const logo = VALIDATOR_LOGO_MAP[data.id]
? VALIDATOR_LOGO_MAP[data.id]
: avatarUrl
? avatarUrl
: null;
return (
<div className="w-[238px] grid grid-cols-[1fr_auto] gap-2 items-center">
<span className="flex overflow-hidden">
{avatarUrl && (
{logo && (
<img
className="h-6 w-6 rounded-full mr-2"
src={avatarUrl}
src={logo}
alt={`Avatar icon for ${name}`}
onError={(e) => (e.currentTarget.style.display = 'none')}
/>
@@ -78,37 +78,38 @@ export const ValidatorTables = ({
const nextDelegation = delegations?.find(
(d) => d.node.id === validatorId && d.epoch === Number(epochId) + 1
);
switch (validator.rankingScore?.status) {
case Schema.ValidatorStatus.VALIDATOR_NODE_STATUS_TENDERMINT:
acc.consensusValidators.push(
addUserDataToValidator(
validator,
currentDelegation,
nextDelegation,
currentUserStakeAvailable
)
);
break;
case Schema.ValidatorStatus.VALIDATOR_NODE_STATUS_ERSATZ:
acc.standbyValidators.push(
addUserDataToValidator(
validator,
currentDelegation,
nextDelegation,
currentUserStakeAvailable
)
);
break;
case Schema.ValidatorStatus.VALIDATOR_NODE_STATUS_PENDING:
acc.pendingValidators.push(
addUserDataToValidator(
validator,
currentDelegation,
nextDelegation,
currentUserStakeAvailable
)
);
if (validator.stakedByOperator !== '0') {
switch (validator.rankingScore?.status) {
case Schema.ValidatorStatus.VALIDATOR_NODE_STATUS_TENDERMINT:
acc.consensusValidators.push(
addUserDataToValidator(
validator,
currentDelegation,
nextDelegation,
currentUserStakeAvailable
)
);
break;
case Schema.ValidatorStatus.VALIDATOR_NODE_STATUS_ERSATZ:
acc.standbyValidators.push(
addUserDataToValidator(
validator,
currentDelegation,
nextDelegation,
currentUserStakeAvailable
)
);
break;
case Schema.ValidatorStatus.VALIDATOR_NODE_STATUS_PENDING:
acc.pendingValidators.push(
addUserDataToValidator(
validator,
currentDelegation,
nextDelegation,
currentUserStakeAvailable
)
);
}
}
return acc;
},
+140 -39
View File
@@ -48,8 +48,8 @@
"tranche_start": "2023-04-20T00:00:00.000Z",
"tranche_end": "2023-05-20T00:00:00.000Z",
"total_added": "19242.125",
"total_removed": "1523.8177488329475",
"locked_amount": "6891.9471601080244395475",
"total_removed": "1979.64045368475",
"locked_amount": "6088.8928113908174973875",
"deposits": [
{
"amount": "188",
@@ -228,6 +228,16 @@
"user": "0xDd7a98557586ce21f770662319C2047C5a3bD605",
"tx": "0x2b3571c143ecebddf91fb62f402d516d51110edfe37b13200a6e5cf682dc5bb0"
},
{
"amount": "336.4580555509875",
"user": "0xf915Da10e5136352Ba049acB0545Deb119054256",
"tx": "0x7af5942634e236f5f9c580f4ed042794ed309e83885f652a55ab37793ea2e85c"
},
{
"amount": "119.364649300815",
"user": "0xDd7a98557586ce21f770662319C2047C5a3bD605",
"tx": "0x47f5bf2c758c5270dd1b6519ac649ddabef8199f8a0bae319e36f8ac5c9c142e"
},
{
"amount": "202.093666077975",
"user": "0x1447Efc62d5077d7AbB20492dF831Dd6c9Eb1756",
@@ -362,6 +372,12 @@
"tranche_id": 56,
"tx": "0x2b3571c143ecebddf91fb62f402d516d51110edfe37b13200a6e5cf682dc5bb0"
},
{
"amount": "119.364649300815",
"user": "0xDd7a98557586ce21f770662319C2047C5a3bD605",
"tranche_id": 56,
"tx": "0x47f5bf2c758c5270dd1b6519ac649ddabef8199f8a0bae319e36f8ac5c9c142e"
},
{
"amount": "195.89040769089",
"user": "0xDd7a98557586ce21f770662319C2047C5a3bD605",
@@ -370,8 +386,8 @@
}
],
"total_tokens": "914.25",
"withdrawn_tokens": "504.71220630471",
"remaining_tokens": "409.53779369529"
"withdrawn_tokens": "624.076855605525",
"remaining_tokens": "290.173144394475"
},
{
"address": "0x9573BDF7FfC5519912d293e4D1f750eab2E471E7",
@@ -705,6 +721,12 @@
}
],
"withdrawals": [
{
"amount": "336.4580555509875",
"user": "0xf915Da10e5136352Ba049acB0545Deb119054256",
"tranche_id": 56,
"tx": "0x7af5942634e236f5f9c580f4ed042794ed309e83885f652a55ab37793ea2e85c"
},
{
"amount": "422.1034736680125",
"user": "0xf915Da10e5136352Ba049acB0545Deb119054256",
@@ -713,8 +735,8 @@
}
],
"total_tokens": "1121.25",
"withdrawn_tokens": "422.1034736680125",
"remaining_tokens": "699.1465263319875"
"withdrawn_tokens": "758.561529219",
"remaining_tokens": "362.688470781"
},
{
"address": "0x237D23FcA6d7B2530C7614a9cB921CF27924911E",
@@ -877,7 +899,7 @@
"tranche_start": "2023-04-06T00:00:00.000Z",
"tranche_end": "2023-05-06T00:00:00.000Z",
"total_added": "14610",
"total_removed": "6141.45090157707",
"total_removed": "6675.45090157707",
"locked_amount": "0",
"deposits": [
{
@@ -1077,6 +1099,11 @@
"user": "0x9E53F72210479BF46aE7ABF77c48B0d611b3c3dB",
"tx": "0x1dbcf713b48965a82aa2e17cb3e7db9a491668d859d408a39c8a74b0ea860b6b"
},
{
"amount": "534",
"user": "0x2586bA83696a92b5467Aaa0CF9EEC052F28F2c02",
"tx": "0x5aa922056cad64f97a7dfa750da57d63abfdaea4499192d4d57958bfb4fba2ea"
},
{
"amount": "106.53500000286",
"user": "0x6F32AA5A6198329c16e438512F992a0548C856f9",
@@ -1200,10 +1227,17 @@
"tx": "0xf970ea0ce3e36fa0014d24bb830dd2ea0dbea06f6e53af486de5ee7e1c63e540"
}
],
"withdrawals": [],
"withdrawals": [
{
"amount": "534",
"user": "0x2586bA83696a92b5467Aaa0CF9EEC052F28F2c02",
"tranche_id": 54,
"tx": "0x5aa922056cad64f97a7dfa750da57d63abfdaea4499192d4d57958bfb4fba2ea"
}
],
"total_tokens": "534",
"withdrawn_tokens": "0",
"remaining_tokens": "534"
"withdrawn_tokens": "534",
"remaining_tokens": "0"
},
{
"address": "0xBf1AaB792D729fA125e6D7122D4b916a1E1C44B1",
@@ -1826,7 +1860,7 @@
"tranche_start": "2023-03-06T00:00:00.000Z",
"tranche_end": "2023-04-06T00:00:00.000Z",
"total_added": "14099",
"total_removed": "3722.49002352036",
"total_removed": "3785.49002352036",
"locked_amount": "0",
"deposits": [
{
@@ -2591,6 +2625,11 @@
}
],
"withdrawals": [
{
"amount": "63",
"user": "0x2a65Ae527C6Ff4665e048B0E0883c486A7BA4DBc",
"tx": "0xb0edcc25e422bc3db3ad8027dfdfc0928abd7a8af8957a1699e4cf2dc8cee8f7"
},
{
"amount": "30",
"user": "0xBe9F912Ad481C61B653463E8F1D2b2b310D49861",
@@ -3629,10 +3668,17 @@
"tx": "0xd4a269b070cbaaff7e29a99f6b3997117d765f69512bb760e28bade799fcbba4"
}
],
"withdrawals": [],
"withdrawals": [
{
"amount": "63",
"user": "0x2a65Ae527C6Ff4665e048B0E0883c486A7BA4DBc",
"tranche_id": 53,
"tx": "0xb0edcc25e422bc3db3ad8027dfdfc0928abd7a8af8957a1699e4cf2dc8cee8f7"
}
],
"total_tokens": "63",
"withdrawn_tokens": "0",
"remaining_tokens": "63"
"withdrawn_tokens": "63",
"remaining_tokens": "0"
},
{
"address": "0xc3B1eB0feE837Db0A3ded5bf16A050726955195B",
@@ -4861,7 +4907,7 @@
"tranche_end": "2023-12-05T00:00:00.000Z",
"total_added": "86666.297",
"total_removed": "0",
"locked_amount": "49802.2753121154213651374",
"locked_amount": "49504.9919953353951513585",
"deposits": [
{
"amount": "86666.297",
@@ -4927,7 +4973,7 @@
"tranche_end": "2023-06-01T00:00:00.000Z",
"total_added": "2500",
"total_removed": "0",
"locked_amount": "312.4325905575906",
"locked_amount": "295.23443859381355",
"deposits": [
{
"amount": "2500",
@@ -4960,7 +5006,7 @@
"tranche_end": "2023-11-01T00:00:00.000Z",
"total_added": "15000.000000000000015",
"total_removed": "0",
"locked_amount": "14327.0455917874395143270455917874395",
"locked_amount": "14224.9782986111115142249782986111115",
"deposits": [
{
"amount": "1.5e-14",
@@ -5048,7 +5094,7 @@
"tranche_end": "2023-09-01T00:00:00.000Z",
"total_added": "17500",
"total_removed": "0",
"locked_amount": "10913.25608896940375",
"locked_amount": "10794.17758026368775",
"deposits": [
{
"amount": "12500",
@@ -5315,7 +5361,7 @@
"tranche_end": "2023-08-01T00:00:00.000Z",
"total_added": "37500",
"total_removed": "18077.0118744",
"locked_amount": "17350.5026089625535",
"locked_amount": "17091.10506829343025",
"deposits": [
{
"amount": "7500",
@@ -5734,7 +5780,7 @@
"tranche_end": "2023-12-05T00:00:00.000Z",
"total_added": "129999.45",
"total_removed": "0",
"locked_amount": "49756.837002255673748325",
"locked_amount": "49459.824919096584707505",
"deposits": [
{
"amount": "129999.45",
@@ -5767,7 +5813,7 @@
"tranche_end": "2024-04-01T00:00:00.000Z",
"total_added": "54144.7663",
"total_removed": "0",
"locked_amount": "48485.46842731635829207292",
"locked_amount": "48300.2481374426486922947",
"deposits": [
{
"amount": "54144.7663",
@@ -5800,7 +5846,7 @@
"tranche_end": "2023-09-03T00:00:00.000Z",
"total_added": "62600",
"total_removed": "0",
"locked_amount": "20022.5830035514954",
"locked_amount": "19807.852061136476024",
"deposits": [
{
"amount": "10000",
@@ -5993,7 +6039,7 @@
"tranche_end": "2023-09-17T00:00:00.000Z",
"total_added": "5000",
"total_removed": "0",
"locked_amount": "1791.028665651953",
"locked_amount": "1773.87763191273475",
"deposits": [
{
"amount": "5000",
@@ -7062,7 +7108,7 @@
"tranche_end": "2023-06-02T00:00:00.000Z",
"total_added": "1939928.38",
"total_removed": "1709370.7872515768348",
"locked_amount": "126202.1342632826843379468",
"locked_amount": "119547.7788438730317094704",
"deposits": [
{
"amount": "1852091.69",
@@ -40934,7 +40980,7 @@
"tranche_end": "2023-06-05T00:00:00.000Z",
"total_added": "3732368.4671",
"total_removed": "715655.108029600523393",
"locked_amount": "218430.066175628697078272948",
"locked_amount": "208204.638696483881349131871",
"deposits": [
{
"amount": "1998.95815",
@@ -42326,8 +42372,8 @@
"tranche_start": "2022-06-05T00:00:00.000Z",
"tranche_end": "2023-12-05T00:00:00.000Z",
"total_added": "15870102.715470999700000001",
"total_removed": "871680.07831804700259352",
"locked_amount": "6074226.5757489411253544394273819693295485",
"total_removed": "872635.89843522227071852",
"locked_amount": "6037967.8664430882362406121449295313314409",
"deposits": [
{
"amount": "16249.93",
@@ -42836,6 +42882,11 @@
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
"tx": "0x625e0c8fb8d5e2dd7fef0359bb396571f7d8aed05a601ddf0f6e6577edb26a13"
},
{
"amount": "955.820117175268125",
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
"tx": "0x4301fcb38d16c1f6aa4a93a18a277a4e77eb3b4565bf98ca6218f6598b78b90b"
},
{
"amount": "10150.87581603206683",
"user": "0xe3eB4CF43C072658401996b71D43eE26E573562D",
@@ -45010,6 +45061,12 @@
"tranche_id": 2,
"tx": "0x625e0c8fb8d5e2dd7fef0359bb396571f7d8aed05a601ddf0f6e6577edb26a13"
},
{
"amount": "955.820117175268125",
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
"tranche_id": 2,
"tx": "0x4301fcb38d16c1f6aa4a93a18a277a4e77eb3b4565bf98ca6218f6598b78b90b"
},
{
"amount": "913.910324501590625",
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
@@ -46566,8 +46623,8 @@
}
],
"total_tokens": "259998.8875",
"withdrawn_tokens": "159590.522104386423875",
"remaining_tokens": "100408.365395613576125"
"withdrawn_tokens": "160546.342221561692",
"remaining_tokens": "99452.545278438308"
},
{
"address": "0x89051CAb67Bc7F8CC44F7e270c6EDaf1EC57676c",
@@ -48948,7 +49005,7 @@
"tranche_start": "2021-11-05T00:00:00.000Z",
"tranche_end": "2023-05-05T00:00:00.000Z",
"total_added": "14597706.0446472999",
"total_removed": "6170228.182342542771392682",
"total_removed": "6246751.954791069035514289",
"locked_amount": "0",
"deposits": [
{
@@ -49158,6 +49215,11 @@
}
],
"withdrawals": [
{
"amount": "76523.772448526264121607",
"user": "0xfc3b2D0b548d3edBb512CeE0Bb79Fb7FaD50AaF3",
"tx": "0x5c4a7a8fbab9c7c5899835a06397ea9f9dfbc434376dd0515482b8ba83944d67"
},
{
"amount": "1257.7360413991436375",
"user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b",
@@ -56357,6 +56419,12 @@
}
],
"withdrawals": [
{
"amount": "76523.772448526264121607",
"user": "0xfc3b2D0b548d3edBb512CeE0Bb79Fb7FaD50AaF3",
"tranche_id": 3,
"tx": "0x5c4a7a8fbab9c7c5899835a06397ea9f9dfbc434376dd0515482b8ba83944d67"
},
{
"amount": "466184.354416277260082874",
"user": "0xfc3b2D0b548d3edBb512CeE0Bb79Fb7FaD50AaF3",
@@ -56371,8 +56439,8 @@
}
],
"total_tokens": "575536.79731735",
"withdrawn_tokens": "499013.024868823735878393",
"remaining_tokens": "76523.772448526264121607"
"withdrawn_tokens": "575536.79731735",
"remaining_tokens": "0"
},
{
"address": "0xaAeD573103e3f981867C4cd31c6674DE1c8a13c3",
@@ -56864,7 +56932,7 @@
"tranche_start": "2021-10-05T00:00:00.000Z",
"tranche_end": "2023-04-05T00:00:00.000Z",
"total_added": "5778205.3912159303",
"total_removed": "3480901.398845846498418769",
"total_removed": "3492469.400132244009296769",
"locked_amount": "0",
"deposits": [
{
@@ -57009,6 +57077,11 @@
}
],
"withdrawals": [
{
"amount": "11568.001286397510878",
"user": "0x759C9ABABA492500c4c730bEB568B5b851Dec2c7",
"tx": "0xd4191eb657ad26e08faddde326514247ae4a88e8aefcc7592590f368010c4046"
},
{
"amount": "58898.87608135458795",
"user": "0x83b1a48376E045D26420200345414e6b93066396",
@@ -57979,6 +58052,12 @@
}
],
"withdrawals": [
{
"amount": "11568.001286397510878",
"user": "0x759C9ABABA492500c4c730bEB568B5b851Dec2c7",
"tranche_id": 4,
"tx": "0xd4191eb657ad26e08faddde326514247ae4a88e8aefcc7592590f368010c4046"
},
{
"amount": "209431.056913602489122",
"user": "0x759C9ABABA492500c4c730bEB568B5b851Dec2c7",
@@ -57987,8 +58066,8 @@
}
],
"total_tokens": "220999.0582",
"withdrawn_tokens": "209431.056913602489122",
"remaining_tokens": "11568.001286397510878"
"withdrawn_tokens": "220999.0582",
"remaining_tokens": "0"
},
{
"address": "0x9058e12e2F32cB1cD4D3123359963D77786477FC",
@@ -59069,8 +59148,8 @@
"tranche_start": "2022-06-05T00:00:00.000Z",
"tranche_end": "2023-06-05T00:00:00.000Z",
"total_added": "472355.6199999996",
"total_removed": "44479.0535455583416",
"locked_amount": "34611.49258501772999518148756976",
"total_removed": "44544.1737890903416",
"locked_amount": "32991.215149911803725776786707268",
"deposits": [
{
"amount": "3000",
@@ -65694,6 +65773,11 @@
"user": "0x0436E2E4EA467f9958403dc56635404EE1bb1E94",
"tx": "0x97f5b5ec5ac839a8d182b9267b5d052cb70f9a18b648874337e3d5360b21a5fb"
},
{
"amount": "65.120243532",
"user": "0x3F9E884B459a6AaC3f66b72555611AdE68a7F472",
"tx": "0x4697229c7f272bd3fc06ecdc1b15143d28e20083e15f1b3cadb3fc80e80af180"
},
{
"amount": "182.662252664",
"user": "0xb1169C6daAc76bAcaf0D8f87641Fc38fbabe569F",
@@ -86287,6 +86371,12 @@
}
],
"withdrawals": [
{
"amount": "65.120243532",
"user": "0x3F9E884B459a6AaC3f66b72555611AdE68a7F472",
"tranche_id": 5,
"tx": "0x4697229c7f272bd3fc06ecdc1b15143d28e20083e15f1b3cadb3fc80e80af180"
},
{
"amount": "120.24984145",
"user": "0x3F9E884B459a6AaC3f66b72555611AdE68a7F472",
@@ -86295,8 +86385,8 @@
}
],
"total_tokens": "200",
"withdrawn_tokens": "120.24984145",
"remaining_tokens": "79.75015855"
"withdrawn_tokens": "185.370084982",
"remaining_tokens": "14.629915018"
},
{
"address": "0x8c951C54F9cd08Cf81F770248e835D7A4F491a46",
@@ -144401,6 +144491,11 @@
}
],
"withdrawals": [
{
"amount": "0",
"user": "0x759C9ABABA492500c4c730bEB568B5b851Dec2c7",
"tx": "0x7b20f640f6947ca553bb2ffb572e316f155781b051b198911b234c6c40e79eaa"
},
{
"amount": "0",
"user": "0x759C9ABABA492500c4c730bEB568B5b851Dec2c7",
@@ -144696,6 +144791,12 @@
}
],
"withdrawals": [
{
"amount": "0",
"user": "0x759C9ABABA492500c4c730bEB568B5b851Dec2c7",
"tranche_id": 9,
"tx": "0x7b20f640f6947ca553bb2ffb572e316f155781b051b198911b234c6c40e79eaa"
},
{
"amount": "0",
"user": "0x759C9ABABA492500c4c730bEB568B5b851Dec2c7",
@@ -12,6 +12,7 @@ import {
createMarketsDataFragment,
assetQuery,
networkParamsQuery,
nodeGuardQuery,
} from '@vegaprotocol/mock';
import {
addDecimalsFormatNumber,
@@ -158,6 +159,7 @@ describe('Closed markets', { tags: '@smoke' }, () => {
cy.mockGQL((req) => {
aliasGQLQuery(req, 'ChainId', chainIdQuery());
aliasGQLQuery(req, 'Statistics', statisticsQuery());
aliasGQLQuery(req, 'NodeGuard', nodeGuardQuery());
aliasGQLQuery(req, 'NetworkParams', networkParamsQuery());
aliasGQLQuery(
req,
@@ -6,6 +6,7 @@ const row = 'key-value-table-row';
const marketTitle = 'accordion-title';
const externalLink = 'external-link';
const accordionContent = 'accordion-content';
const providerName = 'provider-name';
describe('market info is displayed', { tags: '@smoke' }, () => {
beforeEach(() => {
@@ -181,9 +182,20 @@ describe('market info is displayed', { tags: '@smoke' }, () => {
cy.getByTestId(marketTitle).contains('Oracle').click();
cy.getByTestId(accordionContent)
.getByTestId('provider-name')
.getByTestId(providerName)
.and('contain', 'Another oracle');
cy.getByTestId(providerName).should('be.visible').click();
cy.getByTestId('dialog-content')
.eq(1)
.within(() => {
cy.getByTestId('block-explorer-link').contains('Block explorer');
cy.getByTestId('github-link').contains('Oracle repository');
cy.getByTestId('verified-accounts').contains('0 proofs of ownership');
});
cy.getByTestId('dialog-close').click();
cy.getByTestId(accordionContent)
.getByTestId('verified-proofs')
.and('contain', '1');
@@ -17,7 +17,6 @@ describe('ethereum wallet', { tags: '@smoke', testIsolation: true }, () => {
it('can connect', () => {
// 0004-EWAL-001
cy.wait('@NetworkParams');
cy.getByTestId('Deposits').click();
cy.getByTestId('deposit-button').click();
cy.getByTestId('connect-eth-wallet-btn').click();
@@ -30,7 +29,6 @@ describe('ethereum wallet', { tags: '@smoke', testIsolation: true }, () => {
it('should see QR code modal for WalletConnect', () => {
// 0004-EWAL-003
cy.wait('@NetworkParams');
cy.getByTestId('Deposits').click();
cy.getByTestId('deposit-button').click();
cy.getByTestId('connect-eth-wallet-btn').click();
+2
View File
@@ -20,6 +20,7 @@ import {
marketsDataQuery,
marketsQuery,
networkParamsQuery,
nodeGuardQuery,
ordersQuery,
positionsQuery,
proposalListQuery,
@@ -82,6 +83,7 @@ const mockTradingPage = (
) => {
aliasGQLQuery(req, 'ChainId', chainIdQuery());
aliasGQLQuery(req, 'Statistics', statisticsQuery());
aliasGQLQuery(req, 'NodeGuard', nodeGuardQuery());
aliasGQLQuery(
req,
'Markets',
@@ -1,5 +1,6 @@
import type { InMemoryCacheConfig } from '@apollo/client';
import {
AppFailure,
NetworkLoader,
NodeGuard,
useEnvironment,
@@ -9,7 +10,6 @@ import { MaintenancePage } from '@vegaprotocol/ui-toolkit';
import { VegaWalletProvider } from '@vegaprotocol/wallet';
import dynamic from 'next/dynamic';
import type { ReactNode } from 'react';
import { AppFailure } from './app-failure';
import { Web3Provider } from './web3-provider';
export const DynamicLoader = dynamic(() => import('../preloader/preloader'), {
@@ -1,3 +1,2 @@
export * from './app-failure';
export * from './app-loader';
export * from './web3-provider';
@@ -4,18 +4,15 @@ import { NodeHealth, NodeUrl, HealthIndicator } from './footer';
import { MockedProvider } from '@apollo/client/testing';
import { Intent } from '@vegaprotocol/ui-toolkit';
const mockSetNodeSwitcher = jest.fn();
jest.mock('@vegaprotocol/environment', () => ({
...jest.requireActual('@vegaprotocol/environment'),
useEnvironment: jest.fn().mockImplementation(() => ({
VEGA_URL: 'https://vega-url.wtf',
VEGA_INCIDENT_URL: 'https://blog.vega.community',
})),
}));
const mockSetNodeSwitcher = jest.fn();
jest.mock('../../stores', () => ({
...jest.requireActual('../../stores'),
useGlobalStore: () => mockSetNodeSwitcher,
useNodeSwitcherStore: jest.fn(() => mockSetNodeSwitcher),
}));
describe('NodeHealth', () => {
+6 -5
View File
@@ -1,11 +1,14 @@
import { useCallback } from 'react';
import { useEnvironment, useNodeHealth } from '@vegaprotocol/environment';
import {
useEnvironment,
useNodeHealth,
useNodeSwitcherStore,
} from '@vegaprotocol/environment';
import { t } from '@vegaprotocol/i18n';
import type { Intent } from '@vegaprotocol/ui-toolkit';
import { Indicator, ExternalLink } from '@vegaprotocol/ui-toolkit';
import classNames from 'classnames';
import type { ButtonHTMLAttributes, ReactNode } from 'react';
import { useGlobalStore } from '../../stores';
export const Footer = () => {
return (
@@ -20,9 +23,7 @@ export const Footer = () => {
export const NodeHealth = () => {
const { VEGA_URL, VEGA_INCIDENT_URL } = useEnvironment();
const setNodeSwitcher = useGlobalStore(
(store) => (open: boolean) => store.update({ nodeSwitcherDialog: open })
);
const setNodeSwitcher = useNodeSwitcherStore((store) => store.setDialogOpen);
const { datanodeBlockHeight, text, intent } = useNodeHealth();
const onClick = useCallback(() => {
setNodeSwitcher(true);
@@ -7,7 +7,13 @@ import {
} from '@vegaprotocol/ui-toolkit';
import { RISK_ACCEPTED_KEY } from '../constants';
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';
interface Props {
@@ -46,8 +52,9 @@ export const RiskNoticeDialog = ({ onClose, network }: Props) => {
};
const TestnetContent = ({ network }: { network: Networks }) => {
const { VEGA_DOCS_URL, GITHUB_FEEDBACK_URL, VEGA_WALLET_URL } =
useEnvironment();
const { GITHUB_FEEDBACK_URL } = useEnvironment();
const docsLink = useDocsLink();
return (
<>
<p className="mb-4">
@@ -62,17 +69,17 @@ const TestnetContent = ({ network }: { network: Networks }) => {
[network]
)}
</p>
{VEGA_DOCS_URL && GITHUB_FEEDBACK_URL && VEGA_WALLET_URL && (
{GITHUB_FEEDBACK_URL && GET_VEGA_WALLET_URL && docsLink && (
<ul className="list-disc pl-4">
<li className="mb-1">
<Link href={VEGA_WALLET_URL} target="_blank">
<span className="underline">{t('Learn about Vega wallet')}</span>{' '}
<Link href={GET_VEGA_WALLET_URL} target="_blank">
<span className="underline">{t('Get a Vega Wallet')}</span>{' '}
<VegaIcon name={VegaIconNames.OPEN_EXTERNAL} />
</Link>
</li>
<li className="mb-1">
<Link href={VEGA_DOCS_URL} target="_blank">
<span className="underline">{t('View documentation')}</span>{' '}
<Link href={docsLink(DOCS_VEGA_WALLET)} target="_blank">
<span className="underline">{t('Learn about Vega Wallet')}</span>{' '}
<VegaIcon name={VegaIconNames.OPEN_EXTERNAL} />
</Link>
</li>
+6 -6
View File
@@ -21,9 +21,10 @@ import {
NodeSwitcherDialog,
useEnvironment,
useInitializeEnv,
useNodeSwitcherStore,
} from '@vegaprotocol/environment';
import './styles.css';
import { useGlobalStore, usePageTitleStore } from '../stores';
import { usePageTitleStore } from '../stores';
import { Footer } from '../components/footer';
import DialogsContainer from './dialogs-container';
import ToastsManager from './toasts-manager';
@@ -115,11 +116,10 @@ function AppBody({ Component }: AppProps) {
function VegaTradingApp(props: AppProps) {
const status = useEnvironment((store) => store.status);
const { nodeSwitcherOpen, setNodeSwitcher } = useGlobalStore((store) => ({
nodeSwitcherOpen: store.nodeSwitcherDialog,
setNodeSwitcher: (open: boolean) =>
store.update({ nodeSwitcherDialog: open }),
}));
const [nodeSwitcherOpen, setNodeSwitcher] = useNodeSwitcherStore((store) => [
store.dialogOpen,
store.setDialogOpen,
]);
useInitializeEnv();
-2
View File
@@ -3,7 +3,6 @@ import { create } from 'zustand';
import produce from 'immer';
interface GlobalStore {
nodeSwitcherDialog: boolean;
marketId: string | null;
update: (store: Partial<Omit<GlobalStore, 'update'>>) => void;
shouldDisplayWelcomeDialog: boolean;
@@ -15,7 +14,6 @@ interface PageTitleStore {
}
export const useGlobalStore = create<GlobalStore>()((set) => ({
nodeSwitcherDialog: false,
marketId: LocalStorage.getItem('marketId') || null,
shouldDisplayWelcomeDialog: false,
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
@@ -0,0 +1,280 @@
import { VegaDataSource } from './data-source';
import type { ApolloClient } from '@apollo/client';
import { Interval } from 'pennant';
import type {
CandleFieldsFragment,
CandlesQuery,
} from './__generated__/Candles';
import * as Schema from '@vegaprotocol/types';
const returnDataMocks = (nodes: CandleFieldsFragment[]): CandlesQuery => {
return {
data: {
market: {
decimalPlaces: 1,
positionDecimalPlaces: 1,
candlesConnection: {
edges: nodes.map((node) => ({ node })),
},
},
},
} as CandlesQuery;
};
const dataMocks: { [key in Schema.Interval]: Partial<CandleFieldsFragment>[] } =
{
[Schema.Interval.INTERVAL_I1M]: [
{
__typename: 'Candle',
periodStart: '2023-05-10T12:00:00Z',
lastUpdateInPeriod: '',
close: '10',
volume: '1',
},
{
__typename: 'Candle',
periodStart: '2023-05-10T12:05:00Z',
lastUpdateInPeriod: '',
close: '5',
volume: '2',
},
],
[Schema.Interval.INTERVAL_I5M]: [
{
__typename: 'Candle',
periodStart: '2023-05-10T12:00:00Z',
lastUpdateInPeriod: '',
close: '10',
volume: '1',
},
{
__typename: 'Candle',
periodStart: '2023-05-10T12:25:00Z',
lastUpdateInPeriod: '',
close: '5',
volume: '2',
},
],
[Schema.Interval.INTERVAL_I15M]: [
{
__typename: 'Candle',
periodStart: '2023-05-10T12:00:00Z',
lastUpdateInPeriod: '',
close: '10',
volume: '1',
},
{
__typename: 'Candle',
periodStart: '2023-05-10T13:15:00Z',
lastUpdateInPeriod: '',
close: '5',
volume: '2',
},
],
[Schema.Interval.INTERVAL_I1H]: [
{
__typename: 'Candle',
periodStart: '2023-05-10T12:00:00Z',
lastUpdateInPeriod: '',
close: '10',
volume: '1',
},
{
__typename: 'Candle',
periodStart: '2023-05-10T17:00:00Z',
lastUpdateInPeriod: '',
close: '5',
volume: '2',
},
],
[Schema.Interval.INTERVAL_I6H]: [
{
__typename: 'Candle',
periodStart: '2023-05-10T12:00:00Z',
lastUpdateInPeriod: '',
close: '10',
volume: '1',
},
{
__typename: 'Candle',
periodStart: '2023-05-11T18:00:00Z',
lastUpdateInPeriod: '',
close: '5',
volume: '2',
},
],
[Schema.Interval.INTERVAL_I1D]: [
{
__typename: 'Candle',
periodStart: '2023-05-10T00:00:00Z',
lastUpdateInPeriod: '',
close: '10',
volume: '1',
},
{
__typename: 'Candle',
periodStart: '2023-05-15T00:00:00Z',
lastUpdateInPeriod: '',
close: '5',
volume: '2',
},
],
[Schema.Interval.INTERVAL_BLOCK]: [],
};
describe('VegaDataSource', () => {
const marketId = 'marketId';
const partyId = 'partyId';
const client = {
query: jest.fn().mockImplementation(({ variables: { interval } }) => {
return returnDataMocks(
dataMocks[interval as Schema.Interval] as CandleFieldsFragment[]
);
}),
} as unknown as ApolloClient<object>;
it('should be properly initialized', () => {
const dataSource = new VegaDataSource(client, marketId, partyId);
expect(dataSource).toBeInstanceOf(VegaDataSource);
expect(dataSource.onReady).toBeDefined();
expect(dataSource.query).toBeDefined();
expect(dataSource.subscribeData).toBeDefined();
expect(dataSource.unsubscribeData).toBeDefined();
expect(dataSource.decimalPlaces).toBeDefined();
expect(dataSource.positionDecimalPlaces).toBeDefined();
});
describe('query should return continuous data', () => {
it('when interval is I1M', async () => {
const dataSource = new VegaDataSource(client, marketId, partyId);
const data = await dataSource.query(Interval.I1M, '');
expect(data).toHaveLength(6);
expect(data[1]).toStrictEqual({
date: new Date('2023-05-10T12:01:00Z'),
high: 1,
low: 1,
open: 1,
close: 1,
volume: 0,
});
expect(data[2]).toStrictEqual({
date: new Date('2023-05-10T12:02:00Z'),
high: 1,
low: 1,
open: 1,
close: 1,
volume: 0,
});
});
it('when interval is I5M', async () => {
const dataSource = new VegaDataSource(client, marketId, partyId);
const data = await dataSource.query(Interval.I5M, '');
expect(data).toHaveLength(6);
expect(data[1]).toStrictEqual({
date: new Date('2023-05-10T12:05:00Z'),
high: 1,
low: 1,
open: 1,
close: 1,
volume: 0,
});
expect(data[2]).toStrictEqual({
date: new Date('2023-05-10T12:10:00Z'),
high: 1,
low: 1,
open: 1,
close: 1,
volume: 0,
});
});
it('when interval is I15M', async () => {
const dataSource = new VegaDataSource(client, marketId, partyId);
const data = await dataSource.query(Interval.I15M, '');
expect(data).toHaveLength(6);
expect(data[1]).toStrictEqual({
date: new Date('2023-05-10T12:15:00Z'),
high: 1,
low: 1,
open: 1,
close: 1,
volume: 0,
});
expect(data[2]).toStrictEqual({
date: new Date('2023-05-10T12:30:00Z'),
high: 1,
low: 1,
open: 1,
close: 1,
volume: 0,
});
});
it('when interval is I1H', async () => {
const dataSource = new VegaDataSource(client, marketId, partyId);
const data = await dataSource.query(Interval.I1H, '');
expect(data).toHaveLength(6);
expect(data[1]).toStrictEqual({
date: new Date('2023-05-10T13:00:00Z'),
high: 1,
low: 1,
open: 1,
close: 1,
volume: 0,
});
expect(data[2]).toStrictEqual({
date: new Date('2023-05-10T14:00:00Z'),
high: 1,
low: 1,
open: 1,
close: 1,
volume: 0,
});
});
it('when interval is I6H', async () => {
const dataSource = new VegaDataSource(client, marketId, partyId);
const data = await dataSource.query(Interval.I6H, '');
expect(data).toHaveLength(6);
expect(data[1]).toStrictEqual({
date: new Date('2023-05-10T18:00:00Z'),
high: 1,
low: 1,
open: 1,
close: 1,
volume: 0,
});
expect(data[2]).toStrictEqual({
date: new Date('2023-05-11T00:00:00Z'),
high: 1,
low: 1,
open: 1,
close: 1,
volume: 0,
});
});
it('when interval is I1D', async () => {
const dataSource = new VegaDataSource(client, marketId, partyId);
const data = await dataSource.query(Interval.I1D, '');
expect(data).toHaveLength(6);
expect(data[1]).toStrictEqual({
date: new Date('2023-05-11T00:00:00Z'),
high: 1,
low: 1,
open: 1,
close: 1,
volume: 0,
});
expect(data[2]).toStrictEqual({
date: new Date('2023-05-12T00:00:00Z'),
high: 1,
low: 1,
open: 1,
close: 1,
volume: 0,
});
});
});
});
+88 -3
View File
@@ -1,4 +1,11 @@
import type { ApolloClient } from '@apollo/client';
import type { Duration } from 'date-fns';
import {
add,
differenceInDays,
differenceInHours,
differenceInMinutes,
} from 'date-fns';
import type { Candle, DataSource } from 'pennant';
import { Interval as PennantInterval } from 'pennant';
@@ -153,7 +160,6 @@ export class VegaDataSource implements DataSource {
},
fetchPolicy: 'no-cache',
});
if (data?.market?.candlesConnection?.edges) {
const decimalPlaces = data.market.decimalPlaces;
const positionDecimalPlaces = data.market.positionDecimalPlaces;
@@ -163,8 +169,8 @@ export class VegaDataSource implements DataSource {
.filter((node): node is CandleFieldsFragment => !!node)
.map((node) =>
parseCandle(node, decimalPlaces, positionDecimalPlaces)
);
)
.reduce(checkGranulationContinuity(interval), []);
return candles;
} else {
return [];
@@ -213,6 +219,85 @@ export class VegaDataSource implements DataSource {
}
}
const getDuration = (
interval: PennantInterval,
multiplier: number
): Duration => {
switch (interval) {
case 'I1D':
return {
days: 1 * multiplier,
};
case 'I1H':
return {
hours: 1 * multiplier,
};
case 'I1M':
return {
minutes: 1 * multiplier,
};
case 'I5M':
return {
minutes: 5 * multiplier,
};
case 'I6H':
return {
hours: 6 * multiplier,
};
case 'I15M':
return {
minutes: 15 * multiplier,
};
}
};
const getDifference = (
interval: PennantInterval,
dateLeft: Date,
dateRight: Date
): number => {
switch (interval) {
case 'I1D':
return differenceInDays(dateRight, dateLeft);
case 'I6H':
return differenceInHours(dateRight, dateLeft) / 6;
case 'I1H':
return differenceInHours(dateRight, dateLeft);
case 'I15M':
return differenceInMinutes(dateRight, dateLeft) / 15;
case 'I5M':
return differenceInMinutes(dateRight, dateLeft) / 5;
case 'I1M':
return differenceInMinutes(dateRight, dateLeft);
}
};
const checkGranulationContinuity =
(interval: PennantInterval) =>
(agg: Candle[], candle: Candle, i: number): Candle[] => {
if (agg.length && i) {
const previous = agg[agg.length - 1];
const difference = getDifference(interval, previous.date, candle.date);
if (difference > 1) {
for (let j = 1; j < difference; j++) {
const duration = getDuration(interval, j);
const newStartDate = add(previous.date, duration);
const newParsedCandle: Candle = {
date: newStartDate,
high: previous.close,
low: previous.close,
open: previous.close,
close: previous.close,
volume: 0,
};
agg.push(newParsedCandle);
}
}
}
agg.push(candle);
return agg;
};
function parseCandle(
candle: CandleFieldsFragment,
decimalPlaces: number,
+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 '../deposits/src/lib/deposit.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 '../proposals/src/lib/proposals-data-provider/proposals.mock';
export * from '../ledger/src/lib/ledger-entries.mock';
@@ -97,7 +97,7 @@ export const useColumnSizes = ({
const setSizes = useCallback(
(apiEvent: GridReadyEvent | GridSizeChangedEvent) => {
if (!storeKey || !Object.keys(sizes).length || !widthRef.current) {
apiEvent.api.sizeColumnsToFit();
apiEvent?.api.sizeColumnsToFit();
} else {
const recalculatedSizes = recalculateSizes(sizes);
const newSizes = Object.entries(recalculatedSizes).map(
@@ -1,6 +1,6 @@
import { t } from '@vegaprotocol/i18n';
import { Button } from '@vegaprotocol/ui-toolkit';
import { useGlobalStore } from '../../stores';
import { useNodeSwitcherStore } from '../../hooks/use-node-switcher-store';
export const AppFailure = ({
title,
@@ -9,17 +9,13 @@ export const AppFailure = ({
title: string;
error?: string | null;
}) => {
const { setNodeSwitcher } = useGlobalStore((store) => ({
nodeSwitcherOpen: store.nodeSwitcherDialog,
setNodeSwitcher: (open: boolean) =>
store.update({ nodeSwitcherDialog: open }),
}));
const setNodeSwitcher = useNodeSwitcherStore((store) => store.setDialogOpen);
const nonIdealWrapperClasses =
'h-full min-h-screen flex items-center justify-center';
return (
<div className={nonIdealWrapperClasses}>
<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>}
<Button onClick={() => setNodeSwitcher(true)}>
{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-switcher';
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 { useStatisticsQuery } from '../../utils/__generated__/Node';
import { useNodeGuardQuery } from './__generated__/NodeGuard';
export const NodeGuard = ({
children,
@@ -10,14 +10,19 @@ export const NodeGuard = ({
failure: ReactNode;
skeleton: ReactNode;
}) => {
const { error, loading } = useStatisticsQuery();
const wrapperClasses = 'h-full min-h-screen flex items-center justify-center';
const { data, error, loading } = useNodeGuardQuery();
const wrapperClasses =
'h-full min-h-screen flex items-center justify-center text-black dark:text-white';
if (loading) {
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>;
}
@@ -96,13 +96,13 @@ describe('RowData', () => {
mockHeaders(props.url);
render(renderComponent(props, statsQueryMock, subMock));
// radio should be disabled until query resolves
// radio should be enabled until query resolves
expect(
screen.getByRole('radio', {
checked: false,
name: props.url,
})
).toBeDisabled();
).toBeEnabled();
expect(screen.getByTestId('response-time-cell')).toHaveTextContent(
'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, {});
const failedQueryMock: MockedResponse<StatisticsQuery> = {
@@ -149,7 +149,7 @@ describe('RowData', () => {
checked: false,
name: props.url,
})
).toBeDisabled();
).toBeEnabled();
expect(screen.getByTestId('response-time-cell')).toHaveTextContent(
'Checking'
);
@@ -170,7 +170,7 @@ describe('RowData', () => {
checked: false,
name: props.url,
})
).toBeDisabled();
).toBeEnabled();
});
});
@@ -207,19 +207,6 @@ describe('RowData', () => {
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', () => {
render(
renderComponent(
@@ -124,29 +124,6 @@ export const RowData = ({
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 = (
subError: ApolloError | undefined,
subFailed: boolean
@@ -160,12 +137,7 @@ export const RowData = ({
<>
{id !== CUSTOM_NODE_KEY && (
<div className="break-all" data-testid="node">
<Radio
id={`node-url-${id}`}
value={url}
label={url}
disabled={getIsNodeDisabled()}
/>
<Radio id={`node-url-${id}`} value={url} label={url} />
</div>
)}
<LayoutCell
+1
View File
@@ -1,5 +1,6 @@
export * from './use-environment';
export * from './use-links';
export * from './use-node-health';
export * from './use-node-switcher-store';
export * from './use-vega-releases';
export * from './use-vega-release';
@@ -15,6 +15,7 @@ import { Networks } from '../types';
import { compileErrors } from '../utils/compile-errors';
import { envSchema } from '../utils/validate-environment';
import { tomlConfigSchema } from '../utils/validate-configuration';
import uniq from 'lodash/uniq';
type Client = ReturnType<typeof createClient>;
type ClientCollection = {
@@ -32,7 +33,8 @@ type Actions = {
export type Env = Environment & EnvState;
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;
export const useEnvironment = create<EnvStore>()((set, get) => ({
@@ -69,7 +71,10 @@ export const useEnvironment = create<EnvStore>()((set, get) => ({
let nodes: string[] | undefined;
try {
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) {
console.warn(`Could not fetch node config from ${state.VEGA_CONFIG_URL}`);
}
+16
View File
@@ -84,6 +84,16 @@ export const useEtherscanLink = () => {
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
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_PORTFOLIO = '/portfolio';
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 { Link, Lozenge } from '@vegaprotocol/ui-toolkit';
import { NodeSwitcherDialog, useEnvironment } from '@vegaprotocol/environment';
import {
NodeSwitcherDialog,
useEnvironment,
useNodeSwitcherStore,
} from '@vegaprotocol/environment';
const getFeedbackLinks = (gitOriginUrl?: string) =>
[
@@ -19,64 +23,59 @@ export const NetworkInfo = () => {
GITHUB_FEEDBACK_URL,
ETHEREUM_PROVIDER_URL,
} = useEnvironment();
const [nodeSwitcherOpen, setNodeSwitcherOpen] = useState(false);
const setNodeSwitcher = useNodeSwitcherStore((store) => store.setDialogOpen);
const feedbackLinks = getFeedbackLinks(GITHUB_FEEDBACK_URL);
return (
<>
<div data-testid="git-info">
<p data-testid="git-network-data" className="mb-2">
{t('Reading network data from')}{' '}
<Lozenge className="bg-neutral-300 dark:bg-neutral-700">
{VEGA_URL}
</Lozenge>
. <Link onClick={() => setNodeSwitcherOpen(true)}>{t('Edit')}</Link>
<div data-testid="git-info">
<p data-testid="git-network-data" className="mb-2">
{t('Reading network data from')}{' '}
<Lozenge className="bg-neutral-300 dark:bg-neutral-700">
{VEGA_URL}
</Lozenge>
. <Link onClick={() => setNodeSwitcher(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 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>
.{' '}
)}
{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>
{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>
)}
{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}
/>
</>
)}
</div>
);
};
+1 -1
View File
@@ -68,7 +68,7 @@ inquirer
})
.then(() => {
for (const env of answers.envs) {
const branch = env === 'mainnet' ? 'master' : `release/${env}`;
const branch = env === 'mainnet' ? 'main' : `release/${env}`;
release(answers.tag, branch);
}
execSync('git checkout develop');