Compare commits

..
Author SHA1 Message Date
m.ray 45c3453af6 Update apps/governance/.env 2023-10-03 12:25:27 +01:00
Madalina Raicu 01fa0657e6 chore: add wallet connect project ID 2023-10-03 12:23:42 +01:00
Bartłomiej Głownia 60ca6c2eb6 feat(trading): show fills for all markets (#4951) 2023-10-03 11:00:26 +01:00
Art eeff4ffcd4 fix(trading): missing wallet connect button (#4960) 2023-10-03 10:53:32 +01:00
Radosław Szpiech 73ae00f12c chore(ci): add missing quote marks that may have caused some workflow fails (#4963) 2023-10-03 10:44:12 +01:00
Radosław Szpiech 8b249e1917 chore(ci): fix some minor issues in workflows (#4953) 2023-10-02 16:12:24 +02:00
Radosław Szpiech abb771e2f9 chore(ci): add conditions to run e2e tests (#4946) 2023-10-02 14:29:59 +02:00
Edd acf1d50d0f chore(explorer): ignore errors on oracle page (#4944) 2023-09-29 17:29:16 +01:00
Radosław Szpiech 30da1663eb chore(ci): add main develop merge condition to runner (#4941) 2023-09-29 17:36:18 +02:00
Radosław Szpiech 79cbe62774 chore(ci): add mainnet compatible runner to release mainnet branch push actions (#4939) 2023-09-29 11:00:20 -04:00
Maciek 2f0be0bf34 chore(trading): 4774 put connect dialog inside onboarding dialog - main branch (#4920) 2023-09-29 15:07:18 +02:00
Radosław Szpiech 5b5802104e chore(ci): trigger lint-pr on repoened and sync (#4937) 2023-09-29 15:03:58 +02:00
Maciek ff2e2574f6 chore(trading): add handle of autosizing selected columns - main branch (#4888) 2023-09-28 15:14:32 -04:00
Radosław Szpiech 478cc9e753 chore(ci): publish main image in dockerhub (#4930) 2023-09-28 17:38:00 +02:00
Radosław Szpiech e2a72cb395 chore(ci): separate lint pr (main) (#4917) 2023-09-28 14:01:36 +02:00
Radosław Szpiech 7fe269fad6 chore(ci): fix node version (main) (#4911) 2023-09-28 11:26:15 +02:00
Radosław Szpiech b761023069 chore(ci): add logic for choosing correct runner for cypress tests depending on base branch (#4918) 2023-09-28 11:25:44 +02:00
Radosław Szpiech 7ac3a68ac9 chore(trading): add possibility to manualy trigger console-test (#4899) 2023-09-27 15:18:32 +02:00
Radosław Szpiech 2640ccb20a chore(trading): change runner for mainnet compatible vega version (#4907) 2023-09-27 14:42:12 +02:00
Maciek d78de10855 chore(trading): remove underlines on tooltips (#4884) 2023-09-26 13:45:33 -04:00
Matthew Russell bb402c02f6 chore(trading): make volume bars color match candle color (#4881) 2023-09-26 12:08:12 -04:00
Bartłomiej Głownia 8a9b1c7874 feat(deal-ticket): amend fee estimates for postOnly orders and when market is in auction (#4843) 2023-09-26 11:57:47 -04:00
Matthew Russell a7e8b0eb01 fix(market-depth): ensure scroll bar doesn't show on orderbook (#4882) 2023-09-26 11:54:42 -04:00
Bartłomiej Głownia 89b3c06107 feat(deal-ticket): show worst case instead of ranges in estimated values (#4870) 2023-09-26 11:53:45 -04:00
Radosław Szpiech cd5c73d3fd chore(trading): temporary disable console-test to unblock CI queue (#4885) 2023-09-26 10:45:27 +01:00
Joe Tsang b3a5ab022d chore(ci): update genesis file (#4887) 2023-09-26 10:44:55 +01:00
Mikołaj Młodzikowski 496d1f5c68 feat(ci): parse if there are any changes 2023-09-25 18:10:01 -04:00
Mikołaj Młodzikowski e914e7bb70 fix(ci): provide proper ipfs process 2023-09-25 18:09:31 -04:00
Matthew Russell 71a36c2382 fix(trading): remove upgrade banner [main] (#4877) 2023-09-25 17:27:01 -04:00
Matthew Russell 1c6a307bcd fix(wallet): reduce time to check for wallet running [main] (#4876) 2023-09-25 17:26:40 -04:00
Matthew Russell ef4a740b91 fix(trading): allotment breaking [main] (#4875) 2023-09-25 17:26:06 -04:00
Sam Keen 2d4be5fcb3 fix(governance): mainnet rewards query looping (#4865) 2023-09-25 09:59:38 +00:00
m.rayandMatthew Russell 835f2b793f chore(trading): remove static assets console coming from vega.xyz (#4825)
Co-authored-by: Matthew Russell <mattrussell36@gmail.com>
2023-09-19 23:54:07 -07:00
Matthew Russell d50b988b4a Merge branch 'release/mainnet-trading' of github.com:vegaprotocol/frontend-monorepo into release/mainnet-trading 2023-09-18 09:03:44 -07:00
Madalina Raicu a6aec899c8 chore: update tag metamask snaps to true 2023-09-18 15:08:46 +01:00
111 changed files with 713 additions and 2035 deletions
+33 -17
View File
@@ -5,12 +5,12 @@ on:
branches: branches:
- release/* - release/*
- develop - develop
- main
pull_request: pull_request:
types: types:
- opened - opened
- ready_for_review - ready_for_review
- reopened - reopened
- edited
- synchronize - synchronize
jobs: jobs:
node-modules: node-modules:
@@ -42,13 +42,6 @@ jobs:
if: steps.cache.outputs.cache-hit != 'true' if: steps.cache.outputs.cache-hit != 'true'
run: yarn install --pure-lockfile run: yarn install --pure-lockfile
lint-pr-title:
needs: node-modules
if: ${{ github.event_name == 'pull_request' }}
name: Verify PR title
uses: ./.github/workflows/lint-pr.yml
secrets: inherit
lint-format: lint-format:
timeout-minutes: 20 timeout-minutes: 20
needs: node-modules needs: node-modules
@@ -175,19 +168,42 @@ jobs:
preview_explorer: ${{ env.PREVIEW_EXPLORER }} preview_explorer: ${{ env.PREVIEW_EXPLORER }}
preview_tools: ${{ env.PREVIEW_TOOLS }} preview_tools: ${{ env.PREVIEW_TOOLS }}
console-e2e: # console-e2e:
# needs: build-sources
# name: '(CI) console python'
# uses: ./.github/workflows/console-test-run.yml
# secrets: inherit
# if: ${{ contains(fromJSON(needs.build-sources.outputs.projects), 'trading') && github.event_name == 'pull_request' }}
# with:
# github-sha: ${{ github.event.pull_request.head.sha || github.sha }}
check-e2e-needed:
runs-on: ubuntu-latest
needs: build-sources needs: build-sources
name: '(CI) console python' name: '(CI) check if e2e needed'
uses: ./.github/workflows/console-test-run.yml outputs:
secrets: inherit run-tests: ${{ steps.check-test.outputs.e2e-needed }}
if: ${{ contains(fromJSON(needs.build-sources.outputs.projects), 'trading') && github.event_name == 'pull_request' }} steps:
with: - name: Check branch
github-sha: ${{ github.event.pull_request.head.sha || github.sha }} id: check-test
run: |
if [ '${{ github.base_ref }}' == 'develop' ]; then
echo "e2e-needed=true" >> $GITHUB_OUTPUT
elif [ '${{ github.base_ref }}' == 'main' ]; then
echo "e2e-needed=true" >> $GITHUB_OUTPUT
elif [[ '${{ github.event_name }}' == 'push' && ${{ contains(github.ref_name, 'release/') }} ]]; then
echo "e2e-needed=true" >> $GITHUB_OUTPUT
else
echo "e2e-needed=false" >> $GITHUB_OUTPUT
fi
- name: Print result
run: |
echo "e2e-needed: ${{ steps.check-test.outputs.e2e-needed }}"
cypress: cypress:
needs: build-sources needs: [build-sources, check-e2e-needed]
name: '(CI) cypress' name: '(CI) cypress'
# if: ${{ needs.build-sources.outputs.projects-e2e != '[]' }} if: ${{ needs.check-e2e-needed.outputs.run-tests == 'true' }}
uses: ./.github/workflows/cypress-run.yml uses: ./.github/workflows/cypress-run.yml
secrets: inherit secrets: inherit
with: with:
+43 -28
View File
@@ -1,31 +1,42 @@
name: (CI) Console tests name: (CI) Console tests
env:
VEGA_VERSION: v0.72.14
on: on:
workflow_call: workflow_call:
inputs: inputs:
github-sha: github-sha:
required: true required: true
type: string type: string
workflow_dispatch:
inputs:
console-test-branch:
type: choice
description: 'main: v0.72.14, develop: v0.73.0-preview7'
options:
- main
- develop
jobs: jobs:
run-tests: run-tests:
name: run-tests name: run-tests
runs-on: console-test runs-on: 8-cores
timeout-minutes: 40 timeout-minutes: 20
steps: steps:
#---------------------------------------------- #----------------------------------------------
# check-out frontend-monorepo # check-out frontend-monorepo
#---------------------------------------------- #----------------------------------------------
- name: Checkout console test repo - name: Checkout frontend-monorepo
uses: actions/checkout@v3 uses: actions/checkout@v3
with: with:
ref: ${{ inputs.github-sha }} ref: ${{ inputs.github-sha || github.sha }}
#---------------------------------------------- #----------------------------------------------
# cache node modules # cache node modules
#---------------------------------------------- #----------------------------------------------
- name: setup node
uses: actions/setup-node@v3
with:
node-version: '16'
cache: yarn
- name: Cache node modules - name: Cache node modules
id: cache id: cache
uses: actions/cache@v3 uses: actions/cache@v3
@@ -68,36 +79,30 @@ jobs:
uses: actions/checkout@v3 uses: actions/checkout@v3
with: with:
repository: vegaprotocol/console-test repository: vegaprotocol/console-test
ref: ${{ inputs.console-test-branch }}
path: './console-test' path: './console-test'
- name: Load console test envs
id: console-test-env
uses: falti/dotenv-action@v1.0.4
with:
path: './console-test/.env.${{ inputs.console-test-branch }}'
export-variables: true
keys-case: upper
log-variables: true
#---------------------------------------------- #----------------------------------------------
# install dependencies # install dependencies if cache does not exist
#---------------------------------------------- #----------------------------------------------
- name: Install dependencies - name: Install dependencies
working-directory: ./console-test working-directory: ./console-test
run: poetry install --no-interaction --no-root run: poetry install --no-interaction --no-root
#---------------------------------------------- #----------------------------------------------
# find vega binaries path # install vega binaries
#----------------------------------------------
- name: Find vega binaries path
id: vega_bin_path
working-directory: ./console-test
run: echo path=$(poetry run python -c "import vega_sim; print(vega_sim.vega_bin_path)") >> $GITHUB_OUTPUT
#----------------------------------------------
# vega binaries cache
#----------------------------------------------
- name: Vega binaries cache
uses: actions/cache@v3
id: vega_binaries_cache
with:
path: ${{ steps.vega_bin_path.outputs.path }}
key: ${{ runner.os }}-vega-binaries-${{ env.VEGA_VERSION }}
#----------------------------------------------
# install vega binaries
#---------------------------------------------- #----------------------------------------------
- name: Install vega binaries - name: Install vega binaries
working-directory: ./console-test working-directory: ./console-test
if: steps.vega_binaries_cache.outputs.cache-hit != 'true' run: poetry run python -m vega_sim.tools.load_binaries --force --version ${{ env.VEGA_VERSION }}
run: poetry run python -m vega_sim.tools.load_binaries --force --version $VEGA_VERSION
#---------------------------------------------- #----------------------------------------------
# install playwright # install playwright
#---------------------------------------------- #----------------------------------------------
@@ -109,7 +114,7 @@ jobs:
#---------------------------------------------- #----------------------------------------------
- name: Run tests - name: Run tests
working-directory: ./console-test working-directory: ./console-test
run: poetry run pytest -v -s --numprocesses 2 --dist loadfile --durations=20 run: poetry run pytest -v -s --numprocesses 4 --dist loadfile --durations=15
- name: Check files - name: Check files
run: | run: |
ls -al . ls -al .
@@ -124,3 +129,13 @@ jobs:
name: playwright-trace name: playwright-trace
path: ./traces/ path: ./traces/
retention-days: 15 retention-days: 15
#----------------------------------------------
# ----- upload logs -----
#----------------------------------------------
- name: Upload worker logs
uses: actions/upload-artifact@v3
if: always()
with:
name: worker-logs
path: ./logs/
retention-days: 15
+24 -1
View File
@@ -13,13 +13,35 @@ on:
type: string type: string
jobs: jobs:
runner-choice:
runs-on: ubuntu-latest
outputs:
runner: ${{ steps.step.outputs.runner }}
steps:
- name: Check branch
id: step
run: |
if [ '${{ github.base_ref }}' == 'main' ]; then
echo "runner=mainnet-compatible-runner" >> $GITHUB_OUTPUT
elif [[ '${{ github.base_ref }}' == 'develop' && '${{ github.ref_name }}' == 'main' ]]; then
echo "runner=mainnet-compatible-runner" >> $GITHUB_OUTPUT
elif [[ '${{ github.event_name }}' == 'push' && ${{ contains(github.ref_name, 'release/mainnet') }} ]]; then
echo "runner=mainnet-compatible-runner" >> $GITHUB_OUTPUT
else
echo "runner=self-hosted-runner" >> $GITHUB_OUTPUT
fi
- name: Print runner
run: echo ${{ steps.step.outputs.runner }}
e2e: e2e:
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
project: ${{ fromJSON(inputs.projects) }} project: ${{ fromJSON(inputs.projects) }}
name: ${{ matrix.project }} name: ${{ matrix.project }}
runs-on: self-hosted-runner needs: runner-choice
runs-on: ${{ needs.runner-choice.outputs.runner }}
timeout-minutes: 120 timeout-minutes: 120
steps: steps:
# Checks if skip cache was requested # Checks if skip cache was requested
@@ -63,6 +85,7 @@ jobs:
- name: Run Vegacapsule network and Vega wallet - name: Run Vegacapsule network and Vega wallet
id: setup-vega id: setup-vega
uses: ./frontend-monorepo/.github/actions/run-vegacapsule uses: ./frontend-monorepo/.github/actions/run-vegacapsule
timeout-minutes: 10
###### ######
## Run some tests ## Run some tests
+11 -11
View File
@@ -2,7 +2,12 @@
name: Verify PR title name: Verify PR title
on: on:
workflow_call: pull_request:
types:
- opened
- edited
- reopened
- synchronize
jobs: jobs:
lint_pr: lint_pr:
@@ -11,21 +16,16 @@ jobs:
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v3 uses: actions/checkout@v3
with:
ref: ${{ github.event.pull_request.head.sha || github.sha }}
- name: Setup node - name: Setup node
uses: actions/setup-node@v3 uses: actions/setup-node@v3
with: with:
node-version-file: '.nvmrc' node-version: 16
# https://stackoverflow.com/questions/61010294/how-to-cache-yarn-packages-in-github-actions
cache: yarn
- name: Cache node modules - name: Install dependencies
uses: actions/cache@v3 run: |
with: rm package.json
path: node_modules npm install --no-save @commitlint/cli @commitlint/config-conventional @commitlint/config-nx-scopes nx
key: ${{ runner.os }}-cache-node-modules-${{ hashFiles('yarn.lock') }}
- name: Check PR title - name: Check PR title
run: echo "${{ github.event.pull_request.title }}" | npx commitlint --config ./commitlint.config-ci.js run: echo "${{ github.event.pull_request.title }}" | npx commitlint --config ./commitlint.config-ci.js
+20 -12
View File
@@ -30,12 +30,18 @@ jobs:
echo IS_IPFS_RELEASE=false >> $GITHUB_ENV echo IS_IPFS_RELEASE=false >> $GITHUB_ENV
echo IS_S3_RELEASE=false >> $GITHUB_ENV echo IS_S3_RELEASE=false >> $GITHUB_ENV
echo IS_DEV_IMAGE=false >> $GITHUB_ENV echo IS_DEV_IMAGE=false >> $GITHUB_ENV
echo IS_MAIN_IMAGE=false >> $GITHUB_ENV
- name: Is dev image - name: Is dev image
if: ${{ contains(github.ref, 'develop') && github.event_name == 'push' && matrix.app == 'trading' }} if: ${{ github.ref_name == 'develop' && github.event_name == 'push' && matrix.app == 'trading' }}
run: | run: |
echo IS_DEV_IMAGE=true >> $GITHUB_ENV echo IS_DEV_IMAGE=true >> $GITHUB_ENV
- name: Is main image
if: ${{ github.ref_name == 'main' && github.event_name == 'push' && matrix.app == 'trading' }}
run: |
echo IS_MAIN_IMAGE=true >> $GITHUB_ENV
- name: Is PR - name: Is PR
if: ${{ github.event_name == 'pull_request' }} if: ${{ github.event_name == 'pull_request' }}
run: | run: |
@@ -57,7 +63,7 @@ jobs:
echo IS_IPFS_RELEASE=true >> $GITHUB_ENV echo IS_IPFS_RELEASE=true >> $GITHUB_ENV
- name: Is S3 Release - name: Is S3 Release
if: ${{ env.IS_IPFS_RELEASE == 'false' && github.event_name == 'push' }} if: ${{ env.IS_IPFS_RELEASE == 'false' && github.event_name == 'push' && github.ref_name != 'main'}}
run: | run: |
echo IS_S3_RELEASE=true >> $GITHUB_ENV echo IS_S3_RELEASE=true >> $GITHUB_ENV
@@ -81,7 +87,7 @@ jobs:
- name: Log in to the Container registry (docker hub) - name: Log in to the Container registry (docker hub)
uses: docker/login-action@v2 uses: docker/login-action@v2
if: ${{ env.IS_IPFS_RELEASE == 'true' || env.IS_DEV_IMAGE == 'true' }} if: ${{ env.IS_IPFS_RELEASE == 'true' || env.IS_DEV_IMAGE == 'true' || env.IS_MAIN_IMAGE == 'true' }}
with: with:
username: ${{ secrets.DOCKERHUB_USERNAME }} username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }} password: ${{ secrets.DOCKERHUB_TOKEN }}
@@ -155,10 +161,10 @@ jobs:
- name: Sanity check docker image - name: Sanity check docker image
run: | run: |
echo "Check ipfs-hash" echo "Check ipfs-hash"
docker run --rm --entrypoint /bin/sh ghcr.io/vegaprotocol/frontend/${{ matrix.app }}:local -c 'cat /ipfs-hash' docker run --rm ghcr.io/vegaprotocol/frontend/${{ matrix.app }}:local /bin/sh -c 'cat /ipfs-hash'
docker run --rm --entrypoint /bin/sh ghcr.io/vegaprotocol/frontend/${{ matrix.app }}:local -c 'cat /ipfs-hash' > ${{ matrix.app }}-ipfs-hash docker run --rm ghcr.io/vegaprotocol/frontend/${{ matrix.app }}:local /bin/sh -c 'cat /ipfs-hash' > ${{ matrix.app }}-ipfs-hash
echo "List html directory" echo "List html directory"
docker run --rm --entrypoint /bin/sh ghcr.io/vegaprotocol/frontend/${{ matrix.app }}:local -c 'apk add --update tree; tree /usr/share/nginx/html' docker run --rm ghcr.io/vegaprotocol/frontend/${{ matrix.app }}:local /bin/sh -c 'apk add --update tree; tree /usr/share/nginx/html'
- name: Publish dist as docker image (ghcr) - name: Publish dist as docker image (ghcr)
uses: docker/build-push-action@v3 uses: docker/build-push-action@v3
@@ -179,7 +185,7 @@ jobs:
uses: docker/build-push-action@v3 uses: docker/build-push-action@v3
continue-on-error: true continue-on-error: true
id: dockerhub-push id: dockerhub-push
if: ${{ env.IS_IPFS_RELEASE == 'true' || env.IS_DEV_IMAGE == 'true' }} if: ${{ env.IS_IPFS_RELEASE == 'true' || env.IS_DEV_IMAGE == 'true' || env.IS_MAIN_IMAGE == 'true' }}
with: with:
context: . context: .
file: docker/node-outside-docker.Dockerfile file: docker/node-outside-docker.Dockerfile
@@ -189,7 +195,7 @@ jobs:
ENV_NAME=${{ env.ENV_NAME }} ENV_NAME=${{ env.ENV_NAME }}
tags: | tags: |
vegaprotocol/${{ matrix.app }}:${{ github.sha }} vegaprotocol/${{ matrix.app }}:${{ github.sha }}
vegaprotocol/${{ matrix.app }}:${{ env.IS_MAINNET_RELEASE == 'true' && 'mainnet' || env.IS_TESTNET_RELEASE == 'true' && 'testnet' || env.IS_DEV_IMAGE == 'true' && 'develop' || '' }} vegaprotocol/${{ matrix.app }}:${{ env.IS_MAINNET_RELEASE == 'true' && 'mainnet' || env.IS_TESTNET_RELEASE == 'true' && 'testnet' || env.IS_DEV_IMAGE == 'true' && 'develop' || env.IS_MAIN_IMAGE == 'true' && 'main' || '' }}
- name: Publish dist as docker image (ghcr - retry) - name: Publish dist as docker image (ghcr - retry)
uses: docker/build-push-action@v3 uses: docker/build-push-action@v3
@@ -216,7 +222,7 @@ jobs:
ENV_NAME=${{ env.ENV_NAME }} ENV_NAME=${{ env.ENV_NAME }}
tags: | tags: |
vegaprotocol/${{ matrix.app }}:${{ github.sha }} vegaprotocol/${{ matrix.app }}:${{ github.sha }}
vegaprotocol/${{ matrix.app }}:${{ env.IS_MAINNET_RELEASE == 'true' && 'mainnet' || env.IS_TESTNET_RELEASE == 'true' && 'testnet' || '' }} vegaprotocol/${{ matrix.app }}:${{ env.IS_MAINNET_RELEASE == 'true' && 'mainnet' || env.IS_TESTNET_RELEASE == 'true' && 'testnet' || env.IS_DEV_IMAGE == 'true' && 'develop' || env.IS_MAIN_IMAGE == 'true' && 'main' || '' }}
# 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
@@ -329,7 +335,9 @@ jobs:
fi fi
# create commit # create commit
commit_msg="Automated hash update from ${{ github.ref }}" if ! git diff --cached --exit-code; then
git commit -m "$commit_msg" commit_msg="Automated hash update from ${{ github.ref }}"
git push -u origin "main" git commit -m "$commit_msg"
git push -u origin "main"
fi
) )
+1 -1
View File
@@ -118,7 +118,7 @@ On top of that there are two possible scenarios for running docker image - using
to run ipfs on port 3000: to run ipfs on port 3000:
```bash ```bash
docker run -p 3000:80 [TAG] ipfs docker run -p 3000:80 [TAG] /run-ipfs.sh
``` ```
to run nginx on port 3000: to run nginx on port 3000:
@@ -4,7 +4,7 @@ import { AssetTypeMapping, AssetStatusMapping } from '@vegaprotocol/assets';
import { t } from '@vegaprotocol/i18n'; import { t } from '@vegaprotocol/i18n';
import { ButtonLink } from '@vegaprotocol/ui-toolkit'; import { ButtonLink } from '@vegaprotocol/ui-toolkit';
import type { AgGridReact } from 'ag-grid-react'; import type { AgGridReact } from 'ag-grid-react';
import { AgGridLazy as AgGrid } from '@vegaprotocol/datagrid'; import { AgGrid } from '@vegaprotocol/datagrid';
import type { VegaICellRendererParams } from '@vegaprotocol/datagrid'; import type { VegaICellRendererParams } from '@vegaprotocol/datagrid';
import { useRef, useLayoutEffect } from 'react'; import { useRef, useLayoutEffect } from 'react';
import { BREAKPOINT_MD } from '../../config/breakpoints'; import { BREAKPOINT_MD } from '../../config/breakpoints';
@@ -4,7 +4,7 @@ import { t } from '@vegaprotocol/i18n';
import { ButtonLink } from '@vegaprotocol/ui-toolkit'; import { ButtonLink } from '@vegaprotocol/ui-toolkit';
import type { AgGridReact } from 'ag-grid-react'; import type { AgGridReact } from 'ag-grid-react';
import type { ColDef } from 'ag-grid-community'; import type { ColDef } from 'ag-grid-community';
import { AgGridLazy as AgGrid } from '@vegaprotocol/datagrid'; import { AgGrid } from '@vegaprotocol/datagrid';
import type { import type {
VegaICellRendererParams, VegaICellRendererParams,
VegaValueGetterParams, VegaValueGetterParams,
@@ -2,7 +2,7 @@ import type { ProposalListFieldsFragment } from '@vegaprotocol/proposals';
import { VoteProgress } from '@vegaprotocol/proposals'; import { VoteProgress } from '@vegaprotocol/proposals';
import type { AgGridReact } from 'ag-grid-react'; import type { AgGridReact } from 'ag-grid-react';
import { ExternalLink } from '@vegaprotocol/ui-toolkit'; import { ExternalLink } from '@vegaprotocol/ui-toolkit';
import { AgGridLazy as AgGrid } from '@vegaprotocol/datagrid'; import { AgGrid } from '@vegaprotocol/datagrid';
import type { import type {
VegaICellRendererParams, VegaICellRendererParams,
VegaValueFormatterParams, VegaValueFormatterParams,
@@ -105,7 +105,7 @@ export const ProposalsTable = ({ data }: ProposalsTableProps) => {
? new BigNumber(0) ? new BigNumber(0)
: yesTokens.multipliedBy(100).dividedBy(totalTokensVoted); : yesTokens.multipliedBy(100).dividedBy(totalTokensVoted);
return ( return (
<div className="uppercase flex h-full items-center justify-center pt-2"> <div className="flex items-center justify-center h-full pt-2 uppercase">
<VoteProgress <VoteProgress
threshold={requiredMajorityPercentage} threshold={requiredMajorityPercentage}
progress={yesPercentage} progress={yesPercentage}
@@ -8,7 +8,9 @@ import { useScrollToLocation } from '../../../hooks/scroll-to-location';
import filter from 'recursive-key-filter'; import filter from 'recursive-key-filter';
const Oracles = () => { const Oracles = () => {
const { data, loading, error } = useExplorerOracleSpecsQuery(); const { data, loading, error } = useExplorerOracleSpecsQuery({
errorPolicy: 'ignore',
});
useDocumentTitle(['Oracles']); useDocumentTitle(['Oracles']);
useScrollToLocation(); useScrollToLocation();
+1
View File
@@ -8,6 +8,7 @@ NX_FAIRGROUND=false
NX_VEGA_NETWORKS='{"DEVNET":"https://dev.governance.vega.xyz","STAGNET1":"https://governance.stagnet1.vega.rocks","TESTNET":"https://governance.fairground.wtf","MAINNET":"https://governance.vega.xyz"}' NX_VEGA_NETWORKS='{"DEVNET":"https://dev.governance.vega.xyz","STAGNET1":"https://governance.stagnet1.vega.rocks","TESTNET":"https://governance.fairground.wtf","MAINNET":"https://governance.vega.xyz"}'
NX_GITHUB_FEEDBACK_URL=https://github.com/vegaprotocol/feedback/discussions NX_GITHUB_FEEDBACK_URL=https://github.com/vegaprotocol/feedback/discussions
NX_VEGA_WALLET_URL=http://localhost:1789 NX_VEGA_WALLET_URL=http://localhost:1789
NX_WALLETCONNECT_PROJECT_ID=fe8091dc35738863e509fc4947525c72
NX_VEGA_EXPLORER_URL=https://explorer.stagnet1.vega.rocks NX_VEGA_EXPLORER_URL=https://explorer.stagnet1.vega.rocks
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
+1 -1
View File
@@ -17,7 +17,7 @@ NX_VEGA_REST_URL=https://api.vega.community/api/v2/
NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/firefox/addon/vega-wallet-mainnet NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/firefox/addon/vega-wallet-mainnet
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_WALLETCONNECT_PROJECT_ID=fe8091dc35738863e509fc4947525c72
NX_TENDERMINT_URL=https://be.vega.community NX_TENDERMINT_URL=https://be.vega.community
NX_TENDERMINT_WEBSOCKET_URL=wss://be.vega.community/websocket NX_TENDERMINT_WEBSOCKET_URL=wss://be.vega.community/websocket
+1
View File
@@ -10,6 +10,7 @@ NX_TRANCHES_SERVICE_URL=https://tranches-stagnet1-k8s.ops.vega.xyz
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json
NX_VEGA_REST_URL=https://api.n00.stagnet1.vega.xyz/api/v2/ NX_VEGA_REST_URL=https://api.n00.stagnet1.vega.xyz/api/v2/
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_WALLETCONNECT_PROJECT_ID=fe8091dc35738863e509fc4947525c72
NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/firefox/addon/vega-wallet-fairground NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/firefox/addon/vega-wallet-fairground
+1
View File
@@ -15,6 +15,7 @@ NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/annou
NX_VEGA_REST_URL=https://api.n07.testnet.vega.xyz/api/v2/ NX_VEGA_REST_URL=https://api.n07.testnet.vega.xyz/api/v2/
NX_SENTRY_DSN=https://4b8c8a8ba07742648aa4dfe1b8d17e40@o286262.ingest.sentry.io/5882996 NX_SENTRY_DSN=https://4b8c8a8ba07742648aa4dfe1b8d17e40@o286262.ingest.sentry.io/5882996
NX_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_WALLETCONNECT_PROJECT_ID=fe8091dc35738863e509fc4947525c72
NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/firefox/addon/vega-wallet-fairground NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/firefox/addon/vega-wallet-fairground
@@ -6,6 +6,11 @@ import { MockedProvider } from '@apollo/client/testing';
import { MemoryRouter, Route, Routes } from 'react-router-dom'; import { MemoryRouter, Route, Routes } from 'react-router-dom';
import { ProposalDocument } from './__generated__/Proposal'; import { ProposalDocument } from './__generated__/Proposal';
jest.mock('@vegaprotocol/data-provider', () => ({
...jest.requireActual('@vegaprotocol/data-provider'),
useDataProvider: jest.fn(() => ({ data: [], loading: false })),
}));
jest.mock('../components/proposal', () => ({ jest.mock('../components/proposal', () => ({
Proposal: () => <div data-testid="proposal" />, Proposal: () => <div data-testid="proposal" />,
})); }));
@@ -1,4 +1,4 @@
import { useMemo, useEffect, useState, useCallback } from 'react'; import { useMemo, useEffect, useState, useCallback, useRef } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { AsyncRenderer, Pagination } from '@vegaprotocol/ui-toolkit'; import { AsyncRenderer, Pagination } from '@vegaprotocol/ui-toolkit';
import { removePaginationWrapper } from '@vegaprotocol/utils'; import { removePaginationWrapper } from '@vegaprotocol/utils';
@@ -86,12 +86,18 @@ export const EpochIndividualRewards = ({
[epochId, page, refetch, delegationsPagination, pubKey] [epochId, page, refetch, delegationsPagination, pubKey]
); );
const prevEpochIdRef = useRef<number | null>(null);
useEffect(() => { useEffect(() => {
// when the epoch changes, we want to refetch the data to update the current page if (prevEpochIdRef.current === null) {
if (data) { prevEpochIdRef.current = epochId;
} else if (epochId !== prevEpochIdRef.current) {
// When the epoch changes, we want to refetch the data to update the current page
refetchData(); refetchData();
} }
}, [epochId, data, refetchData]);
prevEpochIdRef.current = epochId;
}, [epochId, refetchData]);
return ( return (
<AsyncRenderer <AsyncRenderer
@@ -3,7 +3,7 @@ import { forwardRef, useMemo, useRef, useState } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { useNavigate } from 'react-router-dom'; import { useNavigate } from 'react-router-dom';
import { Button, Icon } from '@vegaprotocol/ui-toolkit'; import { Button, Icon } from '@vegaprotocol/ui-toolkit';
import { AgGridLazy as AgGrid } from '@vegaprotocol/datagrid'; import { AgGrid } from '@vegaprotocol/datagrid';
import { useAppState } from '../../../../contexts/app-state/app-state-context'; import { useAppState } from '../../../../contexts/app-state/app-state-context';
import { BigNumber } from '../../../../lib/bignumber'; import { BigNumber } from '../../../../lib/bignumber';
import { import {
@@ -85,17 +85,17 @@ const TopThirdCellRenderer = (
}} }}
className="grid grid-cols-[60px_1fr] w-full h-full py-4 px-0 text-sm text-white text-center overflow-scroll" className="grid grid-cols-[60px_1fr] w-full h-full py-4 px-0 text-sm text-white text-center overflow-scroll"
> >
<div className="text-xs text-left px-3"> <div className="px-3 text-xs text-left">
{params?.data?.rankingDisplay} {params?.data?.rankingDisplay}
</div> </div>
<div className="whitespace-normal px-3"> <div className="px-3 whitespace-normal">
<div className="mb-4"> <div className="mb-4">
<Button <Button
data-testid="show-all-validators" data-testid="show-all-validators"
rightIcon={ rightIcon={
<Icon <Icon
name="arrow-right" name="arrow-right"
className="fill-current mr-2 align-text-top" className="mr-2 align-text-top fill-current"
/> />
} }
className="inline-flex items-center" className="inline-flex items-center"
@@ -103,7 +103,7 @@ const TopThirdCellRenderer = (
{t('Reveal top validators')} {t('Reveal top validators')}
</Button> </Button>
</div> </div>
<p className="font-semibold text-white mb-0"> <p className="mb-0 font-semibold text-white">
{t( {t(
'Validators with too great a stake share will have the staking rewards for their delegators penalised.' 'Validators with too great a stake share will have the staking rewards for their delegators penalised.'
)} )}
@@ -1,7 +1,7 @@
import { forwardRef, useMemo, useRef } from 'react'; import { forwardRef, useMemo, useRef } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { useNavigate } from 'react-router-dom'; import { useNavigate } from 'react-router-dom';
import { AgGridLazy as AgGrid } from '@vegaprotocol/datagrid'; import { AgGrid } from '@vegaprotocol/datagrid';
import { useAppState } from '../../../../contexts/app-state/app-state-context'; import { useAppState } from '../../../../contexts/app-state/app-state-context';
import { BigNumber } from '../../../../lib/bignumber'; import { BigNumber } from '../../../../lib/bignumber';
import { import {
@@ -9,7 +9,7 @@ import {
testOrderAmendment, testOrderAmendment,
} from '../support/order-validation'; } from '../support/order-validation';
const orderSymbol = 'market.tradableInstrument.instrument.code'; const orderSymbol = 'instrument-code';
const orderSize = 'size'; const orderSize = 'size';
const orderType = 'type'; const orderType = 'type';
const orderStatus = 'status'; const orderStatus = 'status';
@@ -229,10 +229,7 @@ describe('subscribe orders', { tags: '@smoke' }, () => {
status: Schema.OrderStatus.STATUS_FILLED, status: Schema.OrderStatus.STATUS_FILLED,
}); });
cy.getByTestId(`order-status-${orderId}`).should('have.text', 'Filled'); cy.getByTestId(`order-status-${orderId}`).should('have.text', 'Filled');
cy.get('[col-id="market.tradableInstrument.instrument.code"]').contains( cy.get(`[col-id="${orderSymbol}"]`).contains('[title="Future"]', 'Futr');
'[title="Future"]',
'Futr'
);
}); });
it('must see a rejected order', () => { it('must see a rejected order', () => {
+2 -1
View File
@@ -12,6 +12,7 @@ NX_VEGA_WALLET_URL=http://localhost:1789
NX_VEGA_DOCS_URL=https://docs.vega.xyz/testnet NX_VEGA_DOCS_URL=https://docs.vega.xyz/testnet
NX_VEGA_REPO_URL=https://github.com/vegaprotocol/vega/releases NX_VEGA_REPO_URL=https://github.com/vegaprotocol/vega/releases
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json
NX_WALLETCONNECT_PROJECT_ID=fe8091dc35738863e509fc4947525c72
NX_VEGA_INCIDENT_URL=https://blog.vega.xyz/tagged/vega-incident-reports NX_VEGA_INCIDENT_URL=https://blog.vega.xyz/tagged/vega-incident-reports
NX_VEGA_CONSOLE_URL=https://console.fairground.wtf NX_VEGA_CONSOLE_URL=https://console.fairground.wtf
NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn
@@ -23,7 +24,7 @@ NX_SUCCESSOR_MARKETS=true
NX_STOP_ORDERS=true NX_STOP_ORDERS=true
NX_ICEBERG_ORDERS=true NX_ICEBERG_ORDERS=true
# NX_PRODUCT_PERPETUALS # NX_PRODUCT_PERPETUALS
NX_METAMASK_SNAPS=false NX_METAMASK_SNAPS=true
NX_TENDERMINT_URL=https://tm.be.testnet.vega.xyz NX_TENDERMINT_URL=https://tm.be.testnet.vega.xyz
NX_TENDERMINT_WEBSOCKET_URL=wss://be.testnet.vega.xyz/websocket NX_TENDERMINT_WEBSOCKET_URL=wss://be.testnet.vega.xyz/websocket
+1 -4
View File
@@ -16,10 +16,7 @@ NX_VEGA_CONSOLE_URL=https://console.vega.xyz
NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-mainnet/codfcglpplgmmlokgilfkpcjnmkbfiel NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-mainnet/codfcglpplgmmlokgilfkpcjnmkbfiel
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/firefox/addon/vega-wallet-mainnet NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/firefox/addon/vega-wallet-mainnet
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_WALLETCONNECT_PROJECT_ID=fe8091dc35738863e509fc4947525c72
# TAG name of the current app version - TODO: bump to the latest upon release
NX_APP_VERSION=v0.21.2-core-0.72.14
# Cosmic elevator flags # Cosmic elevator flags
NX_SUCCESSOR_MARKETS=true NX_SUCCESSOR_MARKETS=true
+1 -1
View File
@@ -16,7 +16,7 @@ NX_VEGA_INCIDENT_URL=https://blog.vega.xyz/tagged/vega-incident-reports
NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/firefox/addon/vega-wallet-fairground NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/firefox/addon/vega-wallet-fairground
NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json
NX_WALLETCONNECT_PROJECT_ID=fe8091dc35738863e509fc4947525c72
# Cosmic elevator flags # Cosmic elevator flags
NX_SUCCESSOR_MARKETS=true NX_SUCCESSOR_MARKETS=true
+1
View File
@@ -17,6 +17,7 @@ NX_VEGA_CONSOLE_URL=https://console.fairground.wtf
NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/firefox/addon/vega-wallet-fairground NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/firefox/addon/vega-wallet-fairground
NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json
NX_WALLETCONNECT_PROJECT_ID=fe8091dc35738863e509fc4947525c72
# Cosmic elevator flags # Cosmic elevator flags
NX_SUCCESSOR_MARKETS=true NX_SUCCESSOR_MARKETS=true
@@ -128,7 +128,7 @@ const MainGrid = memo(
</Tab> </Tab>
) : null} ) : null}
<Tab id="fills" name={t('Fills')}> <Tab id="fills" name={t('Fills')}>
<TradingViews.fills.component marketId={marketId} /> <TradingViews.fills.component />
</Tab> </Tab>
<Tab <Tab
id="accounts" id="accounts"
+1 -1
View File
@@ -4,7 +4,7 @@ import type {
VegaICellRendererParams, VegaICellRendererParams,
VegaValueFormatterParams, VegaValueFormatterParams,
} from '@vegaprotocol/datagrid'; } from '@vegaprotocol/datagrid';
import { AgGridLazy as AgGrid, COL_DEFS } from '@vegaprotocol/datagrid'; import { AgGrid, COL_DEFS } from '@vegaprotocol/datagrid';
import { useMemo } from 'react'; import { useMemo } from 'react';
import { t } from '@vegaprotocol/i18n'; import { t } from '@vegaprotocol/i18n';
import type { ProductType } from '@vegaprotocol/types'; import type { ProductType } from '@vegaprotocol/types';
@@ -1,5 +1,5 @@
import type { TypedDataAgGrid } from '@vegaprotocol/datagrid'; import type { TypedDataAgGrid } from '@vegaprotocol/datagrid';
import { AgGridLazy as AgGrid, PriceFlashCell } from '@vegaprotocol/datagrid'; import { AgGrid, PriceFlashCell } from '@vegaprotocol/datagrid';
import type { MarketMaybeWithData } from '@vegaprotocol/markets'; import type { MarketMaybeWithData } from '@vegaprotocol/markets';
import { useColumnDefs } from './use-column-defs'; import { useColumnDefs } from './use-column-defs';
@@ -1,83 +0,0 @@
import { Input, InputError } from '@vegaprotocol/ui-toolkit';
import type { FieldValues } from 'react-hook-form';
import { useForm } from 'react-hook-form';
import classNames from 'classnames';
import { useSearchParams } from 'react-router-dom';
import { useEffect } from 'react';
import { Button } from './buttons';
import { useVegaWallet } from '@vegaprotocol/wallet';
export const ApplyCodeForm = () => {
const { isReadOnly, pubKey, sendTx } = useVegaWallet();
const {
register,
handleSubmit,
formState: { errors },
setValue,
setError,
} = useForm();
const [params] = useSearchParams();
useEffect(() => {
const code = params.get('code');
if (code) setValue('code', code);
}, [params, setValue]);
const onSubmit = ({ code }: FieldValues) => {
if (isReadOnly || !pubKey || !code || code.length === 0) {
return;
}
sendTx(pubKey, {
applyReferralCode: {
id: code as string,
},
})
.then((res) => {
// TODO: Do something with response
})
.catch((err) => {
setError('code', {
type: 'required',
message: 'Your code has been rejected',
});
});
};
return (
<div className="w-1/2 mx-auto">
<h3 className="mb-5 text-xl text-center uppercase calt">
Apply a referral code
</h3>
<p className="mb-6 text-center">Enter a referral code</p>
<form
className={classNames('w-full flex flex-col gap-3', {
'animate-shake': Boolean(errors.code),
})}
onSubmit={handleSubmit(onSubmit)}
>
<label className="flex-grow">
<span className="block mb-1 text-sm text-vega-clight-100 dark:text-vega-cdark-100">
Your referral code
</span>
<Input
hasError={Boolean(errors.code)}
{...register('code', {
required: 'You have to provide a code to apply it.',
})}
/>
</label>
<Button
disabled={isReadOnly || !pubKey}
className="w-full"
type="submit"
>
Apply
</Button>
</form>
{errors.code && (
<InputError>{errors.code.message?.toString()}</InputError>
)}
</div>
);
};
@@ -1,83 +0,0 @@
import { Intent, TradingButton } from '@vegaprotocol/ui-toolkit';
import classNames from 'classnames';
import type { ComponentProps, ButtonHTMLAttributes } from 'react';
import { forwardRef } from 'react';
import { NavLink } from 'react-router-dom';
type RainbowButtonProps = {
variant?: 'full' | 'border';
};
export const RainbowButton = ({
variant = 'full',
children,
className,
...props
}: RainbowButtonProps & ButtonHTMLAttributes<HTMLButtonElement>) => (
<button
className={classNames(
'bg-rainbow hover:bg-none hover:bg-rainbow enabled:hover:bg-vega-pink-500 rounded-lg overflow-hidden disabled:opacity-40',
{
'px-5 py-3 text-white': variant === 'full',
'p-[0.125rem]': variant === 'border',
},
className
)}
{...props}
>
<div
className={classNames({
'bg-white dark:bg-vega-cdark-900 text-black dark:text-white px-5 py-3 rounded-[0.35rem] overflow-hidden':
variant === 'border',
})}
>
{children}
</div>
</button>
);
const RAINBOW_TAB_STYLE = classNames(
'inline-block',
'bg-vega-clight-500 dark:bg-vega-cdark-500 hover:bg-vega-clight-400 dark:hover:bg-vega-cdark-400',
'data-[state="active"]:text-white data-[state="active"]:bg-rainbow data-[state="active"]:hover:bg-none data-[state="active"]:hover:bg-vega-pink-500 dark:data-[state="active"]:hover:bg-vega-pink-500',
'[&.active]:text-white [&.active]:bg-rainbow [&.active]:hover:bg-none [&.active]:hover:bg-vega-pink-500 dark:[&.active]:hover:bg-vega-pink-500',
'px-5 py-3',
'first:rounded-tl-lg last:rounded-tr-lg'
);
export const RainbowTabButton = forwardRef<
HTMLButtonElement,
ButtonHTMLAttributes<HTMLButtonElement>
>(({ children, ...props }, ref) => (
<button ref={ref} className={RAINBOW_TAB_STYLE} {...props}>
{children}
</button>
));
RainbowTabButton.displayName = 'RainbowTabButton';
export const RainbowTabLink = ({
to,
children,
...props
}: ComponentProps<typeof NavLink>) => (
<NavLink to={to} className={RAINBOW_TAB_STYLE} {...props}>
{children}
</NavLink>
);
export const Button = forwardRef<
HTMLButtonElement,
ComponentProps<typeof TradingButton>
>(({ children, intent, type, ...props }, ref) => {
return (
<TradingButton
ref={ref}
intent={intent || type === 'submit' ? Intent.Primary : Intent.None}
type={type}
{...props}
>
{children}
</TradingButton>
);
});
Button.displayName = 'TradingButton';
@@ -1,6 +0,0 @@
export const BORDER_COLOR = 'border-vega-clight-500 dark:border-vega-cdark-500';
export const GRADIENT =
'bg-gradient-to-b from-vega-clight-800 dark:from-vega-cdark-800 to-transparent';
export const SKY_BACKGROUND =
'bg-[url(/sky-light.png)] dark:bg-[url(/sky-dark.png)] bg-[40%_0px] bg-[length:1440px] bg-no-repeat';
@@ -1,248 +0,0 @@
import {
useVegaWallet,
useVegaWalletDialogStore,
determineId,
} from '@vegaprotocol/wallet';
import { RainbowButton } from './buttons';
import { useState } from 'react';
import {
CopyWithTooltip,
Dialog,
ExternalLink,
InputError,
Intent,
TradingAnchorButton,
TradingButton,
VegaIcon,
VegaIconNames,
} from '@vegaprotocol/ui-toolkit';
import { gql, useQuery } from '@apollo/client';
import { addDecimalsFormatNumber } from '@vegaprotocol/utils';
import { DApp, TokenStaticLinks, useLinks } from '@vegaprotocol/environment';
const CREATE_CODE_QUERY = gql`
query CreateCode($partyId: ID!) {
party(id: $partyId) {
stakingSummary {
currentStakeAvailable
}
}
networkParameter(key: "referralProgram.minStakedVegaTokens") {
value
}
}
`;
export const CreateCodeContainer = () => {
const { pubKey } = useVegaWallet();
const { data, loading } = useQuery(CREATE_CODE_QUERY, {
variables: { partyId: pubKey || '' },
skip: !pubKey,
// TOOD: remove when network params available
errorPolicy: 'ignore',
});
if (loading) return null;
const requiredStake = data?.networkParameter?.value || '0';
const currentStakeAvailable =
data?.party?.stakingSummary.currentStakeAvailable || '0';
return (
<CreateCodeForm
currentStakeAvailable={currentStakeAvailable}
requiredStake={requiredStake}
/>
);
};
export const CreateCodeForm = ({
currentStakeAvailable,
requiredStake,
}: {
currentStakeAvailable: string;
requiredStake: string;
}) => {
const [dialogOpen, setDialogOpen] = useState(false);
const openWalletDialog = useVegaWalletDialogStore(
(store) => store.openVegaWalletDialog
);
const { pubKey } = useVegaWallet();
return (
<div className="w-1/2 mx-auto">
<h3 className="mb-5 text-xl text-center uppercase calt">
Create a referral code
</h3>
<p className="mb-6 text-center">
Generate a referral code to share with your friends and start earning
commission.
</p>
<div className="mb-5">
<div className="text-center">
<RainbowButton
variant="border"
onClick={() => {
if (pubKey) {
setDialogOpen(true);
} else {
openWalletDialog();
}
}}
>
{pubKey ? 'Create a referral code' : 'Connect wallet'}
</RainbowButton>
</div>
</div>
<Dialog
title="Create a referral code"
open={dialogOpen}
onChange={() => setDialogOpen(false)}
size="small"
>
<CreateCodeDialog
currentStakeAvailable={currentStakeAvailable}
setDialogOpen={setDialogOpen}
requiredStake={requiredStake}
/>
</Dialog>
</div>
);
};
const CreateCodeDialog = ({
setDialogOpen,
currentStakeAvailable,
requiredStake,
}: {
setDialogOpen: (open: boolean) => void;
currentStakeAvailable: string;
requiredStake: string;
}) => {
const createLink = useLinks(DApp.Governance);
const { isReadOnly, pubKey, sendTx } = useVegaWallet();
const [err, setErr] = useState<string | null>(null);
const [code, setCode] = useState<string | null>(null);
const [status, setStatus] = useState<
'idle' | 'loading' | 'success' | 'error'
>('idle');
const onSubmit = () => {
if (isReadOnly || !pubKey) {
setErr('Not connected');
} else {
setErr(null);
setStatus('loading');
setCode(null);
sendTx(pubKey, {
createReferralSet: {
isTeam: false,
},
})
.then((res) => {
if (!res) {
setErr(`Invalid response: ${JSON.stringify(res)}`);
return;
}
const code = determineId(res.signature);
setCode(code);
setStatus('success');
})
.catch((err) => {
if (err.message.includes('user rejected')) {
setStatus('idle');
return;
}
setStatus('error');
setErr(err.message);
});
}
};
const getButtonProps = () => {
if (status === 'idle' || status === 'error') {
return {
children: 'Generate code',
onClick: () => onSubmit(),
};
}
if (status === 'loading') {
return {
children: 'Confirm in wallet...',
disabled: true,
};
}
if (status === 'success') {
return {
children: 'Close',
intent: Intent.Success,
onClick: () => setDialogOpen(false),
};
}
};
// TODO: Add when network parameters are updated
if (
currentStakeAvailable === '0' ||
BigInt(currentStakeAvailable) < BigInt(requiredStake)
) {
return (
<div className="flex flex-col gap-4">
<p>
You need at least {addDecimalsFormatNumber(requiredStake, 18)} VEGA
staked to generate a referral code and participate in the referral
program.
</p>
<TradingAnchorButton
href={createLink(TokenStaticLinks.ASSOCIATE)}
intent={Intent.Primary}
target="_blank"
>
Stake some $VEGA now
</TradingAnchorButton>
</div>
);
}
return (
<div className="flex flex-col gap-4">
{(status === 'idle' || status === 'loading' || status === 'error') && (
<p>
Generate a referral code to share with your friends aand start enaring
commission.
</p>
)}
{status === 'success' && code && (
<div className="flex items-center gap-2">
<div className="flex-1 min-w-0 p-2 text-sm rounded bg-vega-clight-700 dark:bg-vega-cdark-700">
<p className="overflow-hidden whitespace-nowrap text-ellipsis">
{code}
</p>
</div>
<CopyWithTooltip text={code}>
<TradingButton
className="text-sm no-underline"
icon={<VegaIcon name={VegaIconNames.COPY} />}
>
<span>Copy</span>
</TradingButton>
</CopyWithTooltip>
</div>
)}
<TradingButton
fill={true}
intent={Intent.Primary}
{...getButtonProps()}
/>
{err && <InputError>{err}</InputError>}
{/* TODO: Add links */}
<div className="flex justify-center pt-5 mt-2 text-sm border-t gap-4 text-default border-default">
<ExternalLink>About the referral program</ExternalLink>
<ExternalLink>Disclaimer</ExternalLink>
</div>
</div>
);
};
@@ -1,46 +0,0 @@
import { isRouteErrorResponse, useNavigate, useRouteError } from 'react-router';
import { RainbowButton } from './buttons';
import { AnimatedDudeWithWire } from './graphics/dude';
import { LayoutWithSky } from './layout';
export const ErrorBoundary = () => {
const error = useRouteError();
const navigate = useNavigate();
const title = isRouteErrorResponse(error)
? `${error.status} ${error.statusText}`
: 'Something went wrong';
const code = isRouteErrorResponse(error) ? error.status : 0;
const messages: Record<number, string> = {
0: 'An unknown error occurred.',
404: "The page you're looking for doesn't exists.",
};
return (
<LayoutWithSky className="pt-32">
<div
aria-hidden
className="absolute top-64 right-[220px] md:right-[340px] max-sm:hidden"
>
<AnimatedDudeWithWire className="animate-spin" />
</div>
<h1 className="text-6xl font-alpha calt mb-10">{title}</h1>
{Object.keys(messages).includes(code.toString()) ? (
<p className="text-lg mb-10">{messages[code]}</p>
) : null}
<p className="text-lg mb-10">
<RainbowButton
onClick={() => navigate('..')}
variant="border"
className="text-xs"
>
Go back and try again
</RainbowButton>
</p>
</LayoutWithSky>
);
};
@@ -1,70 +0,0 @@
import classNames from 'classnames';
import type { HTMLAttributes } from 'react';
export const Dude = ({ className }: HTMLAttributes<SVGElement>) => {
return (
<svg
width="41"
height="47"
viewBox="0 0 41 47"
fill="none"
className={className}
>
<path
d="M21.1895 0.298767L5.08827 27.4101L8.96133 29.7103L4.36099 37.4564L8.23404 39.7566L12.8344 32.0105L16.7074 34.3107L12.1071 42.0568L15.9801 44.3569L20.5805 36.6108L24.4535 38.911L40.5547 11.7996L21.1895 0.298767Z"
className="fill-black dark:fill-white"
/>
<path
d="M35.9346 15.1683L20.4424 5.96765L14.3086 16.2958L29.8008 25.4965L35.9346 15.1683Z"
className="fill-white dark:fill-black"
/>
<path
d="M25.646 17.7895L23.064 16.2561L21.5305 18.8381L24.1126 20.3716L25.646 17.7895Z"
className="fill-black dark:fill-white"
/>
<path
d="M29.7612 16.7412L27.1792 15.2077L25.6458 17.7898L28.2278 19.3232L29.7612 16.7412Z"
className="fill-black dark:fill-white"
/>
<path
d="M33.877 15.6925L31.2949 14.159L29.7615 16.7411L32.3435 18.2745L33.877 15.6925Z"
className="fill-black dark:fill-white"
/>
<path
d="M29.0342 26.7874L26.4521 25.2539L24.9187 27.836L27.5007 29.3694L29.0342 26.7874Z"
fill="#FF077F"
/>
</svg>
);
};
export const Wire = ({ className }: HTMLAttributes<SVGElement>) => {
return (
<svg
width="157"
height="88"
viewBox="0 0 157 88"
fill="none"
className={className}
>
<path
d="M109.398 6.12235C127.37 -3.81898 146.791 1.45045 153.465 14.307C160.138 27.1636 154.195 43.9948 140.438 52.1164C126.68 60.238 105.767 54.9998 84.9212 43.464C64.0752 31.9281 32.2412 6.42016 18.8175 24.185C6.90871 40.719 41.9332 68.4495 29.2664 82.7049C23.187 88.4974 11.1379 88.2645 0.968295 80.3398"
className="stroke-black dark:stroke-white"
strokeWidth="1.5"
strokeMiterlimit="10"
/>
</svg>
);
};
export const AnimatedDudeWithWire = ({ className }: { className?: string }) => (
<div className="relative">
<Wire className="absolute top-[25px]" />
<Dude
className={classNames(
'absolute left-[96px] animate-[wave_20s_ease-in-out_infinite]',
className
)}
/>
</div>
);
@@ -1,83 +0,0 @@
import { gql } from '@apollo/client';
import { getNumberFormat } from '@vegaprotocol/utils';
import { addDays } from 'date-fns';
// TODO: Generate query
// eslint-disable-next-line
const REFERRAL_PROGRAM_QUERY = gql`
query ReferralProgram {
currentReferralProgram {
id
version
endOfProgramTimestamp
windowLength
endedAt
benefitTiers {
minimumEpochs
minimumRunningNotionalTakerVolume
referralDiscountFactor
referralRewardFactor
}
stakingTiers {
minimumStakedTokens
referralRewardMultiplier
}
}
}
`;
export const useReferralProgram = () => {
// TODO: get real data
// const { data, loading, error } = useQuery(REFERRAL_PROGRAM_QUERY, {
// fetchPolicy: 'cache-and-network',
// });
const dummyData = {
currentReferralProgram: {
id: 'abc',
version: 1,
endOfProgramTimestamp: addDays(new Date(), 10).toISOString(),
windowLength: 10,
benefitTiers: [
{
minimumEpochs: 5,
minimumRunningNotionalTakerVolume: '30000',
referralDiscountFactor: '0.01',
referralRewardFactor: '0.01',
},
{
minimumEpochs: 5,
minimumRunningNotionalTakerVolume: '20000',
referralDiscountFactor: '0.05',
referralRewardFactor: '0.05',
},
{
minimumEpochs: 5,
minimumRunningNotionalTakerVolume: '10000',
referralDiscountFactor: '0.001',
referralRewardFactor: '0.001',
},
],
},
} as const;
const benefitTiers = dummyData.currentReferralProgram.benefitTiers.map(
(t, i) => {
return {
tier: i + 1,
commission: Number(t.referralRewardFactor) * 100 + '%',
discount: Number(t.referralDiscountFactor) * 100 + '%',
volume: getNumberFormat(0).format(
Number(t.minimumRunningNotionalTakerVolume)
),
};
}
);
// TODO: return real loading, error values
return {
benefitTiers,
loading: false,
error: undefined,
};
};
@@ -1,93 +0,0 @@
import { gql, useQuery } from '@apollo/client';
const REFERRAL_QUERY = gql`
query ReferralSets($partyId: ID!) {
referralSets(referrer: $partyId) {
edges {
node {
id
referrer
createdAt
updatedAt
}
}
}
}
`;
const REFEREES_QUERY = gql`
query ReferralSets($code: ID!) {
referralSetReferees(id: $code) {
edges {
node {
referralSetId
refereeId
joinedAt
atEpoch
}
}
}
}
`;
// Fetches the current user's referral and then fetches all referees using
// the referral code
export const useReferral = (pubKey: string) => {
const {
data: referralData,
loading: referralLoading,
error: referralError,
} = useQuery(REFERRAL_QUERY, {
variables: {
partyId: pubKey,
},
fetchPolicy: 'cache-and-network',
});
// A user can only have 1 active referral program at a time
const referral = referralData?.referralSets.edges.length
? referralData.referralSets.edges[0].node
: undefined;
const {
data: refereesData,
loading: refereesLoading,
error: refereesError,
} = useQuery(REFEREES_QUERY, {
variables: {
code: referral?.id,
},
skip: !referral?.id,
fetchPolicy: 'cache-and-network',
});
const referees = refereesData?.referralSetReferees.edges?.map(
// TODO: generate types
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(e: any) => e.node
);
const data =
referral && refereesData
? {
code: referral.id,
referees,
}
: undefined;
return {
// TODO: generate types after perps work is merged
data: data as
| {
code: string;
referees: Array<{
refereeId: string;
joinedAt: string;
atEpoch: number;
}>;
}
| undefined,
loading: referralLoading || refereesLoading,
error: referralError || refereesError,
};
};
@@ -1,52 +0,0 @@
import { Table } from './table';
export const HowItWorksTable = () => (
<Table
className="bg-none bg-vega-clight-800 dark:bg-vega-cdark-800"
noHeader
noCollapse
columns={[{ name: 'number', className: 'px-0 pl-5' }, { name: 'step' }]}
data={[
{
number: (
<span className="text-2xl calt text-vega-clight-100 dark:text-vega-cdark-100">
1
</span>
),
step: 'Referrers generate a code assigned to their key via an on chain transaction',
},
{
number: (
<span className="text-2xl calt text-vega-clight-100 dark:text-vega-cdark-100">
2
</span>
),
step: 'Anyone with the referral link can apply it to their key(s) of choice via an on chain transaction',
},
{
number: (
<span className="text-2xl calt text-vega-clight-100 dark:text-vega-cdark-100">
3
</span>
),
step: 'Discounts are applied automatically during trading based on the key(s) used',
},
{
number: (
<span className="text-2xl calt text-vega-clight-100 dark:text-vega-cdark-100">
4
</span>
),
step: 'Referrers earn commission based on a percentage of the taker fees their referees pay',
},
{
number: (
<span className="text-2xl calt text-vega-clight-100 dark:text-vega-cdark-100">
5
</span>
),
step: 'The commission is taken from the infrastructure fee, maker fee, and liquidity provider fee, not from the referee',
},
]}
></Table>
);
@@ -1,31 +0,0 @@
import classNames from 'classnames';
import { AnimatedDudeWithWire } from './graphics/dude';
export const LandingBanner = () => {
return (
<div className={classNames('relative mb-20')}>
<div className="">
<div
aria-hidden
className="absolute top-64 right-[220px] md:right-[340px] max-sm:hidden"
>
<AnimatedDudeWithWire />
</div>
<div className="pt-32 sm:w-[50%]">
<h1 className="text-6xl font-alpha calt mb-10">
Earn commission & stake rewards
</h1>
<p className="text-lg mb-10">
Invite friends and earn commission in the form of Vega rewards from
the trading fees they pay. Stake those rewards to earn multipliers
on future rewards.
</p>
<p className="text-lg">
Any friends that join using the code will receive discounts off
trading fees.
</p>
</div>
</div>
</div>
);
};
@@ -1,36 +0,0 @@
import classNames from 'classnames';
import type { HTMLAttributes } from 'react';
import { SKY_BACKGROUND } from './constants';
import { Outlet } from 'react-router-dom';
export const Layout = ({
className,
children,
...props
}: HTMLAttributes<HTMLDivElement>) => {
return (
<div
className={classNames(
'max-w-[1440px]',
'mx-auto px-16 md:px-32 pb-32',
'relative z-0',
'h-full overflow-auto',
className
)}
{...props}
>
{children || <Outlet />}
</div>
);
};
export const LayoutWithSky = ({
className,
...props
}: HTMLAttributes<HTMLDivElement>) => {
return (
<div className={classNames('h-full', SKY_BACKGROUND)}>
<Layout className={className} {...props} />
</div>
);
};
@@ -1,126 +0,0 @@
import { Tile } from './tile';
import {
CopyWithTooltip,
Input,
VegaIcon,
VegaIconNames,
} from '@vegaprotocol/ui-toolkit';
import { Button, RainbowButton } from './buttons';
import { Tag } from './tag';
import { useVegaWallet, useVegaWalletDialogStore } from '@vegaprotocol/wallet';
import { useReferral } from './hooks/use-referral';
import { Link } from 'react-router-dom';
export const ReferralStatistics = () => {
const openWalletDialog = useVegaWalletDialogStore(
(store) => store.openVegaWalletDialog
);
const { pubKey } = useVegaWallet();
if (pubKey) {
return <ReferralStatisticsContainer pubKey={pubKey} />;
}
return (
<div className="text-center">
<RainbowButton variant="border" onClick={() => openWalletDialog()}>
Connect wallet
</RainbowButton>
</div>
);
};
const ReferralStatisticsContainer = ({ pubKey }: { pubKey: string }) => {
const { data } = useReferral(pubKey);
return (
<div className="grid grid-cols-1 grid-rows-1 gap-5">
<div className="grid grid-cols-3 grid-rows-1 gap-5">
<Tile>
<h3 className="mb-1 text-4xl text-center">3</h3>
<p className="mb-3 text-sm text-center text-vega-clight-100 dark:text-vega-cdark-100">
Your referral tier
</p>
<Tag className="mx-auto" color="purple">
10,000 until Tier 2
</Tag>
</Tile>
<Tile>
<h3 className="mb-1 text-4xl text-center">0.1%</h3>
<p className="mb-3 text-sm text-center text-vega-clight-100 dark:text-vega-cdark-100">
Your commission
</p>
<Tag className="mx-auto" color="purple">
10,000 until 0.5%x
</Tag>
</Tile>
{data?.code ? (
<Tile variant="rainbow">
<h3 className="mb-1 text-lg calt">Your referral code</h3>
<p className="mb-3 text-sm text-vega-clight-100 dark:text-vega-cdark-100">
Share this code with friends
</p>
<div className="flex gap-2">
<Input size={1} readOnly value={data.code} />
<CopyWithTooltip text={data.code}>
<Button
className="text-sm no-underline"
icon={<VegaIcon name={VegaIconNames.COPY} />}
>
<span>Copy</span>
</Button>
</CopyWithTooltip>
</div>
</Tile>
) : (
<Tile variant="rainbow">
<h3 className="mb-1 text-lg calt">
Create referral code to start earning rewards
</h3>
<p className="mb-3 text-sm text-vega-clight-100 dark:text-vega-cdark-100">
Invite friends and earn commission
</p>
<p>
<Link to="create-code" className="underline underline-offset-4">
Create a referral code
</Link>
</p>
</Tile>
)}
</div>
<div className="grid grid-cols-4 grid-rows-1 gap-5">
<Tile className="py-3">
<h3 className="mb-1 text-2xl text-center">10,000</h3>
<p className="text-sm text-center text-vega-clight-100 dark:text-vega-cdark-100">
Your total trading volume
</p>
</Tile>
{data?.code && (
<Tile className="py-3">
<h3 className="mb-1 text-2xl text-center">
{data.referees.length}
</h3>
<p className="text-sm text-center text-vega-clight-100 dark:text-vega-cdark-100">
{data.referees.length === 1
? 'Trader referred'
: 'Total traders referred'}
</p>
</Tile>
)}
<Tile className="py-3">
<h3 className="mb-1 text-2xl text-center">0.1%</h3>
<p className="text-sm text-center text-vega-clight-100 dark:text-vega-cdark-100">
Maximum trader discount
</p>
</Tile>
<Tile className="py-3">
<h3 className="mb-1 text-2xl text-center">10,000</h3>
<p className="text-sm text-center text-vega-clight-100 dark:text-vega-cdark-100">
Your commission (last 30d)
</p>
</Tile>
</div>
</div>
);
};
@@ -1,151 +0,0 @@
import {
TradingAnchorButton,
VegaIcon,
VegaIconNames,
} from '@vegaprotocol/ui-toolkit';
import classNames from 'classnames';
import { HowItWorksTable } from './how-it-works-table';
import { LandingBanner } from './landing-banner';
import { TiersContainer } from './tiers-table';
import { BORDER_COLOR, GRADIENT } from './constants';
import { RainbowTabLink } from './buttons';
import { Outlet } from 'react-router-dom';
import { Tag } from './tag';
import { Routes } from '../../pages/client-router';
export const Referrals = () => {
return (
<>
<LandingBanner />
<div>
<div className="flex justify-center">
<RainbowTabLink end to={Routes.REFERRALS}>
Your referrals
</RainbowTabLink>
<RainbowTabLink to={Routes.REFERRALS_CREATE_CODE}>
Your referrals
</RainbowTabLink>
<RainbowTabLink to={Routes.REFERRALS_APPLY_CODE}>
Apply a code
</RainbowTabLink>
</div>
<div className="py-16 border-t border-b border-vega-cdark-500">
<Outlet />
</div>
</div>
<div className="flex flex-row items-baseline justify-between mt-10 mb-5">
<h2 className="text-2xl">Referral tiers</h2>
<span className="text-base">
<span className="text-vega-clight-200 dark:text-vega-cdark-200">
Program ends:
</span>{' '}
16 epochs
</span>
</div>
<div className="mb-20">
<TiersContainer />
</div>
<div className="flex flex-row items-baseline justify-between mb-5">
<h2 className="text-2xl">Staking multipliers</h2>
</div>
<div className="flex flex-col mb-20 jjustify-items-stretch md:flex-row gap-5">
<div
className={classNames(
'overflow-hidden',
'border rounded-md w-full',
BORDER_COLOR
)}
>
<div aria-hidden>
{/* eslint-disable-next-line @next/next/no-img-element */}
<img
src="/1x.png"
alt="1x multiplier"
width={768}
height={400}
className="w-full"
/>
</div>
<div className={classNames('p-3', GRADIENT)}>
<h3 className="mb-3 text-xl">Tradestarter</h3>
<p className="text-base text-vega-clight-100 dark:text-vega-cdark-100">
Stake a minimum of 100 $VEGA tokens
</p>
<Tag color="green">Reward multiplier 1x</Tag>
</div>
</div>
<div
className={classNames(
'overflow-hidden',
'border rounded-md w-full',
BORDER_COLOR
)}
>
<div aria-hidden>
{/* eslint-disable-next-line @next/next/no-img-element */}
<img
src="/2x.png"
alt="2x multiplier"
width={768}
height={400}
className="w-full"
/>
</div>
<div className={classNames('p-3', GRADIENT)}>
<h3 className="mb-3 text-xl">Mid level degen</h3>
<p className="text-base text-vega-clight-100 dark:text-vega-cdark-100">
Stake a minimum of 1000 $VEGA tokens
</p>
<Tag color="blue">Reward multiplier 2x</Tag>
</div>
</div>
<div
className={classNames(
'overflow-hidden',
'border rounded-md w-full',
BORDER_COLOR
)}
>
<div aria-hidden>
{/* eslint-disable-next-line @next/next/no-img-element */}
<img
src="/3x.png"
alt="3x multiplier"
width={768}
height={400}
className="w-full"
/>
</div>
<div className={classNames('p-3', GRADIENT)}>
<h3 className="mb-3 text-xl">Reward hoarder</h3>
<p className="text-base text-vega-clight-100 dark:text-vega-cdark-100">
Stake a minimum of 10,000 $VEGA tokens
</p>
<Tag color="pink">Reward multiplier 3x</Tag>
</div>
</div>
</div>
<div className="mt-10 mb-5 text-center">
<h2 className="text-2xl">How it works</h2>
</div>
<div className="md:w-[60%] mx-auto">
<HowItWorksTable />
<div className="mt-5">
<TradingAnchorButton
className="mx-auto w-max"
href="https://docs.vega.xyz/"
target="_blank"
>
Read the terms <VegaIcon name={VegaIconNames.OPEN_EXTERNAL} />
</TradingAnchorButton>
</div>
</div>
</>
);
};
export default Referrals;
@@ -1,116 +0,0 @@
import { Tooltip, VegaIcon, VegaIconNames } from '@vegaprotocol/ui-toolkit';
import classNames from 'classnames';
import type { HTMLAttributes } from 'react';
import { BORDER_COLOR, GRADIENT } from './constants';
type TableColumnDefinition = {
displayName?: string;
name: string;
tooltip?: string;
className?: string;
};
type TableProps = {
columns: TableColumnDefinition[];
data: Record<TableColumnDefinition['name'] | 'className', React.ReactNode>[];
noHeader?: boolean;
noCollapse?: boolean;
};
const INNER_BORDER_STYLE = `border-b ${BORDER_COLOR}`;
export const Table = ({
columns,
data,
noHeader = false,
noCollapse = false,
className,
...props
}: TableProps & HTMLAttributes<HTMLTableElement>) => {
const header = (
<thead className={classNames({ 'max-md:hidden': !noCollapse })}>
<tr>
{columns.map(({ displayName, name, tooltip }) => (
<th
key={name}
col-id={name}
className={classNames(
'px-5 py-3 text-sm text-vega-clight-100 dark:text-vega-cdark-100',
INNER_BORDER_STYLE
)}
>
<span className="flex flex-row gap-2 items-center">
<span>{displayName}</span>
{tooltip ? (
<Tooltip description={tooltip}>
<button className="text-vega-clight-400 dark:text-vega-cdark-400 no-underline decoration-transparent w-[12px] h-[12px] inline-flex">
<VegaIcon size={12} name={VegaIconNames.INFO} />
</button>
</Tooltip>
) : null}
</span>
</th>
))}
</tr>
</thead>
);
return (
<table
className={classNames(
'w-full',
'border-separate border rounded-md border-spacing-0',
BORDER_COLOR,
GRADIENT,
className
)}
{...props}
>
{!noHeader && header}
<tbody>
{data.map((d, i) => (
<tr
key={i}
className={classNames(d['className'] as string, {
'max-md:flex flex-col w-full': !noCollapse,
})}
>
{columns.map(({ name, displayName, className }, j) => (
<td
className={classNames(
'px-5 py-3 text-base',
{
'max-md:flex max-md:flex-col max-md:justify-between':
!noCollapse,
},
INNER_BORDER_STYLE,
{
'border-none': i === data.length - 1 && noCollapse,
'md:border-none': i === data.length - 1,
'max-md:border-none':
i === data.length - 1 && j === columns.length - 1,
},
className
)}
key={`${i}-${name}`}
>
{/** display column name in mobile view */}
{!noCollapse &&
!noHeader &&
displayName &&
displayName.length > 0 && (
<span
aria-hidden
className="md:hidden font-mono text-xs px-0 text-vega-clight-100 dark:text-vega-cdark-100"
>
{displayName}
</span>
)}
<span>{d[name]}</span>
</td>
))}
</tr>
))}
</tbody>
</table>
);
};
@@ -1,32 +0,0 @@
import type { HTMLAttributes } from 'react';
import classNames from 'classnames';
type TagProps = {
color?: 'yellow' | 'green' | 'blue' | 'purple' | 'pink' | 'orange' | 'none';
};
export const Tag = ({
color = 'none',
children,
className,
...props
}: TagProps & HTMLAttributes<HTMLDivElement>) => (
<div
className={classNames(
'mt-3 w-max border rounded-[1rem] py-[0.125rem] px-2 text-xs',
{
'border-vega-yellow-500 text-vega-yellow-500': color === 'yellow',
'border-vega-green-500 text-vega-green-500': color === 'green',
'border-vega-blue-500 text-vega-blue-500': color === 'blue',
'border-vega-purple-500 text-vega-purple-500': color === 'purple',
'border-vega-pink-500 text-vega-pink-500': color === 'pink',
'border-vega-orange-500 text-vega-orange-500': color === 'orange',
'border-vega-clight-100 text-vega-clight-100 dark:border-vega-cdark-100 dark:text-vega-cdark-100':
color === 'none',
},
className
)}
{...props}
>
{children}
</div>
);
@@ -1,39 +0,0 @@
import { useReferralProgram } from './hooks/use-referral-program';
import { Table } from './table';
export const TiersContainer = () => {
const { benefitTiers } = useReferralProgram();
return <TiersTable data={benefitTiers} />;
};
const TiersTable = ({
data,
}: {
data: Array<{
tier: number;
commission: string;
discount: string;
volume: string;
}>;
}) => {
return (
<Table
columns={[
{ name: 'tier', displayName: 'Tier' },
{
name: 'commission',
displayName: 'Referrer commission',
tooltip: 'A percentage of commission earned by the referrer',
},
{ name: 'discount', displayName: 'Referrer trading discount' },
{ name: 'volume', displayName: 'Min. trading volume' },
]}
data={data.map((d) => ({
...d,
className:
'from-vega-pink-400 dark:from-vega-pink-600 to-20% bg-highlight',
}))}
/>
);
};
@@ -1,39 +0,0 @@
import classNames from 'classnames';
import type { HTMLAttributes } from 'react';
import { BORDER_COLOR, GRADIENT } from './constants';
type TileProps = {
variant?: 'rainbow' | 'default';
};
export const Tile = ({
variant = 'default',
className,
children,
}: TileProps & HTMLAttributes<HTMLDivElement>) => {
return (
<div
className={classNames(
{
'bg-rainbow p-[0.125rem]': variant === 'rainbow',
[`border-2 ${BORDER_COLOR} p-0`]: variant === 'default',
},
'rounded-lg overflow-hidden'
)}
>
<div
className={classNames(
{
'bg-white dark:bg-vega-cdark-900 text-black dark:text-white rounded-[0.35rem] overflow-hidden':
variant === 'rainbow',
},
'p-6',
GRADIENT,
className
)}
>
{children}
</div>
</div>
);
};
@@ -9,5 +9,11 @@ export const AnnouncementBanner = () => {
return null; return null;
} }
return <Banner app="console" configUrl={ANNOUNCEMENTS_CONFIG_URL} />; return (
<Banner
app="console"
configUrl={ANNOUNCEMENTS_CONFIG_URL}
background="url('/banner-bg.jpg')"
/>
);
}; };
-1
View File
@@ -1,2 +1 @@
export * from './announcement-banner'; export * from './announcement-banner';
export * from './upgrade-banner';
@@ -1,84 +0,0 @@
import { useMemo, useState } from 'react';
import { gt, prerelease } from 'semver';
import {
ReleasesFeed,
useEnvironment,
useReleases,
Networks,
} from '@vegaprotocol/environment';
import { t } from '@vegaprotocol/i18n';
import {
CopyWithTooltip,
ExternalLink,
Intent,
NotificationBanner,
VegaIcon,
VegaIconNames,
} from '@vegaprotocol/ui-toolkit';
// v0.20.12-core-0.71.4 -> v0.20.12
// we need to strip the "core" suffix in order to determine whether a release
// is a pre-release (candidate); example: v.0.21.0-beta.1-core-0.71.4
const parseTagName = (tagName: string) => tagName.replace(/-core-[\d.]+$/i, '');
type UpgradeBannerProps = {
showVersionChange: boolean;
};
export const UpgradeBanner = ({ showVersionChange }: UpgradeBannerProps) => {
const [visible, setVisible] = useState(true);
const { data } = useReleases(ReleasesFeed.FrontEnd);
const { APP_VERSION, VEGA_ENV, VEGA_NETWORKS } = useEnvironment();
const CANONICAL_URL = VEGA_NETWORKS[VEGA_ENV] || 'https://console.vega.xyz';
const newest = useMemo(() => {
if (!APP_VERSION || !data) return undefined;
const newer = data.filter((r) => gt(r.tagName, APP_VERSION));
const valid =
// filter pre-releases on mainnet
VEGA_ENV === Networks.MAINNET
? newer?.filter((r) => !prerelease(parseTagName(r.tagName)))
: newer;
return valid.sort((a, b) => (gt(a.tagName, b.tagName) ? -1 : 1))[0];
}, [APP_VERSION, VEGA_ENV, data]);
if (!visible || !newest) {
return null;
}
return (
<NotificationBanner
intent={Intent.Warning}
onClose={() => {
setVisible(false);
}}
>
<div className="uppercase mb-1">
<ExternalLink href={CANONICAL_URL}>
{t('Upgrade to the latest version of Console')}
</ExternalLink>
</div>
<div data-testid="bookmark-message">
<a
className="underline"
href={newest.htmlUrl}
rel="noreferrer nofollow noopener"
target="_blank"
>
{t("View what's changed")}
</a>{' '}
{t(' or bookmark')}{' '}
<a className="underline" href={CANONICAL_URL}>
{t('console.vega.xyz')}
</a>{' '}
<CopyWithTooltip text={CANONICAL_URL}>
<button title={t('Copy %s', CANONICAL_URL)}>
<span className="sr-only">{t('Copy %s', CANONICAL_URL)}</span>
<VegaIcon size={14} name={VegaIconNames.COPY} />
</button>
</CopyWithTooltip>{' '}
{'to always see the latest version.'}
</div>
</NotificationBanner>
);
};
@@ -9,7 +9,7 @@ import type { DataGridSlice } from '../../stores/datagrid-store-slice';
import { createDataGridSlice } from '../../stores/datagrid-store-slice'; import { createDataGridSlice } from '../../stores/datagrid-store-slice';
import { useMarketClickHandler } from '../../lib/hooks/use-market-click-handler'; import { useMarketClickHandler } from '../../lib/hooks/use-market-click-handler';
export const FillsContainer = ({ marketId }: { marketId?: string }) => { export const FillsContainer = () => {
const onMarketClick = useMarketClickHandler(true); const onMarketClick = useMarketClickHandler(true);
const { pubKey } = useVegaWallet(); const { pubKey } = useVegaWallet();
@@ -31,7 +31,6 @@ export const FillsContainer = ({ marketId }: { marketId?: string }) => {
return ( return (
<FillsManager <FillsManager
partyId={pubKey} partyId={pubKey}
marketId={marketId}
onMarketClick={onMarketClick} onMarketClick={onMarketClick}
gridProps={gridStoreCallbacks} gridProps={gridStoreCallbacks}
/> />
+1 -1
View File
@@ -53,7 +53,7 @@ export const HeaderStat = ({
<div data-testid="item-header" id={id}> <div data-testid="item-header" id={id}>
{heading} {heading}
</div> </div>
<Tooltip description={description}> <Tooltip description={description} underline>
<div <div
data-testid="item-value" data-testid="item-value"
aria-labelledby={id} aria-labelledby={id}
@@ -180,11 +180,6 @@ const NavbarMenu = ({ onClick }: { onClick: () => void }) => {
{t('Trading')} {t('Trading')}
</NavbarLink> </NavbarLink>
</NavbarItem> </NavbarItem>
<NavbarItem>
<NavbarLink to={Links[Routes.REFERRALS]()} onClick={onClick}>
{t('Referrals')}
</NavbarLink>
</NavbarItem>
<NavbarItem> <NavbarItem>
<NavbarLink to={Links[Routes.PORTFOLIO]()} onClick={onClick}> <NavbarLink to={Links[Routes.PORTFOLIO]()} onClick={onClick}>
{t('Portfolio')} {t('Portfolio')}
@@ -28,14 +28,20 @@ export interface OrderContainerProps {
filter?: Filter; filter?: Filter;
} }
const AUTO_SIZE_COLUMNS = ['instrument-code'];
export const OrdersContainer = ({ filter }: OrderContainerProps) => { export const OrdersContainer = ({ filter }: OrderContainerProps) => {
const { pubKey, isReadOnly } = useVegaWallet(); const { pubKey, isReadOnly } = useVegaWallet();
const onMarketClick = useMarketClickHandler(true); const onMarketClick = useMarketClickHandler(true);
const onOrderTypeClick = useMarketLiquidityClickHandler(); const onOrderTypeClick = useMarketLiquidityClickHandler();
const { gridState, updateGridState } = useOrderListGridState(filter); const { gridState, updateGridState } = useOrderListGridState(filter);
const gridStoreCallbacks = useDataGridEvents(gridState, (newState) => { const gridStoreCallbacks = useDataGridEvents(
updateGridState(filter, newState); gridState,
}); (newState) => {
updateGridState(filter, newState);
},
AUTO_SIZE_COLUMNS
);
if (!pubKey) { if (!pubKey) {
return <Splash>{t('Please connect Vega wallet')}</Splash>; return <Splash>{t('Please connect Vega wallet')}</Splash>;
@@ -10,6 +10,8 @@ import { create } from 'zustand';
import { persist } from 'zustand/middleware'; import { persist } from 'zustand/middleware';
import { useMarketClickHandler } from '../../lib/hooks/use-market-click-handler'; import { useMarketClickHandler } from '../../lib/hooks/use-market-click-handler';
const AUTO_SIZE_COLUMNS = ['marketCode'];
export const PositionsContainer = ({ allKeys }: { allKeys?: boolean }) => { export const PositionsContainer = ({ allKeys }: { allKeys?: boolean }) => {
const onMarketClick = useMarketClickHandler(true); const onMarketClick = useMarketClickHandler(true);
const { pubKey, pubKeys, isReadOnly } = useVegaWallet(); const { pubKey, pubKeys, isReadOnly } = useVegaWallet();
@@ -17,7 +19,11 @@ export const PositionsContainer = ({ allKeys }: { allKeys?: boolean }) => {
const showClosed = usePositionsStore((store) => store.showClosedMarkets); const showClosed = usePositionsStore((store) => store.showClosedMarkets);
const gridStore = usePositionsStore((store) => store.gridStore); const gridStore = usePositionsStore((store) => store.gridStore);
const updateGridStore = usePositionsStore((store) => store.updateGridStore); const updateGridStore = usePositionsStore((store) => store.updateGridStore);
const gridStoreCallbacks = useDataGridEvents(gridStore, updateGridStore); const gridStoreCallbacks = useDataGridEvents(
gridStore,
updateGridStore,
AUTO_SIZE_COLUMNS
);
if (!pubKey) { if (!pubKey) {
return ( return (
@@ -27,8 +27,8 @@ const GetStartedButton = ({ step }: { step: OnboardingStep }) => {
const dismiss = useOnboardingStore((store) => store.dismiss); const dismiss = useOnboardingStore((store) => store.dismiss);
const setDialogOpen = useOnboardingStore((store) => store.setDialogOpen); const setDialogOpen = useOnboardingStore((store) => store.setDialogOpen);
const marketId = useGlobalStore((store) => store.marketId); const marketId = useGlobalStore((store) => store.marketId);
const openVegaWalletDialog = useVegaWalletDialogStore( const setWalletDialogOpen = useOnboardingStore(
(store) => store.openVegaWalletDialog (store) => store.setWalletDialogOpen
); );
const setViews = useSidebar((store) => store.setViews); const setViews = useSidebar((store) => store.setViews);
@@ -40,7 +40,7 @@ const GetStartedButton = ({ step }: { step: OnboardingStep }) => {
if (step <= OnboardingStep.ONBOARDING_CONNECT_STEP) { if (step <= OnboardingStep.ONBOARDING_CONNECT_STEP) {
return ( return (
<TradingButton {...buttonProps} onClick={() => openVegaWalletDialog()}> <TradingButton {...buttonProps} onClick={() => setWalletDialogOpen(true)}>
{t('Connect')} {t('Connect')}
</TradingButton> </TradingButton>
); );
@@ -70,7 +70,7 @@ const GetStartedButton = ({ step }: { step: OnboardingStep }) => {
} }
return ( return (
<TradingButton {...buttonProps} onClick={() => openVegaWalletDialog()}> <TradingButton {...buttonProps} onClick={() => setWalletDialogOpen(true)}>
{t('Get started')} {t('Get started')}
</TradingButton> </TradingButton>
); );
@@ -12,16 +12,20 @@ import { useGlobalStore } from '../../stores';
const ONBOARDING_STORAGE_KEY = 'vega_onboarding'; const ONBOARDING_STORAGE_KEY = 'vega_onboarding';
export const useOnboardingStore = create<{ export const useOnboardingStore = create<{
dialogOpen: boolean; dialogOpen: boolean;
walletDialogOpen: boolean;
dismissed: boolean; dismissed: boolean;
dismiss: () => void; dismiss: () => void;
setDialogOpen: (isOpen: boolean) => void; setDialogOpen: (isOpen: boolean) => void;
setWalletDialogOpen: (isOpen: boolean) => void;
}>()( }>()(
persist( persist(
(set) => ({ (set) => ({
dialogOpen: true, dialogOpen: true,
walletDialogOpen: false,
dismissed: false, dismissed: false,
dismiss: () => set({ dismissed: true }), dismiss: () => set({ dismissed: true }),
setDialogOpen: (isOpen) => set({ dialogOpen: isOpen }), setDialogOpen: (isOpen) => set({ dialogOpen: isOpen }),
setWalletDialogOpen: (isOpen) => set({ walletDialogOpen: isOpen }),
}), }),
{ {
name: ONBOARDING_STORAGE_KEY, name: ONBOARDING_STORAGE_KEY,
@@ -3,30 +3,54 @@ import { t } from '@vegaprotocol/i18n';
import { useEnvironment } from '@vegaprotocol/environment'; import { useEnvironment } from '@vegaprotocol/environment';
import { WelcomeDialogContent } from './welcome-dialog-content'; import { WelcomeDialogContent } from './welcome-dialog-content';
import { useOnboardingStore } from './use-get-onboarding-step'; import { useOnboardingStore } from './use-get-onboarding-step';
import { VegaConnectDialog } from '@vegaprotocol/wallet';
import { Connectors } from '../../lib/vega-connectors';
import { RiskMessage } from './risk-message';
export const WelcomeDialog = () => { export const WelcomeDialog = () => {
const { VEGA_ENV } = useEnvironment(); const { VEGA_ENV } = useEnvironment();
const dismissed = useOnboardingStore((store) => store.dismissed); const dismissed = useOnboardingStore((store) => store.dismissed);
const dialogOpen = useOnboardingStore((store) => store.dialogOpen); const dialogOpen = useOnboardingStore((store) => store.dialogOpen);
const dismiss = useOnboardingStore((store) => store.dismiss); const dismiss = useOnboardingStore((store) => store.dismiss);
const walletDialogOpen = useOnboardingStore(
(store) => store.walletDialogOpen
);
const setWalletDialogOpen = useOnboardingStore(
(store) => store.setWalletDialogOpen
);
const content = walletDialogOpen ? (
<VegaConnectDialog
connectors={Connectors}
riskMessage={<RiskMessage />}
onClose={() => setWalletDialogOpen(false)}
contentOnly
/>
) : (
<WelcomeDialogContent />
);
const onClose = walletDialogOpen ? () => setWalletDialogOpen(false) : dismiss;
const title = walletDialogOpen ? null : (
<span className="font-alpha calt" data-testid="welcome-title">
{t('Console')}{' '}
<span className="text-vega-clight-100 dark:text-vega-cdark-100">
{VEGA_ENV}
</span>
</span>
);
return ( return (
<Dialog <Dialog
open={dismissed ? false : dialogOpen} open={dismissed ? false : dialogOpen}
title={ title={title}
<span className="font-alpha calt" data-testid="welcome-title">
{t('Console')}{' '}
<span className="text-vega-clight-100 dark:text-vega-cdark-100">
{VEGA_ENV}
</span>
</span>
}
size="medium" size="medium"
onChange={() => dismiss()} onChange={onClose}
intent={Intent.None} intent={Intent.None}
dataTestId="welcome-dialog" dataTestId="welcome-dialog"
> >
<WelcomeDialogContent /> {content}
</Dialog> </Dialog>
); );
}; };
+1 -2
View File
@@ -38,7 +38,7 @@ import { AppLoader, DynamicLoader } from '../components/app-loader';
import { useDataProvider } from '@vegaprotocol/data-provider'; import { useDataProvider } from '@vegaprotocol/data-provider';
import { activeOrdersProvider } from '@vegaprotocol/orders'; import { activeOrdersProvider } from '@vegaprotocol/orders';
import { useTelemetryApproval } from '../lib/hooks/use-telemetry-approval'; import { useTelemetryApproval } from '../lib/hooks/use-telemetry-approval';
import { AnnouncementBanner, UpgradeBanner } from '../components/banner'; import { AnnouncementBanner } from '../components/banner';
import { Navbar } from '../components/navbar'; import { Navbar } from '../components/navbar';
import classNames from 'classnames'; import classNames from 'classnames';
import { import {
@@ -115,7 +115,6 @@ function AppBody({ Component }: AppProps) {
/> />
<ProtocolUpgradeInProgressNotification /> <ProtocolUpgradeInProgressNotification />
<ViewingBanner /> <ViewingBanner />
<UpgradeBanner showVersionChange={true} />
</div> </div>
<div data-testid={`pathname-${location.pathname}`}> <div data-testid={`pathname-${location.pathname}`}>
<Component /> <Component />
+8 -17
View File
@@ -4,33 +4,24 @@ export default function Document() {
return ( return (
<> <>
<Head> <Head>
{/* {/*
meta tags meta tags
- next advised against using _document for this, so they exist in our - next advised against using _document for this, so they exist in our
- single page index.page.tsx - single page index.page.tsx
*/} */}
{/* icons */} {/* preload fonts */}
<link
rel="icon"
type="image/x-icon"
href="https://static.vega.xyz/favicon.ico"
/>
<link
rel="apple-touch-icon"
content="https://static.vega.xyz/favicon.ico"
/>
{/* fonts */}
<link <link
rel="preload" rel="preload"
href="https://static.vega.xyz/AlphaLyrae-Medium.woff2" href="/AlphaLyrae-Medium.woff2"
as="font" as="font"
type="font/woff2" type="font/woff2"
/> />
{/* styles */} {/* icons */}
<link rel="stylesheet" href="https://static.vega.xyz/fonts.css" /> <link rel="icon" type="image/x-icon" href="/favicon.ico" />
<link rel="apple-touch-icon" content="/favicon.ico" />
{/* eslint-disable-next-line @next/next/no-css-tags */} {/* eslint-disable-next-line @next/next/no-css-tags */}
<link rel="stylesheet" href="/preloader.css" media="all" /> <link rel="stylesheet" href="/preloader.css" media="all" />
+1 -44
View File
@@ -6,10 +6,6 @@ import { t } from '@vegaprotocol/i18n';
import { Loader, Splash } from '@vegaprotocol/ui-toolkit'; import { Loader, Splash } from '@vegaprotocol/ui-toolkit';
import trimEnd from 'lodash/trimEnd'; import trimEnd from 'lodash/trimEnd';
import { LayoutWithSidebar } from '../components/layouts'; import { LayoutWithSidebar } from '../components/layouts';
import { LayoutWithSky } from '../client-pages/referrals/layout';
import { ReferralStatistics } from '../client-pages/referrals/referral-statistics';
import { ApplyCodeForm } from '../client-pages/referrals/apply-code-form';
import { CreateCodeContainer } from '../client-pages/referrals/create-code-form';
const LazyHome = dynamic(() => import('../client-pages/home'), { const LazyHome = dynamic(() => import('../client-pages/home'), {
ssr: false, ssr: false,
@@ -39,13 +35,6 @@ const LazyDeposit = dynamic(() => import('../client-pages/deposit'), {
ssr: false, ssr: false,
}); });
const LazyReferrals = dynamic(
() => import('../client-pages/referrals/referrals'),
{
ssr: false,
}
);
export enum Routes { export enum Routes {
HOME = '/', HOME = '/',
MARKET = '/markets/:marketId', MARKET = '/markets/:marketId',
@@ -54,10 +43,6 @@ export enum Routes {
LIQUIDITY = '/liquidity/:marketId', LIQUIDITY = '/liquidity/:marketId',
DISCLAIMER = '/disclaimer', DISCLAIMER = '/disclaimer',
DEPOSIT = '/deposit', DEPOSIT = '/deposit',
REFERRALS = '/referrals',
REFERRALS_APPLY_CODE = '/referrals/apply-code',
REFERRALS_CREATE_CODE = '/referrals/create-code',
TEAMS = '/teams',
} }
type ConsoleLinks = { [r in Routes]: (...args: string[]) => string }; type ConsoleLinks = { [r in Routes]: (...args: string[]) => string };
@@ -72,10 +57,6 @@ export const Links: ConsoleLinks = {
trimEnd(Routes.LIQUIDITY.replace(':marketId', marketId)), trimEnd(Routes.LIQUIDITY.replace(':marketId', marketId)),
[Routes.DISCLAIMER]: () => Routes.DISCLAIMER, [Routes.DISCLAIMER]: () => Routes.DISCLAIMER,
[Routes.DEPOSIT]: () => Routes.DEPOSIT, [Routes.DEPOSIT]: () => Routes.DEPOSIT,
[Routes.REFERRALS]: () => Routes.REFERRALS,
[Routes.REFERRALS_APPLY_CODE]: () => Routes.REFERRALS_APPLY_CODE,
[Routes.REFERRALS_CREATE_CODE]: () => Routes.REFERRALS_CREATE_CODE,
[Routes.TEAMS]: () => Routes.TEAMS,
}; };
export const routerConfig: RouteObject[] = [ export const routerConfig: RouteObject[] = [
@@ -128,30 +109,6 @@ export const routerConfig: RouteObject[] = [
element: <LazyDisclaimer />, element: <LazyDisclaimer />,
}, },
{ path: Routes.DEPOSIT, element: <LazyDeposit /> }, { path: Routes.DEPOSIT, element: <LazyDeposit /> },
// Referrals routing:
{
path: Routes.REFERRALS,
element: <LayoutWithSky />,
children: [
{
element: <LazyReferrals />,
children: [
{
index: true,
element: <ReferralStatistics />,
},
{
path: Routes.REFERRALS_APPLY_CODE,
element: <ApplyCodeForm />,
},
{
path: Routes.REFERRALS_CREATE_CODE,
element: <CreateCodeContainer />,
},
],
},
],
},
{ {
path: '*', path: '*',
element: ( element: (
@@ -167,7 +124,7 @@ export const ClientRouter = () => {
return ( return (
<Suspense <Suspense
fallback={ fallback={
<div className="flex items-center justify-center w-full h-full"> <div className="w-full h-full flex justify-center items-center">
<Loader /> <Loader />
</div> </div>
} }
+3 -9
View File
@@ -19,17 +19,11 @@ export default function Index() {
<meta name="og:url" content="https://console.vega.xyz/" /> <meta name="og:url" content="https://console.vega.xyz/" />
<meta name="og:title" content="Vega Protocol - Console" /> <meta name="og:title" content="Vega Protocol - Console" />
<meta name="og:site_name" content="Vega Protocol - Console" /> <meta name="og:site_name" content="Vega Protocol - Console" />
<meta name="og:image" content="https://static.vega.xyz/favicon.ico" /> <meta name="og:image" content="./favicon.ico" />
<meta <meta name="twitter:card" content="./favicon.ico" />
name="twitter:card"
content="https://static.vega.xyz/favicon.ico"
/>
<meta name="twitter:title" content="Vega Protocol - Console" /> <meta name="twitter:title" content="Vega Protocol - Console" />
<meta name="twitter:description" content="Vega Protocol - Console" /> <meta name="twitter:description" content="Vega Protocol - Console" />
<meta <meta name="twitter:image" content="./favicon.ico" />
name="twitter:image"
content="https://static.vega.xyz/favicon.ico"
/>
<meta name="twitter:image:alt" content="VEGA logo" /> <meta name="twitter:image:alt" content="VEGA logo" />
<meta name="twitter:site" content="@vegaprotocol" /> <meta name="twitter:site" content="@vegaprotocol" />
</Head> </Head>
+10 -3
View File
@@ -1,6 +1,13 @@
@import 'ag-grid-community/styles/ag-grid.css'; @import 'ag-grid-community/styles/ag-grid.css';
@import 'ag-grid-community/styles/ag-theme-balham.css'; @import 'ag-grid-community/styles/ag-theme-balham.css';
/** Load AlphaLyrae font */
@font-face {
font-family: AlphaLyrae;
src: url('/AlphaLyrae-Medium.woff2') format('woff2'),
url('/AlphaLyrae-Medium.woff') format('woff');
}
@tailwind base; @tailwind base;
@tailwind components; @tailwind components;
@tailwind utilities; @tailwind utilities;
@@ -103,8 +110,8 @@ html [data-theme='light'] {
--pennant-color-depth-sell-fill: theme(colors.market.red.DEFAULT); --pennant-color-depth-sell-fill: theme(colors.market.red.DEFAULT);
--pennant-color-depth-sell-stroke: theme(colors.market.red.650); --pennant-color-depth-sell-stroke: theme(colors.market.red.650);
--pennant-color-volume-buy: theme(colors.market.green.300); --pennant-color-volume-buy: theme(colors.market.green.DEFAULT);
--pennant-color-volume-sell: theme(colors.market.red.300); --pennant-color-volume-sell: theme(colors.market.red.DEFAULT);
} }
html [data-theme='dark'] { html [data-theme='dark'] {
@@ -125,7 +132,7 @@ html [data-theme='dark'] {
--pennant-color-depth-sell-stroke: theme(colors.market.red.DEFAULT); --pennant-color-depth-sell-stroke: theme(colors.market.red.DEFAULT);
--pennant-color-volume-buy: theme(colors.market.green.600); --pennant-color-volume-buy: theme(colors.market.green.600);
--pennant-color-volume-sell: theme(colors.market.red.650); --pennant-color-volume-sell: theme(colors.market.red.DEFAULT);
} }
/** /**
Binary file not shown.

Before

Width:  |  Height:  |  Size: 614 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 572 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 574 KiB

Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 101 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 947 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 947 KiB

+1 -31
View File
@@ -13,37 +13,7 @@ module.exports = {
], ],
darkMode: 'class', darkMode: 'class',
theme: { theme: {
extend: { extend: theme,
...theme,
colors: {
transparent: 'transparent',
current: 'currentColor',
...theme.colors,
},
backgroundImage: {
...theme.backgroundImage,
rainbow:
'linear-gradient(103.47deg, #FF077F 1.68%, #8028FF 47.49%, #0075FF 100%)',
'rainbow-shifted':
'linear-gradient(103.47deg, #0075FF 1.68%, #8028FF 47.49%, #FF077F 100%)',
highlight:
'linear-gradient(170deg, var(--tw-gradient-from), transparent var(--tw-gradient-to-position))',
},
keyframes: {
...theme.keyframes,
shake: {
'0%': { transform: 'translateX(0)' },
'25%': { transform: 'translateX(5px)' },
'50%': { transform: 'translateX(-5px)' },
'75%': { transform: 'translateX(5px)' },
'100%': { transform: 'translateX(0)' },
},
},
animation: {
...theme.animation,
shake: 'shake 200ms linear',
},
},
}, },
plugins: [vegaCustomClasses], plugins: [vegaCustomClasses],
}; };
-14
View File
@@ -1,14 +0,0 @@
#!/bin/sh
entrypoint="${1:-nginx}"
if [[ "$entrypoint" = "nginx" ]]; then
nginx -g 'daemon off;'
elif [[ "$entrypoint" = "ipfs" ]]; then
ipfs config profile apply server
ipfs config --json Addresses.Gateway '"/ip4/127.0.0.1/tcp/80"'
ipfs daemon
elif [[ "-c" ]]; then
shift
/bin/sh -c "$@"
fi
+1 -2
View File
@@ -20,8 +20,7 @@ RUN sh docker/docker-build.sh
FROM --platform=amd64 nginx:1.23-alpine@sha256:6318314189b40e73145a48060bff4783a116c34cc7241532d0d94198fb2c9629 FROM --platform=amd64 nginx:1.23-alpine@sha256:6318314189b40e73145a48060bff4783a116c34cc7241532d0d94198fb2c9629
# configuration of system # configuration of system
EXPOSE 80 EXPOSE 80
COPY docker/entrypoint.sh /entrypoint.sh COPY docker/run-ipfs.sh /run-ipfs.sh
ENTRYPOINT [ "/entrypoint.sh" ]
# Copy dist # Copy dist
COPY docker/nginx.conf /etc/nginx/conf.d/default.conf COPY docker/nginx.conf /etc/nginx/conf.d/default.conf
RUN rm -rf /usr/share/nginx/html/* RUN rm -rf /usr/share/nginx/html/*
+1 -2
View File
@@ -1,7 +1,6 @@
FROM --platform=amd64 nginx:1.23-alpine@sha256:6318314189b40e73145a48060bff4783a116c34cc7241532d0d94198fb2c9629 FROM --platform=amd64 nginx:1.23-alpine@sha256:6318314189b40e73145a48060bff4783a116c34cc7241532d0d94198fb2c9629
EXPOSE 80 EXPOSE 80
COPY docker/entrypoint.sh /entrypoint.sh COPY docker/run-ipfs.sh /run-ipfs.sh
ENTRYPOINT [ "/entrypoint.sh" ]
COPY docker/nginx.conf /etc/nginx/conf.d/default.conf COPY docker/nginx.conf /etc/nginx/conf.d/default.conf
RUN rm -rf /usr/share/nginx/html/* RUN rm -rf /usr/share/nginx/html/*
COPY ./dist-result/ /usr/share/nginx/html/ COPY ./dist-result/ /usr/share/nginx/html/
+4
View File
@@ -0,0 +1,4 @@
#!/bin/sh
ipfs config profile apply server
ipfs config --json Addresses.Gateway '"/ip4/127.0.0.1/tcp/80"'
ipfs daemon
+1 -1
View File
@@ -18,7 +18,7 @@ import {
VegaIconNames, VegaIconNames,
TooltipCellComponent, TooltipCellComponent,
} from '@vegaprotocol/ui-toolkit'; } from '@vegaprotocol/ui-toolkit';
import { AgGridLazy as AgGrid } from '@vegaprotocol/datagrid'; import { AgGrid } from '@vegaprotocol/datagrid';
import type { import type {
IGetRowsParams, IGetRowsParams,
IRowNode, IRowNode,
+1 -1
View File
@@ -13,7 +13,7 @@ import type {
VegaICellRendererParams, VegaICellRendererParams,
} from '@vegaprotocol/datagrid'; } from '@vegaprotocol/datagrid';
import { ProgressBarCell } from '@vegaprotocol/datagrid'; import { ProgressBarCell } from '@vegaprotocol/datagrid';
import { AgGridLazy as AgGrid, PriceCell } from '@vegaprotocol/datagrid'; import { AgGrid, PriceCell } from '@vegaprotocol/datagrid';
import type { ColDef } from 'ag-grid-community'; import type { ColDef } from 'ag-grid-community';
import { accountValuesComparator } from './accounts-table'; import { accountValuesComparator } from './accounts-table';
import { MarginHealthChart } from './margin-health-chart'; import { MarginHealthChart } from './margin-health-chart';
+5 -3
View File
@@ -14,6 +14,7 @@ import {
export type AnnouncementBannerProps = { export type AnnouncementBannerProps = {
app: AppNameType; app: AppNameType;
configUrl: string; configUrl: string;
background?: string;
}; };
// run only if below the allowed maximum delay ~24.8 days (https://developer.mozilla.org/en-US/docs/Web/API/setTimeout#maximum_delay_value) // run only if below the allowed maximum delay ~24.8 days (https://developer.mozilla.org/en-US/docs/Web/API/setTimeout#maximum_delay_value)
@@ -36,6 +37,7 @@ const doesEndInTheFuture = (now: Date, data: Announcement) => {
export const AnnouncementBanner = ({ export const AnnouncementBanner = ({
app, app,
configUrl, configUrl,
background,
}: AnnouncementBannerProps) => { }: AnnouncementBannerProps) => {
const [isVisible, setVisible] = useState(false); const [isVisible, setVisible] = useState(false);
const { data, reload } = useAnnouncement(app, configUrl); const { data, reload } = useAnnouncement(app, configUrl);
@@ -79,10 +81,10 @@ export const AnnouncementBanner = ({
} }
return ( return (
<Banner className="relative px-10"> <Banner className="relative px-10" background={background}>
<div <div
data-testid="app-announcement" data-testid="app-announcement"
className="relative font-alpha flex gap-2 justify-center text-center text-lg text-white" className="relative flex justify-center text-lg text-center text-white font-alpha gap-2"
> >
<span>{data.text}</span>{' '} <span>{data.text}</span>{' '}
{data.urlText && data.url && ( {data.urlText && data.url && (
@@ -90,7 +92,7 @@ export const AnnouncementBanner = ({
)} )}
</div> </div>
<button <button
className="absolute right-0 top-0 p-4 w-10 h-full flex items-center justify-center text-white" className="absolute top-0 right-0 flex items-center justify-center w-10 h-full p-4 text-white"
data-testid="app-announcement-close" data-testid="app-announcement-close"
onClick={() => { onClick={() => {
setVisible(false); setVisible(false);
+1 -1
View File
@@ -1,4 +1,4 @@
export * from './lib/ag-grid/ag-grid-lazy'; export * from './lib/ag-grid/ag-grid';
export * from './lib/column-definitions'; export * from './lib/column-definitions';
@@ -1,17 +0,0 @@
import { forwardRef, lazy } from 'react';
import type { AgGridReactProps, AgGridReact } from 'ag-grid-react';
type Props = AgGridReactProps & {
style?: React.CSSProperties;
gridRef?: React.Ref<AgGridReact>;
};
export const AgGridLazyInternal = lazy(() =>
import('./ag-grid-lazy-themed').then((module) => ({
default: module.AgGridThemed,
}))
);
export const AgGridLazy = forwardRef<AgGridReact, Props>((props, ref) => (
<AgGridLazyInternal {...props} gridRef={ref} />
));
+12
View File
@@ -0,0 +1,12 @@
import { forwardRef } from 'react';
import type { AgGridReactProps, AgGridReact } from 'ag-grid-react';
import { AgGridThemed } from './ag-grid-themed';
type Props = AgGridReactProps & {
style?: React.CSSProperties;
gridRef?: React.Ref<AgGridReact>;
};
export const AgGrid = forwardRef<AgGridReact, Props>((props, ref) => (
<AgGridThemed {...props} gridRef={ref} />
));
@@ -1,9 +1,6 @@
import { act, render, waitFor } from '@testing-library/react'; import { act, render, waitFor } from '@testing-library/react';
import { import { useDataGridEvents } from './use-datagrid-events';
useDataGridEvents, import { AgGridThemed } from './ag-grid/ag-grid-themed';
GRID_EVENT_DEBOUNCE_TIME,
} from './use-datagrid-events';
import { AgGridThemed } from './ag-grid/ag-grid-lazy-themed';
import type { MutableRefObject } from 'react'; import type { MutableRefObject } from 'react';
import { useRef } from 'react'; import { useRef } from 'react';
import type { AgGridReact } from 'ag-grid-react'; import type { AgGridReact } from 'ag-grid-react';
@@ -19,25 +16,29 @@ const gridProps = {
], ],
style: { width: 500, height: 300 }, style: { width: 500, height: 300 },
}; };
const GRID_EVENT_DEBOUNCE_TIME = 300;
let gridRef: MutableRefObject<AgGridReact | null>;
function TestComponent({
hookParams,
}: {
hookParams: Parameters<typeof useDataGridEvents>;
}) {
const hookCallbacks = useDataGridEvents(...hookParams);
gridRef = useRef<AgGridReact | null>(null);
return <AgGridThemed gridRef={gridRef} {...gridProps} {...hookCallbacks} />;
}
// Not using render hook so I can pass event callbacks // Not using render hook so I can pass event callbacks
// to a rendered grid // to a rendered grid
function setup(...args: Parameters<typeof useDataGridEvents>) { function setup(...args: Parameters<typeof useDataGridEvents>) {
let gridRef; return render(<TestComponent hookParams={args} />);
function TestComponent() {
const hookCallbacks = useDataGridEvents(...args);
gridRef = useRef<AgGridReact | null>(null);
return <AgGridThemed gridRef={gridRef} {...gridProps} {...hookCallbacks} />;
}
render(<TestComponent />);
return gridRef as unknown as MutableRefObject<AgGridReact>;
} }
describe('useDataGridEvents', () => { describe('useDataGridEvents', () => {
const originalWarn = console.warn; const originalWarn = console.warn;
beforeAll(() => { beforeAll(() => {
gridRef = undefined;
jest.useFakeTimers(); jest.useFakeTimers();
// disabling some ag grid warnings that are caused by test setup only // disabling some ag grid warnings that are caused by test setup only
@@ -56,15 +57,15 @@ describe('useDataGridEvents', () => {
columnState: undefined, columnState: undefined,
}; };
const result = setup(initialState, callback); setup(initialState, callback);
// column state was not updated, so the default width provided by the // column state was not updated, so the default width provided by the
// col def should be set // col def should be set
expect(result.current.columnApi.getColumnState()[0].width).toEqual( expect(gridRef.current?.columnApi.getColumnState()[0].width).toEqual(
gridProps.columnDefs[0].width gridProps.columnDefs[0].width
); );
// no filters set // no filters set
expect(result.current.api.getFilterModel()).toEqual({}); expect(gridRef.current?.api.getFilterModel()).toEqual({});
// Set filter // Set filter
const idFilter = { const idFilter = {
@@ -73,7 +74,7 @@ describe('useDataGridEvents', () => {
type: 'equals', type: 'equals',
}; };
await act(async () => { await act(async () => {
result.current.api.setFilterModel({ gridRef.current?.api.setFilterModel({
id: idFilter, id: idFilter,
}); });
}); });
@@ -89,7 +90,7 @@ describe('useDataGridEvents', () => {
}, },
}); });
callback.mockClear(); callback.mockClear();
expect(result.current.api.getFilterModel()['id']).toEqual(idFilter); expect(gridRef.current?.api.getFilterModel()['id']).toEqual(idFilter);
}); });
it('applies grid state on ready', async () => { it('applies grid state on ready', async () => {
@@ -106,11 +107,11 @@ describe('useDataGridEvents', () => {
columnState: [colState], columnState: [colState],
}; };
const result = setup(initialState, jest.fn()); setup(initialState, jest.fn());
await waitFor(() => { await waitFor(() => {
expect(result.current.api.getFilterModel()['id']).toEqual(idFilter); expect(gridRef.current?.api.getFilterModel()['id']).toEqual(idFilter);
expect(result.current.columnApi.getColumnState()[0]).toEqual( expect(gridRef.current?.columnApi.getColumnState()[0]).toEqual(
expect.objectContaining(colState) expect.objectContaining(colState)
); );
}); });
@@ -123,13 +124,13 @@ describe('useDataGridEvents', () => {
columnState: undefined, columnState: undefined,
}; };
const result = setup(initialState, callback); setup(initialState, callback);
const newWidth = 400; const newWidth = 400;
// Set col width multiple times // Set col width multiple times
await act(async () => { await act(async () => {
result.current.columnApi.setColumnWidth('id', newWidth); gridRef.current?.columnApi.setColumnWidth('id', newWidth);
}); });
expect(callback).not.toHaveBeenCalled(); expect(callback).not.toHaveBeenCalled();
@@ -140,4 +141,23 @@ describe('useDataGridEvents', () => {
expect(callback).toHaveBeenCalledTimes(0); expect(callback).toHaveBeenCalledTimes(0);
}); });
it('columns for autosizing should be handle', () => {
const callback = jest.fn();
const initialState = {
filterModel: undefined,
columnState: undefined,
};
const { rerender } = setup(initialState, callback, ['id']);
jest.spyOn(gridRef.current?.columnApi, 'autoSizeColumns');
rerender(<TestComponent hookParams={[initialState, callback, ['id']]} />);
act(() => {
gridRef.current?.api.setRowData([{ id: 'test-id' }]);
jest.advanceTimersByTime(GRID_EVENT_DEBOUNCE_TIME);
});
expect(gridRef.current?.columnApi.autoSizeColumns).toHaveBeenCalledWith([
'id',
]);
});
}); });
+15 -2
View File
@@ -6,6 +6,7 @@ import type {
FilterChangedEvent, FilterChangedEvent,
FirstDataRenderedEvent, FirstDataRenderedEvent,
SortChangedEvent, SortChangedEvent,
GridReadyEvent,
} from 'ag-grid-community'; } from 'ag-grid-community';
import { useCallback } from 'react'; import { useCallback } from 'react';
@@ -17,7 +18,8 @@ type State = {
export const useDataGridEvents = ( export const useDataGridEvents = (
state: State, state: State,
callback: (data: State) => void callback: (data: State) => void,
autoSizeColumns?: string[]
) => { ) => {
/** /**
* Callback for filter events * Callback for filter events
@@ -78,7 +80,7 @@ export const useDataGridEvents = (
* State only applied if found, otherwise columns sized to fit available space * State only applied if found, otherwise columns sized to fit available space
*/ */
const onGridReady = useCallback( const onGridReady = useCallback(
({ api, columnApi }: FirstDataRenderedEvent) => { ({ api, columnApi }: GridReadyEvent) => {
if (!api || !columnApi) return; if (!api || !columnApi) return;
if (state.columnState) { if (state.columnState) {
@@ -97,6 +99,16 @@ export const useDataGridEvents = (
[state] [state]
); );
const onFirstDataRendered = useCallback(
({ columnApi }: FirstDataRenderedEvent) => {
if (!columnApi) return;
if (!state?.columnState && autoSizeColumns?.length) {
columnApi.autoSizeColumns(autoSizeColumns);
}
},
[state, autoSizeColumns]
);
return { return {
onGridReady, onGridReady,
// these events don't use the 'finished' flag // these events don't use the 'finished' flag
@@ -106,5 +118,6 @@ export const useDataGridEvents = (
// these trigger a lot so this callback uses the 'finished' flag // these trigger a lot so this callback uses the 'finished' flag
onColumnMoved: onDebouncedColumnChange, onColumnMoved: onDebouncedColumnChange,
onColumnResized: onDebouncedColumnChange, onColumnResized: onDebouncedColumnChange,
onFirstDataRendered,
}; };
}; };
@@ -12,6 +12,7 @@ import { formatRange, formatValue } from '@vegaprotocol/utils';
import { marketMarginDataProvider } from '@vegaprotocol/accounts'; import { marketMarginDataProvider } from '@vegaprotocol/accounts';
import { useDataProvider } from '@vegaprotocol/data-provider'; import { useDataProvider } from '@vegaprotocol/data-provider';
import * as AccordionPrimitive from '@radix-ui/react-accordion'; import * as AccordionPrimitive from '@radix-ui/react-accordion';
import * as Schema from '@vegaprotocol/types';
import { import {
MARGIN_DIFF_TOOLTIP_TEXT, MARGIN_DIFF_TOOLTIP_TEXT,
@@ -37,14 +38,16 @@ export interface DealTicketFeeDetailsProps {
assetSymbol: string; assetSymbol: string;
order: OrderSubmissionBody['orderSubmission']; order: OrderSubmissionBody['orderSubmission'];
market: Market; market: Market;
isMarketInAuction?: boolean;
} }
export const DealTicketFeeDetails = ({ export const DealTicketFeeDetails = ({
assetSymbol, assetSymbol,
order, order,
market, market,
isMarketInAuction,
}: DealTicketFeeDetailsProps) => { }: DealTicketFeeDetailsProps) => {
const feeEstimate = useEstimateFees(order); const feeEstimate = useEstimateFees(order, isMarketInAuction);
const { settlementAsset: asset } = const { settlementAsset: asset } =
market.tradableInstrument.instrument.product; market.tradableInstrument.instrument.product;
const { decimals: assetDecimals, quantum } = asset; const { decimals: assetDecimals, quantum } = asset;
@@ -64,7 +67,7 @@ export const DealTicketFeeDetails = ({
<> <>
<span> <span>
{t( {t(
`An estimate of the most you would be expected to pay in fees, in the market's settlement asset ${assetSymbol}.` `An estimate of the most you would be expected to pay in fees, in the market's settlement asset ${assetSymbol}. Fees estimated are "taker" fees and will only be payable if the order trades aggressively. Rebate equal to the maker portion will be paid to the trader if the order trades passively.`
)} )}
</span> </span>
<FeesBreakdown <FeesBreakdown
@@ -87,6 +90,7 @@ export interface DealTicketMarginDetailsProps {
onMarketClick?: (marketId: string, metaKey?: boolean) => void; onMarketClick?: (marketId: string, metaKey?: boolean) => void;
assetSymbol: string; assetSymbol: string;
positionEstimate: EstimatePositionQuery['estimatePosition']; positionEstimate: EstimatePositionQuery['estimatePosition'];
side: Schema.Side;
} }
export const DealTicketMarginDetails = ({ export const DealTicketMarginDetails = ({
@@ -96,6 +100,7 @@ export const DealTicketMarginDetails = ({
market, market,
onMarketClick, onMarketClick,
positionEstimate, positionEstimate,
side,
}: DealTicketMarginDetailsProps) => { }: DealTicketMarginDetailsProps) => {
const [breakdownDialog, setBreakdownDialog] = useState(false); const [breakdownDialog, setBreakdownDialog] = useState(false);
const { pubKey: partyId } = useVegaWallet(); const { pubKey: partyId } = useVegaWallet();
@@ -165,10 +170,7 @@ export const DealTicketMarginDetails = ({
: '0', : '0',
assetDecimals assetDecimals
)} )}
formattedValue={formatRange( formattedValue={formatValue(
deductionFromCollateralBestCase > 0
? deductionFromCollateralBestCase.toString()
: '0',
deductionFromCollateralWorstCase > 0 deductionFromCollateralWorstCase > 0
? deductionFromCollateralWorstCase.toString() ? deductionFromCollateralWorstCase.toString()
: '0', : '0',
@@ -187,8 +189,7 @@ export const DealTicketMarginDetails = ({
marginEstimate?.worstCase.initialLevel, marginEstimate?.worstCase.initialLevel,
assetDecimals assetDecimals
)} )}
formattedValue={formatRange( formattedValue={formatValue(
marginEstimate?.bestCase.initialLevel,
marginEstimate?.worstCase.initialLevel, marginEstimate?.worstCase.initialLevel,
assetDecimals, assetDecimals,
quantum quantum
@@ -200,6 +201,7 @@ export const DealTicketMarginDetails = ({
} }
let liquidationPriceEstimate = emptyValue; let liquidationPriceEstimate = emptyValue;
let liquidationPriceEstimateRange = emptyValue;
if (liquidationEstimate) { if (liquidationEstimate) {
const liquidationEstimateBestCaseIncludingBuyOrders = BigInt( const liquidationEstimateBestCaseIncludingBuyOrders = BigInt(
@@ -209,8 +211,7 @@ export const DealTicketMarginDetails = ({
liquidationEstimate.bestCase.including_sell_orders.replace(/\..*/, '') liquidationEstimate.bestCase.including_sell_orders.replace(/\..*/, '')
); );
const liquidationEstimateBestCase = const liquidationEstimateBestCase =
liquidationEstimateBestCaseIncludingBuyOrders > side === Schema.Side.SIDE_BUY
liquidationEstimateBestCaseIncludingSellOrders
? liquidationEstimateBestCaseIncludingBuyOrders ? liquidationEstimateBestCaseIncludingBuyOrders
: liquidationEstimateBestCaseIncludingSellOrders; : liquidationEstimateBestCaseIncludingSellOrders;
@@ -221,14 +222,19 @@ export const DealTicketMarginDetails = ({
liquidationEstimate.worstCase.including_sell_orders.replace(/\..*/, '') liquidationEstimate.worstCase.including_sell_orders.replace(/\..*/, '')
); );
const liquidationEstimateWorstCase = const liquidationEstimateWorstCase =
liquidationEstimateWorstCaseIncludingBuyOrders > side === Schema.Side.SIDE_BUY
liquidationEstimateWorstCaseIncludingSellOrders
? liquidationEstimateWorstCaseIncludingBuyOrders ? liquidationEstimateWorstCaseIncludingBuyOrders
: liquidationEstimateWorstCaseIncludingSellOrders; : liquidationEstimateWorstCaseIncludingSellOrders;
// The estimate order query API gives us the liquidation price in formatted by asset decimals. // The estimate order query API gives us the liquidation price in formatted by asset decimals.
// We need to calculate it with asset decimals, but display it with market decimals precision until the API changes. // We need to calculate it with asset decimals, but display it with market decimals precision until the API changes.
liquidationPriceEstimate = formatRange( liquidationPriceEstimate = formatValue(
liquidationEstimateWorstCase.toString(),
assetDecimals,
undefined,
market.decimalPlaces
);
liquidationPriceEstimateRange = formatRange(
(liquidationEstimateBestCase < liquidationEstimateWorstCase (liquidationEstimateBestCase < liquidationEstimateWorstCase
? liquidationEstimateBestCase ? liquidationEstimateBestCase
: liquidationEstimateWorstCase : liquidationEstimateWorstCase
@@ -284,11 +290,9 @@ export const DealTicketMarginDetails = ({
assetDecimals assetDecimals
) ?? '-' ) ?? '-'
} }
noUnderline
> >
<div className="font-mono text-right"> <div className="font-mono text-right">
{formatRange( {formatValue(
marginRequiredBestCase,
marginRequiredWorstCase, marginRequiredWorstCase,
assetDecimals, assetDecimals,
quantum quantum
@@ -346,7 +350,7 @@ export const DealTicketMarginDetails = ({
{projectedMargin} {projectedMargin}
<KeyValue <KeyValue
label={t('Liquidation')} label={t('Liquidation')}
value={liquidationPriceEstimate} value={liquidationPriceEstimateRange}
formattedValue={liquidationPriceEstimate} formattedValue={liquidationPriceEstimate}
symbol={quoteName} symbol={quoteName}
labelDescription={LIQUIDATION_PRICE_ESTIMATE_TOOLTIP_TEXT} labelDescription={LIQUIDATION_PRICE_ESTIMATE_TOOLTIP_TEXT}
@@ -36,7 +36,7 @@ import {
formatValue, formatValue,
} from '@vegaprotocol/utils'; } from '@vegaprotocol/utils';
import { activeOrdersProvider } from '@vegaprotocol/orders'; import { activeOrdersProvider } from '@vegaprotocol/orders';
import { getDerivedPrice } from '@vegaprotocol/markets'; import { getDerivedPrice, isMarketInAuction } from '@vegaprotocol/markets';
import { import {
validateExpiration, validateExpiration,
validateMarketState, validateMarketState,
@@ -182,6 +182,7 @@ export const DealTicket = ({
const iceberg = watch('iceberg'); const iceberg = watch('iceberg');
const peakSize = watch('peakSize'); const peakSize = watch('peakSize');
const expiresAt = watch('expiresAt'); const expiresAt = watch('expiresAt');
const postOnly = watch('postOnly');
useEffect(() => { useEffect(() => {
const size = storedFormValues?.[dealTicketType]?.size; const size = storedFormValues?.[dealTicketType]?.size;
@@ -211,6 +212,7 @@ export const DealTicket = ({
size: rawSize, size: rawSize,
timeInForce, timeInForce,
type, type,
postOnly,
}, },
market.id, market.id,
market.decimalPlaces, market.decimalPlaces,
@@ -219,8 +221,7 @@ export const DealTicket = ({
const price = const price =
normalizedOrder && normalizedOrder &&
marketPrice && getDerivedPrice(normalizedOrder, marketPrice ?? undefined);
getDerivedPrice(normalizedOrder, marketPrice);
const notionalSize = getNotionalSize( const notionalSize = getNotionalSize(
price, price,
@@ -474,6 +475,7 @@ export const DealTicket = ({
} }
assetSymbol={assetSymbol} assetSymbol={assetSymbol}
market={market} market={market}
isMarketInAuction={isMarketInAuction(marketData.marketTradingMode)}
/> />
</div> </div>
<Controller <Controller
@@ -676,6 +678,7 @@ export const DealTicket = ({
)} )}
</Button> </Button>
<DealTicketMarginDetails <DealTicketMarginDetails
side={normalizedOrder.side}
onMarketClick={onMarketClick} onMarketClick={onMarketClick}
assetSymbol={assetSymbol} assetSymbol={assetSymbol}
marginAccountBalance={marginAccountBalance} marginAccountBalance={marginAccountBalance}
@@ -47,7 +47,7 @@ export const KeyValue = ({
<Tooltip description={labelDescription}> <Tooltip description={labelDescription}>
<div className="text-muted">{label}</div> <div className="text-muted">{label}</div>
</Tooltip> </Tooltip>
<Tooltip description={`${value ?? '-'} ${symbol || ''}`} noUnderline> <Tooltip description={`${value ?? '-'} ${symbol || ''}`}>
{valueElement} {valueElement}
</Tooltip> </Tooltip>
</div> </div>
@@ -0,0 +1,78 @@
import { renderHook } from '@testing-library/react';
import { useEstimateFees } from './use-estimate-fees';
import { Side, OrderTimeInForce, OrderType } from '@vegaprotocol/types';
import type { EstimateFeesQuery } from './__generated__/EstimateOrder';
const data: EstimateFeesQuery = {
estimateFees: {
totalFeeAmount: '12',
fees: {
infrastructureFee: '2',
liquidityFee: '4',
makerFee: '6',
},
},
};
const mockUseEstimateFeesQuery = jest.fn((...args) => ({
data,
}));
jest.mock('./__generated__/EstimateOrder', () => ({
...jest.requireActual('@vegaprotocol/data-provider'),
useEstimateFeesQuery: jest.fn((...args) => mockUseEstimateFeesQuery(...args)),
}));
jest.mock('@vegaprotocol/wallet', () => ({
useVegaWallet: () => ({ pubKey: 'pubKey' }),
}));
describe('useEstimateFees', () => {
it('returns 0 as estimated values if order is postOnly', () => {
const { result } = renderHook(() =>
useEstimateFees({
marketId: 'marketId',
side: Side.SIDE_BUY,
size: '1',
price: '1',
timeInForce: OrderTimeInForce.TIME_IN_FORCE_FOK,
type: OrderType.TYPE_LIMIT,
postOnly: true,
})
);
expect(result.current).toEqual({
totalFeeAmount: '0',
fees: {
infrastructureFee: '0',
liquidityFee: '0',
makerFee: '0',
},
});
expect(mockUseEstimateFeesQuery.mock.lastCall?.[0].skip).toBeTruthy();
});
it('divide values by 2 if market is in auction', () => {
const { result } = renderHook(() =>
useEstimateFees(
{
marketId: 'marketId',
side: Side.SIDE_BUY,
size: '1',
price: '1',
timeInForce: OrderTimeInForce.TIME_IN_FORCE_FOK,
type: OrderType.TYPE_LIMIT,
},
true
)
);
expect(result.current).toEqual({
totalFeeAmount: '6',
fees: {
infrastructureFee: '1',
liquidityFee: '2',
makerFee: '3',
},
});
});
});
@@ -1,13 +1,16 @@
import { useVegaWallet } from '@vegaprotocol/wallet'; import { useVegaWallet } from '@vegaprotocol/wallet';
import type { OrderSubmissionBody } from '@vegaprotocol/wallet'; import type { OrderSubmissionBody } from '@vegaprotocol/wallet';
import type { EstimateFeesQuery } from './__generated__/EstimateOrder';
import { useEstimateFeesQuery } from './__generated__/EstimateOrder'; import { useEstimateFeesQuery } from './__generated__/EstimateOrder';
export const useEstimateFees = ( const divideByTwo = (n: string) => (BigInt(n) / BigInt(2)).toString();
order?: OrderSubmissionBody['orderSubmission']
) => {
const { pubKey } = useVegaWallet();
export const useEstimateFees = (
order?: OrderSubmissionBody['orderSubmission'],
isMarketInAuction?: boolean
): EstimateFeesQuery['estimateFees'] | undefined => {
const { pubKey } = useVegaWallet();
const { data } = useEstimateFeesQuery({ const { data } = useEstimateFeesQuery({
variables: order && { variables: order && {
marketId: order.marketId, marketId: order.marketId,
@@ -19,7 +22,28 @@ export const useEstimateFees = (
type: order.type, type: order.type,
}, },
fetchPolicy: 'no-cache', fetchPolicy: 'no-cache',
skip: !pubKey || !order?.size || !order?.price, skip: !pubKey || !order?.size || !order?.price || order.postOnly,
}); });
return data?.estimateFees; if (order?.postOnly) {
return {
totalFeeAmount: '0',
fees: {
infrastructureFee: '0',
liquidityFee: '0',
makerFee: '0',
},
};
}
return isMarketInAuction && data?.estimateFees
? {
totalFeeAmount: divideByTwo(data.estimateFees.totalFeeAmount),
fees: {
infrastructureFee: divideByTwo(
data.estimateFees.fees.infrastructureFee
),
liquidityFee: divideByTwo(data.estimateFees.fees.liquidityFee),
makerFee: divideByTwo(data.estimateFees.fees.makerFee),
},
}
: data?.estimateFees;
}; };
+1 -1
View File
@@ -6,7 +6,7 @@ import {
isNumeric, isNumeric,
} from '@vegaprotocol/utils'; } from '@vegaprotocol/utils';
import type { ColDef } from 'ag-grid-community'; import type { ColDef } from 'ag-grid-community';
import { AgGridLazy as AgGrid } from '@vegaprotocol/datagrid'; import { AgGrid } from '@vegaprotocol/datagrid';
import type { import type {
VegaICellRendererParams, VegaICellRendererParams,
VegaValueFormatterParams, VegaValueFormatterParams,
-1
View File
@@ -175,5 +175,4 @@ export const ExternalLinks = {
export const TokenStaticLinks = { export const TokenStaticLinks = {
PROPOSAL_PAGE: ':tokenUrl/proposals/:proposalId', PROPOSAL_PAGE: ':tokenUrl/proposals/:proposalId',
UPDATE_PROPOSAL_PAGE: ':tokenUrl/proposals/propose/update-market', UPDATE_PROPOSAL_PAGE: ':tokenUrl/proposals/propose/update-market',
ASSOCIATE: 'token/associate',
}; };
-5
View File
@@ -9,14 +9,12 @@ import { fillsWithMarketProvider } from './fills-data-provider';
interface FillsManagerProps { interface FillsManagerProps {
partyId: string; partyId: string;
marketId?: string;
onMarketClick?: (marketId: string, metaKey?: boolean) => void; onMarketClick?: (marketId: string, metaKey?: boolean) => void;
gridProps: ReturnType<typeof useDataGridEvents>; gridProps: ReturnType<typeof useDataGridEvents>;
} }
export const FillsManager = ({ export const FillsManager = ({
partyId, partyId,
marketId,
onMarketClick, onMarketClick,
gridProps, gridProps,
}: FillsManagerProps) => { }: FillsManagerProps) => {
@@ -24,9 +22,6 @@ export const FillsManager = ({
const filter: Schema.TradesFilter | Schema.TradesSubscriptionFilter = { const filter: Schema.TradesFilter | Schema.TradesSubscriptionFilter = {
partyIds: [partyId], partyIds: [partyId],
}; };
if (marketId) {
filter.marketIds = [marketId];
}
const { data, error } = useDataProvider({ const { data, error } = useDataProvider({
dataProvider: fillsWithMarketProvider, dataProvider: fillsWithMarketProvider,
update: ({ data }) => { update: ({ data }) => {
+2 -2
View File
@@ -15,7 +15,7 @@ import {
import { t } from '@vegaprotocol/i18n'; import { t } from '@vegaprotocol/i18n';
import * as Schema from '@vegaprotocol/types'; import * as Schema from '@vegaprotocol/types';
import { import {
AgGridLazy as AgGrid, AgGrid,
positiveClassNames, positiveClassNames,
negativeClassNames, negativeClassNames,
MarketNameCell, MarketNameCell,
@@ -300,7 +300,7 @@ const FeesBreakdownTooltip = ({
return ( return (
<div <div
data-testid="fee-breakdown-tooltip" data-testid="fee-breakdown-tooltip"
className="max-w-sm bg-vega-light-100 dark:bg-vega-dark-100 border border-vega-light-200 dark:border-vega-dark-200 px-4 py-2 z-20 rounded text-sm break-word text-black dark:text-white" className="z-20 max-w-sm px-4 py-2 text-sm text-black border rounded bg-vega-light-100 dark:bg-vega-dark-100 border-vega-light-200 dark:border-vega-dark-200 break-word dark:text-white"
> >
{role === MAKER && ( {role === MAKER && (
<> <>
+1 -1
View File
@@ -7,7 +7,7 @@ import {
} from '@vegaprotocol/utils'; } from '@vegaprotocol/utils';
import { t } from '@vegaprotocol/i18n'; import { t } from '@vegaprotocol/i18n';
import type { TypedDataAgGrid } from '@vegaprotocol/datagrid'; import type { TypedDataAgGrid } from '@vegaprotocol/datagrid';
import { AgGridLazy as AgGrid } from '@vegaprotocol/datagrid'; import { AgGrid } from '@vegaprotocol/datagrid';
import { TooltipCellComponent } from '@vegaprotocol/ui-toolkit'; import { TooltipCellComponent } from '@vegaprotocol/ui-toolkit';
import type { import type {
ColDef, ColDef,
+1 -1
View File
@@ -175,7 +175,7 @@ export const Orderbook = ({
); );
return ( return (
<div className="h-full text-xs grid grid-rows-[1fr_min-content]"> <div className="h-full text-xs grid grid-rows-[1fr_min-content] overflow-hidden">
<div> <div>
<ReactVirtualizedAutoSizer> <ReactVirtualizedAutoSizer>
{({ width, height }) => { {({ width, height }) => {
@@ -60,6 +60,7 @@ export const FeesBreakdown = ({
.plus(fees.infrastructureFee) .plus(fees.infrastructureFee)
.plus(fees.liquidityFee) .plus(fees.liquidityFee)
.toString(); .toString();
if (totalFees === '0') return null;
const formatValue = (value: string | number | null | undefined): string => { const formatValue = (value: string | number | null | undefined): string => {
return value && !isNaN(Number(value)) return value && !isNaN(Number(value))
? addDecimalsFormatNumber(value, decimals) ? addDecimalsFormatNumber(value, decimals)
+1 -1
View File
@@ -33,7 +33,7 @@ export const getDerivedPrice = (
type: Schema.OrderType; type: Schema.OrderType;
price?: string | undefined; price?: string | undefined;
}, },
marketPrice: string marketPrice?: string
) => { ) => {
// If order type is market we should use either the mark price // If order type is market we should use either the mark price
// or the uncrossing price. If order type is limit use the price // or the uncrossing price. If order type is limit use the price
+2 -6
View File
@@ -96,15 +96,11 @@ export const calcTradedFactor = (m: MarketMaybeWithDataAndCandles) => {
const volume = Number(calcCandleVolume(m.candles || []) || 0); const volume = Number(calcCandleVolume(m.candles || []) || 0);
const price = m.data?.markPrice ? Number(m.data.markPrice) : 0; const price = m.data?.markPrice ? Number(m.data.markPrice) : 0;
const quantum = Number( const quantum = Number(
m.tradableInstrument.instrument.product.settlementAsset?.quantum m.tradableInstrument.instrument.product.settlementAsset.quantum
); );
const decimals = Number( const decimals = Number(
m.tradableInstrument.instrument.product.settlementAsset?.decimals m.tradableInstrument.instrument.product.settlementAsset.decimals
); );
// `settlementAsset` can be undefined hence there's no quantum or decimals
if (isNaN(quantum) || isNaN(decimals)) return 0;
const fp = toBigNum(price, decimals); const fp = toBigNum(price, decimals);
const fq = toBigNum(quantum, decimals); const fq = toBigNum(quantum, decimals);
const factor = fq.multipliedBy(fp).multipliedBy(volume); const factor = fq.multipliedBy(fp).multipliedBy(volume);
@@ -17,7 +17,7 @@ import {
import type { ForwardedRef } from 'react'; import type { ForwardedRef } from 'react';
import { memo, forwardRef, useMemo } from 'react'; import { memo, forwardRef, useMemo } from 'react';
import { import {
AgGridLazy as AgGrid, AgGrid,
SetFilter, SetFilter,
DateRangeFilter, DateRangeFilter,
negativeClassNames, negativeClassNames,
@@ -79,6 +79,7 @@ export const OrderListTable = memo<
() => [ () => [
{ {
headerName: t('Market'), headerName: t('Market'),
colId: 'instrument-code',
field: 'market.tradableInstrument.instrument.code', field: 'market.tradableInstrument.instrument.code',
cellRenderer: 'MarketNameCell', cellRenderer: 'MarketNameCell',
cellRendererParams: { idPath: 'market.id', onMarketClick }, cellRendererParams: { idPath: 'market.id', onMarketClick },
@@ -18,7 +18,7 @@ import {
} from '@vegaprotocol/ui-toolkit'; } from '@vegaprotocol/ui-toolkit';
import { memo, useMemo } from 'react'; import { memo, useMemo } from 'react';
import { import {
AgGridLazy as AgGrid, AgGrid,
SetFilter, SetFilter,
DateRangeFilter, DateRangeFilter,
negativeClassNames, negativeClassNames,
+1 -1
View File
@@ -8,7 +8,7 @@ import type {
VegaICellRendererParams, VegaICellRendererParams,
} from '@vegaprotocol/datagrid'; } from '@vegaprotocol/datagrid';
import { import {
AgGridLazy as AgGrid, AgGrid,
COL_DEFS, COL_DEFS,
PriceFlashCell, PriceFlashCell,
signedNumberCssClassRules, signedNumberCssClassRules,
@@ -100,7 +100,8 @@ describe('ProposalsList', () => {
const container = within( const container = within(
document.querySelector(rowContainerSelector) as HTMLElement document.querySelector(rowContainerSelector) as HTMLElement
); );
expect(container.getAllByRole('row')).toHaveLength(
expect(await container.findAllByRole('row')).toHaveLength(
// @ts-ignore data is mocked // @ts-ignore data is mocked
mock?.result?.data.proposalsConnection.edges.length mock?.result?.data.proposalsConnection.edges.length
); );
@@ -1,5 +1,5 @@
import type { FC } from 'react'; import type { FC } from 'react';
import { AgGridLazy as AgGrid } from '@vegaprotocol/datagrid'; import { AgGrid } from '@vegaprotocol/datagrid';
import { t } from '@vegaprotocol/i18n'; import { t } from '@vegaprotocol/i18n';
import * as Types from '@vegaprotocol/types'; import * as Types from '@vegaprotocol/types';
import { removePaginationWrapper } from '@vegaprotocol/utils'; import { removePaginationWrapper } from '@vegaprotocol/utils';

Some files were not shown because too many files have changed in this diff Show More