Compare commits

..

No commits in common. "main" and "v5.0.1" have entirely different histories.
main ... v5.0.1

255 changed files with 1894 additions and 7419 deletions

View file

@ -55,7 +55,7 @@ jobs:
- name: Flutter action
uses: subosito/flutter-action@v1
with:
flutter-version: "3.27.0"
flutter-version: "3.27.4"
channel: stable
- name: Install package dependencies
@ -153,8 +153,8 @@ jobs:
echo "const shortKey = '${{ secrets.SHORT_KEY }}';" >> lib/.secrets.g.dart
echo "const backupSalt = '${{ secrets.BACKUP_SALT }}';" >> lib/.secrets.g.dart
echo "const backupKeychainSalt = '${{ secrets.BACKUP_KEY_CHAIN_SALT }}';" >> lib/.secrets.g.dart
echo "const changeNowCakeWalletApiKey = '${{ secrets.CHANGE_NOW_API_KEY }}';" >> lib/.secrets.g.dart
echo "const changeNowMoneroApiKey = '${{ secrets.CHANGE_NOW_API_KEY }}';" >> lib/.secrets.g.dart
echo "const changeNowApiKey = '${{ secrets.CHANGE_NOW_API_KEY }}';" >> lib/.secrets.g.dart
echo "const changeNowApiKeyDesktop = '${{ secrets.CHANGE_NOW_API_KEY_DESKTOP }}';" >> lib/.secrets.g.dart
echo "const wyreSecretKey = '${{ secrets.WYRE_SECRET_KEY }}';" >> lib/.secrets.g.dart
echo "const wyreApiKey = '${{ secrets.WYRE_API_KEY }}';" >> lib/.secrets.g.dart
echo "const wyreAccountId = '${{ secrets.WYRE_ACCOUNT_ID }}';" >> lib/.secrets.g.dart
@ -168,7 +168,6 @@ jobs:
echo "const ioniaClientId = '${{ secrets.IONIA_CLIENT_ID }}';" >> lib/.secrets.g.dart
echo "const twitterBearerToken = '${{ secrets.TWITTER_BEARER_TOKEN }}';" >> lib/.secrets.g.dart
echo "const trocadorApiKey = '${{ secrets.TROCADOR_API_KEY }}';" >> lib/.secrets.g.dart
echo "const trocadorMoneroApiKey = '${{ secrets.TROCADOR_API_KEY }}';" >> lib/.secrets.g.dart
echo "const trocadorExchangeMarkup = '${{ secrets.TROCADOR_EXCHANGE_MARKUP }}';" >> lib/.secrets.g.dart
echo "const anonPayReferralCode = '${{ secrets.ANON_PAY_REFERRAL_CODE }}';" >> lib/.secrets.g.dart
echo "const fiatApiKey = '${{ secrets.FIAT_API_KEY }}';" >> lib/.secrets.g.dart
@ -179,8 +178,7 @@ jobs:
echo "const etherScanApiKey = '${{ secrets.ETHER_SCAN_API_KEY }}';" >> cw_evm/lib/.secrets.g.dart
echo "const moralisApiKey = '${{ secrets.MORALIS_API_KEY }}';" >> cw_evm/lib/.secrets.g.dart
echo "const chatwootWebsiteToken = '${{ secrets.CHATWOOT_WEBSITE_TOKEN }}';" >> lib/.secrets.g.dart
echo "const exolixCakeWalletApiKey = '${{ secrets.EXOLIX_API_KEY }}';" >> lib/.secrets.g.dart
echo "const exolixMoneroApiKey = '${{ secrets.EXOLIX_API_KEY }}';" >> lib/.secrets.g.dart
echo "const exolixApiKey = '${{ secrets.EXOLIX_API_KEY }}';" >> lib/.secrets.g.dart
echo "const robinhoodApplicationId = '${{ secrets.ROBINHOOD_APPLICATION_ID }}';" >> lib/.secrets.g.dart
echo "const exchangeHelperApiKey = '${{ secrets.ROBINHOOD_CID_CLIENT_SECRET }}';" >> lib/.secrets.g.dart
echo "const walletConnectProjectId = '${{ secrets.WALLET_CONNECT_PROJECT_ID }}';" >> lib/.secrets.g.dart

View file

@ -1,21 +0,0 @@
name: No http imports
on: [pull_request]
jobs:
PR_test_build:
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
- name: Check for http package usage
if: github.event_name == 'pull_request'
run: |
GIT_GREP_OUT="$(git grep package:http | (grep .dart: || test $? = 1) | (grep -v proxy_wrapper.dart || test $? = 1) | (grep -v very_insecure_http_do_not_use || test $? = 1) || true)"
[[ "x$GIT_GREP_OUT" == "x" ]] && exit 0
echo "$GIT_GREP_OUT"
echo "There are .dart files which use http imports"
echo "Using http package breaks proxy integration"
echo "Please use ProxyWrapper.getHttpClient() from package:cw_core/utils/proxy_wrapper.dart"
exit 1

View file

@ -15,5 +15,5 @@ jobs:
[[ "x$GIT_GREP_OUT" == "x" ]] && exit 0
echo "$GIT_GREP_OUT"
echo "There are .dart files which use print() statements"
echo "Please use printV from package:cw_core/utils/print_verbose.dart"
echo "Please use printV from package: cw_core/utils/print_verbose.dart"
exit 1

View file

@ -9,7 +9,7 @@ jobs:
PR_test_build:
runs-on: linux-amd64
container:
image: ghcr.io/cake-tech/cake_wallet:debian12-flutter3.27.0-go1.24.1-ruststablenightly
image: ghcr.io/cake-tech/cake_wallet:debian12-flutter3.27.4-go1.24.1
env:
STORE_PASS: test@cake_wallet
KEY_PASS: test@cake_wallet
@ -98,8 +98,8 @@ jobs:
else
echo "const backupKeychainSalt = '${{ secrets.BACKUP_KEY_CHAIN_SALT }}';" >> lib/.secrets.g.dart
fi
echo "const changeNowCakeWalletApiKey = '${{ secrets.CHANGE_NOW_API_KEY }}';" >> lib/.secrets.g.dart
echo "const changeNowMoneroApiKey = '${{ secrets.CHANGE_NOW_API_KEY }}';" >> lib/.secrets.g.dart
echo "const changeNowApiKey = '${{ secrets.CHANGE_NOW_API_KEY }}';" >> lib/.secrets.g.dart
echo "const changeNowApiKeyDesktop = '${{ secrets.CHANGE_NOW_API_KEY_DESKTOP }}';" >> lib/.secrets.g.dart
echo "const wyreSecretKey = '${{ secrets.WYRE_SECRET_KEY }}';" >> lib/.secrets.g.dart
echo "const wyreApiKey = '${{ secrets.WYRE_API_KEY }}';" >> lib/.secrets.g.dart
echo "const wyreAccountId = '${{ secrets.WYRE_ACCOUNT_ID }}';" >> lib/.secrets.g.dart
@ -113,7 +113,6 @@ jobs:
echo "const ioniaClientId = '${{ secrets.IONIA_CLIENT_ID }}';" >> lib/.secrets.g.dart
echo "const twitterBearerToken = '${{ secrets.TWITTER_BEARER_TOKEN }}';" >> lib/.secrets.g.dart
echo "const trocadorApiKey = '${{ secrets.TROCADOR_API_KEY }}';" >> lib/.secrets.g.dart
echo "const trocadorMoneroApiKey = '${{ secrets.TROCADOR_API_KEY }}';" >> lib/.secrets.g.dart
echo "const trocadorExchangeMarkup = '${{ secrets.TROCADOR_EXCHANGE_MARKUP }}';" >> lib/.secrets.g.dart
echo "const anonPayReferralCode = '${{ secrets.ANON_PAY_REFERRAL_CODE }}';" >> lib/.secrets.g.dart
echo "const fiatApiKey = '${{ secrets.FIAT_API_KEY }}';" >> lib/.secrets.g.dart
@ -125,8 +124,7 @@ jobs:
echo "const moralisApiKey = '${{ secrets.MORALIS_API_KEY }}';" >> cw_evm/lib/.secrets.g.dart
echo "const nowNodesApiKey = '${{ secrets.EVM_NOWNODES_API_KEY }}';" >> cw_evm/lib/.secrets.g.dart
echo "const chatwootWebsiteToken = '${{ secrets.CHATWOOT_WEBSITE_TOKEN }}';" >> lib/.secrets.g.dart
echo "const exolixCakeWalletApiKey = '${{ secrets.EXOLIX_API_KEY }}';" >> lib/.secrets.g.dart
echo "const exolixMoneroApiKey = '${{ secrets.EXOLIX_API_KEY }}';" >> lib/.secrets.g.dart
echo "const exolixApiKey = '${{ secrets.EXOLIX_API_KEY }}';" >> lib/.secrets.g.dart
echo "const robinhoodApplicationId = '${{ secrets.ROBINHOOD_APPLICATION_ID }}';" >> lib/.secrets.g.dart
echo "const exchangeHelperApiKey = '${{ secrets.ROBINHOOD_CID_CLIENT_SECRET }}';" >> lib/.secrets.g.dart
echo "const walletConnectProjectId = '${{ secrets.WALLET_CONNECT_PROJECT_ID }}';" >> lib/.secrets.g.dart
@ -255,11 +253,6 @@ jobs:
- name: Build generated code
run: |
flutter --version
flutter clean
rm -rf .dart_tool
rm pubspec.lock
flutter pub get
./model_generator.sh async
- name: Generate key properties

View file

@ -9,7 +9,7 @@ jobs:
PR_test_build:
runs-on: linux-amd64
container:
image: ghcr.io/cake-tech/cake_wallet:debian12-flutter3.27.0-go1.24.1-ruststablenightly
image: ghcr.io/cake-tech/cake_wallet:debian12-flutter3.27.4-go1.24.1
env:
STORE_PASS: test@cake_wallet
KEY_PASS: test@cake_wallet
@ -91,8 +91,8 @@ jobs:
else
echo "const backupKeychainSalt = '${{ secrets.BACKUP_KEY_CHAIN_SALT }}';" >> lib/.secrets.g.dart
fi
echo "const changeNowCakeWalletApiKey = '${{ secrets.CHANGE_NOW_API_KEY }}';" >> lib/.secrets.g.dart
echo "const changeNowMoneroApiKey = '${{ secrets.CHANGE_NOW_API_KEY }}';" >> lib/.secrets.g.dart
echo "const changeNowApiKey = '${{ secrets.CHANGE_NOW_API_KEY }}';" >> lib/.secrets.g.dart
echo "const changeNowApiKeyDesktop = '${{ secrets.CHANGE_NOW_API_KEY_DESKTOP }}';" >> lib/.secrets.g.dart
echo "const wyreSecretKey = '${{ secrets.WYRE_SECRET_KEY }}';" >> lib/.secrets.g.dart
echo "const wyreApiKey = '${{ secrets.WYRE_API_KEY }}';" >> lib/.secrets.g.dart
echo "const wyreAccountId = '${{ secrets.WYRE_ACCOUNT_ID }}';" >> lib/.secrets.g.dart
@ -106,7 +106,6 @@ jobs:
echo "const ioniaClientId = '${{ secrets.IONIA_CLIENT_ID }}';" >> lib/.secrets.g.dart
echo "const twitterBearerToken = '${{ secrets.TWITTER_BEARER_TOKEN }}';" >> lib/.secrets.g.dart
echo "const trocadorApiKey = '${{ secrets.TROCADOR_API_KEY }}';" >> lib/.secrets.g.dart
echo "const trocadorMoneroApiKey = '${{ secrets.TROCADOR_API_KEY }}';" >> lib/.secrets.g.dart
echo "const trocadorExchangeMarkup = '${{ secrets.TROCADOR_EXCHANGE_MARKUP }}';" >> lib/.secrets.g.dart
echo "const anonPayReferralCode = '${{ secrets.ANON_PAY_REFERRAL_CODE }}';" >> lib/.secrets.g.dart
echo "const fiatApiKey = '${{ secrets.FIAT_API_KEY }}';" >> lib/.secrets.g.dart
@ -118,8 +117,7 @@ jobs:
echo "const moralisApiKey = '${{ secrets.MORALIS_API_KEY }}';" >> cw_evm/lib/.secrets.g.dart
echo "const nowNodesApiKey = '${{ secrets.EVM_NOWNODES_API_KEY }}';" >> cw_evm/lib/.secrets.g.dart
echo "const chatwootWebsiteToken = '${{ secrets.CHATWOOT_WEBSITE_TOKEN }}';" >> lib/.secrets.g.dart
echo "const exolixCakeWalletApiKey = '${{ secrets.EXOLIX_API_KEY }}';" >> lib/.secrets.g.dart
echo "const exolixMoneroApiKey = '${{ secrets.EXOLIX_API_KEY }}';" >> lib/.secrets.g.dart
echo "const exolixApiKey = '${{ secrets.EXOLIX_API_KEY }}';" >> lib/.secrets.g.dart
echo "const robinhoodApplicationId = '${{ secrets.ROBINHOOD_APPLICATION_ID }}';" >> lib/.secrets.g.dart
echo "const exchangeHelperApiKey = '${{ secrets.ROBINHOOD_CID_CLIENT_SECRET }}';" >> lib/.secrets.g.dart
echo "const walletConnectProjectId = '${{ secrets.WALLET_CONNECT_PROJECT_ID }}';" >> lib/.secrets.g.dart

View file

@ -1,4 +1,4 @@
# docker buildx build --push --pull --platform linux/amd64,linux/arm64 . -f Dockerfile -t ghcr.io/cake-tech/cake_wallet:debian12-flutter3.27.0-go1.24.1-ruststablenightly
# docker buildx build --push --pull --platform linux/amd64,linux/arm64 . -f Dockerfile -t ghcr.io/cake-tech/cake_wallet:debian12-flutter3.27.4-go1.24.1
# Heavily inspired by cirrusci images
# https://github.com/cirruslabs/docker-images-android/blob/master/sdk/tools/Dockerfile
@ -15,11 +15,11 @@ LABEL org.opencontainers.image.source=https://github.com/cake-tech/cake_wallet
ENV GOLANG_VERSION=1.24.1
# Pin Flutter version to latest known-working version
ENV FLUTTER_VERSION=3.27.0
ENV FLUTTER_VERSION=3.27.4
# Pin Android Studio, platform, and build tools versions to latest known-working version
# Comes from https://developer.android.com/studio/#command-tools
ENV ANDROID_SDK_TOOLS_VERSION=13114758
ENV ANDROID_SDK_TOOLS_VERSION=11076708
# Comes from https://developer.android.com/studio/releases/build-tools
ENV ANDROID_PLATFORM_VERSION=35
ENV ANDROID_BUILD_TOOLS_VERSION=34.0.0
@ -164,12 +164,9 @@ RUN (addgroup kvm || true) && \
ENV PATH=${HOME}/.cargo/bin:${PATH}
RUN curl https://sh.rustup.rs -sSf | bash -s -- -y && \
cargo install cargo-ndk && \
for toolchain in stable nightly; \
do \
for target in aarch64-linux-android armv7-linux-androideabi i686-linux-android x86_64-linux-android x86_64-unknown-linux-gnu aarch64-unknown-linux-gnu aarch64-unknown-linux-gnu; \
do \
rustup target add --toolchain $toolchain $target; \
done \
rustup target add --toolchain stable $target; \
done
# Download and install Flutter
@ -178,11 +175,8 @@ ENV FLUTTER_HOME=${HOME}/sdks/flutter/${FLUTTER_VERSION}
ENV FLUTTER_ROOT=$FLUTTER_HOME
ENV PATH=${PATH}:${FLUTTER_HOME}/bin:${FLUTTER_HOME}/bin/cache/dart-sdk/bin
RUN git clone --branch ${FLUTTER_VERSION} https://github.com/flutter/flutter.git ${FLUTTER_HOME} && \
cd ${FLUTTER_HOME} && \
git fetch -a
RUN yes | flutter doctor --android-licenses \
RUN git clone --depth 1 --branch ${FLUTTER_VERSION} https://github.com/flutter/flutter.git ${FLUTTER_HOME} \
&& yes | flutter doctor --android-licenses \
&& flutter doctor \
&& chown -R root:root ${FLUTTER_HOME}

View file

@ -1,10 +0,0 @@
<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>

Before

Width:  |  Height:  |  Size: 984 B

View file

@ -1,3 +0,0 @@
<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>

Before

Width:  |  Height:  |  Size: 532 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 72 KiB

View file

@ -1,76 +0,0 @@
<?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>

Before

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 920 B

View file

@ -1,4 +1,3 @@
Add built-in Tor support (experimental)
Ledger improvements
UI/UX improvements
New themes and UI/UX improvements
Ledger flow enhancements
Bug fixes

View file

@ -1,9 +1,4 @@
Add built-in Tor support (experimental)
Add dEuro investments
Solana fixes/enhancements
Polygon fixes/enhancements
WalletConnect improvements
Ledger improvements
Payjoin improvements
UI/UX improvements
New themes and UI/UX improvements
Payjoin enhancements
Ledger flow enhancements
Bug fixes

View file

@ -266,12 +266,6 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
derivationPath: walletInfo.derivationInfo!.derivationPath!);
}
@override
Future<void> close({bool shouldCleanup = false}) async {
payjoinManager.cleanupSessions();
super.close(shouldCleanup: shouldCleanup);
}
late final PayjoinManager payjoinManager;
bool get isPayjoinAvailable => unspentCoinsInfo.values

View file

@ -31,10 +31,12 @@ abstract class BitcoinWalletAddressesBase extends ElectrumWalletAddresses with S
final PayjoinManager payjoinManager;
@observable
payjoin.Receiver? currentPayjoinReceiver;
@observable
String? payjoinEndpoint = null;
@computed
String? get payjoinEndpoint =>
currentPayjoinReceiver?.pjUriBuilder().build().pjEndpoint();
@override
String getAddress(
@ -57,32 +59,16 @@ abstract class BitcoinWalletAddressesBase extends ElectrumWalletAddresses with S
return generateP2WPKHAddress(hd: hd, index: index, network: network);
}
@action
Future<void> initPayjoin() async {
try {
await payjoinManager.initPayjoin();
currentPayjoinReceiver = await payjoinManager.getUnusedReceiver(primaryAddress);
payjoinEndpoint = (await currentPayjoinReceiver?.pjUri())?.pjEndpoint();
currentPayjoinReceiver = await payjoinManager.initReceiver(primaryAddress);
payjoinManager.resumeSessions();
} catch (e) {
printV(e);
// Ignore Connectivity errors
if (!e.toString().contains("error sending request for url")) rethrow;
}
}
@action
Future<void> newPayjoinReceiver() async {
try {
currentPayjoinReceiver = await payjoinManager.getUnusedReceiver(primaryAddress);
payjoinEndpoint = (await currentPayjoinReceiver?.pjUri())?.pjEndpoint();
currentPayjoinReceiver = await payjoinManager.initReceiver(primaryAddress);
payjoinManager.spawnReceiver(receiver: currentPayjoinReceiver!);
} catch (e) {
printV(e);
// Ignore Connectivity errors
if (!e.toString().contains("error sending request for url")) rethrow;
}
printV("Initializing new Payjoin Receiver");
payjoinManager.spawnNewReceiver(receiver: currentPayjoinReceiver!);
}
}

View file

@ -5,8 +5,6 @@ import 'dart:typed_data';
import 'package:bitcoin_base/bitcoin_base.dart';
import 'package:cw_bitcoin/bitcoin_amount_format.dart';
import 'package:cw_core/utils/print_verbose.dart';
import 'package:cw_core/utils/proxy_socket/abstract.dart';
import 'package:cw_core/utils/proxy_wrapper.dart';
import 'package:flutter/foundation.dart';
import 'package:rxdart/rxdart.dart';
@ -44,7 +42,7 @@ class ElectrumClient {
static const aliveTimerDuration = Duration(seconds: 4);
bool get isConnected => _isConnected;
ProxySocket? socket;
Socket? socket;
void Function(ConnectionStatus)? onConnectionStatusChange;
int _id;
final Map<String, SocketTask> _tasks;
@ -74,11 +72,18 @@ class ElectrumClient {
} catch (_) {}
socket = null;
final ssl = !(useSSL == false || (useSSL == null && uri.toString().contains("btc-electrum")));
try {
socket = await ProxyWrapper().getSocksSocket(ssl, host, port, connectionTimeout: connectionTimeout);
if (useSSL == false || (useSSL == null && uri.toString().contains("btc-electrum"))) {
socket = await Socket.connect(host, port, timeout: connectionTimeout);
} else {
socket = await SecureSocket.connect(
host,
port,
timeout: connectionTimeout,
onBadCertificate: (_) => true,
);
}
} catch (e) {
printV("connect: $e");
if (e is HandshakeException) {
useSSL = !(useSSL ?? false);
}
@ -100,6 +105,7 @@ class ElectrumClient {
// use ping to determine actual connection status since we could've just not timed out yet:
// _setConnectionStatus(ConnectionStatus.connected);
socket!.listen(
(Uint8List event) {
try {

View file

@ -4,7 +4,6 @@ import 'dart:io';
import 'dart:isolate';
import 'package:bitcoin_base/bitcoin_base.dart';
import 'package:cw_core/utils/proxy_wrapper.dart';
import 'package:cw_bitcoin/bitcoin_amount_format.dart';
import 'package:cw_core/format_amount.dart';
import 'package:cw_core/utils/print_verbose.dart';
@ -50,6 +49,7 @@ import 'package:mobx/mobx.dart';
import 'package:rxdart/subjects.dart';
import 'package:sp_scanner/sp_scanner.dart';
import 'package:hex/hex.dart';
import 'package:http/http.dart' as http;
part 'electrum_wallet.g.dart';
@ -493,9 +493,10 @@ abstract class ElectrumWalletBase
Future<void> updateFeeRates() async {
if (await checkIfMempoolAPIIsEnabled() && type == WalletType.bitcoin) {
try {
final response = await ProxyWrapper()
.get(clearnetUri: Uri.parse("https://mempool.cakewallet.com/api/v1/fees/recommended"))
.timeout(Duration(seconds: 15));
final response = await http
.get(Uri.parse("https://mempool.cakewallet.com/api/v1/fees/recommended"))
.timeout(Duration(seconds: 5));
final result = json.decode(response.body) as Map<String, dynamic>;
final slowFee = (result['economyFee'] as num?)?.toInt() ?? 0;
int mediumFee = (result['hourFee'] as num?)?.toInt() ?? 0;
@ -1175,7 +1176,9 @@ abstract class ElectrumWalletBase
}
});
return PendingBitcoinTransaction(transaction, type,
return PendingBitcoinTransaction(
transaction,
type,
electrumClient: electrumClient,
amount: estimatedTx.amount,
fee: estimatedTx.fee,
@ -1185,8 +1188,8 @@ abstract class ElectrumWalletBase
isSendAll: estimatedTx.isSendAll,
hasTaprootInputs: hasTaprootInputs,
utxos: estimatedTx.utxos,
publicKeys: estimatedTx.publicKeys)
..addListener((transaction) async {
publicKeys: estimatedTx.publicKeys
)..addListener((transaction) async {
transactionHistory.addOne(transaction);
if (estimatedTx.spendsSilentPayment) {
transactionHistory.transactions.values.forEach((tx) {
@ -1877,17 +1880,20 @@ abstract class ElectrumWalletBase
if (height != null && height > 0 && await checkIfMempoolAPIIsEnabled()) {
try {
final blockHash = await ProxyWrapper()
.get(clearnetUri: Uri.parse("https://mempool.cakewallet.com/api/v1/block-height/$height"))
.timeout(Duration(seconds: 15));
final blockHash = await http.get(
Uri.parse(
"https://mempool.cakewallet.com/api/v1/block-height/$height",
),
);
if (blockHash.statusCode == 200 &&
blockHash.body.isNotEmpty &&
jsonDecode(blockHash.body) != null) {
final blockResponse = await ProxyWrapper()
.get(clearnetUri: Uri.parse("https://mempool.cakewallet.com/api/v1/block/${blockHash}"))
.timeout(Duration(seconds: 15));
final blockResponse = await http.get(
Uri.parse(
"https://mempool.cakewallet.com/api/v1/block/${blockHash.body}",
),
);
if (blockResponse.statusCode == 200 &&
blockResponse.body.isNotEmpty &&
jsonDecode(blockResponse.body)['timestamp'] != null) {

View file

@ -6,7 +6,6 @@ import 'dart:typed_data';
import 'package:bitcoin_base/bitcoin_base.dart';
import 'package:cw_bitcoin/bitcoin_wallet.dart';
import 'package:cw_bitcoin/bitcoin_wallet_addresses.dart';
import 'package:cw_bitcoin/payjoin/payjoin_persister.dart';
import 'package:cw_bitcoin/payjoin/payjoin_receive_worker.dart';
import 'package:cw_bitcoin/payjoin/payjoin_send_worker.dart';
import 'package:cw_bitcoin/payjoin/payjoin_session_errors.dart';
@ -17,7 +16,6 @@ import 'package:cw_core/utils/print_verbose.dart';
import 'package:payjoin_flutter/common.dart';
import 'package:payjoin_flutter/receive.dart';
import 'package:payjoin_flutter/send.dart';
import 'package:payjoin_flutter/src/config.dart' as pj_config;
import 'package:payjoin_flutter/uri.dart' as PayjoinUri;
class PayjoinManager {
@ -33,13 +31,11 @@ class PayjoinManager {
'https://ohttp.cakewallet.com',
];
static String randomOhttpRelayUrl() =>
ohttpRelayUrls[Random.secure().nextInt(ohttpRelayUrls.length)];
static Future<PayjoinUri.Url> randomOhttpRelayUrl() => PayjoinUri.Url.fromStr(
ohttpRelayUrls[Random.secure().nextInt(ohttpRelayUrls.length)]);
static const payjoinDirectoryUrl = 'https://payjo.in';
Future<void> initPayjoin() => pj_config.PConfig.initializeApp();
Future<void> resumeSessions() async {
final allSessions = _payjoinStorage.readAllOpenSessions(_wallet.id);
@ -47,13 +43,13 @@ class PayjoinManager {
if (session.isSenderSession) {
printV("Resuming Payjoin Sender Session ${session.pjUri!}");
return _spawnSender(
sender: Sender.fromJson(json: session.sender!),
sender: Sender.fromJson(session.sender!),
pjUri: session.pjUri!,
);
}
final receiver = Receiver.fromJson(json: session.receiver!);
final receiver = Receiver.fromJson(session.receiver!);
printV("Resuming Payjoin Receiver Session ${receiver.id()}");
return spawnReceiver(receiver: receiver);
return _spawnReceiver(receiver: receiver);
});
printV("Resumed ${spawnedSessions.length} Payjoin Sessions");
@ -70,12 +66,7 @@ class PayjoinManager {
psbtBase64: originalPsbt,
pjUri: pjUri,
);
final persister = PayjoinSenderPersister.impl();
final newSender =
await senderBuilder.buildRecommended(minFeeRate: minFeeRateSatPerKwu);
final senderToken = await newSender.persist(persister: persister);
return Sender.load(token: senderToken, persister: persister);
return senderBuilder.buildRecommended(minFeeRate: minFeeRateSatPerKwu);
} catch (e) {
throw Exception('Error initializing Payjoin Sender: $e');
}
@ -121,13 +112,15 @@ class PayjoinManager {
}
} catch (e) {
_cleanupSession(pjUri);
await _payjoinStorage.markSenderSessionUnrecoverable(pjUri, e.toString());
completer.complete();
printV(e);
await _payjoinStorage.markSenderSessionUnrecoverable(pjUri);
completer.completeError(e);
}
} else if (message is PayjoinSessionError) {
_cleanupSession(pjUri);
if (message is UnrecoverableError) {
await _payjoinStorage.markSenderSessionUnrecoverable(pjUri, message.message);
printV(message.message);
await _payjoinStorage.markSenderSessionUnrecoverable(pjUri);
completer.complete();
} else if (message is RecoverableError) {
completer.complete();
@ -147,41 +140,42 @@ class PayjoinManager {
return completer.future;
}
Future<Receiver> getUnusedReceiver(String address,
Future<Receiver> initReceiver(String address,
[bool isTestnet = false]) async {
final session = _payjoinStorage.getUnusedActiveReceiverSession(_wallet.id);
if (session != null) {
try {
final payjoinDirectory =
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(
ohttpRelay: await randomOhttpRelayUrl(),
payjoinDirectory: payjoinDirectoryUrl,
payjoinDirectory: payjoinDirectory,
);
final newReceiver = await NewReceiver.create(
final receiver = await Receiver.create(
address: address,
network: isTestnet ? Network.testnet : Network.bitcoin,
directory: payjoinDirectoryUrl,
directory: payjoinDirectory,
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);
return receiver;
} catch (e) {
throw Exception('Error initializing Payjoin Receiver: $e');
}
}
Future<void> spawnReceiver({
Future<void> spawnNewReceiver({
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,
bool isTestnet = false,
}) async {
@ -201,8 +195,7 @@ class PayjoinManager {
rawAmount = getOutputAmountFromTx(tx, _wallet);
break;
case PayjoinReceiverRequestTypes.checkIsOwned:
(_wallet.walletAddresses as BitcoinWalletAddresses)
.newPayjoinReceiver();
(_wallet.walletAddresses as BitcoinWalletAddresses).newPayjoinReceiver();
_payjoinStorage.markReceiverSessionInProgress(receiver.id());
final inputScript = message['input_script'] as Uint8List;
@ -226,10 +219,6 @@ class PayjoinManager {
case PayjoinReceiverRequestTypes.getCandidateInputs:
utxos = _wallet.getUtxoWithPrivateKeys();
if (utxos.isEmpty) {
await _wallet.updateAllUnspents();
utxos = _wallet.getUtxoWithPrivateKeys();
}
mainToIsolateSendPort?.send({
'requestId': message['requestId'],
'result': utxos,

View file

@ -1,66 +0,0 @@
import 'package:payjoin_flutter/src/generated/api/receive.dart';
import 'package:payjoin_flutter/src/generated/api/send.dart';
class PayjoinSenderPersister implements DartSenderPersister {
static DartSenderPersister impl() {
final impl = PayjoinSenderPersister();
return DartSenderPersister(
save: (sender) => impl.save(sender: sender),
load: (token) => impl.load(token: token),
);
}
final Map<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;
}

View file

@ -4,16 +4,14 @@ import 'dart:isolate';
import 'dart:typed_data';
import 'package:blockchain_utils/blockchain_utils.dart';
import 'package:cw_bitcoin/payjoin/manager.dart';
import 'package:cw_bitcoin/payjoin/payjoin_session_errors.dart';
import 'package:cw_bitcoin/psbt/signer.dart';
import 'package:cw_core/utils/print_verbose.dart';
import 'package:cw_core/utils/proxy_wrapper.dart';
import 'package:http/http.dart' as http;
import 'package:payjoin_flutter/bitcoin_ffi.dart';
import 'package:payjoin_flutter/common.dart';
import 'package:payjoin_flutter/receive.dart';
import 'package:payjoin_flutter/src/generated/frb_generated.dart' as pj;
import 'package:http/http.dart' as very_insecure_http_do_not_use; // for errors
enum PayjoinReceiverRequestTypes {
processOriginalTx,
@ -29,7 +27,7 @@ class PayjoinReceiverWorker {
final pendingRequests = <String, Completer<dynamic>>{};
PayjoinReceiverWorker._(this.sendPort);
static final client = ProxyWrapper().getHttpIOClient();
static Future<void> run(List<Object> args) async {
await pj.core.init();
@ -43,10 +41,11 @@ class PayjoinReceiverWorker {
receivePort.listen(worker.handleMessage);
try {
final receiver = Receiver.fromJson(json: receiverJson);
final httpClient = http.Client();
final receiver = Receiver.fromJson(receiverJson);
final uncheckedProposal =
await worker.receiveUncheckedProposal(receiver);
await worker.receiveUncheckedProposal(httpClient, receiver);
final originalTx = await uncheckedProposal.extractTxToScheduleBroadcast();
sendPort.send({
@ -57,14 +56,14 @@ class PayjoinReceiverWorker {
final payjoinProposal = await worker.processPayjoinProposal(
uncheckedProposal,
);
final psbt = await worker.sendFinalProposal(payjoinProposal);
final psbt = await worker.sendFinalProposal(httpClient, payjoinProposal);
sendPort.send({
'type': PayjoinReceiverRequestTypes.proposalSent,
'psbt': psbt,
});
} catch (e) {
if (e is HttpException ||
(e is very_insecure_http_do_not_use.ClientException &&
(e is http.ClientException &&
e.message.contains("Software caused connection abort"))) {
sendPort.send(PayjoinSessionError.recoverable(e.toString()));
} else {
@ -98,16 +97,15 @@ class PayjoinReceiverWorker {
return completer.future;
}
Future<UncheckedProposal> receiveUncheckedProposal(Receiver session) async {
Future<UncheckedProposal> receiveUncheckedProposal(
http.Client httpClient, Receiver session) async {
while (true) {
printV("Polling for Proposal (${session.id()})");
final extractReq = await session.extractReq(
ohttpRelay: await PayjoinManager.randomOhttpRelayUrl(),
);
final extractReq = await session.extractReq();
final request = extractReq.$1;
final url = Uri.parse(request.url.asString());
final httpRequest = await client.post(url,
final httpRequest = await httpClient.post(url,
headers: {'Content-Type': request.contentType}, body: request.body);
final proposal = await session.processRes(
@ -116,14 +114,13 @@ class PayjoinReceiverWorker {
}
}
Future<String> sendFinalProposal(PayjoinProposal finalProposal) async {
final req = await finalProposal.extractReq(
ohttpRelay: await PayjoinManager.randomOhttpRelayUrl(),
);
Future<String> sendFinalProposal(
http.Client httpClient, PayjoinProposal finalProposal) async {
final req = await finalProposal.extractV2Req();
final proposalReq = req.$1;
final proposalCtx = req.$2;
final request = await client.post(
final request = await httpClient.post(
Uri.parse(proposalReq.url.asString()),
headers: {"Content-Type": proposalReq.contentType},
body: proposalReq.body,
@ -174,7 +171,7 @@ class PayjoinReceiverWorker {
final listUnspent =
await _sendRequest(PayjoinReceiverRequestTypes.getCandidateInputs);
final unspent = listUnspent as List<UtxoWithPrivateKey>;
if (unspent.isEmpty) throw RecoverableError('No unspent outputs available');
if (unspent.isEmpty) throw Exception('No unspent outputs available');
final selectedUtxo = await _inputPairFromUtxo(unspent[0]);
final pj6 = await pj5.contributeInputs(replacementInputs: [selectedUtxo]);
@ -217,6 +214,6 @@ class PayjoinReceiverWorker {
sequence: 0,
);
return InputPair.newInstance(txin: txin, psbtin: psbtin);
return InputPair.newInstance(txin, psbtin);
}
}

View file

@ -5,12 +5,10 @@ import 'dart:isolate';
import 'package:cw_bitcoin/payjoin/manager.dart';
import 'package:cw_bitcoin/payjoin/payjoin_session_errors.dart';
import 'package:cw_core/utils/print_verbose.dart';
import 'package:cw_core/utils/proxy_wrapper.dart';
import 'package:http/http.dart' as http;
import 'package:payjoin_flutter/common.dart';
import 'package:payjoin_flutter/send.dart';
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 {
requestPosted,
@ -31,7 +29,7 @@ class PayjoinSenderWorker {
final senderJson = args[1] as String;
final pjUrl = args[2] as String;
final sender = Sender.fromJson(json: senderJson);
final sender = Sender.fromJson(senderJson);
final worker = PayjoinSenderWorker._(sendPort, pjUrl);
try {
@ -44,17 +42,19 @@ class PayjoinSenderWorker {
sendPort.send(e);
}
}
final client = ProxyWrapper().getHttpIOClient();
/// Run a payjoin sender (V2 protocol first, fallback to V1).
Future<String> runSender(Sender sender) async {
final httpClient = http.Client();
try {
return await _runSenderV2(sender);
return await _runSenderV2(sender, httpClient);
} catch (e) {
printV(e);
if (e is pj_error.FfiCreateRequestError) {
return await _runSenderV1(sender);
if (e is PayjoinException &&
// TODO condition on error type instead of message content
e.message?.contains('parse receiver public key') == true) {
return await _runSenderV1(sender, httpClient);
} else if (e is HttpException) {
printV(e);
throw Exception(PayjoinSessionError.recoverable(e.toString()));
@ -65,14 +65,13 @@ class PayjoinSenderWorker {
}
/// Attempt to send payjoin using the V2 of the protocol.
Future<String> _runSenderV2(Sender sender) async {
Future<String> _runSenderV2(Sender sender, http.Client httpClient) async {
try {
final postRequest = await sender.extractV2(
ohttpProxyUrl:
await pj_uri.Url.fromStr(PayjoinManager.randomOhttpRelayUrl()),
ohttpProxyUrl: await PayjoinManager.randomOhttpRelayUrl(),
);
final postResult = await _postRequest(postRequest.$1);
final postResult = await _postRequest(httpClient, postRequest.$1);
final getContext =
await postRequest.$2.processResponse(response: postResult);
@ -84,7 +83,7 @@ class PayjoinSenderWorker {
final getRequest = await getContext.extractReq(
ohttpRelay: await PayjoinManager.randomOhttpRelayUrl(),
);
final getRes = await _postRequest(getRequest.$1);
final getRes = await _postRequest(httpClient, getRequest.$1);
final proposalPsbt = await getContext.processResponse(
response: getRes,
ohttpCtx: getRequest.$2,
@ -98,20 +97,20 @@ class PayjoinSenderWorker {
}
/// Attempt to send payjoin using the V1 of the protocol.
Future<String> _runSenderV1(Sender sender) async {
Future<String> _runSenderV1(Sender sender, http.Client httpClient) async {
try {
final postRequest = await sender.extractV1();
final response = await _postRequest(postRequest.$1);
final response = await _postRequest(httpClient, postRequest.$1);
sendPort.send({'type': PayjoinSenderRequestTypes.requestPosted});
return await postRequest.$2.processResponse(response: response);
} catch (e, stack) {
throw PayjoinSessionError.unrecoverable('Send V1 payjoin error: $e, $stack');
} catch (e) {
throw PayjoinSessionError.unrecoverable('Send V1 payjoin error: $e');
}
}
Future<List<int>> _postRequest(Request req) async {
Future<List<int>> _postRequest(http.Client client, Request req) async {
final httpRequest = await client.post(Uri.parse(req.url.asString()),
headers: {'Content-Type': req.contentType}, body: req.body);

View file

@ -23,14 +23,6 @@ class PayjoinStorage {
),
);
PayjoinSession? getUnusedActiveReceiverSession(String walletId) =>
_payjoinSessionSources.values
.where((session) =>
session.walletId == walletId &&
session.status == PayjoinSessionStatus.created.name &&
!session.isSenderSession)
.firstOrNull;
Future<void> markReceiverSessionComplete(
String sessionId, String txId, String amount) async {
final session = _payjoinSessionSources.get("$_receiverPrefix${sessionId}")!;
@ -84,11 +76,10 @@ class PayjoinStorage {
await session.save();
}
Future<void> markSenderSessionUnrecoverable(String pjUrl, String reason) async {
Future<void> markSenderSessionUnrecoverable(String pjUrl) async {
final session = _payjoinSessionSources.get("$_senderPrefix$pjUrl")!;
session.status = PayjoinSessionStatus.unrecoverable.name;
session.error = reason;
await session.save();
}

View file

@ -5,39 +5,34 @@ packages:
dependency: transitive
description:
name: _fe_analyzer_shared
sha256: "16e298750b6d0af7ce8a3ba7c18c69c3785d11b15ec83f6dcd0ad2a0009b3cab"
sha256: "4897882604d919befd350648c7f91926a9d5de99e67b455bf0917cc2362f4bb8"
url: "https://pub.dev"
source: hosted
version: "76.0.0"
_macros:
dependency: transitive
description: dart
source: sdk
version: "0.3.3"
version: "47.0.0"
analyzer:
dependency: transitive
description:
name: analyzer
sha256: "1f14db053a8c23e260789e9b0980fa27f2680dd640932cae5e1137cce0e46e1e"
sha256: "690e335554a8385bc9d787117d9eb52c0c03ee207a607e593de3c9d71b1cfe80"
url: "https://pub.dev"
source: hosted
version: "6.11.0"
version: "4.7.0"
args:
dependency: transitive
description:
name: args
sha256: d0481093c50b1da8910eb0bb301626d4d8eb7284aa739614d2b394ee09e3ea04
sha256: bf9f5caeea8d8fe6721a9c358dd8a5c1947b27f1cfaa18b39c301273594919e6
url: "https://pub.dev"
source: hosted
version: "2.7.0"
version: "2.6.0"
asn1lib:
dependency: transitive
description:
name: asn1lib
sha256: "1c296cd268f486cabcc3930e9b93a8133169305f18d722916e675959a88f6d2c"
sha256: "4bae5ae63e6d6dd17c4aac8086f3dec26c0236f6a0f03416c6c19d830c367cf5"
url: "https://pub.dev"
source: hosted
version: "1.5.9"
version: "1.5.8"
async:
dependency: transitive
description:
@ -126,10 +121,10 @@ packages:
dependency: transitive
description:
name: build
sha256: cef23f1eda9b57566c81e2133d196f8e3df48f244b317368d65c5943d91148f0
sha256: "80184af8b6cb3e5c1c4ec6d8544d27711700bc3e6d2efad04238c7b5290889f0"
url: "https://pub.dev"
source: hosted
version: "2.4.2"
version: "2.4.1"
build_cli_annotations:
dependency: transitive
description:
@ -142,42 +137,42 @@ packages:
dependency: transitive
description:
name: build_config
sha256: "4ae2de3e1e67ea270081eaee972e1bd8f027d459f249e0f1186730784c2e7e33"
sha256: bf80fcfb46a29945b423bd9aad884590fb1dc69b330a4d4700cac476af1708d1
url: "https://pub.dev"
source: hosted
version: "1.1.2"
version: "1.1.1"
build_daemon:
dependency: transitive
description:
name: build_daemon
sha256: "8e928697a82be082206edb0b9c99c5a4ad6bc31c9e9b8b2f291ae65cd4a25daa"
sha256: "79b2aef6ac2ed00046867ed354c88778c9c0f029df8a20fe10b5436826721ef9"
url: "https://pub.dev"
source: hosted
version: "4.0.4"
version: "4.0.2"
build_resolvers:
dependency: "direct dev"
description:
name: build_resolvers
sha256: b9e4fda21d846e192628e7a4f6deda6888c36b5b69ba02ff291a01fd529140f0
sha256: "687cf90a3951affac1bd5f9ecb5e3e90b60487f3d9cdc359bb310f8876bb02a6"
url: "https://pub.dev"
source: hosted
version: "2.4.4"
version: "2.0.10"
build_runner:
dependency: "direct dev"
description:
name: build_runner
sha256: "058fe9dce1de7d69c4b84fada934df3e0153dd000758c4d65964d0166779aa99"
sha256: "028819cfb90051c6b5440c7e574d1896f8037e3c96cf17aaeb054c9311cfbf4d"
url: "https://pub.dev"
source: hosted
version: "2.4.15"
version: "2.4.13"
build_runner_core:
dependency: transitive
description:
name: build_runner_core
sha256: "22e3aa1c80e0ada3722fe5b63fd43d9c8990759d0a2cf489c8c5d7b2bdebc021"
sha256: "6d6ee4276b1c5f34f21fdf39425202712d2be82019983d52f351c94aafbc2c41"
url: "https://pub.dev"
source: hosted
version: "8.0.0"
version: "7.2.10"
built_collection:
dependency: transitive
description:
@ -190,10 +185,10 @@ packages:
dependency: transitive
description:
name: built_value
sha256: "082001b5c3dc495d4a42f1d5789990505df20d8547d42507c29050af6933ee27"
sha256: "8b158ab94ec6913e480dc3f752418348b5ae099eb75868b5f4775f0572999c61"
url: "https://pub.dev"
source: hosted
version: "8.10.1"
version: "8.9.4"
cake_backup:
dependency: transitive
description:
@ -301,10 +296,10 @@ packages:
dependency: transitive
description:
name: dart_style
sha256: "7306ab8a2359a48d22310ad823521d723acfed60ee1f7e37388e8986853b6820"
sha256: "7a03456c3490394c8e7665890333e91ae8a49be43542b616e414449ac358acd4"
url: "https://pub.dev"
source: hosted
version: "2.3.8"
version: "2.2.4"
dart_varuint_bitcoin:
dependency: transitive
description:
@ -394,10 +389,10 @@ packages:
dependency: transitive
description:
name: flutter_rust_bridge
sha256: "5a5c7a5deeef2cc2ffe6076a33b0429f4a20ceac22a397297aed2b1eb067e611"
sha256: "3292ad6085552987b8b3b9a7e5805567f4013372d302736b702801acb001ee00"
url: "https://pub.dev"
source: hosted
version: "2.9.0"
version: "2.7.1"
flutter_test:
dependency: "direct dev"
description: flutter
@ -407,10 +402,10 @@ packages:
dependency: transitive
description:
name: flutter_web_bluetooth
sha256: ad26a1b3fef95b86ea5f63793b9a0cdc1a33490f35d754e4e711046cae3ebbf8
sha256: "1363831def5eed1e1064d1eca04e8ccb35446e8f758579c3c519e156b77926da"
url: "https://pub.dev"
source: hosted
version: "1.1.0"
version: "1.0.0"
flutter_web_plugins:
dependency: transitive
description: flutter
@ -420,10 +415,10 @@ packages:
dependency: transitive
description:
name: freezed_annotation
sha256: c87ff004c8aa6af2d531668b46a4ea379f7191dc6dfa066acd53d506da6e044b
sha256: c2e2d632dd9b8a2b7751117abcfc2b4888ecfe181bd9fca7170d9ef02e595fe2
url: "https://pub.dev"
source: hosted
version: "3.0.0"
version: "2.4.4"
frontend_server_client:
dependency: transitive
description:
@ -444,18 +439,18 @@ packages:
dependency: transitive
description:
name: google_identity_services_web
sha256: "5d187c46dc59e02646e10fe82665fc3884a9b71bc1c90c2b8b749316d33ee454"
sha256: "55580f436822d64c8ff9a77e37d61f5fb1e6c7ec9d632a43ee324e2a05c3c6c9"
url: "https://pub.dev"
source: hosted
version: "0.3.3+1"
version: "0.3.3"
googleapis_auth:
dependency: transitive
description:
name: googleapis_auth
sha256: b81fe352cc4a330b3710d2b7ad258d9bcef6f909bb759b306bf42973a7d046db
sha256: befd71383a955535060acde8792e7efc11d2fccd03dd1d3ec434e85b68775938
url: "https://pub.dev"
source: hosted
version: "2.0.0"
version: "1.6.0"
graphs:
dependency: transitive
description:
@ -468,10 +463,10 @@ packages:
dependency: "direct main"
description:
name: grpc
sha256: "30e1edae6846b163a64f6d8716e3443980fe1f7d2d1f086f011d24ea186f2582"
sha256: "5b99b7a420937d4361ece68b798c9af8e04b5bc128a7859f2a4be87427694813"
url: "https://pub.dev"
source: hosted
version: "4.0.4"
version: "4.0.1"
hex:
dependency: transitive
description:
@ -492,18 +487,18 @@ packages:
dependency: "direct dev"
description:
name: hive_generator
sha256: "06cb8f58ace74de61f63500564931f9505368f45f98958bd7a6c35ba24159db4"
sha256: "81fd20125cb2ce8fd23623d7744ffbaf653aae93706c9bd3bf7019ea0ace3938"
url: "https://pub.dev"
source: hosted
version: "2.0.1"
version: "1.1.3"
http:
dependency: "direct main"
description:
name: http
sha256: "2c11f3f94c687ee9bad77c171151672986360b2b001d109814ee7140b2cf261b"
sha256: fe7ab022b76f3034adc518fb6ea04a82387620e19977665ea18d30a1cf43442f
url: "https://pub.dev"
source: hosted
version: "1.4.0"
version: "1.3.0"
http2:
dependency: transitive
description:
@ -524,10 +519,10 @@ packages:
dependency: transitive
description:
name: http_parser
sha256: "178d74305e7866013777bab2c3d8726205dc5a4dd935297175b19a23a2e66571"
sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b"
url: "https://pub.dev"
source: hosted
version: "4.1.2"
version: "4.0.2"
intl:
dependency: "direct main"
description:
@ -597,10 +592,10 @@ packages:
dependency: "direct main"
description:
name: ledger_flutter_plus
sha256: "531da5daba5731d9eca2732881ef2f039b97bf8aa3564e7098dfa99a9b07a8e6"
sha256: "1c03f3c4a9754b5f0170a9eb9552ec54fa86e985f8ee71a255ee2c5629b53d31"
url: "https://pub.dev"
source: hosted
version: "1.5.3"
version: "1.5.1"
ledger_litecoin:
dependency: "direct main"
description:
@ -626,14 +621,6 @@ packages:
url: "https://pub.dev"
source: hosted
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:
dependency: transitive
description:
@ -678,10 +665,10 @@ packages:
dependency: "direct dev"
description:
name: mobx_codegen
sha256: e0abbbc651a69550440f6b65c99ec222a1e2a4afd7baec8ba0f3088c7ca582a8
sha256: d4beb9cea4b7b014321235f8fdc7c2193ee0fe1d1198e9da7403f8bc85c4407c
url: "https://pub.dev"
source: hosted
version: "2.7.1"
version: "2.3.0"
nested:
dependency: transitive
description:
@ -703,10 +690,10 @@ packages:
dependency: transitive
description:
name: package_config
sha256: f096c55ebb7deb7e384101542bfba8c52696c1b56fca2eb62827989ef2353bbc
sha256: "92d4488434b520a62570293fbd33bb556c7d49230791c1b4bbd973baf6d2dc67"
url: "https://pub.dev"
source: hosted
version: "2.2.0"
version: "2.1.1"
path:
dependency: transitive
description:
@ -727,10 +714,10 @@ packages:
dependency: transitive
description:
name: path_provider_android
sha256: d0d310befe2c8ab9e7f393288ccbb11b60c019c6b5afc21973eeee4dda2b35e9
sha256: "4adf4fd5423ec60a29506c76581bc05854c55e3a0b72d35bb28d661c9686edf2"
url: "https://pub.dev"
source: hosted
version: "2.2.17"
version: "2.2.15"
path_provider_foundation:
dependency: transitive
description:
@ -767,11 +754,11 @@ packages:
dependency: "direct main"
description:
path: "."
ref: da83a23f3a011cb49eb3b6513cd485b3fb8867ff
resolved-ref: da83a23f3a011cb49eb3b6513cd485b3fb8867ff
url: "https://github.com/OmarHatem28/payjoin-flutter"
ref: "6a3eb32fb9467ac12e7b75d3de47de4ca44fd88c"
resolved-ref: "6a3eb32fb9467ac12e7b75d3de47de4ca44fd88c"
url: "https://github.com/konstantinullrich/payjoin-flutter"
source: git
version: "0.23.0"
version: "0.21.0"
petitparser:
dependency: transitive
description:
@ -824,26 +811,26 @@ packages:
dependency: transitive
description:
name: provider
sha256: "4abbd070a04e9ddc287673bf5a030c7ca8b685ff70218720abab8b092f53dd84"
sha256: c8a055ee5ce3fd98d6fc872478b03823ffdb448699c6ebdbbc71d59b596fd48c
url: "https://pub.dev"
source: hosted
version: "6.1.5"
version: "6.1.2"
pub_semver:
dependency: transitive
description:
name: pub_semver
sha256: "5bfcf68ca79ef689f8990d1160781b4bad40a3bd5e5218ad4076ddb7f4081585"
sha256: "7b3cfbf654f3edd0c6298ecd5be782ce997ddf0e00531b9464b55245185bbbbd"
url: "https://pub.dev"
source: hosted
version: "2.2.0"
version: "2.1.5"
pubspec_parse:
dependency: transitive
description:
name: pubspec_parse
sha256: "0560ba233314abbed0a48a2956f7f022cce7c3e1e73df540277da7544cad4082"
sha256: "81876843eb50dc2e1e5b151792c9a985c5ed2536914115ed04e9c8528f6647b0"
url: "https://pub.dev"
source: hosted
version: "1.5.0"
version: "1.4.0"
quiver:
dependency: transitive
description:
@ -872,18 +859,18 @@ packages:
dependency: "direct main"
description:
name: shared_preferences
sha256: "6e8bf70b7fef813df4e9a36f658ac46d107db4b4cfe1048b477d4e453a8159f5"
sha256: "846849e3e9b68f3ef4b60c60cf4b3e02e9321bc7f4d8c4692cf87ffa82fc8a3a"
url: "https://pub.dev"
source: hosted
version: "2.5.3"
version: "2.5.2"
shared_preferences_android:
dependency: transitive
description:
name: shared_preferences_android
sha256: "20cbd561f743a342c76c151d6ddb93a9ce6005751e7aa458baad3858bfbfb6ac"
sha256: a768fc8ede5f0c8e6150476e14f38e2417c0864ca36bb4582be8e21925a03c22
url: "https://pub.dev"
source: hosted
version: "2.4.10"
version: "2.4.6"
shared_preferences_foundation:
dependency: transitive
description:
@ -928,18 +915,18 @@ packages:
dependency: transitive
description:
name: shelf
sha256: e7dd780a7ffb623c57850b33f43309312fc863fb6aa3d276a754bb299839ef12
sha256: ad29c505aee705f41a4d8963641f91ac4cee3c8fad5947e033390a7bd8180fa4
url: "https://pub.dev"
source: hosted
version: "1.4.2"
version: "1.4.1"
shelf_web_socket:
dependency: transitive
description:
name: shelf_web_socket
sha256: "3632775c8e90d6c9712f883e633716432a27758216dfb61bd86a8321c0580925"
sha256: cc36c297b52866d203dbf9332263c94becc2fe0ceaa9681d07b6ef9807023b67
url: "https://pub.dev"
source: hosted
version: "3.0.0"
version: "2.0.1"
sky_engine:
dependency: transitive
description: flutter
@ -948,37 +935,27 @@ packages:
socks5_proxy:
dependency: transitive
description:
path: "."
ref: "27ad7c2efae8d7460325c74b90f660085cbd0685"
resolved-ref: "27ad7c2efae8d7460325c74b90f660085cbd0685"
url: "https://github.com/LacticWhale/socks_dart"
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"
name: socks5_proxy
sha256: "616818a0ea1064a4823b53c9f7eaf8da64ed82dcd51ed71371c7e54751ed5053"
url: "https://pub.dev"
source: hosted
version: "1.0.6"
source_gen:
dependency: transitive
description:
name: source_gen
sha256: "14658ba5f669685cd3d63701d01b31ea748310f7ab854e471962670abcf57832"
sha256: "2d79738b6bbf38a43920e2b8d189e9a3ce6cc201f4b8fc76be5e4fe377b1c38d"
url: "https://pub.dev"
source: hosted
version: "1.5.0"
version: "1.2.6"
source_helper:
dependency: transitive
description:
name: source_helper
sha256: "86d247119aedce8e63f4751bd9626fc9613255935558447569ad42f9f5b48b3c"
sha256: "3b67aade1d52416149c633ba1bb36df44d97c6b51830c2198e934e3fca87ca1f"
url: "https://pub.dev"
source: hosted
version: "1.3.5"
version: "1.3.3"
source_span:
dependency: transitive
description:
@ -996,6 +973,14 @@ packages:
url: "https://github.com/cake-tech/sp_scanner"
source: git
version: "0.0.1"
sprintf:
dependency: transitive
description:
name: sprintf
sha256: "1fc9ffe69d4df602376b52949af107d8f5703b77cda567c4d7d86a0693120f23"
url: "https://pub.dev"
source: hosted
version: "7.0.0"
stack_trace:
dependency: transitive
description:
@ -1048,19 +1033,10 @@ packages:
dependency: transitive
description:
name: timing
sha256: "70a3b636575d4163c477e6de42f247a23b315ae20e86442bebe32d3cabf61c32"
sha256: "62ee18aca144e4a9f29d212f5a4c6a053be252b895ab14b5821996cff4ed90fe"
url: "https://pub.dev"
source: hosted
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"
version: "1.0.2"
tuple:
dependency: transitive
description:
@ -1081,10 +1057,10 @@ packages:
dependency: transitive
description:
name: universal_ble
sha256: "35d210e93a5938c6a6d1fd3c710cf4ac90b1bdd1b11c8eb2beeb32600672e6e6"
sha256: "1fad089150a29db82b3b7d60327e18c5ad6b3a5bb509defc1c690b0a76b9c098"
url: "https://pub.dev"
source: hosted
version: "0.17.0"
version: "0.15.0"
universal_platform:
dependency: transitive
description:
@ -1101,6 +1077,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "0.3.0"
uuid:
dependency: transitive
description:
name: uuid
sha256: a5be9ef6618a7ac1e964353ef476418026db906c4facdedaa299b7a2e71690ff
url: "https://pub.dev"
source: hosted
version: "4.5.1"
vector_math:
dependency: transitive
description:
@ -1137,18 +1121,18 @@ packages:
dependency: transitive
description:
name: web_socket
sha256: "34d64019aa8e36bf9842ac014bb5d2f5586ca73df5e4d9bf5c936975cae6982c"
sha256: "3c12d96c0c9a4eec095246debcea7b86c0324f22df69893d538fcc6f1b8cce83"
url: "https://pub.dev"
source: hosted
version: "1.0.1"
version: "0.1.6"
web_socket_channel:
dependency: transitive
description:
name: web_socket_channel
sha256: d645757fb0f4773d602444000a8131ff5d48c9e47adfe9772652dd1a4f2d45c8
sha256: "0b8e2457400d8a859b7b2030786835a28a8e80836ef64402abef392ff4f1d0e5"
url: "https://pub.dev"
source: hosted
version: "3.0.3"
version: "3.0.2"
xdg_directories:
dependency: transitive
description:
@ -1182,5 +1166,5 @@ packages:
source: hosted
version: "2.2.2"
sdks:
dart: ">=3.6.0 <4.0.0"
flutter: ">=3.27.0"
dart: ">=3.5.0 <4.0.0"
flutter: ">=3.24.0"

View file

@ -42,8 +42,8 @@ dependencies:
url: https://github.com/cake-tech/bech32.git
payjoin_flutter:
git:
url: https://github.com/OmarHatem28/payjoin-flutter
ref: da83a23f3a011cb49eb3b6513cd485b3fb8867ff #cake-v2
url: https://github.com/konstantinullrich/payjoin-flutter
ref: 6a3eb32fb9467ac12e7b75d3de47de4ca44fd88c #cake-v1
ledger_flutter_plus: ^1.4.1
ledger_bitcoin:
git:
@ -54,18 +54,14 @@ dependencies:
git:
url: https://github.com/cake-tech/ledger-flutter-plus-plugins
path: packages/ledger-litecoin
socks_socket:
git:
url: https://github.com/sneurlax/socks_socket
ref: e6232c53c1595469931ababa878759a067c02e94
dev_dependencies:
flutter_test:
sdk: flutter
build_runner: ^2.4.15
build_resolvers: ^2.4.4
build_runner: ^2.4.7
build_resolvers: ^2.0.9
mobx_codegen: ^2.0.7
hive_generator: ^2.0.1
hive_generator: ^1.1.3
dependency_overrides:
watcher: ^1.1.0

View file

@ -7,7 +7,7 @@ homepage: https://cakewallet.com
environment:
sdk: '>=2.19.0 <3.0.0'
flutter: ">=1.20.0"
flutter: ">=1.17.0"
dependencies:
flutter:
@ -33,9 +33,9 @@ dependencies:
dev_dependencies:
flutter_test:
sdk: flutter
build_runner: ^2.4.15
build_runner: ^2.4.7
mobx_codegen: ^2.0.7
hive_generator: ^2.0.1
hive_generator: ^1.1.3
dependency_overrides:
watcher: ^1.1.0

View file

@ -111,8 +111,7 @@ class CryptoCurrency extends EnumerableItem<int> with Serializable<int> implemen
CryptoCurrency.zano,
CryptoCurrency.ton,
CryptoCurrency.flip,
CryptoCurrency.deuro,
CryptoCurrency.usdtbsc,
CryptoCurrency.deuro
];
static const havenCurrencies = [
@ -234,7 +233,6 @@ 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 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 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 =
[...all, ...havenCurrencies].fold<Map<int, CryptoCurrency>>(<int, CryptoCurrency>{}, (acc, item) {

View file

@ -1,7 +1,7 @@
import 'package:cw_core/utils/proxy_wrapper.dart';
import 'package:cw_core/utils/print_verbose.dart';
import 'package:intl/intl.dart';
import 'dart:convert';
import 'package:http/http.dart' as http;
// FIXME: Hardcoded values; Works only for monero
@ -234,14 +234,10 @@ int getHavenHeightByDate({required DateTime date}) {
}
Future<int> getHavenCurrentHeight() async {
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();
final response = await http.get(Uri.parse('https://explorer.havenprotocol.org/api/networkinfo'));
if (response.statusCode == 200) {
final info = jsonDecode(stringResponse);
final info = jsonDecode(response.body);
return info['data']['height'] as int;
} else {
throw Exception('Failed to load current blockchain height');
@ -273,13 +269,13 @@ const bitcoinDates = {
};
Future<int> getBitcoinHeightByDateAPI({required DateTime date}) async {
final req = await ProxyWrapper().getHttpClient()
.getUrl(Uri.parse("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();
final response = await http.get(
Uri.parse(
"https://mempool.cakewallet.com/api/v1/mining/blocks/timestamp/${(date.millisecondsSinceEpoch / 1000).round()}",
),
);
return jsonDecode(stringResponse)['height'] as int;
return jsonDecode(response.body)['height'] as int;
}
int getBitcoinHeightByDate({required DateTime date}) {

View file

@ -1,12 +1,12 @@
import 'dart:io';
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 'dart:convert';
import 'package:http/http.dart' as http;
import 'package:hive/hive.dart';
import 'package:cw_core/hive_type_ids.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:http/io_client.dart' as ioc;
import 'dart:math' as math;
import 'package:convert/convert.dart';
@ -184,17 +184,23 @@ class Node extends HiveObject with Keyable {
final body = {'jsonrpc': '2.0', 'id': '0', 'method': "getinfo"};
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 response = await ProxyWrapper().post(
clearnetUri: rpcUri,
final response = await client.post(
rpcUri,
headers: {'Content-Type': 'application/json'},
body: jsonBody,
);
printV("node check response: ${response.body}");
final resBody = json.decode(response.body) as Map<String, dynamic>;
return resBody['result']['height'] != null;
} catch (e) {
printV("error: $e");
@ -212,7 +218,11 @@ class Node extends HiveObject with Keyable {
final body = {'jsonrpc': '2.0', 'id': '0', 'method': methodName};
try {
final client = ProxyWrapper().getHttpIOClient();
final authenticatingClient = HttpClient();
authenticatingClient.badCertificateCallback =
((X509Certificate cert, String host, int port) => true);
final http.Client client = ioc.IOClient(authenticatingClient);
final jsonBody = json.encode(body);
@ -232,15 +242,15 @@ class Node extends HiveObject with Keyable {
return !(response['offline'] as bool);
}
final responseString = await response.body;
printV("node check response: ${response.body}");
if ((responseString.contains("400 Bad Request") // Some other generic error
if ((response.body.contains("400 Bad Request") // Some other generic error
||
responseString.contains("plain HTTP request was sent to HTTPS port") // Cloudflare
response.body.contains("plain HTTP request was sent to HTTPS port") // Cloudflare
||
response.headers["location"] != null // Generic reverse proxy
||
responseString
response.body
.contains("301 Moved Permanently") // Poorly configured generic reverse proxy
) &&
!(useSSL ?? false)) {
@ -267,16 +277,15 @@ class Node extends HiveObject with Keyable {
}
Future<bool> requestNodeWithProxy() async {
if (!isValidProxyAddress && !CakeTor.instance.enabled) {
if (!isValidProxyAddress /* && !Tor.instance.enabled*/) {
return false;
}
String? proxy = socksProxyAddress;
if ((proxy?.isEmpty ?? true) && CakeTor.instance.enabled) {
proxy = "${InternetAddress.loopbackIPv4.address}:${CakeTor.instance.port}";
}
printV("proxy: $proxy");
// if ((proxy?.isEmpty ?? true) && Tor.instance.enabled) {
// proxy = "${InternetAddress.loopbackIPv4.address}:${Tor.instance.port}";
// }
if (proxy == null) {
return false;
}
@ -296,9 +305,13 @@ class Node extends HiveObject with Keyable {
// you try to communicate with it
Future<bool> requestElectrumServer() async {
try {
final ProxySocket socket;
socket = await ProxyWrapper().getSocksSocket(useSSL ?? false, uri.host, uri.port);
final Socket socket;
if (useSSL == true) {
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();
return true;
@ -309,8 +322,8 @@ class Node extends HiveObject with Keyable {
Future<bool> requestNanoNode() async {
try {
final response = await ProxyWrapper().post(
clearnetUri: uri,
final response = await http.post(
uri,
headers: {"Content-Type": "application/json", "nano-app": "cake-wallet"},
body: jsonEncode(
{
@ -319,8 +332,7 @@ class Node extends HiveObject with Keyable {
},
),
);
final data = jsonDecode(response.body);
final data = await jsonDecode(response.body);
if (response.statusCode != 200 ||
data["error"] != null ||
data["balance"] == null ||
@ -336,14 +348,13 @@ class Node extends HiveObject with Keyable {
Future<bool> requestEthereumServer() async {
try {
final req = await ProxyWrapper().getHttpClient()
.getUrl(uri,)
.timeout(Duration(seconds: 15));
final response = await req.close();
final response = await http.get(
uri,
headers: {'Content-Type': 'application/json'},
);
return response.statusCode >= 200 && response.statusCode < 300;
} catch (err) {
printV("Failed to request ethereum server: $err");
} catch (_) {
return false;
}
}
@ -451,7 +462,7 @@ class DaemonRpc {
/// Perform a JSON-RPC call with Digest Authentication.
Future<Map<String, dynamic>> call(String method, Map<String, dynamic> params) async {
final client = ProxyWrapper().getHttpIOClient();
final http.Client client = http.Client();
final DigestAuth digestAuth = DigestAuth(username, password);
// Initial request to get the `WWW-Authenticate` header.

View file

@ -1,19 +1,20 @@
import 'dart:convert';
import 'package:cw_core/utils/proxy_wrapper.dart';
import 'package:http/http.dart';
import 'package:on_chain/solana/solana.dart';
class SolanaRPCHTTPService implements SolanaJSONRPCService {
SolanaRPCHTTPService(
{required this.url,
this.defaultRequestTimeout = const Duration(seconds: 30)});
{required this.url, Client? client, this.defaultRequestTimeout = const Duration(seconds: 30)})
: client = client ?? Client();
@override
final String url;
final Client client;
final Duration defaultRequestTimeout;
Future<Map<String, dynamic>> call(SolanaRequestDetails params,
[Duration? timeout]) async {
final response = await ProxyWrapper().post(
clearnetUri: Uri.parse(url),
@override
Future<Map<String, dynamic>> call(SolanaRequestDetails params, [Duration? timeout]) async {
final response = await client.post(
Uri.parse(url),
body: params.toRequestBody(),
headers: {
'Content-Type': 'application/json',

View file

@ -1,29 +0,0 @@
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,
});
}

View file

@ -1,63 +0,0 @@
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,),
);
}
}

View file

@ -1,17 +0,0 @@
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,
}) {}
}

View file

@ -1,47 +0,0 @@
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;
}

View file

@ -1,34 +0,0 @@
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,
);
}
}

View file

@ -1,34 +0,0 @@
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,
);
}
}

View file

@ -1,36 +0,0 @@
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,
);
}
}

View file

@ -1,447 +0,0 @@
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();
}

View file

@ -1,38 +0,0 @@
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();
}
}

View file

@ -1,73 +0,0 @@
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);
}
}

View file

@ -1,21 +0,0 @@
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();
}

View file

@ -1,21 +0,0 @@
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 {}
}

View file

@ -26,18 +26,18 @@ packages:
dependency: transitive
description:
name: args
sha256: d0481093c50b1da8910eb0bb301626d4d8eb7284aa739614d2b394ee09e3ea04
sha256: bf9f5caeea8d8fe6721a9c358dd8a5c1947b27f1cfaa18b39c301273594919e6
url: "https://pub.dev"
source: hosted
version: "2.7.0"
version: "2.6.0"
asn1lib:
dependency: transitive
description:
name: asn1lib
sha256: "1c296cd268f486cabcc3930e9b93a8133169305f18d722916e675959a88f6d2c"
sha256: "4bae5ae63e6d6dd17c4aac8086f3dec26c0236f6a0f03416c6c19d830c367cf5"
url: "https://pub.dev"
source: hosted
version: "1.5.9"
version: "1.5.8"
async:
dependency: transitive
description:
@ -67,50 +67,50 @@ packages:
dependency: transitive
description:
name: build
sha256: cef23f1eda9b57566c81e2133d196f8e3df48f244b317368d65c5943d91148f0
sha256: "80184af8b6cb3e5c1c4ec6d8544d27711700bc3e6d2efad04238c7b5290889f0"
url: "https://pub.dev"
source: hosted
version: "2.4.2"
version: "2.4.1"
build_config:
dependency: transitive
description:
name: build_config
sha256: "4ae2de3e1e67ea270081eaee972e1bd8f027d459f249e0f1186730784c2e7e33"
sha256: bf80fcfb46a29945b423bd9aad884590fb1dc69b330a4d4700cac476af1708d1
url: "https://pub.dev"
source: hosted
version: "1.1.2"
version: "1.1.1"
build_daemon:
dependency: transitive
description:
name: build_daemon
sha256: "8e928697a82be082206edb0b9c99c5a4ad6bc31c9e9b8b2f291ae65cd4a25daa"
sha256: "79b2aef6ac2ed00046867ed354c88778c9c0f029df8a20fe10b5436826721ef9"
url: "https://pub.dev"
source: hosted
version: "4.0.4"
version: "4.0.2"
build_resolvers:
dependency: "direct dev"
description:
name: build_resolvers
sha256: b9e4fda21d846e192628e7a4f6deda6888c36b5b69ba02ff291a01fd529140f0
sha256: "339086358431fa15d7eca8b6a36e5d783728cf025e559b834f4609a1fcfb7b0a"
url: "https://pub.dev"
source: hosted
version: "2.4.4"
version: "2.4.2"
build_runner:
dependency: "direct dev"
description:
name: build_runner
sha256: "058fe9dce1de7d69c4b84fada934df3e0153dd000758c4d65964d0166779aa99"
sha256: "028819cfb90051c6b5440c7e574d1896f8037e3c96cf17aaeb054c9311cfbf4d"
url: "https://pub.dev"
source: hosted
version: "2.4.15"
version: "2.4.13"
build_runner_core:
dependency: transitive
description:
name: build_runner_core
sha256: "22e3aa1c80e0ada3722fe5b63fd43d9c8990759d0a2cf489c8c5d7b2bdebc021"
sha256: f8126682b87a7282a339b871298cc12009cb67109cfa1614d6436fb0289193e0
url: "https://pub.dev"
source: hosted
version: "8.0.0"
version: "7.3.2"
built_collection:
dependency: transitive
description:
@ -123,10 +123,10 @@ packages:
dependency: transitive
description:
name: built_value
sha256: "082001b5c3dc495d4a42f1d5789990505df20d8547d42507c29050af6933ee27"
sha256: "8b158ab94ec6913e480dc3f752418348b5ae099eb75868b5f4775f0572999c61"
url: "https://pub.dev"
source: hosted
version: "8.10.1"
version: "8.9.4"
cake_backup:
dependency: "direct main"
description:
@ -212,10 +212,10 @@ packages:
dependency: transitive
description:
name: dart_style
sha256: "7306ab8a2359a48d22310ad823521d723acfed60ee1f7e37388e8986853b6820"
sha256: "7856d364b589d1f08986e140938578ed36ed948581fbc3bc9aef1805039ac5ab"
url: "https://pub.dev"
source: hosted
version: "2.3.8"
version: "2.3.7"
decimal:
dependency: "direct main"
description:
@ -326,10 +326,10 @@ packages:
dependency: "direct main"
description:
name: http
sha256: "2c11f3f94c687ee9bad77c171151672986360b2b001d109814ee7140b2cf261b"
sha256: fe7ab022b76f3034adc518fb6ea04a82387620e19977665ea18d30a1cf43442f
url: "https://pub.dev"
source: hosted
version: "1.4.0"
version: "1.3.0"
http_multi_server:
dependency: transitive
description:
@ -342,10 +342,10 @@ packages:
dependency: transitive
description:
name: http_parser
sha256: "178d74305e7866013777bab2c3d8726205dc5a4dd935297175b19a23a2e66571"
sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b"
url: "https://pub.dev"
source: hosted
version: "4.1.2"
version: "4.0.2"
intl:
dependency: "direct main"
description:
@ -462,10 +462,10 @@ packages:
dependency: "direct dev"
description:
name: mobx_codegen
sha256: e0abbbc651a69550440f6b65c99ec222a1e2a4afd7baec8ba0f3088c7ca582a8
sha256: "990da80722f7d7c0017dec92040b31545d625b15d40204c36a1e63d167c73cdc"
url: "https://pub.dev"
source: hosted
version: "2.7.1"
version: "2.7.0"
nested:
dependency: transitive
description:
@ -479,7 +479,7 @@ packages:
description:
path: "."
ref: cake-update-v2
resolved-ref: "096865a8c6b89c260beadfec04f7e184c40a3273"
resolved-ref: "93440dc5126369b873ca1fccc13c3c1240b1c5c2"
url: "https://github.com/cake-tech/on_chain.git"
source: git
version: "3.7.0"
@ -487,10 +487,10 @@ packages:
dependency: transitive
description:
name: package_config
sha256: f096c55ebb7deb7e384101542bfba8c52696c1b56fca2eb62827989ef2353bbc
sha256: "92d4488434b520a62570293fbd33bb556c7d49230791c1b4bbd973baf6d2dc67"
url: "https://pub.dev"
source: hosted
version: "2.2.0"
version: "2.1.1"
path:
dependency: transitive
description:
@ -511,10 +511,10 @@ packages:
dependency: transitive
description:
name: path_provider_android
sha256: d0d310befe2c8ab9e7f393288ccbb11b60c019c6b5afc21973eeee4dda2b35e9
sha256: "4adf4fd5423ec60a29506c76581bc05854c55e3a0b72d35bb28d661c9686edf2"
url: "https://pub.dev"
source: hosted
version: "2.2.17"
version: "2.2.15"
path_provider_foundation:
dependency: transitive
description:
@ -583,26 +583,26 @@ packages:
dependency: transitive
description:
name: provider
sha256: "4abbd070a04e9ddc287673bf5a030c7ca8b685ff70218720abab8b092f53dd84"
sha256: c8a055ee5ce3fd98d6fc872478b03823ffdb448699c6ebdbbc71d59b596fd48c
url: "https://pub.dev"
source: hosted
version: "6.1.5"
version: "6.1.2"
pub_semver:
dependency: transitive
description:
name: pub_semver
sha256: "5bfcf68ca79ef689f8990d1160781b4bad40a3bd5e5218ad4076ddb7f4081585"
sha256: "7b3cfbf654f3edd0c6298ecd5be782ce997ddf0e00531b9464b55245185bbbbd"
url: "https://pub.dev"
source: hosted
version: "2.2.0"
version: "2.1.5"
pubspec_parse:
dependency: transitive
description:
name: pubspec_parse
sha256: "0560ba233314abbed0a48a2956f7f022cce7c3e1e73df540277da7544cad4082"
sha256: "81876843eb50dc2e1e5b151792c9a985c5ed2536914115ed04e9c8528f6647b0"
url: "https://pub.dev"
source: hosted
version: "1.5.0"
version: "1.4.0"
rational:
dependency: transitive
description:
@ -615,18 +615,18 @@ packages:
dependency: transitive
description:
name: shelf
sha256: e7dd780a7ffb623c57850b33f43309312fc863fb6aa3d276a754bb299839ef12
sha256: ad29c505aee705f41a4d8963641f91ac4cee3c8fad5947e033390a7bd8180fa4
url: "https://pub.dev"
source: hosted
version: "1.4.2"
version: "1.4.1"
shelf_web_socket:
dependency: transitive
description:
name: shelf_web_socket
sha256: "3632775c8e90d6c9712f883e633716432a27758216dfb61bd86a8321c0580925"
sha256: cc36c297b52866d203dbf9332263c94becc2fe0ceaa9681d07b6ef9807023b67
url: "https://pub.dev"
source: hosted
version: "3.0.0"
version: "2.0.1"
sky_engine:
dependency: transitive
description: flutter
@ -635,21 +635,11 @@ packages:
socks5_proxy:
dependency: "direct main"
description:
path: "."
ref: "27ad7c2efae8d7460325c74b90f660085cbd0685"
resolved-ref: "27ad7c2efae8d7460325c74b90f660085cbd0685"
url: "https://github.com/LacticWhale/socks_dart"
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"
name: socks5_proxy
sha256: "616818a0ea1064a4823b53c9f7eaf8da64ed82dcd51ed71371c7e54751ed5053"
url: "https://pub.dev"
source: hosted
version: "1.0.6"
source_gen:
dependency: transitive
description:
@ -726,19 +716,10 @@ packages:
dependency: transitive
description:
name: timing
sha256: "70a3b636575d4163c477e6de42f247a23b315ae20e86442bebe32d3cabf61c32"
sha256: "62ee18aca144e4a9f29d212f5a4c6a053be252b895ab14b5821996cff4ed90fe"
url: "https://pub.dev"
source: hosted
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"
version: "1.0.2"
tuple:
dependency: transitive
description:
@ -799,18 +780,18 @@ packages:
dependency: transitive
description:
name: web_socket
sha256: "34d64019aa8e36bf9842ac014bb5d2f5586ca73df5e4d9bf5c936975cae6982c"
sha256: "3c12d96c0c9a4eec095246debcea7b86c0324f22df69893d538fcc6f1b8cce83"
url: "https://pub.dev"
source: hosted
version: "1.0.1"
version: "0.1.6"
web_socket_channel:
dependency: transitive
description:
name: web_socket_channel
sha256: d645757fb0f4773d602444000a8131ff5d48c9e47adfe9772652dd1a4f2d45c8
sha256: "0b8e2457400d8a859b7b2030786835a28a8e80836ef64402abef392ff4f1d0e5"
url: "https://pub.dev"
source: hosted
version: "3.0.3"
version: "3.0.2"
xdg_directories:
dependency: transitive
description:
@ -828,5 +809,5 @@ packages:
source: hosted
version: "3.1.3"
sdks:
dart: ">=3.6.0 <4.0.0"
flutter: ">=3.27.0"
dart: ">=3.5.0 <4.0.0"
flutter: ">=3.24.0"

View file

@ -25,29 +25,22 @@ dependencies:
url: https://github.com/cake-tech/cake_backup.git
ref: main
version: 1.0.0
socks5_proxy:
git:
url: https://github.com/LacticWhale/socks_dart
ref: 27ad7c2efae8d7460325c74b90f660085cbd0685
socks5_proxy: ^1.0.4
unorm_dart: ^0.3.0
on_chain:
git:
url: https://github.com/cake-tech/on_chain.git
ref: cake-update-v2
socks_socket:
git:
url: https://github.com/sneurlax/socks_socket
ref: e6232c53c1595469931ababa878759a067c02e94
tor_binary:
git:
url: https://github.com/MrCyjaneK/flutter-tor_binary
ref: cb811c610871a9517d47134b87c2f590c15c96c5
# tor:
# git:
# url: https://github.com/cake-tech/tor.git
# ref: main
dev_dependencies:
flutter_test:
sdk: flutter
build_runner: ^2.4.15
build_resolvers: ^2.4.4
build_runner: ^2.4.7
build_resolvers: ^2.0.9
mobx_codegen: ^2.0.7
hive_generator: ^2.0.1

View file

@ -5,8 +5,6 @@ import 'package:path/path.dart' as p;
import 'package:cw_core/exceptions.dart';
import 'package:cw_core/transaction_direction.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/pending_transaction.dart';
import 'package:cw_decred/transaction_credentials.dart';
@ -309,10 +307,9 @@ abstract class DecredWalletBase
persistantPeer = addr;
await _libwallet.closeWallet(walletInfo.name);
final network = isTestnet ? "testnet" : "mainnet";
final dirPath = await pathForWalletDir(name: walletInfo.name, type: WalletType.decred);
final config = {
"name": walletInfo.name,
"datadir": dirPath,
"datadir": walletInfo.dirPath,
"net": network,
"unsyncedaddrs": true,
};
@ -614,7 +611,7 @@ abstract class DecredWalletBase
}
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);
if (entity is File) {

View file

@ -8,7 +8,6 @@ import 'package:cw_core/wallet_service.dart';
import 'package:cw_core/pathForWallet.dart';
import 'package:cw_core/wallet_info.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:path/path.dart';
import 'package:hive/hive.dart';
import 'package:collection/collection.dart';
import 'package:cw_core/unspent_coins_info.dart';
@ -58,93 +57,42 @@ class DecredWalletService extends WalletService<
@override
Future<DecredWallet> create(DecredNewWalletCredentials credentials, {bool? isTestnet}) async {
await this.init();
final dirPath = await pathForWalletDir(name: credentials.walletInfo!.name, type: getType());
final network = isTestnet == true ? testnet : mainnet;
final config = {
"name": credentials.walletInfo!.name,
"datadir": dirPath,
"datadir": credentials.walletInfo!.dirPath,
"pass": credentials.password!,
"net": network,
"net": isTestnet == true ? testnet : mainnet,
"unsyncedaddrs": true,
};
await libwallet!.createWallet(jsonEncode(config));
final di = DerivationInfo(
derivationPath: isTestnet == true ? seedRestorePathTestnet : seedRestorePath);
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!,
this.unspentCoinsInfoSource, libwallet!, closeLibwallet);
await wallet.init();
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
Future<DecredWallet> openWallet(String name, String password) async {
final walletInfo = walletInfoSource.values
.firstWhereOrNull((info) => info.id == WalletBase.idFor(name, getType()))!;
if (walletInfo.network == null || walletInfo.network == "") {
walletInfo.network = walletInfo.derivationInfo?.derivationPath == seedRestorePathTestnet ||
final network = walletInfo.derivationInfo?.derivationPath == seedRestorePathTestnet ||
walletInfo.derivationInfo?.derivationPath == pubkeyRestorePathTestnet
? testnet
: mainnet;
}
await this.init();
// Cake wallet version 4.27.0 and earlier gave a wallet dir that did not
// 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 walletDirExists = Directory(walletInfo.dirPath).existsSync();
if (!walletDirExists) {
walletInfo.dirPath = await pathForWalletDir(name: name, type: getType());
}
final config = {
"name": name,
"datadir": dirPath,
"net": walletInfo.network,
"name": walletInfo.name,
"datadir": walletInfo.dirPath,
"net": network,
"unsyncedaddrs": true,
};
await libwallet!.loadWallet(jsonEncode(config));
@ -179,11 +127,12 @@ class DecredWalletService extends WalletService<
await currentWallet.renameWalletFiles(newName);
final newDirPath = await pathForWalletDir(name: newName, type: getType());
final newWalletInfo = currentWalletInfo;
newWalletInfo.id = WalletBase.idFor(newName, getType());
newWalletInfo.name = newName;
newWalletInfo.dirPath = "";
newWalletInfo.path = "";
newWalletInfo.dirPath = newDirPath;
newWalletInfo.network = network;
await walletInfoSource.put(currentWalletInfo.key, newWalletInfo);
}
@ -192,23 +141,18 @@ class DecredWalletService extends WalletService<
Future<DecredWallet> restoreFromSeed(DecredRestoreWalletFromSeedCredentials credentials,
{bool? isTestnet}) async {
await this.init();
final network = isTestnet == true ? testnet : mainnet;
final dirPath = await pathForWalletDir(name: credentials.walletInfo!.name, type: getType());
final config = {
"name": credentials.walletInfo!.name,
"datadir": dirPath,
"datadir": credentials.walletInfo!.dirPath,
"pass": credentials.password!,
"mnemonic": credentials.mnemonic,
"net": network,
"net": isTestnet == true ? testnet : mainnet,
"unsyncedaddrs": true,
};
await libwallet!.createWallet(jsonEncode(config));
final di = DerivationInfo(
derivationPath: isTestnet == true ? seedRestorePathTestnet : seedRestorePath);
credentials.walletInfo!.derivationInfo = di;
credentials.walletInfo!.network = network;
credentials.walletInfo!.dirPath = "";
credentials.walletInfo!.path = "";
final wallet = DecredWallet(credentials.walletInfo!, credentials.password!,
this.unspentCoinsInfoSource, libwallet!, closeLibwallet);
await wallet.init();
@ -221,22 +165,17 @@ class DecredWalletService extends WalletService<
Future<DecredWallet> restoreFromKeys(DecredRestoreWalletFromPubkeyCredentials credentials,
{bool? isTestnet}) async {
await this.init();
final network = isTestnet == true ? testnet : mainnet;
final dirPath = await pathForWalletDir(name: credentials.walletInfo!.name, type: getType());
final config = {
"name": credentials.walletInfo!.name,
"datadir": dirPath,
"datadir": credentials.walletInfo!.dirPath,
"pubkey": credentials.pubkey,
"net": network,
"net": isTestnet == true ? testnet : mainnet,
"unsyncedaddrs": true,
};
await libwallet!.createWatchOnlyWallet(jsonEncode(config));
final di = DerivationInfo(
derivationPath: isTestnet == true ? pubkeyRestorePathTestnet : pubkeyRestorePath);
credentials.walletInfo!.derivationInfo = di;
credentials.walletInfo!.network = network;
credentials.walletInfo!.dirPath = "";
credentials.walletInfo!.path = "";
final wallet = DecredWallet(credentials.walletInfo!, credentials.password!,
this.unspentCoinsInfoSource, libwallet!, closeLibwallet);
await wallet.init();

View file

@ -91,26 +91,26 @@ packages:
dependency: "direct dev"
description:
name: build_resolvers
sha256: b9e4fda21d846e192628e7a4f6deda6888c36b5b69ba02ff291a01fd529140f0
sha256: "339086358431fa15d7eca8b6a36e5d783728cf025e559b834f4609a1fcfb7b0a"
url: "https://pub.dev"
source: hosted
version: "2.4.4"
version: "2.4.2"
build_runner:
dependency: "direct dev"
description:
name: build_runner
sha256: "058fe9dce1de7d69c4b84fada934df3e0153dd000758c4d65964d0166779aa99"
sha256: "028819cfb90051c6b5440c7e574d1896f8037e3c96cf17aaeb054c9311cfbf4d"
url: "https://pub.dev"
source: hosted
version: "2.4.15"
version: "2.4.13"
build_runner_core:
dependency: transitive
description:
name: build_runner_core
sha256: "22e3aa1c80e0ada3722fe5b63fd43d9c8990759d0a2cf489c8c5d7b2bdebc021"
sha256: f8126682b87a7282a339b871298cc12009cb67109cfa1614d6436fb0289193e0
url: "https://pub.dev"
source: hosted
version: "8.0.0"
version: "7.3.2"
built_collection:
dependency: transitive
description:
@ -666,21 +666,11 @@ packages:
socks5_proxy:
dependency: transitive
description:
path: "."
ref: "27ad7c2efae8d7460325c74b90f660085cbd0685"
resolved-ref: "27ad7c2efae8d7460325c74b90f660085cbd0685"
url: "https://github.com/LacticWhale/socks_dart"
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"
name: socks5_proxy
sha256: "616818a0ea1064a4823b53c9f7eaf8da64ed82dcd51ed71371c7e54751ed5053"
url: "https://pub.dev"
source: hosted
version: "1.0.6"
source_gen:
dependency: transitive
description:
@ -761,15 +751,6 @@ packages:
url: "https://pub.dev"
source: hosted
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:
dependency: transitive
description:
@ -867,5 +848,5 @@ packages:
source: hosted
version: "2.2.2"
sdks:
dart: ">=3.6.0 <4.0.0"
dart: ">=3.5.0 <4.0.0"
flutter: ">=3.24.0"

View file

@ -19,8 +19,8 @@ dependencies:
dev_dependencies:
flutter_test:
sdk: flutter
build_runner: ^2.4.15
build_resolvers: ^2.4.4
build_runner: ^2.1.11
build_resolvers: ^2.0.9
mobx_codegen: ^2.0.7
hive_generator: ^2.0.1
ffigen: ^16.1.0

View file

@ -1,170 +0,0 @@
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;
}
}
}

File diff suppressed because one or more lines are too long

View file

@ -19,7 +19,7 @@ class EthereumClient extends EVMChainClient {
Future<List<EVMChainTransactionModel>> fetchTransactions(String address,
{String? contractAddress}) async {
try {
final response = await client.get(Uri.https("api.etherscan.io", "/v2/api", {
final response = await httpClient.get(Uri.https("api.etherscan.io", "/v2/api", {
"chainid": "$chainId",
"module": "account",
"action": contractAddress != null ? "tokentx" : "txlist",
@ -51,7 +51,7 @@ class EthereumClient extends EVMChainClient {
@override
Future<List<EVMChainTransactionModel>> fetchInternalTransactions(String address) async {
try {
final response = await client.get(Uri.https("api.etherscan.io", "/v2/api", {
final response = await httpClient.get(Uri.https("api.etherscan.io", "/v2/api", {
"chainid": "$chainId",
"module": "account",
"action": "txlistinternal",

View file

@ -6,7 +6,7 @@ author: Cake Wallet
homepage: https://cakewallet.com
environment:
sdk: ^3.5.0
sdk: '>=2.18.2 <3.0.0'
flutter: ">=1.17.0"
dependencies:
@ -29,7 +29,7 @@ dependency_overrides:
dev_dependencies:
flutter_test:
sdk: flutter
build_runner: ^2.4.15
build_runner: ^2.4.7
flutter:
# assets:

View file

@ -5,7 +5,6 @@ import 'dart:developer';
import 'package:cw_core/crypto_currency.dart';
import 'package:cw_core/erc20_token.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_priority.dart';
import 'package:cw_evm/evm_erc20_balance.dart';
@ -13,12 +12,13 @@ import 'package:cw_evm/pending_evm_chain_transaction.dart';
import 'package:cw_evm/.secrets.g.dart' as secrets;
import 'package:flutter/foundation.dart';
import 'package:hex/hex.dart' as hex;
import 'package:http/http.dart';
import 'package:web3dart/web3dart.dart';
import 'contract/erc20.dart';
abstract class EVMChainClient {
late final client = ProxyWrapper().getHttpIOClient();
final httpClient = Client();
Web3Client? _client;
//! To be overridden by all child classes
@ -47,7 +47,7 @@ abstract class EVMChainClient {
}
_client =
Web3Client(isModifiedNodeUri ? rpcUri!.toString() : node.uri.toString(), client);
Web3Client(isModifiedNodeUri ? rpcUri!.toString() : node.uri.toString(), httpClient);
return true;
} catch (e) {
@ -101,7 +101,6 @@ abstract class EVMChainClient {
String? contractAddress,
EtherAmount? gasPrice,
EtherAmount? maxFeePerGas,
Uint8List? data,
}) async {
try {
if (contractAddress == null) {
@ -109,6 +108,7 @@ abstract class EVMChainClient {
sender: senderAddress,
to: toAddress,
value: value,
// maxFeePerGas: maxFeePerGas,
);
return estimatedGas.toInt();
@ -124,7 +124,7 @@ abstract class EVMChainClient {
final gasEstimate = await _client!.estimateGas(
sender: senderAddress,
to: EthereumAddress.fromHex(contractAddress),
data: data ?? transfer.encodeCall([
data: transfer.encodeCall([
toAddress,
value.getInWei,
]),
@ -137,21 +137,6 @@ 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({
required Credentials privateKey,
required String toAddress,
@ -164,7 +149,6 @@ abstract class EVMChainClient {
required int exponent,
String? contractAddress,
String? data,
int? gasPrice,
}) async {
assert(currency == CryptoCurrency.eth ||
currency == CryptoCurrency.maticpoly ||
@ -180,7 +164,6 @@ abstract class EVMChainClient {
data: data != null ? hexToBytes(data) : null,
maxGas: estimatedGasUnits,
maxFeePerGas: EtherAmount.fromInt(EtherUnit.wei, maxFeePerGas),
gasPrice: gasPrice != null ? EtherAmount.fromInt(EtherUnit.wei, gasPrice) : null,
);
Uint8List signedTransaction;
@ -215,52 +198,6 @@ 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({
required EthereumAddress from,
required EthereumAddress to,
@ -356,7 +293,7 @@ abstract class EVMChainClient {
},
);
final response = await client.get(
final response = await httpClient.get(
uri,
headers: {
"Accept": "application/json",

View file

@ -22,32 +22,3 @@ class EVMChainTransactionFeesException implements Exception {
@override
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;
}

View file

@ -2,7 +2,6 @@ import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'dart:math';
import 'dart:typed_data';
import 'package:bip32/bip32.dart' as bip32;
import 'package:bip39/bip39.dart' as bip39;
@ -198,8 +197,8 @@ abstract class EVMChainWalletBase
for (var token in erc20Currencies) {
bool isPotentialScam = false;
bool isWhitelisted = getDefaultTokenContractAddresses
.any((element) => element.toLowerCase() == token.contractAddress.toLowerCase());
bool isWhitelisted =
getDefaultTokenContractAddresses.any((element) => element == token.contractAddress);
final tokenSymbol = token.title.toUpperCase();
@ -214,16 +213,6 @@ abstract class EVMChainWalletBase
token.iconPath = null;
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();
}
}
}
@ -266,72 +255,36 @@ abstract class EVMChainWalletBase
required String? contractAddress,
required String receivingAddressHex,
required TransactionPriority priority,
Uint8List? data,
}) async {
try {
if (priority is EVMChainTransactionPriority) {
final priorityFee = EtherAmount.fromInt(EtherUnit.gwei, priority.tip).getInWei.toInt();
int maxFeePerGas;
int adjustedGasPrice;
bool isPolygon = _client.chainId == 137;
if (gasBaseFee != null) {
// MaxFeePerGas with EIP1559;
maxFeePerGas = gasBaseFee! + priorityFee;
} else {
// MaxFeePerGas with 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;
maxFeePerGas = gasPrice;
}
final estimatedGas = await _client.getEstimatedGasUnitsForTransaction(
contractAddress: contractAddress,
senderAddress: _evmChainPrivateKey.address,
value: EtherAmount.fromBigInt(EtherUnit.wei, amount!),
gasPrice: EtherAmount.fromInt(EtherUnit.wei, adjustedGasPrice),
gasPrice: EtherAmount.fromInt(EtherUnit.wei, gasPrice),
toAddress: EthereumAddress.fromHex(receivingAddressHex),
maxFeePerGas: EtherAmount.fromInt(EtherUnit.wei, maxFeePerGas),
data: data,
);
final totalGasFee = estimatedGas * adjustedGasPrice;
final totalGasFee = estimatedGas * maxFeePerGas;
return GasParamsHandler(
estimatedGasUnits: estimatedGas,
estimatedGasFee: totalGasFee,
maxFeePerGas: maxFeePerGas,
gasPrice: adjustedGasPrice,
gasPrice: gasPrice,
);
}
return GasParamsHandler.zero();
@ -520,46 +473,11 @@ abstract class EVMChainWalletBase
contractAddress:
transactionCurrency is Erc20Token ? transactionCurrency.contractAddress : null,
data: hexOpReturnMemo,
gasPrice: maxFeePerGasForTransaction,
);
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 {
try {
if (_isTransactionUpdating) {

View file

@ -11,7 +11,6 @@ class PendingEVMChainTransaction with PendingTransaction {
final BigInt fee;
final String amount;
final int exponent;
final bool isInfiniteApproval;
PendingEVMChainTransaction({
required this.sendTransaction,
@ -19,12 +18,10 @@ class PendingEVMChainTransaction with PendingTransaction {
required this.fee,
required this.amount,
required this.exponent,
this.isInfiniteApproval = false,
});
@override
String get amountFormatted {
if (isInfiniteApproval) return "";
final _amount = (BigInt.parse(amount) / BigInt.from(pow(10, exponent))).toString();
return _amount.substring(0, min(10, _amount.length));
}

View file

@ -30,7 +30,6 @@ dependencies:
git:
url: https://github.com/cake-tech/ledger-flutter-plus-plugins
path: packages/ledger-ethereum
ref: f4761cd5171d4c1e2e42fd3298261650539fb2db
dependency_overrides:
web3dart:
@ -42,9 +41,9 @@ dependency_overrides:
dev_dependencies:
flutter_test:
sdk: flutter
build_runner: ^2.4.15
build_runner: ^2.4.7
mobx_codegen: ^2.0.7
hive_generator: ^2.0.1
hive_generator: ^1.1.3
flutter_lints: ^2.0.0
flutter:

View file

@ -5,11 +5,13 @@ class PendingTransactionDescription {
required this.fee,
required this.hash,
required this.hex,
required this.txKey,
required this.pointerAddress});
final int amount;
final int fee;
final String hash;
final String hex;
final String txKey;
final int pointerAddress;
}

View file

@ -1,4 +1,3 @@
import 'dart:async';
import 'dart:ffi';
import 'dart:isolate';
@ -195,12 +194,14 @@ Future<PendingTransactionDescription> createTransactionSync(
final rFee = pendingTx.fee();
final rHash = pendingTx.txid('');
final rHex = pendingTx.hex('');
final rTxKey = rHash;
return PendingTransactionDescription(
amount: rAmt,
fee: rFee,
hash: rHash,
hex: rHex,
txKey: rTxKey,
pointerAddress: pendingTx.ffiAddress(),
);
}
@ -245,6 +246,7 @@ Future<PendingTransactionDescription> createTransactionMultDest(
fee: tx.fee(),
hash: tx.txid(''),
hex: tx.hex(''),
txKey: tx.txid(''),
pointerAddress: tx.ffiAddress(),
);
}
@ -261,7 +263,6 @@ Future<String?> commitTransaction({required Wallet2PendingTransaction tx, requir
filename: '',
overwrite: false,
);
return null;
});
String? error = (() {
@ -284,12 +285,11 @@ Future<String?> commitTransaction({required Wallet2PendingTransaction tx, requir
if (error != null && error != "no tx keys found for this txid") {
throw CreationTransactionException(message: error);
}
unawaited(() async {
storeSync(force: true);
await Future.delayed(Duration(seconds: 5));
storeSync(force: true);
}());
return Future.value(txCommit);
if (useUR) {
return Future.value(txCommit as String?);
} else {
return Future.value(null);
}
}
class Transaction {

View file

@ -116,17 +116,12 @@ String getSeedLegacy(String? language) {
}
String getPassphrase() {
return currentWallet?.getCacheAttribute(key: "cakewallet.passphrase") ?? "";
return currentWallet!.getCacheAttribute(key: "cakewallet.passphrase");
}
Map<int, Map<int, Map<int, String>>> addressCache = {};
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)}");
// 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) {
@ -151,25 +146,7 @@ int getUnlockedBalance({int accountIndex = 0}) =>
int getCurrentHeight() => currentWallet?.blockChainHeight() ?? 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;
}
int getNodeHeightSync() => currentWallet?.daemonBlockChainHeight() ?? 0;
bool isConnectedSync() => currentWallet?.connected() != 0;
@ -225,6 +202,7 @@ Future<bool> setupNodeSync(
}
void startRefreshSync() {
currentWallet!.refreshAsync();
currentWallet!.startRefresh();
}
@ -250,7 +228,7 @@ void storeSync({bool force = false}) async {
return monero.Wallet_synchronized(Pointer.fromAddress(addr));
});
if (lastStorePointer == addr &&
lastStoreHeight + 75000 > currentWallet!.blockChainHeight() &&
lastStoreHeight + 5000 > currentWallet!.blockChainHeight() &&
!synchronized &&
!force) {
return;
@ -277,13 +255,13 @@ void closeCurrentWallet() {
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 {
SyncListener(this.onNewBlock, this.onNewTransaction)

View file

@ -17,7 +17,6 @@ import 'package:cw_core/pending_transaction.dart';
import 'package:cw_core/sync_status.dart';
import 'package:cw_core/transaction_direction.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/wallet_base.dart';
import 'package:cw_core/wallet_info.dart';
@ -208,14 +207,6 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
@override
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 {
syncStatus = ConnectingSyncStatus();
await monero_wallet.setupNodeSync(
@ -225,7 +216,7 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
useSSL: node.isSSL,
isLightWallet: false,
// FIXME: hardcoded value
socksProxyAddress: socksProxy);
socksProxyAddress: node.socksProxyAddress);
await monero_wallet.setTrustedDaemon(node.trusted);
syncStatus = ConnectedSyncStatus();

View file

@ -31,6 +31,8 @@ class PendingMoneroTransaction with PendingTransaction {
@override
String get hex => pendingTransactionDescription.hex;
String get txKey => pendingTransactionDescription.txKey;
@override
String get amountFormatted => AmountConverter.amountIntToString(
CryptoCurrency.xmr, pendingTransactionDescription.amount);

View file

@ -66,11 +66,11 @@ packages:
dependency: transitive
description:
path: "."
ref: cake-update-v4
resolved-ref: "437dadd0bd9bf73ec6a551299577799341f6486a"
ref: cake-update-v2
resolved-ref: "59fdf29d72068e0522a96a8953ed7272833a9f57"
url: "https://github.com/cake-tech/blockchain_utils"
source: git
version: "4.3.0"
version: "3.3.0"
bluez:
dependency: transitive
description:
@ -131,18 +131,18 @@ packages:
dependency: "direct dev"
description:
name: build_runner
sha256: "058fe9dce1de7d69c4b84fada934df3e0153dd000758c4d65964d0166779aa99"
sha256: "028819cfb90051c6b5440c7e574d1896f8037e3c96cf17aaeb054c9311cfbf4d"
url: "https://pub.dev"
source: hosted
version: "2.4.15"
version: "2.4.13"
build_runner_core:
dependency: transitive
description:
name: build_runner_core
sha256: "22e3aa1c80e0ada3722fe5b63fd43d9c8990759d0a2cf489c8c5d7b2bdebc021"
sha256: "6d6ee4276b1c5f34f21fdf39425202712d2be82019983d52f351c94aafbc2c41"
url: "https://pub.dev"
source: hosted
version: "8.0.0"
version: "7.2.10"
built_collection:
dependency: transitive
description:
@ -599,10 +599,10 @@ packages:
description:
path: "."
ref: cake-update-v2
resolved-ref: "084fb7bf13ec42d74f26ac08c883ce07c10fca7e"
resolved-ref: "93440dc5126369b873ca1fccc13c3c1240b1c5c2"
url: "https://github.com/cake-tech/on_chain.git"
source: git
version: "6.2.0"
version: "3.7.0"
package_config:
dependency: transitive
description:
@ -779,21 +779,11 @@ packages:
socks5_proxy:
dependency: transitive
description:
path: "."
ref: "27ad7c2efae8d7460325c74b90f660085cbd0685"
resolved-ref: "27ad7c2efae8d7460325c74b90f660085cbd0685"
url: "https://github.com/LacticWhale/socks_dart"
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"
name: socks5_proxy
sha256: "616818a0ea1064a4823b53c9f7eaf8da64ed82dcd51ed71371c7e54751ed5053"
url: "https://pub.dev"
source: hosted
version: "1.0.6"
source_gen:
dependency: transitive
description:
@ -870,19 +860,10 @@ packages:
dependency: transitive
description:
name: timing
sha256: "70a3b636575d4163c477e6de42f247a23b315ae20e86442bebe32d3cabf61c32"
sha256: "62ee18aca144e4a9f29d212f5a4c6a053be252b895ab14b5821996cff4ed90fe"
url: "https://pub.dev"
source: hosted
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"
version: "1.0.2"
tuple:
dependency: transitive
description:

View file

@ -34,8 +34,8 @@ dependencies:
dev_dependencies:
flutter_test:
sdk: flutter
build_runner: ^2.4.15
build_resolvers: ^2.4.4
build_runner: ^2.4.7
build_resolvers: ^2.0.9
mobx_codegen: ^2.0.7
mockito: ^5.4.5
hive_generator: ^2.0.1

View file

@ -3,11 +3,11 @@ import 'dart:convert';
import 'package:cw_core/nano_account_info_response.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_core/n2_node.dart';
import 'package:cw_nano/nano_balance.dart';
import 'package:cw_nano/nano_transaction_model.dart';
import 'package:http/http.dart' as http;
import 'package:cw_core/node.dart';
import 'package:nanoutil/nanoutil.dart';
import 'package:shared_preferences/shared_preferences.dart';
@ -66,8 +66,8 @@ class NanoClient {
}
Future<NanoBalance> getBalance(String address) async {
final response = await ProxyWrapper().post(
clearnetUri: _node!.uri,
final response = await http.post(
_node!.uri,
headers: getHeaders(_node!.uri.host),
body: jsonEncode(
{
@ -76,8 +76,7 @@ class NanoClient {
},
),
);
final data = jsonDecode(response.body) as Map<String, dynamic>;
final data = await jsonDecode(response.body);
if (response.statusCode != 200 ||
data["error"] != null ||
data["balance"] == null ||
@ -94,8 +93,8 @@ class NanoClient {
Future<AccountInfoResponse?> getAccountInfo(String address) async {
try {
final response = await ProxyWrapper().post(
clearnetUri: _node!.uri,
final response = await http.post(
_node!.uri,
headers: getHeaders(_node!.uri.host),
body: jsonEncode(
{
@ -105,9 +104,8 @@ class NanoClient {
},
),
);
final data = jsonDecode(response.body) as Map<String, dynamic>;
return AccountInfoResponse.fromJson(data);
final data = await jsonDecode(response.body);
return AccountInfoResponse.fromJson(data as Map<String, dynamic>);
} catch (e) {
printV("error while getting account info $e");
return null;
@ -116,8 +114,8 @@ class NanoClient {
Future<BlockContentsResponse?> getBlockContents(String block) async {
try {
final response = await ProxyWrapper().post(
clearnetUri: _node!.uri,
final response = await http.post(
_node!.uri,
headers: getHeaders(_node!.uri.host),
body: jsonEncode(
{
@ -127,8 +125,7 @@ class NanoClient {
},
),
);
final data = jsonDecode(response.body) as Map<String, dynamic>;
final data = await jsonDecode(response.body);
return BlockContentsResponse.fromJson(data["contents"] as Map<String, dynamic>);
} catch (e) {
printV("error while getting block info $e");
@ -184,8 +181,8 @@ class NanoClient {
}
Future<String> requestWork(String hash) async {
final response = await ProxyWrapper().post(
clearnetUri: _powNode!.uri,
final response = await http.post(
_powNode!.uri,
headers: getHeaders(_powNode!.uri.host),
body: json.encode(
{
@ -194,9 +191,8 @@ class NanoClient {
},
),
);
if (response.statusCode == 200) {
final decoded = jsonDecode(response.body) as Map<String, dynamic>;
final Map<String, dynamic> decoded = json.decode(response.body) as Map<String, dynamic>;
if (decoded.containsKey("error")) {
throw Exception("Received error ${decoded["error"]}");
}
@ -228,13 +224,13 @@ class NanoClient {
"block": block,
});
final processResponse = await ProxyWrapper().post(
clearnetUri: _node!.uri,
final processResponse = await http.post(
_node!.uri,
headers: getHeaders(_node!.uri.host),
body: processBody,
);
final Map<String, dynamic> decoded = jsonDecode(processResponse.body) as Map<String, dynamic>;
final Map<String, dynamic> decoded = json.decode(processResponse.body) as Map<String, dynamic>;
if (decoded.containsKey("error")) {
throw Exception("Received error ${decoded["error"]}");
}
@ -427,11 +423,12 @@ class NanoClient {
"subtype": "receive",
"block": receiveBlock,
});
final processResponse = await ProxyWrapper().post(
clearnetUri: _node!.uri,
final processResponse = await http.post(
_node!.uri,
headers: getHeaders(_node!.uri.host),
body: processBody,
);
final Map<String, dynamic> decoded = json.decode(processResponse.body) as Map<String, dynamic>;
if (decoded.containsKey("error")) {
throw Exception("Received error ${decoded["error"]}");
@ -443,17 +440,16 @@ class NanoClient {
required String destinationAddress,
required String privateKey,
}) async {
final receivableResponse = await ProxyWrapper().post(
clearnetUri: _node!.uri,
final receivableResponse = await http.post(_node!.uri,
headers: getHeaders(_node!.uri.host),
body: jsonEncode({
"action": "receivable",
"account": destinationAddress,
"count": "-1",
"source": true,
}),
);
final receivableData = jsonDecode(receivableResponse.body) as Map<String, dynamic>;
}));
final receivableData = await jsonDecode(receivableResponse.body);
if (receivableData["blocks"] == "" || receivableData["blocks"] == null) {
return 0;
}
@ -496,18 +492,15 @@ class NanoClient {
Future<List<NanoTransactionModel>> fetchTransactions(String address) async {
try {
final response = await ProxyWrapper().post(
clearnetUri: _node!.uri,
final response = await http.post(_node!.uri,
headers: getHeaders(_node!.uri.host),
body: jsonEncode({
"action": "account_history",
"account": address,
"count": "100",
// "raw": true,
}),
);
final data = jsonDecode(response.body) as Map<String, dynamic>;
}));
final data = await jsonDecode(response.body);
final transactions = data["history"] is List ? data["history"] as List<dynamic> : [];
// Map the transactions list to NanoTransactionModel using the factory
@ -523,14 +516,13 @@ class NanoClient {
Future<List<N2Node>> getN2Reps() async {
final uri = Uri.parse(N2_REPS_ENDPOINT);
final response = await ProxyWrapper().post(
clearnetUri: uri,
final response = await http.post(
uri,
headers: getHeaders(uri.host),
body: jsonEncode({"action": "reps"}),
);
try {
final List<N2Node> nodes = (jsonDecode(response.body) as List<dynamic>)
final List<N2Node> nodes = (json.decode(response.body) as List<dynamic>)
.map((dynamic e) => N2Node.fromJson(e as Map<String, dynamic>))
.toList();
return nodes;
@ -541,8 +533,8 @@ class NanoClient {
Future<int> getRepScore(String rep) async {
final uri = Uri.parse(N2_REPS_ENDPOINT);
final response = await ProxyWrapper().post(
clearnetUri: uri,
final response = await http.post(
uri,
headers: getHeaders(uri.host),
body: jsonEncode({
"action": "rep_info",
@ -550,8 +542,7 @@ class NanoClient {
}),
);
try {
final N2Node node = N2Node.fromJson(jsonDecode(response.body) as Map<String, dynamic>);
final N2Node node = N2Node.fromJson(json.decode(response.body) as Map<String, dynamic>);
return node.score ?? 100;
} catch (error) {
return 100;

View file

@ -5,39 +5,34 @@ packages:
dependency: transitive
description:
name: _fe_analyzer_shared
sha256: "16e298750b6d0af7ce8a3ba7c18c69c3785d11b15ec83f6dcd0ad2a0009b3cab"
sha256: "4897882604d919befd350648c7f91926a9d5de99e67b455bf0917cc2362f4bb8"
url: "https://pub.dev"
source: hosted
version: "76.0.0"
_macros:
dependency: transitive
description: dart
source: sdk
version: "0.3.3"
version: "47.0.0"
analyzer:
dependency: transitive
description:
name: analyzer
sha256: "1f14db053a8c23e260789e9b0980fa27f2680dd640932cae5e1137cce0e46e1e"
sha256: "690e335554a8385bc9d787117d9eb52c0c03ee207a607e593de3c9d71b1cfe80"
url: "https://pub.dev"
source: hosted
version: "6.11.0"
version: "4.7.0"
args:
dependency: transitive
description:
name: args
sha256: d0481093c50b1da8910eb0bb301626d4d8eb7284aa739614d2b394ee09e3ea04
sha256: bf9f5caeea8d8fe6721a9c358dd8a5c1947b27f1cfaa18b39c301273594919e6
url: "https://pub.dev"
source: hosted
version: "2.7.0"
version: "2.6.0"
asn1lib:
dependency: transitive
description:
name: asn1lib
sha256: "1c296cd268f486cabcc3930e9b93a8133169305f18d722916e675959a88f6d2c"
sha256: "4bae5ae63e6d6dd17c4aac8086f3dec26c0236f6a0f03416c6c19d830c367cf5"
url: "https://pub.dev"
source: hosted
version: "1.5.9"
version: "1.5.8"
async:
dependency: transitive
description:
@ -91,50 +86,50 @@ packages:
dependency: transitive
description:
name: build
sha256: cef23f1eda9b57566c81e2133d196f8e3df48f244b317368d65c5943d91148f0
sha256: "80184af8b6cb3e5c1c4ec6d8544d27711700bc3e6d2efad04238c7b5290889f0"
url: "https://pub.dev"
source: hosted
version: "2.4.2"
version: "2.4.1"
build_config:
dependency: transitive
description:
name: build_config
sha256: "4ae2de3e1e67ea270081eaee972e1bd8f027d459f249e0f1186730784c2e7e33"
sha256: bf80fcfb46a29945b423bd9aad884590fb1dc69b330a4d4700cac476af1708d1
url: "https://pub.dev"
source: hosted
version: "1.1.2"
version: "1.1.1"
build_daemon:
dependency: transitive
description:
name: build_daemon
sha256: "8e928697a82be082206edb0b9c99c5a4ad6bc31c9e9b8b2f291ae65cd4a25daa"
sha256: "79b2aef6ac2ed00046867ed354c88778c9c0f029df8a20fe10b5436826721ef9"
url: "https://pub.dev"
source: hosted
version: "4.0.4"
version: "4.0.2"
build_resolvers:
dependency: transitive
description:
name: build_resolvers
sha256: b9e4fda21d846e192628e7a4f6deda6888c36b5b69ba02ff291a01fd529140f0
sha256: "687cf90a3951affac1bd5f9ecb5e3e90b60487f3d9cdc359bb310f8876bb02a6"
url: "https://pub.dev"
source: hosted
version: "2.4.4"
version: "2.0.10"
build_runner:
dependency: "direct dev"
description:
name: build_runner
sha256: "058fe9dce1de7d69c4b84fada934df3e0153dd000758c4d65964d0166779aa99"
sha256: "028819cfb90051c6b5440c7e574d1896f8037e3c96cf17aaeb054c9311cfbf4d"
url: "https://pub.dev"
source: hosted
version: "2.4.15"
version: "2.4.13"
build_runner_core:
dependency: transitive
dependency: "direct overridden"
description:
name: build_runner_core
sha256: "22e3aa1c80e0ada3722fe5b63fd43d9c8990759d0a2cf489c8c5d7b2bdebc021"
sha256: "0671ad4162ed510b70d0eb4ad6354c249f8429cab4ae7a4cec86bbc2886eb76e"
url: "https://pub.dev"
source: hosted
version: "8.0.0"
version: "7.2.7+1"
built_collection:
dependency: transitive
description:
@ -147,10 +142,10 @@ packages:
dependency: transitive
description:
name: built_value
sha256: "082001b5c3dc495d4a42f1d5789990505df20d8547d42507c29050af6933ee27"
sha256: "8b158ab94ec6913e480dc3f752418348b5ae099eb75868b5f4775f0572999c61"
url: "https://pub.dev"
source: hosted
version: "8.10.1"
version: "8.9.4"
cake_backup:
dependency: transitive
description:
@ -243,10 +238,10 @@ packages:
dependency: transitive
description:
name: dart_style
sha256: "7306ab8a2359a48d22310ad823521d723acfed60ee1f7e37388e8986853b6820"
sha256: "7a03456c3490394c8e7665890333e91ae8a49be43542b616e414449ac358acd4"
url: "https://pub.dev"
source: hosted
version: "2.3.8"
version: "2.2.4"
decimal:
dependency: "direct main"
description:
@ -378,18 +373,18 @@ packages:
dependency: "direct dev"
description:
name: hive_generator
sha256: "06cb8f58ace74de61f63500564931f9505368f45f98958bd7a6c35ba24159db4"
sha256: "81fd20125cb2ce8fd23623d7744ffbaf653aae93706c9bd3bf7019ea0ace3938"
url: "https://pub.dev"
source: hosted
version: "2.0.1"
version: "1.1.3"
http:
dependency: "direct main"
description:
name: http
sha256: "2c11f3f94c687ee9bad77c171151672986360b2b001d109814ee7140b2cf261b"
sha256: fe7ab022b76f3034adc518fb6ea04a82387620e19977665ea18d30a1cf43442f
url: "https://pub.dev"
source: hosted
version: "1.4.0"
version: "1.3.0"
http_multi_server:
dependency: transitive
description:
@ -402,10 +397,10 @@ packages:
dependency: transitive
description:
name: http_parser
sha256: "178d74305e7866013777bab2c3d8726205dc5a4dd935297175b19a23a2e66571"
sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b"
url: "https://pub.dev"
source: hosted
version: "4.1.2"
version: "4.0.2"
intl:
dependency: transitive
description:
@ -478,14 +473,6 @@ packages:
url: "https://pub.dev"
source: hosted
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:
dependency: transitive
description:
@ -530,10 +517,10 @@ packages:
dependency: "direct dev"
description:
name: mobx_codegen
sha256: e0abbbc651a69550440f6b65c99ec222a1e2a4afd7baec8ba0f3088c7ca582a8
sha256: d4beb9cea4b7b014321235f8fdc7c2193ee0fe1d1198e9da7403f8bc85c4407c
url: "https://pub.dev"
source: hosted
version: "2.7.1"
version: "2.3.0"
nanodart:
dependency: transitive
description:
@ -564,7 +551,7 @@ packages:
description:
path: "."
ref: cake-update-v2
resolved-ref: "01cbbacbb05d2113aafa8b7c4a2bb766f749d8d8"
resolved-ref: "93440dc5126369b873ca1fccc13c3c1240b1c5c2"
url: "https://github.com/cake-tech/on_chain.git"
source: git
version: "3.7.0"
@ -572,10 +559,10 @@ packages:
dependency: transitive
description:
name: package_config
sha256: f096c55ebb7deb7e384101542bfba8c52696c1b56fca2eb62827989ef2353bbc
sha256: "92d4488434b520a62570293fbd33bb556c7d49230791c1b4bbd973baf6d2dc67"
url: "https://pub.dev"
source: hosted
version: "2.2.0"
version: "2.1.1"
path:
dependency: transitive
description:
@ -596,10 +583,10 @@ packages:
dependency: transitive
description:
name: path_provider_android
sha256: d0d310befe2c8ab9e7f393288ccbb11b60c019c6b5afc21973eeee4dda2b35e9
sha256: "4adf4fd5423ec60a29506c76581bc05854c55e3a0b72d35bb28d661c9686edf2"
url: "https://pub.dev"
source: hosted
version: "2.2.17"
version: "2.2.15"
path_provider_foundation:
dependency: transitive
description:
@ -676,26 +663,26 @@ packages:
dependency: transitive
description:
name: provider
sha256: "4abbd070a04e9ddc287673bf5a030c7ca8b685ff70218720abab8b092f53dd84"
sha256: c8a055ee5ce3fd98d6fc872478b03823ffdb448699c6ebdbbc71d59b596fd48c
url: "https://pub.dev"
source: hosted
version: "6.1.5"
version: "6.1.2"
pub_semver:
dependency: transitive
description:
name: pub_semver
sha256: "5bfcf68ca79ef689f8990d1160781b4bad40a3bd5e5218ad4076ddb7f4081585"
sha256: "7b3cfbf654f3edd0c6298ecd5be782ce997ddf0e00531b9464b55245185bbbbd"
url: "https://pub.dev"
source: hosted
version: "2.2.0"
version: "2.1.5"
pubspec_parse:
dependency: transitive
description:
name: pubspec_parse
sha256: "0560ba233314abbed0a48a2956f7f022cce7c3e1e73df540277da7544cad4082"
sha256: "81876843eb50dc2e1e5b151792c9a985c5ed2536914115ed04e9c8528f6647b0"
url: "https://pub.dev"
source: hosted
version: "1.5.0"
version: "1.4.0"
rational:
dependency: transitive
description:
@ -708,18 +695,18 @@ packages:
dependency: "direct main"
description:
name: shared_preferences
sha256: "6e8bf70b7fef813df4e9a36f658ac46d107db4b4cfe1048b477d4e453a8159f5"
sha256: "846849e3e9b68f3ef4b60c60cf4b3e02e9321bc7f4d8c4692cf87ffa82fc8a3a"
url: "https://pub.dev"
source: hosted
version: "2.5.3"
version: "2.5.2"
shared_preferences_android:
dependency: transitive
description:
name: shared_preferences_android
sha256: "20cbd561f743a342c76c151d6ddb93a9ce6005751e7aa458baad3858bfbfb6ac"
sha256: a768fc8ede5f0c8e6150476e14f38e2417c0864ca36bb4582be8e21925a03c22
url: "https://pub.dev"
source: hosted
version: "2.4.10"
version: "2.4.6"
shared_preferences_foundation:
dependency: transitive
description:
@ -764,18 +751,18 @@ packages:
dependency: transitive
description:
name: shelf
sha256: e7dd780a7ffb623c57850b33f43309312fc863fb6aa3d276a754bb299839ef12
sha256: ad29c505aee705f41a4d8963641f91ac4cee3c8fad5947e033390a7bd8180fa4
url: "https://pub.dev"
source: hosted
version: "1.4.2"
version: "1.4.1"
shelf_web_socket:
dependency: transitive
description:
name: shelf_web_socket
sha256: "3632775c8e90d6c9712f883e633716432a27758216dfb61bd86a8321c0580925"
sha256: cc36c297b52866d203dbf9332263c94becc2fe0ceaa9681d07b6ef9807023b67
url: "https://pub.dev"
source: hosted
version: "3.0.0"
version: "2.0.1"
sky_engine:
dependency: transitive
description: flutter
@ -784,37 +771,27 @@ packages:
socks5_proxy:
dependency: transitive
description:
path: "."
ref: "27ad7c2efae8d7460325c74b90f660085cbd0685"
resolved-ref: "27ad7c2efae8d7460325c74b90f660085cbd0685"
url: "https://github.com/LacticWhale/socks_dart"
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"
name: socks5_proxy
sha256: "616818a0ea1064a4823b53c9f7eaf8da64ed82dcd51ed71371c7e54751ed5053"
url: "https://pub.dev"
source: hosted
version: "1.0.6"
source_gen:
dependency: transitive
description:
name: source_gen
sha256: "14658ba5f669685cd3d63701d01b31ea748310f7ab854e471962670abcf57832"
sha256: "2d79738b6bbf38a43920e2b8d189e9a3ce6cc201f4b8fc76be5e4fe377b1c38d"
url: "https://pub.dev"
source: hosted
version: "1.5.0"
version: "1.2.6"
source_helper:
dependency: transitive
description:
name: source_helper
sha256: "86d247119aedce8e63f4751bd9626fc9613255935558447569ad42f9f5b48b3c"
sha256: "3b67aade1d52416149c633ba1bb36df44d97c6b51830c2198e934e3fca87ca1f"
url: "https://pub.dev"
source: hosted
version: "1.3.5"
version: "1.3.3"
source_span:
dependency: transitive
description:
@ -875,19 +852,10 @@ packages:
dependency: transitive
description:
name: timing
sha256: "70a3b636575d4163c477e6de42f247a23b315ae20e86442bebe32d3cabf61c32"
sha256: "62ee18aca144e4a9f29d212f5a4c6a053be252b895ab14b5821996cff4ed90fe"
url: "https://pub.dev"
source: hosted
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"
version: "1.0.2"
tuple:
dependency: transitive
description:
@ -948,18 +916,18 @@ packages:
dependency: transitive
description:
name: web_socket
sha256: "34d64019aa8e36bf9842ac014bb5d2f5586ca73df5e4d9bf5c936975cae6982c"
sha256: "3c12d96c0c9a4eec095246debcea7b86c0324f22df69893d538fcc6f1b8cce83"
url: "https://pub.dev"
source: hosted
version: "1.0.1"
version: "0.1.6"
web_socket_channel:
dependency: transitive
description:
name: web_socket_channel
sha256: d645757fb0f4773d602444000a8131ff5d48c9e47adfe9772652dd1a4f2d45c8
sha256: "0b8e2457400d8a859b7b2030786835a28a8e80836ef64402abef392ff4f1d0e5"
url: "https://pub.dev"
source: hosted
version: "3.0.3"
version: "3.0.2"
xdg_directories:
dependency: transitive
description:
@ -977,5 +945,5 @@ packages:
source: hosted
version: "3.1.3"
sdks:
dart: ">=3.6.0 <4.0.0"
flutter: ">=3.27.0"
dart: ">=3.5.0 <4.0.0"
flutter: ">=3.24.0"

View file

@ -31,12 +31,13 @@ dependencies:
dev_dependencies:
flutter_test:
sdk: flutter
build_runner: ^2.4.15
build_runner: ^2.4.7
mobx_codegen: ^2.0.7
hive_generator: ^2.0.1
hive_generator: ^1.1.3
dependency_overrides:
watcher: ^1.1.0
build_runner_core: 7.2.7+1
# For information on the generic Dart part of this file, see the
# following page: https://dart.dev/tools/pub/pubspec

View file

@ -84,6 +84,6 @@ class DefaultPolygonErc20Tokens {
.iconPath;
} catch (_) {}
return Erc20Token.copyWith(token, iconPath, 'POL');
return Erc20Token.copyWith(token, iconPath, 'POLY');
}).toList();
}

View file

@ -18,20 +18,13 @@ class PolygonClient extends EVMChainClient {
EtherAmount? gasPrice,
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(
from: from,
to: to,
value: amount,
data: data,
// data: data,
maxGas: maxGas,
gasPrice: finalGasPrice,
// gasPrice: gasPrice,
// maxFeePerGas: maxFeePerGas,
// maxPriorityFeePerGas: maxPriorityFeePerGas,
);
@ -47,7 +40,7 @@ class PolygonClient extends EVMChainClient {
Future<List<EVMChainTransactionModel>> fetchTransactions(String address,
{String? contractAddress}) async {
try {
final response = await client.get(Uri.https("api.etherscan.io", "/v2/api", {
final response = await httpClient.get(Uri.https("api.etherscan.io", "/v2/api", {
"chainid": "$chainId",
"module": "account",
"action": contractAddress != null ? "tokentx" : "txlist",
@ -75,7 +68,7 @@ class PolygonClient extends EVMChainClient {
@override
Future<List<EVMChainTransactionModel>> fetchInternalTransactions(String address) async {
try {
final response = await client.get(Uri.https("api.etherscan.io", "/v2/api", {
final response = await httpClient.get(Uri.https("api.etherscan.io", "/v2/api", {
"chainid": "$chainId",
"module": "account",
"action": "txlistinternal",

View file

@ -45,12 +45,7 @@ class PolygonWallet extends EVMChainWallet {
final initialErc20Tokens = DefaultPolygonErc20Tokens().initialPolygonErc20Tokens;
for (final token in initialErc20Tokens) {
if (evmChainErc20TokensBox.containsKey(token.contractAddress)) {
final existingToken = evmChainErc20TokensBox.get(token.contractAddress);
if (existingToken?.tag != token.tag) {
evmChainErc20TokensBox.put(token.contractAddress, token);
}
} else {
if (!evmChainErc20TokensBox.containsKey(token.contractAddress)) {
if (isMigration) token.enabled = false;
evmChainErc20TokensBox.put(token.contractAddress, token);
}

View file

@ -34,7 +34,7 @@ dev_dependencies:
flutter_test:
sdk: flutter
flutter_lints: ^2.0.0
build_runner: ^2.4.15
build_runner: ^2.4.7
# For information on the generic Dart part of this file, see the

View file

@ -2,10 +2,8 @@ import 'dart:async';
import 'dart:convert';
import 'dart:math' as math;
import 'package:blockchain_utils/blockchain_utils.dart';
import 'package:cw_core/crypto_currency.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/utils/print_verbose.dart';
import 'package:cw_solana/pending_solana_transaction.dart';
@ -13,17 +11,14 @@ import 'package:cw_solana/solana_balance.dart';
import 'package:cw_solana/solana_exceptions.dart';
import 'package:cw_solana/solana_transaction_model.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/src/instructions/associated_token_account/constant.dart';
import 'package:on_chain/solana/src/models/pda/pda.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;
class SolanaWalletClient {
// Minimum amount in SOL to consider a transaction valid (to filter spam)
static const double minValidAmount = 0.00000003;
final httpClient = ProxyWrapper().getHttpClient();
final httpClient = http.Client();
SolanaRPC? _provider;
bool connect(Node node) {
@ -87,8 +82,7 @@ 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)
final tokenAccounts = await getSPLTokenAccounts(mintAddress, walletAddress);
@ -124,16 +118,14 @@ class SolanaWalletClient {
),
);
final fee =
(feeForMessage?.toDouble() ?? 0.0) / SolanaUtils.lamportsPerSol;
final fee = (feeForMessage?.toDouble() ?? 0.0) / SolanaUtils.lamportsPerSol;
return fee;
} catch (_) {
return 0.0;
}
}
Future<double> getEstimatedFee(
SolanaPublicKey publicKey, Commitment commitment) async {
Future<double> getEstimatedFee(SolanaPublicKey publicKey, Commitment commitment) async {
final message = await _getMessageForNativeTransaction(
publicKey: publicKey,
destinationAddress: publicKey.toAddress().address,
@ -163,170 +155,105 @@ class SolanaWalletClient {
if (meta == null || transaction == null) return null;
final int fee = meta.fee;
final feeInSol = fee / SolanaUtils.lamportsPerSol;
final message = transaction.message;
final instructions = message.compiledInstructions;
String sender = "";
String receiver = "";
String signature = (txResponse.transaction?.signatures.isEmpty ?? true)
? ""
: Base58Encoder.encode(txResponse.transaction!.signatures.first);
for (final instruction in instructions) {
final programId = message.accountKeys[instruction.programIdIndex];
if (programId == SystemProgramConst.programId ||
programId == ComputeBudgetConst.programId) {
if (programId == SystemProgramConst.programId) {
// For native solana transactions
if (instruction.accounts.length < 2) continue;
// Get the fee payer index based on transaction type
// 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];
if (txResponse.version == TransactionType.legacy) {
// For legacy transfers, the fee payer (index 0) is the sender.
sender = message.accountKeys[0].address;
final transactionModel = await _parseNativeTransaction(
message: message,
meta: meta,
fee: fee,
feeInSol: feeInSol,
feePayerIndex: feePayerIndex,
walletAddress: walletAddress,
signature: signature,
blockTime: blockTime,
);
final senderPreBalance = meta.preBalances[0];
final senderPostBalance = meta.postBalances[0];
final feeForTx = fee / SolanaUtils.lamportsPerSol;
if (transactionModel != null) {
return transactionModel;
}
} else if (programId == SPLTokenProgramConst.tokenProgramId) {
// For SPL Token transactions
if (instruction.accounts.length < 2) continue;
// 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);
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;
// 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 there's no token transfer instruction, it means this is just an ATA creation transaction
if (!hasTokenTransfer) {
return null;
if (!foundReceiver) {
// Optionally (and rarely), if no account shows the exact expected change,
// we set the receiver address to unknown.
receiver = "unknown";
}
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;
}
final amount = transferLamports / BigInt.from(1e9);
return SolanaTransactionModel(
isOutgoingTx: sender == walletAddress,
from: sender,
to: receiver,
id: signature,
amount: amountInSol,
amount: amount.abs(),
programId: SystemProgramConst.programId.address,
tokenSymbol: 'SOL',
blockTimeInInt: blockTime?.toInt() ?? 0,
fee: feeInSol,
fee: feeForTx,
);
} else {
if (instruction.accounts.length < 2) continue;
final senderIndex = instruction.accounts[0];
final receiverIndex = instruction.accounts[1];
sender = message.accountKeys[senderIndex].address;
receiver = message.accountKeys[receiverIndex].address;
final feeForTx = fee / SolanaUtils.lamportsPerSol;
final preBalances = meta.preBalances;
final postBalances = meta.postBalances;
final amountInString =
(((preBalances[senderIndex] - postBalances[senderIndex]) / BigInt.from(1e9))
.toDouble() -
feeForTx)
.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,
);
}
} else if (programId == SPLTokenProgramConst.tokenProgramId) {
// For SPL Token transactions
if (instruction.accounts.length < 2) continue;
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 postBalances = meta.postTokenBalances;
@ -390,8 +317,17 @@ class SolanaWalletClient {
programId: SPLTokenProgramConst.tokenProgramId.address,
blockTimeInInt: blockTime?.toInt() ?? 0,
tokenSymbol: tokenSymbol ?? '',
fee: feeInSol,
fee: fee / SolanaUtils.lamportsPerSol,
);
} else {
return null;
}
}
} catch (e, s) {
printV("Error parsing transaction: $e\n$s");
}
return null;
}
/// Load the Address's transactions into the account
@ -433,11 +369,9 @@ 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,
splTokenSymbol: splTokenSymbol,
walletAddress: walletAddress?.address ?? address.address,
@ -445,16 +379,13 @@ class SolanaWalletClient {
final parsedTransactions = await Future.wait(parsedTransactionsFutures);
transactions.addAll(
parsedTransactions.whereType<SolanaTransactionModel>().toList());
transactions.addAll(parsedTransactions.whereType<SolanaTransactionModel>().toList());
// Only update UI if we have new valid transactions
if (parsedTransactions.isNotEmpty) {
// Calling the callback after each batch is processed, therefore passing the current list of transactions.
onUpdate(List<SolanaTransactionModel>.from(transactions));
}
if (i + batchSize < signatures.length) {
await Future.delayed(const Duration(milliseconds: 300));
await Future.delayed(const Duration(milliseconds: 500));
}
}
@ -515,8 +446,8 @@ class SolanaWalletClient {
}
Future<SPLToken?> fetchSPLTokenInfo(String mintAddress) async {
final programAddress = MetaplexTokenMetaDataProgramUtils.findMetadataPda(
mint: SolAddress(mintAddress));
final programAddress =
MetaplexTokenMetaDataProgramUtils.findMetadataPda(mint: SolAddress(mintAddress));
final token = await _provider!.request(
SolanaRPCGetMetadataAccount(
@ -537,9 +468,8 @@ class SolanaWalletClient {
// iconPath = await _client.getIconImageFromTokenUri(metadata.uri);
// } catch (_) {}
String filteredTokenSymbol = metadata.symbol
.replaceFirst(RegExp('^\\\$'), '')
.replaceAll('\u0000', '');
String filteredTokenSymbol =
metadata.symbol.replaceFirst(RegExp('^\\\$'), '').replaceAll('\u0000', '');
return SPLToken.fromMetadata(
name: metadata.name,
@ -655,8 +585,7 @@ class SolanaWalletClient {
return message;
}
Future<double> _getFeeFromCompiledMessage(
Message message, Commitment commitment) async {
Future<double> _getFeeFromCompiledMessage(Message message, Commitment commitment) async {
final base64Message = base64Encode(message.serialize());
final fee = await getFeeForMessage(base64Message, commitment);
@ -795,8 +724,7 @@ class SolanaWalletClient {
required SolAddress mintAddress,
required bool shouldCreateATA,
}) async {
final associatedTokenAccount =
AssociatedTokenAccountProgramUtils.associatedTokenAccount(
final associatedTokenAccount = AssociatedTokenAccountProgramUtils.associatedTokenAccount(
mint: mintAddress,
owner: ownerAddress,
);
@ -804,24 +732,19 @@ class SolanaWalletClient {
SolanaAccountInfo? accountInfo;
try {
accountInfo = await _provider!.request(
SolanaRPCGetAccountInfo(
account: associatedTokenAccount.address,
commitment: Commitment.confirmed,
),
SolanaRPCGetAccountInfo(account: associatedTokenAccount.address),
);
} catch (e) {
accountInfo = null;
}
// If account exists, we return the associated token account
// If aacountInfo is null, signifies that the associatedTokenAccount has only been created locally and not been broadcasted to the blockchain.
if (accountInfo != null) return associatedTokenAccount;
if (!shouldCreateATA) return null;
final payerAddress = payerPrivateKey.publicKey().toAddress();
final createAssociatedTokenAccount = AssociatedTokenAccountProgram.associatedTokenAccount(
payer: payerAddress,
payer: payerPrivateKey.publicKey().toAddress(),
associatedToken: associatedTokenAccount.address,
owner: ownerAddress,
mint: mintAddress,
@ -830,23 +753,19 @@ class SolanaWalletClient {
final blockhash = await _getLatestBlockhash(Commitment.confirmed);
final transaction = SolanaTransaction(
payerKey: payerAddress,
payerKey: payerPrivateKey.publicKey().toAddress(),
instructions: [createAssociatedTokenAccount],
recentBlockhash: blockhash,
type: TransactionType.v0,
);
final serializedTransaction = await _signTransactionInternal(
ownerPrivateKey: payerPrivateKey,
transaction: transaction,
);
transaction.sign([payerPrivateKey]);
await sendTransaction(
serializedTransaction: serializedTransaction,
serializedTransaction: transaction.serializeString(),
commitment: Commitment.confirmed,
);
// Wait for confirmation
// Delay for propagation on the blockchain for newly created associated token addresses
await Future.delayed(const Duration(seconds: 2));
return associatedTokenAccount;
@ -867,8 +786,7 @@ class SolanaWalletClient {
final amount = (inputAmount * math.pow(10, tokenDecimals)).toInt();
ProgramDerivedAddress? associatedSenderAccount;
try {
associatedSenderAccount =
AssociatedTokenAccountProgramUtils.associatedTokenAccount(
associatedSenderAccount = AssociatedTokenAccountProgramUtils.associatedTokenAccount(
mint: mintAddress,
owner: ownerPrivateKey.publicKey().toAddress(),
);
@ -1003,8 +921,7 @@ class SolanaWalletClient {
if (uri.isEmpty || uri == '') return null;
try {
final client = ProxyWrapper().getHttpIOClient();
final response = await client.get(Uri.parse(uri));
final response = await httpClient.get(Uri.parse(uri));
final jsonResponse = json.decode(response.body) as Map<String, dynamic>;

View file

@ -33,9 +33,9 @@ dev_dependencies:
flutter_test:
sdk: flutter
flutter_lints: ^2.0.0
build_runner: ^2.4.15
build_runner: ^2.4.7
mobx_codegen: ^2.0.7
hive_generator: ^2.0.1
hive_generator: ^1.1.3
dependency_overrides:
watcher: ^1.1.0

View file

@ -5,7 +5,6 @@ import 'dart:developer';
import 'package:blockchain_utils/blockchain_utils.dart';
import 'package:cw_core/crypto_currency.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/tron_abi.dart';
import 'package:cw_tron/tron_balance.dart';
@ -14,12 +13,12 @@ import 'package:cw_tron/tron_token.dart';
import 'package:cw_tron/tron_transaction_model.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';
import 'package:http/http.dart';
import '.secrets.g.dart' as secrets;
import 'package:on_chain/on_chain.dart';
class TronClient {
late final client = ProxyWrapper().getHttpIOClient();
final httpClient = Client();
TronProvider? _provider;
// This is an internal tracker, so we don't have to "refetch".
int _nativeTxEstimatedFee = 0;
@ -29,7 +28,7 @@ class TronClient {
Future<List<TronTransactionModel>> fetchTransactions(String address,
{String? contractAddress}) async {
try {
final response = await client.get(
final response = await httpClient.get(
Uri.https(
"api.trongrid.io",
"/v1/accounts/$address/transactions",
@ -62,7 +61,7 @@ class TronClient {
Future<List<TronTRC20TransactionModel>> fetchTrc20ExcludedTransactions(String address) async {
try {
final response = await client.get(
final response = await httpClient.get(
Uri.https(
"api.trongrid.io",
"/v1/accounts/$address/transactions/trc20",

View file

@ -1,22 +1,22 @@
import 'dart:convert';
import 'package:cw_core/utils/proxy_wrapper.dart';
import 'package:http/http.dart' as http;
import 'package:on_chain/tron/tron.dart';
import '.secrets.g.dart' as secrets;
class TronHTTPProvider implements TronServiceProvider {
TronHTTPProvider(
{required this.url,
this.defaultRequestTimeout = const Duration(seconds: 30)});
http.Client? client,
this.defaultRequestTimeout = const Duration(seconds: 30)})
: client = client ?? http.Client();
@override
final String url;
late final client = ProxyWrapper().getHttpIOClient();
final http.Client client;
final Duration defaultRequestTimeout;
@override
Future<Map<String, dynamic>> get(TronRequestDetails params,
[Duration? timeout]) async {
Future<Map<String, dynamic>> get(TronRequestDetails params, [Duration? timeout]) async {
final response = await client.get(Uri.parse(params.url(url)), headers: {
'Content-Type': 'application/json',
if (url.contains("trongrid")) 'TRON-PRO-API-KEY': secrets.tronGridApiKey,
@ -27,16 +27,13 @@ class TronHTTPProvider implements TronServiceProvider {
}
@override
Future<Map<String, dynamic>> post(TronRequestDetails params,
[Duration? timeout]) async {
Future<Map<String, dynamic>> post(TronRequestDetails params, [Duration? timeout]) async {
final response = await client
.post(Uri.parse(params.url(url)),
headers: {
'Content-Type': 'application/json',
if (url.contains("trongrid"))
'TRON-PRO-API-KEY': secrets.tronGridApiKey,
if (url.contains("nownodes"))
'api-key': secrets.tronNowNodesApiKey,
if (url.contains("trongrid")) 'TRON-PRO-API-KEY': secrets.tronGridApiKey,
if (url.contains("nownodes")) 'api-key': secrets.tronNowNodesApiKey,
},
body: params.toRequestBody())
.timeout(timeout ?? defaultRequestTimeout);

View file

@ -31,9 +31,9 @@ dev_dependencies:
flutter_test:
sdk: flutter
flutter_lints: ^2.0.0
build_runner: ^2.4.15
build_runner: ^2.3.3
mobx_codegen: ^2.1.1
hive_generator: ^2.0.1
hive_generator: ^1.1.3
flutter:
# assets:
# - images/a_dot_burr.jpeg

View file

@ -110,16 +110,7 @@ int getUnlockedBalance({int accountIndex = 0}) =>
int getCurrentHeight() => wownero.Wallet_blockChainHeight(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;
}
int getNodeHeightSync() => wownero.Wallet_daemonBlockChainHeight(wptr!);
bool isConnectedSync() => wownero.Wallet_connected(wptr!) != 0;
@ -163,7 +154,7 @@ Future<bool> setupNodeSync(
}
void startRefreshSync() {
// wownero.Wallet_refreshAsync(wptr!);
wownero.Wallet_refreshAsync(wptr!);
wownero.Wallet_startRefresh(wptr!);
}

View file

@ -16,7 +16,6 @@ import 'package:cw_core/transaction_direction.dart';
import 'package:cw_core/transaction_priority.dart';
import 'package:cw_core/unspent_coins_info.dart';
import 'package:cw_core/utils/print_verbose.dart';
import 'package:cw_core/utils/proxy_wrapper.dart';
import 'package:cw_core/wallet_base.dart';
import 'package:cw_core/wallet_info.dart';
import 'package:cw_core/wownero_amount_format.dart';
@ -184,14 +183,6 @@ abstract class WowneroWalletBase
@override
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 {
syncStatus = ConnectingSyncStatus();
await wownero_wallet.setupNode(
@ -201,7 +192,7 @@ abstract class WowneroWalletBase
useSSL: node.isSSL,
isLightWallet: false,
// FIXME: hardcoded value
socksProxyAddress: socksProxy);
socksProxyAddress: node.socksProxyAddress);
wownero_wallet.setTrustedDaemon(node.trusted);
syncStatus = ConnectedSyncStatus();

View file

@ -5,23 +5,18 @@ packages:
dependency: transitive
description:
name: _fe_analyzer_shared
sha256: "16e298750b6d0af7ce8a3ba7c18c69c3785d11b15ec83f6dcd0ad2a0009b3cab"
sha256: "4897882604d919befd350648c7f91926a9d5de99e67b455bf0917cc2362f4bb8"
url: "https://pub.dev"
source: hosted
version: "76.0.0"
_macros:
dependency: transitive
description: dart
source: sdk
version: "0.3.3"
version: "47.0.0"
analyzer:
dependency: transitive
description:
name: analyzer
sha256: "1f14db053a8c23e260789e9b0980fa27f2680dd640932cae5e1137cce0e46e1e"
sha256: "690e335554a8385bc9d787117d9eb52c0c03ee207a607e593de3c9d71b1cfe80"
url: "https://pub.dev"
source: hosted
version: "6.11.0"
version: "4.7.0"
args:
dependency: transitive
description:
@ -91,26 +86,26 @@ packages:
dependency: "direct dev"
description:
name: build_resolvers
sha256: b9e4fda21d846e192628e7a4f6deda6888c36b5b69ba02ff291a01fd529140f0
sha256: "687cf90a3951affac1bd5f9ecb5e3e90b60487f3d9cdc359bb310f8876bb02a6"
url: "https://pub.dev"
source: hosted
version: "2.4.4"
version: "2.0.10"
build_runner:
dependency: "direct dev"
description:
name: build_runner
sha256: "058fe9dce1de7d69c4b84fada934df3e0153dd000758c4d65964d0166779aa99"
sha256: "028819cfb90051c6b5440c7e574d1896f8037e3c96cf17aaeb054c9311cfbf4d"
url: "https://pub.dev"
source: hosted
version: "2.4.15"
version: "2.4.13"
build_runner_core:
dependency: transitive
description:
name: build_runner_core
sha256: "22e3aa1c80e0ada3722fe5b63fd43d9c8990759d0a2cf489c8c5d7b2bdebc021"
sha256: "6d6ee4276b1c5f34f21fdf39425202712d2be82019983d52f351c94aafbc2c41"
url: "https://pub.dev"
source: hosted
version: "8.0.0"
version: "7.2.10"
built_collection:
dependency: transitive
description:
@ -219,10 +214,10 @@ packages:
dependency: transitive
description:
name: dart_style
sha256: "7306ab8a2359a48d22310ad823521d723acfed60ee1f7e37388e8986853b6820"
sha256: "7a03456c3490394c8e7665890333e91ae8a49be43542b616e414449ac358acd4"
url: "https://pub.dev"
source: hosted
version: "2.3.8"
version: "2.2.4"
decimal:
dependency: transitive
description:
@ -341,10 +336,10 @@ packages:
dependency: "direct dev"
description:
name: hive_generator
sha256: "06cb8f58ace74de61f63500564931f9505368f45f98958bd7a6c35ba24159db4"
sha256: "81fd20125cb2ce8fd23623d7744ffbaf653aae93706c9bd3bf7019ea0ace3938"
url: "https://pub.dev"
source: hosted
version: "2.0.1"
version: "1.1.3"
http:
dependency: "direct main"
description:
@ -433,14 +428,6 @@ packages:
url: "https://pub.dev"
source: hosted
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:
dependency: transitive
description:
@ -485,10 +472,10 @@ packages:
dependency: "direct dev"
description:
name: mobx_codegen
sha256: e0abbbc651a69550440f6b65c99ec222a1e2a4afd7baec8ba0f3088c7ca582a8
sha256: d4beb9cea4b7b014321235f8fdc7c2193ee0fe1d1198e9da7403f8bc85c4407c
url: "https://pub.dev"
source: hosted
version: "2.7.1"
version: "2.3.0"
monero:
dependency: "direct main"
description:
@ -683,37 +670,27 @@ packages:
socks5_proxy:
dependency: transitive
description:
path: "."
ref: "27ad7c2efae8d7460325c74b90f660085cbd0685"
resolved-ref: "27ad7c2efae8d7460325c74b90f660085cbd0685"
url: "https://github.com/LacticWhale/socks_dart"
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"
name: socks5_proxy
sha256: "616818a0ea1064a4823b53c9f7eaf8da64ed82dcd51ed71371c7e54751ed5053"
url: "https://pub.dev"
source: hosted
version: "1.0.6"
source_gen:
dependency: transitive
description:
name: source_gen
sha256: "14658ba5f669685cd3d63701d01b31ea748310f7ab854e471962670abcf57832"
sha256: "2d79738b6bbf38a43920e2b8d189e9a3ce6cc201f4b8fc76be5e4fe377b1c38d"
url: "https://pub.dev"
source: hosted
version: "1.5.0"
version: "1.2.6"
source_helper:
dependency: transitive
description:
name: source_helper
sha256: "86d247119aedce8e63f4751bd9626fc9613255935558447569ad42f9f5b48b3c"
sha256: "3b67aade1d52416149c633ba1bb36df44d97c6b51830c2198e934e3fca87ca1f"
url: "https://pub.dev"
source: hosted
version: "1.3.5"
version: "1.3.3"
source_span:
dependency: transitive
description:
@ -774,19 +751,10 @@ packages:
dependency: transitive
description:
name: timing
sha256: "70a3b636575d4163c477e6de42f247a23b315ae20e86442bebe32d3cabf61c32"
sha256: "62ee18aca144e4a9f29d212f5a4c6a053be252b895ab14b5821996cff4ed90fe"
url: "https://pub.dev"
source: hosted
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"
version: "1.0.2"
tuple:
dependency: transitive
description:
@ -876,5 +844,5 @@ packages:
source: hosted
version: "3.1.3"
sdks:
dart: ">=3.6.0 <4.0.0"
dart: ">=3.5.0 <4.0.0"
flutter: ">=3.24.0"

View file

@ -31,10 +31,10 @@ dependencies:
dev_dependencies:
flutter_test:
sdk: flutter
build_runner: ^2.4.15
build_resolvers: ^2.4.4
build_runner: ^2.4.7
build_resolvers: ^2.0.9
mobx_codegen: ^2.0.7
hive_generator: ^2.0.1
hive_generator: ^1.1.3
dependency_overrides:
watcher: ^1.1.0

View file

@ -5,23 +5,18 @@ packages:
dependency: transitive
description:
name: _fe_analyzer_shared
sha256: "16e298750b6d0af7ce8a3ba7c18c69c3785d11b15ec83f6dcd0ad2a0009b3cab"
sha256: "4897882604d919befd350648c7f91926a9d5de99e67b455bf0917cc2362f4bb8"
url: "https://pub.dev"
source: hosted
version: "76.0.0"
_macros:
dependency: transitive
description: dart
source: sdk
version: "0.3.3"
version: "47.0.0"
analyzer:
dependency: transitive
description:
name: analyzer
sha256: "1f14db053a8c23e260789e9b0980fa27f2680dd640932cae5e1137cce0e46e1e"
sha256: "690e335554a8385bc9d787117d9eb52c0c03ee207a607e593de3c9d71b1cfe80"
url: "https://pub.dev"
source: hosted
version: "6.11.0"
version: "4.7.0"
args:
dependency: transitive
description:
@ -91,26 +86,26 @@ packages:
dependency: "direct dev"
description:
name: build_resolvers
sha256: b9e4fda21d846e192628e7a4f6deda6888c36b5b69ba02ff291a01fd529140f0
sha256: "687cf90a3951affac1bd5f9ecb5e3e90b60487f3d9cdc359bb310f8876bb02a6"
url: "https://pub.dev"
source: hosted
version: "2.4.4"
version: "2.0.10"
build_runner:
dependency: "direct dev"
description:
name: build_runner
sha256: "058fe9dce1de7d69c4b84fada934df3e0153dd000758c4d65964d0166779aa99"
sha256: "028819cfb90051c6b5440c7e574d1896f8037e3c96cf17aaeb054c9311cfbf4d"
url: "https://pub.dev"
source: hosted
version: "2.4.15"
version: "2.4.13"
build_runner_core:
dependency: transitive
description:
name: build_runner_core
sha256: "22e3aa1c80e0ada3722fe5b63fd43d9c8990759d0a2cf489c8c5d7b2bdebc021"
sha256: "6d6ee4276b1c5f34f21fdf39425202712d2be82019983d52f351c94aafbc2c41"
url: "https://pub.dev"
source: hosted
version: "8.0.0"
version: "7.2.10"
built_collection:
dependency: transitive
description:
@ -219,10 +214,10 @@ packages:
dependency: transitive
description:
name: dart_style
sha256: "7306ab8a2359a48d22310ad823521d723acfed60ee1f7e37388e8986853b6820"
sha256: "7a03456c3490394c8e7665890333e91ae8a49be43542b616e414449ac358acd4"
url: "https://pub.dev"
source: hosted
version: "2.3.8"
version: "2.2.4"
decimal:
dependency: "direct main"
description:
@ -338,10 +333,10 @@ packages:
dependency: "direct dev"
description:
name: hive_generator
sha256: "06cb8f58ace74de61f63500564931f9505368f45f98958bd7a6c35ba24159db4"
sha256: "81fd20125cb2ce8fd23623d7744ffbaf653aae93706c9bd3bf7019ea0ace3938"
url: "https://pub.dev"
source: hosted
version: "2.0.1"
version: "1.1.3"
http:
dependency: "direct main"
description:
@ -438,14 +433,6 @@ packages:
url: "https://pub.dev"
source: hosted
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:
dependency: transitive
description:
@ -490,10 +477,10 @@ packages:
dependency: "direct dev"
description:
name: mobx_codegen
sha256: e0abbbc651a69550440f6b65c99ec222a1e2a4afd7baec8ba0f3088c7ca582a8
sha256: d4beb9cea4b7b014321235f8fdc7c2193ee0fe1d1198e9da7403f8bc85c4407c
url: "https://pub.dev"
source: hosted
version: "2.7.1"
version: "2.3.0"
monero:
dependency: "direct main"
description:
@ -680,37 +667,27 @@ packages:
socks5_proxy:
dependency: transitive
description:
path: "."
ref: "27ad7c2efae8d7460325c74b90f660085cbd0685"
resolved-ref: "27ad7c2efae8d7460325c74b90f660085cbd0685"
url: "https://github.com/LacticWhale/socks_dart"
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"
name: socks5_proxy
sha256: "616818a0ea1064a4823b53c9f7eaf8da64ed82dcd51ed71371c7e54751ed5053"
url: "https://pub.dev"
source: hosted
version: "1.0.6"
source_gen:
dependency: transitive
description:
name: source_gen
sha256: "14658ba5f669685cd3d63701d01b31ea748310f7ab854e471962670abcf57832"
sha256: "2d79738b6bbf38a43920e2b8d189e9a3ce6cc201f4b8fc76be5e4fe377b1c38d"
url: "https://pub.dev"
source: hosted
version: "1.5.0"
version: "1.2.6"
source_helper:
dependency: transitive
description:
name: source_helper
sha256: "86d247119aedce8e63f4751bd9626fc9613255935558447569ad42f9f5b48b3c"
sha256: "3b67aade1d52416149c633ba1bb36df44d97c6b51830c2198e934e3fca87ca1f"
url: "https://pub.dev"
source: hosted
version: "1.3.5"
version: "1.3.3"
source_span:
dependency: transitive
description:
@ -775,15 +752,6 @@ packages:
url: "https://pub.dev"
source: hosted
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:
dependency: transitive
description:
@ -873,5 +841,5 @@ packages:
source: hosted
version: "3.1.3"
sdks:
dart: ">=3.6.0 <4.0.0"
dart: ">=3.5.0 <4.0.0"
flutter: ">=3.24.0"

View file

@ -30,10 +30,10 @@ dependencies:
dev_dependencies:
flutter_test:
sdk: flutter
build_runner: ^2.4.15
build_runner: ^2.4.7
mobx_codegen: ^2.1.1
build_resolvers: ^2.4.4
hive_generator: ^2.0.1
build_resolvers: ^2.0.9
hive_generator: ^1.1.3
dependency_overrides:
watcher: ^1.1.0

View file

@ -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
# NOTE: Replace `main` with the latest release tag available at https://github.com/cake-tech/cake_wallet/releases/latest.
cd cake_wallet
# 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.0-go1.24.1-ruststablenightly bash -x << EOF
# 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 run -v$(pwd):$(pwd) -w $(pwd) -i --rm ghcr.io/cake-tech/cake_wallet:debian12-flutter3.27.4-go1.24.1 bash -x << EOF
set -x -e
pushd scripts/android
source ./app_env.sh cakewallet

View file

@ -7,7 +7,7 @@ The following are the system requirements to build Cake Wallet for your iOS devi
```txt
macOS 15.3.1
Xcode 16.2
Flutter 3.27.0
Flutter 3.27.4
```
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
Install Flutter, specifically version `3.27.0` by following the [official docs](https://docs.flutter.dev/get-started/install/macos/desktop?tab=download).
Install Flutter, specifically version `3.27.4` by following the [official docs](https://docs.flutter.dev/get-started/install/macos/desktop?tab=download).
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.
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.
### 4. Installing Rust
@ -65,7 +65,7 @@ The output of this command should appear like this, indicating successful instal
```zsh
Doctor summary (to see all details, run flutter doctor -v):
[✓] Flutter (Channel stable, 3.27.0, on macOS 15.x.x)
[✓] Flutter (Channel stable, 3.27.4, on macOS 15.x.x)
[✓] Xcode - develop for iOS and macOS (Xcode 16.2)
```

View file

@ -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
# NOTE: Replace `main` with the latest release tag available at https://github.com/cake-tech/cake_wallet/releases/latest.
cd cake_wallet
# 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.0-go1.24.1-ruststablenightly bash -x << EOF
# 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 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
set -x -e
pushd scripts
./gen_android_manifest.sh

View file

@ -7,7 +7,7 @@ The following are the system requirements to build Cake Wallet for your macOS de
```txt
macOS 15.3.1
Xcode 16.2
Flutter 3.27.0
Flutter 3.27.4
```
### 1. Installing dependencies
@ -34,9 +34,9 @@ sudo xcodebuild -runFirstLaunch
### 3. Installing Flutter
Install Flutter, specifically version `3.27.0` by following the [official docs](https://docs.flutter.dev/get-started/install/macos/desktop?tab=download).
Install Flutter, specifically version `3.27.4` by following the [official docs](https://docs.flutter.dev/get-started/install/macos/desktop?tab=download).
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.
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.
### 4. Installing Rust
@ -56,7 +56,7 @@ The output of this command should appear like this, indicating successful instal
```zsh
Doctor summary (to see all details, run flutter doctor -v):
[✓] Flutter (Channel stable, 3.27.0, on macOS 15.x.x)
[✓] Flutter (Channel stable, 3.27.4, on macOS 15.x.x)
...
[✓] Xcode - develop for iOS and macOS (Xcode 16.2)
...

View file

@ -6,18 +6,18 @@ The following are the system requirements to build Cake Wallet for your Windows
```txt
Windows 10 or later (64-bit), x86-64 based
Flutter 3.27.0
Flutter 3.27.4
```
### 1. Installing Flutter
Install Flutter, specifically version `3.27.0` by following the [official docs](https://docs.flutter.dev/get-started/install/windows).
Install Flutter, specifically version `3.27.4` 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:
Start Menu > search for "Run" > type `ms-settings:developers`, and turn on Developer Mode.
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.
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.
### 2. Install Development Tools

View file

@ -70,10 +70,7 @@ class WalletKeysAndSeedPageRobot {
if (walletType == WalletType.bitcoin ||
walletType == WalletType.litecoin ||
walletType == WalletType.bitcoinCash) {
final seedWords = appStore.wallet!.seed!.split(" ");
for (var seedWord in seedWords) {
commonTestCases.hasTextAtLestOnce(seedWord);
}
commonTestCases.hasText(appStore.wallet!.seed!);
tester.printToConsole('$walletName wallet has seeds properly displayed');
}
@ -81,14 +78,10 @@ class WalletKeysAndSeedPageRobot {
walletType == WalletType.solana ||
walletType == WalletType.tron) {
if (hasSeed) {
final seedWords = appStore.wallet!.seed!.split(" ");
for (var seedWord in seedWords) {
commonTestCases.hasTextAtLestOnce(seedWord);
}
commonTestCases.hasText(appStore.wallet!.seed!);
tester.printToConsole('$walletName wallet has seeds properly displayed');
}
if (hasPrivateKey) {
await commonTestCases.tapItemByKey('wallet_keys_page_keys');
commonTestCases.hasText(appStore.wallet!.privateKey!);
tester.printToConsole('$walletName wallet has private key properly displayed');
}
@ -96,19 +89,14 @@ class WalletKeysAndSeedPageRobot {
if (walletType == WalletType.nano || walletType == WalletType.banano) {
if (hasSeed) {
final seedWords = appStore.wallet!.seed!.split(" ");
for (var seedWord in seedWords) {
commonTestCases.hasTextAtLestOnce(seedWord);
}
commonTestCases.hasText(appStore.wallet!.seed!);
tester.printToConsole('$walletName wallet has seeds properly displayed');
}
if (hasHexSeed) {
await commonTestCases.tapItemByKey('wallet_keys_page_keys');
commonTestCases.hasText(appStore.wallet!.hexSeed!);
tester.printToConsole('$walletName wallet has hexSeed properly displayed');
}
if (hasPrivateKey) {
await commonTestCases.tapItemByKey('wallet_keys_page_keys');
commonTestCases.hasText(appStore.wallet!.privateKey!);
tester.printToConsole('$walletName wallet has private key properly displayed');
}
@ -141,39 +129,35 @@ class WalletKeysAndSeedPageRobot {
final hasSeedLegacy = Polyseed.isValidSeed(seed);
if (hasPublicSpendKey) {
await commonTestCases.tapItemByKey('wallet_keys_page_keys');
commonTestCases.hasText(keys.publicSpendKey);
tester.printToConsole('$walletName wallet has public spend key properly displayed');
}
if (hasPrivateSpendKey) {
await commonTestCases.tapItemByKey('wallet_keys_page_keys');
commonTestCases.hasText(keys.privateSpendKey);
tester.printToConsole('$walletName wallet has private spend key properly displayed');
}
if (hasPublicViewKey) {
await commonTestCases.tapItemByKey('wallet_keys_page_keys');
commonTestCases.hasText(keys.publicViewKey);
tester.printToConsole('$walletName wallet has public view key properly displayed');
}
if (hasPrivateViewKey) {
await commonTestCases.tapItemByKey('wallet_keys_page_keys');
commonTestCases.hasText(keys.privateViewKey);
tester.printToConsole('$walletName wallet has private view key properly displayed');
}
if (hasSeeds) {
await commonTestCases.tapItemByKey('wallet_keys_page_seed');
final seedWords = seed.split(" ");
for (var seedWord in seedWords) {
commonTestCases.hasTextAtLestOnce(seedWord);
}
await commonTestCases.dragUntilVisible(
'${walletName}_wallet_seed_item_key',
'wallet_keys_page_credentials_list_view_key',
);
commonTestCases.hasText(seed);
tester.printToConsole('$walletName wallet has seeds properly displayed');
}
if (hasSeedLegacy) {
await commonTestCases.tapItemByKey('wallet_keys_page_seed_legacy');
final seedWords = legacySeed.split(" ");
for (var seedWord in seedWords) {
commonTestCases.hasTextAtLestOnce(seedWord);
}
await commonTestCases.dragUntilVisible(
'${walletName}_wallet_seed_legacy_item_key',
'wallet_keys_page_credentials_list_view_key',
);
commonTestCases.hasText(legacySeed);
tester.printToConsole('$walletName wallet has legacy seeds properly displayed');
}
}

View file

@ -76,7 +76,7 @@ PODS:
- path_provider_foundation (0.0.1):
- Flutter
- FlutterMacOS
- payjoin_flutter (0.23.0)
- payjoin_flutter (0.20.0)
- permission_handler_apple (9.3.0):
- Flutter
- reown_yttrium (0.0.1):
@ -235,7 +235,7 @@ SPEC CHECKSUMS:
OrderedSet: e539b66b644ff081c73a262d24ad552a69be3a94
package_info_plus: c0502532a26c7662a62a356cebe2692ec5fe4ec4
path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46
payjoin_flutter: d9d4c8aa16bd5dfedb9b21d0edc8199e0187d96e
payjoin_flutter: 6397d7b698cdad6453be4949ab6aca1863f6c5e5
permission_handler_apple: 9878588469a2b0d0fc1e048d9f43605f92e6cec2
reown_yttrium: c0e87e5965fa60a3559564cc35cffbba22976089
SDWebImage: 73c6079366fea25fa4bb9640d5fb58f0893facd8

View file

@ -6,9 +6,8 @@ import 'package:cake_wallet/anonpay/anonpay_status_response.dart';
import 'package:cake_wallet/core/fiat_conversion_service.dart';
import 'package:cake_wallet/entities/fiat_currency.dart';
import 'package:cake_wallet/exchange/limits.dart';
import 'package:cake_wallet/wallet_type_utils.dart';
import 'package:cw_core/utils/proxy_wrapper.dart';
import 'package:cw_core/wallet_base.dart';
import 'package:http/http.dart';
import 'package:cw_core/crypto_currency.dart';
import 'package:cake_wallet/.secrets.g.dart' as secrets;
@ -21,21 +20,17 @@ class AnonPayApi {
final WalletBase wallet;
static const anonpayRef = secrets.anonPayReferralCode;
static const onionApiAuthority = 'tqzngtf2hybjbexznel6dhgsvbynjzezoybvtv6iofomx7gchqfssgqd.onion';
static const clearNetAuthority = 'trocador.app';
// static const onionApiAuthority = 'tqzngtf2hybjbexznel6dhgsvbynjzezoybvtv6iofomx7gchqfssgqd.onion';
static const onionApiAuthority = clearNetAuthority;
static const markup = secrets.trocadorExchangeMarkup;
static const anonPayPath = '/anonpay';
static const anonPayStatus = '/anonpay/status';
static const coinPath = 'api/coin';
static final apiKey = isMoneroOnly ? secrets.trocadorMoneroApiKey : secrets.trocadorApiKey;
static const apiKey = secrets.trocadorApiKey;
Future<AnonpayStatusResponse> paymentStatus(String id) async {
final response = await ProxyWrapper().get(
clearnetUri: Uri.https(clearNetAuthority, "$anonPayStatus/$id"),
onionUri: Uri.https(onionApiAuthority, "$anonPayStatus/$id"),
);
final authority = await _getAuthority();
final response = await get(Uri.https(authority, "$anonPayStatus/$id"));
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
final status = responseJSON['Status'] as String;
final fiatAmount = responseJSON['Fiat_Amount'] as double?;
@ -74,10 +69,9 @@ class AnonPayApi {
if (request.fiatEquivalent != null) {
body['fiat_equiv'] = request.fiatEquivalent;
}
final response = await ProxyWrapper().get(
clearnetUri: Uri.https(clearNetAuthority, anonPayPath, body),
onionUri: Uri.https(onionApiAuthority, anonPayPath, body),
);
final authority = await _getAuthority();
final response = await get(Uri.https(authority, anonPayPath, body));
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
final id = responseJSON['ID'] as String;
@ -152,16 +146,17 @@ class AnonPayApi {
'name': cryptoCurrency.name,
};
final response = await ProxyWrapper().get(
clearnetUri: Uri.https(clearNetAuthority, coinPath, params),
onionUri: Uri.https(onionApiAuthority, coinPath, params),
);
final String apiAuthority = await _getAuthority();
final uri = Uri.https(apiAuthority, coinPath, params);
final response = await get(uri);
final responseJSON = json.decode(response.body) as List<dynamic>;
if (response.statusCode != 200) {
throw Exception('Unexpected http status: ${response.statusCode}');
}
final responseJSON = json.decode(response.body) as List<dynamic>;
if (responseJSON.isEmpty) {
throw Exception('No data');
}
@ -202,4 +197,17 @@ class AnonPayApi {
return tag.toLowerCase();
}
}
Future<String> _getAuthority() async {
try {
if (useTorOnly) {
return onionApiAuthority;
}
final uri = Uri.https(onionApiAuthority, '/anonpay');
await get(uri);
return onionApiAuthority;
} catch (e) {
return clearNetAuthority;
}
}
}

View file

@ -1,8 +1,8 @@
import 'dart:convert';
import 'package:cake_wallet/anypay/any_pay_payment_committed_info.dart';
import 'package:cake_wallet/utils/exception_handler.dart';
import 'package:cw_core/utils/proxy_wrapper.dart';
import 'package:flutter/foundation.dart';
import 'package:http/http.dart';
import 'package:cw_core/crypto_currency.dart';
import 'package:cake_wallet/anypay/any_pay_payment.dart';
import 'package:cake_wallet/anypay/any_pay_trasnaction.dart';
@ -53,11 +53,7 @@ class AnyPayApi {
final body = <String, dynamic>{
'chain': chainByScheme(scheme),
'currency': currencyByScheme(scheme).title};
final response = await ProxyWrapper().post(
clearnetUri: url,
headers: headers,
body: json.encode(body),
);
final response = await post(url, headers: headers, body: utf8.encode(json.encode(body)));
if (response.statusCode != 200) {
await ExceptionHandler.onError(FlutterErrorDetails(exception: response));
@ -83,12 +79,7 @@ class AnyPayApi {
'chain': chain,
'currency': currency,
'transactions': transactions.map((tx) => {'tx': tx.tx, 'tx_hash': tx.id, 'tx_key': tx.key}).toList()};
final response = await ProxyWrapper().post(
clearnetUri: Uri.parse(uri),
headers: headers,
body: json.encode(body),
);
final response = await post(Uri.parse(uri), headers: headers, body: utf8.encode(json.encode(body)));
if (response.statusCode == 400) {
final decodedBody = json.decode(response.body) as Map<String, dynamic>;
throw Exception(decodedBody['message'] as String? ?? 'Unexpected response\nError code: 400');

View file

@ -747,6 +747,5 @@ class CWBitcoin extends Bitcoin {
final _wallet = wallet as ElectrumWallet;
(_wallet.walletAddresses as BitcoinWalletAddresses).payjoinManager.cleanupSessions();
(_wallet.walletAddresses as BitcoinWalletAddresses).currentPayjoinReceiver = null;
(_wallet.walletAddresses as BitcoinWalletAddresses).payjoinEndpoint = null;
}
}

View file

@ -10,7 +10,6 @@ import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/src/screens/connect_device/connect_device_page.dart';
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
import 'package:cw_core/utils/proxy_wrapper.dart';
import 'package:cake_wallet/utils/show_pop_up.dart';
import 'package:cake_wallet/view_model/hardware_wallet/ledger_view_model.dart';
import 'package:cw_core/crypto_currency.dart';
@ -18,6 +17,7 @@ import 'package:cw_core/utils/print_verbose.dart';
import 'package:cw_core/wallet_base.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'package:url_launcher/url_launcher.dart';
class DFXBuyProvider extends BuyProvider {
@ -100,13 +100,12 @@ class DFXBuyProvider extends BuyProvider {
});
final uri = Uri.https(_baseUrl, _authPath);
final response = await ProxyWrapper().post(
clearnetUri: uri,
var response = await http.post(
uri,
headers: {'Content-Type': 'application/json'},
body: requestBody,
);
if (response.statusCode == 201) {
final responseBody = jsonDecode(response.body);
return responseBody['accessToken'] as String;
@ -138,9 +137,7 @@ class DFXBuyProvider extends BuyProvider {
final url = Uri.https(_baseUrl, '/v1/fiat');
try {
final response = await ProxyWrapper().get(
clearnetUri: url,
headers: {'accept': 'application/json'});
final response = await http.get(url, headers: {'accept': 'application/json'});
if (response.statusCode == 200) {
final data = jsonDecode(response.body) as List<dynamic>;
@ -163,7 +160,7 @@ class DFXBuyProvider extends BuyProvider {
final url = Uri.https(_baseUrl, '/v1/asset', {'blockchains': blockchain});
try {
final response = await ProxyWrapper().get(clearnetUri: url, headers: {'accept': 'application/json'});
final response = await http.get(url, headers: {'accept': 'application/json'});
if (response.statusCode == 200) {
final responseData = jsonDecode(response.body);
@ -274,12 +271,7 @@ class DFXBuyProvider extends BuyProvider {
});
try {
final response = await ProxyWrapper().put(
clearnetUri: url,
headers: headers,
body: body,
);
final response = await http.put(url, headers: headers, body: body);
final responseData = jsonDecode(response.body);
if (response.statusCode == 200) {

View file

@ -9,10 +9,10 @@ import 'package:cake_wallet/entities/fiat_currency.dart';
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
import 'package:cake_wallet/utils/show_pop_up.dart';
import 'package:cw_core/crypto_currency.dart';
import 'package:cw_core/utils/proxy_wrapper.dart';
import 'package:cw_core/wallet_base.dart';
import 'package:flutter/material.dart';
import 'dart:developer';
import 'package:http/http.dart' as http;
import 'package:url_launcher/url_launcher.dart';
class KryptonimBuyProvider extends BuyProvider {
@ -74,14 +74,9 @@ class KryptonimBuyProvider extends BuyProvider {
});
try {
final response = await ProxyWrapper().post(
clearnetUri: url,
headers: headers,
body: body,
);
final response = await http.post(url, headers: headers, body: body);
if (response.statusCode == 200 || response.statusCode == 201 || response.statusCode == 401) {
return jsonDecode(response.body) as Map<String, dynamic>;
} else {
return {};

View file

@ -8,13 +8,13 @@ import 'package:cake_wallet/buy/payment_method.dart';
import 'package:cake_wallet/entities/fiat_currency.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
import 'package:cw_core/utils/proxy_wrapper.dart';
import 'package:cake_wallet/utils/show_pop_up.dart';
import 'package:cw_core/crypto_currency.dart';
import 'package:cw_core/utils/print_verbose.dart';
import 'package:cw_core/wallet_base.dart';
import 'package:flutter/material.dart';
import 'dart:developer';
import 'package:http/http.dart' as http;
import 'package:url_launcher/url_launcher.dart';
class MeldBuyProvider extends BuyProvider {
@ -71,8 +71,8 @@ class MeldBuyProvider extends BuyProvider {
final url = Uri.https(_baseUrl, path, params);
try {
final response = await ProxyWrapper().get(
clearnetUri: url,
final response = await http.get(
url,
headers: {
'Authorization': _isProduction ? '' : _testApiKey,
'Meld-Version': '2023-12-19',
@ -81,7 +81,6 @@ class MeldBuyProvider extends BuyProvider {
},
);
if (response.statusCode == 200) {
final data = jsonDecode(response.body) as List<dynamic>;
final paymentMethods =
@ -131,12 +130,7 @@ class MeldBuyProvider extends BuyProvider {
});
try {
final response = await ProxyWrapper().post(
clearnetUri: url,
headers: headers,
body: body,
);
final response = await http.post(url, headers: headers, body: body);
if (response.statusCode == 200) {
final data = jsonDecode(response.body) as Map<String, dynamic>;

View file

@ -16,12 +16,12 @@ import 'package:cake_wallet/palette.dart';
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
import 'package:cake_wallet/store/app_store.dart';
import 'package:cake_wallet/themes/core/material_base_theme.dart';
import 'package:cw_core/utils/proxy_wrapper.dart';
import 'package:cw_core/crypto_currency.dart';
import 'package:cw_core/wallet_base.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:cw_core/utils/print_verbose.dart';
import 'package:flutter/material.dart';
import 'package:http/http.dart';
import 'package:url_launcher/url_launcher.dart';
class MoonPayProvider extends BuyProvider {
@ -98,12 +98,9 @@ class MoonPayProvider extends BuyProvider {
Future<String> getMoonpaySignature(String query) async {
final uri = Uri.https(_cIdBaseUrl, "/api/moonpay");
final response = await ProxyWrapper().post(
clearnetUri: uri,
final response = await post(uri,
headers: {'Content-Type': 'application/json', 'x-api-key': _exchangeHelperApiKey},
body: json.encode({'query': query}),
);
body: json.encode({'query': query}));
if (response.statusCode == 200) {
return (jsonDecode(response.body) as Map<String, dynamic>)['signature'] as String;
@ -123,11 +120,7 @@ class MoonPayProvider extends BuyProvider {
final url = Uri.https(_baseUrl, path, params);
try {
final response = await ProxyWrapper().get(
clearnetUri: url,
headers: {'accept': 'application/json'},
);
final response = await get(url, headers: {'accept': 'application/json'});
if (response.statusCode == 200) {
return jsonDecode(response.body) as Map<String, dynamic>;
} else {
@ -198,7 +191,7 @@ class MoonPayProvider extends BuyProvider {
final path = '$_currenciesPath/$formattedCryptoCurrency$quotePath';
final url = Uri.https(_baseUrl, path, params);
try {
final response = await ProxyWrapper().get(clearnetUri: url);
final response = await get(url);
if (response.statusCode == 200) {
final data = jsonDecode(response.body) as Map<String, dynamic>;
@ -313,8 +306,7 @@ class MoonPayProvider extends BuyProvider {
Future<Order> findOrderById(String id) async {
final url = _apiUrl + _transactionsSuffix + '/$id' + '?apiKey=' + _apiKey;
final uri = Uri.parse(url);
final response = await ProxyWrapper().get(clearnetUri: uri);
final response = await get(uri);
if (response.statusCode != 200) {
throw BuyException(title: providerDescription, content: 'Transaction $id is not found!');

View file

@ -9,12 +9,11 @@ import 'package:cake_wallet/buy/payment_method.dart';
import 'package:cake_wallet/entities/fiat_currency.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/themes/core/theme_store.dart';
import 'package:cake_wallet/store/settings_store.dart';
import 'package:cw_core/utils/proxy_wrapper.dart';
import 'package:cw_core/crypto_currency.dart';
import 'package:cw_core/utils/print_verbose.dart';
import 'package:cw_core/wallet_base.dart';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'package:url_launcher/url_launcher.dart';
class OnRamperBuyProvider extends BuyProvider {
@ -67,10 +66,8 @@ class OnRamperBuyProvider extends BuyProvider {
final url = Uri.https(_baseApiUrl, '$supported$defaultsAll', params);
try {
final response = await ProxyWrapper().get(
clearnetUri: url,
headers: {'Authorization': _apiKey, 'accept': 'application/json'},
);
final response =
await http.get(url, headers: {'Authorization': _apiKey, 'accept': 'application/json'});
if (response.statusCode == 200) {
final Map<String, dynamic> data = jsonDecode(response.body) as Map<String, dynamic>;
@ -105,7 +102,7 @@ class OnRamperBuyProvider extends BuyProvider {
try {
final response =
await ProxyWrapper().get(clearnetUri: url, headers: {'Authorization': _apiKey, 'accept': 'application/json'});
await http.get(url, headers: {'Authorization': _apiKey, 'accept': 'application/json'});
if (response.statusCode == 200) {
final Map<String, dynamic> data = jsonDecode(response.body) as Map<String, dynamic>;
@ -135,8 +132,7 @@ class OnRamperBuyProvider extends BuyProvider {
try {
final response =
await ProxyWrapper().get(clearnetUri: url, headers: {'Authorization': _apiKey, 'accept': 'application/json'});
await http.get(url, headers: {'Authorization': _apiKey, 'accept': 'application/json'});
if (response.statusCode == 200) {
final Map<String, dynamic> data = jsonDecode(response.body) as Map<String, dynamic>;
@ -199,7 +195,7 @@ class OnRamperBuyProvider extends BuyProvider {
final headers = {'Authorization': _apiKey, 'accept': 'application/json'};
try {
final response = await ProxyWrapper().get(clearnetUri: url, headers: headers);
final response = await http.get(url, headers: headers);
if (response.statusCode == 200) {
final data = jsonDecode(response.body) as List<dynamic>;

View file

@ -11,7 +11,6 @@ import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/src/screens/connect_device/connect_device_page.dart';
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
import 'package:cw_core/utils/proxy_wrapper.dart';
import 'package:cake_wallet/utils/show_pop_up.dart';
import 'package:cake_wallet/view_model/hardware_wallet/ledger_view_model.dart';
import 'package:cw_core/crypto_currency.dart';
@ -19,6 +18,7 @@ import 'package:cw_core/utils/print_verbose.dart';
import 'package:cw_core/wallet_base.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'package:url_launcher/url_launcher.dart';
class RobinhoodBuyProvider extends BuyProvider {
@ -69,8 +69,7 @@ class RobinhoodBuyProvider extends BuyProvider {
final uri = Uri.https(_apiBaseUrl, '$_assetsPath', {'applicationId': _applicationId});
try {
final response = await ProxyWrapper().get(clearnetUri: uri, headers: {'accept': 'application/json'});
final response = await http.get(uri, headers: {'accept': 'application/json'});
if (response.statusCode == 200) {
final responseData = jsonDecode(response.body) as Map<String, dynamic>;
@ -123,14 +122,12 @@ class RobinhoodBuyProvider extends BuyProvider {
final uri = Uri.https(_cIdBaseUrl, "/api/robinhood");
var response = await ProxyWrapper().post(
clearnetUri: uri,
var response = await http.post(uri,
headers: {'Content-Type': 'application/json'},
body: json.encode({'valid_until': valid_until, 'wallet': walletAddress, 'signature': signature}),
);
body: json
.encode({'valid_until': valid_until, 'wallet': walletAddress, 'signature': signature}));
if (response.statusCode == 200) {
return (jsonDecode(response.body) as Map<String, dynamic>)['connectId'] as String;
} else {
throw Exception('Provider currently unavailable. Status: ${response.statusCode}');
@ -222,8 +219,7 @@ class RobinhoodBuyProvider extends BuyProvider {
Uri.https('api.robinhood.com', '/catpay/v1/${cryptoCurrency.title}/quote/', queryParams);
try {
final response = await ProxyWrapper().get(clearnetUri: uri, headers: {'accept': 'application/json'});
final response = await http.get(uri, headers: {'accept': 'application/json'});
final responseData = jsonDecode(response.body) as Map<String, dynamic>;
if (response.statusCode == 200) {

View file

@ -3,7 +3,7 @@ import 'package:cake_wallet/buy/buy_exception.dart';
import 'package:cake_wallet/buy/pairs_utils.dart';
import 'package:cake_wallet/entities/fiat_currency.dart';
import 'package:cw_core/crypto_currency.dart';
import 'package:cw_core/utils/proxy_wrapper.dart';
import 'package:http/http.dart';
import 'package:cake_wallet/buy/buy_amount.dart';
import 'package:cake_wallet/buy/buy_provider.dart';
import 'package:cake_wallet/buy/buy_provider_description.dart';
@ -73,21 +73,18 @@ class WyreBuyProvider extends BuyProvider {
'referrerAccountId': _accountId,
'lockFields': ['amount', 'sourceCurrency', 'destCurrency', 'dest']
};
final response = await ProxyWrapper().post(
clearnetUri: uri,
final response = await post(uri,
headers: {
'Authorization': 'Bearer $_secretKey',
'Content-Type': 'application/json',
'cache-control': 'no-cache'
},
body: json.encode(body),
);
body: json.encode(body));
if (response.statusCode != 200) {
throw BuyException(title: providerDescription, content: 'Url $url is not found!');
}
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
final urlFromResponse = responseJSON['url'] as String;
return urlFromResponse;
@ -104,21 +101,18 @@ class WyreBuyProvider extends BuyProvider {
'country': _countryCode
};
final uri = Uri.parse(quoteUrl);
final response = await ProxyWrapper().post(
clearnetUri: uri,
final response = await post(uri,
headers: {
'Authorization': 'Bearer $_secretKey',
'Content-Type': 'application/json',
'cache-control': 'no-cache'
},
body: json.encode(body),
);
body: json.encode(body));
if (response.statusCode != 200) {
throw BuyException(title: providerDescription, content: 'Quote is not found!');
}
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
final sourceAmount = responseJSON['sourceAmount'] as double;
final destAmount = responseJSON['destAmount'] as double;
@ -131,7 +125,8 @@ class WyreBuyProvider extends BuyProvider {
Future<Order> findOrderById(String id) async {
final orderUrl = baseApiUrl + _ordersSuffix + '/$id';
final orderUri = Uri.parse(orderUrl);
final orderResponse = await ProxyWrapper().get(clearnetUri: orderUri);
final orderResponse = await get(orderUri);
if (orderResponse.statusCode != 200) {
throw BuyException(title: providerDescription, content: 'Order $id is not found!');
}
@ -147,7 +142,8 @@ class WyreBuyProvider extends BuyProvider {
final transferUrl = baseApiUrl + _transferSuffix + transferId + _trackSuffix;
final transferUri = Uri.parse(transferUrl);
final transferResponse = await ProxyWrapper().get(clearnetUri: transferUri);
final transferResponse = await get(transferUri);
if (transferResponse.statusCode != 200) {
throw BuyException(title: providerDescription, content: 'Transfer $transferId is not found!');
}

View file

@ -3,9 +3,9 @@ import 'dart:convert';
import 'package:cake_wallet/cake_pay/cake_pay_order.dart';
import 'package:cake_wallet/cake_pay/cake_pay_user_credentials.dart';
import 'package:cake_wallet/cake_pay/cake_pay_vendor.dart';
import 'package:cw_core/utils/proxy_wrapper.dart';
import 'package:cw_core/utils/print_verbose.dart';
import 'package:cake_wallet/entities/country.dart';
import 'package:http/http.dart' as http;
class CakePayApi {
static const testBaseUri = false;
@ -32,17 +32,12 @@ class CakePayApi {
'Content-Type': 'application/json',
'Authorization': 'Api-Key $apiKey',
};
final response = await ProxyWrapper().post(
clearnetUri: uri,
headers: headers,
body: json.encode({'email': email}),
);
final response = await http.post(uri, headers: headers, body: json.encode({'email': email}));
if (response.statusCode != 200) {
throw Exception('Unexpected http status: ${response.statusCode}');
}
final bodyJson = json.decode(response.body) as Map<String, dynamic>;
if (bodyJson.containsKey('user') && bodyJson['user']['email'] != null) {
@ -69,17 +64,12 @@ class CakePayApi {
};
final query = <String, String>{'email': email, 'otp': code};
final response = await ProxyWrapper().post(
clearnetUri: uri,
headers: headers,
body: json.encode(query),
);
final response = await http.post(uri, headers: headers, body: json.encode(query));
if (response.statusCode != 200) {
throw Exception('Unexpected http status: ${response.statusCode}');
}
final bodyJson = json.decode(response.body) as Map<String, dynamic>;
if (bodyJson.containsKey('error')) {
@ -126,14 +116,9 @@ class CakePayApi {
};
try {
final response = await ProxyWrapper().post(
clearnetUri: uri,
headers: headers,
body: json.encode(query),
);
final response = await http.post(uri, headers: headers, body: json.encode(query));
if (response.statusCode != 201) {
final responseBody = json.decode(response.body);
if (responseBody is List) {
throw '${responseBody[0]}';
@ -142,7 +127,6 @@ class CakePayApi {
}
}
final bodyJson = json.decode(response.body) as Map<String, dynamic>;
return CakePayOrder.fromMap(bodyJson);
} catch (e) {
@ -161,8 +145,7 @@ class CakePayApi {
'X-CSRFToken': CSRFToken,
};
final response = await ProxyWrapper().get(clearnetUri: uri, headers: headers);
final response = await http.get(uri, headers: headers);
printV('Response: ${response.statusCode}');
@ -185,11 +168,7 @@ class CakePayApi {
};
try {
final response = await ProxyWrapper().post(
clearnetUri: uri,
headers: headers,
body: json.encode({'email': email}),
);
final response = await http.post(uri, headers: headers, body: json.encode({'email': email}));
if (response.statusCode != 200) {
throw Exception('Unexpected http status: ${response.statusCode}');
@ -208,7 +187,7 @@ class CakePayApi {
'Authorization': 'Api-Key $apiKey',
};
final response = await ProxyWrapper().get(clearnetUri: uri, headers: headers);
final response = await http.get(uri, headers: headers);
if (response.statusCode != 200) {
throw Exception('Unexpected http status: ${response.statusCode}');
@ -255,15 +234,14 @@ class CakePayApi {
'Authorization': 'Api-Key $apiKey',
};
var response = await ProxyWrapper().get(clearnetUri: uri, headers: headers);
var response = await http.get(uri, headers: headers);
if (response.statusCode != 200) {
throw Exception(
'Failed to fetch vendors: statusCode - ${response.statusCode}, queryParams -$queryParams, response - ${response.body}');
}
final bodyJson = json.decode(response.body);
final bodyJson = json.decode(utf8.decode(response.bodyBytes));
if (bodyJson is List<dynamic> && bodyJson.isEmpty) {
return [];

View file

@ -38,9 +38,9 @@ class AddressValidator extends TextValidator {
'|[0-9a-zA-Z]{105}|addr1[0-9a-zA-Z]{98}';
case CryptoCurrency.btc:
pattern =
'${P2pkhAddress.regex.pattern}|${P2shAddress.regex.pattern}|${P2wpkhAddress.regex.pattern}|${P2trAddress.regex.pattern}|${P2wshAddress.regex.pattern}|${SilentPaymentAddress.regex.pattern}';
'${P2pkhAddress.regex.pattern}|${P2shAddress.regex.pattern}|${RegExp(r'(bc|tb)1q[ac-hj-np-z02-9]{25,39}}').pattern}|${P2trAddress.regex.pattern}|${P2wshAddress.regex.pattern}|${SilentPaymentAddress.regex.pattern}';
case CryptoCurrency.ltc:
pattern = '${P2wpkhAddress.regex.pattern}|${MwebAddress.regex.pattern}';
pattern = '^${RegExp(r'ltc1q[ac-hj-np-z02-9]{25,39}').pattern}\$|^${MwebAddress.regex.pattern}\$';
case CryptoCurrency.nano:
pattern = '[0-9a-zA-Z_]+';
case CryptoCurrency.banano:
@ -335,6 +335,10 @@ class AddressValidator extends TextValidator {
}
}
return pattern != null ? "($pattern)" : null;
if (pattern != null) {
return "$BEFORE_REGEX($pattern)$AFTER_REGEX";
}
return null;
}
}

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