Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions apps/core/src/components/coin/AmountWithFiat.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// Copyright (c) 2026 IOTA Stiftung
// SPDX-License-Identifier: Apache-2.0

import { useIotaClientContext } from '@iota/dapp-kit';
import { type Network } from '@iota/iota-sdk/client';
import { IOTA_TYPE_ARG } from '@iota/iota-sdk/utils';
import { useBalanceInUSD } from '../../hooks';
import { CoinFiatValue } from './CoinFiatValue';

export interface AmountWithFiatProps {
amount: bigint | string | number;
formatted: string;
symbol?: string;
coinType?: string;
}

export function AmountWithFiat({
amount,
formatted,
symbol,
coinType = IOTA_TYPE_ARG,
}: AmountWithFiatProps) {
const { network } = useIotaClientContext();
const value = useBalanceInUSD(coinType, amount, network as Network);
const hasFiatValue = value !== null && value !== undefined && Math.abs(value) >= 0.005;

return (
<span className="flex flex-row flex-wrap items-baseline gap-x-1">
<span className="whitespace-nowrap">
{formatted}
{symbol ? ` ${symbol}` : ''}
</span>
{hasFiatValue && (
<span className="flex flex-row items-baseline gap-1 whitespace-nowrap [&>span]:!text-body-sm">
<span className="key-supporting-text-color text-body-sm">~</span>
<CoinFiatValue amount={amount} coinType={coinType} withParentheses={false} />
</span>
)}
</span>
);
}
1 change: 1 addition & 0 deletions apps/core/src/components/coin/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ export * from './CoinIcon';
export * from './CoinSelector';
export * from './CoinItem';
export * from './CoinFiatValue';
export * from './AmountWithFiat';
9 changes: 8 additions & 1 deletion apps/core/src/components/stake/StakedCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { ExtendedDelegatedStake } from '../../utils';
import { useFormatCoin, useStakeRewardStatus, useGetInactiveValidator } from '../../hooks';
import { RewardsOff, Warning } from '@iota/apps-ui-icons';
import { useIotaClientQuery } from '@iota/dapp-kit';
import { AmountWithFiat } from '../coin';

interface StakedCardProps {
extendedStake: ExtendedDelegatedStake;
Expand Down Expand Up @@ -68,7 +69,13 @@ export function StakedCard({
</CardImage>
<CardBody
title={name}
subtitle={`${principalStaked} ${symbol}`}
subtitle={
<AmountWithFiat
amount={principal}
formatted={principalStaked}
symbol={symbol}
/>
}
icon={
activeButNotInTheCommittee ? (
<RewardsOff className="text-iota-warning-60" />
Expand Down
82 changes: 64 additions & 18 deletions apps/core/src/components/stake/UnstakeBreakdown.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { Divider, KeyValueInfo } from '@iota/apps-ui-kit';
import { GAS_SYMBOL } from '../../constants';
import { useFormatCoin } from '../../hooks/useFormatCoin';
import type { UnstakeAmounts } from '../../utils/stake/calculateUnstakeAmounts';
import { AmountWithFiat } from '../coin';

interface UnstakeBreakdownProps {
isPartialUnstake: boolean;
Expand Down Expand Up @@ -32,40 +33,70 @@ export function UnstakeBreakdown({ isPartialUnstake, unstakeAmounts }: UnstakeBr
<>
<KeyValueInfo
keyText="Amount to Unstake"
value={unstakeAmountFormatted}
supportingLabel={GAS_SYMBOL}
value={
<AmountWithFiat
formatted={unstakeAmountFormatted}
symbol={GAS_SYMBOL}
amount={unstakeAmounts.unstakeAmount}
/>
}
fullwidth
/>
<KeyValueInfo
keyText="Rewards Earned"
value={rewardsFormatted}
supportingLabel={rewardSymbol}
value={
<AmountWithFiat
formatted={rewardsFormatted}
symbol={rewardSymbol}
amount={unstakeAmounts.proportionalRewards}
/>
}
fullwidth
/>
<Divider />
<KeyValueInfo
keyText="Remaining Stake"
value={remainingStakeFormatted}
supportingLabel={GAS_SYMBOL}
value={
<AmountWithFiat
formatted={remainingStakeFormatted}
symbol={GAS_SYMBOL}
amount={unstakeAmounts.remainingStake}
/>
}
fullwidth
/>
<KeyValueInfo
keyText="Remaining Rewards"
value={remainingRewardsFormatted}
supportingLabel={remainingRewardsSymbol}
value={
<AmountWithFiat
formatted={remainingRewardsFormatted}
symbol={remainingRewardsSymbol}
amount={unstakeAmounts.remainingRewards}
/>
}
fullwidth
/>
<Divider />
<KeyValueInfo
keyText="Total Unstaked IOTA"
value={totalUnstakeAmountFormatted}
supportingLabel={GAS_SYMBOL}
value={
<AmountWithFiat
formatted={totalUnstakeAmountFormatted}
symbol={GAS_SYMBOL}
amount={unstakeAmounts.totalUnstakeAmount}
/>
}
fullwidth
/>
<KeyValueInfo
keyText="Remaining Total Staked IOTA"
value={remainingTotalStakedFormatted}
supportingLabel={GAS_SYMBOL}
value={
<AmountWithFiat
formatted={remainingTotalStakedFormatted}
symbol={GAS_SYMBOL}
amount={unstakeAmounts.remainingTotalStaked}
/>
}
fullwidth
/>
</>
Expand All @@ -76,21 +107,36 @@ export function UnstakeBreakdown({ isPartialUnstake, unstakeAmounts }: UnstakeBr
<>
<KeyValueInfo
keyText="Your Stake"
value={unstakeAmountFormatted}
supportingLabel={GAS_SYMBOL}
value={
<AmountWithFiat
formatted={unstakeAmountFormatted}
symbol={GAS_SYMBOL}
amount={unstakeAmounts.unstakeAmount}
/>
}
fullwidth
/>
<KeyValueInfo
keyText="Rewards Earned"
value={rewardsFormatted}
supportingLabel={rewardSymbol}
value={
<AmountWithFiat
formatted={rewardsFormatted}
symbol={rewardSymbol}
amount={unstakeAmounts.proportionalRewards}
/>
}
fullwidth
/>
<Divider />
<KeyValueInfo
keyText="Total Unstaked IOTA"
value={totalUnstakeAmountFormatted}
supportingLabel={GAS_SYMBOL}
value={
<AmountWithFiat
formatted={totalUnstakeAmountFormatted}
symbol={GAS_SYMBOL}
amount={unstakeAmounts.totalUnstakeAmount}
/>
}
fullwidth
/>
</>
Expand Down
1 change: 1 addition & 0 deletions apps/core/src/hooks/useGetStakingValidatorDetails.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ export function useGetStakingValidatorDetails({
totalStake: totalStakeFormatted,
totalStakeOriginal: totalStake,
totalValidatorsStake: totalValidatorsStakeFormatted,
totalValidatorsStakeOriginal: totalValidatorStake,
totalStakePercentage,
validatorApy,
systemDataResult,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import cx from 'classnames';
import { SecondaryText } from '@/components/atoms/secondary-text';
import { LABEL_CLASSES } from './input.classes';
import { createElement } from 'react';
import type { ReactNode } from 'react';

export enum LabelHtmlTag {
Label = 'label',
Expand All @@ -19,7 +20,7 @@ export interface InputWrapperProps {
/**
* Shows a caption with the text below the input.
*/
caption?: string;
caption?: ReactNode;
/**
* Error Message. Overrides the caption.
*/
Expand Down
19 changes: 15 additions & 4 deletions apps/wallet-dashboard/app/(protected)/staking/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import {
useGetAllOwnedObjects,
TIMELOCK_IOTA_TYPE,
mapTimelockObjects,
AmountWithFiat,
} from '@iota/core';
import { useCurrentAccount, useIotaClient, useIotaClientQuery } from '@iota/dapp-kit';
import { useMemo } from 'react';
Expand Down Expand Up @@ -167,13 +168,23 @@ function StakingDashboardPage(): React.JSX.Element {
<div className="flex gap-xs">
<DisplayStats
label="Your stake"
value={totalDelegatedStakeFormatted}
supportingLabel={symbol}
value={
<AmountWithFiat
amount={totalDelegatedStake}
formatted={totalDelegatedStakeFormatted}
symbol={symbol}
/>
}
/>
<DisplayStats
label="Earned"
value={totalDelegatedRewardsFormatted}
supportingLabel={symbol}
value={
<AmountWithFiat
amount={totalDelegatedRewards}
formatted={totalDelegatedRewardsFormatted}
symbol={symbol}
/>
}
/>
</div>
<Title title="In progress" size={TitleSize.Small} />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
useIsActiveValidator,
useGetNextEpochCommitteeMember,
useGetInactiveValidator,
AmountWithFiat,
} from '@iota/core';
import {
Header,
Expand Down Expand Up @@ -164,14 +165,24 @@ export function DetailsView({
<div className="flex flex-col gap-y-sm p-md">
<KeyValueInfo
keyText="Your Stake"
value={totalStakeFormatted}
supportingLabel={totalStakeSymbol}
value={
<AmountWithFiat
amount={totalStake}
formatted={totalStakeFormatted}
symbol={totalStakeSymbol}
/>
}
fullwidth
/>
<KeyValueInfo
keyText="Earned"
value={iotaEarnedFormatted}
supportingLabel={iotaEarnedSymbol}
value={
<AmountWithFiat
amount={iotaEarned}
formatted={iotaEarnedFormatted}
symbol={iotaEarnedSymbol}
/>
}
fullwidth
/>
<Divider />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ interface FormValues {
interface EnterAmountDialogLayoutProps {
selectedValidator: string;
senderAddress: string;
caption: string;
caption: React.ReactNode;
renderInfo?: React.JSX.Element;
isLoading: boolean;
onBack: () => void;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
getGasBudgetErrorMessage,
NO_BALANCE_GENERIC_MESSAGE,
useValidatorInfo,
AmountWithFiat,
} from '@iota/core';
import { CoinFormat, IOTA_TYPE_ARG, parseAmount } from '@iota/iota-sdk/utils';
import { useFormikContext } from 'formik';
Expand Down Expand Up @@ -73,9 +74,18 @@ export function EnterAmountView({
balance: availableBalance,
format: CoinFormat.Full,
});
const caption = availableBalance
? `${availableBalanceFormatted} ${availableBalanceFormattedSymbol} Available`
: '--';
const caption = availableBalance ? (
<span className="flex flex-row items-baseline gap-1">
<AmountWithFiat
amount={availableBalance}
formatted={availableBalanceFormatted}
symbol={availableBalanceFormattedSymbol}
/>
<span>Available</span>
</span>
) : (
'--'
);

const gasUnstakeBuffer = gasSummary?.budget ? BigInt(gasSummary.budget) * BigInt(2) : BigInt(0);
const maxSafeAmount = availableBalance - gasUnstakeBuffer;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
toast,
getGasBudgetErrorMessage,
useCoinMetadata,
AmountWithFiat,
} from '@iota/core';
import { CoinFormat, IOTA_TYPE_ARG, parseAmount } from '@iota/iota-sdk/utils';
import { useFormikContext } from 'formik';
Expand Down Expand Up @@ -96,7 +97,16 @@ export function EnterTimelockedAmountView({
format: CoinFormat.Full,
});

const caption = `${maxTokenFormatted} ${maxTokenFormattedSymbol} Available`;
const caption = (
<span className="flex flex-row items-baseline gap-1">
<AmountWithFiat
amount={maxStakableTimelockedAmount}
formatted={maxTokenFormatted}
symbol={maxTokenFormattedSymbol}
/>
<span>Available</span>
</span>
);
const info = useMemo(() => {
if (isSearchingProtocolMaxAmount) {
let message = 'The current amount is not valid due to the large number of objects. ';
Expand Down
Loading
Loading