Compare commits
53
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5b6c8b07f7 | ||
|
|
fb451b862b | ||
|
|
a3d5952074 | ||
|
|
f1f2b58b4d | ||
|
|
66b5e72ea4 | ||
|
|
402fdf13a3 | ||
|
|
1bfbffa1de | ||
|
|
b6647538ce | ||
|
|
b67ef4b2ad | ||
|
|
2d23e86a6a | ||
|
|
ee1be26f59 | ||
|
|
05b63be707 | ||
|
|
a8b58628e9 | ||
|
|
7acc7f247d | ||
|
|
0735d23a6b | ||
|
|
ae760922a8 | ||
|
|
bb17029837 | ||
|
|
1afd9227e4 | ||
|
|
7f967f49bf | ||
|
|
a5b009c21d | ||
|
|
23c4252be6 | ||
|
|
db568a3361 | ||
|
|
00cf58e5e1 | ||
|
|
9fd00f52fa | ||
|
|
5739a8ed9b | ||
|
|
f97a7b2e0e | ||
|
|
ee8e456ca6 | ||
|
|
8cacb83637 | ||
|
|
42556af6df | ||
|
|
480b667080 | ||
|
|
fdde2ca8fa | ||
|
|
dd44c67cdf | ||
|
|
5a1be05565 | ||
|
|
35f7394c29 | ||
|
|
0dbe3550e2 | ||
|
|
b364bf27f0 | ||
|
|
614da1b070 | ||
|
|
d522af60d1 | ||
|
|
0eebf06589 | ||
|
|
78130f86a0 | ||
|
|
185efaf896 | ||
|
|
25b88d6afa | ||
|
|
a53c868082 | ||
|
|
19433907c2 | ||
|
|
2379d532b3 | ||
|
|
34e4311d4b | ||
|
|
7d6763d7a7 | ||
|
|
bc3620d85e | ||
|
|
8f56f26b16 | ||
|
|
af2053639f | ||
|
|
22caee162a | ||
|
|
c3e9148127 | ||
|
|
82b667bd1e |
@@ -4,14 +4,54 @@ on:
|
|||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- release/*
|
- release/*
|
||||||
|
- develop
|
||||||
pull_request:
|
pull_request:
|
||||||
types:
|
types:
|
||||||
- opened
|
- opened
|
||||||
- reopened
|
|
||||||
- synchronize
|
|
||||||
- ready_for_review
|
- ready_for_review
|
||||||
|
- reopened
|
||||||
|
- edited
|
||||||
|
- synchronize
|
||||||
jobs:
|
jobs:
|
||||||
|
node-modules:
|
||||||
|
runs-on: ubuntu-22.04
|
||||||
|
name: 'Cache yarn modules'
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Cache node modules
|
||||||
|
id: cache
|
||||||
|
uses: actions/cache@v3
|
||||||
|
with:
|
||||||
|
path: node_modules
|
||||||
|
key: ${{ runner.os }}-cache-node-modules-${{ hashFiles('yarn.lock') }}
|
||||||
|
# comment out "resotre-keys" if you need to rebuild yarn from 0
|
||||||
|
restore-keys: |
|
||||||
|
${{ runner.os }}-cache-node-modules-
|
||||||
|
|
||||||
|
- name: Setup node
|
||||||
|
uses: actions/setup-node@v3
|
||||||
|
if: steps.cache.outputs.cache-hit != 'true'
|
||||||
|
with:
|
||||||
|
node-version-file: '.nvmrc'
|
||||||
|
# https://stackoverflow.com/questions/61010294/how-to-cache-yarn-packages-in-github-actions
|
||||||
|
cache: yarn
|
||||||
|
|
||||||
|
- name: yarn install
|
||||||
|
if: steps.cache.outputs.cache-hit != 'true'
|
||||||
|
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-test-build:
|
lint-test-build:
|
||||||
|
timeout-minutes: 20
|
||||||
|
needs: node-modules
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-22.04
|
||||||
name: '(CI) lint + unit test + build'
|
name: '(CI) lint + unit test + build'
|
||||||
steps:
|
steps:
|
||||||
@@ -27,8 +67,11 @@ jobs:
|
|||||||
# https://stackoverflow.com/questions/61010294/how-to-cache-yarn-packages-in-github-actions
|
# https://stackoverflow.com/questions/61010294/how-to-cache-yarn-packages-in-github-actions
|
||||||
cache: yarn
|
cache: yarn
|
||||||
|
|
||||||
- name: Install root dependencies
|
- name: Cache node modules
|
||||||
run: yarn install --frozen-lockfile
|
uses: actions/cache@v3
|
||||||
|
with:
|
||||||
|
path: node_modules
|
||||||
|
key: ${{ runner.os }}-cache-node-modules-${{ hashFiles('yarn.lock') }}
|
||||||
|
|
||||||
- name: Derive appropriate SHAs for base and head for `nx affected` commands
|
- name: Derive appropriate SHAs for base and head for `nx affected` commands
|
||||||
uses: nrwl/nx-set-shas@v3
|
uses: nrwl/nx-set-shas@v3
|
||||||
@@ -48,7 +91,7 @@ jobs:
|
|||||||
run: yarn nx affected:test
|
run: yarn nx affected:test
|
||||||
|
|
||||||
- name: Build affected
|
- name: Build affected
|
||||||
run: yarn nx affected:build
|
run: yarn nx affected:build || (yarn install && yarn nx affected:build)
|
||||||
|
|
||||||
# See affected apps
|
# See affected apps
|
||||||
- name: See affected apps
|
- name: See affected apps
|
||||||
@@ -95,6 +138,43 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
projects: ${{ needs.lint-test-build.outputs.projects }}
|
projects: ${{ needs.lint-test-build.outputs.projects }}
|
||||||
|
|
||||||
|
dist-check:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: publish-dist
|
||||||
|
if: ${{ github.event_name == 'pull_request' }}
|
||||||
|
name: '(CD) comment preview links'
|
||||||
|
steps:
|
||||||
|
- name: Find Comment
|
||||||
|
uses: peter-evans/find-comment@v2
|
||||||
|
id: fc
|
||||||
|
with:
|
||||||
|
issue-number: ${{ github.event.pull_request.number }}
|
||||||
|
body-includes: Previews
|
||||||
|
|
||||||
|
- name: Inject slug/short variables
|
||||||
|
if: ${{ steps.fc.outputs.comment-id == 0 }}
|
||||||
|
uses: rlespinasse/github-slug-action@v4
|
||||||
|
with:
|
||||||
|
prefix: CI_
|
||||||
|
|
||||||
|
- name: Create comment
|
||||||
|
if: ${{ steps.fc.outputs.comment-id == 0 }}
|
||||||
|
uses: peter-evans/create-or-update-comment@v3
|
||||||
|
with:
|
||||||
|
issue-number: ${{ github.event.pull_request.number }}
|
||||||
|
body: |
|
||||||
|
Previews
|
||||||
|
- explorer https://explorer.${{ env.CI_GITHUB_REF_NAME }}.vega.rocks
|
||||||
|
- trading https://trading.${{ env.CI_GITHUB_REF_NAME }}.vega.rocks
|
||||||
|
- governance https://governance.${{ env.CI_GITHUB_REF_NAME }}.vega.rocks
|
||||||
|
|
||||||
|
cypress-check:
|
||||||
|
name: '(CI) cypress - check'
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: cypress
|
||||||
|
steps:
|
||||||
|
- run: echo Done!
|
||||||
|
|
||||||
# Report single result at the end, to avoid mess with required checks in PR
|
# Report single result at the end, to avoid mess with required checks in PR
|
||||||
cypress-result:
|
cypress-result:
|
||||||
if: ${{ always() }}
|
if: ${{ always() }}
|
||||||
|
|||||||
@@ -72,6 +72,18 @@ jobs:
|
|||||||
CYPRESS_SLACK_WEBHOOK: ${{ secrets.CYPRESS_SLACK_WEBHOOK }}
|
CYPRESS_SLACK_WEBHOOK: ${{ secrets.CYPRESS_SLACK_WEBHOOK }}
|
||||||
CYPRESS_VEGA_WALLET_API_TOKEN: ${{ steps.setup-vega.outputs.token }}
|
CYPRESS_VEGA_WALLET_API_TOKEN: ${{ steps.setup-vega.outputs.token }}
|
||||||
|
|
||||||
|
######
|
||||||
|
## Show test summary on github page
|
||||||
|
######
|
||||||
|
|
||||||
|
- name: Show test summary
|
||||||
|
if: success() || failure()
|
||||||
|
uses: dorny/test-reporter@v1
|
||||||
|
with:
|
||||||
|
name: UI Tests
|
||||||
|
path: mochawesome.json
|
||||||
|
reporter: mocha-json
|
||||||
|
|
||||||
######
|
######
|
||||||
## Upload logs
|
## Upload logs
|
||||||
######
|
######
|
||||||
@@ -91,5 +103,5 @@ jobs:
|
|||||||
- uses: actions/upload-artifact@v3
|
- uses: actions/upload-artifact@v3
|
||||||
if: ${{ failure() }}
|
if: ${{ failure() }}
|
||||||
with:
|
with:
|
||||||
name: logs-${{ matrix.project }}
|
name: test-report
|
||||||
path: /home/runner/.vegacapsule/testnet/logs
|
path: frontend-monorepo/apps/trading-e2e/reports
|
||||||
|
|||||||
@@ -2,15 +2,11 @@
|
|||||||
name: Verify PR title
|
name: Verify PR title
|
||||||
|
|
||||||
on:
|
on:
|
||||||
pull_request:
|
workflow_call:
|
||||||
types:
|
|
||||||
- opened
|
|
||||||
- ready_for_review
|
|
||||||
- reopened
|
|
||||||
- edited
|
|
||||||
- synchronize
|
|
||||||
jobs:
|
jobs:
|
||||||
lint_pr:
|
lint_pr:
|
||||||
|
timeout-minutes: 10
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-22.04
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
@@ -23,8 +19,11 @@ jobs:
|
|||||||
# https://stackoverflow.com/questions/61010294/how-to-cache-yarn-packages-in-github-actions
|
# https://stackoverflow.com/questions/61010294/how-to-cache-yarn-packages-in-github-actions
|
||||||
cache: yarn
|
cache: yarn
|
||||||
|
|
||||||
- name: Install root dependencies
|
- name: Cache node modules
|
||||||
run: yarn install --frozen-lockfile
|
uses: actions/cache@v3
|
||||||
|
with:
|
||||||
|
path: node_modules
|
||||||
|
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
|
||||||
@@ -15,6 +15,7 @@ jobs:
|
|||||||
app: ${{ fromJSON(inputs.projects) }}
|
app: ${{ fromJSON(inputs.projects) }}
|
||||||
name: ${{ matrix.app }}
|
name: ${{ matrix.app }}
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-22.04
|
||||||
|
timeout-minutes: 20
|
||||||
steps:
|
steps:
|
||||||
- name: Check out code
|
- name: Check out code
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
@@ -36,66 +37,131 @@ jobs:
|
|||||||
username: ${{ github.actor }}
|
username: ${{ github.actor }}
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
# https://docs.github.com/en/actions/learn-github-actions/contexts
|
- name: Setup node
|
||||||
- name: Check node version
|
uses: actions/setup-node@v3
|
||||||
id: tags
|
with:
|
||||||
run: |
|
node-version-file: '.nvmrc'
|
||||||
nodeVersion=$(cat .nvmrc | head -n 1)
|
# https://stackoverflow.com/questions/61010294/how-to-cache-yarn-packages-in-github-actions
|
||||||
echo ::set-output name=nodeVersion::${nodeVersion}
|
cache: yarn
|
||||||
|
|
||||||
|
- name: Cache node modules
|
||||||
|
uses: actions/cache@v3
|
||||||
|
with:
|
||||||
|
path: node_modules
|
||||||
|
key: ${{ runner.os }}-cache-node-modules-${{ hashFiles('yarn.lock') }}
|
||||||
|
|
||||||
|
# https://docs.github.com/en/actions/learn-github-actions/contexts
|
||||||
|
- name: Define variables
|
||||||
|
run: |
|
||||||
|
envName=''
|
||||||
|
dockerfile="dist.Dockerfile"
|
||||||
if [[ "${{ github.event_name }}" = "push" ]]; then
|
if [[ "${{ github.event_name }}" = "push" ]]; then
|
||||||
envName="$(echo ${{ github.ref }} | rev | cut -d '/' -f 1 | rev)"
|
domain="vega.rocks"
|
||||||
bucketName="${{ github.event.repository.name }}-$envName"
|
if [[ "${{ github.ref }}" =~ .*release/.* ]]; then
|
||||||
echo ::set-output name=bucketName::${bucketName}
|
envName="$(echo ${{ github.ref }} | rev | cut -d '/' -f 1 | rev)"
|
||||||
echo ::set-output name=envName::${envName}
|
if [[ "${{ github.ref }}" =~ .*mainnet.* ]]; then
|
||||||
|
domain="vega.community"
|
||||||
|
if [[ "${{ matrix.app }}" = "trading" ]]; then
|
||||||
|
dockerfile="ipfs.Dockerfile"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
elif [[ "${{ github.ref }}" =~ .*develop$ ]]; then
|
||||||
|
envName="stagnet3"
|
||||||
|
fi
|
||||||
|
bucketName="${{ matrix.app }}.${envName}.${domain}"
|
||||||
|
echo BUCKET_NAME=${bucketName} >> $GITHUB_ENV
|
||||||
fi
|
fi
|
||||||
|
nodeVersion=$(cat .nvmrc | head -n 1)
|
||||||
|
echo ENV_NAME=${envName} >> $GITHUB_ENV
|
||||||
|
echo NODE_VERSION=${nodeVersion} >> $GITHUB_ENV
|
||||||
|
echo DOCKERFILE=docker/${dockerfile} >> $GITHUB_ENV
|
||||||
|
|
||||||
|
- name: Build local dist
|
||||||
|
if: ${{ env.DOCKERFILE != 'docker/ipfs.Dockerfile' }}
|
||||||
|
run: |
|
||||||
|
flags=""
|
||||||
|
if [[ ! -z "${{ env.ENV_NAME }}" ]]; then
|
||||||
|
if [[ "${{ env.ENV_NAME }}" != "ops-vega" ]]; then
|
||||||
|
flags="--env=${{ env.ENV_NAME }}"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "${{ matrix.app }}" = "trading" ]; then
|
||||||
|
yarn nx export trading $flags || (yarn install && yarn nx export trading $flags)
|
||||||
|
DIST_LOCATION=dist/apps/trading/exported
|
||||||
|
else
|
||||||
|
yarn nx build ${{ matrix.app }} $flags || (yarn install && yarn nx build ${{ matrix.app }} $flags)
|
||||||
|
DIST_LOCATION=dist/apps/${{ matrix.app }}
|
||||||
|
fi
|
||||||
|
mv $DIST_LOCATION dist-result
|
||||||
|
tree dist-result
|
||||||
|
|
||||||
- name: Build and export to local Docker
|
- name: Build and export to local Docker
|
||||||
id: docker_build
|
id: docker_build
|
||||||
|
if: ${{ github.event_name == 'pull_request' || ( env.DOCKERFILE == 'docker/ipfs.Dockerfile' && github.event_name == 'push' ) }}
|
||||||
uses: docker/build-push-action@v3
|
uses: docker/build-push-action@v3
|
||||||
with:
|
with:
|
||||||
|
context: .
|
||||||
|
file: ${{ env.DOCKERFILE }}
|
||||||
load: true
|
load: true
|
||||||
build-args: |
|
build-args: |
|
||||||
APP=${{ matrix.app }}
|
APP=${{ matrix.app }}
|
||||||
NODE_VERSION=${{ steps.tags.outputs.nodeVersion }}
|
NODE_VERSION=${{ env.NODE_VERSION }}
|
||||||
ENV_NAME=${{ steps.tags.outputs.envName || '' }}
|
ENV_NAME=${{ env.ENV_NAME }}
|
||||||
tags: |
|
tags: |
|
||||||
ghcr.io/vegaprotocol/frontend/${{ matrix.app }}:local
|
ghcr.io/vegaprotocol/frontend/${{ matrix.app }}:local
|
||||||
|
|
||||||
|
- name: Image digest
|
||||||
|
if: ${{ github.event_name == 'pull_request' || ( env.DOCKERFILE == 'docker/ipfs.Dockerfile' && github.event_name == 'push' ) }}
|
||||||
|
run: echo ${{ steps.docker_build.outputs.digest }}
|
||||||
|
|
||||||
- name: Sanity check docker image
|
- name: Sanity check docker image
|
||||||
|
if: ${{ github.event_name == 'pull_request' || ( env.DOCKERFILE == 'docker/ipfs.Dockerfile' && github.event_name == 'push' ) }}
|
||||||
run: |
|
run: |
|
||||||
echo "Check ipfs-hash"
|
echo "Check ipfs-hash"
|
||||||
docker run --rm ghcr.io/vegaprotocol/frontend/${{ matrix.app }}:local cat ipfs-hash
|
if [[ "${{ env.DOCKERFILE }}" = "docker/ipfs.Dockerfile" ]]; then
|
||||||
|
docker run --rm ghcr.io/vegaprotocol/frontend/${{ matrix.app }}:local cat /ipfs-hash
|
||||||
|
fi
|
||||||
|
|
||||||
echo "List html directory"
|
echo "List html directory"
|
||||||
docker run --rm ghcr.io/vegaprotocol/frontend/${{ matrix.app }}:local ls -lah
|
docker run --rm ghcr.io/vegaprotocol/frontend/${{ matrix.app }}:local sh -c 'apk add --update tree; tree .'
|
||||||
|
|
||||||
echo "Copy dist to local filesystem"
|
- name: Copy dist to local filesystem
|
||||||
|
if: ${{ env.DOCKERFILE == 'docker/ipfs.Dockerfile' && github.event_name == 'push' }}
|
||||||
|
run: |
|
||||||
docker create --name=dist ghcr.io/vegaprotocol/frontend/${{ matrix.app }}:local
|
docker create --name=dist ghcr.io/vegaprotocol/frontend/${{ matrix.app }}:local
|
||||||
docker cp dist:/usr/share/nginx/html dist
|
docker cp dist:/usr/share/nginx/html dist
|
||||||
|
|
||||||
echo "Check local dist"
|
echo "check local dist files"
|
||||||
ls -al dist
|
tree dist/html
|
||||||
|
mv dist/html dist-result
|
||||||
|
|
||||||
- name: Publish dist as docker image
|
- name: Publish dist as docker image
|
||||||
uses: docker/build-push-action@v3
|
uses: docker/build-push-action@v3
|
||||||
if: ${{ github.event_name == 'pull_request' }}
|
if: ${{ github.event_name == 'pull_request' }}
|
||||||
with:
|
with:
|
||||||
|
context: .
|
||||||
|
file: ${{ env.DOCKERFILE }}
|
||||||
push: true
|
push: true
|
||||||
build-args: |
|
build-args: |
|
||||||
APP=${{ matrix.app }}
|
APP=${{ matrix.app }}
|
||||||
NODE_VERSION=${{ steps.tags.outputs.nodeVersion }}
|
NODE_VERSION=${{ env.NODE_VERSION }}
|
||||||
|
ENV_NAME=${{ env.ENV_NAME }}
|
||||||
tags: |
|
tags: |
|
||||||
ghcr.io/vegaprotocol/frontend/${{ matrix.app }}:${{ github.event.pull_request.head.sha || github.sha }}
|
ghcr.io/vegaprotocol/frontend/${{ matrix.app }}:${{ github.event.pull_request.head.sha || github.sha }}
|
||||||
|
|
||||||
# - uses: shallwefootball/s3-upload-action@master
|
# bucket creation in github.com/vegaprotocol/terraform//frontend
|
||||||
# if: ${{ github.event_name == 'push' }}
|
- name: Publish dist to s3
|
||||||
# name: Upload dist S3
|
uses: jakejarvis/s3-sync-action@master
|
||||||
# with:
|
if: ${{ github.event_name == 'push' }}
|
||||||
# aws_key_id: ${{ secrets.AWS_KEY_ID }}
|
with:
|
||||||
# aws_secret_access_key: ${{ secrets.AWS_SECRET_ACCESS_KEY}}
|
args: --acl private --follow-symlinks --delete
|
||||||
# aws_bucket: ${{ steps.tags.outputs.bucketName }}
|
env:
|
||||||
# source_dir: 'dist'
|
AWS_S3_BUCKET: ${{ env.BUCKET_NAME }}
|
||||||
|
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
|
||||||
|
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
|
||||||
|
AWS_REGION: 'eu-west-1'
|
||||||
|
SOURCE_DIR: 'dist-result'
|
||||||
|
|
||||||
- name: Add preview label
|
- name: Add preview label
|
||||||
uses: actions-ecosystem/action-add-labels@v1
|
uses: actions-ecosystem/action-add-labels@v1
|
||||||
@@ -103,6 +169,3 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
labels: ${{ matrix.app }}-preview
|
labels: ${{ matrix.app }}-preview
|
||||||
number: ${{ github.event.number }}
|
number: ${{ github.event.number }}
|
||||||
|
|
||||||
- name: Image digest
|
|
||||||
run: echo ${{ steps.docker_build.outputs.digest }}
|
|
||||||
|
|||||||
@@ -46,3 +46,6 @@ cypress.env.json
|
|||||||
|
|
||||||
# Next.js
|
# Next.js
|
||||||
.next
|
.next
|
||||||
|
|
||||||
|
#cypress
|
||||||
|
/apps/trading-e2e/cypress/reports/html
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ export const Footer = () => {
|
|||||||
const [nodeSwitcherOpen, setNodeSwitcherOpen] = useState(false);
|
const [nodeSwitcherOpen, setNodeSwitcherOpen] = useState(false);
|
||||||
const { screenSize } = useScreenDimensions();
|
const { screenSize } = useScreenDimensions();
|
||||||
const showFullFeedbackLabel = useMemo(
|
const showFullFeedbackLabel = useMemo(
|
||||||
() => ['lg', 'xl', 'xxl', 'xxxl'].includes(screenSize),
|
() => ['md', 'lg', 'xl', 'xxl', 'xxxl'].includes(screenSize),
|
||||||
[screenSize]
|
[screenSize]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -38,14 +38,16 @@ const DialogsContainer = () => {
|
|||||||
export const Layout = () => {
|
export const Layout = () => {
|
||||||
const isHome = Boolean(useMatch(Routes.HOME));
|
const isHome = Boolean(useMatch(Routes.HOME));
|
||||||
const { ANNOUNCEMENTS_CONFIG_URL } = useEnvironment();
|
const { ANNOUNCEMENTS_CONFIG_URL } = useEnvironment();
|
||||||
|
const fixedWidthClasses = 'w-full max-w-[1500px] mx-auto';
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div
|
<div
|
||||||
className={classNames(
|
className={classNames(
|
||||||
'max-w-[1500px] min-h-[100vh]',
|
'min-h-screen',
|
||||||
'mx-auto my-0',
|
'mx-auto my-0',
|
||||||
'grid grid-rows-[auto_1fr_auto] grid-cols-1',
|
'grid grid-rows-[auto_1fr_auto] grid-cols-1',
|
||||||
'border-vega-light-200 dark:border-vega-dark-200 lg:border-l lg:border-r',
|
'border-vega-light-200 dark:border-vega-dark-200',
|
||||||
'antialiased text-black dark:text-white',
|
'antialiased text-black dark:text-white',
|
||||||
'overflow-hidden relative'
|
'overflow-hidden relative'
|
||||||
)}
|
)}
|
||||||
@@ -59,13 +61,13 @@ export const Layout = () => {
|
|||||||
)}
|
)}
|
||||||
<Header />
|
<Header />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div className={fixedWidthClasses}>
|
||||||
<main className="p-4">
|
<main className="p-4">
|
||||||
{!isHome && <BreadcrumbsContainer className="mb-4" />}
|
{!isHome && <BreadcrumbsContainer className="mb-4" />}
|
||||||
<Outlet />
|
<Outlet />
|
||||||
</main>
|
</main>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div className={fixedWidthClasses}>
|
||||||
<Footer />
|
<Footer />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -44,7 +44,8 @@ describe(
|
|||||||
function () {
|
function () {
|
||||||
before('connect wallets and set approval limit', function () {
|
before('connect wallets and set approval limit', function () {
|
||||||
cy.visit('/');
|
cy.visit('/');
|
||||||
vegaWalletSetSpecifiedApprovalAmount('1000');
|
ethereumWalletConnect();
|
||||||
|
cy.associateTokensToVegaWallet('1');
|
||||||
});
|
});
|
||||||
|
|
||||||
beforeEach('visit proposals tab', function () {
|
beforeEach('visit proposals tab', function () {
|
||||||
@@ -213,6 +214,7 @@ describe(
|
|||||||
|
|
||||||
// 3001-VOTE-042, 3001-VOTE-057, 3001-VOTE-058, 3001-VOTE-059, 3001-VOTE-060
|
// 3001-VOTE-042, 3001-VOTE-057, 3001-VOTE-058, 3001-VOTE-059, 3001-VOTE-060
|
||||||
it('Newly created proposal details - ability to increase associated tokens - by voting again after association', function () {
|
it('Newly created proposal details - ability to increase associated tokens - by voting again after association', function () {
|
||||||
|
vegaWalletSetSpecifiedApprovalAmount('1000');
|
||||||
createRawProposal();
|
createRawProposal();
|
||||||
cy.get<testFreeformProposal>('@rawProposal').then((rawProposal) => {
|
cy.get<testFreeformProposal>('@rawProposal').then((rawProposal) => {
|
||||||
getSubmittedProposalFromProposalList(rawProposal.rationale.title)
|
getSubmittedProposalFromProposalList(rawProposal.rationale.title)
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ import {
|
|||||||
createUpdateNetworkProposalTxBody,
|
createUpdateNetworkProposalTxBody,
|
||||||
createFreeFormProposalTxBody,
|
createFreeFormProposalTxBody,
|
||||||
} from '../../support/proposal.functions';
|
} from '../../support/proposal.functions';
|
||||||
import { ensureSpecifiedUnstakedTokensAreAssociated } from '../../support/staking.functions';
|
|
||||||
import { ethereumWalletConnect } from '../../support/wallet-eth.functions';
|
import { ethereumWalletConnect } from '../../support/wallet-eth.functions';
|
||||||
import { vegaWalletSetSpecifiedApprovalAmount } from '../../support/wallet-teardown.functions';
|
import { vegaWalletSetSpecifiedApprovalAmount } from '../../support/wallet-teardown.functions';
|
||||||
|
|
||||||
@@ -33,10 +32,6 @@ context(
|
|||||||
before('Connect wallets and set approval', function () {
|
before('Connect wallets and set approval', function () {
|
||||||
cy.visit('/');
|
cy.visit('/');
|
||||||
vegaWalletSetSpecifiedApprovalAmount('1000');
|
vegaWalletSetSpecifiedApprovalAmount('1000');
|
||||||
cy.connectVegaWallet();
|
|
||||||
ethereumWalletConnect();
|
|
||||||
ensureSpecifiedUnstakedTokensAreAssociated('1');
|
|
||||||
cy.clearLocalStorage();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
beforeEach('visit proposals', function () {
|
beforeEach('visit proposals', function () {
|
||||||
@@ -114,7 +109,7 @@ context(
|
|||||||
navigateTo(navigation.proposals);
|
navigateTo(navigation.proposals);
|
||||||
cy.reload();
|
cy.reload();
|
||||||
waitForSpinner();
|
waitForSpinner();
|
||||||
cy.get(openProposals).within(() => {
|
cy.get(openProposals, { timeout: 6000 }).within(() => {
|
||||||
cy.contains(proposalTitle)
|
cy.contains(proposalTitle)
|
||||||
.parentsUntil('[data-testid="proposals-list-item"]')
|
.parentsUntil('[data-testid="proposals-list-item"]')
|
||||||
.within(() => cy.get(viewProposalButton).click());
|
.within(() => cy.get(viewProposalButton).click());
|
||||||
|
|||||||
@@ -232,7 +232,7 @@ context(
|
|||||||
|
|
||||||
it('Unable to create a freeform proposal - when json parent section contains unexpected field', function () {
|
it('Unable to create a freeform proposal - when json parent section contains unexpected field', function () {
|
||||||
const errorMsg =
|
const errorMsg =
|
||||||
'Invalid params: the transaction does not use a valid Vega command: unknown field unexpected" in vega.commands.v1.ProposalSubmission';
|
'Invalid params: the transaction does not use a valid Vega command: unknown field "unexpected" in vega.commands.v1.ProposalSubmission';
|
||||||
|
|
||||||
// 3001-VOTE-038 3002-PROP-013 3002-PROP-014
|
// 3001-VOTE-038 3002-PROP-013 3002-PROP-014
|
||||||
goToMakeNewProposal(governanceProposalType.RAW);
|
goToMakeNewProposal(governanceProposalType.RAW);
|
||||||
@@ -313,7 +313,7 @@ context(
|
|||||||
|
|
||||||
it('Unable to vote on a proposal - when vega wallet disconnected - option to connect from within', function () {
|
it('Unable to vote on a proposal - when vega wallet disconnected - option to connect from within', function () {
|
||||||
createRawProposal();
|
createRawProposal();
|
||||||
cy.get('[data-testid="manage-vega-wallet"]').click();
|
cy.get('[data-testid="manage-vega-wallet"]:visible').click();
|
||||||
cy.get('[data-testid="disconnect"]').click();
|
cy.get('[data-testid="disconnect"]').click();
|
||||||
cy.get<testFreeformProposal>('@rawProposal').then((rawProposal) => {
|
cy.get<testFreeformProposal>('@rawProposal').then((rawProposal) => {
|
||||||
getSubmittedProposalFromProposalList(
|
getSubmittedProposalFromProposalList(
|
||||||
|
|||||||
@@ -218,7 +218,7 @@ context(
|
|||||||
|
|
||||||
it('Unable to submit new market proposal with missing/invalid fields', function () {
|
it('Unable to submit new market proposal with missing/invalid fields', function () {
|
||||||
const errorMsg =
|
const errorMsg =
|
||||||
'Invalid params: the transaction is not a valid Vega command: unknown field "filters" in vega.DataSourceDefinition';
|
'Invalid params: the transaction does not use a valid Vega command: unknown field "invalid" in vega.NewMarket';
|
||||||
|
|
||||||
goToMakeNewProposal(governanceProposalType.NEW_MARKET);
|
goToMakeNewProposal(governanceProposalType.NEW_MARKET);
|
||||||
cy.get(newProposalSubmitButton).should('be.visible').click();
|
cy.get(newProposalSubmitButton).should('be.visible').click();
|
||||||
@@ -436,7 +436,7 @@ context(
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it.only('Able to submit update asset proposal using max deadline', function () {
|
it('Able to submit update asset proposal using max deadline', function () {
|
||||||
goToMakeNewProposal(governanceProposalType.UPDATE_ASSET);
|
goToMakeNewProposal(governanceProposalType.UPDATE_ASSET);
|
||||||
enterUpdateAssetProposalDetails();
|
enterUpdateAssetProposalDetails();
|
||||||
cy.get(maxVoteDeadline).click();
|
cy.get(maxVoteDeadline).click();
|
||||||
|
|||||||
@@ -25,11 +25,11 @@ const vegaWalletUnstakedBalance =
|
|||||||
'[data-testid="vega-wallet-balance-unstaked"]';
|
'[data-testid="vega-wallet-balance-unstaked"]';
|
||||||
const txTimeout = Cypress.env('txTimeout');
|
const txTimeout = Cypress.env('txTimeout');
|
||||||
const vegaWalletPublicKeyShort = Cypress.env('vegaWalletPublicKeyShort');
|
const vegaWalletPublicKeyShort = Cypress.env('vegaWalletPublicKeyShort');
|
||||||
const ethWalletAssociateButton = '[data-testid="associate-btn"]';
|
const ethWalletAssociateButton = '[data-testid="associate-btn"]:visible';
|
||||||
const associateWalletRadioButton = '[data-testid="associate-radio-wallet"]';
|
const associateWalletRadioButton = '[data-testid="associate-radio-wallet"]';
|
||||||
const tokenAmountInputBox = '[data-testid="token-amount-input"]';
|
const tokenAmountInputBox = '[data-testid="token-amount-input"]';
|
||||||
const tokenSubmitButton = '[data-testid="token-input-submit-button"]';
|
const tokenSubmitButton = '[data-testid="token-input-submit-button"]';
|
||||||
const ethWalletDissociateButton = '[href="/token/disassociate"]';
|
const ethWalletDissociateButton = '[href="/token/disassociate"]:visible';
|
||||||
const vestingContractSection = '[data-testid="vega-in-vesting-contract"]';
|
const vestingContractSection = '[data-testid="vega-in-vesting-contract"]';
|
||||||
const vegaInWalletSection = '[data-testid="vega-in-wallet"]';
|
const vegaInWalletSection = '[data-testid="vega-in-wallet"]';
|
||||||
const connectedVegaKey = '[data-testid="connected-vega-key"]';
|
const connectedVegaKey = '[data-testid="connected-vega-key"]';
|
||||||
@@ -78,12 +78,12 @@ context(
|
|||||||
|
|
||||||
cy.getByTestId('currency-title', txTimeout).should(
|
cy.getByTestId('currency-title', txTimeout).should(
|
||||||
'have.length.above',
|
'have.length.above',
|
||||||
3
|
6
|
||||||
);
|
);
|
||||||
validateWalletCurrency('Associated', '0.00');
|
validateWalletCurrency('Associated', '0.00');
|
||||||
validateWalletCurrency('Pending association', '2.00');
|
validateWalletCurrency('Pending association', '2.00');
|
||||||
validateWalletCurrency('Total associated after pending', '2.00');
|
validateWalletCurrency('Total associated after pending', '2.00');
|
||||||
cy.getByTestId('currency-title', txTimeout).should('have.length', 3);
|
cy.getByTestId('currency-title', txTimeout).should('have.length', 6);
|
||||||
|
|
||||||
// 0005-ETXN-002
|
// 0005-ETXN-002
|
||||||
verifyEthWalletAssociatedBalance('2.0');
|
verifyEthWalletAssociatedBalance('2.0');
|
||||||
@@ -111,12 +111,12 @@ context(
|
|||||||
stakingPageDisassociateTokens('2');
|
stakingPageDisassociateTokens('2');
|
||||||
cy.getByTestId('currency-title', txTimeout).should(
|
cy.getByTestId('currency-title', txTimeout).should(
|
||||||
'have.length.above',
|
'have.length.above',
|
||||||
3
|
6
|
||||||
);
|
);
|
||||||
validateWalletCurrency('Associated', '2.00');
|
validateWalletCurrency('Associated', '2.00');
|
||||||
validateWalletCurrency('Pending association', '2.00');
|
validateWalletCurrency('Pending association', '2.00');
|
||||||
validateWalletCurrency('Total associated after pending', '0.00');
|
validateWalletCurrency('Total associated after pending', '0.00');
|
||||||
cy.getByTestId('currency-title', txTimeout).should('have.length', 3);
|
cy.getByTestId('currency-title', txTimeout).should('have.length', 6);
|
||||||
cy.getByTestId('eth-wallet-associated-balances', txTimeout).should(
|
cy.getByTestId('eth-wallet-associated-balances', txTimeout).should(
|
||||||
'not.exist'
|
'not.exist'
|
||||||
);
|
);
|
||||||
@@ -192,12 +192,12 @@ context(
|
|||||||
|
|
||||||
cy.getByTestId('currency-title', txTimeout).should(
|
cy.getByTestId('currency-title', txTimeout).should(
|
||||||
'have.length.above',
|
'have.length.above',
|
||||||
3
|
6
|
||||||
);
|
);
|
||||||
validateWalletCurrency('Associated', '0.00');
|
validateWalletCurrency('Associated', '0.00');
|
||||||
validateWalletCurrency('Pending association', '2.00');
|
validateWalletCurrency('Pending association', '2.00');
|
||||||
validateWalletCurrency('Total associated after pending', '2.00');
|
validateWalletCurrency('Total associated after pending', '2.00');
|
||||||
cy.getByTestId('currency-title', txTimeout).should('have.length', 3);
|
cy.getByTestId('currency-title', txTimeout).should('have.length', 6);
|
||||||
verifyEthWalletAssociatedBalance('2.0');
|
verifyEthWalletAssociatedBalance('2.0');
|
||||||
verifyEthWalletTotalAssociatedBalance('2.0');
|
verifyEthWalletTotalAssociatedBalance('2.0');
|
||||||
cy.get(vegaWallet).within(() => {
|
cy.get(vegaWallet).within(() => {
|
||||||
@@ -210,12 +210,12 @@ context(
|
|||||||
});
|
});
|
||||||
cy.getByTestId('currency-title', txTimeout).should(
|
cy.getByTestId('currency-title', txTimeout).should(
|
||||||
'have.length.above',
|
'have.length.above',
|
||||||
3
|
6
|
||||||
);
|
);
|
||||||
validateWalletCurrency('Associated', '2.00');
|
validateWalletCurrency('Associated', '2.00');
|
||||||
validateWalletCurrency('Pending association', '1.00');
|
validateWalletCurrency('Pending association', '1.00');
|
||||||
validateWalletCurrency('Total associated after pending', '1.00');
|
validateWalletCurrency('Total associated after pending', '1.00');
|
||||||
cy.getByTestId('currency-title', txTimeout).should('have.length', 3);
|
cy.getByTestId('currency-title', txTimeout).should('have.length', 6);
|
||||||
verifyEthWalletAssociatedBalance('1.0');
|
verifyEthWalletAssociatedBalance('1.0');
|
||||||
verifyEthWalletTotalAssociatedBalance('1.0');
|
verifyEthWalletTotalAssociatedBalance('1.0');
|
||||||
});
|
});
|
||||||
@@ -266,7 +266,7 @@ context(
|
|||||||
// 1004-ASSO-008
|
// 1004-ASSO-008
|
||||||
// 1004-ASSO-010
|
// 1004-ASSO-010
|
||||||
// No warning visible as described in AC, but the button is disabled
|
// No warning visible as described in AC, but the button is disabled
|
||||||
cy.get(ethWalletAssociateButton).first().click();
|
cy.get(ethWalletAssociateButton).click();
|
||||||
cy.get(associateWalletRadioButton, { timeout: 30000 }).click();
|
cy.get(associateWalletRadioButton, { timeout: 30000 }).click();
|
||||||
cy.get(tokenSubmitButton, txTimeout).should('be.disabled'); // button disabled with no input
|
cy.get(tokenSubmitButton, txTimeout).should('be.disabled'); // button disabled with no input
|
||||||
cy.get(tokenAmountInputBox, { timeout: 10000 }).type('6500000');
|
cy.get(tokenAmountInputBox, { timeout: 10000 }).type('6500000');
|
||||||
@@ -278,12 +278,12 @@ context(
|
|||||||
vegaWalletAssociate('2');
|
vegaWalletAssociate('2');
|
||||||
cy.getByTestId('currency-title', txTimeout).should(
|
cy.getByTestId('currency-title', txTimeout).should(
|
||||||
'have.length.above',
|
'have.length.above',
|
||||||
3
|
6
|
||||||
);
|
);
|
||||||
validateWalletCurrency('Associated', '0.00');
|
validateWalletCurrency('Associated', '0.00');
|
||||||
validateWalletCurrency('Pending association', '2.00');
|
validateWalletCurrency('Pending association', '2.00');
|
||||||
validateWalletCurrency('Total associated after pending', '2.00');
|
validateWalletCurrency('Total associated after pending', '2.00');
|
||||||
cy.getByTestId('currency-title', txTimeout).should('have.length', 3);
|
cy.getByTestId('currency-title', txTimeout).should('have.length', 6);
|
||||||
validateWalletCurrency('Associated', '2.00');
|
validateWalletCurrency('Associated', '2.00');
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -294,24 +294,24 @@ context(
|
|||||||
});
|
});
|
||||||
cy.getByTestId('currency-title', txTimeout).should(
|
cy.getByTestId('currency-title', txTimeout).should(
|
||||||
'have.length.above',
|
'have.length.above',
|
||||||
3
|
6
|
||||||
);
|
);
|
||||||
validateWalletCurrency('Associated', '2.00');
|
validateWalletCurrency('Associated', '2.00');
|
||||||
validateWalletCurrency('Pending association', '2.00');
|
validateWalletCurrency('Pending association', '2.00');
|
||||||
validateWalletCurrency('Total associated after pending', '0.00');
|
validateWalletCurrency('Total associated after pending', '0.00');
|
||||||
cy.getByTestId('currency-title', txTimeout).should('have.length', 3);
|
cy.getByTestId('currency-title', txTimeout).should('have.length', 6);
|
||||||
validateWalletCurrency('Associated', '0.00');
|
validateWalletCurrency('Associated', '0.00');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Able to associate tokens to different public key of connected vega wallet', function () {
|
it('Able to associate tokens to different public key of connected vega wallet', function () {
|
||||||
cy.get(ethWalletAssociateButton).first().click();
|
cy.get(ethWalletAssociateButton).click();
|
||||||
cy.get(associateWalletRadioButton).click();
|
cy.get(associateWalletRadioButton).click();
|
||||||
cy.get(connectedVegaKey).should(
|
cy.get(connectedVegaKey).should(
|
||||||
'have.text',
|
'have.text',
|
||||||
Cypress.env('vegaWalletPublicKey')
|
Cypress.env('vegaWalletPublicKey')
|
||||||
);
|
);
|
||||||
|
|
||||||
cy.get('[data-testid="manage-vega-wallet"]').click();
|
cy.get('[data-testid="manage-vega-wallet"]:visible').click();
|
||||||
cy.get('[data-testid="select-keypair-button"]').eq(0).click();
|
cy.get('[data-testid="select-keypair-button"]').eq(0).click();
|
||||||
cy.get(connectedVegaKey).should(
|
cy.get(connectedVegaKey).should(
|
||||||
'have.text',
|
'have.text',
|
||||||
|
|||||||
@@ -166,6 +166,7 @@ export function goToMakeNewProposal(proposalType: string) {
|
|||||||
navigateTo(navigation.proposals);
|
navigateTo(navigation.proposals);
|
||||||
cy.get(newProposalButton).should('be.visible').click();
|
cy.get(newProposalButton).should('be.visible').click();
|
||||||
cy.url().should('include', '/proposals/propose');
|
cy.url().should('include', '/proposals/propose');
|
||||||
|
cy.get(navigation.pageSpinner, { timeout: 20000 }).should('not.exist');
|
||||||
cy.get('li').should('contain.text', proposalType).and('be.visible');
|
cy.get('li').should('contain.text', proposalType).and('be.visible');
|
||||||
cy.get('li').contains(proposalType).click();
|
cy.get('li').contains(proposalType).click();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import {
|
|||||||
} from '@vegaprotocol/smart-contracts';
|
} from '@vegaprotocol/smart-contracts';
|
||||||
import { ethers, Wallet } from 'ethers';
|
import { ethers, Wallet } from 'ethers';
|
||||||
|
|
||||||
|
const associatedAmountInWallet = '[data-testid="associated-amount"]:visible';
|
||||||
const vegaWalletContainer = 'aside [data-testid="vega-wallet"]';
|
const vegaWalletContainer = 'aside [data-testid="vega-wallet"]';
|
||||||
const vegaWalletMnemonic = Cypress.env('vegaWalletMnemonic');
|
const vegaWalletMnemonic = Cypress.env('vegaWalletMnemonic');
|
||||||
const vegaWalletPubKey = Cypress.env('vegaWalletPublicKey');
|
const vegaWalletPubKey = Cypress.env('vegaWalletPublicKey');
|
||||||
@@ -59,7 +60,7 @@ export async function faucetAsset(assetEthAddress: string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function vegaWalletTeardown() {
|
export async function vegaWalletTeardown() {
|
||||||
cy.get('[data-testid="associated-amount"]')
|
cy.get(associatedAmountInWallet)
|
||||||
.should('be.visible')
|
.should('be.visible')
|
||||||
.invoke('text')
|
.invoke('text')
|
||||||
.then((associatedAmount) => {
|
.then((associatedAmount) => {
|
||||||
@@ -68,12 +69,12 @@ export async function vegaWalletTeardown() {
|
|||||||
$body.find('[data-testid="eth-wallet-associated-balances"]').length ||
|
$body.find('[data-testid="eth-wallet-associated-balances"]').length ||
|
||||||
associatedAmount != '0.00'
|
associatedAmount != '0.00'
|
||||||
) {
|
) {
|
||||||
vegaWalletTeardownVesting(vestingContract);
|
|
||||||
vegaWalletTeardownStaking(stakingBridgeContract);
|
vegaWalletTeardownStaking(stakingBridgeContract);
|
||||||
|
vegaWalletTeardownVesting(vestingContract);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
cy.get(vegaWalletContainer).within(() => {
|
cy.get(vegaWalletContainer).within(() => {
|
||||||
cy.getByTestId('associated-amount', {
|
cy.get(associatedAmountInWallet, {
|
||||||
timeout: transactionTimeout,
|
timeout: transactionTimeout,
|
||||||
}).contains('0.00', {
|
}).contains('0.00', {
|
||||||
timeout: transactionTimeout,
|
timeout: transactionTimeout,
|
||||||
@@ -90,7 +91,7 @@ export async function vegaWalletSetSpecifiedApprovalAmount(
|
|||||||
await promiseWithTimeout(
|
await promiseWithTimeout(
|
||||||
token.approve(
|
token.approve(
|
||||||
ethStakingBridgeContractAddress,
|
ethStakingBridgeContractAddress,
|
||||||
resetAmount.concat('000000000000000000')
|
resetAmount + '0'.repeat(18)
|
||||||
),
|
),
|
||||||
10 * 60 * 1000,
|
10 * 60 * 1000,
|
||||||
'set approval amount'
|
'set approval amount'
|
||||||
@@ -104,12 +105,23 @@ async function vegaWalletTeardownStaking(stakingBridgeContract: StakingBridge) {
|
|||||||
{ timeout: transactionTimeout, log: false }
|
{ timeout: transactionTimeout, log: false }
|
||||||
).then((stakeBalance) => {
|
).then((stakeBalance) => {
|
||||||
if (Number(stakeBalance) != 0) {
|
if (Number(stakeBalance) != 0) {
|
||||||
cy.wrap(
|
cy.get('[data-testid="vega-wallet-balance-unstaked"]:visible').within(
|
||||||
stakingBridgeContract.remove_stake(
|
() => {
|
||||||
String(stakeBalance),
|
cy.get(associatedAmountInWallet)
|
||||||
vegaWalletPubKey
|
.invoke('text')
|
||||||
),
|
.then(($walletAmount) => {
|
||||||
{ timeout: transactionTimeout, log: false }
|
cy.wrap(
|
||||||
|
stakingBridgeContract.remove_stake(
|
||||||
|
String(stakeBalance),
|
||||||
|
vegaWalletPubKey
|
||||||
|
),
|
||||||
|
{ timeout: transactionTimeout, log: false }
|
||||||
|
);
|
||||||
|
cy.get(associatedAmountInWallet, {
|
||||||
|
timeout: transactionTimeout,
|
||||||
|
}).should('not.have.text', $walletAmount);
|
||||||
|
});
|
||||||
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -124,7 +136,6 @@ async function vegaWalletTeardownVesting(vestingContract: TokenVesting) {
|
|||||||
if (Number(vestingAmount) != 0) {
|
if (Number(vestingAmount) != 0) {
|
||||||
// Wait needed to allow time for ganache to process tx for stakingBridgeContract.remove_stake
|
// Wait needed to allow time for ganache to process tx for stakingBridgeContract.remove_stake
|
||||||
// eslint-disable-next-line cypress/no-unnecessary-waiting
|
// eslint-disable-next-line cypress/no-unnecessary-waiting
|
||||||
cy.wait(1000);
|
|
||||||
cy.wrap(
|
cy.wrap(
|
||||||
vestingContract.remove_stake(String(vestingAmount), vegaWalletPubKey),
|
vestingContract.remove_stake(String(vestingAmount), vegaWalletPubKey),
|
||||||
{ timeout: transactionTimeout, log: false }
|
{ timeout: transactionTimeout, log: false }
|
||||||
|
|||||||
@@ -1,15 +1,19 @@
|
|||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { useVegaWallet } from '@vegaprotocol/wallet';
|
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||||
import type { ReactNode } from 'react';
|
import type { ReactNode } from 'react';
|
||||||
|
import { AnnouncementBanner } from '@vegaprotocol/announcements';
|
||||||
|
import { Nav } from '../nav';
|
||||||
|
import { Networks, useEnvironment } from '@vegaprotocol/environment';
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
interface AppLayoutProps {
|
interface AppLayoutProps {
|
||||||
children: ReactNode;
|
children: ReactNode;
|
||||||
}
|
}
|
||||||
export const AppLayout = ({ children }: AppLayoutProps) => {
|
export const AppLayout = ({ children }: AppLayoutProps) => {
|
||||||
|
const { VEGA_ENV, ANNOUNCEMENTS_CONFIG_URL } = useEnvironment();
|
||||||
const { isReadOnly } = useVegaWallet();
|
const { isReadOnly } = useVegaWallet();
|
||||||
const AppLayoutClasses = classNames(
|
const AppLayoutClasses = classNames(
|
||||||
'app w-full max-w-[1500px] mx-auto grid min-h-full',
|
'app w-full max-w-[1500px] mx-auto grid',
|
||||||
'border-neutral-700 lg:border-l lg:border-r',
|
|
||||||
'lg:text-body-large',
|
'lg:text-body-large',
|
||||||
{
|
{
|
||||||
'grid-rows-[repeat(2,min-content)_1fr_min-content]': !isReadOnly,
|
'grid-rows-[repeat(2,min-content)_1fr_min-content]': !isReadOnly,
|
||||||
@@ -17,5 +21,18 @@ export const AppLayout = ({ children }: AppLayoutProps) => {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
return <div className={AppLayoutClasses}>{children}</div>;
|
return (
|
||||||
|
<div className="min-h-full">
|
||||||
|
<div className="lg:text-body-large">
|
||||||
|
{ANNOUNCEMENTS_CONFIG_URL && (
|
||||||
|
<AnnouncementBanner
|
||||||
|
app="governance"
|
||||||
|
configUrl={ANNOUNCEMENTS_CONFIG_URL}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
<Nav theme={VEGA_ENV === Networks.TESTNET ? 'yellow' : 'dark'} />
|
||||||
|
</div>
|
||||||
|
<div className={AppLayoutClasses}>{children}</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,28 +1,16 @@
|
|||||||
import { Networks, useEnvironment } from '@vegaprotocol/environment';
|
|
||||||
import { ViewingAsBanner } from '@vegaprotocol/ui-toolkit';
|
import { ViewingAsBanner } from '@vegaprotocol/ui-toolkit';
|
||||||
import { AnnouncementBanner } from '@vegaprotocol/announcements';
|
|
||||||
import { useVegaWallet } from '@vegaprotocol/wallet';
|
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import { Nav } from '../nav';
|
|
||||||
|
|
||||||
export interface TemplateSidebarProps {
|
export interface TemplateSidebarProps {
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
sidebar: React.ReactNode[];
|
sidebar: React.ReactNode[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export function TemplateSidebar({ children, sidebar }: TemplateSidebarProps) {
|
export function TemplateSidebar({ children, sidebar }: TemplateSidebarProps) {
|
||||||
const { VEGA_ENV, ANNOUNCEMENTS_CONFIG_URL } = useEnvironment();
|
|
||||||
const { isReadOnly, pubKey, disconnect } = useVegaWallet();
|
const { isReadOnly, pubKey, disconnect } = useVegaWallet();
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{ANNOUNCEMENTS_CONFIG_URL && (
|
|
||||||
<AnnouncementBanner
|
|
||||||
app="governance"
|
|
||||||
configUrl={ANNOUNCEMENTS_CONFIG_URL}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
<Nav theme={VEGA_ENV === Networks.TESTNET ? 'yellow' : 'dark'} />
|
|
||||||
{isReadOnly ? (
|
{isReadOnly ? (
|
||||||
<ViewingAsBanner pubKey={pubKey} disconnect={disconnect} />
|
<ViewingAsBanner pubKey={pubKey} disconnect={disconnect} />
|
||||||
) : null}
|
) : null}
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
import type { ObservableQuery } from '@apollo/client';
|
|
||||||
import { useEffect } from 'react';
|
import { useEffect } from 'react';
|
||||||
|
|
||||||
export const useRefreshAfterEpoch = (
|
export const useRefreshAfterEpoch = (
|
||||||
epochExpiry: string | undefined,
|
epochExpiry: string | undefined,
|
||||||
refetch: ObservableQuery['refetch']
|
refetch: () => void
|
||||||
) => {
|
) => {
|
||||||
return useEffect(() => {
|
return useEffect(() => {
|
||||||
const epochInterval = setInterval(() => {
|
const epochInterval = setInterval(() => {
|
||||||
|
|||||||
@@ -443,8 +443,8 @@
|
|||||||
"rewardType": "Reward type",
|
"rewardType": "Reward type",
|
||||||
"rewardsAndFeesReceived": "Rewards and fees received",
|
"rewardsAndFeesReceived": "Rewards and fees received",
|
||||||
"ThisDoesNotIncludeFeesReceivedForMakersOrLiquidityProviders": "This does not include fees received for makers or liquidity providers",
|
"ThisDoesNotIncludeFeesReceivedForMakersOrLiquidityProviders": "This does not include fees received for makers or liquidity providers",
|
||||||
"totalDistributed": "TOTAL DISTRIBUTED",
|
"totalDistributed": "Total distributed",
|
||||||
"earnedByMe": "EARNED BY ME",
|
"earnedByMe": "Earned by me",
|
||||||
"noRewardsHaveBeenDistributedYet": "NO REWARDS HAVE BEEN DISTRIBUTED YET",
|
"noRewardsHaveBeenDistributedYet": "NO REWARDS HAVE BEEN DISTRIBUTED YET",
|
||||||
"rewardsColAssetHeader": "ASSET",
|
"rewardsColAssetHeader": "ASSET",
|
||||||
"rewardsColStakingHeader": "STAKING",
|
"rewardsColStakingHeader": "STAKING",
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
export const calculateEpochOffset = ({
|
||||||
|
epochId,
|
||||||
|
page,
|
||||||
|
size,
|
||||||
|
}: {
|
||||||
|
epochId: number;
|
||||||
|
page: number;
|
||||||
|
size: number;
|
||||||
|
}) => {
|
||||||
|
// offset the epoch by the current page number times the page size while making sure it doesn't go below the minimum epoch value
|
||||||
|
return {
|
||||||
|
fromEpoch: Math.max(0, epochId - size * page) + 1,
|
||||||
|
toEpoch: epochId - size * page + size,
|
||||||
|
};
|
||||||
|
};
|
||||||
+1
-1
@@ -3,7 +3,7 @@ import { AppStateProvider } from '../../../contexts/app-state/app-state-provider
|
|||||||
import { EpochIndividualRewardsTable } from './epoch-individual-rewards-table';
|
import { EpochIndividualRewardsTable } from './epoch-individual-rewards-table';
|
||||||
|
|
||||||
const mockData = {
|
const mockData = {
|
||||||
epoch: '4441',
|
epoch: 4441,
|
||||||
rewards: [
|
rewards: [
|
||||||
{
|
{
|
||||||
asset: 'tDAI',
|
asset: 'tDAI',
|
||||||
|
|||||||
+68
-21
@@ -1,32 +1,43 @@
|
|||||||
import { useMemo } from 'react';
|
import { useMemo, useEffect, useState, useCallback } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { AsyncRenderer } from '@vegaprotocol/ui-toolkit';
|
import { AsyncRenderer, Pagination } from '@vegaprotocol/ui-toolkit';
|
||||||
import { removePaginationWrapper } from '@vegaprotocol/utils';
|
import { removePaginationWrapper } from '@vegaprotocol/utils';
|
||||||
|
import type { EpochFieldsFragment } from '../home/__generated__/Rewards';
|
||||||
import { useRewardsQuery } from '../home/__generated__/Rewards';
|
import { useRewardsQuery } from '../home/__generated__/Rewards';
|
||||||
import { ENV } from '../../../config';
|
import { ENV } from '../../../config';
|
||||||
import { useVegaWallet } from '@vegaprotocol/wallet';
|
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||||
import { EpochIndividualRewardsTable } from './epoch-individual-rewards-table';
|
import { EpochIndividualRewardsTable } from './epoch-individual-rewards-table';
|
||||||
import { generateEpochIndividualRewardsList } from './generate-epoch-individual-rewards-list';
|
import { generateEpochIndividualRewardsList } from './generate-epoch-individual-rewards-list';
|
||||||
|
import { calculateEpochOffset } from '../../../lib/epoch-pagination';
|
||||||
|
|
||||||
export const EpochIndividualRewards = () => {
|
const EPOCHS_PAGE_SIZE = 10;
|
||||||
|
|
||||||
|
type EpochTotalRewardsProps = {
|
||||||
|
currentEpoch: EpochFieldsFragment;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const EpochIndividualRewards = ({
|
||||||
|
currentEpoch,
|
||||||
|
}: EpochTotalRewardsProps) => {
|
||||||
|
// we start from the previous epoch when displaying rewards data, because the current one has no calculated data while ongoing
|
||||||
|
const epochId = Number(currentEpoch.id) - 1;
|
||||||
|
const totalPages = Math.ceil(epochId / EPOCHS_PAGE_SIZE);
|
||||||
|
const [page, setPage] = useState(1);
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { pubKey } = useVegaWallet();
|
const { pubKey } = useVegaWallet();
|
||||||
const { delegationsPagination } = ENV;
|
const { delegationsPagination } = ENV;
|
||||||
|
|
||||||
const { data, loading, error } = useRewardsQuery({
|
const { data, loading, error, refetch } = useRewardsQuery({
|
||||||
|
notifyOnNetworkStatusChange: true,
|
||||||
variables: {
|
variables: {
|
||||||
partyId: pubKey || '',
|
partyId: pubKey || '',
|
||||||
|
fromEpoch: epochId - EPOCHS_PAGE_SIZE,
|
||||||
|
toEpoch: epochId,
|
||||||
delegationsPagination: delegationsPagination
|
delegationsPagination: delegationsPagination
|
||||||
? {
|
? {
|
||||||
first: Number(delegationsPagination),
|
first: Number(delegationsPagination),
|
||||||
}
|
}
|
||||||
: undefined,
|
: undefined,
|
||||||
// we can use the same value for rewardsPagination as delegationsPagination
|
|
||||||
rewardsPagination: delegationsPagination
|
|
||||||
? {
|
|
||||||
first: Number(delegationsPagination),
|
|
||||||
}
|
|
||||||
: undefined,
|
|
||||||
},
|
},
|
||||||
skip: !pubKey,
|
skip: !pubKey,
|
||||||
});
|
});
|
||||||
@@ -39,8 +50,37 @@ export const EpochIndividualRewards = () => {
|
|||||||
|
|
||||||
const epochIndividualRewardSummaries = useMemo(() => {
|
const epochIndividualRewardSummaries = useMemo(() => {
|
||||||
if (!data?.party) return [];
|
if (!data?.party) return [];
|
||||||
return generateEpochIndividualRewardsList(rewards);
|
return generateEpochIndividualRewardsList({
|
||||||
}, [data?.party, rewards]);
|
rewards,
|
||||||
|
epochId,
|
||||||
|
page,
|
||||||
|
size: EPOCHS_PAGE_SIZE,
|
||||||
|
});
|
||||||
|
}, [data?.party, epochId, page, rewards]);
|
||||||
|
|
||||||
|
const refetchData = useCallback(
|
||||||
|
async (toPage?: number) => {
|
||||||
|
const targetPage = toPage ?? page;
|
||||||
|
await refetch({
|
||||||
|
partyId: pubKey || '',
|
||||||
|
...calculateEpochOffset({ epochId, page, size: EPOCHS_PAGE_SIZE }),
|
||||||
|
delegationsPagination: delegationsPagination
|
||||||
|
? {
|
||||||
|
first: Number(delegationsPagination),
|
||||||
|
}
|
||||||
|
: undefined,
|
||||||
|
});
|
||||||
|
setPage(targetPage);
|
||||||
|
},
|
||||||
|
[epochId, page, refetch, delegationsPagination, pubKey]
|
||||||
|
);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
// when the epoch changes, we want to refetch the data to update the current page
|
||||||
|
if (data) {
|
||||||
|
refetchData();
|
||||||
|
}
|
||||||
|
}, [epochId, data, refetchData]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AsyncRenderer
|
<AsyncRenderer
|
||||||
@@ -53,17 +93,24 @@ export const EpochIndividualRewards = () => {
|
|||||||
{t('Connected Vega key')}:{' '}
|
{t('Connected Vega key')}:{' '}
|
||||||
<span className="text-white">{pubKey}</span>
|
<span className="text-white">{pubKey}</span>
|
||||||
</p>
|
</p>
|
||||||
{epochIndividualRewardSummaries.length ? (
|
{epochIndividualRewardSummaries.map(
|
||||||
epochIndividualRewardSummaries.map(
|
(epochIndividualRewardSummary) => (
|
||||||
(epochIndividualRewardSummary) => (
|
<EpochIndividualRewardsTable
|
||||||
<EpochIndividualRewardsTable
|
data={epochIndividualRewardSummary}
|
||||||
data={epochIndividualRewardSummary}
|
/>
|
||||||
/>
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
) : (
|
|
||||||
<p>{t('noRewards')}</p>
|
|
||||||
)}
|
)}
|
||||||
|
<Pagination
|
||||||
|
isLoading={loading}
|
||||||
|
hasPrevPage={page > 1}
|
||||||
|
hasNextPage={page < totalPages}
|
||||||
|
onBack={() => refetchData(page - 1)}
|
||||||
|
onNext={() => refetchData(page + 1)}
|
||||||
|
onFirst={() => refetchData(1)}
|
||||||
|
onLast={() => refetchData(totalPages)}
|
||||||
|
>
|
||||||
|
{t('Page')} {page}
|
||||||
|
</Pagination>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
|||||||
+204
-15
@@ -43,6 +43,16 @@ describe('generateEpochIndividualRewardsList', () => {
|
|||||||
epoch: { id: '1' },
|
epoch: { id: '1' },
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const reward5: RewardFieldsFragment = {
|
||||||
|
rewardType: AccountType.ACCOUNT_TYPE_FEES_LIQUIDITY,
|
||||||
|
amount: '150',
|
||||||
|
percentageOfTotal: '0.15',
|
||||||
|
receivedAt: new Date(),
|
||||||
|
asset: { id: 'usd', symbol: 'USD', name: 'USD' },
|
||||||
|
party: { id: 'blah' },
|
||||||
|
epoch: { id: '3' },
|
||||||
|
};
|
||||||
|
|
||||||
const rewardWrongType: RewardFieldsFragment = {
|
const rewardWrongType: RewardFieldsFragment = {
|
||||||
rewardType: AccountType.ACCOUNT_TYPE_INSURANCE,
|
rewardType: AccountType.ACCOUNT_TYPE_INSURANCE,
|
||||||
amount: '50',
|
amount: '50',
|
||||||
@@ -54,20 +64,38 @@ describe('generateEpochIndividualRewardsList', () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
it('should return an empty array if no rewards are provided', () => {
|
it('should return an empty array if no rewards are provided', () => {
|
||||||
expect(generateEpochIndividualRewardsList([])).toEqual([]);
|
expect(
|
||||||
|
generateEpochIndividualRewardsList({ rewards: [], epochId: 1 })
|
||||||
|
).toEqual([
|
||||||
|
{
|
||||||
|
epoch: 1,
|
||||||
|
rewards: [],
|
||||||
|
},
|
||||||
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should filter out any rewards of the wrong type', () => {
|
it('should filter out any rewards of the wrong type', () => {
|
||||||
const result = generateEpochIndividualRewardsList([rewardWrongType]);
|
const result = generateEpochIndividualRewardsList({
|
||||||
|
rewards: [rewardWrongType],
|
||||||
|
epochId: 1,
|
||||||
|
});
|
||||||
|
|
||||||
expect(result).toEqual([]);
|
expect(result).toEqual([
|
||||||
|
{
|
||||||
|
epoch: 1,
|
||||||
|
rewards: [],
|
||||||
|
},
|
||||||
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return reward in the correct format', () => {
|
it('should return reward in the correct format', () => {
|
||||||
const result = generateEpochIndividualRewardsList([reward1]);
|
const result = generateEpochIndividualRewardsList({
|
||||||
|
rewards: [reward1],
|
||||||
|
epochId: 1,
|
||||||
|
});
|
||||||
|
|
||||||
expect(result[0]).toEqual({
|
expect(result[0]).toEqual({
|
||||||
epoch: '1',
|
epoch: 1,
|
||||||
rewards: [
|
rewards: [
|
||||||
{
|
{
|
||||||
asset: 'USD',
|
asset: 'USD',
|
||||||
@@ -105,21 +133,24 @@ describe('generateEpochIndividualRewardsList', () => {
|
|||||||
|
|
||||||
it('should return an array sorted by epoch descending', () => {
|
it('should return an array sorted by epoch descending', () => {
|
||||||
const rewards = [reward1, reward2, reward3, reward4];
|
const rewards = [reward1, reward2, reward3, reward4];
|
||||||
const result1 = generateEpochIndividualRewardsList(rewards);
|
const result1 = generateEpochIndividualRewardsList({ rewards, epochId: 2 });
|
||||||
|
|
||||||
expect(result1[0].epoch).toEqual('2');
|
expect(result1[0].epoch).toEqual(2);
|
||||||
expect(result1[1].epoch).toEqual('1');
|
expect(result1[1].epoch).toEqual(1);
|
||||||
|
|
||||||
const reorderedRewards = [reward4, reward3, reward2, reward1];
|
const reorderedRewards = [reward4, reward3, reward2, reward1];
|
||||||
const result2 = generateEpochIndividualRewardsList(reorderedRewards);
|
const result2 = generateEpochIndividualRewardsList({
|
||||||
|
rewards: reorderedRewards,
|
||||||
|
epochId: 2,
|
||||||
|
});
|
||||||
|
|
||||||
expect(result2[0].epoch).toEqual('2');
|
expect(result2[0].epoch).toEqual(2);
|
||||||
expect(result2[1].epoch).toEqual('1');
|
expect(result2[1].epoch).toEqual(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('correctly calculates the total value of rewards for an asset', () => {
|
it('correctly calculates the total value of rewards for an asset', () => {
|
||||||
const rewards = [reward1, reward4];
|
const rewards = [reward1, reward4];
|
||||||
const result = generateEpochIndividualRewardsList(rewards);
|
const result = generateEpochIndividualRewardsList({ rewards, epochId: 1 });
|
||||||
|
|
||||||
expect(result[0].rewards[0].totalAmount).toEqual('200');
|
expect(result[0].rewards[0].totalAmount).toEqual('200');
|
||||||
});
|
});
|
||||||
@@ -127,11 +158,11 @@ describe('generateEpochIndividualRewardsList', () => {
|
|||||||
it('returns data in the expected shape', () => {
|
it('returns data in the expected shape', () => {
|
||||||
// Just sanity checking the whole structure here
|
// Just sanity checking the whole structure here
|
||||||
const rewards = [reward1, reward2, reward3, reward4];
|
const rewards = [reward1, reward2, reward3, reward4];
|
||||||
const result = generateEpochIndividualRewardsList(rewards);
|
const result = generateEpochIndividualRewardsList({ rewards, epochId: 2 });
|
||||||
|
|
||||||
expect(result).toEqual([
|
expect(result).toEqual([
|
||||||
{
|
{
|
||||||
epoch: '2',
|
epoch: 2,
|
||||||
rewards: [
|
rewards: [
|
||||||
{
|
{
|
||||||
asset: 'GBP',
|
asset: 'GBP',
|
||||||
@@ -196,7 +227,165 @@ describe('generateEpochIndividualRewardsList', () => {
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
epoch: '1',
|
epoch: 1,
|
||||||
|
rewards: [
|
||||||
|
{
|
||||||
|
asset: 'USD',
|
||||||
|
totalAmount: '200',
|
||||||
|
rewardTypes: {
|
||||||
|
[AccountType.ACCOUNT_TYPE_FEES_INFRASTRUCTURE]: {
|
||||||
|
amount: '0',
|
||||||
|
percentageOfTotal: '0',
|
||||||
|
},
|
||||||
|
[AccountType.ACCOUNT_TYPE_FEES_LIQUIDITY]: {
|
||||||
|
amount: '100',
|
||||||
|
percentageOfTotal: '0.1',
|
||||||
|
},
|
||||||
|
[AccountType.ACCOUNT_TYPE_GLOBAL_REWARD]: {
|
||||||
|
amount: '100',
|
||||||
|
percentageOfTotal: '0.1',
|
||||||
|
},
|
||||||
|
[AccountType.ACCOUNT_TYPE_REWARD_MAKER_PAID_FEES]: {
|
||||||
|
amount: '0',
|
||||||
|
percentageOfTotal: '0',
|
||||||
|
},
|
||||||
|
[AccountType.ACCOUNT_TYPE_REWARD_MAKER_RECEIVED_FEES]: {
|
||||||
|
amount: '0',
|
||||||
|
percentageOfTotal: '0',
|
||||||
|
},
|
||||||
|
[AccountType.ACCOUNT_TYPE_REWARD_MARKET_PROPOSERS]: {
|
||||||
|
amount: '0',
|
||||||
|
percentageOfTotal: '0',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns data correctly for the requested epoch range', () => {
|
||||||
|
const rewards = [reward1, reward2, reward3, reward4, reward5];
|
||||||
|
const resultPageOne = generateEpochIndividualRewardsList({
|
||||||
|
rewards,
|
||||||
|
epochId: 3,
|
||||||
|
page: 1,
|
||||||
|
size: 2,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(resultPageOne).toEqual([
|
||||||
|
{
|
||||||
|
epoch: 3,
|
||||||
|
rewards: [
|
||||||
|
{
|
||||||
|
asset: 'USD',
|
||||||
|
totalAmount: '150',
|
||||||
|
rewardTypes: {
|
||||||
|
[AccountType.ACCOUNT_TYPE_FEES_INFRASTRUCTURE]: {
|
||||||
|
amount: '0',
|
||||||
|
percentageOfTotal: '0',
|
||||||
|
},
|
||||||
|
[AccountType.ACCOUNT_TYPE_FEES_LIQUIDITY]: {
|
||||||
|
amount: '150',
|
||||||
|
percentageOfTotal: '0.15',
|
||||||
|
},
|
||||||
|
[AccountType.ACCOUNT_TYPE_GLOBAL_REWARD]: {
|
||||||
|
amount: '0',
|
||||||
|
percentageOfTotal: '0',
|
||||||
|
},
|
||||||
|
[AccountType.ACCOUNT_TYPE_REWARD_MAKER_PAID_FEES]: {
|
||||||
|
amount: '0',
|
||||||
|
percentageOfTotal: '0',
|
||||||
|
},
|
||||||
|
[AccountType.ACCOUNT_TYPE_REWARD_MAKER_RECEIVED_FEES]: {
|
||||||
|
amount: '0',
|
||||||
|
percentageOfTotal: '0',
|
||||||
|
},
|
||||||
|
[AccountType.ACCOUNT_TYPE_REWARD_MARKET_PROPOSERS]: {
|
||||||
|
amount: '0',
|
||||||
|
percentageOfTotal: '0',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
epoch: 2,
|
||||||
|
rewards: [
|
||||||
|
{
|
||||||
|
asset: 'GBP',
|
||||||
|
totalAmount: '200',
|
||||||
|
rewardTypes: {
|
||||||
|
[AccountType.ACCOUNT_TYPE_FEES_INFRASTRUCTURE]: {
|
||||||
|
amount: '0',
|
||||||
|
percentageOfTotal: '0',
|
||||||
|
},
|
||||||
|
[AccountType.ACCOUNT_TYPE_FEES_LIQUIDITY]: {
|
||||||
|
amount: '200',
|
||||||
|
percentageOfTotal: '0.2',
|
||||||
|
},
|
||||||
|
[AccountType.ACCOUNT_TYPE_GLOBAL_REWARD]: {
|
||||||
|
amount: '0',
|
||||||
|
percentageOfTotal: '0',
|
||||||
|
},
|
||||||
|
[AccountType.ACCOUNT_TYPE_REWARD_MAKER_PAID_FEES]: {
|
||||||
|
amount: '0',
|
||||||
|
percentageOfTotal: '0',
|
||||||
|
},
|
||||||
|
[AccountType.ACCOUNT_TYPE_REWARD_MAKER_RECEIVED_FEES]: {
|
||||||
|
amount: '0',
|
||||||
|
percentageOfTotal: '0',
|
||||||
|
},
|
||||||
|
[AccountType.ACCOUNT_TYPE_REWARD_MARKET_PROPOSERS]: {
|
||||||
|
amount: '0',
|
||||||
|
percentageOfTotal: '0',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
asset: 'EUR',
|
||||||
|
totalAmount: '50',
|
||||||
|
rewardTypes: {
|
||||||
|
[AccountType.ACCOUNT_TYPE_FEES_INFRASTRUCTURE]: {
|
||||||
|
amount: '0',
|
||||||
|
percentageOfTotal: '0',
|
||||||
|
},
|
||||||
|
[AccountType.ACCOUNT_TYPE_FEES_LIQUIDITY]: {
|
||||||
|
amount: '0',
|
||||||
|
percentageOfTotal: '0',
|
||||||
|
},
|
||||||
|
[AccountType.ACCOUNT_TYPE_GLOBAL_REWARD]: {
|
||||||
|
amount: '50',
|
||||||
|
percentageOfTotal: '0.05',
|
||||||
|
},
|
||||||
|
[AccountType.ACCOUNT_TYPE_REWARD_MAKER_PAID_FEES]: {
|
||||||
|
amount: '0',
|
||||||
|
percentageOfTotal: '0',
|
||||||
|
},
|
||||||
|
[AccountType.ACCOUNT_TYPE_REWARD_MAKER_RECEIVED_FEES]: {
|
||||||
|
amount: '0',
|
||||||
|
percentageOfTotal: '0',
|
||||||
|
},
|
||||||
|
[AccountType.ACCOUNT_TYPE_REWARD_MARKET_PROPOSERS]: {
|
||||||
|
amount: '0',
|
||||||
|
percentageOfTotal: '0',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
const resultPageTwo = generateEpochIndividualRewardsList({
|
||||||
|
rewards,
|
||||||
|
epochId: 3,
|
||||||
|
page: 2,
|
||||||
|
size: 2,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(resultPageTwo).toEqual([
|
||||||
|
{
|
||||||
|
epoch: 1,
|
||||||
rewards: [
|
rewards: [
|
||||||
{
|
{
|
||||||
asset: 'USD',
|
asset: 'USD',
|
||||||
|
|||||||
+30
-11
@@ -2,9 +2,10 @@ import { BigNumber } from '../../../lib/bignumber';
|
|||||||
import { RowAccountTypes } from '../shared-rewards-table-assets/shared-rewards-table-assets';
|
import { RowAccountTypes } from '../shared-rewards-table-assets/shared-rewards-table-assets';
|
||||||
import type { RewardFieldsFragment } from '../home/__generated__/Rewards';
|
import type { RewardFieldsFragment } from '../home/__generated__/Rewards';
|
||||||
import type { AccountType } from '@vegaprotocol/types';
|
import type { AccountType } from '@vegaprotocol/types';
|
||||||
|
import { calculateEpochOffset } from '../../../lib/epoch-pagination';
|
||||||
|
|
||||||
export interface EpochIndividualReward {
|
export interface EpochIndividualReward {
|
||||||
epoch: string;
|
epoch: number;
|
||||||
rewards: {
|
rewards: {
|
||||||
asset: string;
|
asset: string;
|
||||||
totalAmount: string;
|
totalAmount: string;
|
||||||
@@ -27,11 +28,29 @@ const emptyRowAccountTypes = accountTypes.map((type) => [
|
|||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
export const generateEpochIndividualRewardsList = (
|
export const generateEpochIndividualRewardsList = ({
|
||||||
rewards: RewardFieldsFragment[]
|
rewards,
|
||||||
) => {
|
epochId,
|
||||||
|
page = 1,
|
||||||
|
size = 10,
|
||||||
|
}: {
|
||||||
|
rewards: RewardFieldsFragment[];
|
||||||
|
epochId: number;
|
||||||
|
page?: number;
|
||||||
|
size?: number;
|
||||||
|
}) => {
|
||||||
|
const map: Map<string, EpochIndividualReward> = new Map();
|
||||||
|
const { fromEpoch, toEpoch } = calculateEpochOffset({ epochId, page, size });
|
||||||
|
|
||||||
|
for (let i = toEpoch; i >= fromEpoch; i--) {
|
||||||
|
map.set(i.toString(), {
|
||||||
|
epoch: i,
|
||||||
|
rewards: [],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// We take the rewards and aggregate them by epoch and asset.
|
// We take the rewards and aggregate them by epoch and asset.
|
||||||
const epochIndividualRewards = rewards.reduce((map, reward) => {
|
const epochIndividualRewards = rewards.reduce((acc, reward) => {
|
||||||
const epochId = reward.epoch.id;
|
const epochId = reward.epoch.id;
|
||||||
const assetName = reward.asset.name;
|
const assetName = reward.asset.name;
|
||||||
const rewardType = reward.rewardType;
|
const rewardType = reward.rewardType;
|
||||||
@@ -40,14 +59,14 @@ export const generateEpochIndividualRewardsList = (
|
|||||||
|
|
||||||
// if the rewardType is not of a type we display in the table, we skip it.
|
// if the rewardType is not of a type we display in the table, we skip it.
|
||||||
if (!accountTypes.includes(rewardType)) {
|
if (!accountTypes.includes(rewardType)) {
|
||||||
return map;
|
return acc;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!map.has(epochId)) {
|
if (!acc.has(epochId)) {
|
||||||
map.set(epochId, { epoch: epochId, rewards: [] });
|
return acc;
|
||||||
}
|
}
|
||||||
|
|
||||||
const epoch = map.get(epochId);
|
const epoch = acc.get(epochId);
|
||||||
|
|
||||||
let asset = epoch?.rewards.find((r) => r.asset === assetName);
|
let asset = epoch?.rewards.find((r) => r.asset === assetName);
|
||||||
|
|
||||||
@@ -76,8 +95,8 @@ export const generateEpochIndividualRewardsList = (
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return map;
|
return acc;
|
||||||
}, new Map<string, EpochIndividualReward>());
|
}, map);
|
||||||
|
|
||||||
return Array.from(epochIndividualRewards.values()).sort(
|
return Array.from(epochIndividualRewards.values()).sort(
|
||||||
(a, b) => Number(b.epoch) - Number(a.epoch)
|
(a, b) => Number(b.epoch) - Number(a.epoch)
|
||||||
|
|||||||
+54
-34
@@ -1,44 +1,64 @@
|
|||||||
import { render } from '@testing-library/react';
|
import { render } from '@testing-library/react';
|
||||||
import { AppStateProvider } from '../../../contexts/app-state/app-state-provider';
|
import { AppStateProvider } from '../../../contexts/app-state/app-state-provider';
|
||||||
import { EpochTotalRewardsTable } from './epoch-total-rewards-table';
|
import { EpochTotalRewardsTable } from './epoch-total-rewards-table';
|
||||||
|
import type {
|
||||||
|
AggregatedEpochRewardSummary,
|
||||||
|
RewardType,
|
||||||
|
RewardItem,
|
||||||
|
} from './generate-epoch-total-rewards-list';
|
||||||
import { AccountType } from '@vegaprotocol/types';
|
import { AccountType } from '@vegaprotocol/types';
|
||||||
|
|
||||||
|
const assetId =
|
||||||
|
'b340c130096819428a62e5df407fd6abe66e444b89ad64f670beb98621c9c663';
|
||||||
|
|
||||||
|
const rewardsList = [
|
||||||
|
{
|
||||||
|
rewardType: AccountType.ACCOUNT_TYPE_GLOBAL_REWARD,
|
||||||
|
amount: '0',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
rewardType: AccountType.ACCOUNT_TYPE_FEES_INFRASTRUCTURE,
|
||||||
|
amount: '295',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
rewardType: AccountType.ACCOUNT_TYPE_REWARD_MAKER_PAID_FEES,
|
||||||
|
amount: '0',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
rewardType: AccountType.ACCOUNT_TYPE_REWARD_MAKER_RECEIVED_FEES,
|
||||||
|
amount: '0',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
rewardType: AccountType.ACCOUNT_TYPE_FEES_LIQUIDITY,
|
||||||
|
amount: '0',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
rewardType: AccountType.ACCOUNT_TYPE_REWARD_MARKET_PROPOSERS,
|
||||||
|
amount: '0',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const rewards: Map<RewardType, RewardItem> = new Map();
|
||||||
|
|
||||||
|
rewardsList.forEach((r) => {
|
||||||
|
rewards.set(r.rewardType, r);
|
||||||
|
});
|
||||||
|
|
||||||
|
const assetRewards: Map<
|
||||||
|
AggregatedEpochRewardSummary['assetId'],
|
||||||
|
AggregatedEpochRewardSummary
|
||||||
|
> = new Map();
|
||||||
|
|
||||||
|
assetRewards.set(assetId, {
|
||||||
|
assetId,
|
||||||
|
name: 'tDAI TEST',
|
||||||
|
rewards,
|
||||||
|
totalAmount: '295',
|
||||||
|
});
|
||||||
|
|
||||||
const mockData = {
|
const mockData = {
|
||||||
epoch: 4431,
|
epoch: 4431,
|
||||||
assetRewards: [
|
assetRewards,
|
||||||
{
|
|
||||||
assetId:
|
|
||||||
'b340c130096819428a62e5df407fd6abe66e444b89ad64f670beb98621c9c663',
|
|
||||||
name: 'tDAI TEST',
|
|
||||||
rewards: [
|
|
||||||
{
|
|
||||||
rewardType: AccountType.ACCOUNT_TYPE_GLOBAL_REWARD,
|
|
||||||
amount: '0',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
rewardType: AccountType.ACCOUNT_TYPE_FEES_INFRASTRUCTURE,
|
|
||||||
amount: '295',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
rewardType: AccountType.ACCOUNT_TYPE_REWARD_MAKER_PAID_FEES,
|
|
||||||
amount: '0',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
rewardType: AccountType.ACCOUNT_TYPE_REWARD_MAKER_RECEIVED_FEES,
|
|
||||||
amount: '0',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
rewardType: AccountType.ACCOUNT_TYPE_FEES_LIQUIDITY,
|
|
||||||
amount: '0',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
rewardType: AccountType.ACCOUNT_TYPE_REWARD_MARKET_PROPOSERS,
|
|
||||||
amount: '0',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
totalAmount: '295',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
};
|
||||||
|
|
||||||
describe('EpochTotalRewardsTable', () => {
|
describe('EpochTotalRewardsTable', () => {
|
||||||
|
|||||||
+12
-10
@@ -48,17 +48,19 @@ export const EpochTotalRewardsTable = ({
|
|||||||
}: EpochTotalRewardsGridProps) => {
|
}: EpochTotalRewardsGridProps) => {
|
||||||
return (
|
return (
|
||||||
<RewardsTable dataTestId="epoch-total-rewards-table" epoch={data.epoch}>
|
<RewardsTable dataTestId="epoch-total-rewards-table" epoch={data.epoch}>
|
||||||
{data.assetRewards.map(({ name, rewards, totalAmount }, i) => (
|
{Array.from(data.assetRewards.values()).map(
|
||||||
<div className="contents" key={i}>
|
({ name, rewards, totalAmount }, i) => (
|
||||||
<div data-testid="asset" className={`${rowGridItemStyles()} p-5`}>
|
<div className="contents" key={i}>
|
||||||
{name}
|
<div data-testid="asset" className={`${rowGridItemStyles()} p-5`}>
|
||||||
|
{name}
|
||||||
|
</div>
|
||||||
|
{Array.from(rewards.values()).map(({ rewardType, amount }, i) => (
|
||||||
|
<RewardItem key={i} dataTestId={rewardType} value={amount} />
|
||||||
|
))}
|
||||||
|
<RewardItem dataTestId="total" value={totalAmount} last={true} />
|
||||||
</div>
|
</div>
|
||||||
{rewards.map(({ rewardType, amount }, i) => (
|
)
|
||||||
<RewardItem key={i} dataTestId={rewardType} value={amount} />
|
)}
|
||||||
))}
|
|
||||||
<RewardItem dataTestId="total" value={totalAmount} last={true} />
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</RewardsTable>
|
</RewardsTable>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,21 +1,62 @@
|
|||||||
import { AsyncRenderer } from '@vegaprotocol/ui-toolkit';
|
import { useState, useCallback, useEffect } from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { AsyncRenderer, Pagination } from '@vegaprotocol/ui-toolkit';
|
||||||
|
import type { EpochFieldsFragment } from '../home/__generated__/Rewards';
|
||||||
import { useEpochAssetsRewardsQuery } from '../home/__generated__/Rewards';
|
import { useEpochAssetsRewardsQuery } from '../home/__generated__/Rewards';
|
||||||
import { useRefreshAfterEpoch } from '../../../hooks/use-refresh-after-epoch';
|
|
||||||
import { generateEpochTotalRewardsList } from './generate-epoch-total-rewards-list';
|
import { generateEpochTotalRewardsList } from './generate-epoch-total-rewards-list';
|
||||||
import { NoRewards } from '../no-rewards';
|
|
||||||
import { EpochTotalRewardsTable } from './epoch-total-rewards-table';
|
import { EpochTotalRewardsTable } from './epoch-total-rewards-table';
|
||||||
|
import { calculateEpochOffset } from '../../../lib/epoch-pagination';
|
||||||
|
|
||||||
export const EpochTotalRewards = () => {
|
const EPOCHS_PAGE_SIZE = 10;
|
||||||
|
|
||||||
|
type EpochTotalRewardsProps = {
|
||||||
|
currentEpoch: EpochFieldsFragment;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const EpochTotalRewards = ({ currentEpoch }: EpochTotalRewardsProps) => {
|
||||||
|
// we start from the previous epoch when displaying rewards data, because the current one has no calculated data while ongoing
|
||||||
|
const epochId = Number(currentEpoch.id) - 1;
|
||||||
|
const totalPages = Math.ceil(epochId / EPOCHS_PAGE_SIZE);
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const [page, setPage] = useState(1);
|
||||||
const { data, loading, error, refetch } = useEpochAssetsRewardsQuery({
|
const { data, loading, error, refetch } = useEpochAssetsRewardsQuery({
|
||||||
|
notifyOnNetworkStatusChange: true,
|
||||||
variables: {
|
variables: {
|
||||||
epochRewardSummariesPagination: {
|
epochRewardSummariesFilter: {
|
||||||
first: 10,
|
fromEpoch: epochId - EPOCHS_PAGE_SIZE,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
useRefreshAfterEpoch(data?.epoch.timestamps.expiry, refetch);
|
|
||||||
|
|
||||||
const epochTotalRewardSummaries = generateEpochTotalRewardsList(data) || [];
|
const refetchData = useCallback(
|
||||||
|
async (toPage?: number) => {
|
||||||
|
const targetPage = toPage ?? page;
|
||||||
|
await refetch({
|
||||||
|
epochRewardSummariesFilter: calculateEpochOffset({
|
||||||
|
epochId,
|
||||||
|
page: targetPage,
|
||||||
|
size: EPOCHS_PAGE_SIZE,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
setPage(targetPage);
|
||||||
|
},
|
||||||
|
[epochId, page, refetch]
|
||||||
|
);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
// when the epoch changes, we want to refetch the data to update the current page
|
||||||
|
if (data) {
|
||||||
|
refetchData();
|
||||||
|
}
|
||||||
|
}, [epochId, data, refetchData]);
|
||||||
|
|
||||||
|
const epochTotalRewardSummaries =
|
||||||
|
generateEpochTotalRewardsList({
|
||||||
|
data,
|
||||||
|
epochId,
|
||||||
|
page,
|
||||||
|
size: EPOCHS_PAGE_SIZE,
|
||||||
|
}) || [];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AsyncRenderer
|
<AsyncRenderer
|
||||||
@@ -27,15 +68,22 @@ export const EpochTotalRewards = () => {
|
|||||||
className="max-w-full overflow-auto"
|
className="max-w-full overflow-auto"
|
||||||
data-testid="epoch-rewards-total"
|
data-testid="epoch-rewards-total"
|
||||||
>
|
>
|
||||||
{epochTotalRewardSummaries.length === 0 ? (
|
{Array.from(epochTotalRewardSummaries.values()).map(
|
||||||
<NoRewards />
|
(epochTotalSummary, index) => (
|
||||||
) : (
|
<EpochTotalRewardsTable data={epochTotalSummary} key={index} />
|
||||||
<>
|
)
|
||||||
{epochTotalRewardSummaries.map((epochTotalSummary, index) => (
|
|
||||||
<EpochTotalRewardsTable data={epochTotalSummary} key={index} />
|
|
||||||
))}
|
|
||||||
</>
|
|
||||||
)}
|
)}
|
||||||
|
<Pagination
|
||||||
|
isLoading={loading}
|
||||||
|
hasPrevPage={page > 1}
|
||||||
|
hasNextPage={page < totalPages}
|
||||||
|
onBack={() => refetchData(page - 1)}
|
||||||
|
onNext={() => refetchData(page + 1)}
|
||||||
|
onFirst={() => refetchData(1)}
|
||||||
|
onLast={() => refetchData(totalPages)}
|
||||||
|
>
|
||||||
|
{t('Page')} {page}
|
||||||
|
</Pagination>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
|||||||
+521
-117
@@ -3,13 +3,23 @@ import { AccountType } from '@vegaprotocol/types';
|
|||||||
|
|
||||||
describe('generateEpochAssetRewardsList', () => {
|
describe('generateEpochAssetRewardsList', () => {
|
||||||
it('should return an empty array if data is undefined', () => {
|
it('should return an empty array if data is undefined', () => {
|
||||||
const result = generateEpochTotalRewardsList(undefined);
|
const result = generateEpochTotalRewardsList({ epochId: 1 });
|
||||||
|
|
||||||
expect(result).toEqual([]);
|
expect(result).toEqual(
|
||||||
|
new Map([
|
||||||
|
[
|
||||||
|
'1',
|
||||||
|
{
|
||||||
|
epoch: 1,
|
||||||
|
assetRewards: new Map(),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
])
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return an empty array if empty data is provided', () => {
|
it('should return an empty map if empty data is provided', () => {
|
||||||
const epochData = {
|
const data = {
|
||||||
assetsConnection: {
|
assetsConnection: {
|
||||||
edges: [],
|
edges: [],
|
||||||
},
|
},
|
||||||
@@ -23,13 +33,23 @@ describe('generateEpochAssetRewardsList', () => {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const result = generateEpochTotalRewardsList(epochData);
|
const result = generateEpochTotalRewardsList({ data, epochId: 1 });
|
||||||
|
|
||||||
expect(result).toEqual([]);
|
expect(result).toEqual(
|
||||||
|
new Map([
|
||||||
|
[
|
||||||
|
'1',
|
||||||
|
{
|
||||||
|
epoch: 1,
|
||||||
|
assetRewards: new Map(),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
])
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return an empty array if no epochRewardSummaries are provided', () => {
|
it('should return an empty map if no epochRewardSummaries are provided', () => {
|
||||||
const epochData = {
|
const data = {
|
||||||
assetsConnection: {
|
assetsConnection: {
|
||||||
edges: [
|
edges: [
|
||||||
{
|
{
|
||||||
@@ -56,13 +76,23 @@ describe('generateEpochAssetRewardsList', () => {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const result = generateEpochTotalRewardsList(epochData);
|
const result = generateEpochTotalRewardsList({ data, epochId: 1 });
|
||||||
|
|
||||||
expect(result).toEqual([]);
|
expect(result).toEqual(
|
||||||
|
new Map([
|
||||||
|
[
|
||||||
|
'1',
|
||||||
|
{
|
||||||
|
epoch: 1,
|
||||||
|
assetRewards: new Map(),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
])
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return an array of unnamed assets if no asset names are provided (should not happen)', () => {
|
it('should return a map of unnamed assets if no asset names are provided (should not happen)', () => {
|
||||||
const epochData = {
|
const data = {
|
||||||
assetsConnection: {
|
assetsConnection: {
|
||||||
edges: [],
|
edges: [],
|
||||||
},
|
},
|
||||||
@@ -85,50 +115,80 @@ describe('generateEpochAssetRewardsList', () => {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const result = generateEpochTotalRewardsList(epochData);
|
const result = generateEpochTotalRewardsList({ data, epochId: 1 });
|
||||||
|
|
||||||
expect(result).toEqual([
|
expect(result).toEqual(
|
||||||
{
|
new Map([
|
||||||
epoch: 1,
|
[
|
||||||
assetRewards: [
|
'1',
|
||||||
{
|
{
|
||||||
assetId: '1',
|
epoch: 1,
|
||||||
name: '',
|
assetRewards: new Map([
|
||||||
rewards: [
|
[
|
||||||
{
|
'1',
|
||||||
rewardType: AccountType.ACCOUNT_TYPE_GLOBAL_REWARD,
|
{
|
||||||
amount: '123',
|
assetId: '1',
|
||||||
},
|
name: '',
|
||||||
{
|
rewards: new Map([
|
||||||
rewardType: AccountType.ACCOUNT_TYPE_FEES_INFRASTRUCTURE,
|
[
|
||||||
amount: '0',
|
AccountType.ACCOUNT_TYPE_GLOBAL_REWARD,
|
||||||
},
|
{
|
||||||
{
|
rewardType: AccountType.ACCOUNT_TYPE_GLOBAL_REWARD,
|
||||||
rewardType: AccountType.ACCOUNT_TYPE_REWARD_MAKER_PAID_FEES,
|
amount: '123',
|
||||||
amount: '0',
|
},
|
||||||
},
|
],
|
||||||
{
|
[
|
||||||
rewardType: AccountType.ACCOUNT_TYPE_REWARD_MAKER_RECEIVED_FEES,
|
AccountType.ACCOUNT_TYPE_FEES_INFRASTRUCTURE,
|
||||||
amount: '0',
|
{
|
||||||
},
|
rewardType:
|
||||||
{
|
AccountType.ACCOUNT_TYPE_FEES_INFRASTRUCTURE,
|
||||||
rewardType: AccountType.ACCOUNT_TYPE_FEES_LIQUIDITY,
|
amount: '0',
|
||||||
amount: '0',
|
},
|
||||||
},
|
],
|
||||||
{
|
[
|
||||||
rewardType: AccountType.ACCOUNT_TYPE_REWARD_MARKET_PROPOSERS,
|
AccountType.ACCOUNT_TYPE_REWARD_MAKER_PAID_FEES,
|
||||||
amount: '0',
|
{
|
||||||
},
|
rewardType:
|
||||||
],
|
AccountType.ACCOUNT_TYPE_REWARD_MAKER_PAID_FEES,
|
||||||
totalAmount: '123',
|
amount: '0',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
AccountType.ACCOUNT_TYPE_REWARD_MAKER_RECEIVED_FEES,
|
||||||
|
{
|
||||||
|
rewardType:
|
||||||
|
AccountType.ACCOUNT_TYPE_REWARD_MAKER_RECEIVED_FEES,
|
||||||
|
amount: '0',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
AccountType.ACCOUNT_TYPE_FEES_LIQUIDITY,
|
||||||
|
{
|
||||||
|
rewardType: AccountType.ACCOUNT_TYPE_FEES_LIQUIDITY,
|
||||||
|
amount: '0',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
AccountType.ACCOUNT_TYPE_REWARD_MARKET_PROPOSERS,
|
||||||
|
{
|
||||||
|
rewardType:
|
||||||
|
AccountType.ACCOUNT_TYPE_REWARD_MARKET_PROPOSERS,
|
||||||
|
amount: '0',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
]),
|
||||||
|
totalAmount: '123',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
]),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
])
|
||||||
]);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return an array of aggregated epoch summaries', () => {
|
it('should return the aggregated epoch summaries', () => {
|
||||||
const epochData = {
|
const data = {
|
||||||
assetsConnection: {
|
assetsConnection: {
|
||||||
edges: [
|
edges: [
|
||||||
{
|
{
|
||||||
@@ -180,81 +240,425 @@ describe('generateEpochAssetRewardsList', () => {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const result = generateEpochTotalRewardsList(epochData);
|
const result = generateEpochTotalRewardsList({ data, epochId: 2 });
|
||||||
|
|
||||||
expect(result).toEqual([
|
expect(result).toEqual(
|
||||||
{
|
new Map([
|
||||||
epoch: 1,
|
[
|
||||||
assetRewards: [
|
'1',
|
||||||
{
|
{
|
||||||
assetId: '1',
|
epoch: 1,
|
||||||
name: 'Asset 1',
|
assetRewards: new Map([
|
||||||
rewards: [
|
[
|
||||||
{
|
'1',
|
||||||
rewardType: AccountType.ACCOUNT_TYPE_GLOBAL_REWARD,
|
{
|
||||||
amount: '0',
|
assetId: '1',
|
||||||
},
|
name: 'Asset 1',
|
||||||
{
|
rewards: new Map([
|
||||||
rewardType: AccountType.ACCOUNT_TYPE_FEES_INFRASTRUCTURE,
|
[
|
||||||
amount: '100',
|
AccountType.ACCOUNT_TYPE_GLOBAL_REWARD,
|
||||||
},
|
{
|
||||||
{
|
rewardType: AccountType.ACCOUNT_TYPE_GLOBAL_REWARD,
|
||||||
rewardType: AccountType.ACCOUNT_TYPE_REWARD_MAKER_PAID_FEES,
|
amount: '0',
|
||||||
amount: '123',
|
},
|
||||||
},
|
],
|
||||||
{
|
[
|
||||||
rewardType: AccountType.ACCOUNT_TYPE_REWARD_MAKER_RECEIVED_FEES,
|
AccountType.ACCOUNT_TYPE_FEES_INFRASTRUCTURE,
|
||||||
amount: '0',
|
{
|
||||||
},
|
rewardType:
|
||||||
{
|
AccountType.ACCOUNT_TYPE_FEES_INFRASTRUCTURE,
|
||||||
rewardType: AccountType.ACCOUNT_TYPE_FEES_LIQUIDITY,
|
amount: '100',
|
||||||
amount: '0',
|
},
|
||||||
},
|
],
|
||||||
{
|
[
|
||||||
rewardType: AccountType.ACCOUNT_TYPE_REWARD_MARKET_PROPOSERS,
|
AccountType.ACCOUNT_TYPE_REWARD_MAKER_PAID_FEES,
|
||||||
amount: '0',
|
{
|
||||||
},
|
rewardType:
|
||||||
],
|
AccountType.ACCOUNT_TYPE_REWARD_MAKER_PAID_FEES,
|
||||||
totalAmount: '223',
|
amount: '123',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
AccountType.ACCOUNT_TYPE_REWARD_MAKER_RECEIVED_FEES,
|
||||||
|
{
|
||||||
|
rewardType:
|
||||||
|
AccountType.ACCOUNT_TYPE_REWARD_MAKER_RECEIVED_FEES,
|
||||||
|
amount: '0',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
AccountType.ACCOUNT_TYPE_FEES_LIQUIDITY,
|
||||||
|
{
|
||||||
|
rewardType: AccountType.ACCOUNT_TYPE_FEES_LIQUIDITY,
|
||||||
|
amount: '0',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
AccountType.ACCOUNT_TYPE_REWARD_MARKET_PROPOSERS,
|
||||||
|
{
|
||||||
|
rewardType:
|
||||||
|
AccountType.ACCOUNT_TYPE_REWARD_MARKET_PROPOSERS,
|
||||||
|
amount: '0',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
]),
|
||||||
|
totalAmount: '223',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
]),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'2',
|
||||||
|
{
|
||||||
|
epoch: 2,
|
||||||
|
assetRewards: new Map([
|
||||||
|
[
|
||||||
|
'1',
|
||||||
|
{
|
||||||
|
assetId: '1',
|
||||||
|
name: 'Asset 1',
|
||||||
|
rewards: new Map([
|
||||||
|
[
|
||||||
|
AccountType.ACCOUNT_TYPE_GLOBAL_REWARD,
|
||||||
|
{
|
||||||
|
rewardType: AccountType.ACCOUNT_TYPE_GLOBAL_REWARD,
|
||||||
|
amount: '0',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
AccountType.ACCOUNT_TYPE_FEES_INFRASTRUCTURE,
|
||||||
|
{
|
||||||
|
rewardType:
|
||||||
|
AccountType.ACCOUNT_TYPE_FEES_INFRASTRUCTURE,
|
||||||
|
amount: '0',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
AccountType.ACCOUNT_TYPE_REWARD_MAKER_PAID_FEES,
|
||||||
|
{
|
||||||
|
rewardType:
|
||||||
|
AccountType.ACCOUNT_TYPE_REWARD_MAKER_PAID_FEES,
|
||||||
|
amount: '0',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
AccountType.ACCOUNT_TYPE_REWARD_MAKER_RECEIVED_FEES,
|
||||||
|
{
|
||||||
|
rewardType:
|
||||||
|
AccountType.ACCOUNT_TYPE_REWARD_MAKER_RECEIVED_FEES,
|
||||||
|
amount: '0',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
AccountType.ACCOUNT_TYPE_FEES_LIQUIDITY,
|
||||||
|
{
|
||||||
|
rewardType: AccountType.ACCOUNT_TYPE_FEES_LIQUIDITY,
|
||||||
|
amount: '5',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
AccountType.ACCOUNT_TYPE_REWARD_MARKET_PROPOSERS,
|
||||||
|
{
|
||||||
|
rewardType:
|
||||||
|
AccountType.ACCOUNT_TYPE_REWARD_MARKET_PROPOSERS,
|
||||||
|
amount: '0',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
]),
|
||||||
|
totalAmount: '5',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
]),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
])
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return the requested range for aggregated epoch summaries', () => {
|
||||||
|
const data = {
|
||||||
|
assetsConnection: {
|
||||||
|
edges: [
|
||||||
|
{
|
||||||
|
node: {
|
||||||
|
id: '1',
|
||||||
|
name: 'Asset 1',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
node: {
|
||||||
|
id: '2',
|
||||||
|
name: 'Asset 2',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
epochRewardSummaries: {
|
||||||
epoch: 2,
|
edges: [
|
||||||
assetRewards: [
|
|
||||||
{
|
{
|
||||||
assetId: '1',
|
node: {
|
||||||
name: 'Asset 1',
|
epoch: 1,
|
||||||
rewards: [
|
assetId: '1',
|
||||||
{
|
rewardType: AccountType.ACCOUNT_TYPE_REWARD_MAKER_PAID_FEES,
|
||||||
rewardType: AccountType.ACCOUNT_TYPE_GLOBAL_REWARD,
|
amount: '123',
|
||||||
amount: '0',
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
rewardType: AccountType.ACCOUNT_TYPE_FEES_INFRASTRUCTURE,
|
node: {
|
||||||
amount: '0',
|
epoch: 1,
|
||||||
},
|
assetId: '1',
|
||||||
{
|
rewardType: AccountType.ACCOUNT_TYPE_FEES_INFRASTRUCTURE,
|
||||||
rewardType: AccountType.ACCOUNT_TYPE_REWARD_MAKER_PAID_FEES,
|
amount: '100',
|
||||||
amount: '0',
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
rewardType: AccountType.ACCOUNT_TYPE_REWARD_MAKER_RECEIVED_FEES,
|
node: {
|
||||||
amount: '0',
|
epoch: 2,
|
||||||
},
|
assetId: '1',
|
||||||
{
|
rewardType: AccountType.ACCOUNT_TYPE_FEES_LIQUIDITY,
|
||||||
rewardType: AccountType.ACCOUNT_TYPE_FEES_LIQUIDITY,
|
amount: '6',
|
||||||
amount: '5',
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
rewardType: AccountType.ACCOUNT_TYPE_REWARD_MARKET_PROPOSERS,
|
node: {
|
||||||
amount: '0',
|
epoch: 2,
|
||||||
},
|
assetId: '1',
|
||||||
],
|
rewardType: AccountType.ACCOUNT_TYPE_FEES_LIQUIDITY,
|
||||||
totalAmount: '5',
|
amount: '27',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
node: {
|
||||||
|
epoch: 3,
|
||||||
|
assetId: '1',
|
||||||
|
rewardType: AccountType.ACCOUNT_TYPE_FEES_INFRASTRUCTURE,
|
||||||
|
amount: '15',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
]);
|
epoch: {
|
||||||
|
timestamps: {
|
||||||
|
expiry: null,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const resultPageOne = generateEpochTotalRewardsList({
|
||||||
|
data,
|
||||||
|
epochId: 3,
|
||||||
|
page: 1,
|
||||||
|
size: 2,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(resultPageOne).toEqual(
|
||||||
|
new Map([
|
||||||
|
[
|
||||||
|
'2',
|
||||||
|
{
|
||||||
|
epoch: 2,
|
||||||
|
assetRewards: new Map([
|
||||||
|
[
|
||||||
|
'1',
|
||||||
|
{
|
||||||
|
assetId: '1',
|
||||||
|
name: 'Asset 1',
|
||||||
|
rewards: new Map([
|
||||||
|
[
|
||||||
|
AccountType.ACCOUNT_TYPE_GLOBAL_REWARD,
|
||||||
|
{
|
||||||
|
rewardType: AccountType.ACCOUNT_TYPE_GLOBAL_REWARD,
|
||||||
|
amount: '0',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
AccountType.ACCOUNT_TYPE_FEES_INFRASTRUCTURE,
|
||||||
|
{
|
||||||
|
rewardType:
|
||||||
|
AccountType.ACCOUNT_TYPE_FEES_INFRASTRUCTURE,
|
||||||
|
amount: '0',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
AccountType.ACCOUNT_TYPE_REWARD_MAKER_PAID_FEES,
|
||||||
|
{
|
||||||
|
rewardType:
|
||||||
|
AccountType.ACCOUNT_TYPE_REWARD_MAKER_PAID_FEES,
|
||||||
|
amount: '0',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
AccountType.ACCOUNT_TYPE_REWARD_MAKER_RECEIVED_FEES,
|
||||||
|
{
|
||||||
|
rewardType:
|
||||||
|
AccountType.ACCOUNT_TYPE_REWARD_MAKER_RECEIVED_FEES,
|
||||||
|
amount: '0',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
AccountType.ACCOUNT_TYPE_FEES_LIQUIDITY,
|
||||||
|
{
|
||||||
|
rewardType: AccountType.ACCOUNT_TYPE_FEES_LIQUIDITY,
|
||||||
|
amount: '33',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
AccountType.ACCOUNT_TYPE_REWARD_MARKET_PROPOSERS,
|
||||||
|
{
|
||||||
|
rewardType:
|
||||||
|
AccountType.ACCOUNT_TYPE_REWARD_MARKET_PROPOSERS,
|
||||||
|
amount: '0',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
]),
|
||||||
|
totalAmount: '33',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
]),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'3',
|
||||||
|
{
|
||||||
|
epoch: 3,
|
||||||
|
assetRewards: new Map([
|
||||||
|
[
|
||||||
|
'1',
|
||||||
|
{
|
||||||
|
assetId: '1',
|
||||||
|
name: 'Asset 1',
|
||||||
|
rewards: new Map([
|
||||||
|
[
|
||||||
|
AccountType.ACCOUNT_TYPE_GLOBAL_REWARD,
|
||||||
|
{
|
||||||
|
rewardType: AccountType.ACCOUNT_TYPE_GLOBAL_REWARD,
|
||||||
|
amount: '0',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
AccountType.ACCOUNT_TYPE_FEES_INFRASTRUCTURE,
|
||||||
|
{
|
||||||
|
rewardType:
|
||||||
|
AccountType.ACCOUNT_TYPE_FEES_INFRASTRUCTURE,
|
||||||
|
amount: '15',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
AccountType.ACCOUNT_TYPE_REWARD_MAKER_PAID_FEES,
|
||||||
|
{
|
||||||
|
rewardType:
|
||||||
|
AccountType.ACCOUNT_TYPE_REWARD_MAKER_PAID_FEES,
|
||||||
|
amount: '0',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
AccountType.ACCOUNT_TYPE_REWARD_MAKER_RECEIVED_FEES,
|
||||||
|
{
|
||||||
|
rewardType:
|
||||||
|
AccountType.ACCOUNT_TYPE_REWARD_MAKER_RECEIVED_FEES,
|
||||||
|
amount: '0',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
AccountType.ACCOUNT_TYPE_FEES_LIQUIDITY,
|
||||||
|
{
|
||||||
|
rewardType: AccountType.ACCOUNT_TYPE_FEES_LIQUIDITY,
|
||||||
|
amount: '0',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
AccountType.ACCOUNT_TYPE_REWARD_MARKET_PROPOSERS,
|
||||||
|
{
|
||||||
|
rewardType:
|
||||||
|
AccountType.ACCOUNT_TYPE_REWARD_MARKET_PROPOSERS,
|
||||||
|
amount: '0',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
]),
|
||||||
|
totalAmount: '15',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
]),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
])
|
||||||
|
);
|
||||||
|
|
||||||
|
const resultPageTwo = generateEpochTotalRewardsList({
|
||||||
|
data,
|
||||||
|
epochId: 3,
|
||||||
|
page: 2,
|
||||||
|
size: 2,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(resultPageTwo).toEqual(
|
||||||
|
new Map([
|
||||||
|
[
|
||||||
|
'1',
|
||||||
|
{
|
||||||
|
epoch: 1,
|
||||||
|
assetRewards: new Map([
|
||||||
|
[
|
||||||
|
'1',
|
||||||
|
{
|
||||||
|
assetId: '1',
|
||||||
|
name: 'Asset 1',
|
||||||
|
rewards: new Map([
|
||||||
|
[
|
||||||
|
AccountType.ACCOUNT_TYPE_GLOBAL_REWARD,
|
||||||
|
{
|
||||||
|
rewardType: AccountType.ACCOUNT_TYPE_GLOBAL_REWARD,
|
||||||
|
amount: '0',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
AccountType.ACCOUNT_TYPE_FEES_INFRASTRUCTURE,
|
||||||
|
{
|
||||||
|
rewardType:
|
||||||
|
AccountType.ACCOUNT_TYPE_FEES_INFRASTRUCTURE,
|
||||||
|
amount: '100',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
AccountType.ACCOUNT_TYPE_REWARD_MAKER_PAID_FEES,
|
||||||
|
{
|
||||||
|
rewardType:
|
||||||
|
AccountType.ACCOUNT_TYPE_REWARD_MAKER_PAID_FEES,
|
||||||
|
amount: '123',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
AccountType.ACCOUNT_TYPE_REWARD_MAKER_RECEIVED_FEES,
|
||||||
|
{
|
||||||
|
rewardType:
|
||||||
|
AccountType.ACCOUNT_TYPE_REWARD_MAKER_RECEIVED_FEES,
|
||||||
|
amount: '0',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
AccountType.ACCOUNT_TYPE_FEES_LIQUIDITY,
|
||||||
|
{
|
||||||
|
rewardType: AccountType.ACCOUNT_TYPE_FEES_LIQUIDITY,
|
||||||
|
amount: '0',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
AccountType.ACCOUNT_TYPE_REWARD_MARKET_PROPOSERS,
|
||||||
|
{
|
||||||
|
rewardType:
|
||||||
|
AccountType.ACCOUNT_TYPE_REWARD_MARKET_PROPOSERS,
|
||||||
|
amount: '0',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
]),
|
||||||
|
totalAmount: '223',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
]),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
])
|
||||||
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
+74
-104
@@ -5,127 +5,97 @@ import type {
|
|||||||
import { removePaginationWrapper } from '@vegaprotocol/utils';
|
import { removePaginationWrapper } from '@vegaprotocol/utils';
|
||||||
import { RowAccountTypes } from '../shared-rewards-table-assets/shared-rewards-table-assets';
|
import { RowAccountTypes } from '../shared-rewards-table-assets/shared-rewards-table-assets';
|
||||||
import type { AccountType } from '@vegaprotocol/types';
|
import type { AccountType } from '@vegaprotocol/types';
|
||||||
import { BigNumber } from '../../../lib/bignumber';
|
import { calculateEpochOffset } from '../../../lib/epoch-pagination';
|
||||||
|
|
||||||
interface EpochSummaryWithNamedReward extends EpochRewardSummaryFieldsFragment {
|
interface EpochSummaryWithNamedReward extends EpochRewardSummaryFieldsFragment {
|
||||||
name: string;
|
name: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AggregatedEpochRewardSummary {
|
export type RewardType = EpochRewardSummaryFieldsFragment['rewardType'];
|
||||||
|
export type RewardItem = Pick<
|
||||||
|
EpochRewardSummaryFieldsFragment,
|
||||||
|
'rewardType' | 'amount'
|
||||||
|
>;
|
||||||
|
|
||||||
|
export type AggregatedEpochRewardSummary = {
|
||||||
assetId: EpochRewardSummaryFieldsFragment['assetId'];
|
assetId: EpochRewardSummaryFieldsFragment['assetId'];
|
||||||
name: EpochSummaryWithNamedReward['name'];
|
name: EpochSummaryWithNamedReward['name'];
|
||||||
rewards: {
|
rewards: Map<RewardType, RewardItem>;
|
||||||
rewardType: EpochRewardSummaryFieldsFragment['rewardType'];
|
|
||||||
amount: EpochRewardSummaryFieldsFragment['amount'];
|
|
||||||
}[];
|
|
||||||
totalAmount: string;
|
totalAmount: string;
|
||||||
}
|
};
|
||||||
|
|
||||||
export interface EpochTotalSummary {
|
export type EpochTotalSummary = {
|
||||||
epoch: EpochRewardSummaryFieldsFragment['epoch'];
|
epoch: EpochRewardSummaryFieldsFragment['epoch'];
|
||||||
assetRewards: AggregatedEpochRewardSummary[];
|
assetRewards: Map<
|
||||||
}
|
EpochRewardSummaryFieldsFragment['assetId'],
|
||||||
|
AggregatedEpochRewardSummary
|
||||||
|
>;
|
||||||
|
};
|
||||||
|
|
||||||
const emptyRowAccountTypes = Object.keys(RowAccountTypes).map((type) => ({
|
const emptyRowAccountTypes: Map<RewardType, RewardItem> = new Map();
|
||||||
rewardType: type as AccountType,
|
|
||||||
amount: '0',
|
|
||||||
}));
|
|
||||||
|
|
||||||
export const generateEpochTotalRewardsList = (
|
Object.keys(RowAccountTypes).forEach((type) => {
|
||||||
epochData: EpochAssetsRewardsQuery | undefined
|
emptyRowAccountTypes.set(type as AccountType, {
|
||||||
) => {
|
rewardType: type as AccountType,
|
||||||
|
amount: '0',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
export const generateEpochTotalRewardsList = ({
|
||||||
|
data,
|
||||||
|
epochId,
|
||||||
|
page = 1,
|
||||||
|
size = 10,
|
||||||
|
}: {
|
||||||
|
data?: EpochAssetsRewardsQuery | undefined;
|
||||||
|
epochId: number;
|
||||||
|
page?: number;
|
||||||
|
size?: number;
|
||||||
|
}) => {
|
||||||
const epochRewardSummaries = removePaginationWrapper(
|
const epochRewardSummaries = removePaginationWrapper(
|
||||||
epochData?.epochRewardSummaries?.edges
|
data?.epochRewardSummaries?.edges
|
||||||
);
|
);
|
||||||
|
|
||||||
const assets = removePaginationWrapper(epochData?.assetsConnection?.edges);
|
const assets = removePaginationWrapper(data?.assetsConnection?.edges);
|
||||||
|
|
||||||
// Because the epochRewardSummaries don't have the asset name, we need to find it in the assets list
|
const map: Map<string, EpochTotalSummary> = new Map();
|
||||||
const epochSummariesWithNamedReward: EpochSummaryWithNamedReward[] =
|
const { fromEpoch, toEpoch } = calculateEpochOffset({ epochId, page, size });
|
||||||
epochRewardSummaries.map((epochReward) => ({
|
|
||||||
...epochReward,
|
|
||||||
name:
|
|
||||||
assets.find((asset) => asset.id === epochReward.assetId)?.name || '',
|
|
||||||
}));
|
|
||||||
|
|
||||||
// Aggregating the epoch summaries by epoch number
|
for (let i = toEpoch; i >= fromEpoch; i--) {
|
||||||
const aggregatedEpochSummariesByEpochNumber =
|
map.set(i.toString(), {
|
||||||
epochSummariesWithNamedReward.reduce((acc, epochReward) => {
|
epoch: i,
|
||||||
const epoch = epochReward.epoch;
|
assetRewards: new Map(),
|
||||||
const epochSummaryIndex = acc.findIndex(
|
|
||||||
(epochSummary) => epochSummary[0].epoch === epoch
|
|
||||||
);
|
|
||||||
|
|
||||||
if (epochSummaryIndex === -1) {
|
|
||||||
acc.push([epochReward]);
|
|
||||||
} else {
|
|
||||||
acc[epochSummaryIndex].push(epochReward);
|
|
||||||
}
|
|
||||||
|
|
||||||
return acc;
|
|
||||||
}, [] as EpochSummaryWithNamedReward[][]);
|
|
||||||
|
|
||||||
// Now aggregate the array of arrays of epoch summaries by asset rewards.
|
|
||||||
const epochTotalRewards: EpochTotalSummary[] =
|
|
||||||
aggregatedEpochSummariesByEpochNumber.map((epochSummaries) => {
|
|
||||||
const assetRewards = epochSummaries.reduce((acc, epochSummary) => {
|
|
||||||
const assetRewardIndex = acc.findIndex(
|
|
||||||
(assetReward) =>
|
|
||||||
assetReward.assetId === epochSummary.assetId &&
|
|
||||||
assetReward.name === epochSummary.name
|
|
||||||
);
|
|
||||||
|
|
||||||
if (assetRewardIndex === -1) {
|
|
||||||
acc.push({
|
|
||||||
assetId: epochSummary.assetId,
|
|
||||||
name: epochSummary.name,
|
|
||||||
rewards: [
|
|
||||||
...emptyRowAccountTypes.map((emptyRowAccountType) => {
|
|
||||||
if (
|
|
||||||
emptyRowAccountType.rewardType === epochSummary.rewardType
|
|
||||||
) {
|
|
||||||
return {
|
|
||||||
rewardType: epochSummary.rewardType,
|
|
||||||
amount: epochSummary.amount,
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
return emptyRowAccountType;
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
totalAmount: epochSummary.amount,
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
acc[assetRewardIndex].rewards = acc[assetRewardIndex].rewards.map(
|
|
||||||
(reward) => {
|
|
||||||
if (reward.rewardType === epochSummary.rewardType) {
|
|
||||||
return {
|
|
||||||
rewardType: epochSummary.rewardType,
|
|
||||||
amount: (
|
|
||||||
Number(reward.amount) + Number(epochSummary.amount)
|
|
||||||
).toString(),
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
return reward;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
acc[assetRewardIndex].totalAmount = (
|
|
||||||
Number(acc[assetRewardIndex].totalAmount) +
|
|
||||||
Number(epochSummary.amount)
|
|
||||||
).toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
return acc;
|
|
||||||
}, [] as AggregatedEpochRewardSummary[]);
|
|
||||||
|
|
||||||
return {
|
|
||||||
epoch: epochSummaries[0].epoch,
|
|
||||||
assetRewards: assetRewards.sort((a, b) => {
|
|
||||||
return new BigNumber(b.totalAmount).comparedTo(a.totalAmount);
|
|
||||||
}),
|
|
||||||
};
|
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return epochTotalRewards;
|
return epochRewardSummaries.reduce((acc, reward) => {
|
||||||
|
const epoch = acc.get(reward.epoch.toString());
|
||||||
|
|
||||||
|
if (epoch) {
|
||||||
|
const matchingAsset = assets.find((asset) => asset.id === reward.assetId);
|
||||||
|
const assetWithRewards = epoch.assetRewards.get(reward.assetId);
|
||||||
|
|
||||||
|
const rewards =
|
||||||
|
assetWithRewards?.rewards || new Map(emptyRowAccountTypes);
|
||||||
|
const rewardItem = rewards?.get(reward.rewardType);
|
||||||
|
const amount = (
|
||||||
|
(Number(rewardItem?.amount) || 0) + Number(reward.amount)
|
||||||
|
).toString();
|
||||||
|
|
||||||
|
rewards?.set(reward.rewardType, {
|
||||||
|
rewardType: reward.rewardType,
|
||||||
|
amount,
|
||||||
|
});
|
||||||
|
|
||||||
|
epoch.assetRewards.set(reward.assetId, {
|
||||||
|
assetId: reward.assetId,
|
||||||
|
name: matchingAsset?.name || '',
|
||||||
|
rewards: rewards || new Map(emptyRowAccountTypes),
|
||||||
|
totalAmount: (
|
||||||
|
Number(reward.amount) + Number(assetWithRewards?.totalAmount || 0)
|
||||||
|
).toString(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return acc;
|
||||||
|
}, map);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -23,12 +23,18 @@ fragment DelegationFields on Delegation {
|
|||||||
|
|
||||||
query Rewards(
|
query Rewards(
|
||||||
$partyId: ID!
|
$partyId: ID!
|
||||||
$delegationsPagination: Pagination
|
$fromEpoch: Int
|
||||||
|
$toEpoch: Int
|
||||||
$rewardsPagination: Pagination
|
$rewardsPagination: Pagination
|
||||||
|
$delegationsPagination: Pagination
|
||||||
) {
|
) {
|
||||||
party(id: $partyId) {
|
party(id: $partyId) {
|
||||||
id
|
id
|
||||||
rewardsConnection(pagination: $rewardsPagination) {
|
rewardsConnection(
|
||||||
|
fromEpoch: $fromEpoch
|
||||||
|
toEpoch: $toEpoch
|
||||||
|
pagination: $rewardsPagination
|
||||||
|
) {
|
||||||
edges {
|
edges {
|
||||||
node {
|
node {
|
||||||
...RewardFields
|
...RewardFields
|
||||||
@@ -43,14 +49,6 @@ query Rewards(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
epoch {
|
|
||||||
id
|
|
||||||
timestamps {
|
|
||||||
start
|
|
||||||
end
|
|
||||||
expiry
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fragment EpochRewardSummaryFields on EpochRewardSummary {
|
fragment EpochRewardSummaryFields on EpochRewardSummary {
|
||||||
@@ -60,7 +58,10 @@ fragment EpochRewardSummaryFields on EpochRewardSummary {
|
|||||||
rewardType
|
rewardType
|
||||||
}
|
}
|
||||||
|
|
||||||
query EpochAssetsRewards($epochRewardSummariesPagination: Pagination) {
|
query EpochAssetsRewards(
|
||||||
|
$epochRewardSummariesFilter: RewardSummaryFilter
|
||||||
|
$epochRewardSummariesPagination: Pagination
|
||||||
|
) {
|
||||||
assetsConnection {
|
assetsConnection {
|
||||||
edges {
|
edges {
|
||||||
node {
|
node {
|
||||||
@@ -69,18 +70,16 @@ query EpochAssetsRewards($epochRewardSummariesPagination: Pagination) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
epochRewardSummaries(pagination: $epochRewardSummariesPagination) {
|
epochRewardSummaries(
|
||||||
|
filter: $epochRewardSummariesFilter
|
||||||
|
pagination: $epochRewardSummariesPagination
|
||||||
|
) {
|
||||||
edges {
|
edges {
|
||||||
node {
|
node {
|
||||||
...EpochRewardSummaryFields
|
...EpochRewardSummaryFields
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
epoch {
|
|
||||||
timestamps {
|
|
||||||
expiry
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fragment EpochFields on Epoch {
|
fragment EpochFields on Epoch {
|
||||||
|
|||||||
+21
-21
@@ -9,21 +9,24 @@ export type DelegationFieldsFragment = { __typename?: 'Delegation', amount: stri
|
|||||||
|
|
||||||
export type RewardsQueryVariables = Types.Exact<{
|
export type RewardsQueryVariables = Types.Exact<{
|
||||||
partyId: Types.Scalars['ID'];
|
partyId: Types.Scalars['ID'];
|
||||||
delegationsPagination?: Types.InputMaybe<Types.Pagination>;
|
fromEpoch?: Types.InputMaybe<Types.Scalars['Int']>;
|
||||||
|
toEpoch?: Types.InputMaybe<Types.Scalars['Int']>;
|
||||||
rewardsPagination?: Types.InputMaybe<Types.Pagination>;
|
rewardsPagination?: Types.InputMaybe<Types.Pagination>;
|
||||||
|
delegationsPagination?: Types.InputMaybe<Types.Pagination>;
|
||||||
}>;
|
}>;
|
||||||
|
|
||||||
|
|
||||||
export type RewardsQuery = { __typename?: 'Query', party?: { __typename?: 'Party', id: string, rewardsConnection?: { __typename?: 'RewardsConnection', edges?: Array<{ __typename?: 'RewardEdge', node: { __typename?: 'Reward', rewardType: Types.AccountType, amount: string, percentageOfTotal: string, receivedAt: any, asset: { __typename?: 'Asset', id: string, symbol: string, name: string }, party: { __typename?: 'Party', id: string }, epoch: { __typename?: 'Epoch', id: string } } } | null> | null } | null, delegationsConnection?: { __typename?: 'DelegationsConnection', edges?: Array<{ __typename?: 'DelegationEdge', node: { __typename?: 'Delegation', amount: string, epoch: number } } | null> | null } | null } | null, epoch: { __typename?: 'Epoch', id: string, timestamps: { __typename?: 'EpochTimestamps', start?: any | null, end?: any | null, expiry?: any | null } } };
|
export type RewardsQuery = { __typename?: 'Query', party?: { __typename?: 'Party', id: string, rewardsConnection?: { __typename?: 'RewardsConnection', edges?: Array<{ __typename?: 'RewardEdge', node: { __typename?: 'Reward', rewardType: Types.AccountType, amount: string, percentageOfTotal: string, receivedAt: any, asset: { __typename?: 'Asset', id: string, symbol: string, name: string }, party: { __typename?: 'Party', id: string }, epoch: { __typename?: 'Epoch', id: string } } } | null> | null } | null, delegationsConnection?: { __typename?: 'DelegationsConnection', edges?: Array<{ __typename?: 'DelegationEdge', node: { __typename?: 'Delegation', amount: string, epoch: number } } | null> | null } | null } | null };
|
||||||
|
|
||||||
export type EpochRewardSummaryFieldsFragment = { __typename?: 'EpochRewardSummary', epoch: number, assetId: string, amount: string, rewardType: Types.AccountType };
|
export type EpochRewardSummaryFieldsFragment = { __typename?: 'EpochRewardSummary', epoch: number, assetId: string, amount: string, rewardType: Types.AccountType };
|
||||||
|
|
||||||
export type EpochAssetsRewardsQueryVariables = Types.Exact<{
|
export type EpochAssetsRewardsQueryVariables = Types.Exact<{
|
||||||
|
epochRewardSummariesFilter?: Types.InputMaybe<Types.RewardSummaryFilter>;
|
||||||
epochRewardSummariesPagination?: Types.InputMaybe<Types.Pagination>;
|
epochRewardSummariesPagination?: Types.InputMaybe<Types.Pagination>;
|
||||||
}>;
|
}>;
|
||||||
|
|
||||||
|
|
||||||
export type EpochAssetsRewardsQuery = { __typename?: 'Query', assetsConnection?: { __typename?: 'AssetsConnection', edges?: Array<{ __typename?: 'AssetEdge', node: { __typename?: 'Asset', id: string, name: string } } | null> | null } | null, epochRewardSummaries?: { __typename?: 'EpochRewardSummaryConnection', edges?: Array<{ __typename?: 'EpochRewardSummaryEdge', node: { __typename?: 'EpochRewardSummary', epoch: number, assetId: string, amount: string, rewardType: Types.AccountType } } | null> | null } | null, epoch: { __typename?: 'Epoch', timestamps: { __typename?: 'EpochTimestamps', expiry?: any | null } } };
|
export type EpochAssetsRewardsQuery = { __typename?: 'Query', assetsConnection?: { __typename?: 'AssetsConnection', edges?: Array<{ __typename?: 'AssetEdge', node: { __typename?: 'Asset', id: string, name: string } } | null> | null } | null, epochRewardSummaries?: { __typename?: 'EpochRewardSummaryConnection', edges?: Array<{ __typename?: 'EpochRewardSummaryEdge', node: { __typename?: 'EpochRewardSummary', epoch: number, assetId: string, amount: string, rewardType: Types.AccountType } } | null> | null } | null };
|
||||||
|
|
||||||
export type EpochFieldsFragment = { __typename?: 'Epoch', id: string, timestamps: { __typename?: 'EpochTimestamps', start?: any | null, end?: any | null, expiry?: any | null } };
|
export type EpochFieldsFragment = { __typename?: 'Epoch', id: string, timestamps: { __typename?: 'EpochTimestamps', start?: any | null, end?: any | null, expiry?: any | null } };
|
||||||
|
|
||||||
@@ -76,10 +79,14 @@ export const EpochFieldsFragmentDoc = gql`
|
|||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
export const RewardsDocument = gql`
|
export const RewardsDocument = gql`
|
||||||
query Rewards($partyId: ID!, $delegationsPagination: Pagination, $rewardsPagination: Pagination) {
|
query Rewards($partyId: ID!, $fromEpoch: Int, $toEpoch: Int, $rewardsPagination: Pagination, $delegationsPagination: Pagination) {
|
||||||
party(id: $partyId) {
|
party(id: $partyId) {
|
||||||
id
|
id
|
||||||
rewardsConnection(pagination: $rewardsPagination) {
|
rewardsConnection(
|
||||||
|
fromEpoch: $fromEpoch
|
||||||
|
toEpoch: $toEpoch
|
||||||
|
pagination: $rewardsPagination
|
||||||
|
) {
|
||||||
edges {
|
edges {
|
||||||
node {
|
node {
|
||||||
...RewardFields
|
...RewardFields
|
||||||
@@ -94,14 +101,6 @@ export const RewardsDocument = gql`
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
epoch {
|
|
||||||
id
|
|
||||||
timestamps {
|
|
||||||
start
|
|
||||||
end
|
|
||||||
expiry
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
${RewardFieldsFragmentDoc}
|
${RewardFieldsFragmentDoc}
|
||||||
${DelegationFieldsFragmentDoc}`;
|
${DelegationFieldsFragmentDoc}`;
|
||||||
@@ -119,8 +118,10 @@ ${DelegationFieldsFragmentDoc}`;
|
|||||||
* const { data, loading, error } = useRewardsQuery({
|
* const { data, loading, error } = useRewardsQuery({
|
||||||
* variables: {
|
* variables: {
|
||||||
* partyId: // value for 'partyId'
|
* partyId: // value for 'partyId'
|
||||||
* delegationsPagination: // value for 'delegationsPagination'
|
* fromEpoch: // value for 'fromEpoch'
|
||||||
|
* toEpoch: // value for 'toEpoch'
|
||||||
* rewardsPagination: // value for 'rewardsPagination'
|
* rewardsPagination: // value for 'rewardsPagination'
|
||||||
|
* delegationsPagination: // value for 'delegationsPagination'
|
||||||
* },
|
* },
|
||||||
* });
|
* });
|
||||||
*/
|
*/
|
||||||
@@ -136,7 +137,7 @@ export type RewardsQueryHookResult = ReturnType<typeof useRewardsQuery>;
|
|||||||
export type RewardsLazyQueryHookResult = ReturnType<typeof useRewardsLazyQuery>;
|
export type RewardsLazyQueryHookResult = ReturnType<typeof useRewardsLazyQuery>;
|
||||||
export type RewardsQueryResult = Apollo.QueryResult<RewardsQuery, RewardsQueryVariables>;
|
export type RewardsQueryResult = Apollo.QueryResult<RewardsQuery, RewardsQueryVariables>;
|
||||||
export const EpochAssetsRewardsDocument = gql`
|
export const EpochAssetsRewardsDocument = gql`
|
||||||
query EpochAssetsRewards($epochRewardSummariesPagination: Pagination) {
|
query EpochAssetsRewards($epochRewardSummariesFilter: RewardSummaryFilter, $epochRewardSummariesPagination: Pagination) {
|
||||||
assetsConnection {
|
assetsConnection {
|
||||||
edges {
|
edges {
|
||||||
node {
|
node {
|
||||||
@@ -145,18 +146,16 @@ export const EpochAssetsRewardsDocument = gql`
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
epochRewardSummaries(pagination: $epochRewardSummariesPagination) {
|
epochRewardSummaries(
|
||||||
|
filter: $epochRewardSummariesFilter
|
||||||
|
pagination: $epochRewardSummariesPagination
|
||||||
|
) {
|
||||||
edges {
|
edges {
|
||||||
node {
|
node {
|
||||||
...EpochRewardSummaryFields
|
...EpochRewardSummaryFields
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
epoch {
|
|
||||||
timestamps {
|
|
||||||
expiry
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
${EpochRewardSummaryFieldsFragmentDoc}`;
|
${EpochRewardSummaryFieldsFragmentDoc}`;
|
||||||
|
|
||||||
@@ -172,6 +171,7 @@ export const EpochAssetsRewardsDocument = gql`
|
|||||||
* @example
|
* @example
|
||||||
* const { data, loading, error } = useEpochAssetsRewardsQuery({
|
* const { data, loading, error } = useEpochAssetsRewardsQuery({
|
||||||
* variables: {
|
* variables: {
|
||||||
|
* epochRewardSummariesFilter: // value for 'epochRewardSummariesFilter'
|
||||||
* epochRewardSummariesPagination: // value for 'epochRewardSummariesPagination'
|
* epochRewardSummariesPagination: // value for 'epochRewardSummariesPagination'
|
||||||
* },
|
* },
|
||||||
* });
|
* });
|
||||||
|
|||||||
@@ -114,7 +114,7 @@ export const RewardsPage = () => {
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="w-[440px]">
|
<div className="w-[360px]">
|
||||||
<Toggle
|
<Toggle
|
||||||
name="epoch-reward-view-toggle"
|
name="epoch-reward-view-toggle"
|
||||||
toggles={[
|
toggles={[
|
||||||
@@ -136,11 +136,15 @@ export const RewardsPage = () => {
|
|||||||
</section>
|
</section>
|
||||||
|
|
||||||
{toggleRewardsView === 'total' ? (
|
{toggleRewardsView === 'total' ? (
|
||||||
<EpochTotalRewards />
|
epochData?.epoch ? (
|
||||||
|
<EpochTotalRewards currentEpoch={epochData?.epoch} />
|
||||||
|
) : null
|
||||||
) : (
|
) : (
|
||||||
<section>
|
<section>
|
||||||
{pubKey && pubKeys?.length ? (
|
{pubKey && pubKeys?.length ? (
|
||||||
<EpochIndividualRewards />
|
epochData?.epoch ? (
|
||||||
|
<EpochIndividualRewards currentEpoch={epochData?.epoch} />
|
||||||
|
) : null
|
||||||
) : (
|
) : (
|
||||||
<ConnectToSeeRewards />
|
<ConnectToSeeRewards />
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -155,16 +155,16 @@ export const ValidatorTables = ({
|
|||||||
return (
|
return (
|
||||||
<section data-testid="validator-tables">
|
<section data-testid="validator-tables">
|
||||||
<div className="grid w-full justify-end">
|
<div className="grid w-full justify-end">
|
||||||
<div className="w-[400px]">
|
<div className="w-[340px]">
|
||||||
<Toggle
|
<Toggle
|
||||||
name="validators-view-toggle"
|
name="validators-view-toggle"
|
||||||
toggles={[
|
toggles={[
|
||||||
{
|
{
|
||||||
label: t('ALL VALIDATORS'),
|
label: t('All validators'),
|
||||||
value: 'all',
|
value: 'all',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: t('STAKED BY ME'),
|
label: t('Staked by me'),
|
||||||
value: 'myStake',
|
value: 'myStake',
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,8 +1,10 @@
|
|||||||
const { defineConfig } = require('cypress');
|
const { defineConfig } = require('cypress');
|
||||||
|
|
||||||
module.exports = defineConfig({
|
module.exports = defineConfig({
|
||||||
|
reporter: '../../node_modules/cypress-mochawesome-reporter',
|
||||||
e2e: {
|
e2e: {
|
||||||
setupNodeEvents(on, config) {
|
setupNodeEvents(on, config) {
|
||||||
|
require('cypress-mochawesome-reporter/plugin')(on);
|
||||||
require('@cypress/grep/src/plugin')(config);
|
require('@cypress/grep/src/plugin')(config);
|
||||||
return config;
|
return config;
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -56,6 +56,15 @@ describe('deposit form validation', { tags: '@smoke' }, () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('test', () => {
|
||||||
|
cy.getByTestId('enter-pubkey-manually').click();
|
||||||
|
cy.get(toAddressField).clear().type('INVALID_DEPOSIT_TO_ADDRESS');
|
||||||
|
cy.get(`[data-testid="${formFieldError}"][aria-describedby="to"]`).should(
|
||||||
|
'have.text',
|
||||||
|
'Invalid Vega key'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
it('invalid amount', () => {
|
it('invalid amount', () => {
|
||||||
mockWeb3DepositCalls({
|
mockWeb3DepositCalls({
|
||||||
allowance: '1000',
|
allowance: '1000',
|
||||||
|
|||||||
@@ -41,6 +41,12 @@ describe('accounts', { tags: '@smoke' }, () => {
|
|||||||
.should('have.text', '100,001.01');
|
.should('have.text', '100,001.01');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('asset detail should be properly rendered', () => {
|
||||||
|
cy.getByTestId('Collateral').click();
|
||||||
|
cy.getByTestId('asset').contains('tEURO').click();
|
||||||
|
cy.get('[data-testid$="_label"]').should('have.length', 16);
|
||||||
|
});
|
||||||
|
|
||||||
describe('sorting by ag-grid columns should work well', () => {
|
describe('sorting by ag-grid columns should work well', () => {
|
||||||
it('sorting by asset', () => {
|
it('sorting by asset', () => {
|
||||||
cy.getByTestId('Collateral').click();
|
cy.getByTestId('Collateral').click();
|
||||||
|
|||||||
@@ -29,7 +29,10 @@ import { LiquidityContainer } from '../liquidity/liquidity';
|
|||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
import type { PinnedAsset } from '@vegaprotocol/accounts';
|
import type { PinnedAsset } from '@vegaprotocol/accounts';
|
||||||
import { useScreenDimensions } from '@vegaprotocol/react-helpers';
|
import { useScreenDimensions } from '@vegaprotocol/react-helpers';
|
||||||
import { useMarketClickHandler } from '../../lib/hooks/use-market-click-handler';
|
import {
|
||||||
|
useMarketClickHandler,
|
||||||
|
useMarketLiquidityClickHandler,
|
||||||
|
} from '../../lib/hooks/use-market-click-handler';
|
||||||
|
|
||||||
type MarketDependantView =
|
type MarketDependantView =
|
||||||
| typeof CandlesChartContainer
|
| typeof CandlesChartContainer
|
||||||
@@ -79,6 +82,7 @@ const MarketBottomPanel = memo(
|
|||||||
({ marketId, pinnedAsset }: BottomPanelProps) => {
|
({ marketId, pinnedAsset }: BottomPanelProps) => {
|
||||||
const { screenSize } = useScreenDimensions();
|
const { screenSize } = useScreenDimensions();
|
||||||
const onMarketClick = useMarketClickHandler(true);
|
const onMarketClick = useMarketClickHandler(true);
|
||||||
|
const onOrderTypeClick = useMarketLiquidityClickHandler(true);
|
||||||
|
|
||||||
return 'xxxl' === screenSize ? (
|
return 'xxxl' === screenSize ? (
|
||||||
<ResizableGrid proportionalLayout minSize={200}>
|
<ResizableGrid proportionalLayout minSize={200}>
|
||||||
@@ -94,6 +98,7 @@ const MarketBottomPanel = memo(
|
|||||||
<TradingViews.Orders
|
<TradingViews.Orders
|
||||||
marketId={marketId}
|
marketId={marketId}
|
||||||
onMarketClick={onMarketClick}
|
onMarketClick={onMarketClick}
|
||||||
|
onOrderTypeClick={onOrderTypeClick}
|
||||||
enforceBottomPlaceholder
|
enforceBottomPlaceholder
|
||||||
/>
|
/>
|
||||||
</VegaWalletContainer>
|
</VegaWalletContainer>
|
||||||
@@ -150,6 +155,7 @@ const MarketBottomPanel = memo(
|
|||||||
<TradingViews.Orders
|
<TradingViews.Orders
|
||||||
marketId={marketId}
|
marketId={marketId}
|
||||||
onMarketClick={onMarketClick}
|
onMarketClick={onMarketClick}
|
||||||
|
onOrderTypeClick={onOrderTypeClick}
|
||||||
enforceBottomPlaceholder
|
enforceBottomPlaceholder
|
||||||
/>
|
/>
|
||||||
</VegaWalletContainer>
|
</VegaWalletContainer>
|
||||||
@@ -293,6 +299,7 @@ interface TradePanelsProps {
|
|||||||
market: Market | null;
|
market: Market | null;
|
||||||
onSelect: (marketId: string, metaKey?: boolean) => void;
|
onSelect: (marketId: string, metaKey?: boolean) => void;
|
||||||
onMarketClick?: (marketId: string) => void;
|
onMarketClick?: (marketId: string) => void;
|
||||||
|
onOrderTypeClick?: (marketId: string) => void;
|
||||||
onClickCollateral: () => void;
|
onClickCollateral: () => void;
|
||||||
pinnedAsset?: PinnedAsset;
|
pinnedAsset?: PinnedAsset;
|
||||||
}
|
}
|
||||||
@@ -303,12 +310,16 @@ export const TradePanels = ({
|
|||||||
onClickCollateral,
|
onClickCollateral,
|
||||||
pinnedAsset,
|
pinnedAsset,
|
||||||
}: TradePanelsProps) => {
|
}: TradePanelsProps) => {
|
||||||
|
const onMarketClick = useMarketClickHandler(true);
|
||||||
|
const onOrderTypeClick = useMarketLiquidityClickHandler(true);
|
||||||
|
|
||||||
const [view, setView] = useState<TradingView>('Candles');
|
const [view, setView] = useState<TradingView>('Candles');
|
||||||
const renderView = () => {
|
const renderView = () => {
|
||||||
const Component = memo<{
|
const Component = memo<{
|
||||||
marketId: string;
|
marketId: string;
|
||||||
onSelect: (marketId: string, metaKey?: boolean) => void;
|
onSelect: (marketId: string, metaKey?: boolean) => void;
|
||||||
onMarketClick?: (marketId: string) => void;
|
onMarketClick?: (marketId: string) => void;
|
||||||
|
onOrderTypeClick?: (marketId: string) => void;
|
||||||
onClickCollateral: () => void;
|
onClickCollateral: () => void;
|
||||||
pinnedAsset?: PinnedAsset;
|
pinnedAsset?: PinnedAsset;
|
||||||
}>(TradingViews[view]);
|
}>(TradingViews[view]);
|
||||||
@@ -325,6 +336,8 @@ export const TradePanels = ({
|
|||||||
onSelect={onSelect}
|
onSelect={onSelect}
|
||||||
onClickCollateral={onClickCollateral}
|
onClickCollateral={onClickCollateral}
|
||||||
pinnedAsset={pinnedAsset}
|
pinnedAsset={pinnedAsset}
|
||||||
|
onMarketClick={onMarketClick}
|
||||||
|
onOrderTypeClick={onOrderTypeClick}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -19,7 +19,10 @@ import { usePageTitleStore } from '../../stores';
|
|||||||
import { LedgerContainer } from '@vegaprotocol/ledger';
|
import { LedgerContainer } from '@vegaprotocol/ledger';
|
||||||
import { AccountsContainer } from '../../components/accounts-container';
|
import { AccountsContainer } from '../../components/accounts-container';
|
||||||
import { AccountHistoryContainer } from './account-history-container';
|
import { AccountHistoryContainer } from './account-history-container';
|
||||||
import { useMarketClickHandler } from '../../lib/hooks/use-market-click-handler';
|
import {
|
||||||
|
useMarketClickHandler,
|
||||||
|
useMarketLiquidityClickHandler,
|
||||||
|
} from '../../lib/hooks/use-market-click-handler';
|
||||||
|
|
||||||
export const Portfolio = () => {
|
export const Portfolio = () => {
|
||||||
const { updateTitle } = usePageTitleStore((store) => ({
|
const { updateTitle } = usePageTitleStore((store) => ({
|
||||||
@@ -31,6 +34,7 @@ export const Portfolio = () => {
|
|||||||
}, [updateTitle]);
|
}, [updateTitle]);
|
||||||
|
|
||||||
const onMarketClick = useMarketClickHandler(true);
|
const onMarketClick = useMarketClickHandler(true);
|
||||||
|
const onOrderTypeClick = useMarketLiquidityClickHandler(true);
|
||||||
|
|
||||||
const wrapperClasses = 'h-full max-h-full flex flex-col';
|
const wrapperClasses = 'h-full max-h-full flex flex-col';
|
||||||
return (
|
return (
|
||||||
@@ -54,7 +58,10 @@ export const Portfolio = () => {
|
|||||||
</Tab>
|
</Tab>
|
||||||
<Tab id="orders" name={t('Orders')}>
|
<Tab id="orders" name={t('Orders')}>
|
||||||
<VegaWalletContainer>
|
<VegaWalletContainer>
|
||||||
<OrderListContainer onMarketClick={onMarketClick} />
|
<OrderListContainer
|
||||||
|
onMarketClick={onMarketClick}
|
||||||
|
onOrderTypeClick={onOrderTypeClick}
|
||||||
|
/>
|
||||||
</VegaWalletContainer>
|
</VegaWalletContainer>
|
||||||
</Tab>
|
</Tab>
|
||||||
<Tab id="fills" name={t('Fills')}>
|
<Tab id="fills" name={t('Fills')}>
|
||||||
|
|||||||
@@ -19,3 +19,21 @@ export const useMarketClickHandler = (replace = false) => {
|
|||||||
[navigate, marketId, replace, isMarketPage]
|
[navigate, marketId, replace, isMarketPage]
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const useMarketLiquidityClickHandler = (replace = false) => {
|
||||||
|
const navigate = useNavigate();
|
||||||
|
const { marketId } = useParams();
|
||||||
|
const { pathname } = useLocation();
|
||||||
|
const isLiquidityPage = pathname.match(/^\/liquidity\/(.+)/);
|
||||||
|
return useCallback(
|
||||||
|
(selectedId: string, metaKey?: boolean) => {
|
||||||
|
const link = Links[Routes.LIQUIDITY](selectedId);
|
||||||
|
if (metaKey) {
|
||||||
|
window.open(`/#${link}`, '_blank');
|
||||||
|
} else if (selectedId !== marketId || !isLiquidityPage) {
|
||||||
|
navigate(link, { replace });
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[navigate, marketId, replace, isLiquidityPage]
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|||||||
@@ -7,7 +7,11 @@ import {
|
|||||||
} from '@vegaprotocol/types';
|
} from '@vegaprotocol/types';
|
||||||
import type { VegaStoredTxState } from '@vegaprotocol/wallet';
|
import type { VegaStoredTxState } from '@vegaprotocol/wallet';
|
||||||
import { VegaTxStatus } from '@vegaprotocol/wallet';
|
import { VegaTxStatus } from '@vegaprotocol/wallet';
|
||||||
import { VegaTransactionDetails } from './use-vega-transaction-toasts';
|
import {
|
||||||
|
VegaTransactionDetails,
|
||||||
|
getVegaTransactionContentIntent,
|
||||||
|
} from './use-vega-transaction-toasts';
|
||||||
|
import { Intent } from '@vegaprotocol/ui-toolkit';
|
||||||
|
|
||||||
jest.mock('@vegaprotocol/assets', () => {
|
jest.mock('@vegaprotocol/assets', () => {
|
||||||
const A1 = {
|
const A1 = {
|
||||||
@@ -278,3 +282,27 @@ describe('VegaTransactionDetails', () => {
|
|||||||
expect(queryByTestId('toast-panel')?.textContent).toEqual(details);
|
expect(queryByTestId('toast-panel')?.textContent).toEqual(details);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('getVegaTransactionContentIntent', () => {
|
||||||
|
it('returns the correct intent for a transaction', () => {
|
||||||
|
expect(getVegaTransactionContentIntent(withdraw).intent).toBe(
|
||||||
|
Intent.Primary
|
||||||
|
);
|
||||||
|
expect(getVegaTransactionContentIntent(submitOrder).intent).toBe(
|
||||||
|
Intent.Success
|
||||||
|
);
|
||||||
|
expect(getVegaTransactionContentIntent(editOrder).intent).toBe(
|
||||||
|
Intent.Primary
|
||||||
|
);
|
||||||
|
expect(getVegaTransactionContentIntent(cancelOrder).intent).toBe(
|
||||||
|
Intent.Primary
|
||||||
|
);
|
||||||
|
expect(getVegaTransactionContentIntent(cancelAll).intent).toBe(
|
||||||
|
Intent.Primary
|
||||||
|
);
|
||||||
|
expect(getVegaTransactionContentIntent(closePosition).intent).toBe(
|
||||||
|
Intent.Primary
|
||||||
|
);
|
||||||
|
expect(getVegaTransactionContentIntent(batch).intent).toBe(Intent.Primary);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|||||||
@@ -547,7 +547,11 @@ const VegaTxCompleteToastsContent = ({ tx }: VegaTxToastContentProps) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<ToastHeading>{t('Confirmed')}</ToastHeading>
|
<ToastHeading>
|
||||||
|
{tx.order?.status
|
||||||
|
? getOrderToastTitle(tx.order.status)
|
||||||
|
: t('Confirmed')}
|
||||||
|
</ToastHeading>
|
||||||
<p>{t('Your transaction has been confirmed ')}</p>
|
<p>{t('Your transaction has been confirmed ')}</p>
|
||||||
{tx.txHash && (
|
{tx.txHash && (
|
||||||
<p className="break-all">
|
<p className="break-all">
|
||||||
@@ -634,25 +638,8 @@ export const useVegaTransactionToasts = () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const fromVegaTransaction = (tx: VegaStoredTxState): Toast => {
|
const fromVegaTransaction = (tx: VegaStoredTxState): Toast => {
|
||||||
let content: ToastContent;
|
|
||||||
const closeAfter = isFinal(tx) ? CLOSE_AFTER : undefined;
|
const closeAfter = isFinal(tx) ? CLOSE_AFTER : undefined;
|
||||||
if (tx.status === VegaTxStatus.Requested) {
|
const { intent, content } = getVegaTransactionContentIntent(tx);
|
||||||
content = <VegaTxRequestedToastContent tx={tx} />;
|
|
||||||
}
|
|
||||||
if (tx.status === VegaTxStatus.Pending) {
|
|
||||||
content = <VegaTxPendingToastContentProps tx={tx} />;
|
|
||||||
}
|
|
||||||
if (tx.status === VegaTxStatus.Complete) {
|
|
||||||
content = <VegaTxCompleteToastsContent tx={tx} />;
|
|
||||||
}
|
|
||||||
if (tx.status === VegaTxStatus.Error) {
|
|
||||||
content = <VegaTxErrorToastContent tx={tx} />;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Transaction can be successful but the order can be rejected by the network
|
|
||||||
const intent =
|
|
||||||
(tx.order && getOrderToastIntent(tx.order.status)) ||
|
|
||||||
intentMap[tx.status];
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
id: `vega-${tx.id}`,
|
id: `vega-${tx.id}`,
|
||||||
@@ -676,3 +663,27 @@ export const useVegaTransactionToasts = () => {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const getVegaTransactionContentIntent = (tx: VegaStoredTxState) => {
|
||||||
|
let content: ToastContent;
|
||||||
|
if (tx.status === VegaTxStatus.Requested) {
|
||||||
|
content = <VegaTxRequestedToastContent tx={tx} />;
|
||||||
|
}
|
||||||
|
if (tx.status === VegaTxStatus.Pending) {
|
||||||
|
content = <VegaTxPendingToastContentProps tx={tx} />;
|
||||||
|
}
|
||||||
|
if (tx.status === VegaTxStatus.Complete) {
|
||||||
|
content = <VegaTxCompleteToastsContent tx={tx} />;
|
||||||
|
}
|
||||||
|
if (tx.status === VegaTxStatus.Error) {
|
||||||
|
content = <VegaTxErrorToastContent tx={tx} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Transaction can be successful but the order can be rejected by the network
|
||||||
|
const intent =
|
||||||
|
(tx.order &&
|
||||||
|
!isOrderAmendmentTransaction(tx.body) &&
|
||||||
|
getOrderToastIntent(tx.order.status)) ||
|
||||||
|
intentMap[tx.status];
|
||||||
|
return { intent, content };
|
||||||
|
};
|
||||||
|
|||||||
@@ -33,6 +33,8 @@ import { ViewingBanner } from '../components/viewing-banner';
|
|||||||
import { Banner } from '../components/banner';
|
import { Banner } from '../components/banner';
|
||||||
import { AppLoader, DynamicLoader } from '../components/app-loader';
|
import { AppLoader, DynamicLoader } from '../components/app-loader';
|
||||||
import { Navbar } from '../components/navbar';
|
import { Navbar } from '../components/navbar';
|
||||||
|
import { useDataProvider } from '@vegaprotocol/react-helpers';
|
||||||
|
import { activeOrdersProvider } from '@vegaprotocol/orders';
|
||||||
|
|
||||||
const DEFAULT_TITLE = t('Welcome to Vega trading!');
|
const DEFAULT_TITLE = t('Welcome to Vega trading!');
|
||||||
|
|
||||||
@@ -95,6 +97,7 @@ function AppBody({ Component }: AppProps) {
|
|||||||
<ToastsManager />
|
<ToastsManager />
|
||||||
<InitializeHandlers />
|
<InitializeHandlers />
|
||||||
<MaybeConnectEagerly />
|
<MaybeConnectEagerly />
|
||||||
|
<PartyData />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -127,6 +130,18 @@ function VegaTradingApp(props: AppProps) {
|
|||||||
|
|
||||||
export default VegaTradingApp;
|
export default VegaTradingApp;
|
||||||
|
|
||||||
|
const PartyData = () => {
|
||||||
|
const { pubKey } = useVegaWallet();
|
||||||
|
const variables = { partyId: pubKey || '' };
|
||||||
|
const skip = !pubKey;
|
||||||
|
useDataProvider({
|
||||||
|
dataProvider: activeOrdersProvider,
|
||||||
|
variables,
|
||||||
|
skip,
|
||||||
|
});
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
const MaybeConnectEagerly = () => {
|
const MaybeConnectEagerly = () => {
|
||||||
useVegaEagerConnect(Connectors);
|
useVegaEagerConnect(Connectors);
|
||||||
useEthereumEagerConnect();
|
useEthereumEagerConnect();
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ export enum Routes {
|
|||||||
MARKET = '/markets',
|
MARKET = '/markets',
|
||||||
MARKETS = '/markets/all',
|
MARKETS = '/markets/all',
|
||||||
PORTFOLIO = '/portfolio',
|
PORTFOLIO = '/portfolio',
|
||||||
LIQUIDITY = 'liquidity/:marketId',
|
LIQUIDITY = '/liquidity',
|
||||||
}
|
}
|
||||||
|
|
||||||
type ConsoleLinks = { [r in Routes]: (...args: string[]) => string };
|
type ConsoleLinks = { [r in Routes]: (...args: string[]) => string };
|
||||||
@@ -41,8 +41,10 @@ export const Links: ConsoleLinks = {
|
|||||||
marketId ? trimEnd(`${Routes.MARKET}/${marketId}`, '/') : Routes.MARKET,
|
marketId ? trimEnd(`${Routes.MARKET}/${marketId}`, '/') : Routes.MARKET,
|
||||||
[Routes.MARKETS]: () => Routes.MARKETS,
|
[Routes.MARKETS]: () => Routes.MARKETS,
|
||||||
[Routes.PORTFOLIO]: () => Routes.PORTFOLIO,
|
[Routes.PORTFOLIO]: () => Routes.PORTFOLIO,
|
||||||
[Routes.LIQUIDITY]: (marketId: string) =>
|
[Routes.LIQUIDITY]: (marketId: string | null | undefined) =>
|
||||||
Routes.LIQUIDITY.replace(':marketId', marketId),
|
marketId
|
||||||
|
? trimEnd(`${Routes.LIQUIDITY}/${marketId}`, '/')
|
||||||
|
: Routes.LIQUIDITY,
|
||||||
};
|
};
|
||||||
|
|
||||||
const routerConfig: RouteObject[] = [
|
const routerConfig: RouteObject[] = [
|
||||||
@@ -70,6 +72,16 @@ const routerConfig: RouteObject[] = [
|
|||||||
{
|
{
|
||||||
path: Routes.LIQUIDITY,
|
path: Routes.LIQUIDITY,
|
||||||
element: <LazyLiquidity />,
|
element: <LazyLiquidity />,
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
index: true,
|
||||||
|
element: <LazyLiquidity />,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: ':marketId',
|
||||||
|
element: <LazyLiquidity />,
|
||||||
|
},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: Routes.PORTFOLIO,
|
path: Routes.PORTFOLIO,
|
||||||
|
|||||||
@@ -0,0 +1,6 @@
|
|||||||
|
FROM --platform=amd64 nginx:1.23-alpine@sha256:6318314189b40e73145a48060bff4783a116c34cc7241532d0d94198fb2c9629
|
||||||
|
EXPOSE 80
|
||||||
|
WORKDIR /usr/share/nginx/html
|
||||||
|
COPY docker/nginx.conf /etc/nginx/conf.d/default.conf
|
||||||
|
RUN rm -rf /usr/share/nginx/html/*
|
||||||
|
COPY ./dist-result/ /usr/share/nginx/html/
|
||||||
@@ -5,7 +5,9 @@ export PATH="/app/node_modules/.bin:$PATH"
|
|||||||
flags="--network-timeout 100000 --pure-lockfile"
|
flags="--network-timeout 100000 --pure-lockfile"
|
||||||
|
|
||||||
if [[ ! -z "${ENV_NAME}" ]]; then
|
if [[ ! -z "${ENV_NAME}" ]]; then
|
||||||
flags="--env=${ENV_NAME} $flags"
|
if [[ "${ENV_NAME}" != "ops-vega" ]]; then
|
||||||
|
flags="--env=${ENV_NAME} $flags"
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ "${APP}" = "trading" ]; then
|
if [ "${APP}" = "trading" ]; then
|
||||||
@@ -16,7 +18,3 @@ if [ "${APP}" = "trading" ]; then
|
|||||||
else
|
else
|
||||||
yarn nx build ${APP} $flags
|
yarn nx build ${APP} $flags
|
||||||
fi
|
fi
|
||||||
|
|
||||||
env_vars_file="/app/dist/apps/${APP}/.env"
|
|
||||||
# make sure there are no exposed .env files
|
|
||||||
rm $env_vars_file || echo "No env vars file"
|
|
||||||
@@ -13,7 +13,7 @@ RUN apk add --update --no-cache \
|
|||||||
COPY . ./
|
COPY . ./
|
||||||
RUN yarn --network-timeout 100000 --pure-lockfile
|
RUN yarn --network-timeout 100000 --pure-lockfile
|
||||||
# work around for different build process in trading
|
# work around for different build process in trading
|
||||||
RUN sh ./docker-build.sh
|
RUN sh docker/docker-build.sh
|
||||||
|
|
||||||
# Server environment
|
# Server environment
|
||||||
# if this fails you need to docker pull nginx:1.23-alpine and pin new SHA
|
# if this fails you need to docker pull nginx:1.23-alpine and pin new SHA
|
||||||
@@ -23,6 +23,7 @@ FROM --platform=amd64 nginx:1.23-alpine@sha256:6318314189b40e73145a48060bff4783a
|
|||||||
EXPOSE 80
|
EXPOSE 80
|
||||||
# Copy dist
|
# Copy dist
|
||||||
WORKDIR /usr/share/nginx/html
|
WORKDIR /usr/share/nginx/html
|
||||||
COPY nginx/nginx.conf /etc/nginx/conf.d/default.conf
|
COPY docker/nginx.conf /etc/nginx/conf.d/default.conf
|
||||||
COPY --from=build /app/dist/apps/${APP} /usr/share/nginx/html
|
RUN rm -rf /usr/share/nginx/html/*
|
||||||
RUN apk add --no-cache go-ipfs; ipfs init && echo "$(ipfs add -rQ .)" > ipfs-hash; apk del go-ipfs
|
COPY --from=build /app/dist/apps/${APP}/* /usr/share/nginx/html
|
||||||
|
RUN apk add --no-cache go-ipfs; ipfs init && echo "$(ipfs add -rQ .)" > /ipfs-hash; apk del go-ipfs
|
||||||
@@ -1,8 +1,25 @@
|
|||||||
|
fragment AssetListFields on Asset {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
symbol
|
||||||
|
decimals
|
||||||
|
quantum
|
||||||
|
source {
|
||||||
|
__typename
|
||||||
|
... on ERC20 {
|
||||||
|
contractAddress
|
||||||
|
lifetimeLimit
|
||||||
|
withdrawThreshold
|
||||||
|
}
|
||||||
|
}
|
||||||
|
status
|
||||||
|
}
|
||||||
|
|
||||||
query Assets {
|
query Assets {
|
||||||
assetsConnection {
|
assetsConnection {
|
||||||
edges {
|
edges {
|
||||||
node {
|
node {
|
||||||
...AssetFields
|
...AssetListFields
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+23
-5
@@ -1,26 +1,44 @@
|
|||||||
import * as Types from '@vegaprotocol/types';
|
import * as Types from '@vegaprotocol/types';
|
||||||
|
|
||||||
import { gql } from '@apollo/client';
|
import { gql } from '@apollo/client';
|
||||||
import { AssetFieldsFragmentDoc } from './Asset';
|
|
||||||
import * as Apollo from '@apollo/client';
|
import * as Apollo from '@apollo/client';
|
||||||
const defaultOptions = {} as const;
|
const defaultOptions = {} as const;
|
||||||
|
export type AssetListFieldsFragment = { __typename?: 'Asset', id: string, name: string, symbol: string, decimals: number, quantum: string, status: Types.AssetStatus, source: { __typename: 'BuiltinAsset' } | { __typename: 'ERC20', contractAddress: string, lifetimeLimit: string, withdrawThreshold: string } };
|
||||||
|
|
||||||
export type AssetsQueryVariables = Types.Exact<{ [key: string]: never; }>;
|
export type AssetsQueryVariables = Types.Exact<{ [key: string]: never; }>;
|
||||||
|
|
||||||
|
|
||||||
export type AssetsQuery = { __typename?: 'Query', assetsConnection?: { __typename?: 'AssetsConnection', edges?: Array<{ __typename?: 'AssetEdge', node: { __typename?: 'Asset', id: string, name: string, symbol: string, decimals: number, quantum: string, status: Types.AssetStatus, source: { __typename: 'BuiltinAsset', maxFaucetAmountMint: string } | { __typename: 'ERC20', contractAddress: string, lifetimeLimit: string, withdrawThreshold: string }, infrastructureFeeAccount?: { __typename?: 'AccountBalance', balance: string } | null, globalRewardPoolAccount?: { __typename?: 'AccountBalance', balance: string } | null, takerFeeRewardAccount?: { __typename?: 'AccountBalance', balance: string } | null, makerFeeRewardAccount?: { __typename?: 'AccountBalance', balance: string } | null, lpFeeRewardAccount?: { __typename?: 'AccountBalance', balance: string } | null, marketProposerRewardAccount?: { __typename?: 'AccountBalance', balance: string } | null } } | null> | null } | null };
|
export type AssetsQuery = { __typename?: 'Query', assetsConnection?: { __typename?: 'AssetsConnection', edges?: Array<{ __typename?: 'AssetEdge', node: { __typename?: 'Asset', id: string, name: string, symbol: string, decimals: number, quantum: string, status: Types.AssetStatus, source: { __typename: 'BuiltinAsset' } | { __typename: 'ERC20', contractAddress: string, lifetimeLimit: string, withdrawThreshold: string } } } | null> | null } | null };
|
||||||
|
|
||||||
|
|
||||||
|
export const AssetListFieldsFragmentDoc = gql`
|
||||||
|
fragment AssetListFields on Asset {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
symbol
|
||||||
|
decimals
|
||||||
|
quantum
|
||||||
|
source {
|
||||||
|
__typename
|
||||||
|
... on ERC20 {
|
||||||
|
contractAddress
|
||||||
|
lifetimeLimit
|
||||||
|
withdrawThreshold
|
||||||
|
}
|
||||||
|
}
|
||||||
|
status
|
||||||
|
}
|
||||||
|
`;
|
||||||
export const AssetsDocument = gql`
|
export const AssetsDocument = gql`
|
||||||
query Assets {
|
query Assets {
|
||||||
assetsConnection {
|
assetsConnection {
|
||||||
edges {
|
edges {
|
||||||
node {
|
node {
|
||||||
...AssetFields
|
...AssetListFields
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
${AssetFieldsFragmentDoc}`;
|
${AssetListFieldsFragmentDoc}`;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* __useAssetsQuery__
|
* __useAssetsQuery__
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { render, screen } from '@testing-library/react';
|
|||||||
import * as Schema from '@vegaprotocol/types';
|
import * as Schema from '@vegaprotocol/types';
|
||||||
import { AssetDetailsDialog } from './asset-details-dialog';
|
import { AssetDetailsDialog } from './asset-details-dialog';
|
||||||
import { AssetDetail, testId } from './asset-details-table';
|
import { AssetDetail, testId } from './asset-details-table';
|
||||||
import { AssetsDocument } from './__generated__/Assets';
|
import { AssetDocument } from './__generated__/Asset';
|
||||||
import { generateBuiltinAsset, generateERC20Asset } from './test-helpers';
|
import { generateBuiltinAsset, generateERC20Asset } from './test-helpers';
|
||||||
|
|
||||||
const mockedData = {
|
const mockedData = {
|
||||||
@@ -39,15 +39,17 @@ const mockedData = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const mocks = [
|
const mocks = mockedData.data.assetsConnection.edges.map((mock) => ({
|
||||||
{
|
request: {
|
||||||
request: {
|
query: AssetDocument,
|
||||||
query: AssetsDocument,
|
variables: { assetId: mock.node.id },
|
||||||
variables: {},
|
|
||||||
},
|
|
||||||
result: mockedData,
|
|
||||||
},
|
},
|
||||||
];
|
result: {
|
||||||
|
data: {
|
||||||
|
assetsConnection: { edges: [mock] },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
const WrappedAssetDetailsDialog = ({ assetId }: { assetId: string }) => (
|
const WrappedAssetDetailsDialog = ({ assetId }: { assetId: string }) => (
|
||||||
<MockedProvider mocks={mocks}>
|
<MockedProvider mocks={mocks}>
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import { t } from '@vegaprotocol/i18n';
|
import { t } from '@vegaprotocol/i18n';
|
||||||
import { useAssetsDataProvider } from './assets-data-provider';
|
|
||||||
import {
|
import {
|
||||||
Button,
|
Button,
|
||||||
Dialog,
|
Dialog,
|
||||||
@@ -10,6 +9,7 @@ import {
|
|||||||
import { create } from 'zustand';
|
import { create } from 'zustand';
|
||||||
import { AssetDetailsTable } from './asset-details-table';
|
import { AssetDetailsTable } from './asset-details-table';
|
||||||
import { AssetProposalNotification } from '@vegaprotocol/proposals';
|
import { AssetProposalNotification } from '@vegaprotocol/proposals';
|
||||||
|
import { useAssetDataProvider } from './asset-data-provider';
|
||||||
|
|
||||||
export type AssetDetailsDialogStore = {
|
export type AssetDetailsDialogStore = {
|
||||||
isOpen: boolean;
|
isOpen: boolean;
|
||||||
@@ -55,9 +55,8 @@ export const AssetDetailsDialog = ({
|
|||||||
onChange,
|
onChange,
|
||||||
asJson = false,
|
asJson = false,
|
||||||
}: AssetDetailsDialogProps) => {
|
}: AssetDetailsDialogProps) => {
|
||||||
const { data } = useAssetsDataProvider();
|
const { data: asset } = useAssetDataProvider(assetId);
|
||||||
|
|
||||||
const asset = data?.find((a) => a.id === assetId);
|
|
||||||
const assetSymbol = asset?.symbol || '';
|
const assetSymbol = asset?.symbol || '';
|
||||||
|
|
||||||
const content = asset ? (
|
const content = asset ? (
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import {
|
|||||||
} from '@vegaprotocol/ui-toolkit';
|
} from '@vegaprotocol/ui-toolkit';
|
||||||
import type { ReactNode } from 'react';
|
import type { ReactNode } from 'react';
|
||||||
import type { Asset } from './asset-data-provider';
|
import type { Asset } from './asset-data-provider';
|
||||||
|
import { WITHDRAW_THRESHOLD_TOOLTIP_TEXT } from './constants';
|
||||||
|
|
||||||
type Rows = {
|
type Rows = {
|
||||||
key: AssetDetail;
|
key: AssetDetail;
|
||||||
@@ -121,9 +122,7 @@ export const rows: Rows = [
|
|||||||
{
|
{
|
||||||
key: AssetDetail.WITHDRAWAL_THRESHOLD,
|
key: AssetDetail.WITHDRAWAL_THRESHOLD,
|
||||||
label: t('Withdrawal threshold'),
|
label: t('Withdrawal threshold'),
|
||||||
tooltip: t(
|
tooltip: WITHDRAW_THRESHOLD_TOOLTIP_TEXT,
|
||||||
'The maximum you can withdraw instantly. There’s no limit on the size of a withdrawal, but all withdrawals over the threshold will have a delay time added to them'
|
|
||||||
),
|
|
||||||
value: (asset) =>
|
value: (asset) =>
|
||||||
num(asset, (asset.source as Schema.ERC20).withdrawThreshold),
|
num(asset, (asset.source as Schema.ERC20).withdrawThreshold),
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
import merge from 'lodash/merge';
|
import merge from 'lodash/merge';
|
||||||
import type { AssetsQuery } from './__generated__/Assets';
|
import type {
|
||||||
|
AssetsQuery,
|
||||||
|
AssetListFieldsFragment,
|
||||||
|
} from './__generated__/Assets';
|
||||||
import * as Types from '@vegaprotocol/types';
|
import * as Types from '@vegaprotocol/types';
|
||||||
import type { PartialDeep } from 'type-fest';
|
import type { PartialDeep } from 'type-fest';
|
||||||
import type { AssetFieldsFragment } from './__generated__/Asset';
|
|
||||||
|
|
||||||
export const assetsQuery = (
|
export const assetsQuery = (
|
||||||
override?: PartialDeep<AssetsQuery>
|
override?: PartialDeep<AssetsQuery>
|
||||||
@@ -18,7 +20,7 @@ export const assetsQuery = (
|
|||||||
return merge(defaultAssets, override);
|
return merge(defaultAssets, override);
|
||||||
};
|
};
|
||||||
|
|
||||||
const assetFields: AssetFieldsFragment[] = [
|
const assetFields: AssetListFieldsFragment[] = [
|
||||||
{
|
{
|
||||||
__typename: 'Asset',
|
__typename: 'Asset',
|
||||||
id: 'asset-id',
|
id: 'asset-id',
|
||||||
@@ -33,30 +35,6 @@ const assetFields: AssetFieldsFragment[] = [
|
|||||||
},
|
},
|
||||||
quantum: '1',
|
quantum: '1',
|
||||||
status: Types.AssetStatus.STATUS_ENABLED,
|
status: Types.AssetStatus.STATUS_ENABLED,
|
||||||
infrastructureFeeAccount: {
|
|
||||||
balance: '1',
|
|
||||||
__typename: 'AccountBalance',
|
|
||||||
},
|
|
||||||
globalRewardPoolAccount: {
|
|
||||||
balance: '2',
|
|
||||||
__typename: 'AccountBalance',
|
|
||||||
},
|
|
||||||
takerFeeRewardAccount: {
|
|
||||||
balance: '3',
|
|
||||||
__typename: 'AccountBalance',
|
|
||||||
},
|
|
||||||
makerFeeRewardAccount: {
|
|
||||||
balance: '4',
|
|
||||||
__typename: 'AccountBalance',
|
|
||||||
},
|
|
||||||
lpFeeRewardAccount: {
|
|
||||||
balance: '5',
|
|
||||||
__typename: 'AccountBalance',
|
|
||||||
},
|
|
||||||
marketProposerRewardAccount: {
|
|
||||||
balance: '6',
|
|
||||||
__typename: 'AccountBalance',
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
__typename: 'Asset',
|
__typename: 'Asset',
|
||||||
@@ -72,30 +50,6 @@ const assetFields: AssetFieldsFragment[] = [
|
|||||||
},
|
},
|
||||||
quantum: '1',
|
quantum: '1',
|
||||||
status: Types.AssetStatus.STATUS_ENABLED,
|
status: Types.AssetStatus.STATUS_ENABLED,
|
||||||
infrastructureFeeAccount: {
|
|
||||||
balance: '1',
|
|
||||||
__typename: 'AccountBalance',
|
|
||||||
},
|
|
||||||
globalRewardPoolAccount: {
|
|
||||||
balance: '2',
|
|
||||||
__typename: 'AccountBalance',
|
|
||||||
},
|
|
||||||
takerFeeRewardAccount: {
|
|
||||||
balance: '3',
|
|
||||||
__typename: 'AccountBalance',
|
|
||||||
},
|
|
||||||
makerFeeRewardAccount: {
|
|
||||||
balance: '4',
|
|
||||||
__typename: 'AccountBalance',
|
|
||||||
},
|
|
||||||
lpFeeRewardAccount: {
|
|
||||||
balance: '5',
|
|
||||||
__typename: 'AccountBalance',
|
|
||||||
},
|
|
||||||
marketProposerRewardAccount: {
|
|
||||||
balance: '6',
|
|
||||||
__typename: 'AccountBalance',
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
__typename: 'Asset',
|
__typename: 'Asset',
|
||||||
@@ -104,20 +58,10 @@ const assetFields: AssetFieldsFragment[] = [
|
|||||||
decimals: 5,
|
decimals: 5,
|
||||||
name: 'Asto',
|
name: 'Asto',
|
||||||
source: {
|
source: {
|
||||||
maxFaucetAmountMint: '5000000000',
|
|
||||||
__typename: 'BuiltinAsset',
|
__typename: 'BuiltinAsset',
|
||||||
},
|
},
|
||||||
quantum: '1',
|
quantum: '1',
|
||||||
status: Types.AssetStatus.STATUS_ENABLED,
|
status: Types.AssetStatus.STATUS_ENABLED,
|
||||||
infrastructureFeeAccount: {
|
|
||||||
balance: '0',
|
|
||||||
__typename: 'AccountBalance',
|
|
||||||
},
|
|
||||||
globalRewardPoolAccount: null,
|
|
||||||
takerFeeRewardAccount: null,
|
|
||||||
makerFeeRewardAccount: null,
|
|
||||||
lpFeeRewardAccount: null,
|
|
||||||
marketProposerRewardAccount: null,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
__typename: 'Asset',
|
__typename: 'Asset',
|
||||||
@@ -126,20 +70,10 @@ const assetFields: AssetFieldsFragment[] = [
|
|||||||
decimals: 5,
|
decimals: 5,
|
||||||
name: 'tBTC TEST',
|
name: 'tBTC TEST',
|
||||||
source: {
|
source: {
|
||||||
maxFaucetAmountMint: '5000000000',
|
|
||||||
__typename: 'BuiltinAsset',
|
__typename: 'BuiltinAsset',
|
||||||
},
|
},
|
||||||
quantum: '1',
|
quantum: '1',
|
||||||
status: Types.AssetStatus.STATUS_ENABLED,
|
status: Types.AssetStatus.STATUS_ENABLED,
|
||||||
infrastructureFeeAccount: {
|
|
||||||
balance: '0',
|
|
||||||
__typename: 'AccountBalance',
|
|
||||||
},
|
|
||||||
globalRewardPoolAccount: null,
|
|
||||||
takerFeeRewardAccount: null,
|
|
||||||
makerFeeRewardAccount: null,
|
|
||||||
lpFeeRewardAccount: null,
|
|
||||||
marketProposerRewardAccount: null,
|
|
||||||
},
|
},
|
||||||
// NOTE: These assets ids and contract addresses are real assets on Sepolia, this is needed
|
// NOTE: These assets ids and contract addresses are real assets on Sepolia, this is needed
|
||||||
// because we don't currently mock our seplia infura provider. If we change network these will
|
// because we don't currently mock our seplia infura provider. If we change network these will
|
||||||
@@ -158,30 +92,6 @@ const assetFields: AssetFieldsFragment[] = [
|
|||||||
__typename: 'ERC20',
|
__typename: 'ERC20',
|
||||||
},
|
},
|
||||||
quantum: '1',
|
quantum: '1',
|
||||||
infrastructureFeeAccount: {
|
|
||||||
balance: '1',
|
|
||||||
__typename: 'AccountBalance',
|
|
||||||
},
|
|
||||||
globalRewardPoolAccount: {
|
|
||||||
balance: '2',
|
|
||||||
__typename: 'AccountBalance',
|
|
||||||
},
|
|
||||||
takerFeeRewardAccount: {
|
|
||||||
balance: '3',
|
|
||||||
__typename: 'AccountBalance',
|
|
||||||
},
|
|
||||||
makerFeeRewardAccount: {
|
|
||||||
balance: '4',
|
|
||||||
__typename: 'AccountBalance',
|
|
||||||
},
|
|
||||||
lpFeeRewardAccount: {
|
|
||||||
balance: '5',
|
|
||||||
__typename: 'AccountBalance',
|
|
||||||
},
|
|
||||||
marketProposerRewardAccount: {
|
|
||||||
balance: '6',
|
|
||||||
__typename: 'AccountBalance',
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
__typename: 'Asset',
|
__typename: 'Asset',
|
||||||
@@ -197,29 +107,5 @@ const assetFields: AssetFieldsFragment[] = [
|
|||||||
__typename: 'ERC20',
|
__typename: 'ERC20',
|
||||||
},
|
},
|
||||||
quantum: '1',
|
quantum: '1',
|
||||||
infrastructureFeeAccount: {
|
|
||||||
balance: '1',
|
|
||||||
__typename: 'AccountBalance',
|
|
||||||
},
|
|
||||||
globalRewardPoolAccount: {
|
|
||||||
balance: '2',
|
|
||||||
__typename: 'AccountBalance',
|
|
||||||
},
|
|
||||||
takerFeeRewardAccount: {
|
|
||||||
balance: '3',
|
|
||||||
__typename: 'AccountBalance',
|
|
||||||
},
|
|
||||||
makerFeeRewardAccount: {
|
|
||||||
balance: '4',
|
|
||||||
__typename: 'AccountBalance',
|
|
||||||
},
|
|
||||||
lpFeeRewardAccount: {
|
|
||||||
balance: '5',
|
|
||||||
__typename: 'AccountBalance',
|
|
||||||
},
|
|
||||||
marketProposerRewardAccount: {
|
|
||||||
balance: '6',
|
|
||||||
__typename: 'AccountBalance',
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
import { t } from '@vegaprotocol/i18n';
|
||||||
|
export const WITHDRAW_THRESHOLD_TOOLTIP_TEXT = t(
|
||||||
|
"The maximum you can withdraw instantly. There's no limit on the size of a withdrawal, but all withdrawals over the threshold will have a delay time added to them"
|
||||||
|
);
|
||||||
@@ -5,3 +5,4 @@ export * from './assets-data-provider';
|
|||||||
export * from './asset-details-dialog';
|
export * from './asset-details-dialog';
|
||||||
export * from './asset-details-table';
|
export * from './asset-details-table';
|
||||||
export * from './asset-option';
|
export * from './asset-option';
|
||||||
|
export * from './constants';
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ export * from './lib/cells/price-flash-cell';
|
|||||||
export * from './lib/cells/vol-cell';
|
export * from './lib/cells/vol-cell';
|
||||||
export * from './lib/cells/centered-grid-cell';
|
export * from './lib/cells/centered-grid-cell';
|
||||||
export * from './lib/cells/market-name-cell';
|
export * from './lib/cells/market-name-cell';
|
||||||
|
export * from './lib/cells/order-type-cell';
|
||||||
|
|
||||||
export * from './lib/filters/date-range-filter';
|
export * from './lib/filters/date-range-filter';
|
||||||
export * from './lib/filters/set-filter';
|
export * from './lib/filters/set-filter';
|
||||||
|
|||||||
@@ -0,0 +1,52 @@
|
|||||||
|
import type { MouseEvent } from 'react';
|
||||||
|
import { useMemo } from 'react';
|
||||||
|
import { useCallback } from 'react';
|
||||||
|
import { t } from '@vegaprotocol/i18n';
|
||||||
|
import * as Schema from '@vegaprotocol/types';
|
||||||
|
|
||||||
|
interface OrderTypeCellProps {
|
||||||
|
value?: Schema.OrderType;
|
||||||
|
data?: Schema.Order;
|
||||||
|
onClick?: (marketId: string, metaKey?: boolean) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const OrderTypeCell = ({
|
||||||
|
value,
|
||||||
|
data: order,
|
||||||
|
onClick,
|
||||||
|
}: OrderTypeCellProps) => {
|
||||||
|
const id = order ? order.market.id : '';
|
||||||
|
|
||||||
|
const label = useMemo(() => {
|
||||||
|
if (!order) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
if (!value) return '-';
|
||||||
|
if (order?.peggedOrder) {
|
||||||
|
return t('Pegged');
|
||||||
|
}
|
||||||
|
if (order?.liquidityProvision) {
|
||||||
|
return t('Liquidity provision');
|
||||||
|
}
|
||||||
|
return Schema.OrderTypeMapping[value];
|
||||||
|
}, [order, value]);
|
||||||
|
|
||||||
|
const handleOnClick = useCallback(
|
||||||
|
(ev: MouseEvent<HTMLButtonElement>) => {
|
||||||
|
ev.preventDefault();
|
||||||
|
ev.stopPropagation();
|
||||||
|
if (onClick) {
|
||||||
|
onClick(id, ev.metaKey || ev.ctrlKey);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[id, onClick]
|
||||||
|
);
|
||||||
|
if (!order) return null;
|
||||||
|
return order?.liquidityProvision ? (
|
||||||
|
<button onClick={handleOnClick} tabIndex={0} className="underline">
|
||||||
|
{label}
|
||||||
|
</button>
|
||||||
|
) : (
|
||||||
|
<span>{label}</span>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -8,7 +8,7 @@ const commonProps = {
|
|||||||
|
|
||||||
describe('DateRangeFilter', () => {
|
describe('DateRangeFilter', () => {
|
||||||
it('should be properly rendered', async () => {
|
it('should be properly rendered', async () => {
|
||||||
const defaultRangeFilter = {
|
const defaultValue = {
|
||||||
start: '2023-02-14T13:53:01+01:00',
|
start: '2023-02-14T13:53:01+01:00',
|
||||||
end: '2023-02-21T13:53:01+01:00',
|
end: '2023-02-21T13:53:01+01:00',
|
||||||
};
|
};
|
||||||
@@ -17,7 +17,7 @@ describe('DateRangeFilter', () => {
|
|||||||
render(
|
render(
|
||||||
<DateRangeFilter
|
<DateRangeFilter
|
||||||
{...(commonProps as unknown as DateRangeFilterProps)}
|
{...(commonProps as unknown as DateRangeFilterProps)}
|
||||||
defaultRangeFilter={defaultRangeFilter}
|
defaultValue={defaultValue}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import type { ChangeEvent } from 'react';
|
import type { ChangeEvent } from 'react';
|
||||||
import { useEffect, useMemo } from 'react';
|
import { useEffect, useMemo, useRef } from 'react';
|
||||||
import type * as Schema from '@vegaprotocol/types';
|
import type * as Schema from '@vegaprotocol/types';
|
||||||
import { forwardRef, useImperativeHandle, useState } from 'react';
|
import { forwardRef, useImperativeHandle, useState } from 'react';
|
||||||
import type { IDoesFilterPassParams, IFilterParams } from 'ag-grid-community';
|
import type { IDoesFilterPassParams, IFilterParams } from 'ag-grid-community';
|
||||||
@@ -17,9 +17,9 @@ import { formatForInput } from '@vegaprotocol/utils';
|
|||||||
import { t } from '@vegaprotocol/i18n';
|
import { t } from '@vegaprotocol/i18n';
|
||||||
import { InputError } from '@vegaprotocol/ui-toolkit';
|
import { InputError } from '@vegaprotocol/ui-toolkit';
|
||||||
|
|
||||||
const defaultFilterValue: Schema.DateRange = {};
|
const defaultValue: Schema.DateRange = {};
|
||||||
export interface DateRangeFilterProps extends IFilterParams {
|
export interface DateRangeFilterProps extends IFilterParams {
|
||||||
defaultRangeFilter?: Schema.DateRange;
|
defaultValue?: Schema.DateRange;
|
||||||
maxSubDays?: number;
|
maxSubDays?: number;
|
||||||
maxNextDays?: number;
|
maxNextDays?: number;
|
||||||
maxDaysRange?: number;
|
maxDaysRange?: number;
|
||||||
@@ -27,8 +27,9 @@ export interface DateRangeFilterProps extends IFilterParams {
|
|||||||
|
|
||||||
export const DateRangeFilter = forwardRef(
|
export const DateRangeFilter = forwardRef(
|
||||||
(props: DateRangeFilterProps, ref) => {
|
(props: DateRangeFilterProps, ref) => {
|
||||||
const defaultDates = props?.defaultRangeFilter || defaultFilterValue;
|
const defaultDates = props?.defaultValue || defaultValue;
|
||||||
const [value, setValue] = useState<Schema.DateRange>(defaultDates);
|
const [value, setValue] = useState<Schema.DateRange>(defaultDates);
|
||||||
|
const valueRef = useRef<Schema.DateRange>(value);
|
||||||
const [error, setError] = useState<string>('');
|
const [error, setError] = useState<string>('');
|
||||||
const [minStartDate, maxStartDate, minEndDate, maxEndDate] = useMemo(() => {
|
const [minStartDate, maxStartDate, minEndDate, maxEndDate] = useMemo(() => {
|
||||||
const minStartDate =
|
const minStartDate =
|
||||||
@@ -93,7 +94,7 @@ export const DateRangeFilter = forwardRef(
|
|||||||
},
|
},
|
||||||
|
|
||||||
isFilterActive() {
|
isFilterActive() {
|
||||||
return value.start || value.end;
|
return valueRef.current.start || valueRef.current.end;
|
||||||
},
|
},
|
||||||
|
|
||||||
getModel() {
|
getModel() {
|
||||||
@@ -101,13 +102,13 @@ export const DateRangeFilter = forwardRef(
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return { value };
|
return { value: valueRef.current };
|
||||||
},
|
},
|
||||||
|
|
||||||
setModel(model?: { value: Schema.DateRange } | null) {
|
setModel(model?: { value: Schema.DateRange } | null) {
|
||||||
setValue(
|
valueRef.current =
|
||||||
model?.value || props?.defaultRangeFilter || defaultFilterValue
|
model?.value || props?.defaultValue || defaultValue;
|
||||||
);
|
setValue(valueRef.current);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
@@ -185,10 +186,8 @@ export const DateRangeFilter = forwardRef(
|
|||||||
update = { ...update, end: checkForEndDate(endDate, startDate) };
|
update = { ...update, end: checkForEndDate(endDate, startDate) };
|
||||||
|
|
||||||
if (validate(name, date, update)) {
|
if (validate(name, date, update)) {
|
||||||
setValue((curr) => ({
|
valueRef.current = { ...valueRef.current, ...update };
|
||||||
...curr,
|
setValue(valueRef.current);
|
||||||
...update,
|
|
||||||
}));
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -241,7 +240,8 @@ export const DateRangeFilter = forwardRef(
|
|||||||
className="ag-standard-button ag-filter-apply-panel-button"
|
className="ag-standard-button ag-filter-apply-panel-button"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setError('');
|
setError('');
|
||||||
setValue(defaultDates);
|
valueRef.current = defaultDates;
|
||||||
|
setValue(valueRef.current);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{t('Reset')}
|
{t('Reset')}
|
||||||
|
|||||||
@@ -1,10 +1,17 @@
|
|||||||
import type { ChangeEvent } from 'react';
|
import type { ChangeEvent } from 'react';
|
||||||
import { forwardRef, useEffect, useImperativeHandle, useState } from 'react';
|
import {
|
||||||
|
forwardRef,
|
||||||
|
useEffect,
|
||||||
|
useImperativeHandle,
|
||||||
|
useState,
|
||||||
|
useRef,
|
||||||
|
} from 'react';
|
||||||
import type { IDoesFilterPassParams, IFilterParams } from 'ag-grid-community';
|
import type { IDoesFilterPassParams, IFilterParams } from 'ag-grid-community';
|
||||||
import { t } from '@vegaprotocol/i18n';
|
import { t } from '@vegaprotocol/i18n';
|
||||||
|
|
||||||
export const SetFilter = forwardRef((props: IFilterParams, ref) => {
|
export const SetFilter = forwardRef((props: IFilterParams, ref) => {
|
||||||
const [value, setValue] = useState<string[]>([]);
|
const [value, setValue] = useState<string[]>([]);
|
||||||
|
const valueRef = useRef(value);
|
||||||
|
|
||||||
// expose AG Grid Filter Lifecycle callbacks
|
// expose AG Grid Filter Lifecycle callbacks
|
||||||
useImperativeHandle(ref, () => {
|
useImperativeHandle(ref, () => {
|
||||||
@@ -28,29 +35,28 @@ export const SetFilter = forwardRef((props: IFilterParams, ref) => {
|
|||||||
},
|
},
|
||||||
|
|
||||||
isFilterActive() {
|
isFilterActive() {
|
||||||
return value.length !== 0;
|
return valueRef.current.length !== 0;
|
||||||
},
|
},
|
||||||
|
|
||||||
getModel() {
|
getModel() {
|
||||||
if (!this.isFilterActive()) {
|
if (!this.isFilterActive()) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
return { value: valueRef.current };
|
||||||
return { value };
|
|
||||||
},
|
},
|
||||||
|
|
||||||
setModel(model?: { value: string[] } | null) {
|
setModel(model?: { value: string[] } | null) {
|
||||||
setValue(!model ? [] : model.value);
|
valueRef.current = !model ? [] : model.value;
|
||||||
|
setValue(valueRef.current);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
const onChange = (event: ChangeEvent<HTMLInputElement>) => {
|
const onChange = (event: ChangeEvent<HTMLInputElement>) => {
|
||||||
setValue(
|
valueRef.current = event.target.checked
|
||||||
event.target.checked
|
? [...value, event.target.value]
|
||||||
? [...value, event.target.value]
|
: value.filter((v) => v !== event.target.value);
|
||||||
: value.filter((v) => v !== event.target.value)
|
setValue(valueRef.current);
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -77,7 +83,7 @@ export const SetFilter = forwardRef((props: IFilterParams, ref) => {
|
|||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className="ag-standard-button ag-filter-apply-panel-button"
|
className="ag-standard-button ag-filter-apply-panel-button"
|
||||||
onClick={() => setValue([])}
|
onClick={() => setValue((valueRef.current = []))}
|
||||||
>
|
>
|
||||||
{t('Reset')}
|
{t('Reset')}
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -15,8 +15,8 @@ export const useInitialMargin = (
|
|||||||
marketId: OrderSubmissionBody['orderSubmission']['marketId'],
|
marketId: OrderSubmissionBody['orderSubmission']['marketId'],
|
||||||
order?: OrderSubmissionBody['orderSubmission']
|
order?: OrderSubmissionBody['orderSubmission']
|
||||||
) => {
|
) => {
|
||||||
const { pubKey: partyId } = useVegaWallet();
|
const { pubKey } = useVegaWallet();
|
||||||
const commonVariables = { marketId, partyId: partyId || '' };
|
const commonVariables = { marketId, partyId: pubKey || '' };
|
||||||
const { data: marketData } = useDataProvider({
|
const { data: marketData } = useDataProvider({
|
||||||
dataProvider: marketDataProvider,
|
dataProvider: marketDataProvider,
|
||||||
variables: { marketId },
|
variables: { marketId },
|
||||||
@@ -24,7 +24,7 @@ export const useInitialMargin = (
|
|||||||
const { data: activeVolumeAndMargin } = useDataProvider({
|
const { data: activeVolumeAndMargin } = useDataProvider({
|
||||||
dataProvider: volumeAndMarginProvider,
|
dataProvider: volumeAndMarginProvider,
|
||||||
variables: commonVariables,
|
variables: commonVariables,
|
||||||
skip: !partyId,
|
skip: !pubKey,
|
||||||
});
|
});
|
||||||
const { data: marketInfo } = useDataProvider({
|
const { data: marketInfo } = useDataProvider({
|
||||||
dataProvider: marketInfoProvider,
|
dataProvider: marketInfoProvider,
|
||||||
|
|||||||
@@ -100,7 +100,7 @@ export const DepositForm = ({
|
|||||||
defaultValues: {
|
defaultValues: {
|
||||||
to: pubKey ? pubKey : undefined,
|
to: pubKey ? pubKey : undefined,
|
||||||
asset: selectedAsset?.id,
|
asset: selectedAsset?.id,
|
||||||
amount: persistedDeposit.amount,
|
amount: persistedDeposit?.amount,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -70,7 +70,8 @@ export const DepositManager = ({
|
|||||||
|
|
||||||
const onAmountChange = useCallback(
|
const onAmountChange = useCallback(
|
||||||
(amount: string) => {
|
(amount: string) => {
|
||||||
savePersistentDeposit({ ...persistentDeposit, amount });
|
persistentDeposit &&
|
||||||
|
savePersistentDeposit({ ...persistentDeposit, amount });
|
||||||
},
|
},
|
||||||
[savePersistentDeposit, persistentDeposit]
|
[savePersistentDeposit, persistentDeposit]
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { usePersistentDeposit } from './use-persistent-deposit';
|
|||||||
describe('usePersistenDeposit', () => {
|
describe('usePersistenDeposit', () => {
|
||||||
it('should return empty data', () => {
|
it('should return empty data', () => {
|
||||||
const { result } = renderHook(() => usePersistentDeposit());
|
const { result } = renderHook(() => usePersistentDeposit());
|
||||||
expect(result.current).toEqual([{ assetId: '' }, expect.any(Function)]);
|
expect(result.current).toEqual([undefined, expect.any(Function)]);
|
||||||
});
|
});
|
||||||
it('should return empty and properly saved data', async () => {
|
it('should return empty and properly saved data', async () => {
|
||||||
const aId = 'test';
|
const aId = 'test';
|
||||||
|
|||||||
@@ -32,10 +32,14 @@ const usePersistentDepositStore = create<{
|
|||||||
|
|
||||||
export const usePersistentDeposit = (
|
export const usePersistentDeposit = (
|
||||||
assetId?: string
|
assetId?: string
|
||||||
): [PersistedDeposit, (entry: PersistedDeposit) => void] => {
|
): [PersistedDeposit | undefined, (entry: PersistedDeposit) => void] => {
|
||||||
const { deposits, lastVisited, saveValue } = usePersistentDepositStore();
|
const { deposits, lastVisited, saveValue } = usePersistentDepositStore();
|
||||||
const discoveredData = useMemo(() => {
|
const discoveredData = useMemo(() => {
|
||||||
return deposits[assetId || ''] || lastVisited || { assetId: assetId || '' };
|
return assetId
|
||||||
|
? deposits[assetId]
|
||||||
|
? deposits[assetId]
|
||||||
|
: { assetId }
|
||||||
|
: lastVisited;
|
||||||
}, [deposits, lastVisited, assetId]);
|
}, [deposits, lastVisited, assetId]);
|
||||||
|
|
||||||
return [discoveredData, saveValue];
|
return [discoveredData, saveValue];
|
||||||
|
|||||||
@@ -38,10 +38,10 @@ export const TransferTooltipCellComponent = ({
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const defaultRangeFilter = { start: formatRFC3339(subDays(Date.now(), 7)) };
|
const defaultValue = { start: formatRFC3339(subDays(Date.now(), 7)) };
|
||||||
const dateRangeFilterParams = {
|
const dateRangeFilterParams = {
|
||||||
maxNextDays: 0,
|
maxNextDays: 0,
|
||||||
defaultRangeFilter,
|
defaultValue,
|
||||||
};
|
};
|
||||||
type LedgerEntryProps = TypedDataAgGrid<LedgerEntry>;
|
type LedgerEntryProps = TypedDataAgGrid<LedgerEntry>;
|
||||||
|
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import type {
|
|||||||
OrdersQueryVariables,
|
OrdersQueryVariables,
|
||||||
} from './__generated__/Orders';
|
} from './__generated__/Orders';
|
||||||
import { OrdersDocument, OrdersUpdateDocument } from './__generated__/Orders';
|
import { OrdersDocument, OrdersUpdateDocument } from './__generated__/Orders';
|
||||||
|
import type { ApolloClient } from '@apollo/client';
|
||||||
|
|
||||||
export type Order = Omit<OrderFieldsFragment, 'market'> & {
|
export type Order = Omit<OrderFieldsFragment, 'market'> & {
|
||||||
market?: Market;
|
market?: Market;
|
||||||
@@ -81,8 +82,31 @@ const getData = (
|
|||||||
): Edge<OrderFieldsFragment>[] =>
|
): Edge<OrderFieldsFragment>[] =>
|
||||||
responseData?.party?.ordersConnection?.edges || [];
|
responseData?.party?.ordersConnection?.edges || [];
|
||||||
|
|
||||||
const getDelta = (subscriptionData: OrdersUpdateSubscription) =>
|
const getDelta = (
|
||||||
subscriptionData.orders || [];
|
subscriptionData: OrdersUpdateSubscription,
|
||||||
|
variables: OrdersQueryVariables,
|
||||||
|
client: ApolloClient<object>
|
||||||
|
) => {
|
||||||
|
if (!subscriptionData.orders) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
subscriptionData.orders.forEach((order) => {
|
||||||
|
client.cache.modify({
|
||||||
|
id: client.cache.identify({
|
||||||
|
__typename: 'Order',
|
||||||
|
id: order.id,
|
||||||
|
}),
|
||||||
|
fields: {
|
||||||
|
price: () => order.price,
|
||||||
|
size: () => order.size,
|
||||||
|
remaining: () => order.remaining,
|
||||||
|
updatedAt: () => order.updatedAt,
|
||||||
|
status: () => order.status,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return subscriptionData.orders;
|
||||||
|
};
|
||||||
|
|
||||||
const getPageInfo = (responseData: OrdersQuery): PageInfo | null =>
|
const getPageInfo = (responseData: OrdersQuery): PageInfo | null =>
|
||||||
responseData.party?.ordersConnection?.pageInfo || null;
|
responseData.party?.ordersConnection?.pageInfo || null;
|
||||||
@@ -150,7 +174,7 @@ export const update = (
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export const ordersProvider = makeDataProvider<
|
const ordersProvider = makeDataProvider<
|
||||||
OrdersQuery,
|
OrdersQuery,
|
||||||
ReturnType<typeof getData>,
|
ReturnType<typeof getData>,
|
||||||
OrdersUpdateSubscription,
|
OrdersUpdateSubscription,
|
||||||
@@ -165,11 +189,36 @@ export const ordersProvider = makeDataProvider<
|
|||||||
pagination: {
|
pagination: {
|
||||||
getPageInfo,
|
getPageInfo,
|
||||||
append,
|
append,
|
||||||
first: 100,
|
first: 1000,
|
||||||
},
|
},
|
||||||
additionalContext: { isEnlargedTimeout: true },
|
additionalContext: { isEnlargedTimeout: true },
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const activeOrdersProvider = makeDerivedDataProvider<
|
||||||
|
ReturnType<typeof getData>,
|
||||||
|
never,
|
||||||
|
{ partyId: string; marketId?: string }
|
||||||
|
>(
|
||||||
|
[
|
||||||
|
(callback, client, variables) =>
|
||||||
|
ordersProvider(callback, client, {
|
||||||
|
partyId: variables.partyId,
|
||||||
|
filter: {
|
||||||
|
status: [OrderStatus.STATUS_ACTIVE, OrderStatus.STATUS_PARKED],
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
(partsData, variables, prevData, parts, subscriptions) => {
|
||||||
|
if (!parts[0].isUpdate && subscriptions && subscriptions[0].load) {
|
||||||
|
subscriptions[0].load();
|
||||||
|
}
|
||||||
|
const orders = partsData[0] as ReturnType<typeof getData>;
|
||||||
|
return variables.marketId
|
||||||
|
? orders.filter((edge) => variables.marketId === edge.node.market.id)
|
||||||
|
: orders;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
export const ordersWithMarketProvider = makeDerivedDataProvider<
|
export const ordersWithMarketProvider = makeDerivedDataProvider<
|
||||||
(OrderEdge | null)[],
|
(OrderEdge | null)[],
|
||||||
Order[],
|
Order[],
|
||||||
@@ -193,63 +242,20 @@ export const ordersWithMarketProvider = makeDerivedDataProvider<
|
|||||||
combineInsertionData<Order>
|
combineInsertionData<Order>
|
||||||
);
|
);
|
||||||
|
|
||||||
const hasActiveOrderProviderInternal = makeDataProvider<
|
|
||||||
OrdersQuery,
|
|
||||||
boolean,
|
|
||||||
OrdersUpdateSubscription,
|
|
||||||
ReturnType<typeof getDelta>,
|
|
||||||
OrdersQueryVariables
|
|
||||||
>({
|
|
||||||
query: OrdersDocument,
|
|
||||||
subscriptionQuery: OrdersUpdateDocument,
|
|
||||||
update: (
|
|
||||||
data: boolean | null,
|
|
||||||
delta: ReturnType<typeof getDelta>,
|
|
||||||
reload: () => void
|
|
||||||
) => {
|
|
||||||
const orders = delta?.filter(
|
|
||||||
(order) => !(order.peggedOrder || order.liquidityProvisionId)
|
|
||||||
);
|
|
||||||
if (!orders?.length) {
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
const hasActiveOrders = orders.some(
|
|
||||||
(order) => order.status === OrderStatus.STATUS_ACTIVE
|
|
||||||
);
|
|
||||||
if (hasActiveOrders) {
|
|
||||||
return true;
|
|
||||||
} else if (data && !hasActiveOrders) {
|
|
||||||
reload();
|
|
||||||
}
|
|
||||||
return data;
|
|
||||||
},
|
|
||||||
getData: (responseData: OrdersQuery | null) => {
|
|
||||||
const hasActiveOrder = !!responseData?.party?.ordersConnection?.edges?.some(
|
|
||||||
(order) => !(order.node.peggedOrder || order.node.liquidityProvision)
|
|
||||||
);
|
|
||||||
return hasActiveOrder;
|
|
||||||
},
|
|
||||||
getDelta,
|
|
||||||
});
|
|
||||||
|
|
||||||
export const hasActiveOrderProvider = makeDerivedDataProvider<
|
export const hasActiveOrderProvider = makeDerivedDataProvider<
|
||||||
boolean,
|
boolean,
|
||||||
never,
|
never,
|
||||||
{ partyId: string; marketId?: string }
|
{ partyId: string; marketId?: string }
|
||||||
>(
|
>([activeOrdersProvider], (parts) => !!parts[0].length);
|
||||||
[
|
|
||||||
(callback, client, { partyId, marketId }) =>
|
export const hasAmendableOrderProvider = makeDerivedDataProvider<
|
||||||
hasActiveOrderProviderInternal(callback, client, {
|
boolean,
|
||||||
marketIds: marketId ? [marketId] : undefined,
|
never,
|
||||||
filter: {
|
{ partyId: string; marketId?: string }
|
||||||
status: [OrderStatus.STATUS_ACTIVE],
|
>([activeOrdersProvider], (parts) => {
|
||||||
excludeLiquidity: true,
|
const activeOrders = parts[0] as ReturnType<typeof getData>;
|
||||||
},
|
const hasAmendableOrder = activeOrders.some(
|
||||||
pagination: {
|
(edge) => !(edge.node.liquidityProvision || edge.node.peggedOrder)
|
||||||
first: 1,
|
);
|
||||||
},
|
return hasAmendableOrder;
|
||||||
partyId,
|
});
|
||||||
} as OrdersQueryVariables),
|
|
||||||
],
|
|
||||||
(parts) => parts[0]
|
|
||||||
);
|
|
||||||
|
|||||||
@@ -6,10 +6,12 @@ import { OrderListManager } from './order-list-manager';
|
|||||||
export const OrderListContainer = ({
|
export const OrderListContainer = ({
|
||||||
marketId,
|
marketId,
|
||||||
onMarketClick,
|
onMarketClick,
|
||||||
|
onOrderTypeClick,
|
||||||
enforceBottomPlaceholder,
|
enforceBottomPlaceholder,
|
||||||
}: {
|
}: {
|
||||||
marketId?: string;
|
marketId?: string;
|
||||||
onMarketClick?: (marketId: string, metaKey?: boolean) => void;
|
onMarketClick?: (marketId: string, metaKey?: boolean) => void;
|
||||||
|
onOrderTypeClick?: (marketId: string, metaKey?: boolean) => void;
|
||||||
enforceBottomPlaceholder?: boolean;
|
enforceBottomPlaceholder?: boolean;
|
||||||
}) => {
|
}) => {
|
||||||
const { pubKey, isReadOnly } = useVegaWallet();
|
const { pubKey, isReadOnly } = useVegaWallet();
|
||||||
@@ -23,6 +25,7 @@ export const OrderListContainer = ({
|
|||||||
partyId={pubKey}
|
partyId={pubKey}
|
||||||
marketId={marketId}
|
marketId={marketId}
|
||||||
onMarketClick={onMarketClick}
|
onMarketClick={onMarketClick}
|
||||||
|
onOrderTypeClick={onOrderTypeClick}
|
||||||
isReadOnly={isReadOnly}
|
isReadOnly={isReadOnly}
|
||||||
enforceBottomPlaceholder={enforceBottomPlaceholder}
|
enforceBottomPlaceholder={enforceBottomPlaceholder}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import type { GridReadyEvent } from 'ag-grid-community';
|
|||||||
|
|
||||||
import { OrderListTable } from '../order-list/order-list';
|
import { OrderListTable } from '../order-list/order-list';
|
||||||
import { useOrderListData } from './use-order-list-data';
|
import { useOrderListData } from './use-order-list-data';
|
||||||
import { useHasActiveOrder } from '../../order-hooks/use-has-active-order';
|
import { useHasAmendableOrder } from '../../order-hooks/use-has-amendable-order';
|
||||||
import type { Filter, Sort } from './use-order-list-data';
|
import type { Filter, Sort } from './use-order-list-data';
|
||||||
import { useBottomPlaceholder } from '@vegaprotocol/react-helpers';
|
import { useBottomPlaceholder } from '@vegaprotocol/react-helpers';
|
||||||
import { OrderStatus } from '@vegaprotocol/types';
|
import { OrderStatus } from '@vegaprotocol/types';
|
||||||
@@ -16,6 +16,7 @@ import {
|
|||||||
normalizeOrderAmendment,
|
normalizeOrderAmendment,
|
||||||
useVegaTransactionStore,
|
useVegaTransactionStore,
|
||||||
} from '@vegaprotocol/wallet';
|
} from '@vegaprotocol/wallet';
|
||||||
|
import isEqual from 'lodash/isEqual';
|
||||||
import type { OrderTxUpdateFieldsFragment } from '@vegaprotocol/wallet';
|
import type { OrderTxUpdateFieldsFragment } from '@vegaprotocol/wallet';
|
||||||
import { OrderEditDialog } from '../order-list/order-edit-dialog';
|
import { OrderEditDialog } from '../order-list/order-edit-dialog';
|
||||||
import type { Order, OrderEdge } from '../order-data-provider';
|
import type { Order, OrderEdge } from '../order-data-provider';
|
||||||
@@ -24,31 +25,23 @@ export interface OrderListManagerProps {
|
|||||||
partyId: string;
|
partyId: string;
|
||||||
marketId?: string;
|
marketId?: string;
|
||||||
onMarketClick?: (marketId: string, metaKey?: boolean) => void;
|
onMarketClick?: (marketId: string, metaKey?: boolean) => void;
|
||||||
|
onOrderTypeClick?: (marketId: string, metaKey?: boolean) => void;
|
||||||
isReadOnly: boolean;
|
isReadOnly: boolean;
|
||||||
enforceBottomPlaceholder?: boolean;
|
enforceBottomPlaceholder?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const CancelAllOrdersButton = ({
|
const CancelAllOrdersButton = ({ onClick }: { onClick: () => void }) => (
|
||||||
onClick,
|
<div className="dark:bg-black/75 bg-white/75 h-auto flex justify-end px-[11px] py-2 absolute bottom-0 right-3 rounded">
|
||||||
marketId,
|
<Button
|
||||||
}: {
|
variant="primary"
|
||||||
onClick: (marketId?: string) => void;
|
size="sm"
|
||||||
marketId?: string;
|
onClick={onClick}
|
||||||
}) => {
|
data-testid="cancelAll"
|
||||||
const hasActiveOrder = useHasActiveOrder(marketId);
|
>
|
||||||
return hasActiveOrder ? (
|
{t('Cancel all')}
|
||||||
<div className="dark:bg-black/75 bg-white/75 h-auto flex justify-end px-[11px] py-2 absolute bottom-0 right-3 rounded">
|
</Button>
|
||||||
<Button
|
</div>
|
||||||
variant="primary"
|
);
|
||||||
size="sm"
|
|
||||||
onClick={() => onClick(marketId)}
|
|
||||||
data-testid="cancelAll"
|
|
||||||
>
|
|
||||||
{t('Cancel all')}
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
) : null;
|
|
||||||
};
|
|
||||||
|
|
||||||
const initialFilter: Filter = {
|
const initialFilter: Filter = {
|
||||||
status: {
|
status: {
|
||||||
@@ -60,6 +53,7 @@ export const OrderListManager = ({
|
|||||||
partyId,
|
partyId,
|
||||||
marketId,
|
marketId,
|
||||||
onMarketClick,
|
onMarketClick,
|
||||||
|
onOrderTypeClick,
|
||||||
isReadOnly,
|
isReadOnly,
|
||||||
enforceBottomPlaceholder,
|
enforceBottomPlaceholder,
|
||||||
}: OrderListManagerProps) => {
|
}: OrderListManagerProps) => {
|
||||||
@@ -68,9 +62,10 @@ export const OrderListManager = ({
|
|||||||
const scrolledToTop = useRef(false);
|
const scrolledToTop = useRef(false);
|
||||||
const [sort, setSort] = useState<Sort[] | undefined>();
|
const [sort, setSort] = useState<Sort[] | undefined>();
|
||||||
const [filter, setFilter] = useState<Filter | undefined>(initialFilter);
|
const [filter, setFilter] = useState<Filter | undefined>(initialFilter);
|
||||||
|
const filterRef = useRef(initialFilter);
|
||||||
const [editOrder, setEditOrder] = useState<Order | null>(null);
|
const [editOrder, setEditOrder] = useState<Order | null>(null);
|
||||||
const create = useVegaTransactionStore((state) => state.create);
|
const create = useVegaTransactionStore((state) => state.create);
|
||||||
const hasActiveOrder = useHasActiveOrder(marketId);
|
const hasAmendableOrder = useHasAmendableOrder(marketId);
|
||||||
|
|
||||||
const { data, error, loading, reload } = useOrderListData({
|
const { data, error, loading, reload } = useOrderListData({
|
||||||
partyId,
|
partyId,
|
||||||
@@ -86,12 +81,16 @@ export const OrderListManager = ({
|
|||||||
...bottomPlaceholderProps
|
...bottomPlaceholderProps
|
||||||
} = useBottomPlaceholder<Order>({
|
} = useBottomPlaceholder<Order>({
|
||||||
gridRef,
|
gridRef,
|
||||||
disabled: !enforceBottomPlaceholder && !isReadOnly && !hasActiveOrder,
|
disabled: !enforceBottomPlaceholder && !isReadOnly && !hasAmendableOrder,
|
||||||
});
|
});
|
||||||
|
|
||||||
const onFilterChanged = useCallback(
|
const onFilterChanged = useCallback(
|
||||||
(event: FilterChangedEvent) => {
|
(event: FilterChangedEvent) => {
|
||||||
const updatedFilter = event.api.getFilterModel();
|
const updatedFilter = event.api.getFilterModel();
|
||||||
|
if (isEqual(updatedFilter, filterRef.current)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
filterRef.current = updatedFilter;
|
||||||
if (Object.keys(updatedFilter).length) {
|
if (Object.keys(updatedFilter).length) {
|
||||||
setFilter(updatedFilter);
|
setFilter(updatedFilter);
|
||||||
} else {
|
} else {
|
||||||
@@ -142,16 +141,13 @@ export const OrderListManager = ({
|
|||||||
setDataCount(gridRef.current?.api?.getModel().getRowCount() ?? 0);
|
setDataCount(gridRef.current?.api?.getModel().getRowCount() ?? 0);
|
||||||
}, [data]);
|
}, [data]);
|
||||||
|
|
||||||
const cancelAll = useCallback(
|
const cancelAll = useCallback(() => {
|
||||||
(marketId?: string) => {
|
create({
|
||||||
create({
|
orderCancellation: {
|
||||||
orderCancellation: {
|
marketId,
|
||||||
marketId,
|
},
|
||||||
},
|
});
|
||||||
});
|
}, [create, marketId]);
|
||||||
},
|
|
||||||
[create]
|
|
||||||
);
|
|
||||||
const extractedData =
|
const extractedData =
|
||||||
data && !loading
|
data && !loading
|
||||||
? data
|
? data
|
||||||
@@ -171,6 +167,7 @@ export const OrderListManager = ({
|
|||||||
cancel={cancel}
|
cancel={cancel}
|
||||||
setEditOrder={setEditOrder}
|
setEditOrder={setEditOrder}
|
||||||
onMarketClick={onMarketClick}
|
onMarketClick={onMarketClick}
|
||||||
|
onOrderTypeClick={onOrderTypeClick}
|
||||||
isReadOnly={isReadOnly}
|
isReadOnly={isReadOnly}
|
||||||
blockLoadDebounceMillis={100}
|
blockLoadDebounceMillis={100}
|
||||||
suppressLoadingOverlay
|
suppressLoadingOverlay
|
||||||
@@ -188,8 +185,8 @@ export const OrderListManager = ({
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{!isReadOnly && (
|
{!isReadOnly && hasAmendableOrder && (
|
||||||
<CancelAllOrdersButton onClick={cancelAll} marketId={marketId} />
|
<CancelAllOrdersButton onClick={cancelAll} />
|
||||||
)}
|
)}
|
||||||
{editOrder && (
|
{editOrder && (
|
||||||
<OrderEditDialog
|
<OrderEditDialog
|
||||||
|
|||||||
@@ -71,17 +71,27 @@ export const useOrderListData = ({
|
|||||||
// define variable as const to get type safety, using generic with useMemo resulted in lost type safety
|
// define variable as const to get type safety, using generic with useMemo resulted in lost type safety
|
||||||
const allVars: OrdersQueryVariables & OrdersUpdateSubscriptionVariables = {
|
const allVars: OrdersQueryVariables & OrdersUpdateSubscriptionVariables = {
|
||||||
partyId,
|
partyId,
|
||||||
filter: {
|
|
||||||
dateRange: filter?.updatedAt?.value,
|
|
||||||
status: filter?.status?.value,
|
|
||||||
timeInForce: filter?.timeInForce?.value,
|
|
||||||
types: filter?.type?.value,
|
|
||||||
},
|
|
||||||
pagination: {
|
|
||||||
first: 1000,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
if (
|
||||||
|
filter?.updatedAt?.value ||
|
||||||
|
filter?.status?.value.length ||
|
||||||
|
filter?.timeInForce?.value.length ||
|
||||||
|
filter?.type?.value.length
|
||||||
|
) {
|
||||||
|
allVars.filter = {};
|
||||||
|
if (filter?.updatedAt?.value) {
|
||||||
|
allVars.filter.dateRange = filter?.updatedAt?.value;
|
||||||
|
}
|
||||||
|
if (filter?.status?.value.length) {
|
||||||
|
allVars.filter.status = filter?.status?.value;
|
||||||
|
}
|
||||||
|
if (filter?.timeInForce?.value.length) {
|
||||||
|
allVars.filter.timeInForce = filter?.timeInForce?.value;
|
||||||
|
}
|
||||||
|
if (filter?.type?.value.length) {
|
||||||
|
allVars.filter.types = filter?.type?.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
return allVars;
|
return allVars;
|
||||||
}, [partyId, filter]);
|
}, [partyId, filter]);
|
||||||
|
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import {
|
|||||||
negativeClassNames,
|
negativeClassNames,
|
||||||
positiveClassNames,
|
positiveClassNames,
|
||||||
MarketNameCell,
|
MarketNameCell,
|
||||||
|
OrderTypeCell,
|
||||||
} from '@vegaprotocol/datagrid';
|
} from '@vegaprotocol/datagrid';
|
||||||
import type {
|
import type {
|
||||||
TypedDataAgGrid,
|
TypedDataAgGrid,
|
||||||
@@ -31,12 +32,16 @@ export type OrderListTableProps = OrderListProps & {
|
|||||||
cancel: (order: Order) => void;
|
cancel: (order: Order) => void;
|
||||||
setEditOrder: (order: Order) => void;
|
setEditOrder: (order: Order) => void;
|
||||||
onMarketClick?: (marketId: string, metaKey?: boolean) => void;
|
onMarketClick?: (marketId: string, metaKey?: boolean) => void;
|
||||||
|
onOrderTypeClick?: (marketId: string, metaKey?: boolean) => void;
|
||||||
isReadOnly: boolean;
|
isReadOnly: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const OrderListTable = memo(
|
export const OrderListTable = memo(
|
||||||
forwardRef<AgGridReact, OrderListTableProps>(
|
forwardRef<AgGridReact, OrderListTableProps>(
|
||||||
({ cancel, setEditOrder, onMarketClick, ...props }, ref) => {
|
(
|
||||||
|
{ cancel, setEditOrder, onMarketClick, onOrderTypeClick, ...props },
|
||||||
|
ref
|
||||||
|
) => {
|
||||||
return (
|
return (
|
||||||
<AgGrid
|
<AgGrid
|
||||||
ref={ref}
|
ref={ref}
|
||||||
@@ -51,7 +56,7 @@ export const OrderListTable = memo(
|
|||||||
height: '100%',
|
height: '100%',
|
||||||
}}
|
}}
|
||||||
getRowId={({ data }) => data.id}
|
getRowId={({ data }) => data.id}
|
||||||
components={{ MarketNameCell }}
|
components={{ MarketNameCell, OrderTypeCell }}
|
||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
<AgGridColumn
|
<AgGridColumn
|
||||||
@@ -103,17 +108,9 @@ export const OrderListTable = memo(
|
|||||||
filterParams={{
|
filterParams={{
|
||||||
set: Schema.OrderTypeMapping,
|
set: Schema.OrderTypeMapping,
|
||||||
}}
|
}}
|
||||||
valueFormatter={({
|
cellRenderer="OrderTypeCell"
|
||||||
data: order,
|
cellRendererParams={{
|
||||||
value,
|
onClick: onOrderTypeClick,
|
||||||
}: VegaValueFormatterParams<Order, 'type'>) => {
|
|
||||||
if (!order) {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
if (!value) return '-';
|
|
||||||
if (order?.peggedOrder) return t('Pegged');
|
|
||||||
if (order?.liquidityProvision) return t('Liquidity provision');
|
|
||||||
return Schema.OrderTypeMapping[value];
|
|
||||||
}}
|
}}
|
||||||
minWidth={80}
|
minWidth={80}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
export * from './__generated__/OrdersSubscription';
|
export * from './__generated__/OrdersSubscription';
|
||||||
export * from './use-has-active-order';
|
export * from './use-has-amendable-order';
|
||||||
export * from './use-order-update';
|
export * from './use-order-update';
|
||||||
export * from './use-pending-orders-volume';
|
|
||||||
export * from './use-order-store';
|
export * from './use-order-store';
|
||||||
|
|||||||
+6
-6
@@ -1,17 +1,17 @@
|
|||||||
import { useCallback, useState } from 'react';
|
import { useCallback, useState } from 'react';
|
||||||
import { hasActiveOrderProvider } from '../components/order-data-provider/';
|
import { hasAmendableOrderProvider } from '../components/order-data-provider';
|
||||||
import { useVegaWallet } from '@vegaprotocol/wallet';
|
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||||
import { useDataProvider } from '@vegaprotocol/react-helpers';
|
import { useDataProvider } from '@vegaprotocol/react-helpers';
|
||||||
|
|
||||||
export const useHasActiveOrder = (marketId?: string) => {
|
export const useHasAmendableOrder = (marketId?: string) => {
|
||||||
const { pubKey } = useVegaWallet();
|
const { pubKey } = useVegaWallet();
|
||||||
const [hasActiveOrder, setHasActiveOrder] = useState(false);
|
const [hasAmendableOrder, setHasAmendableOrder] = useState(false);
|
||||||
const update = useCallback(({ data }: { data: boolean | null }) => {
|
const update = useCallback(({ data }: { data: boolean | null }) => {
|
||||||
setHasActiveOrder(Boolean(data));
|
setHasAmendableOrder(Boolean(data));
|
||||||
return true;
|
return true;
|
||||||
}, []);
|
}, []);
|
||||||
useDataProvider({
|
useDataProvider({
|
||||||
dataProvider: hasActiveOrderProvider,
|
dataProvider: hasAmendableOrderProvider,
|
||||||
update,
|
update,
|
||||||
variables: {
|
variables: {
|
||||||
partyId: pubKey || '',
|
partyId: pubKey || '',
|
||||||
@@ -20,5 +20,5 @@ export const useHasActiveOrder = (marketId?: string) => {
|
|||||||
skip: !pubKey,
|
skip: !pubKey,
|
||||||
});
|
});
|
||||||
|
|
||||||
return hasActiveOrder;
|
return hasAmendableOrder;
|
||||||
};
|
};
|
||||||
@@ -1,74 +0,0 @@
|
|||||||
import { useState, useCallback } from 'react';
|
|
||||||
import { OrderStatus, Side } from '@vegaprotocol/types';
|
|
||||||
import { ordersProvider } from '../components/order-data-provider/order-data-provider';
|
|
||||||
import type { OrderFieldsFragment } from '../components/order-data-provider/__generated__/Orders';
|
|
||||||
import type { Edge } from '@vegaprotocol/utils';
|
|
||||||
import { useDataProvider } from '@vegaprotocol/react-helpers';
|
|
||||||
|
|
||||||
const sumVolume = (orders: (Edge<OrderFieldsFragment> | null)[], side: Side) =>
|
|
||||||
orders
|
|
||||||
.reduce(
|
|
||||||
(sum, order) =>
|
|
||||||
order?.node.side === side
|
|
||||||
? sum +
|
|
||||||
BigInt(
|
|
||||||
order?.node.status === OrderStatus.STATUS_PARTIALLY_FILLED
|
|
||||||
? order?.node.remaining
|
|
||||||
: order?.node.size
|
|
||||||
)
|
|
||||||
: sum,
|
|
||||||
BigInt(0)
|
|
||||||
)
|
|
||||||
.toString();
|
|
||||||
|
|
||||||
export const useActiveOrdersVolumeAndMargin = (
|
|
||||||
partyId: string | null | undefined,
|
|
||||||
marketId: string
|
|
||||||
) => {
|
|
||||||
const [buyVolume, setBuyVolume] = useState<string | undefined>();
|
|
||||||
const [sellVolume, setSellVolume] = useState<string | undefined>();
|
|
||||||
const [buyInitialMargin, setBuyInitialMargin] = useState<
|
|
||||||
string | undefined
|
|
||||||
>();
|
|
||||||
const [sellInitialMargin, setSellInitialMargin] = useState<
|
|
||||||
string | undefined
|
|
||||||
>();
|
|
||||||
const update = useCallback(
|
|
||||||
({ data }: { data: (Edge<OrderFieldsFragment> | null)[] | null }) => {
|
|
||||||
if (!data) {
|
|
||||||
setBuyVolume(undefined);
|
|
||||||
setSellVolume(undefined);
|
|
||||||
setBuyInitialMargin(undefined);
|
|
||||||
setSellInitialMargin(undefined);
|
|
||||||
} else {
|
|
||||||
setBuyVolume(sumVolume(data, Side.SIDE_BUY));
|
|
||||||
setSellVolume(sumVolume(data, Side.SIDE_SELL));
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
},
|
|
||||||
[]
|
|
||||||
);
|
|
||||||
useDataProvider({
|
|
||||||
dataProvider: ordersProvider,
|
|
||||||
update,
|
|
||||||
variables: {
|
|
||||||
partyId: partyId || '',
|
|
||||||
marketIds: [marketId],
|
|
||||||
filter: {
|
|
||||||
status: [
|
|
||||||
OrderStatus.STATUS_ACTIVE,
|
|
||||||
OrderStatus.STATUS_PARTIALLY_FILLED,
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
skip: !partyId,
|
|
||||||
});
|
|
||||||
return buyVolume || sellVolume
|
|
||||||
? {
|
|
||||||
buyVolume,
|
|
||||||
sellVolume,
|
|
||||||
buyInitialMargin,
|
|
||||||
sellInitialMargin,
|
|
||||||
}
|
|
||||||
: undefined;
|
|
||||||
};
|
|
||||||
@@ -0,0 +1,129 @@
|
|||||||
|
import { Intent } from '@vegaprotocol/ui-toolkit';
|
||||||
|
import {
|
||||||
|
getOrderToastIntent,
|
||||||
|
getOrderToastTitle,
|
||||||
|
getRejectionReason,
|
||||||
|
timeInForceLabel,
|
||||||
|
} from './utils';
|
||||||
|
import * as Types from '@vegaprotocol/types';
|
||||||
|
|
||||||
|
describe('getOrderToastTitle', () => {
|
||||||
|
it('should return the correct title', () => {
|
||||||
|
expect(getOrderToastTitle(Types.OrderStatus.STATUS_ACTIVE)).toBe(
|
||||||
|
'Order submitted'
|
||||||
|
);
|
||||||
|
expect(getOrderToastTitle(Types.OrderStatus.STATUS_FILLED)).toBe(
|
||||||
|
'Order filled'
|
||||||
|
);
|
||||||
|
expect(getOrderToastTitle(Types.OrderStatus.STATUS_PARTIALLY_FILLED)).toBe(
|
||||||
|
'Order partially filled'
|
||||||
|
);
|
||||||
|
expect(getOrderToastTitle(Types.OrderStatus.STATUS_PARKED)).toBe(
|
||||||
|
'Order parked'
|
||||||
|
);
|
||||||
|
expect(getOrderToastTitle(Types.OrderStatus.STATUS_STOPPED)).toBe(
|
||||||
|
'Order stopped'
|
||||||
|
);
|
||||||
|
expect(getOrderToastTitle(Types.OrderStatus.STATUS_CANCELLED)).toBe(
|
||||||
|
'Order cancelled'
|
||||||
|
);
|
||||||
|
expect(getOrderToastTitle(Types.OrderStatus.STATUS_EXPIRED)).toBe(
|
||||||
|
'Order expired'
|
||||||
|
);
|
||||||
|
expect(getOrderToastTitle(Types.OrderStatus.STATUS_REJECTED)).toBe(
|
||||||
|
'Order rejected'
|
||||||
|
);
|
||||||
|
expect(getOrderToastTitle(undefined)).toBe(undefined);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getOrderToastIntent', () => {
|
||||||
|
it('should return the correct intent', () => {
|
||||||
|
expect(getOrderToastIntent(Types.OrderStatus.STATUS_PARKED)).toBe(
|
||||||
|
Intent.Warning
|
||||||
|
);
|
||||||
|
expect(getOrderToastIntent(Types.OrderStatus.STATUS_EXPIRED)).toBe(
|
||||||
|
Intent.Warning
|
||||||
|
);
|
||||||
|
expect(getOrderToastIntent(Types.OrderStatus.STATUS_PARTIALLY_FILLED)).toBe(
|
||||||
|
Intent.Warning
|
||||||
|
);
|
||||||
|
expect(getOrderToastIntent(Types.OrderStatus.STATUS_REJECTED)).toBe(
|
||||||
|
Intent.Danger
|
||||||
|
);
|
||||||
|
expect(getOrderToastIntent(Types.OrderStatus.STATUS_STOPPED)).toBe(
|
||||||
|
Intent.Danger
|
||||||
|
);
|
||||||
|
expect(getOrderToastIntent(Types.OrderStatus.STATUS_FILLED)).toBe(
|
||||||
|
Intent.Success
|
||||||
|
);
|
||||||
|
expect(getOrderToastIntent(Types.OrderStatus.STATUS_ACTIVE)).toBe(
|
||||||
|
Intent.Success
|
||||||
|
);
|
||||||
|
expect(getOrderToastIntent(Types.OrderStatus.STATUS_CANCELLED)).toBe(
|
||||||
|
Intent.Success
|
||||||
|
);
|
||||||
|
expect(getOrderToastIntent(undefined)).toBe(undefined);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getRejectionReason', () => {
|
||||||
|
it('should return the correct rejection reason for insufficient asset balance', () => {
|
||||||
|
expect(
|
||||||
|
getRejectionReason({
|
||||||
|
rejectionReason:
|
||||||
|
Types.OrderRejectionReason.ORDER_ERROR_INSUFFICIENT_ASSET_BALANCE,
|
||||||
|
status: Types.OrderStatus.STATUS_REJECTED,
|
||||||
|
id: '',
|
||||||
|
createdAt: undefined,
|
||||||
|
size: '',
|
||||||
|
price: '',
|
||||||
|
timeInForce: Types.OrderTimeInForce.TIME_IN_FORCE_FOK,
|
||||||
|
side: Types.Side.SIDE_BUY,
|
||||||
|
marketId: '',
|
||||||
|
})
|
||||||
|
).toBe('Insufficient asset balance');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return the correct rejection reason when order is stopped', () => {
|
||||||
|
expect(
|
||||||
|
getRejectionReason({
|
||||||
|
rejectionReason: null,
|
||||||
|
status: Types.OrderStatus.STATUS_STOPPED,
|
||||||
|
id: '',
|
||||||
|
createdAt: undefined,
|
||||||
|
size: '',
|
||||||
|
price: '',
|
||||||
|
timeInForce: Types.OrderTimeInForce.TIME_IN_FORCE_FOK,
|
||||||
|
side: Types.Side.SIDE_BUY,
|
||||||
|
marketId: '',
|
||||||
|
})
|
||||||
|
).toBe(
|
||||||
|
'Your Fill or Kill (FOK) order was not filled and it has been stopped'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('timeInForceLabel', () => {
|
||||||
|
it('should return the correct label for time in force', () => {
|
||||||
|
expect(timeInForceLabel(Types.OrderTimeInForce.TIME_IN_FORCE_FOK)).toBe(
|
||||||
|
`Fill or Kill (FOK)`
|
||||||
|
);
|
||||||
|
expect(timeInForceLabel(Types.OrderTimeInForce.TIME_IN_FORCE_GTC)).toBe(
|
||||||
|
`Good 'til Cancelled (GTC)`
|
||||||
|
);
|
||||||
|
expect(timeInForceLabel(Types.OrderTimeInForce.TIME_IN_FORCE_IOC)).toBe(
|
||||||
|
`Immediate or Cancel (IOC)`
|
||||||
|
);
|
||||||
|
expect(timeInForceLabel(Types.OrderTimeInForce.TIME_IN_FORCE_GTT)).toBe(
|
||||||
|
`Good 'til Time (GTT)`
|
||||||
|
);
|
||||||
|
expect(timeInForceLabel(Types.OrderTimeInForce.TIME_IN_FORCE_GFA)).toBe(
|
||||||
|
`Good for Auction (GFA)`
|
||||||
|
);
|
||||||
|
expect(timeInForceLabel(Types.OrderTimeInForce.TIME_IN_FORCE_GFN)).toBe(
|
||||||
|
`Good for Normal (GFN)`
|
||||||
|
);
|
||||||
|
expect(timeInForceLabel('')).toBe('');
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -82,10 +82,10 @@ export const getOrderToastIntent = (
|
|||||||
return Intent.Warning;
|
return Intent.Warning;
|
||||||
case Schema.OrderStatus.STATUS_REJECTED:
|
case Schema.OrderStatus.STATUS_REJECTED:
|
||||||
case Schema.OrderStatus.STATUS_STOPPED:
|
case Schema.OrderStatus.STATUS_STOPPED:
|
||||||
case Schema.OrderStatus.STATUS_CANCELLED:
|
|
||||||
return Intent.Danger;
|
return Intent.Danger;
|
||||||
case Schema.OrderStatus.STATUS_FILLED:
|
case Schema.OrderStatus.STATUS_FILLED:
|
||||||
case Schema.OrderStatus.STATUS_ACTIVE:
|
case Schema.OrderStatus.STATUS_ACTIVE:
|
||||||
|
case Schema.OrderStatus.STATUS_CANCELLED:
|
||||||
return Intent.Success;
|
return Intent.Success;
|
||||||
default:
|
default:
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -30,12 +30,12 @@ import {
|
|||||||
import { marginsDataProvider } from './margin-data-provider';
|
import { marginsDataProvider } from './margin-data-provider';
|
||||||
import { calculateMargins } from './margin-calculator';
|
import { calculateMargins } from './margin-calculator';
|
||||||
import type { Edge } from '@vegaprotocol/utils';
|
import type { Edge } from '@vegaprotocol/utils';
|
||||||
import { OrderStatus, Side } from '@vegaprotocol/types';
|
import { Side } from '@vegaprotocol/types';
|
||||||
import { marketInfoProvider } from '@vegaprotocol/market-info';
|
import { marketInfoProvider } from '@vegaprotocol/market-info';
|
||||||
import type { MarketInfoQuery } from '@vegaprotocol/market-info';
|
import type { MarketInfoQuery } from '@vegaprotocol/market-info';
|
||||||
import { marketDataProvider } from '@vegaprotocol/market-list';
|
import { marketDataProvider } from '@vegaprotocol/market-list';
|
||||||
import type { MarketData } from '@vegaprotocol/market-list';
|
import type { MarketData } from '@vegaprotocol/market-list';
|
||||||
import { ordersProvider } from '@vegaprotocol/orders';
|
import { activeOrdersProvider } from '@vegaprotocol/orders';
|
||||||
import type { OrderFieldsFragment } from '@vegaprotocol/orders';
|
import type { OrderFieldsFragment } from '@vegaprotocol/orders';
|
||||||
import type { PositionStatus } from '@vegaprotocol/types';
|
import type { PositionStatus } from '@vegaprotocol/types';
|
||||||
|
|
||||||
@@ -350,12 +350,9 @@ export const volumeAndMarginProvider = makeDerivedDataProvider<
|
|||||||
>(
|
>(
|
||||||
[
|
[
|
||||||
(callback, client, { partyId, marketId }) =>
|
(callback, client, { partyId, marketId }) =>
|
||||||
ordersProvider(callback, client, {
|
activeOrdersProvider(callback, client, {
|
||||||
partyId,
|
partyId,
|
||||||
marketIds: [marketId],
|
marketId,
|
||||||
filter: {
|
|
||||||
status: [OrderStatus.STATUS_ACTIVE, OrderStatus.STATUS_PARKED],
|
|
||||||
},
|
|
||||||
}),
|
}),
|
||||||
(callback, client, variables) =>
|
(callback, client, variables) =>
|
||||||
marketDataProvider(callback, client, { marketId: variables.marketId }),
|
marketDataProvider(callback, client, { marketId: variables.marketId }),
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import { useState, useEffect, useRef, useCallback } from 'react';
|
import { useState, useEffect, useRef, useMemo, useCallback } from 'react';
|
||||||
import throttle from 'lodash/throttle';
|
import throttle from 'lodash/throttle';
|
||||||
import isEqual from 'lodash/isEqual';
|
import isEqualWith from 'lodash/isEqualWith';
|
||||||
import { useApolloClient } from '@apollo/client';
|
import { useApolloClient } from '@apollo/client';
|
||||||
import { usePrevious } from './use-previous';
|
|
||||||
import type { OperationVariables } from '@apollo/client';
|
import type { OperationVariables } from '@apollo/client';
|
||||||
import type { Subscribe, Load, UpdateCallback } from '@vegaprotocol/utils';
|
import type { Subscribe, Load, UpdateCallback } from '@vegaprotocol/utils';
|
||||||
|
import { variablesIsEqualCustomizer } from '@vegaprotocol/utils';
|
||||||
|
|
||||||
export interface useDataProviderParams<
|
export interface useDataProviderParams<
|
||||||
Data,
|
Data,
|
||||||
@@ -62,13 +62,19 @@ export const useDataProvider = <
|
|||||||
const flushRef = useRef<(() => void) | undefined>(undefined);
|
const flushRef = useRef<(() => void) | undefined>(undefined);
|
||||||
const reloadRef = useRef<((force?: boolean) => void) | undefined>(undefined);
|
const reloadRef = useRef<((force?: boolean) => void) | undefined>(undefined);
|
||||||
const loadRef = useRef<Load<Data> | undefined>(undefined);
|
const loadRef = useRef<Load<Data> | undefined>(undefined);
|
||||||
const prevVariables = usePrevious(props.variables);
|
const variablesRef = useRef<Variables>(props.variables);
|
||||||
const [variables, setVariables] = useState(props.variables);
|
const variables = useMemo(() => {
|
||||||
useEffect(() => {
|
if (
|
||||||
if (!isEqual(prevVariables, props.variables)) {
|
!isEqualWith(
|
||||||
setVariables(props.variables);
|
variablesRef.current,
|
||||||
|
props.variables,
|
||||||
|
variablesIsEqualCustomizer
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
variablesRef.current = props.variables;
|
||||||
}
|
}
|
||||||
}, [props.variables, prevVariables]);
|
return variablesRef.current;
|
||||||
|
}, [props.variables]);
|
||||||
const flush = useCallback(() => {
|
const flush = useCallback(() => {
|
||||||
if (flushRef.current) {
|
if (flushRef.current) {
|
||||||
flushRef.current();
|
flushRef.current();
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ export * from './nav-dropdown';
|
|||||||
export * from './nav';
|
export * from './nav';
|
||||||
export * from './navigation';
|
export * from './navigation';
|
||||||
export * from './notification';
|
export * from './notification';
|
||||||
|
export * from './pagination';
|
||||||
export * from './popover';
|
export * from './popover';
|
||||||
export * from './progress-bar';
|
export * from './progress-bar';
|
||||||
export * from './radio-group';
|
export * from './radio-group';
|
||||||
|
|||||||
@@ -70,6 +70,8 @@ export const Notification = ({
|
|||||||
'text-vega-green dark:text-vega-green': intent === Intent.Success,
|
'text-vega-green dark:text-vega-green': intent === Intent.Success,
|
||||||
'text-yellow-600 dark:text-yellow': intent === Intent.Warning,
|
'text-yellow-600 dark:text-yellow': intent === Intent.Warning,
|
||||||
'text-vega-pink': intent === Intent.Danger,
|
'text-vega-pink': intent === Intent.Danger,
|
||||||
|
'mt-1': !!title,
|
||||||
|
'mt-[0.125rem]': !title,
|
||||||
},
|
},
|
||||||
'flex items-start mt-1'
|
'flex items-start mt-1'
|
||||||
)}
|
)}
|
||||||
@@ -78,11 +80,16 @@ export const Notification = ({
|
|||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col flex-grow items-start gap-1.5">
|
<div className="flex flex-col flex-grow items-start gap-1.5">
|
||||||
{title && (
|
{title && (
|
||||||
<div className="whitespace-nowrap overflow-hidden text-ellipsis uppercase leading-6">
|
<div
|
||||||
|
key="title"
|
||||||
|
className="whitespace-nowrap overflow-hidden text-ellipsis uppercase leading-6"
|
||||||
|
>
|
||||||
{title}
|
{title}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div className="text-sm [word-break:break-word]">{message}</div>
|
<div key="message" className="text-sm [word-break:break-word]">
|
||||||
|
{message}
|
||||||
|
</div>
|
||||||
{buttonProps && (
|
{buttonProps && (
|
||||||
<Button
|
<Button
|
||||||
size={buttonProps.size || 'sm'}
|
size={buttonProps.size || 'sm'}
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
export * from './pagination';
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
import { useState } from 'react';
|
||||||
|
import { Pagination } from './pagination';
|
||||||
|
|
||||||
|
import type { ComponentStory, ComponentMeta } from '@storybook/react';
|
||||||
|
export default {
|
||||||
|
title: 'Pagination',
|
||||||
|
component: Pagination,
|
||||||
|
} as ComponentMeta<typeof Pagination>;
|
||||||
|
|
||||||
|
const Template: ComponentStory<typeof Pagination> = (args) => {
|
||||||
|
const MAX_PAGE = 3;
|
||||||
|
const [page, setPage] = useState(1);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<Pagination
|
||||||
|
hasPrevPage={page !== 1}
|
||||||
|
hasNextPage={page < MAX_PAGE}
|
||||||
|
onBack={() => setPage(Math.max(1, page - 1))}
|
||||||
|
onNext={() => setPage(Math.min(MAX_PAGE, page + 1))}
|
||||||
|
>
|
||||||
|
Page {page}
|
||||||
|
</Pagination>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Default = Template.bind({});
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
import type { ReactNode } from 'react';
|
||||||
|
import { Button } from '../button';
|
||||||
|
import { Icon } from '../icon';
|
||||||
|
|
||||||
|
export type PaginationProps = {
|
||||||
|
hasPrevPage: boolean;
|
||||||
|
hasNextPage: boolean;
|
||||||
|
isLoading?: boolean;
|
||||||
|
children?: ReactNode;
|
||||||
|
onBack: () => void;
|
||||||
|
onNext: () => void;
|
||||||
|
onFirst?: () => void;
|
||||||
|
onLast?: () => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
const buttonClass = 'rounded-full w-[34px] h-[34px]';
|
||||||
|
|
||||||
|
export const Pagination = ({
|
||||||
|
hasPrevPage,
|
||||||
|
hasNextPage,
|
||||||
|
isLoading,
|
||||||
|
children,
|
||||||
|
onBack,
|
||||||
|
onNext,
|
||||||
|
onFirst,
|
||||||
|
onLast,
|
||||||
|
}: PaginationProps) => {
|
||||||
|
return (
|
||||||
|
<div className={'flex gap-2 my-2 items-center justify-center'}>
|
||||||
|
{onFirst && (
|
||||||
|
<Button
|
||||||
|
size="sm"
|
||||||
|
data-testid="goto-first-page"
|
||||||
|
disabled={isLoading || !hasPrevPage}
|
||||||
|
className={buttonClass}
|
||||||
|
onClick={onFirst}
|
||||||
|
>
|
||||||
|
<Icon name="double-chevron-left" ariaLabel="Back" />
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
<Button
|
||||||
|
size="sm"
|
||||||
|
data-testid="goto-previous-page"
|
||||||
|
disabled={isLoading || !hasPrevPage}
|
||||||
|
className={buttonClass}
|
||||||
|
onClick={onBack}
|
||||||
|
>
|
||||||
|
<Icon name="chevron-left" ariaLabel="Back" />
|
||||||
|
</Button>
|
||||||
|
{children}
|
||||||
|
<Button
|
||||||
|
size="sm"
|
||||||
|
data-testid="goto-next-page"
|
||||||
|
disabled={isLoading || !hasNextPage}
|
||||||
|
className={buttonClass}
|
||||||
|
onClick={onNext}
|
||||||
|
>
|
||||||
|
<Icon name="chevron-right" ariaLabel="Next" />
|
||||||
|
</Button>
|
||||||
|
{onLast && (
|
||||||
|
<Button
|
||||||
|
size="sm"
|
||||||
|
data-testid="goto-last-page"
|
||||||
|
disabled={isLoading || !hasNextPage}
|
||||||
|
className={buttonClass}
|
||||||
|
onClick={onLast}
|
||||||
|
>
|
||||||
|
<Icon name="double-chevron-right" ariaLabel="Back" />
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -45,7 +45,7 @@ type CombinedData = {
|
|||||||
|
|
||||||
type SubscriptionData = QueryData;
|
type SubscriptionData = QueryData;
|
||||||
type Delta = Data;
|
type Delta = Data;
|
||||||
type Variables = { var: string };
|
type Variables = { var: string; filter?: string[] };
|
||||||
|
|
||||||
const update = jest.fn<
|
const update = jest.fn<
|
||||||
ReturnType<Update<Data, Delta, Variables>>,
|
ReturnType<Update<Data, Delta, Variables>>,
|
||||||
@@ -231,10 +231,14 @@ describe('data provider', () => {
|
|||||||
clientSubscribeSubscribe.mockClear();
|
clientSubscribeSubscribe.mockClear();
|
||||||
});
|
});
|
||||||
it('memoize instance and unsubscribe if no subscribers', () => {
|
it('memoize instance and unsubscribe if no subscribers', () => {
|
||||||
const subscription1 = subscribe(jest.fn(), client, variables);
|
const subscription1 = subscribe(jest.fn(), client, {
|
||||||
const subscription2 = subscribe(jest.fn(), client, { ...variables });
|
...variables,
|
||||||
// const subscription1 = subscribe(jest.fn(), client);
|
filter: ['1', '2'],
|
||||||
// const subscription2 = subscribe(jest.fn(), client);
|
});
|
||||||
|
const subscription2 = subscribe(jest.fn(), client, {
|
||||||
|
...variables,
|
||||||
|
filter: ['2', '1'],
|
||||||
|
});
|
||||||
expect(clientSubscribeSubscribe.mock.calls.length).toEqual(1);
|
expect(clientSubscribeSubscribe.mock.calls.length).toEqual(1);
|
||||||
subscription1.unsubscribe();
|
subscription1.unsubscribe();
|
||||||
expect(clientSubscribeUnsubscribe.mock.calls.length).toEqual(0);
|
expect(clientSubscribeUnsubscribe.mock.calls.length).toEqual(0);
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import type {
|
|||||||
} from '@apollo/client';
|
} from '@apollo/client';
|
||||||
import type { GraphQLErrors } from '@apollo/client/errors';
|
import type { GraphQLErrors } from '@apollo/client/errors';
|
||||||
import type { Subscription } from 'zen-observable-ts';
|
import type { Subscription } from 'zen-observable-ts';
|
||||||
import isEqual from 'lodash/isEqual';
|
import isEqualWith from 'lodash/isEqualWith';
|
||||||
import { isNotFoundGraphQLError } from './apollo-client';
|
import { isNotFoundGraphQLError } from './apollo-client';
|
||||||
import type * as Schema from '@vegaprotocol/types';
|
import type * as Schema from '@vegaprotocol/types';
|
||||||
interface UpdateData<Data, Delta> {
|
interface UpdateData<Data, Delta> {
|
||||||
@@ -104,7 +104,11 @@ interface GetTotalCount<QueryData> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface GetDelta<SubscriptionData, Delta, Variables> {
|
interface GetDelta<SubscriptionData, Delta, Variables> {
|
||||||
(subscriptionData: SubscriptionData, variables?: Variables): Delta;
|
(
|
||||||
|
subscriptionData: SubscriptionData,
|
||||||
|
variables: Variables,
|
||||||
|
client: ApolloClient<object>
|
||||||
|
): Delta;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type Node = { id: string };
|
export type Node = { id: string };
|
||||||
@@ -420,7 +424,7 @@ function makeDataProviderInternal<
|
|||||||
if (!subscriptionData || !getDelta || !update) {
|
if (!subscriptionData || !getDelta || !update) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const delta = getDelta(subscriptionData, variables);
|
const delta = getDelta(subscriptionData, variables, client);
|
||||||
if (loading) {
|
if (loading) {
|
||||||
updateQueue.push(delta);
|
updateQueue.push(delta);
|
||||||
} else {
|
} else {
|
||||||
@@ -512,11 +516,26 @@ function makeDataProviderInternal<
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compares two arrays assuming that they are sets of primitive values, used to compare gql query variables
|
||||||
|
*/
|
||||||
|
export const variablesIsEqualCustomizer: NonNullable<
|
||||||
|
Parameters<typeof isEqualWith>['2']
|
||||||
|
> = (value, other) => {
|
||||||
|
if (Array.isArray(value) && Array.isArray(other)) {
|
||||||
|
return (
|
||||||
|
value.length === other.length &&
|
||||||
|
new Set([...value, ...other]).size === value.length
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Memoizes data provider instances using query variables as cache key
|
* Memoizes data provider instances using query variables as cache key
|
||||||
*
|
*
|
||||||
* @param fn
|
* @param fn
|
||||||
* @returns subscibe function
|
* @returns subscribe function
|
||||||
*/
|
*/
|
||||||
const memoize = <
|
const memoize = <
|
||||||
Data,
|
Data,
|
||||||
@@ -530,7 +549,9 @@ const memoize = <
|
|||||||
variables?: Variables;
|
variables?: Variables;
|
||||||
}[] = [];
|
}[] = [];
|
||||||
return (variables?: Variables) => {
|
return (variables?: Variables) => {
|
||||||
const cached = cache.find((c) => isEqual(c.variables, variables));
|
const cached = cache.find((c) =>
|
||||||
|
isEqualWith(c.variables, variables, variablesIsEqualCustomizer)
|
||||||
|
);
|
||||||
if (cached) {
|
if (cached) {
|
||||||
return cached.subscribe;
|
return cached.subscribe;
|
||||||
}
|
}
|
||||||
@@ -582,8 +603,10 @@ export function makeDataProvider<
|
|||||||
const getInstance = memoize<Data, Delta, Variables>(() =>
|
const getInstance = memoize<Data, Delta, Variables>(() =>
|
||||||
makeDataProviderInternal(params)
|
makeDataProviderInternal(params)
|
||||||
);
|
);
|
||||||
return (callback, client, variables) =>
|
return (callback, client, variables) => {
|
||||||
getInstance(variables)(callback, client, variables);
|
const instance = getInstance(variables)(callback, client, variables);
|
||||||
|
return instance;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -605,7 +628,9 @@ export type CombineDerivedData<
|
|||||||
> = (
|
> = (
|
||||||
data: DerivedPart<Variables>['data'][],
|
data: DerivedPart<Variables>['data'][],
|
||||||
variables: Variables,
|
variables: Variables,
|
||||||
prevData: Data | null
|
prevData: Data | null,
|
||||||
|
parts: DerivedPart<Variables>[],
|
||||||
|
subscriptions?: ReturnType<DependencySubscribe<Variables>>[]
|
||||||
) => Data | null;
|
) => Data | null;
|
||||||
|
|
||||||
export type CombineDerivedDelta<
|
export type CombineDerivedDelta<
|
||||||
@@ -687,7 +712,9 @@ function makeDerivedDataProviderInternal<
|
|||||||
? combineData(
|
? combineData(
|
||||||
parts.map((part) => part.data),
|
parts.map((part) => part.data),
|
||||||
variables,
|
variables,
|
||||||
data
|
data,
|
||||||
|
parts,
|
||||||
|
subscriptions
|
||||||
)
|
)
|
||||||
: data;
|
: data;
|
||||||
if (
|
if (
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import {
|
|||||||
removeDecimal,
|
removeDecimal,
|
||||||
required,
|
required,
|
||||||
isAssetTypeERC20,
|
isAssetTypeERC20,
|
||||||
|
formatNumber,
|
||||||
} from '@vegaprotocol/utils';
|
} from '@vegaprotocol/utils';
|
||||||
import { t } from '@vegaprotocol/i18n';
|
import { t } from '@vegaprotocol/i18n';
|
||||||
import { useLocalStorage } from '@vegaprotocol/react-helpers';
|
import { useLocalStorage } from '@vegaprotocol/react-helpers';
|
||||||
@@ -14,12 +15,16 @@ import {
|
|||||||
FormGroup,
|
FormGroup,
|
||||||
Input,
|
Input,
|
||||||
InputError,
|
InputError,
|
||||||
|
Notification,
|
||||||
RichSelect,
|
RichSelect,
|
||||||
|
ExternalLink,
|
||||||
|
Intent,
|
||||||
} from '@vegaprotocol/ui-toolkit';
|
} from '@vegaprotocol/ui-toolkit';
|
||||||
import { useWeb3React } from '@web3-react/core';
|
import { useWeb3React } from '@web3-react/core';
|
||||||
import BigNumber from 'bignumber.js';
|
import BigNumber from 'bignumber.js';
|
||||||
import type { ButtonHTMLAttributes } from 'react';
|
import type { ButtonHTMLAttributes } from 'react';
|
||||||
import type { ControllerRenderProps } from 'react-hook-form';
|
import type { ControllerRenderProps } from 'react-hook-form';
|
||||||
|
import { formatDistanceToNow } from 'date-fns';
|
||||||
import { useForm, Controller, useWatch } from 'react-hook-form';
|
import { useForm, Controller, useWatch } from 'react-hook-form';
|
||||||
import type { WithdrawalArgs } from './use-create-withdraw';
|
import type { WithdrawalArgs } from './use-create-withdraw';
|
||||||
import { WithdrawLimits } from './withdraw-limits';
|
import { WithdrawLimits } from './withdraw-limits';
|
||||||
@@ -45,6 +50,47 @@ export interface WithdrawFormProps {
|
|||||||
submitWithdraw: (withdrawal: WithdrawalArgs) => void;
|
submitWithdraw: (withdrawal: WithdrawalArgs) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const WithdrawDelayNotification = ({
|
||||||
|
threshold,
|
||||||
|
delay,
|
||||||
|
symbol,
|
||||||
|
decimals,
|
||||||
|
}: {
|
||||||
|
threshold: BigNumber;
|
||||||
|
delay: number | undefined;
|
||||||
|
symbol: string;
|
||||||
|
decimals: number;
|
||||||
|
}) => {
|
||||||
|
const replacements = [
|
||||||
|
symbol,
|
||||||
|
delay ? formatDistanceToNow(Date.now() + delay * 1000) : ' ',
|
||||||
|
];
|
||||||
|
return (
|
||||||
|
<Notification
|
||||||
|
intent={Intent.Warning}
|
||||||
|
testId={
|
||||||
|
threshold.isFinite()
|
||||||
|
? 'amount-withdrawal-delay-notification'
|
||||||
|
: 'withdrawals-delay-notification'
|
||||||
|
}
|
||||||
|
message={[
|
||||||
|
!threshold.isFinite()
|
||||||
|
? t('All %s withdrawals are subject to a %s delay.', replacements)
|
||||||
|
: t('Withdrawals of %s %s or more will be delayed for %s.', [
|
||||||
|
formatNumber(threshold, decimals),
|
||||||
|
...replacements,
|
||||||
|
]),
|
||||||
|
<ExternalLink
|
||||||
|
className="ml-1"
|
||||||
|
href="https://docs.vega.xyz/testnet/concepts/deposits-withdrawals#withdrawal-limits"
|
||||||
|
>
|
||||||
|
{t('Read more')}
|
||||||
|
</ExternalLink>,
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
export const WithdrawForm = ({
|
export const WithdrawForm = ({
|
||||||
assets,
|
assets,
|
||||||
balance,
|
balance,
|
||||||
@@ -113,6 +159,12 @@ export const WithdrawForm = ({
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const showWithdrawDelayNotification =
|
||||||
|
delay &&
|
||||||
|
selectedAsset &&
|
||||||
|
(!threshold.isFinite() ||
|
||||||
|
new BigNumber(amount).isGreaterThanOrEqualTo(threshold));
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="mb-4 text-sm">
|
<div className="mb-4 text-sm">
|
||||||
@@ -206,6 +258,16 @@ export const WithdrawForm = ({
|
|||||||
{t('Use maximum')}
|
{t('Use maximum')}
|
||||||
</UseButton>
|
</UseButton>
|
||||||
)}
|
)}
|
||||||
|
{showWithdrawDelayNotification && (
|
||||||
|
<div className="mt-2">
|
||||||
|
<WithdrawDelayNotification
|
||||||
|
threshold={threshold}
|
||||||
|
symbol={selectedAsset.symbol}
|
||||||
|
decimals={selectedAsset.decimals}
|
||||||
|
delay={delay}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
<Button
|
<Button
|
||||||
data-testid="submit-withdrawal"
|
data-testid="submit-withdrawal"
|
||||||
|
|||||||
@@ -1,7 +1,12 @@
|
|||||||
import type { Asset } from '@vegaprotocol/assets';
|
import type { Asset } from '@vegaprotocol/assets';
|
||||||
import { t } from '@vegaprotocol/i18n';
|
import { t } from '@vegaprotocol/i18n';
|
||||||
import { CompactNumber } from '@vegaprotocol/react-helpers';
|
import { CompactNumber } from '@vegaprotocol/react-helpers';
|
||||||
import { KeyValueTable, KeyValueTableRow } from '@vegaprotocol/ui-toolkit';
|
import { WITHDRAW_THRESHOLD_TOOLTIP_TEXT } from '@vegaprotocol/assets';
|
||||||
|
import {
|
||||||
|
KeyValueTable,
|
||||||
|
KeyValueTableRow,
|
||||||
|
Tooltip,
|
||||||
|
} from '@vegaprotocol/ui-toolkit';
|
||||||
import BigNumber from 'bignumber.js';
|
import BigNumber from 'bignumber.js';
|
||||||
import { formatDistanceToNow } from 'date-fns';
|
import { formatDistanceToNow } from 'date-fns';
|
||||||
|
|
||||||
@@ -25,7 +30,13 @@ export const WithdrawLimits = ({
|
|||||||
? formatDistanceToNow(Date.now() + delay * 1000)
|
? formatDistanceToNow(Date.now() + delay * 1000)
|
||||||
: t('None');
|
: t('None');
|
||||||
|
|
||||||
const limits = [
|
const limits: {
|
||||||
|
key: string;
|
||||||
|
label: string;
|
||||||
|
value: string | JSX.Element;
|
||||||
|
rawValue?: BigNumber;
|
||||||
|
tooltip?: string;
|
||||||
|
}[] = [
|
||||||
{
|
{
|
||||||
key: 'BALANCE_AVAILABLE',
|
key: 'BALANCE_AVAILABLE',
|
||||||
label: t('Balance available'),
|
label: t('Balance available'),
|
||||||
@@ -36,24 +47,35 @@ export const WithdrawLimits = ({
|
|||||||
'-'
|
'-'
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
{
|
];
|
||||||
|
if (threshold.isFinite()) {
|
||||||
|
limits.push({
|
||||||
key: 'WITHDRAWAL_THRESHOLD',
|
key: 'WITHDRAWAL_THRESHOLD',
|
||||||
label: t('Delayed withdrawal threshold'),
|
label: t('Delayed withdrawal threshold'),
|
||||||
|
tooltip: WITHDRAW_THRESHOLD_TOOLTIP_TEXT,
|
||||||
rawValue: threshold,
|
rawValue: threshold,
|
||||||
value: <CompactNumber number={threshold} decimals={asset.decimals} />,
|
value: <CompactNumber number={threshold} decimals={asset.decimals} />,
|
||||||
},
|
});
|
||||||
{
|
}
|
||||||
key: 'DELAY_TIME',
|
limits.push({
|
||||||
label: t('Delay time'),
|
key: 'DELAY_TIME',
|
||||||
value: delayTime,
|
label: t('Delay time'),
|
||||||
},
|
value: delayTime,
|
||||||
];
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<KeyValueTable>
|
<KeyValueTable>
|
||||||
{limits.map(({ key, label, rawValue, value }) => (
|
{limits.map(({ key, label, rawValue, value, tooltip }) => (
|
||||||
<KeyValueTableRow key={key}>
|
<KeyValueTableRow key={key}>
|
||||||
<div data-testid={`${key}_label`}>{label}</div>
|
<div data-testid={`${key}_label`}>
|
||||||
|
{tooltip ? (
|
||||||
|
<Tooltip description={tooltip}>
|
||||||
|
<span>{label}</span>
|
||||||
|
</Tooltip>
|
||||||
|
) : (
|
||||||
|
label
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
<div
|
<div
|
||||||
data-testid={`${key}_value`}
|
data-testid={`${key}_value`}
|
||||||
className="truncate"
|
className="truncate"
|
||||||
|
|||||||
@@ -12,15 +12,17 @@ jest.mock('@web3-react/core', () => ({
|
|||||||
useWeb3React: () => ({ account: ethereumAddress }),
|
useWeb3React: () => ({ account: ethereumAddress }),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
const withdrawAsset = {
|
||||||
|
asset,
|
||||||
|
balance: new BigNumber(1),
|
||||||
|
min: new BigNumber(0.0000001),
|
||||||
|
threshold: new BigNumber(1000),
|
||||||
|
delay: 10,
|
||||||
|
handleSelectAsset: jest.fn(),
|
||||||
|
};
|
||||||
|
|
||||||
jest.mock('./use-withdraw-asset', () => ({
|
jest.mock('./use-withdraw-asset', () => ({
|
||||||
useWithdrawAsset: () => ({
|
useWithdrawAsset: () => withdrawAsset,
|
||||||
asset,
|
|
||||||
balance: new BigNumber(1),
|
|
||||||
min: new BigNumber(0.0000001),
|
|
||||||
threshold: new BigNumber(1000),
|
|
||||||
delay: 10,
|
|
||||||
handleSelectAsset: jest.fn(),
|
|
||||||
}),
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
jest.mock('@vegaprotocol/web3', () => ({
|
jest.mock('@vegaprotocol/web3', () => ({
|
||||||
@@ -109,4 +111,25 @@ describe('WithdrawManager', () => {
|
|||||||
});
|
});
|
||||||
fireEvent.submit(screen.getByTestId('withdraw-form'));
|
fireEvent.submit(screen.getByTestId('withdraw-form'));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
it('shows withdraw delay notification if amount greater than threshold', async () => {
|
||||||
|
render(generateJsx(props));
|
||||||
|
fireEvent.change(screen.getByLabelText('Amount'), {
|
||||||
|
target: { value: '1000' },
|
||||||
|
});
|
||||||
|
expect(
|
||||||
|
await screen.findByTestId('amount-withdrawal-delay-notification')
|
||||||
|
).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('shows withdraw delay notification if threshold is 0', async () => {
|
||||||
|
withdrawAsset.threshold = new BigNumber(Infinity);
|
||||||
|
render(generateJsx(props));
|
||||||
|
fireEvent.change(screen.getByLabelText('Amount'), {
|
||||||
|
target: { value: '0.01' },
|
||||||
|
});
|
||||||
|
expect(
|
||||||
|
await screen.findByTestId('withdrawals-delay-notification')
|
||||||
|
).toBeInTheDocument();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
+2
-1
@@ -70,7 +70,7 @@
|
|||||||
"react-hook-form": "^7.27.0",
|
"react-hook-form": "^7.27.0",
|
||||||
"react-i18next": "^11.11.4",
|
"react-i18next": "^11.11.4",
|
||||||
"react-intersection-observer": "^9.2.2",
|
"react-intersection-observer": "^9.2.2",
|
||||||
"react-markdown": "^8.0.5",
|
"react-markdown": "^8.0.6",
|
||||||
"react-router-dom": "^6.9.0",
|
"react-router-dom": "^6.9.0",
|
||||||
"react-syntax-highlighter": "^15.4.5",
|
"react-syntax-highlighter": "^15.4.5",
|
||||||
"react-use-websocket": "^3.0.0",
|
"react-use-websocket": "^3.0.0",
|
||||||
@@ -150,6 +150,7 @@
|
|||||||
"babel-jest": "27.5.1",
|
"babel-jest": "27.5.1",
|
||||||
"babel-loader": "8.1.0",
|
"babel-loader": "8.1.0",
|
||||||
"cypress": "^11.2.0",
|
"cypress": "^11.2.0",
|
||||||
|
"cypress-mochawesome-reporter": "^3.3.0",
|
||||||
"cypress-real-events": "^1.7.1",
|
"cypress-real-events": "^1.7.1",
|
||||||
"dotenv": "^16.0.1",
|
"dotenv": "^16.0.1",
|
||||||
"eslint": "8.15.0",
|
"eslint": "8.15.0",
|
||||||
|
|||||||
@@ -11008,6 +11008,16 @@ cyclist@^1.0.1:
|
|||||||
resolved "https://registry.yarnpkg.com/cyclist/-/cyclist-1.0.1.tgz#596e9698fd0c80e12038c2b82d6eb1b35b6224d9"
|
resolved "https://registry.yarnpkg.com/cyclist/-/cyclist-1.0.1.tgz#596e9698fd0c80e12038c2b82d6eb1b35b6224d9"
|
||||||
integrity sha512-NJGVKPS81XejHcLhaLJS7plab0fK3slPh11mESeeDq2W4ZI5kUKK/LRRdVDvjJseojbPB7ZwjnyOybg3Igea/A==
|
integrity sha512-NJGVKPS81XejHcLhaLJS7plab0fK3slPh11mESeeDq2W4ZI5kUKK/LRRdVDvjJseojbPB7ZwjnyOybg3Igea/A==
|
||||||
|
|
||||||
|
cypress-mochawesome-reporter@^3.3.0:
|
||||||
|
version "3.3.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/cypress-mochawesome-reporter/-/cypress-mochawesome-reporter-3.3.0.tgz#cfcd07bd15b4773917f2b2a240178c7cdb310a51"
|
||||||
|
integrity sha512-X4HU1JpuB62MXLh46660KmIs/L6noWV2KpxaXPDorz1zwgj26NN+BPCLP80D9cCFUwX3hNH0pKFZDwVR7vM8wg==
|
||||||
|
dependencies:
|
||||||
|
fs-extra "^10.0.1"
|
||||||
|
mochawesome "^7.1.3"
|
||||||
|
mochawesome-merge "^4.2.1"
|
||||||
|
mochawesome-report-generator "^6.2.0"
|
||||||
|
|
||||||
cypress-real-events@^1.7.1:
|
cypress-real-events@^1.7.1:
|
||||||
version "1.7.1"
|
version "1.7.1"
|
||||||
resolved "https://registry.yarnpkg.com/cypress-real-events/-/cypress-real-events-1.7.1.tgz#8f430d67c29ea4f05b9c5b0311780120cbc9b935"
|
resolved "https://registry.yarnpkg.com/cypress-real-events/-/cypress-real-events-1.7.1.tgz#8f430d67c29ea4f05b9c5b0311780120cbc9b935"
|
||||||
@@ -11291,6 +11301,11 @@ date-fns@^2.17.0, date-fns@^2.28.0:
|
|||||||
resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.29.3.tgz#27402d2fc67eb442b511b70bbdf98e6411cd68a8"
|
resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.29.3.tgz#27402d2fc67eb442b511b70bbdf98e6411cd68a8"
|
||||||
integrity sha512-dDCnyH2WnnKusqvZZ6+jA1O51Ibt8ZMRNkDZdyAyK4YfbDwa/cEmuztzG5pk6hqlp9aSBPYcjOlktquahGwGeA==
|
integrity sha512-dDCnyH2WnnKusqvZZ6+jA1O51Ibt8ZMRNkDZdyAyK4YfbDwa/cEmuztzG5pk6hqlp9aSBPYcjOlktquahGwGeA==
|
||||||
|
|
||||||
|
dateformat@^4.5.1:
|
||||||
|
version "4.6.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-4.6.3.tgz#556fa6497e5217fedb78821424f8a1c22fa3f4b5"
|
||||||
|
integrity sha512-2P0p0pFGzHS5EMnhdxQi7aJN+iMheud0UhG4dlE1DLAlvL8JHjJJTX/CSm4JXwV0Ka5nGk3zC5mcb5bUQUxxMA==
|
||||||
|
|
||||||
dayjs@^1.10.4:
|
dayjs@^1.10.4:
|
||||||
version "1.11.5"
|
version "1.11.5"
|
||||||
resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.5.tgz#00e8cc627f231f9499c19b38af49f56dc0ac5e93"
|
resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.5.tgz#00e8cc627f231f9499c19b38af49f56dc0ac5e93"
|
||||||
@@ -12080,7 +12095,7 @@ escape-goat@^2.0.0:
|
|||||||
resolved "https://registry.yarnpkg.com/escape-goat/-/escape-goat-2.1.1.tgz#1b2dc77003676c457ec760b2dc68edb648188675"
|
resolved "https://registry.yarnpkg.com/escape-goat/-/escape-goat-2.1.1.tgz#1b2dc77003676c457ec760b2dc68edb648188675"
|
||||||
integrity sha512-8/uIhbG12Csjy2JEW7D9pHbreaVaS/OpN3ycnyvElTdwM5n6GY6W6e2IPemfvGZeUMqZ9A/3GqIZMgKnBhAw/Q==
|
integrity sha512-8/uIhbG12Csjy2JEW7D9pHbreaVaS/OpN3ycnyvElTdwM5n6GY6W6e2IPemfvGZeUMqZ9A/3GqIZMgKnBhAw/Q==
|
||||||
|
|
||||||
escape-html@~1.0.3:
|
escape-html@^1.0.3, escape-html@~1.0.3:
|
||||||
version "1.0.3"
|
version "1.0.3"
|
||||||
resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988"
|
resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988"
|
||||||
integrity sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==
|
integrity sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==
|
||||||
@@ -13261,7 +13276,7 @@ fs-constants@^1.0.0:
|
|||||||
resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad"
|
resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad"
|
||||||
integrity sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==
|
integrity sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==
|
||||||
|
|
||||||
fs-extra@^10.0.0, fs-extra@^10.1.0:
|
fs-extra@^10.0.0, fs-extra@^10.0.1, fs-extra@^10.1.0:
|
||||||
version "10.1.0"
|
version "10.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-10.1.0.tgz#02873cfbc4084dde127eaa5f9905eef2325d1abf"
|
resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-10.1.0.tgz#02873cfbc4084dde127eaa5f9905eef2325d1abf"
|
||||||
integrity sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==
|
integrity sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==
|
||||||
@@ -13270,6 +13285,15 @@ fs-extra@^10.0.0, fs-extra@^10.1.0:
|
|||||||
jsonfile "^6.0.1"
|
jsonfile "^6.0.1"
|
||||||
universalify "^2.0.0"
|
universalify "^2.0.0"
|
||||||
|
|
||||||
|
fs-extra@^7.0.1:
|
||||||
|
version "7.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-7.0.1.tgz#4f189c44aa123b895f722804f55ea23eadc348e9"
|
||||||
|
integrity sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==
|
||||||
|
dependencies:
|
||||||
|
graceful-fs "^4.1.2"
|
||||||
|
jsonfile "^4.0.0"
|
||||||
|
universalify "^0.1.0"
|
||||||
|
|
||||||
fs-extra@^8.1, fs-extra@^8.1.0:
|
fs-extra@^8.1, fs-extra@^8.1.0:
|
||||||
version "8.1.0"
|
version "8.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-8.1.0.tgz#49d43c45a88cd9677668cb7be1b46efdb8d2e1c0"
|
resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-8.1.0.tgz#49d43c45a88cd9677668cb7be1b46efdb8d2e1c0"
|
||||||
@@ -13329,6 +13353,11 @@ fsevents@^2.1.2, fsevents@^2.3.2, fsevents@~2.3.2:
|
|||||||
resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a"
|
resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a"
|
||||||
integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==
|
integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==
|
||||||
|
|
||||||
|
fsu@^1.1.1:
|
||||||
|
version "1.1.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/fsu/-/fsu-1.1.1.tgz#bd36d3579907c59d85b257a75b836aa9e0c31834"
|
||||||
|
integrity sha512-xQVsnjJ/5pQtcKh+KjUoZGzVWn4uNkchxTF6Lwjr4Gf7nQr8fmUfhKJ62zE77+xQg9xnxi5KUps7XGs+VC986A==
|
||||||
|
|
||||||
function-bind@^1.1.1:
|
function-bind@^1.1.1:
|
||||||
version "1.1.1"
|
version "1.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d"
|
resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d"
|
||||||
@@ -16467,11 +16496,21 @@ lodash.isboolean@^3.0.3:
|
|||||||
resolved "https://registry.yarnpkg.com/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz#6c2e171db2a257cd96802fd43b01b20d5f5870f6"
|
resolved "https://registry.yarnpkg.com/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz#6c2e171db2a257cd96802fd43b01b20d5f5870f6"
|
||||||
integrity sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==
|
integrity sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==
|
||||||
|
|
||||||
|
lodash.isempty@^4.4.0:
|
||||||
|
version "4.4.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/lodash.isempty/-/lodash.isempty-4.4.0.tgz#6f86cbedd8be4ec987be9aaf33c9684db1b31e7e"
|
||||||
|
integrity sha512-oKMuF3xEeqDltrGMfDxAPGIVMSSRv8tbRSODbrs4KGsRRLEhrW8N8Rd4DRgB2+621hY8A8XwwrTVhXWpxFvMzg==
|
||||||
|
|
||||||
lodash.isequal@^4.5.0:
|
lodash.isequal@^4.5.0:
|
||||||
version "4.5.0"
|
version "4.5.0"
|
||||||
resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0"
|
resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0"
|
||||||
integrity sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==
|
integrity sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==
|
||||||
|
|
||||||
|
lodash.isfunction@^3.0.9:
|
||||||
|
version "3.0.9"
|
||||||
|
resolved "https://registry.yarnpkg.com/lodash.isfunction/-/lodash.isfunction-3.0.9.tgz#06de25df4db327ac931981d1bdb067e5af68d051"
|
||||||
|
integrity sha512-AirXNj15uRIMMPihnkInB4i3NHeb4iBtNg9WRWuK2o31S+ePwwNmDPaTL3o7dTJ+VXNZim7rFs4rxN4YU1oUJw==
|
||||||
|
|
||||||
lodash.isinteger@^4.0.4:
|
lodash.isinteger@^4.0.4:
|
||||||
version "4.0.4"
|
version "4.0.4"
|
||||||
resolved "https://registry.yarnpkg.com/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz#619c0af3d03f8b04c31f5882840b77b11cd68343"
|
resolved "https://registry.yarnpkg.com/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz#619c0af3d03f8b04c31f5882840b77b11cd68343"
|
||||||
@@ -16482,6 +16521,11 @@ lodash.isnumber@^3.0.3:
|
|||||||
resolved "https://registry.yarnpkg.com/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz#3ce76810c5928d03352301ac287317f11c0b1ffc"
|
resolved "https://registry.yarnpkg.com/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz#3ce76810c5928d03352301ac287317f11c0b1ffc"
|
||||||
integrity sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==
|
integrity sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==
|
||||||
|
|
||||||
|
lodash.isobject@^3.0.2:
|
||||||
|
version "3.0.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/lodash.isobject/-/lodash.isobject-3.0.2.tgz#3c8fb8d5b5bf4bf90ae06e14f2a530a4ed935e1d"
|
||||||
|
integrity sha512-3/Qptq2vr7WeJbB4KHUSKlq8Pl7ASXi3UG6CMbBm8WRtXi8+GHm7mKaU3urfpSEzWe2wCIChs6/sdocUsTKJiA==
|
||||||
|
|
||||||
lodash.isplainobject@^4.0.6:
|
lodash.isplainobject@^4.0.6:
|
||||||
version "4.0.6"
|
version "4.0.6"
|
||||||
resolved "https://registry.yarnpkg.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz#7c526a52d89b45c45cc690b88163be0497f550cb"
|
resolved "https://registry.yarnpkg.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz#7c526a52d89b45c45cc690b88163be0497f550cb"
|
||||||
@@ -17386,6 +17430,49 @@ mkdirp@^0.5.1, mkdirp@^0.5.3, mkdirp@^0.5.5, mkdirp@^0.5.6, mkdirp@~0.5.1:
|
|||||||
dependencies:
|
dependencies:
|
||||||
minimist "^1.2.6"
|
minimist "^1.2.6"
|
||||||
|
|
||||||
|
mochawesome-merge@^4.2.1:
|
||||||
|
version "4.3.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/mochawesome-merge/-/mochawesome-merge-4.3.0.tgz#7cc5d5730c7d8d1b034c8d8fecf3cd36e0010297"
|
||||||
|
integrity sha512-1roR6g+VUlfdaRmL8dCiVpKiaUhbPVm1ZQYUM6zHX46mWk+tpsKVZR6ba98k2zc8nlPvYd71yn5gyH970pKBSw==
|
||||||
|
dependencies:
|
||||||
|
fs-extra "^7.0.1"
|
||||||
|
glob "^7.1.6"
|
||||||
|
yargs "^15.3.1"
|
||||||
|
|
||||||
|
mochawesome-report-generator@^6.2.0:
|
||||||
|
version "6.2.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/mochawesome-report-generator/-/mochawesome-report-generator-6.2.0.tgz#65a30a11235ba7a68e1cf0ca1df80d764b93ae78"
|
||||||
|
integrity sha512-Ghw8JhQFizF0Vjbtp9B0i//+BOkV5OWcQCPpbO0NGOoxV33o+gKDYU0Pr2pGxkIHnqZ+g5mYiXF7GMNgAcDpSg==
|
||||||
|
dependencies:
|
||||||
|
chalk "^4.1.2"
|
||||||
|
dateformat "^4.5.1"
|
||||||
|
escape-html "^1.0.3"
|
||||||
|
fs-extra "^10.0.0"
|
||||||
|
fsu "^1.1.1"
|
||||||
|
lodash.isfunction "^3.0.9"
|
||||||
|
opener "^1.5.2"
|
||||||
|
prop-types "^15.7.2"
|
||||||
|
tcomb "^3.2.17"
|
||||||
|
tcomb-validation "^3.3.0"
|
||||||
|
validator "^13.6.0"
|
||||||
|
yargs "^17.2.1"
|
||||||
|
|
||||||
|
mochawesome@^7.1.3:
|
||||||
|
version "7.1.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/mochawesome/-/mochawesome-7.1.3.tgz#07b358138f37f5b07b51a1b255d84babfa36fa83"
|
||||||
|
integrity sha512-Vkb3jR5GZ1cXohMQQ73H3cZz7RoxGjjUo0G5hu0jLaW+0FdUxUwg3Cj29bqQdh0rFcnyV06pWmqmi5eBPnEuNQ==
|
||||||
|
dependencies:
|
||||||
|
chalk "^4.1.2"
|
||||||
|
diff "^5.0.0"
|
||||||
|
json-stringify-safe "^5.0.1"
|
||||||
|
lodash.isempty "^4.4.0"
|
||||||
|
lodash.isfunction "^3.0.9"
|
||||||
|
lodash.isobject "^3.0.2"
|
||||||
|
lodash.isstring "^4.0.1"
|
||||||
|
mochawesome-report-generator "^6.2.0"
|
||||||
|
strip-ansi "^6.0.1"
|
||||||
|
uuid "^8.3.2"
|
||||||
|
|
||||||
mock-apollo-client@^1.2.0:
|
mock-apollo-client@^1.2.0:
|
||||||
version "1.2.1"
|
version "1.2.1"
|
||||||
resolved "https://registry.yarnpkg.com/mock-apollo-client/-/mock-apollo-client-1.2.1.tgz#e3bfdc3ff73b1fea28fa7e91ec82e43ba8cbfa39"
|
resolved "https://registry.yarnpkg.com/mock-apollo-client/-/mock-apollo-client-1.2.1.tgz#e3bfdc3ff73b1fea28fa7e91ec82e43ba8cbfa39"
|
||||||
@@ -17975,7 +18062,7 @@ open@^8.0.9, open@^8.4.0:
|
|||||||
is-docker "^2.1.1"
|
is-docker "^2.1.1"
|
||||||
is-wsl "^2.2.0"
|
is-wsl "^2.2.0"
|
||||||
|
|
||||||
opener@^1.5.1:
|
opener@^1.5.1, opener@^1.5.2:
|
||||||
version "1.5.2"
|
version "1.5.2"
|
||||||
resolved "https://registry.yarnpkg.com/opener/-/opener-1.5.2.tgz#5d37e1f35077b9dcac4301372271afdeb2a13598"
|
resolved "https://registry.yarnpkg.com/opener/-/opener-1.5.2.tgz#5d37e1f35077b9dcac4301372271afdeb2a13598"
|
||||||
integrity sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==
|
integrity sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==
|
||||||
@@ -19541,10 +19628,10 @@ react-lifecycles-compat@^3.0.4:
|
|||||||
resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362"
|
resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362"
|
||||||
integrity sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==
|
integrity sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==
|
||||||
|
|
||||||
react-markdown@^8.0.5:
|
react-markdown@^8.0.6:
|
||||||
version "8.0.5"
|
version "8.0.6"
|
||||||
resolved "https://registry.yarnpkg.com/react-markdown/-/react-markdown-8.0.5.tgz#c9a70a33ca9aeeafb769c6582e7e38843b9d70ad"
|
resolved "https://registry.yarnpkg.com/react-markdown/-/react-markdown-8.0.6.tgz#3e939018f8bfce800ffdf22cf50aba3cdded7ad1"
|
||||||
integrity sha512-jGJolWWmOWAvzf+xMdB9zwStViODyyFQhNB/bwCerbBKmrTmgmA599CGiOlP58OId1IMoIRsA8UdI1Lod4zb5A==
|
integrity sha512-KgPWsYgHuftdx510wwIzpwf+5js/iHqBR+fzxefv8Khk3mFbnioF1bmL2idHN3ler0LMQmICKeDrWnZrX9mtbQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@types/hast" "^2.0.0"
|
"@types/hast" "^2.0.0"
|
||||||
"@types/prop-types" "^15.0.0"
|
"@types/prop-types" "^15.0.0"
|
||||||
@@ -21760,6 +21847,18 @@ tar@6.1.11, tar@^6.0.2:
|
|||||||
mkdirp "^1.0.3"
|
mkdirp "^1.0.3"
|
||||||
yallist "^4.0.0"
|
yallist "^4.0.0"
|
||||||
|
|
||||||
|
tcomb-validation@^3.3.0:
|
||||||
|
version "3.4.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/tcomb-validation/-/tcomb-validation-3.4.1.tgz#a7696ec176ce56a081d9e019f8b732a5a8894b65"
|
||||||
|
integrity sha512-urVVMQOma4RXwiVCa2nM2eqrAomHROHvWPuj6UkDGz/eb5kcy0x6P0dVt6kzpUZtYMNoAqJLWmz1BPtxrtjtrA==
|
||||||
|
dependencies:
|
||||||
|
tcomb "^3.0.0"
|
||||||
|
|
||||||
|
tcomb@^3.0.0, tcomb@^3.2.17:
|
||||||
|
version "3.2.29"
|
||||||
|
resolved "https://registry.yarnpkg.com/tcomb/-/tcomb-3.2.29.tgz#32404fe9456d90c2cf4798682d37439f1ccc386c"
|
||||||
|
integrity sha512-di2Hd1DB2Zfw6StGv861JoAF5h/uQVu/QJp2g8KVbtfKnoHdBQl5M32YWq6mnSYBQ1vFFrns5B1haWJL7rKaOQ==
|
||||||
|
|
||||||
telejson@^6.0.8:
|
telejson@^6.0.8:
|
||||||
version "6.0.8"
|
version "6.0.8"
|
||||||
resolved "https://registry.yarnpkg.com/telejson/-/telejson-6.0.8.tgz#1c432db7e7a9212c1fbd941c3e5174ec385148f7"
|
resolved "https://registry.yarnpkg.com/telejson/-/telejson-6.0.8.tgz#1c432db7e7a9212c1fbd941c3e5174ec385148f7"
|
||||||
@@ -22867,6 +22966,11 @@ validate-npm-package-license@^3.0.1:
|
|||||||
spdx-correct "^3.0.0"
|
spdx-correct "^3.0.0"
|
||||||
spdx-expression-parse "^3.0.0"
|
spdx-expression-parse "^3.0.0"
|
||||||
|
|
||||||
|
validator@^13.6.0:
|
||||||
|
version "13.9.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/validator/-/validator-13.9.0.tgz#33e7b85b604f3bbce9bb1a05d5c3e22e1c2ff855"
|
||||||
|
integrity sha512-B+dGG8U3fdtM0/aNK4/X8CXq/EcxU2WPrPEkJGslb47qyHsxmbggTWK0yEA4qnYVNF+nxNlN88o14hIcPmSIEA==
|
||||||
|
|
||||||
value-or-promise@1.0.11, value-or-promise@^1.0.11:
|
value-or-promise@1.0.11, value-or-promise@^1.0.11:
|
||||||
version "1.0.11"
|
version "1.0.11"
|
||||||
resolved "https://registry.yarnpkg.com/value-or-promise/-/value-or-promise-1.0.11.tgz#3e90299af31dd014fe843fe309cefa7c1d94b140"
|
resolved "https://registry.yarnpkg.com/value-or-promise/-/value-or-promise-1.0.11.tgz#3e90299af31dd014fe843fe309cefa7c1d94b140"
|
||||||
|
|||||||
Reference in New Issue
Block a user