diff --git a/apps/core/src/components/index.ts b/apps/core/src/components/index.ts index 9f005714aa5..33fa79344a8 100644 --- a/apps/core/src/components/index.ts +++ b/apps/core/src/components/index.ts @@ -7,3 +7,4 @@ export * from './Inputs'; export * from './QR'; export * from './collapsible'; export * from './providers'; +export * from './stake'; diff --git a/apps/wallet/src/ui/app/staking/home/StakedCard.tsx b/apps/core/src/components/stake/StakedCard.tsx similarity index 70% rename from apps/wallet/src/ui/app/staking/home/StakedCard.tsx rename to apps/core/src/components/stake/StakedCard.tsx index 64ef1a09757..dad5c6f091a 100644 --- a/apps/wallet/src/ui/app/staking/home/StakedCard.tsx +++ b/apps/core/src/components/stake/StakedCard.tsx @@ -2,22 +2,15 @@ // Modifications Copyright (c) 2024 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -import { NUM_OF_EPOCH_BEFORE_STAKING_REWARDS_REDEEMABLE } from '@iota/core'; -import { determineCountDownText } from '_src/ui/app/shared/countdown-timer'; -import { - type ExtendedDelegatedStake, - TimeUnit, - useFormatCoin, - useGetTimeBeforeEpochNumber, - useTimeAgo, - ImageIcon, -} from '@iota/core'; import { IOTA_TYPE_ARG } from '@iota/iota-sdk/utils'; import { Card, CardImage, CardType, CardBody, CardAction, CardActionType } from '@iota/apps-ui-kit'; import { useMemo } from 'react'; -import { Link } from 'react-router-dom'; - import { useIotaClientQuery } from '@iota/dapp-kit'; +import { ImageIcon } from '../icon'; +import { determineCountDownText, ExtendedDelegatedStake } from '../../utils'; +import { TimeUnit, useFormatCoin, useGetTimeBeforeEpochNumber, useTimeAgo } from '../../hooks'; +import { NUM_OF_EPOCH_BEFORE_STAKING_REWARDS_REDEEMABLE } from '../../constants'; +import React from 'react'; export enum StakeState { WarmUp = 'WARM_UP', @@ -35,21 +28,22 @@ const STATUS_COPY: { [key in StakeState]: string } = { [StakeState.InActive]: 'Inactive', }; -interface StakeCardProps { +interface StakedCardProps { extendedStake: ExtendedDelegatedStake; currentEpoch: number; inactiveValidator?: boolean; + onClick: () => void; } // For delegationsRequestEpoch n through n + 2, show Start Earning // Show epoch number or date/time for n + 3 epochs -export function StakeCard({ +export function StakedCard({ extendedStake, currentEpoch, inactiveValidator = false, -}: StakeCardProps) { - const { stakedIotaId, principal, stakeRequestEpoch, estimatedReward, validatorAddress } = - extendedStake; + onClick, +}: StakedCardProps) { + const { principal, stakeRequestEpoch, estimatedReward, validatorAddress } = extendedStake; // TODO: Once two step withdraw is available, add cool down and withdraw now logic // For cool down epoch, show Available to withdraw add rewards to principal @@ -115,32 +109,20 @@ export function StakeCard({ }; return ( - - - - - - - + + - - + + + + ); } diff --git a/apps/core/src/components/stake/index.ts b/apps/core/src/components/stake/index.ts new file mode 100644 index 00000000000..e61e23e24a0 --- /dev/null +++ b/apps/core/src/components/stake/index.ts @@ -0,0 +1,4 @@ +// Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +export * from './StakedCard'; diff --git a/apps/core/src/constants/staking.constants.ts b/apps/core/src/constants/staking.constants.ts index 5c1a2ea2b7e..c93842c2408 100644 --- a/apps/core/src/constants/staking.constants.ts +++ b/apps/core/src/constants/staking.constants.ts @@ -9,3 +9,4 @@ export const DELEGATED_STAKES_QUERY_REFETCH_INTERVAL = 30_000; export const NUM_OF_EPOCH_BEFORE_STAKING_REWARDS_REDEEMABLE = 2; export const NUM_OF_EPOCH_BEFORE_STAKING_REWARDS_STARTS = 1; +export const MIN_NUMBER_IOTA_TO_STAKE = 1; diff --git a/apps/core/src/utils/determineCountDownText.ts b/apps/core/src/utils/determineCountDownText.ts new file mode 100644 index 00000000000..437a00b1e7a --- /dev/null +++ b/apps/core/src/utils/determineCountDownText.ts @@ -0,0 +1,16 @@ +// Copyright (c) Mysten Labs, Inc. +// Modifications Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +export function determineCountDownText({ + timeAgo, + label, + endLabel, +}: { + timeAgo: string; + label?: string; + endLabel?: string; +}): string { + const showLabel = timeAgo !== endLabel; + return showLabel ? `${label} ${timeAgo}` : timeAgo; +} diff --git a/apps/core/src/utils/index.ts b/apps/core/src/utils/index.ts index 9973f811e10..cfbe4e93729 100644 --- a/apps/core/src/utils/index.ts +++ b/apps/core/src/utils/index.ts @@ -21,6 +21,7 @@ export * from './api-env'; export * from './getExplorerPaths'; export * from './getExplorerLink'; export * from './truncateString'; +export * from './determineCountDownText'; export * from './stake'; export * from './transaction'; diff --git a/apps/ui-kit/src/lib/components/molecules/card/Card.tsx b/apps/ui-kit/src/lib/components/molecules/card/Card.tsx index 9c25e58d84b..1870f2e77c9 100644 --- a/apps/ui-kit/src/lib/components/molecules/card/Card.tsx +++ b/apps/ui-kit/src/lib/components/molecules/card/Card.tsx @@ -32,6 +32,10 @@ export interface CardProps { * Use case: When the card is wrapped with a Link component */ isHoverable?: boolean; + /** + * The 'data-testid' attribute value (used in e2e tests) + */ + testId?: string; } export function Card({ @@ -40,6 +44,7 @@ export function Card({ isHoverable, onClick, children, + testId, }: CardProps) { return (
{children}
diff --git a/apps/wallet-dashboard/app/(protected)/staking/page.tsx b/apps/wallet-dashboard/app/(protected)/staking/page.tsx index df257d13625..c26c1a39411 100644 --- a/apps/wallet-dashboard/app/(protected)/staking/page.tsx +++ b/apps/wallet-dashboard/app/(protected)/staking/page.tsx @@ -3,23 +3,43 @@ 'use client'; -import { AmountBox, Box, StakeCard, StakeDialog, Button, useStakeDialog } from '@/components'; +import { StartStaking } from '@/components/staking-overview/StartStaking'; +import { + Button, + ButtonSize, + ButtonType, + DisplayStats, + InfoBox, + InfoBoxStyle, + InfoBoxType, + Panel, + Title, + TitleSize, +} from '@iota/apps-ui-kit'; +import { StakeDialog } from '@/components'; +import { StakeDialogView } from '@/components/Dialogs/Staking/StakeDialog'; import { ExtendedDelegatedStake, formatDelegatedStake, - useFormatCoin, useGetDelegatedStake, useTotalDelegatedRewards, useTotalDelegatedStake, DELEGATED_STAKES_QUERY_REFETCH_INTERVAL, DELEGATED_STAKES_QUERY_STALE_TIME, + StakedCard, + useFormatCoin, } from '@iota/core'; -import { useCurrentAccount } from '@iota/dapp-kit'; +import { useCurrentAccount, useIotaClientQuery } from '@iota/dapp-kit'; +import { IotaSystemStateSummary } from '@iota/iota-sdk/client'; +import { Info } from '@iota/ui-icons'; +import { useMemo } from 'react'; +import { useStakeDialog } from '@/components/Dialogs/Staking/hooks/useStakeDialog'; import { IOTA_TYPE_ARG } from '@iota/iota-sdk/utils'; -import { StakeDialogView } from '@/components/Dialogs/Staking/StakeDialog'; function StakingDashboardPage(): JSX.Element { const account = useCurrentAccount(); + const { data: system } = useIotaClientQuery('getLatestIotaSystemState'); + const activeValidators = (system as IotaSystemStateSummary)?.activeValidators; const { isDialogStakeOpen, @@ -42,13 +62,29 @@ function StakingDashboardPage(): JSX.Element { const extendedStakes = delegatedStakeData ? formatDelegatedStake(delegatedStakeData) : []; const totalDelegatedStake = useTotalDelegatedStake(extendedStakes); const totalDelegatedRewards = useTotalDelegatedRewards(extendedStakes); - const [formattedDelegatedStake, stakeSymbol, stakeResult] = useFormatCoin( + const [totalDelegatedStakeFormatted, symbol] = useFormatCoin( totalDelegatedStake, IOTA_TYPE_ARG, ); - const [formattedDelegatedRewards, rewardsSymbol, rewardsResult] = useFormatCoin( - totalDelegatedRewards, - IOTA_TYPE_ARG, + const [totalDelegatedRewardsFormatted] = useFormatCoin(totalDelegatedRewards, IOTA_TYPE_ARG); + + const delegations = useMemo(() => { + return delegatedStakeData?.flatMap((delegation) => { + return delegation.stakes.map((d) => ({ + ...d, + // flag any inactive validator for the stakeIota object + // if the stakingPoolId is not found in the activeValidators list flag as inactive + inactiveValidator: !activeValidators?.find( + ({ stakingPoolId }) => stakingPoolId === delegation.stakingPool, + ), + validatorAddress: delegation.validatorAddress, + })); + }); + }, [activeValidators, delegatedStakeData]); + + // Check if there are any inactive validators + const hasInactiveValidatorDelegation = delegations?.some( + ({ inactiveValidator }) => inactiveValidator, ); const viewStakeDetails = (extendedStake: ExtendedDelegatedStake) => { @@ -57,46 +93,93 @@ function StakingDashboardPage(): JSX.Element { }; return ( - <> -
- - - -
-

List of stakes

- {extendedStakes?.map((extendedStake) => ( - - ))} +
+
+ {(delegatedStakeData?.length ?? 0) > 0 ? ( + + handleNewStake()} + size={ButtonSize.Small} + type={ButtonType.Primary} + text="Stake" + /> + } + /> + <div className="flex h-full w-full flex-col flex-nowrap gap-md p-md--rs"> + <div className="flex gap-xs"> + <DisplayStats + label="Your stake" + value={totalDelegatedStakeFormatted} + supportingLabel={symbol} + /> + <DisplayStats + label="Earned" + value={totalDelegatedRewardsFormatted} + supportingLabel={symbol} + /> + </div> + <Title title="In progress" size={TitleSize.Small} /> + <div className="flex max-h-[420px] w-full flex-1 flex-col items-start overflow-auto"> + {hasInactiveValidatorDelegation ? ( + <div className="mb-3"> + <InfoBox + type={InfoBoxType.Default} + title="Earn with active validators" + supportingText="Unstake IOTA from the inactive validators and stake on an active validator to start earning rewards again." + icon={<Info />} + style={InfoBoxStyle.Elevated} + /> + </div> + ) : null} + <div className="w-full gap-2"> + {system && + delegations + ?.filter(({ inactiveValidator }) => inactiveValidator) + .map((delegation) => ( + <StakedCard + extendedStake={delegation} + currentEpoch={Number(system.epoch)} + key={delegation.stakedIotaId} + inactiveValidator + onClick={() => viewStakeDetails(delegation)} + /> + ))} + </div> + <div className="w-full gap-2"> + {system && + delegations + ?.filter(({ inactiveValidator }) => !inactiveValidator) + .map((delegation) => ( + <StakedCard + extendedStake={delegation} + currentEpoch={Number(system.epoch)} + key={delegation.stakedIotaId} + onClick={() => viewStakeDetails(delegation)} + /> + ))} + </div> + </div> + </div> + <StakeDialog + stakedDetails={selectedStake} + isOpen={isDialogStakeOpen} + handleClose={handleCloseStakeDialog} + view={stakeDialogView} + setView={setStakeDialogView} + selectedValidator={selectedValidator} + setSelectedValidator={setSelectedValidator} + /> + </Panel> + ) : ( + <div className="flex h-[270px] p-lg"> + <StartStaking /> </div> - </Box> - <Button onClick={handleNewStake}>New Stake</Button> + )} </div> - {isDialogStakeOpen && stakeDialogView && ( - <StakeDialog - stakedDetails={selectedStake} - isOpen={isDialogStakeOpen} - handleClose={handleCloseStakeDialog} - view={stakeDialogView} - setView={setStakeDialogView} - selectedValidator={selectedValidator} - setSelectedValidator={setSelectedValidator} - /> - )} - </> + </div> ); } diff --git a/apps/wallet-dashboard/components/Cards/StakeCard.tsx b/apps/wallet-dashboard/components/Cards/StakeCard.tsx deleted file mode 100644 index 9e79abd31ad..00000000000 --- a/apps/wallet-dashboard/components/Cards/StakeCard.tsx +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -import React from 'react'; -import { Box, Button } from '@/components/index'; -import { ExtendedDelegatedStake } from '@iota/core'; - -interface StakeCardProps { - extendedStake: ExtendedDelegatedStake; - onDetailsClick: (extendedStake: ExtendedDelegatedStake) => void; -} - -function StakeCard({ extendedStake, onDetailsClick }: StakeCardProps): JSX.Element { - return ( - <Box> - <div>Validator: {extendedStake.validatorAddress}</div> - <div>Stake: {extendedStake.principal}</div> - {extendedStake.status === 'Active' && ( - <p>Estimated reward: {extendedStake.estimatedReward}</p> - )} - <div>Status: {extendedStake.status}</div> - <Button onClick={() => onDetailsClick(extendedStake)}>Details</Button> - </Box> - ); -} - -export default StakeCard; diff --git a/apps/wallet-dashboard/components/Cards/index.ts b/apps/wallet-dashboard/components/Cards/index.ts index da687047e80..a6bf6b83bcd 100644 --- a/apps/wallet-dashboard/components/Cards/index.ts +++ b/apps/wallet-dashboard/components/Cards/index.ts @@ -1,5 +1,4 @@ // Copyright (c) 2024 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -export { default as StakeCard } from './StakeCard'; export * from './VisualAssetDetailsCard'; diff --git a/apps/wallet-dashboard/components/Dialogs/Staking/StakeDialog.tsx b/apps/wallet-dashboard/components/Dialogs/Staking/StakeDialog.tsx index 6007798119b..bdd129698dd 100644 --- a/apps/wallet-dashboard/components/Dialogs/Staking/StakeDialog.tsx +++ b/apps/wallet-dashboard/components/Dialogs/Staking/StakeDialog.tsx @@ -18,6 +18,7 @@ import { useGetValidatorsApy, useBalance, createValidationSchema, + MIN_NUMBER_IOTA_TO_STAKE, } from '@iota/core'; import { FormikProvider, useFormik } from 'formik'; import type { FormikHelpers } from 'formik'; @@ -29,8 +30,6 @@ import { Dialog } from '@iota/apps-ui-kit'; import { DetailsView, UnstakeView } from './views'; import { FormValues } from './views/EnterAmountView'; -export const MIN_NUMBER_IOTA_TO_STAKE = 1; - export enum StakeDialogView { Details = 'Details', SelectValidator = 'SelectValidator', @@ -43,14 +42,13 @@ const INITIAL_VALUES = { }; interface StakeDialogProps { - isTimelockedStaking?: boolean; - onSuccess?: (digest: string) => void; isOpen: boolean; handleClose: () => void; - view: StakeDialogView; + isTimelockedStaking?: boolean; + onSuccess?: (digest: string) => void; + view?: StakeDialogView; setView?: (view: StakeDialogView) => void; stakedDetails?: ExtendedDelegatedStake | null; - selectedValidator?: string; setSelectedValidator?: (validator: string) => void; } diff --git a/apps/wallet-dashboard/components/staking-overview/StakingData.tsx b/apps/wallet-dashboard/components/staking-overview/StakingData.tsx index 21a4468fe30..746cba58e57 100644 --- a/apps/wallet-dashboard/components/staking-overview/StakingData.tsx +++ b/apps/wallet-dashboard/components/staking-overview/StakingData.tsx @@ -31,7 +31,7 @@ export function StakingData({ stakingData }: StakingDataProps) { <Panel> <Title title="Staking" /> <div className="flex h-full w-full items-center gap-md p-md--rs"> - <div className="w-1/2"> + <div className="w-1/2 text-primary-80"> <LabelText size={LabelTextSize.Large} label="Staked" diff --git a/apps/wallet/src/shared/constants.ts b/apps/wallet/src/shared/constants.ts index 73f2f49935d..0a6a18aab0d 100644 --- a/apps/wallet/src/shared/constants.ts +++ b/apps/wallet/src/shared/constants.ts @@ -5,8 +5,4 @@ export const ToS_LINK = 'https://www.iota.org/iota-wallet-tos'; export const FAQ_LINK = 'https://docs.iota.org/about-iota/iota-wallet/FAQ '; -// number of epochs before earning -// Staking Rewards Redeemable -export const MIN_NUMBER_IOTA_TO_STAKE = 1; - export const DEFAULT_APP_NAME = 'IOTA Wallet'; diff --git a/apps/wallet/src/ui/app/staking/delegation-detail/DelegationDetailCard.tsx b/apps/wallet/src/ui/app/staking/delegation-detail/DelegationDetailCard.tsx index c9ac00f7aec..d55abd8b0b7 100644 --- a/apps/wallet/src/ui/app/staking/delegation-detail/DelegationDetailCard.tsx +++ b/apps/wallet/src/ui/app/staking/delegation-detail/DelegationDetailCard.tsx @@ -4,7 +4,6 @@ import { useAppSelector } from '_hooks'; import { ampli } from '_src/shared/analytics/ampli'; -import { MIN_NUMBER_IOTA_TO_STAKE } from '_src/shared/constants'; import { useBalance, useCoinMetadata, @@ -14,6 +13,7 @@ import { DELEGATED_STAKES_QUERY_STALE_TIME, useFormatCoin, formatPercentageDisplay, + MIN_NUMBER_IOTA_TO_STAKE, } from '@iota/core'; import { useIotaClientQuery } from '@iota/dapp-kit'; import { Network, type StakeObject } from '@iota/iota-sdk/client'; diff --git a/apps/wallet/src/ui/app/staking/stake/StakingCard.tsx b/apps/wallet/src/ui/app/staking/stake/StakingCard.tsx index 57ae11b8108..a4f94a5de47 100644 --- a/apps/wallet/src/ui/app/staking/stake/StakingCard.tsx +++ b/apps/wallet/src/ui/app/staking/stake/StakingCard.tsx @@ -5,7 +5,6 @@ import { Loading } from '_components'; import { Coin } from '_redux/slices/iota-objects/Coin'; import { ampli } from '_src/shared/analytics/ampli'; -import { MIN_NUMBER_IOTA_TO_STAKE } from '_src/shared/constants'; import { createStakeTransaction, createUnstakeTransaction, @@ -17,6 +16,7 @@ import { DELEGATED_STAKES_QUERY_STALE_TIME, getStakeIotaByIotaId, createValidationSchema, + MIN_NUMBER_IOTA_TO_STAKE, } from '@iota/core'; import { useIotaClientQuery } from '@iota/dapp-kit'; import type { StakeObject } from '@iota/iota-sdk/client'; diff --git a/apps/wallet/src/ui/app/staking/validators/StatsDetail.tsx b/apps/wallet/src/ui/app/staking/validators/StatsDetail.tsx deleted file mode 100644 index ba8a478c00e..00000000000 --- a/apps/wallet/src/ui/app/staking/validators/StatsDetail.tsx +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright (c) Mysten Labs, Inc. -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -import { useFormatCoin } from '@iota/core'; -import { IOTA_TYPE_ARG } from '@iota/iota-sdk/utils'; - -interface DisplayStatsProps { - title: string; - balance: bigint | number | string; -} - -export function StatsDetail({ balance, title }: DisplayStatsProps) { - const [formatted, symbol] = useFormatCoin(balance, IOTA_TYPE_ARG); - - return ( - <div className="flex h-[96px] flex-1 flex-col justify-between rounded-xl bg-neutral-96 p-md dark:bg-neutral-12"> - <div className="text-label-sm text-neutral-10 dark:text-neutral-92">{title}</div> - - <div className="flex items-baseline gap-xxs"> - <div className="text-title-md text-neutral-10 dark:text-neutral-92"> - {formatted} - </div> - <div className="text-label-md text-neutral-10 opacity-40 dark:text-neutral-92"> - {symbol} - </div> - </div> - </div> - ); -} diff --git a/apps/wallet/src/ui/app/staking/validators/ValidatorsCard.tsx b/apps/wallet/src/ui/app/staking/validators/ValidatorsCard.tsx index d86c202cb97..606c51ec947 100644 --- a/apps/wallet/src/ui/app/staking/validators/ValidatorsCard.tsx +++ b/apps/wallet/src/ui/app/staking/validators/ValidatorsCard.tsx @@ -10,12 +10,12 @@ import { useTotalDelegatedStake, DELEGATED_STAKES_QUERY_REFETCH_INTERVAL, DELEGATED_STAKES_QUERY_STALE_TIME, + useFormatCoin, + StakedCard, } from '@iota/core'; import { useIotaClientQuery } from '@iota/dapp-kit'; import { useMemo } from 'react'; import { useActiveAddress } from '../../hooks/useActiveAddress'; -import { StakeCard } from '../home/StakedCard'; -import { StatsDetail } from '_app/staking/validators/StatsDetail'; import { Title, TitleSize, @@ -25,9 +25,11 @@ import { InfoBoxStyle, InfoBoxType, LoadingIndicator, + DisplayStats, } from '@iota/apps-ui-kit'; import { useNavigate } from 'react-router-dom'; import { Info, Warning } from '@iota/ui-icons'; +import { IOTA_TYPE_ARG } from '@iota/iota-sdk/utils'; export function ValidatorsCard() { const accountAddress = useActiveAddress(); @@ -50,6 +52,11 @@ export function ValidatorsCard() { // Total active stake for all Staked validators const totalDelegatedStake = useTotalDelegatedStake(delegatedStake); + const [totalDelegatedStakeFormatted, symbol] = useFormatCoin( + totalDelegatedStake, + IOTA_TYPE_ARG, + ); + const delegations = useMemo(() => { return delegatedStakeData?.flatMap((delegation) => { return delegation.stakes.map((d) => ({ @@ -72,6 +79,7 @@ export function ValidatorsCard() { // Get total rewards for all delegations const delegatedStakes = delegatedStakeData ? formatDelegatedStake(delegatedStakeData) : []; const totalDelegatedRewards = useTotalDelegatedRewards(delegatedStakes); + const [totalDelegatedRewardsFormatted] = useFormatCoin(totalDelegatedRewards, IOTA_TYPE_ARG); const handleNewStake = () => { ampli.clickedStakeIota({ @@ -106,8 +114,16 @@ export function ValidatorsCard() { return ( <div className="flex h-full w-full flex-col flex-nowrap"> <div className="flex gap-xs py-md"> - <StatsDetail title="Your stake" balance={totalDelegatedStake} /> - <StatsDetail title="Earned" balance={totalDelegatedRewards} /> + <DisplayStats + label="Your stake" + value={totalDelegatedStakeFormatted} + supportingLabel={symbol} + /> + <DisplayStats + label="Earned" + value={totalDelegatedRewardsFormatted} + supportingLabel={symbol} + /> </div> <Title title="In progress" size={TitleSize.Small} /> <div className="flex max-h-[420px] w-full flex-1 flex-col items-start overflow-auto"> @@ -128,11 +144,19 @@ validator to start earning rewards again." delegations ?.filter(({ inactiveValidator }) => inactiveValidator) .map((delegation) => ( - <StakeCard + <StakedCard extendedStake={delegation} currentEpoch={Number(system.epoch)} key={delegation.stakedIotaId} inactiveValidator + onClick={() => + navigate( + `/stake/delegation-detail?${new URLSearchParams({ + validator: delegation.validatorAddress, + staked: delegation.stakedIotaId, + }).toString()}`, + ) + } /> ))} </div> @@ -142,10 +166,18 @@ validator to start earning rewards again." delegations ?.filter(({ inactiveValidator }) => !inactiveValidator) .map((delegation) => ( - <StakeCard + <StakedCard extendedStake={delegation} currentEpoch={Number(system.epoch)} key={delegation.stakedIotaId} + onClick={() => + navigate( + `/stake/delegation-detail?${new URLSearchParams({ + validator: delegation.validatorAddress, + staked: delegation.stakedIotaId, + }).toString()}`, + ) + } /> ))} </div> diff --git a/apps/wallet/tests/staking.spec.ts b/apps/wallet/tests/staking.spec.ts index de6fc021c0c..c984efef3ab 100644 --- a/apps/wallet/tests/staking.spec.ts +++ b/apps/wallet/tests/staking.spec.ts @@ -42,8 +42,8 @@ test('staking', async ({ page, extensionUrl }) => { }); await page.getByText(`${STAKE_AMOUNT} IOTA`).click(); - await expect(page.getByTestId('stake-card')).toBeVisible({ timeout: LONG_TIMEOUT }); - await page.getByTestId('stake-card').click(); + await expect(page.getByTestId('staked-card')).toBeVisible({ timeout: LONG_TIMEOUT }); + await page.getByTestId('staked-card').click(); await page.getByText('Unstake').click(); await page.getByRole('button', { name: 'Unstake' }).click();