diff --git a/.changeset/rude-zebras-thank.md b/.changeset/rude-zebras-thank.md new file mode 100644 index 000000000000..4deeafeee763 --- /dev/null +++ b/.changeset/rude-zebras-thank.md @@ -0,0 +1,5 @@ +--- +"ledger-live-desktop": minor +--- + +use common MemoTagField for sol/xrp and extract memo related value from transaction coin/by/coin diff --git a/apps/ledger-live-desktop/src/newArch/features/MemoTag/__tests__/utils.test.ts b/apps/ledger-live-desktop/src/newArch/features/MemoTag/__tests__/utils.test.ts new file mode 100644 index 000000000000..4b72db74ab52 --- /dev/null +++ b/apps/ledger-live-desktop/src/newArch/features/MemoTag/__tests__/utils.test.ts @@ -0,0 +1,47 @@ +import { getMemoTagValueByTransactionFamily } from "../utils"; +import { Transaction } from "@ledgerhq/live-common/generated/types"; +import { Transaction as StellarTransaction } from "@ledgerhq/live-common/families/stellar/types"; +import { Transaction as SolanaTransaction } from "@ledgerhq/live-common/families/solana/types"; + +describe("getMemoTagValueByTransactionFamily", () => { + it("should return empty string if transaction family is not recognized", () => { + const transaction: Transaction = { family: "unknown" } as Transaction; + expect(getMemoTagValueByTransactionFamily(transaction)).toBeUndefined(); + }); + + it("should return tag for xrp family", () => { + const transaction: Transaction = { family: "xrp", tag: 12345 } as Transaction; + expect(getMemoTagValueByTransactionFamily(transaction)).toBe(12345); + }); + + it("should return comment text for ton family", () => { + const transaction: Transaction = { + family: "ton", + comment: { text: "Test comment" }, + } as Transaction; + expect(getMemoTagValueByTransactionFamily(transaction)).toBe("Test comment"); + }); + + it("should return memoValue for stellar family", () => { + const transaction: StellarTransaction = { + family: "stellar", + memoValue: "Stellar memo", + } as StellarTransaction; + expect(getMemoTagValueByTransactionFamily(transaction)).toBe("Stellar memo"); + }); + + it("should return memo for solana family", () => { + const transaction: SolanaTransaction = { + family: "solana", + model: { uiState: { memo: "Solana memo" } }, + } as SolanaTransaction; + expect(getMemoTagValueByTransactionFamily(transaction)).toBe("Solana memo"); + }); + + it("should return memo for default case", () => { + const transaction: Transaction = { family: "cosmos", memo: "Default memo" } as Transaction & { + memo: string; + }; + expect(getMemoTagValueByTransactionFamily(transaction)).toBe("Default memo"); + }); +}); diff --git a/apps/ledger-live-desktop/src/newArch/features/MemoTag/constants.ts b/apps/ledger-live-desktop/src/newArch/features/MemoTag/constants.ts index 77ccc2914a04..acd8e01332a9 100644 --- a/apps/ledger-live-desktop/src/newArch/features/MemoTag/constants.ts +++ b/apps/ledger-live-desktop/src/newArch/features/MemoTag/constants.ts @@ -1,15 +1,14 @@ export const MEMO_TAG_COINS: string[] = [ - "ripple", - "stellar", + "algorand", + "cardano", + "casper", "cosmos", - "hedera", - "injective", "crypto_org", - "crypto_org_croeseid", + "hedera", + "internet_computer", + "solana", "stacks", + "stellar", "ton", - "eos", - "bsc", - "casper", - "cardano", + "xrp", ]; diff --git a/apps/ledger-live-desktop/src/newArch/features/MemoTag/utils.ts b/apps/ledger-live-desktop/src/newArch/features/MemoTag/utils.ts new file mode 100644 index 000000000000..fe175df6b2e2 --- /dev/null +++ b/apps/ledger-live-desktop/src/newArch/features/MemoTag/utils.ts @@ -0,0 +1,33 @@ +import { Transaction } from "@ledgerhq/live-common/generated/types"; +import { Transaction as StellarTransaction } from "@ledgerhq/live-common/families/stellar/types"; +import { Transaction as SolanaTransaction } from "@ledgerhq/live-common/families/solana/types"; +import { MEMO_TAG_COINS } from "./constants"; + +/** + * Retrieves the memo tag value from a transaction based on its family type. + * + * @param {Transaction} transaction - The transaction object from which to extract the memo tag value. + * @returns {string | undefined} The memo tag value if the transaction family is supported, otherwise undefined. + */ +export const getMemoTagValueByTransactionFamily = (transaction: Transaction) => { + if (!MEMO_TAG_COINS.includes(transaction?.family as string)) return undefined; + const { family } = transaction; + switch (family) { + case "xrp": + return transaction?.tag; + case "ton": + return transaction?.comment?.text; + case "stellar": + return (transaction as StellarTransaction)?.memoValue; + case "solana": + return ( + transaction as SolanaTransaction & { + model: { + uiState: { memo: string }; + }; + } + )?.model.uiState.memo; + default: + return (transaction as Transaction & { memo: string })?.memo; + } +}; diff --git a/apps/ledger-live-desktop/src/renderer/families/solana/MemoValueField.tsx b/apps/ledger-live-desktop/src/renderer/families/solana/MemoValueField.tsx index 6f0b076a773d..a841bed5a8c7 100644 --- a/apps/ledger-live-desktop/src/renderer/families/solana/MemoValueField.tsx +++ b/apps/ledger-live-desktop/src/renderer/families/solana/MemoValueField.tsx @@ -8,16 +8,21 @@ import { Transaction, SolanaAccount, } from "@ledgerhq/live-common/families/solana/types"; +import { useFeature } from "@ledgerhq/live-common/featureFlags/index"; +import MemoTagField from "~/newArch/features/MemoTag/components/MemoTagField"; type Props = { onChange: (t: Transaction) => void; transaction: Transaction; status: TransactionStatus; account: SolanaAccount; + autoFocus?: boolean; }; -const MemoValueField = ({ onChange, account, transaction, status }: Props) => { +const MemoValueField = ({ onChange, account, transaction, status, autoFocus }: Props) => { const { t } = useTranslation(); + const lldMemoTag = useFeature("lldMemoTag"); + invariant(transaction.family === "solana", "Memo: solana family expected"); const bridge = getAccountBridge(account); const onMemoValueChange = useCallback( @@ -36,13 +41,17 @@ const MemoValueField = ({ onChange, account, transaction, status }: Props) => { }, [onChange, transaction, bridge], ); + + const InputField = lldMemoTag?.enabled ? MemoTagField : Input; + return transaction.model.kind === "transfer" || transaction.model.kind === "token.transfer" ? ( - ) : null; }; diff --git a/apps/ledger-live-desktop/src/renderer/families/solana/SendRecipientFields.tsx b/apps/ledger-live-desktop/src/renderer/families/solana/SendRecipientFields.tsx index 23bd4a699d1a..d15c25e9402d 100644 --- a/apps/ledger-live-desktop/src/renderer/families/solana/SendRecipientFields.tsx +++ b/apps/ledger-live-desktop/src/renderer/families/solana/SendRecipientFields.tsx @@ -8,6 +8,7 @@ import { Transaction, SolanaAccount, } from "@ledgerhq/live-common/families/solana/types"; +import { useFeature } from "@ledgerhq/live-common/featureFlags/index"; type Props = { onChange: (t: Transaction) => void; @@ -17,15 +18,19 @@ type Props = { }; const Root = (props: Props) => { + const lldMemoTag = useFeature("lldMemoTag"); + return ( - - - + {!lldMemoTag?.enabled && ( + + + + )} diff --git a/apps/ledger-live-desktop/src/renderer/families/stellar/MemoTypeField.tsx b/apps/ledger-live-desktop/src/renderer/families/stellar/MemoTypeField.tsx index f5579ba0df51..7e628d028de3 100644 --- a/apps/ledger-live-desktop/src/renderer/families/stellar/MemoTypeField.tsx +++ b/apps/ledger-live-desktop/src/renderer/families/stellar/MemoTypeField.tsx @@ -28,6 +28,7 @@ const MemoTypeField = ({ onChange( bridge.updateTransaction(transaction, { memoType: memoType.value, + memoValue: memoType.value === "NO_MEMO" ? "" : transaction.memoValue, }), ); }, diff --git a/apps/ledger-live-desktop/src/renderer/families/ton/CommentField.tsx b/apps/ledger-live-desktop/src/renderer/families/ton/CommentField.tsx index 09a302497386..03f952c18301 100644 --- a/apps/ledger-live-desktop/src/renderer/families/ton/CommentField.tsx +++ b/apps/ledger-live-desktop/src/renderer/families/ton/CommentField.tsx @@ -1,9 +1,11 @@ import { getAccountBridge } from "@ledgerhq/live-common/bridge/index"; import { Transaction, TransactionStatus } from "@ledgerhq/live-common/families/ton/types"; +import { useFeature } from "@ledgerhq/live-common/featureFlags/index"; import { Account } from "@ledgerhq/types-live"; import invariant from "invariant"; import React, { useCallback } from "react"; import { useTranslation } from "react-i18next"; +import MemoTagField from "~/newArch/features/MemoTag/components/MemoTagField"; import Input from "~/renderer/components/Input"; const CommentField = ({ @@ -11,17 +13,20 @@ const CommentField = ({ account, transaction, status, + autoFocus, }: { onChange: (a: Transaction) => void; account: Account; transaction: Transaction; status: TransactionStatus; + autoFocus?: boolean; }) => { invariant(transaction.family === "ton", "Comment: TON family expected"); const { t } = useTranslation(); const bridge = getAccountBridge(account); + const lldMemoTag = useFeature("lldMemoTag"); const onCommentFieldChange = useCallback( (value: string) => { @@ -34,16 +39,19 @@ const CommentField = ({ [onChange, transaction, bridge], ); + const InputField = lldMemoTag?.enabled ? MemoTagField : Input; + // We use transaction as an error here. // on the ledger-live mobile return ( - ); }; diff --git a/apps/ledger-live-desktop/src/renderer/families/ton/SendRecipientFields.tsx b/apps/ledger-live-desktop/src/renderer/families/ton/SendRecipientFields.tsx index 530bb70fe01d..5387a1d41d49 100644 --- a/apps/ledger-live-desktop/src/renderer/families/ton/SendRecipientFields.tsx +++ b/apps/ledger-live-desktop/src/renderer/families/ton/SendRecipientFields.tsx @@ -6,6 +6,7 @@ import Box from "~/renderer/components/Box"; import Label from "~/renderer/components/Label"; import LabelInfoTooltip from "~/renderer/components/LabelInfoTooltip"; import CommentField from "./CommentField"; +import { useFeature } from "@ledgerhq/live-common/featureFlags/index"; const Root = (props: { account: Account; @@ -13,18 +14,22 @@ const Root = (props: { status: TransactionStatus; onChange: (a: Transaction) => void; trackProperties?: object; + autoFocus?: boolean; }) => { + const lldMemoTag = useFeature("lldMemoTag"); return ( - - - + {!lldMemoTag?.enabled && ( + + + + )} void; transaction: Transaction; account: Account; + autoFocus?: boolean; }; const uint32maxPlus1 = BigNumber(2).pow(32); -const TagField = ({ onChange, account, transaction }: Props) => { +const TagField = ({ onChange, account, transaction, autoFocus }: Props) => { const onChangeTag = useCallback( (str: string) => { const bridge = getAccountBridge(account); @@ -31,7 +32,13 @@ const TagField = ({ onChange, account, transaction }: Props) => { }, [onChange, account, transaction], ); - return ; + return ( + + ); }; export default { component: TagField, diff --git a/apps/ledger-live-desktop/src/renderer/modals/Send/steps/StepRecipient.tsx b/apps/ledger-live-desktop/src/renderer/modals/Send/steps/StepRecipient.tsx index ae00356cea58..e6bbd99a18d2 100644 --- a/apps/ledger-live-desktop/src/renderer/modals/Send/steps/StepRecipient.tsx +++ b/apps/ledger-live-desktop/src/renderer/modals/Send/steps/StepRecipient.tsx @@ -28,6 +28,7 @@ import CheckBox from "~/renderer/components/CheckBox"; import { alwaysShowMemoTagInfoSelector } from "~/renderer/reducers/application"; import { toggleShouldDisplayMemoTagInfo } from "~/renderer/actions/application"; import { useFeature } from "@ledgerhq/live-common/featureFlags/index"; +import { getMemoTagValueByTransactionFamily } from "~/newArch/features/MemoTag/utils"; const StepRecipient = ({ t, @@ -153,9 +154,10 @@ export const StepRecipientFooter = ({ const alwaysShowMemoTagInfo = useSelector(alwaysShowMemoTagInfoSelector); const handleOnNext = async () => { + const memoTagValue = getMemoTagValueByTransactionFamily(transaction as Transaction); if ( lldMemoTag?.enabled && - !transaction?.memo && + !memoTagValue && MEMO_TAG_COINS.includes(transaction?.family as string) && alwaysShowMemoTagInfo ) { diff --git a/apps/ledger-live-desktop/src/renderer/modals/Send/steps/StepSummary.tsx b/apps/ledger-live-desktop/src/renderer/modals/Send/steps/StepSummary.tsx index 69aed778c0d4..dfb2006cb507 100644 --- a/apps/ledger-live-desktop/src/renderer/modals/Send/steps/StepSummary.tsx +++ b/apps/ledger-live-desktop/src/renderer/modals/Send/steps/StepSummary.tsx @@ -30,6 +30,7 @@ import { useMaybeAccountName } from "~/renderer/reducers/wallet"; import MemoIcon from "~/renderer/icons/MemoIcon"; import { Flex } from "@ledgerhq/react-ui"; import { useFeature } from "@ledgerhq/live-common/featureFlags/index"; +import { getMemoTagValueByTransactionFamily } from "~/newArch/features/MemoTag/utils"; const FromToWrapper = styled.div``; const Circle = styled.div` @@ -84,8 +85,13 @@ const StepSummary = (props: StepProps) => { const specific = currency ? getLLDCoinFamily(mainAccount.currency.family) : null; const SpecificSummaryNetworkFeesRow = specific?.StepSummaryNetworkFeesRow; - const memo = "memo" in transaction ? transaction.memo : undefined; - + const memo = lldMemoTag?.enabled + ? getMemoTagValueByTransactionFamily(transaction) + : ( + transaction as Transaction & { + memo: string; + } + )?.memo; const handleOnEditMemo = () => { transitionTo("recipient"); }; diff --git a/apps/ledger-live-desktop/src/renderer/modals/Send/types.ts b/apps/ledger-live-desktop/src/renderer/modals/Send/types.ts index 5c1ed0aee44b..8d96f1b3bfbf 100644 --- a/apps/ledger-live-desktop/src/renderer/modals/Send/types.ts +++ b/apps/ledger-live-desktop/src/renderer/modals/Send/types.ts @@ -12,7 +12,7 @@ export type StepProps = { device: Device | undefined | null; account: AccountLike | undefined | null; parentAccount: Account | undefined | null; - transaction: (Transaction & { memo?: string }) | undefined | null; + transaction: Transaction | undefined | null; status: TransactionStatus; bridgePending: boolean; error: Error | undefined | null;