diff --git a/.changeset/lemon-pianos-stick.md b/.changeset/lemon-pianos-stick.md new file mode 100644 index 0000000000..52714e1245 --- /dev/null +++ b/.changeset/lemon-pianos-stick.md @@ -0,0 +1,5 @@ +--- +"@venusprotocol/evm": minor +--- + +add hub info to Liquidity Hub page diff --git a/apps/evm/src/__mocks__/models/liquidityHubSources.ts b/apps/evm/src/__mocks__/models/liquidityHubSources.ts index 467ddee086..32d3385e94 100644 --- a/apps/evm/src/__mocks__/models/liquidityHubSources.ts +++ b/apps/evm/src/__mocks__/models/liquidityHubSources.ts @@ -7,16 +7,21 @@ import { busd, lisUsd, usdc, usdt, xvs } from './tokens'; export const xvsCoreSource: LiquidityHubSource = { name: 'Core Pool', address: '0x4000000000000000000000000000000000000001', - type: 'core', allocationTokens: new BigNumber(8000), allocationCents: new BigNumber(800000), - allocationCapPercentage: new BigNumber(55), - allocationCapTokens: new BigNumber(16000), allocationCapCents: new BigNumber(1600000), liquidityTokens: new BigNumber(6200), liquidityCents: new BigNumber(620000), supplyApyPercentage: new BigNumber(5.7), - paused: false, + supplyTokenDistributions: [ + { + type: 'venus', + token: xvs, + apyPercentage: new BigNumber(0.85), + dailyDistributedTokens: new BigNumber(0.03), + isActive: true, + }, + ], collateralTokens: [xvs, usdc], lockEndDate: new Date('2026-08-01T00:00:00.000Z'), }; @@ -24,16 +29,21 @@ export const xvsCoreSource: LiquidityHubSource = { export const xvsFluxSource: LiquidityHubSource = { name: 'Flux Vault', address: '0x4000000000000000000000000000000000000002', - type: 'venusFlux', allocationTokens: new BigNumber(7250), allocationCents: new BigNumber(725000), - allocationCapPercentage: new BigNumber(45), - allocationCapTokens: new BigNumber(14500), allocationCapCents: new BigNumber(1450000), liquidityTokens: new BigNumber(5800), liquidityCents: new BigNumber(580000), supplyApyPercentage: new BigNumber(6.8), - paused: false, + supplyTokenDistributions: [ + { + type: 'venus', + token: xvs, + apyPercentage: new BigNumber(1.1), + dailyDistributedTokens: new BigNumber(0.03), + isActive: true, + }, + ], collateralTokens: [xvs, usdt], lockEndDate: new Date('2026-09-15T00:00:00.000Z'), }; @@ -41,16 +51,21 @@ export const xvsFluxSource: LiquidityHubSource = { export const usdcCoreSource: LiquidityHubSource = { name: 'Stable Core', address: '0x4000000000000000000000000000000000000003', - type: 'core', allocationTokens: new BigNumber(175000), allocationCents: new BigNumber(17500000), - allocationCapPercentage: new BigNumber(60), - allocationCapTokens: new BigNumber(350000), allocationCapCents: new BigNumber(35000000), liquidityTokens: new BigNumber(140000), liquidityCents: new BigNumber(14000000), supplyApyPercentage: new BigNumber(3.9), - paused: false, + supplyTokenDistributions: [ + { + type: 'venus', + token: xvs, + apyPercentage: new BigNumber(0.45), + dailyDistributedTokens: new BigNumber(0.3), + isActive: true, + }, + ], collateralTokens: [usdc, usdt, busd], lockEndDate: new Date('2026-07-22T00:00:00.000Z'), }; @@ -58,16 +73,21 @@ export const usdcCoreSource: LiquidityHubSource = { export const usdcFluxSource: LiquidityHubSource = { name: 'Stable Flux', address: '0x4000000000000000000000000000000000000004', - type: 'venusFlux', allocationTokens: new BigNumber(175000), allocationCents: new BigNumber(17500000), - allocationCapPercentage: new BigNumber(40), - allocationCapTokens: new BigNumber(350000), allocationCapCents: new BigNumber(35000000), liquidityTokens: new BigNumber(150000), liquidityCents: new BigNumber(15000000), supplyApyPercentage: new BigNumber(4.4), - paused: false, + supplyTokenDistributions: [ + { + type: 'venus', + token: xvs, + apyPercentage: new BigNumber(0.7), + dailyDistributedTokens: new BigNumber(0.47), + isActive: true, + }, + ], collateralTokens: [usdc, lisUsd], lockEndDate: new Date('2026-10-10T00:00:00.000Z'), }; @@ -75,16 +95,21 @@ export const usdcFluxSource: LiquidityHubSource = { export const usdtCoreSource: LiquidityHubSource = { name: 'USDT Core', address: '0x4000000000000000000000000000000000000005', - type: 'core', allocationTokens: new BigNumber(90000), allocationCents: new BigNumber(9000000), - allocationCapPercentage: new BigNumber(50), - allocationCapTokens: new BigNumber(180000), allocationCapCents: new BigNumber(18000000), liquidityTokens: new BigNumber(76000), liquidityCents: new BigNumber(7600000), supplyApyPercentage: new BigNumber(4.3), - paused: false, + supplyTokenDistributions: [ + { + type: 'venus', + token: xvs, + apyPercentage: new BigNumber(0.55), + dailyDistributedTokens: new BigNumber(0.19), + isActive: true, + }, + ], collateralTokens: [usdt, usdc], lockEndDate: new Date('2026-08-20T00:00:00.000Z'), }; @@ -92,16 +117,21 @@ export const usdtCoreSource: LiquidityHubSource = { export const usdtFluxSource: LiquidityHubSource = { name: 'USDT Flux', address: '0x4000000000000000000000000000000000000006', - type: 'venusFlux', allocationTokens: new BigNumber(120000), allocationCents: new BigNumber(12000000), - allocationCapPercentage: new BigNumber(50), - allocationCapTokens: new BigNumber(240000), allocationCapCents: new BigNumber(24000000), liquidityTokens: new BigNumber(90000), liquidityCents: new BigNumber(9000000), supplyApyPercentage: new BigNumber(5.1), - paused: false, + supplyTokenDistributions: [ + { + type: 'venus', + token: xvs, + apyPercentage: new BigNumber(0.9), + dailyDistributedTokens: new BigNumber(0.41), + isActive: true, + }, + ], collateralTokens: [usdt, xvs], lockEndDate: new Date('2026-11-01T00:00:00.000Z'), }; @@ -109,16 +139,21 @@ export const usdtFluxSource: LiquidityHubSource = { export const busdCoreSource: LiquidityHubSource = { name: 'Reserve Core', address: '0x4000000000000000000000000000000000000007', - type: 'core', allocationTokens: new BigNumber(100000), allocationCents: new BigNumber(10000000), - allocationCapPercentage: new BigNumber(65), - allocationCapTokens: new BigNumber(200000), allocationCapCents: new BigNumber(20000000), liquidityTokens: new BigNumber(78000), liquidityCents: new BigNumber(7800000), supplyApyPercentage: new BigNumber(3.6), - paused: false, + supplyTokenDistributions: [ + { + type: 'venus', + token: xvs, + apyPercentage: new BigNumber(0.35), + dailyDistributedTokens: new BigNumber(0.14), + isActive: true, + }, + ], collateralTokens: [busd, usdc], lockEndDate: new Date('2026-07-30T00:00:00.000Z'), }; @@ -126,16 +161,21 @@ export const busdCoreSource: LiquidityHubSource = { export const busdFluxSource: LiquidityHubSource = { name: 'Reserve Flux', address: '0x4000000000000000000000000000000000000008', - type: 'venusFlux', allocationTokens: new BigNumber(80000), allocationCents: new BigNumber(8000000), - allocationCapPercentage: new BigNumber(35), - allocationCapTokens: new BigNumber(160000), allocationCapCents: new BigNumber(16000000), liquidityTokens: new BigNumber(63000), liquidityCents: new BigNumber(6300000), supplyApyPercentage: new BigNumber(4.2), - paused: false, + supplyTokenDistributions: [ + { + type: 'venus', + token: xvs, + apyPercentage: new BigNumber(0.6), + dailyDistributedTokens: new BigNumber(0.18), + isActive: true, + }, + ], collateralTokens: [busd, lisUsd], lockEndDate: new Date('2026-09-05T00:00:00.000Z'), }; diff --git a/apps/evm/src/__mocks__/models/liquidityHubYieldGroups.ts b/apps/evm/src/__mocks__/models/liquidityHubYieldGroups.ts new file mode 100644 index 0000000000..7165a4a18b --- /dev/null +++ b/apps/evm/src/__mocks__/models/liquidityHubYieldGroups.ts @@ -0,0 +1,161 @@ +import BigNumber from 'bignumber.js'; + +import type { LiquidityHubYieldGroup } from 'types'; + +import { + busdCoreSource, + busdFluxSource, + usdcCoreSource, + usdcFluxSource, + usdtCoreSource, + usdtFluxSource, + xvsCoreSource, + xvsFluxSource, +} from './liquidityHubSources'; + +export const xvsCoreYieldGroup: LiquidityHubYieldGroup = { + address: '0x5000000000000000000000000000000000000001', + type: 'venusCore', + name: 'Venus Core', + bgClassName: 'bg-blue', + allocationTokens: xvsCoreSource.allocationTokens, + allocationCents: xvsCoreSource.allocationCents, + allocationCapPercentage: new BigNumber(55), + allocationCapTokens: new BigNumber(16000), + allocationCapCents: xvsCoreSource.allocationCapCents, + liquidityTokens: xvsCoreSource.liquidityTokens, + liquidityCents: xvsCoreSource.liquidityCents, + averageSupplyApyPercentage: xvsCoreSource.supplyApyPercentage, + paused: false, + sources: [xvsCoreSource, busdCoreSource], +}; + +export const xvsFluxYieldGroup: LiquidityHubYieldGroup = { + address: '0x5000000000000000000000000000000000000002', + type: 'venusFlux', + name: 'Venus Flux', + bgClassName: 'bg-yellow', + allocationTokens: xvsFluxSource.allocationTokens, + allocationCents: xvsFluxSource.allocationCents, + allocationCapPercentage: new BigNumber(45), + allocationCapTokens: new BigNumber(14500), + allocationCapCents: xvsFluxSource.allocationCapCents, + liquidityTokens: xvsFluxSource.liquidityTokens, + liquidityCents: xvsFluxSource.liquidityCents, + averageSupplyApyPercentage: xvsFluxSource.supplyApyPercentage, + paused: false, + sources: [xvsFluxSource], +}; + +export const usdcCoreYieldGroup: LiquidityHubYieldGroup = { + address: '0x5000000000000000000000000000000000000003', + type: 'venusCore', + name: 'Venus Core', + bgClassName: 'bg-blue', + allocationTokens: usdcCoreSource.allocationTokens, + allocationCents: usdcCoreSource.allocationCents, + allocationCapPercentage: new BigNumber(60), + allocationCapTokens: new BigNumber(350000), + allocationCapCents: usdcCoreSource.allocationCapCents, + liquidityTokens: usdcCoreSource.liquidityTokens, + liquidityCents: usdcCoreSource.liquidityCents, + averageSupplyApyPercentage: usdcCoreSource.supplyApyPercentage, + paused: false, + sources: [usdcCoreSource], +}; + +export const usdcFluxYieldGroup: LiquidityHubYieldGroup = { + address: '0x5000000000000000000000000000000000000004', + type: 'venusFlux', + name: 'Venus Flux', + bgClassName: 'bg-yellow', + allocationTokens: usdcFluxSource.allocationTokens, + allocationCents: usdcFluxSource.allocationCents, + allocationCapPercentage: new BigNumber(40), + allocationCapTokens: new BigNumber(350000), + allocationCapCents: usdcFluxSource.allocationCapCents, + liquidityTokens: usdcFluxSource.liquidityTokens, + liquidityCents: usdcFluxSource.liquidityCents, + averageSupplyApyPercentage: usdcFluxSource.supplyApyPercentage, + paused: false, + sources: [usdcFluxSource], +}; + +export const usdtCoreYieldGroup: LiquidityHubYieldGroup = { + address: '0x5000000000000000000000000000000000000005', + type: 'venusCore', + name: 'Venus Core', + bgClassName: 'bg-blue', + allocationTokens: usdtCoreSource.allocationTokens, + allocationCents: usdtCoreSource.allocationCents, + allocationCapPercentage: new BigNumber(50), + allocationCapTokens: new BigNumber(180000), + allocationCapCents: usdtCoreSource.allocationCapCents, + liquidityTokens: usdtCoreSource.liquidityTokens, + liquidityCents: usdtCoreSource.liquidityCents, + averageSupplyApyPercentage: usdtCoreSource.supplyApyPercentage, + paused: false, + sources: [usdtCoreSource], +}; + +export const usdtFluxYieldGroup: LiquidityHubYieldGroup = { + address: '0x5000000000000000000000000000000000000006', + type: 'venusFlux', + name: 'Venus Flux', + bgClassName: 'bg-yellow', + allocationTokens: usdtFluxSource.allocationTokens, + allocationCents: usdtFluxSource.allocationCents, + allocationCapPercentage: new BigNumber(50), + allocationCapTokens: new BigNumber(240000), + allocationCapCents: usdtFluxSource.allocationCapCents, + liquidityTokens: usdtFluxSource.liquidityTokens, + liquidityCents: usdtFluxSource.liquidityCents, + averageSupplyApyPercentage: usdtFluxSource.supplyApyPercentage, + paused: false, + sources: [usdtFluxSource], +}; + +export const busdCoreYieldGroup: LiquidityHubYieldGroup = { + address: '0x5000000000000000000000000000000000000007', + type: 'venusCore', + name: 'Venus Core', + bgClassName: 'bg-blue', + allocationTokens: busdCoreSource.allocationTokens, + allocationCents: busdCoreSource.allocationCents, + allocationCapPercentage: new BigNumber(65), + allocationCapTokens: new BigNumber(200000), + allocationCapCents: busdCoreSource.allocationCapCents, + liquidityTokens: busdCoreSource.liquidityTokens, + liquidityCents: busdCoreSource.liquidityCents, + averageSupplyApyPercentage: busdCoreSource.supplyApyPercentage, + paused: false, + sources: [busdCoreSource], +}; + +export const busdFluxYieldGroup: LiquidityHubYieldGroup = { + address: '0x5000000000000000000000000000000000000008', + type: 'venusFlux', + name: 'Venus Flux', + bgClassName: 'bg-yellow', + allocationTokens: busdFluxSource.allocationTokens, + allocationCents: busdFluxSource.allocationCents, + allocationCapPercentage: new BigNumber(35), + allocationCapTokens: new BigNumber(160000), + allocationCapCents: busdFluxSource.allocationCapCents, + liquidityTokens: busdFluxSource.liquidityTokens, + liquidityCents: busdFluxSource.liquidityCents, + averageSupplyApyPercentage: busdFluxSource.supplyApyPercentage, + paused: false, + sources: [busdFluxSource], +}; + +export const liquidityHubYieldGroups = [ + xvsCoreYieldGroup, + xvsFluxYieldGroup, + usdcCoreYieldGroup, + usdcFluxYieldGroup, + usdtCoreYieldGroup, + usdtFluxYieldGroup, + busdCoreYieldGroup, + busdFluxYieldGroup, +]; diff --git a/apps/evm/src/__mocks__/models/liquidityHubs.ts b/apps/evm/src/__mocks__/models/liquidityHubs.ts index a108c4a649..d685711fcf 100644 --- a/apps/evm/src/__mocks__/models/liquidityHubs.ts +++ b/apps/evm/src/__mocks__/models/liquidityHubs.ts @@ -3,15 +3,15 @@ import BigNumber from 'bignumber.js'; import type { LiquidityHub } from 'types'; import { - busdCoreSource, - busdFluxSource, - usdcCoreSource, - usdcFluxSource, - usdtCoreSource, - usdtFluxSource, - xvsCoreSource, - xvsFluxSource, -} from './liquidityHubSources'; + busdCoreYieldGroup, + busdFluxYieldGroup, + usdcCoreYieldGroup, + usdcFluxYieldGroup, + usdtCoreYieldGroup, + usdtFluxYieldGroup, + xvsCoreYieldGroup, + xvsFluxYieldGroup, +} from './liquidityHubYieldGroups'; import { xvs } from './tokens'; import { vhBusd, vhUsdc, vhUsdt, vhXvs } from './vhTokens'; @@ -32,7 +32,7 @@ export const liquidityHubs: LiquidityHub[] = [ pricePerShare: new BigNumber(1.06), supplierCount: 124, operatorName: 'Venus Core', - sources: [xvsCoreSource, xvsFluxSource], + yieldGroups: [xvsCoreYieldGroup, xvsFluxYieldGroup], supplyTokenDistributions: [ { type: 'venus', @@ -65,7 +65,7 @@ export const liquidityHubs: LiquidityHub[] = [ pricePerShare: new BigNumber(1.02), supplierCount: 289, operatorName: 'Stable Strategies', - sources: [usdcCoreSource, usdcFluxSource], + yieldGroups: [usdcCoreYieldGroup, usdcFluxYieldGroup], supplyTokenDistributions: [ { type: 'venus', @@ -98,7 +98,7 @@ export const liquidityHubs: LiquidityHub[] = [ pricePerShare: new BigNumber(1.03), supplierCount: 198, operatorName: 'Yield Relay', - sources: [usdtCoreSource, usdtFluxSource], + yieldGroups: [usdtCoreYieldGroup, usdtFluxYieldGroup], supplyTokenDistributions: [], userWalletBalanceTokens: new BigNumber(1800), userWalletBalanceCents: new BigNumber(180000), @@ -123,7 +123,7 @@ export const liquidityHubs: LiquidityHub[] = [ pricePerShare: new BigNumber(1.01), supplierCount: 164, operatorName: 'Reserve Engine', - sources: [busdCoreSource, busdFluxSource], + yieldGroups: [busdCoreYieldGroup, busdFluxYieldGroup], supplyTokenDistributions: [], userWalletBalanceTokens: new BigNumber(3200), userWalletBalanceCents: new BigNumber(320000), diff --git a/apps/evm/src/components/AccountHealthBar/index.tsx b/apps/evm/src/components/AccountHealthBar/index.tsx index c9fcdc2092..6ec975442d 100644 --- a/apps/evm/src/components/AccountHealthBar/index.tsx +++ b/apps/evm/src/components/AccountHealthBar/index.tsx @@ -74,7 +74,7 @@ export const AccountHealthBar: React.FC = ({ const readableBorrowLimitUsedPercentage = formatPercentageToReadableValue(borrowLimitUsedPercentage); - const sanitizedFillPercentage = fillPercentage || 0; + const sanitizedFillPercentage = Math.min(Math.max(fillPercentage || 0, 0), 100); const readableBorrowBalance = formatCentsToReadableValue({ value: borrowBalanceCents, @@ -144,16 +144,16 @@ export const AccountHealthBar: React.FC = ({ ], ); - const progressBarColor = useMemo(() => { + const progressBarClassName = useMemo(() => { if (sanitizedFillPercentage <= safeBorrowLimitPercentage) { - return theme.colors.green; + return 'bg-green'; } if (sanitizedFillPercentage <= moderateBorrowLimitPercentage) { - return theme.colors.yellow; + return 'bg-yellow'; } - return theme.colors.red; + return 'bg-red'; }, [sanitizedFillPercentage]); return ( @@ -186,18 +186,20 @@ export const AccountHealthBar: React.FC = ({ }} /> } - value={sanitizedFillPercentage} + progressBars={[ + { + value: sanitizedFillPercentage, + className: progressBarClassName, + }, + ]} marks={[ { value: 80 }, ...(markPercentage !== undefined - ? [{ value: Math.min(markPercentage, 99), color: theme.colors.white }] + ? [{ value: Math.min(markPercentage, 99), className: 'bg-white' }] : []), ]} - step={1} - ariaLabel={t('accountHealth.accessibilityLabel')} min={0} max={100} - progressBarColor={progressBarColor} tooltip={tooltip} /> diff --git a/apps/evm/src/components/ActiveVotingProgress/index.tsx b/apps/evm/src/components/ActiveVotingProgress/index.tsx index 7c56880fa6..eb32979790 100644 --- a/apps/evm/src/components/ActiveVotingProgress/index.tsx +++ b/apps/evm/src/components/ActiveVotingProgress/index.tsx @@ -1,7 +1,6 @@ import { BigNumber } from 'bignumber.js'; import { useMemo } from 'react'; -import { theme } from '@venusprotocol/ui'; import { useTranslation } from 'libs/translations'; import type { Token } from 'types'; import { convertMantissaToTokens } from 'utilities'; @@ -43,7 +42,6 @@ export const ActiveVotingProgress: React.FC = ({ ); const defaultProgressbarProps = { - step: 1, min: 0, max: 100, }; @@ -55,12 +53,15 @@ export const ActiveVotingProgress: React.FC = ({ label: t('vote.for'), value: getValueString({ valueMantissa: votedForMantissa, xvs }), progressBarProps: { - ariaLabel: t('voteProposalUi.statusCard.ariaLabelFor'), - value: - votedForMantissa - ?.dividedBy(votedTotalMantissa || 0) - .times(100) - .toNumber() || 0, + progressBars: [ + { + value: + votedForMantissa + ?.dividedBy(votedTotalMantissa || 0) + .times(100) + .toNumber() || 0, + }, + ], }, }, { @@ -68,13 +69,16 @@ export const ActiveVotingProgress: React.FC = ({ label: t('vote.against'), value: getValueString({ valueMantissa: votedAgainstMantissa, xvs }), progressBarProps: { - progressBarColor: theme.colors.red, - ariaLabel: t('voteProposalUi.statusCard.ariaLabelAgainst'), - value: - votedAgainstMantissa - ?.dividedBy(votedTotalMantissa || 0) - .times(100) - .toNumber() || 0, + progressBars: [ + { + value: + votedAgainstMantissa + ?.dividedBy(votedTotalMantissa || 0) + .times(100) + .toNumber() || 0, + className: 'bg-red', + }, + ], }, }, { @@ -82,13 +86,16 @@ export const ActiveVotingProgress: React.FC = ({ label: t('vote.abstain'), value: getValueString({ valueMantissa: abstainedMantissa, xvs }), progressBarProps: { - progressBarColor: theme.colors.grey, - ariaLabel: t('voteProposalUi.statusCard.ariaLabelAbstain'), - value: - abstainedMantissa - ?.dividedBy(votedTotalMantissa || 0) - .times(100) - .toNumber() || 0, + progressBars: [ + { + value: + abstainedMantissa + ?.dividedBy(votedTotalMantissa || 0) + .times(100) + .toNumber() || 0, + className: 'bg-grey', + }, + ], }, }, ], diff --git a/apps/evm/src/components/CompactTableCardList/__tests__/index.spec.tsx b/apps/evm/src/components/CompactTableCardList/__tests__/index.spec.tsx new file mode 100644 index 0000000000..b4dc534d1a --- /dev/null +++ b/apps/evm/src/components/CompactTableCardList/__tests__/index.spec.tsx @@ -0,0 +1,61 @@ +import { screen } from '@testing-library/react'; + +import type { Order, TableColumn } from 'components/Table'; +import { renderComponent } from 'testUtils/render'; +import { CompactTableCardList } from '..'; + +interface Row { + id: string; + title: string; + value: number; +} + +const rows: Row[] = [ + { id: 'first', title: 'First row', value: 1 }, + { id: 'second', title: 'Second row', value: 2 }, +]; + +const columns: TableColumn[] = [ + { + key: 'title', + label: 'Title', + selectOptionLabel: 'Title', + renderCell: row => {row.title}, + }, + { + key: 'value', + label: 'Value', + selectOptionLabel: 'Value', + renderCell: row => row.value.toString(), + sortRows: (rowA, rowB, direction) => + direction === 'asc' ? rowA.value - rowB.value : rowB.value - rowA.value, + }, +]; + +const order: Order = { + orderBy: columns[1], + orderDirection: 'desc', +}; + +describe('CompactTableCardList', () => { + it('sorts rows and renders actions, footers, and delimiters', () => { + const { container } = renderComponent( + row.id} + order={order} + renderRowAction={row => } + renderRowFooter={row =>
Footer {row.title}
} + />, + ); + + expect(screen.getAllByTestId('row-title').map(element => element.textContent)).toEqual([ + 'Second row', + 'First row', + ]); + expect(screen.getByRole('button', { name: 'Open First row' })).toBeInTheDocument(); + expect(screen.getByText('Footer Second row')).toBeInTheDocument(); + expect(container.querySelectorAll('hr')).toHaveLength(1); + }); +}); diff --git a/apps/evm/src/components/CompactTableCardList/index.tsx b/apps/evm/src/components/CompactTableCardList/index.tsx new file mode 100644 index 0000000000..c0928c6078 --- /dev/null +++ b/apps/evm/src/components/CompactTableCardList/index.tsx @@ -0,0 +1,82 @@ +import { cn } from '@venusprotocol/ui'; + +import { Delimiter } from 'components/Delimiter'; +import { LabeledInlineContent } from 'components/LabeledInlineContent'; +import type { Order, TableColumn } from 'components/Table'; + +export interface CompactTableCardListProps { + columns: TableColumn[]; + data: R[]; + rowKeyExtractor: (row: R) => string; + order?: Order; + className?: string; + onRowClick?: (row: R) => void; + renderRowAction?: (row: R, rowIndex: number) => React.ReactNode; + renderRowFooter?: (row: R, rowIndex: number) => React.ReactNode; +} + +export const CompactTableCardList = ({ + columns, + data, + rowKeyExtractor, + order, + className, + onRowClick, + renderRowAction, + renderRowFooter, +}: CompactTableCardListProps) => { + const [titleColumn, ...otherColumns] = columns; + const sortRows = order?.orderBy.sortRows; + + const sortedData = + sortRows && order + ? [...data].sort((rowA, rowB) => sortRows(rowA, rowB, order.orderDirection)) + : data; + + return ( +
+ {sortedData.map((row, rowIndex) => { + const rowKey = rowKeyExtractor(row); + + return ( +
+
+
onRowClick(row) : undefined} + > +
{titleColumn.renderCell(row, rowIndex)}
+ +
+ {otherColumns.map((column, index) => ( + + {column.renderCell(row, rowIndex)} + + ))} + + {renderRowAction?.(row, rowIndex)} +
+
+ + {renderRowFooter &&
{renderRowFooter(row, rowIndex)}
} +
+ + {rowIndex < sortedData.length - 1 && } +
+ ); + })} +
+ ); +}; diff --git a/apps/evm/src/components/Dropdown/index.tsx b/apps/evm/src/components/Dropdown/index.tsx index 692eaba3c8..5995dd1677 100644 --- a/apps/evm/src/components/Dropdown/index.tsx +++ b/apps/evm/src/components/Dropdown/index.tsx @@ -51,7 +51,7 @@ export const Dropdown = ({ {children({ isDropdownOpen, handleToggleDropdown })} - {/* XS to MD menu */} + {/* MD and up menu */} {isDropdownOpen && (
; export const Default = () => ; - -export const WithEllipseBreakpoint = () => ( - -); diff --git a/apps/evm/src/components/EllipseAddress/index.tsx b/apps/evm/src/components/EllipseAddress/index.tsx index eaa2ebf0df..1a2c392702 100644 --- a/apps/evm/src/components/EllipseAddress/index.tsx +++ b/apps/evm/src/components/EllipseAddress/index.tsx @@ -1,37 +1,14 @@ import { truncateAddress } from 'utilities'; import type { Address } from 'viem'; -import { useStyles } from './styles'; -import type { Breakpoint } from './types'; - -export * from './types'; export interface EllipseAddressProps { address: Address; - ellipseBreakpoint?: Breakpoint; className?: string; } -export const EllipseAddress: React.FC = ({ - className, - address, - ellipseBreakpoint, -}) => { - const styles = useStyles(); - - const truncatedAddress = truncateAddress(address); - - return ( - <> - - {address} - - - - {truncatedAddress} - - - ); -}; +export const EllipseAddress: React.FC = ({ className, address }) => ( + {truncateAddress(address)} +); export default EllipseAddress; diff --git a/apps/evm/src/components/EllipseAddress/styles.ts b/apps/evm/src/components/EllipseAddress/styles.ts deleted file mode 100644 index 85d809befc..0000000000 --- a/apps/evm/src/components/EllipseAddress/styles.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { css } from '@emotion/react'; -import { useTheme } from '@mui/material'; - -import type { Breakpoint } from './types'; - -export const useStyles = () => { - const theme = useTheme(); - - return { - getAddress: ({ ellipseBreakpoint }: { ellipseBreakpoint?: Breakpoint }) => css` - ${ - ellipseBreakpoint - ? css` - ${theme.breakpoints.down(ellipseBreakpoint)} { - display: none; - } - ` - : css` - display: none; - ` - } - `, - getFormattedAddress: ({ ellipseBreakpoint }: { ellipseBreakpoint?: Breakpoint }) => css` - ${ - ellipseBreakpoint && - css` - display: none; - - ${theme.breakpoints.down(ellipseBreakpoint)} { - display: block; - } - ` - } - `, - }; -}; diff --git a/apps/evm/src/components/EllipseAddress/types.ts b/apps/evm/src/components/EllipseAddress/types.ts deleted file mode 100644 index 74d7c05135..0000000000 --- a/apps/evm/src/components/EllipseAddress/types.ts +++ /dev/null @@ -1,3 +0,0 @@ -import type { BREAKPOINTS } from 'App/MuiThemeProvider/muiTheme'; - -export type Breakpoint = keyof typeof BREAKPOINTS.values; diff --git a/apps/evm/src/components/LabeledProgressBar/index.stories.tsx b/apps/evm/src/components/LabeledProgressBar/index.stories.tsx index 3139b3af9f..6cc46344df 100644 --- a/apps/evm/src/components/LabeledProgressBar/index.stories.tsx +++ b/apps/evm/src/components/LabeledProgressBar/index.stories.tsx @@ -13,10 +13,8 @@ export const Default = () => ( whiteLeftText="$5000" greyRightText="Minimum:" whiteRightText="$100" - value={50} - step={5} + progressBars={[{ value: 50 }]} marks={[{ value: 75 }]} - ariaLabel="Storybook slider" min={0} max={100} /> diff --git a/apps/evm/src/components/MarketInfo/index.tsx b/apps/evm/src/components/MarketInfo/index.tsx new file mode 100644 index 0000000000..ab05f78530 --- /dev/null +++ b/apps/evm/src/components/MarketInfo/index.tsx @@ -0,0 +1,27 @@ +import { + LabeledInlineContent, + type LabeledInlineContentProps, + MarketCard, + type MarketCardProps, +} from 'components'; + +export interface MarketInfoProps extends Omit { + items: LabeledInlineContentProps[]; +} + +export const MarketInfo: React.FC = ({ items, ...otherProps }) => ( + +
    + {items.map(({ label, children, ...otherItemProps }) => ( +
  • + + {children} + +
  • + ))} +
+
+); diff --git a/apps/evm/src/components/MarketPageGrid/index.tsx b/apps/evm/src/components/MarketPageGrid/index.tsx index a5757d1abb..a8c7b9ce81 100644 --- a/apps/evm/src/components/MarketPageGrid/index.tsx +++ b/apps/evm/src/components/MarketPageGrid/index.tsx @@ -5,7 +5,7 @@ export interface MarketPageGridProps { export const MarketPageGrid: React.FC = ({ form, content }) => (
-
+
{form}
diff --git a/apps/evm/src/components/Modal/index.tsx b/apps/evm/src/components/Modal/index.tsx index 733641bb86..82e1c63519 100644 --- a/apps/evm/src/components/Modal/index.tsx +++ b/apps/evm/src/components/Modal/index.tsx @@ -16,6 +16,7 @@ import { useModalStyles } from './styles'; export interface ModalProps extends Omit { className?: string; + backdropClassName?: string; buttonClassName?: string; isOpen: boolean; handleClose?: () => void; @@ -27,6 +28,7 @@ export interface ModalProps extends Omit = ({ className, + backdropClassName, buttonClassName, children, componentsProps, @@ -83,7 +85,7 @@ export const Modal: React.FC = ({ return ( <> - {isOpen && } + {isOpen && } = ({ className: cn('pointer-events-none', componentsProps?.root?.className), style: { zIndex: 99999, + ...componentsProps?.root?.style, }, }, }} diff --git a/apps/evm/src/components/ProgressBar/__tests__/index.spec.tsx b/apps/evm/src/components/ProgressBar/__tests__/index.spec.tsx new file mode 100644 index 0000000000..843a41fbee --- /dev/null +++ b/apps/evm/src/components/ProgressBar/__tests__/index.spec.tsx @@ -0,0 +1,84 @@ +import { render } from '@testing-library/react'; + +import { ProgressBar } from '..'; + +vi.mock('../../Tooltip', () => ({ + Tooltip: ({ children, content }: { children: React.ReactNode; content: React.ReactNode }) => ( +
+ {content} + {children} +
+ ), +})); + +describe('ProgressBar', () => { + it('renders a fill relative to the provided range', () => { + const { container } = render( + , + ); + + const rail = container.firstElementChild; + const fill = rail?.querySelector('div'); + + expect(rail).toHaveClass('custom-class', 'bg-lightGrey'); + expect(fill).toHaveClass('bg-green'); + expect(fill).toHaveStyle({ width: '50%' }); + }); + + it('renders multiple fills in array order with custom classes', () => { + const { container } = render( + , + ); + + const fills = container.firstElementChild?.querySelectorAll('div'); + + expect(fills).toHaveLength(2); + expect(fills?.[0]).toHaveClass('bg-yellow'); + expect(fills?.[0]).toHaveStyle({ width: '75%' }); + expect(fills?.[1]).toHaveClass('bg-blue'); + expect(fills?.[1]).toHaveStyle({ width: '25%' }); + }); + + it('renders widths without clamping them to the provided range', () => { + const { container } = render(); + + const fills = container.firstElementChild?.querySelectorAll('div'); + + expect(fills?.[0]).toHaveStyle({ width: '150%' }); + }); + + it('renders positioned marks without changing the fill color', () => { + const { container } = render( + , + ); + + const fill = container.firstElementChild?.querySelector('div'); + const marks = container.querySelectorAll('span'); + + expect(fill).toHaveClass('bg-yellow'); + expect(marks[0]).toHaveClass('bg-red'); + expect(marks[0]).toHaveStyle({ left: '75%' }); + expect(marks[1]).toHaveClass('bg-white'); + expect(marks[1]).toHaveStyle({ left: '25%' }); + }); + + it('wraps the rail in a tooltip when content is provided', () => { + const { getByText } = render( + , + ); + + expect(getByText('Details')).toBeInTheDocument(); + }); +}); diff --git a/apps/evm/src/components/ProgressBar/getProgressBarValuePercentage/__tests__/index.spec.ts b/apps/evm/src/components/ProgressBar/getProgressBarValuePercentage/__tests__/index.spec.ts new file mode 100644 index 0000000000..f4793d832b --- /dev/null +++ b/apps/evm/src/components/ProgressBar/getProgressBarValuePercentage/__tests__/index.spec.ts @@ -0,0 +1,16 @@ +import { getProgressBarValuePercentage } from '..'; + +describe('getProgressBarValuePercentage', () => { + it('returns the value percentage of the range', () => { + expect(getProgressBarValuePercentage({ value: 30, min: 10, max: 50 })).toBe(50); + }); + + it('does not clamp values to the range', () => { + expect(getProgressBarValuePercentage({ value: -10, min: 0, max: 100 })).toBe(-10); + expect(getProgressBarValuePercentage({ value: 150, min: 0, max: 100 })).toBe(150); + }); + + it('returns zero percent for an invalid range', () => { + expect(getProgressBarValuePercentage({ value: 75, min: 50, max: 50 })).toBe(0); + }); +}); diff --git a/apps/evm/src/components/ProgressBar/getProgressBarValuePercentage/index.ts b/apps/evm/src/components/ProgressBar/getProgressBarValuePercentage/index.ts new file mode 100644 index 0000000000..b91e2c9422 --- /dev/null +++ b/apps/evm/src/components/ProgressBar/getProgressBarValuePercentage/index.ts @@ -0,0 +1,17 @@ +export interface GetProgressBarValuePercentageInput { + value: number; + min: number; + max: number; +} + +export const getProgressBarValuePercentage = ({ + value, + min, + max, +}: GetProgressBarValuePercentageInput) => { + if (max <= min) { + return 0; + } + + return ((value - min) / (max - min)) * 100; +}; diff --git a/apps/evm/src/components/ProgressBar/index.stories.tsx b/apps/evm/src/components/ProgressBar/index.stories.tsx index 02189ed74e..e740726012 100644 --- a/apps/evm/src/components/ProgressBar/index.stories.tsx +++ b/apps/evm/src/components/ProgressBar/index.stories.tsx @@ -8,47 +8,35 @@ export default { } as Meta; export const ValidProgressBar = () => ( - + ); export const ProgressBarWithCustomProgressColor = () => ( ); -export const ValidProgressBarWithTooltip = () => ( +export const MultipleProgressBars = () => ( ); -export const InvalidProgressBar = () => ( +export const ValidProgressBarWithTooltip = () => ( ); diff --git a/apps/evm/src/components/ProgressBar/index.tsx b/apps/evm/src/components/ProgressBar/index.tsx index 8dc969ec35..7a5c41612a 100644 --- a/apps/evm/src/components/ProgressBar/index.tsx +++ b/apps/evm/src/components/ProgressBar/index.tsx @@ -1,113 +1,68 @@ -import Box from '@mui/material/Box'; -import MaterialSlider from '@mui/material/Slider'; -import type { SliderTypeMap } from '@mui/material/Slider/Slider'; -import { useCallback } from 'react'; - -import { PALETTE } from 'App/MuiThemeProvider/muiTheme'; +import { cn } from '@venusprotocol/ui'; import { Tooltip, type TooltipProps } from '../Tooltip'; -import { useStyles } from './styles'; +import { getProgressBarValuePercentage } from './getProgressBarValuePercentage'; + +export interface ProgressBarData { + value: number; + className?: string; +} export interface ProgressBarMark { value: number; - color?: string; + className?: string; } export interface ProgressBarProps { - value: number; - secondaryValue?: number; + progressBars: ProgressBarData[]; marks?: ProgressBarMark[]; - step: number; - ariaLabel: string; min: number; max: number; tooltip?: TooltipProps['content']; className?: string; - progressBarColor?: string; } export const ProgressBar = ({ - value, + progressBars, marks, - step, - ariaLabel, min, max, tooltip, className, - progressBarColor = PALETTE.interactive.success, }: ProgressBarProps) => { - const safeValue = value < max ? value : max; - - const sliderMarks = marks?.map(m => ({ value: m.value })); - const hasMarks = !!marks && marks.length > 0; - - const styles = useStyles({ - over: hasMarks ? safeValue > marks[0].value : false, - progressBarColor, - }); - - const renderMark = useCallback( - ( - props?: NonNullable['mark'] & { - className?: string; - style?: React.CSSProperties; - 'data-index'?: number; - }, - ) => { - const index = props?.['data-index'] ?? 0; - const markColor = marks?.[index]?.color; - - return ( - - . - - ); - }, - [styles.mark, styles.tooltipHelper, marks], - ); + const dom = ( +
+ {progressBars.map((progressBar, index) => { + const valuePercentage = getProgressBarValuePercentage({ + value: progressBar.value, + min, + max, + }); - const renderTrack = useCallback( - ( - props?: NonNullable['track'] & { - className?: string; - style?: React.CSSProperties; - }, - ) => { - const primaryRail = ( - - - - ); + return ( +
+ ); + })} - return <>{primaryRail}; - }, - [styles], - ); + {marks?.map((mark, index) => { + const valuePercentage = getProgressBarValuePercentage({ value: mark.value, min, max }); - const dom = ( - + return ( + + ); + })} +
); if (tooltip) { diff --git a/apps/evm/src/components/ProgressBar/styles.ts b/apps/evm/src/components/ProgressBar/styles.ts deleted file mode 100644 index ba400b8fa1..0000000000 --- a/apps/evm/src/components/ProgressBar/styles.ts +++ /dev/null @@ -1,63 +0,0 @@ -import { css } from '@emotion/react'; -import { useTheme } from '@mui/material'; - -export const useStyles = ({ - over, - progressBarColor, -}: { - over: boolean; - progressBarColor: string; -}) => { - const theme = useTheme(); - - return { - slider: css` - z-index: 0; - display: block; - color: ${over ? theme.palette.interactive.error50 : progressBarColor}; - background-color: ${theme.palette.background.default}; - height: ${theme.spacing(2)}; - padding: 0 !important; - &.Mui-disabled { - color: ${over ? theme.palette.interactive.error50 : progressBarColor}; - } - .MuiSlider-track { - background-color: ${over ? theme.palette.interactive.error50 : progressBarColor}; - height: ${theme.spacing(2)}; - border-radius: ${theme.spacing(1)}; - } - .MuiSlider-rail { - height: ${theme.spacing(2)}; - color: ${theme.palette.secondary.light}; - opacity: 1; - } - .MuiSlider-thumb { - box-shadow: none; - width: 0; - height: 0; - } - `, - trackWrapper: css` - position: relative; - z-index: 20; - `, - mark: css` - position: absolute; - border-radius: 1px; - top: 50%; - transform: translate(-1px, -50%); - z-index: 30; - background-color: currentcolor; - - /* theme styles */ - width: ${theme.spacing(1)}; - height: ${theme.spacing(2)}; - color: ${theme.palette.interactive.error}; - `, - tooltipHelper: css` - visibility: hidden; - `, - }; -}; - -export default useStyles; diff --git a/apps/evm/src/components/Select/index.tsx b/apps/evm/src/components/Select/index.tsx index 1d6df9a3cd..56750ca668 100644 --- a/apps/evm/src/components/Select/index.tsx +++ b/apps/evm/src/components/Select/index.tsx @@ -1,11 +1,9 @@ +import { Button, cn } from '@venusprotocol/ui'; import { forwardRef, useCallback, useMemo } from 'react'; import { Dropdown } from '../Dropdown'; - -import { Button } from '@venusprotocol/ui'; -import { cn } from '@venusprotocol/ui'; -import { renderLabel } from '../Dropdown/renderLabel'; import { Icon } from '../Icon'; +import { renderLabel } from './renderLabel'; import type { SelectProps } from './types'; export * from './types'; diff --git a/apps/evm/src/components/Dropdown/renderLabel.tsx b/apps/evm/src/components/Select/renderLabel/index.ts similarity index 100% rename from apps/evm/src/components/Dropdown/renderLabel.tsx rename to apps/evm/src/components/Select/renderLabel/index.ts diff --git a/apps/evm/src/components/TokenGroup/index.tsx b/apps/evm/src/components/TokenGroup/index.tsx index 1602718890..b3e5ac6cc3 100644 --- a/apps/evm/src/components/TokenGroup/index.tsx +++ b/apps/evm/src/components/TokenGroup/index.tsx @@ -24,7 +24,7 @@ export const TokenGroup: React.FC = ({ const filteredTokens = limit > 0 ? sanitizedTokens.slice(0, limit) : tokens; return ( -
+
{filteredTokens.map((token, index) => ( 0 && '-ml-1')} diff --git a/apps/evm/src/components/index.ts b/apps/evm/src/components/index.ts index 32471c7018..8673d58fcf 100644 --- a/apps/evm/src/components/index.ts +++ b/apps/evm/src/components/index.ts @@ -8,6 +8,7 @@ export * from './CapProgressCircle'; export * from './Checkbox'; export * from './Chip'; export * from './Countdown'; +export * from './CompactTableCardList'; export * from './Delimiter'; export * from './Dropdown'; export * from './Username'; @@ -83,3 +84,4 @@ export * from './EModeButton'; export * from './MarketPageGrid'; export * from './MarketHistoryCard'; export * from './MarketCard'; +export * from './MarketInfo'; diff --git a/apps/evm/src/containers/AccountPoolHealth/__tests__/__snapshots__/index.spec.tsx.snap b/apps/evm/src/containers/AccountPoolHealth/__tests__/__snapshots__/index.spec.tsx.snap index 313cbf068c..cf70165130 100644 --- a/apps/evm/src/containers/AccountPoolHealth/__tests__/__snapshots__/index.spec.tsx.snap +++ b/apps/evm/src/containers/AccountPoolHealth/__tests__/__snapshots__/index.spec.tsx.snap @@ -1,5 +1,5 @@ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html -exports[`AccountPoolHealth > renders correctly 1`] = `"Borrowed:$123.33LT:Liquidation threshold:$1.92K..Health factor15.62Healthy"`; +exports[`AccountPoolHealth > renders correctly 1`] = `"Borrowed:$123.33LT:Liquidation threshold:$1.92KHealth factor15.62Healthy"`; -exports[`AccountPoolHealth > renders correctly when passing a simulated pool 1`] = `"Borrowed:$123.33LT:Liquidation threshold:$1.92K..Health factor15.6215.62Healthy"`; +exports[`AccountPoolHealth > renders correctly when passing a simulated pool 1`] = `"Borrowed:$123.33LT:Liquidation threshold:$1.92KHealth factor15.6215.62Healthy"`; diff --git a/apps/evm/src/containers/ChainExplorerLink/index.tsx b/apps/evm/src/containers/ChainExplorerLink/index.tsx index afa832d902..f6431a174e 100644 --- a/apps/evm/src/containers/ChainExplorerLink/index.tsx +++ b/apps/evm/src/containers/ChainExplorerLink/index.tsx @@ -1,7 +1,7 @@ import { useMemo } from 'react'; import { cn } from '@venusprotocol/ui'; -import { type Breakpoint, EllipseAddress } from 'components/EllipseAddress'; +import { EllipseAddress } from 'components/EllipseAddress'; import { Icon } from 'components/Icon'; import { Link } from 'containers/Link'; import { useTranslation } from 'libs/translations'; @@ -13,7 +13,6 @@ import type { Address } from 'viem'; export interface ChainExplorerLinkProps { hash: string; chainId: ChainId; - ellipseBreakpoint?: Breakpoint; urlType?: UrlType; className?: string; text?: string; @@ -25,7 +24,6 @@ export const ChainExplorerLink: React.FC = ({ className, urlType, text, - ellipseBreakpoint, }) => { const { t } = useTranslation(); @@ -41,12 +39,12 @@ export const ChainExplorerLink: React.FC = ({ return t('chainExplorerLink.content', { domainName }); } - if (text && ellipseBreakpoint) { - return ; + if (text) { + return ; } return text; - }, [url, text, ellipseBreakpoint, t]); + }, [url, text, t]); return (
diff --git a/apps/evm/src/containers/Layout/Header/LiquidityHubInfo/index.tsx b/apps/evm/src/containers/Layout/Header/LiquidityHubInfo/index.tsx index a002a2acbe..be5d2e2450 100644 --- a/apps/evm/src/containers/Layout/Header/LiquidityHubInfo/index.tsx +++ b/apps/evm/src/containers/Layout/Header/LiquidityHubInfo/index.tsx @@ -5,8 +5,7 @@ import { useGetLiquidityHub } from 'clients/api'; import { type CellProps, TokenGroup } from 'components'; import { NULL_ADDRESS } from 'constants/address'; import { useTranslation } from 'libs/translations'; -import type { Token } from 'types'; -import { formatCentsToReadableValue } from 'utilities'; +import { formatCentsToReadableValue, getLiquidityHubCollateralTokens } from 'utilities'; import { TokenInfo } from '../TokenInfo'; export const LiquidityHubInfo = () => { @@ -21,10 +20,9 @@ export const LiquidityHubInfo = () => { }); const liquidityHub = getLiquidityHubData?.liquidityHub; - const collateralTokens = (liquidityHub?.sources ?? []).reduce( - (acc, source) => acc.concat(source.collateralTokens), - [], - ); + const collateralTokens = getLiquidityHubCollateralTokens({ + yieldGroups: liquidityHub?.yieldGroups ?? [], + }); const cells: CellProps[] = [ { diff --git a/apps/evm/src/containers/Layout/Header/usePathNodes/__tests__/__snapshots__/index.spec.tsx.snap b/apps/evm/src/containers/Layout/Header/usePathNodes/__tests__/__snapshots__/index.spec.tsx.snap index 667c19e7f3..1919432ae8 100644 --- a/apps/evm/src/containers/Layout/Header/usePathNodes/__tests__/__snapshots__/index.spec.tsx.snap +++ b/apps/evm/src/containers/Layout/Header/usePathNodes/__tests__/__snapshots__/index.spec.tsx.snap @@ -47,7 +47,6 @@ exports[`usePathNodes > outputs the right DOM based on the current path: /govern > { showProvider={false} showTooltip={false} showCopyAddress - ellipseBreakpoint="2xl" />
); diff --git a/apps/evm/src/containers/Layout/NavBar/ConnectButton/AccountModal/__tests__/__snapshots__/index.spec.tsx.snap b/apps/evm/src/containers/Layout/NavBar/ConnectButton/AccountModal/__tests__/__snapshots__/index.spec.tsx.snap index 2b31baf0d2..01f1c0ff7e 100644 --- a/apps/evm/src/containers/Layout/NavBar/ConnectButton/AccountModal/__tests__/__snapshots__/index.spec.tsx.snap +++ b/apps/evm/src/containers/Layout/NavBar/ConnectButton/AccountModal/__tests__/__snapshots__/index.spec.tsx.snap @@ -1,5 +1,5 @@ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html -exports[`AccountModal > renders the blur layer into the body backdrop portal and closes when the outside layer is clicked 1`] = `"0x3d759121234cd36F8124C21aFe1c6852d2bEd8480x3d...d848Net worth-Today's change-30D absolute performance-Language (beta)EnglishJoin private Telegram groupDisconnect"`; +exports[`AccountModal > renders the blur layer into the body backdrop portal and closes when the outside layer is clicked 1`] = `"0x3d...d848Net worth-Today's change-30D absolute performance-Language (beta)EnglishJoin private Telegram groupDisconnect"`; -exports[`AccountModal > renders the connected account details, settings, and action links without the account overview graph 1`] = `"0x3d759121234cd36F8124C21aFe1c6852d2bEd8480x3d...d848Net worth-Today's change-30D absolute performance-Language (beta)EnglishJoin private Telegram groupDisconnect"`; +exports[`AccountModal > renders the connected account details, settings, and action links without the account overview graph 1`] = `"0x3d...d848Net worth-Today's change-30D absolute performance-Language (beta)EnglishJoin private Telegram groupDisconnect"`; diff --git a/apps/evm/src/containers/LiquidityHubForm/Form/index.tsx b/apps/evm/src/containers/LiquidityHubForm/Form/index.tsx index 78bf23d497..b3f4cbc6b8 100644 --- a/apps/evm/src/containers/LiquidityHubForm/Form/index.tsx +++ b/apps/evm/src/containers/LiquidityHubForm/Form/index.tsx @@ -189,18 +189,22 @@ export const Form: React.FC = ({ renderType={isUserConnected ? 'accordion' : 'block'} /> - + {isUserConnected && ( + <> + - {pool && - shouldShowAccountHealth({ - pool, - simulatedPool, - }) && } + {pool && + shouldShowAccountHealth({ + pool, + simulatedPool, + }) && } - + + + )} { justify-content: flex-start; } `, - percentOfLimitProgressBar: css` - width: ${theme.spacing(13)}; - `, white: css` color: ${theme.palette.text.primary}; `, diff --git a/apps/evm/src/containers/MarketTable/useColumns/index.tsx b/apps/evm/src/containers/MarketTable/useColumns/index.tsx index 9779ab860d..12e07ad52d 100644 --- a/apps/evm/src/containers/MarketTable/useColumns/index.tsx +++ b/apps/evm/src/containers/MarketTable/useColumns/index.tsx @@ -368,10 +368,12 @@ export const useColumns = ({
diff --git a/apps/evm/src/containers/PrimeStatusBanner/index.tsx b/apps/evm/src/containers/PrimeStatusBanner/index.tsx index 82a3d6ed21..18bfd6ddc4 100644 --- a/apps/evm/src/containers/PrimeStatusBanner/index.tsx +++ b/apps/evm/src/containers/PrimeStatusBanner/index.tsx @@ -247,9 +247,7 @@ export const PrimeStatusBannerUi: React.FC = ({
diff --git a/apps/evm/src/libs/translations/translations/en.json b/apps/evm/src/libs/translations/translations/en.json index 9607b03da0..d10b2b0f2d 100644 --- a/apps/evm/src/libs/translations/translations/en.json +++ b/apps/evm/src/libs/translations/translations/en.json @@ -128,7 +128,6 @@ } }, "accountHealth": { - "accessibilityLabel": "Representation of the current state of your account", "borrowed": "Borrowed:", "borrowedSpotTooltip": "This borrow value is calculated based on the spot price of the borrowed asset and will be used to determine your account's health status.", "healthFactor": { @@ -886,6 +885,76 @@ "legal": { "lastUpdatedAt": "Last updated: {{ date, textual }}" }, + "liquidityHub": { + "allocationDetails": { + "table": { + "allocationColumn": { + "title": "Alloc." + }, + "averageApyColumn": { + "title": "APY" + }, + "capDollarsColumn": { + "title": "Cap abs.", + "tooltip": "The absolute cap of a yield group", + "unlimited": "Unlimited" + }, + "capPercentageColumn": { + "title": "Cap" + }, + "liquidityColumn": { + "title": "Liquidity" + }, + "sourceColumn": { + "title": "Source" + } + }, + "title": "Allocation details", + "yieldGroup": { + "allocationColumn": { + "title": "Alloc." + }, + "apyColumn": { + "title": "APY" + }, + "collateralColumn": { + "title": "Collateral" + }, + "liquidityColumn": { + "title": "Liquidity" + }, + "lockEndDateColumn": { + "date": "{{date, textualWithTime}}", + "title": "Lock en date" + }, + "nameColumn": { + "title": { + "market": "Market", + "vault": "Vault" + } + } + } + }, + "info": { + "stats": { + "dailySupplyingInterests": "Daily supplying interests", + "exchangeRate": { + "title": "Exchange rate", + "value": "1 {{vhTokenSymbol}} = {{ exchangeRate }} {{ underlyingTokenSymbol }}" + }, + "hubContract": "Hub contract", + "operator": "Operator", + "performanceFee": "Performance fee", + "redeemFee": "Redeem fee", + "riskDisclosures": { + "title": "Risk disclosures", + "value": "By supplying to, withdrawing from, or otherwise interacting with the Venus Liquidity Hub, you acknowledge and agree that:
  • You are interacting with autonomous smart contracts. Venus and its affiliates do not guarantee the performance, security, or availability of the Liquidity Hub or any related software, smart contracts, blockchain networks, or third-party integrations.
  • Supplying assets to the Liquidity Hub does not grant borrowing power and does not constitute a deposit, loan, or investment product. You may not be able to withdraw at all times, and returns, if any, are not guaranteed.
  • You assume all risks associated with your use of the Liquidity Hub and are solely responsible for determining whether it is appropriate for your financial circumstances, risk tolerance, and technical capabilities. You are responsible for conducting your own independent evaluation, including reviewing the relevant source code and on-chain configuration.
  • Venus is a technology provider only and does not provide investment, advisory, fiduciary, or custodial services, and does not manage or hold your assets.
Your interaction with the Liquidity Hub is governed by the Terms of Use." + }, + "vhTokenContract": "{{ vhTokenSymbol }} contract" + }, + "title": "Hub info" + } + }, "liquidityHubForm": { "balanceUpdates": { "corePool": "Core pool", @@ -1400,7 +1469,6 @@ "marketActions": "Open market actions", "notEligibleMessage": "You are currently NOT eligible for sharing the Prime rewards during this cycle. Stake XVS to compete for Prime in the next cycle.", "primeLogoAlt": "Venus Prime logo", - "rewardShareAriaLabel": "Share of total rewards", "title": "Your Prime rewards this cycle" } }, @@ -1416,7 +1484,6 @@ "tooltip": "All {{primeTokenLimit}} Prime tokens are currently claimed. Some may become available if owners no longer meet the eligibility criteria or if we add more." }, "progressBar": { - "ariaLabel": "Your current Prime progression", "label": "Supplied: {{userStakedXvsTokens}} / {{minXvsToStakeForPrimeTokens}}" }, "promotionalTitle": "Boost your earnings by up to {{percentage}}", @@ -2478,7 +2545,6 @@ "searchInput": { "placeholder": "Search proposal" }, - "summaryProgressBar": "Vote Summary Progress Bar - {{voteType}}", "totalLocked": "Total locked", "toVoteYouShould": "To vote you should:", "voteAbstain": "Vote abstain", @@ -2554,11 +2620,6 @@ "readyForExecution": "Ready for execution", "readyForQueuing": "Ready for queuing" }, - "statusCard": { - "ariaLabelAbstain": "votes abstain", - "ariaLabelAgainst": "votes against", - "ariaLabelFor": "votes for" - }, "timeUntilExecutable": "Executable at: {{ date, textualWithTime }}", "timeUntilVotable": "Voting starts at: {{ date, textualWithTime }}", "voteStatus": { diff --git a/apps/evm/src/libs/translations/translations/ja.json b/apps/evm/src/libs/translations/translations/ja.json index 33c914f1c2..05c007884d 100644 --- a/apps/evm/src/libs/translations/translations/ja.json +++ b/apps/evm/src/libs/translations/translations/ja.json @@ -128,7 +128,6 @@ } }, "accountHealth": { - "accessibilityLabel": "現在のアカウント状態の表示", "borrowed": "借入額:", "borrowedSpotTooltip": "この借入額は、借入資産のスポット価格に基づいて計算され、アカウントの健全性を判定するために使用されます。", "healthFactor": { @@ -886,6 +885,76 @@ "legal": { "lastUpdatedAt": "最終更新: {{ date, textual }}" }, + "liquidityHub": { + "allocationDetails": { + "table": { + "allocationColumn": { + "title": "配分" + }, + "averageApyColumn": { + "title": "APY" + }, + "capDollarsColumn": { + "title": "絶対上限", + "tooltip": "利回りグループの絶対上限", + "unlimited": "無制限" + }, + "capPercentageColumn": { + "title": "上限" + }, + "liquidityColumn": { + "title": "流動性" + }, + "sourceColumn": { + "title": "ソース" + } + }, + "title": "配分の詳細", + "yieldGroup": { + "allocationColumn": { + "title": "配分" + }, + "apyColumn": { + "title": "APY" + }, + "collateralColumn": { + "title": "担保" + }, + "liquidityColumn": { + "title": "流動性" + }, + "lockEndDateColumn": { + "date": "{{date, textualWithTime}}", + "title": "ロック終了日" + }, + "nameColumn": { + "title": { + "market": "マーケット", + "vault": "ボールト" + } + } + } + }, + "info": { + "stats": { + "dailySupplyingInterests": "日次供給利息", + "exchangeRate": { + "title": "交換レート", + "value": "1 {{vhTokenSymbol}} = {{ exchangeRate }} {{ underlyingTokenSymbol }}" + }, + "hubContract": "ハブコントラクト", + "operator": "オペレーター", + "performanceFee": "パフォーマンス手数料", + "redeemFee": "償還手数料", + "riskDisclosures": { + "title": "リスク開示", + "value": "Venus流動性ハブに供給、引き出し、またはその他の形で関与することにより、以下を認識し同意したものとみなされます:
  • あなたは自律的に動作するスマートコントラクトとやり取りしています。Venusおよびその関連会社は、流動性ハブまたは関連ソフトウェア、スマートコントラクト、ブロックチェーンネットワーク、第三者連携の性能、安全性、可用性を保証しません。
  • 流動性ハブに資産を供給しても、借入能力は付与されず、預金、貸付、投資商品を構成するものでもありません。常に引き出せるとは限らず、仮にリターンがあっても保証されません。
  • 流動性ハブの利用に伴うすべてのリスクはあなたが負うものとし、それがあなたの財務状況、リスク許容度、技術的能力に照らして適切かどうかを判断する責任はあなたにあります。関連するソースコードやオンチェーン設定の確認を含め、独自に評価を行う責任があります。
  • Venusは技術提供者にすぎず、投資、助言、受託者、またはカストディサービスを提供せず、あなたの資産を管理または保有しません。
流動性ハブとのやり取りには利用規約が適用されます。" + }, + "vhTokenContract": "{{ vhTokenSymbol }} コントラクト" + }, + "title": "ハブ情報" + } + }, "liquidityHubForm": { "balanceUpdates": { "corePool": "コアプール", @@ -1400,7 +1469,6 @@ "marketActions": "マーケット操作を開く", "notEligibleMessage": "今サイクルでは Prime 報酬を分け合う対象ではありません。XVS をステークして次のサイクルで Prime を目指しましょう。", "primeLogoAlt": "Venus Prime ロゴ", - "rewardShareAriaLabel": "報酬総額に占める割合", "title": "今サイクルのあなたの Prime 報酬" } }, @@ -1416,7 +1484,6 @@ "tooltip": "{{primeTokenLimit}}枚のPrimeトークンは現在すべて請求済みです。保有者が条件を満たさなくなるか、追加が行われると利用可能になる場合があります。" }, "progressBar": { - "ariaLabel": "現在のPrime進捗", "label": "供給済み: {{userStakedXvsTokens}} / {{minXvsToStakeForPrimeTokens}}" }, "promotionalTitle": "収益を最大{{percentage}}までブースト", @@ -2476,7 +2543,6 @@ "searchInput": { "placeholder": "提案を検索" }, - "summaryProgressBar": "投票サマリー進捗バー - {{voteType}}", "totalLocked": "総ロック", "toVoteYouShould": "投票するには:", "voteAbstain": "棄権に投票", @@ -2552,11 +2618,6 @@ "readyForExecution": "実行準備完了", "readyForQueuing": "キュー準備完了" }, - "statusCard": { - "ariaLabelAbstain": "棄権票", - "ariaLabelAgainst": "反対票", - "ariaLabelFor": "賛成票" - }, "timeUntilExecutable": "実行可能: {{ date, textualWithTime }}", "timeUntilVotable": "投票開始: {{ date, textualWithTime }}", "voteStatus": { diff --git a/apps/evm/src/libs/translations/translations/th.json b/apps/evm/src/libs/translations/translations/th.json index 840e38abe7..0bc1990fa6 100644 --- a/apps/evm/src/libs/translations/translations/th.json +++ b/apps/evm/src/libs/translations/translations/th.json @@ -128,7 +128,6 @@ } }, "accountHealth": { - "accessibilityLabel": "การแสดงสถานะปัจจุบันของบัญชีของคุณ", "borrowed": "ยอดยืม:", "borrowedSpotTooltip": "มูลค่าการกู้ยืมนี้คำนวณจากราคา spot ของสินทรัพย์ที่กู้ยืม และจะใช้ในการกำหนดสถานะสุขภาพของบัญชีของคุณ", "healthFactor": { @@ -886,6 +885,76 @@ "legal": { "lastUpdatedAt": "อัปเดตล่าสุด: {{ date, textual }}" }, + "liquidityHub": { + "allocationDetails": { + "table": { + "allocationColumn": { + "title": "สัดส่วน" + }, + "averageApyColumn": { + "title": "APY" + }, + "capDollarsColumn": { + "title": "เพดานแบบสัมบูรณ์", + "tooltip": "เพดานแบบสัมบูรณ์ของกลุ่มผลตอบแทน", + "unlimited": "ไม่จำกัด" + }, + "capPercentageColumn": { + "title": "เพดาน" + }, + "liquidityColumn": { + "title": "สภาพคล่อง" + }, + "sourceColumn": { + "title": "แหล่งที่มา" + } + }, + "title": "รายละเอียดการจัดสรร", + "yieldGroup": { + "allocationColumn": { + "title": "สัดส่วน" + }, + "apyColumn": { + "title": "APY" + }, + "collateralColumn": { + "title": "หลักประกัน" + }, + "liquidityColumn": { + "title": "สภาพคล่อง" + }, + "lockEndDateColumn": { + "date": "{{date, textualWithTime}}", + "title": "วันที่สิ้นสุดการล็อก" + }, + "nameColumn": { + "title": { + "market": "ตลาด", + "vault": "คลัง" + } + } + } + }, + "info": { + "stats": { + "dailySupplyingInterests": "ดอกเบี้ยจากการฝากรายวัน", + "exchangeRate": { + "title": "อัตราแลกเปลี่ยน", + "value": "1 {{vhTokenSymbol}} = {{ exchangeRate }} {{ underlyingTokenSymbol }}" + }, + "hubContract": "สัญญาศูนย์", + "operator": "ผู้ดำเนินการ", + "performanceFee": "ค่าธรรมเนียมผลการดำเนินงาน", + "redeemFee": "ค่าธรรมเนียมไถ่ถอน", + "riskDisclosures": { + "title": "การเปิดเผยความเสี่ยง", + "value": "การฝากเข้า ถอนออก หรือมีปฏิสัมพันธ์กับ Venus Liquidity Hub ในลักษณะอื่นใด ถือว่าคุณรับทราบและยอมรับว่า:
  • คุณกำลังโต้ตอบกับสมาร์ตคอนแทรกต์ที่ทำงานอัตโนมัติ Venus และบริษัทในเครือไม่รับประกันประสิทธิภาพ ความปลอดภัย หรือความพร้อมใช้งานของ Liquidity Hub หรือซอฟต์แวร์ สมาร์ตคอนแทรกต์ เครือข่ายบล็อกเชน หรือการผสานรวมกับบุคคลที่สามที่เกี่ยวข้องใด ๆ
  • การฝากสินทรัพย์เข้า Liquidity Hub ไม่ได้มอบอำนาจในการยืม และไม่ถือเป็นเงินฝาก เงินกู้ หรือผลิตภัณฑ์การลงทุน คุณอาจไม่สามารถถอนออกได้ตลอดเวลา และผลตอบแทนหากมีจะไม่ได้รับการรับประกัน
  • คุณยอมรับความเสี่ยงทั้งหมดที่เกี่ยวข้องกับการใช้ Liquidity Hub และเป็นผู้รับผิดชอบแต่เพียงผู้เดียวในการพิจารณาว่าสิ่งนี้เหมาะสมกับฐานะการเงิน ระดับการยอมรับความเสี่ยง และความสามารถทางเทคนิคของคุณหรือไม่ คุณมีหน้าที่ประเมินด้วยตนเองอย่างอิสระ รวมถึงตรวจสอบซอร์สโค้ดที่เกี่ยวข้องและการตั้งค่าบนเชน
  • Venus เป็นเพียงผู้ให้บริการเทคโนโลยีเท่านั้น และไม่ได้ให้บริการด้านการลงทุน ที่ปรึกษา ตัวแทนดูแลผลประโยชน์ หรือการรับฝากทรัพย์สิน รวมทั้งไม่ได้บริหารหรือถือครองทรัพย์สินของคุณ
การใช้งาน Liquidity Hub ของคุณอยู่ภายใต้ ข้อกำหนดการใช้งาน" + }, + "vhTokenContract": "สัญญา {{ vhTokenSymbol }}" + }, + "title": "ข้อมูลศูนย์" + } + }, "liquidityHubForm": { "balanceUpdates": { "corePool": "พูลหลัก", @@ -1400,7 +1469,6 @@ "marketActions": "เปิดการดำเนินการของตลาด", "notEligibleMessage": "คุณยังไม่มีสิทธิ์ร่วมรับรางวัล Prime ในรอบนี้ Stake XVS เพื่อแข่งขันรับ Prime ในรอบถัดไป", "primeLogoAlt": "โลโก้ Venus Prime", - "rewardShareAriaLabel": "สัดส่วนของรางวัลทั้งหมด", "title": "รางวัล Prime ของคุณในรอบนี้" } }, @@ -1416,7 +1484,6 @@ "tooltip": "โทเคน Prime ทั้งหมด {{primeTokenLimit}} โทเคนถูกอ้างสิทธิ์แล้วในขณะนี้ บางส่วนอาจพร้อมใช้งานหากเจ้าของไม่เข้าเกณฑ์อีกต่อไปหรือหากมีการเพิ่มจำนวน" }, "progressBar": { - "ariaLabel": "ความคืบหน้า Prime ปัจจุบันของคุณ", "label": "ฝากแล้ว: {{userStakedXvsTokens}} / {{minXvsToStakeForPrimeTokens}}" }, "promotionalTitle": "เพิ่มรายได้ของคุณสูงสุด {{percentage}}", @@ -2476,7 +2543,6 @@ "searchInput": { "placeholder": "ค้นหาข้อเสนอ" }, - "summaryProgressBar": "แถบสรุปการโหวต - {{voteType}}", "totalLocked": "ยอดล็อกทั้งหมด", "toVoteYouShould": "หากต้องการโหวต คุณควร:", "voteAbstain": "โหวตงดออกเสียง", @@ -2552,11 +2618,6 @@ "readyForExecution": "พร้อมดำเนินการ", "readyForQueuing": "พร้อมเข้าคิว" }, - "statusCard": { - "ariaLabelAbstain": "เสียงงดออกเสียง", - "ariaLabelAgainst": "เสียงคัดค้าน", - "ariaLabelFor": "เสียงเห็นด้วย" - }, "timeUntilExecutable": "ดำเนินการได้ที่: {{ date, textualWithTime }}", "timeUntilVotable": "เริ่มโหวตที่: {{ date, textualWithTime }}", "voteStatus": { diff --git a/apps/evm/src/libs/translations/translations/tr.json b/apps/evm/src/libs/translations/translations/tr.json index 91c706c890..a69fcdfc21 100644 --- a/apps/evm/src/libs/translations/translations/tr.json +++ b/apps/evm/src/libs/translations/translations/tr.json @@ -128,7 +128,6 @@ } }, "accountHealth": { - "accessibilityLabel": "Hesabınızın mevcut durumunun gösterimi", "borrowed": "Borç alınan:", "borrowedSpotTooltip": "Bu borçlanma değeri, borçlanılan varlığın spot fiyatına göre hesaplanır ve hesabınızın sağlık durumunu belirlemek için kullanılır.", "healthFactor": { @@ -886,6 +885,76 @@ "legal": { "lastUpdatedAt": "Son güncelleme: {{ date, textual }}" }, + "liquidityHub": { + "allocationDetails": { + "table": { + "allocationColumn": { + "title": "Tahsis" + }, + "averageApyColumn": { + "title": "APY" + }, + "capDollarsColumn": { + "title": "Mutlak üst sınır", + "tooltip": "Bir getiri grubunun mutlak üst sınırı", + "unlimited": "Sınırsız" + }, + "capPercentageColumn": { + "title": "Üst sınır" + }, + "liquidityColumn": { + "title": "Likidite" + }, + "sourceColumn": { + "title": "Kaynak" + } + }, + "title": "Tahsis ayrıntıları", + "yieldGroup": { + "allocationColumn": { + "title": "Tahsis" + }, + "apyColumn": { + "title": "APY" + }, + "collateralColumn": { + "title": "Teminat" + }, + "liquidityColumn": { + "title": "Likidite" + }, + "lockEndDateColumn": { + "date": "{{date, textualWithTime}}", + "title": "Kilit bitiş tarihi" + }, + "nameColumn": { + "title": { + "market": "Piyasa", + "vault": "Kasa" + } + } + } + }, + "info": { + "stats": { + "dailySupplyingInterests": "Günlük tedarik faizi", + "exchangeRate": { + "title": "Döviz kuru", + "value": "1 {{vhTokenSymbol}} = {{ exchangeRate }} {{ underlyingTokenSymbol }}" + }, + "hubContract": "Merkez sözleşmesi", + "operator": "Operatör", + "performanceFee": "Performans ücreti", + "redeemFee": "İtfa ücreti", + "riskDisclosures": { + "title": "Risk açıklamaları", + "value": "Venus Likidite Merkezi'ne varlık sağlayarak, buradan çekim yaparak veya başka şekillerde etkileşim kurarak aşağıdakileri kabul etmiş ve onaylamış olursunuz:
  • Otonom akıllı sözleşmelerle etkileşim kuruyorsunuz. Venus ve bağlı kuruluşları, Likidite Merkezi'nin veya ilgili herhangi bir yazılımın, akıllı sözleşmenin, blokzincir ağının ya da üçüncü taraf entegrasyonunun performansını, güvenliğini veya kullanılabilirliğini garanti etmez.
  • Likidite Merkezi'ne varlık sağlamak borçlanma gücü vermez ve bir mevduat, kredi veya yatırım ürünü teşkil etmez. Her zaman çekim yapamayabilirsiniz ve varsa getiriler garanti edilmez.
  • Likidite Merkezi kullanımınızla ilişkili tüm riskleri üstlenirsiniz ve bunun mali durumunuz, risk toleransınız ve teknik yetkinlikleriniz açısından uygun olup olmadığını belirlemekten yalnızca siz sorumlusunuz. İlgili kaynak kodunu ve zincir üstü yapılandırmayı incelemek dahil kendi bağımsız değerlendirmenizi yapmakla yükümlüsünüz.
  • Venus yalnızca bir teknoloji sağlayıcısıdır; yatırım, danışmanlık, vekalet veya saklama hizmeti sunmaz ve varlıklarınızı yönetmez ya da elinde tutmaz.
Likidite Merkezi ile etkileşiminiz Kullanım Koşulları'na tabidir." + }, + "vhTokenContract": "{{ vhTokenSymbol }} sözleşmesi" + }, + "title": "Merkez bilgisi" + } + }, "liquidityHubForm": { "balanceUpdates": { "corePool": "Çekirdek havuz", @@ -1400,7 +1469,6 @@ "marketActions": "Piyasa işlemlerini aç", "notEligibleMessage": "Bu döngüde Prime ödüllerini paylaşmaya uygun değilsin. Sonraki döngüde Prime için yarışmak üzere XVS stake et.", "primeLogoAlt": "Venus Prime logosu", - "rewardShareAriaLabel": "Toplam ödüllerdeki pay", "title": "Bu döngüdeki Prime ödülleriniz" } }, @@ -1416,7 +1484,6 @@ "tooltip": "{{primeTokenLimit}} Prime tokenının tamamı şu anda talep edildi. Sahipleri uygunluk kriterlerini karşılamazsa veya daha fazla eklersek bazıları kullanılabilir hale gelebilir." }, "progressBar": { - "ariaLabel": "Mevcut Prime ilerlemeniz", "label": "Sağlanan: {{userStakedXvsTokens}} / {{minXvsToStakeForPrimeTokens}}" }, "promotionalTitle": "Kazancınızı {{percentage}} kadar artırın", @@ -2477,7 +2544,6 @@ "searchInput": { "placeholder": "Teklif ara" }, - "summaryProgressBar": "Oylama Özet İlerleme Çubuğu - {{voteType}}", "totalLocked": "Toplam kilitli", "toVoteYouShould": "Oy vermek için şunları yapmalısınız:", "voteAbstain": "Çekimser oy ver", @@ -2553,11 +2619,6 @@ "readyForExecution": "Yürütmeye hazır", "readyForQueuing": "Kuyruğa almaya hazır" }, - "statusCard": { - "ariaLabelAbstain": "çekimser oylar", - "ariaLabelAgainst": "karşı oylar", - "ariaLabelFor": "lehte oylar" - }, "timeUntilExecutable": "Yürütülebilir: {{ date, textualWithTime }}", "timeUntilVotable": "Oylama başlar: {{ date, textualWithTime }}", "voteStatus": { diff --git a/apps/evm/src/libs/translations/translations/vi.json b/apps/evm/src/libs/translations/translations/vi.json index 9eaac3d0dd..0333af5206 100644 --- a/apps/evm/src/libs/translations/translations/vi.json +++ b/apps/evm/src/libs/translations/translations/vi.json @@ -128,7 +128,6 @@ } }, "accountHealth": { - "accessibilityLabel": "Biểu diễn trạng thái hiện tại của tài khoản của bạn", "borrowed": "Đã vay:", "borrowedSpotTooltip": "Giá trị vay này được tính dựa trên giá spot của tài sản vay và sẽ được sử dụng để xác định tình trạng sức khỏe tài khoản của bạn.", "healthFactor": { @@ -886,6 +885,76 @@ "legal": { "lastUpdatedAt": "Cập nhật lần cuối: {{ date, textual }}" }, + "liquidityHub": { + "allocationDetails": { + "table": { + "allocationColumn": { + "title": "Phân bổ" + }, + "averageApyColumn": { + "title": "APY" + }, + "capDollarsColumn": { + "title": "Trần tuyệt đối", + "tooltip": "Trần tuyệt đối của một nhóm lợi suất", + "unlimited": "Không giới hạn" + }, + "capPercentageColumn": { + "title": "Trần" + }, + "liquidityColumn": { + "title": "Thanh khoản" + }, + "sourceColumn": { + "title": "Nguồn" + } + }, + "title": "Chi tiết phân bổ", + "yieldGroup": { + "allocationColumn": { + "title": "Phân bổ" + }, + "apyColumn": { + "title": "APY" + }, + "collateralColumn": { + "title": "Tài sản thế chấp" + }, + "liquidityColumn": { + "title": "Thanh khoản" + }, + "lockEndDateColumn": { + "date": "{{date, textualWithTime}}", + "title": "Ngày kết thúc khóa" + }, + "nameColumn": { + "title": { + "market": "Thị trường", + "vault": "Kho" + } + } + } + }, + "info": { + "stats": { + "dailySupplyingInterests": "Lãi cung cấp hàng ngày", + "exchangeRate": { + "title": "Tỷ giá hối đoái", + "value": "1 {{vhTokenSymbol}} = {{ exchangeRate }} {{ underlyingTokenSymbol }}" + }, + "hubContract": "Hợp đồng hub", + "operator": "Nhà vận hành", + "performanceFee": "Phí hiệu suất", + "redeemFee": "Phí đổi lại", + "riskDisclosures": { + "title": "Công bố rủi ro", + "value": "Bằng việc cung cấp vào, rút khỏi hoặc tương tác theo cách khác với Venus Liquidity Hub, bạn xác nhận và đồng ý rằng:
  • Bạn đang tương tác với các hợp đồng thông minh tự vận hành. Venus và các bên liên kết không bảo đảm về hiệu suất, tính bảo mật hoặc khả dụng của Liquidity Hub hay bất kỳ phần mềm, hợp đồng thông minh, mạng blockchain hoặc tích hợp bên thứ ba liên quan nào.
  • Việc cung cấp tài sản vào Liquidity Hub không mang lại năng lực vay và không cấu thành sản phẩm tiền gửi, khoản vay hay đầu tư. Bạn có thể không rút được vào mọi thời điểm và lợi nhuận, nếu có, không được bảo đảm.
  • Bạn tự chịu mọi rủi ro liên quan đến việc sử dụng Liquidity Hub và hoàn toàn chịu trách nhiệm xác định liệu nó có phù hợp với hoàn cảnh tài chính, mức chấp nhận rủi ro và năng lực kỹ thuật của mình hay không. Bạn có trách nhiệm tự thực hiện đánh giá độc lập, bao gồm việc xem xét mã nguồn liên quan và cấu hình on-chain.
  • Venus chỉ là nhà cung cấp công nghệ, không cung cấp dịch vụ đầu tư, tư vấn, ủy thác hay lưu ký, đồng thời không quản lý hoặc nắm giữ tài sản của bạn.
Tương tác của bạn với Liquidity Hub được điều chỉnh bởi Điều khoản sử dụng." + }, + "vhTokenContract": "Hợp đồng {{ vhTokenSymbol }}" + }, + "title": "Thông tin hub" + } + }, "liquidityHubForm": { "balanceUpdates": { "corePool": "Pool cốt lõi", @@ -1400,7 +1469,6 @@ "marketActions": "Mở thao tác thị trường", "notEligibleMessage": "Bạn hiện KHÔNG đủ điều kiện chia sẻ phần thưởng Prime trong chu kỳ này. Stake XVS để cạnh tranh Prime cho chu kỳ tiếp theo.", "primeLogoAlt": "Logo Venus Prime", - "rewardShareAriaLabel": "Tỷ lệ trên tổng phần thưởng", "title": "Phần thưởng Prime của bạn trong chu kỳ này" } }, @@ -1416,7 +1484,6 @@ "tooltip": "Tất cả {{primeTokenLimit}} token Prime hiện đã được nhận. Một số có thể khả dụng nếu chủ sở hữu không còn đủ điều kiện hoặc nếu chúng tôi thêm số lượng." }, "progressBar": { - "ariaLabel": "Tiến trình Prime hiện tại của bạn", "label": "Đã cung cấp: {{userStakedXvsTokens}} / {{minXvsToStakeForPrimeTokens}}" }, "promotionalTitle": "Tăng thu nhập lên đến {{percentage}}", @@ -2476,7 +2543,6 @@ "searchInput": { "placeholder": "Tìm đề xuất" }, - "summaryProgressBar": "Thanh tiến độ tóm tắt bỏ phiếu - {{voteType}}", "totalLocked": "Tổng khóa", "toVoteYouShould": "Để bỏ phiếu bạn nên:", "voteAbstain": "Bỏ phiếu trắng", @@ -2552,11 +2618,6 @@ "readyForExecution": "Sẵn sàng thực thi", "readyForQueuing": "Sẵn sàng xếp hàng" }, - "statusCard": { - "ariaLabelAbstain": "phiếu bỏ phiếu trắng", - "ariaLabelAgainst": "phiếu phản đối", - "ariaLabelFor": "phiếu ủng hộ" - }, "timeUntilExecutable": "Thực thi vào: {{ date, textualWithTime }}", "timeUntilVotable": "Bỏ phiếu bắt đầu: {{ date, textualWithTime }}", "voteStatus": { diff --git a/apps/evm/src/libs/translations/translations/zh-Hans.json b/apps/evm/src/libs/translations/translations/zh-Hans.json index da6e091aa1..3c7903b343 100644 --- a/apps/evm/src/libs/translations/translations/zh-Hans.json +++ b/apps/evm/src/libs/translations/translations/zh-Hans.json @@ -128,7 +128,6 @@ } }, "accountHealth": { - "accessibilityLabel": "当前账户状态的表示", "borrowed": "已借:", "borrowedSpotTooltip": "此借款价值基于借入资产的现货价格计算,将用于确定您账户的健康状态。", "healthFactor": { @@ -886,6 +885,76 @@ "legal": { "lastUpdatedAt": "最后更新:{{ date, textual }}" }, + "liquidityHub": { + "allocationDetails": { + "table": { + "allocationColumn": { + "title": "配比" + }, + "averageApyColumn": { + "title": "APY" + }, + "capDollarsColumn": { + "title": "绝对上限", + "tooltip": "收益组的绝对上限", + "unlimited": "无限" + }, + "capPercentageColumn": { + "title": "上限" + }, + "liquidityColumn": { + "title": "流动性" + }, + "sourceColumn": { + "title": "来源" + } + }, + "title": "分配详情", + "yieldGroup": { + "allocationColumn": { + "title": "配比" + }, + "apyColumn": { + "title": "APY" + }, + "collateralColumn": { + "title": "抵押品" + }, + "liquidityColumn": { + "title": "流动性" + }, + "lockEndDateColumn": { + "date": "{{date, textualWithTime}}", + "title": "锁定结束日期" + }, + "nameColumn": { + "title": { + "market": "市场", + "vault": "金库" + } + } + } + }, + "info": { + "stats": { + "dailySupplyingInterests": "每日供应利息", + "exchangeRate": { + "title": "汇率", + "value": "1 {{vhTokenSymbol}} = {{ exchangeRate }} {{ underlyingTokenSymbol }}" + }, + "hubContract": "中心合约", + "operator": "运营方", + "performanceFee": "绩效费", + "redeemFee": "赎回费", + "riskDisclosures": { + "title": "风险披露", + "value": "当您向 Venus 流动性中心供应资产、从中提取资产或以其他方式与其交互时,即表示您确认并同意:
  • 您正在与自治运行的智能合约进行交互。Venus 及其关联方不保证流动性中心或任何相关软件、智能合约、区块链网络或第三方集成的性能、安全性或可用性。
  • 向流动性中心供应资产不会赋予借款能力,也不构成存款、贷款或投资产品。您可能无法随时提取资产,且任何回报(如有)均不作保证。
  • 您承担与使用流动性中心相关的全部风险,并且仅由您自行判断其是否适合您的财务状况、风险承受能力和技术能力。您有责任自行进行独立评估,包括审查相关源代码和链上配置。
  • Venus 仅为技术提供方,不提供投资、顾问、受托或托管服务,也不管理或持有您的资产。
您与流动性中心的交互受 使用条款 约束。" + }, + "vhTokenContract": "{{ vhTokenSymbol }} 合约" + }, + "title": "中心信息" + } + }, "liquidityHubForm": { "balanceUpdates": { "corePool": "核心池", @@ -1400,7 +1469,6 @@ "marketActions": "打开市场操作", "notEligibleMessage": "你在本周期暂不具备分享 Prime 奖励的资格。质押 XVS 以在下一周期竞争 Prime。", "primeLogoAlt": "Venus Prime 标志", - "rewardShareAriaLabel": "占总奖励的比例", "title": "你本周期的 Prime 奖励" } }, @@ -1416,7 +1484,6 @@ "tooltip": "目前所有 {{primeTokenLimit}} 个 Prime 代币均已被领取。如果持有人不再符合资格或我们增加数量,可能会有部分可用。" }, "progressBar": { - "ariaLabel": "你的当前 Prime 进度", "label": "已供应:{{userStakedXvsTokens}} / {{minXvsToStakeForPrimeTokens}}" }, "promotionalTitle": "将收益提升高达 {{percentage}}", @@ -2476,7 +2543,6 @@ "searchInput": { "placeholder": "搜索提案" }, - "summaryProgressBar": "投票总结进度条 - {{voteType}}", "totalLocked": "总锁定", "toVoteYouShould": "投票前你应:", "voteAbstain": "投票弃权", @@ -2552,11 +2618,6 @@ "readyForExecution": "准备执行", "readyForQueuing": "准备排队" }, - "statusCard": { - "ariaLabelAbstain": "弃权票", - "ariaLabelAgainst": "反对票", - "ariaLabelFor": "赞成票" - }, "timeUntilExecutable": "可执行于:{{ date, textualWithTime }}", "timeUntilVotable": "投票开始:{{ date, textualWithTime }}", "voteStatus": { diff --git a/apps/evm/src/libs/translations/translations/zh-Hant.json b/apps/evm/src/libs/translations/translations/zh-Hant.json index 62b043361c..ccf7690ce4 100644 --- a/apps/evm/src/libs/translations/translations/zh-Hant.json +++ b/apps/evm/src/libs/translations/translations/zh-Hant.json @@ -128,7 +128,6 @@ } }, "accountHealth": { - "accessibilityLabel": "當前賬戶狀態的表示", "borrowed": "已借:", "borrowedSpotTooltip": "此借款價值基於借入資產的現貨價格計算,將用於確定您帳戶的健康狀態。", "healthFactor": { @@ -886,6 +885,76 @@ "legal": { "lastUpdatedAt": "最後更新:{{ date, textual }}" }, + "liquidityHub": { + "allocationDetails": { + "table": { + "allocationColumn": { + "title": "配比" + }, + "averageApyColumn": { + "title": "APY" + }, + "capDollarsColumn": { + "title": "絕對上限", + "tooltip": "收益組的絕對上限", + "unlimited": "無上限" + }, + "capPercentageColumn": { + "title": "上限" + }, + "liquidityColumn": { + "title": "流動性" + }, + "sourceColumn": { + "title": "來源" + } + }, + "title": "分配詳情", + "yieldGroup": { + "allocationColumn": { + "title": "配比" + }, + "apyColumn": { + "title": "APY" + }, + "collateralColumn": { + "title": "抵押品" + }, + "liquidityColumn": { + "title": "流動性" + }, + "lockEndDateColumn": { + "date": "{{date, textualWithTime}}", + "title": "鎖定結束日期" + }, + "nameColumn": { + "title": { + "market": "市場", + "vault": "金庫" + } + } + } + }, + "info": { + "stats": { + "dailySupplyingInterests": "每日供應利息", + "exchangeRate": { + "title": "匯率", + "value": "1 {{vhTokenSymbol}} = {{ exchangeRate }} {{ underlyingTokenSymbol }}" + }, + "hubContract": "中心合約", + "operator": "營運方", + "performanceFee": "績效費", + "redeemFee": "贖回費", + "riskDisclosures": { + "title": "風險披露", + "value": "當您向 Venus 流動性中心供應資產、從中提取資產或以其他方式與其互動時,即表示您確認並同意:
  • 您正在與自治運行的智能合約進行互動。Venus 及其關聯方不保證流動性中心或任何相關軟體、智能合約、區塊鏈網路或第三方整合的性能、安全性或可用性。
  • 向流動性中心供應資產不會賦予借款能力,也不構成存款、貸款或投資產品。您可能無法隨時提取資產,且任何回報(如有)均不作保證。
  • 您承擔與使用流動性中心相關的全部風險,並且僅由您自行判斷其是否適合您的財務狀況、風險承受能力和技術能力。您有責任自行進行獨立評估,包括審查相關原始碼和鏈上配置。
  • Venus 僅為技術提供方,不提供投資、顧問、受託或託管服務,也不管理或持有您的資產。
您與流動性中心的互動受 使用條款 約束。" + }, + "vhTokenContract": "{{ vhTokenSymbol }} 合約" + }, + "title": "中心資訊" + } + }, "liquidityHubForm": { "balanceUpdates": { "corePool": "核心池", @@ -1400,7 +1469,6 @@ "marketActions": "開啟市場操作", "notEligibleMessage": "你在本週期暫不具備分享 Prime 獎勵的資格。質押 XVS 以在下一週期競爭 Prime。", "primeLogoAlt": "Venus Prime 標誌", - "rewardShareAriaLabel": "佔總獎勵的比例", "title": "你本週期的 Prime 獎勵" } }, @@ -1416,7 +1484,6 @@ "tooltip": "目前所有 {{primeTokenLimit}} 個 Prime 代幣均已被領取。如果持有人不再符合資格或我們增加數量,可能會有部分可用。" }, "progressBar": { - "ariaLabel": "你的當前 Prime 進度", "label": "已供應:{{userStakedXvsTokens}} / {{minXvsToStakeForPrimeTokens}}" }, "promotionalTitle": "將收益提升高達 {{percentage}}", @@ -2476,7 +2543,6 @@ "searchInput": { "placeholder": "搜索提案" }, - "summaryProgressBar": "投票總結進度條 - {{voteType}}", "totalLocked": "總鎖定", "toVoteYouShould": "投票前你應:", "voteAbstain": "投票棄權", @@ -2552,11 +2618,6 @@ "readyForExecution": "準備執行", "readyForQueuing": "準備排隊" }, - "statusCard": { - "ariaLabelAbstain": "棄權票", - "ariaLabelAgainst": "反對票", - "ariaLabelFor": "贊成票" - }, "timeUntilExecutable": "可執行於:{{ date, textualWithTime }}", "timeUntilVotable": "投票開始:{{ date, textualWithTime }}", "voteStatus": { diff --git a/apps/evm/src/pages/Dashboard/Markets/Positions/__tests__/__snapshots__/index.eMode.spec.tsx.snap b/apps/evm/src/pages/Dashboard/Markets/Positions/__tests__/__snapshots__/index.eMode.spec.tsx.snap index 318dbef646..c50626abe1 100644 --- a/apps/evm/src/pages/Dashboard/Markets/Positions/__tests__/__snapshots__/index.eMode.spec.tsx.snap +++ b/apps/evm/src/pages/Dashboard/Markets/Positions/__tests__/__snapshots__/index.eMode.spec.tsx.snap @@ -1,3 +1,3 @@ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html -exports[`Positions - Feature flag enabled: E-mode > displays E-mode banner correctly when user has enabled an E-mode group 1`] = `"Health factor15.62HealthyNet APY0.02%Daily earnings$1Total supply$1.23MTotal borrow$123.33Borrowed:$123.33LT:Liquidation threshold:$1.92K..Health factor15.62HealthyNet APY0.02%Daily earnings$1Total supply$1.23MTotal borrow$123.33Borrowed:$123.33LT:Liquidation threshold:$1.92K..SuppliedAssetAPY Balancesorted descendingCollateralXVS0.16%90$115.08USDT4.01%5.76%100$100USDC5.99%100$99.99XVSAPY 0.16%Balance90$115.08CollateralUSDTAPY 4.01%5.76%Balance100$100CollateralUSDCAPY 5.99%Balance100$99.99CollateralBorrowedE-mode: StablecoinsAssetAPY Balancesorted descending% of limitBUSD-5.81%50$500%USDT-5.49%-6.51%40$400%BUSDAPY -5.81%Balance50$50% of limit0%USDTAPY -5.49%-6.51%Balance40$40% of limit0%"`; +exports[`Positions - Feature flag enabled: E-mode > displays E-mode banner correctly when user has enabled an E-mode group 1`] = `"Health factor15.62HealthyNet APY0.02%Daily earnings$1Total supply$1.23MTotal borrow$123.33Borrowed:$123.33LT:Liquidation threshold:$1.92KHealth factor15.62HealthyNet APY0.02%Daily earnings$1Total supply$1.23MTotal borrow$123.33Borrowed:$123.33LT:Liquidation threshold:$1.92KSuppliedAssetAPY Balancesorted descendingCollateralXVS0.16%90$115.08USDT4.01%5.76%100$100USDC5.99%100$99.99XVSAPY 0.16%Balance90$115.08CollateralUSDTAPY 4.01%5.76%Balance100$100CollateralUSDCAPY 5.99%Balance100$99.99CollateralBorrowedE-mode: StablecoinsAssetAPY Balancesorted descending% of limitBUSD-5.81%50$500%USDT-5.49%-6.51%40$400%BUSDAPY -5.81%Balance50$50% of limit0%USDTAPY -5.49%-6.51%Balance40$40% of limit0%"`; diff --git a/apps/evm/src/pages/Dashboard/Markets/Positions/__tests__/__snapshots__/index.spec.tsx.snap b/apps/evm/src/pages/Dashboard/Markets/Positions/__tests__/__snapshots__/index.spec.tsx.snap index ac0355c0e6..f60d42fc57 100644 --- a/apps/evm/src/pages/Dashboard/Markets/Positions/__tests__/__snapshots__/index.spec.tsx.snap +++ b/apps/evm/src/pages/Dashboard/Markets/Positions/__tests__/__snapshots__/index.spec.tsx.snap @@ -1,3 +1,3 @@ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html -exports[`Positions > displays content correctly 1`] = `"Health factor15.62HealthyNet APY0.02%Daily earnings$1Total supply$1.23MTotal borrow$123.33Borrowed:$123.33LT:Liquidation threshold:$1.92K..Health factor15.62HealthyNet APY0.02%Daily earnings$1Total supply$1.23MTotal borrow$123.33Borrowed:$123.33LT:Liquidation threshold:$1.92K..SuppliedAssetAPY Balancesorted descendingCollateralXVS0.16%90$115.08USDT4.01%5.76%100$100USDC5.99%100$99.99XVSAPY 0.16%Balance90$115.08CollateralUSDTAPY 4.01%5.76%Balance100$100CollateralUSDCAPY 5.99%Balance100$99.99CollateralBorrowedAssetAPY Balancesorted descending% of limitBUSD-5.81%50$500%USDT-5.49%-6.51%40$400%BUSDAPY -5.81%Balance50$50% of limit0%USDTAPY -5.49%-6.51%Balance40$40% of limit0%"`; +exports[`Positions > displays content correctly 1`] = `"Health factor15.62HealthyNet APY0.02%Daily earnings$1Total supply$1.23MTotal borrow$123.33Borrowed:$123.33LT:Liquidation threshold:$1.92KHealth factor15.62HealthyNet APY0.02%Daily earnings$1Total supply$1.23MTotal borrow$123.33Borrowed:$123.33LT:Liquidation threshold:$1.92KSuppliedAssetAPY Balancesorted descendingCollateralXVS0.16%90$115.08USDT4.01%5.76%100$100USDC5.99%100$99.99XVSAPY 0.16%Balance90$115.08CollateralUSDTAPY 4.01%5.76%Balance100$100CollateralUSDCAPY 5.99%Balance100$99.99CollateralBorrowedAssetAPY Balancesorted descending% of limitBUSD-5.81%50$500%USDT-5.49%-6.51%40$400%BUSDAPY -5.81%Balance50$50% of limit0%USDTAPY -5.49%-6.51%Balance40$40% of limit0%"`; diff --git a/apps/evm/src/pages/Dashboard/Markets/__tests__/__snapshots__/index.spec.tsx.snap b/apps/evm/src/pages/Dashboard/Markets/__tests__/__snapshots__/index.spec.tsx.snap index 4b0e793c3f..b14ddbab53 100644 --- a/apps/evm/src/pages/Dashboard/Markets/__tests__/__snapshots__/index.spec.tsx.snap +++ b/apps/evm/src/pages/Dashboard/Markets/__tests__/__snapshots__/index.spec.tsx.snap @@ -1,5 +1,5 @@ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html -exports[`Markets > displays content correctly 1`] = `"Health factor15.62HealthyNet APY0.02%Daily earnings$1Total supply$1.23MTotal borrow$123.33Borrowed:$123.33LT:Liquidation threshold:$1.92K..Health factor15.62HealthyNet APY0.02%Daily earnings$1Total supply$1.23MTotal borrow$123.33Borrowed:$123.33LT:Liquidation threshold:$1.92K..SuppliedAssetAPY Balancesorted descendingCollateralXVS0.16%90$115.08USDT4.01%5.76%100$100USDC5.99%100$99.99XVSAPY 0.16%Balance90$115.08CollateralUSDTAPY 4.01%5.76%Balance100$100CollateralUSDCAPY 5.99%Balance100$99.99CollateralBorrowedAssetAPY Balancesorted descending% of limitBUSD-5.81%50$500%USDT-5.49%-6.51%40$400%BUSDAPY -5.81%Balance50$50% of limit0%USDTAPY -5.49%-6.51%Balance40$40% of limit0%Spotlight ( Supply/Borrow )USDC5.99%USDT4.01%5.76%BUSD3.56%5.31%"`; +exports[`Markets > displays content correctly 1`] = `"Health factor15.62HealthyNet APY0.02%Daily earnings$1Total supply$1.23MTotal borrow$123.33Borrowed:$123.33LT:Liquidation threshold:$1.92KHealth factor15.62HealthyNet APY0.02%Daily earnings$1Total supply$1.23MTotal borrow$123.33Borrowed:$123.33LT:Liquidation threshold:$1.92KSuppliedAssetAPY Balancesorted descendingCollateralXVS0.16%90$115.08USDT4.01%5.76%100$100USDC5.99%100$99.99XVSAPY 0.16%Balance90$115.08CollateralUSDTAPY 4.01%5.76%Balance100$100CollateralUSDCAPY 5.99%Balance100$99.99CollateralBorrowedAssetAPY Balancesorted descending% of limitBUSD-5.81%50$500%USDT-5.49%-6.51%40$400%BUSDAPY -5.81%Balance50$50% of limit0%USDTAPY -5.49%-6.51%Balance40$40% of limit0%Spotlight ( Supply/Borrow )USDC5.99%USDT4.01%5.76%BUSD3.56%5.31%"`; exports[`Markets > displays placeholder when user has no position in pool 1`] = `"No supply or borrow positions yetConnect walletSpotlight ( Supply/Borrow )USDC5.99%USDT4.01%5.76%BUSD3.56%5.31%"`; diff --git a/apps/evm/src/pages/Dashboard/__tests__/__snapshots__/index.spec.tsx.snap b/apps/evm/src/pages/Dashboard/__tests__/__snapshots__/index.spec.tsx.snap index c30e10b46b..5f178098cb 100644 --- a/apps/evm/src/pages/Dashboard/__tests__/__snapshots__/index.spec.tsx.snap +++ b/apps/evm/src/pages/Dashboard/__tests__/__snapshots__/index.spec.tsx.snap @@ -1,3 +1,3 @@ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html -exports[`Dashboard > displays content correctly 1`] = `"Venus Trade is live nowTrade relative performance on-chainStart now$50K USDT rewards on Venus Flux30 days only. Live May 28 with Binance WalletStart nowPendle vault now openOne-click Pendle yield right from VenusStart nowBoost is available now!One-click leverage to boost your yield up to 5x.Start nowBoost is available now!One-click leverage to boost your yield up to 5x.Isolated pools have been sunsetPlease withdraw or migrate all assets nowStart nowIsolated pools have been sunsetPlease withdraw or migrate all assets nowNet worth$1.23MToday's change+$1.23M99.99%Absolute performance+$1.23MMarketsVaultsHealth factor15.62HealthyNet APY0.02%Daily earnings$1Total supply$1.23MTotal borrow$123.33Borrowed:$123.33LT:Liquidation threshold:$1.92K..Health factor15.62HealthyNet APY0.02%Daily earnings$1Total supply$1.23MTotal borrow$123.33Borrowed:$123.33LT:Liquidation threshold:$1.92K..SuppliedAssetAPY Balancesorted descendingCollateralXVS0.16%90$115.08USDT4.01%5.76%100$100USDC5.99%100$99.99XVSAPY 0.16%Balance90$115.08CollateralUSDTAPY 4.01%5.76%Balance100$100CollateralUSDCAPY 5.99%Balance100$99.99CollateralBorrowedAssetAPY Balancesorted descending% of limitBUSD-5.81%50$500%USDT-5.49%-6.51%40$400%BUSDAPY -5.81%Balance50$50% of limit0%USDTAPY -5.49%-6.51%Balance40$40% of limit0%Spotlight ( Supply/Borrow )USDC5.99%USDT4.01%5.76%BUSD3.56%5.31%"`; +exports[`Dashboard > displays content correctly 1`] = `"Venus Trade is live nowTrade relative performance on-chainStart now$50K USDT rewards on Venus Flux30 days only. Live May 28 with Binance WalletStart nowPendle vault now openOne-click Pendle yield right from VenusStart nowBoost is available now!One-click leverage to boost your yield up to 5x.Start nowBoost is available now!One-click leverage to boost your yield up to 5x.Isolated pools have been sunsetPlease withdraw or migrate all assets nowStart nowIsolated pools have been sunsetPlease withdraw or migrate all assets nowNet worth$1.23MToday's change+$1.23M99.99%Absolute performance+$1.23MMarketsVaultsHealth factor15.62HealthyNet APY0.02%Daily earnings$1Total supply$1.23MTotal borrow$123.33Borrowed:$123.33LT:Liquidation threshold:$1.92KHealth factor15.62HealthyNet APY0.02%Daily earnings$1Total supply$1.23MTotal borrow$123.33Borrowed:$123.33LT:Liquidation threshold:$1.92KSuppliedAssetAPY Balancesorted descendingCollateralXVS0.16%90$115.08USDT4.01%5.76%100$100USDC5.99%100$99.99XVSAPY 0.16%Balance90$115.08CollateralUSDTAPY 4.01%5.76%Balance100$100CollateralUSDCAPY 5.99%Balance100$99.99CollateralBorrowedAssetAPY Balancesorted descending% of limitBUSD-5.81%50$500%USDT-5.49%-6.51%40$400%BUSDAPY -5.81%Balance50$50% of limit0%USDTAPY -5.49%-6.51%Balance40$40% of limit0%Spotlight ( Supply/Borrow )USDC5.99%USDT4.01%5.76%BUSD3.56%5.31%"`; diff --git a/apps/evm/src/pages/LiquidityHub/AllocationDetails/YieldGroupList/RowFooter/CardList/index.tsx b/apps/evm/src/pages/LiquidityHub/AllocationDetails/YieldGroupList/RowFooter/CardList/index.tsx new file mode 100644 index 0000000000..314c3b2866 --- /dev/null +++ b/apps/evm/src/pages/LiquidityHub/AllocationDetails/YieldGroupList/RowFooter/CardList/index.tsx @@ -0,0 +1,32 @@ +import { cn } from '@venusprotocol/ui'; + +import { Card, LabeledInlineContent, type TableColumn } from 'components'; +import type { LiquidityHubSource } from 'types'; + +export interface CardListProps { + columns: TableColumn[]; + sources: LiquidityHubSource[]; + rowKeyExtractor: (row: LiquidityHubSource) => string; + className?: string; +} + +export const CardList: React.FC = ({ + columns, + sources, + rowKeyExtractor, + className, +}) => ( +
+ {sources.map((row, rowIndex) => ( + +
+ {columns.map(column => ( + + {column.renderCell(row, rowIndex)} + + ))} +
+
+ ))} +
+); diff --git a/apps/evm/src/pages/LiquidityHub/AllocationDetails/YieldGroupList/RowFooter/index.tsx b/apps/evm/src/pages/LiquidityHub/AllocationDetails/YieldGroupList/RowFooter/index.tsx new file mode 100644 index 0000000000..d1dec79b46 --- /dev/null +++ b/apps/evm/src/pages/LiquidityHub/AllocationDetails/YieldGroupList/RowFooter/index.tsx @@ -0,0 +1,48 @@ +import { AccordionAnimatedContent, Table } from 'components'; +import type { LiquidityHubSource, LiquidityHubYieldGroup, Token } from 'types'; +import { CardList } from './CardList'; +import { useColumns } from './useColumns'; + +export interface RowFooterProps { + row: LiquidityHubYieldGroup; + underlyingToken: Token; + isOpen: boolean; +} + +export const RowFooter: React.FC = ({ row, underlyingToken, isOpen }) => { + const columns = useColumns({ + yieldGroup: row, + underlyingToken, + }); + + const orderByColumn = columns.find(column => column.key === 'averageApy'); + const initialOrder = orderByColumn && { + orderBy: orderByColumn, + orderDirection: 'desc' as const, + }; + + const rowKeyExtractor = (row: LiquidityHubSource) => row.address; + + return ( + + + +
+ + + + ); +}; diff --git a/apps/evm/src/pages/LiquidityHub/AllocationDetails/YieldGroupList/RowFooter/useColumns/index.tsx b/apps/evm/src/pages/LiquidityHub/AllocationDetails/YieldGroupList/RowFooter/useColumns/index.tsx new file mode 100644 index 0000000000..61c5c679cb --- /dev/null +++ b/apps/evm/src/pages/LiquidityHub/AllocationDetails/YieldGroupList/RowFooter/useColumns/index.tsx @@ -0,0 +1,108 @@ +import { Apy, LayeredValues, type TableColumn, TokenGroup } from 'components'; +import { PLACEHOLDER_KEY } from 'constants/placeholders'; +import { useTranslation } from 'libs/translations'; +import type { LiquidityHubSource, LiquidityHubYieldGroup, Token } from 'types'; +import { formatCentsToReadableValue, formatTokensToReadableValue } from 'utilities'; + +export const useColumns = ({ + yieldGroup, + underlyingToken, +}: { + yieldGroup: LiquidityHubYieldGroup; + underlyingToken: Token; +}) => { + const { t } = useTranslation(); + + const nameColumnContent = + yieldGroup.type === 'institutionCapital' + ? t('liquidityHub.allocationDetails.yieldGroup.nameColumn.title.vault') + : t('liquidityHub.allocationDetails.yieldGroup.nameColumn.title.market'); + + const shouldDisplayLockEndDate = yieldGroup.sources.some(source => !!source.lockEndDate); + + const columns: TableColumn[] = [ + { + key: 'name', + label: nameColumnContent, + selectOptionLabel: nameColumnContent, + renderCell: row => row.name, + }, + { + key: 'apy', + label: t('liquidityHub.allocationDetails.yieldGroup.apyColumn.title'), + selectOptionLabel: t('liquidityHub.allocationDetails.yieldGroup.apyColumn.title'), + align: 'right', + renderCell: ({ supplyApyPercentage, supplyTokenDistributions }) => ( + + ), + }, + { + key: 'allocation', + label: t('liquidityHub.allocationDetails.yieldGroup.allocationColumn.title'), + selectOptionLabel: t('liquidityHub.allocationDetails.yieldGroup.allocationColumn.title'), + align: 'right', + renderCell: ({ allocationCents, allocationTokens }) => ( + + ), + }, + { + key: 'liquidity', + label: t('liquidityHub.allocationDetails.yieldGroup.liquidityColumn.title'), + selectOptionLabel: t('liquidityHub.allocationDetails.yieldGroup.liquidityColumn.title'), + align: 'right', + renderCell: ({ liquidityCents, liquidityTokens }) => ( + + ), + }, + ]; + + if (shouldDisplayLockEndDate) { + columns.push({ + key: 'lockEndDate', + label: t('liquidityHub.allocationDetails.yieldGroup.lockEndDateColumn.title'), + selectOptionLabel: t('liquidityHub.allocationDetails.yieldGroup.lockEndDateColumn.title'), + align: 'right', + renderCell: ({ lockEndDate }) => + lockEndDate + ? t('liquidityHub.allocationDetails.yieldGroup.lockEndDateColumn.date', { + date: lockEndDate, + }) + : PLACEHOLDER_KEY, + }); + } + + columns.push({ + key: 'collateral', + label: t('liquidityHub.allocationDetails.yieldGroup.collateralColumn.title'), + selectOptionLabel: t('liquidityHub.allocationDetails.yieldGroup.collateralColumn.title'), + align: 'right', + renderCell: ({ collateralTokens }) => , + }); + + return columns; +}; diff --git a/apps/evm/src/pages/LiquidityHub/AllocationDetails/YieldGroupList/__tests__/index.spec.tsx b/apps/evm/src/pages/LiquidityHub/AllocationDetails/YieldGroupList/__tests__/index.spec.tsx new file mode 100644 index 0000000000..9341467a66 --- /dev/null +++ b/apps/evm/src/pages/LiquidityHub/AllocationDetails/YieldGroupList/__tests__/index.spec.tsx @@ -0,0 +1,39 @@ +import { fireEvent, screen, waitFor } from '@testing-library/react'; + +import { liquidityHubs } from '__mocks__/models/liquidityHubs'; +import { renderComponent } from 'testUtils/render'; +import { YieldGroupList } from '..'; + +vi.mock('motion/react', () => ({ + AnimatePresence: ({ children }: { children?: React.ReactNode }) => <>{children}, + motion: { + div: ({ children, ...props }: React.HTMLAttributes) => ( +
{children}
+ ), + }, +})); + +describe('YieldGroupList', () => { + it('renders yield groups in card and table views', () => { + renderComponent(); + + expect(screen.getAllByText('Venus Core').length).toBeGreaterThan(1); + expect(screen.getAllByText('Venus Flux').length).toBeGreaterThan(1); + }); + + it('toggles a yield group source list from the card view', async () => { + renderComponent(); + + const [highestApyCardLabel] = screen.getAllByText('Venus Flux'); + + expect(screen.queryByText('Flux Vault')).not.toBeInTheDocument(); + + fireEvent.click(highestApyCardLabel); + + await waitFor(() => expect(screen.getAllByText('Flux Vault').length).toBeGreaterThan(1)); + + fireEvent.click(highestApyCardLabel); + + await waitFor(() => expect(screen.queryByText('Flux Vault')).not.toBeInTheDocument()); + }); +}); diff --git a/apps/evm/src/pages/LiquidityHub/AllocationDetails/YieldGroupList/index.tsx b/apps/evm/src/pages/LiquidityHub/AllocationDetails/YieldGroupList/index.tsx new file mode 100644 index 0000000000..ace5d18964 --- /dev/null +++ b/apps/evm/src/pages/LiquidityHub/AllocationDetails/YieldGroupList/index.tsx @@ -0,0 +1,71 @@ +import { useState } from 'react'; + +import { CompactTableCardList, Table } from 'components'; +import type { LiquidityHub, LiquidityHubYieldGroup } from 'types'; +import { RowFooter } from './RowFooter'; +import { useColumns } from './useColumns'; + +export interface YieldGroupListProps { + liquidityHub: LiquidityHub; +} + +export const YieldGroupList: React.FC = ({ liquidityHub }) => { + const [openPositionAccordionKeys, setOpenAccordionKeys] = useState([]); + + const rowKeyExtractor = (row: LiquidityHubYieldGroup) => row.address; + + const renderRowFooter = (row: LiquidityHubYieldGroup) => { + const isOpen = openPositionAccordionKeys.includes(rowKeyExtractor(row)); + + return ( + + ); + }; + + const handleRowClick = (row: LiquidityHubYieldGroup) => { + const rowKey = rowKeyExtractor(row); + + setOpenAccordionKeys(currOpenKeys => + currOpenKeys.includes(rowKey) + ? currOpenKeys.filter(key => key !== rowKey) + : [...currOpenKeys, rowKey], + ); + }; + + const columns = useColumns({ openPositionAccordionKeys, rowKeyExtractor }); + + const orderByColumn = columns.find(column => column.key === 'averageApy'); + const initialOrder = orderByColumn && { + orderBy: orderByColumn, + orderDirection: 'desc' as const, + }; + + return ( +
+ + +
+
handleRowClick(row)} + renderRowFooter={renderRowFooter} + size="sm" + /> + + + ); +}; diff --git a/apps/evm/src/pages/LiquidityHub/AllocationDetails/YieldGroupList/useColumns/index.tsx b/apps/evm/src/pages/LiquidityHub/AllocationDetails/YieldGroupList/useColumns/index.tsx new file mode 100644 index 0000000000..383ce8e39c --- /dev/null +++ b/apps/evm/src/pages/LiquidityHub/AllocationDetails/YieldGroupList/useColumns/index.tsx @@ -0,0 +1,109 @@ +import { cn } from '@venusprotocol/ui'; + +import { Icon, InfoIcon, type TableColumn } from 'components'; +import { useTranslation } from 'libs/translations'; +import type { LiquidityHubYieldGroup } from 'types'; +import { + compareBigNumbers, + formatCentsToReadableValue, + formatPercentageToReadableValue, +} from 'utilities'; +import { YieldGroupName } from '../../YieldGroupName'; + +export const useColumns = ({ + openPositionAccordionKeys, + rowKeyExtractor, +}: { + openPositionAccordionKeys: string[]; + rowKeyExtractor: (row: LiquidityHubYieldGroup) => string; +}) => { + const { t } = useTranslation(); + + const columns: TableColumn[] = [ + { + key: 'source', + label: t('liquidityHub.allocationDetails.table.sourceColumn.title'), + selectOptionLabel: t('liquidityHub.allocationDetails.table.sourceColumn.title'), + renderCell: row => , + }, + { + key: 'allocation', + label: t('liquidityHub.allocationDetails.table.allocationColumn.title'), + selectOptionLabel: t('liquidityHub.allocationDetails.table.allocationColumn.title'), + sortRows: (rowA, rowB, direction) => + compareBigNumbers(rowA.allocationCents, rowB.allocationCents, direction), + align: 'right', + renderCell: ({ allocationCents }) => + formatCentsToReadableValue({ + value: allocationCents, + }), + }, + { + key: 'liquidity', + label: t('liquidityHub.allocationDetails.table.liquidityColumn.title'), + selectOptionLabel: t('liquidityHub.allocationDetails.table.liquidityColumn.title'), + sortRows: (rowA, rowB, direction) => + compareBigNumbers(rowA.liquidityCents, rowB.liquidityCents, direction), + align: 'right', + renderCell: ({ liquidityCents }) => + formatCentsToReadableValue({ + value: liquidityCents, + }), + }, + { + key: 'averageApy', + label: t('liquidityHub.allocationDetails.table.averageApyColumn.title'), + selectOptionLabel: t('liquidityHub.allocationDetails.table.averageApyColumn.title'), + sortRows: (rowA, rowB, direction) => + compareBigNumbers( + rowA.averageSupplyApyPercentage, + rowB.averageSupplyApyPercentage, + direction, + ), + align: 'right', + renderCell: ({ averageSupplyApyPercentage }) => + formatPercentageToReadableValue(averageSupplyApyPercentage), + }, + { + key: 'capPercentage', + label: t('liquidityHub.allocationDetails.table.capPercentageColumn.title'), + selectOptionLabel: t('liquidityHub.allocationDetails.table.capPercentageColumn.title'), + sortRows: (rowA, rowB, direction) => + compareBigNumbers(rowA.allocationCapPercentage, rowB.allocationCapPercentage, direction), + align: 'right', + renderCell: ({ allocationCapPercentage }) => + formatPercentageToReadableValue(allocationCapPercentage), + }, + { + key: 'capDollars', + label: ( +
+ {t('liquidityHub.allocationDetails.table.capDollarsColumn.title')} + + +
+ ), + selectOptionLabel: t('liquidityHub.allocationDetails.table.capDollarsColumn.title'), + sortRows: (rowA, rowB, direction) => + compareBigNumbers(rowA.allocationCapCents, rowB.allocationCapCents, direction), + align: 'right', + renderCell: row => { + const isOpen = openPositionAccordionKeys.includes(rowKeyExtractor(row)); + + return ( +
+ + {row.allocationCapCents.isFinite() + ? formatCentsToReadableValue({ value: row.allocationCapCents }) + : t('liquidityHub.allocationDetails.table.capDollarsColumn.unlimited')} + + + +
+ ); + }, + }, + ]; + + return columns; +}; diff --git a/apps/evm/src/pages/LiquidityHub/AllocationDetails/YieldGroupName/index.tsx b/apps/evm/src/pages/LiquidityHub/AllocationDetails/YieldGroupName/index.tsx new file mode 100644 index 0000000000..6526c9b028 --- /dev/null +++ b/apps/evm/src/pages/LiquidityHub/AllocationDetails/YieldGroupName/index.tsx @@ -0,0 +1,14 @@ +import { cn } from '@venusprotocol/ui'; + +export interface YieldGroupNameProps { + name: string; + bgClassName: string; +} + +export const YieldGroupName: React.FC = ({ name, bgClassName }) => ( +
+
+ + {name} +
+); diff --git a/apps/evm/src/pages/LiquidityHub/AllocationDetails/index.tsx b/apps/evm/src/pages/LiquidityHub/AllocationDetails/index.tsx new file mode 100644 index 0000000000..35ddf74b55 --- /dev/null +++ b/apps/evm/src/pages/LiquidityHub/AllocationDetails/index.tsx @@ -0,0 +1,90 @@ +import BigNumber from 'bignumber.js'; + +import { Card, ProgressBar, type ProgressBarData } from 'components'; +import { useTranslation } from 'libs/translations'; +import type { LiquidityHub } from 'types'; +import { formatPercentageToReadableValue } from 'utilities'; +import { YieldGroupList } from './YieldGroupList'; +import { YieldGroupName } from './YieldGroupName'; + +interface Allocation { + name: string; + bgClassname: string; + allocationPercentage: number; +} + +export interface AllocationDetailsProps { + liquidityHub: LiquidityHub; +} + +export const AllocationDetails: React.FC = ({ liquidityHub }) => { + const { t } = useTranslation(); + + const totalAllocationCents = liquidityHub.yieldGroups.reduce( + (acc, yieldGroup) => acc.plus(yieldGroup.allocationCents), + new BigNumber(0), + ); + + let allocationLeftPercentage = 100; + + const allocations = liquidityHub.yieldGroups.reduce((acc, yieldGroup, index) => { + const allocationPercentage = + index === liquidityHub.yieldGroups.length - 1 + ? allocationLeftPercentage + : yieldGroup.allocationCents + .multipliedBy(100) + .dividedBy(totalAllocationCents) + .dp(0) + .toNumber(); + + allocationLeftPercentage -= allocationPercentage; + + const allocation: Allocation = { + name: yieldGroup.name, + bgClassname: yieldGroup.bgClassName, + allocationPercentage, + }; + + return [...acc, allocation]; + }, []); + + const progressBars: ProgressBarData[] = allocations + .map((allocation, index) => { + const lastAllocationPosition = allocations + .slice(0, index) + .reduce((acc, allocation) => acc + allocation.allocationPercentage, 0); + + return { + value: lastAllocationPosition + allocation.allocationPercentage, + className: allocation.bgClassname, + }; + }) + .reverse(); + + return ( + +
+

{t('liquidityHub.allocationDetails.title')}

+ + + {allocations.map(allocation => ( +
+ + +

{formatPercentageToReadableValue(allocation.allocationPercentage)}

+
+ ))} +
+ } + /> +
+ + + + ); +}; diff --git a/apps/evm/src/pages/LiquidityHub/LiquidityHubInfo/index.tsx b/apps/evm/src/pages/LiquidityHub/LiquidityHubInfo/index.tsx new file mode 100644 index 0000000000..0cfce32b15 --- /dev/null +++ b/apps/evm/src/pages/LiquidityHub/LiquidityHubInfo/index.tsx @@ -0,0 +1,104 @@ +import { cn } from '@venusprotocol/ui'; + +import { MarketInfo, type MarketInfoProps } from 'components'; +import { routes } from 'constants/routing'; +import { DAYS_PER_YEAR } from 'constants/time'; +import { ChainExplorerLink } from 'containers/ChainExplorerLink'; +import { Link } from 'containers/Link'; +import { useTranslation } from 'libs/translations'; +import { useChainId } from 'libs/wallet'; +import type { LiquidityHub } from 'types'; +import { + formatCentsToReadableValue, + formatPercentageToReadableValue, + getCombinedApy, +} from 'utilities'; + +export interface LiquidityHubInfoProps { + liquidityHub: LiquidityHub; +} + +export const LiquidityHubInfo: React.FC = ({ liquidityHub }) => { + const { t, Trans } = useTranslation(); + const { chainId } = useChainId(); + + const { totalApyPercentage } = getCombinedApy({ + type: 'supply', + baseApyPercentage: liquidityHub.supplyApyPercentage, + tokenDistributions: liquidityHub.supplyTokenDistributions, + }); + + const readableDailySupplyingInterests = formatCentsToReadableValue({ + value: liquidityHub.supplyBalanceCents + .multipliedBy(totalApyPercentage) + .dividedBy(100) + .dividedBy(DAYS_PER_YEAR), + }); + + const stats: MarketInfoProps['items'] = [ + { + label: t('liquidityHub.info.stats.operator'), + children: liquidityHub.operatorName, + }, + { + label: t('liquidityHub.info.stats.hubContract'), + children: ( + + ), + }, + { + label: t('liquidityHub.info.stats.vhTokenContract', { + vhTokenSymbol: liquidityHub.vhToken.symbol, + }), + children: ( + + ), + }, + { + label: t('liquidityHub.info.stats.performanceFee'), + children: formatPercentageToReadableValue(liquidityHub.performanceFeePercentage), + }, + { + label: t('liquidityHub.info.stats.redeemFee'), + children: formatPercentageToReadableValue(liquidityHub.redeemFeePercentage), + }, + { + label: t('liquidityHub.info.stats.dailySupplyingInterests'), + children: readableDailySupplyingInterests, + }, + { + label: t('liquidityHub.info.stats.exchangeRate.title'), + children: t('liquidityHub.info.stats.exchangeRate.value', { + vhTokenSymbol: liquidityHub.vhToken.symbol, + underlyingTokenSymbol: liquidityHub.vhToken.underlyingToken.symbol, + exchangeRate: liquidityHub.pricePerShare.dp(6).toFixed(), + }), + }, + { + label: t('liquidityHub.info.stats.riskDisclosures.title'), + children: ( + , + Ul:
    , + Li:
  • , + // TODO: check this is the link we want to redirect to + Link: , + }} + /> + ), + className: cn('flex-col gap-y-3'), + }, + ]; + + return ; +}; diff --git a/apps/evm/src/pages/LiquidityHub/__tests__/__snapshots__/index.spec.tsx.snap b/apps/evm/src/pages/LiquidityHub/__tests__/__snapshots__/index.spec.tsx.snap new file mode 100644 index 0000000000..ecd8f68fa3 --- /dev/null +++ b/apps/evm/src/pages/LiquidityHub/__tests__/__snapshots__/index.spec.tsx.snap @@ -0,0 +1,3 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`LiquidityHub > loads the liquidity hub from the route and renders its content 1`] = `"SupplyWithdrawWalletCollateralSupply APY6.2%Distribution APY1.2%Total supply APY7.4%Connect walletSupply info61%Total supplied$109.03K / $178.75K15.25K / 25K XVSAverage APY4.1%Current APY7.4%Position unit price (vhXVS)1.06Allocation detailsVenus FluxAlloc.$7.25KLiquidity$5.8KAPY6.8%Cap45%Cap abs.$14.5KVenus CoreAlloc.$8KLiquidity$6.2KAPY5.7%Cap55%Cap abs.$16KSourceAlloc.LiquidityAPYsorted descendingCapCap abs.Venus Flux$7.25K$5.8K6.8%45%$14.5KVenus Core$8K$6.2K5.7%55%$16KSort byAPYVenus FluxAlloc.$7.25KLiquidity$5.8KAPY6.8%Cap45%Cap abs.$14.5KVenus CoreAlloc.$8KLiquidity$6.2KAPY5.7%Cap55%Cap abs.$16KHub infoOperatorVenus CoreHub contract0x10...0001vhXVS contract0x20...0001Performance fee10%Redeem fee1%Daily supplying interests$22.1Exchange rate1 vhXVS = 1.06 XVSRisk disclosuresBy supplying to, withdrawing from, or otherwise interacting with the Venus Liquidity Hub, you acknowledge and agree that:You are interacting with autonomous smart contracts. Venus and its affiliates do not guarantee the performance, security, or availability of the Liquidity Hub or any related software, smart contracts, blockchain networks, or third-party integrations.Supplying assets to the Liquidity Hub does not grant borrowing power and does not constitute a deposit, loan, or investment product. You may not be able to withdraw at all times, and returns, if any, are not guaranteed.You assume all risks associated with your use of the Liquidity Hub and are solely responsible for determining whether it is appropriate for your financial circumstances, risk tolerance, and technical capabilities. You are responsible for conducting your own independent evaluation, including reviewing the relevant source code and on-chain configuration.Venus is a technology provider only and does not provide investment, advisory, fiduciary, or custodial services, and does not manage or hold your assets.Your interaction with the Liquidity Hub is governed by the Terms of Use."`; diff --git a/apps/evm/src/pages/LiquidityHub/__tests__/index.spec.tsx b/apps/evm/src/pages/LiquidityHub/__tests__/index.spec.tsx index b58e1e31bb..d4c4b27477 100644 --- a/apps/evm/src/pages/LiquidityHub/__tests__/index.spec.tsx +++ b/apps/evm/src/pages/LiquidityHub/__tests__/index.spec.tsx @@ -1,12 +1,9 @@ -import { screen } from '@testing-library/react'; import type { Mock } from 'vitest'; import { liquidityHubs } from '__mocks__/models/liquidityHubs'; import { useGetLiquidityHub } from 'clients/api'; import { routes } from 'constants/routing'; import { useIsFeatureEnabled } from 'hooks/useIsFeatureEnabled'; -import { en } from 'libs/translations'; -import { Route } from 'react-router'; import { renderComponent } from 'testUtils/render'; import LiquidityHub from '..'; @@ -35,58 +32,8 @@ describe('LiquidityHub', () => { }); it('loads the liquidity hub from the route and renders its content', () => { - renderLiquidityHub(); + const { container } = renderLiquidityHub(); - expect(mockUseGetLiquidityHub).toHaveBeenNthCalledWith( - 1, - { - vhTokenAddress: liquidityHub.vhToken.address, - }, - { - enabled: true, - }, - ); - expect(screen.getByText(en.market.supplyInfo.title)).toBeInTheDocument(); - expect( - screen.getByText(`Position unit price (${liquidityHub.vhToken.symbol})`), - ).toBeInTheDocument(); - expect(screen.getByText('1.06')).toBeInTheDocument(); - expect( - screen.getByRole('button', { - name: en.liquidityHubForm.supplyTabTitle, - }), - ).toBeInTheDocument(); - }); - - it('renders a spinner while the liquidity hub is loading', () => { - mockUseGetLiquidityHub.mockReturnValue({ - data: undefined, - isLoading: true, - }); - - renderLiquidityHub(); - - expect(screen.getByAltText('Spinner')).toBeInTheDocument(); - expect(screen.queryByText(en.market.supplyInfo.title)).not.toBeInTheDocument(); - }); - - it('redirects to the liquidity hubs page when the route address is invalid', async () => { - mockUseGetLiquidityHub.mockReturnValue({ - data: undefined, - isLoading: false, - }); - const liquidityHubsPageTitle = 'Liquidity hubs page'; - - renderComponent(, { - routePath: routes.liquidityHub.path, - routerInitialEntries: [ - routes.liquidityHub.path.replace(':vhTokenAddress', liquidityHub.vhToken.address), - ], - otherRoutes: ( - {liquidityHubsPageTitle}} /> - ), - }); - - expect(await screen.findByText(liquidityHubsPageTitle)).toBeInTheDocument(); + expect(container.textContent).toMatchSnapshot(); }); }); diff --git a/apps/evm/src/pages/LiquidityHub/index.tsx b/apps/evm/src/pages/LiquidityHub/index.tsx index 70748733ae..9716487f1f 100644 --- a/apps/evm/src/pages/LiquidityHub/index.tsx +++ b/apps/evm/src/pages/LiquidityHub/index.tsx @@ -4,7 +4,9 @@ import type { Address } from 'viem'; import { MarketPageGrid, Page } from 'components'; import { LiquidityHubForm } from 'containers/LiquidityHubForm'; import LiquidityHubLoader from 'containers/LiquidityHubLoader'; +import { AllocationDetails } from './AllocationDetails'; import { LiquidityHubHistory } from './LiquidityHubHistory'; +import { LiquidityHubInfo } from './LiquidityHubInfo'; const LiquidityHub: React.FC = () => { const { vhTokenAddress } = useParams<{ @@ -20,6 +22,10 @@ const LiquidityHub: React.FC = () => { content={
    + + + +
    } /> diff --git a/apps/evm/src/pages/LiquidityHubs/LiquidityHubTable/index.tsx b/apps/evm/src/pages/LiquidityHubs/LiquidityHubTable/index.tsx index c146da5893..31b9915a83 100644 --- a/apps/evm/src/pages/LiquidityHubs/LiquidityHubTable/index.tsx +++ b/apps/evm/src/pages/LiquidityHubs/LiquidityHubTable/index.tsx @@ -20,6 +20,7 @@ import { formatCentsToReadableValue, formatTokensToReadableValue, getCombinedApy, + getLiquidityHubCollateralTokens, } from 'utilities'; import { RowControl } from './RowControl'; @@ -69,6 +70,7 @@ export const LiquidityHubTable: React.FC = ({ key: 'totalSupply', label: t('liquidityHubs.table.columns.totalSupply'), selectOptionLabel: t('liquidityHubs.table.columns.totalSupply'), + align: 'right', sortRows: (rowA, rowB, direction) => compareBigNumbers(rowA.supplyBalanceCents, rowB.supplyBalanceCents, direction), renderCell: ({ vhToken, supplyBalanceTokens, supplyBalanceCents }) => ( @@ -100,9 +102,10 @@ export const LiquidityHubTable: React.FC = ({ /> ), selectOptionLabel: t('liquidityHubs.table.columns.exposure.selectionOptionLabel'), - renderCell: ({ sources }) => ( + align: 'right', + renderCell: ({ yieldGroups }) => ( source.collateralTokens)} + tokens={getLiquidityHubCollateralTokens({ yieldGroups })} removeDuplicates limit={5} /> @@ -112,6 +115,7 @@ export const LiquidityHubTable: React.FC = ({ key: 'supplyApy', label: t('liquidityHubs.table.columns.supplyApy'), selectOptionLabel: t('liquidityHubs.table.columns.supplyApy'), + align: 'right', sortRows: (rowA, rowB, direction) => compareBigNumbers( getCombinedApy({ diff --git a/apps/evm/src/pages/Market/EModeInfo/Mode/ModeCard/index.tsx b/apps/evm/src/pages/Market/EModeInfo/Mode/ModeCard/index.tsx deleted file mode 100644 index 6642f86e4e..0000000000 --- a/apps/evm/src/pages/Market/EModeInfo/Mode/ModeCard/index.tsx +++ /dev/null @@ -1,59 +0,0 @@ -import { cn } from '@venusprotocol/ui'; - -import { Delimiter, LabeledInlineContent, type Order, type TableColumn } from 'components'; -import { useTranslation } from 'libs/translations'; -import type { EModeGroup } from 'types'; -import type { ExtendedEModeAssetSettings } from '../types'; - -export interface ModeCardProps { - columns: TableColumn[]; - eModeAssetSettings: ExtendedEModeAssetSettings[]; - rowOnClick: (e: React.MouseEvent, row: EModeGroup) => void; - order: Order; - className?: string; -} - -export const ModeCard: React.FC = ({ - eModeAssetSettings, - columns, - rowOnClick, - order, - className, -}) => { - const { t } = useTranslation(); - - const sortedEModeAssetSettings = order.orderBy.sortRows - ? [...eModeAssetSettings].sort((rowA, rowB) => - order.orderBy.sortRows!(rowA, rowB, order.orderDirection), - ) - : eModeAssetSettings; - - return ( -
    - {sortedEModeAssetSettings.map((row, rowIndex) => ( -
    -
    -
    {columns[0].renderCell(row, rowIndex)}
    - -
    - {columns.slice(1).map(column => ( - - {column.renderCell(row, rowIndex)} - - ))} - -

    rowOnClick(e, row.eModeGroup)} - > - {t('market.eModeInfo.link')} -

    -
    -
    - - {rowIndex < eModeAssetSettings.length - 1 && } -
    - ))} -
    - ); -}; diff --git a/apps/evm/src/pages/Market/EModeInfo/Mode/index.tsx b/apps/evm/src/pages/Market/EModeInfo/Mode/index.tsx index 2cc988e410..bcba25daba 100644 --- a/apps/evm/src/pages/Market/EModeInfo/Mode/index.tsx +++ b/apps/evm/src/pages/Market/EModeInfo/Mode/index.tsx @@ -1,14 +1,15 @@ -import { Card, InfoIcon, Modal, type Order, Table } from 'components'; +import { Card, CompactTableCardList, InfoIcon, Modal, type Order, Table } from 'components'; import { EModeGroupList } from 'containers/EModeGroupList'; +import { useTranslation } from 'libs/translations'; import { useState } from 'react'; import type { EModeGroup } from 'types'; -import { ModeCard } from './ModeCard'; import type { ExtendedEModeAssetSettings, ModeProps } from './types'; import { useColumns } from './useColumns'; export * from './types'; export const Mode: React.FC = ({ title, tooltip, eModeAssetSettings, pool }) => { + const { t } = useTranslation(); const columns = useColumns(); const [selectedEModeGroup, setSelectedEModeGroup] = useState(); const closeModal = () => setSelectedEModeGroup(undefined); @@ -22,31 +23,43 @@ export const Mode: React.FC = ({ title, tooltip, eModeAssetSettings, <>
    - {title} + {title} + {tooltip && }
    - {/* Card view */} - setSelectedEModeGroup(eModeGroup)} - /> - - {/* Table view */} -
    -
+ {/* Card view */} + row.eModeGroup.name} - rowOnClick={(_e, row) => setSelectedEModeGroup(row.eModeGroup)} - initialOrder={initialOrder} - size="sm" + columns={columns} + order={initialOrder} + rowKeyExtractor={row => `${row.eModeGroup.name}-${row.vToken.address}`} + renderRowAction={row => ( + + )} /> + + {/* Table view */} +
+
row.eModeGroup.name} + rowOnClick={(_e, row) => setSelectedEModeGroup(row.eModeGroup)} + initialOrder={initialOrder} + size="sm" + /> + diff --git a/apps/evm/src/pages/Market/MarketInfo/index.tsx b/apps/evm/src/pages/Market/MarketInfo/index.tsx index 20ab022b86..714fdfcdc9 100644 --- a/apps/evm/src/pages/Market/MarketInfo/index.tsx +++ b/apps/evm/src/pages/Market/MarketInfo/index.tsx @@ -1,5 +1,8 @@ import BigNumber from 'bignumber.js'; -import { LabeledInlineContent, MarketCard } from 'components'; +import { + MarketInfo as MarketInfoCard, + type MarketInfoProps as MarketInfoCardProps, +} from 'components'; import { PLACEHOLDER_KEY } from 'constants/placeholders'; import { DAYS_PER_YEAR } from 'constants/time'; import { useIsFeatureEnabled } from 'hooks/useIsFeatureEnabled'; @@ -12,7 +15,6 @@ import { formatTokensToReadableValue, } from 'utilities'; import TEST_IDS from '../testIds'; -import type { Stat } from '../types'; export interface MarketInfoProps { asset: Asset; @@ -39,7 +41,7 @@ const MarketInfo: React.FC = ({ asset }) => { [asset], ); - const stats: Stat[] = useMemo(() => { + const stats: MarketInfoCardProps['items'] = useMemo(() => { if (!asset) { return []; } @@ -87,7 +89,7 @@ const MarketInfo: React.FC = ({ asset }) => { label: t('market.marketInfo.stats.dailyDistribution', { tokenSymbol: rewardToken.symbol, }), - value: formatTokensToReadableValue({ + children: formatTokensToReadableValue({ value: dailyDistributedTokens, addSymbol: false, token: rewardToken, @@ -99,11 +101,11 @@ const MarketInfo: React.FC = ({ asset }) => { ? [ { label: t('market.marketInfo.stats.supplierCountLabel'), - value: asset.supplierCount ?? '-', + children: asset.supplierCount ?? '-', }, { label: t('market.marketInfo.stats.borrowerCountLabel'), - value: asset.borrowerCount ?? '-', + children: asset.borrowerCount ?? '-', }, ] : []; @@ -112,42 +114,42 @@ const MarketInfo: React.FC = ({ asset }) => { ...participantCountRows, { label: t('market.marketInfo.stats.dailySupplyingInterestsLabel'), - value: formatCentsToReadableValue({ + children: formatCentsToReadableValue({ value: dailySupplyInterestsCents, }), }, { label: t('market.marketInfo.stats.dailyBorrowingInterestsLabel'), - value: formatCentsToReadableValue({ + children: formatCentsToReadableValue({ value: dailyBorrowInterestsCents, }), }, ...distributionRows, { label: t('market.marketInfo.stats.reserveFactorLabel'), - value: formatPercentageToReadableValue(asset.reserveFactor && asset.reserveFactor * 100), + children: formatPercentageToReadableValue(asset.reserveFactor && asset.reserveFactor * 100), }, { label: t('market.marketInfo.stats.isBorrowableLabel'), - value: asset.isBorrowable + children: asset.isBorrowable ? t('market.marketInfo.stats.yes') : t('market.marketInfo.stats.no'), }, { label: t('market.marketInfo.stats.collateralFactorLabel'), - value: formatPercentageToReadableValue(asset.collateralFactor * 100), + children: formatPercentageToReadableValue(asset.collateralFactor * 100), }, { label: t('market.marketInfo.stats.liquidationThresholdLabel'), - value: formatPercentageToReadableValue(asset.liquidationThresholdPercentage), + children: formatPercentageToReadableValue(asset.liquidationThresholdPercentage), }, { label: t('market.marketInfo.stats.liquidationPenaltyLabel'), - value: formatPercentageToReadableValue(asset.liquidationPenaltyPercentage), + children: formatPercentageToReadableValue(asset.liquidationPenaltyPercentage), }, { label: t('market.marketInfo.stats.exchangeRateLabel'), - value: asset.exchangeRateVTokens + children: asset.exchangeRateVTokens ? t('market.marketInfo.stats.exchangeRateValue', { tokenSymbol: asset.vToken.underlyingToken.symbol, vTokenSymbol: asset.vToken.symbol, @@ -165,20 +167,11 @@ const MarketInfo: React.FC = ({ asset }) => { ]); return ( - -
    - {stats.map(stat => ( -
  • - - {stat.value} - -
  • - ))} -
-
+ ); }; diff --git a/apps/evm/src/pages/Market/__tests__/__snapshots__/index.spec.tsx.snap b/apps/evm/src/pages/Market/__tests__/__snapshots__/index.spec.tsx.snap index a0ba1e7b52..e9027405a3 100644 --- a/apps/evm/src/pages/Market/__tests__/__snapshots__/index.spec.tsx.snap +++ b/apps/evm/src/pages/Market/__tests__/__snapshots__/index.spec.tsx.snap @@ -1,3 +1,3 @@ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html -exports[`Market > displays content correctly 1`] = `"Supplying XVS to the Core Pool lets you borrow tokens from this pool exclusively. Show all marketsVisit the mode info section to explore more available groups.SupplyWithdrawBorrowRepayCollateralXVSMAXSupply APY0.05%Distribution APY0.11%Total supply APY0.16%Connect walletSupply info0%Total supplied> $100T / > $100T> 100T / > 100T XVSCurrent APY0.16%Borrow info< 0.01%Total borrowed$2.36M / > $100T1.85M / > 100T XVSCurrent APY-6.48%Threshold50%Penalty4%Interest Rate ModelUtilization rateBorrow APYSupply APYE-modeStablecoinsCollateralBorrowableMax LTV60%Threshold62%Penalty0%Show all marketsGameFiCollateralBorrowableMax LTV60%Threshold62%Penalty0%Show all marketsGroupCollateralBorrowableMax LTVsorted descendingThresholdPenaltyStablecoins60%62%0%GameFi60%62%0%Sort byMax LTVStablecoinsCollateralBorrowableMax LTV60%Threshold62%Penalty0%GameFiCollateralBorrowableMax LTV60%Threshold62%Penalty0%Market infoDaily supplying interests$3.98Daily borrowing interests-$44.81Daily XVS distributed19.99MReserve factor25%Borrowable (Core)YesCollateral factor (Core)50%Liquidation threshold (Core)50%Liquidation penalty (Core)4%Exchange rate1 XVS=49.589181 vXVS"`; +exports[`Market > displays content correctly 1`] = `"Supplying XVS to the Core Pool lets you borrow tokens from this pool exclusively. Show all marketsVisit the mode info section to explore more available groups.SupplyWithdrawBorrowRepayCollateralXVSMAXSupply APY0.05%Distribution APY0.11%Total supply APY0.16%Connect walletSupply info0%Total supplied> $100T / > $100T> 100T / > 100T XVSCurrent APY0.16%Borrow info0%Total borrowed$2.36M / > $100T1.85M / > 100T XVSCurrent APY-6.48%Threshold50%Penalty4%Interest Rate ModelUtilization rateBorrow APYSupply APYE-modeStablecoinsCollateralBorrowableMax LTV60%Threshold62%Penalty0%Show all marketsGameFiCollateralBorrowableMax LTV60%Threshold62%Penalty0%Show all marketsGroupCollateralBorrowableMax LTVsorted descendingThresholdPenaltyStablecoins60%62%0%GameFi60%62%0%Sort byMax LTVStablecoinsCollateralBorrowableMax LTV60%Threshold62%Penalty0%GameFiCollateralBorrowableMax LTV60%Threshold62%Penalty0%Market infoDaily supplying interests$3.98Daily borrowing interests-$44.81Daily XVS distributed19.99MReserve factor25%Borrowable (Core)YesCollateral factor (Core)50%Liquidation threshold (Core)50%Liquidation penalty (Core)4%Exchange rate1 XVS=49.589181 vXVS"`; diff --git a/apps/evm/src/pages/Market/types.ts b/apps/evm/src/pages/Market/types.ts deleted file mode 100644 index cc5708c1e0..0000000000 --- a/apps/evm/src/pages/Market/types.ts +++ /dev/null @@ -1,4 +0,0 @@ -export interface Stat { - label: string; - value: React.ReactNode | string | number; -} diff --git a/apps/evm/src/pages/PrimeLeaderboard/MarketRewardRow/index.tsx b/apps/evm/src/pages/PrimeLeaderboard/MarketRewardRow/index.tsx index 0cbd44eb4d..960db7dc3e 100644 --- a/apps/evm/src/pages/PrimeLeaderboard/MarketRewardRow/index.tsx +++ b/apps/evm/src/pages/PrimeLeaderboard/MarketRewardRow/index.tsx @@ -1,7 +1,6 @@ import { cn } from '@venusprotocol/ui'; import { ProgressBar, TokenIconWithSymbol } from 'components'; -import { useTranslation } from 'libs/translations'; import type { Token } from 'types'; import { formatCentsToReadableValue } from 'utilities'; @@ -22,10 +21,10 @@ export const MarketRewardRow: React.FC = ({ actions, progressBarClassName, }) => { - const { t } = useTranslation(); - const progressPercentage = - totalRewardsCents > 0 ? Math.min(100, (rewardsCents / totalRewardsCents) * 100) : 0; + totalRewardsCents > 0 + ? Math.min(100, Math.max(0, (rewardsCents / totalRewardsCents) * 100)) + : 0; const isExpanded = !!(actions || apy); @@ -53,9 +52,7 @@ export const MarketRewardRow: React.FC = ({ className="w-full" min={0} max={100} - value={progressPercentage} - step={1} - ariaLabel={t('primeLeaderboard.userRewards.rewardShareAriaLabel')} + progressBars={[{ value: progressPercentage }]} /> diff --git a/apps/evm/src/pages/Proposal/ProposalSummary/index.tsx b/apps/evm/src/pages/Proposal/ProposalSummary/index.tsx index 00a8ba0c5f..562026a125 100644 --- a/apps/evm/src/pages/Proposal/ProposalSummary/index.tsx +++ b/apps/evm/src/pages/Proposal/ProposalSummary/index.tsx @@ -110,7 +110,6 @@ export const ProposalSummaryUi: React.FC )} diff --git a/apps/evm/src/pages/Proposal/VoteSummary/index.tsx b/apps/evm/src/pages/Proposal/VoteSummary/index.tsx index 25520bab31..cb6c15b0af 100644 --- a/apps/evm/src/pages/Proposal/VoteSummary/index.tsx +++ b/apps/evm/src/pages/Proposal/VoteSummary/index.tsx @@ -17,7 +17,7 @@ import { useStyles } from './styles'; interface VoteSummaryProps extends CardProps { label: string; - progressBarColor: string; + progressBarClassName: string; votedValueMantissa?: BigNumber; votedTotalMantissa?: BigNumber; voters?: VotersDetails['result']; @@ -29,7 +29,7 @@ interface VoteSummaryProps extends CardProps { const VoteSummary = ({ openVoteModal, label, - progressBarColor, + progressBarClassName, votedTotalMantissa = new BigNumber(0), votedValueMantissa = new BigNumber(0), voters = [], @@ -60,13 +60,15 @@ const VoteSummary = ({ diff --git a/apps/evm/src/pages/Proposal/__tests__/index.spec.tsx b/apps/evm/src/pages/Proposal/__tests__/index.spec.tsx index de90948c67..b15b342b07 100644 --- a/apps/evm/src/pages/Proposal/__tests__/index.spec.tsx +++ b/apps/evm/src/pages/Proposal/__tests__/index.spec.tsx @@ -36,6 +36,7 @@ import { RemoteProposalState, VoteSupport, } from 'types'; +import { truncateAddress } from 'utilities'; import config from 'config'; import { REDIRECT_TEST_CONTENT } from 'containers/Redirect/__mocks__'; @@ -351,17 +352,17 @@ describe('ProposalComp page', () => { const againstVoteSummary = await waitFor(async () => within(screen.getByTestId(TEST_IDS.voteSummary.against)), ); - againstVoteSummary.getByText(voters.result[0].address); + againstVoteSummary.getByText(truncateAddress(voters.result[0].address)); const forVoteSummary = await waitFor(async () => within(screen.getByTestId(TEST_IDS.voteSummary.for)), ); - forVoteSummary.getByText(voters.result[1].address); + forVoteSummary.getByText(truncateAddress(voters.result[1].address)); const abstainVoteSummary = await waitFor(async () => within(screen.getByTestId(TEST_IDS.voteSummary.abstain)), ); - abstainVoteSummary.getByText(voters.result[2].address); + abstainVoteSummary.getByText(truncateAddress(voters.result[2].address)); }); it.each([ diff --git a/apps/evm/src/pages/Proposal/index.tsx b/apps/evm/src/pages/Proposal/index.tsx index 98b54b212d..370fc5cbb9 100644 --- a/apps/evm/src/pages/Proposal/index.tsx +++ b/apps/evm/src/pages/Proposal/index.tsx @@ -105,7 +105,7 @@ export const ProposalUi: React.FC = ({ votedTotalMantissa={proposal.totalVotesMantissa} voters={proposal.forVotes} openVoteModal={() => setVoteModalType(1)} - progressBarColor={styles.successColor} + progressBarClassName="bg-green" votingEnabled={shouldEnableVoteButtons} data-testid={TEST_IDS.voteSummary.for} /> @@ -116,7 +116,7 @@ export const ProposalUi: React.FC = ({ votedTotalMantissa={proposal.totalVotesMantissa} voters={proposal.againstVotes} openVoteModal={() => setVoteModalType(0)} - progressBarColor={styles.againstColor} + progressBarClassName="bg-red" votingEnabled={shouldEnableVoteButtons} data-testid={TEST_IDS.voteSummary.against} /> @@ -127,7 +127,7 @@ export const ProposalUi: React.FC = ({ votedTotalMantissa={proposal.totalVotesMantissa} voters={proposal.abstainVotes} openVoteModal={() => setVoteModalType(2)} - progressBarColor={styles.abstainColor} + progressBarClassName="bg-grey" votingEnabled={shouldEnableVoteButtons} data-testid={TEST_IDS.voteSummary.abstain} /> diff --git a/apps/evm/src/pages/Proposal/styles.ts b/apps/evm/src/pages/Proposal/styles.ts index 64ac57a295..7bc4816903 100644 --- a/apps/evm/src/pages/Proposal/styles.ts +++ b/apps/evm/src/pages/Proposal/styles.ts @@ -1,8 +1,6 @@ import { css } from '@emotion/react'; -import { useTheme } from '@mui/material'; export const useStyles = () => { - const theme = useTheme(); return { root: css` display: flex; @@ -11,8 +9,5 @@ export const useStyles = () => { spinner: css` height: 100%; `, - successColor: theme.palette.interactive.success, - againstColor: theme.palette.interactive.error, - abstainColor: theme.palette.text.secondary, }; }; diff --git a/apps/evm/src/pages/Trade/OperationForm/ManagePositionTabs/CollateralTabs/WithdrawForm/__tests__/__snapshots__/index.spec.tsx.snap b/apps/evm/src/pages/Trade/OperationForm/ManagePositionTabs/CollateralTabs/WithdrawForm/__tests__/__snapshots__/index.spec.tsx.snap index 6f411f2f2e..afa9275898 100644 --- a/apps/evm/src/pages/Trade/OperationForm/ManagePositionTabs/CollateralTabs/WithdrawForm/__tests__/__snapshots__/index.spec.tsx.snap +++ b/apps/evm/src/pages/Trade/OperationForm/ManagePositionTabs/CollateralTabs/WithdrawForm/__tests__/__snapshots__/index.spec.tsx.snap @@ -1,3 +1,3 @@ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html -exports[`WithdrawForm > renders form when data is ready 1`] = `"CollateralXVSAvailable70.440666 XVS0%100%Collateral90Liq. priceN/AN/ANet APY0.75%0.75%Borrowed:$123.33LT:Liquidation threshold:$1.92K..Health factor15.6215.62HealthyWithdraw"`; +exports[`WithdrawForm > renders form when data is ready 1`] = `"CollateralXVSAvailable70.440666 XVS0%100%Collateral90Liq. priceN/AN/ANet APY0.75%0.75%Borrowed:$123.33LT:Liquidation threshold:$1.92KHealth factor15.6215.62HealthyWithdraw"`; diff --git a/apps/evm/src/pages/Trade/OperationForm/ManagePositionTabs/PositionTabs/IncreaseForm/__tests__/__snapshots__/index.spec.tsx.snap b/apps/evm/src/pages/Trade/OperationForm/ManagePositionTabs/PositionTabs/IncreaseForm/__tests__/__snapshots__/index.spec.tsx.snap index 59fdcbb2ed..4652cd1c08 100644 --- a/apps/evm/src/pages/Trade/OperationForm/ManagePositionTabs/PositionTabs/IncreaseForm/__tests__/__snapshots__/index.spec.tsx.snap +++ b/apps/evm/src/pages/Trade/OperationForm/ManagePositionTabs/PositionTabs/IncreaseForm/__tests__/__snapshots__/index.spec.tsx.snap @@ -1,3 +1,3 @@ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html -exports[`IncreaseForm > renders form when data is ready 1`] = `"Long≈ $100.02USDTShortBUSDAvailable180.138339 BUSD0%100%Long100200+ $100.02Short50Liq. priceN/AN/AEntry price0.50.5Net APY0.75%0.75%Borrowed:$123.33LT:Liquidation threshold:$1.92K..Health factor15.6215.62HealthyThis transaction involves low-value swaps that are likely to failIncreaseSlippage tolerance0.5%Price impact0.1%"`; +exports[`IncreaseForm > renders form when data is ready 1`] = `"Long≈ $100.02USDTShortBUSDAvailable180.138339 BUSD0%100%Long100200+ $100.02Short50Liq. priceN/AN/AEntry price0.50.5Net APY0.75%0.75%Borrowed:$123.33LT:Liquidation threshold:$1.92KHealth factor15.6215.62HealthyThis transaction involves low-value swaps that are likely to failIncreaseSlippage tolerance0.5%Price impact0.1%"`; diff --git a/apps/evm/src/pages/Trade/OperationForm/OpenForm/__tests__/__snapshots__/index.spec.tsx.snap b/apps/evm/src/pages/Trade/OperationForm/OpenForm/__tests__/__snapshots__/index.spec.tsx.snap index c325baa7a7..912bd4ef9f 100644 --- a/apps/evm/src/pages/Trade/OperationForm/OpenForm/__tests__/__snapshots__/index.spec.tsx.snap +++ b/apps/evm/src/pages/Trade/OperationForm/OpenForm/__tests__/__snapshots__/index.spec.tsx.snap @@ -1,3 +1,3 @@ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html -exports[`OpenForm > renders form when data is ready 1`] = `"Collateral2xXVSWallet balance100 XVSLongUSDTShortBUSDAvailable0 BUSD0%100%Collateral0Long0Short0Liq. price0.625Entry price0.5Net APY1.56%Borrowed:$123.33LT:Liquidation threshold:$1.92K..Health factor> 10015.62HealthyThis transaction involves low-value swaps that are likely to failOpenSlippage tolerance0.5%Price impact0.1%"`; +exports[`OpenForm > renders form when data is ready 1`] = `"Collateral2xXVSWallet balance100 XVSLongUSDTShortBUSDAvailable0 BUSD0%100%Collateral0Long0Short0Liq. price0.625Entry price0.5Net APY1.56%Borrowed:$123.33LT:Liquidation threshold:$1.92KHealth factor> 10015.62HealthyThis transaction involves low-value swaps that are likely to failOpenSlippage tolerance0.5%Price impact0.1%"`; diff --git a/apps/evm/src/pages/Trade/Positions/PositionList/RowFooter/index.tsx b/apps/evm/src/pages/Trade/Positions/PositionList/RowFooter/index.tsx index fc9450a7cd..b7c3dfbd3b 100644 --- a/apps/evm/src/pages/Trade/Positions/PositionList/RowFooter/index.tsx +++ b/apps/evm/src/pages/Trade/Positions/PositionList/RowFooter/index.tsx @@ -32,7 +32,7 @@ export const RowFooter: React.FC = ({ row, isOpen }) => { ]; return ( - + {isTransactionHistoryFeatureEnabled ? ( diff --git a/apps/evm/src/pages/VoterLeaderboard/LeaderboardTable/index.tsx b/apps/evm/src/pages/VoterLeaderboard/LeaderboardTable/index.tsx index 77758e7990..2d3bd433b1 100644 --- a/apps/evm/src/pages/VoterLeaderboard/LeaderboardTable/index.tsx +++ b/apps/evm/src/pages/VoterLeaderboard/LeaderboardTable/index.tsx @@ -38,7 +38,7 @@ export const LeaderboardTable: React.FC = ({ renderCell: (voter, rowIndex) => ( {rowIndex + 1 + offset} - + {({ innerContent }) => ( { + it('returns collateral tokens from every source in every yield group', () => { + const collateralTokens = getLiquidityHubCollateralTokens({ + yieldGroups: [ + { + ...xvsCoreYieldGroup, + sources: [xvsCoreSource, xvsFluxSource], + }, + xvsFluxYieldGroup, + ], + }); + + expect(collateralTokens).toEqual([ + ...xvsCoreSource.collateralTokens, + ...xvsFluxSource.collateralTokens, + ...xvsFluxSource.collateralTokens, + ]); + }); +}); diff --git a/apps/evm/src/utilities/getLiquidityHubCollateralTokens/index.ts b/apps/evm/src/utilities/getLiquidityHubCollateralTokens/index.ts new file mode 100644 index 0000000000..32f1cbef45 --- /dev/null +++ b/apps/evm/src/utilities/getLiquidityHubCollateralTokens/index.ts @@ -0,0 +1,8 @@ +import type { LiquidityHubYieldGroup } from 'types'; + +export const getLiquidityHubCollateralTokens = ({ + yieldGroups, +}: { + yieldGroups: LiquidityHubYieldGroup[]; +}) => + yieldGroups.flatMap(yieldGroup => yieldGroup.sources.flatMap(source => source.collateralTokens)); diff --git a/apps/evm/src/utilities/index.ts b/apps/evm/src/utilities/index.ts index 0c0f078a17..3b4491a662 100755 --- a/apps/evm/src/utilities/index.ts +++ b/apps/evm/src/utilities/index.ts @@ -72,6 +72,7 @@ export * from './isInstitutionalVault'; export * from './calculateDailyInterests'; export * from './getVaultCategoryName'; export * from './getTransactionName'; +export * from './getLiquidityHubCollateralTokens'; export * from './formatToTradePosition'; export * from './calculateUserMaxBorrowTokens'; export * from './calculateCollateralWithdrawLimits';