From 3a9e7d700b1405da77e032b65057077885efd23e Mon Sep 17 00:00:00 2001 From: sneurlax Date: Thu, 11 Jul 2024 18:56:54 -0500 Subject: [PATCH 1/5] filter unrecognized from extra URI parameters IAW BIP21 Closes #567 https://github.com/cypherstack/stack_wallet/issues/567 TODO refer to BIP21 to see if any other params are required --- lib/utilities/address_utils.dart | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/lib/utilities/address_utils.dart b/lib/utilities/address_utils.dart index 97f2ec965..788bde4d5 100644 --- a/lib/utilities/address_utils.dart +++ b/lib/utilities/address_utils.dart @@ -14,6 +14,8 @@ import 'logger.dart'; import '../wallets/crypto_currency/crypto_currency.dart'; class AddressUtils { + static final Set recognizedParams = {'amount', 'label', 'message'}; + static String condenseAddress(String address) { return '${address.substring(0, 5)}...${address.substring(address.length - 5)}'; } @@ -170,8 +172,13 @@ class AddressUtils { // // } // } - /// parse an address uri - /// returns an empty map if the input string does not begin with "firo:" + /// Return only recognized parameters. + static Map filterParams(Map params) { + return Map.fromEntries(params.entries + .where((entry) => recognizedParams.contains(entry.key.toLowerCase()))); + } + + /// Parses a URI string. static Map parseUri(String uri) { final Map result = {}; try { @@ -202,9 +209,11 @@ class AddressUtils { sanitizedAddress = address.replaceFirst(prefix, ""); } } + // Filter unrecognized parameters. + final filteredParams = filterParams(params); String uriString = "${coin.uriScheme}:$sanitizedAddress"; - if (params.isNotEmpty) { - uriString += Uri(queryParameters: params).toString(); + if (filteredParams.isNotEmpty) { + uriString += Uri(queryParameters: filteredParams).toString(); } return uriString; } From 21b7bfcb4e82c4b1867e1aea6dabf67279d5af3b Mon Sep 17 00:00:00 2001 From: sneurlax Date: Tue, 16 Jul 2024 10:46:22 -0500 Subject: [PATCH 2/5] check if pasted value is URI --- .../wallet_view/sub_widgets/desktop_send.dart | 62 +++++++++++++++---- 1 file changed, 51 insertions(+), 11 deletions(-) diff --git a/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_send.dart b/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_send.dart index e5cc93d92..08afb4a44 100644 --- a/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_send.dart +++ b/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_send.dart @@ -737,18 +737,58 @@ class _DesktopSendState extends ConsumerState { content = content.substring(0, content.indexOf("\n")); } - if (coin is Epiccash) { - // strip http:// and https:// if content contains @ - content = formatAddress(content); + // Check if the pasted value is a URI: + final results = AddressUtils.parseUri(content); + if (results.isNotEmpty) { + if (results["scheme"] == coin.uriScheme) { + // auto fill address + _address = results["address"] ?? ""; + if (coin is Bitcoincash || coin is Ecash) { + sendToController.text = results["scheme"] != null + ? results["scheme"]! + ":" + _address! + : _address!; + } else { + sendToController.text = _address!; + } + + // autofill notes field + if (results["message"] != null) { + _note = results["message"]!; + } else if (results["label"] != null) { + _note = results["label"]!; + } + + // autofill amount field + if (results["amount"] != null) { + final amount = Decimal.parse(results["amount"]!).toAmount( + fractionDigits: coin.fractionDigits, + ); + cryptoAmountController.text = ref + .read(pAmountFormatter(coin)) + .format(amount, withUnitName: false); + ref.read(pSendAmount.notifier).state = amount; + } + + setState(() { + _addressToggleFlag = sendToController.text.isNotEmpty; + }); + + return; + } + } else { + if (coin is Epiccash) { + // strip http:// and https:// if content contains @ + content = formatAddress(content); + } + + sendToController.text = content; + _address = content; + + _setValidAddressProviders(_address); + setState(() { + _addressToggleFlag = sendToController.text.isNotEmpty; + }); } - - sendToController.text = content; - _address = content; - - _setValidAddressProviders(_address); - setState(() { - _addressToggleFlag = sendToController.text.isNotEmpty; - }); } } From b73628d939429119b2f296e9456902a4db370e3d Mon Sep 17 00:00:00 2001 From: sneurlax Date: Tue, 27 Aug 2024 16:41:30 -0500 Subject: [PATCH 3/5] update uri parsing code to support monero URIs bitcoin URIs now also validate properly --- .../receive_view/addresses/address_card.dart | 2 +- .../addresses/address_details_view.dart | 4 +- .../addresses/address_qr_popup.dart | 2 +- .../generate_receiving_uri_qr_code_view.dart | 4 +- lib/pages/receive_view/receive_view.dart | 2 +- .../sub_widgets/desktop_receive.dart | 2 +- .../wallet_view/sub_widgets/desktop_send.dart | 3 + lib/utilities/address_utils.dart | 191 +++++++++++++++--- 8 files changed, 178 insertions(+), 32 deletions(-) diff --git a/lib/pages/receive_view/addresses/address_card.dart b/lib/pages/receive_view/addresses/address_card.dart index db14fd979..59074d979 100644 --- a/lib/pages/receive_view/addresses/address_card.dart +++ b/lib/pages/receive_view/addresses/address_card.dart @@ -304,7 +304,7 @@ class _AddressCardState extends ConsumerState { key: _qrKey, child: QR( data: AddressUtils.buildUriString( - widget.coin, + widget.coin.uriScheme, address.value, {}, ), diff --git a/lib/pages/receive_view/addresses/address_details_view.dart b/lib/pages/receive_view/addresses/address_details_view.dart index 2b0e09187..2006e900b 100644 --- a/lib/pages/receive_view/addresses/address_details_view.dart +++ b/lib/pages/receive_view/addresses/address_details_view.dart @@ -97,7 +97,7 @@ class _AddressDetailsViewState extends ConsumerState { key: _qrKey, child: QR( data: AddressUtils.buildUriString( - ref.watch(pWalletCoin(widget.walletId)), + ref.watch(pWalletCoin(widget.walletId)).uriScheme, address.value, {}, ), @@ -289,7 +289,7 @@ class _AddressDetailsViewState extends ConsumerState { key: _qrKey, child: QR( data: AddressUtils.buildUriString( - coin, + coin.uriScheme, address.value, {}, ), diff --git a/lib/pages/receive_view/addresses/address_qr_popup.dart b/lib/pages/receive_view/addresses/address_qr_popup.dart index 5a8bc1592..b4446f543 100644 --- a/lib/pages/receive_view/addresses/address_qr_popup.dart +++ b/lib/pages/receive_view/addresses/address_qr_popup.dart @@ -142,7 +142,7 @@ class _AddressQrPopupState extends State { key: _qrKey, child: QR( data: AddressUtils.buildUriString( - widget.coin, + widget.coin.uriScheme, widget.addressString, {}, ), diff --git a/lib/pages/receive_view/generate_receiving_uri_qr_code_view.dart b/lib/pages/receive_view/generate_receiving_uri_qr_code_view.dart index 7a7497d0e..f9049a538 100644 --- a/lib/pages/receive_view/generate_receiving_uri_qr_code_view.dart +++ b/lib/pages/receive_view/generate_receiving_uri_qr_code_view.dart @@ -170,7 +170,7 @@ class _GenerateUriQrCodeViewState extends State { } final uriString = AddressUtils.buildUriString( - widget.coin, + widget.coin.uriScheme, receivingAddress, queryParams, ); @@ -263,7 +263,7 @@ class _GenerateUriQrCodeViewState extends State { } _uriString = AddressUtils.buildUriString( - widget.coin, + widget.coin.uriScheme, receivingAddress, {}, ); diff --git a/lib/pages/receive_view/receive_view.dart b/lib/pages/receive_view/receive_view.dart index 2beaab5f2..a40262cdc 100644 --- a/lib/pages/receive_view/receive_view.dart +++ b/lib/pages/receive_view/receive_view.dart @@ -577,7 +577,7 @@ class _ReceiveViewState extends ConsumerState { children: [ QR( data: AddressUtils.buildUriString( - coin, + coin.uriScheme, address, {}, ), diff --git a/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_receive.dart b/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_receive.dart index a792641ba..a96a01b8f 100644 --- a/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_receive.dart +++ b/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_receive.dart @@ -448,7 +448,7 @@ class _DesktopReceiveState extends ConsumerState { Center( child: QR( data: AddressUtils.buildUriString( - coin, + coin.uriScheme, address, {}, ), diff --git a/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_send.dart b/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_send.dart index 9f6f545f6..c9c887fee 100644 --- a/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_send.dart +++ b/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_send.dart @@ -841,6 +841,8 @@ class _DesktopSendState extends ConsumerState { ref.read(pSendAmount.notifier).state = amount; } + // Trigger validation after pasting. + _setValidAddressProviders(_address); setState(() { _addressToggleFlag = sendToController.text.isNotEmpty; }); @@ -856,6 +858,7 @@ class _DesktopSendState extends ConsumerState { sendToController.text = content; _address = content; + // Trigger validation after pasting. _setValidAddressProviders(_address); setState(() { _addressToggleFlag = sendToController.text.isNotEmpty; diff --git a/lib/utilities/address_utils.dart b/lib/utilities/address_utils.dart index 788bde4d5..7b00c242a 100644 --- a/lib/utilities/address_utils.dart +++ b/lib/utilities/address_utils.dart @@ -10,11 +10,19 @@ import 'dart:convert'; -import 'logger.dart'; import '../wallets/crypto_currency/crypto_currency.dart'; class AddressUtils { - static final Set recognizedParams = {'amount', 'label', 'message'}; + static final Set recognizedParams = { + 'amount', + 'label', + 'message', + 'tx_amount', // For Monero/Wownero. + 'tx_payment_id', + 'recipient_name', + 'tx_description', + // TODO [prio=med]: Add more recognized params for other coins. + }; static String condenseAddress(String address) { return '${address.substring(0, 5)}...${address.substring(address.length - 5)}'; @@ -178,43 +186,144 @@ class AddressUtils { .where((entry) => recognizedParams.contains(entry.key.toLowerCase()))); } - /// Parses a URI string. + /// Parses a URI string and returns a map with parsed components. static Map parseUri(String uri) { final Map result = {}; try { final u = Uri.parse(uri); if (u.hasScheme) { result["scheme"] = u.scheme.toLowerCase(); - result["address"] = u.path; - result.addAll(u.queryParameters); + + // Handle different URI formats. + if (result["scheme"] == "bitcoin" || + result["scheme"] == "bitcoincash") { + result["address"] = u.path; + } else if (result["scheme"] == "monero") { + // Monero addresses can contain '?' which Uri.parse interprets as query start. + final addressEnd = + uri.indexOf('?', 7); // 7 is the length of "monero:". + if (addressEnd != -1) { + result["address"] = uri.substring(7, addressEnd); + } else { + result["address"] = uri.substring(7); + } + } else { + // Default case, treat path as address. + result["address"] = u.path; + } + + // Parse query parameters. + result.addAll(_parseQueryParameters(u.queryParameters)); + + // Handle Monero-specific fragment (tx_description). + if (u.fragment.isNotEmpty && result["scheme"] == "monero") { + result["tx_description"] = Uri.decodeComponent(u.fragment); + } } } catch (e) { - Logging.instance - .log("Exception caught in parseUri($uri): $e", level: LogLevel.Error); + print("Exception caught in parseUri($uri): $e"); } return result; } - /// builds a uri string with the given address and query parameters if any + /// Helper method to parse and normalize query parameters. + static Map _parseQueryParameters(Map params) { + final Map result = {}; + params.forEach((key, value) { + final lowerKey = key.toLowerCase(); + if (recognizedParams.contains(lowerKey)) { + switch (lowerKey) { + case 'amount': + case 'tx_amount': + result['amount'] = _normalizeAmount(value); + break; + case 'label': + case 'recipient_name': + result['label'] = Uri.decodeComponent(value); + break; + case 'message': + case 'tx_description': + result['message'] = Uri.decodeComponent(value); + break; + case 'tx_payment_id': + result['tx_payment_id'] = Uri.decodeComponent(value); + break; + default: + result[lowerKey] = Uri.decodeComponent(value); + } + } else { + // Include unrecognized parameters as-is. + result[key] = Uri.decodeComponent(value); + } + }); + return result; + } + + /// Normalizes amount value to a standard format. + static String _normalizeAmount(String amount) { + // Remove any non-numeric characters except for '.' + final sanitized = amount.replaceAll(RegExp(r'[^\d.]'), ''); + // Ensure only one decimal point + final parts = sanitized.split('.'); + if (parts.length > 2) { + return '${parts[0]}.${parts.sublist(1).join()}'; + } + return sanitized; + } + + /// Centralized method to handle various cryptocurrency URIs and return a common object. + static PaymentUriData parsePaymentUri(String uri) { + final Map parsedData = parseUri(uri); + + // Normalize the URI scheme. + final String scheme = parsedData['scheme'] ?? ''; + parsedData.remove('scheme'); + + // Determine the coin type based on the URI scheme. + final CryptoCurrency coin = _getCryptoCurrencyByScheme(scheme); + + // Filter out unrecognized parameters. + final filteredParams = filterParams(parsedData); + + return PaymentUriData( + coin: coin, + address: parsedData['address'] ?? '', + amount: filteredParams['amount'] ?? filteredParams['tx_amount'], + label: filteredParams['label'] ?? filteredParams['recipient_name'], + message: filteredParams['message'] ?? filteredParams['tx_description'], + paymentId: filteredParams['tx_payment_id'], // Specific to Monero + additionalParams: filteredParams, + ); + } + + /// Builds a uri string with the given address and query parameters (if any) static String buildUriString( - CryptoCurrency coin, + String scheme, String address, Map params, ) { - // TODO: other sanitation as well ? - String sanitizedAddress = address; - if (coin is Bitcoincash || coin is Ecash) { - final prefix = "${coin.uriScheme}:"; - if (address.startsWith(prefix)) { - sanitizedAddress = address.replaceFirst(prefix, ""); - } - } // Filter unrecognized parameters. final filteredParams = filterParams(params); - String uriString = "${coin.uriScheme}:$sanitizedAddress"; - if (filteredParams.isNotEmpty) { - uriString += Uri(queryParameters: filteredParams).toString(); + String uriString = "$scheme:$address"; + + if (scheme.toLowerCase() == "monero") { + // Handle Monero-specific formatting. + if (filteredParams.containsKey("tx_description")) { + final description = filteredParams.remove("tx_description")!; + if (filteredParams.isNotEmpty) { + uriString += Uri(queryParameters: filteredParams).toString(); + } + uriString += "#${Uri.encodeComponent(description)}"; + } else if (filteredParams.isNotEmpty) { + uriString += Uri(queryParameters: filteredParams).toString(); + } + } else { + // General case for other cryptocurrencies. + if (filteredParams.isNotEmpty) { + uriString += Uri(queryParameters: filteredParams).toString(); + } } + return uriString; } @@ -224,10 +333,7 @@ class AddressUtils { try { result = Map.from(jsonDecode(data) as Map); } catch (e) { - Logging.instance.log( - "Exception caught in parseQRSeedData($data): $e", - level: LogLevel.Error, - ); + print("Exception caught in parseQRSeedData($data): $e"); } return result; } @@ -236,4 +342,41 @@ class AddressUtils { static String encodeQRSeedData(List words) { return jsonEncode({"mnemonic": words}); } + + /// Method to get CryptoCurrency based on URI scheme. + static CryptoCurrency _getCryptoCurrencyByScheme(String scheme) { + switch (scheme) { + case 'bitcoin': + return Bitcoin(CryptoCurrencyNetwork.main); + case 'bitcoincash': + return Bitcoincash(CryptoCurrencyNetwork.main); + case 'ethereum': + return Ethereum(CryptoCurrencyNetwork.main); + case 'monero': + return Monero(CryptoCurrencyNetwork.main); + // Add more cases as needed for other coins + default: + throw UnsupportedError('Unsupported URI scheme: $scheme'); + } + } +} + +class PaymentUriData { + final CryptoCurrency coin; + final String address; + final String? amount; + final String? label; + final String? message; + final String? paymentId; // Specific to Monero. + final Map additionalParams; + + PaymentUriData({ + required this.coin, + required this.address, + this.amount, + this.label, + this.message, + this.paymentId, + required this.additionalParams, + }); } From 19cae77e2b8755e8d10a5a05bdce01db1e4157eb Mon Sep 17 00:00:00 2001 From: sneurlax Date: Tue, 27 Aug 2024 16:55:19 -0500 Subject: [PATCH 4/5] use AddressUtils where appropriate instead of duplicating logic --- lib/pages/send_view/send_view.dart | 44 ++++-------- .../wallet_view/sub_widgets/desktop_send.dart | 71 +++++++++++-------- lib/utilities/address_utils.dart | 20 ++++++ 3 files changed, 74 insertions(+), 61 deletions(-) diff --git a/lib/pages/send_view/send_view.dart b/lib/pages/send_view/send_view.dart index 0eb6f8881..004b02af1 100644 --- a/lib/pages/send_view/send_view.dart +++ b/lib/pages/send_view/send_view.dart @@ -163,25 +163,23 @@ class _SendViewState extends ConsumerState { level: LogLevel.Info, ); - final results = AddressUtils.parseUri(qrResult.rawContent); + final paymentData = AddressUtils.parsePaymentUri(qrResult.rawContent); - Logging.instance.log("qrResult parsed: $results", level: LogLevel.Info); - - if (results.isNotEmpty && results["scheme"] == coin.uriScheme) { + if (paymentData.coin.uriScheme == coin.uriScheme) { // auto fill address - _address = (results["address"] ?? "").trim(); + _address = paymentData.address.trim(); sendToController.text = _address!; // autofill notes field - if (results["message"] != null) { - noteController.text = results["message"]!; - } else if (results["label"] != null) { - noteController.text = results["label"]!; + if (paymentData.message != null) { + noteController.text = paymentData.message!; + } else if (paymentData.label != null) { + noteController.text = paymentData.label!; } // autofill amount field - if (results["amount"] != null) { - final Amount amount = Decimal.parse(results["amount"]!).toAmount( + if (paymentData.amount != null) { + final Amount amount = Decimal.parse(paymentData.amount!).toAmount( fractionDigits: coin.fractionDigits, ); cryptoAmountController.text = ref.read(pAmountFormatter(coin)).format( @@ -1071,7 +1069,7 @@ class _SendViewState extends ConsumerState { _address = _address!.substring(0, _address!.indexOf("\n")); } - sendToController.text = formatAddress(_address!); + sendToController.text = AddressUtils().formatAddress(_address!); } }); } @@ -1402,7 +1400,8 @@ class _SendViewState extends ConsumerState { if (coin is Epiccash) { // strip http:// and https:// if content contains @ - content = formatAddress( + content = AddressUtils() + .formatAddress( content, ); } @@ -2421,22 +2420,3 @@ class _SendViewState extends ConsumerState { ); } } - -String formatAddress(String epicAddress) { - // strip http:// or https:// prefixes if the address contains an @ symbol (and is thus an epicbox address) - if ((epicAddress.startsWith("http://") || - epicAddress.startsWith("https://")) && - epicAddress.contains("@")) { - epicAddress = epicAddress.replaceAll("http://", ""); - epicAddress = epicAddress.replaceAll("https://", ""); - } - // strip mailto: prefix - if (epicAddress.startsWith("mailto:")) { - epicAddress = epicAddress.replaceAll("mailto:", ""); - } - // strip / suffix if the address contains an @ symbol (and is thus an epicbox address) - if (epicAddress.endsWith("/") && epicAddress.contains("@")) { - epicAddress = epicAddress.substring(0, epicAddress.length - 1); - } - return epicAddress; -} diff --git a/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_send.dart b/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_send.dart index c9c887fee..8d87eb35e 100644 --- a/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_send.dart +++ b/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_send.dart @@ -743,13 +743,15 @@ class _DesktopSendState extends ConsumerState { void _processQrCodeData(String qrCodeData) { try { - var results = AddressUtils.parseUri(qrCodeData); - if (results.isNotEmpty && results["scheme"] == coin.uriScheme) { - _address = (results["address"] ?? "").trim(); + final paymentData = AddressUtils.parsePaymentUri(qrCodeData); + if (paymentData.coin.uriScheme == coin.uriScheme) { + // Auto fill address. + _address = paymentData.address.trim(); sendToController.text = _address!; - if (results["amount"] != null) { - final Amount amount = Decimal.parse(results["amount"]!).toAmount( + // Amount. + if (paymentData.amount != null) { + final Amount amount = Decimal.parse(paymentData.amount!).toAmount( fractionDigits: coin.fractionDigits, ); cryptoAmountController.text = ref.read(pAmountFormatter(coin)).format( @@ -759,6 +761,13 @@ class _DesktopSendState extends ConsumerState { ref.read(pSendAmount.notifier).state = amount; } + // Note/message. + if (paymentData.message != null) { + _note = paymentData.message; + } else if (paymentData.label != null) { + _note = paymentData.label; + } + _setValidAddressProviders(_address); setState(() { _addressToggleFlag = sendToController.text.isNotEmpty; @@ -809,30 +818,23 @@ class _DesktopSendState extends ConsumerState { content = content.substring(0, content.indexOf("\n")); } - // Check if the pasted value is a URI: - final results = AddressUtils.parseUri(content); - if (results.isNotEmpty) { - if (results["scheme"] == coin.uriScheme) { + try { + final paymentData = AddressUtils.parsePaymentUri(content); + if (paymentData.coin.uriScheme == coin.uriScheme) { // auto fill address - _address = results["address"] ?? ""; - if (coin is Bitcoincash || coin is Ecash) { - sendToController.text = results["scheme"] != null - ? results["scheme"]! + ":" + _address! - : _address!; - } else { - sendToController.text = _address!; + _address = paymentData.address; + sendToController.text = _address!; + + // autofill notes field. + if (paymentData.message != null) { + _note = paymentData.message; + } else if (paymentData.label != null) { + _note = paymentData.label; } - // autofill notes field - if (results["message"] != null) { - _note = results["message"]!; - } else if (results["label"] != null) { - _note = results["label"]!; - } - - // autofill amount field - if (results["amount"] != null) { - final amount = Decimal.parse(results["amount"]!).toAmount( + // autofill amoutn field + if (paymentData.amount != null) { + final amount = Decimal.parse(paymentData.amount!).toAmount( fractionDigits: coin.fractionDigits, ); cryptoAmountController.text = ref @@ -846,13 +848,24 @@ class _DesktopSendState extends ConsumerState { setState(() { _addressToggleFlag = sendToController.text.isNotEmpty; }); + } else { + if (coin is Epiccash) { + content = AddressUtils().formatAddress(content); + } - return; + sendToController.text = content; + _address = content; + + _setValidAddressProviders(_address); + setState(() { + _addressToggleFlag = sendToController.text.isNotEmpty; + }); } - } else { + } catch (e) { + // If parsing fails, treat it as a plain address. if (coin is Epiccash) { // strip http:// and https:// if content contains @ - content = formatAddress(content); + content = AddressUtils().formatAddress(content); } sendToController.text = content; diff --git a/lib/utilities/address_utils.dart b/lib/utilities/address_utils.dart index 7b00c242a..85ec784a6 100644 --- a/lib/utilities/address_utils.dart +++ b/lib/utilities/address_utils.dart @@ -359,6 +359,26 @@ class AddressUtils { throw UnsupportedError('Unsupported URI scheme: $scheme'); } } + + /// Formats an address string to remove any unnecessary prefixes or suffixes. + String formatAddress(String epicAddress) { + // strip http:// or https:// prefixes if the address contains an @ symbol (and is thus an epicbox address) + if ((epicAddress.startsWith("http://") || + epicAddress.startsWith("https://")) && + epicAddress.contains("@")) { + epicAddress = epicAddress.replaceAll("http://", ""); + epicAddress = epicAddress.replaceAll("https://", ""); + } + // strip mailto: prefix + if (epicAddress.startsWith("mailto:")) { + epicAddress = epicAddress.replaceAll("mailto:", ""); + } + // strip / suffix if the address contains an @ symbol (and is thus an epicbox address) + if (epicAddress.endsWith("/") && epicAddress.contains("@")) { + epicAddress = epicAddress.substring(0, epicAddress.length - 1); + } + return epicAddress; + } } class PaymentUriData { From db5fcde918edfa02af36323da5f69e01b6ad2354 Mon Sep 17 00:00:00 2001 From: sneurlax Date: Tue, 27 Aug 2024 17:13:02 -0500 Subject: [PATCH 5/5] fix _getCryptoCurrencyByScheme --- lib/utilities/address_utils.dart | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/lib/utilities/address_utils.dart b/lib/utilities/address_utils.dart index 85ec784a6..869aa5895 100644 --- a/lib/utilities/address_utils.dart +++ b/lib/utilities/address_utils.dart @@ -10,6 +10,7 @@ import 'dart:convert'; +import '../app_config.dart'; import '../wallets/crypto_currency/crypto_currency.dart'; class AddressUtils { @@ -345,18 +346,10 @@ class AddressUtils { /// Method to get CryptoCurrency based on URI scheme. static CryptoCurrency _getCryptoCurrencyByScheme(String scheme) { - switch (scheme) { - case 'bitcoin': - return Bitcoin(CryptoCurrencyNetwork.main); - case 'bitcoincash': - return Bitcoincash(CryptoCurrencyNetwork.main); - case 'ethereum': - return Ethereum(CryptoCurrencyNetwork.main); - case 'monero': - return Monero(CryptoCurrencyNetwork.main); - // Add more cases as needed for other coins - default: - throw UnsupportedError('Unsupported URI scheme: $scheme'); + if (AppConfig.coins.map((e) => e.uriScheme).toSet().contains(scheme)) { + return AppConfig.coins.firstWhere((e) => e.uriScheme == scheme); + } else { + throw UnsupportedError('Unsupported URI scheme: $scheme'); } }