From 3b44149fb22cc72603e4bd8f60321d0843f86cab Mon Sep 17 00:00:00 2001 From: Francois Date: Mon, 11 Mar 2024 07:53:05 +0200 Subject: [PATCH 1/2] Remove prefix from QR code scans --- .../amount_address_step.dart | 2 +- lib/utils/utils.dart | 21 +++++++++++ test/utils/utils_test.dart | 36 +++++++++++++++++++ 3 files changed, 58 insertions(+), 1 deletion(-) create mode 100644 test/utils/utils_test.dart diff --git a/lib/screens/portfolio/coin_detail/steps_withdraw.dart/amount_address_step/amount_address_step.dart b/lib/screens/portfolio/coin_detail/steps_withdraw.dart/amount_address_step/amount_address_step.dart index 445d9f38e..74ebc548a 100644 --- a/lib/screens/portfolio/coin_detail/steps_withdraw.dart/amount_address_step/amount_address_step.dart +++ b/lib/screens/portfolio/coin_detail/steps_withdraw.dart/amount_address_step/amount_address_step.dart @@ -301,7 +301,7 @@ class _AmountAddressStepState extends State { barcode = 'Error'; }); } else { - final address = result; + final address = removeBlockchainPrefix(result); final uri = Uri.tryParse(address.trim()); setState(() { diff --git a/lib/utils/utils.dart b/lib/utils/utils.dart index bafbb18d5..d52a096f0 100644 --- a/lib/utils/utils.dart +++ b/lib/utils/utils.dart @@ -865,6 +865,27 @@ Future scanQr(BuildContext context) async { ); } +/// Removes the blockchain prefix from [address] +/// +/// Returns [address] if either the prefix or address are empty. +/// +/// Example usage: +/// ```dart +/// removeBlockchainPrefix(eth:0x123) == '0x123' +/// ``` +String removeBlockchainPrefix(String address) { + if (address.contains(':')) { + final List parts = address.split(':'); + final bool hasPrefix = parts.length == 2 && parts[0].isNotEmpty; + final bool hasAddress = parts.length == 2 && parts[1].isNotEmpty; + if (hasPrefix && hasAddress) { + return parts[1]; + } + } + + return address; +} + /// Function to generate password based on some criteria /// /// Adapted from code at https://blog.albertobonacina.com/password-generator-with-dart. diff --git a/test/utils/utils_test.dart b/test/utils/utils_test.dart new file mode 100644 index 000000000..5fb45f94b --- /dev/null +++ b/test/utils/utils_test.dart @@ -0,0 +1,36 @@ +import 'package:flutter_test/flutter_test.dart'; +import 'package:komodo_dex/utils/utils.dart'; + +void main() { + group('removeBlockchainPrefix', () { + test('removes blockchain prefix from address', () { + const String address = 'eth:0x1234567890abcdef'; + final String result = removeBlockchainPrefix(address); + expect(result, '0x1234567890abcdef'); + }); + + test('removes blackcoin prefix from address', () { + const String address = 'blackcoin:B8Q9aZ4Mz1ZwWYaknJdZ8t3Z6z9F3Fz1Zz'; + final String result = removeBlockchainPrefix(address); + expect(result, 'B8Q9aZ4Mz1ZwWYaknJdZ8t3Z6z9F3Fz1Zz'); + }); + + test('returns the same address if no prefix is present', () { + const String address = '0x1234567890abcdef'; + final String result = removeBlockchainPrefix(address); + expect(result, '0x1234567890abcdef'); + }); + + test('returns the address after the prefix', () { + const String address = ':0x1234567890abcdef'; + final String result = removeBlockchainPrefix(address); + expect(result, ':0x1234567890abcdef'); + }); + + test('returns the same address if no prefix is present', () { + const String address = '0x1234567890abcdef:'; + final String result = removeBlockchainPrefix(address); + expect(result, '0x1234567890abcdef:'); + }); + }); +} From 101ce9436ae31e5310153d75a897be03a4ee6bf6 Mon Sep 17 00:00:00 2001 From: Francois Date: Sun, 17 Mar 2024 14:26:29 +0200 Subject: [PATCH 2/2] Remove query parameters from address and parse amount Did not modify PaymentUriInfo to avoid issues with segwit variants --- .../amount_address_step.dart | 10 +++- lib/utils/utils.dart | 50 +++++++++++-------- test/utils/utils_test.dart | 36 +++++++++---- 3 files changed, 62 insertions(+), 34 deletions(-) diff --git a/lib/screens/portfolio/coin_detail/steps_withdraw.dart/amount_address_step/amount_address_step.dart b/lib/screens/portfolio/coin_detail/steps_withdraw.dart/amount_address_step/amount_address_step.dart index 74ebc548a..91a843274 100644 --- a/lib/screens/portfolio/coin_detail/steps_withdraw.dart/amount_address_step/amount_address_step.dart +++ b/lib/screens/portfolio/coin_detail/steps_withdraw.dart/amount_address_step/amount_address_step.dart @@ -188,6 +188,10 @@ class _AmountAddressStepState extends State { widget.addressController.text = address; } + set qrAmount(String amount) { + widget.amountController.text = amount; + } + void showWrongCoinDialog(PaymentUriInfo uriInfo) { dialogBloc.dialog = showDialog( context: context, @@ -301,8 +305,9 @@ class _AmountAddressStepState extends State { barcode = 'Error'; }); } else { - final address = removeBlockchainPrefix(result); - final uri = Uri.tryParse(address.trim()); + final String address = getAddressFromUri(result.trim()); + final String amount = getParameterValue(result.trim(), 'amount'); + final Uri uri = Uri.tryParse(result.trim()); setState(() { final PaymentUriInfo uriInfo = PaymentUriInfo.fromUri(uri); @@ -310,6 +315,7 @@ class _AmountAddressStepState extends State { handlePaymentData(uriInfo); } else { handleQrAdress(address); + qrAmount = amount ?? ''; } }); } diff --git a/lib/utils/utils.dart b/lib/utils/utils.dart index d52a096f0..4fbe145c8 100644 --- a/lib/utils/utils.dart +++ b/lib/utils/utils.dart @@ -711,6 +711,35 @@ class PaymentUriInfo { final String amount; } +/// Returns the value of a [parameter] from the [address]. +/// Returns null if the [parameter] is not found. +/// [address] is expected to be a blockchain address or URL with parameters, +/// e.g. '?amount=123' +/// +/// Example usage: +/// ```dart +/// getParameterValue('litecoin:NPZcaiggQTy6uxL?amount=0.2', 'amount') == '0.2' +/// ``` +String getParameterValue(String address, String parameter) { + final RegExp regExp = RegExp('(?<=$parameter=)(.*?)(?=&|\$)'); + final RegExpMatch match = regExp.firstMatch(address); + return match?.group(0); +} + +/// Returns the address from the [uri]. +/// Returns the [uri] if no address is found. +/// [uri] is expected to be a blockchain address or URL with parameters, +/// e.g. '?amount=123' +/// Example usage: +/// ```dart +/// getAddressFromUri('litecoin:NPZcaiggQTy6uxL?amount=0.2') == 'NPZcaiggQTy6uxL' +/// ``` +String getAddressFromUri(String uri) { + final RegExp regExp = RegExp(r'(?<=:)(.*?)(?=\?|$)'); + final RegExpMatch match = regExp.firstMatch(uri); + return match?.group(0) ?? uri.split(':').last; +} + void showUriDetailsDialog( BuildContext context, PaymentUriInfo uriInfo, Function callbackIfAccepted) { if (uriInfo == null) return; @@ -865,27 +894,6 @@ Future scanQr(BuildContext context) async { ); } -/// Removes the blockchain prefix from [address] -/// -/// Returns [address] if either the prefix or address are empty. -/// -/// Example usage: -/// ```dart -/// removeBlockchainPrefix(eth:0x123) == '0x123' -/// ``` -String removeBlockchainPrefix(String address) { - if (address.contains(':')) { - final List parts = address.split(':'); - final bool hasPrefix = parts.length == 2 && parts[0].isNotEmpty; - final bool hasAddress = parts.length == 2 && parts[1].isNotEmpty; - if (hasPrefix && hasAddress) { - return parts[1]; - } - } - - return address; -} - /// Function to generate password based on some criteria /// /// Adapted from code at https://blog.albertobonacina.com/password-generator-with-dart. diff --git a/test/utils/utils_test.dart b/test/utils/utils_test.dart index 5fb45f94b..796a786d8 100644 --- a/test/utils/utils_test.dart +++ b/test/utils/utils_test.dart @@ -1,36 +1,50 @@ import 'package:flutter_test/flutter_test.dart'; +import 'package:komodo_dex/model/coin.dart'; import 'package:komodo_dex/utils/utils.dart'; void main() { group('removeBlockchainPrefix', () { test('removes blockchain prefix from address', () { const String address = 'eth:0x1234567890abcdef'; - final String result = removeBlockchainPrefix(address); + final String result = getAddressFromUri(address); expect(result, '0x1234567890abcdef'); }); test('removes blackcoin prefix from address', () { const String address = 'blackcoin:B8Q9aZ4Mz1ZwWYaknJdZ8t3Z6z9F3Fz1Zz'; - final String result = removeBlockchainPrefix(address); + final String result = getAddressFromUri(address); expect(result, 'B8Q9aZ4Mz1ZwWYaknJdZ8t3Z6z9F3Fz1Zz'); }); test('returns the same address if no prefix is present', () { const String address = '0x1234567890abcdef'; - final String result = removeBlockchainPrefix(address); + final String result = getAddressFromUri(address); expect(result, '0x1234567890abcdef'); }); + }); - test('returns the address after the prefix', () { - const String address = ':0x1234567890abcdef'; - final String result = removeBlockchainPrefix(address); - expect(result, ':0x1234567890abcdef'); + group('getParameterValue', () { + test('returns the parameter value from the address', () { + const String address = + 'eth:0x1234567890abcdef?param1=value1¶m2=value2'; + const String parameter = 'param1'; + final String result = getParameterValue(address, parameter); + expect(result, 'value1'); }); - test('returns the same address if no prefix is present', () { - const String address = '0x1234567890abcdef:'; - final String result = removeBlockchainPrefix(address); - expect(result, '0x1234567890abcdef:'); + test('returns null if the parameter is not present in the address', () { + const String address = + 'eth:0x1234567890abcdef?param1=value1¶m2=value2'; + const String parameter = 'param3'; + final String result = getParameterValue(address, parameter); + expect(result, isNull); + }); + + test('returns null if the address does not contain any parameters', () { + const String address = 'eth:0x1234567890abcdef'; + const String parameter = 'param1'; + final String result = getParameterValue(address, parameter); + expect(result, isNull); }); }); }