diff --git a/.github/workflows/automated_integration_test.yml b/.github/workflows/automated_integration_test.yml
index 47b08c44d..84c680dda 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.0"
+ flutter-version: "3.27.4"
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 changeNowCakeWalletApiKey = '${{ secrets.CHANGE_NOW_API_KEY }}';" >> lib/.secrets.g.dart
- echo "const changeNowMoneroApiKey = '${{ secrets.CHANGE_NOW_API_KEY }}';" >> 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
@@ -168,7 +168,6 @@ 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
@@ -179,8 +178,7 @@ 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 exolixCakeWalletApiKey = '${{ secrets.EXOLIX_API_KEY }}';" >> lib/.secrets.g.dart
- echo "const exolixMoneroApiKey = '${{ secrets.EXOLIX_API_KEY }}';" >> 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
diff --git a/.github/workflows/no_http_imports.yaml b/.github/workflows/no_http_imports.yaml
deleted file mode 100644
index dad6821ac..000000000
--- a/.github/workflows/no_http_imports.yaml
+++ /dev/null
@@ -1,21 +0,0 @@
-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 507793bd8..9c3d82bc2 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 f7c226ce4..8f6139747 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.0-go1.24.1-ruststablenightly
+ image: ghcr.io/cake-tech/cake_wallet:debian12-flutter3.27.4-go1.24.1
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 changeNowCakeWalletApiKey = '${{ secrets.CHANGE_NOW_API_KEY }}';" >> lib/.secrets.g.dart
- echo "const changeNowMoneroApiKey = '${{ secrets.CHANGE_NOW_API_KEY }}';" >> 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
@@ -113,7 +113,6 @@ 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
@@ -125,8 +124,7 @@ 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 exolixCakeWalletApiKey = '${{ secrets.EXOLIX_API_KEY }}';" >> lib/.secrets.g.dart
- echo "const exolixMoneroApiKey = '${{ secrets.EXOLIX_API_KEY }}';" >> 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
@@ -255,11 +253,6 @@ 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 f057b19e5..476a033a0 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.0-go1.24.1-ruststablenightly
+ image: ghcr.io/cake-tech/cake_wallet:debian12-flutter3.27.4-go1.24.1
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 changeNowCakeWalletApiKey = '${{ secrets.CHANGE_NOW_API_KEY }}';" >> lib/.secrets.g.dart
- echo "const changeNowMoneroApiKey = '${{ secrets.CHANGE_NOW_API_KEY }}';" >> 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
@@ -106,7 +106,6 @@ 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
@@ -118,8 +117,7 @@ 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 exolixCakeWalletApiKey = '${{ secrets.EXOLIX_API_KEY }}';" >> lib/.secrets.g.dart
- echo "const exolixMoneroApiKey = '${{ secrets.EXOLIX_API_KEY }}';" >> 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
diff --git a/Dockerfile b/Dockerfile
index 151b7af20..84179d645 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.0-go1.24.1-ruststablenightly
+# 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
# 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.0
+ENV FLUTTER_VERSION=3.27.4
# 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=13114758
+ENV ANDROID_SDK_TOOLS_VERSION=11076708
# Comes from https://developer.android.com/studio/releases/build-tools
ENV ANDROID_PLATFORM_VERSION=35
ENV ANDROID_BUILD_TOOLS_VERSION=34.0.0
@@ -164,12 +164,9 @@ 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 $toolchain $target; \
- done \
+ rustup target add --toolchain stable $target; \
done
# Download and install Flutter
@@ -178,11 +175,8 @@ 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 --branch ${FLUTTER_VERSION} https://github.com/flutter/flutter.git ${FLUTTER_HOME} && \
- cd ${FLUTTER_HOME} && \
- git fetch -a
-
-RUN yes | flutter doctor --android-licenses \
+RUN git clone --depth 1 --branch ${FLUTTER_VERSION} https://github.com/flutter/flutter.git ${FLUTTER_HOME} \
+ && yes | flutter doctor --android-licenses \
&& flutter doctor \
&& chown -R root:root ${FLUTTER_HOME}
diff --git a/assets/images/history.svg b/assets/images/history.svg
deleted file mode 100644
index f308ab7e3..000000000
--- a/assets/images/history.svg
+++ /dev/null
@@ -1,10 +0,0 @@
-
diff --git a/assets/images/notif.svg b/assets/images/notif.svg
deleted file mode 100644
index b1ff5b4fa..000000000
--- a/assets/images/notif.svg
+++ /dev/null
@@ -1,3 +0,0 @@
-
diff --git a/assets/images/qr-cake.png b/assets/images/qr-cake.png
deleted file mode 100644
index 7c54dedb0..000000000
Binary files a/assets/images/qr-cake.png and /dev/null differ
diff --git a/assets/images/tor_logo.svg b/assets/images/tor_logo.svg
deleted file mode 100644
index ebd00324d..000000000
--- a/assets/images/tor_logo.svg
+++ /dev/null
@@ -1,76 +0,0 @@
-
-
-
-
diff --git a/assets/images/usdtbsc_icon.png b/assets/images/usdtbsc_icon.png
deleted file mode 100644
index 9f2cda237..000000000
Binary files a/assets/images/usdtbsc_icon.png and /dev/null differ
diff --git a/assets/text/Monerocom_Release_Notes.txt b/assets/text/Monerocom_Release_Notes.txt
index faf57258a..1176d3d8c 100644
--- a/assets/text/Monerocom_Release_Notes.txt
+++ b/assets/text/Monerocom_Release_Notes.txt
@@ -1,4 +1,3 @@
-Add built-in Tor support (experimental)
-Ledger improvements
-UI/UX improvements
+New themes and UI/UX improvements
+Ledger flow enhancements
Bug fixes
\ No newline at end of file
diff --git a/assets/text/Release_Notes.txt b/assets/text/Release_Notes.txt
index c49b895e3..b68800bd9 100644
--- a/assets/text/Release_Notes.txt
+++ b/assets/text/Release_Notes.txt
@@ -1,9 +1,5 @@
-Add built-in Tor support (experimental)
-Add dEuro investments
-Solana fixes/enhancements
-Polygon fixes/enhancements
-WalletConnect improvements
-Ledger improvements
-Payjoin improvements
-UI/UX improvements
+New themes and UI/UX improvements
+Silent Payments scanning fix
+Payjoin enhancements
+Ledger flow enhancements
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 0d985b237..d6e931068 100644
--- a/cw_bitcoin/lib/address_from_output.dart
+++ b/cw_bitcoin/lib/address_from_output.dart
@@ -17,21 +17,16 @@ BitcoinBaseAddress addressFromScript(Script script,
switch (addressType) {
case P2pkhAddressType.p2pkh:
- return P2pkhAddress.fromScriptPubkey(
- script: script, network: BitcoinNetwork.mainnet);
+ return P2pkhAddress.fromScriptPubkey(script: script);
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);
+ return P2shAddress.fromScriptPubkey(script: script);
+ case SegwitAddressType.p2wpkh:
+ return P2wpkhAddress.fromScriptPubkey(script: script);
+ case SegwitAddressType.p2wsh:
+ return P2wshAddress.fromScriptPubkey(script: script);
+ case SegwitAddressType.p2tr:
+ return P2trAddress.fromScriptPubkey(script: script);
}
throw ArgumentError("Invalid script");
diff --git a/cw_bitcoin/lib/bitcoin_address_record.dart b/cw_bitcoin/lib/bitcoin_address_record.dart
index 1509f913a..97b3c08f8 100644
--- a/cw_bitcoin/lib/bitcoin_address_record.dart
+++ b/cw_bitcoin/lib/bitcoin_address_record.dart
@@ -82,7 +82,7 @@ class BitcoinAddressRecord extends BaseBitcoinAddressRecord {
type: decoded['type'] != null && decoded['type'] != ''
? BitcoinAddressType.values
.firstWhere((type) => type.toString() == decoded['type'] as String)
- : SegwitAddresType.p2wpkh,
+ : SegwitAddressType.p2wpkh,
scriptHash: decoded['scriptHash'] as String?,
network: network,
);
diff --git a/cw_bitcoin/lib/bitcoin_receive_page_option.dart b/cw_bitcoin/lib/bitcoin_receive_page_option.dart
index 07083e111..8331a182d 100644
--- a/cw_bitcoin/lib/bitcoin_receive_page_option.dart
+++ b/cw_bitcoin/lib/bitcoin_receive_page_option.dart
@@ -36,9 +36,9 @@ class BitcoinReceivePageOption implements ReceivePageOption {
BitcoinAddressType toType() {
switch (this) {
case BitcoinReceivePageOption.p2tr:
- return SegwitAddresType.p2tr;
+ return SegwitAddressType.p2tr;
case BitcoinReceivePageOption.p2wsh:
- return SegwitAddresType.p2wsh;
+ return SegwitAddressType.p2wsh;
case BitcoinReceivePageOption.p2pkh:
return P2pkhAddressType.p2pkh;
case BitcoinReceivePageOption.p2sh:
@@ -46,20 +46,20 @@ class BitcoinReceivePageOption implements ReceivePageOption {
case BitcoinReceivePageOption.silent_payments:
return SilentPaymentsAddresType.p2sp;
case BitcoinReceivePageOption.mweb:
- return SegwitAddresType.mweb;
+ return SegwitAddressType.mweb;
case BitcoinReceivePageOption.p2wpkh:
default:
- return SegwitAddresType.p2wpkh;
+ return SegwitAddressType.p2wpkh;
}
}
factory BitcoinReceivePageOption.fromType(BitcoinAddressType type) {
switch (type) {
- case SegwitAddresType.p2tr:
+ case SegwitAddressType.p2tr:
return BitcoinReceivePageOption.p2tr;
- case SegwitAddresType.p2wsh:
+ case SegwitAddressType.p2wsh:
return BitcoinReceivePageOption.p2wsh;
- case SegwitAddresType.mweb:
+ case SegwitAddressType.mweb:
return BitcoinReceivePageOption.mweb;
case P2pkhAddressType.p2pkh:
return BitcoinReceivePageOption.p2pkh;
@@ -67,7 +67,7 @@ class BitcoinReceivePageOption implements ReceivePageOption {
return BitcoinReceivePageOption.p2sh;
case SilentPaymentsAddresType.p2sp:
return BitcoinReceivePageOption.silent_payments;
- case SegwitAddresType.p2wpkh:
+ case SegwitAddressType.p2wpkh:
default:
return BitcoinReceivePageOption.p2wpkh;
}
diff --git a/cw_bitcoin/lib/bitcoin_wallet.dart b/cw_bitcoin/lib/bitcoin_wallet.dart
index 9231022f6..73a6ad0ea 100644
--- a/cw_bitcoin/lib/bitcoin_wallet.dart
+++ b/cw_bitcoin/lib/bitcoin_wallet.dart
@@ -73,9 +73,8 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
initialBalance: initialBalance,
seedBytes: seedBytes,
encryptionFileUtils: encryptionFileUtils,
- currency: networkParam == BitcoinNetwork.testnet
- ? CryptoCurrency.tbtc
- : CryptoCurrency.btc,
+ currency:
+ networkParam == BitcoinNetwork.testnet ? CryptoCurrency.tbtc : CryptoCurrency.btc,
alwaysScan: alwaysScan,
) {
// in a standard BIP44 wallet, mainHd derivation path = m/84'/0'/0'/0 (account 0, index unspecified here)
@@ -94,14 +93,12 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
mainHd: hd,
sideHd: accountHD.childKey(Bip32KeyIndex(1)),
network: networkParam ?? network,
- masterHd:
- seedBytes != null ? Bip32Slip10Secp256k1.fromSeed(seedBytes) : null,
+ masterHd: seedBytes != null ? Bip32Slip10Secp256k1.fromSeed(seedBytes) : null,
isHardwareWallet: walletInfo.isHardwareWallet,
payjoinManager: payjoinManager);
autorun((_) {
- this.walletAddresses.isEnabledAutoGenerateSubaddress =
- this.isEnabledAutoGenerateSubaddress;
+ this.walletAddresses.isEnabledAutoGenerateSubaddress = this.isEnabledAutoGenerateSubaddress;
});
}
@@ -136,8 +133,7 @@ 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;
}
@@ -210,10 +206,8 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
walletInfo.derivationInfo ??= DerivationInfo();
// set the default if not present:
- walletInfo.derivationInfo!.derivationPath ??=
- snp?.derivationPath ?? electrum_path;
- walletInfo.derivationInfo!.derivationType ??=
- snp?.derivationType ?? DerivationType.electrum;
+ walletInfo.derivationInfo!.derivationPath ??= snp?.derivationPath ?? electrum_path;
+ walletInfo.derivationInfo!.derivationType ??= snp?.derivationType ?? DerivationType.electrum;
Uint8List? seedBytes = null;
final mnemonic = keysData.mnemonic;
@@ -222,8 +216,7 @@ 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:
@@ -266,17 +259,10 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
derivationPath: walletInfo.derivationInfo!.derivationPath!);
}
- @override
- 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)
+ .where((element) => element.walletId == id && element.isSending && !element.isFrozen)
.isNotEmpty;
Future buildPsbt({
@@ -293,10 +279,8 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
}) async {
final psbtReadyInputs = [];
for (final utxo in utxos) {
- final rawTx =
- await electrumClient.getTransactionHex(hash: utxo.utxo.txHash);
- final publicKeyAndDerivationPath =
- publicKeys[utxo.ownerDetails.address.pubKeyHash()]!;
+ final rawTx = await electrumClient.getTransactionHex(hash: utxo.utxo.txHash);
+ final publicKeyAndDerivationPath = publicKeys[utxo.ownerDetails.address.pubKeyHash()]!;
psbtReadyInputs.add(PSBTReadyUtxoWithAddress(
utxo: utxo.utxo,
@@ -308,8 +292,7 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
));
}
- return PSBTTransactionBuild(
- inputs: psbtReadyInputs, outputs: outputs, enableRBF: enableRBF)
+ return PSBTTransactionBuild(inputs: psbtReadyInputs, outputs: outputs, enableRBF: enableRBF)
.psbt;
}
@@ -348,8 +331,7 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
Future createTransaction(Object credentials) async {
credentials = credentials as BitcoinTransactionCredentials;
- final tx = (await super.createTransaction(credentials))
- as PendingBitcoinTransaction;
+ final tx = (await super.createTransaction(credentials)) as PendingBitcoinTransaction;
final payjoinUri = credentials.payjoinUri;
if (payjoinUri == null) return tx;
@@ -372,12 +354,12 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
publicKeys: tx.publicKeys!,
masterFingerprint: Uint8List(0));
- final originalPsbt = await signPsbt(
- base64.encode(transaction.asPsbtV0()), getUtxoWithPrivateKeys());
+ final originalPsbt =
+ await signPsbt(base64.encode(transaction.asPsbtV0()), getUtxoWithPrivateKeys());
tx.commitOverride = () async {
- final sender = await payjoinManager.initSender(
- payjoinUri, originalPsbt, int.parse(tx.feeRate));
+ final sender =
+ await payjoinManager.initSender(payjoinUri, originalPsbt, int.parse(tx.feeRate));
payjoinManager.spawnNewSender(
sender: sender, pjUrl: payjoinUri, amount: BigInt.from(tx.amount));
};
@@ -393,8 +375,7 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
Future commitPsbt(String finalizedPsbt) {
final psbt = PsbtV2()..deserializeV0(base64.decode(finalizedPsbt));
- final btcTx =
- BtcTransaction.fromRaw(BytesUtils.toHexString(psbt.extract()));
+ final btcTx = BtcTransaction.fromRaw(BytesUtils.toHexString(psbt.extract()));
return PendingBitcoinTransaction(
btcTx,
@@ -408,12 +389,11 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
).commit();
}
- Future signPsbt(
- String preProcessedPsbt, List utxos) async {
+ 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()
+ return utxo.utxo.isP2tr
? key.signTapRoot(
txDigest,
sighash: sighash,
@@ -434,17 +414,15 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
Future signMessage(String message, {String? address = null}) async {
if (walletInfo.isHardwareWallet) {
final addressEntry = address != null
- ? walletAddresses.allAddresses
- .firstWhere((element) => element.address == address)
+ ? walletAddresses.allAddresses.firstWhere((element) => element.address == address)
: null;
final index = addressEntry?.index ?? 0;
final isChange = addressEntry?.isHidden == true ? 1 : 0;
final accountPath = walletInfo.derivationInfo?.derivationPath;
- final derivationPath =
- accountPath != null ? "$accountPath/$isChange/$index" : null;
+ final derivationPath = accountPath != null ? "$accountPath/$isChange/$index" : null;
- final signature = await _bitcoinLedgerApp!.signMessage(
- message: ascii.encode(message), signDerivationPath: derivationPath);
+ final signature = await _bitcoinLedgerApp!
+ .signMessage(message: ascii.encode(message), signDerivationPath: derivationPath);
return base64Encode(signature);
}
diff --git a/cw_bitcoin/lib/bitcoin_wallet_addresses.dart b/cw_bitcoin/lib/bitcoin_wallet_addresses.dart
index d84d958be..dd6dc5fae 100644
--- a/cw_bitcoin/lib/bitcoin_wallet_addresses.dart
+++ b/cw_bitcoin/lib/bitcoin_wallet_addresses.dart
@@ -31,10 +31,12 @@ abstract class BitcoinWalletAddressesBase extends ElectrumWalletAddresses with S
final PayjoinManager payjoinManager;
+ @observable
payjoin.Receiver? currentPayjoinReceiver;
- @observable
- String? payjoinEndpoint = null;
+ @computed
+ String? get payjoinEndpoint =>
+ currentPayjoinReceiver?.pjUriBuilder().build().pjEndpoint();
@override
String getAddress(
@@ -45,10 +47,10 @@ abstract class BitcoinWalletAddressesBase extends ElectrumWalletAddresses with S
if (addressType == P2pkhAddressType.p2pkh)
return generateP2PKHAddress(hd: hd, index: index, network: network);
- if (addressType == SegwitAddresType.p2tr)
+ if (addressType == SegwitAddressType.p2tr)
return generateP2TRAddress(hd: hd, index: index, network: network);
- if (addressType == SegwitAddresType.p2wsh)
+ if (addressType == SegwitAddressType.p2wsh)
return generateP2WSHAddress(hd: hd, index: index, network: network);
if (addressType == P2shAddressType.p2wpkhInP2sh)
@@ -57,32 +59,16 @@ 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;
- }
+ currentPayjoinReceiver = await payjoinManager.initReceiver(primaryAddress);
+
+ payjoinManager.resumeSessions();
}
- @action
Future newPayjoinReceiver() async {
- try {
- currentPayjoinReceiver = await payjoinManager.getUnusedReceiver(primaryAddress);
- payjoinEndpoint = (await currentPayjoinReceiver?.pjUri())?.pjEndpoint();
+ currentPayjoinReceiver = await payjoinManager.initReceiver(primaryAddress);
- payjoinManager.spawnReceiver(receiver: currentPayjoinReceiver!);
- } catch (e) {
- printV(e);
- // Ignore Connectivity errors
- if (!e.toString().contains("error sending request for url")) rethrow;
- }
+ printV("Initializing new Payjoin Receiver");
+ payjoinManager.spawnNewReceiver(receiver: currentPayjoinReceiver!);
}
}
diff --git a/cw_bitcoin/lib/electrum.dart b/cw_bitcoin/lib/electrum.dart
index 2ddd30df6..1f5c369e3 100644
--- a/cw_bitcoin/lib/electrum.dart
+++ b/cw_bitcoin/lib/electrum.dart
@@ -5,8 +5,6 @@ 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';
@@ -44,7 +42,7 @@ class ElectrumClient {
static const aliveTimerDuration = Duration(seconds: 4);
bool get isConnected => _isConnected;
- ProxySocket? socket;
+ Socket? socket;
void Function(ConnectionStatus)? onConnectionStatusChange;
int _id;
final Map _tasks;
@@ -74,11 +72,18 @@ class ElectrumClient {
} catch (_) {}
socket = null;
- final ssl = !(useSSL == false || (useSSL == null && uri.toString().contains("btc-electrum")));
try {
- socket = await ProxyWrapper().getSocksSocket(ssl, host, port, connectionTimeout: connectionTimeout);
+ 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,
+ );
+ }
} catch (e) {
- printV("connect: $e");
if (e is HandshakeException) {
useSSL = !(useSSL ?? false);
}
@@ -100,6 +105,7 @@ 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 bb9cea1bc..d7449c011 100644
--- a/cw_bitcoin/lib/electrum_wallet.dart
+++ b/cw_bitcoin/lib/electrum_wallet.dart
@@ -4,9 +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';
import 'package:cw_bitcoin/bitcoin_wallet.dart';
import 'package:cw_bitcoin/litecoin_wallet.dart';
@@ -19,7 +17,7 @@ import 'package:cw_bitcoin/bitcoin_transaction_credentials.dart';
import 'package:cw_bitcoin/bitcoin_transaction_priority.dart';
import 'package:cw_bitcoin/bitcoin_unspent.dart';
import 'package:cw_bitcoin/bitcoin_wallet_keys.dart';
-import 'package:cw_bitcoin/electrum.dart';
+import 'package:cw_bitcoin/electrum.dart' as electrum;
import 'package:cw_bitcoin/electrum_balance.dart';
import 'package:cw_bitcoin/electrum_derivations.dart';
import 'package:cw_bitcoin/electrum_transaction_history.dart';
@@ -50,6 +48,7 @@ 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';
@@ -69,7 +68,7 @@ abstract class ElectrumWalletBase
Uint8List? seedBytes,
this.passphrase,
List? initialAddresses,
- ElectrumClient? electrumClient,
+ electrum.ElectrumClient? electrumClient,
ElectrumBalance? initialBalance,
CryptoCurrency? currency,
this.alwaysScan,
@@ -96,7 +95,7 @@ abstract class ElectrumWalletBase
this.isTestnet = !network.isMainnet,
this._mnemonic = mnemonic,
super(walletInfo) {
- this.electrumClient = electrumClient ?? ElectrumClient();
+ this.electrumClient = electrumClient ?? electrum.ElectrumClient();
this.walletInfo = walletInfo;
transactionHistory = ElectrumTransactionHistory(
walletInfo: walletInfo,
@@ -167,7 +166,7 @@ abstract class ElectrumWalletBase
@observable
bool isEnabledAutoGenerateSubaddress;
- late ElectrumClient electrumClient;
+ late electrum.ElectrumClient electrumClient;
Box unspentCoinsInfo;
@override
@@ -182,7 +181,7 @@ abstract class ElectrumWalletBase
SyncStatus syncStatus;
Set get addressesSet => walletAddresses.allAddresses
- .where((element) => element.type != SegwitAddresType.mweb)
+ .where((element) => element.type != SegwitAddressType.mweb)
.map((addr) => addr.address)
.toSet();
@@ -333,14 +332,14 @@ abstract class ElectrumWalletBase
final receivePort = ReceivePort();
_isolate = Isolate.spawn(
- startRefresh,
+ _handleScanSilentPayments,
ScanData(
sendPort: receivePort.sendPort,
silentAddress: walletAddresses.silentAddress!,
network: network,
height: height,
chainTip: chainTip,
- electrumClient: ElectrumClient(),
+ electrumClient: electrum.ElectrumClient(),
transactionHistoryIds: transactionHistory.transactions.keys.toList(),
node: (await getNodeSupportsSilentPayments()) == true
? ScanNode(node!.uri, node!.useSSL)
@@ -439,7 +438,6 @@ abstract class ElectrumWalletBase
BigintUtils.fromBytes(BytesUtils.fromHexString(unspent.silentPaymentLabel!)),
)
: silentAddress.B_spend,
- network: network,
);
final addressRecord = walletAddresses.silentAddresses
@@ -493,9 +491,10 @@ abstract class ElectrumWalletBase
Future updateFeeRates() async {
if (await checkIfMempoolAPIIsEnabled() && type == WalletType.bitcoin) {
try {
- final response = await ProxyWrapper()
- .get(clearnetUri: Uri.parse("https://mempool.cakewallet.com/api/v1/fees/recommended"))
- .timeout(Duration(seconds: 15));
+ final response = await http
+ .get(Uri.parse("https://mempool.cakewallet.com/api/v1/fees/recommended"))
+ .timeout(Duration(seconds: 5));
+
final result = json.decode(response.body) as Map;
final slowFee = (result['economyFee'] as num?)?.toInt() ?? 0;
int mediumFee = (result['hourFee'] as num?)?.toInt() ?? 0;
@@ -563,7 +562,7 @@ abstract class ElectrumWalletBase
node!.save();
return node!.supportsSilentPayments!;
}
- } on RequestFailedTimeoutException catch (_) {
+ } on electrum.RequestFailedTimeoutException catch (_) {
node!.supportsSilentPayments = false;
node!.save();
return node!.supportsSilentPayments!;
@@ -624,9 +623,9 @@ abstract class ElectrumWalletBase
switch (coinTypeToSpendFrom) {
case UnspentCoinType.mweb:
- return utx.bitcoinAddressRecord.type == SegwitAddresType.mweb;
+ return utx.bitcoinAddressRecord.type == SegwitAddressType.mweb;
case UnspentCoinType.nonMweb:
- return utx.bitcoinAddressRecord.type != SegwitAddresType.mweb;
+ return utx.bitcoinAddressRecord.type != SegwitAddressType.mweb;
case UnspentCoinType.any:
return true;
}
@@ -634,7 +633,7 @@ abstract class ElectrumWalletBase
final unconfirmedCoins = availableInputs.where((utx) => utx.confirmations == 0).toList();
// sort the unconfirmed coins so that mweb coins are last:
- availableInputs.sort((a, b) => a.bitcoinAddressRecord.type == SegwitAddresType.mweb ? 1 : -1);
+ availableInputs.sort((a, b) => a.bitcoinAddressRecord.type == SegwitAddressType.mweb ? 1 : -1);
for (int i = 0; i < availableInputs.length; i++) {
final utx = availableInputs[i];
@@ -642,7 +641,7 @@ abstract class ElectrumWalletBase
if (paysToSilentPayment) {
// Check inputs for shared secret derivation
- if (utx.bitcoinAddressRecord.type == SegwitAddresType.p2wsh) {
+ if (utx.bitcoinAddressRecord.type == SegwitAddressType.p2wsh) {
throw BitcoinTransactionSilentPaymentsNotSupported();
}
}
@@ -677,7 +676,7 @@ abstract class ElectrumWalletBase
if (privkey != null) {
inputPrivKeyInfos.add(ECPrivateInfo(
privkey,
- address.type == SegwitAddresType.p2tr,
+ address.type == SegwitAddressType.p2tr,
tweak: !isSilentPayment,
));
@@ -1163,7 +1162,7 @@ abstract class ElectrumWalletBase
throw Exception(error);
}
- if (utxo.utxo.isP2tr()) {
+ if (utxo.utxo.isP2tr) {
hasTaprootInputs = true;
return key.privkey.signTapRoot(
txDigest,
@@ -1230,7 +1229,7 @@ abstract class ElectrumWalletBase
'change_address_index': walletAddresses.currentChangeAddressIndexByType,
'addresses': walletAddresses.allAddresses.map((addr) => addr.toJSON()).toList(),
'address_page_type': walletInfo.addressPageType == null
- ? SegwitAddresType.p2wpkh.toString()
+ ? SegwitAddressType.p2wpkh.toString()
: walletInfo.addressPageType.toString(),
'balance': balance[currency]?.toJSON(),
'derivationTypeIndex': walletInfo.derivationInfo?.derivationType?.index,
@@ -1370,7 +1369,7 @@ abstract class ElectrumWalletBase
List updatedUnspentCoins = [];
final previousUnspentCoins = List.from(unspentCoins.where((utxo) =>
- utxo.bitcoinAddressRecord.type != SegwitAddresType.mweb &&
+ utxo.bitcoinAddressRecord.type != SegwitAddressType.mweb &&
utxo.bitcoinAddressRecord is! BitcoinSilentPaymentAddressRecord));
if (hasSilentPaymentsScanning) {
@@ -1384,13 +1383,13 @@ abstract class ElectrumWalletBase
// Set the balance of all non-silent payment and non-mweb addresses to 0 before updating
walletAddresses.allAddresses
- .where((element) => element.type != SegwitAddresType.mweb)
+ .where((element) => element.type != SegwitAddressType.mweb)
.forEach((addr) {
if (addr is! BitcoinSilentPaymentAddressRecord) addr.balance = 0;
});
final addressFutures = walletAddresses.allAddresses
- .where((element) => element.type != SegwitAddresType.mweb)
+ .where((element) => element.type != SegwitAddressType.mweb)
.map((address) => fetchUnspent(address))
.toList();
@@ -1831,7 +1830,7 @@ abstract class ElectrumWalletBase
throw Exception("Cannot find private key");
}
- if (utxo.utxo.isP2tr()) {
+ if (utxo.utxo.isP2tr) {
return key.signTapRoot(txDigest, sighash: sighash);
} else {
return key.signInput(txDigest, sigHash: sighash);
@@ -1877,17 +1876,20 @@ abstract class ElectrumWalletBase
if (height != null && height > 0 && await checkIfMempoolAPIIsEnabled()) {
try {
- final blockHash = await ProxyWrapper()
- .get(clearnetUri: Uri.parse("https://mempool.cakewallet.com/api/v1/block-height/$height"))
- .timeout(Duration(seconds: 15));
+ final blockHash = await http.get(
+ Uri.parse(
+ "https://mempool.cakewallet.com/api/v1/block-height/$height",
+ ),
+ );
if (blockHash.statusCode == 200 &&
blockHash.body.isNotEmpty &&
jsonDecode(blockHash.body) != null) {
- final blockResponse = await ProxyWrapper()
- .get(clearnetUri: Uri.parse("https://mempool.cakewallet.com/api/v1/block/${blockHash}"))
- .timeout(Duration(seconds: 15));
-
+ final blockResponse = await http.get(
+ Uri.parse(
+ "https://mempool.cakewallet.com/api/v1/block/${blockHash.body}",
+ ),
+ );
if (blockResponse.statusCode == 200 &&
blockResponse.body.isNotEmpty &&
jsonDecode(blockResponse.body)['timestamp'] != null) {
@@ -1978,7 +1980,7 @@ abstract class ElectrumWalletBase
.map((type) => fetchTransactionsForAddressType(historiesWithDetails, type)));
} else if (type == WalletType.litecoin) {
await Future.wait(LITECOIN_ADDRESS_TYPES
- .where((type) => type != SegwitAddresType.mweb)
+ .where((type) => type != SegwitAddressType.mweb)
.map((type) => fetchTransactionsForAddressType(historiesWithDetails, type)));
}
@@ -2167,7 +2169,7 @@ abstract class ElectrumWalletBase
final unsubscribedScriptHashes = walletAddresses.allAddresses.where(
(address) =>
!_scripthashesUpdateSubject.containsKey(address.getScriptHash(network)) &&
- address.type != SegwitAddresType.mweb,
+ address.type != SegwitAddressType.mweb,
);
await Future.wait(unsubscribedScriptHashes.map((address) async {
@@ -2390,9 +2392,9 @@ abstract class ElectrumWalletBase
derivationPath.substring(0, derivationPath.lastIndexOf("'") + 1);
@action
- void _onConnectionStatusChange(ConnectionStatus status) {
+ void _onConnectionStatusChange(electrum.ConnectionStatus status) {
switch (status) {
- case ConnectionStatus.connected:
+ case electrum.ConnectionStatus.connected:
if (syncStatus is NotConnectedSyncStatus ||
syncStatus is LostConnectionSyncStatus ||
syncStatus is ConnectingSyncStatus) {
@@ -2400,19 +2402,19 @@ abstract class ElectrumWalletBase
}
break;
- case ConnectionStatus.disconnected:
+ case electrum.ConnectionStatus.disconnected:
if (syncStatus is! NotConnectedSyncStatus &&
syncStatus is! ConnectingSyncStatus &&
syncStatus is! SyncronizingSyncStatus) {
syncStatus = NotConnectedSyncStatus();
}
break;
- case ConnectionStatus.failed:
+ case electrum.ConnectionStatus.failed:
if (syncStatus is! LostConnectionSyncStatus) {
syncStatus = LostConnectionSyncStatus();
}
break;
- case ConnectionStatus.connecting:
+ case electrum.ConnectionStatus.connecting:
if (syncStatus is! ConnectingSyncStatus) {
syncStatus = ConnectingSyncStatus();
}
@@ -2524,7 +2526,7 @@ class ScanData {
final ScanNode? node;
final BasedUtxoNetwork network;
final int chainTip;
- final ElectrumClient electrumClient;
+ final electrum.ElectrumClient electrumClient;
final List transactionHistoryIds;
final Map labels;
final List labelIndexes;
@@ -2568,6 +2570,234 @@ class SyncResponse {
SyncResponse(this.height, this.syncStatus);
}
+Future _handleScanSilentPayments(ScanData scanData) async {
+ try {
+ // if (scanData.shouldSwitchNodes) {
+ var scanningClient = await ElectrumProvider.connect(
+ ElectrumTCPService.connect(
+ Uri.parse("tcp://electrs.cakewallet.com:50001"),
+ ),
+ );
+ // }
+
+ int syncHeight = scanData.height;
+ int initialSyncHeight = syncHeight;
+
+ final receiver = Receiver(
+ scanData.silentAddress.b_scan.toHex(),
+ scanData.silentAddress.B_spend.toHex(),
+ scanData.network == BitcoinNetwork.testnet,
+ scanData.labelIndexes,
+ );
+
+ int getCountToScanPerRequest(int syncHeight) {
+ if (scanData.isSingleScan) {
+ return 1;
+ }
+
+ final amountLeft = scanData.chainTip - syncHeight + 1;
+ return amountLeft;
+ }
+
+ // Initial status UI update, send how many blocks in total to scan
+ scanData.sendPort.send(SyncResponse(syncHeight, StartingScanSyncStatus(syncHeight)));
+
+ final req = ElectrumTweaksSubscribe(
+ height: syncHeight,
+ count: getCountToScanPerRequest(syncHeight),
+ historicalMode: false,
+ );
+
+ var _scanningStream = await scanningClient.subscribe(req);
+
+ void listenFn(Map event, ElectrumTweaksSubscribe req) {
+ final response = req.onResponse(event);
+
+ if (response == null || _scanningStream == null) {
+ return;
+ }
+
+ // is success or error msg
+ final noData = response.message != null;
+
+ if (noData) {
+ if (scanData.isSingleScan) {
+ return;
+ }
+
+ // re-subscribe to continue receiving messages, starting from the next unscanned height
+ final nextHeight = syncHeight + 1;
+
+ if (nextHeight <= scanData.chainTip) {
+ final nextStream = scanningClient.subscribe(
+ ElectrumTweaksSubscribe(
+ height: nextHeight,
+ count: getCountToScanPerRequest(nextHeight),
+ historicalMode: false,
+ ),
+ );
+
+ if (nextStream != null) {
+ nextStream.listen((event) => listenFn(event, req));
+ } else {
+ scanData.sendPort.send(
+ SyncResponse(scanData.height, LostConnectionSyncStatus()),
+ );
+ }
+ }
+
+ return;
+ }
+
+ final tweakHeight = response.block;
+
+ if (initialSyncHeight < tweakHeight) initialSyncHeight = tweakHeight;
+
+ // Continuous status UI update, send how many blocks left to scan
+ final syncingStatus = scanData.isSingleScan
+ ? SyncingSyncStatus(1, 0)
+ : SyncingSyncStatus.fromHeightValues(scanData.chainTip, initialSyncHeight, tweakHeight);
+
+ scanData.sendPort.send(SyncResponse(syncHeight, syncingStatus));
+
+ try {
+ final blockTweaks = response.blockTweaks;
+
+ for (final txid in blockTweaks.keys) {
+ final tweakData = blockTweaks[txid];
+ final outputPubkeys = tweakData!.outputPubkeys;
+ final tweak = tweakData.tweak;
+
+ try {
+ final addToWallet = {};
+
+ // receivers.forEach((receiver) {
+ // NOTE: scanOutputs, from sp_scanner package, called from rust here
+ final scanResult = scanOutputs([outputPubkeys.keys.toList()], tweak, receiver);
+
+ if (scanResult.isEmpty) {
+ continue;
+ }
+
+ if (addToWallet[receiver.BSpend] == null) {
+ addToWallet[receiver.BSpend] = scanResult;
+ } else {
+ addToWallet[receiver.BSpend].addAll(scanResult);
+ }
+ // });
+
+ if (addToWallet.isEmpty) {
+ // no results tx, continue to next tx
+ continue;
+ }
+
+ // initial placeholder ElectrumTransactionInfo object to update values based on new scanned unspent(s) on the following loop
+ final txInfo = ElectrumTransactionInfo(
+ WalletType.bitcoin,
+ id: txid,
+ height: tweakHeight,
+ amount: 0,
+ fee: 0,
+ direction: TransactionDirection.incoming,
+ isReplaced: false,
+ date: DateTime.fromMillisecondsSinceEpoch(
+ DateTime.now().millisecondsSinceEpoch * 1000,
+ ),
+ confirmations: scanData.chainTip - tweakHeight + 1,
+ isReceivedSilentPayment: true,
+ isPending: false,
+ unspents: [],
+ );
+
+ List unspents = [];
+
+ addToWallet.forEach((BSpend, scanResultPerLabel) {
+ scanResultPerLabel.forEach((label, scanOutput) {
+ final labelValue = label == "None" ? null : label.toString();
+
+ (scanOutput as Map).forEach((outputPubkey, tweak) {
+ final t_k = tweak as String;
+
+ final receivingOutputAddress = ECPublic.fromHex(outputPubkey)
+ .toTaprootAddress(tweak: false)
+ .toAddress(scanData.network);
+
+ final matchingOutput = outputPubkeys[outputPubkey]!;
+ final amount = matchingOutput.amount;
+ final pos = matchingOutput.vout;
+
+ // final matchingSPWallet = scanData.silentPaymentsWallets.firstWhere(
+ // (receiver) => receiver.B_spend.toHex() == BSpend.toString(),
+ // );
+
+ // final labelIndex = labelValue != null ? scanData.labels[label] : 0;
+ // final balance = ElectrumBalance();
+ // balance.confirmed = amount;
+
+ final receivedAddressRecord = BitcoinSilentPaymentAddressRecord(
+ receivingOutputAddress,
+ index: 0,
+ isHidden: false,
+ isUsed: true,
+ network: scanData.network,
+ silentPaymentTweak: t_k,
+ type: SegwitAddressType.p2tr,
+ txCount: 1,
+ balance: amount,
+ );
+
+ final unspent = BitcoinSilentPaymentsUnspent(
+ receivedAddressRecord,
+ txid,
+ amount,
+ pos,
+ silentPaymentTweak: t_k,
+ silentPaymentLabel: labelValue,
+ );
+
+ unspents.add(unspent);
+ txInfo.unspents!.add(unspent);
+ txInfo.amount += unspent.value;
+ });
+ });
+ });
+
+ scanData.sendPort.send({txInfo.id: txInfo});
+ } catch (e, stacktrace) {
+ printV(stacktrace);
+ printV(e.toString());
+ }
+ }
+ } catch (e, stacktrace) {
+ printV(stacktrace);
+ printV(e.toString());
+ }
+
+ syncHeight = tweakHeight;
+
+ if ((tweakHeight >= scanData.chainTip) || scanData.isSingleScan) {
+ if (tweakHeight >= scanData.chainTip)
+ scanData.sendPort.send(
+ SyncResponse(syncHeight, SyncedTipSyncStatus(scanData.chainTip)),
+ );
+
+ if (scanData.isSingleScan) {
+ scanData.sendPort.send(SyncResponse(syncHeight, SyncedSyncStatus()));
+ }
+
+ _scanningStream?.close();
+ _scanningStream = null;
+ return;
+ }
+ }
+
+ _scanningStream?.listen((event) => listenFn(event, req));
+ } catch (e) {
+ printV("Error in _handleScanSilentPayments: $e");
+ scanData.sendPort.send(SyncResponse(scanData.height, LostConnectionSyncStatus()));
+ }
+}
+
Future startRefresh(ScanData scanData) async {
int syncHeight = scanData.height;
int initialSyncHeight = syncHeight;
@@ -2580,7 +2810,7 @@ Future startRefresh(ScanData scanData) async {
useSSL: scanData.node?.useSSL ?? false,
);
- int getCountPerRequest(int syncHeight) {
+ int getCountToScanPerRequest(int syncHeight) {
if (scanData.isSingleScan) {
return 1;
}
@@ -2595,11 +2825,10 @@ Future startRefresh(ScanData scanData) async {
scanData.silentAddress.B_spend.toHex(),
scanData.network == BitcoinNetwork.testnet,
scanData.labelIndexes,
- scanData.labelIndexes.length,
);
// Initial status UI update, send how many blocks in total to scan
- final initialCount = getCountPerRequest(syncHeight);
+ final initialCount = getCountToScanPerRequest(syncHeight);
scanData.sendPort.send(SyncResponse(syncHeight, StartingScanSyncStatus(syncHeight)));
tweaksSubscription = await electrumClient.tweaksSubscribe(
@@ -2610,22 +2839,24 @@ Future startRefresh(ScanData scanData) async {
Future listenFn(t) async {
final tweaks = t as Map;
final msg = tweaks["message"];
- // success or error msg
+
+ // is success or error msg
final noData = msg != null;
if (noData) {
+ if (scanData.isSingleScan) {
+ return;
+ }
+
// re-subscribe to continue receiving messages, starting from the next unscanned height
final nextHeight = syncHeight + 1;
- final nextCount = getCountPerRequest(nextHeight);
- if (nextCount > 0) {
- tweaksSubscription?.close();
-
- final nextTweaksSubscription = electrumClient.tweaksSubscribe(
+ if (nextHeight <= scanData.chainTip) {
+ final nextStream = electrumClient.tweaksSubscribe(
height: nextHeight,
- count: nextCount,
+ count: getCountToScanPerRequest(nextHeight),
);
- nextTweaksSubscription?.listen(listenFn);
+ nextStream?.listen(listenFn);
}
return;
@@ -2707,7 +2938,7 @@ Future startRefresh(ScanData scanData) async {
isUsed: true,
network: scanData.network,
silentPaymentTweak: t_k,
- type: SegwitAddresType.p2tr,
+ type: SegwitAddressType.p2tr,
txCount: 1,
balance: amount!,
);
@@ -2800,15 +3031,15 @@ BitcoinAddressType _getScriptType(BitcoinBaseAddress type) {
} else if (type is P2shAddress) {
return P2shAddressType.p2wpkhInP2sh;
} else if (type is P2wshAddress) {
- return SegwitAddresType.p2wsh;
+ return SegwitAddressType.p2wsh;
} else if (type is P2trAddress) {
- return SegwitAddresType.p2tr;
+ return SegwitAddressType.p2tr;
} else if (type is MwebAddress) {
- return SegwitAddresType.mweb;
+ return SegwitAddressType.mweb;
} else if (type is SilentPaymentsAddresType) {
return SilentPaymentsAddresType.p2sp;
} else {
- return SegwitAddresType.p2wpkh;
+ return SegwitAddressType.p2wpkh;
}
}
diff --git a/cw_bitcoin/lib/electrum_wallet_addresses.dart b/cw_bitcoin/lib/electrum_wallet_addresses.dart
index 614a06a3b..9d1ae54aa 100644
--- a/cw_bitcoin/lib/electrum_wallet_addresses.dart
+++ b/cw_bitcoin/lib/electrum_wallet_addresses.dart
@@ -17,16 +17,16 @@ part 'electrum_wallet_addresses.g.dart';
class ElectrumWalletAddresses = ElectrumWalletAddressesBase with _$ElectrumWalletAddresses;
const List BITCOIN_ADDRESS_TYPES = [
- SegwitAddresType.p2wpkh,
+ SegwitAddressType.p2wpkh,
P2pkhAddressType.p2pkh,
- SegwitAddresType.p2tr,
- SegwitAddresType.p2wsh,
+ SegwitAddressType.p2tr,
+ SegwitAddressType.p2wsh,
P2shAddressType.p2wpkhInP2sh,
];
const List LITECOIN_ADDRESS_TYPES = [
- SegwitAddresType.p2wpkh,
- SegwitAddresType.mweb,
+ SegwitAddressType.p2wpkh,
+ SegwitAddressType.mweb,
];
const List BITCOIN_CASH_ADDRESS_TYPES = [
@@ -62,7 +62,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
_addressPageType = initialAddressPageType ??
(walletInfo.addressPageType != null
? BitcoinAddressType.fromValue(walletInfo.addressPageType!)
- : SegwitAddresType.p2wpkh),
+ : SegwitAddressType.p2wpkh),
silentAddresses = ObservableList.of(
(initialSilentAddresses ?? []).toSet()),
currentSilentAddressIndex = initialSilentAddressIndex,
@@ -71,9 +71,12 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
super(walletInfo) {
if (masterHd != null) {
silentAddress = SilentPaymentOwner.fromPrivateKeys(
- b_scan: ECPrivate.fromHex(masterHd.derivePath(SCAN_PATH).privateKey.toHex()),
- b_spend: ECPrivate.fromHex(masterHd.derivePath(SPEND_PATH).privateKey.toHex()),
- network: network,
+ b_scan: ECPrivate.fromHex(
+ masterHd.derivePath("m/352'/1'/0'/1'/0").privateKey.toHex(),
+ ),
+ b_spend: ECPrivate.fromHex(
+ masterHd.derivePath("m/352'/1'/0'/0'/0").privateKey.toHex(),
+ ),
);
if (silentAddresses.length == 0) {
@@ -144,12 +147,13 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
return silentAddress.toString();
}
- final typeMatchingAddresses = _addresses.where((addr) => !addr.isHidden && _isAddressPageTypeMatch(addr)).toList();
- final typeMatchingReceiveAddresses = typeMatchingAddresses.where((addr) => !addr.isUsed).toList();
+ final typeMatchingAddresses =
+ _addresses.where((addr) => !addr.isHidden && _isAddressPageTypeMatch(addr)).toList();
+ final typeMatchingReceiveAddresses =
+ typeMatchingAddresses.where((addr) => !addr.isUsed).toList();
if (!isEnabledAutoGenerateSubaddress) {
- if (previousAddressRecord != null &&
- previousAddressRecord!.type == addressPageType) {
+ if (previousAddressRecord != null && previousAddressRecord!.type == addressPageType) {
return previousAddressRecord!.address;
}
@@ -249,17 +253,17 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
if (walletInfo.type == WalletType.bitcoinCash) {
await _generateInitialAddresses(type: P2pkhAddressType.p2pkh);
} else if (walletInfo.type == WalletType.litecoin) {
- await _generateInitialAddresses(type: SegwitAddresType.p2wpkh);
+ await _generateInitialAddresses(type: SegwitAddressType.p2wpkh);
if ((Platform.isAndroid || Platform.isIOS) && !isHardwareWallet) {
- await _generateInitialAddresses(type: SegwitAddresType.mweb);
+ await _generateInitialAddresses(type: SegwitAddressType.mweb);
}
} else if (walletInfo.type == WalletType.bitcoin) {
await _generateInitialAddresses();
if (!isHardwareWallet) {
await _generateInitialAddresses(type: P2pkhAddressType.p2pkh);
await _generateInitialAddresses(type: P2shAddressType.p2wpkhInP2sh);
- await _generateInitialAddresses(type: SegwitAddresType.p2tr);
- await _generateInitialAddresses(type: SegwitAddresType.p2wsh);
+ await _generateInitialAddresses(type: SegwitAddressType.p2tr);
+ await _generateInitialAddresses(type: SegwitAddressType.p2wsh);
}
}
@@ -323,7 +327,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
BaseBitcoinAddressRecord generateNewAddress({String label = ''}) {
if (addressPageType == SilentPaymentsAddresType.p2sp && silentAddress != null) {
final currentSilentAddressIndex = silentAddresses
- .where((addressRecord) => addressRecord.type != SegwitAddresType.p2tr)
+ .where((addressRecord) => addressRecord.type != SegwitAddressType.p2tr)
.length -
1;
@@ -381,7 +385,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
void addBitcoinAddressTypes() {
final lastP2wpkh = _addresses
.where((addressRecord) =>
- _isUnusedReceiveAddressByType(addressRecord, SegwitAddresType.p2wpkh))
+ _isUnusedReceiveAddressByType(addressRecord, SegwitAddressType.p2wpkh))
.toList()
.last;
if (lastP2wpkh.address != address) {
@@ -407,7 +411,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
}
final lastP2tr = _addresses.firstWhere(
- (addressRecord) => _isUnusedReceiveAddressByType(addressRecord, SegwitAddresType.p2tr));
+ (addressRecord) => _isUnusedReceiveAddressByType(addressRecord, SegwitAddressType.p2tr));
if (lastP2tr.address != address) {
addressesMap[lastP2tr.address] = 'P2TR';
} else {
@@ -415,7 +419,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
}
final lastP2wsh = _addresses.firstWhere(
- (addressRecord) => _isUnusedReceiveAddressByType(addressRecord, SegwitAddresType.p2wsh));
+ (addressRecord) => _isUnusedReceiveAddressByType(addressRecord, SegwitAddressType.p2wsh));
if (lastP2wsh.address != address) {
addressesMap[lastP2wsh.address] = 'P2WSH';
} else {
@@ -440,7 +444,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
void addLitecoinAddressTypes() {
final lastP2wpkh = _addresses
.where((addressRecord) =>
- _isUnusedReceiveAddressByType(addressRecord, SegwitAddresType.p2wpkh))
+ _isUnusedReceiveAddressByType(addressRecord, SegwitAddressType.p2wpkh))
.toList()
.last;
if (lastP2wpkh.address != address) {
@@ -450,7 +454,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
}
final lastMweb = _addresses.firstWhere(
- (addressRecord) => _isUnusedReceiveAddressByType(addressRecord, SegwitAddresType.mweb));
+ (addressRecord) => _isUnusedReceiveAddressByType(addressRecord, SegwitAddressType.mweb));
if (lastMweb.address != address) {
addressesMap[lastMweb.address] = 'MWEB';
} else {
@@ -560,14 +564,14 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
addressRecord.isHidden &&
!addressRecord.isUsed &&
// TODO: feature to change change address type. For now fixed to p2wpkh, the cheapest type
- (walletInfo.type != WalletType.bitcoin || addressRecord.type == SegwitAddresType.p2wpkh));
+ (walletInfo.type != WalletType.bitcoin || addressRecord.type == SegwitAddressType.p2wpkh));
changeAddresses.addAll(newAddresses);
}
@action
Future discoverAddresses(List addressList, bool isHidden,
Future Function(BitcoinAddressRecord) getAddressHistory,
- {BitcoinAddressType type = SegwitAddresType.p2wpkh}) async {
+ {BitcoinAddressType type = SegwitAddressType.p2wpkh}) async {
final newAddresses = await _createNewAddresses(gap,
startIndex: addressList.length, isHidden: isHidden, type: type);
addAddresses(newAddresses);
@@ -581,7 +585,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
}
Future _generateInitialAddresses(
- {BitcoinAddressType type = SegwitAddresType.p2wpkh}) async {
+ {BitcoinAddressType type = SegwitAddressType.p2wpkh}) async {
var countOfReceiveAddresses = 0;
var countOfHiddenAddresses = 0;
@@ -658,7 +662,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
void _validateAddresses() {
_addresses.forEach((element) async {
- if (element.type == SegwitAddresType.mweb) {
+ if (element.type == SegwitAddressType.mweb) {
// this would add a ton of startup lag for mweb addresses since we have 1000 of them
return;
}
diff --git a/cw_bitcoin/lib/electrum_wallet_snapshot.dart b/cw_bitcoin/lib/electrum_wallet_snapshot.dart
index 990719089..3e5f331df 100644
--- a/cw_bitcoin/lib/electrum_wallet_snapshot.dart
+++ b/cw_bitcoin/lib/electrum_wallet_snapshot.dart
@@ -87,8 +87,8 @@ class ElectrumWalletSnapshot {
final balance = ElectrumBalance.fromJSON(data['balance'] as String?) ??
ElectrumBalance(confirmed: 0, unconfirmed: 0, frozen: 0);
- var regularAddressIndexByType = {SegwitAddresType.p2wpkh.toString(): 0};
- var changeAddressIndexByType = {SegwitAddresType.p2wpkh.toString(): 0};
+ var regularAddressIndexByType = {SegwitAddressType.p2wpkh.toString(): 0};
+ var changeAddressIndexByType = {SegwitAddressType.p2wpkh.toString(): 0};
var silentAddressIndex = 0;
final derivationType = DerivationType
@@ -97,10 +97,10 @@ class ElectrumWalletSnapshot {
try {
regularAddressIndexByType = {
- SegwitAddresType.p2wpkh.toString(): int.parse(data['account_index'] as String? ?? '0')
+ SegwitAddressType.p2wpkh.toString(): int.parse(data['account_index'] as String? ?? '0')
};
changeAddressIndexByType = {
- SegwitAddresType.p2wpkh.toString():
+ SegwitAddressType.p2wpkh.toString():
int.parse(data['change_address_index'] as String? ?? '0')
};
silentAddressIndex = int.parse(data['silent_address_index'] as String? ?? '0');
diff --git a/cw_bitcoin/lib/litecoin_wallet.dart b/cw_bitcoin/lib/litecoin_wallet.dart
index 08c56c600..4e6b2536a 100644
--- a/cw_bitcoin/lib/litecoin_wallet.dart
+++ b/cw_bitcoin/lib/litecoin_wallet.dart
@@ -16,7 +16,6 @@ import 'package:fixnum/fixnum.dart';
import 'package:bip39/bip39.dart' as bip39;
import 'package:bitcoin_base/bitcoin_base.dart';
import 'package:blockchain_utils/blockchain_utils.dart';
-import 'package:blockchain_utils/signer/ecdsa_signing_key.dart';
import 'package:cw_bitcoin/bitcoin_address_record.dart';
import 'package:cw_bitcoin/bitcoin_mnemonic.dart';
import 'package:cw_bitcoin/bitcoin_transaction_priority.dart';
@@ -971,9 +970,9 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
List? inputPrivKeyInfos,
List? vinOutpoints,
}) async {
- bool spendsMweb = utxos.any((utxo) => utxo.utxo.scriptType == SegwitAddresType.mweb);
+ bool spendsMweb = utxos.any((utxo) => utxo.utxo.scriptType == SegwitAddressType.mweb);
bool paysToMweb = outputs
- .any((output) => output.toOutput.scriptPubKey.getAddressType() == SegwitAddresType.mweb);
+ .any((output) => output.toOutput.scriptPubKey.getAddressType() == SegwitAddressType.mweb);
bool isRegular = !spendsMweb && !paysToMweb;
bool isMweb = spendsMweb || paysToMweb;
@@ -1064,9 +1063,9 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
tx.isMweb = mwebEnabled;
if (!mwebEnabled) {
- tx.changeAddressOverride =
- (await (walletAddresses as LitecoinWalletAddresses).getChangeAddress(coinTypeToSpendFrom: UnspentCoinType.nonMweb))
- .address;
+ tx.changeAddressOverride = (await (walletAddresses as LitecoinWalletAddresses)
+ .getChangeAddress(coinTypeToSpendFrom: UnspentCoinType.nonMweb))
+ .address;
return tx;
}
await waitForMwebAddresses();
@@ -1108,7 +1107,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
// check if mweb inputs are used:
for (final utxo in tx.utxos) {
- if (utxo.utxo.scriptType == SegwitAddresType.mweb) {
+ if (utxo.utxo.scriptType == SegwitAddressType.mweb) {
hasMwebInput = true;
}
}
@@ -1119,7 +1118,9 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
bool isRegular = !hasMwebInput && !hasMwebOutput;
bool shouldNotUseMwebChange = isPegIn || isRegular || !hasMwebInput;
tx.changeAddressOverride = (await (walletAddresses as LitecoinWalletAddresses)
- .getChangeAddress(coinTypeToSpendFrom: shouldNotUseMwebChange ? UnspentCoinType.nonMweb : UnspentCoinType.any))
+ .getChangeAddress(
+ coinTypeToSpendFrom:
+ shouldNotUseMwebChange ? UnspentCoinType.nonMweb : UnspentCoinType.any))
.address;
if (isRegular) {
tx.isMweb = false;
diff --git a/cw_bitcoin/lib/litecoin_wallet_addresses.dart b/cw_bitcoin/lib/litecoin_wallet_addresses.dart
index bbb987766..5e5a27fe8 100644
--- a/cw_bitcoin/lib/litecoin_wallet_addresses.dart
+++ b/cw_bitcoin/lib/litecoin_wallet_addresses.dart
@@ -106,7 +106,7 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with
.map((e) => BitcoinAddressRecord(
e.value,
index: e.key,
- type: SegwitAddresType.mweb,
+ type: SegwitAddressType.mweb,
network: network,
))
.toList();
@@ -128,7 +128,7 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with
required Bip32Slip10Secp256k1 hd,
BitcoinAddressType? addressType,
}) {
- if (addressType == SegwitAddresType.mweb) {
+ if (addressType == SegwitAddressType.mweb) {
return hd == sideHd ? mwebAddrs[0] : mwebAddrs[index + 1];
}
return generateP2WPKHAddress(hd: hd, index: index, network: network);
@@ -140,7 +140,7 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with
required Bip32Slip10Secp256k1 hd,
BitcoinAddressType? addressType,
}) async {
- if (addressType == SegwitAddresType.mweb) {
+ if (addressType == SegwitAddressType.mweb) {
await ensureMwebAddressUpToIndexExists(index);
}
return getAddress(index: index, hd: hd, addressType: addressType);
@@ -195,7 +195,7 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with
return BitcoinAddressRecord(
mwebAddrs[0],
index: 0,
- type: SegwitAddresType.mweb,
+ type: SegwitAddressType.mweb,
network: network,
);
}
@@ -207,7 +207,7 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with
String get addressForExchange {
// don't use mweb addresses for exchange refund address:
final addresses = receiveAddresses
- .where((element) => element.type == SegwitAddresType.p2wpkh && !element.isUsed);
+ .where((element) => element.type == SegwitAddressType.p2wpkh && !element.isUsed);
return addresses.first.address;
}
}
diff --git a/cw_bitcoin/lib/payjoin/manager.dart b/cw_bitcoin/lib/payjoin/manager.dart
index 95a523d89..9476ce23e 100644
--- a/cw_bitcoin/lib/payjoin/manager.dart
+++ b/cw_bitcoin/lib/payjoin/manager.dart
@@ -6,7 +6,6 @@ import 'dart:typed_data';
import 'package:bitcoin_base/bitcoin_base.dart';
import 'package:cw_bitcoin/bitcoin_wallet.dart';
import 'package:cw_bitcoin/bitcoin_wallet_addresses.dart';
-import 'package:cw_bitcoin/payjoin/payjoin_persister.dart';
import 'package:cw_bitcoin/payjoin/payjoin_receive_worker.dart';
import 'package:cw_bitcoin/payjoin/payjoin_send_worker.dart';
import 'package:cw_bitcoin/payjoin/payjoin_session_errors.dart';
@@ -17,7 +16,6 @@ import 'package:cw_core/utils/print_verbose.dart';
import 'package:payjoin_flutter/common.dart';
import 'package:payjoin_flutter/receive.dart';
import 'package:payjoin_flutter/send.dart';
-import 'package:payjoin_flutter/src/config.dart' as pj_config;
import 'package:payjoin_flutter/uri.dart' as PayjoinUri;
class PayjoinManager {
@@ -33,13 +31,11 @@ class PayjoinManager {
'https://ohttp.cakewallet.com',
];
- static String randomOhttpRelayUrl() =>
- ohttpRelayUrls[Random.secure().nextInt(ohttpRelayUrls.length)];
+ static Future randomOhttpRelayUrl() =>
+ PayjoinUri.Url.fromStr(ohttpRelayUrls[Random.secure().nextInt(ohttpRelayUrls.length)]);
static const payjoinDirectoryUrl = 'https://payjo.in';
- Future initPayjoin() => pj_config.PConfig.initializeApp();
-
Future resumeSessions() async {
final allSessions = _payjoinStorage.readAllOpenSessions(_wallet.id);
@@ -47,13 +43,13 @@ class PayjoinManager {
if (session.isSenderSession) {
printV("Resuming Payjoin Sender Session ${session.pjUri!}");
return _spawnSender(
- sender: Sender.fromJson(json: session.sender!),
+ sender: Sender.fromJson(session.sender!),
pjUri: session.pjUri!,
);
}
- final receiver = Receiver.fromJson(json: session.receiver!);
+ final receiver = Receiver.fromJson(session.receiver!);
printV("Resuming Payjoin Receiver Session ${receiver.id()}");
- return spawnReceiver(receiver: receiver);
+ return _spawnReceiver(receiver: receiver);
});
printV("Resumed ${spawnedSessions.length} Payjoin Sessions");
@@ -63,19 +59,13 @@ class PayjoinManager {
Future initSender(
String pjUriString, String originalPsbt, int networkFeesSatPerVb) async {
try {
- final pjUri =
- (await PayjoinUri.Uri.fromStr(pjUriString)).checkPjSupported();
+ final pjUri = (await PayjoinUri.Uri.fromStr(pjUriString)).checkPjSupported();
final minFeeRateSatPerKwu = BigInt.from(networkFeesSatPerVb * 250);
final senderBuilder = await SenderBuilder.fromPsbtAndUri(
psbtBase64: originalPsbt,
pjUri: pjUri,
);
- final persister = PayjoinSenderPersister.impl();
- final newSender =
- await senderBuilder.buildRecommended(minFeeRate: minFeeRateSatPerKwu);
- final senderToken = await newSender.persist(persister: persister);
-
- return Sender.load(token: senderToken, persister: persister);
+ return senderBuilder.buildRecommended(minFeeRate: minFeeRateSatPerKwu);
} catch (e) {
throw Exception('Error initializing Payjoin Sender: $e');
}
@@ -88,8 +78,7 @@ class PayjoinManager {
bool isTestnet = false,
}) async {
final pjUri = Uri.parse(pjUrl).queryParameters['pj']!;
- await _payjoinStorage.insertSenderSession(
- sender, pjUri, _wallet.id, amount);
+ await _payjoinStorage.insertSenderSession(sender, pjUri, _wallet.id, amount);
return _spawnSender(isTestnet: isTestnet, sender: sender, pjUri: pjUri);
}
@@ -121,13 +110,15 @@ class PayjoinManager {
}
} catch (e) {
_cleanupSession(pjUri);
- await _payjoinStorage.markSenderSessionUnrecoverable(pjUri, e.toString());
- completer.complete();
+ printV(e);
+ await _payjoinStorage.markSenderSessionUnrecoverable(pjUri);
+ completer.completeError(e);
}
} else if (message is PayjoinSessionError) {
_cleanupSession(pjUri);
if (message is UnrecoverableError) {
- await _payjoinStorage.markSenderSessionUnrecoverable(pjUri, message.message);
+ printV(message.message);
+ await _payjoinStorage.markSenderSessionUnrecoverable(pjUri);
completer.complete();
} else if (message is RecoverableError) {
completer.complete();
@@ -147,41 +138,40 @@ class PayjoinManager {
return completer.future;
}
- Future getUnusedReceiver(String address,
- [bool isTestnet = false]) async {
- final session = _payjoinStorage.getUnusedActiveReceiverSession(_wallet.id);
-
- if (session != null) {
- await PayjoinUri.Url.fromStr(payjoinDirectoryUrl);
-
- return Receiver.fromJson(json: session.receiver!);
- }
-
- return initReceiver(address);
- }
-
Future initReceiver(String address, [bool isTestnet = false]) async {
- final ohttpKeys = await PayjoinUri.fetchOhttpKeys(
- ohttpRelay: await randomOhttpRelayUrl(),
- payjoinDirectory: payjoinDirectoryUrl,
- );
+ try {
+ final payjoinDirectory = await PayjoinUri.Url.fromStr(payjoinDirectoryUrl);
- final newReceiver = await NewReceiver.create(
- address: address,
- network: isTestnet ? Network.testnet : Network.bitcoin,
- directory: payjoinDirectoryUrl,
- ohttpKeys: ohttpKeys,
- );
- final persister = PayjoinReceiverPersister.impl();
- final receiverToken = await newReceiver.persist(persister: persister);
- final receiver = await Receiver.load(persister: persister, token: receiverToken);
+ final ohttpKeys = await PayjoinUri.fetchOhttpKeys(
+ ohttpRelay: await randomOhttpRelayUrl(),
+ payjoinDirectory: payjoinDirectory,
+ );
- await _payjoinStorage.insertReceiverSession(receiver, _wallet.id);
+ final receiver = await Receiver.create(
+ address: address,
+ network: isTestnet ? Network.testnet : Network.bitcoin,
+ directory: payjoinDirectory,
+ ohttpKeys: ohttpKeys,
+ ohttpRelay: await randomOhttpRelayUrl(),
+ );
- return receiver;
+ await _payjoinStorage.insertReceiverSession(receiver, _wallet.id);
+
+ return receiver;
+ } catch (e) {
+ throw Exception('Error initializing Payjoin Receiver: $e');
+ }
}
- Future spawnReceiver({
+ Future spawnNewReceiver({
+ required Receiver receiver,
+ bool isTestnet = false,
+ }) async {
+ await _payjoinStorage.insertReceiverSession(receiver, _wallet.id);
+ return _spawnReceiver(isTestnet: isTestnet, receiver: receiver);
+ }
+
+ Future _spawnReceiver({
required Receiver receiver,
bool isTestnet = false,
}) async {
@@ -201,13 +191,11 @@ class PayjoinManager {
rawAmount = getOutputAmountFromTx(tx, _wallet);
break;
case PayjoinReceiverRequestTypes.checkIsOwned:
- (_wallet.walletAddresses as BitcoinWalletAddresses)
- .newPayjoinReceiver();
+ (_wallet.walletAddresses as BitcoinWalletAddresses).newPayjoinReceiver();
_payjoinStorage.markReceiverSessionInProgress(receiver.id());
final inputScript = message['input_script'] as Uint8List;
- final isOwned =
- _wallet.isMine(Script.fromRaw(byteData: inputScript));
+ final isOwned = _wallet.isMine(Script.fromRaw(bytes: inputScript));
mainToIsolateSendPort?.send({
'requestId': message['requestId'],
'result': isOwned,
@@ -216,8 +204,7 @@ class PayjoinManager {
case PayjoinReceiverRequestTypes.checkIsReceiverOutput:
final outputScript = message['output_script'] as Uint8List;
- final isReceiverOutput =
- _wallet.isMine(Script.fromRaw(byteData: outputScript));
+ final isReceiverOutput = _wallet.isMine(Script.fromRaw(bytes: outputScript));
mainToIsolateSendPort?.send({
'requestId': message['requestId'],
'result': isReceiverOutput,
@@ -226,10 +213,6 @@ class PayjoinManager {
case PayjoinReceiverRequestTypes.getCandidateInputs:
utxos = _wallet.getUtxoWithPrivateKeys();
- if (utxos.isEmpty) {
- await _wallet.updateAllUnspents();
- utxos = _wallet.getUtxoWithPrivateKeys();
- }
mainToIsolateSendPort?.send({
'requestId': message['requestId'],
'result': utxos,
@@ -254,15 +237,13 @@ class PayjoinManager {
}
} catch (e) {
_cleanupSession(receiver.id());
- await _payjoinStorage.markReceiverSessionUnrecoverable(
- receiver.id(), e.toString());
+ await _payjoinStorage.markReceiverSessionUnrecoverable(receiver.id(), e.toString());
completer.completeError(e);
}
} else if (message is PayjoinSessionError) {
_cleanupSession(receiver.id());
if (message is UnrecoverableError) {
- await _payjoinStorage.markReceiverSessionUnrecoverable(
- receiver.id(), message.message);
+ await _payjoinStorage.markReceiverSessionUnrecoverable(receiver.id(), message.message);
completer.complete();
} else if (message is RecoverableError) {
completer.complete();
diff --git a/cw_bitcoin/lib/payjoin/payjoin_persister.dart b/cw_bitcoin/lib/payjoin/payjoin_persister.dart
deleted file mode 100644
index 4e395e36a..000000000
--- a/cw_bitcoin/lib/payjoin/payjoin_persister.dart
+++ /dev/null
@@ -1,66 +0,0 @@
-import 'package:payjoin_flutter/src/generated/api/receive.dart';
-import 'package:payjoin_flutter/src/generated/api/send.dart';
-
-class PayjoinSenderPersister implements DartSenderPersister {
- static DartSenderPersister impl() {
- final impl = PayjoinSenderPersister();
- return DartSenderPersister(
- save: (sender) => impl.save(sender: sender),
- load: (token) => impl.load(token: token),
- );
- }
-
- final Map _store = {};
-
- Future save({required FfiSender sender}) async {
- final token = sender.key();
- _store[token.toBytes().toString()] = sender;
- return token;
- }
-
- Future load({required SenderToken token}) async {
- final sender = _store[token.toBytes().toString()];
- if (sender == null) {
- throw Exception('Sender not found for the provided token.');
- }
- return sender;
- }
-
- @override
- void dispose() => _store.clear();
-
- @override
- bool get isDisposed => _store.isEmpty;
-}
-
-class PayjoinReceiverPersister implements DartReceiverPersister {
- static DartReceiverPersister impl() {
- final impl = PayjoinReceiverPersister();
- return DartReceiverPersister(
- save: (receiver) => impl.save(receiver: receiver),
- load: (token) => impl.load(token: token),
- );
- }
-
- final Map _store = {};
-
- Future save({required FfiReceiver receiver}) async {
- final token = receiver.key();
- _store[token.toBytes().toString()] = receiver;
- return token;
- }
-
- Future load({required ReceiverToken token}) async {
- final receiver = _store[token.toBytes().toString()];
- if (receiver == null) {
- throw Exception('Receiver not found for the provided token.');
- }
- return receiver;
- }
-
- @override
- void dispose() => _store.clear();
-
- @override
- bool get isDisposed => _store.isEmpty;
-}
diff --git a/cw_bitcoin/lib/payjoin/payjoin_receive_worker.dart b/cw_bitcoin/lib/payjoin/payjoin_receive_worker.dart
index c56148de2..a499660b0 100644
--- a/cw_bitcoin/lib/payjoin/payjoin_receive_worker.dart
+++ b/cw_bitcoin/lib/payjoin/payjoin_receive_worker.dart
@@ -4,16 +4,14 @@ import 'dart:isolate';
import 'dart:typed_data';
import 'package:blockchain_utils/blockchain_utils.dart';
-import 'package:cw_bitcoin/payjoin/manager.dart';
import 'package:cw_bitcoin/payjoin/payjoin_session_errors.dart';
import 'package:cw_bitcoin/psbt/signer.dart';
import 'package:cw_core/utils/print_verbose.dart';
-import 'package:cw_core/utils/proxy_wrapper.dart';
+import 'package:http/http.dart' as http;
import 'package:payjoin_flutter/bitcoin_ffi.dart';
import 'package:payjoin_flutter/common.dart';
import 'package:payjoin_flutter/receive.dart';
import 'package:payjoin_flutter/src/generated/frb_generated.dart' as pj;
-import 'package:http/http.dart' as very_insecure_http_do_not_use; // for errors
enum PayjoinReceiverRequestTypes {
processOriginalTx,
@@ -29,7 +27,7 @@ class PayjoinReceiverWorker {
final pendingRequests = >{};
PayjoinReceiverWorker._(this.sendPort);
- static final client = ProxyWrapper().getHttpIOClient();
+
static Future run(List