diff --git a/packages/blockchain-link-types/src/common.ts b/packages/blockchain-link-types/src/common.ts
index 1c1a682ee2e5..b653f9016383 100644
--- a/packages/blockchain-link-types/src/common.ts
+++ b/packages/blockchain-link-types/src/common.ts
@@ -42,6 +42,10 @@ export interface TokenTransfer {
from?: string;
to?: string;
standard?: 'ERC20' | 'ERC1155' | 'ERC721';
+ multiTokenValues?: Array<{
+ id: string;
+ value: string;
+ }>;
}
export interface InternalTransfer {
diff --git a/packages/suite/src/components/suite/FormattedNftAmount.tsx b/packages/suite/src/components/suite/FormattedNftAmount.tsx
new file mode 100644
index 000000000000..490026e90f80
--- /dev/null
+++ b/packages/suite/src/components/suite/FormattedNftAmount.tsx
@@ -0,0 +1,66 @@
+import { WalletAccountTransaction } from '@wallet-types/index';
+import React from 'react';
+
+import styled from 'styled-components';
+import { SignValue } from '@suite-common/suite-types';
+import { HiddenPlaceholder } from './HiddenPlaceholder';
+import { Sign } from './Sign';
+import { TrezorLink } from '@suite-components';
+import { useSelector } from '@suite-hooks/useSelector';
+
+const Container = styled.span`
+ max-width: 100%;
+ display: flex;
+`;
+
+const Symbol = styled.span`
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+`;
+
+const StyledTrezorLink = styled(TrezorLink)`
+ color: ${({ theme }) => theme.TYPE_GREEN};
+ text-decoration: underline;
+`;
+
+interface FormattedNftAmountProps {
+ transfer: WalletAccountTransaction['tokens'][number];
+ signValue?: SignValue;
+ className?: string;
+ useLink?: boolean;
+}
+
+export const FormattedNftAmount = ({
+ transfer,
+ signValue,
+ className,
+ useLink,
+}: FormattedNftAmountProps) => {
+ const id = // use 0 index, haven't found an example where multiTokenValues.length > 1
+ transfer.standard === 'ERC1155' && transfer.multiTokenValues?.length
+ ? transfer.multiTokenValues[0].id
+ : transfer.amount;
+
+ const { selectedAccount } = useSelector(state => state.wallet);
+ const { network } = selectedAccount;
+ const explorerUrl = `${network?.explorer.tx.replace('tx', 'nft')}/${transfer.address}/${id}`;
+
+ return (
+
+
+ {!!signValue && }
+
+ ID
+
+ {useLink ? (
+ {id}
+ ) : (
+ {id}
+ )}
+
+ {transfer.symbol}
+
+
+ );
+};
diff --git a/packages/suite/src/components/suite/modals/TransactionDetail/components/AmountDetails.tsx b/packages/suite/src/components/suite/modals/TransactionDetail/components/AmountDetails.tsx
index 61970ff6dab8..dc1832bd1baf 100644
--- a/packages/suite/src/components/suite/modals/TransactionDetail/components/AmountDetails.tsx
+++ b/packages/suite/src/components/suite/modals/TransactionDetail/components/AmountDetails.tsx
@@ -10,8 +10,10 @@ import {
formatCardanoWithdrawal,
formatNetworkAmount,
getTxOperation,
+ isNftTokenTransfer,
} from '@suite-common/wallet-utils';
import BigNumber from 'bignumber.js';
+import { FormattedNftAmount } from '@suite-components/FormattedNftAmount';
const MainContainer = styled.div`
display: flex;
@@ -212,11 +214,19 @@ export const AmountDetails = ({ tx, isTestnet }: AmountDetailsProps) => {
) : undefined
}
secondColumn={
-
+ isNftTokenTransfer(t) ? (
+
+ ) : (
+
+ )
}
// no history rates available for tokens
thirdColumn={null}
diff --git a/packages/suite/src/components/suite/modals/TransactionDetail/components/IODetails.tsx b/packages/suite/src/components/suite/modals/TransactionDetail/components/IODetails.tsx
index 47788b49ff4a..63a2ad6b39b0 100644
--- a/packages/suite/src/components/suite/modals/TransactionDetail/components/IODetails.tsx
+++ b/packages/suite/src/components/suite/modals/TransactionDetail/components/IODetails.tsx
@@ -1,8 +1,8 @@
-import React, { ReactElement } from 'react';
+import React, { ReactElement, ReactNode } from 'react';
import styled, { css } from 'styled-components';
import { WalletAccountTransaction } from '@suite-common/wallet-types';
-import { formatAmount, formatNetworkAmount } from '@suite-common/wallet-utils';
+import { formatAmount, formatNetworkAmount, isNftTokenTransfer } from '@suite-common/wallet-utils';
import { FormattedCryptoAmount, Translation } from '@suite-components';
import { useSelector } from '@suite-hooks/useSelector';
import { AnonymitySet, TokenTransfer } from '@trezor/blockchain-link';
@@ -10,6 +10,7 @@ import { Icon, useTheme, variables, CollapsibleBox } from '@trezor/components';
import { UtxoAnonymity } from '@wallet-components';
import { TxAddressOverflow } from './TxAddressOverflow';
import { AnalyzeInBlockbookBanner } from './AnalyzeInBlockbookBanner';
+import { FormattedNftAmount } from '@suite-components/FormattedNftAmount';
export const blurFix = css`
margin-left: -10px;
@@ -105,6 +106,12 @@ const StyledFormattedCryptoAmount = styled(FormattedCryptoAmount)`
display: block;
`;
+const StyledFormattedNftAmount = styled(FormattedNftAmount)`
+ color: ${({ theme }) => theme.BG_GREEN};
+ margin-top: 4px;
+ display: block;
+`;
+
const GridGroup = styled.div`
&:not(:last-of-type) {
margin-bottom: 8px;
@@ -189,17 +196,10 @@ interface GridRowGroupComponentProps {
from?: string;
to?: string;
symbol: string;
- formattedInValue?: string;
- formattedOutValue?: string;
+ amount?: string | ReactNode;
}
-const GridRowGroupComponent = ({
- from,
- to,
- symbol,
- formattedInValue,
- formattedOutValue,
-}: GridRowGroupComponentProps) => {
+const GridRowGroupComponent = ({ from, to, symbol, amount }: GridRowGroupComponentProps) => {
const theme = useTheme();
return (
@@ -207,11 +207,10 @@ const GridRowGroupComponent = ({
- {formattedInValue && (
-
- )}
- {formattedOutValue && (
-
+ {typeof amount === 'string' ? (
+
+ ) : (
+ amount
)}
@@ -261,7 +260,7 @@ const EthereumSpecificBalanceDetailsRow = ({ tx }: EthereumSpecificBalanceDetail
key={index}
from={t.from}
to={t.to}
- formattedInValue={formatNetworkAmount(t.amount, tx.symbol)}
+ amount={formatNetworkAmount(t.amount, tx.symbol)}
symbol={tx.symbol}
/>
))}
@@ -285,7 +284,13 @@ const EthereumSpecificBalanceDetailsRow = ({ tx }: EthereumSpecificBalanceDetail
key={index}
from={t.from}
to={t.to}
- formattedInValue={formatAmount(t.amount, t.decimals)}
+ amount={
+ isNftTokenTransfer(t) ? (
+
+ ) : (
+ formatAmount(t.amount, t.decimals)
+ )
+ }
symbol={t.symbol}
/>
))}
@@ -303,6 +308,7 @@ interface BalanceDetailsRowProps {
const BalanceDetailsRow = ({ tx }: BalanceDetailsRowProps) => {
const vout = tx?.details?.vout[0];
const vin = tx?.details?.vin[0];
+ const value = formatNetworkAmount(vin.value || vout.value || '', tx.symbol);
return vout.addresses?.[0] && vin.addresses?.[0] ? (
@@ -310,8 +316,7 @@ const BalanceDetailsRow = ({ tx }: BalanceDetailsRowProps) => {
diff --git a/packages/suite/src/components/wallet/TransactionItem/components/Target.tsx b/packages/suite/src/components/wallet/TransactionItem/components/Target.tsx
index 5c2997d5be8d..cdf2c3967926 100644
--- a/packages/suite/src/components/wallet/TransactionItem/components/Target.tsx
+++ b/packages/suite/src/components/wallet/TransactionItem/components/Target.tsx
@@ -1,5 +1,5 @@
import React from 'react';
-import styled from 'styled-components';
+import styled, { css } from 'styled-components';
import BigNumber from 'bignumber.js';
import { variables } from '@trezor/components';
import { getIsZeroValuePhishing } from '@suite-common/suite-utils';
@@ -19,6 +19,7 @@ import {
formatCardanoWithdrawal,
formatCardanoDeposit,
formatNetworkAmount,
+ isNftTokenTransfer,
} from '@suite-common/wallet-utils';
import { WalletAccountTransaction } from '@wallet-types';
import { notificationsActions } from '@suite-common/toast-notifications';
@@ -30,8 +31,9 @@ import { copyToClipboard } from '@trezor/dom-utils';
import { AccountMetadata } from '@suite-types/metadata';
import { ExtendedMessageDescriptor } from '@suite-types';
import { SignOperator } from '@suite-common/suite-types';
+import { FormattedNftAmount } from '@suite-components/FormattedNftAmount';
-export const StyledFormattedCryptoAmount = styled(FormattedCryptoAmount)`
+const amountStyle = css`
width: 100%;
color: ${({ theme }) => theme.TYPE_DARK_GREY};
font-size: ${variables.FONT_SIZE.NORMAL};
@@ -39,6 +41,14 @@ export const StyledFormattedCryptoAmount = styled(FormattedCryptoAmount)`
white-space: nowrap;
`;
+export const StyledFormattedCryptoAmount = styled(FormattedCryptoAmount)`
+ ${amountStyle}
+`;
+
+const StyledFormattedNftAmount = styled(FormattedNftAmount)`
+ ${amountStyle}
+`;
+
interface BaseTransfer {
singleRowLayout?: boolean;
useAnimation?: boolean;
@@ -57,6 +67,7 @@ export const TokenTransfer = ({
...baseLayoutProps
}: TokenTransferProps) => {
const operation = getTxOperation(transfer);
+ const isNft = isNftTokenTransfer(transfer);
const isZeroValuePhishing = getIsZeroValuePhishing(transaction);
return (
@@ -70,7 +81,9 @@ export const TokenTransfer = ({
/>
}
amount={
- !baseLayoutProps.singleRowLayout && (
+ isNft ? (
+
+ ) : (
+ ['ERC1155', 'ERC721'].includes(transfer.standard || '');
+
export const getTxIcon = (txType: WalletAccountTransaction['type']) => {
switch (txType) {
case 'recv':