diff --git a/.github/workflows/cache_dependencies.yml b/.github/workflows/cache_dependencies.yml index e9c53c00f..cca5bb4bf 100644 --- a/.github/workflows/cache_dependencies.yml +++ b/.github/workflows/cache_dependencies.yml @@ -25,7 +25,7 @@ jobs: - uses: actions/checkout@v2 - uses: actions/setup-java@v1 with: - java-version: "11.x" + java-version: "17.x" - name: Configure placeholder git details run: | git config --global user.email "CI@cakewallet.com" diff --git a/.github/workflows/pr_test_build.yml b/.github/workflows/pr_test_build_android.yml similarity index 98% rename from .github/workflows/pr_test_build.yml rename to .github/workflows/pr_test_build_android.yml index 8df7f3fc7..358c4d897 100644 --- a/.github/workflows/pr_test_build.yml +++ b/.github/workflows/pr_test_build_android.yml @@ -53,7 +53,9 @@ jobs: channel: stable - name: Install package dependencies - run: sudo apt-get install -y curl unzip automake build-essential file pkg-config git python libtool libtinfo5 cmake clang + run: | + sudo apt update + sudo apt-get install -y curl unzip automake build-essential file pkg-config git python libtool libtinfo5 cmake clang - name: Execute Build and Setup Commands run: | diff --git a/.github/workflows/pr_test_build_linux.yml b/.github/workflows/pr_test_build_linux.yml new file mode 100644 index 000000000..12c930120 --- /dev/null +++ b/.github/workflows/pr_test_build_linux.yml @@ -0,0 +1,186 @@ +name: PR Test Build linux + +on: + pull_request: + branches: [main] + workflow_dispatch: + inputs: + branch: + description: "Branch name to build" + required: true + default: "main" + +jobs: + PR_test_build: + runs-on: ubuntu-20.04 + env: + STORE_PASS: test@cake_wallet + KEY_PASS: test@cake_wallet + PR_NUMBER: ${{ github.event.number }} + + steps: + - name: is pr + if: github.event_name == 'pull_request' + run: echo "BRANCH_NAME=${GITHUB_HEAD_REF}" >> $GITHUB_ENV + + - name: is not pr + if: github.event_name != 'pull_request' + run: echo "BRANCH_NAME=${{ github.event.inputs.branch }}" >> $GITHUB_ENVg + + - uses: actions/checkout@v2 + - uses: actions/setup-java@v1 + with: + java-version: "17.x" + - name: Configure placeholder git details + run: | + git config --global user.email "CI@cakewallet.com" + git config --global user.name "Cake Github Actions" + - name: Flutter action + uses: subosito/flutter-action@v1 + with: + flutter-version: "3.19.6" + channel: stable + + - name: Install package dependencies + run: | + sudo apt update + sudo apt-get install -y curl unzip automake build-essential file pkg-config git python-is-python3 libtool libtinfo5 cmake clang + + - name: Install desktop dependencies + run: | + sudo apt update + sudo apt install -y ninja-build libgtk-3-dev gperf + - name: Execute Build and Setup Commands + run: | + sudo mkdir -p /opt/android + sudo chown $USER /opt/android + cd /opt/android + -y curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh + cargo install cargo-ndk + git clone https://github.com/cake-tech/cake_wallet.git --branch ${{ env.BRANCH_NAME }} + cd scripts && ./gen_android_manifest.sh && cd .. + cd cake_wallet/scripts/android/ + source ./app_env.sh cakewallet + ./app_config.sh + cd ../../.. + cd cake_wallet/scripts/linux/ + source ./app_env.sh cakewallet + ./app_config.sh + cd ../../.. + + - name: Cache Externals + id: cache-externals + uses: actions/cache@v3 + with: + path: | + /opt/android/cake_wallet/cw_haven/android/.cxx + /opt/android/cake_wallet/scripts/monero_c/release + key: linux-${{ hashFiles('**/prepare_moneroc.sh' ,'**/build_monero_all.sh') }} + + - if: ${{ steps.cache-externals.outputs.cache-hit != 'true' }} + name: Generate Externals + run: | + cd /opt/android/cake_wallet/scripts/linux/ + source ./app_env.sh cakewallet + ./build_monero_all.sh + + - name: Install Flutter dependencies + run: | + cd /opt/android/cake_wallet + flutter pub get + + - name: Generate localization + run: | + cd /opt/android/cake_wallet + flutter packages pub run tool/generate_localization.dart + + - name: Build generated code + run: | + cd /opt/android/cake_wallet + ./model_generator.sh + + - name: Add secrets + run: | + cd /opt/android/cake_wallet + touch lib/.secrets.g.dart + touch cw_evm/lib/.secrets.g.dart + touch cw_solana/lib/.secrets.g.dart + touch cw_core/lib/.secrets.g.dart + touch cw_nano/lib/.secrets.g.dart + touch cw_tron/lib/.secrets.g.dart + echo "const salt = '${{ secrets.SALT }}';" > lib/.secrets.g.dart + echo "const keychainSalt = '${{ secrets.KEY_CHAIN_SALT }}';" >> lib/.secrets.g.dart + echo "const key = '${{ secrets.KEY }}';" >> lib/.secrets.g.dart + echo "const walletSalt = '${{ secrets.WALLET_SALT }}';" >> lib/.secrets.g.dart + echo "const shortKey = '${{ secrets.SHORT_KEY }}';" >> lib/.secrets.g.dart + echo "const backupSalt = '${{ secrets.BACKUP_SALT }}';" >> lib/.secrets.g.dart + echo "const backupKeychainSalt = '${{ secrets.BACKUP_KEY_CHAIN_SALT }}';" >> lib/.secrets.g.dart + echo "const changeNowApiKey = '${{ secrets.CHANGE_NOW_API_KEY }}';" >> lib/.secrets.g.dart + echo "const changeNowApiKeyDesktop = '${{ secrets.CHANGE_NOW_API_KEY_DESKTOP }}';" >> lib/.secrets.g.dart + echo "const wyreSecretKey = '${{ secrets.WYRE_SECRET_KEY }}';" >> lib/.secrets.g.dart + echo "const wyreApiKey = '${{ secrets.WYRE_API_KEY }}';" >> lib/.secrets.g.dart + echo "const wyreAccountId = '${{ secrets.WYRE_ACCOUNT_ID }}';" >> lib/.secrets.g.dart + 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 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 + echo "const anypayToken = '${{ secrets.ANY_PAY_TOKEN }}';" >> lib/.secrets.g.dart + echo "const ioniaClientId = '${{ secrets.IONIA_CLIENT_ID }}';" >> lib/.secrets.g.dart + echo "const twitterBearerToken = '${{ secrets.TWITTER_BEARER_TOKEN }}';" >> lib/.secrets.g.dart + echo "const trocadorApiKey = '${{ secrets.TROCADOR_API_KEY }}';" >> lib/.secrets.g.dart + echo "const trocadorExchangeMarkup = '${{ secrets.TROCADOR_EXCHANGE_MARKUP }}';" >> lib/.secrets.g.dart + echo "const anonPayReferralCode = '${{ secrets.ANON_PAY_REFERRAL_CODE }}';" >> lib/.secrets.g.dart + echo "const fiatApiKey = '${{ secrets.FIAT_API_KEY }}';" >> lib/.secrets.g.dart + echo "const payfuraApiKey = '${{ secrets.PAYFURA_API_KEY }}';" >> lib/.secrets.g.dart + echo "const ankrApiKey = '${{ secrets.ANKR_API_KEY }}';" >> lib/.secrets.g.dart + echo "const etherScanApiKey = '${{ secrets.ETHER_SCAN_API_KEY }}';" >> cw_evm/lib/.secrets.g.dart + echo "const moralisApiKey = '${{ secrets.MORALIS_API_KEY }}';" >> cw_evm/lib/.secrets.g.dart + echo "const chatwootWebsiteToken = '${{ secrets.CHATWOOT_WEBSITE_TOKEN }}';" >> lib/.secrets.g.dart + echo "const exolixApiKey = '${{ secrets.EXOLIX_API_KEY }}';" >> lib/.secrets.g.dart + echo "const robinhoodApplicationId = '${{ secrets.ROBINHOOD_APPLICATION_ID }}';" >> lib/.secrets.g.dart + echo "const exchangeHelperApiKey = '${{ secrets.ROBINHOOD_CID_CLIENT_SECRET }}';" >> lib/.secrets.g.dart + echo "const walletConnectProjectId = '${{ secrets.WALLET_CONNECT_PROJECT_ID }}';" >> lib/.secrets.g.dart + echo "const moralisApiKey = '${{ secrets.MORALIS_API_KEY }}';" >> lib/.secrets.g.dart + echo "const polygonScanApiKey = '${{ secrets.POLYGON_SCAN_API_KEY }}';" >> cw_evm/lib/.secrets.g.dart + echo "const ankrApiKey = '${{ secrets.ANKR_API_KEY }}';" >> cw_solana/lib/.secrets.g.dart + echo "const testCakePayApiKey = '${{ secrets.TEST_CAKE_PAY_API_KEY }}';" >> lib/.secrets.g.dart + echo "const cakePayApiKey = '${{ secrets.CAKE_PAY_API_KEY }}';" >> lib/.secrets.g.dart + echo "const authorization = '${{ secrets.CAKE_PAY_AUTHORIZATION }}';" >> lib/.secrets.g.dart + echo "const CSRFToken = '${{ secrets.CSRF_TOKEN }}';" >> lib/.secrets.g.dart + echo "const quantexExchangeMarkup = '${{ secrets.QUANTEX_EXCHANGE_MARKUP }}';" >> lib/.secrets.g.dart + echo "const nano2ApiKey = '${{ secrets.NANO2_API_KEY }}';" >> cw_nano/lib/.secrets.g.dart + echo "const nanoNowNodesApiKey = '${{ secrets.NANO_NOW_NODES_API_KEY }}';" >> cw_nano/lib/.secrets.g.dart + echo "const tronGridApiKey = '${{ secrets.TRON_GRID_API_KEY }}';" >> cw_tron/lib/.secrets.g.dart + echo "const tronNowNodesApiKey = '${{ secrets.TRON_NOW_NODES_API_KEY }}';" >> cw_tron/lib/.secrets.g.dart + + - name: Rename app + run: | + echo -e "id=com.cakewallet.test_${{ env.PR_NUMBER }}\nname=${{ env.BRANCH_NAME }}" > /opt/android/cake_wallet/android/app.properties + + - name: Build + run: | + cd /opt/android/cake_wallet + flutter build linux --release + + - name: Prepare release zip file + run: | + cd /opt/android/cake_wallet/build/linux/x64/release + zip -r ${{env.BRANCH_NAME}}.zip bundle + + - name: Upload Artifact + uses: kittaakos/upload-artifact-as-is@v0 + with: + path: /opt/android/cake_wallet/build/linux/x64/release/${{env.BRANCH_NAME}}.zip + + - name: Send Test APK + continue-on-error: true + uses: adrey/slack-file-upload-action@1.0.5 + with: + token: ${{ secrets.SLACK_APP_TOKEN }} + path: /opt/android/cake_wallet/build/linux/x64/release/${{env.BRANCH_NAME}}.zip + channel: ${{ secrets.SLACK_APK_CHANNEL }} + title: "${{ env.BRANCH_NAME }}_linux.zip" + filename: ${{ env.BRANCH_NAME }}_linux.zip + initial_comment: ${{ github.event.head_commit.message }} diff --git a/.gitignore b/.gitignore index 77441e66f..8336ca512 100644 --- a/.gitignore +++ b/.gitignore @@ -160,6 +160,8 @@ macos/Runner/Release.entitlements macos/Runner/Runner.entitlements lib/core/secure_storage.dart +lib/core/secure_storage.dart + macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png diff --git a/.metadata b/.metadata index 7d00ca21a..c7b8dc9f8 100644 --- a/.metadata +++ b/.metadata @@ -18,6 +18,12 @@ migration: - platform: windows create_revision: 367f9ea16bfae1ca451b9cc27c1366870b187ae2 base_revision: 367f9ea16bfae1ca451b9cc27c1366870b187ae2 + - platform: macos + create_revision: 367f9ea16bfae1ca451b9cc27c1366870b187ae2 + base_revision: 367f9ea16bfae1ca451b9cc27c1366870b187ae2 + - platform: linux + create_revision: 367f9ea16bfae1ca451b9cc27c1366870b187ae2 + base_revision: 367f9ea16bfae1ca451b9cc27c1366870b187ae2 # User provided section diff --git a/README.md b/README.md index 7823734fb..6e507bfcd 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ <div align="center"> -<img height="100" src=".github/assets/Logo_CakeWallet.png"> + </div> diff --git a/build-guide-linux.md b/build-guide-linux.md new file mode 100644 index 000000000..e0158945b --- /dev/null +++ b/build-guide-linux.md @@ -0,0 +1,176 @@ +# 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.10.x +``` + +## 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` + +> [!WARNING] +> +> ### Check gcc version +> +> It is needed to use gcc 10 or 9 to successfully link dependencies with flutter.\ +> To check what gcc version you are using: +> +> ```bash +> $ gcc --version +> $ g++ --version +> ``` +> +> If you are using gcc version newer than 10, then you need to downgrade to version 10.4.0: +> +> ```bash +> $ 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 +> ``` + +> [!NOTE] +> +> Alternatively, you can use the [nix-shell](https://nixos.org/) with the `gcc10.nix` file\ +> present on `scripts/linux` like so: +> ```bash +> $ nix-shell gcc10.nix +> ``` +> This will get you in a nix environment with all the required dependencies that you can use to build the software from,\ +> and it works in any linux distro. + +### 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.10.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` + +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. 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/configure_cake_wallet.sh b/configure_cake_wallet.sh index 0539221a3..90ce1c446 100755 --- a/configure_cake_wallet.sh +++ b/configure_cake_wallet.sh @@ -3,12 +3,13 @@ IOS="ios" ANDROID="android" MACOS="macos" +LINUX="linux" -PLATFORMS=($IOS $ANDROID $MACOS) +PLATFORMS=($IOS $ANDROID $MACOS $LINUX) PLATFORM=$1 if ! [[ " ${PLATFORMS[*]} " =~ " ${PLATFORM} " ]]; then - echo "specify platform: ./configure_cake_wallet.sh ios|android|macos" + echo "specify platform: ./configure_cake_wallet.sh ios|android|macos|linux" exit 1 fi @@ -27,9 +28,14 @@ if [ "$PLATFORM" == "$ANDROID" ]; then cd scripts/android fi +if [ "$PLATFORM" == "$LINUX" ]; then + echo "Configuring for linux" + cd scripts/linux +fi + source ./app_env.sh cakewallet ./app_config.sh cd ../.. && flutter pub get -#flutter packages pub run tool/generate_localization.dart +flutter packages pub run tool/generate_localization.dart ./model_generator.sh -#cd macos && pod install \ No newline at end of file +#cd macos && pod install diff --git a/cw_bitcoin/lib/bitcoin_wallet.dart b/cw_bitcoin/lib/bitcoin_wallet.dart index 7b8250541..e2e537ee8 100644 --- a/cw_bitcoin/lib/bitcoin_wallet.dart +++ b/cw_bitcoin/lib/bitcoin_wallet.dart @@ -5,9 +5,10 @@ import 'package:bitcoin_base/bitcoin_base.dart'; import 'package:blockchain_utils/blockchain_utils.dart'; import 'package:cw_bitcoin/bitcoin_address_record.dart'; import 'package:cw_bitcoin/bitcoin_mnemonic.dart'; +import 'package:cw_core/encryption_file_utils.dart'; +import 'package:cw_bitcoin/electrum_derivations.dart'; import 'package:cw_bitcoin/bitcoin_wallet_addresses.dart'; import 'package:cw_bitcoin/electrum_balance.dart'; -import 'package:cw_bitcoin/electrum_derivations.dart'; import 'package:cw_bitcoin/electrum_wallet.dart'; import 'package:cw_bitcoin/electrum_wallet_snapshot.dart'; import 'package:cw_bitcoin/psbt_transaction_builder.dart'; @@ -30,6 +31,7 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store { required String password, required WalletInfo walletInfo, required Box<UnspentCoinsInfo> unspentCoinsInfo, + required EncryptionFileUtils encryptionFileUtils, Uint8List? seedBytes, String? mnemonic, String? xpub, @@ -58,6 +60,7 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store { initialAddresses: initialAddresses, initialBalance: initialBalance, seedBytes: seedBytes, + encryptionFileUtils: encryptionFileUtils, currency: networkParam == BitcoinNetwork.testnet ? CryptoCurrency.tbtc : CryptoCurrency.btc, alwaysScan: alwaysScan, @@ -90,6 +93,7 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store { required String password, required WalletInfo walletInfo, required Box<UnspentCoinsInfo> unspentCoinsInfo, + required EncryptionFileUtils encryptionFileUtils, String? passphrase, String? addressPageType, BasedUtxoNetwork? network, @@ -124,6 +128,7 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store { initialSilentAddresses: initialSilentAddresses, initialSilentAddressIndex: initialSilentAddressIndex, initialBalance: initialBalance, + encryptionFileUtils: encryptionFileUtils, seedBytes: seedBytes, initialRegularAddressIndex: initialRegularAddressIndex, initialChangeAddressIndex: initialChangeAddressIndex, @@ -137,6 +142,7 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store { required WalletInfo walletInfo, required Box<UnspentCoinsInfo> unspentCoinsInfo, required String password, + required EncryptionFileUtils encryptionFileUtils, required bool alwaysScan, }) async { final network = walletInfo.network != null @@ -148,7 +154,13 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store { ElectrumWalletSnapshot? snp = null; try { - snp = await ElectrumWalletSnapshot.load(name, walletInfo.type, password, network); + snp = await ElectrumWalletSnapshot.load( + encryptionFileUtils, + name, + walletInfo.type, + password, + network, + ); } catch (e) { if (!hasKeysFile) rethrow; } @@ -156,10 +168,18 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store { final WalletKeysData keysData; // Migrate wallet from the old scheme to then new .keys file scheme if (!hasKeysFile) { - keysData = - WalletKeysData(mnemonic: snp!.mnemonic, xPub: snp.xpub, passphrase: snp.passphrase); + keysData = WalletKeysData( + mnemonic: snp!.mnemonic, + xPub: snp.xpub, + passphrase: snp.passphrase, + ); } else { - keysData = await WalletKeysFile.readKeysFile(name, walletInfo.type, password); + keysData = await WalletKeysFile.readKeysFile( + name, + walletInfo.type, + password, + encryptionFileUtils, + ); } walletInfo.derivationInfo ??= DerivationInfo(); @@ -198,6 +218,7 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store { initialSilentAddresses: snp?.silentAddresses, initialSilentAddressIndex: snp?.silentAddressIndex ?? 0, initialBalance: snp?.balance, + encryptionFileUtils: encryptionFileUtils, seedBytes: seedBytes, 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 915d7cc10..91b8e4ae2 100644 --- a/cw_bitcoin/lib/bitcoin_wallet_creation_credentials.dart +++ b/cw_bitcoin/lib/bitcoin_wallet_creation_credentials.dart @@ -6,11 +6,13 @@ class BitcoinNewWalletCredentials extends WalletCredentials { BitcoinNewWalletCredentials( {required String name, WalletInfo? walletInfo, + String? password, DerivationType? derivationType, String? derivationPath}) : super( name: name, walletInfo: walletInfo, + password: password, ); } diff --git a/cw_bitcoin/lib/bitcoin_wallet_service.dart b/cw_bitcoin/lib/bitcoin_wallet_service.dart index cf93aa29d..d6d97f3de 100644 --- a/cw_bitcoin/lib/bitcoin_wallet_service.dart +++ b/cw_bitcoin/lib/bitcoin_wallet_service.dart @@ -3,6 +3,7 @@ import 'package:bitcoin_base/bitcoin_base.dart'; import 'package:cw_bitcoin/bitcoin_mnemonic.dart'; import 'package:cw_bitcoin/mnemonic_is_incorrect_exception.dart'; import 'package:cw_bitcoin/bitcoin_wallet_creation_credentials.dart'; +import 'package:cw_core/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'; @@ -19,11 +20,12 @@ class BitcoinWalletService extends WalletService< BitcoinRestoreWalletFromSeedCredentials, BitcoinRestoreWalletFromWIFCredentials, BitcoinRestoreWalletFromHardware> { - BitcoinWalletService(this.walletInfoSource, this.unspentCoinsInfoSource, this.alwaysScan); + BitcoinWalletService(this.walletInfoSource, this.unspentCoinsInfoSource, this.alwaysScan, this.isDirect); final Box<WalletInfo> walletInfoSource; final Box<UnspentCoinsInfo> unspentCoinsInfoSource; final bool alwaysScan; + final bool isDirect; @override WalletType getType() => WalletType.bitcoin; @@ -40,6 +42,7 @@ class BitcoinWalletService extends WalletService< walletInfo: credentials.walletInfo!, unspentCoinsInfo: unspentCoinsInfoSource, network: network, + encryptionFileUtils: encryptionFileUtilsFor(isDirect), ); await wallet.save(); @@ -63,6 +66,7 @@ class BitcoinWalletService extends WalletService< walletInfo: walletInfo, unspentCoinsInfo: unspentCoinsInfoSource, alwaysScan: alwaysScan, + encryptionFileUtils: encryptionFileUtilsFor(isDirect), ); await wallet.init(); saveBackup(name); @@ -75,6 +79,7 @@ class BitcoinWalletService extends WalletService< walletInfo: walletInfo, unspentCoinsInfo: unspentCoinsInfoSource, alwaysScan: alwaysScan, + encryptionFileUtils: encryptionFileUtilsFor(isDirect), ); await wallet.init(); return wallet; @@ -99,6 +104,7 @@ class BitcoinWalletService extends WalletService< walletInfo: currentWalletInfo, unspentCoinsInfo: unspentCoinsInfoSource, alwaysScan: alwaysScan, + encryptionFileUtils: encryptionFileUtilsFor(isDirect), ); await currentWallet.renameWalletFiles(newName); @@ -125,6 +131,7 @@ class BitcoinWalletService extends WalletService< walletInfo: credentials.walletInfo!, unspentCoinsInfo: unspentCoinsInfoSource, networkParam: network, + encryptionFileUtils: encryptionFileUtilsFor(isDirect), ); await wallet.save(); await wallet.init(); @@ -153,6 +160,7 @@ class BitcoinWalletService extends WalletService< walletInfo: credentials.walletInfo!, unspentCoinsInfo: unspentCoinsInfoSource, network: network, + encryptionFileUtils: encryptionFileUtilsFor(isDirect), ); await wallet.save(); await wallet.init(); diff --git a/cw_bitcoin/lib/electrum_transaction_history.dart b/cw_bitcoin/lib/electrum_transaction_history.dart index a7de414e4..806f813dd 100644 --- a/cw_bitcoin/lib/electrum_transaction_history.dart +++ b/cw_bitcoin/lib/electrum_transaction_history.dart @@ -1,4 +1,5 @@ import 'dart:convert'; +import 'package:cw_core/encryption_file_utils.dart'; import 'package:cw_bitcoin/electrum_transaction_info.dart'; import 'package:cw_core/pathForWallet.dart'; @@ -6,6 +7,8 @@ import 'package:cw_core/transaction_history.dart'; import 'package:cw_core/utils/file.dart'; import 'package:cw_core/wallet_info.dart'; import 'package:mobx/mobx.dart'; +import 'package:cw_core/transaction_history.dart'; +import 'package:cw_bitcoin/electrum_transaction_info.dart'; part 'electrum_transaction_history.g.dart'; @@ -15,13 +18,15 @@ class ElectrumTransactionHistory = ElectrumTransactionHistoryBase with _$Electru abstract class ElectrumTransactionHistoryBase extends TransactionHistoryBase<ElectrumTransactionInfo> with Store { - ElectrumTransactionHistoryBase({required this.walletInfo, required String password}) + ElectrumTransactionHistoryBase( + {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 +49,7 @@ abstract class ElectrumTransactionHistoryBase txjson[tx.key] = tx.value.toJson(); } final data = json.encode({'height': _height, 'transactions': txjson}); - 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()}'); } @@ -58,7 +63,7 @@ abstract class ElectrumTransactionHistoryBase 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); return json.decode(content) as Map<String, dynamic>; } diff --git a/cw_bitcoin/lib/electrum_wallet.dart b/cw_bitcoin/lib/electrum_wallet.dart index c06435d8e..4ed92c48e 100644 --- a/cw_bitcoin/lib/electrum_wallet.dart +++ b/cw_bitcoin/lib/electrum_wallet.dart @@ -4,6 +4,7 @@ import 'dart:io'; import 'dart:isolate'; import 'package:bitcoin_base/bitcoin_base.dart'; +import 'package:cw_core/encryption_file_utils.dart'; import 'package:blockchain_utils/blockchain_utils.dart'; import 'package:collection/collection.dart'; import 'package:cw_bitcoin/address_from_output.dart'; @@ -32,7 +33,6 @@ import 'package:cw_core/sync_status.dart'; import 'package:cw_core/transaction_direction.dart'; import 'package:cw_core/transaction_priority.dart'; import 'package:cw_core/unspent_coins_info.dart'; -import 'package:cw_core/utils/file.dart'; import 'package:cw_core/wallet_base.dart'; import 'package:cw_core/wallet_info.dart'; import 'package:cw_core/wallet_keys_file.dart'; @@ -58,6 +58,7 @@ abstract class ElectrumWalletBase required WalletInfo walletInfo, required Box<UnspentCoinsInfo> unspentCoinsInfo, required this.network, + required this.encryptionFileUtils, String? xpub, String? mnemonic, Uint8List? seedBytes, @@ -92,7 +93,11 @@ abstract class ElectrumWalletBase super(walletInfo) { this.electrumClient = electrumClient ?? ElectrumClient(); this.walletInfo = walletInfo; - transactionHistory = ElectrumTransactionHistory(walletInfo: walletInfo, password: password); + transactionHistory = ElectrumTransactionHistory( + walletInfo: walletInfo, + password: password, + encryptionFileUtils: encryptionFileUtils, + ); reaction((_) => syncStatus, _syncStatusReaction); } @@ -133,6 +138,8 @@ abstract class ElectrumWalletBase final String? _mnemonic; Bip32Slip10Secp256k1 get hd => accountHD.childKey(Bip32KeyIndex(0)); + + final EncryptionFileUtils encryptionFileUtils; final String? passphrase; @override @@ -175,6 +182,9 @@ abstract class ElectrumWalletBase WalletKeysData get walletKeysData => WalletKeysData(mnemonic: _mnemonic, xPub: xpub, passphrase: passphrase); + @override + String get password => _password; + BasedUtxoNetwork network; @override @@ -1151,12 +1161,12 @@ abstract class ElectrumWalletBase @override Future<void> save() async { if (!(await WalletKeysFile.hasKeysFile(walletInfo.name, walletInfo.type))) { - await saveKeysFile(_password); - saveKeysFile(_password, true); + await saveKeysFile(_password, encryptionFileUtils); + saveKeysFile(_password, encryptionFileUtils, true); } final path = await makePath(); - await write(path: path, password: _password, data: toJSON()); + await encryptionFileUtils.write(path: path, password: _password, data: toJSON()); await transactionHistory.save(); } diff --git a/cw_bitcoin/lib/electrum_wallet_snapshot.dart b/cw_bitcoin/lib/electrum_wallet_snapshot.dart index 082460f72..fa58be238 100644 --- a/cw_bitcoin/lib/electrum_wallet_snapshot.dart +++ b/cw_bitcoin/lib/electrum_wallet_snapshot.dart @@ -2,6 +2,7 @@ import 'dart:convert'; import 'package:bitcoin_base/bitcoin_base.dart'; import 'package:cw_bitcoin/bitcoin_address_record.dart'; import 'package:cw_bitcoin/electrum_balance.dart'; +import 'package:cw_core/encryption_file_utils.dart'; import 'package:cw_bitcoin/electrum_derivations.dart'; import 'package:cw_core/pathForWallet.dart'; import 'package:cw_core/wallet_info.dart'; @@ -51,9 +52,9 @@ class ElectrumWalletSnapshot { String? derivationPath; static Future<ElectrumWalletSnapshot> load( - String name, WalletType type, String password, BasedUtxoNetwork network) async { + EncryptionFileUtils encryptionFileUtils, String name, WalletType type, String password, BasedUtxoNetwork network) 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/litecoin_wallet.dart b/cw_bitcoin/lib/litecoin_wallet.dart index 6d7554bab..0b4ec5459 100644 --- a/cw_bitcoin/lib/litecoin_wallet.dart +++ b/cw_bitcoin/lib/litecoin_wallet.dart @@ -21,6 +21,8 @@ import 'package:cw_core/crypto_currency.dart'; import 'package:cw_core/pending_transaction.dart'; import 'package:cw_core/sync_status.dart'; import 'package:cw_core/transaction_direction.dart'; +import 'package:cw_core/encryption_file_utils.dart'; +import 'package:cw_core/crypto_currency.dart'; import 'package:cw_core/unspent_coins_info.dart'; import 'package:cw_bitcoin/electrum_balance.dart'; import 'package:cw_bitcoin/electrum_wallet.dart'; @@ -46,6 +48,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { required WalletInfo walletInfo, required Box<UnspentCoinsInfo> unspentCoinsInfo, required Uint8List seedBytes, + required EncryptionFileUtils encryptionFileUtils, String? addressPageType, List<BitcoinAddressRecord>? initialAddresses, ElectrumBalance? initialBalance, @@ -62,6 +65,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { initialAddresses: initialAddresses, initialBalance: initialBalance, seedBytes: seedBytes, + encryptionFileUtils: encryptionFileUtils, currency: CryptoCurrency.ltc, ) { mwebHd = Bip32Slip10Secp256k1.fromSeed(seedBytes).derivePath("m/1000'") as Bip32Slip10Secp256k1; @@ -99,6 +103,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { required String password, required WalletInfo walletInfo, required Box<UnspentCoinsInfo> unspentCoinsInfo, + required EncryptionFileUtils encryptionFileUtils, String? passphrase, String? addressPageType, List<BitcoinAddressRecord>? initialAddresses, @@ -126,6 +131,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { unspentCoinsInfo: unspentCoinsInfo, initialAddresses: initialAddresses, initialBalance: initialBalance, + encryptionFileUtils: encryptionFileUtils, seedBytes: seedBytes, initialRegularAddressIndex: initialRegularAddressIndex, initialChangeAddressIndex: initialChangeAddressIndex, @@ -139,6 +145,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { required Box<UnspentCoinsInfo> unspentCoinsInfo, required String password, required bool alwaysScan, + required EncryptionFileUtils encryptionFileUtils, }) async { final hasKeysFile = await WalletKeysFile.hasKeysFile(name, walletInfo.type); @@ -146,7 +153,12 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { try { snp = await ElectrumWalletSnapshot.load( - name, walletInfo.type, password, LitecoinNetwork.mainnet); + encryptionFileUtils, + name, + walletInfo.type, + password, + LitecoinNetwork.mainnet, + ); } catch (e) { if (!hasKeysFile) rethrow; } @@ -157,7 +169,12 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { keysData = WalletKeysData(mnemonic: snp!.mnemonic, xPub: snp.xpub, passphrase: snp.passphrase); } else { - keysData = await WalletKeysFile.readKeysFile(name, walletInfo.type, password); + keysData = await WalletKeysFile.readKeysFile( + name, + walletInfo.type, + password, + encryptionFileUtils, + ); } return LitecoinWallet( @@ -168,6 +185,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { initialAddresses: snp?.addresses, initialBalance: snp?.balance, seedBytes: await mnemonicToSeedBytes(keysData.mnemonic!), + encryptionFileUtils: encryptionFileUtils, initialRegularAddressIndex: snp?.regularAddressIndex, initialChangeAddressIndex: snp?.changeAddressIndex, addressPageType: snp?.addressPageType, diff --git a/cw_bitcoin/lib/litecoin_wallet_service.dart b/cw_bitcoin/lib/litecoin_wallet_service.dart index f66b537e1..a1eeb1d96 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_core/encryption_file_utils.dart'; import 'package:cw_core/unspent_coins_info.dart'; import 'package:hive/hive.dart'; import 'package:cw_bitcoin/bitcoin_mnemonic.dart'; @@ -19,10 +20,11 @@ class LitecoinWalletService extends WalletService< BitcoinRestoreWalletFromSeedCredentials, BitcoinRestoreWalletFromWIFCredentials, BitcoinNewWalletCredentials> { - LitecoinWalletService(this.walletInfoSource, this.unspentCoinsInfoSource, this.alwaysScan); + LitecoinWalletService(this.walletInfoSource, this.unspentCoinsInfoSource, this.isDirect, this.alwaysScan); final Box<WalletInfo> walletInfoSource; final Box<UnspentCoinsInfo> unspentCoinsInfoSource; + final bool isDirect; final bool alwaysScan; @override @@ -36,8 +38,8 @@ class LitecoinWalletService extends WalletService< passphrase: credentials.passphrase, walletInfo: credentials.walletInfo!, unspentCoinsInfo: unspentCoinsInfoSource, + encryptionFileUtils: encryptionFileUtilsFor(isDirect), ); - await wallet.save(); await wallet.init(); @@ -60,6 +62,7 @@ class LitecoinWalletService extends WalletService< walletInfo: walletInfo, unspentCoinsInfo: unspentCoinsInfoSource, alwaysScan: alwaysScan, + encryptionFileUtils: encryptionFileUtilsFor(isDirect), ); await wallet.init(); saveBackup(name); @@ -72,6 +75,7 @@ class LitecoinWalletService extends WalletService< walletInfo: walletInfo, unspentCoinsInfo: unspentCoinsInfoSource, alwaysScan: alwaysScan, + encryptionFileUtils: encryptionFileUtilsFor(isDirect), ); await wallet.init(); return wallet; @@ -105,6 +109,7 @@ class LitecoinWalletService extends WalletService< walletInfo: currentWalletInfo, unspentCoinsInfo: unspentCoinsInfoSource, alwaysScan: alwaysScan, + encryptionFileUtils: encryptionFileUtilsFor(isDirect), ); await currentWallet.renameWalletFiles(newName); @@ -136,11 +141,13 @@ class LitecoinWalletService extends WalletService< } final wallet = await LitecoinWalletBase.create( - password: credentials.password!, - passphrase: credentials.passphrase, - mnemonic: credentials.mnemonic, - walletInfo: credentials.walletInfo!, - unspentCoinsInfo: unspentCoinsInfoSource); + password: credentials.password!, + passphrase: credentials.passphrase, + mnemonic: credentials.mnemonic, + walletInfo: credentials.walletInfo!, + 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 99ad737b1..4445a5bdf 100644 --- a/cw_bitcoin/pubspec.lock +++ b/cw_bitcoin/pubspec.lock @@ -172,6 +172,15 @@ packages: url: "https://pub.dev" source: hosted version: "8.9.2" + cake_backup: + dependency: transitive + 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: @@ -244,6 +253,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.7.0" + cupertino_icons: + dependency: transitive + description: + name: cupertino_icons + sha256: ba631d1c7f7bef6b729a622b7b752645a2d076dba9976925b8f25725a30e1ee6 + url: "https://pub.dev" + source: hosted + version: "1.0.8" cw_core: dependency: "direct main" description: @@ -868,6 +885,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.1" + tuple: + dependency: transitive + description: + name: tuple + sha256: a97ce2013f240b2f3807bcbaf218765b6f301c3eff91092bcfa23a039e7dd151 + url: "https://pub.dev" + source: hosted + version: "2.0.2" typed_data: dependency: transitive description: diff --git a/cw_bitcoin_cash/lib/src/bitcoin_cash_wallet.dart b/cw_bitcoin_cash/lib/src/bitcoin_cash_wallet.dart index 8323c01a8..a59569ae6 100644 --- a/cw_bitcoin_cash/lib/src/bitcoin_cash_wallet.dart +++ b/cw_bitcoin_cash/lib/src/bitcoin_cash_wallet.dart @@ -1,6 +1,7 @@ import 'package:bitbox/bitbox.dart' as bitbox; import 'package:bitcoin_base/bitcoin_base.dart'; import 'package:blockchain_utils/blockchain_utils.dart'; +import 'package:cw_core/encryption_file_utils.dart'; import 'package:cw_bitcoin/bitcoin_address_record.dart'; import 'package:cw_bitcoin/bitcoin_transaction_priority.dart'; import 'package:cw_bitcoin/electrum_balance.dart'; @@ -28,6 +29,7 @@ abstract class BitcoinCashWalletBase extends ElectrumWallet with Store { required WalletInfo walletInfo, required Box<UnspentCoinsInfo> unspentCoinsInfo, required Uint8List seedBytes, + required EncryptionFileUtils encryptionFileUtils, BitcoinAddressType? addressPageType, List<BitcoinAddressRecord>? initialAddresses, ElectrumBalance? initialBalance, @@ -42,7 +44,8 @@ abstract class BitcoinCashWalletBase extends ElectrumWallet with Store { initialAddresses: initialAddresses, initialBalance: initialBalance, seedBytes: seedBytes, - currency: CryptoCurrency.bch) { + currency: CryptoCurrency.bch, + encryptionFileUtils: encryptionFileUtils) { walletAddresses = BitcoinCashWalletAddresses( walletInfo, initialAddresses: initialAddresses, @@ -63,6 +66,7 @@ abstract class BitcoinCashWalletBase extends ElectrumWallet with Store { required String password, required WalletInfo walletInfo, required Box<UnspentCoinsInfo> unspentCoinsInfo, + required EncryptionFileUtils encryptionFileUtils, String? addressPageType, List<BitcoinAddressRecord>? initialAddresses, ElectrumBalance? initialBalance, @@ -76,6 +80,7 @@ abstract class BitcoinCashWalletBase extends ElectrumWallet with Store { initialAddresses: initialAddresses, initialBalance: initialBalance, seedBytes: await MnemonicBip39.toSeed(mnemonic), + encryptionFileUtils: encryptionFileUtils, initialRegularAddressIndex: initialRegularAddressIndex, initialChangeAddressIndex: initialChangeAddressIndex, addressPageType: P2pkhAddressType.p2pkh, @@ -87,6 +92,7 @@ abstract class BitcoinCashWalletBase extends ElectrumWallet with Store { required WalletInfo walletInfo, required Box<UnspentCoinsInfo> unspentCoinsInfo, required String password, + required EncryptionFileUtils encryptionFileUtils, }) async { final hasKeysFile = await WalletKeysFile.hasKeysFile(name, walletInfo.type); @@ -94,7 +100,12 @@ abstract class BitcoinCashWalletBase extends ElectrumWallet with Store { try { snp = await ElectrumWalletSnapshot.load( - name, walletInfo.type, password, BitcoinCashNetwork.mainnet); + encryptionFileUtils, + name, + walletInfo.type, + password, + BitcoinCashNetwork.mainnet, + ); } catch (e) { if (!hasKeysFile) rethrow; } @@ -105,7 +116,12 @@ abstract class BitcoinCashWalletBase extends ElectrumWallet with Store { keysData = WalletKeysData(mnemonic: snp!.mnemonic, xPub: snp.xpub, passphrase: snp.passphrase); } else { - keysData = await WalletKeysFile.readKeysFile(name, walletInfo.type, password); + keysData = await WalletKeysFile.readKeysFile( + name, + walletInfo.type, + password, + encryptionFileUtils, + ); } return BitcoinCashWallet( @@ -135,6 +151,7 @@ abstract class BitcoinCashWalletBase extends ElectrumWallet with Store { }).toList(), initialBalance: snp?.balance, seedBytes: await MnemonicBip39.toSeed(keysData.mnemonic!), + encryptionFileUtils: encryptionFileUtils, initialRegularAddressIndex: snp?.regularAddressIndex, initialChangeAddressIndex: snp?.changeAddressIndex, addressPageType: P2pkhAddressType.p2pkh, diff --git a/cw_bitcoin_cash/lib/src/bitcoin_cash_wallet_creation_credentials.dart b/cw_bitcoin_cash/lib/src/bitcoin_cash_wallet_creation_credentials.dart index 72caa6c58..017040c5d 100644 --- a/cw_bitcoin_cash/lib/src/bitcoin_cash_wallet_creation_credentials.dart +++ b/cw_bitcoin_cash/lib/src/bitcoin_cash_wallet_creation_credentials.dart @@ -2,8 +2,8 @@ import 'package:cw_core/wallet_credentials.dart'; import 'package:cw_core/wallet_info.dart'; class BitcoinCashNewWalletCredentials extends WalletCredentials { - BitcoinCashNewWalletCredentials({required String name, WalletInfo? walletInfo}) - : super(name: name, walletInfo: walletInfo); + BitcoinCashNewWalletCredentials({required String name, WalletInfo? walletInfo, String? password}) + : super(name: name, walletInfo: walletInfo, password: password); } class BitcoinCashRestoreWalletFromSeedCredentials extends WalletCredentials { diff --git a/cw_bitcoin_cash/lib/src/bitcoin_cash_wallet_service.dart b/cw_bitcoin_cash/lib/src/bitcoin_cash_wallet_service.dart index 002e52c4f..a970be261 100644 --- a/cw_bitcoin_cash/lib/src/bitcoin_cash_wallet_service.dart +++ b/cw_bitcoin_cash/lib/src/bitcoin_cash_wallet_service.dart @@ -2,6 +2,7 @@ import 'dart:io'; import 'package:bip39/bip39.dart'; import 'package:cw_bitcoin_cash/cw_bitcoin_cash.dart'; +import 'package:cw_core/encryption_file_utils.dart'; import 'package:cw_core/pathForWallet.dart'; import 'package:cw_core/unspent_coins_info.dart'; import 'package:cw_core/wallet_base.dart'; @@ -16,10 +17,11 @@ class BitcoinCashWalletService extends WalletService< BitcoinCashRestoreWalletFromSeedCredentials, BitcoinCashRestoreWalletFromWIFCredentials, BitcoinCashNewWalletCredentials> { - BitcoinCashWalletService(this.walletInfoSource, this.unspentCoinsInfoSource); + BitcoinCashWalletService(this.walletInfoSource, this.unspentCoinsInfoSource, this.isDirect); final Box<WalletInfo> walletInfoSource; final Box<UnspentCoinsInfo> unspentCoinsInfoSource; + final bool isDirect; @override WalletType getType() => WalletType.bitcoinCash; @@ -34,10 +36,11 @@ class BitcoinCashWalletService extends WalletService< final wallet = await BitcoinCashWalletBase.create( mnemonic: await MnemonicBip39.generate(strength: strength), - password: credentials.password!, - walletInfo: credentials.walletInfo!, - unspentCoinsInfo: unspentCoinsInfoSource); - + password: credentials.password!, + walletInfo: credentials.walletInfo!, + unspentCoinsInfo: unspentCoinsInfoSource, + encryptionFileUtils: encryptionFileUtilsFor(isDirect), + ); await wallet.save(); await wallet.init(); @@ -54,7 +57,9 @@ class BitcoinCashWalletService extends WalletService< password: password, name: name, walletInfo: walletInfo, - unspentCoinsInfo: unspentCoinsInfoSource); + unspentCoinsInfo: unspentCoinsInfoSource, + encryptionFileUtils: encryptionFileUtilsFor(isDirect), + ); await wallet.init(); saveBackup(name); return wallet; @@ -64,7 +69,9 @@ class BitcoinCashWalletService extends WalletService< password: password, name: name, walletInfo: walletInfo, - unspentCoinsInfo: unspentCoinsInfoSource); + unspentCoinsInfo: unspentCoinsInfoSource, + encryptionFileUtils: encryptionFileUtilsFor(isDirect), + ); await wallet.init(); return wallet; } @@ -86,7 +93,8 @@ class BitcoinCashWalletService extends WalletService< password: password, name: currentName, walletInfo: currentWalletInfo, - unspentCoinsInfo: unspentCoinsInfoSource); + unspentCoinsInfo: unspentCoinsInfoSource, + encryptionFileUtils: encryptionFileUtilsFor(isDirect)); await currentWallet.renameWalletFiles(newName); await saveBackup(newName); @@ -121,7 +129,8 @@ class BitcoinCashWalletService 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_core/lib/encryption_file_utils.dart b/cw_core/lib/encryption_file_utils.dart new file mode 100644 index 000000000..1889c4389 --- /dev/null +++ b/cw_core/lib/encryption_file_utils.dart @@ -0,0 +1,42 @@ +import 'dart:io'; +import 'dart:typed_data'; +import 'package:cw_core/utils/file.dart' as file; +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 file.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 file.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_core/lib/wallet_base.dart b/cw_core/lib/wallet_base.dart index fd661c267..4084d69a7 100644 --- a/cw_core/lib/wallet_base.dart +++ b/cw_core/lib/wallet_base.dart @@ -85,6 +85,8 @@ abstract class WalletBase<BalanceType extends Balance, HistoryType extends Trans 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_keys_file.dart b/cw_core/lib/wallet_keys_file.dart index 45539e09d..638cdc39d 100644 --- a/cw_core/lib/wallet_keys_file.dart +++ b/cw_core/lib/wallet_keys_file.dart @@ -3,10 +3,10 @@ import 'dart:developer' as dev; import 'dart:io'; import 'package:cw_core/balance.dart'; +import 'package:cw_core/encryption_file_utils.dart'; import 'package:cw_core/pathForWallet.dart'; import 'package:cw_core/transaction_history.dart'; import 'package:cw_core/transaction_info.dart'; -import 'package:cw_core/utils/file.dart'; import 'package:cw_core/wallet_base.dart'; import 'package:cw_core/wallet_type.dart'; @@ -20,28 +20,32 @@ mixin WalletKeysFile<BalanceType extends Balance, HistoryType extends Transactio Future<String> makeKeysFilePath() async => "${await makePath()}.keys"; - Future<void> saveKeysFile(String password, [bool isBackup = false]) async { + Future<void> saveKeysFile(String password, EncryptionFileUtils encryptionFileUtils, + [bool isBackup = false]) async { try { final rootPath = await makeKeysFilePath(); final path = "$rootPath${isBackup ? ".backup" : ""}"; dev.log("Saving .keys file '$path'"); - await write(path: path, password: password, data: walletKeysData.toJSON()); + await encryptionFileUtils.write( + path: path, password: password, data: walletKeysData.toJSON()); } catch (_) {} } - static Future<void> createKeysFile( - String name, WalletType type, String password, WalletKeysData walletKeysData, + static Future<void> createKeysFile(String name, WalletType type, String password, + WalletKeysData walletKeysData, EncryptionFileUtils encryptionFileUtils, [bool withBackup = true]) async { try { final rootPath = await pathForWallet(name: name, type: type); final path = "$rootPath.keys"; dev.log("Saving .keys file '$path'"); - await write(path: path, password: password, data: walletKeysData.toJSON()); + await encryptionFileUtils.write( + path: path, password: password, data: walletKeysData.toJSON()); if (withBackup) { dev.log("Saving .keys.backup file '$path.backup'"); - await write(path: "$path.backup", password: password, data: walletKeysData.toJSON()); + await encryptionFileUtils.write( + path: "$path.backup", password: password, data: walletKeysData.toJSON()); } } catch (_) {} } @@ -55,14 +59,19 @@ mixin WalletKeysFile<BalanceType extends Balance, HistoryType extends Transactio } } - static Future<WalletKeysData> readKeysFile(String name, WalletType type, String password) async { + static Future<WalletKeysData> readKeysFile( + String name, + WalletType type, + String password, + EncryptionFileUtils encryptionFileUtils, + ) async { final path = await pathForWallet(name: name, type: type); var readPath = "$path.keys"; try { if (!File(readPath).existsSync()) throw Exception("No .keys file found for $name $type"); - final jsonSource = await read(path: readPath, password: password); + final jsonSource = await encryptionFileUtils.read(path: readPath, password: password); final data = json.decode(jsonSource) as Map<String, dynamic>; return WalletKeysData.fromJSON(data); } catch (e) { @@ -72,12 +81,12 @@ mixin WalletKeysFile<BalanceType extends Balance, HistoryType extends Transactio if (!File(readPath).existsSync()) throw Exception("No .keys nor a .keys.backup file found for $name $type"); - final jsonSource = await read(path: readPath, password: password); + final jsonSource = await encryptionFileUtils.read(path: readPath, password: password); final data = json.decode(jsonSource) as Map<String, dynamic>; final keysData = WalletKeysData.fromJSON(data); dev.log("Restoring .keys from .keys.backup"); - createKeysFile(name, type, password, keysData, false); + createKeysFile(name, type, password, keysData, encryptionFileUtils, false); return keysData; } } diff --git a/cw_core/pubspec.lock b/cw_core/pubspec.lock index 518c71b94..e905af2d9 100644 --- a/cw_core/pubspec.lock +++ b/cw_core/pubspec.lock @@ -113,6 +113,15 @@ packages: url: "https://pub.dev" source: hosted version: "8.8.1" + 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: @@ -169,6 +178,22 @@ packages: url: "https://pub.dev" source: hosted version: "3.0.3" + cryptography: + dependency: transitive + description: + name: cryptography + sha256: df156c5109286340817d21fa7b62f9140f17915077127dd70f8bd7a2a0997a35 + url: "https://pub.dev" + source: hosted + version: "2.5.0" + cupertino_icons: + dependency: transitive + description: + name: cupertino_icons + sha256: d57953e10f9f8327ce64a508a355f0b1ec902193f66288e8cb5070e7c47eeb2d + url: "https://pub.dev" + source: hosted + version: "1.0.6" dart_style: dependency: transitive description: @@ -648,6 +673,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.1" + tuple: + dependency: transitive + description: + name: tuple + sha256: a97ce2013f240b2f3807bcbaf218765b6f301c3eff91092bcfa23a039e7dd151 + url: "https://pub.dev" + source: hosted + version: "2.0.2" typed_data: dependency: transitive description: diff --git a/cw_core/pubspec.yaml b/cw_core/pubspec.yaml index 0513b122c..4497a709d 100644 --- a/cw_core/pubspec.yaml +++ b/cw_core/pubspec.yaml @@ -19,6 +19,11 @@ dependencies: flutter_mobx: ^2.0.6+1 intl: ^0.18.0 encrypt: ^5.0.1 + cake_backup: + git: + url: https://github.com/cake-tech/cake_backup.git + ref: main + version: 1.0.0 socks5_proxy: ^1.0.4 unorm_dart: ^0.3.0 # tor: diff --git a/cw_ethereum/lib/ethereum_transaction_history.dart b/cw_ethereum/lib/ethereum_transaction_history.dart index f774ae905..fbb8ab79d 100644 --- a/cw_ethereum/lib/ethereum_transaction_history.dart +++ b/cw_ethereum/lib/ethereum_transaction_history.dart @@ -7,6 +7,7 @@ class EthereumTransactionHistory extends EVMChainTransactionHistory { EthereumTransactionHistory({ required super.walletInfo, required super.password, + required super.encryptionFileUtils, }); @override diff --git a/cw_ethereum/lib/ethereum_wallet.dart b/cw_ethereum/lib/ethereum_wallet.dart index 7bcd55cf4..51aeab5e1 100644 --- a/cw_ethereum/lib/ethereum_wallet.dart +++ b/cw_ethereum/lib/ethereum_wallet.dart @@ -2,6 +2,7 @@ import 'dart:convert'; import 'package:cw_core/cake_hive.dart'; import 'package:cw_core/crypto_currency.dart'; +import 'package:cw_core/encryption_file_utils.dart'; import 'package:cw_core/erc20_token.dart'; import 'package:cw_core/pathForWallet.dart'; import 'package:cw_core/transaction_direction.dart'; @@ -16,7 +17,6 @@ import 'package:cw_evm/evm_chain_transaction_info.dart'; import 'package:cw_evm/evm_chain_transaction_model.dart'; import 'package:cw_evm/evm_chain_wallet.dart'; import 'package:cw_evm/evm_erc20_balance.dart'; -import 'package:cw_evm/file.dart'; class EthereumWallet extends EVMChainWallet { EthereumWallet({ @@ -26,6 +26,7 @@ class EthereumWallet extends EVMChainWallet { super.mnemonic, super.initialBalance, super.privateKey, + required super.encryptionFileUtils, }) : super(nativeCurrency: CryptoCurrency.eth); @override @@ -117,18 +118,24 @@ class EthereumWallet extends EVMChainWallet { } @override - EVMChainTransactionHistory setUpTransactionHistory(WalletInfo walletInfo, String password) { - return EthereumTransactionHistory(walletInfo: walletInfo, password: password); + EVMChainTransactionHistory setUpTransactionHistory( + WalletInfo walletInfo, String password, EncryptionFileUtils encryptionFileUtils) { + return EthereumTransactionHistory( + walletInfo: walletInfo, password: password, encryptionFileUtils: encryptionFileUtils); } - static Future<EthereumWallet> open( - {required String name, required String password, required WalletInfo walletInfo}) async { + static Future<EthereumWallet> open({ + required String name, + required String password, + required WalletInfo walletInfo, + required EncryptionFileUtils encryptionFileUtils, + }) async { final hasKeysFile = await WalletKeysFile.hasKeysFile(name, walletInfo.type); final path = await pathForWallet(name: name, type: walletInfo.type); Map<String, dynamic>? data; try { - final jsonSource = await read(path: path, password: password); + final jsonSource = await encryptionFileUtils.read(path: path, password: password); data = json.decode(jsonSource) as Map<String, dynamic>; } catch (e) { @@ -146,7 +153,12 @@ class EthereumWallet extends EVMChainWallet { keysData = WalletKeysData(mnemonic: mnemonic, privateKey: privateKey); } else { - keysData = await WalletKeysFile.readKeysFile(name, walletInfo.type, password); + keysData = await WalletKeysFile.readKeysFile( + name, + walletInfo.type, + password, + encryptionFileUtils, + ); } return EthereumWallet( @@ -156,6 +168,7 @@ class EthereumWallet extends EVMChainWallet { privateKey: keysData.privateKey, initialBalance: balance, client: EthereumClient(), + encryptionFileUtils: encryptionFileUtils, ); } } diff --git a/cw_ethereum/lib/ethereum_wallet_service.dart b/cw_ethereum/lib/ethereum_wallet_service.dart index c0d3df2d6..84fc0a277 100644 --- a/cw_ethereum/lib/ethereum_wallet_service.dart +++ b/cw_ethereum/lib/ethereum_wallet_service.dart @@ -1,4 +1,5 @@ import 'package:bip39/bip39.dart' as bip39; +import 'package:cw_core/encryption_file_utils.dart'; import 'package:cw_core/wallet_base.dart'; import 'package:cw_core/wallet_info.dart'; import 'package:cw_core/wallet_type.dart'; @@ -9,7 +10,7 @@ import 'package:cw_evm/evm_chain_wallet_creation_credentials.dart'; import 'package:cw_evm/evm_chain_wallet_service.dart'; class EthereumWalletService extends EVMChainWalletService<EthereumWallet> { - EthereumWalletService(super.walletInfoSource, {required this.client}); + EthereumWalletService(super.walletInfoSource, super.isDirect, {required this.client}); late EthereumClient client; @@ -27,6 +28,7 @@ class EthereumWalletService extends EVMChainWalletService<EthereumWallet> { mnemonic: mnemonic, password: credentials.password!, client: client, + encryptionFileUtils: encryptionFileUtilsFor(isDirect), ); await wallet.init(); @@ -46,6 +48,7 @@ class EthereumWalletService extends EVMChainWalletService<EthereumWallet> { name: name, password: password, walletInfo: walletInfo, + encryptionFileUtils: encryptionFileUtilsFor(isDirect), ); await wallet.init(); @@ -59,6 +62,7 @@ class EthereumWalletService extends EVMChainWalletService<EthereumWallet> { name: name, password: password, walletInfo: walletInfo, + encryptionFileUtils: encryptionFileUtilsFor(isDirect), ); await wallet.init(); await wallet.save(); @@ -71,7 +75,11 @@ class EthereumWalletService extends EVMChainWalletService<EthereumWallet> { final currentWalletInfo = walletInfoSource.values .firstWhere((info) => info.id == WalletBase.idFor(currentName, getType())); final currentWallet = await EthereumWallet.open( - password: password, name: currentName, walletInfo: currentWalletInfo); + password: password, + name: currentName, + walletInfo: currentWalletInfo, + encryptionFileUtils: encryptionFileUtilsFor(isDirect), + ); await currentWallet.renameWalletFiles(newName); await saveBackup(newName); @@ -97,6 +105,7 @@ class EthereumWalletService extends EVMChainWalletService<EthereumWallet> { walletInfo: credentials.walletInfo!, password: credentials.password!, client: client, + encryptionFileUtils: encryptionFileUtilsFor(isDirect), ); await wallet.init(); @@ -114,6 +123,7 @@ class EthereumWalletService extends EVMChainWalletService<EthereumWallet> { privateKey: credentials.privateKey, walletInfo: credentials.walletInfo!, client: client, + encryptionFileUtils: encryptionFileUtilsFor(isDirect), ); await wallet.init(); @@ -135,6 +145,7 @@ class EthereumWalletService extends EVMChainWalletService<EthereumWallet> { mnemonic: credentials.mnemonic, walletInfo: credentials.walletInfo!, client: client, + encryptionFileUtils: encryptionFileUtilsFor(isDirect), ); await wallet.init(); diff --git a/cw_evm/lib/evm_chain_transaction_history.dart b/cw_evm/lib/evm_chain_transaction_history.dart index 2f5c31e82..c4d91783f 100644 --- a/cw_evm/lib/evm_chain_transaction_history.dart +++ b/cw_evm/lib/evm_chain_transaction_history.dart @@ -1,10 +1,10 @@ import 'dart:convert'; import 'dart:core'; import 'dart:developer'; +import 'package:cw_core/encryption_file_utils.dart'; import 'package:cw_core/pathForWallet.dart'; import 'package:cw_core/wallet_info.dart'; import 'package:cw_evm/evm_chain_transaction_info.dart'; -import 'package:cw_evm/file.dart'; import 'package:mobx/mobx.dart'; import 'package:cw_core/transaction_history.dart'; @@ -15,7 +15,8 @@ abstract class EVMChainTransactionHistory = EVMChainTransactionHistoryBase abstract class EVMChainTransactionHistoryBase extends TransactionHistoryBase<EVMChainTransactionInfo> with Store { - EVMChainTransactionHistoryBase({required this.walletInfo, required String password}) + EVMChainTransactionHistoryBase( + {required this.walletInfo, required String password, required this.encryptionFileUtils}) : _password = password { transactions = ObservableMap<String, EVMChainTransactionInfo>(); } @@ -23,6 +24,7 @@ abstract class EVMChainTransactionHistoryBase String _password; final WalletInfo walletInfo; + final EncryptionFileUtils encryptionFileUtils; //! Method to be overridden by all child classes @@ -41,7 +43,7 @@ abstract class EVMChainTransactionHistoryBase final dirPath = await pathForWalletDir(name: walletInfo.name, type: walletInfo.type); String path = '$dirPath/$transactionsHistoryFileNameForWallet'; 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) { log('Error while saving ${walletInfo.type.name} transaction history: ${e.toString()}'); log(s.toString()); @@ -59,7 +61,7 @@ abstract class EVMChainTransactionHistoryBase final transactionsHistoryFileNameForWallet = getTransactionHistoryFileName(); final dirPath = await pathForWalletDir(name: walletInfo.name, type: walletInfo.type); String path = '$dirPath/$transactionsHistoryFileNameForWallet'; - 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_evm/lib/evm_chain_wallet.dart b/cw_evm/lib/evm_chain_wallet.dart index 55dcea959..80a366e6f 100644 --- a/cw_evm/lib/evm_chain_wallet.dart +++ b/cw_evm/lib/evm_chain_wallet.dart @@ -7,6 +7,7 @@ import 'package:bip32/bip32.dart' as bip32; import 'package:bip39/bip39.dart' as bip39; import 'package:cw_core/cake_hive.dart'; import 'package:cw_core/crypto_currency.dart'; +import 'package:cw_core/encryption_file_utils.dart'; import 'package:cw_core/erc20_token.dart'; import 'package:cw_core/node.dart'; import 'package:cw_core/pathForWallet.dart'; @@ -27,7 +28,6 @@ import 'package:cw_evm/evm_chain_transaction_model.dart'; import 'package:cw_evm/evm_chain_transaction_priority.dart'; import 'package:cw_evm/evm_chain_wallet_addresses.dart'; import 'package:cw_evm/evm_ledger_credentials.dart'; -import 'package:cw_evm/file.dart'; import 'package:flutter/foundation.dart'; import 'package:hex/hex.dart'; import 'package:hive/hive.dart'; @@ -68,6 +68,7 @@ abstract class EVMChainWalletBase String? privateKey, required String password, EVMChainERC20Balance? initialBalance, + required this.encryptionFileUtils, }) : syncStatus = const NotConnectedSyncStatus(), _password = password, _mnemonic = mnemonic, @@ -83,7 +84,7 @@ abstract class EVMChainWalletBase ), super(walletInfo) { this.walletInfo = walletInfo; - transactionHistory = setUpTransactionHistory(walletInfo, password); + transactionHistory = setUpTransactionHistory(walletInfo, password, encryptionFileUtils); if (!CakeHive.isAdapterRegistered(Erc20Token.typeId)) { CakeHive.registerAdapter(Erc20TokenAdapter()); @@ -95,6 +96,7 @@ abstract class EVMChainWalletBase final String? _mnemonic; final String? _hexPrivateKey; final String _password; + final EncryptionFileUtils encryptionFileUtils; late final Box<Erc20Token> erc20TokensBox; @@ -149,7 +151,11 @@ abstract class EVMChainWalletBase Erc20Token createNewErc20TokenObject(Erc20Token token, String? iconPath); - EVMChainTransactionHistory setUpTransactionHistory(WalletInfo walletInfo, String password); + EVMChainTransactionHistory setUpTransactionHistory( + WalletInfo walletInfo, + String password, + EncryptionFileUtils encryptionFileUtils, + ); //! Common Methods across child classes @@ -510,13 +516,13 @@ abstract class EVMChainWalletBase @override Future<void> save() async { if (!(await WalletKeysFile.hasKeysFile(walletInfo.name, walletInfo.type))) { - await saveKeysFile(_password); - saveKeysFile(_password, true); + await saveKeysFile(_password, encryptionFileUtils); + saveKeysFile(_password, encryptionFileUtils, true); } 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(); } @@ -690,4 +696,7 @@ abstract class EVMChainWalletBase bytesToHex(await _evmChainPrivateKey.signPersonalMessage(ascii.encode(message))); Web3Client? getWeb3Client() => _client.getWeb3Client(); + + @override + String get password => _password; } diff --git a/cw_evm/lib/evm_chain_wallet_creation_credentials.dart b/cw_evm/lib/evm_chain_wallet_creation_credentials.dart index be763bac7..e8a13cbb9 100644 --- a/cw_evm/lib/evm_chain_wallet_creation_credentials.dart +++ b/cw_evm/lib/evm_chain_wallet_creation_credentials.dart @@ -3,8 +3,8 @@ import 'package:cw_core/wallet_credentials.dart'; import 'package:cw_core/wallet_info.dart'; class EVMChainNewWalletCredentials extends WalletCredentials { - EVMChainNewWalletCredentials({required String name, WalletInfo? walletInfo}) - : super(name: name, walletInfo: walletInfo); + EVMChainNewWalletCredentials({required String name, WalletInfo? walletInfo, String? password}) + : super(name: name, walletInfo: walletInfo, password: password); } class EVMChainRestoreWalletFromSeedCredentials extends WalletCredentials { diff --git a/cw_evm/lib/evm_chain_wallet_service.dart b/cw_evm/lib/evm_chain_wallet_service.dart index 2bbe6bd47..e6bb41b86 100644 --- a/cw_evm/lib/evm_chain_wallet_service.dart +++ b/cw_evm/lib/evm_chain_wallet_service.dart @@ -15,9 +15,10 @@ abstract class EVMChainWalletService<T extends EVMChainWallet> extends WalletSer EVMChainRestoreWalletFromSeedCredentials, EVMChainRestoreWalletFromPrivateKey, EVMChainRestoreWalletFromHardware> { - EVMChainWalletService(this.walletInfoSource); + EVMChainWalletService(this.walletInfoSource, this.isDirect); final Box<WalletInfo> walletInfoSource; + final bool isDirect; @override WalletType getType(); diff --git a/cw_evm/lib/file.dart b/cw_evm/lib/file.dart deleted file mode 100644 index 8fd236ec3..000000000 --- a/cw_evm/lib/file.dart +++ /dev/null @@ -1,39 +0,0 @@ -import 'dart:io'; -import 'package:cw_core/key.dart'; -import 'package:encrypt/encrypt.dart' as encrypt; - -Future<void> write( - {required String path, - required String password, - required String data}) async { - final keys = extractKeys(password); - final key = encrypt.Key.fromBase64(keys.first); - final iv = encrypt.IV.fromBase64(keys.last); - final encrypted = await encode(key: key, iv: iv, data: data); - final f = File(path); - f.writeAsStringSync(encrypted); -} - -Future<void> writeData( - {required String path, - required String password, - required String data}) async { - final keys = extractKeys(password); - final key = encrypt.Key.fromBase64(keys.first); - final iv = encrypt.IV.fromBase64(keys.last); - final encrypted = await encode(key: key, iv: iv, data: data); - final f = File(path); - f.writeAsStringSync(encrypted); -} - -Future<String> read({required String path, required String password}) async { - final file = File(path); - - if (!file.existsSync()) { - file.createSync(); - } - - final encrypted = file.readAsStringSync(); - - return decode(password: password, data: encrypted); -} diff --git a/cw_evm/pubspec.yaml b/cw_evm/pubspec.yaml index c3f4347c2..b24e375a7 100644 --- a/cw_evm/pubspec.yaml +++ b/cw_evm/pubspec.yaml @@ -20,6 +20,7 @@ dependencies: hive: ^2.2.3 collection: ^1.17.1 shared_preferences: ^2.0.15 + mobx: ^2.0.7+4 cw_core: path: ../cw_core ledger_flutter: ^1.0.1 diff --git a/cw_haven/lib/haven_wallet.dart b/cw_haven/lib/haven_wallet.dart index e639be4b9..c0ecbca68 100644 --- a/cw_haven/lib/haven_wallet.dart +++ b/cw_haven/lib/haven_wallet.dart @@ -38,9 +38,10 @@ class HavenWallet = HavenWalletBase with _$HavenWallet; abstract class HavenWalletBase extends WalletBase<MoneroBalance, HavenTransactionHistory, HavenTransactionInfo> with Store { - HavenWalletBase({required WalletInfo walletInfo}) + HavenWalletBase({required WalletInfo walletInfo, String? password}) : balance = ObservableMap.of(getHavenBalance(accountIndex: 0)), _isTransactionUpdating = false, + _password = password ?? '', _hasSyncAfterStartup = false, walletAddresses = HavenWalletAddresses(walletInfo), syncStatus = NotConnectedSyncStatus(), @@ -56,6 +57,7 @@ abstract class HavenWalletBase } static const int _autoSaveInterval = 30; + final String _password; @override HavenWalletAddresses walletAddresses; @@ -111,7 +113,7 @@ abstract class HavenWalletBase _onAccountChangeReaction?.reaction.dispose(); _autoSaveTimer?.cancel(); } - + @override Future<void> connectToNode({required Node node}) async { try { @@ -414,4 +416,7 @@ abstract class HavenWalletBase print(e.toString()); } } + + @override + String get password => _password; } diff --git a/cw_haven/pubspec.lock b/cw_haven/pubspec.lock index c34e164f4..6e840224c 100644 --- a/cw_haven/pubspec.lock +++ b/cw_haven/pubspec.lock @@ -113,6 +113,15 @@ packages: url: "https://pub.dev" source: hosted version: "8.4.3" + cake_backup: + dependency: transitive + 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: @@ -169,6 +178,22 @@ packages: url: "https://pub.dev" source: hosted version: "3.0.2" + cryptography: + dependency: transitive + description: + name: cryptography + sha256: df156c5109286340817d21fa7b62f9140f17915077127dd70f8bd7a2a0997a35 + url: "https://pub.dev" + source: hosted + version: "2.5.0" + cupertino_icons: + dependency: transitive + description: + name: cupertino_icons + sha256: d57953e10f9f8327ce64a508a355f0b1ec902193f66288e8cb5070e7c47eeb2d + url: "https://pub.dev" + source: hosted + version: "1.0.6" cw_core: dependency: "direct main" description: @@ -639,6 +664,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.1" + tuple: + dependency: transitive + description: + name: tuple + sha256: a97ce2013f240b2f3807bcbaf218765b6f301c3eff91092bcfa23a039e7dd151 + url: "https://pub.dev" + source: hosted + version: "2.0.2" typed_data: dependency: transitive description: 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/lib/api/account_list.dart b/cw_monero/lib/api/account_list.dart index 199896631..7cb95a507 100644 --- a/cw_monero/lib/api/account_list.dart +++ b/cw_monero/lib/api/account_list.dart @@ -34,7 +34,6 @@ List<monero.SubaddressAccountRow> getAllAccount() { // final size = monero.Wallet_numSubaddressAccounts(wptr!); refreshAccounts(); int size = monero.SubaddressAccount_getAll_size(subaddressAccount!); - print("size: $size"); if (size == 0) { monero.Wallet_addSubaddressAccount(wptr!); return getAllAccount(); diff --git a/cw_monero/lib/api/wallet_manager.dart b/cw_monero/lib/api/wallet_manager.dart index 26c83b06e..14bf92d16 100644 --- a/cw_monero/lib/api/wallet_manager.dart +++ b/cw_monero/lib/api/wallet_manager.dart @@ -7,6 +7,8 @@ import 'package:cw_monero/api/exceptions/wallet_creation_exception.dart'; import 'package:cw_monero/api/exceptions/wallet_opening_exception.dart'; import 'package:cw_monero/api/exceptions/wallet_restore_from_keys_exception.dart'; import 'package:cw_monero/api/exceptions/wallet_restore_from_seed_exception.dart'; +import 'package:cw_monero/api/wallet.dart'; +import 'package:flutter/foundation.dart'; import 'package:cw_monero/api/transaction_history.dart'; import 'package:cw_monero/api/wallet.dart'; import 'package:flutter/foundation.dart'; diff --git a/cw_monero/lib/monero_wallet.dart b/cw_monero/lib/monero_wallet.dart index 9298f8a49..31e09ca2d 100644 --- a/cw_monero/lib/monero_wallet.dart +++ b/cw_monero/lib/monero_wallet.dart @@ -3,6 +3,8 @@ import 'dart:ffi'; import 'dart:io'; import 'dart:isolate'; +import 'package:cw_core/pathForWallet.dart'; +import 'package:cw_core/transaction_priority.dart'; import 'package:cw_core/account.dart'; import 'package:cw_core/crypto_currency.dart'; import 'package:cw_core/monero_amount_format.dart'; @@ -50,7 +52,8 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance, MoneroTransactionHistory, MoneroTransactionInfo> with Store { MoneroWalletBase( {required WalletInfo walletInfo, - required Box<UnspentCoinsInfo> unspentCoinsInfo}) + required Box<UnspentCoinsInfo> unspentCoinsInfo, + required String password}) : balance = ObservableMap<CryptoCurrency, MoneroBalance>.of({ CryptoCurrency.xmr: MoneroBalance( fullBalance: monero_wallet.getFullBalance(accountIndex: 0), @@ -59,6 +62,7 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance, _isTransactionUpdating = false, _hasSyncAfterStartup = false, isEnabledAutoGenerateSubaddress = false, + _password = password, syncStatus = NotConnectedSyncStatus(), unspentCoins = [], this.unspentCoinsInfo = unspentCoinsInfo, @@ -111,6 +115,9 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance, String get seed => monero_wallet.getSeed(); String seedLegacy(String? language) => monero_wallet.getSeedLegacy(language); + @override + String get password => _password; + @override MoneroWalletKeys get keys => MoneroWalletKeys( privateSpendKey: monero_wallet.getSecretSpendKey(), @@ -127,6 +134,7 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance, bool _hasSyncAfterStartup; Timer? _autoSaveTimer; List<MoneroUnspent> unspentCoins; + String _password; Future<void> init() async { await walletAddresses.init(); diff --git a/cw_monero/lib/monero_wallet_service.dart b/cw_monero/lib/monero_wallet_service.dart index 3588ebb78..d771d1815 100644 --- a/cw_monero/lib/monero_wallet_service.dart +++ b/cw_monero/lib/monero_wallet_service.dart @@ -93,7 +93,9 @@ class MoneroWalletService extends WalletService< await monero_wallet_manager.createWallet( path: path, password: credentials.password!, language: credentials.language); final wallet = MoneroWallet( - walletInfo: credentials.walletInfo!, unspentCoinsInfo: unspentCoinsInfoSource); + walletInfo: credentials.walletInfo!, + unspentCoinsInfo: unspentCoinsInfoSource, + password: credentials.password!); await wallet.init(); return wallet; @@ -126,10 +128,14 @@ class MoneroWalletService extends WalletService< await repairOldAndroidWallet(name); } - await monero_wallet_manager.openWalletAsync({'path': path, 'password': password}); - final walletInfo = walletInfoSource.values - .firstWhere((info) => info.id == WalletBase.idFor(name, getType())); - wallet = MoneroWallet(walletInfo: walletInfo, unspentCoinsInfo: unspentCoinsInfoSource); + await monero_wallet_manager + .openWalletAsync({'path': path, 'password': password}); + final walletInfo = walletInfoSource.values.firstWhere( + (info) => info.id == WalletBase.idFor(name, getType())); + final wallet = MoneroWallet( + walletInfo: walletInfo, + unspentCoinsInfo: unspentCoinsInfoSource, + password: password); final isValid = wallet.walletAddresses.validate(); if (!isValid) { @@ -162,15 +168,22 @@ class MoneroWalletService extends WalletService< final bool invalidSignature = e.toString().contains('invalid signature') || (e is WalletOpeningException && e.message.contains('invalid signature')); + final bool invalidPassword = e.toString().contains('invalid password') || + (e is WalletOpeningException && e.message.contains('invalid password')); + if (!isBadAlloc && !doesNotCorrespond && !isMissingCacheFilesIOS && !isMissingCacheFilesAndroid && !invalidSignature && + !invalidPassword && wallet != null && wallet.onError != null) { wallet.onError!(FlutterErrorDetails(exception: e, stack: s)); } + if (invalidPassword) { + rethrow; + } await restoreOrResetWalletFiles(name); return openWallet(name, password); @@ -206,11 +219,15 @@ class MoneroWalletService extends WalletService< } @override - Future<void> rename(String currentName, String password, String newName) async { - final currentWalletInfo = walletInfoSource.values - .firstWhere((info) => info.id == WalletBase.idFor(currentName, getType())); - final currentWallet = - MoneroWallet(walletInfo: currentWalletInfo, unspentCoinsInfo: unspentCoinsInfoSource); + Future<void> rename( + String currentName, String password, String newName) async { + final currentWalletInfo = walletInfoSource.values.firstWhere( + (info) => info.id == WalletBase.idFor(currentName, getType())); + final currentWallet = MoneroWallet( + walletInfo: currentWalletInfo, + unspentCoinsInfo: unspentCoinsInfoSource, + password: password, + ); await currentWallet.renameWalletFiles(newName); @@ -235,7 +252,9 @@ class MoneroWalletService extends WalletService< viewKey: credentials.viewKey, spendKey: credentials.spendKey); final wallet = MoneroWallet( - walletInfo: credentials.walletInfo!, unspentCoinsInfo: unspentCoinsInfoSource); + walletInfo: credentials.walletInfo!, + unspentCoinsInfo: unspentCoinsInfoSource, + password: credentials.password!); await wallet.init(); return wallet; @@ -268,7 +287,9 @@ class MoneroWalletService extends WalletService< seed: credentials.mnemonic, restoreHeight: credentials.height!); final wallet = MoneroWallet( - walletInfo: credentials.walletInfo!, unspentCoinsInfo: unspentCoinsInfoSource); + walletInfo: credentials.walletInfo!, + unspentCoinsInfo: unspentCoinsInfoSource, + password: credentials.password!); await wallet.init(); return wallet; @@ -315,7 +336,11 @@ class MoneroWalletService extends WalletService< restoreHeight: height, spendKey: spendKey); - final wallet = MoneroWallet(walletInfo: walletInfo, unspentCoinsInfo: unspentCoinsInfoSource); + final wallet = MoneroWallet( + walletInfo: walletInfo, + unspentCoinsInfo: unspentCoinsInfoSource, + password: password, + ); await wallet.init(); return wallet; @@ -364,7 +389,11 @@ class MoneroWalletService extends WalletService< await monero_wallet_manager.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, + ); return wallet.seed; } catch (_) { // if the file couldn't be opened or read 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/flutter/generated_plugin_registrant.cc b/cw_monero/linux/flutter/generated_plugin_registrant.cc new file mode 100644 index 000000000..e71a16d23 --- /dev/null +++ b/cw_monero/linux/flutter/generated_plugin_registrant.cc @@ -0,0 +1,11 @@ +// +// Generated file. Do not edit. +// + +// clang-format off + +#include "generated_plugin_registrant.h" + + +void fl_register_plugins(FlPluginRegistry* registry) { +} diff --git a/cw_monero/linux/flutter/generated_plugin_registrant.h b/cw_monero/linux/flutter/generated_plugin_registrant.h new file mode 100644 index 000000000..e0f0a47bc --- /dev/null +++ b/cw_monero/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/linux/flutter/generated_plugins.cmake b/cw_monero/linux/flutter/generated_plugins.cmake new file mode 100644 index 000000000..2e1de87a7 --- /dev/null +++ b/cw_monero/linux/flutter/generated_plugins.cmake @@ -0,0 +1,23 @@ +# +# Generated file, do not edit. +# + +list(APPEND FLUTTER_PLUGIN_LIST +) + +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/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/pubspec.lock b/cw_monero/pubspec.lock index 38299b2dc..07c3b8876 100644 --- a/cw_monero/pubspec.lock +++ b/cw_monero/pubspec.lock @@ -113,6 +113,15 @@ packages: url: "https://pub.dev" source: hosted version: "8.9.2" + cake_backup: + dependency: transitive + 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: @@ -169,6 +178,22 @@ packages: url: "https://pub.dev" source: hosted version: "3.0.3" + cryptography: + dependency: transitive + description: + name: cryptography + sha256: d146b76d33d94548cf035233fbc2f4338c1242fa119013bead807d033fc4ae05 + url: "https://pub.dev" + source: hosted + version: "2.7.0" + cupertino_icons: + dependency: transitive + description: + name: cupertino_icons + sha256: ba631d1c7f7bef6b729a622b7b752645a2d076dba9976925b8f25725a30e1ee6 + url: "https://pub.dev" + source: hosted + version: "1.0.8" cw_core: dependency: "direct main" description: @@ -342,10 +367,10 @@ packages: dependency: transitive description: name: js - sha256: c1b2e9b5ea78c45e1a0788d29606ba27dc5f71f019f32ca5140f61ef071838cf + sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3 url: "https://pub.dev" source: hosted - version: "0.7.1" + version: "0.6.7" json_annotation: dependency: transitive description: @@ -696,6 +721,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.1" + tuple: + dependency: transitive + description: + name: tuple + sha256: a97ce2013f240b2f3807bcbaf218765b6f301c3eff91092bcfa23a039e7dd151 + url: "https://pub.dev" + source: hosted + version: "2.0.2" typed_data: dependency: transitive description: diff --git a/cw_nano/lib/file.dart b/cw_nano/lib/file.dart deleted file mode 100644 index 8fd236ec3..000000000 --- a/cw_nano/lib/file.dart +++ /dev/null @@ -1,39 +0,0 @@ -import 'dart:io'; -import 'package:cw_core/key.dart'; -import 'package:encrypt/encrypt.dart' as encrypt; - -Future<void> write( - {required String path, - required String password, - required String data}) async { - final keys = extractKeys(password); - final key = encrypt.Key.fromBase64(keys.first); - final iv = encrypt.IV.fromBase64(keys.last); - final encrypted = await encode(key: key, iv: iv, data: data); - final f = File(path); - f.writeAsStringSync(encrypted); -} - -Future<void> writeData( - {required String path, - required String password, - required String data}) async { - final keys = extractKeys(password); - final key = encrypt.Key.fromBase64(keys.first); - final iv = encrypt.IV.fromBase64(keys.last); - final encrypted = await encode(key: key, iv: iv, data: data); - final f = File(path); - f.writeAsStringSync(encrypted); -} - -Future<String> read({required String path, required String password}) async { - final file = File(path); - - if (!file.existsSync()) { - file.createSync(); - } - - final encrypted = file.readAsStringSync(); - - return decode(password: password, data: encrypted); -} diff --git a/cw_nano/lib/nano_transaction_history.dart b/cw_nano/lib/nano_transaction_history.dart index dadd353c4..44d64f7d4 100644 --- a/cw_nano/lib/nano_transaction_history.dart +++ b/cw_nano/lib/nano_transaction_history.dart @@ -2,24 +2,29 @@ import 'dart:convert'; import 'dart:core'; import 'package:cw_core/pathForWallet.dart'; import 'package:cw_core/wallet_info.dart'; -import 'package:cw_nano/file.dart'; +import 'package:cw_core/encryption_file_utils.dart'; import 'package:mobx/mobx.dart'; import 'package:cw_core/transaction_history.dart'; import 'package:cw_nano/nano_transaction_info.dart'; part 'nano_transaction_history.g.dart'; + const transactionsHistoryFileName = 'transactions.json'; class NanoTransactionHistory = NanoTransactionHistoryBase with _$NanoTransactionHistory; -abstract class NanoTransactionHistoryBase - extends TransactionHistoryBase<NanoTransactionInfo> with Store { - NanoTransactionHistoryBase({required this.walletInfo, required String password}) - : _password = password { +abstract class NanoTransactionHistoryBase extends TransactionHistoryBase<NanoTransactionInfo> + with Store { + NanoTransactionHistoryBase({ + required this.walletInfo, + required String password, + required this.encryptionFileUtils, + }) : _password = password { transactions = ObservableMap<String, NanoTransactionInfo>(); } final WalletInfo walletInfo; + final EncryptionFileUtils encryptionFileUtils; String _password; Future<void> init() async => await _load(); @@ -30,7 +35,7 @@ abstract class NanoTransactionHistoryBase 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) { print('Error while save nano transaction history: ${e.toString()}'); } @@ -46,7 +51,10 @@ abstract class NanoTransactionHistoryBase 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 {}; + } return json.decode(content) as Map<String, dynamic>; } diff --git a/cw_nano/lib/nano_wallet.dart b/cw_nano/lib/nano_wallet.dart index 55e01d10b..cba8d09a0 100644 --- a/cw_nano/lib/nano_wallet.dart +++ b/cw_nano/lib/nano_wallet.dart @@ -5,6 +5,7 @@ import 'dart:io'; import 'package:bip39/bip39.dart' as bip39; import 'package:cw_core/cake_hive.dart'; import 'package:cw_core/crypto_currency.dart'; +import 'package:cw_core/encryption_file_utils.dart'; import 'package:cw_core/n2_node.dart'; import 'package:cw_core/nano_account.dart'; import 'package:cw_core/nano_account_info_response.dart'; @@ -17,7 +18,6 @@ import 'package:cw_core/transaction_priority.dart'; import 'package:cw_core/wallet_base.dart'; import 'package:cw_core/wallet_info.dart'; import 'package:cw_core/wallet_keys_file.dart'; -import 'package:cw_nano/file.dart'; import 'package:cw_nano/nano_balance.dart'; import 'package:cw_nano/nano_client.dart'; import 'package:cw_nano/nano_transaction_credentials.dart'; @@ -42,11 +42,13 @@ abstract class NanoWalletBase required String mnemonic, required String password, NanoBalance? initialBalance, + required EncryptionFileUtils encryptionFileUtils, }) : syncStatus = NotConnectedSyncStatus(), _password = password, _mnemonic = mnemonic, _derivationType = walletInfo.derivationInfo!.derivationType!, _isTransactionUpdating = false, + _encryptionFileUtils = encryptionFileUtils, _client = NanoClient(), walletAddresses = NanoWalletAddresses(walletInfo), balance = ObservableMap<CryptoCurrency, NanoBalance>.of({ @@ -55,7 +57,11 @@ abstract class NanoWalletBase }), super(walletInfo) { this.walletInfo = walletInfo; - transactionHistory = NanoTransactionHistory(walletInfo: walletInfo, password: password); + transactionHistory = NanoTransactionHistory( + walletInfo: walletInfo, + password: password, + encryptionFileUtils: encryptionFileUtils, + ); if (!CakeHive.isAdapterRegistered(NanoAccount.typeId)) { CakeHive.registerAdapter(NanoAccountAdapter()); } @@ -65,6 +71,8 @@ abstract class NanoWalletBase final String _password; DerivationType _derivationType; + final EncryptionFileUtils _encryptionFileUtils; + String? _privateKey; String? _publicAddress; String? _hexSeed; @@ -89,6 +97,9 @@ abstract class NanoWalletBase @observable late ObservableMap<CryptoCurrency, NanoBalance> balance; + @override + String get password => _password; + static const int POLL_INTERVAL_SECONDS = 10; // initialize the different forms of private / public key we'll need: @@ -308,13 +319,13 @@ abstract class NanoWalletBase @override Future<void> save() async { if (!(await WalletKeysFile.hasKeysFile(walletInfo.name, walletInfo.type))) { - await saveKeysFile(_password); - saveKeysFile(_password, true); + await saveKeysFile(_password, _encryptionFileUtils); + saveKeysFile(_password, _encryptionFileUtils, true); } 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(); } @@ -373,13 +384,14 @@ abstract class NanoWalletBase required String name, required String password, required WalletInfo walletInfo, + required EncryptionFileUtils encryptionFileUtils, }) async { final hasKeysFile = await WalletKeysFile.hasKeysFile(name, walletInfo.type); final path = await pathForWallet(name: name, type: walletInfo.type); Map<String, dynamic>? data = null; try { - final jsonSource = await read(path: path, password: password); + final jsonSource = await encryptionFileUtils.read(path: path, password: password); data = json.decode(jsonSource) as Map<String, dynamic>; } catch (e) { @@ -400,7 +412,12 @@ abstract class NanoWalletBase keysData = WalletKeysData( mnemonic: isHexSeed ? null : mnemonic, altMnemonic: isHexSeed ? mnemonic : null); } else { - keysData = await WalletKeysFile.readKeysFile(name, walletInfo.type, password); + keysData = await WalletKeysFile.readKeysFile( + name, + walletInfo.type, + password, + encryptionFileUtils, + ); } DerivationType derivationType = DerivationType.nano; @@ -416,6 +433,7 @@ abstract class NanoWalletBase password: password, mnemonic: keysData.mnemonic!, initialBalance: balance, + encryptionFileUtils: encryptionFileUtils, ); // init() should always be run after this! } diff --git a/cw_nano/lib/nano_wallet_service.dart b/cw_nano/lib/nano_wallet_service.dart index 755598705..ac3d6581a 100644 --- a/cw_nano/lib/nano_wallet_service.dart +++ b/cw_nano/lib/nano_wallet_service.dart @@ -1,6 +1,7 @@ import 'dart:io'; import 'package:cw_core/pathForWallet.dart'; +import 'package:cw_core/encryption_file_utils.dart'; import 'package:cw_core/wallet_base.dart'; import 'package:cw_core/wallet_info.dart'; import 'package:cw_core/wallet_service.dart'; @@ -15,9 +16,10 @@ import 'package:nanoutil/nanoutil.dart'; class NanoWalletService extends WalletService<NanoNewWalletCredentials, NanoRestoreWalletFromSeedCredentials, NanoRestoreWalletFromKeysCredentials, NanoNewWalletCredentials> { - NanoWalletService(this.walletInfoSource); + NanoWalletService(this.walletInfoSource, this.isDirect); final Box<WalletInfo> walletInfoSource; + final bool isDirect; static bool walletFilesExist(String path) => !File(path).existsSync() && !File('$path.keys').existsSync(); @@ -38,6 +40,7 @@ class NanoWalletService extends WalletService<NanoNewWalletCredentials, walletInfo: credentials.walletInfo!, mnemonic: mnemonic, password: credentials.password!, + encryptionFileUtils: encryptionFileUtilsFor(isDirect), ); await wallet.init(); return wallet; @@ -65,8 +68,12 @@ class NanoWalletService extends WalletService<NanoNewWalletCredentials, String randomWords = (List<String>.from(nm.NanoMnemomics.WORDLIST)..shuffle()).take(24).join(' '); - final currentWallet = - NanoWallet(walletInfo: currentWalletInfo, password: password, mnemonic: randomWords); + final currentWallet = NanoWallet( + walletInfo: currentWalletInfo, + password: password, + mnemonic: randomWords, + encryptionFileUtils: encryptionFileUtilsFor(isDirect), + ); await currentWallet.renameWalletFiles(newName); await saveBackup(newName); @@ -103,6 +110,7 @@ class NanoWalletService extends WalletService<NanoNewWalletCredentials, password: credentials.password!, mnemonic: mnemonic ?? credentials.seedKey, walletInfo: credentials.walletInfo!, + encryptionFileUtils: encryptionFileUtilsFor(isDirect), ); await wallet.init(); await wallet.save(); @@ -139,6 +147,7 @@ class NanoWalletService extends WalletService<NanoNewWalletCredentials, password: credentials.password!, mnemonic: credentials.mnemonic, walletInfo: credentials.walletInfo!, + encryptionFileUtils: encryptionFileUtilsFor(isDirect), ); await wallet.init(); @@ -160,6 +169,7 @@ class NanoWalletService extends WalletService<NanoNewWalletCredentials, name: name, password: password, walletInfo: walletInfo, + encryptionFileUtils: encryptionFileUtilsFor(isDirect), ); await wallet.init(); @@ -172,6 +182,7 @@ class NanoWalletService extends WalletService<NanoNewWalletCredentials, name: name, password: password, walletInfo: walletInfo, + encryptionFileUtils: encryptionFileUtilsFor(isDirect), ); await wallet.init(); diff --git a/cw_nano/pubspec.lock b/cw_nano/pubspec.lock index c3d4b26b6..bbe909199 100644 --- a/cw_nano/pubspec.lock +++ b/cw_nano/pubspec.lock @@ -137,6 +137,15 @@ packages: url: "https://pub.dev" source: hosted version: "8.9.2" + cake_backup: + dependency: transitive + 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: @@ -193,6 +202,22 @@ packages: url: "https://pub.dev" source: hosted version: "3.0.3" + cryptography: + dependency: transitive + description: + name: cryptography + sha256: d146b76d33d94548cf035233fbc2f4338c1242fa119013bead807d033fc4ae05 + url: "https://pub.dev" + source: hosted + version: "2.7.0" + cupertino_icons: + dependency: transitive + description: + name: cupertino_icons + sha256: ba631d1c7f7bef6b729a622b7b752645a2d076dba9976925b8f25725a30e1ee6 + url: "https://pub.dev" + source: hosted + version: "1.0.8" cw_core: dependency: "direct main" description: @@ -797,6 +822,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.1" + tuple: + dependency: transitive + description: + name: tuple + sha256: a97ce2013f240b2f3807bcbaf218765b6f301c3eff91092bcfa23a039e7dd151 + url: "https://pub.dev" + source: hosted + version: "2.0.2" typed_data: dependency: transitive description: diff --git a/cw_polygon/lib/polygon_transaction_history.dart b/cw_polygon/lib/polygon_transaction_history.dart index 8674882cd..27547b7ee 100644 --- a/cw_polygon/lib/polygon_transaction_history.dart +++ b/cw_polygon/lib/polygon_transaction_history.dart @@ -8,6 +8,7 @@ class PolygonTransactionHistory extends EVMChainTransactionHistory { PolygonTransactionHistory({ required super.walletInfo, required super.password, + required super.encryptionFileUtils, }); @override diff --git a/cw_polygon/lib/polygon_wallet.dart b/cw_polygon/lib/polygon_wallet.dart index b0b0793b9..eb59a746e 100644 --- a/cw_polygon/lib/polygon_wallet.dart +++ b/cw_polygon/lib/polygon_wallet.dart @@ -2,6 +2,7 @@ import 'dart:convert'; import 'package:cw_core/cake_hive.dart'; import 'package:cw_core/crypto_currency.dart'; +import 'package:cw_core/encryption_file_utils.dart'; import 'package:cw_core/erc20_token.dart'; import 'package:cw_core/pathForWallet.dart'; import 'package:cw_core/transaction_direction.dart'; @@ -12,7 +13,6 @@ import 'package:cw_evm/evm_chain_transaction_info.dart'; import 'package:cw_evm/evm_chain_transaction_model.dart'; import 'package:cw_evm/evm_chain_wallet.dart'; import 'package:cw_evm/evm_erc20_balance.dart'; -import 'package:cw_evm/file.dart'; import 'package:cw_polygon/default_polygon_erc20_tokens.dart'; import 'package:cw_polygon/polygon_client.dart'; import 'package:cw_polygon/polygon_transaction_history.dart'; @@ -26,6 +26,7 @@ class PolygonWallet extends EVMChainWallet { super.initialBalance, super.privateKey, required super.client, + required super.encryptionFileUtils, }) : super(nativeCurrency: CryptoCurrency.maticpoly); @override @@ -92,18 +93,27 @@ class PolygonWallet extends EVMChainWallet { } @override - EVMChainTransactionHistory setUpTransactionHistory(WalletInfo walletInfo, String password) { - return PolygonTransactionHistory(walletInfo: walletInfo, password: password); + EVMChainTransactionHistory setUpTransactionHistory( + WalletInfo walletInfo, String password, EncryptionFileUtils encryptionFileUtils) { + return PolygonTransactionHistory( + walletInfo: walletInfo, + password: password, + encryptionFileUtils: encryptionFileUtils, + ); } - static Future<PolygonWallet> open( - {required String name, required String password, required WalletInfo walletInfo}) async { + static Future<PolygonWallet> open({ + required String name, + required String password, + required WalletInfo walletInfo, + required EncryptionFileUtils encryptionFileUtils, + }) async { final hasKeysFile = await WalletKeysFile.hasKeysFile(name, walletInfo.type); final path = await pathForWallet(name: name, type: walletInfo.type); Map<String, dynamic>? data; try { - final jsonSource = await read(path: path, password: password); + final jsonSource = await encryptionFileUtils.read(path: path, password: password); data = json.decode(jsonSource) as Map<String, dynamic>; } catch (e) { @@ -121,7 +131,12 @@ class PolygonWallet extends EVMChainWallet { keysData = WalletKeysData(mnemonic: mnemonic, privateKey: privateKey); } else { - keysData = await WalletKeysFile.readKeysFile(name, walletInfo.type, password); + keysData = await WalletKeysFile.readKeysFile( + name, + walletInfo.type, + password, + encryptionFileUtils, + ); } return PolygonWallet( @@ -131,6 +146,7 @@ class PolygonWallet extends EVMChainWallet { privateKey: keysData.privateKey, initialBalance: balance, client: PolygonClient(), + encryptionFileUtils: encryptionFileUtils, ); } } diff --git a/cw_polygon/lib/polygon_wallet_service.dart b/cw_polygon/lib/polygon_wallet_service.dart index 14baffc44..4efc312f7 100644 --- a/cw_polygon/lib/polygon_wallet_service.dart +++ b/cw_polygon/lib/polygon_wallet_service.dart @@ -1,4 +1,5 @@ import 'package:bip39/bip39.dart' as bip39; +import 'package:cw_core/encryption_file_utils.dart'; import 'package:cw_core/wallet_base.dart'; import 'package:cw_core/wallet_info.dart'; import 'package:cw_core/wallet_type.dart'; @@ -10,7 +11,7 @@ import 'package:cw_polygon/polygon_wallet.dart'; class PolygonWalletService extends EVMChainWalletService<PolygonWallet> { PolygonWalletService( - super.walletInfoSource, { + super.walletInfoSource, super.isDirect, { required this.client, }); @@ -30,6 +31,7 @@ class PolygonWalletService extends EVMChainWalletService<PolygonWallet> { mnemonic: mnemonic, password: credentials.password!, client: client, + encryptionFileUtils: encryptionFileUtilsFor(isDirect), ); await wallet.init(); @@ -48,6 +50,7 @@ class PolygonWalletService extends EVMChainWalletService<PolygonWallet> { name: name, password: password, walletInfo: walletInfo, + encryptionFileUtils: encryptionFileUtilsFor(isDirect), ); await wallet.init(); @@ -61,6 +64,7 @@ class PolygonWalletService extends EVMChainWalletService<PolygonWallet> { name: name, password: password, walletInfo: walletInfo, + encryptionFileUtils: encryptionFileUtilsFor(isDirect), ); await wallet.init(); @@ -77,6 +81,7 @@ class PolygonWalletService extends EVMChainWalletService<PolygonWallet> { privateKey: credentials.privateKey, walletInfo: credentials.walletInfo!, client: client, + encryptionFileUtils: encryptionFileUtilsFor(isDirect), ); await wallet.init(); @@ -99,6 +104,7 @@ class PolygonWalletService extends EVMChainWalletService<PolygonWallet> { walletInfo: credentials.walletInfo!, password: credentials.password!, client: client, + encryptionFileUtils: encryptionFileUtilsFor(isDirect), ); await wallet.init(); @@ -120,6 +126,7 @@ class PolygonWalletService extends EVMChainWalletService<PolygonWallet> { mnemonic: credentials.mnemonic, walletInfo: credentials.walletInfo!, client: client, + encryptionFileUtils: encryptionFileUtilsFor(isDirect), ); await wallet.init(); @@ -134,7 +141,11 @@ class PolygonWalletService extends EVMChainWalletService<PolygonWallet> { final currentWalletInfo = walletInfoSource.values .firstWhere((info) => info.id == WalletBase.idFor(currentName, getType())); final currentWallet = await PolygonWallet.open( - password: password, name: currentName, walletInfo: currentWalletInfo); + password: password, + name: currentName, + walletInfo: currentWalletInfo, + encryptionFileUtils: encryptionFileUtilsFor(isDirect), + ); await currentWallet.renameWalletFiles(newName); await saveBackup(newName); diff --git a/cw_solana/lib/file.dart b/cw_solana/lib/file.dart deleted file mode 100644 index 8fd236ec3..000000000 --- a/cw_solana/lib/file.dart +++ /dev/null @@ -1,39 +0,0 @@ -import 'dart:io'; -import 'package:cw_core/key.dart'; -import 'package:encrypt/encrypt.dart' as encrypt; - -Future<void> write( - {required String path, - required String password, - required String data}) async { - final keys = extractKeys(password); - final key = encrypt.Key.fromBase64(keys.first); - final iv = encrypt.IV.fromBase64(keys.last); - final encrypted = await encode(key: key, iv: iv, data: data); - final f = File(path); - f.writeAsStringSync(encrypted); -} - -Future<void> writeData( - {required String path, - required String password, - required String data}) async { - final keys = extractKeys(password); - final key = encrypt.Key.fromBase64(keys.first); - final iv = encrypt.IV.fromBase64(keys.last); - final encrypted = await encode(key: key, iv: iv, data: data); - final f = File(path); - f.writeAsStringSync(encrypted); -} - -Future<String> read({required String path, required String password}) async { - final file = File(path); - - if (!file.existsSync()) { - file.createSync(); - } - - final encrypted = file.readAsStringSync(); - - return decode(password: password, data: encrypted); -} diff --git a/cw_solana/lib/solana_transaction_history.dart b/cw_solana/lib/solana_transaction_history.dart index c03de19ad..77f93b9ee 100644 --- a/cw_solana/lib/solana_transaction_history.dart +++ b/cw_solana/lib/solana_transaction_history.dart @@ -1,8 +1,8 @@ import 'dart:convert'; import 'dart:core'; +import 'package:cw_core/encryption_file_utils.dart'; import 'package:cw_core/pathForWallet.dart'; import 'package:cw_core/wallet_info.dart'; -import 'package:cw_solana/file.dart'; import 'package:cw_solana/solana_transaction_info.dart'; import 'package:mobx/mobx.dart'; import 'package:cw_core/transaction_history.dart'; @@ -15,12 +15,14 @@ class SolanaTransactionHistory = SolanaTransactionHistoryBase with _$SolanaTrans abstract class SolanaTransactionHistoryBase extends TransactionHistoryBase<SolanaTransactionInfo> with Store { - SolanaTransactionHistoryBase({required this.walletInfo, required String password}) + SolanaTransactionHistoryBase( + {required this.walletInfo, required String password, required this.encryptionFileUtils}) : _password = password { transactions = ObservableMap<String, SolanaTransactionInfo>(); } final WalletInfo walletInfo; + final EncryptionFileUtils encryptionFileUtils; String _password; Future<void> init() async => await _load(); @@ -32,7 +34,7 @@ abstract class SolanaTransactionHistoryBase extends TransactionHistoryBase<Solan final path = '$dirPath/$transactionsHistoryFileName'; final transactionMaps = transactions.map((key, value) => MapEntry(key, value.toJson())); final data = json.encode({'transactions': transactionMaps}); - await writeData(path: path, password: _password, data: data); + await encryptionFileUtils.write(path: path, password: _password, data: data); } catch (e, s) { print('Error while saving solana transaction history: ${e.toString()}'); print(s); @@ -49,7 +51,7 @@ abstract class SolanaTransactionHistoryBase extends TransactionHistoryBase<Solan 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_solana/lib/solana_wallet.dart b/cw_solana/lib/solana_wallet.dart index 2b30a204c..66b8bca42 100644 --- a/cw_solana/lib/solana_wallet.dart +++ b/cw_solana/lib/solana_wallet.dart @@ -4,6 +4,7 @@ import 'dart:io'; import 'package:cw_core/cake_hive.dart'; import 'package:cw_core/crypto_currency.dart'; +import 'package:cw_core/encryption_file_utils.dart'; import 'package:cw_core/node.dart'; import 'package:cw_core/pathForWallet.dart'; import 'package:cw_core/pending_transaction.dart'; @@ -15,7 +16,6 @@ import 'package:cw_core/wallet_base.dart'; import 'package:cw_core/wallet_info.dart'; import 'package:cw_core/wallet_keys_file.dart'; import 'package:cw_solana/default_spl_tokens.dart'; -import 'package:cw_solana/file.dart'; import 'package:cw_solana/solana_balance.dart'; import 'package:cw_solana/solana_client.dart'; import 'package:cw_solana/solana_exceptions.dart'; @@ -46,6 +46,7 @@ abstract class SolanaWalletBase String? privateKey, required String password, SolanaBalance? initialBalance, + required this.encryptionFileUtils, }) : syncStatus = const NotConnectedSyncStatus(), _password = password, _mnemonic = mnemonic, @@ -56,7 +57,11 @@ abstract class SolanaWalletBase {CryptoCurrency.sol: initialBalance ?? SolanaBalance(BigInt.zero.toDouble())}), super(walletInfo) { this.walletInfo = walletInfo; - transactionHistory = SolanaTransactionHistory(walletInfo: walletInfo, password: password); + transactionHistory = SolanaTransactionHistory( + walletInfo: walletInfo, + password: password, + encryptionFileUtils: encryptionFileUtils, + ); if (!CakeHive.isAdapterRegistered(SPLToken.typeId)) { CakeHive.registerAdapter(SPLTokenAdapter()); @@ -68,6 +73,7 @@ abstract class SolanaWalletBase final String _password; final String? _mnemonic; final String? _hexPrivateKey; + final EncryptionFileUtils encryptionFileUtils; // The Solana WalletPair Ed25519HDKeyPair? _walletKeyPair; @@ -77,7 +83,7 @@ abstract class SolanaWalletBase // To access the privateKey bytes. Ed25519HDKeyPairData? _keyPairData; - late SolanaWalletClient _client; + late final SolanaWalletClient _client; @observable double? estimatedFee; @@ -97,7 +103,7 @@ abstract class SolanaWalletBase @observable late ObservableMap<CryptoCurrency, SolanaBalance> balance; - Completer<SharedPreferences> _sharedPrefs = Completer(); + final Completer<SharedPreferences> _sharedPrefs = Completer(); @override Ed25519HDKeyPairData get keys { @@ -343,13 +349,13 @@ abstract class SolanaWalletBase @override Future<void> save() async { if (!(await WalletKeysFile.hasKeysFile(walletInfo.name, walletInfo.type))) { - await saveKeysFile(_password); - saveKeysFile(_password, true); + await saveKeysFile(_password, encryptionFileUtils); + saveKeysFile(_password, encryptionFileUtils, true); } 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(); } @@ -382,13 +388,14 @@ abstract class SolanaWalletBase required String name, required String password, required WalletInfo walletInfo, + required EncryptionFileUtils encryptionFileUtils, }) async { final hasKeysFile = await WalletKeysFile.hasKeysFile(name, walletInfo.type); final path = await pathForWallet(name: name, type: walletInfo.type); Map<String, dynamic>? data; try { - final jsonSource = await read(path: path, password: password); + final jsonSource = await encryptionFileUtils.read(path: path, password: password); data = json.decode(jsonSource) as Map<String, dynamic>; } catch (e) { @@ -405,7 +412,12 @@ abstract class SolanaWalletBase keysData = WalletKeysData(mnemonic: mnemonic, privateKey: privateKey); } else { - keysData = await WalletKeysFile.readKeysFile(name, walletInfo.type, password); + keysData = await WalletKeysFile.readKeysFile( + name, + walletInfo.type, + password, + encryptionFileUtils, + ); } return SolanaWallet( @@ -414,6 +426,7 @@ abstract class SolanaWalletBase mnemonic: keysData.mnemonic, privateKey: keysData.privateKey, initialBalance: balance, + encryptionFileUtils: encryptionFileUtils, ); } @@ -572,4 +585,7 @@ abstract class SolanaWalletBase } SolanaClient? get solanaClient => _client.getSolanaClient; + + @override + String get password => _password; } diff --git a/cw_solana/lib/solana_wallet_creation_credentials.dart b/cw_solana/lib/solana_wallet_creation_credentials.dart index 881c30abd..5b4fa1774 100644 --- a/cw_solana/lib/solana_wallet_creation_credentials.dart +++ b/cw_solana/lib/solana_wallet_creation_credentials.dart @@ -2,8 +2,8 @@ import 'package:cw_core/wallet_credentials.dart'; import 'package:cw_core/wallet_info.dart'; class SolanaNewWalletCredentials extends WalletCredentials { - SolanaNewWalletCredentials({required String name, WalletInfo? walletInfo}) - : super(name: name, walletInfo: walletInfo); + SolanaNewWalletCredentials({required String name, WalletInfo? walletInfo, String? password}) + : super(name: name, walletInfo: walletInfo, password: password); } class SolanaRestoreWalletFromSeedCredentials extends WalletCredentials { diff --git a/cw_solana/lib/solana_wallet_service.dart b/cw_solana/lib/solana_wallet_service.dart index 4afb2f7f4..7461be33b 100644 --- a/cw_solana/lib/solana_wallet_service.dart +++ b/cw_solana/lib/solana_wallet_service.dart @@ -2,6 +2,7 @@ import 'dart:io'; import 'package:bip39/bip39.dart' as bip39; import 'package:collection/collection.dart'; +import 'package:cw_core/encryption_file_utils.dart'; import 'package:cw_core/balance.dart'; import 'package:cw_core/pathForWallet.dart'; import 'package:cw_core/transaction_history.dart'; @@ -17,9 +18,10 @@ import 'package:hive/hive.dart'; class SolanaWalletService extends WalletService<SolanaNewWalletCredentials, SolanaRestoreWalletFromSeedCredentials, SolanaRestoreWalletFromPrivateKey, SolanaNewWalletCredentials> { - SolanaWalletService(this.walletInfoSource); + SolanaWalletService(this.walletInfoSource, this.isDirect); final Box<WalletInfo> walletInfoSource; + final bool isDirect; @override Future<SolanaWallet> create(SolanaNewWalletCredentials credentials, {bool? isTestnet}) async { @@ -31,6 +33,7 @@ class SolanaWalletService extends WalletService<SolanaNewWalletCredentials, walletInfo: credentials.walletInfo!, mnemonic: mnemonic, password: credentials.password!, + encryptionFileUtils: encryptionFileUtilsFor(isDirect), ); await wallet.init(); @@ -56,6 +59,7 @@ class SolanaWalletService extends WalletService<SolanaNewWalletCredentials, name: name, password: password, walletInfo: walletInfo, + encryptionFileUtils: encryptionFileUtilsFor(isDirect), ); await wallet.init(); @@ -69,6 +73,7 @@ class SolanaWalletService extends WalletService<SolanaNewWalletCredentials, name: name, password: password, walletInfo: walletInfo, + encryptionFileUtils: encryptionFileUtilsFor(isDirect), ); await wallet.init(); @@ -92,6 +97,7 @@ class SolanaWalletService extends WalletService<SolanaNewWalletCredentials, password: credentials.password!, privateKey: credentials.privateKey, walletInfo: credentials.walletInfo!, + encryptionFileUtils: encryptionFileUtilsFor(isDirect), ); await wallet.init(); @@ -112,6 +118,7 @@ class SolanaWalletService extends WalletService<SolanaNewWalletCredentials, password: credentials.password!, mnemonic: credentials.mnemonic, walletInfo: credentials.walletInfo!, + encryptionFileUtils: encryptionFileUtilsFor(isDirect), ); await wallet.init(); @@ -126,7 +133,11 @@ class SolanaWalletService extends WalletService<SolanaNewWalletCredentials, final currentWalletInfo = walletInfoSource.values .firstWhere((info) => info.id == WalletBase.idFor(currentName, getType())); final currentWallet = await SolanaWalletBase.open( - password: password, name: currentName, walletInfo: currentWalletInfo); + password: password, + name: currentName, + walletInfo: currentWalletInfo, + encryptionFileUtils: encryptionFileUtilsFor(isDirect), + ); await currentWallet.renameWalletFiles(newName); await saveBackup(newName); diff --git a/cw_tron/lib/file.dart b/cw_tron/lib/file.dart deleted file mode 100644 index 8fd236ec3..000000000 --- a/cw_tron/lib/file.dart +++ /dev/null @@ -1,39 +0,0 @@ -import 'dart:io'; -import 'package:cw_core/key.dart'; -import 'package:encrypt/encrypt.dart' as encrypt; - -Future<void> write( - {required String path, - required String password, - required String data}) async { - final keys = extractKeys(password); - final key = encrypt.Key.fromBase64(keys.first); - final iv = encrypt.IV.fromBase64(keys.last); - final encrypted = await encode(key: key, iv: iv, data: data); - final f = File(path); - f.writeAsStringSync(encrypted); -} - -Future<void> writeData( - {required String path, - required String password, - required String data}) async { - final keys = extractKeys(password); - final key = encrypt.Key.fromBase64(keys.first); - final iv = encrypt.IV.fromBase64(keys.last); - final encrypted = await encode(key: key, iv: iv, data: data); - final f = File(path); - f.writeAsStringSync(encrypted); -} - -Future<String> read({required String path, required String password}) async { - final file = File(path); - - if (!file.existsSync()) { - file.createSync(); - } - - final encrypted = file.readAsStringSync(); - - return decode(password: password, data: encrypted); -} diff --git a/cw_tron/lib/tron_transaction_history.dart b/cw_tron/lib/tron_transaction_history.dart index 7d7274226..9d226c09c 100644 --- a/cw_tron/lib/tron_transaction_history.dart +++ b/cw_tron/lib/tron_transaction_history.dart @@ -3,7 +3,7 @@ import 'dart:core'; import 'dart:developer'; import 'package:cw_core/pathForWallet.dart'; import 'package:cw_core/wallet_info.dart'; -import 'package:cw_evm/file.dart'; +import 'package:cw_core/encryption_file_utils.dart'; import 'package:cw_tron/tron_transaction_info.dart'; import 'package:mobx/mobx.dart'; import 'package:cw_core/transaction_history.dart'; @@ -14,7 +14,8 @@ class TronTransactionHistory = TronTransactionHistoryBase with _$TronTransaction abstract class TronTransactionHistoryBase extends TransactionHistoryBase<TronTransactionInfo> with Store { - TronTransactionHistoryBase({required this.walletInfo, required String password}) + TronTransactionHistoryBase( + {required this.walletInfo, required String password, required this.encryptionFileUtils}) : _password = password { transactions = ObservableMap<String, TronTransactionInfo>(); } @@ -22,6 +23,7 @@ abstract class TronTransactionHistoryBase extends TransactionHistoryBase<TronTra String _password; final WalletInfo walletInfo; + final EncryptionFileUtils encryptionFileUtils; Future<void> init() async => await _load(); @@ -33,7 +35,7 @@ abstract class TronTransactionHistoryBase extends TransactionHistoryBase<TronTra String path = '$dirPath/$transactionsHistoryFileNameForWallet'; final transactionMaps = transactions.map((key, value) => MapEntry(key, value.toJson())); final data = json.encode({'transactions': transactionMaps}); - await writeData(path: path, password: _password, data: data); + await encryptionFileUtils.write(path: path, password: _password, data: data); } catch (e, s) { log('Error while saving ${walletInfo.type.name} transaction history: ${e.toString()}'); log(s.toString()); @@ -51,7 +53,7 @@ abstract class TronTransactionHistoryBase extends TransactionHistoryBase<TronTra String transactionsHistoryFileNameForWallet = 'tron_transactions.json'; final dirPath = await pathForWalletDir(name: walletInfo.name, type: walletInfo.type); String path = '$dirPath/$transactionsHistoryFileNameForWallet'; - 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_tron/lib/tron_wallet.dart b/cw_tron/lib/tron_wallet.dart index 3566dcd94..7dc43b4bb 100644 --- a/cw_tron/lib/tron_wallet.dart +++ b/cw_tron/lib/tron_wallet.dart @@ -7,6 +7,7 @@ import 'package:bip39/bip39.dart' as bip39; import 'package:blockchain_utils/blockchain_utils.dart'; import 'package:cw_core/cake_hive.dart'; import 'package:cw_core/crypto_currency.dart'; +import 'package:cw_core/encryption_file_utils.dart'; import 'package:cw_core/node.dart'; import 'package:cw_core/pathForWallet.dart'; import 'package:cw_core/pending_transaction.dart'; @@ -19,7 +20,6 @@ import 'package:cw_core/wallet_info.dart'; import 'package:cw_core/wallet_keys_file.dart'; import 'package:cw_core/wallet_type.dart'; import 'package:cw_tron/default_tron_tokens.dart'; -import 'package:cw_tron/file.dart'; import 'package:cw_tron/tron_abi.dart'; import 'package:cw_tron/tron_balance.dart'; import 'package:cw_tron/tron_client.dart'; @@ -46,6 +46,7 @@ abstract class TronWalletBase String? privateKey, required String password, TronBalance? initialBalance, + required this.encryptionFileUtils, }) : syncStatus = const NotConnectedSyncStatus(), _password = password, _mnemonic = mnemonic, @@ -57,7 +58,8 @@ abstract class TronWalletBase ), super(walletInfo) { this.walletInfo = walletInfo; - transactionHistory = TronTransactionHistory(walletInfo: walletInfo, password: password); + transactionHistory = TronTransactionHistory( + walletInfo: walletInfo, password: password, encryptionFileUtils: encryptionFileUtils); if (!CakeHive.isAdapterRegistered(TronToken.typeId)) { CakeHive.registerAdapter(TronTokenAdapter()); @@ -67,6 +69,7 @@ abstract class TronWalletBase final String? _mnemonic; final String? _hexPrivateKey; final String _password; + final EncryptionFileUtils encryptionFileUtils; late final Box<TronToken> tronTokensBox; @@ -125,13 +128,14 @@ abstract class TronWalletBase required String name, required String password, required WalletInfo walletInfo, + required EncryptionFileUtils encryptionFileUtils, }) async { final hasKeysFile = await WalletKeysFile.hasKeysFile(name, walletInfo.type); final path = await pathForWallet(name: name, type: walletInfo.type); Map<String, dynamic>? data; try { - final jsonSource = await read(path: path, password: password); + final jsonSource = await encryptionFileUtils.read(path: path, password: password); data = json.decode(jsonSource) as Map<String, dynamic>; } catch (e) { @@ -148,7 +152,12 @@ abstract class TronWalletBase keysData = WalletKeysData(mnemonic: mnemonic, privateKey: privateKey); } else { - keysData = await WalletKeysFile.readKeysFile(name, walletInfo.type, password); + keysData = await WalletKeysFile.readKeysFile( + name, + walletInfo.type, + password, + encryptionFileUtils, + ); } return TronWallet( @@ -157,6 +166,7 @@ abstract class TronWalletBase mnemonic: keysData.mnemonic, privateKey: keysData.privateKey, initialBalance: balance, + encryptionFileUtils: encryptionFileUtils, ); } @@ -430,13 +440,13 @@ abstract class TronWalletBase @override Future<void> save() async { if (!(await WalletKeysFile.hasKeysFile(walletInfo.name, walletInfo.type))) { - await saveKeysFile(_password); - saveKeysFile(_password, true); + await saveKeysFile(_password, encryptionFileUtils); + saveKeysFile(_password, encryptionFileUtils, true); } 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(); } @@ -584,4 +594,7 @@ abstract class TronWalletBase _transactionsUpdateTimer?.cancel(); } } + + @override + String get password => _password; } diff --git a/cw_tron/lib/tron_wallet_creation_credentials.dart b/cw_tron/lib/tron_wallet_creation_credentials.dart index dc4f389aa..ed5e1c164 100644 --- a/cw_tron/lib/tron_wallet_creation_credentials.dart +++ b/cw_tron/lib/tron_wallet_creation_credentials.dart @@ -2,8 +2,8 @@ import 'package:cw_core/wallet_credentials.dart'; import 'package:cw_core/wallet_info.dart'; class TronNewWalletCredentials extends WalletCredentials { - TronNewWalletCredentials({required String name, WalletInfo? walletInfo}) - : super(name: name, walletInfo: walletInfo); + TronNewWalletCredentials({required String name, WalletInfo? walletInfo, String? password}) + : super(name: name, walletInfo: walletInfo, password: password); } class TronRestoreWalletFromSeedCredentials extends WalletCredentials { diff --git a/cw_tron/lib/tron_wallet_service.dart b/cw_tron/lib/tron_wallet_service.dart index ba217a265..dacef439a 100644 --- a/cw_tron/lib/tron_wallet_service.dart +++ b/cw_tron/lib/tron_wallet_service.dart @@ -3,6 +3,7 @@ import 'dart:io'; import 'package:bip39/bip39.dart' as bip39; import 'package:collection/collection.dart'; import 'package:cw_core/balance.dart'; +import 'package:cw_core/encryption_file_utils.dart'; import 'package:cw_core/pathForWallet.dart'; import 'package:cw_core/transaction_history.dart'; import 'package:cw_core/transaction_info.dart'; @@ -21,11 +22,12 @@ class TronWalletService extends WalletService< TronRestoreWalletFromSeedCredentials, TronRestoreWalletFromPrivateKey, TronNewWalletCredentials> { - TronWalletService(this.walletInfoSource, {required this.client}); + TronWalletService(this.walletInfoSource, {required this.client, required this.isDirect}); late TronClient client; final Box<WalletInfo> walletInfoSource; + final bool isDirect; @override WalletType getType() => WalletType.tron; @@ -43,6 +45,7 @@ class TronWalletService extends WalletService< walletInfo: credentials.walletInfo!, mnemonic: mnemonic, password: credentials.password!, + encryptionFileUtils: encryptionFileUtilsFor(isDirect), ); await wallet.init(); @@ -62,6 +65,7 @@ class TronWalletService extends WalletService< name: name, password: password, walletInfo: walletInfo, + encryptionFileUtils: encryptionFileUtilsFor(isDirect), ); await wallet.init(); @@ -75,6 +79,7 @@ class TronWalletService extends WalletService< name: name, password: password, walletInfo: walletInfo, + encryptionFileUtils: encryptionFileUtilsFor(isDirect), ); await wallet.init(); @@ -92,6 +97,7 @@ class TronWalletService extends WalletService< password: credentials.password!, privateKey: credentials.privateKey, walletInfo: credentials.walletInfo!, + encryptionFileUtils: encryptionFileUtilsFor(isDirect), ); await wallet.init(); @@ -114,6 +120,7 @@ class TronWalletService extends WalletService< password: credentials.password!, mnemonic: credentials.mnemonic, walletInfo: credentials.walletInfo!, + encryptionFileUtils: encryptionFileUtilsFor(isDirect), ); await wallet.init(); @@ -128,7 +135,11 @@ class TronWalletService extends WalletService< final currentWalletInfo = walletInfoSource.values .firstWhere((info) => info.id == WalletBase.idFor(currentName, getType())); final currentWallet = await TronWalletBase.open( - password: password, name: currentName, walletInfo: currentWalletInfo); + password: password, + name: currentName, + walletInfo: currentWalletInfo, + encryptionFileUtils: encryptionFileUtilsFor(isDirect), + ); await currentWallet.renameWalletFiles(newName); await saveBackup(newName); diff --git a/cw_wownero/lib/wownero_wallet.dart b/cw_wownero/lib/wownero_wallet.dart index e02c0ec2e..85f5e4b2f 100644 --- a/cw_wownero/lib/wownero_wallet.dart +++ b/cw_wownero/lib/wownero_wallet.dart @@ -50,7 +50,7 @@ abstract class WowneroWalletBase extends WalletBase<WowneroBalance, WowneroTransactionHistory, WowneroTransactionInfo> with Store { WowneroWalletBase( - {required WalletInfo walletInfo, required Box<UnspentCoinsInfo> unspentCoinsInfo}) + {required WalletInfo walletInfo, required Box<UnspentCoinsInfo> unspentCoinsInfo, required String password}) : balance = ObservableMap<CryptoCurrency, WowneroBalance>.of({ CryptoCurrency.wow: WowneroBalance( fullBalance: wownero_wallet.getFullBalance(accountIndex: 0), @@ -58,6 +58,7 @@ abstract class WowneroWalletBase }), _isTransactionUpdating = false, _hasSyncAfterStartup = false, + _password = password, isEnabledAutoGenerateSubaddress = false, syncStatus = NotConnectedSyncStatus(), unspentCoins = [], @@ -109,6 +110,10 @@ abstract class WowneroWalletBase String seedLegacy(String? language) => wownero_wallet.getSeedLegacy(language); + String get password => _password; + + String _password; + @override MoneroWalletKeys get keys => MoneroWalletKeys( privateSpendKey: wownero_wallet.getSecretSpendKey(), diff --git a/cw_wownero/lib/wownero_wallet_service.dart b/cw_wownero/lib/wownero_wallet_service.dart index 13cab8f61..286bfccd0 100644 --- a/cw_wownero/lib/wownero_wallet_service.dart +++ b/cw_wownero/lib/wownero_wallet_service.dart @@ -93,7 +93,7 @@ class WowneroWalletService extends WalletService< await wownero_wallet_manager.createWallet( path: path, password: credentials.password!, language: credentials.language); final wallet = WowneroWallet( - walletInfo: credentials.walletInfo!, unspentCoinsInfo: unspentCoinsInfoSource); + walletInfo: credentials.walletInfo!, unspentCoinsInfo: unspentCoinsInfoSource, password: credentials.password!); await wallet.init(); return wallet; @@ -129,7 +129,7 @@ class WowneroWalletService extends WalletService< await wownero_wallet_manager.openWalletAsync({'path': path, 'password': password}); final walletInfo = walletInfoSource.values .firstWhere((info) => info.id == WalletBase.idFor(name, getType())); - wallet = WowneroWallet(walletInfo: walletInfo, unspentCoinsInfo: unspentCoinsInfoSource); + wallet = WowneroWallet(walletInfo: walletInfo, unspentCoinsInfo: unspentCoinsInfoSource, password: password); final isValid = wallet.walletAddresses.validate(); if (!isValid) { @@ -210,7 +210,7 @@ class WowneroWalletService extends WalletService< final currentWalletInfo = walletInfoSource.values .firstWhere((info) => info.id == WalletBase.idFor(currentName, getType())); final currentWallet = - WowneroWallet(walletInfo: currentWalletInfo, unspentCoinsInfo: unspentCoinsInfoSource); + WowneroWallet(walletInfo: currentWalletInfo, unspentCoinsInfo: unspentCoinsInfoSource, password: password); await currentWallet.renameWalletFiles(newName); @@ -235,7 +235,7 @@ class WowneroWalletService extends WalletService< viewKey: credentials.viewKey, spendKey: credentials.spendKey); final wallet = WowneroWallet( - walletInfo: credentials.walletInfo!, unspentCoinsInfo: unspentCoinsInfoSource); + walletInfo: credentials.walletInfo!, unspentCoinsInfo: unspentCoinsInfoSource, password: credentials.password!); await wallet.init(); return wallet; @@ -268,7 +268,7 @@ class WowneroWalletService extends WalletService< seed: credentials.mnemonic, restoreHeight: credentials.height!); final wallet = WowneroWallet( - walletInfo: credentials.walletInfo!, unspentCoinsInfo: unspentCoinsInfoSource); + walletInfo: credentials.walletInfo!, unspentCoinsInfo: unspentCoinsInfoSource, password: credentials.password!); await wallet.init(); return wallet; @@ -315,7 +315,7 @@ class WowneroWalletService extends WalletService< restoreHeight: height, spendKey: spendKey); - final wallet = WowneroWallet(walletInfo: walletInfo, unspentCoinsInfo: unspentCoinsInfoSource); + final wallet = WowneroWallet(walletInfo: walletInfo, unspentCoinsInfo: unspentCoinsInfoSource, password: password); await wallet.init(); return wallet; diff --git a/cw_wownero/pubspec.lock b/cw_wownero/pubspec.lock index 737743925..85d856b35 100644 --- a/cw_wownero/pubspec.lock +++ b/cw_wownero/pubspec.lock @@ -113,6 +113,15 @@ packages: url: "https://pub.dev" source: hosted version: "8.4.3" + cake_backup: + dependency: transitive + 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: @@ -169,6 +178,22 @@ packages: url: "https://pub.dev" source: hosted version: "3.0.2" + cryptography: + dependency: transitive + description: + name: cryptography + sha256: df156c5109286340817d21fa7b62f9140f17915077127dd70f8bd7a2a0997a35 + url: "https://pub.dev" + source: hosted + version: "2.5.0" + cupertino_icons: + dependency: transitive + description: + name: cupertino_icons + sha256: ba631d1c7f7bef6b729a622b7b752645a2d076dba9976925b8f25725a30e1ee6 + url: "https://pub.dev" + source: hosted + version: "1.0.8" cw_core: dependency: "direct main" description: @@ -680,6 +705,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.1" + tuple: + dependency: transitive + description: + name: tuple + sha256: a97ce2013f240b2f3807bcbaf218765b6f301c3eff91092bcfa23a039e7dd151 + url: "https://pub.dev" + source: hosted + version: "2.0.2" typed_data: dependency: transitive description: diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 6277c147d..c549857ba 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -9,6 +9,35 @@ PODS: - ReachabilitySwift - CryptoSwift (1.8.2) - cw_mweb (0.0.1): + - cw_haven (0.0.1): + - cw_haven/Boost (= 0.0.1) + - cw_haven/Haven (= 0.0.1) + - cw_haven/OpenSSL (= 0.0.1) + - cw_haven/Sodium (= 0.0.1) + - cw_shared_external + - Flutter + - cw_haven/Boost (0.0.1): + - cw_shared_external + - Flutter + - cw_haven/Haven (0.0.1): + - cw_shared_external + - Flutter + - cw_haven/OpenSSL (0.0.1): + - cw_shared_external + - Flutter + - cw_haven/Sodium (0.0.1): + - cw_shared_external + - Flutter + - cw_shared_external (0.0.1): + - cw_shared_external/Boost (= 0.0.1) + - cw_shared_external/OpenSSL (= 0.0.1) + - cw_shared_external/Sodium (= 0.0.1) + - Flutter + - cw_shared_external/Boost (0.0.1): + - Flutter + - cw_shared_external/OpenSSL (0.0.1): + - Flutter + - cw_shared_external/Sodium (0.0.1): - Flutter - device_display_brightness (0.0.1): - Flutter @@ -118,6 +147,8 @@ DEPENDENCIES: - connectivity_plus (from `.symlinks/plugins/connectivity_plus/ios`) - CryptoSwift - cw_mweb (from `.symlinks/plugins/cw_mweb/ios`) + - cw_haven (from `.symlinks/plugins/cw_haven/ios`) + - cw_shared_external (from `.symlinks/plugins/cw_shared_external/ios`) - device_display_brightness (from `.symlinks/plugins/device_display_brightness/ios`) - device_info_plus (from `.symlinks/plugins/device_info_plus/ios`) - devicelocale (from `.symlinks/plugins/devicelocale/ios`) @@ -167,6 +198,10 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/connectivity_plus/ios" cw_mweb: :path: ".symlinks/plugins/cw_mweb/ios" + cw_haven: + :path: ".symlinks/plugins/cw_haven/ios" + cw_shared_external: + :path: ".symlinks/plugins/cw_shared_external/ios" device_display_brightness: :path: ".symlinks/plugins/device_display_brightness/ios" device_info_plus: @@ -222,6 +257,8 @@ SPEC CHECKSUMS: connectivity_plus: bf0076dd84a130856aa636df1c71ccaff908fa1d CryptoSwift: c63a805d8bb5e5538e88af4e44bb537776af11ea cw_mweb: 87af74f9659fed0c1a2cbfb44413f1070e79e3ae + cw_haven: b3e54e1fbe7b8e6fda57a93206bc38f8e89b898a + cw_shared_external: 2972d872b8917603478117c9957dfca611845a92 device_display_brightness: 1510e72c567a1f6ce6ffe393dcd9afd1426034f7 device_info_plus: c6fb39579d0f423935b0c9ce7ee2f44b71b9fce6 devicelocale: b22617f40038496deffba44747101255cee005b0 diff --git a/lib/bitcoin/cw_bitcoin.dart b/lib/bitcoin/cw_bitcoin.dart index 4b4495dc3..3f1579fe8 100644 --- a/lib/bitcoin/cw_bitcoin.dart +++ b/lib/bitcoin/cw_bitcoin.dart @@ -29,8 +29,8 @@ class CWBitcoin extends Bitcoin { @override WalletCredentials createBitcoinNewWalletCredentials( - {required String name, WalletInfo? walletInfo}) => - BitcoinNewWalletCredentials(name: name, walletInfo: walletInfo); + {required String name, WalletInfo? walletInfo, String? password}) => + BitcoinNewWalletCredentials(name: name, walletInfo: walletInfo, password: password); @override WalletCredentials createBitcoinHardwareWalletCredentials( @@ -202,14 +202,14 @@ class CWBitcoin extends Bitcoin { await bitcoinWallet.updateAllUnspents(); } - WalletService createBitcoinWalletService( - Box<WalletInfo> walletInfoSource, Box<UnspentCoinsInfo> unspentCoinSource, bool alwaysScan) { - return BitcoinWalletService(walletInfoSource, unspentCoinSource, alwaysScan); + WalletService createBitcoinWalletService(Box<WalletInfo> walletInfoSource, + Box<UnspentCoinsInfo> unspentCoinSource, bool alwaysScan, bool isDirect) { + return BitcoinWalletService(walletInfoSource, unspentCoinSource, alwaysScan, isDirect); } - WalletService createLitecoinWalletService( - Box<WalletInfo> walletInfoSource, Box<UnspentCoinsInfo> unspentCoinSource, bool alwaysScan) { - return LitecoinWalletService(walletInfoSource, unspentCoinSource, alwaysScan); + WalletService createLitecoinWalletService(Box<WalletInfo> walletInfoSource, + Box<UnspentCoinsInfo> unspentCoinSource, bool alwaysScan, bool isDirect) { + return LitecoinWalletService(walletInfoSource, unspentCoinSource, isDirect, alwaysScan); } @override diff --git a/lib/bitcoin_cash/cw_bitcoin_cash.dart b/lib/bitcoin_cash/cw_bitcoin_cash.dart index 6e169209f..fcb34a286 100644 --- a/lib/bitcoin_cash/cw_bitcoin_cash.dart +++ b/lib/bitcoin_cash/cw_bitcoin_cash.dart @@ -6,16 +6,17 @@ class CWBitcoinCash extends BitcoinCash { @override WalletService createBitcoinCashWalletService( - Box<WalletInfo> walletInfoSource, Box<UnspentCoinsInfo> unspentCoinSource) { - return BitcoinCashWalletService(walletInfoSource, unspentCoinSource); + Box<WalletInfo> walletInfoSource, Box<UnspentCoinsInfo> unspentCoinSource, bool isDirect) { + return BitcoinCashWalletService(walletInfoSource, unspentCoinSource, isDirect); } @override WalletCredentials createBitcoinCashNewWalletCredentials({ required String name, WalletInfo? walletInfo, + String? password, }) => - BitcoinCashNewWalletCredentials(name: name, walletInfo: walletInfo); + BitcoinCashNewWalletCredentials(name: name, walletInfo: walletInfo, password: password); @override WalletCredentials createBitcoinCashRestoreWalletFromSeedCredentials( diff --git a/lib/core/backup_service.dart b/lib/core/backup_service.dart index 577238baf..42e24d3c7 100644 --- a/lib/core/backup_service.dart +++ b/lib/core/backup_service.dart @@ -3,6 +3,7 @@ import 'dart:io'; import 'dart:typed_data'; import 'package:cake_wallet/core/secure_storage.dart'; import 'package:cake_wallet/themes/theme_list.dart'; +import 'package:cw_core/root_dir.dart'; import 'package:cake_wallet/utils/device_info.dart'; import 'package:cw_core/root_dir.dart'; import 'package:cw_core/wallet_type.dart'; @@ -20,7 +21,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 { diff --git a/lib/core/wallet_creation_service.dart b/lib/core/wallet_creation_service.dart index 2f3acb6c9..823aa7e84 100644 --- a/lib/core/wallet_creation_service.dart +++ b/lib/core/wallet_creation_service.dart @@ -15,7 +15,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.settingsStore, @@ -25,7 +24,6 @@ class WalletCreationService { } WalletType type; - final SecureStorage secureStorage; final SharedPreferences sharedPreferences; final SettingsStore settingsStore; final KeyService keyService; @@ -56,12 +54,16 @@ class WalletCreationService { Future<WalletBase> create(WalletCredentials credentials, {bool? isTestnet}) async { checkIfExists(credentials.name); - final password = generateWalletPassword(); - credentials.password = password; + + if (credentials.password == null) { + credentials.password = generateWalletPassword(); + await keyService.saveWalletPassword( + password: credentials.password!, walletName: credentials.name); + } + if (_hasSeedPhraseLengthOption) { credentials.seedPhraseLength = settingsStore.seedPhraseLength.value; } - await keyService.saveWalletPassword(password: password, walletName: credentials.name); final wallet = await _service!.create(credentials, isTestnet: isTestnet); if (wallet.type == WalletType.monero) { @@ -94,9 +96,13 @@ class WalletCreationService { Future<WalletBase> restoreFromKeys(WalletCredentials credentials, {bool? isTestnet}) 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, isTestnet: isTestnet); if (wallet.type == WalletType.monero) { @@ -109,9 +115,13 @@ class WalletCreationService { Future<WalletBase> restoreFromSeed(WalletCredentials credentials, {bool? isTestnet}) 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, isTestnet: isTestnet); if (wallet.type == WalletType.monero) { diff --git a/lib/core/wallet_loading_service.dart b/lib/core/wallet_loading_service.dart index ca29576e4..2b570f14c 100644 --- a/lib/core/wallet_loading_service.dart +++ b/lib/core/wallet_loading_service.dart @@ -20,17 +20,18 @@ class WalletLoadingService { 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) { @@ -41,11 +42,11 @@ class WalletLoadingService { } } - Future<WalletBase> load(WalletType type, String name) async { + Future<WalletBase> load(WalletType type, String name, {String? password}) async { try { 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); @@ -67,8 +68,8 @@ class WalletLoadingService { for (var walletInfo in walletInfoSource.values) { try { final walletService = walletServiceFactory.call(walletInfo.type); - final password = await keyService.getWalletPassword(walletName: walletInfo.name); - final wallet = await walletService.openWallet(walletInfo.name, password); + final walletPassword = password ?? (await keyService.getWalletPassword(walletName: name)); + final wallet = await walletService.openWallet(walletInfo.name, walletPassword); if (walletInfo.type == WalletType.monero) { await updateMoneroWalletPassword(wallet); diff --git a/lib/di.dart b/lib/di.dart index 29f50362d..fe4e91414 100644 --- a/lib/di.dart +++ b/lib/di.dart @@ -30,6 +30,7 @@ import 'package:cake_wallet/entities/contact.dart'; import 'package:cake_wallet/entities/contact_record.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/preferences_key.dart'; import 'package:cake_wallet/entities/qr_view_data.dart'; import 'package:cake_wallet/entities/template.dart'; import 'package:cake_wallet/entities/transaction_description.dart'; @@ -114,6 +115,8 @@ 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/src/screens/wallet_connect/wc_connections_listing_view.dart'; +import 'package:cake_wallet/src/screens/wallet_unlock/wallet_unlock_arguments.dart'; +import 'package:cake_wallet/src/screens/wallet_unlock/wallet_unlock_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'; @@ -219,6 +222,8 @@ import 'package:cake_wallet/view_model/wallet_list/wallet_list_view_model.dart'; import 'package:cake_wallet/view_model/wallet_new_vm.dart'; import 'package:cake_wallet/view_model/wallet_restore_view_model.dart'; import 'package:cake_wallet/view_model/wallet_seed_view_model.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:cake_wallet/wownero/wownero.dart'; import 'package:cw_core/crypto_currency.dart'; import 'package:cw_core/receive_page_option.dart'; @@ -339,7 +344,6 @@ Future<void> setup({ WalletCreationService( initialType: type, keyService: getIt.get<KeyService>(), - secureStorage: getIt.get<SecureStorage>(), sharedPreferences: getIt.get<SharedPreferences>(), settingsStore: getIt.get<SettingsStore>(), walletInfoSource: _walletInfoSource)); @@ -359,6 +363,65 @@ Future<void> setup({ getIt.get<AdvancedPrivacySettingsViewModel>(param1: type), type: type)); + 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.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<WalletUnlockPage, WalletUnlockArguments, bool>((args, closable) { + return WalletUnlockPage( + getIt.get<WalletUnlockVerifiableViewModel>(param1: args), + args.callback, + args.authPasswordHandler, + closable: closable); + }, instanceName: 'wallet_unlock_verifiable'); + + 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<WalletRestorationFromQRVM, WalletType, void>((WalletType type, _) { return WalletRestorationFromQRVM(getIt.get<AppStore>(), getIt.get<WalletCreationService>(param1: type), _walletInfoSource, type); @@ -917,27 +980,32 @@ Future<void> setup({ _walletInfoSource, _unspentCoinsInfoSource, getIt.get<SettingsStore>().silentPaymentsAlwaysScan, + SettingsStoreBase.walletPasswordDirectInput, ); case WalletType.litecoin: return bitcoin!.createLitecoinWalletService( _walletInfoSource, _unspentCoinsInfoSource, getIt.get<SettingsStore>().mwebAlwaysScan, + SettingsStoreBase.walletPasswordDirectInput, ); case WalletType.ethereum: - return ethereum!.createEthereumWalletService(_walletInfoSource); + return ethereum!.createEthereumWalletService( + _walletInfoSource, SettingsStoreBase.walletPasswordDirectInput); case WalletType.bitcoinCash: - return bitcoinCash! - .createBitcoinCashWalletService(_walletInfoSource, _unspentCoinsInfoSource); + return bitcoinCash!.createBitcoinCashWalletService(_walletInfoSource, + _unspentCoinsInfoSource, SettingsStoreBase.walletPasswordDirectInput); case WalletType.nano: case WalletType.banano: - return nano!.createNanoWalletService(_walletInfoSource); + return nano!.createNanoWalletService(_walletInfoSource, SettingsStoreBase.walletPasswordDirectInput); case WalletType.polygon: - return polygon!.createPolygonWalletService(_walletInfoSource); + return polygon!.createPolygonWalletService( + _walletInfoSource, SettingsStoreBase.walletPasswordDirectInput); case WalletType.solana: - return solana!.createSolanaWalletService(_walletInfoSource); + return solana!.createSolanaWalletService( + _walletInfoSource, SettingsStoreBase.walletPasswordDirectInput); case WalletType.tron: - return tron!.createTronWalletService(_walletInfoSource); + return tron!.createTronWalletService(_walletInfoSource, SettingsStoreBase.walletPasswordDirectInput); case WalletType.wownero: return wownero!.createWowneroWalletService(_walletInfoSource, _unspentCoinsInfoSource); case WalletType.none: diff --git a/lib/entities/load_current_wallet.dart b/lib/entities/load_current_wallet.dart index 595bc2233..e67b59997 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); await appStore.changeCurrentWallet(wallet); getIt.get<BackgroundTasks>().registerSyncTask(); diff --git a/lib/ethereum/cw_ethereum.dart b/lib/ethereum/cw_ethereum.dart index 7b593d58d..4e210b227 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) => EVMChainMnemonics.englishWordlist; - WalletService createEthereumWalletService(Box<WalletInfo> walletInfoSource) => - EthereumWalletService(walletInfoSource, client: EthereumClient()); + WalletService createEthereumWalletService(Box<WalletInfo> walletInfoSource, bool isDirect) => + EthereumWalletService(walletInfoSource, isDirect, client: EthereumClient()); @override WalletCredentials createEthereumNewWalletCredentials({ required String name, WalletInfo? walletInfo, + String? password, }) => - EVMChainNewWalletCredentials(name: name, walletInfo: walletInfo); + EVMChainNewWalletCredentials(name: name, walletInfo: walletInfo, password: password); @override WalletCredentials createEthereumRestoreWalletFromSeedCredentials({ diff --git a/lib/main.dart b/lib/main.dart index 67da927ce..4d1f12917 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -49,6 +49,7 @@ final rootKey = GlobalKey<RootState>(); final RouteObserver<PageRoute<dynamic>> routeObserver = RouteObserver<PageRoute<dynamic>>(); Future<void> main() async { + bool isAppRunning = false; await runZonedGuarded(() async { WidgetsFlutterBinding.ensureInitialized(); @@ -175,7 +176,6 @@ Future<void> initializeAppConfigs() async { } final secureStorage = secureStorageShared; - final transactionDescriptionsBoxKey = await getEncryptionKey(secureStorage: secureStorage, forKey: TransactionDescription.boxKey); final tradesBoxKey = await getEncryptionKey(secureStorage: secureStorage, forKey: Trade.boxKey); @@ -252,8 +252,8 @@ Future<void> initialSetup( ordersSource: ordersSource, anonpayInvoiceInfoSource: anonpayInvoiceInfo, unspentCoinsInfoSource: unspentCoinsInfoSource, - secureStorage: secureStorage, navigatorKey: navigatorKey, + secureStorage: secureStorage, ); await bootstrap(navigatorKey); } diff --git a/lib/nano/cw_nano.dart b/lib/nano/cw_nano.dart index ad02d2ccb..8cf640d8b 100644 --- a/lib/nano/cw_nano.dart +++ b/lib/nano/cw_nano.dart @@ -75,8 +75,8 @@ class CWNano extends Nano { } @override - WalletService createNanoWalletService(Box<WalletInfo> walletInfoSource) { - return NanoWalletService(walletInfoSource); + WalletService createNanoWalletService(Box<WalletInfo> walletInfoSource, bool isDirect) { + return NanoWalletService(walletInfoSource, isDirect); } @override diff --git a/lib/polygon/cw_polygon.dart b/lib/polygon/cw_polygon.dart index 2dcb1b4a6..5bb87ff5b 100644 --- a/lib/polygon/cw_polygon.dart +++ b/lib/polygon/cw_polygon.dart @@ -4,15 +4,16 @@ class CWPolygon extends Polygon { @override List<String> getPolygonWordList(String language) => EVMChainMnemonics.englishWordlist; - WalletService createPolygonWalletService(Box<WalletInfo> walletInfoSource) => - PolygonWalletService(walletInfoSource, client: PolygonClient()); + WalletService createPolygonWalletService(Box<WalletInfo> walletInfoSource, bool isDirect) => + PolygonWalletService(walletInfoSource, isDirect, client: PolygonClient()); @override WalletCredentials createPolygonNewWalletCredentials({ required String name, WalletInfo? walletInfo, + String? password }) => - EVMChainNewWalletCredentials(name: name, walletInfo: walletInfo); + EVMChainNewWalletCredentials(name: name, walletInfo: walletInfo, password: password); @override WalletCredentials createPolygonRestoreWalletFromSeedCredentials({ diff --git a/lib/reactions/on_authentication_state_change.dart b/lib/reactions/on_authentication_state_change.dart index e4fd9b32f..95cbd51df 100644 --- a/lib/reactions/on_authentication_state_change.dart +++ b/lib/reactions/on_authentication_state_change.dart @@ -2,6 +2,7 @@ import 'dart:async'; 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'; @@ -23,7 +24,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/router.dart b/lib/router.dart index 4094b4d56..99dae83c6 100644 --- a/lib/router.dart +++ b/lib/router.dart @@ -91,7 +91,11 @@ import 'package:cake_wallet/src/screens/wallet/wallet_edit_page.dart'; import 'package:cake_wallet/src/screens/wallet_connect/wc_connections_listing_view.dart'; import 'package:cake_wallet/src/screens/wallet_keys/wallet_keys_page.dart'; import 'package:cake_wallet/src/screens/wallet_list/wallet_list_page.dart'; +import 'package:cake_wallet/src/screens/wallet_unlock/wallet_unlock_page.dart'; import 'package:cake_wallet/src/screens/welcome/create_welcome_page.dart'; +import 'package:cake_wallet/store/settings_store.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/advanced_privacy_settings_view_model.dart'; import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart'; @@ -126,6 +130,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 _) { @@ -177,6 +189,10 @@ Route<dynamic> createRoute(RouteSettings settings) { param2: [false, false])); case Routes.restoreOptions: + if (SettingsStoreBase.walletPasswordDirectInput) { + return createRoute(RouteSettings(name: Routes.restoreWalletType)); + } + final isNewInstall = settings.arguments as bool; return CupertinoPageRoute<void>( fullscreenDialog: true, @@ -329,8 +345,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; @@ -341,24 +365,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.silentPaymentsSettings: return CupertinoPageRoute<void>( @@ -402,6 +434,17 @@ Route<dynamic> createRoute(RouteSettings settings) { builder: (_) => getIt.get<NodeCreateOrEditPage>( 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.newPowNode: final args = settings.arguments as Map<String, dynamic>?; return CupertinoPageRoute<void>( @@ -491,7 +534,9 @@ Route<dynamic> createRoute(RouteSettings settings) { fullscreenDialog: true, builder: (_) => getIt.get<RestoreFromBackupPage>()); case Routes.support: - return CupertinoPageRoute<void>(builder: (_) => getIt.get<SupportPage>()); + return CupertinoPageRoute<void>( + fullscreenDialog: true, + builder: (_) => getIt.get<SupportPage>()); case Routes.supportLiveChat: return CupertinoPageRoute<void>(builder: (_) => getIt.get<SupportChatPage>()); diff --git a/lib/routes.dart b/lib/routes.dart index ec8a8f338..9dac48d94 100644 --- a/lib/routes.dart +++ b/lib/routes.dart @@ -83,6 +83,8 @@ 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'; diff --git a/lib/solana/cw_solana.dart b/lib/solana/cw_solana.dart index af66cf3e5..e70739db9 100644 --- a/lib/solana/cw_solana.dart +++ b/lib/solana/cw_solana.dart @@ -4,15 +4,16 @@ class CWSolana extends Solana { @override List<String> getSolanaWordList(String language) => SolanaMnemonics.englishWordlist; - WalletService createSolanaWalletService(Box<WalletInfo> walletInfoSource) => - SolanaWalletService(walletInfoSource); + WalletService createSolanaWalletService(Box<WalletInfo> walletInfoSource, bool isDirect) => + SolanaWalletService(walletInfoSource, isDirect); @override WalletCredentials createSolanaNewWalletCredentials({ required String name, WalletInfo? walletInfo, + String? password, }) => - SolanaNewWalletCredentials(name: name, walletInfo: walletInfo); + SolanaNewWalletCredentials(name: name, walletInfo: walletInfo, password: password); @override WalletCredentials createSolanaRestoreWalletFromSeedCredentials({ 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/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 46e63af01..94489a945 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,12 @@ 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/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'; @@ -176,12 +179,25 @@ class _DesktopWalletSelectionDropDownState extends State<DesktopWalletSelectionD } Future<void> _loadWallet(WalletListItem wallet) async { - widget._authService.authenticateAction( - context, - onAuthSuccess: (isAuthenticatedSuccessfully) async { - if (!isAuthenticatedSuccessfully) { - return; - } + 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) { + return; + } try { if (context.mounted) { diff --git a/lib/src/screens/new_wallet/new_wallet_page.dart b/lib/src/screens/new_wallet/new_wallet_page.dart index d9427af0a..b66aab4cf 100644 --- a/lib/src/screens/new_wallet/new_wallet_page.dart +++ b/lib/src/screens/new_wallet/new_wallet_page.dart @@ -68,14 +68,19 @@ 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 @@ -130,12 +135,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, @@ -169,10 +173,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( @@ -195,6 +199,80 @@ 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).repeat_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/restore/wallet_restore_from_keys_form.dart b/lib/src/screens/restore/wallet_restore_from_keys_form.dart index f8336a2e8..56e49b087 100644 --- a/lib/src/screens/restore/wallet_restore_from_keys_form.dart +++ b/lib/src/screens/restore/wallet_restore_from_keys_form.dart @@ -16,6 +16,9 @@ class WalletRestoreFromKeysFrom extends StatefulWidget { required this.onPrivateKeyChange, required this.displayPrivateKeyField, required this.onHeightOrDateEntered, + required this.displayWalletPassword, + required this.onRepeatedPasswordChange, + this.onPasswordChange, Key? key, }) : super(key: key); @@ -23,13 +26,17 @@ class WalletRestoreFromKeysFrom extends StatefulWidget { final WalletRestoreViewModel walletRestoreViewModel; final void Function(String)? onPrivateKeyChange; 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(), @@ -37,7 +44,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; @@ -47,9 +56,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(() { @@ -67,6 +89,14 @@ class WalletRestoreFromKeysFromState extends State<WalletRestoreFromKeysFrom> { viewKeyController.dispose(); privateKeyController.dispose(); spendKeyController.dispose(); + passwordTextEditingController?.dispose(); + if (passwordListener != null) { + passwordTextEditingController?.removeListener(passwordListener!); + } + + if (repeatedPasswordListener != null) { + repeatedPasswordTextEditingController?.removeListener(repeatedPasswordListener!); + } super.dispose(); } @@ -114,6 +144,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).repeat_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 1f22af0cb..ec40eb1c1 100644 --- a/lib/src/screens/restore/wallet_restore_from_seed_form.dart +++ b/lib/src/screens/restore/wallet_restore_from_seed_form.dart @@ -22,34 +22,43 @@ class WalletRestoreFromSeedForm extends StatefulWidget { required this.displayBlockHeightSelector, required this.displayPassphrase, required this.type, + required this.displayWalletPassword, required this.seedTypeViewModel, 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 bool displayPassphrase; final SeedTypeViewModel seedTypeViewModel; 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 createState() => + 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(), + passwordTextEditingController = displayWalletPassword ? TextEditingController() : null, + repeatedPasswordTextEditingController = displayWalletPassword ? TextEditingController() : null, passphraseController = TextEditingController(), seedTypeController = TextEditingController(); @@ -57,16 +66,30 @@ class WalletRestoreFromSeedFormState extends State<WalletRestoreFromSeedForm> { final GlobalKey<BlockchainHeightState> blockchainHeightKey; final TextEditingController languageController; final TextEditingController nameTextEditingController; + final TextEditingController? passwordTextEditingController; + final TextEditingController? repeatedPasswordTextEditingController; final TextEditingController seedTypeController; final TextEditingController passphraseController; final GlobalKey<FormState> formKey; late ReactionDisposer moneroSeedTypeReaction; String language; + void Function()? passwordListener; + void Function()? repeatedPasswordListener; @override void initState() { _setSeedType(widget.seedTypeViewModel.moneroSeedType); _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!); + } moneroSeedTypeReaction = reaction((_) => widget.seedTypeViewModel.moneroSeedType, (SeedType item) { _setSeedType(item); @@ -78,8 +101,16 @@ class WalletRestoreFromSeedFormState extends State<WalletRestoreFromSeedForm> { @override void dispose() { - super.dispose(); moneroSeedTypeReaction(); + + if (passwordListener != null) { + passwordTextEditingController?.removeListener(passwordListener!); + } + + if (repeatedPasswordListener != null) { + repeatedPasswordTextEditingController?.removeListener(repeatedPasswordListener!); + } + super.dispose(); } void onSeedChange(String seed) { @@ -177,6 +208,16 @@ class WalletRestoreFromSeedFormState extends State<WalletRestoreFromSeedForm> { ), ), ), + if (widget.displayWalletPassword) + ...[BaseTextFormField( + controller: passwordTextEditingController, + hintText: S.of(context).password, + obscureText: true), + BaseTextFormField( + controller: repeatedPasswordTextEditingController, + hintText: S.of(context).repeat_wallet_password, + obscureText: true)], + if (widget.displayLanguageSelector) if (!seedTypeController.value.text.contains("14") && widget.displayLanguageSelector) GestureDetector( onTap: () async { diff --git a/lib/src/screens/restore/wallet_restore_page.dart b/lib/src/screens/restore/wallet_restore_page.dart index 746b73dca..cd6383c0d 100644 --- a/lib/src/screens/restore/wallet_restore_page.dart +++ b/lib/src/screens/restore/wallet_restore_page.dart @@ -53,7 +53,10 @@ class WalletRestorePage extends BasePage { onLanguageChange: (String language) { final isPolyseed = language.startsWith("POLYSEED_"); _validateOnChange(isPolyseed: isPolyseed); - })); + }, + displayWalletPassword: walletRestoreViewModel.hasWalletPassword, + onPasswordChange: (String password) => walletRestoreViewModel.walletPassword = password, + onRepeatedPasswordChange: (String repeatedPassword) => walletRestoreViewModel.repeatedWalletPassword = repeatedPassword)); break; case WalletRestoreMode.keys: _pages.add(WalletRestoreFromKeysFrom( @@ -66,6 +69,9 @@ class WalletRestorePage extends BasePage { } }, 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: @@ -127,6 +133,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 = ''; diff --git a/lib/src/screens/root/root.dart b/lib/src/screens/root/root.dart index 58a6e7036..c90610733 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/settings/security_backup_page.dart b/lib/src/screens/settings/security_backup_page.dart index 470f49190..04ae53d77 100644 --- a/lib/src/screens/settings/security_backup_page.dart +++ b/lib/src/screens/settings/security_backup_page.dart @@ -10,6 +10,7 @@ import 'package:cake_wallet/src/screens/settings/widgets/settings_cell_with_arro import 'package:cake_wallet/src/screens/settings/widgets/settings_picker_cell.dart'; import 'package:cake_wallet/src/screens/settings/widgets/settings_switcher_cell.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'; @@ -41,15 +42,16 @@ class SecurityBackupPage extends BasePage { _securitySettingsViewModel.shouldRequireTOTP2FAForAllSecurityAndBackupSettings, ), ), - 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, + ), ), - ), SettingsCellWithArrow( title: S.current.settings_change_pin, handler: (_) => _authService.authenticateAction( @@ -119,6 +121,5 @@ class SecurityBackupPage extends BasePage { ], ), ); - } } diff --git a/lib/src/screens/wallet/wallet_edit_page.dart b/lib/src/screens/wallet/wallet_edit_page.dart index 7c90ba2c5..2d1bb9e47 100644 --- a/lib/src/screens/wallet/wallet_edit_page.dart +++ b/lib/src/screens/wallet/wallet_edit_page.dart @@ -4,6 +4,12 @@ import 'package:cake_wallet/core/wallet_name_validator.dart'; import 'package:cake_wallet/palette.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/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 +100,38 @@ 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(); + walletEditViewModel.resetState(); + } } catch (e) {} } } @@ -120,11 +155,11 @@ class WalletEditPage extends BasePage { Future<void> _removeWallet(BuildContext context) async { authService.authenticateAction(context, onAuthSuccess: (isAuthenticatedSuccessfully) async { - if (!isAuthenticatedSuccessfully) { - return; - } + if (!isAuthenticatedSuccessfully) { + return; + } - _onSuccessfulAuth(context); + _onSuccessfulAuth(context); }, conditionToDetermineIfToUse2FA: false, ); diff --git a/lib/src/screens/wallet_list/wallet_list_page.dart b/lib/src/screens/wallet_list/wallet_list_page.dart index 2a4841608..0d6d9e912 100644 --- a/lib/src/screens/wallet_list/wallet_list_page.dart +++ b/lib/src/screens/wallet_list/wallet_list_page.dart @@ -1,7 +1,10 @@ import 'package:cake_wallet/entities/wallet_list_order_types.dart'; import 'package:cake_wallet/src/screens/dashboard/widgets/filter_list_widget.dart'; import 'package:cake_wallet/src/screens/wallet_list/filtered_list.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/src/screens/auth/auth_page.dart'; import 'package:cake_wallet/core/auth_service.dart'; import 'package:cake_wallet/themes/extensions/filter_theme.dart'; import 'package:cake_wallet/themes/extensions/receive_page_theme.dart'; @@ -336,6 +339,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..3e6966f9d --- /dev/null +++ b/lib/src/screens/wallet_unlock/wallet_unlock_page.dart @@ -0,0 +1,238 @@ +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/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: ResponsiveLayoutUtilBase.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 0467b18a2..f79da8069 100644 --- a/lib/src/widgets/address_text_field.dart +++ b/lib/src/widgets/address_text_field.dart @@ -1,5 +1,5 @@ -import 'package:cake_wallet/themes/extensions/cake_text_theme.dart'; import 'package:cake_wallet/utils/device_info.dart'; +import 'package:cake_wallet/themes/extensions/cake_text_theme.dart'; import 'package:cake_wallet/utils/responsive_layout_util.dart'; import 'package:flutter/services.dart'; import 'package:flutter/material.dart'; @@ -19,7 +19,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/store/settings_store.dart b/lib/store/settings_store.dart index 84b0dac2b..c9d2a758c 100644 --- a/lib/store/settings_store.dart +++ b/lib/store/settings_store.dart @@ -575,6 +575,7 @@ abstract class SettingsStoreBase with Store { static const defaultActionsMode = 11; static const defaultPinCodeTimeOutDuration = PinCodeRequiredDuration.tenminutes; static const defaultAutoGenerateSubaddressStatus = AutoGenerateSubaddressStatus.initialized; + static final walletPasswordDirectInput = Platform.isLinux; static const defaultSeedPhraseLength = SeedPhraseLength.twelveWords; static const defaultMoneroSeedType = SeedType.defaultSeedType; diff --git a/lib/tron/cw_tron.dart b/lib/tron/cw_tron.dart index 52b4f59f7..c6ac89342 100644 --- a/lib/tron/cw_tron.dart +++ b/lib/tron/cw_tron.dart @@ -4,15 +4,17 @@ class CWTron extends Tron { @override List<String> getTronWordList(String language) => EVMChainMnemonics.englishWordlist; - WalletService createTronWalletService(Box<WalletInfo> walletInfoSource) => - TronWalletService(walletInfoSource, client: TronClient()); + @override + WalletService createTronWalletService(Box<WalletInfo> walletInfoSource, bool isDirect) => + TronWalletService(walletInfoSource, client: TronClient(), isDirect: isDirect); @override WalletCredentials createTronNewWalletCredentials({ required String name, WalletInfo? walletInfo, + String? password, }) => - TronNewWalletCredentials(name: name, walletInfo: walletInfo); + TronNewWalletCredentials(name: name, walletInfo: walletInfo, password: password); @override WalletCredentials createTronRestoreWalletFromSeedCredentials({ diff --git a/lib/utils/exception_handler.dart b/lib/utils/exception_handler.dart index 04b624e48..908144dbb 100644 --- a/lib/utils/exception_handler.dart +++ b/lib/utils/exception_handler.dart @@ -173,6 +173,7 @@ class ExceptionHandler { "OS Error: Network is unreachable", "ClientException: Write failed, uri=http", "Connection terminated during handshake", + "Corrupted wallets seeds", ]; static Future<void> _addDeviceInfo(File file) async { diff --git a/lib/utils/package_info.dart b/lib/utils/package_info.dart index 8b911f887..5f3f0e743 100644 --- a/lib/utils/package_info.dart +++ b/lib/utils/package_info.dart @@ -1,5 +1,5 @@ import 'dart:io'; -import 'package:package_info/package_info.dart' as __package_info__; +import 'package:package_info_plus/package_info_plus.dart' as __package_info__; abstract class _EnvKeys { static const kWinAppName = 'CW_WIN_APP_NAME'; diff --git a/lib/view_model/wallet_creation_vm.dart b/lib/view_model/wallet_creation_vm.dart index 36661ac7e..43386494e 100644 --- a/lib/view_model/wallet_creation_vm.dart +++ b/lib/view_model/wallet_creation_vm.dart @@ -1,5 +1,7 @@ import 'package:cake_wallet/bitcoin/bitcoin.dart'; import 'package:cake_wallet/core/wallet_creation_service.dart'; +import 'package:cake_wallet/generated/i18n.dart'; +import 'package:cake_wallet/store/settings_store.dart'; import 'package:cake_wallet/di.dart'; import 'package:cake_wallet/entities/background_tasks.dart'; import 'package:cake_wallet/view_model/restore/restore_wallet.dart'; @@ -37,6 +39,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; @@ -59,6 +69,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..e5bfcd4e3 100644 --- a/lib/view_model/wallet_list/wallet_edit_view_model.dart +++ b/lib/view_model/wallet_list/wallet_edit_view_model.dart @@ -32,10 +32,11 @@ 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); + walletItem.type, walletItem.name, newName, + password: password); _walletListViewModel.updateList(); } diff --git a/lib/view_model/wallet_new_vm.dart b/lib/view_model/wallet_new_vm.dart index 4729a38b2..a618695b1 100644 --- a/lib/view_model/wallet_new_vm.dart +++ b/lib/view_model/wallet_new_vm.dart @@ -66,25 +66,25 @@ abstract class WalletNewVMBase extends WalletCreationVM with Store { switch (type) { case WalletType.monero: return monero!.createMoneroNewWalletCredentials( - name: name, language: options!.first as String, isPolyseed: options.last as bool); + name: name, language: options!.first as String, password: walletPassword, isPolyseed: options.last as bool); 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!.first as String); + return haven!.createHavenNewWalletCredentials( + name: name, language: options!.first as String, password: walletPassword); case WalletType.ethereum: - return ethereum!.createEthereumNewWalletCredentials(name: name); + return ethereum!.createEthereumNewWalletCredentials(name: name, password: walletPassword); case WalletType.bitcoinCash: - return bitcoinCash!.createBitcoinCashNewWalletCredentials(name: name); + return bitcoinCash!.createBitcoinCashNewWalletCredentials(name: name, password: walletPassword); case WalletType.nano: case WalletType.banano: return nano!.createNanoNewWalletCredentials(name: name); case WalletType.polygon: - return polygon!.createPolygonNewWalletCredentials(name: name); + return polygon!.createPolygonNewWalletCredentials(name: name, password: walletPassword); case WalletType.solana: - return solana!.createSolanaNewWalletCredentials(name: name); + return solana!.createSolanaNewWalletCredentials(name: name, password: walletPassword); case WalletType.tron: return tron!.createTronNewWalletCredentials(name: name); case WalletType.wownero: diff --git a/lib/view_model/wallet_restore_view_model.dart b/lib/view_model/wallet_restore_view_model.dart index 25a555b44..a38baabd8 100644 --- a/lib/view_model/wallet_restore_view_model.dart +++ b/lib/view_model/wallet_restore_view_model.dart @@ -86,7 +86,7 @@ abstract class WalletRestoreViewModelBase extends WalletCreationVM with Store { @override WalletCredentials getCredentials(dynamic options) { - final password = generateWalletPassword(); + final password = walletPassword ?? generateWalletPassword(); String? passphrase = options['passphrase'] as String?; 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..f5e7bb917 --- /dev/null +++ b/lib/view_model/wallet_unlock_loadable_view_model.dart @@ -0,0 +1,63 @@ +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..d6f5f0e7d --- /dev/null +++ b/lib/view_model/wallet_unlock_verifiable_view_model.dart @@ -0,0 +1,60 @@ +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..bfce34c34 --- /dev/null +++ b/linux/CMakeLists.txt @@ -0,0 +1,144 @@ +# 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(FILES "${CMAKE_CURRENT_SOURCE_DIR}/../scripts/monero_c/release/monero/x86_64-linux-gnu_libwallet2_api_c.so" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" RENAME "monero_libwallet2_api_c.so" + COMPONENT Runtime) + +install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/../scripts/monero_c/release/wownero/x86_64-linux-gnu_libwallet2_api_c.so" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" RENAME "wownero_libwallet2_api_c.so" + COMPONENT Runtime) + +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..01b922894 --- /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 <devicelocale/devicelocale_plugin.h> +#include <flutter_local_authentication/flutter_local_authentication_plugin.h> +#include <flutter_secure_storage_linux/flutter_secure_storage_linux_plugin.h> +#include <url_launcher_linux/url_launcher_plugin.h> + +void fl_register_plugins(FlPluginRegistry* registry) { + g_autoptr(FlPluginRegistrar) devicelocale_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "DevicelocalePlugin"); + devicelocale_plugin_register_with_registrar(devicelocale_registrar); + g_autoptr(FlPluginRegistrar) flutter_local_authentication_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "FlutterLocalAuthenticationPlugin"); + flutter_local_authentication_plugin_register_with_registrar(flutter_local_authentication_registrar); + g_autoptr(FlPluginRegistrar) flutter_secure_storage_linux_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "FlutterSecureStorageLinuxPlugin"); + flutter_secure_storage_linux_plugin_register_with_registrar(flutter_secure_storage_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..f52be7481 --- /dev/null +++ b/linux/flutter/generated_plugins.cmake @@ -0,0 +1,28 @@ +# +# Generated file, do not edit. +# + +list(APPEND FLUTTER_PLUGIN_LIST + devicelocale + flutter_local_authentication + flutter_secure_storage_linux + url_launcher_linux +) + +list(APPEND FLUTTER_FFI_PLUGIN_LIST + sp_scanner +) + +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 3d63ab0ea..86b3462ac 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -13,7 +13,6 @@ import flutter_inappwebview_macos import flutter_local_authentication import flutter_secure_storage_macos import in_app_review -import package_info import package_info_plus import path_provider_foundation import share_plus @@ -30,7 +29,6 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { FlutterLocalAuthenticationPlugin.register(with: registry.registrar(forPlugin: "FlutterLocalAuthenticationPlugin")) FlutterSecureStoragePlugin.register(with: registry.registrar(forPlugin: "FlutterSecureStoragePlugin")) InAppReviewPlugin.register(with: registry.registrar(forPlugin: "InAppReviewPlugin")) - FLTPackageInfoPlugin.register(with: registry.registrar(forPlugin: "FLTPackageInfoPlugin")) FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin")) PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) SharePlusMacosPlugin.register(with: registry.registrar(forPlugin: "SharePlusMacosPlugin")) diff --git a/macos/Runner/RunnerBase.entitlements b/macos/Runner/RunnerBase.entitlements index 186b61b17..22be818c6 100644 --- a/macos/Runner/RunnerBase.entitlements +++ b/macos/Runner/RunnerBase.entitlements @@ -11,4 +11,4 @@ <string>$(AppIdentifierPrefix)${BUNDLE_ID}</string> </array> </dict> -</plist> +</plist> \ No newline at end of file diff --git a/model_generator.sh b/model_generator.sh index 373a72018..293923d1e 100755 --- a/model_generator.sh +++ b/model_generator.sh @@ -1,3 +1,4 @@ +#!/bin/bash cd cw_core; flutter pub get; flutter packages pub run build_runner build --delete-conflicting-outputs; cd .. cd cw_evm; 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 .. diff --git a/pubspec_base.yaml b/pubspec_base.yaml index a2e804350..9a0116efd 100644 --- a/pubspec_base.yaml +++ b/pubspec_base.yaml @@ -31,8 +31,7 @@ dependencies: flutter_local_authentication: git: url: https://github.com/cake-tech/flutter_local_authentication - package_info: ^2.0.0 - #package_info_plus: ^1.4.2 + package_info_plus: ^8.0.1 devicelocale: git: url: https://github.com/cake-tech/flutter-devicelocale @@ -80,6 +79,7 @@ dependencies: path_provider_android: ^2.2.1 shared_preferences_android: 2.0.17 url_launcher_android: 6.0.24 + url_launcher_linux: 3.1.1 # https://github.com/flutter/flutter/issues/153083 sensitive_clipboard: ^1.0.0 walletconnect_flutter_v2: ^2.1.4 eth_sig_util: ^0.0.9 diff --git a/res/values/strings_ar.arb b/res/values/strings_ar.arb index 8ba4888ee..8063d5d73 100644 --- a/res/values/strings_ar.arb +++ b/res/values/strings_ar.arb @@ -236,6 +236,7 @@ "enter_code": "ادخل الرمز", "enter_seed_phrase": "أدخل عبارة البذور الخاصة بك", "enter_totp_code": "الرجاء إدخال رمز TOTP.", + "enter_wallet_password": "أدخل كلمة مرور المحفظة", "enter_your_note": "أدخل ملاحظتك ...", "enter_your_pin": "أدخل كود الرقم السري", "enter_your_pin_again": "أدخل PIN الخاص بك مرة أخرى", @@ -340,6 +341,7 @@ "insufficientFundsForRentError": "ليس لديك ما يكفي من SOL لتغطية رسوم المعاملة والإيجار للحساب. يرجى إضافة المزيد من sol إلى محفظتك أو تقليل مبلغ sol الذي ترسله", "introducing_cake_pay": "نقدم لكم Cake Pay!", "invalid_input": "مدخل غير صالح", + "invalid_password": "رمز مرور خاطئ", "invoice_details": "تفاصيل الفاتورة", "is_percentage": "يكون", "last_30_days": "آخر 30 يومًا", @@ -507,6 +509,8 @@ "rename": "إعادة تسمية", "rep_warning": "تحذير تمثيلي", "rep_warning_sub": "لا يبدو أن ممثلك في وضع جيد. اضغط هنا لاختيار واحدة جديدة", + "repeat_wallet_password": "كرر كلمة مرور المحفظة", + "repeated_password_is_incorrect": "كلمة المرور المتكررة غير صحيحة. يرجى تكرار كلمة مرور المحفظة مرة أخرى.", "require_for_adding_contacts": "تتطلب إضافة جهات اتصال", "require_for_all_security_and_backup_settings": "مطلوب لجميع إعدادات الأمان والنسخ الاحتياطي", "require_for_assessing_wallet": "تتطلب الوصول إلى المحفظة", @@ -805,6 +809,7 @@ "unavailable_balance_description": ".ﺎﻫﺪﻴﻤﺠﺗ ءﺎﻐﻟﺇ ﺭﺮﻘﺗ ﻰﺘﺣ ﺕﻼﻣﺎﻌﻤﻠﻟ ﻝﻮﺻﻮﻠﻟ ﺔﻠﺑﺎﻗ ﺮﻴﻏ ﺓﺪﻤﺠﻤﻟﺍ ﺓﺪﺻﺭﻷﺍ ﻞﻈﺗ ﺎﻤﻨﻴﺑ ،ﺎﻬﺑ ﺔﺻﺎﺨﻟﺍ ﺕﻼﻣﺎﻌﻤﻟﺍ ﻝﺎﻤﺘﻛﺍ ﺩﺮﺠﻤﺑ ﺔﺣﺎﺘﻣ ﺔﻠﻔﻘﻤﻟﺍ ﺓﺪﺻﺭﻷﺍ ﺢﺒﺼﺘﺳ .ﻚﺑ ﺔﺻﺎﺨﻟﺍ ﺕﻼﻤﻌﻟﺍ ﻲﻓ ﻢﻜﺤﺘﻟﺍ ﺕﺍﺩﺍﺪﻋﺇ ﻲﻓ ﻂﺸﻧ ﻞﻜﺸﺑ ﺎﻫﺪﻴﻤﺠﺘﺑ ﺖﻤﻗ", "unconfirmed": "رصيد غير مؤكد", "understand": "لقد فهمت", + "unlock": "الغاء القفل", "unmatched_currencies": "عملة محفظتك الحالية لا تتطابق مع عملة QR الممسوحة ضوئيًا", "unspent_change": "يتغير", "unspent_coins_details_title": "تفاصيل العملات الغير المنفقة", @@ -846,6 +851,7 @@ "wallet_menu": "قائمة", "wallet_name": "اسم المحفظة", "wallet_name_exists": "توجد بالفعل محفظة بهذا الاسم. الرجاء اختيار اسم مختلف أو إعادة تسمية المحفظة الأخرى أولاً.", + "wallet_password_is_empty": "كلمة مرور المحفظة فارغة. يجب ألا تكون كلمة مرور المحفظة فارغة", "wallet_recovery_height": "ارتفاع الاسترداد", "wallet_restoration_store_incorrect_seed_length": "طول السييد غير صحيح", "wallet_seed": "سييد المحفظة", diff --git a/res/values/strings_bg.arb b/res/values/strings_bg.arb index af3f7cc4d..8ab6eebdc 100644 --- a/res/values/strings_bg.arb +++ b/res/values/strings_bg.arb @@ -236,6 +236,7 @@ "enter_code": "Въведете код", "enter_seed_phrase": "Въведете вашата фраза за семена", "enter_totp_code": "Моля, въведете TOTP кода.", + "enter_wallet_password": "Въведете паролата за портфейла", "enter_your_note": "Въвеждане на бележка…", "enter_your_pin": "Въведете PIN", "enter_your_pin_again": "Въведете своя PIN отново", @@ -340,6 +341,7 @@ "insufficientFundsForRentError": "Нямате достатъчно SOL, за да покриете таксата за транзакцията и наемането на сметката. Моля, добавете повече SOL към портфейла си или намалете сумата на SOL, която изпращате", "introducing_cake_pay": "Запознайте се с Cake Pay!", "invalid_input": "Невалиден вход", + "invalid_password": "Невалидна парола", "invoice_details": "IДанни за фактура", "is_percentage": "е", "last_30_days": "Последните 30 дни", @@ -507,6 +509,8 @@ "rename": "Промяна на името", "rep_warning": "Представително предупреждение", "rep_warning_sub": "Вашият представител изглежда не е в добро състояние. Докоснете тук, за да изберете нов", + "repeat_wallet_password": "Повторете паролата на портфейла", + "repeated_password_is_incorrect": "Многократната парола е неправилна. Моля, повторете отново паролата за портфейла.", "require_for_adding_contacts": "Изисква се за добавяне на контакти", "require_for_all_security_and_backup_settings": "Изисква се за всички настройки за сигурност и архивиране", "require_for_assessing_wallet": "Изискване за достъп до портфейла", @@ -805,6 +809,7 @@ "unavailable_balance_description": "Неналично салдо: Тази обща сума включва средства, които са заключени в чакащи транзакции и тези, които сте замразили активно в настройките за контрол на монетите. Заключените баланси ще станат достъпни, след като съответните им транзакции бъдат завършени, докато замразените баланси остават недостъпни за транзакции, докато не решите да ги размразите.", "unconfirmed": "Непотвърден баланс", "understand": "Разбирам", + "unlock": "Отключване", "unmatched_currencies": "Валутата на този портфейл не съвпада с тази от сканирания QR код", "unspent_change": "Промяна", "unspent_coins_details_title": "Подробности за неизползваните монети", @@ -846,6 +851,7 @@ "wallet_menu": "Меню", "wallet_name": "Име на портфейл", "wallet_name_exists": "Вече има портфейл с това име. Моля, изберете друго име или преименувайте другия портфейл.", + "wallet_password_is_empty": "Паролата за портфейл е празна. Паролата за портфейл не трябва да е празна", "wallet_recovery_height": "Височина на възстановяване", "wallet_restoration_store_incorrect_seed_length": "Грешна дължина на seed-а", "wallet_seed": "Seed на портфейла", diff --git a/res/values/strings_cs.arb b/res/values/strings_cs.arb index 032cfd0a5..b1d298ac4 100644 --- a/res/values/strings_cs.arb +++ b/res/values/strings_cs.arb @@ -236,6 +236,7 @@ "enter_code": "Zadejte kód", "enter_seed_phrase": "Zadejte svou frázi semen", "enter_totp_code": "Zadejte kód TOTP.", + "enter_wallet_password": "Zadejte heslo peněženky", "enter_your_note": "Zadejte poznámku…", "enter_your_pin": "Zadejte svůj PIN", "enter_your_pin_again": "Zadejte znovu svůj PIN", @@ -340,6 +341,7 @@ "insufficientFundsForRentError": "Nemáte dostatek SOL na pokrytí transakčního poplatku a nájemného za účet. Laskavě přidejte do své peněženky více SOL nebo snižte množství Sol, kterou odesíláte", "introducing_cake_pay": "Představujeme Cake Pay!", "invalid_input": "Neplatný vstup", + "invalid_password": "Neplatné heslo", "invoice_details": "detaily faktury", "is_percentage": "je", "last_30_days": "Posledních 30 dnů", @@ -507,6 +509,8 @@ "rename": "Přejmenovat", "rep_warning": "Reprezentativní varování", "rep_warning_sub": "Zdá se, že váš zástupce není v dobrém stavu. Klepnutím zde vyberte nový", + "repeat_wallet_password": "Opakujte heslo peněženky", + "repeated_password_is_incorrect": "Opakované heslo je nesprávné. Znovu opakujte heslo peněženky.", "require_for_adding_contacts": "Vyžadovat pro přidání kontaktů", "require_for_all_security_and_backup_settings": "Vyžadovat všechna nastavení zabezpečení a zálohování", "require_for_assessing_wallet": "Vyžadovat pro přístup k peněžence", @@ -805,6 +809,7 @@ "unavailable_balance_description": "Nedostupný zůstatek: Tento součet zahrnuje prostředky, které jsou uzamčeny v nevyřízených transakcích a ty, které jste aktivně zmrazili v nastavení kontroly mincí. Uzamčené zůstatky budou k dispozici po dokončení příslušných transakcí, zatímco zmrazené zůstatky zůstanou pro transakce nepřístupné, dokud se nerozhodnete je uvolnit.", "unconfirmed": "Nepotvrzený zůstatek", "understand": "Rozumím", + "unlock": "Odemknout", "unmatched_currencies": "Měna vaší současné peněženky neodpovídá té v naskenovaném QR kódu", "unspent_change": "Změna", "unspent_coins_details_title": "Podrobnosti o neutracených mincích", @@ -846,6 +851,7 @@ "wallet_menu": "Menu", "wallet_name": "Název peněženky", "wallet_name_exists": "Peněženka s tímto názvem už existuje. Prosím zvolte si jiný název, nebo nejprve přejmenujte nejprve druhou peněženku.", + "wallet_password_is_empty": "Heslo peněženky je prázdné. Heslo peněženky by nemělo být prázdné", "wallet_recovery_height": "Výška zotavení", "wallet_restoration_store_incorrect_seed_length": "Nesprávná délka seedu", "wallet_seed": "Seed peněženky", diff --git a/res/values/strings_de.arb b/res/values/strings_de.arb index 9f4f8caca..78116868f 100644 --- a/res/values/strings_de.arb +++ b/res/values/strings_de.arb @@ -236,6 +236,7 @@ "enter_code": "Code eingeben", "enter_seed_phrase": "Geben Sie Ihre Seed-Phrase ein", "enter_totp_code": "Bitte geben Sie den TOTP-Code ein.", + "enter_wallet_password": "Geben Sie das Brieftaschenkennwort ein", "enter_your_note": "Geben Sie Ihre Bemerkung ein…", "enter_your_pin": "PIN eingeben", "enter_your_pin_again": "Geben Sie Ihre PIN erneut ein", @@ -340,6 +341,7 @@ "insufficientFundsForRentError": "Sie haben nicht genug SOL, um die Transaktionsgebühr und die Miete für das Konto zu decken. Bitte fügen Sie mehr Sol zu Ihrer Brieftasche hinzu oder reduzieren Sie den von Ihnen gesendeten Sol -Betrag", "introducing_cake_pay": "Einführung von Cake Pay!", "invalid_input": "Ungültige Eingabe", + "invalid_password": "Ungültiges Passwort", "invoice_details": "Rechnungs-Details", "is_percentage": "ist", "last_30_days": "Letzte 30 Tage", @@ -508,6 +510,8 @@ "rename": "Umbenennen", "rep_warning": "Repräsentative Warnung", "rep_warning_sub": "Ihr Vertreter scheint nicht gut zu sein. Tippen Sie hier, um eine neue auszuwählen", + "repeat_wallet_password": "Wiederholen Sie das Brieftaschenkennwort", + "repeated_password_is_incorrect": "Wiederholtes Passwort ist falsch. Bitte wiederholen Sie das Brieftaschenkennwort erneut.", "require_for_adding_contacts": "Erforderlich zum Hinzufügen von Kontakten", "require_for_all_security_and_backup_settings": "Für alle Sicherheits- und Sicherungseinstellungen erforderlich", "require_for_assessing_wallet": "Für den Zugriff auf die Wallet erforderlich", @@ -807,6 +811,7 @@ "unconfirmed": "Unbestätigter Saldo", "und": "und", "understand": "Ich verstehe", + "unlock": "Freischalten", "unmatched_currencies": "Die Währung Ihres aktuellen Wallets stimmt nicht mit der des gescannten QR überein", "unspent_change": "Wechselgeld", "unspent_coins_details_title": "Details zu nicht ausgegebenen Coins", @@ -849,6 +854,7 @@ "wallet_menu": "Wallet-Menü", "wallet_name": "Walletname", "wallet_name_exists": "Wallet mit diesem Namen existiert bereits", + "wallet_password_is_empty": "Brieftaschenkennwort ist leer. Brieftaschenkennwort sollte nicht leer sein", "wallet_recovery_height": "Erstellungshöhe", "wallet_restoration_store_incorrect_seed_length": "Falsche Seed-Länge", "wallet_seed": "Wallet-Seed", diff --git a/res/values/strings_en.arb b/res/values/strings_en.arb index 472fa33eb..879bdf10f 100644 --- a/res/values/strings_en.arb +++ b/res/values/strings_en.arb @@ -236,6 +236,7 @@ "enter_code": "Enter code", "enter_seed_phrase": "Enter your seed phrase", "enter_totp_code": "Please enter the TOTP Code.", + "enter_wallet_password": "Enter the wallet password", "enter_your_note": "Enter your note…", "enter_your_pin": "Enter your PIN", "enter_your_pin_again": "Enter your pin again", @@ -340,6 +341,7 @@ "insufficientFundsForRentError": "You do not have enough SOL to cover the transaction fee and rent for the account. Kindly add more SOL to your wallet or reduce the SOL amount you\\'re sending", "introducing_cake_pay": "Introducing Cake Pay!", "invalid_input": "Invalid input", + "invalid_password": "Invalid password", "invoice_details": "Invoice details", "is_percentage": "is", "last_30_days": "Last 30 days", @@ -507,6 +509,8 @@ "rename": "Rename", "rep_warning": "Representative Warning", "rep_warning_sub": "Your representative does not appear to be in good standing. Tap here to select a new one", + "repeat_wallet_password": "Repeat the wallet password", + "repeated_password_is_incorrect": "Repeated password is incorrect. Please repeat the wallet password again.", "require_for_adding_contacts": "Require for adding contacts", "require_for_all_security_and_backup_settings": "Require for all security and backup settings", "require_for_assessing_wallet": "Require for accessing wallet", @@ -806,6 +810,7 @@ "unavailable_balance_description": "Unavailable Balance: This total includes funds that are locked in pending transactions and those you have actively frozen in your coin control settings. Locked balances will become available once their respective transactions are completed, while frozen balances remain inaccessible for transactions until you decide to unfreeze them.", "unconfirmed": "Unconfirmed Balance", "understand": "I understand", + "unlock": "Unlock", "unmatched_currencies": "Your current wallet's currency does not match that of the scanned QR", "unspent_change": "Change", "unspent_coins_details_title": "Unspent coins details", @@ -847,6 +852,7 @@ "wallet_menu": "Menu", "wallet_name": "Wallet name", "wallet_name_exists": "A wallet with that name already exists. Please choose a different name or rename the other wallet first.", + "wallet_password_is_empty": "Wallet password is empty. Wallet password should not be empty", "wallet_recovery_height": "Recovery Height", "wallet_restoration_store_incorrect_seed_length": "Incorrect seed length", "wallet_seed": "Wallet seed", diff --git a/res/values/strings_es.arb b/res/values/strings_es.arb index b26434e43..065b72088 100644 --- a/res/values/strings_es.arb +++ b/res/values/strings_es.arb @@ -236,6 +236,7 @@ "enter_code": "Ingresar código", "enter_seed_phrase": "Ingrese su frase de semillas", "enter_totp_code": "Ingrese el código TOTP.", + "enter_wallet_password": "Ingrese la contraseña de la billetera", "enter_your_note": "Ingresa tu nota…", "enter_your_pin": "Introduce tu PIN", "enter_your_pin_again": "Ingrese su PIN nuevamente", @@ -340,6 +341,7 @@ "insufficientFundsForRentError": "No tiene suficiente SOL para cubrir la tarifa de transacción y alquilar para la cuenta. Por favor, agregue más sol a su billetera o reduzca la cantidad de sol que está enviando", "introducing_cake_pay": "¡Presentamos Cake Pay!", "invalid_input": "Entrada inválida", + "invalid_password": "Contraseña invalida", "invoice_details": "Detalles de la factura", "is_percentage": "es", "last_30_days": "Últimos 30 días", @@ -508,6 +510,8 @@ "rename": "Rebautizar", "rep_warning": "Advertencia representativa", "rep_warning_sub": "Su representante no parece estar en buena posición. Toque aquí para seleccionar uno nuevo", + "repeat_wallet_password": "Repita la contraseña de billetera", + "repeated_password_is_incorrect": "La contraseña repetida es incorrecta. Repita la contraseña de la billetera nuevamente.", "require_for_adding_contacts": "Requerido para agregar contactos", "require_for_all_security_and_backup_settings": "Requerido para todas las configuraciones de seguridad y copia de seguridad", "require_for_assessing_wallet": "Requerido para acceder a la billetera", @@ -806,6 +810,7 @@ "unavailable_balance_description": "Saldo no disponible: este total incluye fondos que están bloqueados en transacciones pendientes y aquellos que usted ha congelado activamente en su configuración de control de monedas. Los saldos bloqueados estarán disponibles una vez que se completen sus respectivas transacciones, mientras que los saldos congelados permanecerán inaccesibles para las transacciones hasta que usted decida descongelarlos.", "unconfirmed": "Saldo no confirmado", "understand": "Entiendo", + "unlock": "desbloquear", "unmatched_currencies": "La moneda de su billetera actual no coincide con la del QR escaneado", "unspent_change": "Cambiar", "unspent_coins_details_title": "Detalles de monedas no gastadas", @@ -847,6 +852,7 @@ "wallet_menu": "Menú de billetera", "wallet_name": "Nombre de la billetera", "wallet_name_exists": "Wallet con ese nombre ya ha existido", + "wallet_password_is_empty": "La contraseña de billetera está vacía. La contraseña de la billetera no debe estar vacía", "wallet_recovery_height": "Altura de recuperación", "wallet_restoration_store_incorrect_seed_length": "Longitud de semilla incorrecta", "wallet_seed": "Semilla de billetera", diff --git a/res/values/strings_fr.arb b/res/values/strings_fr.arb index f052b1781..c5d2c9f41 100644 --- a/res/values/strings_fr.arb +++ b/res/values/strings_fr.arb @@ -236,6 +236,7 @@ "enter_code": "Entrez le code", "enter_seed_phrase": "Entrez votre phrase secrète (seed)", "enter_totp_code": "Veuillez entrer le code TOTP.", + "enter_wallet_password": "Entrez le mot de passe du portefeuille", "enter_your_note": "Entrez votre note…", "enter_your_pin": "Entrez votre code PIN", "enter_your_pin_again": "Entrez à nouveau votre code PIN", @@ -340,6 +341,7 @@ "insufficientFundsForRentError": "Vous n'avez pas assez de SOL pour couvrir les frais de transaction et le loyer pour le compte. Veuillez ajouter plus de Sol à votre portefeuille ou réduire la quantité de sol que vous envoyez", "introducing_cake_pay": "Présentation de Cake Pay !", "invalid_input": "Entrée invalide", + "invalid_password": "Mot de passe incorrect", "invoice_details": "Détails de la facture", "is_percentage": "est", "last_30_days": "30 derniers jours", @@ -507,6 +509,8 @@ "rename": "Renommer", "rep_warning": "Avertissement représentatif", "rep_warning_sub": "Votre représentant ne semble pas être en règle. Appuyez ici pour en sélectionner un nouveau", + "repeat_wallet_password": "Répétez le mot de passe du portefeuille", + "repeated_password_is_incorrect": "Le mot de passe répété est incorrect. Veuillez répéter le mot de passe du portefeuille.", "require_for_adding_contacts": "Requis pour ajouter des contacts", "require_for_all_security_and_backup_settings": "Exiger pour tous les paramètres de sécurité et de sauvegarde", "require_for_assessing_wallet": "Nécessaire pour accéder au portefeuille", @@ -805,6 +809,7 @@ "unavailable_balance_description": "Solde indisponible : ce total comprend les fonds bloqués dans les transactions en attente et ceux que vous avez activement gelés dans vos paramètres de contrôle des pièces. Les soldes bloqués deviendront disponibles une fois leurs transactions respectives terminées, tandis que les soldes gelés resteront inaccessibles aux transactions jusqu'à ce que vous décidiez de les débloquer.", "unconfirmed": "Solde non confirmé", "understand": "J'ai compris", + "unlock": "Ouvrir", "unmatched_currencies": "La devise de votre portefeuille (wallet) actuel ne correspond pas à celle du QR code scanné", "unspent_change": "Monnaie", "unspent_coins_details_title": "Détails des pièces (coins) non dépensées", @@ -846,6 +851,7 @@ "wallet_menu": "Menu", "wallet_name": "Nom du Portefeuille (Wallet)", "wallet_name_exists": "Un portefeuille (wallet) portant ce nom existe déjà", + "wallet_password_is_empty": "Le mot de passe du portefeuille est vide. Le mot de passe du portefeuille ne doit pas être vide", "wallet_recovery_height": "Hauteur de récupération", "wallet_restoration_store_incorrect_seed_length": "Longueur de phrase secrète (seed) incorrecte", "wallet_seed": "Phrase secrète (seed) du portefeuille (wallet)", diff --git a/res/values/strings_ha.arb b/res/values/strings_ha.arb index ef67fac7d..b932df65d 100644 --- a/res/values/strings_ha.arb +++ b/res/values/strings_ha.arb @@ -236,6 +236,7 @@ "enter_code": "Shigar da lamba", "enter_seed_phrase": "Shigar da Sert Sentarku", "enter_totp_code": "Da fatan za a shigar da lambar tnp.", + "enter_wallet_password": "Shigar da kalmar sirri ta walat", "enter_your_note": "Shigar da bayanin kula…", "enter_your_pin": "Shigar da PIN", "enter_your_pin_again": "Shigar da PIN ɗinku na sake", @@ -340,6 +341,7 @@ "insufficientFundsForRentError": "Ba ku da isasshen Sol don rufe kuɗin ma'amala da haya don asusun. Da kyau ƙara ƙarin sool zuwa walat ɗinku ko rage adadin Sol ɗin da kuke aikawa", "introducing_cake_pay": "Gabatar da Cake Pay!", "invalid_input": "Shigar da ba daidai ba", + "invalid_password": "Kalmar sirri mara inganci", "invoice_details": "Bayanin wadannan", "is_percentage": "shine", "last_30_days": "Kwanaki 30 na ƙarshe", @@ -509,6 +511,8 @@ "rename": "Sake suna", "rep_warning": "Gargadi Wakilin", "rep_warning_sub": "Wakilinku bai bayyana ya kasance cikin kyakkyawan yanayi ba. Matsa nan don zaɓar sabon", + "repeat_wallet_password": "Maimaita kalmar sirri", + "repeated_password_is_incorrect": "Maimaita kalmar sirri ba daidai ba ce. Da fatan za a sake maimaita kalmar sirri.", "require_for_adding_contacts": "Bukatar ƙara lambobin sadarwa", "require_for_all_security_and_backup_settings": "Bukatar duk tsaro da saitunan wariyar ajiya", "require_for_assessing_wallet": "Bukatar samun damar walat", @@ -807,6 +811,7 @@ "unavailable_balance_description": "Ma'auni Babu: Wannan jimlar ya haɗa da kuɗi waɗanda ke kulle a cikin ma'amaloli da ke jiran aiki da waɗanda kuka daskare sosai a cikin saitunan sarrafa kuɗin ku. Ma'auni da aka kulle za su kasance da zarar an kammala ma'amalolinsu, yayin da daskararrun ma'auni ba za su iya samun damar yin ciniki ba har sai kun yanke shawarar cire su.", "unconfirmed": "Ba a tabbatar ba", "understand": "na gane", + "unlock": "Buɗe", "unmatched_currencies": "Nau'in walat ɗin ku na yanzu bai dace da na lambar QR da aka bincika ba", "unspent_change": "Canza", "unspent_coins_details_title": "Bayanan tsabar kudi da ba a kashe ba", @@ -848,6 +853,7 @@ "wallet_menu": "Menu", "wallet_name": "Sunan walat", "wallet_name_exists": "Wallet mai wannan sunan ya riga ya wanzu. Da fatan za a zaɓi wani suna daban ko sake suna ɗayan walat tukuna.", + "wallet_password_is_empty": "Alamar Wallet babu komai. Al'adun Wallet bai zama komai ba", "wallet_recovery_height": "Mai Tsaro", "wallet_restoration_store_incorrect_seed_length": "kalmar sirrin iri ba daidai ba", "wallet_seed": "kalmar sirri na walat", diff --git a/res/values/strings_hi.arb b/res/values/strings_hi.arb index dc684e5c7..5398b6675 100644 --- a/res/values/strings_hi.arb +++ b/res/values/strings_hi.arb @@ -236,6 +236,7 @@ "enter_code": "कोड दर्ज करें", "enter_seed_phrase": "अपना बीज वाक्यांश दर्ज करें", "enter_totp_code": "कृपया TOTP कोड दर्ज करें।", + "enter_wallet_password": "वॉलेट पासवर्ड दर्ज करें", "enter_your_note": "अपना नोट दर्ज करें ...", "enter_your_pin": "अपना पिन दर्ज करो", "enter_your_pin_again": "फिर से अपना पिन डालें", @@ -340,6 +341,7 @@ "insufficientFundsForRentError": "आपके पास लेन -देन शुल्क और खाते के लिए किराए को कवर करने के लिए पर्याप्त सोल नहीं है। कृपया अपने बटुए में अधिक सोल जोड़ें या सोल राशि को कम करें जिसे आप भेज रहे हैं", "introducing_cake_pay": "परिचय Cake Pay!", "invalid_input": "अमान्य निवेश", + "invalid_password": "अवैध पासवर्ड", "invoice_details": "चालान विवरण", "is_percentage": "है", "last_30_days": "पिछले 30 दिन", @@ -509,6 +511,8 @@ "rename": "नाम बदलें", "rep_warning": "प्रतिनिधि चेतावनी", "rep_warning_sub": "आपका प्रतिनिधि अच्छी स्थिति में नहीं दिखाई देता है। एक नया चयन करने के लिए यहां टैप करें", + "repeat_wallet_password": "वॉलेट पासवर्ड दोहराएं", + "repeated_password_is_incorrect": "बार -बार पासवर्ड गलत है। कृपया फिर से वॉलेट पासवर्ड दोहराएं।", "require_for_adding_contacts": "संपर्क जोड़ने के लिए आवश्यकता है", "require_for_all_security_and_backup_settings": "सभी सुरक्षा और बैकअप सेटिंग्स की आवश्यकता है", "require_for_assessing_wallet": "वॉलेट तक पहुँचने के लिए आवश्यकता है", @@ -807,6 +811,7 @@ "unavailable_balance_description": "अनुपलब्ध शेष राशि: इस कुल में वे धनराशि शामिल हैं जो लंबित लेनदेन में बंद हैं और जिन्हें आपने अपनी सिक्का नियंत्रण सेटिंग्स में सक्रिय रूप से जमा कर रखा है। लॉक किए गए शेष उनके संबंधित लेन-देन पूरे होने के बाद उपलब्ध हो जाएंगे, जबकि जमे हुए शेष लेन-देन के लिए अप्राप्य रहेंगे जब तक कि आप उन्हें अनफ्रीज करने का निर्णय नहीं लेते।", "unconfirmed": "अपुष्ट शेष राशि", "understand": "मुझे समझ", + "unlock": "अनलॉक", "unmatched_currencies": "आपके वर्तमान वॉलेट की मुद्रा स्कैन किए गए क्यूआर से मेल नहीं खाती", "unspent_change": "परिवर्तन", "unspent_coins_details_title": "अव्ययित सिक्कों का विवरण", @@ -848,6 +853,7 @@ "wallet_menu": "बटुआ मेनू", "wallet_name": "बटुए का नाम", "wallet_name_exists": "उस नाम वाला वॉलेट पहले से मौजूद है", + "wallet_password_is_empty": "वॉलेट पासवर्ड खाली है। वॉलेट पासवर्ड खाली नहीं होना चाहिए", "wallet_recovery_height": "वसूली ऊंचाई", "wallet_restoration_store_incorrect_seed_length": "गलत बीज की लंबाई", "wallet_seed": "बटुआ का बीज", diff --git a/res/values/strings_hr.arb b/res/values/strings_hr.arb index f44f65ae6..f774334a5 100644 --- a/res/values/strings_hr.arb +++ b/res/values/strings_hr.arb @@ -236,6 +236,7 @@ "enter_code": "Unesite kod", "enter_seed_phrase": "Unesite svoju sjemensku frazu", "enter_totp_code": "Unesite TOTP kod.", + "enter_wallet_password": "Unesite lozinku za novčanik", "enter_your_note": "Unesite svoju poruku…", "enter_your_pin": "Upišite PIN", "enter_your_pin_again": "Ponovno upišite pin", @@ -340,6 +341,7 @@ "insufficientFundsForRentError": "Nemate dovoljno SOL -a za pokrivanje naknade za transakciju i najamninu za račun. Ljubazno dodajte više sol u svoj novčanik ili smanjite količinu SOL -a koju šaljete", "introducing_cake_pay": "Predstavljamo Cake Pay!", "invalid_input": "Pogrešan unos", + "invalid_password": "Netočna zaporka", "invoice_details": "Podaci o fakturi", "is_percentage": "je", "last_30_days": "Zadnjih 30 dana", @@ -507,6 +509,8 @@ "rename": "Preimenuj", "rep_warning": "Reprezentativno upozorenje", "rep_warning_sub": "Čini se da vaš predstavnik nije u dobrom stanju. Dodirnite ovdje za odabir novog", + "repeat_wallet_password": "Ponovite lozinku za novčanik", + "repeated_password_is_incorrect": "Ponovljena lozinka je netočna. Molimo ponovite lozinku za novčanik.", "require_for_adding_contacts": "Zahtijeva za dodavanje kontakata", "require_for_all_security_and_backup_settings": "Zahtijeva za sve postavke sigurnosti i sigurnosne kopije", "require_for_assessing_wallet": "Potreban za pristup novčaniku", @@ -805,6 +809,7 @@ "unavailable_balance_description": "Nedostupno stanje: Ovaj ukupni iznos uključuje sredstva koja su zaključana u transakcijama na čekanju i ona koja ste aktivno zamrznuli u postavkama kontrole novčića. Zaključani saldi postat će dostupni kada se dovrše njihove transakcije, dok zamrznuti saldi ostaju nedostupni za transakcije sve dok ih ne odlučite odmrznuti.", "unconfirmed": "Nepotvrđeno stanje", "understand": "Razumijem", + "unlock": "Otključati", "unmatched_currencies": "Valuta vašeg trenutnog novčanika ne odgovara onoj na skeniranom QR-u", "unspent_change": "Promijeniti", "unspent_coins_details_title": "Nepotrošeni detalji o novčićima", @@ -846,6 +851,7 @@ "wallet_menu": "Izbornik", "wallet_name": "Ime novčanika", "wallet_name_exists": "Novčanik s tim nazivom već postoji", + "wallet_password_is_empty": "Lozinka za novčanik je prazna. Lozinka za novčanik ne bi trebala biti prazna", "wallet_recovery_height": "Visina oporavka", "wallet_restoration_store_incorrect_seed_length": "Netočna dužina pristupnog izraza", "wallet_seed": "Pristupni izraz novčanika", diff --git a/res/values/strings_id.arb b/res/values/strings_id.arb index 613799b06..267359a13 100644 --- a/res/values/strings_id.arb +++ b/res/values/strings_id.arb @@ -236,6 +236,7 @@ "enter_code": "Masukkan kode", "enter_seed_phrase": "Masukkan frasa benih Anda", "enter_totp_code": "Masukkan Kode TOTP.", + "enter_wallet_password": "Masukkan Kata Sandi Dompet", "enter_your_note": "Masukkan catatan Anda...", "enter_your_pin": "Masukkan PIN Anda", "enter_your_pin_again": "Masukkan PIN Anda lagi", @@ -340,6 +341,7 @@ "insufficientFundsForRentError": "Anda tidak memiliki cukup SOL untuk menutupi biaya transaksi dan menyewa untuk akun tersebut. Mohon tambahkan lebih banyak sol ke dompet Anda atau kurangi jumlah sol yang Anda kirim", "introducing_cake_pay": "Perkenalkan Cake Pay!", "invalid_input": "Masukan tidak valid", + "invalid_password": "Kata sandi salah", "invoice_details": "Detail faktur", "is_percentage": "adalah", "last_30_days": "30 hari terakhir", @@ -509,6 +511,8 @@ "rename": "Ganti nama", "rep_warning": "Peringatan Perwakilan", "rep_warning_sub": "Perwakilan Anda tampaknya tidak bereputasi baik. Ketuk di sini untuk memilih yang baru", + "repeat_wallet_password": "Ulangi Kata Sandi Dompet", + "repeated_password_is_incorrect": "Kata sandi yang diulang tidak benar. Harap ulangi kata sandi dompet lagi.", "require_for_adding_contacts": "Membutuhkan untuk menambahkan kontak", "require_for_all_security_and_backup_settings": "Memerlukan untuk semua pengaturan keamanan dan pencadangan", "require_for_assessing_wallet": "Diperlukan untuk mengakses dompet", @@ -808,6 +812,7 @@ "unavailable_balance_description": "Saldo Tidak Tersedia: Total ini termasuk dana yang terkunci dalam transaksi yang tertunda dan dana yang telah Anda bekukan secara aktif di pengaturan kontrol koin Anda. Saldo yang terkunci akan tersedia setelah transaksi masing-masing selesai, sedangkan saldo yang dibekukan tetap tidak dapat diakses untuk transaksi sampai Anda memutuskan untuk mencairkannya.", "unconfirmed": "Saldo Belum Dikonfirmasi", "understand": "Saya mengerti", + "unlock": "Membuka kunci", "unmatched_currencies": "Mata uang dompet Anda saat ini tidak cocok dengan yang ditandai QR", "unspent_change": "Mengubah", "unspent_coins_details_title": "Rincian koin yang tidak terpakai", @@ -849,6 +854,7 @@ "wallet_menu": "Menu", "wallet_name": "Nama Dompet", "wallet_name_exists": "Nama dompet sudah ada. Silakan pilih nama yang berbeda atau ganti nama dompet yang lain terlebih dahulu.", + "wallet_password_is_empty": "Kata sandi dompet kosong. Kata sandi dompet tidak boleh kosong", "wallet_recovery_height": "Tinggi pemulihan", "wallet_restoration_store_incorrect_seed_length": "Panjang seed yang salah", "wallet_seed": "Seed dompet", diff --git a/res/values/strings_it.arb b/res/values/strings_it.arb index c64763536..2f7956a22 100644 --- a/res/values/strings_it.arb +++ b/res/values/strings_it.arb @@ -237,6 +237,7 @@ "enter_code": "Inserisci codice", "enter_seed_phrase": "Inserisci la tua frase di semi", "enter_totp_code": "Inserisci il codice TOTP.", + "enter_wallet_password": "Immettere la password del portafoglio", "enter_your_note": "Inserisci la tua nota…", "enter_your_pin": "Inserisci il tuo PIN", "enter_your_pin_again": "Inserisci il tuo pin di nuovo", @@ -341,6 +342,7 @@ "insufficientFundsForRentError": "Non hai abbastanza SOL per coprire la tassa di transazione e l'affitto per il conto. Si prega di aggiungere più SOL al tuo portafoglio o ridurre l'importo SOL che stai inviando", "introducing_cake_pay": "Presentazione di Cake Pay!", "invalid_input": "Inserimento non valido", + "invalid_password": "Password non valida", "invoice_details": "Dettagli della fattura", "is_percentage": "è", "last_30_days": "Ultimi 30 giorni", @@ -509,6 +511,8 @@ "rename": "Rinomina", "rep_warning": "Avvertenza rappresentativa", "rep_warning_sub": "Il tuo rappresentante non sembra essere in regola. Tocca qui per selezionarne uno nuovo", + "repeat_wallet_password": "Ripeti la password del portafoglio", + "repeated_password_is_incorrect": "La password ripetuta non è corretta. Si prega di ripetere di nuovo la password del portafoglio.", "require_for_adding_contacts": "Richiesto per l'aggiunta di contatti", "require_for_all_security_and_backup_settings": "Richiedi per tutte le impostazioni di sicurezza e backup", "require_for_assessing_wallet": "Richiesto per l'accesso al portafoglio", @@ -807,6 +811,7 @@ "unavailable_balance_description": "Saldo non disponibile: questo totale include i fondi bloccati nelle transazioni in sospeso e quelli che hai congelato attivamente nelle impostazioni di controllo delle monete. I saldi bloccati diventeranno disponibili una volta completate le rispettive transazioni, mentre i saldi congelati rimarranno inaccessibili per le transazioni finché non deciderai di sbloccarli.", "unconfirmed": "Saldo non confermato", "understand": "Capisco", + "unlock": "Sbloccare", "unmatched_currencies": "La valuta del tuo portafoglio attuale non corrisponde a quella del QR scansionato", "unspent_change": "Modifica", "unspent_coins_details_title": "Dettagli sulle monete non spese", @@ -849,6 +854,7 @@ "wallet_menu": "Menù", "wallet_name": "Nome del Portafoglio", "wallet_name_exists": "Il portafoglio con quel nome è già esistito", + "wallet_password_is_empty": "La password del portafoglio è vuota. La password del portafoglio non dovrebbe essere vuota", "wallet_recovery_height": "Altezza di recupero", "wallet_restoration_store_incorrect_seed_length": "Lunghezza seme non corretta", "wallet_seed": "Seme Portafoglio", diff --git a/res/values/strings_ja.arb b/res/values/strings_ja.arb index 0ce96f01d..628784371 100644 --- a/res/values/strings_ja.arb +++ b/res/values/strings_ja.arb @@ -236,6 +236,7 @@ "enter_code": "コードを入力", "enter_seed_phrase": "シードフレーズを入力してください", "enter_totp_code": "TOTPコードを入力してください。", + "enter_wallet_password": "ウォレットパスワードを入力します", "enter_your_note": "メモを入力してください…", "enter_your_pin": "PINを入力してください", "enter_your_pin_again": "ピンをもう一度入力してください", @@ -341,6 +342,7 @@ "insufficientFundsForRentError": "アカウントの取引料金とレンタルをカバーするのに十分なソルがありません。財布にソルを追加するか、送信するソル量を減らしてください", "introducing_cake_pay": "序章Cake Pay!", "invalid_input": "無効入力", + "invalid_password": "無効なパスワード", "invoice_details": "請求の詳細", "is_percentage": "is", "last_30_days": "過去30日", @@ -508,6 +510,8 @@ "rename": "リネーム", "rep_warning": "代表的な警告", "rep_warning_sub": "あなたの代表者は良好な状態ではないようです。ここをタップして、新しいものを選択します", + "repeat_wallet_password": "ウォレットパスワードを繰り返します", + "repeated_password_is_incorrect": "繰り返しパスワードが正しくありません。ウォレットのパスワードをもう一度繰り返してください。", "require_for_adding_contacts": "連絡先の追加に必要", "require_for_all_security_and_backup_settings": "すべてのセキュリティおよびバックアップ設定に必須", "require_for_assessing_wallet": "ウォレットにアクセスするために必要です", @@ -806,6 +810,7 @@ "unavailable_balance_description": "利用不可能な残高: この合計には、保留中のトランザクションにロックされている資金と、コイン管理設定でアクティブに凍結した資金が含まれます。ロックされた残高は、それぞれの取引が完了すると利用可能になりますが、凍結された残高は、凍結を解除するまで取引にアクセスできません。", "unconfirmed": "残高未確認", "understand": "わかります", + "unlock": "ロックを解除します", "unmatched_currencies": "現在のウォレットの通貨がスキャンされたQRの通貨と一致しません", "unspent_change": "変化", "unspent_coins_details_title": "未使用のコインの詳細", @@ -847,6 +852,7 @@ "wallet_menu": "ウォレットメニュー", "wallet_name": "ウォレット名", "wallet_name_exists": "その名前のウォレットはすでに存在しています", + "wallet_password_is_empty": "ウォレットパスワードは空です。ウォレットのパスワードは空にしてはいけません", "wallet_recovery_height": "回復の高さ", "wallet_restoration_store_incorrect_seed_length": "誤ったシード長s", "wallet_seed": "ウォレットシード", diff --git a/res/values/strings_ko.arb b/res/values/strings_ko.arb index 621ea2b47..f5642364e 100644 --- a/res/values/strings_ko.arb +++ b/res/values/strings_ko.arb @@ -236,6 +236,7 @@ "enter_code": "코드 입력", "enter_seed_phrase": "시드 문구를 입력하십시오", "enter_totp_code": "TOTP 코드를 입력하세요.", + "enter_wallet_password": "지갑 암호를 입력하십시오", "enter_your_note": "메모를 입력하세요…", "enter_your_pin": "PIN을 입력하십시오", "enter_your_pin_again": "다시 핀을 입력", @@ -340,6 +341,7 @@ "insufficientFundsForRentError": "거래 수수료와 계좌 임대료를 충당하기에 충분한 SOL이 없습니다. 지갑에 더 많은 솔을 추가하거나 보내는 솔을 줄이십시오.", "introducing_cake_pay": "소개 Cake Pay!", "invalid_input": "잘못된 입력", + "invalid_password": "유효하지 않은 비밀번호", "invoice_details": "인보이스 세부정보", "is_percentage": "이다", "last_30_days": "지난 30일", @@ -508,6 +510,8 @@ "rename": "이름 바꾸기", "rep_warning": "대표 경고", "rep_warning_sub": "귀하의 대표는 양호한 상태가 아닌 것 같습니다. 새 것을 선택하려면 여기를 탭하십시오", + "repeat_wallet_password": "지갑 암호를 반복하십시오", + "repeated_password_is_incorrect": "반복 된 비밀번호가 올바르지 않습니다. 지갑 암호를 다시 반복하십시오.", "require_for_adding_contacts": "연락처 추가에 필요", "require_for_all_security_and_backup_settings": "모든 보안 및 백업 설정에 필요", "require_for_assessing_wallet": "지갑 접근을 위해 필요", @@ -806,6 +810,7 @@ "unavailable_balance_description": "사용할 수 없는 잔액: 이 총계에는 보류 중인 거래에 잠겨 있는 자금과 코인 관리 설정에서 적극적으로 동결된 자금이 포함됩니다. 잠긴 잔액은 해당 거래가 완료되면 사용할 수 있게 되며, 동결된 잔액은 동결을 해제하기 전까지 거래에 액세스할 수 없습니다.", "unconfirmed": "확인되지 않은 잔액", "understand": "이해 했어요", + "unlock": "터놓다", "unmatched_currencies": "현재 지갑의 통화가 스캔한 QR의 통화와 일치하지 않습니다.", "unspent_change": "변화", "unspent_coins_details_title": "사용하지 않은 동전 세부 정보", @@ -847,6 +852,7 @@ "wallet_menu": "월렛 메뉴", "wallet_name": "지갑 이름", "wallet_name_exists": "해당 이름의 지갑이 이미 존재합니다.", + "wallet_password_is_empty": "지갑 암호는 비어 있습니다. 지갑 암호는 비어 있지 않아야합니다", "wallet_recovery_height": "복구 높이", "wallet_restoration_store_incorrect_seed_length": "시드 길이가 잘못되었습니다", "wallet_seed": "지갑 시드", diff --git a/res/values/strings_my.arb b/res/values/strings_my.arb index 284fc2b2f..5f3afd029 100644 --- a/res/values/strings_my.arb +++ b/res/values/strings_my.arb @@ -236,6 +236,7 @@ "enter_code": "ကုဒ်ထည့်ပါ။", "enter_seed_phrase": "သင့်ရဲ့မျိုးစေ့စကားစုကိုရိုက်ထည့်ပါ", "enter_totp_code": "ကျေးဇူးပြု၍ TOTP ကုဒ်ကို ထည့်ပါ။", + "enter_wallet_password": "ပိုက်ဆံအိတ်စကားဝှက်ကိုရိုက်ထည့်ပါ", "enter_your_note": "သင့်မှတ်စုကို ထည့်ပါ...", "enter_your_pin": "သင်၏ PIN ကိုထည့်ပါ။", "enter_your_pin_again": "သင့်ပင်နံပါတ်ကို ထပ်မံထည့်သွင်းပါ။", @@ -340,6 +341,7 @@ "insufficientFundsForRentError": "သင်ငွေပေးချေမှုအခကြေးငွေကိုဖုံးအုပ်ရန်နှင့်အကောင့်ငှားရန်လုံလောက်သော sol ရှိသည်မဟုတ်ကြဘူး။ ကြင်နာစွာသင်၏ပိုက်ဆံအိတ်သို့ပိုမို sol ကိုပိုမိုထည့်ပါသို့မဟုတ်သင်ပို့ခြင်း sol ပမာဏကိုလျှော့ချပါ", "introducing_cake_pay": "Cake Pay ကို မိတ်ဆက်ခြင်း။", "invalid_input": "ထည့်သွင်းမှု မမှန်ကန်ပါ။", + "invalid_password": "မမှန်ကန်သောစကားဝှက်", "invoice_details": "ပြေစာအသေးစိတ်", "is_percentage": "သည်", "last_30_days": "လွန်ခဲ့သော ရက် 30", @@ -507,6 +509,8 @@ "rename": "အမည်ပြောင်းပါ။", "rep_warning": "ကိုယ်စားလှယ်သတိပေးချက်", "rep_warning_sub": "သင်၏ကိုယ်စားလှယ်သည်ကောင်းမွန်သောရပ်တည်မှုတွင်မဖြစ်သင့်ပါ။ အသစ်တစ်ခုကိုရွေးချယ်ရန်ဤနေရာတွင်အသာပုတ်ပါ", + "repeat_wallet_password": "ပိုက်ဆံအိတ်စကားဝှက်ကိုပြန်လုပ်ပါ", + "repeated_password_is_incorrect": "ထပ်ခါတလဲလဲစကားဝှက်မမှန်ကန်ပါ ကျေးဇူးပြုပြီးပိုက်ဆံအိတ်စကားဝှက်ကိုပြန်လုပ်ပါ။", "require_for_adding_contacts": "အဆက်အသွယ်များထည့်ရန် လိုအပ်သည်။", "require_for_all_security_and_backup_settings": "လုံခြုံရေးနှင့် အရန်ဆက်တင်များအားလုံးအတွက် လိုအပ်ပါသည်။", "require_for_assessing_wallet": "ပိုက်ဆံအိတ်ကို ဝင်သုံးရန် လိုအပ်သည်။", @@ -805,6 +809,7 @@ "unavailable_balance_description": "မရရှိနိုင်သော လက်ကျန်ငွေ- ဤစုစုပေါင်းတွင် ဆိုင်းငံ့ထားသော ငွေပေးငွေယူများတွင် သော့ခတ်ထားသော ငွေကြေးများနှင့် သင်၏ coin ထိန်းချုပ်မှုဆက်တင်များတွင် သင် တက်ကြွစွာ အေးခဲထားသော ငွေများ ပါဝင်သည်။ သော့ခတ်ထားသော လက်ကျန်ငွေများကို ၎င်းတို့၏ သက်ဆိုင်ရာ ငွေပေးငွေယူများ ပြီးမြောက်သည်နှင့် တပြိုင်နက် ရရှိနိုင်မည်ဖြစ်ပြီး၊ အေးခဲထားသော လက်ကျန်များကို ၎င်းတို့အား ပြန်ဖြုတ်ရန် သင်ဆုံးဖြတ်သည်အထိ ငွေပေးငွေယူများအတွက် ဆက်လက်၍မရနိုင်ပါ။", "unconfirmed": "အတည်မပြုနိုင်သော လက်ကျန်ငွေ", "understand": "ကျွန်တော်နားလည်ပါတယ်", + "unlock": "သော့ဖွင့်", "unmatched_currencies": "သင့်လက်ရှိပိုက်ဆံအိတ်၏ငွေကြေးသည် စကင်ဖတ်ထားသော QR နှင့် မကိုက်ညီပါ။", "unspent_change": "ပေြာင်းလဲခြင်း", "unspent_coins_details_title": "အသုံးမဝင်သော အကြွေစေ့အသေးစိတ်များ", @@ -846,6 +851,7 @@ "wallet_menu": "မီနူး", "wallet_name": "ပိုက်ဆံအိတ်နာမည", "wallet_name_exists": "ထိုအမည်ဖြင့် ပိုက်ဆံအိတ်တစ်ခု ရှိနှင့်ပြီးဖြစ်သည်။ အခြားအမည်တစ်ခုကို ရွေးပါ သို့မဟုတ် အခြားပိုက်ဆံအိတ်ကို ဦးစွာ အမည်ပြောင်းပါ။", + "wallet_password_is_empty": "ပိုက်ဆံအိတ်စကားဝှက်သည်ဗလာဖြစ်သည်။ ပိုက်ဆံအိတ်စကားဝှက်သည်အချည်းနှီးဖြစ်သင့်သည်", "wallet_recovery_height": "ပြန်လည်ထူထောင်ရေးအမြင့်", "wallet_restoration_store_incorrect_seed_length": "မျိုးစေ့အရှည် မမှန်ပါ။", "wallet_seed": "ပိုက်ဆံအိတ်စေ့", diff --git a/res/values/strings_nl.arb b/res/values/strings_nl.arb index 4b3faade0..4030edcbd 100644 --- a/res/values/strings_nl.arb +++ b/res/values/strings_nl.arb @@ -236,6 +236,7 @@ "enter_code": "Voer code in", "enter_seed_phrase": "Voer uw zaadzin in", "enter_totp_code": "Voer de TOTP-code in.", + "enter_wallet_password": "Voer het Wallet -wachtwoord in", "enter_your_note": "Voer uw notitie in ...", "enter_your_pin": "Voer uw pincode in", "enter_your_pin_again": "Voer uw PIN opnieuw in", @@ -340,6 +341,7 @@ "insufficientFundsForRentError": "U hebt niet genoeg SOL om de transactiekosten en huur voor de rekening te dekken. Voeg vriendelijk meer SOL toe aan uw portemonnee of verminder de SOL -hoeveelheid die u verzendt", "introducing_cake_pay": "Introductie van Cake Pay!", "invalid_input": "Ongeldige invoer", + "invalid_password": "Ongeldig wachtwoord", "invoice_details": "Factuurgegevens", "is_percentage": "is", "last_30_days": "Laatste 30 dagen", @@ -507,6 +509,8 @@ "rename": "Hernoemen", "rep_warning": "Representatieve waarschuwing", "rep_warning_sub": "Uw vertegenwoordiger lijkt niet goed te staan. Tik hier om een nieuwe te selecteren", + "repeat_wallet_password": "Herhaal het Wallet -wachtwoord", + "repeated_password_is_incorrect": "Herhaald wachtwoord is onjuist. Herhaal het Wallet -wachtwoord opnieuw.", "require_for_adding_contacts": "Vereist voor het toevoegen van contacten", "require_for_all_security_and_backup_settings": "Vereist voor alle beveiligings- en back-upinstellingen", "require_for_assessing_wallet": "Vereist voor toegang tot portemonnee", @@ -805,6 +809,7 @@ "unavailable_balance_description": "Niet-beschikbaar saldo: Dit totaal omvat het geld dat is vergrendeld in lopende transacties en het geld dat u actief hebt bevroren in uw muntcontrole-instellingen. Vergrendelde saldi komen beschikbaar zodra de betreffende transacties zijn voltooid, terwijl bevroren saldi ontoegankelijk blijven voor transacties totdat u besluit ze weer vrij te geven.", "unconfirmed": "Onbevestigd saldo", "understand": "Ik begrijp het", + "unlock": "Ontgrendelen", "unmatched_currencies": "De valuta van uw huidige portemonnee komt niet overeen met die van de gescande QR", "unspent_change": "Wijziging", "unspent_coins_details_title": "Details van niet-uitgegeven munten", @@ -847,6 +852,7 @@ "wallet_menu": "Portemonnee-menu", "wallet_name": "Portemonnee naam", "wallet_name_exists": "Portemonnee met die naam bestaat al", + "wallet_password_is_empty": "Wallet -wachtwoord is leeg. Wallet -wachtwoord mag niet leeg zijn", "wallet_recovery_height": "Herstelhoogte", "wallet_restoration_store_incorrect_seed_length": "Onjuiste zaadlengte", "wallet_seed": "Portemonnee zaad", diff --git a/res/values/strings_pl.arb b/res/values/strings_pl.arb index f67ea9870..1d8c0fad3 100644 --- a/res/values/strings_pl.arb +++ b/res/values/strings_pl.arb @@ -236,6 +236,7 @@ "enter_code": "Wprowadź kod", "enter_seed_phrase": "Wprowadź swoją frazę nasienną", "enter_totp_code": "Wprowadź kod TOTP.", + "enter_wallet_password": "Wprowadź hasło portfela", "enter_your_note": "Wpisz notatkę…", "enter_your_pin": "Wpisz kod PIN", "enter_your_pin_again": "Wprowadź ponownie swój kod PIN", @@ -340,6 +341,7 @@ "insufficientFundsForRentError": "Nie masz wystarczającej ilości SOL, aby pokryć opłatę za transakcję i czynsz za konto. Uprzejmie dodaj więcej sol do portfela lub zmniejsz solę, którą wysyłasz", "introducing_cake_pay": "Przedstawiamy Cake Pay!", "invalid_input": "Nieprawidłowe dane wejściowe", + "invalid_password": "Nieprawidłowe hasło", "invoice_details": "Dane do faktury", "is_percentage": "jest", "last_30_days": "Ostatnie 30 dni", @@ -507,6 +509,8 @@ "rename": "Zmień nazwę", "rep_warning": "Przedstawicielskie ostrzeżenie", "rep_warning_sub": "Twój przedstawiciel nie wydaje się mieć dobrej opinii. Stuknij tutaj, aby wybrać nowy", + "repeat_wallet_password": "Powtórz hasło portfela", + "repeated_password_is_incorrect": "Powtarzane hasło jest nieprawidłowe. Powtórz ponownie hasło portfela.", "require_for_adding_contacts": "Wymagane do dodania kontaktów", "require_for_all_security_and_backup_settings": "Wymagaj dla wszystkich ustawień zabezpieczeń i kopii zapasowych", "require_for_assessing_wallet": "Wymagaj dostępu do portfela", @@ -805,6 +809,7 @@ "unavailable_balance_description": "Niedostępne saldo: Suma ta obejmuje środki zablokowane w transakcjach oczekujących oraz te, które aktywnie zamroziłeś w ustawieniach kontroli monet. Zablokowane salda staną się dostępne po zakończeniu odpowiednich transakcji, natomiast zamrożone salda pozostaną niedostępne dla transakcji, dopóki nie zdecydujesz się ich odblokować.", "unconfirmed": "Niepotwierdzone saldo", "understand": "Rozumiem", + "unlock": "Odblokować", "unmatched_currencies": "Waluta Twojego obecnego portfela nie zgadza się z waluctą zeskanowanego kodu QR", "unspent_change": "Zmiana", "unspent_coins_details_title": "Szczegóły niewydanych monet", @@ -846,6 +851,7 @@ "wallet_menu": "Menu portfela", "wallet_name": "Nazwa portfela", "wallet_name_exists": "Portfel o tej nazwie już istnieje", + "wallet_password_is_empty": "Hasło portfela jest puste. Hasło portfela nie powinno być puste", "wallet_recovery_height": "Wysokość odzysku", "wallet_restoration_store_incorrect_seed_length": "Nieprawidłowa długość frazy seed", "wallet_seed": "Seed portfela", diff --git a/res/values/strings_pt.arb b/res/values/strings_pt.arb index 6ff347323..6d1473b4b 100644 --- a/res/values/strings_pt.arb +++ b/res/values/strings_pt.arb @@ -236,6 +236,7 @@ "enter_code": "Digite o código", "enter_seed_phrase": "Digite sua frase de semente", "enter_totp_code": "Digite o código TOTP.", + "enter_wallet_password": "Digite a senha da carteira", "enter_your_note": "Insira sua nota ...", "enter_your_pin": "Insira seu PIN", "enter_your_pin_again": "Insira seu PIN novamente", @@ -340,6 +341,7 @@ "insufficientFundsForRentError": "Você não tem Sol suficiente para cobrir a taxa de transação e o aluguel da conta. Por favor, adicione mais sol à sua carteira ou reduza a quantidade de sol que você envia", "introducing_cake_pay": "Apresentando o Cake Pay!", "invalid_input": "Entrada inválida", + "invalid_password": "Senha inválida", "invoice_details": "Detalhes da fatura", "is_percentage": "é", "last_30_days": "Últimos 30 dias", @@ -509,6 +511,8 @@ "rename": "Renomear", "rep_warning": "Aviso representativo", "rep_warning_sub": "Seu representante não parece estar em boa posição. Toque aqui para selecionar um novo", + "repeat_wallet_password": "Repita a senha da carteira", + "repeated_password_is_incorrect": "A senha repetida está incorreta. Repita a senha da carteira novamente.", "require_for_adding_contacts": "Requer para adicionar contatos", "require_for_all_security_and_backup_settings": "Exigir todas as configurações de segurança e backup", "require_for_assessing_wallet": "Requer para acessar a carteira", @@ -807,6 +811,7 @@ "unavailable_balance_description": "Saldo Indisponível: Este total inclui fundos bloqueados em transações pendentes e aqueles que você congelou ativamente nas configurações de controle de moedas. Os saldos bloqueados ficarão disponíveis assim que suas respectivas transações forem concluídas, enquanto os saldos congelados permanecerão inacessíveis para transações até que você decida descongelá-los.", "unconfirmed": "Saldo não confirmado", "understand": "Entendo", + "unlock": "Desbloquear", "unmatched_currencies": "A moeda da sua carteira atual não corresponde à do QR digitalizado", "unspent_change": "Troco", "unspent_coins_details_title": "Detalhes de moedas não gastas", @@ -849,6 +854,7 @@ "wallet_menu": "Menu", "wallet_name": "Nome da carteira", "wallet_name_exists": "A carteira com esse nome já existe", + "wallet_password_is_empty": "A senha da carteira está vazia. A senha da carteira não deve estar vazia", "wallet_recovery_height": "Altura de recuperação", "wallet_restoration_store_incorrect_seed_length": "Comprimento de semente incorreto", "wallet_seed": "Semente de carteira", diff --git a/res/values/strings_ru.arb b/res/values/strings_ru.arb index 6d2fe975a..a2991e4b0 100644 --- a/res/values/strings_ru.arb +++ b/res/values/strings_ru.arb @@ -236,6 +236,7 @@ "enter_code": "Введите код", "enter_seed_phrase": "Введите свою семенную фразу", "enter_totp_code": "Пожалуйста, введите TOTP-код.", + "enter_wallet_password": "Введите пароль кошелька", "enter_your_note": "Введите примечание…", "enter_your_pin": "Введите ваш PIN", "enter_your_pin_again": "Введите PIN еще раз", @@ -340,6 +341,7 @@ "insufficientFundsForRentError": "У вас недостаточно Sol, чтобы покрыть плату за транзакцию и аренду для счета. Пожалуйста, добавьте больше Sol в свой кошелек или уменьшите сумму Sol, которую вы отправляете", "introducing_cake_pay": "Представляем Cake Pay!", "invalid_input": "Неверный Ввод", + "invalid_password": "Неверный пароль", "invoice_details": "Детали счета", "is_percentage": "есть", "last_30_days": "Последние 30 дней", @@ -508,6 +510,8 @@ "rename": "Переименовать", "rep_warning": "Представительное предупреждение", "rep_warning_sub": "Ваш представитель, похоже, не в хорошей репутации. Нажмите здесь, чтобы выбрать новый", + "repeat_wallet_password": "Повторите пароль кошелька", + "repeated_password_is_incorrect": "Повторный пароль неверен. Пожалуйста, повторите пароль кошелька снова.", "require_for_adding_contacts": "Требовать добавления контактов", "require_for_all_security_and_backup_settings": "Требовать все настройки безопасности и резервного копирования", "require_for_assessing_wallet": "Требовать для доступа к кошельку", @@ -806,6 +810,7 @@ "unavailable_balance_description": "Недоступный баланс: в эту сумму входят средства, заблокированные в ожидающих транзакциях, и средства, которые вы активно заморозили в настройках управления монетами. Заблокированные балансы станут доступны после завершения соответствующих транзакций, а замороженные балансы останутся недоступными для транзакций, пока вы не решите их разморозить.", "unconfirmed": "Неподтвержденный баланс", "understand": "Понятно", + "unlock": "Разблокировать", "unmatched_currencies": "Валюта вашего текущего кошелька не соответствует валюте отсканированного QR-кода.", "unspent_change": "Изменять", "unspent_coins_details_title": "Сведения о неизрасходованных монетах", @@ -847,6 +852,7 @@ "wallet_menu": "Меню кошелька", "wallet_name": "Имя кошелька", "wallet_name_exists": "Кошелек с таким именем уже существует", + "wallet_password_is_empty": "Пароль кошелька пуст. Пароль кошелька не должен быть пустым", "wallet_recovery_height": "Высота восстановления", "wallet_restoration_store_incorrect_seed_length": "Неверная длина мнемонической фразы", "wallet_seed": "Мнемоническая фраза кошелька", diff --git a/res/values/strings_th.arb b/res/values/strings_th.arb index 119f9ab0e..0c90b4fa7 100644 --- a/res/values/strings_th.arb +++ b/res/values/strings_th.arb @@ -236,6 +236,7 @@ "enter_code": "กรอกรหัส", "enter_seed_phrase": "ป้อนวลีเมล็ดพันธุ์ของคุณ", "enter_totp_code": "กรุณาใส่รหัสทีโอที", + "enter_wallet_password": "ป้อนรหัสผ่านกระเป๋าเงิน", "enter_your_note": "ใส่บันทึกของคุณ...", "enter_your_pin": "ใส่ PIN ของคุณ", "enter_your_pin_again": "ใส่ PIN ของคุณอีกครั้ง", @@ -340,6 +341,7 @@ "insufficientFundsForRentError": "คุณไม่มีโซลเพียงพอที่จะครอบคลุมค่าธรรมเนียมการทำธุรกรรมและค่าเช่าสำหรับบัญชี กรุณาเพิ่มโซลให้มากขึ้นลงในกระเป๋าเงินของคุณหรือลดจำนวนโซลที่คุณส่งมา", "introducing_cake_pay": "ยินดีต้อนรับสู่ Cake Pay!", "invalid_input": "อินพุตไม่ถูกต้อง", + "invalid_password": "รหัสผ่านไม่ถูกต้อง", "invoice_details": "รายละเอียดใบแจ้งหนี้", "is_percentage": "เป็น", "last_30_days": "30 วันล่าสุด", @@ -507,6 +509,8 @@ "rename": "เปลี่ยนชื่อ", "rep_warning": "คำเตือนตัวแทน", "rep_warning_sub": "ตัวแทนของคุณดูเหมือนจะไม่อยู่ในสถานะที่ดี แตะที่นี่เพื่อเลือกอันใหม่", + "repeat_wallet_password": "ทำซ้ำรหัสผ่านกระเป๋าเงิน", + "repeated_password_is_incorrect": "รหัสผ่านซ้ำไม่ถูกต้อง โปรดทำซ้ำรหัสผ่านกระเป๋าเงินอีกครั้ง", "require_for_adding_contacts": "ต้องการสำหรับการเพิ่มผู้ติดต่อ", "require_for_all_security_and_backup_settings": "จำเป็นสำหรับการตั้งค่าความปลอดภัยและการสำรองข้อมูลทั้งหมด", "require_for_assessing_wallet": "จำเป็นสำหรับการเข้าถึงกระเป๋าเงิน", @@ -805,6 +809,7 @@ "unavailable_balance_description": "ยอดคงเหลือที่ไม่พร้อมใช้งาน: ยอดรวมนี้รวมถึงเงินทุนที่ถูกล็อคในการทำธุรกรรมที่รอดำเนินการและที่คุณได้แช่แข็งไว้ในการตั้งค่าการควบคุมเหรียญของคุณ ยอดคงเหลือที่ถูกล็อคจะพร้อมใช้งานเมื่อธุรกรรมที่เกี่ยวข้องเสร็จสมบูรณ์ ในขณะที่ยอดคงเหลือที่แช่แข็งจะไม่สามารถเข้าถึงได้สำหรับธุรกรรมจนกว่าคุณจะตัดสินใจยกเลิกการแช่แข็ง", "unconfirmed": "ยอดคงเหลือที่ไม่ได้รับการยืนยัน", "understand": "ฉันเข้าใจ", + "unlock": "ปลดล็อค", "unmatched_currencies": "สกุลเงินของกระเป๋าปัจจุบันของคุณไม่ตรงกับของ QR ที่สแกน", "unspent_change": "เปลี่ยน", "unspent_coins_details_title": "รายละเอียดเหรียญที่ไม่ได้ใช้", @@ -846,6 +851,7 @@ "wallet_menu": "เมนู", "wallet_name": "ชื่อกระเป๋า", "wallet_name_exists": "กระเป๋าที่มีชื่อนี้มีอยู่แล้ว โปรดเลือกชื่ออื่นหรือเปลี่ยนชื่อกระเป๋าอื่นก่อน", + "wallet_password_is_empty": "รหัสผ่านกระเป๋าเงินว่างเปล่า รหัสผ่านกระเป๋าเงินไม่ควรว่างเปล่า", "wallet_recovery_height": "ความสูงของการกู้คืน", "wallet_restoration_store_incorrect_seed_length": "ความยาวของซีดไม่ถูกต้อง", "wallet_seed": "ซีดของกระเป๋า", diff --git a/res/values/strings_tl.arb b/res/values/strings_tl.arb index 8bc319c0a..ea340acf3 100644 --- a/res/values/strings_tl.arb +++ b/res/values/strings_tl.arb @@ -236,6 +236,7 @@ "enter_code": "Ipasok ang code", "enter_seed_phrase": "Ipasok ang iyong pariralang binhi", "enter_totp_code": "Mangyaring ipasok ang TOTP code.", + "enter_wallet_password": "Ipasok ang password ng pitaka", "enter_your_note": "Ipasok ang iyong tala ...", "enter_your_pin": "Ipasok ang iyong pin", "enter_your_pin_again": "Ipasok muli ang iyong pin", @@ -340,6 +341,7 @@ "insufficientFundsForRentError": "Wala kang sapat na sol upang masakop ang bayad sa transaksyon at upa para sa account. Mabait na magdagdag ng higit pa sa iyong pitaka o bawasan ang halaga ng sol na iyong ipinapadala", "introducing_cake_pay": "Ipinakikilala ang cake pay!", "invalid_input": "Di -wastong input", + "invalid_password": "Di wastong password", "invoice_details": "Mga detalye ng invoice", "is_percentage": "ay", "last_30_days": "Huling 30 araw", @@ -507,6 +509,8 @@ "rename": "Palitan ang pangalan", "rep_warning": "Babala ng kinatawan", "rep_warning_sub": "Ang iyong kinatawan ay hindi lilitaw na nasa mabuting kalagayan. Tapikin dito upang pumili ng bago", + "repeat_wallet_password": "Ulitin ang password ng pitaka", + "repeated_password_is_incorrect": "Ang paulit -ulit na password ay hindi tama. Mangyaring ulitin muli ang password ng pitaka.", "require_for_adding_contacts": "Nangangailangan para sa pagdaragdag ng mga contact", "require_for_all_security_and_backup_settings": "Nangangailangan para sa lahat ng mga setting ng seguridad at backup", "require_for_assessing_wallet": "Nangangailangan para sa pag -access ng pitaka", @@ -805,6 +809,7 @@ "unavailable_balance_description": "Hindi Available na Balanse: Kasama sa kabuuang ito ang mga pondong naka-lock sa mga nakabinbing transaksyon at ang mga aktibong na-freeze mo sa iyong mga setting ng kontrol ng coin. Magiging available ang mga naka-lock na balanse kapag nakumpleto na ang kani-kanilang mga transaksyon, habang ang mga nakapirming balanse ay nananatiling hindi naa-access para sa mga transaksyon hanggang sa magpasya kang i-unfreeze ang mga ito.", "unconfirmed": "Hindi nakumpirma na balanse", "understand": "naiintindihan ko", + "unlock": "I -unlock", "unmatched_currencies": "Ang pera ng iyong kasalukuyang pitaka ay hindi tumutugma sa na -scan na QR", "unspent_change": "Baguhin", "unspent_coins_details_title": "Mga Detalye ng Unspent Coins", @@ -846,6 +851,7 @@ "wallet_menu": "Menu", "wallet_name": "Pangalan ng Wallet", "wallet_name_exists": "Ang isang pitaka na may pangalang iyon ay mayroon na. Mangyaring pumili ng ibang pangalan o palitan muna ang iba pang pitaka.", + "wallet_password_is_empty": "Walang laman ang password ng wallet. Ang password ng wallet ay hindi dapat walang laman", "wallet_recovery_height": "Taas ng pagbawi", "wallet_restoration_store_incorrect_seed_length": "Maling haba ng binhi", "wallet_seed": "SEED ng Wallet", diff --git a/res/values/strings_tr.arb b/res/values/strings_tr.arb index 22b0e8f82..8ca4c9b29 100644 --- a/res/values/strings_tr.arb +++ b/res/values/strings_tr.arb @@ -236,6 +236,7 @@ "enter_code": "Kodu girin", "enter_seed_phrase": "Tohum ifadenizi girin", "enter_totp_code": "Lütfen TOTP Kodunu giriniz.", + "enter_wallet_password": "Cüzdan şifresini girin", "enter_your_note": "Notunu gir…", "enter_your_pin": "PIN'ini gir", "enter_your_pin_again": "PIN kodunu tekrar girin", @@ -340,6 +341,7 @@ "insufficientFundsForRentError": "İşlem ücretini karşılamak ve hesap için kiralamak için yeterli SOL'nuz yok. Lütfen cüzdanınıza daha fazla sol ekleyin veya gönderdiğiniz sol miktarını azaltın", "introducing_cake_pay": "Cake Pay ile tanışın!", "invalid_input": "Geçersiz Giriş", + "invalid_password": "Geçersiz şifre", "invoice_details": "fatura detayları", "is_percentage": "is", "last_30_days": "Son 30 gün", @@ -507,6 +509,8 @@ "rename": "Yeniden adlandır", "rep_warning": "Temsilci uyarı", "rep_warning_sub": "Temsilciniz iyi durumda görünmüyor. Yeni bir tane seçmek için buraya dokunun", + "repeat_wallet_password": "Cüzdan şifresini tekrarlayın", + "repeated_password_is_incorrect": "Tekrarlanan şifre yanlış. Lütfen cüzdan şifresini tekrarlayın.", "require_for_adding_contacts": "Kişi eklemek için gerekli", "require_for_all_security_and_backup_settings": "Tüm güvenlik ve yedekleme ayarları için iste", "require_for_assessing_wallet": "Cüzdana erişmek için gerekli", @@ -805,6 +809,7 @@ "unavailable_balance_description": "Kullanılamayan Bakiye: Bu toplam, bekleyen işlemlerde kilitlenen fonları ve jeton kontrol ayarlarınızda aktif olarak dondurduğunuz fonları içerir. Kilitli bakiyeler, ilgili işlemleri tamamlandıktan sonra kullanılabilir hale gelir; dondurulmuş bakiyeler ise siz onları dondurmaya karar verene kadar işlemler için erişilemez durumda kalır.", "unconfirmed": "Onaylanmamış Bakiye", "understand": "Anladım", + "unlock": "Kilidini aç", "unmatched_currencies": "Mevcut cüzdanınızın para birimi taranan QR ile eşleşmiyor", "unspent_change": "Değiştirmek", "unspent_coins_details_title": "Harcanmamış koin detayları", @@ -846,6 +851,7 @@ "wallet_menu": "Menü", "wallet_name": "Cüzdan ismi", "wallet_name_exists": "Bu isimde bir cüzdan zaten mevcut. Lütfen farklı bir isim seç veya önce diğer cüzdanı yeniden adlandır.", + "wallet_password_is_empty": "Cüzdan şifresi boş. Cüzdan şifresi boş olmamalı", "wallet_recovery_height": "Kurtarma Yüksekliği", "wallet_restoration_store_incorrect_seed_length": "Yanlış tohum uzunluğu", "wallet_seed": "Cüzdan tohumu", diff --git a/res/values/strings_uk.arb b/res/values/strings_uk.arb index 9d2269149..1b316bd06 100644 --- a/res/values/strings_uk.arb +++ b/res/values/strings_uk.arb @@ -236,6 +236,7 @@ "enter_code": "Введіть код", "enter_seed_phrase": "Введіть свою насіннєву фразу", "enter_totp_code": "Будь ласка, введіть код TOTP.", + "enter_wallet_password": "Введіть пароль гаманця", "enter_your_note": "Введіть примітку…", "enter_your_pin": "Введіть ваш PIN", "enter_your_pin_again": "Введіть PIN ще раз", @@ -340,6 +341,7 @@ "insufficientFundsForRentError": "У вас недостатньо SOL, щоб покрити плату за транзакцію та оренду на рахунок. Будь ласка, додайте до свого гаманця більше SOL або зменшіть суму, яку ви надсилаєте", "introducing_cake_pay": "Представляємо Cake Pay!", "invalid_input": "Неправильні дані", + "invalid_password": "Недійсний пароль", "invoice_details": "Реквізити рахунку-фактури", "is_percentage": "є", "last_30_days": "Останні 30 днів", @@ -508,6 +510,8 @@ "rename": "Перейменувати", "rep_warning": "Представницьке попередження", "rep_warning_sub": "Ваш представник, схоже, не має доброго становища. Торкніться тут, щоб вибрати новий", + "repeat_wallet_password": "Повторіть пароль гаманця", + "repeated_password_is_incorrect": "Повторний пароль невірний. Будь ласка, повторіть пароль гаманця ще раз.", "require_for_adding_contacts": "Потрібен для додавання контактів", "require_for_all_security_and_backup_settings": "Вимагати всіх налаштувань безпеки та резервного копіювання", "require_for_assessing_wallet": "Потрібен доступ до гаманця", @@ -806,6 +810,7 @@ "unavailable_balance_description": "Недоступний баланс: ця сума включає кошти, заблоковані в незавершених транзакціях, і ті, які ви активно заморозили в налаштуваннях контролю монет. Заблоковані баланси стануть доступними після завершення відповідних транзакцій, тоді як заморожені баланси залишаються недоступними для транзакцій, доки ви не вирішите їх розморозити.", "unconfirmed": "Непідтверджений баланс", "understand": "Зрозуміло", + "unlock": "Розблокувати", "unmatched_currencies": "Валюта вашого гаманця не збігається з валютою сканованого QR-коду", "unspent_change": "Зміна", "unspent_coins_details_title": "Відомості про невитрачені монети", @@ -847,6 +852,7 @@ "wallet_menu": "Меню гаманця", "wallet_name": "Ім'я гаманця", "wallet_name_exists": "Гаманець з такою назвою вже існує", + "wallet_password_is_empty": "Пароль гаманця порожній. Пароль гаманця не повинен бути порожнім", "wallet_recovery_height": "Висота відновлення", "wallet_restoration_store_incorrect_seed_length": "Невірна довжина мнемонічної фрази", "wallet_seed": "Мнемонічна фраза гаманця", diff --git a/res/values/strings_ur.arb b/res/values/strings_ur.arb index 6730e5577..89978412c 100644 --- a/res/values/strings_ur.arb +++ b/res/values/strings_ur.arb @@ -236,6 +236,7 @@ "enter_code": "کوڈ درج کریں", "enter_seed_phrase": "اپنے بیج کا جملہ درج کریں", "enter_totp_code": "براہ کرم TOTP کوڈ درج کریں۔", + "enter_wallet_password": "پرس کا پاس ورڈ درج کریں", "enter_your_note": "اپنا نوٹ درج کریں…", "enter_your_pin": "اپنا PIN درج کریں۔", "enter_your_pin_again": "اپنا پن دوبارہ درج کریں۔", @@ -340,6 +341,7 @@ "insufficientFundsForRentError": "آپ کے پاس ٹرانزیکشن فیس اور اکاؤنٹ کے لئے کرایہ لینے کے ل enough اتنا SOL نہیں ہے۔ برائے مہربانی اپنے بٹوے میں مزید سول شامل کریں یا آپ کو بھیجنے والی سول رقم کو کم کریں", "introducing_cake_pay": "Cake پے کا تعارف!", "invalid_input": "غلط ان پٹ", + "invalid_password": "غلط پاسورڈ", "invoice_details": "رسید کی تفصیلات", "is_percentage": "ہے", "last_30_days": "آخری 30 دن", @@ -509,6 +511,8 @@ "rename": "نام تبدیل کریں۔", "rep_warning": "نمائندہ انتباہ", "rep_warning_sub": "آپ کا نمائندہ اچھ standing ے مقام پر نہیں دکھائی دیتا ہے۔ نیا منتخب کرنے کے لئے یہاں ٹیپ کریں", + "repeat_wallet_password": "بٹوے کا پاس ورڈ دہرائیں", + "repeated_password_is_incorrect": "بار بار پاس ورڈ غلط ہے۔ براہ کرم دوبارہ پرس کا پاس ورڈ دہرائیں۔", "require_for_adding_contacts": "رابطوں کو شامل کرنے کی ضرورت ہے۔", "require_for_all_security_and_backup_settings": "تمام سیکورٹی اور بیک اپ کی ترتیبات کے لیے درکار ہے۔", "require_for_assessing_wallet": "بٹوے تک رسائی کے لیے درکار ہے۔", @@ -807,6 +811,7 @@ "unavailable_balance_description": "۔ﮯﺗﺮﮐ ﮟﯿﮩﻧ ﮧﻠﺼﯿﻓ ﺎﮐ ﮯﻧﺮﮐ ﺪﻤﺠﻨﻣ ﻥﺍ ﮟﯿﮩﻧﺍ ﭖﺁ ﮧﮐ ﮏﺗ ﺐﺟ ﮟﯿﮨ ﮯﺘﮨﺭ ﯽﺋﺎﺳﺭ ﻞﺑﺎﻗﺎﻧ ﮏﺗ ﺖﻗﻭ ﺱﺍ ﮯﯿﻟ ﮯﮐ ﻦﯾﺩ ﻦﯿﻟ ﺲﻨﻠﯿﺑ ﺪﻤﺠﻨﻣ ﮧﮐ ﺐﺟ ،ﮯﮔ ﮟﯿﺋﺎﺟ ﻮﮨ ﺏﺎﯿﺘﺳﺩ ﺲﻨﻠﯿﺑ ﻞﻔﻘﻣ ﺪﻌﺑ ﮯﮐ ﮯﻧﻮﮨ ﻞﻤﮑﻣ ﻦﯾﺩ ﻦﯿﻟ ﮧﻘﻠﻌﺘﻣ ﮯﮐ ﻥﺍ ۔ﮯﮨ ﺎﮭﮐﺭ ﺮ", "unconfirmed": "غیر تصدیق شدہ بیلنس", "understand": "میں سمجھتا ہوں۔", + "unlock": "غیر مقفل", "unmatched_currencies": "آپ کے پرس کی موجودہ کرنسی اسکین شدہ QR سے مماثل نہیں ہے۔", "unspent_change": "تبدیل کریں", "unspent_coins_details_title": "غیر خرچ شدہ سککوں کی تفصیلات", @@ -848,6 +853,7 @@ "wallet_menu": "مینو", "wallet_name": "بٹوے کا نام", "wallet_name_exists": "اس نام کا پرس پہلے سے موجود ہے۔ براہ کرم ایک مختلف نام منتخب کریں یا پہلے دوسرے بٹوے کا نام تبدیل کریں۔", + "wallet_password_is_empty": "پرس کا پاس ورڈ خالی ہے۔ پرس کا پاس ورڈ خالی نہیں ہونا چاہئے", "wallet_recovery_height": "بحالی کی اونچائی", "wallet_restoration_store_incorrect_seed_length": "غلط بیج کی لمبائی", "wallet_seed": "بٹوے کا بیج", diff --git a/res/values/strings_yo.arb b/res/values/strings_yo.arb index f08855a77..a7c18087d 100644 --- a/res/values/strings_yo.arb +++ b/res/values/strings_yo.arb @@ -237,6 +237,7 @@ "enter_code": "Tẹ̀ ọ̀rọ̀", "enter_seed_phrase": "Tẹ ọrọ-iru irugbin rẹ", "enter_totp_code": "Jọwọ pọ koodu TOTP.", + "enter_wallet_password": "Tẹ ọrọ igbaniwọle apamọwọ", "enter_your_note": "Tẹ̀ àkọsílẹ̀ yín", "enter_your_pin": "Tẹ̀ òǹkà ìdánimọ̀ àdáni yín", "enter_your_pin_again": "Tún òǹkà ìdánimọ̀ àdáni yín tẹ̀", @@ -341,6 +342,7 @@ "insufficientFundsForRentError": "O ko ni Sol kan lati bo owo isanwo naa ki o yalo fun iroyin naa. Fi agbara kun Sol diẹ sii si apamọwọ rẹ tabi dinku soso naa ti o \\ 'tun n firanṣẹ", "introducing_cake_pay": "Ẹ bá Cake Pay!", "invalid_input": "Iṣawọle ti ko tọ", + "invalid_password": "Ọrọ igbaniwọle ti ko wulo", "invoice_details": "Iru awọn ẹya ọrọ", "is_percentage": "jẹ́", "last_30_days": "Ọ̀jọ̀ mọ́gbọ̀n tó kọjà", @@ -508,6 +510,8 @@ "rename": "Pààrọ̀ orúkọ", "rep_warning": "Ikilọ aṣoju", "rep_warning_sub": "Aṣoju rẹ ko han lati wa ni iduro to dara. Fọwọ ba ibi lati yan ọkan titun kan", + "repeat_wallet_password": "Tun ọrọ igbaniwọle apamọwọ naa", + "repeated_password_is_incorrect": "Ọrọ igbaniwọle tun jẹ aṣiṣe. Jọwọ tun ọrọigbaniwọle apamọwọ lẹẹkansi.", "require_for_adding_contacts": "Beere fun fifi awọn olubasọrọ kun", "require_for_all_security_and_backup_settings": "Beere fun gbogbo aabo ati awọn eto afẹyinti", "require_for_assessing_wallet": "Beere fun wiwọle si apamọwọ", @@ -806,6 +810,7 @@ "unavailable_balance_description": "Iwontunws.funfun ti ko si: Lapapọ yii pẹlu awọn owo ti o wa ni titiipa ni awọn iṣowo isunmọ ati awọn ti o ti didi ni itara ninu awọn eto iṣakoso owo rẹ. Awọn iwọntunwọnsi titiipa yoo wa ni kete ti awọn iṣowo oniwun wọn ba ti pari, lakoko ti awọn iwọntunwọnsi tio tutunini ko ni iraye si fun awọn iṣowo titi iwọ o fi pinnu lati mu wọn kuro.", "unconfirmed": "A kò tí ì jẹ́rìí ẹ̀", "understand": "Ó ye mi", + "unlock": "Sisalẹ", "unmatched_currencies": "Irú owó ti àpamọ́wọ́ yín kì í ṣe irú ti yíya àmì ìlujá", "unspent_change": "Yipada", "unspent_coins_details_title": "Àwọn owó ẹyọ t'á kò tí ì san", @@ -847,6 +852,7 @@ "wallet_menu": "Mẹ́nù", "wallet_name": "Orúkọ àpamọ́wọ́", "wallet_name_exists": "Ẹ ti ní àpamọ́wọ́ pẹ̀lú orúkọ̀ yẹn. Ẹ jọ̀wọ́ yàn orúkọ̀ tó yàtọ̀ tàbí pààrọ̀ orúkọ ti àpamọ́wọ́ tẹ́lẹ̀.", + "wallet_password_is_empty": "Ọrọ igbaniwọle apamọwọ ti ṣofo. Ọrọ igbaniwọle apamọwọ ko yẹ ki o ṣofo", "wallet_recovery_height": "Iga Imularada", "wallet_restoration_store_incorrect_seed_length": "Gígùn hóró tí a máa ń lò kọ́ ni èyí", "wallet_seed": "Hóró àpamọ́wọ́", diff --git a/res/values/strings_zh.arb b/res/values/strings_zh.arb index 1f01662bd..33cdd15c3 100644 --- a/res/values/strings_zh.arb +++ b/res/values/strings_zh.arb @@ -236,6 +236,7 @@ "enter_code": "输入代码", "enter_seed_phrase": "输入您的种子短语", "enter_totp_code": "请输入 TOTP 代码。", + "enter_wallet_password": "输入钱包密码", "enter_your_note": "输入您的笔记...", "enter_your_pin": "输入密码", "enter_your_pin_again": "再次输入您的PIN码", @@ -340,6 +341,7 @@ "insufficientFundsForRentError": "您没有足够的溶胶来支付该帐户的交易费和租金。请在钱包中添加更多溶胶或减少您发送的溶胶量", "introducing_cake_pay": "介绍 Cake Pay!", "invalid_input": "输入无效", + "invalid_password": "无效的密码", "invoice_details": "发票明细", "is_percentage": "是", "last_30_days": "过去 30 天", @@ -507,6 +509,8 @@ "rename": "重命名", "rep_warning": "代表性警告", "rep_warning_sub": "您的代表似乎并不信誉良好。点击这里选择一个新的", + "repeat_wallet_password": "重复钱包密码", + "repeated_password_is_incorrect": "重复密码不正确。请再次重复钱包密码。", "require_for_adding_contacts": "需要添加联系人", "require_for_all_security_and_backup_settings": "需要所有安全和备份设置", "require_for_assessing_wallet": "需要访问钱包", @@ -805,6 +809,7 @@ "unavailable_balance_description": "不可用余额:此总额包括锁定在待处理交易中的资金以及您在硬币控制设置中主动冻结的资金。一旦各自的交易完成,锁定的余额将变得可用,而冻结的余额在您决定解冻之前仍然无法进行交易。", "unconfirmed": "未确认余额", "understand": "我已知晓", + "unlock": "开锁", "unmatched_currencies": "您当前钱包的货币与扫描的 QR 的货币不匹配", "unspent_change": "改变", "unspent_coins_details_title": "未使用代幣詳情", @@ -846,6 +851,7 @@ "wallet_menu": "钱包菜单", "wallet_name": "钱包名称", "wallet_name_exists": "同名的钱包已经存在", + "wallet_password_is_empty": "钱包密码为空。钱包密码不应为空", "wallet_recovery_height": "恢复高度", "wallet_restoration_store_incorrect_seed_length": "种子长度错误", "wallet_seed": "钱包种子", diff --git a/scripts/android/app_env.sh b/scripts/android/app_env.sh index 35444dcd5..c91f24622 100644 --- a/scripts/android/app_env.sh +++ b/scripts/android/app_env.sh @@ -15,15 +15,15 @@ TYPES=($MONERO_COM $CAKEWALLET $HAVEN) APP_ANDROID_TYPE=$1 MONERO_COM_NAME="Monero.com" -MONERO_COM_VERSION="1.16.2" -MONERO_COM_BUILD_NUMBER=96 +MONERO_COM_VERSION="1.16.3" +MONERO_COM_BUILD_NUMBER=97 MONERO_COM_BUNDLE_ID="com.monero.app" MONERO_COM_PACKAGE="com.monero.app" MONERO_COM_SCHEME="monero.com" CAKEWALLET_NAME="Cake Wallet" -CAKEWALLET_VERSION="4.19.2" -CAKEWALLET_BUILD_NUMBER=223 +CAKEWALLET_VERSION="4.19.3" +CAKEWALLET_BUILD_NUMBER=224 CAKEWALLET_BUNDLE_ID="com.cakewallet.cake_wallet" CAKEWALLET_PACKAGE="com.cakewallet.cake_wallet" CAKEWALLET_SCHEME="cakewallet" diff --git a/scripts/android/shell.nix b/scripts/android/shell.nix deleted file mode 100644 index b89da09c0..000000000 --- a/scripts/android/shell.nix +++ /dev/null @@ -1,16 +0,0 @@ -{ pkgs ? import <nixpkgs> {} }: - -pkgs.mkShell { - buildInputs = [ - pkgs.curl - pkgs.unzip - pkgs.automake - pkgs.file - pkgs.pkg-config - pkgs.git - pkgs.libtool - pkgs.ncurses5 - pkgs.openjdk8 - pkgs.clang - ]; -} diff --git a/scripts/ios/app_env.sh b/scripts/ios/app_env.sh index 30573035a..e32b3e9f3 100644 --- a/scripts/ios/app_env.sh +++ b/scripts/ios/app_env.sh @@ -13,13 +13,13 @@ TYPES=($MONERO_COM $CAKEWALLET $HAVEN) APP_IOS_TYPE=$1 MONERO_COM_NAME="Monero.com" -MONERO_COM_VERSION="1.16.2" -MONERO_COM_BUILD_NUMBER=94 +MONERO_COM_VERSION="1.16.3" +MONERO_COM_BUILD_NUMBER=95 MONERO_COM_BUNDLE_ID="com.cakewallet.monero" CAKEWALLET_NAME="Cake Wallet" -CAKEWALLET_VERSION="4.19.2" -CAKEWALLET_BUILD_NUMBER=261 +CAKEWALLET_VERSION="4.19.3" +CAKEWALLET_BUILD_NUMBER=262 CAKEWALLET_BUNDLE_ID="com.fotolockr.cakewallet" HAVEN_NAME="Haven" diff --git a/scripts/ios/gen_framework.sh b/scripts/ios/gen_framework.sh index 950a7afe5..5c9bcd228 100755 --- a/scripts/ios/gen_framework.sh +++ b/scripts/ios/gen_framework.sh @@ -28,4 +28,4 @@ fi cd $FRWK_DIR # go to iOS framework dir lipo -create $DYLIB_LINK_PATH -output WowneroWallet -echo "Generated ${FRWK_DIR}" \ No newline at end of file +echo "Generated ${FRWK_DIR}" diff --git a/scripts/linux/app_config.sh b/scripts/linux/app_config.sh new file mode 100755 index 000000000..b4ca1423c --- /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 --polygon --nano --bitcoinCash --solana --tron --wownero --excludeFlutterSecureStorage";; +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.fish b/scripts/linux/app_env.fish new file mode 100644 index 000000000..8dec90ce3 --- /dev/null +++ b/scripts/linux/app_env.fish @@ -0,0 +1,35 @@ +#!/usr/bin/env fish + +set -g APP_LINUX_NAME "" +set -g APP_LINUX_VERSION "" +set -g APP_LINUX_BUILD_NUMBER "" + +set -g CAKEWALLET "cakewallet" + +set -g TYPES $CAKEWALLET +set -g APP_LINUX_TYPE $CAKEWALLET + +if test -n "$argv[1]" + set -g APP_LINUX_TYPE $argv[1] +end + +set -g CAKEWALLET_NAME "Cake Wallet" +set -g CAKEWALLET_VERSION "1.9.0" +set -g CAKEWALLET_BUILD_NUMBER 29 + +if not contains -- $APP_LINUX_TYPE $TYPES + echo "Wrong app type." + exit 1 +end + +switch $APP_LINUX_TYPE + case $CAKEWALLET + set -g APP_LINUX_NAME $CAKEWALLET_NAME + set -g APP_LINUX_VERSION $CAKEWALLET_VERSION + set -g APP_LINUX_BUILD_NUMBER $CAKEWALLET_BUILD_NUMBER +end + +export APP_LINUX_TYPE +export APP_LINUX_NAME +export APP_LINUX_VERSION +export APP_LINUX_BUILD_NUMBER diff --git a/scripts/linux/app_env.sh b/scripts/linux/app_env.sh new file mode 100755 index 000000000..729cf376b --- /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.9.2" +CAKEWALLET_BUILD_NUMBER=30 + +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..cbefec08e --- /dev/null +++ b/scripts/linux/build_monero.sh @@ -0,0 +1,40 @@ +#!/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.3.2 +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 \ + -DMANUAL_SUBMODULES=1 \ + ../.. + +make wallet_api -j$(($(nproc) / 2)) + +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..5dc512527 --- /dev/null +++ b/scripts/linux/build_monero_all.sh @@ -0,0 +1,21 @@ +#!/bin/bash + + +. ./config.sh + + +set -x -e + +cd "$(dirname "$0")" + +NPROC="-j$(nproc)" + +../prepare_moneroc.sh + +for COIN in monero wownero; +do + pushd ../monero_c + ./build_single.sh ${COIN} $(gcc -dumpmachine) $NPROC + popd + unxz -f ../monero_c/release/${COIN}/$(gcc -dumpmachine)_libwallet2_api_c.so.xz +done 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/gcc10.nix b/scripts/linux/gcc10.nix new file mode 100644 index 000000000..dfc01986a --- /dev/null +++ b/scripts/linux/gcc10.nix @@ -0,0 +1,12 @@ +with import <nixpkgs> {}; +gcc10Stdenv.mkDerivation { + name="gcc10-stdenv"; + buildInputs = [ + pkgs.cmake + pkgs.pkg-config + pkgs.autoconf + pkgs.libtool + pkgs.expat + ]; +} + 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/scripts/windows/build_exe_installer.iss b/scripts/windows/build_exe_installer.iss index ef26941d7..216f367ca 100644 --- a/scripts/windows/build_exe_installer.iss +++ b/scripts/windows/build_exe_installer.iss @@ -1,5 +1,5 @@ #define MyAppName "Cake Wallet" -#define MyAppVersion "0.0.3" +#define MyAppVersion "0.0.4" #define MyAppPublisher "Cake Labs LLC" #define MyAppURL "https://cakewallet.com/" #define MyAppExeName "CakeWallet.exe" diff --git a/tool/configure.dart b/tool/configure.dart index c2802e500..a4f1d6198 100644 --- a/tool/configure.dart +++ b/tool/configure.dart @@ -151,7 +151,7 @@ abstract class Bitcoin { String? passphrase, }); 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}); WalletCredentials createBitcoinHardwareWalletCredentials({required String name, required HardwareAccountData accountData, WalletInfo? walletInfo}); List<String> getWordList(); Map<String, String> getWalletKeys(Object wallet); @@ -180,8 +180,8 @@ abstract class Bitcoin { List<Unspent> getUnspents(Object wallet); Future<void> updateUnspents(Object wallet); WalletService createBitcoinWalletService( - Box<WalletInfo> walletInfoSource, Box<UnspentCoinsInfo> unspentCoinSource, bool alwaysScan); - WalletService createLitecoinWalletService(Box<WalletInfo> walletInfoSource, Box<UnspentCoinsInfo> unspentCoinSource, bool alwaysScan); + Box<WalletInfo> walletInfoSource, Box<UnspentCoinsInfo> unspentCoinSource, bool alwaysScan, bool isDirect); + WalletService createLitecoinWalletService(Box<WalletInfo> walletInfoSource, Box<UnspentCoinsInfo> unspentCoinSource, bool alwaysScan, bool isDirect); TransactionPriority getBitcoinTransactionPriorityMedium(); TransactionPriority getBitcoinTransactionPriorityCustom(); TransactionPriority getLitecoinTransactionPriorityMedium(); @@ -374,7 +374,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, required bool isPolyseed, String password}); + WalletCredentials createMoneroNewWalletCredentials({required String name, required String language, required bool isPolyseed, String? password}); Map<String, String> getKeys(Object wallet); int? getRestoreHeight(Object wallet); Object createMoneroTransactionCreationCredentials({required List<Output> outputs, required TransactionPriority priority}); @@ -739,7 +739,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}); @@ -833,8 +833,8 @@ import 'package:eth_sig_util/util/utils.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}); WalletCredentials createEthereumHardwareWalletCredentials({required String name, required HardwareAccountData hwAccountData, WalletInfo? walletInfo}); @@ -937,8 +937,8 @@ import 'package:eth_sig_util/util/utils.dart'; const polygonContent = """ abstract class Polygon { List<String> getPolygonWordList(String language); - WalletService createPolygonWalletService(Box<WalletInfo> walletInfoSource); - WalletCredentials createPolygonNewWalletCredentials({required String name, WalletInfo? walletInfo}); + WalletService createPolygonWalletService(Box<WalletInfo> walletInfoSource, bool isDirect); + WalletCredentials createPolygonNewWalletCredentials({required String name, WalletInfo? walletInfo, String? password}); WalletCredentials createPolygonRestoreWalletFromSeedCredentials({required String name, required String mnemonic, required String password}); WalletCredentials createPolygonRestoreWalletFromPrivateKey({required String name, required String privateKey, required String password}); WalletCredentials createPolygonHardwareWalletCredentials({required String name, required HardwareAccountData hwAccountData, WalletInfo? walletInfo}); @@ -1022,10 +1022,10 @@ abstract class BitcoinCash { String getCashAddrFormat(String address); WalletService createBitcoinCashWalletService( - Box<WalletInfo> walletInfoSource, Box<UnspentCoinsInfo> unspentCoinSource); + Box<WalletInfo> walletInfoSource, Box<UnspentCoinsInfo> unspentCoinSource, bool isDirect); WalletCredentials createBitcoinCashNewWalletCredentials( - {required String name, WalletInfo? walletInfo}); + {required String name, WalletInfo? walletInfo, String? password}); WalletCredentials createBitcoinCashRestoreWalletFromSeedCredentials( {required String name, required String mnemonic, required String password}); @@ -1102,11 +1102,11 @@ abstract class Nano { void setCurrentAccount(Object wallet, int id, String label, String? balance); - WalletService createNanoWalletService(Box<WalletInfo> walletInfoSource); + WalletService createNanoWalletService(Box<WalletInfo> walletInfoSource, bool isDirect); WalletCredentials createNanoNewWalletCredentials({ required String name, - String password, + String? password, }); WalletCredentials createNanoRestoreWalletFromSeedCredentials({ @@ -1220,9 +1220,9 @@ import 'package:cw_solana/solana_wallet_creation_credentials.dart'; const solanaContent = """ abstract class Solana { List<String> getSolanaWordList(String language); - WalletService createSolanaWalletService(Box<WalletInfo> walletInfoSource); + WalletService createSolanaWalletService(Box<WalletInfo> walletInfoSource, bool isDirect); WalletCredentials createSolanaNewWalletCredentials( - {required String name, WalletInfo? walletInfo}); + {required String name, WalletInfo? walletInfo, String? password}); WalletCredentials createSolanaRestoreWalletFromSeedCredentials( {required String name, required String mnemonic, required String password}); WalletCredentials createSolanaRestoreWalletFromPrivateKey( @@ -1307,8 +1307,8 @@ import 'package:cw_tron/tron_wallet_service.dart'; const tronContent = """ abstract class Tron { List<String> getTronWordList(String language); - WalletService createTronWalletService(Box<WalletInfo> walletInfoSource); - WalletCredentials createTronNewWalletCredentials({required String name, WalletInfo? walletInfo}); + WalletService createTronWalletService(Box<WalletInfo> walletInfoSource, bool isDirect); + WalletCredentials createTronNewWalletCredentials({required String name, WalletInfo? walletInfo, String? password}); WalletCredentials createTronRestoreWalletFromSeedCredentials({required String name, required String mnemonic, required String password}); WalletCredentials createTronRestoreWalletFromPrivateKey({required String name, required String privateKey, required String password}); String getAddress(WalletBase wallet);