diff --git a/.changeset/rude-bats-fold.md b/.changeset/rude-bats-fold.md new file mode 100644 index 000000000000..027392d9dd24 --- /dev/null +++ b/.changeset/rude-bats-fold.md @@ -0,0 +1,7 @@ +--- +"@ledgerhq/coin-stacks": minor +"live-mobile": minor +"@ledgerhq/live-common": minor +--- + +Truncate Stacks memos in the input to prevent the transaction validation from failing diff --git a/apps/ledger-live-mobile/src/families/algorand/MemoTagInput.tsx b/apps/ledger-live-mobile/src/families/algorand/MemoTagInput.tsx index c21f44e967c5..ce2356498c2c 100644 --- a/apps/ledger-live-mobile/src/families/algorand/MemoTagInput.tsx +++ b/apps/ledger-live-mobile/src/families/algorand/MemoTagInput.tsx @@ -8,7 +8,7 @@ import { GenericMemoTagInput } from "LLM/features/MemoTag/components/GenericMemo export default (props: MemoTagInputProps) => ( text.slice(0, ALGORAND_MAX_MEMO_SIZE)} + maxLength={ALGORAND_MAX_MEMO_SIZE} valueToTxPatch={value => tx => ({ ...tx, memo: value || undefined })} /> ); diff --git a/apps/ledger-live-mobile/src/families/stacks/MemoTagInput.tsx b/apps/ledger-live-mobile/src/families/stacks/MemoTagInput.tsx index f15b17279ef1..0b667ab53b87 100644 --- a/apps/ledger-live-mobile/src/families/stacks/MemoTagInput.tsx +++ b/apps/ledger-live-mobile/src/families/stacks/MemoTagInput.tsx @@ -1,12 +1,21 @@ import React from "react"; +import { STACKS_MAX_MEMO_SIZE } from "@ledgerhq/live-common/families/stacks/constants"; import type { Transaction as StacksTransaction } from "@ledgerhq/live-common/families/stacks/types"; import type { MemoTagInputProps } from "LLM/features/MemoTag/types"; import { GenericMemoTagInput } from "LLM/features/MemoTag/components/GenericMemoTagInput"; +import { truncateUtf8 } from "LLM/utils/truncateUtf8"; + +// `TextInputProps.maxLength` can not be used here because it counts the length of the string instead of the byte size. +// E.g. +// Javascript will evaluate a 16👍 emojis string as 34 characters. +// While this string is in fact encoded in 68B in UTF8 which is well above STX limit. +// `truncateUtf8` will truncate the string correctly, which is 8👍 with 2 characters to spare. export default (props: MemoTagInputProps) => ( truncateUtf8(text, STACKS_MAX_MEMO_SIZE)} valueToTxPatch={value => tx => ({ ...tx, memo: value || undefined })} /> ); diff --git a/libs/coin-modules/coin-stacks/src/bridge/getTransactionStatus.ts b/libs/coin-modules/coin-stacks/src/bridge/getTransactionStatus.ts index 33fcfbee31a1..94812a0f53fc 100644 --- a/libs/coin-modules/coin-stacks/src/bridge/getTransactionStatus.ts +++ b/libs/coin-modules/coin-stacks/src/bridge/getTransactionStatus.ts @@ -8,6 +8,7 @@ import { } from "@ledgerhq/errors"; import { AccountBridge } from "@ledgerhq/types-live"; import BigNumber from "bignumber.js"; +import { STACKS_MAX_MEMO_SIZE } from "../contants"; import { StacksMemoTooLong } from "../errors"; import { Transaction, TransactionStatus } from "../types"; import { validateAddress } from "./utils/addresses"; @@ -44,7 +45,9 @@ export const getTransactionStatus: AccountBridge["getTransactionSta if (amount.lte(0)) errors.amount = new AmountRequired(); if (totalSpent.gt(spendableBalance)) errors.amount = new NotEnoughBalance(); - if (memo && memo.length > 34) errors.transaction = new StacksMemoTooLong(); + + const memoBytesLength = Buffer.from(memo ?? "", "utf-8").byteLength; + if (memoBytesLength > STACKS_MAX_MEMO_SIZE) errors.transaction = new StacksMemoTooLong(); return { errors, diff --git a/libs/coin-modules/coin-stacks/src/contants.ts b/libs/coin-modules/coin-stacks/src/contants.ts new file mode 100644 index 000000000000..8566bed0e242 --- /dev/null +++ b/libs/coin-modules/coin-stacks/src/contants.ts @@ -0,0 +1 @@ +export const STACKS_MAX_MEMO_SIZE = 34; diff --git a/libs/ledger-live-common/.unimportedrc.json b/libs/ledger-live-common/.unimportedrc.json index 27a029333edb..e27e298e85b7 100644 --- a/libs/ledger-live-common/.unimportedrc.json +++ b/libs/ledger-live-common/.unimportedrc.json @@ -124,6 +124,7 @@ "src/families/solana/react.ts", "src/families/solana/staking.ts", "src/families/solana/types.ts", + "src/families/stacks/constants.ts", "src/families/stacks/deviceTransactionConfig.ts", "src/families/stacks/types.ts", "src/families/stellar/types.ts", diff --git a/libs/ledger-live-common/src/families/stacks/constants.ts b/libs/ledger-live-common/src/families/stacks/constants.ts new file mode 100644 index 000000000000..57651b257602 --- /dev/null +++ b/libs/ledger-live-common/src/families/stacks/constants.ts @@ -0,0 +1,2 @@ +// Encapsulate for LLD & LLM +export * from "@ledgerhq/coin-stacks/contants";