From eac26c196640f4617aed1681fc78721e62e75131 Mon Sep 17 00:00:00 2001 From: "m.ray" <16125548+MadalinaRaicu@users.noreply.github.com> Date: Tue, 28 Nov 2023 16:43:31 +0200 Subject: [PATCH 1/8] fix(trading): empty connect wallet dialog (#5356) --- .../src/lib/positions-manager.spec.tsx | 1 + libs/positions/src/lib/positions-manager.tsx | 1 + .../src/connect-dialog/use-chain-id.spec.ts | 72 +++++++++++++++++++ .../wallet/src/connect-dialog/use-chain-id.ts | 24 ++++++- 4 files changed, 96 insertions(+), 2 deletions(-) create mode 100644 libs/wallet/src/connect-dialog/use-chain-id.spec.ts diff --git a/libs/positions/src/lib/positions-manager.spec.tsx b/libs/positions/src/lib/positions-manager.spec.tsx index e78448725..07e5426e2 100644 --- a/libs/positions/src/lib/positions-manager.spec.tsx +++ b/libs/positions/src/lib/positions-manager.spec.tsx @@ -28,6 +28,7 @@ jest.mock('@vegaprotocol/data-provider', () => ({ describe('PositionsManager', () => { // TODO: temporarily disable close position + // eslint-disable-next-line jest/no-disabled-tests it.skip('should close position with max uint64', async () => { render(, { wrapper: MockedProvider, diff --git a/libs/positions/src/lib/positions-manager.tsx b/libs/positions/src/lib/positions-manager.tsx index 2056c68ed..a8b89dada 100644 --- a/libs/positions/src/lib/positions-manager.tsx +++ b/libs/positions/src/lib/positions-manager.tsx @@ -29,6 +29,7 @@ export const PositionsManager = ({ }: PositionsManagerProps) => { const { pubKeys, pubKey } = useVegaWallet(); const create = useVegaTransactionStore((store) => store.create); + // eslint-disable-next-line @typescript-eslint/no-unused-vars const onClose = useCallback( ({ marketId, openVolume }: { marketId: string; openVolume: string }) => create({ diff --git a/libs/wallet/src/connect-dialog/use-chain-id.spec.ts b/libs/wallet/src/connect-dialog/use-chain-id.spec.ts new file mode 100644 index 000000000..1e5263879 --- /dev/null +++ b/libs/wallet/src/connect-dialog/use-chain-id.spec.ts @@ -0,0 +1,72 @@ +import { renderHook, waitFor } from '@testing-library/react'; +import { useVegaWallet } from '../use-vega-wallet'; +import { useChainId } from './use-chain-id'; + +global.fetch = jest.fn(); +const mockFetch = global.fetch as jest.Mock; +mockFetch.mockImplementation((url: string) => { + return Promise.resolve({ ok: true }); +}); + +jest.mock('../use-vega-wallet', () => { + const original = jest.requireActual('../use-vega-wallet'); + return { + ...original, + useVegaWallet: jest.fn(), + }; +}); + +describe('useChainId', () => { + it('does not call fetch when statistics url could not be determined', async () => { + (useVegaWallet as jest.Mock).mockReturnValue({ + vegaUrl: '', + }); + renderHook(() => useChainId()); + await waitFor(() => { + expect(mockFetch).toHaveBeenCalledTimes(0); + }); + }); + + it('calls fetch with correct statistics url', async () => { + (useVegaWallet as jest.Mock).mockReturnValue({ + vegaUrl: 'http://localhost:1234/graphql', + }); + renderHook(() => useChainId()); + await waitFor(() => { + expect(mockFetch).toHaveBeenCalledWith( + 'http://localhost:1234/statistics' + ); + }); + }); + + it('does not return chain id when chain id is not present in response', async () => { + (useVegaWallet as jest.Mock).mockReturnValue({ + vegaUrl: 'http://localhost:1234/graphql', + }); + const { result } = renderHook(() => useChainId()); + await waitFor(() => { + expect(result.current).toBeUndefined(); + }); + }); + + it('returns chain id when chain id is present in response', async () => { + (useVegaWallet as jest.Mock).mockReturnValue({ + vegaUrl: 'http://localhost:1234/graphql', + }); + mockFetch.mockImplementation(() => { + return Promise.resolve({ + ok: true, + json: () => ({ + statistics: { + chainId: '1234', + }, + }), + }); + }); + const { result } = renderHook(() => useChainId()); + await waitFor(() => { + expect(result.current).not.toBeUndefined(); + expect(result.current).toEqual('1234'); + }); + }); +}); diff --git a/libs/wallet/src/connect-dialog/use-chain-id.ts b/libs/wallet/src/connect-dialog/use-chain-id.ts index d346bd2b9..559ed628d 100644 --- a/libs/wallet/src/connect-dialog/use-chain-id.ts +++ b/libs/wallet/src/connect-dialog/use-chain-id.ts @@ -3,17 +3,37 @@ import { useVegaWallet } from '../use-vega-wallet'; const cache: Record = {}; +/** + * Gets the statistics url for a given vega url. + * Example: + * https://graphql.example.com/graphql -> https://graphql.example.com/statistics + */ +const getNodeStatisticsUrl = (vegaUrl: string) => { + try { + const url = new URL(vegaUrl); + url.pathname = 'statistics'; + return url.toString(); + } catch (err) { + return undefined; + } +}; + export const useChainId = () => { const { vegaUrl } = useVegaWallet(); const [chainId, setChainId] = useState(cache[vegaUrl]); const [fetchAttempt, setFetchAttempt] = useState(1); + + const statisticsUrl = getNodeStatisticsUrl(vegaUrl); + useEffect(() => { + // abort when `/statistics` URL could not be determined + if (!statisticsUrl) return; let isCancelled = false; if (cache[vegaUrl]) { setChainId(cache[vegaUrl]); return; } - fetch(vegaUrl.replace('graphql', 'statistics')) + fetch(statisticsUrl) .then((response) => response.json()) .then((response) => { if (isCancelled) { @@ -32,6 +52,6 @@ export const useChainId = () => { return () => { isCancelled = true; }; - }, [fetchAttempt, vegaUrl]); + }, [fetchAttempt, statisticsUrl, vegaUrl]); return chainId; }; From 4e2b0d1b1d389e6a441bfaa92ee8083ed29f5ad0 Mon Sep 17 00:00:00 2001 From: "m.ray" <16125548+MadalinaRaicu@users.noreply.github.com> Date: Wed, 29 Nov 2023 17:24:37 +0200 Subject: [PATCH 2/8] fix(trading): fix ag-grid transparent filters (#5377) --- apps/trading/pages/styles.css | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/trading/pages/styles.css b/apps/trading/pages/styles.css index c1d09cceb..ffccd8453 100644 --- a/apps/trading/pages/styles.css +++ b/apps/trading/pages/styles.css @@ -170,7 +170,7 @@ html [data-theme='dark'] { .ag-theme-balham, .ag-theme-balham-dark { - --ag-grid-size: 2px; /* Used for compactness */ + --ag-grid-size: 3px; /* Used for compactness */ --ag-row-height: 36px; --ag-header-height: 28px; } @@ -184,7 +184,7 @@ html [data-theme='dark'] { /* Light variables */ .ag-theme-balham { - --ag-background-color: transparent; + --ag-background-color: theme(colors.vega.clight.900); --ag-border-color: theme(colors.vega.clight.600); --ag-header-background-color: theme(colors.vega.clight.700); --ag-odd-row-background-color: transparent; @@ -196,7 +196,7 @@ html [data-theme='dark'] { /* Dark variables */ .ag-theme-balham-dark { - --ag-background-color: transparent; + --ag-background-color: theme(colors.vega.cdark.900); --ag-border-color: theme(colors.vega.cdark.600); --ag-header-background-color: theme(colors.vega.cdark.700); --ag-odd-row-background-color: transparent; From 52ab0562b05e75b91a266a9732b44786bc1c7665 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20G=C5=82ownia?= Date: Wed, 29 Nov 2023 16:27:00 +0100 Subject: [PATCH 3/8] feat(trading): refactor ledger export form validation (#5379) --- .../ledger-container/ledger-container.tsx | 15 +- .../src/lib/ledger-export-form.spec.tsx | 14 - libs/ledger/src/lib/ledger-export-form.tsx | 329 ++++++++++-------- 3 files changed, 198 insertions(+), 160 deletions(-) diff --git a/apps/trading/components/ledger-container/ledger-container.tsx b/apps/trading/components/ledger-container/ledger-container.tsx index 36bddc043..5d9f62fde 100644 --- a/apps/trading/components/ledger-container/ledger-container.tsx +++ b/apps/trading/components/ledger-container/ledger-container.tsx @@ -15,15 +15,12 @@ export const LedgerContainer = () => { }); const assets = (data?.party?.accountsConnection?.edges ?? []) - .map( - (item) => item?.node?.asset ?? ({} as PartyAssetFieldsFragment) - ) - .reduce((aggr, item) => { - if ('id' in item && 'symbol' in item) { - aggr[item.id as string] = item.symbol as string; - } - return aggr; - }, {} as Record); + .map((item) => item?.node?.asset) + .filter((asset): asset is PartyAssetFieldsFragment => !!asset?.id) + .reduce( + (aggr, item) => Object.assign(aggr, { [item.id]: item.symbol }), + {} as Record + ); if (!pubKey) { return ( diff --git a/libs/ledger/src/lib/ledger-export-form.spec.tsx b/libs/ledger/src/lib/ledger-export-form.spec.tsx index 755b3eade..dedccc6db 100644 --- a/libs/ledger/src/lib/ledger-export-form.spec.tsx +++ b/libs/ledger/src/lib/ledger-export-form.spec.tsx @@ -278,18 +278,4 @@ describe('createDownloadUrl', () => { )}&dateRange.endTimestamp=${toNanoSeconds(dateTo)}` ); }); - - it('should throw if invalid args are provided', () => { - // invalid url - expect(() => { - // @ts-ignore override z.infer type - createDownloadUrl({ ...args, protohost: 'foo' }); - }).toThrow(); - - // invalid partyId - expect(() => { - // @ts-ignore override z.infer type - createDownloadUrl({ ...args, partyId: 'z'.repeat(64) }); - }).toThrow(); - }); }); diff --git a/libs/ledger/src/lib/ledger-export-form.tsx b/libs/ledger/src/lib/ledger-export-form.tsx index 135a63f38..cb585370d 100644 --- a/libs/ledger/src/lib/ledger-export-form.tsx +++ b/libs/ledger/src/lib/ledger-export-form.tsx @@ -1,18 +1,18 @@ -import { useRef, useState } from 'react'; -import { format, subDays } from 'date-fns'; +import { useRef, useCallback } from 'react'; +import { subDays } from 'date-fns'; +import { Controller, useForm } from 'react-hook-form'; import { + InputError, Intent, - Loader, TradingButton, TradingFormGroup, TradingInput, TradingSelect, } from '@vegaprotocol/ui-toolkit'; -import { z } from 'zod'; import { formatForInput, + getDateTimeFormat, toNanoSeconds, - VEGA_ID_REGEX, } from '@vegaprotocol/utils'; import { t } from '@vegaprotocol/i18n'; import { localLoggerFactory } from '@vegaprotocol/logger'; @@ -35,18 +35,18 @@ const getProtoHost = (vegaurl: string) => { return `${loc.protocol}//${loc.host}`; }; -const downloadSchema = z.object({ - protohost: z.string().url().nonempty(), - partyId: z.string().regex(VEGA_ID_REGEX).nonempty(), - assetId: z.string().regex(VEGA_ID_REGEX).nonempty(), - dateFrom: z.string().nonempty(), - dateTo: z.string().optional(), -}); - -export const createDownloadUrl = (args: z.infer) => { - // check args from form inputs - downloadSchema.parse(args); +type LedgerFormValues = { + assetId: string; + dateFrom: string; + dateTo?: string; +}; +export const createDownloadUrl = ( + args: LedgerFormValues & { + partyId: string; + protohost: string; + } +) => { const params = new URLSearchParams(); params.append('partyId', args.partyId); params.append('assetId', args.assetId); @@ -71,114 +71,101 @@ interface Props { export const LedgerExportForm = ({ partyId, vegaUrl, assets }: Props) => { const now = useRef(new Date()); - const [dateFrom, setDateFrom] = useState(() => { - return formatForInput(subDays(now.current, 7)); + const { control, handleSubmit, watch } = useForm({ + defaultValues: { + dateFrom: formatForInput(subDays(now.current, 7)), + dateTo: '', + assetId: Object.keys(assets)[0], + }, }); - const [dateTo, setDateTo] = useState(''); + const dateTo = watch('dateTo'); const maxFromDate = formatForInput(new Date(dateTo || now.current)); const maxToDate = formatForInput(now.current); - - const [assetId, setAssetId] = useState(Object.keys(assets)[0]); const protohost = getProtoHost(vegaUrl); - const disabled = Boolean(!assetId); const hasItem = useLedgerDownloadFile((store) => store.hasItem); const updateDownloadQueue = useLedgerDownloadFile( (store) => store.updateQueue ); - const assetDropDown = ( - { - setAssetId(e.target.value); - }} - className="w-full" - data-testid="select-ledger-asset" - > - {Object.keys(assets).map((assetKey) => ( - - ))} - - ); + const startDownload = useCallback( + async (formValues: LedgerFormValues) => { + const link = createDownloadUrl({ + protohost, + partyId, + ...formValues, + }); - const link = createDownloadUrl({ - protohost, - partyId, - assetId, - dateFrom, - dateTo, - }); + const dateTimeFormatter = getDateTimeFormat(); + const title = t('Downloading for %s from %s till %s', [ + assets[formValues.assetId], + dateTimeFormatter.format(new Date(formValues.dateFrom)), + dateTimeFormatter.format(new Date(formValues.dateTo || Date.now())), + ]); - const startDownload = async (event: React.FormEvent) => { - event.preventDefault(); - - const title = t('Downloading for %s from %s till %s', [ - assets[assetId], - format(new Date(dateFrom), 'dd MMMM yyyy HH:mm'), - format(new Date(dateTo || Date.now()), 'dd MMMM yyyy HH:mm'), - ]); - - const downloadStoreItem = { - title, - link, - isChanged: true, - }; - if (hasItem(link)) { - updateDownloadQueue(downloadStoreItem); - return; - } - const ts = setTimeout(() => { - updateDownloadQueue({ - ...downloadStoreItem, - intent: Intent.Warning, - isDelayed: true, + const downloadStoreItem = { + title, + link, isChanged: true, - }); - }, 1000 * 30); - - try { - updateDownloadQueue(downloadStoreItem); - const resp = await fetch(link); - if (!resp?.ok) { - if (resp?.status === 429) { - throw new Error('Too many requests. Try again later.'); - } - throw new Error('Download of ledger entries failed'); + }; + if (hasItem(link)) { + updateDownloadQueue(downloadStoreItem); + return; } - const { headers } = resp; - const nameHeader = headers.get('content-disposition'); - const filename = nameHeader?.split('=').pop() ?? DEFAULT_EXPORT_FILE_NAME; - updateDownloadQueue({ - ...downloadStoreItem, - filename, - }); - const blob = await resp.blob(); - if (blob) { + const ts = setTimeout(() => { updateDownloadQueue({ ...downloadStoreItem, - blob, - isDownloaded: true, + intent: Intent.Warning, + isDelayed: true, isChanged: true, - intent: Intent.Success, }); + }, 1000 * 30); + + try { + updateDownloadQueue(downloadStoreItem); + const resp = await fetch(link); + if (!resp?.ok) { + if (resp?.status === 429) { + throw new Error('Too many requests. Try again later.'); + } + throw new Error('Download of ledger entries failed'); + } + const { headers } = resp; + const nameHeader = headers.get('content-disposition'); + const filename = + nameHeader?.split('=').pop() ?? DEFAULT_EXPORT_FILE_NAME; + updateDownloadQueue({ + ...downloadStoreItem, + filename, + }); + const blob = await resp.blob(); + if (blob) { + updateDownloadQueue({ + ...downloadStoreItem, + blob, + isDownloaded: true, + isChanged: true, + intent: Intent.Success, + }); + } + } catch (err) { + localLoggerFactory({ application: 'ledger' }).error( + 'Download file', + err + ); + updateDownloadQueue({ + ...downloadStoreItem, + intent: Intent.Danger, + isError: true, + isChanged: true, + errorMessage: (err as Error).message || undefined, + }); + } finally { + clearTimeout(ts); } - } catch (err) { - localLoggerFactory({ application: 'ledger' }).error('Download file', err); - updateDownloadQueue({ - ...downloadStoreItem, - intent: Intent.Danger, - isError: true, - isChanged: true, - errorMessage: (err as Error).message || undefined, - }); - } finally { - clearTimeout(ts); - } - }; + }, + [assets, hasItem, partyId, protohost, updateDownloadQueue] + ); if (!protohost || Object.keys(assets).length === 0) { return null; @@ -187,49 +174,117 @@ export const LedgerExportForm = ({ partyId, vegaUrl, assets }: Props) => { const offset = new Date().getTimezoneOffset(); return ( -
+

{t('Export ledger entries')}

- - {assetDropDown} - - - setDateFrom(e.target.value)} - max={maxFromDate} - /> - - - setDateTo(e.target.value)} - max={maxToDate} - /> - + ( +
+ + + {Object.keys(assets).map((assetKey) => ( + + ))} + + + {fieldState.error && ( + {fieldState.error.message} + )} +
+ )} + /> + ( +
+ + + + {fieldState.error && ( + {fieldState.error.message} + )} +
+ )} + /> + ( +
+ + + + {fieldState.error && ( + {fieldState.error.message} + )} +
+ )} + />
- + {t('Download')}
- {offset && ( + {offset ? (

{t( 'The downloaded file uses the UTC time zone for all listed times. Your time zone is UTC%s.', [toHoursAndMinutes(offset)] )}

- )} + ) : null} ); }; From 4f7918f64ef35bbc014969f864a4e3d119b00b1a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20G=C5=82ownia?= Date: Wed, 29 Nov 2023 18:05:53 +0100 Subject: [PATCH 4/8] feat(trading): remove proposal warning from market header (#5385) --- .../src/integration/market-proposal-notification.cy.ts | 7 +++---- apps/trading/client-pages/market/market-header-stats.tsx | 2 -- .../src/components/market-proposal-notification.tsx | 2 +- .../src/components/notification/notification.tsx | 2 +- 4 files changed, 5 insertions(+), 8 deletions(-) diff --git a/apps/trading-e2e/src/integration/market-proposal-notification.cy.ts b/apps/trading-e2e/src/integration/market-proposal-notification.cy.ts index c78fc8786..d700a3ae1 100644 --- a/apps/trading-e2e/src/integration/market-proposal-notification.cy.ts +++ b/apps/trading-e2e/src/integration/market-proposal-notification.cy.ts @@ -2,8 +2,6 @@ import { aliasGQLQuery } from '@vegaprotocol/cypress'; import { proposalListQuery, marketUpdateProposal } from '@vegaprotocol/mock'; import * as Schema from '@vegaprotocol/types'; -const marketSummaryBlock = 'header-summary'; - describe('Market proposal notification', { tags: '@smoke' }, () => { before(() => { cy.setVegaWallet(); @@ -25,12 +23,13 @@ describe('Market proposal notification', { tags: '@smoke' }, () => { }); cy.mockSubscription(); cy.visit('/#/markets/market-0'); + cy.getByTestId('Info').click(); cy.wait('@MarketData'); - cy.getByTestId(marketSummaryBlock).should('be.visible'); + cy.getByTestId('sidebar-content').should('be.visible'); }); it('should display market proposal notification if proposal found', () => { - cy.getByTestId(marketSummaryBlock).within(() => { + cy.getByTestId('sidebar-content').within(() => { cy.getByTestId('market-proposal-notification').should( 'contain.text', 'Changes have been proposed for this market' diff --git a/apps/trading/client-pages/market/market-header-stats.tsx b/apps/trading/client-pages/market/market-header-stats.tsx index 7906f6801..9f4569657 100644 --- a/apps/trading/client-pages/market/market-header-stats.tsx +++ b/apps/trading/client-pages/market/market-header-stats.tsx @@ -1,7 +1,6 @@ import { useAssetDetailsDialogStore } from '@vegaprotocol/assets'; import { DocsLinks, useEnvironment } from '@vegaprotocol/environment'; import { ButtonLink, ExternalLink, Link } from '@vegaprotocol/ui-toolkit'; -import { MarketProposalNotification } from '@vegaprotocol/proposals'; import type { Market } from '@vegaprotocol/markets'; import { addDecimalsFormatNumber, @@ -144,7 +143,6 @@ export const MarketHeaderStats = ({ market }: MarketHeaderStatsProps) => { /> )} - ); }; diff --git a/libs/proposals/src/components/market-proposal-notification.tsx b/libs/proposals/src/components/market-proposal-notification.tsx index 07f82804d..e5206edba 100644 --- a/libs/proposals/src/components/market-proposal-notification.tsx +++ b/libs/proposals/src/components/market-proposal-notification.tsx @@ -29,7 +29,7 @@ export const MarketProposalNotification = ({ ); return ( -
+
Date: Wed, 29 Nov 2023 19:17:44 +0200 Subject: [PATCH 5/8] fix(trading): fees accrued tooltip (#5387) --- libs/datagrid/src/lib/ag-grid/ag-grid-themed.tsx | 3 +++ libs/liquidity/src/lib/liquidity-table.tsx | 5 +---- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/libs/datagrid/src/lib/ag-grid/ag-grid-themed.tsx b/libs/datagrid/src/lib/ag-grid/ag-grid-themed.tsx index 269aa1c99..033b2dc18 100644 --- a/libs/datagrid/src/lib/ag-grid/ag-grid-themed.tsx +++ b/libs/datagrid/src/lib/ag-grid/ag-grid-themed.tsx @@ -37,6 +37,9 @@ export const AgGridThemed = ({
{ if (!value) return '-'; - const newValue = new BigNumber(value) - .times(Number(stakeToCcyVolume) || 1) - .toString(); let lessThanFull = false, lessThanMinimum = false; if (data.sla) { @@ -153,7 +150,7 @@ export const LiquidityTable = ({ ] ); } - return addDecimalsFormatNumber(newValue, assetDecimalPlaces ?? 0); + return addDecimalsFormatNumber(value, assetDecimalPlaces ?? 0); }; const stakeToCcyVolumeQuantumFormatter = ({ From f57d6a7c7bafceafc20992754bcad7e2d118cc7e Mon Sep 17 00:00:00 2001 From: Art Date: Wed, 29 Nov 2023 18:23:14 +0100 Subject: [PATCH 6/8] fix(trading): missing volume discount program issue, inverted tiers (#5378) --- .../referrals/hooks/use-referral-program.ts | 30 ++++--- .../referrals/referral-statistics.tsx | 4 +- apps/trading/client-pages/referrals/tiers.tsx | 6 +- .../fees-container/fees-container.tsx | 84 ++++++++----------- 4 files changed, 56 insertions(+), 68 deletions(-) diff --git a/apps/trading/client-pages/referrals/hooks/use-referral-program.ts b/apps/trading/client-pages/referrals/hooks/use-referral-program.ts index 7a1a6cc76..d501c0161 100644 --- a/apps/trading/client-pages/referrals/hooks/use-referral-program.ts +++ b/apps/trading/client-pages/referrals/hooks/use-referral-program.ts @@ -75,22 +75,20 @@ export const useReferralProgram = () => { const benefitTiers = sortBy(data.currentReferralProgram.benefitTiers, (t) => Number(t.referralRewardFactor) - ) - .reverse() - .map((t, i) => { - return { - tier: i + 1, - rewardFactor: Number(t.referralRewardFactor), - commission: Number(t.referralRewardFactor) * 100 + '%', - discountFactor: Number(t.referralDiscountFactor), - discount: Number(t.referralDiscountFactor) * 100 + '%', - minimumVolume: Number(t.minimumRunningNotionalTakerVolume), - volume: getNumberFormat(0).format( - Number(t.minimumRunningNotionalTakerVolume) - ), - epochs: Number(t.minimumEpochs), - }; - }); + ).map((t, i) => { + return { + tier: i + 1, // sorted in asc order, hence first is the lowest tier + rewardFactor: Number(t.referralRewardFactor), + commission: Number(t.referralRewardFactor) * 100 + '%', + discountFactor: Number(t.referralDiscountFactor), + discount: Number(t.referralDiscountFactor) * 100 + '%', + minimumVolume: Number(t.minimumRunningNotionalTakerVolume), + volume: getNumberFormat(0).format( + Number(t.minimumRunningNotionalTakerVolume) + ), + epochs: Number(t.minimumEpochs), + }; + }); const stakingTiers = sortBy( data.currentReferralProgram.stakingTiers, diff --git a/apps/trading/client-pages/referrals/referral-statistics.tsx b/apps/trading/client-pages/referrals/referral-statistics.tsx index 69e10d30c..628732687 100644 --- a/apps/trading/client-pages/referrals/referral-statistics.tsx +++ b/apps/trading/client-pages/referrals/referral-statistics.tsx @@ -29,8 +29,8 @@ import { useLayoutEffect, useRef, useState } from 'react'; import { useCurrentEpochInfoQuery } from './hooks/__generated__/Epoch'; import BigNumber from 'bignumber.js'; import { t } from '@vegaprotocol/i18n'; -import maxBy from 'lodash/maxBy'; import { DocsLinks } from '@vegaprotocol/environment'; +import minBy from 'lodash/minBy'; export const ReferralStatistics = () => { const { pubKey } = useVegaWallet(); @@ -124,7 +124,7 @@ export const Statistics = ({ ); const nextBenefitTierValue = currentBenefitTierValue ? benefitTiers.find((t) => t.tier === currentBenefitTierValue.tier - 1) - : maxBy(benefitTiers, (bt) => bt.tier); // max tier number is lowest tier + : minBy(benefitTiers, (bt) => bt.tier); // min tier number is lowest tier const epochsValue = !isNaN(currentEpoch) && refereeInfo?.atEpoch ? currentEpoch - refereeInfo?.atEpoch diff --git a/apps/trading/client-pages/referrals/tiers.tsx b/apps/trading/client-pages/referrals/tiers.tsx index 41cd7c814..46b73b616 100644 --- a/apps/trading/client-pages/referrals/tiers.tsx +++ b/apps/trading/client-pages/referrals/tiers.tsx @@ -199,13 +199,13 @@ const TiersTable = ({ ...d, className: classNames({ 'from-vega-pink-400 dark:from-vega-pink-600 to-20% bg-highlight': - d.tier === 1, + d.tier >= 3, 'from-vega-purple-400 dark:from-vega-purple-600 to-20% bg-highlight': d.tier === 2, 'from-vega-blue-400 dark:from-vega-blue-600 to-20% bg-highlight': - d.tier === 3, + d.tier === 1, 'from-vega-orange-400 dark:from-vega-orange-600 to-20% bg-highlight': - d.tier > 3, + d.tier == 0, }), }))} /> diff --git a/apps/trading/components/fees-container/fees-container.tsx b/apps/trading/components/fees-container/fees-container.tsx index ea4c8a095..2e156b821 100644 --- a/apps/trading/components/fees-container/fees-container.tsx +++ b/apps/trading/components/fees-container/fees-container.tsx @@ -36,7 +36,7 @@ export const FeesContainer = () => { const { data: markets, loading: marketsLoading } = useMarketList(); const { data: programData, loading: programLoading } = - useDiscountProgramsQuery(); + useDiscountProgramsQuery({ errorPolicy: 'ignore' }); const volumeDiscountWindowLength = programData?.currentVolumeDiscountProgram?.windowLength || 1; @@ -459,25 +459,19 @@ const VolumeTiers = ({ - {Array.from(tiers) - .reverse() - .map((tier, i) => { - const isUserTier = tiers.length - 1 - tierIndex === i; + {Array.from(tiers).map((tier, i) => { + const isUserTier = tiers.length - 1 - tierIndex === i; - return ( - - {i + 1} - - {formatPercentage(Number(tier.volumeDiscountFactor))}% - - - {formatNumber(tier.minimumRunningNotionalTakerVolume)} - - {isUserTier ? formatNumber(lastEpochVolume) : ''} - {isUserTier ? : null} - - ); - })} + return ( + + {i + 1} + {formatPercentage(Number(tier.volumeDiscountFactor))}% + {formatNumber(tier.minimumRunningNotionalTakerVolume)} + {isUserTier ? formatNumber(lastEpochVolume) : ''} + {isUserTier ? : null} + + ); + })}
@@ -518,37 +512,33 @@ const ReferralTiers = ({ - {Array.from(tiers) - .reverse() - .map((t, i) => { - const isUserTier = tiers.length - 1 - tierIndex === i; + {Array.from(tiers).map((t, i) => { + const isUserTier = tiers.length - 1 - tierIndex === i; - const requiredVolume = Number( - t.minimumRunningNotionalTakerVolume + const requiredVolume = Number(t.minimumRunningNotionalTakerVolume); + let unlocksIn = null; + + if ( + referralVolumeInWindow >= requiredVolume && + epochsInSet < t.minimumEpochs + ) { + unlocksIn = ( + + Unlocks in {t.minimumEpochs - epochsInSet} epochs + ); - let unlocksIn = null; + } - if ( - referralVolumeInWindow >= requiredVolume && - epochsInSet < t.minimumEpochs - ) { - unlocksIn = ( - - Unlocks in {t.minimumEpochs - epochsInSet} epochs - - ); - } - - return ( - - {i + 1} - {formatPercentage(Number(t.referralDiscountFactor))}% - {formatNumber(t.minimumRunningNotionalTakerVolume)} - {t.minimumEpochs} - {isUserTier ? : unlocksIn} - - ); - })} + return ( + + {i + 1} + {formatPercentage(Number(t.referralDiscountFactor))}% + {formatNumber(t.minimumRunningNotionalTakerVolume)} + {t.minimumEpochs} + {isUserTier ? : unlocksIn} + + ); + })}
From 2d926c0ce05ca18b59b8d2a52d7033d935630846 Mon Sep 17 00:00:00 2001 From: Art Date: Wed, 29 Nov 2023 19:38:41 +0100 Subject: [PATCH 7/8] fix(governance): sensible vote numbers (#5384) Co-authored-by: Madalina Raicu --- .../vote-breakdown/vote-breakdown.spec.tsx | 21 +++++++++++- .../vote-breakdown/vote-breakdown.tsx | 33 ++++++++++--------- libs/react-helpers/src/lib/format/number.tsx | 4 ++- 3 files changed, 40 insertions(+), 18 deletions(-) diff --git a/apps/governance/src/routes/proposals/components/vote-breakdown/vote-breakdown.spec.tsx b/apps/governance/src/routes/proposals/components/vote-breakdown/vote-breakdown.spec.tsx index 623c81f67..efa63fb27 100644 --- a/apps/governance/src/routes/proposals/components/vote-breakdown/vote-breakdown.spec.tsx +++ b/apps/governance/src/routes/proposals/components/vote-breakdown/vote-breakdown.spec.tsx @@ -8,7 +8,7 @@ import { networkParamsQueryMock, nextWeek, } from '../../test-helpers/mocks'; -import { VoteBreakdown } from './vote-breakdown'; +import { CompactVotes, VoteBreakdown } from './vote-breakdown'; import type { ProposalQuery } from '../../proposal/__generated__/Proposal'; import type { MockedResponse } from '@apollo/client/testing'; import { @@ -346,3 +346,22 @@ describe('VoteBreakdown', () => { expect(style.width).toBe(`${expectedProgress}%`); }); }); + +describe('CompactVotes', () => { + it.each([ + [0, '0'], + [1, '1'], + [12, '12'], + [123, '123'], + [1234, '1.2K'], + [12345, '12.3K'], + [123456, '123.5K'], + [1234567, '1.2M'], + [12345678, '12.3M'], + [123456789, '123.5M'], + [1234567890, '1.2B'], + ])('compacts %s to %s', (input, output) => { + const { getByTestId } = render(); + expect(getByTestId('compact-number').textContent).toBe(output); + }); +}); diff --git a/apps/governance/src/routes/proposals/components/vote-breakdown/vote-breakdown.tsx b/apps/governance/src/routes/proposals/components/vote-breakdown/vote-breakdown.tsx index 121ce0800..7e8ae8c9c 100644 --- a/apps/governance/src/routes/proposals/components/vote-breakdown/vote-breakdown.tsx +++ b/apps/governance/src/routes/proposals/components/vote-breakdown/vote-breakdown.tsx @@ -3,11 +3,21 @@ import BigNumber from 'bignumber.js'; import { useTranslation } from 'react-i18next'; import { useVoteInformation } from '../../hooks'; import { Icon, Tooltip } from '@vegaprotocol/ui-toolkit'; -import { formatNumber, toBigNum } from '@vegaprotocol/utils'; +import { formatNumber } from '@vegaprotocol/utils'; import { ProposalState } from '@vegaprotocol/types'; import type { ReactNode } from 'react'; import type { ProposalFieldsFragment } from '../../proposals/__generated__/Proposals'; import type { ProposalQuery } from '../../proposal/__generated__/Proposal'; +import { CompactNumber } from '@vegaprotocol/react-helpers'; + +export const CompactVotes = ({ number }: { number: BigNumber }) => ( + +); interface VoteBreakdownProps { proposal: ProposalFieldsFragment | ProposalQuery['proposal']; @@ -198,10 +208,7 @@ export const VoteBreakdown = ({ proposal }: VoteBreakdownProps) => { )} > @@ -226,10 +233,7 @@ export const VoteBreakdown = ({ proposal }: VoteBreakdownProps) => { )} > @@ -279,10 +283,7 @@ export const VoteBreakdown = ({ proposal }: VoteBreakdownProps) => { )} > @@ -321,7 +322,7 @@ export const VoteBreakdown = ({ proposal }: VoteBreakdownProps) => { {t('tokenVotesFor')}: @@ -341,7 +342,7 @@ export const VoteBreakdown = ({ proposal }: VoteBreakdownProps) => { {t('tokenVotesAgainst')}: @@ -384,7 +385,7 @@ export const VoteBreakdown = ({ proposal }: VoteBreakdownProps) => { {t('totalTokensVoted')}: diff --git a/libs/react-helpers/src/lib/format/number.tsx b/libs/react-helpers/src/lib/format/number.tsx index 299d03c52..b9efb0e9d 100644 --- a/libs/react-helpers/src/lib/format/number.tsx +++ b/libs/react-helpers/src/lib/format/number.tsx @@ -22,11 +22,13 @@ export const CompactNumber = ({ decimals = 'infer', compactDisplay = 'short', testId = 'compact-number', + compactAbove = DEFAULT_COMPACT_ABOVE, }: { number: BigNumber; decimals?: number | 'infer'; compactDisplay?: 'short' | 'long'; testId?: string; + compactAbove?: number; }) => { if (!number.isFinite()) { return ( @@ -44,7 +46,7 @@ export const CompactNumber = ({ return {INFINITY}; } - if (number.isLessThan(DEFAULT_COMPACT_ABOVE)) { + if (number.isLessThan(compactAbove)) { return ( {formatNumber(number, decimalPlaces)} ); From e4c4c2063185dbf6cda6be1c7aa71848fe2e761f Mon Sep 17 00:00:00 2001 From: Matthew Russell Date: Wed, 29 Nov 2023 14:09:08 -0800 Subject: [PATCH 8/8] fix: duplicate props in ag-grid-themed --- libs/datagrid/src/lib/ag-grid/ag-grid-themed.tsx | 3 --- 1 file changed, 3 deletions(-) diff --git a/libs/datagrid/src/lib/ag-grid/ag-grid-themed.tsx b/libs/datagrid/src/lib/ag-grid/ag-grid-themed.tsx index a4288c1b7..0184e46d9 100644 --- a/libs/datagrid/src/lib/ag-grid/ag-grid-themed.tsx +++ b/libs/datagrid/src/lib/ag-grid/ag-grid-themed.tsx @@ -40,9 +40,6 @@ export const AgGridThemed = ({ overlayNoRowsTemplate={t('No data')} suppressDragLeaveHidesColumns ref={gridRef} - overlayLoadingTemplate={t('Loading...')} - overlayNoRowsTemplate={t('No data')} - suppressDragLeaveHidesColumns {...defaultProps} {...props} />