mirror of
https://github.com/cake-tech/cake_wallet.git
synced 2024-12-22 11:39:22 +00:00
Integrate LetsExchange exchange provider (#1562)
* letsExchange provider * add api key * secrets affiliateId * Update letsexchange_exchange_provider.dart * minor fix [skip ci] * fix network type issue * tracking link [skip ci] * fix data type * normalise bch address --------- Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com>
This commit is contained in:
parent
215e785198
commit
7d11d0461f
15 changed files with 400 additions and 4 deletions
2
.github/workflows/pr_test_build_android.yml
vendored
2
.github/workflows/pr_test_build_android.yml
vendored
|
@ -168,6 +168,8 @@ jobs:
|
|||
echo "const nanoNowNodesApiKey = '${{ secrets.NANO_NOW_NODES_API_KEY }}';" >> cw_nano/lib/.secrets.g.dart
|
||||
echo "const tronGridApiKey = '${{ secrets.TRON_GRID_API_KEY }}';" >> cw_tron/lib/.secrets.g.dart
|
||||
echo "const tronNowNodesApiKey = '${{ secrets.TRON_NOW_NODES_API_KEY }}';" >> cw_tron/lib/.secrets.g.dart
|
||||
echo "const letsExchangeBearerToken = '${{ secrets.LETS_EXCHANGE_TOKEN }}';" >> lib/.secrets.g.dart
|
||||
echo "const letsExchangeAffiliateId = '${{ secrets.LETS_EXCHANGE_AFFILIATE_ID }}';" >> lib/.secrets.g.dart
|
||||
echo "const stealthExBearerToken = '${{ secrets.STEALTH_EX_BEARER_TOKEN }}';" >> lib/.secrets.g.dart
|
||||
echo "const stealthExAdditionalFeePercent = '${{ secrets.STEALTH_EX_ADDITIONAL_FEE_PERCENT }}';" >> lib/.secrets.g.dart
|
||||
|
||||
|
|
2
.github/workflows/pr_test_build_linux.yml
vendored
2
.github/workflows/pr_test_build_linux.yml
vendored
|
@ -154,6 +154,8 @@ jobs:
|
|||
echo "const nanoNowNodesApiKey = '${{ secrets.NANO_NOW_NODES_API_KEY }}';" >> cw_nano/lib/.secrets.g.dart
|
||||
echo "const tronGridApiKey = '${{ secrets.TRON_GRID_API_KEY }}';" >> cw_tron/lib/.secrets.g.dart
|
||||
echo "const tronNowNodesApiKey = '${{ secrets.TRON_NOW_NODES_API_KEY }}';" >> cw_tron/lib/.secrets.g.dart
|
||||
echo "const letsExchangeBearerToken = '${{ secrets.LETS_EXCHANGE_TOKEN }}';" >> lib/.secrets.g.dart
|
||||
echo "const letsExchangeAffiliateId = '${{ secrets.LETS_EXCHANGE_AFFILIATE_ID }}';" >> lib/.secrets.g.dart
|
||||
echo "const stealthExBearerToken = '${{ secrets.STEALTH_EX_BEARER_TOKEN }}';" >> lib/.secrets.g.dart
|
||||
echo "const stealthExAdditionalFeePercent = '${{ secrets.STEALTH_EX_ADDITIONAL_FEE_PERCENT }}';" >> lib/.secrets.g.dart
|
||||
|
||||
|
|
5
assets/images/letsexchange_icon.svg
Normal file
5
assets/images/letsexchange_icon.svg
Normal file
|
@ -0,0 +1,5 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" width="32" height="32">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd"
|
||||
d="M16 1.37854C16 0.764286 16.6636 0.379192 17.1969 0.68395L23 4L29.4961 7.71208C29.8077 7.89012 30 8.22147 30 8.58032V16L23.9923 12.567C23.3774 12.2157 22.6226 12.2157 22.0077 12.567L16 16V8V1.37854ZM2 16V8.58032C2 8.22147 2.19229 7.89012 2.50386 7.71208L8.00772 4.56702C8.62259 4.21566 9.37741 4.21566 9.99228 4.56702L16 8L2 16ZM16 30.6215C16 31.2357 15.3364 31.6208 14.8031 31.3161L9 28L2.50386 24.2879C2.19229 24.1099 2 23.7785 2 23.4197V16L8.00772 19.433C8.62259 19.7843 9.37741 19.7843 9.99228 19.433L16 16V24V30.6215ZM22.0077 27.433C22.6226 27.7843 23.3774 27.7843 23.9923 27.433L29.4961 24.2879C29.8077 24.1099 30 23.7785 30 23.4197V16L16 24L22.0077 27.433Z"
|
||||
fill="#159DFF"></path>
|
||||
</svg>
|
After Width: | Height: | Size: 846 B |
|
@ -27,8 +27,10 @@ class ExchangeProviderDescription extends EnumerableItem<int> with Serializable<
|
|||
ExchangeProviderDescription(title: 'ThorChain', raw: 8, image: 'assets/images/thorchain.png');
|
||||
static const quantex =
|
||||
ExchangeProviderDescription(title: 'Quantex', raw: 9, image: 'assets/images/quantex.png');
|
||||
static const letsExchange =
|
||||
ExchangeProviderDescription(title: 'LetsExchange', raw: 10, image: 'assets/images/letsexchange_icon.svg');
|
||||
static const stealthEx =
|
||||
ExchangeProviderDescription(title: 'StealthEx', raw: 10, image: 'assets/images/stealthex.png');
|
||||
ExchangeProviderDescription(title: 'StealthEx', raw: 11, image: 'assets/images/stealthex.png');
|
||||
|
||||
static ExchangeProviderDescription deserialize({required int raw}) {
|
||||
switch (raw) {
|
||||
|
@ -53,6 +55,8 @@ class ExchangeProviderDescription extends EnumerableItem<int> with Serializable<
|
|||
case 9:
|
||||
return quantex;
|
||||
case 10:
|
||||
return letsExchange;
|
||||
case 11:
|
||||
return stealthEx;
|
||||
default:
|
||||
throw Exception('Unexpected token: $raw for ExchangeProviderDescription deserialize');
|
||||
|
|
292
lib/exchange/provider/letsexchange_exchange_provider.dart
Normal file
292
lib/exchange/provider/letsexchange_exchange_provider.dart
Normal file
|
@ -0,0 +1,292 @@
|
|||
import 'dart:convert';
|
||||
import 'dart:developer';
|
||||
|
||||
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/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_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' as http;
|
||||
|
||||
class LetsExchangeExchangeProvider extends ExchangeProvider {
|
||||
LetsExchangeExchangeProvider() : super(pairList: supportedPairs(_notSupported));
|
||||
|
||||
static const List<CryptoCurrency> _notSupported = [];
|
||||
|
||||
static const apiKey = secrets.letsExchangeBearerToken;
|
||||
static const _baseUrl = 'api.letsexchange.io';
|
||||
static const _infoPath = '/api/v1/info';
|
||||
static const _infoRevertPath = '/api/v1/info-revert';
|
||||
static const _createTransactionPath = '/api/v1/transaction';
|
||||
static const _createTransactionRevertPath = '/api/v1/transaction-revert';
|
||||
static const _getTransactionPath = '/api/v1/transaction';
|
||||
|
||||
static const _affiliateId = secrets.letsExchangeAffiliateId;
|
||||
|
||||
@override
|
||||
String get title => 'LetsExchange';
|
||||
|
||||
@override
|
||||
bool get isAvailable => true;
|
||||
|
||||
@override
|
||||
bool get isEnabled => true;
|
||||
|
||||
@override
|
||||
bool get supportsFixedRate => true;
|
||||
|
||||
@override
|
||||
ExchangeProviderDescription get description => ExchangeProviderDescription.letsExchange;
|
||||
|
||||
@override
|
||||
Future<bool> checkIsAvailable() async => true;
|
||||
|
||||
@override
|
||||
Future<Limits> fetchLimits(
|
||||
{required CryptoCurrency from,
|
||||
required CryptoCurrency to,
|
||||
required bool isFixedRateMode}) async {
|
||||
final networkFrom = _getNetworkType(from);
|
||||
final networkTo = _getNetworkType(to);
|
||||
|
||||
try {
|
||||
final params = {
|
||||
'from': from.title,
|
||||
'to': to.title,
|
||||
if (networkFrom != null) 'network_from': networkFrom,
|
||||
if (networkTo != null) 'network_to': networkTo,
|
||||
'amount': '1',
|
||||
'affiliate_id': _affiliateId
|
||||
};
|
||||
|
||||
final responseJSON = await _getInfo(params, isFixedRateMode);
|
||||
final min = double.tryParse(responseJSON['min_amount'] as String);
|
||||
final max = double.tryParse(responseJSON['max_amount'] as String);
|
||||
return Limits(min: min, max: max);
|
||||
} catch (e) {
|
||||
log(e.toString());
|
||||
throw Exception('Failed to fetch limits');
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<double> fetchRate(
|
||||
{required CryptoCurrency from,
|
||||
required CryptoCurrency to,
|
||||
required double amount,
|
||||
required bool isFixedRateMode,
|
||||
required bool isReceiveAmount}) async {
|
||||
final networkFrom = _getNetworkType(from);
|
||||
final networkTo = _getNetworkType(to);
|
||||
try {
|
||||
final params = {
|
||||
'from': from.title,
|
||||
'to': to.title,
|
||||
if (networkFrom != null) 'network_from': networkFrom,
|
||||
if (networkTo != null) 'network_to': networkTo,
|
||||
'amount': amount.toString(),
|
||||
'affiliate_id': _affiliateId
|
||||
};
|
||||
|
||||
final responseJSON = await _getInfo(params, isFixedRateMode);
|
||||
|
||||
final amountToGet = double.tryParse(responseJSON['amount'] as String) ?? 0.0;
|
||||
|
||||
return isFixedRateMode ? amount / amountToGet : amountToGet / amount;
|
||||
} catch (e) {
|
||||
log(e.toString());
|
||||
return 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Trade> createTrade(
|
||||
{required TradeRequest request,
|
||||
required bool isFixedRateMode,
|
||||
required bool isSendAll}) async {
|
||||
final networkFrom = _getNetworkType(request.fromCurrency);
|
||||
final networkTo = _getNetworkType(request.toCurrency);
|
||||
try {
|
||||
final params = {
|
||||
'from': request.fromCurrency.title,
|
||||
'to': request.toCurrency.title,
|
||||
if (networkFrom != null) 'network_from': networkFrom,
|
||||
if (networkTo != null) 'network_to': networkTo,
|
||||
'amount': isFixedRateMode ? request.toAmount.toString() : request.fromAmount.toString(),
|
||||
'affiliate_id': _affiliateId
|
||||
};
|
||||
|
||||
final responseInfoJSON = await _getInfo(params, isFixedRateMode);
|
||||
final rateId = responseInfoJSON['rate_id'] as String;
|
||||
|
||||
final withdrawalAddress = _normalizeBchAddress(request.toAddress);
|
||||
final returnAddress = _normalizeBchAddress(request.refundAddress);
|
||||
|
||||
final tradeParams = {
|
||||
'coin_from': request.fromCurrency.title,
|
||||
'coin_to': request.toCurrency.title,
|
||||
if (!isFixedRateMode) 'deposit_amount': request.fromAmount.toString(),
|
||||
'withdrawal': withdrawalAddress,
|
||||
if (isFixedRateMode) 'withdrawal_amount': request.toAmount.toString(),
|
||||
'withdrawal_extra_id': '',
|
||||
'return': returnAddress,
|
||||
'rate_id': rateId,
|
||||
if (networkFrom != null) 'network_from': networkFrom,
|
||||
if (networkTo != null) 'network_to': networkTo,
|
||||
'affiliate_id': _affiliateId
|
||||
};
|
||||
|
||||
final headers = {
|
||||
'Content-Type': 'application/json',
|
||||
'Accept': 'application/json',
|
||||
'Authorization': apiKey
|
||||
};
|
||||
|
||||
final uri = Uri.https(_baseUrl,
|
||||
isFixedRateMode ? _createTransactionRevertPath : _createTransactionPath, tradeParams);
|
||||
final response = await http.post(uri, headers: headers);
|
||||
|
||||
if (response.statusCode != 200) {
|
||||
throw Exception('LetsExchange create trade failed: ${response.body}');
|
||||
}
|
||||
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
|
||||
final id = responseJSON['transaction_id'] as String;
|
||||
final from = responseJSON['coin_from'] as String;
|
||||
final to = responseJSON['coin_to'] as String;
|
||||
final payoutAddress = responseJSON['withdrawal'] as String;
|
||||
final depositAddress = responseJSON['deposit'] as String;
|
||||
final refundAddress = responseJSON['return'] as String;
|
||||
final depositAmount = responseJSON['deposit_amount'] as String;
|
||||
final receiveAmount = responseJSON['withdrawal_amount'] as String;
|
||||
final status = responseJSON['status'] as String;
|
||||
final createdAtString = responseJSON['created_at'] as String;
|
||||
final expiredAtTimestamp = responseJSON['expired_at'] as int;
|
||||
|
||||
final createdAt = DateTime.parse(createdAtString);
|
||||
final expiredAt = DateTime.fromMillisecondsSinceEpoch(expiredAtTimestamp * 1000);
|
||||
|
||||
CryptoCurrency fromCurrency;
|
||||
if (request.fromCurrency.tag != null && request.fromCurrency.title == from) {
|
||||
fromCurrency = request.fromCurrency;
|
||||
} else {
|
||||
fromCurrency = CryptoCurrency.fromString(from);
|
||||
}
|
||||
|
||||
CryptoCurrency toCurrency;
|
||||
if (request.toCurrency.tag != null && request.toCurrency.title == to) {
|
||||
toCurrency = request.toCurrency;
|
||||
} else {
|
||||
toCurrency = CryptoCurrency.fromString(to);
|
||||
}
|
||||
|
||||
return Trade(
|
||||
id: id,
|
||||
from: fromCurrency,
|
||||
to: toCurrency,
|
||||
provider: description,
|
||||
inputAddress: depositAddress,
|
||||
payoutAddress: payoutAddress,
|
||||
refundAddress: refundAddress,
|
||||
amount: depositAmount,
|
||||
receiveAmount: receiveAmount,
|
||||
state: TradeState.deserialize(raw: status),
|
||||
createdAt: createdAt,
|
||||
expiredAt: expiredAt,
|
||||
);
|
||||
} catch (e) {
|
||||
log(e.toString());
|
||||
throw TradeNotCreatedException(description);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Trade> findTradeById({required String id}) async {
|
||||
final headers = {
|
||||
'Content-Type': 'application/json',
|
||||
'Accept': 'application/json',
|
||||
'Authorization': apiKey
|
||||
};
|
||||
|
||||
final url = Uri.https(_baseUrl, '$_getTransactionPath/$id');
|
||||
final response = await http.get(url, headers: headers);
|
||||
|
||||
if (response.statusCode != 200) {
|
||||
throw Exception('LetsExchange fetch trade failed: ${response.body}');
|
||||
}
|
||||
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
|
||||
final from = responseJSON['coin_from'] as String;
|
||||
final to = responseJSON['coin_to'] as String;
|
||||
final payoutAddress = responseJSON['withdrawal'] as String;
|
||||
final depositAddress = responseJSON['deposit'] as String;
|
||||
final refundAddress = responseJSON['return'] as String;
|
||||
final depositAmount = responseJSON['deposit_amount'] as String;
|
||||
final receiveAmount = responseJSON['withdrawal_amount'] as String;
|
||||
final status = responseJSON['status'] as String;
|
||||
final createdAtString = responseJSON['created_at'] as String;
|
||||
final expiredAtTimestamp = responseJSON['expired_at'] as int;
|
||||
|
||||
final createdAt = DateTime.parse(createdAtString);
|
||||
final expiredAt = DateTime.fromMillisecondsSinceEpoch(expiredAtTimestamp * 1000);
|
||||
|
||||
return Trade(
|
||||
id: id,
|
||||
from: CryptoCurrency.fromString(from),
|
||||
to: CryptoCurrency.fromString(to),
|
||||
provider: description,
|
||||
inputAddress: depositAddress,
|
||||
payoutAddress: payoutAddress,
|
||||
refundAddress: refundAddress,
|
||||
amount: depositAmount,
|
||||
receiveAmount: receiveAmount,
|
||||
state: TradeState.deserialize(raw: status),
|
||||
createdAt: createdAt,
|
||||
expiredAt: expiredAt,
|
||||
isRefund: status == 'refund',
|
||||
);
|
||||
}
|
||||
|
||||
Future<Map<String, dynamic>> _getInfo(Map<String, String> params, bool isFixedRateMode) async {
|
||||
final headers = {
|
||||
'Content-Type': 'application/json',
|
||||
'Accept': 'application/json',
|
||||
'Authorization': apiKey
|
||||
};
|
||||
|
||||
try {
|
||||
final uri = Uri.https(_baseUrl, isFixedRateMode ? _infoRevertPath : _infoPath, params);
|
||||
final response = await http.post(uri, headers: headers);
|
||||
if (response.statusCode != 200) {
|
||||
throw Exception('LetsExchange fetch info failed: ${response.body}');
|
||||
}
|
||||
return json.decode(response.body) as Map<String, dynamic>;
|
||||
} catch (e) {
|
||||
throw Exception('LetsExchange failed to fetch info ${e.toString()}');
|
||||
}
|
||||
}
|
||||
|
||||
String? _getNetworkType(CryptoCurrency currency) {
|
||||
if (currency.tag != null && currency.tag!.isNotEmpty) {
|
||||
switch (currency.tag!) {
|
||||
case 'TRX':
|
||||
return 'TRC20';
|
||||
case 'ETH':
|
||||
return 'ERC20';
|
||||
case 'BSC':
|
||||
return 'BEP20';
|
||||
case 'POLY':
|
||||
return 'MATIC';
|
||||
default:
|
||||
return currency.tag!;
|
||||
}
|
||||
}
|
||||
return currency.title;
|
||||
}
|
||||
|
||||
String _normalizeBchAddress(String address) =>
|
||||
address.startsWith('bitcoincash:') ? address.substring(12) : address;
|
||||
}
|
|
@ -69,7 +69,7 @@ class StealthExExchangeProvider extends ExchangeProvider {
|
|||
throw Exception('StealthEx fetch limits failed: ${response.body}');
|
||||
}
|
||||
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
|
||||
final min = responseJSON['min_amount'] as double?;
|
||||
final min = toDouble(responseJSON['min_amount']);
|
||||
final max = responseJSON['max_amount'] as double?;
|
||||
return Limits(min: min, max: max);
|
||||
} catch (e) {
|
||||
|
|
|
@ -106,6 +106,7 @@ class TradeState extends EnumerableItem<String> with Serializable<String> {
|
|||
case 'waitingAuthorization':
|
||||
return waitingAuthorization;
|
||||
case 'failed':
|
||||
case 'error':
|
||||
return failed;
|
||||
case 'completed':
|
||||
return completed;
|
||||
|
@ -125,6 +126,7 @@ class TradeState extends EnumerableItem<String> with Serializable<String> {
|
|||
case 'exchanging':
|
||||
return exchanging;
|
||||
case 'sending':
|
||||
case 'sending_confirmation':
|
||||
return sending;
|
||||
case 'success':
|
||||
case 'done':
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import 'package:cake_wallet/themes/extensions/cake_text_theme.dart';
|
||||
import 'package:cake_wallet/utils/image_utill.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
import 'package:cake_wallet/exchange/exchange_provider_description.dart';
|
||||
|
@ -36,7 +37,8 @@ class TradeRow extends StatelessWidget {
|
|||
children: [
|
||||
ClipRRect(
|
||||
borderRadius: BorderRadius.circular(50),
|
||||
child: Image.asset(provider.image, width: 36, height: 36)),
|
||||
child: ImageUtil.getImageFromPath(
|
||||
imagePath: provider.image, height: 36, width: 36)),
|
||||
SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: Column(
|
||||
|
|
|
@ -2,6 +2,7 @@ import 'package:cake_wallet/themes/extensions/cake_text_theme.dart';
|
|||
import 'package:cake_wallet/exchange/exchange_provider_description.dart';
|
||||
import 'package:cake_wallet/store/dashboard/trades_store.dart';
|
||||
import 'package:cake_wallet/themes/extensions/exchange_page_theme.dart';
|
||||
import 'package:cake_wallet/utils/image_utill.dart';
|
||||
import 'package:cake_wallet/utils/show_bar.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
|
@ -101,7 +102,8 @@ class ExchangeConfirmPage extends BasePage {
|
|||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
(trade.provider.image?.isNotEmpty ?? false)
|
||||
? Image.asset(trade.provider.image, height: 50)
|
||||
? ImageUtil.getImageFromPath(
|
||||
imagePath: trade.provider.image, width: 50)
|
||||
: const SizedBox(),
|
||||
if (!trade.provider.horizontalLogo)
|
||||
Padding(
|
||||
|
|
|
@ -17,6 +17,7 @@ abstract class TradeFilterStoreBase with Store {
|
|||
displayTrocador = true,
|
||||
displayExolix = true,
|
||||
displayThorChain = true,
|
||||
displayLetsExchange = true,
|
||||
displayStealthEx = true;
|
||||
|
||||
@observable
|
||||
|
@ -43,6 +44,9 @@ abstract class TradeFilterStoreBase with Store {
|
|||
@observable
|
||||
bool displayThorChain;
|
||||
|
||||
@observable
|
||||
bool displayLetsExchange;
|
||||
|
||||
@observable
|
||||
bool displayStealthEx;
|
||||
|
||||
|
@ -54,6 +58,7 @@ abstract class TradeFilterStoreBase with Store {
|
|||
displayTrocador &&
|
||||
displayExolix &&
|
||||
displayThorChain &&
|
||||
displayLetsExchange &&
|
||||
displayStealthEx;
|
||||
|
||||
@action
|
||||
|
@ -83,6 +88,8 @@ abstract class TradeFilterStoreBase with Store {
|
|||
case ExchangeProviderDescription.thorChain:
|
||||
displayThorChain = !displayThorChain;
|
||||
break;
|
||||
case ExchangeProviderDescription.letsExchange:
|
||||
displayLetsExchange = !displayLetsExchange;
|
||||
case ExchangeProviderDescription.stealthEx:
|
||||
displayStealthEx = !displayStealthEx;
|
||||
break;
|
||||
|
@ -96,6 +103,7 @@ abstract class TradeFilterStoreBase with Store {
|
|||
displayTrocador = false;
|
||||
displayExolix = false;
|
||||
displayThorChain = false;
|
||||
displayLetsExchange = false;
|
||||
displayStealthEx = false;
|
||||
} else {
|
||||
displayChangeNow = true;
|
||||
|
@ -106,6 +114,7 @@ abstract class TradeFilterStoreBase with Store {
|
|||
displayTrocador = true;
|
||||
displayExolix = true;
|
||||
displayThorChain = true;
|
||||
displayLetsExchange = true;
|
||||
displayStealthEx = true;
|
||||
}
|
||||
break;
|
||||
|
@ -134,6 +143,8 @@ abstract class TradeFilterStoreBase with Store {
|
|||
(displayExolix && item.trade.provider == ExchangeProviderDescription.exolix) ||
|
||||
(displayThorChain &&
|
||||
item.trade.provider == ExchangeProviderDescription.thorChain) ||
|
||||
(displayLetsExchange &&
|
||||
item.trade.provider == ExchangeProviderDescription.letsExchange) ||
|
||||
(displayStealthEx && item.trade.provider == ExchangeProviderDescription.stealthEx))
|
||||
.toList()
|
||||
: _trades;
|
||||
|
|
60
lib/utils/image_utill.dart
Normal file
60
lib/utils/image_utill.dart
Normal file
|
@ -0,0 +1,60 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
|
||||
class ImageUtil {
|
||||
static Widget getImageFromPath({required String imagePath, double? height, double? width}) {
|
||||
final bool isNetworkImage = imagePath.startsWith('http') || imagePath.startsWith('https');
|
||||
final bool isSvg = imagePath.endsWith('.svg');
|
||||
final double _height = height ?? 35;
|
||||
final double _width = width ?? 35;
|
||||
|
||||
if (isNetworkImage) {
|
||||
return isSvg
|
||||
? SvgPicture.network(
|
||||
imagePath,
|
||||
height: _height,
|
||||
width: _width,
|
||||
placeholderBuilder: (BuildContext context) => Container(
|
||||
height: _height,
|
||||
width: _width,
|
||||
child: Center(
|
||||
child: CircularProgressIndicator(),
|
||||
),
|
||||
),
|
||||
)
|
||||
: Image.network(
|
||||
imagePath,
|
||||
height: _height,
|
||||
width: _width,
|
||||
loadingBuilder:
|
||||
(BuildContext context, Widget child, ImageChunkEvent? loadingProgress) {
|
||||
if (loadingProgress == null) {
|
||||
return child;
|
||||
}
|
||||
return Container(
|
||||
height: _height,
|
||||
width: _width,
|
||||
child: Center(
|
||||
child: CircularProgressIndicator(
|
||||
value: loadingProgress.expectedTotalBytes != null
|
||||
? loadingProgress.cumulativeBytesLoaded /
|
||||
loadingProgress.expectedTotalBytes!
|
||||
: null,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
errorBuilder: (BuildContext context, Object exception, StackTrace? stackTrace) {
|
||||
return Container(
|
||||
height: _height,
|
||||
width: _width,
|
||||
);
|
||||
},
|
||||
);
|
||||
} else {
|
||||
return isSvg
|
||||
? SvgPicture.asset(imagePath, height: _height, width: _width)
|
||||
: Image.asset(imagePath, height: _height, width: _width);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -129,6 +129,11 @@ abstract class DashboardViewModelBase with Store {
|
|||
caption: ExchangeProviderDescription.thorChain.title,
|
||||
onChanged: () =>
|
||||
tradeFilterStore.toggleDisplayExchange(ExchangeProviderDescription.thorChain)),
|
||||
FilterItem(
|
||||
value: () => tradeFilterStore.displayLetsExchange,
|
||||
caption: ExchangeProviderDescription.letsExchange.title,
|
||||
onChanged: () =>
|
||||
tradeFilterStore.toggleDisplayExchange(ExchangeProviderDescription.letsExchange)),
|
||||
FilterItem(
|
||||
value: () => tradeFilterStore.displayStealthEx,
|
||||
caption: ExchangeProviderDescription.stealthEx.title,
|
||||
|
|
|
@ -4,6 +4,7 @@ import 'dart:convert';
|
|||
|
||||
import 'package:bitcoin_base/bitcoin_base.dart';
|
||||
import 'package:cake_wallet/core/create_trade_result.dart';
|
||||
import 'package:cake_wallet/exchange/provider/letsexchange_exchange_provider.dart';
|
||||
import 'package:cake_wallet/exchange/provider/stealth_ex_exchange_provider.dart';
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
import 'package:cw_core/sync_status.dart';
|
||||
|
@ -167,6 +168,7 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
|
|||
ThorChainExchangeProvider(tradesStore: trades),
|
||||
if (FeatureFlag.isExolixEnabled) ExolixExchangeProvider(),
|
||||
QuantexExchangeProvider(),
|
||||
LetsExchangeExchangeProvider(),
|
||||
StealthExExchangeProvider(),
|
||||
TrocadorExchangeProvider(
|
||||
useTorOnly: _useTorOnly, providerStates: _settingsStore.trocadorProviderStates),
|
||||
|
|
|
@ -4,6 +4,7 @@ import 'package:cake_wallet/exchange/exchange_provider_description.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/letsexchange_exchange_provider.dart';
|
||||
import 'package:cake_wallet/exchange/provider/quantex_exchange_provider.dart';
|
||||
import 'package:cake_wallet/exchange/provider/sideshift_exchange_provider.dart';
|
||||
import 'package:cake_wallet/exchange/provider/simpleswap_exchange_provider.dart';
|
||||
|
@ -60,6 +61,8 @@ abstract class TradeDetailsViewModelBase with Store {
|
|||
break;
|
||||
case ExchangeProviderDescription.quantex:
|
||||
_provider = QuantexExchangeProvider();
|
||||
case ExchangeProviderDescription.letsExchange:
|
||||
_provider = LetsExchangeExchangeProvider();
|
||||
break;
|
||||
case ExchangeProviderDescription.stealthEx:
|
||||
_provider = StealthExExchangeProvider();
|
||||
|
@ -90,6 +93,8 @@ abstract class TradeDetailsViewModelBase with Store {
|
|||
return 'https://track.ninerealms.com/${trade.id}';
|
||||
case ExchangeProviderDescription.quantex:
|
||||
return 'https://myquantex.com/send/${trade.id}';
|
||||
case ExchangeProviderDescription.letsExchange:
|
||||
return 'https://letsexchange.io/?transactionId=${trade.id}';
|
||||
case ExchangeProviderDescription.stealthEx:
|
||||
return 'https://stealthex.io/exchange/?id=${trade.id}';
|
||||
}
|
||||
|
|
|
@ -43,6 +43,8 @@ class SecretKey {
|
|||
SecretKey('cakePayApiKey', () => ''),
|
||||
SecretKey('CSRFToken', () => ''),
|
||||
SecretKey('authorization', () => ''),
|
||||
SecretKey('letsExchangeBearerToken', () => ''),
|
||||
SecretKey('letsExchangeAffiliateId', () => ''),
|
||||
SecretKey('stealthExBearerToken', () => ''),
|
||||
SecretKey('stealthExAdditionalFeePercent', () => ''),
|
||||
];
|
||||
|
|
Loading…
Reference in a new issue