Skip to content

Commit

Permalink
Move LnUrl API's to LnUrlBloc
Browse files Browse the repository at this point in the history
* Validate LnUrl payments against lightning limits
* Rename isLnurlPayment to isLnUrlPayment for consistency of capitalization of LnUrl
  • Loading branch information
erdemyerebasmaz committed Jul 12, 2024
1 parent 8859315 commit a67a108
Show file tree
Hide file tree
Showing 9 changed files with 114 additions and 59 deletions.
33 changes: 0 additions & 33 deletions lib/bloc/account/account_bloc.dart
Original file line number Diff line number Diff line change
Expand Up @@ -334,37 +334,4 @@ class AccountBloc extends Cubit<AccountState> with HydratedMixin {
}
return filteredPayments;
}

Future<liquid_sdk.LnUrlWithdrawResult> lnurlWithdraw({
required liquid_sdk.LnUrlWithdrawRequest req,
}) async {
try {
return await _liquidSdk.instance!.lnurlWithdraw(req: req);
} catch (e) {
_log.severe("lnurlWithdraw error", e);
rethrow;
}
}

Future<liquid_sdk.LnUrlPayResult> lnurlPay({
required liquid_sdk.LnUrlPayRequest req,
}) async {
try {
return await _liquidSdk.instance!.lnurlPay(req: req);
} catch (e) {
_log.severe("lnurlPay error", e);
rethrow;
}
}

Future<liquid_sdk.LnUrlCallbackStatus> lnurlAuth({
required liquid_sdk.LnUrlAuthRequestData reqData,
}) async {
try {
return await _liquidSdk.instance!.lnurlAuth(reqData: reqData);
} catch (e) {
_log.severe("lnurlAuth error", e);
rethrow;
}
}
}
56 changes: 56 additions & 0 deletions lib/bloc/lnurl/lnurl_bloc.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import 'package:flutter_breez_liquid/flutter_breez_liquid.dart';
import 'package:hydrated_bloc/hydrated_bloc.dart';
import 'package:l_breez/bloc/account/breez_sdk_liquid.dart';
import 'package:l_breez/bloc/lnurl/lnurl_state.dart';
import 'package:logging/logging.dart';

class LnUrlBloc extends Cubit<LnUrlState> {
final _log = Logger("LnUrlBloc");
final BreezSDKLiquid _liquidSdk;

LnUrlBloc(this._liquidSdk) : super(LnUrlState.initial());

Future<LightningPaymentLimitsResponse> fetchLightningLimits() async {
try {
final limits = await _liquidSdk.instance!.fetchLightningLimits();
emit(state.copyWith(limits: limits));
return limits;
} catch (e) {
_log.severe("fetchLightningLimits error", e);
rethrow;
}
}

Future<LnUrlWithdrawResult> lnurlWithdraw({
required LnUrlWithdrawRequest req,
}) async {
try {
return await _liquidSdk.instance!.lnurlWithdraw(req: req);
} catch (e) {
_log.severe("lnurlWithdraw error", e);
rethrow;
}
}

Future<LnUrlPayResult> lnurlPay({
required LnUrlPayRequest req,
}) async {
try {
return await _liquidSdk.instance!.lnurlPay(req: req);
} catch (e) {
_log.severe("lnurlPay error", e);
rethrow;
}
}

Future<LnUrlCallbackStatus> lnurlAuth({
required LnUrlAuthRequestData reqData,
}) async {
try {
return await _liquidSdk.instance!.lnurlAuth(reqData: reqData);
} catch (e) {
_log.severe("lnurlAuth error", e);
rethrow;
}
}
}
17 changes: 17 additions & 0 deletions lib/bloc/lnurl/lnurl_state.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import 'package:flutter_breez_liquid/flutter_breez_liquid.dart';

class LnUrlState {
final LightningPaymentLimitsResponse? limits;

LnUrlState({this.limits});

LnUrlState.initial() : this();

LnUrlState copyWith({
LightningPaymentLimitsResponse? limits,
}) {
return LnUrlState(
limits: limits ?? this.limits,
);
}
}
4 changes: 4 additions & 0 deletions lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import 'package:l_breez/bloc/account/credentials_manager.dart';
import 'package:l_breez/bloc/backup/backup_bloc.dart';
import 'package:l_breez/bloc/currency/currency_bloc.dart';
import 'package:l_breez/bloc/input/input_bloc.dart';
import 'package:l_breez/bloc/lnurl/lnurl_bloc.dart';
import 'package:l_breez/bloc/security/security_bloc.dart';
import 'package:l_breez/bloc/user_profile/user_profile_bloc.dart';
import 'package:l_breez/services/injector.dart';
Expand Down Expand Up @@ -86,6 +87,9 @@ void main() async {
BlocProvider<BackupBloc>(
create: (BuildContext context) => BackupBloc(injector.liquidSDK),
),
BlocProvider<LnUrlBloc>(
create: (BuildContext context) => LnUrlBloc(injector.liquidSDK),
),
],
child: UserApp(),
),
Expand Down
9 changes: 5 additions & 4 deletions lib/routes/lnurl/auth/lnurl_auth_handler.dart
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import 'package:breez_translations/breez_translations_locales.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_breez_liquid/flutter_breez_liquid.dart';
import 'package:l_breez/bloc/account/account_bloc.dart';
import 'package:l_breez/bloc/lnurl/lnurl_bloc.dart';
import 'package:l_breez/routes/lnurl/auth/login_text.dart';
import 'package:l_breez/routes/lnurl/widgets/lnurl_page_result.dart';
import 'package:l_breez/widgets/error_dialog.dart';
import 'package:l_breez/widgets/loader.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:logging/logging.dart';

final _log = Logger("HandleLNURLAuthRequest");
Expand All @@ -23,7 +23,8 @@ Future<LNURLPageResult?> handleAuthRequest(
final loaderRoute = createLoaderRoute(context);
navigator.push(loaderRoute);
try {
final resp = await context.read<AccountBloc>().lnurlAuth(reqData: reqData);
final lnurlBloc = context.read<LnUrlBloc>();
final resp = await lnurlBloc.lnurlAuth(reqData: reqData);
if (resp is LnUrlCallbackStatus_Ok) {
_log.info("LNURL auth success");
return const LNURLPageResult(protocol: LnUrlProtocol.Auth);
Expand Down
12 changes: 6 additions & 6 deletions lib/routes/lnurl/payment/lnurl_payment_handler.dart
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_breez_liquid/flutter_breez_liquid.dart';
import 'package:l_breez/bloc/account/account_bloc.dart';
import 'package:l_breez/bloc/lnurl/lnurl_bloc.dart';
import 'package:l_breez/routes/lnurl/payment/lnurl_payment_dialog.dart';
import 'package:l_breez/routes/lnurl/payment/lnurl_payment_info.dart';
import 'package:l_breez/routes/lnurl/payment/lnurl_payment_page.dart';
import 'package:l_breez/routes/lnurl/payment/success_action/success_action_dialog.dart';
import 'package:l_breez/routes/lnurl/widgets/lnurl_page_result.dart';
import 'package:l_breez/widgets/payment_dialogs/processing_payment_dialog.dart';
import 'package:l_breez/widgets/route.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:logging/logging.dart';

final _log = Logger("HandleLNURLPayRequest");
Expand Down Expand Up @@ -44,16 +44,16 @@ Future<LNURLPageResult?> handlePayRequest(
context: context,
barrierDismissible: false,
builder: (_) => ProcessingPaymentDialog(
isLnurlPayment: true,
isLnUrlPayment: true,
firstPaymentItemKey: firstPaymentItemKey,
paymentFunc: () {
final accBloc = context.read<AccountBloc>();
final lnurlBloc = context.read<LnUrlBloc>();
final req = LnUrlPayRequest(
amountMsat: BigInt.from(paymentInfo!.amount * 1000),
comment: paymentInfo.comment,
data: data,
);
return accBloc.lnurlPay(req: req);
return lnurlBloc.lnurlPay(req: req);
},
),
).then((result) {
Expand Down
24 changes: 17 additions & 7 deletions lib/routes/lnurl/payment/lnurl_payment_page.dart
Original file line number Diff line number Diff line change
@@ -1,20 +1,22 @@
import 'dart:convert';
import 'dart:math';

import 'package:breez_translations/breez_translations_locales.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_breez_liquid/flutter_breez_liquid.dart';
import 'package:l_breez/bloc/account/account_bloc.dart';
import 'package:l_breez/bloc/currency/currency_bloc.dart';
import 'package:l_breez/bloc/lnurl/lnurl_bloc.dart';
import 'package:l_breez/routes/lnurl/payment/lnurl_payment_info.dart';
import 'package:l_breez/routes/lnurl/widgets/lnurl_metadata.dart';
import 'package:l_breez/theme/theme_provider.dart' as theme;
import 'package:l_breez/utils/payment_validator.dart';
import 'package:l_breez/widgets/amount_form_field/amount_form_field.dart';
import 'package:l_breez/widgets/back_button.dart' as back_button;
import 'package:l_breez/widgets/single_button_bottom_bar.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:logging/logging.dart';

final _log = Logger("LNURLPaymentPage");
Expand Down Expand Up @@ -67,14 +69,16 @@ class LNURLPaymentPageState extends State<LNURLPaymentPage> {
super.initState();
fixedAmount = widget.data.minSendable == widget.data.maxSendable;
WidgetsBinding.instance.addPostFrameCallback(
(_) {
(_) async {
if (fixedAmount) {
final currencyState = context.read<CurrencyBloc>().state;
_amountController.text = currencyState.bitcoinCurrency.format(
(widget.data.maxSendable.toInt() ~/ 1000),
includeDisplayName: false,
);
}
final lnurlBloc = context.read<LnUrlBloc>();
await lnurlBloc.fetchLightningLimits();
},
);
}
Expand Down Expand Up @@ -241,13 +245,19 @@ class LNURLPaymentPageState extends State<LNURLPaymentPage> {
final texts = context.texts();
final accBloc = context.read<AccountBloc>();
final currencyState = context.read<CurrencyBloc>().state;
final lnurlState = context.read<LnUrlBloc>().state;
final limits = lnurlState.limits?.send;

final maxSendable = widget.data.maxSendable.toInt() ~/ 1000;
final maxSendable = (limits != null)
? min(limits.maxSat.toInt(), widget.data.maxSendable.toInt() ~/ 1000)
: widget.data.maxSendable.toInt() ~/ 1000;
if (amount > maxSendable) {
return texts.lnurl_payment_page_error_exceeds_limit(maxSendable);
}

final minSendable = widget.data.minSendable.toInt() ~/ 1000;
final minSendable = (limits != null)
? max(limits.maxSat.toInt(), widget.data.minSendable.toInt() ~/ 1000)
: widget.data.minSendable.toInt() ~/ 1000;
if (amount < minSendable) {
return texts.lnurl_payment_page_error_below_limit(minSendable);
}
Expand Down
10 changes: 5 additions & 5 deletions lib/routes/lnurl/withdraw/lnurl_withdraw_dialog.dart
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import 'package:breez_translations/breez_translations_locales.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_breez_liquid/flutter_breez_liquid.dart';
import 'package:l_breez/bloc/account/account_bloc.dart';
import 'package:l_breez/bloc/lnurl/lnurl_bloc.dart';
import 'package:l_breez/routes/lnurl/widgets/lnurl_page_result.dart';
import 'package:l_breez/theme/theme_provider.dart' as theme;
import 'package:l_breez/utils/exceptions.dart';
import 'package:l_breez/widgets/loading_animated_text.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:logging/logging.dart';

final _log = Logger("LNURLWithdrawDialog");
Expand Down Expand Up @@ -136,7 +136,7 @@ class _LNURLWithdrawDialogState extends State<LNURLWithdrawDialog> with SingleTi
Future<LNURLPageResult> _withdraw(BuildContext context) async {
_log.info("Withdraw ${widget.amountSats} sats");
final texts = context.texts();
final accountBloc = context.read<AccountBloc>();
final lnurlBloc = context.read<LnUrlBloc>();
final description = widget.requestData.defaultDescription;

try {
Expand All @@ -148,7 +148,7 @@ class _LNURLWithdrawDialogState extends State<LNURLWithdrawDialog> with SingleTi
data: widget.requestData,
description: description,
);
final resp = await accountBloc.lnurlWithdraw(req: req);
final resp = await lnurlBloc.lnurlWithdraw(req: req);
if (resp is LnUrlWithdrawResult_Ok) {
final paymentHash = resp.data.invoice.paymentHash;
_log.info("LNURL withdraw success for $paymentHash");
Expand Down
8 changes: 4 additions & 4 deletions lib/widgets/payment_dialogs/processing_payment_dialog.dart
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,15 @@ class ProcessingPaymentDialog extends StatefulWidget {
final GlobalKey? firstPaymentItemKey;
final double minHeight;
final bool popOnCompletion;
final bool isLnurlPayment;
final bool isLnUrlPayment;
final Future Function() paymentFunc;
final Function(PaymentRequestState state)? onStateChange;

const ProcessingPaymentDialog({
this.firstPaymentItemKey,
this.minHeight = 220,
this.popOnCompletion = false,
this.isLnurlPayment = false,
this.isLnUrlPayment = false,
required this.paymentFunc,
this.onStateChange,
super.key,
Expand Down Expand Up @@ -95,15 +95,15 @@ class ProcessingPaymentDialogState extends State<ProcessingPaymentDialog>
final texts = getSystemAppLocalizations();
widget.paymentFunc().then((payResult) async {
await _animateClose();
if (widget.isLnurlPayment) {
if (widget.isLnUrlPayment) {
navigator.pop(payResult);
}
}).catchError((err) {
if (widget.popOnCompletion) {
navigator.removeRoute(_currentRoute!);
}
widget.onStateChange?.call(PaymentRequestState.PAYMENT_COMPLETED);
if (widget.isLnurlPayment) {
if (widget.isLnUrlPayment) {
navigator.pop(err);
}
if (err is FrbException || err is PaymentError_PaymentTimeout) {
Expand Down

0 comments on commit a67a108

Please sign in to comment.