diff --git a/src/app/components/confirmBtcTransaction/itemRow/runeAmount.tsx b/src/app/components/confirmBtcTransaction/itemRow/runeAmount.tsx
new file mode 100644
index 000000000..cf01aa076
--- /dev/null
+++ b/src/app/components/confirmBtcTransaction/itemRow/runeAmount.tsx
@@ -0,0 +1,93 @@
+import TokenImage from '@components/tokenImage';
+import { FungibleToken } from '@secretkeylabs/xverse-core';
+import Avatar from '@ui-library/avatar';
+import { StyledP } from '@ui-library/common.styled';
+import { getFtTicker } from '@utils/tokens';
+import { useTranslation } from 'react-i18next';
+import { NumericFormat } from 'react-number-format';
+import styled from 'styled-components';
+
+type Props = {
+ amountSats: number;
+ token: FungibleToken;
+ amountToSend: string;
+};
+
+const Container = styled.div({
+ width: '100%',
+ display: 'flex',
+ flexDirection: 'row',
+ alignItems: 'center',
+});
+
+const AvatarContainer = styled.div`
+ margin-right: ${(props) => props.theme.space.xs};
+`;
+
+const ColumnContainer = styled.div({
+ width: '100%',
+ display: 'flex',
+ flexDirection: 'row',
+ justifyContent: 'space-between',
+ alignItems: 'center',
+ gap: '24px',
+ whiteSpace: 'nowrap',
+ overflow: 'hidden',
+});
+
+const NumberTypeContainer = styled.div`
+ text-align: right;
+ overflow: hidden;
+`;
+
+const EllipsisStyledP = styled(StyledP)`
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+`;
+
+export default function RuneAmount({ amountSats, token, amountToSend }: Props) {
+ const { t } = useTranslation('translation', { keyPrefix: 'CONFIRM_TRANSACTION' });
+
+ return (
+
+
+
+ }
+ />
+
+
+
+
+ {t('AMOUNT')}
+
+
+ {t('BITCOIN_VALUE')}
+
+
+
+ (
+ {value}
+ )}
+ />
+
+ {`${amountSats} ${t('SATS')}`}
+
+
+
+
+ );
+}
diff --git a/src/app/components/confirmBtcTransaction/transactionSummary.tsx b/src/app/components/confirmBtcTransaction/transactionSummary.tsx
index e81ff4f11..d49a5cc3a 100644
--- a/src/app/components/confirmBtcTransaction/transactionSummary.tsx
+++ b/src/app/components/confirmBtcTransaction/transactionSummary.tsx
@@ -4,7 +4,7 @@ import useWalletSelector from '@hooks/useWalletSelector';
import AssetModal from '@components/assetModal';
import TransferFeeView from '@components/transferFeeView';
import useBtcFeeRate from '@hooks/useBtcFeeRate';
-import { btcTransaction, getBtcFiatEquivalent } from '@secretkeylabs/xverse-core';
+import { btcTransaction, FungibleToken, getBtcFiatEquivalent } from '@secretkeylabs/xverse-core';
import SelectFeeRate from '@ui-components/selectFeeRate';
import Callout from '@ui-library/callout';
import { BLOG_LINK } from '@utils/constants';
@@ -38,10 +38,12 @@ const UnconfirmedInputCallout = styled(Callout)`
type Props = {
isPartialTransaction: boolean;
-
inputs: btcTransaction.EnhancedInput[];
outputs: btcTransaction.EnhancedOutput[];
feeOutput?: btcTransaction.TransactionFeeOutput;
+ token?: FungibleToken;
+ amountToSend?: string;
+ recipientAddress?: string;
getFeeForFeeRate?: (
feeRate: number,
useEffectiveFeeRate?: boolean,
@@ -52,10 +54,13 @@ type Props = {
};
function TransactionSummary({
+ isPartialTransaction,
inputs,
outputs,
feeOutput,
- isPartialTransaction,
+ token,
+ amountToSend,
+ recipientAddress,
isSubmitting,
getFeeForFeeRate,
onFeeRateSet,
@@ -104,6 +109,9 @@ function TransactionSummary({
const showFeeSelector = !!(feeRate && getFeeForFeeRate && onFeeRateSet);
+ // TODO - TEMP SOLUTION - we should detect this via the txContext (input/outputs) for proper PSBT support (v2)
+ const isRuneTransaction = token && token.protocol === 'runes' && amountToSend && recipientAddress;
+
return (
<>
{inscriptionToShow && (
@@ -125,31 +133,27 @@ function TransactionSummary({
anchorRedirect={`${BLOG_LINK}/rare-satoshis`}
/>
)}
-
{isUnConfirmedInput && (
)}
-
-
-
-
- {hasOutputScript &&
}
-
+ {!isRuneTransaction && hasOutputScript &&
}
-
{feeOutput && !showFeeSelector && (
({
padding: `0 ${props.theme.space.m}`,
}));
+const StyledStyledP = styled(StyledP)`
+ display: flex;
+ align-items: center;
+`;
+
+const StyledArrowRight = styled(ArrowRight)({
+ marginRight: 4,
+});
+
type Props = {
outputs: btcTransaction.EnhancedOutput[];
inputs: btcTransaction.EnhancedInput[];
isPartialTransaction: boolean;
netAmount: number;
onShowInscription: (inscription: btcTransaction.IOInscription) => void;
+ token?: FungibleToken;
+ amountToSend?: string;
+ recipientAddress?: string;
};
// if isPartialTransaction, we use inputs instead of outputs
@@ -49,6 +65,9 @@ function TransferSection({
isPartialTransaction,
netAmount,
onShowInscription,
+ token,
+ amountToSend,
+ recipientAddress,
}: Props) {
const { btcAddress, ordinalsAddress } = useWalletSelector();
const { t } = useTranslation('translation', { keyPrefix: 'CONFIRM_TRANSACTION' });
@@ -80,16 +99,27 @@ function TransferSection({
if (!hasData) return null;
+ const isRuneTransaction = token && amountToSend && recipientAddress;
+
return (
{t('YOU_WILL_TRANSFER')}
+ {isRuneTransaction && (
+
+
+ {getTruncatedAddress(recipientAddress, 6)}
+
+ )}
{showAmount && (
-
+ {isRuneTransaction && (
+
+ )}
+ {!isRuneTransaction && }
{
- let amountInCurrency;
- if (currencyType === 'FT') {
- amountInCurrency = new BigNumber(value).multipliedBy(fungibleToken?.tokenFiatRate!);
- if (amountInCurrency.isLessThan(0.01)) {
- amountInCurrency = '0.01';
- }
- } else {
- amountInCurrency = getFiatEquivalent(
+ setFiatAmount(
+ getFiatEquivalent(
Number(value),
currencyType,
BigNumber(stxBtcRate),
BigNumber(btcFiatRate),
fungibleToken,
- );
- }
- setFiatAmount(amountInCurrency);
+ ),
+ );
}, [value]);
function getFtTicker() {
@@ -193,7 +186,12 @@ function RecipientComponent({
return (
-
+
);
};
diff --git a/src/app/components/sendForm/index.tsx b/src/app/components/sendForm/index.tsx
index c10231387..eb196bc9d 100644
--- a/src/app/components/sendForm/index.tsx
+++ b/src/app/components/sendForm/index.tsx
@@ -255,14 +255,15 @@ function SendForm({
return;
}
- const amountInCurrency = getFiatEquivalent(
- Number(amountToSend),
- currencyType,
- BigNumber(stxBtcRate),
- BigNumber(btcFiatRate),
- fungibleToken,
+ setFiatAmount(
+ getFiatEquivalent(
+ Number(amountToSend),
+ currencyType,
+ BigNumber(stxBtcRate),
+ BigNumber(btcFiatRate),
+ fungibleToken,
+ ),
);
- setFiatAmount(amountInCurrency);
}, [amountToSend]);
function getTokenCurrency(): string {
@@ -290,15 +291,15 @@ function SendForm({
} else {
setAmount(newValue);
}
-
- const amountInCurrency = getFiatEquivalent(
- Number(newValue),
- currencyType,
- BigNumber(stxBtcRate),
- BigNumber(btcFiatRate),
- fungibleToken,
+ setFiatAmount(
+ getFiatEquivalent(
+ Number(amountToSend),
+ currencyType,
+ BigNumber(stxBtcRate),
+ BigNumber(btcFiatRate),
+ fungibleToken,
+ ),
);
- setFiatAmount(amountInCurrency);
};
const getTokenEquivalent = (tokenAmount: string): string => {
@@ -465,7 +466,7 @@ function SendForm({
!hideTokenImage && (
diff --git a/src/app/components/tokenImage/index.tsx b/src/app/components/tokenImage/index.tsx
index a114759e2..42bd21acb 100644
--- a/src/app/components/tokenImage/index.tsx
+++ b/src/app/components/tokenImage/index.tsx
@@ -1,89 +1,147 @@
import IconBitcoin from '@assets/img/dashboard/bitcoin_icon.svg';
import IconStacks from '@assets/img/dashboard/stx_icon.svg';
-import BarLoader from '@components/barLoader';
+import StacksIcon from '@assets/img/nftDashboard/stacks_icon.svg';
+import OrdinalIcon from '@assets/img/transactions/ordinal.svg';
+import RunesIcon from '@assets/img/transactions/runes.svg';
+import { StyledBarLoader } from '@components/tilesSkeletonLoader';
import { FungibleToken } from '@secretkeylabs/xverse-core';
-import { LoaderSize } from '@utils/constants';
+import { CurrencyTypes } from '@utils/constants';
import { getTicker } from '@utils/helper';
import { useCallback } from 'react';
-import stc from 'string-to-color';
import styled from 'styled-components';
export interface TokenImageProps {
- token?: string;
- loading?: boolean;
+ currency?: CurrencyTypes;
fungibleToken?: FungibleToken;
+ loading?: boolean;
size?: number;
- loaderSize?: LoaderSize;
round?: boolean;
+ showProtocolIcon?: boolean;
}
+const DEFAULT_SIZE = 40;
+
const TickerImage = styled.img<{ size?: number }>((props) => ({
- height: props.size ?? 44,
- width: props.size ?? 44,
+ height: props.size ?? DEFAULT_SIZE,
+ width: props.size ?? DEFAULT_SIZE,
borderRadius: '50%',
}));
const LoaderImageContainer = styled.div({
- flex: 0.5,
+ display: 'flex',
+ alignItems: 'center',
+ justifyContent: 'center',
});
const TickerIconContainer = styled.div<{ size?: number; round?: boolean }>((props) => ({
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
- height: props.size ?? 44,
- width: props.size ?? 44,
+ height: props.size ?? DEFAULT_SIZE,
+ width: props.size ?? DEFAULT_SIZE,
borderRadius: '50%',
- backgroundColor: props.color,
+ backgroundColor: props.theme.colors.white_400,
}));
const TickerIconText = styled.h1((props) => ({
- ...props.theme.body_bold_m,
- color: props.theme.colors.white_0,
+ ...props.theme.typography.body_bold_m,
+ color: props.theme.colors.elevation0,
textAlign: 'center',
wordBreak: 'break-all',
- fontSize: 13,
+ fontSize: 11,
}));
+const TickerProtocolContainer = styled.div({
+ position: 'relative',
+ alignSelf: 'center',
+ display: 'inline-flex',
+});
+
+const ProtocolIcon = styled.div<{ isSquare?: boolean }>((props) => ({
+ width: props.isSquare ? 18 : 22,
+ height: props.isSquare ? 18 : 22,
+ borderRadius: props.isSquare ? 0 : 22,
+ position: 'absolute',
+ right: props.isSquare ? -9 : -11,
+ bottom: -2,
+ backgroundColor: props.theme.colors.elevation1,
+ padding: 2,
+}));
+
+const ProtocolImage = styled.img({
+ height: '100%',
+ width: '100%',
+});
+
export default function TokenImage({
- token,
- loading,
+ currency,
fungibleToken,
+ loading,
size,
- loaderSize,
round,
+ showProtocolIcon = true,
}: TokenImageProps) {
- const getCoinIcon = useCallback(() => {
- if (token === 'STX') {
+ const ftProtocol = fungibleToken?.protocol;
+
+ const getCurrencyIcon = useCallback(() => {
+ if (currency === 'STX') {
return IconStacks;
}
- if (token === 'BTC') {
+ if (currency === 'BTC') {
return IconBitcoin;
}
- }, [token]);
-
- if (fungibleToken) {
- if (!loading) {
- if (fungibleToken?.image) {
- return ;
- }
- let ticker = fungibleToken?.ticker;
- if (!ticker && fungibleToken?.name) {
- ticker = getTicker(fungibleToken?.name);
- }
- const background = stc(ticker);
- ticker = ticker && ticker.substring(0, 4);
- return (
-
- {ticker}
-
- );
+ }, [currency]);
+
+ const getProtocolIcon = () => {
+ if (!ftProtocol) {
+ return null;
+ }
+ switch (ftProtocol) {
+ case 'stacks':
+ return ;
+ case 'brc-20':
+ return ;
+ case 'runes':
+ return ;
+ default:
+ return null;
+ }
+ };
+
+ const renderIcon = () => {
+ if (!fungibleToken) {
+ return ;
+ }
+ if (fungibleToken?.image) {
+ return ;
}
+ const ticker = fungibleToken?.name
+ ? getTicker(fungibleToken.name)
+ : fungibleToken?.ticker || fungibleToken?.assetName || '';
+
+ return (
+
+ {ticker.substring(0, 4)}
+
+ );
+ };
+
+ if (loading) {
return (
-
-
-
+
+
+
+
+
);
}
- return ;
+
+ return (
+
+ {renderIcon()}
+ {ftProtocol && showProtocolIcon && (
+ {getProtocolIcon()}
+ )}
+
+ );
}
diff --git a/src/app/components/tokenTile/index.tsx b/src/app/components/tokenTile/index.tsx
index 430827aa9..323f85ab6 100644
--- a/src/app/components/tokenTile/index.tsx
+++ b/src/app/components/tokenTile/index.tsx
@@ -1,15 +1,14 @@
import { BetterBarLoader } from '@components/barLoader';
import { StyledFiatAmountText } from '@components/fiatAmountText';
+import TokenImage from '@components/tokenImage';
import type { FungibleToken } from '@secretkeylabs/xverse-core';
import { microstacksToStx, satsToBtc } from '@secretkeylabs/xverse-core';
import { StoreState } from '@stores/index';
import { CurrencyTypes } from '@utils/constants';
-import { getTicker } from '@utils/helper';
import { getFtBalance, getFtTicker } from '@utils/tokens';
import BigNumber from 'bignumber.js';
import { NumericFormat } from 'react-number-format';
import { useSelector } from 'react-redux';
-import stc from 'string-to-color';
import styled from 'styled-components';
interface TileProps {
@@ -17,9 +16,6 @@ interface TileProps {
inModel: boolean;
}
-interface TickerProps {
- enlargeTicker?: boolean;
-}
const TileContainer = styled.button((props) => ({
display: 'flex',
flexDirection: 'row',
@@ -33,51 +29,25 @@ const TileContainer = styled.button((props) => ({
marginBottom: props.inModel ? props.theme.spacing(0) : props.theme.spacing(6),
}));
-const TickerImage = styled.img((props) => ({
- marginRight: props.theme.spacing(3),
- alignSelf: 'center',
- transform: 'all',
- height: props.enlargeTicker ? 40 : 32,
- width: props.enlargeTicker ? 40 : 32,
- borderRadius: '50%',
-}));
-
-const TickerIconContainer = styled.div((props) => ({
- display: 'flex',
- alignItems: 'center',
- justifyContent: 'center',
- height: props.enlargeTicker ? 40 : 32,
- width: props.enlargeTicker ? 40 : 32,
- marginRight: props.theme.spacing(3),
- borderRadius: '50%',
- backgroundColor: props.color,
-}));
-
-const TickerIconText = styled.h1((props) => ({
- ...props.theme.typography.body_bold_m,
- color: props.theme.colors.white_0,
- textAlign: 'center',
- wordBreak: 'break-all',
- fontSize: 10,
-}));
-
const RowContainer = styled.div({
flex: '1 0 auto',
display: 'flex',
});
const TextContainer = styled.div((props) => ({
- marginLeft: props.theme.spacing(6),
+ marginLeft: props.theme.space.m,
display: 'flex',
flexDirection: 'column',
alignItems: 'flex-start',
}));
-const AmountContainer = styled.div({
+const AmountContainer = styled.div((props) => ({
display: 'flex',
flexDirection: 'column',
+ marginLeft: props.theme.space.xxs,
+ overflow: 'hidden',
alignItems: 'flex-end',
-});
+}));
const LoaderMainContainer = styled.div({
flex: '1 1 auto',
@@ -86,19 +56,12 @@ const LoaderMainContainer = styled.div({
alignItems: 'flex-end',
});
-const LoaderImageContainer = styled.div((props) => ({
- display: 'flex',
- alignItems: 'center',
- justifyContent: 'center',
- marginRight: props.theme.spacing(3),
-}));
-
-const CoinTickerText = styled.h1((props) => ({
+const CoinTickerText = styled.p((props) => ({
...props.theme.typography.body_bold_m,
color: props.theme.colors.white_0,
}));
-const SubText = styled.h1((props) => ({
+const SubText = styled.p((props) => ({
...props.theme.headline_category_s,
color: props.theme.colors.white_400,
fontSize: 12,
@@ -108,10 +71,12 @@ const SubText = styled.h1((props) => ({
textOverflow: 'ellipsis',
}));
-const CoinBalanceText = styled.h1((props) => ({
+const CoinBalanceText = styled.p((props) => ({
...props.theme.typography.body_medium_m,
color: props.theme.colors.white_0,
- textAlign: 'end',
+ overflow: 'hidden',
+ textOverflow: 'ellipsis',
+ maxWidth: '100%',
}));
const TokenTitleContainer = styled.div({
@@ -140,22 +105,16 @@ function TokenLoader() {
interface Props {
title: string;
- icon?: string;
underlayColor: string;
loading: boolean;
margin?: number;
- currency?: CurrencyTypes;
- onPress: (token: {
- coin: CurrencyTypes;
- ft: string | undefined;
- brc20Ft?: string | undefined;
- }) => void;
+ currency: CurrencyTypes;
+ onPress: (coin: CurrencyTypes, ftKey: string | undefined) => void;
fungibleToken?: FungibleToken;
enlargeTicker?: boolean;
}
function TokenTile({
- icon,
title,
underlayColor,
loading,
@@ -182,8 +141,6 @@ function TokenTile({
return satsToBtc(new BigNumber(btcBalance)).toString();
case 'FT':
return fungibleToken ? getFtBalance(fungibleToken) : '';
- case 'brc20':
- return fungibleToken ? getFtBalance(fungibleToken) : '';
default:
}
}
@@ -197,7 +154,6 @@ function TokenTile({
case 'BTC':
return satsToBtc(new BigNumber(btcBalance)).multipliedBy(btcFiatRate);
case 'FT':
- case 'brc20':
return fungibleToken?.tokenFiatRate
? new BigNumber(getFtBalance(fungibleToken)).multipliedBy(fungibleToken.tokenFiatRate)
: undefined;
@@ -206,44 +162,7 @@ function TokenTile({
}
}
- function renderFTIcon() {
- if (!loading) {
- if (fungibleToken?.image) {
- return ;
- }
- // render ticker icon
- let ticker = fungibleToken?.ticker;
- if (!ticker && fungibleToken?.name) {
- ticker = getTicker(fungibleToken?.name);
- }
- const background = stc(ticker);
- ticker = ticker && ticker.substring(0, 4);
- return (
-
- {ticker}
-
- );
- }
- return (
-
-
-
- );
- }
-
- function renderIcon() {
- if (currency === 'STX' || currency === 'BTC')
- return ;
- return renderFTIcon();
- }
-
- const handleTokenPressed = () => {
- onPress({
- coin: currency as CurrencyTypes,
- ft: fungibleToken && fungibleToken.principal,
- brc20Ft: currency === 'brc20' ? fungibleToken?.principal : undefined,
- });
- };
+ const handleTokenPressed = () => onPress(currency, fungibleToken?.principal);
return (
- {renderIcon()}
+
{getTickerTitle()}
diff --git a/src/app/components/transactions/btcTransaction.tsx b/src/app/components/transactions/btcOrBrc20Transaction.tsx
similarity index 92%
rename from src/app/components/transactions/btcTransaction.tsx
rename to src/app/components/transactions/btcOrBrc20Transaction.tsx
index ffec7015c..66c8915c9 100644
--- a/src/app/components/transactions/btcTransaction.tsx
+++ b/src/app/components/transactions/btcOrBrc20Transaction.tsx
@@ -77,12 +77,13 @@ interface TransactionHistoryItemProps {
transaction: BtcTransactionData | Brc20HistoryTransactionData;
wallet: RBFProps;
}
-export default function BtcTransactionHistoryItem({
+export default function BtcOrBrc20TransactionHistoryItem({
transaction,
wallet,
}: TransactionHistoryItemProps) {
const { network, hasActivatedRBFKey } = useWalletSelector();
- const isBtc = isBtcTransaction(transaction) ? 'BTC' : 'brc20';
+ const currency = isBtcTransaction(transaction) ? 'BTC' : 'FT';
+ const protocol = currency === 'FT' ? 'brc-20' : undefined;
const theme = useTheme();
const { t } = useTranslation('translation', { keyPrefix: 'COIN_DASHBOARD_SCREEN' });
@@ -94,10 +95,9 @@ export default function BtcTransactionHistoryItem({
hasActivatedRBFKey &&
isBtcTransaction(transaction) &&
rbf.isTransactionRbfEnabled(transaction, wallet);
-
return (
-
+
@@ -105,7 +105,7 @@ export default function BtcTransactionHistoryItem({
-
+
{showAccelerateButton && (