Skip to content

Commit 0870a24

Browse files
authored
Merge pull request #2099 from SatoshiPortal/sell-fixes
Add null check to order error object check. Add timeout to order api client.
2 parents c5276d8 + 9ea392f commit 0870a24

2 files changed

Lines changed: 83 additions & 51 deletions

File tree

lib/core/exchange/data/datasources/bullbitcoin_api_datasource.dart

Lines changed: 63 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -172,23 +172,27 @@ class BullbitcoinApiDatasource implements BitcoinPriceDatasource {
172172
if (statusCode != 200) throw Exception('Failed to create order');
173173
if (error != null) {
174174
final reason = error['data']['reason'];
175-
final limitReason = reason['limit'];
176-
if (limitReason != null) {
177-
final isBelowLimit =
178-
limitReason['conditionalOperator'] == 'GREATER_THAN_OR_EQUAL';
179-
final limitAmount = limitReason['amount'] as String;
180-
final limitCurrency = limitReason['currencyCode'] as String;
181-
if (isBelowLimit) {
182-
throw BullBitcoinApiMinAmountException(
183-
minAmount: double.parse(limitAmount),
184-
currency: limitCurrency,
185-
);
186-
} else {
187-
throw BullBitcoinApiMaxAmountException(
188-
maxAmount: double.parse(limitAmount),
189-
currency: limitCurrency,
190-
);
175+
if (reason != null) {
176+
final limitReason = reason['limit'];
177+
if (limitReason != null) {
178+
final isBelowLimit =
179+
limitReason['conditionalOperator'] == 'GREATER_THAN_OR_EQUAL';
180+
final limitAmount = limitReason['amount'] as String;
181+
final limitCurrency = limitReason['currencyCode'] as String;
182+
if (isBelowLimit) {
183+
throw BullBitcoinApiMinAmountException(
184+
minAmount: double.parse(limitAmount),
185+
currency: limitCurrency,
186+
);
187+
} else {
188+
throw BullBitcoinApiMaxAmountException(
189+
maxAmount: double.parse(limitAmount),
190+
currency: limitCurrency,
191+
);
192+
}
191193
}
194+
} else {
195+
throw Exception('Failed to create buy order: ${error['message']}');
192196
}
193197
}
194198
return OrderModel.fromJson(resp.data['result'] as Map<String, dynamic>);
@@ -354,23 +358,27 @@ class BullbitcoinApiDatasource implements BitcoinPriceDatasource {
354358
if (statusCode != 200) throw Exception('Failed to create sell order');
355359
if (error != null) {
356360
final reason = error['data']['reason'];
357-
final limitReason = reason['limit'];
358-
if (limitReason != null) {
359-
final isBelowLimit =
360-
limitReason['conditionalOperator'] == 'GREATER_THAN_OR_EQUAL';
361-
final limitAmount = limitReason['amount'] as String;
362-
final limitCurrency = limitReason['currencyCode'] as String;
363-
if (isBelowLimit) {
364-
throw BullBitcoinApiMinAmountException(
365-
minAmount: double.parse(limitAmount),
366-
currency: limitCurrency,
367-
);
368-
} else {
369-
throw BullBitcoinApiMaxAmountException(
370-
maxAmount: double.parse(limitAmount),
371-
currency: limitCurrency,
372-
);
361+
if (reason != null) {
362+
final limitReason = reason['limit'];
363+
if (limitReason != null) {
364+
final isBelowLimit =
365+
limitReason['conditionalOperator'] == 'GREATER_THAN_OR_EQUAL';
366+
final limitAmount = limitReason['amount'] as String;
367+
final limitCurrency = limitReason['currencyCode'] as String;
368+
if (isBelowLimit) {
369+
throw BullBitcoinApiMinAmountException(
370+
minAmount: double.parse(limitAmount),
371+
currency: limitCurrency,
372+
);
373+
} else {
374+
throw BullBitcoinApiMaxAmountException(
375+
maxAmount: double.parse(limitAmount),
376+
currency: limitCurrency,
377+
);
378+
}
373379
}
380+
} else {
381+
throw Exception('Failed to create sell order: ${error['message']}');
374382
}
375383
}
376384
return OrderModel.fromJson(resp.data['result'] as Map<String, dynamic>);
@@ -480,25 +488,31 @@ class BullbitcoinApiDatasource implements BitcoinPriceDatasource {
480488
if (statusCode != 200) throw Exception('Failed to create withdrawal order');
481489
if (error != null) {
482490
final reason = error['data']['reason'];
483-
final limitReason = reason['limit'];
484-
if (limitReason != null) {
485-
final isBelowLimit =
486-
limitReason['conditionalOperator'] == 'GREATER_THAN_OR_EQUAL';
487-
final limitAmount = limitReason['amount'] as String;
488-
final limitCurrency = limitReason['currencyCode'] as String;
489-
if (isBelowLimit) {
490-
throw BullBitcoinApiMinAmountException(
491-
minAmount: double.parse(limitAmount),
492-
currency: limitCurrency,
493-
);
494-
} else {
495-
throw BullBitcoinApiMaxAmountException(
496-
maxAmount: double.parse(limitAmount),
497-
currency: limitCurrency,
498-
);
491+
if (reason != null) {
492+
final limitReason = reason['limit'];
493+
if (limitReason != null) {
494+
final isBelowLimit =
495+
limitReason['conditionalOperator'] == 'GREATER_THAN_OR_EQUAL';
496+
final limitAmount = limitReason['amount'] as String;
497+
final limitCurrency = limitReason['currencyCode'] as String;
498+
if (isBelowLimit) {
499+
throw BullBitcoinApiMinAmountException(
500+
minAmount: double.parse(limitAmount),
501+
currency: limitCurrency,
502+
);
503+
} else {
504+
throw BullBitcoinApiMaxAmountException(
505+
maxAmount: double.parse(limitAmount),
506+
currency: limitCurrency,
507+
);
508+
}
499509
}
510+
throw Exception('Failed to create withdrawal order: $reason');
511+
} else {
512+
throw Exception(
513+
'Failed to create withdrawal order: ${error['message']}',
514+
);
500515
}
501-
throw Exception('Failed to create withdrawal order: $reason');
502516
}
503517
return OrderModel.fromJson(resp.data['result'] as Map<String, dynamic>);
504518
}

lib/core/exchange/exchange_locator.dart

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,10 +61,23 @@ class ExchangeLocator {
6161
),
6262
);
6363

64+
// Bound order-endpoint waits when the API is reached over Tor — without
65+
// an explicit timeout, a hung connect lingered for tens of seconds and
66+
// sell-order polling spammed SEVERE on every tick. Scoped to the order
67+
// datasource only; the authenticated chain (recipients, fund_exchange)
68+
// and the price/support-chat clients are intentionally left alone for
69+
// this release.
70+
const orderApiTimeout = Duration(seconds: 30);
71+
6472
locator.registerLazySingleton<BullbitcoinApiDatasource>(
6573
() => BullbitcoinApiDatasource(
6674
bullbitcoinApiHttpClient: Dio(
67-
BaseOptions(baseUrl: ApiServiceConstants.bbApiUrl),
75+
BaseOptions(
76+
baseUrl: ApiServiceConstants.bbApiUrl,
77+
connectTimeout: orderApiTimeout,
78+
receiveTimeout: orderApiTimeout,
79+
sendTimeout: orderApiTimeout,
80+
),
6881
),
6982
),
7083
instanceName: 'mainnetExchangeApiDatasource',
@@ -73,7 +86,12 @@ class ExchangeLocator {
7386
locator.registerLazySingleton<BullbitcoinApiDatasource>(
7487
() => BullbitcoinApiDatasource(
7588
bullbitcoinApiHttpClient: Dio(
76-
BaseOptions(baseUrl: ApiServiceConstants.bbApiTestUrl),
89+
BaseOptions(
90+
baseUrl: ApiServiceConstants.bbApiTestUrl,
91+
connectTimeout: orderApiTimeout,
92+
receiveTimeout: orderApiTimeout,
93+
sendTimeout: orderApiTimeout,
94+
),
7795
),
7896
),
7997
instanceName: 'testnetExchangeApiDatasource',

0 commit comments

Comments
 (0)