= {
component: KeyValueInfo,
@@ -24,7 +24,6 @@ export const Default: Story = {
args: {
keyText: 'Label',
valueText: 'Value',
- showInfoIcon: false,
supportingLabel: 'IOTA',
size: ValueSize.Small,
},
@@ -35,8 +34,14 @@ export const Default: Story = {
valueText: {
control: 'text',
},
- showInfoIcon: {
- control: 'boolean',
+ tooltipText: {
+ control: 'text',
+ },
+ tooltipPosition: {
+ control: {
+ type: 'select',
+ options: Object.values(TooltipPosition),
+ },
},
supportingLabel: {
control: 'text',
diff --git a/apps/wallet/src/ui/app/staking/stake/StakeForm.tsx b/apps/wallet/src/ui/app/staking/stake/StakeForm.tsx
index cf360401f31..ae4e98c5da3 100644
--- a/apps/wallet/src/ui/app/staking/stake/StakeForm.tsx
+++ b/apps/wallet/src/ui/app/staking/stake/StakeForm.tsx
@@ -2,28 +2,24 @@
// Modifications Copyright (c) 2024 IOTA Stiftung
// SPDX-License-Identifier: Apache-2.0
-import { Card } from '_app/shared/card';
-import { Text } from '_app/shared/text';
-import { NumberInput } from '_components';
import {
NUM_OF_EPOCH_BEFORE_STAKING_REWARDS_REDEEMABLE,
NUM_OF_EPOCH_BEFORE_STAKING_REWARDS_STARTS,
} from '_src/shared/constants';
-import { CountDownTimer } from '_src/ui/app/shared/countdown-timer';
import {
createStakeTransaction,
parseAmount,
+ TimeUnit,
useCoinMetadata,
useFormatCoin,
useGetTimeBeforeEpochNumber,
+ useTimeAgo,
} from '@iota/core';
import { Field, Form, useFormikContext } from 'formik';
import { memo, useCallback, useMemo } from 'react';
-
import { useActiveAddress, useTransactionGasBudget } from '../../hooks';
import { type FormValues } from './StakingCard';
-
-const HIDE_MAX = true;
+import { Input, InputType, KeyValueInfo, Panel } from '@iota/apps-ui-kit';
export interface StakeFromProps {
validatorAddress: string;
@@ -33,7 +29,7 @@ export interface StakeFromProps {
}
function StakeForm({ validatorAddress, coinBalance, coinType, epoch }: StakeFromProps) {
- const { values, setFieldValue } = useFormikContext
();
+ const { values, setFieldValue, errors } = useFormikContext();
const { data: metadata } = useCoinMetadata(coinType);
const decimals = metadata?.decimals ?? 0;
@@ -41,6 +37,7 @@ function StakeForm({ validatorAddress, coinBalance, coinType, epoch }: StakeFrom
const transaction = useMemo(() => {
if (!values.amount || !decimals) return null;
+ if (Number(values.amount) < 0) return null;
const amountWithoutDecimals = parseAmount(values.amount, decimals);
return createStakeTransaction(amountWithoutDecimals, validatorAddress);
}, [values.amount, validatorAddress, decimals]);
@@ -63,100 +60,80 @@ function StakeForm({ validatorAddress, coinBalance, coinType, epoch }: StakeFrom
const { data: timeBeforeStakeRewardsStarts } =
useGetTimeBeforeEpochNumber(startEarningRewardsEpoch);
+ const timeBeforeStakeRewardsStartsAgo = useTimeAgo({
+ timeFrom: timeBeforeStakeRewardsStarts,
+ shortedTimeLabel: false,
+ shouldEnd: true,
+ maxTimeUnit: TimeUnit.ONE_HOUR,
+ });
+ const stakedRewardsStartEpoch =
+ timeBeforeStakeRewardsStarts > 0
+ ? `${timeBeforeStakeRewardsStartsAgo === '--' ? '' : 'in'} ${timeBeforeStakeRewardsStartsAgo}`
+ : epoch
+ ? `Epoch #${Number(startEarningRewardsEpoch)}`
+ : '--';
+
const { data: timeBeforeStakeRewardsRedeemable } =
useGetTimeBeforeEpochNumber(redeemableRewardsEpoch);
+ const timeBeforeStakeRewardsRedeemableAgo = useTimeAgo({
+ timeFrom: timeBeforeStakeRewardsRedeemable,
+ shortedTimeLabel: false,
+ shouldEnd: true,
+ maxTimeUnit: TimeUnit.ONE_HOUR,
+ });
+ const timeBeforeStakeRewardsRedeemableAgoDisplay =
+ timeBeforeStakeRewardsRedeemable > 0
+ ? `${timeBeforeStakeRewardsRedeemableAgo === '--' ? '' : 'in'} ${timeBeforeStakeRewardsRedeemableAgo}`
+ : epoch
+ ? `Epoch #${Number(redeemableRewardsEpoch)}`
+ : '--';
return (
-
- }
- footer={
-
-
-
- Staking Rewards Redeemable
-
-
-
- {timeBeforeStakeRewardsRedeemable > 0 ? (
-
- ) : (
-
- {epoch ? `Epoch #${Number(redeemableRewardsEpoch)}` : '--'}
-
- )}
-
+ }
+ errorMessage={errors.amount}
+ label="Amount"
+ />
+ )}
+ />
+
+
+
+
+
-
+
);
}
diff --git a/apps/wallet/src/ui/app/staking/stake/StakingCard.tsx b/apps/wallet/src/ui/app/staking/stake/StakingCard.tsx
index a6dfae7b77d..9d4db883105 100644
--- a/apps/wallet/src/ui/app/staking/stake/StakingCard.tsx
+++ b/apps/wallet/src/ui/app/staking/stake/StakingCard.tsx
@@ -2,11 +2,7 @@
// Modifications Copyright (c) 2024 IOTA Stiftung
// SPDX-License-Identifier: Apache-2.0
-import BottomMenuLayout, { Content, Menu } from '_app/shared/bottom-menu-layout';
-import { Button } from '_app/shared/ButtonUI';
-import { Collapsible } from '_app/shared/collapse';
-import { Text } from '_app/shared/text';
-import { Loading, Alert } from '_components';
+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';
@@ -23,9 +19,8 @@ import {
DELEGATED_STAKES_QUERY_STALE_TIME,
} from '@iota/core';
import { useIotaClientQuery } from '@iota/dapp-kit';
-import { ArrowLeft16 } from '@iota/icons';
import type { StakeObject } from '@iota/iota-sdk/client';
-import { NANO_PER_IOTA, IOTA_TYPE_ARG } from '@iota/iota-sdk/utils';
+import { NANO_PER_IOTA, IOTA_TYPE_ARG, formatAddress } from '@iota/iota-sdk/utils';
// import * as Sentry from '@sentry/react';
import { useMutation, useQueryClient } from '@tanstack/react-query';
import { Formik } from 'formik';
@@ -33,7 +28,6 @@ import type { FormikHelpers } from 'formik';
import { useCallback, useMemo } from 'react';
import { toast } from 'react-hot-toast';
import { Navigate, useNavigate, useSearchParams } from 'react-router-dom';
-
import { getSignerOperationErrorMessage } from '../../helpers/errorMessages';
import { useActiveAccount } from '../../hooks/useActiveAccount';
import { useSigner } from '../../hooks/useSigner';
@@ -43,6 +37,8 @@ import StakeForm from './StakeForm';
import { UnStakeForm } from './UnstakeForm';
import { createValidationSchema } from './utils/validation';
import { ValidatorFormDetail } from './ValidatorFormDetail';
+import { Button, ButtonType, Card, CardBody, CardImage, CardType } from '@iota/apps-ui-kit';
+import { ImageIcon } from '../../shared/image-icon';
const INITIAL_VALUES = {
amount: '',
@@ -73,6 +69,15 @@ function StakingCard() {
const { data: system, isPending: validatorsisPending } = useIotaClientQuery(
'getLatestIotaSystemState',
);
+ const validatorMeta = useMemo(() => {
+ if (!system) return null;
+
+ return (
+ system.activeValidators.find(
+ (validator) => validator.iotaAddress === validatorAddress,
+ ) || null
+ );
+ }, [validatorAddress, system]);
const totalTokenBalance = useMemo(() => {
if (!allDelegation) return 0n;
@@ -265,7 +270,7 @@ function StakingCard() {
return
;
}
return (
-
+
- {({ isSubmitting, isValid, submitForm, errors, touched }) => (
-
-
-
-
(
+ <>
+
+
+
+
+
+
-
-
+
+
{unstake ? (
)}
-
- {(unstake || touched.amount) && errors.amount ? (
-
- ) : null}
-
- {!unstake && (
-
-
-
- Staked IOTA starts counting as validator’s stake at
- the end of the Epoch in which it was staked. Rewards
- are earned separately for each Epoch and become
- available at the end of each Epoch.
-
-
-
- )}
-
-
-
- }
- text="Back"
- />
+
+
-
-
+
+ >
)}
diff --git a/apps/wallet/src/ui/app/staking/stake/ValidatorFormDetail.tsx b/apps/wallet/src/ui/app/staking/stake/ValidatorFormDetail.tsx
index 26b49932bda..e4be6021431 100644
--- a/apps/wallet/src/ui/app/staking/stake/ValidatorFormDetail.tsx
+++ b/apps/wallet/src/ui/app/staking/stake/ValidatorFormDetail.tsx
@@ -2,10 +2,7 @@
// Modifications Copyright (c) 2024 IOTA Stiftung
// SPDX-License-Identifier: Apache-2.0
-import { Card } from '_app/shared/card';
import { Alert, LoadingIndicator } from '_components';
-import { Text } from '_src/ui/app/shared/text';
-import { IconTooltip } from '_src/ui/app/shared/tooltip';
import {
calculateStakeShare,
formatPercentageDisplay,
@@ -13,16 +10,16 @@ import {
useGetValidatorsApy,
DELEGATED_STAKES_QUERY_REFETCH_INTERVAL,
DELEGATED_STAKES_QUERY_STALE_TIME,
+ useFormatCoin,
} from '@iota/core';
import { useIotaClientQuery } from '@iota/dapp-kit';
import { useMemo } from 'react';
import { useSearchParams } from 'react-router-dom';
-
import { useActiveAddress } from '../../hooks/useActiveAddress';
import { getStakeIotaByIotaId } from '../getStakeIotaByIotaId';
import { getTokenStakeIotaForValidator } from '../getTokenStakeIotaForValidator';
-import { StakeAmount } from '../home/StakeAmount';
-import { ValidatorLogo } from '../validators/ValidatorLogo';
+import { KeyValueInfo, Panel, TooltipPosition } from '@iota/apps-ui-kit';
+import { IOTA_TYPE_ARG } from '@iota/iota-sdk/utils';
interface ValidatorFormDetailProps {
validatorAddress: string;
@@ -31,7 +28,6 @@ interface ValidatorFormDetailProps {
export function ValidatorFormDetail({ validatorAddress, unstake }: ValidatorFormDetailProps) {
const accountAddress = useActiveAddress();
-
const [searchParams] = useSearchParams();
const stakeIdParams = searchParams.get('staked');
const {
@@ -88,6 +84,11 @@ export function ValidatorFormDetail({ validatorAddress, unstake }: ValidatorForm
const { apy, isApyApproxZero } = rollingAverageApys?.[validatorAddress] ?? {
apy: null,
};
+ const [totalValidatorStakeFormatted, totalValidatorStakeSymbol] = useFormatCoin(
+ totalValidatorStake,
+ IOTA_TYPE_ARG,
+ );
+ const [totalStakeFormatted, totalStakeSymbol] = useFormatCoin(totalStake, IOTA_TYPE_ARG);
if (isPending || loadingValidators) {
return (
@@ -111,75 +112,38 @@ export function ValidatorFormDetail({ validatorAddress, unstake }: ValidatorForm
return (
- {validatorData && (
-
-
-
- }
- footer={
- !unstake && (
- <>
-
- Your Staked IOTA
-
-
-
- >
- )
- }
- >
-
-
-
-
- Staking APY
-
-
-
-
-
- {formatPercentageDisplay(apy, '--', isApyApproxZero)}
-
-
-
-
-
- Stake Share
-
-
-
-
-
- {formatPercentageDisplay(totalStakePercentage)}
-
-
-
- {!unstake && (
-
-
-
- Total Staked
-
-
-
-
-
- )}
-
-
- )}
+
+
+
+
+ {!unstake && (
+
+ )}
+
+
+
);
}