CW-676 Add Linux scripts to build monero_c for linux platform (#1527)

* Revert "Revert btc address types"

This reverts commit a49e57e3

* Re-add Bitcoin Address types
Fix conflicts with main

* fix: label issues, clear spent utxo

* chore: deps

* fix: build

* fix: missing types

* feat: new electrs API & changes, fixes for last block scanning

* Update Monero

* not sure why it's failing

* Enable Exolix
Improve service updates indicator
New versions

* Add exolix Api token to limits api

* Ignore reporting network issues

* Change default bitcoin node

* Merge main and update linux version

* Update app version [skip ci]

* New versions

* Fix conflicts and update linux version

* minor fix

* feat: Scan Silent Payments homepage toggle

* chore: build configure

* feat: generic fixes, testnet UI improvements, useSSL on bitcoin nodes

* fix: invalid Object in sendData

* feat: improve addresses page & address book displays

* feat: silent payments labeled addresses disclaimer

* fix: missing i18n

* chore: print

* feat: single block scan, rescan by date working for btc mainnet

* feat: new cake features page replace market page, move sp scan toggle, auto switch node pop up alert

* feat: delete silent addresses

* fix: red dot in non ssl nodes

* fix: inconsistent connection states, fix tx history

* fix: tx & balance displays, cpfp sending

* feat: new rust lib

* chore: node path

* fix: check node based on network

* fix: missing txcount from addresses

* style: padding in feature page cards

* fix: restore not getting all wallet addresses by type

* fix: auto switch node broken

* fix: silent payment txs not being restored

* update linux version

* feat: change scanning to subscription model, sync improvements

* fix: scan re-subscription

* fix: default nodes

* fix: improve scanning by date, fix single block scan

* refactor: common function for input tx selection

* various fixes for build issues

* initial monero.dart implementation

* ...

* multiple wallets
new lib
minor fixes

* other fixes from monero.dart and monero_c

* fix: nodes & build

* update build scripts
fix polyseed

* remove unnecessary code

* Add windows app, build scripts and build guide for it.

* Minor fix in generated monero configs

* Merge and fix main

* fix: send all with multiple outs

* add missing monero_c command

* add android build script

* update version

* Merge and fix main

* undo android ndk removal

* Fix modified exception_handler.dart

* Temporarily remove haven

* fix build issues

* fix pr script

* Fixes for build monero.dart (monero_c) for windows.

* monero build script

* wip: ios build script

* refactor: unchanged file

* Added build guides for iOS and macOS. Replaced nproc call on macOS. Added macOS configuration for configure_cake_wallet.sh script.

* Update monero.dart and monero_c versions.

* Add missed windows build scripts

* Update the application configuration for windows build script.

* Update cw_monero pubspec lock file for monero.dart

* Update pr_test_build.yml

* chore: upgrade

* chore: merge changes

* refactor: unchanged files [skip ci]

* Fix conflicts with main

* fix for multiple wallets

* update app version [skip ci]

* Add tron to windows application configuration.

* Add macOS option for description message in configure_cake_wallet.sh

* fix missing encryption utils in hardware wallet functions [skip ci]

* fix conflicts

* Include missed monero dll for windows.

* reformatting [skip ci]

* fix conflicts with main

* Disable haven configuration for iOS as default. Add ability to configure cakewallet for iOS with  for configuration script. Remove cw_shared configuration for cw_monero.

* fix: scan fixes, add date, allow sending while scanning

* add missing nano secrets file [skip ci]

* ios library

* don't pull prebuilds android

* Add auto generation of manifest file for android project even for iOS, macOS, Windows.

* remove tron

* feat: sync fixes, sp settings

* feat: fix resyncing

* store crash fix

* make init async so it won't lag
disable print starts

* fix monero_c build issues

* libstdc++

* merge main and update version

* Fix MacOS saving wallet file issue
Fix Secure Storage issue (somehow)

* update pubspec.lock

* fix build script

* Use dylib as iOS framework. Use custom path for loading of iOS framework for monero.dart. Add script for generate iOS framework for monero wallet.

* fix: date from height logic, status disconnected & chain tip get

* fix: params

* feat: electrum migration if using cake electrum

* fix nodes
update versions

* re-enable tron

* update sp_scanner to work on iOS [skip ci]

* bump monero_c hash

* bump monero_c commit

* bump moneroc version

* bump monero_c commit

* Add ability to build monero wallet lib as universal lib. Update macOS build guide. Change default arch for macOS project to .

* fix: wrong socket for old electrum nodes

* update version

* Fix unchecked wallet type call

* get App Dir correctly in default_settings_migration.dart

* handle previous issue with fetching linux documents directory [skip ci]

* backup fix

* fix NTFS issues

* Add Tron
Update Linux version

* Close the wallet when the wallet gets changed

* fix: double balance

* feat: node domain

* fix: menu name

* bump monero_c commit

* fix: update tip on set scanning

* fix: connection switching back and forth

* feat: check if node is electrs, and supports sp

* chore: fix build

* minor enhancements

* fixes and enhancements

* solve conflicts with main

* Only stop wallet on rename and delete

* fix: status toggle

* minor enhancement

* Monero.com fixes

* bump monero_c commit

* update sp_scanner to include windows and linux

* merge main

* Update macOS build guide. Change brew dependencies for build unbound locally.

* fix: Tron file write, build scripts

* - merge linux with Monero Dart
- Temporarily disable Monero

* fix other issues with linux

* linux ci
fix build script

* Update pr_test_build_linux.yml

install required packages

* add linux desktop dependencies

* don't use apk in linux build releases

* don't copy the file to test-apk

* fix linux runtime issues

* remove libc++_shared.so

* fix issues with linux

* prepare both android and linux (because otherwise it will fail)

* ci script updates

* run apt update

* bump image to ubuntu 22.04
note: remember to put it down later

* bump python version

* remove some dependencies

* remove unused import

* add missing dependencies

* fix dependencies

* some fixes

* remove print [skip ci]

* Add back RunnerBase.entitlements
minor fixes [skip ci]

* fix memory leak / infinite recurrsion when opening xmr wallet

* url_launcher_linux: 3.1.1 # https://github.com/flutter/flutter/issues/153083

* fix conflicts with main

* handle walletKeysFile with encryptionUtils

* update app version [skip ci]

* add wownero [skip ci]

---------

Co-authored-by: OmarHatem <omarh.ismail1@gmail.com>
Co-authored-by: Rafael Saes <git@rafael.saes.dev>
Co-authored-by: M <m@cakewallet.com>
Co-authored-by: Konstantin Ullrich <konstantinullrich12@gmail.com>
This commit is contained in:
cyan 2024-08-13 00:18:14 +02:00 committed by GitHub
parent 3b635218a5
commit 1ce60d62b3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
178 changed files with 3955 additions and 563 deletions

View file

@ -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"

View file

@ -53,7 +53,9 @@ jobs:
channel: stable
- name: Install package dependencies
run: sudo apt-get install -y curl unzip automake build-essential file pkg-config git python libtool libtinfo5 cmake clang
run: |
sudo apt update
sudo apt-get install -y curl unzip automake build-essential file pkg-config git python libtool libtinfo5 cmake clang
- name: Execute Build and Setup Commands
run: |

View file

@ -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 }}

2
.gitignore vendored
View file

@ -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

View file

@ -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

176
build-guide-linux.md Normal file
View file

@ -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.

View file

@ -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

View file

@ -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
#cd macos && pod install

View file

@ -5,9 +5,10 @@ import 'package:bitcoin_base/bitcoin_base.dart';
import 'package:blockchain_utils/blockchain_utils.dart';
import 'package:cw_bitcoin/bitcoin_address_record.dart';
import 'package:cw_bitcoin/bitcoin_mnemonic.dart';
import 'package:cw_core/encryption_file_utils.dart';
import 'package:cw_bitcoin/electrum_derivations.dart';
import 'package:cw_bitcoin/bitcoin_wallet_addresses.dart';
import 'package:cw_bitcoin/electrum_balance.dart';
import 'package:cw_bitcoin/electrum_derivations.dart';
import 'package:cw_bitcoin/electrum_wallet.dart';
import 'package:cw_bitcoin/electrum_wallet_snapshot.dart';
import 'package:cw_bitcoin/psbt_transaction_builder.dart';
@ -30,6 +31,7 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
required String password,
required WalletInfo walletInfo,
required Box<UnspentCoinsInfo> unspentCoinsInfo,
required EncryptionFileUtils encryptionFileUtils,
Uint8List? seedBytes,
String? mnemonic,
String? xpub,
@ -58,6 +60,7 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
initialAddresses: initialAddresses,
initialBalance: initialBalance,
seedBytes: seedBytes,
encryptionFileUtils: encryptionFileUtils,
currency:
networkParam == BitcoinNetwork.testnet ? CryptoCurrency.tbtc : CryptoCurrency.btc,
alwaysScan: alwaysScan,
@ -90,6 +93,7 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
required String password,
required WalletInfo walletInfo,
required Box<UnspentCoinsInfo> unspentCoinsInfo,
required EncryptionFileUtils encryptionFileUtils,
String? passphrase,
String? addressPageType,
BasedUtxoNetwork? network,
@ -124,6 +128,7 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
initialSilentAddresses: initialSilentAddresses,
initialSilentAddressIndex: initialSilentAddressIndex,
initialBalance: initialBalance,
encryptionFileUtils: encryptionFileUtils,
seedBytes: seedBytes,
initialRegularAddressIndex: initialRegularAddressIndex,
initialChangeAddressIndex: initialChangeAddressIndex,
@ -137,6 +142,7 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
required WalletInfo walletInfo,
required Box<UnspentCoinsInfo> unspentCoinsInfo,
required String password,
required EncryptionFileUtils encryptionFileUtils,
required bool alwaysScan,
}) async {
final network = walletInfo.network != null
@ -148,7 +154,13 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
ElectrumWalletSnapshot? snp = null;
try {
snp = await ElectrumWalletSnapshot.load(name, walletInfo.type, password, network);
snp = await ElectrumWalletSnapshot.load(
encryptionFileUtils,
name,
walletInfo.type,
password,
network,
);
} catch (e) {
if (!hasKeysFile) rethrow;
}
@ -156,10 +168,18 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
final WalletKeysData keysData;
// Migrate wallet from the old scheme to then new .keys file scheme
if (!hasKeysFile) {
keysData =
WalletKeysData(mnemonic: snp!.mnemonic, xPub: snp.xpub, passphrase: snp.passphrase);
keysData = WalletKeysData(
mnemonic: snp!.mnemonic,
xPub: snp.xpub,
passphrase: snp.passphrase,
);
} else {
keysData = await WalletKeysFile.readKeysFile(name, walletInfo.type, password);
keysData = await WalletKeysFile.readKeysFile(
name,
walletInfo.type,
password,
encryptionFileUtils,
);
}
walletInfo.derivationInfo ??= DerivationInfo();
@ -198,6 +218,7 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
initialSilentAddresses: snp?.silentAddresses,
initialSilentAddressIndex: snp?.silentAddressIndex ?? 0,
initialBalance: snp?.balance,
encryptionFileUtils: encryptionFileUtils,
seedBytes: seedBytes,
initialRegularAddressIndex: snp?.regularAddressIndex,
initialChangeAddressIndex: snp?.changeAddressIndex,

View file

@ -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,
);
}

View file

@ -3,6 +3,7 @@ import 'package:bitcoin_base/bitcoin_base.dart';
import 'package:cw_bitcoin/bitcoin_mnemonic.dart';
import 'package:cw_bitcoin/mnemonic_is_incorrect_exception.dart';
import 'package:cw_bitcoin/bitcoin_wallet_creation_credentials.dart';
import 'package:cw_core/encryption_file_utils.dart';
import 'package:cw_core/unspent_coins_info.dart';
import 'package:cw_core/wallet_base.dart';
import 'package:cw_core/wallet_service.dart';
@ -19,11 +20,12 @@ class BitcoinWalletService extends WalletService<
BitcoinRestoreWalletFromSeedCredentials,
BitcoinRestoreWalletFromWIFCredentials,
BitcoinRestoreWalletFromHardware> {
BitcoinWalletService(this.walletInfoSource, this.unspentCoinsInfoSource, this.alwaysScan);
BitcoinWalletService(this.walletInfoSource, this.unspentCoinsInfoSource, this.alwaysScan, this.isDirect);
final Box<WalletInfo> walletInfoSource;
final Box<UnspentCoinsInfo> unspentCoinsInfoSource;
final bool alwaysScan;
final bool isDirect;
@override
WalletType getType() => WalletType.bitcoin;
@ -40,6 +42,7 @@ class BitcoinWalletService extends WalletService<
walletInfo: credentials.walletInfo!,
unspentCoinsInfo: unspentCoinsInfoSource,
network: network,
encryptionFileUtils: encryptionFileUtilsFor(isDirect),
);
await wallet.save();
@ -63,6 +66,7 @@ class BitcoinWalletService extends WalletService<
walletInfo: walletInfo,
unspentCoinsInfo: unspentCoinsInfoSource,
alwaysScan: alwaysScan,
encryptionFileUtils: encryptionFileUtilsFor(isDirect),
);
await wallet.init();
saveBackup(name);
@ -75,6 +79,7 @@ class BitcoinWalletService extends WalletService<
walletInfo: walletInfo,
unspentCoinsInfo: unspentCoinsInfoSource,
alwaysScan: alwaysScan,
encryptionFileUtils: encryptionFileUtilsFor(isDirect),
);
await wallet.init();
return wallet;
@ -99,6 +104,7 @@ class BitcoinWalletService extends WalletService<
walletInfo: currentWalletInfo,
unspentCoinsInfo: unspentCoinsInfoSource,
alwaysScan: alwaysScan,
encryptionFileUtils: encryptionFileUtilsFor(isDirect),
);
await currentWallet.renameWalletFiles(newName);
@ -125,6 +131,7 @@ class BitcoinWalletService extends WalletService<
walletInfo: credentials.walletInfo!,
unspentCoinsInfo: unspentCoinsInfoSource,
networkParam: network,
encryptionFileUtils: encryptionFileUtilsFor(isDirect),
);
await wallet.save();
await wallet.init();
@ -153,6 +160,7 @@ class BitcoinWalletService extends WalletService<
walletInfo: credentials.walletInfo!,
unspentCoinsInfo: unspentCoinsInfoSource,
network: network,
encryptionFileUtils: encryptionFileUtilsFor(isDirect),
);
await wallet.save();
await wallet.init();

View file

@ -1,4 +1,5 @@
import 'dart:convert';
import 'package:cw_core/encryption_file_utils.dart';
import 'package:cw_bitcoin/electrum_transaction_info.dart';
import 'package:cw_core/pathForWallet.dart';
@ -6,6 +7,8 @@ import 'package:cw_core/transaction_history.dart';
import 'package:cw_core/utils/file.dart';
import 'package:cw_core/wallet_info.dart';
import 'package:mobx/mobx.dart';
import 'package:cw_core/transaction_history.dart';
import 'package:cw_bitcoin/electrum_transaction_info.dart';
part 'electrum_transaction_history.g.dart';
@ -15,13 +18,15 @@ class ElectrumTransactionHistory = ElectrumTransactionHistoryBase with _$Electru
abstract class ElectrumTransactionHistoryBase
extends TransactionHistoryBase<ElectrumTransactionInfo> with Store {
ElectrumTransactionHistoryBase({required this.walletInfo, required String password})
ElectrumTransactionHistoryBase(
{required this.walletInfo, required String password, required this.encryptionFileUtils})
: _password = password,
_height = 0 {
transactions = ObservableMap<String, ElectrumTransactionInfo>();
}
final WalletInfo walletInfo;
final EncryptionFileUtils encryptionFileUtils;
String _password;
int _height;
@ -44,7 +49,7 @@ abstract class ElectrumTransactionHistoryBase
txjson[tx.key] = tx.value.toJson();
}
final data = json.encode({'height': _height, 'transactions': txjson});
await writeData(path: path, password: _password, data: data);
await encryptionFileUtils.write(path: path, password: _password, data: data);
} catch (e) {
print('Error while save bitcoin transaction history: ${e.toString()}');
}
@ -58,7 +63,7 @@ abstract class ElectrumTransactionHistoryBase
Future<Map<String, dynamic>> _read() async {
final dirPath = await pathForWalletDir(name: walletInfo.name, type: walletInfo.type);
final path = '$dirPath/$transactionsHistoryFileName';
final content = await read(path: path, password: _password);
final content = await encryptionFileUtils.read(path: path, password: _password);
return json.decode(content) as Map<String, dynamic>;
}

View file

@ -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> 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<void> save() async {
if (!(await WalletKeysFile.hasKeysFile(walletInfo.name, walletInfo.type))) {
await saveKeysFile(_password);
saveKeysFile(_password, true);
await saveKeysFile(_password, encryptionFileUtils);
saveKeysFile(_password, encryptionFileUtils, true);
}
final path = await makePath();
await write(path: path, password: _password, data: toJSON());
await encryptionFileUtils.write(path: path, password: _password, data: toJSON());
await transactionHistory.save();
}
@ -2258,4 +2267,3 @@ class UtxoDetails {
required this.spendsUnconfirmedTX,
});
}

View file

@ -2,6 +2,7 @@ import 'dart:convert';
import 'package:bitcoin_base/bitcoin_base.dart';
import 'package:cw_bitcoin/bitcoin_address_record.dart';
import 'package:cw_bitcoin/electrum_balance.dart';
import 'package:cw_core/encryption_file_utils.dart';
import 'package:cw_bitcoin/electrum_derivations.dart';
import 'package:cw_core/pathForWallet.dart';
import 'package:cw_core/wallet_info.dart';
@ -51,9 +52,9 @@ class ElectrumWalletSnapshot {
String? derivationPath;
static Future<ElectrumWalletSnapshot> load(
String name, WalletType type, String password, BasedUtxoNetwork network) async {
EncryptionFileUtils encryptionFileUtils, String name, WalletType type, String password, BasedUtxoNetwork network) async {
final path = await pathForWallet(name: name, type: type);
final jsonSource = await read(path: path, password: password);
final jsonSource = await encryptionFileUtils.read(path: path, password: password);
final data = json.decode(jsonSource) as Map;
final addressesTmp = data['addresses'] as List? ?? <Object>[];
final mnemonic = data['mnemonic'] as String?;

View file

@ -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> unspentCoinsInfo,
required Uint8List seedBytes,
required EncryptionFileUtils encryptionFileUtils,
String? addressPageType,
List<BitcoinAddressRecord>? 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> unspentCoinsInfo,
required EncryptionFileUtils encryptionFileUtils,
String? passphrase,
String? addressPageType,
List<BitcoinAddressRecord>? 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<LitecoinWallet> open({
required String name,
required WalletInfo walletInfo,
required Box<UnspentCoinsInfo> unspentCoinsInfo,
required String password,
}) async {
static Future<LitecoinWallet> open(
{required String name,
required WalletInfo walletInfo,
required Box<UnspentCoinsInfo> 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,

View file

@ -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<WalletInfo> walletInfoSource;
final Box<UnspentCoinsInfo> unspentCoinsInfoSource;
final bool isDirect;
@override
WalletType getType() => WalletType.litecoin;
@ -28,12 +31,13 @@ class LitecoinWalletService extends WalletService<
@override
Future<LitecoinWallet> 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<LitecoinWallet> openWallet(String name, String password) async {
final walletInfo = walletInfoSource.values.firstWhereOrNull(
(info) => info.id == WalletBase.idFor(name, getType()))!;
final walletInfo = walletInfoSource.values
.firstWhereOrNull((info) => info.id == WalletBase.idFor(name, getType()))!;
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<void> remove(String wallet) async {
File(await pathForWalletDir(name: wallet, type: getType()))
.delete(recursive: true);
final walletInfo = walletInfoSource.values.firstWhereOrNull(
(info) => info.id == WalletBase.idFor(wallet, getType()))!;
File(await pathForWalletDir(name: wallet, type: getType())).delete(recursive: true);
final walletInfo = walletInfoSource.values
.firstWhereOrNull((info) => info.id == WalletBase.idFor(wallet, getType()))!;
await walletInfoSource.delete(walletInfo.key);
}
@override
Future<void> rename(String currentName, String password, String newName) async {
final currentWalletInfo = walletInfoSource.values.firstWhereOrNull(
(info) => info.id == WalletBase.idFor(currentName, getType()))!;
final currentWalletInfo = walletInfoSource.values
.firstWhereOrNull((info) => info.id == WalletBase.idFor(currentName, getType()))!;
final currentWallet = await LitecoinWalletBase.open(
password: password,
name: currentName,
walletInfo: currentWalletInfo,
unspentCoinsInfo: unspentCoinsInfoSource);
password: password,
name: currentName,
walletInfo: currentWalletInfo,
unspentCoinsInfo: unspentCoinsInfoSource,
encryptionFileUtils: encryptionFileUtilsFor(isDirect),
);
await currentWallet.renameWalletFiles(newName);
await saveBackup(newName);
@ -97,27 +110,30 @@ class LitecoinWalletService extends WalletService<
@override
Future<LitecoinWallet> 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<LitecoinWallet> restoreFromKeys(
BitcoinRestoreWalletFromWIFCredentials credentials, {bool? isTestnet}) async =>
Future<LitecoinWallet> restoreFromKeys(BitcoinRestoreWalletFromWIFCredentials credentials,
{bool? isTestnet}) async =>
throw UnimplementedError();
@override
Future<LitecoinWallet> restoreFromSeed(
BitcoinRestoreWalletFromSeedCredentials credentials, {bool? isTestnet}) async {
Future<LitecoinWallet> 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;

View file

@ -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:

View file

@ -1,6 +1,7 @@
import 'package:bitbox/bitbox.dart' as bitbox;
import 'package:bitcoin_base/bitcoin_base.dart';
import 'package:blockchain_utils/blockchain_utils.dart';
import 'package:cw_core/encryption_file_utils.dart';
import 'package:cw_bitcoin/bitcoin_address_record.dart';
import 'package:cw_bitcoin/bitcoin_transaction_priority.dart';
import 'package:cw_bitcoin/electrum_balance.dart';
@ -28,6 +29,7 @@ abstract class BitcoinCashWalletBase extends ElectrumWallet with Store {
required WalletInfo walletInfo,
required Box<UnspentCoinsInfo> unspentCoinsInfo,
required Uint8List seedBytes,
required EncryptionFileUtils encryptionFileUtils,
BitcoinAddressType? addressPageType,
List<BitcoinAddressRecord>? initialAddresses,
ElectrumBalance? initialBalance,
@ -42,7 +44,8 @@ abstract class BitcoinCashWalletBase extends ElectrumWallet with Store {
initialAddresses: initialAddresses,
initialBalance: initialBalance,
seedBytes: seedBytes,
currency: CryptoCurrency.bch) {
currency: CryptoCurrency.bch,
encryptionFileUtils: encryptionFileUtils) {
walletAddresses = BitcoinCashWalletAddresses(
walletInfo,
initialAddresses: initialAddresses,
@ -63,6 +66,7 @@ abstract class BitcoinCashWalletBase extends ElectrumWallet with Store {
required String password,
required WalletInfo walletInfo,
required Box<UnspentCoinsInfo> unspentCoinsInfo,
required EncryptionFileUtils encryptionFileUtils,
String? addressPageType,
List<BitcoinAddressRecord>? initialAddresses,
ElectrumBalance? initialBalance,
@ -76,6 +80,7 @@ abstract class BitcoinCashWalletBase extends ElectrumWallet with Store {
initialAddresses: initialAddresses,
initialBalance: initialBalance,
seedBytes: await MnemonicBip39.toSeed(mnemonic),
encryptionFileUtils: encryptionFileUtils,
initialRegularAddressIndex: initialRegularAddressIndex,
initialChangeAddressIndex: initialChangeAddressIndex,
addressPageType: P2pkhAddressType.p2pkh,
@ -87,6 +92,7 @@ abstract class BitcoinCashWalletBase extends ElectrumWallet with Store {
required WalletInfo walletInfo,
required Box<UnspentCoinsInfo> unspentCoinsInfo,
required String password,
required EncryptionFileUtils encryptionFileUtils,
}) async {
final hasKeysFile = await WalletKeysFile.hasKeysFile(name, walletInfo.type);
@ -94,7 +100,12 @@ abstract class BitcoinCashWalletBase extends ElectrumWallet with Store {
try {
snp = await ElectrumWalletSnapshot.load(
name, walletInfo.type, password, BitcoinCashNetwork.mainnet);
encryptionFileUtils,
name,
walletInfo.type,
password,
BitcoinCashNetwork.mainnet,
);
} catch (e) {
if (!hasKeysFile) rethrow;
}
@ -105,7 +116,12 @@ abstract class BitcoinCashWalletBase extends ElectrumWallet with Store {
keysData =
WalletKeysData(mnemonic: snp!.mnemonic, xPub: snp.xpub, passphrase: snp.passphrase);
} else {
keysData = await WalletKeysFile.readKeysFile(name, walletInfo.type, password);
keysData = await WalletKeysFile.readKeysFile(
name,
walletInfo.type,
password,
encryptionFileUtils,
);
}
return BitcoinCashWallet(
@ -135,6 +151,7 @@ abstract class BitcoinCashWalletBase extends ElectrumWallet with Store {
}).toList(),
initialBalance: snp?.balance,
seedBytes: await MnemonicBip39.toSeed(keysData.mnemonic!),
encryptionFileUtils: encryptionFileUtils,
initialRegularAddressIndex: snp?.regularAddressIndex,
initialChangeAddressIndex: snp?.changeAddressIndex,
addressPageType: P2pkhAddressType.p2pkh,

View file

@ -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 {

View file

@ -2,6 +2,7 @@ import 'dart:io';
import 'package:bip39/bip39.dart';
import 'package:cw_bitcoin_cash/cw_bitcoin_cash.dart';
import 'package:cw_core/encryption_file_utils.dart';
import 'package:cw_core/pathForWallet.dart';
import 'package:cw_core/unspent_coins_info.dart';
import 'package:cw_core/wallet_base.dart';
@ -16,10 +17,11 @@ class BitcoinCashWalletService extends WalletService<
BitcoinCashRestoreWalletFromSeedCredentials,
BitcoinCashRestoreWalletFromWIFCredentials,
BitcoinCashNewWalletCredentials> {
BitcoinCashWalletService(this.walletInfoSource, this.unspentCoinsInfoSource);
BitcoinCashWalletService(this.walletInfoSource, this.unspentCoinsInfoSource, this.isDirect);
final Box<WalletInfo> walletInfoSource;
final Box<UnspentCoinsInfo> unspentCoinsInfoSource;
final bool isDirect;
@override
WalletType getType() => WalletType.bitcoinCash;
@ -34,10 +36,11 @@ class BitcoinCashWalletService extends WalletService<
final wallet = await BitcoinCashWalletBase.create(
mnemonic: await MnemonicBip39.generate(strength: strength),
password: credentials.password!,
walletInfo: credentials.walletInfo!,
unspentCoinsInfo: unspentCoinsInfoSource);
password: credentials.password!,
walletInfo: credentials.walletInfo!,
unspentCoinsInfo: unspentCoinsInfoSource,
encryptionFileUtils: encryptionFileUtilsFor(isDirect),
);
await wallet.save();
await wallet.init();
@ -54,7 +57,9 @@ class BitcoinCashWalletService extends WalletService<
password: password,
name: name,
walletInfo: walletInfo,
unspentCoinsInfo: unspentCoinsInfoSource);
unspentCoinsInfo: unspentCoinsInfoSource,
encryptionFileUtils: encryptionFileUtilsFor(isDirect),
);
await wallet.init();
saveBackup(name);
return wallet;
@ -64,7 +69,9 @@ class BitcoinCashWalletService extends WalletService<
password: password,
name: name,
walletInfo: walletInfo,
unspentCoinsInfo: unspentCoinsInfoSource);
unspentCoinsInfo: unspentCoinsInfoSource,
encryptionFileUtils: encryptionFileUtilsFor(isDirect),
);
await wallet.init();
return wallet;
}
@ -86,7 +93,8 @@ class BitcoinCashWalletService extends WalletService<
password: password,
name: currentName,
walletInfo: currentWalletInfo,
unspentCoinsInfo: unspentCoinsInfoSource);
unspentCoinsInfo: unspentCoinsInfoSource,
encryptionFileUtils: encryptionFileUtilsFor(isDirect));
await currentWallet.renameWalletFiles(newName);
await saveBackup(newName);
@ -121,7 +129,8 @@ class BitcoinCashWalletService extends WalletService<
password: credentials.password!,
mnemonic: credentials.mnemonic,
walletInfo: credentials.walletInfo!,
unspentCoinsInfo: unspentCoinsInfoSource);
unspentCoinsInfo: unspentCoinsInfoSource,
encryptionFileUtils: encryptionFileUtilsFor(isDirect));
await wallet.save();
await wallet.init();
return wallet;

View file

@ -0,0 +1,42 @@
import 'dart:io';
import 'dart:typed_data';
import 'package:cw_core/utils/file.dart' as file;
import 'package:cake_backup/backup.dart' as cwb;
EncryptionFileUtils encryptionFileUtilsFor(bool direct)
=> direct
? XChaCha20EncryptionFileUtils()
: Salsa20EncryhptionFileUtils();
abstract class EncryptionFileUtils {
Future<void> write({required String path, required String password, required String data});
Future<String> read({required String path, required String password});
}
class Salsa20EncryhptionFileUtils extends EncryptionFileUtils {
// Requires legacy complex key + iv as password
@override
Future<void> write({required String path, required String password, required String data}) async
=> await file.write(path: path, password: password, data: data);
// Requires legacy complex key + iv as password
@override
Future<String> read({required String path, required String password}) async
=> await file.read(path: path, password: password);
}
class XChaCha20EncryptionFileUtils extends EncryptionFileUtils {
@override
Future<void> write({required String path, required String password, required String data}) async {
final encrypted = await cwb.encrypt(password, Uint8List.fromList(data.codeUnits));
await File(path).writeAsBytes(encrypted);
}
@override
Future<String> read({required String path, required String password}) async {
final file = File(path);
final encrypted = await file.readAsBytes();
final bytes = await cwb.decrypt(password, encrypted);
return String.fromCharCodes(bytes);
}
}

View file

@ -84,6 +84,8 @@ abstract class WalletBase<BalanceType extends Balance, HistoryType extends Trans
Future<void> changePassword(String password);
String get password;
Future<void>? updateBalance();
void setExceptionHandler(void Function(FlutterErrorDetails) onError) => null;

View file

@ -3,10 +3,10 @@ import 'dart:developer' as dev;
import 'dart:io';
import 'package:cw_core/balance.dart';
import 'package:cw_core/encryption_file_utils.dart';
import 'package:cw_core/pathForWallet.dart';
import 'package:cw_core/transaction_history.dart';
import 'package:cw_core/transaction_info.dart';
import 'package:cw_core/utils/file.dart';
import 'package:cw_core/wallet_base.dart';
import 'package:cw_core/wallet_type.dart';
@ -20,28 +20,32 @@ mixin WalletKeysFile<BalanceType extends Balance, HistoryType extends Transactio
Future<String> makeKeysFilePath() async => "${await makePath()}.keys";
Future<void> saveKeysFile(String password, [bool isBackup = false]) async {
Future<void> saveKeysFile(String password, EncryptionFileUtils encryptionFileUtils,
[bool isBackup = false]) async {
try {
final rootPath = await makeKeysFilePath();
final path = "$rootPath${isBackup ? ".backup" : ""}";
dev.log("Saving .keys file '$path'");
await write(path: path, password: password, data: walletKeysData.toJSON());
await encryptionFileUtils.write(
path: path, password: password, data: walletKeysData.toJSON());
} catch (_) {}
}
static Future<void> createKeysFile(
String name, WalletType type, String password, WalletKeysData walletKeysData,
static Future<void> createKeysFile(String name, WalletType type, String password,
WalletKeysData walletKeysData, EncryptionFileUtils encryptionFileUtils,
[bool withBackup = true]) async {
try {
final rootPath = await pathForWallet(name: name, type: type);
final path = "$rootPath.keys";
dev.log("Saving .keys file '$path'");
await write(path: path, password: password, data: walletKeysData.toJSON());
await encryptionFileUtils.write(
path: path, password: password, data: walletKeysData.toJSON());
if (withBackup) {
dev.log("Saving .keys.backup file '$path.backup'");
await write(path: "$path.backup", password: password, data: walletKeysData.toJSON());
await encryptionFileUtils.write(
path: "$path.backup", password: password, data: walletKeysData.toJSON());
}
} catch (_) {}
}
@ -55,14 +59,19 @@ mixin WalletKeysFile<BalanceType extends Balance, HistoryType extends Transactio
}
}
static Future<WalletKeysData> readKeysFile(String name, WalletType type, String password) async {
static Future<WalletKeysData> readKeysFile(
String name,
WalletType type,
String password,
EncryptionFileUtils encryptionFileUtils,
) async {
final path = await pathForWallet(name: name, type: type);
var readPath = "$path.keys";
try {
if (!File(readPath).existsSync()) throw Exception("No .keys file found for $name $type");
final jsonSource = await read(path: readPath, password: password);
final jsonSource = await encryptionFileUtils.read(path: readPath, password: password);
final data = json.decode(jsonSource) as Map<String, dynamic>;
return WalletKeysData.fromJSON(data);
} catch (e) {
@ -72,12 +81,12 @@ mixin WalletKeysFile<BalanceType extends Balance, HistoryType extends Transactio
if (!File(readPath).existsSync())
throw Exception("No .keys nor a .keys.backup file found for $name $type");
final jsonSource = await read(path: readPath, password: password);
final jsonSource = await encryptionFileUtils.read(path: readPath, password: password);
final data = json.decode(jsonSource) as Map<String, dynamic>;
final keysData = WalletKeysData.fromJSON(data);
dev.log("Restoring .keys from .keys.backup");
createKeysFile(name, type, password, keysData, false);
createKeysFile(name, type, password, keysData, encryptionFileUtils, false);
return keysData;
}
}

View file

@ -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:

View file

@ -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:

View file

@ -7,6 +7,7 @@ class EthereumTransactionHistory extends EVMChainTransactionHistory {
EthereumTransactionHistory({
required super.walletInfo,
required super.password,
required super.encryptionFileUtils,
});
@override

View file

@ -2,6 +2,7 @@ import 'dart:convert';
import 'package:cw_core/cake_hive.dart';
import 'package:cw_core/crypto_currency.dart';
import 'package:cw_core/encryption_file_utils.dart';
import 'package:cw_core/erc20_token.dart';
import 'package:cw_core/pathForWallet.dart';
import 'package:cw_core/transaction_direction.dart';
@ -16,7 +17,6 @@ import 'package:cw_evm/evm_chain_transaction_info.dart';
import 'package:cw_evm/evm_chain_transaction_model.dart';
import 'package:cw_evm/evm_chain_wallet.dart';
import 'package:cw_evm/evm_erc20_balance.dart';
import 'package:cw_evm/file.dart';
class EthereumWallet extends EVMChainWallet {
EthereumWallet({
@ -26,6 +26,7 @@ class EthereumWallet extends EVMChainWallet {
super.mnemonic,
super.initialBalance,
super.privateKey,
required super.encryptionFileUtils,
}) : super(nativeCurrency: CryptoCurrency.eth);
@override
@ -117,18 +118,24 @@ class EthereumWallet extends EVMChainWallet {
}
@override
EVMChainTransactionHistory setUpTransactionHistory(WalletInfo walletInfo, String password) {
return EthereumTransactionHistory(walletInfo: walletInfo, password: password);
EVMChainTransactionHistory setUpTransactionHistory(
WalletInfo walletInfo, String password, EncryptionFileUtils encryptionFileUtils) {
return EthereumTransactionHistory(
walletInfo: walletInfo, password: password, encryptionFileUtils: encryptionFileUtils);
}
static Future<EthereumWallet> open(
{required String name, required String password, required WalletInfo walletInfo}) async {
static Future<EthereumWallet> open({
required String name,
required String password,
required WalletInfo walletInfo,
required EncryptionFileUtils encryptionFileUtils,
}) async {
final hasKeysFile = await WalletKeysFile.hasKeysFile(name, walletInfo.type);
final path = await pathForWallet(name: name, type: walletInfo.type);
Map<String, dynamic>? data;
try {
final jsonSource = await read(path: path, password: password);
final jsonSource = await encryptionFileUtils.read(path: path, password: password);
data = json.decode(jsonSource) as Map<String, dynamic>;
} catch (e) {
@ -146,7 +153,12 @@ class EthereumWallet extends EVMChainWallet {
keysData = WalletKeysData(mnemonic: mnemonic, privateKey: privateKey);
} else {
keysData = await WalletKeysFile.readKeysFile(name, walletInfo.type, password);
keysData = await WalletKeysFile.readKeysFile(
name,
walletInfo.type,
password,
encryptionFileUtils,
);
}
return EthereumWallet(
@ -156,6 +168,7 @@ class EthereumWallet extends EVMChainWallet {
privateKey: keysData.privateKey,
initialBalance: balance,
client: EthereumClient(),
encryptionFileUtils: encryptionFileUtils,
);
}
}

View file

@ -1,4 +1,5 @@
import 'package:bip39/bip39.dart' as bip39;
import 'package:cw_core/encryption_file_utils.dart';
import 'package:cw_core/wallet_base.dart';
import 'package:cw_core/wallet_info.dart';
import 'package:cw_core/wallet_type.dart';
@ -9,7 +10,7 @@ import 'package:cw_evm/evm_chain_wallet_creation_credentials.dart';
import 'package:cw_evm/evm_chain_wallet_service.dart';
class EthereumWalletService extends EVMChainWalletService<EthereumWallet> {
EthereumWalletService(super.walletInfoSource, {required this.client});
EthereumWalletService(super.walletInfoSource, super.isDirect, {required this.client});
late EthereumClient client;
@ -27,6 +28,7 @@ class EthereumWalletService extends EVMChainWalletService<EthereumWallet> {
mnemonic: mnemonic,
password: credentials.password!,
client: client,
encryptionFileUtils: encryptionFileUtilsFor(isDirect),
);
await wallet.init();
@ -46,6 +48,7 @@ class EthereumWalletService extends EVMChainWalletService<EthereumWallet> {
name: name,
password: password,
walletInfo: walletInfo,
encryptionFileUtils: encryptionFileUtilsFor(isDirect),
);
await wallet.init();
@ -59,6 +62,7 @@ class EthereumWalletService extends EVMChainWalletService<EthereumWallet> {
name: name,
password: password,
walletInfo: walletInfo,
encryptionFileUtils: encryptionFileUtilsFor(isDirect),
);
await wallet.init();
await wallet.save();
@ -71,7 +75,11 @@ class EthereumWalletService extends EVMChainWalletService<EthereumWallet> {
final currentWalletInfo = walletInfoSource.values
.firstWhere((info) => info.id == WalletBase.idFor(currentName, getType()));
final currentWallet = await EthereumWallet.open(
password: password, name: currentName, walletInfo: currentWalletInfo);
password: password,
name: currentName,
walletInfo: currentWalletInfo,
encryptionFileUtils: encryptionFileUtilsFor(isDirect),
);
await currentWallet.renameWalletFiles(newName);
await saveBackup(newName);
@ -97,6 +105,7 @@ class EthereumWalletService extends EVMChainWalletService<EthereumWallet> {
walletInfo: credentials.walletInfo!,
password: credentials.password!,
client: client,
encryptionFileUtils: encryptionFileUtilsFor(isDirect),
);
await wallet.init();
@ -114,6 +123,7 @@ class EthereumWalletService extends EVMChainWalletService<EthereumWallet> {
privateKey: credentials.privateKey,
walletInfo: credentials.walletInfo!,
client: client,
encryptionFileUtils: encryptionFileUtilsFor(isDirect),
);
await wallet.init();
@ -135,6 +145,7 @@ class EthereumWalletService extends EVMChainWalletService<EthereumWallet> {
mnemonic: credentials.mnemonic,
walletInfo: credentials.walletInfo!,
client: client,
encryptionFileUtils: encryptionFileUtilsFor(isDirect),
);
await wallet.init();

View file

@ -1,10 +1,10 @@
import 'dart:convert';
import 'dart:core';
import 'dart:developer';
import 'package:cw_core/encryption_file_utils.dart';
import 'package:cw_core/pathForWallet.dart';
import 'package:cw_core/wallet_info.dart';
import 'package:cw_evm/evm_chain_transaction_info.dart';
import 'package:cw_evm/file.dart';
import 'package:mobx/mobx.dart';
import 'package:cw_core/transaction_history.dart';
@ -15,7 +15,8 @@ abstract class EVMChainTransactionHistory = EVMChainTransactionHistoryBase
abstract class EVMChainTransactionHistoryBase
extends TransactionHistoryBase<EVMChainTransactionInfo> with Store {
EVMChainTransactionHistoryBase({required this.walletInfo, required String password})
EVMChainTransactionHistoryBase(
{required this.walletInfo, required String password, required this.encryptionFileUtils})
: _password = password {
transactions = ObservableMap<String, EVMChainTransactionInfo>();
}
@ -23,6 +24,7 @@ abstract class EVMChainTransactionHistoryBase
String _password;
final WalletInfo walletInfo;
final EncryptionFileUtils encryptionFileUtils;
//! Method to be overridden by all child classes
@ -41,7 +43,7 @@ abstract class EVMChainTransactionHistoryBase
final dirPath = await pathForWalletDir(name: walletInfo.name, type: walletInfo.type);
String path = '$dirPath/$transactionsHistoryFileNameForWallet';
final data = json.encode({'transactions': transactions});
await writeData(path: path, password: _password, data: data);
await encryptionFileUtils.write(path: path, password: _password, data: data);
} catch (e, s) {
log('Error while saving ${walletInfo.type.name} transaction history: ${e.toString()}');
log(s.toString());
@ -59,7 +61,7 @@ abstract class EVMChainTransactionHistoryBase
final transactionsHistoryFileNameForWallet = getTransactionHistoryFileName();
final dirPath = await pathForWalletDir(name: walletInfo.name, type: walletInfo.type);
String path = '$dirPath/$transactionsHistoryFileNameForWallet';
final content = await read(path: path, password: _password);
final content = await encryptionFileUtils.read(path: path, password: _password);
if (content.isEmpty) {
return {};
}

View file

@ -7,6 +7,7 @@ import 'package:bip32/bip32.dart' as bip32;
import 'package:bip39/bip39.dart' as bip39;
import 'package:cw_core/cake_hive.dart';
import 'package:cw_core/crypto_currency.dart';
import 'package:cw_core/encryption_file_utils.dart';
import 'package:cw_core/erc20_token.dart';
import 'package:cw_core/node.dart';
import 'package:cw_core/pathForWallet.dart';
@ -27,7 +28,6 @@ import 'package:cw_evm/evm_chain_transaction_model.dart';
import 'package:cw_evm/evm_chain_transaction_priority.dart';
import 'package:cw_evm/evm_chain_wallet_addresses.dart';
import 'package:cw_evm/evm_ledger_credentials.dart';
import 'package:cw_evm/file.dart';
import 'package:flutter/foundation.dart';
import 'package:hex/hex.dart';
import 'package:hive/hive.dart';
@ -68,6 +68,7 @@ abstract class EVMChainWalletBase
String? privateKey,
required String password,
EVMChainERC20Balance? initialBalance,
required this.encryptionFileUtils,
}) : syncStatus = const NotConnectedSyncStatus(),
_password = password,
_mnemonic = mnemonic,
@ -83,7 +84,7 @@ abstract class EVMChainWalletBase
),
super(walletInfo) {
this.walletInfo = walletInfo;
transactionHistory = setUpTransactionHistory(walletInfo, password);
transactionHistory = setUpTransactionHistory(walletInfo, password, encryptionFileUtils);
if (!CakeHive.isAdapterRegistered(Erc20Token.typeId)) {
CakeHive.registerAdapter(Erc20TokenAdapter());
@ -95,6 +96,7 @@ abstract class EVMChainWalletBase
final String? _mnemonic;
final String? _hexPrivateKey;
final String _password;
final EncryptionFileUtils encryptionFileUtils;
late final Box<Erc20Token> erc20TokensBox;
@ -149,7 +151,11 @@ abstract class EVMChainWalletBase
Erc20Token createNewErc20TokenObject(Erc20Token token, String? iconPath);
EVMChainTransactionHistory setUpTransactionHistory(WalletInfo walletInfo, String password);
EVMChainTransactionHistory setUpTransactionHistory(
WalletInfo walletInfo,
String password,
EncryptionFileUtils encryptionFileUtils,
);
//! Common Methods across child classes
@ -510,13 +516,13 @@ abstract class EVMChainWalletBase
@override
Future<void> save() async {
if (!(await WalletKeysFile.hasKeysFile(walletInfo.name, walletInfo.type))) {
await saveKeysFile(_password);
saveKeysFile(_password, true);
await saveKeysFile(_password, encryptionFileUtils);
saveKeysFile(_password, encryptionFileUtils, true);
}
await walletAddresses.updateAddressesInBox();
final path = await makePath();
await write(path: path, password: _password, data: toJSON());
await encryptionFileUtils.write(path: path, password: _password, data: toJSON());
await transactionHistory.save();
}
@ -690,4 +696,7 @@ abstract class EVMChainWalletBase
bytesToHex(await _evmChainPrivateKey.signPersonalMessage(ascii.encode(message)));
Web3Client? getWeb3Client() => _client.getWeb3Client();
@override
String get password => _password;
}

View file

@ -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 {

View file

@ -15,9 +15,10 @@ abstract class EVMChainWalletService<T extends EVMChainWallet> extends WalletSer
EVMChainRestoreWalletFromSeedCredentials,
EVMChainRestoreWalletFromPrivateKey,
EVMChainRestoreWalletFromHardware> {
EVMChainWalletService(this.walletInfoSource);
EVMChainWalletService(this.walletInfoSource, this.isDirect);
final Box<WalletInfo> walletInfoSource;
final bool isDirect;
@override
WalletType getType();

View file

@ -1,39 +0,0 @@
import 'dart:io';
import 'package:cw_core/key.dart';
import 'package:encrypt/encrypt.dart' as encrypt;
Future<void> write(
{required String path,
required String password,
required String data}) async {
final keys = extractKeys(password);
final key = encrypt.Key.fromBase64(keys.first);
final iv = encrypt.IV.fromBase64(keys.last);
final encrypted = await encode(key: key, iv: iv, data: data);
final f = File(path);
f.writeAsStringSync(encrypted);
}
Future<void> writeData(
{required String path,
required String password,
required String data}) async {
final keys = extractKeys(password);
final key = encrypt.Key.fromBase64(keys.first);
final iv = encrypt.IV.fromBase64(keys.last);
final encrypted = await encode(key: key, iv: iv, data: data);
final f = File(path);
f.writeAsStringSync(encrypted);
}
Future<String> read({required String path, required String password}) async {
final file = File(path);
if (!file.existsSync()) {
file.createSync();
}
final encrypted = file.readAsStringSync();
return decode(password: password, data: encrypted);
}

View file

@ -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

View file

@ -38,9 +38,10 @@ class HavenWallet = HavenWalletBase with _$HavenWallet;
abstract class HavenWalletBase
extends WalletBase<MoneroBalance, HavenTransactionHistory, HavenTransactionInfo> with Store {
HavenWalletBase({required WalletInfo walletInfo})
HavenWalletBase({required WalletInfo walletInfo, String? password})
: balance = ObservableMap.of(getHavenBalance(accountIndex: 0)),
_isTransactionUpdating = false,
_password = password ?? '',
_hasSyncAfterStartup = false,
walletAddresses = HavenWalletAddresses(walletInfo),
syncStatus = NotConnectedSyncStatus(),
@ -56,6 +57,7 @@ abstract class HavenWalletBase
}
static const int _autoSaveInterval = 30;
final String _password;
@override
HavenWalletAddresses walletAddresses;
@ -111,7 +113,7 @@ abstract class HavenWalletBase
_onAccountChangeReaction?.reaction.dispose();
_autoSaveTimer?.cancel();
}
@override
Future<void> connectToNode({required Node node}) async {
try {
@ -414,4 +416,7 @@ abstract class HavenWalletBase
print(e.toString());
}
}
@override
String get password => _password;
}

View file

@ -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:

View file

@ -18,6 +18,9 @@ migration:
- platform: macos
create_revision: e3c29ec00c9c825c891d75054c63fcc46454dca1
base_revision: e3c29ec00c9c825c891d75054c63fcc46454dca1
- platform: linux
create_revision: e3c29ec00c9c825c891d75054c63fcc46454dca1
base_revision: e3c29ec00c9c825c891d75054c63fcc46454dca1
# User provided section

1
cw_monero/example/linux/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
flutter/ephemeral

View file

@ -0,0 +1,138 @@
# Project-level configuration.
cmake_minimum_required(VERSION 3.10)
project(runner LANGUAGES CXX)
# The name of the executable created for the application. Change this to change
# the on-disk name of your application.
set(BINARY_NAME "cw_monero_example")
# The unique GTK application identifier for this application. See:
# https://wiki.gnome.org/HowDoI/ChooseApplicationID
set(APPLICATION_ID "com.cakewallet.cw_monero")
# Explicitly opt in to modern CMake behaviors to avoid warnings with recent
# versions of CMake.
cmake_policy(SET CMP0063 NEW)
# Load bundled libraries from the lib/ directory relative to the binary.
set(CMAKE_INSTALL_RPATH "$ORIGIN/lib")
# Root filesystem for cross-building.
if(FLUTTER_TARGET_PLATFORM_SYSROOT)
set(CMAKE_SYSROOT ${FLUTTER_TARGET_PLATFORM_SYSROOT})
set(CMAKE_FIND_ROOT_PATH ${CMAKE_SYSROOT})
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
endif()
# Define build configuration options.
if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
set(CMAKE_BUILD_TYPE "Debug" CACHE
STRING "Flutter build mode" FORCE)
set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS
"Debug" "Profile" "Release")
endif()
# Compilation settings that should be applied to most targets.
#
# Be cautious about adding new options here, as plugins use this function by
# default. In most cases, you should add new options to specific targets instead
# of modifying this function.
function(APPLY_STANDARD_SETTINGS TARGET)
target_compile_features(${TARGET} PUBLIC cxx_std_14)
target_compile_options(${TARGET} PRIVATE -Wall -Werror)
target_compile_options(${TARGET} PRIVATE "$<$<NOT:$<CONFIG:Debug>>:-O3>")
target_compile_definitions(${TARGET} PRIVATE "$<$<NOT:$<CONFIG:Debug>>:NDEBUG>")
endfunction()
# Flutter library and tool build rules.
set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter")
add_subdirectory(${FLUTTER_MANAGED_DIR})
# System-level dependencies.
find_package(PkgConfig REQUIRED)
pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0)
add_definitions(-DAPPLICATION_ID="${APPLICATION_ID}")
# Define the application target. To change its name, change BINARY_NAME above,
# not the value here, or `flutter run` will no longer work.
#
# Any new source files that you add to the application should be added here.
add_executable(${BINARY_NAME}
"main.cc"
"my_application.cc"
"${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc"
)
# Apply the standard set of build settings. This can be removed for applications
# that need different build settings.
apply_standard_settings(${BINARY_NAME})
# Add dependency libraries. Add any application-specific dependencies here.
target_link_libraries(${BINARY_NAME} PRIVATE flutter)
target_link_libraries(${BINARY_NAME} PRIVATE PkgConfig::GTK)
# Run the Flutter tool portions of the build. This must not be removed.
add_dependencies(${BINARY_NAME} flutter_assemble)
# Only the install-generated bundle's copy of the executable will launch
# correctly, since the resources must in the right relative locations. To avoid
# people trying to run the unbundled copy, put it in a subdirectory instead of
# the default top-level location.
set_target_properties(${BINARY_NAME}
PROPERTIES
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/intermediates_do_not_run"
)
# Generated plugin build rules, which manage building the plugins and adding
# them to the application.
include(flutter/generated_plugins.cmake)
# === Installation ===
# By default, "installing" just makes a relocatable bundle in the build
# directory.
set(BUILD_BUNDLE_DIR "${PROJECT_BINARY_DIR}/bundle")
if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE)
endif()
# Start with a clean build bundle directory every time.
install(CODE "
file(REMOVE_RECURSE \"${BUILD_BUNDLE_DIR}/\")
" COMPONENT Runtime)
set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data")
set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}/lib")
install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}"
COMPONENT Runtime)
install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}"
COMPONENT Runtime)
install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
COMPONENT Runtime)
foreach(bundled_library ${PLUGIN_BUNDLED_LIBRARIES})
install(FILES "${bundled_library}"
DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
COMPONENT Runtime)
endforeach(bundled_library)
# Fully re-copy the assets directory on each build to avoid having stale files
# from a previous install.
set(FLUTTER_ASSET_DIR_NAME "flutter_assets")
install(CODE "
file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\")
" COMPONENT Runtime)
install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}"
DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime)
# Install the AOT library on non-Debug builds only.
if(NOT CMAKE_BUILD_TYPE MATCHES "Debug")
install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
COMPONENT Runtime)
endif()

View file

@ -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}
)

View file

@ -0,0 +1,15 @@
//
// Generated file. Do not edit.
//
// clang-format off
#include "generated_plugin_registrant.h"
#include <cw_monero/cw_monero_plugin.h>
void fl_register_plugins(FlPluginRegistry* registry) {
g_autoptr(FlPluginRegistrar) cw_monero_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "CwMoneroPlugin");
cw_monero_plugin_register_with_registrar(cw_monero_registrar);
}

View file

@ -0,0 +1,15 @@
//
// Generated file. Do not edit.
//
// clang-format off
#ifndef GENERATED_PLUGIN_REGISTRANT_
#define GENERATED_PLUGIN_REGISTRANT_
#include <flutter_linux/flutter_linux.h>
// Registers Flutter plugins.
void fl_register_plugins(FlPluginRegistry* registry);
#endif // GENERATED_PLUGIN_REGISTRANT_

View file

@ -0,0 +1,24 @@
#
# Generated file, do not edit.
#
list(APPEND FLUTTER_PLUGIN_LIST
cw_monero
)
list(APPEND FLUTTER_FFI_PLUGIN_LIST
)
set(PLUGIN_BUNDLED_LIBRARIES)
foreach(plugin ${FLUTTER_PLUGIN_LIST})
add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/linux plugins/${plugin})
target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin)
list(APPEND PLUGIN_BUNDLED_LIBRARIES $<TARGET_FILE:${plugin}_plugin>)
list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries})
endforeach(plugin)
foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST})
add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/linux plugins/${ffi_plugin})
list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries})
endforeach(ffi_plugin)

View file

@ -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);
}

View file

@ -0,0 +1,104 @@
#include "my_application.h"
#include <flutter_linux/flutter_linux.h>
#ifdef GDK_WINDOWING_X11
#include <gdk/gdkx.h>
#endif
#include "flutter/generated_plugin_registrant.h"
struct _MyApplication {
GtkApplication parent_instance;
char** dart_entrypoint_arguments;
};
G_DEFINE_TYPE(MyApplication, my_application, GTK_TYPE_APPLICATION)
// Implements GApplication::activate.
static void my_application_activate(GApplication* application) {
MyApplication* self = MY_APPLICATION(application);
GtkWindow* window =
GTK_WINDOW(gtk_application_window_new(GTK_APPLICATION(application)));
// Use a header bar when running in GNOME as this is the common style used
// by applications and is the setup most users will be using (e.g. Ubuntu
// desktop).
// If running on X and not using GNOME then just use a traditional title bar
// in case the window manager does more exotic layout, e.g. tiling.
// If running on Wayland assume the header bar will work (may need changing
// if future cases occur).
gboolean use_header_bar = TRUE;
#ifdef GDK_WINDOWING_X11
GdkScreen* screen = gtk_window_get_screen(window);
if (GDK_IS_X11_SCREEN(screen)) {
const gchar* wm_name = gdk_x11_screen_get_window_manager_name(screen);
if (g_strcmp0(wm_name, "GNOME Shell") != 0) {
use_header_bar = FALSE;
}
}
#endif
if (use_header_bar) {
GtkHeaderBar* header_bar = GTK_HEADER_BAR(gtk_header_bar_new());
gtk_widget_show(GTK_WIDGET(header_bar));
gtk_header_bar_set_title(header_bar, "cw_monero_example");
gtk_header_bar_set_show_close_button(header_bar, TRUE);
gtk_window_set_titlebar(window, GTK_WIDGET(header_bar));
} else {
gtk_window_set_title(window, "cw_monero_example");
}
gtk_window_set_default_size(window, 1280, 720);
gtk_widget_show(GTK_WIDGET(window));
g_autoptr(FlDartProject) project = fl_dart_project_new();
fl_dart_project_set_dart_entrypoint_arguments(project, self->dart_entrypoint_arguments);
FlView* view = fl_view_new(project);
gtk_widget_show(GTK_WIDGET(view));
gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(view));
fl_register_plugins(FL_PLUGIN_REGISTRY(view));
gtk_widget_grab_focus(GTK_WIDGET(view));
}
// Implements GApplication::local_command_line.
static gboolean my_application_local_command_line(GApplication* application, gchar*** arguments, int* exit_status) {
MyApplication* self = MY_APPLICATION(application);
// Strip out the first argument as it is the binary name.
self->dart_entrypoint_arguments = g_strdupv(*arguments + 1);
g_autoptr(GError) error = nullptr;
if (!g_application_register(application, nullptr, &error)) {
g_warning("Failed to register: %s", error->message);
*exit_status = 1;
return TRUE;
}
g_application_activate(application);
*exit_status = 0;
return TRUE;
}
// Implements GObject::dispose.
static void my_application_dispose(GObject* object) {
MyApplication* self = MY_APPLICATION(object);
g_clear_pointer(&self->dart_entrypoint_arguments, g_strfreev);
G_OBJECT_CLASS(my_application_parent_class)->dispose(object);
}
static void my_application_class_init(MyApplicationClass* klass) {
G_APPLICATION_CLASS(klass)->activate = my_application_activate;
G_APPLICATION_CLASS(klass)->local_command_line = my_application_local_command_line;
G_OBJECT_CLASS(klass)->dispose = my_application_dispose;
}
static void my_application_init(MyApplication* self) {}
MyApplication* my_application_new() {
return MY_APPLICATION(g_object_new(my_application_get_type(),
"application-id", APPLICATION_ID,
"flags", G_APPLICATION_NON_UNIQUE,
nullptr));
}

View file

@ -0,0 +1,18 @@
#ifndef FLUTTER_MY_APPLICATION_H_
#define FLUTTER_MY_APPLICATION_H_
#include <gtk/gtk.h>
G_DECLARE_FINAL_TYPE(MyApplication, my_application, MY, APPLICATION,
GtkApplication)
/**
* my_application_new:
*
* Creates a new Flutter-based application.
*
* Returns: a new #MyApplication.
*/
MyApplication* my_application_new();
#endif // FLUTTER_MY_APPLICATION_H_

View file

@ -34,7 +34,6 @@ List<monero.SubaddressAccountRow> getAllAccount() {
// final size = monero.Wallet_numSubaddressAccounts(wptr!);
refreshAccounts();
int size = monero.SubaddressAccount_getAll_size(subaddressAccount!);
print("size: $size");
if (size == 0) {
monero.Wallet_addSubaddressAccount(wptr!);
return getAllAccount();

View file

@ -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';

View file

@ -3,6 +3,8 @@ import 'dart:ffi';
import 'dart:io';
import 'dart:isolate';
import 'package:cw_core/pathForWallet.dart';
import 'package:cw_core/transaction_priority.dart';
import 'package:cw_core/account.dart';
import 'package:cw_core/crypto_currency.dart';
import 'package:cw_core/monero_amount_format.dart';
@ -50,7 +52,8 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
MoneroTransactionHistory, MoneroTransactionInfo> with Store {
MoneroWalletBase(
{required WalletInfo walletInfo,
required Box<UnspentCoinsInfo> unspentCoinsInfo})
required Box<UnspentCoinsInfo> unspentCoinsInfo,
required String password})
: balance = ObservableMap<CryptoCurrency, MoneroBalance>.of({
CryptoCurrency.xmr: MoneroBalance(
fullBalance: monero_wallet.getFullBalance(accountIndex: 0),
@ -59,6 +62,7 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
_isTransactionUpdating = false,
_hasSyncAfterStartup = false,
isEnabledAutoGenerateSubaddress = false,
_password = password,
syncStatus = NotConnectedSyncStatus(),
unspentCoins = [],
this.unspentCoinsInfo = unspentCoinsInfo,
@ -111,6 +115,9 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
String get seed => monero_wallet.getSeed();
String seedLegacy(String? language) => monero_wallet.getSeedLegacy(language);
@override
String get password => _password;
@override
MoneroWalletKeys get keys => MoneroWalletKeys(
privateSpendKey: monero_wallet.getSecretSpendKey(),
@ -127,6 +134,7 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
bool _hasSyncAfterStartup;
Timer? _autoSaveTimer;
List<MoneroUnspent> unspentCoins;
String _password;
Future<void> init() async {
await walletAddresses.init();

View file

@ -93,7 +93,9 @@ class MoneroWalletService extends WalletService<
await monero_wallet_manager.createWallet(
path: path, password: credentials.password!, language: credentials.language);
final wallet = MoneroWallet(
walletInfo: credentials.walletInfo!, unspentCoinsInfo: unspentCoinsInfoSource);
walletInfo: credentials.walletInfo!,
unspentCoinsInfo: unspentCoinsInfoSource,
password: credentials.password!);
await wallet.init();
return wallet;
@ -126,10 +128,14 @@ class MoneroWalletService extends WalletService<
await repairOldAndroidWallet(name);
}
await monero_wallet_manager.openWalletAsync({'path': path, 'password': password});
final walletInfo = walletInfoSource.values
.firstWhere((info) => info.id == WalletBase.idFor(name, getType()));
wallet = MoneroWallet(walletInfo: walletInfo, unspentCoinsInfo: unspentCoinsInfoSource);
await monero_wallet_manager
.openWalletAsync({'path': path, 'password': password});
final walletInfo = walletInfoSource.values.firstWhere(
(info) => info.id == WalletBase.idFor(name, getType()));
final wallet = MoneroWallet(
walletInfo: walletInfo,
unspentCoinsInfo: unspentCoinsInfoSource,
password: password);
final isValid = wallet.walletAddresses.validate();
if (!isValid) {
@ -162,15 +168,22 @@ class MoneroWalletService extends WalletService<
final bool invalidSignature = e.toString().contains('invalid signature') ||
(e is WalletOpeningException && e.message.contains('invalid signature'));
final bool invalidPassword = e.toString().contains('invalid password') ||
(e is WalletOpeningException && e.message.contains('invalid password'));
if (!isBadAlloc &&
!doesNotCorrespond &&
!isMissingCacheFilesIOS &&
!isMissingCacheFilesAndroid &&
!invalidSignature &&
!invalidPassword &&
wallet != null &&
wallet.onError != null) {
wallet.onError!(FlutterErrorDetails(exception: e, stack: s));
}
if (invalidPassword) {
rethrow;
}
await restoreOrResetWalletFiles(name);
return openWallet(name, password);
@ -206,11 +219,15 @@ class MoneroWalletService extends WalletService<
}
@override
Future<void> rename(String currentName, String password, String newName) async {
final currentWalletInfo = walletInfoSource.values
.firstWhere((info) => info.id == WalletBase.idFor(currentName, getType()));
final currentWallet =
MoneroWallet(walletInfo: currentWalletInfo, unspentCoinsInfo: unspentCoinsInfoSource);
Future<void> rename(
String currentName, String password, String newName) async {
final currentWalletInfo = walletInfoSource.values.firstWhere(
(info) => info.id == WalletBase.idFor(currentName, getType()));
final currentWallet = MoneroWallet(
walletInfo: currentWalletInfo,
unspentCoinsInfo: unspentCoinsInfoSource,
password: password,
);
await currentWallet.renameWalletFiles(newName);
@ -235,7 +252,9 @@ class MoneroWalletService extends WalletService<
viewKey: credentials.viewKey,
spendKey: credentials.spendKey);
final wallet = MoneroWallet(
walletInfo: credentials.walletInfo!, unspentCoinsInfo: unspentCoinsInfoSource);
walletInfo: credentials.walletInfo!,
unspentCoinsInfo: unspentCoinsInfoSource,
password: credentials.password!);
await wallet.init();
return wallet;
@ -268,7 +287,9 @@ class MoneroWalletService extends WalletService<
seed: credentials.mnemonic,
restoreHeight: credentials.height!);
final wallet = MoneroWallet(
walletInfo: credentials.walletInfo!, unspentCoinsInfo: unspentCoinsInfoSource);
walletInfo: credentials.walletInfo!,
unspentCoinsInfo: unspentCoinsInfoSource,
password: credentials.password!);
await wallet.init();
return wallet;
@ -315,7 +336,11 @@ class MoneroWalletService extends WalletService<
restoreHeight: height,
spendKey: spendKey);
final wallet = MoneroWallet(walletInfo: walletInfo, unspentCoinsInfo: unspentCoinsInfoSource);
final wallet = MoneroWallet(
walletInfo: walletInfo,
unspentCoinsInfo: unspentCoinsInfoSource,
password: password,
);
await wallet.init();
return wallet;
@ -364,7 +389,11 @@ class MoneroWalletService extends WalletService<
await monero_wallet_manager.openWalletAsync({'path': path, 'password': password});
final walletInfo = walletInfoSource.values
.firstWhere((info) => info.id == WalletBase.idFor(name, getType()));
final wallet = MoneroWallet(walletInfo: walletInfo, unspentCoinsInfo: unspentCoinsInfoSource);
final wallet = MoneroWallet(
walletInfo: walletInfo,
unspentCoinsInfo: unspentCoinsInfoSource,
password: password,
);
return wallet.seed;
} catch (_) {
// if the file couldn't be opened or read

View file

@ -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
)

View file

@ -0,0 +1,70 @@
#include "include/cw_monero/cw_monero_plugin.h"
#include <flutter_linux/flutter_linux.h>
#include <gtk/gtk.h>
#include <sys/utsname.h>
#include <cstring>
#define CW_MONERO_PLUGIN(obj) \
(G_TYPE_CHECK_INSTANCE_CAST((obj), cw_monero_plugin_get_type(), \
CwMoneroPlugin))
struct _CwMoneroPlugin {
GObject parent_instance;
};
G_DEFINE_TYPE(CwMoneroPlugin, cw_monero_plugin, g_object_get_type())
// Called when a method call is received from Flutter.
static void cw_monero_plugin_handle_method_call(
CwMoneroPlugin* self,
FlMethodCall* method_call) {
g_autoptr(FlMethodResponse) response = nullptr;
const gchar* method = fl_method_call_get_name(method_call);
if (strcmp(method, "getPlatformVersion") == 0) {
struct utsname uname_data = {};
uname(&uname_data);
g_autofree gchar *version = g_strdup_printf("Linux %s", uname_data.version);
g_autoptr(FlValue) result = fl_value_new_string(version);
response = FL_METHOD_RESPONSE(fl_method_success_response_new(result));
} else {
response = FL_METHOD_RESPONSE(fl_method_not_implemented_response_new());
}
fl_method_call_respond(method_call, response, nullptr);
}
static void cw_monero_plugin_dispose(GObject* object) {
G_OBJECT_CLASS(cw_monero_plugin_parent_class)->dispose(object);
}
static void cw_monero_plugin_class_init(CwMoneroPluginClass* klass) {
G_OBJECT_CLASS(klass)->dispose = cw_monero_plugin_dispose;
}
static void cw_monero_plugin_init(CwMoneroPlugin* self) {}
static void method_call_cb(FlMethodChannel* channel, FlMethodCall* method_call,
gpointer user_data) {
CwMoneroPlugin* plugin = CW_MONERO_PLUGIN(user_data);
cw_monero_plugin_handle_method_call(plugin, method_call);
}
void cw_monero_plugin_register_with_registrar(FlPluginRegistrar* registrar) {
CwMoneroPlugin* plugin = CW_MONERO_PLUGIN(
g_object_new(cw_monero_plugin_get_type(), nullptr));
g_autoptr(FlStandardMethodCodec) codec = fl_standard_method_codec_new();
g_autoptr(FlMethodChannel) channel =
fl_method_channel_new(fl_plugin_registrar_get_messenger(registrar),
"cw_monero",
FL_METHOD_CODEC(codec));
fl_method_channel_set_method_call_handler(channel, method_call_cb,
g_object_ref(plugin),
g_object_unref);
g_object_unref(plugin);
}

View file

@ -0,0 +1,11 @@
//
// Generated file. Do not edit.
//
// clang-format off
#include "generated_plugin_registrant.h"
void fl_register_plugins(FlPluginRegistry* registry) {
}

View file

@ -0,0 +1,15 @@
//
// Generated file. Do not edit.
//
// clang-format off
#ifndef GENERATED_PLUGIN_REGISTRANT_
#define GENERATED_PLUGIN_REGISTRANT_
#include <flutter_linux/flutter_linux.h>
// Registers Flutter plugins.
void fl_register_plugins(FlPluginRegistry* registry);
#endif // GENERATED_PLUGIN_REGISTRANT_

View file

@ -0,0 +1,23 @@
#
# Generated file, do not edit.
#
list(APPEND FLUTTER_PLUGIN_LIST
)
list(APPEND FLUTTER_FFI_PLUGIN_LIST
)
set(PLUGIN_BUNDLED_LIBRARIES)
foreach(plugin ${FLUTTER_PLUGIN_LIST})
add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/linux plugins/${plugin})
target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin)
list(APPEND PLUGIN_BUNDLED_LIBRARIES $<TARGET_FILE:${plugin}_plugin>)
list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries})
endforeach(plugin)
foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST})
add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/linux plugins/${ffi_plugin})
list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries})
endforeach(ffi_plugin)

View file

@ -0,0 +1,26 @@
#ifndef FLUTTER_PLUGIN_CW_MONERO_PLUGIN_H_
#define FLUTTER_PLUGIN_CW_MONERO_PLUGIN_H_
#include <flutter_linux/flutter_linux.h>
G_BEGIN_DECLS
#ifdef FLUTTER_PLUGIN_IMPL
#define FLUTTER_PLUGIN_EXPORT __attribute__((visibility("default")))
#else
#define FLUTTER_PLUGIN_EXPORT
#endif
typedef struct _CwMoneroPlugin CwMoneroPlugin;
typedef struct {
GObjectClass parent_class;
} CwMoneroPluginClass;
FLUTTER_PLUGIN_EXPORT GType cw_monero_plugin_get_type();
FLUTTER_PLUGIN_EXPORT void cw_monero_plugin_register_with_registrar(
FlPluginRegistrar* registrar);
G_END_DECLS
#endif // FLUTTER_PLUGIN_CW_MONERO_PLUGIN_H_

View file

@ -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:

View file

@ -1,39 +0,0 @@
import 'dart:io';
import 'package:cw_core/key.dart';
import 'package:encrypt/encrypt.dart' as encrypt;
Future<void> write(
{required String path,
required String password,
required String data}) async {
final keys = extractKeys(password);
final key = encrypt.Key.fromBase64(keys.first);
final iv = encrypt.IV.fromBase64(keys.last);
final encrypted = await encode(key: key, iv: iv, data: data);
final f = File(path);
f.writeAsStringSync(encrypted);
}
Future<void> writeData(
{required String path,
required String password,
required String data}) async {
final keys = extractKeys(password);
final key = encrypt.Key.fromBase64(keys.first);
final iv = encrypt.IV.fromBase64(keys.last);
final encrypted = await encode(key: key, iv: iv, data: data);
final f = File(path);
f.writeAsStringSync(encrypted);
}
Future<String> read({required String path, required String password}) async {
final file = File(path);
if (!file.existsSync()) {
file.createSync();
}
final encrypted = file.readAsStringSync();
return decode(password: password, data: encrypted);
}

View file

@ -2,24 +2,29 @@ import 'dart:convert';
import 'dart:core';
import 'package:cw_core/pathForWallet.dart';
import 'package:cw_core/wallet_info.dart';
import 'package:cw_nano/file.dart';
import 'package:cw_core/encryption_file_utils.dart';
import 'package:mobx/mobx.dart';
import 'package:cw_core/transaction_history.dart';
import 'package:cw_nano/nano_transaction_info.dart';
part 'nano_transaction_history.g.dart';
const transactionsHistoryFileName = 'transactions.json';
class NanoTransactionHistory = NanoTransactionHistoryBase with _$NanoTransactionHistory;
abstract class NanoTransactionHistoryBase
extends TransactionHistoryBase<NanoTransactionInfo> with Store {
NanoTransactionHistoryBase({required this.walletInfo, required String password})
: _password = password {
abstract class NanoTransactionHistoryBase extends TransactionHistoryBase<NanoTransactionInfo>
with Store {
NanoTransactionHistoryBase({
required this.walletInfo,
required String password,
required this.encryptionFileUtils,
}) : _password = password {
transactions = ObservableMap<String, NanoTransactionInfo>();
}
final WalletInfo walletInfo;
final EncryptionFileUtils encryptionFileUtils;
String _password;
Future<void> init() async => await _load();
@ -30,7 +35,7 @@ abstract class NanoTransactionHistoryBase
final dirPath = await pathForWalletDir(name: walletInfo.name, type: walletInfo.type);
final path = '$dirPath/$transactionsHistoryFileName';
final data = json.encode({'transactions': transactions});
await writeData(path: path, password: _password, data: data);
await encryptionFileUtils.write(path: path, password: _password, data: data);
} catch (e) {
print('Error while save nano transaction history: ${e.toString()}');
}
@ -46,7 +51,10 @@ abstract class NanoTransactionHistoryBase
Future<Map<String, dynamic>> _read() async {
final dirPath = await pathForWalletDir(name: walletInfo.name, type: walletInfo.type);
final path = '$dirPath/$transactionsHistoryFileName';
final content = await read(path: path, password: _password);
final content = await encryptionFileUtils.read(path: path, password: _password);
if (content.isEmpty) {
return {};
}
return json.decode(content) as Map<String, dynamic>;
}

View file

@ -5,6 +5,7 @@ import 'dart:io';
import 'package:bip39/bip39.dart' as bip39;
import 'package:cw_core/cake_hive.dart';
import 'package:cw_core/crypto_currency.dart';
import 'package:cw_core/encryption_file_utils.dart';
import 'package:cw_core/n2_node.dart';
import 'package:cw_core/nano_account.dart';
import 'package:cw_core/nano_account_info_response.dart';
@ -17,7 +18,6 @@ import 'package:cw_core/transaction_priority.dart';
import 'package:cw_core/wallet_base.dart';
import 'package:cw_core/wallet_info.dart';
import 'package:cw_core/wallet_keys_file.dart';
import 'package:cw_nano/file.dart';
import 'package:cw_nano/nano_balance.dart';
import 'package:cw_nano/nano_client.dart';
import 'package:cw_nano/nano_transaction_credentials.dart';
@ -42,11 +42,13 @@ abstract class NanoWalletBase
required String mnemonic,
required String password,
NanoBalance? initialBalance,
required EncryptionFileUtils encryptionFileUtils,
}) : syncStatus = NotConnectedSyncStatus(),
_password = password,
_mnemonic = mnemonic,
_derivationType = walletInfo.derivationInfo!.derivationType!,
_isTransactionUpdating = false,
_encryptionFileUtils = encryptionFileUtils,
_client = NanoClient(),
walletAddresses = NanoWalletAddresses(walletInfo),
balance = ObservableMap<CryptoCurrency, NanoBalance>.of({
@ -55,7 +57,11 @@ abstract class NanoWalletBase
}),
super(walletInfo) {
this.walletInfo = walletInfo;
transactionHistory = NanoTransactionHistory(walletInfo: walletInfo, password: password);
transactionHistory = NanoTransactionHistory(
walletInfo: walletInfo,
password: password,
encryptionFileUtils: encryptionFileUtils,
);
if (!CakeHive.isAdapterRegistered(NanoAccount.typeId)) {
CakeHive.registerAdapter(NanoAccountAdapter());
}
@ -65,6 +71,8 @@ abstract class NanoWalletBase
final String _password;
DerivationType _derivationType;
final EncryptionFileUtils _encryptionFileUtils;
String? _privateKey;
String? _publicAddress;
String? _hexSeed;
@ -89,6 +97,9 @@ abstract class NanoWalletBase
@observable
late ObservableMap<CryptoCurrency, NanoBalance> balance;
@override
String get password => _password;
static const int POLL_INTERVAL_SECONDS = 10;
// initialize the different forms of private / public key we'll need:
@ -308,13 +319,13 @@ abstract class NanoWalletBase
@override
Future<void> save() async {
if (!(await WalletKeysFile.hasKeysFile(walletInfo.name, walletInfo.type))) {
await saveKeysFile(_password);
saveKeysFile(_password, true);
await saveKeysFile(_password, _encryptionFileUtils);
saveKeysFile(_password, _encryptionFileUtils, true);
}
await walletAddresses.updateAddressesInBox();
final path = await makePath();
await write(path: path, password: _password, data: toJSON());
await _encryptionFileUtils.write(path: path, password: _password, data: toJSON());
await transactionHistory.save();
}
@ -373,13 +384,14 @@ abstract class NanoWalletBase
required String name,
required String password,
required WalletInfo walletInfo,
required EncryptionFileUtils encryptionFileUtils,
}) async {
final hasKeysFile = await WalletKeysFile.hasKeysFile(name, walletInfo.type);
final path = await pathForWallet(name: name, type: walletInfo.type);
Map<String, dynamic>? data = null;
try {
final jsonSource = await read(path: path, password: password);
final jsonSource = await encryptionFileUtils.read(path: path, password: password);
data = json.decode(jsonSource) as Map<String, dynamic>;
} catch (e) {
@ -400,7 +412,12 @@ abstract class NanoWalletBase
keysData = WalletKeysData(
mnemonic: isHexSeed ? null : mnemonic, altMnemonic: isHexSeed ? mnemonic : null);
} else {
keysData = await WalletKeysFile.readKeysFile(name, walletInfo.type, password);
keysData = await WalletKeysFile.readKeysFile(
name,
walletInfo.type,
password,
encryptionFileUtils,
);
}
DerivationType derivationType = DerivationType.nano;
@ -416,6 +433,7 @@ abstract class NanoWalletBase
password: password,
mnemonic: keysData.mnemonic!,
initialBalance: balance,
encryptionFileUtils: encryptionFileUtils,
);
// init() should always be run after this!
}

View file

@ -1,6 +1,7 @@
import 'dart:io';
import 'package:cw_core/pathForWallet.dart';
import 'package:cw_core/encryption_file_utils.dart';
import 'package:cw_core/wallet_base.dart';
import 'package:cw_core/wallet_info.dart';
import 'package:cw_core/wallet_service.dart';
@ -15,9 +16,10 @@ import 'package:nanoutil/nanoutil.dart';
class NanoWalletService extends WalletService<NanoNewWalletCredentials,
NanoRestoreWalletFromSeedCredentials, NanoRestoreWalletFromKeysCredentials, NanoNewWalletCredentials> {
NanoWalletService(this.walletInfoSource);
NanoWalletService(this.walletInfoSource, this.isDirect);
final Box<WalletInfo> walletInfoSource;
final bool isDirect;
static bool walletFilesExist(String path) =>
!File(path).existsSync() && !File('$path.keys').existsSync();
@ -38,6 +40,7 @@ class NanoWalletService extends WalletService<NanoNewWalletCredentials,
walletInfo: credentials.walletInfo!,
mnemonic: mnemonic,
password: credentials.password!,
encryptionFileUtils: encryptionFileUtilsFor(isDirect),
);
await wallet.init();
return wallet;
@ -65,8 +68,12 @@ class NanoWalletService extends WalletService<NanoNewWalletCredentials,
String randomWords =
(List<String>.from(nm.NanoMnemomics.WORDLIST)..shuffle()).take(24).join(' ');
final currentWallet =
NanoWallet(walletInfo: currentWalletInfo, password: password, mnemonic: randomWords);
final currentWallet = NanoWallet(
walletInfo: currentWalletInfo,
password: password,
mnemonic: randomWords,
encryptionFileUtils: encryptionFileUtilsFor(isDirect),
);
await currentWallet.renameWalletFiles(newName);
await saveBackup(newName);
@ -103,6 +110,7 @@ class NanoWalletService extends WalletService<NanoNewWalletCredentials,
password: credentials.password!,
mnemonic: mnemonic ?? credentials.seedKey,
walletInfo: credentials.walletInfo!,
encryptionFileUtils: encryptionFileUtilsFor(isDirect),
);
await wallet.init();
await wallet.save();
@ -139,6 +147,7 @@ class NanoWalletService extends WalletService<NanoNewWalletCredentials,
password: credentials.password!,
mnemonic: credentials.mnemonic,
walletInfo: credentials.walletInfo!,
encryptionFileUtils: encryptionFileUtilsFor(isDirect),
);
await wallet.init();
@ -160,6 +169,7 @@ class NanoWalletService extends WalletService<NanoNewWalletCredentials,
name: name,
password: password,
walletInfo: walletInfo,
encryptionFileUtils: encryptionFileUtilsFor(isDirect),
);
await wallet.init();
@ -172,6 +182,7 @@ class NanoWalletService extends WalletService<NanoNewWalletCredentials,
name: name,
password: password,
walletInfo: walletInfo,
encryptionFileUtils: encryptionFileUtilsFor(isDirect),
);
await wallet.init();

View file

@ -137,6 +137,15 @@ packages:
url: "https://pub.dev"
source: hosted
version: "8.9.2"
cake_backup:
dependency: transitive
description:
path: "."
ref: main
resolved-ref: "3aba867dcab6737f6707782f5db15d71f303db38"
url: "https://github.com/cake-tech/cake_backup.git"
source: git
version: "1.0.0+1"
characters:
dependency: transitive
description:
@ -193,6 +202,22 @@ packages:
url: "https://pub.dev"
source: hosted
version: "3.0.3"
cryptography:
dependency: transitive
description:
name: cryptography
sha256: d146b76d33d94548cf035233fbc2f4338c1242fa119013bead807d033fc4ae05
url: "https://pub.dev"
source: hosted
version: "2.7.0"
cupertino_icons:
dependency: transitive
description:
name: cupertino_icons
sha256: ba631d1c7f7bef6b729a622b7b752645a2d076dba9976925b8f25725a30e1ee6
url: "https://pub.dev"
source: hosted
version: "1.0.8"
cw_core:
dependency: "direct main"
description:
@ -797,6 +822,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.0.1"
tuple:
dependency: transitive
description:
name: tuple
sha256: a97ce2013f240b2f3807bcbaf218765b6f301c3eff91092bcfa23a039e7dd151
url: "https://pub.dev"
source: hosted
version: "2.0.2"
typed_data:
dependency: transitive
description:

View file

@ -8,6 +8,7 @@ class PolygonTransactionHistory extends EVMChainTransactionHistory {
PolygonTransactionHistory({
required super.walletInfo,
required super.password,
required super.encryptionFileUtils,
});
@override

View file

@ -2,6 +2,7 @@ import 'dart:convert';
import 'package:cw_core/cake_hive.dart';
import 'package:cw_core/crypto_currency.dart';
import 'package:cw_core/encryption_file_utils.dart';
import 'package:cw_core/erc20_token.dart';
import 'package:cw_core/pathForWallet.dart';
import 'package:cw_core/transaction_direction.dart';
@ -12,7 +13,6 @@ import 'package:cw_evm/evm_chain_transaction_info.dart';
import 'package:cw_evm/evm_chain_transaction_model.dart';
import 'package:cw_evm/evm_chain_wallet.dart';
import 'package:cw_evm/evm_erc20_balance.dart';
import 'package:cw_evm/file.dart';
import 'package:cw_polygon/default_polygon_erc20_tokens.dart';
import 'package:cw_polygon/polygon_client.dart';
import 'package:cw_polygon/polygon_transaction_history.dart';
@ -26,6 +26,7 @@ class PolygonWallet extends EVMChainWallet {
super.initialBalance,
super.privateKey,
required super.client,
required super.encryptionFileUtils,
}) : super(nativeCurrency: CryptoCurrency.maticpoly);
@override
@ -92,18 +93,27 @@ class PolygonWallet extends EVMChainWallet {
}
@override
EVMChainTransactionHistory setUpTransactionHistory(WalletInfo walletInfo, String password) {
return PolygonTransactionHistory(walletInfo: walletInfo, password: password);
EVMChainTransactionHistory setUpTransactionHistory(
WalletInfo walletInfo, String password, EncryptionFileUtils encryptionFileUtils) {
return PolygonTransactionHistory(
walletInfo: walletInfo,
password: password,
encryptionFileUtils: encryptionFileUtils,
);
}
static Future<PolygonWallet> open(
{required String name, required String password, required WalletInfo walletInfo}) async {
static Future<PolygonWallet> open({
required String name,
required String password,
required WalletInfo walletInfo,
required EncryptionFileUtils encryptionFileUtils,
}) async {
final hasKeysFile = await WalletKeysFile.hasKeysFile(name, walletInfo.type);
final path = await pathForWallet(name: name, type: walletInfo.type);
Map<String, dynamic>? data;
try {
final jsonSource = await read(path: path, password: password);
final jsonSource = await encryptionFileUtils.read(path: path, password: password);
data = json.decode(jsonSource) as Map<String, dynamic>;
} catch (e) {
@ -121,7 +131,12 @@ class PolygonWallet extends EVMChainWallet {
keysData = WalletKeysData(mnemonic: mnemonic, privateKey: privateKey);
} else {
keysData = await WalletKeysFile.readKeysFile(name, walletInfo.type, password);
keysData = await WalletKeysFile.readKeysFile(
name,
walletInfo.type,
password,
encryptionFileUtils,
);
}
return PolygonWallet(
@ -131,6 +146,7 @@ class PolygonWallet extends EVMChainWallet {
privateKey: keysData.privateKey,
initialBalance: balance,
client: PolygonClient(),
encryptionFileUtils: encryptionFileUtils,
);
}
}

View file

@ -1,4 +1,5 @@
import 'package:bip39/bip39.dart' as bip39;
import 'package:cw_core/encryption_file_utils.dart';
import 'package:cw_core/wallet_base.dart';
import 'package:cw_core/wallet_info.dart';
import 'package:cw_core/wallet_type.dart';
@ -10,7 +11,7 @@ import 'package:cw_polygon/polygon_wallet.dart';
class PolygonWalletService extends EVMChainWalletService<PolygonWallet> {
PolygonWalletService(
super.walletInfoSource, {
super.walletInfoSource, super.isDirect, {
required this.client,
});
@ -30,6 +31,7 @@ class PolygonWalletService extends EVMChainWalletService<PolygonWallet> {
mnemonic: mnemonic,
password: credentials.password!,
client: client,
encryptionFileUtils: encryptionFileUtilsFor(isDirect),
);
await wallet.init();
@ -48,6 +50,7 @@ class PolygonWalletService extends EVMChainWalletService<PolygonWallet> {
name: name,
password: password,
walletInfo: walletInfo,
encryptionFileUtils: encryptionFileUtilsFor(isDirect),
);
await wallet.init();
@ -61,6 +64,7 @@ class PolygonWalletService extends EVMChainWalletService<PolygonWallet> {
name: name,
password: password,
walletInfo: walletInfo,
encryptionFileUtils: encryptionFileUtilsFor(isDirect),
);
await wallet.init();
@ -77,6 +81,7 @@ class PolygonWalletService extends EVMChainWalletService<PolygonWallet> {
privateKey: credentials.privateKey,
walletInfo: credentials.walletInfo!,
client: client,
encryptionFileUtils: encryptionFileUtilsFor(isDirect),
);
await wallet.init();
@ -99,6 +104,7 @@ class PolygonWalletService extends EVMChainWalletService<PolygonWallet> {
walletInfo: credentials.walletInfo!,
password: credentials.password!,
client: client,
encryptionFileUtils: encryptionFileUtilsFor(isDirect),
);
await wallet.init();
@ -120,6 +126,7 @@ class PolygonWalletService extends EVMChainWalletService<PolygonWallet> {
mnemonic: credentials.mnemonic,
walletInfo: credentials.walletInfo!,
client: client,
encryptionFileUtils: encryptionFileUtilsFor(isDirect),
);
await wallet.init();
@ -134,7 +141,11 @@ class PolygonWalletService extends EVMChainWalletService<PolygonWallet> {
final currentWalletInfo = walletInfoSource.values
.firstWhere((info) => info.id == WalletBase.idFor(currentName, getType()));
final currentWallet = await PolygonWallet.open(
password: password, name: currentName, walletInfo: currentWalletInfo);
password: password,
name: currentName,
walletInfo: currentWalletInfo,
encryptionFileUtils: encryptionFileUtilsFor(isDirect),
);
await currentWallet.renameWalletFiles(newName);
await saveBackup(newName);

View file

@ -1,39 +0,0 @@
import 'dart:io';
import 'package:cw_core/key.dart';
import 'package:encrypt/encrypt.dart' as encrypt;
Future<void> write(
{required String path,
required String password,
required String data}) async {
final keys = extractKeys(password);
final key = encrypt.Key.fromBase64(keys.first);
final iv = encrypt.IV.fromBase64(keys.last);
final encrypted = await encode(key: key, iv: iv, data: data);
final f = File(path);
f.writeAsStringSync(encrypted);
}
Future<void> writeData(
{required String path,
required String password,
required String data}) async {
final keys = extractKeys(password);
final key = encrypt.Key.fromBase64(keys.first);
final iv = encrypt.IV.fromBase64(keys.last);
final encrypted = await encode(key: key, iv: iv, data: data);
final f = File(path);
f.writeAsStringSync(encrypted);
}
Future<String> read({required String path, required String password}) async {
final file = File(path);
if (!file.existsSync()) {
file.createSync();
}
final encrypted = file.readAsStringSync();
return decode(password: password, data: encrypted);
}

View file

@ -1,8 +1,8 @@
import 'dart:convert';
import 'dart:core';
import 'package:cw_core/encryption_file_utils.dart';
import 'package:cw_core/pathForWallet.dart';
import 'package:cw_core/wallet_info.dart';
import 'package:cw_solana/file.dart';
import 'package:cw_solana/solana_transaction_info.dart';
import 'package:mobx/mobx.dart';
import 'package:cw_core/transaction_history.dart';
@ -15,12 +15,14 @@ class SolanaTransactionHistory = SolanaTransactionHistoryBase with _$SolanaTrans
abstract class SolanaTransactionHistoryBase extends TransactionHistoryBase<SolanaTransactionInfo>
with Store {
SolanaTransactionHistoryBase({required this.walletInfo, required String password})
SolanaTransactionHistoryBase(
{required this.walletInfo, required String password, required this.encryptionFileUtils})
: _password = password {
transactions = ObservableMap<String, SolanaTransactionInfo>();
}
final WalletInfo walletInfo;
final EncryptionFileUtils encryptionFileUtils;
String _password;
Future<void> init() async => await _load();
@ -32,7 +34,7 @@ abstract class SolanaTransactionHistoryBase extends TransactionHistoryBase<Solan
final path = '$dirPath/$transactionsHistoryFileName';
final transactionMaps = transactions.map((key, value) => MapEntry(key, value.toJson()));
final data = json.encode({'transactions': transactionMaps});
await writeData(path: path, password: _password, data: data);
await encryptionFileUtils.write(path: path, password: _password, data: data);
} catch (e, s) {
print('Error while saving solana transaction history: ${e.toString()}');
print(s);
@ -49,7 +51,7 @@ abstract class SolanaTransactionHistoryBase extends TransactionHistoryBase<Solan
Future<Map<String, dynamic>> _read() async {
final dirPath = await pathForWalletDir(name: walletInfo.name, type: walletInfo.type);
final path = '$dirPath/$transactionsHistoryFileName';
final content = await read(path: path, password: _password);
final content = await encryptionFileUtils.read(path: path, password: _password);
if (content.isEmpty) {
return {};
}

View file

@ -4,6 +4,7 @@ import 'dart:io';
import 'package:cw_core/cake_hive.dart';
import 'package:cw_core/crypto_currency.dart';
import 'package:cw_core/encryption_file_utils.dart';
import 'package:cw_core/node.dart';
import 'package:cw_core/pathForWallet.dart';
import 'package:cw_core/pending_transaction.dart';
@ -15,7 +16,6 @@ import 'package:cw_core/wallet_base.dart';
import 'package:cw_core/wallet_info.dart';
import 'package:cw_core/wallet_keys_file.dart';
import 'package:cw_solana/default_spl_tokens.dart';
import 'package:cw_solana/file.dart';
import 'package:cw_solana/solana_balance.dart';
import 'package:cw_solana/solana_client.dart';
import 'package:cw_solana/solana_exceptions.dart';
@ -46,6 +46,7 @@ abstract class SolanaWalletBase
String? privateKey,
required String password,
SolanaBalance? initialBalance,
required this.encryptionFileUtils,
}) : syncStatus = const NotConnectedSyncStatus(),
_password = password,
_mnemonic = mnemonic,
@ -56,7 +57,11 @@ abstract class SolanaWalletBase
{CryptoCurrency.sol: initialBalance ?? SolanaBalance(BigInt.zero.toDouble())}),
super(walletInfo) {
this.walletInfo = walletInfo;
transactionHistory = SolanaTransactionHistory(walletInfo: walletInfo, password: password);
transactionHistory = SolanaTransactionHistory(
walletInfo: walletInfo,
password: password,
encryptionFileUtils: encryptionFileUtils,
);
if (!CakeHive.isAdapterRegistered(SPLToken.typeId)) {
CakeHive.registerAdapter(SPLTokenAdapter());
@ -68,6 +73,7 @@ abstract class SolanaWalletBase
final String _password;
final String? _mnemonic;
final String? _hexPrivateKey;
final EncryptionFileUtils encryptionFileUtils;
// The Solana WalletPair
Ed25519HDKeyPair? _walletKeyPair;
@ -77,7 +83,7 @@ abstract class SolanaWalletBase
// To access the privateKey bytes.
Ed25519HDKeyPairData? _keyPairData;
late SolanaWalletClient _client;
late final SolanaWalletClient _client;
@observable
double? estimatedFee;
@ -97,7 +103,7 @@ abstract class SolanaWalletBase
@observable
late ObservableMap<CryptoCurrency, SolanaBalance> balance;
Completer<SharedPreferences> _sharedPrefs = Completer();
final Completer<SharedPreferences> _sharedPrefs = Completer();
@override
Ed25519HDKeyPairData get keys {
@ -343,13 +349,13 @@ abstract class SolanaWalletBase
@override
Future<void> save() async {
if (!(await WalletKeysFile.hasKeysFile(walletInfo.name, walletInfo.type))) {
await saveKeysFile(_password);
saveKeysFile(_password, true);
await saveKeysFile(_password, encryptionFileUtils);
saveKeysFile(_password, encryptionFileUtils, true);
}
await walletAddresses.updateAddressesInBox();
final path = await makePath();
await write(path: path, password: _password, data: toJSON());
await encryptionFileUtils.write(path: path, password: _password, data: toJSON());
await transactionHistory.save();
}
@ -382,13 +388,14 @@ abstract class SolanaWalletBase
required String name,
required String password,
required WalletInfo walletInfo,
required EncryptionFileUtils encryptionFileUtils,
}) async {
final hasKeysFile = await WalletKeysFile.hasKeysFile(name, walletInfo.type);
final path = await pathForWallet(name: name, type: walletInfo.type);
Map<String, dynamic>? data;
try {
final jsonSource = await read(path: path, password: password);
final jsonSource = await encryptionFileUtils.read(path: path, password: password);
data = json.decode(jsonSource) as Map<String, dynamic>;
} catch (e) {
@ -405,7 +412,12 @@ abstract class SolanaWalletBase
keysData = WalletKeysData(mnemonic: mnemonic, privateKey: privateKey);
} else {
keysData = await WalletKeysFile.readKeysFile(name, walletInfo.type, password);
keysData = await WalletKeysFile.readKeysFile(
name,
walletInfo.type,
password,
encryptionFileUtils,
);
}
return SolanaWallet(
@ -414,6 +426,7 @@ abstract class SolanaWalletBase
mnemonic: keysData.mnemonic,
privateKey: keysData.privateKey,
initialBalance: balance,
encryptionFileUtils: encryptionFileUtils,
);
}
@ -572,4 +585,7 @@ abstract class SolanaWalletBase
}
SolanaClient? get solanaClient => _client.getSolanaClient;
@override
String get password => _password;
}

View file

@ -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 {

View file

@ -2,6 +2,7 @@ import 'dart:io';
import 'package:bip39/bip39.dart' as bip39;
import 'package:collection/collection.dart';
import 'package:cw_core/encryption_file_utils.dart';
import 'package:cw_core/balance.dart';
import 'package:cw_core/pathForWallet.dart';
import 'package:cw_core/transaction_history.dart';
@ -17,9 +18,10 @@ import 'package:hive/hive.dart';
class SolanaWalletService extends WalletService<SolanaNewWalletCredentials,
SolanaRestoreWalletFromSeedCredentials, SolanaRestoreWalletFromPrivateKey, SolanaNewWalletCredentials> {
SolanaWalletService(this.walletInfoSource);
SolanaWalletService(this.walletInfoSource, this.isDirect);
final Box<WalletInfo> walletInfoSource;
final bool isDirect;
@override
Future<SolanaWallet> create(SolanaNewWalletCredentials credentials, {bool? isTestnet}) async {
@ -31,6 +33,7 @@ class SolanaWalletService extends WalletService<SolanaNewWalletCredentials,
walletInfo: credentials.walletInfo!,
mnemonic: mnemonic,
password: credentials.password!,
encryptionFileUtils: encryptionFileUtilsFor(isDirect),
);
await wallet.init();
@ -56,6 +59,7 @@ class SolanaWalletService extends WalletService<SolanaNewWalletCredentials,
name: name,
password: password,
walletInfo: walletInfo,
encryptionFileUtils: encryptionFileUtilsFor(isDirect),
);
await wallet.init();
@ -69,6 +73,7 @@ class SolanaWalletService extends WalletService<SolanaNewWalletCredentials,
name: name,
password: password,
walletInfo: walletInfo,
encryptionFileUtils: encryptionFileUtilsFor(isDirect),
);
await wallet.init();
@ -92,6 +97,7 @@ class SolanaWalletService extends WalletService<SolanaNewWalletCredentials,
password: credentials.password!,
privateKey: credentials.privateKey,
walletInfo: credentials.walletInfo!,
encryptionFileUtils: encryptionFileUtilsFor(isDirect),
);
await wallet.init();
@ -112,6 +118,7 @@ class SolanaWalletService extends WalletService<SolanaNewWalletCredentials,
password: credentials.password!,
mnemonic: credentials.mnemonic,
walletInfo: credentials.walletInfo!,
encryptionFileUtils: encryptionFileUtilsFor(isDirect),
);
await wallet.init();
@ -126,7 +133,11 @@ class SolanaWalletService extends WalletService<SolanaNewWalletCredentials,
final currentWalletInfo = walletInfoSource.values
.firstWhere((info) => info.id == WalletBase.idFor(currentName, getType()));
final currentWallet = await SolanaWalletBase.open(
password: password, name: currentName, walletInfo: currentWalletInfo);
password: password,
name: currentName,
walletInfo: currentWalletInfo,
encryptionFileUtils: encryptionFileUtilsFor(isDirect),
);
await currentWallet.renameWalletFiles(newName);
await saveBackup(newName);

View file

@ -1,39 +0,0 @@
import 'dart:io';
import 'package:cw_core/key.dart';
import 'package:encrypt/encrypt.dart' as encrypt;
Future<void> write(
{required String path,
required String password,
required String data}) async {
final keys = extractKeys(password);
final key = encrypt.Key.fromBase64(keys.first);
final iv = encrypt.IV.fromBase64(keys.last);
final encrypted = await encode(key: key, iv: iv, data: data);
final f = File(path);
f.writeAsStringSync(encrypted);
}
Future<void> writeData(
{required String path,
required String password,
required String data}) async {
final keys = extractKeys(password);
final key = encrypt.Key.fromBase64(keys.first);
final iv = encrypt.IV.fromBase64(keys.last);
final encrypted = await encode(key: key, iv: iv, data: data);
final f = File(path);
f.writeAsStringSync(encrypted);
}
Future<String> read({required String path, required String password}) async {
final file = File(path);
if (!file.existsSync()) {
file.createSync();
}
final encrypted = file.readAsStringSync();
return decode(password: password, data: encrypted);
}

View file

@ -3,7 +3,7 @@ import 'dart:core';
import 'dart:developer';
import 'package:cw_core/pathForWallet.dart';
import 'package:cw_core/wallet_info.dart';
import 'package:cw_evm/file.dart';
import 'package:cw_core/encryption_file_utils.dart';
import 'package:cw_tron/tron_transaction_info.dart';
import 'package:mobx/mobx.dart';
import 'package:cw_core/transaction_history.dart';
@ -14,7 +14,8 @@ class TronTransactionHistory = TronTransactionHistoryBase with _$TronTransaction
abstract class TronTransactionHistoryBase extends TransactionHistoryBase<TronTransactionInfo>
with Store {
TronTransactionHistoryBase({required this.walletInfo, required String password})
TronTransactionHistoryBase(
{required this.walletInfo, required String password, required this.encryptionFileUtils})
: _password = password {
transactions = ObservableMap<String, TronTransactionInfo>();
}
@ -22,6 +23,7 @@ abstract class TronTransactionHistoryBase extends TransactionHistoryBase<TronTra
String _password;
final WalletInfo walletInfo;
final EncryptionFileUtils encryptionFileUtils;
Future<void> init() async => await _load();
@ -33,7 +35,7 @@ abstract class TronTransactionHistoryBase extends TransactionHistoryBase<TronTra
String path = '$dirPath/$transactionsHistoryFileNameForWallet';
final transactionMaps = transactions.map((key, value) => MapEntry(key, value.toJson()));
final data = json.encode({'transactions': transactionMaps});
await writeData(path: path, password: _password, data: data);
await encryptionFileUtils.write(path: path, password: _password, data: data);
} catch (e, s) {
log('Error while saving ${walletInfo.type.name} transaction history: ${e.toString()}');
log(s.toString());
@ -51,7 +53,7 @@ abstract class TronTransactionHistoryBase extends TransactionHistoryBase<TronTra
String transactionsHistoryFileNameForWallet = 'tron_transactions.json';
final dirPath = await pathForWalletDir(name: walletInfo.name, type: walletInfo.type);
String path = '$dirPath/$transactionsHistoryFileNameForWallet';
final content = await read(path: path, password: _password);
final content = await encryptionFileUtils.read(path: path, password: _password);
if (content.isEmpty) {
return {};
}

View file

@ -7,6 +7,7 @@ import 'package:bip39/bip39.dart' as bip39;
import 'package:blockchain_utils/blockchain_utils.dart';
import 'package:cw_core/cake_hive.dart';
import 'package:cw_core/crypto_currency.dart';
import 'package:cw_core/encryption_file_utils.dart';
import 'package:cw_core/node.dart';
import 'package:cw_core/pathForWallet.dart';
import 'package:cw_core/pending_transaction.dart';
@ -19,7 +20,6 @@ import 'package:cw_core/wallet_info.dart';
import 'package:cw_core/wallet_keys_file.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:cw_tron/default_tron_tokens.dart';
import 'package:cw_tron/file.dart';
import 'package:cw_tron/tron_abi.dart';
import 'package:cw_tron/tron_balance.dart';
import 'package:cw_tron/tron_client.dart';
@ -46,6 +46,7 @@ abstract class TronWalletBase
String? privateKey,
required String password,
TronBalance? initialBalance,
required this.encryptionFileUtils,
}) : syncStatus = const NotConnectedSyncStatus(),
_password = password,
_mnemonic = mnemonic,
@ -57,7 +58,8 @@ abstract class TronWalletBase
),
super(walletInfo) {
this.walletInfo = walletInfo;
transactionHistory = TronTransactionHistory(walletInfo: walletInfo, password: password);
transactionHistory = TronTransactionHistory(
walletInfo: walletInfo, password: password, encryptionFileUtils: encryptionFileUtils);
if (!CakeHive.isAdapterRegistered(TronToken.typeId)) {
CakeHive.registerAdapter(TronTokenAdapter());
@ -67,6 +69,7 @@ abstract class TronWalletBase
final String? _mnemonic;
final String? _hexPrivateKey;
final String _password;
final EncryptionFileUtils encryptionFileUtils;
late final Box<TronToken> tronTokensBox;
@ -125,13 +128,14 @@ abstract class TronWalletBase
required String name,
required String password,
required WalletInfo walletInfo,
required EncryptionFileUtils encryptionFileUtils,
}) async {
final hasKeysFile = await WalletKeysFile.hasKeysFile(name, walletInfo.type);
final path = await pathForWallet(name: name, type: walletInfo.type);
Map<String, dynamic>? data;
try {
final jsonSource = await read(path: path, password: password);
final jsonSource = await encryptionFileUtils.read(path: path, password: password);
data = json.decode(jsonSource) as Map<String, dynamic>;
} catch (e) {
@ -148,7 +152,12 @@ abstract class TronWalletBase
keysData = WalletKeysData(mnemonic: mnemonic, privateKey: privateKey);
} else {
keysData = await WalletKeysFile.readKeysFile(name, walletInfo.type, password);
keysData = await WalletKeysFile.readKeysFile(
name,
walletInfo.type,
password,
encryptionFileUtils,
);
}
return TronWallet(
@ -157,6 +166,7 @@ abstract class TronWalletBase
mnemonic: keysData.mnemonic,
privateKey: keysData.privateKey,
initialBalance: balance,
encryptionFileUtils: encryptionFileUtils,
);
}
@ -430,13 +440,13 @@ abstract class TronWalletBase
@override
Future<void> save() async {
if (!(await WalletKeysFile.hasKeysFile(walletInfo.name, walletInfo.type))) {
await saveKeysFile(_password);
saveKeysFile(_password, true);
await saveKeysFile(_password, encryptionFileUtils);
saveKeysFile(_password, encryptionFileUtils, true);
}
await walletAddresses.updateAddressesInBox();
final path = await makePath();
await write(path: path, password: _password, data: toJSON());
await encryptionFileUtils.write(path: path, password: _password, data: toJSON());
await transactionHistory.save();
}
@ -584,4 +594,7 @@ abstract class TronWalletBase
_transactionsUpdateTimer?.cancel();
}
}
@override
String get password => _password;
}

View file

@ -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 {

View file

@ -3,6 +3,7 @@ import 'dart:io';
import 'package:bip39/bip39.dart' as bip39;
import 'package:collection/collection.dart';
import 'package:cw_core/balance.dart';
import 'package:cw_core/encryption_file_utils.dart';
import 'package:cw_core/pathForWallet.dart';
import 'package:cw_core/transaction_history.dart';
import 'package:cw_core/transaction_info.dart';
@ -21,11 +22,12 @@ class TronWalletService extends WalletService<
TronRestoreWalletFromSeedCredentials,
TronRestoreWalletFromPrivateKey,
TronNewWalletCredentials> {
TronWalletService(this.walletInfoSource, {required this.client});
TronWalletService(this.walletInfoSource, {required this.client, required this.isDirect});
late TronClient client;
final Box<WalletInfo> walletInfoSource;
final bool isDirect;
@override
WalletType getType() => WalletType.tron;
@ -43,6 +45,7 @@ class TronWalletService extends WalletService<
walletInfo: credentials.walletInfo!,
mnemonic: mnemonic,
password: credentials.password!,
encryptionFileUtils: encryptionFileUtilsFor(isDirect),
);
await wallet.init();
@ -62,6 +65,7 @@ class TronWalletService extends WalletService<
name: name,
password: password,
walletInfo: walletInfo,
encryptionFileUtils: encryptionFileUtilsFor(isDirect),
);
await wallet.init();
@ -75,6 +79,7 @@ class TronWalletService extends WalletService<
name: name,
password: password,
walletInfo: walletInfo,
encryptionFileUtils: encryptionFileUtilsFor(isDirect),
);
await wallet.init();
@ -92,6 +97,7 @@ class TronWalletService extends WalletService<
password: credentials.password!,
privateKey: credentials.privateKey,
walletInfo: credentials.walletInfo!,
encryptionFileUtils: encryptionFileUtilsFor(isDirect),
);
await wallet.init();
@ -114,6 +120,7 @@ class TronWalletService extends WalletService<
password: credentials.password!,
mnemonic: credentials.mnemonic,
walletInfo: credentials.walletInfo!,
encryptionFileUtils: encryptionFileUtilsFor(isDirect),
);
await wallet.init();
@ -128,7 +135,11 @@ class TronWalletService extends WalletService<
final currentWalletInfo = walletInfoSource.values
.firstWhere((info) => info.id == WalletBase.idFor(currentName, getType()));
final currentWallet = await TronWalletBase.open(
password: password, name: currentName, walletInfo: currentWalletInfo);
password: password,
name: currentName,
walletInfo: currentWalletInfo,
encryptionFileUtils: encryptionFileUtilsFor(isDirect),
);
await currentWallet.renameWalletFiles(newName);
await saveBackup(newName);

View file

@ -50,7 +50,7 @@ abstract class WowneroWalletBase
extends WalletBase<WowneroBalance, WowneroTransactionHistory, WowneroTransactionInfo>
with Store {
WowneroWalletBase(
{required WalletInfo walletInfo, required Box<UnspentCoinsInfo> unspentCoinsInfo})
{required WalletInfo walletInfo, required Box<UnspentCoinsInfo> unspentCoinsInfo, required String password})
: balance = ObservableMap<CryptoCurrency, WowneroBalance>.of({
CryptoCurrency.wow: WowneroBalance(
fullBalance: wownero_wallet.getFullBalance(accountIndex: 0),
@ -58,6 +58,7 @@ abstract class WowneroWalletBase
}),
_isTransactionUpdating = false,
_hasSyncAfterStartup = false,
_password = password,
isEnabledAutoGenerateSubaddress = false,
syncStatus = NotConnectedSyncStatus(),
unspentCoins = [],
@ -109,6 +110,10 @@ abstract class WowneroWalletBase
String seedLegacy(String? language) => wownero_wallet.getSeedLegacy(language);
String get password => _password;
String _password;
@override
MoneroWalletKeys get keys => MoneroWalletKeys(
privateSpendKey: wownero_wallet.getSecretSpendKey(),

View file

@ -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;

View file

@ -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:

View file

@ -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<WalletInfo> walletInfoSource, Box<UnspentCoinsInfo> unspentCoinSource, bool alwaysScan) {
return BitcoinWalletService(walletInfoSource, unspentCoinSource, alwaysScan);
Box<WalletInfo> walletInfoSource, Box<UnspentCoinsInfo> unspentCoinSource, bool alwaysScan, bool isDirect) {
return BitcoinWalletService(walletInfoSource, unspentCoinSource, alwaysScan, isDirect);
}
WalletService createLitecoinWalletService(
Box<WalletInfo> walletInfoSource, Box<UnspentCoinsInfo> unspentCoinSource) {
return LitecoinWalletService(walletInfoSource, unspentCoinSource);
Box<WalletInfo> walletInfoSource, Box<UnspentCoinsInfo> unspentCoinSource, bool isDirect) {
return LitecoinWalletService(walletInfoSource, unspentCoinSource, isDirect);
}
@override

View file

@ -6,16 +6,17 @@ class CWBitcoinCash extends BitcoinCash {
@override
WalletService createBitcoinCashWalletService(
Box<WalletInfo> walletInfoSource, Box<UnspentCoinsInfo> unspentCoinSource) {
return BitcoinCashWalletService(walletInfoSource, unspentCoinSource);
Box<WalletInfo> walletInfoSource, Box<UnspentCoinsInfo> unspentCoinSource, bool isDirect) {
return BitcoinCashWalletService(walletInfoSource, unspentCoinSource, isDirect);
}
@override
WalletCredentials createBitcoinCashNewWalletCredentials({
required String name,
WalletInfo? walletInfo,
String? password,
}) =>
BitcoinCashNewWalletCredentials(name: name, walletInfo: walletInfo);
BitcoinCashNewWalletCredentials(name: name, walletInfo: walletInfo, password: password);
@override
WalletCredentials createBitcoinCashRestoreWalletFromSeedCredentials(

View file

@ -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 {

View file

@ -15,7 +15,6 @@ import 'package:cw_core/wallet_type.dart';
class WalletCreationService {
WalletCreationService(
{required WalletType initialType,
required this.secureStorage,
required this.keyService,
required this.sharedPreferences,
required this.settingsStore,
@ -25,7 +24,6 @@ class WalletCreationService {
}
WalletType type;
final SecureStorage secureStorage;
final SharedPreferences sharedPreferences;
final SettingsStore settingsStore;
final KeyService keyService;
@ -56,12 +54,16 @@ class WalletCreationService {
Future<WalletBase> create(WalletCredentials credentials, {bool? isTestnet}) async {
checkIfExists(credentials.name);
final password = generateWalletPassword();
credentials.password = password;
if (credentials.password == null) {
credentials.password = generateWalletPassword();
await keyService.saveWalletPassword(
password: credentials.password!, walletName: credentials.name);
}
if (_hasSeedPhraseLengthOption) {
credentials.seedPhraseLength = settingsStore.seedPhraseLength.value;
}
await keyService.saveWalletPassword(password: password, walletName: credentials.name);
final wallet = await _service!.create(credentials, isTestnet: isTestnet);
if (wallet.type == WalletType.monero) {
@ -94,9 +96,13 @@ class WalletCreationService {
Future<WalletBase> restoreFromKeys(WalletCredentials credentials, {bool? isTestnet}) async {
checkIfExists(credentials.name);
final password = generateWalletPassword();
credentials.password = password;
await keyService.saveWalletPassword(password: password, walletName: credentials.name);
if (credentials.password == null) {
credentials.password = generateWalletPassword();
await keyService.saveWalletPassword(
password: credentials.password!, walletName: credentials.name);
}
final wallet = await _service!.restoreFromKeys(credentials, isTestnet: isTestnet);
if (wallet.type == WalletType.monero) {
@ -109,9 +115,13 @@ class WalletCreationService {
Future<WalletBase> restoreFromSeed(WalletCredentials credentials, {bool? isTestnet}) async {
checkIfExists(credentials.name);
final password = generateWalletPassword();
credentials.password = password;
await keyService.saveWalletPassword(password: password, walletName: credentials.name);
if (credentials.password == null) {
credentials.password = generateWalletPassword();
await keyService.saveWalletPassword(
password: credentials.password!, walletName: credentials.name);
}
final wallet = await _service!.restoreFromSeed(credentials, isTestnet: isTestnet);
if (wallet.type == WalletType.monero) {

View file

@ -20,17 +20,18 @@ class WalletLoadingService {
final KeyService keyService;
final WalletService Function(WalletType type) walletServiceFactory;
Future<void> renameWallet(WalletType type, String name, String newName) async {
Future<void> renameWallet(WalletType type, String name, String newName,
{String? password}) async {
final walletService = walletServiceFactory.call(type);
final password = await keyService.getWalletPassword(walletName: name);
final walletPassword = password ?? (await keyService.getWalletPassword(walletName: name));
// Save the current wallet's password to the new wallet name's key
await keyService.saveWalletPassword(walletName: newName, password: password);
await keyService.saveWalletPassword(walletName: newName, password: walletPassword);
// Delete previous wallet name from keyService to keep only new wallet's name
// otherwise keeps duplicate (old and new names)
await keyService.deleteWalletPassword(walletName: name);
await walletService.rename(name, password, newName);
await walletService.rename(name, walletPassword, newName);
// set shared preferences flag based on previous wallet name
if (type == WalletType.monero) {
@ -41,11 +42,11 @@ class WalletLoadingService {
}
}
Future<WalletBase> load(WalletType type, String name) async {
Future<WalletBase> load(WalletType type, String name, {String? password}) async {
try {
final walletService = walletServiceFactory.call(type);
final password = await keyService.getWalletPassword(walletName: name);
final wallet = await walletService.openWallet(name, password);
final walletPassword = password ?? (await keyService.getWalletPassword(walletName: name));
final wallet = await walletService.openWallet(name, walletPassword);
if (type == WalletType.monero) {
await updateMoneroWalletPassword(wallet);
@ -67,8 +68,8 @@ class WalletLoadingService {
for (var walletInfo in walletInfoSource.values) {
try {
final walletService = walletServiceFactory.call(walletInfo.type);
final password = await keyService.getWalletPassword(walletName: walletInfo.name);
final wallet = await walletService.openWallet(walletInfo.name, password);
final walletPassword = password ?? (await keyService.getWalletPassword(walletName: name));
final wallet = await walletService.openWallet(walletInfo.name, walletPassword);
if (walletInfo.type == WalletType.monero) {
await updateMoneroWalletPassword(wallet);

View file

@ -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<void> setup({
WalletCreationService(
initialType: type,
keyService: getIt.get<KeyService>(),
secureStorage: getIt.get<SecureStorage>(),
sharedPreferences: getIt.get<SharedPreferences>(),
settingsStore: getIt.get<SettingsStore>(),
walletInfoSource: _walletInfoSource));
@ -357,6 +361,65 @@ Future<void> setup({
getIt.get<AdvancedPrivacySettingsViewModel>(param1: type),
type: type));
getIt.registerFactoryParam<WalletUnlockPage, WalletUnlockArguments, bool>((args, closable) {
return WalletUnlockPage(
getIt.get<WalletUnlockLoadableViewModel>(param1: args),
args.callback,
args.authPasswordHandler,
closable: closable);
}, instanceName: 'wallet_unlock_loadable');
getIt.registerFactory<WalletUnlockPage>(
() => getIt.get<WalletUnlockPage>(
param1: WalletUnlockArguments(
callback: (bool successful, _) {
if (successful) {
final authStore = getIt.get<AuthenticationStore>();
authStore.allowed();
}}),
param2: false,
instanceName: 'wallet_unlock_loadable'),
instanceName: 'wallet_password_login');
getIt.registerFactoryParam<WalletUnlockPage, WalletUnlockArguments, bool>((args, closable) {
return WalletUnlockPage(
getIt.get<WalletUnlockVerifiableViewModel>(param1: args),
args.callback,
args.authPasswordHandler,
closable: closable);
}, instanceName: 'wallet_unlock_verifiable');
getIt.registerFactoryParam<WalletUnlockLoadableViewModel, WalletUnlockArguments, void>((args, _) {
final currentWalletName = getIt
.get<SharedPreferences>()
.getString(PreferencesKey.currentWalletName) ?? '';
final currentWalletTypeRaw =
getIt.get<SharedPreferences>()
.getInt(PreferencesKey.currentWalletType) ?? 0;
final currentWalletType = deserializeFromInt(currentWalletTypeRaw);
return WalletUnlockLoadableViewModel(
getIt.get<AppStore>(),
getIt.get<WalletLoadingService>(),
walletName: args.walletName ?? currentWalletName,
walletType: args.walletType ?? currentWalletType);
});
getIt.registerFactoryParam<WalletUnlockVerifiableViewModel, WalletUnlockArguments, void>((args, _) {
final currentWalletName = getIt
.get<SharedPreferences>()
.getString(PreferencesKey.currentWalletName) ?? '';
final currentWalletTypeRaw =
getIt.get<SharedPreferences>()
.getInt(PreferencesKey.currentWalletType) ?? 0;
final currentWalletType = deserializeFromInt(currentWalletTypeRaw);
return WalletUnlockVerifiableViewModel(
getIt.get<AppStore>(),
walletName: args.walletName ?? currentWalletName,
walletType: args.walletType ?? currentWalletType);
});
getIt.registerFactoryParam<WalletRestorationFromQRVM, WalletType, void>((WalletType type, _) {
return WalletRestorationFromQRVM(getIt.get<AppStore>(),
getIt.get<WalletCreationService>(param1: type), _walletInfoSource, type);
@ -907,23 +970,28 @@ Future<void> setup({
_walletInfoSource,
_unspentCoinsInfoSource,
getIt.get<SettingsStore>().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:

View file

@ -6,7 +6,7 @@ import 'package:cake_wallet/entities/preferences_key.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:cake_wallet/core/wallet_loading_service.dart';
Future<void> loadCurrentWallet() async {
Future<void> loadCurrentWallet({String? password}) async {
final appStore = getIt.get<AppStore>();
final name = getIt
.get<SharedPreferences>()
@ -21,7 +21,10 @@ Future<void> loadCurrentWallet() async {
final type = deserializeFromInt(typeRaw);
final walletLoadingService = getIt.get<WalletLoadingService>();
final wallet = await walletLoadingService.load(type, name);
final wallet = await walletLoadingService.load(
type,
name,
password: password);
await appStore.changeCurrentWallet(wallet);
getIt.get<BackgroundTasks>().registerSyncTask();

View file

@ -4,15 +4,16 @@ class CWEthereum extends Ethereum {
@override
List<String> getEthereumWordList(String language) => EVMChainMnemonics.englishWordlist;
WalletService createEthereumWalletService(Box<WalletInfo> walletInfoSource) =>
EthereumWalletService(walletInfoSource, client: EthereumClient());
WalletService createEthereumWalletService(Box<WalletInfo> walletInfoSource, bool isDirect) =>
EthereumWalletService(walletInfoSource, isDirect, client: EthereumClient());
@override
WalletCredentials createEthereumNewWalletCredentials({
required String name,
WalletInfo? walletInfo,
String? password,
}) =>
EVMChainNewWalletCredentials(name: name, walletInfo: walletInfo);
EVMChainNewWalletCredentials(name: name, walletInfo: walletInfo, password: password);
@override
WalletCredentials createEthereumRestoreWalletFromSeedCredentials({

View file

@ -48,6 +48,7 @@ final rootKey = GlobalKey<RootState>();
final RouteObserver<PageRoute<dynamic>> routeObserver = RouteObserver<PageRoute<dynamic>>();
Future<void> main() async {
bool isAppRunning = false;
await runZonedGuarded(() async {
WidgetsFlutterBinding.ensureInitialized();
@ -170,7 +171,6 @@ Future<void> initializeAppConfigs() async {
}
final secureStorage = secureStorageShared;
final transactionDescriptionsBoxKey =
await getEncryptionKey(secureStorage: secureStorage, forKey: TransactionDescription.boxKey);
final tradesBoxKey = await getEncryptionKey(secureStorage: secureStorage, forKey: Trade.boxKey);
@ -247,8 +247,8 @@ Future<void> initialSetup(
ordersSource: ordersSource,
anonpayInvoiceInfoSource: anonpayInvoiceInfo,
unspentCoinsInfoSource: unspentCoinsInfoSource,
secureStorage: secureStorage,
navigatorKey: navigatorKey,
secureStorage: secureStorage,
);
await bootstrap(navigatorKey);
monero?.onStartup();

View file

@ -75,8 +75,8 @@ class CWNano extends Nano {
}
@override
WalletService createNanoWalletService(Box<WalletInfo> walletInfoSource) {
return NanoWalletService(walletInfoSource);
WalletService createNanoWalletService(Box<WalletInfo> walletInfoSource, bool isDirect) {
return NanoWalletService(walletInfoSource, isDirect);
}
@override

View file

@ -4,15 +4,16 @@ class CWPolygon extends Polygon {
@override
List<String> getPolygonWordList(String language) => EVMChainMnemonics.englishWordlist;
WalletService createPolygonWalletService(Box<WalletInfo> walletInfoSource) =>
PolygonWalletService(walletInfoSource, client: PolygonClient());
WalletService createPolygonWalletService(Box<WalletInfo> walletInfoSource, bool isDirect) =>
PolygonWalletService(walletInfoSource, isDirect, client: PolygonClient());
@override
WalletCredentials createPolygonNewWalletCredentials({
required String name,
WalletInfo? walletInfo,
String? password
}) =>
EVMChainNewWalletCredentials(name: name, walletInfo: walletInfo);
EVMChainNewWalletCredentials(name: name, walletInfo: walletInfo, password: password);
@override
WalletCredentials createPolygonRestoreWalletFromSeedCredentials({

View file

@ -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) {

View file

@ -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<dynamic> createRoute(RouteSettings settings) {
return MaterialPageRoute<void>(builder: (_) => createWelcomePage());
case Routes.newWalletFromWelcome:
if (SettingsStoreBase.walletPasswordDirectInput) {
if (availableWalletTypes.length == 1) {
return createRoute(RouteSettings(name: Routes.newWallet, arguments: availableWalletTypes.first));
} else {
return createRoute(RouteSettings(name: Routes.newWalletType));
}
}
return CupertinoPageRoute<void>(
builder: (_) =>
getIt.get<SetupPinCodePage>(param1: (PinCodeState<PinCodeWidget> context, dynamic _) {
@ -176,6 +188,10 @@ Route<dynamic> createRoute(RouteSettings settings) {
param2: [false, false]));
case Routes.restoreOptions:
if (SettingsStoreBase.walletPasswordDirectInput) {
return createRoute(RouteSettings(name: Routes.restoreWalletType));
}
final isNewInstall = settings.arguments as bool;
return CupertinoPageRoute<void>(
fullscreenDialog: true,
@ -328,8 +344,16 @@ Route<dynamic> createRoute(RouteSettings settings) {
case Routes.auth:
return MaterialPageRoute<void>(
fullscreenDialog: true,
builder: (_) => getIt.get<AuthPage>(
param1: settings.arguments as OnAuthenticationFinished, param2: true));
builder: (_)
=> SettingsStoreBase.walletPasswordDirectInput
? getIt.get<WalletUnlockPage>(
param1: WalletUnlockArguments(
callback: settings.arguments as OnAuthenticationFinished),
instanceName: 'wallet_unlock_verifiable',
param2: true)
: getIt.get<AuthPage>(
param1: settings.arguments as OnAuthenticationFinished,
param2: true));
case Routes.totpAuthCodePage:
final args = settings.arguments as TotpAuthArgumentsModel;
@ -340,24 +364,32 @@ Route<dynamic> createRoute(RouteSettings settings) {
),
);
case Routes.login:
return CupertinoPageRoute<void>(
builder: (context) => WillPopScope(
child: getIt.get<AuthPage>(instanceName: 'login'),
onWillPop: () async =>
// FIX-ME: Additional check does it works correctly
(await SystemChannels.platform.invokeMethod<bool>('SystemNavigator.pop') ??
false),
),
fullscreenDialog: true);
case Routes.walletUnlockLoadable:
return MaterialPageRoute<void>(
fullscreenDialog: true,
builder: (_)
=> getIt.get<WalletUnlockPage>(
param1: settings.arguments as WalletUnlockArguments,
instanceName: 'wallet_unlock_loadable',
param2: true));
case Routes.unlock:
return MaterialPageRoute<void>(
fullscreenDialog: true,
builder: (_) => WillPopScope(
child: getIt.get<AuthPage>(
param1: settings.arguments as OnAuthenticationFinished, param2: false),
onWillPop: () async => false));
builder: (_)
=> SettingsStoreBase.walletPasswordDirectInput
? WillPopScope(
child: getIt.get<WalletUnlockPage>(
param1: WalletUnlockArguments(
callback: settings.arguments as OnAuthenticationFinished),
param2: false,
instanceName: 'wallet_unlock_verifiable'),
onWillPop: () async => false)
: WillPopScope(
child: getIt.get<AuthPage>(
param1: settings.arguments as OnAuthenticationFinished,
param2: false),
onWillPop: () async => false));
case Routes.silentPaymentsSettings:
return CupertinoPageRoute<void>(
@ -397,6 +429,17 @@ Route<dynamic> createRoute(RouteSettings settings) {
builder: (_) => getIt.get<NodeCreateOrEditPage>(
param1: args?['editingNode'] as Node?, param2: args?['isSelected'] as bool?));
case Routes.login:
return CupertinoPageRoute<void>(
builder: (context) => WillPopScope(
child: SettingsStoreBase.walletPasswordDirectInput
? getIt.get<WalletUnlockPage>(instanceName: 'wallet_password_login')
: getIt.get<AuthPage>(instanceName: 'login'),
onWillPop: () async =>
// FIX-ME: Additional check does it works correctly
(await SystemChannels.platform.invokeMethod<bool>('SystemNavigator.pop') ?? false)),
fullscreenDialog: true);
case Routes.newPowNode:
final args = settings.arguments as Map<String, dynamic>?;
return CupertinoPageRoute<void>(
@ -486,7 +529,9 @@ Route<dynamic> createRoute(RouteSettings settings) {
fullscreenDialog: true, builder: (_) => getIt.get<RestoreFromBackupPage>());
case Routes.support:
return CupertinoPageRoute<void>(builder: (_) => getIt.get<SupportPage>());
return CupertinoPageRoute<void>(
fullscreenDialog: true,
builder: (_) => getIt.get<SupportPage>());
case Routes.supportLiveChat:
return CupertinoPageRoute<void>(builder: (_) => getIt.get<SupportChatPage>());

View file

@ -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';

View file

@ -4,15 +4,16 @@ class CWSolana extends Solana {
@override
List<String> getSolanaWordList(String language) => SolanaMnemonics.englishWordlist;
WalletService createSolanaWalletService(Box<WalletInfo> walletInfoSource) =>
SolanaWalletService(walletInfoSource);
WalletService createSolanaWalletService(Box<WalletInfo> walletInfoSource, bool isDirect) =>
SolanaWalletService(walletInfoSource, isDirect);
@override
WalletCredentials createSolanaNewWalletCredentials({
required String name,
WalletInfo? walletInfo,
String? password,
}) =>
SolanaNewWalletCredentials(name: name, walletInfo: walletInfo);
SolanaNewWalletCredentials(name: name, walletInfo: walletInfo, password: password);
@override
WalletCredentials createSolanaRestoreWalletFromSeedCredentials({

View file

@ -12,6 +12,12 @@ import 'package:cake_wallet/core/execution_state.dart';
typedef OnAuthenticationFinished = void Function(bool, AuthPageState);
abstract class AuthPageState<T extends StatefulWidget> extends State<T> {
void changeProcessText(String text);
void hideProgressText();
Future<void> close({String? route, dynamic arguments});
}
class AuthPage extends StatefulWidget {
AuthPage(this.authViewModel,
{required this.onAuthenticationFinished,
@ -22,10 +28,10 @@ class AuthPage extends StatefulWidget {
final bool closable;
@override
AuthPageState createState() => AuthPageState();
AuthPageState createState() => AuthPagePinCodeStateImpl();
}
class AuthPageState extends State<AuthPage> {
class AuthPagePinCodeStateImpl extends AuthPageState<AuthPage> {
final _key = GlobalKey<ScaffoldState>();
final _pinCodeKey = GlobalKey<PinCodeState>();
final _backArrowImageDarkTheme =
@ -55,8 +61,6 @@ class AuthPageState extends State<AuthPage> {
}
if (state is FailureState) {
print('X');
print(state.error);
WidgetsBinding.instance.addPostFrameCallback((_) async {
_pinCodeKey.currentState?.clear();
dismissFlushBar(_authBar);
@ -95,17 +99,20 @@ class AuthPageState extends State<AuthPage> {
super.dispose();
}
@override
void changeProcessText(String text) {
dismissFlushBar(_authBar);
_progressBar = createBar<void>(text, duration: null)
..show(_key.currentContext!);
}
@override
void hideProgressText() {
dismissFlushBar(_progressBar);
_progressBar = null;
}
@override
Future<void> close({String? route, dynamic arguments}) async {
if (_key.currentContext == null) {
throw Exception('Key context is null. Should be not happened');

View file

@ -2,6 +2,9 @@ import 'package:auto_size_text/auto_size_text.dart';
import 'package:cake_wallet/themes/extensions/balance_page_theme.dart';
import 'package:cake_wallet/themes/extensions/dashboard_page_theme.dart';
import 'package:cake_wallet/themes/extensions/sync_indicator_theme.dart';
import 'package:cake_wallet/themes/extensions/balance_page_theme.dart';
import 'package:cake_wallet/themes/extensions/dashboard_page_theme.dart';
import 'package:cake_wallet/themes/extensions/sync_indicator_theme.dart';
import 'package:flutter/material.dart';
class DesktopActionButton extends StatelessWidget {
@ -24,45 +27,48 @@ class DesktopActionButton extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.fromLTRB(8, 0, 8, 8),
child: GestureDetector(
onTap: onTap,
child: Container(
padding: EdgeInsets.symmetric(vertical: 25),
width: double.infinity,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(15.0),
color: Theme.of(context).extension<SyncIndicatorTheme>()!.syncedBackgroundColor,
),
child: Center(
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Image.asset(
image,
height: 30,
width: 30,
color: isEnabled
? Theme.of(context).extension<DashboardPageTheme>()!.textColor
: Theme.of(context).extension<BalancePageTheme>()!.labelTextColor,
),
const SizedBox(width: 10),
AutoSizeText(
title,
style: TextStyle(
fontSize: 24,
fontFamily: 'Lato',
fontWeight: FontWeight.bold,
return MouseRegion(
cursor: SystemMouseCursors.click,
child: Padding(
padding: const EdgeInsets.fromLTRB(8, 0, 8, 8),
child: GestureDetector(
onTap: onTap,
child: Container(
padding: EdgeInsets.symmetric(vertical: 25),
width: double.infinity,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(15.0),
color: Theme.of(context).extension<SyncIndicatorTheme>()!.syncedBackgroundColor,
),
child: Center(
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Image.asset(
image,
height: 30,
width: 30,
color: isEnabled
? Theme.of(context).extension<DashboardPageTheme>()!.textColor
: null,
height: 1,
: Theme.of(context).extension<BalancePageTheme>()!.labelTextColor,
),
maxLines: 1,
textAlign: TextAlign.center,
)
],
const SizedBox(width: 10),
AutoSizeText(
title,
style: TextStyle(
fontSize: 24,
fontFamily: 'Lato',
fontWeight: FontWeight.bold,
color: isEnabled
? Theme.of(context).extension<DashboardPageTheme>()!.textColor
: null,
height: 1,
),
maxLines: 1,
textAlign: TextAlign.center,
)
],
),
),
),
),

View file

@ -4,9 +4,12 @@ import 'package:cake_wallet/core/auth_service.dart';
import 'package:cake_wallet/entities/desktop_dropdown_item.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/src/screens/auth/auth_page.dart';
import 'package:cake_wallet/src/screens/dashboard/desktop_widgets/dropdown_item_widget.dart';
import 'package:cake_wallet/src/screens/wallet_unlock/wallet_unlock_arguments.dart';
import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart';
import 'package:cake_wallet/themes/extensions/menu_theme.dart';
import 'package:cake_wallet/store/settings_store.dart';
import 'package:cake_wallet/utils/show_bar.dart';
import 'package:cake_wallet/utils/show_pop_up.dart';
import 'package:cake_wallet/view_model/wallet_list/wallet_list_item.dart';
@ -176,12 +179,25 @@ class _DesktopWalletSelectionDropDownState extends State<DesktopWalletSelectionD
}
Future<void> _loadWallet(WalletListItem wallet) async {
widget._authService.authenticateAction(
context,
onAuthSuccess: (isAuthenticatedSuccessfully) async {
if (!isAuthenticatedSuccessfully) {
return;
}
if (SettingsStoreBase.walletPasswordDirectInput) {
Navigator.of(context).pushNamed(
Routes.walletUnlockLoadable,
arguments: WalletUnlockArguments(
callback: (bool isAuthenticatedSuccessfully, AuthPageState auth) async {
if (isAuthenticatedSuccessfully) {
auth.close();
setState(() {});
}
}, walletName: wallet.name,
walletType: wallet.type));
return;
}
widget._authService.authenticateAction(context,
onAuthSuccess: (isAuthenticatedSuccessfully) async {
if (!isAuthenticatedSuccessfully) {
return;
}
try {
if (context.mounted) {

View file

@ -68,14 +68,19 @@ class _WalletNameFormState extends State<WalletNameForm> {
_WalletNameFormState(this._walletNewVM)
: _formKey = GlobalKey<FormState>(),
_languageSelectorKey = GlobalKey<SeedLanguageSelectorState>(),
_controller = TextEditingController();
_nameController = TextEditingController(),
_passwordController = _walletNewVM.hasWalletPassword ? TextEditingController() : null,
_repeatedPasswordController =
_walletNewVM.hasWalletPassword ? TextEditingController() : null;
static const aspectRatioImage = 1.22;
final GlobalKey<FormState> _formKey;
final GlobalKey<SeedLanguageSelectorState> _languageSelectorKey;
final WalletNewVM _walletNewVM;
final TextEditingController _controller;
final TextEditingController _nameController;
final TextEditingController? _passwordController;
final TextEditingController? _repeatedPasswordController;
ReactionDisposer? _stateReaction;
@override
@ -130,12 +135,11 @@ class _WalletNameFormState extends State<WalletNameForm> {
padding: EdgeInsets.only(top: 24),
child: Form(
key: _formKey,
child: Stack(
alignment: Alignment.centerRight,
child: Column(
children: [
TextFormField(
onChanged: (value) => _walletNewVM.name = value,
controller: _controller,
controller: _nameController,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 20.0,
@ -169,10 +173,10 @@ class _WalletNameFormState extends State<WalletNameForm> {
FocusManager.instance.primaryFocus?.unfocus();
setState(() {
_controller.text = rName;
_nameController.text = rName;
_walletNewVM.name = rName;
_controller.selection = TextSelection.fromPosition(
TextPosition(offset: _controller.text.length));
_nameController.selection = TextSelection.fromPosition(
TextPosition(offset: _nameController.text.length));
});
},
icon: Container(
@ -195,6 +199,80 @@ class _WalletNameFormState extends State<WalletNameForm> {
),
validator: WalletNameValidator(),
),
if (_walletNewVM.hasWalletPassword) ...[
TextFormField(
onChanged: (value) => _walletNewVM.walletPassword = value,
controller: _passwordController,
textAlign: TextAlign.center,
obscureText: true,
style: TextStyle(
fontSize: 20.0,
fontWeight: FontWeight.w600,
color: Theme.of(context).extension<CakeTextTheme>()!.titleColor,
),
decoration: InputDecoration(
hintStyle: TextStyle(
fontSize: 18.0,
fontWeight: FontWeight.w500,
color:
Theme.of(context).extension<NewWalletTheme>()!.hintTextColor,
),
hintText: S.of(context).password,
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Theme.of(context)
.extension<NewWalletTheme>()!
.underlineColor,
width: 1.0,
),
),
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Theme.of(context)
.extension<NewWalletTheme>()!
.underlineColor,
width: 1.0,
),
),
),
),
TextFormField(
onChanged: (value) => _walletNewVM.repeatedWalletPassword = value,
controller: _repeatedPasswordController,
textAlign: TextAlign.center,
obscureText: true,
style: TextStyle(
fontSize: 20.0,
fontWeight: FontWeight.w600,
color: Theme.of(context).extension<CakeTextTheme>()!.titleColor,
),
decoration: InputDecoration(
hintStyle: TextStyle(
fontSize: 18.0,
fontWeight: FontWeight.w500,
color:
Theme.of(context).extension<NewWalletTheme>()!.hintTextColor,
),
hintText: S.of(context).repeat_wallet_password,
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Theme.of(context)
.extension<NewWalletTheme>()!
.underlineColor,
width: 1.0,
),
),
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Theme.of(context)
.extension<NewWalletTheme>()!
.underlineColor,
width: 1.0,
),
),
),
),
],
],
),
),

View file

@ -16,6 +16,9 @@ class WalletRestoreFromKeysFrom extends StatefulWidget {
required this.onPrivateKeyChange,
required this.displayPrivateKeyField,
required this.onHeightOrDateEntered,
required this.displayWalletPassword,
required this.onRepeatedPasswordChange,
this.onPasswordChange,
Key? key,
}) : super(key: key);
@ -23,13 +26,17 @@ class WalletRestoreFromKeysFrom extends StatefulWidget {
final WalletRestoreViewModel walletRestoreViewModel;
final void Function(String)? onPrivateKeyChange;
final bool displayPrivateKeyField;
final bool displayWalletPassword;
final void Function(String)? onPasswordChange;
final void Function(String)? onRepeatedPasswordChange;
@override
WalletRestoreFromKeysFromState createState() => WalletRestoreFromKeysFromState();
WalletRestoreFromKeysFromState createState() =>
WalletRestoreFromKeysFromState(displayWalletPassword: displayWalletPassword);
}
class WalletRestoreFromKeysFromState extends State<WalletRestoreFromKeysFrom> {
WalletRestoreFromKeysFromState()
WalletRestoreFromKeysFromState({required bool displayWalletPassword})
: formKey = GlobalKey<FormState>(),
blockchainHeightKey = GlobalKey<BlockchainHeightState>(),
nameController = TextEditingController(),
@ -37,7 +44,9 @@ class WalletRestoreFromKeysFromState extends State<WalletRestoreFromKeysFrom> {
viewKeyController = TextEditingController(),
spendKeyController = TextEditingController(),
privateKeyController = TextEditingController(),
nameTextEditingController = TextEditingController();
nameTextEditingController = TextEditingController(),
passwordTextEditingController = displayWalletPassword ? TextEditingController() : null,
repeatedPasswordTextEditingController = displayWalletPassword ? TextEditingController() : null;
final GlobalKey<FormState> formKey;
final GlobalKey<BlockchainHeightState> blockchainHeightKey;
@ -47,9 +56,22 @@ class WalletRestoreFromKeysFromState extends State<WalletRestoreFromKeysFrom> {
final TextEditingController spendKeyController;
final TextEditingController nameTextEditingController;
final TextEditingController privateKeyController;
final TextEditingController? passwordTextEditingController;
final TextEditingController? repeatedPasswordTextEditingController;
void Function()? passwordListener;
void Function()? repeatedPasswordListener;
@override
void initState() {
if (passwordTextEditingController != null) {
passwordListener = () => widget.onPasswordChange?.call(passwordTextEditingController!.text);
passwordTextEditingController?.addListener(passwordListener!);
}
if (repeatedPasswordTextEditingController != null) {
repeatedPasswordListener = () => widget.onRepeatedPasswordChange?.call(repeatedPasswordTextEditingController!.text);
repeatedPasswordTextEditingController?.addListener(repeatedPasswordListener!);
}
super.initState();
privateKeyController.addListener(() {
@ -67,6 +89,14 @@ class WalletRestoreFromKeysFromState extends State<WalletRestoreFromKeysFrom> {
viewKeyController.dispose();
privateKeyController.dispose();
spendKeyController.dispose();
passwordTextEditingController?.dispose();
if (passwordListener != null) {
passwordTextEditingController?.removeListener(passwordListener!);
}
if (repeatedPasswordListener != null) {
repeatedPasswordTextEditingController?.removeListener(repeatedPasswordListener!);
}
super.dispose();
}
@ -114,6 +144,19 @@ class WalletRestoreFromKeysFromState extends State<WalletRestoreFromKeysFrom> {
),
],
),
if (widget.displayWalletPassword)
...[Container(
padding: EdgeInsets.only(top: 20.0),
child: BaseTextFormField(
controller: passwordTextEditingController,
hintText: S.of(context).password,
obscureText: true)),
Container(
padding: EdgeInsets.only(top: 20.0),
child: BaseTextFormField(
controller: repeatedPasswordTextEditingController,
hintText: S.of(context).repeat_wallet_password,
obscureText: true))],
Container(height: 20),
_restoreFromKeysFormFields(),
],

View file

@ -22,34 +22,43 @@ class WalletRestoreFromSeedForm extends StatefulWidget {
required this.displayBlockHeightSelector,
required this.displayPassphrase,
required this.type,
required this.displayWalletPassword,
required this.seedTypeViewModel,
this.blockHeightFocusNode,
this.onHeightOrDateEntered,
this.onSeedChange,
this.onLanguageChange})
this.onLanguageChange,
this.onPasswordChange,
this.onRepeatedPasswordChange})
: super(key: key);
final WalletType type;
final bool displayLanguageSelector;
final bool displayBlockHeightSelector;
final bool displayWalletPassword;
final bool displayPassphrase;
final SeedTypeViewModel seedTypeViewModel;
final FocusNode? blockHeightFocusNode;
final Function(bool)? onHeightOrDateEntered;
final void Function(String)? onSeedChange;
final void Function(String)? onLanguageChange;
final void Function(String)? onPasswordChange;
final void Function(String)? onRepeatedPasswordChange;
@override
WalletRestoreFromSeedFormState createState() => WalletRestoreFromSeedFormState('English');
WalletRestoreFromSeedFormState createState() =>
WalletRestoreFromSeedFormState('English', displayWalletPassword: displayWalletPassword);
}
class WalletRestoreFromSeedFormState extends State<WalletRestoreFromSeedForm> {
WalletRestoreFromSeedFormState(this.language)
WalletRestoreFromSeedFormState(this.language, {required bool displayWalletPassword})
: seedWidgetStateKey = GlobalKey<SeedWidgetState>(),
blockchainHeightKey = GlobalKey<BlockchainHeightState>(),
formKey = GlobalKey<FormState>(),
languageController = TextEditingController(),
nameTextEditingController = TextEditingController(),
passwordTextEditingController = displayWalletPassword ? TextEditingController() : null,
repeatedPasswordTextEditingController = displayWalletPassword ? TextEditingController() : null,
passphraseController = TextEditingController(),
seedTypeController = TextEditingController();
@ -57,16 +66,30 @@ class WalletRestoreFromSeedFormState extends State<WalletRestoreFromSeedForm> {
final GlobalKey<BlockchainHeightState> blockchainHeightKey;
final TextEditingController languageController;
final TextEditingController nameTextEditingController;
final TextEditingController? passwordTextEditingController;
final TextEditingController? repeatedPasswordTextEditingController;
final TextEditingController seedTypeController;
final TextEditingController passphraseController;
final GlobalKey<FormState> formKey;
late ReactionDisposer moneroSeedTypeReaction;
String language;
void Function()? passwordListener;
void Function()? repeatedPasswordListener;
@override
void initState() {
_setSeedType(widget.seedTypeViewModel.moneroSeedType);
_setLanguageLabel(language);
if (passwordTextEditingController != null) {
passwordListener = () => widget.onPasswordChange?.call(passwordTextEditingController!.text);
passwordTextEditingController?.addListener(passwordListener!);
}
if (repeatedPasswordTextEditingController != null) {
repeatedPasswordListener = () => widget.onRepeatedPasswordChange?.call(repeatedPasswordTextEditingController!.text);
repeatedPasswordTextEditingController?.addListener(repeatedPasswordListener!);
}
moneroSeedTypeReaction =
reaction((_) => widget.seedTypeViewModel.moneroSeedType, (SeedType item) {
_setSeedType(item);
@ -78,8 +101,16 @@ class WalletRestoreFromSeedFormState extends State<WalletRestoreFromSeedForm> {
@override
void dispose() {
super.dispose();
moneroSeedTypeReaction();
if (passwordListener != null) {
passwordTextEditingController?.removeListener(passwordListener!);
}
if (repeatedPasswordListener != null) {
repeatedPasswordTextEditingController?.removeListener(repeatedPasswordListener!);
}
super.dispose();
}
void onSeedChange(String seed) {
@ -177,6 +208,16 @@ class WalletRestoreFromSeedFormState extends State<WalletRestoreFromSeedForm> {
),
),
),
if (widget.displayWalletPassword)
...[BaseTextFormField(
controller: passwordTextEditingController,
hintText: S.of(context).password,
obscureText: true),
BaseTextFormField(
controller: repeatedPasswordTextEditingController,
hintText: S.of(context).repeat_wallet_password,
obscureText: true)],
if (widget.displayLanguageSelector)
if (!seedTypeController.value.text.contains("14") && widget.displayLanguageSelector)
GestureDetector(
onTap: () async {

View file

@ -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 = '';

Some files were not shown because too many files have changed in this diff Show more