Merge branch 'main' into main
14
.github/workflows/cache_dependencies.yml
vendored
|
@ -62,10 +62,22 @@ jobs:
|
||||||
/opt/android/cake_wallet/cw_haven/android/.cxx
|
/opt/android/cake_wallet/cw_haven/android/.cxx
|
||||||
/opt/android/cake_wallet/scripts/monero_c/release
|
/opt/android/cake_wallet/scripts/monero_c/release
|
||||||
key: ${{ hashFiles('**/prepare_moneroc.sh' ,'**/build_monero_all.sh' ,'**/cache_dependencies.yml') }}
|
key: ${{ hashFiles('**/prepare_moneroc.sh' ,'**/build_monero_all.sh' ,'**/cache_dependencies.yml') }}
|
||||||
|
|
||||||
- if: ${{ steps.cache-externals.outputs.cache-hit != 'true' }}
|
- if: ${{ steps.cache-externals.outputs.cache-hit != 'true' }}
|
||||||
name: Generate Externals
|
name: Generate Externals
|
||||||
run: |
|
run: |
|
||||||
cd /opt/android/cake_wallet/scripts/android/
|
cd /opt/android/cake_wallet/scripts/android/
|
||||||
source ./app_env.sh cakewallet
|
source ./app_env.sh cakewallet
|
||||||
./build_monero_all.sh
|
./build_monero_all.sh
|
||||||
|
|
||||||
|
- name: Cache Keystore
|
||||||
|
id: cache-keystore
|
||||||
|
uses: actions/cache@v3
|
||||||
|
with:
|
||||||
|
path: /opt/android/cake_wallet/android/app/key.jks
|
||||||
|
key: $STORE_PASS
|
||||||
|
|
||||||
|
- if: ${{ steps.cache-keystore.outputs.cache-hit != 'true' }}
|
||||||
|
name: Generate KeyStore
|
||||||
|
run: |
|
||||||
|
cd /opt/android/cake_wallet/android/app
|
||||||
|
keytool -genkey -v -keystore key.jks -keyalg RSA -keysize 2048 -validity 10000 -alias testKey -noprompt -dname "CN=CakeWallet, OU=CakeWallet, O=CakeWallet, L=Florida, S=America, C=USA" -storepass $STORE_PASS -keypass $KEY_PASS
|
||||||
|
|
48
.github/workflows/pr_test_build_android.yml
vendored
|
@ -115,6 +115,14 @@ jobs:
|
||||||
cd /opt/android/cake_wallet/scripts/android/
|
cd /opt/android/cake_wallet/scripts/android/
|
||||||
./build_mwebd.sh --dont-install
|
./build_mwebd.sh --dont-install
|
||||||
|
|
||||||
|
# - name: Cache Keystore
|
||||||
|
# id: cache-keystore
|
||||||
|
# uses: actions/cache@v3
|
||||||
|
# with:
|
||||||
|
# path: /opt/android/cake_wallet/android/app/key.jks
|
||||||
|
# key: $STORE_PASS
|
||||||
|
#
|
||||||
|
# - if: ${{ steps.cache-keystore.outputs.cache-hit != 'true' }}
|
||||||
- name: Generate KeyStore
|
- name: Generate KeyStore
|
||||||
run: |
|
run: |
|
||||||
cd /opt/android/cake_wallet/android/app
|
cd /opt/android/cake_wallet/android/app
|
||||||
|
@ -192,6 +200,8 @@ jobs:
|
||||||
echo "const nanoNowNodesApiKey = '${{ secrets.NANO_NOW_NODES_API_KEY }}';" >> cw_nano/lib/.secrets.g.dart
|
echo "const nanoNowNodesApiKey = '${{ secrets.NANO_NOW_NODES_API_KEY }}';" >> cw_nano/lib/.secrets.g.dart
|
||||||
echo "const tronGridApiKey = '${{ secrets.TRON_GRID_API_KEY }}';" >> cw_tron/lib/.secrets.g.dart
|
echo "const tronGridApiKey = '${{ secrets.TRON_GRID_API_KEY }}';" >> cw_tron/lib/.secrets.g.dart
|
||||||
echo "const tronNowNodesApiKey = '${{ secrets.TRON_NOW_NODES_API_KEY }}';" >> cw_tron/lib/.secrets.g.dart
|
echo "const tronNowNodesApiKey = '${{ secrets.TRON_NOW_NODES_API_KEY }}';" >> cw_tron/lib/.secrets.g.dart
|
||||||
|
echo "const meldTestApiKey = '${{ secrets.MELD_TEST_API_KEY }}';" >> lib/.secrets.g.dart
|
||||||
|
echo "const meldTestPublicKey = '${{ secrets.MELD_TEST_PUBLIC_KEY}}';" >> lib/.secrets.g.dar
|
||||||
echo "const letsExchangeBearerToken = '${{ secrets.LETS_EXCHANGE_TOKEN }}';" >> lib/.secrets.g.dart
|
echo "const letsExchangeBearerToken = '${{ secrets.LETS_EXCHANGE_TOKEN }}';" >> lib/.secrets.g.dart
|
||||||
echo "const letsExchangeAffiliateId = '${{ secrets.LETS_EXCHANGE_AFFILIATE_ID }}';" >> lib/.secrets.g.dart
|
echo "const letsExchangeAffiliateId = '${{ secrets.LETS_EXCHANGE_AFFILIATE_ID }}';" >> lib/.secrets.g.dart
|
||||||
echo "const stealthExBearerToken = '${{ secrets.STEALTH_EX_BEARER_TOKEN }}';" >> lib/.secrets.g.dart
|
echo "const stealthExBearerToken = '${{ secrets.STEALTH_EX_BEARER_TOKEN }}';" >> lib/.secrets.g.dart
|
||||||
|
@ -201,6 +211,36 @@ jobs:
|
||||||
run: |
|
run: |
|
||||||
echo -e "id=com.cakewallet.test_${{ env.PR_NUMBER }}\nname=${{ env.BRANCH_NAME }}" > /opt/android/cake_wallet/android/app.properties
|
echo -e "id=com.cakewallet.test_${{ env.PR_NUMBER }}\nname=${{ env.BRANCH_NAME }}" > /opt/android/cake_wallet/android/app.properties
|
||||||
|
|
||||||
|
# Step 3: Download previous build number
|
||||||
|
- name: Download previous build number
|
||||||
|
id: download-build-number
|
||||||
|
run: |
|
||||||
|
# Download the artifact if it exists
|
||||||
|
if [[ ! -f build_number.txt ]]; then
|
||||||
|
echo "1" > build_number.txt
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Step 4: Read and Increment Build Number
|
||||||
|
- name: Increment Build Number
|
||||||
|
id: increment-build-number
|
||||||
|
run: |
|
||||||
|
# Read current build number from file
|
||||||
|
BUILD_NUMBER=$(cat build_number.txt)
|
||||||
|
BUILD_NUMBER=$((BUILD_NUMBER + 1))
|
||||||
|
echo "New build number: $BUILD_NUMBER"
|
||||||
|
|
||||||
|
# Save the incremented build number
|
||||||
|
echo "$BUILD_NUMBER" > build_number.txt
|
||||||
|
|
||||||
|
# Export the build number to use in later steps
|
||||||
|
echo "BUILD_NUMBER=$BUILD_NUMBER" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
# Step 5: Update pubspec.yaml with new build number
|
||||||
|
- name: Update build number
|
||||||
|
run: |
|
||||||
|
cd /opt/android/cake_wallet
|
||||||
|
sed -i "s/^version: .*/version: 1.0.$BUILD_NUMBER/" pubspec.yaml
|
||||||
|
|
||||||
- name: Build
|
- name: Build
|
||||||
run: |
|
run: |
|
||||||
cd /opt/android/cake_wallet
|
cd /opt/android/cake_wallet
|
||||||
|
@ -231,6 +271,13 @@ jobs:
|
||||||
with:
|
with:
|
||||||
path: /opt/android/cake_wallet/build/app/outputs/flutter-apk/test-apk/
|
path: /opt/android/cake_wallet/build/app/outputs/flutter-apk/test-apk/
|
||||||
|
|
||||||
|
# Re-upload updated build number for the next run
|
||||||
|
- name: Upload updated build number
|
||||||
|
uses: actions/upload-artifact@v3
|
||||||
|
with:
|
||||||
|
name: build_number
|
||||||
|
path: build_number.txt
|
||||||
|
|
||||||
- name: Send Test APK
|
- name: Send Test APK
|
||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
uses: adrey/slack-file-upload-action@1.0.5
|
uses: adrey/slack-file-upload-action@1.0.5
|
||||||
|
@ -241,3 +288,4 @@ jobs:
|
||||||
title: "${{ env.BRANCH_NAME }}.apk"
|
title: "${{ env.BRANCH_NAME }}.apk"
|
||||||
filename: ${{ env.BRANCH_NAME }}.apk
|
filename: ${{ env.BRANCH_NAME }}.apk
|
||||||
initial_comment: ${{ github.event.head_commit.message }}
|
initial_comment: ${{ github.event.head_commit.message }}
|
||||||
|
|
||||||
|
|
2
.github/workflows/pr_test_build_linux.yml
vendored
|
@ -175,6 +175,8 @@ jobs:
|
||||||
echo "const nanoNowNodesApiKey = '${{ secrets.NANO_NOW_NODES_API_KEY }}';" >> cw_nano/lib/.secrets.g.dart
|
echo "const nanoNowNodesApiKey = '${{ secrets.NANO_NOW_NODES_API_KEY }}';" >> cw_nano/lib/.secrets.g.dart
|
||||||
echo "const tronGridApiKey = '${{ secrets.TRON_GRID_API_KEY }}';" >> cw_tron/lib/.secrets.g.dart
|
echo "const tronGridApiKey = '${{ secrets.TRON_GRID_API_KEY }}';" >> cw_tron/lib/.secrets.g.dart
|
||||||
echo "const tronNowNodesApiKey = '${{ secrets.TRON_NOW_NODES_API_KEY }}';" >> cw_tron/lib/.secrets.g.dart
|
echo "const tronNowNodesApiKey = '${{ secrets.TRON_NOW_NODES_API_KEY }}';" >> cw_tron/lib/.secrets.g.dart
|
||||||
|
echo "const meldTestApiKey = '${{ secrets.MELD_TEST_API_KEY }}';" >> lib/.secrets.g.dart
|
||||||
|
echo "const meldTestPublicKey = '${{ secrets.MELD_TEST_PUBLIC_KEY}}';" >> lib/.secrets.g.dar
|
||||||
echo "const letsExchangeBearerToken = '${{ secrets.LETS_EXCHANGE_TOKEN }}';" >> lib/.secrets.g.dart
|
echo "const letsExchangeBearerToken = '${{ secrets.LETS_EXCHANGE_TOKEN }}';" >> lib/.secrets.g.dart
|
||||||
echo "const letsExchangeAffiliateId = '${{ secrets.LETS_EXCHANGE_AFFILIATE_ID }}';" >> lib/.secrets.g.dart
|
echo "const letsExchangeAffiliateId = '${{ secrets.LETS_EXCHANGE_AFFILIATE_ID }}';" >> lib/.secrets.g.dart
|
||||||
echo "const stealthExBearerToken = '${{ secrets.STEALTH_EX_BEARER_TOKEN }}';" >> lib/.secrets.g.dart
|
echo "const stealthExBearerToken = '${{ secrets.STEALTH_EX_BEARER_TOKEN }}';" >> lib/.secrets.g.dart
|
||||||
|
|
|
@ -92,3 +92,8 @@ dependencies {
|
||||||
androidTestImplementation 'androidx.test:runner:1.3.0'
|
androidTestImplementation 'androidx.test:runner:1.3.0'
|
||||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
|
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
|
||||||
}
|
}
|
||||||
|
configurations {
|
||||||
|
implementation.exclude module:'proto-google-common-protos'
|
||||||
|
implementation.exclude module:'protolite-well-known-types'
|
||||||
|
implementation.exclude module:'protobuf-javalite'
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
org.gradle.jvmargs=-Xmx1536M
|
org.gradle.jvmargs=-Xmx3072M
|
||||||
android.enableR8=true
|
android.enableR8=true
|
||||||
android.useAndroidX=true
|
android.useAndroidX=true
|
||||||
android.enableJetifier=true
|
android.enableJetifier=true
|
||||||
|
|
BIN
assets/images/apple_pay_logo.png
Normal file
After Width: | Height: | Size: 55 KiB |
9
assets/images/apple_pay_round_dark.svg
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="256" height="256" viewBox="0 0 256 256" xml:space="preserve">
|
||||||
|
|
||||||
|
<defs>
|
||||||
|
</defs>
|
||||||
|
<g style="stroke: none; stroke-width: 0; stroke-dasharray: none; stroke-linecap: butt; stroke-linejoin: miter; stroke-miterlimit: 10; fill: none; fill-rule: nonzero; opacity: 1;" transform="translate(1.4065934065934016 1.4065934065934016) scale(2.81 2.81)" >
|
||||||
|
<path d="M 24.144 34.985 c -0.77 0.912 -2.003 1.631 -3.236 1.528 c -0.154 -1.233 0.449 -2.542 1.156 -3.351 c 0.77 -0.937 2.119 -1.605 3.21 -1.656 C 25.402 32.789 24.902 34.047 24.144 34.985 M 25.261 36.757 c -1.785 -0.103 -3.313 1.014 -4.16 1.014 c -0.86 0 -2.157 -0.963 -3.57 -0.937 c -1.836 0.026 -3.544 1.066 -4.481 2.722 c -1.926 3.313 -0.501 8.218 1.361 10.914 c 0.912 1.335 2.003 2.799 3.441 2.748 c 1.361 -0.051 1.9 -0.886 3.544 -0.886 c 1.656 0 2.131 0.886 3.57 0.86 c 1.489 -0.026 2.427 -1.335 3.338 -2.671 c 1.04 -1.515 1.464 -2.992 1.489 -3.069 c -0.026 -0.026 -2.876 -1.117 -2.902 -4.404 c -0.026 -2.748 2.247 -4.057 2.35 -4.134 C 27.958 37.014 25.954 36.808 25.261 36.757 M 35.572 33.033 v 20.018 h 3.107 v -6.844 h 4.301 c 3.929 0 6.69 -2.696 6.69 -6.6 s -2.709 -6.574 -6.587 -6.574 H 35.572 L 35.572 33.033 z M 38.679 35.652 h 3.582 c 2.696 0 4.237 1.438 4.237 3.968 c 0 2.529 -1.541 3.98 -4.25 3.98 h -3.57 V 35.652 z M 55.345 53.205 c 1.952 0 3.762 -0.989 4.584 -2.555 h 0.064 v 2.401 h 2.876 v -9.964 c 0 -2.889 -2.311 -4.751 -5.868 -4.751 c -3.3 0 -5.739 1.887 -5.829 4.481 h 2.799 c 0.231 -1.233 1.374 -2.042 2.94 -2.042 c 1.9 0 2.966 0.886 2.966 2.517 v 1.104 L 56 44.628 c -3.608 0.218 -5.56 1.695 -5.56 4.263 C 50.44 51.484 52.456 53.205 55.345 53.205 z M 56.18 50.829 c -1.656 0 -2.709 -0.796 -2.709 -2.016 c 0 -1.258 1.014 -1.99 2.953 -2.106 l 3.454 -0.218 v 1.13 C 59.878 49.494 58.286 50.829 56.18 50.829 z M 66.709 58.495 c 3.03 0 4.455 -1.156 5.701 -4.661 l 5.457 -15.305 h -3.159 l -3.659 11.826 h -0.064 l -3.659 -11.826 h -3.249 l 5.264 14.573 l -0.282 0.886 c -0.475 1.502 -1.245 2.08 -2.619 2.08 c -0.244 0 -0.719 -0.026 -0.912 -0.051 v 2.401 C 65.707 58.469 66.478 58.495 66.709 58.495 z" style="stroke: none; stroke-width: 1; stroke-dasharray: none; stroke-linecap: butt; stroke-linejoin: miter; stroke-miterlimit: 10; fill: white; fill-rule: nonzero; opacity: 1;" transform=" matrix(1 0 0 1 0 0) " stroke-linecap="round" />
|
||||||
|
<path d="M 45 90 C 20.187 90 0 69.813 0 45 C 0 20.187 20.187 0 45 0 c 24.813 0 45 20.187 45 45 C 90 69.813 69.813 90 45 90 z M 45 3 C 21.841 3 3 21.841 3 45 c 0 23.159 18.841 42 42 42 c 23.159 0 42 -18.841 42 -42 C 87 21.841 68.159 3 45 3 z" style="stroke: none; stroke-width: 1; stroke-dasharray: none; stroke-linecap: butt; stroke-linejoin: miter; stroke-miterlimit: 10; fill: white; fill-rule: nonzero; opacity: 1;" transform=" matrix(1 0 0 1 0 0) " stroke-linecap="round" />
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 2.8 KiB |
9
assets/images/apple_pay_round_light.svg
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="256" height="256" viewBox="0 0 256 256" xml:space="preserve">
|
||||||
|
|
||||||
|
<defs>
|
||||||
|
</defs>
|
||||||
|
<g style="stroke: none; stroke-width: 0; stroke-dasharray: none; stroke-linecap: butt; stroke-linejoin: miter; stroke-miterlimit: 10; fill: none; fill-rule: nonzero; opacity: 1;" transform="translate(1.4065934065934016 1.4065934065934016) scale(2.81 2.81)" >
|
||||||
|
<path d="M 24.144 34.985 c -0.77 0.912 -2.003 1.631 -3.236 1.528 c -0.154 -1.233 0.449 -2.542 1.156 -3.351 c 0.77 -0.937 2.119 -1.605 3.21 -1.656 C 25.402 32.789 24.902 34.047 24.144 34.985 M 25.261 36.757 c -1.785 -0.103 -3.313 1.014 -4.16 1.014 c -0.86 0 -2.157 -0.963 -3.57 -0.937 c -1.836 0.026 -3.544 1.066 -4.481 2.722 c -1.926 3.313 -0.501 8.218 1.361 10.914 c 0.912 1.335 2.003 2.799 3.441 2.748 c 1.361 -0.051 1.9 -0.886 3.544 -0.886 c 1.656 0 2.131 0.886 3.57 0.86 c 1.489 -0.026 2.427 -1.335 3.338 -2.671 c 1.04 -1.515 1.464 -2.992 1.489 -3.069 c -0.026 -0.026 -2.876 -1.117 -2.902 -4.404 c -0.026 -2.748 2.247 -4.057 2.35 -4.134 C 27.958 37.014 25.954 36.808 25.261 36.757 M 35.572 33.033 v 20.018 h 3.107 v -6.844 h 4.301 c 3.929 0 6.69 -2.696 6.69 -6.6 s -2.709 -6.574 -6.587 -6.574 H 35.572 L 35.572 33.033 z M 38.679 35.652 h 3.582 c 2.696 0 4.237 1.438 4.237 3.968 c 0 2.529 -1.541 3.98 -4.25 3.98 h -3.57 V 35.652 z M 55.345 53.205 c 1.952 0 3.762 -0.989 4.584 -2.555 h 0.064 v 2.401 h 2.876 v -9.964 c 0 -2.889 -2.311 -4.751 -5.868 -4.751 c -3.3 0 -5.739 1.887 -5.829 4.481 h 2.799 c 0.231 -1.233 1.374 -2.042 2.94 -2.042 c 1.9 0 2.966 0.886 2.966 2.517 v 1.104 L 56 44.628 c -3.608 0.218 -5.56 1.695 -5.56 4.263 C 50.44 51.484 52.456 53.205 55.345 53.205 z M 56.18 50.829 c -1.656 0 -2.709 -0.796 -2.709 -2.016 c 0 -1.258 1.014 -1.99 2.953 -2.106 l 3.454 -0.218 v 1.13 C 59.878 49.494 58.286 50.829 56.18 50.829 z M 66.709 58.495 c 3.03 0 4.455 -1.156 5.701 -4.661 l 5.457 -15.305 h -3.159 l -3.659 11.826 h -0.064 l -3.659 -11.826 h -3.249 l 5.264 14.573 l -0.282 0.886 c -0.475 1.502 -1.245 2.08 -2.619 2.08 c -0.244 0 -0.719 -0.026 -0.912 -0.051 v 2.401 C 65.707 58.469 66.478 58.495 66.709 58.495 z" style="stroke: none; stroke-width: 1; stroke-dasharray: none; stroke-linecap: butt; stroke-linejoin: miter; stroke-miterlimit: 10; fill: rgb(0,0,0); fill-rule: nonzero; opacity: 1;" transform=" matrix(1 0 0 1 0 0) " stroke-linecap="round" />
|
||||||
|
<path d="M 45 90 C 20.187 90 0 69.813 0 45 C 0 20.187 20.187 0 45 0 c 24.813 0 45 20.187 45 45 C 90 69.813 69.813 90 45 90 z M 45 3 C 21.841 3 3 21.841 3 45 c 0 23.159 18.841 42 42 42 c 23.159 0 42 -18.841 42 -42 C 87 21.841 68.159 3 45 3 z" style="stroke: none; stroke-width: 1; stroke-dasharray: none; stroke-linecap: butt; stroke-linejoin: miter; stroke-miterlimit: 10; fill: rgb(0,0,0); fill-rule: nonzero; opacity: 1;" transform=" matrix(1 0 0 1 0 0) " stroke-linecap="round" />
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 2.8 KiB |
BIN
assets/images/bank.png
Normal file
After Width: | Height: | Size: 1.3 KiB |
8
assets/images/bank_dark.svg
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="256" height="256" viewBox="0 0 256 256" xml:space="preserve">
|
||||||
|
|
||||||
|
<defs>
|
||||||
|
</defs>
|
||||||
|
<g style="stroke: none; stroke-width: 0; stroke-dasharray: none; stroke-linecap: butt; stroke-linejoin: miter; stroke-miterlimit: 10; fill: white; fill-rule: nonzero; opacity: 1;" transform="translate(1.4065934065934016 1.4065934065934016) scale(2.81 2.81)" >
|
||||||
|
<path d="M 84.668 38.004 v -6.27 H 90 V 20 L 45 3.034 L 0 20 v 11.734 h 5.332 v 6.27 h 4.818 v 30.892 H 5.332 v 6.271 H 0 v 11.8 h 90 v -11.8 h -5.332 v -6.271 H 79.85 V 38.004 H 84.668 z M 81.668 35.004 H 66.332 v -3.27 h 15.336 V 35.004 z M 63.332 68.896 v 6.271 h -7.664 v -6.271 H 50.85 V 38.004 h 4.818 v -6.27 h 7.664 v 6.27 h 4.818 v 30.892 H 63.332 z M 26.668 38.004 v -6.27 h 7.664 v 6.27 h 4.818 v 30.892 h -4.818 v 6.271 h -7.664 v -6.271 H 21.85 V 38.004 H 26.668 z M 42.15 68.896 V 38.004 h 5.7 v 30.892 H 42.15 z M 37.332 35.004 v -3.27 h 15.336 v 3.27 H 37.332 z M 37.332 71.896 h 15.336 v 3.271 H 37.332 V 71.896 z M 3 22.075 L 45 6.24 l 42 15.835 v 6.659 H 3 V 22.075 z M 8.332 31.734 h 15.336 v 3.27 H 8.332 V 31.734 z M 13.15 38.004 h 5.7 v 30.892 h -5.7 V 38.004 z M 8.332 71.896 h 15.336 v 3.271 H 8.332 V 71.896 z M 87 83.966 H 3 v -5.8 h 84 V 83.966 z M 81.668 75.166 H 66.332 v -3.271 h 15.336 V 75.166 z M 76.85 68.896 H 71.15 V 38.004 h 5.699 V 68.896 z" style="stroke: none; stroke-width: 1; stroke-dasharray: none; stroke-linecap: butt; stroke-linejoin: miter; stroke-miterlimit: 10; fill: white; fill-rule: nonzero; opacity: 1;" transform=" matrix(1 0 0 1 0 0) " stroke-linecap="round" />
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.6 KiB |
8
assets/images/bank_light.svg
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="256" height="256" viewBox="0 0 256 256" xml:space="preserve">
|
||||||
|
|
||||||
|
<defs>
|
||||||
|
</defs>
|
||||||
|
<g style="stroke: none; stroke-width: 0; stroke-dasharray: none; stroke-linecap: butt; stroke-linejoin: miter; stroke-miterlimit: 10; fill: black; fill-rule: nonzero; opacity: 1;" transform="translate(1.4065934065934016 1.4065934065934016) scale(2.81 2.81)" >
|
||||||
|
<path d="M 84.668 38.004 v -6.27 H 90 V 20 L 45 3.034 L 0 20 v 11.734 h 5.332 v 6.27 h 4.818 v 30.892 H 5.332 v 6.271 H 0 v 11.8 h 90 v -11.8 h -5.332 v -6.271 H 79.85 V 38.004 H 84.668 z M 81.668 35.004 H 66.332 v -3.27 h 15.336 V 35.004 z M 63.332 68.896 v 6.271 h -7.664 v -6.271 H 50.85 V 38.004 h 4.818 v -6.27 h 7.664 v 6.27 h 4.818 v 30.892 H 63.332 z M 26.668 38.004 v -6.27 h 7.664 v 6.27 h 4.818 v 30.892 h -4.818 v 6.271 h -7.664 v -6.271 H 21.85 V 38.004 H 26.668 z M 42.15 68.896 V 38.004 h 5.7 v 30.892 H 42.15 z M 37.332 35.004 v -3.27 h 15.336 v 3.27 H 37.332 z M 37.332 71.896 h 15.336 v 3.271 H 37.332 V 71.896 z M 3 22.075 L 45 6.24 l 42 15.835 v 6.659 H 3 V 22.075 z M 8.332 31.734 h 15.336 v 3.27 H 8.332 V 31.734 z M 13.15 38.004 h 5.7 v 30.892 h -5.7 V 38.004 z M 8.332 71.896 h 15.336 v 3.271 H 8.332 V 71.896 z M 87 83.966 H 3 v -5.8 h 84 V 83.966 z M 81.668 75.166 H 66.332 v -3.271 h 15.336 V 75.166 z M 76.85 68.896 H 71.15 V 38.004 h 5.699 V 68.896 z" style="stroke: none; stroke-width: 1; stroke-dasharray: none; stroke-linecap: butt; stroke-linejoin: miter; stroke-miterlimit: 10; fill: black; fill-rule: nonzero; opacity: 1;" transform=" matrix(1 0 0 1 0 0) " stroke-linecap="round" />
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.6 KiB |
BIN
assets/images/buy_sell.png
Normal file
After Width: | Height: | Size: 9.7 KiB |
1
assets/images/card.svg
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<?xml version="1.0" encoding="iso-8859-1"?><!-- Generator: Adobe Illustrator 19.2.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) --><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 50 50" width="100px" height="100px"><path style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-miterlimit:10;" d="M43,40H7c-2.209,0-4-1.791-4-4V14c0-2.209,1.791-4,4-4h36c2.209,0,4,1.791,4,4v22C47,38.209,45.209,40,43,40z"/><rect x="3" y="16" width="44" height="5"/><line style="fill:none;stroke:#000000;stroke-width:2;stroke-miterlimit:10;" x1="9" y1="25" x2="25" y2="25"/></svg>
|
After Width: | Height: | Size: 633 B |
7
assets/images/card_dark.svg
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||||
|
<!-- Generator: Adobe Illustrator 19.2.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 50 50" width="100px" height="100px">
|
||||||
|
<path style="fill:none;stroke:#ffffff;stroke-width:2;stroke-linecap:round;stroke-miterlimit:10;" d="M43,40H7c-2.209,0-4-1.791-4-4V14c0-2.209,1.791-4,4-4h36c2.209,0,4,1.791,4,4v22C47,38.209,45.209,40,43,40z"/>
|
||||||
|
<rect x="3" y="16" width="44" height="5" style="fill:white;stroke:#ffffff;stroke-width:2;"/>
|
||||||
|
<line style="fill:none;stroke:#ffffff;stroke-width:2;stroke-miterlimit:10;" x1="9" y1="25" x2="25" y2="25"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 701 B |
12
assets/images/dollar_coin.svg
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
<?xml version="1.0" ?>
|
||||||
|
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
||||||
|
<svg fill="#000000" width="800px" height="800px" viewBox="0 0 64 64" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<g id="Outline">
|
||||||
|
<g data-name="Outline" id="Outline-2">
|
||||||
|
<path d="M39,36.852a6.8,6.8,0,0,0-6.793-6.793h-.319A3.716,3.716,0,1,1,35.6,26.344a1,1,0,0,0,2,0,5.725,5.725,0,0,0-4.561-5.6V18.09a1,1,0,0,0-2,0V20.7a5.712,5.712,0,0,0,.846,11.361h.319a4.793,4.793,0,1,1-4.793,4.793,1,1,0,0,0-2,0A6.8,6.8,0,0,0,31.451,43.6v2.947a1,1,0,0,0,2,0v-3c0-.008,0-.014,0-.021A6.8,6.8,0,0,0,39,36.852Z"/>
|
||||||
|
<path d="M32,2A30,30,0,1,0,62,32,30.034,30.034,0,0,0,32,2Zm0,58A28,28,0,1,1,60,32,28.032,28.032,0,0,1,32,60Z"/>
|
||||||
|
<path d="M49.655,16.793a3.172,3.172,0,1,0-3.172,3.172,3.137,3.137,0,0,0,1.263-.266A19.994,19.994,0,0,1,22.692,49.707a1,1,0,0,0-.933,1.769,21.986,21.986,0,0,0,27.47-33.124A3.141,3.141,0,0,0,49.655,16.793Zm-4.344,0a1.172,1.172,0,1,1,1.172,1.172A1.172,1.172,0,0,1,45.311,16.793Z"/>
|
||||||
|
<path d="M16.793,44.035a3.164,3.164,0,0,0-.692.081A19.779,19.779,0,0,1,12,32,20.023,20.023,0,0,1,32,12a19.811,19.811,0,0,1,8.463,1.874,1,1,0,0,0,.848-1.812A21.989,21.989,0,0,0,14.39,45.16a3.141,3.141,0,0,0-.769,2.047,3.172,3.172,0,1,0,3.172-3.172Zm0,4.344a1.172,1.172,0,1,1,1.172-1.172A1.172,1.172,0,0,1,16.793,48.379Z"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.3 KiB |
BIN
assets/images/google_pay_icon.png
Normal file
After Width: | Height: | Size: 12 KiB |
30
assets/images/meld_logo.svg
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
<svg width="40" height="42" viewBox="0 0 40 42" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path opacity="0.7" d="M19.9526 41.9076L7.3877 34.655V26.1226L19.9526 33.3751V41.9076Z" fill="url(#paint0_linear_2113_32117)"/>
|
||||||
|
<path opacity="0.7" d="M19.9521 41.9076L32.5171 34.655V26.1226L19.9521 33.3751V41.9076Z" fill="url(#paint1_linear_2113_32117)"/>
|
||||||
|
<path opacity="0.7" d="M39.9095 7.34521V21.8562L32.5166 26.1225V11.6114L39.9095 7.34521Z" fill="url(#paint2_linear_2113_32117)"/>
|
||||||
|
<path d="M39.9099 7.34536L27.345 0.0927734L19.9521 4.359L32.5171 11.6116L39.9099 7.34536Z" fill="url(#paint3_linear_2113_32117)"/>
|
||||||
|
<path d="M0 7.34536L12.5649 0.0927734L19.9519 4.359L7.387 11.6116L0 7.34536Z" fill="#F969D3"/>
|
||||||
|
<path opacity="0.7" d="M0 7.34521V21.8562L7.387 26.1225V11.6114L0 7.34521Z" fill="url(#paint4_linear_2113_32117)"/>
|
||||||
|
<defs>
|
||||||
|
<linearGradient id="paint0_linear_2113_32117" x1="18.6099" y1="41.8335" x2="7.73529" y2="8.31842" gradientUnits="userSpaceOnUse">
|
||||||
|
<stop stop-color="#E98ADA"/>
|
||||||
|
<stop offset="1" stop-color="#7E4DBD"/>
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient id="paint1_linear_2113_32117" x1="26.2346" y1="26.1226" x2="26.2346" y2="41.9076" gradientUnits="userSpaceOnUse">
|
||||||
|
<stop stop-color="#719DED"/>
|
||||||
|
<stop offset="1" stop-color="#2545BE"/>
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient id="paint2_linear_2113_32117" x1="36.213" y1="7.34521" x2="36.213" y2="26.1225" gradientUnits="userSpaceOnUse">
|
||||||
|
<stop stop-color="#93EBFF"/>
|
||||||
|
<stop offset="1" stop-color="#197DDB"/>
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient id="paint3_linear_2113_32117" x1="29.931" y1="0.0927734" x2="38.2156" y2="14.8448" gradientUnits="userSpaceOnUse">
|
||||||
|
<stop stop-color="#F969D3"/>
|
||||||
|
<stop offset="1" stop-color="#4F51C0"/>
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient id="paint4_linear_2113_32117" x1="18.1251" y1="44.2539" x2="-7.06792" y2="15.2763" gradientUnits="userSpaceOnUse">
|
||||||
|
<stop stop-color="#E98ADA"/>
|
||||||
|
<stop offset="1" stop-color="#7E4DBD"/>
|
||||||
|
</linearGradient>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.9 KiB |
BIN
assets/images/revolut.png
Normal file
After Width: | Height: | Size: 11 KiB |
15
assets/images/skrill.svg
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="256" height="256" viewBox="0 0 256 256" xml:space="preserve">
|
||||||
|
|
||||||
|
<defs>
|
||||||
|
</defs>
|
||||||
|
<g style="stroke: none; stroke-width: 0; stroke-dasharray: none; stroke-linecap: butt; stroke-linejoin: miter; stroke-miterlimit: 10; fill: none; fill-rule: nonzero; opacity: 1;" transform="translate(1.4065934065934016 1.4065934065934016) scale(2.81 2.81)" >
|
||||||
|
<circle cx="45" cy="45" r="45" style="stroke: none; stroke-width: 1; stroke-dasharray: none; stroke-linecap: butt; stroke-linejoin: miter; stroke-miterlimit: 10; fill: rgb(127,33,99); fill-rule: nonzero; opacity: 1;" transform=" matrix(1 0 0 1 0 0) "/>
|
||||||
|
<polygon points="69.59,36.9 69.59,54.86 74.5,54.86 74.5,36.02 " style="stroke: none; stroke-width: 1; stroke-dasharray: none; stroke-linecap: butt; stroke-linejoin: miter; stroke-miterlimit: 10; fill: rgb(249,249,249); fill-rule: nonzero; opacity: 1;" transform=" matrix(1 0 0 1 0 0) "/>
|
||||||
|
<polygon points="62.42,36.9 67.33,36.02 67.33,54.87 62.42,54.87 " style="stroke: none; stroke-width: 1; stroke-dasharray: none; stroke-linecap: butt; stroke-linejoin: miter; stroke-miterlimit: 10; fill: rgb(249,249,249); fill-rule: nonzero; opacity: 1;" transform=" matrix(1 0 0 1 0 0) "/>
|
||||||
|
<rect x="55.43" y="41.08" rx="0" ry="0" width="4.91" height="13.78" style="stroke: none; stroke-width: 1; stroke-dasharray: none; stroke-linecap: butt; stroke-linejoin: miter; stroke-miterlimit: 10; fill: rgb(249,249,249); fill-rule: nonzero; opacity: 1;" transform=" matrix(1 0 0 1 0 0) "/>
|
||||||
|
<path d="M 57.879 39.76 c 1.332 0 2.425 -1.082 2.425 -2.415 s -1.082 -2.425 -2.425 -2.425 c -1.332 0 -2.415 1.082 -2.415 2.425 C 55.465 38.677 56.547 39.76 57.879 39.76 z" style="stroke: none; stroke-width: 1; stroke-dasharray: none; stroke-linecap: butt; stroke-linejoin: miter; stroke-miterlimit: 10; fill: rgb(249,249,249); fill-rule: nonzero; opacity: 1;" transform=" matrix(1 0 0 1 0 0) " stroke-linecap="round" />
|
||||||
|
<path d="M 52.665 40.884 c -4.538 0.146 -6.838 2.186 -6.838 6.234 v 7.754 h 4.954 v -6.328 c 0 -2.425 0.312 -3.466 3.195 -3.559 v -4.038 C 53.477 40.853 52.665 40.884 52.665 40.884 L 52.665 40.884 z" style="stroke: none; stroke-width: 1; stroke-dasharray: none; stroke-linecap: butt; stroke-linejoin: miter; stroke-miterlimit: 10; fill: rgb(249,249,249); fill-rule: nonzero; opacity: 1;" transform=" matrix(1 0 0 1 0 0) " stroke-linecap="round" />
|
||||||
|
<path d="M 39.344 41.071 c -0.104 0.271 -0.895 2.498 -2.8 4.798 v -9.845 l -5.068 0.999 v 17.838 h 5.068 v -5.516 c 1.467 2.206 2.196 5.516 2.196 5.516 h 6.068 c -0.604 -2.498 -3.226 -7.098 -3.226 -7.098 c 2.352 -2.987 3.393 -6.172 3.559 -6.702 h -5.797 L 39.344 41.071 z" style="stroke: none; stroke-width: 1; stroke-dasharray: none; stroke-linecap: butt; stroke-linejoin: miter; stroke-miterlimit: 10; fill: rgb(249,249,249); fill-rule: nonzero; opacity: 1;" transform=" matrix(1 0 0 1 0 0) " stroke-linecap="round" />
|
||||||
|
<path d="M 22.973 43.028 c -0.624 -0.042 -2.061 -0.135 -2.061 -1.426 c 0 -1.561 2.071 -1.561 2.841 -1.561 c 1.363 0 3.133 0.406 4.392 0.781 c 0 0 0.708 0.25 1.301 0.5 l 0.052 0.01 v -4.267 l -0.073 -0.021 c -1.488 -0.52 -3.216 -1.02 -6.432 -1.02 c -5.537 0 -7.493 3.226 -7.493 5.984 c 0 1.592 0.687 5.339 7.025 5.776 c 0.541 0.031 1.967 0.114 1.967 1.457 c 0 1.103 -1.166 1.759 -3.133 1.759 c -2.154 0 -4.236 -0.552 -5.506 -1.072 v 4.402 c 1.894 0.5 4.038 0.749 6.546 0.749 c 5.412 0 7.837 -3.049 7.837 -6.078 C 30.237 45.567 27.531 43.34 22.973 43.028 z" style="stroke: none; stroke-width: 1; stroke-dasharray: none; stroke-linecap: butt; stroke-linejoin: miter; stroke-miterlimit: 10; fill: rgb(249,249,249); fill-rule: nonzero; opacity: 1;" transform=" matrix(1 0 0 1 0 0) " stroke-linecap="round" />
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 3.7 KiB |
4
assets/images/usd_round_dark.svg
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" id="Layer_1" data-name="Layer 1" viewBox="0 0 24 24" width="512" height="512">
|
||||||
|
<path d="M12,0C5.383,0,0,5.383,0,12s5.383,12,12,12,12-5.383,12-12S18.617,0,12,0Zm0,23c-6.065,0-11-4.935-11-11S5.935,1,12,1s11,4.935,11,11-4.935,11-11,11Zm4-8.626c0,1.448-1.178,2.626-2.626,2.626h-.874v1.5c0,.276-.224,.5-.5,.5s-.5-.224-.5-.5v-1.5h-.926c-.979,0-1.893-.526-2.382-1.375-.139-.239-.057-.545,.183-.683,.238-.14,.544-.057,.683,.183,.312,.54,.894,.875,1.517,.875h2.8c.896,0,1.626-.729,1.626-1.626,0-.803-.575-1.478-1.368-1.605l-3.422-.55c-1.28-.206-2.21-1.296-2.21-2.593,0-1.448,1.178-2.626,2.626-2.626h.874v-1.5c0-.276,.224-.5,.5-.5s.5,.224,.5,.5v1.5h.926c.979,0,1.892,.527,2.382,1.375,.139,.239,.057,.545-.183,.683-.236,.136-.545,.057-.683-.183-.312-.54-.894-.875-1.517-.875h-2.8c-.896,0-1.626,.729-1.626,1.626,0,.803,.575,1.478,1.368,1.605l3.422,.55c1.28,.206,2.21,1.297,2.21,2.593Z" fill="white"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 978 B |
2
assets/images/usd_round_light.svg
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" id="Layer_1" data-name="Layer 1" viewBox="0 0 24 24" width="512" height="512"><path d="M12,0C5.383,0,0,5.383,0,12s5.383,12,12,12,12-5.383,12-12S18.617,0,12,0Zm0,23c-6.065,0-11-4.935-11-11S5.935,1,12,1s11,4.935,11,11-4.935,11-11,11Zm4-8.626c0,1.448-1.178,2.626-2.626,2.626h-.874v1.5c0,.276-.224,.5-.5,.5s-.5-.224-.5-.5v-1.5h-.926c-.979,0-1.893-.526-2.382-1.375-.139-.239-.057-.545,.183-.683,.238-.14,.544-.057,.683,.183,.312,.54,.894,.875,1.517,.875h2.8c.896,0,1.626-.729,1.626-1.626,0-.803-.575-1.478-1.368-1.605l-3.422-.55c-1.28-.206-2.21-1.296-2.21-2.593,0-1.448,1.178-2.626,2.626-2.626h.874v-1.5c0-.276,.224-.5,.5-.5s.5,.224,.5,.5v1.5h.926c.979,0,1.892,.527,2.382,1.375,.139,.239,.057,.545-.183,.683-.236,.136-.545,.057-.683-.183-.312-.54-.894-.875-1.517-.875h-2.8c-.896,0-1.626,.729-1.626,1.626,0,.803,.575,1.478,1.368,1.605l3.422,.55c1.28,.206,2.21,1.297,2.21,2.593Z"/></svg>
|
After Width: | Height: | Size: 960 B |
BIN
assets/images/wallet_new.png
Normal file
After Width: | Height: | Size: 5.5 KiB |
|
@ -1,6 +1,7 @@
|
||||||
-
|
-
|
||||||
uri: xmr-node.cakewallet.com:18081
|
uri: xmr-node.cakewallet.com:18081
|
||||||
is_default: true
|
is_default: true
|
||||||
|
trusted: true
|
||||||
-
|
-
|
||||||
uri: cakexmrl7bonq7ovjka5kuwuyd3f7qnkz6z6s6dmsy3uckwra7bvggyd.onion:18081
|
uri: cakexmrl7bonq7ovjka5kuwuyd3f7qnkz6z6s6dmsy3uckwra7bvggyd.onion:18081
|
||||||
is_default: false
|
is_default: false
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
Monero enhancements
|
Add airgapped Monero wallet support (best used with our new offline app Cupcake)
|
||||||
Introducing StealthEx and LetxExchange
|
New Buy & Sell flow
|
||||||
Bug fixes
|
Bug fixes
|
|
@ -1,7 +1,5 @@
|
||||||
Added Litecoin MWEB
|
Add Litecoin Ledger support
|
||||||
Added wallet groups
|
Add airgapped Monero wallet support (best used with our new offline app Cupcake)
|
||||||
Silent Payment enhancements for speed & reliability
|
MWEB fixes and enhancements
|
||||||
Monero enhancements
|
New Buy & Sell flow
|
||||||
Introducing StealthEx and LetxExchange
|
|
||||||
Additional ERC20 tokens scam detection
|
|
||||||
Bug fixes
|
Bug fixes
|
38
build-guide-win.md
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
# Building CakeWallet for Windows
|
||||||
|
|
||||||
|
## Requirements and Setup
|
||||||
|
|
||||||
|
The following are the system requirements to build CakeWallet for your Windows PC.
|
||||||
|
|
||||||
|
```
|
||||||
|
Windows 10 or later (64-bit), x86-64 based
|
||||||
|
Flutter 3 or above
|
||||||
|
```
|
||||||
|
|
||||||
|
## Building CakeWallet on Windows
|
||||||
|
|
||||||
|
These steps will help you configure and execute a build of CakeWallet from its source code.
|
||||||
|
|
||||||
|
### 1. Installing Package Dependencies
|
||||||
|
|
||||||
|
For build CakeWallet windows application from sources you will be needed to have:
|
||||||
|
> [Install Flutter]Follow installation guide (https://docs.flutter.dev/get-started/install/windows) and install do not miss to dev tools (install https://docs.flutter.dev/get-started/install/windows/desktop#development-tools) which are required for windows desktop development (need to install Git for Windows and Visual Studio 2022). Then install `Desktop development with C++` packages via GUI Visual Studio 2022, or Visual Studio Build Tools 2022 including: `C++ Build Tools core features`, `C++ 2022 Redistributable Update`, `C++ core desktop features`, `MVC v143 - VS 2022 C++ x64/x86 build tools`, `C++ CMake tools for Windwos`, `Testing tools core features - Build Tools`, `C++ AddressSanitizer`.
|
||||||
|
> [Install WSL] for building monero dependencies need to install Windows WSL (https://learn.microsoft.com/en-us/windows/wsl/install) and required packages for WSL (Ubuntu):
|
||||||
|
`$ sudo apt update `
|
||||||
|
`$ sudo apt build-essential cmake gcc-mingw-w64 g++-mingw-w64 autoconf libtool pkg-config`
|
||||||
|
|
||||||
|
### 2. Pull CakeWallet source code
|
||||||
|
|
||||||
|
You can downlaod CakeWallet source code from our [GitHub repository](github.com/cake-tech/cake_wallet) via git by following next command:
|
||||||
|
`$ git clone https://github.com/cake-tech/cake_wallet.git --branch MrCyjaneK-cyjan-monerodart`
|
||||||
|
OR you can download it as [Zip archive](https://github.com/cake-tech/cake_wallet/archive/refs/heads/MrCyjaneK-cyjan-monerodart.zip)
|
||||||
|
|
||||||
|
### 3. Build Monero, Monero_c and their dependencies
|
||||||
|
|
||||||
|
For use monero in the application need to build Monero wrapper - Monero_C which will be used by monero.dart package. For that need to run shell (bash - typically same named utility should be available after WSL is enabled in your system) with previously installed WSL, then change current directory to the application project directory with your used shell and then change current directory to `scripts/windows`: `$ cd scripts/windows`. Run build script: `$ ./build_all.sh`.
|
||||||
|
|
||||||
|
### 4. Configure and build CakeWallet application
|
||||||
|
|
||||||
|
To configure the application open directory where you have downloaded or unarchived CakeWallet sources and run `cakewallet.bat`.
|
||||||
|
Or if you used WSL and have active shell session you can run `$ ./cakewallet.sh` script in `scripts/windows` which will run `cakewallet.bat` in WSL.
|
||||||
|
After execution of `cakewallet.bat` you should to get `Cake Wallet.zip` in project root directory which will contains `CakeWallet.exe` file and another needed files for run the application. Now you can extract files from `Cake Wallet.zip` archive and run the application.
|
|
@ -574,6 +574,8 @@ abstract class ElectrumWalletBase
|
||||||
Future<void> connectToNode({required Node node}) async {
|
Future<void> connectToNode({required Node node}) async {
|
||||||
this.node = node;
|
this.node = node;
|
||||||
|
|
||||||
|
if (syncStatus is ConnectingSyncStatus) return;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
syncStatus = ConnectingSyncStatus();
|
syncStatus = ConnectingSyncStatus();
|
||||||
|
|
||||||
|
@ -2216,13 +2218,14 @@ abstract class ElectrumWalletBase
|
||||||
if (syncStatus is NotConnectedSyncStatus ||
|
if (syncStatus is NotConnectedSyncStatus ||
|
||||||
syncStatus is LostConnectionSyncStatus ||
|
syncStatus is LostConnectionSyncStatus ||
|
||||||
syncStatus is ConnectingSyncStatus) {
|
syncStatus is ConnectingSyncStatus) {
|
||||||
syncStatus = AttemptingSyncStatus();
|
syncStatus = ConnectedSyncStatus();
|
||||||
startSync();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case ConnectionStatus.disconnected:
|
case ConnectionStatus.disconnected:
|
||||||
if (syncStatus is! NotConnectedSyncStatus) {
|
if (syncStatus is! NotConnectedSyncStatus &&
|
||||||
|
syncStatus is! ConnectingSyncStatus &&
|
||||||
|
syncStatus is! SyncronizingSyncStatus) {
|
||||||
syncStatus = NotConnectedSyncStatus();
|
syncStatus = NotConnectedSyncStatus();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -153,4 +153,9 @@ class PendingBitcoinTransaction with PendingTransaction {
|
||||||
inputAddresses: _tx.inputs.map((input) => input.txId).toList(),
|
inputAddresses: _tx.inputs.map((input) => input.txId).toList(),
|
||||||
outputAddresses: outputAddresses,
|
outputAddresses: outputAddresses,
|
||||||
fee: fee);
|
fee: fee);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<String?> commitUR() {
|
||||||
|
throw UnimplementedError();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -87,8 +87,8 @@ packages:
|
||||||
dependency: "direct overridden"
|
dependency: "direct overridden"
|
||||||
description:
|
description:
|
||||||
path: "."
|
path: "."
|
||||||
ref: cake-update-v8
|
ref: cake-update-v9
|
||||||
resolved-ref: fc045a11db3d85d806ca67f75e8b916c706745a2
|
resolved-ref: "86969a14e337383e14965f5fb45a72a63e5009bc"
|
||||||
url: "https://github.com/cake-tech/bitcoin_base"
|
url: "https://github.com/cake-tech/bitcoin_base"
|
||||||
source: git
|
source: git
|
||||||
version: "4.7.0"
|
version: "4.7.0"
|
||||||
|
@ -386,10 +386,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: flutter_web_bluetooth
|
name: flutter_web_bluetooth
|
||||||
sha256: "52ce64f65d7321c4bf6abfe9dac02fb888731339a5e0ad6de59fb916c20c9f02"
|
sha256: fcd03e2e5f82edcedcbc940f1b6a0635a50757374183254f447640886c53208e
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.2.3"
|
version: "0.2.4"
|
||||||
flutter_web_plugins:
|
flutter_web_plugins:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description: flutter
|
description: flutter
|
||||||
|
@ -560,7 +560,7 @@ packages:
|
||||||
description:
|
description:
|
||||||
path: "packages/ledger-bitcoin"
|
path: "packages/ledger-bitcoin"
|
||||||
ref: HEAD
|
ref: HEAD
|
||||||
resolved-ref: dbb5c4956949dc734af3fc8febdbabed89da72aa
|
resolved-ref: "07cd61ef76a2a017b6d5ef233396740163265457"
|
||||||
url: "https://github.com/cake-tech/ledger-flutter-plus-plugins"
|
url: "https://github.com/cake-tech/ledger-flutter-plus-plugins"
|
||||||
source: git
|
source: git
|
||||||
version: "0.0.3"
|
version: "0.0.3"
|
||||||
|
@ -577,7 +577,7 @@ packages:
|
||||||
description:
|
description:
|
||||||
path: "packages/ledger-litecoin"
|
path: "packages/ledger-litecoin"
|
||||||
ref: HEAD
|
ref: HEAD
|
||||||
resolved-ref: dbb5c4956949dc734af3fc8febdbabed89da72aa
|
resolved-ref: "07cd61ef76a2a017b6d5ef233396740163265457"
|
||||||
url: "https://github.com/cake-tech/ledger-flutter-plus-plugins"
|
url: "https://github.com/cake-tech/ledger-flutter-plus-plugins"
|
||||||
source: git
|
source: git
|
||||||
version: "0.0.2"
|
version: "0.0.2"
|
||||||
|
|
|
@ -85,4 +85,8 @@ class PendingBitcoinCashTransaction with PendingTransaction {
|
||||||
fee: fee,
|
fee: fee,
|
||||||
isReplaced: false,
|
isReplaced: false,
|
||||||
);
|
);
|
||||||
|
@override
|
||||||
|
Future<String?> commitUR() {
|
||||||
|
throw UnimplementedError();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,3 +35,34 @@ CryptoCurrency currencyForWalletType(WalletType type, {bool? isTestnet}) {
|
||||||
'Unexpected wallet type: ${type.toString()} for CryptoCurrency currencyForWalletType');
|
'Unexpected wallet type: ${type.toString()} for CryptoCurrency currencyForWalletType');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
WalletType? walletTypeForCurrency(CryptoCurrency currency) {
|
||||||
|
switch (currency) {
|
||||||
|
case CryptoCurrency.btc:
|
||||||
|
return WalletType.bitcoin;
|
||||||
|
case CryptoCurrency.xmr:
|
||||||
|
return WalletType.monero;
|
||||||
|
case CryptoCurrency.ltc:
|
||||||
|
return WalletType.litecoin;
|
||||||
|
case CryptoCurrency.xhv:
|
||||||
|
return WalletType.haven;
|
||||||
|
case CryptoCurrency.eth:
|
||||||
|
return WalletType.ethereum;
|
||||||
|
case CryptoCurrency.bch:
|
||||||
|
return WalletType.bitcoinCash;
|
||||||
|
case CryptoCurrency.nano:
|
||||||
|
return WalletType.nano;
|
||||||
|
case CryptoCurrency.banano:
|
||||||
|
return WalletType.banano;
|
||||||
|
case CryptoCurrency.maticpoly:
|
||||||
|
return WalletType.polygon;
|
||||||
|
case CryptoCurrency.sol:
|
||||||
|
return WalletType.solana;
|
||||||
|
case CryptoCurrency.trx:
|
||||||
|
return WalletType.tron;
|
||||||
|
case CryptoCurrency.wow:
|
||||||
|
return WalletType.wownero;
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@ enum DeviceConnectionType {
|
||||||
static List<DeviceConnectionType> supportedConnectionTypes(WalletType walletType,
|
static List<DeviceConnectionType> supportedConnectionTypes(WalletType walletType,
|
||||||
[bool isIOS = false]) {
|
[bool isIOS = false]) {
|
||||||
switch (walletType) {
|
switch (walletType) {
|
||||||
|
// case WalletType.monero:
|
||||||
case WalletType.bitcoin:
|
case WalletType.bitcoin:
|
||||||
case WalletType.litecoin:
|
case WalletType.litecoin:
|
||||||
case WalletType.ethereum:
|
case WalletType.ethereum:
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
class MoneroWalletKeys {
|
class MoneroWalletKeys {
|
||||||
const MoneroWalletKeys(
|
const MoneroWalletKeys(
|
||||||
{required this.privateSpendKey,
|
{required this.primaryAddress,
|
||||||
|
required this.privateSpendKey,
|
||||||
required this.privateViewKey,
|
required this.privateViewKey,
|
||||||
required this.publicSpendKey,
|
required this.publicSpendKey,
|
||||||
required this.publicViewKey});
|
required this.publicViewKey});
|
||||||
|
|
||||||
|
final String primaryAddress;
|
||||||
final String publicViewKey;
|
final String publicViewKey;
|
||||||
final String privateViewKey;
|
final String privateViewKey;
|
||||||
final String publicSpendKey;
|
final String publicSpendKey;
|
||||||
|
|
|
@ -14,5 +14,8 @@ mixin PendingTransaction {
|
||||||
int? get outputCount => null;
|
int? get outputCount => null;
|
||||||
PendingChange? change;
|
PendingChange? change;
|
||||||
|
|
||||||
|
bool shouldCommitUR() => false;
|
||||||
|
|
||||||
Future<void> commit();
|
Future<void> commit();
|
||||||
|
Future<String?> commitUR();
|
||||||
}
|
}
|
||||||
|
|
|
@ -61,4 +61,8 @@ abstract class WalletService<N extends WalletCredentials, RFS extends WalletCred
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Check if the Wallet requires a hardware wallet to be connected during
|
||||||
|
/// the opening flow. (Currently only the case for Monero)
|
||||||
|
bool requireHardwareWalletConnection(String name) => false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,4 +47,9 @@ class PendingEVMChainTransaction with PendingTransaction {
|
||||||
|
|
||||||
return '0x${Hex.HEX.encode(txid)}';
|
return '0x${Hex.HEX.encode(txid)}';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<String?> commitUR() {
|
||||||
|
throw UnimplementedError();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,4 +2,9 @@ class WalletRestoreFromKeysException implements Exception {
|
||||||
WalletRestoreFromKeysException({required this.message});
|
WalletRestoreFromKeysException({required this.message});
|
||||||
|
|
||||||
final String message;
|
final String message;
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return message;
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -73,6 +73,7 @@ abstract class HavenWalletBase
|
||||||
|
|
||||||
@override
|
@override
|
||||||
MoneroWalletKeys get keys => MoneroWalletKeys(
|
MoneroWalletKeys get keys => MoneroWalletKeys(
|
||||||
|
primaryAddress: haven_wallet.getAddress(accountIndex: 0, addressIndex: 0),
|
||||||
privateSpendKey: haven_wallet.getSecretSpendKey(),
|
privateSpendKey: haven_wallet.getSecretSpendKey(),
|
||||||
privateViewKey: haven_wallet.getSecretViewKey(),
|
privateViewKey: haven_wallet.getSecretViewKey(),
|
||||||
publicSpendKey: haven_wallet.getPublicSpendKey(),
|
publicSpendKey: haven_wallet.getPublicSpendKey(),
|
||||||
|
|
|
@ -48,4 +48,9 @@ class PendingHavenTransaction with PendingTransaction {
|
||||||
rethrow;
|
rethrow;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<String?> commitUR() {
|
||||||
|
throw UnimplementedError();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ import 'package:cw_monero/api/wallet.dart';
|
||||||
import 'package:monero/monero.dart' as monero;
|
import 'package:monero/monero.dart' as monero;
|
||||||
|
|
||||||
monero.wallet? wptr = null;
|
monero.wallet? wptr = null;
|
||||||
|
bool get isViewOnly => int.tryParse(monero.Wallet_secretSpendKey(wptr!)) == 0;
|
||||||
|
|
||||||
int _wlptrForW = 0;
|
int _wlptrForW = 0;
|
||||||
monero.WalletListener? _wlptr = null;
|
monero.WalletListener? _wlptr = null;
|
||||||
|
|
|
@ -13,7 +13,13 @@ import 'package:mutex/mutex.dart';
|
||||||
|
|
||||||
|
|
||||||
String getTxKey(String txId) {
|
String getTxKey(String txId) {
|
||||||
return monero.Wallet_getTxKey(wptr!, txid: txId);
|
final txKey = monero.Wallet_getTxKey(wptr!, txid: txId);
|
||||||
|
final status = monero.Wallet_status(wptr!);
|
||||||
|
if (status != 0) {
|
||||||
|
final error = monero.Wallet_errorString(wptr!);
|
||||||
|
return txId+"_"+error;
|
||||||
|
}
|
||||||
|
return txKey;
|
||||||
}
|
}
|
||||||
final txHistoryMutex = Mutex();
|
final txHistoryMutex = Mutex();
|
||||||
monero.TransactionHistory? txhistory;
|
monero.TransactionHistory? txhistory;
|
||||||
|
@ -178,12 +184,13 @@ PendingTransactionDescription createTransactionMultDestSync(
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void commitTransactionFromPointerAddress({required int address}) =>
|
String? commitTransactionFromPointerAddress({required int address, required bool useUR}) =>
|
||||||
commitTransaction(transactionPointer: monero.PendingTransaction.fromAddress(address));
|
commitTransaction(transactionPointer: monero.PendingTransaction.fromAddress(address), useUR: useUR);
|
||||||
|
|
||||||
void commitTransaction({required monero.PendingTransaction transactionPointer}) {
|
String? commitTransaction({required monero.PendingTransaction transactionPointer, required bool useUR}) {
|
||||||
|
final txCommit = useUR
|
||||||
final txCommit = monero.PendingTransaction_commit(transactionPointer, filename: '', overwrite: false);
|
? monero.PendingTransaction_commitUR(transactionPointer, 120)
|
||||||
|
: monero.PendingTransaction_commit(transactionPointer, filename: '', overwrite: false);
|
||||||
|
|
||||||
final String? error = (() {
|
final String? error = (() {
|
||||||
final status = monero.PendingTransaction_status(transactionPointer.cast());
|
final status = monero.PendingTransaction_status(transactionPointer.cast());
|
||||||
|
@ -196,6 +203,11 @@ void commitTransaction({required monero.PendingTransaction transactionPointer})
|
||||||
if (error != null) {
|
if (error != null) {
|
||||||
throw CreationTransactionException(message: error);
|
throw CreationTransactionException(message: error);
|
||||||
}
|
}
|
||||||
|
if (useUR) {
|
||||||
|
return txCommit as String?;
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<PendingTransactionDescription> _createTransactionSync(Map args) async {
|
Future<PendingTransactionDescription> _createTransactionSync(Map args) async {
|
||||||
|
|
|
@ -119,7 +119,7 @@ Future<bool> setupNodeSync(
|
||||||
daemonUsername: login ?? '',
|
daemonUsername: login ?? '',
|
||||||
daemonPassword: password ?? '');
|
daemonPassword: password ?? '');
|
||||||
});
|
});
|
||||||
// monero.Wallet_init3(wptr!, argv0: '', defaultLogBaseName: 'moneroc', console: true);
|
// monero.Wallet_init3(wptr!, argv0: '', defaultLogBaseName: 'moneroc', console: true, logPath: '');
|
||||||
|
|
||||||
final status = monero.Wallet_status(wptr!);
|
final status = monero.Wallet_status(wptr!);
|
||||||
|
|
||||||
|
@ -330,4 +330,4 @@ String signMessage(String message, {String address = ""}) {
|
||||||
|
|
||||||
bool verifyMessage(String message, String address, String signature) {
|
bool verifyMessage(String message, String address, String signature) {
|
||||||
return monero.Wallet_verifySignedMessage(wptr!, message: message, address: address, signature: signature);
|
return monero.Wallet_verifySignedMessage(wptr!, message: message, address: address, signature: signature);
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,19 +7,18 @@ import 'package:cw_monero/api/exceptions/wallet_creation_exception.dart';
|
||||||
import 'package:cw_monero/api/exceptions/wallet_opening_exception.dart';
|
import 'package:cw_monero/api/exceptions/wallet_opening_exception.dart';
|
||||||
import 'package:cw_monero/api/exceptions/wallet_restore_from_keys_exception.dart';
|
import 'package:cw_monero/api/exceptions/wallet_restore_from_keys_exception.dart';
|
||||||
import 'package:cw_monero/api/exceptions/wallet_restore_from_seed_exception.dart';
|
import 'package:cw_monero/api/exceptions/wallet_restore_from_seed_exception.dart';
|
||||||
import 'package:cw_monero/api/wallet.dart';
|
|
||||||
import 'package:cw_monero/api/transaction_history.dart';
|
import 'package:cw_monero/api/transaction_history.dart';
|
||||||
|
import 'package:cw_monero/api/wallet.dart';
|
||||||
|
import 'package:cw_monero/ledger.dart';
|
||||||
import 'package:monero/monero.dart' as monero;
|
import 'package:monero/monero.dart' as monero;
|
||||||
|
|
||||||
class MoneroCException implements Exception {
|
class MoneroCException implements Exception {
|
||||||
final String message;
|
final String message;
|
||||||
|
|
||||||
MoneroCException(this.message);
|
MoneroCException(this.message);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() => message;
|
||||||
return message;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void checkIfMoneroCIsFine() {
|
void checkIfMoneroCIsFine() {
|
||||||
|
@ -43,7 +42,6 @@ void checkIfMoneroCIsFine() {
|
||||||
throw MoneroCException("monero_c and monero.dart wrapper export list mismatch.\nLogic errors can occur.\nRefusing to run in release mode.\ncpp: '$cppCsExp'\ndart: '$dartCsExp'");
|
throw MoneroCException("monero_c and monero.dart wrapper export list mismatch.\nLogic errors can occur.\nRefusing to run in release mode.\ncpp: '$cppCsExp'\ndart: '$dartCsExp'");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
monero.WalletManager? _wmPtr;
|
monero.WalletManager? _wmPtr;
|
||||||
final monero.WalletManager wmPtr = Pointer.fromAddress((() {
|
final monero.WalletManager wmPtr = Pointer.fromAddress((() {
|
||||||
try {
|
try {
|
||||||
|
@ -60,6 +58,13 @@ final monero.WalletManager wmPtr = Pointer.fromAddress((() {
|
||||||
return _wmPtr!.address;
|
return _wmPtr!.address;
|
||||||
})());
|
})());
|
||||||
|
|
||||||
|
void createWalletPointer() {
|
||||||
|
final newWptr = monero.WalletManager_createWallet(wmPtr,
|
||||||
|
path: "", password: "", language: "", networkType: 0);
|
||||||
|
|
||||||
|
wptr = newWptr;
|
||||||
|
}
|
||||||
|
|
||||||
void createWalletSync(
|
void createWalletSync(
|
||||||
{required String path,
|
{required String path,
|
||||||
required String password,
|
required String password,
|
||||||
|
@ -124,24 +129,24 @@ void restoreWalletFromKeysSync(
|
||||||
int restoreHeight = 0}) {
|
int restoreHeight = 0}) {
|
||||||
txhistory = null;
|
txhistory = null;
|
||||||
var newWptr = (spendKey != "")
|
var newWptr = (spendKey != "")
|
||||||
? monero.WalletManager_createDeterministicWalletFromSpendKey(
|
? monero.WalletManager_createDeterministicWalletFromSpendKey(wmPtr,
|
||||||
wmPtr,
|
path: path,
|
||||||
path: path,
|
password: password,
|
||||||
password: password,
|
language: language,
|
||||||
language: language,
|
spendKeyString: spendKey,
|
||||||
spendKeyString: spendKey,
|
newWallet: true,
|
||||||
newWallet: true, // TODO(mrcyjanek): safe to remove
|
// TODO(mrcyjanek): safe to remove
|
||||||
restoreHeight: restoreHeight)
|
restoreHeight: restoreHeight)
|
||||||
: monero.WalletManager_createWalletFromKeys(
|
: monero.WalletManager_createWalletFromKeys(
|
||||||
wmPtr,
|
wmPtr,
|
||||||
path: path,
|
path: path,
|
||||||
password: password,
|
password: password,
|
||||||
restoreHeight: restoreHeight,
|
restoreHeight: restoreHeight,
|
||||||
addressString: address,
|
addressString: address,
|
||||||
viewKeyString: viewKey,
|
viewKeyString: viewKey,
|
||||||
spendKeyString: spendKey,
|
spendKeyString: spendKey,
|
||||||
nettype: 0,
|
nettype: 0,
|
||||||
);
|
);
|
||||||
|
|
||||||
final status = monero.Wallet_status(newWptr);
|
final status = monero.Wallet_status(newWptr);
|
||||||
if (status != 0) {
|
if (status != 0) {
|
||||||
|
@ -156,7 +161,7 @@ void restoreWalletFromKeysSync(
|
||||||
if (viewKey != viewKeyRestored && viewKey != "") {
|
if (viewKey != viewKeyRestored && viewKey != "") {
|
||||||
monero.WalletManager_closeWallet(wmPtr, newWptr, false);
|
monero.WalletManager_closeWallet(wmPtr, newWptr, false);
|
||||||
File(path).deleteSync();
|
File(path).deleteSync();
|
||||||
File(path+".keys").deleteSync();
|
File(path + ".keys").deleteSync();
|
||||||
newWptr = monero.WalletManager_createWalletFromKeys(
|
newWptr = monero.WalletManager_createWalletFromKeys(
|
||||||
wmPtr,
|
wmPtr,
|
||||||
path: path,
|
path: path,
|
||||||
|
@ -199,7 +204,7 @@ void restoreWalletFromSpendKeySync(
|
||||||
// viewKeyString: '',
|
// viewKeyString: '',
|
||||||
// nettype: 0,
|
// nettype: 0,
|
||||||
// );
|
// );
|
||||||
|
|
||||||
txhistory = null;
|
txhistory = null;
|
||||||
final newWptr = monero.WalletManager_createDeterministicWalletFromSpendKey(
|
final newWptr = monero.WalletManager_createDeterministicWalletFromSpendKey(
|
||||||
wmPtr,
|
wmPtr,
|
||||||
|
@ -230,41 +235,39 @@ void restoreWalletFromSpendKeySync(
|
||||||
|
|
||||||
String _lastOpenedWallet = "";
|
String _lastOpenedWallet = "";
|
||||||
|
|
||||||
// void restoreMoneroWalletFromDevice(
|
Future<void> restoreWalletFromHardwareWallet(
|
||||||
// {required String path,
|
{required String path,
|
||||||
// required String password,
|
required String password,
|
||||||
// required String deviceName,
|
required String deviceName,
|
||||||
// int nettype = 0,
|
int nettype = 0,
|
||||||
// int restoreHeight = 0}) {
|
int restoreHeight = 0}) async {
|
||||||
//
|
txhistory = null;
|
||||||
// final pathPointer = path.toNativeUtf8();
|
|
||||||
// final passwordPointer = password.toNativeUtf8();
|
final newWptrAddr = await Isolate.run(() {
|
||||||
// final deviceNamePointer = deviceName.toNativeUtf8();
|
return monero.WalletManager_createWalletFromDevice(wmPtr,
|
||||||
// final errorMessagePointer = ''.toNativeUtf8();
|
path: path,
|
||||||
//
|
password: password,
|
||||||
// final isWalletRestored = restoreWalletFromDeviceNative(
|
restoreHeight: restoreHeight,
|
||||||
// pathPointer,
|
deviceName: deviceName)
|
||||||
// passwordPointer,
|
.address;
|
||||||
// deviceNamePointer,
|
});
|
||||||
// nettype,
|
final newWptr = Pointer<Void>.fromAddress(newWptrAddr);
|
||||||
// restoreHeight,
|
|
||||||
// errorMessagePointer) != 0;
|
final status = monero.Wallet_status(newWptr);
|
||||||
//
|
|
||||||
// calloc.free(pathPointer);
|
if (status != 0) {
|
||||||
// calloc.free(passwordPointer);
|
final error = monero.Wallet_errorString(newWptr);
|
||||||
//
|
throw WalletRestoreFromSeedException(message: error);
|
||||||
// storeSync();
|
}
|
||||||
//
|
wptr = newWptr;
|
||||||
// if (!isWalletRestored) {
|
|
||||||
// throw WalletRestoreFromKeysException(
|
openedWalletsByPath[path] = wptr!;
|
||||||
// message: convertUTF8ToString(pointer: errorMessagePointer));
|
}
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
Map<String, monero.wallet> openedWalletsByPath = {};
|
Map<String, monero.wallet> openedWalletsByPath = {};
|
||||||
|
|
||||||
void loadWallet(
|
Future<void> loadWallet(
|
||||||
{required String path, required String password, int nettype = 0}) {
|
{required String path, required String password, int nettype = 0}) async {
|
||||||
if (openedWalletsByPath[path] != null) {
|
if (openedWalletsByPath[path] != null) {
|
||||||
txhistory = null;
|
txhistory = null;
|
||||||
wptr = openedWalletsByPath[path]!;
|
wptr = openedWalletsByPath[path]!;
|
||||||
|
@ -278,8 +281,29 @@ void loadWallet(
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
txhistory = null;
|
txhistory = null;
|
||||||
final newWptr = monero.WalletManager_openWallet(wmPtr,
|
|
||||||
path: path, password: password);
|
/// Get the device type
|
||||||
|
/// 0: Software Wallet
|
||||||
|
/// 1: Ledger
|
||||||
|
/// 2: Trezor
|
||||||
|
final deviceType = monero.WalletManager_queryWalletDevice(wmPtr,
|
||||||
|
keysFileName: "$path.keys", password: password, kdfRounds: 1);
|
||||||
|
|
||||||
|
if (deviceType == 1) {
|
||||||
|
final dummyWPtr = wptr ??
|
||||||
|
monero.WalletManager_openWallet(wmPtr, path: '', password: '');
|
||||||
|
enableLedgerExchange(dummyWPtr, gLedger!);
|
||||||
|
}
|
||||||
|
|
||||||
|
final addr = wmPtr.address;
|
||||||
|
final newWptrAddr = await Isolate.run(() {
|
||||||
|
return monero.WalletManager_openWallet(Pointer.fromAddress(addr),
|
||||||
|
path: path, password: password)
|
||||||
|
.address;
|
||||||
|
});
|
||||||
|
|
||||||
|
final newWptr = Pointer<Void>.fromAddress(newWptrAddr);
|
||||||
|
|
||||||
_lastOpenedWallet = path;
|
_lastOpenedWallet = path;
|
||||||
final status = monero.Wallet_status(newWptr);
|
final status = monero.Wallet_status(newWptr);
|
||||||
if (status != 0) {
|
if (status != 0) {
|
||||||
|
@ -287,6 +311,7 @@ void loadWallet(
|
||||||
print(err);
|
print(err);
|
||||||
throw WalletOpeningException(message: err);
|
throw WalletOpeningException(message: err);
|
||||||
}
|
}
|
||||||
|
|
||||||
wptr = newWptr;
|
wptr = newWptr;
|
||||||
openedWalletsByPath[path] = wptr!;
|
openedWalletsByPath[path] = wptr!;
|
||||||
}
|
}
|
||||||
|
@ -351,7 +376,7 @@ Future<void> _openWallet(Map<String, String> args) async => loadWallet(
|
||||||
|
|
||||||
bool _isWalletExist(String path) => isWalletExistSync(path: path);
|
bool _isWalletExist(String path) => isWalletExistSync(path: path);
|
||||||
|
|
||||||
void openWallet(
|
Future<void> openWallet(
|
||||||
{required String path,
|
{required String path,
|
||||||
required String password,
|
required String password,
|
||||||
int nettype = 0}) async =>
|
int nettype = 0}) async =>
|
||||||
|
@ -425,3 +450,5 @@ Future<void> restoreFromSpendKey(
|
||||||
});
|
});
|
||||||
|
|
||||||
bool isWalletExist({required String path}) => _isWalletExist(path);
|
bool isWalletExist({required String path}) => _isWalletExist(path);
|
||||||
|
|
||||||
|
bool isViewOnlyBySpendKey() => int.tryParse(monero.Wallet_secretSpendKey(wptr!)) == 0;
|
84
cw_monero/lib/ledger.dart
Normal file
|
@ -0,0 +1,84 @@
|
||||||
|
import 'dart:async';
|
||||||
|
import 'dart:ffi';
|
||||||
|
import 'dart:typed_data';
|
||||||
|
|
||||||
|
import 'package:ffi/ffi.dart';
|
||||||
|
import 'package:ledger_flutter_plus/ledger_flutter_plus.dart';
|
||||||
|
import 'package:ledger_flutter_plus/ledger_flutter_plus_dart.dart';
|
||||||
|
import 'package:monero/monero.dart' as monero;
|
||||||
|
// import 'package:polyseed/polyseed.dart';
|
||||||
|
|
||||||
|
LedgerConnection? gLedger;
|
||||||
|
|
||||||
|
Timer? _ledgerExchangeTimer;
|
||||||
|
Timer? _ledgerKeepAlive;
|
||||||
|
|
||||||
|
void enableLedgerExchange(monero.wallet ptr, LedgerConnection connection) {
|
||||||
|
_ledgerExchangeTimer?.cancel();
|
||||||
|
_ledgerExchangeTimer = Timer.periodic(Duration(milliseconds: 1), (_) async {
|
||||||
|
final ledgerRequestLength = monero.Wallet_getSendToDeviceLength(ptr);
|
||||||
|
final ledgerRequest = monero.Wallet_getSendToDevice(ptr)
|
||||||
|
.cast<Uint8>()
|
||||||
|
.asTypedList(ledgerRequestLength);
|
||||||
|
if (ledgerRequestLength > 0) {
|
||||||
|
_ledgerKeepAlive?.cancel();
|
||||||
|
|
||||||
|
final Pointer<Uint8> emptyPointer = malloc<Uint8>(0);
|
||||||
|
monero.Wallet_setDeviceSendData(
|
||||||
|
ptr, emptyPointer.cast<UnsignedChar>(), 0);
|
||||||
|
malloc.free(emptyPointer);
|
||||||
|
|
||||||
|
// print("> ${ledgerRequest.toHexString()}");
|
||||||
|
final response = await exchange(connection, ledgerRequest);
|
||||||
|
// print("< ${response.toHexString()}");
|
||||||
|
|
||||||
|
final Pointer<Uint8> result = malloc<Uint8>(response.length);
|
||||||
|
for (var i = 0; i < response.length; i++) {
|
||||||
|
result.asTypedList(response.length)[i] = response[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
monero.Wallet_setDeviceReceivedData(
|
||||||
|
ptr, result.cast<UnsignedChar>(), response.length);
|
||||||
|
malloc.free(result);
|
||||||
|
keepAlive(connection);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void keepAlive(LedgerConnection connection) {
|
||||||
|
if (connection.connectionType == ConnectionType.ble) {
|
||||||
|
_ledgerKeepAlive = Timer.periodic(Duration(seconds: 10), (_) async {
|
||||||
|
try {
|
||||||
|
UniversalBle.setNotifiable(
|
||||||
|
connection.device.id,
|
||||||
|
connection.device.deviceInfo.serviceId,
|
||||||
|
connection.device.deviceInfo.notifyCharacteristicKey,
|
||||||
|
BleInputProperty.notification,
|
||||||
|
);
|
||||||
|
} catch (_) {}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void disableLedgerExchange() {
|
||||||
|
_ledgerExchangeTimer?.cancel();
|
||||||
|
_ledgerKeepAlive?.cancel();
|
||||||
|
gLedger?.disconnect();
|
||||||
|
gLedger = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<Uint8List> exchange(LedgerConnection connection, Uint8List data) async =>
|
||||||
|
connection.sendOperation<Uint8List>(ExchangeOperation(data));
|
||||||
|
|
||||||
|
class ExchangeOperation extends LedgerRawOperation<Uint8List> {
|
||||||
|
final Uint8List inputData;
|
||||||
|
|
||||||
|
ExchangeOperation(this.inputData);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<Uint8List> read(ByteDataReader reader) async =>
|
||||||
|
reader.read(reader.remainingLength);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<List<Uint8List>> write(ByteDataWriter writer) async => [inputData];
|
||||||
|
}
|
|
@ -19,6 +19,7 @@ import 'package:cw_core/transaction_direction.dart';
|
||||||
import 'package:cw_core/unspent_coins_info.dart';
|
import 'package:cw_core/unspent_coins_info.dart';
|
||||||
import 'package:cw_core/wallet_base.dart';
|
import 'package:cw_core/wallet_base.dart';
|
||||||
import 'package:cw_core/wallet_info.dart';
|
import 'package:cw_core/wallet_info.dart';
|
||||||
|
import 'package:cw_monero/api/account_list.dart';
|
||||||
import 'package:cw_monero/api/coins_info.dart';
|
import 'package:cw_monero/api/coins_info.dart';
|
||||||
import 'package:cw_monero/api/monero_output.dart';
|
import 'package:cw_monero/api/monero_output.dart';
|
||||||
import 'package:cw_monero/api/structs/pending_transaction.dart';
|
import 'package:cw_monero/api/structs/pending_transaction.dart';
|
||||||
|
@ -27,6 +28,7 @@ import 'package:cw_monero/api/wallet.dart' as monero_wallet;
|
||||||
import 'package:cw_monero/api/wallet_manager.dart';
|
import 'package:cw_monero/api/wallet_manager.dart';
|
||||||
import 'package:cw_monero/exceptions/monero_transaction_creation_exception.dart';
|
import 'package:cw_monero/exceptions/monero_transaction_creation_exception.dart';
|
||||||
import 'package:cw_monero/exceptions/monero_transaction_no_inputs_exception.dart';
|
import 'package:cw_monero/exceptions/monero_transaction_no_inputs_exception.dart';
|
||||||
|
import 'package:cw_monero/ledger.dart';
|
||||||
import 'package:cw_monero/monero_transaction_creation_credentials.dart';
|
import 'package:cw_monero/monero_transaction_creation_credentials.dart';
|
||||||
import 'package:cw_monero/monero_transaction_history.dart';
|
import 'package:cw_monero/monero_transaction_history.dart';
|
||||||
import 'package:cw_monero/monero_transaction_info.dart';
|
import 'package:cw_monero/monero_transaction_info.dart';
|
||||||
|
@ -35,6 +37,7 @@ import 'package:cw_monero/monero_wallet_addresses.dart';
|
||||||
import 'package:cw_monero/pending_monero_transaction.dart';
|
import 'package:cw_monero/pending_monero_transaction.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:hive/hive.dart';
|
import 'package:hive/hive.dart';
|
||||||
|
import 'package:ledger_flutter_plus/ledger_flutter_plus.dart';
|
||||||
import 'package:mobx/mobx.dart';
|
import 'package:mobx/mobx.dart';
|
||||||
import 'package:monero/monero.dart' as monero;
|
import 'package:monero/monero.dart' as monero;
|
||||||
|
|
||||||
|
@ -121,6 +124,7 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
|
||||||
|
|
||||||
@override
|
@override
|
||||||
MoneroWalletKeys get keys => MoneroWalletKeys(
|
MoneroWalletKeys get keys => MoneroWalletKeys(
|
||||||
|
primaryAddress: monero_wallet.getAddress(accountIndex: 0, addressIndex: 0),
|
||||||
privateSpendKey: monero_wallet.getSecretSpendKey(),
|
privateSpendKey: monero_wallet.getSecretSpendKey(),
|
||||||
privateViewKey: monero_wallet.getSecretViewKey(),
|
privateViewKey: monero_wallet.getSecretViewKey(),
|
||||||
publicSpendKey: monero_wallet.getPublicSpendKey(),
|
publicSpendKey: monero_wallet.getPublicSpendKey(),
|
||||||
|
@ -230,6 +234,36 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<bool> submitTransactionUR(String ur) async {
|
||||||
|
final retStatus = monero.Wallet_submitTransactionUR(wptr!, ur);
|
||||||
|
final status = monero.Wallet_status(wptr!);
|
||||||
|
if (status != 0) {
|
||||||
|
final err = monero.Wallet_errorString(wptr!);
|
||||||
|
throw MoneroTransactionCreationException("unable to broadcast signed transaction: $err");
|
||||||
|
}
|
||||||
|
return retStatus;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool importKeyImagesUR(String ur) {
|
||||||
|
final retStatus = monero.Wallet_importKeyImagesUR(wptr!, ur);
|
||||||
|
final status = monero.Wallet_status(wptr!);
|
||||||
|
if (status != 0) {
|
||||||
|
final err = monero.Wallet_errorString(wptr!);
|
||||||
|
throw Exception("unable to import key images: $err");
|
||||||
|
}
|
||||||
|
return retStatus;
|
||||||
|
}
|
||||||
|
|
||||||
|
String exportOutputsUR(bool all) {
|
||||||
|
final str = monero.Wallet_exportOutputsUR(wptr!, all: all);
|
||||||
|
final status = monero.Wallet_status(wptr!);
|
||||||
|
if (status != 0) {
|
||||||
|
final err = monero.Wallet_errorString(wptr!);
|
||||||
|
throw MoneroTransactionCreationException("unable to export UR: $err");
|
||||||
|
}
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<PendingTransaction> createTransaction(Object credentials) async {
|
Future<PendingTransaction> createTransaction(Object credentials) async {
|
||||||
final _credentials = credentials as MoneroTransactionCreationCredentials;
|
final _credentials = credentials as MoneroTransactionCreationCredentials;
|
||||||
|
@ -796,4 +830,9 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
|
||||||
return monero_wallet.verifyMessage(message, address, signature);
|
return monero_wallet.verifyMessage(message, address, signature);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void setLedgerConnection(LedgerConnection connection) {
|
||||||
|
final dummyWPtr = wptr ??
|
||||||
|
monero.WalletManager_openWallet(wmPtr, path: '', password: '');
|
||||||
|
enableLedgerExchange(dummyWPtr, connection);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,10 +9,13 @@ import 'package:cw_core/wallet_info.dart';
|
||||||
import 'package:cw_core/wallet_service.dart';
|
import 'package:cw_core/wallet_service.dart';
|
||||||
import 'package:cw_core/wallet_type.dart';
|
import 'package:cw_core/wallet_type.dart';
|
||||||
import 'package:cw_core/get_height_by_date.dart';
|
import 'package:cw_core/get_height_by_date.dart';
|
||||||
|
import 'package:cw_monero/api/account_list.dart';
|
||||||
import 'package:cw_monero/api/wallet_manager.dart' as monero_wallet_manager;
|
import 'package:cw_monero/api/wallet_manager.dart' as monero_wallet_manager;
|
||||||
import 'package:cw_monero/api/wallet_manager.dart';
|
import 'package:cw_monero/api/wallet_manager.dart';
|
||||||
|
import 'package:cw_monero/ledger.dart';
|
||||||
import 'package:cw_monero/monero_wallet.dart';
|
import 'package:cw_monero/monero_wallet.dart';
|
||||||
import 'package:hive/hive.dart';
|
import 'package:hive/hive.dart';
|
||||||
|
import 'package:ledger_flutter_plus/ledger_flutter_plus.dart';
|
||||||
import 'package:polyseed/polyseed.dart';
|
import 'package:polyseed/polyseed.dart';
|
||||||
import 'package:monero/monero.dart' as monero;
|
import 'package:monero/monero.dart' as monero;
|
||||||
|
|
||||||
|
@ -25,6 +28,15 @@ class MoneroNewWalletCredentials extends WalletCredentials {
|
||||||
final bool isPolyseed;
|
final bool isPolyseed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class MoneroRestoreWalletFromHardwareCredentials extends WalletCredentials {
|
||||||
|
MoneroRestoreWalletFromHardwareCredentials({required String name,
|
||||||
|
required this.ledgerConnection,
|
||||||
|
int height = 0,
|
||||||
|
String? password})
|
||||||
|
: super(name: name, password: password, height: height);
|
||||||
|
LedgerConnection ledgerConnection;
|
||||||
|
}
|
||||||
|
|
||||||
class MoneroRestoreWalletFromSeedCredentials extends WalletCredentials {
|
class MoneroRestoreWalletFromSeedCredentials extends WalletCredentials {
|
||||||
MoneroRestoreWalletFromSeedCredentials(
|
MoneroRestoreWalletFromSeedCredentials(
|
||||||
{required String name, required this.mnemonic, int height = 0, String? password})
|
{required String name, required this.mnemonic, int height = 0, String? password})
|
||||||
|
@ -39,14 +51,13 @@ class MoneroWalletLoadingException implements Exception {
|
||||||
}
|
}
|
||||||
|
|
||||||
class MoneroRestoreWalletFromKeysCredentials extends WalletCredentials {
|
class MoneroRestoreWalletFromKeysCredentials extends WalletCredentials {
|
||||||
MoneroRestoreWalletFromKeysCredentials(
|
MoneroRestoreWalletFromKeysCredentials({required String name,
|
||||||
{required String name,
|
required String password,
|
||||||
required String password,
|
required this.language,
|
||||||
required this.language,
|
required this.address,
|
||||||
required this.address,
|
required this.viewKey,
|
||||||
required this.viewKey,
|
required this.spendKey,
|
||||||
required this.spendKey,
|
int height = 0})
|
||||||
int height = 0})
|
|
||||||
: super(name: name, password: password, height: height);
|
: super(name: name, password: password, height: height);
|
||||||
|
|
||||||
final String language;
|
final String language;
|
||||||
|
@ -59,7 +70,7 @@ class MoneroWalletService extends WalletService<
|
||||||
MoneroNewWalletCredentials,
|
MoneroNewWalletCredentials,
|
||||||
MoneroRestoreWalletFromSeedCredentials,
|
MoneroRestoreWalletFromSeedCredentials,
|
||||||
MoneroRestoreWalletFromKeysCredentials,
|
MoneroRestoreWalletFromKeysCredentials,
|
||||||
MoneroNewWalletCredentials> {
|
MoneroRestoreWalletFromHardwareCredentials> {
|
||||||
MoneroWalletService(this.walletInfoSource, this.unspentCoinsInfoSource);
|
MoneroWalletService(this.walletInfoSource, this.unspentCoinsInfoSource);
|
||||||
|
|
||||||
final Box<WalletInfo> walletInfoSource;
|
final Box<WalletInfo> walletInfoSource;
|
||||||
|
@ -81,7 +92,7 @@ class MoneroWalletService extends WalletService<
|
||||||
final lang = PolyseedLang.getByEnglishName(credentials.language);
|
final lang = PolyseedLang.getByEnglishName(credentials.language);
|
||||||
|
|
||||||
final heightOverride =
|
final heightOverride =
|
||||||
getMoneroHeigthByDate(date: DateTime.now().subtract(Duration(days: 2)));
|
getMoneroHeigthByDate(date: DateTime.now().subtract(Duration(days: 2)));
|
||||||
|
|
||||||
return _restoreFromPolyseed(
|
return _restoreFromPolyseed(
|
||||||
path, credentials.password!, polyseed, credentials.walletInfo!, lang,
|
path, credentials.password!, polyseed, credentials.walletInfo!, lang,
|
||||||
|
@ -91,9 +102,9 @@ class MoneroWalletService extends WalletService<
|
||||||
await monero_wallet_manager.createWallet(
|
await monero_wallet_manager.createWallet(
|
||||||
path: path, password: credentials.password!, language: credentials.language);
|
path: path, password: credentials.password!, language: credentials.language);
|
||||||
final wallet = MoneroWallet(
|
final wallet = MoneroWallet(
|
||||||
walletInfo: credentials.walletInfo!,
|
walletInfo: credentials.walletInfo!,
|
||||||
unspentCoinsInfo: unspentCoinsInfoSource,
|
unspentCoinsInfo: unspentCoinsInfoSource,
|
||||||
password: credentials.password!);
|
password: credentials.password!);
|
||||||
await wallet.init();
|
await wallet.init();
|
||||||
|
|
||||||
return wallet;
|
return wallet;
|
||||||
|
@ -128,13 +139,18 @@ class MoneroWalletService extends WalletService<
|
||||||
await monero_wallet_manager
|
await monero_wallet_manager
|
||||||
.openWalletAsync({'path': path, 'password': password});
|
.openWalletAsync({'path': path, 'password': password});
|
||||||
final walletInfo = walletInfoSource.values.firstWhere(
|
final walletInfo = walletInfoSource.values.firstWhere(
|
||||||
(info) => info.id == WalletBase.idFor(name, getType()));
|
(info) => info.id == WalletBase.idFor(name, getType()));
|
||||||
final wallet = MoneroWallet(
|
final wallet = MoneroWallet(
|
||||||
walletInfo: walletInfo,
|
walletInfo: walletInfo,
|
||||||
unspentCoinsInfo: unspentCoinsInfoSource,
|
unspentCoinsInfo: unspentCoinsInfoSource,
|
||||||
password: password);
|
password: password);
|
||||||
final isValid = wallet.walletAddresses.validate();
|
final isValid = wallet.walletAddresses.validate();
|
||||||
|
|
||||||
|
if (wallet.isHardwareWallet) {
|
||||||
|
wallet.setLedgerConnection(gLedger!);
|
||||||
|
gLedger = null;
|
||||||
|
}
|
||||||
|
|
||||||
if (!isValid) {
|
if (!isValid) {
|
||||||
await restoreOrResetWalletFiles(name);
|
await restoreOrResetWalletFiles(name);
|
||||||
wallet.close(shouldCleanup: false);
|
wallet.close(shouldCleanup: false);
|
||||||
|
@ -185,10 +201,9 @@ class MoneroWalletService extends WalletService<
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> rename(
|
Future<void> rename(String currentName, String password, String newName) async {
|
||||||
String currentName, String password, String newName) async {
|
|
||||||
final currentWalletInfo = walletInfoSource.values.firstWhere(
|
final currentWalletInfo = walletInfoSource.values.firstWhere(
|
||||||
(info) => info.id == WalletBase.idFor(currentName, getType()));
|
(info) => info.id == WalletBase.idFor(currentName, getType()));
|
||||||
final currentWallet = MoneroWallet(
|
final currentWallet = MoneroWallet(
|
||||||
walletInfo: currentWalletInfo,
|
walletInfo: currentWalletInfo,
|
||||||
unspentCoinsInfo: unspentCoinsInfoSource,
|
unspentCoinsInfo: unspentCoinsInfoSource,
|
||||||
|
@ -218,9 +233,9 @@ class MoneroWalletService extends WalletService<
|
||||||
viewKey: credentials.viewKey,
|
viewKey: credentials.viewKey,
|
||||||
spendKey: credentials.spendKey);
|
spendKey: credentials.spendKey);
|
||||||
final wallet = MoneroWallet(
|
final wallet = MoneroWallet(
|
||||||
walletInfo: credentials.walletInfo!,
|
walletInfo: credentials.walletInfo!,
|
||||||
unspentCoinsInfo: unspentCoinsInfoSource,
|
unspentCoinsInfo: unspentCoinsInfoSource,
|
||||||
password: credentials.password!);
|
password: credentials.password!);
|
||||||
await wallet.init();
|
await wallet.init();
|
||||||
|
|
||||||
return wallet;
|
return wallet;
|
||||||
|
@ -232,9 +247,34 @@ class MoneroWalletService extends WalletService<
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<MoneroWallet> restoreFromHardwareWallet(MoneroNewWalletCredentials credentials) {
|
Future<MoneroWallet> restoreFromHardwareWallet(
|
||||||
throw UnimplementedError(
|
MoneroRestoreWalletFromHardwareCredentials credentials) async {
|
||||||
"Restoring a Monero wallet from a hardware wallet is not yet supported!");
|
try {
|
||||||
|
final path = await pathForWallet(name: credentials.name, type: getType());
|
||||||
|
final password = credentials.password;
|
||||||
|
final height = credentials.height;
|
||||||
|
|
||||||
|
if (wptr == null ) monero_wallet_manager.createWalletPointer();
|
||||||
|
|
||||||
|
enableLedgerExchange(wptr!, credentials.ledgerConnection);
|
||||||
|
await monero_wallet_manager.restoreWalletFromHardwareWallet(
|
||||||
|
path: path,
|
||||||
|
password: password!,
|
||||||
|
restoreHeight: height!,
|
||||||
|
deviceName: 'Ledger');
|
||||||
|
|
||||||
|
final wallet = MoneroWallet(
|
||||||
|
walletInfo: credentials.walletInfo!,
|
||||||
|
unspentCoinsInfo: unspentCoinsInfoSource,
|
||||||
|
password: credentials.password!);
|
||||||
|
await wallet.init();
|
||||||
|
|
||||||
|
return wallet;
|
||||||
|
} catch (e) {
|
||||||
|
// TODO: Implement Exception for wallet list service.
|
||||||
|
print('MoneroWalletsManager Error: $e');
|
||||||
|
rethrow;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -253,9 +293,9 @@ class MoneroWalletService extends WalletService<
|
||||||
seed: credentials.mnemonic,
|
seed: credentials.mnemonic,
|
||||||
restoreHeight: credentials.height!);
|
restoreHeight: credentials.height!);
|
||||||
final wallet = MoneroWallet(
|
final wallet = MoneroWallet(
|
||||||
walletInfo: credentials.walletInfo!,
|
walletInfo: credentials.walletInfo!,
|
||||||
unspentCoinsInfo: unspentCoinsInfoSource,
|
unspentCoinsInfo: unspentCoinsInfoSource,
|
||||||
password: credentials.password!);
|
password: credentials.password!);
|
||||||
await wallet.init();
|
await wallet.init();
|
||||||
|
|
||||||
return wallet;
|
return wallet;
|
||||||
|
@ -283,8 +323,8 @@ class MoneroWalletService extends WalletService<
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<MoneroWallet> _restoreFromPolyseed(
|
Future<MoneroWallet> _restoreFromPolyseed(String path, String password, Polyseed polyseed,
|
||||||
String path, String password, Polyseed polyseed, WalletInfo walletInfo, PolyseedLang lang,
|
WalletInfo walletInfo, PolyseedLang lang,
|
||||||
{PolyseedCoin coin = PolyseedCoin.POLYSEED_MONERO, int? overrideHeight}) async {
|
{PolyseedCoin coin = PolyseedCoin.POLYSEED_MONERO, int? overrideHeight}) async {
|
||||||
final height = overrideHeight ??
|
final height = overrideHeight ??
|
||||||
getMoneroHeigthByDate(date: DateTime.fromMillisecondsSinceEpoch(polyseed.birthday * 1000));
|
getMoneroHeigthByDate(date: DateTime.fromMillisecondsSinceEpoch(polyseed.birthday * 1000));
|
||||||
|
@ -329,7 +369,9 @@ class MoneroWalletService extends WalletService<
|
||||||
|
|
||||||
dir.listSync().forEach((f) {
|
dir.listSync().forEach((f) {
|
||||||
final file = File(f.path);
|
final file = File(f.path);
|
||||||
final name = f.path.split('/').last;
|
final name = f.path
|
||||||
|
.split('/')
|
||||||
|
.last;
|
||||||
final newPath = newWalletDirPath + '/$name';
|
final newPath = newWalletDirPath + '/$name';
|
||||||
final newFile = File(newPath);
|
final newFile = File(newPath);
|
||||||
|
|
||||||
|
@ -366,4 +408,11 @@ class MoneroWalletService extends WalletService<
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool requireHardwareWalletConnection(String name) {
|
||||||
|
final walletInfo = walletInfoSource.values
|
||||||
|
.firstWhere((info) => info.id == WalletBase.idFor(name, getType()));
|
||||||
|
return walletInfo.isHardwareWallet;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import 'package:cw_monero/api/account_list.dart';
|
||||||
import 'package:cw_monero/api/structs/pending_transaction.dart';
|
import 'package:cw_monero/api/structs/pending_transaction.dart';
|
||||||
import 'package:cw_monero/api/transaction_history.dart'
|
import 'package:cw_monero/api/transaction_history.dart'
|
||||||
as monero_transaction_history;
|
as monero_transaction_history;
|
||||||
|
@ -35,11 +36,32 @@ class PendingMoneroTransaction with PendingTransaction {
|
||||||
String get feeFormatted => AmountConverter.amountIntToString(
|
String get feeFormatted => AmountConverter.amountIntToString(
|
||||||
CryptoCurrency.xmr, pendingTransactionDescription.fee);
|
CryptoCurrency.xmr, pendingTransactionDescription.fee);
|
||||||
|
|
||||||
|
bool shouldCommitUR() => isViewOnly;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> commit() async {
|
Future<void> commit() async {
|
||||||
try {
|
try {
|
||||||
monero_transaction_history.commitTransactionFromPointerAddress(
|
monero_transaction_history.commitTransactionFromPointerAddress(
|
||||||
address: pendingTransactionDescription.pointerAddress);
|
address: pendingTransactionDescription.pointerAddress,
|
||||||
|
useUR: false);
|
||||||
|
} catch (e) {
|
||||||
|
final message = e.toString();
|
||||||
|
|
||||||
|
if (message.contains('Reason: double spend')) {
|
||||||
|
throw DoubleSpendException();
|
||||||
|
}
|
||||||
|
|
||||||
|
rethrow;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<String?> commitUR() async {
|
||||||
|
try {
|
||||||
|
final ret = monero_transaction_history.commitTransactionFromPointerAddress(
|
||||||
|
address: pendingTransactionDescription.pointerAddress,
|
||||||
|
useUR: true);
|
||||||
|
return ret;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
final message = e.toString();
|
final message = e.toString();
|
||||||
|
|
||||||
|
|
|
@ -29,10 +29,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: asn1lib
|
name: asn1lib
|
||||||
sha256: "58082b3f0dca697204dbab0ef9ff208bfaea7767ea771076af9a343488428dda"
|
sha256: "6b151826fcc95ff246cd219a0bf4c753ea14f4081ad71c61939becf3aba27f70"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.5.3"
|
version: "1.5.5"
|
||||||
async:
|
async:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -41,6 +41,14 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.11.0"
|
version: "2.11.0"
|
||||||
|
bluez:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: bluez
|
||||||
|
sha256: "203a1924e818a9dd74af2b2c7a8f375ab8e5edf0e486bba8f90a0d8a17ed9fce"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.8.2"
|
||||||
boolean_selector:
|
boolean_selector:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -209,6 +217,14 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.2.4"
|
version: "2.2.4"
|
||||||
|
dbus:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: dbus
|
||||||
|
sha256: "365c771ac3b0e58845f39ec6deebc76e3276aa9922b0cc60840712094d9047ac"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.7.10"
|
||||||
encrypt:
|
encrypt:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
@ -229,10 +245,10 @@ packages:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: ffi
|
name: ffi
|
||||||
sha256: "493f37e7df1804778ff3a53bd691d8692ddf69702cf4c1c1096a2e41b4779e21"
|
sha256: "16ed7b077ef01ad6170a3d0c57caa4a112a38d7a2ed5602e0aca9ca6f3d98da6"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.2"
|
version: "2.1.3"
|
||||||
file:
|
file:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -267,6 +283,14 @@ packages:
|
||||||
description: flutter
|
description: flutter
|
||||||
source: sdk
|
source: sdk
|
||||||
version: "0.0.0"
|
version: "0.0.0"
|
||||||
|
flutter_web_bluetooth:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: flutter_web_bluetooth
|
||||||
|
sha256: "52ce64f65d7321c4bf6abfe9dac02fb888731339a5e0ad6de59fb916c20c9f02"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.2.3"
|
||||||
frontend_server_client:
|
frontend_server_client:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -295,18 +319,18 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: hashlib
|
name: hashlib
|
||||||
sha256: d41795742c10947930630118c6836608deeb9047cd05aee32d2baeb697afd66a
|
sha256: f572f2abce09fc7aee53f15927052b9732ea1053e540af8cae211111ee0b99b1
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.19.2"
|
version: "1.21.0"
|
||||||
hashlib_codecs:
|
hashlib_codecs:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: hashlib_codecs
|
name: hashlib_codecs
|
||||||
sha256: "2b570061f5a4b378425be28a576c1e11783450355ad4345a19f606ff3d96db0f"
|
sha256: "8cea9ccafcfeaa7324d2ae52c61c69f7ff71f4237507a018caab31b9e416e3b1"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.5.0"
|
version: "2.6.0"
|
||||||
hive:
|
hive:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -327,10 +351,10 @@ packages:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: http
|
name: http
|
||||||
sha256: "761a297c042deedc1ffbb156d6e2af13886bb305c2a343a4d972504cd67dd938"
|
sha256: b9c29a161230ee03d3ccf545097fccd9b87a5264228c5d348202e0f0c28f9010
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.2.1"
|
version: "1.2.2"
|
||||||
http_multi_server:
|
http_multi_server:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -403,6 +427,22 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.1"
|
version: "2.0.1"
|
||||||
|
ledger_flutter_plus:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: ledger_flutter_plus
|
||||||
|
sha256: c7b04008553193dbca7e17b430768eecc372a72b0ff3625b5e7fc5e5c8d3231b
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.4.1"
|
||||||
|
ledger_usb_plus:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: ledger_usb_plus
|
||||||
|
sha256: "21cc5d976cf7edb3518bd2a0c4164139cbb0817d2e4f2054707fc4edfdf9ce87"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.0.4"
|
||||||
logging:
|
logging:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -439,10 +479,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: mime
|
name: mime
|
||||||
sha256: "2e123074287cc9fd6c09de8336dae606d1ddb88d9ac47358826db698c176a1f2"
|
sha256: "801fd0b26f14a4a58ccb09d5892c3fbdeff209594300a542492cf13fba9d247a"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.5"
|
version: "1.0.6"
|
||||||
mobx:
|
mobx:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
@ -463,8 +503,8 @@ packages:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
path: "impls/monero.dart"
|
path: "impls/monero.dart"
|
||||||
ref: "6eb571ea498ed7b854934785f00fabfd0dadf75b"
|
ref: d72c15f4339791a7bbdf17e9d827b7b56ca144e4
|
||||||
resolved-ref: "6eb571ea498ed7b854934785f00fabfd0dadf75b"
|
resolved-ref: d72c15f4339791a7bbdf17e9d827b7b56ca144e4
|
||||||
url: "https://github.com/mrcyjanek/monero_c"
|
url: "https://github.com/mrcyjanek/monero_c"
|
||||||
source: git
|
source: git
|
||||||
version: "0.0.0"
|
version: "0.0.0"
|
||||||
|
@ -504,10 +544,10 @@ packages:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: path_provider
|
name: path_provider
|
||||||
sha256: c9e7d3a4cd1410877472158bee69963a4579f78b68c65a2b7d40d1a7a88bb161
|
sha256: fec0d61223fba3154d87759e3cc27fe2c8dc498f6386c6d6fc80d1afdd1bf378
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.3"
|
version: "2.1.4"
|
||||||
path_provider_android:
|
path_provider_android:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -544,10 +584,18 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: path_provider_windows
|
name: path_provider_windows
|
||||||
sha256: "8bc9f22eee8690981c22aa7fc602f5c85b497a6fb2ceb35ee5a5e5ed85ad8170"
|
sha256: bd6f00dbd873bfb70d0761682da2b3a2c2fccc2b9e84c495821639601d81afe7
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.2.1"
|
version: "2.3.0"
|
||||||
|
petitparser:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: petitparser
|
||||||
|
sha256: c15605cd28af66339f8eb6fbe0e541bfe2d1b72d5825efc6598f3e0a31b9ad27
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "6.0.2"
|
||||||
platform:
|
platform:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -612,6 +660,14 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.3.0"
|
version: "1.3.0"
|
||||||
|
rxdart:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: rxdart
|
||||||
|
sha256: "5c3004a4a8dbb94bd4bf5412a4def4acdaa12e12f269737a5751369e12d1a962"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.28.0"
|
||||||
shelf:
|
shelf:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -737,6 +793,22 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.3.2"
|
version: "1.3.2"
|
||||||
|
universal_ble:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: universal_ble
|
||||||
|
sha256: "0dfbd6b64bff3ad61ed7a895c232530d9614e9b01ab261a74433a43267edb7f3"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.12.0"
|
||||||
|
universal_platform:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: universal_platform
|
||||||
|
sha256: "64e16458a0ea9b99260ceb5467a214c1f298d647c659af1bff6d3bf82536b1ec"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.1.0"
|
||||||
unorm_dart:
|
unorm_dart:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -785,22 +857,22 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.4.5"
|
version: "2.4.5"
|
||||||
win32:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: win32
|
|
||||||
sha256: "0eaf06e3446824099858367950a813472af675116bf63f008a4c2a75ae13e9cb"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "5.5.0"
|
|
||||||
xdg_directories:
|
xdg_directories:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: xdg_directories
|
name: xdg_directories
|
||||||
sha256: faea9dee56b520b55a566385b84f2e8de55e7496104adada9962e0bd11bcff1d
|
sha256: "7a3f37b05d989967cdddcbb571f1ea834867ae2faa29725fd085180e0883aa15"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.4"
|
version: "1.1.0"
|
||||||
|
xml:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: xml
|
||||||
|
sha256: b015a8ad1c488f66851d762d3090a21c600e479dc75e68328c52774040cf9226
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "6.5.0"
|
||||||
yaml:
|
yaml:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -811,4 +883,4 @@ packages:
|
||||||
version: "3.1.2"
|
version: "3.1.2"
|
||||||
sdks:
|
sdks:
|
||||||
dart: ">=3.3.0 <4.0.0"
|
dart: ">=3.3.0 <4.0.0"
|
||||||
flutter: ">=3.16.6"
|
flutter: ">=3.19.0"
|
||||||
|
|
|
@ -25,9 +25,11 @@ dependencies:
|
||||||
monero:
|
monero:
|
||||||
git:
|
git:
|
||||||
url: https://github.com/mrcyjanek/monero_c
|
url: https://github.com/mrcyjanek/monero_c
|
||||||
ref: 6eb571ea498ed7b854934785f00fabfd0dadf75b # monero_c hash
|
ref: d72c15f4339791a7bbdf17e9d827b7b56ca144e4
|
||||||
|
# ref: 6eb571ea498ed7b854934785f00fabfd0dadf75b # monero_c hash
|
||||||
path: impls/monero.dart
|
path: impls/monero.dart
|
||||||
mutex: ^3.1.0
|
mutex: ^3.1.0
|
||||||
|
ledger_flutter_plus: ^1.4.1
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
|
|
|
@ -37,4 +37,9 @@ class PendingNanoTransaction with PendingTransaction {
|
||||||
await nanoClient.processBlock(block, "send");
|
await nanoClient.processBlock(block, "send");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<String?> commitUR() {
|
||||||
|
throw UnimplementedError();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
189
cw_shared_external/pubspec.lock
Normal file
|
@ -0,0 +1,189 @@
|
||||||
|
# Generated by pub
|
||||||
|
# See https://dart.dev/tools/pub/glossary#lockfile
|
||||||
|
packages:
|
||||||
|
async:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: async
|
||||||
|
sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.11.0"
|
||||||
|
boolean_selector:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: boolean_selector
|
||||||
|
sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.1.1"
|
||||||
|
characters:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: characters
|
||||||
|
sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.3.0"
|
||||||
|
clock:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: clock
|
||||||
|
sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.1.1"
|
||||||
|
collection:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: collection
|
||||||
|
sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.18.0"
|
||||||
|
fake_async:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: fake_async
|
||||||
|
sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.3.1"
|
||||||
|
flutter:
|
||||||
|
dependency: "direct main"
|
||||||
|
description: flutter
|
||||||
|
source: sdk
|
||||||
|
version: "0.0.0"
|
||||||
|
flutter_test:
|
||||||
|
dependency: "direct dev"
|
||||||
|
description: flutter
|
||||||
|
source: sdk
|
||||||
|
version: "0.0.0"
|
||||||
|
leak_tracker:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: leak_tracker
|
||||||
|
sha256: "7f0df31977cb2c0b88585095d168e689669a2cc9b97c309665e3386f3e9d341a"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "10.0.4"
|
||||||
|
leak_tracker_flutter_testing:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: leak_tracker_flutter_testing
|
||||||
|
sha256: "06e98f569d004c1315b991ded39924b21af84cf14cc94791b8aea337d25b57f8"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.0.3"
|
||||||
|
leak_tracker_testing:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: leak_tracker_testing
|
||||||
|
sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.0.1"
|
||||||
|
matcher:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: matcher
|
||||||
|
sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.12.16+1"
|
||||||
|
material_color_utilities:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: material_color_utilities
|
||||||
|
sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.8.0"
|
||||||
|
meta:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: meta
|
||||||
|
sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.12.0"
|
||||||
|
path:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: path
|
||||||
|
sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.9.0"
|
||||||
|
sky_engine:
|
||||||
|
dependency: transitive
|
||||||
|
description: flutter
|
||||||
|
source: sdk
|
||||||
|
version: "0.0.99"
|
||||||
|
source_span:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: source_span
|
||||||
|
sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.10.0"
|
||||||
|
stack_trace:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: stack_trace
|
||||||
|
sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.11.1"
|
||||||
|
stream_channel:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: stream_channel
|
||||||
|
sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.1.2"
|
||||||
|
string_scanner:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: string_scanner
|
||||||
|
sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.2.0"
|
||||||
|
term_glyph:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: term_glyph
|
||||||
|
sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.2.1"
|
||||||
|
test_api:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: test_api
|
||||||
|
sha256: "9955ae474176f7ac8ee4e989dadfb411a58c30415bcfb648fa04b2b8a03afa7f"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.7.0"
|
||||||
|
vector_math:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: vector_math
|
||||||
|
sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.1.4"
|
||||||
|
vm_service:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: vm_service
|
||||||
|
sha256: "3923c89304b715fb1eb6423f017651664a03bf5f4b29983627c4da791f74a4ec"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "14.2.1"
|
||||||
|
sdks:
|
||||||
|
dart: ">=3.3.0 <4.0.0"
|
||||||
|
flutter: ">=3.18.0-18.0.pre.54"
|
|
@ -40,4 +40,9 @@ class PendingSolanaTransaction with PendingTransaction {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get id => '';
|
String get id => '';
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<String?> commitUR() {
|
||||||
|
throw UnimplementedError();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,4 +30,9 @@ class PendingTronTransaction with PendingTransaction {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get id => '';
|
String get id => '';
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<String?> commitUR() {
|
||||||
|
throw UnimplementedError();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
class ConnectionToNodeException implements Exception {
|
||||||
|
ConnectionToNodeException({required this.message});
|
||||||
|
|
||||||
|
final String message;
|
||||||
|
}
|
|
@ -3,5 +3,6 @@ class WalletRestoreFromKeysException implements Exception {
|
||||||
|
|
||||||
final String message;
|
final String message;
|
||||||
|
|
||||||
|
@override
|
||||||
String toString() => message;
|
String toString() => message;
|
||||||
}
|
}
|
12
cw_wownero/lib/api/structs/account_row.dart
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
import 'dart:ffi';
|
||||||
|
import 'package:ffi/ffi.dart';
|
||||||
|
|
||||||
|
class AccountRow extends Struct {
|
||||||
|
@Int64()
|
||||||
|
external int id;
|
||||||
|
|
||||||
|
external Pointer<Utf8> label;
|
||||||
|
|
||||||
|
String getLabel() => label.toDartString();
|
||||||
|
int getId() => id;
|
||||||
|
}
|
73
cw_wownero/lib/api/structs/coins_info_row.dart
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
import 'dart:ffi';
|
||||||
|
import 'package:ffi/ffi.dart';
|
||||||
|
|
||||||
|
class CoinsInfoRow extends Struct {
|
||||||
|
@Int64()
|
||||||
|
external int blockHeight;
|
||||||
|
|
||||||
|
external Pointer<Utf8> hash;
|
||||||
|
|
||||||
|
@Uint64()
|
||||||
|
external int internalOutputIndex;
|
||||||
|
|
||||||
|
@Uint64()
|
||||||
|
external int globalOutputIndex;
|
||||||
|
|
||||||
|
@Int8()
|
||||||
|
external int spent;
|
||||||
|
|
||||||
|
@Int8()
|
||||||
|
external int frozen;
|
||||||
|
|
||||||
|
@Uint64()
|
||||||
|
external int spentHeight;
|
||||||
|
|
||||||
|
@Uint64()
|
||||||
|
external int amount;
|
||||||
|
|
||||||
|
@Int8()
|
||||||
|
external int rct;
|
||||||
|
|
||||||
|
@Int8()
|
||||||
|
external int keyImageKnown;
|
||||||
|
|
||||||
|
@Uint64()
|
||||||
|
external int pkIndex;
|
||||||
|
|
||||||
|
@Uint32()
|
||||||
|
external int subaddrIndex;
|
||||||
|
|
||||||
|
@Uint32()
|
||||||
|
external int subaddrAccount;
|
||||||
|
|
||||||
|
external Pointer<Utf8> address;
|
||||||
|
|
||||||
|
external Pointer<Utf8> addressLabel;
|
||||||
|
|
||||||
|
external Pointer<Utf8> keyImage;
|
||||||
|
|
||||||
|
@Uint64()
|
||||||
|
external int unlockTime;
|
||||||
|
|
||||||
|
@Int8()
|
||||||
|
external int unlocked;
|
||||||
|
|
||||||
|
external Pointer<Utf8> pubKey;
|
||||||
|
|
||||||
|
@Int8()
|
||||||
|
external int coinbase;
|
||||||
|
|
||||||
|
external Pointer<Utf8> description;
|
||||||
|
|
||||||
|
String getHash() => hash.toDartString();
|
||||||
|
|
||||||
|
String getAddress() => address.toDartString();
|
||||||
|
|
||||||
|
String getAddressLabel() => addressLabel.toDartString();
|
||||||
|
|
||||||
|
String getKeyImage() => keyImage.toDartString();
|
||||||
|
|
||||||
|
String getPubKey() => pubKey.toDartString();
|
||||||
|
|
||||||
|
String getDescription() => description.toDartString();
|
||||||
|
}
|
15
cw_wownero/lib/api/structs/subaddress_row.dart
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
import 'dart:ffi';
|
||||||
|
import 'package:ffi/ffi.dart';
|
||||||
|
|
||||||
|
class SubaddressRow extends Struct {
|
||||||
|
@Int64()
|
||||||
|
external int id;
|
||||||
|
|
||||||
|
external Pointer<Utf8> address;
|
||||||
|
|
||||||
|
external Pointer<Utf8> label;
|
||||||
|
|
||||||
|
String getLabel() => label.toDartString();
|
||||||
|
String getAddress() => address.toDartString();
|
||||||
|
int getId() => id;
|
||||||
|
}
|
41
cw_wownero/lib/api/structs/transaction_info_row.dart
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
import 'dart:ffi';
|
||||||
|
import 'package:ffi/ffi.dart';
|
||||||
|
|
||||||
|
class TransactionInfoRow extends Struct {
|
||||||
|
@Uint64()
|
||||||
|
external int amount;
|
||||||
|
|
||||||
|
@Uint64()
|
||||||
|
external int fee;
|
||||||
|
|
||||||
|
@Uint64()
|
||||||
|
external int blockHeight;
|
||||||
|
|
||||||
|
@Uint64()
|
||||||
|
external int confirmations;
|
||||||
|
|
||||||
|
@Uint32()
|
||||||
|
external int subaddrAccount;
|
||||||
|
|
||||||
|
@Int8()
|
||||||
|
external int direction;
|
||||||
|
|
||||||
|
@Int8()
|
||||||
|
external int isPending;
|
||||||
|
|
||||||
|
@Uint32()
|
||||||
|
external int subaddrIndex;
|
||||||
|
|
||||||
|
external Pointer<Utf8> hash;
|
||||||
|
|
||||||
|
external Pointer<Utf8> paymentId;
|
||||||
|
|
||||||
|
@Int64()
|
||||||
|
external int datetime;
|
||||||
|
|
||||||
|
int getDatetime() => datetime;
|
||||||
|
int getAmount() => amount >= 0 ? amount : amount * -1;
|
||||||
|
bool getIsPending() => isPending != 0;
|
||||||
|
String getHash() => hash.toDartString();
|
||||||
|
String getPaymentId() => paymentId.toDartString();
|
||||||
|
}
|
8
cw_wownero/lib/api/structs/ut8_box.dart
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
import 'dart:ffi';
|
||||||
|
import 'package:ffi/ffi.dart';
|
||||||
|
|
||||||
|
class Utf8Box extends Struct {
|
||||||
|
external Pointer<Utf8> value;
|
||||||
|
|
||||||
|
String getValue() => value.toDartString();
|
||||||
|
}
|
8
cw_wownero/lib/cw_wownero.dart
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
|
||||||
|
import 'cw_wownero_platform_interface.dart';
|
||||||
|
|
||||||
|
class CwWownero {
|
||||||
|
Future<String?> getPlatformVersion() {
|
||||||
|
return CwWowneroPlatform.instance.getPlatformVersion();
|
||||||
|
}
|
||||||
|
}
|
17
cw_wownero/lib/cw_wownero_method_channel.dart
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
|
|
||||||
|
import 'cw_wownero_platform_interface.dart';
|
||||||
|
|
||||||
|
/// An implementation of [CwWowneroPlatform] that uses method channels.
|
||||||
|
class MethodChannelCwWownero extends CwWowneroPlatform {
|
||||||
|
/// The method channel used to interact with the native platform.
|
||||||
|
@visibleForTesting
|
||||||
|
final methodChannel = const MethodChannel('cw_wownero');
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<String?> getPlatformVersion() async {
|
||||||
|
final version = await methodChannel.invokeMethod<String>('getPlatformVersion');
|
||||||
|
return version;
|
||||||
|
}
|
||||||
|
}
|
29
cw_wownero/lib/cw_wownero_platform_interface.dart
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
import 'package:plugin_platform_interface/plugin_platform_interface.dart';
|
||||||
|
|
||||||
|
import 'cw_wownero_method_channel.dart';
|
||||||
|
|
||||||
|
abstract class CwWowneroPlatform extends PlatformInterface {
|
||||||
|
/// Constructs a CwWowneroPlatform.
|
||||||
|
CwWowneroPlatform() : super(token: _token);
|
||||||
|
|
||||||
|
static final Object _token = Object();
|
||||||
|
|
||||||
|
static CwWowneroPlatform _instance = MethodChannelCwWownero();
|
||||||
|
|
||||||
|
/// The default instance of [CwWowneroPlatform] to use.
|
||||||
|
///
|
||||||
|
/// Defaults to [MethodChannelCwWownero].
|
||||||
|
static CwWowneroPlatform get instance => _instance;
|
||||||
|
|
||||||
|
/// Platform-specific implementations should set this with their own
|
||||||
|
/// platform-specific class that extends [CwWowneroPlatform] when
|
||||||
|
/// they register themselves.
|
||||||
|
static set instance(CwWowneroPlatform instance) {
|
||||||
|
PlatformInterface.verifyToken(instance, _token);
|
||||||
|
_instance = instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<String?> getPlatformVersion() {
|
||||||
|
throw UnimplementedError('platformVersion() has not been implemented.');
|
||||||
|
}
|
||||||
|
}
|
1689
cw_wownero/lib/mywownero.dart
Normal file
|
@ -50,4 +50,9 @@ class PendingWowneroTransaction with PendingTransaction {
|
||||||
rethrow;
|
rethrow;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<String?> commitUR() {
|
||||||
|
throw UnimplementedError();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -120,6 +120,7 @@ abstract class WowneroWalletBase
|
||||||
|
|
||||||
@override
|
@override
|
||||||
MoneroWalletKeys get keys => MoneroWalletKeys(
|
MoneroWalletKeys get keys => MoneroWalletKeys(
|
||||||
|
primaryAddress: wownero_wallet.getAddress(accountIndex: 0, addressIndex: 0),
|
||||||
privateSpendKey: wownero_wallet.getSecretSpendKey(),
|
privateSpendKey: wownero_wallet.getSecretSpendKey(),
|
||||||
privateViewKey: wownero_wallet.getSecretViewKey(),
|
privateViewKey: wownero_wallet.getSecretViewKey(),
|
||||||
publicSpendKey: wownero_wallet.getPublicSpendKey(),
|
publicSpendKey: wownero_wallet.getPublicSpendKey(),
|
||||||
|
|
|
@ -463,8 +463,8 @@ packages:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
path: "impls/monero.dart"
|
path: "impls/monero.dart"
|
||||||
ref: "6eb571ea498ed7b854934785f00fabfd0dadf75b"
|
ref: d72c15f4339791a7bbdf17e9d827b7b56ca144e4
|
||||||
resolved-ref: "6eb571ea498ed7b854934785f00fabfd0dadf75b"
|
resolved-ref: d72c15f4339791a7bbdf17e9d827b7b56ca144e4
|
||||||
url: "https://github.com/mrcyjanek/monero_c"
|
url: "https://github.com/mrcyjanek/monero_c"
|
||||||
source: git
|
source: git
|
||||||
version: "0.0.0"
|
version: "0.0.0"
|
||||||
|
@ -552,10 +552,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: plugin_platform_interface
|
name: plugin_platform_interface
|
||||||
sha256: dbf0f707c78beedc9200146ad3cb0ab4d5da13c246336987be6940f026500d3a
|
sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.3"
|
version: "2.1.8"
|
||||||
pointycastle:
|
pointycastle:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
|
@ -25,7 +25,8 @@ dependencies:
|
||||||
monero:
|
monero:
|
||||||
git:
|
git:
|
||||||
url: https://github.com/mrcyjanek/monero_c
|
url: https://github.com/mrcyjanek/monero_c
|
||||||
ref: 6eb571ea498ed7b854934785f00fabfd0dadf75b # monero_c hash
|
ref: d72c15f4339791a7bbdf17e9d827b7b56ca144e4
|
||||||
|
# ref: 6eb571ea498ed7b854934785f00fabfd0dadf75b # monero_c hash
|
||||||
path: impls/monero.dart
|
path: impls/monero.dart
|
||||||
mutex: ^3.1.0
|
mutex: ^3.1.0
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,4 @@
|
||||||
PODS:
|
PODS:
|
||||||
- barcode_scan2 (0.0.1):
|
|
||||||
- Flutter
|
|
||||||
- MTBBarcodeScanner
|
|
||||||
- SwiftProtobuf
|
|
||||||
- connectivity_plus (0.0.1):
|
- connectivity_plus (0.0.1):
|
||||||
- Flutter
|
- Flutter
|
||||||
- ReachabilitySwift
|
- ReachabilitySwift
|
||||||
|
@ -76,6 +72,8 @@ PODS:
|
||||||
- DKPhotoGallery/Resource (0.0.19):
|
- DKPhotoGallery/Resource (0.0.19):
|
||||||
- SDWebImage
|
- SDWebImage
|
||||||
- SwiftyGif
|
- SwiftyGif
|
||||||
|
- fast_scanner (5.1.1):
|
||||||
|
- Flutter
|
||||||
- file_picker (0.0.1):
|
- file_picker (0.0.1):
|
||||||
- DKImagePickerController/PhotoGallery
|
- DKImagePickerController/PhotoGallery
|
||||||
- Flutter
|
- Flutter
|
||||||
|
@ -100,7 +98,6 @@ PODS:
|
||||||
- Flutter
|
- Flutter
|
||||||
- integration_test (0.0.1):
|
- integration_test (0.0.1):
|
||||||
- Flutter
|
- Flutter
|
||||||
- MTBBarcodeScanner (5.0.11)
|
|
||||||
- OrderedSet (5.0.0)
|
- OrderedSet (5.0.0)
|
||||||
- package_info_plus (0.4.5):
|
- package_info_plus (0.4.5):
|
||||||
- Flutter
|
- Flutter
|
||||||
|
@ -109,12 +106,7 @@ PODS:
|
||||||
- FlutterMacOS
|
- FlutterMacOS
|
||||||
- permission_handler_apple (9.1.1):
|
- permission_handler_apple (9.1.1):
|
||||||
- Flutter
|
- Flutter
|
||||||
- Protobuf (3.28.2)
|
|
||||||
- ReachabilitySwift (5.2.3)
|
- ReachabilitySwift (5.2.3)
|
||||||
- reactive_ble_mobile (0.0.1):
|
|
||||||
- Flutter
|
|
||||||
- Protobuf (~> 3.5)
|
|
||||||
- SwiftProtobuf (~> 1.0)
|
|
||||||
- SDWebImage (5.19.7):
|
- SDWebImage (5.19.7):
|
||||||
- SDWebImage/Core (= 5.19.7)
|
- SDWebImage/Core (= 5.19.7)
|
||||||
- SDWebImage/Core (5.19.7)
|
- SDWebImage/Core (5.19.7)
|
||||||
|
@ -127,11 +119,13 @@ PODS:
|
||||||
- FlutterMacOS
|
- FlutterMacOS
|
||||||
- sp_scanner (0.0.1):
|
- sp_scanner (0.0.1):
|
||||||
- Flutter
|
- Flutter
|
||||||
- SwiftProtobuf (1.27.1)
|
|
||||||
- SwiftyGif (5.4.5)
|
- SwiftyGif (5.4.5)
|
||||||
- Toast (4.1.1)
|
- Toast (4.1.1)
|
||||||
- uni_links (0.0.1):
|
- uni_links (0.0.1):
|
||||||
- Flutter
|
- Flutter
|
||||||
|
- universal_ble (0.0.1):
|
||||||
|
- Flutter
|
||||||
|
- FlutterMacOS
|
||||||
- url_launcher_ios (0.0.1):
|
- url_launcher_ios (0.0.1):
|
||||||
- Flutter
|
- Flutter
|
||||||
- wakelock_plus (0.0.1):
|
- wakelock_plus (0.0.1):
|
||||||
|
@ -140,7 +134,6 @@ PODS:
|
||||||
- Flutter
|
- Flutter
|
||||||
|
|
||||||
DEPENDENCIES:
|
DEPENDENCIES:
|
||||||
- barcode_scan2 (from `.symlinks/plugins/barcode_scan2/ios`)
|
|
||||||
- connectivity_plus (from `.symlinks/plugins/connectivity_plus/ios`)
|
- connectivity_plus (from `.symlinks/plugins/connectivity_plus/ios`)
|
||||||
- CryptoSwift
|
- CryptoSwift
|
||||||
- cw_haven (from `.symlinks/plugins/cw_haven/ios`)
|
- cw_haven (from `.symlinks/plugins/cw_haven/ios`)
|
||||||
|
@ -149,6 +142,7 @@ DEPENDENCIES:
|
||||||
- device_display_brightness (from `.symlinks/plugins/device_display_brightness/ios`)
|
- device_display_brightness (from `.symlinks/plugins/device_display_brightness/ios`)
|
||||||
- device_info_plus (from `.symlinks/plugins/device_info_plus/ios`)
|
- device_info_plus (from `.symlinks/plugins/device_info_plus/ios`)
|
||||||
- devicelocale (from `.symlinks/plugins/devicelocale/ios`)
|
- devicelocale (from `.symlinks/plugins/devicelocale/ios`)
|
||||||
|
- fast_scanner (from `.symlinks/plugins/fast_scanner/ios`)
|
||||||
- file_picker (from `.symlinks/plugins/file_picker/ios`)
|
- file_picker (from `.symlinks/plugins/file_picker/ios`)
|
||||||
- Flutter (from `Flutter`)
|
- Flutter (from `Flutter`)
|
||||||
- flutter_inappwebview_ios (from `.symlinks/plugins/flutter_inappwebview_ios/ios`)
|
- flutter_inappwebview_ios (from `.symlinks/plugins/flutter_inappwebview_ios/ios`)
|
||||||
|
@ -161,12 +155,12 @@ DEPENDENCIES:
|
||||||
- package_info_plus (from `.symlinks/plugins/package_info_plus/ios`)
|
- package_info_plus (from `.symlinks/plugins/package_info_plus/ios`)
|
||||||
- path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`)
|
- path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`)
|
||||||
- permission_handler_apple (from `.symlinks/plugins/permission_handler_apple/ios`)
|
- permission_handler_apple (from `.symlinks/plugins/permission_handler_apple/ios`)
|
||||||
- reactive_ble_mobile (from `.symlinks/plugins/reactive_ble_mobile/ios`)
|
|
||||||
- sensitive_clipboard (from `.symlinks/plugins/sensitive_clipboard/ios`)
|
- sensitive_clipboard (from `.symlinks/plugins/sensitive_clipboard/ios`)
|
||||||
- share_plus (from `.symlinks/plugins/share_plus/ios`)
|
- share_plus (from `.symlinks/plugins/share_plus/ios`)
|
||||||
- shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`)
|
- shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`)
|
||||||
- sp_scanner (from `.symlinks/plugins/sp_scanner/ios`)
|
- sp_scanner (from `.symlinks/plugins/sp_scanner/ios`)
|
||||||
- uni_links (from `.symlinks/plugins/uni_links/ios`)
|
- uni_links (from `.symlinks/plugins/uni_links/ios`)
|
||||||
|
- universal_ble (from `.symlinks/plugins/universal_ble/darwin`)
|
||||||
- url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`)
|
- url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`)
|
||||||
- wakelock_plus (from `.symlinks/plugins/wakelock_plus/ios`)
|
- wakelock_plus (from `.symlinks/plugins/wakelock_plus/ios`)
|
||||||
- workmanager (from `.symlinks/plugins/workmanager/ios`)
|
- workmanager (from `.symlinks/plugins/workmanager/ios`)
|
||||||
|
@ -176,18 +170,13 @@ SPEC REPOS:
|
||||||
- CryptoSwift
|
- CryptoSwift
|
||||||
- DKImagePickerController
|
- DKImagePickerController
|
||||||
- DKPhotoGallery
|
- DKPhotoGallery
|
||||||
- MTBBarcodeScanner
|
|
||||||
- OrderedSet
|
- OrderedSet
|
||||||
- Protobuf
|
|
||||||
- ReachabilitySwift
|
- ReachabilitySwift
|
||||||
- SDWebImage
|
- SDWebImage
|
||||||
- SwiftProtobuf
|
|
||||||
- SwiftyGif
|
- SwiftyGif
|
||||||
- Toast
|
- Toast
|
||||||
|
|
||||||
EXTERNAL SOURCES:
|
EXTERNAL SOURCES:
|
||||||
barcode_scan2:
|
|
||||||
:path: ".symlinks/plugins/barcode_scan2/ios"
|
|
||||||
connectivity_plus:
|
connectivity_plus:
|
||||||
:path: ".symlinks/plugins/connectivity_plus/ios"
|
:path: ".symlinks/plugins/connectivity_plus/ios"
|
||||||
cw_haven:
|
cw_haven:
|
||||||
|
@ -202,6 +191,8 @@ EXTERNAL SOURCES:
|
||||||
:path: ".symlinks/plugins/device_info_plus/ios"
|
:path: ".symlinks/plugins/device_info_plus/ios"
|
||||||
devicelocale:
|
devicelocale:
|
||||||
:path: ".symlinks/plugins/devicelocale/ios"
|
:path: ".symlinks/plugins/devicelocale/ios"
|
||||||
|
fast_scanner:
|
||||||
|
:path: ".symlinks/plugins/fast_scanner/ios"
|
||||||
file_picker:
|
file_picker:
|
||||||
:path: ".symlinks/plugins/file_picker/ios"
|
:path: ".symlinks/plugins/file_picker/ios"
|
||||||
Flutter:
|
Flutter:
|
||||||
|
@ -226,8 +217,6 @@ EXTERNAL SOURCES:
|
||||||
:path: ".symlinks/plugins/path_provider_foundation/darwin"
|
:path: ".symlinks/plugins/path_provider_foundation/darwin"
|
||||||
permission_handler_apple:
|
permission_handler_apple:
|
||||||
:path: ".symlinks/plugins/permission_handler_apple/ios"
|
:path: ".symlinks/plugins/permission_handler_apple/ios"
|
||||||
reactive_ble_mobile:
|
|
||||||
:path: ".symlinks/plugins/reactive_ble_mobile/ios"
|
|
||||||
sensitive_clipboard:
|
sensitive_clipboard:
|
||||||
:path: ".symlinks/plugins/sensitive_clipboard/ios"
|
:path: ".symlinks/plugins/sensitive_clipboard/ios"
|
||||||
share_plus:
|
share_plus:
|
||||||
|
@ -238,6 +227,8 @@ EXTERNAL SOURCES:
|
||||||
:path: ".symlinks/plugins/sp_scanner/ios"
|
:path: ".symlinks/plugins/sp_scanner/ios"
|
||||||
uni_links:
|
uni_links:
|
||||||
:path: ".symlinks/plugins/uni_links/ios"
|
:path: ".symlinks/plugins/uni_links/ios"
|
||||||
|
universal_ble:
|
||||||
|
:path: ".symlinks/plugins/universal_ble/darwin"
|
||||||
url_launcher_ios:
|
url_launcher_ios:
|
||||||
:path: ".symlinks/plugins/url_launcher_ios/ios"
|
:path: ".symlinks/plugins/url_launcher_ios/ios"
|
||||||
wakelock_plus:
|
wakelock_plus:
|
||||||
|
@ -246,7 +237,6 @@ EXTERNAL SOURCES:
|
||||||
:path: ".symlinks/plugins/workmanager/ios"
|
:path: ".symlinks/plugins/workmanager/ios"
|
||||||
|
|
||||||
SPEC CHECKSUMS:
|
SPEC CHECKSUMS:
|
||||||
barcode_scan2: 0af2bb63c81b4565aab6cd78278e4c0fa136dbb0
|
|
||||||
connectivity_plus: bf0076dd84a130856aa636df1c71ccaff908fa1d
|
connectivity_plus: bf0076dd84a130856aa636df1c71ccaff908fa1d
|
||||||
CryptoSwift: c63a805d8bb5e5538e88af4e44bb537776af11ea
|
CryptoSwift: c63a805d8bb5e5538e88af4e44bb537776af11ea
|
||||||
cw_haven: b3e54e1fbe7b8e6fda57a93206bc38f8e89b898a
|
cw_haven: b3e54e1fbe7b8e6fda57a93206bc38f8e89b898a
|
||||||
|
@ -257,6 +247,7 @@ SPEC CHECKSUMS:
|
||||||
devicelocale: b22617f40038496deffba44747101255cee005b0
|
devicelocale: b22617f40038496deffba44747101255cee005b0
|
||||||
DKImagePickerController: 946cec48c7873164274ecc4624d19e3da4c1ef3c
|
DKImagePickerController: 946cec48c7873164274ecc4624d19e3da4c1ef3c
|
||||||
DKPhotoGallery: b3834fecb755ee09a593d7c9e389d8b5d6deed60
|
DKPhotoGallery: b3834fecb755ee09a593d7c9e389d8b5d6deed60
|
||||||
|
fast_scanner: 44c00940355a51258cd6c2085734193cd23d95bc
|
||||||
file_picker: 15fd9539e4eb735dc54bae8c0534a7a9511a03de
|
file_picker: 15fd9539e4eb735dc54bae8c0534a7a9511a03de
|
||||||
Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7
|
Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7
|
||||||
flutter_inappwebview_ios: 97215cf7d4677db55df76782dbd2930c5e1c1ea0
|
flutter_inappwebview_ios: 97215cf7d4677db55df76782dbd2930c5e1c1ea0
|
||||||
|
@ -266,23 +257,20 @@ SPEC CHECKSUMS:
|
||||||
fluttertoast: 48c57db1b71b0ce9e6bba9f31c940ff4b001293c
|
fluttertoast: 48c57db1b71b0ce9e6bba9f31c940ff4b001293c
|
||||||
in_app_review: 318597b3a06c22bb46dc454d56828c85f444f99d
|
in_app_review: 318597b3a06c22bb46dc454d56828c85f444f99d
|
||||||
integration_test: 13825b8a9334a850581300559b8839134b124670
|
integration_test: 13825b8a9334a850581300559b8839134b124670
|
||||||
MTBBarcodeScanner: f453b33c4b7dfe545d8c6484ed744d55671788cb
|
|
||||||
OrderedSet: aaeb196f7fef5a9edf55d89760da9176ad40b93c
|
OrderedSet: aaeb196f7fef5a9edf55d89760da9176ad40b93c
|
||||||
package_info_plus: 58f0028419748fad15bf008b270aaa8e54380b1c
|
package_info_plus: 58f0028419748fad15bf008b270aaa8e54380b1c
|
||||||
path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46
|
path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46
|
||||||
permission_handler_apple: e76247795d700c14ea09e3a2d8855d41ee80a2e6
|
permission_handler_apple: e76247795d700c14ea09e3a2d8855d41ee80a2e6
|
||||||
Protobuf: 28c89b24435762f60244e691544ed80f50d82701
|
|
||||||
ReachabilitySwift: 7f151ff156cea1481a8411701195ac6a984f4979
|
ReachabilitySwift: 7f151ff156cea1481a8411701195ac6a984f4979
|
||||||
reactive_ble_mobile: 9ce6723d37ccf701dbffd202d487f23f5de03b4c
|
|
||||||
SDWebImage: 8a6b7b160b4d710e2a22b6900e25301075c34cb3
|
SDWebImage: 8a6b7b160b4d710e2a22b6900e25301075c34cb3
|
||||||
sensitive_clipboard: d4866e5d176581536c27bb1618642ee83adca986
|
sensitive_clipboard: d4866e5d176581536c27bb1618642ee83adca986
|
||||||
share_plus: 8875f4f2500512ea181eef553c3e27dba5135aad
|
share_plus: 8875f4f2500512ea181eef553c3e27dba5135aad
|
||||||
shared_preferences_foundation: fcdcbc04712aee1108ac7fda236f363274528f78
|
shared_preferences_foundation: fcdcbc04712aee1108ac7fda236f363274528f78
|
||||||
sp_scanner: eaa617fa827396b967116b7f1f43549ca62e9a12
|
sp_scanner: eaa617fa827396b967116b7f1f43549ca62e9a12
|
||||||
SwiftProtobuf: b109bd17979d7993a84da14b1e1fdd8b0ded934a
|
|
||||||
SwiftyGif: 706c60cf65fa2bc5ee0313beece843c8eb8194d4
|
SwiftyGif: 706c60cf65fa2bc5ee0313beece843c8eb8194d4
|
||||||
Toast: 1f5ea13423a1e6674c4abdac5be53587ae481c4e
|
Toast: 1f5ea13423a1e6674c4abdac5be53587ae481c4e
|
||||||
uni_links: d97da20c7701486ba192624d99bffaaffcfc298a
|
uni_links: d97da20c7701486ba192624d99bffaaffcfc298a
|
||||||
|
universal_ble: cf52a7b3fd2e7c14d6d7262e9fdadb72ab6b88a6
|
||||||
url_launcher_ios: 5334b05cef931de560670eeae103fd3e431ac3fe
|
url_launcher_ios: 5334b05cef931de560670eeae103fd3e431ac3fe
|
||||||
wakelock_plus: 78ec7c5b202cab7761af8e2b2b3d0671be6c4ae1
|
wakelock_plus: 78ec7c5b202cab7761af8e2b2b3d0671be6c4ae1
|
||||||
workmanager: 0afdcf5628bbde6924c21af7836fed07b42e30e6
|
workmanager: 0afdcf5628bbde6924c21af7836fed07b42e30e6
|
||||||
|
|
|
@ -230,6 +230,7 @@
|
||||||
97C146EC1CF9000F007C117D /* Resources */,
|
97C146EC1CF9000F007C117D /* Resources */,
|
||||||
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
|
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
|
||||||
F6F67323547956BC4F7B67F1 /* [CP] Embed Pods Frameworks */,
|
F6F67323547956BC4F7B67F1 /* [CP] Embed Pods Frameworks */,
|
||||||
|
777FE2B16F25A3E820834145 /* [CP] Copy Pods Resources */,
|
||||||
);
|
);
|
||||||
buildRules = (
|
buildRules = (
|
||||||
);
|
);
|
||||||
|
@ -343,6 +344,23 @@
|
||||||
shellPath = /bin/sh;
|
shellPath = /bin/sh;
|
||||||
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin\n";
|
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin\n";
|
||||||
};
|
};
|
||||||
|
777FE2B16F25A3E820834145 /* [CP] Copy Pods Resources */ = {
|
||||||
|
isa = PBXShellScriptBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
inputFileListPaths = (
|
||||||
|
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-input-files.xcfilelist",
|
||||||
|
);
|
||||||
|
name = "[CP] Copy Pods Resources";
|
||||||
|
outputFileListPaths = (
|
||||||
|
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-output-files.xcfilelist",
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
shellPath = /bin/sh;
|
||||||
|
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n";
|
||||||
|
showEnvVarsInLog = 0;
|
||||||
|
};
|
||||||
9740EEB61CF901F6004384FC /* Run Script */ = {
|
9740EEB61CF901F6004384FC /* Run Script */ = {
|
||||||
isa = PBXShellScriptBuildPhase;
|
isa = PBXShellScriptBuildPhase;
|
||||||
alwaysOutOfDate = 1;
|
alwaysOutOfDate = 1;
|
||||||
|
|
|
@ -1,6 +1,10 @@
|
||||||
import 'package:cake_wallet/buy/buy_amount.dart';
|
import 'package:cake_wallet/buy/buy_amount.dart';
|
||||||
|
import 'package:cake_wallet/buy/buy_quote.dart';
|
||||||
import 'package:cake_wallet/buy/order.dart';
|
import 'package:cake_wallet/buy/order.dart';
|
||||||
|
import 'package:cake_wallet/buy/payment_method.dart';
|
||||||
|
import 'package:cake_wallet/entities/fiat_currency.dart';
|
||||||
import 'package:cake_wallet/view_model/hardware_wallet/ledger_view_model.dart';
|
import 'package:cake_wallet/view_model/hardware_wallet/ledger_view_model.dart';
|
||||||
|
import 'package:cw_core/crypto_currency.dart';
|
||||||
import 'package:cw_core/wallet_base.dart';
|
import 'package:cw_core/wallet_base.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
@ -23,14 +27,38 @@ abstract class BuyProvider {
|
||||||
|
|
||||||
String get darkIcon;
|
String get darkIcon;
|
||||||
|
|
||||||
|
bool get isAggregator;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() => title;
|
String toString() => title;
|
||||||
|
|
||||||
Future<void> launchProvider(BuildContext context, bool? isBuyAction);
|
Future<void>? launchProvider(
|
||||||
|
{required BuildContext context,
|
||||||
|
required Quote quote,
|
||||||
|
required double amount,
|
||||||
|
required bool isBuyAction,
|
||||||
|
required String cryptoCurrencyAddress,
|
||||||
|
String? countryCode}) =>
|
||||||
|
null;
|
||||||
|
|
||||||
Future<String> requestUrl(String amount, String sourceCurrency) => throw UnimplementedError();
|
Future<String> requestUrl(String amount, String sourceCurrency) => throw UnimplementedError();
|
||||||
|
|
||||||
Future<Order> findOrderById(String id) => throw UnimplementedError();
|
Future<Order> findOrderById(String id) => throw UnimplementedError();
|
||||||
|
|
||||||
Future<BuyAmount> calculateAmount(String amount, String sourceCurrency) => throw UnimplementedError();
|
Future<BuyAmount> calculateAmount(String amount, String sourceCurrency) =>
|
||||||
|
throw UnimplementedError();
|
||||||
|
|
||||||
|
Future<List<PaymentMethod>> getAvailablePaymentTypes(
|
||||||
|
String fiatCurrency, String cryptoCurrency, bool isBuyAction) async =>
|
||||||
|
[];
|
||||||
|
|
||||||
|
Future<List<Quote>?> fetchQuote(
|
||||||
|
{required CryptoCurrency cryptoCurrency,
|
||||||
|
required FiatCurrency fiatCurrency,
|
||||||
|
required double amount,
|
||||||
|
required bool isBuyAction,
|
||||||
|
required String walletAddress,
|
||||||
|
PaymentType? paymentType,
|
||||||
|
String? countryCode}) async =>
|
||||||
|
null;
|
||||||
}
|
}
|
||||||
|
|
302
lib/buy/buy_quote.dart
Normal file
|
@ -0,0 +1,302 @@
|
||||||
|
import 'package:cake_wallet/buy/buy_provider.dart';
|
||||||
|
import 'package:cake_wallet/buy/payment_method.dart';
|
||||||
|
import 'package:cake_wallet/core/selectable_option.dart';
|
||||||
|
import 'package:cake_wallet/entities/fiat_currency.dart';
|
||||||
|
import 'package:cake_wallet/entities/provider_types.dart';
|
||||||
|
import 'package:cake_wallet/exchange/limits.dart';
|
||||||
|
import 'package:cw_core/crypto_currency.dart';
|
||||||
|
|
||||||
|
enum ProviderRecommendation { bestRate, lowKyc, successRate }
|
||||||
|
|
||||||
|
extension RecommendationTitle on ProviderRecommendation {
|
||||||
|
String get title {
|
||||||
|
switch (this) {
|
||||||
|
case ProviderRecommendation.bestRate:
|
||||||
|
return 'BEST RATE';
|
||||||
|
case ProviderRecommendation.lowKyc:
|
||||||
|
return 'LOW KYC';
|
||||||
|
case ProviderRecommendation.successRate:
|
||||||
|
return 'SUCCESS RATE';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ProviderRecommendation? getRecommendationFromString(String title) {
|
||||||
|
switch (title) {
|
||||||
|
case 'BEST RATE':
|
||||||
|
return ProviderRecommendation.bestRate;
|
||||||
|
case 'LowKyc':
|
||||||
|
return ProviderRecommendation.lowKyc;
|
||||||
|
case 'SuccessRate':
|
||||||
|
return ProviderRecommendation.successRate;
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Quote extends SelectableOption {
|
||||||
|
Quote({
|
||||||
|
required this.rate,
|
||||||
|
required this.feeAmount,
|
||||||
|
required this.networkFee,
|
||||||
|
required this.transactionFee,
|
||||||
|
required this.payout,
|
||||||
|
required this.provider,
|
||||||
|
required this.paymentType,
|
||||||
|
required this.recommendations,
|
||||||
|
this.isBuyAction = true,
|
||||||
|
this.quoteId,
|
||||||
|
this.rampId,
|
||||||
|
this.rampName,
|
||||||
|
this.rampIconPath,
|
||||||
|
this.limits,
|
||||||
|
}) : super(title: provider.isAggregator ? rampName ?? '' : provider.title);
|
||||||
|
|
||||||
|
final double rate;
|
||||||
|
final double feeAmount;
|
||||||
|
final double networkFee;
|
||||||
|
final double transactionFee;
|
||||||
|
final double payout;
|
||||||
|
final PaymentType paymentType;
|
||||||
|
final BuyProvider provider;
|
||||||
|
final String? quoteId;
|
||||||
|
final List<ProviderRecommendation> recommendations;
|
||||||
|
String? rampId;
|
||||||
|
String? rampName;
|
||||||
|
String? rampIconPath;
|
||||||
|
bool _isSelected = false;
|
||||||
|
bool _isBestRate = false;
|
||||||
|
bool isBuyAction;
|
||||||
|
Limits? limits;
|
||||||
|
|
||||||
|
late FiatCurrency _fiatCurrency;
|
||||||
|
late CryptoCurrency _cryptoCurrency;
|
||||||
|
|
||||||
|
|
||||||
|
bool get isSelected => _isSelected;
|
||||||
|
bool get isBestRate => _isBestRate;
|
||||||
|
FiatCurrency get fiatCurrency => _fiatCurrency;
|
||||||
|
CryptoCurrency get cryptoCurrency => _cryptoCurrency;
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool get isOptionSelected => this._isSelected;
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get lightIconPath =>
|
||||||
|
provider.isAggregator ? rampIconPath ?? provider.lightIcon : provider.lightIcon;
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get darkIconPath =>
|
||||||
|
provider.isAggregator ? rampIconPath ?? provider.darkIcon : provider.darkIcon;
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<String> get badges => recommendations.map((e) => e.title).toList();
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get topLeftSubTitle =>
|
||||||
|
this.rate > 0 ? '1 $cryptoName = ${rate.toStringAsFixed(2)} $fiatName' : '';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get bottomLeftSubTitle {
|
||||||
|
if (limits != null) {
|
||||||
|
final min = limits!.min;
|
||||||
|
final max = limits!.max;
|
||||||
|
return 'min: ${min} ${fiatCurrency.toString()} | max: ${max == double.infinity ? '' : '${max} ${fiatCurrency.toString()}'}';
|
||||||
|
}
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
String get fiatName => isBuyAction ? fiatCurrency.toString() : cryptoCurrency.toString();
|
||||||
|
|
||||||
|
String get cryptoName => isBuyAction ? cryptoCurrency.toString() : fiatCurrency.toString();
|
||||||
|
|
||||||
|
@override
|
||||||
|
String? get topRightSubTitle => '';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get topRightSubTitleLightIconPath => provider.isAggregator ? provider.lightIcon : '';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get topRightSubTitleDarkIconPath => provider.isAggregator ? provider.darkIcon : '';
|
||||||
|
|
||||||
|
String get quoteTitle => '${provider.title} - ${paymentType.name}';
|
||||||
|
|
||||||
|
String get formatedFee => '$feeAmount ${isBuyAction ? fiatCurrency : cryptoCurrency}';
|
||||||
|
|
||||||
|
set setIsSelected(bool isSelected) => _isSelected = isSelected;
|
||||||
|
set setIsBestRate(bool isBestRate) => _isBestRate = isBestRate;
|
||||||
|
set setFiatCurrency(FiatCurrency fiatCurrency) => _fiatCurrency = fiatCurrency;
|
||||||
|
set setCryptoCurrency(CryptoCurrency cryptoCurrency) => _cryptoCurrency = cryptoCurrency;
|
||||||
|
set setLimits(Limits limits) => this.limits = limits;
|
||||||
|
|
||||||
|
factory Quote.fromOnramperJson(Map<String, dynamic> json, bool isBuyAction,
|
||||||
|
Map<String, dynamic> metaData, PaymentType paymentType) {
|
||||||
|
final rate = _toDouble(json['rate']) ?? 0.0;
|
||||||
|
final networkFee = _toDouble(json['networkFee']) ?? 0.0;
|
||||||
|
final transactionFee = _toDouble(json['transactionFee']) ?? 0.0;
|
||||||
|
final feeAmount = double.parse((networkFee + transactionFee).toStringAsFixed(2));
|
||||||
|
|
||||||
|
final rampId = json['ramp'] as String? ?? '';
|
||||||
|
final rampData = metaData[rampId] ?? {};
|
||||||
|
final rampName = rampData['displayName'] as String? ?? '';
|
||||||
|
final rampIconPath = rampData['svg'] as String? ?? '';
|
||||||
|
|
||||||
|
final recommendations = json['recommendations'] != null
|
||||||
|
? List<String>.from(json['recommendations'] as List<dynamic>)
|
||||||
|
: <String>[];
|
||||||
|
|
||||||
|
final enumRecommendations = recommendations
|
||||||
|
.map((e) => getRecommendationFromString(e))
|
||||||
|
.whereType<ProviderRecommendation>()
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
final availablePaymentMethods = json['availablePaymentMethods'] as List<dynamic>? ?? [];
|
||||||
|
double minLimit = 0.0;
|
||||||
|
double maxLimit = double.infinity;
|
||||||
|
|
||||||
|
for (var paymentMethod in availablePaymentMethods) {
|
||||||
|
if (paymentMethod is Map<String, dynamic>) {
|
||||||
|
final details = paymentMethod['details'] as Map<String, dynamic>?;
|
||||||
|
|
||||||
|
if (details != null) {
|
||||||
|
final limits = details['limits'] as Map<String, dynamic>?;
|
||||||
|
|
||||||
|
if (limits != null && limits.isNotEmpty) {
|
||||||
|
final firstLimitEntry = limits.values.first as Map<String, dynamic>?;
|
||||||
|
if (firstLimitEntry != null) {
|
||||||
|
minLimit = _toDouble(firstLimitEntry['min'])?.roundToDouble() ?? 0.0;
|
||||||
|
maxLimit = _toDouble(firstLimitEntry['max'])?.roundToDouble() ?? double.infinity;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Quote(
|
||||||
|
rate: rate,
|
||||||
|
feeAmount: feeAmount,
|
||||||
|
networkFee: networkFee,
|
||||||
|
transactionFee: transactionFee,
|
||||||
|
payout: json['payout'] as double? ?? 0.0,
|
||||||
|
rampId: rampId,
|
||||||
|
rampName: rampName,
|
||||||
|
rampIconPath: rampIconPath,
|
||||||
|
paymentType: paymentType,
|
||||||
|
quoteId: json['quoteId'] as String? ?? '',
|
||||||
|
recommendations: enumRecommendations,
|
||||||
|
provider: ProvidersHelper.getProviderByType(ProviderType.onramper)!,
|
||||||
|
isBuyAction: isBuyAction,
|
||||||
|
limits: Limits(min: minLimit, max: maxLimit),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
factory Quote.fromMoonPayJson(
|
||||||
|
Map<String, dynamic> json, bool isBuyAction, PaymentType paymentType) {
|
||||||
|
final rate = isBuyAction
|
||||||
|
? json['quoteCurrencyPrice'] as double? ?? 0.0
|
||||||
|
: json['baseCurrencyPrice'] as double? ?? 0.0;
|
||||||
|
final fee = _toDouble(json['feeAmount']) ?? 0.0;
|
||||||
|
final networkFee = _toDouble(json['networkFeeAmount']) ?? 0.0;
|
||||||
|
final transactionFee = _toDouble(json['extraFeeAmount']) ?? 0.0;
|
||||||
|
final feeAmount = double.parse((fee + networkFee + transactionFee).toStringAsFixed(2));
|
||||||
|
|
||||||
|
final baseCurrency = json['baseCurrency'] as Map<String, dynamic>?;
|
||||||
|
|
||||||
|
double minLimit = 0.0;
|
||||||
|
double maxLimit = double.infinity;
|
||||||
|
|
||||||
|
if (baseCurrency != null) {
|
||||||
|
minLimit = _toDouble(baseCurrency['minAmount']) ?? minLimit;
|
||||||
|
maxLimit = _toDouble(baseCurrency['maxAmount']) ?? maxLimit;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Quote(
|
||||||
|
rate: rate,
|
||||||
|
feeAmount: feeAmount,
|
||||||
|
networkFee: networkFee,
|
||||||
|
transactionFee: transactionFee,
|
||||||
|
payout: _toDouble(json['quoteCurrencyAmount']) ?? 0.0,
|
||||||
|
paymentType: paymentType,
|
||||||
|
recommendations: [],
|
||||||
|
quoteId: json['signature'] as String? ?? '',
|
||||||
|
provider: ProvidersHelper.getProviderByType(ProviderType.moonpay)!,
|
||||||
|
isBuyAction: isBuyAction,
|
||||||
|
limits: Limits(min: minLimit, max: maxLimit),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
factory Quote.fromDFXJson(
|
||||||
|
Map<String, dynamic> json,
|
||||||
|
bool isBuyAction,
|
||||||
|
PaymentType paymentType,
|
||||||
|
) {
|
||||||
|
final rate = _toDouble(json['exchangeRate']) ?? 0.0;
|
||||||
|
final fees = json['fees'] as Map<String, dynamic>;
|
||||||
|
|
||||||
|
final minVolume = _toDouble(json['minVolume']) ?? 0.0;
|
||||||
|
final maxVolume = _toDouble(json['maxVolume']) ?? double.infinity;
|
||||||
|
|
||||||
|
return Quote(
|
||||||
|
rate: isBuyAction ? rate : 1 / rate,
|
||||||
|
feeAmount: _toDouble(json['feeAmount']) ?? 0.0,
|
||||||
|
networkFee: _toDouble(fees['network']) ?? 0.0,
|
||||||
|
transactionFee: _toDouble(fees['rate']) ?? 0.0,
|
||||||
|
payout: _toDouble(json['payout']) ?? 0.0,
|
||||||
|
paymentType: paymentType,
|
||||||
|
recommendations: [ProviderRecommendation.lowKyc],
|
||||||
|
provider: ProvidersHelper.getProviderByType(ProviderType.dfx)!,
|
||||||
|
isBuyAction: isBuyAction,
|
||||||
|
limits: Limits(min: minVolume, max: maxVolume),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
factory Quote.fromRobinhoodJson(
|
||||||
|
Map<String, dynamic> json, bool isBuyAction, PaymentType paymentType) {
|
||||||
|
final networkFee = json['networkFee'] as Map<String, dynamic>;
|
||||||
|
final processingFee = json['processingFee'] as Map<String, dynamic>;
|
||||||
|
final networkFeeAmount = _toDouble(networkFee['fiatAmount']) ?? 0.0;
|
||||||
|
final transactionFeeAmount = _toDouble(processingFee['fiatAmount']) ?? 0.0;
|
||||||
|
final feeAmount = double.parse((networkFeeAmount + transactionFeeAmount).toStringAsFixed(2));
|
||||||
|
|
||||||
|
return Quote(
|
||||||
|
rate: _toDouble(json['price']) ?? 0.0,
|
||||||
|
feeAmount: feeAmount,
|
||||||
|
networkFee: _toDouble(networkFee['fiatAmount']) ?? 0.0,
|
||||||
|
transactionFee: _toDouble(processingFee['fiatAmount']) ?? 0.0,
|
||||||
|
payout: _toDouble(json['cryptoAmount']) ?? 0.0,
|
||||||
|
paymentType: paymentType,
|
||||||
|
recommendations: [],
|
||||||
|
provider: ProvidersHelper.getProviderByType(ProviderType.robinhood)!,
|
||||||
|
isBuyAction: isBuyAction,
|
||||||
|
limits: Limits(min: 0.0, max: double.infinity),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
factory Quote.fromMeldJson(Map<String, dynamic> json, bool isBuyAction, PaymentType paymentType) {
|
||||||
|
final quotes = json['quotes'][0] as Map<String, dynamic>;
|
||||||
|
return Quote(
|
||||||
|
rate: quotes['exchangeRate'] as double? ?? 0.0,
|
||||||
|
feeAmount: quotes['totalFee'] as double? ?? 0.0,
|
||||||
|
networkFee: quotes['networkFee'] as double? ?? 0.0,
|
||||||
|
transactionFee: quotes['transactionFee'] as double? ?? 0.0,
|
||||||
|
payout: quotes['payout'] as double? ?? 0.0,
|
||||||
|
paymentType: paymentType,
|
||||||
|
recommendations: [],
|
||||||
|
provider: ProvidersHelper.getProviderByType(ProviderType.meld)!,
|
||||||
|
isBuyAction: isBuyAction,
|
||||||
|
limits: Limits(min: 0.0, max: double.infinity),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static double? _toDouble(dynamic value) {
|
||||||
|
if (value is int) {
|
||||||
|
return value.toDouble();
|
||||||
|
} else if (value is double) {
|
||||||
|
return value;
|
||||||
|
} else if (value is String) {
|
||||||
|
return double.tryParse(value);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,13 +1,17 @@
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
import 'dart:developer';
|
||||||
|
|
||||||
import 'package:cake_wallet/buy/buy_provider.dart';
|
import 'package:cake_wallet/buy/buy_provider.dart';
|
||||||
|
import 'package:cake_wallet/buy/buy_quote.dart';
|
||||||
|
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/generated/i18n.dart';
|
||||||
import 'package:cake_wallet/routes.dart';
|
import 'package:cake_wallet/routes.dart';
|
||||||
import 'package:cake_wallet/src/screens/connect_device/connect_device_page.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:cake_wallet/src/widgets/alert_with_one_action.dart';
|
||||||
import 'package:cake_wallet/utils/device_info.dart';
|
|
||||||
import 'package:cake_wallet/utils/show_pop_up.dart';
|
import 'package:cake_wallet/utils/show_pop_up.dart';
|
||||||
import 'package:cake_wallet/view_model/hardware_wallet/ledger_view_model.dart';
|
import 'package:cake_wallet/view_model/hardware_wallet/ledger_view_model.dart';
|
||||||
|
import 'package:cw_core/crypto_currency.dart';
|
||||||
import 'package:cw_core/wallet_base.dart';
|
import 'package:cw_core/wallet_base.dart';
|
||||||
import 'package:cw_core/wallet_type.dart';
|
import 'package:cw_core/wallet_type.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
@ -15,10 +19,12 @@ import 'package:http/http.dart' as http;
|
||||||
import 'package:url_launcher/url_launcher.dart';
|
import 'package:url_launcher/url_launcher.dart';
|
||||||
|
|
||||||
class DFXBuyProvider extends BuyProvider {
|
class DFXBuyProvider extends BuyProvider {
|
||||||
DFXBuyProvider({required WalletBase wallet, bool isTestEnvironment = false, LedgerViewModel? ledgerVM})
|
DFXBuyProvider(
|
||||||
|
{required WalletBase wallet, bool isTestEnvironment = false, LedgerViewModel? ledgerVM})
|
||||||
: super(wallet: wallet, isTestEnvironment: isTestEnvironment, ledgerVM: ledgerVM);
|
: super(wallet: wallet, isTestEnvironment: isTestEnvironment, ledgerVM: ledgerVM);
|
||||||
|
|
||||||
static const _baseUrl = 'api.dfx.swiss';
|
static const _baseUrl = 'api.dfx.swiss';
|
||||||
|
|
||||||
// static const _signMessagePath = '/v1/auth/signMessage';
|
// static const _signMessagePath = '/v1/auth/signMessage';
|
||||||
static const _authPath = '/v1/auth';
|
static const _authPath = '/v1/auth';
|
||||||
static const walletName = 'CakeWallet';
|
static const walletName = 'CakeWallet';
|
||||||
|
@ -35,24 +41,8 @@ class DFXBuyProvider extends BuyProvider {
|
||||||
@override
|
@override
|
||||||
String get darkIcon => 'assets/images/dfx_dark.png';
|
String get darkIcon => 'assets/images/dfx_dark.png';
|
||||||
|
|
||||||
String get assetOut {
|
@override
|
||||||
switch (wallet.type) {
|
bool get isAggregator => false;
|
||||||
case WalletType.bitcoin:
|
|
||||||
return 'BTC';
|
|
||||||
case WalletType.bitcoinCash:
|
|
||||||
return 'BCH';
|
|
||||||
case WalletType.litecoin:
|
|
||||||
return 'LTC';
|
|
||||||
case WalletType.monero:
|
|
||||||
return 'XMR';
|
|
||||||
case WalletType.ethereum:
|
|
||||||
return 'ETH';
|
|
||||||
case WalletType.polygon:
|
|
||||||
return 'MATIC';
|
|
||||||
default:
|
|
||||||
throw Exception("WalletType is not available for DFX ${wallet.type}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
String get blockchain {
|
String get blockchain {
|
||||||
switch (wallet.type) {
|
switch (wallet.type) {
|
||||||
|
@ -60,21 +50,13 @@ class DFXBuyProvider extends BuyProvider {
|
||||||
case WalletType.bitcoinCash:
|
case WalletType.bitcoinCash:
|
||||||
case WalletType.litecoin:
|
case WalletType.litecoin:
|
||||||
return 'Bitcoin';
|
return 'Bitcoin';
|
||||||
case WalletType.monero:
|
|
||||||
return 'Monero';
|
|
||||||
case WalletType.ethereum:
|
|
||||||
return 'Ethereum';
|
|
||||||
case WalletType.polygon:
|
|
||||||
return 'Polygon';
|
|
||||||
default:
|
default:
|
||||||
throw Exception("WalletType is not available for DFX ${wallet.type}");
|
return walletTypeToString(wallet.type);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
String get walletAddress =>
|
|
||||||
wallet.walletAddresses.primaryAddress ?? wallet.walletAddresses.address;
|
|
||||||
|
|
||||||
Future<String> getSignMessage() async =>
|
Future<String> getSignMessage(String walletAddress) async =>
|
||||||
"By_signing_this_message,_you_confirm_that_you_are_the_sole_owner_of_the_provided_Blockchain_address._Your_ID:_$walletAddress";
|
"By_signing_this_message,_you_confirm_that_you_are_the_sole_owner_of_the_provided_Blockchain_address._Your_ID:_$walletAddress";
|
||||||
|
|
||||||
// // Lets keep this just in case, but we can avoid this API Call
|
// // Lets keep this just in case, but we can avoid this API Call
|
||||||
|
@ -92,8 +74,9 @@ class DFXBuyProvider extends BuyProvider {
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
|
|
||||||
Future<String> auth() async {
|
Future<String> auth(String walletAddress) async {
|
||||||
final signMessage = await getSignature(await getSignMessage());
|
final signMessage = await getSignature(
|
||||||
|
await getSignMessage(walletAddress), walletAddress);
|
||||||
|
|
||||||
final requestBody = jsonEncode({
|
final requestBody = jsonEncode({
|
||||||
'wallet': walletName,
|
'wallet': walletName,
|
||||||
|
@ -120,7 +103,7 @@ class DFXBuyProvider extends BuyProvider {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<String> getSignature(String message) async {
|
Future<String> getSignature(String message, String walletAddress) async {
|
||||||
switch (wallet.type) {
|
switch (wallet.type) {
|
||||||
case WalletType.ethereum:
|
case WalletType.ethereum:
|
||||||
case WalletType.polygon:
|
case WalletType.polygon:
|
||||||
|
@ -135,8 +118,178 @@ class DFXBuyProvider extends BuyProvider {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<Map<String, dynamic>> fetchFiatCredentials(String fiatCurrency) async {
|
||||||
|
final url = Uri.https(_baseUrl, '/v1/fiat');
|
||||||
|
|
||||||
|
try {
|
||||||
|
final response = await http.get(url, headers: {'accept': 'application/json'});
|
||||||
|
|
||||||
|
if (response.statusCode == 200) {
|
||||||
|
final data = jsonDecode(response.body) as List<dynamic>;
|
||||||
|
for (final item in data) {
|
||||||
|
if (item['name'] == fiatCurrency) return item as Map<String, dynamic>;
|
||||||
|
}
|
||||||
|
log('DFX does not support fiat: $fiatCurrency');
|
||||||
|
return {};
|
||||||
|
} else {
|
||||||
|
log('DFX Failed to fetch fiat currencies: ${response.statusCode}');
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
print('DFX Error fetching fiat currencies: $e');
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<Map<String, dynamic>> fetchAssetCredential(String assetsName) async {
|
||||||
|
final url = Uri.https(_baseUrl, '/v1/asset', {'blockchains': blockchain});
|
||||||
|
|
||||||
|
try {
|
||||||
|
final response = await http.get(url, headers: {'accept': 'application/json'});
|
||||||
|
|
||||||
|
if (response.statusCode == 200) {
|
||||||
|
final responseData = jsonDecode(response.body);
|
||||||
|
|
||||||
|
if (responseData is List && responseData.isNotEmpty) {
|
||||||
|
return responseData.first as Map<String, dynamic>;
|
||||||
|
} else if (responseData is Map<String, dynamic>) {
|
||||||
|
return responseData;
|
||||||
|
} else {
|
||||||
|
log('DFX: Does not support this asset name : ${blockchain}');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log('DFX: Failed to fetch assets: ${response.statusCode}');
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
log('DFX: Error fetching assets: $e');
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<List<PaymentMethod>> getAvailablePaymentTypes(
|
||||||
|
String fiatCurrency, String cryptoCurrency, bool isBuyAction) async {
|
||||||
|
final List<PaymentMethod> paymentMethods = [];
|
||||||
|
|
||||||
|
if (isBuyAction) {
|
||||||
|
final fiatBuyCredentials = await fetchFiatCredentials(fiatCurrency);
|
||||||
|
if (fiatBuyCredentials.isNotEmpty) {
|
||||||
|
fiatBuyCredentials.forEach((key, value) {
|
||||||
|
if (key == 'limits') {
|
||||||
|
final limits = value as Map<String, dynamic>;
|
||||||
|
limits.forEach((paymentMethodKey, paymentMethodValue) {
|
||||||
|
final min = _toDouble(paymentMethodValue['minVolume']);
|
||||||
|
final max = _toDouble(paymentMethodValue['maxVolume']);
|
||||||
|
if (min != null && max != null && min > 0 && max > 0) {
|
||||||
|
final paymentMethod = PaymentMethod.fromDFX(
|
||||||
|
paymentMethodKey, _getPaymentTypeByString(paymentMethodKey));
|
||||||
|
paymentMethods.add(paymentMethod);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
final assetCredentials = await fetchAssetCredential(cryptoCurrency);
|
||||||
|
if (assetCredentials.isNotEmpty) {
|
||||||
|
if (assetCredentials['sellable'] == true) {
|
||||||
|
final availablePaymentTypes = [
|
||||||
|
PaymentType.bankTransfer,
|
||||||
|
PaymentType.creditCard,
|
||||||
|
PaymentType.sepa
|
||||||
|
];
|
||||||
|
availablePaymentTypes.forEach((element) {
|
||||||
|
final paymentMethod = PaymentMethod.fromDFX(normalizePaymentMethod(element)!, element);
|
||||||
|
paymentMethods.add(paymentMethod);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return paymentMethods;
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> launchProvider(BuildContext context, bool? isBuyAction) async {
|
Future<List<Quote>?> fetchQuote(
|
||||||
|
{required CryptoCurrency cryptoCurrency,
|
||||||
|
required FiatCurrency fiatCurrency,
|
||||||
|
required double amount,
|
||||||
|
required bool isBuyAction,
|
||||||
|
required String walletAddress,
|
||||||
|
PaymentType? paymentType,
|
||||||
|
String? countryCode}) async {
|
||||||
|
/// if buying with any currency other than eur or chf then DFX is not supported
|
||||||
|
|
||||||
|
if (isBuyAction && (fiatCurrency != FiatCurrency.eur && fiatCurrency != FiatCurrency.chf)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
String? paymentMethod;
|
||||||
|
if (paymentType != null && paymentType != PaymentType.all) {
|
||||||
|
paymentMethod = normalizePaymentMethod(paymentType);
|
||||||
|
if (paymentMethod == null) paymentMethod = paymentType.name;
|
||||||
|
} else {
|
||||||
|
paymentMethod = 'Bank';
|
||||||
|
}
|
||||||
|
|
||||||
|
final action = isBuyAction ? 'buy' : 'sell';
|
||||||
|
|
||||||
|
if (isBuyAction && cryptoCurrency != wallet.currency) return null;
|
||||||
|
|
||||||
|
final fiatCredentials = await fetchFiatCredentials(fiatCurrency.name.toString());
|
||||||
|
if (fiatCredentials['id'] == null) return null;
|
||||||
|
|
||||||
|
final assetCredentials = await fetchAssetCredential(cryptoCurrency.title.toString());
|
||||||
|
if (assetCredentials['id'] == null) return null;
|
||||||
|
|
||||||
|
log('DFX: Fetching $action quote: ${isBuyAction ? cryptoCurrency : fiatCurrency} -> ${isBuyAction ? fiatCurrency : cryptoCurrency}, amount: $amount, paymentMethod: $paymentMethod');
|
||||||
|
|
||||||
|
final url = Uri.https(_baseUrl, '/v1/$action/quote');
|
||||||
|
final headers = {'accept': 'application/json', 'Content-Type': 'application/json'};
|
||||||
|
final body = jsonEncode({
|
||||||
|
'currency': {'id': fiatCredentials['id'] as int},
|
||||||
|
'asset': {'id': assetCredentials['id']},
|
||||||
|
'amount': amount,
|
||||||
|
'targetAmount': 0,
|
||||||
|
'paymentMethod': paymentMethod,
|
||||||
|
'discountCode': ''
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
final response = await http.put(url, headers: headers, body: body);
|
||||||
|
final responseData = jsonDecode(response.body);
|
||||||
|
|
||||||
|
if (response.statusCode == 200) {
|
||||||
|
if (responseData is Map<String, dynamic>) {
|
||||||
|
final paymentType = _getPaymentTypeByString(responseData['paymentMethod'] as String?);
|
||||||
|
final quote = Quote.fromDFXJson(responseData, isBuyAction, paymentType);
|
||||||
|
quote.setFiatCurrency = fiatCurrency;
|
||||||
|
quote.setCryptoCurrency = cryptoCurrency;
|
||||||
|
return [quote];
|
||||||
|
} else {
|
||||||
|
print('DFX: Unexpected data type: ${responseData.runtimeType}');
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (responseData is Map<String, dynamic> && responseData.containsKey('message')) {
|
||||||
|
print('DFX Error: ${responseData['message']}');
|
||||||
|
} else {
|
||||||
|
print('DFX Failed to fetch buy quote: ${response.statusCode}');
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
print('DFX Error fetching buy quote: $e');
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void>? launchProvider(
|
||||||
|
{required BuildContext context,
|
||||||
|
required Quote quote,
|
||||||
|
required double amount,
|
||||||
|
required bool isBuyAction,
|
||||||
|
required String cryptoCurrencyAddress,
|
||||||
|
String? countryCode}) async {
|
||||||
if (wallet.isHardwareWallet) {
|
if (wallet.isHardwareWallet) {
|
||||||
if (!ledgerVM!.isConnected) {
|
if (!ledgerVM!.isConnected) {
|
||||||
await Navigator.of(context).pushNamed(Routes.connectDevices,
|
await Navigator.of(context).pushNamed(Routes.connectDevices,
|
||||||
|
@ -152,26 +305,21 @@ class DFXBuyProvider extends BuyProvider {
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final assetOut = this.assetOut;
|
final actionType = isBuyAction ? '/buy' : '/sell';
|
||||||
final blockchain = this.blockchain;
|
|
||||||
final actionType = isBuyAction == true ? '/buy' : '/sell';
|
|
||||||
|
|
||||||
final accessToken = await auth();
|
final accessToken = await auth(cryptoCurrencyAddress);
|
||||||
|
|
||||||
final uri = Uri.https('services.dfx.swiss', actionType, {
|
final uri = Uri.https('services.dfx.swiss', actionType, {
|
||||||
'session': accessToken,
|
'session': accessToken,
|
||||||
'lang': 'en',
|
'lang': 'en',
|
||||||
'asset-out': assetOut,
|
'asset-out': isBuyAction ? quote.cryptoCurrency.toString() : quote.fiatCurrency.toString(),
|
||||||
'blockchain': blockchain,
|
'blockchain': blockchain,
|
||||||
'asset-in': 'EUR',
|
'asset-in': isBuyAction ? quote.fiatCurrency.toString() : quote.cryptoCurrency.toString(),
|
||||||
|
'amount': amount.toString() //TODO: Amount does not work
|
||||||
});
|
});
|
||||||
|
|
||||||
if (await canLaunchUrl(uri)) {
|
if (await canLaunchUrl(uri)) {
|
||||||
if (DeviceInfo.instance.isMobile) {
|
await launchUrl(uri, mode: LaunchMode.externalApplication);
|
||||||
Navigator.of(context).pushNamed(Routes.webViewPage, arguments: [title, uri]);
|
|
||||||
} else {
|
|
||||||
await launchUrl(uri, mode: LaunchMode.externalApplication);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
throw Exception('Could not launch URL');
|
throw Exception('Could not launch URL');
|
||||||
}
|
}
|
||||||
|
@ -187,4 +335,39 @@ class DFXBuyProvider extends BuyProvider {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String? normalizePaymentMethod(PaymentType paymentMethod) {
|
||||||
|
switch (paymentMethod) {
|
||||||
|
case PaymentType.bankTransfer:
|
||||||
|
return 'Bank';
|
||||||
|
case PaymentType.creditCard:
|
||||||
|
return 'Card';
|
||||||
|
case PaymentType.sepa:
|
||||||
|
return 'Instant';
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PaymentType _getPaymentTypeByString(String? paymentMethod) {
|
||||||
|
switch (paymentMethod) {
|
||||||
|
case 'Bank':
|
||||||
|
return PaymentType.bankTransfer;
|
||||||
|
case 'Card':
|
||||||
|
return PaymentType.creditCard;
|
||||||
|
case 'Instant':
|
||||||
|
return PaymentType.sepa;
|
||||||
|
default:
|
||||||
|
return PaymentType.all;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
double? _toDouble(dynamic value) {
|
||||||
|
if (value is int) {
|
||||||
|
return value.toDouble();
|
||||||
|
} else if (value is double) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
262
lib/buy/meld/meld_buy_provider.dart
Normal file
|
@ -0,0 +1,262 @@
|
||||||
|
import 'dart:convert';
|
||||||
|
|
||||||
|
import 'package:cake_wallet/.secrets.g.dart' as secrets;
|
||||||
|
import 'package:cake_wallet/buy/buy_provider.dart';
|
||||||
|
import 'package:cake_wallet/buy/buy_quote.dart';
|
||||||
|
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:cake_wallet/utils/show_pop_up.dart';
|
||||||
|
import 'package:cw_core/crypto_currency.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 {
|
||||||
|
MeldBuyProvider({required WalletBase wallet, bool isTestEnvironment = false})
|
||||||
|
: super(wallet: wallet, isTestEnvironment: isTestEnvironment, ledgerVM: null);
|
||||||
|
|
||||||
|
static const _isProduction = false;
|
||||||
|
|
||||||
|
static const _baseUrl = _isProduction ? 'api.meld.io' : 'api-sb.meld.io';
|
||||||
|
static const _providersProperties = '/service-providers/properties';
|
||||||
|
static const _paymentMethodsPath = '/payment-methods';
|
||||||
|
static const _quotePath = '/payments/crypto/quote';
|
||||||
|
|
||||||
|
static const String sandboxUrl = 'sb.fluidmoney.xyz';
|
||||||
|
static const String productionUrl = 'fluidmoney.xyz';
|
||||||
|
|
||||||
|
static const String _baseWidgetUrl = _isProduction ? productionUrl : sandboxUrl;
|
||||||
|
|
||||||
|
static String get _testApiKey => secrets.meldTestApiKey;
|
||||||
|
|
||||||
|
static String get _testPublicKey => '' ; //secrets.meldTestPublicKey;
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get title => 'Meld';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get providerDescription => 'Meld Buy Provider';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get lightIcon => 'assets/images/meld_logo.svg';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get darkIcon => 'assets/images/meld_logo.svg';
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool get isAggregator => true;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<List<PaymentMethod>> getAvailablePaymentTypes(
|
||||||
|
String fiatCurrency, String cryptoCurrency, bool isBuyAction) async {
|
||||||
|
final params = {'fiatCurrencies': fiatCurrency, 'statuses': 'LIVE,RECENTLY_ADDED,BUILDING'};
|
||||||
|
|
||||||
|
final path = '$_providersProperties$_paymentMethodsPath';
|
||||||
|
final url = Uri.https(_baseUrl, path, params);
|
||||||
|
|
||||||
|
try {
|
||||||
|
final response = await http.get(
|
||||||
|
url,
|
||||||
|
headers: {
|
||||||
|
'Authorization': _isProduction ? '' : _testApiKey,
|
||||||
|
'Meld-Version': '2023-12-19',
|
||||||
|
'accept': 'application/json',
|
||||||
|
'content-type': 'application/json',
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
if (response.statusCode == 200) {
|
||||||
|
final data = jsonDecode(response.body) as List<dynamic>;
|
||||||
|
final paymentMethods =
|
||||||
|
data.map((e) => PaymentMethod.fromMeldJson(e as Map<String, dynamic>)).toList();
|
||||||
|
return paymentMethods;
|
||||||
|
} else {
|
||||||
|
print('Meld: Failed to fetch payment types');
|
||||||
|
return List<PaymentMethod>.empty();
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
print('Meld: Failed to fetch payment types: $e');
|
||||||
|
return List<PaymentMethod>.empty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<List<Quote>?> fetchQuote(
|
||||||
|
{required CryptoCurrency cryptoCurrency,
|
||||||
|
required FiatCurrency fiatCurrency,
|
||||||
|
required double amount,
|
||||||
|
required bool isBuyAction,
|
||||||
|
required String walletAddress,
|
||||||
|
PaymentType? paymentType,
|
||||||
|
String? countryCode}) async {
|
||||||
|
String? paymentMethod;
|
||||||
|
if (paymentType != null && paymentType != PaymentType.all) {
|
||||||
|
paymentMethod = normalizePaymentMethod(paymentType);
|
||||||
|
if (paymentMethod == null) paymentMethod = paymentType.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
log('Meld: Fetching buy quote: ${isBuyAction ? cryptoCurrency : fiatCurrency} -> ${isBuyAction ? fiatCurrency : cryptoCurrency}, amount: $amount');
|
||||||
|
|
||||||
|
final url = Uri.https(_baseUrl, _quotePath);
|
||||||
|
final headers = {
|
||||||
|
'Authorization': _testApiKey,
|
||||||
|
'Meld-Version': '2023-12-19',
|
||||||
|
'accept': 'application/json',
|
||||||
|
'content-type': 'application/json',
|
||||||
|
};
|
||||||
|
final body = jsonEncode({
|
||||||
|
'countryCode': countryCode,
|
||||||
|
'destinationCurrencyCode': isBuyAction ? fiatCurrency.name : cryptoCurrency.title,
|
||||||
|
'sourceAmount': amount,
|
||||||
|
'sourceCurrencyCode': isBuyAction ? cryptoCurrency.title : fiatCurrency.name,
|
||||||
|
if (paymentMethod != null) 'paymentMethod': paymentMethod,
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
final response = await http.post(url, headers: headers, body: body);
|
||||||
|
|
||||||
|
if (response.statusCode == 200) {
|
||||||
|
final data = jsonDecode(response.body) as Map<String, dynamic>;
|
||||||
|
final paymentType = _getPaymentTypeByString(data['paymentMethodType'] as String?);
|
||||||
|
final quote = Quote.fromMeldJson(data, isBuyAction, paymentType);
|
||||||
|
|
||||||
|
quote.setFiatCurrency = fiatCurrency;
|
||||||
|
quote.setCryptoCurrency = cryptoCurrency;
|
||||||
|
|
||||||
|
return [quote];
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
print('Error fetching buy quote: $e');
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void>? launchProvider(
|
||||||
|
{required BuildContext context,
|
||||||
|
required Quote quote,
|
||||||
|
required double amount,
|
||||||
|
required bool isBuyAction,
|
||||||
|
required String cryptoCurrencyAddress,
|
||||||
|
String? countryCode}) async {
|
||||||
|
final actionType = isBuyAction ? 'BUY' : 'SELL';
|
||||||
|
|
||||||
|
final params = {
|
||||||
|
'publicKey': _isProduction ? '' : _testPublicKey,
|
||||||
|
'countryCode': countryCode,
|
||||||
|
//'paymentMethodType': normalizePaymentMethod(paymentMethod.paymentMethodType),
|
||||||
|
'sourceAmount': amount.toString(),
|
||||||
|
'sourceCurrencyCode': quote.fiatCurrency,
|
||||||
|
'destinationCurrencyCode': quote.cryptoCurrency,
|
||||||
|
'walletAddress': cryptoCurrencyAddress,
|
||||||
|
'transactionType': actionType
|
||||||
|
};
|
||||||
|
|
||||||
|
final uri = Uri.https(_baseWidgetUrl, '', params);
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (await canLaunchUrl(uri)) {
|
||||||
|
await launchUrl(uri, mode: LaunchMode.externalApplication);
|
||||||
|
} else {
|
||||||
|
throw Exception('Could not launch URL');
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
await showPopUp<void>(
|
||||||
|
context: context,
|
||||||
|
builder: (BuildContext context) {
|
||||||
|
return AlertWithOneAction(
|
||||||
|
alertTitle: "Meld",
|
||||||
|
alertContent: S.of(context).buy_provider_unavailable + ': $e',
|
||||||
|
buttonText: S.of(context).ok,
|
||||||
|
buttonAction: () => Navigator.of(context).pop());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String? normalizePaymentMethod(PaymentType paymentType) {
|
||||||
|
switch (paymentType) {
|
||||||
|
case PaymentType.creditCard:
|
||||||
|
return 'CREDIT_DEBIT_CARD';
|
||||||
|
case PaymentType.applePay:
|
||||||
|
return 'APPLE_PAY';
|
||||||
|
case PaymentType.googlePay:
|
||||||
|
return 'GOOGLE_PAY';
|
||||||
|
case PaymentType.neteller:
|
||||||
|
return 'NETELLER';
|
||||||
|
case PaymentType.skrill:
|
||||||
|
return 'SKRILL';
|
||||||
|
case PaymentType.sepa:
|
||||||
|
return 'SEPA';
|
||||||
|
case PaymentType.sepaInstant:
|
||||||
|
return 'SEPA_INSTANT';
|
||||||
|
case PaymentType.ach:
|
||||||
|
return 'ACH';
|
||||||
|
case PaymentType.achInstant:
|
||||||
|
return 'INSTANT_ACH';
|
||||||
|
case PaymentType.Khipu:
|
||||||
|
return 'KHIPU';
|
||||||
|
case PaymentType.ovo:
|
||||||
|
return 'OVO';
|
||||||
|
case PaymentType.zaloPay:
|
||||||
|
return 'ZALOPAY';
|
||||||
|
case PaymentType.zaloBankTransfer:
|
||||||
|
return 'ZA_BANK_TRANSFER';
|
||||||
|
case PaymentType.gcash:
|
||||||
|
return 'GCASH';
|
||||||
|
case PaymentType.imps:
|
||||||
|
return 'IMPS';
|
||||||
|
case PaymentType.dana:
|
||||||
|
return 'DANA';
|
||||||
|
case PaymentType.ideal:
|
||||||
|
return 'IDEAL';
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PaymentType _getPaymentTypeByString(String? paymentMethod) {
|
||||||
|
switch (paymentMethod?.toUpperCase()) {
|
||||||
|
case 'CREDIT_DEBIT_CARD':
|
||||||
|
return PaymentType.creditCard;
|
||||||
|
case 'APPLE_PAY':
|
||||||
|
return PaymentType.applePay;
|
||||||
|
case 'GOOGLE_PAY':
|
||||||
|
return PaymentType.googlePay;
|
||||||
|
case 'NETELLER':
|
||||||
|
return PaymentType.neteller;
|
||||||
|
case 'SKRILL':
|
||||||
|
return PaymentType.skrill;
|
||||||
|
case 'SEPA':
|
||||||
|
return PaymentType.sepa;
|
||||||
|
case 'SEPA_INSTANT':
|
||||||
|
return PaymentType.sepaInstant;
|
||||||
|
case 'ACH':
|
||||||
|
return PaymentType.ach;
|
||||||
|
case 'INSTANT_ACH':
|
||||||
|
return PaymentType.achInstant;
|
||||||
|
case 'KHIPU':
|
||||||
|
return PaymentType.Khipu;
|
||||||
|
case 'OVO':
|
||||||
|
return PaymentType.ovo;
|
||||||
|
case 'ZALOPAY':
|
||||||
|
return PaymentType.zaloPay;
|
||||||
|
case 'ZA_BANK_TRANSFER':
|
||||||
|
return PaymentType.zaloBankTransfer;
|
||||||
|
case 'GCASH':
|
||||||
|
return PaymentType.gcash;
|
||||||
|
case 'IMPS':
|
||||||
|
return PaymentType.imps;
|
||||||
|
case 'DANA':
|
||||||
|
return PaymentType.dana;
|
||||||
|
case 'IDEAL':
|
||||||
|
return PaymentType.ideal;
|
||||||
|
default:
|
||||||
|
return PaymentType.all;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,19 +1,20 @@
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
import 'dart:developer';
|
||||||
|
|
||||||
import 'package:cake_wallet/.secrets.g.dart' as secrets;
|
import 'package:cake_wallet/.secrets.g.dart' as secrets;
|
||||||
import 'package:cake_wallet/buy/buy_amount.dart';
|
|
||||||
import 'package:cake_wallet/buy/buy_exception.dart';
|
import 'package:cake_wallet/buy/buy_exception.dart';
|
||||||
import 'package:cake_wallet/buy/buy_provider.dart';
|
import 'package:cake_wallet/buy/buy_provider.dart';
|
||||||
import 'package:cake_wallet/buy/buy_provider_description.dart';
|
import 'package:cake_wallet/buy/buy_provider_description.dart';
|
||||||
|
import 'package:cake_wallet/buy/buy_quote.dart';
|
||||||
import 'package:cake_wallet/buy/order.dart';
|
import 'package:cake_wallet/buy/order.dart';
|
||||||
|
import 'package:cake_wallet/buy/payment_method.dart';
|
||||||
|
import 'package:cake_wallet/entities/fiat_currency.dart';
|
||||||
import 'package:cake_wallet/exchange/trade_state.dart';
|
import 'package:cake_wallet/exchange/trade_state.dart';
|
||||||
import 'package:cake_wallet/generated/i18n.dart';
|
import 'package:cake_wallet/generated/i18n.dart';
|
||||||
import 'package:cake_wallet/palette.dart';
|
import 'package:cake_wallet/palette.dart';
|
||||||
import 'package:cake_wallet/routes.dart';
|
|
||||||
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
|
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
|
||||||
import 'package:cake_wallet/store/settings_store.dart';
|
import 'package:cake_wallet/store/settings_store.dart';
|
||||||
import 'package:cake_wallet/themes/theme_base.dart';
|
import 'package:cake_wallet/themes/theme_base.dart';
|
||||||
import 'package:cake_wallet/utils/device_info.dart';
|
|
||||||
import 'package:cw_core/crypto_currency.dart';
|
import 'package:cw_core/crypto_currency.dart';
|
||||||
import 'package:cw_core/wallet_base.dart';
|
import 'package:cw_core/wallet_base.dart';
|
||||||
import 'package:cw_core/wallet_type.dart';
|
import 'package:cw_core/wallet_type.dart';
|
||||||
|
@ -39,6 +40,15 @@ class MoonPayProvider extends BuyProvider {
|
||||||
static const _baseBuyProductUrl = 'buy.moonpay.com';
|
static const _baseBuyProductUrl = 'buy.moonpay.com';
|
||||||
static const _cIdBaseUrl = 'exchange-helper.cakewallet.com';
|
static const _cIdBaseUrl = 'exchange-helper.cakewallet.com';
|
||||||
static const _apiUrl = 'https://api.moonpay.com';
|
static const _apiUrl = 'https://api.moonpay.com';
|
||||||
|
static const _baseUrl = 'api.moonpay.com';
|
||||||
|
static const _currenciesPath = '/v3/currencies';
|
||||||
|
static const _buyQuote = '/buy_quote';
|
||||||
|
static const _sellQuote = '/sell_quote';
|
||||||
|
|
||||||
|
static const _transactionsSuffix = '/v1/transactions';
|
||||||
|
|
||||||
|
final String baseBuyUrl;
|
||||||
|
final String baseSellUrl;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get providerDescription =>
|
String get providerDescription =>
|
||||||
|
@ -53,6 +63,17 @@ class MoonPayProvider extends BuyProvider {
|
||||||
@override
|
@override
|
||||||
String get darkIcon => 'assets/images/moonpay_dark.png';
|
String get darkIcon => 'assets/images/moonpay_dark.png';
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool get isAggregator => false;
|
||||||
|
|
||||||
|
static String get _apiKey => secrets.moonPayApiKey;
|
||||||
|
|
||||||
|
String get currencyCode => walletTypeToCryptoCurrency(wallet.type).title.toLowerCase();
|
||||||
|
|
||||||
|
String get trackUrl => baseBuyUrl + '/transaction_receipt?transactionId=';
|
||||||
|
|
||||||
|
static String get _exchangeHelperApiKey => secrets.exchangeHelperApiKey;
|
||||||
|
|
||||||
static String themeToMoonPayTheme(ThemeBase theme) {
|
static String themeToMoonPayTheme(ThemeBase theme) {
|
||||||
switch (theme.type) {
|
switch (theme.type) {
|
||||||
case ThemeType.bright:
|
case ThemeType.bright:
|
||||||
|
@ -63,28 +84,12 @@ class MoonPayProvider extends BuyProvider {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static String get _apiKey => secrets.moonPayApiKey;
|
|
||||||
|
|
||||||
final String baseBuyUrl;
|
|
||||||
final String baseSellUrl;
|
|
||||||
|
|
||||||
String get currencyCode => walletTypeToCryptoCurrency(wallet.type).title.toLowerCase();
|
|
||||||
|
|
||||||
String get trackUrl => baseBuyUrl + '/transaction_receipt?transactionId=';
|
|
||||||
|
|
||||||
static String get _exchangeHelperApiKey => secrets.exchangeHelperApiKey;
|
|
||||||
|
|
||||||
Future<String> getMoonpaySignature(String query) async {
|
Future<String> getMoonpaySignature(String query) async {
|
||||||
final uri = Uri.https(_cIdBaseUrl, "/api/moonpay");
|
final uri = Uri.https(_cIdBaseUrl, "/api/moonpay");
|
||||||
|
|
||||||
final response = await post(
|
final response = await post(uri,
|
||||||
uri,
|
headers: {'Content-Type': 'application/json', 'x-api-key': _exchangeHelperApiKey},
|
||||||
headers: {
|
body: json.encode({'query': query}));
|
||||||
'Content-Type': 'application/json',
|
|
||||||
'x-api-key': _exchangeHelperApiKey,
|
|
||||||
},
|
|
||||||
body: json.encode({'query': query}),
|
|
||||||
);
|
|
||||||
|
|
||||||
if (response.statusCode == 200) {
|
if (response.statusCode == 200) {
|
||||||
return (jsonDecode(response.body) as Map<String, dynamic>)['signature'] as String;
|
return (jsonDecode(response.body) as Map<String, dynamic>)['signature'] as String;
|
||||||
|
@ -94,85 +99,195 @@ class MoonPayProvider extends BuyProvider {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<Uri> requestSellMoonPayUrl({
|
Future<Map<String, dynamic>> fetchFiatCredentials(
|
||||||
required CryptoCurrency currency,
|
String fiatCurrency, String cryptocurrency, String? paymentMethod) async {
|
||||||
required String refundWalletAddress,
|
final params = {'baseCurrencyCode': fiatCurrency.toLowerCase(), 'apiKey': _apiKey};
|
||||||
required SettingsStore settingsStore,
|
|
||||||
}) async {
|
|
||||||
final params = {
|
|
||||||
'theme': themeToMoonPayTheme(settingsStore.currentTheme),
|
|
||||||
'language': settingsStore.languageCode,
|
|
||||||
'colorCode': settingsStore.currentTheme.type == ThemeType.dark
|
|
||||||
? '#${Palette.blueCraiola.value.toRadixString(16).substring(2, 8)}'
|
|
||||||
: '#${Palette.moderateSlateBlue.value.toRadixString(16).substring(2, 8)}',
|
|
||||||
'defaultCurrencyCode': _normalizeCurrency(currency),
|
|
||||||
'refundWalletAddress': refundWalletAddress,
|
|
||||||
};
|
|
||||||
|
|
||||||
if (_apiKey.isNotEmpty) {
|
if (paymentMethod != null) params['paymentMethod'] = paymentMethod;
|
||||||
params['apiKey'] = _apiKey;
|
|
||||||
|
final path = '$_currenciesPath/${cryptocurrency.toLowerCase()}/limits';
|
||||||
|
final url = Uri.https(_baseUrl, path, params);
|
||||||
|
|
||||||
|
try {
|
||||||
|
final response = await get(url, headers: {'accept': 'application/json'});
|
||||||
|
if (response.statusCode == 200) {
|
||||||
|
return jsonDecode(response.body) as Map<String, dynamic>;
|
||||||
|
} else {
|
||||||
|
print('MoonPay does not support fiat: $fiatCurrency');
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
print('MoonPay Error fetching fiat currencies: $e');
|
||||||
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
final originalUri = Uri.https(
|
|
||||||
baseSellUrl,
|
|
||||||
'',
|
|
||||||
params,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (isTestEnvironment) {
|
|
||||||
return originalUri;
|
|
||||||
}
|
|
||||||
|
|
||||||
final signature = await getMoonpaySignature('?${originalUri.query}');
|
|
||||||
|
|
||||||
final query = Map<String, dynamic>.from(originalUri.queryParameters);
|
|
||||||
query['signature'] = signature;
|
|
||||||
final signedUri = originalUri.replace(queryParameters: query);
|
|
||||||
return signedUri;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// BUY:
|
Future<List<PaymentMethod>> getAvailablePaymentTypes(
|
||||||
static const _currenciesSuffix = '/v3/currencies';
|
String fiatCurrency, String cryptoCurrency, bool isBuyAction) async {
|
||||||
static const _quoteSuffix = '/buy_quote';
|
final List<PaymentMethod> paymentMethods = [];
|
||||||
static const _transactionsSuffix = '/v1/transactions';
|
|
||||||
static const _ipAddressSuffix = '/v4/ip_address';
|
if (isBuyAction) {
|
||||||
|
final fiatBuyCredentials = await fetchFiatCredentials(fiatCurrency, cryptoCurrency, null);
|
||||||
|
if (fiatBuyCredentials.isNotEmpty) {
|
||||||
|
final paymentMethod = fiatBuyCredentials['paymentMethod'] as String?;
|
||||||
|
paymentMethods.add(PaymentMethod.fromMoonPayJson(
|
||||||
|
fiatBuyCredentials, _getPaymentTypeByString(paymentMethod)));
|
||||||
|
return paymentMethods;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return paymentMethods;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<List<Quote>?> fetchQuote(
|
||||||
|
{required CryptoCurrency cryptoCurrency,
|
||||||
|
required FiatCurrency fiatCurrency,
|
||||||
|
required double amount,
|
||||||
|
required bool isBuyAction,
|
||||||
|
required String walletAddress,
|
||||||
|
PaymentType? paymentType,
|
||||||
|
String? countryCode}) async {
|
||||||
|
String? paymentMethod;
|
||||||
|
|
||||||
|
if (paymentType != null && paymentType != PaymentType.all) {
|
||||||
|
paymentMethod = normalizePaymentMethod(paymentType);
|
||||||
|
if (paymentMethod == null) paymentMethod = paymentType.name;
|
||||||
|
} else {
|
||||||
|
paymentMethod = 'credit_debit_card';
|
||||||
|
}
|
||||||
|
|
||||||
|
final action = isBuyAction ? 'buy' : 'sell';
|
||||||
|
|
||||||
|
final formattedCryptoCurrency = _normalizeCurrency(cryptoCurrency);
|
||||||
|
final baseCurrencyCode =
|
||||||
|
isBuyAction ? fiatCurrency.name.toLowerCase() : cryptoCurrency.title.toLowerCase();
|
||||||
|
|
||||||
Future<Uri> requestBuyMoonPayUrl({
|
|
||||||
required CryptoCurrency currency,
|
|
||||||
required SettingsStore settingsStore,
|
|
||||||
required String walletAddress,
|
|
||||||
String? amount,
|
|
||||||
}) async {
|
|
||||||
final params = {
|
final params = {
|
||||||
'theme': themeToMoonPayTheme(settingsStore.currentTheme),
|
'baseCurrencyCode': baseCurrencyCode,
|
||||||
'language': settingsStore.languageCode,
|
'baseCurrencyAmount': amount.toString(),
|
||||||
'colorCode': settingsStore.currentTheme.type == ThemeType.dark
|
'amount': amount.toString(),
|
||||||
|
'paymentMethod': paymentMethod,
|
||||||
|
'areFeesIncluded': 'false',
|
||||||
|
'apiKey': _apiKey
|
||||||
|
};
|
||||||
|
|
||||||
|
log('MoonPay: Fetching $action quote: ${isBuyAction ? formattedCryptoCurrency : fiatCurrency.name.toLowerCase()} -> ${isBuyAction ? baseCurrencyCode : formattedCryptoCurrency}, amount: $amount, paymentMethod: $paymentMethod');
|
||||||
|
|
||||||
|
final quotePath = isBuyAction ? _buyQuote : _sellQuote;
|
||||||
|
|
||||||
|
final path = '$_currenciesPath/$formattedCryptoCurrency$quotePath';
|
||||||
|
final url = Uri.https(_baseUrl, path, params);
|
||||||
|
try {
|
||||||
|
final response = await get(url);
|
||||||
|
|
||||||
|
if (response.statusCode == 200) {
|
||||||
|
final data = jsonDecode(response.body) as Map<String, dynamic>;
|
||||||
|
|
||||||
|
// Check if the response is for the correct fiat currency
|
||||||
|
if (isBuyAction) {
|
||||||
|
final fiatCurrencyCode = data['baseCurrencyCode'] as String?;
|
||||||
|
if (fiatCurrencyCode == null || fiatCurrencyCode != fiatCurrency.name.toLowerCase())
|
||||||
|
return null;
|
||||||
|
} else {
|
||||||
|
final quoteCurrency = data['quoteCurrency'] as Map<String, dynamic>?;
|
||||||
|
if (quoteCurrency == null || quoteCurrency['code'] != fiatCurrency.name.toLowerCase())
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
final paymentMethods = data['paymentMethod'] as String?;
|
||||||
|
final quote =
|
||||||
|
Quote.fromMoonPayJson(data, isBuyAction, _getPaymentTypeByString(paymentMethods));
|
||||||
|
|
||||||
|
quote.setFiatCurrency = fiatCurrency;
|
||||||
|
quote.setCryptoCurrency = cryptoCurrency;
|
||||||
|
|
||||||
|
return [quote];
|
||||||
|
} else {
|
||||||
|
print('Moon Pay: Error fetching buy quote: ');
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
print('Moon Pay: Error fetching buy quote: $e');
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void>? launchProvider(
|
||||||
|
{required BuildContext context,
|
||||||
|
required Quote quote,
|
||||||
|
required double amount,
|
||||||
|
required bool isBuyAction,
|
||||||
|
required String cryptoCurrencyAddress,
|
||||||
|
String? countryCode}) async {
|
||||||
|
|
||||||
|
final Map<String, String> params = {
|
||||||
|
'theme': themeToMoonPayTheme(_settingsStore.currentTheme),
|
||||||
|
'language': _settingsStore.languageCode,
|
||||||
|
'colorCode': _settingsStore.currentTheme.type == ThemeType.dark
|
||||||
? '#${Palette.blueCraiola.value.toRadixString(16).substring(2, 8)}'
|
? '#${Palette.blueCraiola.value.toRadixString(16).substring(2, 8)}'
|
||||||
: '#${Palette.moderateSlateBlue.value.toRadixString(16).substring(2, 8)}',
|
: '#${Palette.moderateSlateBlue.value.toRadixString(16).substring(2, 8)}',
|
||||||
'baseCurrencyCode': settingsStore.fiatCurrency.title,
|
'baseCurrencyCode': isBuyAction ? quote.fiatCurrency.name : quote.cryptoCurrency.name,
|
||||||
'baseCurrencyAmount': amount ?? '0',
|
'baseCurrencyAmount': amount.toString(),
|
||||||
'currencyCode': _normalizeCurrency(currency),
|
'walletAddress': cryptoCurrencyAddress,
|
||||||
'walletAddress': walletAddress,
|
|
||||||
'lockAmount': 'false',
|
'lockAmount': 'false',
|
||||||
'showAllCurrencies': 'false',
|
'showAllCurrencies': 'false',
|
||||||
'showWalletAddressForm': 'false',
|
'showWalletAddressForm': 'false',
|
||||||
'enabledPaymentMethods':
|
if (isBuyAction)
|
||||||
'credit_debit_card,apple_pay,google_pay,samsung_pay,sepa_bank_transfer,gbp_bank_transfer,gbp_open_banking_payment',
|
'enabledPaymentMethods': normalizePaymentMethod(quote.paymentType) ??
|
||||||
|
'credit_debit_card,apple_pay,google_pay,samsung_pay,sepa_bank_transfer,gbp_bank_transfer,gbp_open_banking_payment',
|
||||||
|
if (!isBuyAction) 'refundWalletAddress': cryptoCurrencyAddress
|
||||||
};
|
};
|
||||||
|
|
||||||
if (_apiKey.isNotEmpty) {
|
if (isBuyAction) params['currencyCode'] = quote.cryptoCurrency.name;
|
||||||
params['apiKey'] = _apiKey;
|
if (!isBuyAction) params['quoteCurrencyCode'] = quote.cryptoCurrency.name;
|
||||||
}
|
|
||||||
|
|
||||||
final originalUri = Uri.https(
|
try {
|
||||||
baseBuyUrl,
|
{
|
||||||
'',
|
final uri = await requestMoonPayUrl(
|
||||||
params,
|
walletAddress: cryptoCurrencyAddress,
|
||||||
);
|
settingsStore: _settingsStore,
|
||||||
|
isBuyAction: isBuyAction,
|
||||||
|
amount: amount.toString(),
|
||||||
|
params: params);
|
||||||
|
|
||||||
if (isTestEnvironment) {
|
if (await canLaunchUrl(uri)) {
|
||||||
return originalUri;
|
await launchUrl(uri, mode: LaunchMode.externalApplication);
|
||||||
|
} else {
|
||||||
|
throw Exception('Could not launch URL');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
if (context.mounted) {
|
||||||
|
await showDialog<void>(
|
||||||
|
context: context,
|
||||||
|
builder: (BuildContext context) {
|
||||||
|
return AlertWithOneAction(
|
||||||
|
alertTitle: 'MoonPay',
|
||||||
|
alertContent: 'The MoonPay service is currently unavailable: $e',
|
||||||
|
buttonText: S.of(context).ok,
|
||||||
|
buttonAction: () => Navigator.of(context).pop(),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<Uri> requestMoonPayUrl({
|
||||||
|
required String walletAddress,
|
||||||
|
required SettingsStore settingsStore,
|
||||||
|
required bool isBuyAction,
|
||||||
|
required Map<String, String> params,
|
||||||
|
String? amount,
|
||||||
|
}) async {
|
||||||
|
if (_apiKey.isNotEmpty) params['apiKey'] = _apiKey;
|
||||||
|
|
||||||
|
final baseUrl = isBuyAction ? baseBuyUrl : baseSellUrl;
|
||||||
|
final originalUri = Uri.https(baseUrl, '', params);
|
||||||
|
|
||||||
|
if (isTestEnvironment) return originalUri;
|
||||||
|
|
||||||
final signature = await getMoonpaySignature('?${originalUri.query}');
|
final signature = await getMoonpaySignature('?${originalUri.query}');
|
||||||
final query = Map<String, dynamic>.from(originalUri.queryParameters);
|
final query = Map<String, dynamic>.from(originalUri.queryParameters);
|
||||||
|
@ -181,33 +296,6 @@ class MoonPayProvider extends BuyProvider {
|
||||||
return signedUri;
|
return signedUri;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<BuyAmount> calculateAmount(String amount, String sourceCurrency) async {
|
|
||||||
final url = _apiUrl +
|
|
||||||
_currenciesSuffix +
|
|
||||||
'/$currencyCode' +
|
|
||||||
_quoteSuffix +
|
|
||||||
'/?apiKey=' +
|
|
||||||
_apiKey +
|
|
||||||
'&baseCurrencyAmount=' +
|
|
||||||
amount +
|
|
||||||
'&baseCurrencyCode=' +
|
|
||||||
sourceCurrency.toLowerCase();
|
|
||||||
final uri = Uri.parse(url);
|
|
||||||
final response = await get(uri);
|
|
||||||
|
|
||||||
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['totalAmount'] as double;
|
|
||||||
final destAmount = responseJSON['quoteCurrencyAmount'] as double;
|
|
||||||
final minSourceAmount = responseJSON['baseCurrency']['minAmount'] as int;
|
|
||||||
|
|
||||||
return BuyAmount(
|
|
||||||
sourceAmount: sourceAmount, destAmount: destAmount, minAmount: minSourceAmount);
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<Order> findOrderById(String id) async {
|
Future<Order> findOrderById(String id) async {
|
||||||
final url = _apiUrl + _transactionsSuffix + '/$id' + '?apiKey=' + _apiKey;
|
final url = _apiUrl + _transactionsSuffix + '/$id' + '?apiKey=' + _apiKey;
|
||||||
final uri = Uri.parse(url);
|
final uri = Uri.parse(url);
|
||||||
|
@ -235,74 +323,83 @@ class MoonPayProvider extends BuyProvider {
|
||||||
walletId: wallet.id);
|
walletId: wallet.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
static Future<bool> onEnabled() async {
|
|
||||||
final url = _apiUrl + _ipAddressSuffix + '?apiKey=' + _apiKey;
|
|
||||||
var isBuyEnable = false;
|
|
||||||
final uri = Uri.parse(url);
|
|
||||||
final response = await get(uri);
|
|
||||||
|
|
||||||
try {
|
|
||||||
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
|
|
||||||
isBuyEnable = responseJSON['isBuyAllowed'] as bool;
|
|
||||||
} catch (e) {
|
|
||||||
isBuyEnable = false;
|
|
||||||
print(e.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
return isBuyEnable;
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<void> launchProvider(BuildContext context, bool? isBuyAction) async {
|
|
||||||
try {
|
|
||||||
late final Uri uri;
|
|
||||||
if (isBuyAction ?? true) {
|
|
||||||
uri = await requestBuyMoonPayUrl(
|
|
||||||
currency: wallet.currency,
|
|
||||||
walletAddress: wallet.walletAddresses.address,
|
|
||||||
settingsStore: _settingsStore,
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
uri = await requestSellMoonPayUrl(
|
|
||||||
currency: wallet.currency,
|
|
||||||
refundWalletAddress: wallet.walletAddresses.address,
|
|
||||||
settingsStore: _settingsStore,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (await canLaunchUrl(uri)) {
|
|
||||||
if (DeviceInfo.instance.isMobile) {
|
|
||||||
Navigator.of(context).pushNamed(Routes.webViewPage, arguments: ['MoonPay', uri]);
|
|
||||||
} else {
|
|
||||||
await launchUrl(uri, mode: LaunchMode.externalApplication);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
throw Exception('Could not launch URL');
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
if (context.mounted) {
|
|
||||||
await showDialog<void>(
|
|
||||||
context: context,
|
|
||||||
builder: (BuildContext context) {
|
|
||||||
return AlertWithOneAction(
|
|
||||||
alertTitle: 'MoonPay',
|
|
||||||
alertContent: 'The MoonPay service is currently unavailable: $e',
|
|
||||||
buttonText: S.of(context).ok,
|
|
||||||
buttonAction: () => Navigator.of(context).pop(),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
String _normalizeCurrency(CryptoCurrency currency) {
|
String _normalizeCurrency(CryptoCurrency currency) {
|
||||||
if (currency == CryptoCurrency.maticpoly) {
|
if (currency.tag == 'POLY') {
|
||||||
return "POL_POLYGON";
|
return '${currency.title.toLowerCase()}_polygon';
|
||||||
} else if (currency == CryptoCurrency.matic) {
|
}
|
||||||
return "POL";
|
|
||||||
|
if (currency.tag == 'TRX') {
|
||||||
|
return '${currency.title.toLowerCase()}_trx';
|
||||||
}
|
}
|
||||||
|
|
||||||
return currency.toString().toLowerCase();
|
return currency.toString().toLowerCase();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String? normalizePaymentMethod(PaymentType paymentMethod) {
|
||||||
|
switch (paymentMethod) {
|
||||||
|
case PaymentType.creditCard:
|
||||||
|
return 'credit_debit_card';
|
||||||
|
case PaymentType.debitCard:
|
||||||
|
return 'credit_debit_card';
|
||||||
|
case PaymentType.ach:
|
||||||
|
return 'ach_bank_transfer';
|
||||||
|
case PaymentType.applePay:
|
||||||
|
return 'apple_pay';
|
||||||
|
case PaymentType.googlePay:
|
||||||
|
return 'google_pay';
|
||||||
|
case PaymentType.sepa:
|
||||||
|
return 'sepa_bank_transfer';
|
||||||
|
case PaymentType.paypal:
|
||||||
|
return 'paypal';
|
||||||
|
case PaymentType.sepaOpenBankingPayment:
|
||||||
|
return 'sepa_open_banking_payment';
|
||||||
|
case PaymentType.gbpOpenBankingPayment:
|
||||||
|
return 'gbp_open_banking_payment';
|
||||||
|
case PaymentType.lowCostAch:
|
||||||
|
return 'low_cost_ach';
|
||||||
|
case PaymentType.mobileWallet:
|
||||||
|
return 'mobile_wallet';
|
||||||
|
case PaymentType.pixInstantPayment:
|
||||||
|
return 'pix_instant_payment';
|
||||||
|
case PaymentType.yellowCardBankTransfer:
|
||||||
|
return 'yellow_card_bank_transfer';
|
||||||
|
case PaymentType.fiatBalance:
|
||||||
|
return 'fiat_balance';
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PaymentType _getPaymentTypeByString(String? paymentMethod) {
|
||||||
|
switch (paymentMethod) {
|
||||||
|
case 'ach_bank_transfer':
|
||||||
|
return PaymentType.ach;
|
||||||
|
case 'apple_pay':
|
||||||
|
return PaymentType.applePay;
|
||||||
|
case 'credit_debit_card':
|
||||||
|
return PaymentType.creditCard;
|
||||||
|
case 'fiat_balance':
|
||||||
|
return PaymentType.fiatBalance;
|
||||||
|
case 'gbp_open_banking_payment':
|
||||||
|
return PaymentType.gbpOpenBankingPayment;
|
||||||
|
case 'google_pay':
|
||||||
|
return PaymentType.googlePay;
|
||||||
|
case 'low_cost_ach':
|
||||||
|
return PaymentType.lowCostAch;
|
||||||
|
case 'mobile_wallet':
|
||||||
|
return PaymentType.mobileWallet;
|
||||||
|
case 'paypal':
|
||||||
|
return PaymentType.paypal;
|
||||||
|
case 'pix_instant_payment':
|
||||||
|
return PaymentType.pixInstantPayment;
|
||||||
|
case 'sepa_bank_transfer':
|
||||||
|
return PaymentType.sepa;
|
||||||
|
case 'sepa_open_banking_payment':
|
||||||
|
return PaymentType.sepaOpenBankingPayment;
|
||||||
|
case 'yellow_card_bank_transfer':
|
||||||
|
return PaymentType.yellowCardBankTransfer;
|
||||||
|
default:
|
||||||
|
return PaymentType.all;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,19 @@
|
||||||
|
import 'dart:convert';
|
||||||
|
import 'dart:developer';
|
||||||
|
|
||||||
import 'package:cake_wallet/.secrets.g.dart' as secrets;
|
import 'package:cake_wallet/.secrets.g.dart' as secrets;
|
||||||
import 'package:cake_wallet/buy/buy_provider.dart';
|
import 'package:cake_wallet/buy/buy_provider.dart';
|
||||||
|
import 'package:cake_wallet/buy/buy_quote.dart';
|
||||||
|
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/generated/i18n.dart';
|
||||||
import 'package:cake_wallet/routes.dart';
|
|
||||||
import 'package:cake_wallet/store/settings_store.dart';
|
import 'package:cake_wallet/store/settings_store.dart';
|
||||||
import 'package:cake_wallet/themes/extensions/cake_text_theme.dart';
|
import 'package:cake_wallet/themes/extensions/cake_text_theme.dart';
|
||||||
import 'package:cake_wallet/utils/device_info.dart';
|
|
||||||
import 'package:cw_core/crypto_currency.dart';
|
import 'package:cw_core/crypto_currency.dart';
|
||||||
|
import 'package:cw_core/currency.dart';
|
||||||
import 'package:cw_core/wallet_base.dart';
|
import 'package:cw_core/wallet_base.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:http/http.dart' as http;
|
||||||
import 'package:url_launcher/url_launcher.dart';
|
import 'package:url_launcher/url_launcher.dart';
|
||||||
|
|
||||||
class OnRamperBuyProvider extends BuyProvider {
|
class OnRamperBuyProvider extends BuyProvider {
|
||||||
|
@ -16,9 +22,15 @@ class OnRamperBuyProvider extends BuyProvider {
|
||||||
: super(wallet: wallet, isTestEnvironment: isTestEnvironment, ledgerVM: null);
|
: super(wallet: wallet, isTestEnvironment: isTestEnvironment, ledgerVM: null);
|
||||||
|
|
||||||
static const _baseUrl = 'buy.onramper.com';
|
static const _baseUrl = 'buy.onramper.com';
|
||||||
|
static const _baseApiUrl = 'api.onramper.com';
|
||||||
|
static const quotes = '/quotes';
|
||||||
|
static const paymentTypes = '/payment-types';
|
||||||
|
static const supported = '/supported';
|
||||||
|
|
||||||
final SettingsStore _settingsStore;
|
final SettingsStore _settingsStore;
|
||||||
|
|
||||||
|
String get _apiKey => secrets.onramperApiKey;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get title => 'Onramper';
|
String get title => 'Onramper';
|
||||||
|
|
||||||
|
@ -31,74 +43,327 @@ class OnRamperBuyProvider extends BuyProvider {
|
||||||
@override
|
@override
|
||||||
String get darkIcon => 'assets/images/onramper_dark.png';
|
String get darkIcon => 'assets/images/onramper_dark.png';
|
||||||
|
|
||||||
String get _apiKey => secrets.onramperApiKey;
|
@override
|
||||||
|
bool get isAggregator => true;
|
||||||
|
|
||||||
String get _normalizeCryptoCurrency {
|
Future<List<PaymentMethod>> getAvailablePaymentTypes(
|
||||||
switch (wallet.currency) {
|
String fiatCurrency, String cryptoCurrency, bool isBuyAction) async {
|
||||||
case CryptoCurrency.ltc:
|
final params = {
|
||||||
return "LTC_LITECOIN";
|
'fiatCurrency': fiatCurrency,
|
||||||
case CryptoCurrency.xmr:
|
'type': isBuyAction ? 'buy' : 'sell',
|
||||||
return "XMR_MONERO";
|
'isRecurringPayment': 'false'
|
||||||
case CryptoCurrency.bch:
|
};
|
||||||
return "BCH_BITCOINCASH";
|
|
||||||
case CryptoCurrency.nano:
|
final url = Uri.https(_baseApiUrl, '$supported$paymentTypes/$fiatCurrency', params);
|
||||||
return "XNO_NANO";
|
|
||||||
default:
|
try {
|
||||||
return wallet.currency.title;
|
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>;
|
||||||
|
final List<dynamic> message = data['message'] as List<dynamic>;
|
||||||
|
return message
|
||||||
|
.map((item) => PaymentMethod.fromOnramperJson(item as Map<String, dynamic>))
|
||||||
|
.toList();
|
||||||
|
} else {
|
||||||
|
print('Failed to fetch available payment types');
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
print('Failed to fetch available payment types: $e');
|
||||||
|
return [];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
String getColorStr(Color color) {
|
Future<Map<String, dynamic>> getOnrampMetadata() async {
|
||||||
return color.value.toRadixString(16).replaceAll(RegExp(r'^ff'), "");
|
final url = Uri.https(_baseApiUrl, '$supported/onramps/all');
|
||||||
|
|
||||||
|
try {
|
||||||
|
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>;
|
||||||
|
|
||||||
|
final List<dynamic> onramps = data['message'] as List<dynamic>;
|
||||||
|
|
||||||
|
final Map<String, dynamic> result = {
|
||||||
|
for (var onramp in onramps)
|
||||||
|
(onramp['id'] as String): {
|
||||||
|
'displayName': onramp['displayName'] as String,
|
||||||
|
'svg': onramp['icons']['svg'] as String
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return result;
|
||||||
|
} else {
|
||||||
|
print('Failed to fetch onramp metadata');
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
print('Error occurred: $e');
|
||||||
|
return {};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Uri requestOnramperUrl(BuildContext context, bool? isBuyAction) {
|
@override
|
||||||
String primaryColor,
|
Future<List<Quote>?> fetchQuote(
|
||||||
secondaryColor,
|
{required CryptoCurrency cryptoCurrency,
|
||||||
primaryTextColor,
|
required FiatCurrency fiatCurrency,
|
||||||
secondaryTextColor,
|
required double amount,
|
||||||
containerColor,
|
required bool isBuyAction,
|
||||||
cardColor;
|
required String walletAddress,
|
||||||
|
PaymentType? paymentType,
|
||||||
|
String? countryCode}) async {
|
||||||
|
String? paymentMethod;
|
||||||
|
|
||||||
primaryColor = getColorStr(Theme.of(context).primaryColor);
|
if (paymentType != null && paymentType != PaymentType.all) {
|
||||||
secondaryColor = getColorStr(Theme.of(context).colorScheme.background);
|
paymentMethod = normalizePaymentMethod(paymentType);
|
||||||
primaryTextColor =
|
if (paymentMethod == null) paymentMethod = paymentType.name;
|
||||||
getColorStr(Theme.of(context).extension<CakeTextTheme>()!.titleColor);
|
}
|
||||||
secondaryTextColor = getColorStr(
|
|
||||||
Theme.of(context).extension<CakeTextTheme>()!.secondaryTextColor);
|
final actionType = isBuyAction ? 'buy' : 'sell';
|
||||||
containerColor = getColorStr(Theme.of(context).colorScheme.background);
|
|
||||||
cardColor = getColorStr(Theme.of(context).cardColor);
|
final normalizedCryptoCurrency = _getNormalizeCryptoCurrency(cryptoCurrency);
|
||||||
|
|
||||||
|
final params = {
|
||||||
|
'amount': amount.toString(),
|
||||||
|
if (paymentMethod != null) 'paymentMethod': paymentMethod,
|
||||||
|
'clientName': 'CakeWallet',
|
||||||
|
'type': actionType,
|
||||||
|
'walletAddress': walletAddress,
|
||||||
|
'isRecurringPayment': 'false',
|
||||||
|
'input': 'source',
|
||||||
|
};
|
||||||
|
|
||||||
|
log('Onramper: Fetching $actionType quote: ${isBuyAction ? normalizedCryptoCurrency : fiatCurrency.name} -> ${isBuyAction ? fiatCurrency.name : normalizedCryptoCurrency}, amount: $amount, paymentMethod: $paymentMethod');
|
||||||
|
|
||||||
|
final sourceCurrency = isBuyAction ? fiatCurrency.name : normalizedCryptoCurrency;
|
||||||
|
final destinationCurrency = isBuyAction ? normalizedCryptoCurrency : fiatCurrency.name;
|
||||||
|
|
||||||
|
final url = Uri.https(_baseApiUrl, '$quotes/${sourceCurrency}/${destinationCurrency}', params);
|
||||||
|
final headers = {'Authorization': _apiKey, 'accept': 'application/json'};
|
||||||
|
|
||||||
|
try {
|
||||||
|
final response = await http.get(url, headers: headers);
|
||||||
|
|
||||||
|
if (response.statusCode == 200) {
|
||||||
|
final data = jsonDecode(response.body) as List<dynamic>;
|
||||||
|
if (data.isEmpty) return null;
|
||||||
|
|
||||||
|
List<Quote> validQuotes = [];
|
||||||
|
|
||||||
|
final onrampMetadata = await getOnrampMetadata();
|
||||||
|
|
||||||
|
for (var item in data) {
|
||||||
|
|
||||||
|
if (item['errors'] != null) continue;
|
||||||
|
|
||||||
|
final paymentMethod = (item as Map<String, dynamic>)['paymentMethod'] as String;
|
||||||
|
|
||||||
|
final rampId = item['ramp'] as String?;
|
||||||
|
final rampMetaData = onrampMetadata[rampId] as Map<String, dynamic>?;
|
||||||
|
|
||||||
|
if (rampMetaData == null) continue;
|
||||||
|
|
||||||
|
final quote = Quote.fromOnramperJson(
|
||||||
|
item, isBuyAction, onrampMetadata, _getPaymentTypeByString(paymentMethod));
|
||||||
|
quote.setFiatCurrency = fiatCurrency;
|
||||||
|
quote.setCryptoCurrency = cryptoCurrency;
|
||||||
|
validQuotes.add(quote);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (validQuotes.isEmpty) return null;
|
||||||
|
|
||||||
|
return validQuotes;
|
||||||
|
} else {
|
||||||
|
print('Onramper: Failed to fetch rate');
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
print('Onramper: Failed to fetch rate $e');
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void>? launchProvider(
|
||||||
|
{required BuildContext context,
|
||||||
|
required Quote quote,
|
||||||
|
required double amount,
|
||||||
|
required bool isBuyAction,
|
||||||
|
required String cryptoCurrencyAddress,
|
||||||
|
String? countryCode}) async {
|
||||||
|
final actionType = isBuyAction ? 'buy' : 'sell';
|
||||||
|
final prefix = actionType == 'sell' ? actionType + '_' : '';
|
||||||
|
|
||||||
|
final primaryColor = getColorStr(Theme.of(context).primaryColor);
|
||||||
|
final secondaryColor = getColorStr(Theme.of(context).colorScheme.background);
|
||||||
|
final primaryTextColor = getColorStr(Theme.of(context).extension<CakeTextTheme>()!.titleColor);
|
||||||
|
final secondaryTextColor =
|
||||||
|
getColorStr(Theme.of(context).extension<CakeTextTheme>()!.secondaryTextColor);
|
||||||
|
final containerColor = getColorStr(Theme.of(context).colorScheme.background);
|
||||||
|
var cardColor = getColorStr(Theme.of(context).cardColor);
|
||||||
|
|
||||||
if (_settingsStore.currentTheme.title == S.current.high_contrast_theme) {
|
if (_settingsStore.currentTheme.title == S.current.high_contrast_theme) {
|
||||||
cardColor = getColorStr(Colors.white);
|
cardColor = getColorStr(Colors.white);
|
||||||
}
|
}
|
||||||
|
|
||||||
final networkName =
|
final defaultCrypto = _getNormalizeCryptoCurrency(quote.cryptoCurrency);
|
||||||
wallet.currency.fullName?.toUpperCase().replaceAll(" ", "");
|
|
||||||
|
|
||||||
return Uri.https(_baseUrl, '', <String, dynamic>{
|
final paymentMethod = normalizePaymentMethod(quote.paymentType);
|
||||||
|
|
||||||
|
final uri = Uri.https(_baseUrl, '', {
|
||||||
'apiKey': _apiKey,
|
'apiKey': _apiKey,
|
||||||
'defaultCrypto': _normalizeCryptoCurrency,
|
'mode': actionType,
|
||||||
'sell_defaultCrypto': _normalizeCryptoCurrency,
|
'${prefix}defaultFiat': quote.fiatCurrency.name,
|
||||||
'networkWallets': '${networkName}:${wallet.walletAddresses.address}',
|
'${prefix}defaultCrypto': defaultCrypto,
|
||||||
|
'${prefix}defaultAmount': amount.toString(),
|
||||||
|
if (paymentMethod != null) '${prefix}defaultPaymentMethod': paymentMethod,
|
||||||
|
'onlyOnramps': quote.rampId,
|
||||||
|
'networkWallets': '$defaultCrypto:$cryptoCurrencyAddress',
|
||||||
|
'walletAddress': cryptoCurrencyAddress,
|
||||||
'supportSwap': "false",
|
'supportSwap': "false",
|
||||||
'primaryColor': primaryColor,
|
'primaryColor': primaryColor,
|
||||||
'secondaryColor': secondaryColor,
|
'secondaryColor': secondaryColor,
|
||||||
|
'containerColor': containerColor,
|
||||||
'primaryTextColor': primaryTextColor,
|
'primaryTextColor': primaryTextColor,
|
||||||
'secondaryTextColor': secondaryTextColor,
|
'secondaryTextColor': secondaryTextColor,
|
||||||
'containerColor': containerColor,
|
|
||||||
'cardColor': cardColor,
|
'cardColor': cardColor,
|
||||||
'mode': isBuyAction == true ? 'buy' : 'sell',
|
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> launchProvider(BuildContext context, bool? isBuyAction) async {
|
if (await canLaunchUrl(uri)) {
|
||||||
final uri = requestOnramperUrl(context, isBuyAction);
|
await launchUrl(uri, mode: LaunchMode.externalApplication);
|
||||||
if (DeviceInfo.instance.isMobile) {
|
|
||||||
Navigator.of(context)
|
|
||||||
.pushNamed(Routes.webViewPage, arguments: [title, uri]);
|
|
||||||
} else {
|
} else {
|
||||||
await launchUrl(uri);
|
throw Exception('Could not launch URL');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
List<CryptoCurrency> mainCurrency = [
|
||||||
|
CryptoCurrency.btc,
|
||||||
|
CryptoCurrency.eth,
|
||||||
|
CryptoCurrency.sol,
|
||||||
|
];
|
||||||
|
|
||||||
|
String _tagToNetwork(String tag) {
|
||||||
|
switch (tag) {
|
||||||
|
case 'OMNI':
|
||||||
|
return tag;
|
||||||
|
case 'POL':
|
||||||
|
return 'POLYGON';
|
||||||
|
default:
|
||||||
|
return CryptoCurrency.fromString(tag).fullName ?? tag;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String _getNormalizeCryptoCurrency(Currency currency) {
|
||||||
|
if (currency is CryptoCurrency) {
|
||||||
|
if (!mainCurrency.contains(currency)) {
|
||||||
|
final network = currency.tag == null ? currency.fullName : _tagToNetwork(currency.tag!);
|
||||||
|
return '${currency.title}_${network?.replaceAll(' ', '')}'.toUpperCase();
|
||||||
|
}
|
||||||
|
return currency.title.toUpperCase();
|
||||||
|
}
|
||||||
|
return currency.name.toUpperCase();
|
||||||
|
}
|
||||||
|
|
||||||
|
String? normalizePaymentMethod(PaymentType paymentType) {
|
||||||
|
switch (paymentType) {
|
||||||
|
case PaymentType.bankTransfer:
|
||||||
|
return 'banktransfer';
|
||||||
|
case PaymentType.creditCard:
|
||||||
|
return 'creditcard';
|
||||||
|
case PaymentType.debitCard:
|
||||||
|
return 'debitcard';
|
||||||
|
case PaymentType.applePay:
|
||||||
|
return 'applepay';
|
||||||
|
case PaymentType.googlePay:
|
||||||
|
return 'googlepay';
|
||||||
|
case PaymentType.revolutPay:
|
||||||
|
return 'revolutpay';
|
||||||
|
case PaymentType.neteller:
|
||||||
|
return 'neteller';
|
||||||
|
case PaymentType.skrill:
|
||||||
|
return 'skrill';
|
||||||
|
case PaymentType.sepa:
|
||||||
|
return 'sepabanktransfer';
|
||||||
|
case PaymentType.sepaInstant:
|
||||||
|
return 'sepainstant';
|
||||||
|
case PaymentType.ach:
|
||||||
|
return 'ach';
|
||||||
|
case PaymentType.achInstant:
|
||||||
|
return 'iach';
|
||||||
|
case PaymentType.Khipu:
|
||||||
|
return 'khipu';
|
||||||
|
case PaymentType.palomaBanktTansfer:
|
||||||
|
return 'palomabanktransfer';
|
||||||
|
case PaymentType.ovo:
|
||||||
|
return 'ovo';
|
||||||
|
case PaymentType.zaloPay:
|
||||||
|
return 'zalopay';
|
||||||
|
case PaymentType.zaloBankTransfer:
|
||||||
|
return 'zalobanktransfer';
|
||||||
|
case PaymentType.gcash:
|
||||||
|
return 'gcash';
|
||||||
|
case PaymentType.imps:
|
||||||
|
return 'imps';
|
||||||
|
case PaymentType.dana:
|
||||||
|
return 'dana';
|
||||||
|
case PaymentType.ideal:
|
||||||
|
return 'ideal';
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PaymentType _getPaymentTypeByString(String paymentMethod) {
|
||||||
|
switch (paymentMethod.toLowerCase()) {
|
||||||
|
case 'banktransfer':
|
||||||
|
return PaymentType.bankTransfer;
|
||||||
|
case 'creditcard':
|
||||||
|
return PaymentType.creditCard;
|
||||||
|
case 'debitcard':
|
||||||
|
return PaymentType.debitCard;
|
||||||
|
case 'applepay':
|
||||||
|
return PaymentType.applePay;
|
||||||
|
case 'googlepay':
|
||||||
|
return PaymentType.googlePay;
|
||||||
|
case 'revolutpay':
|
||||||
|
return PaymentType.revolutPay;
|
||||||
|
case 'neteller':
|
||||||
|
return PaymentType.neteller;
|
||||||
|
case 'skrill':
|
||||||
|
return PaymentType.skrill;
|
||||||
|
case 'sepabanktransfer':
|
||||||
|
return PaymentType.sepa;
|
||||||
|
case 'sepainstant':
|
||||||
|
return PaymentType.sepaInstant;
|
||||||
|
case 'ach':
|
||||||
|
return PaymentType.ach;
|
||||||
|
case 'iach':
|
||||||
|
return PaymentType.achInstant;
|
||||||
|
case 'khipu':
|
||||||
|
return PaymentType.Khipu;
|
||||||
|
case 'palomabanktransfer':
|
||||||
|
return PaymentType.palomaBanktTansfer;
|
||||||
|
case 'ovo':
|
||||||
|
return PaymentType.ovo;
|
||||||
|
case 'zalopay':
|
||||||
|
return PaymentType.zaloPay;
|
||||||
|
case 'zalobanktransfer':
|
||||||
|
return PaymentType.zaloBankTransfer;
|
||||||
|
case 'gcash':
|
||||||
|
return PaymentType.gcash;
|
||||||
|
case 'imps':
|
||||||
|
return PaymentType.imps;
|
||||||
|
case 'dana':
|
||||||
|
return PaymentType.dana;
|
||||||
|
case 'ideal':
|
||||||
|
return PaymentType.ideal;
|
||||||
|
default:
|
||||||
|
return PaymentType.all;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String getColorStr(Color color) => color.value.toRadixString(16).replaceAll(RegExp(r'^ff'), "");
|
||||||
}
|
}
|
||||||
|
|
287
lib/buy/payment_method.dart
Normal file
|
@ -0,0 +1,287 @@
|
||||||
|
import 'dart:ui';
|
||||||
|
|
||||||
|
import 'package:cake_wallet/core/selectable_option.dart';
|
||||||
|
|
||||||
|
enum PaymentType {
|
||||||
|
all,
|
||||||
|
bankTransfer,
|
||||||
|
creditCard,
|
||||||
|
debitCard,
|
||||||
|
applePay,
|
||||||
|
googlePay,
|
||||||
|
revolutPay,
|
||||||
|
neteller,
|
||||||
|
skrill,
|
||||||
|
sepa,
|
||||||
|
sepaInstant,
|
||||||
|
ach,
|
||||||
|
achInstant,
|
||||||
|
Khipu,
|
||||||
|
palomaBanktTansfer,
|
||||||
|
ovo,
|
||||||
|
zaloPay,
|
||||||
|
zaloBankTransfer,
|
||||||
|
gcash,
|
||||||
|
imps,
|
||||||
|
dana,
|
||||||
|
ideal,
|
||||||
|
paypal,
|
||||||
|
sepaOpenBankingPayment,
|
||||||
|
gbpOpenBankingPayment,
|
||||||
|
lowCostAch,
|
||||||
|
mobileWallet,
|
||||||
|
pixInstantPayment,
|
||||||
|
yellowCardBankTransfer,
|
||||||
|
fiatBalance,
|
||||||
|
bancontact,
|
||||||
|
}
|
||||||
|
|
||||||
|
extension PaymentTypeTitle on PaymentType {
|
||||||
|
String? get title {
|
||||||
|
switch (this) {
|
||||||
|
case PaymentType.all:
|
||||||
|
return 'All Payment Methods';
|
||||||
|
case PaymentType.bankTransfer:
|
||||||
|
return 'Bank Transfer';
|
||||||
|
case PaymentType.creditCard:
|
||||||
|
return 'Credit Card';
|
||||||
|
case PaymentType.debitCard:
|
||||||
|
return 'Debit Card';
|
||||||
|
case PaymentType.applePay:
|
||||||
|
return 'Apple Pay';
|
||||||
|
case PaymentType.googlePay:
|
||||||
|
return 'Google Pay';
|
||||||
|
case PaymentType.revolutPay:
|
||||||
|
return 'Revolut Pay';
|
||||||
|
case PaymentType.neteller:
|
||||||
|
return 'Neteller';
|
||||||
|
case PaymentType.skrill:
|
||||||
|
return 'Skrill';
|
||||||
|
case PaymentType.sepa:
|
||||||
|
return 'SEPA';
|
||||||
|
case PaymentType.sepaInstant:
|
||||||
|
return 'SEPA Instant';
|
||||||
|
case PaymentType.ach:
|
||||||
|
return 'ACH';
|
||||||
|
case PaymentType.achInstant:
|
||||||
|
return 'ACH Instant';
|
||||||
|
case PaymentType.Khipu:
|
||||||
|
return 'Khipu';
|
||||||
|
case PaymentType.palomaBanktTansfer:
|
||||||
|
return 'Paloma Bank Transfer';
|
||||||
|
case PaymentType.ovo:
|
||||||
|
return 'OVO';
|
||||||
|
case PaymentType.zaloPay:
|
||||||
|
return 'Zalo Pay';
|
||||||
|
case PaymentType.zaloBankTransfer:
|
||||||
|
return 'Zalo Bank Transfer';
|
||||||
|
case PaymentType.gcash:
|
||||||
|
return 'GCash';
|
||||||
|
case PaymentType.imps:
|
||||||
|
return 'IMPS';
|
||||||
|
case PaymentType.dana:
|
||||||
|
return 'DANA';
|
||||||
|
case PaymentType.ideal:
|
||||||
|
return 'iDEAL';
|
||||||
|
case PaymentType.paypal:
|
||||||
|
return 'PayPal';
|
||||||
|
case PaymentType.sepaOpenBankingPayment:
|
||||||
|
return 'SEPA Open Banking Payment';
|
||||||
|
case PaymentType.gbpOpenBankingPayment:
|
||||||
|
return 'GBP Open Banking Payment';
|
||||||
|
case PaymentType.lowCostAch:
|
||||||
|
return 'Low Cost ACH';
|
||||||
|
case PaymentType.mobileWallet:
|
||||||
|
return 'Mobile Wallet';
|
||||||
|
case PaymentType.pixInstantPayment:
|
||||||
|
return 'PIX Instant Payment';
|
||||||
|
case PaymentType.yellowCardBankTransfer:
|
||||||
|
return 'Yellow Card Bank Transfer';
|
||||||
|
case PaymentType.fiatBalance:
|
||||||
|
return 'Fiat Balance';
|
||||||
|
case PaymentType.bancontact:
|
||||||
|
return 'Bancontact';
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String? get lightIconPath {
|
||||||
|
switch (this) {
|
||||||
|
case PaymentType.all:
|
||||||
|
return 'assets/images/usd_round_light.svg';
|
||||||
|
case PaymentType.creditCard:
|
||||||
|
case PaymentType.debitCard:
|
||||||
|
case PaymentType.yellowCardBankTransfer:
|
||||||
|
return 'assets/images/card.svg';
|
||||||
|
case PaymentType.bankTransfer:
|
||||||
|
return 'assets/images/bank_light.svg';
|
||||||
|
case PaymentType.skrill:
|
||||||
|
return 'assets/images/skrill.svg';
|
||||||
|
case PaymentType.applePay:
|
||||||
|
return 'assets/images/apple_pay_round_light.svg';
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String? get darkIconPath {
|
||||||
|
switch (this) {
|
||||||
|
case PaymentType.all:
|
||||||
|
return 'assets/images/usd_round_dark.svg';
|
||||||
|
case PaymentType.creditCard:
|
||||||
|
case PaymentType.debitCard:
|
||||||
|
case PaymentType.yellowCardBankTransfer:
|
||||||
|
return 'assets/images/card_dark.svg';
|
||||||
|
case PaymentType.bankTransfer:
|
||||||
|
return 'assets/images/bank_dark.svg';
|
||||||
|
case PaymentType.skrill:
|
||||||
|
return 'assets/images/skrill.svg';
|
||||||
|
case PaymentType.applePay:
|
||||||
|
return 'assets/images/apple_pay_round_dark.svg';
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String? get description {
|
||||||
|
switch (this) {
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class PaymentMethod extends SelectableOption {
|
||||||
|
PaymentMethod({
|
||||||
|
required this.paymentMethodType,
|
||||||
|
required this.customTitle,
|
||||||
|
required this.customIconPath,
|
||||||
|
this.customDescription,
|
||||||
|
}) : super(title: paymentMethodType.title ?? customTitle);
|
||||||
|
|
||||||
|
final PaymentType paymentMethodType;
|
||||||
|
final String customTitle;
|
||||||
|
final String customIconPath;
|
||||||
|
final String? customDescription;
|
||||||
|
bool isSelected = false;
|
||||||
|
|
||||||
|
@override
|
||||||
|
String? get description => paymentMethodType.description ?? customDescription;
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get lightIconPath => paymentMethodType.lightIconPath ?? customIconPath;
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get darkIconPath => paymentMethodType.darkIconPath ?? customIconPath;
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool get isOptionSelected => isSelected;
|
||||||
|
|
||||||
|
factory PaymentMethod.all() {
|
||||||
|
return PaymentMethod(
|
||||||
|
paymentMethodType: PaymentType.all,
|
||||||
|
customTitle: 'All Payment Methods',
|
||||||
|
customIconPath: 'assets/images/dollar_coin.svg');
|
||||||
|
}
|
||||||
|
|
||||||
|
factory PaymentMethod.fromOnramperJson(Map<String, dynamic> json) {
|
||||||
|
final type = PaymentMethod.getPaymentTypeId(json['paymentTypeId'] as String?);
|
||||||
|
return PaymentMethod(
|
||||||
|
paymentMethodType: type,
|
||||||
|
customTitle: json['name'] as String? ?? 'Unknown',
|
||||||
|
customIconPath: json['icon'] as String? ?? 'assets/images/card.png',
|
||||||
|
customDescription: json['description'] as String?);
|
||||||
|
}
|
||||||
|
|
||||||
|
factory PaymentMethod.fromDFX(String paymentMethod, PaymentType paymentType) {
|
||||||
|
return PaymentMethod(
|
||||||
|
paymentMethodType: paymentType,
|
||||||
|
customTitle: paymentMethod,
|
||||||
|
customIconPath: 'assets/images/card.png');
|
||||||
|
}
|
||||||
|
|
||||||
|
factory PaymentMethod.fromMoonPayJson(Map<String, dynamic> json, PaymentType paymentType) {
|
||||||
|
return PaymentMethod(
|
||||||
|
paymentMethodType: paymentType,
|
||||||
|
customTitle: json['paymentMethod'] as String,
|
||||||
|
customIconPath: 'assets/images/card.png');
|
||||||
|
}
|
||||||
|
|
||||||
|
factory PaymentMethod.fromMeldJson(Map<String, dynamic> json) {
|
||||||
|
final type = PaymentMethod.getPaymentTypeId(json['paymentMethod'] as String?);
|
||||||
|
final logos = json['logos'] as Map<String, dynamic>;
|
||||||
|
return PaymentMethod(
|
||||||
|
paymentMethodType: type,
|
||||||
|
customTitle: json['name'] as String? ?? 'Unknown',
|
||||||
|
customIconPath: logos['dark'] as String? ?? 'assets/images/card.png',
|
||||||
|
customDescription: json['description'] as String?);
|
||||||
|
}
|
||||||
|
|
||||||
|
static PaymentType getPaymentTypeId(String? type) {
|
||||||
|
switch (type?.toLowerCase()) {
|
||||||
|
case 'banktransfer':
|
||||||
|
case 'bank':
|
||||||
|
case 'yellow_card_bank_transfer':
|
||||||
|
return PaymentType.bankTransfer;
|
||||||
|
case 'creditcard':
|
||||||
|
case 'card':
|
||||||
|
case 'credit_debit_card':
|
||||||
|
return PaymentType.creditCard;
|
||||||
|
case 'debitcard':
|
||||||
|
return PaymentType.debitCard;
|
||||||
|
case 'applepay':
|
||||||
|
case 'apple_pay':
|
||||||
|
return PaymentType.applePay;
|
||||||
|
case 'googlepay':
|
||||||
|
case 'google_pay':
|
||||||
|
return PaymentType.googlePay;
|
||||||
|
case 'revolutpay':
|
||||||
|
return PaymentType.revolutPay;
|
||||||
|
case 'neteller':
|
||||||
|
return PaymentType.neteller;
|
||||||
|
case 'skrill':
|
||||||
|
return PaymentType.skrill;
|
||||||
|
case 'sepabanktransfer':
|
||||||
|
case 'sepa':
|
||||||
|
case 'sepa_bank_transfer':
|
||||||
|
return PaymentType.sepa;
|
||||||
|
case 'sepainstant':
|
||||||
|
case 'sepa_instant':
|
||||||
|
return PaymentType.sepaInstant;
|
||||||
|
case 'ach':
|
||||||
|
case 'ach_bank_transfer':
|
||||||
|
return PaymentType.ach;
|
||||||
|
case 'iach':
|
||||||
|
case 'instant_ach':
|
||||||
|
return PaymentType.achInstant;
|
||||||
|
case 'khipu':
|
||||||
|
return PaymentType.Khipu;
|
||||||
|
case 'palomabanktransfer':
|
||||||
|
return PaymentType.palomaBanktTansfer;
|
||||||
|
case 'ovo':
|
||||||
|
return PaymentType.ovo;
|
||||||
|
case 'zalopay':
|
||||||
|
return PaymentType.zaloPay;
|
||||||
|
case 'zalobanktransfer':
|
||||||
|
case 'za_bank_transfer':
|
||||||
|
return PaymentType.zaloBankTransfer;
|
||||||
|
case 'gcash':
|
||||||
|
return PaymentType.gcash;
|
||||||
|
case 'imps':
|
||||||
|
return PaymentType.imps;
|
||||||
|
case 'dana':
|
||||||
|
return PaymentType.dana;
|
||||||
|
case 'ideal':
|
||||||
|
return PaymentType.ideal;
|
||||||
|
case 'paypal':
|
||||||
|
return PaymentType.paypal;
|
||||||
|
case 'sepa_open_banking_payment':
|
||||||
|
return PaymentType.sepaOpenBankingPayment;
|
||||||
|
case 'bancontact':
|
||||||
|
return PaymentType.bancontact;
|
||||||
|
default:
|
||||||
|
return PaymentType.all;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,13 +1,18 @@
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
import 'dart:developer';
|
||||||
|
|
||||||
import 'package:cake_wallet/.secrets.g.dart' as secrets;
|
import 'package:cake_wallet/.secrets.g.dart' as secrets;
|
||||||
import 'package:cake_wallet/buy/buy_provider.dart';
|
import 'package:cake_wallet/buy/buy_provider.dart';
|
||||||
|
import 'package:cake_wallet/buy/buy_quote.dart';
|
||||||
|
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/generated/i18n.dart';
|
||||||
import 'package:cake_wallet/routes.dart';
|
import 'package:cake_wallet/routes.dart';
|
||||||
import 'package:cake_wallet/src/screens/connect_device/connect_device_page.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:cake_wallet/src/widgets/alert_with_one_action.dart';
|
||||||
import 'package:cake_wallet/utils/show_pop_up.dart';
|
import 'package:cake_wallet/utils/show_pop_up.dart';
|
||||||
import 'package:cake_wallet/view_model/hardware_wallet/ledger_view_model.dart';
|
import 'package:cake_wallet/view_model/hardware_wallet/ledger_view_model.dart';
|
||||||
|
import 'package:cw_core/crypto_currency.dart';
|
||||||
import 'package:cw_core/wallet_base.dart';
|
import 'package:cw_core/wallet_base.dart';
|
||||||
import 'package:cw_core/wallet_type.dart';
|
import 'package:cw_core/wallet_type.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
@ -15,7 +20,8 @@ import 'package:http/http.dart' as http;
|
||||||
import 'package:url_launcher/url_launcher.dart';
|
import 'package:url_launcher/url_launcher.dart';
|
||||||
|
|
||||||
class RobinhoodBuyProvider extends BuyProvider {
|
class RobinhoodBuyProvider extends BuyProvider {
|
||||||
RobinhoodBuyProvider({required WalletBase wallet, bool isTestEnvironment = false, LedgerViewModel? ledgerVM})
|
RobinhoodBuyProvider(
|
||||||
|
{required WalletBase wallet, bool isTestEnvironment = false, LedgerViewModel? ledgerVM})
|
||||||
: super(wallet: wallet, isTestEnvironment: isTestEnvironment, ledgerVM: ledgerVM);
|
: super(wallet: wallet, isTestEnvironment: isTestEnvironment, ledgerVM: ledgerVM);
|
||||||
|
|
||||||
static const _baseUrl = 'applink.robinhood.com';
|
static const _baseUrl = 'applink.robinhood.com';
|
||||||
|
@ -33,6 +39,9 @@ class RobinhoodBuyProvider extends BuyProvider {
|
||||||
@override
|
@override
|
||||||
String get darkIcon => 'assets/images/robinhood_dark.png';
|
String get darkIcon => 'assets/images/robinhood_dark.png';
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool get isAggregator => false;
|
||||||
|
|
||||||
String get _applicationId => secrets.robinhoodApplicationId;
|
String get _applicationId => secrets.robinhoodApplicationId;
|
||||||
|
|
||||||
String get _apiSecret => secrets.exchangeHelperApiKey;
|
String get _apiSecret => secrets.exchangeHelperApiKey;
|
||||||
|
@ -86,7 +95,13 @@ class RobinhoodBuyProvider extends BuyProvider {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> launchProvider(BuildContext context, bool? isBuyAction) async {
|
Future<void>? launchProvider(
|
||||||
|
{required BuildContext context,
|
||||||
|
required Quote quote,
|
||||||
|
required double amount,
|
||||||
|
required bool isBuyAction,
|
||||||
|
required String cryptoCurrencyAddress,
|
||||||
|
String? countryCode}) async {
|
||||||
if (wallet.isHardwareWallet) {
|
if (wallet.isHardwareWallet) {
|
||||||
if (!ledgerVM!.isConnected) {
|
if (!ledgerVM!.isConnected) {
|
||||||
await Navigator.of(context).pushNamed(Routes.connectDevices,
|
await Navigator.of(context).pushNamed(Routes.connectDevices,
|
||||||
|
@ -116,4 +131,87 @@ class RobinhoodBuyProvider extends BuyProvider {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<List<Quote>?> fetchQuote(
|
||||||
|
{required CryptoCurrency cryptoCurrency,
|
||||||
|
required FiatCurrency fiatCurrency,
|
||||||
|
required double amount,
|
||||||
|
required bool isBuyAction,
|
||||||
|
required String walletAddress,
|
||||||
|
PaymentType? paymentType,
|
||||||
|
String? countryCode}) async {
|
||||||
|
String? paymentMethod;
|
||||||
|
|
||||||
|
if (paymentType != null && paymentType != PaymentType.all) {
|
||||||
|
paymentMethod = normalizePaymentMethod(paymentType);
|
||||||
|
if (paymentMethod == null) paymentMethod = paymentType.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
final action = isBuyAction ? 'buy' : 'sell';
|
||||||
|
log('Robinhood: Fetching $action quote: ${isBuyAction ? cryptoCurrency.title : fiatCurrency.name.toUpperCase()} -> ${isBuyAction ? fiatCurrency.name.toUpperCase() : cryptoCurrency.title}, amount: $amount paymentMethod: $paymentMethod');
|
||||||
|
|
||||||
|
final queryParams = {
|
||||||
|
'applicationId': _applicationId,
|
||||||
|
'fiatCode': fiatCurrency.name,
|
||||||
|
'assetCode': cryptoCurrency.title,
|
||||||
|
'fiatAmount': amount.toString(),
|
||||||
|
if (paymentMethod != null) 'paymentMethod': paymentMethod,
|
||||||
|
};
|
||||||
|
|
||||||
|
final uri =
|
||||||
|
Uri.https('api.robinhood.com', '/catpay/v1/${cryptoCurrency.title}/quote/', queryParams);
|
||||||
|
|
||||||
|
try {
|
||||||
|
final response = await http.get(uri, headers: {'accept': 'application/json'});
|
||||||
|
final responseData = jsonDecode(response.body) as Map<String, dynamic>;
|
||||||
|
|
||||||
|
if (response.statusCode == 200) {
|
||||||
|
final paymentType = _getPaymentTypeByString(responseData['paymentMethod'] as String?);
|
||||||
|
final quote = Quote.fromRobinhoodJson(responseData, isBuyAction, paymentType);
|
||||||
|
quote.setFiatCurrency = fiatCurrency;
|
||||||
|
quote.setCryptoCurrency = cryptoCurrency;
|
||||||
|
return [quote];
|
||||||
|
} else {
|
||||||
|
if (responseData.containsKey('message')) {
|
||||||
|
log('Robinhood Error: ${responseData['message']}');
|
||||||
|
} else {
|
||||||
|
print('Robinhood Failed to fetch $action quote: ${response.statusCode}');
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
log('Robinhood: Failed to fetch $action quote: $e');
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ● buying_power
|
||||||
|
// ● crypto_balance
|
||||||
|
// ● debit_card
|
||||||
|
// ● bank_transfer
|
||||||
|
}
|
||||||
|
|
||||||
|
String? normalizePaymentMethod(PaymentType paymentMethod) {
|
||||||
|
switch (paymentMethod) {
|
||||||
|
case PaymentType.creditCard:
|
||||||
|
return 'debit_card';
|
||||||
|
case PaymentType.debitCard:
|
||||||
|
return 'debit_card';
|
||||||
|
case PaymentType.bankTransfer:
|
||||||
|
return 'bank_transfer';
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PaymentType _getPaymentTypeByString(String? paymentMethod) {
|
||||||
|
switch (paymentMethod) {
|
||||||
|
case 'debit_card':
|
||||||
|
return PaymentType.debitCard;
|
||||||
|
case 'bank_transfer':
|
||||||
|
return PaymentType.bankTransfer;
|
||||||
|
default:
|
||||||
|
return PaymentType.all;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
20
lib/buy/sell_buy_states.dart
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
abstract class PaymentMethodLoadingState {}
|
||||||
|
|
||||||
|
class InitialPaymentMethod extends PaymentMethodLoadingState {}
|
||||||
|
|
||||||
|
class PaymentMethodLoading extends PaymentMethodLoadingState {}
|
||||||
|
|
||||||
|
class PaymentMethodLoaded extends PaymentMethodLoadingState {}
|
||||||
|
|
||||||
|
class PaymentMethodFailed extends PaymentMethodLoadingState {}
|
||||||
|
|
||||||
|
|
||||||
|
abstract class BuySellQuotLoadingState {}
|
||||||
|
|
||||||
|
class InitialBuySellQuotState extends BuySellQuotLoadingState {}
|
||||||
|
|
||||||
|
class BuySellQuotLoading extends BuySellQuotLoadingState {}
|
||||||
|
|
||||||
|
class BuySellQuotLoaded extends BuySellQuotLoadingState {}
|
||||||
|
|
||||||
|
class BuySellQuotFailed extends BuySellQuotLoadingState {}
|
|
@ -42,6 +42,9 @@ class WyreBuyProvider extends BuyProvider {
|
||||||
@override
|
@override
|
||||||
String get darkIcon => 'assets/images/robinhood_dark.png';
|
String get darkIcon => 'assets/images/robinhood_dark.png';
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool get isAggregator => false;
|
||||||
|
|
||||||
String get trackUrl => isTestEnvironment ? _trackTestUrl : _trackProductUrl;
|
String get trackUrl => isTestEnvironment ? _trackTestUrl : _trackProductUrl;
|
||||||
|
|
||||||
String baseApiUrl;
|
String baseApiUrl;
|
||||||
|
@ -148,10 +151,4 @@ class WyreBuyProvider extends BuyProvider {
|
||||||
receiveAddress: wallet.walletAddresses.address,
|
receiveAddress: wallet.walletAddresses.address,
|
||||||
walletId: wallet.id);
|
walletId: wallet.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
Future<void> launchProvider(BuildContext context, bool? isBuyAction) {
|
|
||||||
// TODO: implement launchProvider
|
|
||||||
throw UnimplementedError();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -268,9 +268,7 @@ class BackupService {
|
||||||
final currentFiatCurrency = data[PreferencesKey.currentFiatCurrencyKey] as String?;
|
final currentFiatCurrency = data[PreferencesKey.currentFiatCurrencyKey] as String?;
|
||||||
final shouldSaveRecipientAddress = data[PreferencesKey.shouldSaveRecipientAddressKey] as bool?;
|
final shouldSaveRecipientAddress = data[PreferencesKey.shouldSaveRecipientAddressKey] as bool?;
|
||||||
final isAppSecure = data[PreferencesKey.isAppSecureKey] as bool?;
|
final isAppSecure = data[PreferencesKey.isAppSecureKey] as bool?;
|
||||||
final disableBuy = data[PreferencesKey.disableBuyKey] as bool?;
|
final disableTradeOption = data[PreferencesKey.disableTradeOption] as bool?;
|
||||||
final disableSell = data[PreferencesKey.disableSellKey] as bool?;
|
|
||||||
final defaultBuyProvider = data[PreferencesKey.defaultBuyProvider] as int?;
|
|
||||||
final currentTransactionPriorityKeyLegacy =
|
final currentTransactionPriorityKeyLegacy =
|
||||||
data[PreferencesKey.currentTransactionPriorityKeyLegacy] as int?;
|
data[PreferencesKey.currentTransactionPriorityKeyLegacy] as int?;
|
||||||
final currentBitcoinElectrumSererId =
|
final currentBitcoinElectrumSererId =
|
||||||
|
@ -323,14 +321,8 @@ class BackupService {
|
||||||
if (isAppSecure != null)
|
if (isAppSecure != null)
|
||||||
await _sharedPreferences.setBool(PreferencesKey.isAppSecureKey, isAppSecure);
|
await _sharedPreferences.setBool(PreferencesKey.isAppSecureKey, isAppSecure);
|
||||||
|
|
||||||
if (disableBuy != null)
|
if (disableTradeOption != null)
|
||||||
await _sharedPreferences.setBool(PreferencesKey.disableBuyKey, disableBuy);
|
await _sharedPreferences.setBool(PreferencesKey.disableTradeOption, disableTradeOption);
|
||||||
|
|
||||||
if (disableSell != null)
|
|
||||||
await _sharedPreferences.setBool(PreferencesKey.disableSellKey, disableSell);
|
|
||||||
|
|
||||||
if (defaultBuyProvider != null)
|
|
||||||
await _sharedPreferences.setInt(PreferencesKey.defaultBuyProvider, defaultBuyProvider);
|
|
||||||
|
|
||||||
if (currentTransactionPriorityKeyLegacy != null)
|
if (currentTransactionPriorityKeyLegacy != null)
|
||||||
await _sharedPreferences.setInt(
|
await _sharedPreferences.setInt(
|
||||||
|
@ -516,10 +508,7 @@ class BackupService {
|
||||||
_sharedPreferences.getString(PreferencesKey.currentFiatCurrencyKey),
|
_sharedPreferences.getString(PreferencesKey.currentFiatCurrencyKey),
|
||||||
PreferencesKey.shouldSaveRecipientAddressKey:
|
PreferencesKey.shouldSaveRecipientAddressKey:
|
||||||
_sharedPreferences.getBool(PreferencesKey.shouldSaveRecipientAddressKey),
|
_sharedPreferences.getBool(PreferencesKey.shouldSaveRecipientAddressKey),
|
||||||
PreferencesKey.disableBuyKey: _sharedPreferences.getBool(PreferencesKey.disableBuyKey),
|
PreferencesKey.disableTradeOption: _sharedPreferences.getBool(PreferencesKey.disableTradeOption),
|
||||||
PreferencesKey.disableSellKey: _sharedPreferences.getBool(PreferencesKey.disableSellKey),
|
|
||||||
PreferencesKey.defaultBuyProvider:
|
|
||||||
_sharedPreferences.getInt(PreferencesKey.defaultBuyProvider),
|
|
||||||
PreferencesKey.currentPinLength: _sharedPreferences.getInt(PreferencesKey.currentPinLength),
|
PreferencesKey.currentPinLength: _sharedPreferences.getInt(PreferencesKey.currentPinLength),
|
||||||
PreferencesKey.currentTransactionPriorityKeyLegacy:
|
PreferencesKey.currentTransactionPriorityKeyLegacy:
|
||||||
_sharedPreferences.getInt(PreferencesKey.currentTransactionPriorityKeyLegacy),
|
_sharedPreferences.getInt(PreferencesKey.currentTransactionPriorityKeyLegacy),
|
||||||
|
|
47
lib/core/selectable_option.dart
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
abstract class SelectableItem {
|
||||||
|
SelectableItem({required this.title});
|
||||||
|
final String title;
|
||||||
|
}
|
||||||
|
|
||||||
|
class OptionTitle extends SelectableItem {
|
||||||
|
OptionTitle({required String title}) : super(title: title);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class SelectableOption extends SelectableItem {
|
||||||
|
SelectableOption({required String title}) : super(title: title);
|
||||||
|
|
||||||
|
String get lightIconPath;
|
||||||
|
|
||||||
|
String get darkIconPath;
|
||||||
|
|
||||||
|
String? get description => null;
|
||||||
|
|
||||||
|
String? get topLeftSubTitle => null;
|
||||||
|
|
||||||
|
String? get topLeftSubTitleIconPath => null;
|
||||||
|
|
||||||
|
String? get topRightSubTitle => null;
|
||||||
|
|
||||||
|
String? get topRightSubTitleLightIconPath => null;
|
||||||
|
|
||||||
|
String? get topRightSubTitleDarkIconPath => null;
|
||||||
|
|
||||||
|
String? get bottomLeftSubTitle => null;
|
||||||
|
|
||||||
|
String? get bottomLeftSubTitleIconPath => null;
|
||||||
|
|
||||||
|
String? get bottomRightSubTitle => null;
|
||||||
|
|
||||||
|
String? get bottomRightSubTitleLightIconPath => null;
|
||||||
|
|
||||||
|
String? get bottomRightSubTitleDarkIconPath => null;
|
||||||
|
|
||||||
|
List<String> get badges => [];
|
||||||
|
|
||||||
|
bool get isOptionSelected => false;
|
||||||
|
|
||||||
|
set isOptionSelected(bool isSelected) => false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,11 @@ import 'package:flutter/material.dart';
|
||||||
import 'package:shared_preferences/shared_preferences.dart';
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
|
|
||||||
class WalletLoadingService {
|
class WalletLoadingService {
|
||||||
WalletLoadingService(this.sharedPreferences, this.keyService, this.walletServiceFactory);
|
WalletLoadingService(
|
||||||
|
this.sharedPreferences,
|
||||||
|
this.keyService,
|
||||||
|
this.walletServiceFactory,
|
||||||
|
);
|
||||||
|
|
||||||
final SharedPreferences sharedPreferences;
|
final SharedPreferences sharedPreferences;
|
||||||
final KeyService keyService;
|
final KeyService keyService;
|
||||||
|
@ -77,7 +81,8 @@ class WalletLoadingService {
|
||||||
await updateMoneroWalletPassword(wallet);
|
await updateMoneroWalletPassword(wallet);
|
||||||
}
|
}
|
||||||
|
|
||||||
await sharedPreferences.setString(PreferencesKey.currentWalletName, wallet.name);
|
await sharedPreferences.setString(
|
||||||
|
PreferencesKey.currentWalletName, wallet.name);
|
||||||
await sharedPreferences.setInt(
|
await sharedPreferences.setInt(
|
||||||
PreferencesKey.currentWalletType, serializeToInt(wallet.type));
|
PreferencesKey.currentWalletType, serializeToInt(wallet.type));
|
||||||
|
|
||||||
|
@ -129,4 +134,9 @@ class WalletLoadingService {
|
||||||
|
|
||||||
return "\n\n$type ($name): ${await walletService.getSeeds(name, password, type)}";
|
return "\n\n$type ($name): ${await walletService.getSeeds(name, password, type)}";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool requireHardwareWalletConnection(WalletType type, String name) {
|
||||||
|
final walletService = walletServiceFactory.call(type);
|
||||||
|
return walletService.requireHardwareWalletConnection(name);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
54
lib/di.dart
|
@ -19,6 +19,7 @@ import 'package:cake_wallet/core/backup_service.dart';
|
||||||
import 'package:cake_wallet/core/key_service.dart';
|
import 'package:cake_wallet/core/key_service.dart';
|
||||||
import 'package:cake_wallet/core/new_wallet_type_arguments.dart';
|
import 'package:cake_wallet/core/new_wallet_type_arguments.dart';
|
||||||
import 'package:cake_wallet/core/secure_storage.dart';
|
import 'package:cake_wallet/core/secure_storage.dart';
|
||||||
|
import 'package:cake_wallet/core/selectable_option.dart';
|
||||||
import 'package:cake_wallet/core/totp_request_details.dart';
|
import 'package:cake_wallet/core/totp_request_details.dart';
|
||||||
import 'package:cake_wallet/core/wallet_connect/wallet_connect_key_service.dart';
|
import 'package:cake_wallet/core/wallet_connect/wallet_connect_key_service.dart';
|
||||||
import 'package:cake_wallet/core/wallet_connect/wc_bottom_sheet_service.dart';
|
import 'package:cake_wallet/core/wallet_connect/wc_bottom_sheet_service.dart';
|
||||||
|
@ -31,10 +32,14 @@ import 'package:cake_wallet/entities/biometric_auth.dart';
|
||||||
import 'package:cake_wallet/entities/contact.dart';
|
import 'package:cake_wallet/entities/contact.dart';
|
||||||
import 'package:cake_wallet/entities/contact_record.dart';
|
import 'package:cake_wallet/entities/contact_record.dart';
|
||||||
import 'package:cake_wallet/entities/exchange_api_mode.dart';
|
import 'package:cake_wallet/entities/exchange_api_mode.dart';
|
||||||
|
import 'package:cake_wallet/entities/hardware_wallet/require_hardware_wallet_connection.dart';
|
||||||
import 'package:cake_wallet/entities/parse_address_from_domain.dart';
|
import 'package:cake_wallet/entities/parse_address_from_domain.dart';
|
||||||
import 'package:cake_wallet/entities/wallet_edit_page_arguments.dart';
|
import 'package:cake_wallet/entities/wallet_edit_page_arguments.dart';
|
||||||
import 'package:cake_wallet/entities/wallet_manager.dart';
|
import 'package:cake_wallet/entities/wallet_manager.dart';
|
||||||
|
import 'package:cake_wallet/src/screens/buy/buy_sell_options_page.dart';
|
||||||
|
import 'package:cake_wallet/src/screens/buy/payment_method_options_page.dart';
|
||||||
import 'package:cake_wallet/src/screens/receive/address_list_page.dart';
|
import 'package:cake_wallet/src/screens/receive/address_list_page.dart';
|
||||||
|
import 'package:cake_wallet/src/screens/wallet_list/wallet_list_page.dart';
|
||||||
import 'package:cake_wallet/src/screens/settings/mweb_logs_page.dart';
|
import 'package:cake_wallet/src/screens/settings/mweb_logs_page.dart';
|
||||||
import 'package:cake_wallet/src/screens/settings/mweb_node_page.dart';
|
import 'package:cake_wallet/src/screens/settings/mweb_node_page.dart';
|
||||||
import 'package:cake_wallet/view_model/link_view_model.dart';
|
import 'package:cake_wallet/view_model/link_view_model.dart';
|
||||||
|
@ -61,7 +66,6 @@ import 'package:cake_wallet/src/screens/anonpay_details/anonpay_details_page.dar
|
||||||
import 'package:cake_wallet/src/screens/auth/auth_page.dart';
|
import 'package:cake_wallet/src/screens/auth/auth_page.dart';
|
||||||
import 'package:cake_wallet/src/screens/backup/backup_page.dart';
|
import 'package:cake_wallet/src/screens/backup/backup_page.dart';
|
||||||
import 'package:cake_wallet/src/screens/backup/edit_backup_password_page.dart';
|
import 'package:cake_wallet/src/screens/backup/edit_backup_password_page.dart';
|
||||||
import 'package:cake_wallet/src/screens/buy/buy_options_page.dart';
|
|
||||||
import 'package:cake_wallet/src/screens/buy/buy_webview_page.dart';
|
import 'package:cake_wallet/src/screens/buy/buy_webview_page.dart';
|
||||||
import 'package:cake_wallet/src/screens/buy/webview_page.dart';
|
import 'package:cake_wallet/src/screens/buy/webview_page.dart';
|
||||||
import 'package:cake_wallet/src/screens/contact/contact_list_page.dart';
|
import 'package:cake_wallet/src/screens/contact/contact_list_page.dart';
|
||||||
|
@ -125,6 +129,7 @@ import 'package:cake_wallet/src/screens/subaddress/address_edit_or_create_page.d
|
||||||
import 'package:cake_wallet/src/screens/support/support_page.dart';
|
import 'package:cake_wallet/src/screens/support/support_page.dart';
|
||||||
import 'package:cake_wallet/src/screens/support_chat/support_chat_page.dart';
|
import 'package:cake_wallet/src/screens/support_chat/support_chat_page.dart';
|
||||||
import 'package:cake_wallet/src/screens/support_other_links/support_other_links_page.dart';
|
import 'package:cake_wallet/src/screens/support_other_links/support_other_links_page.dart';
|
||||||
|
import 'package:cake_wallet/src/screens/ur/animated_ur_page.dart';
|
||||||
import 'package:cake_wallet/src/screens/wallet/wallet_edit_page.dart';
|
import 'package:cake_wallet/src/screens/wallet/wallet_edit_page.dart';
|
||||||
import 'package:cake_wallet/src/screens/wallet_connect/wc_connections_listing_view.dart';
|
import 'package:cake_wallet/src/screens/wallet_connect/wc_connections_listing_view.dart';
|
||||||
import 'package:cake_wallet/src/screens/wallet_unlock/wallet_unlock_arguments.dart';
|
import 'package:cake_wallet/src/screens/wallet_unlock/wallet_unlock_arguments.dart';
|
||||||
|
@ -134,6 +139,8 @@ import 'package:cake_wallet/utils/device_info.dart';
|
||||||
import 'package:cake_wallet/store/anonpay/anonpay_transactions_store.dart';
|
import 'package:cake_wallet/store/anonpay/anonpay_transactions_store.dart';
|
||||||
import 'package:cake_wallet/utils/payment_request.dart';
|
import 'package:cake_wallet/utils/payment_request.dart';
|
||||||
import 'package:cake_wallet/utils/responsive_layout_util.dart';
|
import 'package:cake_wallet/utils/responsive_layout_util.dart';
|
||||||
|
import 'package:cake_wallet/view_model/buy/buy_sell_view_model.dart';
|
||||||
|
import 'package:cake_wallet/view_model/animated_ur_model.dart';
|
||||||
import 'package:cake_wallet/view_model/dashboard/desktop_sidebar_view_model.dart';
|
import 'package:cake_wallet/view_model/dashboard/desktop_sidebar_view_model.dart';
|
||||||
import 'package:cake_wallet/view_model/anon_invoice_page_view_model.dart';
|
import 'package:cake_wallet/view_model/anon_invoice_page_view_model.dart';
|
||||||
import 'package:cake_wallet/view_model/anonpay_details_view_model.dart';
|
import 'package:cake_wallet/view_model/anonpay_details_view_model.dart';
|
||||||
|
@ -179,7 +186,6 @@ import 'package:cake_wallet/src/screens/transaction_details/transaction_details_
|
||||||
import 'package:cake_wallet/src/screens/unspent_coins/unspent_coins_details_page.dart';
|
import 'package:cake_wallet/src/screens/unspent_coins/unspent_coins_details_page.dart';
|
||||||
import 'package:cake_wallet/src/screens/unspent_coins/unspent_coins_list_page.dart';
|
import 'package:cake_wallet/src/screens/unspent_coins/unspent_coins_list_page.dart';
|
||||||
import 'package:cake_wallet/src/screens/wallet_keys/wallet_keys_page.dart';
|
import 'package:cake_wallet/src/screens/wallet_keys/wallet_keys_page.dart';
|
||||||
import 'package:cake_wallet/src/screens/wallet_list/wallet_list_page.dart';
|
|
||||||
import 'package:cake_wallet/store/app_store.dart';
|
import 'package:cake_wallet/store/app_store.dart';
|
||||||
import 'package:cake_wallet/store/authentication_store.dart';
|
import 'package:cake_wallet/store/authentication_store.dart';
|
||||||
import 'package:cake_wallet/store/dashboard/fiat_conversion_store.dart';
|
import 'package:cake_wallet/store/dashboard/fiat_conversion_store.dart';
|
||||||
|
@ -246,6 +252,8 @@ import 'package:get_it/get_it.dart';
|
||||||
import 'package:hive/hive.dart';
|
import 'package:hive/hive.dart';
|
||||||
import 'package:mobx/mobx.dart';
|
import 'package:mobx/mobx.dart';
|
||||||
import 'package:shared_preferences/shared_preferences.dart';
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
|
import 'buy/meld/meld_buy_provider.dart';
|
||||||
|
import 'src/screens/buy/buy_sell_page.dart';
|
||||||
import 'cake_pay/cake_pay_payment_credantials.dart';
|
import 'cake_pay/cake_pay_payment_credantials.dart';
|
||||||
|
|
||||||
final getIt = GetIt.instance;
|
final getIt = GetIt.instance;
|
||||||
|
@ -579,7 +587,7 @@ Future<void> setup({
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
// wallet is already loaded:
|
// wallet is already loaded:
|
||||||
if (appStore.wallet != null) {
|
if (appStore.wallet != null || requireHardwareWalletConnection()) {
|
||||||
// goes to the dashboard:
|
// goes to the dashboard:
|
||||||
authStore.allowed();
|
authStore.allowed();
|
||||||
// trigger any deep links:
|
// trigger any deep links:
|
||||||
|
@ -773,10 +781,12 @@ Future<void> setup({
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
getIt.registerFactory(() => WalletListPage(
|
getIt.registerFactoryParam<WalletListPage, Function(BuildContext)?, void>(
|
||||||
walletListViewModel: getIt.get<WalletListViewModel>(),
|
(Function(BuildContext)? onWalletLoaded, _) => WalletListPage(
|
||||||
authService: getIt.get<AuthService>(),
|
walletListViewModel: getIt.get<WalletListViewModel>(),
|
||||||
));
|
authService: getIt.get<AuthService>(),
|
||||||
|
onWalletLoaded: onWalletLoaded,
|
||||||
|
));
|
||||||
|
|
||||||
getIt.registerFactoryParam<WalletEditViewModel, WalletListViewModel, void>(
|
getIt.registerFactoryParam<WalletEditViewModel, WalletListViewModel, void>(
|
||||||
(WalletListViewModel walletListViewModel, _) => WalletEditViewModel(
|
(WalletListViewModel walletListViewModel, _) => WalletEditViewModel(
|
||||||
|
@ -903,6 +913,11 @@ Future<void> setup({
|
||||||
getIt.registerFactory(() => WalletKeysViewModel(getIt.get<AppStore>()));
|
getIt.registerFactory(() => WalletKeysViewModel(getIt.get<AppStore>()));
|
||||||
|
|
||||||
getIt.registerFactory(() => WalletKeysPage(getIt.get<WalletKeysViewModel>()));
|
getIt.registerFactory(() => WalletKeysPage(getIt.get<WalletKeysViewModel>()));
|
||||||
|
|
||||||
|
getIt.registerFactory(() => AnimatedURModel(getIt.get<AppStore>()));
|
||||||
|
|
||||||
|
getIt.registerFactoryParam<AnimatedURPage, String, void>((String urQr, _) =>
|
||||||
|
AnimatedURPage(getIt.get<AnimatedURModel>(), urQr: urQr));
|
||||||
|
|
||||||
getIt.registerFactoryParam<ContactViewModel, ContactRecord?, void>(
|
getIt.registerFactoryParam<ContactViewModel, ContactRecord?, void>(
|
||||||
(ContactRecord? contact, _) => ContactViewModel(_contactSource, contact: contact));
|
(ContactRecord? contact, _) => ContactViewModel(_contactSource, contact: contact));
|
||||||
|
@ -997,6 +1012,10 @@ Future<void> setup({
|
||||||
wallet: getIt.get<AppStore>().wallet!,
|
wallet: getIt.get<AppStore>().wallet!,
|
||||||
));
|
));
|
||||||
|
|
||||||
|
getIt.registerFactory<MeldBuyProvider>(() => MeldBuyProvider(
|
||||||
|
wallet: getIt.get<AppStore>().wallet!,
|
||||||
|
));
|
||||||
|
|
||||||
getIt.registerFactoryParam<WebViewPage, String, Uri>((title, uri) => WebViewPage(title, uri));
|
getIt.registerFactoryParam<WebViewPage, String, Uri>((title, uri) => WebViewPage(title, uri));
|
||||||
|
|
||||||
getIt.registerFactory<PayfuraBuyProvider>(() => PayfuraBuyProvider(
|
getIt.registerFactory<PayfuraBuyProvider>(() => PayfuraBuyProvider(
|
||||||
|
@ -1186,8 +1205,25 @@ Future<void> setup({
|
||||||
|
|
||||||
getIt.registerFactory(() => BuyAmountViewModel());
|
getIt.registerFactory(() => BuyAmountViewModel());
|
||||||
|
|
||||||
getIt.registerFactoryParam<BuySellOptionsPage, bool, void>(
|
getIt.registerFactory(() => BuySellViewModel(getIt.get<AppStore>()));
|
||||||
(isBuyOption, _) => BuySellOptionsPage(getIt.get<DashboardViewModel>(), isBuyOption));
|
|
||||||
|
getIt.registerFactory(() => BuySellPage(getIt.get<BuySellViewModel>()));
|
||||||
|
|
||||||
|
getIt.registerFactoryParam<BuyOptionsPage, List<dynamic>, void>((List<dynamic> args, _) {
|
||||||
|
final items = args.first as List<SelectableItem>;
|
||||||
|
final pickAnOption = args[1] as void Function(SelectableOption option)?;
|
||||||
|
final confirmOption = args[2] as void Function(BuildContext contex)?;
|
||||||
|
return BuyOptionsPage(
|
||||||
|
items: items, pickAnOption: pickAnOption, confirmOption: confirmOption);
|
||||||
|
});
|
||||||
|
|
||||||
|
getIt.registerFactoryParam<PaymentMethodOptionsPage, List<dynamic>, void>((List<dynamic> args, _) {
|
||||||
|
final items = args.first as List<SelectableOption>;
|
||||||
|
final pickAnOption = args[1] as void Function(SelectableOption option)?;
|
||||||
|
|
||||||
|
return PaymentMethodOptionsPage(
|
||||||
|
items: items, pickAnOption: pickAnOption);
|
||||||
|
});
|
||||||
|
|
||||||
getIt.registerFactory(() {
|
getIt.registerFactory(() {
|
||||||
final wallet = getIt.get<AppStore>().wallet;
|
final wallet = getIt.get<AppStore>().wallet;
|
||||||
|
|
|
@ -252,13 +252,19 @@ Future<void> defaultSettingsMigration(
|
||||||
await removeMoneroWorld(sharedPreferences: sharedPreferences, nodes: nodes);
|
await removeMoneroWorld(sharedPreferences: sharedPreferences, nodes: nodes);
|
||||||
break;
|
break;
|
||||||
case 41:
|
case 41:
|
||||||
_deselectQuantex(sharedPreferences);
|
_deselectExchangeProvider(sharedPreferences, "Quantex");
|
||||||
await _addSethNode(nodes, sharedPreferences);
|
await _addSethNode(nodes, sharedPreferences);
|
||||||
await updateTronNodesWithNowNodes(sharedPreferences: sharedPreferences, nodes: nodes);
|
await updateTronNodesWithNowNodes(sharedPreferences: sharedPreferences, nodes: nodes);
|
||||||
break;
|
break;
|
||||||
case 42:
|
case 42:
|
||||||
updateBtcElectrumNodeToUseSSL(nodes, sharedPreferences);
|
updateBtcElectrumNodeToUseSSL(nodes, sharedPreferences);
|
||||||
break;
|
break;
|
||||||
|
case 43:
|
||||||
|
_updateCakeXmrNode(nodes);
|
||||||
|
_deselectExchangeProvider(sharedPreferences, "THORChain");
|
||||||
|
_deselectExchangeProvider(sharedPreferences, "SimpleSwap");
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -273,6 +279,15 @@ Future<void> defaultSettingsMigration(
|
||||||
await sharedPreferences.setInt(PreferencesKey.currentDefaultSettingsMigrationVersion, version);
|
await sharedPreferences.setInt(PreferencesKey.currentDefaultSettingsMigrationVersion, version);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _updateCakeXmrNode(Box<Node> nodes) {
|
||||||
|
final node = nodes.values.firstWhereOrNull((element) => element.uriRaw == newCakeWalletMoneroUri);
|
||||||
|
|
||||||
|
if (node != null && !node.trusted) {
|
||||||
|
node.trusted = true;
|
||||||
|
node.save();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void updateBtcElectrumNodeToUseSSL(Box<Node> nodes, SharedPreferences sharedPreferences) {
|
void updateBtcElectrumNodeToUseSSL(Box<Node> nodes, SharedPreferences sharedPreferences) {
|
||||||
final btcElectrumNode = nodes.values.firstWhereOrNull((element) => element.uriRaw == newCakeWalletBitcoinUri);
|
final btcElectrumNode = nodes.values.firstWhereOrNull((element) => element.uriRaw == newCakeWalletBitcoinUri);
|
||||||
|
|
||||||
|
@ -282,12 +297,12 @@ void updateBtcElectrumNodeToUseSSL(Box<Node> nodes, SharedPreferences sharedPref
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void _deselectQuantex(SharedPreferences sharedPreferences) {
|
void _deselectExchangeProvider(SharedPreferences sharedPreferences, String providerName) {
|
||||||
final Map<String, dynamic> exchangeProvidersSelection =
|
final Map<String, dynamic> exchangeProvidersSelection =
|
||||||
json.decode(sharedPreferences.getString(PreferencesKey.exchangeProvidersSelection) ?? "{}")
|
json.decode(sharedPreferences.getString(PreferencesKey.exchangeProvidersSelection) ?? "{}")
|
||||||
as Map<String, dynamic>;
|
as Map<String, dynamic>;
|
||||||
|
|
||||||
exchangeProvidersSelection['Quantex'] = false;
|
exchangeProvidersSelection[providerName] = false;
|
||||||
|
|
||||||
sharedPreferences.setString(
|
sharedPreferences.setString(
|
||||||
PreferencesKey.exchangeProvidersSelection,
|
PreferencesKey.exchangeProvidersSelection,
|
||||||
|
@ -843,7 +858,7 @@ Future<void> changeDefaultMoneroNode(
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
final newCakeWalletNode = Node(uri: newCakeWalletMoneroUri, type: WalletType.monero);
|
final newCakeWalletNode = Node(uri: newCakeWalletMoneroUri, type: WalletType.monero, trusted: true);
|
||||||
|
|
||||||
await nodeSource.add(newCakeWalletNode);
|
await nodeSource.add(newCakeWalletNode);
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
import 'package:cake_wallet/core/wallet_loading_service.dart';
|
||||||
|
import 'package:cake_wallet/di.dart';
|
||||||
|
import 'package:cake_wallet/entities/preferences_key.dart';
|
||||||
|
import 'package:cw_core/wallet_type.dart';
|
||||||
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
|
|
||||||
|
bool requireHardwareWalletConnection() {
|
||||||
|
final name = getIt
|
||||||
|
.get<SharedPreferences>()
|
||||||
|
.getString(PreferencesKey.currentWalletName);
|
||||||
|
final typeRaw =
|
||||||
|
getIt.get<SharedPreferences>().getInt(PreferencesKey.currentWalletType);
|
||||||
|
|
||||||
|
if (typeRaw == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (name == null) {
|
||||||
|
throw Exception('Incorrect current wallet name: $name');
|
||||||
|
}
|
||||||
|
|
||||||
|
final type = deserializeFromInt(typeRaw);
|
||||||
|
final walletLoadingService = getIt.get<WalletLoadingService>();
|
||||||
|
return walletLoadingService.requireHardwareWalletConnection(type, name);
|
||||||
|
}
|
|
@ -23,31 +23,18 @@ class MainActions {
|
||||||
});
|
});
|
||||||
|
|
||||||
static List<MainActions> all = [
|
static List<MainActions> all = [
|
||||||
buyAction,
|
showWalletsAction,
|
||||||
receiveAction,
|
receiveAction,
|
||||||
exchangeAction,
|
exchangeAction,
|
||||||
sendAction,
|
sendAction,
|
||||||
sellAction,
|
tradeAction,
|
||||||
];
|
];
|
||||||
|
|
||||||
static MainActions buyAction = MainActions._(
|
static MainActions showWalletsAction = MainActions._(
|
||||||
name: (context) => S.of(context).buy,
|
name: (context) => S.of(context).wallets,
|
||||||
image: 'assets/images/buy.png',
|
image: 'assets/images/wallet_new.png',
|
||||||
isEnabled: (viewModel) => viewModel.isEnabledBuyAction,
|
|
||||||
canShow: (viewModel) => viewModel.hasBuyAction,
|
|
||||||
onTap: (BuildContext context, DashboardViewModel viewModel) async {
|
onTap: (BuildContext context, DashboardViewModel viewModel) async {
|
||||||
if (!viewModel.isEnabledBuyAction) {
|
Navigator.pushNamed(context, Routes.walletList);
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
final defaultBuyProvider = viewModel.defaultBuyProvider;
|
|
||||||
try {
|
|
||||||
defaultBuyProvider != null
|
|
||||||
? await defaultBuyProvider.launchProvider(context, true)
|
|
||||||
: await Navigator.of(context).pushNamed(Routes.buySellPage, arguments: true);
|
|
||||||
} catch (e) {
|
|
||||||
await _showErrorDialog(context, defaultBuyProvider.toString(), e.toString());
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -79,39 +66,15 @@ class MainActions {
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
static MainActions sellAction = MainActions._(
|
|
||||||
name: (context) => S.of(context).sell,
|
|
||||||
image: 'assets/images/sell.png',
|
|
||||||
isEnabled: (viewModel) => viewModel.isEnabledSellAction,
|
|
||||||
canShow: (viewModel) => viewModel.hasSellAction,
|
|
||||||
onTap: (BuildContext context, DashboardViewModel viewModel) async {
|
|
||||||
if (!viewModel.isEnabledSellAction) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
final defaultSellProvider = viewModel.defaultSellProvider;
|
static MainActions tradeAction = MainActions._(
|
||||||
try {
|
name: (context) => '${S.of(context).buy} / ${S.of(context).sell}',
|
||||||
defaultSellProvider != null
|
image: 'assets/images/buy_sell.png',
|
||||||
? await defaultSellProvider.launchProvider(context, false)
|
isEnabled: (viewModel) => viewModel.isEnabledTradeAction,
|
||||||
: await Navigator.of(context).pushNamed(Routes.buySellPage, arguments: false);
|
canShow: (viewModel) => viewModel.hasTradeAction,
|
||||||
} catch (e) {
|
onTap: (BuildContext context, DashboardViewModel viewModel) async {
|
||||||
await _showErrorDialog(context, defaultSellProvider.toString(), e.toString());
|
if (!viewModel.isEnabledTradeAction) return;
|
||||||
}
|
await Navigator.of(context).pushNamed(Routes.buySellPage, arguments: false);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
static Future<void> _showErrorDialog(
|
|
||||||
BuildContext context, String title, String errorMessage) async {
|
|
||||||
await showPopUp<void>(
|
|
||||||
context: context,
|
|
||||||
builder: (BuildContext context) {
|
|
||||||
return AlertWithOneAction(
|
|
||||||
alertTitle: title,
|
|
||||||
alertContent: errorMessage,
|
|
||||||
buttonText: S.of(context).ok,
|
|
||||||
buttonAction: () => Navigator.of(context).pop(),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -41,7 +41,8 @@ class AddressResolver {
|
||||||
'kresus',
|
'kresus',
|
||||||
'anime',
|
'anime',
|
||||||
'manga',
|
'manga',
|
||||||
'binanceus'
|
'binanceus',
|
||||||
|
'xmr',
|
||||||
];
|
];
|
||||||
|
|
||||||
static String? extractAddressByType({required String raw, required CryptoCurrency type}) {
|
static String? extractAddressByType({required String raw, required CryptoCurrency type}) {
|
||||||
|
|
|
@ -21,10 +21,8 @@ class PreferencesKey {
|
||||||
static const currentBalanceDisplayModeKey = 'current_balance_display_mode';
|
static const currentBalanceDisplayModeKey = 'current_balance_display_mode';
|
||||||
static const shouldSaveRecipientAddressKey = 'save_recipient_address';
|
static const shouldSaveRecipientAddressKey = 'save_recipient_address';
|
||||||
static const isAppSecureKey = 'is_app_secure';
|
static const isAppSecureKey = 'is_app_secure';
|
||||||
static const disableBuyKey = 'disable_buy';
|
static const disableTradeOption = 'disable_buy';
|
||||||
static const disableSellKey = 'disable_sell';
|
|
||||||
static const disableBulletinKey = 'disable_bulletin';
|
static const disableBulletinKey = 'disable_bulletin';
|
||||||
static const defaultBuyProvider = 'default_buy_provider';
|
|
||||||
static const walletListOrder = 'wallet_list_order';
|
static const walletListOrder = 'wallet_list_order';
|
||||||
static const contactListOrder = 'contact_list_order';
|
static const contactListOrder = 'contact_list_order';
|
||||||
static const walletListAscending = 'wallet_list_ascending';
|
static const walletListAscending = 'wallet_list_ascending';
|
||||||
|
|
|
@ -1,24 +1,18 @@
|
||||||
import 'package:cake_wallet/buy/buy_provider.dart';
|
import 'package:cake_wallet/buy/buy_provider.dart';
|
||||||
import 'package:cake_wallet/buy/dfx/dfx_buy_provider.dart';
|
import 'package:cake_wallet/buy/dfx/dfx_buy_provider.dart';
|
||||||
|
import 'package:cake_wallet/buy/meld/meld_buy_provider.dart';
|
||||||
import 'package:cake_wallet/buy/moonpay/moonpay_provider.dart';
|
import 'package:cake_wallet/buy/moonpay/moonpay_provider.dart';
|
||||||
import 'package:cake_wallet/buy/onramper/onramper_buy_provider.dart';
|
import 'package:cake_wallet/buy/onramper/onramper_buy_provider.dart';
|
||||||
import 'package:cake_wallet/buy/robinhood/robinhood_buy_provider.dart';
|
import 'package:cake_wallet/buy/robinhood/robinhood_buy_provider.dart';
|
||||||
import 'package:cake_wallet/di.dart';
|
import 'package:cake_wallet/di.dart';
|
||||||
import 'package:cw_core/wallet_type.dart';
|
import 'package:cw_core/wallet_type.dart';
|
||||||
|
import 'package:http/http.dart';
|
||||||
|
|
||||||
enum ProviderType {
|
enum ProviderType { robinhood, dfx, onramper, moonpay, meld }
|
||||||
askEachTime,
|
|
||||||
robinhood,
|
|
||||||
dfx,
|
|
||||||
onramper,
|
|
||||||
moonpay,
|
|
||||||
}
|
|
||||||
|
|
||||||
extension ProviderTypeName on ProviderType {
|
extension ProviderTypeName on ProviderType {
|
||||||
String get title {
|
String get title {
|
||||||
switch (this) {
|
switch (this) {
|
||||||
case ProviderType.askEachTime:
|
|
||||||
return 'Ask each time';
|
|
||||||
case ProviderType.robinhood:
|
case ProviderType.robinhood:
|
||||||
return 'Robinhood Connect';
|
return 'Robinhood Connect';
|
||||||
case ProviderType.dfx:
|
case ProviderType.dfx:
|
||||||
|
@ -27,13 +21,13 @@ extension ProviderTypeName on ProviderType {
|
||||||
return 'Onramper';
|
return 'Onramper';
|
||||||
case ProviderType.moonpay:
|
case ProviderType.moonpay:
|
||||||
return 'MoonPay';
|
return 'MoonPay';
|
||||||
|
case ProviderType.meld:
|
||||||
|
return 'Meld';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
String get id {
|
String get id {
|
||||||
switch (this) {
|
switch (this) {
|
||||||
case ProviderType.askEachTime:
|
|
||||||
return 'ask_each_time_provider';
|
|
||||||
case ProviderType.robinhood:
|
case ProviderType.robinhood:
|
||||||
return 'robinhood_connect_provider';
|
return 'robinhood_connect_provider';
|
||||||
case ProviderType.dfx:
|
case ProviderType.dfx:
|
||||||
|
@ -42,6 +36,8 @@ extension ProviderTypeName on ProviderType {
|
||||||
return 'onramper_provider';
|
return 'onramper_provider';
|
||||||
case ProviderType.moonpay:
|
case ProviderType.moonpay:
|
||||||
return 'moonpay_provider';
|
return 'moonpay_provider';
|
||||||
|
case ProviderType.meld:
|
||||||
|
return 'meld_provider';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -52,14 +48,13 @@ class ProvidersHelper {
|
||||||
case WalletType.nano:
|
case WalletType.nano:
|
||||||
case WalletType.banano:
|
case WalletType.banano:
|
||||||
case WalletType.wownero:
|
case WalletType.wownero:
|
||||||
return [ProviderType.askEachTime, ProviderType.onramper];
|
return [ProviderType.onramper];
|
||||||
case WalletType.monero:
|
case WalletType.monero:
|
||||||
return [ProviderType.askEachTime, ProviderType.onramper, ProviderType.dfx];
|
return [ProviderType.onramper, ProviderType.dfx];
|
||||||
case WalletType.bitcoin:
|
case WalletType.bitcoin:
|
||||||
case WalletType.polygon:
|
case WalletType.polygon:
|
||||||
case WalletType.ethereum:
|
case WalletType.ethereum:
|
||||||
return [
|
return [
|
||||||
ProviderType.askEachTime,
|
|
||||||
ProviderType.onramper,
|
ProviderType.onramper,
|
||||||
ProviderType.dfx,
|
ProviderType.dfx,
|
||||||
ProviderType.robinhood,
|
ProviderType.robinhood,
|
||||||
|
@ -68,10 +63,13 @@ class ProvidersHelper {
|
||||||
case WalletType.litecoin:
|
case WalletType.litecoin:
|
||||||
case WalletType.bitcoinCash:
|
case WalletType.bitcoinCash:
|
||||||
case WalletType.solana:
|
case WalletType.solana:
|
||||||
return [ProviderType.askEachTime, ProviderType.onramper, ProviderType.robinhood, ProviderType.moonpay];
|
return [
|
||||||
|
ProviderType.onramper,
|
||||||
|
ProviderType.robinhood,
|
||||||
|
ProviderType.moonpay
|
||||||
|
];
|
||||||
case WalletType.tron:
|
case WalletType.tron:
|
||||||
return [
|
return [
|
||||||
ProviderType.askEachTime,
|
|
||||||
ProviderType.onramper,
|
ProviderType.onramper,
|
||||||
ProviderType.robinhood,
|
ProviderType.robinhood,
|
||||||
ProviderType.moonpay,
|
ProviderType.moonpay,
|
||||||
|
@ -88,28 +86,24 @@ class ProvidersHelper {
|
||||||
case WalletType.ethereum:
|
case WalletType.ethereum:
|
||||||
case WalletType.polygon:
|
case WalletType.polygon:
|
||||||
return [
|
return [
|
||||||
ProviderType.askEachTime,
|
|
||||||
ProviderType.onramper,
|
ProviderType.onramper,
|
||||||
ProviderType.moonpay,
|
ProviderType.moonpay,
|
||||||
ProviderType.dfx,
|
ProviderType.dfx,
|
||||||
];
|
];
|
||||||
case WalletType.litecoin:
|
case WalletType.litecoin:
|
||||||
case WalletType.bitcoinCash:
|
case WalletType.bitcoinCash:
|
||||||
return [ProviderType.askEachTime, ProviderType.moonpay];
|
return [ProviderType.moonpay];
|
||||||
case WalletType.solana:
|
case WalletType.solana:
|
||||||
return [
|
return [
|
||||||
ProviderType.askEachTime,
|
|
||||||
ProviderType.onramper,
|
ProviderType.onramper,
|
||||||
ProviderType.robinhood,
|
|
||||||
ProviderType.moonpay,
|
ProviderType.moonpay,
|
||||||
];
|
];
|
||||||
case WalletType.tron:
|
case WalletType.tron:
|
||||||
return [
|
return [
|
||||||
ProviderType.askEachTime,
|
|
||||||
ProviderType.robinhood,
|
|
||||||
ProviderType.moonpay,
|
ProviderType.moonpay,
|
||||||
];
|
];
|
||||||
case WalletType.monero:
|
case WalletType.monero:
|
||||||
|
return [ProviderType.dfx];
|
||||||
case WalletType.nano:
|
case WalletType.nano:
|
||||||
case WalletType.banano:
|
case WalletType.banano:
|
||||||
case WalletType.none:
|
case WalletType.none:
|
||||||
|
@ -129,7 +123,9 @@ class ProvidersHelper {
|
||||||
return getIt.get<OnRamperBuyProvider>();
|
return getIt.get<OnRamperBuyProvider>();
|
||||||
case ProviderType.moonpay:
|
case ProviderType.moonpay:
|
||||||
return getIt.get<MoonPayProvider>();
|
return getIt.get<MoonPayProvider>();
|
||||||
case ProviderType.askEachTime:
|
case ProviderType.meld:
|
||||||
|
return getIt.get<MeldBuyProvider>();
|
||||||
|
default:
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,15 +1,376 @@
|
||||||
import 'package:barcode_scan2/barcode_scan2.dart';
|
import 'dart:math';
|
||||||
|
|
||||||
|
import 'package:cake_wallet/generated/i18n.dart';
|
||||||
|
import 'package:cake_wallet/main.dart';
|
||||||
|
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
|
||||||
|
import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart';
|
||||||
|
import 'package:cake_wallet/utils/show_pop_up.dart';
|
||||||
|
import 'package:fast_scanner/fast_scanner.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/scheduler.dart';
|
||||||
|
|
||||||
var isQrScannerShown = false;
|
var isQrScannerShown = false;
|
||||||
|
|
||||||
Future<String> presentQRScanner() async {
|
Future<String> presentQRScanner(BuildContext context) async {
|
||||||
isQrScannerShown = true;
|
isQrScannerShown = true;
|
||||||
try {
|
try {
|
||||||
final result = await BarcodeScanner.scan();
|
final result = await Navigator.of(context).push<String>(
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder:(context) {
|
||||||
|
return BarcodeScannerSimple();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
isQrScannerShown = false;
|
isQrScannerShown = false;
|
||||||
return result.rawContent.trim();
|
return result??'';
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
isQrScannerShown = false;
|
isQrScannerShown = false;
|
||||||
rethrow;
|
rethrow;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// https://github.com/MrCyjaneK/fast_scanner/blob/master/example/lib/barcode_scanner_simple.dart
|
||||||
|
class BarcodeScannerSimple extends StatefulWidget {
|
||||||
|
const BarcodeScannerSimple({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<BarcodeScannerSimple> createState() => _BarcodeScannerSimpleState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _BarcodeScannerSimpleState extends State<BarcodeScannerSimple> {
|
||||||
|
Barcode? _barcode;
|
||||||
|
bool popped = false;
|
||||||
|
|
||||||
|
List<String> urCodes = [];
|
||||||
|
late var ur = URQRToURQRData(urCodes);
|
||||||
|
|
||||||
|
void _handleBarcode(BarcodeCapture barcodes) {
|
||||||
|
try {
|
||||||
|
_handleBarcodeInternal(barcodes);
|
||||||
|
} catch (e) {
|
||||||
|
showPopUp<void>(
|
||||||
|
context: context,
|
||||||
|
builder: (context) {
|
||||||
|
return AlertWithOneAction(
|
||||||
|
alertTitle: S.of(context).error,
|
||||||
|
alertContent: S.of(context).error_dialog_content,
|
||||||
|
buttonText: 'ok',
|
||||||
|
buttonAction: () {
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
print(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _handleBarcodeInternal(BarcodeCapture barcodes) {
|
||||||
|
for (final barcode in barcodes.barcodes) {
|
||||||
|
// don't handle unknown QR codes
|
||||||
|
if (barcode.rawValue?.trim().isEmpty??false == false) continue;
|
||||||
|
if (barcode.rawValue!.startsWith("ur:")) {
|
||||||
|
if (urCodes.contains(barcode.rawValue)) continue;
|
||||||
|
setState(() {
|
||||||
|
urCodes.add(barcode.rawValue!);
|
||||||
|
ur = URQRToURQRData(urCodes);
|
||||||
|
});
|
||||||
|
if (ur.progress == 1) {
|
||||||
|
setState(() {
|
||||||
|
popped = true;
|
||||||
|
});
|
||||||
|
SchedulerBinding.instance.addPostFrameCallback((_) {
|
||||||
|
Navigator.of(context).pop(ur.inputs.join("\n"));
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (urCodes.isNotEmpty) return;
|
||||||
|
if (mounted) {
|
||||||
|
setState(() {
|
||||||
|
_barcode = barcodes.barcodes.firstOrNull;
|
||||||
|
});
|
||||||
|
if (_barcode != null && popped != true) {
|
||||||
|
setState(() {
|
||||||
|
popped = true;
|
||||||
|
});
|
||||||
|
SchedulerBinding.instance.addPostFrameCallback((_) {
|
||||||
|
Navigator.of(context).pop(_barcode?.rawValue ?? "");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final MobileScannerController ctrl = MobileScannerController();
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
title: const Text('Scan'),
|
||||||
|
actions: [
|
||||||
|
SwitchCameraButton(controller: ctrl),
|
||||||
|
ToggleFlashlightButton(controller: ctrl),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
backgroundColor: Colors.black,
|
||||||
|
body: Stack(
|
||||||
|
children: [
|
||||||
|
MobileScanner(
|
||||||
|
onDetect: _handleBarcode,
|
||||||
|
controller: ctrl,
|
||||||
|
),
|
||||||
|
if (ur.inputs.length != 0)
|
||||||
|
Center(child:
|
||||||
|
Text(
|
||||||
|
"${ur.inputs.length}/${ur.count}",
|
||||||
|
style: Theme.of(context).textTheme.displayLarge?.copyWith(color: Colors.white)
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
child: Center(
|
||||||
|
child: SizedBox(
|
||||||
|
width: 250,
|
||||||
|
height: 250,
|
||||||
|
child: CustomPaint(
|
||||||
|
painter: ProgressPainter(
|
||||||
|
urQrProgress: URQrProgress(
|
||||||
|
expectedPartCount: ur.count - 1,
|
||||||
|
processedPartsCount: ur.inputs.length,
|
||||||
|
receivedPartIndexes: _urParts(),
|
||||||
|
percentage: ur.progress,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<int> _urParts() {
|
||||||
|
List<int> l = [];
|
||||||
|
for (var inp in ur.inputs) {
|
||||||
|
try {
|
||||||
|
l.add(int.parse(inp.split("/")[1].split("-")[0]));
|
||||||
|
} catch (e) {}
|
||||||
|
}
|
||||||
|
return l;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class ToggleFlashlightButton extends StatelessWidget {
|
||||||
|
const ToggleFlashlightButton({required this.controller, super.key});
|
||||||
|
|
||||||
|
final MobileScannerController controller;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return ValueListenableBuilder(
|
||||||
|
valueListenable: controller,
|
||||||
|
builder: (context, state, child) {
|
||||||
|
if (!state.isInitialized || !state.isRunning) {
|
||||||
|
return const SizedBox.shrink();
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (state.torchState) {
|
||||||
|
case TorchState.auto:
|
||||||
|
return IconButton(
|
||||||
|
iconSize: 32.0,
|
||||||
|
icon: const Icon(Icons.flash_auto),
|
||||||
|
onPressed: () async {
|
||||||
|
await controller.toggleTorch();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
case TorchState.off:
|
||||||
|
return IconButton(
|
||||||
|
iconSize: 32.0,
|
||||||
|
icon: const Icon(Icons.flash_off),
|
||||||
|
onPressed: () async {
|
||||||
|
await controller.toggleTorch();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
case TorchState.on:
|
||||||
|
return IconButton(
|
||||||
|
iconSize: 32.0,
|
||||||
|
icon: const Icon(Icons.flash_on),
|
||||||
|
onPressed: () async {
|
||||||
|
await controller.toggleTorch();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
case TorchState.unavailable:
|
||||||
|
return const Icon(
|
||||||
|
Icons.no_flash,
|
||||||
|
color: Colors.grey,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class SwitchCameraButton extends StatelessWidget {
|
||||||
|
const SwitchCameraButton({required this.controller, super.key});
|
||||||
|
|
||||||
|
final MobileScannerController controller;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return ValueListenableBuilder(
|
||||||
|
valueListenable: controller,
|
||||||
|
builder: (context, state, child) {
|
||||||
|
if (!state.isInitialized || !state.isRunning) {
|
||||||
|
return const SizedBox.shrink();
|
||||||
|
}
|
||||||
|
|
||||||
|
final int? availableCameras = state.availableCameras;
|
||||||
|
|
||||||
|
if (availableCameras != null && availableCameras < 2) {
|
||||||
|
return const SizedBox.shrink();
|
||||||
|
}
|
||||||
|
|
||||||
|
final Widget icon;
|
||||||
|
|
||||||
|
switch (state.cameraDirection) {
|
||||||
|
case CameraFacing.front:
|
||||||
|
icon = const Icon(Icons.camera_front);
|
||||||
|
case CameraFacing.back:
|
||||||
|
icon = const Icon(Icons.camera_rear);
|
||||||
|
}
|
||||||
|
|
||||||
|
return IconButton(
|
||||||
|
iconSize: 32.0,
|
||||||
|
icon: icon,
|
||||||
|
onPressed: () async {
|
||||||
|
await controller.switchCamera();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class URQRData {
|
||||||
|
URQRData(
|
||||||
|
{required this.tag,
|
||||||
|
required this.str,
|
||||||
|
required this.progress,
|
||||||
|
required this.count,
|
||||||
|
required this.error,
|
||||||
|
required this.inputs});
|
||||||
|
final String tag;
|
||||||
|
final String str;
|
||||||
|
final double progress;
|
||||||
|
final int count;
|
||||||
|
final String error;
|
||||||
|
final List<String> inputs;
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
return {
|
||||||
|
"tag": tag,
|
||||||
|
"str": str,
|
||||||
|
"progress": progress,
|
||||||
|
"count": count,
|
||||||
|
"error": error,
|
||||||
|
"inputs": inputs,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
URQRData URQRToURQRData(List<String> urqr_) {
|
||||||
|
final urqr = urqr_.toSet().toList();
|
||||||
|
urqr.sort((s1, s2) {
|
||||||
|
final s1s = s1.split("/");
|
||||||
|
final s1frameStr = s1s[1].split("-");
|
||||||
|
final s1curFrame = int.parse(s1frameStr[0]);
|
||||||
|
final s2s = s2.split("/");
|
||||||
|
final s2frameStr = s2s[1].split("-");
|
||||||
|
final s2curFrame = int.parse(s2frameStr[0]);
|
||||||
|
return s1curFrame - s2curFrame;
|
||||||
|
});
|
||||||
|
|
||||||
|
String tag = '';
|
||||||
|
int count = 0;
|
||||||
|
String bw = '';
|
||||||
|
for (var elm in urqr) {
|
||||||
|
final s = elm.substring(elm.indexOf(":") + 1); // strip down ur: prefix
|
||||||
|
final s2 = s.split("/");
|
||||||
|
tag = s2[0];
|
||||||
|
final frameStr = s2[1].split("-");
|
||||||
|
// final curFrame = int.parse(frameStr[0]);
|
||||||
|
count = int.parse(frameStr[1]);
|
||||||
|
final byteWords = s2[2];
|
||||||
|
bw += byteWords;
|
||||||
|
}
|
||||||
|
String? error;
|
||||||
|
|
||||||
|
return URQRData(
|
||||||
|
tag: tag,
|
||||||
|
str: bw,
|
||||||
|
progress: count == 0 ? 0 : (urqr.length / count),
|
||||||
|
count: count,
|
||||||
|
error: error ?? "",
|
||||||
|
inputs: urqr,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
class ProgressPainter extends CustomPainter {
|
||||||
|
final URQrProgress urQrProgress;
|
||||||
|
|
||||||
|
ProgressPainter({required this.urQrProgress});
|
||||||
|
|
||||||
|
@override
|
||||||
|
void paint(Canvas canvas, Size size) {
|
||||||
|
final c = Offset(size.width / 2.0, size.height / 2.0);
|
||||||
|
final radius = size.width * 0.9;
|
||||||
|
final rect = Rect.fromCenter(center: c, width: radius, height: radius);
|
||||||
|
const fullAngle = 360.0;
|
||||||
|
var startAngle = 0.0;
|
||||||
|
for (int i = 0; i < urQrProgress.expectedPartCount.toInt(); i++) {
|
||||||
|
var sweepAngle =
|
||||||
|
(1 / urQrProgress.expectedPartCount) * fullAngle * pi / 180.0;
|
||||||
|
drawSector(canvas, urQrProgress.receivedPartIndexes.contains(i), rect,
|
||||||
|
startAngle, sweepAngle);
|
||||||
|
startAngle += sweepAngle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void drawSector(Canvas canvas, bool isActive, Rect rect, double startAngle,
|
||||||
|
double sweepAngle) {
|
||||||
|
final paint = Paint()
|
||||||
|
..style = PaintingStyle.stroke
|
||||||
|
..strokeWidth = 8
|
||||||
|
..strokeCap = StrokeCap.round
|
||||||
|
..strokeJoin = StrokeJoin.round
|
||||||
|
..color = isActive ? const Color(0xffff6600) : Colors.white70;
|
||||||
|
canvas.drawArc(rect, startAngle, sweepAngle, false, paint);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool shouldRepaint(covariant ProgressPainter oldDelegate) {
|
||||||
|
return urQrProgress != oldDelegate.urQrProgress;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class URQrProgress {
|
||||||
|
int expectedPartCount;
|
||||||
|
int processedPartsCount;
|
||||||
|
List<int> receivedPartIndexes;
|
||||||
|
double percentage;
|
||||||
|
|
||||||
|
URQrProgress({
|
||||||
|
required this.expectedPartCount,
|
||||||
|
required this.processedPartsCount,
|
||||||
|
required this.receivedPartIndexes,
|
||||||
|
required this.percentage,
|
||||||
|
});
|
||||||
|
|
||||||
|
bool equals(URQrProgress? progress) {
|
||||||
|
if (progress == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return processedPartsCount == progress.processedPartsCount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -89,13 +89,12 @@ class TrocadorExchangeProvider extends ExchangeProvider {
|
||||||
required CryptoCurrency to,
|
required CryptoCurrency to,
|
||||||
required bool isFixedRateMode}) async {
|
required bool isFixedRateMode}) async {
|
||||||
final params = {
|
final params = {
|
||||||
'api_key': apiKey,
|
|
||||||
'ticker': _normalizeCurrency(from),
|
'ticker': _normalizeCurrency(from),
|
||||||
'name': from.name,
|
'name': from.name,
|
||||||
};
|
};
|
||||||
|
|
||||||
final uri = await _getUri(coinPath, params);
|
final uri = await _getUri(coinPath, params);
|
||||||
final response = await get(uri);
|
final response = await get(uri, headers: {'API-Key': apiKey});
|
||||||
|
|
||||||
if (response.statusCode != 200)
|
if (response.statusCode != 200)
|
||||||
throw Exception('Unexpected http status: ${response.statusCode}');
|
throw Exception('Unexpected http status: ${response.statusCode}');
|
||||||
|
@ -123,7 +122,6 @@ class TrocadorExchangeProvider extends ExchangeProvider {
|
||||||
if (amount == 0) return 0.0;
|
if (amount == 0) return 0.0;
|
||||||
|
|
||||||
final params = <String, String>{
|
final params = <String, String>{
|
||||||
'api_key': apiKey,
|
|
||||||
'ticker_from': _normalizeCurrency(from),
|
'ticker_from': _normalizeCurrency(from),
|
||||||
'ticker_to': _normalizeCurrency(to),
|
'ticker_to': _normalizeCurrency(to),
|
||||||
'network_from': _networkFor(from),
|
'network_from': _networkFor(from),
|
||||||
|
@ -136,7 +134,8 @@ class TrocadorExchangeProvider extends ExchangeProvider {
|
||||||
};
|
};
|
||||||
|
|
||||||
final uri = await _getUri(newRatePath, params);
|
final uri = await _getUri(newRatePath, params);
|
||||||
final response = await get(uri);
|
final response = await get(uri, headers: {'API-Key': apiKey});
|
||||||
|
|
||||||
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
|
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
|
||||||
final fromAmount = double.parse(responseJSON['amount_from'].toString());
|
final fromAmount = double.parse(responseJSON['amount_from'].toString());
|
||||||
final toAmount = double.parse(responseJSON['amount_to'].toString());
|
final toAmount = double.parse(responseJSON['amount_to'].toString());
|
||||||
|
@ -161,7 +160,6 @@ class TrocadorExchangeProvider extends ExchangeProvider {
|
||||||
required bool isSendAll,
|
required bool isSendAll,
|
||||||
}) async {
|
}) async {
|
||||||
final params = {
|
final params = {
|
||||||
'api_key': apiKey,
|
|
||||||
'ticker_from': _normalizeCurrency(request.fromCurrency),
|
'ticker_from': _normalizeCurrency(request.fromCurrency),
|
||||||
'ticker_to': _normalizeCurrency(request.toCurrency),
|
'ticker_to': _normalizeCurrency(request.toCurrency),
|
||||||
'network_from': _networkFor(request.fromCurrency),
|
'network_from': _networkFor(request.fromCurrency),
|
||||||
|
@ -202,7 +200,7 @@ class TrocadorExchangeProvider extends ExchangeProvider {
|
||||||
params['provider'] = firstAvailableProvider;
|
params['provider'] = firstAvailableProvider;
|
||||||
|
|
||||||
final uri = await _getUri(createTradePath, params);
|
final uri = await _getUri(createTradePath, params);
|
||||||
final response = await get(uri);
|
final response = await get(uri, headers: {'API-Key': apiKey});
|
||||||
|
|
||||||
if (response.statusCode == 400) {
|
if (response.statusCode == 400) {
|
||||||
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
|
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
|
||||||
|
@ -248,8 +246,8 @@ class TrocadorExchangeProvider extends ExchangeProvider {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<Trade> findTradeById({required String id}) async {
|
Future<Trade> findTradeById({required String id}) async {
|
||||||
final uri = await _getUri(tradePath, {'api_key': apiKey, 'id': id});
|
final uri = await _getUri(tradePath, {'id': id});
|
||||||
return get(uri).then((response) {
|
return get(uri, headers: {'API-Key': apiKey}).then((response) {
|
||||||
if (response.statusCode != 200)
|
if (response.statusCode != 200)
|
||||||
throw Exception('Unexpected http status: ${response.statusCode}');
|
throw Exception('Unexpected http status: ${response.statusCode}');
|
||||||
|
|
||||||
|
|
|
@ -204,7 +204,7 @@ Future<void> initializeAppConfigs() async {
|
||||||
transactionDescriptions: transactionDescriptions,
|
transactionDescriptions: transactionDescriptions,
|
||||||
secureStorage: secureStorage,
|
secureStorage: secureStorage,
|
||||||
anonpayInvoiceInfo: anonpayInvoiceInfo,
|
anonpayInvoiceInfo: anonpayInvoiceInfo,
|
||||||
initialMigrationVersion: 42,
|
initialMigrationVersion: 43,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -225,6 +225,19 @@ class CWMonero extends Monero {
|
||||||
language: language,
|
language: language,
|
||||||
height: height);
|
height: height);
|
||||||
|
|
||||||
|
@override
|
||||||
|
WalletCredentials createMoneroRestoreWalletFromHardwareCredentials({
|
||||||
|
required String name,
|
||||||
|
required String password,
|
||||||
|
required int height,
|
||||||
|
required ledger.LedgerConnection ledgerConnection,
|
||||||
|
}) =>
|
||||||
|
MoneroRestoreWalletFromHardwareCredentials(
|
||||||
|
name: name,
|
||||||
|
password: password,
|
||||||
|
height: height,
|
||||||
|
ledgerConnection: ledgerConnection);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
WalletCredentials createMoneroRestoreWalletFromSeedCredentials(
|
WalletCredentials createMoneroRestoreWalletFromSeedCredentials(
|
||||||
{required String name,
|
{required String name,
|
||||||
|
@ -248,6 +261,7 @@ class CWMonero extends Monero {
|
||||||
final moneroWallet = wallet as MoneroWallet;
|
final moneroWallet = wallet as MoneroWallet;
|
||||||
final keys = moneroWallet.keys;
|
final keys = moneroWallet.keys;
|
||||||
return <String, String>{
|
return <String, String>{
|
||||||
|
'primaryAddress': keys.primaryAddress,
|
||||||
'privateSpendKey': keys.privateSpendKey,
|
'privateSpendKey': keys.privateSpendKey,
|
||||||
'privateViewKey': keys.privateViewKey,
|
'privateViewKey': keys.privateViewKey,
|
||||||
'publicSpendKey': keys.publicSpendKey,
|
'publicSpendKey': keys.publicSpendKey,
|
||||||
|
@ -357,9 +371,48 @@ class CWMonero extends Monero {
|
||||||
Future<int> getCurrentHeight() async {
|
Future<int> getCurrentHeight() async {
|
||||||
return monero_wallet_api.getCurrentHeight();
|
return monero_wallet_api.getCurrentHeight();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool importKeyImagesUR(Object wallet, String ur) {
|
||||||
|
final moneroWallet = wallet as MoneroWallet;
|
||||||
|
return moneroWallet.importKeyImagesUR(ur);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<bool> commitTransactionUR(Object wallet, String ur) {
|
||||||
|
final moneroWallet = wallet as MoneroWallet;
|
||||||
|
return moneroWallet.submitTransactionUR(ur);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String exportOutputsUR(Object wallet, bool all) {
|
||||||
|
final moneroWallet = wallet as MoneroWallet;
|
||||||
|
return moneroWallet.exportOutputsUR(all);
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void monerocCheck() {
|
void monerocCheck() {
|
||||||
checkIfMoneroCIsFine();
|
checkIfMoneroCIsFine();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void setLedgerConnection(Object wallet, ledger.LedgerConnection connection) {
|
||||||
|
final moneroWallet = wallet as MoneroWallet;
|
||||||
|
moneroWallet.setLedgerConnection(connection);
|
||||||
|
}
|
||||||
|
|
||||||
|
void resetLedgerConnection() {
|
||||||
|
disableLedgerExchange();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void setGlobalLedgerConnection(ledger.LedgerConnection connection) {
|
||||||
|
gLedger = connection;
|
||||||
|
keepAlive(connection);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isViewOnly() {
|
||||||
|
return isViewOnlyBySpendKey();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,18 +1,28 @@
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:cake_wallet/core/wallet_connect/wc_bottom_sheet_service.dart';
|
||||||
|
import 'package:cake_wallet/di.dart';
|
||||||
|
import 'package:cake_wallet/entities/hardware_wallet/require_hardware_wallet_connection.dart';
|
||||||
|
import 'package:cake_wallet/entities/load_current_wallet.dart';
|
||||||
|
import 'package:cake_wallet/generated/i18n.dart';
|
||||||
|
import 'package:cake_wallet/monero/monero.dart';
|
||||||
import 'package:cake_wallet/routes.dart';
|
import 'package:cake_wallet/routes.dart';
|
||||||
import 'package:cake_wallet/utils/exception_handler.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:cake_wallet/store/authentication_store.dart';
|
||||||
import 'package:cake_wallet/store/settings_store.dart';
|
import 'package:cake_wallet/store/settings_store.dart';
|
||||||
|
import 'package:cake_wallet/utils/exception_handler.dart';
|
||||||
|
import 'package:cake_wallet/utils/show_pop_up.dart';
|
||||||
|
import 'package:cw_core/wallet_type.dart';
|
||||||
import 'package:flutter/widgets.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
import 'package:mobx/mobx.dart';
|
import 'package:mobx/mobx.dart';
|
||||||
import 'package:cake_wallet/entities/load_current_wallet.dart';
|
|
||||||
import 'package:cake_wallet/store/authentication_store.dart';
|
|
||||||
import 'package:rxdart/subjects.dart';
|
import 'package:rxdart/subjects.dart';
|
||||||
|
|
||||||
ReactionDisposer? _onAuthenticationStateChange;
|
ReactionDisposer? _onAuthenticationStateChange;
|
||||||
|
|
||||||
dynamic loginError;
|
dynamic loginError;
|
||||||
StreamController<dynamic> authenticatedErrorStreamController = BehaviorSubject<dynamic>();
|
StreamController<dynamic> authenticatedErrorStreamController =
|
||||||
|
BehaviorSubject<dynamic>();
|
||||||
|
|
||||||
void startAuthenticationStateChange(
|
void startAuthenticationStateChange(
|
||||||
AuthenticationStore authenticationStore,
|
AuthenticationStore authenticationStore,
|
||||||
|
@ -27,18 +37,49 @@ void startAuthenticationStateChange(
|
||||||
_onAuthenticationStateChange ??= autorun((_) async {
|
_onAuthenticationStateChange ??= autorun((_) async {
|
||||||
final state = authenticationStore.state;
|
final state = authenticationStore.state;
|
||||||
|
|
||||||
if (state == AuthenticationState.installed && !SettingsStoreBase.walletPasswordDirectInput) {
|
if (state == AuthenticationState.installed &&
|
||||||
|
!SettingsStoreBase.walletPasswordDirectInput) {
|
||||||
try {
|
try {
|
||||||
await loadCurrentWallet();
|
if (!requireHardwareWalletConnection()) await loadCurrentWallet();
|
||||||
} catch (error, stack) {
|
} catch (error, stack) {
|
||||||
loginError = error;
|
loginError = error;
|
||||||
ExceptionHandler.onError(FlutterErrorDetails(exception: error, stack: stack));
|
ExceptionHandler.onError(
|
||||||
|
FlutterErrorDetails(exception: error, stack: stack));
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (state == AuthenticationState.allowed) {
|
if (state == AuthenticationState.allowed) {
|
||||||
await navigatorKey.currentState!.pushNamedAndRemoveUntil(Routes.dashboard, (route) => false);
|
if (requireHardwareWalletConnection()) {
|
||||||
|
await navigatorKey.currentState!.pushNamedAndRemoveUntil(
|
||||||
|
Routes.connectDevices,
|
||||||
|
(route) => false,
|
||||||
|
arguments: ConnectDevicePageParams(
|
||||||
|
walletType: WalletType.monero,
|
||||||
|
onConnectDevice: (context, ledgerVM) async {
|
||||||
|
monero!.setGlobalLedgerConnection(ledgerVM.connection);
|
||||||
|
showPopUp<void>(
|
||||||
|
context: context,
|
||||||
|
builder: (BuildContext context) => AlertWithOneAction(
|
||||||
|
alertTitle: S.of(context).proceed_on_device,
|
||||||
|
alertContent: S.of(context).proceed_on_device_description,
|
||||||
|
buttonText: S.of(context).cancel,
|
||||||
|
buttonAction: () => Navigator.of(context).pop()),
|
||||||
|
);
|
||||||
|
await loadCurrentWallet();
|
||||||
|
getIt.get<BottomSheetService>().resetCurrentSheet();
|
||||||
|
await navigatorKey.currentState!
|
||||||
|
.pushNamedAndRemoveUntil(Routes.dashboard, (route) => false);
|
||||||
|
},
|
||||||
|
allowChangeWallet: true,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
// await navigatorKey.currentState!.pushNamedAndRemoveUntil(Routes.connectDevices, (route) => false, arguments: ConnectDevicePageParams(walletType: walletType, onConnectDevice: onConnectDevice));
|
||||||
|
} else {
|
||||||
|
await navigatorKey.currentState!
|
||||||
|
.pushNamedAndRemoveUntil(Routes.dashboard, (route) => false);
|
||||||
|
}
|
||||||
if (!(await authenticatedErrorStreamController.stream.isEmpty)) {
|
if (!(await authenticatedErrorStreamController.stream.isEmpty)) {
|
||||||
ExceptionHandler.showError(
|
ExceptionHandler.showError(
|
||||||
(await authenticatedErrorStreamController.stream.first).toString());
|
(await authenticatedErrorStreamController.stream.first).toString());
|
||||||
|
|
|
@ -10,9 +10,6 @@ import 'package:cw_core/transaction_history.dart';
|
||||||
import 'package:cw_core/balance.dart';
|
import 'package:cw_core/balance.dart';
|
||||||
import 'package:cw_core/transaction_info.dart';
|
import 'package:cw_core/transaction_info.dart';
|
||||||
import 'package:mobx/mobx.dart';
|
import 'package:mobx/mobx.dart';
|
||||||
import 'package:shared_preferences/shared_preferences.dart';
|
|
||||||
import 'package:cake_wallet/di.dart';
|
|
||||||
import 'package:cake_wallet/entities/preferences_key.dart';
|
|
||||||
import 'package:cake_wallet/reactions/check_connection.dart';
|
import 'package:cake_wallet/reactions/check_connection.dart';
|
||||||
import 'package:cake_wallet/reactions/on_wallet_sync_status_change.dart';
|
import 'package:cake_wallet/reactions/on_wallet_sync_status_change.dart';
|
||||||
import 'package:cake_wallet/store/dashboard/fiat_conversion_store.dart';
|
import 'package:cake_wallet/store/dashboard/fiat_conversion_store.dart';
|
||||||
|
@ -65,10 +62,6 @@ void startCurrentWalletChangeReaction(
|
||||||
|
|
||||||
startWalletSyncStatusChangeReaction(wallet, fiatConversionStore);
|
startWalletSyncStatusChangeReaction(wallet, fiatConversionStore);
|
||||||
startCheckConnectionReaction(wallet, settingsStore);
|
startCheckConnectionReaction(wallet, settingsStore);
|
||||||
await getIt.get<SharedPreferences>().setString(PreferencesKey.currentWalletName, wallet.name);
|
|
||||||
await getIt
|
|
||||||
.get<SharedPreferences>()
|
|
||||||
.setInt(PreferencesKey.currentWalletType, serializeToInt(wallet.type));
|
|
||||||
|
|
||||||
if (wallet.type == WalletType.monero ||
|
if (wallet.type == WalletType.monero ||
|
||||||
wallet.type == WalletType.wownero ||
|
wallet.type == WalletType.wownero ||
|
||||||
|
|
|
@ -17,12 +17,14 @@ import 'package:cake_wallet/src/screens/anonpay_details/anonpay_details_page.dar
|
||||||
import 'package:cake_wallet/src/screens/auth/auth_page.dart';
|
import 'package:cake_wallet/src/screens/auth/auth_page.dart';
|
||||||
import 'package:cake_wallet/src/screens/backup/backup_page.dart';
|
import 'package:cake_wallet/src/screens/backup/backup_page.dart';
|
||||||
import 'package:cake_wallet/src/screens/backup/edit_backup_password_page.dart';
|
import 'package:cake_wallet/src/screens/backup/edit_backup_password_page.dart';
|
||||||
import 'package:cake_wallet/src/screens/buy/buy_options_page.dart';
|
import 'package:cake_wallet/src/screens/buy/buy_sell_options_page.dart';
|
||||||
import 'package:cake_wallet/src/screens/buy/buy_webview_page.dart';
|
import 'package:cake_wallet/src/screens/buy/buy_webview_page.dart';
|
||||||
|
import 'package:cake_wallet/src/screens/buy/payment_method_options_page.dart';
|
||||||
import 'package:cake_wallet/src/screens/buy/webview_page.dart';
|
import 'package:cake_wallet/src/screens/buy/webview_page.dart';
|
||||||
import 'package:cake_wallet/src/screens/cake_pay/auth/cake_pay_account_page.dart';
|
import 'package:cake_wallet/src/screens/cake_pay/auth/cake_pay_account_page.dart';
|
||||||
import 'package:cake_wallet/src/screens/cake_pay/cake_pay.dart';
|
import 'package:cake_wallet/src/screens/cake_pay/cake_pay.dart';
|
||||||
import 'package:cake_wallet/src/screens/connect_device/connect_device_page.dart';
|
import 'package:cake_wallet/src/screens/connect_device/connect_device_page.dart';
|
||||||
|
import 'package:cake_wallet/src/screens/connect_device/monero_hardware_wallet_options_page.dart';
|
||||||
import 'package:cake_wallet/src/screens/connect_device/select_hardware_wallet_account_page.dart';
|
import 'package:cake_wallet/src/screens/connect_device/select_hardware_wallet_account_page.dart';
|
||||||
import 'package:cake_wallet/src/screens/contact/contact_list_page.dart';
|
import 'package:cake_wallet/src/screens/contact/contact_list_page.dart';
|
||||||
import 'package:cake_wallet/src/screens/contact/contact_page.dart';
|
import 'package:cake_wallet/src/screens/contact/contact_page.dart';
|
||||||
|
@ -96,6 +98,7 @@ import 'package:cake_wallet/src/screens/transaction_details/rbf_details_page.dar
|
||||||
import 'package:cake_wallet/src/screens/transaction_details/transaction_details_page.dart';
|
import 'package:cake_wallet/src/screens/transaction_details/transaction_details_page.dart';
|
||||||
import 'package:cake_wallet/src/screens/unspent_coins/unspent_coins_details_page.dart';
|
import 'package:cake_wallet/src/screens/unspent_coins/unspent_coins_details_page.dart';
|
||||||
import 'package:cake_wallet/src/screens/unspent_coins/unspent_coins_list_page.dart';
|
import 'package:cake_wallet/src/screens/unspent_coins/unspent_coins_list_page.dart';
|
||||||
|
import 'package:cake_wallet/src/screens/ur/animated_ur_page.dart';
|
||||||
import 'package:cake_wallet/src/screens/wallet/wallet_edit_page.dart';
|
import 'package:cake_wallet/src/screens/wallet/wallet_edit_page.dart';
|
||||||
import 'package:cake_wallet/src/screens/wallet_connect/wc_connections_listing_view.dart';
|
import 'package:cake_wallet/src/screens/wallet_connect/wc_connections_listing_view.dart';
|
||||||
import 'package:cake_wallet/src/screens/wallet_keys/wallet_keys_page.dart';
|
import 'package:cake_wallet/src/screens/wallet_keys/wallet_keys_page.dart';
|
||||||
|
@ -128,7 +131,8 @@ import 'package:cw_core/wallet_type.dart';
|
||||||
import 'package:flutter/cupertino.dart';
|
import 'package:flutter/cupertino.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
|
import 'package:cake_wallet/src/screens/cake_pay/cake_pay.dart';
|
||||||
|
import 'src/screens/buy/buy_sell_page.dart';
|
||||||
import 'src/screens/dashboard/pages/nft_import_page.dart';
|
import 'src/screens/dashboard/pages/nft_import_page.dart';
|
||||||
|
|
||||||
late RouteSettings currentRouteSettings;
|
late RouteSettings currentRouteSettings;
|
||||||
|
@ -209,6 +213,9 @@ Route<dynamic> createRoute(RouteSettings settings) {
|
||||||
final type = arguments[0] as WalletType;
|
final type = arguments[0] as WalletType;
|
||||||
final walletVM = getIt.get<WalletHardwareRestoreViewModel>(param1: type);
|
final walletVM = getIt.get<WalletHardwareRestoreViewModel>(param1: type);
|
||||||
|
|
||||||
|
if (type == WalletType.monero)
|
||||||
|
return CupertinoPageRoute<void>(builder: (_) => MoneroHardwareWalletOptionsPage(walletVM));
|
||||||
|
|
||||||
return CupertinoPageRoute<void>(builder: (_) => SelectHardwareWalletAccountPage(walletVM));
|
return CupertinoPageRoute<void>(builder: (_) => SelectHardwareWalletAccountPage(walletVM));
|
||||||
|
|
||||||
case Routes.setupPin:
|
case Routes.setupPin:
|
||||||
|
@ -400,8 +407,11 @@ Route<dynamic> createRoute(RouteSettings settings) {
|
||||||
return CupertinoPageRoute<void>(builder: (_) => getIt.get<NanoChangeRepPage>());
|
return CupertinoPageRoute<void>(builder: (_) => getIt.get<NanoChangeRepPage>());
|
||||||
|
|
||||||
case Routes.walletList:
|
case Routes.walletList:
|
||||||
|
final onWalletLoaded = settings.arguments as Function(BuildContext)?;
|
||||||
return MaterialPageRoute<void>(
|
return MaterialPageRoute<void>(
|
||||||
fullscreenDialog: true, builder: (_) => getIt.get<WalletListPage>());
|
fullscreenDialog: true,
|
||||||
|
builder: (_) => getIt.get<WalletListPage>(param1: onWalletLoaded),
|
||||||
|
);
|
||||||
|
|
||||||
case Routes.walletEdit:
|
case Routes.walletEdit:
|
||||||
return MaterialPageRoute<void>(
|
return MaterialPageRoute<void>(
|
||||||
|
@ -570,7 +580,15 @@ Route<dynamic> createRoute(RouteSettings settings) {
|
||||||
|
|
||||||
case Routes.buySellPage:
|
case Routes.buySellPage:
|
||||||
final args = settings.arguments as bool;
|
final args = settings.arguments as bool;
|
||||||
return MaterialPageRoute<void>(builder: (_) => getIt.get<BuySellOptionsPage>(param1: args));
|
return MaterialPageRoute<void>(builder: (_) => getIt.get<BuySellPage>(param1: args));
|
||||||
|
|
||||||
|
case Routes.buyOptionsPage:
|
||||||
|
final args = settings.arguments as List;
|
||||||
|
return MaterialPageRoute<void>(builder: (_) => getIt.get<BuyOptionsPage>(param1: args));
|
||||||
|
|
||||||
|
case Routes.paymentMethodOptionsPage:
|
||||||
|
final args = settings.arguments as List;
|
||||||
|
return MaterialPageRoute<void>(builder: (_) => getIt.get<PaymentMethodOptionsPage>(param1: args));
|
||||||
|
|
||||||
case Routes.buyWebView:
|
case Routes.buyWebView:
|
||||||
final args = settings.arguments as List;
|
final args = settings.arguments as List;
|
||||||
|
@ -732,6 +750,9 @@ Route<dynamic> createRoute(RouteSettings settings) {
|
||||||
case Routes.setup2faInfoPage:
|
case Routes.setup2faInfoPage:
|
||||||
return MaterialPageRoute<void>(builder: (_) => getIt.get<Setup2FAInfoPage>());
|
return MaterialPageRoute<void>(builder: (_) => getIt.get<Setup2FAInfoPage>());
|
||||||
|
|
||||||
|
case Routes.urqrAnimatedPage:
|
||||||
|
return MaterialPageRoute<void>(builder: (_) => getIt.get<AnimatedURPage>(param1: settings.arguments));
|
||||||
|
|
||||||
case Routes.homeSettings:
|
case Routes.homeSettings:
|
||||||
return CupertinoPageRoute<void>(
|
return CupertinoPageRoute<void>(
|
||||||
builder: (_) => getIt.get<HomeSettingsPage>(param1: settings.arguments),
|
builder: (_) => getIt.get<HomeSettingsPage>(param1: settings.arguments),
|
||||||
|
|
|
@ -59,6 +59,8 @@ class Routes {
|
||||||
static const supportOtherLinks = '/support/other';
|
static const supportOtherLinks = '/support/other';
|
||||||
static const orderDetails = '/order_details';
|
static const orderDetails = '/order_details';
|
||||||
static const buySellPage = '/buy_sell_page';
|
static const buySellPage = '/buy_sell_page';
|
||||||
|
static const buyOptionsPage = '/buy_sell_options';
|
||||||
|
static const paymentMethodOptionsPage = '/payment_method_options';
|
||||||
static const buyWebView = '/buy_web_view';
|
static const buyWebView = '/buy_web_view';
|
||||||
static const unspentCoinsList = '/unspent_coins_list';
|
static const unspentCoinsList = '/unspent_coins_list';
|
||||||
static const unspentCoinsDetails = '/unspent_coins_details';
|
static const unspentCoinsDetails = '/unspent_coins_details';
|
||||||
|
@ -108,6 +110,7 @@ class Routes {
|
||||||
|
|
||||||
static const signPage = '/sign_page';
|
static const signPage = '/sign_page';
|
||||||
static const connectDevices = '/device/connect';
|
static const connectDevices = '/device/connect';
|
||||||
|
static const urqrAnimatedPage = '/urqr/animated_page';
|
||||||
static const walletGroupsDisplayPage = '/wallet_groups_display_page';
|
static const walletGroupsDisplayPage = '/wallet_groups_display_page';
|
||||||
static const walletGroupDescription = '/wallet_group_description';
|
static const walletGroupDescription = '/wallet_group_description';
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,74 +0,0 @@
|
||||||
import 'package:cake_wallet/generated/i18n.dart';
|
|
||||||
import 'package:cake_wallet/src/screens/base_page.dart';
|
|
||||||
import 'package:cake_wallet/src/widgets/option_tile.dart';
|
|
||||||
import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart';
|
|
||||||
import 'package:cake_wallet/themes/extensions/option_tile_theme.dart';
|
|
||||||
import 'package:cake_wallet/themes/extensions/transaction_trade_theme.dart';
|
|
||||||
import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
|
|
||||||
class BuySellOptionsPage extends BasePage {
|
|
||||||
BuySellOptionsPage(this.dashboardViewModel, this.isBuyAction);
|
|
||||||
|
|
||||||
final DashboardViewModel dashboardViewModel;
|
|
||||||
final bool isBuyAction;
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get title => isBuyAction ? S.current.buy : S.current.sell;
|
|
||||||
|
|
||||||
@override
|
|
||||||
AppBarStyle get appBarStyle => AppBarStyle.regular;
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget body(BuildContext context) {
|
|
||||||
final isLightMode = Theme.of(context).extension<OptionTileTheme>()?.useDarkImage ?? false;
|
|
||||||
final availableProviders = isBuyAction
|
|
||||||
? dashboardViewModel.availableBuyProviders
|
|
||||||
: dashboardViewModel.availableSellProviders;
|
|
||||||
|
|
||||||
return ScrollableWithBottomSection(
|
|
||||||
content: Container(
|
|
||||||
child: Center(
|
|
||||||
child: ConstrainedBox(
|
|
||||||
constraints: BoxConstraints(maxWidth: 330),
|
|
||||||
child: Column(
|
|
||||||
children: [
|
|
||||||
...availableProviders.map((provider) {
|
|
||||||
final icon = Image.asset(
|
|
||||||
isLightMode ? provider.lightIcon : provider.darkIcon,
|
|
||||||
height: 40,
|
|
||||||
width: 40,
|
|
||||||
);
|
|
||||||
|
|
||||||
return Padding(
|
|
||||||
padding: EdgeInsets.only(top: 24),
|
|
||||||
child: OptionTile(
|
|
||||||
image: icon,
|
|
||||||
title: provider.toString(),
|
|
||||||
description: provider.providerDescription,
|
|
||||||
onPressed: () => provider.launchProvider(context, isBuyAction),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}).toList(),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
bottomSection: Padding(
|
|
||||||
padding: EdgeInsets.fromLTRB(24, 24, 24, 32),
|
|
||||||
child: Text(
|
|
||||||
isBuyAction
|
|
||||||
? S.of(context).select_buy_provider_notice
|
|
||||||
: S.of(context).select_sell_provider_notice,
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 14,
|
|
||||||
fontWeight: FontWeight.normal,
|
|
||||||
color: Theme.of(context).extension<TransactionTradeTheme>()!.detailsTitlesColor,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|