Pay Anything Flow Enhancements (#2564)

* fix: update Bitcoin and Litecoin Bech32 address patterns for improved detection for pay anything flow

* feat: add Litecoin MWEB address detection patterns for pay anything flow

* feat: Add support for ERC-681 payment scheme to QR and link to exchange send from external QR

* feat: Introduce TokenUtilities to centralize token management across viewmodels

- Added TokenUtilities class to centralize token-related operations for EVM, Solana, and Tron tokens.
- Refactored existing methods in ExchangeTradeViewModel and SendViewModel to utilize TokenUtilities.
- Updated PaymentRequest to include contractAddress.
- Enhanced payment QR scanning to fetch token details for QRs with contract addresses - ERC-681 QR scheme

* fix: Prefill amount with other data when app is brought up via payment deeplink on QR scan

* fix: Reduce trade not created errors WIP

* feat: Add exchange provider logging functionality

* feat: Enhance ERC681URI with amount normalization, prevents parsing issues with various input formats for amounts

* - Use regex from address validator for btc/ltc manual checks
- Add check for solana addresses
- Handle failures in normalizeToInteger method
- fix issue with address only being added when there's an amount

* feat: Trigger pay anything flow when address is pasted via ctrl v or paste input

* fix: remove duplicate registration

* fix: disable text selection in SendCard address field

* feat: Prevent swap option from showing for Mweb and Silent Payment addresses in Pay Anything flow

* refactor: remove notes widget and controllers from swap confirmation bottomsheet

* fix: Issue with tap send all button not responsive, and updateing fiat amount, after wallet syncing is complete if it was previously tapped while wallet was syncing

* fix: Resolve excessive decimal places error with input sanitization and formatting for amount entry

* fix: Add check for already usable amount format to prevent errors with Nano QR scanning

* fix: Improve fiat input handling by preventing unnecessary updates during user input

* fix: Handle potential null case for setReceiveAmountFromFiat

* Update lib/src/widgets/bottom_sheet/payment_confirmation_bottom_sheet.dart

---------

Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com>
This commit is contained in:
David Adegoke
2025-10-11 04:20:09 +03:00
committed by GitHub
co-authored by Omar Hatem
parent a56ae8e4ab
commit 025fc9154c
7 changed files with 179 additions and 55 deletions
+56 -13
View File
@@ -107,6 +107,8 @@ class SendCardState extends State<SendCard> with AutomaticKeepAliveClientMixin<S
bool _effectsInstalled = false;
BuildContext? loadingBottomSheetContext;
bool _justHandledPasteButton = false;
String _lastHandledAddress = '';
@override
void initState() {
@@ -385,22 +387,27 @@ class SendCardState extends State<SendCard> with AutomaticKeepAliveClientMixin<S
color: Theme.of(context).colorScheme.onSurfaceVariant,
),
onPushPasteButton: (context) async {
output.resetParsedAddress();
await output.fetchParsedAddress(context);
_justHandledPasteButton = true;
try {
output.resetParsedAddress();
await output.fetchParsedAddress(context);
final address =
output.isParsedAddress ? output.extractedAddress : output.address;
final address =
output.isParsedAddress ? output.extractedAddress : output.address;
await _handlePaymentFlow(
address,
PaymentRequest(
await _handlePaymentFlow(
address,
cryptoAmountController.text,
noteController.text,
"",
null,
),
);
PaymentRequest(
address,
cryptoAmountController.text,
noteController.text,
"",
null,
),
);
} finally {
_justHandledPasteButton = false;
}
},
onPushAddressBookButton: (context) async {
output.resetParsedAddress();
@@ -419,6 +426,7 @@ class SendCardState extends State<SendCard> with AutomaticKeepAliveClientMixin<S
fillColor: Theme.of(context).colorScheme.surfaceContainerHighest,
controller: extractedAddressController,
readOnly: true,
enableInteractiveSelection: false,
textStyle: Theme.of(context).textTheme.bodyMedium?.copyWith(
fontSize: 16,
fontWeight: FontWeight.w500,
@@ -768,7 +776,42 @@ class SendCardState extends State<SendCard> with AutomaticKeepAliveClientMixin<S
addressFocusNode.addListener(() async {
if (!addressFocusNode.hasFocus && addressController.text.isNotEmpty) {
final current = addressController.text.trim();
if (current.isEmpty) return;
if (_justHandledPasteButton || _lastHandledAddress == current) return;
await output.fetchParsedAddress(context);
// If it's a URI with params, go through URI flow
if (current.contains('=')) {
try {
final uri = Uri.parse(current);
_lastHandledAddress = current;
await _handlePaymentFlow(
uri.toString(),
PaymentRequest.fromUri(uri),
);
return;
} catch (_) {
// fall through to plain address
}
}
final parsedAddress = output.isParsedAddress
? output.extractedAddress
: output.address;
_lastHandledAddress = current;
await _handlePaymentFlow(
parsedAddress,
PaymentRequest(
parsedAddress,
cryptoAmountController.text,
noteController.text,
"",
null,
),
);
}
});
@@ -138,6 +138,12 @@ class OtherSettingsPage extends BasePage {
title: '[dev] *QR tools',
handler: (context) => Navigator.of(context).pushNamed(Routes.devQRTools),
),
if (FeatureFlag.hasDevOptions)
SettingsCellWithArrow(
title: '[dev] exchange provider logs',
handler: (BuildContext context) =>
Navigator.of(context).pushNamed(Routes.devExchangeProviderLogs),
),
Spacer(),
SettingsVersionCell(
title: S.of(context).version(_otherSettingsViewModel.currentVersion),