diff --git a/.github/workflows/pr_test_build.yml b/.github/workflows/pr_test_build.yml index 788d02126..b0177965e 100644 --- a/.github/workflows/pr_test_build.yml +++ b/.github/workflows/pr_test_build.yml @@ -80,7 +80,7 @@ jobs: /opt/android/cake_wallet/cw_monero/ios/External /opt/android/cake_wallet/cw_shared_external/ios/External /opt/android/cake_wallet/scripts/monero_c/release - key: ${{ hashFiles('**/prepare_moneroc.sh' ,'**/build_monero_all.sh', '**/build_haven.sh', '**/monero_api.cpp') }} + key: ${{ hashFiles('**/prepare_moneroc.sh' ,'**/build_monero_all.sh') }} - if: ${{ steps.cache-externals.outputs.cache-hit != 'true' }} name: Generate Externals diff --git a/assets/litecoin_electrum_server_list.yml b/assets/litecoin_electrum_server_list.yml index e61d0996c..991762885 100644 --- a/assets/litecoin_electrum_server_list.yml +++ b/assets/litecoin_electrum_server_list.yml @@ -1,2 +1,4 @@ - - uri: ltc-electrum.cakewallet.com:50002 \ No newline at end of file + uri: ltc-electrum.cakewallet.com:50002 + useSSL: true + isDefault: true \ No newline at end of file diff --git a/cw_bitcoin/lib/electrum_wallet.dart b/cw_bitcoin/lib/electrum_wallet.dart index db42e2356..6cc82780f 100644 --- a/cw_bitcoin/lib/electrum_wallet.dart +++ b/cw_bitcoin/lib/electrum_wallet.dart @@ -97,26 +97,7 @@ abstract class ElectrumWalletBase this.walletInfo = walletInfo; transactionHistory = ElectrumTransactionHistory(walletInfo: walletInfo, password: password); - reaction((_) => syncStatus, (SyncStatus syncStatus) async { - if (syncStatus is! AttemptingSyncStatus && syncStatus is! SyncedTipSyncStatus) { - silentPaymentsScanningActive = syncStatus is SyncingSyncStatus; - } - - if (syncStatus is NotConnectedSyncStatus) { - // Needs to re-subscribe to all scripthashes when reconnected - _scripthashesUpdateSubject = {}; - - // TODO: double check this and make sure it doesn't cause any un-necessary calls - // await this.electrumClient.connectToUri(node!.uri, useSSL: node!.useSSL); - } - - // Message is shown on the UI for 3 seconds, revert to synced - if (syncStatus is SyncedTipSyncStatus) { - Timer(Duration(seconds: 3), () { - if (this.syncStatus is SyncedTipSyncStatus) this.syncStatus = SyncedSyncStatus(); - }); - } - }); + reaction((_) => syncStatus, _syncStatusReaction); } static bitcoin.HDWallet getAccountHDWallet( @@ -201,6 +182,8 @@ abstract class ElectrumWalletBase @observable bool silentPaymentsScanningActive = false; + bool _isTryingToConnect = false; + @action Future setSilentPaymentsScanning(bool active, bool usingElectrs) async { silentPaymentsScanningActive = active; @@ -1830,6 +1813,38 @@ abstract class ElectrumWalletBase syncStatus = NotConnectedSyncStatus(); } } + + void _syncStatusReaction(SyncStatus syncStatus) async { + if (syncStatus is! AttemptingSyncStatus && syncStatus is! SyncedTipSyncStatus) { + silentPaymentsScanningActive = syncStatus is SyncingSyncStatus; + } + + if (syncStatus is NotConnectedSyncStatus) { + // Needs to re-subscribe to all scripthashes when reconnected + _scripthashesUpdateSubject = {}; + + if (_isTryingToConnect) return; + + _isTryingToConnect = true; + + Future.delayed(Duration(seconds: 10), () { + if (this.syncStatus is! SyncedSyncStatus && this.syncStatus is! SyncedTipSyncStatus) { + this.electrumClient.connectToUri( + node!.uri, + useSSL: node!.useSSL ?? false, + ); + } + _isTryingToConnect = false; + }); + } + + // Message is shown on the UI for 3 seconds, revert to synced + if (syncStatus is SyncedTipSyncStatus) { + Timer(Duration(seconds: 3), () { + if (this.syncStatus is SyncedTipSyncStatus) this.syncStatus = SyncedSyncStatus(); + }); + } + } } class ScanNode { diff --git a/cw_bitcoin/pubspec.lock b/cw_bitcoin/pubspec.lock index ffc224e93..15f7cdb43 100644 --- a/cw_bitcoin/pubspec.lock +++ b/cw_bitcoin/pubspec.lock @@ -866,10 +866,10 @@ packages: dependency: transitive description: name: unorm_dart - sha256: "5b35bff83fce4d76467641438f9e867dc9bcfdb8c1694854f230579d68cd8f4b" + sha256: "23d8bf65605401a6a32cff99435fed66ef3dab3ddcad3454059165df46496a3b" url: "https://pub.dev" source: hosted - version: "0.2.0" + version: "0.3.0" vector_math: dependency: transitive description: diff --git a/cw_core/lib/node.dart b/cw_core/lib/node.dart index a82479c86..f35ea589f 100644 --- a/cw_core/lib/node.dart +++ b/cw_core/lib/node.dart @@ -227,6 +227,9 @@ class Node extends HiveObject with Keyable { } } + // TODO: this will return true most of the time, even if the node has useSSL set to true while + // it doesn't support SSL or vice versa, because it will connect normally, but it will fail if + // you try to communicate with it Future requestElectrumServer() async { try { if (useSSL == true) { diff --git a/cw_haven/pubspec.lock b/cw_haven/pubspec.lock index 8aeb70a97..b8583d219 100644 --- a/cw_haven/pubspec.lock +++ b/cw_haven/pubspec.lock @@ -655,6 +655,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.3.1" + unorm_dart: + dependency: transitive + description: + name: unorm_dart + sha256: "23d8bf65605401a6a32cff99435fed66ef3dab3ddcad3454059165df46496a3b" + url: "https://pub.dev" + source: hosted + version: "0.3.0" vector_math: dependency: transitive description: diff --git a/cw_monero/pubspec.lock b/cw_monero/pubspec.lock index f5dc3de3f..011fed169 100644 --- a/cw_monero/pubspec.lock +++ b/cw_monero/pubspec.lock @@ -543,10 +543,10 @@ packages: dependency: "direct main" description: name: polyseed - sha256: a340962242d7917b0f3e6bd02c4acc3f90eae8ff766f1244f793ae7a6414dd68 + sha256: edf28042e7b0b28f97a0469aa98e6e4015937cef6b9340cd6ad2822139c95217 url: "https://pub.dev" source: hosted - version: "0.0.4" + version: "0.0.5" pool: dependency: transitive description: @@ -696,6 +696,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.3.1" + unorm_dart: + dependency: transitive + description: + name: unorm_dart + sha256: "23d8bf65605401a6a32cff99435fed66ef3dab3ddcad3454059165df46496a3b" + url: "https://pub.dev" + source: hosted + version: "0.3.0" vector_math: dependency: transitive description: diff --git a/cw_nano/pubspec.lock b/cw_nano/pubspec.lock index 2c2a342ca..70f2f6f0b 100644 --- a/cw_nano/pubspec.lock +++ b/cw_nano/pubspec.lock @@ -805,6 +805,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.3.2" + unorm_dart: + dependency: transitive + description: + name: unorm_dart + sha256: "23d8bf65605401a6a32cff99435fed66ef3dab3ddcad3454059165df46496a3b" + url: "https://pub.dev" + source: hosted + version: "0.3.0" vector_math: dependency: transitive description: diff --git a/cw_wownero/pubspec.lock b/cw_wownero/pubspec.lock index f5dc3de3f..011fed169 100644 --- a/cw_wownero/pubspec.lock +++ b/cw_wownero/pubspec.lock @@ -543,10 +543,10 @@ packages: dependency: "direct main" description: name: polyseed - sha256: a340962242d7917b0f3e6bd02c4acc3f90eae8ff766f1244f793ae7a6414dd68 + sha256: edf28042e7b0b28f97a0469aa98e6e4015937cef6b9340cd6ad2822139c95217 url: "https://pub.dev" source: hosted - version: "0.0.4" + version: "0.0.5" pool: dependency: transitive description: @@ -696,6 +696,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.3.1" + unorm_dart: + dependency: transitive + description: + name: unorm_dart + sha256: "23d8bf65605401a6a32cff99435fed66ef3dab3ddcad3454059165df46496a3b" + url: "https://pub.dev" + source: hosted + version: "0.3.0" vector_math: dependency: transitive description: diff --git a/cw_wownero/pubspec.yaml b/cw_wownero/pubspec.yaml index 5e2a11461..4537955ab 100644 --- a/cw_wownero/pubspec.yaml +++ b/cw_wownero/pubspec.yaml @@ -19,7 +19,7 @@ dependencies: flutter_mobx: ^2.0.6+1 intl: ^0.18.0 encrypt: ^5.0.1 - polyseed: ^0.0.4 + polyseed: ^0.0.5 cw_core: path: ../cw_core monero: diff --git a/lib/bitcoin/cw_bitcoin.dart b/lib/bitcoin/cw_bitcoin.dart index c62030504..86d9c4985 100644 --- a/lib/bitcoin/cw_bitcoin.dart +++ b/lib/bitcoin/cw_bitcoin.dart @@ -438,7 +438,7 @@ class CWBitcoin extends Bitcoin { @override int getMaxCustomFeeRate(Object wallet) { final bitcoinWallet = wallet as ElectrumWallet; - return (bitcoinWallet.feeRate(BitcoinTransactionPriority.fast) * 1.1).round(); + return (bitcoinWallet.feeRate(BitcoinTransactionPriority.fast) * 10).round(); } @override diff --git a/lib/entities/default_settings_migration.dart b/lib/entities/default_settings_migration.dart index 144ca456d..3aad38179 100644 --- a/lib/entities/default_settings_migration.dart +++ b/lib/entities/default_settings_migration.dart @@ -242,6 +242,7 @@ Future defaultSettingsMigration( await fixBtcDerivationPaths(walletInfoSource); break; case 39: + _fixNodesUseSSLFlag(nodes); await changeDefaultNanoNode(nodes, sharedPreferences); break; default: @@ -258,6 +259,17 @@ Future defaultSettingsMigration( await sharedPreferences.setInt(PreferencesKey.currentDefaultSettingsMigrationVersion, version); } +void _fixNodesUseSSLFlag(Box nodes) { + for (Node node in nodes.values) { + switch (node.uriRaw) { + case cakeWalletLitecoinElectrumUri: + case cakeWalletBitcoinElectrumUri: + node.useSSL = true; + break; + } + } +} + Future updateNanoNodeList({required Box nodes}) async { final nodeList = await loadDefaultNanoNodes(); var listOfNewEndpoints = [ diff --git a/lib/src/screens/ionia/auth/ionia_create_account_page.dart b/lib/src/screens/ionia/auth/ionia_create_account_page.dart deleted file mode 100644 index 4762216c1..000000000 --- a/lib/src/screens/ionia/auth/ionia_create_account_page.dart +++ /dev/null @@ -1,163 +0,0 @@ -import 'package:cake_wallet/themes/extensions/cake_text_theme.dart'; -import 'package:cake_wallet/core/email_validator.dart'; -import 'package:cake_wallet/ionia/ionia_create_state.dart'; -import 'package:cake_wallet/routes.dart'; -import 'package:cake_wallet/src/screens/base_page.dart'; -import 'package:cake_wallet/src/widgets/alert_with_one_action.dart'; -import 'package:cake_wallet/src/widgets/base_text_form_field.dart'; -import 'package:cake_wallet/src/widgets/primary_button.dart'; -import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart'; -import 'package:cake_wallet/typography.dart'; -import 'package:cake_wallet/utils/show_pop_up.dart'; -import 'package:cake_wallet/view_model/ionia/ionia_auth_view_model.dart'; -import 'package:flutter/gestures.dart'; -import 'package:flutter/material.dart'; -import 'package:cake_wallet/generated/i18n.dart'; -import 'package:flutter_mobx/flutter_mobx.dart'; -import 'package:mobx/mobx.dart'; -import 'package:url_launcher/url_launcher.dart'; - -class IoniaCreateAccountPage extends BasePage { - IoniaCreateAccountPage(this._authViewModel) - : _emailFocus = FocusNode(), - _emailController = TextEditingController(), - _formKey = GlobalKey() { - _emailController.text = _authViewModel.email; - _emailController.addListener(() => _authViewModel.email = _emailController.text); - } - - final IoniaAuthViewModel _authViewModel; - - final GlobalKey _formKey; - - final FocusNode _emailFocus; - final TextEditingController _emailController; - - static const privacyPolicyUrl = 'https://ionia.docsend.com/view/jhjvdn7qq7k3ukwt'; - static const termsAndConditionsUrl = 'https://ionia.docsend.com/view/uceirymz2ijacq5g'; - - @override - Widget middle(BuildContext context) { - return Text( - S.current.sign_up, - style: textMediumSemiBold( - color: Theme.of(context).extension()!.titleColor, - ), - ); - } - - @override - Widget body(BuildContext context) { - reaction((_) => _authViewModel.createUserState, (IoniaCreateAccountState state) { - if (state is IoniaCreateStateFailure) { - _onCreateUserFailure(context, state.error); - } - if (state is IoniaCreateStateSuccess) { - _onCreateSuccessful(context, _authViewModel); - } - }); - - return ScrollableWithBottomSection( - contentPadding: EdgeInsets.all(24), - content: Form( - key: _formKey, - child: BaseTextFormField( - hintText: S.of(context).email_address, - focusNode: _emailFocus, - validator: EmailValidator(), - keyboardType: TextInputType.emailAddress, - controller: _emailController, - onSubmit: (_) => _createAccount(), - ), - ), - bottomSectionPadding: EdgeInsets.symmetric(vertical: 36, horizontal: 24), - bottomSection: Column( - children: [ - Column( - mainAxisAlignment: MainAxisAlignment.end, - children: [ - Observer( - builder: (_) => LoadingPrimaryButton( - text: S.of(context).create_account, - onPressed: _createAccount, - isLoading: - _authViewModel.createUserState is IoniaCreateStateLoading, - color: Theme.of(context).primaryColor, - textColor: Colors.white, - ), - ), - SizedBox( - height: 20, - ), - RichText( - textAlign: TextAlign.center, - text: TextSpan( - text: S.of(context).agree_to, - style: TextStyle( - color: Color(0xff7A93BA), - fontSize: 12, - fontFamily: 'Lato', - ), - children: [ - TextSpan( - text: S.of(context).settings_terms_and_conditions, - style: TextStyle( - color: Theme.of(context).primaryColor, - fontWeight: FontWeight.w700, - ), - recognizer: TapGestureRecognizer() - ..onTap = () async { - final uri = Uri.parse(termsAndConditionsUrl); - if (await canLaunchUrl(uri)) - await launchUrl(uri, mode: LaunchMode.externalApplication); - }, - ), - TextSpan(text: ' ${S.of(context).and} '), - TextSpan( - text: S.of(context).privacy_policy, - style: TextStyle( - color: Theme.of(context).primaryColor, - fontWeight: FontWeight.w700, - ), - recognizer: TapGestureRecognizer() - ..onTap = () async { - final uri = Uri.parse(privacyPolicyUrl); - if (await canLaunchUrl(uri)) - await launchUrl(uri, mode: LaunchMode.externalApplication); - }), - TextSpan(text: ' ${S.of(context).by_cake_pay}'), - ], - ), - ), - ], - ), - ], - ), - ); - } - - void _onCreateUserFailure(BuildContext context, String error) { - showPopUp( - context: context, - builder: (BuildContext context) { - return AlertWithOneAction( - alertTitle: S.current.create_account, - alertContent: error, - buttonText: S.of(context).ok, - buttonAction: () => Navigator.of(context).pop()); - }); - } - - void _onCreateSuccessful(BuildContext context, IoniaAuthViewModel authViewModel) => Navigator.pushNamed( - context, - Routes.ioniaVerifyIoniaOtpPage, - arguments: [authViewModel.email, false], - ); - - void _createAccount() async { - if (_formKey.currentState != null && !_formKey.currentState!.validate()) { - return; - } - await _authViewModel.createUser(_emailController.text); - } -} diff --git a/lib/view_model/dashboard/dashboard_view_model.dart b/lib/view_model/dashboard/dashboard_view_model.dart index 8f22e5be1..b4c5240e1 100644 --- a/lib/view_model/dashboard/dashboard_view_model.dart +++ b/lib/view_model/dashboard/dashboard_view_model.dart @@ -48,6 +48,7 @@ import 'package:mobx/mobx.dart'; import 'package:cake_wallet/bitcoin/bitcoin.dart'; import 'package:http/http.dart' as http; import 'package:shared_preferences/shared_preferences.dart'; +import 'package:cake_wallet/.secrets.g.dart' as secrets; part 'dashboard_view_model.g.dart'; @@ -375,7 +376,8 @@ abstract class DashboardViewModelBase with Store { .toList(); } - bool get hasSellProviders => ProvidersHelper.getAvailableSellProviderTypes(wallet.type).isNotEmpty; + bool get hasSellProviders => + ProvidersHelper.getAvailableSellProviderTypes(wallet.type).isNotEmpty; bool get shouldShowYatPopup => settingsStore.shouldShowYatPopup; @@ -534,8 +536,11 @@ abstract class DashboardViewModelBase with Store { void setSyncAll(bool value) => settingsStore.currentSyncAll = value; Future> checkForHavenWallets() async { - final walletInfoSource = await CakeHive.openBox(WalletInfo.boxName); - return walletInfoSource.values.where((element) => element.type == WalletType.haven).map((e) => e.name).toList(); + final walletInfoSource = await CakeHive.openBox(WalletInfo.boxName); + return walletInfoSource.values + .where((element) => element.type == WalletType.haven) + .map((e) => e.name) + .toList(); } Future> checkAffectedWallets() async { @@ -576,29 +581,34 @@ abstract class DashboardViewModelBase with Store { Future getServicesStatus() async { try { if (isEnabledBulletinAction) { - final res = await http.get(Uri.parse("https://service-api.cakewallet.com/v1/active-notices")); + final uri = Uri.https( + "service-api.cakewallet.com", + "/v1/active-notices", + {'key': secrets.fiatApiKey}, + ); - if (res.statusCode < 200 || res.statusCode >= 300) { - throw res.body; - } + final res = await http.get(uri); - final oldSha = sharedPreferences.getString(PreferencesKey.serviceStatusShaKey); - - final hash = await Cryptography.instance.sha256().hash(utf8.encode(res.body)); - final currentSha = bytesToHex(hash.bytes); - - final hasUpdates = oldSha != currentSha; - - return ServicesResponse.fromJson( - json.decode(res.body) as Map, - hasUpdates, - currentSha, - ); - } - else { - return ServicesResponse([], false, ''); + if (res.statusCode < 200 || res.statusCode >= 300) { + throw res.body; } - } catch (_) { + + final oldSha = sharedPreferences.getString(PreferencesKey.serviceStatusShaKey); + + final hash = await Cryptography.instance.sha256().hash(utf8.encode(res.body)); + final currentSha = bytesToHex(hash.bytes); + + final hasUpdates = oldSha != currentSha; + + return ServicesResponse.fromJson( + json.decode(res.body) as Map, + hasUpdates, + currentSha, + ); + } else { + return ServicesResponse([], false, ''); + } + } catch (e) { return ServicesResponse([], false, ''); } } diff --git a/lib/view_model/exchange/exchange_view_model.dart b/lib/view_model/exchange/exchange_view_model.dart index b4711068c..404fa0f25 100644 --- a/lib/view_model/exchange/exchange_view_model.dart +++ b/lib/view_model/exchange/exchange_view_model.dart @@ -508,7 +508,7 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with if (limitsState is LimitsLoadedSuccessfully) { if (double.tryParse(amount) == null) continue; - if (limits.max != null && double.parse(amount) < limits.min!) + if (limits.min != null && double.parse(amount) < limits.min!) continue; else if (limits.max != null && double.parse(amount) > limits.max!) continue; diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index 7ef453eb1..873d50649 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -10,6 +10,7 @@ import device_info_plus import devicelocale import flutter_inappwebview_macos import flutter_local_authentication +import flutter_secure_storage_macos import in_app_review import package_info import package_info_plus @@ -25,6 +26,7 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { DevicelocalePlugin.register(with: registry.registrar(forPlugin: "DevicelocalePlugin")) InAppWebViewFlutterPlugin.register(with: registry.registrar(forPlugin: "InAppWebViewFlutterPlugin")) FlutterLocalAuthenticationPlugin.register(with: registry.registrar(forPlugin: "FlutterLocalAuthenticationPlugin")) + FlutterSecureStoragePlugin.register(with: registry.registrar(forPlugin: "FlutterSecureStoragePlugin")) InAppReviewPlugin.register(with: registry.registrar(forPlugin: "InAppReviewPlugin")) FLTPackageInfoPlugin.register(with: registry.registrar(forPlugin: "FLTPackageInfoPlugin")) FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin")) diff --git a/pubspec_base.yaml b/pubspec_base.yaml index 0ce331d57..9cebba658 100644 --- a/pubspec_base.yaml +++ b/pubspec_base.yaml @@ -106,6 +106,7 @@ dependencies: url: https://github.com/cake-tech/bitcoin_base ref: cake-update-v3 ledger_flutter: ^1.0.1 + hashlib: 1.12.0 dev_dependencies: flutter_test: diff --git a/tool/generate_secrets_config.dart b/tool/generate_secrets_config.dart index 735e8e359..8e9762b7a 100644 --- a/tool/generate_secrets_config.dart +++ b/tool/generate_secrets_config.dart @@ -4,7 +4,6 @@ import 'utils/secret_key.dart'; import 'utils/utils.dart'; const baseConfigPath = 'tool/.secrets-config.json'; -const coreConfigPath = 'tool/.core-secrets-config.json'; const evmChainsConfigPath = 'tool/.evm-secrets-config.json'; const solanaConfigPath = 'tool/.solana-secrets-config.json'; const nanoConfigPath = 'tool/.nano-secrets-config.json'; @@ -38,7 +37,6 @@ Future generateSecretsConfig(List args) async { }); final baseConfigFile = File(baseConfigPath); - final coreConfigFile = File(coreConfigPath); final evmChainsConfigFile = File(evmChainsConfigPath); final solanaConfigFile = File(solanaConfigPath); final nanoConfigFile = File(nanoConfigPath); @@ -64,7 +62,6 @@ Future generateSecretsConfig(List args) async { await writeConfig(baseConfigFile, SecretKey.base, existingSecrets: secrets); - await writeConfig(coreConfigFile, SecretKey.coreSecrets); await writeConfig(evmChainsConfigFile, SecretKey.evmChainsSecrets); await writeConfig(solanaConfigFile, SecretKey.solanaSecrets); await writeConfig(nanoConfigFile, SecretKey.nanoSecrets); diff --git a/tool/utils/secret_key.dart b/tool/utils/secret_key.dart index ed22d152a..a1d93fcf9 100644 --- a/tool/utils/secret_key.dart +++ b/tool/utils/secret_key.dart @@ -45,8 +45,6 @@ class SecretKey { SecretKey('authorization', () => ''), ]; - static final coreSecrets = []; - static final evmChainsSecrets = [ SecretKey('etherScanApiKey', () => ''), SecretKey('polygonScanApiKey', () => ''),