fix(firo): fill spark memo from payment URI message
What changed, and why it matters
This commit fixes a small user-experience bug in Stack Wallet for Firo cryptocurrency. When a user scanned or pasted a firo: payment link containing a message, the app previously put that message only in the local private note field. Now, if the recipient is a Spark privacy address, the message is also placed in the Spark memo field, matching how the official Firo desktop wallet (firo-qt) behaves. This is a feature-parity fix, not a security patch.
No security action required. Treat as normal functional fix. If desired, review whether memoController.text assignment could ever overflow UI limits or whether paymentData.message needs length validation, but this is a product-quality concern rather than a vulnerability.
Security signals we found
No input sanitization on URI-derived memo before assigning to controller
Behavior aligned with firo-qt reference implementation
No changes to signing, encryption, address parsing, or network calls
No privilege escalation, authentication bypass, or asset-loss mechanism visible in diff
Evidence from the diff
The change adds identical blocks in both mobile and desktop send views. After parsing a BIP21-style payment URI, if the coin is Firo, a message parameter exists, and the destination address validates as a Spark address via SparkInterface.validateSparkAddress(), the code sets memoController.text to paymentData.message. Previously only paymentData.label populated the local note. There is no validation, sanitization, or cryptographic change; the memo is user-facing data intended for the recipient.
Changed components
lib/pages/send_view/send_view.dartlib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_send.dartInspect captured patch +20 / −0
### lib/pages/send_view/send_view.dart
@@ -169,6 +169,16 @@ class _SendViewState extends ConsumerState<SendView> {
noteController.text = paymentData.label!;
}
+ // firo-qt treats message as the spark memo when paying a spark address
+ if (coin is Firo &&
+ paymentData.message != null &&
+ SparkInterface.validateSparkAddress(
+ address: _address!,
+ isTestNet: coin.network.isTestNet,
+ )) {
+ memoController.text = paymentData.message!;
+ }
+
// autofill amount field
if (paymentData.amount != null) {
final amount = Amount.tryParseCanonicalAmount(
### lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_send.dart
@@ -1010,6 +1010,16 @@ class _DesktopSendState extends ConsumerState<DesktopSend> {
_note = paymentData.label;
}
+ // firo-qt treats message as the spark memo when paying a spark address
+ if (coin is Firo &&
+ paymentData.message != null &&
+ SparkInterface.validateSparkAddress(
+ address: _address!,
+ isTestNet: coin.network.isTestNet,
+ )) {
+ memoController.text = paymentData.message!;
+ }
+
// autofill amount field
if (paymentData.amount != null) {
final amount = Amount.tryParseCanonicalAmount(Why this scored 19/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.