diff --git a/apps/core/src/hooks/stake/useValidatorInfo.tsx b/apps/core/src/hooks/stake/useValidatorInfo.tsx index 5c960ea3587..3b8ec1bacf7 100644 --- a/apps/core/src/hooks/stake/useValidatorInfo.tsx +++ b/apps/core/src/hooks/stake/useValidatorInfo.tsx @@ -2,20 +2,11 @@ // SPDX-License-Identifier: Apache-2.0 import { useMemo } from 'react'; import { useIotaClientQuery } from '@iota/dapp-kit'; -import { useGetValidatorsApy, useGetDelegatedStake } from '../'; -import { - DELEGATED_STAKES_QUERY_REFETCH_INTERVAL, - DELEGATED_STAKES_QUERY_STALE_TIME, -} from '../../constants'; +import { useGetValidatorsApy } from '../'; -export function useValidatorInfo(validatorAddress: string) { +export function useValidatorInfo({ validatorAddress }: { validatorAddress: string }) { const { data: system } = useIotaClientQuery('getLatestIotaSystemState'); const { data: rollingAverageApys } = useGetValidatorsApy(); - const { data: delegatedStake } = useGetDelegatedStake({ - address: validatorAddress || '', - staleTime: DELEGATED_STAKES_QUERY_STALE_TIME, - refetchInterval: DELEGATED_STAKES_QUERY_REFETCH_INTERVAL, - }); const validatorSummary = useMemo(() => { if (!system) return null; @@ -47,7 +38,6 @@ export function useValidatorInfo(validatorAddress: string) { return { system, - delegatedStake, validatorSummary, name: validatorSummary?.name || '', stakingPoolActivationEpoch, diff --git a/apps/wallet-dashboard/app/(protected)/staking/page.tsx b/apps/wallet-dashboard/app/(protected)/staking/page.tsx index b40c42bf312..42f6a19d026 100644 --- a/apps/wallet-dashboard/app/(protected)/staking/page.tsx +++ b/apps/wallet-dashboard/app/(protected)/staking/page.tsx @@ -50,7 +50,6 @@ function StakingDashboardPage(): JSX.Element { function handleNewStake() { setIsDialogStakeOpen(true); } - console.log(stakedDetails); return ( <> diff --git a/apps/wallet-dashboard/components/Dialogs/Staking/StakeDialog.tsx b/apps/wallet-dashboard/components/Dialogs/Staking/StakeDialog.tsx index 9433d86c8f0..4debae8e578 100644 --- a/apps/wallet-dashboard/components/Dialogs/Staking/StakeDialog.tsx +++ b/apps/wallet-dashboard/components/Dialogs/Staking/StakeDialog.tsx @@ -142,7 +142,7 @@ function StakeDialog({ validators={validators} onSelect={handleValidatorSelect} onNext={handleSelectValidatorNext} - isValidatorSelected={!!selectedValidator} + selectedValidator={selectedValidator} /> )} {step === Step.EnterAmount && ( @@ -153,7 +153,6 @@ function StakeDialog({ onChange={(e) => setAmount(e.target.value)} onBack={handleBack} onStake={handleStake} - isStakeDisabled={!amount} /> )} diff --git a/apps/wallet-dashboard/components/Dialogs/Staking/StakedDetailsDialog.tsx b/apps/wallet-dashboard/components/Dialogs/Staking/StakedDetailsDialog.tsx index 1de1afc9280..4f32601a2dc 100644 --- a/apps/wallet-dashboard/components/Dialogs/Staking/StakedDetailsDialog.tsx +++ b/apps/wallet-dashboard/components/Dialogs/Staking/StakedDetailsDialog.tsx @@ -47,7 +47,7 @@ interface StakeDialogProps { handleClose: () => void; } -export function DialogStakedDetails({ +export function StakedDetailsDialog({ handleClose, stakedDetails, showActiveStatus, @@ -62,8 +62,9 @@ export function DialogStakedDetails({ const [iotaEarnedFormatted, iotaEarnedSymbol] = useFormatCoin(iotaEarned, IOTA_TYPE_ARG); const [totalStakeFormatted, totalStakeSymbol] = useFormatCoin(totalStake, IOTA_TYPE_ARG); - const { name, commission, newValidator, isAtRisk, apy, isApyApproxZero } = - useValidatorInfo(validatorAddress); + const { name, commission, newValidator, isAtRisk, apy, isApyApproxZero } = useValidatorInfo({ + validatorAddress: validatorAddress, + }); const { data: unstakeData } = useUnstakeTransaction( stakedDetails.stakedIotaId, @@ -89,7 +90,7 @@ export function DialogStakedDetails({ } function handleAddNewStake() { - console.log('Stake'); + // pass } if (loadingValidators) { diff --git a/apps/wallet-dashboard/components/Dialogs/Staking/index.ts b/apps/wallet-dashboard/components/Dialogs/Staking/index.ts index 1e5ad764bbc..e415159b7c5 100644 --- a/apps/wallet-dashboard/components/Dialogs/Staking/index.ts +++ b/apps/wallet-dashboard/components/Dialogs/Staking/index.ts @@ -2,3 +2,4 @@ // SPDX-License-Identifier: Apache-2.0 export { default as StakeDialog } from './StakeDialog'; +export * from './StakedDetailsDialog'; diff --git a/apps/wallet-dashboard/components/Dialogs/Staking/views/EnterAmountView.tsx b/apps/wallet-dashboard/components/Dialogs/Staking/views/EnterAmountView.tsx index 0d63c25b30e..c01cdd6d9ed 100644 --- a/apps/wallet-dashboard/components/Dialogs/Staking/views/EnterAmountView.tsx +++ b/apps/wallet-dashboard/components/Dialogs/Staking/views/EnterAmountView.tsx @@ -1,38 +1,22 @@ // Copyright (c) 2024 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -import React, { useMemo } from 'react'; -// import { Button } from '@/components'; -import { - ImageIcon, - ImageIconSize, - formatPercentageDisplay, - calculateStakeShare, - useFormatCoin, - getTokenStakeIotaForValidator, - useBalance, - CoinFormat, -} from '@iota/core'; -import { IOTA_TYPE_ARG, formatAddress } from '@iota/iota-sdk/utils'; -import { useValidatorInfo } from '@/hooks'; +import React from 'react'; +import { useFormatCoin, useBalance, CoinFormat } from '@iota/core'; +import { IOTA_TYPE_ARG } from '@iota/iota-sdk/utils'; import { Button, ButtonType, - Card, - CardBody, - CardImage, - CardType, - Badge, - BadgeType, KeyValueInfo, Panel, - TooltipPosition, Divider, Input, InputType, } from '@iota/apps-ui-kit'; import { useStakeTxnInfo } from '../hooks'; -import { useCurrentAccount } from '@iota/dapp-kit'; +import { useCurrentAccount, useIotaClientQuery } from '@iota/dapp-kit'; +import { Validator } from './Validator'; +import { StakedInfo } from './StakedInfo'; interface EnterAmountViewProps { selectedValidator: string; @@ -40,24 +24,25 @@ interface EnterAmountViewProps { onChange: (e: React.ChangeEvent) => void; onBack: () => void; onStake: () => void; - isStakeDisabled: boolean; showActiveStatus?: boolean; gasBudget?: string | number | null; } function EnterAmountView({ - selectedValidator: validatorAddress, + selectedValidator: selectedValidatorAddress, amount, showActiveStatus, onChange, onBack, onStake, - isStakeDisabled, gasBudget = 0, }: EnterAmountViewProps): JSX.Element { const account = useCurrentAccount(); const accountAddress = account?.address; + + const { data: system } = useIotaClientQuery('getLatestIotaSystemState'); const { data: iotaBalance } = useBalance(accountAddress!); + const coinBalance = BigInt(iotaBalance?.totalBalance || 0); const maxTokenBalance = coinBalance - BigInt(Number(gasBudget)); const [maxTokenFormatted, maxTokenFormattedSymbol] = useFormatCoin( @@ -65,111 +50,19 @@ function EnterAmountView({ IOTA_TYPE_ARG, CoinFormat.FULL, ); - const { - name, - newValidator, - isAtRisk, - apy, - isApyApproxZero, - validatorSummary, - delegatedStake: stakeData, - system, - } = useValidatorInfo(validatorAddress); const [gas, symbol] = useFormatCoin(gasBudget, IOTA_TYPE_ARG); const { stakedRewardsStartEpoch, timeBeforeStakeRewardsRedeemableAgoDisplay } = useStakeTxnInfo( system?.epoch, ); - const totalStake = useMemo(() => { - if (!stakeData) return 0n; - return getTokenStakeIotaForValidator(stakeData, validatorAddress); - }, [stakeData, validatorAddress]); - - const totalValidatorsStake = useMemo(() => { - if (!system) return 0; - return system.activeValidators.reduce( - (acc, curr) => (acc += BigInt(curr.stakingPoolIotaBalance)), - 0n, - ); - }, [system]); - - const totalStakePercentage = useMemo(() => { - if (!system || !validatorSummary) return null; - - return calculateStakeShare( - BigInt(validatorSummary.stakingPoolIotaBalance), - BigInt(totalValidatorsStake), - ); - }, [system, totalValidatorsStake, validatorSummary]); - - //TODO: verify this is the correct validator stake balance - const totalValidatorStake = validatorSummary?.stakingPoolIotaBalance || 0; - - const [totalValidatorStakeFormatted, totalValidatorStakeSymbol] = useFormatCoin( - totalValidatorStake, - IOTA_TYPE_ARG, - ); - const [totalStakeFormatted, totalStakeSymbol] = useFormatCoin(totalStake, IOTA_TYPE_ARG); - - const subtitle = showActiveStatus ? ( -
- {formatAddress(validatorAddress)} - {newValidator && } - {isAtRisk && } -
- ) : ( - formatAddress(validatorAddress) - ); return (
- - - - - - - - -
- - - - -
-
- + +
diff --git a/apps/wallet-dashboard/components/Dialogs/Staking/views/SelectValidatorView.tsx b/apps/wallet-dashboard/components/Dialogs/Staking/views/SelectValidatorView.tsx index 057a7aff162..fc244d4f203 100644 --- a/apps/wallet-dashboard/components/Dialogs/Staking/views/SelectValidatorView.tsx +++ b/apps/wallet-dashboard/components/Dialogs/Staking/views/SelectValidatorView.tsx @@ -2,81 +2,39 @@ // SPDX-License-Identifier: Apache-2.0 import React from 'react'; -import { ImageIcon, ImageIconSize, formatPercentageDisplay } from '@iota/core'; -import { - Button, - Card, - CardBody, - CardImage, - CardAction, - CardActionType, - CardType, - Badge, - BadgeType, -} from '@iota/apps-ui-kit'; -import { formatAddress } from '@iota/iota-sdk/utils'; -import { useValidatorInfo } from '@/hooks'; +import { Button } from '@iota/apps-ui-kit'; +import { Validator } from './Validator'; interface SelectValidatorViewProps { validators: string[]; onSelect: (validator: string) => void; onNext: () => void; - isValidatorSelected: boolean; + selectedValidator: string; } function SelectValidatorView({ validators, onSelect, onNext, - isValidatorSelected, + selectedValidator, }: SelectValidatorViewProps): JSX.Element { return (
-
+
{validators.map((validator) => ( - + ))}
- {isValidatorSelected && ( + {!!selectedValidator && (
); } -function Validator({ - address, - showActiveStatus, - onClick, -}: { - address: string; - showActiveStatus?: boolean; - onClick: (address: string) => void; -}) { - const { name, newValidator, isAtRisk, apy, isApyApproxZero } = useValidatorInfo(address); - - const subtitle = showActiveStatus ? ( -
- {formatAddress(address)} - {newValidator && } - {isAtRisk && } -
- ) : ( - formatAddress(address) - ); - return ( - onClick(address)}> - - - - - - - ); -} - export default SelectValidatorView; diff --git a/apps/wallet-dashboard/components/Dialogs/Staking/views/StakedInfo.tsx b/apps/wallet-dashboard/components/Dialogs/Staking/views/StakedInfo.tsx new file mode 100644 index 00000000000..024f9f9384a --- /dev/null +++ b/apps/wallet-dashboard/components/Dialogs/Staking/views/StakedInfo.tsx @@ -0,0 +1,100 @@ +// Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 +import { useMemo } from 'react'; +import { IOTA_TYPE_ARG } from '@iota/iota-sdk/utils'; +import { + formatPercentageDisplay, + calculateStakeShare, + useFormatCoin, + getTokenStakeIotaForValidator, + useGetDelegatedStake, + DELEGATED_STAKES_QUERY_REFETCH_INTERVAL, + DELEGATED_STAKES_QUERY_STALE_TIME, +} from '@iota/core'; +import { KeyValueInfo, Panel, TooltipPosition } from '@iota/apps-ui-kit'; +import { useValidatorInfo } from '@/hooks'; + +export function StakedInfo({ + validatorAddress, + accountAddress, +}: { + validatorAddress: string; + accountAddress: string; +}) { + const { data: delegatedStake } = useGetDelegatedStake({ + address: accountAddress || '', + staleTime: DELEGATED_STAKES_QUERY_STALE_TIME, + refetchInterval: DELEGATED_STAKES_QUERY_REFETCH_INTERVAL, + }); + const { apy, isApyApproxZero, validatorSummary, system } = useValidatorInfo({ + validatorAddress: validatorAddress, + }); + + const totalValidatorsStake = useMemo(() => { + if (!system) return 0; + return system.activeValidators.reduce( + (acc, curr) => (acc += BigInt(curr.stakingPoolIotaBalance)), + 0n, + ); + }, [system]); + + const totalStakePercentage = useMemo(() => { + if (!system || !validatorSummary) return null; + + return calculateStakeShare( + BigInt(validatorSummary.stakingPoolIotaBalance), + BigInt(totalValidatorsStake), + ); + }, [system, totalValidatorsStake, validatorSummary]); + + const totalStake = useMemo(() => { + if (!delegatedStake) return 0n; + return getTokenStakeIotaForValidator(delegatedStake, validatorAddress); + }, [delegatedStake, validatorAddress]); + + //TODO: verify this is the correct validator stake balance + const totalValidatorStake = validatorSummary?.stakingPoolIotaBalance || 0; + + const [totalValidatorStakeFormatted, totalValidatorStakeSymbol] = useFormatCoin( + totalValidatorStake, + IOTA_TYPE_ARG, + ); + const [totalStakeFormatted, totalStakeSymbol] = useFormatCoin(totalStake, IOTA_TYPE_ARG); + + return ( + +
+ + + + +
+
+ ); +} diff --git a/apps/wallet-dashboard/components/Dialogs/Staking/views/Validator.tsx b/apps/wallet-dashboard/components/Dialogs/Staking/views/Validator.tsx new file mode 100644 index 00000000000..1e281433a77 --- /dev/null +++ b/apps/wallet-dashboard/components/Dialogs/Staking/views/Validator.tsx @@ -0,0 +1,61 @@ +// Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 +import { ImageIcon, ImageIconSize, formatPercentageDisplay } from '@iota/core'; +import { + Card, + CardBody, + CardImage, + CardAction, + CardActionType, + CardType, + Badge, + BadgeType, +} from '@iota/apps-ui-kit'; +import { formatAddress } from '@iota/iota-sdk/utils'; +import { useValidatorInfo } from '@/hooks'; + +export function Validator({ + address, + showActiveStatus, + onClick, + isSelected, + showAction = true, +}: { + isSelected: boolean; + address: string; + showActiveStatus?: boolean; + onClick?: (address: string) => void; + showAction?: boolean; +}) { + const { name, newValidator, isAtRisk, apy, isApyApproxZero } = useValidatorInfo({ + validatorAddress: address, + }); + + const subtitle = showActiveStatus ? ( +
+ {formatAddress(address)} + {newValidator && } + {isAtRisk && } +
+ ) : ( + formatAddress(address) + ); + + const handleClick = onClick ? () => onClick(address) : undefined; + + return ( + + + + + + {showAction && ( + + )} + + ); +} diff --git a/apps/wallet-dashboard/components/Popup/Popups/StakeDetailsPopup.tsx b/apps/wallet-dashboard/components/Popup/Popups/StakeDetailsPopup.tsx index fe84a7e3497..696e74549c5 100644 --- a/apps/wallet-dashboard/components/Popup/Popups/StakeDetailsPopup.tsx +++ b/apps/wallet-dashboard/components/Popup/Popups/StakeDetailsPopup.tsx @@ -43,7 +43,7 @@ function StakeDetailsPopup({ extendedStake, onClose }: StakeDetailsPopupProps): - +
);