Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
94889b7396 |
@@ -8,50 +8,11 @@ on:
|
||||
pull_request:
|
||||
types:
|
||||
- opened
|
||||
- ready_for_review
|
||||
- reopened
|
||||
- edited
|
||||
- synchronize
|
||||
- ready_for_review
|
||||
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:
|
||||
timeout-minutes: 20
|
||||
needs: node-modules
|
||||
runs-on: ubuntu-22.04
|
||||
name: '(CI) lint + unit test + build'
|
||||
steps:
|
||||
@@ -67,11 +28,8 @@ jobs:
|
||||
# https://stackoverflow.com/questions/61010294/how-to-cache-yarn-packages-in-github-actions
|
||||
cache: yarn
|
||||
|
||||
- name: Cache node modules
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: node_modules
|
||||
key: ${{ runner.os }}-cache-node-modules-${{ hashFiles('yarn.lock') }}
|
||||
- name: Install root dependencies
|
||||
run: yarn install --frozen-lockfile
|
||||
|
||||
- name: Derive appropriate SHAs for base and head for `nx affected` commands
|
||||
uses: nrwl/nx-set-shas@v3
|
||||
@@ -91,7 +49,7 @@ jobs:
|
||||
run: yarn nx affected:test
|
||||
|
||||
- name: Build affected
|
||||
run: yarn nx affected:build || (yarn install && yarn nx affected:build)
|
||||
run: yarn nx affected:build
|
||||
|
||||
# See affected apps
|
||||
- name: See affected apps
|
||||
@@ -138,43 +96,6 @@ jobs:
|
||||
with:
|
||||
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
|
||||
cypress-result:
|
||||
if: ${{ always() }}
|
||||
|
||||
@@ -2,11 +2,15 @@
|
||||
name: Verify PR title
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
|
||||
pull_request:
|
||||
types:
|
||||
- opened
|
||||
- ready_for_review
|
||||
- reopened
|
||||
- edited
|
||||
- synchronize
|
||||
jobs:
|
||||
lint_pr:
|
||||
timeout-minutes: 10
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Checkout
|
||||
@@ -19,11 +23,8 @@ jobs:
|
||||
# https://stackoverflow.com/questions/61010294/how-to-cache-yarn-packages-in-github-actions
|
||||
cache: yarn
|
||||
|
||||
- name: Cache node modules
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: node_modules
|
||||
key: ${{ runner.os }}-cache-node-modules-${{ hashFiles('yarn.lock') }}
|
||||
- name: Install root dependencies
|
||||
run: yarn install --frozen-lockfile
|
||||
|
||||
- name: Check PR title
|
||||
run: echo "${{ github.event.pull_request.title }}" | npx commitlint --config ./commitlint.config-ci.js
|
||||
@@ -15,7 +15,6 @@ jobs:
|
||||
app: ${{ fromJSON(inputs.projects) }}
|
||||
name: ${{ matrix.app }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 20
|
||||
steps:
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v3
|
||||
@@ -37,116 +36,63 @@ jobs:
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Setup node
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version-file: '.nvmrc'
|
||||
# https://stackoverflow.com/questions/61010294/how-to-cache-yarn-packages-in-github-actions
|
||||
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
|
||||
id: tags
|
||||
run: |
|
||||
envName=''
|
||||
dockerfile="dist.Dockerfile"
|
||||
if [[ "${{ github.event_name }}" = "push" ]]; then
|
||||
domain="vega.rocks"
|
||||
if [[ "${{ github.ref }}" =~ .*release/.* ]]; then
|
||||
envName="$(echo ${{ github.ref }} | rev | cut -d '/' -f 1 | rev)"
|
||||
if [[ "${{ github.ref }}" =~ .*mainnet.* ]]; then
|
||||
domain="vega.community"
|
||||
if [[ "${{ matrix.app }}" = "trading" ]]; then
|
||||
dockerfile="ipfs.Dockerfile"
|
||||
fi
|
||||
fi
|
||||
elif [[ "${{ github.ref }}" =~ .*develop$ ]]; then
|
||||
envName="stagnet3"
|
||||
fi
|
||||
bucketName="${{ matrix.app }}.${envName}.${domain}"
|
||||
echo BUCKET_NAME=${bucketName} >> $GITHUB_ENV
|
||||
echo ::set-output name=bucketName::${bucketName}
|
||||
echo ::set-output name=envName::${envName}
|
||||
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
|
||||
echo ::set-output name=nodeVersion::${nodeVersion}
|
||||
|
||||
- name: Build and export to local Docker
|
||||
id: docker_build
|
||||
if: ${{ github.event_name == 'pull_request' || ( env.DOCKERFILE == 'docker/ipfs.Dockerfile' && github.event_name == 'push' ) }}
|
||||
uses: docker/build-push-action@v3
|
||||
with:
|
||||
context: .
|
||||
file: ${{ env.DOCKERFILE }}
|
||||
load: true
|
||||
build-args: |
|
||||
APP=${{ matrix.app }}
|
||||
NODE_VERSION=${{ env.NODE_VERSION }}
|
||||
ENV_NAME=${{ env.ENV_NAME }}
|
||||
NODE_VERSION=${{ steps.tags.outputs.nodeVersion }}
|
||||
ENV_NAME=${{ steps.tags.outputs.envName || '' }}
|
||||
tags: |
|
||||
ghcr.io/vegaprotocol/frontend/${{ matrix.app }}:local
|
||||
|
||||
- name: Image digest
|
||||
if: ${{ github.event_name == 'pull_request' || ( env.DOCKERFILE == 'docker/ipfs.Dockerfile' && github.event_name == 'push' ) }}
|
||||
run: echo ${{ steps.docker_build.outputs.digest }}
|
||||
|
||||
- name: Sanity check docker image
|
||||
if: ${{ github.event_name == 'pull_request' || ( env.DOCKERFILE == 'docker/ipfs.Dockerfile' && github.event_name == 'push' ) }}
|
||||
run: |
|
||||
echo "Check ipfs-hash"
|
||||
if [[ "${{ env.DOCKERFILE }}" = "docker/ipfs.Dockerfile" ]]; then
|
||||
docker run --rm ghcr.io/vegaprotocol/frontend/${{ matrix.app }}:local cat /ipfs-hash
|
||||
fi
|
||||
docker run --rm ghcr.io/vegaprotocol/frontend/${{ matrix.app }}:local cat ipfs-hash
|
||||
|
||||
echo "List html directory"
|
||||
docker run --rm ghcr.io/vegaprotocol/frontend/${{ matrix.app }}:local sh -c 'apk add --update tree; tree .'
|
||||
|
||||
- name: Copy dist to local filesystem
|
||||
if: ${{ env.DOCKERFILE == 'docker/ipfs.Dockerfile' && github.event_name == 'push' }}
|
||||
run: |
|
||||
docker create --name=dist ghcr.io/vegaprotocol/frontend/${{ matrix.app }}:local
|
||||
docker cp dist:/usr/share/nginx/html dist
|
||||
|
||||
echo "check local dist files"
|
||||
tree dist/html
|
||||
mv dist/html dist-result
|
||||
tree dist
|
||||
|
||||
- name: Publish dist as docker image
|
||||
uses: docker/build-push-action@v3
|
||||
if: ${{ github.event_name == 'pull_request' }}
|
||||
with:
|
||||
context: .
|
||||
file: ${{ env.DOCKERFILE }}
|
||||
push: true
|
||||
build-args: |
|
||||
APP=${{ matrix.app }}
|
||||
NODE_VERSION=${{ env.NODE_VERSION }}
|
||||
ENV_NAME=${{ env.ENV_NAME }}
|
||||
NODE_VERSION=${{ steps.tags.outputs.nodeVersion }}
|
||||
tags: |
|
||||
ghcr.io/vegaprotocol/frontend/${{ matrix.app }}:${{ github.event.pull_request.head.sha || github.sha }}
|
||||
|
||||
@@ -157,11 +103,11 @@ jobs:
|
||||
with:
|
||||
args: --acl private --follow-symlinks --delete
|
||||
env:
|
||||
AWS_S3_BUCKET: ${{ env.BUCKET_NAME }}
|
||||
AWS_S3_BUCKET: ${{ steps.tags.outputs.bucketName }}
|
||||
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'
|
||||
SOURCE_DIR: 'dist/${{ matrix.app }}'
|
||||
|
||||
- name: Add preview label
|
||||
uses: actions-ecosystem/action-add-labels@v1
|
||||
@@ -169,3 +115,6 @@ jobs:
|
||||
with:
|
||||
labels: ${{ matrix.app }}-preview
|
||||
number: ${{ github.event.number }}
|
||||
|
||||
- name: Image digest
|
||||
run: echo ${{ steps.docker_build.outputs.digest }}
|
||||
|
||||
@@ -13,7 +13,7 @@ RUN apk add --update --no-cache \
|
||||
COPY . ./
|
||||
RUN yarn --network-timeout 100000 --pure-lockfile
|
||||
# work around for different build process in trading
|
||||
RUN sh docker/docker-build.sh
|
||||
RUN sh ./docker-build.sh
|
||||
|
||||
# Server environment
|
||||
# if this fails you need to docker pull nginx:1.23-alpine and pin new SHA
|
||||
@@ -23,7 +23,7 @@ FROM --platform=amd64 nginx:1.23-alpine@sha256:6318314189b40e73145a48060bff4783a
|
||||
EXPOSE 80
|
||||
# Copy dist
|
||||
WORKDIR /usr/share/nginx/html
|
||||
COPY docker/nginx.conf /etc/nginx/conf.d/default.conf
|
||||
COPY nginx/nginx.conf /etc/nginx/conf.d/default.conf
|
||||
RUN rm -rf /usr/share/nginx/html/*
|
||||
COPY --from=build /app/dist/apps/${APP}/* /usr/share/nginx/html
|
||||
RUN apk add --no-cache go-ipfs; 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
|
||||
@@ -38,16 +38,14 @@ const DialogsContainer = () => {
|
||||
export const Layout = () => {
|
||||
const isHome = Boolean(useMatch(Routes.HOME));
|
||||
const { ANNOUNCEMENTS_CONFIG_URL } = useEnvironment();
|
||||
const fixedWidthClasses = 'w-full max-w-[1500px] mx-auto';
|
||||
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
className={classNames(
|
||||
'min-h-screen',
|
||||
'max-w-[1500px] min-h-[100vh]',
|
||||
'mx-auto my-0',
|
||||
'grid grid-rows-[auto_1fr_auto] grid-cols-1',
|
||||
'border-vega-light-200 dark:border-vega-dark-200',
|
||||
'border-vega-light-200 dark:border-vega-dark-200 lg:border-l lg:border-r',
|
||||
'antialiased text-black dark:text-white',
|
||||
'overflow-hidden relative'
|
||||
)}
|
||||
@@ -61,13 +59,13 @@ export const Layout = () => {
|
||||
)}
|
||||
<Header />
|
||||
</div>
|
||||
<div className={fixedWidthClasses}>
|
||||
<div>
|
||||
<main className="p-4">
|
||||
{!isHome && <BreadcrumbsContainer className="mb-4" />}
|
||||
<Outlet />
|
||||
</main>
|
||||
</div>
|
||||
<div className={fixedWidthClasses}>
|
||||
<div>
|
||||
<Footer />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,19 +1,15 @@
|
||||
import classNames from 'classnames';
|
||||
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||
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 {
|
||||
children: ReactNode;
|
||||
}
|
||||
export const AppLayout = ({ children }: AppLayoutProps) => {
|
||||
const { VEGA_ENV, ANNOUNCEMENTS_CONFIG_URL } = useEnvironment();
|
||||
const { isReadOnly } = useVegaWallet();
|
||||
const AppLayoutClasses = classNames(
|
||||
'app w-full max-w-[1500px] mx-auto grid',
|
||||
'app w-full max-w-[1500px] mx-auto grid min-h-full',
|
||||
'border-neutral-700 lg:border-l lg:border-r',
|
||||
'lg:text-body-large',
|
||||
{
|
||||
'grid-rows-[repeat(2,min-content)_1fr_min-content]': !isReadOnly,
|
||||
@@ -21,18 +17,5 @@ export const AppLayout = ({ children }: AppLayoutProps) => {
|
||||
}
|
||||
);
|
||||
|
||||
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>
|
||||
);
|
||||
return <div className={AppLayoutClasses}>{children}</div>;
|
||||
};
|
||||
|
||||
@@ -1,16 +1,28 @@
|
||||
import { Networks, useEnvironment } from '@vegaprotocol/environment';
|
||||
import { ViewingAsBanner } from '@vegaprotocol/ui-toolkit';
|
||||
import { AnnouncementBanner } from '@vegaprotocol/announcements';
|
||||
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||
import React from 'react';
|
||||
|
||||
import { Nav } from '../nav';
|
||||
|
||||
export interface TemplateSidebarProps {
|
||||
children: React.ReactNode;
|
||||
sidebar: React.ReactNode[];
|
||||
}
|
||||
|
||||
export function TemplateSidebar({ children, sidebar }: TemplateSidebarProps) {
|
||||
const { VEGA_ENV, ANNOUNCEMENTS_CONFIG_URL } = useEnvironment();
|
||||
const { isReadOnly, pubKey, disconnect } = useVegaWallet();
|
||||
return (
|
||||
<>
|
||||
{ANNOUNCEMENTS_CONFIG_URL && (
|
||||
<AnnouncementBanner
|
||||
app="governance"
|
||||
configUrl={ANNOUNCEMENTS_CONFIG_URL}
|
||||
/>
|
||||
)}
|
||||
<Nav theme={VEGA_ENV === Networks.TESTNET ? 'yellow' : 'dark'} />
|
||||
{isReadOnly ? (
|
||||
<ViewingAsBanner pubKey={pubKey} disconnect={disconnect} />
|
||||
) : null}
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import type { ObservableQuery } from '@apollo/client';
|
||||
import { useEffect } from 'react';
|
||||
|
||||
export const useRefreshAfterEpoch = (
|
||||
epochExpiry: string | undefined,
|
||||
refetch: () => void
|
||||
refetch: ObservableQuery['refetch']
|
||||
) => {
|
||||
return useEffect(() => {
|
||||
const epochInterval = setInterval(() => {
|
||||
|
||||
@@ -443,8 +443,8 @@
|
||||
"rewardType": "Reward type",
|
||||
"rewardsAndFeesReceived": "Rewards and fees received",
|
||||
"ThisDoesNotIncludeFeesReceivedForMakersOrLiquidityProviders": "This does not include fees received for makers or liquidity providers",
|
||||
"totalDistributed": "Total distributed",
|
||||
"earnedByMe": "Earned by me",
|
||||
"totalDistributed": "TOTAL DISTRIBUTED",
|
||||
"earnedByMe": "EARNED BY ME",
|
||||
"noRewardsHaveBeenDistributedYet": "NO REWARDS HAVE BEEN DISTRIBUTED YET",
|
||||
"rewardsColAssetHeader": "ASSET",
|
||||
"rewardsColStakingHeader": "STAKING",
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
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';
|
||||
|
||||
const mockData = {
|
||||
epoch: 4441,
|
||||
epoch: '4441',
|
||||
rewards: [
|
||||
{
|
||||
asset: 'tDAI',
|
||||
|
||||
+21
-68
@@ -1,43 +1,32 @@
|
||||
import { useMemo, useEffect, useState, useCallback } from 'react';
|
||||
import { useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { AsyncRenderer, Pagination } from '@vegaprotocol/ui-toolkit';
|
||||
import { AsyncRenderer } from '@vegaprotocol/ui-toolkit';
|
||||
import { removePaginationWrapper } from '@vegaprotocol/utils';
|
||||
import type { EpochFieldsFragment } from '../home/__generated__/Rewards';
|
||||
import { useRewardsQuery } from '../home/__generated__/Rewards';
|
||||
import { ENV } from '../../../config';
|
||||
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||
import { EpochIndividualRewardsTable } from './epoch-individual-rewards-table';
|
||||
import { generateEpochIndividualRewardsList } from './generate-epoch-individual-rewards-list';
|
||||
import { calculateEpochOffset } from '../../../lib/epoch-pagination';
|
||||
|
||||
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);
|
||||
export const EpochIndividualRewards = () => {
|
||||
const { t } = useTranslation();
|
||||
const { pubKey } = useVegaWallet();
|
||||
const { delegationsPagination } = ENV;
|
||||
|
||||
const { data, loading, error, refetch } = useRewardsQuery({
|
||||
notifyOnNetworkStatusChange: true,
|
||||
const { data, loading, error } = useRewardsQuery({
|
||||
variables: {
|
||||
partyId: pubKey || '',
|
||||
fromEpoch: epochId - EPOCHS_PAGE_SIZE,
|
||||
toEpoch: epochId,
|
||||
delegationsPagination: delegationsPagination
|
||||
? {
|
||||
first: Number(delegationsPagination),
|
||||
}
|
||||
: undefined,
|
||||
// we can use the same value for rewardsPagination as delegationsPagination
|
||||
rewardsPagination: delegationsPagination
|
||||
? {
|
||||
first: Number(delegationsPagination),
|
||||
}
|
||||
: undefined,
|
||||
},
|
||||
skip: !pubKey,
|
||||
});
|
||||
@@ -50,37 +39,8 @@ export const EpochIndividualRewards = ({
|
||||
|
||||
const epochIndividualRewardSummaries = useMemo(() => {
|
||||
if (!data?.party) return [];
|
||||
return generateEpochIndividualRewardsList({
|
||||
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 generateEpochIndividualRewardsList(rewards);
|
||||
}, [data?.party, rewards]);
|
||||
|
||||
return (
|
||||
<AsyncRenderer
|
||||
@@ -93,24 +53,17 @@ export const EpochIndividualRewards = ({
|
||||
{t('Connected Vega key')}:{' '}
|
||||
<span className="text-white">{pubKey}</span>
|
||||
</p>
|
||||
{epochIndividualRewardSummaries.map(
|
||||
(epochIndividualRewardSummary) => (
|
||||
<EpochIndividualRewardsTable
|
||||
data={epochIndividualRewardSummary}
|
||||
/>
|
||||
{epochIndividualRewardSummaries.length ? (
|
||||
epochIndividualRewardSummaries.map(
|
||||
(epochIndividualRewardSummary) => (
|
||||
<EpochIndividualRewardsTable
|
||||
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>
|
||||
)}
|
||||
/>
|
||||
|
||||
+15
-204
@@ -43,16 +43,6 @@ describe('generateEpochIndividualRewardsList', () => {
|
||||
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 = {
|
||||
rewardType: AccountType.ACCOUNT_TYPE_INSURANCE,
|
||||
amount: '50',
|
||||
@@ -64,38 +54,20 @@ describe('generateEpochIndividualRewardsList', () => {
|
||||
};
|
||||
|
||||
it('should return an empty array if no rewards are provided', () => {
|
||||
expect(
|
||||
generateEpochIndividualRewardsList({ rewards: [], epochId: 1 })
|
||||
).toEqual([
|
||||
{
|
||||
epoch: 1,
|
||||
rewards: [],
|
||||
},
|
||||
]);
|
||||
expect(generateEpochIndividualRewardsList([])).toEqual([]);
|
||||
});
|
||||
|
||||
it('should filter out any rewards of the wrong type', () => {
|
||||
const result = generateEpochIndividualRewardsList({
|
||||
rewards: [rewardWrongType],
|
||||
epochId: 1,
|
||||
});
|
||||
const result = generateEpochIndividualRewardsList([rewardWrongType]);
|
||||
|
||||
expect(result).toEqual([
|
||||
{
|
||||
epoch: 1,
|
||||
rewards: [],
|
||||
},
|
||||
]);
|
||||
expect(result).toEqual([]);
|
||||
});
|
||||
|
||||
it('should return reward in the correct format', () => {
|
||||
const result = generateEpochIndividualRewardsList({
|
||||
rewards: [reward1],
|
||||
epochId: 1,
|
||||
});
|
||||
const result = generateEpochIndividualRewardsList([reward1]);
|
||||
|
||||
expect(result[0]).toEqual({
|
||||
epoch: 1,
|
||||
epoch: '1',
|
||||
rewards: [
|
||||
{
|
||||
asset: 'USD',
|
||||
@@ -133,24 +105,21 @@ describe('generateEpochIndividualRewardsList', () => {
|
||||
|
||||
it('should return an array sorted by epoch descending', () => {
|
||||
const rewards = [reward1, reward2, reward3, reward4];
|
||||
const result1 = generateEpochIndividualRewardsList({ rewards, epochId: 2 });
|
||||
const result1 = generateEpochIndividualRewardsList(rewards);
|
||||
|
||||
expect(result1[0].epoch).toEqual(2);
|
||||
expect(result1[1].epoch).toEqual(1);
|
||||
expect(result1[0].epoch).toEqual('2');
|
||||
expect(result1[1].epoch).toEqual('1');
|
||||
|
||||
const reorderedRewards = [reward4, reward3, reward2, reward1];
|
||||
const result2 = generateEpochIndividualRewardsList({
|
||||
rewards: reorderedRewards,
|
||||
epochId: 2,
|
||||
});
|
||||
const result2 = generateEpochIndividualRewardsList(reorderedRewards);
|
||||
|
||||
expect(result2[0].epoch).toEqual(2);
|
||||
expect(result2[1].epoch).toEqual(1);
|
||||
expect(result2[0].epoch).toEqual('2');
|
||||
expect(result2[1].epoch).toEqual('1');
|
||||
});
|
||||
|
||||
it('correctly calculates the total value of rewards for an asset', () => {
|
||||
const rewards = [reward1, reward4];
|
||||
const result = generateEpochIndividualRewardsList({ rewards, epochId: 1 });
|
||||
const result = generateEpochIndividualRewardsList(rewards);
|
||||
|
||||
expect(result[0].rewards[0].totalAmount).toEqual('200');
|
||||
});
|
||||
@@ -158,11 +127,11 @@ describe('generateEpochIndividualRewardsList', () => {
|
||||
it('returns data in the expected shape', () => {
|
||||
// Just sanity checking the whole structure here
|
||||
const rewards = [reward1, reward2, reward3, reward4];
|
||||
const result = generateEpochIndividualRewardsList({ rewards, epochId: 2 });
|
||||
const result = generateEpochIndividualRewardsList(rewards);
|
||||
|
||||
expect(result).toEqual([
|
||||
{
|
||||
epoch: 2,
|
||||
epoch: '2',
|
||||
rewards: [
|
||||
{
|
||||
asset: 'GBP',
|
||||
@@ -227,165 +196,7 @@ describe('generateEpochIndividualRewardsList', () => {
|
||||
],
|
||||
},
|
||||
{
|
||||
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,
|
||||
epoch: '1',
|
||||
rewards: [
|
||||
{
|
||||
asset: 'USD',
|
||||
|
||||
+11
-30
@@ -2,10 +2,9 @@ import { BigNumber } from '../../../lib/bignumber';
|
||||
import { RowAccountTypes } from '../shared-rewards-table-assets/shared-rewards-table-assets';
|
||||
import type { RewardFieldsFragment } from '../home/__generated__/Rewards';
|
||||
import type { AccountType } from '@vegaprotocol/types';
|
||||
import { calculateEpochOffset } from '../../../lib/epoch-pagination';
|
||||
|
||||
export interface EpochIndividualReward {
|
||||
epoch: number;
|
||||
epoch: string;
|
||||
rewards: {
|
||||
asset: string;
|
||||
totalAmount: string;
|
||||
@@ -28,29 +27,11 @@ const emptyRowAccountTypes = accountTypes.map((type) => [
|
||||
},
|
||||
]);
|
||||
|
||||
export const generateEpochIndividualRewardsList = ({
|
||||
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: [],
|
||||
});
|
||||
}
|
||||
|
||||
export const generateEpochIndividualRewardsList = (
|
||||
rewards: RewardFieldsFragment[]
|
||||
) => {
|
||||
// We take the rewards and aggregate them by epoch and asset.
|
||||
const epochIndividualRewards = rewards.reduce((acc, reward) => {
|
||||
const epochIndividualRewards = rewards.reduce((map, reward) => {
|
||||
const epochId = reward.epoch.id;
|
||||
const assetName = reward.asset.name;
|
||||
const rewardType = reward.rewardType;
|
||||
@@ -59,14 +40,14 @@ export const generateEpochIndividualRewardsList = ({
|
||||
|
||||
// if the rewardType is not of a type we display in the table, we skip it.
|
||||
if (!accountTypes.includes(rewardType)) {
|
||||
return acc;
|
||||
return map;
|
||||
}
|
||||
|
||||
if (!acc.has(epochId)) {
|
||||
return acc;
|
||||
if (!map.has(epochId)) {
|
||||
map.set(epochId, { epoch: epochId, rewards: [] });
|
||||
}
|
||||
|
||||
const epoch = acc.get(epochId);
|
||||
const epoch = map.get(epochId);
|
||||
|
||||
let asset = epoch?.rewards.find((r) => r.asset === assetName);
|
||||
|
||||
@@ -95,8 +76,8 @@ export const generateEpochIndividualRewardsList = ({
|
||||
});
|
||||
}
|
||||
|
||||
return acc;
|
||||
}, map);
|
||||
return map;
|
||||
}, new Map<string, EpochIndividualReward>());
|
||||
|
||||
return Array.from(epochIndividualRewards.values()).sort(
|
||||
(a, b) => Number(b.epoch) - Number(a.epoch)
|
||||
|
||||
+34
-54
@@ -1,64 +1,44 @@
|
||||
import { render } from '@testing-library/react';
|
||||
import { AppStateProvider } from '../../../contexts/app-state/app-state-provider';
|
||||
import { EpochTotalRewardsTable } from './epoch-total-rewards-table';
|
||||
import type {
|
||||
AggregatedEpochRewardSummary,
|
||||
RewardType,
|
||||
RewardItem,
|
||||
} from './generate-epoch-total-rewards-list';
|
||||
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 = {
|
||||
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', () => {
|
||||
|
||||
+10
-12
@@ -48,19 +48,17 @@ export const EpochTotalRewardsTable = ({
|
||||
}: EpochTotalRewardsGridProps) => {
|
||||
return (
|
||||
<RewardsTable dataTestId="epoch-total-rewards-table" epoch={data.epoch}>
|
||||
{Array.from(data.assetRewards.values()).map(
|
||||
({ name, rewards, totalAmount }, i) => (
|
||||
<div className="contents" key={i}>
|
||||
<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} />
|
||||
{data.assetRewards.map(({ name, rewards, totalAmount }, i) => (
|
||||
<div className="contents" key={i}>
|
||||
<div data-testid="asset" className={`${rowGridItemStyles()} p-5`}>
|
||||
{name}
|
||||
</div>
|
||||
)
|
||||
)}
|
||||
{rewards.map(({ rewardType, amount }, i) => (
|
||||
<RewardItem key={i} dataTestId={rewardType} value={amount} />
|
||||
))}
|
||||
<RewardItem dataTestId="total" value={totalAmount} last={true} />
|
||||
</div>
|
||||
))}
|
||||
</RewardsTable>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,62 +1,21 @@
|
||||
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 { AsyncRenderer } from '@vegaprotocol/ui-toolkit';
|
||||
import { useEpochAssetsRewardsQuery } from '../home/__generated__/Rewards';
|
||||
import { useRefreshAfterEpoch } from '../../../hooks/use-refresh-after-epoch';
|
||||
import { generateEpochTotalRewardsList } from './generate-epoch-total-rewards-list';
|
||||
import { NoRewards } from '../no-rewards';
|
||||
import { EpochTotalRewardsTable } from './epoch-total-rewards-table';
|
||||
import { calculateEpochOffset } from '../../../lib/epoch-pagination';
|
||||
|
||||
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);
|
||||
export const EpochTotalRewards = () => {
|
||||
const { data, loading, error, refetch } = useEpochAssetsRewardsQuery({
|
||||
notifyOnNetworkStatusChange: true,
|
||||
variables: {
|
||||
epochRewardSummariesFilter: {
|
||||
fromEpoch: epochId - EPOCHS_PAGE_SIZE,
|
||||
epochRewardSummariesPagination: {
|
||||
first: 10,
|
||||
},
|
||||
},
|
||||
});
|
||||
useRefreshAfterEpoch(data?.epoch.timestamps.expiry, refetch);
|
||||
|
||||
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,
|
||||
}) || [];
|
||||
const epochTotalRewardSummaries = generateEpochTotalRewardsList(data) || [];
|
||||
|
||||
return (
|
||||
<AsyncRenderer
|
||||
@@ -68,22 +27,15 @@ export const EpochTotalRewards = ({ currentEpoch }: EpochTotalRewardsProps) => {
|
||||
className="max-w-full overflow-auto"
|
||||
data-testid="epoch-rewards-total"
|
||||
>
|
||||
{Array.from(epochTotalRewardSummaries.values()).map(
|
||||
(epochTotalSummary, index) => (
|
||||
<EpochTotalRewardsTable data={epochTotalSummary} key={index} />
|
||||
)
|
||||
{epochTotalRewardSummaries.length === 0 ? (
|
||||
<NoRewards />
|
||||
) : (
|
||||
<>
|
||||
{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>
|
||||
)}
|
||||
/>
|
||||
|
||||
+117
-521
@@ -3,23 +3,13 @@ import { AccountType } from '@vegaprotocol/types';
|
||||
|
||||
describe('generateEpochAssetRewardsList', () => {
|
||||
it('should return an empty array if data is undefined', () => {
|
||||
const result = generateEpochTotalRewardsList({ epochId: 1 });
|
||||
const result = generateEpochTotalRewardsList(undefined);
|
||||
|
||||
expect(result).toEqual(
|
||||
new Map([
|
||||
[
|
||||
'1',
|
||||
{
|
||||
epoch: 1,
|
||||
assetRewards: new Map(),
|
||||
},
|
||||
],
|
||||
])
|
||||
);
|
||||
expect(result).toEqual([]);
|
||||
});
|
||||
|
||||
it('should return an empty map if empty data is provided', () => {
|
||||
const data = {
|
||||
it('should return an empty array if empty data is provided', () => {
|
||||
const epochData = {
|
||||
assetsConnection: {
|
||||
edges: [],
|
||||
},
|
||||
@@ -33,23 +23,13 @@ describe('generateEpochAssetRewardsList', () => {
|
||||
},
|
||||
};
|
||||
|
||||
const result = generateEpochTotalRewardsList({ data, epochId: 1 });
|
||||
const result = generateEpochTotalRewardsList(epochData);
|
||||
|
||||
expect(result).toEqual(
|
||||
new Map([
|
||||
[
|
||||
'1',
|
||||
{
|
||||
epoch: 1,
|
||||
assetRewards: new Map(),
|
||||
},
|
||||
],
|
||||
])
|
||||
);
|
||||
expect(result).toEqual([]);
|
||||
});
|
||||
|
||||
it('should return an empty map if no epochRewardSummaries are provided', () => {
|
||||
const data = {
|
||||
it('should return an empty array if no epochRewardSummaries are provided', () => {
|
||||
const epochData = {
|
||||
assetsConnection: {
|
||||
edges: [
|
||||
{
|
||||
@@ -76,23 +56,13 @@ describe('generateEpochAssetRewardsList', () => {
|
||||
},
|
||||
};
|
||||
|
||||
const result = generateEpochTotalRewardsList({ data, epochId: 1 });
|
||||
const result = generateEpochTotalRewardsList(epochData);
|
||||
|
||||
expect(result).toEqual(
|
||||
new Map([
|
||||
[
|
||||
'1',
|
||||
{
|
||||
epoch: 1,
|
||||
assetRewards: new Map(),
|
||||
},
|
||||
],
|
||||
])
|
||||
);
|
||||
expect(result).toEqual([]);
|
||||
});
|
||||
|
||||
it('should return a map of unnamed assets if no asset names are provided (should not happen)', () => {
|
||||
const data = {
|
||||
it('should return an array of unnamed assets if no asset names are provided (should not happen)', () => {
|
||||
const epochData = {
|
||||
assetsConnection: {
|
||||
edges: [],
|
||||
},
|
||||
@@ -115,80 +85,50 @@ describe('generateEpochAssetRewardsList', () => {
|
||||
},
|
||||
};
|
||||
|
||||
const result = generateEpochTotalRewardsList({ data, epochId: 1 });
|
||||
const result = generateEpochTotalRewardsList(epochData);
|
||||
|
||||
expect(result).toEqual(
|
||||
new Map([
|
||||
[
|
||||
'1',
|
||||
expect(result).toEqual([
|
||||
{
|
||||
epoch: 1,
|
||||
assetRewards: [
|
||||
{
|
||||
epoch: 1,
|
||||
assetRewards: new Map([
|
||||
[
|
||||
'1',
|
||||
{
|
||||
assetId: '1',
|
||||
name: '',
|
||||
rewards: new Map([
|
||||
[
|
||||
AccountType.ACCOUNT_TYPE_GLOBAL_REWARD,
|
||||
{
|
||||
rewardType: AccountType.ACCOUNT_TYPE_GLOBAL_REWARD,
|
||||
amount: '123',
|
||||
},
|
||||
],
|
||||
[
|
||||
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: '0',
|
||||
},
|
||||
],
|
||||
[
|
||||
AccountType.ACCOUNT_TYPE_REWARD_MARKET_PROPOSERS,
|
||||
{
|
||||
rewardType:
|
||||
AccountType.ACCOUNT_TYPE_REWARD_MARKET_PROPOSERS,
|
||||
amount: '0',
|
||||
},
|
||||
],
|
||||
]),
|
||||
totalAmount: '123',
|
||||
},
|
||||
],
|
||||
]),
|
||||
assetId: '1',
|
||||
name: '',
|
||||
rewards: [
|
||||
{
|
||||
rewardType: AccountType.ACCOUNT_TYPE_GLOBAL_REWARD,
|
||||
amount: '123',
|
||||
},
|
||||
{
|
||||
rewardType: AccountType.ACCOUNT_TYPE_FEES_INFRASTRUCTURE,
|
||||
amount: '0',
|
||||
},
|
||||
{
|
||||
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: '123',
|
||||
},
|
||||
],
|
||||
])
|
||||
);
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should return the aggregated epoch summaries', () => {
|
||||
const data = {
|
||||
it('should return an array of aggregated epoch summaries', () => {
|
||||
const epochData = {
|
||||
assetsConnection: {
|
||||
edges: [
|
||||
{
|
||||
@@ -240,425 +180,81 @@ describe('generateEpochAssetRewardsList', () => {
|
||||
},
|
||||
};
|
||||
|
||||
const result = generateEpochTotalRewardsList({ data, epochId: 2 });
|
||||
const result = generateEpochTotalRewardsList(epochData);
|
||||
|
||||
expect(result).toEqual(
|
||||
new Map([
|
||||
[
|
||||
'1',
|
||||
expect(result).toEqual([
|
||||
{
|
||||
epoch: 1,
|
||||
assetRewards: [
|
||||
{
|
||||
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',
|
||||
},
|
||||
],
|
||||
]),
|
||||
},
|
||||
],
|
||||
[
|
||||
'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',
|
||||
},
|
||||
assetId: '1',
|
||||
name: 'Asset 1',
|
||||
rewards: [
|
||||
{
|
||||
rewardType: AccountType.ACCOUNT_TYPE_GLOBAL_REWARD,
|
||||
amount: '0',
|
||||
},
|
||||
{
|
||||
rewardType: AccountType.ACCOUNT_TYPE_FEES_INFRASTRUCTURE,
|
||||
amount: '100',
|
||||
},
|
||||
{
|
||||
rewardType: AccountType.ACCOUNT_TYPE_REWARD_MAKER_PAID_FEES,
|
||||
amount: '123',
|
||||
},
|
||||
{
|
||||
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: '223',
|
||||
},
|
||||
],
|
||||
},
|
||||
epochRewardSummaries: {
|
||||
edges: [
|
||||
{
|
||||
epoch: 2,
|
||||
assetRewards: [
|
||||
{
|
||||
node: {
|
||||
epoch: 1,
|
||||
assetId: '1',
|
||||
rewardType: AccountType.ACCOUNT_TYPE_REWARD_MAKER_PAID_FEES,
|
||||
amount: '123',
|
||||
},
|
||||
},
|
||||
{
|
||||
node: {
|
||||
epoch: 1,
|
||||
assetId: '1',
|
||||
rewardType: AccountType.ACCOUNT_TYPE_FEES_INFRASTRUCTURE,
|
||||
amount: '100',
|
||||
},
|
||||
},
|
||||
{
|
||||
node: {
|
||||
epoch: 2,
|
||||
assetId: '1',
|
||||
rewardType: AccountType.ACCOUNT_TYPE_FEES_LIQUIDITY,
|
||||
amount: '6',
|
||||
},
|
||||
},
|
||||
{
|
||||
node: {
|
||||
epoch: 2,
|
||||
assetId: '1',
|
||||
rewardType: AccountType.ACCOUNT_TYPE_FEES_LIQUIDITY,
|
||||
amount: '27',
|
||||
},
|
||||
},
|
||||
{
|
||||
node: {
|
||||
epoch: 3,
|
||||
assetId: '1',
|
||||
rewardType: AccountType.ACCOUNT_TYPE_FEES_INFRASTRUCTURE,
|
||||
amount: '15',
|
||||
},
|
||||
assetId: '1',
|
||||
name: 'Asset 1',
|
||||
rewards: [
|
||||
{
|
||||
rewardType: AccountType.ACCOUNT_TYPE_GLOBAL_REWARD,
|
||||
amount: '0',
|
||||
},
|
||||
{
|
||||
rewardType: AccountType.ACCOUNT_TYPE_FEES_INFRASTRUCTURE,
|
||||
amount: '0',
|
||||
},
|
||||
{
|
||||
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: '5',
|
||||
},
|
||||
{
|
||||
rewardType: AccountType.ACCOUNT_TYPE_REWARD_MARKET_PROPOSERS,
|
||||
amount: '0',
|
||||
},
|
||||
],
|
||||
totalAmount: '5',
|
||||
},
|
||||
],
|
||||
},
|
||||
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',
|
||||
},
|
||||
],
|
||||
]),
|
||||
},
|
||||
],
|
||||
])
|
||||
);
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
+104
-74
@@ -5,97 +5,127 @@ import type {
|
||||
import { removePaginationWrapper } from '@vegaprotocol/utils';
|
||||
import { RowAccountTypes } from '../shared-rewards-table-assets/shared-rewards-table-assets';
|
||||
import type { AccountType } from '@vegaprotocol/types';
|
||||
import { calculateEpochOffset } from '../../../lib/epoch-pagination';
|
||||
import { BigNumber } from '../../../lib/bignumber';
|
||||
|
||||
interface EpochSummaryWithNamedReward extends EpochRewardSummaryFieldsFragment {
|
||||
name: string;
|
||||
}
|
||||
|
||||
export type RewardType = EpochRewardSummaryFieldsFragment['rewardType'];
|
||||
export type RewardItem = Pick<
|
||||
EpochRewardSummaryFieldsFragment,
|
||||
'rewardType' | 'amount'
|
||||
>;
|
||||
|
||||
export type AggregatedEpochRewardSummary = {
|
||||
export interface AggregatedEpochRewardSummary {
|
||||
assetId: EpochRewardSummaryFieldsFragment['assetId'];
|
||||
name: EpochSummaryWithNamedReward['name'];
|
||||
rewards: Map<RewardType, RewardItem>;
|
||||
rewards: {
|
||||
rewardType: EpochRewardSummaryFieldsFragment['rewardType'];
|
||||
amount: EpochRewardSummaryFieldsFragment['amount'];
|
||||
}[];
|
||||
totalAmount: string;
|
||||
};
|
||||
}
|
||||
|
||||
export type EpochTotalSummary = {
|
||||
export interface EpochTotalSummary {
|
||||
epoch: EpochRewardSummaryFieldsFragment['epoch'];
|
||||
assetRewards: Map<
|
||||
EpochRewardSummaryFieldsFragment['assetId'],
|
||||
AggregatedEpochRewardSummary
|
||||
>;
|
||||
};
|
||||
assetRewards: AggregatedEpochRewardSummary[];
|
||||
}
|
||||
|
||||
const emptyRowAccountTypes: Map<RewardType, RewardItem> = new Map();
|
||||
const emptyRowAccountTypes = Object.keys(RowAccountTypes).map((type) => ({
|
||||
rewardType: type as AccountType,
|
||||
amount: '0',
|
||||
}));
|
||||
|
||||
Object.keys(RowAccountTypes).forEach((type) => {
|
||||
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;
|
||||
}) => {
|
||||
export const generateEpochTotalRewardsList = (
|
||||
epochData: EpochAssetsRewardsQuery | undefined
|
||||
) => {
|
||||
const epochRewardSummaries = removePaginationWrapper(
|
||||
data?.epochRewardSummaries?.edges
|
||||
epochData?.epochRewardSummaries?.edges
|
||||
);
|
||||
|
||||
const assets = removePaginationWrapper(data?.assetsConnection?.edges);
|
||||
const assets = removePaginationWrapper(epochData?.assetsConnection?.edges);
|
||||
|
||||
const map: Map<string, EpochTotalSummary> = new Map();
|
||||
const { fromEpoch, toEpoch } = calculateEpochOffset({ epochId, page, size });
|
||||
// Because the epochRewardSummaries don't have the asset name, we need to find it in the assets list
|
||||
const epochSummariesWithNamedReward: EpochSummaryWithNamedReward[] =
|
||||
epochRewardSummaries.map((epochReward) => ({
|
||||
...epochReward,
|
||||
name:
|
||||
assets.find((asset) => asset.id === epochReward.assetId)?.name || '',
|
||||
}));
|
||||
|
||||
for (let i = toEpoch; i >= fromEpoch; i--) {
|
||||
map.set(i.toString(), {
|
||||
epoch: i,
|
||||
assetRewards: new Map(),
|
||||
// Aggregating the epoch summaries by epoch number
|
||||
const aggregatedEpochSummariesByEpochNumber =
|
||||
epochSummariesWithNamedReward.reduce((acc, epochReward) => {
|
||||
const epoch = epochReward.epoch;
|
||||
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 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);
|
||||
return epochTotalRewards;
|
||||
};
|
||||
|
||||
@@ -23,18 +23,12 @@ fragment DelegationFields on Delegation {
|
||||
|
||||
query Rewards(
|
||||
$partyId: ID!
|
||||
$fromEpoch: Int
|
||||
$toEpoch: Int
|
||||
$rewardsPagination: Pagination
|
||||
$delegationsPagination: Pagination
|
||||
$rewardsPagination: Pagination
|
||||
) {
|
||||
party(id: $partyId) {
|
||||
id
|
||||
rewardsConnection(
|
||||
fromEpoch: $fromEpoch
|
||||
toEpoch: $toEpoch
|
||||
pagination: $rewardsPagination
|
||||
) {
|
||||
rewardsConnection(pagination: $rewardsPagination) {
|
||||
edges {
|
||||
node {
|
||||
...RewardFields
|
||||
@@ -49,6 +43,14 @@ query Rewards(
|
||||
}
|
||||
}
|
||||
}
|
||||
epoch {
|
||||
id
|
||||
timestamps {
|
||||
start
|
||||
end
|
||||
expiry
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fragment EpochRewardSummaryFields on EpochRewardSummary {
|
||||
@@ -58,10 +60,7 @@ fragment EpochRewardSummaryFields on EpochRewardSummary {
|
||||
rewardType
|
||||
}
|
||||
|
||||
query EpochAssetsRewards(
|
||||
$epochRewardSummariesFilter: RewardSummaryFilter
|
||||
$epochRewardSummariesPagination: Pagination
|
||||
) {
|
||||
query EpochAssetsRewards($epochRewardSummariesPagination: Pagination) {
|
||||
assetsConnection {
|
||||
edges {
|
||||
node {
|
||||
@@ -70,16 +69,18 @@ query EpochAssetsRewards(
|
||||
}
|
||||
}
|
||||
}
|
||||
epochRewardSummaries(
|
||||
filter: $epochRewardSummariesFilter
|
||||
pagination: $epochRewardSummariesPagination
|
||||
) {
|
||||
epochRewardSummaries(pagination: $epochRewardSummariesPagination) {
|
||||
edges {
|
||||
node {
|
||||
...EpochRewardSummaryFields
|
||||
}
|
||||
}
|
||||
}
|
||||
epoch {
|
||||
timestamps {
|
||||
expiry
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fragment EpochFields on Epoch {
|
||||
|
||||
+21
-21
@@ -9,24 +9,21 @@ export type DelegationFieldsFragment = { __typename?: 'Delegation', amount: stri
|
||||
|
||||
export type RewardsQueryVariables = Types.Exact<{
|
||||
partyId: Types.Scalars['ID'];
|
||||
fromEpoch?: Types.InputMaybe<Types.Scalars['Int']>;
|
||||
toEpoch?: Types.InputMaybe<Types.Scalars['Int']>;
|
||||
rewardsPagination?: Types.InputMaybe<Types.Pagination>;
|
||||
delegationsPagination?: Types.InputMaybe<Types.Pagination>;
|
||||
rewardsPagination?: 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 };
|
||||
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 EpochRewardSummaryFieldsFragment = { __typename?: 'EpochRewardSummary', epoch: number, assetId: string, amount: string, rewardType: Types.AccountType };
|
||||
|
||||
export type EpochAssetsRewardsQueryVariables = Types.Exact<{
|
||||
epochRewardSummariesFilter?: Types.InputMaybe<Types.RewardSummaryFilter>;
|
||||
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 };
|
||||
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 EpochFieldsFragment = { __typename?: 'Epoch', id: string, timestamps: { __typename?: 'EpochTimestamps', start?: any | null, end?: any | null, expiry?: any | null } };
|
||||
|
||||
@@ -79,14 +76,10 @@ export const EpochFieldsFragmentDoc = gql`
|
||||
}
|
||||
`;
|
||||
export const RewardsDocument = gql`
|
||||
query Rewards($partyId: ID!, $fromEpoch: Int, $toEpoch: Int, $rewardsPagination: Pagination, $delegationsPagination: Pagination) {
|
||||
query Rewards($partyId: ID!, $delegationsPagination: Pagination, $rewardsPagination: Pagination) {
|
||||
party(id: $partyId) {
|
||||
id
|
||||
rewardsConnection(
|
||||
fromEpoch: $fromEpoch
|
||||
toEpoch: $toEpoch
|
||||
pagination: $rewardsPagination
|
||||
) {
|
||||
rewardsConnection(pagination: $rewardsPagination) {
|
||||
edges {
|
||||
node {
|
||||
...RewardFields
|
||||
@@ -101,6 +94,14 @@ export const RewardsDocument = gql`
|
||||
}
|
||||
}
|
||||
}
|
||||
epoch {
|
||||
id
|
||||
timestamps {
|
||||
start
|
||||
end
|
||||
expiry
|
||||
}
|
||||
}
|
||||
}
|
||||
${RewardFieldsFragmentDoc}
|
||||
${DelegationFieldsFragmentDoc}`;
|
||||
@@ -118,10 +119,8 @@ ${DelegationFieldsFragmentDoc}`;
|
||||
* const { data, loading, error } = useRewardsQuery({
|
||||
* variables: {
|
||||
* partyId: // value for 'partyId'
|
||||
* fromEpoch: // value for 'fromEpoch'
|
||||
* toEpoch: // value for 'toEpoch'
|
||||
* rewardsPagination: // value for 'rewardsPagination'
|
||||
* delegationsPagination: // value for 'delegationsPagination'
|
||||
* rewardsPagination: // value for 'rewardsPagination'
|
||||
* },
|
||||
* });
|
||||
*/
|
||||
@@ -137,7 +136,7 @@ export type RewardsQueryHookResult = ReturnType<typeof useRewardsQuery>;
|
||||
export type RewardsLazyQueryHookResult = ReturnType<typeof useRewardsLazyQuery>;
|
||||
export type RewardsQueryResult = Apollo.QueryResult<RewardsQuery, RewardsQueryVariables>;
|
||||
export const EpochAssetsRewardsDocument = gql`
|
||||
query EpochAssetsRewards($epochRewardSummariesFilter: RewardSummaryFilter, $epochRewardSummariesPagination: Pagination) {
|
||||
query EpochAssetsRewards($epochRewardSummariesPagination: Pagination) {
|
||||
assetsConnection {
|
||||
edges {
|
||||
node {
|
||||
@@ -146,16 +145,18 @@ export const EpochAssetsRewardsDocument = gql`
|
||||
}
|
||||
}
|
||||
}
|
||||
epochRewardSummaries(
|
||||
filter: $epochRewardSummariesFilter
|
||||
pagination: $epochRewardSummariesPagination
|
||||
) {
|
||||
epochRewardSummaries(pagination: $epochRewardSummariesPagination) {
|
||||
edges {
|
||||
node {
|
||||
...EpochRewardSummaryFields
|
||||
}
|
||||
}
|
||||
}
|
||||
epoch {
|
||||
timestamps {
|
||||
expiry
|
||||
}
|
||||
}
|
||||
}
|
||||
${EpochRewardSummaryFieldsFragmentDoc}`;
|
||||
|
||||
@@ -171,7 +172,6 @@ export const EpochAssetsRewardsDocument = gql`
|
||||
* @example
|
||||
* const { data, loading, error } = useEpochAssetsRewardsQuery({
|
||||
* variables: {
|
||||
* epochRewardSummariesFilter: // value for 'epochRewardSummariesFilter'
|
||||
* epochRewardSummariesPagination: // value for 'epochRewardSummariesPagination'
|
||||
* },
|
||||
* });
|
||||
|
||||
@@ -114,7 +114,7 @@ export const RewardsPage = () => {
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="w-[360px]">
|
||||
<div className="w-[440px]">
|
||||
<Toggle
|
||||
name="epoch-reward-view-toggle"
|
||||
toggles={[
|
||||
@@ -136,15 +136,11 @@ export const RewardsPage = () => {
|
||||
</section>
|
||||
|
||||
{toggleRewardsView === 'total' ? (
|
||||
epochData?.epoch ? (
|
||||
<EpochTotalRewards currentEpoch={epochData?.epoch} />
|
||||
) : null
|
||||
<EpochTotalRewards />
|
||||
) : (
|
||||
<section>
|
||||
{pubKey && pubKeys?.length ? (
|
||||
epochData?.epoch ? (
|
||||
<EpochIndividualRewards currentEpoch={epochData?.epoch} />
|
||||
) : null
|
||||
<EpochIndividualRewards />
|
||||
) : (
|
||||
<ConnectToSeeRewards />
|
||||
)}
|
||||
|
||||
@@ -155,16 +155,16 @@ export const ValidatorTables = ({
|
||||
return (
|
||||
<section data-testid="validator-tables">
|
||||
<div className="grid w-full justify-end">
|
||||
<div className="w-[340px]">
|
||||
<div className="w-[400px]">
|
||||
<Toggle
|
||||
name="validators-view-toggle"
|
||||
toggles={[
|
||||
{
|
||||
label: t('All validators'),
|
||||
label: t('ALL VALIDATORS'),
|
||||
value: 'all',
|
||||
},
|
||||
{
|
||||
label: t('Staked by me'),
|
||||
label: t('STAKED BY ME'),
|
||||
value: 'myStake',
|
||||
},
|
||||
]}
|
||||
|
||||
@@ -1,44 +1,11 @@
|
||||
[
|
||||
{
|
||||
"tranche_id": 57,
|
||||
"tranche_start": "2024-04-01T00:00:00.000Z",
|
||||
"tranche_end": "2024-10-01T00:00:00.000Z",
|
||||
"total_added": "2500",
|
||||
"total_removed": "0",
|
||||
"locked_amount": "2500",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "2500",
|
||||
"user": "0x6ad69157EfB1E17EBf6614D352D098589EaE3060",
|
||||
"tx": "0xd89dcda79394278ed62c6d2d36c94d90c59a6cf2c0135903796db75c1797b644"
|
||||
}
|
||||
],
|
||||
"withdrawals": [],
|
||||
"users": [
|
||||
{
|
||||
"address": "0x6ad69157EfB1E17EBf6614D352D098589EaE3060",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "2500",
|
||||
"user": "0x6ad69157EfB1E17EBf6614D352D098589EaE3060",
|
||||
"tranche_id": 57,
|
||||
"tx": "0xd89dcda79394278ed62c6d2d36c94d90c59a6cf2c0135903796db75c1797b644"
|
||||
}
|
||||
],
|
||||
"withdrawals": [],
|
||||
"total_tokens": "2500",
|
||||
"withdrawn_tokens": "0",
|
||||
"remaining_tokens": "2500"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"tranche_id": 56,
|
||||
"tranche_start": "2023-04-20T00:00:00.000Z",
|
||||
"tranche_end": "2023-05-20T00:00:00.000Z",
|
||||
"total_added": "15761.125",
|
||||
"total_added": "15711.125",
|
||||
"total_removed": "0",
|
||||
"locked_amount": "15761.125",
|
||||
"locked_amount": "15711.125",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "241.5",
|
||||
@@ -120,11 +87,6 @@
|
||||
"user": "0x697cEF6741F519621fE14c72041e4B8B1f7e2c8A",
|
||||
"tx": "0xc7483de16af39896b3ae052dd8a6e01fb3dc3794ceb9d2a76d81436992af320e"
|
||||
},
|
||||
{
|
||||
"amount": "50",
|
||||
"user": "0x9E53F72210479BF46aE7ABF77c48B0d611b3c3dB",
|
||||
"tx": "0x3ce5a0e3e1eed31ca786cb22ee1b7bea1936491604306b10390d93198602d746"
|
||||
},
|
||||
{
|
||||
"amount": "50",
|
||||
"user": "0x15024E62134A8BFFCce11f5ce58CeCDe853038D7",
|
||||
@@ -414,21 +376,6 @@
|
||||
"withdrawn_tokens": "0",
|
||||
"remaining_tokens": "103.5"
|
||||
},
|
||||
{
|
||||
"address": "0x9E53F72210479BF46aE7ABF77c48B0d611b3c3dB",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "50",
|
||||
"user": "0x9E53F72210479BF46aE7ABF77c48B0d611b3c3dB",
|
||||
"tranche_id": 56,
|
||||
"tx": "0x3ce5a0e3e1eed31ca786cb22ee1b7bea1936491604306b10390d93198602d746"
|
||||
}
|
||||
],
|
||||
"withdrawals": [],
|
||||
"total_tokens": "50",
|
||||
"withdrawn_tokens": "0",
|
||||
"remaining_tokens": "50"
|
||||
},
|
||||
{
|
||||
"address": "0x15024E62134A8BFFCce11f5ce58CeCDe853038D7",
|
||||
"deposits": [
|
||||
@@ -600,15 +547,10 @@
|
||||
"tranche_id": 55,
|
||||
"tranche_start": "2023-09-01T00:00:00.000Z",
|
||||
"tranche_end": "2024-03-01T00:00:00.000Z",
|
||||
"total_added": "30000",
|
||||
"total_added": "17500",
|
||||
"total_removed": "0",
|
||||
"locked_amount": "30000",
|
||||
"locked_amount": "17500",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "12500",
|
||||
"user": "0x52dD21Bd0A8fBD31f4D5002c34b98d0c328791c8",
|
||||
"tx": "0xd89dcda79394278ed62c6d2d36c94d90c59a6cf2c0135903796db75c1797b644"
|
||||
},
|
||||
{
|
||||
"amount": "12500",
|
||||
"user": "0x52dD21Bd0A8fBD31f4D5002c34b98d0c328791c8",
|
||||
@@ -625,12 +567,6 @@
|
||||
{
|
||||
"address": "0x52dD21Bd0A8fBD31f4D5002c34b98d0c328791c8",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "12500",
|
||||
"user": "0x52dD21Bd0A8fBD31f4D5002c34b98d0c328791c8",
|
||||
"tranche_id": 55,
|
||||
"tx": "0xd89dcda79394278ed62c6d2d36c94d90c59a6cf2c0135903796db75c1797b644"
|
||||
},
|
||||
{
|
||||
"amount": "12500",
|
||||
"user": "0x52dD21Bd0A8fBD31f4D5002c34b98d0c328791c8",
|
||||
@@ -639,9 +575,9 @@
|
||||
}
|
||||
],
|
||||
"withdrawals": [],
|
||||
"total_tokens": "25000",
|
||||
"total_tokens": "12500",
|
||||
"withdrawn_tokens": "0",
|
||||
"remaining_tokens": "25000"
|
||||
"remaining_tokens": "12500"
|
||||
},
|
||||
{
|
||||
"address": "0xbCDBCC3Ff54D659f2CC737bEf49B10D92dF3FDbf",
|
||||
@@ -665,8 +601,8 @@
|
||||
"tranche_start": "2023-04-06T00:00:00.000Z",
|
||||
"tranche_end": "2023-05-06T00:00:00.000Z",
|
||||
"total_added": "8865",
|
||||
"total_removed": "362.31015740205",
|
||||
"locked_amount": "4947.631059027778128",
|
||||
"total_removed": "212.71903124703",
|
||||
"locked_amount": "6720.641319444444729",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "33",
|
||||
@@ -815,16 +751,6 @@
|
||||
"user": "0x6F32AA5A6198329c16e438512F992a0548C856f9",
|
||||
"tx": "0x7da34162df1b167ed5a3de6d98759af7100bcd51ec4e4c55abce2d145c98ba15"
|
||||
},
|
||||
{
|
||||
"amount": "60.16845949026",
|
||||
"user": "0x15024E62134A8BFFCce11f5ce58CeCDe853038D7",
|
||||
"tx": "0x5f8e8e83b18d0bc718c83a9afb0594e95a7b8ea2eea3d83cfbb770be317cfd70"
|
||||
},
|
||||
{
|
||||
"amount": "89.42266666476",
|
||||
"user": "0x6F32AA5A6198329c16e438512F992a0548C856f9",
|
||||
"tx": "0x05d086879e23724f1397d41adcbe2f3316dd4039c240edbc3cbbcacf1ca1d00e"
|
||||
},
|
||||
{
|
||||
"amount": "52.2185023074",
|
||||
"user": "0x6F32AA5A6198329c16e438512F992a0548C856f9",
|
||||
@@ -1044,12 +970,6 @@
|
||||
"tranche_id": 54,
|
||||
"tx": "0x7da34162df1b167ed5a3de6d98759af7100bcd51ec4e4c55abce2d145c98ba15"
|
||||
},
|
||||
{
|
||||
"amount": "89.42266666476",
|
||||
"user": "0x6F32AA5A6198329c16e438512F992a0548C856f9",
|
||||
"tranche_id": 54,
|
||||
"tx": "0x05d086879e23724f1397d41adcbe2f3316dd4039c240edbc3cbbcacf1ca1d00e"
|
||||
},
|
||||
{
|
||||
"amount": "52.2185023074",
|
||||
"user": "0x6F32AA5A6198329c16e438512F992a0548C856f9",
|
||||
@@ -1058,8 +978,8 @@
|
||||
}
|
||||
],
|
||||
"total_tokens": "858",
|
||||
"withdrawn_tokens": "296.67303008772",
|
||||
"remaining_tokens": "561.32696991228"
|
||||
"withdrawn_tokens": "207.25036342296",
|
||||
"remaining_tokens": "650.74963657704"
|
||||
},
|
||||
{
|
||||
"address": "0x0bBf7580e036eA5D69ABe679CC90117EeC2e3dc1",
|
||||
@@ -1086,17 +1006,10 @@
|
||||
"tx": "0x67855754e5b66b548c11d1c5377c8b7786d107d6064902318ba11dd6eb33b8d0"
|
||||
}
|
||||
],
|
||||
"withdrawals": [
|
||||
{
|
||||
"amount": "60.16845949026",
|
||||
"user": "0x15024E62134A8BFFCce11f5ce58CeCDe853038D7",
|
||||
"tranche_id": 54,
|
||||
"tx": "0x5f8e8e83b18d0bc718c83a9afb0594e95a7b8ea2eea3d83cfbb770be317cfd70"
|
||||
}
|
||||
],
|
||||
"withdrawals": [],
|
||||
"total_tokens": "177",
|
||||
"withdrawn_tokens": "60.16845949026",
|
||||
"remaining_tokens": "116.83154050974"
|
||||
"withdrawn_tokens": "0",
|
||||
"remaining_tokens": "177"
|
||||
},
|
||||
{
|
||||
"address": "0xAc1c2783d64c29CDB79608f5B66a37Fd1ea35dD6",
|
||||
@@ -1276,15 +1189,10 @@
|
||||
"tranche_id": 50,
|
||||
"tranche_start": "2023-10-01T00:00:00.000Z",
|
||||
"tranche_end": "2024-04-01T00:00:00.000Z",
|
||||
"total_added": "10000",
|
||||
"total_added": "2500",
|
||||
"total_removed": "0",
|
||||
"locked_amount": "10000",
|
||||
"locked_amount": "2500",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "7500",
|
||||
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
|
||||
"tx": "0xd89dcda79394278ed62c6d2d36c94d90c59a6cf2c0135903796db75c1797b644"
|
||||
},
|
||||
{
|
||||
"amount": "2500",
|
||||
"user": "0x6ad69157EfB1E17EBf6614D352D098589EaE3060",
|
||||
@@ -1293,21 +1201,6 @@
|
||||
],
|
||||
"withdrawals": [],
|
||||
"users": [
|
||||
{
|
||||
"address": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "7500",
|
||||
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
|
||||
"tranche_id": 50,
|
||||
"tx": "0xd89dcda79394278ed62c6d2d36c94d90c59a6cf2c0135903796db75c1797b644"
|
||||
}
|
||||
],
|
||||
"withdrawals": [],
|
||||
"total_tokens": "7500",
|
||||
"withdrawn_tokens": "0",
|
||||
"remaining_tokens": "7500"
|
||||
},
|
||||
{
|
||||
"address": "0x6ad69157EfB1E17EBf6614D352D098589EaE3060",
|
||||
"deposits": [
|
||||
@@ -1396,7 +1289,7 @@
|
||||
"tranche_start": "2023-03-06T00:00:00.000Z",
|
||||
"tranche_end": "2023-04-06T00:00:00.000Z",
|
||||
"total_added": "14099",
|
||||
"total_removed": "3112.49002352036",
|
||||
"total_removed": "2337.49002352036",
|
||||
"locked_amount": "0",
|
||||
"deposits": [
|
||||
{
|
||||
@@ -2196,16 +2089,6 @@
|
||||
"user": "0x4081455DB9Ec3B9b66f370Fa12161E6763c40779",
|
||||
"tx": "0xaf39ced29169a9e88e845cb6c069496920a0ad473b5bc4a0896df4154f51de10"
|
||||
},
|
||||
{
|
||||
"amount": "745",
|
||||
"user": "0x981D29D939cAa4ADC04A5e44224dBB45539E8730",
|
||||
"tx": "0x2db650d52bf11faa6981f557d876bf65b9a3f28861eec847f5d3aa6ee0089843"
|
||||
},
|
||||
{
|
||||
"amount": "30",
|
||||
"user": "0xd5BF378669d8ff4A3f65f577DDB55f2d7bb6f706",
|
||||
"tx": "0xcb0e4d8bc79d0a6dc08926fdf0fbc791cf78c93e6cfed2fdd7e6e27ffdb8e772"
|
||||
},
|
||||
{
|
||||
"amount": "19.9634296593",
|
||||
"user": "0x47e6217C1667e2B1fD177Ab8fD89037bCEC7dc6C",
|
||||
@@ -2912,17 +2795,10 @@
|
||||
"tx": "0x46d80a145d2f49ef3152cbfaa6d2bb054deabe21396ab43553a3f076e2db62e1"
|
||||
}
|
||||
],
|
||||
"withdrawals": [
|
||||
{
|
||||
"amount": "745",
|
||||
"user": "0x981D29D939cAa4ADC04A5e44224dBB45539E8730",
|
||||
"tranche_id": 53,
|
||||
"tx": "0x2db650d52bf11faa6981f557d876bf65b9a3f28861eec847f5d3aa6ee0089843"
|
||||
}
|
||||
],
|
||||
"withdrawals": [],
|
||||
"total_tokens": "745",
|
||||
"withdrawn_tokens": "745",
|
||||
"remaining_tokens": "0"
|
||||
"withdrawn_tokens": "0",
|
||||
"remaining_tokens": "745"
|
||||
},
|
||||
{
|
||||
"address": "0xF3C6c37B9EBB4D21e8bf119841C0e0e296e3EA04",
|
||||
@@ -4120,17 +3996,10 @@
|
||||
"tx": "0x819f57b15fef080ab898405133d798a683e5fd805682901e160f5c0187c5fb16"
|
||||
}
|
||||
],
|
||||
"withdrawals": [
|
||||
{
|
||||
"amount": "30",
|
||||
"user": "0xd5BF378669d8ff4A3f65f577DDB55f2d7bb6f706",
|
||||
"tranche_id": 53,
|
||||
"tx": "0xcb0e4d8bc79d0a6dc08926fdf0fbc791cf78c93e6cfed2fdd7e6e27ffdb8e772"
|
||||
}
|
||||
],
|
||||
"withdrawals": [],
|
||||
"total_tokens": "30",
|
||||
"withdrawn_tokens": "30",
|
||||
"remaining_tokens": "0"
|
||||
"withdrawn_tokens": "0",
|
||||
"remaining_tokens": "30"
|
||||
},
|
||||
{
|
||||
"address": "0xa7B28013389C4E74718Ca9d90fC2644aAf754A0e",
|
||||
@@ -4407,7 +4276,7 @@
|
||||
"tranche_end": "2023-12-05T00:00:00.000Z",
|
||||
"total_added": "86666.297",
|
||||
"total_removed": "0",
|
||||
"locked_amount": "54550.6765448265232514477",
|
||||
"locked_amount": "55975.3362468715096583895",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "86666.297",
|
||||
@@ -4473,7 +4342,7 @@
|
||||
"tranche_end": "2023-06-01T00:00:00.000Z",
|
||||
"total_added": "2500",
|
||||
"total_removed": "0",
|
||||
"locked_amount": "587.132586741961625",
|
||||
"locked_amount": "669.55064611314625",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "2500",
|
||||
@@ -4594,7 +4463,7 @@
|
||||
"tranche_end": "2023-09-01T00:00:00.000Z",
|
||||
"total_added": "17500",
|
||||
"total_removed": "0",
|
||||
"locked_amount": "12815.2549755938015",
|
||||
"locked_amount": "13385.91045189210925",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "12500",
|
||||
@@ -4860,8 +4729,8 @@
|
||||
"tranche_start": "2023-02-01T00:00:00.000Z",
|
||||
"tranche_end": "2023-08-01T00:00:00.000Z",
|
||||
"total_added": "37500",
|
||||
"total_removed": "12294.731717775",
|
||||
"locked_amount": "21493.76774478207375",
|
||||
"total_removed": "12151.298246325",
|
||||
"locked_amount": "22736.8688612645775",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "7500",
|
||||
@@ -4885,11 +4754,6 @@
|
||||
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
|
||||
"tx": "0x07414226f479239baa4081a211d749f54d8fe507067b1d3b122eb47bc69ff569"
|
||||
},
|
||||
{
|
||||
"amount": "143.43347145",
|
||||
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
|
||||
"tx": "0x2c8a0681fe27258861489cd7a891cf213c7ab930d86b1bf08cd54f2f3739a8b5"
|
||||
},
|
||||
{
|
||||
"amount": "158.943370125",
|
||||
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
|
||||
@@ -5040,12 +4904,6 @@
|
||||
"tranche_id": 34,
|
||||
"tx": "0x07414226f479239baa4081a211d749f54d8fe507067b1d3b122eb47bc69ff569"
|
||||
},
|
||||
{
|
||||
"amount": "143.43347145",
|
||||
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
|
||||
"tranche_id": 34,
|
||||
"tx": "0x2c8a0681fe27258861489cd7a891cf213c7ab930d86b1bf08cd54f2f3739a8b5"
|
||||
},
|
||||
{
|
||||
"amount": "158.943370125",
|
||||
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
|
||||
@@ -5186,8 +5044,8 @@
|
||||
}
|
||||
],
|
||||
"total_tokens": "7500",
|
||||
"withdrawn_tokens": "3059.282439375",
|
||||
"remaining_tokens": "4440.717560625"
|
||||
"withdrawn_tokens": "2915.848967925",
|
||||
"remaining_tokens": "4584.151032075"
|
||||
},
|
||||
{
|
||||
"address": "0x0B4e6fcE839B01ef43DA6F890FAC7B1Afb004600",
|
||||
@@ -5225,7 +5083,7 @@
|
||||
"tranche_end": "2023-12-05T00:00:00.000Z",
|
||||
"total_added": "129999.45",
|
||||
"total_removed": "0",
|
||||
"locked_amount": "54500.905916308673138415",
|
||||
"locked_amount": "55924.265795633105973795",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "129999.45",
|
||||
@@ -5258,7 +5116,7 @@
|
||||
"tranche_end": "2024-04-01T00:00:00.000Z",
|
||||
"total_added": "54144.7663",
|
||||
"total_removed": "0",
|
||||
"locked_amount": "51443.92658193328217573951",
|
||||
"locked_amount": "52331.55083829013672190566",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "54144.7663",
|
||||
@@ -5291,7 +5149,7 @@
|
||||
"tranche_end": "2023-09-03T00:00:00.000Z",
|
||||
"total_added": "62600",
|
||||
"total_removed": "0",
|
||||
"locked_amount": "23452.40436960933488",
|
||||
"locked_amount": "24481.45142059868271",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "10000",
|
||||
@@ -5484,7 +5342,7 @@
|
||||
"tranche_end": "2023-09-17T00:00:00.000Z",
|
||||
"total_added": "5000",
|
||||
"total_removed": "0",
|
||||
"locked_amount": "2064.976059107052",
|
||||
"locked_amount": "2147.16831557584975",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "5000",
|
||||
@@ -6024,8 +5882,8 @@
|
||||
"tranche_start": "2022-11-01T00:00:00.000Z",
|
||||
"tranche_end": "2023-05-01T00:00:00.000Z",
|
||||
"total_added": "22500",
|
||||
"total_removed": "6871.43713545",
|
||||
"locked_amount": "1459.796558471453925",
|
||||
"total_removed": "6727.9921539",
|
||||
"locked_amount": "2205.657228360958425",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "7500",
|
||||
@@ -6049,11 +5907,6 @@
|
||||
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
|
||||
"tx": "0xea8772c7ce65ecfdf87e34cf61b2614212c07d195a9e4d6962fcf758ba08c442"
|
||||
},
|
||||
{
|
||||
"amount": "143.44498155",
|
||||
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
|
||||
"tx": "0x15840e283faffb39a5d0e3389137ec92cec3deee7df535a534908ccd7d6b0873"
|
||||
},
|
||||
{
|
||||
"amount": "158.9433702",
|
||||
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
|
||||
@@ -6259,12 +6112,6 @@
|
||||
"tranche_id": 33,
|
||||
"tx": "0xea8772c7ce65ecfdf87e34cf61b2614212c07d195a9e4d6962fcf758ba08c442"
|
||||
},
|
||||
{
|
||||
"amount": "143.44498155",
|
||||
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
|
||||
"tranche_id": 33,
|
||||
"tx": "0x15840e283faffb39a5d0e3389137ec92cec3deee7df535a534908ccd7d6b0873"
|
||||
},
|
||||
{
|
||||
"amount": "158.9433702",
|
||||
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
|
||||
@@ -6483,8 +6330,8 @@
|
||||
}
|
||||
],
|
||||
"total_tokens": "7500",
|
||||
"withdrawn_tokens": "6871.43713545",
|
||||
"remaining_tokens": "628.56286455"
|
||||
"withdrawn_tokens": "6727.9921539",
|
||||
"remaining_tokens": "772.0078461"
|
||||
},
|
||||
{
|
||||
"address": "0x2539b51EbDE65a75672aBcfE9439a706a99D18D1",
|
||||
@@ -6509,7 +6356,7 @@
|
||||
"tranche_end": "2023-06-02T00:00:00.000Z",
|
||||
"total_added": "1939928.38",
|
||||
"total_removed": "928642.9598472029154",
|
||||
"locked_amount": "232489.7989013972454998708",
|
||||
"locked_amount": "264379.2170894088612502104",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "1852091.69",
|
||||
@@ -40325,7 +40172,7 @@
|
||||
"tranche_end": "2023-06-05T00:00:00.000Z",
|
||||
"total_added": "3732368.4671",
|
||||
"total_removed": "715655.108029600523393",
|
||||
"locked_amount": "381757.201289964680148718477",
|
||||
"locked_amount": "430760.134328755393533225946",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "1998.95815",
|
||||
@@ -41717,8 +41564,8 @@
|
||||
"tranche_start": "2022-06-05T00:00:00.000Z",
|
||||
"tranche_end": "2023-12-05T00:00:00.000Z",
|
||||
"total_added": "15870102.715470999700000001",
|
||||
"total_removed": "835090.78677275839915352",
|
||||
"locked_amount": "6653374.1102600029976151216559241015233847",
|
||||
"total_removed": "832085.86755480045340352",
|
||||
"locked_amount": "6827135.3645265337866511762819105499945931",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "16249.93",
|
||||
@@ -42247,36 +42094,6 @@
|
||||
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
|
||||
"tx": "0x6c9a470f50be5317670260a9d3842614e4dc7f32e9c483582f3d1a8d992864d4"
|
||||
},
|
||||
{
|
||||
"amount": "564.068956625387125",
|
||||
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
|
||||
"tx": "0xb27dbf718e77796051638b35d898c62ffe6bbe0c2aed4e5adaf7cacbcfd1b17a"
|
||||
},
|
||||
{
|
||||
"amount": "404.86444883989775",
|
||||
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
|
||||
"tx": "0x07d1c74ebc4f402882d688c06da30455c4890f66792149aa34dd5c5c7f4a5114"
|
||||
},
|
||||
{
|
||||
"amount": "673.58795260844725",
|
||||
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
|
||||
"tx": "0x1283a5378ec8e25b00c1260eae937285858c4f9ba689ac7c133e1a96602be122"
|
||||
},
|
||||
{
|
||||
"amount": "278.739682710677375",
|
||||
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
|
||||
"tx": "0xd23bde9b41f3e73644f3e3bf69c25739dc721014516803de4e76724b9ccb6b76"
|
||||
},
|
||||
{
|
||||
"amount": "249.877276409002",
|
||||
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
|
||||
"tx": "0x94df9afacf00f3d2a01e8856c83050a2737cd31f7736ae472bfbb3f19e66f5e4"
|
||||
},
|
||||
{
|
||||
"amount": "833.78090076453425",
|
||||
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
|
||||
"tx": "0xa56a064890829624fb37f887adb87987dfafd709ebea2cdf423c91a38e91b82f"
|
||||
},
|
||||
{
|
||||
"amount": "998.454771147362375",
|
||||
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
|
||||
@@ -44339,42 +44156,6 @@
|
||||
"tranche_id": 2,
|
||||
"tx": "0x6c9a470f50be5317670260a9d3842614e4dc7f32e9c483582f3d1a8d992864d4"
|
||||
},
|
||||
{
|
||||
"amount": "564.068956625387125",
|
||||
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
|
||||
"tranche_id": 2,
|
||||
"tx": "0xb27dbf718e77796051638b35d898c62ffe6bbe0c2aed4e5adaf7cacbcfd1b17a"
|
||||
},
|
||||
{
|
||||
"amount": "404.86444883989775",
|
||||
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
|
||||
"tranche_id": 2,
|
||||
"tx": "0x07d1c74ebc4f402882d688c06da30455c4890f66792149aa34dd5c5c7f4a5114"
|
||||
},
|
||||
{
|
||||
"amount": "673.58795260844725",
|
||||
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
|
||||
"tranche_id": 2,
|
||||
"tx": "0x1283a5378ec8e25b00c1260eae937285858c4f9ba689ac7c133e1a96602be122"
|
||||
},
|
||||
{
|
||||
"amount": "278.739682710677375",
|
||||
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
|
||||
"tranche_id": 2,
|
||||
"tx": "0xd23bde9b41f3e73644f3e3bf69c25739dc721014516803de4e76724b9ccb6b76"
|
||||
},
|
||||
{
|
||||
"amount": "249.877276409002",
|
||||
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
|
||||
"tranche_id": 2,
|
||||
"tx": "0x94df9afacf00f3d2a01e8856c83050a2737cd31f7736ae472bfbb3f19e66f5e4"
|
||||
},
|
||||
{
|
||||
"amount": "833.78090076453425",
|
||||
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
|
||||
"tranche_id": 2,
|
||||
"tx": "0xa56a064890829624fb37f887adb87987dfafd709ebea2cdf423c91a38e91b82f"
|
||||
},
|
||||
{
|
||||
"amount": "998.454771147362375",
|
||||
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
|
||||
@@ -45811,8 +45592,8 @@
|
||||
}
|
||||
],
|
||||
"total_tokens": "259998.8875",
|
||||
"withdrawn_tokens": "150733.980517927478375",
|
||||
"remaining_tokens": "109264.906982072521625"
|
||||
"withdrawn_tokens": "147729.061299969532625",
|
||||
"remaining_tokens": "112269.826200030467375"
|
||||
},
|
||||
{
|
||||
"address": "0x89051CAb67Bc7F8CC44F7e270c6EDaf1EC57676c",
|
||||
@@ -48163,8 +47944,8 @@
|
||||
"tranche_start": "2021-11-05T00:00:00.000Z",
|
||||
"tranche_end": "2023-05-05T00:00:00.000Z",
|
||||
"total_added": "14597706.0446472999",
|
||||
"total_removed": "5844740.479416869886902626",
|
||||
"locked_amount": "420907.270106923449603004591821117",
|
||||
"total_removed": "5825428.980241267385732886",
|
||||
"locked_amount": "581322.550569700658722837432388667",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "129284.449",
|
||||
@@ -48398,51 +48179,6 @@
|
||||
"user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b",
|
||||
"tx": "0x98bfad2d34e3a47b0487e1219456287f109dfb14658df5848c2f801247039314"
|
||||
},
|
||||
{
|
||||
"amount": "771.74274329335158625",
|
||||
"user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b",
|
||||
"tx": "0xca63b7531ec4b383cd6a69ed94313e68cac27f5af75b2ba1e6d5607f0f25810a"
|
||||
},
|
||||
{
|
||||
"amount": "570.76807052329924925",
|
||||
"user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b",
|
||||
"tx": "0x2825f014fc3902bc2e8f41823cee18aefa209a0763f9a5bafe6b6b5b956bf45a"
|
||||
},
|
||||
{
|
||||
"amount": "14990.57490287555017806",
|
||||
"user": "0x66827bCD635f2bB1779d68c46aEB16541bCA6ba8",
|
||||
"tx": "0x0d970fd46e511e419ec9c040ac873302023b1f14ba66cda4efc9b76439569cba"
|
||||
},
|
||||
{
|
||||
"amount": "155.90688399188605818",
|
||||
"user": "0x66827bCD635f2bB1779d68c46aEB16541bCA6ba8",
|
||||
"tx": "0x2830acf9d15cbaeaec3ad60506f4c6542a63ffbedeab7408f30adb495e8ec7cb"
|
||||
},
|
||||
{
|
||||
"amount": "933.80141068366436375",
|
||||
"user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b",
|
||||
"tx": "0x07fdd83f04929f7f0adba54f0af9c1cb4e266e430ed0188ca1f02b7b528f73be"
|
||||
},
|
||||
{
|
||||
"amount": "386.69353993983405875",
|
||||
"user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b",
|
||||
"tx": "0x3f8387f07e77c7734dc9fde19c9b0882c9192685f97be72c15f681e4be3f39ca"
|
||||
},
|
||||
{
|
||||
"amount": "346.2245518019684565",
|
||||
"user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b",
|
||||
"tx": "0x467e16d3f10ba66de4ab7452682f50266a731200be9e036c9b35692e0d447572"
|
||||
},
|
||||
{
|
||||
"amount": "472.655886985174737",
|
||||
"user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b",
|
||||
"tx": "0xca4c0571969c50f47990b214485913df8e2b7d1d92cdd6ac4c8bcc6ce760ce0b"
|
||||
},
|
||||
{
|
||||
"amount": "683.131185507772482",
|
||||
"user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b",
|
||||
"tx": "0x5cee329248d32efdbd9e7f2b223f1714843a154b530cafd9f5df43a1d45c625b"
|
||||
},
|
||||
{
|
||||
"amount": "37462.74710421168752574",
|
||||
"user": "0x66827bCD635f2bB1779d68c46aEB16541bCA6ba8",
|
||||
@@ -51682,48 +51418,6 @@
|
||||
"tranche_id": 3,
|
||||
"tx": "0x98bfad2d34e3a47b0487e1219456287f109dfb14658df5848c2f801247039314"
|
||||
},
|
||||
{
|
||||
"amount": "771.74274329335158625",
|
||||
"user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b",
|
||||
"tranche_id": 3,
|
||||
"tx": "0xca63b7531ec4b383cd6a69ed94313e68cac27f5af75b2ba1e6d5607f0f25810a"
|
||||
},
|
||||
{
|
||||
"amount": "570.76807052329924925",
|
||||
"user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b",
|
||||
"tranche_id": 3,
|
||||
"tx": "0x2825f014fc3902bc2e8f41823cee18aefa209a0763f9a5bafe6b6b5b956bf45a"
|
||||
},
|
||||
{
|
||||
"amount": "933.80141068366436375",
|
||||
"user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b",
|
||||
"tranche_id": 3,
|
||||
"tx": "0x07fdd83f04929f7f0adba54f0af9c1cb4e266e430ed0188ca1f02b7b528f73be"
|
||||
},
|
||||
{
|
||||
"amount": "386.69353993983405875",
|
||||
"user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b",
|
||||
"tranche_id": 3,
|
||||
"tx": "0x3f8387f07e77c7734dc9fde19c9b0882c9192685f97be72c15f681e4be3f39ca"
|
||||
},
|
||||
{
|
||||
"amount": "346.2245518019684565",
|
||||
"user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b",
|
||||
"tranche_id": 3,
|
||||
"tx": "0x467e16d3f10ba66de4ab7452682f50266a731200be9e036c9b35692e0d447572"
|
||||
},
|
||||
{
|
||||
"amount": "472.655886985174737",
|
||||
"user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b",
|
||||
"tranche_id": 3,
|
||||
"tx": "0xca4c0571969c50f47990b214485913df8e2b7d1d92cdd6ac4c8bcc6ce760ce0b"
|
||||
},
|
||||
{
|
||||
"amount": "683.131185507772482",
|
||||
"user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b",
|
||||
"tranche_id": 3,
|
||||
"tx": "0x5cee329248d32efdbd9e7f2b223f1714843a154b530cafd9f5df43a1d45c625b"
|
||||
},
|
||||
{
|
||||
"amount": "1383.345213676043696",
|
||||
"user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b",
|
||||
@@ -54462,8 +54156,8 @@
|
||||
}
|
||||
],
|
||||
"total_tokens": "359123.469575",
|
||||
"withdrawn_tokens": "348403.655627419118415",
|
||||
"remaining_tokens": "10719.813947580881585"
|
||||
"withdrawn_tokens": "344238.6382386840534815",
|
||||
"remaining_tokens": "14884.8313363159465185"
|
||||
},
|
||||
{
|
||||
"address": "0xBdd412797c1B78535Afc5F71503b91fAbD0160fB",
|
||||
@@ -54699,18 +54393,6 @@
|
||||
"tranche_id": 3,
|
||||
"tx": "0xa22abf6f3db05a14a0c55fc053a05218f412ad15be9a022580b2ca99ed805bf7"
|
||||
},
|
||||
{
|
||||
"amount": "14990.57490287555017806",
|
||||
"user": "0x66827bCD635f2bB1779d68c46aEB16541bCA6ba8",
|
||||
"tranche_id": 3,
|
||||
"tx": "0x0d970fd46e511e419ec9c040ac873302023b1f14ba66cda4efc9b76439569cba"
|
||||
},
|
||||
{
|
||||
"amount": "155.90688399188605818",
|
||||
"user": "0x66827bCD635f2bB1779d68c46aEB16541bCA6ba8",
|
||||
"tranche_id": 3,
|
||||
"tx": "0x2830acf9d15cbaeaec3ad60506f4c6542a63ffbedeab7408f30adb495e8ec7cb"
|
||||
},
|
||||
{
|
||||
"amount": "37462.74710421168752574",
|
||||
"user": "0x66827bCD635f2bB1779d68c46aEB16541bCA6ba8",
|
||||
@@ -55085,8 +54767,8 @@
|
||||
}
|
||||
],
|
||||
"total_tokens": "1266324.603486",
|
||||
"withdrawn_tokens": "1220819.41847917671599364",
|
||||
"remaining_tokens": "45505.18500682328400636"
|
||||
"withdrawn_tokens": "1205672.9366923092797574",
|
||||
"remaining_tokens": "60651.6667936907202426"
|
||||
},
|
||||
{
|
||||
"address": "0xC5d9221EB9c28A69859264c0A2Fe0d3272228296",
|
||||
@@ -55914,7 +55596,7 @@
|
||||
"tranche_start": "2021-10-05T00:00:00.000Z",
|
||||
"tranche_end": "2023-04-05T00:00:00.000Z",
|
||||
"total_added": "5778205.3912159303",
|
||||
"total_removed": "3421810.610823310514313769",
|
||||
"total_removed": "3421590.665302980803025769",
|
||||
"locked_amount": "0",
|
||||
"deposits": [
|
||||
{
|
||||
@@ -56059,11 +55741,6 @@
|
||||
}
|
||||
],
|
||||
"withdrawals": [
|
||||
{
|
||||
"amount": "219.945520329711288",
|
||||
"user": "0xafa64cCa337eFEE0AD827F6C2684e69275226e90",
|
||||
"tx": "0x4680e076a50f1fde4339e9edacd3bd4cb8fe3ce5a3b0a7cb7917f7ace67b537a"
|
||||
},
|
||||
{
|
||||
"amount": "10509.407528939748325",
|
||||
"user": "0xBc934494675a6ceB639B9EfEe5b9C0f017D35a75",
|
||||
@@ -57722,12 +57399,6 @@
|
||||
}
|
||||
],
|
||||
"withdrawals": [
|
||||
{
|
||||
"amount": "219.945520329711288",
|
||||
"user": "0xafa64cCa337eFEE0AD827F6C2684e69275226e90",
|
||||
"tranche_id": 4,
|
||||
"tx": "0x4680e076a50f1fde4339e9edacd3bd4cb8fe3ce5a3b0a7cb7917f7ace67b537a"
|
||||
},
|
||||
{
|
||||
"amount": "13312.213609122936225",
|
||||
"user": "0xafa64cCa337eFEE0AD827F6C2684e69275226e90",
|
||||
@@ -57994,8 +57665,8 @@
|
||||
}
|
||||
],
|
||||
"total_tokens": "331498.5873",
|
||||
"withdrawn_tokens": "331498.5873",
|
||||
"remaining_tokens": "0"
|
||||
"withdrawn_tokens": "331278.641779670288712",
|
||||
"remaining_tokens": "219.945520329711288"
|
||||
},
|
||||
{
|
||||
"address": "0x16da609341ed67750A8BCC5AAa2005471006Cd77",
|
||||
@@ -58097,8 +57768,8 @@
|
||||
"tranche_start": "2022-06-05T00:00:00.000Z",
|
||||
"tranche_end": "2023-06-05T00:00:00.000Z",
|
||||
"total_added": "472355.6199999996",
|
||||
"total_removed": "43120.0853616251849",
|
||||
"locked_amount": "60491.61076159115414066601116184",
|
||||
"total_removed": "42408.3252372941949",
|
||||
"locked_amount": "68256.405614294737212900293658024",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "3000",
|
||||
@@ -64732,26 +64403,6 @@
|
||||
"user": "0x9347177014d3fEA775b5B29fFC96bF6B911686F9",
|
||||
"tx": "0xd1d4724eae5566e3162c1b210a19d73f5a2e4b9b3d52d9d1e77c4b9cb9bdc2e1"
|
||||
},
|
||||
{
|
||||
"amount": "26.662404872",
|
||||
"user": "0x4eD2b3c68BB4fda084ce1591a210F4aC8b71234A",
|
||||
"tx": "0x7c02dcf328b3538b3c8cd11d19cb0d21859ebfdda209b30546358092870deb23"
|
||||
},
|
||||
{
|
||||
"amount": "343.420230844",
|
||||
"user": "0x981D29D939cAa4ADC04A5e44224dBB45539E8730",
|
||||
"tx": "0x53b7d1e71a9bc909cd594c4d62380ba31cd0d8687a5894ad62bfa48733e73174"
|
||||
},
|
||||
{
|
||||
"amount": "257.36242012299",
|
||||
"user": "0x07d1a589447395fe78bca3627Cb82F7C3310f598",
|
||||
"tx": "0x71bd2cf2152fd2175e3a63a2ba70ffc4db0d8d47c8feb493ae9de59c9164ffcf"
|
||||
},
|
||||
{
|
||||
"amount": "84.315068492",
|
||||
"user": "0x40447e8eDee2F8D0088844E35Ff7aD2Bb5fCabfb",
|
||||
"tx": "0x2aa7bdc96b05e86a7c689062112e932630e7e14b7f05534af1a5d140fc2a88c4"
|
||||
},
|
||||
{
|
||||
"amount": "19.918569252",
|
||||
"user": "0xcB555C5602cC0b2434A27d277e14Cb5C7bF4Ead1",
|
||||
@@ -73498,17 +73149,10 @@
|
||||
"tx": "0x057f65938b1360b6d2c4ebf5e789c67897cf60cb6a13f947004def03891afbd8"
|
||||
}
|
||||
],
|
||||
"withdrawals": [
|
||||
{
|
||||
"amount": "257.36242012299",
|
||||
"user": "0x07d1a589447395fe78bca3627Cb82F7C3310f598",
|
||||
"tranche_id": 5,
|
||||
"tx": "0x71bd2cf2152fd2175e3a63a2ba70ffc4db0d8d47c8feb493ae9de59c9164ffcf"
|
||||
}
|
||||
],
|
||||
"withdrawals": [],
|
||||
"total_tokens": "299",
|
||||
"withdrawn_tokens": "257.36242012299",
|
||||
"remaining_tokens": "41.63757987701"
|
||||
"withdrawn_tokens": "0",
|
||||
"remaining_tokens": "299"
|
||||
},
|
||||
{
|
||||
"address": "0xA257E4Fcf635a1ba1BA32C6f063a3037f1741453",
|
||||
@@ -76349,12 +75993,6 @@
|
||||
}
|
||||
],
|
||||
"withdrawals": [
|
||||
{
|
||||
"amount": "84.315068492",
|
||||
"user": "0x40447e8eDee2F8D0088844E35Ff7aD2Bb5fCabfb",
|
||||
"tranche_id": 5,
|
||||
"tx": "0x2aa7bdc96b05e86a7c689062112e932630e7e14b7f05534af1a5d140fc2a88c4"
|
||||
},
|
||||
{
|
||||
"amount": "89.067954084",
|
||||
"user": "0x40447e8eDee2F8D0088844E35Ff7aD2Bb5fCabfb",
|
||||
@@ -76363,8 +76001,8 @@
|
||||
}
|
||||
],
|
||||
"total_tokens": "200",
|
||||
"withdrawn_tokens": "173.383022576",
|
||||
"remaining_tokens": "26.616977424"
|
||||
"withdrawn_tokens": "89.067954084",
|
||||
"remaining_tokens": "110.932045916"
|
||||
},
|
||||
{
|
||||
"address": "0x1bAc2912394A42Cec87767327FC039f2EfBB95Dd",
|
||||
@@ -78725,12 +78363,6 @@
|
||||
}
|
||||
],
|
||||
"withdrawals": [
|
||||
{
|
||||
"amount": "26.662404872",
|
||||
"user": "0x4eD2b3c68BB4fda084ce1591a210F4aC8b71234A",
|
||||
"tranche_id": 5,
|
||||
"tx": "0x7c02dcf328b3538b3c8cd11d19cb0d21859ebfdda209b30546358092870deb23"
|
||||
},
|
||||
{
|
||||
"amount": "15.02267884",
|
||||
"user": "0x4eD2b3c68BB4fda084ce1591a210F4aC8b71234A",
|
||||
@@ -78829,8 +78461,8 @@
|
||||
}
|
||||
],
|
||||
"total_tokens": "400",
|
||||
"withdrawn_tokens": "342.891159308",
|
||||
"remaining_tokens": "57.108840692"
|
||||
"withdrawn_tokens": "316.228754436",
|
||||
"remaining_tokens": "83.771245564"
|
||||
},
|
||||
{
|
||||
"address": "0x5c90765F50629570738fEe7b7FA82ae118f81Ed1",
|
||||
@@ -79986,17 +79618,10 @@
|
||||
"tx": "0x9f916cf09e8a3c4ade0ffce5190db464d0a2b1dadba78e1ee7ba5d6e751d6148"
|
||||
}
|
||||
],
|
||||
"withdrawals": [
|
||||
{
|
||||
"amount": "343.420230844",
|
||||
"user": "0x981D29D939cAa4ADC04A5e44224dBB45539E8730",
|
||||
"tranche_id": 5,
|
||||
"tx": "0x53b7d1e71a9bc909cd594c4d62380ba31cd0d8687a5894ad62bfa48733e73174"
|
||||
}
|
||||
],
|
||||
"withdrawals": [],
|
||||
"total_tokens": "400",
|
||||
"withdrawn_tokens": "343.420230844",
|
||||
"remaining_tokens": "56.579769156"
|
||||
"withdrawn_tokens": "0",
|
||||
"remaining_tokens": "400"
|
||||
},
|
||||
{
|
||||
"address": "0x5f59907Ef66f016aE218Db5CE06917Ed6a87F02D",
|
||||
@@ -143295,16 +142920,6 @@
|
||||
"user": "0x759C9ABABA492500c4c730bEB568B5b851Dec2c7",
|
||||
"tx": "0x882126a437003e60226611d623415bf2e00befa69d76aba57fd4cf1c5b403d1f"
|
||||
},
|
||||
{
|
||||
"amount": "0",
|
||||
"user": "0x759C9ABABA492500c4c730bEB568B5b851Dec2c7",
|
||||
"tx": "0xf4cdc0b0f6c24cb52dd0126bc6bff9c75ec715502809f9c80d675327beb4889d"
|
||||
},
|
||||
{
|
||||
"amount": "0",
|
||||
"user": "0x759C9ABABA492500c4c730bEB568B5b851Dec2c7",
|
||||
"tx": "0x11aafa8a2fb510a50d52e5d1e149c5c2027ad2c3dc2d9ef67c18d391c0331c49"
|
||||
},
|
||||
{
|
||||
"amount": "0",
|
||||
"user": "0x759C9ABABA492500c4c730bEB568B5b851Dec2c7",
|
||||
@@ -143581,18 +143196,6 @@
|
||||
"tranche_id": 9,
|
||||
"tx": "0x882126a437003e60226611d623415bf2e00befa69d76aba57fd4cf1c5b403d1f"
|
||||
},
|
||||
{
|
||||
"amount": "0",
|
||||
"user": "0x759C9ABABA492500c4c730bEB568B5b851Dec2c7",
|
||||
"tranche_id": 9,
|
||||
"tx": "0xf4cdc0b0f6c24cb52dd0126bc6bff9c75ec715502809f9c80d675327beb4889d"
|
||||
},
|
||||
{
|
||||
"amount": "0",
|
||||
"user": "0x759C9ABABA492500c4c730bEB568B5b851Dec2c7",
|
||||
"tranche_id": 9,
|
||||
"tx": "0x11aafa8a2fb510a50d52e5d1e149c5c2027ad2c3dc2d9ef67c18d391c0331c49"
|
||||
},
|
||||
{
|
||||
"amount": "0",
|
||||
"user": "0x759C9ABABA492500c4c730bEB568B5b851Dec2c7",
|
||||
|
||||
@@ -29,10 +29,7 @@ import { LiquidityContainer } from '../liquidity/liquidity';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import type { PinnedAsset } from '@vegaprotocol/accounts';
|
||||
import { useScreenDimensions } from '@vegaprotocol/react-helpers';
|
||||
import {
|
||||
useMarketClickHandler,
|
||||
useMarketLiquidityClickHandler,
|
||||
} from '../../lib/hooks/use-market-click-handler';
|
||||
import { useMarketClickHandler } from '../../lib/hooks/use-market-click-handler';
|
||||
|
||||
type MarketDependantView =
|
||||
| typeof CandlesChartContainer
|
||||
@@ -82,7 +79,6 @@ const MarketBottomPanel = memo(
|
||||
({ marketId, pinnedAsset }: BottomPanelProps) => {
|
||||
const { screenSize } = useScreenDimensions();
|
||||
const onMarketClick = useMarketClickHandler(true);
|
||||
const onOrderTypeClick = useMarketLiquidityClickHandler(true);
|
||||
|
||||
return 'xxxl' === screenSize ? (
|
||||
<ResizableGrid proportionalLayout minSize={200}>
|
||||
@@ -98,7 +94,6 @@ const MarketBottomPanel = memo(
|
||||
<TradingViews.Orders
|
||||
marketId={marketId}
|
||||
onMarketClick={onMarketClick}
|
||||
onOrderTypeClick={onOrderTypeClick}
|
||||
enforceBottomPlaceholder
|
||||
/>
|
||||
</VegaWalletContainer>
|
||||
@@ -155,7 +150,6 @@ const MarketBottomPanel = memo(
|
||||
<TradingViews.Orders
|
||||
marketId={marketId}
|
||||
onMarketClick={onMarketClick}
|
||||
onOrderTypeClick={onOrderTypeClick}
|
||||
enforceBottomPlaceholder
|
||||
/>
|
||||
</VegaWalletContainer>
|
||||
@@ -299,7 +293,6 @@ interface TradePanelsProps {
|
||||
market: Market | null;
|
||||
onSelect: (marketId: string, metaKey?: boolean) => void;
|
||||
onMarketClick?: (marketId: string) => void;
|
||||
onOrderTypeClick?: (marketId: string) => void;
|
||||
onClickCollateral: () => void;
|
||||
pinnedAsset?: PinnedAsset;
|
||||
}
|
||||
@@ -310,16 +303,12 @@ export const TradePanels = ({
|
||||
onClickCollateral,
|
||||
pinnedAsset,
|
||||
}: TradePanelsProps) => {
|
||||
const onMarketClick = useMarketClickHandler(true);
|
||||
const onOrderTypeClick = useMarketLiquidityClickHandler(true);
|
||||
|
||||
const [view, setView] = useState<TradingView>('Candles');
|
||||
const renderView = () => {
|
||||
const Component = memo<{
|
||||
marketId: string;
|
||||
onSelect: (marketId: string, metaKey?: boolean) => void;
|
||||
onMarketClick?: (marketId: string) => void;
|
||||
onOrderTypeClick?: (marketId: string) => void;
|
||||
onClickCollateral: () => void;
|
||||
pinnedAsset?: PinnedAsset;
|
||||
}>(TradingViews[view]);
|
||||
@@ -336,8 +325,6 @@ export const TradePanels = ({
|
||||
onSelect={onSelect}
|
||||
onClickCollateral={onClickCollateral}
|
||||
pinnedAsset={pinnedAsset}
|
||||
onMarketClick={onMarketClick}
|
||||
onOrderTypeClick={onOrderTypeClick}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -19,10 +19,7 @@ import { usePageTitleStore } from '../../stores';
|
||||
import { LedgerContainer } from '@vegaprotocol/ledger';
|
||||
import { AccountsContainer } from '../../components/accounts-container';
|
||||
import { AccountHistoryContainer } from './account-history-container';
|
||||
import {
|
||||
useMarketClickHandler,
|
||||
useMarketLiquidityClickHandler,
|
||||
} from '../../lib/hooks/use-market-click-handler';
|
||||
import { useMarketClickHandler } from '../../lib/hooks/use-market-click-handler';
|
||||
|
||||
export const Portfolio = () => {
|
||||
const { updateTitle } = usePageTitleStore((store) => ({
|
||||
@@ -34,7 +31,6 @@ export const Portfolio = () => {
|
||||
}, [updateTitle]);
|
||||
|
||||
const onMarketClick = useMarketClickHandler(true);
|
||||
const onOrderTypeClick = useMarketLiquidityClickHandler(true);
|
||||
|
||||
const wrapperClasses = 'h-full max-h-full flex flex-col';
|
||||
return (
|
||||
@@ -58,10 +54,7 @@ export const Portfolio = () => {
|
||||
</Tab>
|
||||
<Tab id="orders" name={t('Orders')}>
|
||||
<VegaWalletContainer>
|
||||
<OrderListContainer
|
||||
onMarketClick={onMarketClick}
|
||||
onOrderTypeClick={onOrderTypeClick}
|
||||
/>
|
||||
<OrderListContainer onMarketClick={onMarketClick} />
|
||||
</VegaWalletContainer>
|
||||
</Tab>
|
||||
<Tab id="fills" name={t('Fills')}>
|
||||
|
||||
+17
-35
@@ -23,44 +23,26 @@ const generateJsx = (context: VegaWalletContextShape) => {
|
||||
);
|
||||
};
|
||||
|
||||
describe('VegaWalletConnectButton', () => {
|
||||
it('should fire dialog when not connected', () => {
|
||||
render(generateJsx({ pubKey: null } as VegaWalletContextShape));
|
||||
it('Not connected', () => {
|
||||
render(generateJsx({ pubKey: null } as VegaWalletContextShape));
|
||||
|
||||
const button = screen.getByTestId('connect-vega-wallet');
|
||||
expect(button).toHaveTextContent('Connect Vega wallet');
|
||||
fireEvent.click(button);
|
||||
expect(mockUpdateDialogOpen).toHaveBeenCalled();
|
||||
});
|
||||
const button = screen.getByTestId('connect-vega-wallet');
|
||||
expect(button).toHaveTextContent('Connect Vega wallet');
|
||||
fireEvent.click(button);
|
||||
expect(mockUpdateDialogOpen).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should retrieve keys when connected', async () => {
|
||||
const pubKey = { publicKey: '123456__123456', name: 'test' };
|
||||
const pubKey2 = { publicKey: '123456__123457', name: 'test2' };
|
||||
render(
|
||||
generateJsx({
|
||||
pubKey: pubKey.publicKey,
|
||||
pubKeys: [pubKey],
|
||||
fetchPubKeys: () => Promise.resolve([pubKey, pubKey2]),
|
||||
} as VegaWalletContextShape)
|
||||
);
|
||||
|
||||
const button = screen.getByTestId('manage-vega-wallet');
|
||||
expect(button).toHaveTextContent(truncateByChars(pubKey.publicKey));
|
||||
userEvent.click(button);
|
||||
expect(mockUpdateDialogOpen).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should fetch keys when connected', async () => {
|
||||
const pubKey = { publicKey: '123456__123456', name: 'test' };
|
||||
const context = {
|
||||
it('Connected', async () => {
|
||||
const pubKey = { publicKey: '123456__123456', name: 'test' };
|
||||
render(
|
||||
generateJsx({
|
||||
pubKey: pubKey.publicKey,
|
||||
pubKeys: [pubKey],
|
||||
} as VegaWalletContextShape;
|
||||
render(generateJsx(context));
|
||||
} as VegaWalletContextShape)
|
||||
);
|
||||
|
||||
const button = screen.getByTestId('manage-vega-wallet');
|
||||
expect(button).toHaveTextContent(truncateByChars(pubKey.publicKey));
|
||||
userEvent.click(button);
|
||||
expect(mockUpdateDialogOpen).not.toHaveBeenCalled();
|
||||
});
|
||||
const button = screen.getByTestId('manage-vega-wallet');
|
||||
expect(button).toHaveTextContent(truncateByChars(pubKey.publicKey));
|
||||
userEvent.click(button);
|
||||
expect(mockUpdateDialogOpen).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
@@ -29,7 +29,7 @@ const MobileWalletButton = ({
|
||||
isConnected?: boolean;
|
||||
activeKey?: PubKey;
|
||||
}) => {
|
||||
const { pubKeys, selectPubKey, disconnect, fetchPubKeys } = useVegaWallet();
|
||||
const { pubKeys, selectPubKey, disconnect } = useVegaWallet();
|
||||
const openVegaWalletDialog = useVegaWalletDialogStore(
|
||||
(store) => store.openVegaWalletDialog
|
||||
);
|
||||
@@ -46,12 +46,9 @@ const MobileWalletButton = ({
|
||||
openVegaWalletDialog();
|
||||
setDrawerOpen(false);
|
||||
} else {
|
||||
if (fetchPubKeys) {
|
||||
fetchPubKeys();
|
||||
}
|
||||
setDrawerOpen(!drawerOpen);
|
||||
}
|
||||
}, [drawerOpen, fetchPubKeys, isConnected, openVegaWalletDialog]);
|
||||
}, [drawerOpen, isConnected, openVegaWalletDialog]);
|
||||
|
||||
const iconClass = drawerOpen
|
||||
? 'hidden'
|
||||
@@ -148,14 +145,8 @@ export const VegaWalletConnectButton = () => {
|
||||
(store) => store.openVegaWalletDialog
|
||||
);
|
||||
const openTransferDialog = useTransferDialog((store) => store.open);
|
||||
const {
|
||||
pubKey,
|
||||
pubKeys,
|
||||
selectPubKey,
|
||||
disconnect,
|
||||
isReadOnly,
|
||||
fetchPubKeys,
|
||||
} = useVegaWallet();
|
||||
const { pubKey, pubKeys, selectPubKey, disconnect, isReadOnly } =
|
||||
useVegaWallet();
|
||||
const isConnected = pubKey !== null;
|
||||
|
||||
const activeKey = useMemo(() => {
|
||||
@@ -171,12 +162,7 @@ export const VegaWalletConnectButton = () => {
|
||||
trigger={
|
||||
<DropdownMenuTrigger
|
||||
data-testid="manage-vega-wallet"
|
||||
onClick={() => {
|
||||
if (fetchPubKeys) {
|
||||
fetchPubKeys();
|
||||
}
|
||||
setDropdownOpen(!dropdownOpen);
|
||||
}}
|
||||
onClick={() => setDropdownOpen((curr) => !curr)}
|
||||
>
|
||||
{activeKey && (
|
||||
<span className="uppercase">{activeKey.name}</span>
|
||||
|
||||
@@ -19,21 +19,3 @@ export const useMarketClickHandler = (replace = false) => {
|
||||
[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]
|
||||
);
|
||||
};
|
||||
|
||||
@@ -33,8 +33,6 @@ import { ViewingBanner } from '../components/viewing-banner';
|
||||
import { Banner } from '../components/banner';
|
||||
import { AppLoader, DynamicLoader } from '../components/app-loader';
|
||||
import { Navbar } from '../components/navbar';
|
||||
import { useDataProvider } from '@vegaprotocol/react-helpers';
|
||||
import { activeOrdersProvider } from '@vegaprotocol/orders';
|
||||
|
||||
const DEFAULT_TITLE = t('Welcome to Vega trading!');
|
||||
|
||||
@@ -97,7 +95,6 @@ function AppBody({ Component }: AppProps) {
|
||||
<ToastsManager />
|
||||
<InitializeHandlers />
|
||||
<MaybeConnectEagerly />
|
||||
<PartyData />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -130,18 +127,6 @@ function VegaTradingApp(props: AppProps) {
|
||||
|
||||
export default VegaTradingApp;
|
||||
|
||||
const PartyData = () => {
|
||||
const { pubKey } = useVegaWallet();
|
||||
const variables = { partyId: pubKey || '' };
|
||||
const skip = !pubKey;
|
||||
useDataProvider({
|
||||
dataProvider: activeOrdersProvider,
|
||||
variables,
|
||||
skip,
|
||||
});
|
||||
return null;
|
||||
};
|
||||
|
||||
const MaybeConnectEagerly = () => {
|
||||
useVegaEagerConnect(Connectors);
|
||||
useEthereumEagerConnect();
|
||||
|
||||
@@ -31,7 +31,7 @@ export enum Routes {
|
||||
MARKET = '/markets',
|
||||
MARKETS = '/markets/all',
|
||||
PORTFOLIO = '/portfolio',
|
||||
LIQUIDITY = '/liquidity',
|
||||
LIQUIDITY = 'liquidity/:marketId',
|
||||
}
|
||||
|
||||
type ConsoleLinks = { [r in Routes]: (...args: string[]) => string };
|
||||
@@ -41,10 +41,8 @@ export const Links: ConsoleLinks = {
|
||||
marketId ? trimEnd(`${Routes.MARKET}/${marketId}`, '/') : Routes.MARKET,
|
||||
[Routes.MARKETS]: () => Routes.MARKETS,
|
||||
[Routes.PORTFOLIO]: () => Routes.PORTFOLIO,
|
||||
[Routes.LIQUIDITY]: (marketId: string | null | undefined) =>
|
||||
marketId
|
||||
? trimEnd(`${Routes.LIQUIDITY}/${marketId}`, '/')
|
||||
: Routes.LIQUIDITY,
|
||||
[Routes.LIQUIDITY]: (marketId: string) =>
|
||||
Routes.LIQUIDITY.replace(':marketId', marketId),
|
||||
};
|
||||
|
||||
const routerConfig: RouteObject[] = [
|
||||
@@ -72,16 +70,6 @@ const routerConfig: RouteObject[] = [
|
||||
{
|
||||
path: Routes.LIQUIDITY,
|
||||
element: <LazyLiquidity />,
|
||||
children: [
|
||||
{
|
||||
index: true,
|
||||
element: <LazyLiquidity />,
|
||||
},
|
||||
{
|
||||
path: ':marketId',
|
||||
element: <LazyLiquidity />,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: Routes.PORTFOLIO,
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
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/
|
||||
@@ -10,7 +10,6 @@ export * from './lib/cells/price-flash-cell';
|
||||
export * from './lib/cells/vol-cell';
|
||||
export * from './lib/cells/centered-grid-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/set-filter';
|
||||
|
||||
@@ -1,52 +0,0 @@
|
||||
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', () => {
|
||||
it('should be properly rendered', async () => {
|
||||
const defaultValue = {
|
||||
const defaultRangeFilter = {
|
||||
start: '2023-02-14T13:53:01+01:00',
|
||||
end: '2023-02-21T13:53:01+01:00',
|
||||
};
|
||||
@@ -17,7 +17,7 @@ describe('DateRangeFilter', () => {
|
||||
render(
|
||||
<DateRangeFilter
|
||||
{...(commonProps as unknown as DateRangeFilterProps)}
|
||||
defaultValue={defaultValue}
|
||||
defaultRangeFilter={defaultRangeFilter}
|
||||
/>
|
||||
);
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { ChangeEvent } from 'react';
|
||||
import { useEffect, useMemo, useRef } from 'react';
|
||||
import { useEffect, useMemo } from 'react';
|
||||
import type * as Schema from '@vegaprotocol/types';
|
||||
import { forwardRef, useImperativeHandle, useState } from 'react';
|
||||
import type { IDoesFilterPassParams, IFilterParams } from 'ag-grid-community';
|
||||
@@ -17,9 +17,9 @@ import { formatForInput } from '@vegaprotocol/utils';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { InputError } from '@vegaprotocol/ui-toolkit';
|
||||
|
||||
const defaultValue: Schema.DateRange = {};
|
||||
const defaultFilterValue: Schema.DateRange = {};
|
||||
export interface DateRangeFilterProps extends IFilterParams {
|
||||
defaultValue?: Schema.DateRange;
|
||||
defaultRangeFilter?: Schema.DateRange;
|
||||
maxSubDays?: number;
|
||||
maxNextDays?: number;
|
||||
maxDaysRange?: number;
|
||||
@@ -27,9 +27,8 @@ export interface DateRangeFilterProps extends IFilterParams {
|
||||
|
||||
export const DateRangeFilter = forwardRef(
|
||||
(props: DateRangeFilterProps, ref) => {
|
||||
const defaultDates = props?.defaultValue || defaultValue;
|
||||
const defaultDates = props?.defaultRangeFilter || defaultFilterValue;
|
||||
const [value, setValue] = useState<Schema.DateRange>(defaultDates);
|
||||
const valueRef = useRef<Schema.DateRange>(value);
|
||||
const [error, setError] = useState<string>('');
|
||||
const [minStartDate, maxStartDate, minEndDate, maxEndDate] = useMemo(() => {
|
||||
const minStartDate =
|
||||
@@ -94,7 +93,7 @@ export const DateRangeFilter = forwardRef(
|
||||
},
|
||||
|
||||
isFilterActive() {
|
||||
return valueRef.current.start || valueRef.current.end;
|
||||
return value.start || value.end;
|
||||
},
|
||||
|
||||
getModel() {
|
||||
@@ -102,13 +101,13 @@ export const DateRangeFilter = forwardRef(
|
||||
return null;
|
||||
}
|
||||
|
||||
return { value: valueRef.current };
|
||||
return { value };
|
||||
},
|
||||
|
||||
setModel(model?: { value: Schema.DateRange } | null) {
|
||||
valueRef.current =
|
||||
model?.value || props?.defaultValue || defaultValue;
|
||||
setValue(valueRef.current);
|
||||
setValue(
|
||||
model?.value || props?.defaultRangeFilter || defaultFilterValue
|
||||
);
|
||||
},
|
||||
};
|
||||
});
|
||||
@@ -186,8 +185,10 @@ export const DateRangeFilter = forwardRef(
|
||||
update = { ...update, end: checkForEndDate(endDate, startDate) };
|
||||
|
||||
if (validate(name, date, update)) {
|
||||
valueRef.current = { ...valueRef.current, ...update };
|
||||
setValue(valueRef.current);
|
||||
setValue((curr) => ({
|
||||
...curr,
|
||||
...update,
|
||||
}));
|
||||
}
|
||||
};
|
||||
useEffect(() => {
|
||||
@@ -240,8 +241,7 @@ export const DateRangeFilter = forwardRef(
|
||||
className="ag-standard-button ag-filter-apply-panel-button"
|
||||
onClick={() => {
|
||||
setError('');
|
||||
valueRef.current = defaultDates;
|
||||
setValue(valueRef.current);
|
||||
setValue(defaultDates);
|
||||
}}
|
||||
>
|
||||
{t('Reset')}
|
||||
|
||||
@@ -1,17 +1,10 @@
|
||||
import type { ChangeEvent } from 'react';
|
||||
import {
|
||||
forwardRef,
|
||||
useEffect,
|
||||
useImperativeHandle,
|
||||
useState,
|
||||
useRef,
|
||||
} from 'react';
|
||||
import { forwardRef, useEffect, useImperativeHandle, useState } from 'react';
|
||||
import type { IDoesFilterPassParams, IFilterParams } from 'ag-grid-community';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
|
||||
export const SetFilter = forwardRef((props: IFilterParams, ref) => {
|
||||
const [value, setValue] = useState<string[]>([]);
|
||||
const valueRef = useRef(value);
|
||||
|
||||
// expose AG Grid Filter Lifecycle callbacks
|
||||
useImperativeHandle(ref, () => {
|
||||
@@ -35,28 +28,29 @@ export const SetFilter = forwardRef((props: IFilterParams, ref) => {
|
||||
},
|
||||
|
||||
isFilterActive() {
|
||||
return valueRef.current.length !== 0;
|
||||
return value.length !== 0;
|
||||
},
|
||||
|
||||
getModel() {
|
||||
if (!this.isFilterActive()) {
|
||||
return null;
|
||||
}
|
||||
return { value: valueRef.current };
|
||||
|
||||
return { value };
|
||||
},
|
||||
|
||||
setModel(model?: { value: string[] } | null) {
|
||||
valueRef.current = !model ? [] : model.value;
|
||||
setValue(valueRef.current);
|
||||
setValue(!model ? [] : model.value);
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
const onChange = (event: ChangeEvent<HTMLInputElement>) => {
|
||||
valueRef.current = event.target.checked
|
||||
? [...value, event.target.value]
|
||||
: value.filter((v) => v !== event.target.value);
|
||||
setValue(valueRef.current);
|
||||
setValue(
|
||||
event.target.checked
|
||||
? [...value, event.target.value]
|
||||
: value.filter((v) => v !== event.target.value)
|
||||
);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
@@ -83,7 +77,7 @@ export const SetFilter = forwardRef((props: IFilterParams, ref) => {
|
||||
<button
|
||||
type="button"
|
||||
className="ag-standard-button ag-filter-apply-panel-button"
|
||||
onClick={() => setValue((valueRef.current = []))}
|
||||
onClick={() => setValue([])}
|
||||
>
|
||||
{t('Reset')}
|
||||
</button>
|
||||
|
||||
@@ -15,8 +15,8 @@ export const useInitialMargin = (
|
||||
marketId: OrderSubmissionBody['orderSubmission']['marketId'],
|
||||
order?: OrderSubmissionBody['orderSubmission']
|
||||
) => {
|
||||
const { pubKey } = useVegaWallet();
|
||||
const commonVariables = { marketId, partyId: pubKey || '' };
|
||||
const { pubKey: partyId } = useVegaWallet();
|
||||
const commonVariables = { marketId, partyId: partyId || '' };
|
||||
const { data: marketData } = useDataProvider({
|
||||
dataProvider: marketDataProvider,
|
||||
variables: { marketId },
|
||||
@@ -24,7 +24,7 @@ export const useInitialMargin = (
|
||||
const { data: activeVolumeAndMargin } = useDataProvider({
|
||||
dataProvider: volumeAndMarginProvider,
|
||||
variables: commonVariables,
|
||||
skip: !pubKey,
|
||||
skip: !partyId,
|
||||
});
|
||||
const { data: marketInfo } = useDataProvider({
|
||||
dataProvider: marketInfoProvider,
|
||||
|
||||
@@ -38,10 +38,10 @@ export const TransferTooltipCellComponent = ({
|
||||
);
|
||||
};
|
||||
|
||||
const defaultValue = { start: formatRFC3339(subDays(Date.now(), 7)) };
|
||||
const defaultRangeFilter = { start: formatRFC3339(subDays(Date.now(), 7)) };
|
||||
const dateRangeFilterParams = {
|
||||
maxNextDays: 0,
|
||||
defaultValue,
|
||||
defaultRangeFilter,
|
||||
};
|
||||
type LedgerEntryProps = TypedDataAgGrid<LedgerEntry>;
|
||||
|
||||
|
||||
@@ -20,7 +20,6 @@ import type {
|
||||
OrdersQueryVariables,
|
||||
} from './__generated__/Orders';
|
||||
import { OrdersDocument, OrdersUpdateDocument } from './__generated__/Orders';
|
||||
import type { ApolloClient } from '@apollo/client';
|
||||
|
||||
export type Order = Omit<OrderFieldsFragment, 'market'> & {
|
||||
market?: Market;
|
||||
@@ -82,31 +81,8 @@ const getData = (
|
||||
): Edge<OrderFieldsFragment>[] =>
|
||||
responseData?.party?.ordersConnection?.edges || [];
|
||||
|
||||
const getDelta = (
|
||||
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 getDelta = (subscriptionData: OrdersUpdateSubscription) =>
|
||||
subscriptionData.orders || [];
|
||||
|
||||
const getPageInfo = (responseData: OrdersQuery): PageInfo | null =>
|
||||
responseData.party?.ordersConnection?.pageInfo || null;
|
||||
@@ -174,7 +150,7 @@ export const update = (
|
||||
});
|
||||
};
|
||||
|
||||
const ordersProvider = makeDataProvider<
|
||||
export const ordersProvider = makeDataProvider<
|
||||
OrdersQuery,
|
||||
ReturnType<typeof getData>,
|
||||
OrdersUpdateSubscription,
|
||||
@@ -189,36 +165,11 @@ const ordersProvider = makeDataProvider<
|
||||
pagination: {
|
||||
getPageInfo,
|
||||
append,
|
||||
first: 1000,
|
||||
first: 100,
|
||||
},
|
||||
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<
|
||||
(OrderEdge | null)[],
|
||||
Order[],
|
||||
@@ -242,20 +193,63 @@ export const ordersWithMarketProvider = makeDerivedDataProvider<
|
||||
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<
|
||||
boolean,
|
||||
never,
|
||||
{ partyId: string; marketId?: string }
|
||||
>([activeOrdersProvider], (parts) => !!parts[0].length);
|
||||
|
||||
export const hasAmendableOrderProvider = makeDerivedDataProvider<
|
||||
boolean,
|
||||
never,
|
||||
{ partyId: string; marketId?: string }
|
||||
>([activeOrdersProvider], (parts) => {
|
||||
const activeOrders = parts[0] as ReturnType<typeof getData>;
|
||||
const hasAmendableOrder = activeOrders.some(
|
||||
(edge) => !(edge.node.liquidityProvision || edge.node.peggedOrder)
|
||||
);
|
||||
return hasAmendableOrder;
|
||||
});
|
||||
>(
|
||||
[
|
||||
(callback, client, { partyId, marketId }) =>
|
||||
hasActiveOrderProviderInternal(callback, client, {
|
||||
marketIds: marketId ? [marketId] : undefined,
|
||||
filter: {
|
||||
status: [OrderStatus.STATUS_ACTIVE],
|
||||
excludeLiquidity: true,
|
||||
},
|
||||
pagination: {
|
||||
first: 1,
|
||||
},
|
||||
partyId,
|
||||
} as OrdersQueryVariables),
|
||||
],
|
||||
(parts) => parts[0]
|
||||
);
|
||||
|
||||
@@ -6,12 +6,10 @@ import { OrderListManager } from './order-list-manager';
|
||||
export const OrderListContainer = ({
|
||||
marketId,
|
||||
onMarketClick,
|
||||
onOrderTypeClick,
|
||||
enforceBottomPlaceholder,
|
||||
}: {
|
||||
marketId?: string;
|
||||
onMarketClick?: (marketId: string, metaKey?: boolean) => void;
|
||||
onOrderTypeClick?: (marketId: string, metaKey?: boolean) => void;
|
||||
enforceBottomPlaceholder?: boolean;
|
||||
}) => {
|
||||
const { pubKey, isReadOnly } = useVegaWallet();
|
||||
@@ -25,7 +23,6 @@ export const OrderListContainer = ({
|
||||
partyId={pubKey}
|
||||
marketId={marketId}
|
||||
onMarketClick={onMarketClick}
|
||||
onOrderTypeClick={onOrderTypeClick}
|
||||
isReadOnly={isReadOnly}
|
||||
enforceBottomPlaceholder={enforceBottomPlaceholder}
|
||||
/>
|
||||
|
||||
@@ -8,7 +8,7 @@ import type { GridReadyEvent } from 'ag-grid-community';
|
||||
|
||||
import { OrderListTable } from '../order-list/order-list';
|
||||
import { useOrderListData } from './use-order-list-data';
|
||||
import { useHasAmendableOrder } from '../../order-hooks/use-has-amendable-order';
|
||||
import { useHasActiveOrder } from '../../order-hooks/use-has-active-order';
|
||||
import type { Filter, Sort } from './use-order-list-data';
|
||||
import { useBottomPlaceholder } from '@vegaprotocol/react-helpers';
|
||||
import { OrderStatus } from '@vegaprotocol/types';
|
||||
@@ -16,7 +16,6 @@ import {
|
||||
normalizeOrderAmendment,
|
||||
useVegaTransactionStore,
|
||||
} from '@vegaprotocol/wallet';
|
||||
import isEqual from 'lodash/isEqual';
|
||||
import type { OrderTxUpdateFieldsFragment } from '@vegaprotocol/wallet';
|
||||
import { OrderEditDialog } from '../order-list/order-edit-dialog';
|
||||
import type { Order, OrderEdge } from '../order-data-provider';
|
||||
@@ -25,23 +24,31 @@ export interface OrderListManagerProps {
|
||||
partyId: string;
|
||||
marketId?: string;
|
||||
onMarketClick?: (marketId: string, metaKey?: boolean) => void;
|
||||
onOrderTypeClick?: (marketId: string, metaKey?: boolean) => void;
|
||||
isReadOnly: boolean;
|
||||
enforceBottomPlaceholder?: boolean;
|
||||
}
|
||||
|
||||
const CancelAllOrdersButton = ({ onClick }: { onClick: () => void }) => (
|
||||
<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
|
||||
variant="primary"
|
||||
size="sm"
|
||||
onClick={onClick}
|
||||
data-testid="cancelAll"
|
||||
>
|
||||
{t('Cancel all')}
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
const CancelAllOrdersButton = ({
|
||||
onClick,
|
||||
marketId,
|
||||
}: {
|
||||
onClick: (marketId?: string) => void;
|
||||
marketId?: string;
|
||||
}) => {
|
||||
const hasActiveOrder = useHasActiveOrder(marketId);
|
||||
return hasActiveOrder ? (
|
||||
<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
|
||||
variant="primary"
|
||||
size="sm"
|
||||
onClick={() => onClick(marketId)}
|
||||
data-testid="cancelAll"
|
||||
>
|
||||
{t('Cancel all')}
|
||||
</Button>
|
||||
</div>
|
||||
) : null;
|
||||
};
|
||||
|
||||
const initialFilter: Filter = {
|
||||
status: {
|
||||
@@ -53,7 +60,6 @@ export const OrderListManager = ({
|
||||
partyId,
|
||||
marketId,
|
||||
onMarketClick,
|
||||
onOrderTypeClick,
|
||||
isReadOnly,
|
||||
enforceBottomPlaceholder,
|
||||
}: OrderListManagerProps) => {
|
||||
@@ -62,10 +68,9 @@ export const OrderListManager = ({
|
||||
const scrolledToTop = useRef(false);
|
||||
const [sort, setSort] = useState<Sort[] | undefined>();
|
||||
const [filter, setFilter] = useState<Filter | undefined>(initialFilter);
|
||||
const filterRef = useRef(initialFilter);
|
||||
const [editOrder, setEditOrder] = useState<Order | null>(null);
|
||||
const create = useVegaTransactionStore((state) => state.create);
|
||||
const hasAmendableOrder = useHasAmendableOrder(marketId);
|
||||
const hasActiveOrder = useHasActiveOrder(marketId);
|
||||
|
||||
const { data, error, loading, reload } = useOrderListData({
|
||||
partyId,
|
||||
@@ -81,16 +86,12 @@ export const OrderListManager = ({
|
||||
...bottomPlaceholderProps
|
||||
} = useBottomPlaceholder<Order>({
|
||||
gridRef,
|
||||
disabled: !enforceBottomPlaceholder && !isReadOnly && !hasAmendableOrder,
|
||||
disabled: !enforceBottomPlaceholder && !isReadOnly && !hasActiveOrder,
|
||||
});
|
||||
|
||||
const onFilterChanged = useCallback(
|
||||
(event: FilterChangedEvent) => {
|
||||
const updatedFilter = event.api.getFilterModel();
|
||||
if (isEqual(updatedFilter, filterRef.current)) {
|
||||
return;
|
||||
}
|
||||
filterRef.current = updatedFilter;
|
||||
if (Object.keys(updatedFilter).length) {
|
||||
setFilter(updatedFilter);
|
||||
} else {
|
||||
@@ -141,13 +142,16 @@ export const OrderListManager = ({
|
||||
setDataCount(gridRef.current?.api?.getModel().getRowCount() ?? 0);
|
||||
}, [data]);
|
||||
|
||||
const cancelAll = useCallback(() => {
|
||||
create({
|
||||
orderCancellation: {
|
||||
marketId,
|
||||
},
|
||||
});
|
||||
}, [create, marketId]);
|
||||
const cancelAll = useCallback(
|
||||
(marketId?: string) => {
|
||||
create({
|
||||
orderCancellation: {
|
||||
marketId,
|
||||
},
|
||||
});
|
||||
},
|
||||
[create]
|
||||
);
|
||||
const extractedData =
|
||||
data && !loading
|
||||
? data
|
||||
@@ -167,7 +171,6 @@ export const OrderListManager = ({
|
||||
cancel={cancel}
|
||||
setEditOrder={setEditOrder}
|
||||
onMarketClick={onMarketClick}
|
||||
onOrderTypeClick={onOrderTypeClick}
|
||||
isReadOnly={isReadOnly}
|
||||
blockLoadDebounceMillis={100}
|
||||
suppressLoadingOverlay
|
||||
@@ -185,8 +188,8 @@ export const OrderListManager = ({
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{!isReadOnly && hasAmendableOrder && (
|
||||
<CancelAllOrdersButton onClick={cancelAll} />
|
||||
{!isReadOnly && (
|
||||
<CancelAllOrdersButton onClick={cancelAll} marketId={marketId} />
|
||||
)}
|
||||
{editOrder && (
|
||||
<OrderEditDialog
|
||||
|
||||
@@ -71,27 +71,17 @@ export const useOrderListData = ({
|
||||
// define variable as const to get type safety, using generic with useMemo resulted in lost type safety
|
||||
const allVars: OrdersQueryVariables & OrdersUpdateSubscriptionVariables = {
|
||||
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;
|
||||
}, [partyId, filter]);
|
||||
|
||||
|
||||
@@ -16,7 +16,6 @@ import {
|
||||
negativeClassNames,
|
||||
positiveClassNames,
|
||||
MarketNameCell,
|
||||
OrderTypeCell,
|
||||
} from '@vegaprotocol/datagrid';
|
||||
import type {
|
||||
TypedDataAgGrid,
|
||||
@@ -32,16 +31,12 @@ export type OrderListTableProps = OrderListProps & {
|
||||
cancel: (order: Order) => void;
|
||||
setEditOrder: (order: Order) => void;
|
||||
onMarketClick?: (marketId: string, metaKey?: boolean) => void;
|
||||
onOrderTypeClick?: (marketId: string, metaKey?: boolean) => void;
|
||||
isReadOnly: boolean;
|
||||
};
|
||||
|
||||
export const OrderListTable = memo(
|
||||
forwardRef<AgGridReact, OrderListTableProps>(
|
||||
(
|
||||
{ cancel, setEditOrder, onMarketClick, onOrderTypeClick, ...props },
|
||||
ref
|
||||
) => {
|
||||
({ cancel, setEditOrder, onMarketClick, ...props }, ref) => {
|
||||
return (
|
||||
<AgGrid
|
||||
ref={ref}
|
||||
@@ -56,7 +51,7 @@ export const OrderListTable = memo(
|
||||
height: '100%',
|
||||
}}
|
||||
getRowId={({ data }) => data.id}
|
||||
components={{ MarketNameCell, OrderTypeCell }}
|
||||
components={{ MarketNameCell }}
|
||||
{...props}
|
||||
>
|
||||
<AgGridColumn
|
||||
@@ -108,9 +103,17 @@ export const OrderListTable = memo(
|
||||
filterParams={{
|
||||
set: Schema.OrderTypeMapping,
|
||||
}}
|
||||
cellRenderer="OrderTypeCell"
|
||||
cellRendererParams={{
|
||||
onClick: onOrderTypeClick,
|
||||
valueFormatter={({
|
||||
data: order,
|
||||
value,
|
||||
}: 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}
|
||||
/>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
export * from './__generated__/OrdersSubscription';
|
||||
export * from './use-has-amendable-order';
|
||||
export * from './use-has-active-order';
|
||||
export * from './use-order-update';
|
||||
export * from './use-pending-orders-volume';
|
||||
export * from './use-order-store';
|
||||
|
||||
+6
-6
@@ -1,17 +1,17 @@
|
||||
import { useCallback, useState } from 'react';
|
||||
import { hasAmendableOrderProvider } from '../components/order-data-provider';
|
||||
import { hasActiveOrderProvider } from '../components/order-data-provider/';
|
||||
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||
import { useDataProvider } from '@vegaprotocol/react-helpers';
|
||||
|
||||
export const useHasAmendableOrder = (marketId?: string) => {
|
||||
export const useHasActiveOrder = (marketId?: string) => {
|
||||
const { pubKey } = useVegaWallet();
|
||||
const [hasAmendableOrder, setHasAmendableOrder] = useState(false);
|
||||
const [hasActiveOrder, setHasActiveOrder] = useState(false);
|
||||
const update = useCallback(({ data }: { data: boolean | null }) => {
|
||||
setHasAmendableOrder(Boolean(data));
|
||||
setHasActiveOrder(Boolean(data));
|
||||
return true;
|
||||
}, []);
|
||||
useDataProvider({
|
||||
dataProvider: hasAmendableOrderProvider,
|
||||
dataProvider: hasActiveOrderProvider,
|
||||
update,
|
||||
variables: {
|
||||
partyId: pubKey || '',
|
||||
@@ -20,5 +20,5 @@ export const useHasAmendableOrder = (marketId?: string) => {
|
||||
skip: !pubKey,
|
||||
});
|
||||
|
||||
return hasAmendableOrder;
|
||||
return hasActiveOrder;
|
||||
};
|
||||
@@ -0,0 +1,74 @@
|
||||
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;
|
||||
};
|
||||
@@ -30,12 +30,12 @@ import {
|
||||
import { marginsDataProvider } from './margin-data-provider';
|
||||
import { calculateMargins } from './margin-calculator';
|
||||
import type { Edge } from '@vegaprotocol/utils';
|
||||
import { Side } from '@vegaprotocol/types';
|
||||
import { OrderStatus, Side } from '@vegaprotocol/types';
|
||||
import { marketInfoProvider } from '@vegaprotocol/market-info';
|
||||
import type { MarketInfoQuery } from '@vegaprotocol/market-info';
|
||||
import { marketDataProvider } from '@vegaprotocol/market-list';
|
||||
import type { MarketData } from '@vegaprotocol/market-list';
|
||||
import { activeOrdersProvider } from '@vegaprotocol/orders';
|
||||
import { ordersProvider } from '@vegaprotocol/orders';
|
||||
import type { OrderFieldsFragment } from '@vegaprotocol/orders';
|
||||
import type { PositionStatus } from '@vegaprotocol/types';
|
||||
|
||||
@@ -350,9 +350,12 @@ export const volumeAndMarginProvider = makeDerivedDataProvider<
|
||||
>(
|
||||
[
|
||||
(callback, client, { partyId, marketId }) =>
|
||||
activeOrdersProvider(callback, client, {
|
||||
ordersProvider(callback, client, {
|
||||
partyId,
|
||||
marketId,
|
||||
marketIds: [marketId],
|
||||
filter: {
|
||||
status: [OrderStatus.STATUS_ACTIVE, OrderStatus.STATUS_PARKED],
|
||||
},
|
||||
}),
|
||||
(callback, client, variables) =>
|
||||
marketDataProvider(callback, client, { marketId: variables.marketId }),
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { useState, useEffect, useRef, useMemo, useCallback } from 'react';
|
||||
import { useState, useEffect, useRef, useCallback } from 'react';
|
||||
import throttle from 'lodash/throttle';
|
||||
import isEqualWith from 'lodash/isEqualWith';
|
||||
import isEqual from 'lodash/isEqual';
|
||||
import { useApolloClient } from '@apollo/client';
|
||||
import { usePrevious } from './use-previous';
|
||||
import type { OperationVariables } from '@apollo/client';
|
||||
import type { Subscribe, Load, UpdateCallback } from '@vegaprotocol/utils';
|
||||
import { variablesIsEqualCustomizer } from '@vegaprotocol/utils';
|
||||
|
||||
export interface useDataProviderParams<
|
||||
Data,
|
||||
@@ -62,19 +62,13 @@ export const useDataProvider = <
|
||||
const flushRef = useRef<(() => void) | undefined>(undefined);
|
||||
const reloadRef = useRef<((force?: boolean) => void) | undefined>(undefined);
|
||||
const loadRef = useRef<Load<Data> | undefined>(undefined);
|
||||
const variablesRef = useRef<Variables>(props.variables);
|
||||
const variables = useMemo(() => {
|
||||
if (
|
||||
!isEqualWith(
|
||||
variablesRef.current,
|
||||
props.variables,
|
||||
variablesIsEqualCustomizer
|
||||
)
|
||||
) {
|
||||
variablesRef.current = props.variables;
|
||||
const prevVariables = usePrevious(props.variables);
|
||||
const [variables, setVariables] = useState(props.variables);
|
||||
useEffect(() => {
|
||||
if (!isEqual(prevVariables, props.variables)) {
|
||||
setVariables(props.variables);
|
||||
}
|
||||
return variablesRef.current;
|
||||
}, [props.variables]);
|
||||
}, [props.variables, prevVariables]);
|
||||
const flush = useCallback(() => {
|
||||
if (flushRef.current) {
|
||||
flushRef.current();
|
||||
|
||||
@@ -25,7 +25,6 @@ export * from './nav-dropdown';
|
||||
export * from './nav';
|
||||
export * from './navigation';
|
||||
export * from './notification';
|
||||
export * from './pagination';
|
||||
export * from './popover';
|
||||
export * from './progress-bar';
|
||||
export * from './radio-group';
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
export * from './pagination';
|
||||
@@ -1,28 +0,0 @@
|
||||
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({});
|
||||
@@ -1,73 +0,0 @@
|
||||
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 Delta = Data;
|
||||
type Variables = { var: string; filter?: string[] };
|
||||
type Variables = { var: string };
|
||||
|
||||
const update = jest.fn<
|
||||
ReturnType<Update<Data, Delta, Variables>>,
|
||||
@@ -231,14 +231,10 @@ describe('data provider', () => {
|
||||
clientSubscribeSubscribe.mockClear();
|
||||
});
|
||||
it('memoize instance and unsubscribe if no subscribers', () => {
|
||||
const subscription1 = subscribe(jest.fn(), client, {
|
||||
...variables,
|
||||
filter: ['1', '2'],
|
||||
});
|
||||
const subscription2 = subscribe(jest.fn(), client, {
|
||||
...variables,
|
||||
filter: ['2', '1'],
|
||||
});
|
||||
const subscription1 = subscribe(jest.fn(), client, variables);
|
||||
const subscription2 = subscribe(jest.fn(), client, { ...variables });
|
||||
// const subscription1 = subscribe(jest.fn(), client);
|
||||
// const subscription2 = subscribe(jest.fn(), client);
|
||||
expect(clientSubscribeSubscribe.mock.calls.length).toEqual(1);
|
||||
subscription1.unsubscribe();
|
||||
expect(clientSubscribeUnsubscribe.mock.calls.length).toEqual(0);
|
||||
|
||||
@@ -10,7 +10,7 @@ import type {
|
||||
} from '@apollo/client';
|
||||
import type { GraphQLErrors } from '@apollo/client/errors';
|
||||
import type { Subscription } from 'zen-observable-ts';
|
||||
import isEqualWith from 'lodash/isEqualWith';
|
||||
import isEqual from 'lodash/isEqual';
|
||||
import { isNotFoundGraphQLError } from './apollo-client';
|
||||
import type * as Schema from '@vegaprotocol/types';
|
||||
interface UpdateData<Data, Delta> {
|
||||
@@ -104,11 +104,7 @@ interface GetTotalCount<QueryData> {
|
||||
}
|
||||
|
||||
interface GetDelta<SubscriptionData, Delta, Variables> {
|
||||
(
|
||||
subscriptionData: SubscriptionData,
|
||||
variables: Variables,
|
||||
client: ApolloClient<object>
|
||||
): Delta;
|
||||
(subscriptionData: SubscriptionData, variables?: Variables): Delta;
|
||||
}
|
||||
|
||||
export type Node = { id: string };
|
||||
@@ -424,7 +420,7 @@ function makeDataProviderInternal<
|
||||
if (!subscriptionData || !getDelta || !update) {
|
||||
return;
|
||||
}
|
||||
const delta = getDelta(subscriptionData, variables, client);
|
||||
const delta = getDelta(subscriptionData, variables);
|
||||
if (loading) {
|
||||
updateQueue.push(delta);
|
||||
} else {
|
||||
@@ -516,26 +512,11 @@ 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
|
||||
*
|
||||
* @param fn
|
||||
* @returns subscribe function
|
||||
* @returns subscibe function
|
||||
*/
|
||||
const memoize = <
|
||||
Data,
|
||||
@@ -549,9 +530,7 @@ const memoize = <
|
||||
variables?: Variables;
|
||||
}[] = [];
|
||||
return (variables?: Variables) => {
|
||||
const cached = cache.find((c) =>
|
||||
isEqualWith(c.variables, variables, variablesIsEqualCustomizer)
|
||||
);
|
||||
const cached = cache.find((c) => isEqual(c.variables, variables));
|
||||
if (cached) {
|
||||
return cached.subscribe;
|
||||
}
|
||||
@@ -603,10 +582,8 @@ export function makeDataProvider<
|
||||
const getInstance = memoize<Data, Delta, Variables>(() =>
|
||||
makeDataProviderInternal(params)
|
||||
);
|
||||
return (callback, client, variables) => {
|
||||
const instance = getInstance(variables)(callback, client, variables);
|
||||
return instance;
|
||||
};
|
||||
return (callback, client, variables) =>
|
||||
getInstance(variables)(callback, client, variables);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -628,9 +605,7 @@ export type CombineDerivedData<
|
||||
> = (
|
||||
data: DerivedPart<Variables>['data'][],
|
||||
variables: Variables,
|
||||
prevData: Data | null,
|
||||
parts: DerivedPart<Variables>[],
|
||||
subscriptions?: ReturnType<DependencySubscribe<Variables>>[]
|
||||
prevData: Data | null
|
||||
) => Data | null;
|
||||
|
||||
export type CombineDerivedDelta<
|
||||
@@ -712,9 +687,7 @@ function makeDerivedDataProviderInternal<
|
||||
? combineData(
|
||||
parts.map((part) => part.data),
|
||||
variables,
|
||||
data,
|
||||
parts,
|
||||
subscriptions
|
||||
data
|
||||
)
|
||||
: data;
|
||||
if (
|
||||
|
||||
@@ -31,9 +31,6 @@ export interface VegaWalletContextShape {
|
||||
/** Transaction payload */
|
||||
transaction: Transaction
|
||||
) => Promise<TransactionResponse | null>;
|
||||
|
||||
/** Fetch public keys */
|
||||
fetchPubKeys?: () => Promise<PubKey[] | null>;
|
||||
}
|
||||
|
||||
export const VegaWalletContext = createContext<
|
||||
|
||||
@@ -50,7 +50,6 @@ describe('VegaWalletProvider', () => {
|
||||
connect: expect.any(Function),
|
||||
disconnect: expect.any(Function),
|
||||
sendTx: expect.any(Function),
|
||||
fetchPubKeys: expect.any(Function || undefined),
|
||||
});
|
||||
|
||||
// Connect
|
||||
@@ -78,47 +77,14 @@ describe('VegaWalletProvider', () => {
|
||||
expect(spyOnSend).toHaveBeenCalledWith(mockPubKeys[1].publicKey, {});
|
||||
});
|
||||
|
||||
it('should fetch new keypairs', async () => {
|
||||
const { result } = setup();
|
||||
|
||||
// Default state
|
||||
expect(result.current).toEqual({
|
||||
pubKey: null,
|
||||
pubKeys: null,
|
||||
isReadOnly: false,
|
||||
selectPubKey: expect.any(Function),
|
||||
connect: expect.any(Function),
|
||||
disconnect: expect.any(Function),
|
||||
sendTx: expect.any(Function),
|
||||
fetchPubKeys: expect.any(Function),
|
||||
});
|
||||
|
||||
// Connect
|
||||
await act(async () => {
|
||||
result.current.connect(restConnector);
|
||||
result.current.selectPubKey(mockPubKeys[0].publicKey);
|
||||
});
|
||||
expect(spyOnConnect).toHaveBeenCalled();
|
||||
expect(result.current.pubKeys).toHaveLength(mockPubKeys.length);
|
||||
expect(result.current.pubKey).toBe(mockPubKeys[0].publicKey);
|
||||
|
||||
// Fetch pub keys
|
||||
mockPubKeys.push({ publicKey: '333', name: 'public key 3' });
|
||||
await act(async () => {
|
||||
result.current.fetchPubKeys && result.current.fetchPubKeys();
|
||||
});
|
||||
expect(result.current.pubKeys).toHaveLength(mockPubKeys.length);
|
||||
});
|
||||
|
||||
it('persists selected pubkey and disconnects', async () => {
|
||||
const { result } = setup();
|
||||
expect(result.current.pubKey).toBe(null);
|
||||
|
||||
await act(async () => {
|
||||
result.current.connect(restConnector);
|
||||
result.current.selectPubKey(mockPubKeys[0].publicKey);
|
||||
});
|
||||
expect(result.current.pubKey).toBe(mockPubKeys[0].publicKey);
|
||||
expect(result.current.pubKey).toBe(mockPubKeys[1].publicKey);
|
||||
|
||||
// Disconnect
|
||||
await act(async () => {
|
||||
|
||||
@@ -21,7 +21,7 @@ export const VegaWalletProvider = ({ children }: VegaWalletProviderProps) => {
|
||||
const [pubKey, setPubKey] = useState<string | null>(null);
|
||||
const [isReadOnly, setIsReadOnly] = useState<boolean>(false);
|
||||
|
||||
// Array of public keys retrieved from the connector
|
||||
// Arary of pubkeys retrieved from the connector
|
||||
const [pubKeys, setPubKeys] = useState<PubKey[] | null>(null);
|
||||
|
||||
// Reference to the current connector instance
|
||||
@@ -32,28 +32,6 @@ export const VegaWalletProvider = ({ children }: VegaWalletProviderProps) => {
|
||||
LocalStorage.setItem(WALLET_KEY, pk);
|
||||
}, []);
|
||||
|
||||
const fetchPubKeys = useCallback(async () => {
|
||||
if (!connector.current) {
|
||||
throw new Error('No connector');
|
||||
}
|
||||
try {
|
||||
const keys = await connector.current.connect();
|
||||
|
||||
if (keys?.length) {
|
||||
setPubKeys(keys);
|
||||
setIsReadOnly(connector.current instanceof ViewConnector);
|
||||
return keys;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
} catch (err) {
|
||||
if (err instanceof WalletClientError) {
|
||||
throw err;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}, []);
|
||||
|
||||
const connect = useCallback(async (c: VegaConnector) => {
|
||||
connector.current = c;
|
||||
try {
|
||||
@@ -117,18 +95,8 @@ export const VegaWalletProvider = ({ children }: VegaWalletProviderProps) => {
|
||||
connect,
|
||||
disconnect,
|
||||
sendTx,
|
||||
fetchPubKeys,
|
||||
};
|
||||
}, [
|
||||
isReadOnly,
|
||||
pubKey,
|
||||
pubKeys,
|
||||
selectPubKey,
|
||||
connect,
|
||||
disconnect,
|
||||
sendTx,
|
||||
fetchPubKeys,
|
||||
]);
|
||||
}, [isReadOnly, pubKey, pubKeys, selectPubKey, connect, disconnect, sendTx]);
|
||||
|
||||
return (
|
||||
<VegaWalletContext.Provider value={contextValue}>
|
||||
|
||||
@@ -30,13 +30,13 @@ export const useJsonRpcConnect = (onConnect: () => void) => {
|
||||
// Check if wallet is configured for the same chain as the app
|
||||
setStatus(Status.GettingChainId);
|
||||
|
||||
// Do not throw in when cypress is running as trading app relies on
|
||||
// Dont throw in when cypress is running as trading app relies on
|
||||
// mocks which result in a mismatch between chainId for app and
|
||||
// chainId for wallet
|
||||
if (!('Cypress' in window)) {
|
||||
const chainIdResult = await connector.getChainId();
|
||||
if (chainIdResult.chainID !== appChainId) {
|
||||
// Throw wallet error for consistent error handling
|
||||
// Throw wallet error for consitent error handling
|
||||
throw ClientErrors.WRONG_NETWORK;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user