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 88% rename from .github/workflows/pr_test_build.yml rename to .github/workflows/pr_test_build_android.yml index a7c5fd66a..0b8309aad 100644 --- a/.github/workflows/pr_test_build.yml +++ b/.github/workflows/pr_test_build_android.yml @@ -56,7 +56,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: | @@ -212,35 +214,3 @@ jobs: title: "${{ env.BRANCH_NAME }}.apk" filename: ${{ env.BRANCH_NAME }}.apk initial_comment: ${{ github.event.head_commit.message }} - - - name: 🦾 Cache gradle - uses: gradle/actions/setup-gradle@v3 - - - name: 🦾 Cache AVD - uses: actions/cache@v4 - id: avd-cache - with: - path: | - ~/.android/avd/* - ~/.android/adb* - key: avd-${{ matrix.api-level }} - - - name: 🦾 Create AVD and generate snapshot for caching - if: steps.avd-cache.outputs.cache-hit != 'true' - uses: reactivecircus/android-emulator-runner@v2 - with: - api-level: ${{ matrix.api-level }} - force-avd-creation: false - emulator-options: -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none - disable-animations: false - script: echo "Generated AVD snapshot for caching." - - - name: 🚀 Integration tests on Android Emualator - timeout-minutes: 30 - uses: reactivecircus/android-emulator-runner@v2 - with: - api-level: ${{ matrix.api-level }} - force-avd-creation: false - emulator-options: -no-snapshot-save -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none - disable-animations: true - script: flutter test integration_test 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 d8f5bb988..970241189 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 @@
- +![logo](.github/assets/Logo_CakeWallet.png)
diff --git a/android/app/build.gradle b/android/app/build.gradle index 60defb1fd..2f5427531 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -91,5 +91,4 @@ dependencies { testImplementation 'junit:junit:4.12' androidTestImplementation 'androidx.test:runner:1.3.0' androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0' - implementation 'com.unstoppabledomains:resolution:5.0.0' } diff --git a/android/app/src/main/java/com/cakewallet/cake_wallet/MainActivity.java b/android/app/src/main/java/com/cakewallet/cake_wallet/MainActivity.java index 29b37c46c..df3f6be01 100644 --- a/android/app/src/main/java/com/cakewallet/cake_wallet/MainActivity.java +++ b/android/app/src/main/java/com/cakewallet/cake_wallet/MainActivity.java @@ -20,14 +20,10 @@ import android.net.Uri; import android.os.PowerManager; import android.provider.Settings; -import com.unstoppabledomains.resolution.DomainResolution; -import com.unstoppabledomains.resolution.Resolution; - import java.security.SecureRandom; public class MainActivity extends FlutterFragmentActivity { final String UTILS_CHANNEL = "com.cake_wallet/native_utils"; - final int UNSTOPPABLE_DOMAIN_MIN_VERSION_SDK = 24; boolean isAppSecure = false; @Override @@ -53,14 +49,6 @@ public class MainActivity extends FlutterFragmentActivity { random.nextBytes(bytes); handler.post(() -> result.success(bytes)); break; - case "getUnstoppableDomainAddress": - int version = Build.VERSION.SDK_INT; - if (version >= UNSTOPPABLE_DOMAIN_MIN_VERSION_SDK) { - getUnstoppableDomainAddress(call, result); - } else { - handler.post(() -> result.success("")); - } - break; case "setIsAppSecure": isAppSecure = call.argument("isAppSecure"); if (isAppSecure) { @@ -85,23 +73,6 @@ public class MainActivity extends FlutterFragmentActivity { } } - private void getUnstoppableDomainAddress(@NonNull MethodCall call, @NonNull MethodChannel.Result result) { - DomainResolution resolution = new Resolution(); - Handler handler = new Handler(Looper.getMainLooper()); - String domain = call.argument("domain"); - String ticker = call.argument("ticker"); - - AsyncTask.execute(() -> { - try { - String address = resolution.getAddress(domain, ticker); - handler.post(() -> result.success(address)); - } catch (Exception e) { - System.out.println("Expected Address, but got " + e.getMessage()); - handler.post(() -> result.success("")); - } - }); - } - private void disableBatteryOptimization() { String packageName = getPackageName(); PowerManager pm = (PowerManager) getSystemService(POWER_SERVICE); diff --git a/android/app/src/main/java/com/cakewallet/haven/MainActivity.java b/android/app/src/main/java/com/cakewallet/haven/MainActivity.java index d0a465d22..83a790683 100644 --- a/android/app/src/main/java/com/cakewallet/haven/MainActivity.java +++ b/android/app/src/main/java/com/cakewallet/haven/MainActivity.java @@ -19,14 +19,10 @@ import android.net.Uri; import android.os.PowerManager; import android.provider.Settings; -import com.unstoppabledomains.resolution.DomainResolution; -import com.unstoppabledomains.resolution.Resolution; - import java.security.SecureRandom; public class MainActivity extends FlutterFragmentActivity { final String UTILS_CHANNEL = "com.cake_wallet/native_utils"; - final int UNSTOPPABLE_DOMAIN_MIN_VERSION_SDK = 24; @Override public void configureFlutterEngine(@NonNull FlutterEngine flutterEngine) { @@ -51,14 +47,6 @@ public class MainActivity extends FlutterFragmentActivity { random.nextBytes(bytes); handler.post(() -> result.success(bytes)); break; - case "getUnstoppableDomainAddress": - int version = Build.VERSION.SDK_INT; - if (version >= UNSTOPPABLE_DOMAIN_MIN_VERSION_SDK) { - getUnstoppableDomainAddress(call, result); - } else { - handler.post(() -> result.success("")); - } - break; case "disableBatteryOptimization": disableBatteryOptimization(); handler.post(() -> result.success(null)); @@ -75,23 +63,6 @@ public class MainActivity extends FlutterFragmentActivity { } } - private void getUnstoppableDomainAddress(@NonNull MethodCall call, @NonNull MethodChannel.Result result) { - DomainResolution resolution = new Resolution(); - Handler handler = new Handler(Looper.getMainLooper()); - String domain = call.argument("domain"); - String ticker = call.argument("ticker"); - - AsyncTask.execute(() -> { - try { - String address = resolution.getAddress(domain, ticker); - handler.post(() -> result.success(address)); - } catch (Exception e) { - System.out.println("Expected Address, but got " + e.getMessage()); - handler.post(() -> result.success("")); - } - }); - } - private void disableBatteryOptimization() { String packageName = getPackageName(); PowerManager pm = (PowerManager) getSystemService(POWER_SERVICE); diff --git a/android/app/src/main/java/com/monero/app/MainActivity.java b/android/app/src/main/java/com/monero/app/MainActivity.java index 49c368ec7..e6306d27b 100644 --- a/android/app/src/main/java/com/monero/app/MainActivity.java +++ b/android/app/src/main/java/com/monero/app/MainActivity.java @@ -19,14 +19,10 @@ import android.net.Uri; import android.os.PowerManager; import android.provider.Settings; -import com.unstoppabledomains.resolution.DomainResolution; -import com.unstoppabledomains.resolution.Resolution; - import java.security.SecureRandom; public class MainActivity extends FlutterFragmentActivity { final String UTILS_CHANNEL = "com.cake_wallet/native_utils"; - final int UNSTOPPABLE_DOMAIN_MIN_VERSION_SDK = 24; boolean isAppSecure = false; @Override @@ -52,14 +48,6 @@ public class MainActivity extends FlutterFragmentActivity { random.nextBytes(bytes); handler.post(() -> result.success(bytes)); break; - case "getUnstoppableDomainAddress": - int version = Build.VERSION.SDK_INT; - if (version >= UNSTOPPABLE_DOMAIN_MIN_VERSION_SDK) { - getUnstoppableDomainAddress(call, result); - } else { - handler.post(() -> result.success("")); - } - break; case "setIsAppSecure": isAppSecure = call.argument("isAppSecure"); if (isAppSecure) { @@ -84,23 +72,6 @@ public class MainActivity extends FlutterFragmentActivity { } } - private void getUnstoppableDomainAddress(@NonNull MethodCall call, @NonNull MethodChannel.Result result) { - DomainResolution resolution = new Resolution(); - Handler handler = new Handler(Looper.getMainLooper()); - String domain = call.argument("domain"); - String ticker = call.argument("ticker"); - - AsyncTask.execute(() -> { - try { - String address = resolution.getAddress(domain, ticker); - handler.post(() -> result.success(address)); - } catch (Exception e) { - System.out.println("Expected Address, but got " + e.getMessage()); - handler.post(() -> result.success("")); - } - }); - } - private void disableBatteryOptimization() { String packageName = getPackageName(); PowerManager pm = (PowerManager) getSystemService(POWER_SERVICE); diff --git a/android/build.gradle b/android/build.gradle index aa9f5005d..7ddb75179 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -2,7 +2,7 @@ buildscript { ext.kotlin_version = '1.8.21' repositories { google() - jcenter() + mavenCentral() } dependencies { @@ -15,7 +15,7 @@ buildscript { allprojects { repositories { google() - jcenter() + mavenCentral() } } diff --git a/assets/images/dfx_dark.png b/assets/images/dfx_dark.png index cbba87372..6ac112eae 100644 Binary files a/assets/images/dfx_dark.png and b/assets/images/dfx_dark.png differ diff --git a/assets/images/dfx_light.png b/assets/images/dfx_light.png index e4836be3e..a045d3e68 100644 Binary files a/assets/images/dfx_light.png and b/assets/images/dfx_light.png differ diff --git a/assets/images/moonpay.png b/assets/images/moonpay.png index b02af6c00..088c93d59 100644 Binary files a/assets/images/moonpay.png and b/assets/images/moonpay.png differ diff --git a/assets/images/moonpay_dark.png b/assets/images/moonpay_dark.png index 872e322e2..21de98eb4 100644 Binary files a/assets/images/moonpay_dark.png and b/assets/images/moonpay_dark.png differ diff --git a/assets/images/moonpay_light.png b/assets/images/moonpay_light.png index c76ae6e74..3d3de2e4f 100644 Binary files a/assets/images/moonpay_light.png and b/assets/images/moonpay_light.png differ diff --git a/assets/images/trocador.png b/assets/images/trocador.png index 67c9f221c..37e643de4 100644 Binary files a/assets/images/trocador.png and b/assets/images/trocador.png differ 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, + 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, + 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, 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 walletInfoSource; final Box 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 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(); } 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> _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; } diff --git a/cw_bitcoin/lib/electrum_wallet.dart b/cw_bitcoin/lib/electrum_wallet.dart index e1b038beb..501d94e54 100644 --- a/cw_bitcoin/lib/electrum_wallet.dart +++ b/cw_bitcoin/lib/electrum_wallet.dart @@ -5,6 +5,7 @@ import 'dart:isolate'; import 'dart:math'; 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, 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); } @@ -127,6 +132,8 @@ abstract class ElectrumWalletBase final String? _mnemonic; Bip32Slip10Secp256k1 get hd => accountHD.childKey(Bip32KeyIndex(0)); + + final EncryptionFileUtils encryptionFileUtils; final String? passphrase; @override @@ -167,6 +174,9 @@ abstract class ElectrumWalletBase WalletKeysData get walletKeysData => WalletKeysData(mnemonic: _mnemonic, xPub: xpub, passphrase: passphrase); + @override + String get password => _password; + BasedUtxoNetwork network; @override @@ -455,7 +465,6 @@ abstract class ElectrumWalletBase } } - node!.isElectrs = false; node!.save(); return node!.isElectrs!; @@ -1130,12 +1139,12 @@ abstract class ElectrumWalletBase @override Future 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(); } @@ -2258,4 +2267,3 @@ class UtxoDetails { required this.spendsUnconfirmedTX, }); } - 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 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? ?? []; final mnemonic = data['mnemonic'] as String?; diff --git a/cw_bitcoin/lib/litecoin_wallet.dart b/cw_bitcoin/lib/litecoin_wallet.dart index 64e53ca5d..d8c04dba6 100644 --- a/cw_bitcoin/lib/litecoin_wallet.dart +++ b/cw_bitcoin/lib/litecoin_wallet.dart @@ -4,13 +4,14 @@ 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_bitcoin/bitcoin_transaction_priority.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'; import 'package:cw_bitcoin/electrum_wallet_snapshot.dart'; import 'package:cw_bitcoin/litecoin_wallet_addresses.dart'; -import 'package:cw_core/crypto_currency.dart'; import 'package:cw_core/transaction_priority.dart'; -import 'package:cw_core/unspent_coins_info.dart'; import 'package:cw_core/wallet_info.dart'; import 'package:cw_core/wallet_keys_file.dart'; import 'package:flutter/foundation.dart'; @@ -28,6 +29,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { required WalletInfo walletInfo, required Box unspentCoinsInfo, required Uint8List seedBytes, + required EncryptionFileUtils encryptionFileUtils, String? addressPageType, List? initialAddresses, ElectrumBalance? initialBalance, @@ -42,6 +44,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { initialAddresses: initialAddresses, initialBalance: initialBalance, seedBytes: seedBytes, + encryptionFileUtils: encryptionFileUtils, currency: CryptoCurrency.ltc) { walletAddresses = LitecoinWalletAddresses( walletInfo, @@ -62,6 +65,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { required String password, required WalletInfo walletInfo, required Box unspentCoinsInfo, + required EncryptionFileUtils encryptionFileUtils, String? passphrase, String? addressPageType, List? initialAddresses, @@ -89,6 +93,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { unspentCoinsInfo: unspentCoinsInfo, initialAddresses: initialAddresses, initialBalance: initialBalance, + encryptionFileUtils: encryptionFileUtils, seedBytes: seedBytes, initialRegularAddressIndex: initialRegularAddressIndex, initialChangeAddressIndex: initialChangeAddressIndex, @@ -96,19 +101,24 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { ); } - static Future open({ - required String name, - required WalletInfo walletInfo, - required Box unspentCoinsInfo, - required String password, - }) async { + static Future open( + {required String name, + required WalletInfo walletInfo, + required Box unspentCoinsInfo, + required String password, + required EncryptionFileUtils encryptionFileUtils}) async { final hasKeysFile = await WalletKeysFile.hasKeysFile(name, walletInfo.type); ElectrumWalletSnapshot? snp = null; try { snp = await ElectrumWalletSnapshot.load( - name, walletInfo.type, password, LitecoinNetwork.mainnet); + encryptionFileUtils, + name, + walletInfo.type, + password, + LitecoinNetwork.mainnet, + ); } catch (e) { if (!hasKeysFile) rethrow; } @@ -119,7 +129,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( @@ -130,6 +145,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 7025b72e5..a46b12a2e 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'; @@ -16,11 +17,13 @@ import 'package:bip39/bip39.dart' as bip39; class LitecoinWalletService extends WalletService< BitcoinNewWalletCredentials, BitcoinRestoreWalletFromSeedCredentials, - BitcoinRestoreWalletFromWIFCredentials,BitcoinNewWalletCredentials> { - LitecoinWalletService(this.walletInfoSource, this.unspentCoinsInfoSource); + BitcoinRestoreWalletFromWIFCredentials, + BitcoinNewWalletCredentials> { + LitecoinWalletService(this.walletInfoSource, this.unspentCoinsInfoSource, this.isDirect); final Box walletInfoSource; final Box unspentCoinsInfoSource; + final bool isDirect; @override WalletType getType() => WalletType.litecoin; @@ -28,12 +31,13 @@ class LitecoinWalletService extends WalletService< @override Future create(BitcoinNewWalletCredentials credentials, {bool? isTestnet}) async { final wallet = await LitecoinWalletBase.create( - mnemonic: await generateElectrumMnemonic(), - password: credentials.password!, - passphrase: credentials.passphrase, - walletInfo: credentials.walletInfo!, - unspentCoinsInfo: unspentCoinsInfoSource); - + mnemonic: await generateElectrumMnemonic(), + password: credentials.password!, + passphrase: credentials.passphrase, + walletInfo: credentials.walletInfo!, + unspentCoinsInfo: unspentCoinsInfoSource, + encryptionFileUtils: encryptionFileUtilsFor(isDirect), + ); await wallet.save(); await wallet.init(); @@ -46,21 +50,29 @@ class LitecoinWalletService extends WalletService< @override Future openWallet(String name, String password) async { - final walletInfo = walletInfoSource.values.firstWhereOrNull( - (info) => info.id == WalletBase.idFor(name, getType()))!; + final walletInfo = walletInfoSource.values + .firstWhereOrNull((info) => info.id == WalletBase.idFor(name, getType()))!; try { final wallet = await LitecoinWalletBase.open( - password: password, name: name, walletInfo: walletInfo, - unspentCoinsInfo: unspentCoinsInfoSource); + password: password, + name: name, + walletInfo: walletInfo, + unspentCoinsInfo: unspentCoinsInfoSource, + encryptionFileUtils: encryptionFileUtilsFor(isDirect), + ); await wallet.init(); saveBackup(name); return wallet; } catch (_) { await restoreWalletFilesFromBackup(name); final wallet = await LitecoinWalletBase.open( - password: password, name: name, walletInfo: walletInfo, - unspentCoinsInfo: unspentCoinsInfoSource); + password: password, + name: name, + walletInfo: walletInfo, + unspentCoinsInfo: unspentCoinsInfoSource, + encryptionFileUtils: encryptionFileUtilsFor(isDirect), + ); await wallet.init(); return wallet; } @@ -68,22 +80,23 @@ class LitecoinWalletService extends WalletService< @override Future remove(String wallet) async { - File(await pathForWalletDir(name: wallet, type: getType())) - .delete(recursive: true); - final walletInfo = walletInfoSource.values.firstWhereOrNull( - (info) => info.id == WalletBase.idFor(wallet, getType()))!; + File(await pathForWalletDir(name: wallet, type: getType())).delete(recursive: true); + final walletInfo = walletInfoSource.values + .firstWhereOrNull((info) => info.id == WalletBase.idFor(wallet, getType()))!; await walletInfoSource.delete(walletInfo.key); } @override Future rename(String currentName, String password, String newName) async { - final currentWalletInfo = walletInfoSource.values.firstWhereOrNull( - (info) => info.id == WalletBase.idFor(currentName, getType()))!; + final currentWalletInfo = walletInfoSource.values + .firstWhereOrNull((info) => info.id == WalletBase.idFor(currentName, getType()))!; final currentWallet = await LitecoinWalletBase.open( - password: password, - name: currentName, - walletInfo: currentWalletInfo, - unspentCoinsInfo: unspentCoinsInfoSource); + password: password, + name: currentName, + walletInfo: currentWalletInfo, + unspentCoinsInfo: unspentCoinsInfoSource, + encryptionFileUtils: encryptionFileUtilsFor(isDirect), + ); await currentWallet.renameWalletFiles(newName); await saveBackup(newName); @@ -97,27 +110,30 @@ class LitecoinWalletService extends WalletService< @override Future restoreFromHardwareWallet(BitcoinNewWalletCredentials credentials) { - throw UnimplementedError("Restoring a Litecoin wallet from a hardware wallet is not yet supported!"); + throw UnimplementedError( + "Restoring a Litecoin wallet from a hardware wallet is not yet supported!"); } @override - Future restoreFromKeys( - BitcoinRestoreWalletFromWIFCredentials credentials, {bool? isTestnet}) async => + Future restoreFromKeys(BitcoinRestoreWalletFromWIFCredentials credentials, + {bool? isTestnet}) async => throw UnimplementedError(); @override - Future restoreFromSeed( - BitcoinRestoreWalletFromSeedCredentials credentials, {bool? isTestnet}) async { + Future restoreFromSeed(BitcoinRestoreWalletFromSeedCredentials credentials, + {bool? isTestnet}) async { if (!validateMnemonic(credentials.mnemonic) && !bip39.validateMnemonic(credentials.mnemonic)) { throw LitecoinMnemonicIsIncorrectException(); } 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 d768b11ad..bf743f921 100644 --- a/cw_bitcoin/pubspec.lock +++ b/cw_bitcoin/pubspec.lock @@ -164,6 +164,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: @@ -236,6 +245,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: @@ -837,6 +854,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, required Uint8List seedBytes, + required EncryptionFileUtils encryptionFileUtils, BitcoinAddressType? addressPageType, List? 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, + required EncryptionFileUtils encryptionFileUtils, String? addressPageType, List? 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, 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 walletInfoSource; final Box 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 write({required String path, required String password, required String data}); + Future read({required String path, required String password}); +} + +class Salsa20EncryhptionFileUtils extends EncryptionFileUtils { + // Requires legacy complex key + iv as password + @override + Future 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 read({required String path, required String password}) async + => await file.read(path: path, password: password); +} + +class XChaCha20EncryptionFileUtils extends EncryptionFileUtils { + @override + Future 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 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 a616b0bfd..f7af15224 100644 --- a/cw_core/lib/wallet_base.dart +++ b/cw_core/lib/wallet_base.dart @@ -84,6 +84,8 @@ abstract class WalletBase changePassword(String password); + String get password; + Future? 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 makeKeysFilePath() async => "${await makePath()}.keys"; - Future saveKeysFile(String password, [bool isBackup = false]) async { + Future 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 createKeysFile( - String name, WalletType type, String password, WalletKeysData walletKeysData, + static Future 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 readKeysFile(String name, WalletType type, String password) async { + static Future 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; return WalletKeysData.fromJSON(data); } catch (e) { @@ -72,12 +81,12 @@ mixin WalletKeysFile; 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 53946925c..c2bdda5f1 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 b2c16276d..070779caa 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 open( - {required String name, required String password, required WalletInfo walletInfo}) async { + static Future 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? 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; } 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 { - 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 { mnemonic: mnemonic, password: credentials.password!, client: client, + encryptionFileUtils: encryptionFileUtilsFor(isDirect), ); await wallet.init(); @@ -46,6 +48,7 @@ class EthereumWalletService extends EVMChainWalletService { name: name, password: password, walletInfo: walletInfo, + encryptionFileUtils: encryptionFileUtilsFor(isDirect), ); await wallet.init(); @@ -59,6 +62,7 @@ class EthereumWalletService extends EVMChainWalletService { name: name, password: password, walletInfo: walletInfo, + encryptionFileUtils: encryptionFileUtilsFor(isDirect), ); await wallet.init(); await wallet.save(); @@ -71,7 +75,11 @@ class EthereumWalletService extends EVMChainWalletService { 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 { walletInfo: credentials.walletInfo!, password: credentials.password!, client: client, + encryptionFileUtils: encryptionFileUtilsFor(isDirect), ); await wallet.init(); @@ -114,6 +123,7 @@ class EthereumWalletService extends EVMChainWalletService { privateKey: credentials.privateKey, walletInfo: credentials.walletInfo!, client: client, + encryptionFileUtils: encryptionFileUtilsFor(isDirect), ); await wallet.init(); @@ -135,6 +145,7 @@ class EthereumWalletService extends EVMChainWalletService { 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 with Store { - EVMChainTransactionHistoryBase({required this.walletInfo, required String password}) + EVMChainTransactionHistoryBase( + {required this.walletInfo, required String password, required this.encryptionFileUtils}) : _password = password { transactions = ObservableMap(); } @@ -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 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 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 extends WalletSer EVMChainRestoreWalletFromSeedCredentials, EVMChainRestoreWalletFromPrivateKey, EVMChainRestoreWalletFromHardware> { - EVMChainWalletService(this.walletInfoSource); + EVMChainWalletService(this.walletInfoSource, this.isDirect); final Box 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 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 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 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/android/build.gradle b/cw_haven/android/build.gradle index fb941f657..87e8df641 100644 --- a/cw_haven/android/build.gradle +++ b/cw_haven/android/build.gradle @@ -5,7 +5,7 @@ buildscript { ext.kotlin_version = '1.7.10' repositories { google() - jcenter() + mavenCentral() } dependencies { @@ -17,7 +17,7 @@ buildscript { rootProject.allprojects { repositories { google() - jcenter() + mavenCentral() } } 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 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 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 667eb1744..1369675f5 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 "$<$>:-O3>") + target_compile_definitions(${TARGET} PRIVATE "$<$>: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 + +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 + +// 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 $) + 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 +#ifdef GDK_WINDOWING_X11 +#include +#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 + +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 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 with Store { MoneroWalletBase( {required WalletInfo walletInfo, - required Box unspentCoinsInfo}) + required Box unspentCoinsInfo, + required String password}) : balance = ObservableMap.of({ CryptoCurrency.xmr: MoneroBalance( fullBalance: monero_wallet.getFullBalance(accountIndex: 0), @@ -59,6 +62,7 @@ abstract class MoneroWalletBase extends WalletBase 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 unspentCoins; + String _password; Future 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 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 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 +#include +#include + +#include + +#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 + +// 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 $) + 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 + +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 7cbff56d2..a92ad5b4c 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 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 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 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 with Store { - NanoTransactionHistoryBase({required this.walletInfo, required String password}) - : _password = password { +abstract class NanoTransactionHistoryBase extends TransactionHistoryBase + with Store { + NanoTransactionHistoryBase({ + required this.walletInfo, + required String password, + required this.encryptionFileUtils, + }) : _password = password { transactions = ObservableMap(); } final WalletInfo walletInfo; + final EncryptionFileUtils encryptionFileUtils; String _password; Future 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> _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; } 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.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 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 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? 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; } 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 { - NanoWalletService(this.walletInfoSource); + NanoWalletService(this.walletInfoSource, this.isDirect); final Box walletInfoSource; + final bool isDirect; static bool walletFilesExist(String path) => !File(path).existsSync() && !File('$path.keys').existsSync(); @@ -38,6 +40,7 @@ class NanoWalletService extends WalletService.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 open( - {required String name, required String password, required WalletInfo walletInfo}) async { + static Future 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? 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; } 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 { PolygonWalletService( - super.walletInfoSource, { + super.walletInfoSource, super.isDirect, { required this.client, }); @@ -30,6 +31,7 @@ class PolygonWalletService extends EVMChainWalletService { mnemonic: mnemonic, password: credentials.password!, client: client, + encryptionFileUtils: encryptionFileUtilsFor(isDirect), ); await wallet.init(); @@ -48,6 +50,7 @@ class PolygonWalletService extends EVMChainWalletService { name: name, password: password, walletInfo: walletInfo, + encryptionFileUtils: encryptionFileUtilsFor(isDirect), ); await wallet.init(); @@ -61,6 +64,7 @@ class PolygonWalletService extends EVMChainWalletService { name: name, password: password, walletInfo: walletInfo, + encryptionFileUtils: encryptionFileUtilsFor(isDirect), ); await wallet.init(); @@ -77,6 +81,7 @@ class PolygonWalletService extends EVMChainWalletService { privateKey: credentials.privateKey, walletInfo: credentials.walletInfo!, client: client, + encryptionFileUtils: encryptionFileUtilsFor(isDirect), ); await wallet.init(); @@ -99,6 +104,7 @@ class PolygonWalletService extends EVMChainWalletService { walletInfo: credentials.walletInfo!, password: credentials.password!, client: client, + encryptionFileUtils: encryptionFileUtilsFor(isDirect), ); await wallet.init(); @@ -120,6 +126,7 @@ class PolygonWalletService extends EVMChainWalletService { mnemonic: credentials.mnemonic, walletInfo: credentials.walletInfo!, client: client, + encryptionFileUtils: encryptionFileUtilsFor(isDirect), ); await wallet.init(); @@ -134,7 +141,11 @@ class PolygonWalletService extends EVMChainWalletService { 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_shared_external/android/build.gradle b/cw_shared_external/android/build.gradle index 8db51f0e6..64b550364 100644 --- a/cw_shared_external/android/build.gradle +++ b/cw_shared_external/android/build.gradle @@ -5,7 +5,7 @@ buildscript { ext.kotlin_version = '1.7.10' repositories { google() - jcenter() + mavenCentral() } dependencies { @@ -17,7 +17,7 @@ buildscript { rootProject.allprojects { repositories { google() - jcenter() + mavenCentral() } } 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 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 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 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 with Store { - SolanaTransactionHistoryBase({required this.walletInfo, required String password}) + SolanaTransactionHistoryBase( + {required this.walletInfo, required String password, required this.encryptionFileUtils}) : _password = password { transactions = ObservableMap(); } final WalletInfo walletInfo; + final EncryptionFileUtils encryptionFileUtils; String _password; Future init() async => await _load(); @@ -32,7 +34,7 @@ abstract class SolanaTransactionHistoryBase extends TransactionHistoryBase 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> _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 balance; - Completer _sharedPrefs = Completer(); + final Completer _sharedPrefs = Completer(); @override Ed25519HDKeyPairData get keys { @@ -343,13 +349,13 @@ abstract class SolanaWalletBase @override Future 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? 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; } 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 { - SolanaWalletService(this.walletInfoSource); + SolanaWalletService(this.walletInfoSource, this.isDirect); final Box walletInfoSource; + final bool isDirect; @override Future create(SolanaNewWalletCredentials credentials, {bool? isTestnet}) async { @@ -31,6 +33,7 @@ class SolanaWalletService extends WalletService 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 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 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 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 with Store { - TronTransactionHistoryBase({required this.walletInfo, required String password}) + TronTransactionHistoryBase( + {required this.walletInfo, required String password, required this.encryptionFileUtils}) : _password = password { transactions = ObservableMap(); } @@ -22,6 +23,7 @@ abstract class TronTransactionHistoryBase extends TransactionHistoryBase init() async => await _load(); @@ -33,7 +35,7 @@ abstract class TronTransactionHistoryBase extends TransactionHistoryBase 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 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? 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; } 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 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 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 with Store { WowneroWalletBase( - {required WalletInfo walletInfo, required Box unspentCoinsInfo}) + {required WalletInfo walletInfo, required Box unspentCoinsInfo, required String password}) : balance = ObservableMap.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 60bb0793f..cadc809fe 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 b/ios/Podfile index 51622ff10..f0a0721a6 100644 --- a/ios/Podfile +++ b/ios/Podfile @@ -36,7 +36,6 @@ target 'Runner' do # Cake Wallet (Legacy) pod 'CryptoSwift' - pod 'UnstoppableDomainsResolution', '~> 4.0.0' end post_install do |installer| diff --git a/ios/Podfile.lock b/ios/Podfile.lock index f99f1dddf..56de513b6 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -8,6 +8,36 @@ PODS: - Flutter - ReachabilitySwift - CryptoSwift (1.8.2) + - 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 - device_info_plus (0.0.1): @@ -117,6 +147,8 @@ DEPENDENCIES: - barcode_scan2 (from `.symlinks/plugins/barcode_scan2/ios`) - connectivity_plus (from `.symlinks/plugins/connectivity_plus/ios`) - CryptoSwift + - 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`) @@ -165,6 +197,10 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/barcode_scan2/ios" connectivity_plus: :path: ".symlinks/plugins/connectivity_plus/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: @@ -221,6 +257,8 @@ SPEC CHECKSUMS: BigInt: f668a80089607f521586bbe29513d708491ef2f7 connectivity_plus: bf0076dd84a130856aa636df1c71ccaff908fa1d CryptoSwift: c63a805d8bb5e5538e88af4e44bb537776af11ea + cw_haven: b3e54e1fbe7b8e6fda57a93206bc38f8e89b898a + cw_shared_external: 2972d872b8917603478117c9957dfca611845a92 device_display_brightness: 1510e72c567a1f6ce6ffe393dcd9afd1426034f7 device_info_plus: c6fb39579d0f423935b0c9ce7ee2f44b71b9fce6 devicelocale: b22617f40038496deffba44747101255cee005b0 diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index 417c522a6..688fa2c39 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -483,7 +483,7 @@ "$(PROJECT_DIR)/Flutter", ); MARKETING_VERSION = 1.0.1; - PRODUCT_BUNDLE_IDENTIFIER = "com.fotolockr.cakewallet"; + PRODUCT_BUNDLE_IDENTIFIER = com.fotolockr.cakewallet; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_VERSION = 5.0; @@ -629,7 +629,7 @@ "$(PROJECT_DIR)/Flutter", ); MARKETING_VERSION = 1.0.1; - PRODUCT_BUNDLE_IDENTIFIER = "com.fotolockr.cakewallet"; + PRODUCT_BUNDLE_IDENTIFIER = com.fotolockr.cakewallet; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; @@ -667,7 +667,7 @@ "$(PROJECT_DIR)/Flutter", ); MARKETING_VERSION = 1.0.1; - PRODUCT_BUNDLE_IDENTIFIER = "com.fotolockr.cakewallet"; + PRODUCT_BUNDLE_IDENTIFIER = com.fotolockr.cakewallet; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_VERSION = 5.0; diff --git a/ios/Runner/AppDelegate.swift b/ios/Runner/AppDelegate.swift index acdfa4346..0cc4eebe8 100644 --- a/ios/Runner/AppDelegate.swift +++ b/ios/Runner/AppDelegate.swift @@ -1,6 +1,5 @@ import UIKit import Flutter -import UnstoppableDomainsResolution import workmanager @UIApplicationMain @@ -87,27 +86,7 @@ import workmanager } result(secRandom(count: count)) - case "getUnstoppableDomainAddress": - guard let args = call.arguments as? Dictionary, - let domain = args["domain"], - let ticker = args["ticker"], - let resolution = self?.resolution else { - result(nil) - return - } - - resolution.addr(domain: domain, ticker: ticker) { addrResult in - var address : String = "" - - switch addrResult { - case .success(let returnValue): - address = returnValue - case .failure(let error): - print("Expected Address, but got \(error)") - } - - result(address) - } + case "setIsAppSecure": guard let args = call.arguments as? Dictionary, let isAppSecure = args["isAppSecure"] else { diff --git a/lib/bitcoin/cw_bitcoin.dart b/lib/bitcoin/cw_bitcoin.dart index edfc77acb..989cd2b35 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( @@ -203,13 +203,13 @@ class CWBitcoin extends Bitcoin { } WalletService createBitcoinWalletService( - Box walletInfoSource, Box unspentCoinSource, bool alwaysScan) { - return BitcoinWalletService(walletInfoSource, unspentCoinSource, alwaysScan); + Box walletInfoSource, Box unspentCoinSource, bool alwaysScan, bool isDirect) { + return BitcoinWalletService(walletInfoSource, unspentCoinSource, alwaysScan, isDirect); } WalletService createLitecoinWalletService( - Box walletInfoSource, Box unspentCoinSource) { - return LitecoinWalletService(walletInfoSource, unspentCoinSource); + Box walletInfoSource, Box unspentCoinSource, bool isDirect) { + return LitecoinWalletService(walletInfoSource, unspentCoinSource, isDirect); } @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 walletInfoSource, Box unspentCoinSource) { - return BitcoinCashWalletService(walletInfoSource, unspentCoinSource); + Box walletInfoSource, Box 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 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 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 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 renameWallet(WalletType type, String name, String newName) async { + Future 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 load(WalletType type, String name) async { + Future 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 a64270f6d..7c22e809c 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'; @@ -113,6 +114,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'; @@ -217,6 +220,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'; @@ -337,7 +342,6 @@ Future setup({ WalletCreationService( initialType: type, keyService: getIt.get(), - secureStorage: getIt.get(), sharedPreferences: getIt.get(), settingsStore: getIt.get(), walletInfoSource: _walletInfoSource)); @@ -357,6 +361,65 @@ Future setup({ getIt.get(param1: type), type: type)); + getIt.registerFactoryParam((args, closable) { + return WalletUnlockPage( + getIt.get(param1: args), + args.callback, + args.authPasswordHandler, + closable: closable); + }, instanceName: 'wallet_unlock_loadable'); + + getIt.registerFactory( + () => getIt.get( + param1: WalletUnlockArguments( + callback: (bool successful, _) { + if (successful) { + final authStore = getIt.get(); + authStore.allowed(); + }}), + param2: false, + instanceName: 'wallet_unlock_loadable'), + instanceName: 'wallet_password_login'); + + getIt.registerFactoryParam((args, closable) { + return WalletUnlockPage( + getIt.get(param1: args), + args.callback, + args.authPasswordHandler, + closable: closable); + }, instanceName: 'wallet_unlock_verifiable'); + + getIt.registerFactoryParam((args, _) { + final currentWalletName = getIt + .get() + .getString(PreferencesKey.currentWalletName) ?? ''; + final currentWalletTypeRaw = + getIt.get() + .getInt(PreferencesKey.currentWalletType) ?? 0; + final currentWalletType = deserializeFromInt(currentWalletTypeRaw); + + return WalletUnlockLoadableViewModel( + getIt.get(), + getIt.get(), + walletName: args.walletName ?? currentWalletName, + walletType: args.walletType ?? currentWalletType); + }); + + getIt.registerFactoryParam((args, _) { + final currentWalletName = getIt + .get() + .getString(PreferencesKey.currentWalletName) ?? ''; + final currentWalletTypeRaw = + getIt.get() + .getInt(PreferencesKey.currentWalletType) ?? 0; + final currentWalletType = deserializeFromInt(currentWalletTypeRaw); + + return WalletUnlockVerifiableViewModel( + getIt.get(), + walletName: args.walletName ?? currentWalletName, + walletType: args.walletType ?? currentWalletType); + }); + getIt.registerFactoryParam((WalletType type, _) { return WalletRestorationFromQRVM(getIt.get(), getIt.get(param1: type), _walletInfoSource, type); @@ -907,23 +970,28 @@ Future setup({ _walletInfoSource, _unspentCoinsInfoSource, getIt.get().silentPaymentsAlwaysScan, + SettingsStoreBase.walletPasswordDirectInput, ); case WalletType.litecoin: - return bitcoin!.createLitecoinWalletService(_walletInfoSource, _unspentCoinsInfoSource); + return bitcoin!.createLitecoinWalletService(_walletInfoSource, _unspentCoinsInfoSource, + SettingsStoreBase.walletPasswordDirectInput); case WalletType.ethereum: - return ethereum!.createEthereumWalletService(_walletInfoSource); + return ethereum!.createEthereumWalletService( + _walletInfoSource, SettingsStoreBase.walletPasswordDirectInput); 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 loadCurrentWallet() async { +Future loadCurrentWallet({String? password}) async { final appStore = getIt.get(); final name = getIt .get() @@ -21,7 +21,10 @@ Future loadCurrentWallet() async { final type = deserializeFromInt(typeRaw); final walletLoadingService = getIt.get(); - final wallet = await walletLoadingService.load(type, name); + final wallet = await walletLoadingService.load( + type, + name, + password: password); await appStore.changeCurrentWallet(wallet); getIt.get().registerSyncTask(); diff --git a/lib/entities/unstoppable_domain_address.dart b/lib/entities/unstoppable_domain_address.dart index c5ec71ab5..0f56517b8 100644 --- a/lib/entities/unstoppable_domain_address.dart +++ b/lib/entities/unstoppable_domain_address.dart @@ -1,5 +1,7 @@ -import 'package:cake_wallet/utils/device_info.dart'; +import 'dart:convert'; + import 'package:flutter/services.dart'; +import 'package:http/http.dart' as http; const channel = MethodChannel('com.cake_wallet/native_utils'); @@ -7,18 +9,19 @@ Future fetchUnstoppableDomainAddress(String domain, String ticker) async var address = ''; try { - if (DeviceInfo.instance.isMobile) { - address = await channel.invokeMethod( - 'getUnstoppableDomainAddress', - { - 'domain' : domain, - 'ticker' : ticker - } - ) ?? ''; - } else { - // TODO: Integrate with Unstoppable domains resolution API - return address; + final uri = Uri.parse("https://api.unstoppabledomains.com/profile/public/${Uri.encodeQueryComponent(domain)}?fields=records"); + final jsonString = await http.read(uri); + final jsonParsed = json.decode(jsonString) as Map; + if (jsonParsed["records"] == null) { + throw Exception(".records response from $uri is empty"); + }; + final records = jsonParsed["records"] as Map; + final key = "crypto.${ticker.toUpperCase()}.address"; + if (records[key] == null) { + throw Exception(".records.${key} response from $uri is empty"); } + + return records[key] as String? ?? ''; } catch (e) { print('Unstoppable domain error: ${e.toString()}'); address = ''; 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 getEthereumWordList(String language) => EVMChainMnemonics.englishWordlist; - WalletService createEthereumWalletService(Box walletInfoSource) => - EthereumWalletService(walletInfoSource, client: EthereumClient()); + WalletService createEthereumWalletService(Box 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 577d54a6d..cf1429cd4 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -170,7 +170,6 @@ Future initializeAppConfigs() async { } final secureStorage = secureStorageShared; - final transactionDescriptionsBoxKey = await getEncryptionKey(secureStorage: secureStorage, forKey: TransactionDescription.boxKey); final tradesBoxKey = await getEncryptionKey(secureStorage: secureStorage, forKey: Trade.boxKey); @@ -247,8 +246,8 @@ Future initialSetup( ordersSource: ordersSource, anonpayInvoiceInfoSource: anonpayInvoiceInfo, unspentCoinsInfoSource: unspentCoinsInfoSource, - secureStorage: secureStorage, navigatorKey: navigatorKey, + secureStorage: secureStorage, ); await bootstrap(navigatorKey); monero?.onStartup(); 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 walletInfoSource) { - return NanoWalletService(walletInfoSource); + WalletService createNanoWalletService(Box 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 getPolygonWordList(String language) => EVMChainMnemonics.englishWordlist; - WalletService createPolygonWalletService(Box walletInfoSource) => - PolygonWalletService(walletInfoSource, client: PolygonClient()); + WalletService createPolygonWalletService(Box 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 c09664cef..498077511 100644 --- a/lib/router.dart +++ b/lib/router.dart @@ -90,7 +90,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'; @@ -125,6 +129,14 @@ Route createRoute(RouteSettings settings) { return MaterialPageRoute(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( builder: (_) => getIt.get(param1: (PinCodeState context, dynamic _) { @@ -176,6 +188,10 @@ Route 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( fullscreenDialog: true, @@ -328,8 +344,16 @@ Route createRoute(RouteSettings settings) { case Routes.auth: return MaterialPageRoute( fullscreenDialog: true, - builder: (_) => getIt.get( - param1: settings.arguments as OnAuthenticationFinished, param2: true)); + builder: (_) + => SettingsStoreBase.walletPasswordDirectInput + ? getIt.get( + param1: WalletUnlockArguments( + callback: settings.arguments as OnAuthenticationFinished), + instanceName: 'wallet_unlock_verifiable', + param2: true) + : getIt.get( + param1: settings.arguments as OnAuthenticationFinished, + param2: true)); case Routes.totpAuthCodePage: final args = settings.arguments as TotpAuthArgumentsModel; @@ -340,24 +364,32 @@ Route createRoute(RouteSettings settings) { ), ); - case Routes.login: - return CupertinoPageRoute( - builder: (context) => WillPopScope( - child: getIt.get(instanceName: 'login'), - onWillPop: () async => - // FIX-ME: Additional check does it works correctly - (await SystemChannels.platform.invokeMethod('SystemNavigator.pop') ?? - false), - ), - fullscreenDialog: true); + case Routes.walletUnlockLoadable: + return MaterialPageRoute( + fullscreenDialog: true, + builder: (_) + => getIt.get( + param1: settings.arguments as WalletUnlockArguments, + instanceName: 'wallet_unlock_loadable', + param2: true)); case Routes.unlock: return MaterialPageRoute( fullscreenDialog: true, - builder: (_) => WillPopScope( - child: getIt.get( - param1: settings.arguments as OnAuthenticationFinished, param2: false), - onWillPop: () async => false)); + builder: (_) + => SettingsStoreBase.walletPasswordDirectInput + ? WillPopScope( + child: getIt.get( + param1: WalletUnlockArguments( + callback: settings.arguments as OnAuthenticationFinished), + param2: false, + instanceName: 'wallet_unlock_verifiable'), + onWillPop: () async => false) + : WillPopScope( + child: getIt.get( + param1: settings.arguments as OnAuthenticationFinished, + param2: false), + onWillPop: () async => false)); case Routes.silentPaymentsSettings: return CupertinoPageRoute( @@ -397,6 +429,17 @@ Route createRoute(RouteSettings settings) { builder: (_) => getIt.get( param1: args?['editingNode'] as Node?, param2: args?['isSelected'] as bool?)); + case Routes.login: + return CupertinoPageRoute( + builder: (context) => WillPopScope( + child: SettingsStoreBase.walletPasswordDirectInput + ? getIt.get(instanceName: 'wallet_password_login') + : getIt.get(instanceName: 'login'), + onWillPop: () async => + // FIX-ME: Additional check does it works correctly + (await SystemChannels.platform.invokeMethod('SystemNavigator.pop') ?? false)), + fullscreenDialog: true); + case Routes.newPowNode: final args = settings.arguments as Map?; return CupertinoPageRoute( @@ -486,7 +529,9 @@ Route createRoute(RouteSettings settings) { fullscreenDialog: true, builder: (_) => getIt.get()); case Routes.support: - return CupertinoPageRoute(builder: (_) => getIt.get()); + return CupertinoPageRoute( + fullscreenDialog: true, + builder: (_) => getIt.get()); case Routes.supportLiveChat: return CupertinoPageRoute(builder: (_) => getIt.get()); diff --git a/lib/routes.dart b/lib/routes.dart index 78a93bee7..caa7eb39e 100644 --- a/lib/routes.dart +++ b/lib/routes.dart @@ -82,6 +82,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 getSolanaWordList(String language) => SolanaMnemonics.englishWordlist; - WalletService createSolanaWalletService(Box walletInfoSource) => - SolanaWalletService(walletInfoSource); + WalletService createSolanaWalletService(Box 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 extends State { + void changeProcessText(String text); + void hideProgressText(); + Future 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 { +class AuthPagePinCodeStateImpl extends AuthPageState { final _key = GlobalKey(); final _pinCodeKey = GlobalKey(); final _backArrowImageDarkTheme = @@ -55,8 +61,6 @@ class AuthPageState extends State { } 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 { super.dispose(); } + @override void changeProcessText(String text) { dismissFlushBar(_authBar); _progressBar = createBar(text, duration: null) ..show(_key.currentContext!); } + @override void hideProgressText() { dismissFlushBar(_progressBar); _progressBar = null; } + @override Future 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()!.syncedBackgroundColor, - ), - child: Center( - child: Row( - mainAxisSize: MainAxisSize.min, - children: [ - Image.asset( - image, - height: 30, - width: 30, - color: isEnabled - ? Theme.of(context).extension()!.textColor - : Theme.of(context).extension()!.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()!.syncedBackgroundColor, + ), + child: Center( + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Image.asset( + image, + height: 30, + width: 30, color: isEnabled ? Theme.of(context).extension()!.textColor - : null, - height: 1, + : Theme.of(context).extension()!.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()!.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 _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 { _WalletNameFormState(this._walletNewVM) : _formKey = GlobalKey(), _languageSelectorKey = GlobalKey(), - _controller = TextEditingController(); + _nameController = TextEditingController(), + _passwordController = _walletNewVM.hasWalletPassword ? TextEditingController() : null, + _repeatedPasswordController = + _walletNewVM.hasWalletPassword ? TextEditingController() : null; static const aspectRatioImage = 1.22; final GlobalKey _formKey; final GlobalKey _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 { 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 { 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 { ), 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()!.titleColor, + ), + decoration: InputDecoration( + hintStyle: TextStyle( + fontSize: 18.0, + fontWeight: FontWeight.w500, + color: + Theme.of(context).extension()!.hintTextColor, + ), + hintText: S.of(context).password, + focusedBorder: UnderlineInputBorder( + borderSide: BorderSide( + color: Theme.of(context) + .extension()! + .underlineColor, + width: 1.0, + ), + ), + enabledBorder: UnderlineInputBorder( + borderSide: BorderSide( + color: Theme.of(context) + .extension()! + .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()!.titleColor, + ), + decoration: InputDecoration( + hintStyle: TextStyle( + fontSize: 18.0, + fontWeight: FontWeight.w500, + color: + Theme.of(context).extension()!.hintTextColor, + ), + hintText: S.of(context).repeat_wallet_password, + focusedBorder: UnderlineInputBorder( + borderSide: BorderSide( + color: Theme.of(context) + .extension()! + .underlineColor, + width: 1.0, + ), + ), + enabledBorder: UnderlineInputBorder( + borderSide: BorderSide( + color: Theme.of(context) + .extension()! + .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 c34e6c968..83772f866 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 { - WalletRestoreFromKeysFromState() + WalletRestoreFromKeysFromState({required bool displayWalletPassword}) : formKey = GlobalKey(), blockchainHeightKey = GlobalKey(), nameController = TextEditingController(), @@ -37,7 +44,9 @@ class WalletRestoreFromKeysFromState extends State { viewKeyController = TextEditingController(), spendKeyController = TextEditingController(), privateKeyController = TextEditingController(), - nameTextEditingController = TextEditingController(); + nameTextEditingController = TextEditingController(), + passwordTextEditingController = displayWalletPassword ? TextEditingController() : null, + repeatedPasswordTextEditingController = displayWalletPassword ? TextEditingController() : null; final GlobalKey formKey; final GlobalKey blockchainHeightKey; @@ -47,9 +56,22 @@ class WalletRestoreFromKeysFromState extends State { 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 { viewKeyController.dispose(); privateKeyController.dispose(); spendKeyController.dispose(); + passwordTextEditingController?.dispose(); + if (passwordListener != null) { + passwordTextEditingController?.removeListener(passwordListener!); + } + + if (repeatedPasswordListener != null) { + repeatedPasswordTextEditingController?.removeListener(repeatedPasswordListener!); + } super.dispose(); } @@ -116,6 +146,19 @@ class WalletRestoreFromKeysFromState extends State { ), ], ), + 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 aa3b743fc..1a9a30457 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 { - WalletRestoreFromSeedFormState(this.language) + WalletRestoreFromSeedFormState(this.language, {required bool displayWalletPassword}) : seedWidgetStateKey = GlobalKey(), blockchainHeightKey = GlobalKey(), formKey = GlobalKey(), languageController = TextEditingController(), nameTextEditingController = TextEditingController(), + passwordTextEditingController = displayWalletPassword ? TextEditingController() : null, + repeatedPasswordTextEditingController = displayWalletPassword ? TextEditingController() : null, passphraseController = TextEditingController(), seedTypeController = TextEditingController(); @@ -57,16 +66,30 @@ class WalletRestoreFromSeedFormState extends State { final GlobalKey blockchainHeightKey; final TextEditingController languageController; final TextEditingController nameTextEditingController; + final TextEditingController? passwordTextEditingController; + final TextEditingController? repeatedPasswordTextEditingController; final TextEditingController seedTypeController; final TextEditingController passphraseController; final GlobalKey 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 { @override void dispose() { - super.dispose(); moneroSeedTypeReaction(); + + if (passwordListener != null) { + passwordTextEditingController?.removeListener(passwordListener!); + } + + if (repeatedPasswordListener != null) { + repeatedPasswordTextEditingController?.removeListener(repeatedPasswordListener!); + } + super.dispose(); } void onSeedChange(String seed) { @@ -182,6 +213,16 @@ class WalletRestoreFromSeedFormState extends State { ), ), ), + 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 40a7794c0..99ab52aba 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 7ad8af4c5..8ce0ddde9 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/support_other_links/support_other_links_page.dart b/lib/src/screens/support_other_links/support_other_links_page.dart index 681a44f8f..7a1a945ca 100644 --- a/lib/src/screens/support_other_links/support_other_links_page.dart +++ b/lib/src/screens/support_other_links/support_other_links_page.dart @@ -7,6 +7,7 @@ import 'package:cake_wallet/view_model/settings/regular_list_item.dart'; import 'package:cake_wallet/view_model/support_view_model.dart'; import 'package:flutter/material.dart'; import 'package:cake_wallet/src/screens/base_page.dart'; +import 'package:cake_wallet/themes/extensions/option_tile_theme.dart'; import 'package:cake_wallet/generated/i18n.dart'; class SupportOtherLinksPage extends BasePage { @@ -22,8 +23,11 @@ class SupportOtherLinksPage extends BasePage { @override Widget body(BuildContext context) { + final iconColor = Theme.of(context).extension()!.iconColor; + final isLightMode = Theme.of(context).extension()?.useDarkImage ?? false; + return Container( child: Center( child: ConstrainedBox( @@ -37,16 +41,16 @@ class SupportOtherLinksPage extends BasePage { if (item is RegularListItem) { return SettingsCellWithArrow(title: item.title, handler: item.handler); } - if (item is LinkListItem) { + bool hasLightIcon = false; + if (item.lightIcon != null) hasLightIcon = true; return SettingsLinkProviderCell( title: item.title, - icon: item.icon, + icon: isLightMode && hasLightIcon ? item.lightIcon : item.icon, iconColor: item.hasIconColor ? iconColor : null, link: item.link, linkTitle: item.linkTitle); } - return Container(); }), ), 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 _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 dab0a1d23..43828bcc8 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'; @@ -338,6 +341,20 @@ class WalletListBodyState extends State { } Future _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 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 createState() => WalletUnlockPageState(); +} + +class WalletUnlockPageState extends AuthPageState { + WalletUnlockPageState() : _passwordController = TextEditingController(); + + final TextEditingController _passwordController; + final _key = GlobalKey(); + final _backArrowImageDarkTheme = Image.asset('assets/images/close_button.png'); + ReactionDisposer? _reaction; + Flushbar? _authBar; + Flushbar? _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(S.of(context).authentication, duration: null)..show(context); + }); + } + + if (state is FailureState) { + WidgetsBinding.instance.addPostFrameCallback((_) async { + dismissFlushBar(_authBar); + showBar(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(text, duration: null)..show(_key.currentContext!); + } + + @override + void hideProgressText() { + dismissFlushBar(_progressBar); + _progressBar = null; + } + + void dismissFlushBar(Flushbar? bar) { + WidgetsBinding.instance.addPostFrameCallback((_) async { + await bar?.dismiss(); + }); + } + + @override + Future 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.delayed(Duration(milliseconds: 50)); + await _authBar?.dismiss(); + await Future.delayed(Duration(milliseconds: 50)); + await _progressBar?.dismiss(); + await Future.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()!.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()!.titleColor, + ), + decoration: InputDecoration( + hintStyle: TextStyle( + fontSize: 18.0, + fontWeight: FontWeight.w500, + color: Theme.of(context).extension()!.hintTextColor, + ), + hintText: S.of(context).enter_wallet_password, + focusedBorder: UnderlineInputBorder( + borderSide: BorderSide( + color: Theme.of(context).extension()!.underlineColor, + width: 1.0, + ), + ), + enabledBorder: UnderlineInputBorder( + borderSide: BorderSide( + color: Theme.of(context).extension()!.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 46e9708f1..dcb857df1 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 8fb26df53..df2c7767f 100644 --- a/lib/store/settings_store.dart +++ b/lib/store/settings_store.dart @@ -553,6 +553,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 getTronWordList(String language) => EVMChainMnemonics.englishWordlist; - WalletService createTronWalletService(Box walletInfoSource) => - TronWalletService(walletInfoSource, client: TronClient()); + @override + WalletService createTronWalletService(Box 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 6045c0004..5e0c83f88 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 _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/settings/link_list_item.dart b/lib/view_model/settings/link_list_item.dart index 4ee4162a9..8d7607eb5 100644 --- a/lib/view_model/settings/link_list_item.dart +++ b/lib/view_model/settings/link_list_item.dart @@ -8,10 +8,12 @@ class LinkListItem extends SettingsListItem { required this.link, required this.linkTitle, this.icon, + this.lightIcon, this.hasIconColor = false}) : super(title); final String? icon; + final String? lightIcon; final String link; final String linkTitle; final bool hasIconColor; diff --git a/lib/view_model/support_view_model.dart b/lib/view_model/support_view_model.dart index ccef76154..2bb749b42 100644 --- a/lib/view_model/support_view_model.dart +++ b/lib/view_model/support_view_model.dart @@ -33,11 +33,6 @@ abstract class SupportViewModelBase with Store { icon: 'assets/images/Telegram.png', linkTitle: '@cakewallet_bot', link: 'https://t.me/cakewallet_bot'), - LinkListItem( - title: 'Twitter', - icon: 'assets/images/Twitter.png', - linkTitle: '@cakewallet', - link: 'https://twitter.com/cakewallet'), LinkListItem( title: 'ChangeNow', icon: 'assets/images/change_now.png', @@ -46,7 +41,7 @@ abstract class SupportViewModelBase with Store { LinkListItem( title: 'SideShift', icon: 'assets/images/sideshift.png', - linkTitle: S.current.help, + linkTitle: 'help.sideshift.ai', link: 'https://help.sideshift.ai/en/'), LinkListItem( title: 'SimpleSwap', @@ -58,19 +53,41 @@ abstract class SupportViewModelBase with Store { icon: 'assets/images/exolix.png', linkTitle: 'support@exolix.com', link: 'mailto:support@exolix.com'), - if (!isMoneroOnly) ... [ - LinkListItem( - title: 'Wyre', - icon: 'assets/images/wyre.png', - linkTitle: S.current.submit_request, - link: 'https://wyre-support.zendesk.com/hc/en-us/requests/new'), + LinkListItem( + title: 'Quantex', + icon: 'assets/images/quantex.png', + linkTitle: 'help.myquantex.com', + link: 'mailto:support@exolix.com'), + LinkListItem( + title: 'Trocador', + icon: 'assets/images/trocador.png', + linkTitle: 'mail@trocador.app', + link: 'mailto:mail@trocador.app'), + LinkListItem( + title: 'Onramper', + icon: 'assets/images/onramper_dark.png', + lightIcon: 'assets/images/onramper_light.png', + linkTitle: 'View exchanges', + link: 'https://guides.cakewallet.com/docs/service-support/buy/#onramper'), + LinkListItem( + title: 'DFX', + icon: 'assets/images/dfx_dark.png', + lightIcon: 'assets/images/dfx_light.png', + linkTitle: 'support@dfx.swiss', + link: 'mailto:support@dfx.swiss'), + if (!isMoneroOnly) ... [ LinkListItem( title: 'MoonPay', icon: 'assets/images/moonpay.png', - hasIconColor: true, linkTitle: S.current.submit_request, - link: 'https://support.moonpay.com/hc/en-gb/requests/new') - ] + link: 'https://support.moonpay.com/hc/en-gb/requests/new'), + LinkListItem( + title: 'Robinhood Connect', + icon: 'assets/images/robinhood_dark.png', + lightIcon: 'assets/images/robinhood_light.png', + linkTitle: S.current.submit_request, + link: 'https://robinhood.com/contact') + ] //LinkListItem( // title: 'Yat', // icon: 'assets/images/yat_mini_logo.png', 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 changeName(WalletListItem walletItem) async { + Future 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 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 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 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 "$<$>:-O3>") + target_compile_definitions(${TARGET} PRIVATE "$<$>: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 +#include +#include +#include + +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 + +// 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 $) + 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 +#ifdef GDK_WINDOWING_X11 +#include +#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 + +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 338ece4ce..0b4ee9415 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -12,7 +12,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 @@ -28,7 +27,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 @@ $(AppIdentifierPrefix)${BUNDLE_ID} - + \ No newline at end of file diff --git a/model_generator.sh b/model_generator.sh index 58ce9b5d0..8a1173f7d 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 ec49d214b..fdc541131 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 4cf9509f8..98cafdebc 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 يومًا", @@ -499,6 +501,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": "تتطلب الوصول إلى المحفظة", @@ -797,6 +801,7 @@ "unavailable_balance_description": ".ﺎﻫﺪﻴﻤﺠﺗ ءﺎﻐﻟﺇ ﺭﺮﻘﺗ ﻰﺘﺣ ﺕﻼﻣﺎﻌﻤﻠﻟ ﻝﻮﺻﻮﻠﻟ ﺔﻠﺑﺎﻗ ﺮﻴﻏ ﺓﺪﻤﺠﻤﻟﺍ ﺓﺪﺻﺭﻷﺍ ﻞﻈﺗ ﺎﻤﻨﻴﺑ ،ﺎﻬﺑ ﺔﺻﺎﺨﻟﺍ ﺕﻼﻣﺎﻌﻤﻟﺍ ﻝﺎﻤﺘﻛﺍ ﺩﺮﺠﻤﺑ ﺔﺣﺎﺘﻣ ﺔﻠﻔﻘﻤﻟﺍ ﺓﺪﺻﺭﻷﺍ ﺢﺒﺼﺘﺳ .ﻚﺑ ﺔﺻﺎﺨﻟﺍ ﺕﻼﻤﻌﻟﺍ ﻲﻓ ﻢﻜﺤﺘﻟﺍ ﺕﺍﺩﺍﺪﻋﺇ ﻲﻓ ﻂﺸﻧ ﻞﻜﺸﺑ ﺎﻫﺪﻴﻤﺠﺘﺑ ﺖﻤﻗ", "unconfirmed": "رصيد غير مؤكد", "understand": "لقد فهمت", + "unlock": "الغاء القفل", "unmatched_currencies": "عملة محفظتك الحالية لا تتطابق مع عملة QR الممسوحة ضوئيًا", "unspent_change": "يتغير", "unspent_coins_details_title": "تفاصيل العملات الغير المنفقة", @@ -838,6 +843,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 b76401cf4..b51167dab 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 дни", @@ -499,6 +501,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": "Изискване за достъп до портфейла", @@ -797,6 +801,7 @@ "unavailable_balance_description": "Неналично салдо: Тази обща сума включва средства, които са заключени в чакащи транзакции и тези, които сте замразили активно в настройките за контрол на монетите. Заключените баланси ще станат достъпни, след като съответните им транзакции бъдат завършени, докато замразените баланси остават недостъпни за транзакции, докато не решите да ги размразите.", "unconfirmed": "Непотвърден баланс", "understand": "Разбирам", + "unlock": "Отключване", "unmatched_currencies": "Валутата на този портфейл не съвпада с тази от сканирания QR код", "unspent_change": "Промяна", "unspent_coins_details_title": "Подробности за неизползваните монети", @@ -838,6 +843,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 c5d374dd0..d47ca932c 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ů", @@ -499,6 +501,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", @@ -797,6 +801,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", @@ -838,6 +843,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 7b6613dd6..e64c3bc27 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", @@ -500,6 +502,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", @@ -799,6 +803,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", @@ -841,6 +846,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 35712a780..9d7d9ad49 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", @@ -499,6 +501,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", @@ -798,6 +802,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", @@ -839,6 +844,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 31a6e6865..e7f1366a5 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", @@ -500,6 +502,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", @@ -798,6 +802,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", @@ -839,6 +844,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 03d4a73dd..7965eca12 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", @@ -499,6 +501,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", @@ -797,6 +801,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", @@ -838,6 +843,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 922f9a51b..d2c58971f 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", @@ -501,6 +503,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", @@ -799,6 +803,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", @@ -840,6 +845,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 db6940e8b..baa3925c0 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 दिन", @@ -501,6 +503,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": "वॉलेट तक पहुँचने के लिए आवश्यकता है", @@ -799,6 +803,7 @@ "unavailable_balance_description": "अनुपलब्ध शेष राशि: इस कुल में वे धनराशि शामिल हैं जो लंबित लेनदेन में बंद हैं और जिन्हें आपने अपनी सिक्का नियंत्रण सेटिंग्स में सक्रिय रूप से जमा कर रखा है। लॉक किए गए शेष उनके संबंधित लेन-देन पूरे होने के बाद उपलब्ध हो जाएंगे, जबकि जमे हुए शेष लेन-देन के लिए अप्राप्य रहेंगे जब तक कि आप उन्हें अनफ्रीज करने का निर्णय नहीं लेते।", "unconfirmed": "अपुष्ट शेष राशि", "understand": "मुझे समझ", + "unlock": "अनलॉक", "unmatched_currencies": "आपके वर्तमान वॉलेट की मुद्रा स्कैन किए गए क्यूआर से मेल नहीं खाती", "unspent_change": "परिवर्तन", "unspent_coins_details_title": "अव्ययित सिक्कों का विवरण", @@ -840,6 +845,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 57cf1361e..e6b820300 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", @@ -499,6 +501,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", @@ -797,6 +801,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", @@ -838,6 +843,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 97a4afd3f..0b8077807 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", @@ -501,6 +503,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", @@ -800,6 +804,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", @@ -841,6 +846,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 42c2e628d..b7a81ef9a 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", @@ -501,6 +503,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", @@ -799,6 +803,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", @@ -841,6 +846,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 72b1f7d09..a13a4e6f0 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日", @@ -500,6 +502,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": "ウォレットにアクセスするために必要です", @@ -798,6 +802,7 @@ "unavailable_balance_description": "利用不可能な残高: この合計には、保留中のトランザクションにロックされている資金と、コイン管理設定でアクティブに凍結した資金が含まれます。ロックされた残高は、それぞれの取引が完了すると利用可能になりますが、凍結された残高は、凍結を解除するまで取引にアクセスできません。", "unconfirmed": "残高未確認", "understand": "わかります", + "unlock": "ロックを解除します", "unmatched_currencies": "現在のウォレットの通貨がスキャンされたQRの通貨と一致しません", "unspent_change": "変化", "unspent_coins_details_title": "未使用のコインの詳細", @@ -839,6 +844,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 b8cfee1b5..d20546f41 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일", @@ -500,6 +502,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": "지갑 접근을 위해 필요", @@ -798,6 +802,7 @@ "unavailable_balance_description": "사용할 수 없는 잔액: 이 총계에는 보류 중인 거래에 잠겨 있는 자금과 코인 관리 설정에서 적극적으로 동결된 자금이 포함됩니다. 잠긴 잔액은 해당 거래가 완료되면 사용할 수 있게 되며, 동결된 잔액은 동결을 해제하기 전까지 거래에 액세스할 수 없습니다.", "unconfirmed": "확인되지 않은 잔액", "understand": "이해 했어요", + "unlock": "터놓다", "unmatched_currencies": "현재 지갑의 통화가 스캔한 QR의 통화와 일치하지 않습니다.", "unspent_change": "변화", "unspent_coins_details_title": "사용하지 않은 동전 세부 정보", @@ -839,6 +844,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 52fe72ea6..06d7cf627 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", @@ -499,6 +501,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": "ပိုက်ဆံအိတ်ကို ဝင်သုံးရန် လိုအပ်သည်။", @@ -797,6 +801,7 @@ "unavailable_balance_description": "မရရှိနိုင်သော လက်ကျန်ငွေ- ဤစုစုပေါင်းတွင် ဆိုင်းငံ့ထားသော ငွေပေးငွေယူများတွင် သော့ခတ်ထားသော ငွေကြေးများနှင့် သင်၏ coin ထိန်းချုပ်မှုဆက်တင်များတွင် သင် တက်ကြွစွာ အေးခဲထားသော ငွေများ ပါဝင်သည်။ သော့ခတ်ထားသော လက်ကျန်ငွေများကို ၎င်းတို့၏ သက်ဆိုင်ရာ ငွေပေးငွေယူများ ပြီးမြောက်သည်နှင့် တပြိုင်နက် ရရှိနိုင်မည်ဖြစ်ပြီး၊ အေးခဲထားသော လက်ကျန်များကို ၎င်းတို့အား ပြန်ဖြုတ်ရန် သင်ဆုံးဖြတ်သည်အထိ ငွေပေးငွေယူများအတွက် ဆက်လက်၍မရနိုင်ပါ။", "unconfirmed": "အတည်မပြုနိုင်သော လက်ကျန်ငွေ", "understand": "ကျွန်တော်နားလည်ပါတယ်", + "unlock": "သော့ဖွင့်", "unmatched_currencies": "သင့်လက်ရှိပိုက်ဆံအိတ်၏ငွေကြေးသည် စကင်ဖတ်ထားသော QR နှင့် မကိုက်ညီပါ။", "unspent_change": "ပေြာင်းလဲခြင်း", "unspent_coins_details_title": "အသုံးမဝင်သော အကြွေစေ့အသေးစိတ်များ", @@ -838,6 +843,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 cde10506f..78caef912 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", @@ -499,6 +501,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", @@ -797,6 +801,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", @@ -839,6 +844,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 a22034c96..7de435319 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", @@ -499,6 +501,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", @@ -797,6 +801,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", @@ -838,6 +843,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 8f87ca59f..a3d789cab 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", @@ -501,6 +503,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", @@ -799,6 +803,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", @@ -841,6 +846,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 9f360137f..d84aa146f 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 дней", @@ -500,6 +502,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": "Требовать для доступа к кошельку", @@ -798,6 +802,7 @@ "unavailable_balance_description": "Недоступный баланс: в эту сумму входят средства, заблокированные в ожидающих транзакциях, и средства, которые вы активно заморозили в настройках управления монетами. Заблокированные балансы станут доступны после завершения соответствующих транзакций, а замороженные балансы останутся недоступными для транзакций, пока вы не решите их разморозить.", "unconfirmed": "Неподтвержденный баланс", "understand": "Понятно", + "unlock": "Разблокировать", "unmatched_currencies": "Валюта вашего текущего кошелька не соответствует валюте отсканированного QR-кода.", "unspent_change": "Изменять", "unspent_coins_details_title": "Сведения о неизрасходованных монетах", @@ -839,6 +844,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 a178d2452..2adccb2cf 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 วันล่าสุด", @@ -499,6 +501,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": "จำเป็นสำหรับการเข้าถึงกระเป๋าเงิน", @@ -797,6 +801,7 @@ "unavailable_balance_description": "ยอดคงเหลือที่ไม่พร้อมใช้งาน: ยอดรวมนี้รวมถึงเงินทุนที่ถูกล็อคในการทำธุรกรรมที่รอดำเนินการและที่คุณได้แช่แข็งไว้ในการตั้งค่าการควบคุมเหรียญของคุณ ยอดคงเหลือที่ถูกล็อคจะพร้อมใช้งานเมื่อธุรกรรมที่เกี่ยวข้องเสร็จสมบูรณ์ ในขณะที่ยอดคงเหลือที่แช่แข็งจะไม่สามารถเข้าถึงได้สำหรับธุรกรรมจนกว่าคุณจะตัดสินใจยกเลิกการแช่แข็ง", "unconfirmed": "ยอดคงเหลือที่ไม่ได้รับการยืนยัน", "understand": "ฉันเข้าใจ", + "unlock": "ปลดล็อค", "unmatched_currencies": "สกุลเงินของกระเป๋าปัจจุบันของคุณไม่ตรงกับของ QR ที่สแกน", "unspent_change": "เปลี่ยน", "unspent_coins_details_title": "รายละเอียดเหรียญที่ไม่ได้ใช้", @@ -838,6 +843,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 f49d3ddee..3bbae2e50 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", @@ -499,6 +501,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", @@ -797,6 +801,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", @@ -838,6 +843,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 c73765f64..b47787bbd 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", @@ -499,6 +501,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", @@ -797,6 +801,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ı", @@ -838,6 +843,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 d088dd1b2..328548087 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 днів", @@ -500,6 +502,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": "Потрібен доступ до гаманця", @@ -798,6 +802,7 @@ "unavailable_balance_description": "Недоступний баланс: ця сума включає кошти, заблоковані в незавершених транзакціях, і ті, які ви активно заморозили в налаштуваннях контролю монет. Заблоковані баланси стануть доступними після завершення відповідних транзакцій, тоді як заморожені баланси залишаються недоступними для транзакцій, доки ви не вирішите їх розморозити.", "unconfirmed": "Непідтверджений баланс", "understand": "Зрозуміло", + "unlock": "Розблокувати", "unmatched_currencies": "Валюта вашого гаманця не збігається з валютою сканованого QR-коду", "unspent_change": "Зміна", "unspent_coins_details_title": "Відомості про невитрачені монети", @@ -839,6 +844,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 0694463de..7d794f9bb 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 دن", @@ -501,6 +503,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": "بٹوے تک رسائی کے لیے درکار ہے۔", @@ -799,6 +803,7 @@ "unavailable_balance_description": "۔ﮯﺗﺮﮐ ﮟﯿﮩﻧ ﮧﻠﺼﯿﻓ ﺎﮐ ﮯﻧﺮﮐ ﺪﻤﺠﻨﻣ ﻥﺍ ﮟﯿﮩﻧﺍ ﭖﺁ ﮧﮐ ﮏﺗ ﺐﺟ ﮟﯿﮨ ﮯﺘﮨﺭ ﯽﺋﺎﺳﺭ ﻞﺑﺎﻗﺎﻧ ﮏﺗ ﺖﻗﻭ ﺱﺍ ﮯﯿﻟ ﮯﮐ ﻦﯾﺩ ﻦﯿﻟ ﺲﻨﻠﯿﺑ ﺪﻤﺠﻨﻣ ﮧﮐ ﺐﺟ ،ﮯﮔ ﮟﯿﺋﺎﺟ ﻮﮨ ﺏﺎﯿﺘﺳﺩ ﺲﻨﻠﯿﺑ ﻞﻔﻘﻣ ﺪﻌﺑ ﮯﮐ ﮯﻧﻮﮨ ﻞﻤﮑﻣ ﻦﯾﺩ ﻦﯿﻟ ﮧﻘﻠﻌﺘﻣ ﮯﮐ ﻥﺍ ۔ﮯﮨ ﺎﮭﮐﺭ ﺮ", "unconfirmed": "غیر تصدیق شدہ بیلنس", "understand": "میں سمجھتا ہوں۔", + "unlock": "غیر مقفل", "unmatched_currencies": "آپ کے پرس کی موجودہ کرنسی اسکین شدہ QR سے مماثل نہیں ہے۔", "unspent_change": "تبدیل کریں", "unspent_coins_details_title": "غیر خرچ شدہ سککوں کی تفصیلات", @@ -840,6 +845,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 87df87aca..2150a503f 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à", @@ -500,6 +502,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ọ", @@ -798,6 +802,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", @@ -839,6 +844,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 89eca2073..5db53a423 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 天", @@ -499,6 +501,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": "需要访问钱包", @@ -797,6 +801,7 @@ "unavailable_balance_description": "不可用余额:此总额包括锁定在待处理交易中的资金以及您在硬币控制设置中主动冻结的资金。一旦各自的交易完成,锁定的余额将变得可用,而冻结的余额在您决定解冻之前仍然无法进行交易。", "unconfirmed": "未确认余额", "understand": "我已知晓", + "unlock": "开锁", "unmatched_currencies": "您当前钱包的货币与扫描的 QR 的货币不匹配", "unspent_change": "改变", "unspent_coins_details_title": "未使用代幣詳情", @@ -838,6 +843,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 {} }: - -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 {}; +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 c37946476..a0104c34e 100644 --- a/tool/configure.dart +++ b/tool/configure.dart @@ -150,7 +150,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 getWordList(); Map getWalletKeys(Object wallet); @@ -179,8 +179,8 @@ abstract class Bitcoin { List getUnspents(Object wallet); Future updateUnspents(Object wallet); WalletService createBitcoinWalletService( - Box walletInfoSource, Box unspentCoinSource, bool alwaysScan); - WalletService createLitecoinWalletService(Box walletInfoSource, Box unspentCoinSource); + Box walletInfoSource, Box unspentCoinSource, bool alwaysScan, bool isDirect); + WalletService createLitecoinWalletService(Box walletInfoSource, Box unspentCoinSource, bool isDirect); TransactionPriority getBitcoinTransactionPriorityMedium(); TransactionPriority getBitcoinTransactionPriorityCustom(); TransactionPriority getLitecoinTransactionPriorityMedium(); @@ -369,7 +369,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 getKeys(Object wallet); int? getRestoreHeight(Object wallet); Object createMoneroTransactionCreationCredentials({required List outputs, required TransactionPriority priority}); @@ -734,7 +734,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 getKeys(Object wallet); Object createHavenTransactionCreationCredentials({required List outputs, required TransactionPriority priority, required String assetType}); String formatterMoneroAmountToString({required int amount}); @@ -828,8 +828,8 @@ import 'package:eth_sig_util/util/utils.dart'; const ethereumContent = """ abstract class Ethereum { List getEthereumWordList(String language); - WalletService createEthereumWalletService(Box walletInfoSource); - WalletCredentials createEthereumNewWalletCredentials({required String name, WalletInfo? walletInfo}); + WalletService createEthereumWalletService(Box 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}); @@ -932,8 +932,8 @@ import 'package:eth_sig_util/util/utils.dart'; const polygonContent = """ abstract class Polygon { List getPolygonWordList(String language); - WalletService createPolygonWalletService(Box walletInfoSource); - WalletCredentials createPolygonNewWalletCredentials({required String name, WalletInfo? walletInfo}); + WalletService createPolygonWalletService(Box 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}); @@ -1017,10 +1017,10 @@ abstract class BitcoinCash { String getCashAddrFormat(String address); WalletService createBitcoinCashWalletService( - Box walletInfoSource, Box unspentCoinSource); + Box walletInfoSource, Box 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}); @@ -1097,11 +1097,11 @@ abstract class Nano { void setCurrentAccount(Object wallet, int id, String label, String? balance); - WalletService createNanoWalletService(Box walletInfoSource); + WalletService createNanoWalletService(Box walletInfoSource, bool isDirect); WalletCredentials createNanoNewWalletCredentials({ required String name, - String password, + String? password, }); WalletCredentials createNanoRestoreWalletFromSeedCredentials({ @@ -1215,9 +1215,9 @@ import 'package:cw_solana/solana_wallet_creation_credentials.dart'; const solanaContent = """ abstract class Solana { List getSolanaWordList(String language); - WalletService createSolanaWalletService(Box walletInfoSource); + WalletService createSolanaWalletService(Box 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( @@ -1302,8 +1302,8 @@ import 'package:cw_tron/tron_wallet_service.dart'; const tronContent = """ abstract class Tron { List getTronWordList(String language); - WalletService createTronWalletService(Box walletInfoSource); - WalletCredentials createTronNewWalletCredentials({required String name, WalletInfo? walletInfo}); + WalletService createTronWalletService(Box 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);