diff --git a/.github/workflows/pr_test_build.yml b/.github/workflows/pr_test_build.yml index 80d5d74aa..66272c6af 100644 --- a/.github/workflows/pr_test_build.yml +++ b/.github/workflows/pr_test_build.yml @@ -115,6 +115,7 @@ jobs: echo "const moonPayApiKey = '${{ secrets.MOON_PAY_API_KEY }}';" >> lib/.secrets.g.dart echo "const moonPaySecretKey = '${{ secrets.MOON_PAY_SECRET_KEY }}';" >> lib/.secrets.g.dart echo "const sideShiftAffiliateId = '${{ secrets.SIDE_SHIFT_AFFILIATE_ID }}';" >> lib/.secrets.g.dart + echo "const sideShiftApiKey = '${{ secrets.SIDE_SHIFT_API_KEY }}';" >> lib/.secrets.g.dart echo "const simpleSwapApiKey = '${{ secrets.SIMPLE_SWAP_API_KEY }}';" >> lib/.secrets.g.dart echo "const simpleSwapApiKeyDesktop = '${{ secrets.SIMPLE_SWAP_API_KEY_DESKTOP }}';" >> lib/.secrets.g.dart echo "const onramperApiKey = '${{ secrets.ONRAMPER_API_KEY }}';" >> lib/.secrets.g.dart diff --git a/.gitignore b/.gitignore index 09583004b..0f7098cd4 100644 --- a/.gitignore +++ b/.gitignore @@ -144,3 +144,5 @@ assets/images/app_logo.png macos/Runner/Info.plist macos/Runner/DebugProfile.entitlements macos/Runner/Release.entitlements + +lib/core/secure_storage.dart diff --git a/.metadata b/.metadata index cdddb9350..d25ddea79 100644 --- a/.metadata +++ b/.metadata @@ -18,6 +18,9 @@ migration: - platform: macos create_revision: e3c29ec00c9c825c891d75054c63fcc46454dca1 base_revision: e3c29ec00c9c825c891d75054c63fcc46454dca1 + - platform: linux + create_revision: e3c29ec00c9c825c891d75054c63fcc46454dca1 + base_revision: e3c29ec00c9c825c891d75054c63fcc46454dca1 # User provided section diff --git a/assets/images/flags/nga.png b/assets/images/flags/yor.png similarity index 100% rename from assets/images/flags/nga.png rename to assets/images/flags/yor.png diff --git a/assets/images/live_support.png b/assets/images/live_support.png deleted file mode 100644 index 89ad61f45..000000000 Binary files a/assets/images/live_support.png and /dev/null differ diff --git a/assets/images/more_links.png b/assets/images/more_links.png deleted file mode 100644 index 97891f3ad..000000000 Binary files a/assets/images/more_links.png and /dev/null differ diff --git a/assets/images/wallet_guides.png b/assets/images/wallet_guides.png deleted file mode 100644 index 3f2d9f270..000000000 Binary files a/assets/images/wallet_guides.png and /dev/null differ diff --git a/build-guide-linux.md b/build-guide-linux.md new file mode 100644 index 000000000..c08d0405b --- /dev/null +++ b/build-guide-linux.md @@ -0,0 +1,195 @@ +# Building CakeWallet for Linux + +## Requirements and Setup + +The following are the system requirements to build CakeWallet for your Linux device. + +``` +Ubuntu >= 16.04 +Flutter 3 or above +``` + +## Building CakeWallet on Linux + +These steps will help you configure and execute a build of CakeWallet from its source code. + +### 1. Installing Package Dependencies + +CakeWallet requires some packages to be install on your build system. You may easily install them on your build system with the following command: + +`$ sudo apt install build-essential cmake pkg-config git curl autoconf libtool` + +> ### Check gcc version +> Need to use gcc 10 or 9 for successfully link dependecnies with flutter.\ +> Check what gcc version is using:\ +> ``` +> $ gcc --version +> $ g++ --version +> ``` +> If you are using gcc version newer than 10, then need to downgrade to version 10.4.0.\ +> ``` +> $ sudo apt install gcc-10 g++-10 +> $ sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-10 10 +> $ sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-10 10 +> ``` + +### 2. Installing Flutter + +Need to install flutter. For this please check section [How to install flutter on Linux](https://docs.flutter.dev/get-started/install/linux). + + + + +### 3. Verify Installations + +Verify that the Flutter have been correctly installed on your system with the following command: + +`$ flutter doctor` + +The output of this command will appear like this, indicating successful installations. If there are problems with your installation, they **must** be corrected before proceeding. +``` +Doctor summary (to see all details, run flutter doctor -v): +[✓] Flutter (Channel stable, 3.7.x, on Linux, locale en_US.UTF-8) +``` + +### 4. Acquiring the CakeWallet Source Code + +Download CakeWallet source code + +`$ git clone https://github.com/cake-tech/cake_wallet.git --branch linux/password-direct-input` + +Proceed into the source code before proceeding with the next steps: + +`$ cd cake_wallet/scripts/linux/` + +To configure some project properties run: + +`$ ./cakewallet.sh` + +Build the Monero libraries and their dependencies: + +`$ ./build_all.sh` + +Now the dependencies need to be copied into the CakeWallet project with this command: + +`$ ./setup.sh` + +It is now time to change back to the base directory of the CakeWallet source code: + +`$ cd ../../` + +Install Flutter package dependencies with this command: + +`$ flutter pub get` + + + +> #### If you will get an error like: +> ``` +> The plugin `cw_shared_external` requires your app to be migrated to the Android embedding v2. Follow the steps on the migration doc above and re-run +> this command. +> ``` +> Then need to config Android project settings. For this open `scripts/android` (`$ cd scripts/android`) directory and run followed commands: +> ``` +> $ source ./app_env.sh cakewallet +> $ ./app_config.sh +> $ cd ../.. +> ``` +> Then re-configure Linux project again. For this open `scripts/linux` (`$cd scripts/linux`) directory and run: +> `$ ./cakewallet.sh` +> and back to project root directory: +> `$ cd ../..` +> and fetch dependecies again +> `$ flutter pub get` + + +> ### If you get the error like: +> ``` +> The lower bound of "sdk: '>=2.0.0-dev.68.0 <3.0.0'" must be 2.12.0 or higher to enable null safety. +> ``` +> +> #### Downgrade Flutter to version 3.7.x +> Make sure that Flutter is reverted back to version 3.7.x (which would automatically revert Dart to 2.18 or 2.19) +> +> In your Linux terminal, find where your Flutter SDK is installed with: +> +> ``` +> $ which flutter +> ``` +> +> Proceed to the Flutter SDK path: +> +> ``` +> $ cd user/snap/flutter/common/flutter +> ``` +> +> In the Flutter SDK directory, revert to a 3.7.x version (I used 3.7.12): +> +> +> ``` +> $ git checkout 3.7.12 +> ``` +> Then re-configure Cake Wallet's Linux project again. For this open `scripts/linux` (`$cd scripts/linux`) directory and run: +> `$ ./cakewallet.sh` +> and back to project root directory: +> `$ cd ../..` +> and fetch dependecies again +> `$ flutter pub get` + +Your CakeWallet binary will be built with some specific keys for iterate with 3rd party services. You may generate these secret keys placeholders with the following command: + +`$ flutter packages pub run tool/generate_new_secrets.dart` + +We will generate mobx models for the project. + +`$ ./model_generator.sh` + +Then we need to generate localization files. + +`$ flutter packages pub run tool/generate_localization.dart` + +### 5. Build! + +`$ flutter build linux --release` + +Path to executable file will be: + +`build/linux/x64/release/bundle/cake_wallet` + +> ### Troubleshooting +> +> If you got an error while building the application with `$ flutter build linux --release` command, add `-v` argument to the command (`$ flutter build linux -v --release`) to get details.\ +> If you got in flutter build logs: undefined reference to `hid_free_enumeration`, or another error with undefined reference to `hid_*`, then rebuild monero lib without hidapi lib. Check does exists `libhidapi-dev` in your scope and remove it from your scope for build without it. + +# Flatpak + +For package the built application into flatpak you need fistly to install `flatpak` and `flatpak-builder`: + +`$ sudo apt install flatpak flatpak-builder` + +Then need to [add flathub](https://flatpak.org/setup/Ubuntu) (or just `$ flatpak remote-add --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo`). Then need to install freedesktop runtime and sdk: + +`$ flatpak install flathub org.freedesktop.Platform//22.08 org.freedesktop.Sdk//22.08` + +To build with using of `flatpak-build` directory run next: + +`$ flatpak-builder --force-clean flatpak-build com.cakewallet.CakeWallet.yml` + +And then export bundle: + +`$ flatpak build-export export flatpak-build` + +`$ flatpak build-bundle export cake_wallet.flatpak com.cakewallet.CakeWallet` + + +Result file: `cake_wallet.flatpak` should be generated in current directory. + +For install generated flatpak file use: + +`$ flatpak --user install cake_wallet.flatpak` + +For run the installed application run: + +`$ flatpak run com.cakewallet.CakeWallet` + +Copyright (c) 2023 Cake Technologies LLC. \ No newline at end of file diff --git a/com.cakewallet.CakeWallet.yml b/com.cakewallet.CakeWallet.yml new file mode 100644 index 000000000..83efa1388 --- /dev/null +++ b/com.cakewallet.CakeWallet.yml @@ -0,0 +1,35 @@ +app-id: com.cakewallet.CakeWallet +runtime: org.freedesktop.Platform +runtime-version: '22.08' +sdk: org.freedesktop.Sdk +command: cake_wallet +separate-locales: false +finish-args: + - --share=ipc + - --socket=fallback-x11 + - --socket=wayland + - --device=dri + - --socket=pulseaudio + - --share=network + - --filesystem=home +modules: + - name: cake_wallet + buildsystem: simple + only-arches: + - x86_64 + build-commands: + - "cp -R bundle /app/cake_wallet" + - "chmod +x /app/cake_wallet/cake_wallet" + - "mkdir -p /app/bin" + - "ln -s /app/cake_wallet/cake_wallet /app/bin/cake_wallet" + - "mkdir -p /app/share/icons/hicolor/scalable/apps" + - "cp cakewallet_icon_180.png /app/share/icons/hicolor/scalable/apps/com.cakewallet.CakeWallet.png" + - "mkdir -p /app/share/applications" + - "cp com.cakewallet.CakeWallet.desktop /app/share/applications" + sources: + - type: dir + path: build/linux/x64/release + - type: file + path: assets/images/cakewallet_icon_180.png + - type: file + path: linux/com.cakewallet.CakeWallet.desktop diff --git a/cw_bitcoin/lib/bitcoin_wallet.dart b/cw_bitcoin/lib/bitcoin_wallet.dart index c4675df1c..3e4601eb3 100644 --- a/cw_bitcoin/lib/bitcoin_wallet.dart +++ b/cw_bitcoin/lib/bitcoin_wallet.dart @@ -1,4 +1,5 @@ import 'package:cw_bitcoin/bitcoin_mnemonic.dart'; +import 'package:cw_bitcoin/encryption_file_utils.dart'; import 'package:cw_core/crypto_currency.dart'; import 'package:cw_core/unspent_coins_info.dart'; import 'package:hive/hive.dart'; @@ -23,6 +24,7 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store { required WalletInfo walletInfo, required Box<UnspentCoinsInfo> unspentCoinsInfo, required Uint8List seedBytes, + required EncryptionFileUtils encryptionFileUtils, List<BitcoinAddressRecord>? initialAddresses, ElectrumBalance? initialBalance, int initialRegularAddressIndex = 0, @@ -36,7 +38,8 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store { initialAddresses: initialAddresses, initialBalance: initialBalance, seedBytes: seedBytes, - currency: CryptoCurrency.btc) { + currency: CryptoCurrency.btc, + encryptionFileUtils: encryptionFileUtils) { walletAddresses = BitcoinWalletAddresses( walletInfo, electrumClient: electrumClient, @@ -54,6 +57,7 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store { required String password, required WalletInfo walletInfo, required Box<UnspentCoinsInfo> unspentCoinsInfo, + required EncryptionFileUtils encryptionFileUtils, List<BitcoinAddressRecord>? initialAddresses, ElectrumBalance? initialBalance, int initialRegularAddressIndex = 0, @@ -66,6 +70,7 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store { unspentCoinsInfo: unspentCoinsInfo, initialAddresses: initialAddresses, initialBalance: initialBalance, + encryptionFileUtils: encryptionFileUtils, seedBytes: await mnemonicToSeedBytes(mnemonic), initialRegularAddressIndex: initialRegularAddressIndex, initialChangeAddressIndex: initialChangeAddressIndex); @@ -76,8 +81,9 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store { required WalletInfo walletInfo, required Box<UnspentCoinsInfo> unspentCoinsInfo, required String password, + required EncryptionFileUtils encryptionFileUtils }) async { - final snp = await ElectrumWallletSnapshot.load(name, walletInfo.type, password); + final snp = await ElectrumWallletSnapshot.load(encryptionFileUtils, name, walletInfo.type, password); return BitcoinWallet( mnemonic: snp.mnemonic, password: password, @@ -86,6 +92,7 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store { initialAddresses: snp.addresses, initialBalance: snp.balance, seedBytes: await mnemonicToSeedBytes(snp.mnemonic), + encryptionFileUtils: encryptionFileUtils, initialRegularAddressIndex: snp.regularAddressIndex, initialChangeAddressIndex: snp.changeAddressIndex); } diff --git a/cw_bitcoin/lib/bitcoin_wallet_creation_credentials.dart b/cw_bitcoin/lib/bitcoin_wallet_creation_credentials.dart index 82173b2d2..4c4c7ae23 100644 --- a/cw_bitcoin/lib/bitcoin_wallet_creation_credentials.dart +++ b/cw_bitcoin/lib/bitcoin_wallet_creation_credentials.dart @@ -2,8 +2,8 @@ import 'package:cw_core/wallet_credentials.dart'; import 'package:cw_core/wallet_info.dart'; class BitcoinNewWalletCredentials extends WalletCredentials { - BitcoinNewWalletCredentials({required String name, WalletInfo? walletInfo}) - : super(name: name, walletInfo: walletInfo); + BitcoinNewWalletCredentials({required String name, WalletInfo? walletInfo, String? password}) + : super(name: name, walletInfo: walletInfo, password: password); } class BitcoinRestoreWalletFromSeedCredentials extends WalletCredentials { diff --git a/cw_bitcoin/lib/bitcoin_wallet_service.dart b/cw_bitcoin/lib/bitcoin_wallet_service.dart index bfadaf2a3..3cf72b308 100644 --- a/cw_bitcoin/lib/bitcoin_wallet_service.dart +++ b/cw_bitcoin/lib/bitcoin_wallet_service.dart @@ -2,6 +2,7 @@ import 'dart:io'; import 'package:cw_bitcoin/bitcoin_mnemonic.dart'; import 'package:cw_bitcoin/bitcoin_mnemonic_is_incorrect_exception.dart'; import 'package:cw_bitcoin/bitcoin_wallet_creation_credentials.dart'; +import 'package:cw_bitcoin/encryption_file_utils.dart'; import 'package:cw_core/unspent_coins_info.dart'; import 'package:cw_core/wallet_base.dart'; import 'package:cw_core/wallet_service.dart'; @@ -12,14 +13,13 @@ import 'package:cw_core/wallet_type.dart'; import 'package:hive/hive.dart'; import 'package:collection/collection.dart'; -class BitcoinWalletService extends WalletService< - BitcoinNewWalletCredentials, - BitcoinRestoreWalletFromSeedCredentials, - BitcoinRestoreWalletFromWIFCredentials> { - BitcoinWalletService(this.walletInfoSource, this.unspentCoinsInfoSource); +class BitcoinWalletService extends WalletService<BitcoinNewWalletCredentials, + BitcoinRestoreWalletFromSeedCredentials, BitcoinRestoreWalletFromWIFCredentials> { + BitcoinWalletService(this.walletInfoSource, this.unspentCoinsInfoSource, this.isDirect); final Box<WalletInfo> walletInfoSource; final Box<UnspentCoinsInfo> unspentCoinsInfoSource; + final bool isDirect; @override WalletType getType() => WalletType.bitcoin; @@ -30,7 +30,8 @@ class BitcoinWalletService extends WalletService< mnemonic: await generateMnemonic(), password: credentials.password!, walletInfo: credentials.walletInfo!, - unspentCoinsInfo: unspentCoinsInfoSource); + unspentCoinsInfo: unspentCoinsInfoSource, + encryptionFileUtils: encryptionFileUtilsFor(isDirect)); await wallet.save(); await wallet.init(); return wallet; @@ -42,33 +43,37 @@ class BitcoinWalletService extends WalletService< @override Future<BitcoinWallet> openWallet(String name, String password) async { - final walletInfo = walletInfoSource.values.firstWhereOrNull( - (info) => info.id == WalletBase.idFor(name, getType()))!; + final walletInfo = walletInfoSource.values + .firstWhereOrNull((info) => info.id == WalletBase.idFor(name, getType()))!; final wallet = await BitcoinWalletBase.open( - password: password, name: name, walletInfo: walletInfo, - unspentCoinsInfo: unspentCoinsInfoSource); + password: password, + name: name, + walletInfo: walletInfo, + unspentCoinsInfo: unspentCoinsInfoSource, + encryptionFileUtils: encryptionFileUtilsFor(isDirect)); await wallet.init(); return wallet; } @override Future<void> remove(String wallet) async { - File(await pathForWalletDir(name: wallet, type: getType())) - .delete(recursive: true); - final walletInfo = walletInfoSource.values.firstWhereOrNull( - (info) => info.id == WalletBase.idFor(wallet, getType()))!; + File(await pathForWalletDir(name: wallet, type: getType())).delete(recursive: true); + final walletInfo = walletInfoSource.values + .firstWhereOrNull((info) => info.id == WalletBase.idFor(wallet, getType()))!; await walletInfoSource.delete(walletInfo.key); } @override Future<void> rename(String currentName, String password, String newName) async { - final currentWalletInfo = walletInfoSource.values.firstWhereOrNull( - (info) => info.id == WalletBase.idFor(currentName, getType()))!; + final currentWalletInfo = walletInfoSource.values + .firstWhereOrNull((info) => info.id == WalletBase.idFor(currentName, getType()))!; final currentWallet = await BitcoinWalletBase.open( - password: password, - name: currentName, - walletInfo: currentWalletInfo, - unspentCoinsInfo: unspentCoinsInfoSource); + password: password, + name: currentName, + walletInfo: currentWalletInfo, + unspentCoinsInfo: unspentCoinsInfoSource, + encryptionFileUtils: encryptionFileUtilsFor(isDirect), + ); await currentWallet.renameWalletFiles(newName); @@ -80,13 +85,11 @@ class BitcoinWalletService extends WalletService< } @override - Future<BitcoinWallet> restoreFromKeys( - BitcoinRestoreWalletFromWIFCredentials credentials) async => + Future<BitcoinWallet> restoreFromKeys(BitcoinRestoreWalletFromWIFCredentials credentials) async => throw UnimplementedError(); @override - Future<BitcoinWallet> restoreFromSeed( - BitcoinRestoreWalletFromSeedCredentials credentials) async { + Future<BitcoinWallet> restoreFromSeed(BitcoinRestoreWalletFromSeedCredentials credentials) async { if (!validateMnemonic(credentials.mnemonic)) { throw BitcoinMnemonicIsIncorrectException(); } @@ -95,7 +98,8 @@ class BitcoinWalletService extends WalletService< password: credentials.password!, mnemonic: credentials.mnemonic, walletInfo: credentials.walletInfo!, - unspentCoinsInfo: unspentCoinsInfoSource); + unspentCoinsInfo: unspentCoinsInfoSource, + encryptionFileUtils: encryptionFileUtilsFor(isDirect)); await wallet.save(); await wallet.init(); return wallet; diff --git a/cw_bitcoin/lib/electrum_transaction_history.dart b/cw_bitcoin/lib/electrum_transaction_history.dart index be039fa36..29001e160 100644 --- a/cw_bitcoin/lib/electrum_transaction_history.dart +++ b/cw_bitcoin/lib/electrum_transaction_history.dart @@ -1,9 +1,9 @@ import 'dart:convert'; +import 'package:cw_bitcoin/encryption_file_utils.dart'; import 'package:cw_core/pathForWallet.dart'; import 'package:cw_core/wallet_info.dart'; import 'package:mobx/mobx.dart'; import 'package:cw_core/transaction_history.dart'; -import 'package:cw_bitcoin/file.dart'; import 'package:cw_bitcoin/electrum_transaction_info.dart'; part 'electrum_transaction_history.g.dart'; @@ -16,13 +16,14 @@ class ElectrumTransactionHistory = ElectrumTransactionHistoryBase abstract class ElectrumTransactionHistoryBase extends TransactionHistoryBase<ElectrumTransactionInfo> with Store { ElectrumTransactionHistoryBase( - {required this.walletInfo, required String password}) + {required this.walletInfo, required String password, required this.encryptionFileUtils}) : _password = password, _height = 0 { transactions = ObservableMap<String, ElectrumTransactionInfo>(); } final WalletInfo walletInfo; + final EncryptionFileUtils encryptionFileUtils; String _password; int _height; @@ -44,7 +45,7 @@ abstract class ElectrumTransactionHistoryBase final path = '$dirPath/$transactionsHistoryFileName'; final data = json.encode({'height': _height, 'transactions': transactions}); - await writeData(path: path, password: _password, data: data); + await encryptionFileUtils.write(path: path, password: _password, data: data); } catch (e) { print('Error while save bitcoin transaction history: ${e.toString()}'); } @@ -59,7 +60,7 @@ abstract class ElectrumTransactionHistoryBase final dirPath = await pathForWalletDir(name: walletInfo.name, type: walletInfo.type); final path = '$dirPath/$transactionsHistoryFileName'; - final content = await read(path: path, password: _password); + final content = await encryptionFileUtils.read(path: path, password: _password); return json.decode(content) as Map<String, dynamic>; } @@ -85,5 +86,4 @@ abstract class ElectrumTransactionHistoryBase void _update(ElectrumTransactionInfo transaction) => transactions[transaction.id] = transaction; - } diff --git a/cw_bitcoin/lib/electrum_wallet.dart b/cw_bitcoin/lib/electrum_wallet.dart index f9437e668..27ea925ed 100644 --- a/cw_bitcoin/lib/electrum_wallet.dart +++ b/cw_bitcoin/lib/electrum_wallet.dart @@ -2,6 +2,8 @@ import 'dart:async'; import 'dart:convert'; import 'dart:io'; import 'dart:math'; +import 'dart:typed_data'; +import 'package:cw_bitcoin/encryption_file_utils.dart'; import 'package:cw_core/unspent_coins_info.dart'; import 'package:hive/hive.dart'; import 'package:cw_bitcoin/electrum_wallet_addresses.dart'; @@ -14,6 +16,7 @@ import 'package:cw_core/pathForWallet.dart'; import 'package:cw_bitcoin/address_to_output_script.dart'; import 'package:cw_bitcoin/bitcoin_address_record.dart'; import 'package:cw_bitcoin/electrum_balance.dart'; +import 'package:cw_bitcoin/bitcoin_mnemonic.dart'; import 'package:cw_bitcoin/bitcoin_transaction_credentials.dart'; import 'package:cw_bitcoin/electrum_transaction_history.dart'; import 'package:cw_bitcoin/bitcoin_transaction_no_inputs_exception.dart'; @@ -21,7 +24,6 @@ import 'package:cw_bitcoin/bitcoin_transaction_priority.dart'; import 'package:cw_bitcoin/bitcoin_transaction_wrong_balance_exception.dart'; import 'package:cw_bitcoin/bitcoin_unspent.dart'; import 'package:cw_bitcoin/bitcoin_wallet_keys.dart'; -import 'package:cw_bitcoin/file.dart'; import 'package:cw_bitcoin/pending_bitcoin_transaction.dart'; import 'package:cw_bitcoin/script_hash.dart'; import 'package:cw_bitcoin/utils.dart'; @@ -48,6 +50,7 @@ abstract class ElectrumWalletBase extends WalletBase<ElectrumBalance, required this.networkType, required this.mnemonic, required Uint8List seedBytes, + required this.encryptionFileUtils, List<BitcoinAddressRecord>? initialAddresses, ElectrumClient? electrumClient, ElectrumBalance? initialBalance, @@ -70,7 +73,10 @@ abstract class ElectrumWalletBase extends WalletBase<ElectrumBalance, this.electrumClient = electrumClient ?? ElectrumClient(); this.walletInfo = walletInfo; transactionHistory = - ElectrumTransactionHistory(walletInfo: walletInfo, password: password); + ElectrumTransactionHistory( + walletInfo: walletInfo, + password: password, + encryptionFileUtils: encryptionFileUtils); } static int estimatedTransactionSize(int inputsCount, int outputsCounts) => @@ -78,6 +84,7 @@ abstract class ElectrumWalletBase extends WalletBase<ElectrumBalance, final bitcoin.HDWallet hd; final String mnemonic; + final EncryptionFileUtils encryptionFileUtils; late ElectrumClient electrumClient; Box<UnspentCoinsInfo> unspentCoinsInfo; @@ -107,6 +114,9 @@ abstract class ElectrumWalletBase extends WalletBase<ElectrumBalance, @override String get seed => mnemonic; + @override + String get password => _password; + bitcoin.NetworkType networkType; @override @@ -119,8 +129,6 @@ abstract class ElectrumWalletBase extends WalletBase<ElectrumBalance, Map<String, BehaviorSubject<Object>?> _scripthashesUpdateSubject; bool _isTransactionUpdating; - void Function(FlutterErrorDetails)? _onError; - Future<void> init() async { await walletAddresses.init(); await transactionHistory.init(); @@ -323,7 +331,7 @@ abstract class ElectrumWalletBase extends WalletBase<ElectrumBalance, } else { feeAmount = feeRate(transactionCredentials.priority!) * estimatedSize; } - + final changeValue = totalInputAmount - amount - feeAmount; if (changeValue > minAmount) { @@ -427,7 +435,7 @@ abstract class ElectrumWalletBase extends WalletBase<ElectrumBalance, @override Future<void> save() async { final path = await makePath(); - await write(path: path, password: _password, data: toJSON()); + await encryptionFileUtils.write(path: path, password: _password, data: toJSON()); await transactionHistory.save(); } @@ -665,13 +673,8 @@ abstract class ElectrumWalletBase extends WalletBase<ElectrumBalance, await updateUnspent(); await updateBalance(); await updateTransactions(); - } catch (e, s) { + } catch (e) { print(e.toString()); - _onError?.call(FlutterErrorDetails( - exception: e, - stack: s, - library: this.runtimeType.toString(), - )); } }); }); @@ -737,7 +740,4 @@ abstract class ElectrumWalletBase extends WalletBase<ElectrumBalance, return addresses[random.nextInt(addresses.length)].address; } - - @override - void setExceptionHandler(void Function(FlutterErrorDetails) onError) => _onError = onError; } diff --git a/cw_bitcoin/lib/electrum_wallet_snapshot.dart b/cw_bitcoin/lib/electrum_wallet_snapshot.dart index 6db0c23f2..61a1e898d 100644 --- a/cw_bitcoin/lib/electrum_wallet_snapshot.dart +++ b/cw_bitcoin/lib/electrum_wallet_snapshot.dart @@ -1,7 +1,7 @@ import 'dart:convert'; import 'package:cw_bitcoin/bitcoin_address_record.dart'; import 'package:cw_bitcoin/electrum_balance.dart'; -import 'package:cw_bitcoin/file.dart'; +import 'package:cw_bitcoin/encryption_file_utils.dart'; import 'package:cw_core/pathForWallet.dart'; import 'package:cw_core/wallet_type.dart'; @@ -26,9 +26,9 @@ class ElectrumWallletSnapshot { int regularAddressIndex; int changeAddressIndex; - static Future<ElectrumWallletSnapshot> load(String name, WalletType type, String password) async { + static Future<ElectrumWallletSnapshot> load(EncryptionFileUtils encryptionFileUtils, String name, WalletType type, String password) async { final path = await pathForWallet(name: name, type: type); - final jsonSource = await read(path: path, password: password); + final jsonSource = await encryptionFileUtils.read(path: path, password: password); final data = json.decode(jsonSource) as Map; final addressesTmp = data['addresses'] as List? ?? <Object>[]; final mnemonic = data['mnemonic'] as String; diff --git a/cw_bitcoin/lib/encryption_file_utils.dart b/cw_bitcoin/lib/encryption_file_utils.dart new file mode 100644 index 000000000..95f000f04 --- /dev/null +++ b/cw_bitcoin/lib/encryption_file_utils.dart @@ -0,0 +1,42 @@ +import 'dart:io'; +import 'dart:typed_data'; +import 'package:cw_bitcoin/file.dart' as bf; +import 'package:cake_backup/backup.dart' as cwb; + +EncryptionFileUtils encryptionFileUtilsFor(bool direct) + => direct + ? XChaCha20EncryptionFileUtils() + : Salsa20EncryhptionFileUtils(); + +abstract class EncryptionFileUtils { + Future<void> write({required String path, required String password, required String data}); + Future<String> read({required String path, required String password}); +} + +class Salsa20EncryhptionFileUtils extends EncryptionFileUtils { + // Requires legacy complex key + iv as password + @override + Future<void> write({required String path, required String password, required String data}) async + => await bf.write(path: path, password: password, data: data); + + // Requires legacy complex key + iv as password + @override + Future<String> read({required String path, required String password}) async + => await bf.read(path: path, password: password); +} + +class XChaCha20EncryptionFileUtils extends EncryptionFileUtils { + @override + Future<void> write({required String path, required String password, required String data}) async { + final encrypted = await cwb.encrypt(password, Uint8List.fromList(data.codeUnits)); + await File(path).writeAsBytes(encrypted); + } + + @override + Future<String> read({required String path, required String password}) async { + final file = File(path); + final encrypted = await file.readAsBytes(); + final bytes = await cwb.decrypt(password, encrypted); + return String.fromCharCodes(bytes); + } +} \ No newline at end of file diff --git a/cw_bitcoin/lib/file.dart b/cw_bitcoin/lib/file.dart index 8fd236ec3..49b7d895e 100644 --- a/cw_bitcoin/lib/file.dart +++ b/cw_bitcoin/lib/file.dart @@ -2,6 +2,7 @@ import 'dart:io'; import 'package:cw_core/key.dart'; import 'package:encrypt/encrypt.dart' as encrypt; +// Do not use directly, move to Salsa20EncryhptionFile Future<void> write( {required String path, required String password, diff --git a/cw_bitcoin/lib/litecoin_wallet.dart b/cw_bitcoin/lib/litecoin_wallet.dart index 6bf1c5735..6d5171fd7 100644 --- a/cw_bitcoin/lib/litecoin_wallet.dart +++ b/cw_bitcoin/lib/litecoin_wallet.dart @@ -1,5 +1,6 @@ import 'package:cw_bitcoin/bitcoin_mnemonic.dart'; import 'package:cw_bitcoin/bitcoin_transaction_priority.dart'; +import 'package:cw_bitcoin/encryption_file_utils.dart'; import 'package:cw_core/crypto_currency.dart'; import 'package:cw_core/unspent_coins_info.dart'; import 'package:cw_bitcoin/litecoin_wallet_addresses.dart'; @@ -26,6 +27,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { required WalletInfo walletInfo, required Box<UnspentCoinsInfo> unspentCoinsInfo, required Uint8List seedBytes, + required EncryptionFileUtils encryptionFileUtils, List<BitcoinAddressRecord>? initialAddresses, ElectrumBalance? initialBalance, int initialRegularAddressIndex = 0, @@ -39,6 +41,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { initialAddresses: initialAddresses, initialBalance: initialBalance, seedBytes: seedBytes, + encryptionFileUtils: encryptionFileUtils, currency: CryptoCurrency.ltc) { walletAddresses = LitecoinWalletAddresses( walletInfo, @@ -58,6 +61,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { required String password, required WalletInfo walletInfo, required Box<UnspentCoinsInfo> unspentCoinsInfo, + required EncryptionFileUtils encryptionFileUtils, List<BitcoinAddressRecord>? initialAddresses, ElectrumBalance? initialBalance, int initialRegularAddressIndex = 0, @@ -71,6 +75,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { initialAddresses: initialAddresses, initialBalance: initialBalance, seedBytes: await mnemonicToSeedBytes(mnemonic), + encryptionFileUtils: encryptionFileUtils, initialRegularAddressIndex: initialRegularAddressIndex, initialChangeAddressIndex: initialChangeAddressIndex); } @@ -80,8 +85,9 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { required WalletInfo walletInfo, required Box<UnspentCoinsInfo> unspentCoinsInfo, required String password, + required EncryptionFileUtils encryptionFileUtils }) async { - final snp = await ElectrumWallletSnapshot.load (name, walletInfo.type, password); + final snp = await ElectrumWallletSnapshot.load(encryptionFileUtils, name, walletInfo.type, password); return LitecoinWallet( mnemonic: snp.mnemonic, password: password, @@ -90,6 +96,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { initialAddresses: snp.addresses, initialBalance: snp.balance, seedBytes: await mnemonicToSeedBytes(snp.mnemonic), + encryptionFileUtils: encryptionFileUtils, initialRegularAddressIndex: snp.regularAddressIndex, initialChangeAddressIndex: snp.changeAddressIndex); } diff --git a/cw_bitcoin/lib/litecoin_wallet_service.dart b/cw_bitcoin/lib/litecoin_wallet_service.dart index b13ac7a7f..1df08d8e0 100644 --- a/cw_bitcoin/lib/litecoin_wallet_service.dart +++ b/cw_bitcoin/lib/litecoin_wallet_service.dart @@ -1,4 +1,5 @@ import 'dart:io'; +import 'package:cw_bitcoin/encryption_file_utils.dart'; import 'package:cw_core/unspent_coins_info.dart'; import 'package:hive/hive.dart'; import 'package:cw_bitcoin/bitcoin_mnemonic.dart'; @@ -12,14 +13,13 @@ import 'package:cw_core/wallet_info.dart'; import 'package:cw_core/wallet_base.dart'; import 'package:collection/collection.dart'; -class LitecoinWalletService extends WalletService< - BitcoinNewWalletCredentials, - BitcoinRestoreWalletFromSeedCredentials, - BitcoinRestoreWalletFromWIFCredentials> { - LitecoinWalletService(this.walletInfoSource, this.unspentCoinsInfoSource); +class LitecoinWalletService extends WalletService<BitcoinNewWalletCredentials, + BitcoinRestoreWalletFromSeedCredentials, BitcoinRestoreWalletFromWIFCredentials> { + LitecoinWalletService(this.walletInfoSource, this.unspentCoinsInfoSource, this.isDirect); final Box<WalletInfo> walletInfoSource; final Box<UnspentCoinsInfo> unspentCoinsInfoSource; + final bool isDirect; @override WalletType getType() => WalletType.litecoin; @@ -30,7 +30,8 @@ class LitecoinWalletService extends WalletService< mnemonic: await generateMnemonic(), password: credentials.password!, walletInfo: credentials.walletInfo!, - unspentCoinsInfo: unspentCoinsInfoSource); + unspentCoinsInfo: unspentCoinsInfoSource, + encryptionFileUtils: encryptionFileUtilsFor(isDirect)); await wallet.save(); await wallet.init(); @@ -43,33 +44,37 @@ class LitecoinWalletService extends WalletService< @override Future<LitecoinWallet> openWallet(String name, String password) async { - final walletInfo = walletInfoSource.values.firstWhereOrNull( - (info) => info.id == WalletBase.idFor(name, getType()))!; + final walletInfo = walletInfoSource.values + .firstWhereOrNull((info) => info.id == WalletBase.idFor(name, getType()))!; final wallet = await LitecoinWalletBase.open( - password: password, name: name, walletInfo: walletInfo, - unspentCoinsInfo: unspentCoinsInfoSource); + password: password, + name: name, + walletInfo: walletInfo, + unspentCoinsInfo: unspentCoinsInfoSource, + encryptionFileUtils: encryptionFileUtilsFor(isDirect)); await wallet.init(); return wallet; } @override Future<void> remove(String wallet) async { - File(await pathForWalletDir(name: wallet, type: getType())) - .delete(recursive: true); - final walletInfo = walletInfoSource.values.firstWhereOrNull( - (info) => info.id == WalletBase.idFor(wallet, getType()))!; + File(await pathForWalletDir(name: wallet, type: getType())).delete(recursive: true); + final walletInfo = walletInfoSource.values + .firstWhereOrNull((info) => info.id == WalletBase.idFor(wallet, getType()))!; await walletInfoSource.delete(walletInfo.key); } @override Future<void> rename(String currentName, String password, String newName) async { - final currentWalletInfo = walletInfoSource.values.firstWhereOrNull( - (info) => info.id == WalletBase.idFor(currentName, getType()))!; + final currentWalletInfo = walletInfoSource.values + .firstWhereOrNull((info) => info.id == WalletBase.idFor(currentName, getType()))!; final currentWallet = await LitecoinWalletBase.open( - password: password, - name: currentName, - walletInfo: currentWalletInfo, - unspentCoinsInfo: unspentCoinsInfoSource); + password: password, + name: currentName, + walletInfo: currentWalletInfo, + unspentCoinsInfo: unspentCoinsInfoSource, + encryptionFileUtils: encryptionFileUtilsFor(isDirect), + ); await currentWallet.renameWalletFiles(newName); @@ -96,7 +101,8 @@ class LitecoinWalletService extends WalletService< password: credentials.password!, mnemonic: credentials.mnemonic, walletInfo: credentials.walletInfo!, - unspentCoinsInfo: unspentCoinsInfoSource); + unspentCoinsInfo: unspentCoinsInfoSource, + encryptionFileUtils: encryptionFileUtilsFor(isDirect)); await wallet.save(); await wallet.init(); return wallet; diff --git a/cw_bitcoin/pubspec.lock b/cw_bitcoin/pubspec.lock index bfcd9e5a6..4bce06bab 100644 --- a/cw_bitcoin/pubspec.lock +++ b/cw_bitcoin/pubspec.lock @@ -21,10 +21,10 @@ packages: dependency: transitive description: name: args - sha256: "139d809800a412ebb26a3892da228b2d0ba36f0ef5d9a82166e5e52ec8d61611" + sha256: "4cab82a83ffef80b262ddedf47a0a8e56ee6fbf7fe21e6e768b02792034dd440" url: "https://pub.dev" source: hosted - version: "2.3.2" + version: "2.4.0" asn1lib: dependency: transitive description: @@ -111,10 +111,10 @@ packages: dependency: transitive description: name: build_daemon - sha256: "6bc5544ea6ce4428266e7ea680e945c68806c4aae2da0eb5e9ccf38df8d6acbf" + sha256: "757153e5d9cd88253cb13f28c2fb55a537dc31fefd98137549895b5beb7c6169" url: "https://pub.dev" source: hosted - version: "3.1.0" + version: "3.1.1" build_resolvers: dependency: "direct dev" description: @@ -151,10 +151,19 @@ packages: dependency: transitive description: name: built_value - sha256: "169565c8ad06adb760c3645bf71f00bff161b00002cace266cad42c5d22a7725" + sha256: "31b7c748fd4b9adf8d25d72a4c4a59ef119f12876cf414f94f8af5131d5fa2b0" url: "https://pub.dev" source: hosted - version: "8.4.3" + version: "8.4.4" + cake_backup: + dependency: "direct main" + description: + path: "." + ref: main + resolved-ref: "3aba867dcab6737f6707782f5db15d71f303db38" + url: "https://github.com/cake-tech/cake_backup.git" + source: git + version: "1.0.0+1" characters: dependency: transitive description: @@ -215,10 +224,18 @@ packages: dependency: "direct main" description: name: cryptography - sha256: e0e37f79665cd5c86e8897f9abe1accfe813c0cc5299dab22256e22fddc1fef8 + sha256: df156c5109286340817d21fa7b62f9140f17915077127dd70f8bd7a2a0997a35 url: "https://pub.dev" source: hosted - version: "2.0.5" + version: "2.5.0" + cupertino_icons: + dependency: transitive + description: + name: cupertino_icons + sha256: e35129dc44c9118cee2a5603506d823bab99c68393879edb440e0090d07586be + url: "https://pub.dev" + source: hosted + version: "1.0.5" cw_core: dependency: "direct main" description: @@ -270,10 +287,10 @@ packages: dependency: transitive description: name: fixnum - sha256: "25517a4deb0c03aa0f32fd12db525856438902d9c16536311e76cdc57b31d7d1" + sha256: "04be3e934c52e082558cc9ee21f42f5c1cd7a1262f4c63cd0357c08d5bba81ec" url: "https://pub.dev" source: hosted - version: "1.1.0" + version: "1.0.1" flutter: dependency: "direct main" description: flutter @@ -440,10 +457,10 @@ packages: dependency: "direct main" description: name: mobx - sha256: f1862bd92c6a903fab67338f27e2f731117c3cb9ea37cee1a487f9e4e0de314a + sha256: "6738620307a424d2c9ad8b873f4dce391c44e9135eb4e75668ac8202fec7a9b8" url: "https://pub.dev" source: hosted - version: "2.1.3+1" + version: "2.1.4" mobx_codegen: dependency: "direct dev" description: @@ -472,50 +489,50 @@ packages: dependency: "direct main" description: name: path_provider - sha256: dcea5feb97d8abf90cab9e9030b497fb7c3cbf26b7a1fe9e3ef7dcb0a1ddec95 + sha256: "04890b994ee89bfa80bf3080bfec40d5a92c5c7a785ebb02c13084a099d2b6f9" url: "https://pub.dev" source: hosted - version: "2.0.12" + version: "2.0.13" path_provider_android: dependency: transitive description: name: path_provider_android - sha256: a776c088d671b27f6e3aa8881d64b87b3e80201c64e8869b811325de7a76c15e + sha256: "019f18c9c10ae370b08dce1f3e3b73bc9f58e7f087bb5e921f06529438ac0ae7" url: "https://pub.dev" source: hosted - version: "2.0.22" + version: "2.0.24" path_provider_foundation: dependency: transitive description: name: path_provider_foundation - sha256: "62a68e7e1c6c459f9289859e2fae58290c981ce21d1697faf54910fe1faa4c74" + sha256: "026b97a6c29da75181a37aae2eba9227f5fe13cb2838c6b975ce209328b8ab4e" url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "2.1.3" path_provider_linux: dependency: transitive description: name: path_provider_linux - sha256: ab0987bf95bc591da42dffb38c77398fc43309f0b9b894dcc5d6f40c4b26c379 + sha256: "2ae08f2216225427e64ad224a24354221c2c7907e448e6e0e8b57b1eb9f10ad1" url: "https://pub.dev" source: hosted - version: "2.1.7" + version: "2.1.10" path_provider_platform_interface: dependency: transitive description: name: path_provider_platform_interface - sha256: f0abc8ebd7253741f05488b4813d936b4d07c6bae3e86148a09e342ee4b08e76 + sha256: "57585299a729335f1298b43245842678cb9f43a6310351b18fb577d6e33165ec" url: "https://pub.dev" source: hosted - version: "2.0.5" + version: "2.0.6" path_provider_windows: dependency: transitive description: name: path_provider_windows - sha256: bcabbe399d4042b8ee687e17548d5d3f527255253b4a639f5f8d2094a9c2b45c + sha256: f53720498d5a543f9607db4b0e997c4b5438884de25b0f73098cc2671a51b130 url: "https://pub.dev" source: hosted - version: "2.1.3" + version: "2.1.5" platform: dependency: transitive description: @@ -528,10 +545,10 @@ packages: dependency: transitive description: name: plugin_platform_interface - sha256: dbf0f707c78beedc9200146ad3cb0ab4d5da13c246336987be6940f026500d3a + sha256: "6a2128648c854906c53fa8e33986fc0247a1116122f9534dd20e3ab9e16a32bc" url: "https://pub.dev" source: hosted - version: "2.1.3" + version: "2.1.4" pointycastle: dependency: transitive description: @@ -568,10 +585,10 @@ packages: dependency: transitive description: name: pubspec_parse - sha256: "75f6614d6dde2dc68948dffbaa4fe5dae32cd700eb9fb763fe11dfb45a3c4d0a" + sha256: ec85d7d55339d85f44ec2b682a82fea340071e8978257e5a43e69f79e98ef50c url: "https://pub.dev" source: hosted - version: "1.2.1" + version: "1.2.2" rxdart: dependency: "direct main" description: @@ -681,6 +698,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.1" + tuple: + dependency: transitive + description: + name: tuple + sha256: "0ea99cd2f9352b2586583ab2ce6489d1f95a5f6de6fb9492faaf97ae2060f0aa" + url: "https://pub.dev" + source: hosted + version: "2.0.1" typed_data: dependency: transitive description: @@ -733,10 +758,10 @@ packages: dependency: transitive description: name: xdg_directories - sha256: bd512f03919aac5f1313eb8249f223bacf4927031bf60b02601f81f687689e86 + sha256: ee1505df1426458f7f60aac270645098d318a8b4766d85fde75f76f2e21807d1 url: "https://pub.dev" source: hosted - version: "0.2.0+3" + version: "1.0.0" yaml: dependency: transitive description: diff --git a/cw_bitcoin/pubspec.yaml b/cw_bitcoin/pubspec.yaml index dae0af39b..e0f5dad67 100644 --- a/cw_bitcoin/pubspec.yaml +++ b/cw_bitcoin/pubspec.yaml @@ -27,6 +27,11 @@ dependencies: unorm_dart: ^0.2.0 cryptography: ^2.0.5 encrypt: ^5.0.1 + cake_backup: + git: + url: https://github.com/cake-tech/cake_backup.git + ref: main + version: 1.0.0 dev_dependencies: flutter_test: diff --git a/cw_core/lib/cake_hive.dart b/cw_core/lib/cake_hive.dart deleted file mode 100644 index aadf6bf9a..000000000 --- a/cw_core/lib/cake_hive.dart +++ /dev/null @@ -1,4 +0,0 @@ -import 'package:hive/hive.dart'; -import 'package:hive/src/hive_impl.dart'; - -final HiveInterface CakeHive = HiveImpl(); diff --git a/cw_core/lib/erc20_token.dart b/cw_core/lib/erc20_token.dart index fd27aaba6..db5b6db5b 100644 --- a/cw_core/lib/erc20_token.dart +++ b/cw_core/lib/erc20_token.dart @@ -1,5 +1,4 @@ import 'package:cw_core/crypto_currency.dart'; -import 'package:cw_core/hive_type_ids.dart'; import 'package:hive/hive.dart'; part 'erc20_token.g.dart'; @@ -54,7 +53,7 @@ class Erc20Token extends CryptoCurrency with HiveObjectMixin { iconPath: icon, ); - static const typeId = ERC20_TOKEN_TYPE_ID; + static const typeId = 12; static const boxName = 'Erc20Tokens'; @override diff --git a/cw_core/lib/hive_type_ids.dart b/cw_core/lib/hive_type_ids.dart deleted file mode 100644 index 0961182bc..000000000 --- a/cw_core/lib/hive_type_ids.dart +++ /dev/null @@ -1,13 +0,0 @@ -const CONTACT_TYPE_ID = 0; -const NODE_TYPE_ID = 1; -const TRANSACTION_TYPE_ID = 2; -const TRADE_TYPE_ID = 3; -const WALLET_INFO_TYPE_ID = 4; -const WALLET_TYPE_TYPE_ID = 5; -const TEMPLATE_TYPE_ID = 6; -const EXCHANGE_TEMPLATE_TYPE_ID = 7; -const ORDER_TYPE_ID = 8; -const UNSPENT_COINS_INFO_TYPE_ID = 9; -const ANONPAY_INVOICE_INFO_TYPE_ID = 10; - -const ERC20_TOKEN_TYPE_ID = 12; diff --git a/cw_core/lib/node.dart b/cw_core/lib/node.dart index 59a1450f6..3fa45b44c 100644 --- a/cw_core/lib/node.dart +++ b/cw_core/lib/node.dart @@ -3,7 +3,6 @@ import 'package:cw_core/keyable.dart'; import 'dart:convert'; import 'package:http/http.dart' as http; import 'package:hive/hive.dart'; -import 'package:cw_core/hive_type_ids.dart'; import 'package:cw_core/wallet_type.dart'; import 'package:http/io_client.dart' as ioc; @@ -38,7 +37,7 @@ class Node extends HiveObject with Keyable { trusted = map['trusted'] as bool? ?? false, socksProxyAddress = map['socksProxyPort'] as String?; - static const typeId = NODE_TYPE_ID; + static const typeId = 1; static const boxName = 'Nodes'; @HiveField(0, defaultValue: '') diff --git a/cw_core/lib/pathForWallet.dart b/cw_core/lib/pathForWallet.dart index af4838ffa..5b27a4729 100644 --- a/cw_core/lib/pathForWallet.dart +++ b/cw_core/lib/pathForWallet.dart @@ -1,10 +1,11 @@ import 'dart:io'; +import 'package:cw_core/root_dir.dart'; import 'package:cw_core/wallet_type.dart'; import 'package:flutter/foundation.dart'; import 'package:path_provider/path_provider.dart'; Future<String> pathForWalletDir({required String name, required WalletType type}) async { - final root = await getApplicationDocumentsDirectory(); + final root = await getAppDir(); final prefix = walletTypeToString(type).toLowerCase(); final walletsDir = Directory('${root.path}/wallets'); final walletDire = Directory('${walletsDir.path}/$prefix/$name'); @@ -21,7 +22,7 @@ Future<String> pathForWallet({required String name, required WalletType type}) a .then((path) => path + '/$name'); Future<String> outdatedAndroidPathForWalletDir({required String name}) async { - final directory = await getApplicationDocumentsDirectory(); + final directory = await getAppDir(); final pathDir = directory.path + '/$name'; return pathDir; diff --git a/cw_core/lib/root_dir.dart b/cw_core/lib/root_dir.dart new file mode 100644 index 000000000..efbe59ce9 --- /dev/null +++ b/cw_core/lib/root_dir.dart @@ -0,0 +1,26 @@ +import 'dart:io'; +import 'package:path_provider/path_provider.dart'; + +String? _rootDirPath; + +void setRootDirFromEnv() + => _rootDirPath = Platform.environment['CAKE_WALLET_DIR']; + +Future<Directory> getAppDir({String appName = 'cake_wallet'}) async { + Directory dir; + + if (_rootDirPath != null && _rootDirPath!.isNotEmpty) { + dir = Directory.fromUri(Uri.file(_rootDirPath!)); + dir.create(recursive: true); + } else { + dir = await getApplicationDocumentsDirectory(); + + if (Platform.isLinux) { + final appDirPath = '${dir.path}/$appName'; + dir = Directory.fromUri(Uri.file(appDirPath)); + await dir.create(recursive: true); + } + } + + return dir; +} \ No newline at end of file diff --git a/cw_core/lib/sec_random_native.dart b/cw_core/lib/sec_random_native.dart index ce251efc0..02230b872 100644 --- a/cw_core/lib/sec_random_native.dart +++ b/cw_core/lib/sec_random_native.dart @@ -1,11 +1,18 @@ -import 'dart:typed_data'; - +import 'dart:io'; +import 'dart:math'; import 'package:flutter/services.dart'; const utils = const MethodChannel('com.cake_wallet/native_utils'); Future<Uint8List> secRandom(int count) async { try { + if (Platform.isLinux) { + // Used method to get securely generated random bytes from cake backups + const byteSize = 256; + final rng = Random.secure(); + return Uint8List.fromList(List<int>.generate(count, (_) => rng.nextInt(byteSize))); + } + return await utils.invokeMethod<Uint8List>('sec_random', {'count': count}) ?? Uint8List.fromList([]); } on PlatformException catch (_) { return Uint8List.fromList([]); diff --git a/cw_core/lib/unspent_coins_info.dart b/cw_core/lib/unspent_coins_info.dart index 68bbcbfd2..6fd682c06 100644 --- a/cw_core/lib/unspent_coins_info.dart +++ b/cw_core/lib/unspent_coins_info.dart @@ -1,4 +1,3 @@ -import 'package:cw_core/hive_type_ids.dart'; import 'package:hive/hive.dart'; part 'unspent_coins_info.g.dart'; @@ -17,7 +16,7 @@ class UnspentCoinsInfo extends HiveObject { this.keyImage = null }); - static const typeId = UNSPENT_COINS_INFO_TYPE_ID; + static const typeId = 9; static const boxName = 'Unspent'; static const boxKey = 'unspentBoxKey'; @@ -51,4 +50,4 @@ class UnspentCoinsInfo extends HiveObject { String get note => noteRaw ?? ''; set note(String value) => noteRaw = value; -} +} \ No newline at end of file diff --git a/cw_core/lib/wallet_base.dart b/cw_core/lib/wallet_base.dart index 5bc5ef914..3f6b57edc 100644 --- a/cw_core/lib/wallet_base.dart +++ b/cw_core/lib/wallet_base.dart @@ -74,6 +74,8 @@ abstract class WalletBase< Future<void> changePassword(String password); + String get password; + Future<void>? updateBalance(); void setExceptionHandler(void Function(FlutterErrorDetails) onError) => null; diff --git a/cw_core/lib/wallet_info.dart b/cw_core/lib/wallet_info.dart index 6b3fa9e98..a25702cf7 100644 --- a/cw_core/lib/wallet_info.dart +++ b/cw_core/lib/wallet_info.dart @@ -1,7 +1,7 @@ -import 'dart:async'; -import 'package:cw_core/hive_type_ids.dart'; -import 'package:cw_core/wallet_type.dart'; +import 'package:flutter/foundation.dart'; import 'package:hive/hive.dart'; +import 'package:cw_core/wallet_type.dart'; +import 'dart:async'; part 'wallet_info.g.dart'; @@ -30,7 +30,7 @@ class WalletInfo extends HiveObject { yatEid, yatLastUsedAddressRaw, showIntroCakePayCard); } - static const typeId = WALLET_INFO_TYPE_ID; + static const typeId = 4; static const boxName = 'WalletInfo'; @HiveField(0, defaultValue: '') diff --git a/cw_core/lib/wallet_type.dart b/cw_core/lib/wallet_type.dart index 62c2ad410..a65839041 100644 --- a/cw_core/lib/wallet_type.dart +++ b/cw_core/lib/wallet_type.dart @@ -1,5 +1,4 @@ import 'package:cw_core/crypto_currency.dart'; -import 'package:cw_core/hive_type_ids.dart'; import 'package:hive/hive.dart'; part 'wallet_type.g.dart'; @@ -11,8 +10,9 @@ const walletTypes = [ WalletType.haven, WalletType.ethereum, ]; +const walletTypeTypeId = 5; -@HiveType(typeId: WALLET_TYPE_TYPE_ID) +@HiveType(typeId: walletTypeTypeId) enum WalletType { @HiveField(0) monero, diff --git a/cw_core/pubspec.lock b/cw_core/pubspec.lock index 01e19dda4..b0fb67435 100644 --- a/cw_core/pubspec.lock +++ b/cw_core/pubspec.lock @@ -5,218 +5,191 @@ packages: dependency: transitive description: name: _fe_analyzer_shared - sha256: "4897882604d919befd350648c7f91926a9d5de99e67b455bf0917cc2362f4bb8" - url: "https://pub.dev" + url: "https://pub.dartlang.org" source: hosted version: "47.0.0" analyzer: dependency: transitive description: name: analyzer - sha256: "690e335554a8385bc9d787117d9eb52c0c03ee207a607e593de3c9d71b1cfe80" - url: "https://pub.dev" + url: "https://pub.dartlang.org" source: hosted version: "4.7.0" args: dependency: transitive description: name: args - sha256: "139d809800a412ebb26a3892da228b2d0ba36f0ef5d9a82166e5e52ec8d61611" - url: "https://pub.dev" + url: "https://pub.dartlang.org" source: hosted - version: "2.3.2" + version: "2.4.0" asn1lib: dependency: transitive description: name: asn1lib - sha256: ab96a1cb3beeccf8145c52e449233fe68364c9641623acd3adad66f8184f1039 - url: "https://pub.dev" + url: "https://pub.dartlang.org" source: hosted version: "1.4.0" async: dependency: transitive description: name: async - sha256: bfe67ef28df125b7dddcea62755991f807aa39a2492a23e1550161692950bbe0 - url: "https://pub.dev" + url: "https://pub.dartlang.org" source: hosted - version: "2.10.0" + version: "2.9.0" boolean_selector: dependency: transitive description: name: boolean_selector - sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" - url: "https://pub.dev" + url: "https://pub.dartlang.org" source: hosted - version: "2.1.1" + version: "2.1.0" build: dependency: transitive description: name: build - sha256: "3fbda25365741f8251b39f3917fb3c8e286a96fd068a5a242e11c2012d495777" - url: "https://pub.dev" + url: "https://pub.dartlang.org" source: hosted version: "2.3.1" build_config: dependency: transitive description: name: build_config - sha256: bf80fcfb46a29945b423bd9aad884590fb1dc69b330a4d4700cac476af1708d1 - url: "https://pub.dev" + url: "https://pub.dartlang.org" source: hosted version: "1.1.1" build_daemon: dependency: transitive description: name: build_daemon - sha256: "6bc5544ea6ce4428266e7ea680e945c68806c4aae2da0eb5e9ccf38df8d6acbf" - url: "https://pub.dev" + url: "https://pub.dartlang.org" source: hosted - version: "3.1.0" + version: "3.1.1" build_resolvers: dependency: "direct dev" description: name: build_resolvers - sha256: "687cf90a3951affac1bd5f9ecb5e3e90b60487f3d9cdc359bb310f8876bb02a6" - url: "https://pub.dev" + url: "https://pub.dartlang.org" source: hosted version: "2.0.10" build_runner: dependency: "direct dev" description: name: build_runner - sha256: b0a8a7b8a76c493e85f1b84bffa0588859a06197863dba8c9036b15581fd9727 - url: "https://pub.dev" + url: "https://pub.dartlang.org" source: hosted version: "2.3.3" build_runner_core: dependency: transitive description: name: build_runner_core - sha256: "14febe0f5bac5ae474117a36099b4de6f1dbc52df6c5e55534b3da9591bf4292" - url: "https://pub.dev" + url: "https://pub.dartlang.org" source: hosted version: "7.2.7" built_collection: dependency: transitive description: name: built_collection - sha256: "376e3dd27b51ea877c28d525560790aee2e6fbb5f20e2f85d5081027d94e2100" - url: "https://pub.dev" + url: "https://pub.dartlang.org" source: hosted version: "5.1.1" built_value: dependency: transitive description: name: built_value - sha256: "169565c8ad06adb760c3645bf71f00bff161b00002cace266cad42c5d22a7725" - url: "https://pub.dev" + url: "https://pub.dartlang.org" source: hosted - version: "8.4.3" + version: "8.4.4" characters: dependency: transitive description: name: characters - sha256: e6a326c8af69605aec75ed6c187d06b349707a27fbff8222ca9cc2cff167975c - url: "https://pub.dev" + url: "https://pub.dartlang.org" source: hosted version: "1.2.1" checked_yaml: dependency: transitive description: name: checked_yaml - sha256: "3d1505d91afa809d177efd4eed5bb0eb65805097a1463abdd2add076effae311" - url: "https://pub.dev" + url: "https://pub.dartlang.org" source: hosted version: "2.0.2" clock: dependency: transitive description: name: clock - sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf - url: "https://pub.dev" + url: "https://pub.dartlang.org" source: hosted version: "1.1.1" code_builder: dependency: transitive description: name: code_builder - sha256: "0d43dd1288fd145de1ecc9a3948ad4a6d5a82f0a14c4fdd0892260787d975cbe" - url: "https://pub.dev" + url: "https://pub.dartlang.org" source: hosted version: "4.4.0" collection: dependency: transitive description: name: collection - sha256: cfc915e6923fe5ce6e153b0723c753045de46de1b4d63771530504004a45fae0 - url: "https://pub.dev" + url: "https://pub.dartlang.org" source: hosted - version: "1.17.0" + version: "1.16.0" convert: dependency: transitive description: name: convert - sha256: "0f08b14755d163f6e2134cb58222dd25ea2a2ee8a195e53983d57c075324d592" - url: "https://pub.dev" + url: "https://pub.dartlang.org" source: hosted version: "3.1.1" crypto: dependency: transitive description: name: crypto - sha256: aa274aa7774f8964e4f4f38cc994db7b6158dd36e9187aaceaddc994b35c6c67 - url: "https://pub.dev" + url: "https://pub.dartlang.org" source: hosted version: "3.0.2" dart_style: dependency: transitive description: name: dart_style - sha256: "7a03456c3490394c8e7665890333e91ae8a49be43542b616e414449ac358acd4" - url: "https://pub.dev" + url: "https://pub.dartlang.org" source: hosted version: "2.2.4" encrypt: dependency: "direct main" description: name: encrypt - sha256: "4fd4e4fdc21b9d7d4141823e1e6515cd94e7b8d84749504c232999fba25d9bbb" - url: "https://pub.dev" + url: "https://pub.dartlang.org" source: hosted version: "5.0.1" fake_async: dependency: transitive description: name: fake_async - sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" - url: "https://pub.dev" + url: "https://pub.dartlang.org" source: hosted version: "1.3.1" ffi: dependency: transitive description: name: ffi - sha256: a38574032c5f1dd06c4aee541789906c12ccaab8ba01446e800d9c5b79c4a978 - url: "https://pub.dev" + url: "https://pub.dartlang.org" source: hosted version: "2.0.1" file: dependency: "direct main" description: name: file - sha256: "1b92bec4fc2a72f59a8e15af5f52cd441e4a7860b49499d69dfa817af20e925d" - url: "https://pub.dev" + url: "https://pub.dartlang.org" source: hosted version: "6.1.4" fixnum: dependency: transitive description: name: fixnum - sha256: "25517a4deb0c03aa0f32fd12db525856438902d9c16536311e76cdc57b31d7d1" - url: "https://pub.dev" + url: "https://pub.dartlang.org" source: hosted - version: "1.1.0" + version: "1.0.1" flutter: dependency: "direct main" description: flutter @@ -226,8 +199,7 @@ packages: dependency: "direct main" description: name: flutter_mobx - sha256: "0da4add0016387a7bf309a0d0c41d36c6b3ae25ed7a176409267f166509e723e" - url: "https://pub.dev" + url: "https://pub.dartlang.org" source: hosted version: "2.0.6+5" flutter_test: @@ -239,288 +211,252 @@ packages: dependency: transitive description: name: frontend_server_client - sha256: "408e3ca148b31c20282ad6f37ebfa6f4bdc8fede5b74bc2f08d9d92b55db3612" - url: "https://pub.dev" + url: "https://pub.dartlang.org" source: hosted version: "3.2.0" glob: dependency: transitive description: name: glob - sha256: "4515b5b6ddb505ebdd242a5f2cc5d22d3d6a80013789debfbda7777f47ea308c" - url: "https://pub.dev" + url: "https://pub.dartlang.org" source: hosted version: "2.1.1" graphs: dependency: transitive description: name: graphs - sha256: f9e130f3259f52d26f0cfc0e964513796dafed572fa52e45d2f8d6ca14db39b2 - url: "https://pub.dev" + url: "https://pub.dartlang.org" source: hosted version: "2.2.0" hive: dependency: transitive description: name: hive - sha256: "8dcf6db979d7933da8217edcec84e9df1bdb4e4edc7fc77dbd5aa74356d6d941" - url: "https://pub.dev" + url: "https://pub.dartlang.org" source: hosted version: "2.2.3" hive_generator: dependency: "direct dev" description: name: hive_generator - sha256: "81fd20125cb2ce8fd23623d7744ffbaf653aae93706c9bd3bf7019ea0ace3938" - url: "https://pub.dev" + url: "https://pub.dartlang.org" source: hosted version: "1.1.3" http: dependency: "direct main" description: name: http - sha256: "6aa2946395183537c8b880962d935877325d6a09a2867c3970c05c0fed6ac482" - url: "https://pub.dev" + url: "https://pub.dartlang.org" source: hosted version: "0.13.5" http_multi_server: dependency: transitive description: name: http_multi_server - sha256: "97486f20f9c2f7be8f514851703d0119c3596d14ea63227af6f7a481ef2b2f8b" - url: "https://pub.dev" + url: "https://pub.dartlang.org" source: hosted version: "3.2.1" http_parser: dependency: transitive description: name: http_parser - sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b" - url: "https://pub.dev" + url: "https://pub.dartlang.org" source: hosted version: "4.0.2" intl: dependency: "direct main" description: name: intl - sha256: "910f85bce16fb5c6f614e117efa303e85a1731bb0081edf3604a2ae6e9a3cc91" - url: "https://pub.dev" + url: "https://pub.dartlang.org" source: hosted version: "0.17.0" io: dependency: transitive description: name: io - sha256: "2ec25704aba361659e10e3e5f5d672068d332fc8ac516421d483a11e5cbd061e" - url: "https://pub.dev" + url: "https://pub.dartlang.org" source: hosted version: "1.0.4" js: dependency: transitive description: name: js - sha256: "5528c2f391ededb7775ec1daa69e65a2d61276f7552de2b5f7b8d34ee9fd4ab7" - url: "https://pub.dev" + url: "https://pub.dartlang.org" source: hosted version: "0.6.5" json_annotation: dependency: transitive description: name: json_annotation - sha256: c33da08e136c3df0190bd5bbe51ae1df4a7d96e7954d1d7249fea2968a72d317 - url: "https://pub.dev" + url: "https://pub.dartlang.org" source: hosted version: "4.8.0" logging: dependency: transitive description: name: logging - sha256: "04094f2eb032cbb06c6f6e8d3607edcfcb0455e2bb6cbc010cb01171dcb64e6d" - url: "https://pub.dev" + url: "https://pub.dartlang.org" source: hosted version: "1.1.1" matcher: dependency: transitive description: name: matcher - sha256: "16db949ceee371e9b99d22f88fa3a73c4e59fd0afed0bd25fc336eb76c198b72" - url: "https://pub.dev" + url: "https://pub.dartlang.org" source: hosted - version: "0.12.13" + version: "0.12.12" material_color_utilities: dependency: transitive description: name: material_color_utilities - sha256: d92141dc6fe1dad30722f9aa826c7fbc896d021d792f80678280601aff8cf724 - url: "https://pub.dev" + url: "https://pub.dartlang.org" source: hosted - version: "0.2.0" + version: "0.1.5" meta: dependency: transitive description: name: meta - sha256: "6c268b42ed578a53088d834796959e4a1814b5e9e164f147f580a386e5decf42" - url: "https://pub.dev" + url: "https://pub.dartlang.org" source: hosted version: "1.8.0" mime: dependency: transitive description: name: mime - sha256: e4ff8e8564c03f255408decd16e7899da1733852a9110a58fe6d1b817684a63e - url: "https://pub.dev" + url: "https://pub.dartlang.org" source: hosted version: "1.0.4" mobx: dependency: "direct main" description: name: mobx - sha256: f1862bd92c6a903fab67338f27e2f731117c3cb9ea37cee1a487f9e4e0de314a - url: "https://pub.dev" + url: "https://pub.dartlang.org" source: hosted - version: "2.1.3+1" + version: "2.1.4" mobx_codegen: dependency: "direct dev" description: name: mobx_codegen - sha256: "86122e410d8ea24dda0c69adb5c2a6ccadd5ce02ad46e144764e0d0184a06181" - url: "https://pub.dev" + url: "https://pub.dartlang.org" source: hosted version: "2.1.1" package_config: dependency: transitive description: name: package_config - sha256: "1c5b77ccc91e4823a5af61ee74e6b972db1ef98c2ff5a18d3161c982a55448bd" - url: "https://pub.dev" + url: "https://pub.dartlang.org" source: hosted version: "2.1.0" path: dependency: transitive description: name: path - sha256: db9d4f58c908a4ba5953fcee2ae317c94889433e5024c27ce74a37f94267945b - url: "https://pub.dev" + url: "https://pub.dartlang.org" source: hosted version: "1.8.2" path_provider: dependency: "direct main" description: name: path_provider - sha256: dcea5feb97d8abf90cab9e9030b497fb7c3cbf26b7a1fe9e3ef7dcb0a1ddec95 - url: "https://pub.dev" + url: "https://pub.dartlang.org" source: hosted - version: "2.0.12" + version: "2.0.13" path_provider_android: dependency: transitive description: name: path_provider_android - sha256: a776c088d671b27f6e3aa8881d64b87b3e80201c64e8869b811325de7a76c15e - url: "https://pub.dev" + url: "https://pub.dartlang.org" source: hosted - version: "2.0.22" + version: "2.0.24" path_provider_foundation: dependency: transitive description: name: path_provider_foundation - sha256: "62a68e7e1c6c459f9289859e2fae58290c981ce21d1697faf54910fe1faa4c74" - url: "https://pub.dev" + url: "https://pub.dartlang.org" source: hosted - version: "2.1.1" + version: "2.1.3" path_provider_linux: dependency: transitive description: name: path_provider_linux - sha256: ab0987bf95bc591da42dffb38c77398fc43309f0b9b894dcc5d6f40c4b26c379 - url: "https://pub.dev" + url: "https://pub.dartlang.org" source: hosted - version: "2.1.7" + version: "2.1.10" path_provider_platform_interface: dependency: transitive description: name: path_provider_platform_interface - sha256: f0abc8ebd7253741f05488b4813d936b4d07c6bae3e86148a09e342ee4b08e76 - url: "https://pub.dev" + url: "https://pub.dartlang.org" source: hosted - version: "2.0.5" + version: "2.0.6" path_provider_windows: dependency: transitive description: name: path_provider_windows - sha256: bcabbe399d4042b8ee687e17548d5d3f527255253b4a639f5f8d2094a9c2b45c - url: "https://pub.dev" + url: "https://pub.dartlang.org" source: hosted - version: "2.1.3" + version: "2.1.5" platform: dependency: transitive description: name: platform - sha256: "4a451831508d7d6ca779f7ac6e212b4023dd5a7d08a27a63da33756410e32b76" - url: "https://pub.dev" + url: "https://pub.dartlang.org" source: hosted version: "3.1.0" plugin_platform_interface: dependency: transitive description: name: plugin_platform_interface - sha256: dbf0f707c78beedc9200146ad3cb0ab4d5da13c246336987be6940f026500d3a - url: "https://pub.dev" + url: "https://pub.dartlang.org" source: hosted - version: "2.1.3" + version: "2.1.4" pointycastle: dependency: transitive description: name: pointycastle - sha256: db7306cf0249f838d1a24af52b5a5887c5bf7f31d8bb4e827d071dc0939ad346 - url: "https://pub.dev" + url: "https://pub.dartlang.org" source: hosted version: "3.6.2" pool: dependency: transitive description: name: pool - sha256: "20fe868b6314b322ea036ba325e6fc0711a22948856475e2c2b6306e8ab39c2a" - url: "https://pub.dev" + url: "https://pub.dartlang.org" source: hosted version: "1.5.1" process: dependency: transitive description: name: process - sha256: "53fd8db9cec1d37b0574e12f07520d582019cb6c44abf5479a01505099a34a09" - url: "https://pub.dev" + url: "https://pub.dartlang.org" source: hosted version: "4.2.4" pub_semver: dependency: transitive description: name: pub_semver - sha256: "307de764d305289ff24ad257ad5c5793ce56d04947599ad68b3baa124105fc17" - url: "https://pub.dev" + url: "https://pub.dartlang.org" source: hosted version: "2.1.3" pubspec_parse: dependency: transitive description: name: pubspec_parse - sha256: "75f6614d6dde2dc68948dffbaa4fe5dae32cd700eb9fb763fe11dfb45a3c4d0a" - url: "https://pub.dev" + url: "https://pub.dartlang.org" source: hosted - version: "1.2.1" + version: "1.2.2" shelf: dependency: transitive description: name: shelf - sha256: c24a96135a2ccd62c64b69315a14adc5c3419df63b4d7c05832a346fdb73682c - url: "https://pub.dev" + url: "https://pub.dartlang.org" source: hosted version: "1.4.0" shelf_web_socket: dependency: transitive description: name: shelf_web_socket - sha256: a988c0e8d8ffbdb8a28aa7ec8e449c260f3deb808781fe1284d22c5bba7156e8 - url: "https://pub.dev" + url: "https://pub.dartlang.org" source: hosted version: "1.0.3" sky_engine: @@ -532,136 +468,119 @@ packages: dependency: transitive description: name: source_gen - sha256: "2d79738b6bbf38a43920e2b8d189e9a3ce6cc201f4b8fc76be5e4fe377b1c38d" - url: "https://pub.dev" + url: "https://pub.dartlang.org" source: hosted version: "1.2.6" source_helper: dependency: transitive description: name: source_helper - sha256: "3b67aade1d52416149c633ba1bb36df44d97c6b51830c2198e934e3fca87ca1f" - url: "https://pub.dev" + url: "https://pub.dartlang.org" source: hosted version: "1.3.3" source_span: dependency: transitive description: name: source_span - sha256: dd904f795d4b4f3b870833847c461801f6750a9fa8e61ea5ac53f9422b31f250 - url: "https://pub.dev" + url: "https://pub.dartlang.org" source: hosted - version: "1.9.1" + version: "1.9.0" stack_trace: dependency: transitive description: name: stack_trace - sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5 - url: "https://pub.dev" + url: "https://pub.dartlang.org" source: hosted - version: "1.11.0" + version: "1.10.0" stream_channel: dependency: transitive description: name: stream_channel - sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8" - url: "https://pub.dev" + url: "https://pub.dartlang.org" source: hosted - version: "2.1.1" + version: "2.1.0" stream_transform: dependency: transitive description: name: stream_transform - sha256: "14a00e794c7c11aa145a170587321aedce29769c08d7f58b1d141da75e3b1c6f" - url: "https://pub.dev" + url: "https://pub.dartlang.org" source: hosted version: "2.1.0" string_scanner: dependency: transitive description: name: string_scanner - sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" - url: "https://pub.dev" + url: "https://pub.dartlang.org" source: hosted - version: "1.2.0" + version: "1.1.1" term_glyph: dependency: transitive description: name: term_glyph - sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 - url: "https://pub.dev" + url: "https://pub.dartlang.org" source: hosted version: "1.2.1" test_api: dependency: transitive description: name: test_api - sha256: ad540f65f92caa91bf21dfc8ffb8c589d6e4dc0c2267818b4cc2792857706206 - url: "https://pub.dev" + url: "https://pub.dartlang.org" source: hosted - version: "0.4.16" + version: "0.4.12" timing: dependency: transitive description: name: timing - sha256: "70a3b636575d4163c477e6de42f247a23b315ae20e86442bebe32d3cabf61c32" - url: "https://pub.dev" + url: "https://pub.dartlang.org" source: hosted version: "1.0.1" typed_data: dependency: transitive description: name: typed_data - sha256: "26f87ade979c47a150c9eaab93ccd2bebe70a27dc0b4b29517f2904f04eb11a5" - url: "https://pub.dev" + url: "https://pub.dartlang.org" source: hosted version: "1.3.1" vector_math: dependency: transitive description: name: vector_math - sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" - url: "https://pub.dev" + url: "https://pub.dartlang.org" source: hosted - version: "2.1.4" + version: "2.1.2" watcher: dependency: transitive description: name: watcher - sha256: "6a7f46926b01ce81bfc339da6a7f20afbe7733eff9846f6d6a5466aa4c6667c0" - url: "https://pub.dev" + url: "https://pub.dartlang.org" source: hosted version: "1.0.2" web_socket_channel: dependency: transitive description: name: web_socket_channel - sha256: ca49c0bc209c687b887f30527fb6a9d80040b072cc2990f34b9bec3e7663101b - url: "https://pub.dev" + url: "https://pub.dartlang.org" source: hosted version: "2.3.0" win32: dependency: transitive description: name: win32 - sha256: c9ebe7ee4ab0c2194e65d3a07d8c54c5d00bb001b76081c4a04cdb8448b59e46 - url: "https://pub.dev" + url: "https://pub.dartlang.org" source: hosted version: "3.1.3" xdg_directories: dependency: transitive description: name: xdg_directories - sha256: bd512f03919aac5f1313eb8249f223bacf4927031bf60b02601f81f687689e86 - url: "https://pub.dev" + url: "https://pub.dartlang.org" source: hosted - version: "0.2.0+3" + version: "1.0.0" yaml: dependency: transitive description: name: yaml - sha256: "23812a9b125b48d4007117254bca50abb6c712352927eece9e155207b1db2370" - url: "https://pub.dev" + url: "https://pub.dartlang.org" source: hosted version: "3.1.1" sdks: diff --git a/cw_ethereum/lib/encryption_file_utils.dart b/cw_ethereum/lib/encryption_file_utils.dart new file mode 100644 index 000000000..4644b8cfb --- /dev/null +++ b/cw_ethereum/lib/encryption_file_utils.dart @@ -0,0 +1,42 @@ +import 'dart:io'; +import 'dart:typed_data'; +import 'package:cw_ethereum/file.dart' as ef; +import 'package:cake_backup/backup.dart' as cwb; + +EncryptionFileUtils encryptionFileUtilsFor(bool direct) + => direct + ? XChaCha20EncryptionFileUtils() + : Salsa20EncryhptionFileUtils(); + +abstract class EncryptionFileUtils { + Future<void> write({required String path, required String password, required String data}); + Future<String> read({required String path, required String password}); +} + +class Salsa20EncryhptionFileUtils extends EncryptionFileUtils { + // Requires legacy complex key + iv as password + @override + Future<void> write({required String path, required String password, required String data}) async + => await ef.write(path: path, password: password, data: data); + + // Requires legacy complex key + iv as password + @override + Future<String> read({required String path, required String password}) async + => await ef.read(path: path, password: password); +} + +class XChaCha20EncryptionFileUtils extends EncryptionFileUtils { + @override + Future<void> write({required String path, required String password, required String data}) async { + final encrypted = await cwb.encrypt(password, Uint8List.fromList(data.codeUnits)); + await File(path).writeAsBytes(encrypted); + } + + @override + Future<String> read({required String path, required String password}) async { + final file = File(path); + final encrypted = await file.readAsBytes(); + final bytes = await cwb.decrypt(password, encrypted); + return String.fromCharCodes(bytes); + } +} \ No newline at end of file diff --git a/cw_ethereum/lib/ethereum_transaction_history.dart b/cw_ethereum/lib/ethereum_transaction_history.dart index 4511f4436..397625f05 100644 --- a/cw_ethereum/lib/ethereum_transaction_history.dart +++ b/cw_ethereum/lib/ethereum_transaction_history.dart @@ -2,7 +2,7 @@ import 'dart:convert'; import 'dart:core'; import 'package:cw_core/pathForWallet.dart'; import 'package:cw_core/wallet_info.dart'; -import 'package:cw_ethereum/file.dart'; +import 'package:cw_ethereum/encryption_file_utils.dart'; import 'package:mobx/mobx.dart'; import 'package:cw_core/transaction_history.dart'; import 'package:cw_ethereum/ethereum_transaction_info.dart'; @@ -15,12 +15,16 @@ class EthereumTransactionHistory = EthereumTransactionHistoryBase with _$Ethereu abstract class EthereumTransactionHistoryBase extends TransactionHistoryBase<EthereumTransactionInfo> with Store { - EthereumTransactionHistoryBase({required this.walletInfo, required String password}) - : _password = password { + EthereumTransactionHistoryBase({ + required this.walletInfo, + required String password, + required this.encryptionFileUtils, + }) : _password = password { transactions = ObservableMap<String, EthereumTransactionInfo>(); } final WalletInfo walletInfo; + final EncryptionFileUtils encryptionFileUtils; String _password; Future<void> init() async => await _load(); @@ -31,7 +35,7 @@ abstract class EthereumTransactionHistoryBase final dirPath = await pathForWalletDir(name: walletInfo.name, type: walletInfo.type); final path = '$dirPath/$transactionsHistoryFileName'; final data = json.encode({'transactions': transactions}); - await writeData(path: path, password: _password, data: data); + await encryptionFileUtils.write(path: path, password: _password, data: data); } catch (e, s) { print('Error while save ethereum transaction history: ${e.toString()}'); print(s); @@ -48,7 +52,7 @@ abstract class EthereumTransactionHistoryBase Future<Map<String, dynamic>> _read() async { final dirPath = await pathForWalletDir(name: walletInfo.name, type: walletInfo.type); final path = '$dirPath/$transactionsHistoryFileName'; - final content = await read(path: path, password: _password); + final content = await encryptionFileUtils.read(path: path, password: _password); if (content.isEmpty) { return {}; } diff --git a/cw_ethereum/lib/ethereum_wallet.dart b/cw_ethereum/lib/ethereum_wallet.dart index 8d7c477e1..d8c236657 100644 --- a/cw_ethereum/lib/ethereum_wallet.dart +++ b/cw_ethereum/lib/ethereum_wallet.dart @@ -4,7 +4,6 @@ import 'dart:io'; import 'dart:math'; import 'package:cw_core/crypto_currency.dart'; -import 'package:cw_core/cake_hive.dart'; import 'package:cw_core/node.dart'; import 'package:cw_core/pathForWallet.dart'; import 'package:cw_core/pending_transaction.dart'; @@ -15,6 +14,7 @@ import 'package:cw_core/wallet_addresses.dart'; import 'package:cw_core/wallet_base.dart'; import 'package:cw_core/wallet_info.dart'; import 'package:cw_ethereum/default_erc20_tokens.dart'; +import 'package:cw_ethereum/encryption_file_utils.dart'; import 'package:cw_ethereum/erc20_balance.dart'; import 'package:cw_ethereum/ethereum_client.dart'; import 'package:cw_ethereum/ethereum_exceptions.dart'; @@ -47,22 +47,28 @@ abstract class EthereumWalletBase String? mnemonic, String? privateKey, required String password, + required EncryptionFileUtils encryptionFileUtils, ERC20Balance? initialBalance, }) : syncStatus = NotConnectedSyncStatus(), _password = password, _mnemonic = mnemonic, _hexPrivateKey = privateKey, _isTransactionUpdating = false, + _encryptionFileUtils = encryptionFileUtils, _client = EthereumClient(), walletAddresses = EthereumWalletAddresses(walletInfo), balance = ObservableMap<CryptoCurrency, ERC20Balance>.of( {CryptoCurrency.eth: initialBalance ?? ERC20Balance(BigInt.zero)}), super(walletInfo) { this.walletInfo = walletInfo; - transactionHistory = EthereumTransactionHistory(walletInfo: walletInfo, password: password); + transactionHistory = EthereumTransactionHistory( + walletInfo: walletInfo, + password: password, + encryptionFileUtils: encryptionFileUtils, + ); - if (!CakeHive.isAdapterRegistered(Erc20Token.typeId)) { - CakeHive.registerAdapter(Erc20TokenAdapter()); + if (!Hive.isAdapterRegistered(Erc20Token.typeId)) { + Hive.registerAdapter(Erc20TokenAdapter()); } _sharedPrefs.complete(SharedPreferences.getInstance()); @@ -72,6 +78,8 @@ abstract class EthereumWalletBase final String? _hexPrivateKey; final String _password; + final EncryptionFileUtils _encryptionFileUtils; + late final Box<Erc20Token> erc20TokensBox; late final EthPrivateKey _ethPrivateKey; @@ -99,7 +107,7 @@ abstract class EthereumWalletBase Completer<SharedPreferences> _sharedPrefs = Completer(); Future<void> init() async { - erc20TokensBox = await CakeHive.openBox<Erc20Token>(Erc20Token.boxName); + erc20TokensBox = await Hive.openBox<Erc20Token>(Erc20Token.boxName); await walletAddresses.init(); await transactionHistory.init(); _ethPrivateKey = await getPrivateKey( @@ -301,7 +309,7 @@ abstract class EthereumWalletBase Future<void> save() async { await walletAddresses.updateAddressesInBox(); final path = await makePath(); - await write(path: path, password: _password, data: toJSON()); + await _encryptionFileUtils.write(path: path, password: _password, data: toJSON()); await transactionHistory.save(); } @@ -344,9 +352,10 @@ abstract class EthereumWalletBase required String name, required String password, required WalletInfo walletInfo, + required EncryptionFileUtils encryptionFileUtils, }) async { final path = await pathForWallet(name: name, type: walletInfo.type); - final jsonSource = await read(path: path, password: password); + final jsonSource = await encryptionFileUtils.read(path: path, password: password); final data = json.decode(jsonSource) as Map; final mnemonic = data['mnemonic'] as String?; final privateKey = data['private_key'] as String?; @@ -358,6 +367,7 @@ abstract class EthereumWalletBase mnemonic: mnemonic, privateKey: privateKey, initialBalance: balance, + encryptionFileUtils: encryptionFileUtils, ); } @@ -502,4 +512,7 @@ abstract class EthereumWalletBase _transactionsUpdateTimer?.cancel(); } } + + @override + String get password => _password; } diff --git a/cw_ethereum/lib/ethereum_wallet_creation_credentials.dart b/cw_ethereum/lib/ethereum_wallet_creation_credentials.dart index 6546f2fae..3d1efa54a 100644 --- a/cw_ethereum/lib/ethereum_wallet_creation_credentials.dart +++ b/cw_ethereum/lib/ethereum_wallet_creation_credentials.dart @@ -2,8 +2,8 @@ import 'package:cw_core/wallet_credentials.dart'; import 'package:cw_core/wallet_info.dart'; class EthereumNewWalletCredentials extends WalletCredentials { - EthereumNewWalletCredentials({required String name, WalletInfo? walletInfo}) - : super(name: name, walletInfo: walletInfo); + EthereumNewWalletCredentials({required String name, WalletInfo? walletInfo, String? password}) + : super(name: name, walletInfo: walletInfo, password: password); } class EthereumRestoreWalletFromSeedCredentials extends WalletCredentials { diff --git a/cw_ethereum/lib/ethereum_wallet_service.dart b/cw_ethereum/lib/ethereum_wallet_service.dart index 16dbc0b04..66066b996 100644 --- a/cw_ethereum/lib/ethereum_wallet_service.dart +++ b/cw_ethereum/lib/ethereum_wallet_service.dart @@ -5,6 +5,7 @@ import 'package:cw_core/wallet_base.dart'; import 'package:cw_core/wallet_info.dart'; import 'package:cw_core/wallet_service.dart'; import 'package:cw_core/wallet_type.dart'; +import 'package:cw_ethereum/encryption_file_utils.dart'; import 'package:cw_ethereum/ethereum_mnemonics.dart'; import 'package:cw_ethereum/ethereum_wallet.dart'; import 'package:cw_ethereum/ethereum_wallet_creation_credentials.dart'; @@ -14,9 +15,10 @@ import 'package:collection/collection.dart'; class EthereumWalletService extends WalletService<EthereumNewWalletCredentials, EthereumRestoreWalletFromSeedCredentials, EthereumRestoreWalletFromPrivateKey> { - EthereumWalletService(this.walletInfoSource); + EthereumWalletService(this.walletInfoSource, this.isDirect); final Box<WalletInfo> walletInfoSource; + final bool isDirect; @override Future<EthereumWallet> create(EthereumNewWalletCredentials credentials) async { @@ -25,6 +27,7 @@ class EthereumWalletService extends WalletService<EthereumNewWalletCredentials, walletInfo: credentials.walletInfo!, mnemonic: mnemonic, password: credentials.password!, + encryptionFileUtils: encryptionFileUtilsFor(isDirect), ); await wallet.init(); @@ -49,6 +52,7 @@ class EthereumWalletService extends WalletService<EthereumNewWalletCredentials, name: name, password: password, walletInfo: walletInfo, + encryptionFileUtils: encryptionFileUtilsFor(isDirect), ); await wallet.init(); @@ -60,8 +64,8 @@ class EthereumWalletService extends WalletService<EthereumNewWalletCredentials, @override Future<void> remove(String wallet) async { File(await pathForWalletDir(name: wallet, type: getType())).delete(recursive: true); - final walletInfo = walletInfoSource.values.firstWhereOrNull( - (info) => info.id == WalletBase.idFor(wallet, getType()))!; + final walletInfo = walletInfoSource.values + .firstWhereOrNull((info) => info.id == WalletBase.idFor(wallet, getType()))!; await walletInfoSource.delete(walletInfo.key); } @@ -91,6 +95,7 @@ class EthereumWalletService extends WalletService<EthereumNewWalletCredentials, password: credentials.password!, mnemonic: credentials.mnemonic, walletInfo: credentials.walletInfo!, + encryptionFileUtils: encryptionFileUtilsFor(isDirect), ); await wallet.init(); @@ -105,7 +110,11 @@ class EthereumWalletService extends WalletService<EthereumNewWalletCredentials, final currentWalletInfo = walletInfoSource.values .firstWhere((info) => info.id == WalletBase.idFor(currentName, getType())); final currentWallet = await EthereumWalletBase.open( - password: password, name: currentName, walletInfo: currentWalletInfo); + password: password, + name: currentName, + walletInfo: currentWalletInfo, + encryptionFileUtils: encryptionFileUtilsFor(isDirect), + ); await currentWallet.renameWalletFiles(newName); diff --git a/cw_ethereum/pubspec.yaml b/cw_ethereum/pubspec.yaml index 5d19589f3..6b324aa7b 100644 --- a/cw_ethereum/pubspec.yaml +++ b/cw_ethereum/pubspec.yaml @@ -23,6 +23,11 @@ dependencies: shared_preferences: ^2.0.15 cw_core: path: ../cw_core + cake_backup: + git: + url: https://github.com/cake-tech/cake_backup.git + ref: main + version: 1.0.0 dev_dependencies: flutter_test: diff --git a/cw_monero/.metadata b/cw_monero/.metadata index 46a2f7f6f..679a0404c 100644 --- a/cw_monero/.metadata +++ b/cw_monero/.metadata @@ -18,6 +18,9 @@ migration: - platform: macos create_revision: e3c29ec00c9c825c891d75054c63fcc46454dca1 base_revision: e3c29ec00c9c825c891d75054c63fcc46454dca1 + - platform: linux + create_revision: e3c29ec00c9c825c891d75054c63fcc46454dca1 + base_revision: e3c29ec00c9c825c891d75054c63fcc46454dca1 # User provided section diff --git a/cw_monero/example/linux/.gitignore b/cw_monero/example/linux/.gitignore new file mode 100644 index 000000000..d3896c984 --- /dev/null +++ b/cw_monero/example/linux/.gitignore @@ -0,0 +1 @@ +flutter/ephemeral diff --git a/cw_monero/example/linux/CMakeLists.txt b/cw_monero/example/linux/CMakeLists.txt new file mode 100644 index 000000000..8b2f28252 --- /dev/null +++ b/cw_monero/example/linux/CMakeLists.txt @@ -0,0 +1,138 @@ +# Project-level configuration. +cmake_minimum_required(VERSION 3.10) +project(runner LANGUAGES CXX) + +# The name of the executable created for the application. Change this to change +# the on-disk name of your application. +set(BINARY_NAME "cw_monero_example") +# The unique GTK application identifier for this application. See: +# https://wiki.gnome.org/HowDoI/ChooseApplicationID +set(APPLICATION_ID "com.cakewallet.cw_monero") + +# Explicitly opt in to modern CMake behaviors to avoid warnings with recent +# versions of CMake. +cmake_policy(SET CMP0063 NEW) + +# Load bundled libraries from the lib/ directory relative to the binary. +set(CMAKE_INSTALL_RPATH "$ORIGIN/lib") + +# Root filesystem for cross-building. +if(FLUTTER_TARGET_PLATFORM_SYSROOT) + set(CMAKE_SYSROOT ${FLUTTER_TARGET_PLATFORM_SYSROOT}) + set(CMAKE_FIND_ROOT_PATH ${CMAKE_SYSROOT}) + set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) + set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) + set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) + set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) +endif() + +# Define build configuration options. +if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) + set(CMAKE_BUILD_TYPE "Debug" CACHE + STRING "Flutter build mode" FORCE) + set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS + "Debug" "Profile" "Release") +endif() + +# Compilation settings that should be applied to most targets. +# +# Be cautious about adding new options here, as plugins use this function by +# default. In most cases, you should add new options to specific targets instead +# of modifying this function. +function(APPLY_STANDARD_SETTINGS TARGET) + target_compile_features(${TARGET} PUBLIC cxx_std_14) + target_compile_options(${TARGET} PRIVATE -Wall -Werror) + target_compile_options(${TARGET} PRIVATE "$<$<NOT:$<CONFIG:Debug>>:-O3>") + target_compile_definitions(${TARGET} PRIVATE "$<$<NOT:$<CONFIG:Debug>>:NDEBUG>") +endfunction() + +# Flutter library and tool build rules. +set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") +add_subdirectory(${FLUTTER_MANAGED_DIR}) + +# System-level dependencies. +find_package(PkgConfig REQUIRED) +pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0) + +add_definitions(-DAPPLICATION_ID="${APPLICATION_ID}") + +# Define the application target. To change its name, change BINARY_NAME above, +# not the value here, or `flutter run` will no longer work. +# +# Any new source files that you add to the application should be added here. +add_executable(${BINARY_NAME} + "main.cc" + "my_application.cc" + "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" +) + +# Apply the standard set of build settings. This can be removed for applications +# that need different build settings. +apply_standard_settings(${BINARY_NAME}) + +# Add dependency libraries. Add any application-specific dependencies here. +target_link_libraries(${BINARY_NAME} PRIVATE flutter) +target_link_libraries(${BINARY_NAME} PRIVATE PkgConfig::GTK) + +# Run the Flutter tool portions of the build. This must not be removed. +add_dependencies(${BINARY_NAME} flutter_assemble) + +# Only the install-generated bundle's copy of the executable will launch +# correctly, since the resources must in the right relative locations. To avoid +# people trying to run the unbundled copy, put it in a subdirectory instead of +# the default top-level location. +set_target_properties(${BINARY_NAME} + PROPERTIES + RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/intermediates_do_not_run" +) + +# Generated plugin build rules, which manage building the plugins and adding +# them to the application. +include(flutter/generated_plugins.cmake) + + +# === Installation === +# By default, "installing" just makes a relocatable bundle in the build +# directory. +set(BUILD_BUNDLE_DIR "${PROJECT_BINARY_DIR}/bundle") +if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) + set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE) +endif() + +# Start with a clean build bundle directory every time. +install(CODE " + file(REMOVE_RECURSE \"${BUILD_BUNDLE_DIR}/\") + " COMPONENT Runtime) + +set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data") +set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}/lib") + +install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) + +foreach(bundled_library ${PLUGIN_BUNDLED_LIBRARIES}) + install(FILES "${bundled_library}" + DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) +endforeach(bundled_library) + +# Fully re-copy the assets directory on each build to avoid having stale files +# from a previous install. +set(FLUTTER_ASSET_DIR_NAME "flutter_assets") +install(CODE " + file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\") + " COMPONENT Runtime) +install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}" + DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime) + +# Install the AOT library on non-Debug builds only. +if(NOT CMAKE_BUILD_TYPE MATCHES "Debug") + install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) +endif() diff --git a/cw_monero/example/linux/flutter/CMakeLists.txt b/cw_monero/example/linux/flutter/CMakeLists.txt new file mode 100644 index 000000000..d5bd01648 --- /dev/null +++ b/cw_monero/example/linux/flutter/CMakeLists.txt @@ -0,0 +1,88 @@ +# This file controls Flutter-level build steps. It should not be edited. +cmake_minimum_required(VERSION 3.10) + +set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral") + +# Configuration provided via flutter tool. +include(${EPHEMERAL_DIR}/generated_config.cmake) + +# TODO: Move the rest of this into files in ephemeral. See +# https://github.com/flutter/flutter/issues/57146. + +# Serves the same purpose as list(TRANSFORM ... PREPEND ...), +# which isn't available in 3.10. +function(list_prepend LIST_NAME PREFIX) + set(NEW_LIST "") + foreach(element ${${LIST_NAME}}) + list(APPEND NEW_LIST "${PREFIX}${element}") + endforeach(element) + set(${LIST_NAME} "${NEW_LIST}" PARENT_SCOPE) +endfunction() + +# === Flutter Library === +# System-level dependencies. +find_package(PkgConfig REQUIRED) +pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0) +pkg_check_modules(GLIB REQUIRED IMPORTED_TARGET glib-2.0) +pkg_check_modules(GIO REQUIRED IMPORTED_TARGET gio-2.0) + +set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/libflutter_linux_gtk.so") + +# Published to parent scope for install step. +set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE) +set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE) +set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE) +set(AOT_LIBRARY "${PROJECT_DIR}/build/lib/libapp.so" PARENT_SCOPE) + +list(APPEND FLUTTER_LIBRARY_HEADERS + "fl_basic_message_channel.h" + "fl_binary_codec.h" + "fl_binary_messenger.h" + "fl_dart_project.h" + "fl_engine.h" + "fl_json_message_codec.h" + "fl_json_method_codec.h" + "fl_message_codec.h" + "fl_method_call.h" + "fl_method_channel.h" + "fl_method_codec.h" + "fl_method_response.h" + "fl_plugin_registrar.h" + "fl_plugin_registry.h" + "fl_standard_message_codec.h" + "fl_standard_method_codec.h" + "fl_string_codec.h" + "fl_value.h" + "fl_view.h" + "flutter_linux.h" +) +list_prepend(FLUTTER_LIBRARY_HEADERS "${EPHEMERAL_DIR}/flutter_linux/") +add_library(flutter INTERFACE) +target_include_directories(flutter INTERFACE + "${EPHEMERAL_DIR}" +) +target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}") +target_link_libraries(flutter INTERFACE + PkgConfig::GTK + PkgConfig::GLIB + PkgConfig::GIO +) +add_dependencies(flutter flutter_assemble) + +# === Flutter tool backend === +# _phony_ is a non-existent file to force this command to run every time, +# since currently there's no way to get a full input/output list from the +# flutter tool. +add_custom_command( + OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} + ${CMAKE_CURRENT_BINARY_DIR}/_phony_ + COMMAND ${CMAKE_COMMAND} -E env + ${FLUTTER_TOOL_ENVIRONMENT} + "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.sh" + ${FLUTTER_TARGET_PLATFORM} ${CMAKE_BUILD_TYPE} + VERBATIM +) +add_custom_target(flutter_assemble DEPENDS + "${FLUTTER_LIBRARY}" + ${FLUTTER_LIBRARY_HEADERS} +) diff --git a/cw_monero/example/linux/flutter/generated_plugin_registrant.cc b/cw_monero/example/linux/flutter/generated_plugin_registrant.cc new file mode 100644 index 000000000..1936c88a6 --- /dev/null +++ b/cw_monero/example/linux/flutter/generated_plugin_registrant.cc @@ -0,0 +1,15 @@ +// +// Generated file. Do not edit. +// + +// clang-format off + +#include "generated_plugin_registrant.h" + +#include <cw_monero/cw_monero_plugin.h> + +void fl_register_plugins(FlPluginRegistry* registry) { + g_autoptr(FlPluginRegistrar) cw_monero_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "CwMoneroPlugin"); + cw_monero_plugin_register_with_registrar(cw_monero_registrar); +} diff --git a/cw_monero/example/linux/flutter/generated_plugin_registrant.h b/cw_monero/example/linux/flutter/generated_plugin_registrant.h new file mode 100644 index 000000000..e0f0a47bc --- /dev/null +++ b/cw_monero/example/linux/flutter/generated_plugin_registrant.h @@ -0,0 +1,15 @@ +// +// Generated file. Do not edit. +// + +// clang-format off + +#ifndef GENERATED_PLUGIN_REGISTRANT_ +#define GENERATED_PLUGIN_REGISTRANT_ + +#include <flutter_linux/flutter_linux.h> + +// Registers Flutter plugins. +void fl_register_plugins(FlPluginRegistry* registry); + +#endif // GENERATED_PLUGIN_REGISTRANT_ diff --git a/cw_monero/example/linux/flutter/generated_plugins.cmake b/cw_monero/example/linux/flutter/generated_plugins.cmake new file mode 100644 index 000000000..efcc9a8f9 --- /dev/null +++ b/cw_monero/example/linux/flutter/generated_plugins.cmake @@ -0,0 +1,24 @@ +# +# Generated file, do not edit. +# + +list(APPEND FLUTTER_PLUGIN_LIST + cw_monero +) + +list(APPEND FLUTTER_FFI_PLUGIN_LIST +) + +set(PLUGIN_BUNDLED_LIBRARIES) + +foreach(plugin ${FLUTTER_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/linux plugins/${plugin}) + target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) + list(APPEND PLUGIN_BUNDLED_LIBRARIES $<TARGET_FILE:${plugin}_plugin>) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) +endforeach(plugin) + +foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/linux plugins/${ffi_plugin}) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) +endforeach(ffi_plugin) diff --git a/cw_monero/example/linux/main.cc b/cw_monero/example/linux/main.cc new file mode 100644 index 000000000..e7c5c5437 --- /dev/null +++ b/cw_monero/example/linux/main.cc @@ -0,0 +1,6 @@ +#include "my_application.h" + +int main(int argc, char** argv) { + g_autoptr(MyApplication) app = my_application_new(); + return g_application_run(G_APPLICATION(app), argc, argv); +} diff --git a/cw_monero/example/linux/my_application.cc b/cw_monero/example/linux/my_application.cc new file mode 100644 index 000000000..875fc557a --- /dev/null +++ b/cw_monero/example/linux/my_application.cc @@ -0,0 +1,104 @@ +#include "my_application.h" + +#include <flutter_linux/flutter_linux.h> +#ifdef GDK_WINDOWING_X11 +#include <gdk/gdkx.h> +#endif + +#include "flutter/generated_plugin_registrant.h" + +struct _MyApplication { + GtkApplication parent_instance; + char** dart_entrypoint_arguments; +}; + +G_DEFINE_TYPE(MyApplication, my_application, GTK_TYPE_APPLICATION) + +// Implements GApplication::activate. +static void my_application_activate(GApplication* application) { + MyApplication* self = MY_APPLICATION(application); + GtkWindow* window = + GTK_WINDOW(gtk_application_window_new(GTK_APPLICATION(application))); + + // Use a header bar when running in GNOME as this is the common style used + // by applications and is the setup most users will be using (e.g. Ubuntu + // desktop). + // If running on X and not using GNOME then just use a traditional title bar + // in case the window manager does more exotic layout, e.g. tiling. + // If running on Wayland assume the header bar will work (may need changing + // if future cases occur). + gboolean use_header_bar = TRUE; +#ifdef GDK_WINDOWING_X11 + GdkScreen* screen = gtk_window_get_screen(window); + if (GDK_IS_X11_SCREEN(screen)) { + const gchar* wm_name = gdk_x11_screen_get_window_manager_name(screen); + if (g_strcmp0(wm_name, "GNOME Shell") != 0) { + use_header_bar = FALSE; + } + } +#endif + if (use_header_bar) { + GtkHeaderBar* header_bar = GTK_HEADER_BAR(gtk_header_bar_new()); + gtk_widget_show(GTK_WIDGET(header_bar)); + gtk_header_bar_set_title(header_bar, "cw_monero_example"); + gtk_header_bar_set_show_close_button(header_bar, TRUE); + gtk_window_set_titlebar(window, GTK_WIDGET(header_bar)); + } else { + gtk_window_set_title(window, "cw_monero_example"); + } + + gtk_window_set_default_size(window, 1280, 720); + gtk_widget_show(GTK_WIDGET(window)); + + g_autoptr(FlDartProject) project = fl_dart_project_new(); + fl_dart_project_set_dart_entrypoint_arguments(project, self->dart_entrypoint_arguments); + + FlView* view = fl_view_new(project); + gtk_widget_show(GTK_WIDGET(view)); + gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(view)); + + fl_register_plugins(FL_PLUGIN_REGISTRY(view)); + + gtk_widget_grab_focus(GTK_WIDGET(view)); +} + +// Implements GApplication::local_command_line. +static gboolean my_application_local_command_line(GApplication* application, gchar*** arguments, int* exit_status) { + MyApplication* self = MY_APPLICATION(application); + // Strip out the first argument as it is the binary name. + self->dart_entrypoint_arguments = g_strdupv(*arguments + 1); + + g_autoptr(GError) error = nullptr; + if (!g_application_register(application, nullptr, &error)) { + g_warning("Failed to register: %s", error->message); + *exit_status = 1; + return TRUE; + } + + g_application_activate(application); + *exit_status = 0; + + return TRUE; +} + +// Implements GObject::dispose. +static void my_application_dispose(GObject* object) { + MyApplication* self = MY_APPLICATION(object); + g_clear_pointer(&self->dart_entrypoint_arguments, g_strfreev); + G_OBJECT_CLASS(my_application_parent_class)->dispose(object); +} + +static void my_application_class_init(MyApplicationClass* klass) { + G_APPLICATION_CLASS(klass)->activate = my_application_activate; + G_APPLICATION_CLASS(klass)->local_command_line = my_application_local_command_line; + G_OBJECT_CLASS(klass)->dispose = my_application_dispose; +} + +static void my_application_init(MyApplication* self) {} + +MyApplication* my_application_new() { + return MY_APPLICATION(g_object_new(my_application_get_type(), + "application-id", APPLICATION_ID, + "flags", G_APPLICATION_NON_UNIQUE, + nullptr)); +} diff --git a/cw_monero/example/linux/my_application.h b/cw_monero/example/linux/my_application.h new file mode 100644 index 000000000..72271d5e4 --- /dev/null +++ b/cw_monero/example/linux/my_application.h @@ -0,0 +1,18 @@ +#ifndef FLUTTER_MY_APPLICATION_H_ +#define FLUTTER_MY_APPLICATION_H_ + +#include <gtk/gtk.h> + +G_DECLARE_FINAL_TYPE(MyApplication, my_application, MY, APPLICATION, + GtkApplication) + +/** + * my_application_new: + * + * Creates a new Flutter-based application. + * + * Returns: a new #MyApplication. + */ +MyApplication* my_application_new(); + +#endif // FLUTTER_MY_APPLICATION_H_ diff --git a/cw_monero/ios/Classes/monero_api.cpp b/cw_monero/ios/Classes/monero_api.cpp index 66b8605c6..d81907813 100644 --- a/cw_monero/ios/Classes/monero_api.cpp +++ b/cw_monero/ios/Classes/monero_api.cpp @@ -13,16 +13,22 @@ // Fix for randomx on ios void __clear_cache(void* start, void* end) { } #include "../External/ios/include/wallet2_api.h" +#elif __linux__ +#include "../External/linux/include/wallet2_api.h" +#include <string.h> #else #include "../External/android/include/wallet2_api.h" #endif +#if defined(__GNUC__) + #define FUNCTION_VISABILITY_ATTRIBUTE __attribute__((visibility("default"))) __attribute__((used)) +#endif + using namespace std::chrono_literals; #ifdef __cplusplus extern "C" { #endif - const uint64_t MONERO_BLOCK_SIZE = 1000; struct Utf8Box { @@ -151,7 +157,8 @@ extern "C" fee = transaction->fee(); blockHeight = transaction->blockHeight(); subaddrAccount = transaction->subaddrAccount(); - std::set<uint32_t>::iterator it = transaction->subaddrIndex().begin(); + std::set<uint32_t> subIndex = transaction->subaddrIndex(); + std::set<uint32_t>::iterator it = subIndex.begin(); subaddrIndex = *it; confirmations = transaction->confirmations(); datetime = static_cast<int64_t>(transaction->timestamp()); @@ -250,6 +257,7 @@ extern "C" std::mutex store_lock; bool is_storing = false; + FUNCTION_VISABILITY_ATTRIBUTE void change_current_wallet(Monero::Wallet *wallet) { m_wallet = wallet; @@ -300,6 +308,7 @@ extern "C" return m_wallet; } + FUNCTION_VISABILITY_ATTRIBUTE bool create_wallet(char *path, char *password, char *language, int32_t networkType, char *error) { Monero::NetworkType _networkType = static_cast<Monero::NetworkType>(networkType); @@ -322,6 +331,7 @@ extern "C" return true; } + FUNCTION_VISABILITY_ATTRIBUTE bool restore_wallet_from_seed(char *path, char *password, char *seed, int32_t networkType, uint64_t restoreHeight, char *error) { Monero::NetworkType _networkType = static_cast<Monero::NetworkType>(networkType); @@ -347,6 +357,7 @@ extern "C" return true; } + FUNCTION_VISABILITY_ATTRIBUTE bool restore_wallet_from_keys(char *path, char *password, char *language, char *address, char *viewKey, char *spendKey, int32_t networkType, uint64_t restoreHeight, char *error) { Monero::NetworkType _networkType = static_cast<Monero::NetworkType>(networkType); @@ -375,6 +386,7 @@ extern "C" return true; } + FUNCTION_VISABILITY_ATTRIBUTE bool load_wallet(char *path, char *password, int32_t nettype) { nice(19); @@ -390,78 +402,91 @@ extern "C" return !(status != Monero::Wallet::Status_Ok || !errorString.empty()); } + FUNCTION_VISABILITY_ATTRIBUTE char *error_string() { return strdup(get_current_wallet()->errorString().c_str()); } - + FUNCTION_VISABILITY_ATTRIBUTE bool is_wallet_exist(char *path) { return Monero::WalletManagerFactory::getWalletManager()->walletExists(std::string(path)); } + FUNCTION_VISABILITY_ATTRIBUTE void close_current_wallet() { Monero::WalletManagerFactory::getWalletManager()->closeWallet(get_current_wallet()); change_current_wallet(nullptr); } + FUNCTION_VISABILITY_ATTRIBUTE char *get_filename() { return strdup(get_current_wallet()->filename().c_str()); } + FUNCTION_VISABILITY_ATTRIBUTE char *secret_view_key() { return strdup(get_current_wallet()->secretViewKey().c_str()); } + FUNCTION_VISABILITY_ATTRIBUTE char *public_view_key() { return strdup(get_current_wallet()->publicViewKey().c_str()); } + FUNCTION_VISABILITY_ATTRIBUTE char *secret_spend_key() { return strdup(get_current_wallet()->secretSpendKey().c_str()); } + FUNCTION_VISABILITY_ATTRIBUTE char *public_spend_key() { return strdup(get_current_wallet()->publicSpendKey().c_str()); } + FUNCTION_VISABILITY_ATTRIBUTE char *get_address(uint32_t account_index, uint32_t address_index) { return strdup(get_current_wallet()->address(account_index, address_index).c_str()); } - + FUNCTION_VISABILITY_ATTRIBUTE const char *seed() { return strdup(get_current_wallet()->seed().c_str()); } + FUNCTION_VISABILITY_ATTRIBUTE uint64_t get_full_balance(uint32_t account_index) { return get_current_wallet()->balance(account_index); } + FUNCTION_VISABILITY_ATTRIBUTE uint64_t get_unlocked_balance(uint32_t account_index) { return get_current_wallet()->unlockedBalance(account_index); } + FUNCTION_VISABILITY_ATTRIBUTE uint64_t get_current_height() { return get_current_wallet()->blockChainHeight(); } + FUNCTION_VISABILITY_ATTRIBUTE uint64_t get_node_height() { return get_current_wallet()->daemonBlockChainHeight(); } + FUNCTION_VISABILITY_ATTRIBUTE bool connect_to_node(char *error) { nice(19); @@ -475,6 +500,7 @@ extern "C" return is_connected; } + FUNCTION_VISABILITY_ATTRIBUTE bool setup_node(char *address, char *login, char *password, bool use_ssl, bool is_light_wallet, char *socksProxyAddress, char *error) { nice(19); @@ -495,9 +521,9 @@ extern "C" } if (socksProxyAddress != nullptr) - { - _socksProxyAddress = std::string(socksProxyAddress); - } + { + _socksProxyAddress = std::string(socksProxyAddress); + } bool inited = wallet->init(std::string(address), 0, _login, _password, use_ssl, is_light_wallet, _socksProxyAddress); @@ -511,27 +537,32 @@ extern "C" return inited; } + FUNCTION_VISABILITY_ATTRIBUTE bool is_connected() { return get_current_wallet()->connected(); } + FUNCTION_VISABILITY_ATTRIBUTE void start_refresh() { get_current_wallet()->refreshAsync(); get_current_wallet()->startRefresh(); } + FUNCTION_VISABILITY_ATTRIBUTE void set_refresh_from_block_height(uint64_t height) { get_current_wallet()->setRefreshFromBlockHeight(height); } + FUNCTION_VISABILITY_ATTRIBUTE void set_recovering_from_seed(bool is_recovery) { get_current_wallet()->setRecoveringFromSeed(is_recovery); } + FUNCTION_VISABILITY_ATTRIBUTE void store(char *path) { store_lock.lock(); @@ -545,6 +576,7 @@ extern "C" store_lock.unlock(); } + FUNCTION_VISABILITY_ATTRIBUTE bool set_password(char *password, Utf8Box &error) { bool is_changed = get_current_wallet()->setPassword(std::string(password)); @@ -555,6 +587,7 @@ extern "C" return is_changed; } + FUNCTION_VISABILITY_ATTRIBUTE bool transaction_create(char *address, char *payment_id, char *amount, uint8_t priority_raw, uint32_t subaddr_account, char **preferred_inputs, uint32_t preferred_inputs_size, @@ -604,6 +637,7 @@ extern "C" return true; } + FUNCTION_VISABILITY_ATTRIBUTE bool transaction_create_mult_dest(char **addresses, char *payment_id, char **amounts, uint32_t size, uint8_t priority_raw, uint32_t subaddr_account, char **preferred_inputs, uint32_t preferred_inputs_size, @@ -655,6 +689,7 @@ extern "C" return true; } + FUNCTION_VISABILITY_ATTRIBUTE bool transaction_commit(PendingTransactionRaw *transaction, Utf8Box &error) { bool committed = transaction->transaction->commit(); @@ -669,6 +704,7 @@ extern "C" return committed; } + FUNCTION_VISABILITY_ATTRIBUTE uint64_t get_node_height_or_update(uint64_t base_eight) { if (m_cached_syncing_blockchain_height < base_eight) { @@ -678,6 +714,7 @@ extern "C" return m_cached_syncing_blockchain_height; } + FUNCTION_VISABILITY_ATTRIBUTE uint64_t get_syncing_height() { if (m_listener == nullptr) { @@ -698,6 +735,7 @@ extern "C" return height; } + FUNCTION_VISABILITY_ATTRIBUTE uint64_t is_needed_to_refresh() { if (m_listener == nullptr) { @@ -713,6 +751,7 @@ extern "C" return should_refresh; } + FUNCTION_VISABILITY_ATTRIBUTE uint8_t is_new_transaction_exist() { if (m_listener == nullptr) { @@ -729,6 +768,7 @@ extern "C" return is_new_transaction_exist; } + FUNCTION_VISABILITY_ATTRIBUTE void set_listener() { m_last_known_wallet_height = 0; @@ -742,6 +782,7 @@ extern "C" get_current_wallet()->setListener(m_listener); } + FUNCTION_VISABILITY_ATTRIBUTE int64_t *subaddrress_get_all() { std::vector<Monero::SubaddressRow *> _subaddresses = m_subaddress->getAll(); @@ -758,33 +799,39 @@ extern "C" return subaddresses; } + FUNCTION_VISABILITY_ATTRIBUTE int32_t subaddrress_size() { std::vector<Monero::SubaddressRow *> _subaddresses = m_subaddress->getAll(); return _subaddresses.size(); } + FUNCTION_VISABILITY_ATTRIBUTE void subaddress_add_row(uint32_t accountIndex, char *label) { m_subaddress->addRow(accountIndex, std::string(label)); } + FUNCTION_VISABILITY_ATTRIBUTE void subaddress_set_label(uint32_t accountIndex, uint32_t addressIndex, char *label) { m_subaddress->setLabel(accountIndex, addressIndex, std::string(label)); } + FUNCTION_VISABILITY_ATTRIBUTE void subaddress_refresh(uint32_t accountIndex) { m_subaddress->refresh(accountIndex); } + FUNCTION_VISABILITY_ATTRIBUTE int32_t account_size() { std::vector<Monero::SubaddressAccountRow *> _accocunts = m_account->getAll(); return _accocunts.size(); } + FUNCTION_VISABILITY_ATTRIBUTE int64_t *account_get_all() { std::vector<Monero::SubaddressAccountRow *> _accocunts = m_account->getAll(); @@ -801,21 +848,25 @@ extern "C" return accocunts; } + FUNCTION_VISABILITY_ATTRIBUTE void account_add_row(char *label) { m_account->addRow(std::string(label)); } + FUNCTION_VISABILITY_ATTRIBUTE void account_set_label_row(uint32_t account_index, char *label) { m_account->setLabel(account_index, label); } + FUNCTION_VISABILITY_ATTRIBUTE void account_refresh() { m_account->refresh(); } + FUNCTION_VISABILITY_ATTRIBUTE int64_t *transactions_get_all() { std::vector<Monero::TransactionInfo *> transactions = m_transaction_history->getAll(); @@ -832,16 +883,19 @@ extern "C" return transactionAddresses; } + FUNCTION_VISABILITY_ATTRIBUTE void transactions_refresh() { m_transaction_history->refresh(); } + FUNCTION_VISABILITY_ATTRIBUTE int64_t transactions_count() { return m_transaction_history->count(); } + FUNCTION_VISABILITY_ATTRIBUTE int LedgerExchange( unsigned char *command, unsigned int cmd_len, @@ -851,37 +905,44 @@ extern "C" return -1; } + FUNCTION_VISABILITY_ATTRIBUTE int LedgerFind(char *buffer, size_t len) { return -1; } + FUNCTION_VISABILITY_ATTRIBUTE void on_startup() { Monero::Utils::onStartup(); Monero::WalletManagerFactory::setLogLevel(0); } + FUNCTION_VISABILITY_ATTRIBUTE void rescan_blockchain() { m_wallet->rescanBlockchainAsync(); } + FUNCTION_VISABILITY_ATTRIBUTE char * get_tx_key(char * txId) { return strdup(m_wallet->getTxKey(std::string(txId)).c_str()); } + FUNCTION_VISABILITY_ATTRIBUTE char *get_subaddress_label(uint32_t accountIndex, uint32_t addressIndex) { return strdup(get_current_wallet()->getSubaddressLabel(accountIndex, addressIndex).c_str()); } + FUNCTION_VISABILITY_ATTRIBUTE void set_trusted_daemon(bool arg) { m_wallet->setTrustedDaemon(arg); } + FUNCTION_VISABILITY_ATTRIBUTE bool trusted_daemon() { return m_wallet->trustedDaemon(); diff --git a/cw_monero/lib/api/monero_api.dart b/cw_monero/lib/api/monero_api.dart index 398d737d1..3b502ac16 100644 --- a/cw_monero/lib/api/monero_api.dart +++ b/cw_monero/lib/api/monero_api.dart @@ -3,4 +3,4 @@ import 'dart:io'; final DynamicLibrary moneroApi = Platform.isAndroid ? DynamicLibrary.open("libcw_monero.so") - : DynamicLibrary.open("cw_monero.framework/cw_monero"); \ No newline at end of file + : DynamicLibrary.process(); diff --git a/cw_monero/lib/monero_subaddress_list.dart b/cw_monero/lib/monero_subaddress_list.dart index 53efc1251..0dcddc679 100644 --- a/cw_monero/lib/monero_subaddress_list.dart +++ b/cw_monero/lib/monero_subaddress_list.dart @@ -22,7 +22,7 @@ abstract class MoneroSubaddressListBase with Store { bool _isUpdating; void update({required int accountIndex}) { - refreshCoins(accountIndex); + // refreshCoins(accountIndex); if (_isUpdating) { return; diff --git a/cw_monero/lib/monero_wallet.dart b/cw_monero/lib/monero_wallet.dart index 76563310e..12381af44 100644 --- a/cw_monero/lib/monero_wallet.dart +++ b/cw_monero/lib/monero_wallet.dart @@ -39,8 +39,10 @@ class MoneroWallet = MoneroWalletBase with _$MoneroWallet; abstract class MoneroWalletBase extends WalletBase<MoneroBalance, MoneroTransactionHistory, MoneroTransactionInfo> with Store { - MoneroWalletBase({required WalletInfo walletInfo, - required Box<UnspentCoinsInfo> unspentCoinsInfo}) + MoneroWalletBase({ + required WalletInfo walletInfo, + required Box<UnspentCoinsInfo> unspentCoinsInfo, + required String password}) : balance = ObservableMap<CryptoCurrency, MoneroBalance>.of({ CryptoCurrency.xmr: MoneroBalance( fullBalance: monero_wallet.getFullBalance(accountIndex: 0), @@ -48,6 +50,7 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance, }), _isTransactionUpdating = false, _hasSyncAfterStartup = false, + _password = password, walletAddresses = MoneroWalletAddresses(walletInfo), syncStatus = NotConnectedSyncStatus(), unspentCoins = [], @@ -86,6 +89,9 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance, @override String get seed => monero_wallet.getSeed(); + @override + String get password => _password; + @override MoneroWalletKeys get keys => MoneroWalletKeys( privateSpendKey: monero_wallet.getSecretSpendKey(), @@ -99,6 +105,7 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance, bool _hasSyncAfterStartup; Timer? _autoSaveTimer; List<MoneroUnspent> unspentCoins; + String _password; Future<void> init() async { await walletAddresses.init(); @@ -352,6 +359,7 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance, @override Future<void> changePassword(String password) async { monero_wallet.setPasswordSync(password); + _password = password; } Future<int> getNodeHeight() async => monero_wallet.getNodeHeight(); @@ -378,7 +386,7 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance, } Future<void> updateUnspent() async { - refreshCoins(walletAddresses.account!.id); + // refreshCoins(walletAddresses.account!.id); unspentCoins.clear(); diff --git a/cw_monero/lib/monero_wallet_service.dart b/cw_monero/lib/monero_wallet_service.dart index 90c63fb72..181bc7c66 100644 --- a/cw_monero/lib/monero_wallet_service.dart +++ b/cw_monero/lib/monero_wallet_service.dart @@ -73,7 +73,9 @@ class MoneroWalletService extends WalletService< password: credentials.password!, language: credentials.language); final wallet = MoneroWallet( - walletInfo: credentials.walletInfo!, unspentCoinsInfo: unspentCoinsInfoSource); + + walletInfo: credentials.walletInfo!, + password: credentials.password!, unspentCoinsInfo: unspentCoinsInfoSource); await wallet.init(); return wallet; @@ -109,7 +111,9 @@ class MoneroWalletService extends WalletService< .openWalletAsync({'path': path, 'password': password}); final walletInfo = walletInfoSource.values.firstWhere( (info) => info.id == WalletBase.idFor(name, getType())); - final wallet = MoneroWallet(walletInfo: walletInfo, unspentCoinsInfo: unspentCoinsInfoSource); + final wallet = MoneroWallet( + walletInfo: walletInfo, unspentCoinsInfo: unspentCoinsInfoSource, + password: password); final isValid = wallet.walletAddresses.validate(); if (!isValid) { @@ -160,7 +164,7 @@ class MoneroWalletService extends WalletService< final currentWalletInfo = walletInfoSource.values.firstWhere( (info) => info.id == WalletBase.idFor(currentName, getType())); final currentWallet = - MoneroWallet(walletInfo: currentWalletInfo, unspentCoinsInfo: unspentCoinsInfoSource); + MoneroWallet(walletInfo: currentWalletInfo, unspentCoinsInfo: unspentCoinsInfoSource, password: password); await currentWallet.renameWalletFiles(newName); @@ -185,7 +189,9 @@ class MoneroWalletService extends WalletService< viewKey: credentials.viewKey, spendKey: credentials.spendKey); final wallet = MoneroWallet( - walletInfo: credentials.walletInfo!, unspentCoinsInfo: unspentCoinsInfoSource); + + walletInfo: credentials.walletInfo!, + password: credentials.password!, unspentCoinsInfo: unspentCoinsInfoSource); await wallet.init(); return wallet; @@ -207,7 +213,9 @@ class MoneroWalletService extends WalletService< seed: credentials.mnemonic, restoreHeight: credentials.height!); final wallet = MoneroWallet( - walletInfo: credentials.walletInfo!, unspentCoinsInfo: unspentCoinsInfoSource); + + walletInfo: credentials.walletInfo!, + password: credentials.password!, unspentCoinsInfo: unspentCoinsInfoSource); await wallet.init(); return wallet; diff --git a/cw_monero/linux/CMakeLists.txt b/cw_monero/linux/CMakeLists.txt new file mode 100644 index 000000000..ba685269d --- /dev/null +++ b/cw_monero/linux/CMakeLists.txt @@ -0,0 +1,270 @@ +# The Flutter tooling requires that developers have CMake 3.10 or later +# installed. You should not increase this version, as doing so will cause +# the plugin to fail to compile for some customers of the plugin. +cmake_minimum_required(VERSION 3.10) + +# Project-level configuration. +set(PROJECT_NAME "cw_monero") +project(${PROJECT_NAME} LANGUAGES CXX) + +# This value is used when generating builds using this plugin, so it must +# not be changed. +set(PLUGIN_NAME "cw_monero_plugin") + +# Define the plugin library target. Its name must not be changed (see comment +# on PLUGIN_NAME above). +# +# Any new source files that you add to the plugin should be added here. +add_library(${PLUGIN_NAME} SHARED + "cw_monero_plugin.cc" + "../ios/Classes/monero_api.cpp" +) + +# Apply a standard set of build settings that are configured in the +# application-level CMakeLists.txt. This can be removed for plugins that want +# full control over build settings. +apply_standard_settings(${PLUGIN_NAME}) + +# Symbols are hidden by default to reduce the chance of accidental conflicts +# between plugins. This should not be removed; any symbols that should be +# exported should be explicitly exported with the FLUTTER_PLUGIN_EXPORT macro. +set_target_properties(${PLUGIN_NAME} PROPERTIES + CXX_VISIBILITY_PRESET hidden) +target_compile_definitions(${PLUGIN_NAME} PRIVATE FLUTTER_PLUGIN_IMPL) + +# Source include directories and library dependencies. Add any plugin-specific +# dependencies here. +target_include_directories(${PLUGIN_NAME} INTERFACE + "${CMAKE_CURRENT_SOURCE_DIR}/include") +target_link_libraries(${PLUGIN_NAME} PRIVATE flutter) +target_link_libraries(${PLUGIN_NAME} PRIVATE PkgConfig::GTK) +target_link_libraries(${PLUGIN_NAME} PUBLIC cw_monero) +# List of absolute paths to libraries that should be bundled with the plugin. +# This list could contain prebuilt libraries, or libraries created by an +# external build triggered from this build file. +set(cw_monero_bundled_libraries + "" + PARENT_SCOPE +) + +add_library( cw_monero + STATIC + ../ios/Classes/monero_api.cpp) + +set(EXTERNAL_LIBS_DIR ${CMAKE_SOURCE_DIR}/../cw_shared_external/ios/External/linux) + +############ +# libsodium +############ + +add_library(sodium STATIC IMPORTED) +set_target_properties(sodium PROPERTIES IMPORTED_LOCATION + ${EXTERNAL_LIBS_DIR}/lib/libsodium.a) + +############ +# OpenSSL +############ + +add_library(crypto STATIC IMPORTED) +set_target_properties(crypto PROPERTIES IMPORTED_LOCATION + ${EXTERNAL_LIBS_DIR}/lib/libcrypto.a) + +add_library(ssl STATIC IMPORTED) +set_target_properties(ssl PROPERTIES IMPORTED_LOCATION + ${EXTERNAL_LIBS_DIR}/lib/libssl.a) + +############ +# Boost +############ + +add_library(boost_chrono STATIC IMPORTED) +set_target_properties(boost_chrono PROPERTIES IMPORTED_LOCATION + ${EXTERNAL_LIBS_DIR}/lib/libboost_chrono.a) + +add_library(boost_date_time STATIC IMPORTED) +set_target_properties(boost_date_time PROPERTIES IMPORTED_LOCATION + ${EXTERNAL_LIBS_DIR}/lib/libboost_date_time.a) + +add_library(boost_filesystem STATIC IMPORTED) +set_target_properties(boost_filesystem PROPERTIES IMPORTED_LOCATION + ${EXTERNAL_LIBS_DIR}/lib/libboost_filesystem.a) + +add_library(boost_program_options STATIC IMPORTED) +set_target_properties(boost_program_options PROPERTIES IMPORTED_LOCATION + ${EXTERNAL_LIBS_DIR}/lib/libboost_program_options.a) + +add_library(boost_regex STATIC IMPORTED) +set_target_properties(boost_regex PROPERTIES IMPORTED_LOCATION + ${EXTERNAL_LIBS_DIR}/lib/libboost_regex.a) + +add_library(boost_serialization STATIC IMPORTED) +set_target_properties(boost_serialization PROPERTIES IMPORTED_LOCATION + ${EXTERNAL_LIBS_DIR}/lib/libboost_serialization.a) + +add_library(boost_system STATIC IMPORTED) +set_target_properties(boost_system PROPERTIES IMPORTED_LOCATION + ${EXTERNAL_LIBS_DIR}/lib/libboost_system.a) + +add_library(boost_thread STATIC IMPORTED) +set_target_properties(boost_thread PROPERTIES IMPORTED_LOCATION + ${EXTERNAL_LIBS_DIR}/lib/libboost_thread.a) + +add_library(boost_wserialization STATIC IMPORTED) +set_target_properties(boost_wserialization PROPERTIES IMPORTED_LOCATION + ${EXTERNAL_LIBS_DIR}/lib/libboost_wserialization.a) + +############# +# Monero +############# + + +add_library(wallet STATIC IMPORTED) +set_target_properties(wallet PROPERTIES IMPORTED_LOCATION + ${EXTERNAL_LIBS_DIR}/lib/monero/libwallet.a) + +add_library(wallet_api STATIC IMPORTED) +set_target_properties(wallet_api PROPERTIES IMPORTED_LOCATION + ${EXTERNAL_LIBS_DIR}/lib/monero/libwallet_api.a) + +add_library(cryptonote_core STATIC IMPORTED) +set_target_properties(cryptonote_core PROPERTIES IMPORTED_LOCATION + ${EXTERNAL_LIBS_DIR}/lib/monero/libcryptonote_core.a) + +add_library(cryptonote_basic STATIC IMPORTED) +set_target_properties(cryptonote_basic PROPERTIES IMPORTED_LOCATION + ${EXTERNAL_LIBS_DIR}/lib/monero/libcryptonote_basic.a) + +add_library(cryptonote_format_utils_basic STATIC IMPORTED) +set_target_properties(cryptonote_format_utils_basic PROPERTIES IMPORTED_LOCATION + ${EXTERNAL_LIBS_DIR}/lib/monero/libcryptonote_format_utils_basic.a) + +add_library(mnemonics STATIC IMPORTED) +set_target_properties(mnemonics PROPERTIES IMPORTED_LOCATION + ${EXTERNAL_LIBS_DIR}/lib/monero/libmnemonics.a) + +add_library(common STATIC IMPORTED) +set_target_properties(common PROPERTIES IMPORTED_LOCATION + ${EXTERNAL_LIBS_DIR}/lib/monero/libcommon.a) + +add_library(cncrypto STATIC IMPORTED) +set_target_properties(cncrypto PROPERTIES IMPORTED_LOCATION + ${EXTERNAL_LIBS_DIR}/lib/monero/libcncrypto.a) + +add_library(ringct STATIC IMPORTED) +set_target_properties(ringct PROPERTIES IMPORTED_LOCATION + ${EXTERNAL_LIBS_DIR}/lib/monero/libringct.a) + +add_library(ringct_basic STATIC IMPORTED) +set_target_properties(ringct_basic PROPERTIES IMPORTED_LOCATION + ${EXTERNAL_LIBS_DIR}/lib/monero/libringct_basic.a) + +add_library(blockchain_db STATIC IMPORTED) +set_target_properties(blockchain_db PROPERTIES IMPORTED_LOCATION + ${EXTERNAL_LIBS_DIR}/lib/monero/libblockchain_db.a) + +add_library(lmdb STATIC IMPORTED) +set_target_properties(lmdb PROPERTIES IMPORTED_LOCATION + ${EXTERNAL_LIBS_DIR}/lib/monero/liblmdb.a) + +add_library(easylogging STATIC IMPORTED) +set_target_properties(easylogging PROPERTIES IMPORTED_LOCATION + ${EXTERNAL_LIBS_DIR}/lib/monero/libeasylogging.a) + +add_library(epee STATIC IMPORTED) +set_target_properties(epee PROPERTIES IMPORTED_LOCATION + ${EXTERNAL_LIBS_DIR}/lib/monero/libepee.a) + +add_library(blocks STATIC IMPORTED) +set_target_properties(blocks PROPERTIES IMPORTED_LOCATION + ${EXTERNAL_LIBS_DIR}/lib/monero/libblocks.a) + +add_library(checkpoints STATIC IMPORTED) +set_target_properties(checkpoints PROPERTIES IMPORTED_LOCATION + ${EXTERNAL_LIBS_DIR}/lib/monero/libcheckpoints.a) + +add_library(device STATIC IMPORTED) +set_target_properties(device PROPERTIES IMPORTED_LOCATION + ${EXTERNAL_LIBS_DIR}/lib/monero/libdevice.a) + +add_library(device_trezor STATIC IMPORTED) +set_target_properties(device_trezor PROPERTIES IMPORTED_LOCATION + ${EXTERNAL_LIBS_DIR}/lib/monero/libdevice_trezor.a) + +add_library(multisig STATIC IMPORTED) +set_target_properties(multisig PROPERTIES IMPORTED_LOCATION + ${EXTERNAL_LIBS_DIR}/lib/monero/libmultisig.a) + +add_library(version STATIC IMPORTED) +set_target_properties(version PROPERTIES IMPORTED_LOCATION + ${EXTERNAL_LIBS_DIR}/lib/monero/libversion.a) + +add_library(net STATIC IMPORTED) +set_target_properties(net PROPERTIES IMPORTED_LOCATION + ${EXTERNAL_LIBS_DIR}/lib/monero/libnet.a) + +add_library(hardforks STATIC IMPORTED) +set_target_properties(hardforks PROPERTIES IMPORTED_LOCATION + ${EXTERNAL_LIBS_DIR}/lib/monero/libhardforks.a) + +add_library(randomx STATIC IMPORTED) +set_target_properties(randomx PROPERTIES IMPORTED_LOCATION + ${EXTERNAL_LIBS_DIR}/lib/monero/librandomx.a) + +add_library(rpc_base STATIC IMPORTED) +set_target_properties(rpc_base PROPERTIES IMPORTED_LOCATION + ${EXTERNAL_LIBS_DIR}/lib/monero/librpc_base.a) + +add_library(wallet-crypto STATIC IMPORTED) +set_target_properties(wallet-crypto PROPERTIES IMPORTED_LOCATION + ${EXTERNAL_LIBS_DIR}/lib/monero/libwallet-crypto.a) + +add_library(unbound STATIC IMPORTED) +set_target_properties(unbound PROPERTIES IMPORTED_LOCATION + ${EXTERNAL_LIBS_DIR}/lib/libunbound.a) + +include_directories( ${EXTERNAL_LIBS_DIR}/include ) + +target_link_libraries( cw_monero + + wallet_api + wallet + cryptonote_core + cryptonote_basic + cryptonote_format_utils_basic + mnemonics + ringct + ringct_basic + net + common + cncrypto + blockchain_db + lmdb + easylogging + unbound + epee + blocks + checkpoints + device + device_trezor + multisig + version + randomx + hardforks + rpc_base + wallet-crypto + + boost_chrono + boost_date_time + boost_filesystem + boost_program_options + boost_regex + boost_serialization + boost_system + boost_thread + boost_wserialization + + ssl + crypto + + sodium + ) diff --git a/cw_monero/linux/cw_monero_plugin.cc b/cw_monero/linux/cw_monero_plugin.cc new file mode 100644 index 000000000..ca8524c9e --- /dev/null +++ b/cw_monero/linux/cw_monero_plugin.cc @@ -0,0 +1,70 @@ +#include "include/cw_monero/cw_monero_plugin.h" + +#include <flutter_linux/flutter_linux.h> +#include <gtk/gtk.h> +#include <sys/utsname.h> + +#include <cstring> + +#define CW_MONERO_PLUGIN(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj), cw_monero_plugin_get_type(), \ + CwMoneroPlugin)) + +struct _CwMoneroPlugin { + GObject parent_instance; +}; + +G_DEFINE_TYPE(CwMoneroPlugin, cw_monero_plugin, g_object_get_type()) + +// Called when a method call is received from Flutter. +static void cw_monero_plugin_handle_method_call( + CwMoneroPlugin* self, + FlMethodCall* method_call) { + g_autoptr(FlMethodResponse) response = nullptr; + + const gchar* method = fl_method_call_get_name(method_call); + + if (strcmp(method, "getPlatformVersion") == 0) { + struct utsname uname_data = {}; + uname(&uname_data); + g_autofree gchar *version = g_strdup_printf("Linux %s", uname_data.version); + g_autoptr(FlValue) result = fl_value_new_string(version); + response = FL_METHOD_RESPONSE(fl_method_success_response_new(result)); + } else { + response = FL_METHOD_RESPONSE(fl_method_not_implemented_response_new()); + } + + fl_method_call_respond(method_call, response, nullptr); +} + +static void cw_monero_plugin_dispose(GObject* object) { + G_OBJECT_CLASS(cw_monero_plugin_parent_class)->dispose(object); +} + +static void cw_monero_plugin_class_init(CwMoneroPluginClass* klass) { + G_OBJECT_CLASS(klass)->dispose = cw_monero_plugin_dispose; +} + +static void cw_monero_plugin_init(CwMoneroPlugin* self) {} + +static void method_call_cb(FlMethodChannel* channel, FlMethodCall* method_call, + gpointer user_data) { + CwMoneroPlugin* plugin = CW_MONERO_PLUGIN(user_data); + cw_monero_plugin_handle_method_call(plugin, method_call); +} + +void cw_monero_plugin_register_with_registrar(FlPluginRegistrar* registrar) { + CwMoneroPlugin* plugin = CW_MONERO_PLUGIN( + g_object_new(cw_monero_plugin_get_type(), nullptr)); + + g_autoptr(FlStandardMethodCodec) codec = fl_standard_method_codec_new(); + g_autoptr(FlMethodChannel) channel = + fl_method_channel_new(fl_plugin_registrar_get_messenger(registrar), + "cw_monero", + FL_METHOD_CODEC(codec)); + fl_method_channel_set_method_call_handler(channel, method_call_cb, + g_object_ref(plugin), + g_object_unref); + + g_object_unref(plugin); +} diff --git a/cw_monero/linux/include/cw_monero/cw_monero_plugin.h b/cw_monero/linux/include/cw_monero/cw_monero_plugin.h new file mode 100644 index 000000000..387903ff6 --- /dev/null +++ b/cw_monero/linux/include/cw_monero/cw_monero_plugin.h @@ -0,0 +1,26 @@ +#ifndef FLUTTER_PLUGIN_CW_MONERO_PLUGIN_H_ +#define FLUTTER_PLUGIN_CW_MONERO_PLUGIN_H_ + +#include <flutter_linux/flutter_linux.h> + +G_BEGIN_DECLS + +#ifdef FLUTTER_PLUGIN_IMPL +#define FLUTTER_PLUGIN_EXPORT __attribute__((visibility("default"))) +#else +#define FLUTTER_PLUGIN_EXPORT +#endif + +typedef struct _CwMoneroPlugin CwMoneroPlugin; +typedef struct { + GObjectClass parent_class; +} CwMoneroPluginClass; + +FLUTTER_PLUGIN_EXPORT GType cw_monero_plugin_get_type(); + +FLUTTER_PLUGIN_EXPORT void cw_monero_plugin_register_with_registrar( + FlPluginRegistrar* registrar); + +G_END_DECLS + +#endif // FLUTTER_PLUGIN_CW_MONERO_PLUGIN_H_ diff --git a/cw_monero/macos/Classes/monero_api.cpp b/cw_monero/macos/Classes/monero_api.cpp index 56548e79e..e1cbb5b59 100644 --- a/cw_monero/macos/Classes/monero_api.cpp +++ b/cw_monero/macos/Classes/monero_api.cpp @@ -20,8 +20,6 @@ using namespace std::chrono_literals; extern "C" { #endif - const uint64_t MONERO_BLOCK_SIZE = 1000; - struct Utf8Box { char *value; @@ -149,7 +147,8 @@ extern "C" fee = transaction->fee(); blockHeight = transaction->blockHeight(); subaddrAccount = transaction->subaddrAccount(); - std::set<uint32_t>::iterator it = transaction->subaddrIndex().begin(); + std::set<uint32_t> subIndex = transaction->subaddrIndex(); + std::set<uint32_t>::iterator it = subIndex.begin(); subaddrIndex = *it; confirmations = transaction->confirmations(); datetime = static_cast<int64_t>(transaction->timestamp()); diff --git a/cw_monero/pubspec.yaml b/cw_monero/pubspec.yaml index cf2993ef3..cdc6245b0 100644 --- a/cw_monero/pubspec.yaml +++ b/cw_monero/pubspec.yaml @@ -46,9 +46,10 @@ flutter: pluginClass: CwMoneroPlugin ios: pluginClass: CwMoneroPlugin - macos: pluginClass: CwMoneroPlugin + linux: + pluginClass: CwMoneroPlugin # To add assets to your plugin package, add an assets section, like this: # assets: diff --git a/ios/Podfile b/ios/Podfile index 027d48ceb..b29d40484 100644 --- a/ios/Podfile +++ b/ios/Podfile @@ -44,7 +44,6 @@ post_install do |installer| flutter_additional_ios_build_settings(target) target.build_configurations.each do |config| - config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '11.0' config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] ||= [ '$(inherited)', diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index 4bc10f9be..424adbfec 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -10,8 +10,8 @@ 0C44A71A2518EF8000B570ED /* decrypt.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C44A7192518EF8000B570ED /* decrypt.swift */; }; 0C9D68C9264854B60011B691 /* secRandom.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C9D68C8264854B60011B691 /* secRandom.swift */; }; 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; + 20ED0868E1BD7E12278C0CB3 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B26E3F56D69167FBB1DC160A /* Pods_Runner.framework */; }; 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; - 4DFD1BB54A3A50573E19A583 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3C663361C56EBB242598F609 /* Pods_Runner.framework */; }; 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; @@ -23,13 +23,13 @@ 0C44A7192518EF8000B570ED /* decrypt.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = decrypt.swift; sourceTree = "<group>"; }; 0C9986A3251A932F00D566FD /* CryptoSwift.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = CryptoSwift.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 0C9D68C8264854B60011B691 /* secRandom.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = secRandom.swift; sourceTree = "<group>"; }; - 11F9FC13F9EE2A705B213FA9 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = "<group>"; }; 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; }; 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; }; - 1F083F2041D1F553F2AF8B62 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = "<group>"; }; + 20F67A1B2C2FCB2A3BB048C1 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = "<group>"; }; 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; }; - 3C663361C56EBB242598F609 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 501EA9286675DC8636978EA4 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = "<group>"; }; 5AFFEBFC279AD49C00F906A4 /* wakeLock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = wakeLock.swift; sourceTree = "<group>"; }; + 61CAA8652B54F23356F7592A /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = "<group>"; }; 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = "<group>"; }; 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; }; @@ -40,7 +40,7 @@ 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; }; 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; }; 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; }; - AD0937B0140D5A4C24E73BEA /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = "<group>"; }; + B26E3F56D69167FBB1DC160A /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -48,7 +48,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 4DFD1BB54A3A50573E19A583 /* Pods_Runner.framework in Frameworks */, + 20ED0868E1BD7E12278C0CB3 /* Pods_Runner.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -59,7 +59,7 @@ isa = PBXGroup; children = ( 0C9986A3251A932F00D566FD /* CryptoSwift.framework */, - 3C663361C56EBB242598F609 /* Pods_Runner.framework */, + B26E3F56D69167FBB1DC160A /* Pods_Runner.framework */, ); name = Frameworks; sourceTree = "<group>"; @@ -77,9 +77,9 @@ 84389F1A05D5860790D82820 /* Pods */ = { isa = PBXGroup; children = ( - 11F9FC13F9EE2A705B213FA9 /* Pods-Runner.debug.xcconfig */, - 1F083F2041D1F553F2AF8B62 /* Pods-Runner.release.xcconfig */, - AD0937B0140D5A4C24E73BEA /* Pods-Runner.profile.xcconfig */, + 20F67A1B2C2FCB2A3BB048C1 /* Pods-Runner.debug.xcconfig */, + 501EA9286675DC8636978EA4 /* Pods-Runner.release.xcconfig */, + 61CAA8652B54F23356F7592A /* Pods-Runner.profile.xcconfig */, ); path = Pods; sourceTree = "<group>"; @@ -138,13 +138,13 @@ isa = PBXNativeTarget; buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; buildPhases = ( - B91154210ADCED81FBF06A85 /* [CP] Check Pods Manifest.lock */, + 0843B0813AFBAF53935AD24E /* [CP] Check Pods Manifest.lock */, 9740EEB61CF901F6004384FC /* Run Script */, 97C146EA1CF9000F007C117D /* Sources */, 97C146EB1CF9000F007C117D /* Frameworks */, 97C146EC1CF9000F007C117D /* Resources */, 3B06AD1E1E4923F5004D2608 /* Thin Binary */, - 32D0076A9969C0C38D68AF62 /* [CP] Embed Pods Frameworks */, + DD8DB3179CA4E511F9954A6F /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); @@ -203,21 +203,26 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ - 32D0076A9969C0C38D68AF62 /* [CP] Embed Pods Frameworks */ = { + 0843B0813AFBAF53935AD24E /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", ); - name = "[CP] Embed Pods Frameworks"; + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; outputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { @@ -250,26 +255,21 @@ shellPath = /bin/sh; shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build\n"; }; - B91154210ADCED81FBF06A85 /* [CP] Check Pods Manifest.lock */ = { + DD8DB3179CA4E511F9954A6F /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", ); - inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; + name = "[CP] Embed Pods Frameworks"; outputFileListPaths = ( - ); - outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; /* End PBXShellScriptBuildPhase section */ @@ -390,7 +390,7 @@ PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; + TARGETED_DEVICE_FAMILY = 1; VALID_ARCHS = arm64; VERSIONING_SYSTEM = "apple-generic"; }; @@ -537,7 +537,7 @@ SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; + TARGETED_DEVICE_FAMILY = 1; VALID_ARCHS = arm64; VERSIONING_SYSTEM = "apple-generic"; }; @@ -574,7 +574,7 @@ PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; + TARGETED_DEVICE_FAMILY = 1; VALID_ARCHS = arm64; VERSIONING_SYSTEM = "apple-generic"; }; diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json index df5cf1cc5..6d834d4ee 100644 --- a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json +++ b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -1,25 +1,21 @@ { "images" : [ { - "filename" : "Icon-App-40x40@1x.png", "idiom" : "iphone", "scale" : "2x", "size" : "20x20" }, { - "filename" : "Icon-App-20x20@3x.png", "idiom" : "iphone", "scale" : "3x", "size" : "20x20" }, { - "filename" : "Icon-App-29x29@2x 1.png", "idiom" : "iphone", "scale" : "2x", "size" : "29x29" }, { - "filename" : "Icon-App-29x29@3x.png", "idiom" : "iphone", "scale" : "3x", "size" : "29x29" @@ -30,7 +26,6 @@ "size" : "40x40" }, { - "filename" : "Icon-App-40x40@3x.png", "idiom" : "iphone", "scale" : "3x", "size" : "40x40" @@ -48,31 +43,26 @@ "size" : "60x60" }, { - "filename" : "Icon-App-20x20@1x.png", "idiom" : "ipad", "scale" : "1x", "size" : "20x20" }, { - "filename" : "Icon-App-20x20@2x.png", "idiom" : "ipad", "scale" : "2x", "size" : "20x20" }, { - "filename" : "Icon-App-29x29@1x.png", "idiom" : "ipad", "scale" : "1x", "size" : "29x29" }, { - "filename" : "Icon-App-29x29@2x.png", "idiom" : "ipad", "scale" : "2x", "size" : "29x29" }, { - "filename" : "Icon-App-40x40@1x 1.png", "idiom" : "ipad", "scale" : "1x", "size" : "40x40" @@ -83,19 +73,16 @@ "size" : "40x40" }, { - "filename" : "Icon-App-76x76@1x.png", "idiom" : "ipad", "scale" : "1x", "size" : "76x76" }, { - "filename" : "Icon-App-76x76@2x.png", "idiom" : "ipad", "scale" : "2x", "size" : "76x76" }, { - "filename" : "Icon-App-83.5x83.5@2x.png", "idiom" : "ipad", "scale" : "2x", "size" : "83.5x83.5" diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png deleted file mode 100644 index 369d8d9a4..000000000 Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png and /dev/null differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png deleted file mode 100644 index 65ed7f3db..000000000 Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png and /dev/null differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png deleted file mode 100644 index fb14bfc55..000000000 Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png and /dev/null differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png deleted file mode 100644 index d24d594a3..000000000 Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png and /dev/null differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x 1.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x 1.png deleted file mode 100644 index 07acd0a82..000000000 Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x 1.png and /dev/null differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png deleted file mode 100644 index 07acd0a82..000000000 Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png and /dev/null differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png deleted file mode 100644 index bdc20091d..000000000 Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png and /dev/null differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x 1.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x 1.png deleted file mode 100644 index 65ed7f3db..000000000 Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x 1.png and /dev/null differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png deleted file mode 100644 index 65ed7f3db..000000000 Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png and /dev/null differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png deleted file mode 100644 index 80e78be41..000000000 Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png and /dev/null differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png deleted file mode 100644 index e06998b67..000000000 Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png and /dev/null differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png deleted file mode 100644 index 78a2ccfb1..000000000 Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png and /dev/null differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png deleted file mode 100644 index 0ba8d647c..000000000 Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png and /dev/null differ diff --git a/lib/anonpay/anonpay_invoice_info.dart b/lib/anonpay/anonpay_invoice_info.dart index bd6776d00..89613224e 100644 --- a/lib/anonpay/anonpay_invoice_info.dart +++ b/lib/anonpay/anonpay_invoice_info.dart @@ -1,5 +1,4 @@ import 'package:cake_wallet/anonpay/anonpay_info_base.dart'; -import 'package:cw_core/hive_type_ids.dart'; import 'package:cw_core/keyable.dart'; import 'package:hive/hive.dart'; @@ -36,7 +35,7 @@ class AnonpayInvoiceInfo extends HiveObject with Keyable implements AnonpayInfoB @HiveField(13) final String provider; - static const typeId = ANONPAY_INVOICE_INFO_TYPE_ID; + static const typeId = 10; static const boxName = 'AnonpayInvoiceInfo'; AnonpayInvoiceInfo({ diff --git a/lib/bitcoin/cw_bitcoin.dart b/lib/bitcoin/cw_bitcoin.dart index 3164aacaa..f03e47460 100644 --- a/lib/bitcoin/cw_bitcoin.dart +++ b/lib/bitcoin/cw_bitcoin.dart @@ -22,8 +22,9 @@ class CWBitcoin extends Bitcoin { @override WalletCredentials createBitcoinNewWalletCredentials({ required String name, - WalletInfo? walletInfo}) - => BitcoinNewWalletCredentials(name: name, walletInfo: walletInfo); + WalletInfo? walletInfo, + String? password}) + => BitcoinNewWalletCredentials(name: name, walletInfo: walletInfo, password: password); @override List<String> getWordList() => wordlist; @@ -138,12 +139,12 @@ class CWBitcoin extends Bitcoin { await bitcoinWallet.updateUnspent(); } - WalletService createBitcoinWalletService(Box<WalletInfo> walletInfoSource, Box<UnspentCoinsInfo> unspentCoinSource) { - return BitcoinWalletService(walletInfoSource, unspentCoinSource); + WalletService createBitcoinWalletService(Box<WalletInfo> walletInfoSource, Box<UnspentCoinsInfo> unspentCoinSource, bool isDirect) { + return BitcoinWalletService(walletInfoSource, unspentCoinSource, isDirect); } - WalletService createLitecoinWalletService(Box<WalletInfo> walletInfoSource, Box<UnspentCoinsInfo> unspentCoinSource) { - return LitecoinWalletService(walletInfoSource, unspentCoinSource); + WalletService createLitecoinWalletService(Box<WalletInfo> walletInfoSource, Box<UnspentCoinsInfo> unspentCoinSource, bool isDirect) { + return LitecoinWalletService(walletInfoSource, unspentCoinSource, isDirect); } @override diff --git a/lib/buy/moonpay/moonpay_buy_provider.dart b/lib/buy/moonpay/moonpay_buy_provider.dart index 0736edeb7..ab62fcf68 100644 --- a/lib/buy/moonpay/moonpay_buy_provider.dart +++ b/lib/buy/moonpay/moonpay_buy_provider.dart @@ -16,11 +16,11 @@ import 'package:cake_wallet/.secrets.g.dart' as secrets; import 'package:cw_core/crypto_currency.dart'; class MoonPaySellProvider { - MoonPaySellProvider({this.isTest = false}) - : baseUrl = isTest ? _baseTestUrl : _baseProductUrl; + MoonPaySellProvider({this.isTest = false}) : baseUrl = isTest ? _baseTestUrl : _baseProductUrl; static const _baseTestUrl = 'sell-sandbox.moonpay.com'; static const _baseProductUrl = 'sell.moonpay.com'; + static String themeToMoonPayTheme(ThemeBase theme) { switch (theme.type) { case ThemeType.bright: @@ -31,16 +31,17 @@ class MoonPaySellProvider { return 'dark'; } } - static String get _apiKey => secrets.moonPayApiKey; - static String get _secretKey => secrets.moonPaySecretKey; + + static String get _apiKey => secrets.moonPayApiKey; + + static String get _secretKey => secrets.moonPaySecretKey; final bool isTest; final String baseUrl; Future<Uri> requestUrl( {required CryptoCurrency currency, - required String refundWalletAddress, - required SettingsStore settingsStore}) async { - + required String refundWalletAddress, + required SettingsStore settingsStore}) async { final customParams = { 'theme': themeToMoonPayTheme(settingsStore.currentTheme), 'language': settingsStore.languageCode, @@ -50,11 +51,13 @@ class MoonPaySellProvider { }; final originalUri = Uri.https( - baseUrl, '', <String, dynamic>{ - 'apiKey': _apiKey, - 'defaultBaseCurrencyCode': currency.toString().toLowerCase(), - 'refundWalletAddress': refundWalletAddress - }..addAll(customParams)); + baseUrl, + '', + <String, dynamic>{ + 'apiKey': _apiKey, + 'defaultBaseCurrencyCode': currency.toString().toLowerCase(), + 'refundWalletAddress': refundWalletAddress + }..addAll(customParams)); final messageBytes = utf8.encode('?${originalUri.query}'); final key = utf8.encode(_secretKey); final hmac = Hmac(sha256, key); @@ -93,8 +96,7 @@ class MoonPayBuyProvider extends BuyProvider { @override BuyProviderDescription get description => BuyProviderDescription.moonPay; - String get currencyCode => - walletTypeToCryptoCurrency(walletType).title.toLowerCase(); + String get currencyCode => walletTypeToCryptoCurrency(walletType).title.toLowerCase(); @override String get trackUrl => baseUrl + '/transaction_receipt?transactionId='; @@ -103,16 +105,24 @@ class MoonPayBuyProvider extends BuyProvider { @override Future<String> requestUrl(String amount, String sourceCurrency) async { - final enabledPaymentMethods = - 'credit_debit_card%2Capple_pay%2Cgoogle_pay%2Csamsung_pay' + final enabledPaymentMethods = 'credit_debit_card%2Capple_pay%2Cgoogle_pay%2Csamsung_pay' '%2Csepa_bank_transfer%2Cgbp_bank_transfer%2Cgbp_open_banking_payment'; - final suffix = '?apiKey=' + _apiKey + '¤cyCode=' + - currencyCode + '&enabledPaymentMethods=' + enabledPaymentMethods + - '&walletAddress=' + walletAddress + - '&baseCurrencyCode=' + sourceCurrency.toLowerCase() + - '&baseCurrencyAmount=' + amount + '&lockAmount=true' + - '&showAllCurrencies=false' + '&showWalletAddressForm=false'; + final suffix = '?apiKey=' + + _apiKey + + '¤cyCode=' + + currencyCode + + '&enabledPaymentMethods=' + + enabledPaymentMethods + + '&walletAddress=' + + walletAddress + + '&baseCurrencyCode=' + + sourceCurrency.toLowerCase() + + '&baseCurrencyAmount=' + + amount + + '&lockAmount=true' + + '&showAllCurrencies=false' + + '&showWalletAddressForm=false'; final originalUrl = baseUrl + suffix; @@ -121,25 +131,28 @@ class MoonPayBuyProvider extends BuyProvider { final hmac = Hmac(sha256, key); final digest = hmac.convert(messageBytes); final signature = base64.encode(digest.bytes); - final urlWithSignature = originalUrl + - '&signature=${Uri.encodeComponent(signature)}'; + final urlWithSignature = originalUrl + '&signature=${Uri.encodeComponent(signature)}'; return isTestEnvironment ? originalUrl : urlWithSignature; } @override Future<BuyAmount> calculateAmount(String amount, String sourceCurrency) async { - final url = _apiUrl + _currenciesSuffix + '/$currencyCode' + - _quoteSuffix + '/?apiKey=' + _apiKey + - '&baseCurrencyAmount=' + amount + - '&baseCurrencyCode=' + sourceCurrency.toLowerCase(); + final url = _apiUrl + + _currenciesSuffix + + '/$currencyCode' + + _quoteSuffix + + '/?apiKey=' + + _apiKey + + '&baseCurrencyAmount=' + + amount + + '&baseCurrencyCode=' + + sourceCurrency.toLowerCase(); final uri = Uri.parse(url); final response = await get(uri); if (response.statusCode != 200) { - throw BuyException( - description: description, - text: 'Quote is not found!'); + throw BuyException(description: description, text: 'Quote is not found!'); } final responseJSON = json.decode(response.body) as Map<String, dynamic>; @@ -148,22 +161,17 @@ class MoonPayBuyProvider extends BuyProvider { final minSourceAmount = responseJSON['baseCurrency']['minAmount'] as int; return BuyAmount( - sourceAmount: sourceAmount, - destAmount: destAmount, - minAmount: minSourceAmount); + sourceAmount: sourceAmount, destAmount: destAmount, minAmount: minSourceAmount); } @override Future<Order> findOrderById(String id) async { - final url = _apiUrl + _transactionsSuffix + '/$id' + - '?apiKey=' + _apiKey; + final url = _apiUrl + _transactionsSuffix + '/$id' + '?apiKey=' + _apiKey; final uri = Uri.parse(url); final response = await get(uri); if (response.statusCode != 200) { - throw BuyException( - description: description, - text: 'Transaction $id is not found!'); + throw BuyException(description: description, text: 'Transaction $id is not found!'); } final responseJSON = json.decode(response.body) as Map<String, dynamic>; @@ -181,8 +189,7 @@ class MoonPayBuyProvider extends BuyProvider { createdAt: createdAt, amount: amount.toString(), receiveAddress: walletAddress, - walletId: walletId - ); + walletId: walletId); } static Future<bool> onEnabled() async { @@ -201,4 +208,4 @@ class MoonPayBuyProvider extends BuyProvider { return isBuyEnable; } -} \ No newline at end of file +} diff --git a/lib/buy/order.dart b/lib/buy/order.dart index 5a677d291..387fbcd34 100644 --- a/lib/buy/order.dart +++ b/lib/buy/order.dart @@ -1,8 +1,7 @@ import 'package:cake_wallet/buy/buy_provider_description.dart'; +import 'package:hive/hive.dart'; import 'package:cake_wallet/exchange/trade_state.dart'; import 'package:cw_core/format_amount.dart'; -import 'package:cw_core/hive_type_ids.dart'; -import 'package:hive/hive.dart'; part 'order.g.dart'; @@ -27,7 +26,7 @@ class Order extends HiveObject { } } - static const typeId = ORDER_TYPE_ID; + static const typeId = 8; static const boxName = 'Orders'; static const boxKey = 'ordersBoxKey'; @@ -67,4 +66,4 @@ class Order extends HiveObject { BuyProviderDescription.deserialize(raw: providerRaw); String amountFormatted() => formatAmount(amount); -} +} \ No newline at end of file diff --git a/lib/core/auth_service.dart b/lib/core/auth_service.dart index 854640015..5a48f76e4 100644 --- a/lib/core/auth_service.dart +++ b/lib/core/auth_service.dart @@ -3,7 +3,7 @@ import 'package:cake_wallet/routes.dart'; import 'package:cake_wallet/src/screens/auth/auth_page.dart'; import 'package:flutter/material.dart'; import 'package:mobx/mobx.dart'; -import 'package:flutter_secure_storage/flutter_secure_storage.dart'; +import 'package:cake_wallet/core/secure_storage.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:cake_wallet/entities/preferences_key.dart'; import 'package:cake_wallet/entities/secret_store_key.dart'; @@ -31,7 +31,7 @@ class AuthService with Store { Routes.restoreOptions, ]; - final FlutterSecureStorage secureStorage; + final SecureStorage secureStorage; final SharedPreferences sharedPreferences; final SettingsStore settingsStore; @@ -106,7 +106,7 @@ class AuthService with Store { } } - + Navigator.of(context).pushNamed(Routes.auth, arguments: (bool isAuthenticatedSuccessfully, AuthPageState auth) async { if (!isAuthenticatedSuccessfully) { @@ -140,8 +140,8 @@ class AuthService with Store { } } } - + }); - + } } diff --git a/lib/core/backup_service.dart b/lib/core/backup_service.dart index 6476891ed..03581f156 100644 --- a/lib/core/backup_service.dart +++ b/lib/core/backup_service.dart @@ -1,15 +1,16 @@ import 'dart:convert'; import 'dart:io'; import 'dart:typed_data'; +import 'package:cw_core/root_dir.dart'; +import 'package:cake_wallet/entities/cake_2fa_preset_options.dart'; import 'package:cw_core/wallet_type.dart'; import 'package:flutter/foundation.dart'; import 'package:hive/hive.dart'; -import 'package:flutter_secure_storage/flutter_secure_storage.dart'; +import 'package:cake_wallet/core/secure_storage.dart'; import 'package:path_provider/path_provider.dart'; import 'package:cryptography/cryptography.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:archive/archive_io.dart'; -import 'package:cw_core/cake_hive.dart'; import 'package:cake_wallet/core/key_service.dart'; import 'package:cake_wallet/entities/encrypt.dart'; import 'package:cake_wallet/entities/preferences_key.dart'; @@ -17,7 +18,6 @@ import 'package:cake_wallet/entities/secret_store_key.dart'; import 'package:cw_core/wallet_info.dart'; import 'package:cake_wallet/.secrets.g.dart' as secrets; import 'package:cake_wallet/wallet_types.g.dart'; - import 'package:cake_backup/backup.dart' as cake_backup; class BackupService { @@ -32,7 +32,7 @@ class BackupService { static const _v2 = 2; final Cipher _cipher; - final FlutterSecureStorage _flutterSecureStorage; + final SecureStorage _flutterSecureStorage; final SharedPreferences _sharedPreferences; final Box<WalletInfo> _walletInfoSource; final KeyService _keyService; @@ -74,7 +74,7 @@ class BackupService { Future<Uint8List> _exportBackupV2(String password) async { final zipEncoder = ZipFileEncoder(); - final appDir = await getApplicationDocumentsDirectory(); + final appDir = await getAppDir(); final now = DateTime.now(); final tmpDir = Directory('${appDir.path}/~_BACKUP_TMP'); final archivePath = '${tmpDir.path}/backup_${now.toString()}.zip'; @@ -114,7 +114,7 @@ class BackupService { } Future<void> _importBackupV1(Uint8List data, String password, {required String nonce}) async { - final appDir = await getApplicationDocumentsDirectory(); + final appDir = await getAppDir(); final decryptedData = await _decryptV1(data, password, nonce); final zip = ZipDecoder().decodeBytes(decryptedData); @@ -137,7 +137,7 @@ class BackupService { } Future<void> _importBackupV2(Uint8List data, String password) async { - final appDir = await getApplicationDocumentsDirectory(); + final appDir = await getAppDir(); final decryptedData = await _decryptV2(data, password); final zip = ZipDecoder().decodeBytes(decryptedData); @@ -170,19 +170,19 @@ class BackupService { } Future<Box<WalletInfo>> _reloadHiveWalletInfoBox() async { - final appDir = await getApplicationDocumentsDirectory(); - await CakeHive.close(); - CakeHive.init(appDir.path); + final appDir = await getAppDir(); + await Hive.close(); + Hive.init(appDir.path); - if (!CakeHive.isAdapterRegistered(WalletInfo.typeId)) { - CakeHive.registerAdapter(WalletInfoAdapter()); + if (!Hive.isAdapterRegistered(WalletInfo.typeId)) { + Hive.registerAdapter(WalletInfoAdapter()); } - return await CakeHive.openBox<WalletInfo>(WalletInfo.boxName); + return await Hive.openBox<WalletInfo>(WalletInfo.boxName); } Future<void> _importPreferencesDump() async { - final appDir = await getApplicationDocumentsDirectory(); + final appDir = await getAppDir(); final preferencesFile = File('${appDir.path}/~_preferences_dump'); if (!preferencesFile.existsSync()) { @@ -375,7 +375,7 @@ class BackupService { Future<void> _importKeychainDumpV1(String password, {required String nonce, String keychainSalt = secrets.backupKeychainSalt}) async { - final appDir = await getApplicationDocumentsDirectory(); + final appDir = await getAppDir(); final keychainDumpFile = File('${appDir.path}/~_keychain_dump'); final decryptedKeychainDumpFileData = await _decryptV1(keychainDumpFile.readAsBytesSync(), '$keychainSalt$password', nonce); @@ -401,7 +401,7 @@ class BackupService { Future<void> _importKeychainDumpV2(String password, {String keychainSalt = secrets.backupKeychainSalt}) async { - final appDir = await getApplicationDocumentsDirectory(); + final appDir = await getAppDir(); final keychainDumpFile = File('${appDir.path}/~_keychain_dump'); final decryptedKeychainDumpFileData = await _decryptV2(keychainDumpFile.readAsBytesSync(), '$keychainSalt$password'); diff --git a/lib/core/key_service.dart b/lib/core/key_service.dart index 337f1ef21..a15c92784 100644 --- a/lib/core/key_service.dart +++ b/lib/core/key_service.dart @@ -1,11 +1,11 @@ -import 'package:flutter_secure_storage/flutter_secure_storage.dart'; +import 'package:cake_wallet/core/secure_storage.dart'; import 'package:cake_wallet/entities/secret_store_key.dart'; import 'package:cake_wallet/entities/encrypt.dart'; class KeyService { KeyService(this._secureStorage); - final FlutterSecureStorage _secureStorage; + final SecureStorage _secureStorage; Future<String> getWalletPassword({required String walletName}) async { final key = generateStoreKeyFor( diff --git a/lib/core/wallet_creation_service.dart b/lib/core/wallet_creation_service.dart index 3b28f36c3..25f2d75be 100644 --- a/lib/core/wallet_creation_service.dart +++ b/lib/core/wallet_creation_service.dart @@ -2,7 +2,6 @@ import 'package:cake_wallet/di.dart'; import 'package:cw_core/wallet_info.dart'; import 'package:cake_wallet/entities/preferences_key.dart'; import 'package:flutter/foundation.dart'; -import 'package:flutter_secure_storage/flutter_secure_storage.dart'; import 'package:hive/hive.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:cake_wallet/core/key_service.dart'; @@ -15,7 +14,6 @@ import 'package:cw_core/wallet_type.dart'; class WalletCreationService { WalletCreationService( {required WalletType initialType, - required this.secureStorage, required this.keyService, required this.sharedPreferences, required this.walletInfoSource}) @@ -24,7 +22,6 @@ class WalletCreationService { } WalletType type; - final FlutterSecureStorage secureStorage; final SharedPreferences sharedPreferences; final KeyService keyService; final Box<WalletInfo> walletInfoSource; @@ -58,10 +55,13 @@ class WalletCreationService { Future<WalletBase> create(WalletCredentials credentials) async { checkIfExists(credentials.name); - final password = generateWalletPassword(); - credentials.password = password; - await keyService.saveWalletPassword( - password: password, walletName: credentials.name); + + if (credentials.password == null) { + credentials.password = generateWalletPassword(); + await keyService.saveWalletPassword( + password: credentials.password!, walletName: credentials.name); + } + final wallet = await _service!.create(credentials); if (wallet.type == WalletType.monero) { @@ -76,10 +76,13 @@ class WalletCreationService { Future<WalletBase> restoreFromKeys(WalletCredentials credentials) async { checkIfExists(credentials.name); - final password = generateWalletPassword(); - credentials.password = password; - await keyService.saveWalletPassword( - password: password, walletName: credentials.name); + + if (credentials.password == null) { + credentials.password = generateWalletPassword(); + await keyService.saveWalletPassword( + password: credentials.password!, walletName: credentials.name); + } + final wallet = await _service!.restoreFromKeys(credentials); if (wallet.type == WalletType.monero) { @@ -94,10 +97,13 @@ class WalletCreationService { Future<WalletBase> restoreFromSeed(WalletCredentials credentials) async { checkIfExists(credentials.name); - final password = generateWalletPassword(); - credentials.password = password; - await keyService.saveWalletPassword( - password: password, walletName: credentials.name); + + if (credentials.password == null) { + credentials.password = generateWalletPassword(); + await keyService.saveWalletPassword( + password: credentials.password!, walletName: credentials.name); + } + final wallet = await _service!.restoreFromSeed(credentials); if (wallet.type == WalletType.monero) { diff --git a/lib/core/wallet_loading_service.dart b/lib/core/wallet_loading_service.dart index 3323e7831..9596015bc 100644 --- a/lib/core/wallet_loading_service.dart +++ b/lib/core/wallet_loading_service.dart @@ -7,26 +7,24 @@ import 'package:cw_core/wallet_type.dart'; import 'package:shared_preferences/shared_preferences.dart'; class WalletLoadingService { - WalletLoadingService( - this.sharedPreferences, this.keyService, this.walletServiceFactory); + WalletLoadingService(this.sharedPreferences, this.keyService, this.walletServiceFactory); final SharedPreferences sharedPreferences; final KeyService keyService; final WalletService Function(WalletType type) walletServiceFactory; - Future<void> renameWallet( - WalletType type, String name, String newName) async { + Future<void> renameWallet(WalletType type, String name, String newName, + {String? password}) async { final walletService = walletServiceFactory.call(type); - final password = await keyService.getWalletPassword(walletName: name); + final walletPassword = password ?? (await keyService.getWalletPassword(walletName: name)); // Save the current wallet's password to the new wallet name's key - await keyService.saveWalletPassword( - walletName: newName, password: password); + await keyService.saveWalletPassword(walletName: newName, password: walletPassword); // Delete previous wallet name from keyService to keep only new wallet's name // otherwise keeps duplicate (old and new names) await keyService.deleteWalletPassword(walletName: name); - await walletService.rename(name, password, newName); + await walletService.rename(name, walletPassword, newName); // set shared preferences flag based on previous wallet name if (type == WalletType.monero) { @@ -37,10 +35,10 @@ class WalletLoadingService { } } - Future<WalletBase> load(WalletType type, String name) async { + Future<WalletBase> load(WalletType type, String name, {String? password}) async { final walletService = walletServiceFactory.call(type); - final password = await keyService.getWalletPassword(walletName: name); - final wallet = await walletService.openWallet(name, password); + final walletPassword = password ?? (await keyService.getWalletPassword(walletName: name)); + final wallet = await walletService.openWallet(name, walletPassword); if (type == WalletType.monero) { await updateMoneroWalletPassword(wallet); @@ -61,11 +59,9 @@ class WalletLoadingService { // Save new generated password with backup key for case where // wallet will change password, but it will fail to update in secure storage final bakWalletName = '#__${wallet.name}_bak__#'; - await keyService.saveWalletPassword( - walletName: bakWalletName, password: password); + await keyService.saveWalletPassword(walletName: bakWalletName, password: password); await wallet.changePassword(password); - await keyService.saveWalletPassword( - walletName: wallet.name, password: password); + await keyService.saveWalletPassword(walletName: wallet.name, password: password); isPasswordUpdated = true; await sharedPreferences.setBool(key, isPasswordUpdated); } diff --git a/lib/di.dart b/lib/di.dart index 80a55e1c6..258638be4 100644 --- a/lib/di.dart +++ b/lib/di.dart @@ -1,12 +1,15 @@ + +import 'package:cake_wallet/buy/onramper/onramper_buy_provider.dart'; +import 'package:cake_wallet/core/yat_service.dart'; +import 'package:cake_wallet/entities/parse_address_from_domain.dart'; +import 'package:cake_wallet/entities/preferences_key.dart'; import 'package:cake_wallet/anonpay/anonpay_api.dart'; import 'package:cake_wallet/anonpay/anonpay_info_base.dart'; import 'package:cake_wallet/anonpay/anonpay_invoice_info.dart'; -import 'package:cake_wallet/buy/onramper/onramper_buy_provider.dart'; import 'package:cake_wallet/buy/payfura/payfura_buy_provider.dart'; import 'package:cake_wallet/core/yat_service.dart'; import 'package:cake_wallet/entities/background_tasks.dart'; import 'package:cake_wallet/entities/exchange_api_mode.dart'; -import 'package:cake_wallet/entities/parse_address_from_domain.dart'; import 'package:cake_wallet/entities/receive_page_option.dart'; import 'package:cake_wallet/ethereum/ethereum.dart'; import 'package:cake_wallet/ionia/ionia_anypay.dart'; @@ -15,6 +18,8 @@ import 'package:cake_wallet/ionia/ionia_tip.dart'; import 'package:cake_wallet/routes.dart'; import 'package:cake_wallet/src/screens/anonpay_details/anonpay_details_page.dart'; import 'package:cake_wallet/src/screens/buy/webview_page.dart'; +import 'package:cake_wallet/src/screens/dashboard/desktop_widgets/desktop_wallet_selection_dropdown.dart'; +import 'package:cake_wallet/src/screens/dashboard/widgets/transactions_page.dart'; import 'package:cake_wallet/src/screens/dashboard/desktop_dashboard_page.dart'; import 'package:cake_wallet/src/screens/dashboard/desktop_widgets/desktop_sidebar_wrapper.dart'; import 'package:cake_wallet/src/screens/dashboard/desktop_widgets/desktop_wallet_selection_dropdown.dart'; @@ -32,19 +37,18 @@ import 'package:cake_wallet/src/screens/ionia/cards/ionia_custom_redeem_page.dar import 'package:cake_wallet/src/screens/ionia/cards/ionia_gift_card_detail_page.dart'; import 'package:cake_wallet/src/screens/ionia/cards/ionia_more_options_page.dart'; import 'package:cake_wallet/src/screens/settings/connection_sync_page.dart'; +import 'package:cake_wallet/src/screens/wallet_unlock/wallet_unlock_arguments.dart'; +import 'package:cake_wallet/themes/theme_list.dart'; +import 'package:cake_wallet/utils/payment_request.dart'; +import 'package:cake_wallet/utils/responsive_layout_util.dart'; +import 'package:cake_wallet/view_model/dashboard/desktop_sidebar_view_model.dart'; import 'package:cake_wallet/src/screens/setup_2fa/modify_2fa_page.dart'; import 'package:cake_wallet/src/screens/setup_2fa/setup_2fa_qr_page.dart'; import 'package:cake_wallet/src/screens/setup_2fa/setup_2fa.dart'; import 'package:cake_wallet/src/screens/setup_2fa/setup_2fa_enter_code_page.dart'; -import 'package:cake_wallet/src/screens/support_chat/support_chat_page.dart'; -import 'package:cake_wallet/src/screens/support_other_links/support_other_links_page.dart'; import 'package:cake_wallet/src/screens/wallet/wallet_edit_page.dart'; -import 'package:cake_wallet/themes/theme_list.dart'; import 'package:cake_wallet/utils/device_info.dart'; import 'package:cake_wallet/store/anonpay/anonpay_transactions_store.dart'; -import 'package:cake_wallet/utils/payment_request.dart'; -import 'package:cake_wallet/utils/responsive_layout_util.dart'; -import 'package:cake_wallet/view_model/dashboard/desktop_sidebar_view_model.dart'; import 'package:cake_wallet/view_model/anon_invoice_page_view_model.dart'; import 'package:cake_wallet/view_model/anonpay_details_view_model.dart'; import 'package:cake_wallet/view_model/dashboard/home_settings_view_model.dart'; @@ -79,6 +83,8 @@ import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_list_i import 'package:cake_wallet/view_model/wallet_list/wallet_edit_view_model.dart'; import 'package:cake_wallet/view_model/wallet_list/wallet_list_item.dart'; import 'package:cw_core/erc20_token.dart'; +import 'package:cake_wallet/view_model/wallet_unlock_loadable_view_model.dart'; +import 'package:cake_wallet/view_model/wallet_unlock_verifiable_view_model.dart'; import 'package:cw_core/unspent_coins_info.dart'; import 'package:cake_wallet/core/backup_service.dart'; import 'package:cw_core/wallet_service.dart'; @@ -176,7 +182,7 @@ import 'package:get_it/get_it.dart'; import 'package:hive/hive.dart'; import 'package:mobx/mobx.dart'; import 'package:shared_preferences/shared_preferences.dart'; -import 'package:flutter_secure_storage/flutter_secure_storage.dart'; +import 'package:cake_wallet/core/secure_storage.dart'; import 'package:cake_wallet/core/wallet_creation_service.dart'; import 'package:cake_wallet/store/app_store.dart'; import 'package:cw_core/wallet_type.dart'; @@ -201,6 +207,7 @@ import 'package:cake_wallet/ionia/ionia_any_pay_payment_info.dart'; import 'package:cake_wallet/src/screens/receive/fullscreen_qr_page.dart'; import 'package:cake_wallet/core/wallet_loading_service.dart'; import 'package:cw_core/crypto_currency.dart'; +import 'package:cake_wallet/src/screens/wallet_unlock/wallet_unlock_page.dart'; import 'package:cake_wallet/entities/qr_view_data.dart'; import 'core/totp_request_details.dart'; @@ -269,7 +276,7 @@ Future<void> setup({ getIt.registerFactory<Box<Node>>(() => _nodeSource); - getIt.registerSingleton<FlutterSecureStorage>(FlutterSecureStorage()); + getIt.registerSingleton<SecureStorage>(secureStorageShared); getIt.registerSingleton(AuthenticationStore()); getIt.registerSingleton<WalletListStore>(WalletListStore()); getIt.registerSingleton(NodeListStoreBase.instance); @@ -290,22 +297,21 @@ Future<void> setup({ getIt.registerSingleton<ExchangeTemplateStore>( ExchangeTemplateStore(templateSource: _exchangeTemplates)); getIt.registerSingleton<YatStore>( - YatStore(appStore: getIt.get<AppStore>(), secureStorage: getIt.get<FlutterSecureStorage>()) + YatStore(appStore: getIt.get<AppStore>(), secureStorage: getIt.get<SecureStorage>()) ..init()); getIt.registerSingleton<AnonpayTransactionsStore>( AnonpayTransactionsStore(anonpayInvoiceInfoSource: _anonpayInvoiceInfoSource)); - final secretStore = await SecretStoreBase.load(getIt.get<FlutterSecureStorage>()); + final secretStore = await SecretStoreBase.load(getIt.get<SecureStorage>()); getIt.registerSingleton<SecretStore>(secretStore); - getIt.registerFactory<KeyService>(() => KeyService(getIt.get<FlutterSecureStorage>())); + getIt.registerFactory<KeyService>(() => KeyService(getIt.get<SecureStorage>())); getIt.registerFactoryParam<WalletCreationService, WalletType, void>((type, _) => WalletCreationService( initialType: type, keyService: getIt.get<KeyService>(), - secureStorage: getIt.get<FlutterSecureStorage>(), sharedPreferences: getIt.get<SharedPreferences>(), walletInfoSource: _walletInfoSource)); @@ -346,7 +352,7 @@ Future<void> setup({ getIt.registerFactory<AuthService>( () => AuthService( - secureStorage: getIt.get<FlutterSecureStorage>(), + secureStorage: getIt.get<SecureStorage>(), sharedPreferences: getIt.get<SharedPreferences>(), settingsStore: getIt.get<SettingsStore>(), ), @@ -733,11 +739,17 @@ Future<void> setup({ case WalletType.monero: return monero!.createMoneroWalletService(_walletInfoSource, _unspentCoinsInfoSource); case WalletType.bitcoin: - return bitcoin!.createBitcoinWalletService(_walletInfoSource, _unspentCoinsInfoSource); + return bitcoin!.createBitcoinWalletService( + _walletInfoSource, _unspentCoinsInfoSource, + SettingsStoreBase.walletPasswordDirectInput); case WalletType.litecoin: - return bitcoin!.createLitecoinWalletService(_walletInfoSource, _unspentCoinsInfoSource); + return bitcoin!.createLitecoinWalletService( + _walletInfoSource, _unspentCoinsInfoSource, + SettingsStoreBase.walletPasswordDirectInput); case WalletType.ethereum: - return ethereum!.createEthereumWalletService(_walletInfoSource); + return ethereum!.createEthereumWalletService( + _walletInfoSource, + SettingsStoreBase.walletPasswordDirectInput); default: throw Exception('Unexpected token: ${param1.toString()} for generating of WalletService'); } @@ -792,16 +804,19 @@ Future<void> setup({ trades: _tradesSource, settingsStore: getIt.get<SettingsStore>())); - getIt.registerFactory(() => BackupService(getIt.get<FlutterSecureStorage>(), _walletInfoSource, - getIt.get<KeyService>(), getIt.get<SharedPreferences>())); + getIt.registerFactory(() => BackupService( + getIt.get<SecureStorage>(), + _walletInfoSource, + getIt.get<KeyService>(), + getIt.get<SharedPreferences>())); getIt.registerFactory(() => BackupViewModel( - getIt.get<FlutterSecureStorage>(), getIt.get<SecretStore>(), getIt.get<BackupService>())); + getIt.get<SecureStorage>(), getIt.get<SecretStore>(), getIt.get<BackupService>())); getIt.registerFactory(() => BackupPage(getIt.get<BackupViewModel>())); getIt.registerFactory(() => - EditBackupPasswordViewModel(getIt.get<FlutterSecureStorage>(), getIt.get<SecretStore>())); + EditBackupPasswordViewModel(getIt.get<SecureStorage>(), getIt.get<SecretStore>())); getIt.registerFactory(() => EditBackupPasswordPage(getIt.get<EditBackupPasswordViewModel>())); @@ -850,12 +865,6 @@ Future<void> setup({ getIt.registerFactory(() => SupportPage(getIt.get<SupportViewModel>())); - getIt.registerFactory(() => - SupportChatPage( - getIt.get<SupportViewModel>(), secureStorage: getIt.get<FlutterSecureStorage>())); - - getIt.registerFactory(() => SupportOtherLinksPage(getIt.get<SupportViewModel>())); - getIt.registerFactory(() { final wallet = getIt.get<AppStore>().wallet; @@ -892,7 +901,7 @@ Future<void> setup({ getIt.registerFactory(() => AnyPayApi()); getIt.registerFactory<IoniaService>( - () => IoniaService(getIt.get<FlutterSecureStorage>(), getIt.get<IoniaApi>())); + () => IoniaService(getIt.get<SecureStorage>(), getIt.get<IoniaApi>())); getIt.registerFactory<IoniaAnyPay>(() => IoniaAnyPay( getIt.get<IoniaService>(), getIt.get<AnyPayApi>(), getIt.get<AppStore>().wallet!)); @@ -930,7 +939,7 @@ Future<void> setup({ return IoniaVerifyIoniaOtp(getIt.get<IoniaAuthViewModel>(), email, isSignIn); }); - getIt.registerFactory(() => IoniaWelcomePage()); + getIt.registerFactory(() => IoniaWelcomePage(getIt.get<IoniaGiftCardsListViewModel>())); getIt.registerFactoryParam<IoniaBuyGiftCardPage, List, void>((List args, _) { final merchant = args.first as IoniaMerchant; @@ -1033,6 +1042,65 @@ Future<void> setup({ getIt.registerFactoryParam<AdvancedPrivacySettingsViewModel, WalletType, void>( (type, _) => AdvancedPrivacySettingsViewModel(type, getIt.get<SettingsStore>())); + getIt.registerFactoryParam<WalletUnlockLoadableViewModel, WalletUnlockArguments, void>((args, _) { + final currentWalletName = getIt + .get<SharedPreferences>() + .getString(PreferencesKey.currentWalletName) ?? ''; + final currentWalletTypeRaw = + getIt.get<SharedPreferences>() + .getInt(PreferencesKey.currentWalletType) ?? 0; + final currentWalletType = deserializeFromInt(currentWalletTypeRaw); + + return WalletUnlockLoadableViewModel( + getIt.get<AppStore>(), + getIt.get<WalletLoadingService>(), + walletName: args.walletName ?? currentWalletName, + walletType: args.walletType ?? currentWalletType); + }); + + getIt.registerFactoryParam<WalletUnlockVerifiableViewModel, WalletUnlockArguments, void>((args, _) { + final currentWalletName = getIt + .get<SharedPreferences>() + .getString(PreferencesKey.currentWalletName) ?? ''; + final currentWalletTypeRaw = + getIt.get<SharedPreferences>() + .getInt(PreferencesKey.currentWalletType) ?? 0; + final currentWalletType = deserializeFromInt(currentWalletTypeRaw); + + return WalletUnlockVerifiableViewModel( + getIt.get<AppStore>(), + walletName: args.walletName ?? currentWalletName, + walletType: args.walletType ?? currentWalletType); + }); + + getIt.registerFactoryParam<WalletUnlockPage, WalletUnlockArguments, bool>((args, closable) { + return WalletUnlockPage( + getIt.get<WalletUnlockLoadableViewModel>(param1: args), + args.callback, + args.authPasswordHandler, + closable: closable); + }, instanceName: 'wallet_unlock_loadable'); + + getIt.registerFactoryParam<WalletUnlockPage, WalletUnlockArguments, bool>((args, closable) { + return WalletUnlockPage( + getIt.get<WalletUnlockVerifiableViewModel>(param1: args), + args.callback, + args.authPasswordHandler, + closable: closable); + }, instanceName: 'wallet_unlock_verifiable'); + + getIt.registerFactory<WalletUnlockPage>( + () => getIt.get<WalletUnlockPage>( + param1: WalletUnlockArguments( + callback: (bool successful, _) { + if (successful) { + final authStore = getIt.get<AuthenticationStore>(); + authStore.allowed(); + }}), + param2: false, + instanceName: 'wallet_unlock_loadable'), + instanceName: 'wallet_password_login'); + getIt.registerFactoryParam<HomeSettingsPage, BalanceViewModel, void>((balanceViewModel, _) => HomeSettingsPage(getIt.get<HomeSettingsViewModel>(param1: balanceViewModel))); diff --git a/lib/entities/contact.dart b/lib/entities/contact.dart index cd4fa55a2..e111429ca 100644 --- a/lib/entities/contact.dart +++ b/lib/entities/contact.dart @@ -1,7 +1,8 @@ -import 'package:cw_core/crypto_currency.dart'; -import 'package:cw_core/hive_type_ids.dart'; -import 'package:cw_core/keyable.dart'; +import 'package:flutter/foundation.dart'; import 'package:hive/hive.dart'; +import 'package:cw_core/crypto_currency.dart'; +import 'package:cake_wallet/utils/mobx.dart'; +import 'package:cw_core/keyable.dart'; part 'contact.g.dart'; @@ -13,7 +14,7 @@ class Contact extends HiveObject with Keyable { } } - static const typeId = CONTACT_TYPE_ID; + static const typeId = 0; static const boxName = 'Contacts'; @HiveField(0, defaultValue: '') diff --git a/lib/entities/default_settings_migration.dart b/lib/entities/default_settings_migration.dart index b4cb23131..42b6d5324 100644 --- a/lib/entities/default_settings_migration.dart +++ b/lib/entities/default_settings_migration.dart @@ -4,7 +4,7 @@ import 'package:cake_wallet/entities/exchange_api_mode.dart'; import 'package:cw_core/pathForWallet.dart'; import 'package:cake_wallet/entities/secret_store_key.dart'; import 'package:flutter/foundation.dart'; -import 'package:flutter_secure_storage/flutter_secure_storage.dart'; +import 'package:cake_wallet/core/secure_storage.dart'; import 'package:hive/hive.dart'; import 'package:share_plus/share_plus.dart'; import 'package:shared_preferences/shared_preferences.dart'; @@ -31,7 +31,7 @@ const ethereumDefaultNodeUri = 'ethereum.publicnode.com'; Future defaultSettingsMigration( {required int version, required SharedPreferences sharedPreferences, - required FlutterSecureStorage secureStorage, + required SecureStorage secureStorage, required Box<Node> nodes, required Box<WalletInfo> walletInfoSource, required Box<Trade> tradeSource, @@ -392,7 +392,7 @@ Future<void> updateDisplayModes(SharedPreferences sharedPreferences) async { PreferencesKey.currentBalanceDisplayModeKey, balanceDisplayMode); } -Future<void> generateBackupPassword(FlutterSecureStorage secureStorage) async { +Future<void> generateBackupPassword(SecureStorage secureStorage) async { final key = generateStoreKeyFor(key: SecretStoreKey.backupPassword); if ((await secureStorage.read(key: key))?.isNotEmpty ?? false) { diff --git a/lib/entities/fs_migration.dart b/lib/entities/fs_migration.dart index e3088ff54..ed5503c27 100644 --- a/lib/entities/fs_migration.dart +++ b/lib/entities/fs_migration.dart @@ -1,7 +1,7 @@ import 'dart:io'; import 'dart:convert'; import 'package:collection/collection.dart'; -import 'package:flutter_secure_storage/flutter_secure_storage.dart'; +import 'package:cake_wallet/core/secure_storage.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:hive/hive.dart'; import 'package:path_provider/path_provider.dart'; @@ -136,9 +136,8 @@ Future<void> ios_migrate_pin() async { return; } - final flutterSecureStorage = FlutterSecureStorage(); - final pinPassword = await flutterSecureStorage.read( - key: 'pin_password', iOptions: IOSOptions()); + final flutterSecureStorage = secureStorageShared; + final pinPassword = await flutterSecureStorage.readNoIOptions(key: 'pin_password'); // No pin if (pinPassword == null) { await prefs.setBool('ios_migration_pin_completed', true); @@ -159,7 +158,7 @@ Future<void> ios_migrate_wallet_passwords() async { } final appDocDir = await getApplicationDocumentsDirectory(); - final flutterSecureStorage = FlutterSecureStorage(); + final flutterSecureStorage = secureStorageShared; final keyService = KeyService(flutterSecureStorage); final walletsDir = Directory('${appDocDir.path}/wallets'); final moneroWalletsDir = Directory('${walletsDir.path}/monero'); @@ -174,8 +173,7 @@ Future<void> ios_migrate_wallet_passwords() async { if (item is Directory) { final name = item.path.split('/').last; final oldKey = 'wallet_monero_' + name + '_password'; - final password = await flutterSecureStorage.read( - key: oldKey, iOptions: IOSOptions()); + final password = await flutterSecureStorage.readNoIOptions(key: oldKey); await keyService.saveWalletPassword( walletName: name, password: password!); } @@ -371,9 +369,8 @@ Future<void> ios_migrate_trades_list(Box<Trade> tradeSource) async { } final content = file.readAsBytesSync(); - final flutterSecureStorage = FlutterSecureStorage(); - final masterPassword = await flutterSecureStorage.read( - key: 'master_password', iOptions: IOSOptions()); + final flutterSecureStorage = secureStorageShared; + final masterPassword = await flutterSecureStorage.readNoIOptions(key: 'master_password'); final key = masterPassword!.replaceAll('-', ''); final decoded = await ios_legacy_helper.decrypt(content, key: key, salt: secrets.salt); diff --git a/lib/entities/get_encryption_key.dart b/lib/entities/get_encryption_key.dart index e67380bb8..af84bd447 100644 --- a/lib/entities/get_encryption_key.dart +++ b/lib/entities/get_encryption_key.dart @@ -1,18 +1,23 @@ -import 'package:flutter_secure_storage/flutter_secure_storage.dart'; -import 'package:cw_core/cake_hive.dart'; +import 'package:cake_wallet/core/secure_storage.dart'; +import 'package:hive/hive.dart'; Future<List<int>> getEncryptionKey( - {required String forKey, required FlutterSecureStorage secureStorage}) async { - final stringifiedKey = await secureStorage.read(key: 'transactionDescriptionsBoxKey'); + {required String forKey, required SecureStorage secureStorage}) async { + final stringifiedKey = + await secureStorage.read(key: 'transactionDescriptionsBoxKey'); List<int> key; if (stringifiedKey == null) { - key = CakeHive.generateSecureKey(); + key = Hive.generateSecureKey(); final keyStringified = key.join(','); - await secureStorage.write(key: 'transactionDescriptionsBoxKey', value: keyStringified); + await secureStorage.write( + key: 'transactionDescriptionsBoxKey', value: keyStringified); } else { - key = stringifiedKey.split(',').map((i) => int.parse(i)).toList(); + key = stringifiedKey + .split(',') + .map((i) => int.parse(i)) + .toList(); } return key; -} +} \ No newline at end of file diff --git a/lib/entities/language_service.dart b/lib/entities/language_service.dart index 87ee99ff5..05e84cef9 100644 --- a/lib/entities/language_service.dart +++ b/lib/entities/language_service.dart @@ -55,11 +55,11 @@ class LanguageService { 'cs': 'czk', 'ur': 'pak', 'id': 'idn', - 'yo': 'nga', + 'yo': 'yor', 'ha': 'hau' }; - static final list = <String, String>{}; + static final list = <String, String> {}; static void loadLocaleList() { supportedLocales.forEach((key, value) { diff --git a/lib/entities/load_current_wallet.dart b/lib/entities/load_current_wallet.dart index d758b6697..5fedbd016 100644 --- a/lib/entities/load_current_wallet.dart +++ b/lib/entities/load_current_wallet.dart @@ -6,7 +6,7 @@ import 'package:cake_wallet/entities/preferences_key.dart'; import 'package:cw_core/wallet_type.dart'; import 'package:cake_wallet/core/wallet_loading_service.dart'; -Future<void> loadCurrentWallet() async { +Future<void> loadCurrentWallet({String? password}) async { final appStore = getIt.get<AppStore>(); final name = getIt .get<SharedPreferences>() @@ -21,7 +21,10 @@ Future<void> loadCurrentWallet() async { final type = deserializeFromInt(typeRaw); final walletLoadingService = getIt.get<WalletLoadingService>(); - final wallet = await walletLoadingService.load(type, name); + final wallet = await walletLoadingService.load( + type, + name, + password: password); appStore.changeCurrentWallet(wallet); getIt.get<BackgroundTasks>().registerSyncTask(); diff --git a/lib/entities/template.dart b/lib/entities/template.dart index 7cdd2c74a..6955136e0 100644 --- a/lib/entities/template.dart +++ b/lib/entities/template.dart @@ -1,4 +1,3 @@ -import 'package:cw_core/hive_type_ids.dart'; import 'package:hive/hive.dart'; part 'template.g.dart'; @@ -15,7 +14,7 @@ class Template extends HiveObject { required this.amountFiatRaw, this.additionalRecipientsRaw}); - static const typeId = TEMPLATE_TYPE_ID; + static const typeId = 6; static const boxName = 'Template'; @HiveField(0) diff --git a/lib/entities/transaction_description.dart b/lib/entities/transaction_description.dart index 088f9c480..86d6b043a 100644 --- a/lib/entities/transaction_description.dart +++ b/lib/entities/transaction_description.dart @@ -1,4 +1,3 @@ -import 'package:cw_core/hive_type_ids.dart'; import 'package:hive/hive.dart'; part 'transaction_description.g.dart'; @@ -7,7 +6,7 @@ part 'transaction_description.g.dart'; class TransactionDescription extends HiveObject { TransactionDescription({required this.id, this.recipientAddress, this.transactionNote}); - static const typeId = TRANSACTION_TYPE_ID; + static const typeId = 2; static const boxName = 'TransactionDescriptions'; static const boxKey = 'transactionDescriptionsBoxKey'; diff --git a/lib/ethereum/cw_ethereum.dart b/lib/ethereum/cw_ethereum.dart index f2db7741e..cb13b30d2 100644 --- a/lib/ethereum/cw_ethereum.dart +++ b/lib/ethereum/cw_ethereum.dart @@ -4,15 +4,16 @@ class CWEthereum extends Ethereum { @override List<String> getEthereumWordList(String language) => EthereumMnemonics.englishWordlist; - WalletService createEthereumWalletService(Box<WalletInfo> walletInfoSource) => - EthereumWalletService(walletInfoSource); + WalletService createEthereumWalletService(Box<WalletInfo> walletInfoSource, bool isDirect) => + EthereumWalletService(walletInfoSource, isDirect); @override WalletCredentials createEthereumNewWalletCredentials({ required String name, WalletInfo? walletInfo, + String? password, }) => - EthereumNewWalletCredentials(name: name, walletInfo: walletInfo); + EthereumNewWalletCredentials(name: name, walletInfo: walletInfo, password: password); @override WalletCredentials createEthereumRestoreWalletFromSeedCredentials({ diff --git a/lib/exchange/changenow/changenow_exchange_provider.dart b/lib/exchange/changenow/changenow_exchange_provider.dart index 6166a8875..da4e7eccb 100644 --- a/lib/exchange/changenow/changenow_exchange_provider.dart +++ b/lib/exchange/changenow/changenow_exchange_provider.dart @@ -202,11 +202,11 @@ class ChangeNowExchangeProvider extends ExchangeProvider { final expectedSendAmount = responseJSON['expectedAmountFrom'].toString(); final status = responseJSON['status'] as String; final state = TradeState.deserialize(raw: status); - final extraId = responseJSON['payinExtraId'] as String?; - final outputTransaction = responseJSON['payoutHash'] as String?; - final expiredAtRaw = responseJSON['validUntil'] as String?; + final extraId = responseJSON['payinExtraId'] as String; + final outputTransaction = responseJSON['payoutHash'] as String; + final expiredAtRaw = responseJSON['validUntil'] as String; final payoutAddress = responseJSON['payoutAddress'] as String; - final expiredAt = DateTime.tryParse(expiredAtRaw ?? '')?.toLocal(); + final expiredAt = DateTime.tryParse(expiredAtRaw)?.toLocal(); return Trade( id: id, diff --git a/lib/exchange/exchange_template.dart b/lib/exchange/exchange_template.dart index 2182efd8c..dcfd8d8e8 100644 --- a/lib/exchange/exchange_template.dart +++ b/lib/exchange/exchange_template.dart @@ -1,4 +1,3 @@ -import 'package:cw_core/hive_type_ids.dart'; import 'package:hive/hive.dart'; part 'exchange_template.g.dart'; @@ -15,7 +14,7 @@ class ExchangeTemplate extends HiveObject { required this.depositCurrencyTitleRaw, required this.receiveCurrencyTitleRaw}); - static const typeId = EXCHANGE_TEMPLATE_TYPE_ID; + static const typeId = 7; static const boxName = 'ExchangeTemplate'; @HiveField(0) diff --git a/lib/exchange/sideshift/sideshift_exchange_provider.dart b/lib/exchange/sideshift/sideshift_exchange_provider.dart index 257d339cf..884899e54 100644 --- a/lib/exchange/sideshift/sideshift_exchange_provider.dart +++ b/lib/exchange/sideshift/sideshift_exchange_provider.dart @@ -19,10 +19,10 @@ class SideShiftExchangeProvider extends ExchangeProvider { static const affiliateId = secrets.sideShiftAffiliateId; static const apiBaseUrl = 'https://sideshift.ai/api'; - static const rangePath = '/v2/pair'; - static const orderPath = '/v2/shifts'; - static const quotePath = '/v2/quotes'; - static const permissionPath = '/v2/permissions'; + static const rangePath = '/v1/pairs'; + static const orderPath = '/v1/orders'; + static const quotePath = '/v1/quotes'; + static const permissionPath = '/v1/permissions'; static const List<CryptoCurrency> _notSupported = [ CryptoCurrency.xhv, @@ -35,22 +35,23 @@ class SideShiftExchangeProvider extends ExchangeProvider { CryptoCurrency.scrt, CryptoCurrency.stx, CryptoCurrency.bttc, - CryptoCurrency.usdt, - CryptoCurrency.eos, ]; static List<ExchangePair> _supportedPairs() { - final supportedCurrencies = - CryptoCurrency.all.where((element) => !_notSupported.contains(element)).toList(); + final supportedCurrencies = CryptoCurrency.all + .where((element) => !_notSupported.contains(element)) + .toList(); return supportedCurrencies - .map((i) => supportedCurrencies.map((k) => ExchangePair(from: i, to: k, reverse: true))) + .map((i) => supportedCurrencies + .map((k) => ExchangePair(from: i, to: k, reverse: true))) .expand((i) => i) .toList(); } @override - ExchangeProviderDescription get description => ExchangeProviderDescription.sideShift; + ExchangeProviderDescription get description => + ExchangeProviderDescription.sideShift; @override Future<double> fetchRate( @@ -63,18 +64,17 @@ class SideShiftExchangeProvider extends ExchangeProvider { if (amount == 0) { return 0.0; } - - final fromCurrency = from.title.toLowerCase(); - final toCurrency = to.title.toLowerCase(); - final depositNetwork = _networkFor(from); - final settleNetwork = _networkFor(to); - - final url = "$apiBaseUrl$rangePath/$fromCurrency-$depositNetwork/$toCurrency-$settleNetwork"; - + final fromCurrency = _normalizeCryptoCurrency(from); + final toCurrency = _normalizeCryptoCurrency(to); + final url = + apiBaseUrl + rangePath + '/' + fromCurrency + '/' + toCurrency; final uri = Uri.parse(url); final response = await get(uri); final responseJSON = json.decode(response.body) as Map<String, dynamic>; final rate = double.parse(responseJSON['rate'] as String); + final max = double.parse(responseJSON['max'] as String); + + if (amount > max) return 0.00; return rate; } catch (_) { @@ -100,38 +100,25 @@ class SideShiftExchangeProvider extends ExchangeProvider { } final responseJSON = json.decode(response.body) as Map<String, dynamic>; - final cancreateShift = responseJSON['createShift'] as bool; - return cancreateShift; + final canCreateOrder = responseJSON['createOrder'] as bool; + final canCreateQuote = responseJSON['createQuote'] as bool; + return canCreateOrder && canCreateQuote; } @override - Future<Trade> createTrade({required TradeRequest request, required bool isFixedRateMode}) async { + Future<Trade> createTrade( + {required TradeRequest request, required bool isFixedRateMode}) async { final _request = request as SideShiftRequest; - String url = ''; - final depositCoin = request.depositMethod.title.toLowerCase(); - final settleCoin = request.settleMethod.title.toLowerCase(); + final quoteId = await _createQuote(_request); + final url = apiBaseUrl + orderPath; + final headers = {'Content-Type': 'application/json'}; final body = { + 'type': 'fixed', + 'quoteId': quoteId, 'affiliateId': affiliateId, 'settleAddress': _request.settleAddress, - 'refundAddress': _request.refundAddress, + 'refundAddress': _request.refundAddress }; - - if (isFixedRateMode) { - final quoteId = await _createQuote(_request); - body['quoteId'] = quoteId; - - url = apiBaseUrl + orderPath + '/fixed'; - } else { - url = apiBaseUrl + orderPath + '/variable'; - final depositNetwork = _networkFor(request.depositMethod); - final settleNetwork = _networkFor(request.settleMethod); - body["depositCoin"] = depositCoin; - body["settleCoin"] = settleCoin; - body["settleNetwork"] = settleNetwork; - body["depositNetwork"] = depositNetwork; - } - final headers = {'Content-Type': 'application/json'}; - final uri = Uri.parse(url); final response = await post(uri, headers: headers, body: json.encode(body)); @@ -148,9 +135,8 @@ class SideShiftExchangeProvider extends ExchangeProvider { final responseJSON = json.decode(response.body) as Map<String, dynamic>; final id = responseJSON['id'] as String; - final inputAddress = responseJSON['depositAddress'] as String; - final settleAddress = responseJSON['settleAddress'] as String; - final depositAmount = responseJSON['depositAmount'] as String?; + final inputAddress = responseJSON['depositAddress']['address'] as String; + final settleAddress = responseJSON['settleAddress']['address'] as String; return Trade( id: id, @@ -160,7 +146,7 @@ class SideShiftExchangeProvider extends ExchangeProvider { inputAddress: inputAddress, refundAddress: settleAddress, state: TradeState.created, - amount: depositAmount ?? _request.depositAmount, + amount: _request.depositAmount, payoutAddress: settleAddress, createdAt: DateTime.now(), ); @@ -169,17 +155,13 @@ class SideShiftExchangeProvider extends ExchangeProvider { Future<String> _createQuote(SideShiftRequest request) async { final url = apiBaseUrl + quotePath; final headers = {'Content-Type': 'application/json'}; - final depositMethod = request.depositMethod.title.toLowerCase(); - final settleMethod = request.settleMethod.title.toLowerCase(); - final depositNetwork = _networkFor(request.depositMethod); - final settleNetwork = _networkFor(request.settleMethod); + final depositMethod = _normalizeCryptoCurrency(request.depositMethod); + final settleMethod = _normalizeCryptoCurrency(request.settleMethod); final body = { - 'depositCoin': depositMethod, - 'settleCoin': settleMethod, + 'depositMethod': depositMethod, + 'settleMethod': settleMethod, 'affiliateId': affiliateId, - 'settleAmount': request.depositAmount, - 'settleNetwork': settleNetwork, - 'depositNetwork': depositNetwork, + 'depositAmount': request.depositAmount, }; final uri = Uri.parse(url); final response = await post(uri, headers: headers, body: json.encode(body)); @@ -206,15 +188,9 @@ class SideShiftExchangeProvider extends ExchangeProvider { {required CryptoCurrency from, required CryptoCurrency to, required bool isFixedRateMode}) async { - final fromCurrency = isFixedRateMode ? to : from; - final toCurrency = isFixedRateMode ? from : to; - - final fromNetwork = _networkFor(fromCurrency); - final toNetwork = _networkFor(toCurrency); - - final url = - "$apiBaseUrl$rangePath/${fromCurrency.title.toLowerCase()}-$fromNetwork/${toCurrency.title.toLowerCase()}-$toNetwork"; - + final fromCurrency = _normalizeCryptoCurrency(from); + final toCurrency = _normalizeCryptoCurrency(to); + final url = apiBaseUrl + rangePath + '/' + fromCurrency + '/' + toCurrency; final uri = Uri.parse(url); final response = await get(uri); @@ -233,14 +209,6 @@ class SideShiftExchangeProvider extends ExchangeProvider { final min = double.tryParse(responseJSON['min'] as String? ?? ''); final max = double.tryParse(responseJSON['max'] as String? ?? ''); - if (isFixedRateMode) { - final currentRate = double.parse(responseJSON['rate'] as String); - return Limits( - min: min != null ? (min * currentRate) : null, - max: max != null ? (max * currentRate) : null, - ); - } - return Limits(min: min, max: max); } @@ -258,7 +226,8 @@ class SideShiftExchangeProvider extends ExchangeProvider { final responseJSON = json.decode(response.body) as Map<String, dynamic>; final error = responseJSON['error']['message'] as String; - throw TradeNotFoundException(id, provider: description, description: error); + throw TradeNotFoundException(id, + provider: description, description: error); } if (response.statusCode != 200) { @@ -266,32 +235,36 @@ class SideShiftExchangeProvider extends ExchangeProvider { } final responseJSON = json.decode(response.body) as Map<String, dynamic>; - final fromCurrency = responseJSON['depositCoin'] as String; + final fromCurrency = responseJSON['depositMethodId'] as String; final from = CryptoCurrency.fromString(fromCurrency); - final toCurrency = responseJSON['settleCoin'] as String; + final toCurrency = responseJSON['settleMethodId'] as String; final to = CryptoCurrency.fromString(toCurrency); - final inputAddress = responseJSON['depositAddress'] as String; - final expectedSendAmount = responseJSON['depositAmount'] as String?; - final status = responseJSON['status'] as String?; - final settleAddress = responseJSON['settleAddress'] as String; + final inputAddress = responseJSON['depositAddress']['address'] as String; + final expectedSendAmount = responseJSON['depositAmount'].toString(); + final deposits = responseJSON['deposits'] as List?; + final settleAddress = responseJSON['settleAddress']['address'] as String; TradeState? state; + String? status; + if (deposits?.isNotEmpty ?? false) { + status = deposits![0]['status'] as String?; + } state = TradeState.deserialize(raw: status ?? 'created'); - final isVariable = (responseJSON['type'] as String) == 'variable'; - final expiredAtRaw = responseJSON['expiresAt'] as String; - final expiredAt = isVariable ? null : DateTime.tryParse(expiredAtRaw)?.toLocal(); + final expiredAtRaw = responseJSON['expiresAtISO'] as String; + final expiredAt = DateTime.tryParse(expiredAtRaw)?.toLocal(); return Trade( - id: id, - from: from, - to: to, - provider: description, - inputAddress: inputAddress, - amount: expectedSendAmount ?? '', - state: state, - expiredAt: expiredAt, - payoutAddress: settleAddress); + id: id, + from: from, + to: to, + provider: description, + inputAddress: inputAddress, + amount: expectedSendAmount, + state: state, + expiredAt: expiredAt, + payoutAddress: settleAddress + ); } @override @@ -306,25 +279,28 @@ class SideShiftExchangeProvider extends ExchangeProvider { @override String get title => 'SideShift'; - String _networkFor(CryptoCurrency currency) => - currency.tag != null ? _normalizeTag(currency.tag!) : 'mainnet'; - - String _normalizeTag(String tag) { - switch (tag) { - case 'ETH': - return 'ethereum'; - case 'TRX': - return 'tron'; - case 'LN': - return 'lightning'; - case 'POLY': + static String _normalizeCryptoCurrency(CryptoCurrency currency) { + switch (currency) { + case CryptoCurrency.zaddr: + return 'zaddr'; + case CryptoCurrency.zec: + return 'zec'; + case CryptoCurrency.bnb: + return currency.tag!.toLowerCase(); + case CryptoCurrency.usdterc20: + return 'usdtErc20'; + case CryptoCurrency.usdttrc20: + return 'usdtTrc20'; + case CryptoCurrency.usdcpoly: + return 'usdcpolygon'; + case CryptoCurrency.usdcsol: + return 'usdcsol'; + case CryptoCurrency.maticpoly: return 'polygon'; - case 'ZEC': - return 'zcash'; - case 'AVAXC': - return 'avax'; + case CryptoCurrency.btcln: + return 'ln'; default: - return tag.toLowerCase(); + return currency.title.toLowerCase(); } } } diff --git a/lib/exchange/trade.dart b/lib/exchange/trade.dart index db8c8fb3b..70dfa5713 100644 --- a/lib/exchange/trade.dart +++ b/lib/exchange/trade.dart @@ -1,9 +1,8 @@ +import 'package:hive/hive.dart'; import 'package:cw_core/crypto_currency.dart'; import 'package:cake_wallet/exchange/exchange_provider_description.dart'; import 'package:cake_wallet/exchange/trade_state.dart'; import 'package:cw_core/format_amount.dart'; -import 'package:cw_core/hive_type_ids.dart'; -import 'package:hive/hive.dart'; part 'trade.g.dart'; @@ -42,7 +41,7 @@ class Trade extends HiveObject { } } - static const typeId = TRADE_TYPE_ID; + static const typeId = 3; static const boxName = 'Trades'; static const boxKey = 'tradesBoxKey'; diff --git a/lib/ionia/ionia_service.dart b/lib/ionia/ionia_service.dart index 51e23ad28..9d4deeaf8 100644 --- a/lib/ionia/ionia_service.dart +++ b/lib/ionia/ionia_service.dart @@ -1,7 +1,7 @@ import 'package:cake_wallet/ionia/ionia_merchant.dart'; import 'package:cake_wallet/ionia/ionia_order.dart'; import 'package:cake_wallet/ionia/ionia_virtual_card.dart'; -import 'package:flutter_secure_storage/flutter_secure_storage.dart'; +import 'package:cake_wallet/core/secure_storage.dart'; import 'package:cake_wallet/.secrets.g.dart' as secrets; import 'package:cake_wallet/ionia/ionia_api.dart'; import 'package:cake_wallet/ionia/ionia_gift_card.dart'; @@ -17,7 +17,7 @@ class IoniaService { static String get clientId => secrets.ioniaClientId; - final FlutterSecureStorage secureStorage; + final SecureStorage secureStorage; final IoniaApi ioniaApi; // Create user diff --git a/lib/main.dart b/lib/main.dart index db5335ac1..8ade81e3e 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -7,7 +7,8 @@ import 'package:cake_wallet/locales/locale.dart'; import 'package:cake_wallet/store/yat/yat_store.dart'; import 'package:cake_wallet/utils/exception_handler.dart'; import 'package:cake_wallet/utils/responsive_layout_util.dart'; -import 'package:cw_core/hive_type_ids.dart'; +import 'package:cw_core/root_dir.dart'; +import 'package:cake_wallet/utils/responsive_layout_util.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; @@ -15,7 +16,7 @@ import 'package:hive/hive.dart'; import 'package:cake_wallet/di.dart'; import 'package:path_provider/path_provider.dart'; import 'package:shared_preferences/shared_preferences.dart'; -import 'package:flutter_secure_storage/flutter_secure_storage.dart'; +import 'package:cake_wallet/core/secure_storage.dart'; import 'package:flutter_mobx/flutter_mobx.dart'; import 'package:cake_wallet/themes/theme_base.dart'; import 'package:cake_wallet/router.dart' as Router; @@ -39,7 +40,6 @@ import 'package:uni_links/uni_links.dart'; import 'package:cw_core/unspent_coins_info.dart'; import 'package:cake_wallet/monero/monero.dart'; import 'package:cake_wallet/wallet_type_utils.dart'; -import 'package:cw_core/cake_hive.dart'; final navigatorKey = GlobalKey<NavigatorState>(); final rootKey = GlobalKey<RootState>(); @@ -58,8 +58,7 @@ Future<void> main() async { return true; }; - - await CakeHive.close(); + await Hive.close(); await initializeAppConfigs(); @@ -70,70 +69,71 @@ Future<void> main() async { } Future<void> initializeAppConfigs() async { - final appDir = await getApplicationDocumentsDirectory(); - CakeHive.init(appDir.path); + setRootDirFromEnv(); + final appDir = await getAppDir(); + Hive.init(appDir.path); - if (!CakeHive.isAdapterRegistered(Contact.typeId)) { - CakeHive.registerAdapter(ContactAdapter()); + if (!Hive.isAdapterRegistered(Contact.typeId)) { + Hive.registerAdapter(ContactAdapter()); } - if (!CakeHive.isAdapterRegistered(Node.typeId)) { - CakeHive.registerAdapter(NodeAdapter()); + if (!Hive.isAdapterRegistered(Node.typeId)) { + Hive.registerAdapter(NodeAdapter()); } - if (!CakeHive.isAdapterRegistered(TransactionDescription.typeId)) { - CakeHive.registerAdapter(TransactionDescriptionAdapter()); + if (!Hive.isAdapterRegistered(TransactionDescription.typeId)) { + Hive.registerAdapter(TransactionDescriptionAdapter()); } - if (!CakeHive.isAdapterRegistered(Trade.typeId)) { - CakeHive.registerAdapter(TradeAdapter()); + if (!Hive.isAdapterRegistered(Trade.typeId)) { + Hive.registerAdapter(TradeAdapter()); } - if (!CakeHive.isAdapterRegistered(WalletInfo.typeId)) { - CakeHive.registerAdapter(WalletInfoAdapter()); + if (!Hive.isAdapterRegistered(WalletInfo.typeId)) { + Hive.registerAdapter(WalletInfoAdapter()); } - if (!CakeHive.isAdapterRegistered(WALLET_TYPE_TYPE_ID)) { - CakeHive.registerAdapter(WalletTypeAdapter()); + if (!Hive.isAdapterRegistered(walletTypeTypeId)) { + Hive.registerAdapter(WalletTypeAdapter()); } - if (!CakeHive.isAdapterRegistered(Template.typeId)) { - CakeHive.registerAdapter(TemplateAdapter()); + if (!Hive.isAdapterRegistered(Template.typeId)) { + Hive.registerAdapter(TemplateAdapter()); } - if (!CakeHive.isAdapterRegistered(ExchangeTemplate.typeId)) { - CakeHive.registerAdapter(ExchangeTemplateAdapter()); + if (!Hive.isAdapterRegistered(ExchangeTemplate.typeId)) { + Hive.registerAdapter(ExchangeTemplateAdapter()); } - if (!CakeHive.isAdapterRegistered(Order.typeId)) { - CakeHive.registerAdapter(OrderAdapter()); + if (!Hive.isAdapterRegistered(Order.typeId)) { + Hive.registerAdapter(OrderAdapter()); } - if (!isMoneroOnly && !CakeHive.isAdapterRegistered(UnspentCoinsInfo.typeId)) { - CakeHive.registerAdapter(UnspentCoinsInfoAdapter()); + if (!isMoneroOnly && !Hive.isAdapterRegistered(UnspentCoinsInfo.typeId)) { + Hive.registerAdapter(UnspentCoinsInfoAdapter()); } - if (!CakeHive.isAdapterRegistered(AnonpayInvoiceInfo.typeId)) { - CakeHive.registerAdapter(AnonpayInvoiceInfoAdapter()); + if (!Hive.isAdapterRegistered(AnonpayInvoiceInfo.typeId)) { + Hive.registerAdapter(AnonpayInvoiceInfoAdapter()); } - final secureStorage = FlutterSecureStorage(); - final transactionDescriptionsBoxKey = - await getEncryptionKey(secureStorage: secureStorage, forKey: TransactionDescription.boxKey); - final tradesBoxKey = await getEncryptionKey(secureStorage: secureStorage, forKey: Trade.boxKey); - final ordersBoxKey = await getEncryptionKey(secureStorage: secureStorage, forKey: Order.boxKey); - final contacts = await CakeHive.openBox<Contact>(Contact.boxName); - final nodes = await CakeHive.openBox<Node>(Node.boxName); - final transactionDescriptions = await CakeHive.openBox<TransactionDescription>( - TransactionDescription.boxName, - encryptionKey: transactionDescriptionsBoxKey); - final trades = await CakeHive.openBox<Trade>(Trade.boxName, encryptionKey: tradesBoxKey); - final orders = await CakeHive.openBox<Order>(Order.boxName, encryptionKey: ordersBoxKey); - final walletInfoSource = await CakeHive.openBox<WalletInfo>(WalletInfo.boxName); - final templates = await CakeHive.openBox<Template>(Template.boxName); - final exchangeTemplates = await CakeHive.openBox<ExchangeTemplate>(ExchangeTemplate.boxName); - final anonpayInvoiceInfo = await CakeHive.openBox<AnonpayInvoiceInfo>(AnonpayInvoiceInfo.boxName); - final unspentCoinsInfoSource = await CakeHive.openBox<UnspentCoinsInfo>(UnspentCoinsInfo.boxName); + final secureStorage = secureStorageShared; + final transactionDescriptionsBoxKey = + await getEncryptionKey(secureStorage: secureStorage, forKey: TransactionDescription.boxKey); + final tradesBoxKey = await getEncryptionKey(secureStorage: secureStorage, forKey: Trade.boxKey); + final ordersBoxKey = await getEncryptionKey(secureStorage: secureStorage, forKey: Order.boxKey); + final contacts = await Hive.openBox<Contact>(Contact.boxName); + final nodes = await Hive.openBox<Node>(Node.boxName); + final transactionDescriptions = await Hive.openBox<TransactionDescription>( + TransactionDescription.boxName, + encryptionKey: transactionDescriptionsBoxKey); + final trades = await Hive.openBox<Trade>(Trade.boxName, encryptionKey: tradesBoxKey); + final orders = await Hive.openBox<Order>(Order.boxName, encryptionKey: ordersBoxKey); + final walletInfoSource = await Hive.openBox<WalletInfo>(WalletInfo.boxName); + final templates = await Hive.openBox<Template>(Template.boxName); + final exchangeTemplates = await Hive.openBox<ExchangeTemplate>(ExchangeTemplate.boxName); + final anonpayInvoiceInfo = await Hive.openBox<AnonpayInvoiceInfo>(AnonpayInvoiceInfo.boxName); + final unspentCoinsInfoSource = await Hive.openBox<UnspentCoinsInfo>(UnspentCoinsInfo.boxName); await initialSetup( sharedPreferences: await SharedPreferences.getInstance(), @@ -163,7 +163,7 @@ Future<void> initialSetup( required Box<Template> templates, required Box<ExchangeTemplate> exchangeTemplates, required Box<TransactionDescription> transactionDescriptions, - required FlutterSecureStorage secureStorage, + required SecureStorage secureStorage, required Box<AnonpayInvoiceInfo> anonpayInvoiceInfo, required Box<UnspentCoinsInfo> unspentCoinsInfoSource, int initialMigrationVersion = 15}) async { diff --git a/lib/reactions/check_connection.dart b/lib/reactions/check_connection.dart index 9185ffe15..6b7b79941 100644 --- a/lib/reactions/check_connection.dart +++ b/lib/reactions/check_connection.dart @@ -1,9 +1,10 @@ import 'dart:async'; -import 'package:connectivity_plus/connectivity_plus.dart'; import 'package:cw_core/wallet_base.dart'; import 'package:cw_core/sync_status.dart'; import 'package:cake_wallet/store/settings_store.dart'; +import 'package:connectivity/connectivity.dart'; + Timer? _checkConnectionTimer; void startCheckConnectionReaction( diff --git a/lib/reactions/on_authentication_state_change.dart b/lib/reactions/on_authentication_state_change.dart index 5f1214b76..8ab93f3a5 100644 --- a/lib/reactions/on_authentication_state_change.dart +++ b/lib/reactions/on_authentication_state_change.dart @@ -1,5 +1,6 @@ import 'package:cake_wallet/routes.dart'; import 'package:cake_wallet/utils/exception_handler.dart'; +import 'package:cake_wallet/store/settings_store.dart'; import 'package:flutter/widgets.dart'; import 'package:mobx/mobx.dart'; import 'package:cake_wallet/entities/load_current_wallet.dart'; @@ -14,7 +15,7 @@ void startAuthenticationStateChange( _onAuthenticationStateChange ??= autorun((_) async { final state = authenticationStore.state; - if (state == AuthenticationState.installed) { + if (state == AuthenticationState.installed && !SettingsStoreBase.walletPasswordDirectInput) { try { await loadCurrentWallet(); } catch (error, stack) { diff --git a/lib/reactions/on_wallet_sync_status_change.dart b/lib/reactions/on_wallet_sync_status_change.dart index 767bfd7e8..96305de04 100644 --- a/lib/reactions/on_wallet_sync_status_change.dart +++ b/lib/reactions/on_wallet_sync_status_change.dart @@ -7,17 +7,15 @@ import 'package:cw_core/wallet_base.dart'; import 'package:cw_core/balance.dart'; import 'package:cw_core/transaction_info.dart'; import 'package:cw_core/sync_status.dart'; -import 'package:wakelock/wakelock.dart'; +import 'package:wakelock_plus/wakelock_plus.dart'; ReactionDisposer? _onWalletSyncStatusChangeReaction; void startWalletSyncStatusChangeReaction( - WalletBase<Balance, TransactionHistoryBase<TransactionInfo>, - TransactionInfo> wallet, + WalletBase<Balance, TransactionHistoryBase<TransactionInfo>, TransactionInfo> wallet, FiatConversionStore fiatConversionStore) { _onWalletSyncStatusChangeReaction?.reaction.dispose(); - _onWalletSyncStatusChangeReaction = - reaction((_) => wallet.syncStatus, (SyncStatus status) async { + _onWalletSyncStatusChangeReaction = reaction((_) => wallet.syncStatus, (SyncStatus status) async { try { if (status is ConnectedSyncStatus) { await wallet.startSync(); @@ -27,12 +25,12 @@ void startWalletSyncStatusChangeReaction( } } if (status is SyncingSyncStatus) { - await Wakelock.enable(); + await WakelockPlus.enable(); } if (status is SyncedSyncStatus || status is FailedSyncStatus) { - await Wakelock.disable(); + await WakelockPlus.disable(); } - } catch(e) { + } catch (e) { print(e.toString()); } }); diff --git a/lib/router.dart b/lib/router.dart index 0ef91e16f..5d5c64a53 100644 --- a/lib/router.dart +++ b/lib/router.dart @@ -41,10 +41,10 @@ import 'package:cake_wallet/src/screens/setup_2fa/setup_2fa_qr_page.dart'; import 'package:cake_wallet/src/screens/setup_2fa/setup_2fa.dart'; import 'package:cake_wallet/src/screens/setup_2fa/setup_2fa_enter_code_page.dart'; import 'package:cake_wallet/src/screens/support/support_page.dart'; -import 'package:cake_wallet/src/screens/support_chat/support_chat_page.dart'; -import 'package:cake_wallet/src/screens/support_other_links/support_other_links_page.dart'; import 'package:cake_wallet/src/screens/unspent_coins/unspent_coins_details_page.dart'; import 'package:cake_wallet/src/screens/unspent_coins/unspent_coins_list_page.dart'; +import 'package:cake_wallet/src/screens/wallet_unlock/wallet_unlock_arguments.dart'; +import 'package:cake_wallet/store/settings_store.dart'; import 'package:cake_wallet/utils/payment_request.dart'; import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart'; import 'package:cake_wallet/view_model/monero_account_list/account_list_item.dart'; @@ -99,6 +99,7 @@ import 'package:cake_wallet/anypay/any_pay_payment_committed_info.dart'; import 'package:cake_wallet/ionia/ionia_any_pay_payment_info.dart'; import 'package:cw_core/crypto_currency.dart'; import 'package:cw_core/node.dart'; +import 'package:cake_wallet/src/screens/wallet_unlock/wallet_unlock_page.dart'; late RouteSettings currentRouteSettings; @@ -110,6 +111,14 @@ Route<dynamic> createRoute(RouteSettings settings) { return MaterialPageRoute<void>(builder: (_) => createWelcomePage()); case Routes.newWalletFromWelcome: + if (SettingsStoreBase.walletPasswordDirectInput) { + if (availableWalletTypes.length == 1) { + return createRoute(RouteSettings(name: Routes.newWallet, arguments: availableWalletTypes.first)); + } else { + return createRoute(RouteSettings(name: Routes.newWalletType)); + } + } + return CupertinoPageRoute<void>( builder: (_) => getIt.get<SetupPinCodePage>( param1: (PinCodeState<PinCodeWidget> context, dynamic _) { @@ -118,7 +127,7 @@ Route<dynamic> createRoute(RouteSettings settings) { } else { Navigator.of(context.context).pushNamed(Routes.newWalletType); } - }), + }), fullscreenDialog: true); case Routes.newWalletType: @@ -155,6 +164,10 @@ Route<dynamic> createRoute(RouteSettings settings) { param2: false)); case Routes.restoreOptions: + if (SettingsStoreBase.walletPasswordDirectInput) { + return createRoute(RouteSettings(name: Routes.restoreWalletType)); + } + final isNewInstall = settings.arguments as bool; return CupertinoPageRoute<void>( fullscreenDialog: true, @@ -178,6 +191,7 @@ Route<dynamic> createRoute(RouteSettings settings) { fullscreenDialog: true); } else if (isSingleCoin) { return MaterialPageRoute<void>( + fullscreenDialog: true, builder: (_) => getIt.get<WalletRestorePage>( param1: availableWalletTypes.first )); @@ -198,6 +212,7 @@ Route<dynamic> createRoute(RouteSettings settings) { case Routes.restoreWallet: return MaterialPageRoute<void>( + fullscreenDialog: true, builder: (_) => getIt.get<WalletRestorePage>( param1: settings.arguments as WalletType)); @@ -207,7 +222,6 @@ Route<dynamic> createRoute(RouteSettings settings) { case Routes.dashboard: return CupertinoPageRoute<void>( - settings: settings, builder: (_) => getIt.get<DashboardPage>()); case Routes.send: @@ -273,9 +287,16 @@ Route<dynamic> createRoute(RouteSettings settings) { case Routes.auth: return MaterialPageRoute<void>( fullscreenDialog: true, - builder: (_) => getIt.get<AuthPage>( - param1: settings.arguments as OnAuthenticationFinished, - param2: true)); + builder: (_) + => SettingsStoreBase.walletPasswordDirectInput + ? getIt.get<WalletUnlockPage>( + param1: WalletUnlockArguments( + callback: settings.arguments as OnAuthenticationFinished), + instanceName: 'wallet_unlock_verifiable', + param2: true) + : getIt.get<AuthPage>( + param1: settings.arguments as OnAuthenticationFinished, + param2: true)); case Routes.totpAuthCodePage: final args = settings.arguments as TotpAuthArgumentsModel; @@ -286,25 +307,32 @@ Route<dynamic> createRoute(RouteSettings settings) { ), ); - case Routes.login: - return CupertinoPageRoute<void>( - builder: (context) => WillPopScope( - child: getIt.get<AuthPage>(instanceName: 'login'), - onWillPop: () async => - // FIX-ME: Additional check does it works correctly - (await SystemChannels.platform.invokeMethod<bool>('SystemNavigator.pop') ?? - false), - ), - fullscreenDialog: true); + case Routes.walletUnlockLoadable: + return MaterialPageRoute<void>( + fullscreenDialog: true, + builder: (_) + => getIt.get<WalletUnlockPage>( + param1: settings.arguments as WalletUnlockArguments, + instanceName: 'wallet_unlock_loadable', + param2: true)); case Routes.unlock: return MaterialPageRoute<void>( fullscreenDialog: true, - builder: (_) => WillPopScope( - child: getIt.get<AuthPage>( - param1: settings.arguments as OnAuthenticationFinished, - param2: false), - onWillPop: () async => false)); + builder: (_) + => SettingsStoreBase.walletPasswordDirectInput + ? WillPopScope( + child: getIt.get<WalletUnlockPage>( + param1: WalletUnlockArguments( + callback: settings.arguments as OnAuthenticationFinished), + param2: false, + instanceName: 'wallet_unlock_verifiable'), + onWillPop: () async => false) + : WillPopScope( + child: getIt.get<AuthPage>( + param1: settings.arguments as OnAuthenticationFinished, + param2: false), + onWillPop: () async => false)); case Routes.connectionSync: return CupertinoPageRoute<void>( @@ -338,7 +366,16 @@ Route<dynamic> createRoute(RouteSettings settings) { param1: args?['editingNode'] as Node?, param2: args?['isSelected'] as bool?)); - + case Routes.login: + return CupertinoPageRoute<void>( + builder: (context) => WillPopScope( + child: SettingsStoreBase.walletPasswordDirectInput + ? getIt.get<WalletUnlockPage>(instanceName: 'wallet_password_login') + : getIt.get<AuthPage>(instanceName: 'login'), + onWillPop: () async => + // FIX-ME: Additional check does it works correctly + (await SystemChannels.platform.invokeMethod<bool>('SystemNavigator.pop') ?? false)), + fullscreenDialog: true); case Routes.accountCreation: return CupertinoPageRoute<String>( @@ -430,17 +467,9 @@ Route<dynamic> createRoute(RouteSettings settings) { builder: (_) => getIt.get<RestoreFromBackupPage>()); case Routes.support: - return CupertinoPageRoute<void>( - builder: (_) => getIt.get<SupportPage>()); - - case Routes.supportLiveChat: - return CupertinoPageRoute<void>( - builder: (_) => getIt.get<SupportChatPage>()); - - case Routes.supportOtherLinks: return CupertinoPageRoute<void>( fullscreenDialog: true, - builder: (_) => getIt.get<SupportOtherLinksPage>()); + builder: (_) => getIt.get<SupportPage>()); case Routes.unspentCoinsList: return MaterialPageRoute<void>( @@ -474,8 +503,7 @@ Route<dynamic> createRoute(RouteSettings settings) { return CupertinoPageRoute<void>( builder: (_) => getIt.get<IoniaCreateAccountPage>()); case Routes.ioniaManageCardsPage: - return CupertinoPageRoute<void>( - builder: (_) => getIt.get<IoniaManageCardsPage>()); + return CupertinoPageRoute<void>(builder: (_) => getIt.get<IoniaManageCardsPage>()); case Routes.ioniaBuyGiftCardPage: final args = settings.arguments as List; @@ -545,6 +573,7 @@ Route<dynamic> createRoute(RouteSettings settings) { case Routes.anonPayInvoicePage: final args = settings.arguments as List; return CupertinoPageRoute<void>( + fullscreenDialog: true, builder: (_) => getIt.get<AnonPayInvoicePage>(param1: args)); case Routes.anonPayReceivePage: diff --git a/lib/routes.dart b/lib/routes.dart index bcb4c38b5..0d3f7e1f6 100644 --- a/lib/routes.dart +++ b/lib/routes.dart @@ -47,8 +47,6 @@ class Routes { static const editBackupPassword = '/edit_backup_passowrd'; static const restoreFromBackup = '/restore_from_backup'; static const support = '/support'; - static const supportLiveChat = '/support/live_chat'; - static const supportOtherLinks = '/support/other'; static const orderDetails = '/order_details'; static const preOrder = '/pre_order'; static const buyWebView = '/buy_web_view'; @@ -80,9 +78,12 @@ class Routes { static const otherSettingsPage = '/other_settings_page'; static const advancedPrivacySettings = '/advanced_privacy_settings'; static const sweepingWalletPage = '/sweeping_wallet_page'; + static const walletPasswordUnlock = '/wallet_password_unlock'; + static const walletUnlockLoadable = '/wallet_unlock_loadable'; static const anonPayInvoicePage = '/anon_pay_invoice_page'; static const anonPayReceivePage = '/anon_pay_receive_page'; static const anonPayDetailsPage = '/anon_pay_details_page'; + static const payfuraPage = '/pay_fura_page'; static const desktop_actions = '/desktop_actions'; static const transactionsPage = '/transactions_page'; static const setup_2faPage = '/setup_2fa_page'; diff --git a/lib/src/screens/auth/auth_page.dart b/lib/src/screens/auth/auth_page.dart index dcd1c8016..d14a12527 100644 --- a/lib/src/screens/auth/auth_page.dart +++ b/lib/src/screens/auth/auth_page.dart @@ -12,6 +12,12 @@ import 'package:cake_wallet/core/execution_state.dart'; typedef OnAuthenticationFinished = void Function(bool, AuthPageState); +abstract class AuthPageState<T extends StatefulWidget> extends State<T> { + void changeProcessText(String text); + void hideProgressText(); + Future<void> close({String? route, dynamic arguments}); +} + class AuthPage extends StatefulWidget { AuthPage(this.authViewModel, {required this.onAuthenticationFinished, @@ -22,10 +28,10 @@ class AuthPage extends StatefulWidget { final bool closable; @override - AuthPageState createState() => AuthPageState(); + AuthPageState createState() => AuthPagePinCodeStateImpl(); } -class AuthPageState extends State<AuthPage> { +class AuthPagePinCodeStateImpl extends AuthPageState<AuthPage> { final _key = GlobalKey<ScaffoldState>(); final _pinCodeKey = GlobalKey<PinCodeState>(); final _backArrowImageDarkTheme = @@ -55,8 +61,6 @@ class AuthPageState extends State<AuthPage> { } if (state is FailureState) { - print('X'); - print(state.error); WidgetsBinding.instance.addPostFrameCallback((_) async { _pinCodeKey.currentState?.clear(); dismissFlushBar(_authBar); @@ -95,17 +99,20 @@ class AuthPageState extends State<AuthPage> { super.dispose(); } + @override void changeProcessText(String text) { dismissFlushBar(_authBar); _progressBar = createBar<void>(text, duration: null) ..show(_key.currentContext!); } + @override void hideProgressText() { dismissFlushBar(_progressBar); _progressBar = null; } + @override Future<void> close({String? route, dynamic arguments}) async { if (_key.currentContext == null) { throw Exception('Key context is null. Should be not happened'); diff --git a/lib/src/screens/buy/payfura_page.dart b/lib/src/screens/buy/payfura_page.dart new file mode 100644 index 000000000..a974aec25 --- /dev/null +++ b/lib/src/screens/buy/payfura_page.dart @@ -0,0 +1,58 @@ +import 'package:cake_wallet/buy/payfura/payfura_buy_provider.dart'; +import 'package:cake_wallet/generated/i18n.dart'; +import 'package:cake_wallet/src/screens/base_page.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_inappwebview/flutter_inappwebview.dart'; +import 'package:permission_handler/permission_handler.dart'; + +class PayFuraPage extends BasePage { + PayFuraPage(this._PayfuraBuyProvider); + + final PayfuraBuyProvider _PayfuraBuyProvider; + + @override + String get title => S.current.buy; + + @override + Widget body(BuildContext context) { + return PayFuraPageBody(_PayfuraBuyProvider); + } +} + +class PayFuraPageBody extends StatefulWidget { + PayFuraPageBody(this._PayfuraBuyProvider); + + final PayfuraBuyProvider _PayfuraBuyProvider; + + Uri get uri => _PayfuraBuyProvider.requestUrl(); + + @override + PayFuraPageBodyState createState() => PayFuraPageBodyState(); +} + +class PayFuraPageBodyState extends State<PayFuraPageBody> { + PayFuraPageBodyState(); + + @override + Widget build(BuildContext context) { + return InAppWebView( + initialOptions: InAppWebViewGroupOptions( + crossPlatform: InAppWebViewOptions(transparentBackground: true), + ), + initialUrlRequest: URLRequest(url: widget.uri), + androidOnPermissionRequest: (_, __, resources) async { + bool permissionGranted = await Permission.camera.status == PermissionStatus.granted; + if (!permissionGranted) { + permissionGranted = await Permission.camera.request().isGranted; + } + + return PermissionRequestResponse( + resources: resources, + action: permissionGranted + ? PermissionRequestResponseAction.GRANT + : PermissionRequestResponseAction.DENY, + ); + }, + ); + } +} diff --git a/lib/src/screens/dashboard/desktop_widgets/desktop_action_button.dart b/lib/src/screens/dashboard/desktop_widgets/desktop_action_button.dart index 7e9b2b23d..f49047e0b 100644 --- a/lib/src/screens/dashboard/desktop_widgets/desktop_action_button.dart +++ b/lib/src/screens/dashboard/desktop_widgets/desktop_action_button.dart @@ -2,6 +2,9 @@ import 'package:auto_size_text/auto_size_text.dart'; import 'package:cake_wallet/themes/extensions/balance_page_theme.dart'; import 'package:cake_wallet/themes/extensions/dashboard_page_theme.dart'; import 'package:cake_wallet/themes/extensions/sync_indicator_theme.dart'; +import 'package:cake_wallet/themes/extensions/balance_page_theme.dart'; +import 'package:cake_wallet/themes/extensions/dashboard_page_theme.dart'; +import 'package:cake_wallet/themes/extensions/sync_indicator_theme.dart'; import 'package:flutter/material.dart'; class DesktopActionButton extends StatelessWidget { @@ -24,45 +27,48 @@ class DesktopActionButton extends StatelessWidget { @override Widget build(BuildContext context) { - return Padding( - padding: const EdgeInsets.fromLTRB(8, 0, 8, 8), - child: GestureDetector( - onTap: onTap, - child: Container( - padding: EdgeInsets.symmetric(vertical: 25), - width: double.infinity, - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(15.0), - color: Theme.of(context).extension<SyncIndicatorTheme>()!.syncedBackgroundColor, - ), - child: Center( - child: Row( - mainAxisSize: MainAxisSize.min, - children: [ - Image.asset( - image, - height: 30, - width: 30, - color: isEnabled - ? Theme.of(context).extension<DashboardPageTheme>()!.textColor - : Theme.of(context).extension<BalancePageTheme>()!.labelTextColor, - ), - const SizedBox(width: 10), - AutoSizeText( - title, - style: TextStyle( - fontSize: 24, - fontFamily: 'Lato', - fontWeight: FontWeight.bold, + return MouseRegion( + cursor: SystemMouseCursors.click, + child: Padding( + padding: const EdgeInsets.fromLTRB(8, 0, 8, 8), + child: GestureDetector( + onTap: onTap, + child: Container( + padding: EdgeInsets.symmetric(vertical: 25), + width: double.infinity, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(15.0), + color: Theme.of(context).extension<SyncIndicatorTheme>()!.syncedBackgroundColor, + ), + child: Center( + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Image.asset( + image, + height: 30, + width: 30, color: isEnabled ? Theme.of(context).extension<DashboardPageTheme>()!.textColor - : null, - height: 1, + : Theme.of(context).extension<BalancePageTheme>()!.labelTextColor, ), - maxLines: 1, - textAlign: TextAlign.center, - ) - ], + const SizedBox(width: 10), + AutoSizeText( + title, + style: TextStyle( + fontSize: 24, + fontFamily: 'Lato', + fontWeight: FontWeight.bold, + color: isEnabled + ? Theme.of(context).extension<DashboardPageTheme>()!.textColor + : null, + height: 1, + ), + maxLines: 1, + textAlign: TextAlign.center, + ) + ], + ), ), ), ), 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 684f7cd08..92b2886b9 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 @@ -4,9 +4,13 @@ import 'package:cake_wallet/core/auth_service.dart'; import 'package:cake_wallet/entities/desktop_dropdown_item.dart'; import 'package:cake_wallet/generated/i18n.dart'; import 'package:cake_wallet/routes.dart'; +import 'package:cake_wallet/src/screens/auth/auth_page.dart'; import 'package:cake_wallet/src/screens/dashboard/desktop_widgets/dropdown_item_widget.dart'; +import 'package:cake_wallet/src/screens/wallet_unlock/wallet_unlock_arguments.dart'; import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart'; import 'package:cake_wallet/themes/extensions/menu_theme.dart'; +import 'package:cake_wallet/themes/extensions/menu_theme.dart'; +import 'package:cake_wallet/store/settings_store.dart'; import 'package:cake_wallet/utils/show_bar.dart'; import 'package:cake_wallet/utils/show_pop_up.dart'; import 'package:cake_wallet/view_model/wallet_list/wallet_list_item.dart'; @@ -34,14 +38,12 @@ class _DesktopWalletSelectionDropDownState extends State<DesktopWalletSelectionD final havenIcon = Image.asset('assets/images/haven_logo.png', height: 24, width: 24); final ethereumIcon = Image.asset('assets/images/eth_icon.png', height: 24, width: 24); final nonWalletTypeIcon = Image.asset('assets/images/close.png', height: 24, width: 24); - Image _newWalletImage(BuildContext context) => Image.asset( 'assets/images/new_wallet.png', height: 12, width: 12, color: Theme.of(context).extension<CakeTextTheme>()!.titleColor, ); - Image _restoreWalletImage(BuildContext context) => Image.asset( 'assets/images/restore_wallet.png', height: 12, @@ -147,6 +149,20 @@ class _DesktopWalletSelectionDropDownState extends State<DesktopWalletSelectionD } Future<void> _loadWallet(WalletListItem wallet) async { + if (SettingsStoreBase.walletPasswordDirectInput) { + Navigator.of(context).pushNamed( + Routes.walletUnlockLoadable, + arguments: WalletUnlockArguments( + callback: (bool isAuthenticatedSuccessfully, AuthPageState auth) async { + if (isAuthenticatedSuccessfully) { + auth.close(); + setState(() {}); + } + }, walletName: wallet.name, + walletType: wallet.type)); + return; + } + widget._authService.authenticateAction(context, onAuthSuccess: (isAuthenticatedSuccessfully) async { if (!isAuthenticatedSuccessfully) { diff --git a/lib/src/screens/dashboard/widgets/address_page.dart b/lib/src/screens/dashboard/widgets/address_page.dart index 236087595..5c2db79df 100644 --- a/lib/src/screens/dashboard/widgets/address_page.dart +++ b/lib/src/screens/dashboard/widgets/address_page.dart @@ -1,4 +1,5 @@ import 'package:cake_wallet/themes/extensions/keyboard_theme.dart'; +import 'package:cake_wallet/themes/extensions/keyboard_theme.dart'; import 'package:cake_wallet/anonpay/anonpay_donation_link_info.dart'; import 'package:cake_wallet/entities/preferences_key.dart'; import 'package:cake_wallet/entities/receive_page_option.dart'; @@ -6,9 +7,12 @@ import 'package:cake_wallet/src/screens/base_page.dart'; import 'package:cake_wallet/src/screens/dashboard/widgets/present_receive_option_picker.dart'; import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart'; import 'package:cake_wallet/src/widgets/gradient_background.dart'; +import 'package:cake_wallet/src/widgets/gradient_background.dart'; import 'package:cake_wallet/src/widgets/keyboard_done_button.dart'; import 'package:cake_wallet/themes/extensions/receive_page_theme.dart'; import 'package:cake_wallet/themes/extensions/sync_indicator_theme.dart'; +import 'package:cake_wallet/themes/extensions/receive_page_theme.dart'; +import 'package:cake_wallet/themes/extensions/sync_indicator_theme.dart'; import 'package:cake_wallet/themes/theme_base.dart'; import 'package:cake_wallet/utils/responsive_layout_util.dart'; import 'package:cake_wallet/utils/share_util.dart'; @@ -236,7 +240,7 @@ class AddressPage extends BasePage { reaction((_) => receiveOptionViewModel.selectedReceiveOption, (ReceivePageOption option) { switch (option) { case ReceivePageOption.anonPayInvoice: - Navigator.pushNamed( + Navigator.pushReplacementNamed( context, Routes.anonPayInvoicePage, arguments: [addressListViewModel.address.address, option], @@ -248,7 +252,7 @@ class AddressPage extends BasePage { final onionUrl = sharedPreferences.getString(PreferencesKey.onionDonationLink); if (clearnetUrl != null && onionUrl != null) { - Navigator.pushNamed( + Navigator.pushReplacementNamed( context, Routes.anonPayReceivePage, arguments: AnonpayDonationLinkInfo( @@ -258,7 +262,7 @@ class AddressPage extends BasePage { ), ); } else { - Navigator.pushNamed( + Navigator.pushReplacementNamed( context, Routes.anonPayInvoicePage, arguments: [addressListViewModel.address.address, option], diff --git a/lib/src/screens/dashboard/widgets/balance_page.dart b/lib/src/screens/dashboard/widgets/balance_page.dart index c8d7faf11..23a2d6d88 100644 --- a/lib/src/screens/dashboard/widgets/balance_page.dart +++ b/lib/src/screens/dashboard/widgets/balance_page.dart @@ -2,6 +2,7 @@ import 'package:cake_wallet/routes.dart'; import 'package:cake_wallet/src/screens/exchange_trade/information_page.dart'; import 'package:cake_wallet/store/settings_store.dart'; import 'package:cake_wallet/themes/extensions/sync_indicator_theme.dart'; +import 'package:cake_wallet/themes/extensions/sync_indicator_theme.dart'; import 'package:cake_wallet/utils/feature_flag.dart'; import 'package:cake_wallet/utils/show_pop_up.dart'; import 'package:flutter/material.dart'; @@ -12,6 +13,8 @@ import 'package:cake_wallet/src/widgets/introducing_card.dart'; import 'package:cake_wallet/generated/i18n.dart'; import 'package:cake_wallet/themes/extensions/dashboard_page_theme.dart'; import 'package:cake_wallet/themes/extensions/balance_page_theme.dart'; +import 'package:cake_wallet/themes/extensions/dashboard_page_theme.dart'; +import 'package:cake_wallet/themes/extensions/balance_page_theme.dart'; class BalancePage extends StatelessWidget { BalancePage({required this.dashboardViewModel, required this.settingsStore}); diff --git a/lib/src/screens/dashboard/widgets/market_place_page.dart b/lib/src/screens/dashboard/widgets/market_place_page.dart index 2d6539370..70098767d 100644 --- a/lib/src/screens/dashboard/widgets/market_place_page.dart +++ b/lib/src/screens/dashboard/widgets/market_place_page.dart @@ -82,7 +82,7 @@ class MarketPlacePage extends StatelessWidget { switch (walletType) { case WalletType.haven: - showPopUp<void>( + showPopUp<void>( context: context, builder: (BuildContext context) { return AlertWithOneAction( diff --git a/lib/src/screens/dashboard/widgets/transactions_page.dart b/lib/src/screens/dashboard/widgets/transactions_page.dart index 3f6b8a3c6..e7069c9bd 100644 --- a/lib/src/screens/dashboard/widgets/transactions_page.dart +++ b/lib/src/screens/dashboard/widgets/transactions_page.dart @@ -46,9 +46,11 @@ class TransactionsPage extends StatelessWidget { return Padding( padding: const EdgeInsets.fromLTRB(24, 0, 24, 8), child: DashBoardRoundedCardWidget( - onTap: () => Navigator.of(context).pushNamed( - Routes.webViewPage, - arguments: ['', Uri.parse('https://guides.cakewallet.com/docs/bugs-service-status/why_are_my_funds_not_appearing/')]), + onTap: () => Navigator.of(context).pushNamed(Routes.webViewPage, arguments: [ + '', + Uri.parse( + 'https://guides.cakewallet.com/docs/bugs-service-status/why_are_my_funds_not_appearing/') + ]), title: S.of(context).syncing_wallet_alert_title, subTitle: S.of(context).syncing_wallet_alert_content, ), @@ -76,40 +78,34 @@ class TransactionsPage extends StatelessWidget { return Observer( builder: (_) => TransactionRow( - onTap: () => Navigator.of(context).pushNamed( - Routes.transactionDetails, - arguments: transaction), + onTap: () => Navigator.of(context) + .pushNamed(Routes.transactionDetails, arguments: transaction), direction: transaction.direction, - formattedDate: DateFormat('HH:mm') - .format(transaction.date), + formattedDate: DateFormat('HH:mm').format(transaction.date), formattedAmount: item.formattedCryptoAmount, - formattedFiatAmount: dashboardViewModel - .balanceViewModel.isFiatDisabled - ? '' - : item.formattedFiatAmount, + formattedFiatAmount: + dashboardViewModel.balanceViewModel.isFiatDisabled + ? '' + : item.formattedFiatAmount, isPending: transaction.isPending, - title: item.formattedTitle + - item.formattedStatus)); + title: item.formattedTitle + item.formattedStatus)); } if (item is AnonpayTransactionListItem) { final transactionInfo = item.transaction; return AnonpayTransactionRow( - onTap: () => Navigator.of(context).pushNamed( - Routes.anonPayDetailsPage, - arguments: transactionInfo), + onTap: () => Navigator.of(context) + .pushNamed(Routes.anonPayDetailsPage, arguments: transactionInfo), currency: transactionInfo.fiatAmount != null ? transactionInfo.fiatEquiv ?? '' - : CryptoCurrency.fromFullName( - transactionInfo.coinTo) + : CryptoCurrency.fromFullName(transactionInfo.coinTo) .name .toUpperCase(), provider: transactionInfo.provider, amount: transactionInfo.fiatAmount?.toString() ?? (transactionInfo.amountTo?.toString() ?? ''), - createdAt: DateFormat('HH:mm') - .format(transactionInfo.createdAt), + createdAt: DateFormat('HH:mm').format(transactionInfo.createdAt), ); } @@ -118,17 +114,14 @@ class TransactionsPage extends StatelessWidget { return Observer( builder: (_) => TradeRow( - onTap: () => Navigator.of(context).pushNamed( - Routes.tradeDetails, - arguments: trade), + onTap: () => Navigator.of(context) + .pushNamed(Routes.tradeDetails, arguments: trade), provider: trade.provider, from: trade.from, to: trade.to, - createdAtFormattedDate: - trade.createdAt != null - ? DateFormat('HH:mm') - .format(trade.createdAt!) - : null, + createdAtFormattedDate: trade.createdAt != null + ? DateFormat('HH:mm').format(trade.createdAt!) + : null, formattedAmount: item.tradeFormattedAmount)); } @@ -138,13 +131,12 @@ class TransactionsPage extends StatelessWidget { return Observer( builder: (_) => OrderRow( onTap: () => Navigator.of(context) - .pushNamed(Routes.orderDetails, - arguments: order), + .pushNamed(Routes.orderDetails, arguments: order), provider: order.provider, from: order.from!, to: order.to!, - createdAtFormattedDate: DateFormat('HH:mm') - .format(order.createdAt), + createdAtFormattedDate: + DateFormat('HH:mm').format(order.createdAt), formattedAmount: item.orderFormattedAmount, )); } diff --git a/lib/src/screens/ionia/auth/ionia_welcome_page.dart b/lib/src/screens/ionia/auth/ionia_welcome_page.dart index e44e3a26d..1b29ffd10 100644 --- a/lib/src/screens/ionia/auth/ionia_welcome_page.dart +++ b/lib/src/screens/ionia/auth/ionia_welcome_page.dart @@ -4,11 +4,14 @@ import 'package:cake_wallet/routes.dart'; import 'package:cake_wallet/src/screens/base_page.dart'; import 'package:cake_wallet/src/widgets/primary_button.dart'; import 'package:cake_wallet/typography.dart'; +import 'package:cake_wallet/view_model/ionia/ionia_gift_cards_list_view_model.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/src/widgets/framework.dart'; import 'package:cake_wallet/generated/i18n.dart'; +import 'package:mobx/mobx.dart'; class IoniaWelcomePage extends BasePage { - IoniaWelcomePage(); + IoniaWelcomePage(this._cardsListViewModel); @override Widget middle(BuildContext context) { @@ -20,8 +23,15 @@ class IoniaWelcomePage extends BasePage { ); } + final IoniaGiftCardsListViewModel _cardsListViewModel; + @override Widget body(BuildContext context) { + reaction((_) => _cardsListViewModel.isLoggedIn, (bool state) { + if (state) { + Navigator.pushReplacementNamed(context, Routes.ioniaManageCardsPage); + } + }); return Padding( padding: const EdgeInsets.all(24.0), child: Column( @@ -29,7 +39,7 @@ class IoniaWelcomePage extends BasePage { children: [ Column( children: [ - SizedBox(height: 90), + SizedBox(height: 100), Text( S.of(context).about_cake_pay, style: TextStyle( diff --git a/lib/src/screens/new_wallet/new_wallet_page.dart b/lib/src/screens/new_wallet/new_wallet_page.dart index b9dcc5ae3..cc614453e 100644 --- a/lib/src/screens/new_wallet/new_wallet_page.dart +++ b/lib/src/screens/new_wallet/new_wallet_page.dart @@ -52,14 +52,18 @@ class _WalletNameFormState extends State<WalletNameForm> { _WalletNameFormState(this._walletNewVM) : _formKey = GlobalKey<FormState>(), _languageSelectorKey = GlobalKey<SeedLanguageSelectorState>(), - _controller = TextEditingController(); + _nameController = TextEditingController(), + _passwordController = _walletNewVM.hasWalletPassword ? TextEditingController() : null, + _repeatedPasswordController = _walletNewVM.hasWalletPassword ? TextEditingController() : null; static const aspectRatioImage = 1.22; final GlobalKey<FormState> _formKey; final GlobalKey<SeedLanguageSelectorState> _languageSelectorKey; final WalletNewVM _walletNewVM; - final TextEditingController _controller; + final TextEditingController _nameController; + final TextEditingController? _passwordController; + final TextEditingController? _repeatedPasswordController; ReactionDisposer? _stateReaction; @override @@ -110,12 +114,11 @@ class _WalletNameFormState extends State<WalletNameForm> { padding: EdgeInsets.only(top: 24), child: Form( key: _formKey, - child: Stack( - alignment: Alignment.centerRight, + child: Column( children: [ TextFormField( onChanged: (value) => _walletNewVM.name = value, - controller: _controller, + controller: _nameController, textAlign: TextAlign.center, style: TextStyle( fontSize: 20.0, @@ -144,10 +147,10 @@ class _WalletNameFormState extends State<WalletNameForm> { FocusManager.instance.primaryFocus?.unfocus(); setState(() { - _controller.text = rName; + _nameController.text = rName; _walletNewVM.name = rName; - _controller.selection = TextSelection.fromPosition( - TextPosition(offset: _controller.text.length)); + _nameController.selection = TextSelection.fromPosition( + TextPosition(offset: _nameController.text.length)); }); }, icon: Container( @@ -168,6 +171,59 @@ class _WalletNameFormState extends State<WalletNameForm> { ), validator: WalletNameValidator(), ), + if (_walletNewVM.hasWalletPassword) + ...[TextFormField( + onChanged: (value) => _walletNewVM.walletPassword = value, + controller: _passwordController, + textAlign: TextAlign.center, + obscureText: true, + style: TextStyle( + fontSize: 20.0, + fontWeight: FontWeight.w600, + color: Theme.of(context).extension<CakeTextTheme>()!.titleColor), + decoration: InputDecoration( + hintStyle: TextStyle( + fontSize: 18.0, + fontWeight: FontWeight.w500, + color: Theme.of(context).extension<NewWalletTheme>()!.hintTextColor), + hintText: S.of(context).password, + focusedBorder: UnderlineInputBorder( + borderSide: BorderSide( + color: Theme.of(context).extension<NewWalletTheme>()!.underlineColor, + width: 1.0)), + enabledBorder: UnderlineInputBorder( + borderSide: BorderSide( + color: Theme.of(context).extension<NewWalletTheme>()!.underlineColor, + width: 1.0), + ) + ) + ), + TextFormField( + onChanged: (value) => _walletNewVM.repeatedWalletPassword = value, + controller: _repeatedPasswordController, + textAlign: TextAlign.center, + obscureText: true, + style: TextStyle( + fontSize: 20.0, + fontWeight: FontWeight.w600, + color: Theme.of(context).extension<CakeTextTheme>()!.titleColor), + decoration: InputDecoration( + hintStyle: TextStyle( + fontSize: 18.0, + fontWeight: FontWeight.w500, + color: Theme.of(context).extension<NewWalletTheme>()!.hintTextColor), + hintText: S.of(context).repeate_wallet_password, + focusedBorder: UnderlineInputBorder( + borderSide: BorderSide( + color: Theme.of(context).extension<NewWalletTheme>()!.underlineColor, + width: 1.0)), + enabledBorder: UnderlineInputBorder( + borderSide: BorderSide( + color: Theme.of(context).extension<NewWalletTheme>()!.underlineColor, + width: 1.0), + ) + ) + )], ], ), ), diff --git a/lib/src/screens/receive/widgets/currency_input_field.dart b/lib/src/screens/receive/widgets/currency_input_field.dart index 20e7bd660..2dbb36297 100644 --- a/lib/src/screens/receive/widgets/currency_input_field.dart +++ b/lib/src/screens/receive/widgets/currency_input_field.dart @@ -1,5 +1,4 @@ import 'package:cake_wallet/src/widgets/base_text_form_field.dart'; -import 'package:cake_wallet/utils/responsive_layout_util.dart'; import 'package:cw_core/currency.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; @@ -13,8 +12,7 @@ class CurrencyInputField extends StatelessWidget { required this.onTapPicker, required this.selectedCurrency, this.focusNode, - required this.controller, - required this.isLight, + required this.controller, required this.isLight, }); final Function() onTapPicker; @@ -30,9 +28,7 @@ class CurrencyInputField extends StatelessWidget { color: Theme.of(context).extension<DashboardPageTheme>()!.textColor, height: 8, ); - // This magic number for wider screen sets the text input focus at center of the inputfield - final _width = - ResponsiveLayoutUtil.instance.isMobile ? MediaQuery.of(context).size.width : 500; + final _width = MediaQuery.of(context).size.width; return Column( children: [ diff --git a/lib/src/screens/restore/restore_options_page.dart b/lib/src/screens/restore/restore_options_page.dart index 74e33b87a..93a44c752 100644 --- a/lib/src/screens/restore/restore_options_page.dart +++ b/lib/src/screens/restore/restore_options_page.dart @@ -2,10 +2,12 @@ import 'package:cake_wallet/core/execution_state.dart'; import 'package:cake_wallet/di.dart'; import 'package:cake_wallet/src/screens/pin_code/pin_code_widget.dart'; import 'package:cake_wallet/src/widgets/alert_with_one_action.dart'; +import 'package:cake_wallet/utils/language_list.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/utils/responsive_layout_util.dart'; +import 'package:cake_wallet/wallet_type_utils.dart'; import 'package:flutter/material.dart'; import 'package:cake_wallet/routes.dart'; import 'package:flutter/cupertino.dart'; diff --git a/lib/src/screens/restore/wallet_restore_from_keys_form.dart b/lib/src/screens/restore/wallet_restore_from_keys_form.dart index a1e2a82f0..e73e3ffa9 100644 --- a/lib/src/screens/restore/wallet_restore_from_keys_form.dart +++ b/lib/src/screens/restore/wallet_restore_from_keys_form.dart @@ -15,19 +15,25 @@ class WalletRestoreFromKeysFrom extends StatefulWidget { required this.walletRestoreViewModel, required this.displayPrivateKeyField, required this.onHeightOrDateEntered, + required this.displayWalletPassword, + required this.onRepeatedPasswordChange, + this.onPasswordChange, Key? key, }) : super(key: key); final Function(bool) onHeightOrDateEntered; final WalletRestoreViewModel walletRestoreViewModel; final bool displayPrivateKeyField; + final bool displayWalletPassword; + final void Function(String)? onPasswordChange; + final void Function(String)? onRepeatedPasswordChange; @override - WalletRestoreFromKeysFromState createState() => WalletRestoreFromKeysFromState(); + WalletRestoreFromKeysFromState createState() => WalletRestoreFromKeysFromState(displayWalletPassword: displayWalletPassword); } class WalletRestoreFromKeysFromState extends State<WalletRestoreFromKeysFrom> { - WalletRestoreFromKeysFromState() + WalletRestoreFromKeysFromState({required bool displayWalletPassword}) : formKey = GlobalKey<FormState>(), blockchainHeightKey = GlobalKey<BlockchainHeightState>(), nameController = TextEditingController(), @@ -35,7 +41,9 @@ class WalletRestoreFromKeysFromState extends State<WalletRestoreFromKeysFrom> { viewKeyController = TextEditingController(), spendKeyController = TextEditingController(), privateKeyController = TextEditingController(), - nameTextEditingController = TextEditingController(); + nameTextEditingController = TextEditingController(), + passwordTextEditingController = displayWalletPassword ? TextEditingController() : null, + repeatedPasswordTextEditingController = displayWalletPassword ? TextEditingController() : null; final GlobalKey<FormState> formKey; final GlobalKey<BlockchainHeightState> blockchainHeightKey; @@ -45,9 +53,22 @@ class WalletRestoreFromKeysFromState extends State<WalletRestoreFromKeysFrom> { final TextEditingController spendKeyController; final TextEditingController nameTextEditingController; final TextEditingController privateKeyController; + final TextEditingController? passwordTextEditingController; + final TextEditingController? repeatedPasswordTextEditingController; + void Function()? passwordListener; + void Function()? repeatedPasswordListener; @override void initState() { + if (passwordTextEditingController != null) { + passwordListener = () => widget.onPasswordChange?.call(passwordTextEditingController!.text); + passwordTextEditingController?.addListener(passwordListener!); + } + + if (repeatedPasswordTextEditingController != null) { + repeatedPasswordListener = () => widget.onRepeatedPasswordChange?.call(repeatedPasswordTextEditingController!.text); + repeatedPasswordTextEditingController?.addListener(repeatedPasswordListener!); + } super.initState(); privateKeyController.addListener(() { @@ -57,6 +78,7 @@ class WalletRestoreFromKeysFromState extends State<WalletRestoreFromKeysFrom> { }); } + @override void dispose() { nameController.dispose(); @@ -64,6 +86,14 @@ class WalletRestoreFromKeysFromState extends State<WalletRestoreFromKeysFrom> { viewKeyController.dispose(); spendKeyController.dispose(); privateKeyController.dispose(); + passwordTextEditingController?.dispose(); + if (passwordListener != null) { + passwordTextEditingController?.removeListener(passwordListener!); + } + + if (repeatedPasswordListener != null) { + repeatedPasswordTextEditingController?.removeListener(repeatedPasswordListener!); + } super.dispose(); } @@ -111,6 +141,19 @@ class WalletRestoreFromKeysFromState extends State<WalletRestoreFromKeysFrom> { ), ], ), + if (widget.displayWalletPassword) + ...[Container( + padding: EdgeInsets.only(top: 20.0), + child: BaseTextFormField( + controller: passwordTextEditingController, + hintText: S.of(context).password, + obscureText: true)), + Container( + padding: EdgeInsets.only(top: 20.0), + child: BaseTextFormField( + controller: repeatedPasswordTextEditingController, + hintText: S.of(context).repeate_wallet_password, + obscureText: true))], Container(height: 20), _restoreFromKeysFormFields(), ], diff --git a/lib/src/screens/restore/wallet_restore_from_seed_form.dart b/lib/src/screens/restore/wallet_restore_from_seed_form.dart index d10277ced..9c193daf3 100644 --- a/lib/src/screens/restore/wallet_restore_from_seed_form.dart +++ b/lib/src/screens/restore/wallet_restore_from_seed_form.dart @@ -19,46 +19,79 @@ class WalletRestoreFromSeedForm extends StatefulWidget { required this.displayLanguageSelector, required this.displayBlockHeightSelector, required this.type, + required this.displayWalletPassword, this.blockHeightFocusNode, this.onHeightOrDateEntered, this.onSeedChange, - this.onLanguageChange}) + this.onLanguageChange, + this.onPasswordChange, + this.onRepeatedPasswordChange}) : super(key: key); final WalletType type; final bool displayLanguageSelector; final bool displayBlockHeightSelector; + final bool displayWalletPassword; final FocusNode? blockHeightFocusNode; final Function(bool)? onHeightOrDateEntered; final void Function(String)? onSeedChange; final void Function(String)? onLanguageChange; + final void Function(String)? onPasswordChange; + final void Function(String)? onRepeatedPasswordChange; @override WalletRestoreFromSeedFormState createState() => - WalletRestoreFromSeedFormState('English'); + WalletRestoreFromSeedFormState('English', displayWalletPassword: displayWalletPassword); } class WalletRestoreFromSeedFormState extends State<WalletRestoreFromSeedForm> { - WalletRestoreFromSeedFormState(this.language) + WalletRestoreFromSeedFormState(this.language, {required bool displayWalletPassword}) : seedWidgetStateKey = GlobalKey<SeedWidgetState>(), blockchainHeightKey = GlobalKey<BlockchainHeightState>(), formKey = GlobalKey<FormState>(), languageController = TextEditingController(), - nameTextEditingController = TextEditingController(); + nameTextEditingController = TextEditingController(), + passwordTextEditingController = displayWalletPassword ? TextEditingController() : null, + repeatedPasswordTextEditingController = displayWalletPassword ? TextEditingController() : null; final GlobalKey<SeedWidgetState> seedWidgetStateKey; final GlobalKey<BlockchainHeightState> blockchainHeightKey; final TextEditingController languageController; final TextEditingController nameTextEditingController; + final TextEditingController? passwordTextEditingController; + final TextEditingController? repeatedPasswordTextEditingController; final GlobalKey<FormState> formKey; String language; + void Function()? passwordListener; + void Function()? repeatedPasswordListener; @override void initState() { _setLanguageLabel(language); + if (passwordTextEditingController != null) { + passwordListener = () => widget.onPasswordChange?.call(passwordTextEditingController!.text); + passwordTextEditingController?.addListener(passwordListener!); + } + + if (repeatedPasswordTextEditingController != null) { + repeatedPasswordListener = () => widget.onRepeatedPasswordChange?.call(repeatedPasswordTextEditingController!.text); + repeatedPasswordTextEditingController?.addListener(repeatedPasswordListener!); + } super.initState(); } + @override + void dispose() { + if (passwordListener != null) { + passwordTextEditingController?.removeListener(passwordListener!); + } + + if (repeatedPasswordListener != null) { + repeatedPasswordTextEditingController?.removeListener(repeatedPasswordListener!); + } + super.dispose(); + } + @override Widget build(BuildContext context) { return Container( @@ -108,6 +141,15 @@ class WalletRestoreFromSeedFormState extends State<WalletRestoreFromSeedForm> { language: language, type: widget.type, onSeedChange: widget.onSeedChange), + if (widget.displayWalletPassword) + ...[BaseTextFormField( + controller: passwordTextEditingController, + hintText: S.of(context).password, + obscureText: true), + BaseTextFormField( + controller: repeatedPasswordTextEditingController, + hintText: S.of(context).repeate_wallet_password, + obscureText: true)], if (widget.displayLanguageSelector) GestureDetector( onTap: () async { @@ -129,7 +171,7 @@ class WalletRestoreFromSeedFormState extends State<WalletRestoreFromSeedForm> { focusNode: widget.blockHeightFocusNode, key: blockchainHeightKey, onHeightOrDateEntered: widget.onHeightOrDateEntered, - hasDatePicker: widget.type == WalletType.monero) + hasDatePicker: widget.type == WalletType.monero), ])); } diff --git a/lib/src/screens/restore/wallet_restore_page.dart b/lib/src/screens/restore/wallet_restore_page.dart index bea84a7c9..0e9d66313 100644 --- a/lib/src/screens/restore/wallet_restore_page.dart +++ b/lib/src/screens/restore/wallet_restore_page.dart @@ -63,13 +63,19 @@ class WalletRestorePage extends BasePage { } else { walletRestoreViewModel.isButtonEnabled = _isValidSeed(); } - })); + }, + displayWalletPassword: walletRestoreViewModel.hasWalletPassword, + onPasswordChange: (String password) => walletRestoreViewModel.walletPassword = password, + onRepeatedPasswordChange: (String repeatedPassword) => walletRestoreViewModel.repeatedWalletPassword = repeatedPassword)); break; case WalletRestoreMode.keys: _pages.add(WalletRestoreFromKeysFrom( key: walletRestoreFromKeysFormKey, walletRestoreViewModel: walletRestoreViewModel, displayPrivateKeyField: walletRestoreViewModel.hasRestoreFromPrivateKey, + displayWalletPassword: walletRestoreViewModel.hasWalletPassword, + onPasswordChange: (String password) => walletRestoreViewModel.walletPassword = password, + onRepeatedPasswordChange: (String repeatedPassword) => walletRestoreViewModel.repeatedWalletPassword = repeatedPassword, onHeightOrDateEntered: (value) => walletRestoreViewModel.isButtonEnabled = value)); break; default: @@ -118,6 +124,8 @@ class WalletRestorePage extends BasePage { reaction((_) => walletRestoreViewModel.mode, (WalletRestoreMode mode) { walletRestoreViewModel.isButtonEnabled = false; + walletRestoreViewModel.walletPassword = null; + walletRestoreViewModel.repeatedWalletPassword = null; walletRestoreFromSeedFormKey .currentState!.blockchainHeightKey.currentState!.restoreHeightController.text = ''; @@ -264,8 +272,6 @@ class WalletRestorePage extends BasePage { } void _confirmForm() { - // Dismissing all visible keyboard to provide context for navigation - FocusManager.instance.primaryFocus?.unfocus(); final formContext = walletRestoreViewModel.mode == WalletRestoreMode.seed ? walletRestoreFromSeedFormKey.currentContext : walletRestoreFromKeysFormKey.currentContext; diff --git a/lib/src/screens/restore/widgets/restore_button.dart b/lib/src/screens/restore/widgets/restore_button.dart index c196de059..85a44a326 100644 --- a/lib/src/screens/restore/widgets/restore_button.dart +++ b/lib/src/screens/restore/widgets/restore_button.dart @@ -3,11 +3,11 @@ import 'package:flutter/material.dart'; import 'package:cake_wallet/themes/extensions/transaction_trade_theme.dart'; class RestoreButton extends StatelessWidget { - const RestoreButton( - {required this.onPressed, - required this.image, - required this.title, - required this.description}); + const RestoreButton({ + required this.onPressed, + required this.image, + required this.title, + required this.description}); final VoidCallback onPressed; final Image image; diff --git a/lib/src/screens/root/root.dart b/lib/src/screens/root/root.dart index 3298a50c0..4bc7cb81b 100644 --- a/lib/src/screens/root/root.dart +++ b/lib/src/screens/root/root.dart @@ -1,4 +1,5 @@ import 'dart:async'; +import 'dart:io'; import 'package:cake_wallet/core/auth_service.dart'; import 'package:cake_wallet/core/totp_request_details.dart'; import 'package:cake_wallet/utils/device_info.dart'; diff --git a/lib/src/screens/send/send_page.dart b/lib/src/screens/send/send_page.dart index b24e9b01f..82e2842f7 100644 --- a/lib/src/screens/send/send_page.dart +++ b/lib/src/screens/send/send_page.dart @@ -363,14 +363,13 @@ class SendPage extends BasePage { showErrorValidationAlert(context); } - return; - } + return; + } - final notValidItems = sendViewModel.outputs - .where((item) => - item.address.isEmpty || - item.cryptoAmount.isEmpty) - .toList(); + final notValidItems = sendViewModel.outputs + .where((item) => + item.address.isEmpty || item.cryptoAmount.isEmpty) + .toList(); if (notValidItems.isNotEmpty) { showErrorValidationAlert(context); @@ -428,54 +427,51 @@ class SendPage extends BasePage { WidgetsBinding.instance.addPostFrameCallback((_) { if (context.mounted) { showPopUp<void>( - context: context, - builder: (BuildContext context) { - return ConfirmSendingAlert( - alertTitle: S.of(context).confirm_sending, - amount: S.of(context).send_amount, - amountValue: - sendViewModel.pendingTransaction!.amountFormatted, - fiatAmountValue: - sendViewModel.pendingTransactionFiatAmountFormatted, - fee: S.of(context).send_fee, - feeValue: sendViewModel.pendingTransaction!.feeFormatted, - feeFiatAmount: sendViewModel - .pendingTransactionFeeFiatAmountFormatted, - outputs: sendViewModel.outputs, - rightButtonText: S.of(context).ok, - leftButtonText: S.of(context).cancel, - actionRightButton: () { - Navigator.of(context).pop(); - sendViewModel.commitTransaction(); - showPopUp<void>( - context: context, - builder: (BuildContext context) { - return Observer(builder: (_) { - final state = sendViewModel.state; + context: context, + builder: (BuildContext context) { + return ConfirmSendingAlert( + alertTitle: S.of(context).confirm_sending, + amount: S.of(context).send_amount, + amountValue: + sendViewModel.pendingTransaction!.amountFormatted, + fiatAmountValue: sendViewModel.pendingTransactionFiatAmountFormatted, + fee: S.of(context).send_fee, + feeValue: sendViewModel.pendingTransaction!.feeFormatted, + feeFiatAmount: sendViewModel.pendingTransactionFeeFiatAmountFormatted, + outputs: sendViewModel.outputs, + rightButtonText: S.of(context).ok, + leftButtonText: S.of(context).cancel, + actionRightButton: () { + Navigator.of(context).pop(); + sendViewModel.commitTransaction(); + showPopUp<void>( + context: context, + builder: (BuildContext context) { + return Observer(builder: (_) { + final state = sendViewModel.state; - if (state is FailureState) { - Navigator.of(context).pop(); + if (state is FailureState) { + Navigator.of(context).pop(); + } + + if (state is TransactionCommitted) { + return AlertWithOneAction( + alertTitle: '', + alertContent: S.of(context).send_success( + sendViewModel.selectedCryptoCurrency.toString()), + buttonText: S.of(context).ok, + buttonAction: () { + Navigator.of(context).pop(); + RequestReviewHandler.requestReview(); + }); } - if (state is TransactionCommitted) { - return AlertWithOneAction( - alertTitle: '', - alertContent: S.of(context).send_success( - sendViewModel.selectedCryptoCurrency - .toString()), - buttonText: S.of(context).ok, - buttonAction: () { - Navigator.of(context).pop(); - RequestReviewHandler.requestReview(); - }); - } - - return Offstage(); - }); + return Offstage(); }); - }, - actionLeftButton: () => Navigator.of(context).pop()); - }); + }); + }, + actionLeftButton: () => Navigator.of(context).pop()); + }); } }); } @@ -529,18 +525,16 @@ class SendPage extends BasePage { }); } - void presentCurrencyPicker(BuildContext context) async { + void presentCurrencyPicker(BuildContext context) async { await showPopUp<CryptoCurrency>( builder: (_) => Picker( - items: sendViewModel.currencies, - displayItem: (Object item) => item.toString(), - selectedAtIndex: sendViewModel.currencies - .indexOf(sendViewModel.selectedCryptoCurrency), - title: S.of(context).please_select, - mainAxisAlignment: MainAxisAlignment.center, - onItemSelected: (CryptoCurrency cur) => - sendViewModel.selectedCryptoCurrency = cur, - ), + items: sendViewModel.currencies, + displayItem: (Object item) => item.toString(), + selectedAtIndex: sendViewModel.currencies.indexOf(sendViewModel.selectedCryptoCurrency), + title: S.of(context).please_select, + mainAxisAlignment: MainAxisAlignment.center, + onItemSelected: (CryptoCurrency cur) => sendViewModel.selectedCryptoCurrency = cur, + ), context: context); } } diff --git a/lib/src/screens/settings/display_settings_page.dart b/lib/src/screens/settings/display_settings_page.dart index 6e572abe0..4c8acfe6f 100644 --- a/lib/src/screens/settings/display_settings_page.dart +++ b/lib/src/screens/settings/display_settings_page.dart @@ -27,11 +27,11 @@ class DisplaySettingsPage extends BasePage { child: Column( children: [ SettingsSwitcherCell( - title: S.current.settings_display_balance, - value: _displaySettingsViewModel.shouldDisplayBalance, - onValueChange: (_, bool value) { - _displaySettingsViewModel.setShouldDisplayBalance(value); - }), + title: S.current.settings_display_balance, + value: _displaySettingsViewModel.shouldDisplayBalance, + onValueChange: (_, bool value) { + _displaySettingsViewModel.setShouldDisplayBalance(value); + }), SettingsSwitcherCell( title: S.current.show_market_place, value: _displaySettingsViewModel.shouldShowMarketPlaceInDashboard, @@ -40,17 +40,14 @@ class DisplaySettingsPage extends BasePage { }, ), //if (!isHaven) it does not work correctly - if (!_displaySettingsViewModel.disabledFiatApiMode) + if(!_displaySettingsViewModel.disabledFiatApiMode) SettingsPickerCell<FiatCurrency>( title: S.current.settings_currency, searchHintText: S.current.search_currency, items: FiatCurrency.all, selectedItem: _displaySettingsViewModel.fiatCurrency, - onItemSelected: (FiatCurrency currency) => - _displaySettingsViewModel.setFiatCurrency(currency), - images: FiatCurrency.all - .map((e) => Image.asset("assets/images/flags/${e.countryCode}.png")) - .toList(), + onItemSelected: (FiatCurrency currency) => _displaySettingsViewModel.setFiatCurrency(currency), + images: FiatCurrency.all.map((e) => Image.asset("assets/images/flags/${e.countryCode}.png")).toList(), isGridView: true, matchingCriteria: (FiatCurrency currency, String searchText) { return currency.title.toLowerCase().contains(searchText) || @@ -67,8 +64,7 @@ class DisplaySettingsPage extends BasePage { selectedItem: _displaySettingsViewModel.languageCode, onItemSelected: _displaySettingsViewModel.onLanguageSelected, images: LanguageService.list.keys - .map((e) => Image.asset( - "assets/images/flags/${LanguageService.localeCountryCode[e]}.png")) + .map((e) => Image.asset("assets/images/flags/${LanguageService.localeCountryCode[e]}.png")) .toList(), matchingCriteria: (String code, String searchText) { return LanguageService.list[code]?.toLowerCase().contains(searchText) ?? false; diff --git a/lib/src/screens/settings/security_backup_page.dart b/lib/src/screens/settings/security_backup_page.dart index a0fb16cb6..13ec50db1 100644 --- a/lib/src/screens/settings/security_backup_page.dart +++ b/lib/src/screens/settings/security_backup_page.dart @@ -9,6 +9,7 @@ import 'package:cake_wallet/src/screens/settings/widgets/settings_picker_cell.da import 'package:cake_wallet/src/screens/settings/widgets/settings_switcher_cell.dart'; import 'package:cake_wallet/src/widgets/standard_list.dart'; import 'package:cake_wallet/utils/device_info.dart'; +import 'package:cake_wallet/store/settings_store.dart'; import 'package:cake_wallet/view_model/settings/security_settings_view_model.dart'; import 'package:flutter/material.dart'; import 'package:flutter_mobx/flutter_mobx.dart'; @@ -26,28 +27,32 @@ class SecurityBackupPage extends BasePage { @override Widget body(BuildContext context) { return Container( - padding: EdgeInsets.only(top: 10), - child: Column(mainAxisSize: MainAxisSize.min, children: [ + padding: EdgeInsets.only(top: 10), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ SettingsCellWithArrow( title: S.current.show_keys, handler: (_) => _authService.authenticateAction( context, route: Routes.showKeys, - conditionToDetermineIfToUse2FA: _securitySettingsViewModel - .shouldRequireTOTP2FAForAllSecurityAndBackupSettings, + conditionToDetermineIfToUse2FA: + _securitySettingsViewModel.shouldRequireTOTP2FAForAllSecurityAndBackupSettings, ), ), StandardListSeparator(padding: EdgeInsets.symmetric(horizontal: 24)), - SettingsCellWithArrow( - title: S.current.create_backup, - handler: (_) => _authService.authenticateAction( - context, - route: Routes.backup, - conditionToDetermineIfToUse2FA: _securitySettingsViewModel - .shouldRequireTOTP2FAForAllSecurityAndBackupSettings, + if (!SettingsStoreBase.walletPasswordDirectInput) ...[ + SettingsCellWithArrow( + title: S.current.create_backup, + handler: (_) => _authService.authenticateAction( + context, + route: Routes.backup, + conditionToDetermineIfToUse2FA: + _securitySettingsViewModel.shouldRequireTOTP2FAForAllSecurityAndBackupSettings, + ), ), - ), - StandardListSeparator(padding: EdgeInsets.symmetric(horizontal: 24)), + StandardListSeparator(padding: EdgeInsets.symmetric(horizontal: 24)), + ], SettingsCellWithArrow( title: S.current.settings_change_pin, handler: (_) => _authService.authenticateAction( @@ -56,8 +61,8 @@ class SecurityBackupPage extends BasePage { arguments: (PinCodeState<PinCodeWidget> setupPinContext, String _) { setupPinContext.close(); }, - conditionToDetermineIfToUse2FA: _securitySettingsViewModel - .shouldRequireTOTP2FAForAllSecurityAndBackupSettings, + conditionToDetermineIfToUse2FA: + _securitySettingsViewModel.shouldRequireTOTP2FAForAllSecurityAndBackupSettings, ), ), StandardListSeparator(padding: EdgeInsets.symmetric(horizontal: 24)), @@ -68,17 +73,18 @@ class SecurityBackupPage extends BasePage { value: _securitySettingsViewModel.allowBiometricalAuthentication, onValueChange: (BuildContext context, bool value) { if (value) { - _authService.authenticateAction(context, - onAuthSuccess: (isAuthenticatedSuccessfully) async { - if (isAuthenticatedSuccessfully) { - if (await _securitySettingsViewModel.biometricAuthenticated()) { + _authService.authenticateAction( + context, + onAuthSuccess: (isAuthenticatedSuccessfully) async { + if (isAuthenticatedSuccessfully) { + if (await _securitySettingsViewModel.biometricAuthenticated()) { + _securitySettingsViewModel + .setAllowBiometricalAuthentication(isAuthenticatedSuccessfully); + } + } else { _securitySettingsViewModel .setAllowBiometricalAuthentication(isAuthenticatedSuccessfully); } - } else { - _securitySettingsViewModel - .setAllowBiometricalAuthentication(isAuthenticatedSuccessfully); - } }, conditionToDetermineIfToUse2FA: _securitySettingsViewModel .shouldRequireTOTP2FAForAllSecurityAndBackupSettings, @@ -104,11 +110,11 @@ class SecurityBackupPage extends BasePage { title: _securitySettingsViewModel.useTotp2FA ? S.current.modify_2fa : S.current.setup_2fa, - handler: (_) => _authService.authenticateAction( - context, - route: _securitySettingsViewModel.useTotp2FA - ? Routes.modify2FAPage - : Routes.setup_2faPage, + handler: (_) => _authService.authenticateAction( + context, + route: _securitySettingsViewModel.useTotp2FA + ? Routes.modify2FAPage + : Routes.setup_2faPage, conditionToDetermineIfToUse2FA: _securitySettingsViewModel .shouldRequireTOTP2FAForAllSecurityAndBackupSettings, ), @@ -118,6 +124,5 @@ class SecurityBackupPage extends BasePage { ], ), ); - } } diff --git a/lib/src/screens/setup_2fa/setup_2fa_enter_code_page.dart b/lib/src/screens/setup_2fa/setup_2fa_enter_code_page.dart index 70660c59d..37d7dc0bf 100644 --- a/lib/src/screens/setup_2fa/setup_2fa_enter_code_page.dart +++ b/lib/src/screens/setup_2fa/setup_2fa_enter_code_page.dart @@ -200,7 +200,7 @@ class TOTPEnterCode extends BasePage { Navigator.pushReplacementNamed( context, Routes.modify2FAPage); } - + }, text: S.of(context).continue_text, color: Theme.of(context).primaryColor, diff --git a/lib/src/screens/support/support_page.dart b/lib/src/screens/support/support_page.dart index 883677832..f13bcd1d5 100644 --- a/lib/src/screens/support/support_page.dart +++ b/lib/src/screens/support/support_page.dart @@ -1,78 +1,51 @@ -import 'package:cake_wallet/generated/i18n.dart'; -import 'package:cake_wallet/routes.dart'; -import 'package:cake_wallet/src/screens/support/widgets/support_tiles.dart'; -import 'package:cake_wallet/src/screens/base_page.dart'; -import 'package:cake_wallet/utils/device_info.dart'; +import 'package:cake_wallet/src/screens/settings/widgets/settings_cell_with_arrow.dart'; +import 'package:cake_wallet/src/screens/settings/widgets/settings_link_provider_cell.dart'; +import 'package:cake_wallet/src/widgets/standard_list.dart'; +import 'package:cake_wallet/themes/extensions/support_page_theme.dart'; +import 'package:cake_wallet/view_model/settings/link_list_item.dart'; +import 'package:cake_wallet/view_model/settings/regular_list_item.dart'; import 'package:cake_wallet/view_model/support_view_model.dart'; import 'package:flutter/material.dart'; -import 'package:url_launcher/url_launcher.dart'; +import 'package:cake_wallet/src/screens/base_page.dart'; +import 'package:cake_wallet/generated/i18n.dart'; class SupportPage extends BasePage { SupportPage(this.supportViewModel); final SupportViewModel supportViewModel; - final imageLiveSupport = Image.asset('assets/images/live_support.png'); - final imageWalletGuides = Image.asset('assets/images/wallet_guides.png'); - final imageMoreLinks = Image.asset('assets/images/more_links.png'); - @override String get title => S.current.settings_support; - @override - AppBarStyle get appBarStyle => AppBarStyle.regular; - @override Widget body(BuildContext context) { - return Container( - child: Center( - child: ConstrainedBox( - constraints: BoxConstraints(maxWidth: 330), - child: Column( - children: [ - Padding( - padding: EdgeInsets.only(top: 24), - child: SupportTile( - image: imageLiveSupport, - title: S.of(context).support_title_live_chat, - description: S.of(context).support_description_live_chat, - onPressed: () { - if (DeviceInfo.instance.isDesktop) { - _launchUrl(supportViewModel.fetchUrl()); - } else { - Navigator.pushNamed(context, Routes.supportLiveChat); - } - }, - ), - ), - Padding( - padding: EdgeInsets.only(top: 24), - child: SupportTile( - image: imageWalletGuides, - title: S.of(context).support_title_guides, - description: S.of(context).support_description_guides, - onPressed: () => _launchUrl(supportViewModel.guidesUrl), - ), - ), - Padding( - padding: EdgeInsets.only(top: 24), - child: SupportTile( - image: imageMoreLinks, - title: S.of(context).support_title_other_links, - description: S.of(context).support_description_other_links, - onPressed: () => Navigator.pushNamed(context, Routes.supportOtherLinks), - ), - ), - ], - ), - ), + final iconColor = Theme.of(context).extension<SupportPageTheme>()!.iconColor; + // FIX-ME: Added `context` it was not used here before, maby bug ? + return Center( + child: ConstrainedBox( + constraints: BoxConstraints(maxWidth: 500), + child: SectionStandardList( + sectionCount: 1, + itemCounter: (int _) => supportViewModel.items.length, + itemBuilder: (_, index) { + final item = supportViewModel.items[index]; + + if (item is RegularListItem) { + return SettingsCellWithArrow(title: item.title, handler: item.handler); + } + + if (item is LinkListItem) { + return SettingsLinkProviderCell( + title: item.title, + icon: item.icon, + iconColor: item.hasIconColor ? iconColor : null, + link: item.link, + linkTitle: item.linkTitle); + } + + return Container(); + }), ), ); } - - void _launchUrl(String url) async { - try { - await launchUrl(Uri.parse(url), mode: LaunchMode.externalApplication); - } catch (e) {} - } } diff --git a/lib/src/screens/support_chat/support_chat_page.dart b/lib/src/screens/support_chat/support_chat_page.dart deleted file mode 100644 index e84965b23..000000000 --- a/lib/src/screens/support_chat/support_chat_page.dart +++ /dev/null @@ -1,38 +0,0 @@ -import 'package:cake_wallet/generated/i18n.dart'; -import 'package:cake_wallet/src/screens/base_page.dart'; -import 'package:cake_wallet/src/screens/support_chat/widgets/chatwoot_widget.dart'; -import 'package:cake_wallet/view_model/support_view_model.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_secure_storage/flutter_secure_storage.dart'; - - -class SupportChatPage extends BasePage { - SupportChatPage(this.supportViewModel, {required this.secureStorage}); - - final SupportViewModel supportViewModel; - final FlutterSecureStorage secureStorage; - - @override - String get title => S.current.settings_support; - - @override - AppBarStyle get appBarStyle => AppBarStyle.regular; - - @override - Widget body(BuildContext context) => FutureBuilder<String>( - future: getCookie(), - builder: (BuildContext context, AsyncSnapshot<String> snapshot) { - print(snapshot.data); - if (snapshot.hasData) - return ChatwootWidget( - secureStorage, - supportUrl: supportViewModel.fetchUrl(authToken: snapshot.data!) - ); - return Container(); - }, - ); - - Future<String> getCookie() async { - return await secureStorage.read(key: COOKIE_KEY) ?? ""; - } -} diff --git a/lib/src/screens/support_chat/widgets/chatwoot_widget.dart b/lib/src/screens/support_chat/widgets/chatwoot_widget.dart deleted file mode 100644 index d9a5f9bb8..000000000 --- a/lib/src/screens/support_chat/widgets/chatwoot_widget.dart +++ /dev/null @@ -1,62 +0,0 @@ -import 'dart:convert'; - -import 'package:flutter/material.dart'; -import 'package:flutter_inappwebview/flutter_inappwebview.dart'; -import 'package:flutter_secure_storage/flutter_secure_storage.dart'; - -const COOKIE_KEY = 'chatwootCookie'; - -class ChatwootWidget extends StatefulWidget { - ChatwootWidget(this.secureStorage, {required this.supportUrl}); - - final FlutterSecureStorage secureStorage; - final String supportUrl; - - @override - ChatwootWidgetState createState() => ChatwootWidgetState(); -} - -class ChatwootWidgetState extends State<ChatwootWidget> { - final GlobalKey _webViewkey = GlobalKey(); - - @override - Widget build(BuildContext context) => InAppWebView( - key: _webViewkey, - initialOptions: InAppWebViewGroupOptions( - crossPlatform: InAppWebViewOptions(transparentBackground: true), - ), - initialUrlRequest: URLRequest(url: Uri.tryParse(widget.supportUrl)), - onWebViewCreated: (InAppWebViewController controller) { - controller.addWebMessageListener( - WebMessageListener( - jsObjectName: 'ReactNativeWebView', - onPostMessage: (String? message, Uri? sourceOrigin, bool isMainFrame, - JavaScriptReplyProxy replyProxy) { - final shortenedMessage = message?.substring(16); - if (shortenedMessage != null && isJsonString(shortenedMessage)) { - final parsedMessage = jsonDecode(shortenedMessage); - final eventType = parsedMessage["event"]; - if (eventType == 'loaded') { - final authToken = parsedMessage["config"]["authToken"]; - print(authToken); - storeCookie(authToken as String); - } - } - }, - ), - ); - }, - ); - - bool isJsonString(String str) { - try { - jsonDecode(str); - } catch (e) { - return false; - } - return true; - } - - Future<void> storeCookie(String value) async => - await widget.secureStorage.write(key: COOKIE_KEY, value: value); -} diff --git a/lib/src/screens/wallet/wallet_edit_page.dart b/lib/src/screens/wallet/wallet_edit_page.dart index 7c90ba2c5..f32528667 100644 --- a/lib/src/screens/wallet/wallet_edit_page.dart +++ b/lib/src/screens/wallet/wallet_edit_page.dart @@ -2,8 +2,12 @@ import 'package:another_flushbar/flushbar.dart'; import 'package:cake_wallet/core/auth_service.dart'; import 'package:cake_wallet/core/wallet_name_validator.dart'; import 'package:cake_wallet/palette.dart'; +import 'package:cake_wallet/routes.dart'; +import 'package:cake_wallet/src/screens/auth/auth_page.dart'; +import 'package:cake_wallet/src/screens/wallet_unlock/wallet_unlock_arguments.dart'; import 'package:cake_wallet/src/widgets/alert_with_one_action.dart'; import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart'; +import 'package:cake_wallet/store/settings_store.dart'; import 'package:cake_wallet/utils/show_bar.dart'; import 'package:cake_wallet/utils/show_pop_up.dart'; import 'package:cake_wallet/view_model/wallet_list/wallet_edit_view_model.dart'; @@ -94,9 +98,36 @@ class WalletEditPage extends BasePage { ); } else { try { - await walletEditViewModel.changeName(editingWallet); - Navigator.of(context).pop(); - walletEditViewModel.resetState(); + bool confirmed = false; + + if (SettingsStoreBase + .walletPasswordDirectInput) { + await Navigator.of(context).pushNamed( + Routes.walletUnlockLoadable, + arguments: WalletUnlockArguments( + authPasswordHandler: + (String password) async { + await walletEditViewModel + .changeName(editingWallet, + password: password); + }, + callback: (bool + isAuthenticatedSuccessfully, + AuthPageState auth) async { + if (isAuthenticatedSuccessfully) { + auth.close(); + confirmed = true; + } + }, + walletName: editingWallet.name, + walletType: editingWallet.type)); + } else { + await walletEditViewModel + .changeName(editingWallet); + confirmed = true; + } + + if (confirmed) Navigator.of(context).pop(); } catch (e) {} } } diff --git a/lib/src/screens/wallet_list/wallet_list_page.dart b/lib/src/screens/wallet_list/wallet_list_page.dart index fd7952628..088fa8e18 100644 --- a/lib/src/screens/wallet_list/wallet_list_page.dart +++ b/lib/src/screens/wallet_list/wallet_list_page.dart @@ -1,3 +1,6 @@ +import 'package:cake_wallet/src/screens/auth/auth_page.dart'; +import 'package:cake_wallet/src/screens/wallet_unlock/wallet_unlock_arguments.dart'; +import 'package:cake_wallet/store/settings_store.dart'; import 'package:cake_wallet/themes/extensions/cake_text_theme.dart'; import 'package:cake_wallet/core/auth_service.dart'; import 'package:cake_wallet/themes/extensions/receive_page_theme.dart'; @@ -248,6 +251,20 @@ class WalletListBodyState extends State<WalletListBody> { } Future<void> _loadWallet(WalletListItem wallet) async { + if (SettingsStoreBase.walletPasswordDirectInput) { + Navigator.of(context).pushNamed( + Routes.walletUnlockLoadable, + arguments: WalletUnlockArguments( + callback: (bool isAuthenticatedSuccessfully, AuthPageState auth) async { + if (isAuthenticatedSuccessfully) { + auth.close(); + setState(() {}); + } + }, walletName: wallet.name, + walletType: wallet.type)); + return; + } + await widget.authService.authenticateAction( context, onAuthSuccess: (isAuthenticatedSuccessfully) async { diff --git a/lib/src/screens/wallet_unlock/wallet_unlock_arguments.dart b/lib/src/screens/wallet_unlock/wallet_unlock_arguments.dart new file mode 100644 index 000000000..5b6d4dd16 --- /dev/null +++ b/lib/src/screens/wallet_unlock/wallet_unlock_arguments.dart @@ -0,0 +1,17 @@ +import 'package:cake_wallet/src/screens/auth/auth_page.dart'; +import 'package:cw_core/wallet_type.dart'; + +typedef AuthPasswordHandler = Future<void> Function(String); + +class WalletUnlockArguments { + WalletUnlockArguments( + {required this.callback, + this.walletName, + this.walletType, + this.authPasswordHandler}); + + final OnAuthenticationFinished callback; + final AuthPasswordHandler? authPasswordHandler; + final String? walletName; + final WalletType? walletType; +} diff --git a/lib/src/screens/wallet_unlock/wallet_unlock_page.dart b/lib/src/screens/wallet_unlock/wallet_unlock_page.dart new file mode 100644 index 000000000..a5421766c --- /dev/null +++ b/lib/src/screens/wallet_unlock/wallet_unlock_page.dart @@ -0,0 +1,228 @@ +import 'package:another_flushbar/flushbar.dart'; +import 'package:cake_wallet/core/execution_state.dart'; +import 'package:cake_wallet/generated/i18n.dart'; +import 'package:cake_wallet/src/screens/auth/auth_page.dart'; +import 'package:cake_wallet/src/screens/wallet_unlock/wallet_unlock_arguments.dart'; +import 'package:cake_wallet/src/widgets/primary_button.dart'; +import 'package:cake_wallet/themes/extensions/cake_text_theme.dart'; +import 'package:cake_wallet/themes/extensions/new_wallet_theme.dart'; +import 'package:cake_wallet/themes/extensions/send_page_theme.dart'; +import 'package:cake_wallet/utils/responsive_layout_util.dart'; +import 'package:cake_wallet/utils/show_bar.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:cake_wallet/view_model/wallet_unlock_view_model.dart'; +import 'package:flutter_mobx/flutter_mobx.dart'; +import 'package:mobx/mobx.dart'; + + +class WalletUnlockPage extends StatefulWidget { + WalletUnlockPage( + this.walletUnlockViewModel, + this.onAuthenticationFinished, + this.authPasswordHandler, + {required this.closable}); + + final WalletUnlockViewModel walletUnlockViewModel; + final OnAuthenticationFinished onAuthenticationFinished; + final AuthPasswordHandler? authPasswordHandler; + final bool closable; + + @override + State<StatefulWidget> createState() => WalletUnlockPageState(); +} + +class WalletUnlockPageState extends AuthPageState<WalletUnlockPage> { + WalletUnlockPageState() + : _passwordController = TextEditingController(); + + final TextEditingController _passwordController; + final _key = GlobalKey<ScaffoldState>(); + final _backArrowImageDarkTheme = + Image.asset('assets/images/close_button.png'); + ReactionDisposer? _reaction; + Flushbar<void>? _authBar; + Flushbar<void>? _progressBar; + void Function()? _passwordControllerListener; + + @override + void initState() { + _reaction ??= + reaction((_) => widget.walletUnlockViewModel.state, (ExecutionState state) { + if (state is ExecutedSuccessfullyState) { + WidgetsBinding.instance.addPostFrameCallback((_) { + widget.onAuthenticationFinished(true, this); + }); + setState(() {}); + } + + if (state is IsExecutingState) { + WidgetsBinding.instance.addPostFrameCallback((_) { + // null duration to make it indefinite until its disposed + _authBar = + createBar<void>(S.of(context).authentication, duration: null) + ..show(context); + }); + } + + if (state is FailureState) { + WidgetsBinding.instance.addPostFrameCallback((_) async { + dismissFlushBar(_authBar); + showBar<void>( + context, S.of(context).failed_authentication(state.error)); + + widget.onAuthenticationFinished(false, this); + }); + } + }); + + _passwordControllerListener = () => widget.walletUnlockViewModel.setPassword(_passwordController.text); + + if (_passwordControllerListener != null) { + _passwordController.addListener(_passwordControllerListener!); + } + + super.initState(); + } + + @override + void dispose() { + _reaction?.reaction.dispose(); + + if (_passwordControllerListener != null) { + _passwordController.removeListener(_passwordControllerListener!); + } + + super.dispose(); + } + + @override + void changeProcessText(String text) { + dismissFlushBar(_authBar); + _progressBar = createBar<void>(text, duration: null) + ..show(_key.currentContext!); + } + + @override + void hideProgressText() { + dismissFlushBar(_progressBar); + _progressBar = null; + } + + void dismissFlushBar(Flushbar<dynamic>? bar) { + WidgetsBinding.instance.addPostFrameCallback((_) async { + await bar?.dismiss(); + }); + } + + @override + Future<void> close({String? route, arguments}) async { + if (_key.currentContext == null) { + throw Exception('Key context is null. Should be not happened'); + } + + /// not the best scenario, but WidgetsBinding is not behaving correctly on Android + await Future<void>.delayed(Duration(milliseconds: 50)); + await _authBar?.dismiss(); + await Future<void>.delayed(Duration(milliseconds: 50)); + await _progressBar?.dismiss(); + await Future<void>.delayed(Duration(milliseconds: 50)); + if (route != null) { + Navigator.of(_key.currentContext!).pushReplacementNamed(route, arguments: arguments); + } else { + Navigator.of(_key.currentContext!).pop(); + } + } + + @override + Widget build(BuildContext context) { + return Scaffold( + key: _key, + appBar: CupertinoNavigationBar( + leading: widget.closable + ? Container( + padding: EdgeInsets.only(top: 10), + child: SizedBox( + height: 37, + width: 37, + child: InkWell( + onTap: () => Navigator.of(context).pop(), + child: _backArrowImageDarkTheme, + ), + )) + : Container(), + backgroundColor: Theme.of(context).colorScheme.background, + border: null), + resizeToAvoidBottomInset: false, + body: Center( + child: ConstrainedBox( + constraints: BoxConstraints(maxWidth: ResponsiveLayoutUtil.kDesktopMaxWidthConstraint), + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Expanded(child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Text(widget.walletUnlockViewModel.walletName, + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 24, + fontWeight: FontWeight.w500, + color: Theme.of(context).extension<CakeTextTheme>()!.titleColor)), + SizedBox(height: 24), + Form( + child: TextFormField( + onChanged: (value) => null, + controller: _passwordController, + textAlign: TextAlign.center, + obscureText: true, + style: TextStyle( + fontSize: 20.0, + fontWeight: FontWeight.w600, + color: Theme.of(context).extension<CakeTextTheme>()!.titleColor), + decoration: InputDecoration( + hintStyle: TextStyle( + fontSize: 18.0, + fontWeight: FontWeight.w500, + color: Theme.of(context).extension<NewWalletTheme>()!.hintTextColor), + hintText: S.of(context).enter_wallet_password, + focusedBorder: UnderlineInputBorder( + borderSide: BorderSide( + color: Theme.of(context).extension<NewWalletTheme>()!.underlineColor, + width: 1.0)), + enabledBorder: UnderlineInputBorder( + borderSide: BorderSide( + color: Theme.of(context).extension<NewWalletTheme>()!.underlineColor, + width: 1.0), + ) + )))])), + Padding( + padding: EdgeInsets.only(bottom: 24), + child: Observer( + builder: (_) => + LoadingPrimaryButton( + onPressed: () async { + if (widget.authPasswordHandler != null) { + try { + await widget.authPasswordHandler!(widget + .walletUnlockViewModel.password); + widget.walletUnlockViewModel.success(); + } catch (e) { + widget.walletUnlockViewModel.failure(e); + } + return; + } + + widget.walletUnlockViewModel.unlock(); + }, + text: S.of(context).unlock, + color: Colors.green, + textColor: Colors.white, + isLoading: widget.walletUnlockViewModel.state is IsExecutingState, + isDisabled: widget.walletUnlockViewModel.state is IsExecutingState))) + ])) + )); + } +} diff --git a/lib/src/widgets/address_text_field.dart b/lib/src/widgets/address_text_field.dart index 000e6325e..5bfbd3080 100644 --- a/lib/src/widgets/address_text_field.dart +++ b/lib/src/widgets/address_text_field.dart @@ -17,7 +17,10 @@ class AddressTextField extends StatelessWidget { {required this.controller, this.isActive = true, this.placeholder, - this.options = const [AddressTextFieldOption.qrCode, AddressTextFieldOption.addressBook], + this.options = const [ + AddressTextFieldOption.qrCode, + AddressTextFieldOption.addressBook + ], this.onURIScanned, this.focusNode, this.isBorderExist = true, diff --git a/lib/src/widgets/base_text_form_field.dart b/lib/src/widgets/base_text_form_field.dart index 5649a0784..6cf1ad581 100644 --- a/lib/src/widgets/base_text_form_field.dart +++ b/lib/src/widgets/base_text_form_field.dart @@ -29,7 +29,8 @@ class BaseTextFormField extends StatelessWidget { this.focusNode, this.initialValue, this.onSubmit, - this.borderWidth = 1.0}); + this.borderWidth = 1.0, + this.obscureText = false}); final TextEditingController? controller; final TextInputType? keyboardType; @@ -57,6 +58,7 @@ class BaseTextFormField extends StatelessWidget { final String? initialValue; final double borderWidth; final void Function(String)? onSubmit; + final bool obscureText; @override Widget build(BuildContext context) { @@ -75,6 +77,7 @@ class BaseTextFormField extends StatelessWidget { enabled: enabled, maxLength: maxLength, onFieldSubmitted: onSubmit, + obscureText: obscureText, style: textStyle ?? TextStyle( fontSize: 16.0, diff --git a/lib/store/app_store.dart b/lib/store/app_store.dart index 639efacb6..196cc48a8 100644 --- a/lib/store/app_store.dart +++ b/lib/store/app_store.dart @@ -1,4 +1,3 @@ -import 'package:cake_wallet/utils/exception_handler.dart'; import 'package:cw_core/transaction_info.dart'; import 'package:mobx/mobx.dart'; import 'package:cw_core/balance.dart'; @@ -39,6 +38,5 @@ abstract class AppStoreBase with Store { wallet) { this.wallet?.close(); this.wallet = wallet; - this.wallet!.setExceptionHandler(ExceptionHandler.onError); } } diff --git a/lib/store/secret_store.dart b/lib/store/secret_store.dart index 6d958acb5..fc66a0f74 100644 --- a/lib/store/secret_store.dart +++ b/lib/store/secret_store.dart @@ -1,6 +1,6 @@ import 'package:cake_wallet/entities/secret_store_key.dart'; import 'package:flutter/foundation.dart'; -import 'package:flutter_secure_storage/flutter_secure_storage.dart'; +import 'package:cake_wallet/core/secure_storage.dart'; import 'package:mobx/mobx.dart'; part 'secret_store.g.dart'; @@ -8,7 +8,7 @@ part 'secret_store.g.dart'; class SecretStore = SecretStoreBase with _$SecretStore; abstract class SecretStoreBase with Store { - static Future<SecretStore> load(FlutterSecureStorage storage) async { + static Future<SecretStore> load(SecureStorage storage) async { final secretStore = SecretStore(); final backupPasswordKey = generateStoreKeyFor(key: SecretStoreKey.backupPassword); final backupPassword = await storage.read(key: backupPasswordKey); diff --git a/lib/store/settings_store.dart b/lib/store/settings_store.dart index 703c7d73e..17acf9c59 100644 --- a/lib/store/settings_store.dart +++ b/lib/store/settings_store.dart @@ -17,7 +17,7 @@ import 'package:device_info_plus/device_info_plus.dart'; import 'package:flutter/material.dart'; import 'package:hive/hive.dart'; import 'package:mobx/mobx.dart'; -import 'package:package_info/package_info.dart'; +import 'package:package_info_plus/package_info_plus.dart'; import 'package:cake_wallet/di.dart'; import 'package:cw_core/wallet_type.dart'; import 'package:shared_preferences/shared_preferences.dart'; @@ -29,6 +29,7 @@ import 'package:cake_wallet/monero/monero.dart'; import 'package:cake_wallet/entities/action_list_display_mode.dart'; import 'package:cake_wallet/entities/fiat_api_mode.dart'; import 'package:cw_core/set_app_secure_native.dart'; +import 'package:cake_wallet/utils/device_info.dart'; part 'settings_store.g.dart'; @@ -337,6 +338,7 @@ abstract class SettingsStoreBase with Store { static const defaultPinLength = 4; static const defaultActionsMode = 11; static const defaultPinCodeTimeOutDuration = PinCodeRequiredDuration.tenminutes; + static final walletPasswordDirectInput = Platform.isLinux; @observable FiatCurrency fiatCurrency; @@ -599,9 +601,14 @@ abstract class SettingsStoreBase with Store { final litecoinElectrumServer = nodeSource.get(litecoinElectrumServerId); final havenNode = nodeSource.get(havenNodeId); final ethereumNode = nodeSource.get(ethereumNodeId); - final packageInfo = await PackageInfo.fromPlatform(); final deviceName = await _getDeviceName() ?? ''; final shouldShowYatPopup = sharedPreferences.getBool(PreferencesKey.shouldShowYatPopup) ?? true; + var appVersion = ''; + + try { + final packageInfo = await PackageInfo.fromPlatform(); + appVersion = packageInfo.version; + } catch(_) {} final nodes = <WalletType, Node>{}; @@ -634,7 +641,7 @@ abstract class SettingsStoreBase with Store { sharedPreferences: sharedPreferences, initialShouldShowMarketPlaceInDashboard: shouldShowMarketPlaceInDashboard, nodes: nodes, - appVersion: packageInfo.version, + appVersion: appVersion, deviceName: deviceName, isBitcoinBuyEnabled: isBitcoinBuyEnabled, initialFiatCurrency: currentFiatCurrency, @@ -849,6 +856,7 @@ abstract class SettingsStoreBase with Store { if (Platform.isAndroid) { final androidInfo = await deviceInfoPlugin.androidInfo; deviceName = '${androidInfo.brand}%20${androidInfo.manufacturer}%20${androidInfo.model}'; + print(deviceName); } else if (Platform.isIOS) { final iosInfo = await deviceInfoPlugin.iosInfo; deviceName = iosInfo.model; diff --git a/lib/store/yat/yat_store.dart b/lib/store/yat/yat_store.dart index 290b991c9..57cae088c 100644 --- a/lib/store/yat/yat_store.dart +++ b/lib/store/yat/yat_store.dart @@ -10,7 +10,7 @@ import 'dart:convert'; import 'package:cake_wallet/store/yat/yat_exception.dart'; import 'package:http/http.dart'; import 'dart:async'; -import 'package:flutter_secure_storage/flutter_secure_storage.dart'; +import 'package:cake_wallet/core/secure_storage.dart'; part 'yat_store.g.dart'; @@ -193,7 +193,7 @@ abstract class YatStoreBase with Store { AppStore appStore; - FlutterSecureStorage secureStorage; + SecureStorage secureStorage; @observable String emoji; diff --git a/lib/utils/distribution_info.dart b/lib/utils/distribution_info.dart index 859c507a3..0d7f65db4 100644 --- a/lib/utils/distribution_info.dart +++ b/lib/utils/distribution_info.dart @@ -1,5 +1,5 @@ import 'dart:io'; -import 'package:package_info/package_info.dart'; +import 'package:package_info_plus/package_info_plus.dart'; enum DistributionType { googleplay, github, appstore, fdroid } diff --git a/lib/utils/exception_handler.dart b/lib/utils/exception_handler.dart index e8e7702fa..705cac0c2 100644 --- a/lib/utils/exception_handler.dart +++ b/lib/utils/exception_handler.dart @@ -5,11 +5,12 @@ import 'package:cake_wallet/generated/i18n.dart'; import 'package:cake_wallet/main.dart'; import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart'; import 'package:cake_wallet/utils/show_pop_up.dart'; +import 'package:cw_core/root_dir.dart'; import 'package:device_info_plus/device_info_plus.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_mailer/flutter_mailer.dart'; -import 'package:package_info/package_info.dart'; +import 'package:package_info_plus/package_info_plus.dart'; import 'package:path_provider/path_provider.dart'; import 'package:shared_preferences/shared_preferences.dart'; @@ -18,7 +19,7 @@ class ExceptionHandler { static const _coolDownDurationInDays = 7; static void _saveException(String? error, StackTrace? stackTrace, {String? library}) async { - final appDocDir = await getApplicationDocumentsDirectory(); + final appDocDir = await getAppDir(); final file = File('${appDocDir.path}/error.txt'); final exception = { @@ -40,7 +41,7 @@ class ExceptionHandler { static void _sendExceptionFile() async { try { - final appDocDir = await getApplicationDocumentsDirectory(); + final appDocDir = await getAppDir(); final file = File('${appDocDir.path}/error.txt'); diff --git a/lib/view_model/backup_view_model.dart b/lib/view_model/backup_view_model.dart index 8cc651b17..41f8f8dc5 100644 --- a/lib/view_model/backup_view_model.dart +++ b/lib/view_model/backup_view_model.dart @@ -3,8 +3,9 @@ import 'package:cake_wallet/core/backup_service.dart'; import 'package:cake_wallet/core/execution_state.dart'; import 'package:cake_wallet/entities/secret_store_key.dart'; import 'package:cake_wallet/store/secret_store.dart'; +import 'package:cw_core/root_dir.dart'; import 'package:flutter/foundation.dart'; -import 'package:flutter_secure_storage/flutter_secure_storage.dart'; +import 'package:cake_wallet/core/secure_storage.dart'; import 'package:mobx/mobx.dart'; import 'package:intl/intl.dart'; import 'package:cake_wallet/wallet_type_utils.dart'; @@ -34,7 +35,7 @@ abstract class BackupViewModelBase with Store { }, fireImmediately: true); } - final FlutterSecureStorage secureStorage; + final SecureStorage secureStorage; final SecretStore secretStore; final BackupService backupService; @@ -73,7 +74,7 @@ abstract class BackupViewModelBase with Store { } Future<String> saveBackupFileLocally(BackupExportFile backup) async { - final appDir = await getApplicationDocumentsDirectory(); + final appDir = await getAppDir(); final path = '${appDir.path}/${backup.name}'; final backupFile = File(path); await backupFile.writeAsBytes(backup.content); @@ -81,7 +82,7 @@ abstract class BackupViewModelBase with Store { } Future<void> removeBackupFileLocally(BackupExportFile backup) async { - final appDir = await getApplicationDocumentsDirectory(); + final appDir = await getAppDir(); final path = '${appDir.path}/${backup.name}'; final backupFile = File(path); await backupFile.delete(); diff --git a/lib/view_model/dashboard/dashboard_view_model.dart b/lib/view_model/dashboard/dashboard_view_model.dart index 7035130c0..4051c3d3f 100644 --- a/lib/view_model/dashboard/dashboard_view_model.dart +++ b/lib/view_model/dashboard/dashboard_view_model.dart @@ -297,7 +297,8 @@ abstract class DashboardViewModelBase with Store { bool get isEnabledSellAction => !settingsStore.disableSell && wallet.type != WalletType.haven && - wallet.type != WalletType.monero; + wallet.type != WalletType.monero && + wallet.type != WalletType.litecoin; @observable bool hasSellAction; diff --git a/lib/view_model/dashboard/transaction_list_item.dart b/lib/view_model/dashboard/transaction_list_item.dart index ac74df89d..07dfeae07 100644 --- a/lib/view_model/dashboard/transaction_list_item.dart +++ b/lib/view_model/dashboard/transaction_list_item.dart @@ -14,6 +14,7 @@ import 'package:cake_wallet/view_model/dashboard/balance_view_model.dart'; import 'package:cw_core/keyable.dart'; import 'package:cw_core/wallet_type.dart'; + class TransactionListItem extends ActionListItem with Keyable { TransactionListItem( {required this.transaction, @@ -28,7 +29,7 @@ class TransactionListItem extends ActionListItem with Keyable { FiatCurrency get fiatCurrency => settingsStore.fiatCurrency; - BalanceDisplayMode get displayMode => balanceViewModel.displayMode; + BalanceDisplayMode get displayMode => settingsStore.balanceDisplayMode; @override dynamic get keyIndex => transaction.id; diff --git a/lib/view_model/edit_backup_password_view_model.dart b/lib/view_model/edit_backup_password_view_model.dart index e7b8ee017..5c49b676c 100644 --- a/lib/view_model/edit_backup_password_view_model.dart +++ b/lib/view_model/edit_backup_password_view_model.dart @@ -1,5 +1,5 @@ import 'package:mobx/mobx.dart'; -import 'package:flutter_secure_storage/flutter_secure_storage.dart'; +import 'package:cake_wallet/core/secure_storage.dart'; import 'package:cake_wallet/entities/secret_store_key.dart'; import 'package:cake_wallet/store/secret_store.dart'; @@ -13,7 +13,7 @@ abstract class EditBackupPasswordViewModelBase with Store { : backupPassword = secretStore.read(generateStoreKeyFor(key: SecretStoreKey.backupPassword)), _originalPassword = ''; - final FlutterSecureStorage secureStorage; + final SecureStorage secureStorage; final SecretStore secretStore; @observable diff --git a/lib/view_model/exchange/exchange_trade_view_model.dart b/lib/view_model/exchange/exchange_trade_view_model.dart index cfabd994f..de10b9221 100644 --- a/lib/view_model/exchange/exchange_trade_view_model.dart +++ b/lib/view_model/exchange/exchange_trade_view_model.dart @@ -50,7 +50,7 @@ abstract class ExchangeTradeViewModelBase with Store { case ExchangeProviderDescription.simpleSwap: _provider = SimpleSwapExchangeProvider(); break; - case ExchangeProviderDescription.trocador: + case ExchangeProviderDescription.trocador: _provider = TrocadorExchangeProvider(); break; } @@ -118,10 +118,6 @@ abstract class ExchangeTradeViewModelBase with Store { updatedTrade.createdAt = trade.createdAt; } - if (updatedTrade.amount.isEmpty) { - updatedTrade.amount = trade.amount; - } - trade = updatedTrade; _updateItems(); @@ -131,8 +127,7 @@ abstract class ExchangeTradeViewModelBase with Store { } void _updateItems() { - final tagFrom = - tradesStore.trade!.from.tag != null ? '${tradesStore.trade!.from.tag}' + ' ' : ''; + final tagFrom = tradesStore.trade!.from.tag != null ? '${tradesStore.trade!.from.tag}' + ' ' : ''; final tagTo = tradesStore.trade!.to.tag != null ? '${tradesStore.trade!.to.tag}' + ' ' : ''; items.clear(); items.add(ExchangeTradeItem( diff --git a/lib/view_model/ionia/ionia_gift_cards_list_view_model.dart b/lib/view_model/ionia/ionia_gift_cards_list_view_model.dart index b4974c420..77d8af6c2 100644 --- a/lib/view_model/ionia/ionia_gift_cards_list_view_model.dart +++ b/lib/view_model/ionia/ionia_gift_cards_list_view_model.dart @@ -16,10 +16,12 @@ abstract class IoniaGiftCardsListViewModelBase with Store { ioniaCategories = IoniaCategory.allCategories, selectedIndices = ObservableList<IoniaCategory>.of([IoniaCategory.all]), scrollOffsetFromTop = 0.0, + isLoggedIn = false, merchantState = InitialIoniaMerchantLoadingState(), createCardState = IoniaCreateCardState(), searchString = '', ioniaMerchantList = <IoniaMerchant>[] { + _getAuthStatus().then((value) => isLoggedIn = value); } final IoniaService ioniaService; @@ -43,17 +45,24 @@ abstract class IoniaGiftCardsListViewModelBase with Store { @observable List<IoniaMerchant> ioniaMerchants; + @observable + bool isLoggedIn; + @observable List<IoniaCategory> ioniaCategories; @observable ObservableList<IoniaCategory> selectedIndices; + Future<bool> _getAuthStatus() async { + return await ioniaService.isLogined(); + } + @action Future<void> createCard() async { try { createCardState = IoniaCreateCardLoading(); - await ioniaService.createCard(); + final card = await ioniaService.createCard(); createCardState = IoniaCreateCardSuccess(); } catch (e) { createCardState = IoniaCreateCardFailure(error: e.toString()); diff --git a/lib/view_model/settings/other_settings_view_model.dart b/lib/view_model/settings/other_settings_view_model.dart index c57af50d9..d9f0c122d 100644 --- a/lib/view_model/settings/other_settings_view_model.dart +++ b/lib/view_model/settings/other_settings_view_model.dart @@ -8,7 +8,7 @@ import 'package:cw_core/transaction_priority.dart'; import 'package:cw_core/wallet_base.dart'; import 'package:cw_core/wallet_type.dart'; import 'package:mobx/mobx.dart'; -import 'package:package_info/package_info.dart'; +import 'package:package_info_plus/package_info_plus.dart'; part 'other_settings_view_model.g.dart'; diff --git a/lib/view_model/support_view_model.dart b/lib/view_model/support_view_model.dart index d3b14c59b..a94b5fa34 100644 --- a/lib/view_model/support_view_model.dart +++ b/lib/view_model/support_view_model.dart @@ -1,8 +1,12 @@ import 'package:cake_wallet/generated/i18n.dart'; import 'package:cake_wallet/view_model/settings/link_list_item.dart'; +import 'package:cake_wallet/view_model/settings/regular_list_item.dart'; import 'package:cake_wallet/view_model/settings/settings_list_item.dart'; -import 'package:cake_wallet/wallet_type_utils.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; import 'package:mobx/mobx.dart'; +import 'package:url_launcher/url_launcher.dart'; +import 'package:cake_wallet/wallet_type_utils.dart'; import 'package:cake_wallet/.secrets.g.dart' as secrets; part 'support_view_model.g.dart'; @@ -12,6 +16,14 @@ class SupportViewModel = SupportViewModelBase with _$SupportViewModel; abstract class SupportViewModelBase with Store { SupportViewModelBase() : items = [ + RegularListItem( + title: S.current.faq, + handler: (BuildContext context) async { + try { + await launchUrl(url); + } catch (e) {} + }, + ), LinkListItem( title: 'Email', linkTitle: 'support@cakewallet.com', @@ -76,6 +88,8 @@ abstract class SupportViewModelBase with Store { final guidesUrl = 'https://guides.cakewallet.com'; + static final url = Uri(scheme: "https", host: "guides.cakewallet.com"); + String fetchUrl({String locale = "en", String authToken = ""}) { var supportUrl = "https://app.chatwoot.com/widget?website_token=${secrets.chatwootWebsiteToken}&locale=${locale}"; diff --git a/lib/view_model/trade_details_view_model.dart b/lib/view_model/trade_details_view_model.dart index c0b1ac461..33e9d391a 100644 --- a/lib/view_model/trade_details_view_model.dart +++ b/lib/view_model/trade_details_view_model.dart @@ -21,7 +21,6 @@ import 'package:cake_wallet/src/screens/trade_details/track_trade_list_item.dart import 'package:cake_wallet/src/screens/trade_details/trade_details_list_card.dart'; import 'package:cake_wallet/src/screens/trade_details/trade_details_status_item.dart'; import 'package:url_launcher/url_launcher.dart'; -import 'package:collection/collection.dart'; part 'trade_details_view_model.g.dart'; @@ -33,8 +32,7 @@ abstract class TradeDetailsViewModelBase with Store { required this.trades, required this.settingsStore, }) : items = ObservableList<StandartListItem>(), - trade = trades.values.firstWhereOrNull((element) => element.id == tradeForDetails.id) ?? - tradeForDetails { + trade = tradeForDetails { switch (trade.provider) { case ExchangeProviderDescription.xmrto: _provider = XMRTOExchangeProvider(); @@ -56,6 +54,8 @@ abstract class TradeDetailsViewModelBase with Store { break; } + items = ObservableList<StandartListItem>(); + _updateItems(); _updateTrade(); @@ -85,12 +85,6 @@ abstract class TradeDetailsViewModelBase with Store { if (updatedTrade.createdAt == null && trade.createdAt != null) { updatedTrade.createdAt = trade.createdAt; } - Trade? foundElement = trades.values.firstWhereOrNull((element) => element.id == trade.id); - if (foundElement != null) { - final editedTrade = trades.get(foundElement.key); - editedTrade?.stateRaw = updatedTrade.stateRaw; - editedTrade?.save(); - } trade = updatedTrade; @@ -160,9 +154,8 @@ abstract class TradeDetailsViewModelBase with Store { } void _launchUrl(String url) { - final uri = Uri.parse(url); try { - launchUrl(uri); + launch(url); } catch (e) {} } } diff --git a/lib/view_model/wallet_creation_vm.dart b/lib/view_model/wallet_creation_vm.dart index c6cea1214..b82bcfb32 100644 --- a/lib/view_model/wallet_creation_vm.dart +++ b/lib/view_model/wallet_creation_vm.dart @@ -1,6 +1,8 @@ import 'package:cake_wallet/core/wallet_creation_service.dart'; import 'package:cake_wallet/di.dart'; import 'package:cake_wallet/entities/background_tasks.dart'; +import 'package:cake_wallet/generated/i18n.dart'; +import 'package:cake_wallet/store/settings_store.dart'; import 'package:cake_wallet/view_model/restore/restore_wallet.dart'; import 'package:hive/hive.dart'; import 'package:mobx/mobx.dart'; @@ -29,6 +31,14 @@ abstract class WalletCreationVMBase with Store { @observable ExecutionState state; + @observable + String? walletPassword; + + @observable + String? repeatedWalletPassword; + + bool get hasWalletPassword => SettingsStoreBase.walletPasswordDirectInput; + WalletType type; final bool isRecovery; final WalletCreationService walletCreationService; @@ -49,6 +59,14 @@ abstract class WalletCreationVMBase with Store { name = await generateName(); } + if (hasWalletPassword && (walletPassword?.isEmpty ?? true)) { + throw Exception(S.current.wallet_password_is_empty); + } + + if (hasWalletPassword && walletPassword != repeatedWalletPassword) { + throw Exception(S.current.repeated_password_is_incorrect); + } + walletCreationService.checkIfExists(name); final dirPath = await pathForWalletDir(name: name, type: type); final path = await pathForWallet(name: name, type: type); diff --git a/lib/view_model/wallet_list/wallet_edit_view_model.dart b/lib/view_model/wallet_list/wallet_edit_view_model.dart index 0582c3f87..806d76022 100644 --- a/lib/view_model/wallet_list/wallet_edit_view_model.dart +++ b/lib/view_model/wallet_list/wallet_edit_view_model.dart @@ -32,11 +32,12 @@ abstract class WalletEditViewModelBase with Store { final WalletLoadingService _walletLoadingService; @action - Future<void> changeName(WalletListItem walletItem) async { + Future<void> changeName(WalletListItem walletItem, {String? password}) async { state = WalletEditRenamePending(); await _walletLoadingService.renameWallet( - walletItem.type, walletItem.name, newName); - _walletListViewModel.updateList(); + walletItem.type, walletItem.name, newName, + password: password); + resetState(); } @action @@ -45,11 +46,11 @@ abstract class WalletEditViewModelBase with Store { final walletService = getIt.get<WalletService>(param1: wallet.type); await walletService.remove(wallet.name); resetState(); - _walletListViewModel.updateList(); } @action void resetState() { + _walletListViewModel.updateList(); state = WalletEditViewModelInitialState(); } } diff --git a/lib/view_model/wallet_new_vm.dart b/lib/view_model/wallet_new_vm.dart index dcb9785e7..42ca1d2fc 100644 --- a/lib/view_model/wallet_new_vm.dart +++ b/lib/view_model/wallet_new_vm.dart @@ -35,16 +35,16 @@ abstract class WalletNewVMBase extends WalletCreationVM with Store { switch (type) { case WalletType.monero: return monero!.createMoneroNewWalletCredentials( - name: name, language: options as String); + name: name, language: options as String, password: walletPassword); case WalletType.bitcoin: - return bitcoin!.createBitcoinNewWalletCredentials(name: name); + return bitcoin!.createBitcoinNewWalletCredentials(name: name, password: walletPassword); case WalletType.litecoin: - return bitcoin!.createBitcoinNewWalletCredentials(name: name); + return bitcoin!.createBitcoinNewWalletCredentials(name: name, password: walletPassword); case WalletType.haven: return haven!.createHavenNewWalletCredentials( - name: name, language: options as String); + name: name, language: options as String, password: walletPassword); case WalletType.ethereum: - return ethereum!.createEthereumNewWalletCredentials(name: name); + return ethereum!.createEthereumNewWalletCredentials(name: name, password: walletPassword); default: throw Exception('Unexpected type: ${type.toString()}');; } diff --git a/lib/view_model/wallet_restore_view_model.dart b/lib/view_model/wallet_restore_view_model.dart index 65f122bf0..3c51e3e03 100644 --- a/lib/view_model/wallet_restore_view_model.dart +++ b/lib/view_model/wallet_restore_view_model.dart @@ -55,7 +55,7 @@ abstract class WalletRestoreViewModelBase extends WalletCreationVM with Store { @override WalletCredentials getCredentials(dynamic options) { - final password = generateWalletPassword(); + final password = walletPassword ?? generateWalletPassword(); final height = options['height'] as int? ?? 0; name = options['name'] as String; diff --git a/lib/view_model/wallet_unlock_loadable_view_model.dart b/lib/view_model/wallet_unlock_loadable_view_model.dart new file mode 100644 index 000000000..e1e5d5238 --- /dev/null +++ b/lib/view_model/wallet_unlock_loadable_view_model.dart @@ -0,0 +1,65 @@ +import 'package:mobx/mobx.dart'; +import 'package:cake_wallet/core/execution_state.dart'; +import 'package:cake_wallet/core/wallet_loading_service.dart'; +import 'package:cake_wallet/store/app_store.dart'; +import 'package:cw_core/wallet_type.dart'; +import 'package:cake_wallet/view_model/wallet_unlock_view_model.dart'; + +part 'wallet_unlock_loadable_view_model.g.dart'; + +class WalletUnlockLoadableViewModel = WalletUnlockLoadableViewModelBase + with _$WalletUnlockLoadableViewModel; + +abstract class WalletUnlockLoadableViewModelBase extends WalletUnlockViewModel + with Store { + WalletUnlockLoadableViewModelBase(this._appStore, this._walletLoadingService, + {required this.walletName, required this.walletType}) + : password = '', + state = InitialExecutionState(); + + final String walletName; + + final WalletType walletType; + + @override + @observable + String password; + + @override + @observable + ExecutionState state; + + final WalletLoadingService _walletLoadingService; + + final AppStore _appStore; + + @override + @action + void setPassword(String password) => this.password = password; + + @override + @action + Future<void> unlock() async { + try { + state = InitialExecutionState(); + final wallet = await _walletLoadingService.load(walletType, walletName, + password: password); + _appStore.changeCurrentWallet(wallet); + success(); + } catch (e) { + failure(e.toString()); + } + } + + @override + @action + void success() { + state = ExecutedSuccessfullyState(); + } + + @override + @action + void failure(e) { + state = FailureState(e.toString()); + } +} diff --git a/lib/view_model/wallet_unlock_verifiable_view_model.dart b/lib/view_model/wallet_unlock_verifiable_view_model.dart new file mode 100644 index 000000000..11fe3b674 --- /dev/null +++ b/lib/view_model/wallet_unlock_verifiable_view_model.dart @@ -0,0 +1,61 @@ +import 'package:cake_wallet/generated/i18n.dart'; +import 'package:cake_wallet/store/app_store.dart'; +import 'package:mobx/mobx.dart'; +import 'package:cw_core/wallet_type.dart'; +import 'package:cake_wallet/core/execution_state.dart'; +import 'package:cake_wallet/view_model/wallet_unlock_view_model.dart'; + +part 'wallet_unlock_verifiable_view_model.g.dart'; + +class WalletUnlockVerifiableViewModel = WalletUnlockVerifiableViewModelBase + with _$WalletUnlockVerifiableViewModel; + +abstract class WalletUnlockVerifiableViewModelBase extends WalletUnlockViewModel + with Store { + WalletUnlockVerifiableViewModelBase(this.appStore, + {required this.walletName, required this.walletType}) + : password = '', + state = InitialExecutionState(); + + final String walletName; + + final WalletType walletType; + + final AppStore appStore; + + @override + @observable + String password; + + @override + @observable + ExecutionState state; + + @override + @action + void setPassword(String password) => this.password = password; + + @override + @action + Future<void> unlock() async { + try { + state = appStore.wallet!.password == password + ? ExecutedSuccessfullyState() + : FailureState(S.current.invalid_password); + } catch (e) { + failure('${S.current.invalid_password}\n${e.toString()}'); + } + } + + @override + @action + void success() { + state = ExecutedSuccessfullyState(); + } + + @override + @action + void failure(e) { + state = FailureState(e.toString()); + } +} diff --git a/lib/view_model/wallet_unlock_view_model.dart b/lib/view_model/wallet_unlock_view_model.dart new file mode 100644 index 000000000..f0131c61f --- /dev/null +++ b/lib/view_model/wallet_unlock_view_model.dart @@ -0,0 +1,11 @@ +import 'package:cake_wallet/core/execution_state.dart'; + +abstract class WalletUnlockViewModel { + String get walletName; + String get password; + void setPassword(String password); + ExecutionState get state; + Future<void> unlock(); + void success(); + void failure(dynamic e); +} diff --git a/linux/.gitignore b/linux/.gitignore new file mode 100644 index 000000000..d3896c984 --- /dev/null +++ b/linux/.gitignore @@ -0,0 +1 @@ +flutter/ephemeral diff --git a/linux/CMakeLists.txt b/linux/CMakeLists.txt new file mode 100644 index 000000000..9fb1666a9 --- /dev/null +++ b/linux/CMakeLists.txt @@ -0,0 +1,138 @@ +# Project-level configuration. +cmake_minimum_required(VERSION 3.10) +project(runner LANGUAGES CXX) + +# The name of the executable created for the application. Change this to change +# the on-disk name of your application. +set(BINARY_NAME "cake_wallet") +# The unique GTK application identifier for this application. See: +# https://wiki.gnome.org/HowDoI/ChooseApplicationID +set(APPLICATION_ID "com.example.cake_wallet") + +# Explicitly opt in to modern CMake behaviors to avoid warnings with recent +# versions of CMake. +cmake_policy(SET CMP0063 NEW) + +# Load bundled libraries from the lib/ directory relative to the binary. +set(CMAKE_INSTALL_RPATH "$ORIGIN/lib") + +# Root filesystem for cross-building. +if(FLUTTER_TARGET_PLATFORM_SYSROOT) + set(CMAKE_SYSROOT ${FLUTTER_TARGET_PLATFORM_SYSROOT}) + set(CMAKE_FIND_ROOT_PATH ${CMAKE_SYSROOT}) + set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) + set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) + set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) + set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) +endif() + +# Define build configuration options. +if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) + set(CMAKE_BUILD_TYPE "Debug" CACHE + STRING "Flutter build mode" FORCE) + set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS + "Debug" "Profile" "Release") +endif() + +# Compilation settings that should be applied to most targets. +# +# Be cautious about adding new options here, as plugins use this function by +# default. In most cases, you should add new options to specific targets instead +# of modifying this function. +function(APPLY_STANDARD_SETTINGS TARGET) + target_compile_features(${TARGET} PUBLIC cxx_std_14) + target_compile_options(${TARGET} PRIVATE -Wall -Werror) + target_compile_options(${TARGET} PRIVATE "$<$<NOT:$<CONFIG:Debug>>:-O3>") + target_compile_definitions(${TARGET} PRIVATE "$<$<NOT:$<CONFIG:Debug>>:NDEBUG>") +endfunction() + +# Flutter library and tool build rules. +set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") +add_subdirectory(${FLUTTER_MANAGED_DIR}) + +# System-level dependencies. +find_package(PkgConfig REQUIRED) +pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0) + +add_definitions(-DAPPLICATION_ID="${APPLICATION_ID}") + +# Define the application target. To change its name, change BINARY_NAME above, +# not the value here, or `flutter run` will no longer work. +# +# Any new source files that you add to the application should be added here. +add_executable(${BINARY_NAME} + "main.cc" + "my_application.cc" + "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" +) + +# Apply the standard set of build settings. This can be removed for applications +# that need different build settings. +apply_standard_settings(${BINARY_NAME}) + +# Add dependency libraries. Add any application-specific dependencies here. +target_link_libraries(${BINARY_NAME} PRIVATE flutter) +target_link_libraries(${BINARY_NAME} PRIVATE PkgConfig::GTK) + +# Run the Flutter tool portions of the build. This must not be removed. +add_dependencies(${BINARY_NAME} flutter_assemble) + +# Only the install-generated bundle's copy of the executable will launch +# correctly, since the resources must in the right relative locations. To avoid +# people trying to run the unbundled copy, put it in a subdirectory instead of +# the default top-level location. +set_target_properties(${BINARY_NAME} + PROPERTIES + RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/intermediates_do_not_run" +) + +# Generated plugin build rules, which manage building the plugins and adding +# them to the application. +include(flutter/generated_plugins.cmake) + + +# === Installation === +# By default, "installing" just makes a relocatable bundle in the build +# directory. +set(BUILD_BUNDLE_DIR "${PROJECT_BINARY_DIR}/bundle") +if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) + set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE) +endif() + +# Start with a clean build bundle directory every time. +install(CODE " + file(REMOVE_RECURSE \"${BUILD_BUNDLE_DIR}/\") + " COMPONENT Runtime) + +set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data") +set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}/lib") + +install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) + +foreach(bundled_library ${PLUGIN_BUNDLED_LIBRARIES}) + install(FILES "${bundled_library}" + DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) +endforeach(bundled_library) + +# Fully re-copy the assets directory on each build to avoid having stale files +# from a previous install. +set(FLUTTER_ASSET_DIR_NAME "flutter_assets") +install(CODE " + file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\") + " COMPONENT Runtime) +install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}" + DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime) + +# Install the AOT library on non-Debug builds only. +if(NOT CMAKE_BUILD_TYPE MATCHES "Debug") + install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) +endif() diff --git a/linux/com.cakewallet.CakeWallet.desktop b/linux/com.cakewallet.CakeWallet.desktop new file mode 100644 index 000000000..eb76a2fb5 --- /dev/null +++ b/linux/com.cakewallet.CakeWallet.desktop @@ -0,0 +1,9 @@ +[Desktop Entry] +Version=1.0 +Type=Application +Name=Cake Wallet +Comment=A noncustodial multi-currency wallet +Categories=Office;Finance; +Terminal=false +Icon=com.cakewallet.CakeWallet +Exec=cake_wallet diff --git a/linux/flutter/CMakeLists.txt b/linux/flutter/CMakeLists.txt new file mode 100644 index 000000000..d5bd01648 --- /dev/null +++ b/linux/flutter/CMakeLists.txt @@ -0,0 +1,88 @@ +# This file controls Flutter-level build steps. It should not be edited. +cmake_minimum_required(VERSION 3.10) + +set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral") + +# Configuration provided via flutter tool. +include(${EPHEMERAL_DIR}/generated_config.cmake) + +# TODO: Move the rest of this into files in ephemeral. See +# https://github.com/flutter/flutter/issues/57146. + +# Serves the same purpose as list(TRANSFORM ... PREPEND ...), +# which isn't available in 3.10. +function(list_prepend LIST_NAME PREFIX) + set(NEW_LIST "") + foreach(element ${${LIST_NAME}}) + list(APPEND NEW_LIST "${PREFIX}${element}") + endforeach(element) + set(${LIST_NAME} "${NEW_LIST}" PARENT_SCOPE) +endfunction() + +# === Flutter Library === +# System-level dependencies. +find_package(PkgConfig REQUIRED) +pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0) +pkg_check_modules(GLIB REQUIRED IMPORTED_TARGET glib-2.0) +pkg_check_modules(GIO REQUIRED IMPORTED_TARGET gio-2.0) + +set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/libflutter_linux_gtk.so") + +# Published to parent scope for install step. +set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE) +set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE) +set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE) +set(AOT_LIBRARY "${PROJECT_DIR}/build/lib/libapp.so" PARENT_SCOPE) + +list(APPEND FLUTTER_LIBRARY_HEADERS + "fl_basic_message_channel.h" + "fl_binary_codec.h" + "fl_binary_messenger.h" + "fl_dart_project.h" + "fl_engine.h" + "fl_json_message_codec.h" + "fl_json_method_codec.h" + "fl_message_codec.h" + "fl_method_call.h" + "fl_method_channel.h" + "fl_method_codec.h" + "fl_method_response.h" + "fl_plugin_registrar.h" + "fl_plugin_registry.h" + "fl_standard_message_codec.h" + "fl_standard_method_codec.h" + "fl_string_codec.h" + "fl_value.h" + "fl_view.h" + "flutter_linux.h" +) +list_prepend(FLUTTER_LIBRARY_HEADERS "${EPHEMERAL_DIR}/flutter_linux/") +add_library(flutter INTERFACE) +target_include_directories(flutter INTERFACE + "${EPHEMERAL_DIR}" +) +target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}") +target_link_libraries(flutter INTERFACE + PkgConfig::GTK + PkgConfig::GLIB + PkgConfig::GIO +) +add_dependencies(flutter flutter_assemble) + +# === Flutter tool backend === +# _phony_ is a non-existent file to force this command to run every time, +# since currently there's no way to get a full input/output list from the +# flutter tool. +add_custom_command( + OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} + ${CMAKE_CURRENT_BINARY_DIR}/_phony_ + COMMAND ${CMAKE_COMMAND} -E env + ${FLUTTER_TOOL_ENVIRONMENT} + "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.sh" + ${FLUTTER_TARGET_PLATFORM} ${CMAKE_BUILD_TYPE} + VERBATIM +) +add_custom_target(flutter_assemble DEPENDS + "${FLUTTER_LIBRARY}" + ${FLUTTER_LIBRARY_HEADERS} +) diff --git a/linux/flutter/generated_plugin_registrant.cc b/linux/flutter/generated_plugin_registrant.cc new file mode 100644 index 000000000..77cfc4257 --- /dev/null +++ b/linux/flutter/generated_plugin_registrant.cc @@ -0,0 +1,27 @@ +// +// Generated file. Do not edit. +// + +// clang-format off + +#include "generated_plugin_registrant.h" + +#include <cw_monero/cw_monero_plugin.h> +#include <devicelocale/devicelocale_plugin.h> +#include <platform_device_id_linux/platform_device_id_linux_plugin.h> +#include <url_launcher_linux/url_launcher_plugin.h> + +void fl_register_plugins(FlPluginRegistry* registry) { + g_autoptr(FlPluginRegistrar) cw_monero_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "CwMoneroPlugin"); + cw_monero_plugin_register_with_registrar(cw_monero_registrar); + g_autoptr(FlPluginRegistrar) devicelocale_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "DevicelocalePlugin"); + devicelocale_plugin_register_with_registrar(devicelocale_registrar); + g_autoptr(FlPluginRegistrar) platform_device_id_linux_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "PlatformDeviceIdLinuxPlugin"); + platform_device_id_linux_plugin_register_with_registrar(platform_device_id_linux_registrar); + g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin"); + url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar); +} diff --git a/linux/flutter/generated_plugin_registrant.h b/linux/flutter/generated_plugin_registrant.h new file mode 100644 index 000000000..e0f0a47bc --- /dev/null +++ b/linux/flutter/generated_plugin_registrant.h @@ -0,0 +1,15 @@ +// +// Generated file. Do not edit. +// + +// clang-format off + +#ifndef GENERATED_PLUGIN_REGISTRANT_ +#define GENERATED_PLUGIN_REGISTRANT_ + +#include <flutter_linux/flutter_linux.h> + +// Registers Flutter plugins. +void fl_register_plugins(FlPluginRegistry* registry); + +#endif // GENERATED_PLUGIN_REGISTRANT_ diff --git a/linux/flutter/generated_plugins.cmake b/linux/flutter/generated_plugins.cmake new file mode 100644 index 000000000..e432c44eb --- /dev/null +++ b/linux/flutter/generated_plugins.cmake @@ -0,0 +1,27 @@ +# +# Generated file, do not edit. +# + +list(APPEND FLUTTER_PLUGIN_LIST + cw_monero + devicelocale + platform_device_id_linux + url_launcher_linux +) + +list(APPEND FLUTTER_FFI_PLUGIN_LIST +) + +set(PLUGIN_BUNDLED_LIBRARIES) + +foreach(plugin ${FLUTTER_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/linux plugins/${plugin}) + target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) + list(APPEND PLUGIN_BUNDLED_LIBRARIES $<TARGET_FILE:${plugin}_plugin>) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) +endforeach(plugin) + +foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/linux plugins/${ffi_plugin}) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) +endforeach(ffi_plugin) diff --git a/linux/main.cc b/linux/main.cc new file mode 100644 index 000000000..e7c5c5437 --- /dev/null +++ b/linux/main.cc @@ -0,0 +1,6 @@ +#include "my_application.h" + +int main(int argc, char** argv) { + g_autoptr(MyApplication) app = my_application_new(); + return g_application_run(G_APPLICATION(app), argc, argv); +} diff --git a/linux/my_application.cc b/linux/my_application.cc new file mode 100644 index 000000000..7375d05ca --- /dev/null +++ b/linux/my_application.cc @@ -0,0 +1,104 @@ +#include "my_application.h" + +#include <flutter_linux/flutter_linux.h> +#ifdef GDK_WINDOWING_X11 +#include <gdk/gdkx.h> +#endif + +#include "flutter/generated_plugin_registrant.h" + +struct _MyApplication { + GtkApplication parent_instance; + char** dart_entrypoint_arguments; +}; + +G_DEFINE_TYPE(MyApplication, my_application, GTK_TYPE_APPLICATION) + +// Implements GApplication::activate. +static void my_application_activate(GApplication* application) { + MyApplication* self = MY_APPLICATION(application); + GtkWindow* window = + GTK_WINDOW(gtk_application_window_new(GTK_APPLICATION(application))); + + // Use a header bar when running in GNOME as this is the common style used + // by applications and is the setup most users will be using (e.g. Ubuntu + // desktop). + // If running on X and not using GNOME then just use a traditional title bar + // in case the window manager does more exotic layout, e.g. tiling. + // If running on Wayland assume the header bar will work (may need changing + // if future cases occur). + gboolean use_header_bar = TRUE; +#ifdef GDK_WINDOWING_X11 + GdkScreen* screen = gtk_window_get_screen(window); + if (GDK_IS_X11_SCREEN(screen)) { + const gchar* wm_name = gdk_x11_screen_get_window_manager_name(screen); + if (g_strcmp0(wm_name, "GNOME Shell") != 0) { + use_header_bar = FALSE; + } + } +#endif + if (use_header_bar) { + GtkHeaderBar* header_bar = GTK_HEADER_BAR(gtk_header_bar_new()); + gtk_widget_show(GTK_WIDGET(header_bar)); + gtk_header_bar_set_title(header_bar, "cake_wallet"); + gtk_header_bar_set_show_close_button(header_bar, TRUE); + gtk_window_set_titlebar(window, GTK_WIDGET(header_bar)); + } else { + gtk_window_set_title(window, "cake_wallet"); + } + + gtk_window_set_default_size(window, 1280, 720); + gtk_widget_show(GTK_WIDGET(window)); + + g_autoptr(FlDartProject) project = fl_dart_project_new(); + fl_dart_project_set_dart_entrypoint_arguments(project, self->dart_entrypoint_arguments); + + FlView* view = fl_view_new(project); + gtk_widget_show(GTK_WIDGET(view)); + gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(view)); + + fl_register_plugins(FL_PLUGIN_REGISTRY(view)); + + gtk_widget_grab_focus(GTK_WIDGET(view)); +} + +// Implements GApplication::local_command_line. +static gboolean my_application_local_command_line(GApplication* application, gchar*** arguments, int* exit_status) { + MyApplication* self = MY_APPLICATION(application); + // Strip out the first argument as it is the binary name. + self->dart_entrypoint_arguments = g_strdupv(*arguments + 1); + + g_autoptr(GError) error = nullptr; + if (!g_application_register(application, nullptr, &error)) { + g_warning("Failed to register: %s", error->message); + *exit_status = 1; + return TRUE; + } + + g_application_activate(application); + *exit_status = 0; + + return TRUE; +} + +// Implements GObject::dispose. +static void my_application_dispose(GObject* object) { + MyApplication* self = MY_APPLICATION(object); + g_clear_pointer(&self->dart_entrypoint_arguments, g_strfreev); + G_OBJECT_CLASS(my_application_parent_class)->dispose(object); +} + +static void my_application_class_init(MyApplicationClass* klass) { + G_APPLICATION_CLASS(klass)->activate = my_application_activate; + G_APPLICATION_CLASS(klass)->local_command_line = my_application_local_command_line; + G_OBJECT_CLASS(klass)->dispose = my_application_dispose; +} + +static void my_application_init(MyApplication* self) {} + +MyApplication* my_application_new() { + return MY_APPLICATION(g_object_new(my_application_get_type(), + "application-id", APPLICATION_ID, + "flags", G_APPLICATION_NON_UNIQUE, + nullptr)); +} diff --git a/linux/my_application.h b/linux/my_application.h new file mode 100644 index 000000000..72271d5e4 --- /dev/null +++ b/linux/my_application.h @@ -0,0 +1,18 @@ +#ifndef FLUTTER_MY_APPLICATION_H_ +#define FLUTTER_MY_APPLICATION_H_ + +#include <gtk/gtk.h> + +G_DECLARE_FINAL_TYPE(MyApplication, my_application, MY, APPLICATION, + GtkApplication) + +/** + * my_application_new: + * + * Creates a new Flutter-based application. + * + * Returns: a new #MyApplication. + */ +MyApplication* my_application_new(); + +#endif // FLUTTER_MY_APPLICATION_H_ diff --git a/macos/CakeWallet/decrypt.swift b/macos/CakeWallet/decrypt.swift new file mode 100644 index 000000000..5f24ad3fb --- /dev/null +++ b/macos/CakeWallet/decrypt.swift @@ -0,0 +1,16 @@ +import Foundation +import CryptoSwift + +func decrypt(data: Data, key: String, salt: String) -> String? { + let keyBytes = key.data(using: .utf8)?.bytes ?? [] + let saltBytes = salt.data(using: .utf8)?.bytes ?? [] + + guard let PBKDF2key = try? PKCS5.PBKDF2(password: keyBytes, salt: saltBytes, iterations: 4096, variant: .sha256).calculate(), + let cipher = try? Blowfish(key: PBKDF2key, padding: .pkcs7), + let decryptedBytes = try? cipher.decrypt(data.bytes) else { + return nil + } + + let decryptedData = Data(decryptedBytes) + return String(data: decryptedData, encoding: .utf8) +} diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index c55241797..d99b4bedd 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -5,13 +5,12 @@ import FlutterMacOS import Foundation -import connectivity_plus_macos +import connectivity_macos import cw_monero import device_info_plus import devicelocale -import flutter_secure_storage_macos import in_app_review -import package_info +import package_info_plus import path_provider_foundation import platform_device_id import platform_device_id_macos @@ -25,9 +24,8 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { CwMoneroPlugin.register(with: registry.registrar(forPlugin: "CwMoneroPlugin")) DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin")) DevicelocalePlugin.register(with: registry.registrar(forPlugin: "DevicelocalePlugin")) - FlutterSecureStoragePlugin.register(with: registry.registrar(forPlugin: "FlutterSecureStoragePlugin")) InAppReviewPlugin.register(with: registry.registrar(forPlugin: "InAppReviewPlugin")) - FLTPackageInfoPlugin.register(with: registry.registrar(forPlugin: "FLTPackageInfoPlugin")) + FLTPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FLTPackageInfoPlusPlugin")) PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) PlatformDeviceIdMacosPlugin.register(with: registry.registrar(forPlugin: "PlatformDeviceIdMacosPlugin")) PlatformDeviceIdMacosPlugin.register(with: registry.registrar(forPlugin: "PlatformDeviceIdMacosPlugin")) diff --git a/macos/Podfile b/macos/Podfile index fe5678c70..0c76ccf54 100644 --- a/macos/Podfile +++ b/macos/Podfile @@ -36,9 +36,5 @@ end post_install do |installer| installer.pods_project.targets.each do |target| flutter_additional_macos_build_settings(target) - - target.build_configurations.each do |config| - config.build_settings['MACOSX_DEPLOYMENT_TARGET'] = '12.0' - end end end diff --git a/macos/Runner.xcodeproj/project.pbxproj b/macos/Runner.xcodeproj/project.pbxproj index 911fa9fcc..a7d9e2807 100644 --- a/macos/Runner.xcodeproj/project.pbxproj +++ b/macos/Runner.xcodeproj/project.pbxproj @@ -75,6 +75,7 @@ 9646C67C7114830A5ACFF5DF /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = "<group>"; }; 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = "<group>"; }; 9F565D5729954F53009A75FB /* secRandom.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = secRandom.swift; path = CakeWallet/secRandom.swift; sourceTree = "<group>"; }; + 9F565D5829954F53009A75FB /* decrypt.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = decrypt.swift; path = CakeWallet/decrypt.swift; sourceTree = "<group>"; }; B38D1DBC56DBD386923BC063 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ @@ -104,6 +105,7 @@ 33CC10E42044A3C60003C045 = { isa = PBXGroup; children = ( + 9F565D5829954F53009A75FB /* decrypt.swift */, 9F565D5729954F53009A75FB /* secRandom.swift */, 33FAB671232836740065AC1E /* Runner */, 33CEB47122A05771004F2AC0 /* Flutter */, diff --git a/model_generator.sh b/model_generator.sh old mode 100644 new mode 100755 index d6e417843..f45fe9637 --- a/model_generator.sh +++ b/model_generator.sh @@ -1,3 +1,5 @@ +#!/bin/sh + cd cw_core && flutter pub get && flutter packages pub run build_runner build --delete-conflicting-outputs && cd .. cd cw_monero && flutter pub get && flutter packages pub run build_runner build --delete-conflicting-outputs && cd .. cd cw_bitcoin && flutter pub get && flutter packages pub run build_runner build --delete-conflicting-outputs && cd .. diff --git a/pubspec_base.yaml b/pubspec_base.yaml index 4655a70b5..0216658be 100644 --- a/pubspec_base.yaml +++ b/pubspec_base.yaml @@ -11,12 +11,6 @@ dependencies: ref: cake-4.0.2 version: 4.0.2 shared_preferences: ^2.0.15 - flutter_secure_storage: - git: - url: https://github.com/cake-tech/flutter_secure_storage.git - path: flutter_secure_storage - ref: cake-8.0.0 - version: 8.0.0 # provider: ^6.0.3 rxdart: ^0.27.4 yaml: ^3.1.1 @@ -35,8 +29,7 @@ dependencies: hive_flutter: ^1.1.0 local_auth: ^2.1.0 local_auth_android: 1.0.21 - package_info: ^2.0.0 - #package_info_plus: ^1.4.2 + package_info_plus: ^4.1.0 devicelocale: git: url: https://github.com/cake-tech/flutter-devicelocale @@ -54,8 +47,8 @@ dependencies: # password: ^1.0.0 basic_utils: ^5.6.1 get_it: ^7.2.0 - # connectivity: ^3.0.3 - connectivity_plus: ^2.3.5 + connectivity: ^3.0.3 + # connectivity_plus: ^2.3.5 keyboard_actions: ^4.0.1 another_flushbar: ^1.12.29 archive: ^3.3.0 @@ -67,7 +60,7 @@ dependencies: device_display_brightness: ^0.0.6 workmanager: ^0.5.1 platform_device_id: ^1.0.1 - wakelock: ^0.6.2 + wakelock_plus: ^1.1.1 flutter_mailer: ^2.0.2 device_info_plus: 8.1.0 base32: 2.1.3 diff --git a/res/values/strings_ar.arb b/res/values/strings_ar.arb index 5bd8906fe..41aef08dd 100644 --- a/res/values/strings_ar.arb +++ b/res/values/strings_ar.arb @@ -322,6 +322,12 @@ "trade_state_timeout": "نفذ الوقت", "trade_state_created": "تم الأنشاء", "trade_state_finished": "تم", + "invalid_password": "رمز مرور خاطئ", + "unlock": "الغاء القفل", + "enter_wallet_password": "أدخل كلمة مرور المحفظة", + "repeate_wallet_password": "كرر كلمة مرور المحفظة", + "wallet_password_is_empty": "كلمة مرور المحفظة فارغة. يجب ألا تكون كلمة مرور المحفظة فارغة", + "repeated_password_is_incorrect": "كلمة المرور المتكررة غير صحيحة. يرجى إعادة كلمة مرور المحفظة مرة أخرى.", "change_language": "تغيير اللغة", "change_language_to": "هل تريد تغيير اللغة إلى ${language}؟", "paste": "لصق", @@ -339,6 +345,12 @@ "template": "قالب", "confirm_delete_template": "سيؤدي هذا الإجراء إلى حذف هذا القالب. هل ترغب في الاستمرار؟", "confirm_delete_wallet": "سيؤدي هذا الإجراء إلى حذف هذه المحفظة. هل ترغب في الاستمرار؟", + "invalid_password": "رمز مرور خاطئ", + "unlock": "الغاء القفل", + "enter_wallet_password": "أدخل كلمة مرور المحفظة", + "repeate_wallet_password": "كرر كلمة مرور المحفظة", + "wallet_password_is_empty": "كلمة مرور المحفظة فارغة. يجب ألا تكون كلمة مرور المحفظة فارغة", + "repeated_password_is_incorrect": "كلمة المرور المتكررة غير صحيحة. يرجى إعادة كلمة مرور المحفظة مرة أخرى.", "picker_description": "لاختيار ChangeNOW أو MorphToken ، يرجى تغيير زوج التداول الخاص بك أولاً", "change_wallet_alert_title": "تغيير المحفظة الحالية", "change_wallet_alert_content": "هل تريد تغيير المحفظة الحالية إلى ${wallet_name}؟", @@ -630,18 +642,18 @@ "setup_totp_recommended": "إعداد TOTP (موصى به)", "disable_buy": "تعطيل إجراء الشراء", "disable_sell": "قم بتعطيل إجراء البيع", - "cake_2fa_preset": " كعكة 2FA مسبقا", + "cake_2fa_preset" : " كعكة 2FA مسبقا", "narrow": "ضيق", "normal": "طبيعي", "aggressive": "عنيف", "require_for_assessing_wallet": "تتطلب الوصول إلى المحفظة", - "require_for_sends_to_non_contacts": "تتطلب لارسال لغير جهات الاتصال", - "require_for_sends_to_contacts": "تتطلب لارسال جهات الاتصال", - "require_for_sends_to_internal_wallets": "تتطلب عمليات الإرسال إلى المحافظ الداخلية", - "require_for_exchanges_to_internal_wallets": "تتطلب عمليات التبادل إلى المحافظ الداخلية", - "require_for_adding_contacts": "تتطلب إضافة جهات اتصال", - "require_for_creating_new_wallets": "تتطلب إنشاء محافظ جديدة", - "require_for_all_security_and_backup_settings": "مطلوب لجميع إعدادات الأمان والنسخ الاحتياطي", + "require_for_sends_to_non_contacts" : "تتطلب لارسال لغير جهات الاتصال", + "require_for_sends_to_contacts" : "تتطلب لارسال جهات الاتصال", + "require_for_sends_to_internal_wallets" : "تتطلب عمليات الإرسال إلى المحافظ الداخلية", + "require_for_exchanges_to_internal_wallets" : "تتطلب عمليات التبادل إلى المحافظ الداخلية", + "require_for_adding_contacts" : "تتطلب إضافة جهات اتصال", + "require_for_creating_new_wallets" : "تتطلب إنشاء محافظ جديدة", + "require_for_all_security_and_backup_settings" : "مطلوب لجميع إعدادات الأمان والنسخ الاحتياطي", "available_balance_description": "الرصيد المتاح هو الرصيد الذي يمكنك إنفاقه أو تحويله إلى محفظة أخرى. يتم تجميد الرصيد المتاح للمعاملات الصادرة والمعاملات الواردة غير المؤكدة.", "syncing_wallet_alert_title": "محفظتك تتم مزامنتها", "syncing_wallet_alert_content": "قد لا يكتمل رصيدك وقائمة المعاملات الخاصة بك حتى تظهر عبارة “SYNCHRONIZED“ في الأعلى. انقر / اضغط لمعرفة المزيد.", diff --git a/res/values/strings_bg.arb b/res/values/strings_bg.arb index 325057aa4..bff3c71d3 100644 --- a/res/values/strings_bg.arb +++ b/res/values/strings_bg.arb @@ -322,6 +322,12 @@ "trade_state_timeout": "Време за изчакване", "trade_state_created": "Създадено", "trade_state_finished": "Завършено", + "invalid_password" : "Невалидна парола", + "unlock" : "Отключи", + "enter_wallet_password" : "Въведете паролата на портфейла", + "repeate_wallet_password" : "Повторете паролата на портфейла", + "wallet_password_is_empty" : "Паролата за Wallet е празна. Паролата за Wallet не трябва да е празна", + "repeated_password_is_incorrect" : "Повтарящата се парола е неправилна. Моля, повторете отново паролата за портфейла.", "change_language": "Смяна на езика", "change_language_to": "Смяна на езика на ${language}?", "paste": "Поставяне", @@ -344,6 +350,12 @@ "change_wallet_alert_content": "Искате ли да смените сегашния портфейл на ${wallet_name}?", "creating_new_wallet": "Създаване на нов портфейл", "creating_new_wallet_error": "Грешка: ${description}", + "invalid_password" : "Невалидна парола", + "unlock" : "Отключи", + "enter_wallet_password" : "Въведете паролата на портфейла", + "repeate_wallet_password" : "Повторете паролата на портфейла", + "wallet_password_is_empty" : "Паролата за Wallet е празна. Паролата за Wallet не трябва да е празна", + "repeated_password_is_incorrect" : "Повтарящата се парола е неправилна. Моля, повторете отново паролата за портфейла.", "seed_alert_title": "Внимание", "seed_alert_content": "Seed-ът е единственият начин да възстановите портфейла си. Записахте ли го?", "seed_alert_back": "Назад", @@ -603,6 +615,7 @@ "sell_monero_com_alert_content": "Продажбата на Monero все още не се поддържа", "error_text_input_below_minimum_limit": "Сумата е по-малко от минималната", "error_text_input_above_maximum_limit": "Сумата надвишава максималната", + "settings": "Настройки", "show_market_place": "Покажи пазар", "prevent_screenshots": "Предотвратете екранни снимки и запис на екрана", "profile": "Профил", diff --git a/res/values/strings_cs.arb b/res/values/strings_cs.arb index a258eaa40..442b304bb 100644 --- a/res/values/strings_cs.arb +++ b/res/values/strings_cs.arb @@ -322,6 +322,12 @@ "trade_state_timeout": "Vypršel časový limit", "trade_state_created": "Vytvořeno", "trade_state_finished": "Hotovo", + "invalid_password" : "Neplatné heslo", + "unlock" : "Odemknout", + "enter_wallet_password" : "Zadejte heslo peněženky", + "repeate_wallet_password" : "Opakujte heslo peněženky", + "wallet_password_is_empty" : "Heslo peněženky je prázdné. Heslo peněženky by nemělo být prázdné", + "repeated_password_is_incorrect" : "Opakované heslo je nesprávné. Zopakujte prosím heslo peněženky znovu.", "change_language": "Změnit jazyk", "change_language_to": "Změnit jazyk na ${language}?", "paste": "Vložit", @@ -670,6 +676,7 @@ "monero_light_theme": "Světlé téma Monero", "manage_nodes": "Spravovat uzly", "etherscan_history": "Historie Etherscanu", +<<<<<<< HEAD "template_name": "Název šablony", "support_title_live_chat": "Živá podpora", "support_description_live_chat": "Zdarma a rychle! K dispozici jsou zástupci vyškolených podpůrných podpory", @@ -679,4 +686,7 @@ "support_description_other_links": "Připojte se k našim komunitám nebo se k nám oslovte další metody", "select_destination": "Vyberte cíl pro záložní soubor.", "save_to_downloads": "Uložit do Stažených souborů" +======= + "template_name": "Název šablony" +>>>>>>> origin/linux/password-direct-input } diff --git a/res/values/strings_de.arb b/res/values/strings_de.arb index b315ddb56..3856b2d91 100644 --- a/res/values/strings_de.arb +++ b/res/values/strings_de.arb @@ -211,7 +211,7 @@ "settings_only_trades": "Nur Handel", "settings_only_transactions": "Nur Transaktionen", "settings_none": "Keiner", - "settings_support": "Hilfe", + "settings_support": "Unterstützen", "settings_terms_and_conditions": "Geschäftsbedingungen", "pin_is_incorrect": "PIN ist falsch", "setup_pin": "PIN einrichten", @@ -322,6 +322,12 @@ "trade_state_timeout": "Timeout", "trade_state_created": "Erstellt", "trade_state_finished": "Fertig", + "invalid_password" : "Ongeldig wachtwoord", + "unlock" : "Freischalten", + "enter_wallet_password" : "Geben Sie das Wallet-Passwort ein", + "repeat_wallet_password" : "Wiederholen Sie das Wallet-Passwort", + "wallet_password_is_empty" : "Wallet-Passwort ist leer. Wallet-Passwort darf nicht leer sein", + "repeated_password_is_incorrect" : "Wiederholtes Passwort ist falsch. Bitte wiederholen Sie das Wallet-Passwort noch einmal.", "change_language": "Sprache ändern", "change_language_to": "Sprache zu ${language} ändern?", "paste": "Einfügen", @@ -640,7 +646,11 @@ "high_contrast_theme": "Kontrastreiches Thema", "matrix_green_dark_theme": "Matrix Green Dark Theme", "monero_light_theme": "Monero Light-Thema", +<<<<<<< HEAD "cake_2fa_preset" : "Cake 2FA-Voreinstellung", +======= + "cake_2fa_preset" : "Kuchen 2FA-Voreinstellung", +>>>>>>> origin/linux/password-direct-input "narrow": "Eng", "normal": "Normal", "aggressive": "Übereifrig", @@ -678,6 +688,7 @@ "slidable": "Verschiebbar", "manage_nodes": "Knoten verwalten", "etherscan_history": "Etherscan-Geschichte", +<<<<<<< HEAD "template_name": "Vorlagenname", "support_title_live_chat": "Live Support", "support_description_live_chat": "Kostenlos und schnell! Ausgebildete Mitarbeiter stehen zur Unterstützung bereit, um zu helfen", @@ -687,4 +698,7 @@ "support_description_other_links": "Treten Sie unseren Communities bei oder erreichen Sie uns oder unsere Partner über andere Methoden", "select_destination": "Bitte wählen Sie das Ziel für die Sicherungsdatei aus.", "save_to_downloads": "Unter „Downloads“ speichern" +======= + "template_name": "Vorlagenname" +>>>>>>> origin/linux/password-direct-input } diff --git a/res/values/strings_en.arb b/res/values/strings_en.arb index 9e8e3788c..0bcd23554 100644 --- a/res/values/strings_en.arb +++ b/res/values/strings_en.arb @@ -322,6 +322,12 @@ "trade_state_timeout": "Timeout", "trade_state_created": "Created", "trade_state_finished": "Finished", + "invalid_password" : "Invalid password", + "unlock" : "Unlock", + "enter_wallet_password" : "Enter the wallet password", + "repeate_wallet_password" : "Repeat the wallet password", + "wallet_password_is_empty" : "Wallet password is empty. Wallet password should not be empty", + "repeated_password_is_incorrect" : "Repeated password is incorrect. Please repeat the wallet password again.", "change_language": "Change language", "change_language_to": "Change language to ${language}?", "paste": "Paste", @@ -640,7 +646,11 @@ "high_contrast_theme": "High Contrast Theme", "matrix_green_dark_theme": "Matrix Green Dark Theme", "monero_light_theme": "Monero Light Theme", +<<<<<<< HEAD "cake_2fa_preset": "Cake 2FA Preset", +======= + "cake_2fa_preset" : "Cake 2FA Preset", +>>>>>>> origin/linux/password-direct-input "narrow": "Narrow", "normal": "Normal", "aggressive": "Aggressive", @@ -678,6 +688,7 @@ "slidable": "Slidable", "manage_nodes": "Manage nodes", "etherscan_history": "Etherscan history", +<<<<<<< HEAD "template_name": "Template Name", "support_title_live_chat": "Live support", "support_description_live_chat": "Free and fast! Trained support representatives are available to assist", @@ -687,4 +698,7 @@ "support_description_other_links": "Join our communities or reach us our our partners through other methods", "select_destination": "Please select destination for the backup file.", "save_to_downloads": "Save to Downloads" +======= + "template_name": "Template Name" +>>>>>>> origin/linux/password-direct-input } diff --git a/res/values/strings_es.arb b/res/values/strings_es.arb index 3c0abce48..d8713fba3 100644 --- a/res/values/strings_es.arb +++ b/res/values/strings_es.arb @@ -322,6 +322,12 @@ "trade_state_timeout": "Se acabó el tiempo", "trade_state_created": "Creado", "trade_state_finished": "Terminado", + "invalid_password" : "Contraseña invalida", + "unlock" : "desbloquear", + "enter_wallet_password" : "Ingrese la contraseña de la billetera", + "repeate_wallet_password" : "Repita la contraseña de la billetera", + "wallet_password_is_empty" : "La contraseña de la billetera está vacía. La contraseña de la billetera no debe estar vacía", + "repeated_password_is_incorrect" : "La contraseña repetida es incorrecta. Repita la contraseña de la billetera nuevamente.", "change_language": "Cambiar idioma", "change_language_to": "Cambiar el idioma a ${language}?", "paste": "Pegar", @@ -640,7 +646,11 @@ "high_contrast_theme": "Tema de alto contraste", "matrix_green_dark_theme": "Matrix verde oscuro tema", "monero_light_theme": "Tema ligero de Monero", +<<<<<<< HEAD "cake_2fa_preset": "Pastel 2FA preestablecido", +======= + "cake_2fa_preset" : "Pastel 2FA preestablecido", +>>>>>>> origin/linux/password-direct-input "narrow": "Angosto", "normal": "Normal", "aggressive": "Demasiado entusiasta", @@ -678,6 +688,7 @@ "slidable": "deslizable", "manage_nodes": "Administrar nodos", "etherscan_history": "historia de etherscan", +<<<<<<< HEAD "template_name": "Nombre de la plantilla", "support_title_live_chat": "Soporte vital", "support_description_live_chat": "¡GRATIS y RÁPIDO! Los representantes de apoyo capacitado están disponibles para ayudar", @@ -687,4 +698,7 @@ "support_description_other_links": "Únase a nuestras comunidades o comuníquese con nosotros nuestros socios a través de otros métodos", "select_destination": "Seleccione el destino del archivo de copia de seguridad.", "save_to_downloads": "Guardar en Descargas" +======= + "template_name": "Nombre de la plantilla" +>>>>>>> origin/linux/password-direct-input } diff --git a/res/values/strings_fr.arb b/res/values/strings_fr.arb index 1449d6370..8a3d60169 100644 --- a/res/values/strings_fr.arb +++ b/res/values/strings_fr.arb @@ -322,6 +322,12 @@ "trade_state_timeout": "Délai dépassé", "trade_state_created": "Créé", "trade_state_finished": "Terminé", + "invalid_password" : "Mot de passe incorrect", + "unlock" : "Ouvrir", + "enter_wallet_password" : "Entrez le mot de passe du portefeuille", + "repeate_wallet_password" : "Répétez le mot de passe du portefeuille", + "wallet_password_is_empty" : "Le mot de passe du portefeuille est vide. Le mot de passe du portefeuille ne doit pas être vide", + "repeated_password_is_incorrect" : "Le mot de passe répété est incorrect. Veuillez répéter le mot de passe du portefeuille.", "change_language": "Changer de langue", "change_language_to": "Changer la langue vers ${language} ?", "paste": "Coller", @@ -640,7 +646,11 @@ "high_contrast_theme": "Thème à contraste élevé", "matrix_green_dark_theme": "Thème Matrix Green Dark", "monero_light_theme": "Thème de lumière Monero", +<<<<<<< HEAD "cake_2fa_preset": "Gâteau 2FA prédéfini", +======= + "cake_2fa_preset" : "Gâteau 2FA prédéfini", +>>>>>>> origin/linux/password-direct-input "narrow": "Étroit", "normal": "Normal", "aggressive": "Trop zélé", @@ -678,6 +688,7 @@ "slidable": "Glissable", "manage_nodes": "Gérer les nœuds", "etherscan_history": "Historique d'Etherscan", +<<<<<<< HEAD "template_name": "Nom du modèle", "support_title_live_chat": "Support en direct", "support_description_live_chat": "GRATUIT ET RAPIDE! Des représentants de soutien formé sont disponibles pour aider", @@ -687,4 +698,7 @@ "support_description_other_links": "Rejoignez nos communautés ou contactez-nous nos partenaires à travers d'autres méthodes", "select_destination": "Veuillez sélectionner la destination du fichier de sauvegarde.", "save_to_downloads": "Enregistrer dans les téléchargements" +======= + "template_name": "Nom du modèle" +>>>>>>> origin/linux/password-direct-input } diff --git a/res/values/strings_ha.arb b/res/values/strings_ha.arb index e3542e9ac..b2b0269b5 100644 --- a/res/values/strings_ha.arb +++ b/res/values/strings_ha.arb @@ -322,6 +322,12 @@ "trade_state_timeout": "lokacin da ya ƙare", "trade_state_created": "an halicci", "trade_state_finished": "an kammala", + "invalid_password" : "Mot de passe incorrect", + "unlock" : "Ouvrir", + "enter_wallet_password" : "Entrez le mot de passe du portefeuille", + "repeate_wallet_password" : "Répétez le mot de passe du portefeuille", + "wallet_password_is_empty" : "Le mot de passe du portefeuille est vide. Le mot de passe du portefeuille ne doit pas être vide", + "repeated_password_is_incorrect" : "Le mot de passe répété est incorrect. Veuillez répéter le mot de passe du portefeuille.", "change_language": "canja harshen", "change_language_to": "canja harshen zuwa ${language}?", "paste": "Manna", @@ -618,7 +624,11 @@ "high_contrast_theme": "Babban Jigon Kwatance", "matrix_green_dark_theme": "Matrix Green Dark Jigo", "monero_light_theme": "Jigon Hasken Monero", +<<<<<<< HEAD "cake_2fa_preset": "Cake 2FA saiti", +======= + "cake_2fa_preset" : "Cake 2FA saiti", +>>>>>>> origin/linux/password-direct-input "narrow": "kunkuntar", "normal": "Na al'ada", "aggressive": "Mai tsananin kishi", @@ -656,6 +666,7 @@ "slidable": "Mai iya zamewa", "etherscan_history": "Etherscan tarihin kowane zamani", "manage_nodes": "Sarrafa nodes", +<<<<<<< HEAD "template_name": "Sunan Samfura", "support_title_live_chat": "Tallafi na Live", "support_description_live_chat": "Kyauta da sauri! An horar da wakilan tallafi na tallafi don taimakawa", @@ -665,4 +676,7 @@ "support_description_other_links": "Kasance tare da al'ummominmu ko kuma ka kai mu abokanmu ta hanyar wasu hanyoyi", "select_destination": "Da fatan za a zaɓi wurin da za a yi wa madadin fayil ɗin.", "save_to_downloads": "Ajiye zuwa Zazzagewa" +======= + "template_name": "Sunan Samfura" +>>>>>>> origin/linux/password-direct-input } diff --git a/res/values/strings_hi.arb b/res/values/strings_hi.arb index ca3e79bd0..77a88184c 100644 --- a/res/values/strings_hi.arb +++ b/res/values/strings_hi.arb @@ -322,6 +322,12 @@ "trade_state_timeout": "समय समाप्त", "trade_state_created": "बनाया था", "trade_state_finished": "ख़त्म होना", + "invalid_password" : "अवैध पासवर्ड", + "unlock" : "अनलॉक", + "enter_wallet_password" : "वॉलेट पासवर्ड दर्ज करें", + "repeate_wallet_password" : "वॉलेट पासवर्ड दोहराएं", + "wallet_password_is_empty" : "वॉलेट पासवर्ड खाली है। वॉलेट पासवर्ड खाली नहीं होना चाहिए", + "repeated_password_is_incorrect" : "दोहराया गया पासवर्ड गलत है। कृपया वॉलेट पासवर्ड दोबारा दोहराएं।", "change_language": "भाषा बदलो", "change_language_to": "को भाषा बदलें ${language}?", "paste": "पेस्ट करें", @@ -640,7 +646,11 @@ "high_contrast_theme": "उच्च कंट्रास्ट थीम", "matrix_green_dark_theme": "मैट्रिक्स ग्रीन डार्क थीम", "monero_light_theme": "मोनेरो लाइट थीम", +<<<<<<< HEAD "cake_2fa_preset": "केक 2एफए प्रीसेट", +======= + "cake_2fa_preset" : "केक 2एफए प्रीसेट", +>>>>>>> origin/linux/password-direct-input "narrow": "सँकरा", "normal": "सामान्य", "aggressive": "ज्यादा", @@ -678,6 +688,7 @@ "slidable": "फिसलने लायक", "manage_nodes": "नोड्स प्रबंधित करें", "etherscan_history": "इथरस्कैन इतिहास", +<<<<<<< HEAD "template_name": "टेम्पलेट नाम", "support_title_live_chat": "लाइव सहायता", "support_description_live_chat": "मुक्त और तेजी से! प्रशिक्षित सहायता प्रतिनिधि सहायता के लिए उपलब्ध हैं", @@ -687,4 +698,7 @@ "support_description_other_links": "हमारे समुदायों में शामिल हों या अन्य तरीकों के माध्यम से हमारे साथी तक पहुंचें", "select_destination": "कृपया बैकअप फ़ाइल के लिए गंतव्य का चयन करें।", "save_to_downloads": "डाउनलोड में सहेजें" +======= + "template_name": "टेम्पलेट नाम" +>>>>>>> origin/linux/password-direct-input } diff --git a/res/values/strings_hr.arb b/res/values/strings_hr.arb index e19d32ea1..81de2bcf5 100644 --- a/res/values/strings_hr.arb +++ b/res/values/strings_hr.arb @@ -322,6 +322,12 @@ "trade_state_timeout": "Isteklo", "trade_state_created": "Stvoreno", "trade_state_finished": "Završeno", + "invalid_password" : "Érvénytelen jelszó", + "unlock" : "Kinyit", + "enter_wallet_password" : "Adja meg a pénztárca jelszavát", + "repeate_wallet_password" : "Az ismételt jelszó helytelen. Kérjük, ismételje meg újra a pénztárca jelszavát.", + "wallet_password_is_empty" : "A Wallet jelszó üres. A Wallet jelszó nem lehet üres", + "repeated_password_is_incorrect" : "Az ismételt jelszó helytelen. Kérjük, ismételje meg újra a pénztárca jelszavát.", "change_language": "Promijeni jezik", "change_language_to": "Promijeni jezik u ${language}?", "paste": "Zalijepi", @@ -640,7 +646,11 @@ "high_contrast_theme": "Tema visokog kontrasta", "matrix_green_dark_theme": "Matrix Green Dark Theme", "monero_light_theme": "Monero lagana tema", +<<<<<<< HEAD "cake_2fa_preset": "Cake 2FA Preset", +======= + "cake_2fa_preset" : "Cake 2FA Preset", +>>>>>>> origin/linux/password-direct-input "narrow": "Usko", "normal": "Normalno", "aggressive": "Preterano", @@ -678,6 +688,7 @@ "slidable": "Klizna", "manage_nodes": "Upravljanje čvorovima", "etherscan_history": "Etherscan povijest", +<<<<<<< HEAD "template_name": "Naziv predloška", "support_title_live_chat": "Podrška uživo", "support_description_live_chat": "Besplatno i brzo! Obučeni predstavnici podrške dostupni su za pomoć", @@ -687,4 +698,7 @@ "support_description_other_links": "Pridružite se našim zajednicama ili nam dosegnu naše partnere drugim metodama", "select_destination": "Odaberite odredište za datoteku sigurnosne kopije.", "save_to_downloads": "Spremi u Preuzimanja" +======= + "template_name": "Naziv predloška" +>>>>>>> origin/linux/password-direct-input } diff --git a/res/values/strings_id.arb b/res/values/strings_id.arb index 3f62f968c..f94a514a0 100644 --- a/res/values/strings_id.arb +++ b/res/values/strings_id.arb @@ -322,6 +322,12 @@ "trade_state_timeout": "Waktu habis", "trade_state_created": "Dibuat", "trade_state_finished": "Selesai", + "invalid_password" : "Kata sandi salah", + "unlock" : "Membuka kunci", + "enter_wallet_password" : "Masukkan kata sandi dompet", + "repeate_wallet_password" : "Ulangi kata sandi dompet", + "wallet_password_is_empty" : "Kata sandi dompet kosong. Kata sandi dompet tidak boleh kosong", + "repeated_password_is_incorrect" : "Kata sandi berulang salah. Harap ulangi kata sandi dompet lagi.", "change_language": "Ganti bahasa", "change_language_to": "Ganti bahasa ke ${language}?", "paste": "Tempel", @@ -599,6 +605,7 @@ "sell_monero_com_alert_content": "Menjual Monero belum didukung", "error_text_input_below_minimum_limit": "Jumlah kurang dari minimal", "error_text_input_above_maximum_limit": "Jumlah lebih dari maksimal", + "settings": "Pengaturan", "show_market_place": "Tampilkan Pasar", "prevent_screenshots": "Cegah tangkapan layar dan perekaman layar", "profile": "Profil", @@ -666,6 +673,7 @@ "monero_light_theme": "Tema Cahaya Monero", "manage_nodes": "Kelola node", "etherscan_history": "Sejarah Etherscan", +<<<<<<< HEAD "template_name": "Nama Templat", "support_title_live_chat": "Dukungan langsung", "support_description_live_chat": "Gratis dan Cepat! Perwakilan dukungan terlatih tersedia untuk membantu", @@ -675,4 +683,7 @@ "support_description_other_links": "Bergabunglah dengan komunitas kami atau hubungi kami mitra kami melalui metode lain", "select_destination": "Silakan pilih tujuan untuk file cadangan.", "save_to_downloads": "Simpan ke Unduhan" +======= + "template_name": "Nama Templat" +>>>>>>> origin/linux/password-direct-input } diff --git a/res/values/strings_it.arb b/res/values/strings_it.arb index 8ad7502fd..f6c5938e6 100644 --- a/res/values/strings_it.arb +++ b/res/values/strings_it.arb @@ -322,6 +322,12 @@ "trade_state_timeout": "Timeout", "trade_state_created": "Creato", "trade_state_finished": "Finito", + "invalid_password" : "Password non valida", + "unlock" : "Sbloccare", + "enter_wallet_password" : "Inserisci la password del portafoglio", + "repeate_wallet_password" : "Ripeti la password del portafoglio", + "wallet_password_is_empty" : "La password del portafoglio è vuota. La password del portafoglio non deve essere vuota", + "repeated_password_is_incorrect" : "La password ripetuta non è corretta. Ripeti di nuovo la password del portafoglio.", "change_language": "Cambia lingua", "change_language_to": "Cambiare lingua in ${language}?", "paste": "Incolla", @@ -678,6 +684,7 @@ "monero_light_theme": "Tema leggero Monero", "manage_nodes": "Gestisci i nodi", "etherscan_history": "Storia Etherscan", +<<<<<<< HEAD "template_name": "Nome modello", "support_title_live_chat": "Supporto dal vivo", "support_description_live_chat": "Gratuito e veloce! I rappresentanti di supporto qualificati sono disponibili per assistere", @@ -687,4 +694,7 @@ "support_description_other_links": "Unisciti alle nostre comunità o raggiungici i nostri partner attraverso altri metodi", "select_destination": "Seleziona la destinazione per il file di backup.", "save_to_downloads": "Salva in Download" +======= + "template_name": "Nome modello" +>>>>>>> origin/linux/password-direct-input } diff --git a/res/values/strings_ja.arb b/res/values/strings_ja.arb index 3fe586279..25fc67556 100644 --- a/res/values/strings_ja.arb +++ b/res/values/strings_ja.arb @@ -322,6 +322,12 @@ "trade_state_timeout": "タイムアウト", "trade_state_created": "作成した", "trade_state_finished": "完成した", + "invalid_password" : "無効なパスワード", + "unlock" : "ロック解除", + "enter_wallet_password" : "ウォレットのパスワードを入力してください", + "repeate_wallet_password" : "ウォレットのパスワードを繰り返す", + "wallet_password_is_empty" : "ウォレットのパスワードが空です。 ウォレットのパスワードを空にすることはできません", + "repeated_password_is_incorrect" : "繰り返されるパスワードが正しくありません。 ウォレットのパスワードをもう一度入力してください。", "change_language": "言語を変えてください", "change_language_to": "言語を変更 ${language}?", "paste": "ペースト", @@ -634,18 +640,18 @@ "setup_totp_recommended": "TOTP を設定する (推奨)", "disable_buy": "購入アクションを無効にする", "disable_sell": "販売アクションを無効にする", - "cake_2fa_preset": "ケーキ 2FA プリセット", + "cake_2fa_preset" : "ケーキ 2FA プリセット", "narrow": "狭い", "normal": "普通", "aggressive": "熱心すぎる", "require_for_assessing_wallet": "ウォレットにアクセスするために必要です", - "require_for_sends_to_non_contacts": "非連絡先への送信に必須", - "require_for_sends_to_contacts": "連絡先に送信する場合に必須", - "require_for_sends_to_internal_wallets": "内部ウォレットへの送信に必須", - "require_for_exchanges_to_internal_wallets": "内部ウォレットへの交換に必要", - "require_for_adding_contacts": "連絡先の追加に必要", - "require_for_creating_new_wallets": "新しいウォレットを作成するために必要です", - "require_for_all_security_and_backup_settings": "すべてのセキュリティおよびバックアップ設定に必須", + "require_for_sends_to_non_contacts" : "非連絡先への送信に必須", + "require_for_sends_to_contacts" : "連絡先に送信する場合に必須", + "require_for_sends_to_internal_wallets" : "内部ウォレットへの送信に必須", + "require_for_exchanges_to_internal_wallets" : "内部ウォレットへの交換に必要", + "require_for_adding_contacts" : "連絡先の追加に必要", + "require_for_creating_new_wallets" : "新しいウォレットを作成するために必要です", + "require_for_all_security_and_backup_settings" : "すべてのセキュリティおよびバックアップ設定に必須", "available_balance_description": "利用可能な残高は、ウォレットの残高から冷凍残高を差し引いたものです。", "syncing_wallet_alert_title": "ウォレットは同期中です", "syncing_wallet_alert_content": "上部に「同期済み」と表示されるまで、残高と取引リストが完了していない可能性があります。詳細については、クリック/タップしてください。", @@ -678,6 +684,7 @@ "monero_light_theme": "モネロ ライト テーマ", "manage_nodes": "ノードの管理", "etherscan_history": "イーサスキャンの歴史", +<<<<<<< HEAD "template_name": "テンプレート名", "support_title_live_chat": "ライブサポート", "support_description_live_chat": "無料で速い!訓練されたサポート担当者が支援することができます", @@ -687,4 +694,7 @@ "support_description_other_links": "私たちのコミュニティに参加するか、他の方法を通して私たちのパートナーに連絡してください", "select_destination": "バックアップファイルの保存先を選択してください。", "save_to_downloads": "ダウンロードに保存" +======= + "template_name": "テンプレート名" +>>>>>>> origin/linux/password-direct-input } diff --git a/res/values/strings_ko.arb b/res/values/strings_ko.arb index 1b7116e90..1ed301108 100644 --- a/res/values/strings_ko.arb +++ b/res/values/strings_ko.arb @@ -322,6 +322,12 @@ "trade_state_timeout": "타임 아웃", "trade_state_created": "만들어진", "trade_state_finished": "끝마친", + "invalid_password" : "유효하지 않은 비밀번호", + "unlock" : "터놓다", + "enter_wallet_password" : "지갑 비밀번호를 입력하세요", + "repeate_wallet_password" : "지갑 비밀번호를 반복하십시오", + "wallet_password_is_empty" : "지갑 비밀번호가 비어 있습니다. 월렛 비밀번호는 비워둘 수 없습니다.", + "repeated_password_is_incorrect" : "반복되는 비밀번호가 올바르지 않습니다. 지갑 비밀번호를 다시 한번 입력해주세요.", "change_language": "언어 변경", "change_language_to": "언어를로 변경 ${language}?", "paste": "풀", @@ -634,18 +640,18 @@ "setup_totp_recommended": "TOTP 설정(권장)", "disable_buy": "구매 행동 비활성화", "disable_sell": "판매 조치 비활성화", - "cake_2fa_preset": "케이크 2FA 프리셋", + "cake_2fa_preset" : "케이크 2FA 프리셋", "narrow": "좁은", "normal": "정상", "aggressive": "지나치게 열심인", "require_for_assessing_wallet": "지갑 접근을 위해 필요", - "require_for_sends_to_non_contacts": "비접촉자에게 보내는 데 필요", - "require_for_sends_to_contacts": "연락처로 보내기에 필요", - "require_for_sends_to_internal_wallets": "내부 지갑으로 보내는 데 필요", - "require_for_exchanges_to_internal_wallets": "내부 지갑으로의 교환에 필요", - "require_for_adding_contacts": "연락처 추가에 필요", - "require_for_creating_new_wallets": "새 지갑 생성에 필요", - "require_for_all_security_and_backup_settings": "모든 보안 및 백업 설정에 필요", + "require_for_sends_to_non_contacts" : "비접촉자에게 보내는 데 필요", + "require_for_sends_to_contacts" : "연락처로 보내기에 필요", + "require_for_sends_to_internal_wallets" : "내부 지갑으로 보내는 데 필요", + "require_for_exchanges_to_internal_wallets" : "내부 지갑으로의 교환에 필요", + "require_for_adding_contacts" : "연락처 추가에 필요", + "require_for_creating_new_wallets" : "새 지갑 생성에 필요", + "require_for_all_security_and_backup_settings" : "모든 보안 및 백업 설정에 필요", "available_balance_description": "이 지갑에서 사용할 수 있는 잔액입니다. 이 잔액은 블록체인에서 가져온 것이며, Cake Wallet이 사용할 수 없습니다.", "syncing_wallet_alert_title": "지갑 동기화 중", "syncing_wallet_alert_content": "상단에 \"동기화됨\"이라고 표시될 때까지 잔액 및 거래 목록이 완전하지 않을 수 있습니다. 자세히 알아보려면 클릭/탭하세요.", @@ -678,6 +684,7 @@ "monero_light_theme": "모네로 라이트 테마", "manage_nodes": "노드 관리", "etherscan_history": "이더스캔 역사", +<<<<<<< HEAD "template_name": "템플릿 이름", "support_title_live_chat": "실시간 지원", "support_description_live_chat": "자유롭고 빠릅니다! 훈련 된 지원 담당자가 지원할 수 있습니다", @@ -687,4 +694,7 @@ "support_description_other_links": "다른 방법을 통해 커뮤니티에 가입하거나 파트너에게 연락하십시오.", "select_destination": "백업 파일의 대상을 선택하십시오.", "save_to_downloads": "다운로드에 저장" +======= + "template_name": "템플릿 이름" +>>>>>>> origin/linux/password-direct-input } diff --git a/res/values/strings_my.arb b/res/values/strings_my.arb index c12adb5ac..47539ddc8 100644 --- a/res/values/strings_my.arb +++ b/res/values/strings_my.arb @@ -322,6 +322,12 @@ "trade_state_timeout": "ခဏပွဲရပ်ခြင်း", "trade_state_created": "ဖန်တီးခဲ့သည်။", "trade_state_finished": "ပြီးပြီ။", + "invalid_password" : "စကားဝှက် မမှန်ကန်ပါ။", + "unlock" : "သော့ဖွင့်ပါ။", + "enter_wallet_password" : "ပိုက်ဆံအိတ်စကားဝှက်ကိုထည့်ပါ။", + "repeate_wallet_password" : "ပိုက်ဆံအိတ်စကားဝှက်ကို ပြန်လုပ်ပါ။", + "wallet_password_is_empty" : "ပိုက်ဆံအိတ်စကားဝှက်သည် ဗလာဖြစ်နေသည်။ ပိုက်ဆံအိတ်စကားဝှက်သည် ဗလာမဖြစ်သင့်ပါ။", + "repeated_password_is_incorrect" : "ထပ်ခါတလဲလဲ စကားဝှက် မမှန်ပါ။ ပိုက်ဆံအိတ်စကားဝှက်ကို ထပ်လုပ်ပါ။", "change_language": "ဘာသာစကားပြောင်းပါ။", "change_language_to": "ဘာသာစကားကို ${language} သို့ ပြောင်းမလား။", "paste": "ငါးပိ", @@ -632,18 +638,18 @@ "setup_totp_recommended": "TOTP ကို စနစ်ထည့်သွင်းပါ (အကြံပြုထားသည်)", "disable_buy": "ဝယ်ယူမှု လုပ်ဆောင်ချက်ကို ပိတ်ပါ။", "disable_sell": "ရောင်းချခြင်းလုပ်ဆောင်ချက်ကို ပိတ်ပါ။", - "cake_2fa_preset": "ကိတ်မုန့် 2FA ကြိုတင်သတ်မှတ်", + "cake_2fa_preset" : "ကိတ်မုန့် 2FA ကြိုတင်သတ်မှတ်", "narrow": "ကျဉ်းသော", "normal": "ပုံမှန်", "aggressive": "စိတ်အားထက်သန်ခြင်း။", "require_for_assessing_wallet": "ပိုက်ဆံအိတ်ကို ဝင်သုံးရန် လိုအပ်သည်။", - "require_for_sends_to_non_contacts": "အဆက်အသွယ်မရှိသူများထံ ပေးပို့ရန် လိုအပ်သည်။", - "require_for_sends_to_contacts": "အဆက်အသွယ်များထံ ပေးပို့ရန် လိုအပ်သည်။", - "require_for_sends_to_internal_wallets": "အတွင်းပိုင်း ပိုက်ဆံအိတ်များသို့ ပေးပို့ရန် လိုအပ်သည်။", - "require_for_exchanges_to_internal_wallets": "အတွင်းပိုင်းပိုက်ဆံအိတ်များသို့ လဲလှယ်ရန် လိုအပ်သည်။", - "require_for_adding_contacts": "အဆက်အသွယ်များထည့်ရန် လိုအပ်သည်။", - "require_for_creating_new_wallets": "ပိုက်ဆံအိတ်အသစ်များ ဖန်တီးရန် လိုအပ်သည်။", - "require_for_all_security_and_backup_settings": "လုံခြုံရေးနှင့် အရန်ဆက်တင်များအားလုံးအတွက် လိုအပ်ပါသည်။", + "require_for_sends_to_non_contacts" : "အဆက်အသွယ်မရှိသူများထံ ပေးပို့ရန် လိုအပ်သည်။", + "require_for_sends_to_contacts" : "အဆက်အသွယ်များထံ ပေးပို့ရန် လိုအပ်သည်။", + "require_for_sends_to_internal_wallets" : "အတွင်းပိုင်း ပိုက်ဆံအိတ်များသို့ ပေးပို့ရန် လိုအပ်သည်။", + "require_for_exchanges_to_internal_wallets" : "အတွင်းပိုင်းပိုက်ဆံအိတ်များသို့ လဲလှယ်ရန် လိုအပ်သည်။", + "require_for_adding_contacts" : "အဆက်အသွယ်များထည့်ရန် လိုအပ်သည်။", + "require_for_creating_new_wallets" : "ပိုက်ဆံအိတ်အသစ်များ ဖန်တီးရန် လိုအပ်သည်။", + "require_for_all_security_and_backup_settings" : "လုံခြုံရေးနှင့် အရန်ဆက်တင်များအားလုံးအတွက် လိုအပ်ပါသည်။", "available_balance_description": "သင့်ရဲ့ အကောင့်တွင် ရရှိနိုင်သော ငွေကျန်ငွေကို ပြန်လည်ပေးသွင်းပါ။", "syncing_wallet_alert_title": "သင့်ပိုက်ဆံအိတ်ကို စင့်ခ်လုပ်နေပါသည်။", "syncing_wallet_alert_content": "သင်၏လက်ကျန်နှင့် ငွေပေးငွေယူစာရင်းသည် ထိပ်တွင် \"Synchronizeed\" ဟုပြောသည်အထိ မပြီးမြောက်နိုင်ပါ။ ပိုမိုလေ့လာရန် နှိပ်/နှိပ်ပါ။", @@ -676,6 +682,7 @@ "monero_light_theme": "Monero Light အပြင်အဆင်", "manage_nodes": "ဆုံမှတ်များကို စီမံပါ။", "etherscan_history": "Etherscan သမိုင်း", +<<<<<<< HEAD "template_name": "နမူနာပုံစံ", "support_title_live_chat": "တိုက်ရိုက်ပံ့ပိုးမှု", "support_description_live_chat": "အခမဲ့နှင့်အစာရှောင်ခြင်း! လေ့ကျင့်ထားသောထောက်ခံသူကိုယ်စားလှယ်များသည်ကူညီနိုင်သည်", @@ -685,4 +692,7 @@ "support_description_other_links": "ကျွန်ုပ်တို့၏လူမှုအသိုင်းအဝိုင်းများသို့ 0 င်ရောက်ပါ", "select_destination": "အရန်ဖိုင်အတွက် ဦးတည်ရာကို ရွေးပါ။", "save_to_downloads": "ဒေါင်းလုဒ်များထံ သိမ်းဆည်းပါ။" +======= + "template_name": "နမူနာပုံစံ" +>>>>>>> origin/linux/password-direct-input } diff --git a/res/values/strings_nl.arb b/res/values/strings_nl.arb index 18b3b5e5b..35a9bb0c8 100644 --- a/res/values/strings_nl.arb +++ b/res/values/strings_nl.arb @@ -322,6 +322,12 @@ "trade_state_timeout": "Time-out", "trade_state_created": "Gemaakt", "trade_state_finished": "Afgewerkt", + "invalid_password" : "Ongeldig wachtwoord", + "unlock" : "Ontgrendelen", + "enter_wallet_password" : "Voer het portemonnee-wachtwoord in", + "repeate_wallet_password" : "Herhaal het wachtwoord van de portemonnee", + "wallet_password_is_empty" : "Wallet-wachtwoord is leeg. Wallet-wachtwoord mag niet leeg zijn", + "repeated_password_is_incorrect" : "Herhaald wachtwoord is onjuist. Herhaal het wachtwoord van de portemonnee nogmaals.", "change_language": "Verander de taal", "change_language_to": "Verander de taal in ${language}?", "paste": "Plakken", @@ -678,6 +684,7 @@ "monero_light_theme": "Monero Light-thema", "manage_nodes": "Beheer knooppunten", "etherscan_history": "Etherscan-geschiedenis", +<<<<<<< HEAD "template_name": "Sjabloonnaam", "support_title_live_chat": "Live ondersteuning", "support_description_live_chat": "Gratis en snel! Getrainde ondersteuningsvertegenwoordigers zijn beschikbaar om te helpen", @@ -687,4 +694,7 @@ "support_description_other_links": "Word lid van onze gemeenschappen of bereik ons onze partners via andere methoden", "select_destination": "Selecteer de bestemming voor het back-upbestand.", "save_to_downloads": "Opslaan in downloads" +======= + "template_name": "Sjabloonnaam" +>>>>>>> origin/linux/password-direct-input } diff --git a/res/values/strings_pl.arb b/res/values/strings_pl.arb index e458339ba..c98a4bf98 100644 --- a/res/values/strings_pl.arb +++ b/res/values/strings_pl.arb @@ -322,6 +322,12 @@ "trade_state_timeout": "Koniec czasu", "trade_state_created": "Stworzona", "trade_state_finished": "Zakończona", + "invalid_password" : "Nieprawidłowe hasło", + "unlock" : "Odblokować", + "enter_wallet_password" : "Wprowadź hasło do portfela", + "repeate_wallet_password" : "Powtórz hasło do portfela", + "wallet_password_is_empty" : "Hasło portfela jest puste. Hasło portfela nie powinno być puste", + "repeated_password_is_incorrect" : "Powtórzone hasło jest nieprawidłowe. Powtórz hasło do portfela jeszcze raz.", "change_language": "Zmień język", "change_language_to": "Zmień język na ${language}?", "paste": "Wklej", @@ -678,6 +684,7 @@ "monero_light_theme": "Lekki motyw Monero", "manage_nodes": "Zarządzaj węzłami", "etherscan_history": "Historia Etherscanu", +<<<<<<< HEAD "template_name": "Nazwa szablonu", "support_title_live_chat": "Wsparcie na żywo", "support_description_live_chat": "Darmowe i szybkie! Do pomocy są dostępni przeszkoleni przedstawiciele wsparcia", @@ -687,4 +694,7 @@ "support_description_other_links": "Dołącz do naszych społeczności lub skontaktuj się z nami naszymi partnerami za pomocą innych metod", "select_destination": "Wybierz miejsce docelowe dla pliku kopii zapasowej.", "save_to_downloads": "Zapisz w Pobranych" +======= + "template_name": "Nazwa szablonu" +>>>>>>> origin/linux/password-direct-input } diff --git a/res/values/strings_pt.arb b/res/values/strings_pt.arb index aa06cd095..223d96be8 100644 --- a/res/values/strings_pt.arb +++ b/res/values/strings_pt.arb @@ -322,6 +322,12 @@ "trade_state_timeout": "Tempo esgotado", "trade_state_created": "Criada", "trade_state_finished": "Finalizada", + "invalid_password" : "Senha inválida", + "unlock" : "desbloquear", + "enter_wallet_password" : "Digite a senha da carteira", + "repeate_wallet_password" : "Repita a senha da carteira", + "wallet_password_is_empty" : "A senha da carteira está vazia. A senha da carteira não deve estar vazia", + "repeated_password_is_incorrect" : "A senha repetida está incorreta. Por favor, repita a senha da carteira novamente.", "change_language": "Mudar idioma", "change_language_to": "Alterar idioma para ${language}?", "paste": "Colar", @@ -677,6 +683,7 @@ "monero_light_theme": "Monero Light Theme", "manage_nodes": "Gerenciar nós", "etherscan_history": "história Etherscan", +<<<<<<< HEAD "template_name": "Nome do modelo", "support_title_live_chat": "Apoio ao vivo", "support_description_live_chat": "Livre e rápido! Representantes de suporte treinado estão disponíveis para ajudar", @@ -686,4 +693,7 @@ "support_description_other_links": "Junte -se às nossas comunidades ou chegue a nós nossos parceiros por meio de outros métodos", "select_destination": "Selecione o destino para o arquivo de backup.", "save_to_downloads": "Salvar em Downloads" +======= + "template_name": "Nome do modelo" +>>>>>>> origin/linux/password-direct-input } diff --git a/res/values/strings_ru.arb b/res/values/strings_ru.arb index 533ca431d..cca458579 100644 --- a/res/values/strings_ru.arb +++ b/res/values/strings_ru.arb @@ -270,6 +270,7 @@ "error_text_node_address": "Пожалуйста, введите iPv4 адрес", "error_text_node_port": "Порт ноды может содержать только цифры от 0 до 65535", "error_text_node_proxy_address": "Введите <IPv4-адрес>:<порт>, например 127.0.0.1:9050.", + "error_text_payment_id": "Идентификатор платежа может содержать от 16 до 64 символов в hex", "error_text_xmr": "Значение XMR не может превышать доступный баланс.\nКоличество цифр после запятой должно быть меньше или равно 12", "error_text_fiat": "Значение суммы не может превышать доступный баланс.\nКоличество цифр после запятой должно быть меньше или равно 2", @@ -322,6 +323,12 @@ "trade_state_timeout": "Таймаут", "trade_state_created": "Созданная", "trade_state_finished": "Завершена", + "invalid_password" : "Invalid password", + "unlock" : "Unlock", + "enter_wallet_password" : "Enter the wallet password", + "repeate_wallet_password" : "Repeat the wallet password", + "wallet_password_is_empty" : "Wallet password is empty. Wallet password should not be empty", + "repeated_password_is_incorrect" : "Repeated password is incorrect. Please repeat the wallet password again.", "change_language": "Изменить язык", "change_language_to": "Изменить язык на ${language}?", "paste": "Вставить", @@ -634,18 +641,18 @@ "setup_totp_recommended": "Настроить TOTP (рекомендуется)", "disable_buy": "Отключить действие покупки", "disable_sell": "Отключить действие продажи", - "cake_2fa_preset": "Торт 2FA Preset", + "cake_2fa_preset" : "Торт 2FA Preset", "narrow": "Узкий", "normal": "Нормальный", "aggressive": "чрезмерно усердный", "require_for_assessing_wallet": "Требовать для доступа к кошельку", - "require_for_sends_to_non_contacts": "Требовать для отправки не контактам", - "require_for_sends_to_contacts": "Требовать для отправки контактам", - "require_for_sends_to_internal_wallets": "Требовать отправки на внутренние кошельки", - "require_for_exchanges_to_internal_wallets": "Требовать для обмена на внутренние кошельки", - "require_for_adding_contacts": "Требовать добавления контактов", - "require_for_creating_new_wallets": "Требовать для создания новых кошельков", - "require_for_all_security_and_backup_settings": "Требовать все настройки безопасности и резервного копирования", + "require_for_sends_to_non_contacts" : "Требовать для отправки не контактам", + "require_for_sends_to_contacts" : "Требовать для отправки контактам", + "require_for_sends_to_internal_wallets" : "Требовать отправки на внутренние кошельки", + "require_for_exchanges_to_internal_wallets" : "Требовать для обмена на внутренние кошельки", + "require_for_adding_contacts" : "Требовать добавления контактов", + "require_for_creating_new_wallets" : "Требовать для создания новых кошельков", + "require_for_all_security_and_backup_settings" : "Требовать все настройки безопасности и резервного копирования", "available_balance_description": "Доступный баланс - это средства, которые вы можете использовать для покупки или продажи криптовалюты.", "syncing_wallet_alert_title": "Ваш кошелек синхронизируется", "syncing_wallet_alert_content": "Ваш баланс и список транзакций могут быть неполными, пока вверху не будет написано «СИНХРОНИЗИРОВАНО». Щелкните/коснитесь, чтобы узнать больше.", @@ -678,6 +685,7 @@ "monero_light_theme": "Светлая тема Monero", "manage_nodes": "Управление узлами", "etherscan_history": "История Эфириума", +<<<<<<< HEAD "template_name": "Имя Шаблона", "support_title_live_chat": "Живая поддержка", "support_description_live_chat": "Бесплатно и быстро! Обученные представители поддержки доступны для оказания помощи", @@ -687,4 +695,7 @@ "support_description_other_links": "Присоединяйтесь к нашим сообществам или охватите нас наших партнеров с помощью других методов", "select_destination": "Пожалуйста, выберите место для файла резервной копии.", "save_to_downloads": "Сохранить в загрузках" +======= + "template_name": "Имя Шаблона" +>>>>>>> origin/linux/password-direct-input } diff --git a/res/values/strings_th.arb b/res/values/strings_th.arb index f54194617..9f49ce63f 100644 --- a/res/values/strings_th.arb +++ b/res/values/strings_th.arb @@ -322,6 +322,12 @@ "trade_state_timeout": "เวลาหมด", "trade_state_created": "ถูกสร้าง", "trade_state_finished": "เสร็จสิ้น", + "invalid_password" : "รหัสผ่านไม่ถูกต้อง", + "unlock" : "ปลดล็อค", + "enter_wallet_password" : "ป้อนรหัสผ่านกระเป๋าเงิน", + "repeate_wallet_password" : "ทำซ้ำรหัสผ่านกระเป๋าเงิน", + "wallet_password_is_empty" : "รหัสผ่าน Wallet ว่างเปล่า รหัสผ่าน Wallet ไม่ควรว่างเปล่า", + "repeated_password_is_incorrect" : "รหัสผ่านซ้ำไม่ถูกต้อง กรุณากรอกรหัสผ่านกระเป๋าเงินซ้ำอีกครั้ง", "change_language": "เปลี่ยนภาษา", "change_language_to": "เปลี่ยนภาษาเป็น ${language}?", "paste": "วาง", @@ -632,18 +638,18 @@ "setup_totp_recommended": "ตั้งค่า TOTP (แนะนำ)", "disable_buy": "ปิดการใช้งานการซื้อ", "disable_sell": "ปิดการใช้งานการขาย", - "cake_2fa_preset": "เค้ก 2FA ที่ตั้งไว้ล่วงหน้า", + "cake_2fa_preset" : "เค้ก 2FA ที่ตั้งไว้ล่วงหน้า", "narrow": "แคบ", "normal": "ปกติ", "aggressive": "กระตือรือร้นมากเกินไป", "require_for_assessing_wallet": "จำเป็นสำหรับการเข้าถึงกระเป๋าเงิน", - "require_for_sends_to_non_contacts": "จำเป็นต้องส่งไปยังผู้ที่ไม่ได้ติดต่อ", - "require_for_sends_to_contacts": "จำเป็นต้องส่งไปยังผู้ติดต่อ", - "require_for_sends_to_internal_wallets": "จำเป็นต้องส่งไปยังกระเป๋าเงินภายใน", - "require_for_exchanges_to_internal_wallets": "ต้องการการแลกเปลี่ยนไปยังกระเป๋าเงินภายใน", - "require_for_adding_contacts": "ต้องการสำหรับการเพิ่มผู้ติดต่อ", - "require_for_creating_new_wallets": "จำเป็นสำหรับการสร้างกระเป๋าเงินใหม่", - "require_for_all_security_and_backup_settings": "จำเป็นสำหรับการตั้งค่าความปลอดภัยและการสำรองข้อมูลทั้งหมด", + "require_for_sends_to_non_contacts" : "จำเป็นต้องส่งไปยังผู้ที่ไม่ได้ติดต่อ", + "require_for_sends_to_contacts" : "จำเป็นต้องส่งไปยังผู้ติดต่อ", + "require_for_sends_to_internal_wallets" : "จำเป็นต้องส่งไปยังกระเป๋าเงินภายใน", + "require_for_exchanges_to_internal_wallets" : "ต้องการการแลกเปลี่ยนไปยังกระเป๋าเงินภายใน", + "require_for_adding_contacts" : "ต้องการสำหรับการเพิ่มผู้ติดต่อ", + "require_for_creating_new_wallets" : "จำเป็นสำหรับการสร้างกระเป๋าเงินใหม่", + "require_for_all_security_and_backup_settings" : "จำเป็นสำหรับการตั้งค่าความปลอดภัยและการสำรองข้อมูลทั้งหมด", "available_balance_description": "จำนวนเงินที่คุณสามารถใช้ได้ในการซื้อหรือขาย", "syncing_wallet_alert_title": "กระเป๋าสตางค์ของคุณกำลังซิงค์", "syncing_wallet_alert_content": "รายการยอดเงินและธุรกรรมของคุณอาจไม่สมบูรณ์จนกว่าจะมีข้อความว่า “ซิงโครไนซ์” ที่ด้านบน คลิก/แตะเพื่อเรียนรู้เพิ่มเติม่", @@ -676,6 +682,7 @@ "monero_light_theme": "ธีมแสง Monero", "manage_nodes": "จัดการโหนด", "etherscan_history": "ประวัติอีเธอร์สแกน", +<<<<<<< HEAD "template_name": "ชื่อแม่แบบ", "support_title_live_chat": "การสนับสนุนสด", "support_description_live_chat": "ฟรีและรวดเร็ว! ตัวแทนฝ่ายสนับสนุนที่ผ่านการฝึกอบรมพร้อมให้ความช่วยเหลือ", @@ -685,4 +692,7 @@ "support_description_other_links": "เข้าร่วมชุมชนของเราหรือเข้าถึงเราพันธมิตรของเราผ่านวิธีการอื่น ๆ", "select_destination": "โปรดเลือกปลายทางสำหรับไฟล์สำรอง", "save_to_downloads": "บันทึกลงดาวน์โหลด" +======= + "template_name": "ชื่อแม่แบบ" +>>>>>>> origin/linux/password-direct-input } diff --git a/res/values/strings_tr.arb b/res/values/strings_tr.arb index fa835912a..8dc828075 100644 --- a/res/values/strings_tr.arb +++ b/res/values/strings_tr.arb @@ -322,6 +322,12 @@ "trade_state_timeout": "Zaman aşımı", "trade_state_created": "Oluşturuldu", "trade_state_finished": "Tamamlandı", + "invalid_password" : "Geçersiz şifre", + "unlock" : "Kilidini aç", + "enter_wallet_password" : "cüzdan şifresini girin", + "repeate_wallet_password" : "M-cüzdan şifresini tekrarla", + "wallet_password_is_empty" : "Cüzdan şifresi boş. Cüzdan şifresi boş olmamalıdır", + "repeated_password_is_incorrect" : "Tekrarlanan şifre yanlış. Lütfen cüzdan şifresini tekrar tekrarlayın.", "change_language": "Dili değiştir", "change_language_to": "Dili şuna değiştir: ${language}?", "paste": "Yapıştır", @@ -676,6 +682,7 @@ "monero_light_theme": "Monero Hafif Tema", "manage_nodes": "Düğümleri yönet", "etherscan_history": "Etherscan geçmişi", +<<<<<<< HEAD "template_name": "şablon adı", "support_title_live_chat": "Canlı destek", "support_description_live_chat": "Ücretsiz ve hızlı! Eğitimli destek temsilcileri yardımcı olmak için mevcuttur", @@ -685,4 +692,7 @@ "support_description_other_links": "Topluluklarımıza katılın veya ortaklarımıza diğer yöntemlerle bize ulaşın", "select_destination": "Lütfen yedekleme dosyası için hedef seçin.", "save_to_downloads": "İndirilenlere Kaydet" +======= + "template_name": "şablon adı" +>>>>>>> origin/linux/password-direct-input } diff --git a/res/values/strings_uk.arb b/res/values/strings_uk.arb index 6d9a648fa..0f05714cc 100644 --- a/res/values/strings_uk.arb +++ b/res/values/strings_uk.arb @@ -322,6 +322,12 @@ "trade_state_timeout": "Таймаут", "trade_state_created": "Створена", "trade_state_finished": "Завершена", + "invalid_password" : "Невірний пароль", + "unlock" : "Розблокувати", + "enter_wallet_password" : "Введіть пароль гаманця", + "repeate_wallet_password" : "Повторіть пароль гаманця", + "wallet_password_is_empty" : "Пароль гаманця порожній. Пароль гаманця не повинен бути порожнім", + "repeated_password_is_incorrect" : "Повторний пароль неправильний. Будь ласка, повторіть пароль гаманця ще раз.", "change_language": "Змінити мову", "change_language_to": "Змінити мову на ${language}?", "paste": "Вставити", @@ -678,6 +684,7 @@ "monero_light_theme": "Легка тема Monero", "manage_nodes": "Керуйте вузлами", "etherscan_history": "Історія Etherscan", +<<<<<<< HEAD "template_name": "Назва шаблону", "support_title_live_chat": "Жива підтримка", "support_description_live_chat": "Безкоштовно і швидко! Навчені представники підтримки доступні для надання допомоги", @@ -687,4 +694,7 @@ "support_description_other_links": "Приєднуйтесь до наших спільнот або досягайте нас нашими партнерами іншими методами", "select_destination": "Виберіть місце призначення для файлу резервної копії.", "save_to_downloads": "Зберегти до завантажень" +======= + "template_name": "Назва шаблону" +>>>>>>> origin/linux/password-direct-input } diff --git a/res/values/strings_ur.arb b/res/values/strings_ur.arb index 39cae33d0..10f050b14 100644 --- a/res/values/strings_ur.arb +++ b/res/values/strings_ur.arb @@ -322,6 +322,12 @@ "trade_state_timeout": "وقت ختم", "trade_state_created": "بنایا", "trade_state_finished": "ختم", + "invalid_password" : "غلط پاسورڈ", + "unlock" : "غیر مقفل کریں۔", + "enter_wallet_password" : "بٹوے کا پاس ورڈ درج کریں۔", + "repeate_wallet_password" : "والیٹ پاس ورڈ کو دہرائیں۔", + "wallet_password_is_empty" : "والیٹ کا پاس ورڈ خالی ہے۔ والیٹ کا پاس ورڈ خالی نہیں ہونا چاہیے۔", + "repeated_password_is_incorrect" : "بار بار پاس ورڈ غلط ہے۔ براہ کرم بٹوے کا پاس ورڈ دوبارہ دہرائیں۔", "change_language": "زبان تبدیل کریں", "change_language_to": "زبان کو ${language} میں تبدیل کریں؟", "paste": "چسپاں کریں۔", @@ -603,6 +609,7 @@ "sell_monero_com_alert_content": "Monero فروخت کرنا ابھی تک تعاون یافتہ نہیں ہے۔", "error_text_input_below_minimum_limit": "رقم کم از کم سے کم ہے۔", "error_text_input_above_maximum_limit": "رقم زیادہ سے زیادہ سے زیادہ ہے۔", + "settings": "ترتیبات", "show_market_place": "بازار دکھائیں۔", "prevent_screenshots": "اسکرین شاٹس اور اسکرین ریکارڈنگ کو روکیں۔", "profile": "پروفائل", @@ -626,18 +633,18 @@ "setup_totp_recommended": "TOTP ترتیب دیں (تجویز کردہ)", "disable_buy": "خرید ایکشن کو غیر فعال کریں۔", "disable_sell": "فروخت کی کارروائی کو غیر فعال کریں۔", - "cake_2fa_preset": "کیک 2FA پیش سیٹ", + "cake_2fa_preset" : "کیک 2FA پیش سیٹ", "narrow": "تنگ", "normal": "نارمل", "aggressive": "حد سے زیادہ پرجوش", "require_for_assessing_wallet": "بٹوے تک رسائی کے لیے درکار ہے۔", - "require_for_sends_to_non_contacts": "غیر رابطوں کو بھیجنے کی ضرورت ہے۔", - "require_for_sends_to_contacts": "رابطوں کو بھیجنے کی ضرورت ہے۔", - "require_for_sends_to_internal_wallets": "اندرونی بٹوے پر بھیجنے کے لیے درکار ہے۔", - "require_for_exchanges_to_internal_wallets": "اندرونی بٹوے میں تبادلے کی ضرورت ہے۔", - "require_for_adding_contacts": "رابطوں کو شامل کرنے کی ضرورت ہے۔", - "require_for_creating_new_wallets": "نئے بٹوے بنانے کی ضرورت ہے۔", - "require_for_all_security_and_backup_settings": "تمام سیکورٹی اور بیک اپ کی ترتیبات کے لیے درکار ہے۔", + "require_for_sends_to_non_contacts" : "غیر رابطوں کو بھیجنے کی ضرورت ہے۔", + "require_for_sends_to_contacts" : "رابطوں کو بھیجنے کی ضرورت ہے۔", + "require_for_sends_to_internal_wallets" : "اندرونی بٹوے پر بھیجنے کے لیے درکار ہے۔", + "require_for_exchanges_to_internal_wallets" : "اندرونی بٹوے میں تبادلے کی ضرورت ہے۔", + "require_for_adding_contacts" : "رابطوں کو شامل کرنے کی ضرورت ہے۔", + "require_for_creating_new_wallets" : "نئے بٹوے بنانے کی ضرورت ہے۔", + "require_for_all_security_and_backup_settings" : "تمام سیکورٹی اور بیک اپ کی ترتیبات کے لیے درکار ہے۔", "available_balance_description": "”دستیاب بیلنس” یا ”تصدیق شدہ بیلنس” وہ فنڈز ہیں جو فوری طور پر خرچ کیے جا سکتے ہیں۔ اگر فنڈز کم بیلنس میں ظاہر ہوتے ہیں لیکن اوپر کے بیلنس میں نہیں، تو آپ کو مزید نیٹ ورک کی تصدیقات حاصل کرنے کے لیے آنے والے فنڈز کے لیے چند منٹ انتظار کرنا چاہیے۔ مزید تصدیق حاصل کرنے کے بعد، وہ قابل خرچ ہوں گے۔", "syncing_wallet_alert_title": "آپ کا بٹوہ مطابقت پذیر ہو رہا ہے۔", "syncing_wallet_alert_content": "آپ کے بیلنس اور لین دین کی فہرست اس وقت تک مکمل نہیں ہو سکتی جب تک کہ یہ سب سے اوپر \"SYNCRONIZED\" نہ کہے۔ مزید جاننے کے لیے کلک/تھپتھپائیں۔", @@ -670,6 +677,7 @@ "monero_light_theme": "مونیرو لائٹ تھیم", "manage_nodes": "۔ﮟﯾﺮﮐ ﻢﻈﻧ ﺎﮐ ﺱﮈﻮﻧ", "etherscan_history": "ﺦﯾﺭﺎﺗ ﯽﮐ ﻦﯿﮑﺳﺍ ﺮﮭﺘﯾﺍ", +<<<<<<< HEAD "template_name": "ٹیمپلیٹ کا نام", "support_title_live_chat": "براہ راست مدد", "support_description_live_chat": "مفت اور تیز! تربیت یافتہ معاون نمائندے مدد کے لئے دستیاب ہیں", @@ -679,4 +687,7 @@ "support_description_other_links": "ہماری برادریوں میں شامل ہوں یا دوسرے طریقوں سے ہمارے شراکت داروں تک پہنچیں", "select_destination": "۔ﮟﯾﺮﮐ ﺏﺎﺨﺘﻧﺍ ﺎﮐ ﻝﺰﻨﻣ ﮯﯿﻟ ﮯﮐ ﻞﺋﺎﻓ ﭖﺍ ﮏﯿﺑ ﻡﺮﮐ ﮦﺍﺮﺑ", "save_to_downloads": "۔ﮟﯾﺮﮐ ﻅﻮﻔﺤﻣ ﮟﯿﻣ ﺯﮈﻮﻟ ﻥﺅﺍﮈ" +======= + "template_name": "ٹیمپلیٹ کا نام" +>>>>>>> origin/linux/password-direct-input } diff --git a/res/values/strings_yo.arb b/res/values/strings_yo.arb index 75df8de6b..9360e7f1b 100644 --- a/res/values/strings_yo.arb +++ b/res/values/strings_yo.arb @@ -628,18 +628,18 @@ "setup_totp_recommended": "Sọ TOTP (Kẹṣọdọ)", "disable_buy": "Ko iṣọrọ ọja", "disable_sell": "Ko iṣọrọ iṣọrọ", - "cake_2fa_preset": "Cake 2FA Tito", + "cake_2fa_preset" : "Cake 2FA Tito", "narrow": "Taara", "normal": "Deede", "aggressive": "Onítara", "require_for_assessing_wallet": "Beere fun wiwọle si apamọwọ", - "require_for_sends_to_non_contacts": "Beere fun fifiranṣẹ si awọn ti kii ṣe awọn olubasọrọ", - "require_for_sends_to_contacts": "Beere fun fifiranṣẹ si awọn olubasọrọ", - "require_for_sends_to_internal_wallets": "Beere fun fifiranṣẹ si awọn apamọwọ inu", - "require_for_exchanges_to_internal_wallets": "Beere fun awọn paṣipaarọ si awọn apamọwọ inu", - "require_for_adding_contacts": "Beere fun fifi awọn olubasọrọ kun", - "require_for_creating_new_wallets": "Beere fun ṣiṣẹda titun Woleti", - "require_for_all_security_and_backup_settings": "Beere fun gbogbo aabo ati awọn eto afẹyinti", + "require_for_sends_to_non_contacts" : "Beere fun fifiranṣẹ si awọn ti kii ṣe awọn olubasọrọ", + "require_for_sends_to_contacts" : "Beere fun fifiranṣẹ si awọn olubasọrọ", + "require_for_sends_to_internal_wallets" : "Beere fun fifiranṣẹ si awọn apamọwọ inu", + "require_for_exchanges_to_internal_wallets" : "Beere fun awọn paṣipaarọ si awọn apamọwọ inu", + "require_for_adding_contacts" : "Beere fun fifi awọn olubasọrọ kun", + "require_for_creating_new_wallets" : "Beere fun ṣiṣẹda titun Woleti", + "require_for_all_security_and_backup_settings" : "Beere fun gbogbo aabo ati awọn eto afẹyinti", "available_balance_description": "“Iwọntunwọnsi Wa” tabi “Iwọntunwọnsi Ijẹrisi” jẹ awọn owo ti o le ṣee lo lẹsẹkẹsẹ. Ti awọn owo ba han ni iwọntunwọnsi kekere ṣugbọn kii ṣe iwọntunwọnsi oke, lẹhinna o gbọdọ duro iṣẹju diẹ fun awọn owo ti nwọle lati gba awọn ijẹrisi nẹtiwọọki diẹ sii. Lẹhin ti wọn gba awọn ijẹrisi diẹ sii, wọn yoo jẹ inawo.", "syncing_wallet_alert_title": "Apamọwọ rẹ n muṣiṣẹpọ", "syncing_wallet_alert_content": "Iwontunws.funfun rẹ ati atokọ idunadura le ma pari titi ti yoo fi sọ “SYNCHRONIZED” ni oke. Tẹ/tẹ ni kia kia lati ni imọ siwaju sii.", @@ -667,18 +667,25 @@ "manage_nodes": "Ṣakoso awọn apa", "etherscan_history": "Etherscan itan", "template_name": "Orukọ Awoṣe", +<<<<<<< HEAD "support_title_live_chat": "Atilẹyin ifiwe", "support_description_live_chat": "Free ati sare! Ti oṣiṣẹ awọn aṣoju wa lati ṣe iranlọwọ", "support_title_guides": "Akara oyinbo Awọn Itọsọna Awọki oyinbo", "support_description_guides": "Iwe ati atilẹyin fun awọn ọran ti o wọpọ", "support_title_other_links": "Awọn ọna asopọ atilẹyin miiran", "support_description_other_links": "Darapọ mọ awọn agbegbe wa tabi de wa awọn alabaṣepọ wa nipasẹ awọn ọna miiran", +======= +>>>>>>> origin/linux/password-direct-input "monero_dark_theme": "Monero Dudu Akori", "bitcoin_dark_theme": "Bitcoin Dark Akori", "bitcoin_light_theme": "Bitcoin Light Akori", "high_contrast_theme": "Akori Iyatọ giga", "matrix_green_dark_theme": "Matrix Green Dark Akori", +<<<<<<< HEAD "monero_light_theme": "Monero Light Akori", "select_destination": "Jọwọ yan ibi ti o nlo fun faili afẹyinti.", "save_to_downloads": "Fipamọ si Awọn igbasilẹ" +======= + "monero_light_theme": "Monero Light Akori" +>>>>>>> origin/linux/password-direct-input } diff --git a/res/values/strings_zh.arb b/res/values/strings_zh.arb index 3fa31bb10..6b6cbb31f 100644 --- a/res/values/strings_zh.arb +++ b/res/values/strings_zh.arb @@ -322,6 +322,12 @@ "trade_state_timeout": "超时", "trade_state_created": "已建立", "trade_state_finished": "已完成", + "invalid_password" : "无效的密码", + "unlock" : "开锁", + "enter_wallet_password" : "输入钱包密码", + "repeate_wallet_password" : "重复钱包密码", + "wallet_password_is_empty" : "钱包密码为空。 钱包密码不能为空", + "repeated_password_is_incorrect" : "重复的密码不正确。 请再次输入钱包密码。", "change_language": "修改语言", "change_language_to": "修改语言为 ${language}?", "paste": "粘贴", @@ -633,18 +639,18 @@ "setup_totp_recommended": "设置 TOTP(推荐)", "disable_buy": "禁用购买操作", "disable_sell": "禁用卖出操作", - "cake_2fa_preset": "蛋糕 2FA 预设", + "cake_2fa_preset" : "蛋糕 2FA 预设", "narrow": "狭窄的", "normal": "普通的", "aggressive": "过分热心", "require_for_assessing_wallet": "需要访问钱包", - "require_for_sends_to_non_contacts": "需要发送给非联系人", - "require_for_sends_to_contacts": "需要发送给联系人", - "require_for_sends_to_internal_wallets": "需要发送到内部钱包", - "require_for_exchanges_to_internal_wallets": "需要兑换到内部钱包", - "require_for_adding_contacts": "需要添加联系人", - "require_for_creating_new_wallets": "创建新钱包的要求", - "require_for_all_security_and_backup_settings": "需要所有安全和备份设置", + "require_for_sends_to_non_contacts" : "需要发送给非联系人", + "require_for_sends_to_contacts" : "需要发送给联系人", + "require_for_sends_to_internal_wallets" : "需要发送到内部钱包", + "require_for_exchanges_to_internal_wallets" : "需要兑换到内部钱包", + "require_for_adding_contacts" : "需要添加联系人", + "require_for_creating_new_wallets" : "创建新钱包的要求", + "require_for_all_security_and_backup_settings" : "需要所有安全和备份设置", "available_balance_description": "可用余额是您可以使用的金额。冻结余额是您当前正在等待确认的金额。", "syncing_wallet_alert_title": "您的钱包正在同步", "syncing_wallet_alert_content": "您的余额和交易列表可能不完整,直到顶部显示“已同步”。单击/点击以了解更多信息。", @@ -672,18 +678,25 @@ "manage_nodes": "管理节点", "etherscan_history": "以太扫描历史", "template_name": "模板名称", +<<<<<<< HEAD "support_title_live_chat": "实时支持", "support_description_live_chat": "免费快速!训练有素的支持代表可以协助", "support_title_guides": "蛋糕钱包指南", "support_description_guides": "对常见问题的文档和支持", "support_title_other_links": "其他支持链接", "support_description_other_links": "加入我们的社区或通过其他方法与我们联系我们的合作伙伴", +======= +>>>>>>> origin/linux/password-direct-input "monero_dark_theme": "门罗币深色主题", "bitcoin_dark_theme": "比特币黑暗主题", "bitcoin_light_theme": "比特币浅色主题", "high_contrast_theme": "高对比度主题", "matrix_green_dark_theme": "矩阵绿暗主题", +<<<<<<< HEAD "monero_light_theme": "门罗币浅色主题", "select_destination": "请选择备份文件的目的地。", "save_to_downloads": "保存到下载" +======= + "monero_light_theme": "门罗币浅色主题" +>>>>>>> origin/linux/password-direct-input } diff --git a/scripts/linux/app_config.sh b/scripts/linux/app_config.sh new file mode 100755 index 000000000..f130887fe --- /dev/null +++ b/scripts/linux/app_config.sh @@ -0,0 +1,25 @@ +#!/bin/bash + +CAKEWALLET="cakewallet" +DIR=`pwd` + +if [ -z "$APP_LINUX_TYPE" ]; then + echo "Please set APP_LINUX_TYPE" + exit 1 +fi + +cd ../.. # go to root +CONFIG_ARGS="" + +case $APP_LINUX_TYPE in + $CAKEWALLET) + CONFIG_ARGS="--monero --bitcoin --ethereum --excludeFlutterSecureStorage";; #--haven +esac + +cp -rf pubspec_description.yaml pubspec.yaml +flutter pub get +flutter pub run tool/generate_pubspec.dart +flutter pub get +flutter packages pub run tool/configure.dart $CONFIG_ARGS +sed -i '0,/version: 0.0.0/s//version: '"${APP_LINUX_VERSION}"'+'"${APP_LINUX_BUILD_NUMBER}"'/' pubspec.yaml +cd $DIR diff --git a/scripts/linux/app_env.sh b/scripts/linux/app_env.sh new file mode 100755 index 000000000..ac8c7c9df --- /dev/null +++ b/scripts/linux/app_env.sh @@ -0,0 +1,35 @@ +#!/bin/sh + +APP_LINUX_NAME="" +APP_LINUX_VERSION="" +APP_LINUX_BUILD_VERSION="" + +CAKEWALLET="cakewallet" + +TYPES=($CAKEWALLET) +APP_LINUX_TYPE=$CAKEWALLET + +if [ -n "$1" ]; then + APP_LINUX_TYPE=$1 +fi + +CAKEWALLET_NAME="Cake Wallet" +CAKEWALLET_VERSION="1.0.2" +CAKEWALLET_BUILD_NUMBER=3 + +if ! [[ " ${TYPES[*]} " =~ " ${APP_LINUX_TYPE} " ]]; then + echo "Wrong app type." + exit 1 +fi + +case $APP_LINUX_TYPE in + $CAKEWALLET) + APP_LINUX_NAME=$CAKEWALLET_NAME + APP_LINUX_VERSION=$CAKEWALLET_VERSION + APP_LINUX_BUILD_NUMBER=$CAKEWALLET_BUILD_NUMBER;; +esac + +export APP_LINUX_TYPE +export APP_LINUX_NAME +export APP_LINUX_VERSION +export APP_LINUX_BUILD_NUMBER diff --git a/scripts/linux/build_all.sh b/scripts/linux/build_all.sh new file mode 100755 index 000000000..e2bdb081c --- /dev/null +++ b/scripts/linux/build_all.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +./build_monero_all.sh diff --git a/scripts/linux/build_boost.sh b/scripts/linux/build_boost.sh new file mode 100755 index 000000000..3ac613e7c --- /dev/null +++ b/scripts/linux/build_boost.sh @@ -0,0 +1,36 @@ +#!/bin/sh + +set -e + +. ./config.sh + +BOOST_SRC_DIR=${EXTERNAL_LINUX_SOURCE_DIR}/boost_1_82_0 +BOOST_FILENAME=boost_1_82_0.tar.bz2 +BOOST_VERSION=1.82.0 +BOOST_FILE_PATH=${EXTERNAL_LINUX_SOURCE_DIR}/$BOOST_FILENAME +BOOST_SHA256="a6e1ab9b0860e6a2881dd7b21fe9f737a095e5f33a3a874afc6a345228597ee6" + +if [ ! -e "$BOOST_FILE_PATH" ]; then + curl -L http://downloads.sourceforge.net/project/boost/boost/${BOOST_VERSION}/${BOOST_FILENAME} > $BOOST_FILE_PATH +fi + +echo $BOOST_SHA256 $BOOST_FILE_PATH | sha256sum -c - || exit 1 + +cd $EXTERNAL_LINUX_SOURCE_DIR +rm -rf $BOOST_SRC_DIR +tar -xvf $BOOST_FILE_PATH -C $EXTERNAL_LINUX_SOURCE_DIR +cd $BOOST_SRC_DIR +./bootstrap.sh --prefix=${EXTERNAL_LINUX_DIR} +./b2 cxxflags=-fPIC cflags=-fPIC \ + --with-chrono \ + --with-date_time \ + --with-filesystem \ + --with-program_options \ + --with-regex \ + --with-serialization \ + --with-system \ + --with-thread \ + --with-locale \ + link=static \ + install + diff --git a/scripts/linux/build_expat.sh b/scripts/linux/build_expat.sh new file mode 100755 index 000000000..a45852d1d --- /dev/null +++ b/scripts/linux/build_expat.sh @@ -0,0 +1,20 @@ +#!/bin/sh + +set -e + +. ./config.sh + + +EXPAT_VERSION=R_2_4_8 +EXPAT_HASH="3bab6c09bbe8bf42d84b81563ddbcf4cca4be838" +EXPAT_SRC_DIR=${EXTERNAL_LINUX_SOURCE_DIR}/libexpat + +git clone https://github.com/libexpat/libexpat.git -b ${EXPAT_VERSION} ${EXPAT_SRC_DIR} +cd $EXPAT_SRC_DIR +test `git rev-parse HEAD` = ${EXPAT_HASH} || exit 1 +cd $EXPAT_SRC_DIR/expat + +./buildconf.sh +./configure --enable-static --disable-shared --prefix=${EXTERNAL_LINUX_DIR} +make +make install diff --git a/scripts/linux/build_iconv.sh b/scripts/linux/build_iconv.sh new file mode 100755 index 000000000..29812cdb3 --- /dev/null +++ b/scripts/linux/build_iconv.sh @@ -0,0 +1,22 @@ +#!/bin/sh + +set -e + +. ./config.sh + +export ICONV_FILENAME=libiconv-1.16.tar.gz +export ICONV_FILE_PATH=${EXTERNAL_LINUX_SOURCE_DIR}/${ICONV_FILENAME} +export ICONV_SRC_DIR=${EXTERNAL_LINUX_SOURCE_DIR}/libiconv-1.16 +ICONV_SHA256="e6a1b1b589654277ee790cce3734f07876ac4ccfaecbee8afa0b649cf529cc04" + +curl http://ftp.gnu.org/pub/gnu/libiconv/${ICONV_FILENAME} -o $ICONV_FILE_PATH +echo $ICONV_SHA256 $ICONV_FILE_PATH | sha256sum -c - || exit 1 + +cd $EXTERNAL_LINUX_SOURCE_DIR +rm -rf $ICONV_SRC_DIR +tar -xzf $ICONV_FILE_PATH -C $EXTERNAL_LINUX_SOURCE_DIR +cd $ICONV_SRC_DIR + +./configure --prefix=${EXTERNAL_LINUX_DIR} +make +make install diff --git a/scripts/linux/build_monero.sh b/scripts/linux/build_monero.sh new file mode 100755 index 000000000..559b424fb --- /dev/null +++ b/scripts/linux/build_monero.sh @@ -0,0 +1,43 @@ +#!/bin/sh + +. ./config.sh + + +MONERO_URL="https://github.com/cake-tech/monero.git" +MONERO_DIR_PATH="${EXTERNAL_LINUX_SOURCE_DIR}/monero" +MONERO_VERSION=release-v0.18.2.2 +BUILD_TYPE=release +PREFIX=${EXTERNAL_LINUX_DIR} +DEST_LIB_DIR=${EXTERNAL_LINUX_LIB_DIR}/monero +DEST_INCLUDE_DIR=${EXTERNAL_LINUX_INCLUDE_DIR}/monero + +echo "Cloning monero from - $MONERO_URL to - $MONERO_DIR_PATH" +git clone $MONERO_URL $MONERO_DIR_PATH +cd $MONERO_DIR_PATH +git checkout $MONERO_VERSION +git submodule update --init --force +rm -rf ./build/release +mkdir -p ./build/release +cd ./build/release + +mkdir -p $DEST_LIB_DIR +mkdir -p $DEST_INCLUDE_DIR + +echo "Building LINUX" +export CMAKE_INCLUDE_PATH="${PREFIX}/include" +export CMAKE_LIBRARY_PATH="${PREFIX}/lib" + +cmake -DSTATIC=ON \ + -DBUILD_GUI_DEPS=ON \ + -DUNBOUND_INCLUDE_DIR=${EXTERNAL_LINUX_INCLUDE_DIR} \ + -DCMAKE_INSTALL_PREFIX=${PREFIX} \ + -DUSE_DEVICE_TREZOR=OFF \ + ../.. + +make wallet_api -j4 + +find . -path ./lib -prune -o -name '*.a' -exec cp '{}' lib \; +cp -r ./lib/* $DEST_LIB_DIR +cp ../../src/wallet/api/wallet2_api.h $DEST_INCLUDE_DIR + + diff --git a/scripts/linux/build_monero_all.sh b/scripts/linux/build_monero_all.sh new file mode 100755 index 000000000..0ca40af5b --- /dev/null +++ b/scripts/linux/build_monero_all.sh @@ -0,0 +1,13 @@ +#!/bin/sh + + +. ./config.sh + +./build_openssl.sh +./build_iconv.sh +./build_boost.sh +./build_zmq.sh +./build_expat.sh +./build_unbound.sh +./build_sodium.sh +./build_monero.sh diff --git a/scripts/linux/build_openssl.sh b/scripts/linux/build_openssl.sh new file mode 100755 index 000000000..205cf7abf --- /dev/null +++ b/scripts/linux/build_openssl.sh @@ -0,0 +1,19 @@ +#!/bin/sh + +set -e + +. ./config.sh + +OPENSSL_FILENAME=openssl-1.1.1q.tar.gz +OPENSSL_FILE_PATH=${EXTERNAL_LINUX_SOURCE_DIR}/${OPENSSL_FILENAME} +OPENSSL_SRC_DIR=${EXTERNAL_LINUX_SOURCE_DIR}/openssl-1.1.1q +OPENSSL_SHA256="d7939ce614029cdff0b6c20f0e2e5703158a489a72b2507b8bd51bf8c8fd10ca" + +curl https://www.openssl.org/source/${OPENSSL_FILENAME} -o ${OPENSSL_FILE_PATH} + +rm -rf $OPENSSL_SRC_DIR +tar -xzf $OPENSSL_FILE_PATH -C $EXTERNAL_LINUX_SOURCE_DIR +cd $OPENSSL_SRC_DIR +export CFLAGS=-fPIC +./config -fPIC shared --prefix=${EXTERNAL_LINUX_DIR} +make install diff --git a/scripts/linux/build_sodium.sh b/scripts/linux/build_sodium.sh new file mode 100755 index 000000000..3a6f6adf9 --- /dev/null +++ b/scripts/linux/build_sodium.sh @@ -0,0 +1,19 @@ +#!/bin/sh + +set -e + +. ./config.sh + +SODIUM_PATH="${EXTERNAL_LINUX_SOURCE_DIR}/libsodium" +SODIUM_URL="https://github.com/jedisct1/libsodium.git" + +echo "============================ SODIUM ============================" + +echo "Cloning SODIUM from - $SODIUM_URL" +git clone $SODIUM_URL $SODIUM_PATH --branch stable +cd $SODIUM_PATH + + +./configure --prefix=${EXTERNAL_LINUX_DIR} +make +make install diff --git a/scripts/linux/build_unbound.sh b/scripts/linux/build_unbound.sh new file mode 100755 index 000000000..1ae917da9 --- /dev/null +++ b/scripts/linux/build_unbound.sh @@ -0,0 +1,25 @@ +#!/bin/sh + +set -e + +. ./config.sh + +UNBOUND_VERSION=release-1.16.2 +UNBOUND_HASH="cbed768b8ff9bfcf11089a5f1699b7e5707f1ea5" +UNBOUND_DIR_PATH="${EXTERNAL_LINUX_SOURCE_DIR}/unbound-1.16.2" + +echo "============================ Unbound ============================" +rm -rf ${UNBOUND_DIR_PATH} +git clone https://github.com/NLnetLabs/unbound.git -b ${UNBOUND_VERSION} ${UNBOUND_DIR_PATH} +cd $UNBOUND_DIR_PATH +test `git rev-parse HEAD` = ${UNBOUND_HASH} || exit 1 + +export CFLAGS=-fPIC +./configure cxxflags=-fPIC cflags=-fPIC \ + --prefix="${EXTERNAL_LINUX_DIR}" \ + --with-ssl="${EXTERNAL_LINUX_DIR}" \ + --with-libexpat="${EXTERNAL_LINUX_DIR}" \ + --enable-static \ + --disable-flto +make +make install diff --git a/scripts/linux/build_zmq.sh b/scripts/linux/build_zmq.sh new file mode 100755 index 000000000..f6980e40d --- /dev/null +++ b/scripts/linux/build_zmq.sh @@ -0,0 +1,17 @@ +#!/bin/sh + +. ./config.sh + +ZMQ_PATH="${EXTERNAL_LINUX_SOURCE_DIR}/libzmq" +ZMQ_URL="https://github.com/zeromq/libzmq.git" + +echo "============================ ZMQ ============================" + +echo "Cloning ZMQ from - $ZMQ_URL" +git clone $ZMQ_URL $ZMQ_PATH +cd $ZMQ_PATH +mkdir cmake-build +cd cmake-build +cmake .. -DCMAKE_INSTALL_PREFIX="${EXTERNAL_LINUX_DIR}" +make +make install diff --git a/scripts/linux/cakewallet.sh b/scripts/linux/cakewallet.sh new file mode 100755 index 000000000..89c7a7ef0 --- /dev/null +++ b/scripts/linux/cakewallet.sh @@ -0,0 +1,4 @@ +#!/bin/bash + +. ./app_env.sh "cakewallet" +. ./app_config.sh diff --git a/scripts/linux/config.sh b/scripts/linux/config.sh new file mode 100755 index 000000000..3fbdf349e --- /dev/null +++ b/scripts/linux/config.sh @@ -0,0 +1,13 @@ +#!/bin/sh + +export LINUX_SCRIPTS_DIR=`pwd` +export CW_ROOT=${LINUX_SCRIPTS_DIR}/../.. +export EXTERNAL_DIR=${CW_ROOT}/cw_shared_external/ios/External +export EXTERNAL_LINUX_DIR=${EXTERNAL_DIR}/linux +export EXTERNAL_LINUX_SOURCE_DIR=${EXTERNAL_LINUX_DIR}/sources +export EXTERNAL_LINUX_LIB_DIR=${EXTERNAL_LINUX_DIR}/lib +export EXTERNAL_LINUX_INCLUDE_DIR=${EXTERNAL_LINUX_DIR}/include + +mkdir -p $EXTERNAL_LINUX_LIB_DIR +mkdir -p $EXTERNAL_LINUX_INCLUDE_DIR +mkdir -p $EXTERNAL_LINUX_SOURCE_DIR diff --git a/scripts/linux/setup.sh b/scripts/linux/setup.sh new file mode 100755 index 000000000..a323cf027 --- /dev/null +++ b/scripts/linux/setup.sh @@ -0,0 +1,9 @@ +#!/bin/sh + +. ./config.sh + +CW_EXTERNAL_DIR=${CW_ROOT}/cw_monero/ios/External/linux +CW_EXTERNAL_DIR_INCLUDE=${CW_EXTERNAL_DIR}/include + +mkdir -p $CW_EXTERNAL_DIR_INCLUDE +cp $EXTERNAL_LINUX_INCLUDE_DIR/monero/wallet2_api.h $CW_EXTERNAL_DIR_INCLUDE diff --git a/tool/append_translation.dart b/tool/append_translation.dart index e56ad89d6..080b2c5e7 100644 --- a/tool/append_translation.dart +++ b/tool/append_translation.dart @@ -1,66 +1,66 @@ -import 'dart:convert'; -import 'dart:io'; - -import 'package:translator/translator.dart'; - -const defaultLang = "en"; -const langs = [ - "ar", "bg", "cs", "de", "en", "es", "fr", "ha", "hi", "hr", "id", "it", - "ja", "ko", "my", "nl", "pl", "pt", "ru", "th", "tr", "uk", "ur", "yo", - "zh-cn" // zh, but Google Translate uses zh-cn for Chinese (Simplified) -]; -final translator = GoogleTranslator(); - -void main(List<String> args) async { - if (args.length != 2) { - throw Exception( - 'Insufficient arguments!\n\nTry to run `./append_translation.dart greetings "Hello World!"`'); - } - - final name = args.first; - final text = args.last; - - print('Appending "$name": "$text"'); - - for (var lang in langs) { - final fileName = getFileName(lang); - final translation = await getTranslation(text, lang); - - appendArbFile(fileName, name, translation); - } -} - -void appendArbFile(String fileName, String name, String text) { - final file = File(fileName); - final inputContent = file.readAsStringSync(); - final arbObj = json.decode(inputContent) as Map<String, dynamic>; - - if (arbObj.containsKey(name)) { - print("String $name already exists in $fileName!"); - return; - } - - arbObj.addAll({name: text}); - - final outputContent = json - .encode(arbObj) - .replaceAll('","', '",\n "') - .replaceAll('{"', '{\n "') - .replaceAll('"}', '"\n}') - .replaceAll('":"', '": "'); - - file.writeAsStringSync(outputContent); -} - - -Future<String> getTranslation(String text, String lang) async { - if (lang == defaultLang) return text; - return (await translator.translate(text, from: defaultLang, to: lang)).text; -} - -String getFileName(String lang) { - final shortLang = lang - .split("-") - .first; - return "./res/values/strings_$shortLang.arb"; -} +// import 'dart:convert'; +// import 'dart:io'; +// +// import 'package:translator/translator.dart'; +// +// const defaultLang = "en"; +// const langs = [ +// "ar", "bg", "cs", "de", "en", "es", "fr", "ha", "hi", "hr", "id", "it", +// "ja", "ko", "my", "nl", "pl", "pt", "ru", "th", "tr", "uk", "ur", "yo", +// "zh-cn" // zh, but Google Translate uses zh-cn for Chinese (Simplified) +// ]; +// final translator = GoogleTranslator(); +// +// void main(List<String> args) async { +// if (args.length != 2) { +// throw Exception( +// 'Insufficient arguments!\n\nTry to run `./append_translation.dart greetings "Hello World!"`'); +// } +// +// final name = args.first; +// final text = args.last; +// +// print('Appending "$name": "$text"'); +// +// for (var lang in langs) { +// final fileName = getFileName(lang); +// final translation = await getTranslation(text, lang); +// +// appendArbFile(fileName, name, translation); +// } +// } +// +// void appendArbFile(String fileName, String name, String text) { +// final file = File(fileName); +// final inputContent = file.readAsStringSync(); +// final arbObj = json.decode(inputContent) as Map<String, dynamic>; +// +// if (arbObj.containsKey(name)) { +// print("String $name already exists in $fileName!"); +// return; +// } +// +// arbObj.addAll({name: text}); +// +// final outputContent = json +// .encode(arbObj) +// .replaceAll('","', '",\n "') +// .replaceAll('{"', '{\n "') +// .replaceAll('"}', '"\n}') +// .replaceAll('":"', '": "'); +// +// file.writeAsStringSync(outputContent); +// } +// +// +// Future<String> getTranslation(String text, String lang) async { +// if (lang == defaultLang) return text; +// return (await translator.translate(text, from: defaultLang, to: lang)).text; +// } +// +// String getFileName(String lang) { +// final shortLang = lang +// .split("-") +// .first; +// return "./res/values/strings_$shortLang.arb"; +// } diff --git a/tool/configure.dart b/tool/configure.dart index 5172f4244..b6e455d5f 100644 --- a/tool/configure.dart +++ b/tool/configure.dart @@ -5,6 +5,7 @@ const moneroOutputPath = 'lib/monero/monero.dart'; const havenOutputPath = 'lib/haven/haven.dart'; const ethereumOutputPath = 'lib/ethereum/ethereum.dart'; const walletTypesPath = 'lib/wallet_types.g.dart'; +const secureStoragePath = 'lib/core/secure_storage.dart'; const pubspecDefaultPath = 'pubspec_default.yaml'; const pubspecOutputPath = 'pubspec.yaml'; @@ -14,12 +15,14 @@ Future<void> main(List<String> args) async { final hasMonero = args.contains('${prefix}monero'); final hasHaven = args.contains('${prefix}haven'); final hasEthereum = args.contains('${prefix}ethereum'); + final excludeFlutterSecureStorage = args.contains('${prefix}excludeFlutterSecureStorage'); await generateBitcoin(hasBitcoin); await generateMonero(hasMonero); await generateHaven(hasHaven); await generateEthereum(hasEthereum); - await generatePubspec(hasMonero: hasMonero, hasBitcoin: hasBitcoin, hasHaven: hasHaven, hasEthereum: hasEthereum); + await generatePubspec(hasMonero: hasMonero, hasBitcoin: hasBitcoin, hasHaven: hasHaven, hasEthereum: hasEthereum, hasFlutterSecureStorage: !excludeFlutterSecureStorage); await generateWalletTypes(hasMonero: hasMonero, hasBitcoin: hasBitcoin, hasHaven: hasHaven, hasEthereum: hasEthereum); + await injectSecureStorage(!excludeFlutterSecureStorage); } Future<void> generateBitcoin(bool hasImplementation) async { @@ -54,7 +57,7 @@ abstract class Bitcoin { WalletCredentials createBitcoinRestoreWalletFromSeedCredentials({required String name, required String mnemonic, required String password}); WalletCredentials createBitcoinRestoreWalletFromWIFCredentials({required String name, required String password, required String wif, WalletInfo? walletInfo}); - WalletCredentials createBitcoinNewWalletCredentials({required String name, WalletInfo? walletInfo}); + WalletCredentials createBitcoinNewWalletCredentials({required String name, WalletInfo? walletInfo, String? password}); List<String> getWordList(); Map<String, String> getWalletKeys(Object wallet); List<TransactionPriority> getTransactionPriorities(); @@ -76,8 +79,8 @@ abstract class Bitcoin { List<Unspent> getUnspents(Object wallet); void updateUnspents(Object wallet); - WalletService createBitcoinWalletService(Box<WalletInfo> walletInfoSource, Box<UnspentCoinsInfo> unspentCoinSource); - WalletService createLitecoinWalletService(Box<WalletInfo> walletInfoSource, Box<UnspentCoinsInfo> unspentCoinSource); + WalletService createBitcoinWalletService(Box<WalletInfo> walletInfoSource, Box<UnspentCoinsInfo> unspentCoinSource, bool isDirect); + WalletService createLitecoinWalletService(Box<WalletInfo> walletInfoSource, Box<UnspentCoinsInfo> unspentCoinSource, bool isDirect); TransactionPriority getBitcoinTransactionPriorityMedium(); TransactionPriority getLitecoinTransactionPriorityMedium(); TransactionPriority getBitcoinTransactionPrioritySlow(); @@ -230,7 +233,7 @@ abstract class Monero { required String language, required int height}); WalletCredentials createMoneroRestoreWalletFromSeedCredentials({required String name, required String password, required int height, required String mnemonic}); - WalletCredentials createMoneroNewWalletCredentials({required String name, required String language, String password,}); + WalletCredentials createMoneroNewWalletCredentials({required String name, required String language, String? password,}); Map<String, String> getKeys(Object wallet); Object createMoneroTransactionCreationCredentials({required List<Output> outputs, required TransactionPriority priority}); Object createMoneroTransactionCreationCredentialsRaw({required List<OutputInfo> outputs, required TransactionPriority priority}); @@ -410,7 +413,7 @@ abstract class Haven { required String language, required int height}); WalletCredentials createHavenRestoreWalletFromSeedCredentials({required String name, required String password, required int height, required String mnemonic}); - WalletCredentials createHavenNewWalletCredentials({required String name, required String language, String password}); + WalletCredentials createHavenNewWalletCredentials({required String name, required String language, String? password}); Map<String, String> getKeys(Object wallet); Object createHavenTransactionCreationCredentials({required List<Output> outputs, required TransactionPriority priority, required String assetType}); String formatterMoneroAmountToString({required int amount}); @@ -493,8 +496,8 @@ import 'package:cw_ethereum/ethereum_transaction_priority.dart'; const ethereumContent = """ abstract class Ethereum { List<String> getEthereumWordList(String language); - WalletService createEthereumWalletService(Box<WalletInfo> walletInfoSource); - WalletCredentials createEthereumNewWalletCredentials({required String name, WalletInfo? walletInfo}); + WalletService createEthereumWalletService(Box<WalletInfo> walletInfoSource, bool isDirect); + WalletCredentials createEthereumNewWalletCredentials({required String name, WalletInfo? walletInfo, String? password}); WalletCredentials createEthereumRestoreWalletFromSeedCredentials({required String name, required String mnemonic, required String password}); WalletCredentials createEthereumRestoreWalletFromPrivateKey({required String name, required String privateKey, required String password}); String getAddress(WalletBase wallet); @@ -545,7 +548,12 @@ abstract class Ethereum { await outputFile.writeAsString(output); } -Future<void> generatePubspec({required bool hasMonero, required bool hasBitcoin, required bool hasHaven, required bool hasEthereum}) async { +Future<void> generatePubspec({ + required bool hasMonero, + required bool hasBitcoin, + required bool hasHaven, + required bool hasEthereum, + required bool hasFlutterSecureStorage}) async { const cwCore = """ cw_core: path: ./cw_core @@ -570,6 +578,14 @@ Future<void> generatePubspec({required bool hasMonero, required bool hasBitcoin, cw_ethereum: path: ./cw_ethereum """; + const flutterSecureStorage = """ + flutter_secure_storage: + git: + url: https://github.com/cake-tech/flutter_secure_storage.git + path: flutter_secure_storage + ref: cake-6.0.0 + version: 6.0.0 + """; final inputFile = File(pubspecOutputPath); final inputText = await inputFile.readAsString(); final inputLines = inputText.split('\n'); @@ -594,6 +610,10 @@ Future<void> generatePubspec({required bool hasMonero, required bool hasBitcoin, output += '\n$cwEthereum'; } + if (hasFlutterSecureStorage) { + output += '\n$flutterSecureStorage\n'; + } + final outputLines = output.split('\n'); inputLines.insertAll(dependenciesIndex + 1, outputLines); final outputContent = inputLines.join('\n'); @@ -640,3 +660,73 @@ Future<void> generateWalletTypes({required bool hasMonero, required bool hasBitc outputContent += '];\n'; await walletTypesFile.writeAsString(outputContent); } + +Future<void> injectSecureStorage(bool hasFlutterSecureStorage) async { + const flutterSecureStorageHeader = "import 'package:flutter_secure_storage/flutter_secure_storage.dart';"; + const abstractSecureStorage = """ +abstract class SecureStorage { + Future<String?> read({required String key}); + Future<void> write({required String key, required String? value}); + Future<void> delete({required String key}); + // Legacy + Future<String?> readNoIOptions({required String key}); +}"""; + const defaultSecureStorage = """ +class DefaultSecureStorage extends SecureStorage { + DefaultSecureStorage._(this._secureStorage); + + factory DefaultSecureStorage() => _instance; + + static final _instance = DefaultSecureStorage._(FlutterSecureStorage()); + + final FlutterSecureStorage _secureStorage; + + @override + Future<String?> read({required String key}) async + => _secureStorage.read(key: key); + + @override + Future<void> write({required String key, required String? value}) async + => _secureStorage.write(key: key, value: value); + + @override + Future<void> delete({required String key}) async + => _secureStorage.delete(key: key); + + @override + Future<String?> readNoIOptions({required String key}) async + => _secureStorage.read(key: key, iOptions: IOSOptions()); +}"""; + const fakeSecureStorage = """ +class FakeSecureStorage extends SecureStorage { + @override + Future<String?> read({required String key}) async => null; + + @override + Future<void> write({required String key, required String? value}) async {} + + @override + Future<void> delete({required String key}) async {} + + @override + Future<String?> readNoIOptions({required String key}) async => null; +}"""; + final outputFile = File(secureStoragePath); + final header = hasFlutterSecureStorage + ? '${flutterSecureStorageHeader}\n\nfinal SecureStorage secureStorageShared = DefaultSecureStorage();\n' + : 'final SecureStorage secureStorageShared = FakeSecureStorage();\n'; + var output = ''; + if (outputFile.existsSync()) { + await outputFile.delete(); + } + + output += '${header}\n${abstractSecureStorage}\n\n'; + + if (hasFlutterSecureStorage) { + output += '${defaultSecureStorage}\n'; + } else { + output += '${fakeSecureStorage}\n'; + } + + await outputFile.writeAsString(output); +} diff --git a/tool/update_secrets.dart b/tool/update_secrets.dart index 0f2e59419..69e3f8447 100644 --- a/tool/update_secrets.dart +++ b/tool/update_secrets.dart @@ -33,7 +33,8 @@ Future<void> updateSecretsConfig(List<String> args) async { }); final fileConfig = - json.decode(configFile.readAsStringSync()) as Map<String, dynamic>; + json.decode(configFile.readAsStringSync()) as Map<String, dynamic> ?? + <String, dynamic>{}; fileConfig.forEach((key, dynamic value) { if (secrets[key] == null) { secrets[key] = value; diff --git a/tool/utils/secret_key.dart b/tool/utils/secret_key.dart index 7819a582c..14594e371 100644 --- a/tool/utils/secret_key.dart +++ b/tool/utils/secret_key.dart @@ -20,6 +20,7 @@ class SecretKey { SecretKey('moonPayApiKey', () => ''), SecretKey('moonPaySecretKey', () => ''), SecretKey('sideShiftAffiliateId', () => ''), + SecretKey('sideShiftApiKey', () => ''), SecretKey('simpleSwapApiKey', () => ''), SecretKey('simpleSwapApiKeyDesktop', () => ''), SecretKey('anypayToken', () => ''), @@ -31,7 +32,6 @@ class SecretKey { SecretKey('anonPayReferralCode', () => ''), SecretKey('fiatApiKey', () => ''), SecretKey('payfuraApiKey', () => ''), - SecretKey('chatwootWebsiteToken', () => ''), ]; static final ethereumSecrets = [