diff --git a/.github/workflows/automated_integration_test.yml b/.github/workflows/automated_integration_test.yml
index 84c680dda..47b08c44d 100644
--- a/.github/workflows/automated_integration_test.yml
+++ b/.github/workflows/automated_integration_test.yml
@@ -55,7 +55,7 @@ jobs:
- name: Flutter action
uses: subosito/flutter-action@v1
with:
- flutter-version: "3.27.4"
+ flutter-version: "3.27.0"
channel: stable
- name: Install package dependencies
@@ -153,8 +153,8 @@ jobs:
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 changeNowCakeWalletApiKey = '${{ secrets.CHANGE_NOW_API_KEY }}';" >> lib/.secrets.g.dart
+ echo "const changeNowMoneroApiKey = '${{ secrets.CHANGE_NOW_API_KEY }}';" >> 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
@@ -168,6 +168,7 @@ jobs:
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 trocadorMoneroApiKey = '${{ 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
@@ -178,7 +179,8 @@ jobs:
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 exolixCakeWalletApiKey = '${{ secrets.EXOLIX_API_KEY }}';" >> lib/.secrets.g.dart
+ echo "const exolixMoneroApiKey = '${{ 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
diff --git a/.github/workflows/no_http_imports.yaml b/.github/workflows/no_http_imports.yaml
new file mode 100644
index 000000000..dad6821ac
--- /dev/null
+++ b/.github/workflows/no_http_imports.yaml
@@ -0,0 +1,21 @@
+name: No http imports
+
+on: [pull_request]
+
+jobs:
+ PR_test_build:
+ runs-on: ubuntu-24.04
+
+ steps:
+ - uses: actions/checkout@v4
+ - name: Check for http package usage
+ if: github.event_name == 'pull_request'
+ run: |
+ GIT_GREP_OUT="$(git grep package:http | (grep .dart: || test $? = 1) | (grep -v proxy_wrapper.dart || test $? = 1) | (grep -v very_insecure_http_do_not_use || test $? = 1) || true)"
+ [[ "x$GIT_GREP_OUT" == "x" ]] && exit 0
+ echo "$GIT_GREP_OUT"
+ echo "There are .dart files which use http imports"
+ echo "Using http package breaks proxy integration"
+ echo "Please use ProxyWrapper.getHttpClient() from package:cw_core/utils/proxy_wrapper.dart"
+ exit 1
+
\ No newline at end of file
diff --git a/.github/workflows/no_print_in_dart.yaml b/.github/workflows/no_print_in_dart.yaml
index 9c3d82bc2..507793bd8 100644
--- a/.github/workflows/no_print_in_dart.yaml
+++ b/.github/workflows/no_print_in_dart.yaml
@@ -15,5 +15,5 @@ jobs:
[[ "x$GIT_GREP_OUT" == "x" ]] && exit 0
echo "$GIT_GREP_OUT"
echo "There are .dart files which use print() statements"
- echo "Please use printV from package: cw_core/utils/print_verbose.dart"
+ echo "Please use printV from package:cw_core/utils/print_verbose.dart"
exit 1
diff --git a/.github/workflows/pr_test_build_android.yml b/.github/workflows/pr_test_build_android.yml
index 8f6139747..f7c226ce4 100644
--- a/.github/workflows/pr_test_build_android.yml
+++ b/.github/workflows/pr_test_build_android.yml
@@ -9,7 +9,7 @@ jobs:
PR_test_build:
runs-on: linux-amd64
container:
- image: ghcr.io/cake-tech/cake_wallet:debian12-flutter3.27.4-go1.24.1
+ image: ghcr.io/cake-tech/cake_wallet:debian12-flutter3.27.0-go1.24.1-ruststablenightly
env:
STORE_PASS: test@cake_wallet
KEY_PASS: test@cake_wallet
@@ -98,8 +98,8 @@ jobs:
else
echo "const backupKeychainSalt = '${{ secrets.BACKUP_KEY_CHAIN_SALT }}';" >> lib/.secrets.g.dart
fi
- 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 changeNowCakeWalletApiKey = '${{ secrets.CHANGE_NOW_API_KEY }}';" >> lib/.secrets.g.dart
+ echo "const changeNowMoneroApiKey = '${{ secrets.CHANGE_NOW_API_KEY }}';" >> 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
@@ -113,6 +113,7 @@ jobs:
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 trocadorMoneroApiKey = '${{ 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
@@ -124,7 +125,8 @@ jobs:
echo "const moralisApiKey = '${{ secrets.MORALIS_API_KEY }}';" >> cw_evm/lib/.secrets.g.dart
echo "const nowNodesApiKey = '${{ secrets.EVM_NOWNODES_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 exolixCakeWalletApiKey = '${{ secrets.EXOLIX_API_KEY }}';" >> lib/.secrets.g.dart
+ echo "const exolixMoneroApiKey = '${{ 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
@@ -253,6 +255,11 @@ jobs:
- name: Build generated code
run: |
+ flutter --version
+ flutter clean
+ rm -rf .dart_tool
+ rm pubspec.lock
+ flutter pub get
./model_generator.sh async
- name: Generate key properties
diff --git a/.github/workflows/pr_test_build_linux.yml b/.github/workflows/pr_test_build_linux.yml
index 476a033a0..f057b19e5 100644
--- a/.github/workflows/pr_test_build_linux.yml
+++ b/.github/workflows/pr_test_build_linux.yml
@@ -9,7 +9,7 @@ jobs:
PR_test_build:
runs-on: linux-amd64
container:
- image: ghcr.io/cake-tech/cake_wallet:debian12-flutter3.27.4-go1.24.1
+ image: ghcr.io/cake-tech/cake_wallet:debian12-flutter3.27.0-go1.24.1-ruststablenightly
env:
STORE_PASS: test@cake_wallet
KEY_PASS: test@cake_wallet
@@ -91,8 +91,8 @@ jobs:
else
echo "const backupKeychainSalt = '${{ secrets.BACKUP_KEY_CHAIN_SALT }}';" >> lib/.secrets.g.dart
fi
- 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 changeNowCakeWalletApiKey = '${{ secrets.CHANGE_NOW_API_KEY }}';" >> lib/.secrets.g.dart
+ echo "const changeNowMoneroApiKey = '${{ secrets.CHANGE_NOW_API_KEY }}';" >> 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
@@ -106,6 +106,7 @@ jobs:
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 trocadorMoneroApiKey = '${{ 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
@@ -117,7 +118,8 @@ jobs:
echo "const moralisApiKey = '${{ secrets.MORALIS_API_KEY }}';" >> cw_evm/lib/.secrets.g.dart
echo "const nowNodesApiKey = '${{ secrets.EVM_NOWNODES_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 exolixCakeWalletApiKey = '${{ secrets.EXOLIX_API_KEY }}';" >> lib/.secrets.g.dart
+ echo "const exolixMoneroApiKey = '${{ 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
diff --git a/Dockerfile b/Dockerfile
index 84179d645..151b7af20 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,4 +1,4 @@
-# docker buildx build --push --pull --platform linux/amd64,linux/arm64 . -f Dockerfile -t ghcr.io/cake-tech/cake_wallet:debian12-flutter3.27.4-go1.24.1
+# docker buildx build --push --pull --platform linux/amd64,linux/arm64 . -f Dockerfile -t ghcr.io/cake-tech/cake_wallet:debian12-flutter3.27.0-go1.24.1-ruststablenightly
# Heavily inspired by cirrusci images
# https://github.com/cirruslabs/docker-images-android/blob/master/sdk/tools/Dockerfile
@@ -15,11 +15,11 @@ LABEL org.opencontainers.image.source=https://github.com/cake-tech/cake_wallet
ENV GOLANG_VERSION=1.24.1
# Pin Flutter version to latest known-working version
-ENV FLUTTER_VERSION=3.27.4
+ENV FLUTTER_VERSION=3.27.0
# Pin Android Studio, platform, and build tools versions to latest known-working version
# Comes from https://developer.android.com/studio/#command-tools
-ENV ANDROID_SDK_TOOLS_VERSION=11076708
+ENV ANDROID_SDK_TOOLS_VERSION=13114758
# Comes from https://developer.android.com/studio/releases/build-tools
ENV ANDROID_PLATFORM_VERSION=35
ENV ANDROID_BUILD_TOOLS_VERSION=34.0.0
@@ -164,9 +164,12 @@ RUN (addgroup kvm || true) && \
ENV PATH=${HOME}/.cargo/bin:${PATH}
RUN curl https://sh.rustup.rs -sSf | bash -s -- -y && \
cargo install cargo-ndk && \
+ for toolchain in stable nightly; \
+ do \
for target in aarch64-linux-android armv7-linux-androideabi i686-linux-android x86_64-linux-android x86_64-unknown-linux-gnu aarch64-unknown-linux-gnu aarch64-unknown-linux-gnu; \
do \
- rustup target add --toolchain stable $target; \
+ rustup target add --toolchain $toolchain $target; \
+ done \
done
# Download and install Flutter
@@ -175,8 +178,11 @@ ENV FLUTTER_HOME=${HOME}/sdks/flutter/${FLUTTER_VERSION}
ENV FLUTTER_ROOT=$FLUTTER_HOME
ENV PATH=${PATH}:${FLUTTER_HOME}/bin:${FLUTTER_HOME}/bin/cache/dart-sdk/bin
-RUN git clone --depth 1 --branch ${FLUTTER_VERSION} https://github.com/flutter/flutter.git ${FLUTTER_HOME} \
- && yes | flutter doctor --android-licenses \
+RUN git clone --branch ${FLUTTER_VERSION} https://github.com/flutter/flutter.git ${FLUTTER_HOME} && \
+ cd ${FLUTTER_HOME} && \
+ git fetch -a
+
+RUN yes | flutter doctor --android-licenses \
&& flutter doctor \
&& chown -R root:root ${FLUTTER_HOME}
diff --git a/android/app/src/main/AndroidManifestBase.xml b/android/app/src/main/AndroidManifestBase.xml
index 280a45b3c..8283a7c8c 100644
--- a/android/app/src/main/AndroidManifestBase.xml
+++ b/android/app/src/main/AndroidManifestBase.xml
@@ -111,6 +111,8 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/assets/images/2fa_warning_light.svg b/assets/images/2fa_warning_light.svg
new file mode 100644
index 000000000..087d8e99b
--- /dev/null
+++ b/assets/images/2fa_warning_light.svg
@@ -0,0 +1,101 @@
+
diff --git a/assets/images/birthday_cake.png b/assets/images/birthday_cake.png
index 84b084fba..293cd10f6 100644
Binary files a/assets/images/birthday_cake.png and b/assets/images/birthday_cake.png differ
diff --git a/assets/images/birthday_cake.svg b/assets/images/birthday_cake.svg
deleted file mode 100644
index b5e31dddb..000000000
--- a/assets/images/birthday_cake.svg
+++ /dev/null
@@ -1,31 +0,0 @@
-
diff --git a/assets/images/btc_lock_dark.png b/assets/images/btc_lock_dark.png
new file mode 100644
index 000000000..f5b3d7e27
Binary files /dev/null and b/assets/images/btc_lock_dark.png differ
diff --git a/assets/images/btc_lock_light.png b/assets/images/btc_lock_light.png
new file mode 100644
index 000000000..4320d96e3
Binary files /dev/null and b/assets/images/btc_lock_light.png differ
diff --git a/assets/images/buy.png b/assets/images/buy.png
index ff4549d5a..32c116e6b 100644
Binary files a/assets/images/buy.png and b/assets/images/buy.png differ
diff --git a/assets/images/cake_logo_dark.svg b/assets/images/cake_logo_dark.svg
new file mode 100644
index 000000000..095077443
--- /dev/null
+++ b/assets/images/cake_logo_dark.svg
@@ -0,0 +1,3 @@
+
diff --git a/assets/images/cake_logo_light.svg b/assets/images/cake_logo_light.svg
new file mode 100644
index 000000000..767e205d1
--- /dev/null
+++ b/assets/images/cake_logo_light.svg
@@ -0,0 +1,3 @@
+
diff --git a/assets/images/contact.png b/assets/images/contact.png
new file mode 100644
index 000000000..5cf96694b
Binary files /dev/null and b/assets/images/contact.png differ
diff --git a/assets/images/hero/cw_welcome_dark.svg b/assets/images/hero/cw_welcome_dark.svg
new file mode 100644
index 000000000..5479cb1ee
--- /dev/null
+++ b/assets/images/hero/cw_welcome_dark.svg
@@ -0,0 +1,5 @@
+
diff --git a/assets/images/hero/cw_welcome_light.svg b/assets/images/hero/cw_welcome_light.svg
new file mode 100644
index 000000000..ece7d1f84
--- /dev/null
+++ b/assets/images/hero/cw_welcome_light.svg
@@ -0,0 +1,5 @@
+
diff --git a/assets/images/history.svg b/assets/images/history.svg
new file mode 100644
index 000000000..f308ab7e3
--- /dev/null
+++ b/assets/images/history.svg
@@ -0,0 +1,10 @@
+
diff --git a/assets/images/home_screen_setting_icon.svg b/assets/images/home_screen_setting_icon.svg
new file mode 100644
index 000000000..7b3aa7b4c
--- /dev/null
+++ b/assets/images/home_screen_setting_icon.svg
@@ -0,0 +1,6 @@
+
diff --git a/assets/images/menu.svg b/assets/images/menu.svg
new file mode 100644
index 000000000..0a4cc3784
--- /dev/null
+++ b/assets/images/menu.svg
@@ -0,0 +1,3 @@
+
diff --git a/assets/images/notif.svg b/assets/images/notif.svg
new file mode 100644
index 000000000..b1ff5b4fa
--- /dev/null
+++ b/assets/images/notif.svg
@@ -0,0 +1,3 @@
+
diff --git a/assets/images/notification_icon.svg b/assets/images/notification_icon.svg
index 099039e67..360d0b4e6 100644
--- a/assets/images/notification_icon.svg
+++ b/assets/images/notification_icon.svg
@@ -1,69 +1,3 @@
-
-
-
-
\ No newline at end of file
+
diff --git a/assets/images/passphrase_dark.png b/assets/images/passphrase_dark.png
new file mode 100644
index 000000000..f72d1e1a1
Binary files /dev/null and b/assets/images/passphrase_dark.png differ
diff --git a/assets/images/passphrase_key.svg b/assets/images/passphrase_key.svg
new file mode 100644
index 000000000..c577dc30a
--- /dev/null
+++ b/assets/images/passphrase_key.svg
@@ -0,0 +1,22 @@
+
diff --git a/assets/images/passphrase_light.png b/assets/images/passphrase_light.png
new file mode 100644
index 000000000..f86f68156
Binary files /dev/null and b/assets/images/passphrase_light.png differ
diff --git a/assets/images/payjoin.png b/assets/images/payjoin.png
new file mode 100644
index 000000000..1ba3dccdb
Binary files /dev/null and b/assets/images/payjoin.png differ
diff --git a/assets/images/qr-cake.png b/assets/images/qr-cake.png
new file mode 100644
index 000000000..7c54dedb0
Binary files /dev/null and b/assets/images/qr-cake.png differ
diff --git a/assets/images/receive.png b/assets/images/receive.png
new file mode 100644
index 000000000..180a4e5b3
Binary files /dev/null and b/assets/images/receive.png differ
diff --git a/assets/images/seed_verified_dark.png b/assets/images/seed_verified_dark.png
new file mode 100644
index 000000000..cbefa5d8c
Binary files /dev/null and b/assets/images/seed_verified_dark.png differ
diff --git a/assets/images/seed_verified_light.png b/assets/images/seed_verified_light.png
new file mode 100644
index 000000000..a51eddcd7
Binary files /dev/null and b/assets/images/seed_verified_light.png differ
diff --git a/assets/images/seed_warning_dark.svg b/assets/images/seed_warning_dark.svg
new file mode 100644
index 000000000..0a47254ff
--- /dev/null
+++ b/assets/images/seed_warning_dark.svg
@@ -0,0 +1,86 @@
+
diff --git a/assets/images/seed_warning_light.svg b/assets/images/seed_warning_light.svg
new file mode 100644
index 000000000..cab27ec31
--- /dev/null
+++ b/assets/images/seed_warning_light.svg
@@ -0,0 +1,111 @@
+
diff --git a/assets/images/send.png b/assets/images/send.png
deleted file mode 100644
index aef504999..000000000
Binary files a/assets/images/send.png and /dev/null differ
diff --git a/assets/images/send2.png b/assets/images/send2.png
new file mode 100644
index 000000000..85fc570a8
Binary files /dev/null and b/assets/images/send2.png differ
diff --git a/assets/images/swap.png b/assets/images/swap.png
new file mode 100644
index 000000000..fe3fc0893
Binary files /dev/null and b/assets/images/swap.png differ
diff --git a/assets/images/tor_logo.svg b/assets/images/tor_logo.svg
new file mode 100644
index 000000000..ebd00324d
--- /dev/null
+++ b/assets/images/tor_logo.svg
@@ -0,0 +1,76 @@
+
+
+
+
diff --git a/assets/images/usdtbsc_icon.png b/assets/images/usdtbsc_icon.png
new file mode 100644
index 000000000..9f2cda237
Binary files /dev/null and b/assets/images/usdtbsc_icon.png differ
diff --git a/assets/images/wallet_group_bright.png b/assets/images/wallet_group_bright.png
deleted file mode 100644
index 263361db6..000000000
Binary files a/assets/images/wallet_group_bright.png and /dev/null differ
diff --git a/assets/images/wallet_group_confirmed_dark.png b/assets/images/wallet_group_confirmed_dark.png
new file mode 100644
index 000000000..a047cb29c
Binary files /dev/null and b/assets/images/wallet_group_confirmed_dark.png differ
diff --git a/assets/images/wallet_group_confirmed_light.png b/assets/images/wallet_group_confirmed_light.png
new file mode 100644
index 000000000..851d32300
Binary files /dev/null and b/assets/images/wallet_group_confirmed_light.png differ
diff --git a/assets/images/wallet_group_dark.png b/assets/images/wallet_group_dark.png
deleted file mode 100644
index 7cd08d2cd..000000000
Binary files a/assets/images/wallet_group_dark.png and /dev/null differ
diff --git a/assets/images/wallet_group_empty_dark.png b/assets/images/wallet_group_empty_dark.png
new file mode 100644
index 000000000..e613d876e
Binary files /dev/null and b/assets/images/wallet_group_empty_dark.png differ
diff --git a/assets/images/wallet_group_empty_light.png b/assets/images/wallet_group_empty_light.png
new file mode 100644
index 000000000..f795648ae
Binary files /dev/null and b/assets/images/wallet_group_empty_light.png differ
diff --git a/assets/images/wallet_group_light.png b/assets/images/wallet_group_light.png
deleted file mode 100644
index 7827971e7..000000000
Binary files a/assets/images/wallet_group_light.png and /dev/null differ
diff --git a/assets/images/wallet_group_options_dark.png b/assets/images/wallet_group_options_dark.png
new file mode 100644
index 000000000..479aac57c
Binary files /dev/null and b/assets/images/wallet_group_options_dark.png differ
diff --git a/assets/images/wallet_group_options_light.png b/assets/images/wallet_group_options_light.png
new file mode 100644
index 000000000..308930520
Binary files /dev/null and b/assets/images/wallet_group_options_light.png differ
diff --git a/assets/images/wallet_name.png b/assets/images/wallet_name.png
deleted file mode 100644
index f586682bd..000000000
Binary files a/assets/images/wallet_name.png and /dev/null differ
diff --git a/assets/images/wallet_name_light.png b/assets/images/wallet_name_light.png
deleted file mode 100644
index 0199c1b30..000000000
Binary files a/assets/images/wallet_name_light.png and /dev/null differ
diff --git a/assets/images/wallet_type.png b/assets/images/wallet_type.png
deleted file mode 100644
index 4e0eba8b5..000000000
Binary files a/assets/images/wallet_type.png and /dev/null differ
diff --git a/assets/images/wallet_type_light.png b/assets/images/wallet_type_light.png
deleted file mode 100644
index e36c0d3aa..000000000
Binary files a/assets/images/wallet_type_light.png and /dev/null differ
diff --git a/assets/images/wallet_type_wallet_dark.png b/assets/images/wallet_type_wallet_dark.png
new file mode 100644
index 000000000..b840f5547
Binary files /dev/null and b/assets/images/wallet_type_wallet_dark.png differ
diff --git a/assets/images/wallet_type_wallet_light.png b/assets/images/wallet_type_wallet_light.png
new file mode 100644
index 000000000..ee759a109
Binary files /dev/null and b/assets/images/wallet_type_wallet_light.png differ
diff --git a/assets/images/wallets.png b/assets/images/wallets.png
new file mode 100644
index 000000000..62ea20039
Binary files /dev/null and b/assets/images/wallets.png differ
diff --git a/assets/images/welcome.png b/assets/images/welcome.png
deleted file mode 100644
index f1132d253..000000000
Binary files a/assets/images/welcome.png and /dev/null differ
diff --git a/assets/images/welcome_dark_theme.svg b/assets/images/welcome_dark_theme.svg
new file mode 100644
index 000000000..8f60c931a
--- /dev/null
+++ b/assets/images/welcome_dark_theme.svg
@@ -0,0 +1,215 @@
+
diff --git a/assets/images/welcome_light.png b/assets/images/welcome_light.png
deleted file mode 100644
index 6feff85d1..000000000
Binary files a/assets/images/welcome_light.png and /dev/null differ
diff --git a/assets/images/welcome_light_theme.svg b/assets/images/welcome_light_theme.svg
new file mode 100644
index 000000000..178b2853d
--- /dev/null
+++ b/assets/images/welcome_light_theme.svg
@@ -0,0 +1,14 @@
+
diff --git a/assets/images/welcome_wallet_dark.png b/assets/images/welcome_wallet_dark.png
new file mode 100644
index 000000000..771a600d3
Binary files /dev/null and b/assets/images/welcome_wallet_dark.png differ
diff --git a/assets/images/welcome_wallet_light.png b/assets/images/welcome_wallet_light.png
new file mode 100644
index 000000000..2a738be0b
Binary files /dev/null and b/assets/images/welcome_wallet_light.png differ
diff --git a/assets/images/wyre-icon.png b/assets/images/wyre-icon.png
deleted file mode 100644
index a2810948e..000000000
Binary files a/assets/images/wyre-icon.png and /dev/null differ
diff --git a/assets/images/wyre.png b/assets/images/wyre.png
deleted file mode 100644
index a16bbdc8b..000000000
Binary files a/assets/images/wyre.png and /dev/null differ
diff --git a/assets/images/yat_crypto.png b/assets/images/yat_crypto.png
deleted file mode 100644
index fbd5d2483..000000000
Binary files a/assets/images/yat_crypto.png and /dev/null differ
diff --git a/assets/text/Monerocom_Release_Notes.txt b/assets/text/Monerocom_Release_Notes.txt
index 852e6ad0d..faf57258a 100644
--- a/assets/text/Monerocom_Release_Notes.txt
+++ b/assets/text/Monerocom_Release_Notes.txt
@@ -1,4 +1,4 @@
-Background sync improvements
-Payment notifications
+Add built-in Tor support (experimental)
+Ledger improvements
UI/UX improvements
Bug fixes
\ No newline at end of file
diff --git a/assets/text/Release_Notes.txt b/assets/text/Release_Notes.txt
index c766c39ff..c49b895e3 100644
--- a/assets/text/Release_Notes.txt
+++ b/assets/text/Release_Notes.txt
@@ -1,5 +1,9 @@
-Background sync improvements
-Payment notifications
-WalletConnect enhancements
+Add built-in Tor support (experimental)
+Add dEuro investments
+Solana fixes/enhancements
+Polygon fixes/enhancements
+WalletConnect improvements
+Ledger improvements
+Payjoin improvements
UI/UX improvements
Bug fixes
\ No newline at end of file
diff --git a/cw_bitcoin/lib/address_from_output.dart b/cw_bitcoin/lib/address_from_output.dart
index 73bc101c4..0d985b237 100644
--- a/cw_bitcoin/lib/address_from_output.dart
+++ b/cw_bitcoin/lib/address_from_output.dart
@@ -2,22 +2,37 @@ import 'package:bitcoin_base/bitcoin_base.dart';
String addressFromOutputScript(Script script, BasedUtxoNetwork network) {
try {
- switch (script.getAddressType()) {
- case P2pkhAddressType.p2pkh:
- return P2pkhAddress.fromScriptPubkey(script: script).toAddress(network);
- case P2shAddressType.p2pkInP2sh:
- return P2shAddress.fromScriptPubkey(script: script).toAddress(network);
- case SegwitAddresType.p2wpkh:
- return P2wpkhAddress.fromScriptPubkey(script: script).toAddress(network);
- case P2shAddressType.p2pkhInP2sh:
- return P2shAddress.fromScriptPubkey(script: script).toAddress(network);
- case SegwitAddresType.p2wsh:
- return P2wshAddress.fromScriptPubkey(script: script).toAddress(network);
- case SegwitAddresType.p2tr:
- return P2trAddress.fromScriptPubkey(script: script).toAddress(network);
- default:
- }
+ return addressFromScript(script, network).toAddress(network);
} catch (_) {}
return '';
}
+
+BitcoinBaseAddress addressFromScript(Script script,
+ [BasedUtxoNetwork network = BitcoinNetwork.mainnet]) {
+ final addressType = script.getAddressType();
+ if (addressType == null) {
+ throw ArgumentError("Invalid script");
+ }
+
+ switch (addressType) {
+ case P2pkhAddressType.p2pkh:
+ return P2pkhAddress.fromScriptPubkey(
+ script: script, network: BitcoinNetwork.mainnet);
+ case P2shAddressType.p2pkhInP2sh:
+ case P2shAddressType.p2pkInP2sh:
+ return P2shAddress.fromScriptPubkey(
+ script: script, network: BitcoinNetwork.mainnet);
+ case SegwitAddresType.p2wpkh:
+ return P2wpkhAddress.fromScriptPubkey(
+ script: script, network: BitcoinNetwork.mainnet);
+ case SegwitAddresType.p2wsh:
+ return P2wshAddress.fromScriptPubkey(
+ script: script, network: BitcoinNetwork.mainnet);
+ case SegwitAddresType.p2tr:
+ return P2trAddress.fromScriptPubkey(
+ script: script, network: BitcoinNetwork.mainnet);
+ }
+
+ throw ArgumentError("Invalid script");
+}
diff --git a/cw_bitcoin/lib/bitcoin_address_record.dart b/cw_bitcoin/lib/bitcoin_address_record.dart
index 7e4b5f58f..1509f913a 100644
--- a/cw_bitcoin/lib/bitcoin_address_record.dart
+++ b/cw_bitcoin/lib/bitcoin_address_record.dart
@@ -1,4 +1,5 @@
import 'dart:convert';
+import 'package:mobx/mobx.dart';
import 'package:bitcoin_base/bitcoin_base.dart';
@@ -16,7 +17,7 @@ abstract class BaseBitcoinAddressRecord {
}) : _txCount = txCount,
_balance = balance,
_name = name,
- _isUsed = isUsed;
+ _isUsed = Observable(isUsed);
@override
bool operator ==(Object o) => o is BaseBitcoinAddressRecord && address == o.address;
@@ -27,7 +28,7 @@ abstract class BaseBitcoinAddressRecord {
int _txCount;
int _balance;
String _name;
- bool _isUsed;
+ final Observable _isUsed;
BasedUtxoNetwork? network;
int get txCount => _txCount;
@@ -40,9 +41,9 @@ abstract class BaseBitcoinAddressRecord {
set balance(int value) => _balance = value;
- bool get isUsed => _isUsed;
+ bool get isUsed => _isUsed.value;
- void setAsUsed() => _isUsed = true;
+ void setAsUsed() => _isUsed.value = true;
void setNewName(String label) => _name = label;
int get hashCode => address.hashCode;
diff --git a/cw_bitcoin/lib/bitcoin_transaction_credentials.dart b/cw_bitcoin/lib/bitcoin_transaction_credentials.dart
index 01e905fb0..7d6894e14 100644
--- a/cw_bitcoin/lib/bitcoin_transaction_credentials.dart
+++ b/cw_bitcoin/lib/bitcoin_transaction_credentials.dart
@@ -3,11 +3,17 @@ import 'package:cw_core/output_info.dart';
import 'package:cw_core/unspent_coin_type.dart';
class BitcoinTransactionCredentials {
- BitcoinTransactionCredentials(this.outputs,
- {required this.priority, this.feeRate, this.coinTypeToSpendFrom = UnspentCoinType.any});
+ BitcoinTransactionCredentials(
+ this.outputs, {
+ required this.priority,
+ this.feeRate,
+ this.coinTypeToSpendFrom = UnspentCoinType.any,
+ this.payjoinUri,
+ });
final List outputs;
final BitcoinTransactionPriority? priority;
final int? feeRate;
final UnspentCoinType coinTypeToSpendFrom;
+ final String? payjoinUri;
}
diff --git a/cw_bitcoin/lib/bitcoin_wallet.dart b/cw_bitcoin/lib/bitcoin_wallet.dart
index 7135d0a7a..9231022f6 100644
--- a/cw_bitcoin/lib/bitcoin_wallet.dart
+++ b/cw_bitcoin/lib/bitcoin_wallet.dart
@@ -3,22 +3,33 @@ import 'dart:convert';
import 'package:bip39/bip39.dart' as bip39;
import 'package:bitcoin_base/bitcoin_base.dart';
import 'package:blockchain_utils/blockchain_utils.dart';
+import 'package:cw_bitcoin/address_from_output.dart';
import 'package:cw_bitcoin/bitcoin_address_record.dart';
import 'package:cw_bitcoin/bitcoin_mnemonic.dart';
-import 'package:cw_bitcoin/psbt_transaction_builder.dart';
-import 'package:cw_core/encryption_file_utils.dart';
-import 'package:cw_bitcoin/electrum_derivations.dart';
+import 'package:cw_bitcoin/bitcoin_transaction_credentials.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/payjoin/manager.dart';
+import 'package:cw_bitcoin/payjoin/storage.dart';
+import 'package:cw_bitcoin/pending_bitcoin_transaction.dart';
+import 'package:cw_bitcoin/psbt/signer.dart';
+import 'package:cw_bitcoin/psbt/transaction_builder.dart';
+import 'package:cw_bitcoin/psbt/v0_deserialize.dart';
+import 'package:cw_bitcoin/psbt/v0_finalizer.dart';
import 'package:cw_core/crypto_currency.dart';
+import 'package:cw_core/encryption_file_utils.dart';
+import 'package:cw_core/payjoin_session.dart';
+import 'package:cw_core/pending_transaction.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';
import 'package:hive/hive.dart';
import 'package:ledger_bitcoin/ledger_bitcoin.dart';
+import 'package:ledger_bitcoin/psbt.dart';
import 'package:ledger_flutter_plus/ledger_flutter_plus.dart';
import 'package:mobx/mobx.dart';
@@ -31,6 +42,7 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
required String password,
required WalletInfo walletInfo,
required Box unspentCoinsInfo,
+ required Box payjoinBox,
required EncryptionFileUtils encryptionFileUtils,
Uint8List? seedBytes,
String? mnemonic,
@@ -71,20 +83,21 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
// String derivationPath = walletInfo.derivationInfo!.derivationPath!;
// String sideDerivationPath = derivationPath.substring(0, derivationPath.length - 1) + "1";
// final hd = bitcoin.HDWallet.fromSeed(seedBytes, network: networkType);
- walletAddresses = BitcoinWalletAddresses(
- walletInfo,
- initialAddresses: initialAddresses,
- initialRegularAddressIndex: initialRegularAddressIndex,
- initialChangeAddressIndex: initialChangeAddressIndex,
- initialSilentAddresses: initialSilentAddresses,
- initialSilentAddressIndex: initialSilentAddressIndex,
- mainHd: hd,
- sideHd: accountHD.childKey(Bip32KeyIndex(1)),
- network: networkParam ?? network,
- masterHd:
- seedBytes != null ? Bip32Slip10Secp256k1.fromSeed(seedBytes) : null,
- isHardwareWallet: walletInfo.isHardwareWallet,
- );
+
+ payjoinManager = PayjoinManager(PayjoinStorage(payjoinBox), this);
+ walletAddresses = BitcoinWalletAddresses(walletInfo,
+ initialAddresses: initialAddresses,
+ initialRegularAddressIndex: initialRegularAddressIndex,
+ initialChangeAddressIndex: initialChangeAddressIndex,
+ initialSilentAddresses: initialSilentAddresses,
+ initialSilentAddressIndex: initialSilentAddressIndex,
+ mainHd: hd,
+ sideHd: accountHD.childKey(Bip32KeyIndex(1)),
+ network: networkParam ?? network,
+ masterHd:
+ seedBytes != null ? Bip32Slip10Secp256k1.fromSeed(seedBytes) : null,
+ isHardwareWallet: walletInfo.isHardwareWallet,
+ payjoinManager: payjoinManager);
autorun((_) {
this.walletAddresses.isEnabledAutoGenerateSubaddress =
@@ -100,6 +113,7 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
required String password,
required WalletInfo walletInfo,
required Box unspentCoinsInfo,
+ required Box payjoinBox,
required EncryptionFileUtils encryptionFileUtils,
String? passphrase,
String? addressPageType,
@@ -122,9 +136,11 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
break;
case DerivationType.electrum:
default:
- seedBytes = await mnemonicToSeedBytes(mnemonic, passphrase: passphrase ?? "");
+ seedBytes =
+ await mnemonicToSeedBytes(mnemonic, passphrase: passphrase ?? "");
break;
}
+
return BitcoinWallet(
mnemonic: mnemonic,
passphrase: passphrase ?? "",
@@ -141,6 +157,7 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
initialChangeAddressIndex: initialChangeAddressIndex,
addressPageType: addressPageType,
networkParam: network,
+ payjoinBox: payjoinBox,
);
}
@@ -148,6 +165,7 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
required String name,
required WalletInfo walletInfo,
required Box unspentCoinsInfo,
+ required Box payjoinBox,
required String password,
required EncryptionFileUtils encryptionFileUtils,
required bool alwaysScan,
@@ -204,7 +222,8 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
if (mnemonic != null) {
switch (walletInfo.derivationInfo!.derivationType) {
case DerivationType.electrum:
- seedBytes = await mnemonicToSeedBytes(mnemonic, passphrase: passphrase ?? "");
+ seedBytes =
+ await mnemonicToSeedBytes(mnemonic, passphrase: passphrase ?? "");
break;
case DerivationType.bip39:
default:
@@ -217,24 +236,24 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
}
return BitcoinWallet(
- mnemonic: mnemonic,
- xpub: keysData.xPub,
- password: password,
- passphrase: passphrase,
- walletInfo: walletInfo,
- unspentCoinsInfo: unspentCoinsInfo,
- initialAddresses: snp?.addresses,
- initialSilentAddresses: snp?.silentAddresses,
- initialSilentAddressIndex: snp?.silentAddressIndex ?? 0,
- initialBalance: snp?.balance,
- encryptionFileUtils: encryptionFileUtils,
- seedBytes: seedBytes,
- initialRegularAddressIndex: snp?.regularAddressIndex,
- initialChangeAddressIndex: snp?.changeAddressIndex,
- addressPageType: snp?.addressPageType,
- networkParam: network,
- alwaysScan: alwaysScan,
- );
+ mnemonic: mnemonic,
+ xpub: keysData.xPub,
+ password: password,
+ passphrase: passphrase,
+ walletInfo: walletInfo,
+ unspentCoinsInfo: unspentCoinsInfo,
+ initialAddresses: snp?.addresses,
+ initialSilentAddresses: snp?.silentAddresses,
+ initialSilentAddressIndex: snp?.silentAddressIndex ?? 0,
+ initialBalance: snp?.balance,
+ encryptionFileUtils: encryptionFileUtils,
+ seedBytes: seedBytes,
+ initialRegularAddressIndex: snp?.regularAddressIndex,
+ initialChangeAddressIndex: snp?.changeAddressIndex,
+ addressPageType: snp?.addressPageType,
+ networkParam: network,
+ alwaysScan: alwaysScan,
+ payjoinBox: payjoinBox);
}
LedgerConnection? _ledgerConnection;
@@ -248,19 +267,30 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
}
@override
- Future buildHardwareWalletTransaction({
+ Future close({bool shouldCleanup = false}) async {
+ payjoinManager.cleanupSessions();
+ super.close(shouldCleanup: shouldCleanup);
+ }
+
+ late final PayjoinManager payjoinManager;
+
+ bool get isPayjoinAvailable => unspentCoinsInfo.values
+ .where((element) =>
+ element.walletId == id && element.isSending && !element.isFrozen)
+ .isNotEmpty;
+
+ Future buildPsbt({
required List outputs,
required BigInt fee,
required BasedUtxoNetwork network,
required List utxos,
required Map publicKeys,
+ required Uint8List masterFingerprint,
String? memo,
bool enableRBF = false,
BitcoinOrdering inputOrdering = BitcoinOrdering.bip69,
BitcoinOrdering outputOrdering = BitcoinOrdering.bip69,
}) async {
- final masterFingerprint = await _bitcoinLedgerApp!.getMasterFingerprint();
-
final psbtReadyInputs = [];
for (final utxo in utxos) {
final rawTx =
@@ -278,13 +308,128 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
));
}
- final psbt = PSBTTransactionBuild(
- inputs: psbtReadyInputs, outputs: outputs, enableRBF: enableRBF);
+ return PSBTTransactionBuild(
+ inputs: psbtReadyInputs, outputs: outputs, enableRBF: enableRBF)
+ .psbt;
+ }
- final rawHex = await _bitcoinLedgerApp!.signPsbt(psbt: psbt.psbt);
+ @override
+ Future buildHardwareWalletTransaction({
+ required List outputs,
+ required BigInt fee,
+ required BasedUtxoNetwork network,
+ required List utxos,
+ required Map publicKeys,
+ String? memo,
+ bool enableRBF = false,
+ BitcoinOrdering inputOrdering = BitcoinOrdering.bip69,
+ BitcoinOrdering outputOrdering = BitcoinOrdering.bip69,
+ }) async {
+ final masterFingerprint = await _bitcoinLedgerApp!.getMasterFingerprint();
+
+ final psbt = await buildPsbt(
+ outputs: outputs,
+ fee: fee,
+ network: network,
+ utxos: utxos,
+ publicKeys: publicKeys,
+ masterFingerprint: masterFingerprint,
+ memo: memo,
+ enableRBF: enableRBF,
+ inputOrdering: inputOrdering,
+ outputOrdering: outputOrdering,
+ );
+
+ final rawHex = await _bitcoinLedgerApp!.signPsbt(psbt: psbt);
return BtcTransaction.fromRaw(BytesUtils.toHexString(rawHex));
}
+ @override
+ Future createTransaction(Object credentials) async {
+ credentials = credentials as BitcoinTransactionCredentials;
+
+ final tx = (await super.createTransaction(credentials))
+ as PendingBitcoinTransaction;
+
+ final payjoinUri = credentials.payjoinUri;
+ if (payjoinUri == null) return tx;
+
+ final transaction = await buildPsbt(
+ utxos: tx.utxos,
+ outputs: tx.outputs
+ .map((e) => BitcoinOutput(
+ address: addressFromScript(e.scriptPubKey),
+ value: e.amount,
+ isSilentPayment: e.isSilentPayment,
+ isChange: e.isChange,
+ ))
+ .toList(),
+ fee: BigInt.from(tx.fee),
+ network: network,
+ memo: credentials.outputs.first.memo,
+ outputOrdering: BitcoinOrdering.none,
+ enableRBF: true,
+ publicKeys: tx.publicKeys!,
+ masterFingerprint: Uint8List(0));
+
+ final originalPsbt = await signPsbt(
+ base64.encode(transaction.asPsbtV0()), getUtxoWithPrivateKeys());
+
+ tx.commitOverride = () async {
+ final sender = await payjoinManager.initSender(
+ payjoinUri, originalPsbt, int.parse(tx.feeRate));
+ payjoinManager.spawnNewSender(
+ sender: sender, pjUrl: payjoinUri, amount: BigInt.from(tx.amount));
+ };
+
+ return tx;
+ }
+
+ List getUtxoWithPrivateKeys() => unspentCoins
+ .where((e) => (e.isSending && !e.isFrozen))
+ .map((unspent) => UtxoWithPrivateKey.fromUnspent(unspent, this))
+ .toList();
+
+ Future commitPsbt(String finalizedPsbt) {
+ final psbt = PsbtV2()..deserializeV0(base64.decode(finalizedPsbt));
+
+ final btcTx =
+ BtcTransaction.fromRaw(BytesUtils.toHexString(psbt.extract()));
+
+ return PendingBitcoinTransaction(
+ btcTx,
+ type,
+ electrumClient: electrumClient,
+ amount: 0,
+ fee: 0,
+ feeRate: "",
+ network: network,
+ hasChange: true,
+ ).commit();
+ }
+
+ Future signPsbt(
+ String preProcessedPsbt, List utxos) async {
+ final psbt = PsbtV2()..deserializeV0(base64Decode(preProcessedPsbt));
+
+ await psbt.signWithUTXO(utxos, (txDigest, utxo, key, sighash) {
+ return utxo.utxo.isP2tr()
+ ? key.signTapRoot(
+ txDigest,
+ sighash: sighash,
+ tweak: utxo.utxo.isSilentPayment != true,
+ )
+ : key.signInput(txDigest, sigHash: sighash);
+ }, (txId, vout) async {
+ final txHex = await electrumClient.getTransactionHex(hash: txId);
+ final output = BtcTransaction.fromRaw(txHex).outputs[vout];
+ return TaprootAmountScriptPair(output.amount, output.scriptPubKey);
+ });
+
+ psbt.finalizeV0();
+ return base64Encode(psbt.asPsbtV0());
+ }
+
@override
Future signMessage(String message, {String? address = null}) async {
if (walletInfo.isHardwareWallet) {
diff --git a/cw_bitcoin/lib/bitcoin_wallet_addresses.dart b/cw_bitcoin/lib/bitcoin_wallet_addresses.dart
index 1a122ef9e..d84d958be 100644
--- a/cw_bitcoin/lib/bitcoin_wallet_addresses.dart
+++ b/cw_bitcoin/lib/bitcoin_wallet_addresses.dart
@@ -1,10 +1,13 @@
import 'package:bitcoin_base/bitcoin_base.dart';
import 'package:blockchain_utils/bip/bip/bip32/bip32.dart';
import 'package:cw_bitcoin/electrum_wallet_addresses.dart';
+import 'package:cw_bitcoin/payjoin/manager.dart';
import 'package:cw_bitcoin/utils.dart';
import 'package:cw_core/unspent_coin_type.dart';
+import 'package:cw_core/utils/print_verbose.dart';
import 'package:cw_core/wallet_info.dart';
import 'package:mobx/mobx.dart';
+import 'package:payjoin_flutter/receive.dart' as payjoin;
part 'bitcoin_wallet_addresses.g.dart';
@@ -17,6 +20,7 @@ abstract class BitcoinWalletAddressesBase extends ElectrumWalletAddresses with S
required super.sideHd,
required super.network,
required super.isHardwareWallet,
+ required this.payjoinManager,
super.initialAddresses,
super.initialRegularAddressIndex,
super.initialChangeAddressIndex,
@@ -25,6 +29,13 @@ abstract class BitcoinWalletAddressesBase extends ElectrumWalletAddresses with S
super.masterHd,
}) : super(walletInfo);
+ final PayjoinManager payjoinManager;
+
+ payjoin.Receiver? currentPayjoinReceiver;
+
+ @observable
+ String? payjoinEndpoint = null;
+
@override
String getAddress(
{required int index,
@@ -45,4 +56,33 @@ abstract class BitcoinWalletAddressesBase extends ElectrumWalletAddresses with S
return generateP2WPKHAddress(hd: hd, index: index, network: network);
}
+
+ @action
+ Future initPayjoin() async {
+ try {
+ await payjoinManager.initPayjoin();
+ currentPayjoinReceiver = await payjoinManager.getUnusedReceiver(primaryAddress);
+ payjoinEndpoint = (await currentPayjoinReceiver?.pjUri())?.pjEndpoint();
+
+ payjoinManager.resumeSessions();
+ } catch (e) {
+ printV(e);
+ // Ignore Connectivity errors
+ if (!e.toString().contains("error sending request for url")) rethrow;
+ }
+ }
+
+ @action
+ Future newPayjoinReceiver() async {
+ try {
+ currentPayjoinReceiver = await payjoinManager.getUnusedReceiver(primaryAddress);
+ payjoinEndpoint = (await currentPayjoinReceiver?.pjUri())?.pjEndpoint();
+
+ payjoinManager.spawnReceiver(receiver: currentPayjoinReceiver!);
+ } catch (e) {
+ printV(e);
+ // Ignore Connectivity errors
+ if (!e.toString().contains("error sending request for url")) rethrow;
+ }
+ }
}
diff --git a/cw_bitcoin/lib/bitcoin_wallet_service.dart b/cw_bitcoin/lib/bitcoin_wallet_service.dart
index 7ee1534bf..317b25bcd 100644
--- a/cw_bitcoin/lib/bitcoin_wallet_service.dart
+++ b/cw_bitcoin/lib/bitcoin_wallet_service.dart
@@ -5,6 +5,7 @@ import 'package:cw_bitcoin/bitcoin_mnemonics_bip39.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/payjoin_session.dart';
import 'package:cw_core/unspent_coins_info.dart';
import 'package:cw_core/wallet_base.dart';
import 'package:cw_core/wallet_service.dart';
@@ -21,10 +22,12 @@ class BitcoinWalletService extends WalletService<
BitcoinRestoreWalletFromSeedCredentials,
BitcoinRestoreWalletFromWIFCredentials,
BitcoinRestoreWalletFromHardware> {
- BitcoinWalletService(this.walletInfoSource, this.unspentCoinsInfoSource, this.alwaysScan, this.isDirect);
+ BitcoinWalletService(this.walletInfoSource, this.unspentCoinsInfoSource,
+ this.payjoinSessionSource, this.alwaysScan, this.isDirect);
final Box walletInfoSource;
final Box unspentCoinsInfoSource;
+ final Box payjoinSessionSource;
final bool alwaysScan;
final bool isDirect;
@@ -55,6 +58,7 @@ class BitcoinWalletService extends WalletService<
passphrase: credentials.passphrase,
walletInfo: credentials.walletInfo!,
unspentCoinsInfo: unspentCoinsInfoSource,
+ payjoinBox: payjoinSessionSource,
network: network,
encryptionFileUtils: encryptionFileUtilsFor(isDirect),
);
@@ -79,6 +83,7 @@ class BitcoinWalletService extends WalletService<
name: name,
walletInfo: walletInfo,
unspentCoinsInfo: unspentCoinsInfoSource,
+ payjoinBox: payjoinSessionSource,
alwaysScan: alwaysScan,
encryptionFileUtils: encryptionFileUtilsFor(isDirect),
);
@@ -92,6 +97,7 @@ class BitcoinWalletService extends WalletService<
name: name,
walletInfo: walletInfo,
unspentCoinsInfo: unspentCoinsInfoSource,
+ payjoinBox: payjoinSessionSource,
alwaysScan: alwaysScan,
encryptionFileUtils: encryptionFileUtilsFor(isDirect),
);
@@ -126,6 +132,7 @@ class BitcoinWalletService extends WalletService<
name: currentName,
walletInfo: currentWalletInfo,
unspentCoinsInfo: unspentCoinsInfoSource,
+ payjoinBox: payjoinSessionSource,
alwaysScan: alwaysScan,
encryptionFileUtils: encryptionFileUtilsFor(isDirect),
);
@@ -147,7 +154,6 @@ class BitcoinWalletService extends WalletService<
credentials.walletInfo?.network = network.value;
credentials.walletInfo?.derivationInfo?.derivationPath =
credentials.hwAccountData.derivationPath;
-
final wallet = await BitcoinWallet(
password: credentials.password!,
xpub: credentials.hwAccountData.xpub,
@@ -155,6 +161,7 @@ class BitcoinWalletService extends WalletService<
unspentCoinsInfo: unspentCoinsInfoSource,
networkParam: network,
encryptionFileUtils: encryptionFileUtilsFor(isDirect),
+ payjoinBox: payjoinSessionSource,
);
await wallet.save();
await wallet.init();
@@ -182,6 +189,7 @@ class BitcoinWalletService extends WalletService<
mnemonic: credentials.mnemonic,
walletInfo: credentials.walletInfo!,
unspentCoinsInfo: unspentCoinsInfoSource,
+ payjoinBox: payjoinSessionSource,
network: network,
encryptionFileUtils: encryptionFileUtilsFor(isDirect),
);
diff --git a/cw_bitcoin/lib/electrum.dart b/cw_bitcoin/lib/electrum.dart
index 1f5c369e3..2ddd30df6 100644
--- a/cw_bitcoin/lib/electrum.dart
+++ b/cw_bitcoin/lib/electrum.dart
@@ -5,6 +5,8 @@ import 'dart:typed_data';
import 'package:bitcoin_base/bitcoin_base.dart';
import 'package:cw_bitcoin/bitcoin_amount_format.dart';
import 'package:cw_core/utils/print_verbose.dart';
+import 'package:cw_core/utils/proxy_socket/abstract.dart';
+import 'package:cw_core/utils/proxy_wrapper.dart';
import 'package:flutter/foundation.dart';
import 'package:rxdart/rxdart.dart';
@@ -42,7 +44,7 @@ class ElectrumClient {
static const aliveTimerDuration = Duration(seconds: 4);
bool get isConnected => _isConnected;
- Socket? socket;
+ ProxySocket? socket;
void Function(ConnectionStatus)? onConnectionStatusChange;
int _id;
final Map _tasks;
@@ -72,18 +74,11 @@ class ElectrumClient {
} catch (_) {}
socket = null;
+ final ssl = !(useSSL == false || (useSSL == null && uri.toString().contains("btc-electrum")));
try {
- if (useSSL == false || (useSSL == null && uri.toString().contains("btc-electrum"))) {
- socket = await Socket.connect(host, port, timeout: connectionTimeout);
- } else {
- socket = await SecureSocket.connect(
- host,
- port,
- timeout: connectionTimeout,
- onBadCertificate: (_) => true,
- );
- }
+ socket = await ProxyWrapper().getSocksSocket(ssl, host, port, connectionTimeout: connectionTimeout);
} catch (e) {
+ printV("connect: $e");
if (e is HandshakeException) {
useSSL = !(useSSL ?? false);
}
@@ -105,7 +100,6 @@ class ElectrumClient {
// use ping to determine actual connection status since we could've just not timed out yet:
// _setConnectionStatus(ConnectionStatus.connected);
-
socket!.listen(
(Uint8List event) {
try {
diff --git a/cw_bitcoin/lib/electrum_wallet.dart b/cw_bitcoin/lib/electrum_wallet.dart
index 14fd1d3a9..bb9cea1bc 100644
--- a/cw_bitcoin/lib/electrum_wallet.dart
+++ b/cw_bitcoin/lib/electrum_wallet.dart
@@ -4,6 +4,7 @@ import 'dart:io';
import 'dart:isolate';
import 'package:bitcoin_base/bitcoin_base.dart';
+import 'package:cw_core/utils/proxy_wrapper.dart';
import 'package:cw_bitcoin/bitcoin_amount_format.dart';
import 'package:cw_core/format_amount.dart';
import 'package:cw_core/utils/print_verbose.dart';
@@ -49,7 +50,6 @@ import 'package:mobx/mobx.dart';
import 'package:rxdart/subjects.dart';
import 'package:sp_scanner/sp_scanner.dart';
import 'package:hex/hex.dart';
-import 'package:http/http.dart' as http;
part 'electrum_wallet.g.dart';
@@ -493,10 +493,9 @@ abstract class ElectrumWalletBase
Future updateFeeRates() async {
if (await checkIfMempoolAPIIsEnabled() && type == WalletType.bitcoin) {
try {
- final response = await http
- .get(Uri.parse("https://mempool.cakewallet.com/api/v1/fees/recommended"))
- .timeout(Duration(seconds: 5));
-
+ final response = await ProxyWrapper()
+ .get(clearnetUri: Uri.parse("https://mempool.cakewallet.com/api/v1/fees/recommended"))
+ .timeout(Duration(seconds: 15));
final result = json.decode(response.body) as Map;
final slowFee = (result['economyFee'] as num?)?.toInt() ?? 0;
int mediumFee = (result['hourFee'] as num?)?.toInt() ?? 0;
@@ -1176,19 +1175,18 @@ abstract class ElectrumWalletBase
}
});
- return PendingBitcoinTransaction(
- transaction,
- type,
- electrumClient: electrumClient,
- amount: estimatedTx.amount,
- fee: estimatedTx.fee,
- feeRate: feeRateInt.toString(),
- network: network,
- hasChange: estimatedTx.hasChange,
- isSendAll: estimatedTx.isSendAll,
- hasTaprootInputs: hasTaprootInputs,
- utxos: estimatedTx.utxos,
- )..addListener((transaction) async {
+ return PendingBitcoinTransaction(transaction, type,
+ electrumClient: electrumClient,
+ amount: estimatedTx.amount,
+ fee: estimatedTx.fee,
+ feeRate: feeRateInt.toString(),
+ network: network,
+ hasChange: estimatedTx.hasChange,
+ isSendAll: estimatedTx.isSendAll,
+ hasTaprootInputs: hasTaprootInputs,
+ utxos: estimatedTx.utxos,
+ publicKeys: estimatedTx.publicKeys)
+ ..addListener((transaction) async {
transactionHistory.addOne(transaction);
if (estimatedTx.spendsSilentPayment) {
transactionHistory.transactions.values.forEach((tx) {
@@ -1879,20 +1877,17 @@ abstract class ElectrumWalletBase
if (height != null && height > 0 && await checkIfMempoolAPIIsEnabled()) {
try {
- final blockHash = await http.get(
- Uri.parse(
- "https://mempool.cakewallet.com/api/v1/block-height/$height",
- ),
- );
+ final blockHash = await ProxyWrapper()
+ .get(clearnetUri: Uri.parse("https://mempool.cakewallet.com/api/v1/block-height/$height"))
+ .timeout(Duration(seconds: 15));
if (blockHash.statusCode == 200 &&
blockHash.body.isNotEmpty &&
jsonDecode(blockHash.body) != null) {
- final blockResponse = await http.get(
- Uri.parse(
- "https://mempool.cakewallet.com/api/v1/block/${blockHash.body}",
- ),
- );
+ final blockResponse = await ProxyWrapper()
+ .get(clearnetUri: Uri.parse("https://mempool.cakewallet.com/api/v1/block/${blockHash}"))
+ .timeout(Duration(seconds: 15));
+
if (blockResponse.statusCode == 200 &&
blockResponse.body.isNotEmpty &&
jsonDecode(blockResponse.body)['timestamp'] != null) {
@@ -1965,6 +1960,11 @@ abstract class ElectrumWalletBase
}
}
+ bool isMine(Script script) {
+ final derivedAddress = addressFromOutputScript(script, network);
+ return addressesSet.contains(derivedAddress);
+ }
+
@override
Future