2022-08-26 08:11:35 +00:00
|
|
|
import 'dart:convert';
|
|
|
|
|
|
|
|
import 'package:decimal/decimal.dart';
|
|
|
|
import 'package:flutter/foundation.dart';
|
|
|
|
import 'package:http/http.dart' as http;
|
|
|
|
import 'package:stackwallet/external_api_keys.dart';
|
|
|
|
import 'package:stackwallet/models/exchange/change_now/available_floating_rate_pair.dart';
|
|
|
|
import 'package:stackwallet/models/exchange/change_now/change_now_response.dart';
|
2022-09-08 16:56:29 +00:00
|
|
|
import 'package:stackwallet/models/exchange/change_now/cn_exchange_estimate.dart';
|
2022-08-26 08:11:35 +00:00
|
|
|
import 'package:stackwallet/models/exchange/change_now/currency.dart';
|
|
|
|
import 'package:stackwallet/models/exchange/change_now/estimated_exchange_amount.dart';
|
|
|
|
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/utilities/logger.dart';
|
|
|
|
|
2022-08-30 22:38:07 +00:00
|
|
|
class ChangeNow {
|
2022-08-26 08:11:35 +00:00
|
|
|
static const String scheme = "https";
|
|
|
|
static const String authority = "api.changenow.io";
|
|
|
|
static const String apiVersion = "/v1";
|
2022-09-08 16:56:29 +00:00
|
|
|
static const String apiVersionV2 = "/v2";
|
2022-08-26 08:11:35 +00:00
|
|
|
|
2022-08-30 22:38:07 +00:00
|
|
|
ChangeNow._();
|
|
|
|
static final ChangeNow _instance = ChangeNow._();
|
|
|
|
static ChangeNow get instance => _instance;
|
|
|
|
|
2022-08-29 20:47:38 +00:00
|
|
|
/// set this to override using standard http client. Useful for testing
|
2022-08-30 22:38:07 +00:00
|
|
|
http.Client? client;
|
2022-08-29 20:47:38 +00:00
|
|
|
|
2022-08-30 22:38:07 +00:00
|
|
|
Uri _buildUri(String path, Map<String, dynamic>? params) {
|
2022-08-26 08:11:35 +00:00
|
|
|
return Uri.https(authority, apiVersion + path, params);
|
|
|
|
}
|
|
|
|
|
2022-09-08 16:56:29 +00:00
|
|
|
Uri _buildUriV2(String path, Map<String, dynamic>? params) {
|
|
|
|
return Uri.https(authority, apiVersionV2 + path, params);
|
|
|
|
}
|
|
|
|
|
2022-08-30 22:38:07 +00:00
|
|
|
Future<dynamic> _makeGetRequest(Uri uri) async {
|
|
|
|
final client = this.client ?? http.Client();
|
2022-08-26 08:11:35 +00:00
|
|
|
try {
|
|
|
|
final response = await client.get(
|
|
|
|
uri,
|
|
|
|
headers: {'Content-Type': 'application/json'},
|
|
|
|
);
|
|
|
|
|
|
|
|
final parsed = jsonDecode(response.body);
|
|
|
|
|
|
|
|
return parsed;
|
|
|
|
} catch (e, s) {
|
|
|
|
Logging.instance
|
|
|
|
.log("_makeRequest($uri) threw: $e\n$s", level: LogLevel.Error);
|
|
|
|
rethrow;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-09-08 16:56:29 +00:00
|
|
|
Future<dynamic> _makeGetRequestV2(Uri uri, String apiKey) async {
|
|
|
|
final client = this.client ?? http.Client();
|
|
|
|
try {
|
|
|
|
final response = await client.get(
|
|
|
|
uri,
|
|
|
|
headers: {
|
|
|
|
// 'Content-Type': 'application/json',
|
|
|
|
'x-changenow-api-key': apiKey,
|
|
|
|
},
|
|
|
|
);
|
|
|
|
|
|
|
|
final parsed = jsonDecode(response.body);
|
|
|
|
|
|
|
|
return parsed;
|
|
|
|
} catch (e, s) {
|
|
|
|
Logging.instance
|
|
|
|
.log("_makeRequestV2($uri) threw: $e\n$s", level: LogLevel.Error);
|
|
|
|
rethrow;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-08-30 22:38:07 +00:00
|
|
|
Future<dynamic> _makePostRequest(
|
2022-08-26 08:11:35 +00:00
|
|
|
Uri uri,
|
|
|
|
Map<String, String> body,
|
|
|
|
) async {
|
2022-08-30 22:38:07 +00:00
|
|
|
final client = this.client ?? http.Client();
|
2022-08-26 08:11:35 +00:00
|
|
|
try {
|
|
|
|
final response = await client.post(
|
|
|
|
uri,
|
|
|
|
headers: {'Content-Type': 'application/json'},
|
|
|
|
body: jsonEncode(body),
|
|
|
|
);
|
|
|
|
|
|
|
|
final parsed = jsonDecode(response.body);
|
|
|
|
|
|
|
|
return parsed;
|
|
|
|
} catch (e, s) {
|
|
|
|
Logging.instance
|
|
|
|
.log("_makeRequest($uri) threw: $e\n$s", level: LogLevel.Error);
|
|
|
|
rethrow;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// This API endpoint returns the list of available currencies.
|
|
|
|
///
|
|
|
|
/// Set [active] to true to return only active currencies.
|
|
|
|
/// Set [fixedRate] to true to return only currencies available on a fixed-rate flow.
|
2022-08-30 22:38:07 +00:00
|
|
|
Future<ChangeNowResponse<List<Currency>>> getAvailableCurrencies({
|
2022-08-26 08:11:35 +00:00
|
|
|
bool? fixedRate,
|
|
|
|
bool? active,
|
|
|
|
}) async {
|
|
|
|
Map<String, dynamic>? params;
|
|
|
|
|
|
|
|
if (active != null || fixedRate != null) {
|
|
|
|
params = {};
|
|
|
|
if (fixedRate != null) {
|
|
|
|
params.addAll({"fixedRate": fixedRate.toString()});
|
|
|
|
}
|
|
|
|
if (active != null) {
|
|
|
|
params.addAll({"active": active.toString()});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
final uri = _buildUri("/currencies", params);
|
|
|
|
|
|
|
|
try {
|
|
|
|
// json array is expected here
|
|
|
|
final jsonArray = await _makeGetRequest(uri);
|
|
|
|
|
|
|
|
try {
|
|
|
|
final result = await compute(
|
|
|
|
_parseAvailableCurrenciesJson, jsonArray as List<dynamic>);
|
|
|
|
return result;
|
|
|
|
} catch (e, s) {
|
|
|
|
Logging.instance.log("getAvailableCurrencies exception: $e\n$s",
|
|
|
|
level: LogLevel.Error);
|
|
|
|
return ChangeNowResponse(
|
|
|
|
exception: ChangeNowException(
|
|
|
|
"Error: $jsonArray",
|
|
|
|
ChangeNowExceptionType.serializeResponseError,
|
|
|
|
),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
} catch (e, s) {
|
|
|
|
Logging.instance.log("getAvailableCurrencies exception: $e\n$s",
|
|
|
|
level: LogLevel.Error);
|
|
|
|
return ChangeNowResponse(
|
|
|
|
exception: ChangeNowException(
|
|
|
|
e.toString(),
|
|
|
|
ChangeNowExceptionType.generic,
|
|
|
|
),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-08-30 22:38:07 +00:00
|
|
|
ChangeNowResponse<List<Currency>> _parseAvailableCurrenciesJson(
|
2022-08-26 08:11:35 +00:00
|
|
|
List<dynamic> jsonArray) {
|
|
|
|
try {
|
|
|
|
List<Currency> currencies = [];
|
|
|
|
|
|
|
|
for (final json in jsonArray) {
|
|
|
|
try {
|
|
|
|
currencies
|
|
|
|
.add(Currency.fromJson(Map<String, dynamic>.from(json as Map)));
|
|
|
|
} catch (_) {
|
|
|
|
return ChangeNowResponse(
|
2022-08-30 20:26:43 +00:00
|
|
|
exception: ChangeNowException("Failed to serialize $json",
|
|
|
|
ChangeNowExceptionType.serializeResponseError));
|
2022-08-26 08:11:35 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return ChangeNowResponse(value: currencies);
|
|
|
|
} catch (_) {
|
|
|
|
rethrow;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// This API endpoint returns the array of markets available for the specified currency be default.
|
|
|
|
/// The availability of a particular pair is determined by the 'isAvailable' field.
|
|
|
|
///
|
|
|
|
/// Required [ticker] to fetch paired currencies for.
|
|
|
|
/// Set [fixedRate] to true to return only currencies available on a fixed-rate flow.
|
2022-08-30 22:38:07 +00:00
|
|
|
Future<ChangeNowResponse<List<Currency>>> getPairedCurrencies({
|
2022-08-26 08:11:35 +00:00
|
|
|
required String ticker,
|
|
|
|
bool? fixedRate,
|
|
|
|
}) async {
|
|
|
|
Map<String, dynamic>? params;
|
|
|
|
|
|
|
|
if (fixedRate != null) {
|
|
|
|
params = {};
|
|
|
|
params.addAll({"fixedRate": fixedRate.toString()});
|
|
|
|
}
|
|
|
|
|
|
|
|
final uri = _buildUri("/currencies-to/$ticker", params);
|
|
|
|
|
|
|
|
try {
|
|
|
|
// json array is expected here
|
|
|
|
final jsonArray = (await _makeGetRequest(uri)) as List;
|
|
|
|
|
|
|
|
List<Currency> currencies = [];
|
|
|
|
try {
|
|
|
|
for (final json in jsonArray) {
|
|
|
|
try {
|
|
|
|
currencies
|
|
|
|
.add(Currency.fromJson(Map<String, dynamic>.from(json as Map)));
|
|
|
|
} catch (_) {
|
|
|
|
return ChangeNowResponse(
|
|
|
|
exception: ChangeNowException(
|
|
|
|
"Failed to serialize $json",
|
|
|
|
ChangeNowExceptionType.serializeResponseError,
|
|
|
|
),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} catch (e, s) {
|
|
|
|
Logging.instance.log("getPairedCurrencies exception: $e\n$s",
|
|
|
|
level: LogLevel.Error);
|
|
|
|
return ChangeNowResponse(
|
2022-08-30 20:26:43 +00:00
|
|
|
exception: ChangeNowException("Error: $jsonArray",
|
|
|
|
ChangeNowExceptionType.serializeResponseError));
|
2022-08-26 08:11:35 +00:00
|
|
|
}
|
|
|
|
return ChangeNowResponse(value: currencies);
|
|
|
|
} catch (e, s) {
|
|
|
|
Logging.instance
|
|
|
|
.log("getPairedCurrencies exception: $e\n$s", level: LogLevel.Error);
|
|
|
|
return ChangeNowResponse(
|
|
|
|
exception: ChangeNowException(
|
|
|
|
e.toString(),
|
|
|
|
ChangeNowExceptionType.generic,
|
|
|
|
),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// The API endpoint returns minimal payment amount required to make
|
|
|
|
/// an exchange of [fromTicker] to [toTicker].
|
|
|
|
/// If you try to exchange less, the transaction will most likely fail.
|
2022-08-30 22:38:07 +00:00
|
|
|
Future<ChangeNowResponse<Decimal>> getMinimalExchangeAmount({
|
2022-08-26 08:11:35 +00:00
|
|
|
required String fromTicker,
|
|
|
|
required String toTicker,
|
2022-08-29 21:20:27 +00:00
|
|
|
String? apiKey,
|
2022-08-26 08:11:35 +00:00
|
|
|
}) async {
|
2022-08-29 21:20:27 +00:00
|
|
|
Map<String, dynamic>? params = {"api_key": apiKey ?? kChangeNowApiKey};
|
2022-08-26 08:11:35 +00:00
|
|
|
|
|
|
|
final uri = _buildUri("/min-amount/${fromTicker}_$toTicker", params);
|
|
|
|
|
|
|
|
try {
|
|
|
|
// simple json object is expected here
|
|
|
|
final json = await _makeGetRequest(uri);
|
|
|
|
|
|
|
|
try {
|
|
|
|
final value = Decimal.parse(json["minAmount"].toString());
|
|
|
|
return ChangeNowResponse(value: value);
|
|
|
|
} catch (_) {
|
|
|
|
return ChangeNowResponse(
|
|
|
|
exception: ChangeNowException(
|
|
|
|
"Failed to serialize $json",
|
|
|
|
ChangeNowExceptionType.serializeResponseError,
|
|
|
|
),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
} catch (e, s) {
|
|
|
|
Logging.instance.log("getMinimalExchangeAmount exception: $e\n$s",
|
|
|
|
level: LogLevel.Error);
|
|
|
|
return ChangeNowResponse(
|
|
|
|
exception: ChangeNowException(
|
|
|
|
e.toString(),
|
|
|
|
ChangeNowExceptionType.generic,
|
|
|
|
),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Get estimated amount of [toTicker] cryptocurrency to receive
|
|
|
|
/// for [fromAmount] of [fromTicker]
|
2022-08-30 22:38:07 +00:00
|
|
|
Future<ChangeNowResponse<EstimatedExchangeAmount>>
|
2022-08-26 08:11:35 +00:00
|
|
|
getEstimatedExchangeAmount({
|
|
|
|
required String fromTicker,
|
|
|
|
required String toTicker,
|
|
|
|
required Decimal fromAmount,
|
2022-08-29 21:20:27 +00:00
|
|
|
String? apiKey,
|
2022-08-26 08:11:35 +00:00
|
|
|
}) async {
|
2022-08-29 21:20:27 +00:00
|
|
|
Map<String, dynamic> params = {"api_key": apiKey ?? kChangeNowApiKey};
|
2022-08-26 08:11:35 +00:00
|
|
|
|
|
|
|
final uri = _buildUri(
|
|
|
|
"/exchange-amount/${fromAmount.toString()}/${fromTicker}_$toTicker",
|
|
|
|
params,
|
|
|
|
);
|
|
|
|
|
|
|
|
try {
|
|
|
|
// simple json object is expected here
|
|
|
|
final json = await _makeGetRequest(uri);
|
|
|
|
|
|
|
|
try {
|
|
|
|
final value = EstimatedExchangeAmount.fromJson(
|
|
|
|
Map<String, dynamic>.from(json as Map));
|
|
|
|
return ChangeNowResponse(value: value);
|
|
|
|
} catch (_) {
|
|
|
|
return ChangeNowResponse(
|
|
|
|
exception: ChangeNowException(
|
|
|
|
"Failed to serialize $json",
|
|
|
|
ChangeNowExceptionType.serializeResponseError,
|
|
|
|
),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
} catch (e, s) {
|
|
|
|
Logging.instance.log("getEstimatedExchangeAmount exception: $e\n$s",
|
|
|
|
level: LogLevel.Error);
|
|
|
|
return ChangeNowResponse(
|
|
|
|
exception: ChangeNowException(
|
|
|
|
e.toString(),
|
|
|
|
ChangeNowExceptionType.generic,
|
|
|
|
),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-09-08 16:56:29 +00:00
|
|
|
// old v1 version
|
2022-08-26 08:11:35 +00:00
|
|
|
/// This API endpoint returns fixed-rate estimated exchange amount of
|
|
|
|
/// [toTicker] cryptocurrency to receive for [fromAmount] of [fromTicker]
|
2022-09-08 16:56:29 +00:00
|
|
|
// Future<ChangeNowResponse<EstimatedExchangeAmount>>
|
|
|
|
// getEstimatedFixedRateExchangeAmount({
|
|
|
|
// required String fromTicker,
|
|
|
|
// required String toTicker,
|
|
|
|
// required Decimal fromAmount,
|
|
|
|
// // (Optional) Use rateId for fixed-rate flow. If this field is true, you
|
|
|
|
// // could use returned field "rateId" in next method for creating transaction
|
|
|
|
// // to freeze estimated amount that you got in this method. Current estimated
|
|
|
|
// // amount would be valid until time in field "validUntil"
|
|
|
|
// bool useRateId = true,
|
|
|
|
// String? apiKey,
|
|
|
|
// }) async {
|
|
|
|
// Map<String, dynamic> params = {
|
|
|
|
// "api_key": apiKey ?? kChangeNowApiKey,
|
|
|
|
// "useRateId": useRateId.toString(),
|
|
|
|
// };
|
|
|
|
//
|
|
|
|
// final 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<String, dynamic>.from(json as Map));
|
|
|
|
// return ChangeNowResponse(value: value);
|
|
|
|
// } catch (_) {
|
|
|
|
// return ChangeNowResponse(
|
|
|
|
// exception: ChangeNowException(
|
|
|
|
// "Failed to serialize $json",
|
|
|
|
// ChangeNowExceptionType.serializeResponseError,
|
|
|
|
// ),
|
|
|
|
// );
|
|
|
|
// }
|
|
|
|
// } catch (e, s) {
|
|
|
|
// Logging.instance.log(
|
|
|
|
// "getEstimatedFixedRateExchangeAmount exception: $e\n$s",
|
|
|
|
// level: LogLevel.Error);
|
|
|
|
// return ChangeNowResponse(
|
|
|
|
// exception: ChangeNowException(
|
|
|
|
// e.toString(),
|
|
|
|
// ChangeNowExceptionType.generic,
|
|
|
|
// ),
|
|
|
|
// );
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
|
|
|
|
/// Get estimated amount of [toTicker] cryptocurrency to receive
|
|
|
|
/// for [fromAmount] of [fromTicker]
|
|
|
|
Future<ChangeNowResponse<CNExchangeEstimate>> getEstimatedExchangeAmountV2({
|
2022-08-26 08:11:35 +00:00
|
|
|
required String fromTicker,
|
|
|
|
required String toTicker,
|
2022-09-08 16:56:29 +00:00
|
|
|
required CNEstimateType fromOrTo,
|
|
|
|
required Decimal amount,
|
|
|
|
String? fromNetwork,
|
|
|
|
String? toNetwork,
|
|
|
|
CNFlowType flow = CNFlowType.standard,
|
2022-08-29 21:20:27 +00:00
|
|
|
String? apiKey,
|
2022-08-26 08:11:35 +00:00
|
|
|
}) async {
|
2022-09-08 16:56:29 +00:00
|
|
|
Map<String, dynamic>? params = {
|
|
|
|
"fromCurrency": fromTicker,
|
|
|
|
"toCurrency": toTicker,
|
|
|
|
"flow": flow.value,
|
|
|
|
"type": fromOrTo.name,
|
2022-08-26 08:11:35 +00:00
|
|
|
};
|
|
|
|
|
2022-09-08 16:56:29 +00:00
|
|
|
switch (fromOrTo) {
|
|
|
|
case CNEstimateType.direct:
|
|
|
|
params["fromAmount"] = amount.toString();
|
|
|
|
break;
|
|
|
|
case CNEstimateType.reverse:
|
|
|
|
params["toAmount"] = amount.toString();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (fromNetwork != null) {
|
|
|
|
params["fromNetwork"] = fromNetwork;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (toNetwork != null) {
|
|
|
|
params["toNetwork"] = toNetwork;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (flow == CNFlowType.fixedRate) {
|
|
|
|
params["useRateId"] = "true";
|
|
|
|
}
|
|
|
|
|
|
|
|
final uri = _buildUriV2("/exchange/estimated-amount", params);
|
2022-08-26 08:11:35 +00:00
|
|
|
|
|
|
|
try {
|
|
|
|
// simple json object is expected here
|
2022-09-08 16:56:29 +00:00
|
|
|
final json = await _makeGetRequestV2(uri, apiKey ?? kChangeNowApiKey);
|
2022-08-26 08:11:35 +00:00
|
|
|
|
|
|
|
try {
|
2022-09-08 16:56:29 +00:00
|
|
|
final value =
|
|
|
|
CNExchangeEstimate.fromJson(Map<String, dynamic>.from(json as Map));
|
2022-08-26 08:11:35 +00:00
|
|
|
return ChangeNowResponse(value: value);
|
|
|
|
} catch (_) {
|
|
|
|
return ChangeNowResponse(
|
|
|
|
exception: ChangeNowException(
|
|
|
|
"Failed to serialize $json",
|
|
|
|
ChangeNowExceptionType.serializeResponseError,
|
|
|
|
),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
} catch (e, s) {
|
2022-09-08 16:56:29 +00:00
|
|
|
Logging.instance.log("getEstimatedExchangeAmountV2 exception: $e\n$s",
|
2022-08-26 08:11:35 +00:00
|
|
|
level: LogLevel.Error);
|
|
|
|
return ChangeNowResponse(
|
|
|
|
exception: ChangeNowException(
|
|
|
|
e.toString(),
|
|
|
|
ChangeNowExceptionType.generic,
|
|
|
|
),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// This API endpoint returns the list of all the pairs available on a
|
|
|
|
/// fixed-rate flow. Some currencies get enabled or disabled from time to
|
|
|
|
/// time and the market info gets updates, so make sure to refresh the list
|
|
|
|
/// occasionally. One time per minute is sufficient.
|
2022-08-30 22:38:07 +00:00
|
|
|
Future<ChangeNowResponse<List<FixedRateMarket>>>
|
2022-08-29 20:47:38 +00:00
|
|
|
getAvailableFixedRateMarkets({
|
2022-08-29 21:20:27 +00:00
|
|
|
String? apiKey,
|
2022-08-29 20:47:38 +00:00
|
|
|
}) async {
|
2022-08-29 21:20:27 +00:00
|
|
|
final uri = _buildUri(
|
|
|
|
"/market-info/fixed-rate/${apiKey ?? kChangeNowApiKey}", null);
|
2022-08-26 08:11:35 +00:00
|
|
|
|
|
|
|
try {
|
|
|
|
// json array is expected here
|
|
|
|
final jsonArray = await _makeGetRequest(uri);
|
|
|
|
|
|
|
|
try {
|
|
|
|
final result =
|
|
|
|
await compute(_parseFixedRateMarketsJson, jsonArray as List);
|
|
|
|
return result;
|
|
|
|
} catch (e, s) {
|
|
|
|
Logging.instance.log("getAvailableFixedRateMarkets exception: $e\n$s",
|
|
|
|
level: LogLevel.Error);
|
|
|
|
return ChangeNowResponse(
|
|
|
|
exception: ChangeNowException(
|
|
|
|
"Error: $jsonArray",
|
|
|
|
ChangeNowExceptionType.serializeResponseError,
|
|
|
|
),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
} catch (e, s) {
|
|
|
|
Logging.instance.log("getAvailableFixedRateMarkets exception: $e\n$s",
|
|
|
|
level: LogLevel.Error);
|
|
|
|
return ChangeNowResponse(
|
|
|
|
exception: ChangeNowException(
|
|
|
|
e.toString(),
|
|
|
|
ChangeNowExceptionType.generic,
|
|
|
|
),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-08-30 22:38:07 +00:00
|
|
|
ChangeNowResponse<List<FixedRateMarket>> _parseFixedRateMarketsJson(
|
2022-08-26 08:11:35 +00:00
|
|
|
List<dynamic> jsonArray) {
|
|
|
|
try {
|
|
|
|
List<FixedRateMarket> markets = [];
|
|
|
|
for (final json in jsonArray) {
|
|
|
|
try {
|
|
|
|
markets.add(
|
|
|
|
FixedRateMarket.fromJson(Map<String, dynamic>.from(json as Map)));
|
|
|
|
} catch (_) {
|
|
|
|
return ChangeNowResponse(
|
2022-08-30 20:26:43 +00:00
|
|
|
exception: ChangeNowException("Failed to serialize $json",
|
|
|
|
ChangeNowExceptionType.serializeResponseError));
|
2022-08-26 08:11:35 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return ChangeNowResponse(value: markets);
|
|
|
|
} catch (_) {
|
|
|
|
rethrow;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// The API endpoint creates a transaction, generates an address for
|
|
|
|
/// sending funds and returns transaction attributes.
|
2022-08-30 22:38:07 +00:00
|
|
|
Future<ChangeNowResponse<ExchangeTransaction>>
|
2022-08-26 08:11:35 +00:00
|
|
|
createStandardExchangeTransaction({
|
|
|
|
required String fromTicker,
|
|
|
|
required String toTicker,
|
|
|
|
required String receivingAddress,
|
|
|
|
required Decimal amount,
|
|
|
|
String extraId = "",
|
|
|
|
String userId = "",
|
|
|
|
String contactEmail = "",
|
|
|
|
String refundAddress = "",
|
|
|
|
String refundExtraId = "",
|
2022-08-29 21:20:27 +00:00
|
|
|
String? apiKey,
|
2022-08-26 08:11:35 +00:00
|
|
|
}) async {
|
|
|
|
final Map<String, String> map = {
|
|
|
|
"from": fromTicker,
|
|
|
|
"to": toTicker,
|
|
|
|
"address": receivingAddress,
|
|
|
|
"amount": amount.toString(),
|
|
|
|
"flow": "standard",
|
|
|
|
"extraId": extraId,
|
|
|
|
"userId": userId,
|
|
|
|
"contactEmail": contactEmail,
|
|
|
|
"refundAddress": refundAddress,
|
|
|
|
"refundExtraId": refundExtraId,
|
|
|
|
};
|
|
|
|
|
2022-08-29 21:20:27 +00:00
|
|
|
final uri = _buildUri("/transactions/${apiKey ?? kChangeNowApiKey}", null);
|
2022-08-26 08:11:35 +00:00
|
|
|
|
|
|
|
try {
|
|
|
|
// simple json object is expected here
|
|
|
|
final json = await _makePostRequest(uri, map);
|
|
|
|
|
|
|
|
// pass in date to prevent using default 1970 date
|
2022-08-29 20:47:38 +00:00
|
|
|
json["date"] = DateTime.now().toString();
|
2022-08-26 08:11:35 +00:00
|
|
|
|
|
|
|
try {
|
|
|
|
final value = ExchangeTransaction.fromJson(
|
|
|
|
Map<String, dynamic>.from(json as Map));
|
|
|
|
return ChangeNowResponse(value: value);
|
|
|
|
} catch (_) {
|
|
|
|
return ChangeNowResponse(
|
|
|
|
exception: ChangeNowException(
|
|
|
|
"Failed to serialize $json",
|
|
|
|
ChangeNowExceptionType.serializeResponseError,
|
|
|
|
),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
} catch (e, s) {
|
|
|
|
Logging.instance.log(
|
|
|
|
"createStandardExchangeTransaction exception: $e\n$s",
|
|
|
|
level: LogLevel.Error);
|
|
|
|
return ChangeNowResponse(
|
|
|
|
exception: ChangeNowException(
|
|
|
|
e.toString(),
|
|
|
|
ChangeNowExceptionType.generic,
|
|
|
|
),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// The API endpoint creates a transaction, generates an address for
|
|
|
|
/// sending funds and returns transaction attributes.
|
2022-08-30 22:38:07 +00:00
|
|
|
Future<ChangeNowResponse<ExchangeTransaction>>
|
2022-08-26 08:11:35 +00:00
|
|
|
createFixedRateExchangeTransaction({
|
|
|
|
required String fromTicker,
|
|
|
|
required String toTicker,
|
|
|
|
required String receivingAddress,
|
|
|
|
required Decimal amount,
|
|
|
|
required String rateId,
|
|
|
|
String extraId = "",
|
|
|
|
String userId = "",
|
|
|
|
String contactEmail = "",
|
|
|
|
String refundAddress = "",
|
|
|
|
String refundExtraId = "",
|
2022-08-29 21:20:27 +00:00
|
|
|
String? apiKey,
|
2022-08-26 08:11:35 +00:00
|
|
|
}) async {
|
|
|
|
final Map<String, String> map = {
|
|
|
|
"from": fromTicker,
|
|
|
|
"to": toTicker,
|
|
|
|
"address": receivingAddress,
|
|
|
|
"amount": amount.toString(),
|
|
|
|
"flow": "fixed-rate",
|
|
|
|
"extraId": extraId,
|
|
|
|
"userId": userId,
|
|
|
|
"contactEmail": contactEmail,
|
|
|
|
"refundAddress": refundAddress,
|
|
|
|
"refundExtraId": refundExtraId,
|
|
|
|
"rateId": rateId,
|
|
|
|
};
|
|
|
|
|
2022-08-29 21:20:27 +00:00
|
|
|
final uri = _buildUri(
|
|
|
|
"/transactions/fixed-rate/${apiKey ?? kChangeNowApiKey}", null);
|
2022-08-26 08:11:35 +00:00
|
|
|
|
|
|
|
try {
|
|
|
|
// simple json object is expected here
|
|
|
|
final json = await _makePostRequest(uri, map);
|
|
|
|
|
|
|
|
// pass in date to prevent using default 1970 date
|
2022-08-29 20:47:38 +00:00
|
|
|
json["date"] = DateTime.now().toString();
|
2022-08-26 08:11:35 +00:00
|
|
|
|
|
|
|
try {
|
|
|
|
final value = ExchangeTransaction.fromJson(
|
|
|
|
Map<String, dynamic>.from(json as Map));
|
|
|
|
return ChangeNowResponse(value: value);
|
|
|
|
} catch (_) {
|
|
|
|
return ChangeNowResponse(
|
|
|
|
exception: ChangeNowException(
|
|
|
|
"Failed to serialize $json",
|
|
|
|
ChangeNowExceptionType.serializeResponseError,
|
|
|
|
),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
} catch (e, s) {
|
|
|
|
Logging.instance.log(
|
|
|
|
"createFixedRateExchangeTransaction exception: $e\n$s",
|
|
|
|
level: LogLevel.Error);
|
|
|
|
return ChangeNowResponse(
|
|
|
|
exception: ChangeNowException(
|
|
|
|
e.toString(),
|
|
|
|
ChangeNowExceptionType.generic,
|
|
|
|
),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-08-30 22:38:07 +00:00
|
|
|
Future<ChangeNowResponse<ExchangeTransactionStatus>> getTransactionStatus({
|
2022-08-26 08:11:35 +00:00
|
|
|
required String id,
|
2022-08-29 21:20:27 +00:00
|
|
|
String? apiKey,
|
2022-08-26 08:11:35 +00:00
|
|
|
}) async {
|
2022-08-29 21:20:27 +00:00
|
|
|
final uri =
|
|
|
|
_buildUri("/transactions/$id/${apiKey ?? kChangeNowApiKey}", null);
|
2022-08-26 08:11:35 +00:00
|
|
|
|
|
|
|
try {
|
|
|
|
// simple json object is expected here
|
|
|
|
final json = await _makeGetRequest(uri);
|
|
|
|
|
|
|
|
try {
|
|
|
|
final value = ExchangeTransactionStatus.fromJson(
|
|
|
|
Map<String, dynamic>.from(json as Map));
|
|
|
|
return ChangeNowResponse(value: value);
|
|
|
|
} catch (_) {
|
|
|
|
return ChangeNowResponse(
|
|
|
|
exception: ChangeNowException(
|
|
|
|
"Failed to serialize $json",
|
|
|
|
ChangeNowExceptionType.serializeResponseError,
|
|
|
|
),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
} catch (e, s) {
|
|
|
|
Logging.instance
|
|
|
|
.log("getTransactionStatus exception: $e\n$s", level: LogLevel.Error);
|
|
|
|
return ChangeNowResponse(
|
|
|
|
exception: ChangeNowException(
|
|
|
|
e.toString(),
|
|
|
|
ChangeNowExceptionType.generic,
|
|
|
|
),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-08-30 22:38:07 +00:00
|
|
|
Future<ChangeNowResponse<List<AvailableFloatingRatePair>>>
|
2022-08-26 08:11:35 +00:00
|
|
|
getAvailableFloatingRatePairs({
|
|
|
|
bool includePartners = false,
|
|
|
|
}) async {
|
|
|
|
final uri = _buildUri("/market-info/available-pairs",
|
|
|
|
{"includePartners": includePartners.toString()});
|
|
|
|
|
|
|
|
try {
|
|
|
|
// json array is expected here
|
|
|
|
final jsonArray = await _makeGetRequest(uri);
|
|
|
|
|
|
|
|
try {
|
|
|
|
final result = await compute(
|
|
|
|
_parseAvailableFloatingRatePairsJson, jsonArray as List);
|
|
|
|
return result;
|
|
|
|
} catch (e, s) {
|
|
|
|
Logging.instance.log("getAvailableFloatingRatePairs exception: $e\n$s",
|
|
|
|
level: LogLevel.Error);
|
|
|
|
return ChangeNowResponse(
|
|
|
|
exception: ChangeNowException(
|
|
|
|
"Error: $jsonArray",
|
|
|
|
ChangeNowExceptionType.serializeResponseError,
|
|
|
|
),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
} catch (e, s) {
|
|
|
|
Logging.instance.log("getAvailableFloatingRatePairs exception: $e\n$s",
|
|
|
|
level: LogLevel.Error);
|
|
|
|
return ChangeNowResponse(
|
|
|
|
exception: ChangeNowException(
|
|
|
|
e.toString(),
|
|
|
|
ChangeNowExceptionType.generic,
|
|
|
|
),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-08-30 22:38:07 +00:00
|
|
|
ChangeNowResponse<List<AvailableFloatingRatePair>>
|
2022-08-26 08:11:35 +00:00
|
|
|
_parseAvailableFloatingRatePairsJson(List<dynamic> jsonArray) {
|
|
|
|
try {
|
|
|
|
List<AvailableFloatingRatePair> pairs = [];
|
|
|
|
for (final json in jsonArray) {
|
|
|
|
try {
|
|
|
|
final List<String> stringPair = (json as String).split("_");
|
|
|
|
pairs.add(AvailableFloatingRatePair(
|
|
|
|
fromTicker: stringPair[0], toTicker: stringPair[1]));
|
|
|
|
} catch (_) {
|
|
|
|
return ChangeNowResponse(
|
2022-08-30 20:26:43 +00:00
|
|
|
exception: ChangeNowException("Failed to serialize $json",
|
|
|
|
ChangeNowExceptionType.serializeResponseError));
|
2022-08-26 08:11:35 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return ChangeNowResponse(value: pairs);
|
|
|
|
} catch (_) {
|
|
|
|
rethrow;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|