Cw 995 buy sell issues (#2127)

* add not supported warning

* warning localisation

* providers depend on the selected pair
This commit is contained in:
Serhii
2025-03-29 07:46:16 +02:00
committed by GitHub
parent 48fa647a8c
commit eb136efb27
40 changed files with 268 additions and 94 deletions
+5
View File
@@ -1,6 +1,7 @@
import 'package:cake_wallet/buy/buy_amount.dart';
import 'package:cake_wallet/buy/buy_quote.dart';
import 'package:cake_wallet/buy/order.dart';
import 'package:cake_wallet/buy/pairs_utils.dart';
import 'package:cake_wallet/buy/payment_method.dart';
import 'package:cake_wallet/entities/fiat_currency.dart';
import 'package:cake_wallet/view_model/hardware_wallet/ledger_view_model.dart';
@@ -13,11 +14,15 @@ abstract class BuyProvider {
required this.wallet,
required this.isTestEnvironment,
required this.ledgerVM,
required this.supportedCryptoList,
required this.supportedFiatList
});
final WalletBase wallet;
final bool isTestEnvironment;
final LedgerViewModel? ledgerVM;
final List<TradePair<dynamic, dynamic>> supportedCryptoList;
final List<TradePair<dynamic, dynamic>> supportedFiatList;
String get title;
+17 -3
View File
@@ -3,6 +3,7 @@ import 'dart:developer';
import 'package:cake_wallet/buy/buy_provider.dart';
import 'package:cake_wallet/buy/buy_quote.dart';
import 'package:cake_wallet/buy/pairs_utils.dart';
import 'package:cake_wallet/buy/payment_method.dart';
import 'package:cake_wallet/entities/fiat_currency.dart';
import 'package:cake_wallet/generated/i18n.dart';
@@ -20,9 +21,19 @@ import 'package:http/http.dart' as http;
import 'package:url_launcher/url_launcher.dart';
class DFXBuyProvider extends BuyProvider {
DFXBuyProvider(
{required WalletBase wallet, bool isTestEnvironment = false, LedgerViewModel? ledgerVM})
: super(wallet: wallet, isTestEnvironment: isTestEnvironment, ledgerVM: ledgerVM);
DFXBuyProvider({
required WalletBase wallet,
bool isTestEnvironment = false,
LedgerViewModel? ledgerVM,
}) : super(
wallet: wallet,
isTestEnvironment: isTestEnvironment,
ledgerVM: ledgerVM,
supportedCryptoList: supportedCryptoToFiatPairs(
notSupportedCrypto: _notSupportedCrypto, notSupportedFiat: _notSupportedFiat),
supportedFiatList: supportedFiatToCryptoPairs(
notSupportedFiat: _notSupportedFiat, notSupportedCrypto: _notSupportedCrypto)
);
static const _baseUrl = 'api.dfx.swiss';
@@ -30,6 +41,9 @@ class DFXBuyProvider extends BuyProvider {
static const _authPath = '/v1/auth';
static const walletName = 'CakeWallet';
static const List<CryptoCurrency> _notSupportedCrypto = [];
static const List<FiatCurrency> _notSupportedFiat = [];
@override
String get title => 'DFX.swiss';
+12 -1
View File
@@ -3,6 +3,7 @@ import 'dart:convert';
import 'package:cake_wallet/.secrets.g.dart' as secrets;
import 'package:cake_wallet/buy/buy_provider.dart';
import 'package:cake_wallet/buy/buy_quote.dart';
import 'package:cake_wallet/buy/pairs_utils.dart';
import 'package:cake_wallet/buy/payment_method.dart';
import 'package:cake_wallet/entities/fiat_currency.dart';
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
@@ -16,7 +17,14 @@ import 'package:url_launcher/url_launcher.dart';
class KryptonimBuyProvider extends BuyProvider {
KryptonimBuyProvider({required WalletBase wallet, bool isTestEnvironment = false})
: super(wallet: wallet, isTestEnvironment: isTestEnvironment, ledgerVM: null);
: super(
wallet: wallet,
isTestEnvironment: isTestEnvironment,
ledgerVM: null,
supportedCryptoList: supportedCryptoToFiatPairs(
notSupportedCrypto: _notSupportedCrypto, notSupportedFiat: _notSupportedFiat),
supportedFiatList: supportedFiatToCryptoPairs(
notSupportedFiat: _notSupportedFiat, notSupportedCrypto: _notSupportedCrypto));
static const _isProduction = true;
@@ -27,6 +35,9 @@ class KryptonimBuyProvider extends BuyProvider {
static String get _kryptonimApiKey => secrets.kryptonimApiKey;
static const List<CryptoCurrency> _notSupportedCrypto = [];
static const List<FiatCurrency> _notSupportedFiat = [];
@override
String get title => 'Kryptonim';
+12 -1
View File
@@ -3,6 +3,7 @@ import 'dart:convert';
import 'package:cake_wallet/.secrets.g.dart' as secrets;
import 'package:cake_wallet/buy/buy_provider.dart';
import 'package:cake_wallet/buy/buy_quote.dart';
import 'package:cake_wallet/buy/pairs_utils.dart';
import 'package:cake_wallet/buy/payment_method.dart';
import 'package:cake_wallet/entities/fiat_currency.dart';
import 'package:cake_wallet/generated/i18n.dart';
@@ -18,7 +19,14 @@ import 'package:url_launcher/url_launcher.dart';
class MeldBuyProvider extends BuyProvider {
MeldBuyProvider({required WalletBase wallet, bool isTestEnvironment = false})
: super(wallet: wallet, isTestEnvironment: isTestEnvironment, ledgerVM: null);
: super(
wallet: wallet,
isTestEnvironment: isTestEnvironment,
ledgerVM: null,
supportedCryptoList: supportedCryptoToFiatPairs(
notSupportedCrypto: _notSupportedCrypto, notSupportedFiat: _notSupportedFiat),
supportedFiatList: supportedFiatToCryptoPairs(
notSupportedFiat: _notSupportedFiat, notSupportedCrypto: _notSupportedCrypto));
static const _isProduction = false;
@@ -34,6 +42,9 @@ class MeldBuyProvider extends BuyProvider {
static String get _testApiKey => secrets.meldTestApiKey;
static const List<CryptoCurrency> _notSupportedCrypto = [];
static const List<FiatCurrency> _notSupportedFiat = [];
static String get _testPublicKey => '' ; //secrets.meldTestPublicKey;
@override
+12 -1
View File
@@ -7,6 +7,7 @@ import 'package:cake_wallet/buy/buy_provider.dart';
import 'package:cake_wallet/buy/buy_provider_description.dart';
import 'package:cake_wallet/buy/buy_quote.dart';
import 'package:cake_wallet/buy/order.dart';
import 'package:cake_wallet/buy/pairs_utils.dart';
import 'package:cake_wallet/buy/payment_method.dart';
import 'package:cake_wallet/entities/fiat_currency.dart';
import 'package:cake_wallet/exchange/trade_state.dart';
@@ -31,7 +32,14 @@ class MoonPayProvider extends BuyProvider {
}) : baseSellUrl = isTestEnvironment ? _baseSellTestUrl : _baseSellProductUrl,
baseBuyUrl = isTestEnvironment ? _baseBuyTestUrl : _baseBuyProductUrl,
this._settingsStore = settingsStore,
super(wallet: wallet, isTestEnvironment: isTestEnvironment, ledgerVM: null);
super(
wallet: wallet,
isTestEnvironment: isTestEnvironment,
ledgerVM: null,
supportedCryptoList: supportedCryptoToFiatPairs(
notSupportedCrypto: _notSupportedCrypto, notSupportedFiat: _notSupportedFiat),
supportedFiatList: supportedFiatToCryptoPairs(
notSupportedFiat: _notSupportedFiat, notSupportedCrypto: _notSupportedCrypto));
final SettingsStore _settingsStore;
@@ -48,6 +56,9 @@ class MoonPayProvider extends BuyProvider {
static const _transactionsSuffix = '/v1/transactions';
static const List<CryptoCurrency> _notSupportedCrypto = [];
static const List<FiatCurrency> _notSupportedFiat = [];
final String baseBuyUrl;
final String baseSellUrl;
+11 -1
View File
@@ -4,6 +4,7 @@ import 'dart:developer';
import 'package:cake_wallet/.secrets.g.dart' as secrets;
import 'package:cake_wallet/buy/buy_provider.dart';
import 'package:cake_wallet/buy/buy_quote.dart';
import 'package:cake_wallet/buy/pairs_utils.dart';
import 'package:cake_wallet/buy/payment_method.dart';
import 'package:cake_wallet/entities/fiat_currency.dart';
import 'package:cake_wallet/generated/i18n.dart';
@@ -20,7 +21,13 @@ import 'package:url_launcher/url_launcher.dart';
class OnRamperBuyProvider extends BuyProvider {
OnRamperBuyProvider(this._settingsStore,
{required WalletBase wallet, bool isTestEnvironment = false})
: super(wallet: wallet, isTestEnvironment: isTestEnvironment, ledgerVM: null);
: super(wallet: wallet,
isTestEnvironment: isTestEnvironment,
ledgerVM: null,
supportedCryptoList: supportedCryptoToFiatPairs(
notSupportedCrypto: _notSupportedCrypto, notSupportedFiat: _notSupportedFiat),
supportedFiatList: supportedFiatToCryptoPairs(
notSupportedFiat: _notSupportedFiat, notSupportedCrypto: _notSupportedCrypto));
static const _baseUrl = 'buy.onramper.com';
static const _baseApiUrl = 'api.onramper.com';
@@ -28,6 +35,9 @@ class OnRamperBuyProvider extends BuyProvider {
static const paymentTypes = '/payment-types';
static const supported = '/supported';
static const List<CryptoCurrency> _notSupportedCrypto = [];
static const List<FiatCurrency> _notSupportedFiat = [];
final SettingsStore _settingsStore;
String get _apiKey => secrets.onramperApiKey;
+37
View File
@@ -0,0 +1,37 @@
import 'package:cake_wallet/entities/fiat_currency.dart';
import 'package:cw_core/crypto_currency.dart';
class TradePair<T, U> {
TradePair({required this.from, required this.to});
final T from;
final U to;
}
List<TradePair<CryptoCurrency, FiatCurrency>> supportedCryptoToFiatPairs({
required List<CryptoCurrency> notSupportedCrypto,
required List<FiatCurrency> notSupportedFiat,
}) {
final supportedCrypto =
CryptoCurrency.all.where((crypto) => !notSupportedCrypto.contains(crypto)).toList();
final supportedFiat = FiatCurrency.all.where((fiat) => !notSupportedFiat.contains(fiat)).toList();
return supportedCrypto
.expand((crypto) => supportedFiat
.map((fiat) => TradePair<CryptoCurrency, FiatCurrency>(from: crypto, to: fiat)))
.toList();
}
List<TradePair<FiatCurrency, CryptoCurrency>> supportedFiatToCryptoPairs({
required List<FiatCurrency> notSupportedFiat,
required List<CryptoCurrency> notSupportedCrypto,
}) {
final supportedFiat = FiatCurrency.all.where((fiat) => !notSupportedFiat.contains(fiat)).toList();
final supportedCrypto =
CryptoCurrency.all.where((crypto) => !notSupportedCrypto.contains(crypto)).toList();
return supportedFiat
.expand((fiat) => supportedCrypto
.map((crypto) => TradePair<FiatCurrency, CryptoCurrency>(from: fiat, to: crypto)))
.toList();
}
+12 -1
View File
@@ -4,6 +4,7 @@ import 'dart:developer';
import 'package:cake_wallet/.secrets.g.dart' as secrets;
import 'package:cake_wallet/buy/buy_provider.dart';
import 'package:cake_wallet/buy/buy_quote.dart';
import 'package:cake_wallet/buy/pairs_utils.dart';
import 'package:cake_wallet/buy/payment_method.dart';
import 'package:cake_wallet/entities/fiat_currency.dart';
import 'package:cake_wallet/generated/i18n.dart';
@@ -23,11 +24,21 @@ import 'package:url_launcher/url_launcher.dart';
class RobinhoodBuyProvider extends BuyProvider {
RobinhoodBuyProvider(
{required WalletBase wallet, bool isTestEnvironment = false, LedgerViewModel? ledgerVM})
: super(wallet: wallet, isTestEnvironment: isTestEnvironment, ledgerVM: ledgerVM);
: super(
wallet: wallet,
isTestEnvironment: isTestEnvironment,
ledgerVM: ledgerVM,
supportedCryptoList: supportedCryptoToFiatPairs(
notSupportedCrypto: _notSupportedCrypto, notSupportedFiat: _notSupportedFiat),
supportedFiatList: supportedFiatToCryptoPairs(
notSupportedFiat: _notSupportedFiat, notSupportedCrypto: _notSupportedCrypto));
static const _baseUrl = 'applink.robinhood.com';
static const _cIdBaseUrl = 'exchange-helper.cakewallet.com';
static const List<CryptoCurrency> _notSupportedCrypto = [];
static const List<FiatCurrency> _notSupportedFiat = [];
@override
String get title => 'Robinhood Connect';
+14 -2
View File
@@ -1,6 +1,8 @@
import 'dart:convert';
import 'package:cake_wallet/buy/buy_exception.dart';
import 'package:flutter/src/widgets/framework.dart';
import 'package:cake_wallet/buy/pairs_utils.dart';
import 'package:cake_wallet/entities/fiat_currency.dart';
import 'package:cw_core/crypto_currency.dart';
import 'package:http/http.dart';
import 'package:cake_wallet/buy/buy_amount.dart';
import 'package:cake_wallet/buy/buy_provider.dart';
@@ -14,7 +16,14 @@ import 'package:cake_wallet/.secrets.g.dart' as secrets;
class WyreBuyProvider extends BuyProvider {
WyreBuyProvider({required WalletBase wallet, bool isTestEnvironment = false})
: baseApiUrl = isTestEnvironment ? _baseTestApiUrl : _baseProductApiUrl,
super(wallet: wallet, isTestEnvironment: isTestEnvironment, ledgerVM: null);
super(
wallet: wallet,
isTestEnvironment: isTestEnvironment,
ledgerVM: null,
supportedCryptoList: supportedCryptoToFiatPairs(
notSupportedCrypto: _notSupportedCrypto, notSupportedFiat: _notSupportedFiat),
supportedFiatList: supportedFiatToCryptoPairs(
notSupportedFiat: _notSupportedFiat, notSupportedCrypto: _notSupportedCrypto));
static const _baseTestApiUrl = 'https://api.testwyre.com';
static const _baseProductApiUrl = 'https://api.sendwyre.com';
@@ -30,6 +39,9 @@ class WyreBuyProvider extends BuyProvider {
static const _secretKey = secrets.wyreSecretKey;
static const _accountId = secrets.wyreAccountId;
static const List<CryptoCurrency> _notSupportedCrypto = [];
static const List<FiatCurrency> _notSupportedFiat = [];
@override
String get title => 'Wyre';
+14 -76
View File
@@ -7,7 +7,6 @@ import 'package:cake_wallet/buy/onramper/onramper_buy_provider.dart';
import 'package:cake_wallet/buy/robinhood/robinhood_buy_provider.dart';
import 'package:cake_wallet/di.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:http/http.dart';
enum ProviderType { robinhood, dfx, onramper, moonpay, meld, kriptonim }
@@ -48,82 +47,21 @@ extension ProviderTypeName on ProviderType {
}
class ProvidersHelper {
static List<ProviderType> getAvailableBuyProviderTypes(WalletType walletType) {
switch (walletType) {
case WalletType.nano:
case WalletType.banano:
case WalletType.wownero:
return [ProviderType.onramper];
case WalletType.monero:
return [ProviderType.onramper, ProviderType.dfx];
case WalletType.bitcoin:
case WalletType.polygon:
case WalletType.ethereum:
return [
ProviderType.onramper,
ProviderType.dfx,
ProviderType.robinhood,
ProviderType.moonpay,
ProviderType.kriptonim
];
case WalletType.litecoin:
case WalletType.bitcoinCash:
case WalletType.solana:
return [
ProviderType.onramper,
ProviderType.robinhood,
ProviderType.moonpay,
ProviderType.kriptonim
];
case WalletType.tron:
return [
ProviderType.onramper,
ProviderType.robinhood,
ProviderType.moonpay,
ProviderType.kriptonim
];
case WalletType.decred:
case WalletType.none:
case WalletType.haven:
case WalletType.zano:
return [];
}
}
static List<ProviderType> getAvailableBuyProviderTypes(WalletType walletType) => [
ProviderType.robinhood,
ProviderType.dfx,
ProviderType.onramper,
ProviderType.moonpay,
ProviderType.kriptonim
];
static List<ProviderType> getAvailableSellProviderTypes(WalletType walletType) {
switch (walletType) {
case WalletType.bitcoin:
case WalletType.ethereum:
case WalletType.polygon:
return [
ProviderType.onramper,
ProviderType.moonpay,
ProviderType.dfx,
];
case WalletType.litecoin:
case WalletType.bitcoinCash:
return [ProviderType.moonpay];
case WalletType.solana:
return [
ProviderType.onramper,
ProviderType.moonpay,
];
case WalletType.tron:
return [
ProviderType.moonpay,
];
case WalletType.monero:
return [ProviderType.dfx];
case WalletType.decred:
case WalletType.nano:
case WalletType.banano:
case WalletType.none:
case WalletType.haven:
case WalletType.wownero:
case WalletType.zano:
return [];
}
}
static List<ProviderType> getAvailableSellProviderTypes(WalletType walletType) => [
ProviderType.robinhood,
ProviderType.dfx,
ProviderType.onramper,
ProviderType.moonpay,
ProviderType.kriptonim
];
static BuyProvider? getProviderByType(ProviderType type) {
switch (type) {
+41 -5
View File
@@ -160,9 +160,45 @@ class BuySellPage extends BasePage {
),
])),
bottomSectionPadding: EdgeInsets.only(left: 24, right: 24, bottom: 24),
bottomSection: Column(children: [
Observer(
builder: (_) => LoadingPrimaryButton(
bottomSection: Observer(
builder: (_) => Column(children: [
buySellViewModel.isBuySellQuotFailed
? Padding(
padding: EdgeInsets.only(bottom: 15),
child: Row(
children: [
Expanded(
child: Container(
alignment: Alignment.centerRight,
child: Icon(Icons.warning_amber_rounded,
color: Theme.of(context)
.extension<ExchangePageTheme>()!
.receiveAmountColor,
size: 26),
),
),
Expanded(
flex: 8,
child: Text(
S.of(context).buy_sell_pair_is_not_supported_warning,
textAlign: TextAlign.center,
softWrap: true,
overflow: TextOverflow.ellipsis,
maxLines: 3,
style: TextStyle(
color: Theme.of(context)
.extension<ExchangePageTheme>()!
.receiveAmountColor,
fontWeight: FontWeight.w500,
fontSize: 12,
),
),
),
],
),
)
: Container(),
LoadingPrimaryButton(
text: S.current.choose_a_provider,
onPressed: () async {
if(!_formKey.currentState!.validate()) return;
@@ -172,8 +208,8 @@ class BuySellPage extends BasePage {
textColor: Colors.white,
isDisabled: buySellViewModel.isBuySellQuotFailed,
isLoading: !buySellViewModel.isReadyToTrade &&
!buySellViewModel.isBuySellQuotFailed)),
]),
!buySellViewModel.isBuySellQuotFailed),
])),
)),
));
}
+25 -3
View File
@@ -210,9 +210,12 @@ abstract class BuySellViewModelBase extends WalletChangeListenerViewModel with S
final enteredAmount = double.tryParse(amount.replaceAll(',', '.')) ?? 0;
if (!isReadyToTrade) {
if (!isReadyToTrade && !isBuySellQuotFailed) {
cryptoAmount = S.current.fetching;
return;
} else if (isBuySellQuotFailed) {
cryptoAmount = '';
return;
}
if (bestRateQuote != null) {
@@ -238,8 +241,12 @@ abstract class BuySellViewModelBase extends WalletChangeListenerViewModel with S
final enteredAmount = double.tryParse(amount.replaceAll(',', '.')) ?? 0;
if (!isReadyToTrade) {
if (!isReadyToTrade && !isBuySellQuotFailed) {
fiatAmount = S.current.fetching;
return;
} else if (isBuySellQuotFailed) {
fiatAmount = '';
return;
}
if (bestRateQuote != null) {
@@ -379,7 +386,22 @@ abstract class BuySellViewModelBase extends WalletChangeListenerViewModel with S
Future<void> calculateBestRate() async {
buySellQuotState = BuySellQuotLoading();
final result = await Future.wait<List<Quote>?>(providerList.map((element) => element
final List<BuyProvider> validProviders = providerList.where((provider) {
if (isBuyAction) {
return provider.supportedCryptoList.any((pair) =>
pair.from == cryptoCurrency && pair.to == fiatCurrency);
} else {
return provider.supportedFiatList.any((pair) =>
pair.from == fiatCurrency && pair.to == cryptoCurrency);
}
}).toList();
if (validProviders.isEmpty) {
buySellQuotState = BuySellQuotFailed();
return;
}
final result = await Future.wait<List<Quote>?>(validProviders.map((element) => element
.fetchQuote(
cryptoCurrency: cryptoCurrency,
fiatCurrency: fiatCurrency,