Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
45323d4
Feature/move history fetch to bg (#2273)
piyalbasu Oct 7, 2025
b61ec65
check for updated appdata before showing password modal (#2300)
piyalbasu Oct 8, 2025
91a49a5
stringify errors rather than using `cause` (#2302)
piyalbasu Oct 9, 2025
cc130fc
Feature/move icons to own hook (#2308)
piyalbasu Oct 15, 2025
c50cbc1
skip blockaid scan on first fetch of account-balances (#2310)
piyalbasu Oct 17, 2025
89b00b0
Merge branch 'master' into release/5.36.0
piyalbasu Oct 23, 2025
47203df
Dropdown menu option to copy wallet address (#2316)
leofelix077 Oct 27, 2025
3da9b6b
scroll on long strings; pretty print json (#2320)
piyalbasu Oct 27, 2025
9218e3f
Merge branch 'master' into release/5.36.0
aristidesstaffieri Oct 29, 2025
077117e
re-searching so should abort any in flight API requests (#2323)
piyalbasu Oct 30, 2025
45f8bd6
[FEATURE] new send/swap navigation flow (#2353)
aristidesstaffieri Nov 5, 2025
1779c51
[FEATURE] adds send and swap buttons to asset detail view (#2351)
aristidesstaffieri Nov 7, 2025
5199754
only fetch asset list data if needed (#2369)
piyalbasu Nov 7, 2025
cdafa95
[BUG] SAC token management improvements (#2374)
aristidesstaffieri Nov 10, 2025
85014fb
Merge branch 'master' into release/5.36.0
piyalbasu Nov 11, 2025
5e591ce
Feature/cache token prices (#2373)
piyalbasu Nov 11, 2025
6268562
load backend settings async on Account view (#2381)
piyalbasu Nov 13, 2025
4f2b5c6
Feature/use ledger key for home domains (#2363)
piyalbasu Nov 14, 2025
9a0dd5e
update version numbers for release
piyalbasu Nov 17, 2025
0aed2a4
rm unnecessary calls to make flows even faster (#2391)
piyalbasu Nov 19, 2025
1e8117a
makes send swap buttons stay in the container in full screen mode (#2…
piyalbasu Nov 19, 2025
d50e25b
add v1 of memo-required flow for transaction confirmation
leofelix077 Nov 20, 2025
631356b
adjust memo required flow for dapp and tx rebuild
leofelix077 Nov 21, 2025
f4bb26b
add memo max bytes error handling
leofelix077 Nov 21, 2025
37ab418
update transaction loose text strings
leofelix077 Nov 21, 2025
adbb936
update transaction loose text strings
leofelix077 Nov 21, 2025
5ef1a5b
Merge branch 'master' into feature/check-memo-required-on-send
leofelix077 Nov 21, 2025
d33b807
update tranlation with uppercase
leofelix077 Nov 21, 2025
6f912f8
Merge branch 'feature/check-memo-required-on-send' of github.com:stel…
leofelix077 Nov 21, 2025
99b5099
fix transaction fee setting
leofelix077 Nov 21, 2025
63ddc20
Merge branch 'release/5.36.0' of github.com:stellar/freighter into fe…
leofelix077 Nov 21, 2025
bd6034d
Merge branch 'master' into release/5.36.0
piyalbasu Nov 21, 2025
2b20b9d
Merge branch 'release/5.36.0' of github.com:stellar/freighter into fe…
leofelix077 Nov 21, 2025
ad3091e
adjust memo required on revalidation and add a container for message
leofelix077 Nov 21, 2025
4e74e54
update memo-required flow to slide from right
leofelix077 Nov 21, 2025
619a4af
update translation keys and memo required panes
leofelix077 Nov 21, 2025
1f3710d
use redux selector for allAccounts to properly update rename (#2403)
piyalbasu Nov 21, 2025
dc88115
Merge branch 'release/5.36.0' of github.com:stellar/freighter into fe…
leofelix077 Nov 21, 2025
7d52b3e
fix unit tests
leofelix077 Nov 21, 2025
1e9be95
Merge branch 'release/5.37.0' of github.com:stellar/freighter into fe…
leofelix077 Nov 26, 2025
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
3 changes: 2 additions & 1 deletion extension/e2e-tests/onboarding.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -369,7 +369,8 @@ test("Incorrect mnemonic phrase", async ({ page }) => {
const shuffledWords = shuffle(words);

for (let i = 0; i < shuffledWords.length; i++) {
await page.getByLabel(shuffledWords[i]).check({ force: true });
// Use nth() to handle duplicate labels by selecting the first matching element
await page.getByLabel(shuffledWords[i]).first().check({ force: true });
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm, does this work in the case of duped words? I would think from this code it would never select the 2nd instance of a word so if the phrase was "foo bar bar", it would end up just selecting the first instance of "bar" and it would try to validate "foo bar", which would fail

}

await page.getByTestId("display-mnemonic-phrase-confirm-btn").click();
Expand Down
112 changes: 67 additions & 45 deletions extension/src/popup/components/InternalTransaction/EditMemo/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { Field, FieldProps, Form, Formik } from "formik";
import { useTranslation } from "react-i18next";

import { View } from "popup/basics/layout/View";
import { useValidateMemo } from "popup/helpers/useValidateMemo";

import "./styles.scss";

Expand All @@ -19,61 +20,82 @@ interface EditMemoProps {

export const EditMemo = ({ memo, onClose, onSubmit }: EditMemoProps) => {
const { t } = useTranslation();
const [localMemo, setLocalMemo] = React.useState(memo);
const { error: memoError } = useValidateMemo(localMemo);

const initialValues: FormValue = {
memo,
};
const handleSubmit = async (values: FormValue) => {

const handleSubmit = (values: FormValue) => {
// Prevent submission if there's a validation error
if (memoError) {
return;
}
onSubmit(values);
};

const handleFieldChange = (value: string) => {
setLocalMemo(value);
};

const renderField = ({ field }: FieldProps) => (
<Input
data-testid="edit-memo-input"
autoFocus
fieldSize="md"
autoComplete="off"
id="memo"
placeholder={t("Type your memo")}
{...field}
onChange={(e) => {
field.onChange(e);
handleFieldChange(e.target.value);
}}
error={memoError}
/>
);

const renderForm = () => (
<Form className="EditMemo__form">
<Field name="memo">{renderField}</Field>
<div className="EditMemo__description">
{t("What is this transaction for? (optional)")}
</div>
<div className="EditMemo__actions">
<Button
type="button"
size="md"
isRounded
variant="tertiary"
onClick={onClose}
>
{t("Cancel")}
</Button>
<Button
type="submit"
size="md"
isRounded
variant="secondary"
disabled={!!memoError}
>
{t("Save")}
</Button>
</div>
</Form>
);

return (
<View.Content hasNoTopPadding>
<div className="EditMemo">
<Card>
<p>Memo</p>
<Formik initialValues={initialValues} onSubmit={handleSubmit}>
{({ errors }) => (
<>
<Form className="EditMemo__form">
<Field name="memo">
{({ field }: FieldProps) => (
<Input
data-testid="edit-memo-input"
autoFocus
fieldSize="md"
autoComplete="off"
id="memo"
placeholder={"Memo"}
{...field}
error={errors.memo}
/>
)}
</Field>
<div className="EditMemo__description">
What is this transaction for? (optional)
</div>
<div className="EditMemo__actions">
<Button
type="button"
size="md"
isRounded
variant="tertiary"
onClick={onClose}
>
{t("Cancel")}
</Button>
<Button
type="submit"
size="md"
isRounded
variant="secondary"
>
{t("Save")}
</Button>
</div>
</Form>
</>
)}
<p>{t("Memo")}</p>
<Formik
initialValues={initialValues}
onSubmit={handleSubmit}
enableReinitialize
>
{renderForm}
</Formik>
</Card>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,8 @@ export const EditSettings = ({
fieldSize="md"
autoComplete="off"
id="fee"
placeholder={"Fee"}
label="Transaction Fee"
placeholder={t("Fee")}
label={t("Transaction Fee")}
{...field}
error={errors.fee}
onChange={(e) => {
Expand Down Expand Up @@ -92,7 +92,7 @@ export const EditSettings = ({
</Field>
<div className="EditTxSettings__congestion">
<CongestionIndicator congestion={congestion} />
{congestion} congestion
{congestion} {t("congestion")}
</div>
<Field name="timeout">
{({ field }: FieldProps) => (
Expand All @@ -101,8 +101,8 @@ export const EditSettings = ({
fieldSize="md"
autoComplete="off"
id="timeout"
placeholder={"Timeout"}
label="Timeout (seconds)"
placeholder={t("Timeout")}
label={t("Timeout (seconds)")}
{...field}
error={errors.timeout}
onChange={(e) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,13 @@ import { IdenticonImg } from "popup/components/identicons/IdenticonImg";
import {
BlockaidTxScanLabel,
BlockAidTxScanExpanded,
MemoRequiredLabel,
} from "popup/components/WarningMessages";
import { HardwareSign } from "popup/components/hardwareConnect/HardwareSign";
import { hardwareWalletTypeSelector } from "popup/ducks/accountServices";
import { MultiPaneSlider } from "popup/components/SlidingPaneSwitcher";
import { CopyValue } from "popup/components/CopyValue";
import { useValidateTransactionMemo } from "popup/helpers/useValidateTransactionMemo";

import "./styles.scss";

Expand All @@ -49,6 +51,7 @@ interface ReviewTxProps {
title: string;
onConfirm: () => void;
onCancel: () => void;
onAddMemo?: () => void;
}

export const ReviewTx = ({
Expand All @@ -63,6 +66,7 @@ export const ReviewTx = ({
title,
onConfirm,
onCancel,
onAddMemo,
}: ReviewTxProps) => {
const { t } = useTranslation();
const dispatch = useDispatch();
Expand All @@ -77,6 +81,14 @@ export const ReviewTx = ({
transactionData: { destination, memo, federationAddress },
} = submission;

// Validate memo requirements using the transaction XDR
const transactionXdr = simulationState.data?.transactionXdr;
const { isMemoMissing: isRequiredMemoMissing, isValidatingMemo } =
useValidateTransactionMemo(transactionXdr);

// Disable button while validating or if memo is missing
const isSubmitDisabled = isRequiredMemoMissing || isValidatingMemo;

const asset = getAssetFromCanonical(srcAsset);
const dest = dstAsset ? getAssetFromCanonical(dstAsset.canonical) : null;
const assetIcons = srcAsset !== "native" ? { [srcAsset]: assetIcon } : {};
Expand Down Expand Up @@ -200,18 +212,21 @@ export const ReviewTx = ({
onClick={() => setActivePaneIndex(1)}
/>
)}
{isRequiredMemoMissing && !isValidatingMemo && (
<MemoRequiredLabel onClick={() => setActivePaneIndex(2)} />
)}
</div>
<div className="ReviewTx__Details">
<div className="ReviewTx__Details__Row">
<div className="ReviewTx__Details__Row__Title">
<Icon.File02 />
Memo
{t("Memo")}
</div>
<div
className="ReviewTx__Details__Row__Value"
data-testid="review-tx-memo"
>
{memo || "None"}
{memo || t("None")}
</div>
</div>
<div className="ReviewTx__Details__Row">
Expand Down Expand Up @@ -244,29 +259,76 @@ export const ReviewTx = ({
scanResult={simulationState.data?.scanResult!}
onClose={() => setActivePaneIndex(0)}
/>,
<div className="ReviewTx__MemoDetails">
<div className="ReviewTx__MemoDetails__Header">
<div className="ReviewTx__MemoDetails__Header__Icon">
<Icon.InfoOctagon className="WarningMessage__icon" />
</div>
<div
className="ReviewTx__MemoDetails__Header__Close"
onClick={() => setActivePaneIndex(0)}
>
<Icon.X />
</div>
</div>
<div className="ReviewTx__MemoDetails__Title">
<span>{t("Memo is required")}</span>
</div>
<div className="ReviewTx__MemoDetails__Content">
<div className="ReviewTx__MemoDetails__Text">
{t(
"Some destination accounts on the Stellar network require a memo to identify your payment.",
)}
</div>
<div className="ReviewTx__MemoDetails__Text">
{t(
"If a required memo is missing or incorrect, your funds may not reach the intended recipient.",
)}
</div>
</div>
</div>,
]}
/>
<div className="ReviewTx__Actions">
<Button
size="lg"
isFullWidth
isRounded
variant="secondary"
data-testid="SubmitAction"
onClick={(e) => {
e.preventDefault();
onConfirmTx();
}}
>
{dstAsset && dest
? `Swap ${asset.code} to ${dest.code}`
: `Send to ${truncatedDest}`}
</Button>
{isRequiredMemoMissing && !isValidatingMemo && onAddMemo ? (
<Button
size="lg"
isFullWidth
isRounded
variant="secondary"
data-testid="AddMemoAction"
onClick={(e) => {
e.preventDefault();
onAddMemo();
}}
>
{t("Add Memo")}
</Button>
) : (
<Button
size="lg"
isFullWidth
isRounded
variant="secondary"
data-testid="SubmitAction"
disabled={isSubmitDisabled}
isLoading={isValidatingMemo}
onClick={(e) => {
e.preventDefault();
onConfirmTx();
}}
>
{dstAsset && dest
? `Swap ${asset.code} to ${dest.code}`
: `Send to ${truncatedDest}`}
</Button>
)}
<Button
size="lg"
isFullWidth
isRounded
variant="tertiary"
disabled={isValidatingMemo}
onClick={(e) => {
e.preventDefault();
onCancel();
Expand Down
Loading