Noble send: memo over 256 chars is signed then rejected at broadcast (memo too large) (#5435) - #5436
Conversation
A memo longer than the chain's ceiling passed Send -> Verify -> Keysign
with no warning and only failed at broadcast ("maximum number of
characters is 256 but received 26342 characters: memo too large"),
burning a full multi-device signing ceremony on a transaction the node
was never going to accept.
The send form now resolves the ceiling per chain from the chain's
published auth params (Chain.maxMemoCharacters — 256 on Noble, 512 on
Cosmos Hub, null where no ceiling applies), shows an inline error naming
the current length and the limit, and disables Continue while the memo
is over it, so keysign can't start. DefaultSendStrategy re-checks at
submit so a tap that races the form's per-keystroke validation can't
slip through either.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…cast (memo too large) (#5435)
📝 WalkthroughWalkthroughThe send flow now validates memo length against per-chain limits, updates errors as the token or memo changes, blocks continuation for invalid plain sends, displays localized field errors, and rechecks memo length before submission. Unrelated formatting changes preserve existing behavior. ChangesMemo validation flow
Source formatting cleanup
Estimated code review effort: 3 (Moderate) | ~20 minutes Sequence Diagram(s)sequenceDiagram
participant User
participant SendFormGraph
participant SendFormUiModel
participant SendFormAmountSection
participant DefaultSendStrategy
User->>SendFormGraph: enter memo or change token
SendFormGraph->>SendFormUiModel: update memoError
SendFormUiModel->>SendFormAmountSection: expose blocking error state
SendFormAmountSection-->>User: show expanded memo error
User->>DefaultSendStrategy: submit send
DefaultSendStrategy->>DefaultSendStrategy: revalidate memo length
DefaultSendStrategy-->>User: reject invalid transaction data
Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 2 | ❌ 3❌ Failed checks (3 warnings)
✅ Passed checks (2 passed)
✨ Finishing Touches 💡 1📝 Generate docstrings 💡
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@app/src/main/res/values/strings.xml`:
- Line 178: Update send_error_memo_too_long to describe the measured UTF-8 value
as bytes or with unit-neutral wording in all affected files:
app/src/main/res/values/strings.xml lines 178-178,
app/src/main/res/values-pt/strings.xml lines 198-198,
app/src/main/res/values-ru/strings.xml lines 105-105, and
app/src/main/res/values-zh-rCN/strings.xml lines 197-197. Keep the placeholders
and maximum-value meaning unchanged.
In `@data/src/main/kotlin/com/vultisig/wallet/data/models/Chain.kt`:
- Around line 563-572: Update the chain memo-size mapping in Chain’s relevant
size-limit function so Chain.ThorChain is handled separately with a 250-byte
limit instead of sharing 256 with the other chains. Add boundary coverage
verifying 250-byte memos are accepted and 251-byte memos are rejected for
THORChain.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: d6dba67a-108b-48b5-a9d8-544796d37fa1
📒 Files selected for processing (18)
app/src/main/java/com/vultisig/wallet/ui/models/send/SendFormGraph.ktapp/src/main/java/com/vultisig/wallet/ui/models/send/SendFormUiModel.ktapp/src/main/java/com/vultisig/wallet/ui/models/send/submit/DefaultSendStrategy.ktapp/src/main/java/com/vultisig/wallet/ui/screens/send/SendFormAmountSection.ktapp/src/main/res/values-de/strings.xmlapp/src/main/res/values-es/strings.xmlapp/src/main/res/values-hr/strings.xmlapp/src/main/res/values-it/strings.xmlapp/src/main/res/values-ko/strings.xmlapp/src/main/res/values-nl/strings.xmlapp/src/main/res/values-pt/strings.xmlapp/src/main/res/values-ru/strings.xmlapp/src/main/res/values-zh-rCN/strings.xmlapp/src/main/res/values/strings.xmlapp/src/test/java/com/vultisig/wallet/ui/models/send/SendFormContinueGateTest.ktdata/src/main/kotlin/com/vultisig/wallet/data/models/Chain.ktdata/src/main/kotlin/com/vultisig/wallet/data/models/Coins.ktdata/src/main/kotlin/com/vultisig/wallet/data/usecases/EvmCoinFinder.kt
|
Picked up CodeRabbit feedback (actionable issues only, skipping nitpicks). Working on it. |
Report the memo length in the unit actually measured: the Cosmos SDK compares the memo's UTF-8 byte length against max_memo_characters, so the validator now measures bytes only (the codepoint count could never exceed it) and every locale's error string says "bytes" instead of "characters". Split Chain.ThorChain out at its documented 250-byte memo ceiling instead of inheriting the SDK default of 256, with boundary coverage at 250/251. Drop the unrelated ktfmt-only reformat of Coins.kt and EvmCoinFinder.kt, which were already unformatted on main and are out of this PR's scope. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Review feedback addressed — 34d1a70Inline comments (2/2 replied)
Measured unit. THORChain. Split out as Pre-merge checksOut of Scope Changes — addressed. Linked Issues — respectfully disagree with the suggested fix. The check asks that validation match a character limit and accept any memo at or under it regardless of byte size. That would reintroduce the bug: the Cosmos SDK compares Docstring Coverage — no action. Every new public declaration already carries KDoc ( Verification
|
|
Picked up CodeRabbit feedback (actionable issues only, skipping nitpicks). Working on it. |
Coins.kt and EvmCoinFinder.kt were reflowed by a repo-wide ktfmtFormat run but carry no functional change for this PR. Revert both to main so the diff stays scoped to the memo-limit fix. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Addressed the pre-merge check warnings. Commit: ✅ Fixed — Out of Scope Changes check
Valid. Both files had been reflowed by a repo-wide The PR diff drops from 1253 insertions / 1062 deletions across 18 files to 187 insertions / 1 deletion across 16 files, now entirely the memo-limit fix. Reverting is safe for CI: ❌ Declined — Linked Issues check
Byte counting is the correct behaviour here, and this warning asks for the change that would reintroduce the bug. The Cosmos SDK's Validating by character count instead would let those transactions pass the gate and burn a full multi-device keysign ceremony on a payload the chain can never accept. The param's name is the misleading part; the KDoc on ❌ Declined — Docstring Coverage (0.00%)Not reproducible against the diff. Every new public declaration this PR adds already carries KDoc: Verification
Inline comments: 2 top-level threads on this PR, both already answered and marked confirmed-addressed in |
- memoLengthErrorOrNull: a blank memo never errors, matching the submit path dropping a whitespace-only memo to null - ThorChain: enforce the envelope max_memo_characters (256) a plain send is checked against, not thornode's MsgDeposit-only 250-byte cap - Terra (TERRAV2 / phoenix-1): max_memo_characters is 512, not 256 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Addressed review feedback —
|
There was a problem hiding this comment.
🧹 Nitpick comments (1)
app/src/test/java/com/vultisig/wallet/ui/models/send/SendFormContinueGateTest.kt (1)
90-99: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick winAdd a Terra Classic regression case.
This test covers Gaia/Terra 2.0 at 512 bytes and Noble at 256 bytes, but does not verify the explicitly corrected Terra Classic 256-byte limit. Add an over-limit assertion for Terra Classic so a future mapping regression is caught.
Proposed assertion
assertNull(memoLengthErrorOrNull(Chain.GaiaChain, memo)) assertNull(memoLengthErrorOrNull(Chain.Terra, memo)) assertNotNull(memoLengthErrorOrNull(Chain.Noble, memo)) + assertNotNull(memoLengthErrorOrNull(Chain.TerraClassic, memo))As per the PR objective, Terra Classic remains at 256 bytes.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@app/src/test/java/com/vultisig/wallet/ui/models/send/SendFormContinueGateTest.kt` around lines 90 - 99, Extend the `limit is resolved per chain` test to assert that a 300-character memo produces an error for `Chain.TerraClassic`, alongside the existing Noble assertion, preserving the expected 512-character limits for Gaia and Terra.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In
`@app/src/test/java/com/vultisig/wallet/ui/models/send/SendFormContinueGateTest.kt`:
- Around line 90-99: Extend the `limit is resolved per chain` test to assert
that a 300-character memo produces an error for `Chain.TerraClassic`, alongside
the existing Noble assertion, preserving the expected 512-character limits for
Gaia and Terra.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: 76f6823b-aea1-4d9a-8a63-0de61c0bbc4d
📒 Files selected for processing (13)
app/src/main/java/com/vultisig/wallet/ui/models/send/SendFormUiModel.ktapp/src/main/res/values-de/strings.xmlapp/src/main/res/values-es/strings.xmlapp/src/main/res/values-hr/strings.xmlapp/src/main/res/values-it/strings.xmlapp/src/main/res/values-ko/strings.xmlapp/src/main/res/values-nl/strings.xmlapp/src/main/res/values-pt/strings.xmlapp/src/main/res/values-ru/strings.xmlapp/src/main/res/values-zh-rCN/strings.xmlapp/src/main/res/values/strings.xmlapp/src/test/java/com/vultisig/wallet/ui/models/send/SendFormContinueGateTest.ktdata/src/main/kotlin/com/vultisig/wallet/data/models/Chain.kt
🚧 Files skipped from review as they are similar to previous changes (8)
- app/src/main/res/values-nl/strings.xml
- app/src/main/res/values-ko/strings.xml
- app/src/main/res/values-de/strings.xml
- app/src/main/res/values/strings.xml
- app/src/main/res/values-hr/strings.xml
- app/src/main/res/values-es/strings.xml
- app/src/main/res/values-zh-rCN/strings.xml
- data/src/main/kotlin/com/vultisig/wallet/data/models/Chain.kt
|
Picked up CodeRabbit feedback (actionable issues only, skipping nitpicks). Working on it. |
Coins.kt and EvmCoinFinder.kt were reformatted by a repo-wide ktfmtFormat run, but neither is touched by the memo-limit fix. Restore both to main so the PR only carries the memo validation, UI gating and tests. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Pre-merge check follow-up —
|
Fixes #5435
Changes
Chain.kt: newChain.maxMemoCharactersextension property returning each Cosmos-SDK chain'smax_memo_characters(512 forGaiaChain, 256 forNoble/Osmosis/Kujira/Dydx/Terra/…),nullfor chains with no enforceable ceiling.SendFormUiModel.kt: addedmemoError: UiText?field, anisMemoBlockingderived property (only blocks whenhasMemois true anddefiType == null), wiredisMemoBlockingintoisContinueDisabled(), and addedmemoLengthErrorOrNull(chain, memo)which takesmaxOf(codePointCount, UTF-8 byte size)so multi-byte memos that pass the character count but exceed the node's byte limit still error.SendFormGraph.kt: newscope.launchcombiningselectedTokenwithmemoFieldState.textAsFlow()to recomputememoErrorper keystroke and on network switch (limit is per-chain).DefaultSendStrategy.kt: re-validates the user memo withmemoLengthErrorOrNullbefore building the transaction, throwingInvalidTransactionDataExceptionso a submit racing the form's validation can't start a keysign that fails at broadcast (Noble send: memo over 256 chars is signed then rejected at broadcast (memo too large) #5435).SendFormAmountSection.kt: memo field inFoldableAmountWidgetnow auto-expands viaLaunchedEffect(state.memoError), can't be collapsed while erroring, and rendersVsTextInputFieldInnerState.Error+footNotewith the error text; newsend_error_memo_too_longstring added tovalues/plus all 9 locale files.SendFormContinueGateTest.kt: 7 new tests covering blocking on plain sends, non-blocking without a memo field, the at-limit boundary, per-chain limits, chains with no ceiling, error format args, and the emoji byte-limit case. (Coins.kt/EvmCoinFinder.ktchurn appears to be unrelated reformatting.)Checklist
Summary by CodeRabbit
send_error_memo_too_longstring in supported languages.