mirror of
https://github.com/cake-tech/cake_wallet.git
synced 2025-06-28 20:39:51 +00:00
Compare commits
35 commits
Author | SHA1 | Date | |
---|---|---|---|
|
c2cca1ff37 | ||
|
5aeb6b7522 | ||
|
b3c20a5818 | ||
|
d0827dd39e | ||
|
900304d405 | ||
|
73588071ba | ||
|
1a5601f755 | ||
|
04c86c567f | ||
|
65bb917bfb | ||
|
af89603b81 | ||
|
4434ad7363 | ||
|
5082dc20f3 | ||
|
18c2ba9366 | ||
|
4b137bc968 | ||
|
21d5c51cc9 | ||
|
edaf485993 | ||
|
150becb679 | ||
|
c6cb48096d | ||
|
65402ba1eb | ||
|
17d99e5451 | ||
|
85d3e727e2 | ||
|
4fb2fc47ad | ||
|
a96b493b60 | ||
|
fe0c9ecc0e | ||
|
e5d0194f11 | ||
|
8457a45c2a | ||
|
b0edf1fe75 | ||
|
1d6e594e04 | ||
|
1134915920 | ||
|
418fdb62d3 | ||
|
d332377a2b | ||
|
b77c22b0df | ||
|
b79fb6af1f | ||
|
1fa1181c82 | ||
|
9a426ee5d4 |
272 changed files with 7808 additions and 2553 deletions
10
.github/workflows/automated_integration_test.yml
vendored
10
.github/workflows/automated_integration_test.yml
vendored
|
@ -55,7 +55,7 @@ jobs:
|
||||||
- name: Flutter action
|
- name: Flutter action
|
||||||
uses: subosito/flutter-action@v1
|
uses: subosito/flutter-action@v1
|
||||||
with:
|
with:
|
||||||
flutter-version: "3.27.4"
|
flutter-version: "3.27.0"
|
||||||
channel: stable
|
channel: stable
|
||||||
|
|
||||||
- name: Install package dependencies
|
- name: Install package dependencies
|
||||||
|
@ -153,8 +153,8 @@ jobs:
|
||||||
echo "const shortKey = '${{ secrets.SHORT_KEY }}';" >> lib/.secrets.g.dart
|
echo "const shortKey = '${{ secrets.SHORT_KEY }}';" >> lib/.secrets.g.dart
|
||||||
echo "const backupSalt = '${{ secrets.BACKUP_SALT }}';" >> lib/.secrets.g.dart
|
echo "const backupSalt = '${{ secrets.BACKUP_SALT }}';" >> lib/.secrets.g.dart
|
||||||
echo "const backupKeychainSalt = '${{ secrets.BACKUP_KEY_CHAIN_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 changeNowCakeWalletApiKey = '${{ secrets.CHANGE_NOW_API_KEY }}';" >> lib/.secrets.g.dart
|
||||||
echo "const changeNowApiKeyDesktop = '${{ secrets.CHANGE_NOW_API_KEY_DESKTOP }}';" >> 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 wyreSecretKey = '${{ secrets.WYRE_SECRET_KEY }}';" >> lib/.secrets.g.dart
|
||||||
echo "const wyreApiKey = '${{ secrets.WYRE_API_KEY }}';" >> lib/.secrets.g.dart
|
echo "const wyreApiKey = '${{ secrets.WYRE_API_KEY }}';" >> lib/.secrets.g.dart
|
||||||
echo "const wyreAccountId = '${{ secrets.WYRE_ACCOUNT_ID }}';" >> lib/.secrets.g.dart
|
echo "const 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 ioniaClientId = '${{ secrets.IONIA_CLIENT_ID }}';" >> lib/.secrets.g.dart
|
||||||
echo "const twitterBearerToken = '${{ secrets.TWITTER_BEARER_TOKEN }}';" >> 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 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 trocadorExchangeMarkup = '${{ secrets.TROCADOR_EXCHANGE_MARKUP }}';" >> lib/.secrets.g.dart
|
||||||
echo "const anonPayReferralCode = '${{ secrets.ANON_PAY_REFERRAL_CODE }}';" >> lib/.secrets.g.dart
|
echo "const anonPayReferralCode = '${{ secrets.ANON_PAY_REFERRAL_CODE }}';" >> lib/.secrets.g.dart
|
||||||
echo "const fiatApiKey = '${{ secrets.FIAT_API_KEY }}';" >> lib/.secrets.g.dart
|
echo "const 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 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 moralisApiKey = '${{ secrets.MORALIS_API_KEY }}';" >> cw_evm/lib/.secrets.g.dart
|
||||||
echo "const chatwootWebsiteToken = '${{ secrets.CHATWOOT_WEBSITE_TOKEN }}';" >> 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 robinhoodApplicationId = '${{ secrets.ROBINHOOD_APPLICATION_ID }}';" >> lib/.secrets.g.dart
|
||||||
echo "const exchangeHelperApiKey = '${{ secrets.ROBINHOOD_CID_CLIENT_SECRET }}';" >> lib/.secrets.g.dart
|
echo "const exchangeHelperApiKey = '${{ secrets.ROBINHOOD_CID_CLIENT_SECRET }}';" >> lib/.secrets.g.dart
|
||||||
echo "const walletConnectProjectId = '${{ secrets.WALLET_CONNECT_PROJECT_ID }}';" >> lib/.secrets.g.dart
|
echo "const walletConnectProjectId = '${{ secrets.WALLET_CONNECT_PROJECT_ID }}';" >> lib/.secrets.g.dart
|
||||||
|
|
21
.github/workflows/no_http_imports.yaml
vendored
Normal file
21
.github/workflows/no_http_imports.yaml
vendored
Normal file
|
@ -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
|
||||||
|
|
2
.github/workflows/no_print_in_dart.yaml
vendored
2
.github/workflows/no_print_in_dart.yaml
vendored
|
@ -15,5 +15,5 @@ jobs:
|
||||||
[[ "x$GIT_GREP_OUT" == "x" ]] && exit 0
|
[[ "x$GIT_GREP_OUT" == "x" ]] && exit 0
|
||||||
echo "$GIT_GREP_OUT"
|
echo "$GIT_GREP_OUT"
|
||||||
echo "There are .dart files which use print() statements"
|
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
|
exit 1
|
||||||
|
|
15
.github/workflows/pr_test_build_android.yml
vendored
15
.github/workflows/pr_test_build_android.yml
vendored
|
@ -9,7 +9,7 @@ jobs:
|
||||||
PR_test_build:
|
PR_test_build:
|
||||||
runs-on: linux-amd64
|
runs-on: linux-amd64
|
||||||
container:
|
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:
|
env:
|
||||||
STORE_PASS: test@cake_wallet
|
STORE_PASS: test@cake_wallet
|
||||||
KEY_PASS: test@cake_wallet
|
KEY_PASS: test@cake_wallet
|
||||||
|
@ -98,8 +98,8 @@ jobs:
|
||||||
else
|
else
|
||||||
echo "const backupKeychainSalt = '${{ secrets.BACKUP_KEY_CHAIN_SALT }}';" >> lib/.secrets.g.dart
|
echo "const backupKeychainSalt = '${{ secrets.BACKUP_KEY_CHAIN_SALT }}';" >> lib/.secrets.g.dart
|
||||||
fi
|
fi
|
||||||
echo "const changeNowApiKey = '${{ secrets.CHANGE_NOW_API_KEY }}';" >> lib/.secrets.g.dart
|
echo "const changeNowCakeWalletApiKey = '${{ secrets.CHANGE_NOW_API_KEY }}';" >> lib/.secrets.g.dart
|
||||||
echo "const changeNowApiKeyDesktop = '${{ secrets.CHANGE_NOW_API_KEY_DESKTOP }}';" >> 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 wyreSecretKey = '${{ secrets.WYRE_SECRET_KEY }}';" >> lib/.secrets.g.dart
|
||||||
echo "const wyreApiKey = '${{ secrets.WYRE_API_KEY }}';" >> lib/.secrets.g.dart
|
echo "const wyreApiKey = '${{ secrets.WYRE_API_KEY }}';" >> lib/.secrets.g.dart
|
||||||
echo "const wyreAccountId = '${{ secrets.WYRE_ACCOUNT_ID }}';" >> lib/.secrets.g.dart
|
echo "const 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 ioniaClientId = '${{ secrets.IONIA_CLIENT_ID }}';" >> lib/.secrets.g.dart
|
||||||
echo "const twitterBearerToken = '${{ secrets.TWITTER_BEARER_TOKEN }}';" >> 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 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 trocadorExchangeMarkup = '${{ secrets.TROCADOR_EXCHANGE_MARKUP }}';" >> lib/.secrets.g.dart
|
||||||
echo "const anonPayReferralCode = '${{ secrets.ANON_PAY_REFERRAL_CODE }}';" >> lib/.secrets.g.dart
|
echo "const anonPayReferralCode = '${{ secrets.ANON_PAY_REFERRAL_CODE }}';" >> lib/.secrets.g.dart
|
||||||
echo "const fiatApiKey = '${{ secrets.FIAT_API_KEY }}';" >> lib/.secrets.g.dart
|
echo "const 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 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 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 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 robinhoodApplicationId = '${{ secrets.ROBINHOOD_APPLICATION_ID }}';" >> lib/.secrets.g.dart
|
||||||
echo "const exchangeHelperApiKey = '${{ secrets.ROBINHOOD_CID_CLIENT_SECRET }}';" >> lib/.secrets.g.dart
|
echo "const exchangeHelperApiKey = '${{ secrets.ROBINHOOD_CID_CLIENT_SECRET }}';" >> lib/.secrets.g.dart
|
||||||
echo "const walletConnectProjectId = '${{ secrets.WALLET_CONNECT_PROJECT_ID }}';" >> lib/.secrets.g.dart
|
echo "const walletConnectProjectId = '${{ secrets.WALLET_CONNECT_PROJECT_ID }}';" >> lib/.secrets.g.dart
|
||||||
|
@ -253,6 +255,11 @@ jobs:
|
||||||
|
|
||||||
- name: Build generated code
|
- name: Build generated code
|
||||||
run: |
|
run: |
|
||||||
|
flutter --version
|
||||||
|
flutter clean
|
||||||
|
rm -rf .dart_tool
|
||||||
|
rm pubspec.lock
|
||||||
|
flutter pub get
|
||||||
./model_generator.sh async
|
./model_generator.sh async
|
||||||
|
|
||||||
- name: Generate key properties
|
- name: Generate key properties
|
||||||
|
|
10
.github/workflows/pr_test_build_linux.yml
vendored
10
.github/workflows/pr_test_build_linux.yml
vendored
|
@ -9,7 +9,7 @@ jobs:
|
||||||
PR_test_build:
|
PR_test_build:
|
||||||
runs-on: linux-amd64
|
runs-on: linux-amd64
|
||||||
container:
|
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:
|
env:
|
||||||
STORE_PASS: test@cake_wallet
|
STORE_PASS: test@cake_wallet
|
||||||
KEY_PASS: test@cake_wallet
|
KEY_PASS: test@cake_wallet
|
||||||
|
@ -91,8 +91,8 @@ jobs:
|
||||||
else
|
else
|
||||||
echo "const backupKeychainSalt = '${{ secrets.BACKUP_KEY_CHAIN_SALT }}';" >> lib/.secrets.g.dart
|
echo "const backupKeychainSalt = '${{ secrets.BACKUP_KEY_CHAIN_SALT }}';" >> lib/.secrets.g.dart
|
||||||
fi
|
fi
|
||||||
echo "const changeNowApiKey = '${{ secrets.CHANGE_NOW_API_KEY }}';" >> lib/.secrets.g.dart
|
echo "const changeNowCakeWalletApiKey = '${{ secrets.CHANGE_NOW_API_KEY }}';" >> lib/.secrets.g.dart
|
||||||
echo "const changeNowApiKeyDesktop = '${{ secrets.CHANGE_NOW_API_KEY_DESKTOP }}';" >> 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 wyreSecretKey = '${{ secrets.WYRE_SECRET_KEY }}';" >> lib/.secrets.g.dart
|
||||||
echo "const wyreApiKey = '${{ secrets.WYRE_API_KEY }}';" >> lib/.secrets.g.dart
|
echo "const wyreApiKey = '${{ secrets.WYRE_API_KEY }}';" >> lib/.secrets.g.dart
|
||||||
echo "const wyreAccountId = '${{ secrets.WYRE_ACCOUNT_ID }}';" >> lib/.secrets.g.dart
|
echo "const 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 ioniaClientId = '${{ secrets.IONIA_CLIENT_ID }}';" >> lib/.secrets.g.dart
|
||||||
echo "const twitterBearerToken = '${{ secrets.TWITTER_BEARER_TOKEN }}';" >> 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 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 trocadorExchangeMarkup = '${{ secrets.TROCADOR_EXCHANGE_MARKUP }}';" >> lib/.secrets.g.dart
|
||||||
echo "const anonPayReferralCode = '${{ secrets.ANON_PAY_REFERRAL_CODE }}';" >> lib/.secrets.g.dart
|
echo "const anonPayReferralCode = '${{ secrets.ANON_PAY_REFERRAL_CODE }}';" >> lib/.secrets.g.dart
|
||||||
echo "const fiatApiKey = '${{ secrets.FIAT_API_KEY }}';" >> lib/.secrets.g.dart
|
echo "const 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 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 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 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 robinhoodApplicationId = '${{ secrets.ROBINHOOD_APPLICATION_ID }}';" >> lib/.secrets.g.dart
|
||||||
echo "const exchangeHelperApiKey = '${{ secrets.ROBINHOOD_CID_CLIENT_SECRET }}';" >> lib/.secrets.g.dart
|
echo "const exchangeHelperApiKey = '${{ secrets.ROBINHOOD_CID_CLIENT_SECRET }}';" >> lib/.secrets.g.dart
|
||||||
echo "const walletConnectProjectId = '${{ secrets.WALLET_CONNECT_PROJECT_ID }}';" >> lib/.secrets.g.dart
|
echo "const walletConnectProjectId = '${{ secrets.WALLET_CONNECT_PROJECT_ID }}';" >> lib/.secrets.g.dart
|
||||||
|
|
18
Dockerfile
18
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
|
# Heavily inspired by cirrusci images
|
||||||
# https://github.com/cirruslabs/docker-images-android/blob/master/sdk/tools/Dockerfile
|
# 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
|
ENV GOLANG_VERSION=1.24.1
|
||||||
|
|
||||||
# Pin Flutter version to latest known-working version
|
# 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
|
# Pin Android Studio, platform, and build tools versions to latest known-working version
|
||||||
# Comes from https://developer.android.com/studio/#command-tools
|
# 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
|
# Comes from https://developer.android.com/studio/releases/build-tools
|
||||||
ENV ANDROID_PLATFORM_VERSION=35
|
ENV ANDROID_PLATFORM_VERSION=35
|
||||||
ENV ANDROID_BUILD_TOOLS_VERSION=34.0.0
|
ENV ANDROID_BUILD_TOOLS_VERSION=34.0.0
|
||||||
|
@ -164,9 +164,12 @@ RUN (addgroup kvm || true) && \
|
||||||
ENV PATH=${HOME}/.cargo/bin:${PATH}
|
ENV PATH=${HOME}/.cargo/bin:${PATH}
|
||||||
RUN curl https://sh.rustup.rs -sSf | bash -s -- -y && \
|
RUN curl https://sh.rustup.rs -sSf | bash -s -- -y && \
|
||||||
cargo install cargo-ndk && \
|
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; \
|
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 \
|
do \
|
||||||
rustup target add --toolchain stable $target; \
|
rustup target add --toolchain $toolchain $target; \
|
||||||
|
done \
|
||||||
done
|
done
|
||||||
|
|
||||||
# Download and install Flutter
|
# Download and install Flutter
|
||||||
|
@ -175,8 +178,11 @@ ENV FLUTTER_HOME=${HOME}/sdks/flutter/${FLUTTER_VERSION}
|
||||||
ENV FLUTTER_ROOT=$FLUTTER_HOME
|
ENV FLUTTER_ROOT=$FLUTTER_HOME
|
||||||
ENV PATH=${PATH}:${FLUTTER_HOME}/bin:${FLUTTER_HOME}/bin/cache/dart-sdk/bin
|
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} \
|
RUN git clone --branch ${FLUTTER_VERSION} https://github.com/flutter/flutter.git ${FLUTTER_HOME} && \
|
||||||
&& yes | flutter doctor --android-licenses \
|
cd ${FLUTTER_HOME} && \
|
||||||
|
git fetch -a
|
||||||
|
|
||||||
|
RUN yes | flutter doctor --android-licenses \
|
||||||
&& flutter doctor \
|
&& flutter doctor \
|
||||||
&& chown -R root:root ${FLUTTER_HOME}
|
&& chown -R root:root ${FLUTTER_HOME}
|
||||||
|
|
||||||
|
|
10
assets/images/history.svg
Normal file
10
assets/images/history.svg
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
<svg width="128" height="128" viewBox="0 0 128 128" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<g clip-path="url(#clip0_67_21)">
|
||||||
|
<path d="M70.4443 8.33334C101.173 8.33336 126 33.1598 126 63.889C126 94.6181 101.173 119.445 70.4443 119.445C53.0832 119.445 37.4581 111.458 27.3887 98.9583L37.2842 89.0628C44.7495 98.9587 56.9027 105.556 70.4443 105.556C93.361 105.556 112.111 86.8056 112.111 63.889C112.111 40.9723 93.361 22.222 70.4443 22.222C56.9027 22.222 44.7495 28.8193 37.2842 38.7152C33.1175 44.0971 30.3405 50.521 29.2988 57.639C28.9516 59.7223 28.7773 61.8057 28.7773 63.889H42.667L21.833 84.722L1 63.889H14.8887C14.8887 61.9793 14.8891 60.2431 15.2363 58.3333C15.7572 52.7778 17.146 47.5692 19.0557 42.5345C27.3891 22.5694 47.3542 8.33334 70.4443 8.33334ZM75.6523 35.4163V60.9378L93.708 78.9925L83.8125 88.889L61.7637 66.6663V35.4163H75.6523Z" fill="white"/>
|
||||||
|
</g>
|
||||||
|
<defs>
|
||||||
|
<clipPath id="clip0_67_21">
|
||||||
|
<rect width="128" height="128" fill="white"/>
|
||||||
|
</clipPath>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 984 B |
3
assets/images/notif.svg
Normal file
3
assets/images/notif.svg
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
<svg width="128" height="128" viewBox="0 0 128 128" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M77.9219 103.359C77.9219 111.953 71.0469 119 62.4531 119C53.8594 119 46.8125 111.953 46.8125 103.359C46.8125 102.672 46.8125 102.156 46.9844 101.641H9V87.8906H21.0312V51.9688C21.0312 28.25 40.2812 9 64 9C87.7188 9 106.969 28.25 106.969 51.9688V87.8906H119V101.641H77.9219V103.359ZM64 22.75C47.8438 22.75 34.7812 35.8125 34.7812 51.9688V87.8906H93.2188V51.9688C93.2188 35.8125 80.1562 22.75 64 22.75Z" fill="white"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 532 B |
BIN
assets/images/qr-cake.png
Normal file
BIN
assets/images/qr-cake.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 72 KiB |
76
assets/images/tor_logo.svg
Normal file
76
assets/images/tor_logo.svg
Normal file
|
@ -0,0 +1,76 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Generator: Adobe Illustrator 17.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||||
|
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||||
|
viewBox="0 0 150.5 91.1" enable-background="new 0 0 150.5 91.1" xml:space="preserve">
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<g id="layer3_22_" transform="translate(-92,-63.999774)">
|
||||||
|
<g id="layer5_22_">
|
||||||
|
<g id="path2554_38_">
|
||||||
|
<path fill="#68B044" d="M176.9,70.8l-2.4,9.6c3.4-6.8,8.9-11.9,15.2-16.4c-4.6,5.3-8.8,10.6-11.3,16
|
||||||
|
c4.3-6.1,10.1-9.4,16.6-11.7c-8.7,7.7-15.6,16.1-20.8,24.4l-4.2-1.8C170.7,84.2,173.2,77.4,176.9,70.8L176.9,70.8z"/>
|
||||||
|
</g>
|
||||||
|
<g id="path2534_13_">
|
||||||
|
<path fill="#F5F8DE" d="M165.8,89.1l7.9,3.3c0,2-0.2,8.2,1.1,10c13.2,17,11,51.2-2.7,52c-20.8,0-28.8-14.1-28.8-27.1
|
||||||
|
c0-11.9,14.2-19.7,22.7-26.7C168.3,98.7,167.9,94.5,165.8,89.1L165.8,89.1z"/>
|
||||||
|
</g>
|
||||||
|
<g id="path2536_23_">
|
||||||
|
<path fill="#7E4798" d="M173.7,92.3l2.9,1.5c-0.3,1.9,0.1,6.1,2,7.1c8.4,5.2,16.2,10.8,19.3,16.5c11,19.9-7.7,38.4-24,36.6
|
||||||
|
c8.8-6.5,11.4-19.9,8.1-34.6c-1.3-5.7-3.4-10.9-7.1-16.8C173.3,99.9,173.9,96.3,173.7,92.3L173.7,92.3z"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g id="layer4_22_">
|
||||||
|
<g id="path2540_23_">
|
||||||
|
<path fill="#010101" d="M170.5,101.8c-0.6,3.1-1.3,8.7-4,10.8c-1.1,0.8-2.3,1.6-3.5,2.4c-4.9,3.3-9.7,6.4-11.9,14.3
|
||||||
|
c-0.5,1.7-0.1,3.5,0.3,5.2c1.2,4.9,4.6,10.1,7.3,13.2c0,0.1,0.5,0.5,0.5,0.6c2.2,2.6,2.9,3.4,11.3,5.3l-0.2,0.9
|
||||||
|
c-5.1-1.3-9.2-2.6-11.9-5.6c0-0.1-0.5-0.5-0.5-0.5c-2.8-3.2-6.3-8.6-7.5-13.7c-0.5-2-0.9-3.6-0.3-5.7
|
||||||
|
c2.3-8.2,7.3-11.5,12.3-14.9c1.1-0.7,2.5-1.4,3.6-2.3C168.3,110.3,169.4,105.6,170.5,101.8L170.5,101.8z"/>
|
||||||
|
</g>
|
||||||
|
<g id="path2542_23_">
|
||||||
|
<path fill="#010101" d="M172.7,114.8c0.1,3.5-0.3,5.3,0.6,7.8c0.5,1.5,2.4,3.5,2.9,5.5c0.7,2.6,1.5,5.5,1.5,7.3
|
||||||
|
c0,2-0.1,5.8-1,9.8c-0.7,3.3-2.2,6.2-4.8,7.8c-2.7-0.5-5.8-1.5-7.6-3.1c-3.6-3.1-6.7-8.3-7.1-12.8c-0.3-3.7,3.1-9.2,7.9-11.9
|
||||||
|
c4-2.4,5-5,5.9-9.4c-1.2,3.8-2.4,6.9-6.3,9c-5.7,3-8.6,7.9-8.3,12.7c0.4,6.1,2.8,10.2,7.6,13.5c2,1.4,5.8,2.9,8.2,3.3v-0.3
|
||||||
|
c1.8-0.3,4.1-3.3,5.3-7.2c1-3.6,1.4-8.1,1.3-11c-0.1-1.7-0.8-5.3-2.2-8.6c-0.7-1.8-1.9-3.6-2.6-4.9
|
||||||
|
C173.1,120.8,173.1,118,172.7,114.8z"/>
|
||||||
|
</g>
|
||||||
|
<g id="path2544_23_">
|
||||||
|
<path fill="#010101" d="M172.1,128.5c0.1,2.4,1,5.4,1.4,8.5c0.3,2.3,0.2,4.6,0.1,6.6c-0.1,2.3-0.8,6.5-1.9,8.6
|
||||||
|
c-1-0.5-1.4-1-2.1-1.8c-0.8-1.1-1.4-2.3-1.9-3.6c-0.4-1-0.9-2.2-1.1-3.5c-0.3-2-0.2-5.2,2.1-8.4c1.8-2.6,2.2-2.8,2.8-5.7
|
||||||
|
c-0.8,2.6-1.4,2.9-3.3,5.1c-2.1,2.4-2.4,6-2.4,8.9c0,1.2,0.5,2.6,1,3.8c0.5,1.3,1,2.7,1.7,3.7c1.1,1.6,2.5,2.6,3.2,2.7
|
||||||
|
c0,0,0,0,0,0c0,0,0,0,0,0v-0.1c1.3-1.5,2.1-2.9,2.4-4.4c0.3-1.8,0.4-3.5,0.6-5.6c0.2-1.8,0.1-4.1-0.4-6.5
|
||||||
|
C173.7,133.8,172.6,130.7,172.1,128.5L172.1,128.5z"/>
|
||||||
|
</g>
|
||||||
|
<g id="path2550_23_">
|
||||||
|
<path fill="#010101" d="M172.5,99c0.1,3.5,0.3,10,1.3,12.6c0.3,0.9,2.8,4.7,4.5,9.4c1.2,3.2,1.5,6.2,1.7,7.1
|
||||||
|
c0.8,3.8-0.2,10.3-1.5,16.4c-0.7,3.3-3,7.4-5.6,9l-0.5,0.9c1.5-0.1,5.1-3.6,6.4-8.1c2.2-7.5,3-11,2-19.4
|
||||||
|
c-0.1-0.8-0.5-3.6-1.8-6.5c-1.9-4.5-4.6-8.8-4.9-9.7C173.4,109.3,172.6,103.1,172.5,99L172.5,99z"/>
|
||||||
|
</g>
|
||||||
|
<g id="path2552_23_">
|
||||||
|
<path fill="#010101" d="M173.7,92.6c-0.2,3.6-0.2,6.4,0.4,9.1c0.7,2.9,4.5,7.1,6.1,11.9c3,9.2,2.2,21.2,0.1,30.5
|
||||||
|
c-0.8,3.3-4.6,8.1-8.5,9.6l2.8,0.7c1.5-0.1,5.5-3.8,7.1-8c2.5-6.7,3-14.6,2-23c-0.1-0.8-1.4-8-2.7-11
|
||||||
|
c-1.8-4.5-4.7-7.7-5.7-10.5c-0.8-2.1-1.1-7.7-0.6-8.8L173.7,92.6z"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g id="path2528_18_">
|
||||||
|
<path fill="#7E4798" d="M3.8,8.8h54.1c2,0,3.8,1.7,3.8,3.8v16c0,2.1-1.8,3.8-3.8,3.8H46.8c-2.5,0-3.6,1.4-3.6,3v52.3
|
||||||
|
c0,1.8-1.4,3.1-3.1,3.1H21.7c-1.7,0-3-1.3-3-3.1V34.9c0-1.6-1.5-2.6-2.6-2.6H3.8c-2.1,0-3.8-1.7-3.8-3.8v-16
|
||||||
|
C0,10.5,1.7,8.8,3.8,8.8z"/>
|
||||||
|
</g>
|
||||||
|
<g id="path2532_18_">
|
||||||
|
<path fill="#7E4798" d="M142.6,30h4.8c1.8,0,3.2,1.4,3.2,3.1v17.1c0,2.2,0.1,3.1-2.6,3.1c-5.3,0-7.7,2.8-7.7,5.9v28.9
|
||||||
|
c0,1.3-1.3,2.5-2.8,2.5h-17.2c-1.5,0-2.8-1.1-2.8-2.5V55.2c0-0.6,0-1.4,0.1-1.9c0.9-12.2,10.5-21.9,22.6-23.2
|
||||||
|
C140.5,30.1,141.9,30,142.6,30L142.6,30z"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path fill="#010101" d="M101.7,46.3c-2.9-2.6-6.5-4.8-10.3-6.9c-1.7-0.9-6.9-5-5.1-10.8l-13.1-5.4l-0.9,0.7
|
||||||
|
c4.4,7.9,2.1,12.1-0.1,13.5c-4.4,3-10.8,6.8-13.9,10.1c-6.1,6.3-7.9,12.3-7.3,20.1c0.6,10.1,7.9,18.5,17.8,21.8
|
||||||
|
c4.3,1.4,8.3,1.6,12.7,1.6c7.1,0,14.5-1.9,19.8-6.3c5.7-4.7,9-11.8,9-19.1C110.3,58.3,107.2,51.3,101.7,46.3z M99.8,83.2
|
||||||
|
c-4.9,4-13.7,6.8-18.4,6.6c-5.2-0.3-10.3-1.1-14.8-3.3c-7.9-3.8-13.1-12.1-13.5-18.8C52.4,54,59,50.1,65.1,45.1
|
||||||
|
c3.4-2.8,8.2-4.2,10.9-9.2c0.5-1.1,0.8-3.5,0.2-6c-0.3-0.9-1.5-3.9-2-4.6l9.8,4.3c-1.2,4.5,2.5,9.2,5.5,10.9
|
||||||
|
c3,1.7,7.7,4.9,10.6,7.5c5.1,4.5,7.7,10.9,7.7,17.6C107.8,72.3,105,78.9,99.8,83.2z"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 4.8 KiB |
BIN
assets/images/usdtbsc_icon.png
Normal file
BIN
assets/images/usdtbsc_icon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 920 B |
|
@ -1,3 +1,4 @@
|
||||||
New themes and UI/UX improvements
|
Add built-in Tor support (experimental)
|
||||||
Ledger flow enhancements
|
Ledger improvements
|
||||||
|
UI/UX improvements
|
||||||
Bug fixes
|
Bug fixes
|
|
@ -1,5 +1,9 @@
|
||||||
New themes and UI/UX improvements
|
Add built-in Tor support (experimental)
|
||||||
Silent Payments scanning fix
|
Add dEuro investments
|
||||||
Payjoin enhancements
|
Solana fixes/enhancements
|
||||||
Ledger flow enhancements
|
Polygon fixes/enhancements
|
||||||
|
WalletConnect improvements
|
||||||
|
Ledger improvements
|
||||||
|
Payjoin improvements
|
||||||
|
UI/UX improvements
|
||||||
Bug fixes
|
Bug fixes
|
|
@ -17,16 +17,21 @@ BitcoinBaseAddress addressFromScript(Script script,
|
||||||
|
|
||||||
switch (addressType) {
|
switch (addressType) {
|
||||||
case P2pkhAddressType.p2pkh:
|
case P2pkhAddressType.p2pkh:
|
||||||
return P2pkhAddress.fromScriptPubkey(script: script);
|
return P2pkhAddress.fromScriptPubkey(
|
||||||
|
script: script, network: BitcoinNetwork.mainnet);
|
||||||
case P2shAddressType.p2pkhInP2sh:
|
case P2shAddressType.p2pkhInP2sh:
|
||||||
case P2shAddressType.p2pkInP2sh:
|
case P2shAddressType.p2pkInP2sh:
|
||||||
return P2shAddress.fromScriptPubkey(script: script);
|
return P2shAddress.fromScriptPubkey(
|
||||||
case SegwitAddressType.p2wpkh:
|
script: script, network: BitcoinNetwork.mainnet);
|
||||||
return P2wpkhAddress.fromScriptPubkey(script: script);
|
case SegwitAddresType.p2wpkh:
|
||||||
case SegwitAddressType.p2wsh:
|
return P2wpkhAddress.fromScriptPubkey(
|
||||||
return P2wshAddress.fromScriptPubkey(script: script);
|
script: script, network: BitcoinNetwork.mainnet);
|
||||||
case SegwitAddressType.p2tr:
|
case SegwitAddresType.p2wsh:
|
||||||
return P2trAddress.fromScriptPubkey(script: script);
|
return P2wshAddress.fromScriptPubkey(
|
||||||
|
script: script, network: BitcoinNetwork.mainnet);
|
||||||
|
case SegwitAddresType.p2tr:
|
||||||
|
return P2trAddress.fromScriptPubkey(
|
||||||
|
script: script, network: BitcoinNetwork.mainnet);
|
||||||
}
|
}
|
||||||
|
|
||||||
throw ArgumentError("Invalid script");
|
throw ArgumentError("Invalid script");
|
||||||
|
|
|
@ -82,7 +82,7 @@ class BitcoinAddressRecord extends BaseBitcoinAddressRecord {
|
||||||
type: decoded['type'] != null && decoded['type'] != ''
|
type: decoded['type'] != null && decoded['type'] != ''
|
||||||
? BitcoinAddressType.values
|
? BitcoinAddressType.values
|
||||||
.firstWhere((type) => type.toString() == decoded['type'] as String)
|
.firstWhere((type) => type.toString() == decoded['type'] as String)
|
||||||
: SegwitAddressType.p2wpkh,
|
: SegwitAddresType.p2wpkh,
|
||||||
scriptHash: decoded['scriptHash'] as String?,
|
scriptHash: decoded['scriptHash'] as String?,
|
||||||
network: network,
|
network: network,
|
||||||
);
|
);
|
||||||
|
|
|
@ -36,9 +36,9 @@ class BitcoinReceivePageOption implements ReceivePageOption {
|
||||||
BitcoinAddressType toType() {
|
BitcoinAddressType toType() {
|
||||||
switch (this) {
|
switch (this) {
|
||||||
case BitcoinReceivePageOption.p2tr:
|
case BitcoinReceivePageOption.p2tr:
|
||||||
return SegwitAddressType.p2tr;
|
return SegwitAddresType.p2tr;
|
||||||
case BitcoinReceivePageOption.p2wsh:
|
case BitcoinReceivePageOption.p2wsh:
|
||||||
return SegwitAddressType.p2wsh;
|
return SegwitAddresType.p2wsh;
|
||||||
case BitcoinReceivePageOption.p2pkh:
|
case BitcoinReceivePageOption.p2pkh:
|
||||||
return P2pkhAddressType.p2pkh;
|
return P2pkhAddressType.p2pkh;
|
||||||
case BitcoinReceivePageOption.p2sh:
|
case BitcoinReceivePageOption.p2sh:
|
||||||
|
@ -46,20 +46,20 @@ class BitcoinReceivePageOption implements ReceivePageOption {
|
||||||
case BitcoinReceivePageOption.silent_payments:
|
case BitcoinReceivePageOption.silent_payments:
|
||||||
return SilentPaymentsAddresType.p2sp;
|
return SilentPaymentsAddresType.p2sp;
|
||||||
case BitcoinReceivePageOption.mweb:
|
case BitcoinReceivePageOption.mweb:
|
||||||
return SegwitAddressType.mweb;
|
return SegwitAddresType.mweb;
|
||||||
case BitcoinReceivePageOption.p2wpkh:
|
case BitcoinReceivePageOption.p2wpkh:
|
||||||
default:
|
default:
|
||||||
return SegwitAddressType.p2wpkh;
|
return SegwitAddresType.p2wpkh;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
factory BitcoinReceivePageOption.fromType(BitcoinAddressType type) {
|
factory BitcoinReceivePageOption.fromType(BitcoinAddressType type) {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case SegwitAddressType.p2tr:
|
case SegwitAddresType.p2tr:
|
||||||
return BitcoinReceivePageOption.p2tr;
|
return BitcoinReceivePageOption.p2tr;
|
||||||
case SegwitAddressType.p2wsh:
|
case SegwitAddresType.p2wsh:
|
||||||
return BitcoinReceivePageOption.p2wsh;
|
return BitcoinReceivePageOption.p2wsh;
|
||||||
case SegwitAddressType.mweb:
|
case SegwitAddresType.mweb:
|
||||||
return BitcoinReceivePageOption.mweb;
|
return BitcoinReceivePageOption.mweb;
|
||||||
case P2pkhAddressType.p2pkh:
|
case P2pkhAddressType.p2pkh:
|
||||||
return BitcoinReceivePageOption.p2pkh;
|
return BitcoinReceivePageOption.p2pkh;
|
||||||
|
@ -67,7 +67,7 @@ class BitcoinReceivePageOption implements ReceivePageOption {
|
||||||
return BitcoinReceivePageOption.p2sh;
|
return BitcoinReceivePageOption.p2sh;
|
||||||
case SilentPaymentsAddresType.p2sp:
|
case SilentPaymentsAddresType.p2sp:
|
||||||
return BitcoinReceivePageOption.silent_payments;
|
return BitcoinReceivePageOption.silent_payments;
|
||||||
case SegwitAddressType.p2wpkh:
|
case SegwitAddresType.p2wpkh:
|
||||||
default:
|
default:
|
||||||
return BitcoinReceivePageOption.p2wpkh;
|
return BitcoinReceivePageOption.p2wpkh;
|
||||||
}
|
}
|
||||||
|
|
|
@ -73,8 +73,9 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
|
||||||
initialBalance: initialBalance,
|
initialBalance: initialBalance,
|
||||||
seedBytes: seedBytes,
|
seedBytes: seedBytes,
|
||||||
encryptionFileUtils: encryptionFileUtils,
|
encryptionFileUtils: encryptionFileUtils,
|
||||||
currency:
|
currency: networkParam == BitcoinNetwork.testnet
|
||||||
networkParam == BitcoinNetwork.testnet ? CryptoCurrency.tbtc : CryptoCurrency.btc,
|
? CryptoCurrency.tbtc
|
||||||
|
: CryptoCurrency.btc,
|
||||||
alwaysScan: alwaysScan,
|
alwaysScan: alwaysScan,
|
||||||
) {
|
) {
|
||||||
// in a standard BIP44 wallet, mainHd derivation path = m/84'/0'/0'/0 (account 0, index unspecified here)
|
// in a standard BIP44 wallet, mainHd derivation path = m/84'/0'/0'/0 (account 0, index unspecified here)
|
||||||
|
@ -93,12 +94,14 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
|
||||||
mainHd: hd,
|
mainHd: hd,
|
||||||
sideHd: accountHD.childKey(Bip32KeyIndex(1)),
|
sideHd: accountHD.childKey(Bip32KeyIndex(1)),
|
||||||
network: networkParam ?? network,
|
network: networkParam ?? network,
|
||||||
masterHd: seedBytes != null ? Bip32Slip10Secp256k1.fromSeed(seedBytes) : null,
|
masterHd:
|
||||||
|
seedBytes != null ? Bip32Slip10Secp256k1.fromSeed(seedBytes) : null,
|
||||||
isHardwareWallet: walletInfo.isHardwareWallet,
|
isHardwareWallet: walletInfo.isHardwareWallet,
|
||||||
payjoinManager: payjoinManager);
|
payjoinManager: payjoinManager);
|
||||||
|
|
||||||
autorun((_) {
|
autorun((_) {
|
||||||
this.walletAddresses.isEnabledAutoGenerateSubaddress = this.isEnabledAutoGenerateSubaddress;
|
this.walletAddresses.isEnabledAutoGenerateSubaddress =
|
||||||
|
this.isEnabledAutoGenerateSubaddress;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -133,7 +136,8 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
|
||||||
break;
|
break;
|
||||||
case DerivationType.electrum:
|
case DerivationType.electrum:
|
||||||
default:
|
default:
|
||||||
seedBytes = await mnemonicToSeedBytes(mnemonic, passphrase: passphrase ?? "");
|
seedBytes =
|
||||||
|
await mnemonicToSeedBytes(mnemonic, passphrase: passphrase ?? "");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -206,8 +210,10 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
|
||||||
walletInfo.derivationInfo ??= DerivationInfo();
|
walletInfo.derivationInfo ??= DerivationInfo();
|
||||||
|
|
||||||
// set the default if not present:
|
// set the default if not present:
|
||||||
walletInfo.derivationInfo!.derivationPath ??= snp?.derivationPath ?? electrum_path;
|
walletInfo.derivationInfo!.derivationPath ??=
|
||||||
walletInfo.derivationInfo!.derivationType ??= snp?.derivationType ?? DerivationType.electrum;
|
snp?.derivationPath ?? electrum_path;
|
||||||
|
walletInfo.derivationInfo!.derivationType ??=
|
||||||
|
snp?.derivationType ?? DerivationType.electrum;
|
||||||
|
|
||||||
Uint8List? seedBytes = null;
|
Uint8List? seedBytes = null;
|
||||||
final mnemonic = keysData.mnemonic;
|
final mnemonic = keysData.mnemonic;
|
||||||
|
@ -216,7 +222,8 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
|
||||||
if (mnemonic != null) {
|
if (mnemonic != null) {
|
||||||
switch (walletInfo.derivationInfo!.derivationType) {
|
switch (walletInfo.derivationInfo!.derivationType) {
|
||||||
case DerivationType.electrum:
|
case DerivationType.electrum:
|
||||||
seedBytes = await mnemonicToSeedBytes(mnemonic, passphrase: passphrase ?? "");
|
seedBytes =
|
||||||
|
await mnemonicToSeedBytes(mnemonic, passphrase: passphrase ?? "");
|
||||||
break;
|
break;
|
||||||
case DerivationType.bip39:
|
case DerivationType.bip39:
|
||||||
default:
|
default:
|
||||||
|
@ -259,10 +266,17 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
|
||||||
derivationPath: walletInfo.derivationInfo!.derivationPath!);
|
derivationPath: walletInfo.derivationInfo!.derivationPath!);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> close({bool shouldCleanup = false}) async {
|
||||||
|
payjoinManager.cleanupSessions();
|
||||||
|
super.close(shouldCleanup: shouldCleanup);
|
||||||
|
}
|
||||||
|
|
||||||
late final PayjoinManager payjoinManager;
|
late final PayjoinManager payjoinManager;
|
||||||
|
|
||||||
bool get isPayjoinAvailable => unspentCoinsInfo.values
|
bool get isPayjoinAvailable => unspentCoinsInfo.values
|
||||||
.where((element) => element.walletId == id && element.isSending && !element.isFrozen)
|
.where((element) =>
|
||||||
|
element.walletId == id && element.isSending && !element.isFrozen)
|
||||||
.isNotEmpty;
|
.isNotEmpty;
|
||||||
|
|
||||||
Future<PsbtV2> buildPsbt({
|
Future<PsbtV2> buildPsbt({
|
||||||
|
@ -279,8 +293,10 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
|
||||||
}) async {
|
}) async {
|
||||||
final psbtReadyInputs = <PSBTReadyUtxoWithAddress>[];
|
final psbtReadyInputs = <PSBTReadyUtxoWithAddress>[];
|
||||||
for (final utxo in utxos) {
|
for (final utxo in utxos) {
|
||||||
final rawTx = await electrumClient.getTransactionHex(hash: utxo.utxo.txHash);
|
final rawTx =
|
||||||
final publicKeyAndDerivationPath = publicKeys[utxo.ownerDetails.address.pubKeyHash()]!;
|
await electrumClient.getTransactionHex(hash: utxo.utxo.txHash);
|
||||||
|
final publicKeyAndDerivationPath =
|
||||||
|
publicKeys[utxo.ownerDetails.address.pubKeyHash()]!;
|
||||||
|
|
||||||
psbtReadyInputs.add(PSBTReadyUtxoWithAddress(
|
psbtReadyInputs.add(PSBTReadyUtxoWithAddress(
|
||||||
utxo: utxo.utxo,
|
utxo: utxo.utxo,
|
||||||
|
@ -292,7 +308,8 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
return PSBTTransactionBuild(inputs: psbtReadyInputs, outputs: outputs, enableRBF: enableRBF)
|
return PSBTTransactionBuild(
|
||||||
|
inputs: psbtReadyInputs, outputs: outputs, enableRBF: enableRBF)
|
||||||
.psbt;
|
.psbt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -331,7 +348,8 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
|
||||||
Future<PendingTransaction> createTransaction(Object credentials) async {
|
Future<PendingTransaction> createTransaction(Object credentials) async {
|
||||||
credentials = credentials as BitcoinTransactionCredentials;
|
credentials = credentials as BitcoinTransactionCredentials;
|
||||||
|
|
||||||
final tx = (await super.createTransaction(credentials)) as PendingBitcoinTransaction;
|
final tx = (await super.createTransaction(credentials))
|
||||||
|
as PendingBitcoinTransaction;
|
||||||
|
|
||||||
final payjoinUri = credentials.payjoinUri;
|
final payjoinUri = credentials.payjoinUri;
|
||||||
if (payjoinUri == null) return tx;
|
if (payjoinUri == null) return tx;
|
||||||
|
@ -354,12 +372,12 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
|
||||||
publicKeys: tx.publicKeys!,
|
publicKeys: tx.publicKeys!,
|
||||||
masterFingerprint: Uint8List(0));
|
masterFingerprint: Uint8List(0));
|
||||||
|
|
||||||
final originalPsbt =
|
final originalPsbt = await signPsbt(
|
||||||
await signPsbt(base64.encode(transaction.asPsbtV0()), getUtxoWithPrivateKeys());
|
base64.encode(transaction.asPsbtV0()), getUtxoWithPrivateKeys());
|
||||||
|
|
||||||
tx.commitOverride = () async {
|
tx.commitOverride = () async {
|
||||||
final sender =
|
final sender = await payjoinManager.initSender(
|
||||||
await payjoinManager.initSender(payjoinUri, originalPsbt, int.parse(tx.feeRate));
|
payjoinUri, originalPsbt, int.parse(tx.feeRate));
|
||||||
payjoinManager.spawnNewSender(
|
payjoinManager.spawnNewSender(
|
||||||
sender: sender, pjUrl: payjoinUri, amount: BigInt.from(tx.amount));
|
sender: sender, pjUrl: payjoinUri, amount: BigInt.from(tx.amount));
|
||||||
};
|
};
|
||||||
|
@ -375,7 +393,8 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
|
||||||
Future<void> commitPsbt(String finalizedPsbt) {
|
Future<void> commitPsbt(String finalizedPsbt) {
|
||||||
final psbt = PsbtV2()..deserializeV0(base64.decode(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(
|
return PendingBitcoinTransaction(
|
||||||
btcTx,
|
btcTx,
|
||||||
|
@ -389,11 +408,12 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
|
||||||
).commit();
|
).commit();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<String> signPsbt(String preProcessedPsbt, List<UtxoWithPrivateKey> utxos) async {
|
Future<String> signPsbt(
|
||||||
|
String preProcessedPsbt, List<UtxoWithPrivateKey> utxos) async {
|
||||||
final psbt = PsbtV2()..deserializeV0(base64Decode(preProcessedPsbt));
|
final psbt = PsbtV2()..deserializeV0(base64Decode(preProcessedPsbt));
|
||||||
|
|
||||||
await psbt.signWithUTXO(utxos, (txDigest, utxo, key, sighash) {
|
await psbt.signWithUTXO(utxos, (txDigest, utxo, key, sighash) {
|
||||||
return utxo.utxo.isP2tr
|
return utxo.utxo.isP2tr()
|
||||||
? key.signTapRoot(
|
? key.signTapRoot(
|
||||||
txDigest,
|
txDigest,
|
||||||
sighash: sighash,
|
sighash: sighash,
|
||||||
|
@ -414,15 +434,17 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
|
||||||
Future<String> signMessage(String message, {String? address = null}) async {
|
Future<String> signMessage(String message, {String? address = null}) async {
|
||||||
if (walletInfo.isHardwareWallet) {
|
if (walletInfo.isHardwareWallet) {
|
||||||
final addressEntry = address != null
|
final addressEntry = address != null
|
||||||
? walletAddresses.allAddresses.firstWhere((element) => element.address == address)
|
? walletAddresses.allAddresses
|
||||||
|
.firstWhere((element) => element.address == address)
|
||||||
: null;
|
: null;
|
||||||
final index = addressEntry?.index ?? 0;
|
final index = addressEntry?.index ?? 0;
|
||||||
final isChange = addressEntry?.isHidden == true ? 1 : 0;
|
final isChange = addressEntry?.isHidden == true ? 1 : 0;
|
||||||
final accountPath = walletInfo.derivationInfo?.derivationPath;
|
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!
|
final signature = await _bitcoinLedgerApp!.signMessage(
|
||||||
.signMessage(message: ascii.encode(message), signDerivationPath: derivationPath);
|
message: ascii.encode(message), signDerivationPath: derivationPath);
|
||||||
return base64Encode(signature);
|
return base64Encode(signature);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -31,12 +31,10 @@ abstract class BitcoinWalletAddressesBase extends ElectrumWalletAddresses with S
|
||||||
|
|
||||||
final PayjoinManager payjoinManager;
|
final PayjoinManager payjoinManager;
|
||||||
|
|
||||||
@observable
|
|
||||||
payjoin.Receiver? currentPayjoinReceiver;
|
payjoin.Receiver? currentPayjoinReceiver;
|
||||||
|
|
||||||
@computed
|
@observable
|
||||||
String? get payjoinEndpoint =>
|
String? payjoinEndpoint = null;
|
||||||
currentPayjoinReceiver?.pjUriBuilder().build().pjEndpoint();
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String getAddress(
|
String getAddress(
|
||||||
|
@ -47,10 +45,10 @@ abstract class BitcoinWalletAddressesBase extends ElectrumWalletAddresses with S
|
||||||
if (addressType == P2pkhAddressType.p2pkh)
|
if (addressType == P2pkhAddressType.p2pkh)
|
||||||
return generateP2PKHAddress(hd: hd, index: index, network: network);
|
return generateP2PKHAddress(hd: hd, index: index, network: network);
|
||||||
|
|
||||||
if (addressType == SegwitAddressType.p2tr)
|
if (addressType == SegwitAddresType.p2tr)
|
||||||
return generateP2TRAddress(hd: hd, index: index, network: network);
|
return generateP2TRAddress(hd: hd, index: index, network: network);
|
||||||
|
|
||||||
if (addressType == SegwitAddressType.p2wsh)
|
if (addressType == SegwitAddresType.p2wsh)
|
||||||
return generateP2WSHAddress(hd: hd, index: index, network: network);
|
return generateP2WSHAddress(hd: hd, index: index, network: network);
|
||||||
|
|
||||||
if (addressType == P2shAddressType.p2wpkhInP2sh)
|
if (addressType == P2shAddressType.p2wpkhInP2sh)
|
||||||
|
@ -59,16 +57,32 @@ abstract class BitcoinWalletAddressesBase extends ElectrumWalletAddresses with S
|
||||||
return generateP2WPKHAddress(hd: hd, index: index, network: network);
|
return generateP2WPKHAddress(hd: hd, index: index, network: network);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@action
|
||||||
Future<void> initPayjoin() async {
|
Future<void> initPayjoin() async {
|
||||||
currentPayjoinReceiver = await payjoinManager.initReceiver(primaryAddress);
|
try {
|
||||||
|
await payjoinManager.initPayjoin();
|
||||||
|
currentPayjoinReceiver = await payjoinManager.getUnusedReceiver(primaryAddress);
|
||||||
|
payjoinEndpoint = (await currentPayjoinReceiver?.pjUri())?.pjEndpoint();
|
||||||
|
|
||||||
payjoinManager.resumeSessions();
|
payjoinManager.resumeSessions();
|
||||||
|
} catch (e) {
|
||||||
|
printV(e);
|
||||||
|
// Ignore Connectivity errors
|
||||||
|
if (!e.toString().contains("error sending request for url")) rethrow;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@action
|
||||||
Future<void> newPayjoinReceiver() async {
|
Future<void> newPayjoinReceiver() async {
|
||||||
currentPayjoinReceiver = await payjoinManager.initReceiver(primaryAddress);
|
try {
|
||||||
|
currentPayjoinReceiver = await payjoinManager.getUnusedReceiver(primaryAddress);
|
||||||
|
payjoinEndpoint = (await currentPayjoinReceiver?.pjUri())?.pjEndpoint();
|
||||||
|
|
||||||
printV("Initializing new Payjoin Receiver");
|
payjoinManager.spawnReceiver(receiver: currentPayjoinReceiver!);
|
||||||
payjoinManager.spawnNewReceiver(receiver: currentPayjoinReceiver!);
|
} catch (e) {
|
||||||
|
printV(e);
|
||||||
|
// Ignore Connectivity errors
|
||||||
|
if (!e.toString().contains("error sending request for url")) rethrow;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,8 @@ import 'dart:typed_data';
|
||||||
import 'package:bitcoin_base/bitcoin_base.dart';
|
import 'package:bitcoin_base/bitcoin_base.dart';
|
||||||
import 'package:cw_bitcoin/bitcoin_amount_format.dart';
|
import 'package:cw_bitcoin/bitcoin_amount_format.dart';
|
||||||
import 'package:cw_core/utils/print_verbose.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:flutter/foundation.dart';
|
||||||
import 'package:rxdart/rxdart.dart';
|
import 'package:rxdart/rxdart.dart';
|
||||||
|
|
||||||
|
@ -42,7 +44,7 @@ class ElectrumClient {
|
||||||
static const aliveTimerDuration = Duration(seconds: 4);
|
static const aliveTimerDuration = Duration(seconds: 4);
|
||||||
|
|
||||||
bool get isConnected => _isConnected;
|
bool get isConnected => _isConnected;
|
||||||
Socket? socket;
|
ProxySocket? socket;
|
||||||
void Function(ConnectionStatus)? onConnectionStatusChange;
|
void Function(ConnectionStatus)? onConnectionStatusChange;
|
||||||
int _id;
|
int _id;
|
||||||
final Map<String, SocketTask> _tasks;
|
final Map<String, SocketTask> _tasks;
|
||||||
|
@ -72,18 +74,11 @@ class ElectrumClient {
|
||||||
} catch (_) {}
|
} catch (_) {}
|
||||||
socket = null;
|
socket = null;
|
||||||
|
|
||||||
|
final ssl = !(useSSL == false || (useSSL == null && uri.toString().contains("btc-electrum")));
|
||||||
try {
|
try {
|
||||||
if (useSSL == false || (useSSL == null && uri.toString().contains("btc-electrum"))) {
|
socket = await ProxyWrapper().getSocksSocket(ssl, host, port, connectionTimeout: connectionTimeout);
|
||||||
socket = await Socket.connect(host, port, timeout: connectionTimeout);
|
|
||||||
} else {
|
|
||||||
socket = await SecureSocket.connect(
|
|
||||||
host,
|
|
||||||
port,
|
|
||||||
timeout: connectionTimeout,
|
|
||||||
onBadCertificate: (_) => true,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
printV("connect: $e");
|
||||||
if (e is HandshakeException) {
|
if (e is HandshakeException) {
|
||||||
useSSL = !(useSSL ?? false);
|
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:
|
// use ping to determine actual connection status since we could've just not timed out yet:
|
||||||
// _setConnectionStatus(ConnectionStatus.connected);
|
// _setConnectionStatus(ConnectionStatus.connected);
|
||||||
|
|
||||||
socket!.listen(
|
socket!.listen(
|
||||||
(Uint8List event) {
|
(Uint8List event) {
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -4,7 +4,9 @@ import 'dart:io';
|
||||||
import 'dart:isolate';
|
import 'dart:isolate';
|
||||||
|
|
||||||
import 'package:bitcoin_base/bitcoin_base.dart';
|
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_bitcoin/bitcoin_amount_format.dart';
|
||||||
|
import 'package:cw_core/format_amount.dart';
|
||||||
import 'package:cw_core/utils/print_verbose.dart';
|
import 'package:cw_core/utils/print_verbose.dart';
|
||||||
import 'package:cw_bitcoin/bitcoin_wallet.dart';
|
import 'package:cw_bitcoin/bitcoin_wallet.dart';
|
||||||
import 'package:cw_bitcoin/litecoin_wallet.dart';
|
import 'package:cw_bitcoin/litecoin_wallet.dart';
|
||||||
|
@ -17,7 +19,7 @@ import 'package:cw_bitcoin/bitcoin_transaction_credentials.dart';
|
||||||
import 'package:cw_bitcoin/bitcoin_transaction_priority.dart';
|
import 'package:cw_bitcoin/bitcoin_transaction_priority.dart';
|
||||||
import 'package:cw_bitcoin/bitcoin_unspent.dart';
|
import 'package:cw_bitcoin/bitcoin_unspent.dart';
|
||||||
import 'package:cw_bitcoin/bitcoin_wallet_keys.dart';
|
import 'package:cw_bitcoin/bitcoin_wallet_keys.dart';
|
||||||
import 'package:cw_bitcoin/electrum.dart' as electrum;
|
import 'package:cw_bitcoin/electrum.dart';
|
||||||
import 'package:cw_bitcoin/electrum_balance.dart';
|
import 'package:cw_bitcoin/electrum_balance.dart';
|
||||||
import 'package:cw_bitcoin/electrum_derivations.dart';
|
import 'package:cw_bitcoin/electrum_derivations.dart';
|
||||||
import 'package:cw_bitcoin/electrum_transaction_history.dart';
|
import 'package:cw_bitcoin/electrum_transaction_history.dart';
|
||||||
|
@ -48,7 +50,6 @@ import 'package:mobx/mobx.dart';
|
||||||
import 'package:rxdart/subjects.dart';
|
import 'package:rxdart/subjects.dart';
|
||||||
import 'package:sp_scanner/sp_scanner.dart';
|
import 'package:sp_scanner/sp_scanner.dart';
|
||||||
import 'package:hex/hex.dart';
|
import 'package:hex/hex.dart';
|
||||||
import 'package:http/http.dart' as http;
|
|
||||||
|
|
||||||
part 'electrum_wallet.g.dart';
|
part 'electrum_wallet.g.dart';
|
||||||
|
|
||||||
|
@ -68,7 +69,7 @@ abstract class ElectrumWalletBase
|
||||||
Uint8List? seedBytes,
|
Uint8List? seedBytes,
|
||||||
this.passphrase,
|
this.passphrase,
|
||||||
List<BitcoinAddressRecord>? initialAddresses,
|
List<BitcoinAddressRecord>? initialAddresses,
|
||||||
electrum.ElectrumClient? electrumClient,
|
ElectrumClient? electrumClient,
|
||||||
ElectrumBalance? initialBalance,
|
ElectrumBalance? initialBalance,
|
||||||
CryptoCurrency? currency,
|
CryptoCurrency? currency,
|
||||||
this.alwaysScan,
|
this.alwaysScan,
|
||||||
|
@ -95,7 +96,7 @@ abstract class ElectrumWalletBase
|
||||||
this.isTestnet = !network.isMainnet,
|
this.isTestnet = !network.isMainnet,
|
||||||
this._mnemonic = mnemonic,
|
this._mnemonic = mnemonic,
|
||||||
super(walletInfo) {
|
super(walletInfo) {
|
||||||
this.electrumClient = electrumClient ?? electrum.ElectrumClient();
|
this.electrumClient = electrumClient ?? ElectrumClient();
|
||||||
this.walletInfo = walletInfo;
|
this.walletInfo = walletInfo;
|
||||||
transactionHistory = ElectrumTransactionHistory(
|
transactionHistory = ElectrumTransactionHistory(
|
||||||
walletInfo: walletInfo,
|
walletInfo: walletInfo,
|
||||||
|
@ -166,7 +167,7 @@ abstract class ElectrumWalletBase
|
||||||
@observable
|
@observable
|
||||||
bool isEnabledAutoGenerateSubaddress;
|
bool isEnabledAutoGenerateSubaddress;
|
||||||
|
|
||||||
late electrum.ElectrumClient electrumClient;
|
late ElectrumClient electrumClient;
|
||||||
Box<UnspentCoinsInfo> unspentCoinsInfo;
|
Box<UnspentCoinsInfo> unspentCoinsInfo;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -181,7 +182,7 @@ abstract class ElectrumWalletBase
|
||||||
SyncStatus syncStatus;
|
SyncStatus syncStatus;
|
||||||
|
|
||||||
Set<String> get addressesSet => walletAddresses.allAddresses
|
Set<String> get addressesSet => walletAddresses.allAddresses
|
||||||
.where((element) => element.type != SegwitAddressType.mweb)
|
.where((element) => element.type != SegwitAddresType.mweb)
|
||||||
.map((addr) => addr.address)
|
.map((addr) => addr.address)
|
||||||
.toSet();
|
.toSet();
|
||||||
|
|
||||||
|
@ -332,14 +333,14 @@ abstract class ElectrumWalletBase
|
||||||
|
|
||||||
final receivePort = ReceivePort();
|
final receivePort = ReceivePort();
|
||||||
_isolate = Isolate.spawn(
|
_isolate = Isolate.spawn(
|
||||||
_handleScanSilentPayments,
|
startRefresh,
|
||||||
ScanData(
|
ScanData(
|
||||||
sendPort: receivePort.sendPort,
|
sendPort: receivePort.sendPort,
|
||||||
silentAddress: walletAddresses.silentAddress!,
|
silentAddress: walletAddresses.silentAddress!,
|
||||||
network: network,
|
network: network,
|
||||||
height: height,
|
height: height,
|
||||||
chainTip: chainTip,
|
chainTip: chainTip,
|
||||||
electrumClient: electrum.ElectrumClient(),
|
electrumClient: ElectrumClient(),
|
||||||
transactionHistoryIds: transactionHistory.transactions.keys.toList(),
|
transactionHistoryIds: transactionHistory.transactions.keys.toList(),
|
||||||
node: (await getNodeSupportsSilentPayments()) == true
|
node: (await getNodeSupportsSilentPayments()) == true
|
||||||
? ScanNode(node!.uri, node!.useSSL)
|
? ScanNode(node!.uri, node!.useSSL)
|
||||||
|
@ -438,6 +439,7 @@ abstract class ElectrumWalletBase
|
||||||
BigintUtils.fromBytes(BytesUtils.fromHexString(unspent.silentPaymentLabel!)),
|
BigintUtils.fromBytes(BytesUtils.fromHexString(unspent.silentPaymentLabel!)),
|
||||||
)
|
)
|
||||||
: silentAddress.B_spend,
|
: silentAddress.B_spend,
|
||||||
|
network: network,
|
||||||
);
|
);
|
||||||
|
|
||||||
final addressRecord = walletAddresses.silentAddresses
|
final addressRecord = walletAddresses.silentAddresses
|
||||||
|
@ -491,10 +493,9 @@ abstract class ElectrumWalletBase
|
||||||
Future<void> updateFeeRates() async {
|
Future<void> updateFeeRates() async {
|
||||||
if (await checkIfMempoolAPIIsEnabled() && type == WalletType.bitcoin) {
|
if (await checkIfMempoolAPIIsEnabled() && type == WalletType.bitcoin) {
|
||||||
try {
|
try {
|
||||||
final response = await http
|
final response = await ProxyWrapper()
|
||||||
.get(Uri.parse("https://mempool.cakewallet.com/api/v1/fees/recommended"))
|
.get(clearnetUri: Uri.parse("https://mempool.cakewallet.com/api/v1/fees/recommended"))
|
||||||
.timeout(Duration(seconds: 5));
|
.timeout(Duration(seconds: 15));
|
||||||
|
|
||||||
final result = json.decode(response.body) as Map<String, dynamic>;
|
final result = json.decode(response.body) as Map<String, dynamic>;
|
||||||
final slowFee = (result['economyFee'] as num?)?.toInt() ?? 0;
|
final slowFee = (result['economyFee'] as num?)?.toInt() ?? 0;
|
||||||
int mediumFee = (result['hourFee'] as num?)?.toInt() ?? 0;
|
int mediumFee = (result['hourFee'] as num?)?.toInt() ?? 0;
|
||||||
|
@ -562,7 +563,7 @@ abstract class ElectrumWalletBase
|
||||||
node!.save();
|
node!.save();
|
||||||
return node!.supportsSilentPayments!;
|
return node!.supportsSilentPayments!;
|
||||||
}
|
}
|
||||||
} on electrum.RequestFailedTimeoutException catch (_) {
|
} on RequestFailedTimeoutException catch (_) {
|
||||||
node!.supportsSilentPayments = false;
|
node!.supportsSilentPayments = false;
|
||||||
node!.save();
|
node!.save();
|
||||||
return node!.supportsSilentPayments!;
|
return node!.supportsSilentPayments!;
|
||||||
|
@ -623,9 +624,9 @@ abstract class ElectrumWalletBase
|
||||||
|
|
||||||
switch (coinTypeToSpendFrom) {
|
switch (coinTypeToSpendFrom) {
|
||||||
case UnspentCoinType.mweb:
|
case UnspentCoinType.mweb:
|
||||||
return utx.bitcoinAddressRecord.type == SegwitAddressType.mweb;
|
return utx.bitcoinAddressRecord.type == SegwitAddresType.mweb;
|
||||||
case UnspentCoinType.nonMweb:
|
case UnspentCoinType.nonMweb:
|
||||||
return utx.bitcoinAddressRecord.type != SegwitAddressType.mweb;
|
return utx.bitcoinAddressRecord.type != SegwitAddresType.mweb;
|
||||||
case UnspentCoinType.any:
|
case UnspentCoinType.any:
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -633,7 +634,7 @@ abstract class ElectrumWalletBase
|
||||||
final unconfirmedCoins = availableInputs.where((utx) => utx.confirmations == 0).toList();
|
final unconfirmedCoins = availableInputs.where((utx) => utx.confirmations == 0).toList();
|
||||||
|
|
||||||
// sort the unconfirmed coins so that mweb coins are last:
|
// sort the unconfirmed coins so that mweb coins are last:
|
||||||
availableInputs.sort((a, b) => a.bitcoinAddressRecord.type == SegwitAddressType.mweb ? 1 : -1);
|
availableInputs.sort((a, b) => a.bitcoinAddressRecord.type == SegwitAddresType.mweb ? 1 : -1);
|
||||||
|
|
||||||
for (int i = 0; i < availableInputs.length; i++) {
|
for (int i = 0; i < availableInputs.length; i++) {
|
||||||
final utx = availableInputs[i];
|
final utx = availableInputs[i];
|
||||||
|
@ -641,7 +642,7 @@ abstract class ElectrumWalletBase
|
||||||
|
|
||||||
if (paysToSilentPayment) {
|
if (paysToSilentPayment) {
|
||||||
// Check inputs for shared secret derivation
|
// Check inputs for shared secret derivation
|
||||||
if (utx.bitcoinAddressRecord.type == SegwitAddressType.p2wsh) {
|
if (utx.bitcoinAddressRecord.type == SegwitAddresType.p2wsh) {
|
||||||
throw BitcoinTransactionSilentPaymentsNotSupported();
|
throw BitcoinTransactionSilentPaymentsNotSupported();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -676,7 +677,7 @@ abstract class ElectrumWalletBase
|
||||||
if (privkey != null) {
|
if (privkey != null) {
|
||||||
inputPrivKeyInfos.add(ECPrivateInfo(
|
inputPrivKeyInfos.add(ECPrivateInfo(
|
||||||
privkey,
|
privkey,
|
||||||
address.type == SegwitAddressType.p2tr,
|
address.type == SegwitAddresType.p2tr,
|
||||||
tweak: !isSilentPayment,
|
tweak: !isSilentPayment,
|
||||||
));
|
));
|
||||||
|
|
||||||
|
@ -1162,7 +1163,7 @@ abstract class ElectrumWalletBase
|
||||||
throw Exception(error);
|
throw Exception(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (utxo.utxo.isP2tr) {
|
if (utxo.utxo.isP2tr()) {
|
||||||
hasTaprootInputs = true;
|
hasTaprootInputs = true;
|
||||||
return key.privkey.signTapRoot(
|
return key.privkey.signTapRoot(
|
||||||
txDigest,
|
txDigest,
|
||||||
|
@ -1229,7 +1230,7 @@ abstract class ElectrumWalletBase
|
||||||
'change_address_index': walletAddresses.currentChangeAddressIndexByType,
|
'change_address_index': walletAddresses.currentChangeAddressIndexByType,
|
||||||
'addresses': walletAddresses.allAddresses.map((addr) => addr.toJSON()).toList(),
|
'addresses': walletAddresses.allAddresses.map((addr) => addr.toJSON()).toList(),
|
||||||
'address_page_type': walletInfo.addressPageType == null
|
'address_page_type': walletInfo.addressPageType == null
|
||||||
? SegwitAddressType.p2wpkh.toString()
|
? SegwitAddresType.p2wpkh.toString()
|
||||||
: walletInfo.addressPageType.toString(),
|
: walletInfo.addressPageType.toString(),
|
||||||
'balance': balance[currency]?.toJSON(),
|
'balance': balance[currency]?.toJSON(),
|
||||||
'derivationTypeIndex': walletInfo.derivationInfo?.derivationType?.index,
|
'derivationTypeIndex': walletInfo.derivationInfo?.derivationType?.index,
|
||||||
|
@ -1369,7 +1370,7 @@ abstract class ElectrumWalletBase
|
||||||
List<BitcoinUnspent> updatedUnspentCoins = [];
|
List<BitcoinUnspent> updatedUnspentCoins = [];
|
||||||
|
|
||||||
final previousUnspentCoins = List<BitcoinUnspent>.from(unspentCoins.where((utxo) =>
|
final previousUnspentCoins = List<BitcoinUnspent>.from(unspentCoins.where((utxo) =>
|
||||||
utxo.bitcoinAddressRecord.type != SegwitAddressType.mweb &&
|
utxo.bitcoinAddressRecord.type != SegwitAddresType.mweb &&
|
||||||
utxo.bitcoinAddressRecord is! BitcoinSilentPaymentAddressRecord));
|
utxo.bitcoinAddressRecord is! BitcoinSilentPaymentAddressRecord));
|
||||||
|
|
||||||
if (hasSilentPaymentsScanning) {
|
if (hasSilentPaymentsScanning) {
|
||||||
|
@ -1383,13 +1384,13 @@ abstract class ElectrumWalletBase
|
||||||
|
|
||||||
// Set the balance of all non-silent payment and non-mweb addresses to 0 before updating
|
// Set the balance of all non-silent payment and non-mweb addresses to 0 before updating
|
||||||
walletAddresses.allAddresses
|
walletAddresses.allAddresses
|
||||||
.where((element) => element.type != SegwitAddressType.mweb)
|
.where((element) => element.type != SegwitAddresType.mweb)
|
||||||
.forEach((addr) {
|
.forEach((addr) {
|
||||||
if (addr is! BitcoinSilentPaymentAddressRecord) addr.balance = 0;
|
if (addr is! BitcoinSilentPaymentAddressRecord) addr.balance = 0;
|
||||||
});
|
});
|
||||||
|
|
||||||
final addressFutures = walletAddresses.allAddresses
|
final addressFutures = walletAddresses.allAddresses
|
||||||
.where((element) => element.type != SegwitAddressType.mweb)
|
.where((element) => element.type != SegwitAddresType.mweb)
|
||||||
.map((address) => fetchUnspent(address))
|
.map((address) => fetchUnspent(address))
|
||||||
.toList();
|
.toList();
|
||||||
|
|
||||||
|
@ -1830,7 +1831,7 @@ abstract class ElectrumWalletBase
|
||||||
throw Exception("Cannot find private key");
|
throw Exception("Cannot find private key");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (utxo.utxo.isP2tr) {
|
if (utxo.utxo.isP2tr()) {
|
||||||
return key.signTapRoot(txDigest, sighash: sighash);
|
return key.signTapRoot(txDigest, sighash: sighash);
|
||||||
} else {
|
} else {
|
||||||
return key.signInput(txDigest, sigHash: sighash);
|
return key.signInput(txDigest, sigHash: sighash);
|
||||||
|
@ -1876,20 +1877,17 @@ abstract class ElectrumWalletBase
|
||||||
|
|
||||||
if (height != null && height > 0 && await checkIfMempoolAPIIsEnabled()) {
|
if (height != null && height > 0 && await checkIfMempoolAPIIsEnabled()) {
|
||||||
try {
|
try {
|
||||||
final blockHash = await http.get(
|
final blockHash = await ProxyWrapper()
|
||||||
Uri.parse(
|
.get(clearnetUri: Uri.parse("https://mempool.cakewallet.com/api/v1/block-height/$height"))
|
||||||
"https://mempool.cakewallet.com/api/v1/block-height/$height",
|
.timeout(Duration(seconds: 15));
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
if (blockHash.statusCode == 200 &&
|
if (blockHash.statusCode == 200 &&
|
||||||
blockHash.body.isNotEmpty &&
|
blockHash.body.isNotEmpty &&
|
||||||
jsonDecode(blockHash.body) != null) {
|
jsonDecode(blockHash.body) != null) {
|
||||||
final blockResponse = await http.get(
|
final blockResponse = await ProxyWrapper()
|
||||||
Uri.parse(
|
.get(clearnetUri: Uri.parse("https://mempool.cakewallet.com/api/v1/block/${blockHash}"))
|
||||||
"https://mempool.cakewallet.com/api/v1/block/${blockHash.body}",
|
.timeout(Duration(seconds: 15));
|
||||||
),
|
|
||||||
);
|
|
||||||
if (blockResponse.statusCode == 200 &&
|
if (blockResponse.statusCode == 200 &&
|
||||||
blockResponse.body.isNotEmpty &&
|
blockResponse.body.isNotEmpty &&
|
||||||
jsonDecode(blockResponse.body)['timestamp'] != null) {
|
jsonDecode(blockResponse.body)['timestamp'] != null) {
|
||||||
|
@ -1980,7 +1978,7 @@ abstract class ElectrumWalletBase
|
||||||
.map((type) => fetchTransactionsForAddressType(historiesWithDetails, type)));
|
.map((type) => fetchTransactionsForAddressType(historiesWithDetails, type)));
|
||||||
} else if (type == WalletType.litecoin) {
|
} else if (type == WalletType.litecoin) {
|
||||||
await Future.wait(LITECOIN_ADDRESS_TYPES
|
await Future.wait(LITECOIN_ADDRESS_TYPES
|
||||||
.where((type) => type != SegwitAddressType.mweb)
|
.where((type) => type != SegwitAddresType.mweb)
|
||||||
.map((type) => fetchTransactionsForAddressType(historiesWithDetails, type)));
|
.map((type) => fetchTransactionsForAddressType(historiesWithDetails, type)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2169,7 +2167,7 @@ abstract class ElectrumWalletBase
|
||||||
final unsubscribedScriptHashes = walletAddresses.allAddresses.where(
|
final unsubscribedScriptHashes = walletAddresses.allAddresses.where(
|
||||||
(address) =>
|
(address) =>
|
||||||
!_scripthashesUpdateSubject.containsKey(address.getScriptHash(network)) &&
|
!_scripthashesUpdateSubject.containsKey(address.getScriptHash(network)) &&
|
||||||
address.type != SegwitAddressType.mweb,
|
address.type != SegwitAddresType.mweb,
|
||||||
);
|
);
|
||||||
|
|
||||||
await Future.wait(unsubscribedScriptHashes.map((address) async {
|
await Future.wait(unsubscribedScriptHashes.map((address) async {
|
||||||
|
@ -2392,9 +2390,9 @@ abstract class ElectrumWalletBase
|
||||||
derivationPath.substring(0, derivationPath.lastIndexOf("'") + 1);
|
derivationPath.substring(0, derivationPath.lastIndexOf("'") + 1);
|
||||||
|
|
||||||
@action
|
@action
|
||||||
void _onConnectionStatusChange(electrum.ConnectionStatus status) {
|
void _onConnectionStatusChange(ConnectionStatus status) {
|
||||||
switch (status) {
|
switch (status) {
|
||||||
case electrum.ConnectionStatus.connected:
|
case ConnectionStatus.connected:
|
||||||
if (syncStatus is NotConnectedSyncStatus ||
|
if (syncStatus is NotConnectedSyncStatus ||
|
||||||
syncStatus is LostConnectionSyncStatus ||
|
syncStatus is LostConnectionSyncStatus ||
|
||||||
syncStatus is ConnectingSyncStatus) {
|
syncStatus is ConnectingSyncStatus) {
|
||||||
|
@ -2402,19 +2400,19 @@ abstract class ElectrumWalletBase
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case electrum.ConnectionStatus.disconnected:
|
case ConnectionStatus.disconnected:
|
||||||
if (syncStatus is! NotConnectedSyncStatus &&
|
if (syncStatus is! NotConnectedSyncStatus &&
|
||||||
syncStatus is! ConnectingSyncStatus &&
|
syncStatus is! ConnectingSyncStatus &&
|
||||||
syncStatus is! SyncronizingSyncStatus) {
|
syncStatus is! SyncronizingSyncStatus) {
|
||||||
syncStatus = NotConnectedSyncStatus();
|
syncStatus = NotConnectedSyncStatus();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case electrum.ConnectionStatus.failed:
|
case ConnectionStatus.failed:
|
||||||
if (syncStatus is! LostConnectionSyncStatus) {
|
if (syncStatus is! LostConnectionSyncStatus) {
|
||||||
syncStatus = LostConnectionSyncStatus();
|
syncStatus = LostConnectionSyncStatus();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case electrum.ConnectionStatus.connecting:
|
case ConnectionStatus.connecting:
|
||||||
if (syncStatus is! ConnectingSyncStatus) {
|
if (syncStatus is! ConnectingSyncStatus) {
|
||||||
syncStatus = ConnectingSyncStatus();
|
syncStatus = ConnectingSyncStatus();
|
||||||
}
|
}
|
||||||
|
@ -2526,7 +2524,7 @@ class ScanData {
|
||||||
final ScanNode? node;
|
final ScanNode? node;
|
||||||
final BasedUtxoNetwork network;
|
final BasedUtxoNetwork network;
|
||||||
final int chainTip;
|
final int chainTip;
|
||||||
final electrum.ElectrumClient electrumClient;
|
final ElectrumClient electrumClient;
|
||||||
final List<String> transactionHistoryIds;
|
final List<String> transactionHistoryIds;
|
||||||
final Map<String, String> labels;
|
final Map<String, String> labels;
|
||||||
final List<int> labelIndexes;
|
final List<int> labelIndexes;
|
||||||
|
@ -2570,234 +2568,6 @@ class SyncResponse {
|
||||||
SyncResponse(this.height, this.syncStatus);
|
SyncResponse(this.height, this.syncStatus);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _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<String, dynamic> 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<BitcoinUnspent> unspents = [];
|
|
||||||
|
|
||||||
addToWallet.forEach((BSpend, scanResultPerLabel) {
|
|
||||||
scanResultPerLabel.forEach((label, scanOutput) {
|
|
||||||
final labelValue = label == "None" ? null : label.toString();
|
|
||||||
|
|
||||||
(scanOutput as Map<String, dynamic>).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<void> startRefresh(ScanData scanData) async {
|
Future<void> startRefresh(ScanData scanData) async {
|
||||||
int syncHeight = scanData.height;
|
int syncHeight = scanData.height;
|
||||||
int initialSyncHeight = syncHeight;
|
int initialSyncHeight = syncHeight;
|
||||||
|
@ -2810,7 +2580,7 @@ Future<void> startRefresh(ScanData scanData) async {
|
||||||
useSSL: scanData.node?.useSSL ?? false,
|
useSSL: scanData.node?.useSSL ?? false,
|
||||||
);
|
);
|
||||||
|
|
||||||
int getCountToScanPerRequest(int syncHeight) {
|
int getCountPerRequest(int syncHeight) {
|
||||||
if (scanData.isSingleScan) {
|
if (scanData.isSingleScan) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
@ -2825,10 +2595,11 @@ Future<void> startRefresh(ScanData scanData) async {
|
||||||
scanData.silentAddress.B_spend.toHex(),
|
scanData.silentAddress.B_spend.toHex(),
|
||||||
scanData.network == BitcoinNetwork.testnet,
|
scanData.network == BitcoinNetwork.testnet,
|
||||||
scanData.labelIndexes,
|
scanData.labelIndexes,
|
||||||
|
scanData.labelIndexes.length,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Initial status UI update, send how many blocks in total to scan
|
// Initial status UI update, send how many blocks in total to scan
|
||||||
final initialCount = getCountToScanPerRequest(syncHeight);
|
final initialCount = getCountPerRequest(syncHeight);
|
||||||
scanData.sendPort.send(SyncResponse(syncHeight, StartingScanSyncStatus(syncHeight)));
|
scanData.sendPort.send(SyncResponse(syncHeight, StartingScanSyncStatus(syncHeight)));
|
||||||
|
|
||||||
tweaksSubscription = await electrumClient.tweaksSubscribe(
|
tweaksSubscription = await electrumClient.tweaksSubscribe(
|
||||||
|
@ -2839,24 +2610,22 @@ Future<void> startRefresh(ScanData scanData) async {
|
||||||
Future<void> listenFn(t) async {
|
Future<void> listenFn(t) async {
|
||||||
final tweaks = t as Map<String, dynamic>;
|
final tweaks = t as Map<String, dynamic>;
|
||||||
final msg = tweaks["message"];
|
final msg = tweaks["message"];
|
||||||
|
// success or error msg
|
||||||
// is success or error msg
|
|
||||||
final noData = msg != null;
|
final noData = msg != null;
|
||||||
|
|
||||||
if (noData) {
|
if (noData) {
|
||||||
if (scanData.isSingleScan) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// re-subscribe to continue receiving messages, starting from the next unscanned height
|
// re-subscribe to continue receiving messages, starting from the next unscanned height
|
||||||
final nextHeight = syncHeight + 1;
|
final nextHeight = syncHeight + 1;
|
||||||
|
final nextCount = getCountPerRequest(nextHeight);
|
||||||
|
|
||||||
if (nextHeight <= scanData.chainTip) {
|
if (nextCount > 0) {
|
||||||
final nextStream = electrumClient.tweaksSubscribe(
|
tweaksSubscription?.close();
|
||||||
|
|
||||||
|
final nextTweaksSubscription = electrumClient.tweaksSubscribe(
|
||||||
height: nextHeight,
|
height: nextHeight,
|
||||||
count: getCountToScanPerRequest(nextHeight),
|
count: nextCount,
|
||||||
);
|
);
|
||||||
nextStream?.listen(listenFn);
|
nextTweaksSubscription?.listen(listenFn);
|
||||||
}
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
|
@ -2938,7 +2707,7 @@ Future<void> startRefresh(ScanData scanData) async {
|
||||||
isUsed: true,
|
isUsed: true,
|
||||||
network: scanData.network,
|
network: scanData.network,
|
||||||
silentPaymentTweak: t_k,
|
silentPaymentTweak: t_k,
|
||||||
type: SegwitAddressType.p2tr,
|
type: SegwitAddresType.p2tr,
|
||||||
txCount: 1,
|
txCount: 1,
|
||||||
balance: amount!,
|
balance: amount!,
|
||||||
);
|
);
|
||||||
|
@ -3031,15 +2800,15 @@ BitcoinAddressType _getScriptType(BitcoinBaseAddress type) {
|
||||||
} else if (type is P2shAddress) {
|
} else if (type is P2shAddress) {
|
||||||
return P2shAddressType.p2wpkhInP2sh;
|
return P2shAddressType.p2wpkhInP2sh;
|
||||||
} else if (type is P2wshAddress) {
|
} else if (type is P2wshAddress) {
|
||||||
return SegwitAddressType.p2wsh;
|
return SegwitAddresType.p2wsh;
|
||||||
} else if (type is P2trAddress) {
|
} else if (type is P2trAddress) {
|
||||||
return SegwitAddressType.p2tr;
|
return SegwitAddresType.p2tr;
|
||||||
} else if (type is MwebAddress) {
|
} else if (type is MwebAddress) {
|
||||||
return SegwitAddressType.mweb;
|
return SegwitAddresType.mweb;
|
||||||
} else if (type is SilentPaymentsAddresType) {
|
} else if (type is SilentPaymentsAddresType) {
|
||||||
return SilentPaymentsAddresType.p2sp;
|
return SilentPaymentsAddresType.p2sp;
|
||||||
} else {
|
} else {
|
||||||
return SegwitAddressType.p2wpkh;
|
return SegwitAddresType.p2wpkh;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,16 +17,16 @@ part 'electrum_wallet_addresses.g.dart';
|
||||||
class ElectrumWalletAddresses = ElectrumWalletAddressesBase with _$ElectrumWalletAddresses;
|
class ElectrumWalletAddresses = ElectrumWalletAddressesBase with _$ElectrumWalletAddresses;
|
||||||
|
|
||||||
const List<BitcoinAddressType> BITCOIN_ADDRESS_TYPES = [
|
const List<BitcoinAddressType> BITCOIN_ADDRESS_TYPES = [
|
||||||
SegwitAddressType.p2wpkh,
|
SegwitAddresType.p2wpkh,
|
||||||
P2pkhAddressType.p2pkh,
|
P2pkhAddressType.p2pkh,
|
||||||
SegwitAddressType.p2tr,
|
SegwitAddresType.p2tr,
|
||||||
SegwitAddressType.p2wsh,
|
SegwitAddresType.p2wsh,
|
||||||
P2shAddressType.p2wpkhInP2sh,
|
P2shAddressType.p2wpkhInP2sh,
|
||||||
];
|
];
|
||||||
|
|
||||||
const List<BitcoinAddressType> LITECOIN_ADDRESS_TYPES = [
|
const List<BitcoinAddressType> LITECOIN_ADDRESS_TYPES = [
|
||||||
SegwitAddressType.p2wpkh,
|
SegwitAddresType.p2wpkh,
|
||||||
SegwitAddressType.mweb,
|
SegwitAddresType.mweb,
|
||||||
];
|
];
|
||||||
|
|
||||||
const List<BitcoinAddressType> BITCOIN_CASH_ADDRESS_TYPES = [
|
const List<BitcoinAddressType> BITCOIN_CASH_ADDRESS_TYPES = [
|
||||||
|
@ -62,7 +62,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
||||||
_addressPageType = initialAddressPageType ??
|
_addressPageType = initialAddressPageType ??
|
||||||
(walletInfo.addressPageType != null
|
(walletInfo.addressPageType != null
|
||||||
? BitcoinAddressType.fromValue(walletInfo.addressPageType!)
|
? BitcoinAddressType.fromValue(walletInfo.addressPageType!)
|
||||||
: SegwitAddressType.p2wpkh),
|
: SegwitAddresType.p2wpkh),
|
||||||
silentAddresses = ObservableList<BitcoinSilentPaymentAddressRecord>.of(
|
silentAddresses = ObservableList<BitcoinSilentPaymentAddressRecord>.of(
|
||||||
(initialSilentAddresses ?? []).toSet()),
|
(initialSilentAddresses ?? []).toSet()),
|
||||||
currentSilentAddressIndex = initialSilentAddressIndex,
|
currentSilentAddressIndex = initialSilentAddressIndex,
|
||||||
|
@ -71,12 +71,9 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
||||||
super(walletInfo) {
|
super(walletInfo) {
|
||||||
if (masterHd != null) {
|
if (masterHd != null) {
|
||||||
silentAddress = SilentPaymentOwner.fromPrivateKeys(
|
silentAddress = SilentPaymentOwner.fromPrivateKeys(
|
||||||
b_scan: ECPrivate.fromHex(
|
b_scan: ECPrivate.fromHex(masterHd.derivePath(SCAN_PATH).privateKey.toHex()),
|
||||||
masterHd.derivePath("m/352'/1'/0'/1'/0").privateKey.toHex(),
|
b_spend: ECPrivate.fromHex(masterHd.derivePath(SPEND_PATH).privateKey.toHex()),
|
||||||
),
|
network: network,
|
||||||
b_spend: ECPrivate.fromHex(
|
|
||||||
masterHd.derivePath("m/352'/1'/0'/0'/0").privateKey.toHex(),
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
if (silentAddresses.length == 0) {
|
if (silentAddresses.length == 0) {
|
||||||
|
@ -147,13 +144,12 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
||||||
return silentAddress.toString();
|
return silentAddress.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
final typeMatchingAddresses =
|
final typeMatchingAddresses = _addresses.where((addr) => !addr.isHidden && _isAddressPageTypeMatch(addr)).toList();
|
||||||
_addresses.where((addr) => !addr.isHidden && _isAddressPageTypeMatch(addr)).toList();
|
final typeMatchingReceiveAddresses = typeMatchingAddresses.where((addr) => !addr.isUsed).toList();
|
||||||
final typeMatchingReceiveAddresses =
|
|
||||||
typeMatchingAddresses.where((addr) => !addr.isUsed).toList();
|
|
||||||
|
|
||||||
if (!isEnabledAutoGenerateSubaddress) {
|
if (!isEnabledAutoGenerateSubaddress) {
|
||||||
if (previousAddressRecord != null && previousAddressRecord!.type == addressPageType) {
|
if (previousAddressRecord != null &&
|
||||||
|
previousAddressRecord!.type == addressPageType) {
|
||||||
return previousAddressRecord!.address;
|
return previousAddressRecord!.address;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -253,17 +249,17 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
||||||
if (walletInfo.type == WalletType.bitcoinCash) {
|
if (walletInfo.type == WalletType.bitcoinCash) {
|
||||||
await _generateInitialAddresses(type: P2pkhAddressType.p2pkh);
|
await _generateInitialAddresses(type: P2pkhAddressType.p2pkh);
|
||||||
} else if (walletInfo.type == WalletType.litecoin) {
|
} else if (walletInfo.type == WalletType.litecoin) {
|
||||||
await _generateInitialAddresses(type: SegwitAddressType.p2wpkh);
|
await _generateInitialAddresses(type: SegwitAddresType.p2wpkh);
|
||||||
if ((Platform.isAndroid || Platform.isIOS) && !isHardwareWallet) {
|
if ((Platform.isAndroid || Platform.isIOS) && !isHardwareWallet) {
|
||||||
await _generateInitialAddresses(type: SegwitAddressType.mweb);
|
await _generateInitialAddresses(type: SegwitAddresType.mweb);
|
||||||
}
|
}
|
||||||
} else if (walletInfo.type == WalletType.bitcoin) {
|
} else if (walletInfo.type == WalletType.bitcoin) {
|
||||||
await _generateInitialAddresses();
|
await _generateInitialAddresses();
|
||||||
if (!isHardwareWallet) {
|
if (!isHardwareWallet) {
|
||||||
await _generateInitialAddresses(type: P2pkhAddressType.p2pkh);
|
await _generateInitialAddresses(type: P2pkhAddressType.p2pkh);
|
||||||
await _generateInitialAddresses(type: P2shAddressType.p2wpkhInP2sh);
|
await _generateInitialAddresses(type: P2shAddressType.p2wpkhInP2sh);
|
||||||
await _generateInitialAddresses(type: SegwitAddressType.p2tr);
|
await _generateInitialAddresses(type: SegwitAddresType.p2tr);
|
||||||
await _generateInitialAddresses(type: SegwitAddressType.p2wsh);
|
await _generateInitialAddresses(type: SegwitAddresType.p2wsh);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -327,7 +323,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
||||||
BaseBitcoinAddressRecord generateNewAddress({String label = ''}) {
|
BaseBitcoinAddressRecord generateNewAddress({String label = ''}) {
|
||||||
if (addressPageType == SilentPaymentsAddresType.p2sp && silentAddress != null) {
|
if (addressPageType == SilentPaymentsAddresType.p2sp && silentAddress != null) {
|
||||||
final currentSilentAddressIndex = silentAddresses
|
final currentSilentAddressIndex = silentAddresses
|
||||||
.where((addressRecord) => addressRecord.type != SegwitAddressType.p2tr)
|
.where((addressRecord) => addressRecord.type != SegwitAddresType.p2tr)
|
||||||
.length -
|
.length -
|
||||||
1;
|
1;
|
||||||
|
|
||||||
|
@ -385,7 +381,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
||||||
void addBitcoinAddressTypes() {
|
void addBitcoinAddressTypes() {
|
||||||
final lastP2wpkh = _addresses
|
final lastP2wpkh = _addresses
|
||||||
.where((addressRecord) =>
|
.where((addressRecord) =>
|
||||||
_isUnusedReceiveAddressByType(addressRecord, SegwitAddressType.p2wpkh))
|
_isUnusedReceiveAddressByType(addressRecord, SegwitAddresType.p2wpkh))
|
||||||
.toList()
|
.toList()
|
||||||
.last;
|
.last;
|
||||||
if (lastP2wpkh.address != address) {
|
if (lastP2wpkh.address != address) {
|
||||||
|
@ -411,7 +407,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
||||||
}
|
}
|
||||||
|
|
||||||
final lastP2tr = _addresses.firstWhere(
|
final lastP2tr = _addresses.firstWhere(
|
||||||
(addressRecord) => _isUnusedReceiveAddressByType(addressRecord, SegwitAddressType.p2tr));
|
(addressRecord) => _isUnusedReceiveAddressByType(addressRecord, SegwitAddresType.p2tr));
|
||||||
if (lastP2tr.address != address) {
|
if (lastP2tr.address != address) {
|
||||||
addressesMap[lastP2tr.address] = 'P2TR';
|
addressesMap[lastP2tr.address] = 'P2TR';
|
||||||
} else {
|
} else {
|
||||||
|
@ -419,7 +415,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
||||||
}
|
}
|
||||||
|
|
||||||
final lastP2wsh = _addresses.firstWhere(
|
final lastP2wsh = _addresses.firstWhere(
|
||||||
(addressRecord) => _isUnusedReceiveAddressByType(addressRecord, SegwitAddressType.p2wsh));
|
(addressRecord) => _isUnusedReceiveAddressByType(addressRecord, SegwitAddresType.p2wsh));
|
||||||
if (lastP2wsh.address != address) {
|
if (lastP2wsh.address != address) {
|
||||||
addressesMap[lastP2wsh.address] = 'P2WSH';
|
addressesMap[lastP2wsh.address] = 'P2WSH';
|
||||||
} else {
|
} else {
|
||||||
|
@ -444,7 +440,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
||||||
void addLitecoinAddressTypes() {
|
void addLitecoinAddressTypes() {
|
||||||
final lastP2wpkh = _addresses
|
final lastP2wpkh = _addresses
|
||||||
.where((addressRecord) =>
|
.where((addressRecord) =>
|
||||||
_isUnusedReceiveAddressByType(addressRecord, SegwitAddressType.p2wpkh))
|
_isUnusedReceiveAddressByType(addressRecord, SegwitAddresType.p2wpkh))
|
||||||
.toList()
|
.toList()
|
||||||
.last;
|
.last;
|
||||||
if (lastP2wpkh.address != address) {
|
if (lastP2wpkh.address != address) {
|
||||||
|
@ -454,7 +450,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
||||||
}
|
}
|
||||||
|
|
||||||
final lastMweb = _addresses.firstWhere(
|
final lastMweb = _addresses.firstWhere(
|
||||||
(addressRecord) => _isUnusedReceiveAddressByType(addressRecord, SegwitAddressType.mweb));
|
(addressRecord) => _isUnusedReceiveAddressByType(addressRecord, SegwitAddresType.mweb));
|
||||||
if (lastMweb.address != address) {
|
if (lastMweb.address != address) {
|
||||||
addressesMap[lastMweb.address] = 'MWEB';
|
addressesMap[lastMweb.address] = 'MWEB';
|
||||||
} else {
|
} else {
|
||||||
|
@ -564,14 +560,14 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
||||||
addressRecord.isHidden &&
|
addressRecord.isHidden &&
|
||||||
!addressRecord.isUsed &&
|
!addressRecord.isUsed &&
|
||||||
// TODO: feature to change change address type. For now fixed to p2wpkh, the cheapest type
|
// TODO: feature to change change address type. For now fixed to p2wpkh, the cheapest type
|
||||||
(walletInfo.type != WalletType.bitcoin || addressRecord.type == SegwitAddressType.p2wpkh));
|
(walletInfo.type != WalletType.bitcoin || addressRecord.type == SegwitAddresType.p2wpkh));
|
||||||
changeAddresses.addAll(newAddresses);
|
changeAddresses.addAll(newAddresses);
|
||||||
}
|
}
|
||||||
|
|
||||||
@action
|
@action
|
||||||
Future<void> discoverAddresses(List<BitcoinAddressRecord> addressList, bool isHidden,
|
Future<void> discoverAddresses(List<BitcoinAddressRecord> addressList, bool isHidden,
|
||||||
Future<String?> Function(BitcoinAddressRecord) getAddressHistory,
|
Future<String?> Function(BitcoinAddressRecord) getAddressHistory,
|
||||||
{BitcoinAddressType type = SegwitAddressType.p2wpkh}) async {
|
{BitcoinAddressType type = SegwitAddresType.p2wpkh}) async {
|
||||||
final newAddresses = await _createNewAddresses(gap,
|
final newAddresses = await _createNewAddresses(gap,
|
||||||
startIndex: addressList.length, isHidden: isHidden, type: type);
|
startIndex: addressList.length, isHidden: isHidden, type: type);
|
||||||
addAddresses(newAddresses);
|
addAddresses(newAddresses);
|
||||||
|
@ -585,7 +581,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _generateInitialAddresses(
|
Future<void> _generateInitialAddresses(
|
||||||
{BitcoinAddressType type = SegwitAddressType.p2wpkh}) async {
|
{BitcoinAddressType type = SegwitAddresType.p2wpkh}) async {
|
||||||
var countOfReceiveAddresses = 0;
|
var countOfReceiveAddresses = 0;
|
||||||
var countOfHiddenAddresses = 0;
|
var countOfHiddenAddresses = 0;
|
||||||
|
|
||||||
|
@ -662,7 +658,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
||||||
|
|
||||||
void _validateAddresses() {
|
void _validateAddresses() {
|
||||||
_addresses.forEach((element) async {
|
_addresses.forEach((element) async {
|
||||||
if (element.type == SegwitAddressType.mweb) {
|
if (element.type == SegwitAddresType.mweb) {
|
||||||
// this would add a ton of startup lag for mweb addresses since we have 1000 of them
|
// this would add a ton of startup lag for mweb addresses since we have 1000 of them
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -87,8 +87,8 @@ class ElectrumWalletSnapshot {
|
||||||
|
|
||||||
final balance = ElectrumBalance.fromJSON(data['balance'] as String?) ??
|
final balance = ElectrumBalance.fromJSON(data['balance'] as String?) ??
|
||||||
ElectrumBalance(confirmed: 0, unconfirmed: 0, frozen: 0);
|
ElectrumBalance(confirmed: 0, unconfirmed: 0, frozen: 0);
|
||||||
var regularAddressIndexByType = {SegwitAddressType.p2wpkh.toString(): 0};
|
var regularAddressIndexByType = {SegwitAddresType.p2wpkh.toString(): 0};
|
||||||
var changeAddressIndexByType = {SegwitAddressType.p2wpkh.toString(): 0};
|
var changeAddressIndexByType = {SegwitAddresType.p2wpkh.toString(): 0};
|
||||||
var silentAddressIndex = 0;
|
var silentAddressIndex = 0;
|
||||||
|
|
||||||
final derivationType = DerivationType
|
final derivationType = DerivationType
|
||||||
|
@ -97,10 +97,10 @@ class ElectrumWalletSnapshot {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
regularAddressIndexByType = {
|
regularAddressIndexByType = {
|
||||||
SegwitAddressType.p2wpkh.toString(): int.parse(data['account_index'] as String? ?? '0')
|
SegwitAddresType.p2wpkh.toString(): int.parse(data['account_index'] as String? ?? '0')
|
||||||
};
|
};
|
||||||
changeAddressIndexByType = {
|
changeAddressIndexByType = {
|
||||||
SegwitAddressType.p2wpkh.toString():
|
SegwitAddresType.p2wpkh.toString():
|
||||||
int.parse(data['change_address_index'] as String? ?? '0')
|
int.parse(data['change_address_index'] as String? ?? '0')
|
||||||
};
|
};
|
||||||
silentAddressIndex = int.parse(data['silent_address_index'] as String? ?? '0');
|
silentAddressIndex = int.parse(data['silent_address_index'] as String? ?? '0');
|
||||||
|
|
|
@ -16,6 +16,7 @@ import 'package:fixnum/fixnum.dart';
|
||||||
import 'package:bip39/bip39.dart' as bip39;
|
import 'package:bip39/bip39.dart' as bip39;
|
||||||
import 'package:bitcoin_base/bitcoin_base.dart';
|
import 'package:bitcoin_base/bitcoin_base.dart';
|
||||||
import 'package:blockchain_utils/blockchain_utils.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_address_record.dart';
|
||||||
import 'package:cw_bitcoin/bitcoin_mnemonic.dart';
|
import 'package:cw_bitcoin/bitcoin_mnemonic.dart';
|
||||||
import 'package:cw_bitcoin/bitcoin_transaction_priority.dart';
|
import 'package:cw_bitcoin/bitcoin_transaction_priority.dart';
|
||||||
|
@ -970,9 +971,9 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
|
||||||
List<ECPrivateInfo>? inputPrivKeyInfos,
|
List<ECPrivateInfo>? inputPrivKeyInfos,
|
||||||
List<Outpoint>? vinOutpoints,
|
List<Outpoint>? vinOutpoints,
|
||||||
}) async {
|
}) async {
|
||||||
bool spendsMweb = utxos.any((utxo) => utxo.utxo.scriptType == SegwitAddressType.mweb);
|
bool spendsMweb = utxos.any((utxo) => utxo.utxo.scriptType == SegwitAddresType.mweb);
|
||||||
bool paysToMweb = outputs
|
bool paysToMweb = outputs
|
||||||
.any((output) => output.toOutput.scriptPubKey.getAddressType() == SegwitAddressType.mweb);
|
.any((output) => output.toOutput.scriptPubKey.getAddressType() == SegwitAddresType.mweb);
|
||||||
|
|
||||||
bool isRegular = !spendsMweb && !paysToMweb;
|
bool isRegular = !spendsMweb && !paysToMweb;
|
||||||
bool isMweb = spendsMweb || paysToMweb;
|
bool isMweb = spendsMweb || paysToMweb;
|
||||||
|
@ -1063,8 +1064,8 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
|
||||||
tx.isMweb = mwebEnabled;
|
tx.isMweb = mwebEnabled;
|
||||||
|
|
||||||
if (!mwebEnabled) {
|
if (!mwebEnabled) {
|
||||||
tx.changeAddressOverride = (await (walletAddresses as LitecoinWalletAddresses)
|
tx.changeAddressOverride =
|
||||||
.getChangeAddress(coinTypeToSpendFrom: UnspentCoinType.nonMweb))
|
(await (walletAddresses as LitecoinWalletAddresses).getChangeAddress(coinTypeToSpendFrom: UnspentCoinType.nonMweb))
|
||||||
.address;
|
.address;
|
||||||
return tx;
|
return tx;
|
||||||
}
|
}
|
||||||
|
@ -1107,7 +1108,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
|
||||||
|
|
||||||
// check if mweb inputs are used:
|
// check if mweb inputs are used:
|
||||||
for (final utxo in tx.utxos) {
|
for (final utxo in tx.utxos) {
|
||||||
if (utxo.utxo.scriptType == SegwitAddressType.mweb) {
|
if (utxo.utxo.scriptType == SegwitAddresType.mweb) {
|
||||||
hasMwebInput = true;
|
hasMwebInput = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1118,9 +1119,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
|
||||||
bool isRegular = !hasMwebInput && !hasMwebOutput;
|
bool isRegular = !hasMwebInput && !hasMwebOutput;
|
||||||
bool shouldNotUseMwebChange = isPegIn || isRegular || !hasMwebInput;
|
bool shouldNotUseMwebChange = isPegIn || isRegular || !hasMwebInput;
|
||||||
tx.changeAddressOverride = (await (walletAddresses as LitecoinWalletAddresses)
|
tx.changeAddressOverride = (await (walletAddresses as LitecoinWalletAddresses)
|
||||||
.getChangeAddress(
|
.getChangeAddress(coinTypeToSpendFrom: shouldNotUseMwebChange ? UnspentCoinType.nonMweb : UnspentCoinType.any))
|
||||||
coinTypeToSpendFrom:
|
|
||||||
shouldNotUseMwebChange ? UnspentCoinType.nonMweb : UnspentCoinType.any))
|
|
||||||
.address;
|
.address;
|
||||||
if (isRegular) {
|
if (isRegular) {
|
||||||
tx.isMweb = false;
|
tx.isMweb = false;
|
||||||
|
|
|
@ -106,7 +106,7 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with
|
||||||
.map((e) => BitcoinAddressRecord(
|
.map((e) => BitcoinAddressRecord(
|
||||||
e.value,
|
e.value,
|
||||||
index: e.key,
|
index: e.key,
|
||||||
type: SegwitAddressType.mweb,
|
type: SegwitAddresType.mweb,
|
||||||
network: network,
|
network: network,
|
||||||
))
|
))
|
||||||
.toList();
|
.toList();
|
||||||
|
@ -128,7 +128,7 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with
|
||||||
required Bip32Slip10Secp256k1 hd,
|
required Bip32Slip10Secp256k1 hd,
|
||||||
BitcoinAddressType? addressType,
|
BitcoinAddressType? addressType,
|
||||||
}) {
|
}) {
|
||||||
if (addressType == SegwitAddressType.mweb) {
|
if (addressType == SegwitAddresType.mweb) {
|
||||||
return hd == sideHd ? mwebAddrs[0] : mwebAddrs[index + 1];
|
return hd == sideHd ? mwebAddrs[0] : mwebAddrs[index + 1];
|
||||||
}
|
}
|
||||||
return generateP2WPKHAddress(hd: hd, index: index, network: network);
|
return generateP2WPKHAddress(hd: hd, index: index, network: network);
|
||||||
|
@ -140,7 +140,7 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with
|
||||||
required Bip32Slip10Secp256k1 hd,
|
required Bip32Slip10Secp256k1 hd,
|
||||||
BitcoinAddressType? addressType,
|
BitcoinAddressType? addressType,
|
||||||
}) async {
|
}) async {
|
||||||
if (addressType == SegwitAddressType.mweb) {
|
if (addressType == SegwitAddresType.mweb) {
|
||||||
await ensureMwebAddressUpToIndexExists(index);
|
await ensureMwebAddressUpToIndexExists(index);
|
||||||
}
|
}
|
||||||
return getAddress(index: index, hd: hd, addressType: addressType);
|
return getAddress(index: index, hd: hd, addressType: addressType);
|
||||||
|
@ -195,7 +195,7 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with
|
||||||
return BitcoinAddressRecord(
|
return BitcoinAddressRecord(
|
||||||
mwebAddrs[0],
|
mwebAddrs[0],
|
||||||
index: 0,
|
index: 0,
|
||||||
type: SegwitAddressType.mweb,
|
type: SegwitAddresType.mweb,
|
||||||
network: network,
|
network: network,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -207,7 +207,7 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with
|
||||||
String get addressForExchange {
|
String get addressForExchange {
|
||||||
// don't use mweb addresses for exchange refund address:
|
// don't use mweb addresses for exchange refund address:
|
||||||
final addresses = receiveAddresses
|
final addresses = receiveAddresses
|
||||||
.where((element) => element.type == SegwitAddressType.p2wpkh && !element.isUsed);
|
.where((element) => element.type == SegwitAddresType.p2wpkh && !element.isUsed);
|
||||||
return addresses.first.address;
|
return addresses.first.address;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@ import 'dart:typed_data';
|
||||||
import 'package:bitcoin_base/bitcoin_base.dart';
|
import 'package:bitcoin_base/bitcoin_base.dart';
|
||||||
import 'package:cw_bitcoin/bitcoin_wallet.dart';
|
import 'package:cw_bitcoin/bitcoin_wallet.dart';
|
||||||
import 'package:cw_bitcoin/bitcoin_wallet_addresses.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_receive_worker.dart';
|
||||||
import 'package:cw_bitcoin/payjoin/payjoin_send_worker.dart';
|
import 'package:cw_bitcoin/payjoin/payjoin_send_worker.dart';
|
||||||
import 'package:cw_bitcoin/payjoin/payjoin_session_errors.dart';
|
import 'package:cw_bitcoin/payjoin/payjoin_session_errors.dart';
|
||||||
|
@ -16,6 +17,7 @@ import 'package:cw_core/utils/print_verbose.dart';
|
||||||
import 'package:payjoin_flutter/common.dart';
|
import 'package:payjoin_flutter/common.dart';
|
||||||
import 'package:payjoin_flutter/receive.dart';
|
import 'package:payjoin_flutter/receive.dart';
|
||||||
import 'package:payjoin_flutter/send.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;
|
import 'package:payjoin_flutter/uri.dart' as PayjoinUri;
|
||||||
|
|
||||||
class PayjoinManager {
|
class PayjoinManager {
|
||||||
|
@ -31,11 +33,13 @@ class PayjoinManager {
|
||||||
'https://ohttp.cakewallet.com',
|
'https://ohttp.cakewallet.com',
|
||||||
];
|
];
|
||||||
|
|
||||||
static Future<PayjoinUri.Url> randomOhttpRelayUrl() =>
|
static String randomOhttpRelayUrl() =>
|
||||||
PayjoinUri.Url.fromStr(ohttpRelayUrls[Random.secure().nextInt(ohttpRelayUrls.length)]);
|
ohttpRelayUrls[Random.secure().nextInt(ohttpRelayUrls.length)];
|
||||||
|
|
||||||
static const payjoinDirectoryUrl = 'https://payjo.in';
|
static const payjoinDirectoryUrl = 'https://payjo.in';
|
||||||
|
|
||||||
|
Future<void> initPayjoin() => pj_config.PConfig.initializeApp();
|
||||||
|
|
||||||
Future<void> resumeSessions() async {
|
Future<void> resumeSessions() async {
|
||||||
final allSessions = _payjoinStorage.readAllOpenSessions(_wallet.id);
|
final allSessions = _payjoinStorage.readAllOpenSessions(_wallet.id);
|
||||||
|
|
||||||
|
@ -43,13 +47,13 @@ class PayjoinManager {
|
||||||
if (session.isSenderSession) {
|
if (session.isSenderSession) {
|
||||||
printV("Resuming Payjoin Sender Session ${session.pjUri!}");
|
printV("Resuming Payjoin Sender Session ${session.pjUri!}");
|
||||||
return _spawnSender(
|
return _spawnSender(
|
||||||
sender: Sender.fromJson(session.sender!),
|
sender: Sender.fromJson(json: session.sender!),
|
||||||
pjUri: session.pjUri!,
|
pjUri: session.pjUri!,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
final receiver = Receiver.fromJson(session.receiver!);
|
final receiver = Receiver.fromJson(json: session.receiver!);
|
||||||
printV("Resuming Payjoin Receiver Session ${receiver.id()}");
|
printV("Resuming Payjoin Receiver Session ${receiver.id()}");
|
||||||
return _spawnReceiver(receiver: receiver);
|
return spawnReceiver(receiver: receiver);
|
||||||
});
|
});
|
||||||
|
|
||||||
printV("Resumed ${spawnedSessions.length} Payjoin Sessions");
|
printV("Resumed ${spawnedSessions.length} Payjoin Sessions");
|
||||||
|
@ -59,13 +63,19 @@ class PayjoinManager {
|
||||||
Future<Sender> initSender(
|
Future<Sender> initSender(
|
||||||
String pjUriString, String originalPsbt, int networkFeesSatPerVb) async {
|
String pjUriString, String originalPsbt, int networkFeesSatPerVb) async {
|
||||||
try {
|
try {
|
||||||
final pjUri = (await PayjoinUri.Uri.fromStr(pjUriString)).checkPjSupported();
|
final pjUri =
|
||||||
|
(await PayjoinUri.Uri.fromStr(pjUriString)).checkPjSupported();
|
||||||
final minFeeRateSatPerKwu = BigInt.from(networkFeesSatPerVb * 250);
|
final minFeeRateSatPerKwu = BigInt.from(networkFeesSatPerVb * 250);
|
||||||
final senderBuilder = await SenderBuilder.fromPsbtAndUri(
|
final senderBuilder = await SenderBuilder.fromPsbtAndUri(
|
||||||
psbtBase64: originalPsbt,
|
psbtBase64: originalPsbt,
|
||||||
pjUri: pjUri,
|
pjUri: pjUri,
|
||||||
);
|
);
|
||||||
return senderBuilder.buildRecommended(minFeeRate: minFeeRateSatPerKwu);
|
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);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
throw Exception('Error initializing Payjoin Sender: $e');
|
throw Exception('Error initializing Payjoin Sender: $e');
|
||||||
}
|
}
|
||||||
|
@ -78,7 +88,8 @@ class PayjoinManager {
|
||||||
bool isTestnet = false,
|
bool isTestnet = false,
|
||||||
}) async {
|
}) async {
|
||||||
final pjUri = Uri.parse(pjUrl).queryParameters['pj']!;
|
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);
|
return _spawnSender(isTestnet: isTestnet, sender: sender, pjUri: pjUri);
|
||||||
}
|
}
|
||||||
|
@ -110,15 +121,13 @@ class PayjoinManager {
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
_cleanupSession(pjUri);
|
_cleanupSession(pjUri);
|
||||||
printV(e);
|
await _payjoinStorage.markSenderSessionUnrecoverable(pjUri, e.toString());
|
||||||
await _payjoinStorage.markSenderSessionUnrecoverable(pjUri);
|
completer.complete();
|
||||||
completer.completeError(e);
|
|
||||||
}
|
}
|
||||||
} else if (message is PayjoinSessionError) {
|
} else if (message is PayjoinSessionError) {
|
||||||
_cleanupSession(pjUri);
|
_cleanupSession(pjUri);
|
||||||
if (message is UnrecoverableError) {
|
if (message is UnrecoverableError) {
|
||||||
printV(message.message);
|
await _payjoinStorage.markSenderSessionUnrecoverable(pjUri, message.message);
|
||||||
await _payjoinStorage.markSenderSessionUnrecoverable(pjUri);
|
|
||||||
completer.complete();
|
completer.complete();
|
||||||
} else if (message is RecoverableError) {
|
} else if (message is RecoverableError) {
|
||||||
completer.complete();
|
completer.complete();
|
||||||
|
@ -138,40 +147,41 @@ class PayjoinManager {
|
||||||
return completer.future;
|
return completer.future;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<Receiver> initReceiver(String address, [bool isTestnet = false]) async {
|
Future<Receiver> getUnusedReceiver(String address,
|
||||||
try {
|
[bool isTestnet = false]) async {
|
||||||
final payjoinDirectory = await PayjoinUri.Url.fromStr(payjoinDirectoryUrl);
|
final session = _payjoinStorage.getUnusedActiveReceiverSession(_wallet.id);
|
||||||
|
|
||||||
|
if (session != null) {
|
||||||
|
await PayjoinUri.Url.fromStr(payjoinDirectoryUrl);
|
||||||
|
|
||||||
|
return Receiver.fromJson(json: session.receiver!);
|
||||||
|
}
|
||||||
|
|
||||||
|
return initReceiver(address);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<Receiver> initReceiver(String address, [bool isTestnet = false]) async {
|
||||||
final ohttpKeys = await PayjoinUri.fetchOhttpKeys(
|
final ohttpKeys = await PayjoinUri.fetchOhttpKeys(
|
||||||
ohttpRelay: await randomOhttpRelayUrl(),
|
ohttpRelay: await randomOhttpRelayUrl(),
|
||||||
payjoinDirectory: payjoinDirectory,
|
payjoinDirectory: payjoinDirectoryUrl,
|
||||||
);
|
);
|
||||||
|
|
||||||
final receiver = await Receiver.create(
|
final newReceiver = await NewReceiver.create(
|
||||||
address: address,
|
address: address,
|
||||||
network: isTestnet ? Network.testnet : Network.bitcoin,
|
network: isTestnet ? Network.testnet : Network.bitcoin,
|
||||||
directory: payjoinDirectory,
|
directory: payjoinDirectoryUrl,
|
||||||
ohttpKeys: ohttpKeys,
|
ohttpKeys: ohttpKeys,
|
||||||
ohttpRelay: await randomOhttpRelayUrl(),
|
|
||||||
);
|
);
|
||||||
|
final persister = PayjoinReceiverPersister.impl();
|
||||||
|
final receiverToken = await newReceiver.persist(persister: persister);
|
||||||
|
final receiver = await Receiver.load(persister: persister, token: receiverToken);
|
||||||
|
|
||||||
await _payjoinStorage.insertReceiverSession(receiver, _wallet.id);
|
await _payjoinStorage.insertReceiverSession(receiver, _wallet.id);
|
||||||
|
|
||||||
return receiver;
|
return receiver;
|
||||||
} catch (e) {
|
|
||||||
throw Exception('Error initializing Payjoin Receiver: $e');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> spawnNewReceiver({
|
Future<void> spawnReceiver({
|
||||||
required Receiver receiver,
|
|
||||||
bool isTestnet = false,
|
|
||||||
}) async {
|
|
||||||
await _payjoinStorage.insertReceiverSession(receiver, _wallet.id);
|
|
||||||
return _spawnReceiver(isTestnet: isTestnet, receiver: receiver);
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> _spawnReceiver({
|
|
||||||
required Receiver receiver,
|
required Receiver receiver,
|
||||||
bool isTestnet = false,
|
bool isTestnet = false,
|
||||||
}) async {
|
}) async {
|
||||||
|
@ -191,11 +201,13 @@ class PayjoinManager {
|
||||||
rawAmount = getOutputAmountFromTx(tx, _wallet);
|
rawAmount = getOutputAmountFromTx(tx, _wallet);
|
||||||
break;
|
break;
|
||||||
case PayjoinReceiverRequestTypes.checkIsOwned:
|
case PayjoinReceiverRequestTypes.checkIsOwned:
|
||||||
(_wallet.walletAddresses as BitcoinWalletAddresses).newPayjoinReceiver();
|
(_wallet.walletAddresses as BitcoinWalletAddresses)
|
||||||
|
.newPayjoinReceiver();
|
||||||
_payjoinStorage.markReceiverSessionInProgress(receiver.id());
|
_payjoinStorage.markReceiverSessionInProgress(receiver.id());
|
||||||
|
|
||||||
final inputScript = message['input_script'] as Uint8List;
|
final inputScript = message['input_script'] as Uint8List;
|
||||||
final isOwned = _wallet.isMine(Script.fromRaw(bytes: inputScript));
|
final isOwned =
|
||||||
|
_wallet.isMine(Script.fromRaw(byteData: inputScript));
|
||||||
mainToIsolateSendPort?.send({
|
mainToIsolateSendPort?.send({
|
||||||
'requestId': message['requestId'],
|
'requestId': message['requestId'],
|
||||||
'result': isOwned,
|
'result': isOwned,
|
||||||
|
@ -204,7 +216,8 @@ class PayjoinManager {
|
||||||
|
|
||||||
case PayjoinReceiverRequestTypes.checkIsReceiverOutput:
|
case PayjoinReceiverRequestTypes.checkIsReceiverOutput:
|
||||||
final outputScript = message['output_script'] as Uint8List;
|
final outputScript = message['output_script'] as Uint8List;
|
||||||
final isReceiverOutput = _wallet.isMine(Script.fromRaw(bytes: outputScript));
|
final isReceiverOutput =
|
||||||
|
_wallet.isMine(Script.fromRaw(byteData: outputScript));
|
||||||
mainToIsolateSendPort?.send({
|
mainToIsolateSendPort?.send({
|
||||||
'requestId': message['requestId'],
|
'requestId': message['requestId'],
|
||||||
'result': isReceiverOutput,
|
'result': isReceiverOutput,
|
||||||
|
@ -213,6 +226,10 @@ class PayjoinManager {
|
||||||
|
|
||||||
case PayjoinReceiverRequestTypes.getCandidateInputs:
|
case PayjoinReceiverRequestTypes.getCandidateInputs:
|
||||||
utxos = _wallet.getUtxoWithPrivateKeys();
|
utxos = _wallet.getUtxoWithPrivateKeys();
|
||||||
|
if (utxos.isEmpty) {
|
||||||
|
await _wallet.updateAllUnspents();
|
||||||
|
utxos = _wallet.getUtxoWithPrivateKeys();
|
||||||
|
}
|
||||||
mainToIsolateSendPort?.send({
|
mainToIsolateSendPort?.send({
|
||||||
'requestId': message['requestId'],
|
'requestId': message['requestId'],
|
||||||
'result': utxos,
|
'result': utxos,
|
||||||
|
@ -237,13 +254,15 @@ class PayjoinManager {
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
_cleanupSession(receiver.id());
|
_cleanupSession(receiver.id());
|
||||||
await _payjoinStorage.markReceiverSessionUnrecoverable(receiver.id(), e.toString());
|
await _payjoinStorage.markReceiverSessionUnrecoverable(
|
||||||
|
receiver.id(), e.toString());
|
||||||
completer.completeError(e);
|
completer.completeError(e);
|
||||||
}
|
}
|
||||||
} else if (message is PayjoinSessionError) {
|
} else if (message is PayjoinSessionError) {
|
||||||
_cleanupSession(receiver.id());
|
_cleanupSession(receiver.id());
|
||||||
if (message is UnrecoverableError) {
|
if (message is UnrecoverableError) {
|
||||||
await _payjoinStorage.markReceiverSessionUnrecoverable(receiver.id(), message.message);
|
await _payjoinStorage.markReceiverSessionUnrecoverable(
|
||||||
|
receiver.id(), message.message);
|
||||||
completer.complete();
|
completer.complete();
|
||||||
} else if (message is RecoverableError) {
|
} else if (message is RecoverableError) {
|
||||||
completer.complete();
|
completer.complete();
|
||||||
|
|
66
cw_bitcoin/lib/payjoin/payjoin_persister.dart
Normal file
66
cw_bitcoin/lib/payjoin/payjoin_persister.dart
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
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<String, FfiSender> _store = {};
|
||||||
|
|
||||||
|
Future<SenderToken> save({required FfiSender sender}) async {
|
||||||
|
final token = sender.key();
|
||||||
|
_store[token.toBytes().toString()] = sender;
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<FfiSender> 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<String, FfiReceiver> _store = {};
|
||||||
|
|
||||||
|
Future<ReceiverToken> save({required FfiReceiver receiver}) async {
|
||||||
|
final token = receiver.key();
|
||||||
|
_store[token.toBytes().toString()] = receiver;
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<FfiReceiver> 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;
|
||||||
|
}
|
|
@ -4,14 +4,16 @@ import 'dart:isolate';
|
||||||
import 'dart:typed_data';
|
import 'dart:typed_data';
|
||||||
|
|
||||||
import 'package:blockchain_utils/blockchain_utils.dart';
|
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/payjoin/payjoin_session_errors.dart';
|
||||||
import 'package:cw_bitcoin/psbt/signer.dart';
|
import 'package:cw_bitcoin/psbt/signer.dart';
|
||||||
import 'package:cw_core/utils/print_verbose.dart';
|
import 'package:cw_core/utils/print_verbose.dart';
|
||||||
import 'package:http/http.dart' as http;
|
import 'package:cw_core/utils/proxy_wrapper.dart';
|
||||||
import 'package:payjoin_flutter/bitcoin_ffi.dart';
|
import 'package:payjoin_flutter/bitcoin_ffi.dart';
|
||||||
import 'package:payjoin_flutter/common.dart';
|
import 'package:payjoin_flutter/common.dart';
|
||||||
import 'package:payjoin_flutter/receive.dart';
|
import 'package:payjoin_flutter/receive.dart';
|
||||||
import 'package:payjoin_flutter/src/generated/frb_generated.dart' as pj;
|
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 {
|
enum PayjoinReceiverRequestTypes {
|
||||||
processOriginalTx,
|
processOriginalTx,
|
||||||
|
@ -27,7 +29,7 @@ class PayjoinReceiverWorker {
|
||||||
final pendingRequests = <String, Completer<dynamic>>{};
|
final pendingRequests = <String, Completer<dynamic>>{};
|
||||||
|
|
||||||
PayjoinReceiverWorker._(this.sendPort);
|
PayjoinReceiverWorker._(this.sendPort);
|
||||||
|
static final client = ProxyWrapper().getHttpIOClient();
|
||||||
static Future<void> run(List<Object> args) async {
|
static Future<void> run(List<Object> args) async {
|
||||||
await pj.core.init();
|
await pj.core.init();
|
||||||
|
|
||||||
|
@ -41,11 +43,10 @@ class PayjoinReceiverWorker {
|
||||||
receivePort.listen(worker.handleMessage);
|
receivePort.listen(worker.handleMessage);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final httpClient = http.Client();
|
final receiver = Receiver.fromJson(json: receiverJson);
|
||||||
final receiver = Receiver.fromJson(receiverJson);
|
|
||||||
|
|
||||||
final uncheckedProposal =
|
final uncheckedProposal =
|
||||||
await worker.receiveUncheckedProposal(httpClient, receiver);
|
await worker.receiveUncheckedProposal(receiver);
|
||||||
|
|
||||||
final originalTx = await uncheckedProposal.extractTxToScheduleBroadcast();
|
final originalTx = await uncheckedProposal.extractTxToScheduleBroadcast();
|
||||||
sendPort.send({
|
sendPort.send({
|
||||||
|
@ -56,14 +57,14 @@ class PayjoinReceiverWorker {
|
||||||
final payjoinProposal = await worker.processPayjoinProposal(
|
final payjoinProposal = await worker.processPayjoinProposal(
|
||||||
uncheckedProposal,
|
uncheckedProposal,
|
||||||
);
|
);
|
||||||
final psbt = await worker.sendFinalProposal(httpClient, payjoinProposal);
|
final psbt = await worker.sendFinalProposal(payjoinProposal);
|
||||||
sendPort.send({
|
sendPort.send({
|
||||||
'type': PayjoinReceiverRequestTypes.proposalSent,
|
'type': PayjoinReceiverRequestTypes.proposalSent,
|
||||||
'psbt': psbt,
|
'psbt': psbt,
|
||||||
});
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e is HttpException ||
|
if (e is HttpException ||
|
||||||
(e is http.ClientException &&
|
(e is very_insecure_http_do_not_use.ClientException &&
|
||||||
e.message.contains("Software caused connection abort"))) {
|
e.message.contains("Software caused connection abort"))) {
|
||||||
sendPort.send(PayjoinSessionError.recoverable(e.toString()));
|
sendPort.send(PayjoinSessionError.recoverable(e.toString()));
|
||||||
} else {
|
} else {
|
||||||
|
@ -97,15 +98,16 @@ class PayjoinReceiverWorker {
|
||||||
return completer.future;
|
return completer.future;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<UncheckedProposal> receiveUncheckedProposal(
|
Future<UncheckedProposal> receiveUncheckedProposal(Receiver session) async {
|
||||||
http.Client httpClient, Receiver session) async {
|
|
||||||
while (true) {
|
while (true) {
|
||||||
printV("Polling for Proposal (${session.id()})");
|
printV("Polling for Proposal (${session.id()})");
|
||||||
final extractReq = await session.extractReq();
|
final extractReq = await session.extractReq(
|
||||||
|
ohttpRelay: await PayjoinManager.randomOhttpRelayUrl(),
|
||||||
|
);
|
||||||
final request = extractReq.$1;
|
final request = extractReq.$1;
|
||||||
|
|
||||||
final url = Uri.parse(request.url.asString());
|
final url = Uri.parse(request.url.asString());
|
||||||
final httpRequest = await httpClient.post(url,
|
final httpRequest = await client.post(url,
|
||||||
headers: {'Content-Type': request.contentType}, body: request.body);
|
headers: {'Content-Type': request.contentType}, body: request.body);
|
||||||
|
|
||||||
final proposal = await session.processRes(
|
final proposal = await session.processRes(
|
||||||
|
@ -114,13 +116,14 @@ class PayjoinReceiverWorker {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<String> sendFinalProposal(
|
Future<String> sendFinalProposal(PayjoinProposal finalProposal) async {
|
||||||
http.Client httpClient, PayjoinProposal finalProposal) async {
|
final req = await finalProposal.extractReq(
|
||||||
final req = await finalProposal.extractV2Req();
|
ohttpRelay: await PayjoinManager.randomOhttpRelayUrl(),
|
||||||
|
);
|
||||||
final proposalReq = req.$1;
|
final proposalReq = req.$1;
|
||||||
final proposalCtx = req.$2;
|
final proposalCtx = req.$2;
|
||||||
|
|
||||||
final request = await httpClient.post(
|
final request = await client.post(
|
||||||
Uri.parse(proposalReq.url.asString()),
|
Uri.parse(proposalReq.url.asString()),
|
||||||
headers: {"Content-Type": proposalReq.contentType},
|
headers: {"Content-Type": proposalReq.contentType},
|
||||||
body: proposalReq.body,
|
body: proposalReq.body,
|
||||||
|
@ -171,7 +174,7 @@ class PayjoinReceiverWorker {
|
||||||
final listUnspent =
|
final listUnspent =
|
||||||
await _sendRequest(PayjoinReceiverRequestTypes.getCandidateInputs);
|
await _sendRequest(PayjoinReceiverRequestTypes.getCandidateInputs);
|
||||||
final unspent = listUnspent as List<UtxoWithPrivateKey>;
|
final unspent = listUnspent as List<UtxoWithPrivateKey>;
|
||||||
if (unspent.isEmpty) throw Exception('No unspent outputs available');
|
if (unspent.isEmpty) throw RecoverableError('No unspent outputs available');
|
||||||
|
|
||||||
final selectedUtxo = await _inputPairFromUtxo(unspent[0]);
|
final selectedUtxo = await _inputPairFromUtxo(unspent[0]);
|
||||||
final pj6 = await pj5.contributeInputs(replacementInputs: [selectedUtxo]);
|
final pj6 = await pj5.contributeInputs(replacementInputs: [selectedUtxo]);
|
||||||
|
@ -214,6 +217,6 @@ class PayjoinReceiverWorker {
|
||||||
sequence: 0,
|
sequence: 0,
|
||||||
);
|
);
|
||||||
|
|
||||||
return InputPair.newInstance(txin, psbtin);
|
return InputPair.newInstance(txin: txin, psbtin: psbtin);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,10 +5,12 @@ import 'dart:isolate';
|
||||||
import 'package:cw_bitcoin/payjoin/manager.dart';
|
import 'package:cw_bitcoin/payjoin/manager.dart';
|
||||||
import 'package:cw_bitcoin/payjoin/payjoin_session_errors.dart';
|
import 'package:cw_bitcoin/payjoin/payjoin_session_errors.dart';
|
||||||
import 'package:cw_core/utils/print_verbose.dart';
|
import 'package:cw_core/utils/print_verbose.dart';
|
||||||
import 'package:http/http.dart' as http;
|
import 'package:cw_core/utils/proxy_wrapper.dart';
|
||||||
import 'package:payjoin_flutter/common.dart';
|
import 'package:payjoin_flutter/common.dart';
|
||||||
import 'package:payjoin_flutter/send.dart';
|
import 'package:payjoin_flutter/send.dart';
|
||||||
import 'package:payjoin_flutter/src/generated/frb_generated.dart' as pj;
|
import 'package:payjoin_flutter/src/generated/frb_generated.dart' as pj;
|
||||||
|
import 'package:payjoin_flutter/src/generated/api/send/error.dart' as pj_error;
|
||||||
|
import 'package:payjoin_flutter/uri.dart' as pj_uri;
|
||||||
|
|
||||||
enum PayjoinSenderRequestTypes {
|
enum PayjoinSenderRequestTypes {
|
||||||
requestPosted,
|
requestPosted,
|
||||||
|
@ -29,7 +31,7 @@ class PayjoinSenderWorker {
|
||||||
final senderJson = args[1] as String;
|
final senderJson = args[1] as String;
|
||||||
final pjUrl = args[2] as String;
|
final pjUrl = args[2] as String;
|
||||||
|
|
||||||
final sender = Sender.fromJson(senderJson);
|
final sender = Sender.fromJson(json: senderJson);
|
||||||
final worker = PayjoinSenderWorker._(sendPort, pjUrl);
|
final worker = PayjoinSenderWorker._(sendPort, pjUrl);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -42,19 +44,17 @@ class PayjoinSenderWorker {
|
||||||
sendPort.send(e);
|
sendPort.send(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
final client = ProxyWrapper().getHttpIOClient();
|
||||||
|
|
||||||
/// Run a payjoin sender (V2 protocol first, fallback to V1).
|
/// Run a payjoin sender (V2 protocol first, fallback to V1).
|
||||||
Future<String> runSender(Sender sender) async {
|
Future<String> runSender(Sender sender) async {
|
||||||
final httpClient = http.Client();
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return await _runSenderV2(sender, httpClient);
|
return await _runSenderV2(sender);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
printV(e);
|
printV(e);
|
||||||
if (e is PayjoinException &&
|
if (e is pj_error.FfiCreateRequestError) {
|
||||||
// TODO condition on error type instead of message content
|
return await _runSenderV1(sender);
|
||||||
e.message?.contains('parse receiver public key') == true) {
|
|
||||||
return await _runSenderV1(sender, httpClient);
|
|
||||||
} else if (e is HttpException) {
|
} else if (e is HttpException) {
|
||||||
printV(e);
|
printV(e);
|
||||||
throw Exception(PayjoinSessionError.recoverable(e.toString()));
|
throw Exception(PayjoinSessionError.recoverable(e.toString()));
|
||||||
|
@ -65,13 +65,14 @@ class PayjoinSenderWorker {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Attempt to send payjoin using the V2 of the protocol.
|
/// Attempt to send payjoin using the V2 of the protocol.
|
||||||
Future<String> _runSenderV2(Sender sender, http.Client httpClient) async {
|
Future<String> _runSenderV2(Sender sender) async {
|
||||||
try {
|
try {
|
||||||
final postRequest = await sender.extractV2(
|
final postRequest = await sender.extractV2(
|
||||||
ohttpProxyUrl: await PayjoinManager.randomOhttpRelayUrl(),
|
ohttpProxyUrl:
|
||||||
|
await pj_uri.Url.fromStr(PayjoinManager.randomOhttpRelayUrl()),
|
||||||
);
|
);
|
||||||
|
|
||||||
final postResult = await _postRequest(httpClient, postRequest.$1);
|
final postResult = await _postRequest(postRequest.$1);
|
||||||
final getContext =
|
final getContext =
|
||||||
await postRequest.$2.processResponse(response: postResult);
|
await postRequest.$2.processResponse(response: postResult);
|
||||||
|
|
||||||
|
@ -83,7 +84,7 @@ class PayjoinSenderWorker {
|
||||||
final getRequest = await getContext.extractReq(
|
final getRequest = await getContext.extractReq(
|
||||||
ohttpRelay: await PayjoinManager.randomOhttpRelayUrl(),
|
ohttpRelay: await PayjoinManager.randomOhttpRelayUrl(),
|
||||||
);
|
);
|
||||||
final getRes = await _postRequest(httpClient, getRequest.$1);
|
final getRes = await _postRequest(getRequest.$1);
|
||||||
final proposalPsbt = await getContext.processResponse(
|
final proposalPsbt = await getContext.processResponse(
|
||||||
response: getRes,
|
response: getRes,
|
||||||
ohttpCtx: getRequest.$2,
|
ohttpCtx: getRequest.$2,
|
||||||
|
@ -97,20 +98,20 @@ class PayjoinSenderWorker {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Attempt to send payjoin using the V1 of the protocol.
|
/// Attempt to send payjoin using the V1 of the protocol.
|
||||||
Future<String> _runSenderV1(Sender sender, http.Client httpClient) async {
|
Future<String> _runSenderV1(Sender sender) async {
|
||||||
try {
|
try {
|
||||||
final postRequest = await sender.extractV1();
|
final postRequest = await sender.extractV1();
|
||||||
final response = await _postRequest(httpClient, postRequest.$1);
|
final response = await _postRequest(postRequest.$1);
|
||||||
|
|
||||||
sendPort.send({'type': PayjoinSenderRequestTypes.requestPosted});
|
sendPort.send({'type': PayjoinSenderRequestTypes.requestPosted});
|
||||||
|
|
||||||
return await postRequest.$2.processResponse(response: response);
|
return await postRequest.$2.processResponse(response: response);
|
||||||
} catch (e) {
|
} catch (e, stack) {
|
||||||
throw PayjoinSessionError.unrecoverable('Send V1 payjoin error: $e');
|
throw PayjoinSessionError.unrecoverable('Send V1 payjoin error: $e, $stack');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<List<int>> _postRequest(http.Client client, Request req) async {
|
Future<List<int>> _postRequest(Request req) async {
|
||||||
final httpRequest = await client.post(Uri.parse(req.url.asString()),
|
final httpRequest = await client.post(Uri.parse(req.url.asString()),
|
||||||
headers: {'Content-Type': req.contentType}, body: req.body);
|
headers: {'Content-Type': req.contentType}, body: req.body);
|
||||||
|
|
||||||
|
|
|
@ -23,6 +23,14 @@ class PayjoinStorage {
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
PayjoinSession? getUnusedActiveReceiverSession(String walletId) =>
|
||||||
|
_payjoinSessionSources.values
|
||||||
|
.where((session) =>
|
||||||
|
session.walletId == walletId &&
|
||||||
|
session.status == PayjoinSessionStatus.created.name &&
|
||||||
|
!session.isSenderSession)
|
||||||
|
.firstOrNull;
|
||||||
|
|
||||||
Future<void> markReceiverSessionComplete(
|
Future<void> markReceiverSessionComplete(
|
||||||
String sessionId, String txId, String amount) async {
|
String sessionId, String txId, String amount) async {
|
||||||
final session = _payjoinSessionSources.get("$_receiverPrefix${sessionId}")!;
|
final session = _payjoinSessionSources.get("$_receiverPrefix${sessionId}")!;
|
||||||
|
@ -76,10 +84,11 @@ class PayjoinStorage {
|
||||||
await session.save();
|
await session.save();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> markSenderSessionUnrecoverable(String pjUrl) async {
|
Future<void> markSenderSessionUnrecoverable(String pjUrl, String reason) async {
|
||||||
final session = _payjoinSessionSources.get("$_senderPrefix$pjUrl")!;
|
final session = _payjoinSessionSources.get("$_senderPrefix$pjUrl")!;
|
||||||
|
|
||||||
session.status = PayjoinSessionStatus.unrecoverable.name;
|
session.status = PayjoinSessionStatus.unrecoverable.name;
|
||||||
|
session.error = reason;
|
||||||
await session.save();
|
await session.save();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -40,7 +40,8 @@ extension PsbtSigner on PsbtV2 {
|
||||||
return tx.buffer();
|
return tx.buffer();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> signWithUTXO(List<UtxoWithPrivateKey> utxos, UTXOSignerCallBack signer,
|
Future<void> signWithUTXO(
|
||||||
|
List<UtxoWithPrivateKey> utxos, UTXOSignerCallBack signer,
|
||||||
[UTXOGetterCallBack? getTaprootPair]) async {
|
[UTXOGetterCallBack? getTaprootPair]) async {
|
||||||
final raw = BytesUtils.toHexString(extractUnsignedTX(getSegwit: false));
|
final raw = BytesUtils.toHexString(extractUnsignedTX(getSegwit: false));
|
||||||
final tx = BtcTransaction.fromRaw(raw);
|
final tx = BtcTransaction.fromRaw(raw);
|
||||||
|
@ -50,10 +51,10 @@ extension PsbtSigner on PsbtV2 {
|
||||||
List<BigInt> taprootAmounts = [];
|
List<BigInt> taprootAmounts = [];
|
||||||
List<Script> taprootScripts = [];
|
List<Script> taprootScripts = [];
|
||||||
|
|
||||||
if (utxos.any((e) => e.utxo.isP2tr)) {
|
if (utxos.any((e) => e.utxo.isP2tr())) {
|
||||||
for (final input in tx.inputs) {
|
for (final input in tx.inputs) {
|
||||||
final utxo = utxos
|
final utxo = utxos.firstWhereOrNull(
|
||||||
.firstWhereOrNull((u) => u.utxo.txHash == input.txId && u.utxo.vout == input.txIndex);
|
(u) => u.utxo.txHash == input.txId && u.utxo.vout == input.txIndex);
|
||||||
|
|
||||||
if (utxo == null) {
|
if (utxo == null) {
|
||||||
final trPair = await getTaprootPair!.call(input.txId, input.txIndex);
|
final trPair = await getTaprootPair!.call(input.txId, input.txIndex);
|
||||||
|
@ -75,29 +76,37 @@ extension PsbtSigner on PsbtV2 {
|
||||||
/// We receive the owner's ScriptPubKey
|
/// We receive the owner's ScriptPubKey
|
||||||
final script = _findLockingScript(utxo, false);
|
final script = _findLockingScript(utxo, false);
|
||||||
|
|
||||||
final int sighash =
|
final int sighash = utxo.utxo.isP2tr()
|
||||||
utxo.utxo.isP2tr ? BitcoinOpCodeConst.sighashDefault : BitcoinOpCodeConst.sighashAll;
|
? BitcoinOpCodeConst.TAPROOT_SIGHASH_ALL
|
||||||
|
: BitcoinOpCodeConst.SIGHASH_ALL;
|
||||||
|
|
||||||
/// We generate transaction digest for current input
|
/// We generate transaction digest for current input
|
||||||
final digest =
|
final digest = _generateTransactionDigest(
|
||||||
_generateTransactionDigest(script, i, utxo.utxo, tx, taprootAmounts, taprootScripts);
|
script, i, utxo.utxo, tx, taprootAmounts, taprootScripts);
|
||||||
|
|
||||||
/// now we need sign the transaction digest
|
/// now we need sign the transaction digest
|
||||||
final sig = signer(digest, utxo, utxo.privateKey, sighash);
|
final sig = signer(digest, utxo, utxo.privateKey, sighash);
|
||||||
|
|
||||||
if (utxo.utxo.isP2tr) {
|
if (utxo.utxo.isP2tr()) {
|
||||||
setInputTapKeySig(i, Uint8List.fromList(BytesUtils.fromHexString(sig)));
|
setInputTapKeySig(i, Uint8List.fromList(BytesUtils.fromHexString(sig)));
|
||||||
} else {
|
} else {
|
||||||
setInputPartialSig(i, Uint8List.fromList(BytesUtils.fromHexString(utxo.public().toHex())),
|
setInputPartialSig(
|
||||||
|
i,
|
||||||
|
Uint8List.fromList(BytesUtils.fromHexString(utxo.public().toHex())),
|
||||||
Uint8List.fromList(BytesUtils.fromHexString(sig)));
|
Uint8List.fromList(BytesUtils.fromHexString(sig)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
List<int> _generateTransactionDigest(Script scriptPubKeys, int input, BitcoinUtxo utxo,
|
List<int> _generateTransactionDigest(
|
||||||
BtcTransaction transaction, List<BigInt> taprootAmounts, List<Script> tapRootPubKeys) {
|
Script scriptPubKeys,
|
||||||
if (utxo.isSegwit) {
|
int input,
|
||||||
if (utxo.isP2tr) {
|
BitcoinUtxo utxo,
|
||||||
|
BtcTransaction transaction,
|
||||||
|
List<BigInt> taprootAmounts,
|
||||||
|
List<Script> tapRootPubKeys) {
|
||||||
|
if (utxo.isSegwit()) {
|
||||||
|
if (utxo.isP2tr()) {
|
||||||
return transaction.getTransactionTaprootDigset(
|
return transaction.getTransactionTaprootDigset(
|
||||||
txIndex: input,
|
txIndex: input,
|
||||||
scriptPubKeys: tapRootPubKeys,
|
scriptPubKeys: tapRootPubKeys,
|
||||||
|
@ -107,7 +116,8 @@ extension PsbtSigner on PsbtV2 {
|
||||||
return transaction.getTransactionSegwitDigit(
|
return transaction.getTransactionSegwitDigit(
|
||||||
txInIndex: input, script: scriptPubKeys, amount: utxo.value);
|
txInIndex: input, script: scriptPubKeys, amount: utxo.value);
|
||||||
}
|
}
|
||||||
return transaction.getTransactionDigest(txInIndex: input, script: scriptPubKeys);
|
return transaction.getTransactionDigest(
|
||||||
|
txInIndex: input, script: scriptPubKeys);
|
||||||
}
|
}
|
||||||
|
|
||||||
Script _findLockingScript(UtxoWithAddress utxo, bool isTaproot) {
|
Script _findLockingScript(UtxoWithAddress utxo, bool isTaproot) {
|
||||||
|
@ -119,23 +129,23 @@ extension PsbtSigner on PsbtV2 {
|
||||||
switch (utxo.utxo.scriptType) {
|
switch (utxo.utxo.scriptType) {
|
||||||
case PubKeyAddressType.p2pk:
|
case PubKeyAddressType.p2pk:
|
||||||
return senderPub.toRedeemScript();
|
return senderPub.toRedeemScript();
|
||||||
case SegwitAddressType.p2wsh:
|
case SegwitAddresType.p2wsh:
|
||||||
if (isTaproot) {
|
if (isTaproot) {
|
||||||
return senderPub.toP2wshAddress().toScriptPubKey();
|
return senderPub.toP2wshAddress().toScriptPubKey();
|
||||||
}
|
}
|
||||||
return senderPub.toP2wshRedeemScript();
|
return senderPub.toP2wshRedeemScript();
|
||||||
case P2pkhAddressType.p2pkh:
|
case P2pkhAddressType.p2pkh:
|
||||||
return senderPub.toP2pkhAddress().toScriptPubKey();
|
return senderPub.toP2pkhAddress().toScriptPubKey();
|
||||||
case SegwitAddressType.p2wpkh:
|
case SegwitAddresType.p2wpkh:
|
||||||
if (isTaproot) {
|
if (isTaproot) {
|
||||||
return senderPub.toP2wpkhAddress().toScriptPubKey();
|
return senderPub.toP2wpkhAddress().toScriptPubKey();
|
||||||
}
|
}
|
||||||
return senderPub.toP2pkhAddress().toScriptPubKey();
|
return senderPub.toP2pkhAddress().toScriptPubKey();
|
||||||
case SegwitAddressType.p2tr:
|
case SegwitAddresType.p2tr:
|
||||||
return senderPub
|
return senderPub
|
||||||
.toTaprootAddress(tweak: utxo.utxo.isSilentPayment != true)
|
.toTaprootAddress(tweak: utxo.utxo.isSilentPayment != true)
|
||||||
.toScriptPubKey();
|
.toScriptPubKey();
|
||||||
case SegwitAddressType.mweb:
|
case SegwitAddresType.mweb:
|
||||||
return Script(script: []);
|
return Script(script: []);
|
||||||
case P2shAddressType.p2pkhInP2sh:
|
case P2shAddressType.p2pkhInP2sh:
|
||||||
if (isTaproot) {
|
if (isTaproot) {
|
||||||
|
@ -162,10 +172,11 @@ extension PsbtSigner on PsbtV2 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef UTXOSignerCallBack = String Function(
|
typedef UTXOSignerCallBack = String Function(List<int> trDigest,
|
||||||
List<int> trDigest, UtxoWithAddress utxo, ECPrivate privateKey, int sighash);
|
UtxoWithAddress utxo, ECPrivate privateKey, int sighash);
|
||||||
|
|
||||||
typedef UTXOGetterCallBack = Future<TaprootAmountScriptPair> Function(String txId, int vout);
|
typedef UTXOGetterCallBack = Future<TaprootAmountScriptPair> Function(
|
||||||
|
String txId, int vout);
|
||||||
|
|
||||||
class TaprootAmountScriptPair {
|
class TaprootAmountScriptPair {
|
||||||
final BigInt value;
|
final BigInt value;
|
||||||
|
@ -205,17 +216,23 @@ class UtxoWithPrivateKey extends UtxoWithAddress {
|
||||||
}
|
}
|
||||||
|
|
||||||
return UtxoWithPrivateKey(
|
return UtxoWithPrivateKey(
|
||||||
utxo: input.utxo, ownerDetails: input.ownerDetails, privateKey: key.privkey);
|
utxo: input.utxo,
|
||||||
|
ownerDetails: input.ownerDetails,
|
||||||
|
privateKey: key.privkey);
|
||||||
}
|
}
|
||||||
|
|
||||||
factory UtxoWithPrivateKey.fromUnspent(BitcoinUnspent input, BitcoinWalletBase wallet) {
|
factory UtxoWithPrivateKey.fromUnspent(
|
||||||
final address = RegexUtils.addressTypeFromStr(input.address, BitcoinNetwork.mainnet);
|
BitcoinUnspent input, BitcoinWalletBase wallet) {
|
||||||
|
final address =
|
||||||
|
RegexUtils.addressTypeFromStr(input.address, BitcoinNetwork.mainnet);
|
||||||
|
|
||||||
final newHd = input.bitcoinAddressRecord.isHidden ? wallet.sideHd : wallet.hd;
|
final newHd =
|
||||||
|
input.bitcoinAddressRecord.isHidden ? wallet.sideHd : wallet.hd;
|
||||||
|
|
||||||
ECPrivate privkey;
|
ECPrivate privkey;
|
||||||
if (input.bitcoinAddressRecord is BitcoinSilentPaymentAddressRecord) {
|
if (input.bitcoinAddressRecord is BitcoinSilentPaymentAddressRecord) {
|
||||||
final unspentAddress = input.bitcoinAddressRecord as BitcoinSilentPaymentAddressRecord;
|
final unspentAddress =
|
||||||
|
input.bitcoinAddressRecord as BitcoinSilentPaymentAddressRecord;
|
||||||
privkey = wallet.walletAddresses.silentAddress!.b_spend.tweakAdd(
|
privkey = wallet.walletAddresses.silentAddress!.b_spend.tweakAdd(
|
||||||
BigintUtils.fromBytes(
|
BigintUtils.fromBytes(
|
||||||
BytesUtils.fromHexString(unspentAddress.silentPaymentTweak!),
|
BytesUtils.fromHexString(unspentAddress.silentPaymentTweak!),
|
||||||
|
@ -223,7 +240,9 @@ class UtxoWithPrivateKey extends UtxoWithAddress {
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
privkey = generateECPrivate(
|
privkey = generateECPrivate(
|
||||||
hd: newHd, index: input.bitcoinAddressRecord.index, network: BitcoinNetwork.mainnet);
|
hd: newHd,
|
||||||
|
index: input.bitcoinAddressRecord.index,
|
||||||
|
network: BitcoinNetwork.mainnet);
|
||||||
}
|
}
|
||||||
|
|
||||||
return UtxoWithPrivateKey(
|
return UtxoWithPrivateKey(
|
||||||
|
@ -232,7 +251,8 @@ class UtxoWithPrivateKey extends UtxoWithAddress {
|
||||||
value: BigInt.from(input.value),
|
value: BigInt.from(input.value),
|
||||||
vout: input.vout,
|
vout: input.vout,
|
||||||
scriptType: input.bitcoinAddressRecord.type,
|
scriptType: input.bitcoinAddressRecord.type,
|
||||||
isSilentPayment: input.bitcoinAddressRecord is BitcoinSilentPaymentAddressRecord,
|
isSilentPayment:
|
||||||
|
input.bitcoinAddressRecord is BitcoinSilentPaymentAddressRecord,
|
||||||
),
|
),
|
||||||
ownerDetails: UtxoAddressDetails(
|
ownerDetails: UtxoAddressDetails(
|
||||||
publicKey: privkey.getPublic().toHex(),
|
publicKey: privkey.getPublic().toHex(),
|
||||||
|
|
|
@ -9,9 +9,7 @@ class PSBTTransactionBuild {
|
||||||
final PsbtV2 psbt = PsbtV2();
|
final PsbtV2 psbt = PsbtV2();
|
||||||
|
|
||||||
PSBTTransactionBuild(
|
PSBTTransactionBuild(
|
||||||
{required List<PSBTReadyUtxoWithAddress> inputs,
|
{required List<PSBTReadyUtxoWithAddress> inputs, required List<BitcoinBaseOutput> outputs, bool enableRBF = true}) {
|
||||||
required List<BitcoinBaseOutput> outputs,
|
|
||||||
bool enableRBF = true}) {
|
|
||||||
psbt.setGlobalTxVersion(2);
|
psbt.setGlobalTxVersion(2);
|
||||||
psbt.setGlobalInputCount(inputs.length);
|
psbt.setGlobalInputCount(inputs.length);
|
||||||
psbt.setGlobalOutputCount(outputs.length);
|
psbt.setGlobalOutputCount(outputs.length);
|
||||||
|
@ -19,20 +17,20 @@ class PSBTTransactionBuild {
|
||||||
for (var i = 0; i < inputs.length; i++) {
|
for (var i = 0; i < inputs.length; i++) {
|
||||||
final input = inputs[i];
|
final input = inputs[i];
|
||||||
|
|
||||||
printV(input.utxo.isP2tr);
|
printV(input.utxo.isP2tr());
|
||||||
printV(input.utxo.isSegwit);
|
printV(input.utxo.isSegwit());
|
||||||
printV(input.utxo.isP2shSegwit);
|
printV(input.utxo.isP2shSegwit());
|
||||||
|
|
||||||
psbt.setInputPreviousTxId(
|
psbt.setInputPreviousTxId(i, Uint8List.fromList(hex.decode(input.utxo.txHash).reversed.toList()));
|
||||||
i, Uint8List.fromList(hex.decode(input.utxo.txHash).reversed.toList()));
|
|
||||||
psbt.setInputOutputIndex(i, input.utxo.vout);
|
psbt.setInputOutputIndex(i, input.utxo.vout);
|
||||||
psbt.setInputSequence(i, enableRBF ? 0x1 : 0xffffffff);
|
psbt.setInputSequence(i, enableRBF ? 0x1 : 0xffffffff);
|
||||||
|
|
||||||
if (input.utxo.isSegwit) {
|
|
||||||
|
if (input.utxo.isSegwit()) {
|
||||||
setInputSegwit(i, input);
|
setInputSegwit(i, input);
|
||||||
} else if (input.utxo.isP2shSegwit) {
|
} else if (input.utxo.isP2shSegwit()) {
|
||||||
setInputP2shSegwit(i, input);
|
setInputP2shSegwit(i, input);
|
||||||
} else if (input.utxo.isP2tr) {
|
} else if (input.utxo.isP2tr()) {
|
||||||
// ToDo: (Konsti) Handle Taproot Inputs
|
// ToDo: (Konsti) Handle Taproot Inputs
|
||||||
} else {
|
} else {
|
||||||
setInputP2pkh(i, input);
|
setInputP2pkh(i, input);
|
||||||
|
@ -51,14 +49,20 @@ class PSBTTransactionBuild {
|
||||||
|
|
||||||
void setInputP2pkh(int i, PSBTReadyUtxoWithAddress input) {
|
void setInputP2pkh(int i, PSBTReadyUtxoWithAddress input) {
|
||||||
psbt.setInputNonWitnessUtxo(i, Uint8List.fromList(hex.decode(input.rawTx)));
|
psbt.setInputNonWitnessUtxo(i, Uint8List.fromList(hex.decode(input.rawTx)));
|
||||||
psbt.setInputBip32Derivation(i, Uint8List.fromList(hex.decode(input.ownerPublicKey)),
|
psbt.setInputBip32Derivation(
|
||||||
input.ownerMasterFingerprint, BIPPath.fromString(input.ownerDerivationPath).toPathArray());
|
i,
|
||||||
|
Uint8List.fromList(hex.decode(input.ownerPublicKey)),
|
||||||
|
input.ownerMasterFingerprint,
|
||||||
|
BIPPath.fromString(input.ownerDerivationPath).toPathArray());
|
||||||
}
|
}
|
||||||
|
|
||||||
void setInputSegwit(int i, PSBTReadyUtxoWithAddress input) {
|
void setInputSegwit(int i, PSBTReadyUtxoWithAddress input) {
|
||||||
psbt.setInputNonWitnessUtxo(i, Uint8List.fromList(hex.decode(input.rawTx)));
|
psbt.setInputNonWitnessUtxo(i, Uint8List.fromList(hex.decode(input.rawTx)));
|
||||||
psbt.setInputBip32Derivation(i, Uint8List.fromList(hex.decode(input.ownerPublicKey)),
|
psbt.setInputBip32Derivation(
|
||||||
input.ownerMasterFingerprint, BIPPath.fromString(input.ownerDerivationPath).toPathArray());
|
i,
|
||||||
|
Uint8List.fromList(hex.decode(input.ownerPublicKey)),
|
||||||
|
input.ownerMasterFingerprint,
|
||||||
|
BIPPath.fromString(input.ownerDerivationPath).toPathArray());
|
||||||
|
|
||||||
psbt.setInputWitnessUtxo(i, Uint8List.fromList(bigIntToUint64LE(input.utxo.value)),
|
psbt.setInputWitnessUtxo(i, Uint8List.fromList(bigIntToUint64LE(input.utxo.value)),
|
||||||
Uint8List.fromList(input.ownerDetails.address.toScriptPubKey().toBytes()));
|
Uint8List.fromList(input.ownerDetails.address.toScriptPubKey().toBytes()));
|
||||||
|
|
|
@ -21,7 +21,7 @@ String getOutputAmountFromPsbt(String psbtV0, BitcoinWalletBase wallet) {
|
||||||
int amount = 0;
|
int amount = 0;
|
||||||
for (var i = 0; i < psbt.getGlobalOutputCount(); i++) {
|
for (var i = 0; i < psbt.getGlobalOutputCount(); i++) {
|
||||||
final script = psbt.getOutputScript(i);
|
final script = psbt.getOutputScript(i);
|
||||||
if (wallet.isMine(Script.fromRaw(bytes: script))) {
|
if (wallet.isMine(Script.fromRaw(byteData: script))) {
|
||||||
amount += psbt.getOutputAmount(i);
|
amount += psbt.getOutputAmount(i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,34 +5,39 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: _fe_analyzer_shared
|
name: _fe_analyzer_shared
|
||||||
sha256: "4897882604d919befd350648c7f91926a9d5de99e67b455bf0917cc2362f4bb8"
|
sha256: "16e298750b6d0af7ce8a3ba7c18c69c3785d11b15ec83f6dcd0ad2a0009b3cab"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "47.0.0"
|
version: "76.0.0"
|
||||||
|
_macros:
|
||||||
|
dependency: transitive
|
||||||
|
description: dart
|
||||||
|
source: sdk
|
||||||
|
version: "0.3.3"
|
||||||
analyzer:
|
analyzer:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: analyzer
|
name: analyzer
|
||||||
sha256: "690e335554a8385bc9d787117d9eb52c0c03ee207a607e593de3c9d71b1cfe80"
|
sha256: "1f14db053a8c23e260789e9b0980fa27f2680dd640932cae5e1137cce0e46e1e"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.7.0"
|
version: "6.11.0"
|
||||||
args:
|
args:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: args
|
name: args
|
||||||
sha256: bf9f5caeea8d8fe6721a9c358dd8a5c1947b27f1cfaa18b39c301273594919e6
|
sha256: d0481093c50b1da8910eb0bb301626d4d8eb7284aa739614d2b394ee09e3ea04
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.6.0"
|
version: "2.7.0"
|
||||||
asn1lib:
|
asn1lib:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: asn1lib
|
name: asn1lib
|
||||||
sha256: "4bae5ae63e6d6dd17c4aac8086f3dec26c0236f6a0f03416c6c19d830c367cf5"
|
sha256: "1c296cd268f486cabcc3930e9b93a8133169305f18d722916e675959a88f6d2c"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.5.8"
|
version: "1.5.9"
|
||||||
async:
|
async:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -79,20 +84,20 @@ packages:
|
||||||
dependency: "direct overridden"
|
dependency: "direct overridden"
|
||||||
description:
|
description:
|
||||||
path: "."
|
path: "."
|
||||||
ref: cake-update-v15
|
ref: cake-update-v9
|
||||||
resolved-ref: "29160733cbc4ef2c7b8c8fe9ed0297c9bffecfe2"
|
resolved-ref: bb4318511312a454fd91bf49042e25ecc855e4ac
|
||||||
url: "https://github.com/cake-tech/bitcoin_base"
|
url: "https://github.com/cake-tech/bitcoin_base"
|
||||||
source: git
|
source: git
|
||||||
version: "6.1.0"
|
version: "4.7.0"
|
||||||
blockchain_utils:
|
blockchain_utils:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
path: "."
|
path: "."
|
||||||
ref: cake-update-v4
|
ref: cake-update-v2
|
||||||
resolved-ref: "437dadd0bd9bf73ec6a551299577799341f6486a"
|
resolved-ref: "59fdf29d72068e0522a96a8953ed7272833a9f57"
|
||||||
url: "https://github.com/cake-tech/blockchain_utils"
|
url: "https://github.com/cake-tech/blockchain_utils"
|
||||||
source: git
|
source: git
|
||||||
version: "4.3.0"
|
version: "3.3.0"
|
||||||
bluez:
|
bluez:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -121,10 +126,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: build
|
name: build
|
||||||
sha256: "80184af8b6cb3e5c1c4ec6d8544d27711700bc3e6d2efad04238c7b5290889f0"
|
sha256: cef23f1eda9b57566c81e2133d196f8e3df48f244b317368d65c5943d91148f0
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.4.1"
|
version: "2.4.2"
|
||||||
build_cli_annotations:
|
build_cli_annotations:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -137,42 +142,42 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: build_config
|
name: build_config
|
||||||
sha256: bf80fcfb46a29945b423bd9aad884590fb1dc69b330a4d4700cac476af1708d1
|
sha256: "4ae2de3e1e67ea270081eaee972e1bd8f027d459f249e0f1186730784c2e7e33"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.1.1"
|
version: "1.1.2"
|
||||||
build_daemon:
|
build_daemon:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: build_daemon
|
name: build_daemon
|
||||||
sha256: "79b2aef6ac2ed00046867ed354c88778c9c0f029df8a20fe10b5436826721ef9"
|
sha256: "8e928697a82be082206edb0b9c99c5a4ad6bc31c9e9b8b2f291ae65cd4a25daa"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.0.2"
|
version: "4.0.4"
|
||||||
build_resolvers:
|
build_resolvers:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description:
|
description:
|
||||||
name: build_resolvers
|
name: build_resolvers
|
||||||
sha256: "687cf90a3951affac1bd5f9ecb5e3e90b60487f3d9cdc359bb310f8876bb02a6"
|
sha256: b9e4fda21d846e192628e7a4f6deda6888c36b5b69ba02ff291a01fd529140f0
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.10"
|
version: "2.4.4"
|
||||||
build_runner:
|
build_runner:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description:
|
description:
|
||||||
name: build_runner
|
name: build_runner
|
||||||
sha256: "028819cfb90051c6b5440c7e574d1896f8037e3c96cf17aaeb054c9311cfbf4d"
|
sha256: "058fe9dce1de7d69c4b84fada934df3e0153dd000758c4d65964d0166779aa99"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.4.13"
|
version: "2.4.15"
|
||||||
build_runner_core:
|
build_runner_core:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: build_runner_core
|
name: build_runner_core
|
||||||
sha256: "6d6ee4276b1c5f34f21fdf39425202712d2be82019983d52f351c94aafbc2c41"
|
sha256: "22e3aa1c80e0ada3722fe5b63fd43d9c8990759d0a2cf489c8c5d7b2bdebc021"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "7.2.10"
|
version: "8.0.0"
|
||||||
built_collection:
|
built_collection:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -185,10 +190,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: built_value
|
name: built_value
|
||||||
sha256: "8b158ab94ec6913e480dc3f752418348b5ae099eb75868b5f4775f0572999c61"
|
sha256: "082001b5c3dc495d4a42f1d5789990505df20d8547d42507c29050af6933ee27"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "8.9.4"
|
version: "8.10.1"
|
||||||
cake_backup:
|
cake_backup:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -296,10 +301,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: dart_style
|
name: dart_style
|
||||||
sha256: "7a03456c3490394c8e7665890333e91ae8a49be43542b616e414449ac358acd4"
|
sha256: "7306ab8a2359a48d22310ad823521d723acfed60ee1f7e37388e8986853b6820"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.2.4"
|
version: "2.3.8"
|
||||||
dart_varuint_bitcoin:
|
dart_varuint_bitcoin:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -389,10 +394,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: flutter_rust_bridge
|
name: flutter_rust_bridge
|
||||||
sha256: "3292ad6085552987b8b3b9a7e5805567f4013372d302736b702801acb001ee00"
|
sha256: "5a5c7a5deeef2cc2ffe6076a33b0429f4a20ceac22a397297aed2b1eb067e611"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.7.1"
|
version: "2.9.0"
|
||||||
flutter_test:
|
flutter_test:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description: flutter
|
description: flutter
|
||||||
|
@ -402,10 +407,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: flutter_web_bluetooth
|
name: flutter_web_bluetooth
|
||||||
sha256: "1363831def5eed1e1064d1eca04e8ccb35446e8f758579c3c519e156b77926da"
|
sha256: ad26a1b3fef95b86ea5f63793b9a0cdc1a33490f35d754e4e711046cae3ebbf8
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.0"
|
version: "1.1.0"
|
||||||
flutter_web_plugins:
|
flutter_web_plugins:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description: flutter
|
description: flutter
|
||||||
|
@ -415,10 +420,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: freezed_annotation
|
name: freezed_annotation
|
||||||
sha256: c2e2d632dd9b8a2b7751117abcfc2b4888ecfe181bd9fca7170d9ef02e595fe2
|
sha256: c87ff004c8aa6af2d531668b46a4ea379f7191dc6dfa066acd53d506da6e044b
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.4.4"
|
version: "3.0.0"
|
||||||
frontend_server_client:
|
frontend_server_client:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -439,18 +444,18 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: google_identity_services_web
|
name: google_identity_services_web
|
||||||
sha256: "55580f436822d64c8ff9a77e37d61f5fb1e6c7ec9d632a43ee324e2a05c3c6c9"
|
sha256: "5d187c46dc59e02646e10fe82665fc3884a9b71bc1c90c2b8b749316d33ee454"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.3.3"
|
version: "0.3.3+1"
|
||||||
googleapis_auth:
|
googleapis_auth:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: googleapis_auth
|
name: googleapis_auth
|
||||||
sha256: befd71383a955535060acde8792e7efc11d2fccd03dd1d3ec434e85b68775938
|
sha256: b81fe352cc4a330b3710d2b7ad258d9bcef6f909bb759b306bf42973a7d046db
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.6.0"
|
version: "2.0.0"
|
||||||
graphs:
|
graphs:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -463,10 +468,10 @@ packages:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: grpc
|
name: grpc
|
||||||
sha256: "5b99b7a420937d4361ece68b798c9af8e04b5bc128a7859f2a4be87427694813"
|
sha256: "30e1edae6846b163a64f6d8716e3443980fe1f7d2d1f086f011d24ea186f2582"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.0.1"
|
version: "4.0.4"
|
||||||
hex:
|
hex:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -487,18 +492,18 @@ packages:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description:
|
description:
|
||||||
name: hive_generator
|
name: hive_generator
|
||||||
sha256: "81fd20125cb2ce8fd23623d7744ffbaf653aae93706c9bd3bf7019ea0ace3938"
|
sha256: "06cb8f58ace74de61f63500564931f9505368f45f98958bd7a6c35ba24159db4"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.1.3"
|
version: "2.0.1"
|
||||||
http:
|
http:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: http
|
name: http
|
||||||
sha256: fe7ab022b76f3034adc518fb6ea04a82387620e19977665ea18d30a1cf43442f
|
sha256: "2c11f3f94c687ee9bad77c171151672986360b2b001d109814ee7140b2cf261b"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.3.0"
|
version: "1.4.0"
|
||||||
http2:
|
http2:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -519,10 +524,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: http_parser
|
name: http_parser
|
||||||
sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b"
|
sha256: "178d74305e7866013777bab2c3d8726205dc5a4dd935297175b19a23a2e66571"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.0.2"
|
version: "4.1.2"
|
||||||
intl:
|
intl:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
@ -592,10 +597,10 @@ packages:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: ledger_flutter_plus
|
name: ledger_flutter_plus
|
||||||
sha256: "1c03f3c4a9754b5f0170a9eb9552ec54fa86e985f8ee71a255ee2c5629b53d31"
|
sha256: "531da5daba5731d9eca2732881ef2f039b97bf8aa3564e7098dfa99a9b07a8e6"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.5.1"
|
version: "1.5.3"
|
||||||
ledger_litecoin:
|
ledger_litecoin:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
@ -621,6 +626,14 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.3.0"
|
version: "1.3.0"
|
||||||
|
macros:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: macros
|
||||||
|
sha256: "1d9e801cd66f7ea3663c45fc708450db1fa57f988142c64289142c9b7ee80656"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.1.3-main.0"
|
||||||
matcher:
|
matcher:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -665,10 +678,10 @@ packages:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description:
|
description:
|
||||||
name: mobx_codegen
|
name: mobx_codegen
|
||||||
sha256: d4beb9cea4b7b014321235f8fdc7c2193ee0fe1d1198e9da7403f8bc85c4407c
|
sha256: e0abbbc651a69550440f6b65c99ec222a1e2a4afd7baec8ba0f3088c7ca582a8
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.3.0"
|
version: "2.7.1"
|
||||||
nested:
|
nested:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -681,19 +694,19 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
path: "."
|
path: "."
|
||||||
ref: cake-update-v4
|
ref: cake-update-v2
|
||||||
resolved-ref: "084fb7bf13ec42d74f26ac08c883ce07c10fca7e"
|
resolved-ref: "93440dc5126369b873ca1fccc13c3c1240b1c5c2"
|
||||||
url: "https://github.com/cake-tech/on_chain.git"
|
url: "https://github.com/cake-tech/on_chain.git"
|
||||||
source: git
|
source: git
|
||||||
version: "6.2.0"
|
version: "3.7.0"
|
||||||
package_config:
|
package_config:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: package_config
|
name: package_config
|
||||||
sha256: "92d4488434b520a62570293fbd33bb556c7d49230791c1b4bbd973baf6d2dc67"
|
sha256: f096c55ebb7deb7e384101542bfba8c52696c1b56fca2eb62827989ef2353bbc
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.1"
|
version: "2.2.0"
|
||||||
path:
|
path:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -714,10 +727,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: path_provider_android
|
name: path_provider_android
|
||||||
sha256: "4adf4fd5423ec60a29506c76581bc05854c55e3a0b72d35bb28d661c9686edf2"
|
sha256: d0d310befe2c8ab9e7f393288ccbb11b60c019c6b5afc21973eeee4dda2b35e9
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.2.15"
|
version: "2.2.17"
|
||||||
path_provider_foundation:
|
path_provider_foundation:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -754,11 +767,11 @@ packages:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
path: "."
|
path: "."
|
||||||
ref: "6a3eb32fb9467ac12e7b75d3de47de4ca44fd88c"
|
ref: da83a23f3a011cb49eb3b6513cd485b3fb8867ff
|
||||||
resolved-ref: "6a3eb32fb9467ac12e7b75d3de47de4ca44fd88c"
|
resolved-ref: da83a23f3a011cb49eb3b6513cd485b3fb8867ff
|
||||||
url: "https://github.com/konstantinullrich/payjoin-flutter"
|
url: "https://github.com/OmarHatem28/payjoin-flutter"
|
||||||
source: git
|
source: git
|
||||||
version: "0.21.0"
|
version: "0.23.0"
|
||||||
petitparser:
|
petitparser:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -811,26 +824,26 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: provider
|
name: provider
|
||||||
sha256: c8a055ee5ce3fd98d6fc872478b03823ffdb448699c6ebdbbc71d59b596fd48c
|
sha256: "4abbd070a04e9ddc287673bf5a030c7ca8b685ff70218720abab8b092f53dd84"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "6.1.2"
|
version: "6.1.5"
|
||||||
pub_semver:
|
pub_semver:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: pub_semver
|
name: pub_semver
|
||||||
sha256: "7b3cfbf654f3edd0c6298ecd5be782ce997ddf0e00531b9464b55245185bbbbd"
|
sha256: "5bfcf68ca79ef689f8990d1160781b4bad40a3bd5e5218ad4076ddb7f4081585"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.5"
|
version: "2.2.0"
|
||||||
pubspec_parse:
|
pubspec_parse:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: pubspec_parse
|
name: pubspec_parse
|
||||||
sha256: "81876843eb50dc2e1e5b151792c9a985c5ed2536914115ed04e9c8528f6647b0"
|
sha256: "0560ba233314abbed0a48a2956f7f022cce7c3e1e73df540277da7544cad4082"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.4.0"
|
version: "1.5.0"
|
||||||
quiver:
|
quiver:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -859,18 +872,18 @@ packages:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: shared_preferences
|
name: shared_preferences
|
||||||
sha256: "846849e3e9b68f3ef4b60c60cf4b3e02e9321bc7f4d8c4692cf87ffa82fc8a3a"
|
sha256: "6e8bf70b7fef813df4e9a36f658ac46d107db4b4cfe1048b477d4e453a8159f5"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.5.2"
|
version: "2.5.3"
|
||||||
shared_preferences_android:
|
shared_preferences_android:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: shared_preferences_android
|
name: shared_preferences_android
|
||||||
sha256: a768fc8ede5f0c8e6150476e14f38e2417c0864ca36bb4582be8e21925a03c22
|
sha256: "20cbd561f743a342c76c151d6ddb93a9ce6005751e7aa458baad3858bfbfb6ac"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.4.6"
|
version: "2.4.10"
|
||||||
shared_preferences_foundation:
|
shared_preferences_foundation:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -915,18 +928,18 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: shelf
|
name: shelf
|
||||||
sha256: ad29c505aee705f41a4d8963641f91ac4cee3c8fad5947e033390a7bd8180fa4
|
sha256: e7dd780a7ffb623c57850b33f43309312fc863fb6aa3d276a754bb299839ef12
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.4.1"
|
version: "1.4.2"
|
||||||
shelf_web_socket:
|
shelf_web_socket:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: shelf_web_socket
|
name: shelf_web_socket
|
||||||
sha256: cc36c297b52866d203dbf9332263c94becc2fe0ceaa9681d07b6ef9807023b67
|
sha256: "3632775c8e90d6c9712f883e633716432a27758216dfb61bd86a8321c0580925"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.1"
|
version: "3.0.0"
|
||||||
sky_engine:
|
sky_engine:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description: flutter
|
description: flutter
|
||||||
|
@ -935,27 +948,37 @@ packages:
|
||||||
socks5_proxy:
|
socks5_proxy:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: socks5_proxy
|
path: "."
|
||||||
sha256: "616818a0ea1064a4823b53c9f7eaf8da64ed82dcd51ed71371c7e54751ed5053"
|
ref: "27ad7c2efae8d7460325c74b90f660085cbd0685"
|
||||||
url: "https://pub.dev"
|
resolved-ref: "27ad7c2efae8d7460325c74b90f660085cbd0685"
|
||||||
source: hosted
|
url: "https://github.com/LacticWhale/socks_dart"
|
||||||
version: "1.0.6"
|
source: git
|
||||||
|
version: "2.1.0"
|
||||||
|
socks_socket:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
path: "."
|
||||||
|
ref: e6232c53c1595469931ababa878759a067c02e94
|
||||||
|
resolved-ref: e6232c53c1595469931ababa878759a067c02e94
|
||||||
|
url: "https://github.com/sneurlax/socks_socket"
|
||||||
|
source: git
|
||||||
|
version: "1.1.1"
|
||||||
source_gen:
|
source_gen:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: source_gen
|
name: source_gen
|
||||||
sha256: "2d79738b6bbf38a43920e2b8d189e9a3ce6cc201f4b8fc76be5e4fe377b1c38d"
|
sha256: "14658ba5f669685cd3d63701d01b31ea748310f7ab854e471962670abcf57832"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.2.6"
|
version: "1.5.0"
|
||||||
source_helper:
|
source_helper:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: source_helper
|
name: source_helper
|
||||||
sha256: "3b67aade1d52416149c633ba1bb36df44d97c6b51830c2198e934e3fca87ca1f"
|
sha256: "86d247119aedce8e63f4751bd9626fc9613255935558447569ad42f9f5b48b3c"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.3.3"
|
version: "1.3.5"
|
||||||
source_span:
|
source_span:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -968,19 +991,11 @@ packages:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
path: "."
|
path: "."
|
||||||
ref: cake-update-v4
|
ref: "sp_v4.0.0"
|
||||||
resolved-ref: f3c172a7dc5155f5e745e4630b05f197e098a5cd
|
resolved-ref: "2554cb8bd3ee1d026bc63e76a30d1226960c7cb4"
|
||||||
url: "https://github.com/cake-tech/sp_scanner"
|
url: "https://github.com/cake-tech/sp_scanner"
|
||||||
source: git
|
source: git
|
||||||
version: "0.0.1"
|
version: "0.0.1"
|
||||||
sprintf:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: sprintf
|
|
||||||
sha256: "1fc9ffe69d4df602376b52949af107d8f5703b77cda567c4d7d86a0693120f23"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "7.0.0"
|
|
||||||
stack_trace:
|
stack_trace:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -1033,10 +1048,19 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: timing
|
name: timing
|
||||||
sha256: "62ee18aca144e4a9f29d212f5a4c6a053be252b895ab14b5821996cff4ed90fe"
|
sha256: "70a3b636575d4163c477e6de42f247a23b315ae20e86442bebe32d3cabf61c32"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.2"
|
version: "1.0.1"
|
||||||
|
tor_binary:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
path: "."
|
||||||
|
ref: cb811c610871a9517d47134b87c2f590c15c96c5
|
||||||
|
resolved-ref: cb811c610871a9517d47134b87c2f590c15c96c5
|
||||||
|
url: "https://github.com/MrCyjaneK/flutter-tor_binary"
|
||||||
|
source: git
|
||||||
|
version: "4.7.14"
|
||||||
tuple:
|
tuple:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -1057,10 +1081,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: universal_ble
|
name: universal_ble
|
||||||
sha256: "1fad089150a29db82b3b7d60327e18c5ad6b3a5bb509defc1c690b0a76b9c098"
|
sha256: "35d210e93a5938c6a6d1fd3c710cf4ac90b1bdd1b11c8eb2beeb32600672e6e6"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.15.0"
|
version: "0.17.0"
|
||||||
universal_platform:
|
universal_platform:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -1077,14 +1101,6 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.3.0"
|
version: "0.3.0"
|
||||||
uuid:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: uuid
|
|
||||||
sha256: a5be9ef6618a7ac1e964353ef476418026db906c4facdedaa299b7a2e71690ff
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "4.5.1"
|
|
||||||
vector_math:
|
vector_math:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -1121,18 +1137,18 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: web_socket
|
name: web_socket
|
||||||
sha256: "3c12d96c0c9a4eec095246debcea7b86c0324f22df69893d538fcc6f1b8cce83"
|
sha256: "34d64019aa8e36bf9842ac014bb5d2f5586ca73df5e4d9bf5c936975cae6982c"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.1.6"
|
version: "1.0.1"
|
||||||
web_socket_channel:
|
web_socket_channel:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: web_socket_channel
|
name: web_socket_channel
|
||||||
sha256: "0b8e2457400d8a859b7b2030786835a28a8e80836ef64402abef392ff4f1d0e5"
|
sha256: d645757fb0f4773d602444000a8131ff5d48c9e47adfe9772652dd1a4f2d45c8
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.0.2"
|
version: "3.0.3"
|
||||||
xdg_directories:
|
xdg_directories:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -1166,5 +1182,5 @@ packages:
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.2.2"
|
version: "2.2.2"
|
||||||
sdks:
|
sdks:
|
||||||
dart: ">=3.5.0 <4.0.0"
|
dart: ">=3.6.0 <4.0.0"
|
||||||
flutter: ">=3.24.0"
|
flutter: ">=3.27.0"
|
||||||
|
|
|
@ -29,21 +29,21 @@ dependencies:
|
||||||
blockchain_utils:
|
blockchain_utils:
|
||||||
git:
|
git:
|
||||||
url: https://github.com/cake-tech/blockchain_utils
|
url: https://github.com/cake-tech/blockchain_utils
|
||||||
ref: cake-update-v4
|
ref: cake-update-v2
|
||||||
cw_mweb:
|
cw_mweb:
|
||||||
path: ../cw_mweb
|
path: ../cw_mweb
|
||||||
grpc: ^4.0.1
|
grpc: ^4.0.1
|
||||||
sp_scanner:
|
sp_scanner:
|
||||||
git:
|
git:
|
||||||
url: https://github.com/cake-tech/sp_scanner
|
url: https://github.com/cake-tech/sp_scanner
|
||||||
ref: cake-update-v4
|
ref: sp_v4.0.0
|
||||||
bech32:
|
bech32:
|
||||||
git:
|
git:
|
||||||
url: https://github.com/cake-tech/bech32.git
|
url: https://github.com/cake-tech/bech32.git
|
||||||
payjoin_flutter:
|
payjoin_flutter:
|
||||||
git:
|
git:
|
||||||
url: https://github.com/konstantinullrich/payjoin-flutter
|
url: https://github.com/OmarHatem28/payjoin-flutter
|
||||||
ref: 6a3eb32fb9467ac12e7b75d3de47de4ca44fd88c #cake-v1
|
ref: da83a23f3a011cb49eb3b6513cd485b3fb8867ff #cake-v2
|
||||||
ledger_flutter_plus: ^1.4.1
|
ledger_flutter_plus: ^1.4.1
|
||||||
ledger_bitcoin:
|
ledger_bitcoin:
|
||||||
git:
|
git:
|
||||||
|
@ -54,14 +54,18 @@ dependencies:
|
||||||
git:
|
git:
|
||||||
url: https://github.com/cake-tech/ledger-flutter-plus-plugins
|
url: https://github.com/cake-tech/ledger-flutter-plus-plugins
|
||||||
path: packages/ledger-litecoin
|
path: packages/ledger-litecoin
|
||||||
|
socks_socket:
|
||||||
|
git:
|
||||||
|
url: https://github.com/sneurlax/socks_socket
|
||||||
|
ref: e6232c53c1595469931ababa878759a067c02e94
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
build_runner: ^2.4.7
|
build_runner: ^2.4.15
|
||||||
build_resolvers: ^2.0.9
|
build_resolvers: ^2.4.4
|
||||||
mobx_codegen: ^2.0.7
|
mobx_codegen: ^2.0.7
|
||||||
hive_generator: ^1.1.3
|
hive_generator: ^2.0.1
|
||||||
|
|
||||||
dependency_overrides:
|
dependency_overrides:
|
||||||
watcher: ^1.1.0
|
watcher: ^1.1.0
|
||||||
|
@ -69,7 +73,7 @@ dependency_overrides:
|
||||||
bitcoin_base:
|
bitcoin_base:
|
||||||
git:
|
git:
|
||||||
url: https://github.com/cake-tech/bitcoin_base
|
url: https://github.com/cake-tech/bitcoin_base
|
||||||
ref: cake-update-v15
|
ref: cake-update-v9
|
||||||
pointycastle: 3.7.4
|
pointycastle: 3.7.4
|
||||||
ffi: 2.1.0
|
ffi: 2.1.0
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@ homepage: https://cakewallet.com
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: '>=2.19.0 <3.0.0'
|
sdk: '>=2.19.0 <3.0.0'
|
||||||
flutter: ">=1.17.0"
|
flutter: ">=1.20.0"
|
||||||
|
|
||||||
dependencies:
|
dependencies:
|
||||||
flutter:
|
flutter:
|
||||||
|
@ -28,21 +28,21 @@ dependencies:
|
||||||
blockchain_utils:
|
blockchain_utils:
|
||||||
git:
|
git:
|
||||||
url: https://github.com/cake-tech/blockchain_utils
|
url: https://github.com/cake-tech/blockchain_utils
|
||||||
ref: cake-update-v4
|
ref: cake-update-v2
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
build_runner: ^2.4.7
|
build_runner: ^2.4.15
|
||||||
mobx_codegen: ^2.0.7
|
mobx_codegen: ^2.0.7
|
||||||
hive_generator: ^1.1.3
|
hive_generator: ^2.0.1
|
||||||
|
|
||||||
dependency_overrides:
|
dependency_overrides:
|
||||||
watcher: ^1.1.0
|
watcher: ^1.1.0
|
||||||
bitcoin_base:
|
bitcoin_base:
|
||||||
git:
|
git:
|
||||||
url: https://github.com/cake-tech/bitcoin_base
|
url: https://github.com/cake-tech/bitcoin_base
|
||||||
ref: cake-update-v15
|
ref: cake-update-v9
|
||||||
|
|
||||||
# For information on the generic Dart part of this file, see the
|
# For information on the generic Dart part of this file, see the
|
||||||
# following page: https://dart.dev/tools/pub/pubspec
|
# following page: https://dart.dev/tools/pub/pubspec
|
||||||
|
|
|
@ -111,7 +111,8 @@ class CryptoCurrency extends EnumerableItem<int> with Serializable<int> implemen
|
||||||
CryptoCurrency.zano,
|
CryptoCurrency.zano,
|
||||||
CryptoCurrency.ton,
|
CryptoCurrency.ton,
|
||||||
CryptoCurrency.flip,
|
CryptoCurrency.flip,
|
||||||
CryptoCurrency.deuro
|
CryptoCurrency.deuro,
|
||||||
|
CryptoCurrency.usdtbsc,
|
||||||
];
|
];
|
||||||
|
|
||||||
static const havenCurrencies = [
|
static const havenCurrencies = [
|
||||||
|
@ -233,6 +234,7 @@ class CryptoCurrency extends EnumerableItem<int> with Serializable<int> implemen
|
||||||
static const zano = CryptoCurrency(title: 'ZANO', tag: 'ZANO', fullName: 'Zano', raw: 96, name: 'zano', iconPath: 'assets/images/zano_icon.png', decimals: 12);
|
static const zano = CryptoCurrency(title: 'ZANO', tag: 'ZANO', fullName: 'Zano', raw: 96, name: 'zano', iconPath: 'assets/images/zano_icon.png', decimals: 12);
|
||||||
static const flip = CryptoCurrency(title: 'FLIP', tag: 'ETH', fullName: 'Chainflip', raw: 97, name: 'flip', iconPath: 'assets/images/flip_icon.png', decimals: 18);
|
static const flip = CryptoCurrency(title: 'FLIP', tag: 'ETH', fullName: 'Chainflip', raw: 97, name: 'flip', iconPath: 'assets/images/flip_icon.png', decimals: 18);
|
||||||
static const deuro = CryptoCurrency(title: 'DEURO', tag: 'ETH', fullName: 'Decentralized Euro', raw: 98, name: 'deuro', iconPath: 'assets/images/deuro_icon.png', decimals: 18);
|
static const deuro = CryptoCurrency(title: 'DEURO', tag: 'ETH', fullName: 'Decentralized Euro', raw: 98, name: 'deuro', iconPath: 'assets/images/deuro_icon.png', decimals: 18);
|
||||||
|
static const usdtbsc = CryptoCurrency(title: 'USDT', tag: 'BSC', fullName: 'USDT Binance coin', raw: 99, name: 'usdtbsc', iconPath: 'assets/images/usdtbsc_icon.png', decimals: 18);
|
||||||
|
|
||||||
static final Map<int, CryptoCurrency> _rawCurrencyMap =
|
static final Map<int, CryptoCurrency> _rawCurrencyMap =
|
||||||
[...all, ...havenCurrencies].fold<Map<int, CryptoCurrency>>(<int, CryptoCurrency>{}, (acc, item) {
|
[...all, ...havenCurrencies].fold<Map<int, CryptoCurrency>>(<int, CryptoCurrency>{}, (acc, item) {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
|
import 'package:cw_core/utils/proxy_wrapper.dart';
|
||||||
import 'package:cw_core/utils/print_verbose.dart';
|
import 'package:cw_core/utils/print_verbose.dart';
|
||||||
import 'package:intl/intl.dart';
|
import 'package:intl/intl.dart';
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
import 'package:http/http.dart' as http;
|
|
||||||
|
|
||||||
// FIXME: Hardcoded values; Works only for monero
|
// FIXME: Hardcoded values; Works only for monero
|
||||||
|
|
||||||
|
@ -234,10 +234,14 @@ int getHavenHeightByDate({required DateTime date}) {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<int> getHavenCurrentHeight() async {
|
Future<int> getHavenCurrentHeight() async {
|
||||||
final response = await http.get(Uri.parse('https://explorer.havenprotocol.org/api/networkinfo'));
|
final req = await ProxyWrapper().getHttpClient()
|
||||||
|
.getUrl(Uri.parse('https://explorer.havenprotocol.org/api/networkinfo'))
|
||||||
|
.timeout(Duration(seconds: 15));
|
||||||
|
final response = await req.close();
|
||||||
|
final stringResponse = await response.transform(utf8.decoder).join();
|
||||||
|
|
||||||
if (response.statusCode == 200) {
|
if (response.statusCode == 200) {
|
||||||
final info = jsonDecode(response.body);
|
final info = jsonDecode(stringResponse);
|
||||||
return info['data']['height'] as int;
|
return info['data']['height'] as int;
|
||||||
} else {
|
} else {
|
||||||
throw Exception('Failed to load current blockchain height');
|
throw Exception('Failed to load current blockchain height');
|
||||||
|
@ -269,13 +273,13 @@ const bitcoinDates = {
|
||||||
};
|
};
|
||||||
|
|
||||||
Future<int> getBitcoinHeightByDateAPI({required DateTime date}) async {
|
Future<int> getBitcoinHeightByDateAPI({required DateTime date}) async {
|
||||||
final response = await http.get(
|
final req = await ProxyWrapper().getHttpClient()
|
||||||
Uri.parse(
|
.getUrl(Uri.parse("https://mempool.cakewallet.com/api/v1/mining/blocks/timestamp/${(date.millisecondsSinceEpoch / 1000).round()}"))
|
||||||
"https://mempool.cakewallet.com/api/v1/mining/blocks/timestamp/${(date.millisecondsSinceEpoch / 1000).round()}",
|
.timeout(Duration(seconds: 15));
|
||||||
),
|
final response = await req.close();
|
||||||
);
|
final stringResponse = await response.transform(utf8.decoder).join();
|
||||||
|
|
||||||
return jsonDecode(response.body)['height'] as int;
|
return jsonDecode(stringResponse)['height'] as int;
|
||||||
}
|
}
|
||||||
|
|
||||||
int getBitcoinHeightByDate({required DateTime date}) {
|
int getBitcoinHeightByDate({required DateTime date}) {
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
import 'package:cw_core/keyable.dart';
|
import 'package:cw_core/keyable.dart';
|
||||||
|
import 'package:cw_core/utils/proxy_socket/abstract.dart';
|
||||||
|
import 'package:cw_core/utils/proxy_wrapper.dart';
|
||||||
import 'package:cw_core/utils/print_verbose.dart';
|
import 'package:cw_core/utils/print_verbose.dart';
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
import 'package:http/http.dart' as http;
|
|
||||||
import 'package:hive/hive.dart';
|
import 'package:hive/hive.dart';
|
||||||
import 'package:cw_core/hive_type_ids.dart';
|
import 'package:cw_core/hive_type_ids.dart';
|
||||||
import 'package:cw_core/wallet_type.dart';
|
import 'package:cw_core/wallet_type.dart';
|
||||||
import 'package:http/io_client.dart' as ioc;
|
|
||||||
import 'dart:math' as math;
|
import 'dart:math' as math;
|
||||||
import 'package:convert/convert.dart';
|
import 'package:convert/convert.dart';
|
||||||
|
|
||||||
|
@ -184,23 +184,17 @@ class Node extends HiveObject with Keyable {
|
||||||
final body = {'jsonrpc': '2.0', 'id': '0', 'method': "getinfo"};
|
final body = {'jsonrpc': '2.0', 'id': '0', 'method': "getinfo"};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final authenticatingClient = HttpClient();
|
|
||||||
authenticatingClient.badCertificateCallback =
|
|
||||||
((X509Certificate cert, String host, int port) => true);
|
|
||||||
|
|
||||||
final http.Client client = ioc.IOClient(authenticatingClient);
|
|
||||||
|
|
||||||
final jsonBody = json.encode(body);
|
final jsonBody = json.encode(body);
|
||||||
|
|
||||||
final response = await client.post(
|
final response = await ProxyWrapper().post(
|
||||||
rpcUri,
|
clearnetUri: rpcUri,
|
||||||
headers: {'Content-Type': 'application/json'},
|
headers: {'Content-Type': 'application/json'},
|
||||||
body: jsonBody,
|
body: jsonBody,
|
||||||
);
|
);
|
||||||
|
|
||||||
printV("node check response: ${response.body}");
|
|
||||||
|
|
||||||
final resBody = json.decode(response.body) as Map<String, dynamic>;
|
final resBody = json.decode(response.body) as Map<String, dynamic>;
|
||||||
|
|
||||||
return resBody['result']['height'] != null;
|
return resBody['result']['height'] != null;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
printV("error: $e");
|
printV("error: $e");
|
||||||
|
@ -218,11 +212,7 @@ class Node extends HiveObject with Keyable {
|
||||||
final body = {'jsonrpc': '2.0', 'id': '0', 'method': methodName};
|
final body = {'jsonrpc': '2.0', 'id': '0', 'method': methodName};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final authenticatingClient = HttpClient();
|
final client = ProxyWrapper().getHttpIOClient();
|
||||||
authenticatingClient.badCertificateCallback =
|
|
||||||
((X509Certificate cert, String host, int port) => true);
|
|
||||||
|
|
||||||
final http.Client client = ioc.IOClient(authenticatingClient);
|
|
||||||
|
|
||||||
final jsonBody = json.encode(body);
|
final jsonBody = json.encode(body);
|
||||||
|
|
||||||
|
@ -242,15 +232,15 @@ class Node extends HiveObject with Keyable {
|
||||||
return !(response['offline'] as bool);
|
return !(response['offline'] as bool);
|
||||||
}
|
}
|
||||||
|
|
||||||
printV("node check response: ${response.body}");
|
final responseString = await response.body;
|
||||||
|
|
||||||
if ((response.body.contains("400 Bad Request") // Some other generic error
|
if ((responseString.contains("400 Bad Request") // Some other generic error
|
||||||
||
|
||
|
||||||
response.body.contains("plain HTTP request was sent to HTTPS port") // Cloudflare
|
responseString.contains("plain HTTP request was sent to HTTPS port") // Cloudflare
|
||||||
||
|
||
|
||||||
response.headers["location"] != null // Generic reverse proxy
|
response.headers["location"] != null // Generic reverse proxy
|
||||||
||
|
||
|
||||||
response.body
|
responseString
|
||||||
.contains("301 Moved Permanently") // Poorly configured generic reverse proxy
|
.contains("301 Moved Permanently") // Poorly configured generic reverse proxy
|
||||||
) &&
|
) &&
|
||||||
!(useSSL ?? false)) {
|
!(useSSL ?? false)) {
|
||||||
|
@ -277,15 +267,16 @@ class Node extends HiveObject with Keyable {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<bool> requestNodeWithProxy() async {
|
Future<bool> requestNodeWithProxy() async {
|
||||||
if (!isValidProxyAddress /* && !Tor.instance.enabled*/) {
|
if (!isValidProxyAddress && !CakeTor.instance.enabled) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
String? proxy = socksProxyAddress;
|
String? proxy = socksProxyAddress;
|
||||||
|
|
||||||
// if ((proxy?.isEmpty ?? true) && Tor.instance.enabled) {
|
if ((proxy?.isEmpty ?? true) && CakeTor.instance.enabled) {
|
||||||
// proxy = "${InternetAddress.loopbackIPv4.address}:${Tor.instance.port}";
|
proxy = "${InternetAddress.loopbackIPv4.address}:${CakeTor.instance.port}";
|
||||||
// }
|
}
|
||||||
|
printV("proxy: $proxy");
|
||||||
if (proxy == null) {
|
if (proxy == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -305,13 +296,9 @@ class Node extends HiveObject with Keyable {
|
||||||
// you try to communicate with it
|
// you try to communicate with it
|
||||||
Future<bool> requestElectrumServer() async {
|
Future<bool> requestElectrumServer() async {
|
||||||
try {
|
try {
|
||||||
final Socket socket;
|
final ProxySocket socket;
|
||||||
if (useSSL == true) {
|
socket = await ProxyWrapper().getSocksSocket(useSSL ?? false, uri.host, uri.port);
|
||||||
socket = await SecureSocket.connect(uri.host, uri.port,
|
|
||||||
timeout: Duration(seconds: 5), onBadCertificate: (_) => true);
|
|
||||||
} else {
|
|
||||||
socket = await Socket.connect(uri.host, uri.port, timeout: Duration(seconds: 5));
|
|
||||||
}
|
|
||||||
|
|
||||||
socket.destroy();
|
socket.destroy();
|
||||||
return true;
|
return true;
|
||||||
|
@ -322,8 +309,8 @@ class Node extends HiveObject with Keyable {
|
||||||
|
|
||||||
Future<bool> requestNanoNode() async {
|
Future<bool> requestNanoNode() async {
|
||||||
try {
|
try {
|
||||||
final response = await http.post(
|
final response = await ProxyWrapper().post(
|
||||||
uri,
|
clearnetUri: uri,
|
||||||
headers: {"Content-Type": "application/json", "nano-app": "cake-wallet"},
|
headers: {"Content-Type": "application/json", "nano-app": "cake-wallet"},
|
||||||
body: jsonEncode(
|
body: jsonEncode(
|
||||||
{
|
{
|
||||||
|
@ -332,7 +319,8 @@ class Node extends HiveObject with Keyable {
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
final data = await jsonDecode(response.body);
|
|
||||||
|
final data = jsonDecode(response.body);
|
||||||
if (response.statusCode != 200 ||
|
if (response.statusCode != 200 ||
|
||||||
data["error"] != null ||
|
data["error"] != null ||
|
||||||
data["balance"] == null ||
|
data["balance"] == null ||
|
||||||
|
@ -348,13 +336,14 @@ class Node extends HiveObject with Keyable {
|
||||||
|
|
||||||
Future<bool> requestEthereumServer() async {
|
Future<bool> requestEthereumServer() async {
|
||||||
try {
|
try {
|
||||||
final response = await http.get(
|
final req = await ProxyWrapper().getHttpClient()
|
||||||
uri,
|
.getUrl(uri,)
|
||||||
headers: {'Content-Type': 'application/json'},
|
.timeout(Duration(seconds: 15));
|
||||||
);
|
final response = await req.close();
|
||||||
|
|
||||||
return response.statusCode >= 200 && response.statusCode < 300;
|
return response.statusCode >= 200 && response.statusCode < 300;
|
||||||
} catch (_) {
|
} catch (err) {
|
||||||
|
printV("Failed to request ethereum server: $err");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -462,7 +451,7 @@ class DaemonRpc {
|
||||||
|
|
||||||
/// Perform a JSON-RPC call with Digest Authentication.
|
/// Perform a JSON-RPC call with Digest Authentication.
|
||||||
Future<Map<String, dynamic>> call(String method, Map<String, dynamic> params) async {
|
Future<Map<String, dynamic>> call(String method, Map<String, dynamic> params) async {
|
||||||
final http.Client client = http.Client();
|
final client = ProxyWrapper().getHttpIOClient();
|
||||||
final DigestAuth digestAuth = DigestAuth(username, password);
|
final DigestAuth digestAuth = DigestAuth(username, password);
|
||||||
|
|
||||||
// Initial request to get the `WWW-Authenticate` header.
|
// Initial request to get the `WWW-Authenticate` header.
|
||||||
|
|
|
@ -1,33 +1,25 @@
|
||||||
import 'package:http/http.dart';
|
import 'dart:convert';
|
||||||
|
import 'package:cw_core/utils/proxy_wrapper.dart';
|
||||||
import 'package:on_chain/solana/solana.dart';
|
import 'package:on_chain/solana/solana.dart';
|
||||||
|
|
||||||
class SolanaRPCHTTPService implements SolanaServiceProvider {
|
class SolanaRPCHTTPService implements SolanaJSONRPCService {
|
||||||
SolanaRPCHTTPService(
|
SolanaRPCHTTPService(
|
||||||
{required this.url, Client? client, this.defaultRequestTimeout = const Duration(seconds: 30)})
|
{required this.url,
|
||||||
: client = client ?? Client();
|
this.defaultRequestTimeout = const Duration(seconds: 30)});
|
||||||
|
@override
|
||||||
final String url;
|
final String url;
|
||||||
final Client client;
|
|
||||||
final Duration defaultRequestTimeout;
|
final Duration defaultRequestTimeout;
|
||||||
|
|
||||||
@override
|
Future<Map<String, dynamic>> call(SolanaRequestDetails params,
|
||||||
Future<SolanaServiceResponse<T>> doRequest<T>(SolanaRequestDetails params,
|
[Duration? timeout]) async {
|
||||||
{Duration? timeout}) async {
|
final response = await ProxyWrapper().post(
|
||||||
if (!params.type.isPostRequest) {
|
clearnetUri: Uri.parse(url),
|
||||||
final response = await client.get(
|
body: params.toRequestBody(),
|
||||||
params.toUri(url),
|
headers: {
|
||||||
headers: {'Content-Type': 'application/json'},
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
).timeout(timeout ?? defaultRequestTimeout);
|
).timeout(timeout ?? defaultRequestTimeout);
|
||||||
return params.toResponse(response.bodyBytes, response.statusCode);
|
final data = json.decode(response.body) as Map<String, dynamic>;
|
||||||
}
|
return data;
|
||||||
|
|
||||||
final response = await client
|
|
||||||
.post(
|
|
||||||
params.toUri(url),
|
|
||||||
headers: {'Content-Type': 'application/json'},
|
|
||||||
body: params.body(),
|
|
||||||
)
|
|
||||||
.timeout(timeout ?? defaultRequestTimeout);
|
|
||||||
return params.toResponse(response.bodyBytes, response.statusCode);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
29
cw_core/lib/utils/proxy_logger/abstract.dart
Normal file
29
cw_core/lib/utils/proxy_logger/abstract.dart
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
import 'dart:typed_data';
|
||||||
|
import 'package:http/http.dart' as very_insecure_http_do_not_use;
|
||||||
|
|
||||||
|
enum RequestNetwork {
|
||||||
|
clearnet,
|
||||||
|
tor,
|
||||||
|
}
|
||||||
|
|
||||||
|
enum RequestMethod {
|
||||||
|
get,
|
||||||
|
post,
|
||||||
|
put,
|
||||||
|
delete,
|
||||||
|
|
||||||
|
newHttpClient,
|
||||||
|
newHttpIOClient,
|
||||||
|
newProxySocket,
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class ProxyLogger {
|
||||||
|
void log({
|
||||||
|
required Uri? uri,
|
||||||
|
required RequestMethod method,
|
||||||
|
required Uint8List body,
|
||||||
|
required very_insecure_http_do_not_use.Response? response,
|
||||||
|
required RequestNetwork network,
|
||||||
|
required String? error,
|
||||||
|
});
|
||||||
|
}
|
63
cw_core/lib/utils/proxy_logger/memory_proxy_logger.dart
Normal file
63
cw_core/lib/utils/proxy_logger/memory_proxy_logger.dart
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
import 'dart:typed_data';
|
||||||
|
|
||||||
|
import 'package:cw_core/utils/proxy_logger/abstract.dart';
|
||||||
|
import 'package:http/http.dart' as very_insecure_http_do_not_use;
|
||||||
|
|
||||||
|
class MemoryProxyLoggerEntry {
|
||||||
|
MemoryProxyLoggerEntry({
|
||||||
|
required this.trace,
|
||||||
|
required this.uri,
|
||||||
|
required this.body,
|
||||||
|
required this.network,
|
||||||
|
required this.method,
|
||||||
|
required this.response,
|
||||||
|
required this.error,
|
||||||
|
}) : time = DateTime.now();
|
||||||
|
|
||||||
|
final StackTrace trace;
|
||||||
|
final Uri? uri;
|
||||||
|
final Uint8List body;
|
||||||
|
final RequestNetwork network;
|
||||||
|
final very_insecure_http_do_not_use.Response? response;
|
||||||
|
final RequestMethod method;
|
||||||
|
final String? error;
|
||||||
|
final DateTime time;
|
||||||
|
@override
|
||||||
|
String toString() => """MemoryProxyLoggerEntry(
|
||||||
|
uri: $uri,
|
||||||
|
body: $body,
|
||||||
|
network: $network,
|
||||||
|
method: $method,
|
||||||
|
response:
|
||||||
|
code: ${response?.statusCode},
|
||||||
|
headers: ${response?.headers},
|
||||||
|
body: ${response?.body},
|
||||||
|
error: $error,
|
||||||
|
time: $time,
|
||||||
|
trace: ${trace}
|
||||||
|
);""";
|
||||||
|
}
|
||||||
|
|
||||||
|
class MemoryProxyLogger implements ProxyLogger {
|
||||||
|
static List<MemoryProxyLoggerEntry> logs = [];
|
||||||
|
@override
|
||||||
|
void log({
|
||||||
|
required Uri? uri,
|
||||||
|
required RequestMethod method,
|
||||||
|
required Uint8List body,
|
||||||
|
required very_insecure_http_do_not_use.Response? response,
|
||||||
|
required RequestNetwork network,
|
||||||
|
required String? error,
|
||||||
|
}) {
|
||||||
|
final trace = StackTrace.current;
|
||||||
|
logs.add(MemoryProxyLoggerEntry(
|
||||||
|
method: method,
|
||||||
|
trace: trace,
|
||||||
|
uri: uri,
|
||||||
|
body: body,
|
||||||
|
network: network,
|
||||||
|
response: response,
|
||||||
|
error: error,),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
17
cw_core/lib/utils/proxy_logger/silent_logger.dart
Normal file
17
cw_core/lib/utils/proxy_logger/silent_logger.dart
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
import 'dart:typed_data';
|
||||||
|
|
||||||
|
import 'package:cw_core/utils/proxy_logger/abstract.dart';
|
||||||
|
import 'package:http/http.dart' as very_insecure_http_do_not_use;
|
||||||
|
|
||||||
|
// we are not doing anything
|
||||||
|
class SilentProxyLogger implements ProxyLogger {
|
||||||
|
@override
|
||||||
|
void log({
|
||||||
|
required Uri? uri,
|
||||||
|
required RequestMethod method,
|
||||||
|
required Uint8List body,
|
||||||
|
required very_insecure_http_do_not_use.Response? response,
|
||||||
|
required RequestNetwork network,
|
||||||
|
required String? error,
|
||||||
|
}) {}
|
||||||
|
}
|
47
cw_core/lib/utils/proxy_socket/abstract.dart
Normal file
47
cw_core/lib/utils/proxy_socket/abstract.dart
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
import 'dart:async';
|
||||||
|
import 'dart:io';
|
||||||
|
import 'dart:typed_data';
|
||||||
|
|
||||||
|
import 'package:cw_core/utils/proxy_socket/insecure.dart';
|
||||||
|
import 'package:cw_core/utils/proxy_socket/secure.dart';
|
||||||
|
import 'package:cw_core/utils/proxy_socket/socks.dart';
|
||||||
|
import 'package:cw_core/utils/proxy_wrapper.dart';
|
||||||
|
import 'package:socks_socket/socks_socket.dart';
|
||||||
|
|
||||||
|
class ProxyAddress {
|
||||||
|
final String host;
|
||||||
|
final int port;
|
||||||
|
|
||||||
|
ProxyAddress({required this.host, required this.port});
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class ProxySocket {
|
||||||
|
static Future<ProxySocket> connect(bool sslEnabled, ProxyAddress address, {Duration? connectionTimeout}) async {
|
||||||
|
if (CakeTor.instance.started) {
|
||||||
|
var socksSocket = await SOCKSSocket.create(
|
||||||
|
proxyHost: InternetAddress.loopbackIPv4.address,
|
||||||
|
proxyPort: CakeTor.instance.port,
|
||||||
|
sslEnabled: sslEnabled,
|
||||||
|
);
|
||||||
|
await socksSocket.connect();
|
||||||
|
await socksSocket.connectTo(address.host, address.port);
|
||||||
|
return ProxySocketSocks(socksSocket);
|
||||||
|
}
|
||||||
|
if (sslEnabled == false) {
|
||||||
|
return ProxySocketInsecure(await Socket.connect(address.host, address.port, timeout: connectionTimeout));
|
||||||
|
} else {
|
||||||
|
return ProxySocketSecure(await SecureSocket.connect(
|
||||||
|
address.host,
|
||||||
|
address.port,
|
||||||
|
timeout: connectionTimeout,
|
||||||
|
onBadCertificate: (_) => true,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> close();
|
||||||
|
Future<void> destroy();
|
||||||
|
Future<void> write(String data);
|
||||||
|
StreamSubscription<List<int>> listen(Function(Uint8List event) onData, {Function (Object error)? onError, Function ()? onDone, bool cancelOnError = true});
|
||||||
|
ProxyAddress get address;
|
||||||
|
}
|
34
cw_core/lib/utils/proxy_socket/insecure.dart
Normal file
34
cw_core/lib/utils/proxy_socket/insecure.dart
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
|
||||||
|
import 'package:cw_core/utils/proxy_socket/abstract.dart';
|
||||||
|
import 'dart:async';
|
||||||
|
import 'dart:typed_data';
|
||||||
|
import 'dart:io';
|
||||||
|
|
||||||
|
class ProxySocketInsecure implements ProxySocket {
|
||||||
|
final Socket socket;
|
||||||
|
|
||||||
|
ProxySocketInsecure(this.socket);
|
||||||
|
|
||||||
|
ProxyAddress get address => ProxyAddress(host: socket.remoteAddress.host, port: socket.remotePort);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> close() => socket.close();
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> destroy() async => socket.destroy();
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> write(String data) async => socket.write(data);
|
||||||
|
|
||||||
|
@override
|
||||||
|
StreamSubscription<List<int>> listen(Function(Uint8List event) onData, {Function(Object error)? onError, Function()? onDone, bool cancelOnError = true}) {
|
||||||
|
return socket.listen(
|
||||||
|
(data) {
|
||||||
|
onData(Uint8List.fromList(data));
|
||||||
|
},
|
||||||
|
onError: onError,
|
||||||
|
onDone: onDone,
|
||||||
|
cancelOnError: cancelOnError,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
34
cw_core/lib/utils/proxy_socket/secure.dart
Normal file
34
cw_core/lib/utils/proxy_socket/secure.dart
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
import 'dart:async';
|
||||||
|
import 'dart:io';
|
||||||
|
import 'dart:typed_data';
|
||||||
|
|
||||||
|
import 'package:cw_core/utils/proxy_socket/abstract.dart';
|
||||||
|
|
||||||
|
class ProxySocketSecure implements ProxySocket {
|
||||||
|
final SecureSocket socket;
|
||||||
|
|
||||||
|
ProxySocketSecure(this.socket);
|
||||||
|
|
||||||
|
ProxyAddress get address => ProxyAddress(host: socket.remoteAddress.host, port: socket.remotePort);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> close() => socket.close();
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> destroy() async => socket.destroy();
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> write(String data) async => socket.write(data);
|
||||||
|
|
||||||
|
@override
|
||||||
|
StreamSubscription<List<int>> listen(Function(Uint8List event) onData, {Function(Object error)? onError, Function()? onDone, bool cancelOnError = true}) {
|
||||||
|
return socket.listen(
|
||||||
|
(data) {
|
||||||
|
onData(Uint8List.fromList(data));
|
||||||
|
},
|
||||||
|
onError: onError,
|
||||||
|
onDone: onDone,
|
||||||
|
cancelOnError: cancelOnError,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
36
cw_core/lib/utils/proxy_socket/socks.dart
Normal file
36
cw_core/lib/utils/proxy_socket/socks.dart
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
import 'dart:async';
|
||||||
|
import 'dart:typed_data';
|
||||||
|
|
||||||
|
import 'package:cw_core/utils/proxy_socket/abstract.dart';
|
||||||
|
import 'package:socks_socket/socks_socket.dart';
|
||||||
|
|
||||||
|
class ProxySocketSocks implements ProxySocket {
|
||||||
|
final SOCKSSocket socket;
|
||||||
|
|
||||||
|
ProxySocketSocks(this.socket);
|
||||||
|
|
||||||
|
@override
|
||||||
|
ProxyAddress get address => ProxyAddress(host: socket.proxyHost, port: socket.proxyPort);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> close() => socket.close();
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> destroy() => close();
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> write(String data) async => socket.write(data);
|
||||||
|
|
||||||
|
@override
|
||||||
|
StreamSubscription<List<int>> listen(Function(Uint8List event) onData, {Function(Object error)? onError, Function()? onDone, bool cancelOnError = true}) {
|
||||||
|
return socket.listen(
|
||||||
|
(data) {
|
||||||
|
onData(Uint8List.fromList(data));
|
||||||
|
},
|
||||||
|
onError: onError,
|
||||||
|
onDone: onDone,
|
||||||
|
cancelOnError: cancelOnError,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
447
cw_core/lib/utils/proxy_wrapper.dart
Normal file
447
cw_core/lib/utils/proxy_wrapper.dart
Normal file
|
@ -0,0 +1,447 @@
|
||||||
|
import 'dart:async';
|
||||||
|
import 'dart:convert';
|
||||||
|
import 'dart:io';
|
||||||
|
import 'dart:typed_data';
|
||||||
|
import 'package:cw_core/utils/proxy_logger/abstract.dart';
|
||||||
|
import 'package:cw_core/utils/proxy_socket/abstract.dart';
|
||||||
|
import 'package:cw_core/utils/tor/abstract.dart';
|
||||||
|
import 'package:cw_core/utils/tor/android.dart';
|
||||||
|
import 'package:cw_core/utils/tor/disabled.dart';
|
||||||
|
import 'package:http/http.dart';
|
||||||
|
import 'package:socks5_proxy/socks_client.dart';
|
||||||
|
import 'package:http/io_client.dart' as ioc;
|
||||||
|
|
||||||
|
class ProxyWrapper {
|
||||||
|
static final ProxyWrapper _proxyWrapper = ProxyWrapper._internal();
|
||||||
|
static ProxyLogger? logger;
|
||||||
|
|
||||||
|
factory ProxyWrapper() {
|
||||||
|
return _proxyWrapper;
|
||||||
|
}
|
||||||
|
|
||||||
|
ProxyWrapper._internal();
|
||||||
|
Future<ProxySocket> getSocksSocket(bool sslEnabled, String host, int port, {Duration? connectionTimeout}) async {
|
||||||
|
logger?.log(
|
||||||
|
uri: Uri(
|
||||||
|
scheme: sslEnabled ? "https" : "http",
|
||||||
|
host: host,
|
||||||
|
port: port,
|
||||||
|
),
|
||||||
|
method: RequestMethod.newProxySocket,
|
||||||
|
body: Uint8List(0),
|
||||||
|
response: null,
|
||||||
|
network: requestNetwork(),
|
||||||
|
error: null
|
||||||
|
);
|
||||||
|
return ProxySocket.connect(sslEnabled, ProxyAddress(host: host, port: port), connectionTimeout: connectionTimeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
RequestNetwork requestNetwork() {
|
||||||
|
return CakeTor.instance.started ? RequestNetwork.tor : RequestNetwork.clearnet;
|
||||||
|
}
|
||||||
|
|
||||||
|
ioc.IOClient getHttpIOClient({int? portOverride, bool internal = false}) {
|
||||||
|
if (!internal) {
|
||||||
|
logger?.log(
|
||||||
|
uri: null,
|
||||||
|
method: RequestMethod.newHttpIOClient,
|
||||||
|
body: Uint8List(0),
|
||||||
|
response: null,
|
||||||
|
network: requestNetwork(),
|
||||||
|
error: null,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// ignore: deprecated_member_use_from_same_package
|
||||||
|
final httpClient = ProxyWrapper().getHttpClient(portOverride: portOverride, internal: true);
|
||||||
|
return ioc.IOClient(httpClient);
|
||||||
|
}
|
||||||
|
|
||||||
|
int getPort() => CakeTor.instance.port;
|
||||||
|
|
||||||
|
@Deprecated('Use ProxyWrapper().get/post/put methods instead, and provide proper clearnet and onion uri.')
|
||||||
|
HttpClient getHttpClient({int? portOverride, bool internal = false}) {
|
||||||
|
if (!internal) {
|
||||||
|
logger?.log(
|
||||||
|
uri: null,
|
||||||
|
method: RequestMethod.newProxySocket,
|
||||||
|
body: Uint8List(0),
|
||||||
|
response: null,
|
||||||
|
network: requestNetwork(),
|
||||||
|
error: null
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (CakeTor.instance.started) {
|
||||||
|
// Assign connection factory.
|
||||||
|
final client = HttpClient();
|
||||||
|
SocksTCPClient.assignToHttpClient(client, [
|
||||||
|
ProxySettings(
|
||||||
|
InternetAddress.loopbackIPv4,
|
||||||
|
CakeTor.instance.port,
|
||||||
|
password: null,
|
||||||
|
),
|
||||||
|
]);
|
||||||
|
return client;
|
||||||
|
} else {
|
||||||
|
return HttpClient();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Future<Response> _make({
|
||||||
|
required RequestMethod method,
|
||||||
|
required ioc.IOClient client,
|
||||||
|
required Uri uri,
|
||||||
|
required Map<String, String>? headers,
|
||||||
|
String? body,
|
||||||
|
}) async {
|
||||||
|
Object? error;
|
||||||
|
Response? resp;
|
||||||
|
try {
|
||||||
|
switch (method) {
|
||||||
|
case RequestMethod.get:
|
||||||
|
resp = await client. get(
|
||||||
|
uri,
|
||||||
|
headers: headers,
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
case RequestMethod.delete:
|
||||||
|
resp = await client.delete(
|
||||||
|
uri,
|
||||||
|
headers: headers,
|
||||||
|
body: body,
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
case RequestMethod.post:
|
||||||
|
resp = await client.post(
|
||||||
|
uri,
|
||||||
|
headers: headers,
|
||||||
|
body: body,
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
case RequestMethod.put:
|
||||||
|
resp = await client.put(
|
||||||
|
uri,
|
||||||
|
headers: headers,
|
||||||
|
body: body,
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
case RequestMethod.newHttpClient:
|
||||||
|
case RequestMethod.newHttpIOClient:
|
||||||
|
case RequestMethod.newProxySocket:
|
||||||
|
throw UnimplementedError();
|
||||||
|
}
|
||||||
|
return resp;
|
||||||
|
} catch (e) {
|
||||||
|
error = e;
|
||||||
|
rethrow;
|
||||||
|
} finally {
|
||||||
|
logger?.log(
|
||||||
|
uri: uri,
|
||||||
|
method: RequestMethod.get,
|
||||||
|
body: utf8.encode(body ?? ''),
|
||||||
|
response: resp,
|
||||||
|
network: requestNetwork(),
|
||||||
|
error: error?.toString(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<Response> get({
|
||||||
|
Map<String, String>? headers,
|
||||||
|
int? portOverride,
|
||||||
|
Uri? clearnetUri,
|
||||||
|
Uri? onionUri,
|
||||||
|
}) async {
|
||||||
|
ioc.IOClient? torClient;
|
||||||
|
bool torEnabled = CakeTor.instance.started;
|
||||||
|
|
||||||
|
if (CakeTor.instance.started) {
|
||||||
|
torEnabled = true;
|
||||||
|
} else {
|
||||||
|
torEnabled = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if tor is enabled, try to connect to the onion url first:
|
||||||
|
if (torEnabled) {
|
||||||
|
try {
|
||||||
|
// ignore: deprecated_member_use_from_same_package
|
||||||
|
torClient = await getHttpIOClient(portOverride: portOverride, internal: true);
|
||||||
|
} catch (_) {
|
||||||
|
rethrow;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (onionUri != null) {
|
||||||
|
try {
|
||||||
|
return await _make(
|
||||||
|
method: RequestMethod.get,
|
||||||
|
client: torClient,
|
||||||
|
uri: onionUri,
|
||||||
|
headers: headers,
|
||||||
|
);
|
||||||
|
} catch (_) {
|
||||||
|
rethrow;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (clearnetUri != null) {
|
||||||
|
try {
|
||||||
|
return await _make(
|
||||||
|
method: RequestMethod.get,
|
||||||
|
client: torClient,
|
||||||
|
uri: clearnetUri,
|
||||||
|
headers: headers,
|
||||||
|
);
|
||||||
|
} catch (_) {
|
||||||
|
rethrow;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (clearnetUri != null) {
|
||||||
|
try {
|
||||||
|
return HttpOverrides.runZoned(
|
||||||
|
() async {
|
||||||
|
return await _make(
|
||||||
|
method: RequestMethod.get,
|
||||||
|
client: ioc.IOClient(),
|
||||||
|
uri: clearnetUri,
|
||||||
|
headers: headers,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
} catch (_) {
|
||||||
|
// we weren't able to get a response:
|
||||||
|
rethrow;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw Exception("Unable to connect to server");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Future<Response> post({
|
||||||
|
Map<String, String>? headers,
|
||||||
|
int? portOverride,
|
||||||
|
Uri? clearnetUri,
|
||||||
|
Uri? onionUri,
|
||||||
|
String? body,
|
||||||
|
bool allowMitmMoneroBypassSSLCheck = false,
|
||||||
|
}) async {
|
||||||
|
HttpClient? torHttpClient;
|
||||||
|
HttpClient cleatnetHttpClient = HttpClient();
|
||||||
|
if (allowMitmMoneroBypassSSLCheck) {
|
||||||
|
cleatnetHttpClient.badCertificateCallback =
|
||||||
|
((X509Certificate cert, String host, int port) => true);
|
||||||
|
}
|
||||||
|
|
||||||
|
ioc.IOClient clearnetClient = ioc.IOClient(cleatnetHttpClient);
|
||||||
|
|
||||||
|
|
||||||
|
bool torEnabled = CakeTor.instance.started;
|
||||||
|
|
||||||
|
if (torEnabled) {
|
||||||
|
try {
|
||||||
|
// ignore: deprecated_member_use_from_same_package
|
||||||
|
torHttpClient = await getHttpClient(portOverride: portOverride);
|
||||||
|
} catch (_) {
|
||||||
|
rethrow;
|
||||||
|
}
|
||||||
|
if (allowMitmMoneroBypassSSLCheck) {
|
||||||
|
torHttpClient.badCertificateCallback =
|
||||||
|
((X509Certificate cert, String host, int port) => true);
|
||||||
|
}
|
||||||
|
if (onionUri != null) {
|
||||||
|
try {
|
||||||
|
return await _make(
|
||||||
|
method: RequestMethod.post,
|
||||||
|
client: ioc.IOClient(torHttpClient),
|
||||||
|
uri: onionUri,
|
||||||
|
headers: headers,
|
||||||
|
body: body,
|
||||||
|
);
|
||||||
|
} catch (_) {
|
||||||
|
rethrow;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (clearnetUri != null) {
|
||||||
|
try {
|
||||||
|
return await _make(
|
||||||
|
method: RequestMethod.post,
|
||||||
|
client: ioc.IOClient(torHttpClient),
|
||||||
|
uri: clearnetUri,
|
||||||
|
headers: headers,
|
||||||
|
body: body,
|
||||||
|
);
|
||||||
|
} catch (_) {
|
||||||
|
rethrow;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (clearnetUri != null) {
|
||||||
|
try {
|
||||||
|
return HttpOverrides.runZoned(
|
||||||
|
() async {
|
||||||
|
return await _make(
|
||||||
|
method: RequestMethod.post,
|
||||||
|
client: clearnetClient,
|
||||||
|
uri: clearnetUri,
|
||||||
|
headers: headers,
|
||||||
|
body: body,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
} catch (_) {
|
||||||
|
rethrow;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw Exception("Unable to connect to server");
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<Response> put({
|
||||||
|
Map<String, String>? headers,
|
||||||
|
int? portOverride,
|
||||||
|
Uri? clearnetUri,
|
||||||
|
Uri? onionUri,
|
||||||
|
String? body,
|
||||||
|
}) async {
|
||||||
|
ioc.IOClient? torClient;
|
||||||
|
bool torEnabled = CakeTor.instance.started;
|
||||||
|
|
||||||
|
if (torEnabled) {
|
||||||
|
try {
|
||||||
|
// ignore: deprecated_member_use_from_same_package
|
||||||
|
torClient = await getHttpIOClient(portOverride: portOverride, internal: true);
|
||||||
|
} catch (_) {}
|
||||||
|
|
||||||
|
if (onionUri != null) {
|
||||||
|
try {
|
||||||
|
return await _make(
|
||||||
|
method: RequestMethod.put,
|
||||||
|
client: torClient!,
|
||||||
|
uri: onionUri,
|
||||||
|
headers: headers,
|
||||||
|
body: body,
|
||||||
|
);
|
||||||
|
} catch (_) {
|
||||||
|
rethrow;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (clearnetUri != null) {
|
||||||
|
try {
|
||||||
|
return await _make(
|
||||||
|
method: RequestMethod.put,
|
||||||
|
client: torClient!,
|
||||||
|
uri: clearnetUri,
|
||||||
|
headers: headers,
|
||||||
|
body: body,
|
||||||
|
);
|
||||||
|
} catch (_) {
|
||||||
|
rethrow;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (clearnetUri != null) {
|
||||||
|
try {
|
||||||
|
return HttpOverrides.runZoned(
|
||||||
|
() async {
|
||||||
|
return await _make(
|
||||||
|
method: RequestMethod.put,
|
||||||
|
client: ioc.IOClient(),
|
||||||
|
uri: clearnetUri,
|
||||||
|
headers: headers,
|
||||||
|
body: body,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
} catch (_) {
|
||||||
|
// we weren't able to get a response:
|
||||||
|
rethrow;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw Exception("Unable to connect to server");
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<Response> delete({
|
||||||
|
Map<String, String>? headers,
|
||||||
|
int? portOverride,
|
||||||
|
Uri? clearnetUri,
|
||||||
|
Uri? onionUri,
|
||||||
|
}) async {
|
||||||
|
ioc.IOClient? torClient;
|
||||||
|
bool torEnabled = CakeTor.instance.started;
|
||||||
|
|
||||||
|
if (CakeTor.instance.started) {
|
||||||
|
torEnabled = true;
|
||||||
|
} else {
|
||||||
|
torEnabled = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if tor is enabled, try to connect to the onion url first:
|
||||||
|
if (torEnabled) {
|
||||||
|
try {
|
||||||
|
// ignore: deprecated_member_use_from_same_package
|
||||||
|
torClient = await getHttpIOClient(portOverride: portOverride, internal: true);
|
||||||
|
} catch (_) {
|
||||||
|
rethrow;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (onionUri != null) {
|
||||||
|
try {
|
||||||
|
return await _make(
|
||||||
|
method: RequestMethod.delete,
|
||||||
|
client: torClient,
|
||||||
|
uri: onionUri,
|
||||||
|
headers: headers,
|
||||||
|
);
|
||||||
|
} catch (_) {
|
||||||
|
rethrow;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (clearnetUri != null) {
|
||||||
|
try {
|
||||||
|
return await _make(
|
||||||
|
method: RequestMethod.delete,
|
||||||
|
client: torClient,
|
||||||
|
uri: clearnetUri,
|
||||||
|
headers: headers,
|
||||||
|
);
|
||||||
|
} catch (_) {
|
||||||
|
rethrow;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (clearnetUri != null) {
|
||||||
|
try {
|
||||||
|
return HttpOverrides.runZoned(
|
||||||
|
() async {
|
||||||
|
return await _make(
|
||||||
|
method: RequestMethod.delete,
|
||||||
|
client: ioc.IOClient(),
|
||||||
|
uri: clearnetUri,
|
||||||
|
headers: headers,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
} catch (_) {
|
||||||
|
// we weren't able to get a response:
|
||||||
|
rethrow;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw Exception("Unable to connect to server");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class CakeTor {
|
||||||
|
static final CakeTorInstance instance = CakeTorInstance.getInstance();
|
||||||
|
}
|
38
cw_core/lib/utils/tor/abstract.dart
Normal file
38
cw_core/lib/utils/tor/abstract.dart
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:cw_core/utils/print_verbose.dart';
|
||||||
|
import 'package:cw_core/utils/tor/android.dart';
|
||||||
|
import 'package:cw_core/utils/tor/disabled.dart';
|
||||||
|
import 'package:cw_core/utils/tor/tails.dart';
|
||||||
|
|
||||||
|
abstract class CakeTorInstance {
|
||||||
|
bool get started;
|
||||||
|
|
||||||
|
int get port => -1;
|
||||||
|
|
||||||
|
bool get enabled => false;
|
||||||
|
|
||||||
|
bool get bootstrapped => false;
|
||||||
|
|
||||||
|
Future<void> start();
|
||||||
|
Future<void> stop();
|
||||||
|
|
||||||
|
static CakeTorInstance getInstance() {
|
||||||
|
if (Platform.isAndroid) {
|
||||||
|
return CakeTorAndroid();
|
||||||
|
}
|
||||||
|
if (Platform.isLinux) {
|
||||||
|
try {
|
||||||
|
final os = File("/etc/os-release").readAsLinesSync();
|
||||||
|
for (var line in os) {
|
||||||
|
if (!line.startsWith("ID=")) continue;
|
||||||
|
if (!line.contains("tails")) continue;
|
||||||
|
return CakeTorTails();
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
printV("Failed to identify linux version - /etc/os-release missing");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return CakeTorDisabled();
|
||||||
|
}
|
||||||
|
}
|
73
cw_core/lib/utils/tor/android.dart
Normal file
73
cw_core/lib/utils/tor/android.dart
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
import 'dart:convert';
|
||||||
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:cw_core/utils/print_verbose.dart';
|
||||||
|
import 'package:cw_core/utils/tor/abstract.dart';
|
||||||
|
import 'package:path_provider/path_provider.dart';
|
||||||
|
import 'package:path/path.dart' as p;
|
||||||
|
import 'package:tor_binary/tor_binary_platform_interface.dart';
|
||||||
|
|
||||||
|
class CakeTorAndroid implements CakeTorInstance {
|
||||||
|
@override
|
||||||
|
bool get bootstrapped => _proc != null;
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool get enabled => _proc != null;
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get port => 42142;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> start() async {
|
||||||
|
await _runEmbeddedTor();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool get started => _proc != null;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> stop() async {
|
||||||
|
_proc?.kill();
|
||||||
|
await _proc?.exitCode;
|
||||||
|
_proc = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Process? _proc;
|
||||||
|
|
||||||
|
Future<void> _runEmbeddedTor() async {
|
||||||
|
final dir = await getApplicationCacheDirectory();
|
||||||
|
|
||||||
|
final torBinPath = p.join((await TorBinaryPlatform.instance.getBinaryPath())!, "libtor.so");
|
||||||
|
printV("torPath: $torBinPath");
|
||||||
|
|
||||||
|
if (started) {
|
||||||
|
printV("Proxy is running");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
printV("Starting embedded tor");
|
||||||
|
printV("app docs: $dir");
|
||||||
|
final torrc = """
|
||||||
|
SocksPort $port
|
||||||
|
Log notice file ${p.join(dir.path, "tor.log")}
|
||||||
|
RunAsDaemon 0
|
||||||
|
DataDirectory ${p.join(dir.path, "tor-data")}
|
||||||
|
""";
|
||||||
|
final torrcPath = p.join(dir.absolute.path, "torrc");
|
||||||
|
File(torrcPath).writeAsStringSync(torrc);
|
||||||
|
|
||||||
|
if (_proc != null) {
|
||||||
|
try {
|
||||||
|
_proc?.kill();
|
||||||
|
await _proc?.exitCode;
|
||||||
|
_proc = null;
|
||||||
|
} catch (e) {
|
||||||
|
printV(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
printV("path: $torBinPath -f $torrcPath");
|
||||||
|
_proc = await Process.start(torBinPath, ["-f", torrcPath]);
|
||||||
|
_proc?.stdout.transform(utf8.decoder).forEach(printV);
|
||||||
|
_proc?.stderr.transform(utf8.decoder).forEach(printV);
|
||||||
|
}
|
||||||
|
}
|
21
cw_core/lib/utils/tor/disabled.dart
Normal file
21
cw_core/lib/utils/tor/disabled.dart
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
import 'package:cw_core/utils/tor/abstract.dart';
|
||||||
|
|
||||||
|
class CakeTorDisabled implements CakeTorInstance {
|
||||||
|
@override
|
||||||
|
bool get bootstrapped => false;
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool get enabled => false;
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get port => -1;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> start() => throw UnimplementedError();
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool get started => false;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> stop() => throw UnimplementedError();
|
||||||
|
}
|
21
cw_core/lib/utils/tor/tails.dart
Normal file
21
cw_core/lib/utils/tor/tails.dart
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
import 'package:cw_core/utils/tor/abstract.dart';
|
||||||
|
|
||||||
|
class CakeTorTails implements CakeTorInstance {
|
||||||
|
@override
|
||||||
|
bool get bootstrapped => true;
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool get enabled => true;
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get port => 9150;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> start() async {}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool get started => true;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> stop() async {}
|
||||||
|
}
|
|
@ -26,18 +26,18 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: args
|
name: args
|
||||||
sha256: bf9f5caeea8d8fe6721a9c358dd8a5c1947b27f1cfaa18b39c301273594919e6
|
sha256: d0481093c50b1da8910eb0bb301626d4d8eb7284aa739614d2b394ee09e3ea04
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.6.0"
|
version: "2.7.0"
|
||||||
asn1lib:
|
asn1lib:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: asn1lib
|
name: asn1lib
|
||||||
sha256: "4bae5ae63e6d6dd17c4aac8086f3dec26c0236f6a0f03416c6c19d830c367cf5"
|
sha256: "1c296cd268f486cabcc3930e9b93a8133169305f18d722916e675959a88f6d2c"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.5.8"
|
version: "1.5.9"
|
||||||
async:
|
async:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -50,11 +50,11 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
path: "."
|
path: "."
|
||||||
ref: cake-update-v4
|
ref: cake-update-v2
|
||||||
resolved-ref: "437dadd0bd9bf73ec6a551299577799341f6486a"
|
resolved-ref: "59fdf29d72068e0522a96a8953ed7272833a9f57"
|
||||||
url: "https://github.com/cake-tech/blockchain_utils"
|
url: "https://github.com/cake-tech/blockchain_utils"
|
||||||
source: git
|
source: git
|
||||||
version: "4.3.0"
|
version: "3.3.0"
|
||||||
boolean_selector:
|
boolean_selector:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -67,50 +67,50 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: build
|
name: build
|
||||||
sha256: "80184af8b6cb3e5c1c4ec6d8544d27711700bc3e6d2efad04238c7b5290889f0"
|
sha256: cef23f1eda9b57566c81e2133d196f8e3df48f244b317368d65c5943d91148f0
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.4.1"
|
version: "2.4.2"
|
||||||
build_config:
|
build_config:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: build_config
|
name: build_config
|
||||||
sha256: bf80fcfb46a29945b423bd9aad884590fb1dc69b330a4d4700cac476af1708d1
|
sha256: "4ae2de3e1e67ea270081eaee972e1bd8f027d459f249e0f1186730784c2e7e33"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.1.1"
|
version: "1.1.2"
|
||||||
build_daemon:
|
build_daemon:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: build_daemon
|
name: build_daemon
|
||||||
sha256: "79b2aef6ac2ed00046867ed354c88778c9c0f029df8a20fe10b5436826721ef9"
|
sha256: "8e928697a82be082206edb0b9c99c5a4ad6bc31c9e9b8b2f291ae65cd4a25daa"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.0.2"
|
version: "4.0.4"
|
||||||
build_resolvers:
|
build_resolvers:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description:
|
description:
|
||||||
name: build_resolvers
|
name: build_resolvers
|
||||||
sha256: "339086358431fa15d7eca8b6a36e5d783728cf025e559b834f4609a1fcfb7b0a"
|
sha256: b9e4fda21d846e192628e7a4f6deda6888c36b5b69ba02ff291a01fd529140f0
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.4.2"
|
version: "2.4.4"
|
||||||
build_runner:
|
build_runner:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description:
|
description:
|
||||||
name: build_runner
|
name: build_runner
|
||||||
sha256: "028819cfb90051c6b5440c7e574d1896f8037e3c96cf17aaeb054c9311cfbf4d"
|
sha256: "058fe9dce1de7d69c4b84fada934df3e0153dd000758c4d65964d0166779aa99"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.4.13"
|
version: "2.4.15"
|
||||||
build_runner_core:
|
build_runner_core:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: build_runner_core
|
name: build_runner_core
|
||||||
sha256: f8126682b87a7282a339b871298cc12009cb67109cfa1614d6436fb0289193e0
|
sha256: "22e3aa1c80e0ada3722fe5b63fd43d9c8990759d0a2cf489c8c5d7b2bdebc021"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "7.3.2"
|
version: "8.0.0"
|
||||||
built_collection:
|
built_collection:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -123,10 +123,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: built_value
|
name: built_value
|
||||||
sha256: "8b158ab94ec6913e480dc3f752418348b5ae099eb75868b5f4775f0572999c61"
|
sha256: "082001b5c3dc495d4a42f1d5789990505df20d8547d42507c29050af6933ee27"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "8.9.4"
|
version: "8.10.1"
|
||||||
cake_backup:
|
cake_backup:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
@ -212,10 +212,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: dart_style
|
name: dart_style
|
||||||
sha256: "7856d364b589d1f08986e140938578ed36ed948581fbc3bc9aef1805039ac5ab"
|
sha256: "7306ab8a2359a48d22310ad823521d723acfed60ee1f7e37388e8986853b6820"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.3.7"
|
version: "2.3.8"
|
||||||
decimal:
|
decimal:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
@ -326,10 +326,10 @@ packages:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: http
|
name: http
|
||||||
sha256: fe7ab022b76f3034adc518fb6ea04a82387620e19977665ea18d30a1cf43442f
|
sha256: "2c11f3f94c687ee9bad77c171151672986360b2b001d109814ee7140b2cf261b"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.3.0"
|
version: "1.4.0"
|
||||||
http_multi_server:
|
http_multi_server:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -342,10 +342,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: http_parser
|
name: http_parser
|
||||||
sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b"
|
sha256: "178d74305e7866013777bab2c3d8726205dc5a4dd935297175b19a23a2e66571"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.0.2"
|
version: "4.1.2"
|
||||||
intl:
|
intl:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
@ -462,10 +462,10 @@ packages:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description:
|
description:
|
||||||
name: mobx_codegen
|
name: mobx_codegen
|
||||||
sha256: "990da80722f7d7c0017dec92040b31545d625b15d40204c36a1e63d167c73cdc"
|
sha256: e0abbbc651a69550440f6b65c99ec222a1e2a4afd7baec8ba0f3088c7ca582a8
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.7.0"
|
version: "2.7.1"
|
||||||
nested:
|
nested:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -478,19 +478,19 @@ packages:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
path: "."
|
path: "."
|
||||||
ref: cake-update-v4
|
ref: cake-update-v2
|
||||||
resolved-ref: "084fb7bf13ec42d74f26ac08c883ce07c10fca7e"
|
resolved-ref: "096865a8c6b89c260beadfec04f7e184c40a3273"
|
||||||
url: "https://github.com/cake-tech/on_chain.git"
|
url: "https://github.com/cake-tech/on_chain.git"
|
||||||
source: git
|
source: git
|
||||||
version: "6.2.0"
|
version: "3.7.0"
|
||||||
package_config:
|
package_config:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: package_config
|
name: package_config
|
||||||
sha256: "92d4488434b520a62570293fbd33bb556c7d49230791c1b4bbd973baf6d2dc67"
|
sha256: f096c55ebb7deb7e384101542bfba8c52696c1b56fca2eb62827989ef2353bbc
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.1"
|
version: "2.2.0"
|
||||||
path:
|
path:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -511,10 +511,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: path_provider_android
|
name: path_provider_android
|
||||||
sha256: "4adf4fd5423ec60a29506c76581bc05854c55e3a0b72d35bb28d661c9686edf2"
|
sha256: d0d310befe2c8ab9e7f393288ccbb11b60c019c6b5afc21973eeee4dda2b35e9
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.2.15"
|
version: "2.2.17"
|
||||||
path_provider_foundation:
|
path_provider_foundation:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -583,26 +583,26 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: provider
|
name: provider
|
||||||
sha256: c8a055ee5ce3fd98d6fc872478b03823ffdb448699c6ebdbbc71d59b596fd48c
|
sha256: "4abbd070a04e9ddc287673bf5a030c7ca8b685ff70218720abab8b092f53dd84"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "6.1.2"
|
version: "6.1.5"
|
||||||
pub_semver:
|
pub_semver:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: pub_semver
|
name: pub_semver
|
||||||
sha256: "7b3cfbf654f3edd0c6298ecd5be782ce997ddf0e00531b9464b55245185bbbbd"
|
sha256: "5bfcf68ca79ef689f8990d1160781b4bad40a3bd5e5218ad4076ddb7f4081585"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.5"
|
version: "2.2.0"
|
||||||
pubspec_parse:
|
pubspec_parse:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: pubspec_parse
|
name: pubspec_parse
|
||||||
sha256: "81876843eb50dc2e1e5b151792c9a985c5ed2536914115ed04e9c8528f6647b0"
|
sha256: "0560ba233314abbed0a48a2956f7f022cce7c3e1e73df540277da7544cad4082"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.4.0"
|
version: "1.5.0"
|
||||||
rational:
|
rational:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -615,18 +615,18 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: shelf
|
name: shelf
|
||||||
sha256: ad29c505aee705f41a4d8963641f91ac4cee3c8fad5947e033390a7bd8180fa4
|
sha256: e7dd780a7ffb623c57850b33f43309312fc863fb6aa3d276a754bb299839ef12
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.4.1"
|
version: "1.4.2"
|
||||||
shelf_web_socket:
|
shelf_web_socket:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: shelf_web_socket
|
name: shelf_web_socket
|
||||||
sha256: cc36c297b52866d203dbf9332263c94becc2fe0ceaa9681d07b6ef9807023b67
|
sha256: "3632775c8e90d6c9712f883e633716432a27758216dfb61bd86a8321c0580925"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.1"
|
version: "3.0.0"
|
||||||
sky_engine:
|
sky_engine:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description: flutter
|
description: flutter
|
||||||
|
@ -635,11 +635,21 @@ packages:
|
||||||
socks5_proxy:
|
socks5_proxy:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: socks5_proxy
|
path: "."
|
||||||
sha256: "616818a0ea1064a4823b53c9f7eaf8da64ed82dcd51ed71371c7e54751ed5053"
|
ref: "27ad7c2efae8d7460325c74b90f660085cbd0685"
|
||||||
url: "https://pub.dev"
|
resolved-ref: "27ad7c2efae8d7460325c74b90f660085cbd0685"
|
||||||
source: hosted
|
url: "https://github.com/LacticWhale/socks_dart"
|
||||||
version: "1.0.6"
|
source: git
|
||||||
|
version: "2.1.0"
|
||||||
|
socks_socket:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
path: "."
|
||||||
|
ref: e6232c53c1595469931ababa878759a067c02e94
|
||||||
|
resolved-ref: e6232c53c1595469931ababa878759a067c02e94
|
||||||
|
url: "https://github.com/sneurlax/socks_socket"
|
||||||
|
source: git
|
||||||
|
version: "1.1.1"
|
||||||
source_gen:
|
source_gen:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -716,10 +726,19 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: timing
|
name: timing
|
||||||
sha256: "62ee18aca144e4a9f29d212f5a4c6a053be252b895ab14b5821996cff4ed90fe"
|
sha256: "70a3b636575d4163c477e6de42f247a23b315ae20e86442bebe32d3cabf61c32"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.2"
|
version: "1.0.1"
|
||||||
|
tor_binary:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
path: "."
|
||||||
|
ref: cb811c610871a9517d47134b87c2f590c15c96c5
|
||||||
|
resolved-ref: cb811c610871a9517d47134b87c2f590c15c96c5
|
||||||
|
url: "https://github.com/MrCyjaneK/flutter-tor_binary"
|
||||||
|
source: git
|
||||||
|
version: "4.7.14"
|
||||||
tuple:
|
tuple:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -780,18 +799,18 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: web_socket
|
name: web_socket
|
||||||
sha256: "3c12d96c0c9a4eec095246debcea7b86c0324f22df69893d538fcc6f1b8cce83"
|
sha256: "34d64019aa8e36bf9842ac014bb5d2f5586ca73df5e4d9bf5c936975cae6982c"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.1.6"
|
version: "1.0.1"
|
||||||
web_socket_channel:
|
web_socket_channel:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: web_socket_channel
|
name: web_socket_channel
|
||||||
sha256: "0b8e2457400d8a859b7b2030786835a28a8e80836ef64402abef392ff4f1d0e5"
|
sha256: d645757fb0f4773d602444000a8131ff5d48c9e47adfe9772652dd1a4f2d45c8
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.0.2"
|
version: "3.0.3"
|
||||||
xdg_directories:
|
xdg_directories:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -809,5 +828,5 @@ packages:
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.1.3"
|
version: "3.1.3"
|
||||||
sdks:
|
sdks:
|
||||||
dart: ">=3.5.0 <4.0.0"
|
dart: ">=3.6.0 <4.0.0"
|
||||||
flutter: ">=3.24.0"
|
flutter: ">=3.27.0"
|
||||||
|
|
|
@ -25,22 +25,29 @@ dependencies:
|
||||||
url: https://github.com/cake-tech/cake_backup.git
|
url: https://github.com/cake-tech/cake_backup.git
|
||||||
ref: main
|
ref: main
|
||||||
version: 1.0.0
|
version: 1.0.0
|
||||||
socks5_proxy: ^1.0.4
|
socks5_proxy:
|
||||||
|
git:
|
||||||
|
url: https://github.com/LacticWhale/socks_dart
|
||||||
|
ref: 27ad7c2efae8d7460325c74b90f660085cbd0685
|
||||||
unorm_dart: ^0.3.0
|
unorm_dart: ^0.3.0
|
||||||
on_chain:
|
on_chain:
|
||||||
git:
|
git:
|
||||||
url: https://github.com/cake-tech/on_chain.git
|
url: https://github.com/cake-tech/on_chain.git
|
||||||
ref: cake-update-v4
|
ref: cake-update-v2
|
||||||
# tor:
|
socks_socket:
|
||||||
# git:
|
git:
|
||||||
# url: https://github.com/cake-tech/tor.git
|
url: https://github.com/sneurlax/socks_socket
|
||||||
# ref: main
|
ref: e6232c53c1595469931ababa878759a067c02e94
|
||||||
|
tor_binary:
|
||||||
|
git:
|
||||||
|
url: https://github.com/MrCyjaneK/flutter-tor_binary
|
||||||
|
ref: cb811c610871a9517d47134b87c2f590c15c96c5
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
build_runner: ^2.4.7
|
build_runner: ^2.4.15
|
||||||
build_resolvers: ^2.0.9
|
build_resolvers: ^2.4.4
|
||||||
mobx_codegen: ^2.0.7
|
mobx_codegen: ^2.0.7
|
||||||
hive_generator: ^2.0.1
|
hive_generator: ^2.0.1
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,8 @@ import 'package:path/path.dart' as p;
|
||||||
import 'package:cw_core/exceptions.dart';
|
import 'package:cw_core/exceptions.dart';
|
||||||
import 'package:cw_core/transaction_direction.dart';
|
import 'package:cw_core/transaction_direction.dart';
|
||||||
import 'package:cw_core/utils/print_verbose.dart';
|
import 'package:cw_core/utils/print_verbose.dart';
|
||||||
|
import 'package:cw_core/pathForWallet.dart';
|
||||||
|
import 'package:cw_core/wallet_type.dart';
|
||||||
import 'package:cw_decred/amount_format.dart';
|
import 'package:cw_decred/amount_format.dart';
|
||||||
import 'package:cw_decred/pending_transaction.dart';
|
import 'package:cw_decred/pending_transaction.dart';
|
||||||
import 'package:cw_decred/transaction_credentials.dart';
|
import 'package:cw_decred/transaction_credentials.dart';
|
||||||
|
@ -307,9 +309,10 @@ abstract class DecredWalletBase
|
||||||
persistantPeer = addr;
|
persistantPeer = addr;
|
||||||
await _libwallet.closeWallet(walletInfo.name);
|
await _libwallet.closeWallet(walletInfo.name);
|
||||||
final network = isTestnet ? "testnet" : "mainnet";
|
final network = isTestnet ? "testnet" : "mainnet";
|
||||||
|
final dirPath = await pathForWalletDir(name: walletInfo.name, type: WalletType.decred);
|
||||||
final config = {
|
final config = {
|
||||||
"name": walletInfo.name,
|
"name": walletInfo.name,
|
||||||
"datadir": walletInfo.dirPath,
|
"datadir": dirPath,
|
||||||
"net": network,
|
"net": network,
|
||||||
"unsyncedaddrs": true,
|
"unsyncedaddrs": true,
|
||||||
};
|
};
|
||||||
|
@ -611,7 +614,7 @@ abstract class DecredWalletBase
|
||||||
}
|
}
|
||||||
|
|
||||||
await for (final entity in sourceDir.list(recursive: true)) {
|
await for (final entity in sourceDir.list(recursive: true)) {
|
||||||
final relativePath = entity.path.substring(sourceDir.path.length+1);
|
final relativePath = entity.path.substring(sourceDir.path.length + 1);
|
||||||
final targetPath = p.join(targetDir.path, relativePath);
|
final targetPath = p.join(targetDir.path, relativePath);
|
||||||
|
|
||||||
if (entity is File) {
|
if (entity is File) {
|
||||||
|
|
|
@ -8,6 +8,7 @@ import 'package:cw_core/wallet_service.dart';
|
||||||
import 'package:cw_core/pathForWallet.dart';
|
import 'package:cw_core/pathForWallet.dart';
|
||||||
import 'package:cw_core/wallet_info.dart';
|
import 'package:cw_core/wallet_info.dart';
|
||||||
import 'package:cw_core/wallet_type.dart';
|
import 'package:cw_core/wallet_type.dart';
|
||||||
|
import 'package:path/path.dart';
|
||||||
import 'package:hive/hive.dart';
|
import 'package:hive/hive.dart';
|
||||||
import 'package:collection/collection.dart';
|
import 'package:collection/collection.dart';
|
||||||
import 'package:cw_core/unspent_coins_info.dart';
|
import 'package:cw_core/unspent_coins_info.dart';
|
||||||
|
@ -57,42 +58,93 @@ class DecredWalletService extends WalletService<
|
||||||
@override
|
@override
|
||||||
Future<DecredWallet> create(DecredNewWalletCredentials credentials, {bool? isTestnet}) async {
|
Future<DecredWallet> create(DecredNewWalletCredentials credentials, {bool? isTestnet}) async {
|
||||||
await this.init();
|
await this.init();
|
||||||
|
final dirPath = await pathForWalletDir(name: credentials.walletInfo!.name, type: getType());
|
||||||
|
final network = isTestnet == true ? testnet : mainnet;
|
||||||
final config = {
|
final config = {
|
||||||
"name": credentials.walletInfo!.name,
|
"name": credentials.walletInfo!.name,
|
||||||
"datadir": credentials.walletInfo!.dirPath,
|
"datadir": dirPath,
|
||||||
"pass": credentials.password!,
|
"pass": credentials.password!,
|
||||||
"net": isTestnet == true ? testnet : mainnet,
|
"net": network,
|
||||||
"unsyncedaddrs": true,
|
"unsyncedaddrs": true,
|
||||||
};
|
};
|
||||||
await libwallet!.createWallet(jsonEncode(config));
|
await libwallet!.createWallet(jsonEncode(config));
|
||||||
final di = DerivationInfo(
|
final di = DerivationInfo(
|
||||||
derivationPath: isTestnet == true ? seedRestorePathTestnet : seedRestorePath);
|
derivationPath: isTestnet == true ? seedRestorePathTestnet : seedRestorePath);
|
||||||
credentials.walletInfo!.derivationInfo = di;
|
credentials.walletInfo!.derivationInfo = di;
|
||||||
|
credentials.walletInfo!.network = network;
|
||||||
|
// ios will move our wallet directory when updating. Since we must
|
||||||
|
// recalculate the new path every time we open the wallet, ensure this path
|
||||||
|
// is not used. An older wallet will have a directory here which is a
|
||||||
|
// condition for moving the wallet when opening, so this must be kept blank
|
||||||
|
// going forward.
|
||||||
|
credentials.walletInfo!.dirPath = "";
|
||||||
|
credentials.walletInfo!.path = "";
|
||||||
final wallet = DecredWallet(credentials.walletInfo!, credentials.password!,
|
final wallet = DecredWallet(credentials.walletInfo!, credentials.password!,
|
||||||
this.unspentCoinsInfoSource, libwallet!, closeLibwallet);
|
this.unspentCoinsInfoSource, libwallet!, closeLibwallet);
|
||||||
await wallet.init();
|
await wallet.init();
|
||||||
return wallet;
|
return wallet;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void copyDirectorySync(Directory source, Directory destination) {
|
||||||
|
/// create destination folder if not exist
|
||||||
|
if (!destination.existsSync()) {
|
||||||
|
destination.createSync(recursive: true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// get all files from source (recursive: false is important here)
|
||||||
|
source.listSync(recursive: false).forEach((entity) {
|
||||||
|
final newPath = destination.path + Platform.pathSeparator + basename(entity.path);
|
||||||
|
if (entity is File) {
|
||||||
|
entity.rename(newPath);
|
||||||
|
} else if (entity is Directory) {
|
||||||
|
copyDirectorySync(entity, Directory(newPath));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> moveWallet(String fromPath, String toPath) async {
|
||||||
|
final oldWalletDir = new Directory(fromPath);
|
||||||
|
final newWalletDir = new Directory(toPath);
|
||||||
|
copyDirectorySync(oldWalletDir, newWalletDir);
|
||||||
|
// It would be ideal to delete the old directory here, but ios will error
|
||||||
|
// sometimes with "OS Error: No such file or directory, errno = 2" even
|
||||||
|
// after checking if it exists.
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<DecredWallet> openWallet(String name, String password) async {
|
Future<DecredWallet> openWallet(String name, String password) async {
|
||||||
final walletInfo = walletInfoSource.values
|
final walletInfo = walletInfoSource.values
|
||||||
.firstWhereOrNull((info) => info.id == WalletBase.idFor(name, getType()))!;
|
.firstWhereOrNull((info) => info.id == WalletBase.idFor(name, getType()))!;
|
||||||
final network = walletInfo.derivationInfo?.derivationPath == seedRestorePathTestnet ||
|
if (walletInfo.network == null || walletInfo.network == "") {
|
||||||
|
walletInfo.network = walletInfo.derivationInfo?.derivationPath == seedRestorePathTestnet ||
|
||||||
walletInfo.derivationInfo?.derivationPath == pubkeyRestorePathTestnet
|
walletInfo.derivationInfo?.derivationPath == pubkeyRestorePathTestnet
|
||||||
? testnet
|
? testnet
|
||||||
: mainnet;
|
: mainnet;
|
||||||
|
}
|
||||||
|
|
||||||
await this.init();
|
await this.init();
|
||||||
final walletDirExists = Directory(walletInfo.dirPath).existsSync();
|
|
||||||
if (!walletDirExists) {
|
// Cake wallet version 4.27.0 and earlier gave a wallet dir that did not
|
||||||
walletInfo.dirPath = await pathForWalletDir(name: name, type: getType());
|
// match the name. Move those to the correct place.
|
||||||
|
final dirPath = await pathForWalletDir(name: name, type: getType());
|
||||||
|
if (walletInfo.path != "") {
|
||||||
|
// On ios the stored dir no longer exists. We can only trust the basename.
|
||||||
|
// dirPath may already be updated and lost the basename, so look at path.
|
||||||
|
final randomBasename = basename(walletInfo.path);
|
||||||
|
final oldDir = await pathForWalletDir(name: randomBasename, type: getType());
|
||||||
|
if (oldDir != dirPath) {
|
||||||
|
await this.moveWallet(oldDir, dirPath);
|
||||||
|
}
|
||||||
|
// Clear the path so this does not trigger again.
|
||||||
|
walletInfo.dirPath = "";
|
||||||
|
walletInfo.path = "";
|
||||||
|
await walletInfo.save();
|
||||||
}
|
}
|
||||||
|
|
||||||
final config = {
|
final config = {
|
||||||
"name": walletInfo.name,
|
"name": name,
|
||||||
"datadir": walletInfo.dirPath,
|
"datadir": dirPath,
|
||||||
"net": network,
|
"net": walletInfo.network,
|
||||||
"unsyncedaddrs": true,
|
"unsyncedaddrs": true,
|
||||||
};
|
};
|
||||||
await libwallet!.loadWallet(jsonEncode(config));
|
await libwallet!.loadWallet(jsonEncode(config));
|
||||||
|
@ -127,12 +179,11 @@ class DecredWalletService extends WalletService<
|
||||||
|
|
||||||
await currentWallet.renameWalletFiles(newName);
|
await currentWallet.renameWalletFiles(newName);
|
||||||
|
|
||||||
final newDirPath = await pathForWalletDir(name: newName, type: getType());
|
|
||||||
final newWalletInfo = currentWalletInfo;
|
final newWalletInfo = currentWalletInfo;
|
||||||
newWalletInfo.id = WalletBase.idFor(newName, getType());
|
newWalletInfo.id = WalletBase.idFor(newName, getType());
|
||||||
newWalletInfo.name = newName;
|
newWalletInfo.name = newName;
|
||||||
newWalletInfo.dirPath = newDirPath;
|
newWalletInfo.dirPath = "";
|
||||||
newWalletInfo.network = network;
|
newWalletInfo.path = "";
|
||||||
|
|
||||||
await walletInfoSource.put(currentWalletInfo.key, newWalletInfo);
|
await walletInfoSource.put(currentWalletInfo.key, newWalletInfo);
|
||||||
}
|
}
|
||||||
|
@ -141,18 +192,23 @@ class DecredWalletService extends WalletService<
|
||||||
Future<DecredWallet> restoreFromSeed(DecredRestoreWalletFromSeedCredentials credentials,
|
Future<DecredWallet> restoreFromSeed(DecredRestoreWalletFromSeedCredentials credentials,
|
||||||
{bool? isTestnet}) async {
|
{bool? isTestnet}) async {
|
||||||
await this.init();
|
await this.init();
|
||||||
|
final network = isTestnet == true ? testnet : mainnet;
|
||||||
|
final dirPath = await pathForWalletDir(name: credentials.walletInfo!.name, type: getType());
|
||||||
final config = {
|
final config = {
|
||||||
"name": credentials.walletInfo!.name,
|
"name": credentials.walletInfo!.name,
|
||||||
"datadir": credentials.walletInfo!.dirPath,
|
"datadir": dirPath,
|
||||||
"pass": credentials.password!,
|
"pass": credentials.password!,
|
||||||
"mnemonic": credentials.mnemonic,
|
"mnemonic": credentials.mnemonic,
|
||||||
"net": isTestnet == true ? testnet : mainnet,
|
"net": network,
|
||||||
"unsyncedaddrs": true,
|
"unsyncedaddrs": true,
|
||||||
};
|
};
|
||||||
await libwallet!.createWallet(jsonEncode(config));
|
await libwallet!.createWallet(jsonEncode(config));
|
||||||
final di = DerivationInfo(
|
final di = DerivationInfo(
|
||||||
derivationPath: isTestnet == true ? seedRestorePathTestnet : seedRestorePath);
|
derivationPath: isTestnet == true ? seedRestorePathTestnet : seedRestorePath);
|
||||||
credentials.walletInfo!.derivationInfo = di;
|
credentials.walletInfo!.derivationInfo = di;
|
||||||
|
credentials.walletInfo!.network = network;
|
||||||
|
credentials.walletInfo!.dirPath = "";
|
||||||
|
credentials.walletInfo!.path = "";
|
||||||
final wallet = DecredWallet(credentials.walletInfo!, credentials.password!,
|
final wallet = DecredWallet(credentials.walletInfo!, credentials.password!,
|
||||||
this.unspentCoinsInfoSource, libwallet!, closeLibwallet);
|
this.unspentCoinsInfoSource, libwallet!, closeLibwallet);
|
||||||
await wallet.init();
|
await wallet.init();
|
||||||
|
@ -165,17 +221,22 @@ class DecredWalletService extends WalletService<
|
||||||
Future<DecredWallet> restoreFromKeys(DecredRestoreWalletFromPubkeyCredentials credentials,
|
Future<DecredWallet> restoreFromKeys(DecredRestoreWalletFromPubkeyCredentials credentials,
|
||||||
{bool? isTestnet}) async {
|
{bool? isTestnet}) async {
|
||||||
await this.init();
|
await this.init();
|
||||||
|
final network = isTestnet == true ? testnet : mainnet;
|
||||||
|
final dirPath = await pathForWalletDir(name: credentials.walletInfo!.name, type: getType());
|
||||||
final config = {
|
final config = {
|
||||||
"name": credentials.walletInfo!.name,
|
"name": credentials.walletInfo!.name,
|
||||||
"datadir": credentials.walletInfo!.dirPath,
|
"datadir": dirPath,
|
||||||
"pubkey": credentials.pubkey,
|
"pubkey": credentials.pubkey,
|
||||||
"net": isTestnet == true ? testnet : mainnet,
|
"net": network,
|
||||||
"unsyncedaddrs": true,
|
"unsyncedaddrs": true,
|
||||||
};
|
};
|
||||||
await libwallet!.createWatchOnlyWallet(jsonEncode(config));
|
await libwallet!.createWatchOnlyWallet(jsonEncode(config));
|
||||||
final di = DerivationInfo(
|
final di = DerivationInfo(
|
||||||
derivationPath: isTestnet == true ? pubkeyRestorePathTestnet : pubkeyRestorePath);
|
derivationPath: isTestnet == true ? pubkeyRestorePathTestnet : pubkeyRestorePath);
|
||||||
credentials.walletInfo!.derivationInfo = di;
|
credentials.walletInfo!.derivationInfo = di;
|
||||||
|
credentials.walletInfo!.network = network;
|
||||||
|
credentials.walletInfo!.dirPath = "";
|
||||||
|
credentials.walletInfo!.path = "";
|
||||||
final wallet = DecredWallet(credentials.walletInfo!, credentials.password!,
|
final wallet = DecredWallet(credentials.walletInfo!, credentials.password!,
|
||||||
this.unspentCoinsInfoSource, libwallet!, closeLibwallet);
|
this.unspentCoinsInfoSource, libwallet!, closeLibwallet);
|
||||||
await wallet.init();
|
await wallet.init();
|
||||||
|
|
|
@ -50,11 +50,11 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
path: "."
|
path: "."
|
||||||
ref: cake-update-v4
|
ref: cake-update-v2
|
||||||
resolved-ref: "437dadd0bd9bf73ec6a551299577799341f6486a"
|
resolved-ref: "59fdf29d72068e0522a96a8953ed7272833a9f57"
|
||||||
url: "https://github.com/cake-tech/blockchain_utils"
|
url: "https://github.com/cake-tech/blockchain_utils"
|
||||||
source: git
|
source: git
|
||||||
version: "4.3.0"
|
version: "3.3.0"
|
||||||
boolean_selector:
|
boolean_selector:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -91,26 +91,26 @@ packages:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description:
|
description:
|
||||||
name: build_resolvers
|
name: build_resolvers
|
||||||
sha256: "339086358431fa15d7eca8b6a36e5d783728cf025e559b834f4609a1fcfb7b0a"
|
sha256: b9e4fda21d846e192628e7a4f6deda6888c36b5b69ba02ff291a01fd529140f0
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.4.2"
|
version: "2.4.4"
|
||||||
build_runner:
|
build_runner:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description:
|
description:
|
||||||
name: build_runner
|
name: build_runner
|
||||||
sha256: "028819cfb90051c6b5440c7e574d1896f8037e3c96cf17aaeb054c9311cfbf4d"
|
sha256: "058fe9dce1de7d69c4b84fada934df3e0153dd000758c4d65964d0166779aa99"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.4.13"
|
version: "2.4.15"
|
||||||
build_runner_core:
|
build_runner_core:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: build_runner_core
|
name: build_runner_core
|
||||||
sha256: f8126682b87a7282a339b871298cc12009cb67109cfa1614d6436fb0289193e0
|
sha256: "22e3aa1c80e0ada3722fe5b63fd43d9c8990759d0a2cf489c8c5d7b2bdebc021"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "7.3.2"
|
version: "8.0.0"
|
||||||
built_collection:
|
built_collection:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -501,11 +501,11 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
path: "."
|
path: "."
|
||||||
ref: cake-update-v4
|
ref: cake-update-v2
|
||||||
resolved-ref: "084fb7bf13ec42d74f26ac08c883ce07c10fca7e"
|
resolved-ref: "93440dc5126369b873ca1fccc13c3c1240b1c5c2"
|
||||||
url: "https://github.com/cake-tech/on_chain.git"
|
url: "https://github.com/cake-tech/on_chain.git"
|
||||||
source: git
|
source: git
|
||||||
version: "6.2.0"
|
version: "3.7.0"
|
||||||
package_config:
|
package_config:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -666,11 +666,21 @@ packages:
|
||||||
socks5_proxy:
|
socks5_proxy:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: socks5_proxy
|
path: "."
|
||||||
sha256: "616818a0ea1064a4823b53c9f7eaf8da64ed82dcd51ed71371c7e54751ed5053"
|
ref: "27ad7c2efae8d7460325c74b90f660085cbd0685"
|
||||||
url: "https://pub.dev"
|
resolved-ref: "27ad7c2efae8d7460325c74b90f660085cbd0685"
|
||||||
source: hosted
|
url: "https://github.com/LacticWhale/socks_dart"
|
||||||
version: "1.0.6"
|
source: git
|
||||||
|
version: "2.1.0"
|
||||||
|
socks_socket:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
path: "."
|
||||||
|
ref: e6232c53c1595469931ababa878759a067c02e94
|
||||||
|
resolved-ref: e6232c53c1595469931ababa878759a067c02e94
|
||||||
|
url: "https://github.com/sneurlax/socks_socket"
|
||||||
|
source: git
|
||||||
|
version: "1.1.1"
|
||||||
source_gen:
|
source_gen:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -751,6 +761,15 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.2"
|
version: "1.0.2"
|
||||||
|
tor_binary:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
path: "."
|
||||||
|
ref: cb811c610871a9517d47134b87c2f590c15c96c5
|
||||||
|
resolved-ref: cb811c610871a9517d47134b87c2f590c15c96c5
|
||||||
|
url: "https://github.com/MrCyjaneK/flutter-tor_binary"
|
||||||
|
source: git
|
||||||
|
version: "4.7.14"
|
||||||
tuple:
|
tuple:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -848,5 +867,5 @@ packages:
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.2.2"
|
version: "2.2.2"
|
||||||
sdks:
|
sdks:
|
||||||
dart: ">=3.5.0 <4.0.0"
|
dart: ">=3.6.0 <4.0.0"
|
||||||
flutter: ">=3.24.0"
|
flutter: ">=3.24.0"
|
||||||
|
|
|
@ -19,8 +19,8 @@ dependencies:
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
build_runner: ^2.1.11
|
build_runner: ^2.4.15
|
||||||
build_resolvers: ^2.0.9
|
build_resolvers: ^2.4.4
|
||||||
mobx_codegen: ^2.0.7
|
mobx_codegen: ^2.0.7
|
||||||
hive_generator: ^2.0.1
|
hive_generator: ^2.0.1
|
||||||
ffigen: ^16.1.0
|
ffigen: ^16.1.0
|
||||||
|
|
170
cw_ethereum/lib/deuro/deuro_savings.dart
Normal file
170
cw_ethereum/lib/deuro/deuro_savings.dart
Normal file
|
@ -0,0 +1,170 @@
|
||||||
|
import 'package:cw_core/crypto_currency.dart';
|
||||||
|
import 'package:cw_ethereum/deuro/deuro_savings_contract.dart';
|
||||||
|
import 'package:cw_ethereum/ethereum_wallet.dart';
|
||||||
|
import 'package:cw_evm/contract/erc20.dart';
|
||||||
|
import 'package:cw_evm/evm_chain_exceptions.dart';
|
||||||
|
import 'package:cw_evm/evm_chain_transaction_priority.dart';
|
||||||
|
import 'package:cw_evm/pending_evm_chain_transaction.dart';
|
||||||
|
import 'package:web3dart/crypto.dart';
|
||||||
|
import 'package:web3dart/web3dart.dart';
|
||||||
|
|
||||||
|
const String savingsGatewayAddress = "0x073493d73258C4BEb6542e8dd3e1b2891C972303";
|
||||||
|
|
||||||
|
const String dEuroAddress = "0xbA3f535bbCcCcA2A154b573Ca6c5A49BAAE0a3ea";
|
||||||
|
const String frontendCode = "0x00000000000000000000000000000000000000000043616b652057616c6c6574";
|
||||||
|
|
||||||
|
class DEuro {
|
||||||
|
final SavingsGateway _savingsGateway;
|
||||||
|
final ERC20 _dEuro;
|
||||||
|
final EthereumWallet _wallet;
|
||||||
|
|
||||||
|
DEuro(EthereumWallet wallet)
|
||||||
|
: _wallet = wallet,
|
||||||
|
_savingsGateway = _getSavingsGateway(wallet.getWeb3Client()!),
|
||||||
|
_dEuro = _getDEuroToken(wallet.getWeb3Client()!);
|
||||||
|
|
||||||
|
static SavingsGateway _getSavingsGateway(Web3Client client) => SavingsGateway(
|
||||||
|
address: EthereumAddress.fromHex(savingsGatewayAddress),
|
||||||
|
client: client,
|
||||||
|
);
|
||||||
|
|
||||||
|
static ERC20 _getDEuroToken(Web3Client client) => ERC20(
|
||||||
|
address: EthereumAddress.fromHex(dEuroAddress),
|
||||||
|
client: client,
|
||||||
|
);
|
||||||
|
|
||||||
|
EthereumAddress get _address => EthereumAddress.fromHex(_wallet.walletAddresses.primaryAddress);
|
||||||
|
|
||||||
|
Future<BigInt> get savingsBalance async =>
|
||||||
|
(await _savingsGateway.savings(accountOwner: _address)).saved;
|
||||||
|
|
||||||
|
Future<BigInt> get accruedInterest => _savingsGateway.accruedInterest(accountOwner: _address);
|
||||||
|
|
||||||
|
Future<BigInt> get interestRate => _savingsGateway.currentRatePPM();
|
||||||
|
|
||||||
|
Future<BigInt> get approvedBalance => _dEuro.allowance(_address, _savingsGateway.self.address);
|
||||||
|
|
||||||
|
Future<void> _checkEthBalanceForGasFees(EVMChainTransactionPriority priority) async {
|
||||||
|
final ethBalance = await _wallet.getWeb3Client()!.getBalance(_address);
|
||||||
|
final currentBalance = ethBalance.getInWei;
|
||||||
|
|
||||||
|
final gasFeesModel = await _wallet.calculateActualEstimatedFeeForCreateTransaction(
|
||||||
|
amount: BigInt.zero,
|
||||||
|
contractAddress: _savingsGateway.self.address.hexEip55,
|
||||||
|
receivingAddressHex: _savingsGateway.self.address.hexEip55,
|
||||||
|
priority: priority,
|
||||||
|
data: _savingsGateway.self.abi.functions[17]
|
||||||
|
.encodeCall([BigInt.zero, hexToBytes(frontendCode)]),
|
||||||
|
);
|
||||||
|
|
||||||
|
final estimatedGasFee = BigInt.from(gasFeesModel.estimatedGasFee);
|
||||||
|
final requiredBalance = estimatedGasFee;
|
||||||
|
|
||||||
|
if (currentBalance < requiredBalance) {
|
||||||
|
throw DeuroGasFeeException(
|
||||||
|
requiredGasFee: requiredBalance,
|
||||||
|
currentBalance: currentBalance,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<PendingEVMChainTransaction> depositSavings(
|
||||||
|
BigInt amount, EVMChainTransactionPriority priority) async {
|
||||||
|
try {
|
||||||
|
await _checkEthBalanceForGasFees(priority);
|
||||||
|
|
||||||
|
final signedTransaction = await _savingsGateway.save(
|
||||||
|
(amount: amount, frontendCode: hexToBytes(frontendCode)),
|
||||||
|
credentials: _wallet.evmChainPrivateKey,
|
||||||
|
);
|
||||||
|
|
||||||
|
final fee = await _wallet.calculateActualEstimatedFeeForCreateTransaction(
|
||||||
|
amount: amount,
|
||||||
|
contractAddress: _savingsGateway.self.address.hexEip55,
|
||||||
|
receivingAddressHex: _savingsGateway.self.address.hexEip55,
|
||||||
|
priority: priority,
|
||||||
|
data: _savingsGateway.self.abi.functions[17].encodeCall([amount, hexToBytes(frontendCode)]),
|
||||||
|
);
|
||||||
|
|
||||||
|
final sendTransaction = () => _wallet.getWeb3Client()!.sendRawTransaction(signedTransaction);
|
||||||
|
|
||||||
|
return PendingEVMChainTransaction(
|
||||||
|
sendTransaction: sendTransaction,
|
||||||
|
signedTransaction: signedTransaction,
|
||||||
|
fee: BigInt.from(fee.estimatedGasFee),
|
||||||
|
amount: amount.toString(),
|
||||||
|
exponent: 18,
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
if (e.toString().contains('insufficient funds for gas')) {
|
||||||
|
final ethBalance = await _wallet.getWeb3Client()!.getBalance(_address);
|
||||||
|
throw DeuroGasFeeException(
|
||||||
|
currentBalance: ethBalance.getInWei,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
rethrow;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<PendingEVMChainTransaction> withdrawSavings(
|
||||||
|
BigInt amount, EVMChainTransactionPriority priority) async {
|
||||||
|
try {
|
||||||
|
await _checkEthBalanceForGasFees(priority);
|
||||||
|
|
||||||
|
final signedTransaction = await _savingsGateway.withdraw(
|
||||||
|
(target: _address, amount: amount, frontendCode: hexToBytes(frontendCode)),
|
||||||
|
credentials: _wallet.evmChainPrivateKey,
|
||||||
|
);
|
||||||
|
|
||||||
|
final fee = await _wallet.calculateActualEstimatedFeeForCreateTransaction(
|
||||||
|
amount: amount,
|
||||||
|
contractAddress: _savingsGateway.self.address.hexEip55,
|
||||||
|
receivingAddressHex: _savingsGateway.self.address.hexEip55,
|
||||||
|
priority: priority,
|
||||||
|
data: _savingsGateway.self.abi.functions[17].encodeCall([amount, hexToBytes(frontendCode)]),
|
||||||
|
);
|
||||||
|
|
||||||
|
final sendTransaction = () => _wallet.getWeb3Client()!.sendRawTransaction(signedTransaction);
|
||||||
|
|
||||||
|
return PendingEVMChainTransaction(
|
||||||
|
sendTransaction: sendTransaction,
|
||||||
|
signedTransaction: signedTransaction,
|
||||||
|
fee: BigInt.from(fee.estimatedGasFee),
|
||||||
|
amount: amount.toString(),
|
||||||
|
exponent: 18);
|
||||||
|
} catch (e) {
|
||||||
|
if (e.toString().contains('insufficient funds for gas')) {
|
||||||
|
final ethBalance = await _wallet.getWeb3Client()!.getBalance(_address);
|
||||||
|
throw DeuroGasFeeException(
|
||||||
|
currentBalance: ethBalance.getInWei,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
rethrow;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set an infinite approval to save gas in the future
|
||||||
|
Future<PendingEVMChainTransaction> enableSavings(EVMChainTransactionPriority priority) async {
|
||||||
|
try {
|
||||||
|
await _checkEthBalanceForGasFees(priority);
|
||||||
|
|
||||||
|
return (await _wallet.createApprovalTransaction(
|
||||||
|
BigInt.parse(
|
||||||
|
'ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff',
|
||||||
|
radix: 16,
|
||||||
|
),
|
||||||
|
_savingsGateway.self.address.hexEip55,
|
||||||
|
CryptoCurrency.deuro,
|
||||||
|
priority,
|
||||||
|
)) as PendingEVMChainTransaction;
|
||||||
|
} catch (e) {
|
||||||
|
if (e.toString().contains('insufficient funds for gas')) {
|
||||||
|
final ethBalance = await _wallet.getWeb3Client()!.getBalance(_address);
|
||||||
|
throw DeuroGasFeeException(
|
||||||
|
currentBalance: ethBalance.getInWei,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
rethrow;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
543
cw_ethereum/lib/deuro/deuro_savings_contract.dart
Normal file
543
cw_ethereum/lib/deuro/deuro_savings_contract.dart
Normal file
File diff suppressed because one or more lines are too long
|
@ -19,7 +19,7 @@ class EthereumClient extends EVMChainClient {
|
||||||
Future<List<EVMChainTransactionModel>> fetchTransactions(String address,
|
Future<List<EVMChainTransactionModel>> fetchTransactions(String address,
|
||||||
{String? contractAddress}) async {
|
{String? contractAddress}) async {
|
||||||
try {
|
try {
|
||||||
final response = await httpClient.get(Uri.https("api.etherscan.io", "/v2/api", {
|
final response = await client.get(Uri.https("api.etherscan.io", "/v2/api", {
|
||||||
"chainid": "$chainId",
|
"chainid": "$chainId",
|
||||||
"module": "account",
|
"module": "account",
|
||||||
"action": contractAddress != null ? "tokentx" : "txlist",
|
"action": contractAddress != null ? "tokentx" : "txlist",
|
||||||
|
@ -51,7 +51,7 @@ class EthereumClient extends EVMChainClient {
|
||||||
@override
|
@override
|
||||||
Future<List<EVMChainTransactionModel>> fetchInternalTransactions(String address) async {
|
Future<List<EVMChainTransactionModel>> fetchInternalTransactions(String address) async {
|
||||||
try {
|
try {
|
||||||
final response = await httpClient.get(Uri.https("api.etherscan.io", "/v2/api", {
|
final response = await client.get(Uri.https("api.etherscan.io", "/v2/api", {
|
||||||
"chainid": "$chainId",
|
"chainid": "$chainId",
|
||||||
"module": "account",
|
"module": "account",
|
||||||
"action": "txlistinternal",
|
"action": "txlistinternal",
|
||||||
|
|
|
@ -6,7 +6,7 @@ author: Cake Wallet
|
||||||
homepage: https://cakewallet.com
|
homepage: https://cakewallet.com
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: '>=2.18.2 <3.0.0'
|
sdk: ^3.5.0
|
||||||
flutter: ">=1.17.0"
|
flutter: ">=1.17.0"
|
||||||
|
|
||||||
dependencies:
|
dependencies:
|
||||||
|
@ -29,7 +29,7 @@ dependency_overrides:
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
build_runner: ^2.4.7
|
build_runner: ^2.4.15
|
||||||
|
|
||||||
flutter:
|
flutter:
|
||||||
# assets:
|
# assets:
|
||||||
|
|
|
@ -5,6 +5,7 @@ import 'dart:developer';
|
||||||
import 'package:cw_core/crypto_currency.dart';
|
import 'package:cw_core/crypto_currency.dart';
|
||||||
import 'package:cw_core/erc20_token.dart';
|
import 'package:cw_core/erc20_token.dart';
|
||||||
import 'package:cw_core/node.dart';
|
import 'package:cw_core/node.dart';
|
||||||
|
import 'package:cw_core/utils/proxy_wrapper.dart';
|
||||||
import 'package:cw_evm/evm_chain_transaction_model.dart';
|
import 'package:cw_evm/evm_chain_transaction_model.dart';
|
||||||
import 'package:cw_evm/evm_chain_transaction_priority.dart';
|
import 'package:cw_evm/evm_chain_transaction_priority.dart';
|
||||||
import 'package:cw_evm/evm_erc20_balance.dart';
|
import 'package:cw_evm/evm_erc20_balance.dart';
|
||||||
|
@ -12,13 +13,12 @@ import 'package:cw_evm/pending_evm_chain_transaction.dart';
|
||||||
import 'package:cw_evm/.secrets.g.dart' as secrets;
|
import 'package:cw_evm/.secrets.g.dart' as secrets;
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:hex/hex.dart' as hex;
|
import 'package:hex/hex.dart' as hex;
|
||||||
import 'package:http/http.dart';
|
|
||||||
import 'package:web3dart/web3dart.dart';
|
import 'package:web3dart/web3dart.dart';
|
||||||
|
|
||||||
import 'contract/erc20.dart';
|
import 'contract/erc20.dart';
|
||||||
|
|
||||||
abstract class EVMChainClient {
|
abstract class EVMChainClient {
|
||||||
final httpClient = Client();
|
late final client = ProxyWrapper().getHttpIOClient();
|
||||||
Web3Client? _client;
|
Web3Client? _client;
|
||||||
|
|
||||||
//! To be overridden by all child classes
|
//! To be overridden by all child classes
|
||||||
|
@ -47,7 +47,7 @@ abstract class EVMChainClient {
|
||||||
}
|
}
|
||||||
|
|
||||||
_client =
|
_client =
|
||||||
Web3Client(isModifiedNodeUri ? rpcUri!.toString() : node.uri.toString(), httpClient);
|
Web3Client(isModifiedNodeUri ? rpcUri!.toString() : node.uri.toString(), client);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
@ -101,6 +101,7 @@ abstract class EVMChainClient {
|
||||||
String? contractAddress,
|
String? contractAddress,
|
||||||
EtherAmount? gasPrice,
|
EtherAmount? gasPrice,
|
||||||
EtherAmount? maxFeePerGas,
|
EtherAmount? maxFeePerGas,
|
||||||
|
Uint8List? data,
|
||||||
}) async {
|
}) async {
|
||||||
try {
|
try {
|
||||||
if (contractAddress == null) {
|
if (contractAddress == null) {
|
||||||
|
@ -108,7 +109,6 @@ abstract class EVMChainClient {
|
||||||
sender: senderAddress,
|
sender: senderAddress,
|
||||||
to: toAddress,
|
to: toAddress,
|
||||||
value: value,
|
value: value,
|
||||||
// maxFeePerGas: maxFeePerGas,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
return estimatedGas.toInt();
|
return estimatedGas.toInt();
|
||||||
|
@ -124,7 +124,7 @@ abstract class EVMChainClient {
|
||||||
final gasEstimate = await _client!.estimateGas(
|
final gasEstimate = await _client!.estimateGas(
|
||||||
sender: senderAddress,
|
sender: senderAddress,
|
||||||
to: EthereumAddress.fromHex(contractAddress),
|
to: EthereumAddress.fromHex(contractAddress),
|
||||||
data: transfer.encodeCall([
|
data: data ?? transfer.encodeCall([
|
||||||
toAddress,
|
toAddress,
|
||||||
value.getInWei,
|
value.getInWei,
|
||||||
]),
|
]),
|
||||||
|
@ -137,6 +137,21 @@ abstract class EVMChainClient {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Uint8List getEncodedDataForApprovalTransaction({
|
||||||
|
required EthereumAddress toAddress,
|
||||||
|
required EtherAmount value,
|
||||||
|
required EthereumAddress contractAddress,
|
||||||
|
}) {
|
||||||
|
final contract = DeployedContract(ethereumContractAbi, contractAddress);
|
||||||
|
|
||||||
|
final approve = contract.function('approve');
|
||||||
|
|
||||||
|
return approve.encodeCall([
|
||||||
|
toAddress,
|
||||||
|
value.getInWei,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
Future<PendingEVMChainTransaction> signTransaction({
|
Future<PendingEVMChainTransaction> signTransaction({
|
||||||
required Credentials privateKey,
|
required Credentials privateKey,
|
||||||
required String toAddress,
|
required String toAddress,
|
||||||
|
@ -149,6 +164,7 @@ abstract class EVMChainClient {
|
||||||
required int exponent,
|
required int exponent,
|
||||||
String? contractAddress,
|
String? contractAddress,
|
||||||
String? data,
|
String? data,
|
||||||
|
int? gasPrice,
|
||||||
}) async {
|
}) async {
|
||||||
assert(currency == CryptoCurrency.eth ||
|
assert(currency == CryptoCurrency.eth ||
|
||||||
currency == CryptoCurrency.maticpoly ||
|
currency == CryptoCurrency.maticpoly ||
|
||||||
|
@ -164,6 +180,7 @@ abstract class EVMChainClient {
|
||||||
data: data != null ? hexToBytes(data) : null,
|
data: data != null ? hexToBytes(data) : null,
|
||||||
maxGas: estimatedGasUnits,
|
maxGas: estimatedGasUnits,
|
||||||
maxFeePerGas: EtherAmount.fromInt(EtherUnit.wei, maxFeePerGas),
|
maxFeePerGas: EtherAmount.fromInt(EtherUnit.wei, maxFeePerGas),
|
||||||
|
gasPrice: gasPrice != null ? EtherAmount.fromInt(EtherUnit.wei, gasPrice) : null,
|
||||||
);
|
);
|
||||||
|
|
||||||
Uint8List signedTransaction;
|
Uint8List signedTransaction;
|
||||||
|
@ -198,6 +215,52 @@ abstract class EVMChainClient {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<PendingEVMChainTransaction> signApprovalTransaction({
|
||||||
|
required Credentials privateKey,
|
||||||
|
required String spender,
|
||||||
|
required BigInt amount,
|
||||||
|
required BigInt gasFee,
|
||||||
|
required int estimatedGasUnits,
|
||||||
|
required int maxFeePerGas,
|
||||||
|
required EVMChainTransactionPriority priority,
|
||||||
|
required int exponent,
|
||||||
|
required String contractAddress,
|
||||||
|
int? gasPrice,
|
||||||
|
}) async {
|
||||||
|
|
||||||
|
final Transaction transaction = createTransaction(
|
||||||
|
from: privateKey.address,
|
||||||
|
to: EthereumAddress.fromHex(contractAddress),
|
||||||
|
maxPriorityFeePerGas: EtherAmount.fromInt(EtherUnit.gwei, priority.tip),
|
||||||
|
amount: EtherAmount.zero(),
|
||||||
|
maxGas: estimatedGasUnits,
|
||||||
|
maxFeePerGas: EtherAmount.fromInt(EtherUnit.wei, maxFeePerGas),
|
||||||
|
gasPrice: gasPrice != null ? EtherAmount.fromInt(EtherUnit.wei, gasPrice) : null,
|
||||||
|
);
|
||||||
|
|
||||||
|
final erc20 = ERC20(
|
||||||
|
client: _client!,
|
||||||
|
address: EthereumAddress.fromHex(contractAddress),
|
||||||
|
chainId: chainId,
|
||||||
|
);
|
||||||
|
|
||||||
|
final signedTransaction = await erc20.approve(
|
||||||
|
EthereumAddress.fromHex(spender),
|
||||||
|
amount,
|
||||||
|
credentials: privateKey,
|
||||||
|
transaction: transaction,
|
||||||
|
);
|
||||||
|
|
||||||
|
return PendingEVMChainTransaction(
|
||||||
|
signedTransaction: prepareSignedTransactionForSending(signedTransaction),
|
||||||
|
amount: amount.toString(),
|
||||||
|
fee: gasFee,
|
||||||
|
sendTransaction: () => sendTransaction(signedTransaction),
|
||||||
|
exponent: exponent,
|
||||||
|
isInfiniteApproval: amount.toRadixString(16) == 'ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
Transaction createTransaction({
|
Transaction createTransaction({
|
||||||
required EthereumAddress from,
|
required EthereumAddress from,
|
||||||
required EthereumAddress to,
|
required EthereumAddress to,
|
||||||
|
@ -293,7 +356,7 @@ abstract class EVMChainClient {
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
final response = await httpClient.get(
|
final response = await client.get(
|
||||||
uri,
|
uri,
|
||||||
headers: {
|
headers: {
|
||||||
"Accept": "application/json",
|
"Accept": "application/json",
|
||||||
|
|
|
@ -22,3 +22,32 @@ class EVMChainTransactionFeesException implements Exception {
|
||||||
@override
|
@override
|
||||||
String toString() => exceptionMessage;
|
String toString() => exceptionMessage;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class DeuroGasFeeException implements Exception {
|
||||||
|
final String exceptionMessage;
|
||||||
|
final BigInt? requiredGasFee;
|
||||||
|
final BigInt? currentBalance;
|
||||||
|
|
||||||
|
DeuroGasFeeException({
|
||||||
|
this.requiredGasFee,
|
||||||
|
this.currentBalance,
|
||||||
|
}) : exceptionMessage = _buildMessage(requiredGasFee, currentBalance);
|
||||||
|
|
||||||
|
static String _buildMessage(BigInt? requiredGasFee, BigInt? currentBalance) {
|
||||||
|
const baseMessage = 'Insufficient ETH for gas fees.';
|
||||||
|
const addEthMessage = ' Please add ETH to your wallet to cover transaction fees.';
|
||||||
|
|
||||||
|
if (requiredGasFee != null) {
|
||||||
|
final requiredEth = (requiredGasFee / BigInt.from(10).pow(18)).toStringAsFixed(8);
|
||||||
|
final balanceInfo = currentBalance != null
|
||||||
|
? ', Available: ${(currentBalance / BigInt.from(10).pow(18)).toStringAsFixed(8)} ETH'
|
||||||
|
: '';
|
||||||
|
return '$baseMessage Required: ~$requiredEth ETH$balanceInfo.$addEthMessage';
|
||||||
|
}
|
||||||
|
|
||||||
|
return '$baseMessage$addEthMessage';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() => exceptionMessage;
|
||||||
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ import 'dart:async';
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
import 'dart:math';
|
import 'dart:math';
|
||||||
|
import 'dart:typed_data';
|
||||||
|
|
||||||
import 'package:bip32/bip32.dart' as bip32;
|
import 'package:bip32/bip32.dart' as bip32;
|
||||||
import 'package:bip39/bip39.dart' as bip39;
|
import 'package:bip39/bip39.dart' as bip39;
|
||||||
|
@ -197,8 +198,8 @@ abstract class EVMChainWalletBase
|
||||||
for (var token in erc20Currencies) {
|
for (var token in erc20Currencies) {
|
||||||
bool isPotentialScam = false;
|
bool isPotentialScam = false;
|
||||||
|
|
||||||
bool isWhitelisted =
|
bool isWhitelisted = getDefaultTokenContractAddresses
|
||||||
getDefaultTokenContractAddresses.any((element) => element == token.contractAddress);
|
.any((element) => element.toLowerCase() == token.contractAddress.toLowerCase());
|
||||||
|
|
||||||
final tokenSymbol = token.title.toUpperCase();
|
final tokenSymbol = token.title.toUpperCase();
|
||||||
|
|
||||||
|
@ -213,6 +214,16 @@ abstract class EVMChainWalletBase
|
||||||
token.iconPath = null;
|
token.iconPath = null;
|
||||||
await token.save();
|
await token.save();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// For fixing wrongly classified tokens
|
||||||
|
if (!isPotentialScam && token.isPotentialScam) {
|
||||||
|
token.isPotentialScam = false;
|
||||||
|
final iconPath = CryptoCurrency.all
|
||||||
|
.firstWhere((element) => element.title.toUpperCase() == token.symbol.toUpperCase())
|
||||||
|
.iconPath;
|
||||||
|
token.iconPath = iconPath;
|
||||||
|
await token.save();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -255,36 +266,72 @@ abstract class EVMChainWalletBase
|
||||||
required String? contractAddress,
|
required String? contractAddress,
|
||||||
required String receivingAddressHex,
|
required String receivingAddressHex,
|
||||||
required TransactionPriority priority,
|
required TransactionPriority priority,
|
||||||
|
Uint8List? data,
|
||||||
}) async {
|
}) async {
|
||||||
try {
|
try {
|
||||||
if (priority is EVMChainTransactionPriority) {
|
if (priority is EVMChainTransactionPriority) {
|
||||||
final priorityFee = EtherAmount.fromInt(EtherUnit.gwei, priority.tip).getInWei.toInt();
|
final priorityFee = EtherAmount.fromInt(EtherUnit.gwei, priority.tip).getInWei.toInt();
|
||||||
|
|
||||||
int maxFeePerGas;
|
int maxFeePerGas;
|
||||||
|
int adjustedGasPrice;
|
||||||
|
|
||||||
|
bool isPolygon = _client.chainId == 137;
|
||||||
|
|
||||||
if (gasBaseFee != null) {
|
if (gasBaseFee != null) {
|
||||||
// MaxFeePerGas with EIP1559;
|
// MaxFeePerGas with EIP1559;
|
||||||
maxFeePerGas = gasBaseFee! + priorityFee;
|
maxFeePerGas = gasBaseFee! + priorityFee;
|
||||||
} else {
|
} else {
|
||||||
// MaxFeePerGas with gasPrice
|
// MaxFeePerGas with gasPrice
|
||||||
maxFeePerGas = gasPrice;
|
maxFeePerGas = gasPrice + priorityFee;
|
||||||
|
}
|
||||||
|
|
||||||
|
adjustedGasPrice = maxFeePerGas;
|
||||||
|
|
||||||
|
// Polygon has a minimum priority fee of 25 gwei
|
||||||
|
if (isPolygon) {
|
||||||
|
int minPriorityFee = 25;
|
||||||
|
int minPriorityFeeWei =
|
||||||
|
EtherAmount.fromInt(EtherUnit.gwei, minPriorityFee).getInWei.toInt();
|
||||||
|
|
||||||
|
// Calculate user selected priority-based additional fee on top of minimum
|
||||||
|
int additionalPriorityFee = 0;
|
||||||
|
switch (priority) {
|
||||||
|
case EVMChainTransactionPriority.slow:
|
||||||
|
// We use minimum priority fee only
|
||||||
|
additionalPriorityFee = 0;
|
||||||
|
break;
|
||||||
|
case EVMChainTransactionPriority.medium:
|
||||||
|
// We add 15 gwei on top of minimum
|
||||||
|
additionalPriorityFee = EtherAmount.fromInt(EtherUnit.gwei, 15).getInWei.toInt();
|
||||||
|
break;
|
||||||
|
case EVMChainTransactionPriority.fast:
|
||||||
|
// We add 35 gwei on top of minimum
|
||||||
|
additionalPriorityFee = EtherAmount.fromInt(EtherUnit.gwei, 35).getInWei.toInt();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
int totalPriorityFee = minPriorityFeeWei + additionalPriorityFee;
|
||||||
|
adjustedGasPrice = gasPrice + totalPriorityFee;
|
||||||
|
maxFeePerGas = gasPrice + totalPriorityFee;
|
||||||
}
|
}
|
||||||
|
|
||||||
final estimatedGas = await _client.getEstimatedGasUnitsForTransaction(
|
final estimatedGas = await _client.getEstimatedGasUnitsForTransaction(
|
||||||
contractAddress: contractAddress,
|
contractAddress: contractAddress,
|
||||||
senderAddress: _evmChainPrivateKey.address,
|
senderAddress: _evmChainPrivateKey.address,
|
||||||
value: EtherAmount.fromBigInt(EtherUnit.wei, amount!),
|
value: EtherAmount.fromBigInt(EtherUnit.wei, amount!),
|
||||||
gasPrice: EtherAmount.fromInt(EtherUnit.wei, gasPrice),
|
gasPrice: EtherAmount.fromInt(EtherUnit.wei, adjustedGasPrice),
|
||||||
toAddress: EthereumAddress.fromHex(receivingAddressHex),
|
toAddress: EthereumAddress.fromHex(receivingAddressHex),
|
||||||
maxFeePerGas: EtherAmount.fromInt(EtherUnit.wei, maxFeePerGas),
|
maxFeePerGas: EtherAmount.fromInt(EtherUnit.wei, maxFeePerGas),
|
||||||
|
data: data,
|
||||||
);
|
);
|
||||||
|
|
||||||
final totalGasFee = estimatedGas * maxFeePerGas;
|
final totalGasFee = estimatedGas * adjustedGasPrice;
|
||||||
|
|
||||||
return GasParamsHandler(
|
return GasParamsHandler(
|
||||||
estimatedGasUnits: estimatedGas,
|
estimatedGasUnits: estimatedGas,
|
||||||
estimatedGasFee: totalGasFee,
|
estimatedGasFee: totalGasFee,
|
||||||
maxFeePerGas: maxFeePerGas,
|
maxFeePerGas: maxFeePerGas,
|
||||||
gasPrice: gasPrice,
|
gasPrice: adjustedGasPrice,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return GasParamsHandler.zero();
|
return GasParamsHandler.zero();
|
||||||
|
@ -473,11 +520,46 @@ abstract class EVMChainWalletBase
|
||||||
contractAddress:
|
contractAddress:
|
||||||
transactionCurrency is Erc20Token ? transactionCurrency.contractAddress : null,
|
transactionCurrency is Erc20Token ? transactionCurrency.contractAddress : null,
|
||||||
data: hexOpReturnMemo,
|
data: hexOpReturnMemo,
|
||||||
|
gasPrice: maxFeePerGasForTransaction,
|
||||||
);
|
);
|
||||||
|
|
||||||
return pendingEVMChainTransaction;
|
return pendingEVMChainTransaction;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<PendingTransaction> createApprovalTransaction(BigInt amount, String spender,
|
||||||
|
CryptoCurrency token, EVMChainTransactionPriority priority) async {
|
||||||
|
final CryptoCurrency transactionCurrency =
|
||||||
|
balance.keys.firstWhere((element) => element.title == token.title);
|
||||||
|
assert(transactionCurrency is Erc20Token);
|
||||||
|
|
||||||
|
final data = _client.getEncodedDataForApprovalTransaction(
|
||||||
|
contractAddress: EthereumAddress.fromHex((transactionCurrency as Erc20Token).contractAddress),
|
||||||
|
value: EtherAmount.fromBigInt(EtherUnit.wei, amount),
|
||||||
|
toAddress: EthereumAddress.fromHex(spender),
|
||||||
|
);
|
||||||
|
|
||||||
|
final gasFeesModel = await calculateActualEstimatedFeeForCreateTransaction(
|
||||||
|
amount: amount,
|
||||||
|
receivingAddressHex: spender,
|
||||||
|
priority: priority,
|
||||||
|
contractAddress: transactionCurrency.contractAddress,
|
||||||
|
data: data,
|
||||||
|
);
|
||||||
|
|
||||||
|
return _client.signApprovalTransaction(
|
||||||
|
privateKey: _evmChainPrivateKey,
|
||||||
|
spender: spender,
|
||||||
|
amount: amount,
|
||||||
|
priority: priority,
|
||||||
|
gasFee: BigInt.from(gasFeesModel.estimatedGasFee),
|
||||||
|
maxFeePerGas: gasFeesModel.maxFeePerGas,
|
||||||
|
estimatedGasUnits: gasFeesModel.estimatedGasUnits,
|
||||||
|
exponent: transactionCurrency.decimal,
|
||||||
|
contractAddress: transactionCurrency.contractAddress,
|
||||||
|
gasPrice: gasFeesModel.gasPrice,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> _updateTransactions() async {
|
Future<void> _updateTransactions() async {
|
||||||
try {
|
try {
|
||||||
if (_isTransactionUpdating) {
|
if (_isTransactionUpdating) {
|
||||||
|
|
|
@ -11,6 +11,7 @@ class PendingEVMChainTransaction with PendingTransaction {
|
||||||
final BigInt fee;
|
final BigInt fee;
|
||||||
final String amount;
|
final String amount;
|
||||||
final int exponent;
|
final int exponent;
|
||||||
|
final bool isInfiniteApproval;
|
||||||
|
|
||||||
PendingEVMChainTransaction({
|
PendingEVMChainTransaction({
|
||||||
required this.sendTransaction,
|
required this.sendTransaction,
|
||||||
|
@ -18,10 +19,12 @@ class PendingEVMChainTransaction with PendingTransaction {
|
||||||
required this.fee,
|
required this.fee,
|
||||||
required this.amount,
|
required this.amount,
|
||||||
required this.exponent,
|
required this.exponent,
|
||||||
|
this.isInfiniteApproval = false,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get amountFormatted {
|
String get amountFormatted {
|
||||||
|
if (isInfiniteApproval) return "∞";
|
||||||
final _amount = (BigInt.parse(amount) / BigInt.from(pow(10, exponent))).toString();
|
final _amount = (BigInt.parse(amount) / BigInt.from(pow(10, exponent))).toString();
|
||||||
return _amount.substring(0, min(10, _amount.length));
|
return _amount.substring(0, min(10, _amount.length));
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,6 +30,7 @@ dependencies:
|
||||||
git:
|
git:
|
||||||
url: https://github.com/cake-tech/ledger-flutter-plus-plugins
|
url: https://github.com/cake-tech/ledger-flutter-plus-plugins
|
||||||
path: packages/ledger-ethereum
|
path: packages/ledger-ethereum
|
||||||
|
ref: f4761cd5171d4c1e2e42fd3298261650539fb2db
|
||||||
|
|
||||||
dependency_overrides:
|
dependency_overrides:
|
||||||
web3dart:
|
web3dart:
|
||||||
|
@ -41,9 +42,9 @@ dependency_overrides:
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
build_runner: ^2.4.7
|
build_runner: ^2.4.15
|
||||||
mobx_codegen: ^2.0.7
|
mobx_codegen: ^2.0.7
|
||||||
hive_generator: ^1.1.3
|
hive_generator: ^2.0.1
|
||||||
flutter_lints: ^2.0.0
|
flutter_lints: ^2.0.0
|
||||||
|
|
||||||
flutter:
|
flutter:
|
||||||
|
|
|
@ -5,13 +5,11 @@ class PendingTransactionDescription {
|
||||||
required this.fee,
|
required this.fee,
|
||||||
required this.hash,
|
required this.hash,
|
||||||
required this.hex,
|
required this.hex,
|
||||||
required this.txKey,
|
|
||||||
required this.pointerAddress});
|
required this.pointerAddress});
|
||||||
|
|
||||||
final int amount;
|
final int amount;
|
||||||
final int fee;
|
final int fee;
|
||||||
final String hash;
|
final String hash;
|
||||||
final String hex;
|
final String hex;
|
||||||
final String txKey;
|
|
||||||
final int pointerAddress;
|
final int pointerAddress;
|
||||||
}
|
}
|
|
@ -1,3 +1,4 @@
|
||||||
|
import 'dart:async';
|
||||||
import 'dart:ffi';
|
import 'dart:ffi';
|
||||||
import 'dart:isolate';
|
import 'dart:isolate';
|
||||||
|
|
||||||
|
@ -194,14 +195,12 @@ Future<PendingTransactionDescription> createTransactionSync(
|
||||||
final rFee = pendingTx.fee();
|
final rFee = pendingTx.fee();
|
||||||
final rHash = pendingTx.txid('');
|
final rHash = pendingTx.txid('');
|
||||||
final rHex = pendingTx.hex('');
|
final rHex = pendingTx.hex('');
|
||||||
final rTxKey = rHash;
|
|
||||||
|
|
||||||
return PendingTransactionDescription(
|
return PendingTransactionDescription(
|
||||||
amount: rAmt,
|
amount: rAmt,
|
||||||
fee: rFee,
|
fee: rFee,
|
||||||
hash: rHash,
|
hash: rHash,
|
||||||
hex: rHex,
|
hex: rHex,
|
||||||
txKey: rTxKey,
|
|
||||||
pointerAddress: pendingTx.ffiAddress(),
|
pointerAddress: pendingTx.ffiAddress(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -246,7 +245,6 @@ Future<PendingTransactionDescription> createTransactionMultDest(
|
||||||
fee: tx.fee(),
|
fee: tx.fee(),
|
||||||
hash: tx.txid(''),
|
hash: tx.txid(''),
|
||||||
hex: tx.hex(''),
|
hex: tx.hex(''),
|
||||||
txKey: tx.txid(''),
|
|
||||||
pointerAddress: tx.ffiAddress(),
|
pointerAddress: tx.ffiAddress(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -263,6 +261,7 @@ Future<String?> commitTransaction({required Wallet2PendingTransaction tx, requir
|
||||||
filename: '',
|
filename: '',
|
||||||
overwrite: false,
|
overwrite: false,
|
||||||
);
|
);
|
||||||
|
return null;
|
||||||
});
|
});
|
||||||
|
|
||||||
String? error = (() {
|
String? error = (() {
|
||||||
|
@ -285,11 +284,12 @@ Future<String?> commitTransaction({required Wallet2PendingTransaction tx, requir
|
||||||
if (error != null && error != "no tx keys found for this txid") {
|
if (error != null && error != "no tx keys found for this txid") {
|
||||||
throw CreationTransactionException(message: error);
|
throw CreationTransactionException(message: error);
|
||||||
}
|
}
|
||||||
if (useUR) {
|
unawaited(() async {
|
||||||
return Future.value(txCommit as String?);
|
storeSync(force: true);
|
||||||
} else {
|
await Future.delayed(Duration(seconds: 5));
|
||||||
return Future.value(null);
|
storeSync(force: true);
|
||||||
}
|
}());
|
||||||
|
return Future.value(txCommit);
|
||||||
}
|
}
|
||||||
|
|
||||||
class Transaction {
|
class Transaction {
|
||||||
|
|
|
@ -116,12 +116,17 @@ String getSeedLegacy(String? language) {
|
||||||
}
|
}
|
||||||
|
|
||||||
String getPassphrase() {
|
String getPassphrase() {
|
||||||
return currentWallet!.getCacheAttribute(key: "cakewallet.passphrase");
|
return currentWallet?.getCacheAttribute(key: "cakewallet.passphrase") ?? "";
|
||||||
}
|
}
|
||||||
|
|
||||||
Map<int, Map<int, Map<int, String>>> addressCache = {};
|
Map<int, Map<int, Map<int, String>>> addressCache = {};
|
||||||
|
|
||||||
String getAddress({int accountIndex = 0, int addressIndex = 0}) {
|
String getAddress({int accountIndex = 0, int addressIndex = 0}) {
|
||||||
|
// this is a workaround for when we switch the wallet pointer,
|
||||||
|
// it should never reach UI but should be good enough to prevent gray screen
|
||||||
|
// or other errors because of forced null check.
|
||||||
|
if (currentWallet == null) return "<wallet not ready ($accountIndex:$addressIndex)>";
|
||||||
|
|
||||||
// printV("getaddress: ${accountIndex}/${addressIndex}: ${monero.Wallet_numSubaddresses(wptr!, accountIndex: accountIndex)}: ${monero.Wallet_address(wptr!, accountIndex: accountIndex, addressIndex: addressIndex)}");
|
// printV("getaddress: ${accountIndex}/${addressIndex}: ${monero.Wallet_numSubaddresses(wptr!, accountIndex: accountIndex)}: ${monero.Wallet_address(wptr!, accountIndex: accountIndex, addressIndex: addressIndex)}");
|
||||||
// this could be a while loop, but I'm in favor of making it if to not cause freezes
|
// this could be a while loop, but I'm in favor of making it if to not cause freezes
|
||||||
if (currentWallet!.numSubaddresses(accountIndex: accountIndex)-1 < addressIndex) {
|
if (currentWallet!.numSubaddresses(accountIndex: accountIndex)-1 < addressIndex) {
|
||||||
|
@ -146,7 +151,25 @@ int getUnlockedBalance({int accountIndex = 0}) =>
|
||||||
|
|
||||||
int getCurrentHeight() => currentWallet?.blockChainHeight() ?? 0;
|
int getCurrentHeight() => currentWallet?.blockChainHeight() ?? 0;
|
||||||
|
|
||||||
int getNodeHeightSync() => currentWallet?.daemonBlockChainHeight() ?? 0;
|
|
||||||
|
int cachedNodeHeight = 0;
|
||||||
|
bool isHeightRefreshing = false;
|
||||||
|
int getNodeHeightSync() {
|
||||||
|
if (isHeightRefreshing == false) {
|
||||||
|
(() async {
|
||||||
|
try {
|
||||||
|
isHeightRefreshing = true;
|
||||||
|
final wptrAddress = currentWallet!.ffiAddress();
|
||||||
|
cachedNodeHeight = await Isolate.run(() async {
|
||||||
|
return monero.Wallet_daemonBlockChainHeight(Pointer.fromAddress(wptrAddress));
|
||||||
|
});
|
||||||
|
} finally {
|
||||||
|
isHeightRefreshing = false;
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
}
|
||||||
|
return cachedNodeHeight;
|
||||||
|
}
|
||||||
|
|
||||||
bool isConnectedSync() => currentWallet?.connected() != 0;
|
bool isConnectedSync() => currentWallet?.connected() != 0;
|
||||||
|
|
||||||
|
@ -202,7 +225,6 @@ Future<bool> setupNodeSync(
|
||||||
}
|
}
|
||||||
|
|
||||||
void startRefreshSync() {
|
void startRefreshSync() {
|
||||||
currentWallet!.refreshAsync();
|
|
||||||
currentWallet!.startRefresh();
|
currentWallet!.startRefresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -228,7 +250,7 @@ void storeSync({bool force = false}) async {
|
||||||
return monero.Wallet_synchronized(Pointer.fromAddress(addr));
|
return monero.Wallet_synchronized(Pointer.fromAddress(addr));
|
||||||
});
|
});
|
||||||
if (lastStorePointer == addr &&
|
if (lastStorePointer == addr &&
|
||||||
lastStoreHeight + 5000 > currentWallet!.blockChainHeight() &&
|
lastStoreHeight + 75000 > currentWallet!.blockChainHeight() &&
|
||||||
!synchronized &&
|
!synchronized &&
|
||||||
!force) {
|
!force) {
|
||||||
return;
|
return;
|
||||||
|
@ -255,13 +277,13 @@ void closeCurrentWallet() {
|
||||||
currentWallet!.stop();
|
currentWallet!.stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
String getSecretViewKey() => currentWallet!.secretViewKey();
|
String getSecretViewKey() => currentWallet?.secretViewKey() ?? "";
|
||||||
|
|
||||||
String getPublicViewKey() => currentWallet!.publicViewKey();
|
String getPublicViewKey() => currentWallet?.publicViewKey() ?? "";
|
||||||
|
|
||||||
String getSecretSpendKey() => currentWallet!.secretSpendKey();
|
String getSecretSpendKey() => currentWallet?.secretSpendKey() ?? "";
|
||||||
|
|
||||||
String getPublicSpendKey() => currentWallet!.publicSpendKey();
|
String getPublicSpendKey() => currentWallet?.publicSpendKey() ?? "";
|
||||||
|
|
||||||
class SyncListener {
|
class SyncListener {
|
||||||
SyncListener(this.onNewBlock, this.onNewTransaction)
|
SyncListener(this.onNewBlock, this.onNewTransaction)
|
||||||
|
|
|
@ -17,6 +17,7 @@ import 'package:cw_core/pending_transaction.dart';
|
||||||
import 'package:cw_core/sync_status.dart';
|
import 'package:cw_core/sync_status.dart';
|
||||||
import 'package:cw_core/transaction_direction.dart';
|
import 'package:cw_core/transaction_direction.dart';
|
||||||
import 'package:cw_core/unspent_coins_info.dart';
|
import 'package:cw_core/unspent_coins_info.dart';
|
||||||
|
import 'package:cw_core/utils/proxy_wrapper.dart';
|
||||||
import 'package:cw_core/utils/print_verbose.dart';
|
import 'package:cw_core/utils/print_verbose.dart';
|
||||||
import 'package:cw_core/wallet_base.dart';
|
import 'package:cw_core/wallet_base.dart';
|
||||||
import 'package:cw_core/wallet_info.dart';
|
import 'package:cw_core/wallet_info.dart';
|
||||||
|
@ -207,6 +208,14 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> connectToNode({required Node node}) async {
|
Future<void> connectToNode({required Node node}) async {
|
||||||
|
String socksProxy = node.socksProxyAddress ?? '';
|
||||||
|
printV("bootstrapped: ${CakeTor.instance.bootstrapped}");
|
||||||
|
printV(" enabled: ${CakeTor.instance.enabled}");
|
||||||
|
printV(" port: ${CakeTor.instance.port}");
|
||||||
|
printV(" started: ${CakeTor.instance.started}");
|
||||||
|
if (CakeTor.instance.enabled) {
|
||||||
|
socksProxy = "127.0.0.1:${CakeTor.instance.port}";
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
syncStatus = ConnectingSyncStatus();
|
syncStatus = ConnectingSyncStatus();
|
||||||
await monero_wallet.setupNodeSync(
|
await monero_wallet.setupNodeSync(
|
||||||
|
@ -216,7 +225,7 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
|
||||||
useSSL: node.isSSL,
|
useSSL: node.isSSL,
|
||||||
isLightWallet: false,
|
isLightWallet: false,
|
||||||
// FIXME: hardcoded value
|
// FIXME: hardcoded value
|
||||||
socksProxyAddress: node.socksProxyAddress);
|
socksProxyAddress: socksProxy);
|
||||||
|
|
||||||
await monero_wallet.setTrustedDaemon(node.trusted);
|
await monero_wallet.setTrustedDaemon(node.trusted);
|
||||||
syncStatus = ConnectedSyncStatus();
|
syncStatus = ConnectedSyncStatus();
|
||||||
|
|
|
@ -31,8 +31,6 @@ class PendingMoneroTransaction with PendingTransaction {
|
||||||
@override
|
@override
|
||||||
String get hex => pendingTransactionDescription.hex;
|
String get hex => pendingTransactionDescription.hex;
|
||||||
|
|
||||||
String get txKey => pendingTransactionDescription.txKey;
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get amountFormatted => AmountConverter.amountIntToString(
|
String get amountFormatted => AmountConverter.amountIntToString(
|
||||||
CryptoCurrency.xmr, pendingTransactionDescription.amount);
|
CryptoCurrency.xmr, pendingTransactionDescription.amount);
|
||||||
|
|
|
@ -131,18 +131,18 @@ packages:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description:
|
description:
|
||||||
name: build_runner
|
name: build_runner
|
||||||
sha256: "028819cfb90051c6b5440c7e574d1896f8037e3c96cf17aaeb054c9311cfbf4d"
|
sha256: "058fe9dce1de7d69c4b84fada934df3e0153dd000758c4d65964d0166779aa99"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.4.13"
|
version: "2.4.15"
|
||||||
build_runner_core:
|
build_runner_core:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: build_runner_core
|
name: build_runner_core
|
||||||
sha256: "6d6ee4276b1c5f34f21fdf39425202712d2be82019983d52f351c94aafbc2c41"
|
sha256: "22e3aa1c80e0ada3722fe5b63fd43d9c8990759d0a2cf489c8c5d7b2bdebc021"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "7.2.10"
|
version: "8.0.0"
|
||||||
built_collection:
|
built_collection:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -598,7 +598,7 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
path: "."
|
path: "."
|
||||||
ref: cake-update-v4
|
ref: cake-update-v2
|
||||||
resolved-ref: "084fb7bf13ec42d74f26ac08c883ce07c10fca7e"
|
resolved-ref: "084fb7bf13ec42d74f26ac08c883ce07c10fca7e"
|
||||||
url: "https://github.com/cake-tech/on_chain.git"
|
url: "https://github.com/cake-tech/on_chain.git"
|
||||||
source: git
|
source: git
|
||||||
|
@ -779,11 +779,21 @@ packages:
|
||||||
socks5_proxy:
|
socks5_proxy:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: socks5_proxy
|
path: "."
|
||||||
sha256: "616818a0ea1064a4823b53c9f7eaf8da64ed82dcd51ed71371c7e54751ed5053"
|
ref: "27ad7c2efae8d7460325c74b90f660085cbd0685"
|
||||||
url: "https://pub.dev"
|
resolved-ref: "27ad7c2efae8d7460325c74b90f660085cbd0685"
|
||||||
source: hosted
|
url: "https://github.com/LacticWhale/socks_dart"
|
||||||
version: "1.0.6"
|
source: git
|
||||||
|
version: "2.1.0"
|
||||||
|
socks_socket:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
path: "."
|
||||||
|
ref: e6232c53c1595469931ababa878759a067c02e94
|
||||||
|
resolved-ref: e6232c53c1595469931ababa878759a067c02e94
|
||||||
|
url: "https://github.com/sneurlax/socks_socket"
|
||||||
|
source: git
|
||||||
|
version: "1.1.1"
|
||||||
source_gen:
|
source_gen:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -860,10 +870,19 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: timing
|
name: timing
|
||||||
sha256: "62ee18aca144e4a9f29d212f5a4c6a053be252b895ab14b5821996cff4ed90fe"
|
sha256: "70a3b636575d4163c477e6de42f247a23b315ae20e86442bebe32d3cabf61c32"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.2"
|
version: "1.0.1"
|
||||||
|
tor_binary:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
path: "."
|
||||||
|
ref: cb811c610871a9517d47134b87c2f590c15c96c5
|
||||||
|
resolved-ref: cb811c610871a9517d47134b87c2f590c15c96c5
|
||||||
|
url: "https://github.com/MrCyjaneK/flutter-tor_binary"
|
||||||
|
source: git
|
||||||
|
version: "4.7.14"
|
||||||
tuple:
|
tuple:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
|
@ -34,8 +34,8 @@ dependencies:
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
build_runner: ^2.4.7
|
build_runner: ^2.4.15
|
||||||
build_resolvers: ^2.0.9
|
build_resolvers: ^2.4.4
|
||||||
mobx_codegen: ^2.0.7
|
mobx_codegen: ^2.0.7
|
||||||
mockito: ^5.4.5
|
mockito: ^5.4.5
|
||||||
hive_generator: ^2.0.1
|
hive_generator: ^2.0.1
|
||||||
|
|
|
@ -3,11 +3,11 @@ import 'dart:convert';
|
||||||
|
|
||||||
import 'package:cw_core/nano_account_info_response.dart';
|
import 'package:cw_core/nano_account_info_response.dart';
|
||||||
import 'package:cw_core/utils/print_verbose.dart';
|
import 'package:cw_core/utils/print_verbose.dart';
|
||||||
|
import 'package:cw_core/utils/proxy_wrapper.dart';
|
||||||
import 'package:cw_nano/nano_block_info_response.dart';
|
import 'package:cw_nano/nano_block_info_response.dart';
|
||||||
import 'package:cw_core/n2_node.dart';
|
import 'package:cw_core/n2_node.dart';
|
||||||
import 'package:cw_nano/nano_balance.dart';
|
import 'package:cw_nano/nano_balance.dart';
|
||||||
import 'package:cw_nano/nano_transaction_model.dart';
|
import 'package:cw_nano/nano_transaction_model.dart';
|
||||||
import 'package:http/http.dart' as http;
|
|
||||||
import 'package:cw_core/node.dart';
|
import 'package:cw_core/node.dart';
|
||||||
import 'package:nanoutil/nanoutil.dart';
|
import 'package:nanoutil/nanoutil.dart';
|
||||||
import 'package:shared_preferences/shared_preferences.dart';
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
|
@ -66,8 +66,8 @@ class NanoClient {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<NanoBalance> getBalance(String address) async {
|
Future<NanoBalance> getBalance(String address) async {
|
||||||
final response = await http.post(
|
final response = await ProxyWrapper().post(
|
||||||
_node!.uri,
|
clearnetUri: _node!.uri,
|
||||||
headers: getHeaders(_node!.uri.host),
|
headers: getHeaders(_node!.uri.host),
|
||||||
body: jsonEncode(
|
body: jsonEncode(
|
||||||
{
|
{
|
||||||
|
@ -76,7 +76,8 @@ class NanoClient {
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
final data = await jsonDecode(response.body);
|
|
||||||
|
final data = jsonDecode(response.body) as Map<String, dynamic>;
|
||||||
if (response.statusCode != 200 ||
|
if (response.statusCode != 200 ||
|
||||||
data["error"] != null ||
|
data["error"] != null ||
|
||||||
data["balance"] == null ||
|
data["balance"] == null ||
|
||||||
|
@ -93,8 +94,8 @@ class NanoClient {
|
||||||
|
|
||||||
Future<AccountInfoResponse?> getAccountInfo(String address) async {
|
Future<AccountInfoResponse?> getAccountInfo(String address) async {
|
||||||
try {
|
try {
|
||||||
final response = await http.post(
|
final response = await ProxyWrapper().post(
|
||||||
_node!.uri,
|
clearnetUri: _node!.uri,
|
||||||
headers: getHeaders(_node!.uri.host),
|
headers: getHeaders(_node!.uri.host),
|
||||||
body: jsonEncode(
|
body: jsonEncode(
|
||||||
{
|
{
|
||||||
|
@ -104,8 +105,9 @@ class NanoClient {
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
final data = await jsonDecode(response.body);
|
|
||||||
return AccountInfoResponse.fromJson(data as Map<String, dynamic>);
|
final data = jsonDecode(response.body) as Map<String, dynamic>;
|
||||||
|
return AccountInfoResponse.fromJson(data);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
printV("error while getting account info $e");
|
printV("error while getting account info $e");
|
||||||
return null;
|
return null;
|
||||||
|
@ -114,8 +116,8 @@ class NanoClient {
|
||||||
|
|
||||||
Future<BlockContentsResponse?> getBlockContents(String block) async {
|
Future<BlockContentsResponse?> getBlockContents(String block) async {
|
||||||
try {
|
try {
|
||||||
final response = await http.post(
|
final response = await ProxyWrapper().post(
|
||||||
_node!.uri,
|
clearnetUri: _node!.uri,
|
||||||
headers: getHeaders(_node!.uri.host),
|
headers: getHeaders(_node!.uri.host),
|
||||||
body: jsonEncode(
|
body: jsonEncode(
|
||||||
{
|
{
|
||||||
|
@ -125,7 +127,8 @@ class NanoClient {
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
final data = await jsonDecode(response.body);
|
|
||||||
|
final data = jsonDecode(response.body) as Map<String, dynamic>;
|
||||||
return BlockContentsResponse.fromJson(data["contents"] as Map<String, dynamic>);
|
return BlockContentsResponse.fromJson(data["contents"] as Map<String, dynamic>);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
printV("error while getting block info $e");
|
printV("error while getting block info $e");
|
||||||
|
@ -181,8 +184,8 @@ class NanoClient {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<String> requestWork(String hash) async {
|
Future<String> requestWork(String hash) async {
|
||||||
final response = await http.post(
|
final response = await ProxyWrapper().post(
|
||||||
_powNode!.uri,
|
clearnetUri: _powNode!.uri,
|
||||||
headers: getHeaders(_powNode!.uri.host),
|
headers: getHeaders(_powNode!.uri.host),
|
||||||
body: json.encode(
|
body: json.encode(
|
||||||
{
|
{
|
||||||
|
@ -191,8 +194,9 @@ class NanoClient {
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
if (response.statusCode == 200) {
|
if (response.statusCode == 200) {
|
||||||
final Map<String, dynamic> decoded = json.decode(response.body) as Map<String, dynamic>;
|
final decoded = jsonDecode(response.body) as Map<String, dynamic>;
|
||||||
if (decoded.containsKey("error")) {
|
if (decoded.containsKey("error")) {
|
||||||
throw Exception("Received error ${decoded["error"]}");
|
throw Exception("Received error ${decoded["error"]}");
|
||||||
}
|
}
|
||||||
|
@ -224,13 +228,13 @@ class NanoClient {
|
||||||
"block": block,
|
"block": block,
|
||||||
});
|
});
|
||||||
|
|
||||||
final processResponse = await http.post(
|
final processResponse = await ProxyWrapper().post(
|
||||||
_node!.uri,
|
clearnetUri: _node!.uri,
|
||||||
headers: getHeaders(_node!.uri.host),
|
headers: getHeaders(_node!.uri.host),
|
||||||
body: processBody,
|
body: processBody,
|
||||||
);
|
);
|
||||||
|
|
||||||
final Map<String, dynamic> decoded = json.decode(processResponse.body) as Map<String, dynamic>;
|
final Map<String, dynamic> decoded = jsonDecode(processResponse.body) as Map<String, dynamic>;
|
||||||
if (decoded.containsKey("error")) {
|
if (decoded.containsKey("error")) {
|
||||||
throw Exception("Received error ${decoded["error"]}");
|
throw Exception("Received error ${decoded["error"]}");
|
||||||
}
|
}
|
||||||
|
@ -423,12 +427,11 @@ class NanoClient {
|
||||||
"subtype": "receive",
|
"subtype": "receive",
|
||||||
"block": receiveBlock,
|
"block": receiveBlock,
|
||||||
});
|
});
|
||||||
final processResponse = await http.post(
|
final processResponse = await ProxyWrapper().post(
|
||||||
_node!.uri,
|
clearnetUri: _node!.uri,
|
||||||
headers: getHeaders(_node!.uri.host),
|
headers: getHeaders(_node!.uri.host),
|
||||||
body: processBody,
|
body: processBody,
|
||||||
);
|
);
|
||||||
|
|
||||||
final Map<String, dynamic> decoded = json.decode(processResponse.body) as Map<String, dynamic>;
|
final Map<String, dynamic> decoded = json.decode(processResponse.body) as Map<String, dynamic>;
|
||||||
if (decoded.containsKey("error")) {
|
if (decoded.containsKey("error")) {
|
||||||
throw Exception("Received error ${decoded["error"]}");
|
throw Exception("Received error ${decoded["error"]}");
|
||||||
|
@ -440,16 +443,17 @@ class NanoClient {
|
||||||
required String destinationAddress,
|
required String destinationAddress,
|
||||||
required String privateKey,
|
required String privateKey,
|
||||||
}) async {
|
}) async {
|
||||||
final receivableResponse = await http.post(_node!.uri,
|
final receivableResponse = await ProxyWrapper().post(
|
||||||
|
clearnetUri: _node!.uri,
|
||||||
headers: getHeaders(_node!.uri.host),
|
headers: getHeaders(_node!.uri.host),
|
||||||
body: jsonEncode({
|
body: jsonEncode({
|
||||||
"action": "receivable",
|
"action": "receivable",
|
||||||
"account": destinationAddress,
|
"account": destinationAddress,
|
||||||
"count": "-1",
|
"count": "-1",
|
||||||
"source": true,
|
"source": true,
|
||||||
}));
|
}),
|
||||||
|
);
|
||||||
final receivableData = await jsonDecode(receivableResponse.body);
|
final receivableData = jsonDecode(receivableResponse.body) as Map<String, dynamic>;
|
||||||
if (receivableData["blocks"] == "" || receivableData["blocks"] == null) {
|
if (receivableData["blocks"] == "" || receivableData["blocks"] == null) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -492,15 +496,18 @@ class NanoClient {
|
||||||
|
|
||||||
Future<List<NanoTransactionModel>> fetchTransactions(String address) async {
|
Future<List<NanoTransactionModel>> fetchTransactions(String address) async {
|
||||||
try {
|
try {
|
||||||
final response = await http.post(_node!.uri,
|
final response = await ProxyWrapper().post(
|
||||||
|
clearnetUri: _node!.uri,
|
||||||
headers: getHeaders(_node!.uri.host),
|
headers: getHeaders(_node!.uri.host),
|
||||||
body: jsonEncode({
|
body: jsonEncode({
|
||||||
"action": "account_history",
|
"action": "account_history",
|
||||||
"account": address,
|
"account": address,
|
||||||
"count": "100",
|
"count": "100",
|
||||||
// "raw": true,
|
// "raw": true,
|
||||||
}));
|
}),
|
||||||
final data = await jsonDecode(response.body);
|
);
|
||||||
|
|
||||||
|
final data = jsonDecode(response.body) as Map<String, dynamic>;
|
||||||
final transactions = data["history"] is List ? data["history"] as List<dynamic> : [];
|
final transactions = data["history"] is List ? data["history"] as List<dynamic> : [];
|
||||||
|
|
||||||
// Map the transactions list to NanoTransactionModel using the factory
|
// Map the transactions list to NanoTransactionModel using the factory
|
||||||
|
@ -516,13 +523,14 @@ class NanoClient {
|
||||||
|
|
||||||
Future<List<N2Node>> getN2Reps() async {
|
Future<List<N2Node>> getN2Reps() async {
|
||||||
final uri = Uri.parse(N2_REPS_ENDPOINT);
|
final uri = Uri.parse(N2_REPS_ENDPOINT);
|
||||||
final response = await http.post(
|
final response = await ProxyWrapper().post(
|
||||||
uri,
|
clearnetUri: uri,
|
||||||
headers: getHeaders(uri.host),
|
headers: getHeaders(uri.host),
|
||||||
body: jsonEncode({"action": "reps"}),
|
body: jsonEncode({"action": "reps"}),
|
||||||
);
|
);
|
||||||
try {
|
try {
|
||||||
final List<N2Node> nodes = (json.decode(response.body) as List<dynamic>)
|
|
||||||
|
final List<N2Node> nodes = (jsonDecode(response.body) as List<dynamic>)
|
||||||
.map((dynamic e) => N2Node.fromJson(e as Map<String, dynamic>))
|
.map((dynamic e) => N2Node.fromJson(e as Map<String, dynamic>))
|
||||||
.toList();
|
.toList();
|
||||||
return nodes;
|
return nodes;
|
||||||
|
@ -533,8 +541,8 @@ class NanoClient {
|
||||||
|
|
||||||
Future<int> getRepScore(String rep) async {
|
Future<int> getRepScore(String rep) async {
|
||||||
final uri = Uri.parse(N2_REPS_ENDPOINT);
|
final uri = Uri.parse(N2_REPS_ENDPOINT);
|
||||||
final response = await http.post(
|
final response = await ProxyWrapper().post(
|
||||||
uri,
|
clearnetUri: uri,
|
||||||
headers: getHeaders(uri.host),
|
headers: getHeaders(uri.host),
|
||||||
body: jsonEncode({
|
body: jsonEncode({
|
||||||
"action": "rep_info",
|
"action": "rep_info",
|
||||||
|
@ -542,7 +550,8 @@ class NanoClient {
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
try {
|
try {
|
||||||
final N2Node node = N2Node.fromJson(json.decode(response.body) as Map<String, dynamic>);
|
|
||||||
|
final N2Node node = N2Node.fromJson(jsonDecode(response.body) as Map<String, dynamic>);
|
||||||
return node.score ?? 100;
|
return node.score ?? 100;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return 100;
|
return 100;
|
||||||
|
|
|
@ -5,34 +5,39 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: _fe_analyzer_shared
|
name: _fe_analyzer_shared
|
||||||
sha256: "4897882604d919befd350648c7f91926a9d5de99e67b455bf0917cc2362f4bb8"
|
sha256: "16e298750b6d0af7ce8a3ba7c18c69c3785d11b15ec83f6dcd0ad2a0009b3cab"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "47.0.0"
|
version: "76.0.0"
|
||||||
|
_macros:
|
||||||
|
dependency: transitive
|
||||||
|
description: dart
|
||||||
|
source: sdk
|
||||||
|
version: "0.3.3"
|
||||||
analyzer:
|
analyzer:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: analyzer
|
name: analyzer
|
||||||
sha256: "690e335554a8385bc9d787117d9eb52c0c03ee207a607e593de3c9d71b1cfe80"
|
sha256: "1f14db053a8c23e260789e9b0980fa27f2680dd640932cae5e1137cce0e46e1e"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.7.0"
|
version: "6.11.0"
|
||||||
args:
|
args:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: args
|
name: args
|
||||||
sha256: bf9f5caeea8d8fe6721a9c358dd8a5c1947b27f1cfaa18b39c301273594919e6
|
sha256: d0481093c50b1da8910eb0bb301626d4d8eb7284aa739614d2b394ee09e3ea04
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.6.0"
|
version: "2.7.0"
|
||||||
asn1lib:
|
asn1lib:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: asn1lib
|
name: asn1lib
|
||||||
sha256: "4bae5ae63e6d6dd17c4aac8086f3dec26c0236f6a0f03416c6c19d830c367cf5"
|
sha256: "1c296cd268f486cabcc3930e9b93a8133169305f18d722916e675959a88f6d2c"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.5.8"
|
version: "1.5.9"
|
||||||
async:
|
async:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -61,11 +66,11 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
path: "."
|
path: "."
|
||||||
ref: cake-update-v4
|
ref: cake-update-v2
|
||||||
resolved-ref: "437dadd0bd9bf73ec6a551299577799341f6486a"
|
resolved-ref: "59fdf29d72068e0522a96a8953ed7272833a9f57"
|
||||||
url: "https://github.com/cake-tech/blockchain_utils"
|
url: "https://github.com/cake-tech/blockchain_utils"
|
||||||
source: git
|
source: git
|
||||||
version: "4.3.0"
|
version: "3.3.0"
|
||||||
boolean_selector:
|
boolean_selector:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -86,50 +91,50 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: build
|
name: build
|
||||||
sha256: "80184af8b6cb3e5c1c4ec6d8544d27711700bc3e6d2efad04238c7b5290889f0"
|
sha256: cef23f1eda9b57566c81e2133d196f8e3df48f244b317368d65c5943d91148f0
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.4.1"
|
version: "2.4.2"
|
||||||
build_config:
|
build_config:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: build_config
|
name: build_config
|
||||||
sha256: bf80fcfb46a29945b423bd9aad884590fb1dc69b330a4d4700cac476af1708d1
|
sha256: "4ae2de3e1e67ea270081eaee972e1bd8f027d459f249e0f1186730784c2e7e33"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.1.1"
|
version: "1.1.2"
|
||||||
build_daemon:
|
build_daemon:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: build_daemon
|
name: build_daemon
|
||||||
sha256: "79b2aef6ac2ed00046867ed354c88778c9c0f029df8a20fe10b5436826721ef9"
|
sha256: "8e928697a82be082206edb0b9c99c5a4ad6bc31c9e9b8b2f291ae65cd4a25daa"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.0.2"
|
version: "4.0.4"
|
||||||
build_resolvers:
|
build_resolvers:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: build_resolvers
|
name: build_resolvers
|
||||||
sha256: "687cf90a3951affac1bd5f9ecb5e3e90b60487f3d9cdc359bb310f8876bb02a6"
|
sha256: b9e4fda21d846e192628e7a4f6deda6888c36b5b69ba02ff291a01fd529140f0
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.10"
|
version: "2.4.4"
|
||||||
build_runner:
|
build_runner:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description:
|
description:
|
||||||
name: build_runner
|
name: build_runner
|
||||||
sha256: "028819cfb90051c6b5440c7e574d1896f8037e3c96cf17aaeb054c9311cfbf4d"
|
sha256: "058fe9dce1de7d69c4b84fada934df3e0153dd000758c4d65964d0166779aa99"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.4.13"
|
version: "2.4.15"
|
||||||
build_runner_core:
|
build_runner_core:
|
||||||
dependency: "direct overridden"
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: build_runner_core
|
name: build_runner_core
|
||||||
sha256: "0671ad4162ed510b70d0eb4ad6354c249f8429cab4ae7a4cec86bbc2886eb76e"
|
sha256: "22e3aa1c80e0ada3722fe5b63fd43d9c8990759d0a2cf489c8c5d7b2bdebc021"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "7.2.7+1"
|
version: "8.0.0"
|
||||||
built_collection:
|
built_collection:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -142,10 +147,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: built_value
|
name: built_value
|
||||||
sha256: "8b158ab94ec6913e480dc3f752418348b5ae099eb75868b5f4775f0572999c61"
|
sha256: "082001b5c3dc495d4a42f1d5789990505df20d8547d42507c29050af6933ee27"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "8.9.4"
|
version: "8.10.1"
|
||||||
cake_backup:
|
cake_backup:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -238,10 +243,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: dart_style
|
name: dart_style
|
||||||
sha256: "7a03456c3490394c8e7665890333e91ae8a49be43542b616e414449ac358acd4"
|
sha256: "7306ab8a2359a48d22310ad823521d723acfed60ee1f7e37388e8986853b6820"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.2.4"
|
version: "2.3.8"
|
||||||
decimal:
|
decimal:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
@ -373,18 +378,18 @@ packages:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description:
|
description:
|
||||||
name: hive_generator
|
name: hive_generator
|
||||||
sha256: "81fd20125cb2ce8fd23623d7744ffbaf653aae93706c9bd3bf7019ea0ace3938"
|
sha256: "06cb8f58ace74de61f63500564931f9505368f45f98958bd7a6c35ba24159db4"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.1.3"
|
version: "2.0.1"
|
||||||
http:
|
http:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: http
|
name: http
|
||||||
sha256: fe7ab022b76f3034adc518fb6ea04a82387620e19977665ea18d30a1cf43442f
|
sha256: "2c11f3f94c687ee9bad77c171151672986360b2b001d109814ee7140b2cf261b"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.3.0"
|
version: "1.4.0"
|
||||||
http_multi_server:
|
http_multi_server:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -397,10 +402,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: http_parser
|
name: http_parser
|
||||||
sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b"
|
sha256: "178d74305e7866013777bab2c3d8726205dc5a4dd935297175b19a23a2e66571"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.0.2"
|
version: "4.1.2"
|
||||||
intl:
|
intl:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -473,6 +478,14 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.3.0"
|
version: "1.3.0"
|
||||||
|
macros:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: macros
|
||||||
|
sha256: "1d9e801cd66f7ea3663c45fc708450db1fa57f988142c64289142c9b7ee80656"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.1.3-main.0"
|
||||||
matcher:
|
matcher:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -517,10 +530,10 @@ packages:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description:
|
description:
|
||||||
name: mobx_codegen
|
name: mobx_codegen
|
||||||
sha256: d4beb9cea4b7b014321235f8fdc7c2193ee0fe1d1198e9da7403f8bc85c4407c
|
sha256: e0abbbc651a69550440f6b65c99ec222a1e2a4afd7baec8ba0f3088c7ca582a8
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.3.0"
|
version: "2.7.1"
|
||||||
nanodart:
|
nanodart:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -550,19 +563,19 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
path: "."
|
path: "."
|
||||||
ref: cake-update-v4
|
ref: cake-update-v2
|
||||||
resolved-ref: "084fb7bf13ec42d74f26ac08c883ce07c10fca7e"
|
resolved-ref: "01cbbacbb05d2113aafa8b7c4a2bb766f749d8d8"
|
||||||
url: "https://github.com/cake-tech/on_chain.git"
|
url: "https://github.com/cake-tech/on_chain.git"
|
||||||
source: git
|
source: git
|
||||||
version: "6.2.0"
|
version: "3.7.0"
|
||||||
package_config:
|
package_config:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: package_config
|
name: package_config
|
||||||
sha256: "92d4488434b520a62570293fbd33bb556c7d49230791c1b4bbd973baf6d2dc67"
|
sha256: f096c55ebb7deb7e384101542bfba8c52696c1b56fca2eb62827989ef2353bbc
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.1"
|
version: "2.2.0"
|
||||||
path:
|
path:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -583,10 +596,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: path_provider_android
|
name: path_provider_android
|
||||||
sha256: "4adf4fd5423ec60a29506c76581bc05854c55e3a0b72d35bb28d661c9686edf2"
|
sha256: d0d310befe2c8ab9e7f393288ccbb11b60c019c6b5afc21973eeee4dda2b35e9
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.2.15"
|
version: "2.2.17"
|
||||||
path_provider_foundation:
|
path_provider_foundation:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -663,26 +676,26 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: provider
|
name: provider
|
||||||
sha256: c8a055ee5ce3fd98d6fc872478b03823ffdb448699c6ebdbbc71d59b596fd48c
|
sha256: "4abbd070a04e9ddc287673bf5a030c7ca8b685ff70218720abab8b092f53dd84"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "6.1.2"
|
version: "6.1.5"
|
||||||
pub_semver:
|
pub_semver:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: pub_semver
|
name: pub_semver
|
||||||
sha256: "7b3cfbf654f3edd0c6298ecd5be782ce997ddf0e00531b9464b55245185bbbbd"
|
sha256: "5bfcf68ca79ef689f8990d1160781b4bad40a3bd5e5218ad4076ddb7f4081585"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.5"
|
version: "2.2.0"
|
||||||
pubspec_parse:
|
pubspec_parse:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: pubspec_parse
|
name: pubspec_parse
|
||||||
sha256: "81876843eb50dc2e1e5b151792c9a985c5ed2536914115ed04e9c8528f6647b0"
|
sha256: "0560ba233314abbed0a48a2956f7f022cce7c3e1e73df540277da7544cad4082"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.4.0"
|
version: "1.5.0"
|
||||||
rational:
|
rational:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -695,18 +708,18 @@ packages:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: shared_preferences
|
name: shared_preferences
|
||||||
sha256: "846849e3e9b68f3ef4b60c60cf4b3e02e9321bc7f4d8c4692cf87ffa82fc8a3a"
|
sha256: "6e8bf70b7fef813df4e9a36f658ac46d107db4b4cfe1048b477d4e453a8159f5"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.5.2"
|
version: "2.5.3"
|
||||||
shared_preferences_android:
|
shared_preferences_android:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: shared_preferences_android
|
name: shared_preferences_android
|
||||||
sha256: a768fc8ede5f0c8e6150476e14f38e2417c0864ca36bb4582be8e21925a03c22
|
sha256: "20cbd561f743a342c76c151d6ddb93a9ce6005751e7aa458baad3858bfbfb6ac"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.4.6"
|
version: "2.4.10"
|
||||||
shared_preferences_foundation:
|
shared_preferences_foundation:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -751,18 +764,18 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: shelf
|
name: shelf
|
||||||
sha256: ad29c505aee705f41a4d8963641f91ac4cee3c8fad5947e033390a7bd8180fa4
|
sha256: e7dd780a7ffb623c57850b33f43309312fc863fb6aa3d276a754bb299839ef12
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.4.1"
|
version: "1.4.2"
|
||||||
shelf_web_socket:
|
shelf_web_socket:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: shelf_web_socket
|
name: shelf_web_socket
|
||||||
sha256: cc36c297b52866d203dbf9332263c94becc2fe0ceaa9681d07b6ef9807023b67
|
sha256: "3632775c8e90d6c9712f883e633716432a27758216dfb61bd86a8321c0580925"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.1"
|
version: "3.0.0"
|
||||||
sky_engine:
|
sky_engine:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description: flutter
|
description: flutter
|
||||||
|
@ -771,27 +784,37 @@ packages:
|
||||||
socks5_proxy:
|
socks5_proxy:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: socks5_proxy
|
path: "."
|
||||||
sha256: "616818a0ea1064a4823b53c9f7eaf8da64ed82dcd51ed71371c7e54751ed5053"
|
ref: "27ad7c2efae8d7460325c74b90f660085cbd0685"
|
||||||
url: "https://pub.dev"
|
resolved-ref: "27ad7c2efae8d7460325c74b90f660085cbd0685"
|
||||||
source: hosted
|
url: "https://github.com/LacticWhale/socks_dart"
|
||||||
version: "1.0.6"
|
source: git
|
||||||
|
version: "2.1.0"
|
||||||
|
socks_socket:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
path: "."
|
||||||
|
ref: e6232c53c1595469931ababa878759a067c02e94
|
||||||
|
resolved-ref: e6232c53c1595469931ababa878759a067c02e94
|
||||||
|
url: "https://github.com/sneurlax/socks_socket"
|
||||||
|
source: git
|
||||||
|
version: "1.1.1"
|
||||||
source_gen:
|
source_gen:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: source_gen
|
name: source_gen
|
||||||
sha256: "2d79738b6bbf38a43920e2b8d189e9a3ce6cc201f4b8fc76be5e4fe377b1c38d"
|
sha256: "14658ba5f669685cd3d63701d01b31ea748310f7ab854e471962670abcf57832"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.2.6"
|
version: "1.5.0"
|
||||||
source_helper:
|
source_helper:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: source_helper
|
name: source_helper
|
||||||
sha256: "3b67aade1d52416149c633ba1bb36df44d97c6b51830c2198e934e3fca87ca1f"
|
sha256: "86d247119aedce8e63f4751bd9626fc9613255935558447569ad42f9f5b48b3c"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.3.3"
|
version: "1.3.5"
|
||||||
source_span:
|
source_span:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -852,10 +875,19 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: timing
|
name: timing
|
||||||
sha256: "62ee18aca144e4a9f29d212f5a4c6a053be252b895ab14b5821996cff4ed90fe"
|
sha256: "70a3b636575d4163c477e6de42f247a23b315ae20e86442bebe32d3cabf61c32"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.2"
|
version: "1.0.1"
|
||||||
|
tor_binary:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
path: "."
|
||||||
|
ref: cb811c610871a9517d47134b87c2f590c15c96c5
|
||||||
|
resolved-ref: cb811c610871a9517d47134b87c2f590c15c96c5
|
||||||
|
url: "https://github.com/MrCyjaneK/flutter-tor_binary"
|
||||||
|
source: git
|
||||||
|
version: "4.7.14"
|
||||||
tuple:
|
tuple:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -916,18 +948,18 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: web_socket
|
name: web_socket
|
||||||
sha256: "3c12d96c0c9a4eec095246debcea7b86c0324f22df69893d538fcc6f1b8cce83"
|
sha256: "34d64019aa8e36bf9842ac014bb5d2f5586ca73df5e4d9bf5c936975cae6982c"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.1.6"
|
version: "1.0.1"
|
||||||
web_socket_channel:
|
web_socket_channel:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: web_socket_channel
|
name: web_socket_channel
|
||||||
sha256: "0b8e2457400d8a859b7b2030786835a28a8e80836ef64402abef392ff4f1d0e5"
|
sha256: d645757fb0f4773d602444000a8131ff5d48c9e47adfe9772652dd1a4f2d45c8
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.0.2"
|
version: "3.0.3"
|
||||||
xdg_directories:
|
xdg_directories:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -945,5 +977,5 @@ packages:
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.1.3"
|
version: "3.1.3"
|
||||||
sdks:
|
sdks:
|
||||||
dart: ">=3.5.0 <4.0.0"
|
dart: ">=3.6.0 <4.0.0"
|
||||||
flutter: ">=3.24.0"
|
flutter: ">=3.27.0"
|
||||||
|
|
|
@ -31,13 +31,12 @@ dependencies:
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
build_runner: ^2.4.7
|
build_runner: ^2.4.15
|
||||||
mobx_codegen: ^2.0.7
|
mobx_codegen: ^2.0.7
|
||||||
hive_generator: ^1.1.3
|
hive_generator: ^2.0.1
|
||||||
|
|
||||||
dependency_overrides:
|
dependency_overrides:
|
||||||
watcher: ^1.1.0
|
watcher: ^1.1.0
|
||||||
build_runner_core: 7.2.7+1
|
|
||||||
|
|
||||||
# For information on the generic Dart part of this file, see the
|
# For information on the generic Dart part of this file, see the
|
||||||
# following page: https://dart.dev/tools/pub/pubspec
|
# following page: https://dart.dev/tools/pub/pubspec
|
||||||
|
|
|
@ -84,6 +84,6 @@ class DefaultPolygonErc20Tokens {
|
||||||
.iconPath;
|
.iconPath;
|
||||||
} catch (_) {}
|
} catch (_) {}
|
||||||
|
|
||||||
return Erc20Token.copyWith(token, iconPath, 'POLY');
|
return Erc20Token.copyWith(token, iconPath, 'POL');
|
||||||
}).toList();
|
}).toList();
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,13 +18,20 @@ class PolygonClient extends EVMChainClient {
|
||||||
EtherAmount? gasPrice,
|
EtherAmount? gasPrice,
|
||||||
EtherAmount? maxFeePerGas,
|
EtherAmount? maxFeePerGas,
|
||||||
}) {
|
}) {
|
||||||
|
EtherAmount? finalGasPrice = gasPrice;
|
||||||
|
|
||||||
|
if (gasPrice == null && maxFeePerGas != null) {
|
||||||
|
// If we have EIP-1559 parameters but no legacy gasPrice, then use maxFeePerGas as gasPrice
|
||||||
|
finalGasPrice = maxFeePerGas;
|
||||||
|
}
|
||||||
|
|
||||||
return Transaction(
|
return Transaction(
|
||||||
from: from,
|
from: from,
|
||||||
to: to,
|
to: to,
|
||||||
value: amount,
|
value: amount,
|
||||||
// data: data,
|
data: data,
|
||||||
maxGas: maxGas,
|
maxGas: maxGas,
|
||||||
// gasPrice: gasPrice,
|
gasPrice: finalGasPrice,
|
||||||
// maxFeePerGas: maxFeePerGas,
|
// maxFeePerGas: maxFeePerGas,
|
||||||
// maxPriorityFeePerGas: maxPriorityFeePerGas,
|
// maxPriorityFeePerGas: maxPriorityFeePerGas,
|
||||||
);
|
);
|
||||||
|
@ -40,7 +47,7 @@ class PolygonClient extends EVMChainClient {
|
||||||
Future<List<EVMChainTransactionModel>> fetchTransactions(String address,
|
Future<List<EVMChainTransactionModel>> fetchTransactions(String address,
|
||||||
{String? contractAddress}) async {
|
{String? contractAddress}) async {
|
||||||
try {
|
try {
|
||||||
final response = await httpClient.get(Uri.https("api.etherscan.io", "/v2/api", {
|
final response = await client.get(Uri.https("api.etherscan.io", "/v2/api", {
|
||||||
"chainid": "$chainId",
|
"chainid": "$chainId",
|
||||||
"module": "account",
|
"module": "account",
|
||||||
"action": contractAddress != null ? "tokentx" : "txlist",
|
"action": contractAddress != null ? "tokentx" : "txlist",
|
||||||
|
@ -68,7 +75,7 @@ class PolygonClient extends EVMChainClient {
|
||||||
@override
|
@override
|
||||||
Future<List<EVMChainTransactionModel>> fetchInternalTransactions(String address) async {
|
Future<List<EVMChainTransactionModel>> fetchInternalTransactions(String address) async {
|
||||||
try {
|
try {
|
||||||
final response = await httpClient.get(Uri.https("api.etherscan.io", "/v2/api", {
|
final response = await client.get(Uri.https("api.etherscan.io", "/v2/api", {
|
||||||
"chainid": "$chainId",
|
"chainid": "$chainId",
|
||||||
"module": "account",
|
"module": "account",
|
||||||
"action": "txlistinternal",
|
"action": "txlistinternal",
|
||||||
|
|
|
@ -45,7 +45,12 @@ class PolygonWallet extends EVMChainWallet {
|
||||||
final initialErc20Tokens = DefaultPolygonErc20Tokens().initialPolygonErc20Tokens;
|
final initialErc20Tokens = DefaultPolygonErc20Tokens().initialPolygonErc20Tokens;
|
||||||
|
|
||||||
for (final token in initialErc20Tokens) {
|
for (final token in initialErc20Tokens) {
|
||||||
if (!evmChainErc20TokensBox.containsKey(token.contractAddress)) {
|
if (evmChainErc20TokensBox.containsKey(token.contractAddress)) {
|
||||||
|
final existingToken = evmChainErc20TokensBox.get(token.contractAddress);
|
||||||
|
if (existingToken?.tag != token.tag) {
|
||||||
|
evmChainErc20TokensBox.put(token.contractAddress, token);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
if (isMigration) token.enabled = false;
|
if (isMigration) token.enabled = false;
|
||||||
evmChainErc20TokensBox.put(token.contractAddress, token);
|
evmChainErc20TokensBox.put(token.contractAddress, token);
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,7 +34,7 @@ dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
flutter_lints: ^2.0.0
|
flutter_lints: ^2.0.0
|
||||||
build_runner: ^2.4.7
|
build_runner: ^2.4.15
|
||||||
|
|
||||||
|
|
||||||
# For information on the generic Dart part of this file, see the
|
# For information on the generic Dart part of this file, see the
|
||||||
|
|
|
@ -2,8 +2,10 @@ import 'dart:async';
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
import 'dart:math' as math;
|
import 'dart:math' as math;
|
||||||
|
|
||||||
|
import 'package:blockchain_utils/blockchain_utils.dart';
|
||||||
import 'package:cw_core/crypto_currency.dart';
|
import 'package:cw_core/crypto_currency.dart';
|
||||||
import 'package:cw_core/node.dart';
|
import 'package:cw_core/node.dart';
|
||||||
|
import 'package:cw_core/utils/proxy_wrapper.dart';
|
||||||
import 'package:cw_core/solana_rpc_http_service.dart';
|
import 'package:cw_core/solana_rpc_http_service.dart';
|
||||||
import 'package:cw_core/utils/print_verbose.dart';
|
import 'package:cw_core/utils/print_verbose.dart';
|
||||||
import 'package:cw_solana/pending_solana_transaction.dart';
|
import 'package:cw_solana/pending_solana_transaction.dart';
|
||||||
|
@ -11,15 +13,18 @@ import 'package:cw_solana/solana_balance.dart';
|
||||||
import 'package:cw_solana/solana_exceptions.dart';
|
import 'package:cw_solana/solana_exceptions.dart';
|
||||||
import 'package:cw_solana/solana_transaction_model.dart';
|
import 'package:cw_solana/solana_transaction_model.dart';
|
||||||
import 'package:cw_solana/spl_token.dart';
|
import 'package:cw_solana/spl_token.dart';
|
||||||
import 'package:http/http.dart' as http;
|
|
||||||
import 'package:on_chain/solana/solana.dart';
|
import 'package:on_chain/solana/solana.dart';
|
||||||
|
import 'package:on_chain/solana/src/instructions/associated_token_account/constant.dart';
|
||||||
import 'package:on_chain/solana/src/models/pda/pda.dart';
|
import 'package:on_chain/solana/src/models/pda/pda.dart';
|
||||||
import 'package:blockchain_utils/blockchain_utils.dart';
|
import 'package:blockchain_utils/blockchain_utils.dart';
|
||||||
|
import 'package:on_chain/solana/src/rpc/models/models/confirmed_transaction_meta.dart';
|
||||||
import '.secrets.g.dart' as secrets;
|
import '.secrets.g.dart' as secrets;
|
||||||
|
|
||||||
class SolanaWalletClient {
|
class SolanaWalletClient {
|
||||||
final httpClient = http.Client();
|
// Minimum amount in SOL to consider a transaction valid (to filter spam)
|
||||||
SolanaProvider? _provider;
|
static const double minValidAmount = 0.00000003;
|
||||||
|
final httpClient = ProxyWrapper().getHttpClient();
|
||||||
|
SolanaRPC? _provider;
|
||||||
|
|
||||||
bool connect(Node node) {
|
bool connect(Node node) {
|
||||||
try {
|
try {
|
||||||
|
@ -38,7 +43,7 @@ class SolanaWalletClient {
|
||||||
formattedUrl = '$protocolUsed://${node.uriRaw}';
|
formattedUrl = '$protocolUsed://${node.uriRaw}';
|
||||||
}
|
}
|
||||||
|
|
||||||
_provider = SolanaProvider(SolanaRPCHTTPService(url: formattedUrl));
|
_provider = SolanaRPC(SolanaRPCHTTPService(url: formattedUrl));
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
@ -49,7 +54,7 @@ class SolanaWalletClient {
|
||||||
Future<double> getBalance(String walletAddress) async {
|
Future<double> getBalance(String walletAddress) async {
|
||||||
try {
|
try {
|
||||||
final balance = await _provider!.requestWithContext(
|
final balance = await _provider!.requestWithContext(
|
||||||
SolanaRequestGetBalance(
|
SolanaRPCGetBalance(
|
||||||
account: SolAddress(walletAddress),
|
account: SolAddress(walletAddress),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
@ -68,11 +73,11 @@ class SolanaWalletClient {
|
||||||
String mintAddress, String publicKey) async {
|
String mintAddress, String publicKey) async {
|
||||||
try {
|
try {
|
||||||
final result = await _provider!.request(
|
final result = await _provider!.request(
|
||||||
SolanaRequestGetTokenAccountsByOwner(
|
SolanaRPCGetTokenAccountsByOwner(
|
||||||
account: SolAddress(publicKey),
|
account: SolAddress(publicKey),
|
||||||
mint: SolAddress(mintAddress),
|
mint: SolAddress(mintAddress),
|
||||||
commitment: Commitment.confirmed,
|
commitment: Commitment.confirmed,
|
||||||
encoding: SolanaRequestEncoding.base64,
|
encoding: SolanaRPCEncoding.base64,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -82,7 +87,8 @@ class SolanaWalletClient {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<SolanaBalance?> getSplTokenBalance(String mintAddress, String walletAddress) async {
|
Future<SolanaBalance?> getSplTokenBalance(
|
||||||
|
String mintAddress, String walletAddress) async {
|
||||||
// Fetch the token accounts (a token can have multiple accounts for various uses)
|
// Fetch the token accounts (a token can have multiple accounts for various uses)
|
||||||
final tokenAccounts = await getSPLTokenAccounts(mintAddress, walletAddress);
|
final tokenAccounts = await getSPLTokenAccounts(mintAddress, walletAddress);
|
||||||
|
|
||||||
|
@ -96,7 +102,7 @@ class SolanaWalletClient {
|
||||||
|
|
||||||
for (var tokenAccount in tokenAccounts) {
|
for (var tokenAccount in tokenAccounts) {
|
||||||
final tokenAmountResult = await _provider!.request(
|
final tokenAmountResult = await _provider!.request(
|
||||||
SolanaRequestGetTokenAccountBalance(account: tokenAccount.pubkey),
|
SolanaRPCGetTokenAccountBalance(account: tokenAccount.pubkey),
|
||||||
);
|
);
|
||||||
|
|
||||||
final balance = tokenAmountResult.uiAmountString;
|
final balance = tokenAmountResult.uiAmountString;
|
||||||
|
@ -112,20 +118,22 @@ class SolanaWalletClient {
|
||||||
Future<double> getFeeForMessage(String message, Commitment commitment) async {
|
Future<double> getFeeForMessage(String message, Commitment commitment) async {
|
||||||
try {
|
try {
|
||||||
final feeForMessage = await _provider!.request(
|
final feeForMessage = await _provider!.request(
|
||||||
SolanaRequestGetFeeForMessage(
|
SolanaRPCGetFeeForMessage(
|
||||||
encodedMessage: message,
|
encodedMessage: message,
|
||||||
commitment: commitment,
|
commitment: commitment,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
final fee = (feeForMessage?.toDouble() ?? 0.0) / SolanaUtils.lamportsPerSol;
|
final fee =
|
||||||
|
(feeForMessage?.toDouble() ?? 0.0) / SolanaUtils.lamportsPerSol;
|
||||||
return fee;
|
return fee;
|
||||||
} catch (_) {
|
} catch (_) {
|
||||||
return 0.0;
|
return 0.0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<double> getEstimatedFee(SolanaPublicKey publicKey, Commitment commitment) async {
|
Future<double> getEstimatedFee(
|
||||||
|
SolanaPublicKey publicKey, Commitment commitment) async {
|
||||||
final message = await _getMessageForNativeTransaction(
|
final message = await _getMessageForNativeTransaction(
|
||||||
publicKey: publicKey,
|
publicKey: publicKey,
|
||||||
destinationAddress: publicKey.toAddress().address,
|
destinationAddress: publicKey.toAddress().address,
|
||||||
|
@ -155,105 +163,170 @@ class SolanaWalletClient {
|
||||||
if (meta == null || transaction == null) return null;
|
if (meta == null || transaction == null) return null;
|
||||||
|
|
||||||
final int fee = meta.fee;
|
final int fee = meta.fee;
|
||||||
|
final feeInSol = fee / SolanaUtils.lamportsPerSol;
|
||||||
|
|
||||||
final message = transaction.message;
|
final message = transaction.message;
|
||||||
final instructions = message.compiledInstructions;
|
final instructions = message.compiledInstructions;
|
||||||
|
|
||||||
String sender = "";
|
|
||||||
String receiver = "";
|
|
||||||
|
|
||||||
String signature = (txResponse.transaction?.signatures.isEmpty ?? true)
|
String signature = (txResponse.transaction?.signatures.isEmpty ?? true)
|
||||||
? ""
|
? ""
|
||||||
: Base58Encoder.encode(txResponse.transaction!.signatures.first);
|
: Base58Encoder.encode(txResponse.transaction!.signatures.first);
|
||||||
|
|
||||||
|
|
||||||
for (final instruction in instructions) {
|
for (final instruction in instructions) {
|
||||||
final programId = message.accountKeys[instruction.programIdIndex];
|
final programId = message.accountKeys[instruction.programIdIndex];
|
||||||
|
|
||||||
if (programId == SystemProgramConst.programId) {
|
if (programId == SystemProgramConst.programId ||
|
||||||
|
programId == ComputeBudgetConst.programId) {
|
||||||
// For native solana transactions
|
// For native solana transactions
|
||||||
|
|
||||||
if (txResponse.version == TransactionType.legacy) {
|
|
||||||
// For legacy transfers, the fee payer (index 0) is the sender.
|
|
||||||
sender = message.accountKeys[0].address;
|
|
||||||
|
|
||||||
final senderPreBalance = meta.preBalances[0];
|
|
||||||
final senderPostBalance = meta.postBalances[0];
|
|
||||||
final feeForTx = fee / SolanaUtils.lamportsPerSol;
|
|
||||||
|
|
||||||
// The loss on the sender's account would include both the transfer amount and the fee.
|
|
||||||
// So we would subtract the fee to calculate the actual amount that was transferred (in lamports).
|
|
||||||
final transferLamports = (senderPreBalance - senderPostBalance) - BigInt.from(fee);
|
|
||||||
|
|
||||||
// Next, we attempt to find the receiver by comparing the balance changes.
|
|
||||||
// (The index 0 is for the sender so we skip it.)
|
|
||||||
bool foundReceiver = false;
|
|
||||||
for (int i = 1; i < meta.preBalances.length; i++) {
|
|
||||||
// The increase in balance on the receiver account should correspond to the transfer amount we calculated earlieer.
|
|
||||||
final pre = meta.preBalances[i];
|
|
||||||
final post = meta.postBalances[i];
|
|
||||||
if ((post - pre) == transferLamports) {
|
|
||||||
receiver = message.accountKeys[i].address;
|
|
||||||
foundReceiver = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!foundReceiver) {
|
|
||||||
// Optionally (and rarely), if no account shows the exact expected change,
|
|
||||||
// we set the receiver address to unknown.
|
|
||||||
receiver = "unknown";
|
|
||||||
}
|
|
||||||
|
|
||||||
final amount = transferLamports / BigInt.from(1e9);
|
|
||||||
|
|
||||||
return SolanaTransactionModel(
|
|
||||||
isOutgoingTx: sender == walletAddress,
|
|
||||||
from: sender,
|
|
||||||
to: receiver,
|
|
||||||
id: signature,
|
|
||||||
amount: amount.abs(),
|
|
||||||
programId: SystemProgramConst.programId.address,
|
|
||||||
tokenSymbol: 'SOL',
|
|
||||||
blockTimeInInt: blockTime?.toInt() ?? 0,
|
|
||||||
fee: feeForTx,
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
if (instruction.accounts.length < 2) continue;
|
if (instruction.accounts.length < 2) continue;
|
||||||
final senderIndex = instruction.accounts[0];
|
|
||||||
final receiverIndex = instruction.accounts[1];
|
|
||||||
|
|
||||||
sender = message.accountKeys[senderIndex].address;
|
// Get the fee payer index based on transaction type
|
||||||
receiver = message.accountKeys[receiverIndex].address;
|
// For legacy transfers, the first account is usually the fee payer
|
||||||
|
// For versioned, the first account in instruction is usually the fee payer
|
||||||
|
final feePayerIndex =
|
||||||
|
txResponse.version == TransactionType.legacy ? 0 : instruction.accounts[0];
|
||||||
|
|
||||||
final feeForTx = fee / SolanaUtils.lamportsPerSol;
|
final transactionModel = await _parseNativeTransaction(
|
||||||
|
message: message,
|
||||||
final preBalances = meta.preBalances;
|
meta: meta,
|
||||||
final postBalances = meta.postBalances;
|
fee: fee,
|
||||||
|
feeInSol: feeInSol,
|
||||||
final amountInString =
|
feePayerIndex: feePayerIndex,
|
||||||
(((preBalances[senderIndex] - postBalances[senderIndex]) / BigInt.from(1e9))
|
walletAddress: walletAddress,
|
||||||
.toDouble() -
|
signature: signature,
|
||||||
feeForTx)
|
blockTime: blockTime,
|
||||||
.toStringAsFixed(6);
|
|
||||||
|
|
||||||
final amount = double.parse(amountInString);
|
|
||||||
|
|
||||||
return SolanaTransactionModel(
|
|
||||||
isOutgoingTx: sender == walletAddress,
|
|
||||||
from: sender,
|
|
||||||
to: receiver,
|
|
||||||
id: signature,
|
|
||||||
amount: amount.abs(),
|
|
||||||
programId: SystemProgramConst.programId.address,
|
|
||||||
tokenSymbol: 'SOL',
|
|
||||||
blockTimeInInt: blockTime?.toInt() ?? 0,
|
|
||||||
fee: feeForTx,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (transactionModel != null) {
|
||||||
|
return transactionModel;
|
||||||
}
|
}
|
||||||
} else if (programId == SPLTokenProgramConst.tokenProgramId) {
|
} else if (programId == SPLTokenProgramConst.tokenProgramId) {
|
||||||
// For SPL Token transactions
|
// For SPL Token transactions
|
||||||
if (instruction.accounts.length < 2) continue;
|
if (instruction.accounts.length < 2) continue;
|
||||||
|
|
||||||
|
final transactionModel = await _parseSPLTokenTransaction(
|
||||||
|
message: message,
|
||||||
|
meta: meta,
|
||||||
|
fee: fee,
|
||||||
|
feeInSol: feeInSol,
|
||||||
|
instruction: instruction,
|
||||||
|
walletAddress: walletAddress,
|
||||||
|
signature: signature,
|
||||||
|
blockTime: blockTime,
|
||||||
|
splTokenSymbol: splTokenSymbol,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (transactionModel != null) {
|
||||||
|
return transactionModel;
|
||||||
|
}
|
||||||
|
} else if (programId == AssociatedTokenAccountProgramConst.associatedTokenProgramId) {
|
||||||
|
// For ATA program, we need to check if this is a create account transaction
|
||||||
|
// or if it's part of a normal token transfer
|
||||||
|
|
||||||
|
// We skip this transaction if this is the only instruction (this means that it's a create account transaction)
|
||||||
|
if (instructions.length == 1) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We look for a token transfer instruction in the same transaction
|
||||||
|
bool hasTokenTransfer = false;
|
||||||
|
for (final otherInstruction in instructions) {
|
||||||
|
final otherProgramId = message.accountKeys[otherInstruction.programIdIndex];
|
||||||
|
if (otherProgramId == SPLTokenProgramConst.tokenProgramId) {
|
||||||
|
hasTokenTransfer = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If there's no token transfer instruction, it means this is just an ATA creation transaction
|
||||||
|
if (!hasTokenTransfer) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e, s) {
|
||||||
|
printV("Error parsing transaction: $e\n$s");
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<SolanaTransactionModel?> _parseNativeTransaction({
|
||||||
|
required VersionedMessage message,
|
||||||
|
required ConfirmedTransactionMeta meta,
|
||||||
|
required int fee,
|
||||||
|
required double feeInSol,
|
||||||
|
required int feePayerIndex,
|
||||||
|
required String walletAddress,
|
||||||
|
required String signature,
|
||||||
|
required BigInt? blockTime,
|
||||||
|
}) async {
|
||||||
|
// Calculate total balance changes across all accounts
|
||||||
|
BigInt totalBalanceChange = BigInt.zero;
|
||||||
|
String? sender;
|
||||||
|
String? receiver;
|
||||||
|
|
||||||
|
for (int i = 0; i < meta.preBalances.length; i++) {
|
||||||
|
final preBalance = meta.preBalances[i];
|
||||||
|
final postBalance = meta.postBalances[i];
|
||||||
|
final balanceChange = preBalance - postBalance;
|
||||||
|
|
||||||
|
if (balanceChange > BigInt.zero) {
|
||||||
|
// This account sent funds
|
||||||
|
sender = message.accountKeys[i].address;
|
||||||
|
totalBalanceChange += balanceChange;
|
||||||
|
} else if (balanceChange < BigInt.zero) {
|
||||||
|
// This account received funds
|
||||||
|
receiver = message.accountKeys[i].address;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// We subtract the fee from total balance change if the fee payer is the sender
|
||||||
|
if (sender == message.accountKeys[feePayerIndex].address) {
|
||||||
|
totalBalanceChange -= BigInt.from(fee);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sender == null || receiver == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
final amount = totalBalanceChange / BigInt.from(1e9);
|
||||||
|
final amountInSol = amount.abs().toDouble();
|
||||||
|
|
||||||
|
// Skip transactions with very small amounts (likely spam)
|
||||||
|
if (amountInSol < minValidAmount) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return SolanaTransactionModel(
|
||||||
|
isOutgoingTx: sender == walletAddress,
|
||||||
|
from: sender,
|
||||||
|
to: receiver,
|
||||||
|
id: signature,
|
||||||
|
amount: amountInSol,
|
||||||
|
programId: SystemProgramConst.programId.address,
|
||||||
|
tokenSymbol: 'SOL',
|
||||||
|
blockTimeInInt: blockTime?.toInt() ?? 0,
|
||||||
|
fee: feeInSol,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<SolanaTransactionModel?> _parseSPLTokenTransaction({
|
||||||
|
required VersionedMessage message,
|
||||||
|
required ConfirmedTransactionMeta meta,
|
||||||
|
required int fee,
|
||||||
|
required double feeInSol,
|
||||||
|
required CompiledInstruction instruction,
|
||||||
|
required String walletAddress,
|
||||||
|
required String signature,
|
||||||
|
required BigInt? blockTime,
|
||||||
|
String? splTokenSymbol,
|
||||||
|
}) async {
|
||||||
final preBalances = meta.preTokenBalances;
|
final preBalances = meta.preTokenBalances;
|
||||||
final postBalances = meta.postTokenBalances;
|
final postBalances = meta.postTokenBalances;
|
||||||
|
|
||||||
|
@ -317,17 +390,8 @@ class SolanaWalletClient {
|
||||||
programId: SPLTokenProgramConst.tokenProgramId.address,
|
programId: SPLTokenProgramConst.tokenProgramId.address,
|
||||||
blockTimeInInt: blockTime?.toInt() ?? 0,
|
blockTimeInInt: blockTime?.toInt() ?? 0,
|
||||||
tokenSymbol: tokenSymbol ?? '',
|
tokenSymbol: tokenSymbol ?? '',
|
||||||
fee: fee / SolanaUtils.lamportsPerSol,
|
fee: feeInSol,
|
||||||
);
|
);
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (e, s) {
|
|
||||||
printV("Error parsing transaction: $e\n$s");
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Load the Address's transactions into the account
|
/// Load the Address's transactions into the account
|
||||||
|
@ -342,7 +406,7 @@ class SolanaWalletClient {
|
||||||
List<SolanaTransactionModel> transactions = [];
|
List<SolanaTransactionModel> transactions = [];
|
||||||
try {
|
try {
|
||||||
final signatures = await _provider!.request(
|
final signatures = await _provider!.request(
|
||||||
SolanaRequestGetSignaturesForAddress(
|
SolanaRPCGetSignaturesForAddress(
|
||||||
account: address,
|
account: address,
|
||||||
commitment: commitment,
|
commitment: commitment,
|
||||||
),
|
),
|
||||||
|
@ -357,9 +421,9 @@ class SolanaWalletClient {
|
||||||
final batchResponses = await Future.wait(batch.map((signature) async {
|
final batchResponses = await Future.wait(batch.map((signature) async {
|
||||||
try {
|
try {
|
||||||
return await _provider!.request(
|
return await _provider!.request(
|
||||||
SolanaRequestGetTransaction(
|
SolanaRPCGetTransaction(
|
||||||
transactionSignature: signature['signature'],
|
transactionSignature: signature['signature'],
|
||||||
encoding: SolanaRequestEncoding.jsonParsed,
|
encoding: SolanaRPCEncoding.jsonParsed,
|
||||||
maxSupportedTransactionVersion: 0,
|
maxSupportedTransactionVersion: 0,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
@ -369,9 +433,11 @@ class SolanaWalletClient {
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
final versionedBatchResponses = batchResponses.whereType<VersionedTransactionResponse>();
|
final versionedBatchResponses =
|
||||||
|
batchResponses.whereType<VersionedTransactionResponse>();
|
||||||
|
|
||||||
final parsedTransactionsFutures = versionedBatchResponses.map((tx) => parseTransaction(
|
final parsedTransactionsFutures =
|
||||||
|
versionedBatchResponses.map((tx) => parseTransaction(
|
||||||
txResponse: tx,
|
txResponse: tx,
|
||||||
splTokenSymbol: splTokenSymbol,
|
splTokenSymbol: splTokenSymbol,
|
||||||
walletAddress: walletAddress?.address ?? address.address,
|
walletAddress: walletAddress?.address ?? address.address,
|
||||||
|
@ -379,13 +445,16 @@ class SolanaWalletClient {
|
||||||
|
|
||||||
final parsedTransactions = await Future.wait(parsedTransactionsFutures);
|
final parsedTransactions = await Future.wait(parsedTransactionsFutures);
|
||||||
|
|
||||||
transactions.addAll(parsedTransactions.whereType<SolanaTransactionModel>().toList());
|
transactions.addAll(
|
||||||
|
parsedTransactions.whereType<SolanaTransactionModel>().toList());
|
||||||
|
|
||||||
// Calling the callback after each batch is processed, therefore passing the current list of transactions.
|
// Only update UI if we have new valid transactions
|
||||||
|
if (parsedTransactions.isNotEmpty) {
|
||||||
onUpdate(List<SolanaTransactionModel>.from(transactions));
|
onUpdate(List<SolanaTransactionModel>.from(transactions));
|
||||||
|
}
|
||||||
|
|
||||||
if (i + batchSize < signatures.length) {
|
if (i + batchSize < signatures.length) {
|
||||||
await Future.delayed(const Duration(milliseconds: 500));
|
await Future.delayed(const Duration(milliseconds: 300));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -446,8 +515,8 @@ class SolanaWalletClient {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<SPLToken?> fetchSPLTokenInfo(String mintAddress) async {
|
Future<SPLToken?> fetchSPLTokenInfo(String mintAddress) async {
|
||||||
final programAddress =
|
final programAddress = MetaplexTokenMetaDataProgramUtils.findMetadataPda(
|
||||||
MetaplexTokenMetaDataProgramUtils.findMetadataPda(mint: SolAddress(mintAddress));
|
mint: SolAddress(mintAddress));
|
||||||
|
|
||||||
final token = await _provider!.request(
|
final token = await _provider!.request(
|
||||||
SolanaRPCGetMetadataAccount(
|
SolanaRPCGetMetadataAccount(
|
||||||
|
@ -468,8 +537,9 @@ class SolanaWalletClient {
|
||||||
// iconPath = await _client.getIconImageFromTokenUri(metadata.uri);
|
// iconPath = await _client.getIconImageFromTokenUri(metadata.uri);
|
||||||
// } catch (_) {}
|
// } catch (_) {}
|
||||||
|
|
||||||
String filteredTokenSymbol =
|
String filteredTokenSymbol = metadata.symbol
|
||||||
metadata.symbol.replaceFirst(RegExp('^\\\$'), '').replaceAll('\u0000', '');
|
.replaceFirst(RegExp('^\\\$'), '')
|
||||||
|
.replaceAll('\u0000', '');
|
||||||
|
|
||||||
return SPLToken.fromMetadata(
|
return SPLToken.fromMetadata(
|
||||||
name: metadata.name,
|
name: metadata.name,
|
||||||
|
@ -482,7 +552,7 @@ class SolanaWalletClient {
|
||||||
|
|
||||||
void stop() {}
|
void stop() {}
|
||||||
|
|
||||||
SolanaProvider? get getSolanaProvider => _provider;
|
SolanaRPC? get getSolanaProvider => _provider;
|
||||||
|
|
||||||
Future<PendingSolanaTransaction> signSolanaTransaction({
|
Future<PendingSolanaTransaction> signSolanaTransaction({
|
||||||
required String tokenTitle,
|
required String tokenTitle,
|
||||||
|
@ -523,7 +593,7 @@ class SolanaWalletClient {
|
||||||
|
|
||||||
Future<SolAddress> _getLatestBlockhash(Commitment commitment) async {
|
Future<SolAddress> _getLatestBlockhash(Commitment commitment) async {
|
||||||
final latestBlockhash = await _provider!.request(
|
final latestBlockhash = await _provider!.request(
|
||||||
const SolanaRequestGetLatestBlockhash(),
|
const SolanaRPCGetLatestBlockhash(),
|
||||||
);
|
);
|
||||||
|
|
||||||
return latestBlockhash.blockhash;
|
return latestBlockhash.blockhash;
|
||||||
|
@ -585,7 +655,8 @@ class SolanaWalletClient {
|
||||||
return message;
|
return message;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<double> _getFeeFromCompiledMessage(Message message, Commitment commitment) async {
|
Future<double> _getFeeFromCompiledMessage(
|
||||||
|
Message message, Commitment commitment) async {
|
||||||
final base64Message = base64Encode(message.serialize());
|
final base64Message = base64Encode(message.serialize());
|
||||||
|
|
||||||
final fee = await getFeeForMessage(base64Message, commitment);
|
final fee = await getFeeForMessage(base64Message, commitment);
|
||||||
|
@ -599,7 +670,7 @@ class SolanaWalletClient {
|
||||||
required double fee,
|
required double fee,
|
||||||
}) async {
|
}) async {
|
||||||
final rent = await _provider!.request(
|
final rent = await _provider!.request(
|
||||||
SolanaRequestGetMinimumBalanceForRentExemption(
|
SolanaRPCGetMinimumBalanceForRentExemption(
|
||||||
size: SolanaTokenAccountUtils.accountSize,
|
size: SolanaTokenAccountUtils.accountSize,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
@ -724,7 +795,8 @@ class SolanaWalletClient {
|
||||||
required SolAddress mintAddress,
|
required SolAddress mintAddress,
|
||||||
required bool shouldCreateATA,
|
required bool shouldCreateATA,
|
||||||
}) async {
|
}) async {
|
||||||
final associatedTokenAccount = AssociatedTokenAccountProgramUtils.associatedTokenAccount(
|
final associatedTokenAccount =
|
||||||
|
AssociatedTokenAccountProgramUtils.associatedTokenAccount(
|
||||||
mint: mintAddress,
|
mint: mintAddress,
|
||||||
owner: ownerAddress,
|
owner: ownerAddress,
|
||||||
);
|
);
|
||||||
|
@ -732,19 +804,24 @@ class SolanaWalletClient {
|
||||||
SolanaAccountInfo? accountInfo;
|
SolanaAccountInfo? accountInfo;
|
||||||
try {
|
try {
|
||||||
accountInfo = await _provider!.request(
|
accountInfo = await _provider!.request(
|
||||||
SolanaRequestGetAccountInfo(account: associatedTokenAccount.address),
|
SolanaRPCGetAccountInfo(
|
||||||
|
account: associatedTokenAccount.address,
|
||||||
|
commitment: Commitment.confirmed,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
accountInfo = null;
|
accountInfo = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If aacountInfo is null, signifies that the associatedTokenAccount has only been created locally and not been broadcasted to the blockchain.
|
// If account exists, we return the associated token account
|
||||||
if (accountInfo != null) return associatedTokenAccount;
|
if (accountInfo != null) return associatedTokenAccount;
|
||||||
|
|
||||||
if (!shouldCreateATA) return null;
|
if (!shouldCreateATA) return null;
|
||||||
|
|
||||||
|
final payerAddress = payerPrivateKey.publicKey().toAddress();
|
||||||
|
|
||||||
final createAssociatedTokenAccount = AssociatedTokenAccountProgram.associatedTokenAccount(
|
final createAssociatedTokenAccount = AssociatedTokenAccountProgram.associatedTokenAccount(
|
||||||
payer: payerPrivateKey.publicKey().toAddress(),
|
payer: payerAddress,
|
||||||
associatedToken: associatedTokenAccount.address,
|
associatedToken: associatedTokenAccount.address,
|
||||||
owner: ownerAddress,
|
owner: ownerAddress,
|
||||||
mint: mintAddress,
|
mint: mintAddress,
|
||||||
|
@ -753,19 +830,23 @@ class SolanaWalletClient {
|
||||||
final blockhash = await _getLatestBlockhash(Commitment.confirmed);
|
final blockhash = await _getLatestBlockhash(Commitment.confirmed);
|
||||||
|
|
||||||
final transaction = SolanaTransaction(
|
final transaction = SolanaTransaction(
|
||||||
payerKey: payerPrivateKey.publicKey().toAddress(),
|
payerKey: payerAddress,
|
||||||
instructions: [createAssociatedTokenAccount],
|
instructions: [createAssociatedTokenAccount],
|
||||||
recentBlockhash: blockhash,
|
recentBlockhash: blockhash,
|
||||||
|
type: TransactionType.v0,
|
||||||
);
|
);
|
||||||
|
|
||||||
transaction.sign([payerPrivateKey]);
|
final serializedTransaction = await _signTransactionInternal(
|
||||||
|
ownerPrivateKey: payerPrivateKey,
|
||||||
|
transaction: transaction,
|
||||||
|
);
|
||||||
|
|
||||||
await sendTransaction(
|
await sendTransaction(
|
||||||
serializedTransaction: transaction.serializeString(),
|
serializedTransaction: serializedTransaction,
|
||||||
commitment: Commitment.confirmed,
|
commitment: Commitment.confirmed,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Delay for propagation on the blockchain for newly created associated token addresses
|
// Wait for confirmation
|
||||||
await Future.delayed(const Duration(seconds: 2));
|
await Future.delayed(const Duration(seconds: 2));
|
||||||
|
|
||||||
return associatedTokenAccount;
|
return associatedTokenAccount;
|
||||||
|
@ -786,7 +867,8 @@ class SolanaWalletClient {
|
||||||
final amount = (inputAmount * math.pow(10, tokenDecimals)).toInt();
|
final amount = (inputAmount * math.pow(10, tokenDecimals)).toInt();
|
||||||
ProgramDerivedAddress? associatedSenderAccount;
|
ProgramDerivedAddress? associatedSenderAccount;
|
||||||
try {
|
try {
|
||||||
associatedSenderAccount = AssociatedTokenAccountProgramUtils.associatedTokenAccount(
|
associatedSenderAccount =
|
||||||
|
AssociatedTokenAccountProgramUtils.associatedTokenAccount(
|
||||||
mint: mintAddress,
|
mint: mintAddress,
|
||||||
owner: ownerPrivateKey.publicKey().toAddress(),
|
owner: ownerPrivateKey.publicKey().toAddress(),
|
||||||
);
|
);
|
||||||
|
@ -906,7 +988,7 @@ class SolanaWalletClient {
|
||||||
try {
|
try {
|
||||||
/// Send the transaction to the Solana network.
|
/// Send the transaction to the Solana network.
|
||||||
final signature = await _provider!.request(
|
final signature = await _provider!.request(
|
||||||
SolanaRequestSendTransaction(
|
SolanaRPCSendTransaction(
|
||||||
encodedTransaction: serializedTransaction,
|
encodedTransaction: serializedTransaction,
|
||||||
commitment: commitment,
|
commitment: commitment,
|
||||||
),
|
),
|
||||||
|
@ -921,7 +1003,8 @@ class SolanaWalletClient {
|
||||||
if (uri.isEmpty || uri == '…') return null;
|
if (uri.isEmpty || uri == '…') return null;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final response = await httpClient.get(Uri.parse(uri));
|
final client = ProxyWrapper().getHttpIOClient();
|
||||||
|
final response = await client.get(Uri.parse(uri));
|
||||||
|
|
||||||
final jsonResponse = json.decode(response.body) as Map<String, dynamic>;
|
final jsonResponse = json.decode(response.body) as Map<String, dynamic>;
|
||||||
|
|
||||||
|
|
|
@ -611,7 +611,7 @@ abstract class SolanaWalletBase
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
SolanaProvider? get solanaProvider => _client.getSolanaProvider;
|
SolanaRPC? get solanaProvider => _client.getSolanaProvider;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get password => _password;
|
String get password => _password;
|
||||||
|
|
|
@ -23,19 +23,19 @@ dependencies:
|
||||||
on_chain:
|
on_chain:
|
||||||
git:
|
git:
|
||||||
url: https://github.com/cake-tech/on_chain.git
|
url: https://github.com/cake-tech/on_chain.git
|
||||||
ref: cake-update-v4
|
ref: cake-update-v2
|
||||||
blockchain_utils:
|
blockchain_utils:
|
||||||
git:
|
git:
|
||||||
url: https://github.com/cake-tech/blockchain_utils
|
url: https://github.com/cake-tech/blockchain_utils
|
||||||
ref: cake-update-v4
|
ref: cake-update-v2
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
flutter_lints: ^2.0.0
|
flutter_lints: ^2.0.0
|
||||||
build_runner: ^2.4.7
|
build_runner: ^2.4.15
|
||||||
mobx_codegen: ^2.0.7
|
mobx_codegen: ^2.0.7
|
||||||
hive_generator: ^1.1.3
|
hive_generator: ^2.0.1
|
||||||
|
|
||||||
dependency_overrides:
|
dependency_overrides:
|
||||||
watcher: ^1.1.0
|
watcher: ^1.1.0
|
||||||
|
|
|
@ -5,6 +5,7 @@ import 'dart:developer';
|
||||||
import 'package:blockchain_utils/blockchain_utils.dart';
|
import 'package:blockchain_utils/blockchain_utils.dart';
|
||||||
import 'package:cw_core/crypto_currency.dart';
|
import 'package:cw_core/crypto_currency.dart';
|
||||||
import 'package:cw_core/node.dart';
|
import 'package:cw_core/node.dart';
|
||||||
|
import 'package:cw_core/utils/proxy_wrapper.dart';
|
||||||
import 'package:cw_tron/pending_tron_transaction.dart';
|
import 'package:cw_tron/pending_tron_transaction.dart';
|
||||||
import 'package:cw_tron/tron_abi.dart';
|
import 'package:cw_tron/tron_abi.dart';
|
||||||
import 'package:cw_tron/tron_balance.dart';
|
import 'package:cw_tron/tron_balance.dart';
|
||||||
|
@ -13,12 +14,12 @@ import 'package:cw_tron/tron_token.dart';
|
||||||
import 'package:cw_tron/tron_transaction_model.dart';
|
import 'package:cw_tron/tron_transaction_model.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:http/http.dart';
|
|
||||||
import '.secrets.g.dart' as secrets;
|
import '.secrets.g.dart' as secrets;
|
||||||
import 'package:on_chain/on_chain.dart';
|
import 'package:on_chain/on_chain.dart';
|
||||||
|
|
||||||
class TronClient {
|
class TronClient {
|
||||||
final httpClient = Client();
|
late final client = ProxyWrapper().getHttpIOClient();
|
||||||
|
|
||||||
TronProvider? _provider;
|
TronProvider? _provider;
|
||||||
// This is an internal tracker, so we don't have to "refetch".
|
// This is an internal tracker, so we don't have to "refetch".
|
||||||
int _nativeTxEstimatedFee = 0;
|
int _nativeTxEstimatedFee = 0;
|
||||||
|
@ -28,7 +29,7 @@ class TronClient {
|
||||||
Future<List<TronTransactionModel>> fetchTransactions(String address,
|
Future<List<TronTransactionModel>> fetchTransactions(String address,
|
||||||
{String? contractAddress}) async {
|
{String? contractAddress}) async {
|
||||||
try {
|
try {
|
||||||
final response = await httpClient.get(
|
final response = await client.get(
|
||||||
Uri.https(
|
Uri.https(
|
||||||
"api.trongrid.io",
|
"api.trongrid.io",
|
||||||
"/v1/accounts/$address/transactions",
|
"/v1/accounts/$address/transactions",
|
||||||
|
@ -61,7 +62,7 @@ class TronClient {
|
||||||
|
|
||||||
Future<List<TronTRC20TransactionModel>> fetchTrc20ExcludedTransactions(String address) async {
|
Future<List<TronTRC20TransactionModel>> fetchTrc20ExcludedTransactions(String address) async {
|
||||||
try {
|
try {
|
||||||
final response = await httpClient.get(
|
final response = await client.get(
|
||||||
Uri.https(
|
Uri.https(
|
||||||
"api.trongrid.io",
|
"api.trongrid.io",
|
||||||
"/v1/accounts/$address/transactions/trc20",
|
"/v1/accounts/$address/transactions/trc20",
|
||||||
|
@ -235,7 +236,7 @@ class TronClient {
|
||||||
String contractAddress = 'TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t';
|
String contractAddress = 'TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t';
|
||||||
String constantAmount =
|
String constantAmount =
|
||||||
'0'; // We're using 0 as the base amount here as we get an error when balance is zero i.e for new wallets.
|
'0'; // We're using 0 as the base amount here as we get an error when balance is zero i.e for new wallets.
|
||||||
final contract = ContractABI.fromJson(trc20Abi);
|
final contract = ContractABI.fromJson(trc20Abi, isTron: true);
|
||||||
|
|
||||||
final function = contract.functionFromName("transfer");
|
final function = contract.functionFromName("transfer");
|
||||||
|
|
||||||
|
@ -405,7 +406,7 @@ class TronClient {
|
||||||
String contractAddress,
|
String contractAddress,
|
||||||
BigInt tronBalance,
|
BigInt tronBalance,
|
||||||
) async {
|
) async {
|
||||||
final contract = ContractABI.fromJson(trc20Abi);
|
final contract = ContractABI.fromJson(trc20Abi, isTron: true);
|
||||||
|
|
||||||
final function = contract.functionFromName("transfer");
|
final function = contract.functionFromName("transfer");
|
||||||
|
|
||||||
|
@ -483,7 +484,7 @@ class TronClient {
|
||||||
|
|
||||||
final tokenAddress = TronAddress(contractAddress);
|
final tokenAddress = TronAddress(contractAddress);
|
||||||
|
|
||||||
final contract = ContractABI.fromJson(trc20Abi);
|
final contract = ContractABI.fromJson(trc20Abi, isTron: true);
|
||||||
|
|
||||||
final function = contract.functionFromName("balanceOf");
|
final function = contract.functionFromName("balanceOf");
|
||||||
|
|
||||||
|
@ -510,7 +511,7 @@ class TronClient {
|
||||||
|
|
||||||
final ownerAddress = TronAddress(userAddress);
|
final ownerAddress = TronAddress(userAddress);
|
||||||
|
|
||||||
final contract = ContractABI.fromJson(trc20Abi);
|
final contract = ContractABI.fromJson(trc20Abi, isTron: true);
|
||||||
|
|
||||||
final name =
|
final name =
|
||||||
(await getTokenDetail(contract, "name", ownerAddress, tokenAddress) as String?) ?? '';
|
(await getTokenDetail(contract, "name", ownerAddress, tokenAddress) as String?) ?? '';
|
||||||
|
|
|
@ -1,44 +1,46 @@
|
||||||
import 'package:http/http.dart' as http;
|
import 'dart:convert';
|
||||||
import '.secrets.g.dart' as secrets;
|
|
||||||
|
import 'package:cw_core/utils/proxy_wrapper.dart';
|
||||||
import 'package:on_chain/tron/tron.dart';
|
import 'package:on_chain/tron/tron.dart';
|
||||||
|
import '.secrets.g.dart' as secrets;
|
||||||
|
|
||||||
class TronHTTPProvider implements TronServiceProvider {
|
class TronHTTPProvider implements TronServiceProvider {
|
||||||
TronHTTPProvider(
|
TronHTTPProvider(
|
||||||
{required this.url,
|
{required this.url,
|
||||||
http.Client? client,
|
this.defaultRequestTimeout = const Duration(seconds: 30)});
|
||||||
this.defaultRequestTimeout = const Duration(seconds: 30)})
|
|
||||||
: client = client ?? http.Client();
|
|
||||||
|
|
||||||
|
@override
|
||||||
final String url;
|
final String url;
|
||||||
final http.Client client;
|
late final client = ProxyWrapper().getHttpIOClient();
|
||||||
final Duration defaultRequestTimeout;
|
final Duration defaultRequestTimeout;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<TronServiceResponse<T>> doRequest<T>(TronRequestDetails params,
|
Future<Map<String, dynamic>> get(TronRequestDetails params,
|
||||||
{Duration? timeout}) async {
|
[Duration? timeout]) async {
|
||||||
if (!params.type.isPostRequest) {
|
final response = await client.get(Uri.parse(params.url(url)), headers: {
|
||||||
final response = await client.get(
|
|
||||||
params.toUri(url),
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
if (url.contains("trongrid")) 'TRON-PRO-API-KEY': secrets.tronGridApiKey,
|
if (url.contains("trongrid")) 'TRON-PRO-API-KEY': secrets.tronGridApiKey,
|
||||||
if (url.contains("nownodes")) 'api-key': secrets.tronNowNodesApiKey,
|
if (url.contains("nownodes")) 'api-key': secrets.tronNowNodesApiKey,
|
||||||
},
|
}).timeout(timeout ?? defaultRequestTimeout);
|
||||||
).timeout(timeout ?? defaultRequestTimeout);
|
final data = json.decode(response.body) as Map<String, dynamic>;
|
||||||
return params.toResponse(response.bodyBytes, response.statusCode);
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<Map<String, dynamic>> post(TronRequestDetails params,
|
||||||
|
[Duration? timeout]) async {
|
||||||
final response = await client
|
final response = await client
|
||||||
.post(
|
.post(Uri.parse(params.url(url)),
|
||||||
params.toUri(url),
|
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
if (url.contains("trongrid")) 'TRON-PRO-API-KEY': secrets.tronGridApiKey,
|
if (url.contains("trongrid"))
|
||||||
if (url.contains("nownodes")) 'api-key': secrets.tronNowNodesApiKey,
|
'TRON-PRO-API-KEY': secrets.tronGridApiKey,
|
||||||
|
if (url.contains("nownodes"))
|
||||||
|
'api-key': secrets.tronNowNodesApiKey,
|
||||||
},
|
},
|
||||||
body: params.body(),
|
body: params.toRequestBody())
|
||||||
)
|
|
||||||
.timeout(timeout ?? defaultRequestTimeout);
|
.timeout(timeout ?? defaultRequestTimeout);
|
||||||
return params.toResponse(response.bodyBytes, response.statusCode);
|
final data = json.decode(response.body) as Map<String, dynamic>;
|
||||||
|
return data;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import 'package:blockchain_utils/hex/hex.dart';
|
import 'package:blockchain_utils/hex/hex.dart';
|
||||||
import 'package:on_chain/on_chain.dart';
|
import 'package:on_chain/on_chain.dart';
|
||||||
import 'package:on_chain/solidity/address/core.dart';
|
|
||||||
|
|
||||||
class TronTRC20TransactionModel extends TronTransactionModel {
|
class TronTRC20TransactionModel extends TronTransactionModel {
|
||||||
String? transactionId;
|
String? transactionId;
|
||||||
|
@ -189,7 +188,7 @@ class Value {
|
||||||
output = output.replaceFirst('0x', '').substring(8);
|
output = output.replaceFirst('0x', '').substring(8);
|
||||||
final abiCoder = ABICoder.fromType('address');
|
final abiCoder = ABICoder.fromType('address');
|
||||||
final decoded = abiCoder.decode(AbiParameter.bytes, hex.decode(output));
|
final decoded = abiCoder.decode(AbiParameter.bytes, hex.decode(output));
|
||||||
final tronAddress = TronAddress.fromEthAddress((decoded.result as SolidityAddress).toBytes());
|
final tronAddress = TronAddress.fromEthAddress((decoded.result as ETHAddress).toBytes());
|
||||||
|
|
||||||
return tronAddress.toString();
|
return tronAddress.toString();
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,7 +31,7 @@ import 'package:cw_tron/tron_transaction_info.dart';
|
||||||
import 'package:cw_tron/tron_wallet_addresses.dart';
|
import 'package:cw_tron/tron_wallet_addresses.dart';
|
||||||
import 'package:hive/hive.dart';
|
import 'package:hive/hive.dart';
|
||||||
import 'package:mobx/mobx.dart';
|
import 'package:mobx/mobx.dart';
|
||||||
import 'package:on_chain/on_chain.dart' as on_chain;
|
import 'package:on_chain/on_chain.dart';
|
||||||
|
|
||||||
part 'tron_wallet.g.dart';
|
part 'tron_wallet.g.dart';
|
||||||
|
|
||||||
|
@ -74,13 +74,13 @@ abstract class TronWalletBase
|
||||||
|
|
||||||
late final Box<TronToken> tronTokensBox;
|
late final Box<TronToken> tronTokensBox;
|
||||||
|
|
||||||
late final on_chain.TronPrivateKey _tronPrivateKey;
|
late final TronPrivateKey _tronPrivateKey;
|
||||||
|
|
||||||
late final on_chain.TronPublicKey _tronPublicKey;
|
late final TronPublicKey _tronPublicKey;
|
||||||
|
|
||||||
on_chain.TronPublicKey get tronPublicKey => _tronPublicKey;
|
TronPublicKey get tronPublicKey => _tronPublicKey;
|
||||||
|
|
||||||
on_chain.TronPrivateKey get tronPrivateKey => _tronPrivateKey;
|
TronPrivateKey get tronPrivateKey => _tronPrivateKey;
|
||||||
|
|
||||||
late String _tronAddress;
|
late String _tronAddress;
|
||||||
|
|
||||||
|
@ -190,7 +190,7 @@ abstract class TronWalletBase
|
||||||
|
|
||||||
String idFor(String name, WalletType type) => '${walletTypeToString(type).toLowerCase()}_$name';
|
String idFor(String name, WalletType type) => '${walletTypeToString(type).toLowerCase()}_$name';
|
||||||
|
|
||||||
Future<on_chain.TronPrivateKey> getPrivateKey({
|
Future<TronPrivateKey> getPrivateKey({
|
||||||
String? mnemonic,
|
String? mnemonic,
|
||||||
String? privateKey,
|
String? privateKey,
|
||||||
required String password,
|
required String password,
|
||||||
|
@ -198,7 +198,7 @@ abstract class TronWalletBase
|
||||||
}) async {
|
}) async {
|
||||||
assert(mnemonic != null || privateKey != null);
|
assert(mnemonic != null || privateKey != null);
|
||||||
|
|
||||||
if (privateKey != null) return on_chain.TronPrivateKey(privateKey);
|
if (privateKey != null) return TronPrivateKey(privateKey);
|
||||||
|
|
||||||
final seed = bip39.mnemonicToSeed(mnemonic!, passphrase: passphrase ?? '');
|
final seed = bip39.mnemonicToSeed(mnemonic!, passphrase: passphrase ?? '');
|
||||||
|
|
||||||
|
@ -207,7 +207,7 @@ abstract class TronWalletBase
|
||||||
|
|
||||||
final childKey = bip44.deriveDefaultPath;
|
final childKey = bip44.deriveDefaultPath;
|
||||||
|
|
||||||
return on_chain.TronPrivateKey.fromBytes(childKey.privateKey.raw);
|
return TronPrivateKey.fromBytes(childKey.privateKey.raw);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -242,10 +242,10 @@ abstract class TronWalletBase
|
||||||
|
|
||||||
Future<void> _getEstimatedFees() async {
|
Future<void> _getEstimatedFees() async {
|
||||||
final nativeFee = await _getNativeTxFee();
|
final nativeFee = await _getNativeTxFee();
|
||||||
nativeTxEstimatedFee = on_chain.TronHelper.fromSun(BigInt.from(nativeFee));
|
nativeTxEstimatedFee = TronHelper.fromSun(BigInt.from(nativeFee));
|
||||||
|
|
||||||
final trc20Fee = await _getTrc20TxFee();
|
final trc20Fee = await _getTrc20TxFee();
|
||||||
trc20EstimatedFee = on_chain.TronHelper.fromSun(BigInt.from(trc20Fee));
|
trc20EstimatedFee = TronHelper.fromSun(BigInt.from(trc20Fee));
|
||||||
|
|
||||||
log('Native Estimated Fee: $nativeTxEstimatedFee');
|
log('Native Estimated Fee: $nativeTxEstimatedFee');
|
||||||
log('TRC20 Estimated Fee: $trc20EstimatedFee');
|
log('TRC20 Estimated Fee: $trc20EstimatedFee');
|
||||||
|
@ -323,7 +323,7 @@ abstract class TronWalletBase
|
||||||
totalAmount = walletBalanceForCurrency;
|
totalAmount = walletBalanceForCurrency;
|
||||||
} else {
|
} else {
|
||||||
final totalOriginalAmount = double.parse(output.cryptoAmount ?? '0.0');
|
final totalOriginalAmount = double.parse(output.cryptoAmount ?? '0.0');
|
||||||
totalAmount = on_chain.TronHelper.toSun(totalOriginalAmount.toString());
|
totalAmount = TronHelper.toSun(totalOriginalAmount.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (walletBalanceForCurrency < totalAmount || totalAmount < BigInt.zero) {
|
if (walletBalanceForCurrency < totalAmount || totalAmount < BigInt.zero) {
|
||||||
|
@ -338,7 +338,7 @@ abstract class TronWalletBase
|
||||||
toAddress: tronCredentials.outputs.first.isParsedAddress
|
toAddress: tronCredentials.outputs.first.isParsedAddress
|
||||||
? tronCredentials.outputs.first.extractedAddress!
|
? tronCredentials.outputs.first.extractedAddress!
|
||||||
: tronCredentials.outputs.first.address,
|
: tronCredentials.outputs.first.address,
|
||||||
amount: on_chain.TronHelper.fromSun(totalAmount),
|
amount: TronHelper.fromSun(totalAmount),
|
||||||
currency: transactionCurrency,
|
currency: transactionCurrency,
|
||||||
tronBalance: tronBalance,
|
tronBalance: tronBalance,
|
||||||
sendAll: shouldSendAll,
|
sendAll: shouldSendAll,
|
||||||
|
@ -355,9 +355,9 @@ abstract class TronWalletBase
|
||||||
|
|
||||||
final Map<String, TronTransactionInfo> result = {};
|
final Map<String, TronTransactionInfo> result = {};
|
||||||
|
|
||||||
final contract = on_chain.ContractABI.fromJson(trc20Abi);
|
final contract = ContractABI.fromJson(trc20Abi, isTron: true);
|
||||||
|
|
||||||
final ownerAddress = on_chain.TronAddress(_tronAddress);
|
final ownerAddress = TronAddress(_tronAddress);
|
||||||
|
|
||||||
for (var transactionModel in transactions) {
|
for (var transactionModel in transactions) {
|
||||||
if (transactionModel.isError) {
|
if (transactionModel.isError) {
|
||||||
|
@ -371,7 +371,7 @@ abstract class TronWalletBase
|
||||||
|
|
||||||
String? tokenSymbol;
|
String? tokenSymbol;
|
||||||
if (transactionModel.contractAddress != null) {
|
if (transactionModel.contractAddress != null) {
|
||||||
final tokenAddress = on_chain.TronAddress(transactionModel.contractAddress!);
|
final tokenAddress = TronAddress(transactionModel.contractAddress!);
|
||||||
|
|
||||||
tokenSymbol = (await _client.getTokenDetail(
|
tokenSymbol = (await _client.getTokenDetail(
|
||||||
contract,
|
contract,
|
||||||
|
@ -385,8 +385,7 @@ abstract class TronWalletBase
|
||||||
result[transactionModel.hash] = TronTransactionInfo(
|
result[transactionModel.hash] = TronTransactionInfo(
|
||||||
id: transactionModel.hash,
|
id: transactionModel.hash,
|
||||||
tronAmount: transactionModel.amount ?? BigInt.zero,
|
tronAmount: transactionModel.amount ?? BigInt.zero,
|
||||||
direction:
|
direction: TronAddress(transactionModel.from!, visible: false).toAddress() == address
|
||||||
on_chain.TronAddress(transactionModel.from!, visible: false).toAddress() == address
|
|
||||||
? TransactionDirection.outgoing
|
? TransactionDirection.outgoing
|
||||||
: TransactionDirection.incoming,
|
: TransactionDirection.incoming,
|
||||||
blockTime: transactionModel.date,
|
blockTime: transactionModel.date,
|
||||||
|
@ -605,13 +604,11 @@ abstract class TronWalletBase
|
||||||
if (address == null) {
|
if (address == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
on_chain.TronPublicKey pubKey =
|
TronPublicKey pubKey = TronPublicKey.fromPersonalSignature(ascii.encode(message), signature)!;
|
||||||
on_chain.TronPublicKey.fromPersonalSignature(ascii.encode(message), signature)!;
|
|
||||||
return pubKey.toAddress().toString() == address;
|
return pubKey.toAddress().toString() == address;
|
||||||
}
|
}
|
||||||
|
|
||||||
String getTronBase58AddressFromHex(String hexAddress) =>
|
String getTronBase58AddressFromHex(String hexAddress) => TronAddress(hexAddress).toAddress();
|
||||||
on_chain.TronAddress(hexAddress).toAddress();
|
|
||||||
|
|
||||||
void updateScanProviderUsageState(bool isEnabled) {
|
void updateScanProviderUsageState(bool isEnabled) {
|
||||||
if (isEnabled) {
|
if (isEnabled) {
|
||||||
|
|
|
@ -18,11 +18,11 @@ dependencies:
|
||||||
on_chain:
|
on_chain:
|
||||||
git:
|
git:
|
||||||
url: https://github.com/cake-tech/on_chain.git
|
url: https://github.com/cake-tech/on_chain.git
|
||||||
ref: cake-update-v4
|
ref: cake-update-v2
|
||||||
blockchain_utils:
|
blockchain_utils:
|
||||||
git:
|
git:
|
||||||
url: https://github.com/cake-tech/blockchain_utils
|
url: https://github.com/cake-tech/blockchain_utils
|
||||||
ref: cake-update-v4
|
ref: cake-update-v2
|
||||||
mobx: ^2.3.0+1
|
mobx: ^2.3.0+1
|
||||||
bip39: ^1.0.6
|
bip39: ^1.0.6
|
||||||
hive: ^2.2.3
|
hive: ^2.2.3
|
||||||
|
@ -31,9 +31,9 @@ dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
flutter_lints: ^2.0.0
|
flutter_lints: ^2.0.0
|
||||||
build_runner: ^2.3.3
|
build_runner: ^2.4.15
|
||||||
mobx_codegen: ^2.1.1
|
mobx_codegen: ^2.1.1
|
||||||
hive_generator: ^1.1.3
|
hive_generator: ^2.0.1
|
||||||
flutter:
|
flutter:
|
||||||
# assets:
|
# assets:
|
||||||
# - images/a_dot_burr.jpeg
|
# - images/a_dot_burr.jpeg
|
||||||
|
|
|
@ -110,7 +110,16 @@ int getUnlockedBalance({int accountIndex = 0}) =>
|
||||||
|
|
||||||
int getCurrentHeight() => wownero.Wallet_blockChainHeight(wptr!);
|
int getCurrentHeight() => wownero.Wallet_blockChainHeight(wptr!);
|
||||||
|
|
||||||
int getNodeHeightSync() => wownero.Wallet_daemonBlockChainHeight(wptr!);
|
int cachedNodeHeight = 0;
|
||||||
|
int getNodeHeightSync() {
|
||||||
|
(() async {
|
||||||
|
final wptrAddress = wptr!.address;
|
||||||
|
cachedNodeHeight = await Isolate.run(() async {
|
||||||
|
return wownero.Wallet_daemonBlockChainHeight(Pointer.fromAddress(wptrAddress));
|
||||||
|
});
|
||||||
|
})();
|
||||||
|
return cachedNodeHeight;
|
||||||
|
}
|
||||||
|
|
||||||
bool isConnectedSync() => wownero.Wallet_connected(wptr!) != 0;
|
bool isConnectedSync() => wownero.Wallet_connected(wptr!) != 0;
|
||||||
|
|
||||||
|
@ -154,7 +163,7 @@ Future<bool> setupNodeSync(
|
||||||
}
|
}
|
||||||
|
|
||||||
void startRefreshSync() {
|
void startRefreshSync() {
|
||||||
wownero.Wallet_refreshAsync(wptr!);
|
// wownero.Wallet_refreshAsync(wptr!);
|
||||||
wownero.Wallet_startRefresh(wptr!);
|
wownero.Wallet_startRefresh(wptr!);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,6 +16,7 @@ import 'package:cw_core/transaction_direction.dart';
|
||||||
import 'package:cw_core/transaction_priority.dart';
|
import 'package:cw_core/transaction_priority.dart';
|
||||||
import 'package:cw_core/unspent_coins_info.dart';
|
import 'package:cw_core/unspent_coins_info.dart';
|
||||||
import 'package:cw_core/utils/print_verbose.dart';
|
import 'package:cw_core/utils/print_verbose.dart';
|
||||||
|
import 'package:cw_core/utils/proxy_wrapper.dart';
|
||||||
import 'package:cw_core/wallet_base.dart';
|
import 'package:cw_core/wallet_base.dart';
|
||||||
import 'package:cw_core/wallet_info.dart';
|
import 'package:cw_core/wallet_info.dart';
|
||||||
import 'package:cw_core/wownero_amount_format.dart';
|
import 'package:cw_core/wownero_amount_format.dart';
|
||||||
|
@ -183,6 +184,14 @@ abstract class WowneroWalletBase
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> connectToNode({required Node node}) async {
|
Future<void> connectToNode({required Node node}) async {
|
||||||
|
String socksProxy = node.socksProxyAddress ?? '';
|
||||||
|
printV("bootstrapped: ${CakeTor.instance.bootstrapped}");
|
||||||
|
printV(" enabled: ${CakeTor.instance.enabled}");
|
||||||
|
printV(" port: ${CakeTor.instance.port}");
|
||||||
|
printV(" started: ${CakeTor.instance.started}");
|
||||||
|
if (CakeTor.instance.enabled) {
|
||||||
|
socksProxy = "127.0.0.1:${CakeTor.instance.port}";
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
syncStatus = ConnectingSyncStatus();
|
syncStatus = ConnectingSyncStatus();
|
||||||
await wownero_wallet.setupNode(
|
await wownero_wallet.setupNode(
|
||||||
|
@ -192,7 +201,7 @@ abstract class WowneroWalletBase
|
||||||
useSSL: node.isSSL,
|
useSSL: node.isSSL,
|
||||||
isLightWallet: false,
|
isLightWallet: false,
|
||||||
// FIXME: hardcoded value
|
// FIXME: hardcoded value
|
||||||
socksProxyAddress: node.socksProxyAddress);
|
socksProxyAddress: socksProxy);
|
||||||
|
|
||||||
wownero_wallet.setTrustedDaemon(node.trusted);
|
wownero_wallet.setTrustedDaemon(node.trusted);
|
||||||
syncStatus = ConnectedSyncStatus();
|
syncStatus = ConnectedSyncStatus();
|
||||||
|
|
|
@ -5,18 +5,23 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: _fe_analyzer_shared
|
name: _fe_analyzer_shared
|
||||||
sha256: "4897882604d919befd350648c7f91926a9d5de99e67b455bf0917cc2362f4bb8"
|
sha256: "16e298750b6d0af7ce8a3ba7c18c69c3785d11b15ec83f6dcd0ad2a0009b3cab"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "47.0.0"
|
version: "76.0.0"
|
||||||
|
_macros:
|
||||||
|
dependency: transitive
|
||||||
|
description: dart
|
||||||
|
source: sdk
|
||||||
|
version: "0.3.3"
|
||||||
analyzer:
|
analyzer:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: analyzer
|
name: analyzer
|
||||||
sha256: "690e335554a8385bc9d787117d9eb52c0c03ee207a607e593de3c9d71b1cfe80"
|
sha256: "1f14db053a8c23e260789e9b0980fa27f2680dd640932cae5e1137cce0e46e1e"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.7.0"
|
version: "6.11.0"
|
||||||
args:
|
args:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -45,11 +50,11 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
path: "."
|
path: "."
|
||||||
ref: cake-update-v4
|
ref: cake-update-v2
|
||||||
resolved-ref: "437dadd0bd9bf73ec6a551299577799341f6486a"
|
resolved-ref: "59fdf29d72068e0522a96a8953ed7272833a9f57"
|
||||||
url: "https://github.com/cake-tech/blockchain_utils"
|
url: "https://github.com/cake-tech/blockchain_utils"
|
||||||
source: git
|
source: git
|
||||||
version: "4.3.0"
|
version: "3.3.0"
|
||||||
boolean_selector:
|
boolean_selector:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -86,26 +91,26 @@ packages:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description:
|
description:
|
||||||
name: build_resolvers
|
name: build_resolvers
|
||||||
sha256: "687cf90a3951affac1bd5f9ecb5e3e90b60487f3d9cdc359bb310f8876bb02a6"
|
sha256: b9e4fda21d846e192628e7a4f6deda6888c36b5b69ba02ff291a01fd529140f0
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.10"
|
version: "2.4.4"
|
||||||
build_runner:
|
build_runner:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description:
|
description:
|
||||||
name: build_runner
|
name: build_runner
|
||||||
sha256: "028819cfb90051c6b5440c7e574d1896f8037e3c96cf17aaeb054c9311cfbf4d"
|
sha256: "058fe9dce1de7d69c4b84fada934df3e0153dd000758c4d65964d0166779aa99"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.4.13"
|
version: "2.4.15"
|
||||||
build_runner_core:
|
build_runner_core:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: build_runner_core
|
name: build_runner_core
|
||||||
sha256: "6d6ee4276b1c5f34f21fdf39425202712d2be82019983d52f351c94aafbc2c41"
|
sha256: "22e3aa1c80e0ada3722fe5b63fd43d9c8990759d0a2cf489c8c5d7b2bdebc021"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "7.2.10"
|
version: "8.0.0"
|
||||||
built_collection:
|
built_collection:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -214,10 +219,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: dart_style
|
name: dart_style
|
||||||
sha256: "7a03456c3490394c8e7665890333e91ae8a49be43542b616e414449ac358acd4"
|
sha256: "7306ab8a2359a48d22310ad823521d723acfed60ee1f7e37388e8986853b6820"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.2.4"
|
version: "2.3.8"
|
||||||
decimal:
|
decimal:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -336,10 +341,10 @@ packages:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description:
|
description:
|
||||||
name: hive_generator
|
name: hive_generator
|
||||||
sha256: "81fd20125cb2ce8fd23623d7744ffbaf653aae93706c9bd3bf7019ea0ace3938"
|
sha256: "06cb8f58ace74de61f63500564931f9505368f45f98958bd7a6c35ba24159db4"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.1.3"
|
version: "2.0.1"
|
||||||
http:
|
http:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
@ -428,6 +433,14 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.3.0"
|
version: "1.3.0"
|
||||||
|
macros:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: macros
|
||||||
|
sha256: "1d9e801cd66f7ea3663c45fc708450db1fa57f988142c64289142c9b7ee80656"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.1.3-main.0"
|
||||||
matcher:
|
matcher:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -472,10 +485,10 @@ packages:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description:
|
description:
|
||||||
name: mobx_codegen
|
name: mobx_codegen
|
||||||
sha256: d4beb9cea4b7b014321235f8fdc7c2193ee0fe1d1198e9da7403f8bc85c4407c
|
sha256: e0abbbc651a69550440f6b65c99ec222a1e2a4afd7baec8ba0f3088c7ca582a8
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.3.0"
|
version: "2.7.1"
|
||||||
monero:
|
monero:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
@ -505,11 +518,11 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
path: "."
|
path: "."
|
||||||
ref: cake-update-v4
|
ref: cake-update-v2
|
||||||
resolved-ref: "084fb7bf13ec42d74f26ac08c883ce07c10fca7e"
|
resolved-ref: "93440dc5126369b873ca1fccc13c3c1240b1c5c2"
|
||||||
url: "https://github.com/cake-tech/on_chain.git"
|
url: "https://github.com/cake-tech/on_chain.git"
|
||||||
source: git
|
source: git
|
||||||
version: "6.2.0"
|
version: "3.7.0"
|
||||||
package_config:
|
package_config:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -670,27 +683,37 @@ packages:
|
||||||
socks5_proxy:
|
socks5_proxy:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: socks5_proxy
|
path: "."
|
||||||
sha256: "616818a0ea1064a4823b53c9f7eaf8da64ed82dcd51ed71371c7e54751ed5053"
|
ref: "27ad7c2efae8d7460325c74b90f660085cbd0685"
|
||||||
url: "https://pub.dev"
|
resolved-ref: "27ad7c2efae8d7460325c74b90f660085cbd0685"
|
||||||
source: hosted
|
url: "https://github.com/LacticWhale/socks_dart"
|
||||||
version: "1.0.6"
|
source: git
|
||||||
|
version: "2.1.0"
|
||||||
|
socks_socket:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
path: "."
|
||||||
|
ref: e6232c53c1595469931ababa878759a067c02e94
|
||||||
|
resolved-ref: e6232c53c1595469931ababa878759a067c02e94
|
||||||
|
url: "https://github.com/sneurlax/socks_socket"
|
||||||
|
source: git
|
||||||
|
version: "1.1.1"
|
||||||
source_gen:
|
source_gen:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: source_gen
|
name: source_gen
|
||||||
sha256: "2d79738b6bbf38a43920e2b8d189e9a3ce6cc201f4b8fc76be5e4fe377b1c38d"
|
sha256: "14658ba5f669685cd3d63701d01b31ea748310f7ab854e471962670abcf57832"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.2.6"
|
version: "1.5.0"
|
||||||
source_helper:
|
source_helper:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: source_helper
|
name: source_helper
|
||||||
sha256: "3b67aade1d52416149c633ba1bb36df44d97c6b51830c2198e934e3fca87ca1f"
|
sha256: "86d247119aedce8e63f4751bd9626fc9613255935558447569ad42f9f5b48b3c"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.3.3"
|
version: "1.3.5"
|
||||||
source_span:
|
source_span:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -751,10 +774,19 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: timing
|
name: timing
|
||||||
sha256: "62ee18aca144e4a9f29d212f5a4c6a053be252b895ab14b5821996cff4ed90fe"
|
sha256: "70a3b636575d4163c477e6de42f247a23b315ae20e86442bebe32d3cabf61c32"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.2"
|
version: "1.0.1"
|
||||||
|
tor_binary:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
path: "."
|
||||||
|
ref: cb811c610871a9517d47134b87c2f590c15c96c5
|
||||||
|
resolved-ref: cb811c610871a9517d47134b87c2f590c15c96c5
|
||||||
|
url: "https://github.com/MrCyjaneK/flutter-tor_binary"
|
||||||
|
source: git
|
||||||
|
version: "4.7.14"
|
||||||
tuple:
|
tuple:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -844,5 +876,5 @@ packages:
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.1.3"
|
version: "3.1.3"
|
||||||
sdks:
|
sdks:
|
||||||
dart: ">=3.5.0 <4.0.0"
|
dart: ">=3.6.0 <4.0.0"
|
||||||
flutter: ">=3.24.0"
|
flutter: ">=3.24.0"
|
||||||
|
|
|
@ -31,10 +31,10 @@ dependencies:
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
build_runner: ^2.4.7
|
build_runner: ^2.4.15
|
||||||
build_resolvers: ^2.0.9
|
build_resolvers: ^2.4.4
|
||||||
mobx_codegen: ^2.0.7
|
mobx_codegen: ^2.0.7
|
||||||
hive_generator: ^1.1.3
|
hive_generator: ^2.0.1
|
||||||
|
|
||||||
dependency_overrides:
|
dependency_overrides:
|
||||||
watcher: ^1.1.0
|
watcher: ^1.1.0
|
||||||
|
|
|
@ -5,18 +5,23 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: _fe_analyzer_shared
|
name: _fe_analyzer_shared
|
||||||
sha256: "4897882604d919befd350648c7f91926a9d5de99e67b455bf0917cc2362f4bb8"
|
sha256: "16e298750b6d0af7ce8a3ba7c18c69c3785d11b15ec83f6dcd0ad2a0009b3cab"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "47.0.0"
|
version: "76.0.0"
|
||||||
|
_macros:
|
||||||
|
dependency: transitive
|
||||||
|
description: dart
|
||||||
|
source: sdk
|
||||||
|
version: "0.3.3"
|
||||||
analyzer:
|
analyzer:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: analyzer
|
name: analyzer
|
||||||
sha256: "690e335554a8385bc9d787117d9eb52c0c03ee207a607e593de3c9d71b1cfe80"
|
sha256: "1f14db053a8c23e260789e9b0980fa27f2680dd640932cae5e1137cce0e46e1e"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.7.0"
|
version: "6.11.0"
|
||||||
args:
|
args:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -45,11 +50,11 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
path: "."
|
path: "."
|
||||||
ref: cake-update-v4
|
ref: cake-update-v2
|
||||||
resolved-ref: "437dadd0bd9bf73ec6a551299577799341f6486a"
|
resolved-ref: "59fdf29d72068e0522a96a8953ed7272833a9f57"
|
||||||
url: "https://github.com/cake-tech/blockchain_utils"
|
url: "https://github.com/cake-tech/blockchain_utils"
|
||||||
source: git
|
source: git
|
||||||
version: "4.3.0"
|
version: "3.3.0"
|
||||||
boolean_selector:
|
boolean_selector:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -86,26 +91,26 @@ packages:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description:
|
description:
|
||||||
name: build_resolvers
|
name: build_resolvers
|
||||||
sha256: "687cf90a3951affac1bd5f9ecb5e3e90b60487f3d9cdc359bb310f8876bb02a6"
|
sha256: b9e4fda21d846e192628e7a4f6deda6888c36b5b69ba02ff291a01fd529140f0
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.10"
|
version: "2.4.4"
|
||||||
build_runner:
|
build_runner:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description:
|
description:
|
||||||
name: build_runner
|
name: build_runner
|
||||||
sha256: "028819cfb90051c6b5440c7e574d1896f8037e3c96cf17aaeb054c9311cfbf4d"
|
sha256: "058fe9dce1de7d69c4b84fada934df3e0153dd000758c4d65964d0166779aa99"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.4.13"
|
version: "2.4.15"
|
||||||
build_runner_core:
|
build_runner_core:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: build_runner_core
|
name: build_runner_core
|
||||||
sha256: "6d6ee4276b1c5f34f21fdf39425202712d2be82019983d52f351c94aafbc2c41"
|
sha256: "22e3aa1c80e0ada3722fe5b63fd43d9c8990759d0a2cf489c8c5d7b2bdebc021"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "7.2.10"
|
version: "8.0.0"
|
||||||
built_collection:
|
built_collection:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -214,10 +219,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: dart_style
|
name: dart_style
|
||||||
sha256: "7a03456c3490394c8e7665890333e91ae8a49be43542b616e414449ac358acd4"
|
sha256: "7306ab8a2359a48d22310ad823521d723acfed60ee1f7e37388e8986853b6820"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.2.4"
|
version: "2.3.8"
|
||||||
decimal:
|
decimal:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
@ -333,10 +338,10 @@ packages:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description:
|
description:
|
||||||
name: hive_generator
|
name: hive_generator
|
||||||
sha256: "81fd20125cb2ce8fd23623d7744ffbaf653aae93706c9bd3bf7019ea0ace3938"
|
sha256: "06cb8f58ace74de61f63500564931f9505368f45f98958bd7a6c35ba24159db4"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.1.3"
|
version: "2.0.1"
|
||||||
http:
|
http:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
@ -433,6 +438,14 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.3.0"
|
version: "1.3.0"
|
||||||
|
macros:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: macros
|
||||||
|
sha256: "1d9e801cd66f7ea3663c45fc708450db1fa57f988142c64289142c9b7ee80656"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.1.3-main.0"
|
||||||
matcher:
|
matcher:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -477,10 +490,10 @@ packages:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description:
|
description:
|
||||||
name: mobx_codegen
|
name: mobx_codegen
|
||||||
sha256: d4beb9cea4b7b014321235f8fdc7c2193ee0fe1d1198e9da7403f8bc85c4407c
|
sha256: e0abbbc651a69550440f6b65c99ec222a1e2a4afd7baec8ba0f3088c7ca582a8
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.3.0"
|
version: "2.7.1"
|
||||||
monero:
|
monero:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
@ -502,11 +515,11 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
path: "."
|
path: "."
|
||||||
ref: cake-update-v4
|
ref: cake-update-v2
|
||||||
resolved-ref: "084fb7bf13ec42d74f26ac08c883ce07c10fca7e"
|
resolved-ref: "93440dc5126369b873ca1fccc13c3c1240b1c5c2"
|
||||||
url: "https://github.com/cake-tech/on_chain.git"
|
url: "https://github.com/cake-tech/on_chain.git"
|
||||||
source: git
|
source: git
|
||||||
version: "6.2.0"
|
version: "3.7.0"
|
||||||
package_config:
|
package_config:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -667,27 +680,37 @@ packages:
|
||||||
socks5_proxy:
|
socks5_proxy:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: socks5_proxy
|
path: "."
|
||||||
sha256: "616818a0ea1064a4823b53c9f7eaf8da64ed82dcd51ed71371c7e54751ed5053"
|
ref: "27ad7c2efae8d7460325c74b90f660085cbd0685"
|
||||||
url: "https://pub.dev"
|
resolved-ref: "27ad7c2efae8d7460325c74b90f660085cbd0685"
|
||||||
source: hosted
|
url: "https://github.com/LacticWhale/socks_dart"
|
||||||
version: "1.0.6"
|
source: git
|
||||||
|
version: "2.1.0"
|
||||||
|
socks_socket:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
path: "."
|
||||||
|
ref: e6232c53c1595469931ababa878759a067c02e94
|
||||||
|
resolved-ref: e6232c53c1595469931ababa878759a067c02e94
|
||||||
|
url: "https://github.com/sneurlax/socks_socket"
|
||||||
|
source: git
|
||||||
|
version: "1.1.1"
|
||||||
source_gen:
|
source_gen:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: source_gen
|
name: source_gen
|
||||||
sha256: "2d79738b6bbf38a43920e2b8d189e9a3ce6cc201f4b8fc76be5e4fe377b1c38d"
|
sha256: "14658ba5f669685cd3d63701d01b31ea748310f7ab854e471962670abcf57832"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.2.6"
|
version: "1.5.0"
|
||||||
source_helper:
|
source_helper:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: source_helper
|
name: source_helper
|
||||||
sha256: "3b67aade1d52416149c633ba1bb36df44d97c6b51830c2198e934e3fca87ca1f"
|
sha256: "86d247119aedce8e63f4751bd9626fc9613255935558447569ad42f9f5b48b3c"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.3.3"
|
version: "1.3.5"
|
||||||
source_span:
|
source_span:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -752,6 +775,15 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.2"
|
version: "1.0.2"
|
||||||
|
tor_binary:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
path: "."
|
||||||
|
ref: cb811c610871a9517d47134b87c2f590c15c96c5
|
||||||
|
resolved-ref: cb811c610871a9517d47134b87c2f590c15c96c5
|
||||||
|
url: "https://github.com/MrCyjaneK/flutter-tor_binary"
|
||||||
|
source: git
|
||||||
|
version: "4.7.14"
|
||||||
tuple:
|
tuple:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -841,5 +873,5 @@ packages:
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.1.3"
|
version: "3.1.3"
|
||||||
sdks:
|
sdks:
|
||||||
dart: ">=3.5.0 <4.0.0"
|
dart: ">=3.6.0 <4.0.0"
|
||||||
flutter: ">=3.24.0"
|
flutter: ">=3.24.0"
|
||||||
|
|
|
@ -30,10 +30,10 @@ dependencies:
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
build_runner: ^2.4.7
|
build_runner: ^2.4.15
|
||||||
mobx_codegen: ^2.1.1
|
mobx_codegen: ^2.1.1
|
||||||
build_resolvers: ^2.0.9
|
build_resolvers: ^2.4.4
|
||||||
hive_generator: ^1.1.3
|
hive_generator: ^2.0.1
|
||||||
|
|
||||||
dependency_overrides:
|
dependency_overrides:
|
||||||
watcher: ^1.1.0
|
watcher: ^1.1.0
|
||||||
|
|
|
@ -18,8 +18,8 @@ In order to build the latest version of Cake Wallet, simply run the following:
|
||||||
git clone --branch main https://github.com/cake-tech/cake_wallet.git
|
git clone --branch main https://github.com/cake-tech/cake_wallet.git
|
||||||
# NOTE: Replace `main` with the latest release tag available at https://github.com/cake-tech/cake_wallet/releases/latest.
|
# NOTE: Replace `main` with the latest release tag available at https://github.com/cake-tech/cake_wallet/releases/latest.
|
||||||
cd cake_wallet
|
cd cake_wallet
|
||||||
# docker build -t ghcr.io/cake-tech/cake_wallet:debian12-flutter3.27.4-go1.24.1 . # Uncomment to build the docker image yourself instead of pulling it from the registry
|
# docker build -t ghcr.io/cake-tech/cake_wallet:debian12-flutter3.27.0-go1.24.1-ruststablenightly . # Uncomment to build the docker image yourself instead of pulling it from the registry
|
||||||
docker run -v$(pwd):$(pwd) -w $(pwd) -i --rm ghcr.io/cake-tech/cake_wallet:debian12-flutter3.27.4-go1.24.1 bash -x << EOF
|
docker run -v$(pwd):$(pwd) -w $(pwd) -i --rm ghcr.io/cake-tech/cake_wallet:debian12-flutter3.27.0-go1.24.1-ruststablenightly bash -x << EOF
|
||||||
set -x -e
|
set -x -e
|
||||||
pushd scripts/android
|
pushd scripts/android
|
||||||
source ./app_env.sh cakewallet
|
source ./app_env.sh cakewallet
|
||||||
|
|
|
@ -7,7 +7,7 @@ The following are the system requirements to build Cake Wallet for your iOS devi
|
||||||
```txt
|
```txt
|
||||||
macOS 15.3.1
|
macOS 15.3.1
|
||||||
Xcode 16.2
|
Xcode 16.2
|
||||||
Flutter 3.27.4
|
Flutter 3.27.0
|
||||||
```
|
```
|
||||||
|
|
||||||
NOTE: Newer versions of macOS and Xcode may also work, but have not been confirmed to work by the Cake team.
|
NOTE: Newer versions of macOS and Xcode may also work, but have not been confirmed to work by the Cake team.
|
||||||
|
@ -43,9 +43,9 @@ To enable iOS build support for Xcode, perform the following:
|
||||||
|
|
||||||
### 3. Installing Flutter
|
### 3. Installing Flutter
|
||||||
|
|
||||||
Install Flutter, specifically version `3.27.4` by following the [official docs](https://docs.flutter.dev/get-started/install/macos/desktop?tab=download).
|
Install Flutter, specifically version `3.27.0` by following the [official docs](https://docs.flutter.dev/get-started/install/macos/desktop?tab=download).
|
||||||
|
|
||||||
NOTE: as `3.27.4` is not the latest version, you'll need to download it from <https://docs.flutter.dev/release/archive> instead of the link in the docs above.
|
NOTE: as `3.27.0` is not the latest version, you'll need to download it from <https://docs.flutter.dev/release/archive> instead of the link in the docs above.
|
||||||
|
|
||||||
### 4. Installing Rust
|
### 4. Installing Rust
|
||||||
|
|
||||||
|
@ -65,7 +65,7 @@ The output of this command should appear like this, indicating successful instal
|
||||||
|
|
||||||
```zsh
|
```zsh
|
||||||
Doctor summary (to see all details, run flutter doctor -v):
|
Doctor summary (to see all details, run flutter doctor -v):
|
||||||
[✓] Flutter (Channel stable, 3.27.4, on macOS 15.x.x)
|
[✓] Flutter (Channel stable, 3.27.0, on macOS 15.x.x)
|
||||||
[✓] Xcode - develop for iOS and macOS (Xcode 16.2)
|
[✓] Xcode - develop for iOS and macOS (Xcode 16.2)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
@ -20,8 +20,8 @@ In order to build the latest version of Cake Wallet, simply run the following:
|
||||||
git clone --branch main https://github.com/cake-tech/cake_wallet.git
|
git clone --branch main https://github.com/cake-tech/cake_wallet.git
|
||||||
# NOTE: Replace `main` with the latest release tag available at https://github.com/cake-tech/cake_wallet/releases/latest.
|
# NOTE: Replace `main` with the latest release tag available at https://github.com/cake-tech/cake_wallet/releases/latest.
|
||||||
cd cake_wallet
|
cd cake_wallet
|
||||||
# docker build -t ghcr.io/cake-tech/cake_wallet:debian12-flutter3.27.4-go1.24.1 . # Uncomment to build the docker image yourself instead of pulling it from the registry
|
# docker build -t ghcr.io/cake-tech/cake_wallet:debian12-flutter3.27.0-go1.24.1-ruststablenightly . # Uncomment to build the docker image yourself instead of pulling it from the registry
|
||||||
docker run --privileged -v$(pwd):$(pwd) -w $(pwd) -i --rm ghcr.io/cake-tech/cake_wallet:debian12-flutter3.27.4-go1.24.1 bash -x << EOF
|
docker run --privileged -v$(pwd):$(pwd) -w $(pwd) -i --rm ghcr.io/cake-tech/cake_wallet:debian12-flutter3.27.0-go1.24.1-ruststablenightly bash -x << EOF
|
||||||
set -x -e
|
set -x -e
|
||||||
pushd scripts
|
pushd scripts
|
||||||
./gen_android_manifest.sh
|
./gen_android_manifest.sh
|
||||||
|
|
|
@ -7,7 +7,7 @@ The following are the system requirements to build Cake Wallet for your macOS de
|
||||||
```txt
|
```txt
|
||||||
macOS 15.3.1
|
macOS 15.3.1
|
||||||
Xcode 16.2
|
Xcode 16.2
|
||||||
Flutter 3.27.4
|
Flutter 3.27.0
|
||||||
```
|
```
|
||||||
|
|
||||||
### 1. Installing dependencies
|
### 1. Installing dependencies
|
||||||
|
@ -34,9 +34,9 @@ sudo xcodebuild -runFirstLaunch
|
||||||
|
|
||||||
### 3. Installing Flutter
|
### 3. Installing Flutter
|
||||||
|
|
||||||
Install Flutter, specifically version `3.27.4` by following the [official docs](https://docs.flutter.dev/get-started/install/macos/desktop?tab=download).
|
Install Flutter, specifically version `3.27.0` by following the [official docs](https://docs.flutter.dev/get-started/install/macos/desktop?tab=download).
|
||||||
|
|
||||||
NOTE: as `3.27.4` is not the latest version, you'll need to download it from <https://docs.flutter.dev/release/archive> instead of the link in the docs above.
|
NOTE: as `3.27.0` is not the latest version, you'll need to download it from <https://docs.flutter.dev/release/archive> instead of the link in the docs above.
|
||||||
|
|
||||||
### 4. Installing Rust
|
### 4. Installing Rust
|
||||||
|
|
||||||
|
@ -56,7 +56,7 @@ The output of this command should appear like this, indicating successful instal
|
||||||
|
|
||||||
```zsh
|
```zsh
|
||||||
Doctor summary (to see all details, run flutter doctor -v):
|
Doctor summary (to see all details, run flutter doctor -v):
|
||||||
[✓] Flutter (Channel stable, 3.27.4, on macOS 15.x.x)
|
[✓] Flutter (Channel stable, 3.27.0, on macOS 15.x.x)
|
||||||
...
|
...
|
||||||
[✓] Xcode - develop for iOS and macOS (Xcode 16.2)
|
[✓] Xcode - develop for iOS and macOS (Xcode 16.2)
|
||||||
...
|
...
|
||||||
|
|
|
@ -6,18 +6,18 @@ The following are the system requirements to build Cake Wallet for your Windows
|
||||||
|
|
||||||
```txt
|
```txt
|
||||||
Windows 10 or later (64-bit), x86-64 based
|
Windows 10 or later (64-bit), x86-64 based
|
||||||
Flutter 3.27.4
|
Flutter 3.27.0
|
||||||
```
|
```
|
||||||
|
|
||||||
### 1. Installing Flutter
|
### 1. Installing Flutter
|
||||||
|
|
||||||
Install Flutter, specifically version `3.27.4` by following the [official docs](https://docs.flutter.dev/get-started/install/windows).
|
Install Flutter, specifically version `3.27.0` by following the [official docs](https://docs.flutter.dev/get-started/install/windows).
|
||||||
|
|
||||||
In order for Flutter to function, you'll also need to enable Developer Mode:
|
In order for Flutter to function, you'll also need to enable Developer Mode:
|
||||||
|
|
||||||
Start Menu > search for "Run" > type `ms-settings:developers`, and turn on Developer Mode.
|
Start Menu > search for "Run" > type `ms-settings:developers`, and turn on Developer Mode.
|
||||||
|
|
||||||
NOTE: as `3.27.4` is not the latest version, you'll need to download it from <https://docs.flutter.dev/release/archive> instead of the link in the docs above.
|
NOTE: as `3.27.0` is not the latest version, you'll need to download it from <https://docs.flutter.dev/release/archive> instead of the link in the docs above.
|
||||||
|
|
||||||
### 2. Install Development Tools
|
### 2. Install Development Tools
|
||||||
|
|
||||||
|
|
|
@ -70,7 +70,10 @@ class WalletKeysAndSeedPageRobot {
|
||||||
if (walletType == WalletType.bitcoin ||
|
if (walletType == WalletType.bitcoin ||
|
||||||
walletType == WalletType.litecoin ||
|
walletType == WalletType.litecoin ||
|
||||||
walletType == WalletType.bitcoinCash) {
|
walletType == WalletType.bitcoinCash) {
|
||||||
commonTestCases.hasText(appStore.wallet!.seed!);
|
final seedWords = appStore.wallet!.seed!.split(" ");
|
||||||
|
for (var seedWord in seedWords) {
|
||||||
|
commonTestCases.hasTextAtLestOnce(seedWord);
|
||||||
|
}
|
||||||
tester.printToConsole('$walletName wallet has seeds properly displayed');
|
tester.printToConsole('$walletName wallet has seeds properly displayed');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -78,10 +81,14 @@ class WalletKeysAndSeedPageRobot {
|
||||||
walletType == WalletType.solana ||
|
walletType == WalletType.solana ||
|
||||||
walletType == WalletType.tron) {
|
walletType == WalletType.tron) {
|
||||||
if (hasSeed) {
|
if (hasSeed) {
|
||||||
commonTestCases.hasText(appStore.wallet!.seed!);
|
final seedWords = appStore.wallet!.seed!.split(" ");
|
||||||
|
for (var seedWord in seedWords) {
|
||||||
|
commonTestCases.hasTextAtLestOnce(seedWord);
|
||||||
|
}
|
||||||
tester.printToConsole('$walletName wallet has seeds properly displayed');
|
tester.printToConsole('$walletName wallet has seeds properly displayed');
|
||||||
}
|
}
|
||||||
if (hasPrivateKey) {
|
if (hasPrivateKey) {
|
||||||
|
await commonTestCases.tapItemByKey('wallet_keys_page_keys');
|
||||||
commonTestCases.hasText(appStore.wallet!.privateKey!);
|
commonTestCases.hasText(appStore.wallet!.privateKey!);
|
||||||
tester.printToConsole('$walletName wallet has private key properly displayed');
|
tester.printToConsole('$walletName wallet has private key properly displayed');
|
||||||
}
|
}
|
||||||
|
@ -89,14 +96,19 @@ class WalletKeysAndSeedPageRobot {
|
||||||
|
|
||||||
if (walletType == WalletType.nano || walletType == WalletType.banano) {
|
if (walletType == WalletType.nano || walletType == WalletType.banano) {
|
||||||
if (hasSeed) {
|
if (hasSeed) {
|
||||||
commonTestCases.hasText(appStore.wallet!.seed!);
|
final seedWords = appStore.wallet!.seed!.split(" ");
|
||||||
|
for (var seedWord in seedWords) {
|
||||||
|
commonTestCases.hasTextAtLestOnce(seedWord);
|
||||||
|
}
|
||||||
tester.printToConsole('$walletName wallet has seeds properly displayed');
|
tester.printToConsole('$walletName wallet has seeds properly displayed');
|
||||||
}
|
}
|
||||||
if (hasHexSeed) {
|
if (hasHexSeed) {
|
||||||
|
await commonTestCases.tapItemByKey('wallet_keys_page_keys');
|
||||||
commonTestCases.hasText(appStore.wallet!.hexSeed!);
|
commonTestCases.hasText(appStore.wallet!.hexSeed!);
|
||||||
tester.printToConsole('$walletName wallet has hexSeed properly displayed');
|
tester.printToConsole('$walletName wallet has hexSeed properly displayed');
|
||||||
}
|
}
|
||||||
if (hasPrivateKey) {
|
if (hasPrivateKey) {
|
||||||
|
await commonTestCases.tapItemByKey('wallet_keys_page_keys');
|
||||||
commonTestCases.hasText(appStore.wallet!.privateKey!);
|
commonTestCases.hasText(appStore.wallet!.privateKey!);
|
||||||
tester.printToConsole('$walletName wallet has private key properly displayed');
|
tester.printToConsole('$walletName wallet has private key properly displayed');
|
||||||
}
|
}
|
||||||
|
@ -129,35 +141,39 @@ class WalletKeysAndSeedPageRobot {
|
||||||
final hasSeedLegacy = Polyseed.isValidSeed(seed);
|
final hasSeedLegacy = Polyseed.isValidSeed(seed);
|
||||||
|
|
||||||
if (hasPublicSpendKey) {
|
if (hasPublicSpendKey) {
|
||||||
|
await commonTestCases.tapItemByKey('wallet_keys_page_keys');
|
||||||
commonTestCases.hasText(keys.publicSpendKey);
|
commonTestCases.hasText(keys.publicSpendKey);
|
||||||
tester.printToConsole('$walletName wallet has public spend key properly displayed');
|
tester.printToConsole('$walletName wallet has public spend key properly displayed');
|
||||||
}
|
}
|
||||||
if (hasPrivateSpendKey) {
|
if (hasPrivateSpendKey) {
|
||||||
|
await commonTestCases.tapItemByKey('wallet_keys_page_keys');
|
||||||
commonTestCases.hasText(keys.privateSpendKey);
|
commonTestCases.hasText(keys.privateSpendKey);
|
||||||
tester.printToConsole('$walletName wallet has private spend key properly displayed');
|
tester.printToConsole('$walletName wallet has private spend key properly displayed');
|
||||||
}
|
}
|
||||||
if (hasPublicViewKey) {
|
if (hasPublicViewKey) {
|
||||||
|
await commonTestCases.tapItemByKey('wallet_keys_page_keys');
|
||||||
commonTestCases.hasText(keys.publicViewKey);
|
commonTestCases.hasText(keys.publicViewKey);
|
||||||
tester.printToConsole('$walletName wallet has public view key properly displayed');
|
tester.printToConsole('$walletName wallet has public view key properly displayed');
|
||||||
}
|
}
|
||||||
if (hasPrivateViewKey) {
|
if (hasPrivateViewKey) {
|
||||||
|
await commonTestCases.tapItemByKey('wallet_keys_page_keys');
|
||||||
commonTestCases.hasText(keys.privateViewKey);
|
commonTestCases.hasText(keys.privateViewKey);
|
||||||
tester.printToConsole('$walletName wallet has private view key properly displayed');
|
tester.printToConsole('$walletName wallet has private view key properly displayed');
|
||||||
}
|
}
|
||||||
if (hasSeeds) {
|
if (hasSeeds) {
|
||||||
await commonTestCases.dragUntilVisible(
|
await commonTestCases.tapItemByKey('wallet_keys_page_seed');
|
||||||
'${walletName}_wallet_seed_item_key',
|
final seedWords = seed.split(" ");
|
||||||
'wallet_keys_page_credentials_list_view_key',
|
for (var seedWord in seedWords) {
|
||||||
);
|
commonTestCases.hasTextAtLestOnce(seedWord);
|
||||||
commonTestCases.hasText(seed);
|
}
|
||||||
tester.printToConsole('$walletName wallet has seeds properly displayed');
|
tester.printToConsole('$walletName wallet has seeds properly displayed');
|
||||||
}
|
}
|
||||||
if (hasSeedLegacy) {
|
if (hasSeedLegacy) {
|
||||||
await commonTestCases.dragUntilVisible(
|
await commonTestCases.tapItemByKey('wallet_keys_page_seed_legacy');
|
||||||
'${walletName}_wallet_seed_legacy_item_key',
|
final seedWords = legacySeed.split(" ");
|
||||||
'wallet_keys_page_credentials_list_view_key',
|
for (var seedWord in seedWords) {
|
||||||
);
|
commonTestCases.hasTextAtLestOnce(seedWord);
|
||||||
commonTestCases.hasText(legacySeed);
|
}
|
||||||
tester.printToConsole('$walletName wallet has legacy seeds properly displayed');
|
tester.printToConsole('$walletName wallet has legacy seeds properly displayed');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue