diff --git a/src/app/components/ledger/reviewLedgerStxTransactionComponent/index.tsx b/src/app/components/ledger/reviewLedgerStxTransactionComponent/index.tsx
new file mode 100644
index 000000000..dcc359b22
--- /dev/null
+++ b/src/app/components/ledger/reviewLedgerStxTransactionComponent/index.tsx
@@ -0,0 +1,206 @@
+import { useTranslation } from 'react-i18next';
+import styled from 'styled-components';
+import { ReactNode, useEffect, useState } from 'react';
+import BigNumber from 'bignumber.js';
+import ActionButton from '@components/button';
+import SettingIcon from '@assets/img/dashboard/faders_horizontal.svg';
+import TransactionSettingAlert from '@components/transactionSetting';
+import { microstacksToStx, stxToMicrostacks } from '@secretkeylabs/xverse-core/currency';
+import { StacksTransaction } from '@secretkeylabs/xverse-core/types';
+import TransferFeeView from '@components/transferFeeView';
+import {
+ setFee,
+ setNonce,
+ getNonce,
+ signMultiStxTransactions,
+ signTransaction,
+} from '@secretkeylabs/xverse-core';
+import useWalletSelector from '@hooks/useWalletSelector';
+import useNetworkSelector from '@hooks/useNetwork';
+
+const Container = styled.div`
+ display: flex;
+ flex-direction: column;
+ flex: 1;
+ margin-top: 22px;
+ margin-left: 16px;
+ margin-right: 16px;
+ overflow-y: auto;
+ &::-webkit-scrollbar {
+ display: none;
+ }
+`;
+
+const ButtonContainer = styled.div((props) => ({
+ display: 'flex',
+ flexDirection: 'row',
+ marginBottom: props.theme.spacing(20),
+ marginTop: props.theme.spacing(12),
+ marginLeft: props.theme.spacing(8),
+ marginRight: props.theme.spacing(8),
+}));
+
+const TransparentButtonContainer = styled.div((props) => ({
+ marginLeft: props.theme.spacing(2),
+ marginRight: props.theme.spacing(2),
+ width: '100%',
+}));
+
+const Button = styled.button((props) => ({
+ display: 'flex',
+ flexDirection: 'row',
+ alignItems: 'center',
+ borderRadius: props.theme.radius(1),
+ backgroundColor: 'transparent',
+ width: '100%',
+ marginTop: props.theme.spacing(10),
+}));
+
+const ButtonText = styled.div((props) => ({
+ ...props.theme.body_medium_m,
+ color: props.theme.colors.white['0'],
+ textAlign: 'center',
+}));
+
+const TransferFeeContainer = styled.div((props) => ({
+ marginBottom: props.theme.spacing(12),
+}));
+
+const ButtonImage = styled.img((props) => ({
+ marginRight: props.theme.spacing(3),
+ alignSelf: 'center',
+ transform: 'all',
+}));
+
+const SponsoredInfoText = styled.h1((props) => ({
+ ...props.theme.body_m,
+ color: props.theme.colors.white['400'],
+}));
+
+interface Props {
+ initialStxTransactions: StacksTransaction[];
+ loading: boolean;
+ onCancelClick: () => void;
+ onConfirmClick: (transactions: StacksTransaction[]) => void;
+ children: ReactNode;
+ isSponsored?: boolean;
+}
+
+function ReviewLedgerStxTransactionComponent({
+ initialStxTransactions,
+ loading,
+ isSponsored,
+ children,
+ onConfirmClick,
+ onCancelClick,
+}: Props) {
+ const { t } = useTranslation('translation', { keyPrefix: 'CONFIRM_TRANSACTION' });
+ const selectedNetwork = useNetworkSelector();
+ const { selectedAccount, seedPhrase } = useWalletSelector();
+ const [openTransactionSettingModal, setOpenTransactionSettingModal] = useState(false);
+ const [buttonLoading, setButtonLoading] = useState(loading);
+
+ useEffect(() => {
+ setButtonLoading(loading);
+ }, [loading]);
+
+ const getFee = () =>
+ isSponsored
+ ? new BigNumber(0)
+ : new BigNumber(
+ initialStxTransactions
+ .map((tx) => tx?.auth?.spendingCondition?.fee ?? BigInt(0))
+ .reduce((prev, curr) => prev + curr, BigInt(0))
+ .toString(10)
+ );
+
+ const getTxNonce = (): string => {
+ const nonce = getNonce(initialStxTransactions[0]);
+ return nonce.toString();
+ };
+
+ const onAdvancedSettingClick = () => {
+ setOpenTransactionSettingModal(true);
+ };
+
+ const closeTransactionSettingAlert = () => {
+ setOpenTransactionSettingModal(false);
+ };
+
+ const onConfirmButtonClick = async () => {
+ let signedTxs: StacksTransaction[] = [];
+ if (initialStxTransactions.length === 1) {
+ const signedContractCall = await signTransaction(
+ initialStxTransactions[0],
+ seedPhrase,
+ selectedAccount?.id ?? 0,
+ selectedNetwork
+ );
+ signedTxs.push(signedContractCall);
+ } else if (initialStxTransactions.length === 2) {
+ signedTxs = await signMultiStxTransactions(
+ initialStxTransactions,
+ selectedAccount?.id ?? 0,
+ selectedNetwork,
+ seedPhrase
+ );
+ }
+ onConfirmClick(signedTxs);
+ };
+
+ const applyTxSettings = (settingFee: string, nonce?: string) => {
+ const fee = stxToMicrostacks(new BigNumber(settingFee));
+ setFee(initialStxTransactions[0], BigInt(fee.toString()));
+ if (nonce && nonce !== '') {
+ setNonce(initialStxTransactions[0], BigInt(nonce));
+ }
+ setOpenTransactionSettingModal(false);
+ };
+
+ return (
+ <>
+
+ {children}
+
+
+
+
+ {!isSponsored && (
+
+ )}
+ {isSponsored && {t('SPONSORED_TX_INFO')}}
+
+
+
+
+
+
+
+
+ >
+ );
+}
+
+export default ReviewLedgerStxTransactionComponent;
diff --git a/src/app/routes/index.tsx b/src/app/routes/index.tsx
index 6a082fac3..b78401cae 100644
--- a/src/app/routes/index.tsx
+++ b/src/app/routes/index.tsx
@@ -49,8 +49,10 @@ import RestoreBtc from '@screens/restoreFunds/restoreBtc';
import RestoreOrdinals from '@screens/restoreFunds/restoreOrdinals';
import ImportLedger from '@screens/ledger/importLedgerAccount';
import ReviewLedgerBtcTransaction from '@screens/ledger/reviewLedgerBtcTransaction';
-import ConfirmLedgerBtcTransaction from '@screens/ledger/confirmLedgerBtcTransaction';
+import ConfirmLedgerTransaction from '@screens/ledger/confirmLedgerTransaction';
import LedgerSendBtcScreen from '@screens/ledger/ledgerSendBtc';
+import LedgerSendStxScreen from '@screens/ledger/ledgerSendStx';
+import ReviewLedgerStxTransaction from '@screens/ledger/reviewLedgerStxTransaction';
const router = createHashRouter([
{
@@ -110,6 +112,10 @@ const router = createHashRouter([
path: 'send-btc-ledger',
element: ,
},
+ {
+ path: 'send-stx-ledger',
+ element: ,
+ },
{
path: 'confirm-stx-tx',
element: ,
@@ -127,8 +133,12 @@ const router = createHashRouter([
element: ,
},
{
- path: 'confirm-ledger-btc-tx',
- element: ,
+ path: 'review-ledger-stx-tx',
+ element: ,
+ },
+ {
+ path: 'confirm-ledger-tx',
+ element: ,
},
{
path: 'backup',
diff --git a/src/app/screens/coinDashboard/coinHeader.tsx b/src/app/screens/coinDashboard/coinHeader.tsx
index 1cfd85bb0..21b880a9c 100644
--- a/src/app/screens/coinDashboard/coinHeader.tsx
+++ b/src/app/screens/coinDashboard/coinHeader.tsx
@@ -332,6 +332,12 @@ export default function CoinHeader(props: CoinBalanceProps) {
});
return;
}
+ if (coin === 'STX') {
+ await chrome.tabs.create({
+ url: chrome.runtime.getURL('options.html#/send-stx-ledger'),
+ });
+ return;
+ }
}
if (coin === 'STX' || coin === 'BTC') {
navigate(`/send-${coin}`);
diff --git a/src/app/screens/home/index.tsx b/src/app/screens/home/index.tsx
index e8a49a832..5bccb953c 100644
--- a/src/app/screens/home/index.tsx
+++ b/src/app/screens/home/index.tsx
@@ -174,7 +174,13 @@ function Home() {
navigate('/manage-tokens');
};
- const onStxSendClick = () => {
+ const onStxSendClick = async () => {
+ if (isLedgerAccount) {
+ await chrome.tabs.create({
+ url: chrome.runtime.getURL('options.html#/send-stx-ledger'),
+ });
+ return;
+ }
navigate('/send-stx');
};
diff --git a/src/app/screens/ledger/confirmLedgerBtcTransaction/index.tsx b/src/app/screens/ledger/confirmLedgerTransaction/index.tsx
similarity index 79%
rename from src/app/screens/ledger/confirmLedgerBtcTransaction/index.tsx
rename to src/app/screens/ledger/confirmLedgerTransaction/index.tsx
index 008fd7e1f..d0e3f9023 100644
--- a/src/app/screens/ledger/confirmLedgerBtcTransaction/index.tsx
+++ b/src/app/screens/ledger/confirmLedgerTransaction/index.tsx
@@ -7,7 +7,9 @@ import Transport from '@ledgerhq/hw-transport-webusb';
import ActionButton from '@components/button';
import {
broadcastRawBtcTransaction,
+ broadcastSignedTransaction,
signLedgerNestedSegwitBtcTransaction,
+ signStxTransaction,
} from '@secretkeylabs/xverse-core';
import BigNumber from 'bignumber.js';
import { LedgerTransactionType } from '../reviewLedgerBtcTransaction';
@@ -15,11 +17,13 @@ import useWalletSelector from '@hooks/useWalletSelector';
import { Recipient } from '@secretkeylabs/xverse-core/transactions/btc';
import LedgerConnectionView from '@components/ledger/connectLedgerView';
import { ledgerDelay } from '@common/utils/ledger';
-import { getBtcTxStatusUrl } from '@utils/helper';
+import { getBtcTxStatusUrl, getStxTxStatusUrl } from '@utils/helper';
import FullScreenHeader from '@components/ledger/fullScreenHeader';
import LedgerConnectDefaultSVG from '@assets/img/ledger/ledger_connect_default.svg';
import CheckCircleSVG from '@assets/img/ledger/check_circle.svg';
+import { StacksTransaction } from '@stacks/transactions';
+import useNetworkSelector from '@hooks/useNetwork';
const Container = styled.div`
display: flex;
@@ -70,7 +74,7 @@ const TxConfirmedDescription = styled.p((props) => ({
color: props.theme.colors.white[200],
}));
-function ConfirmLedgerBtcTransaction(): JSX.Element {
+function ConfirmLedgerTransaction(): JSX.Element {
const [currentStepIndex, setCurrentStepIndex] = useState(0);
const [txId, setTxId] = useState(undefined);
@@ -82,12 +86,20 @@ function ConfirmLedgerBtcTransaction(): JSX.Element {
const { t } = useTranslation('translation', { keyPrefix: 'LEDGER_CONFIRM_TRANSACTION_SCREEN' });
const location = useLocation();
+ const selectedNetwork = useNetworkSelector();
+
const { network, selectedAccount } = useWalletSelector();
const {
recipient,
type,
- }: { amount: BigNumber; recipient: Recipient; type: LedgerTransactionType } = location.state;
+ unsignedTx,
+ }: {
+ amount: BigNumber;
+ recipient: Recipient;
+ type: LedgerTransactionType;
+ unsignedTx: StacksTransaction;
+ } = location.state;
const transition = useTransition(currentStepIndex, {
from: {
@@ -100,6 +112,45 @@ function ConfirmLedgerBtcTransaction(): JSX.Element {
},
});
+ const signAndBroadcastBtcTx = async (transport: Transport, accountId: number) => {
+ try {
+ const result = await signLedgerNestedSegwitBtcTransaction(
+ transport,
+ network.type,
+ accountId,
+ recipient
+ );
+ setIsTxApproved(true);
+ await ledgerDelay(1500);
+ const transactionId = await broadcastRawBtcTransaction(result, network.type);
+ setTxId(transactionId.tx.hash);
+ setCurrentStepIndex(2);
+ } catch (err) {
+ console.error(err);
+ setIsTxRejected(true);
+ setIsButtonDisabled(false);
+ } finally {
+ transport.close();
+ }
+ };
+
+ const signAndBroadcastStxTx = async (transport: Transport, accountId: number) => {
+ try {
+ const result = await signStxTransaction(transport, unsignedTx, accountId);
+ setIsTxApproved(true);
+ await ledgerDelay(1500);
+ const transactionHash = await broadcastSignedTransaction(result, selectedNetwork);
+ setTxId(transactionHash);
+ setCurrentStepIndex(2);
+ } catch (err) {
+ console.error(err);
+ setIsTxRejected(true);
+ setIsButtonDisabled(false);
+ } finally {
+ transport.close();
+ }
+ };
+
const handleConnectAndConfirm = async () => {
if (!selectedAccount) {
console.error('No account selected');
@@ -120,24 +171,16 @@ function ConfirmLedgerBtcTransaction(): JSX.Element {
await ledgerDelay(1500);
setCurrentStepIndex(1);
- try {
- const result = await signLedgerNestedSegwitBtcTransaction(
- transport,
- network.type,
- selectedAccount.id,
- recipient
- );
- setIsTxApproved(true);
- await ledgerDelay(1500);
- const transactionId = await broadcastRawBtcTransaction(result, network.type);
- setTxId(transactionId.tx.hash);
- setCurrentStepIndex(2);
- } catch (err) {
- console.error(err);
- setIsTxRejected(true);
- setIsButtonDisabled(false);
- } finally {
- transport.close();
+ switch (type) {
+ case 'BTC':
+ await signAndBroadcastBtcTx(transport as Transport, selectedAccount.id);
+ break;
+ case 'STX':
+ await signAndBroadcastStxTx(transport as Transport, selectedAccount.id);
+ break;
+ case 'ORDINALS':
+ default:
+ break;
}
};
@@ -158,7 +201,18 @@ function ConfirmLedgerBtcTransaction(): JSX.Element {
console.error('No txId found');
return;
}
- window.open(getBtcTxStatusUrl(txId, network), '_blank', 'noopener,noreferrer');
+
+ switch (type) {
+ case 'BTC':
+ window.open(getBtcTxStatusUrl(txId, network), '_blank', 'noopener,noreferrer');
+ break;
+ case 'STX':
+ window.open(getStxTxStatusUrl(txId, network), '_blank', 'noopener,noreferrer');
+ break;
+ case 'ORDINALS':
+ default:
+ break;
+ }
};
return (
@@ -230,4 +284,4 @@ function ConfirmLedgerBtcTransaction(): JSX.Element {
);
}
-export default ConfirmLedgerBtcTransaction;
+export default ConfirmLedgerTransaction;
diff --git a/src/app/screens/ledger/importLedgerAccount/index.tsx b/src/app/screens/ledger/importLedgerAccount/index.tsx
index bdb0b7984..48c2db9e6 100644
--- a/src/app/screens/ledger/importLedgerAccount/index.tsx
+++ b/src/app/screens/ledger/importLedgerAccount/index.tsx
@@ -1,4 +1,4 @@
-import { useState } from 'react';
+import { useState } from 'react';
import { useTranslation } from 'react-i18next';
import styled from 'styled-components';
import { animated, useTransition } from '@react-spring/web';
@@ -268,13 +268,13 @@ function ImportLedger(): JSX.Element {
(account) => account.stxAddress !== ''
).length;
setAddressIndex(newAddressIndex);
- const { address, publicKey } = await importStacksAccountFromLedger(
+ const { address, publicKey, testnetAddress } = await importStacksAccountFromLedger(
transport,
network,
0,
newAddressIndex
);
- setStacksCredentials({ address, publicKey });
+ setStacksCredentials({ address: testnetAddress, publicKey });
await transport.close();
};
diff --git a/src/app/screens/ledger/ledgerSendStx/index.tsx b/src/app/screens/ledger/ledgerSendStx/index.tsx
new file mode 100644
index 000000000..f4deb937b
--- /dev/null
+++ b/src/app/screens/ledger/ledgerSendStx/index.tsx
@@ -0,0 +1,171 @@
+import { useMutation } from '@tanstack/react-query';
+import BigNumber from 'bignumber.js';
+import { useEffect, useState } from 'react';
+import { useTranslation } from 'react-i18next';
+import { useSelector } from 'react-redux';
+import { useLocation, useNavigate } from 'react-router-dom';
+import { generateUnsignedStxTokenTransferTransaction } from '@secretkeylabs/xverse-core/transactions';
+import { microstacksToStx, stxToMicrostacks } from '@secretkeylabs/xverse-core/currency';
+import { StacksTransaction } from '@secretkeylabs/xverse-core/types';
+import { validateStxAddress } from '@secretkeylabs/xverse-core/wallet';
+import SendForm from '@components/sendForm';
+import TopRow from '@components/topRow';
+import useStxPendingTxData from '@hooks/queries/useStxPendingTxData';
+import { StoreState } from '@stores/index';
+import { replaceCommaByDot } from '@utils/helper';
+import BottomBar from '@components/tabBar';
+import useNetworkSelector from '@hooks/useNetwork';
+
+function LedgerSendStxScreen() {
+ const { t } = useTranslation('translation', { keyPrefix: 'SEND' });
+ const navigate = useNavigate();
+ const {
+ stxAddress,
+ stxAvailableBalance,
+ stxPublicKey,
+ feeMultipliers,
+ network,
+ isLedgerAccount,
+ } = useSelector((state: StoreState) => state.walletState);
+ const [amountError, setAmountError] = useState('');
+ const [addressError, setAddressError] = useState('');
+ const [memoError, setMemoError] = useState('');
+ const selectedNetwork = useNetworkSelector();
+ const { data: stxPendingTxData } = useStxPendingTxData();
+ const location = useLocation();
+ let recipientAddress: string | undefined;
+ let amountToSend: string | undefined;
+ let stxMemo: string | undefined;
+
+ useEffect(() => {
+ if (!isLedgerAccount) {
+ //TODO - Handle window close or navigate to home
+ console.warn('Not Ledger Account');
+ }
+ }, [isLedgerAccount]);
+
+ if (location.state) {
+ recipientAddress = location.state.recipientAddress;
+ amountToSend = location.state.amountToSend;
+ stxMemo = location.state.stxMemo;
+ }
+ const { isLoading, data, mutate } = useMutation<
+ StacksTransaction,
+ Error,
+ { associatedAddress: string; amount: string; memo?: string }
+ >(async ({ associatedAddress, amount, memo }) => {
+ const unsignedSendStxTx: StacksTransaction = await generateUnsignedStxTokenTransferTransaction(
+ associatedAddress,
+ stxToMicrostacks(new BigNumber(amount)).toString(),
+ memo!,
+ stxPendingTxData?.pendingTransactions ?? [],
+ stxPublicKey,
+ selectedNetwork
+ );
+ // increasing the fees with multiplication factor
+ const fee: bigint =
+ BigInt(unsignedSendStxTx.auth.spendingCondition.fee.toString()) ?? BigInt(0);
+ if (feeMultipliers?.stxSendTxMultiplier) {
+ unsignedSendStxTx.setFee(fee * BigInt(feeMultipliers.stxSendTxMultiplier));
+ }
+ return unsignedSendStxTx;
+ });
+
+ useEffect(() => {
+ // FIXME route doesn't get updated in url bar
+ if (data) {
+ navigate('/review-ledger-stx-tx', {
+ state: {
+ unsignedTx: data,
+ },
+ });
+ }
+ }, [data]);
+
+ const handleBackButtonClick = () => {
+ navigate(-1);
+ };
+
+ function validateFields(associatedAddress: string, amount: string, memo: string): boolean {
+ if (!associatedAddress) {
+ setAddressError(t('ERRORS.ADDRESS_REQUIRED'));
+ return false;
+ }
+
+ if (!amount) {
+ setAmountError(t('ERRORS.AMOUNT_REQUIRED'));
+ return false;
+ }
+ if (!validateStxAddress({ stxAddress: associatedAddress, network: network.type })) {
+ setAddressError(t('ERRORS.ADDRESS_INVALID'));
+ return false;
+ }
+
+ if (associatedAddress === stxAddress) {
+ setAddressError(t('ERRORS.SEND_TO_SELF'));
+ return false;
+ }
+
+ let parsedAmount = new BigNumber(0);
+ try {
+ if (!Number.isNaN(Number(amount))) {
+ parsedAmount = new BigNumber(amount);
+ } else {
+ setAmountError(t('ERRORS.INVALID_AMOUNT'));
+ return false;
+ }
+ } catch (e) {
+ setAmountError(t('ERRORS.INVALID_AMOUNT'));
+ return false;
+ }
+
+ if (stxToMicrostacks(parsedAmount).lt(1)) {
+ setAmountError(t('ERRORS.MINIMUM_AMOUNT'));
+ return false;
+ }
+
+ if (stxToMicrostacks(parsedAmount).gt(stxAvailableBalance)) {
+ setAmountError(t('ERRORS.INSUFFICIENT_BALANCE'));
+ return false;
+ }
+
+ if (memo) {
+ if (Buffer.from(memo).byteLength >= 34) {
+ setMemoError(t('ERRORS.MEMO_LENGTH'));
+ return false;
+ }
+ }
+ return true;
+ }
+
+ const onPressSendSTX = async (associatedAddress: string, amount: string, memo?: string) => {
+ const modifyAmount = replaceCommaByDot(amount);
+ const addMemo = memo ?? '';
+ if (validateFields(associatedAddress.trim(), modifyAmount, memo!)) {
+ setAddressError('');
+ setMemoError('');
+ setAmountError('');
+ mutate({ amount, associatedAddress, memo: addMemo });
+ }
+ };
+
+ return (
+ <>
+ {}} showBackButton={false} />
+
+ >
+ );
+}
+
+export default LedgerSendStxScreen;
diff --git a/src/app/screens/ledger/reviewLedgerBtcTransaction/index.tsx b/src/app/screens/ledger/reviewLedgerBtcTransaction/index.tsx
index 9648691a3..170d21c8e 100644
--- a/src/app/screens/ledger/reviewLedgerBtcTransaction/index.tsx
+++ b/src/app/screens/ledger/reviewLedgerBtcTransaction/index.tsx
@@ -25,7 +25,7 @@ function ReviewLedgerBtcTransaction() {
const handleOnConfirmClick = async () => {
const txType: LedgerTransactionType = 'BTC';
- navigate('/confirm-ledger-btc-tx', { state: { amount, recipient, type: txType } });
+ navigate('/confirm-ledger-tx', { state: { amount, recipient, type: txType } });
};
const goBackToScreen = () => {
diff --git a/src/app/screens/ledger/reviewLedgerStxTransaction/index.tsx b/src/app/screens/ledger/reviewLedgerStxTransaction/index.tsx
new file mode 100644
index 000000000..b81940f26
--- /dev/null
+++ b/src/app/screens/ledger/reviewLedgerStxTransaction/index.tsx
@@ -0,0 +1,211 @@
+import { useTranslation } from 'react-i18next';
+import styled from 'styled-components';
+import { useMutation } from '@tanstack/react-query';
+import BigNumber from 'bignumber.js';
+import { useEffect, useState } from 'react';
+import { useLocation, useNavigate } from 'react-router-dom';
+import { getStxFiatEquivalent, microstacksToStx } from '@secretkeylabs/xverse-core/currency';
+import { StacksTransaction, TokenTransferPayload } from '@secretkeylabs/xverse-core/types';
+import {
+ addressToString,
+ broadcastSignedTransaction,
+} from '@secretkeylabs/xverse-core/transactions';
+import Seperator from '@components/seperator';
+import BottomBar from '@components/tabBar';
+import RecipientAddressView from '@components/recipinetAddressView';
+import TransferAmountView from '@components/transferAmountView';
+import TopRow from '@components/topRow';
+import AccountHeaderComponent from '@components/accountHeader';
+import finalizeTxSignature from '@components/transactionsRequests/utils';
+import InfoContainer from '@components/infoContainer';
+import useOnOriginTabClose from '@hooks/useOnTabClosed';
+import useNetworkSelector from '@hooks/useNetwork';
+import useStxWalletData from '@hooks/queries/useStxWalletData';
+import useWalletSelector from '@hooks/useWalletSelector';
+import ConfirmStxTransationComponent from '@components/confirmStxTransactionComponent';
+import ReviewLedgerStxTransactionComponent from '@components/ledger/reviewLedgerStxTransactionComponent';
+import { LedgerTransactionType } from '../reviewLedgerBtcTransaction';
+
+const Container = styled.div((props) => ({
+ display: 'flex',
+ flexDirection: 'column',
+ marginTop: props.theme.spacing(12),
+ marginBottom: props.theme.spacing(4),
+}));
+
+const AlertContainer = styled.div((props) => ({
+ marginTop: props.theme.spacing(12),
+}));
+
+const TitleText = styled.h1((props) => ({
+ ...props.theme.headline_category_s,
+ color: props.theme.colors.white['400'],
+ textTransform: 'uppercase',
+}));
+
+const ValueText = styled.h1((props) => ({
+ ...props.theme.body_m,
+ marginTop: props.theme.spacing(2),
+ wordBreak: 'break-all',
+}));
+
+function ReviewLedgerStxTransaction() {
+ const { t } = useTranslation('translation');
+ const [fee, setStateFee] = useState(new BigNumber(0));
+ const [amount, setAmount] = useState(new BigNumber(0));
+ const [fiatAmount, setFiatAmount] = useState(new BigNumber(0));
+ const [total, setTotal] = useState(new BigNumber(0));
+ const [fiatTotal, setFiatTotal] = useState(new BigNumber(0));
+ const [hasTabClosed, setHasTabClosed] = useState(false);
+ const [recipient, setRecipient] = useState('');
+ const [txRaw, setTxRaw] = useState('');
+ const [memo, setMemo] = useState('');
+ const navigate = useNavigate();
+ const location = useLocation();
+ const selectedNetwork = useNetworkSelector();
+ const { unsignedTx, sponsored, isBrowserTx, tabId, requestToken } = location.state;
+ useOnOriginTabClose(Number(tabId), () => {
+ setHasTabClosed(true);
+ window.scrollTo({ top: 0, behavior: 'smooth' });
+ });
+ const { stxBtcRate, btcFiatRate, network } = useWalletSelector();
+ const { refetch } = useStxWalletData();
+ const {
+ isLoading,
+ error: txError,
+ data: stxTxBroadcastData,
+ mutate,
+ } = useMutation(async ({ signedTx }) =>
+ broadcastSignedTransaction(signedTx, selectedNetwork)
+ );
+
+ useEffect(() => {
+ if (stxTxBroadcastData) {
+ if (isBrowserTx) {
+ finalizeTxSignature({
+ requestPayload: requestToken,
+ tabId: Number(tabId),
+ data: { txId: stxTxBroadcastData, txRaw },
+ });
+ }
+ navigate('/tx-status', {
+ state: {
+ txid: stxTxBroadcastData,
+ currency: 'STX',
+ error: '',
+ browserTx: isBrowserTx,
+ },
+ });
+ setTimeout(() => {
+ refetch();
+ }, 1000);
+ }
+ }, [stxTxBroadcastData]);
+
+ useEffect(() => {
+ if (txError) {
+ navigate('/tx-status', {
+ state: {
+ txid: '',
+ currency: 'STX',
+ error: txError.toString(),
+ browserTx: isBrowserTx,
+ },
+ });
+ }
+ }, [txError]);
+
+ function updateUI() {
+ const txPayload = unsignedTx.payload as TokenTransferPayload;
+
+ if (txPayload.recipient.address) {
+ setRecipient(addressToString(txPayload.recipient.address));
+ }
+
+ const txAmount = new BigNumber(txPayload.amount.toString(10));
+ const txFee = new BigNumber(unsignedTx.auth.spendingCondition.fee.toString());
+ const txTotal = amount.plus(fee);
+ const txFiatAmount = getStxFiatEquivalent(amount, stxBtcRate, btcFiatRate);
+ const txFiatTotal = getStxFiatEquivalent(amount, stxBtcRate, btcFiatRate);
+ const { memo: txMemo } = txPayload;
+
+ setAmount(txAmount);
+ setStateFee(txFee);
+ setFiatAmount(txFiatAmount);
+ setTotal(txTotal);
+ setFiatTotal(txFiatTotal);
+ setMemo(txMemo.content);
+ }
+
+ useEffect(() => {
+ if (recipient === '' || !fee || !amount || !fiatAmount || !total || !fiatTotal) {
+ updateUI();
+ }
+ });
+
+ const networkInfoSection = (
+
+ {t('CONFIRM_TRANSACTION.NETWORK')}
+ {network.type}
+
+ );
+
+ const memoInfoSection = !!memo && (
+ <>
+
+ {t('CONFIRM_TRANSACTION.MEMO')}
+ {memo}
+
+
+ >
+ );
+
+ const getAmount = () => {
+ const txPayload = unsignedTx?.payload as TokenTransferPayload;
+ const amountToTransfer = new BigNumber(txPayload?.amount?.toString(10));
+ return microstacksToStx(amountToTransfer);
+ };
+
+ const handleOnConfirmClick = () => {
+ const txType: LedgerTransactionType = 'STX';
+ navigate('/confirm-ledger-tx', { state: { unsignedTx, type: txType } });
+ };
+
+ const handleOnCancelClick = () => {
+ navigate('/send-stx-ledger', {
+ state: {
+ recipientAddress: recipient,
+ amountToSend: getAmount().toString(),
+ stxMemo: memo,
+ },
+ });
+ };
+
+ return (
+ <>
+
+
+
+ {hasTabClosed && (
+
+
+
+ )}
+
+ {networkInfoSection}
+
+ {memoInfoSection}
+
+ >
+ );
+}
+export default ReviewLedgerStxTransaction;