From 1507c81800b874cbfdf8d5f7c19649962246f00d Mon Sep 17 00:00:00 2001 From: JCNoguera <88061365+VmMad@users.noreply.github.com> Date: Thu, 14 Nov 2024 15:42:23 +0100 Subject: [PATCH] feat(wallet-dashboard): add staking confirmation screen --- apps/core/src/hooks/useGetValidatorsApy.ts | 10 +-- .../components/organisms/dialog/Dialog.tsx | 4 +- .../Dialogs/Staking/StakeDialog.tsx | 28 +++++--- .../Dialogs/Staking/views/ConfirmAndExit.tsx | 67 +++++++++++++++++++ .../Dialogs/Staking/views/EnterAmountView.tsx | 47 ++----------- .../views/StakingTransactionDetails.tsx | 55 +++++++++++++++ .../components/Dialogs/Staking/views/index.ts | 2 + 7 files changed, 159 insertions(+), 54 deletions(-) create mode 100644 apps/wallet-dashboard/components/Dialogs/Staking/views/ConfirmAndExit.tsx create mode 100644 apps/wallet-dashboard/components/Dialogs/Staking/views/StakingTransactionDetails.tsx diff --git a/apps/core/src/hooks/useGetValidatorsApy.ts b/apps/core/src/hooks/useGetValidatorsApy.ts index 42b9cc0f2b7..e8190b06d46 100644 --- a/apps/core/src/hooks/useGetValidatorsApy.ts +++ b/apps/core/src/hooks/useGetValidatorsApy.ts @@ -13,11 +13,13 @@ import { roundFloat } from '../utils/roundFloat'; const DEFAULT_APY_DECIMALS = 2; +export interface ValidatorApyData { + apy: number; + isApyApproxZero: boolean; +} + export interface ApyByValidator { - [validatorAddress: string]: { - apy: number; - isApyApproxZero: boolean; - }; + [validatorAddress: string]: ValidatorApyData; } // For small APY, show ~0% instead of 0% // If APY falls below 0.001, show ~0% instead of 0% since we round to 2 decimal places diff --git a/apps/ui-kit/src/lib/components/organisms/dialog/Dialog.tsx b/apps/ui-kit/src/lib/components/organisms/dialog/Dialog.tsx index ddd77e87df7..53971681633 100644 --- a/apps/ui-kit/src/lib/components/organisms/dialog/Dialog.tsx +++ b/apps/ui-kit/src/lib/components/organisms/dialog/Dialog.tsx @@ -21,7 +21,7 @@ const DialogOverlay = React.forwardRef< >(({ showCloseIcon, ...props }, ref) => ( @@ -70,7 +70,7 @@ const DialogContent = React.forwardRef< void; isOpen: boolean; handleClose: () => void; view: StakeDialogView; @@ -42,7 +43,6 @@ interface StakeDialogProps { } function StakeDialog({ - onSuccess, isTimelockedStaking, isOpen, handleClose, @@ -86,6 +86,9 @@ function StakeDialog({ const validators = Object.keys(rollingAverageApys ?? {}) ?? []; + const validatorApy = + rollingAverageApys && selectedValidator ? rollingAverageApys[selectedValidator] : null; + function handleBack(): void { setView(StakeDialogView.SelectValidator); } @@ -122,10 +125,8 @@ function StakeDialog({ transaction: newStakeData?.transaction, }, { - onSuccess: (tx) => { - if (onSuccess) { - onSuccess(tx.digest); - } + onSuccess: () => { + setView(StakeDialogView.TransactionDetails); }, }, ) @@ -156,7 +157,7 @@ function StakeDialog({ onNext={selectValidatorHandleNext} /> )} - {view === StakeDialogView.EnterAmount && ( + {view === StakeDialogView.EnterAmount && validatorApy !== null && ( setAmount(e.target.value)} onBack={handleBack} onStake={handleStake} + validatorApy={validatorApy} /> )} {view === StakeDialogView.Unstake && stakedDetails && ( @@ -173,6 +175,16 @@ function StakeDialog({ showActiveStatus /> )} + {view === StakeDialogView.TransactionDetails && validatorApy !== null && ( + + )} ); } diff --git a/apps/wallet-dashboard/components/Dialogs/Staking/views/ConfirmAndExit.tsx b/apps/wallet-dashboard/components/Dialogs/Staking/views/ConfirmAndExit.tsx new file mode 100644 index 00000000000..4d4df8f9379 --- /dev/null +++ b/apps/wallet-dashboard/components/Dialogs/Staking/views/ConfirmAndExit.tsx @@ -0,0 +1,67 @@ +// Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +import { + Button, + ButtonType, + Card, + CardBody, + CardImage, + CardType, + ImageShape, + ImageType, +} from '@iota/apps-ui-kit'; +import { ValidatorApyData } from '@iota/core'; +import { StakingTransactionDetails } from './StakingTransactionDetails'; +import { Validator } from './Validator'; +import { IotaLogoMark } from '@iota/ui-icons'; + +interface SuccessScreenViewProps { + validatorAddress: string; + gasBudget: string | number | null | undefined; + onConfirm: () => void; + amount: string; + symbol: string | undefined; + validatorApy: ValidatorApyData; +} + +export function SuccessScreenView({ + validatorAddress, + gasBudget, + onConfirm, + amount, + symbol, + validatorApy: { apy, isApyApproxZero }, +}: SuccessScreenViewProps): React.JSX.Element { + return ( +
+
+
+ + + + + + + + + + +
+
+ +
+
+
+ ); +} diff --git a/apps/wallet-dashboard/components/Dialogs/Staking/views/EnterAmountView.tsx b/apps/wallet-dashboard/components/Dialogs/Staking/views/EnterAmountView.tsx index 6451fa98644..53cccc31cb5 100644 --- a/apps/wallet-dashboard/components/Dialogs/Staking/views/EnterAmountView.tsx +++ b/apps/wallet-dashboard/components/Dialogs/Staking/views/EnterAmountView.tsx @@ -2,23 +2,14 @@ // SPDX-License-Identifier: Apache-2.0 import React from 'react'; -import { useFormatCoin, useBalance, CoinFormat } from '@iota/core'; +import { useFormatCoin, useBalance, CoinFormat, ValidatorApyData } from '@iota/core'; import { IOTA_TYPE_ARG } from '@iota/iota-sdk/utils'; -import { - Button, - ButtonType, - KeyValueInfo, - Panel, - Divider, - Input, - InputType, - Header, -} from '@iota/apps-ui-kit'; -import { useStakeTxnInfo } from '../hooks'; -import { useCurrentAccount, useIotaClientQuery } from '@iota/dapp-kit'; +import { Button, ButtonType, Input, InputType, Header } from '@iota/apps-ui-kit'; +import { useCurrentAccount } from '@iota/dapp-kit'; import { Validator } from './Validator'; import { StakedInfo } from './StakedInfo'; import { Layout, LayoutBody, LayoutFooter } from './Layout'; +import { StakingTransactionDetails } from './StakingTransactionDetails'; interface EnterAmountViewProps { selectedValidator: string; @@ -29,6 +20,7 @@ interface EnterAmountViewProps { showActiveStatus?: boolean; gasBudget?: string | number | null; handleClose: () => void; + validatorApy: ValidatorApyData; } function EnterAmountView({ @@ -39,11 +31,11 @@ function EnterAmountView({ onStake, gasBudget = 0, handleClose, + validatorApy, }: 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); @@ -53,10 +45,6 @@ function EnterAmountView({ IOTA_TYPE_ARG, CoinFormat.FULL, ); - const [gas, symbol] = useFormatCoin(gasBudget, IOTA_TYPE_ARG); - const { stakedRewardsStartEpoch, timeBeforeStakeRewardsRedeemableAgoDisplay } = useStakeTxnInfo( - system?.epoch, - ); return ( @@ -83,28 +71,7 @@ function EnterAmountView({ caption={`${maxTokenFormatted} ${maxTokenFormattedSymbol} Available`} /> - - -
- - - - -
-
+ diff --git a/apps/wallet-dashboard/components/Dialogs/Staking/views/StakingTransactionDetails.tsx b/apps/wallet-dashboard/components/Dialogs/Staking/views/StakingTransactionDetails.tsx new file mode 100644 index 00000000000..c8f6581f990 --- /dev/null +++ b/apps/wallet-dashboard/components/Dialogs/Staking/views/StakingTransactionDetails.tsx @@ -0,0 +1,55 @@ +// Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +import { Divider, KeyValueInfo, Panel } from '@iota/apps-ui-kit'; +import { useFormatCoin, ValidatorApyData } from '@iota/core'; +import { useIotaClientQuery } from '@iota/dapp-kit'; +import { IOTA_TYPE_ARG } from '@iota/iota-sdk/utils'; +import { useStakeTxnInfo } from '../hooks'; + +interface StakingTransactionDetailsProps extends ValidatorApyData { + gasBudget: string | number | null | undefined; +} + +export function StakingTransactionDetails({ + gasBudget, + apy, + isApyApproxZero, +}: StakingTransactionDetailsProps): React.JSX.Element { + const [gas, gasSymbol] = useFormatCoin(gasBudget, IOTA_TYPE_ARG); + const { data: system } = useIotaClientQuery('getLatestIotaSystemState'); + + const { stakedRewardsStartEpoch, timeBeforeStakeRewardsRedeemableAgoDisplay } = useStakeTxnInfo( + system?.epoch, + ); + + const apyDisplay = `${isApyApproxZero ? '~' : ''}${apy}%`; + + return ( + +
+ {apy !== null && apy !== undefined ? ( + + ) : null} + + + + + +
+
+ ); +} diff --git a/apps/wallet-dashboard/components/Dialogs/Staking/views/index.ts b/apps/wallet-dashboard/components/Dialogs/Staking/views/index.ts index 69e70ed7315..c4ccf943233 100644 --- a/apps/wallet-dashboard/components/Dialogs/Staking/views/index.ts +++ b/apps/wallet-dashboard/components/Dialogs/Staking/views/index.ts @@ -3,5 +3,7 @@ export { default as EnterAmountView } from './EnterAmountView'; export { default as SelectValidatorView } from './SelectValidatorView'; + export * from './DetailsView'; export * from './UnstakeView'; +export * from './StakingTransactionDetails';