diff --git a/lib/services/exchange/change_now/change_now_api.dart b/lib/services/exchange/change_now/change_now_api.dart index e88114aa5..993d9a08e 100644 --- a/lib/services/exchange/change_now/change_now_api.dart +++ b/lib/services/exchange/change_now/change_now_api.dart @@ -11,6 +11,7 @@ import 'package:stackwallet/models/exchange/change_now/exchange_transaction.dart import 'package:stackwallet/models/exchange/change_now/exchange_transaction_status.dart'; import 'package:stackwallet/models/exchange/change_now/fixed_rate_market.dart'; import 'package:stackwallet/models/exchange/response_objects/currency.dart'; +import 'package:stackwallet/models/exchange/response_objects/range.dart'; import 'package:stackwallet/services/exchange/exchange_response.dart'; import 'package:stackwallet/utilities/logger.dart'; @@ -266,6 +267,46 @@ class ChangeNowAPI { } } + /// The API endpoint returns minimal payment amount and maximum payment amount + /// required to make an exchange. If you try to exchange less than minimum or + /// more than maximum, the transaction will most likely fail. Any pair of + /// assets has minimum amount and some of pairs have maximum amount. + Future> getRange({ + required String fromTicker, + required String toTicker, + required bool isFixedRate, + String? apiKey, + }) async { + Map? params = {"api_key": apiKey ?? kChangeNowApiKey}; + + final uri = _buildUri( + "/exchange-range${isFixedRate ? "/fixed-rate" : ""}/${fromTicker}_$toTicker", + params); + + try { + final jsonObject = await _makeGetRequest(uri); + + final json = Map.from(jsonObject as Map); + return ExchangeResponse( + value: Range( + max: Decimal.tryParse(json["maxAmount"] as String? ?? ""), + min: Decimal.tryParse(json["minAmount"] as String? ?? ""), + ), + ); + } catch (e, s) { + Logging.instance.log( + "getRange exception: $e\n$s", + level: LogLevel.Error, + ); + return ExchangeResponse( + exception: ExchangeException( + e.toString(), + ExchangeExceptionType.generic, + ), + ); + } + } + /// Get estimated amount of [toTicker] cryptocurrency to receive /// for [fromAmount] of [fromTicker] Future> getEstimatedExchangeAmount({ @@ -309,6 +350,59 @@ class ChangeNowAPI { } } + /// Get estimated amount of [toTicker] cryptocurrency to receive + /// for [fromAmount] of [fromTicker] + Future> + getEstimatedExchangeAmountFixedRate({ + required String fromTicker, + required String toTicker, + required Decimal fromAmount, + required bool reversed, + String? apiKey, + }) async { + Map params = {"api_key": apiKey ?? kChangeNowApiKey}; + + late final Uri uri; + if (reversed) { + uri = _buildUri( + "/exchange-deposit/fixed-rate/${fromAmount.toString()}/${fromTicker}_$toTicker", + params, + ); + } else { + uri = _buildUri( + "/exchange-amount/fixed-rate/${fromAmount.toString()}/${fromTicker}_$toTicker", + params, + ); + } + + try { + // simple json object is expected here + final json = await _makeGetRequest(uri); + + try { + final value = EstimatedExchangeAmount.fromJson( + Map.from(json as Map)); + return ExchangeResponse(value: value); + } catch (_) { + return ExchangeResponse( + exception: ExchangeException( + "Failed to serialize $json", + ExchangeExceptionType.serializeResponseError, + ), + ); + } + } catch (e, s) { + Logging.instance.log("getEstimatedExchangeAmount exception: $e\n$s", + level: LogLevel.Error); + return ExchangeResponse( + exception: ExchangeException( + e.toString(), + ExchangeExceptionType.generic, + ), + ); + } + } + // old v1 version /// This API endpoint returns fixed-rate estimated exchange amount of /// [toTicker] cryptocurrency to receive for [fromAmount] of [fromTicker] diff --git a/lib/services/exchange/change_now/change_now_exchange.dart b/lib/services/exchange/change_now/change_now_exchange.dart index 5072db528..4220be17a 100644 --- a/lib/services/exchange/change_now/change_now_exchange.dart +++ b/lib/services/exchange/change_now/change_now_exchange.dart @@ -1,10 +1,13 @@ import 'package:decimal/decimal.dart'; +import 'package:stackwallet/models/exchange/change_now/estimated_exchange_amount.dart'; import 'package:stackwallet/models/exchange/response_objects/currency.dart'; import 'package:stackwallet/models/exchange/response_objects/pair.dart'; import 'package:stackwallet/models/exchange/response_objects/range.dart'; import 'package:stackwallet/models/exchange/response_objects/trade.dart'; +import 'package:stackwallet/services/exchange/change_now/change_now_api.dart'; import 'package:stackwallet/services/exchange/exchange.dart'; import 'package:stackwallet/services/exchange/exchange_response.dart'; +import 'package:uuid/uuid.dart'; class ChangeNowExchange extends Exchange { @override @@ -25,8 +28,10 @@ class ChangeNowExchange extends Exchange { Future>> getAllCurrencies( bool fixedRate, ) async { - // TODO: implement getAllCurrencies - throw UnimplementedError(); + return await ChangeNowAPI.instance.getAvailableCurrencies( + fixedRate: fixedRate ? true : null, + active: true, + ); } @override @@ -41,19 +46,41 @@ class ChangeNowExchange extends Exchange { String to, Decimal amount, bool fixedRate, + bool reversed, ) async { - // TODO: implement getEstimate - throw UnimplementedError(); + late final ExchangeResponse response; + if (fixedRate) { + response = + await ChangeNowAPI.instance.getEstimatedExchangeAmountFixedRate( + fromTicker: from, + toTicker: to, + fromAmount: amount, + reversed: reversed, + ); + } else { + response = await ChangeNowAPI.instance.getEstimatedExchangeAmount( + fromTicker: from, + toTicker: to, + fromAmount: amount, + ); + } + if (response.exception != null) { + return ExchangeResponse(exception: response.exception); + } + return ExchangeResponse(value: response.value?.estimatedAmount); } @override - Future> getMinMaxExchangeAmounts( + Future> getRange( String from, String to, bool fixedRate, ) async { - // TODO: implement getMinMaxExchangeAmounts - throw UnimplementedError(); + return await ChangeNowAPI.instance.getRange( + fromTicker: from, + toTicker: to, + isFixedRate: fixedRate, + ); } @override @@ -67,8 +94,76 @@ class ChangeNowExchange extends Exchange { @override Future> getTrade(String tradeId) async { - // TODO: implement getTrade - throw UnimplementedError(); + final response = + await ChangeNowAPI.instance.getTransactionStatus(id: tradeId); + if (response.exception != null) { + return ExchangeResponse(exception: response.exception); + } + final t = response.value!; + final timestamp = DateTime.tryParse(t.createdAt) ?? DateTime.now(); + + final trade = Trade( + uuid: const Uuid().v1(), + tradeId: tradeId, + rateType: "", + direction: "", + timestamp: timestamp, + updatedAt: DateTime.tryParse(t.updatedAt) ?? timestamp, + payInCurrency: t.fromCurrency, + payInAmount: t.expectedSendAmountDecimal, + payInAddress: t.payinAddress, + payInNetwork: "", + payInExtraId: t.payinExtraId, + payInTxid: "", + payOutCurrency: t.toCurrency, + payOutAmount: t.expectedReceiveAmountDecimal, + payOutAddress: t.payoutAddress, + payOutNetwork: "", + payOutExtraId: t.payoutExtraId, + payOutTxid: "", + refundAddress: t.refundAddress, + refundExtraId: t.refundExtraId, + status: t.status.name, + ); + + return ExchangeResponse(value: trade); + } + + @override + Future> updateTrade(Trade trade) async { + final response = + await ChangeNowAPI.instance.getTransactionStatus(id: trade.tradeId); + if (response.exception != null) { + return ExchangeResponse(exception: response.exception); + } + final t = response.value!; + final timestamp = DateTime.tryParse(t.createdAt) ?? DateTime.now(); + + final _trade = Trade( + uuid: trade.uuid, + tradeId: trade.tradeId, + rateType: "", + direction: "", + timestamp: timestamp, + updatedAt: DateTime.tryParse(t.updatedAt) ?? timestamp, + payInCurrency: t.fromCurrency, + payInAmount: t.expectedSendAmountDecimal, + payInAddress: t.payinAddress, + payInNetwork: "", + payInExtraId: t.payinExtraId, + payInTxid: "", + payOutCurrency: t.toCurrency, + payOutAmount: t.expectedReceiveAmountDecimal, + payOutAddress: t.payoutAddress, + payOutNetwork: "", + payOutExtraId: t.payoutExtraId, + payOutTxid: "", + refundAddress: t.refundAddress, + refundExtraId: t.refundExtraId, + status: t.status.name, + ); + + return ExchangeResponse(value: _trade); } @override diff --git a/lib/services/exchange/exchange.dart b/lib/services/exchange/exchange.dart index 03b11445f..e629b0e0d 100644 --- a/lib/services/exchange/exchange.dart +++ b/lib/services/exchange/exchange.dart @@ -18,10 +18,11 @@ abstract class Exchange { Future>> getAllPairs(bool fixedRate); Future> getTrade(String tradeId); + Future> updateTrade(Trade trade); Future>> getTrades(); - Future> getMinMaxExchangeAmounts( + Future> getRange( String from, String to, bool fixedRate, @@ -32,6 +33,7 @@ abstract class Exchange { String to, Decimal amount, bool fixedRate, + bool reversed, ); Future> createTrade({ diff --git a/lib/services/exchange/simpleswap/simpleswap_api.dart b/lib/services/exchange/simpleswap/simpleswap_api.dart index 3465765c9..944f9a520 100644 --- a/lib/services/exchange/simpleswap/simpleswap_api.dart +++ b/lib/services/exchange/simpleswap/simpleswap_api.dart @@ -334,6 +334,7 @@ class SimpleSwapAPI { Future> getExchange({ required String exchangeId, String? apiKey, + Trade? oldTrade, }) async { final uri = _buildUri( "/get_exchange", @@ -349,7 +350,7 @@ class SimpleSwapAPI { final json = Map.from(jsonObject as Map); final ts = DateTime.parse(json["timestamp"] as String); final trade = Trade( - uuid: const Uuid().v1(), + uuid: oldTrade?.uuid ?? const Uuid().v1(), tradeId: json["id"] as String, rateType: json["type"] as String, direction: "direct", diff --git a/lib/services/exchange/simpleswap/simpleswap_exchange.dart b/lib/services/exchange/simpleswap/simpleswap_exchange.dart index 18e8f4181..ca45317ea 100644 --- a/lib/services/exchange/simpleswap/simpleswap_exchange.dart +++ b/lib/services/exchange/simpleswap/simpleswap_exchange.dart @@ -73,6 +73,7 @@ class SimpleSwapExchange extends Exchange { String to, Decimal amount, bool fixedRate, + bool reversed, ) async { final response = await SimpleSwapAPI.instance.getEstimated( isFixedRate: fixedRate, @@ -88,7 +89,7 @@ class SimpleSwapExchange extends Exchange { } @override - Future> getMinMaxExchangeAmounts( + Future> getRange( String from, String to, bool fixedRate, @@ -114,6 +115,14 @@ class SimpleSwapExchange extends Exchange { return await SimpleSwapAPI.instance.getExchange(exchangeId: tradeId); } + @override + Future> updateTrade(Trade trade) async { + return await SimpleSwapAPI.instance.getExchange( + exchangeId: trade.tradeId, + oldTrade: trade, + ); + } + @override Future>> getTrades() async { // TODO: implement getTrades