Skip to content

Commit

Permalink
fix: use unified memo field and extract memo value from different coi…
Browse files Browse the repository at this point in the history
…n transaction implementation/labels (#8392)
  • Loading branch information
themooneer authored Nov 20, 2024
1 parent be83cab commit 72245a5
Show file tree
Hide file tree
Showing 13 changed files with 161 additions and 34 deletions.
5 changes: 5 additions & 0 deletions .changeset/rude-zebras-thank.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"ledger-live-desktop": minor
---

use common MemoTagField for sol/xrp and extract memo related value from transaction coin/by/coin
Original file line number Diff line number Diff line change
@@ -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");
});
});
Original file line number Diff line number Diff line change
@@ -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",
];
33 changes: 33 additions & 0 deletions apps/ledger-live-desktop/src/newArch/features/MemoTag/utils.ts
Original file line number Diff line number Diff line change
@@ -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;
}
};
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand All @@ -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" ? (
<Input
<InputField
warning={status.warnings.memo}
error={status.errors.memo}
value={transaction.model.uiState.memo || ""}
onChange={onMemoValueChange}
placeholder={t("families.solana.memoPlaceholder")}
autoFocus={autoFocus}
/>
) : null;
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -17,15 +18,19 @@ type Props = {
};

const Root = (props: Props) => {
const lldMemoTag = useFeature("lldMemoTag");

return (
<Box flow={1}>
<Box mb={10}>
<Label>
<span>
<Trans i18nKey="families.solana.memo" />
</span>
</Label>
</Box>
{!lldMemoTag?.enabled && (
<Box mb={10}>
<Label>
<span>
<Trans i18nKey="families.solana.memo" />
</span>
</Label>
</Box>
)}
<Box mb={15} horizontal grow alignItems="center" justifyContent="space-between">
<Box grow={1}>
<MemoValueField {...props} />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ const MemoTypeField = ({
onChange(
bridge.updateTransaction(transaction, {
memoType: memoType.value,
memoValue: memoType.value === "NO_MEMO" ? "" : transaction.memoValue,
}),
);
},
Expand Down
Original file line number Diff line number Diff line change
@@ -1,27 +1,32 @@
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 = ({
onChange,
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) => {
Expand All @@ -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 (
<Input
<InputField
warning={status.warnings.transaction}
error={status.errors.transaction}
value={transaction.comment.text}
placeholder={t("families.ton.commentPlaceholder")}
onChange={onCommentFieldChange}
spellCheck="false"
autoFocus={autoFocus}
/>
);
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,25 +6,30 @@ 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;
transaction: Transaction;
status: TransactionStatus;
onChange: (a: Transaction) => void;
trackProperties?: object;
autoFocus?: boolean;
}) => {
const lldMemoTag = useFeature("lldMemoTag");
return (
<Box flow={1}>
<Box mb={10}>
<Label>
<LabelInfoTooltip text={<Trans i18nKey="errors.TonCommentInvalid.title" />}>
<span>
<Trans i18nKey="families.ton.comment" />
</span>
</LabelInfoTooltip>
</Label>
</Box>
{!lldMemoTag?.enabled && (
<Box mb={10}>
<Label>
<LabelInfoTooltip text={<Trans i18nKey="errors.TonCommentInvalid.title" />}>
<span>
<Trans i18nKey="families.ton.comment" />
</span>
</LabelInfoTooltip>
</Label>
</Box>
)}
<Box
mb={15}
horizontal
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@ type Props = {
onChange: (a: Transaction) => 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);
Expand All @@ -31,7 +32,13 @@ const TagField = ({ onChange, account, transaction }: Props) => {
},
[onChange, account, transaction],
);
return <MemoTagField value={String(transaction.tag || "")} onChange={onChangeTag} />;
return (
<MemoTagField
value={String(transaction.tag || "")}
onChange={onChangeTag}
autoFocus={autoFocus}
/>
);
};
export default {
component: TagField,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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
) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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`
Expand Down Expand Up @@ -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");
};
Expand Down
2 changes: 1 addition & 1 deletion apps/ledger-live-desktop/src/renderer/modals/Send/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down

0 comments on commit 72245a5

Please sign in to comment.