What changed, and why it matters
This commit makes several small UI and error-handling tweaks in a cryptocurrency wallet app. The most user-relevant change is that address and amount text fields now disable autocorrect and predictive suggestions, which reduces the chance that a sensitive crypto address or amount gets leaked to a third-party keyboard/cloud service. Other changes clean up clipboard pasting, Bluetooth error handling, and which non-fatal errors are suppressed. There is no obvious severe security bug being fixed, but the autocorrect change is a privacy improvement.
Treat as a routine privacy/robustness patch. Review whether other sensitive input fields (seed phrase, password, memo fields) already disable autocorrect and suggestions consistently. No urgent security response is indicated by this diff alone.
Security signals we found
Disabling autocorrect/suggestions on sensitive input fields (address, amount) to reduce keyboard-side data exposure
No hardening of crypto primitives, seed storage, or network validation
Error-suppression changes are defensive and non-fatal only
Clipboard paste change is a robustness fix, not a security boundary change
Evidence from the diff
The patch touches five Dart files. (1) send_address_input.dart and (2) send_amount_input.dart add autocorrect: false and enableSuggestions: false to TextFields handling recipient addresses and amounts. (3) send_amount_input.dart also changes clipboard paste to set a full TextEditingValue with collapsed selection instead of assigning text directly. (4) connect_device_page.dart replaces a rethrow of a BLE error with a typed Object catch and a printV log. (5) decimal_input_formatter.dart returns a fully specified empty TextEditingValue. (6) exception_handler.dart expands an ignore-list with ‘Invalid image data’, ‘invalid selection start’, and a stack-shape check for a grpc-dart ‘Cannot add event after closing’ teardown race. No cryptographic, authentication, or network-trust boundary changes are visible.
Changed components
lib/new-ui/widgets/send_page/send_address_input.dartlib/new-ui/widgets/send_page/send_amount_input.dartlib/src/screens/connect_device/connect_device_page.dartlib/utils/decimal_input_formatter.dartlib/utils/exception_handler.dartInspect captured patch +35 / −4
diff --git a/lib/new-ui/widgets/send_page/send_address_input.dart b/lib/new-ui/widgets/send_page/send_address_input.dart
index d02b7105..26ae6986 100644
--- a/lib/new-ui/widgets/send_page/send_address_input.dart
+++ b/lib/new-ui/widgets/send_page/send_address_input.dart
@@ -88,6 +88,8 @@ class _NewSendAddressInputState extends State<NewSendAddressInput> {
children: [
TextField(
focusNode: widget.focusNode,
+ autocorrect: false,
+ enableSuggestions: false,
onSubmitted: (val) => FocusScope.of(context).unfocus(),
onChanged: state.didChange,
onEditingComplete: () {
diff --git a/lib/new-ui/widgets/send_page/send_amount_input.dart b/lib/new-ui/widgets/send_page/send_amount_input.dart
index a9576d13..2ed9b939 100644
--- a/lib/new-ui/widgets/send_page/send_amount_input.dart
+++ b/lib/new-ui/widgets/send_page/send_amount_input.dart
@@ -71,6 +71,8 @@ class _NewSendAmountInputState extends State<NewSendAmountInput> {
signed: false,
decimal: widget.maxDecimals > 0,
),
+ autocorrect: false,
+ enableSuggestions: false,
inputFormatters: <TextInputFormatter>[
DecimalInputFormatter(maxDecimals: widget.maxDecimals),
],
@@ -87,7 +89,11 @@ class _NewSendAmountInputState extends State<NewSendAmountInput> {
onPressed: () async {
final data = await Clipboard.getData(Clipboard.kTextPlain);
if (data != null && data.text != null) {
- widget.amountController.text = data.text!;
+ final text = data.text!;
+ widget.amountController.value = TextEditingValue(
+ text: text,
+ selection: TextSelection.collapsed(offset: text.length),
+ );
}
}),
],
diff --git a/lib/src/screens/connect_device/connect_device_page.dart b/lib/src/screens/connect_device/connect_device_page.dart
index 3e3bda04..6aeb36a2 100644
--- a/lib/src/screens/connect_device/connect_device_page.dart
+++ b/lib/src/screens/connect_device/connect_device_page.dart
@@ -149,8 +149,8 @@ class ConnectDevicePageBodyState extends State<ConnectDevicePageBody> {
bleDevices.add(device);
if (longWait) longWait = false;
}))
- ..onError((e) {
- throw e.toString();
+ ..onError((Object e) {
+ printV(e);
});
_bleRefreshTimer?.cancel();
_bleRefreshTimer = null;
diff --git a/lib/utils/decimal_input_formatter.dart b/lib/utils/decimal_input_formatter.dart
index 18a2b558..b45a554e 100644
--- a/lib/utils/decimal_input_formatter.dart
+++ b/lib/utils/decimal_input_formatter.dart
@@ -11,7 +11,12 @@ class DecimalInputFormatter extends TextInputFormatter {
final text = newValue.text;
if (text.isEmpty) return newValue;
- if (S.current.all.startsWith(text)) return TextEditingValue(text: "");
+ if (S.current.all.startsWith(text)) {
+ return const TextEditingValue(
+ text: "",
+ selection: TextSelection.collapsed(offset: 0),
+ );
+ }
final regex = maxDecimals == 0 ? RegExp(r'^\d*$') : RegExp('^\\d*([.,]\\d{0,$maxDecimals})?\$');
return regex.hasMatch(text) ? newValue.copyWith(text: text.replaceAll(',', '.')) : oldValue;
diff --git a/lib/utils/exception_handler.dart b/lib/utils/exception_handler.dart
index eee768c6..abbef80c 100644
--- a/lib/utils/exception_handler.dart
+++ b/lib/utils/exception_handler.dart
@@ -277,6 +277,7 @@ class ExceptionHandler {
"invalid signature",
"invalid password",
"NetworkImage._loadAsync",
+ "Invalid image data",
"SSLV3_ALERT_BAD_RECORD_MAC",
"PlatformException(already_active, File picker is already active",
// SVG-related errors
@@ -296,6 +297,10 @@ class ExceptionHandler {
"Wrong Device Status: 0x5515 (UNKNOWN)",
"Command handling failed. With error: hostUnreachable",
+ // Android IME/Gboard occasionally reports a caret offset past the end of
+ // the text on the platform text-input channel while typing. Non-fatal
+ // framework<->platform desync; the app keeps working.
+ "invalid selection start",
"FocusScopeNode was used after being disposed",
"_getDismissibleFlushbar",
"_QueuedFuture.execute (package:universal_ble/src/queue.dart:65)",
@@ -456,6 +461,19 @@ class ExceptionHandler {
}
}
+ if (errorDetails.exception.toString().contains("Cannot add event after closing")) {
+ // grpc-dart teardown race (e.g. the MWEB channel on litecoin): a buffered outgoing
+ // frame is delivered to the http2 stream's sink after the call/channel was
+ // terminated. The message alone is too generic to ignore, so require the exact
+ // shape of grpc's forwarding chain: .map().map().handleError().listen(sink.add).
+ final stack = errorDetails.stack.toString();
+ if (stack.contains("_StreamSinkWrapper.add") &&
+ stack.contains("_MapStream._handleData") &&
+ stack.contains("_HandleErrorStream._handleData")) {
+ return true;
+ }
+ }
+
return false;
}
}
Why this scored 27/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.