mirror of
https://github.com/Such-Software/hash-wallet.git
synced 2026-10-19 05:58:50 +02:00
Cw 473 exchange refactoring (#1126)
* CW-473 Remove deprecated Exchanges * CW-473 Clean up Exchange Code * CW-473 Add Decimals to Crypto Enums * CW-473 Fix minor merge error * CW-473 Fix minor merge error * CW-473 Fix FAQ Locals * CW-473 Apply requested changes * CW-473 Show Error if Exchange Provider is not supported anymore * CW-473 Implement Requested Changes
This commit is contained in:
@@ -1,22 +0,0 @@
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
import 'package:cake_wallet/exchange/trade_request.dart';
|
||||
|
||||
class ChangeNowRequest extends TradeRequest {
|
||||
ChangeNowRequest(
|
||||
{required this.from,
|
||||
required this.to,
|
||||
required this.address,
|
||||
required this.fromAmount,
|
||||
required this.toAmount,
|
||||
required this.refundAddress,
|
||||
required this.isReverse});
|
||||
|
||||
CryptoCurrency from;
|
||||
CryptoCurrency to;
|
||||
String address;
|
||||
String fromAmount;
|
||||
String toAmount;
|
||||
String refundAddress;
|
||||
bool isReverse;
|
||||
}
|
||||
@@ -1,10 +1,7 @@
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
|
||||
class ExchangePair {
|
||||
ExchangePair({
|
||||
required this.from,
|
||||
required this.to,
|
||||
this.reverse = true});
|
||||
ExchangePair({required this.from, required this.to, this.reverse = true});
|
||||
|
||||
final CryptoCurrency from;
|
||||
final CryptoCurrency to;
|
||||
|
||||
@@ -14,16 +14,12 @@ class ExchangeProviderDescription extends EnumerableItem<int> with Serializable<
|
||||
ExchangeProviderDescription(title: 'ChangeNOW', raw: 1, image: 'assets/images/changenow.png');
|
||||
static const morphToken =
|
||||
ExchangeProviderDescription(title: 'MorphToken', raw: 2, image: 'assets/images/morph.png');
|
||||
|
||||
static const sideShift =
|
||||
ExchangeProviderDescription(title: 'SideShift', raw: 3, image: 'assets/images/sideshift.png');
|
||||
|
||||
static const simpleSwap = ExchangeProviderDescription(
|
||||
title: 'SimpleSwap', raw: 4, image: 'assets/images/simpleSwap.png');
|
||||
|
||||
static const trocador =
|
||||
ExchangeProviderDescription(title: 'Trocador', raw: 5, image: 'assets/images/trocador.png');
|
||||
|
||||
static const exolix =
|
||||
ExchangeProviderDescription(title: 'Exolix', raw: 6, image: 'assets/images/exolix.png');
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:cake_wallet/exchange/trade.dart';
|
||||
|
||||
abstract class ExchangeTradeState {}
|
||||
@@ -18,4 +17,4 @@ class TradeIsCreatedFailure extends ExchangeTradeState {
|
||||
|
||||
final String title;
|
||||
final String error;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
import 'package:cake_wallet/exchange/trade_request.dart';
|
||||
|
||||
class ExolixRequest extends TradeRequest {
|
||||
ExolixRequest(
|
||||
{required this.from,
|
||||
required this.to,
|
||||
required this.address,
|
||||
required this.fromAmount,
|
||||
required this.toAmount,
|
||||
required this.refundAddress});
|
||||
|
||||
CryptoCurrency from;
|
||||
CryptoCurrency to;
|
||||
String address;
|
||||
String fromAmount;
|
||||
String toAmount;
|
||||
String refundAddress;
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
class Limits {
|
||||
const Limits({this.min, this.max});
|
||||
|
||||
|
||||
final double? min;
|
||||
final double? max;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:cake_wallet/exchange/limits.dart';
|
||||
|
||||
abstract class LimitsState {}
|
||||
|
||||
@@ -1,225 +0,0 @@
|
||||
import 'dart:convert';
|
||||
import 'package:cw_core/amount_converter.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:cake_wallet/exchange/trade_not_found_exeption.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:http/http.dart';
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
import 'package:cake_wallet/exchange/exchange_pair.dart';
|
||||
import 'package:cake_wallet/exchange/exchange_provider.dart';
|
||||
import 'package:cake_wallet/exchange/limits.dart';
|
||||
import 'package:cake_wallet/exchange/trade.dart';
|
||||
import 'package:cake_wallet/exchange/trade_request.dart';
|
||||
import 'package:cake_wallet/exchange/trade_state.dart';
|
||||
import 'package:cake_wallet/exchange/morphtoken/morphtoken_request.dart';
|
||||
import 'package:cake_wallet/exchange/exchange_provider_description.dart';
|
||||
import 'package:cake_wallet/exchange/trade_not_created_exeption.dart';
|
||||
|
||||
class MorphTokenExchangeProvider extends ExchangeProvider {
|
||||
MorphTokenExchangeProvider({required this.trades})
|
||||
: super(pairList: [
|
||||
ExchangePair(from: CryptoCurrency.xmr, to: CryptoCurrency.eth),
|
||||
ExchangePair(from: CryptoCurrency.xmr, to: CryptoCurrency.bch),
|
||||
ExchangePair(from: CryptoCurrency.xmr, to: CryptoCurrency.ltc),
|
||||
ExchangePair(from: CryptoCurrency.xmr, to: CryptoCurrency.dash),
|
||||
ExchangePair(from: CryptoCurrency.dash, to: CryptoCurrency.btc),
|
||||
ExchangePair(from: CryptoCurrency.dash, to: CryptoCurrency.eth),
|
||||
ExchangePair(from: CryptoCurrency.dash, to: CryptoCurrency.bch),
|
||||
ExchangePair(from: CryptoCurrency.dash, to: CryptoCurrency.ltc),
|
||||
ExchangePair(from: CryptoCurrency.dash, to: CryptoCurrency.xmr),
|
||||
ExchangePair(from: CryptoCurrency.ltc, to: CryptoCurrency.btc),
|
||||
ExchangePair(from: CryptoCurrency.ltc, to: CryptoCurrency.eth),
|
||||
ExchangePair(from: CryptoCurrency.ltc, to: CryptoCurrency.bch),
|
||||
ExchangePair(from: CryptoCurrency.ltc, to: CryptoCurrency.dash),
|
||||
ExchangePair(from: CryptoCurrency.ltc, to: CryptoCurrency.xmr),
|
||||
ExchangePair(from: CryptoCurrency.bch, to: CryptoCurrency.btc),
|
||||
ExchangePair(from: CryptoCurrency.bch, to: CryptoCurrency.eth),
|
||||
ExchangePair(from: CryptoCurrency.bch, to: CryptoCurrency.ltc),
|
||||
ExchangePair(from: CryptoCurrency.bch, to: CryptoCurrency.dash),
|
||||
ExchangePair(from: CryptoCurrency.bch, to: CryptoCurrency.xmr),
|
||||
ExchangePair(from: CryptoCurrency.eth, to: CryptoCurrency.btc),
|
||||
ExchangePair(from: CryptoCurrency.eth, to: CryptoCurrency.bch),
|
||||
ExchangePair(from: CryptoCurrency.eth, to: CryptoCurrency.ltc),
|
||||
ExchangePair(from: CryptoCurrency.eth, to: CryptoCurrency.dash),
|
||||
ExchangePair(from: CryptoCurrency.eth, to: CryptoCurrency.xmr),
|
||||
ExchangePair(from: CryptoCurrency.btc, to: CryptoCurrency.eth),
|
||||
ExchangePair(from: CryptoCurrency.btc, to: CryptoCurrency.bch),
|
||||
ExchangePair(from: CryptoCurrency.btc, to: CryptoCurrency.ltc),
|
||||
ExchangePair(from: CryptoCurrency.btc, to: CryptoCurrency.dash),
|
||||
ExchangePair(from: CryptoCurrency.btc, to: CryptoCurrency.xmr)
|
||||
]);
|
||||
|
||||
Box<Trade> trades;
|
||||
|
||||
static const apiUri = 'https://api.morphtoken.com';
|
||||
static const _morphURISuffix = '/morph';
|
||||
static const _limitsURISuffix = '/limits';
|
||||
static const _ratesURISuffix = '/rates';
|
||||
static const weight = 10000;
|
||||
|
||||
@override
|
||||
String get title => 'MorphToken';
|
||||
|
||||
@override
|
||||
bool get isAvailable => true;
|
||||
|
||||
@override
|
||||
bool get isEnabled => true;
|
||||
|
||||
@override
|
||||
bool get supportsFixedRate => false;
|
||||
|
||||
@override
|
||||
ExchangeProviderDescription get description =>
|
||||
ExchangeProviderDescription.morphToken;
|
||||
|
||||
@override
|
||||
Future<bool> checkIsAvailable() async => true;
|
||||
|
||||
@override
|
||||
Future<Limits> fetchLimits({
|
||||
required CryptoCurrency from,
|
||||
required CryptoCurrency to,
|
||||
required bool isFixedRateMode}) async {
|
||||
final url = apiUri + _limitsURISuffix;
|
||||
final uri = Uri.parse(url);
|
||||
final headers = {'Content-type': 'application/json'};
|
||||
final body = json.encode({
|
||||
"input": {"asset": from.toString()},
|
||||
"output": [
|
||||
{"asset": to.toString(), "weight": weight}
|
||||
]
|
||||
});
|
||||
final response = await post(uri, headers: headers, body: body);
|
||||
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
|
||||
|
||||
final min = responseJSON['input']['limits']['min'] as int;
|
||||
int max = 0;
|
||||
double ethMax;
|
||||
|
||||
if (from == CryptoCurrency.eth) {
|
||||
ethMax = responseJSON['input']['limits']['max'] as double;
|
||||
} else {
|
||||
max = responseJSON['input']['limits']['max'] as int;
|
||||
}
|
||||
|
||||
final minFormatted = AmountConverter.amountIntToDouble(from, min);
|
||||
final maxFormatted = AmountConverter.amountIntToDouble(from, max);
|
||||
|
||||
return Limits(min: minFormatted, max: maxFormatted);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Trade> createTrade({
|
||||
required TradeRequest request,
|
||||
required bool isFixedRateMode}) async {
|
||||
const url = apiUri + _morphURISuffix;
|
||||
final _request = request as MorphTokenRequest;
|
||||
final body = {
|
||||
"input": {
|
||||
"asset": _request.from.toString(),
|
||||
"refund": _request.refundAddress
|
||||
},
|
||||
"output": [
|
||||
{
|
||||
"asset": _request.to.toString(),
|
||||
"weight": weight,
|
||||
"address": _request.address
|
||||
}
|
||||
],
|
||||
"tag": "cakewallet"
|
||||
};
|
||||
final uri = Uri.parse(url);
|
||||
final response = await post(uri,
|
||||
headers: {'Content-Type': 'application/json'}, body: json.encode(body));
|
||||
|
||||
if (response.statusCode != 200) {
|
||||
if (response.statusCode == 400) {
|
||||
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
|
||||
final error = responseJSON['description'] as String;
|
||||
|
||||
throw TradeNotCreatedException(description, description: error);
|
||||
}
|
||||
|
||||
throw TradeNotCreatedException(description);
|
||||
}
|
||||
|
||||
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
|
||||
final id = responseJSON['id'] as String;
|
||||
|
||||
return Trade(
|
||||
id: id,
|
||||
provider: description,
|
||||
from: _request.from,
|
||||
to: _request.to,
|
||||
state: TradeState.created,
|
||||
amount: _request.amount,
|
||||
createdAt: DateTime.now());
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Trade> findTradeById({required String id}) async {
|
||||
final url = apiUri + _morphURISuffix + '/' + id;
|
||||
final uri = Uri.parse(url);
|
||||
final response = await get(uri);
|
||||
|
||||
if (response.statusCode != 200) {
|
||||
if (response.statusCode == 400) {
|
||||
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
|
||||
final error = responseJSON['description'] as String;
|
||||
|
||||
throw TradeNotFoundException(id,
|
||||
provider: description, description: error);
|
||||
}
|
||||
|
||||
throw TradeNotFoundException(id, provider: description);
|
||||
}
|
||||
|
||||
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
|
||||
final fromCurrency = responseJSON['input']['asset'] as String;
|
||||
final from = CryptoCurrency.fromString(fromCurrency.toLowerCase());
|
||||
final toCurrency = responseJSON['output'][0]['asset'] as String;
|
||||
final to = CryptoCurrency.fromString(toCurrency.toLowerCase());
|
||||
final inputAddress = responseJSON['input']['deposit_address'] as String;
|
||||
final status = responseJSON['state'] as String;
|
||||
final state = TradeState.deserialize(raw: status.toLowerCase());
|
||||
|
||||
String amount = "";
|
||||
for (final trade in trades.values) {
|
||||
if (trade.id == id) {
|
||||
amount = trade.amount;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return Trade(
|
||||
id: id,
|
||||
from: from,
|
||||
to: to,
|
||||
provider: description,
|
||||
inputAddress: inputAddress,
|
||||
amount: amount,
|
||||
state: state);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<double> fetchRate(
|
||||
{required CryptoCurrency from,
|
||||
required CryptoCurrency to,
|
||||
required double amount,
|
||||
required bool isFixedRateMode,
|
||||
required bool isReceiveAmount}) async {
|
||||
final url = apiUri + _ratesURISuffix;
|
||||
final uri = Uri.parse(url);
|
||||
final response = await get(uri);
|
||||
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
|
||||
final rate = responseJSON['data'][from.toString()][to.toString()] as String;
|
||||
|
||||
try {
|
||||
final estimatedAmount = double.parse(rate) * amount;
|
||||
return estimatedAmount;
|
||||
} catch (_) {
|
||||
return 0.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
import 'package:cake_wallet/exchange/trade_request.dart';
|
||||
|
||||
class MorphTokenRequest extends TradeRequest {
|
||||
MorphTokenRequest(
|
||||
{required this.from,
|
||||
required this.to,
|
||||
required this.address,
|
||||
required this.amount,
|
||||
required this.refundAddress});
|
||||
|
||||
CryptoCurrency from;
|
||||
CryptoCurrency to;
|
||||
String address;
|
||||
String amount;
|
||||
String refundAddress;
|
||||
}
|
||||
+89
-105
@@ -1,33 +1,32 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
import 'package:cake_wallet/exchange/trade_not_found_exeption.dart';
|
||||
|
||||
import 'package:cake_wallet/.secrets.g.dart' as secrets;
|
||||
import 'package:cake_wallet/exchange/exchange_provider_description.dart';
|
||||
import 'package:cake_wallet/exchange/limits.dart';
|
||||
import 'package:cake_wallet/exchange/provider/exchange_provider.dart';
|
||||
import 'package:cake_wallet/exchange/trade.dart';
|
||||
import 'package:cake_wallet/exchange/trade_not_found_exception.dart';
|
||||
import 'package:cake_wallet/exchange/trade_request.dart';
|
||||
import 'package:cake_wallet/exchange/trade_state.dart';
|
||||
import 'package:cake_wallet/exchange/utils/currency_pairs_utils.dart';
|
||||
import 'package:cake_wallet/store/settings_store.dart';
|
||||
import 'package:cake_wallet/utils/device_info.dart';
|
||||
import 'package:cake_wallet/utils/distribution_info.dart';
|
||||
import 'package:cake_wallet/wallet_type_utils.dart';
|
||||
import 'package:http/http.dart';
|
||||
import 'package:cake_wallet/.secrets.g.dart' as secrets;
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
import 'package:cake_wallet/exchange/exchange_pair.dart';
|
||||
import 'package:cake_wallet/exchange/exchange_provider.dart';
|
||||
import 'package:cake_wallet/exchange/limits.dart';
|
||||
import 'package:cake_wallet/exchange/trade.dart';
|
||||
import 'package:cake_wallet/exchange/trade_request.dart';
|
||||
import 'package:cake_wallet/exchange/trade_state.dart';
|
||||
import 'package:cake_wallet/exchange/changenow/changenow_request.dart';
|
||||
import 'package:cake_wallet/exchange/exchange_provider_description.dart';
|
||||
import 'package:http/http.dart';
|
||||
|
||||
class ChangeNowExchangeProvider extends ExchangeProvider {
|
||||
ChangeNowExchangeProvider({required this.settingsStore})
|
||||
: _lastUsedRateId = '',
|
||||
super(
|
||||
pairList: CryptoCurrency.all
|
||||
.where((i) => i != CryptoCurrency.xhv)
|
||||
.map((i) => CryptoCurrency.all
|
||||
.where((i) => i != CryptoCurrency.xhv)
|
||||
.map((k) => ExchangePair(from: i, to: k, reverse: true)))
|
||||
.expand((i) => i)
|
||||
.toList());
|
||||
ChangeNowExchangeProvider({required SettingsStore settingsStore})
|
||||
: _settingsStore = settingsStore,
|
||||
_lastUsedRateId = '',
|
||||
super(pairList: supportedPairs(_notSupported));
|
||||
|
||||
static const List<CryptoCurrency> _notSupported = [
|
||||
CryptoCurrency.zaddr,
|
||||
CryptoCurrency.xhv,
|
||||
];
|
||||
|
||||
static final apiKey =
|
||||
DeviceInfo.instance.isMobile ? secrets.changeNowApiKey : secrets.changeNowApiKeyDesktop;
|
||||
@@ -38,6 +37,9 @@ class ChangeNowExchangeProvider extends ExchangeProvider {
|
||||
static const rangePath = '/v2/exchange/range';
|
||||
static const apiHeaderKey = 'x-changenow-api-key';
|
||||
|
||||
final SettingsStore _settingsStore;
|
||||
String _lastUsedRateId;
|
||||
|
||||
@override
|
||||
String get title => 'ChangeNOW';
|
||||
|
||||
@@ -56,25 +58,18 @@ class ChangeNowExchangeProvider extends ExchangeProvider {
|
||||
@override
|
||||
Future<bool> checkIsAvailable() async => true;
|
||||
|
||||
final SettingsStore settingsStore;
|
||||
|
||||
String _lastUsedRateId;
|
||||
|
||||
static String getFlow(bool isFixedRate) => isFixedRate ? 'fixed-rate' : 'standard';
|
||||
|
||||
@override
|
||||
Future<Limits> fetchLimits(
|
||||
{required CryptoCurrency from,
|
||||
required CryptoCurrency to,
|
||||
required bool isFixedRateMode}) async {
|
||||
final headers = {apiHeaderKey: apiKey};
|
||||
final flow = getFlow(isFixedRateMode);
|
||||
final params = <String, String>{
|
||||
'fromCurrency': _normalizeCurrency(from),
|
||||
'toCurrency': _normalizeCurrency(to),
|
||||
'fromNetwork': _networkFor(from),
|
||||
'toNetwork': _networkFor(to),
|
||||
'flow': flow
|
||||
'flow': _getFlow(isFixedRateMode)
|
||||
};
|
||||
final uri = Uri.https(apiAuthority, rangePath, params);
|
||||
final response = await get(uri, headers: headers);
|
||||
@@ -86,20 +81,61 @@ class ChangeNowExchangeProvider extends ExchangeProvider {
|
||||
throw Exception('${error}\n$message');
|
||||
}
|
||||
|
||||
if (response.statusCode != 200) {
|
||||
if (response.statusCode != 200)
|
||||
throw Exception('Unexpected http status: ${response.statusCode}');
|
||||
}
|
||||
|
||||
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
|
||||
return Limits(
|
||||
min: responseJSON['minAmount'] as double?, max: responseJSON['maxAmount'] as double?);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<double> fetchRate(
|
||||
{required CryptoCurrency from,
|
||||
required CryptoCurrency to,
|
||||
required double amount,
|
||||
required bool isFixedRateMode,
|
||||
required bool isReceiveAmount}) async {
|
||||
try {
|
||||
if (amount == 0) return 0.0;
|
||||
|
||||
final headers = {apiHeaderKey: apiKey};
|
||||
final isReverse = isReceiveAmount;
|
||||
final type = isReverse ? 'reverse' : 'direct';
|
||||
final params = <String, String>{
|
||||
'fromCurrency': _normalizeCurrency(from),
|
||||
'toCurrency': _normalizeCurrency(to),
|
||||
'fromNetwork': _networkFor(from),
|
||||
'toNetwork': _networkFor(to),
|
||||
'type': type,
|
||||
'flow': _getFlow(isFixedRateMode)
|
||||
};
|
||||
|
||||
if (isReverse)
|
||||
params['toAmount'] = amount.toString();
|
||||
else
|
||||
params['fromAmount'] = amount.toString();
|
||||
|
||||
final uri = Uri.https(apiAuthority, estimatedAmountPath, params);
|
||||
final response = await get(uri, headers: headers);
|
||||
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
|
||||
final fromAmount = double.parse(responseJSON['fromAmount'].toString());
|
||||
final toAmount = double.parse(responseJSON['toAmount'].toString());
|
||||
final rateId = responseJSON['rateId'] as String? ?? '';
|
||||
|
||||
if (rateId.isNotEmpty) _lastUsedRateId = rateId;
|
||||
|
||||
return isReverse ? (amount / fromAmount) : (toAmount / amount);
|
||||
} catch (e) {
|
||||
print(e.toString());
|
||||
return 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Trade> createTrade({required TradeRequest request, required bool isFixedRateMode}) async {
|
||||
final _request = request as ChangeNowRequest;
|
||||
final distributionPath = await DistributionInfo.instance.getDistributionPath();
|
||||
final formattedAppVersion = int.tryParse(settingsStore.appVersion.replaceAll('.', '')) ?? 0;
|
||||
final formattedAppVersion = int.tryParse(_settingsStore.appVersion.replaceAll('.', '')) ?? 0;
|
||||
final payload = {
|
||||
'app': isMoneroOnly ? 'monerocom' : 'cakewallet',
|
||||
'device': Platform.operatingSystem,
|
||||
@@ -107,19 +143,18 @@ class ChangeNowExchangeProvider extends ExchangeProvider {
|
||||
'version': formattedAppVersion
|
||||
};
|
||||
final headers = {apiHeaderKey: apiKey, 'Content-Type': 'application/json'};
|
||||
final flow = getFlow(isFixedRateMode);
|
||||
final type = isFixedRateMode ? 'reverse' : 'direct';
|
||||
final body = <String, dynamic>{
|
||||
'fromCurrency': _normalizeCurrency(_request.from),
|
||||
'toCurrency': _normalizeCurrency(_request.to),
|
||||
'fromNetwork': _networkFor(_request.from),
|
||||
'toNetwork': _networkFor(_request.to),
|
||||
if (!isFixedRateMode) 'fromAmount': _request.fromAmount,
|
||||
if (isFixedRateMode) 'toAmount': _request.toAmount,
|
||||
'address': _request.address,
|
||||
'flow': flow,
|
||||
'fromCurrency': _normalizeCurrency(request.fromCurrency),
|
||||
'toCurrency': _normalizeCurrency(request.toCurrency),
|
||||
'fromNetwork': _networkFor(request.fromCurrency),
|
||||
'toNetwork': _networkFor(request.toCurrency),
|
||||
if (!isFixedRateMode) 'fromAmount': request.fromAmount,
|
||||
if (isFixedRateMode) 'toAmount': request.toAmount,
|
||||
'address': request.toAddress,
|
||||
'flow': _getFlow(isFixedRateMode),
|
||||
'type': type,
|
||||
'refundAddress': _request.refundAddress,
|
||||
'refundAddress': request.refundAddress,
|
||||
'payload': payload,
|
||||
};
|
||||
|
||||
@@ -127,9 +162,9 @@ class ChangeNowExchangeProvider extends ExchangeProvider {
|
||||
// since we schedule to calculate the rate every 5 seconds we need to ensure that
|
||||
// we have the latest rate id with the given inputs before creating the trade
|
||||
await fetchRate(
|
||||
from: _request.from,
|
||||
to: _request.to,
|
||||
amount: double.tryParse(_request.toAmount) ?? 0,
|
||||
from: request.fromCurrency,
|
||||
to: request.toCurrency,
|
||||
amount: double.tryParse(request.toAmount) ?? 0,
|
||||
isFixedRateMode: true,
|
||||
isReceiveAmount: true,
|
||||
);
|
||||
@@ -146,9 +181,8 @@ class ChangeNowExchangeProvider extends ExchangeProvider {
|
||||
throw Exception('${error}\n$message');
|
||||
}
|
||||
|
||||
if (response.statusCode != 200) {
|
||||
if (response.statusCode != 200)
|
||||
throw Exception('Unexpected http status: ${response.statusCode}');
|
||||
}
|
||||
|
||||
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
|
||||
final id = responseJSON['id'] as String;
|
||||
@@ -159,14 +193,14 @@ class ChangeNowExchangeProvider extends ExchangeProvider {
|
||||
|
||||
return Trade(
|
||||
id: id,
|
||||
from: _request.from,
|
||||
to: _request.to,
|
||||
from: request.fromCurrency,
|
||||
to: request.toCurrency,
|
||||
provider: description,
|
||||
inputAddress: inputAddress,
|
||||
refundAddress: refundAddress,
|
||||
extraId: extraId,
|
||||
createdAt: DateTime.now(),
|
||||
amount: responseJSON['fromAmount']?.toString() ?? _request.fromAmount,
|
||||
amount: responseJSON['fromAmount']?.toString() ?? request.fromAmount,
|
||||
state: TradeState.created,
|
||||
payoutAddress: payoutAddress);
|
||||
}
|
||||
@@ -178,9 +212,7 @@ class ChangeNowExchangeProvider extends ExchangeProvider {
|
||||
final uri = Uri.https(apiAuthority, findTradeByIdPath, params);
|
||||
final response = await get(uri, headers: headers);
|
||||
|
||||
if (response.statusCode == 404) {
|
||||
throw TradeNotFoundException(id, provider: description);
|
||||
}
|
||||
if (response.statusCode == 404) throw TradeNotFoundException(id, provider: description);
|
||||
|
||||
if (response.statusCode == 400) {
|
||||
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
|
||||
@@ -189,9 +221,8 @@ class ChangeNowExchangeProvider extends ExchangeProvider {
|
||||
throw TradeNotFoundException(id, provider: description, description: error);
|
||||
}
|
||||
|
||||
if (response.statusCode != 200) {
|
||||
if (response.statusCode != 200)
|
||||
throw Exception('Unexpected http status: ${response.statusCode}');
|
||||
}
|
||||
|
||||
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
|
||||
final fromCurrency = responseJSON['fromCurrency'] as String;
|
||||
@@ -222,54 +253,7 @@ class ChangeNowExchangeProvider extends ExchangeProvider {
|
||||
payoutAddress: payoutAddress);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<double> fetchRate(
|
||||
{required CryptoCurrency from,
|
||||
required CryptoCurrency to,
|
||||
required double amount,
|
||||
required bool isFixedRateMode,
|
||||
required bool isReceiveAmount}) async {
|
||||
try {
|
||||
if (amount == 0) {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
final headers = {apiHeaderKey: apiKey};
|
||||
final isReverse = isReceiveAmount;
|
||||
final type = isReverse ? 'reverse' : 'direct';
|
||||
final flow = getFlow(isFixedRateMode);
|
||||
final params = <String, String>{
|
||||
'fromCurrency': _normalizeCurrency(from),
|
||||
'toCurrency': _normalizeCurrency(to),
|
||||
'fromNetwork': _networkFor(from),
|
||||
'toNetwork': _networkFor(to),
|
||||
'type': type,
|
||||
'flow': flow
|
||||
};
|
||||
|
||||
if (isReverse) {
|
||||
params['toAmount'] = amount.toString();
|
||||
} else {
|
||||
params['fromAmount'] = amount.toString();
|
||||
}
|
||||
|
||||
final uri = Uri.https(apiAuthority, estimatedAmountPath, params);
|
||||
final response = await get(uri, headers: headers);
|
||||
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
|
||||
final fromAmount = double.parse(responseJSON['fromAmount'].toString());
|
||||
final toAmount = double.parse(responseJSON['toAmount'].toString());
|
||||
final rateId = responseJSON['rateId'] as String? ?? '';
|
||||
|
||||
if (rateId.isNotEmpty) {
|
||||
_lastUsedRateId = rateId;
|
||||
}
|
||||
|
||||
return isReverse ? (amount / fromAmount) : (toAmount / amount);
|
||||
} catch (e) {
|
||||
print(e.toString());
|
||||
return 0.0;
|
||||
}
|
||||
}
|
||||
String _getFlow(bool isFixedRate) => isFixedRate ? 'fixed-rate' : 'standard';
|
||||
|
||||
String _networkFor(CryptoCurrency currency) {
|
||||
switch (currency) {
|
||||
@@ -301,4 +285,4 @@ class ChangeNowExchangeProvider extends ExchangeProvider {
|
||||
return tag.toLowerCase();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+21
-15
@@ -1,37 +1,43 @@
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
import 'package:cake_wallet/exchange/trade_request.dart';
|
||||
import 'package:cake_wallet/exchange/exchange_pair.dart';
|
||||
import 'package:cake_wallet/exchange/exchange_provider_description.dart';
|
||||
import 'package:cake_wallet/exchange/limits.dart';
|
||||
import 'package:cake_wallet/exchange/trade.dart';
|
||||
import 'package:cake_wallet/exchange/exchange_provider_description.dart';
|
||||
import 'package:cake_wallet/exchange/trade_request.dart';
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
|
||||
abstract class ExchangeProvider {
|
||||
ExchangeProvider({required this.pairList});
|
||||
|
||||
|
||||
String get title;
|
||||
|
||||
List<ExchangePair> pairList;
|
||||
|
||||
ExchangeProviderDescription get description;
|
||||
|
||||
bool get isAvailable;
|
||||
|
||||
bool get isEnabled;
|
||||
|
||||
bool get supportsFixedRate;
|
||||
|
||||
bool get supportsOnionAddress => false;
|
||||
|
||||
@override
|
||||
String toString() => title;
|
||||
|
||||
Future<Limits> fetchLimits(
|
||||
{required CryptoCurrency from, required CryptoCurrency to, required bool isFixedRateMode});
|
||||
|
||||
Future<Trade> createTrade({required TradeRequest request, required bool isFixedRateMode});
|
||||
|
||||
Future<Trade> findTradeById({required String id});
|
||||
|
||||
Future<double> fetchRate(
|
||||
{required CryptoCurrency from,
|
||||
required CryptoCurrency to,
|
||||
required bool isFixedRateMode});
|
||||
Future<Trade> createTrade({
|
||||
required TradeRequest request,
|
||||
required bool isFixedRateMode});
|
||||
Future<Trade> findTradeById({required String id});
|
||||
Future<double> fetchRate({
|
||||
required CryptoCurrency from,
|
||||
required CryptoCurrency to,
|
||||
required double amount,
|
||||
required bool isFixedRateMode,
|
||||
required bool isReceiveAmount});
|
||||
required double amount,
|
||||
required bool isFixedRateMode,
|
||||
required bool isReceiveAmount});
|
||||
|
||||
Future<bool> checkIsAvailable();
|
||||
}
|
||||
+72
-98
@@ -1,19 +1,19 @@
|
||||
import 'dart:convert';
|
||||
import 'package:cake_wallet/exchange/trade_not_found_exeption.dart';
|
||||
import 'package:http/http.dart';
|
||||
|
||||
import 'package:cake_wallet/.secrets.g.dart' as secrets;
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
import 'package:cake_wallet/exchange/exchange_pair.dart';
|
||||
import 'package:cake_wallet/exchange/exchange_provider.dart';
|
||||
import 'package:cake_wallet/exchange/exchange_provider_description.dart';
|
||||
import 'package:cake_wallet/exchange/limits.dart';
|
||||
import 'package:cake_wallet/exchange/provider/exchange_provider.dart';
|
||||
import 'package:cake_wallet/exchange/trade.dart';
|
||||
import 'package:cake_wallet/exchange/trade_not_found_exception.dart';
|
||||
import 'package:cake_wallet/exchange/trade_request.dart';
|
||||
import 'package:cake_wallet/exchange/trade_state.dart';
|
||||
import 'package:cake_wallet/exchange/exolix/exolix_request.dart';
|
||||
import 'package:cake_wallet/exchange/exchange_provider_description.dart';
|
||||
import 'package:cake_wallet/exchange/utils/currency_pairs_utils.dart';
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
import 'package:http/http.dart';
|
||||
|
||||
class ExolixExchangeProvider extends ExchangeProvider {
|
||||
ExolixExchangeProvider() : super(pairList: _supportedPairs());
|
||||
ExolixExchangeProvider() : super(pairList: supportedPairs(_notSupported));
|
||||
|
||||
static final apiKey = secrets.exolixApiKey;
|
||||
static const apiBaseUrl = 'exolix.com';
|
||||
@@ -40,16 +40,6 @@ class ExolixExchangeProvider extends ExchangeProvider {
|
||||
CryptoCurrency.weth,
|
||||
];
|
||||
|
||||
static List<ExchangePair> _supportedPairs() {
|
||||
final supportedCurrencies =
|
||||
CryptoCurrency.all.where((element) => !_notSupported.contains(element)).toList();
|
||||
|
||||
return supportedCurrencies
|
||||
.map((i) => supportedCurrencies.map((k) => ExchangePair(from: i, to: k, reverse: true)))
|
||||
.expand((i) => i)
|
||||
.toList();
|
||||
}
|
||||
|
||||
@override
|
||||
String get title => 'Exolix';
|
||||
|
||||
@@ -68,15 +58,13 @@ class ExolixExchangeProvider extends ExchangeProvider {
|
||||
@override
|
||||
Future<bool> checkIsAvailable() async => true;
|
||||
|
||||
static String getRateType(bool isFixedRate) => isFixedRate ? 'fixed' : 'float';
|
||||
|
||||
@override
|
||||
Future<Limits> fetchLimits(
|
||||
{required CryptoCurrency from,
|
||||
required CryptoCurrency to,
|
||||
required bool isFixedRateMode}) async {
|
||||
final params = <String, String>{
|
||||
'rateType': getRateType(isFixedRateMode),
|
||||
'rateType': _getRateType(isFixedRateMode),
|
||||
'amount': '1',
|
||||
};
|
||||
if (isFixedRateMode) {
|
||||
@@ -93,35 +81,71 @@ class ExolixExchangeProvider extends ExchangeProvider {
|
||||
final uri = Uri.https(apiBaseUrl, ratePath, params);
|
||||
final response = await get(uri);
|
||||
|
||||
if (response.statusCode != 200) {
|
||||
if (response.statusCode != 200)
|
||||
throw Exception('Unexpected http status: ${response.statusCode}');
|
||||
}
|
||||
|
||||
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
|
||||
return Limits(min: responseJSON['minAmount'] as double?);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Trade> createTrade({required TradeRequest request, required bool isFixedRateMode}) async {
|
||||
final _request = request as ExolixRequest;
|
||||
Future<double> fetchRate(
|
||||
{required CryptoCurrency from,
|
||||
required CryptoCurrency to,
|
||||
required double amount,
|
||||
required bool isFixedRateMode,
|
||||
required bool isReceiveAmount}) async {
|
||||
try {
|
||||
if (amount == 0) return 0.0;
|
||||
|
||||
final params = {
|
||||
'coinFrom': _normalizeCurrency(from),
|
||||
'coinTo': _normalizeCurrency(to),
|
||||
'networkFrom': _networkFor(from),
|
||||
'networkTo': _networkFor(to),
|
||||
'rateType': _getRateType(isFixedRateMode),
|
||||
'apiToken': apiKey,
|
||||
};
|
||||
|
||||
if (isReceiveAmount)
|
||||
params['withdrawalAmount'] = amount.toString();
|
||||
else
|
||||
params['amount'] = amount.toString();
|
||||
|
||||
final uri = Uri.https(apiBaseUrl, ratePath, params);
|
||||
final response = await get(uri);
|
||||
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
|
||||
|
||||
if (response.statusCode != 200) {
|
||||
final message = responseJSON['message'] as String?;
|
||||
throw Exception(message);
|
||||
}
|
||||
|
||||
return responseJSON['rate'] as double;
|
||||
} catch (e) {
|
||||
print(e.toString());
|
||||
return 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Trade> createTrade({required TradeRequest request, required bool isFixedRateMode}) async {
|
||||
final headers = {'Content-Type': 'application/json'};
|
||||
final body = <String, dynamic>{
|
||||
'coinFrom': _normalizeCurrency(_request.from),
|
||||
'coinTo': _normalizeCurrency(_request.to),
|
||||
'networkFrom': _networkFor(_request.from),
|
||||
'networkTo': _networkFor(_request.to),
|
||||
'withdrawalAddress': _request.address,
|
||||
'refundAddress': _request.refundAddress,
|
||||
'rateType': getRateType(isFixedRateMode),
|
||||
final body = {
|
||||
'coinFrom': _normalizeCurrency(request.fromCurrency),
|
||||
'coinTo': _normalizeCurrency(request.toCurrency),
|
||||
'networkFrom': _networkFor(request.fromCurrency),
|
||||
'networkTo': _networkFor(request.toCurrency),
|
||||
'withdrawalAddress': request.toAddress,
|
||||
'refundAddress': request.refundAddress,
|
||||
'rateType': _getRateType(isFixedRateMode),
|
||||
'apiToken': apiKey,
|
||||
};
|
||||
|
||||
if (isFixedRateMode) {
|
||||
body['withdrawalAmount'] = _request.toAmount;
|
||||
} else {
|
||||
body['amount'] = _request.fromAmount;
|
||||
}
|
||||
if (isFixedRateMode)
|
||||
body['withdrawalAmount'] = request.toAmount;
|
||||
else
|
||||
body['amount'] = request.fromAmount;
|
||||
|
||||
final uri = Uri.https(apiBaseUrl, transactionsPath);
|
||||
final response = await post(uri, headers: headers, body: json.encode(body));
|
||||
@@ -133,9 +157,8 @@ class ExolixExchangeProvider extends ExchangeProvider {
|
||||
throw Exception(errorMessage);
|
||||
}
|
||||
|
||||
if (response.statusCode != 200 && response.statusCode != 201) {
|
||||
if (response.statusCode != 200 && response.statusCode != 201)
|
||||
throw Exception('Unexpected http status: ${response.statusCode}');
|
||||
}
|
||||
|
||||
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
|
||||
final id = responseJSON['id'] as String;
|
||||
@@ -147,8 +170,8 @@ class ExolixExchangeProvider extends ExchangeProvider {
|
||||
|
||||
return Trade(
|
||||
id: id,
|
||||
from: _request.from,
|
||||
to: _request.to,
|
||||
from: request.fromCurrency,
|
||||
to: request.toCurrency,
|
||||
provider: description,
|
||||
inputAddress: inputAddress,
|
||||
refundAddress: refundAddress,
|
||||
@@ -161,13 +184,11 @@ class ExolixExchangeProvider extends ExchangeProvider {
|
||||
|
||||
@override
|
||||
Future<Trade> findTradeById({required String id}) async {
|
||||
final findTradeByIdPath = transactionsPath + '/$id';
|
||||
final findTradeByIdPath = '$transactionsPath/$id';
|
||||
final uri = Uri.https(apiBaseUrl, findTradeByIdPath);
|
||||
final response = await get(uri);
|
||||
|
||||
if (response.statusCode == 404) {
|
||||
throw TradeNotFoundException(id, provider: description);
|
||||
}
|
||||
if (response.statusCode == 404) throw TradeNotFoundException(id, provider: description);
|
||||
|
||||
if (response.statusCode == 400) {
|
||||
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
|
||||
@@ -177,80 +198,33 @@ class ExolixExchangeProvider extends ExchangeProvider {
|
||||
throw TradeNotFoundException(id, provider: description, description: errorMessage);
|
||||
}
|
||||
|
||||
if (response.statusCode != 200) {
|
||||
if (response.statusCode != 200)
|
||||
throw Exception('Unexpected http status: ${response.statusCode}');
|
||||
}
|
||||
|
||||
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
|
||||
final coinFrom = responseJSON['coinFrom']['coinCode'] as String;
|
||||
final from = CryptoCurrency.fromString(coinFrom);
|
||||
final coinTo = responseJSON['coinTo']['coinCode'] as String;
|
||||
final to = CryptoCurrency.fromString(coinTo);
|
||||
final inputAddress = responseJSON['depositAddress'] as String;
|
||||
final amount = responseJSON['amount'].toString();
|
||||
final status = responseJSON['status'] as String;
|
||||
final state = TradeState.deserialize(raw: _prepareStatus(status));
|
||||
final extraId = responseJSON['depositExtraId'] as String?;
|
||||
final outputTransaction = responseJSON['hashOut']['hash'] as String?;
|
||||
final payoutAddress = responseJSON['withdrawalAddress'] as String;
|
||||
|
||||
return Trade(
|
||||
id: id,
|
||||
from: from,
|
||||
to: to,
|
||||
from: CryptoCurrency.fromString(coinFrom),
|
||||
to: CryptoCurrency.fromString(coinTo),
|
||||
provider: description,
|
||||
inputAddress: inputAddress,
|
||||
amount: amount,
|
||||
state: state,
|
||||
state: TradeState.deserialize(raw: _prepareStatus(status)),
|
||||
extraId: extraId,
|
||||
outputTransaction: outputTransaction,
|
||||
payoutAddress: payoutAddress);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<double> fetchRate(
|
||||
{required CryptoCurrency from,
|
||||
required CryptoCurrency to,
|
||||
required double amount,
|
||||
required bool isFixedRateMode,
|
||||
required bool isReceiveAmount}) async {
|
||||
try {
|
||||
if (amount == 0) {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
final params = <String, String>{
|
||||
'coinFrom': _normalizeCurrency(from),
|
||||
'coinTo': _normalizeCurrency(to),
|
||||
'networkFrom': _networkFor(from),
|
||||
'networkTo': _networkFor(to),
|
||||
'rateType': getRateType(isFixedRateMode),
|
||||
'apiToken': apiKey,
|
||||
};
|
||||
|
||||
if (isReceiveAmount) {
|
||||
params['withdrawalAmount'] = amount.toString();
|
||||
} else {
|
||||
params['amount'] = amount.toString();
|
||||
}
|
||||
|
||||
final uri = Uri.https(apiBaseUrl, ratePath, params);
|
||||
final response = await get(uri);
|
||||
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
|
||||
|
||||
if (response.statusCode != 200) {
|
||||
final message = responseJSON['message'] as String?;
|
||||
throw Exception(message);
|
||||
}
|
||||
|
||||
final rate = responseJSON['rate'] as double;
|
||||
|
||||
return rate;
|
||||
} catch (e) {
|
||||
print(e.toString());
|
||||
return 0.0;
|
||||
}
|
||||
}
|
||||
String _getRateType(bool isFixedRate) => isFixedRate ? 'fixed' : 'float';
|
||||
|
||||
String _prepareStatus(String status) {
|
||||
switch (status) {
|
||||
+143
-175
@@ -1,28 +1,20 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:cake_wallet/exchange/exchange_pair.dart';
|
||||
import 'package:cake_wallet/exchange/exchange_provider.dart';
|
||||
import 'package:cake_wallet/exchange/exchange_provider_description.dart';
|
||||
import 'package:cake_wallet/exchange/sideshift/sideshift_request.dart';
|
||||
import 'package:cake_wallet/exchange/trade_not_created_exeption.dart';
|
||||
import 'package:cake_wallet/exchange/trade_not_found_exeption.dart';
|
||||
import 'package:cake_wallet/exchange/trade_state.dart';
|
||||
import 'package:cake_wallet/.secrets.g.dart' as secrets;
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
import 'package:cake_wallet/exchange/trade_request.dart';
|
||||
import 'package:cake_wallet/exchange/trade.dart';
|
||||
import 'package:cake_wallet/exchange/provider/exchange_provider.dart';
|
||||
import 'package:cake_wallet/exchange/exchange_provider_description.dart';
|
||||
import 'package:cake_wallet/exchange/limits.dart';
|
||||
import 'package:cake_wallet/exchange/trade.dart';
|
||||
import 'package:cake_wallet/exchange/trade_not_created_exception.dart';
|
||||
import 'package:cake_wallet/exchange/trade_not_found_exception.dart';
|
||||
import 'package:cake_wallet/exchange/trade_request.dart';
|
||||
import 'package:cake_wallet/exchange/trade_state.dart';
|
||||
import 'package:cake_wallet/exchange/utils/currency_pairs_utils.dart';
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
import 'package:http/http.dart';
|
||||
|
||||
class SideShiftExchangeProvider extends ExchangeProvider {
|
||||
SideShiftExchangeProvider() : super(pairList: _supportedPairs());
|
||||
|
||||
static const affiliateId = secrets.sideShiftAffiliateId;
|
||||
static const apiBaseUrl = 'https://sideshift.ai/api';
|
||||
static const rangePath = '/v2/pair';
|
||||
static const orderPath = '/v2/shifts';
|
||||
static const quotePath = '/v2/quotes';
|
||||
static const permissionPath = '/v2/permissions';
|
||||
SideShiftExchangeProvider() : super(pairList: supportedPairs(_notSupported));
|
||||
|
||||
static const List<CryptoCurrency> _notSupported = [
|
||||
CryptoCurrency.xhv,
|
||||
@@ -39,49 +31,28 @@ class SideShiftExchangeProvider extends ExchangeProvider {
|
||||
CryptoCurrency.eos,
|
||||
];
|
||||
|
||||
static List<ExchangePair> _supportedPairs() {
|
||||
final supportedCurrencies =
|
||||
CryptoCurrency.all.where((element) => !_notSupported.contains(element)).toList();
|
||||
static const affiliateId = secrets.sideShiftAffiliateId;
|
||||
static const apiBaseUrl = 'https://sideshift.ai/api';
|
||||
static const rangePath = '/v2/pair';
|
||||
static const orderPath = '/v2/shifts';
|
||||
static const quotePath = '/v2/quotes';
|
||||
static const permissionPath = '/v2/permissions';
|
||||
|
||||
return supportedCurrencies
|
||||
.map((i) => supportedCurrencies.map((k) => ExchangePair(from: i, to: k, reverse: true)))
|
||||
.expand((i) => i)
|
||||
.toList();
|
||||
}
|
||||
@override
|
||||
String get title => 'SideShift';
|
||||
|
||||
@override
|
||||
bool get isAvailable => true;
|
||||
|
||||
@override
|
||||
bool get isEnabled => true;
|
||||
|
||||
@override
|
||||
bool get supportsFixedRate => true;
|
||||
|
||||
@override
|
||||
ExchangeProviderDescription get description => ExchangeProviderDescription.sideShift;
|
||||
|
||||
@override
|
||||
Future<double> fetchRate(
|
||||
{required CryptoCurrency from,
|
||||
required CryptoCurrency to,
|
||||
required double amount,
|
||||
required bool isFixedRateMode,
|
||||
required bool isReceiveAmount}) async {
|
||||
try {
|
||||
if (amount == 0) {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
final fromCurrency = from.title.toLowerCase();
|
||||
final toCurrency = to.title.toLowerCase();
|
||||
final depositNetwork = _networkFor(from);
|
||||
final settleNetwork = _networkFor(to);
|
||||
|
||||
final url = "$apiBaseUrl$rangePath/$fromCurrency-$depositNetwork/$toCurrency-$settleNetwork?amount=$amount";
|
||||
|
||||
final uri = Uri.parse(url);
|
||||
final response = await get(uri);
|
||||
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
|
||||
final rate = double.parse(responseJSON['rate'] as String);
|
||||
|
||||
return rate;
|
||||
} catch (_) {
|
||||
return 0.00;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<bool> checkIsAvailable() async {
|
||||
const url = apiBaseUrl + permissionPath;
|
||||
@@ -95,110 +66,10 @@ class SideShiftExchangeProvider extends ExchangeProvider {
|
||||
throw Exception('$error');
|
||||
}
|
||||
|
||||
if (response.statusCode != 200) {
|
||||
return false;
|
||||
}
|
||||
if (response.statusCode != 200) return false;
|
||||
|
||||
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
|
||||
final cancreateShift = responseJSON['createShift'] as bool;
|
||||
return cancreateShift;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Trade> createTrade({required TradeRequest request, required bool isFixedRateMode}) async {
|
||||
final _request = request as SideShiftRequest;
|
||||
String url = '';
|
||||
final depositCoin = request.depositMethod.title.toLowerCase();
|
||||
final settleCoin = request.settleMethod.title.toLowerCase();
|
||||
final body = {
|
||||
'affiliateId': affiliateId,
|
||||
'settleAddress': _request.settleAddress,
|
||||
'refundAddress': _request.refundAddress,
|
||||
};
|
||||
|
||||
if (isFixedRateMode) {
|
||||
final quoteId = await _createQuote(_request);
|
||||
body['quoteId'] = quoteId;
|
||||
|
||||
url = apiBaseUrl + orderPath + '/fixed';
|
||||
} else {
|
||||
url = apiBaseUrl + orderPath + '/variable';
|
||||
final depositNetwork = _networkFor(request.depositMethod);
|
||||
final settleNetwork = _networkFor(request.settleMethod);
|
||||
body["depositCoin"] = depositCoin;
|
||||
body["settleCoin"] = settleCoin;
|
||||
body["settleNetwork"] = settleNetwork;
|
||||
body["depositNetwork"] = depositNetwork;
|
||||
}
|
||||
final headers = {'Content-Type': 'application/json'};
|
||||
|
||||
final uri = Uri.parse(url);
|
||||
final response = await post(uri, headers: headers, body: json.encode(body));
|
||||
|
||||
if (response.statusCode != 201) {
|
||||
if (response.statusCode == 400) {
|
||||
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
|
||||
final error = responseJSON['error']['message'] as String;
|
||||
|
||||
throw TradeNotCreatedException(description, description: error);
|
||||
}
|
||||
|
||||
throw TradeNotCreatedException(description);
|
||||
}
|
||||
|
||||
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
|
||||
final id = responseJSON['id'] as String;
|
||||
final inputAddress = responseJSON['depositAddress'] as String;
|
||||
final settleAddress = responseJSON['settleAddress'] as String;
|
||||
final depositAmount = responseJSON['depositAmount'] as String?;
|
||||
|
||||
return Trade(
|
||||
id: id,
|
||||
provider: description,
|
||||
from: _request.depositMethod,
|
||||
to: _request.settleMethod,
|
||||
inputAddress: inputAddress,
|
||||
refundAddress: settleAddress,
|
||||
state: TradeState.created,
|
||||
amount: depositAmount ?? _request.depositAmount,
|
||||
payoutAddress: settleAddress,
|
||||
createdAt: DateTime.now(),
|
||||
);
|
||||
}
|
||||
|
||||
Future<String> _createQuote(SideShiftRequest request) async {
|
||||
final url = apiBaseUrl + quotePath;
|
||||
final headers = {'Content-Type': 'application/json'};
|
||||
final depositMethod = request.depositMethod.title.toLowerCase();
|
||||
final settleMethod = request.settleMethod.title.toLowerCase();
|
||||
final depositNetwork = _networkFor(request.depositMethod);
|
||||
final settleNetwork = _networkFor(request.settleMethod);
|
||||
final body = {
|
||||
'depositCoin': depositMethod,
|
||||
'settleCoin': settleMethod,
|
||||
'affiliateId': affiliateId,
|
||||
'settleAmount': request.depositAmount,
|
||||
'settleNetwork': settleNetwork,
|
||||
'depositNetwork': depositNetwork,
|
||||
};
|
||||
final uri = Uri.parse(url);
|
||||
final response = await post(uri, headers: headers, body: json.encode(body));
|
||||
|
||||
if (response.statusCode != 201) {
|
||||
if (response.statusCode == 400) {
|
||||
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
|
||||
final error = responseJSON['error']['message'] as String;
|
||||
|
||||
throw TradeNotCreatedException(description, description: error);
|
||||
}
|
||||
|
||||
throw TradeNotCreatedException(description);
|
||||
}
|
||||
|
||||
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
|
||||
final quoteId = responseJSON['id'] as String;
|
||||
|
||||
return quoteId;
|
||||
return responseJSON['createShift'] as bool;
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -244,6 +115,91 @@ class SideShiftExchangeProvider extends ExchangeProvider {
|
||||
return Limits(min: min, max: max);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<double> fetchRate(
|
||||
{required CryptoCurrency from,
|
||||
required CryptoCurrency to,
|
||||
required double amount,
|
||||
required bool isFixedRateMode,
|
||||
required bool isReceiveAmount}) async {
|
||||
try {
|
||||
if (amount == 0) return 0.0;
|
||||
|
||||
final fromCurrency = from.title.toLowerCase();
|
||||
final toCurrency = to.title.toLowerCase();
|
||||
final depositNetwork = _networkFor(from);
|
||||
final settleNetwork = _networkFor(to);
|
||||
|
||||
final url =
|
||||
"$apiBaseUrl$rangePath/$fromCurrency-$depositNetwork/$toCurrency-$settleNetwork?amount=$amount";
|
||||
|
||||
final uri = Uri.parse(url);
|
||||
final response = await get(uri);
|
||||
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
|
||||
|
||||
return double.parse(responseJSON['rate'] as String);
|
||||
} catch (_) {
|
||||
return 0.00;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Trade> createTrade({required TradeRequest request, required bool isFixedRateMode}) async {
|
||||
String url = '';
|
||||
final body = {
|
||||
'affiliateId': affiliateId,
|
||||
'settleAddress': request.toAddress,
|
||||
'refundAddress': request.refundAddress,
|
||||
};
|
||||
|
||||
if (isFixedRateMode) {
|
||||
final quoteId = await _createQuote(request);
|
||||
body['quoteId'] = quoteId;
|
||||
|
||||
url = apiBaseUrl + orderPath + '/fixed';
|
||||
} else {
|
||||
url = apiBaseUrl + orderPath + '/variable';
|
||||
body["depositCoin"] = request.fromCurrency.title.toLowerCase();
|
||||
body["settleCoin"] = request.toCurrency.title.toLowerCase();
|
||||
body["settleNetwork"] = _networkFor(request.toCurrency);
|
||||
body["depositNetwork"] = _networkFor(request.fromCurrency);
|
||||
}
|
||||
final headers = {'Content-Type': 'application/json'};
|
||||
|
||||
final uri = Uri.parse(url);
|
||||
final response = await post(uri, headers: headers, body: json.encode(body));
|
||||
|
||||
if (response.statusCode != 201) {
|
||||
if (response.statusCode == 400) {
|
||||
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
|
||||
final error = responseJSON['error']['message'] as String;
|
||||
|
||||
throw TradeNotCreatedException(description, description: error);
|
||||
}
|
||||
|
||||
throw TradeNotCreatedException(description);
|
||||
}
|
||||
|
||||
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
|
||||
final id = responseJSON['id'] as String;
|
||||
final inputAddress = responseJSON['depositAddress'] as String;
|
||||
final settleAddress = responseJSON['settleAddress'] as String;
|
||||
final depositAmount = responseJSON['depositAmount'] as String?;
|
||||
|
||||
return Trade(
|
||||
id: id,
|
||||
provider: description,
|
||||
from: request.fromCurrency,
|
||||
to: request.toCurrency,
|
||||
inputAddress: inputAddress,
|
||||
refundAddress: settleAddress,
|
||||
state: TradeState.created,
|
||||
amount: depositAmount ?? request.fromAmount,
|
||||
payoutAddress: settleAddress,
|
||||
createdAt: DateTime.now(),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Trade> findTradeById({required String id}) async {
|
||||
final url = apiBaseUrl + orderPath + '/' + id;
|
||||
@@ -267,44 +223,56 @@ class SideShiftExchangeProvider extends ExchangeProvider {
|
||||
|
||||
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
|
||||
final fromCurrency = responseJSON['depositCoin'] as String;
|
||||
final from = CryptoCurrency.fromString(fromCurrency);
|
||||
final toCurrency = responseJSON['settleCoin'] as String;
|
||||
final to = CryptoCurrency.fromString(toCurrency);
|
||||
final inputAddress = responseJSON['depositAddress'] as String;
|
||||
final expectedSendAmount = responseJSON['depositAmount'] as String?;
|
||||
final status = responseJSON['status'] as String?;
|
||||
final settleAddress = responseJSON['settleAddress'] as String;
|
||||
TradeState? state;
|
||||
|
||||
state = TradeState.deserialize(raw: status ?? 'created');
|
||||
final isVariable = (responseJSON['type'] as String) == 'variable';
|
||||
|
||||
final expiredAtRaw = responseJSON['expiresAt'] as String;
|
||||
final expiredAt = isVariable ? null : DateTime.tryParse(expiredAtRaw)?.toLocal();
|
||||
|
||||
return Trade(
|
||||
id: id,
|
||||
from: from,
|
||||
to: to,
|
||||
from: CryptoCurrency.fromString(fromCurrency),
|
||||
to: CryptoCurrency.fromString(toCurrency),
|
||||
provider: description,
|
||||
inputAddress: inputAddress,
|
||||
amount: expectedSendAmount ?? '',
|
||||
state: state,
|
||||
state: TradeState.deserialize(raw: status ?? 'created'),
|
||||
expiredAt: expiredAt,
|
||||
payoutAddress: settleAddress);
|
||||
}
|
||||
|
||||
@override
|
||||
bool get isAvailable => true;
|
||||
Future<String> _createQuote(TradeRequest request) async {
|
||||
final url = apiBaseUrl + quotePath;
|
||||
final headers = {'Content-Type': 'application/json'};
|
||||
final body = {
|
||||
'depositCoin': request.fromCurrency.title.toLowerCase(),
|
||||
'settleCoin': request.toCurrency.title.toLowerCase(),
|
||||
'affiliateId': affiliateId,
|
||||
'settleAmount': request.toAmount,
|
||||
'settleNetwork': _networkFor(request.toCurrency),
|
||||
'depositNetwork': _networkFor(request.fromCurrency),
|
||||
};
|
||||
final uri = Uri.parse(url);
|
||||
final response = await post(uri, headers: headers, body: json.encode(body));
|
||||
|
||||
@override
|
||||
bool get isEnabled => true;
|
||||
if (response.statusCode != 201) {
|
||||
if (response.statusCode == 400) {
|
||||
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
|
||||
final error = responseJSON['error']['message'] as String;
|
||||
|
||||
@override
|
||||
bool get supportsFixedRate => true;
|
||||
throw TradeNotCreatedException(description, description: error);
|
||||
}
|
||||
|
||||
@override
|
||||
String get title => 'SideShift';
|
||||
throw TradeNotCreatedException(description);
|
||||
}
|
||||
|
||||
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
|
||||
|
||||
return responseJSON['id'] as String;
|
||||
}
|
||||
|
||||
String _networkFor(CryptoCurrency currency) =>
|
||||
currency.tag != null ? _normalizeTag(currency.tag!) : 'mainnet';
|
||||
+104
-116
@@ -1,71 +1,49 @@
|
||||
import 'dart:convert';
|
||||
import 'package:cake_wallet/exchange/exchange_pair.dart';
|
||||
import 'package:cake_wallet/exchange/exchange_provider.dart';
|
||||
|
||||
import 'package:cake_wallet/.secrets.g.dart' as secrets;
|
||||
import 'package:cake_wallet/exchange/provider/exchange_provider.dart';
|
||||
import 'package:cake_wallet/exchange/exchange_provider_description.dart';
|
||||
import 'package:cake_wallet/exchange/simpleswap/simpleswap_request.dart';
|
||||
import 'package:cake_wallet/exchange/trade_not_created_exeption.dart';
|
||||
import 'package:cake_wallet/exchange/trade_not_found_exeption.dart';
|
||||
import 'package:cake_wallet/exchange/limits.dart';
|
||||
import 'package:cake_wallet/exchange/trade.dart';
|
||||
import 'package:cake_wallet/exchange/trade_not_created_exception.dart';
|
||||
import 'package:cake_wallet/exchange/trade_not_found_exception.dart';
|
||||
import 'package:cake_wallet/exchange/trade_request.dart';
|
||||
import 'package:cake_wallet/exchange/trade_state.dart';
|
||||
import 'package:cake_wallet/exchange/utils/currency_pairs_utils.dart';
|
||||
import 'package:cake_wallet/utils/device_info.dart';
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
import 'package:cake_wallet/exchange/trade_request.dart';
|
||||
import 'package:cake_wallet/exchange/trade.dart';
|
||||
import 'package:cake_wallet/exchange/limits.dart';
|
||||
import 'package:cake_wallet/.secrets.g.dart' as secrets;
|
||||
import 'package:http/http.dart';
|
||||
|
||||
class SimpleSwapExchangeProvider extends ExchangeProvider {
|
||||
SimpleSwapExchangeProvider()
|
||||
: super(
|
||||
pairList: CryptoCurrency.all
|
||||
.where((i) => i != CryptoCurrency.zaddr)
|
||||
.map((i) => CryptoCurrency.all
|
||||
.where((i) => i != CryptoCurrency.zaddr)
|
||||
.map((k) => ExchangePair(from: i, to: k, reverse: true)))
|
||||
.expand((i) => i)
|
||||
.toList());
|
||||
SimpleSwapExchangeProvider() : super(pairList: supportedPairs(_notSupported));
|
||||
|
||||
static const List<CryptoCurrency> _notSupported = [
|
||||
CryptoCurrency.zaddr,
|
||||
CryptoCurrency.xhv,
|
||||
];
|
||||
|
||||
static final apiKey =
|
||||
DeviceInfo.instance.isMobile ? secrets.simpleSwapApiKey : secrets.simpleSwapApiKeyDesktop;
|
||||
static const apiAuthority = 'api.simpleswap.io';
|
||||
static const getEstimatePath = '/v1/get_estimated';
|
||||
static const rangePath = '/v1/get_ranges';
|
||||
static const getExchangePath = '/v1/get_exchange';
|
||||
static const createExchangePath = '/v1/create_exchange';
|
||||
static final apiKey = DeviceInfo.instance.isMobile ? secrets.simpleSwapApiKey : secrets.simpleSwapApiKeyDesktop;
|
||||
|
||||
@override
|
||||
ExchangeProviderDescription get description =>
|
||||
ExchangeProviderDescription.simpleSwap;
|
||||
String get title => 'SimpleSwap';
|
||||
|
||||
@override
|
||||
Future<double> fetchRate(
|
||||
{required CryptoCurrency from,
|
||||
required CryptoCurrency to,
|
||||
required double amount,
|
||||
required bool isFixedRateMode,
|
||||
required bool isReceiveAmount}) async {
|
||||
try {
|
||||
if (amount == 0) {
|
||||
return 0.0;
|
||||
}
|
||||
final fromCurrency = _normalizeCryptoCurrency(from);
|
||||
final toCurrency = _normalizeCryptoCurrency(to);
|
||||
final params = <String, String>{
|
||||
'api_key': apiKey,
|
||||
'currency_from': fromCurrency,
|
||||
'currency_to': toCurrency,
|
||||
'amount': amount.toString(),
|
||||
'fixed': isFixedRateMode.toString()
|
||||
};
|
||||
final uri = Uri.https(apiAuthority, getEstimatePath, params);
|
||||
final response = await get(uri);
|
||||
bool get isAvailable => true;
|
||||
|
||||
if (response.body == "null") return 0.00;
|
||||
final data = json.decode(response.body) as String;
|
||||
return double.parse(data) / amount;
|
||||
} catch (_) {
|
||||
return 0.00;
|
||||
}
|
||||
}
|
||||
@override
|
||||
bool get isEnabled => true;
|
||||
|
||||
@override
|
||||
bool get supportsFixedRate => false;
|
||||
|
||||
@override
|
||||
ExchangeProviderDescription get description => ExchangeProviderDescription.simpleSwap;
|
||||
|
||||
@override
|
||||
Future<bool> checkIsAvailable() async {
|
||||
@@ -76,20 +54,79 @@ class SimpleSwapExchangeProvider extends ExchangeProvider {
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Trade> createTrade({required TradeRequest request, required bool isFixedRateMode}) async {
|
||||
final _request = request as SimpleSwapRequest;
|
||||
final headers = {
|
||||
'Content-Type': 'application/json'};
|
||||
final params = <String, String>{
|
||||
Future<Limits> fetchLimits(
|
||||
{required CryptoCurrency from,
|
||||
required CryptoCurrency to,
|
||||
required bool isFixedRateMode}) async {
|
||||
final params = <String, dynamic>{
|
||||
'api_key': apiKey,
|
||||
'fixed': isFixedRateMode.toString(),
|
||||
'currency_from': _normalizeCurrency(from),
|
||||
'currency_to': _normalizeCurrency(to),
|
||||
};
|
||||
final uri = Uri.https(apiAuthority, rangePath, params);
|
||||
|
||||
final response = await get(uri);
|
||||
|
||||
if (response.statusCode == 500) {
|
||||
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
|
||||
final error = responseJSON['message'] as String;
|
||||
|
||||
throw Exception('$error');
|
||||
}
|
||||
|
||||
if (response.statusCode != 200) {
|
||||
throw Exception('Unexpected http status: ${response.statusCode}');
|
||||
}
|
||||
|
||||
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
|
||||
final min = double.tryParse(responseJSON['min'] as String? ?? '');
|
||||
final max = double.tryParse(responseJSON['max'] as String? ?? '');
|
||||
|
||||
return Limits(min: min, max: max);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<double> fetchRate(
|
||||
{required CryptoCurrency from,
|
||||
required CryptoCurrency to,
|
||||
required double amount,
|
||||
required bool isFixedRateMode,
|
||||
required bool isReceiveAmount}) async {
|
||||
try {
|
||||
if (amount == 0) return 0.0;
|
||||
|
||||
final params = {
|
||||
'api_key': apiKey,
|
||||
'currency_from': _normalizeCurrency(from),
|
||||
'currency_to': _normalizeCurrency(to),
|
||||
'amount': amount.toString(),
|
||||
'fixed': isFixedRateMode.toString()
|
||||
};
|
||||
final uri = Uri.https(apiAuthority, getEstimatePath, params);
|
||||
final response = await get(uri);
|
||||
|
||||
if (response.body == "null") return 0.00;
|
||||
|
||||
final data = json.decode(response.body) as String;
|
||||
|
||||
return double.parse(data) / amount;
|
||||
} catch (_) {
|
||||
return 0.00;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Trade> createTrade({required TradeRequest request, required bool isFixedRateMode}) async {
|
||||
final headers = {'Content-Type': 'application/json'};
|
||||
final params = {'api_key': apiKey};
|
||||
final body = <String, dynamic>{
|
||||
"currency_from": _normalizeCryptoCurrency(_request.from),
|
||||
"currency_to": _normalizeCryptoCurrency(_request.to),
|
||||
"amount": _request.amount,
|
||||
"currency_from": _normalizeCurrency(request.fromCurrency),
|
||||
"currency_to": _normalizeCurrency(request.toCurrency),
|
||||
"amount": request.fromAmount,
|
||||
"fixed": isFixedRateMode,
|
||||
"user_refund_address": _request.refundAddress,
|
||||
"address_to": _request.address
|
||||
"user_refund_address": request.refundAddress,
|
||||
"address_to": request.toAddress
|
||||
};
|
||||
final uri = Uri.https(apiAuthority, createExchangePath, params);
|
||||
|
||||
@@ -112,56 +149,22 @@ class SimpleSwapExchangeProvider extends ExchangeProvider {
|
||||
final payoutAddress = responseJSON['address_to'] as String;
|
||||
final settleAddress = responseJSON['user_refund_address'] as String;
|
||||
final extraId = responseJSON['extra_id_from'] as String?;
|
||||
|
||||
return Trade(
|
||||
id: id,
|
||||
provider: description,
|
||||
from: _request.from,
|
||||
to: _request.to,
|
||||
from: request.fromCurrency,
|
||||
to: request.toCurrency,
|
||||
inputAddress: inputAddress,
|
||||
refundAddress: settleAddress,
|
||||
extraId: extraId,
|
||||
state: TradeState.created,
|
||||
amount: _request.amount,
|
||||
amount: request.fromAmount,
|
||||
payoutAddress: payoutAddress,
|
||||
createdAt: DateTime.now(),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Limits> fetchLimits({
|
||||
required CryptoCurrency from,
|
||||
required CryptoCurrency to,
|
||||
required bool isFixedRateMode}) async {
|
||||
final fromCurrency = _normalizeCryptoCurrency(from);
|
||||
final toCurrency = _normalizeCryptoCurrency(to);
|
||||
final params = <String, dynamic>{
|
||||
'api_key': apiKey,
|
||||
'fixed': isFixedRateMode.toString(),
|
||||
'currency_from': fromCurrency,
|
||||
'currency_to': toCurrency,
|
||||
};
|
||||
final uri = Uri.https(apiAuthority, rangePath, params);
|
||||
|
||||
final response = await get(uri);
|
||||
|
||||
if (response.statusCode == 500) {
|
||||
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
|
||||
final error = responseJSON['message'] as String;
|
||||
|
||||
throw Exception('$error');
|
||||
}
|
||||
|
||||
if (response.statusCode != 200) {
|
||||
throw Exception('Unexpected http status: ${response.statusCode}');
|
||||
}
|
||||
|
||||
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
|
||||
final min = double.tryParse(responseJSON['min'] as String? ?? '');
|
||||
final max = double.tryParse(responseJSON['max'] as String? ?? '');
|
||||
|
||||
return Limits(min: min, max: max);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Trade> findTradeById({required String id}) async {
|
||||
final params = {'api_key': apiKey, 'id': id};
|
||||
@@ -185,42 +188,27 @@ class SimpleSwapExchangeProvider extends ExchangeProvider {
|
||||
|
||||
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
|
||||
final fromCurrency = responseJSON['currency_from'] as String;
|
||||
final from = CryptoCurrency.fromString(fromCurrency);
|
||||
final toCurrency = responseJSON['currency_to'] as String;
|
||||
final to = CryptoCurrency.fromString(toCurrency);
|
||||
final inputAddress = responseJSON['address_from'] as String;
|
||||
final expectedSendAmount = responseJSON['expected_amount'].toString();
|
||||
final extraId = responseJSON['extra_id_from'] as String?;
|
||||
final status = responseJSON['status'] as String;
|
||||
final payoutAddress = responseJSON['address_to'] as String;
|
||||
final state = TradeState.deserialize(raw: status);
|
||||
|
||||
return Trade(
|
||||
id: id,
|
||||
from: from,
|
||||
to: to,
|
||||
from: CryptoCurrency.fromString(fromCurrency),
|
||||
to: CryptoCurrency.fromString(toCurrency),
|
||||
extraId: extraId,
|
||||
provider: description,
|
||||
inputAddress: inputAddress,
|
||||
amount: expectedSendAmount,
|
||||
state: state,
|
||||
state: TradeState.deserialize(raw: status),
|
||||
payoutAddress: payoutAddress,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
bool get isAvailable => true;
|
||||
|
||||
@override
|
||||
bool get isEnabled => true;
|
||||
|
||||
@override
|
||||
bool get supportsFixedRate => false;
|
||||
|
||||
@override
|
||||
String get title => 'SimpleSwap';
|
||||
|
||||
static String _normalizeCryptoCurrency(CryptoCurrency currency) {
|
||||
static String _normalizeCurrency(CryptoCurrency currency) {
|
||||
switch (currency) {
|
||||
case CryptoCurrency.zaddr:
|
||||
return 'zec';
|
||||
+102
-142
@@ -1,21 +1,20 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:cake_wallet/exchange/exchange_pair.dart';
|
||||
import 'package:cake_wallet/exchange/exchange_provider.dart';
|
||||
import 'package:cake_wallet/exchange/trade_state.dart';
|
||||
import 'package:cake_wallet/exchange/trocador/trocador_request.dart';
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
import 'package:cake_wallet/exchange/trade_request.dart';
|
||||
import 'package:cake_wallet/exchange/trade.dart';
|
||||
import 'package:cake_wallet/exchange/limits.dart';
|
||||
import 'package:cake_wallet/exchange/exchange_provider_description.dart';
|
||||
import 'package:cake_wallet/.secrets.g.dart' as secrets;
|
||||
import 'package:cake_wallet/exchange/exchange_provider_description.dart';
|
||||
import 'package:cake_wallet/exchange/limits.dart';
|
||||
import 'package:cake_wallet/exchange/provider/exchange_provider.dart';
|
||||
import 'package:cake_wallet/exchange/trade.dart';
|
||||
import 'package:cake_wallet/exchange/trade_request.dart';
|
||||
import 'package:cake_wallet/exchange/trade_state.dart';
|
||||
import 'package:cake_wallet/exchange/utils/currency_pairs_utils.dart';
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
import 'package:http/http.dart';
|
||||
|
||||
class TrocadorExchangeProvider extends ExchangeProvider {
|
||||
TrocadorExchangeProvider({this.useTorOnly = false})
|
||||
: _lastUsedRateId = '',
|
||||
super(pairList: _supportedPairs());
|
||||
super(pairList: supportedPairs(_notSupported));
|
||||
|
||||
bool useTorOnly;
|
||||
|
||||
@@ -24,135 +23,58 @@ class TrocadorExchangeProvider extends ExchangeProvider {
|
||||
CryptoCurrency.zaddr,
|
||||
];
|
||||
|
||||
static List<ExchangePair> _supportedPairs() {
|
||||
final supportedCurrencies =
|
||||
CryptoCurrency.all.where((element) => !_notSupported.contains(element)).toList();
|
||||
|
||||
return supportedCurrencies
|
||||
.map((i) => supportedCurrencies.map((k) => ExchangePair(from: i, to: k, reverse: true)))
|
||||
.expand((i) => i)
|
||||
.toList();
|
||||
}
|
||||
|
||||
static const apiKey = secrets.trocadorApiKey;
|
||||
static const onionApiAuthority = 'trocadorfyhlu27aefre5u7zri66gudtzdyelymftvr4yjwcxhfaqsid.onion';
|
||||
static const clearNetAuthority = 'trocador.app';
|
||||
static const apiKey = secrets.trocadorApiKey;
|
||||
static const markup = secrets.trocadorExchangeMarkup;
|
||||
static const newRatePath = '/api/new_rate';
|
||||
static const createTradePath = 'api/new_trade';
|
||||
static const tradePath = 'api/trade';
|
||||
static const coinPath = 'api/coin';
|
||||
|
||||
String _lastUsedRateId;
|
||||
|
||||
@override
|
||||
Future<bool> checkIsAvailable() async => true;
|
||||
String get title => 'Trocador';
|
||||
|
||||
@override
|
||||
Future<Trade> createTrade({required TradeRequest request, required bool isFixedRateMode}) {
|
||||
final _request = request as TrocadorRequest;
|
||||
return _createTrade(request: _request, isFixedRateMode: isFixedRateMode);
|
||||
}
|
||||
bool get isAvailable => true;
|
||||
|
||||
Future<Trade> _createTrade({
|
||||
required TrocadorRequest request,
|
||||
required bool isFixedRateMode,
|
||||
}) async {
|
||||
final params = <String, String>{
|
||||
'api_key': apiKey,
|
||||
'ticker_from': _normalizeCurrency(request.from),
|
||||
'ticker_to': _normalizeCurrency(request.to),
|
||||
'network_from': _networkFor(request.from),
|
||||
'network_to': _networkFor(request.to),
|
||||
'payment': isFixedRateMode ? 'True' : 'False',
|
||||
'min_kycrating': 'C',
|
||||
'markup': markup,
|
||||
'best_only': 'True',
|
||||
if (!isFixedRateMode) 'amount_from': request.fromAmount,
|
||||
if (isFixedRateMode) 'amount_to': request.toAmount,
|
||||
'address': request.address,
|
||||
'refund': request.refundAddress
|
||||
};
|
||||
@override
|
||||
bool get isEnabled => true;
|
||||
|
||||
if (isFixedRateMode) {
|
||||
await fetchRate(
|
||||
from: request.from,
|
||||
to: request.to,
|
||||
amount: double.tryParse(request.toAmount) ?? 0,
|
||||
isFixedRateMode: true,
|
||||
isReceiveAmount: true,
|
||||
);
|
||||
params['id'] = _lastUsedRateId;
|
||||
}
|
||||
@override
|
||||
bool get supportsFixedRate => true;
|
||||
|
||||
final uri = await _getUri(createTradePath, params);
|
||||
final response = await get(uri);
|
||||
|
||||
if (response.statusCode == 400) {
|
||||
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
|
||||
final error = responseJSON['error'] as String;
|
||||
final message = responseJSON['message'] as String;
|
||||
throw Exception('${error}\n$message');
|
||||
}
|
||||
|
||||
if (response.statusCode != 200) {
|
||||
throw Exception('Unexpected http status: ${response.statusCode}');
|
||||
}
|
||||
|
||||
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
|
||||
final id = responseJSON['trade_id'] as String;
|
||||
final inputAddress = responseJSON['address_provider'] as String;
|
||||
final refundAddress = responseJSON['refund_address'] as String;
|
||||
final status = responseJSON['status'] as String;
|
||||
final state = TradeState.deserialize(raw: status);
|
||||
final payoutAddress = responseJSON['address_user'] as String;
|
||||
final date = responseJSON['date'] as String;
|
||||
final password = responseJSON['password'] as String;
|
||||
final providerId = responseJSON['id_provider'] as String;
|
||||
final providerName = responseJSON['provider'] as String;
|
||||
|
||||
return Trade(
|
||||
id: id,
|
||||
from: request.from,
|
||||
to: request.to,
|
||||
provider: description,
|
||||
inputAddress: inputAddress,
|
||||
refundAddress: refundAddress,
|
||||
state: state,
|
||||
password: password,
|
||||
providerId: providerId,
|
||||
providerName: providerName,
|
||||
createdAt: DateTime.tryParse(date)?.toLocal(),
|
||||
amount: responseJSON['amount_from']?.toString() ?? request.fromAmount,
|
||||
payoutAddress: payoutAddress);
|
||||
}
|
||||
@override
|
||||
bool get supportsOnionAddress => true;
|
||||
|
||||
@override
|
||||
ExchangeProviderDescription get description => ExchangeProviderDescription.trocador;
|
||||
|
||||
@override
|
||||
Future<bool> checkIsAvailable() async => true;
|
||||
|
||||
@override
|
||||
Future<Limits> fetchLimits(
|
||||
{required CryptoCurrency from,
|
||||
required CryptoCurrency to,
|
||||
required bool isFixedRateMode}) async {
|
||||
final params = <String, String>{
|
||||
final params = {
|
||||
'api_key': apiKey,
|
||||
'ticker': _normalizeCurrency(from),
|
||||
'name': from.name,
|
||||
};
|
||||
|
||||
final uri = await _getUri(coinPath, params);
|
||||
|
||||
final response = await get(uri);
|
||||
|
||||
if (response.statusCode != 200) {
|
||||
if (response.statusCode != 200)
|
||||
throw Exception('Unexpected http status: ${response.statusCode}');
|
||||
}
|
||||
|
||||
final responseJSON = json.decode(response.body) as List<dynamic>;
|
||||
|
||||
if (responseJSON.isEmpty) {
|
||||
throw Exception('No data');
|
||||
}
|
||||
if (responseJSON.isEmpty) throw Exception('No data');
|
||||
|
||||
final coinJson = responseJSON.first as Map<String, dynamic>;
|
||||
|
||||
@@ -170,9 +92,7 @@ class TrocadorExchangeProvider extends ExchangeProvider {
|
||||
required bool isFixedRateMode,
|
||||
required bool isReceiveAmount}) async {
|
||||
try {
|
||||
if (amount == 0) {
|
||||
return 0.0;
|
||||
}
|
||||
if (amount == 0) return 0.0;
|
||||
|
||||
final params = <String, String>{
|
||||
'api_key': apiKey,
|
||||
@@ -195,9 +115,7 @@ class TrocadorExchangeProvider extends ExchangeProvider {
|
||||
final toAmount = double.parse(responseJSON['amount_to'].toString());
|
||||
final rateId = responseJSON['trade_id'] as String? ?? '';
|
||||
|
||||
if (rateId.isNotEmpty) {
|
||||
_lastUsedRateId = rateId;
|
||||
}
|
||||
if (rateId.isNotEmpty) _lastUsedRateId = rateId;
|
||||
|
||||
return isReceiveAmount ? (amount / fromAmount) : (toAmount / amount);
|
||||
} catch (e) {
|
||||
@@ -206,40 +124,103 @@ class TrocadorExchangeProvider extends ExchangeProvider {
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Trade> createTrade({required TradeRequest request, required bool isFixedRateMode}) async {
|
||||
final params = {
|
||||
'api_key': apiKey,
|
||||
'ticker_from': _normalizeCurrency(request.fromCurrency),
|
||||
'ticker_to': _normalizeCurrency(request.toCurrency),
|
||||
'network_from': _networkFor(request.fromCurrency),
|
||||
'network_to': _networkFor(request.toCurrency),
|
||||
'payment': isFixedRateMode ? 'True' : 'False',
|
||||
'min_kycrating': 'C',
|
||||
'markup': markup,
|
||||
'best_only': 'True',
|
||||
if (!isFixedRateMode) 'amount_from': request.fromAmount,
|
||||
if (isFixedRateMode) 'amount_to': request.toAmount,
|
||||
'address': request.toAddress,
|
||||
'refund': request.refundAddress
|
||||
};
|
||||
|
||||
if (isFixedRateMode) {
|
||||
await fetchRate(
|
||||
from: request.fromCurrency,
|
||||
to: request.toCurrency,
|
||||
amount: double.tryParse(request.toAmount) ?? 0,
|
||||
isFixedRateMode: true,
|
||||
isReceiveAmount: true,
|
||||
);
|
||||
params['id'] = _lastUsedRateId;
|
||||
}
|
||||
|
||||
final uri = await _getUri(createTradePath, params);
|
||||
final response = await get(uri);
|
||||
|
||||
if (response.statusCode == 400) {
|
||||
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
|
||||
final error = responseJSON['error'] as String;
|
||||
final message = responseJSON['message'] as String;
|
||||
throw Exception('${error}\n$message');
|
||||
}
|
||||
|
||||
if (response.statusCode != 200)
|
||||
throw Exception('Unexpected http status: ${response.statusCode}');
|
||||
|
||||
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
|
||||
final id = responseJSON['trade_id'] as String;
|
||||
final inputAddress = responseJSON['address_provider'] as String;
|
||||
final refundAddress = responseJSON['refund_address'] as String;
|
||||
final status = responseJSON['status'] as String;
|
||||
final payoutAddress = responseJSON['address_user'] as String;
|
||||
final date = responseJSON['date'] as String;
|
||||
final password = responseJSON['password'] as String;
|
||||
final providerId = responseJSON['id_provider'] as String;
|
||||
final providerName = responseJSON['provider'] as String;
|
||||
|
||||
return Trade(
|
||||
id: id,
|
||||
from: request.fromCurrency,
|
||||
to: request.toCurrency,
|
||||
provider: description,
|
||||
inputAddress: inputAddress,
|
||||
refundAddress: refundAddress,
|
||||
state: TradeState.deserialize(raw: status),
|
||||
password: password,
|
||||
providerId: providerId,
|
||||
providerName: providerName,
|
||||
createdAt: DateTime.tryParse(date)?.toLocal(),
|
||||
amount: responseJSON['amount_from']?.toString() ?? request.fromAmount,
|
||||
payoutAddress: payoutAddress);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Trade> findTradeById({required String id}) async {
|
||||
final uri = await _getUri(tradePath, {'api_key': apiKey, 'id': id});
|
||||
return get(uri).then((response) {
|
||||
if (response.statusCode != 200) {
|
||||
if (response.statusCode != 200)
|
||||
throw Exception('Unexpected http status: ${response.statusCode}');
|
||||
}
|
||||
|
||||
final responseListJson = json.decode(response.body) as List;
|
||||
|
||||
final responseJSON = responseListJson.first;
|
||||
final id = responseJSON['trade_id'] as String;
|
||||
final payoutAddress = responseJSON['address_user'] as String;
|
||||
final refundAddress = responseJSON['refund_address'] as String;
|
||||
final inputAddress = responseJSON['address_provider'] as String;
|
||||
final fromAmount = responseJSON['amount_from']?.toString() ?? '0';
|
||||
final from = CryptoCurrency.fromString(responseJSON['ticker_from'] as String);
|
||||
final to = CryptoCurrency.fromString(responseJSON['ticker_to'] as String);
|
||||
final state = TradeState.deserialize(raw: responseJSON['status'] as String);
|
||||
final date = DateTime.parse(responseJSON['date'] as String);
|
||||
final password = responseJSON['password'] as String;
|
||||
final providerId = responseJSON['id_provider'] as String;
|
||||
final providerName = responseJSON['provider'] as String;
|
||||
|
||||
return Trade(
|
||||
id: id,
|
||||
from: from,
|
||||
to: to,
|
||||
from: CryptoCurrency.fromString(responseJSON['ticker_from'] as String),
|
||||
to: CryptoCurrency.fromString(responseJSON['ticker_to'] as String),
|
||||
provider: description,
|
||||
inputAddress: inputAddress,
|
||||
refundAddress: refundAddress,
|
||||
createdAt: date,
|
||||
createdAt: DateTime.parse(responseJSON['date'] as String),
|
||||
amount: fromAmount,
|
||||
state: state,
|
||||
state: TradeState.deserialize(raw: responseJSON['status'] as String),
|
||||
payoutAddress: payoutAddress,
|
||||
password: password,
|
||||
providerId: providerId,
|
||||
@@ -248,21 +229,6 @@ class TrocadorExchangeProvider extends ExchangeProvider {
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
bool get isAvailable => true;
|
||||
|
||||
@override
|
||||
bool get isEnabled => true;
|
||||
|
||||
@override
|
||||
bool get supportsFixedRate => true;
|
||||
|
||||
@override
|
||||
bool get supportsOnionAddress => true;
|
||||
|
||||
@override
|
||||
String get title => 'Trocador';
|
||||
|
||||
String _networkFor(CryptoCurrency currency) {
|
||||
switch (currency) {
|
||||
case CryptoCurrency.eth:
|
||||
@@ -301,15 +267,9 @@ class TrocadorExchangeProvider extends ExchangeProvider {
|
||||
}
|
||||
|
||||
Future<Uri> _getUri(String path, Map<String, String> queryParams) async {
|
||||
if (!supportsOnionAddress) {
|
||||
return Uri.https(clearNetAuthority, path, queryParams);
|
||||
}
|
||||
|
||||
final uri = Uri.http(onionApiAuthority, path, queryParams);
|
||||
|
||||
if (useTorOnly) {
|
||||
return uri;
|
||||
}
|
||||
if (useTorOnly) return uri;
|
||||
|
||||
try {
|
||||
await get(uri);
|
||||
@@ -1,17 +0,0 @@
|
||||
import 'package:cake_wallet/exchange/trade_request.dart';
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
|
||||
class SideShiftRequest extends TradeRequest {
|
||||
SideShiftRequest(
|
||||
{required this.depositMethod,
|
||||
required this.settleMethod,
|
||||
required this.depositAmount,
|
||||
required this.settleAddress,
|
||||
required this.refundAddress});
|
||||
|
||||
final CryptoCurrency depositMethod;
|
||||
final CryptoCurrency settleMethod;
|
||||
final String depositAmount;
|
||||
final String settleAddress;
|
||||
final String refundAddress;
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
import 'package:cake_wallet/exchange/trade_request.dart';
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class SimpleSwapRequest extends TradeRequest {
|
||||
SimpleSwapRequest({
|
||||
required this.from,
|
||||
required this.to,
|
||||
required this.address,
|
||||
required this.amount,
|
||||
required this.refundAddress,
|
||||
this.toAmount = ''
|
||||
});
|
||||
|
||||
CryptoCurrency from;
|
||||
CryptoCurrency to;
|
||||
String address;
|
||||
String amount;
|
||||
String toAmount;
|
||||
String refundAddress;
|
||||
}
|
||||
+8
-13
@@ -1,6 +1,6 @@
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
import 'package:cake_wallet/exchange/exchange_provider_description.dart';
|
||||
import 'package:cake_wallet/exchange/trade_state.dart';
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
import 'package:cw_core/format_amount.dart';
|
||||
import 'package:cw_core/hive_type_ids.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
@@ -28,18 +28,13 @@ class Trade extends HiveObject {
|
||||
this.providerId,
|
||||
this.providerName,
|
||||
}) {
|
||||
if (provider != null) {
|
||||
providerRaw = provider.raw;
|
||||
}
|
||||
if (from != null) {
|
||||
fromRaw = from.raw;
|
||||
}
|
||||
if (to != null) {
|
||||
toRaw = to.raw;
|
||||
}
|
||||
if (state != null) {
|
||||
stateRaw = state.raw;
|
||||
}
|
||||
if (provider != null) providerRaw = provider.raw;
|
||||
|
||||
if (from != null) fromRaw = from.raw;
|
||||
|
||||
if (to != null) toRaw = to.raw;
|
||||
|
||||
if (state != null) stateRaw = state.raw;
|
||||
}
|
||||
|
||||
static const typeId = TRADE_TYPE_ID;
|
||||
|
||||
+1
-8
@@ -8,12 +8,5 @@ class TradeNotCreatedException implements Exception {
|
||||
String description;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
var text = provider != null
|
||||
? S.current.trade_for_not_created(provider.title)
|
||||
: S.current.trade_not_created;
|
||||
text += ' $description';
|
||||
|
||||
return text;
|
||||
}
|
||||
String toString() => '${S.current.trade_for_not_created(provider.title)} $description';
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
import 'package:cake_wallet/exchange/exchange_provider_description.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
|
||||
class TradeNotFoundException implements Exception {
|
||||
TradeNotFoundException(this.tradeId, {required this.provider, this.description = ''});
|
||||
|
||||
String tradeId;
|
||||
ExchangeProviderDescription provider;
|
||||
String description;
|
||||
|
||||
@override
|
||||
String toString() => '${S.current.trade_id_not_found(tradeId, provider.title)} $description';
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
import 'package:cake_wallet/exchange/exchange_provider_description.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
|
||||
class TradeNotFoundException implements Exception {
|
||||
TradeNotFoundException(this.tradeId, {this.provider, this.description = ''});
|
||||
|
||||
String? tradeId;
|
||||
ExchangeProviderDescription? provider;
|
||||
String description;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
var text = tradeId != null && provider != null
|
||||
? S.current.trade_id_not_found(tradeId!, provider!.title)
|
||||
: S.current.trade_not_found;
|
||||
text += ' $description';
|
||||
|
||||
return text;
|
||||
}
|
||||
}
|
||||
@@ -1 +1,20 @@
|
||||
abstract class TradeRequest {}
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
|
||||
class TradeRequest {
|
||||
TradeRequest(
|
||||
{required this.fromCurrency,
|
||||
required this.toCurrency,
|
||||
required this.toAddress,
|
||||
required this.refundAddress,
|
||||
required this.fromAmount,
|
||||
this.toAmount = '',
|
||||
this.isFixedRate = false});
|
||||
|
||||
final CryptoCurrency fromCurrency;
|
||||
final CryptoCurrency toCurrency;
|
||||
final String toAddress;
|
||||
final String refundAddress;
|
||||
final String fromAmount;
|
||||
final String toAmount;
|
||||
final bool isFixedRate;
|
||||
}
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:cw_core/enumerable_item.dart';
|
||||
|
||||
class TradeState extends EnumerableItem<String> with Serializable<String> {
|
||||
const TradeState({required String raw, required String title})
|
||||
: super(raw: raw, title: title);
|
||||
const TradeState({required String raw, required String title}) : super(raw: raw, title: title);
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) => other is TradeState && other.raw == raw;
|
||||
@@ -13,12 +11,10 @@ class TradeState extends EnumerableItem<String> with Serializable<String> {
|
||||
static const trading = TradeState(raw: 'trading', title: 'Trading');
|
||||
static const traded = TradeState(raw: 'traded', title: 'Traded');
|
||||
static const complete = TradeState(raw: 'complete', title: 'Complete');
|
||||
static const toBeCreated =
|
||||
TradeState(raw: 'TO_BE_CREATED', title: 'To be created');
|
||||
static const toBeCreated = TradeState(raw: 'TO_BE_CREATED', title: 'To be created');
|
||||
static const unpaid = TradeState(raw: 'UNPAID', title: 'Unpaid');
|
||||
static const underpaid = TradeState(raw: 'UNDERPAID', title: 'Underpaid');
|
||||
static const paidUnconfirmed =
|
||||
TradeState(raw: 'PAID_UNCONFIRMED', title: 'Paid unconfirmed');
|
||||
static const paidUnconfirmed = TradeState(raw: 'PAID_UNCONFIRMED', title: 'Paid unconfirmed');
|
||||
static const paid = TradeState(raw: 'PAID', title: 'Paid');
|
||||
static const btcSent = TradeState(raw: 'BTC_SENT', title: 'Btc sent');
|
||||
static const timeout = TradeState(raw: 'TIMED_OUT', title: 'Timeout');
|
||||
@@ -27,8 +23,7 @@ class TradeState extends EnumerableItem<String> with Serializable<String> {
|
||||
static const finished = TradeState(raw: 'finished', title: 'Finished');
|
||||
static const waiting = TradeState(raw: 'waiting', title: 'Waiting');
|
||||
static const processing = TradeState(raw: 'processing', title: 'Processing');
|
||||
static const waitingPayment =
|
||||
TradeState(raw: 'waitingPayment', title: 'Waiting payment');
|
||||
static const waitingPayment = TradeState(raw: 'waitingPayment', title: 'Waiting payment');
|
||||
static const waitingAuthorization =
|
||||
TradeState(raw: 'waitingAuthorization', title: 'Waiting authorization');
|
||||
static const failed = TradeState(raw: 'failed', title: 'Failed');
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
import 'package:cake_wallet/exchange/trade_request.dart';
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
|
||||
class TrocadorRequest extends TradeRequest {
|
||||
TrocadorRequest(
|
||||
{required this.from,
|
||||
required this.to,
|
||||
required this.address,
|
||||
required this.fromAmount,
|
||||
required this.toAmount,
|
||||
required this.refundAddress,
|
||||
required this.isReverse});
|
||||
|
||||
CryptoCurrency from;
|
||||
CryptoCurrency to;
|
||||
String address;
|
||||
String fromAmount;
|
||||
String toAmount;
|
||||
String refundAddress;
|
||||
bool isReverse;
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
import 'package:cake_wallet/exchange/exchange_pair.dart';
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
|
||||
List<ExchangePair> supportedPairs(List<CryptoCurrency> notSupported) {
|
||||
final supportedCurrencies =
|
||||
CryptoCurrency.all.where((element) => !notSupported.contains(element)).toList();
|
||||
|
||||
return supportedCurrencies
|
||||
.map((i) => supportedCurrencies.map((k) => ExchangePair(from: i, to: k, reverse: true)))
|
||||
.expand((i) => i)
|
||||
.toList();
|
||||
}
|
||||
@@ -1,236 +0,0 @@
|
||||
import 'dart:convert';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:http/http.dart';
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
import 'package:cake_wallet/exchange/exchange_pair.dart';
|
||||
import 'package:cake_wallet/exchange/exchange_provider.dart';
|
||||
import 'package:cake_wallet/exchange/limits.dart';
|
||||
import 'package:cake_wallet/exchange/trade.dart';
|
||||
import 'package:cake_wallet/exchange/trade_request.dart';
|
||||
import 'package:cake_wallet/exchange/trade_state.dart';
|
||||
import 'package:cake_wallet/exchange/xmrto/xmrto_trade_request.dart';
|
||||
import 'package:cake_wallet/exchange/trade_not_created_exeption.dart';
|
||||
import 'package:cake_wallet/exchange/exchange_provider_description.dart';
|
||||
import 'package:cake_wallet/exchange/trade_not_found_exeption.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
|
||||
class XMRTOExchangeProvider extends ExchangeProvider {
|
||||
XMRTOExchangeProvider()
|
||||
: _isAvailable = false,
|
||||
super(pairList: [
|
||||
ExchangePair(
|
||||
from: CryptoCurrency.xmr, to: CryptoCurrency.btc, reverse: false)
|
||||
]);
|
||||
|
||||
static const userAgent = 'CakeWallet/XMR iOS';
|
||||
static const originalApiUri = 'https://xmr.to/api/v3/xmr2btc';
|
||||
static const _orderParameterUriSuffix = '/order_parameter_query';
|
||||
static const _orderStatusUriSuffix = '/order_status_query/';
|
||||
static const _orderCreateUriSuffix = '/order_create/';
|
||||
static const _headers = {
|
||||
'Content-Type': 'application/json',
|
||||
'User-Agent': userAgent
|
||||
};
|
||||
|
||||
static Future<bool> _checkIsAvailable() async {
|
||||
const url = originalApiUri + _orderParameterUriSuffix;
|
||||
final uri = Uri.parse(url);
|
||||
final response = await get(uri, headers: _headers);
|
||||
return !(response.statusCode == 403);
|
||||
}
|
||||
|
||||
@override
|
||||
String get title => 'XMR.TO';
|
||||
|
||||
@override
|
||||
bool get isAvailable => _isAvailable;
|
||||
|
||||
@override
|
||||
bool get isEnabled => true;
|
||||
|
||||
@override
|
||||
bool get supportsFixedRate => false;
|
||||
|
||||
@override
|
||||
ExchangeProviderDescription get description =>
|
||||
ExchangeProviderDescription.xmrto;
|
||||
|
||||
double _rate = 0;
|
||||
bool _isAvailable;
|
||||
|
||||
@override
|
||||
Future<bool> checkIsAvailable() async {
|
||||
_isAvailable = await _checkIsAvailable();
|
||||
return isAvailable;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Limits> fetchLimits({
|
||||
required CryptoCurrency from,
|
||||
required CryptoCurrency to,
|
||||
required bool isFixedRateMode}) async {
|
||||
final url = originalApiUri + _orderParameterUriSuffix;
|
||||
final uri = Uri.parse(url);
|
||||
final response = await get(uri);
|
||||
final correction = 0.001;
|
||||
|
||||
if (response.statusCode != 200) {
|
||||
return Limits(min: 0, max: 0);
|
||||
}
|
||||
|
||||
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
|
||||
double min = double.parse(responseJSON['lower_limit'] as String);
|
||||
double max = double.parse(responseJSON['upper_limit'] as String);
|
||||
final price = double.parse(responseJSON['price'] as String);
|
||||
|
||||
if (price > 0) {
|
||||
try {
|
||||
min = min / price + correction;
|
||||
min = _limitsFormat(min);
|
||||
max = max / price - correction;
|
||||
max = _limitsFormat(max);
|
||||
} catch (e) {
|
||||
min = 0;
|
||||
max = 0;
|
||||
}
|
||||
} else {
|
||||
min = 0;
|
||||
max = 0;
|
||||
}
|
||||
|
||||
return Limits(min: min, max: max);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Trade> createTrade({required TradeRequest request, required bool isFixedRateMode}) async {
|
||||
final _request = request as XMRTOTradeRequest;
|
||||
final url = originalApiUri + _orderCreateUriSuffix;
|
||||
final _amount =
|
||||
_request.isBTCRequest ? _request.receiveAmount : _request.amount;
|
||||
final _amountCurrency = _request.isBTCRequest
|
||||
? _request.to.toString()
|
||||
: _request.from.toString();
|
||||
final pattern = '^([0-9]+([.\,][0-9]{0,8})?|[.\,][0-9]{1,8})\$';
|
||||
final isValid = RegExp(pattern).hasMatch(_amount);
|
||||
|
||||
if (!isValid) {
|
||||
throw TradeNotCreatedException(description,
|
||||
description: S.current.xmr_to_error_description);
|
||||
}
|
||||
|
||||
final body = {
|
||||
'amount': _amount,
|
||||
'amount_currency': _amountCurrency,
|
||||
'btc_dest_address': _request.address};
|
||||
final uri = Uri.parse(url);
|
||||
final response =
|
||||
await post(uri, headers: _headers, body: json.encode(body));
|
||||
|
||||
if (response.statusCode != 201) {
|
||||
if (response.statusCode == 400) {
|
||||
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
|
||||
final error = responseJSON['error_msg'] as String;
|
||||
|
||||
throw TradeNotCreatedException(description, description: error);
|
||||
}
|
||||
|
||||
throw TradeNotCreatedException(description);
|
||||
}
|
||||
|
||||
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
|
||||
final uuid = responseJSON["uuid"] as String;
|
||||
|
||||
return Trade(
|
||||
id: uuid,
|
||||
provider: description,
|
||||
from: _request.from,
|
||||
to: _request.to,
|
||||
state: TradeState.created,
|
||||
amount: _request.amount,
|
||||
createdAt: DateTime.now());
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Trade> findTradeById({required String id}) async {
|
||||
final url = originalApiUri + _orderStatusUriSuffix;
|
||||
final uri = Uri.parse(url);
|
||||
final body = {'uuid': id};
|
||||
final response =
|
||||
await post(uri, headers: _headers, body: json.encode(body));
|
||||
|
||||
if (response.statusCode != 200) {
|
||||
if (response.statusCode == 400) {
|
||||
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
|
||||
final error = responseJSON['error_msg'] as String;
|
||||
|
||||
throw TradeNotFoundException(id,
|
||||
provider: description, description: error);
|
||||
}
|
||||
|
||||
throw TradeNotFoundException(id, provider: description);
|
||||
}
|
||||
|
||||
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
|
||||
final address = responseJSON['receiving_subaddress'] as String;
|
||||
final paymentId = responseJSON['xmr_required_payment_id_short'] as String;
|
||||
final amount = responseJSON['incoming_amount_total'].toString();
|
||||
final stateRaw = responseJSON['state'] as String;
|
||||
final expiredAtRaw = responseJSON['expires_at'] as String;
|
||||
final expiredAt = DateTime.parse(expiredAtRaw).toLocal();
|
||||
final outputTransaction = responseJSON['btc_transaction_id'] as String;
|
||||
final state = TradeState.deserialize(raw: stateRaw);
|
||||
|
||||
return Trade(
|
||||
id: id,
|
||||
provider: description,
|
||||
from: CryptoCurrency.xmr,
|
||||
to: CryptoCurrency.btc,
|
||||
inputAddress: address,
|
||||
extraId: paymentId,
|
||||
expiredAt: expiredAt,
|
||||
amount: amount,
|
||||
state: state,
|
||||
outputTransaction: outputTransaction);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<double> fetchRate(
|
||||
{required CryptoCurrency from,
|
||||
required CryptoCurrency to,
|
||||
required double amount,
|
||||
required bool isFixedRateMode,
|
||||
required bool isReceiveAmount}) async {
|
||||
if (from != CryptoCurrency.xmr && to != CryptoCurrency.btc) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (_rate == 0) {
|
||||
_rate = await _fetchRates();
|
||||
}
|
||||
|
||||
final double result = isReceiveAmount
|
||||
? _rate == 0
|
||||
? 0
|
||||
: amount / _rate
|
||||
: _rate * amount;
|
||||
|
||||
return double.parse(result.toStringAsFixed(12));
|
||||
}
|
||||
|
||||
Future<double> _fetchRates() async {
|
||||
try {
|
||||
final url = originalApiUri + _orderParameterUriSuffix;
|
||||
final uri = Uri.parse(url);
|
||||
final response = await get(uri, headers: _headers);
|
||||
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
|
||||
final price = double.parse(responseJSON['price'] as String);
|
||||
|
||||
return price;
|
||||
} catch (e) {
|
||||
print(e.toString());
|
||||
return 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
double _limitsFormat(double limit) => double.parse(limit.toStringAsFixed(3));
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
import 'package:cake_wallet/exchange/trade_request.dart';
|
||||
|
||||
class XMRTOTradeRequest extends TradeRequest {
|
||||
XMRTOTradeRequest(
|
||||
{required this.from,
|
||||
required this.to,
|
||||
required this.amount,
|
||||
required this.receiveAmount,
|
||||
required this.address,
|
||||
required this.refundAddress,
|
||||
required this.isBTCRequest});
|
||||
|
||||
final CryptoCurrency from;
|
||||
final CryptoCurrency to;
|
||||
final String amount;
|
||||
final String receiveAmount;
|
||||
final String address;
|
||||
final String refundAddress;
|
||||
final bool isBTCRequest;
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
import 'package:cake_wallet/themes/extensions/exchange_page_theme.dart';
|
||||
import 'package:cake_wallet/themes/extensions/keyboard_theme.dart';
|
||||
import 'package:cake_wallet/exchange/exchange_provider.dart';
|
||||
import 'package:cake_wallet/exchange/provider/exchange_provider.dart';
|
||||
import 'package:cake_wallet/src/screens/base_page.dart';
|
||||
import 'package:cake_wallet/src/widgets/keyboard_done_button.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
@@ -9,9 +9,6 @@ import 'package:keyboard_actions/keyboard_actions.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
import 'package:cake_wallet/exchange/xmrto/xmrto_exchange_provider.dart';
|
||||
// import 'package:cake_wallet/exchange/exchange_trade_state.dart';
|
||||
// import 'package:cake_wallet/exchange/limits_state.dart';
|
||||
import 'package:cake_wallet/src/screens/exchange/widgets/exchange_card.dart';
|
||||
import 'package:cake_wallet/src/widgets/primary_button.dart';
|
||||
import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart';
|
||||
@@ -176,9 +173,7 @@ class ExchangeTemplatePage extends BasePage {
|
||||
exchangeViewModel.wallet.currency
|
||||
? exchangeViewModel.wallet.walletAddresses.address
|
||||
: exchangeViewModel.receiveAddress,
|
||||
initialIsAmountEditable:
|
||||
exchangeViewModel.provider is
|
||||
XMRTOExchangeProvider ? true : false,
|
||||
initialIsAmountEditable: false,
|
||||
initialIsAddressEditable:
|
||||
exchangeViewModel.isReceiveAddressEnabled,
|
||||
isAmountEstimated: true,
|
||||
@@ -205,26 +200,25 @@ class ExchangeTemplatePage extends BasePage {
|
||||
bottomSectionPadding:
|
||||
EdgeInsets.only(left: 24, right: 24, bottom: 24),
|
||||
bottomSection: Column(children: <Widget>[
|
||||
Padding(
|
||||
padding: EdgeInsets.only(bottom: 15),
|
||||
child: Observer(builder: (_) {
|
||||
final description =
|
||||
exchangeViewModel.provider is XMRTOExchangeProvider
|
||||
? S.of(context).amount_is_guaranteed
|
||||
: S.of(context).amount_is_estimate;
|
||||
return Center(
|
||||
child: Text(
|
||||
description,
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).extension<ExchangePageTheme>()!.receiveAmountColor,
|
||||
fontWeight: FontWeight.w500,
|
||||
fontSize: 12),
|
||||
Padding(
|
||||
padding: EdgeInsets.only(bottom: 15),
|
||||
child: Observer(
|
||||
builder: (_) => Center(
|
||||
child: Text(
|
||||
S.of(context).amount_is_estimate,
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
color: Theme.of(context)
|
||||
.extension<ExchangePageTheme>()!
|
||||
.receiveAmountColor,
|
||||
fontWeight: FontWeight.w500,
|
||||
fontSize: 12,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}),
|
||||
),
|
||||
PrimaryButton(
|
||||
),
|
||||
PrimaryButton(
|
||||
onPressed: () {
|
||||
if (_formKey.currentState != null && _formKey.currentState!.validate()) {
|
||||
exchangeViewModel.addTemplate(
|
||||
@@ -340,9 +334,7 @@ class ExchangeTemplatePage extends BasePage {
|
||||
});
|
||||
|
||||
reaction((_) => exchangeViewModel.provider, (ExchangeProvider? provider) {
|
||||
provider is XMRTOExchangeProvider
|
||||
? receiveKey.currentState!.isAmountEditable(isEditable: true)
|
||||
: receiveKey.currentState!.isAmountEditable(isEditable: false);
|
||||
receiveKey.currentState!.isAmountEditable(isEditable: false);
|
||||
});
|
||||
|
||||
/*reaction((_) => exchangeViewModel.limitsState, (LimitsState state) {
|
||||
|
||||
@@ -1,18 +1,19 @@
|
||||
import 'package:auto_size_text/auto_size_text.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/src/screens/base_page.dart';
|
||||
import 'package:cake_wallet/src/screens/trade_details/track_trade_list_item.dart';
|
||||
import 'package:cake_wallet/src/screens/trade_details/trade_details_list_card.dart';
|
||||
import 'package:cake_wallet/src/screens/trade_details/trade_details_status_item.dart';
|
||||
import 'package:cake_wallet/src/screens/trade_details/trade_provider_unsupported_item.dart';
|
||||
import 'package:cake_wallet/src/widgets/list_row.dart';
|
||||
import 'package:cake_wallet/src/widgets/standard_list.dart';
|
||||
import 'package:cake_wallet/src/widgets/standard_list_card.dart';
|
||||
import 'package:cake_wallet/src/widgets/standard_list_status_row.dart';
|
||||
import 'package:cake_wallet/utils/show_bar.dart';
|
||||
import 'package:cake_wallet/view_model/trade_details_view_model.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/src/screens/base_page.dart';
|
||||
import 'package:cake_wallet/src/widgets/list_row.dart';
|
||||
import 'package:cake_wallet/src/screens/trade_details/track_trade_list_item.dart';
|
||||
import 'package:cake_wallet/src/screens/trade_details/trade_details_list_card.dart';
|
||||
import 'package:cake_wallet/src/screens/trade_details/trade_details_status_item.dart';
|
||||
|
||||
class TradeDetailsPage extends BasePage {
|
||||
TradeDetailsPage(this.tradeDetailsViewModel);
|
||||
@@ -23,8 +24,7 @@ class TradeDetailsPage extends BasePage {
|
||||
final TradeDetailsViewModel tradeDetailsViewModel;
|
||||
|
||||
@override
|
||||
Widget body(BuildContext context) =>
|
||||
TradeDetailsPageBody(tradeDetailsViewModel);
|
||||
Widget body(BuildContext context) => TradeDetailsPageBody(tradeDetailsViewModel);
|
||||
}
|
||||
|
||||
class TradeDetailsPageBody extends StatefulWidget {
|
||||
@@ -33,8 +33,7 @@ class TradeDetailsPageBody extends StatefulWidget {
|
||||
final TradeDetailsViewModel tradeDetailsViewModel;
|
||||
|
||||
@override
|
||||
TradeDetailsPageBodyState createState() =>
|
||||
TradeDetailsPageBodyState(tradeDetailsViewModel);
|
||||
TradeDetailsPageBodyState createState() => TradeDetailsPageBodyState(tradeDetailsViewModel);
|
||||
}
|
||||
|
||||
class TradeDetailsPageBodyState extends State<TradeDetailsPageBody> {
|
||||
@@ -51,7 +50,7 @@ class TradeDetailsPageBodyState extends State<TradeDetailsPageBody> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Observer(builder: (_) {
|
||||
int itemsCount = tradeDetailsViewModel.items.length;
|
||||
final itemsCount = tradeDetailsViewModel.items.length;
|
||||
|
||||
return SectionStandardList(
|
||||
sectionCount: 1,
|
||||
@@ -59,37 +58,39 @@ class TradeDetailsPageBodyState extends State<TradeDetailsPageBody> {
|
||||
itemBuilder: (__, index) {
|
||||
final item = tradeDetailsViewModel.items[index];
|
||||
|
||||
if (item is TrackTradeListItem) {
|
||||
if (item is TrackTradeListItem)
|
||||
return GestureDetector(
|
||||
onTap: item.onTap,
|
||||
child: ListRow(
|
||||
title: '${item.title}', value: '${item.value}'));
|
||||
}
|
||||
child: ListRow(title: '${item.title}', value: '${item.value}'));
|
||||
|
||||
if (item is DetailsListStatusItem) {
|
||||
return StandardListStatusRow(
|
||||
title: item.title,
|
||||
value: item.value);
|
||||
}
|
||||
if (item is DetailsListStatusItem)
|
||||
return StandardListStatusRow(title: item.title, value: item.value);
|
||||
|
||||
if (item is TradeDetailsListCardItem) {
|
||||
if (item is TradeDetailsListCardItem)
|
||||
return TradeDetailsStandardListCard(
|
||||
id: item.id,
|
||||
create: item.createdAt,
|
||||
pair: item.pair,
|
||||
currentTheme: tradeDetailsViewModel.settingsStore.currentTheme.type,
|
||||
onTap: item.onTap,);
|
||||
}
|
||||
id: item.id,
|
||||
create: item.createdAt,
|
||||
pair: item.pair,
|
||||
currentTheme: tradeDetailsViewModel.settingsStore.currentTheme.type,
|
||||
onTap: item.onTap,
|
||||
);
|
||||
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
Clipboard.setData(ClipboardData(text: '${item.value}'));
|
||||
showBar<void>(context, S.of(context).copied_to_clipboard);
|
||||
},
|
||||
child: ListRow(
|
||||
title: '${item.title}', value: '${item.value}'));
|
||||
if (item is TradeProviderUnsupportedItem)
|
||||
return AutoSizeText(item.value,
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Colors.red,
|
||||
));
|
||||
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
Clipboard.setData(ClipboardData(text: '${item.value}'));
|
||||
showBar<void>(context, S.of(context).copied_to_clipboard);
|
||||
},
|
||||
child: ListRow(title: '${item.title}', value: '${item.value}'));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
import 'package:cake_wallet/src/screens/transaction_details/standart_list_item.dart';
|
||||
|
||||
class TradeProviderUnsupportedItem extends StandartListItem {
|
||||
TradeProviderUnsupportedItem({required String error}) : super(title: '', value: error);
|
||||
}
|
||||
@@ -1,22 +1,21 @@
|
||||
import 'dart:async';
|
||||
import 'package:cake_wallet/exchange/exolix/exolix_exchange_provider.dart';
|
||||
import 'package:cake_wallet/exchange/sideshift/sideshift_exchange_provider.dart';
|
||||
import 'package:cake_wallet/exchange/simpleswap/simpleswap_exchange_provider.dart';
|
||||
import 'package:cake_wallet/exchange/trocador/trocador_exchange_provider.dart';
|
||||
import 'package:cw_core/wallet_base.dart';
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
import 'package:cake_wallet/exchange/changenow/changenow_exchange_provider.dart';
|
||||
import 'package:cake_wallet/exchange/exchange_provider.dart';
|
||||
|
||||
import 'package:cake_wallet/exchange/exchange_provider_description.dart';
|
||||
import 'package:cake_wallet/exchange/morphtoken/morphtoken_exchange_provider.dart';
|
||||
import 'package:cake_wallet/exchange/provider/changenow_exchange_provider.dart';
|
||||
import 'package:cake_wallet/exchange/provider/exchange_provider.dart';
|
||||
import 'package:cake_wallet/exchange/provider/exolix_exchange_provider.dart';
|
||||
import 'package:cake_wallet/exchange/provider/sideshift_exchange_provider.dart';
|
||||
import 'package:cake_wallet/exchange/provider/simpleswap_exchange_provider.dart';
|
||||
import 'package:cake_wallet/exchange/provider/trocador_exchange_provider.dart';
|
||||
import 'package:cake_wallet/exchange/trade.dart';
|
||||
import 'package:cake_wallet/exchange/xmrto/xmrto_exchange_provider.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/src/screens/exchange_trade/exchange_trade_item.dart';
|
||||
import 'package:cake_wallet/store/dashboard/trades_store.dart';
|
||||
import 'package:cake_wallet/view_model/send/send_view_model.dart';
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
import 'package:cw_core/wallet_base.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
import 'package:cake_wallet/src/screens/exchange_trade/exchange_trade_item.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
|
||||
part 'exchange_trade_view_model.g.dart';
|
||||
|
||||
@@ -35,16 +34,10 @@ abstract class ExchangeTradeViewModelBase with Store {
|
||||
tradesStore.trade!.from.tag == CryptoCurrency.eth.title),
|
||||
items = ObservableList<ExchangeTradeItem>() {
|
||||
switch (trade.provider) {
|
||||
case ExchangeProviderDescription.xmrto:
|
||||
_provider = XMRTOExchangeProvider();
|
||||
break;
|
||||
case ExchangeProviderDescription.changeNow:
|
||||
_provider =
|
||||
ChangeNowExchangeProvider(settingsStore: sendViewModel.balanceViewModel.settingsStore);
|
||||
break;
|
||||
case ExchangeProviderDescription.morphToken:
|
||||
_provider = MorphTokenExchangeProvider(trades: trades);
|
||||
break;
|
||||
case ExchangeProviderDescription.sideShift:
|
||||
_provider = SideShiftExchangeProvider();
|
||||
break;
|
||||
@@ -60,8 +53,11 @@ abstract class ExchangeTradeViewModelBase with Store {
|
||||
}
|
||||
|
||||
_updateItems();
|
||||
_updateTrade();
|
||||
timer = Timer.periodic(Duration(seconds: 20), (_) async => _updateTrade());
|
||||
|
||||
if (_provider != null) {
|
||||
_updateTrade();
|
||||
timer = Timer.periodic(Duration(seconds: 20), (_) async => _updateTrade());
|
||||
}
|
||||
}
|
||||
|
||||
final WalletBase wallet;
|
||||
@@ -100,10 +96,8 @@ abstract class ExchangeTradeViewModelBase with Store {
|
||||
Timer? timer;
|
||||
|
||||
@action
|
||||
Future confirmSending() async {
|
||||
if (!isSendable) {
|
||||
return;
|
||||
}
|
||||
Future<void> confirmSending() async {
|
||||
if (!isSendable) return;
|
||||
|
||||
sendViewModel.clearOutputs();
|
||||
final output = sendViewModel.outputs.first;
|
||||
@@ -118,13 +112,10 @@ abstract class ExchangeTradeViewModelBase with Store {
|
||||
try {
|
||||
final updatedTrade = await _provider!.findTradeById(id: trade.id);
|
||||
|
||||
if (updatedTrade.createdAt == null && trade.createdAt != null) {
|
||||
if (updatedTrade.createdAt == null && trade.createdAt != null)
|
||||
updatedTrade.createdAt = trade.createdAt;
|
||||
}
|
||||
|
||||
if (updatedTrade.amount.isEmpty) {
|
||||
updatedTrade.amount = trade.amount;
|
||||
}
|
||||
if (updatedTrade.amount.isEmpty) updatedTrade.amount = trade.amount;
|
||||
|
||||
trade = updatedTrade;
|
||||
|
||||
|
||||
@@ -3,48 +3,39 @@ import 'dart:collection';
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:cake_wallet/bitcoin_cash/bitcoin_cash.dart';
|
||||
import 'package:cake_wallet/bitcoin/bitcoin.dart';
|
||||
import 'package:cake_wallet/core/wallet_change_listener_view_model.dart';
|
||||
import 'package:cake_wallet/entities/exchange_api_mode.dart';
|
||||
import 'package:cake_wallet/entities/preferences_key.dart';
|
||||
import 'package:cake_wallet/entities/wallet_contact.dart';
|
||||
import 'package:cake_wallet/ethereum/ethereum.dart';
|
||||
import 'package:cake_wallet/exchange/exolix/exolix_exchange_provider.dart';
|
||||
import 'package:cake_wallet/exchange/exolix/exolix_request.dart';
|
||||
import 'package:cake_wallet/exchange/sideshift/sideshift_exchange_provider.dart';
|
||||
import 'package:cake_wallet/exchange/sideshift/sideshift_request.dart';
|
||||
import 'package:cake_wallet/exchange/simpleswap/simpleswap_exchange_provider.dart';
|
||||
import 'package:cake_wallet/exchange/simpleswap/simpleswap_request.dart';
|
||||
import 'package:cake_wallet/exchange/trocador/trocador_exchange_provider.dart';
|
||||
import 'package:cake_wallet/exchange/trocador/trocador_request.dart';
|
||||
import 'package:cake_wallet/utils/feature_flag.dart';
|
||||
import 'package:cake_wallet/view_model/contact_list/contact_list_view_model.dart';
|
||||
import 'package:cw_core/transaction_priority.dart';
|
||||
import 'package:cake_wallet/store/app_store.dart';
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
import 'package:cw_core/sync_status.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
import 'package:cake_wallet/bitcoin/bitcoin.dart';
|
||||
import 'package:cake_wallet/monero/monero.dart';
|
||||
import 'package:cake_wallet/exchange/exchange_provider.dart';
|
||||
import 'package:cake_wallet/exchange/exchange_template.dart';
|
||||
import 'package:cake_wallet/exchange/exchange_trade_state.dart';
|
||||
import 'package:cake_wallet/exchange/limits.dart';
|
||||
import 'package:cake_wallet/exchange/trade.dart';
|
||||
import 'package:cake_wallet/exchange/limits_state.dart';
|
||||
import 'package:cake_wallet/exchange/provider/changenow_exchange_provider.dart';
|
||||
import 'package:cake_wallet/exchange/provider/exchange_provider.dart';
|
||||
import 'package:cake_wallet/exchange/provider/exolix_exchange_provider.dart';
|
||||
import 'package:cake_wallet/exchange/provider/sideshift_exchange_provider.dart';
|
||||
import 'package:cake_wallet/exchange/provider/simpleswap_exchange_provider.dart';
|
||||
import 'package:cake_wallet/exchange/provider/trocador_exchange_provider.dart';
|
||||
import 'package:cake_wallet/exchange/trade.dart';
|
||||
import 'package:cake_wallet/exchange/trade_request.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/monero/monero.dart';
|
||||
import 'package:cake_wallet/store/app_store.dart';
|
||||
import 'package:cake_wallet/store/dashboard/trades_store.dart';
|
||||
import 'package:cake_wallet/store/settings_store.dart';
|
||||
import 'package:cake_wallet/store/templates/exchange_template_store.dart';
|
||||
import 'package:cake_wallet/utils/feature_flag.dart';
|
||||
import 'package:cake_wallet/view_model/contact_list/contact_list_view_model.dart';
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
import 'package:cw_core/sync_status.dart';
|
||||
import 'package:cw_core/transaction_priority.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:cake_wallet/exchange/exchange_trade_state.dart';
|
||||
import 'package:cake_wallet/exchange/changenow/changenow_exchange_provider.dart';
|
||||
import 'package:cake_wallet/exchange/changenow/changenow_request.dart';
|
||||
import 'package:cake_wallet/exchange/trade_request.dart';
|
||||
import 'package:cake_wallet/exchange/xmrto/xmrto_exchange_provider.dart';
|
||||
import 'package:cake_wallet/exchange/xmrto/xmrto_trade_request.dart';
|
||||
import 'package:cake_wallet/exchange/morphtoken/morphtoken_exchange_provider.dart';
|
||||
import 'package:cake_wallet/exchange/morphtoken/morphtoken_request.dart';
|
||||
import 'package:cake_wallet/store/templates/exchange_template_store.dart';
|
||||
import 'package:cake_wallet/exchange/exchange_template.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
part 'exchange_view_model.g.dart';
|
||||
@@ -144,6 +135,7 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
|
||||
_calculateBestRate();
|
||||
});
|
||||
}
|
||||
|
||||
bool _useTorOnly;
|
||||
final Box<Trade> trades;
|
||||
final ExchangeTemplateStore _exchangeTemplateStore;
|
||||
@@ -384,13 +376,9 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
|
||||
double minLimit = limits.min ?? 0;
|
||||
double? maxLimit = limits.max;
|
||||
|
||||
if (_enteredAmount < minLimit) {
|
||||
return false;
|
||||
}
|
||||
if (_enteredAmount < minLimit) return false;
|
||||
|
||||
if (maxLimit != null && _enteredAmount > maxLimit) {
|
||||
return false;
|
||||
}
|
||||
if (maxLimit != null && _enteredAmount > maxLimit) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -422,16 +410,12 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
|
||||
}
|
||||
}
|
||||
}
|
||||
if (_sortedAvailableProviders.isNotEmpty) {
|
||||
_bestRate = _sortedAvailableProviders.keys.first;
|
||||
}
|
||||
if (_sortedAvailableProviders.isNotEmpty) _bestRate = _sortedAvailableProviders.keys.first;
|
||||
}
|
||||
|
||||
@action
|
||||
Future<void> loadLimits() async {
|
||||
if (selectedProviders.isEmpty) {
|
||||
return;
|
||||
}
|
||||
if (selectedProviders.isEmpty) return;
|
||||
|
||||
limitsState = LimitsIsLoading();
|
||||
|
||||
@@ -444,20 +428,16 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
|
||||
try {
|
||||
for (var provider in selectedProviders) {
|
||||
/// if this provider is not valid for the current pair, skip it
|
||||
if (!providersForCurrentPair().contains(provider)) {
|
||||
continue;
|
||||
}
|
||||
if (!providersForCurrentPair().contains(provider)) continue;
|
||||
|
||||
try {
|
||||
final tempLimits =
|
||||
await provider.fetchLimits(from: from, to: to, isFixedRateMode: isFixedRateMode);
|
||||
|
||||
if (lowestMin != null && (tempLimits.min ?? -1) < lowestMin) {
|
||||
lowestMin = tempLimits.min;
|
||||
}
|
||||
if (highestMax != null && (tempLimits.max ?? double.maxFinite) > highestMax) {
|
||||
if (lowestMin != null && (tempLimits.min ?? -1) < lowestMin) lowestMin = tempLimits.min;
|
||||
|
||||
if (highestMax != null && (tempLimits.max ?? double.maxFinite) > highestMax)
|
||||
highestMax = tempLimits.max;
|
||||
}
|
||||
} catch (e) {
|
||||
continue;
|
||||
}
|
||||
@@ -482,111 +462,34 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
|
||||
|
||||
@action
|
||||
Future<void> createTrade() async {
|
||||
TradeRequest? request;
|
||||
String amount = '';
|
||||
|
||||
try {
|
||||
for (var provider in _sortedAvailableProviders.values) {
|
||||
if (!(await provider.checkIsAvailable())) {
|
||||
continue;
|
||||
}
|
||||
if (!(await provider.checkIsAvailable())) continue;
|
||||
|
||||
if (provider is SideShiftExchangeProvider) {
|
||||
request = SideShiftRequest(
|
||||
depositMethod: depositCurrency,
|
||||
settleMethod: receiveCurrency,
|
||||
depositAmount: isFixedRateMode
|
||||
? receiveAmount.replaceAll(',', '.')
|
||||
: depositAmount.replaceAll(',', '.'),
|
||||
settleAddress: receiveAddress,
|
||||
final request = TradeRequest(
|
||||
fromCurrency: depositCurrency,
|
||||
toCurrency: receiveCurrency,
|
||||
fromAmount: depositAmount.replaceAll(',', '.'),
|
||||
toAmount: receiveAmount.replaceAll(',', '.'),
|
||||
refundAddress: depositAddress,
|
||||
);
|
||||
amount = isFixedRateMode ? receiveAmount : depositAmount;
|
||||
}
|
||||
|
||||
if (provider is SimpleSwapExchangeProvider) {
|
||||
request = SimpleSwapRequest(
|
||||
from: depositCurrency,
|
||||
to: receiveCurrency,
|
||||
amount: depositAmount.replaceAll(',', '.'),
|
||||
address: receiveAddress,
|
||||
refundAddress: depositAddress,
|
||||
);
|
||||
amount = isFixedRateMode ? receiveAmount : depositAmount;
|
||||
}
|
||||
|
||||
if (provider is XMRTOExchangeProvider) {
|
||||
request = XMRTOTradeRequest(
|
||||
from: depositCurrency,
|
||||
to: receiveCurrency,
|
||||
amount: depositAmount.replaceAll(',', '.'),
|
||||
receiveAmount: receiveAmount.replaceAll(',', '.'),
|
||||
address: receiveAddress,
|
||||
refundAddress: depositAddress,
|
||||
isBTCRequest: isReceiveAmountEntered);
|
||||
amount = isFixedRateMode ? receiveAmount : depositAmount;
|
||||
}
|
||||
|
||||
if (provider is ChangeNowExchangeProvider) {
|
||||
request = ChangeNowRequest(
|
||||
from: depositCurrency,
|
||||
to: receiveCurrency,
|
||||
fromAmount: depositAmount.replaceAll(',', '.'),
|
||||
toAmount: receiveAmount.replaceAll(',', '.'),
|
||||
refundAddress: depositAddress,
|
||||
address: receiveAddress,
|
||||
isReverse: isFixedRateMode);
|
||||
amount = isFixedRateMode ? receiveAmount : depositAmount;
|
||||
}
|
||||
|
||||
if (provider is MorphTokenExchangeProvider) {
|
||||
request = MorphTokenRequest(
|
||||
from: depositCurrency,
|
||||
to: receiveCurrency,
|
||||
amount: depositAmount.replaceAll(',', '.'),
|
||||
refundAddress: depositAddress,
|
||||
address: receiveAddress);
|
||||
amount = isFixedRateMode ? receiveAmount : depositAmount;
|
||||
}
|
||||
|
||||
if (provider is TrocadorExchangeProvider) {
|
||||
request = TrocadorRequest(
|
||||
from: depositCurrency,
|
||||
to: receiveCurrency,
|
||||
fromAmount: depositAmount.replaceAll(',', '.'),
|
||||
toAmount: receiveAmount.replaceAll(',', '.'),
|
||||
refundAddress: depositAddress,
|
||||
address: receiveAddress,
|
||||
isReverse: isFixedRateMode);
|
||||
amount = isFixedRateMode ? receiveAmount : depositAmount;
|
||||
}
|
||||
|
||||
if (provider is ExolixExchangeProvider) {
|
||||
request = ExolixRequest(
|
||||
from: depositCurrency,
|
||||
to: receiveCurrency,
|
||||
fromAmount: depositAmount.replaceAll(',', '.'),
|
||||
toAmount: receiveAmount.replaceAll(',', '.'),
|
||||
refundAddress: depositAddress,
|
||||
address: receiveAddress);
|
||||
amount = isFixedRateMode ? receiveAmount : depositAmount;
|
||||
}
|
||||
toAddress: receiveAddress,
|
||||
isFixedRate: isFixedRateMode);
|
||||
|
||||
var amount = isFixedRateMode ? receiveAmount : depositAmount;
|
||||
amount = amount.replaceAll(',', '.');
|
||||
|
||||
if (limitsState is LimitsLoadedSuccessfully) {
|
||||
if (double.tryParse(amount) == null) {
|
||||
if (double.tryParse(amount) == null) continue;
|
||||
|
||||
if (limits.max != null && double.parse(amount) < limits.min!)
|
||||
continue;
|
||||
}
|
||||
if (limits.max != null && double.parse(amount) < limits.min!) {
|
||||
else if (limits.max != null && double.parse(amount) > limits.max!)
|
||||
continue;
|
||||
} else if (limits.max != null && double.parse(amount) > limits.max!) {
|
||||
continue;
|
||||
} else {
|
||||
else {
|
||||
try {
|
||||
tradeState = TradeIsCreating();
|
||||
final trade =
|
||||
await provider.createTrade(request: request!, isFixedRateMode: isFixedRateMode);
|
||||
await provider.createTrade(request: request, isFixedRateMode: isFixedRateMode);
|
||||
trade.walletId = wallet.id;
|
||||
tradesStore.setTrade(trade);
|
||||
await trades.add(trade);
|
||||
@@ -636,9 +539,7 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
|
||||
final priority = _settingsStore.priority[wallet.type]!;
|
||||
final fee = wallet.calculateEstimatedFee(priority, null);
|
||||
|
||||
if (availableBalance < fee || availableBalance == 0) {
|
||||
return;
|
||||
}
|
||||
if (availableBalance < fee || availableBalance == 0) return;
|
||||
|
||||
final amount = availableBalance - fee;
|
||||
changeDepositAmount(amount: bitcoin!.formatterBitcoinAmountToString(amount: amount));
|
||||
@@ -669,19 +570,15 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
|
||||
void removeTemplate({required ExchangeTemplate template}) =>
|
||||
_exchangeTemplateStore.remove(template: template);
|
||||
|
||||
List<ExchangeProvider> providersForCurrentPair() {
|
||||
return _providersForPair(from: depositCurrency, to: receiveCurrency);
|
||||
}
|
||||
List<ExchangeProvider> providersForCurrentPair() =>
|
||||
_providersForPair(from: depositCurrency, to: receiveCurrency);
|
||||
|
||||
List<ExchangeProvider> _providersForPair(
|
||||
{required CryptoCurrency from, required CryptoCurrency to}) {
|
||||
final providers = providerList
|
||||
.where((provider) =>
|
||||
provider.pairList.where((pair) => pair.from == from && pair.to == to).isNotEmpty)
|
||||
.toList();
|
||||
|
||||
return providers;
|
||||
}
|
||||
{required CryptoCurrency from, required CryptoCurrency to}) =>
|
||||
providerList
|
||||
.where((provider) =>
|
||||
provider.pairList.where((pair) => pair.from == from && pair.to == to).isNotEmpty)
|
||||
.toList();
|
||||
|
||||
void _onPairChange() {
|
||||
depositAmount = '';
|
||||
@@ -744,9 +641,7 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
|
||||
@action
|
||||
void addExchangeProvider(ExchangeProvider provider) {
|
||||
selectedProviders.add(provider);
|
||||
if (providersForCurrentPair().contains(provider)) {
|
||||
_tradeAvailableProviders.add(provider);
|
||||
}
|
||||
if (providersForCurrentPair().contains(provider)) _tradeAvailableProviders.add(provider);
|
||||
}
|
||||
|
||||
@action
|
||||
@@ -817,14 +712,13 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
|
||||
}
|
||||
|
||||
void _setProviders() {
|
||||
if (_settingsStore.exchangeStatus == ExchangeApiMode.torOnly) {
|
||||
if (_settingsStore.exchangeStatus == ExchangeApiMode.torOnly)
|
||||
providerList = _allProviders.where((provider) => provider.supportsOnionAddress).toList();
|
||||
} else {
|
||||
else
|
||||
providerList = _allProviders;
|
||||
}
|
||||
}
|
||||
|
||||
int get depositMaxDigits => depositCurrency == CryptoCurrency.btc ? 8 : 12;
|
||||
int get depositMaxDigits => depositCurrency.decimals;
|
||||
|
||||
int get receiveMaxDigits => receiveCurrency == CryptoCurrency.btc ? 8 : 12;
|
||||
int get receiveMaxDigits => receiveCurrency.decimals;
|
||||
}
|
||||
|
||||
@@ -1,28 +1,28 @@
|
||||
import 'dart:async';
|
||||
import 'package:cake_wallet/exchange/changenow/changenow_exchange_provider.dart';
|
||||
import 'package:cake_wallet/exchange/exchange_provider.dart';
|
||||
|
||||
import 'package:cake_wallet/exchange/exchange_provider_description.dart';
|
||||
import 'package:cake_wallet/exchange/exolix/exolix_exchange_provider.dart';
|
||||
import 'package:cake_wallet/exchange/morphtoken/morphtoken_exchange_provider.dart';
|
||||
import 'package:cake_wallet/exchange/sideshift/sideshift_exchange_provider.dart';
|
||||
import 'package:cake_wallet/exchange/simpleswap/simpleswap_exchange_provider.dart';
|
||||
import 'package:cake_wallet/exchange/provider/changenow_exchange_provider.dart';
|
||||
import 'package:cake_wallet/exchange/provider/exchange_provider.dart';
|
||||
import 'package:cake_wallet/exchange/provider/exolix_exchange_provider.dart';
|
||||
import 'package:cake_wallet/exchange/provider/sideshift_exchange_provider.dart';
|
||||
import 'package:cake_wallet/exchange/provider/simpleswap_exchange_provider.dart';
|
||||
import 'package:cake_wallet/exchange/provider/trocador_exchange_provider.dart';
|
||||
import 'package:cake_wallet/exchange/trade.dart';
|
||||
import 'package:cake_wallet/exchange/trocador/trocador_exchange_provider.dart';
|
||||
import 'package:cake_wallet/exchange/xmrto/xmrto_exchange_provider.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/src/screens/trade_details/track_trade_list_item.dart';
|
||||
import 'package:cake_wallet/src/screens/trade_details/trade_details_list_card.dart';
|
||||
import 'package:cake_wallet/src/screens/trade_details/trade_details_status_item.dart';
|
||||
import 'package:cake_wallet/src/screens/trade_details/trade_provider_unsupported_item.dart';
|
||||
import 'package:cake_wallet/src/screens/transaction_details/standart_list_item.dart';
|
||||
import 'package:cake_wallet/store/settings_store.dart';
|
||||
import 'package:cake_wallet/utils/date_formatter.dart';
|
||||
import 'package:cake_wallet/utils/show_bar.dart';
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/src/screens/transaction_details/standart_list_item.dart';
|
||||
import 'package:cake_wallet/src/screens/trade_details/track_trade_list_item.dart';
|
||||
import 'package:cake_wallet/src/screens/trade_details/trade_details_list_card.dart';
|
||||
import 'package:cake_wallet/src/screens/trade_details/trade_details_status_item.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
import 'package:collection/collection.dart';
|
||||
|
||||
part 'trade_details_view_model.g.dart';
|
||||
|
||||
@@ -37,15 +37,9 @@ abstract class TradeDetailsViewModelBase with Store {
|
||||
trade = trades.values.firstWhereOrNull((element) => element.id == tradeForDetails.id) ??
|
||||
tradeForDetails {
|
||||
switch (trade.provider) {
|
||||
case ExchangeProviderDescription.xmrto:
|
||||
_provider = XMRTOExchangeProvider();
|
||||
break;
|
||||
case ExchangeProviderDescription.changeNow:
|
||||
_provider = ChangeNowExchangeProvider(settingsStore: settingsStore);
|
||||
break;
|
||||
case ExchangeProviderDescription.morphToken:
|
||||
_provider = MorphTokenExchangeProvider(trades: trades);
|
||||
break;
|
||||
case ExchangeProviderDescription.sideShift:
|
||||
_provider = SideShiftExchangeProvider();
|
||||
break;
|
||||
@@ -62,9 +56,10 @@ abstract class TradeDetailsViewModelBase with Store {
|
||||
|
||||
_updateItems();
|
||||
|
||||
_updateTrade();
|
||||
|
||||
timer = Timer.periodic(Duration(seconds: 20), (_) async => _updateTrade());
|
||||
if (_provider != null) {
|
||||
_updateTrade();
|
||||
timer = Timer.periodic(Duration(seconds: 20), (_) async => _updateTrade());
|
||||
}
|
||||
}
|
||||
|
||||
final Box<Trade> trades;
|
||||
@@ -86,9 +81,9 @@ abstract class TradeDetailsViewModelBase with Store {
|
||||
try {
|
||||
final updatedTrade = await _provider!.findTradeById(id: trade.id);
|
||||
|
||||
if (updatedTrade.createdAt == null && trade.createdAt != null) {
|
||||
if (updatedTrade.createdAt == null && trade.createdAt != null)
|
||||
updatedTrade.createdAt = trade.createdAt;
|
||||
}
|
||||
|
||||
Trade? foundElement = trades.values.firstWhereOrNull((element) => element.id == trade.id);
|
||||
if (foundElement != null) {
|
||||
final editedTrade = trades.get(foundElement.key);
|
||||
@@ -109,6 +104,10 @@ abstract class TradeDetailsViewModelBase with Store {
|
||||
|
||||
items.clear();
|
||||
|
||||
if (_provider == null)
|
||||
items.add(TradeProviderUnsupportedItem(
|
||||
error: S.current.exchange_provider_unsupported(trade.provider.title)));
|
||||
|
||||
items.add(
|
||||
DetailsListStatusItem(title: S.current.trade_details_state, value: trade.state.toString()));
|
||||
|
||||
@@ -165,7 +164,7 @@ abstract class TradeDetailsViewModelBase with Store {
|
||||
if (trade.provider == ExchangeProviderDescription.exolix) {
|
||||
final buildURL = 'https://exolix.com/transaction/${trade.id.toString()}';
|
||||
items.add(
|
||||
TrackTradeListItem(title: 'Track', value: buildURL, onTap: () => _launchUrl(buildURL)));
|
||||
TrackTradeListItem(title: 'Track', value: buildURL, onTap: () => _launchUrl(buildURL)));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user