diff --git a/.github/workflows/pr_test_build.yml b/.github/workflows/pr_test_build.yml index 69c632967..acaa12fe0 100644 --- a/.github/workflows/pr_test_build.yml +++ b/.github/workflows/pr_test_build.yml @@ -151,6 +151,7 @@ jobs: echo "const moralisApiKey = '${{ secrets.MORALIS_API_KEY }}';" >> lib/.secrets.g.dart echo "const polygonScanApiKey = '${{ secrets.POLYGON_SCAN_API_KEY }}';" >> cw_evm/lib/.secrets.g.dart echo "const ankrApiKey = '${{ secrets.ANKR_API_KEY }}';" >> cw_solana/lib/.secrets.g.dart + echo "const quantexExchangeMarkup = '${{ secrets.QUANTEX_EXCHANGE_MARKUP }}';" >> lib/.secrets.g.dart echo "const nano2ApiKey = '${{ secrets.NANO2_API_KEY }}';" >> cw_nano/lib/.secrets.g.dart echo "const tronGridApiKey = '${{ secrets.TRON_GRID_API_KEY }}';" >> cw_tron/lib/.secrets.g.dart diff --git a/assets/images/quantex.png b/assets/images/quantex.png new file mode 100644 index 000000000..cfa32b382 Binary files /dev/null and b/assets/images/quantex.png differ diff --git a/cw_bitcoin/lib/bitcoin_wallet.dart b/cw_bitcoin/lib/bitcoin_wallet.dart index 05ba82da0..ccc77bfe8 100644 --- a/cw_bitcoin/lib/bitcoin_wallet.dart +++ b/cw_bitcoin/lib/bitcoin_wallet.dart @@ -1,24 +1,25 @@ -import 'package:bitcoin_base/bitcoin_base.dart'; -import 'package:convert/convert.dart'; +import 'dart:convert'; +import 'package:bip39/bip39.dart' as bip39; +import 'package:bitcoin_base/bitcoin_base.dart'; +import 'package:bitcoin_flutter/bitcoin_flutter.dart' as bitcoin; +import 'package:convert/convert.dart'; +import 'package:cw_bitcoin/bitcoin_address_record.dart'; import 'package:cw_bitcoin/bitcoin_mnemonic.dart'; import 'package:cw_core/encryption_file_utils.dart'; +import 'package:cw_bitcoin/bitcoin_wallet_addresses.dart'; +import 'package:cw_bitcoin/electrum_balance.dart'; +import 'package:cw_bitcoin/electrum_wallet.dart'; +import 'package:cw_bitcoin/electrum_wallet_snapshot.dart'; import 'package:cw_bitcoin/psbt_transaction_builder.dart'; import 'package:cw_core/crypto_currency.dart'; import 'package:cw_core/unspent_coins_info.dart'; +import 'package:cw_core/wallet_info.dart'; +import 'package:flutter/foundation.dart'; import 'package:hive/hive.dart'; import 'package:ledger_bitcoin/ledger_bitcoin.dart'; import 'package:ledger_flutter/ledger_flutter.dart'; import 'package:mobx/mobx.dart'; -import 'package:flutter/foundation.dart'; -import 'package:bitcoin_flutter/bitcoin_flutter.dart' as bitcoin; -import 'package:cw_bitcoin/electrum_wallet_snapshot.dart'; -import 'package:cw_bitcoin/electrum_wallet.dart'; -import 'package:cw_core/wallet_info.dart'; -import 'package:cw_bitcoin/bitcoin_address_record.dart'; -import 'package:cw_bitcoin/electrum_balance.dart'; -import 'package:cw_bitcoin/bitcoin_wallet_addresses.dart'; -import 'package:bip39/bip39.dart' as bip39; part 'bitcoin_wallet.g.dart'; @@ -223,4 +224,23 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store { final rawHex = await _bitcoinLedgerApp!.signPsbt(_ledgerDevice!, psbt: psbt.psbt); return BtcTransaction.fromRaw(hex.encode(rawHex)); } + + @override + Future signMessage(String message, {String? address = null}) async { + if (walletInfo.isHardwareWallet) { + final addressEntry = address != null + ? walletAddresses.allAddresses.firstWhere((element) => element.address == address) + : null; + final index = addressEntry?.index ?? 0; + final isChange = addressEntry?.isHidden == true ? 1 : 0; + final accountPath = walletInfo.derivationInfo?.derivationPath; + final derivationPath = accountPath != null ? "$accountPath/$isChange/$index" : null; + + final signature = await _bitcoinLedgerApp! + .signMessage(_ledgerDevice!, message: ascii.encode(message), signDerivationPath: derivationPath); + return base64Encode(signature); + } + + return super.signMessage(message, address: address); + } } diff --git a/cw_bitcoin/pubspec.lock b/cw_bitcoin/pubspec.lock index e0bafe50b..825012f19 100644 --- a/cw_bitcoin/pubspec.lock +++ b/cw_bitcoin/pubspec.lock @@ -492,10 +492,10 @@ packages: description: path: "." ref: HEAD - resolved-ref: b6ed573cbeb57d5f0d39dfe4254bf9d15b620ab6 - url: "https://github.com/cake-tech/ledger-bitcoin.git" + resolved-ref: f819d37e235e239c315e93856abbf5e5d3b71dab + url: "https://github.com/cake-tech/ledger-bitcoin" source: git - version: "0.0.1" + version: "0.0.2" ledger_flutter: dependency: "direct main" description: diff --git a/cw_bitcoin/pubspec.yaml b/cw_bitcoin/pubspec.yaml index 84254b5b5..265d2f9a2 100644 --- a/cw_bitcoin/pubspec.yaml +++ b/cw_bitcoin/pubspec.yaml @@ -38,7 +38,7 @@ dependencies: ledger_flutter: ^1.0.1 ledger_bitcoin: git: - url: https://github.com/cake-tech/ledger-bitcoin.git + url: https://github.com/cake-tech/ledger-bitcoin dev_dependencies: flutter_test: diff --git a/cw_tron/lib/tron_client.dart b/cw_tron/lib/tron_client.dart index 92684cebc..8eca02af6 100644 --- a/cw_tron/lib/tron_client.dart +++ b/cw_tron/lib/tron_client.dart @@ -208,7 +208,7 @@ class TronClient { TransactionContract(type: contract.contractType, parameter: parameter); // Set the transaction expiration time (maximum 24 hours) - final expireTime = DateTime.now().add(const Duration(hours: 24)); + final expireTime = DateTime.now().add(const Duration(minutes: 30)); // Create a raw transaction TransactionRaw rawTransaction = TransactionRaw( @@ -369,7 +369,7 @@ class TronClient { TransactionContract(type: contract.contractType, parameter: parameter); // Set the transaction expiration time (maximum 24 hours) - final expireTime = DateTime.now().add(const Duration(hours: 24)); + final expireTime = DateTime.now().add(const Duration(minutes: 30)); // Create a raw transaction TransactionRaw rawTransaction = TransactionRaw( diff --git a/lib/exchange/exchange_provider_description.dart b/lib/exchange/exchange_provider_description.dart index 4d9691035..c28de5b72 100644 --- a/lib/exchange/exchange_provider_description.dart +++ b/lib/exchange/exchange_provider_description.dart @@ -22,10 +22,11 @@ class ExchangeProviderDescription extends EnumerableItem with Serializable< ExchangeProviderDescription(title: 'Trocador', raw: 5, image: 'assets/images/trocador.png'); static const exolix = ExchangeProviderDescription(title: 'Exolix', raw: 6, image: 'assets/images/exolix.png'); - static const thorChain = - ExchangeProviderDescription(title: 'ThorChain' , raw: 8, image: 'assets/images/thorchain.png'); - static const all = ExchangeProviderDescription(title: 'All trades', raw: 7, image: ''); + static const thorChain = + ExchangeProviderDescription(title: 'ThorChain', raw: 8, image: 'assets/images/thorchain.png'); + static const quantex = + ExchangeProviderDescription(title: 'Quantex', raw: 9, image: 'assets/images/quantex.png'); static ExchangeProviderDescription deserialize({required int raw}) { switch (raw) { @@ -43,10 +44,12 @@ class ExchangeProviderDescription extends EnumerableItem with Serializable< return trocador; case 6: return exolix; - case 8: - return thorChain; case 7: return all; + case 8: + return thorChain; + case 9: + return quantex; default: throw Exception('Unexpected token: $raw for ExchangeProviderDescription deserialize'); } diff --git a/lib/exchange/provider/quantex_exchange_provider.dart b/lib/exchange/provider/quantex_exchange_provider.dart new file mode 100644 index 000000000..9ab7fbb55 --- /dev/null +++ b/lib/exchange/provider/quantex_exchange_provider.dart @@ -0,0 +1,252 @@ +import 'dart:convert'; + +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_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 QuantexExchangeProvider extends ExchangeProvider { + QuantexExchangeProvider() : super(pairList: supportedPairs(_notSupported)); + + static final List _notSupported = [ + ...(CryptoCurrency.all + .where((element) => ![ + CryptoCurrency.btc, + CryptoCurrency.sol, + CryptoCurrency.eth, + CryptoCurrency.ltc, + CryptoCurrency.ada, + CryptoCurrency.bch, + CryptoCurrency.usdt, + CryptoCurrency.bnb, + CryptoCurrency.xmr, + ].contains(element)) + .toList()) + ]; + + static final markup = secrets.quantexExchangeMarkup; + + static const apiAuthority = 'api.myquantex.com'; + static const getRate = '/api/swap/get-rate'; + static const getCoins = '/api/swap/get-coins'; + static const createOrder = '/api/swap/create-order'; + + @override + String get title => 'Quantex'; + + @override + bool get isAvailable => true; + + @override + bool get isEnabled => true; + + @override + bool get supportsFixedRate => false; + + @override + ExchangeProviderDescription get description => ExchangeProviderDescription.quantex; + + @override + Future checkIsAvailable() async => true; + + @override + Future fetchLimits({ + required CryptoCurrency from, + required CryptoCurrency to, + required bool isFixedRateMode, + }) async { + try { + final uri = Uri.https(apiAuthority, getCoins); + final response = await get(uri); + + final responseJSON = json.decode(response.body) as Map; + + if (response.statusCode != 200) + throw Exception('Unexpected http status: ${response.statusCode}'); + + final coinsInfo = responseJSON['data'] as List; + + for (var coin in coinsInfo) { + if (coin['id'].toString().toUpperCase() == _normalizeCurrency(from)) { + return Limits( + min: double.parse(coin['min'].toString()), + max: double.parse(coin['max'].toString()), + ); + } + } + + // coin not found: + return Limits(min: 0, max: 0); + } catch (e) { + print(e.toString()); + return Limits(min: 0, max: 0); + } + } + + @override + Future 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 = {}; + final params = {}; + final body = { + 'coin_send': _normalizeCurrency(from), + 'coin_receive': _normalizeCurrency(to), + 'ref': 'cake', + }; + + final uri = Uri.https(apiAuthority, getRate, params); + final response = await post(uri, body: body, headers: headers); + final responseBody = json.decode(response.body) as Map; + + if (response.statusCode != 200) + throw Exception('Unexpected http status: ${response.statusCode}'); + + final data = responseBody['data'] as Map; + double rate = double.parse(data['price'].toString()); + return rate; + } catch (e) { + print("error fetching rate: ${e.toString()}"); + return 0.0; + } + } + + @override + Future createTrade({ + required TradeRequest request, + required bool isFixedRateMode, + required bool isSendAll, + }) async { + try { + final headers = {}; + final params = {}; + var body = { + 'coin_send': _normalizeCurrency(request.fromCurrency), + 'coin_receive': _normalizeCurrency(request.toCurrency), + 'amount_send': request.fromAmount, + 'recipient': request.toAddress, + 'ref': 'cake', + 'markup': markup, + }; + + String? fromNetwork = _networkFor(request.fromCurrency); + String? toNetwork = _networkFor(request.toCurrency); + if (fromNetwork != null) body['coin_send_network'] = fromNetwork; + if (toNetwork != null) body['coin_receive_network'] = toNetwork; + + final uri = Uri.https(apiAuthority, createOrder, params); + final response = await post(uri, body: body, headers: headers); + final responseBody = json.decode(response.body) as Map; + + if (response.statusCode == 400 || responseBody["success"] == false) { + final error = responseBody['errors'][0]['msg'] as String; + throw TradeNotCreatedException(description, description: error); + } + + if (response.statusCode != 200) + throw Exception('Unexpected http status: ${response.statusCode}'); + + final responseData = responseBody['data'] as Map; + + return Trade( + id: responseData["order_id"] as String, + inputAddress: responseData["server_address"] as String, + amount: request.fromAmount, + from: request.fromCurrency, + to: request.toCurrency, + provider: description, + createdAt: DateTime.now(), + state: TradeState.created, + payoutAddress: request.toAddress, + isSendAll: isSendAll, + ); + } catch (e) { + print("error creating trade: ${e.toString()}"); + throw TradeNotCreatedException(description, description: e.toString()); + } + } + + @override + Future findTradeById({required String id}) async { + try { + final headers = {}; + final params = {}; + var body = { + 'order_id': id, + }; + + final uri = Uri.https(apiAuthority, createOrder, params); + final response = await post(uri, body: body, headers: headers); + final responseBody = json.decode(response.body) as Map; + + if (response.statusCode == 400 || responseBody["success"] == false) { + final error = responseBody['errors'][0]['msg'] as String; + throw TradeNotCreatedException(description, description: error); + } + + if (response.statusCode != 200) + throw Exception('Unexpected http status: ${response.statusCode}'); + + final responseData = responseBody['data'] as Map; + final fromCurrency = responseData['coin_send'] as String; + final from = CryptoCurrency.fromString(fromCurrency); + final toCurrency = responseData['coin_receive'] as String; + final to = CryptoCurrency.fromString(toCurrency); + final inputAddress = responseData['server_address'] as String; + final status = responseData['status'] as String; + final state = TradeState.deserialize(raw: status); + final response_id = responseData['order_id'] as String; + final expectedSendAmount = responseData['amount_send'] as String; + + return Trade( + id: response_id, + from: from, + to: to, + provider: description, + inputAddress: inputAddress, + amount: expectedSendAmount, + state: state, + ); + } catch (e) { + print("error getting trade: ${e.toString()}"); + throw TradeNotFoundException( + id, + provider: description, + description: e.toString(), + ); + } + } + + String _normalizeCurrency(CryptoCurrency currency) { + switch (currency) { + default: + return currency.title.toUpperCase(); + } + } + + String? _networkFor(CryptoCurrency currency) { + switch (currency) { + case CryptoCurrency.usdt: + return "USDT_ERC20"; + case CryptoCurrency.bnb: + return "BNB_BSC"; + default: + return null; + } + } +} diff --git a/lib/exchange/trade_state.dart b/lib/exchange/trade_state.dart index 2c58a96f4..0a196835e 100644 --- a/lib/exchange/trade_state.dart +++ b/lib/exchange/trade_state.dart @@ -28,6 +28,7 @@ class TradeState extends EnumerableItem with Serializable { TradeState(raw: 'waitingAuthorization', title: 'Waiting authorization'); static const failed = TradeState(raw: 'failed', title: 'Failed'); static const completed = TradeState(raw: 'completed', title: 'Completed'); + static const expired = TradeState(raw: 'expired', title: 'Expired'); static const settling = TradeState(raw: 'settling', title: 'Settlement in progress'); static const settled = TradeState(raw: 'settled', title: 'Settlement completed'); static const wait = TradeState(raw: 'wait', title: 'Waiting'); @@ -39,7 +40,33 @@ class TradeState extends EnumerableItem with Serializable { static const exchanging = TradeState(raw: 'exchanging', title: 'Exchanging'); static const sending = TradeState(raw: 'sending', title: 'Sending'); static const success = TradeState(raw: 'success', title: 'Success'); + static TradeState deserialize({required String raw}) { + + switch (raw) { + case '1': + return unpaid; + case '2': + return paidUnconfirmed; + case '3': + return sending; + case '4': + return confirmed; + case '5': + case '6': + return exchanging; + case '7': + return sending; + case '8': + return complete; + case '9': + return expired; + case '10': + return underpaid; + case '11': + return failed; + } + switch (raw) { case 'NOT_FOUND': return notFound; diff --git a/lib/main.dart b/lib/main.dart index 2bf3a6f94..163ffddfb 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -47,6 +47,7 @@ final rootKey = GlobalKey(); final RouteObserver> routeObserver = RouteObserver>(); Future main() async { + bool isAppRunning = false; await runZonedGuarded(() async { WidgetsFlutterBinding.ensureInitialized(); @@ -67,31 +68,35 @@ Future main() async { await initializeAppConfigs(); runApp(App()); + + isAppRunning = true; }, (error, stackTrace) async { - runApp( - MaterialApp( - debugShowCheckedModeBanner: false, - home: Scaffold( - body: SingleChildScrollView( - child: Container( - margin: EdgeInsets.only(top: 50, left: 20, right: 20, bottom: 20), - child: Column( - children: [ - Text( - 'Error:\n${error.toString()}', - style: TextStyle(fontSize: 22), - ), - Text( - 'Stack trace:\n${stackTrace.toString()}', - style: TextStyle(fontSize: 16), - ), - ], + if (!isAppRunning) { + runApp( + MaterialApp( + debugShowCheckedModeBanner: false, + home: Scaffold( + body: SingleChildScrollView( + child: Container( + margin: EdgeInsets.only(top: 50, left: 20, right: 20, bottom: 20), + child: Column( + children: [ + Text( + 'Error:\n${error.toString()}', + style: TextStyle(fontSize: 22), + ), + Text( + 'Stack trace:\n${stackTrace.toString()}', + style: TextStyle(fontSize: 16), + ), + ], + ), ), ), ), ), - ), - ); + ); + } ExceptionHandler.onError(FlutterErrorDetails(exception: error, stack: stackTrace)); }); diff --git a/lib/src/screens/connect_device/connect_device_page.dart b/lib/src/screens/connect_device/connect_device_page.dart index dfb32beba..80b396a34 100644 --- a/lib/src/screens/connect_device/connect_device_page.dart +++ b/lib/src/screens/connect_device/connect_device_page.dart @@ -3,6 +3,7 @@ import 'dart:io'; import 'package:cake_wallet/generated/i18n.dart'; import 'package:cake_wallet/src/screens/base_page.dart'; +// import 'package:cake_wallet/src/screens/connect_device/debug_device_page.dart'; import 'package:cake_wallet/src/screens/connect_device/widgets/device_tile.dart'; import 'package:cake_wallet/themes/extensions/cake_text_theme.dart'; import 'package:cake_wallet/utils/responsive_layout_util.dart'; @@ -78,15 +79,13 @@ class ConnectDevicePageBodyState extends State { @override void initState() { super.initState(); - Future.delayed( - Duration(seconds: 1), - () => _bleRefresh = ledger.scan().listen((device) => setState(() => bleDevices.add(device))), - ); - // _bleRefreshTimer = Timer.periodic(Duration(seconds: 1), (_) => _refreshBleDevices()); + WidgetsBinding.instance.addPostFrameCallback((_) { + _bleRefreshTimer = Timer.periodic(Duration(seconds: 1), (_) => _refreshBleDevices()); - if (Platform.isAndroid) { - _usbRefreshTimer = Timer.periodic(Duration(seconds: 1), (_) => _refreshUsbDevices()); - } + if (Platform.isAndroid) { + _usbRefreshTimer = Timer.periodic(Duration(seconds: 1), (_) => _refreshUsbDevices()); + } + }); } @override @@ -103,14 +102,16 @@ class ConnectDevicePageBodyState extends State { } Future _refreshBleDevices() async { - final isBleEnabled = await Permission.bluetooth.serviceStatus.isEnabled; - - setState(() => bleIsEnabled = isBleEnabled); - - if (isBleEnabled) { - _bleRefresh = ledger.scan().listen((device) => setState(() => bleDevices.add(device))); + try { + _bleRefresh = ledger.scan().listen((device) => setState(() => bleDevices.add(device))) + ..onError((e) { + throw e as Exception; + }); + setState(() => bleIsEnabled = true); _bleRefreshTimer?.cancel(); _bleRefreshTimer = null; + } catch (e) { + setState(() => bleIsEnabled = false); } } @@ -142,6 +143,15 @@ class ConnectDevicePageBodyState extends State { textAlign: TextAlign.center, ), ), + // DeviceTile( + // onPressed: () => Navigator.of(context).push( + // MaterialPageRoute( + // builder: (BuildContext context) => DebugDevicePage(), + // ), + // ), + // title: "Debug Ledger", + // leading: imageLedger, + // ), if (!bleIsEnabled) Padding( padding: EdgeInsets.only(left: 20, right: 20, bottom: 20), diff --git a/lib/src/screens/dashboard/desktop_widgets/desktop_wallet_selection_dropdown.dart b/lib/src/screens/dashboard/desktop_widgets/desktop_wallet_selection_dropdown.dart index 5bdae1248..cc53968b9 100644 --- a/lib/src/screens/dashboard/desktop_widgets/desktop_wallet_selection_dropdown.dart +++ b/lib/src/screens/dashboard/desktop_widgets/desktop_wallet_selection_dropdown.dart @@ -120,22 +120,25 @@ class _DesktopWalletSelectionDropDownState extends State( - context: context, - builder: (dialogContext) { - return AlertWithTwoActions( - alertTitle: S.of(context).change_wallet_alert_title, - alertContent: S.of(context).change_wallet_alert_content(selectedWallet.name), - leftButtonText: S.of(context).cancel, - rightButtonText: S.of(context).change, - actionLeftButton: () => Navigator.of(dialogContext).pop(false), - actionRightButton: () => Navigator.of(dialogContext).pop(true)); - }) ?? - false; - if (confirmed) { - await _loadWallet(selectedWallet); - } + WidgetsBinding.instance.addPostFrameCallback((_) async { + final confirmed = await showPopUp( + context: context, + builder: (dialogContext) { + return AlertWithTwoActions( + alertTitle: S.of(context).change_wallet_alert_title, + alertContent: S.of(context).change_wallet_alert_content(selectedWallet.name), + leftButtonText: S.of(context).cancel, + rightButtonText: S.of(context).change, + actionLeftButton: () => Navigator.of(dialogContext).pop(false), + actionRightButton: () => Navigator.of(dialogContext).pop(true)); + }) ?? + false; + + if (confirmed) { + await _loadWallet(selectedWallet); + } + }); } Image _imageFor({required WalletType type}) { diff --git a/lib/view_model/exchange/exchange_trade_view_model.dart b/lib/view_model/exchange/exchange_trade_view_model.dart index 94fec2fa2..c5ce7a591 100644 --- a/lib/view_model/exchange/exchange_trade_view_model.dart +++ b/lib/view_model/exchange/exchange_trade_view_model.dart @@ -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/quantex_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/thorchain_exchange.provider.dart'; @@ -48,6 +49,9 @@ abstract class ExchangeTradeViewModelBase with Store { case ExchangeProviderDescription.exolix: _provider = ExolixExchangeProvider(); break; + case ExchangeProviderDescription.quantex: + _provider = QuantexExchangeProvider(); + break; case ExchangeProviderDescription.thorChain: _provider = ThorChainExchangeProvider(tradesStore: trades); break; diff --git a/lib/view_model/exchange/exchange_view_model.dart b/lib/view_model/exchange/exchange_view_model.dart index e5533f48a..1560a4be0 100644 --- a/lib/view_model/exchange/exchange_view_model.dart +++ b/lib/view_model/exchange/exchange_view_model.dart @@ -30,6 +30,7 @@ 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/quantex_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/thorchain_exchange.provider.dart'; @@ -157,6 +158,7 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with useTorOnly: _useTorOnly, providerStates: _settingsStore.trocadorProviderStates), ThorChainExchangeProvider(tradesStore: trades), if (FeatureFlag.isExolixEnabled) ExolixExchangeProvider(), + QuantexExchangeProvider(), ]; @observable diff --git a/lib/view_model/trade_details_view_model.dart b/lib/view_model/trade_details_view_model.dart index 1da322778..c88008982 100644 --- a/lib/view_model/trade_details_view_model.dart +++ b/lib/view_model/trade_details_view_model.dart @@ -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/quantex_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/thorchain_exchange.provider.dart'; @@ -56,6 +57,9 @@ abstract class TradeDetailsViewModelBase with Store { case ExchangeProviderDescription.thorChain: _provider = ThorChainExchangeProvider(tradesStore: trades); break; + case ExchangeProviderDescription.quantex: + _provider = QuantexExchangeProvider(); + break; } _updateItems(); @@ -80,6 +84,8 @@ abstract class TradeDetailsViewModelBase with Store { return 'https://exolix.com/transaction/${trade.id}'; case ExchangeProviderDescription.thorChain: return 'https://track.ninerealms.com/${trade.id}'; + case ExchangeProviderDescription.quantex: + return 'https://myquantex.com/send/${trade.id}'; } return null; } diff --git a/scripts/android/app_env.sh b/scripts/android/app_env.sh index 3e5db68d2..b520a3179 100644 --- a/scripts/android/app_env.sh +++ b/scripts/android/app_env.sh @@ -15,15 +15,15 @@ TYPES=($MONERO_COM $CAKEWALLET $HAVEN) APP_ANDROID_TYPE=$1 MONERO_COM_NAME="Monero.com" -MONERO_COM_VERSION="1.13.1" -MONERO_COM_BUILD_NUMBER=87 +MONERO_COM_VERSION="1.13.2" +MONERO_COM_BUILD_NUMBER=88 MONERO_COM_BUNDLE_ID="com.monero.app" MONERO_COM_PACKAGE="com.monero.app" MONERO_COM_SCHEME="monero.com" CAKEWALLET_NAME="Cake Wallet" -CAKEWALLET_VERSION="4.16.1" -CAKEWALLET_BUILD_NUMBER=211 +CAKEWALLET_VERSION="4.16.2" +CAKEWALLET_BUILD_NUMBER=212 CAKEWALLET_BUNDLE_ID="com.cakewallet.cake_wallet" CAKEWALLET_PACKAGE="com.cakewallet.cake_wallet" CAKEWALLET_SCHEME="cakewallet" diff --git a/scripts/android/pubspec_gen.sh b/scripts/android/pubspec_gen.sh index d238052fe..bc7985506 100755 --- a/scripts/android/pubspec_gen.sh +++ b/scripts/android/pubspec_gen.sh @@ -10,7 +10,7 @@ case $APP_ANDROID_TYPE in CONFIG_ARGS="--monero" ;; $CAKEWALLET) - CONFIG_ARGS="--monero --bitcoin --haven --ethereum --polygon --nano --bitcoinCash --solana" + CONFIG_ARGS="--monero --bitcoin --haven --ethereum --polygon --nano --bitcoinCash --solana --tron" ;; $HAVEN) CONFIG_ARGS="--haven" diff --git a/scripts/ios/app_config.sh b/scripts/ios/app_config.sh index 9f59d6632..ab7fbd422 100755 --- a/scripts/ios/app_config.sh +++ b/scripts/ios/app_config.sh @@ -28,7 +28,7 @@ case $APP_IOS_TYPE in CONFIG_ARGS="--monero" ;; $CAKEWALLET) - CONFIG_ARGS="--monero --bitcoin --haven --ethereum --polygon --nano --bitcoinCash --solana" + CONFIG_ARGS="--monero --bitcoin --haven --ethereum --polygon --nano --bitcoinCash --solana --tron" ;; $HAVEN) diff --git a/scripts/ios/app_env.sh b/scripts/ios/app_env.sh index c5a2e6306..f70963745 100644 --- a/scripts/ios/app_env.sh +++ b/scripts/ios/app_env.sh @@ -13,13 +13,13 @@ TYPES=($MONERO_COM $CAKEWALLET $HAVEN) APP_IOS_TYPE=$1 MONERO_COM_NAME="Monero.com" -MONERO_COM_VERSION="1.13.1" -MONERO_COM_BUILD_NUMBER=85 +MONERO_COM_VERSION="1.13.2" +MONERO_COM_BUILD_NUMBER=86 MONERO_COM_BUNDLE_ID="com.cakewallet.monero" CAKEWALLET_NAME="Cake Wallet" -CAKEWALLET_VERSION="4.16.1" -CAKEWALLET_BUILD_NUMBER=239 +CAKEWALLET_VERSION="4.16.2" +CAKEWALLET_BUILD_NUMBER=240 CAKEWALLET_BUNDLE_ID="com.fotolockr.cakewallet" HAVEN_NAME="Haven" diff --git a/scripts/macos/app_config.sh b/scripts/macos/app_config.sh index bd1417c4b..a1143bb12 100755 --- a/scripts/macos/app_config.sh +++ b/scripts/macos/app_config.sh @@ -31,7 +31,7 @@ case $APP_MACOS_TYPE in $MONERO_COM) CONFIG_ARGS="--monero";; $CAKEWALLET) - CONFIG_ARGS="--monero --bitcoin --ethereum --polygon --nano --bitcoinCash --solana";; #--haven + CONFIG_ARGS="--monero --bitcoin --ethereum --polygon --nano --bitcoinCash --solana --tron";; #--haven esac cp -rf pubspec_description.yaml pubspec.yaml diff --git a/scripts/macos/app_env.sh b/scripts/macos/app_env.sh index ebe3115be..afdac3e6c 100755 --- a/scripts/macos/app_env.sh +++ b/scripts/macos/app_env.sh @@ -16,13 +16,13 @@ if [ -n "$1" ]; then fi MONERO_COM_NAME="Monero.com" -MONERO_COM_VERSION="1.3.1" -MONERO_COM_BUILD_NUMBER=18 +MONERO_COM_VERSION="1.3.2" +MONERO_COM_BUILD_NUMBER=19 MONERO_COM_BUNDLE_ID="com.cakewallet.monero" CAKEWALLET_NAME="Cake Wallet" -CAKEWALLET_VERSION="1.9.1" -CAKEWALLET_BUILD_NUMBER=72 +CAKEWALLET_VERSION="1.9.2" +CAKEWALLET_BUILD_NUMBER=73 CAKEWALLET_BUNDLE_ID="com.fotolockr.cakewallet" if ! [[ " ${TYPES[*]} " =~ " ${APP_MACOS_TYPE} " ]]; then diff --git a/tool/utils/secret_key.dart b/tool/utils/secret_key.dart index 89e4de12d..9559e83b3 100644 --- a/tool/utils/secret_key.dart +++ b/tool/utils/secret_key.dart @@ -38,6 +38,7 @@ class SecretKey { SecretKey('walletConnectProjectId', () => ''), SecretKey('moralisApiKey', () => ''), SecretKey('ankrApiKey', () => ''), + SecretKey('quantexExchangeMarkup', () => ''), ]; static final evmChainsSecrets = [