diff --git a/assets/text/Monerocom_Release_Notes.txt b/assets/text/Monerocom_Release_Notes.txt index d5297ebe1..faad67777 100644 --- a/assets/text/Monerocom_Release_Notes.txt +++ b/assets/text/Monerocom_Release_Notes.txt @@ -1 +1 @@ -Generic bug fixes and enhancements \ No newline at end of file +Bug fixes and generic enhancements \ No newline at end of file diff --git a/assets/text/Release_Notes.txt b/assets/text/Release_Notes.txt index f9b05cea2..faad67777 100644 --- a/assets/text/Release_Notes.txt +++ b/assets/text/Release_Notes.txt @@ -1,3 +1 @@ -Hardware wallets support for Bitcoin, Ethereum and Polygon -Security enhancements Bug fixes and generic enhancements \ No newline at end of file diff --git a/lib/exchange/trade.dart b/lib/exchange/trade.dart index 6cc3fddbe..aeb544ece 100644 --- a/lib/exchange/trade.dart +++ b/lib/exchange/trade.dart @@ -5,9 +5,6 @@ import 'package:cw_core/format_amount.dart'; import 'package:cw_core/hive_type_ids.dart'; import 'package:hive/hive.dart'; -part 'trade.g.dart'; - -@HiveType(typeId: Trade.typeId) class Trade extends HiveObject { Trade({ required this.id, @@ -32,6 +29,7 @@ class Trade extends HiveObject { this.txId, this.isRefund, this.isSendAll, + this.router, }) { if (provider != null) providerRaw = provider.raw; @@ -121,21 +119,26 @@ class Trade extends HiveObject { @HiveField(21) bool? isSendAll; + @HiveField(22) + String? router; + static Trade fromMap(Map map) { return Trade( - id: map['id'] as String, - provider: ExchangeProviderDescription.deserialize(raw: map['provider'] as int), - from: CryptoCurrency.deserialize(raw: map['input'] as int), - to: CryptoCurrency.deserialize(raw: map['output'] as int), - createdAt: - map['date'] != null ? DateTime.fromMillisecondsSinceEpoch(map['date'] as int) : null, - amount: map['amount'] as String, - walletId: map['wallet_id'] as String, - fromWalletAddress: map['from_wallet_address'] as String?, - memo: map['memo'] as String?, - txId: map['tx_id'] as String?, - isRefund: map['isRefund'] as bool?, - isSendAll: map['isSendAll'] as bool?); + id: map['id'] as String, + provider: ExchangeProviderDescription.deserialize(raw: map['provider'] as int), + from: CryptoCurrency.deserialize(raw: map['input'] as int), + to: CryptoCurrency.deserialize(raw: map['output'] as int), + createdAt: + map['date'] != null ? DateTime.fromMillisecondsSinceEpoch(map['date'] as int) : null, + amount: map['amount'] as String, + walletId: map['wallet_id'] as String, + fromWalletAddress: map['from_wallet_address'] as String?, + memo: map['memo'] as String?, + txId: map['tx_id'] as String?, + isRefund: map['isRefund'] as bool?, + isSendAll: map['isSendAll'] as bool?, + router: map['router'] as String?, + ); } Map toMap() { @@ -152,8 +155,111 @@ class Trade extends HiveObject { 'tx_id': txId, 'isRefund': isRefund, 'isSendAll': isSendAll, + 'router': router, }; } String amountFormatted() => formatAmount(amount); } + +class TradeAdapter extends TypeAdapter { + @override + final int typeId = Trade.typeId; + + @override + Trade read(BinaryReader reader) { + final numOfFields = reader.readByte(); + final fields = {}; + for (int i = 0; i < numOfFields; i++) { + try { + fields[reader.readByte()] = reader.read(); + } catch (_) {} + } + + return Trade( + id: fields[0] == null ? '' : fields[0] as String, + amount: fields[7] == null ? '' : fields[7] as String, + createdAt: fields[5] as DateTime?, + expiredAt: fields[6] as DateTime?, + inputAddress: fields[8] as String?, + extraId: fields[9] as String?, + outputTransaction: fields[10] as String?, + refundAddress: fields[11] as String?, + walletId: fields[12] as String?, + payoutAddress: fields[13] as String?, + password: fields[14] as String?, + providerId: fields[15] as String?, + providerName: fields[16] as String?, + fromWalletAddress: fields[17] as String?, + memo: fields[18] as String?, + txId: fields[19] as String?, + isRefund: fields[20] as bool?, + isSendAll: fields[21] as bool?, + router: fields[22] as String?, + ) + ..providerRaw = fields[1] == null ? 0 : fields[1] as int + ..fromRaw = fields[2] == null ? 0 : fields[2] as int + ..toRaw = fields[3] == null ? 0 : fields[3] as int + ..stateRaw = fields[4] == null ? '' : fields[4] as String; + } + + @override + void write(BinaryWriter writer, Trade obj) { + writer + ..writeByte(23) + ..writeByte(0) + ..write(obj.id) + ..writeByte(1) + ..write(obj.providerRaw) + ..writeByte(2) + ..write(obj.fromRaw) + ..writeByte(3) + ..write(obj.toRaw) + ..writeByte(4) + ..write(obj.stateRaw) + ..writeByte(5) + ..write(obj.createdAt) + ..writeByte(6) + ..write(obj.expiredAt) + ..writeByte(7) + ..write(obj.amount) + ..writeByte(8) + ..write(obj.inputAddress) + ..writeByte(9) + ..write(obj.extraId) + ..writeByte(10) + ..write(obj.outputTransaction) + ..writeByte(11) + ..write(obj.refundAddress) + ..writeByte(12) + ..write(obj.walletId) + ..writeByte(13) + ..write(obj.payoutAddress) + ..writeByte(14) + ..write(obj.password) + ..writeByte(15) + ..write(obj.providerId) + ..writeByte(16) + ..write(obj.providerName) + ..writeByte(17) + ..write(obj.fromWalletAddress) + ..writeByte(18) + ..write(obj.memo) + ..writeByte(19) + ..write(obj.txId) + ..writeByte(20) + ..write(obj.isRefund) + ..writeByte(21) + ..write(obj.isSendAll) + ..writeByte(22) + ..write(obj.router); + } + + @override + int get hashCode => typeId.hashCode; + + @override + bool operator ==(Object other) => + identical(this, other) || + other is TradeAdapter && runtimeType == other.runtimeType && typeId == other.typeId; +} diff --git a/lib/main.dart b/lib/main.dart index 8cb845f88..63edee409 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -42,12 +42,34 @@ import 'package:flutter_mobx/flutter_mobx.dart'; import 'package:hive/hive.dart'; import 'package:cw_core/root_dir.dart'; import 'package:shared_preferences/shared_preferences.dart'; +import 'package:flutter_mobx/flutter_mobx.dart'; +import 'package:cake_wallet/themes/theme_base.dart'; +import 'package:cake_wallet/router.dart' as Router; +import 'package:cake_wallet/routes.dart'; +import 'package:cake_wallet/generated/i18n.dart'; +import 'package:cake_wallet/reactions/bootstrap.dart'; +import 'package:cake_wallet/store/app_store.dart'; +import 'package:cake_wallet/store/authentication_store.dart'; +import 'package:cake_wallet/entities/transaction_description.dart'; +import 'package:cake_wallet/entities/get_encryption_key.dart'; +import 'package:cake_wallet/entities/contact.dart'; +import 'package:cw_core/node.dart'; +import 'package:cw_core/wallet_info.dart'; +import 'package:cake_wallet/entities/default_settings_migration.dart'; +import 'package:cw_core/wallet_type.dart'; +import 'package:cake_wallet/entities/template.dart'; +import 'package:cake_wallet/exchange/trade.dart'; +import 'package:cake_wallet/exchange/exchange_template.dart'; +import 'package:cake_wallet/src/screens/root/root.dart'; +import 'package:cw_core/unspent_coins_info.dart'; +import 'package:cake_wallet/monero/monero.dart'; +import 'package:cw_core/cake_hive.dart'; import 'package:uni_links/uni_links.dart'; import 'package:cw_core/window_size.dart'; final navigatorKey = GlobalKey(); final rootKey = GlobalKey(); -final RouteObserver routeObserver = RouteObserver(); +final RouteObserver> routeObserver = RouteObserver>(); Future main() async { await runZonedGuarded(() async { @@ -71,6 +93,31 @@ Future main() async { runApp(App()); }, (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), + ), + ], + ), + ), + ), + ), + ), + ); + ExceptionHandler.onError(FlutterErrorDetails(exception: error, stack: stackTrace)); }); } @@ -231,61 +278,6 @@ class App extends StatefulWidget { } class AppState extends State with SingleTickerProviderStateMixin { - AppState() : yatStore = getIt.get(); - - YatStore yatStore; - StreamSubscription? stream; - - @override - void initState() { - super.initState(); - //_handleIncomingLinks(); - //_handleInitialUri(); - } - - Future _handleInitialUri() async { - try { - final uri = await getInitialUri(); - print('uri: $uri'); - if (uri == null) { - return; - } - if (!mounted) return; - //_fetchEmojiFromUri(uri); - } catch (e) { - if (!mounted) return; - print(e.toString()); - } - } - - void _handleIncomingLinks() { - if (!kIsWeb) { - stream = getUriLinksStream().listen((Uri? uri) { - print('uri: $uri'); - if (!mounted) return; - //_fetchEmojiFromUri(uri); - }, onError: (Object error) { - if (!mounted) return; - print('Error: $error'); - }); - } - } - - void _fetchEmojiFromUri(Uri uri) { - //final queryParameters = uri.queryParameters; - //if (queryParameters?.isEmpty ?? true) { - // return; - //} - //final emoji = queryParameters['eid']; - //final refreshToken = queryParameters['refresh_token']; - //if ((emoji?.isEmpty ?? true)||(refreshToken?.isEmpty ?? true)) { - // return; - //} - //yatStore.emoji = emoji; - //yatStore.refreshToken = refreshToken; - //yatStore.emojiIncommingSC.add(emoji); - } - @override Widget build(BuildContext context) { return Observer(builder: (BuildContext context) { diff --git a/lib/src/screens/restore/restore_options_page.dart b/lib/src/screens/restore/restore_options_page.dart index 454d124da..a703c9f9e 100644 --- a/lib/src/screens/restore/restore_options_page.dart +++ b/lib/src/screens/restore/restore_options_page.dart @@ -1,3 +1,5 @@ +import 'dart:io'; + import 'package:cake_wallet/core/execution_state.dart'; import 'package:cake_wallet/di.dart'; import 'package:cake_wallet/generated/i18n.dart'; @@ -13,6 +15,9 @@ import 'package:cake_wallet/utils/responsive_layout_util.dart'; import 'package:cake_wallet/utils/show_pop_up.dart'; import 'package:cake_wallet/view_model/restore/restore_from_qr_vm.dart'; import 'package:cake_wallet/view_model/restore/wallet_restore_from_qr_code.dart'; +import 'package:cake_wallet/wallet_type_utils.dart'; +import 'package:cw_core/hardware/device_connection_type.dart'; +import 'package:cw_core/wallet_type.dart'; import 'package:flutter/material.dart'; import 'package:permission_handler/permission_handler.dart'; @@ -24,6 +29,19 @@ class RestoreOptionsPage extends BasePage { final bool isNewInstall; + bool get _doesSupportHardwareWallets { + if (!DeviceInfo.instance.isMobile) { + return false; + } + + if (isMoneroOnly) { + return DeviceConnectionType.supportedConnectionTypes(WalletType.monero, Platform.isIOS) + .isNotEmpty; + } + + return true; + } + @override Widget body(BuildContext context) { final imageColor = Theme.of(context).extension()!.titleColor; @@ -57,7 +75,7 @@ class RestoreOptionsPage extends BasePage { description: S.of(context).restore_description_from_backup, ), ), - if (DeviceInfo.instance.isMobile) + if (_doesSupportHardwareWallets) Padding( padding: EdgeInsets.only(top: 24), child: OptionTile( diff --git a/lib/src/screens/root/root.dart b/lib/src/screens/root/root.dart index afdd14865..b6406dfbd 100644 --- a/lib/src/screens/root/root.dart +++ b/lib/src/screens/root/root.dart @@ -1,10 +1,7 @@ import 'dart:async'; import 'package:cake_wallet/core/auth_service.dart'; import 'package:cake_wallet/core/totp_request_details.dart'; -import 'package:cake_wallet/generated/i18n.dart'; -import 'package:cake_wallet/reactions/wallet_connect.dart'; import 'package:cake_wallet/utils/device_info.dart'; -import 'package:cake_wallet/utils/payment_request.dart'; import 'package:cake_wallet/view_model/link_view_model.dart'; import 'package:cw_core/wallet_base.dart'; import 'package:flutter/material.dart'; @@ -13,7 +10,6 @@ import 'package:cake_wallet/src/screens/auth/auth_page.dart'; import 'package:cake_wallet/store/app_store.dart'; import 'package:cake_wallet/store/authentication_store.dart'; import 'package:cake_wallet/entities/qr_scanner.dart'; -import 'package:fluttertoast/fluttertoast.dart'; import 'package:mobx/mobx.dart'; import 'package:uni_links/uni_links.dart'; import 'package:cake_wallet/src/screens/setup_2fa/setup_2fa_enter_code_page.dart'; diff --git a/lib/view_model/hardware_wallet/ledger_view_model.dart b/lib/view_model/hardware_wallet/ledger_view_model.dart index 06ddaf275..f05b1c805 100644 --- a/lib/view_model/hardware_wallet/ledger_view_model.dart +++ b/lib/view_model/hardware_wallet/ledger_view_model.dart @@ -1,8 +1,12 @@ +import 'dart:io'; + import 'package:cake_wallet/bitcoin/bitcoin.dart'; import 'package:cake_wallet/ethereum/ethereum.dart'; import 'package:cake_wallet/generated/i18n.dart'; import 'package:cake_wallet/polygon/polygon.dart'; import 'package:cake_wallet/utils/device_info.dart'; +import 'package:cake_wallet/wallet_type_utils.dart'; +import 'package:cw_core/hardware/device_connection_type.dart'; import 'package:cw_core/wallet_base.dart'; import 'package:cw_core/wallet_type.dart'; import 'package:ledger_flutter/ledger_flutter.dart'; @@ -11,8 +15,21 @@ import 'package:permission_handler/permission_handler.dart'; class LedgerViewModel { late final Ledger ledger; + bool get _doesSupportHardwareWallets { + if (!DeviceInfo.instance.isMobile) { + return false; + } + + if (isMoneroOnly) { + return DeviceConnectionType.supportedConnectionTypes(WalletType.monero, Platform.isIOS) + .isNotEmpty; + } + + return true; + } + LedgerViewModel() { - if (DeviceInfo.instance.isMobile) { + if (_doesSupportHardwareWallets) { ledger = Ledger( options: LedgerOptions( scanMode: ScanMode.balanced, diff --git a/scripts/android/app_env.sh b/scripts/android/app_env.sh index c671a013f..3e5db68d2 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.0" -MONERO_COM_BUILD_NUMBER=86 +MONERO_COM_VERSION="1.13.1" +MONERO_COM_BUILD_NUMBER=87 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.0" -CAKEWALLET_BUILD_NUMBER=210 +CAKEWALLET_VERSION="4.16.1" +CAKEWALLET_BUILD_NUMBER=211 CAKEWALLET_BUNDLE_ID="com.cakewallet.cake_wallet" CAKEWALLET_PACKAGE="com.cakewallet.cake_wallet" CAKEWALLET_SCHEME="cakewallet" diff --git a/scripts/ios/app_env.sh b/scripts/ios/app_env.sh index f7887e4bf..c5a2e6306 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.0" -MONERO_COM_BUILD_NUMBER=84 +MONERO_COM_VERSION="1.13.1" +MONERO_COM_BUILD_NUMBER=85 MONERO_COM_BUNDLE_ID="com.cakewallet.monero" CAKEWALLET_NAME="Cake Wallet" -CAKEWALLET_VERSION="4.16.0" -CAKEWALLET_BUILD_NUMBER=236 +CAKEWALLET_VERSION="4.16.1" +CAKEWALLET_BUILD_NUMBER=239 CAKEWALLET_BUNDLE_ID="com.fotolockr.cakewallet" HAVEN_NAME="Haven" diff --git a/scripts/macos/app_env.sh b/scripts/macos/app_env.sh index e16df1e61..ebe3115be 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.0" -MONERO_COM_BUILD_NUMBER=17 +MONERO_COM_VERSION="1.3.1" +MONERO_COM_BUILD_NUMBER=18 MONERO_COM_BUNDLE_ID="com.cakewallet.monero" CAKEWALLET_NAME="Cake Wallet" -CAKEWALLET_VERSION="1.9.0" -CAKEWALLET_BUILD_NUMBER=71 +CAKEWALLET_VERSION="1.9.1" +CAKEWALLET_BUILD_NUMBER=72 CAKEWALLET_BUNDLE_ID="com.fotolockr.cakewallet" if ! [[ " ${TYPES[*]} " =~ " ${APP_MACOS_TYPE} " ]]; then