From f7fafde74a03c4d5ca804d78dcc1bb87a3466682 Mon Sep 17 00:00:00 2001 From: ruben beck Date: Wed, 18 Oct 2023 11:30:24 +0200 Subject: [PATCH 1/3] add blockexplorer to ServiceInjector --- .../closed_channel_payment_details.dart | 16 +++++----- .../subswap/swap/widgets/inprogress_swap.dart | 29 +++++++++++++------ .../in_progress/reverse_swap_in_progress.dart | 7 ++--- lib/services/injector.dart | 11 +++++++ 4 files changed, 42 insertions(+), 21 deletions(-) diff --git a/lib/routes/home/widgets/payments_list/dialog/closed_channel_payment_details.dart b/lib/routes/home/widgets/payments_list/dialog/closed_channel_payment_details.dart index 7c9d3bd60..4cd5545ea 100644 --- a/lib/routes/home/widgets/payments_list/dialog/closed_channel_payment_details.dart +++ b/lib/routes/home/widgets/payments_list/dialog/closed_channel_payment_details.dart @@ -1,8 +1,8 @@ import 'package:breez_sdk/bridge_generated.dart' as sdk; import 'package:breez_translations/breez_translations_locales.dart'; -import 'package:c_breez/config.dart'; import 'package:c_breez/models/payment_minutiae.dart'; import 'package:c_breez/routes/home/widgets/payments_list/dialog/tx_widget.dart'; +import 'package:c_breez/services/injector.dart'; import 'package:c_breez/widgets/loader.dart'; import 'package:flutter/material.dart'; @@ -18,11 +18,11 @@ class ClosedChannelPaymentDetailsWidget extends StatelessWidget { Widget build(BuildContext context) { final themeData = Theme.of(context); final texts = context.texts(); - return FutureBuilder( - future: Config.instance(), - builder: (BuildContext context, AsyncSnapshot snapshot) { + return FutureBuilder( + future: ServiceInjector().blockexplorer, + builder: (BuildContext context, AsyncSnapshot snapshot) { if (snapshot.hasData) { - final blockExplorer = snapshot.data!.defaultMempoolUrl; + final blockexplorer = snapshot.data!; if (paymentMinutiae.status == sdk.PaymentStatus.Complete) { return Column( mainAxisSize: MainAxisSize.min, @@ -36,7 +36,7 @@ class ClosedChannelPaymentDetailsWidget extends StatelessWidget { if (paymentMinutiae.paymentType == sdk.PaymentType.ClosedChannel && paymentMinutiae.closingTxid != null) ...[ TxWidget( - txURL: "$blockExplorer/tx/${paymentMinutiae.closingTxid!}", + txURL: "$blockexplorer/tx/${paymentMinutiae.closingTxid!}", txID: paymentMinutiae.closingTxid!, ), ], @@ -58,13 +58,13 @@ class ClosedChannelPaymentDetailsWidget extends StatelessWidget { ), if (paymentMinutiae.fundingTxid != null) ...[ TxWidget( - txURL: "$blockExplorer/tx/${paymentMinutiae.fundingTxid!}", + txURL: "$blockexplorer/tx/${paymentMinutiae.fundingTxid!}", txID: paymentMinutiae.fundingTxid!, ), ], if (paymentMinutiae.closingTxid != null) ...[ TxWidget( - txURL: "$blockExplorer/tx/${paymentMinutiae.closingTxid!}", + txURL: "$blockexplorer/tx/${paymentMinutiae.closingTxid!}", txID: paymentMinutiae.closingTxid!, ), ] diff --git a/lib/routes/subswap/swap/widgets/inprogress_swap.dart b/lib/routes/subswap/swap/widgets/inprogress_swap.dart index 8d7debd01..36e93a7be 100644 --- a/lib/routes/subswap/swap/widgets/inprogress_swap.dart +++ b/lib/routes/subswap/swap/widgets/inprogress_swap.dart @@ -3,6 +3,7 @@ import 'package:breez_translations/breez_translations_locales.dart'; import 'package:c_breez/services/injector.dart'; import 'package:c_breez/widgets/flushbar.dart'; import 'package:c_breez/widgets/link_launcher.dart'; +import 'package:c_breez/widgets/loader.dart'; import 'package:flutter/material.dart'; class SwapInprogress extends StatelessWidget { @@ -49,15 +50,25 @@ class _TxLink extends StatelessWidget { Widget build(BuildContext context) { final text = context.texts(); - return LinkLauncher( - linkName: txid, - linkAddress: "https://blockstream.info/tx/$txid", - onCopy: () { - ServiceInjector().device.setClipboardText(txid); - showFlushbar( - context, - message: text.add_funds_transaction_id_copied, - duration: const Duration(seconds: 3), + return FutureBuilder( + future: ServiceInjector().blockexplorer, + builder: (context, snapshot) { + if (snapshot.connectionState != ConnectionState.done) { + return const Loader(); + } + final blockexplorer = snapshot.data!; + + return LinkLauncher( + linkName: txid, + linkAddress: "$blockexplorer/tx/$txid", + onCopy: () { + ServiceInjector().device.setClipboardText(txid); + showFlushbar( + context, + message: text.add_funds_transaction_id_copied, + duration: const Duration(seconds: 3), + ); + }, ); }, ); diff --git a/lib/routes/withdraw/reverse_swap/in_progress/reverse_swap_in_progress.dart b/lib/routes/withdraw/reverse_swap/in_progress/reverse_swap_in_progress.dart index 3f5f9e3a6..4836b97ab 100644 --- a/lib/routes/withdraw/reverse_swap/in_progress/reverse_swap_in_progress.dart +++ b/lib/routes/withdraw/reverse_swap/in_progress/reverse_swap_in_progress.dart @@ -1,6 +1,5 @@ import 'package:breez_sdk/bridge_generated.dart' as sdk; import 'package:breez_translations/breez_translations_locales.dart'; -import 'package:c_breez/config.dart'; import 'package:c_breez/services/injector.dart'; import 'package:c_breez/widgets/flushbar.dart'; import 'package:c_breez/widgets/link_launcher.dart'; @@ -48,17 +47,17 @@ class _TxLink extends StatelessWidget { final text = context.texts(); return FutureBuilder( - future: Config.instance(), + future: ServiceInjector().blockexplorer, builder: (context, snapshot) { if (snapshot.connectionState != ConnectionState.done) { return const Loader(); } - final blockExplorer = snapshot.data!.defaultMempoolUrl; + final blockexplorer = snapshot.data!; return LinkLauncher( linkName: txid, - linkAddress: "$blockExplorer/tx/$txid", + linkAddress: "$blockexplorer/tx/$txid", onCopy: () { ServiceInjector().device.setClipboardText(txid); showFlushbar( diff --git a/lib/services/injector.dart b/lib/services/injector.dart index 079d306c2..74890f97f 100644 --- a/lib/services/injector.dart +++ b/lib/services/injector.dart @@ -1,6 +1,7 @@ import 'dart:async'; import 'package:breez_sdk/breez_sdk.dart'; +import 'package:c_breez/config.dart'; import 'package:c_breez/services/breez_server.dart'; import 'package:c_breez/services/deep_links.dart'; import 'package:c_breez/services/device.dart'; @@ -77,4 +78,14 @@ class ServiceInjector { Preferences get preferences { return _preferences ??= Preferences(); } + + Future get blockexplorer async { + String? blockexplorer = await _preferences?.getMempoolSpaceUrl(); + + if (blockexplorer == null) { + final config = await Config.instance(); + blockexplorer = config.defaultMempoolUrl; + } + return blockexplorer; + } } From 4933430da944e85b6aabfdcf9377c795881e8f38 Mon Sep 17 00:00:00 2001 From: ruben beck Date: Wed, 18 Oct 2023 14:09:55 +0200 Subject: [PATCH 2/3] create mempool helper --- .../dialog/closed_channel_payment_details.dart | 14 +++++++++----- .../subswap/swap/widgets/inprogress_swap.dart | 7 ++++--- .../in_progress/reverse_swap_in_progress.dart | 8 +++++--- lib/services/injector.dart | 11 ----------- lib/utils/mempool_helper.dart | 17 +++++++++++++++++ 5 files changed, 35 insertions(+), 22 deletions(-) create mode 100644 lib/utils/mempool_helper.dart diff --git a/lib/routes/home/widgets/payments_list/dialog/closed_channel_payment_details.dart b/lib/routes/home/widgets/payments_list/dialog/closed_channel_payment_details.dart index 4cd5545ea..664067b24 100644 --- a/lib/routes/home/widgets/payments_list/dialog/closed_channel_payment_details.dart +++ b/lib/routes/home/widgets/payments_list/dialog/closed_channel_payment_details.dart @@ -2,7 +2,7 @@ import 'package:breez_sdk/bridge_generated.dart' as sdk; import 'package:breez_translations/breez_translations_locales.dart'; import 'package:c_breez/models/payment_minutiae.dart'; import 'package:c_breez/routes/home/widgets/payments_list/dialog/tx_widget.dart'; -import 'package:c_breez/services/injector.dart'; +import 'package:c_breez/utils/mempool_helper.dart'; import 'package:c_breez/widgets/loader.dart'; import 'package:flutter/material.dart'; @@ -18,8 +18,9 @@ class ClosedChannelPaymentDetailsWidget extends StatelessWidget { Widget build(BuildContext context) { final themeData = Theme.of(context); final texts = context.texts(); + final mempoolHelper = MempoolHelper(); return FutureBuilder( - future: ServiceInjector().blockexplorer, + future: mempoolHelper.blockexplorer, builder: (BuildContext context, AsyncSnapshot snapshot) { if (snapshot.hasData) { final blockexplorer = snapshot.data!; @@ -36,7 +37,8 @@ class ClosedChannelPaymentDetailsWidget extends StatelessWidget { if (paymentMinutiae.paymentType == sdk.PaymentType.ClosedChannel && paymentMinutiae.closingTxid != null) ...[ TxWidget( - txURL: "$blockexplorer/tx/${paymentMinutiae.closingTxid!}", + txURL: mempoolHelper.formatTransactionUrl( + txid: paymentMinutiae.closingTxid!, blockexplorer: blockexplorer), txID: paymentMinutiae.closingTxid!, ), ], @@ -58,13 +60,15 @@ class ClosedChannelPaymentDetailsWidget extends StatelessWidget { ), if (paymentMinutiae.fundingTxid != null) ...[ TxWidget( - txURL: "$blockexplorer/tx/${paymentMinutiae.fundingTxid!}", + txURL: mempoolHelper.formatTransactionUrl( + txid: paymentMinutiae.fundingTxid!, blockexplorer: blockexplorer), txID: paymentMinutiae.fundingTxid!, ), ], if (paymentMinutiae.closingTxid != null) ...[ TxWidget( - txURL: "$blockexplorer/tx/${paymentMinutiae.closingTxid!}", + txURL: mempoolHelper.formatTransactionUrl( + txid: paymentMinutiae.closingTxid!, blockexplorer: blockexplorer), txID: paymentMinutiae.closingTxid!, ), ] diff --git a/lib/routes/subswap/swap/widgets/inprogress_swap.dart b/lib/routes/subswap/swap/widgets/inprogress_swap.dart index 36e93a7be..d8180de47 100644 --- a/lib/routes/subswap/swap/widgets/inprogress_swap.dart +++ b/lib/routes/subswap/swap/widgets/inprogress_swap.dart @@ -1,6 +1,7 @@ import 'package:breez_sdk/bridge_generated.dart'; import 'package:breez_translations/breez_translations_locales.dart'; import 'package:c_breez/services/injector.dart'; +import 'package:c_breez/utils/mempool_helper.dart'; import 'package:c_breez/widgets/flushbar.dart'; import 'package:c_breez/widgets/link_launcher.dart'; import 'package:c_breez/widgets/loader.dart'; @@ -49,9 +50,9 @@ class _TxLink extends StatelessWidget { @override Widget build(BuildContext context) { final text = context.texts(); - + final mempoolExplorer = MempoolHelper(); return FutureBuilder( - future: ServiceInjector().blockexplorer, + future: mempoolExplorer.blockexplorer, builder: (context, snapshot) { if (snapshot.connectionState != ConnectionState.done) { return const Loader(); @@ -60,7 +61,7 @@ class _TxLink extends StatelessWidget { return LinkLauncher( linkName: txid, - linkAddress: "$blockexplorer/tx/$txid", + linkAddress: mempoolExplorer.formatTransactionUrl(txid: txid, blockexplorer: blockexplorer), onCopy: () { ServiceInjector().device.setClipboardText(txid); showFlushbar( diff --git a/lib/routes/withdraw/reverse_swap/in_progress/reverse_swap_in_progress.dart b/lib/routes/withdraw/reverse_swap/in_progress/reverse_swap_in_progress.dart index 4836b97ab..e6e858fb3 100644 --- a/lib/routes/withdraw/reverse_swap/in_progress/reverse_swap_in_progress.dart +++ b/lib/routes/withdraw/reverse_swap/in_progress/reverse_swap_in_progress.dart @@ -1,6 +1,7 @@ import 'package:breez_sdk/bridge_generated.dart' as sdk; import 'package:breez_translations/breez_translations_locales.dart'; import 'package:c_breez/services/injector.dart'; +import 'package:c_breez/utils/mempool_helper.dart'; import 'package:c_breez/widgets/flushbar.dart'; import 'package:c_breez/widgets/link_launcher.dart'; import 'package:c_breez/widgets/loader.dart'; @@ -45,19 +46,20 @@ class _TxLink extends StatelessWidget { @override Widget build(BuildContext context) { final text = context.texts(); + final mempoolHelper = MempoolHelper(); return FutureBuilder( - future: ServiceInjector().blockexplorer, + future: mempoolHelper.blockexplorer, builder: (context, snapshot) { if (snapshot.connectionState != ConnectionState.done) { return const Loader(); } - final blockexplorer = snapshot.data!; + final transactionUrl = mempoolHelper.formatTransactionUrl(blockexplorer: snapshot.data!, txid: txid); return LinkLauncher( linkName: txid, - linkAddress: "$blockexplorer/tx/$txid", + linkAddress: transactionUrl, onCopy: () { ServiceInjector().device.setClipboardText(txid); showFlushbar( diff --git a/lib/services/injector.dart b/lib/services/injector.dart index 74890f97f..079d306c2 100644 --- a/lib/services/injector.dart +++ b/lib/services/injector.dart @@ -1,7 +1,6 @@ import 'dart:async'; import 'package:breez_sdk/breez_sdk.dart'; -import 'package:c_breez/config.dart'; import 'package:c_breez/services/breez_server.dart'; import 'package:c_breez/services/deep_links.dart'; import 'package:c_breez/services/device.dart'; @@ -78,14 +77,4 @@ class ServiceInjector { Preferences get preferences { return _preferences ??= Preferences(); } - - Future get blockexplorer async { - String? blockexplorer = await _preferences?.getMempoolSpaceUrl(); - - if (blockexplorer == null) { - final config = await Config.instance(); - blockexplorer = config.defaultMempoolUrl; - } - return blockexplorer; - } } diff --git a/lib/utils/mempool_helper.dart b/lib/utils/mempool_helper.dart new file mode 100644 index 000000000..37953ba75 --- /dev/null +++ b/lib/utils/mempool_helper.dart @@ -0,0 +1,17 @@ +import 'package:c_breez/config.dart'; +import 'package:c_breez/services/injector.dart'; + +class MempoolHelper { + String formatTransactionUrl({required String txid, required String blockexplorer}) { + return "$blockexplorer/tx/$txid"; + } + + Future get blockexplorer async { + String? blockexplorer = await ServiceInjector().preferences.getMempoolSpaceUrl(); + if (blockexplorer == null) { + final config = await Config.instance(); + blockexplorer = config.defaultMempoolUrl; + } + return blockexplorer; + } +} From 0a1991236548a0f20367101f15a6e49e2a26d2ab Mon Sep 17 00:00:00 2001 From: ruben beck Date: Thu, 19 Oct 2023 12:59:30 +0200 Subject: [PATCH 3/3] use mempool instance accross app update tests rename to mempoolInstance get mempool instance from NetworkSettingsBloc create blockchain explorer utils --- lib/bloc/network/network_settings_bloc.dart | 30 ++++++- .../closed_channel_payment_details.dart | 89 ++++++++++--------- .../subswap/swap/widgets/inprogress_swap.dart | 14 +-- .../in_progress/reverse_swap_in_progress.dart | 10 ++- lib/utils/blockchain_explorer_utils.dart | 9 ++ lib/utils/mempool_helper.dart | 17 ---- .../network/network_settings_bloc_test.dart | 33 ++++++- 7 files changed, 125 insertions(+), 77 deletions(-) create mode 100644 lib/utils/blockchain_explorer_utils.dart delete mode 100644 lib/utils/mempool_helper.dart diff --git a/lib/bloc/network/network_settings_bloc.dart b/lib/bloc/network/network_settings_bloc.dart index e53801935..c17855f9f 100644 --- a/lib/bloc/network/network_settings_bloc.dart +++ b/lib/bloc/network/network_settings_bloc.dart @@ -1,5 +1,9 @@ +import 'dart:convert'; import 'package:c_breez/bloc/network/network_settings_state.dart'; import 'package:c_breez/config.dart' as lib; +import 'package:c_breez/config.dart'; +import 'package:c_breez/services/injector.dart'; +import 'package:c_breez/utils/blockchain_explorer_utils.dart'; import 'package:c_breez/utils/preferences.dart'; import 'package:logging/logging.dart'; import 'package:http/http.dart' as http; @@ -47,7 +51,7 @@ class NetworkSettingsBloc extends Cubit with HydratedMixin _log.warning("Invalid mempool url: $mempoolUrl"); return false; } - if (!await _testUri(uri)) { + if (!await _testUriSupportsMempoolApi(uri)) { _log.warning("Mempool url is not reachable: $mempoolUrl"); return false; } @@ -83,10 +87,28 @@ class NetworkSettingsBloc extends Cubit with HydratedMixin )); } - Future _testUri(Uri uri) async { + Future get mempoolInstance async { + String? mempoolInstance = await ServiceInjector().preferences.getMempoolSpaceUrl(); + if (mempoolInstance == null) { + final config = await Config.instance(); + mempoolInstance = config.defaultMempoolUrl; + } + return mempoolInstance; + } + + Future _testUriSupportsMempoolApi(Uri uri) async { + // We need to make sure that the mempool rest api is supported + // as the sdk depends on it. + final mempoolUri = + Uri.tryParse(BlockChainExplorerUtils().formatRecommendedFeesUrl(mempoolInstance: uri.toString())); + if (mempoolUri == null) return false; try { - final response = await _httpClient.get(uri); - return response.statusCode < 400; + final response = await _httpClient.get(mempoolUri); + if (response.statusCode != 200) { + return false; + } + final Map body = jsonDecode(response.body); + return body.containsKey("fastestFee"); } catch (e) { _log.warning("Failed to test mempool url: $uri", e); return false; diff --git a/lib/routes/home/widgets/payments_list/dialog/closed_channel_payment_details.dart b/lib/routes/home/widgets/payments_list/dialog/closed_channel_payment_details.dart index 664067b24..58ba584fc 100644 --- a/lib/routes/home/widgets/payments_list/dialog/closed_channel_payment_details.dart +++ b/lib/routes/home/widgets/payments_list/dialog/closed_channel_payment_details.dart @@ -1,10 +1,12 @@ import 'package:breez_sdk/bridge_generated.dart' as sdk; import 'package:breez_translations/breez_translations_locales.dart'; +import 'package:c_breez/bloc/network/network_settings_bloc.dart'; import 'package:c_breez/models/payment_minutiae.dart'; import 'package:c_breez/routes/home/widgets/payments_list/dialog/tx_widget.dart'; -import 'package:c_breez/utils/mempool_helper.dart'; +import 'package:c_breez/utils/blockchain_explorer_utils.dart'; import 'package:c_breez/widgets/loader.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; class ClosedChannelPaymentDetailsWidget extends StatelessWidget { final PaymentMinutiae paymentMinutiae; @@ -18,65 +20,66 @@ class ClosedChannelPaymentDetailsWidget extends StatelessWidget { Widget build(BuildContext context) { final themeData = Theme.of(context); final texts = context.texts(); - final mempoolHelper = MempoolHelper(); + final networkSettingsBloc = context.read(); + return FutureBuilder( - future: mempoolHelper.blockexplorer, + future: networkSettingsBloc.mempoolInstance, builder: (BuildContext context, AsyncSnapshot snapshot) { - if (snapshot.hasData) { - final blockexplorer = snapshot.data!; - if (paymentMinutiae.status == sdk.PaymentStatus.Complete) { - return Column( - mainAxisSize: MainAxisSize.min, - children: [ - RichText( - text: TextSpan( - style: themeData.dialogTheme.contentTextStyle, - text: texts.payment_details_dialog_closed_channel_local_wallet, - ), - ), - if (paymentMinutiae.paymentType == sdk.PaymentType.ClosedChannel && - paymentMinutiae.closingTxid != null) ...[ - TxWidget( - txURL: mempoolHelper.formatTransactionUrl( - txid: paymentMinutiae.closingTxid!, blockexplorer: blockexplorer), - txID: paymentMinutiae.closingTxid!, - ), - ], - ], - ); - } - // TODO pendingExpirationHeight - // TODO hoursToExpire - String estimation = texts.payment_details_dialog_closed_channel_transfer_no_estimation; + if (snapshot.connectionState != ConnectionState.done) { + return const Loader(); + } + final mempoolInstance = snapshot.data!; + if (paymentMinutiae.status == sdk.PaymentStatus.Complete) { return Column( mainAxisSize: MainAxisSize.min, children: [ RichText( text: TextSpan( style: themeData.dialogTheme.contentTextStyle, - text: estimation, + text: texts.payment_details_dialog_closed_channel_local_wallet, ), ), - if (paymentMinutiae.fundingTxid != null) ...[ - TxWidget( - txURL: mempoolHelper.formatTransactionUrl( - txid: paymentMinutiae.fundingTxid!, blockexplorer: blockexplorer), - txID: paymentMinutiae.fundingTxid!, - ), - ], - if (paymentMinutiae.closingTxid != null) ...[ + if (paymentMinutiae.paymentType == sdk.PaymentType.ClosedChannel && + paymentMinutiae.closingTxid != null) ...[ TxWidget( - txURL: mempoolHelper.formatTransactionUrl( - txid: paymentMinutiae.closingTxid!, blockexplorer: blockexplorer), + txURL: BlockChainExplorerUtils().formatTransactionUrl( + txid: paymentMinutiae.closingTxid!, mempoolInstance: mempoolInstance), txID: paymentMinutiae.closingTxid!, ), - ] + ], ], ); - } else { - return const Loader(); } + // TODO pendingExpirationHeight + // TODO hoursToExpire + String estimation = texts.payment_details_dialog_closed_channel_transfer_no_estimation; + + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + RichText( + text: TextSpan( + style: themeData.dialogTheme.contentTextStyle, + text: estimation, + ), + ), + if (paymentMinutiae.fundingTxid != null) ...[ + TxWidget( + txURL: BlockChainExplorerUtils().formatTransactionUrl( + txid: paymentMinutiae.fundingTxid!, mempoolInstance: mempoolInstance), + txID: paymentMinutiae.fundingTxid!, + ), + ], + if (paymentMinutiae.closingTxid != null) ...[ + TxWidget( + txURL: BlockChainExplorerUtils().formatTransactionUrl( + txid: paymentMinutiae.closingTxid!, mempoolInstance: mempoolInstance), + txID: paymentMinutiae.closingTxid!, + ), + ] + ], + ); }, ); } diff --git a/lib/routes/subswap/swap/widgets/inprogress_swap.dart b/lib/routes/subswap/swap/widgets/inprogress_swap.dart index d8180de47..30f0bca8a 100644 --- a/lib/routes/subswap/swap/widgets/inprogress_swap.dart +++ b/lib/routes/subswap/swap/widgets/inprogress_swap.dart @@ -1,11 +1,13 @@ import 'package:breez_sdk/bridge_generated.dart'; import 'package:breez_translations/breez_translations_locales.dart'; +import 'package:c_breez/bloc/network/network_settings_bloc.dart'; import 'package:c_breez/services/injector.dart'; -import 'package:c_breez/utils/mempool_helper.dart'; +import 'package:c_breez/utils/blockchain_explorer_utils.dart'; import 'package:c_breez/widgets/flushbar.dart'; import 'package:c_breez/widgets/link_launcher.dart'; import 'package:c_breez/widgets/loader.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; class SwapInprogress extends StatelessWidget { final SwapInfo swap; @@ -50,18 +52,20 @@ class _TxLink extends StatelessWidget { @override Widget build(BuildContext context) { final text = context.texts(); - final mempoolExplorer = MempoolHelper(); + final networkSettingsBloc = context.read(); + return FutureBuilder( - future: mempoolExplorer.blockexplorer, + future: networkSettingsBloc.mempoolInstance, builder: (context, snapshot) { if (snapshot.connectionState != ConnectionState.done) { return const Loader(); } - final blockexplorer = snapshot.data!; + final mempoolInstance = snapshot.data!; return LinkLauncher( linkName: txid, - linkAddress: mempoolExplorer.formatTransactionUrl(txid: txid, blockexplorer: blockexplorer), + linkAddress: + BlockChainExplorerUtils().formatTransactionUrl(txid: txid, mempoolInstance: mempoolInstance), onCopy: () { ServiceInjector().device.setClipboardText(txid); showFlushbar( diff --git a/lib/routes/withdraw/reverse_swap/in_progress/reverse_swap_in_progress.dart b/lib/routes/withdraw/reverse_swap/in_progress/reverse_swap_in_progress.dart index e6e858fb3..12f4dfb11 100644 --- a/lib/routes/withdraw/reverse_swap/in_progress/reverse_swap_in_progress.dart +++ b/lib/routes/withdraw/reverse_swap/in_progress/reverse_swap_in_progress.dart @@ -1,7 +1,8 @@ import 'package:breez_sdk/bridge_generated.dart' as sdk; import 'package:breez_translations/breez_translations_locales.dart'; +import 'package:c_breez/bloc/network/network_settings_bloc.dart'; import 'package:c_breez/services/injector.dart'; -import 'package:c_breez/utils/mempool_helper.dart'; +import 'package:c_breez/utils/blockchain_explorer_utils.dart'; import 'package:c_breez/widgets/flushbar.dart'; import 'package:c_breez/widgets/link_launcher.dart'; import 'package:c_breez/widgets/loader.dart'; @@ -46,16 +47,17 @@ class _TxLink extends StatelessWidget { @override Widget build(BuildContext context) { final text = context.texts(); - final mempoolHelper = MempoolHelper(); + final networkSettingsBloc = context.read(); return FutureBuilder( - future: mempoolHelper.blockexplorer, + future: networkSettingsBloc.mempoolInstance, builder: (context, snapshot) { if (snapshot.connectionState != ConnectionState.done) { return const Loader(); } - final transactionUrl = mempoolHelper.formatTransactionUrl(blockexplorer: snapshot.data!, txid: txid); + final transactionUrl = + BlockChainExplorerUtils().formatTransactionUrl(mempoolInstance: snapshot.data!, txid: txid); return LinkLauncher( linkName: txid, diff --git a/lib/utils/blockchain_explorer_utils.dart b/lib/utils/blockchain_explorer_utils.dart new file mode 100644 index 000000000..963444e99 --- /dev/null +++ b/lib/utils/blockchain_explorer_utils.dart @@ -0,0 +1,9 @@ +class BlockChainExplorerUtils { + String formatTransactionUrl({required String txid, required String mempoolInstance}) { + return "$mempoolInstance/tx/$txid"; + } + + String formatRecommendedFeesUrl({required String mempoolInstance}) { + return "$mempoolInstance/api/v1/fees/recommended"; + } +} diff --git a/lib/utils/mempool_helper.dart b/lib/utils/mempool_helper.dart deleted file mode 100644 index 37953ba75..000000000 --- a/lib/utils/mempool_helper.dart +++ /dev/null @@ -1,17 +0,0 @@ -import 'package:c_breez/config.dart'; -import 'package:c_breez/services/injector.dart'; - -class MempoolHelper { - String formatTransactionUrl({required String txid, required String blockexplorer}) { - return "$blockexplorer/tx/$txid"; - } - - Future get blockexplorer async { - String? blockexplorer = await ServiceInjector().preferences.getMempoolSpaceUrl(); - if (blockexplorer == null) { - final config = await Config.instance(); - blockexplorer = config.defaultMempoolUrl; - } - return blockexplorer; - } -} diff --git a/test/bloc/network/network_settings_bloc_test.dart b/test/bloc/network/network_settings_bloc_test.dart index fc156dae5..5330b8af8 100644 --- a/test/bloc/network/network_settings_bloc_test.dart +++ b/test/bloc/network/network_settings_bloc_test.dart @@ -1,6 +1,9 @@ +import 'dart:convert'; + import 'package:c_breez/bloc/network/network_settings_bloc.dart'; import 'package:c_breez/bloc/network/network_settings_state.dart'; import 'package:c_breez/services/injector.dart'; +import 'package:c_breez/utils/blockchain_explorer_utils.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:http/http.dart' as http; import 'package:path_provider_platform_interface/path_provider_platform_interface.dart'; @@ -12,6 +15,17 @@ import '../../unit_logger.dart'; import '../../utils/fake_path_provider_platform.dart'; import '../../utils/hydrated_bloc_storage.dart'; +String get _recomendedMockFeesResponse { + final Map recomendedFees = { + "fastestFee": 1, + "halfHourFee": 1, + "hourFee": 1, + "economyFee": 1, + "minimumFee": 1 + }; + return jsonEncode(recomendedFees); +} + void main() { TestWidgetsFlutterBinding.ensureInitialized(); final platform = FakePathProviderPlatform(); @@ -75,8 +89,12 @@ void main() { test('set mempool space url with a valid url should set on the preferences', () async { const url = "https://mempool.space"; + httpClient.getAnswer[url] = http.Response("{}", 200); final bloc = make(); + + httpClient.getAnswer[BlockChainExplorerUtils().formatRecommendedFeesUrl(mempoolInstance: url)] = + http.Response(_recomendedMockFeesResponse, 200); final result = await bloc.setMempoolUrl(url); expect(result, true); expect(injector.preferencesMock.setMempoolSpaceUrlUrl, url); @@ -84,8 +102,11 @@ void main() { test('set mempool space url with a valid url missing scheme should set on the preferences', () async { const url = "mempool.space"; - httpClient.getAnswer["https://$url"] = http.Response("{}", 200); + final bloc = make(); + httpClient.getAnswer[ + "https://${BlockChainExplorerUtils().formatRecommendedFeesUrl(mempoolInstance: url)}"] = + http.Response(_recomendedMockFeesResponse, 200); final result = await bloc.setMempoolUrl(url); expect(result, true); expect(injector.preferencesMock.setMempoolSpaceUrlUrl, "https://$url"); @@ -101,8 +122,9 @@ void main() { test('set mempool space url with an ip should should set on the preferences', () async { const url = "https://192.168.15.2"; - httpClient.getAnswer[url] = http.Response("{}", 200); final bloc = make(); + httpClient.getAnswer[BlockChainExplorerUtils().formatRecommendedFeesUrl(mempoolInstance: url)] = + http.Response(_recomendedMockFeesResponse, 200); final result = await bloc.setMempoolUrl(url); expect(result, true); expect(injector.preferencesMock.setMempoolSpaceUrlUrl, url); @@ -110,8 +132,9 @@ void main() { test('set mempool space url with an ip and port should should set on the preferences', () async { const url = "https://192.168.15.2:3006"; - httpClient.getAnswer[url] = http.Response("{}", 200); final bloc = make(); + httpClient.getAnswer[BlockChainExplorerUtils().formatRecommendedFeesUrl(mempoolInstance: url)] = + http.Response(_recomendedMockFeesResponse, 200); final result = await bloc.setMempoolUrl(url); expect(result, true); expect(injector.preferencesMock.setMempoolSpaceUrlUrl, url); @@ -119,8 +142,10 @@ void main() { test('set mempool space url with an ip missing scheme should should set on the preferences', () async { const url = "192.168.15.2"; - httpClient.getAnswer["https://$url"] = http.Response("{}", 200); final bloc = make(); + httpClient.getAnswer[ + "https://${BlockChainExplorerUtils().formatRecommendedFeesUrl(mempoolInstance: url)}"] = + http.Response(_recomendedMockFeesResponse, 200); final result = await bloc.setMempoolUrl(url); expect(result, true); expect(injector.preferencesMock.setMempoolSpaceUrlUrl, "https://$url");