Skip to content
Open
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
5 changes: 5 additions & 0 deletions next.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,11 @@ const nextConfig = {
destination: "/transaction/build",
permanent: true,
},
{
source: "/transaction/sign",
destination: "/transaction/import",
permanent: true,
Comment thread
jeesunikim marked this conversation as resolved.
},
];
},
};
Expand Down
39 changes: 27 additions & 12 deletions src/app/(sidebar)/transaction/cli-sign/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,26 @@ import { Routes } from "@/constants/routes";
import { NetworkOptions } from "@/constants/settings";
import { useStore } from "@/store/useStore";
import { trackEvent, TrackingEvent } from "@/metrics/tracking";

import { useImportFlowStore } from "@/store/createTransactionFlowStore";
import { delayedAction } from "@/helpers/delayedAction";

/**
* Deep-link adapter for the Stellar CLI sign flow.
*
* The CLI opens `/transaction/cli-sign?xdr=...&networkPassphrase=...`. We
* select the matching network, hand the XDR off to the import flow store, and
* redirect to the import page — which parses the envelope and renders the
* transaction overview.
*/
export default function CliSignTransaction() {
const { network, updateIsDynamicNetworkSelect, transaction, selectNetwork } =
useStore();
const { updateIsDynamicNetworkSelect, selectNetwork } = useStore();
const { resetAll, setImportXdr } = useImportFlowStore();
const searchParams = useSearchParams();

const getNetworkByPassphrase = (passphrase: string) => {
return NetworkOptions.find((network) => network.passphrase === passphrase);
};
const router = useRouter();

const getNetworkByPassphrase = (passphrase: string) =>
NetworkOptions.find((network) => network.passphrase === passphrase);

useEffect(() => {
const networkPassphrase = searchParams?.get("networkPassphrase");
const xdr = searchParams?.get("xdr");
Expand All @@ -32,16 +41,22 @@ export default function CliSignTransaction() {
}

if (xdr) {
transaction.updateSignActiveView("overview");
transaction.updateSignImportXdr(xdr);
resetAll();
setImportXdr(xdr);
Comment thread
jeesunikim marked this conversation as resolved.
Comment thread
jeesunikim marked this conversation as resolved.
}

trackEvent(TrackingEvent.TRANSACTION_CLI_SIGN);

router.push(Routes.SIGN_TRANSACTION);
// Not including other deps
delayedAction({
action() {
router.push(Routes.IMPORT_TRANSACTION);
},
delay: 0,
});

Comment thread
jeesunikim marked this conversation as resolved.
// Run once for the deep link's search params; the import page owns parsing.
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [transaction.sign.importXdr, network.id]);
}, [searchParams]);

return <Loader />;
}
2 changes: 1 addition & 1 deletion src/app/(sidebar)/transaction/components/Signatures.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -257,7 +257,7 @@ const EnvelopeSignaturesTable = ({
</thead>
<tbody>
{rows.map((row, index) => (
<tr role="row" key={`${envelope}-${index}-${row.hint}`}>
<tr key={`${envelope}-${index}-${row.hint}`}>
<td>
<SignatureCell>
{renderSigner(row.matchStatus, row.signerPubKey)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ export const Signatures = ({
);

return (
<tr role="row" key={rowKey}>
<tr key={rowKey}>
<td>
<SignatureCell>
{signerPubKey ? renderSigner(isVerified, signerPubKey) : "-"}
Expand All @@ -106,7 +106,7 @@ export const Signatures = ({
const isMatch = verifyAuthEntryPublicKey(entry.publicKey, entry.address);

return (
<tr role="row" key={rowKey}>
<tr key={rowKey}>
<td>
<SignatureCell>
{renderSigner(isMatch, entry.address)}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
"use client";

import { useEffect } from "react";
import {
FeeBumpTransaction,
Transaction,
Expand All @@ -12,9 +13,8 @@ import { useStore } from "@/store/useStore";

import { useImportSignatureCompleteness } from "@/hooks/useImportSignatureCompleteness";

import { hasSorobanData, isSorobanOperationType } from "@/helpers/sorobanUtils";
import { parseImportXdr, ParsedImportXdr } from "@/helpers/parseImportXdr";

import { validate } from "@/validate";
import { trackEvent, TrackingEvent } from "@/metrics/tracking";

import { FEE_BUMP_TX_FIELDS, TX_FIELDS } from "@/constants/signTransactionPage";
Expand Down Expand Up @@ -80,63 +80,46 @@ export const ImportStepContent = ({
}
})();

// Push parse-derived fields into the import flow store as a unit so every
// entry point (paste here, or the cli-sign deep link) leaves identical state.
const applyParseResult = (result: ParsedImportXdr) => {
setImportParsedType(result.parsedTxType);
setImportHasSignatures(result.hasSignatures);
setImportIsSimulated(result.isSimulated);
setImportIsFeeBump(result.isFeeBump);
setImportParseError(result.parseError);
};

const onChange = (value: string) => {
setImportXdr(value);

if (!value) {
setImportParseError(null);
setImportParsedType(null);
setImportHasSignatures(false);
setImportIsSimulated(false);
setImportIsFeeBump(false);
return;
if (value) {
trackEvent(TrackingEvent.TRANSACTION_IMPORT_XDR_PASTE);
}

trackEvent(TrackingEvent.TRANSACTION_IMPORT_XDR_PASTE);
const result = parseImportXdr(value, network.passphrase);
applyParseResult(result);

const xdrValidation = validate.getXdrError(value);

if (xdrValidation?.result === "error") {
setImportParseError(xdrValidation.message ?? "Invalid XDR");
setImportParsedType(null);
setImportHasSignatures(false);
setImportIsSimulated(false);
setImportIsFeeBump(false);
trackEvent(TrackingEvent.TRANSACTION_IMPORT_XDR_INVALID);
return;
}

try {
const tx = TransactionBuilder.fromXDR(value, network.passphrase) as
| Transaction
| FeeBumpTransaction;

const operations = isFeeBumpTransaction(tx)
? tx.innerTransaction.operations
: tx.operations;

const isSoroban = isSorobanOperationType(operations?.[0]?.type ?? "");

setImportParsedType(isSoroban ? "soroban" : "classic");
setImportHasSignatures(tx.signatures.length > 0);
setImportIsSimulated(isSoroban && hasSorobanData(tx));
setImportIsFeeBump(isFeeBumpTransaction(tx));
setImportParseError(null);
trackEvent(TrackingEvent.TRANSACTION_IMPORT_XDR_VALID);
} catch (e) {
setImportParseError(
e instanceof Error
? e.message
: "Unable to parse transaction envelope XDR",
if (value) {
trackEvent(
result.parseError
? TrackingEvent.TRANSACTION_IMPORT_XDR_INVALID
: TrackingEvent.TRANSACTION_IMPORT_XDR_VALID,
);
setImportParsedType(null);
setImportHasSignatures(false);
setImportIsSimulated(false);
setImportIsFeeBump(false);
trackEvent(TrackingEvent.TRANSACTION_IMPORT_XDR_INVALID);
}
};

// Parse XDR that arrived from an external entry point — e.g. the CLI deep
// link at `/transaction/cli-sign`, which sets only `importXdr` before
// redirecting here. Without this, the derived fields stay empty and the
// overview never renders. Runs once on mount; pasting goes through onChange.
useEffect(() => {
if (importXdr && !parsedTxType && !parseError) {
applyParseResult(parseImportXdr(importXdr, network.passphrase));
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
Comment thread
jeesunikim marked this conversation as resolved.

const renderSuccessImportAlert = () => {
if (isMultisigDeferred) {
return (
Expand Down
111 changes: 0 additions & 111 deletions src/app/(sidebar)/transaction/sign/components/Import.tsx

This file was deleted.

Loading
Loading