Skip to content

Commit 2669304

Browse files
authored
Merge pull request #2684 from SatoshiPortal/fix/swap-route-unavailable-message
fix(swap): name the networks when the exchange rejects a swap
2 parents 02d341a + d6ff17b commit 2669304

18 files changed

Lines changed: 234 additions & 10 deletions
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
/// The network a payment moves through: on-chain Bitcoin, Lightning, or
2+
/// Liquid. Shared across features (e.g. `swap`, `dca`) that need to name a
3+
/// network without depending on each other's internal types.
4+
enum PaymentNetwork { bitcoin, lightning, liquid }
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import 'package:bb_mobile/core/primitives/payment_network.dart';
2+
import 'package:bb_mobile/core/utils/build_context_x.dart';
3+
import 'package:flutter/widgets.dart';
4+
5+
/// Kept in its own file so [PaymentNetwork] itself stays Flutter-free.
6+
extension PaymentNetworkL10n on PaymentNetwork {
7+
String toTranslated(BuildContext context) => switch (this) {
8+
PaymentNetwork.bitcoin => context.loc.transactionNetworkBitcoin,
9+
PaymentNetwork.lightning => context.loc.transactionNetworkLightning,
10+
PaymentNetwork.liquid => context.loc.transactionNetworkLiquid,
11+
};
12+
}

lib/features/receive/domain/receive_failure.dart

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import 'package:bb_mobile/core/failures/failure.dart';
2+
import 'package:bb_mobile/core/primitives/payment_network.dart';
23

34
sealed class ReceiveFailure extends Failure {
45
const ReceiveFailure([super.logMessage]);
@@ -23,6 +24,17 @@ final class ReceiveSwapUnavailableFailure extends ReceiveFailure {
2324
const ReceiveSwapUnavailableFailure([super.logMessage]);
2425
}
2526

27+
final class ReceiveSwapRouteUnavailableFailure extends ReceiveFailure {
28+
final PaymentNetwork? inNetwork;
29+
final PaymentNetwork? outNetwork;
30+
31+
const ReceiveSwapRouteUnavailableFailure({
32+
this.inNetwork,
33+
this.outNetwork,
34+
String? logMessage,
35+
}) : super(logMessage);
36+
}
37+
2638
final class ReceiveNetworkFailure extends ReceiveFailure {
2739
const ReceiveNetworkFailure([super.logMessage]);
2840
}

lib/features/receive/domain/usecases/create_receive_order_swap_usecase.dart

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,12 @@ class CreateReceiveOrderSwapUsecase {
143143
isMinimum: isMinimum,
144144
logMessage: logMessage,
145145
),
146-
SwapNoPaymentOptionFailure() ||
146+
SwapNoPaymentOptionFailure(:final inNetwork, :final outNetwork) =>
147+
ReceiveSwapRouteUnavailableFailure(
148+
inNetwork: inNetwork,
149+
outNetwork: outNetwork,
150+
logMessage: failure.logMessage,
151+
),
147152
SwapValidationFailure() ||
148153
SwapProviderFailure() => ReceiveSwapUnavailableFailure(failure.logMessage),
149154
SwapRateLimitedFailure(:final retryAfter) => ReceiveRateLimitedFailure(

lib/features/receive/presentation/receive_failure_l10n.dart

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import 'package:bb_mobile/core/primitives/payment_network_l10n.dart';
12
import 'package:bb_mobile/core/utils/build_context_x.dart';
23
import 'package:bb_mobile/features/receive/domain/receive_failure.dart';
34
import 'package:flutter/widgets.dart';
@@ -18,6 +19,13 @@ extension ReceiveFailureL10n on ReceiveFailure {
1819
ReceiveInvalidInvoiceFailure() ||
1920
ReceiveSwapUnavailableFailure() ||
2021
ReceiveUnexpectedFailure() => context.loc.oopsSomethingWentWrong,
22+
ReceiveSwapRouteUnavailableFailure(:final inNetwork?, :final outNetwork?) =>
23+
context.loc.swapErrorRouteUnavailable(
24+
inNetwork.toTranslated(context),
25+
outNetwork.toTranslated(context),
26+
),
27+
ReceiveSwapRouteUnavailableFailure() =>
28+
context.loc.swapErrorRouteUnavailableGeneric,
2129
ReceiveRateLimitedFailure(:final retryAfter) =>
2230
context.loc.swapErrorRateLimited(retryAfter?.inSeconds ?? 30),
2331
ReceiveNetworkFailure() => context.loc.payNetworkError,

lib/features/send/domain/send_failure.dart

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import 'package:bb_mobile/core/failures/failure.dart';
2+
import 'package:bb_mobile/core/primitives/payment_network.dart';
23

34
sealed class SendFailure extends Failure {
45
const SendFailure([super.logMessage]);
@@ -46,6 +47,17 @@ final class SendSwapCreationFailure extends SendFailure {
4647
const SendSwapCreationFailure([super.logMessage]);
4748
}
4849

50+
final class SendSwapRouteUnavailableFailure extends SendFailure {
51+
final PaymentNetwork? inNetwork;
52+
final PaymentNetwork? outNetwork;
53+
54+
const SendSwapRouteUnavailableFailure({
55+
this.inNetwork,
56+
this.outNetwork,
57+
String? logMessage,
58+
}) : super(logMessage);
59+
}
60+
4961
final class SendRateLimitedFailure extends SendFailure {
5062
final Duration? retryAfter;
5163

lib/features/send/domain/swap_failure_to_send_failure.dart

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,12 @@ SendFailure mapSwapFailureToSendFailure(SwapFailure failure) =>
1212
SwapValidationFailure() => SendInvalidPaymentRequestFailure(
1313
logMessage: failure.logMessage,
1414
),
15-
SwapNoPaymentOptionFailure() ||
15+
SwapNoPaymentOptionFailure(:final inNetwork, :final outNetwork) =>
16+
SendSwapRouteUnavailableFailure(
17+
inNetwork: inNetwork,
18+
outNetwork: outNetwork,
19+
logMessage: failure.logMessage,
20+
),
1621
SwapOrderExpiredFailure() ||
1722
SwapCreationUnknownFailure() ||
1823
SwapOrderMismatchFailure() ||

lib/features/send/presentation/send_failure_l10n.dart

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import 'package:bb_mobile/core/primitives/payment_network_l10n.dart';
12
import 'package:bb_mobile/core/utils/build_context_x.dart';
23
import 'package:bb_mobile/features/send/domain/send_failure.dart';
34
import 'package:flutter/widgets.dart';
@@ -22,6 +23,13 @@ extension SendFailureL10n on SendFailure {
2223
SendAmountOutOfBoundsFailure() =>
2324
context.loc.sendErrorAmountBelowSwapLimits,
2425
SendSwapCreationFailure() => context.loc.sendErrorSwapCreationFailed,
26+
SendSwapRouteUnavailableFailure(:final inNetwork?, :final outNetwork?) =>
27+
context.loc.swapErrorRouteUnavailable(
28+
inNetwork.toTranslated(context),
29+
outNetwork.toTranslated(context),
30+
),
31+
SendSwapRouteUnavailableFailure() =>
32+
context.loc.swapErrorRouteUnavailableGeneric,
2533
SendRateLimitedFailure(:final retryAfter) =>
2634
context.loc.swapErrorRateLimited(retryAfter?.inSeconds ?? 30),
2735
SendTransactionBuildFailure() => context.loc.sendErrorBuildFailed,

lib/features/swap/data/order_swap_repository_impl.dart

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,11 @@ class OrderSwapRepositoryImpl implements OrderSwapRepository {
6868
);
6969
return Ok(model.toEntity(inNetwork: inNetwork, outNetwork: outNetwork));
7070
} catch (error) {
71-
final failure = _mapFailure(error);
71+
final failure = _mapFailure(
72+
error,
73+
inNetwork: inNetwork,
74+
outNetwork: outNetwork,
75+
);
7276
log.warning(
7377
'[OrderSwap] quote failed environment=${environment.name} '
7478
'route=${inNetwork.name}->${outNetwork.name} '
@@ -175,7 +179,13 @@ class OrderSwapRepositoryImpl implements OrderSwapRepository {
175179
} catch (error) {
176180
if (error is ArgumentError) {
177181
await _saveRecordLocked(matching.markFailed());
178-
return Err(_mapFailure(error));
182+
return Err(
183+
_mapFailure(
184+
error,
185+
inNetwork: inNetwork,
186+
outNetwork: outNetwork,
187+
),
188+
);
179189
}
180190
return const Err(
181191
SwapCreationUnknownFailure(
@@ -280,7 +290,9 @@ class OrderSwapRepositoryImpl implements OrderSwapRepository {
280290
return Err(SwapStorageFailure(storageError.toString()));
281291
}
282292
}
283-
return Err(_mapFailure(error));
293+
return Err(
294+
_mapFailure(error, inNetwork: inNetwork, outNetwork: outNetwork),
295+
);
284296
}
285297
}
286298

@@ -654,7 +666,11 @@ class OrderSwapRepositoryImpl implements OrderSwapRepository {
654666
return current;
655667
}
656668

657-
SwapFailure _mapFailure(Object error) {
669+
SwapFailure _mapFailure(
670+
Object error, {
671+
OrderSwapNetwork? inNetwork,
672+
OrderSwapNetwork? outNetwork,
673+
}) {
658674
if (error is ArgumentError) {
659675
return SwapOrderMismatchFailure(error.message.toString());
660676
}
@@ -680,7 +696,11 @@ class OrderSwapRepositoryImpl implements OrderSwapRepository {
680696
}
681697
if (error is ExchangeRpcException) {
682698
return switch (error.apiCode) {
683-
'ERR_ORD_PO404' => SwapNoPaymentOptionFailure(error.logMessage),
699+
'ERR_ORD_PO404' => SwapNoPaymentOptionFailure(
700+
inNetwork: inNetwork?.toPaymentNetwork,
701+
outNetwork: outNetwork?.toPaymentNetwork,
702+
logMessage: error.logMessage,
703+
),
684704
'ERR_ORD_LMT001' => SwapAmountOutOfBoundsFailure(
685705
limitAmountSat: error.limit == null
686706
? null
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,15 @@
1+
import 'package:bb_mobile/core/primitives/payment_network.dart';
2+
13
enum OrderSwapNetwork {
24
bitcoin,
35
liquid,
46
lightning;
57

68
String get apiName => name;
9+
10+
PaymentNetwork get toPaymentNetwork => switch (this) {
11+
OrderSwapNetwork.bitcoin => PaymentNetwork.bitcoin,
12+
OrderSwapNetwork.liquid => PaymentNetwork.liquid,
13+
OrderSwapNetwork.lightning => PaymentNetwork.lightning,
14+
};
715
}

0 commit comments

Comments
 (0)