diff --git a/src/app/components/batchPsbtSigning/index.tsx b/src/app/components/batchPsbtSigning/index.tsx index 8cbf51b0c..fcb6007ae 100644 --- a/src/app/components/batchPsbtSigning/index.tsx +++ b/src/app/components/batchPsbtSigning/index.tsx @@ -2,7 +2,7 @@ import AccountHeaderComponent from '@components/accountHeader'; import { TxSummaryContext } from '@components/confirmBtcTransaction/hooks/useTxSummaryContext'; import ConfirmBatchBtcTransactions from '@components/confirmBtcTransaction/indexBatch'; import TransactionSummary from '@components/confirmBtcTransaction/transactionSummary'; -import InfoContainer from '@components/infoContainer'; +import LedgerSteps from '@components/ledgerSteps'; import LoadingTransactionStatus from '@components/loadingTransactionStatus'; import type { ConfirmationStatus } from '@components/loadingTransactionStatus/circularSvgAnimation'; import useSelectedAccount from '@hooks/useSelectedAccount'; @@ -16,9 +16,11 @@ import { btcTransaction, extractViewSummary, type AggregatedSummary, + type Transport, type UserTransactionSummary, } from '@secretkeylabs/xverse-core'; import Button from '@ui-library/button'; +import Sheet from '@ui-library/sheet'; import Spinner from '@ui-library/spinner'; import { isLedgerAccount } from '@utils/helper'; import { trackMixPanel } from '@utils/mixpanel'; @@ -48,22 +50,25 @@ interface BatchPsbtSigningProps { psbts: SignMultiplePsbtPayload[]; onSigned: (signedPsbts: string[]) => void | Promise; onCancel: () => void; + onPostSignDone: () => void; } -function BatchPsbtSigning({ onSigned, psbts, onCancel }: BatchPsbtSigningProps) { +function BatchPsbtSigning({ onSigned, psbts, onCancel, onPostSignDone }: BatchPsbtSigningProps) { const selectedAccount = useSelectedAccount(); const { network } = useWalletSelector(); const navigate = useNavigate(); const { t } = useTranslation('translation', { keyPrefix: 'CONFIRM_TRANSACTION' }); + const txnContext = useTransactionContext(); + useTrackMixPanelPageViewed(); + const [isSigning, setIsSigning] = useState(false); const [isSigningComplete, setIsSigningComplete] = useState(false); const [signingPsbtIndex, setSigningPsbtIndex] = useState(1); const [currentPsbtIndex, setCurrentPsbtIndex] = useState(0); const [reviewTransaction, setReviewTransaction] = useState(false); const [isLoading, setIsLoading] = useState(true); - const txnContext = useTransactionContext(); - useTrackMixPanelPageViewed(); const [parsedPsbts, setParsedPsbts] = useState([]); + const [isLedgerModalVisible, setIsLedgerModalVisible] = useState(false); const individualTxSummaryContext = useMemo( () => ({ @@ -109,11 +114,13 @@ function BatchPsbtSigning({ onSigned, psbts, onCancel }: BatchPsbtSigningProps) })(); }, [psbts, txnContext, network, navigate, t]); - const onSignPsbtConfirmed = async () => { + const onSignPsbtConfirmed = async (transport?: Transport) => { try { - if (isLedgerAccount(selectedAccount)) { + if (isLedgerAccount(selectedAccount) && !transport) { + setIsLedgerModalVisible(true); return; } + setIsSigning(true); const signedPsbts: string[] = []; @@ -128,6 +135,7 @@ function BatchPsbtSigning({ onSigned, psbts, onCancel }: BatchPsbtSigningProps) const psbtBase64 = await enhancedPsbt.getSignedPsbtBase64({ finalize: false, + ledgerTransport: transport, }); signedPsbts.push(psbtBase64); @@ -146,6 +154,7 @@ function BatchPsbtSigning({ onSigned, psbts, onCancel }: BatchPsbtSigningProps) setIsSigning(false); onSigned(signedPsbts); + setIsLedgerModalVisible(false); } catch (err) { setIsSigning(false); setIsSigningComplete(false); @@ -164,13 +173,128 @@ function BatchPsbtSigning({ onSigned, psbts, onCancel }: BatchPsbtSigningProps) } }; - const closeCallback = () => { - window.close(); + const renderSign = isSigning || isSigningComplete; + + const renderPreSign = () => { + if (renderSign) return null; + + const visitedInputs = new Set(); + const hasDuplicateInputs = parsedPsbts.some((parsedPsbt) => { + const inputLocations = parsedPsbt.summary.inputs.map( + (input) => input.extendedUtxo.utxo.txid + input.extendedUtxo.utxo.vout, + ); + const hasDuplicate = inputLocations.some((input) => { + if (visitedInputs.has(input)) { + return true; + } + visitedInputs.add(input); + return false; + }); + return hasDuplicate; + }); + + const renderBody = () => { + if (hasDuplicateInputs) { + // if there are duplicate inputs on the individual transactions, we won't show a summary + // and will have to ask the user to review each txn individually + return null; + } + + if (isLoading) { + return ( + + + + ); + } + + return ( + <> + + + + {t('SIGN_TRANSACTIONS', { count: parsedPsbts.length })} + + setReviewTransaction(true)}> + {t('REVIEW_ALL')} + + + + + + + +