Merge branch 'main' of https://github.com/cake-tech/cake_wallet into cw_linux_direct_input_password
Conflicts: .gitignore assets/text/Monerocom_Release_Notes.txt assets/text/Release_Notes.txt cw_bitcoin/lib/bitcoin_wallet_service.dart cw_bitcoin/lib/electrum_transaction_history.dart cw_bitcoin/lib/litecoin_wallet_service.dart cw_bitcoin/pubspec.yaml cw_core/pubspec.lock cw_monero/ios/Classes/monero_api.cpp cw_monero/lib/monero_wallet.dart cw_monero/lib/monero_wallet_service.dart lib/core/backup_service.dart lib/core/wallet_loading_service.dart lib/di.dart lib/entities/default_settings_migration.dart lib/entities/get_encryption_key.dart lib/entities/main_actions.dart lib/main.dart lib/router.dart lib/src/screens/dashboard/desktop_widgets/desktop_action_button.dart lib/src/screens/dashboard/desktop_widgets/desktop_wallet_selection_dropdown.dart lib/src/screens/dashboard/widgets/market_place_page.dart lib/src/screens/dashboard/widgets/transactions_page.dart lib/src/screens/receive/anonpay_invoice_page.dart lib/src/screens/restore/wallet_restore_from_keys_form.dart lib/src/screens/restore/wallet_restore_page.dart lib/src/screens/settings/security_backup_page.dart lib/src/screens/wallet/wallet_edit_page.dart lib/src/screens/wallet_list/wallet_list_page.dart lib/store/settings_store.dart lib/utils/distribution_info.dart lib/view_model/wallet_creation_vm.dart lib/view_model/wallet_list/wallet_edit_view_model.dart lib/view_model/wallet_list/wallet_list_view_model.dart lib/view_model/wallet_new_vm.dart res/values/strings_ar.arb res/values/strings_bg.arb res/values/strings_cs.arb res/values/strings_de.arb res/values/strings_en.arb res/values/strings_es.arb res/values/strings_fr.arb res/values/strings_ha.arb res/values/strings_hi.arb res/values/strings_hr.arb res/values/strings_id.arb res/values/strings_it.arb res/values/strings_ja.arb res/values/strings_ko.arb res/values/strings_my.arb res/values/strings_nl.arb res/values/strings_pl.arb res/values/strings_pt.arb res/values/strings_ru.arb res/values/strings_th.arb res/values/strings_tr.arb res/values/strings_uk.arb res/values/strings_ur.arb res/values/strings_yo.arb res/values/strings_zh.arb scripts/android/app_env.sh scripts/ios/app_env.sh scripts/macos/app_env.sh tool/configure.dart
5
.github/workflows/cache_dependencies.yml
vendored
|
@ -1,6 +1,7 @@
|
||||||
name: Cache Dependencies
|
name: Cache Dependencies
|
||||||
|
|
||||||
on:
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
push:
|
push:
|
||||||
branches: [ main ]
|
branches: [ main ]
|
||||||
|
|
||||||
|
@ -18,7 +19,7 @@ jobs:
|
||||||
- name: Flutter action
|
- name: Flutter action
|
||||||
uses: subosito/flutter-action@v1
|
uses: subosito/flutter-action@v1
|
||||||
with:
|
with:
|
||||||
flutter-version: '3.3.x'
|
flutter-version: '3.10.x'
|
||||||
channel: stable
|
channel: stable
|
||||||
|
|
||||||
- name: Install package dependencies
|
- name: Install package dependencies
|
||||||
|
@ -45,7 +46,7 @@ jobs:
|
||||||
/opt/android/cake_wallet/cw_monero/android/.cxx
|
/opt/android/cake_wallet/cw_monero/android/.cxx
|
||||||
/opt/android/cake_wallet/cw_monero/ios/External
|
/opt/android/cake_wallet/cw_monero/ios/External
|
||||||
/opt/android/cake_wallet/cw_shared_external/ios/External
|
/opt/android/cake_wallet/cw_shared_external/ios/External
|
||||||
key: ${{ hashFiles('**/build_monero.sh', '**/build_haven.sh') }}
|
key: ${{ hashFiles('**/build_monero.sh', '**/build_haven.sh', '**/monero_api.cpp') }}
|
||||||
|
|
||||||
- if: ${{ steps.cache-externals.outputs.cache-hit != 'true' }}
|
- if: ${{ steps.cache-externals.outputs.cache-hit != 'true' }}
|
||||||
name: Generate Externals
|
name: Generate Externals
|
||||||
|
|
27
.github/workflows/pr_test_build.yml
vendored
|
@ -13,6 +13,13 @@ jobs:
|
||||||
KEY_PASS: test@cake_wallet
|
KEY_PASS: test@cake_wallet
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
|
- name: Free Up GitHub Actions Ubuntu Runner Disk Space
|
||||||
|
run: |
|
||||||
|
sudo rm -rf /usr/share/dotnet
|
||||||
|
sudo rm -rf /opt/ghc
|
||||||
|
sudo rm -rf "/usr/local/share/boost"
|
||||||
|
sudo rm -rf "$AGENT_TOOLSDIRECTORY"
|
||||||
|
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
- uses: actions/setup-java@v1
|
- uses: actions/setup-java@v1
|
||||||
with:
|
with:
|
||||||
|
@ -21,7 +28,7 @@ jobs:
|
||||||
- name: Flutter action
|
- name: Flutter action
|
||||||
uses: subosito/flutter-action@v1
|
uses: subosito/flutter-action@v1
|
||||||
with:
|
with:
|
||||||
flutter-version: '3.7.x'
|
flutter-version: '3.10.x'
|
||||||
channel: stable
|
channel: stable
|
||||||
|
|
||||||
- name: Install package dependencies
|
- name: Install package dependencies
|
||||||
|
@ -48,7 +55,7 @@ jobs:
|
||||||
/opt/android/cake_wallet/cw_monero/android/.cxx
|
/opt/android/cake_wallet/cw_monero/android/.cxx
|
||||||
/opt/android/cake_wallet/cw_monero/ios/External
|
/opt/android/cake_wallet/cw_monero/ios/External
|
||||||
/opt/android/cake_wallet/cw_shared_external/ios/External
|
/opt/android/cake_wallet/cw_shared_external/ios/External
|
||||||
key: ${{ hashFiles('**/build_monero.sh', '**/build_haven.sh') }}
|
key: ${{ hashFiles('**/build_monero.sh', '**/build_haven.sh', '**/monero_api.cpp') }}
|
||||||
|
|
||||||
- if: ${{ steps.cache-externals.outputs.cache-hit != 'true' }}
|
- if: ${{ steps.cache-externals.outputs.cache-hit != 'true' }}
|
||||||
name: Generate Externals
|
name: Generate Externals
|
||||||
|
@ -85,12 +92,14 @@ jobs:
|
||||||
cd cw_monero && flutter pub get && flutter packages pub run build_runner build --delete-conflicting-outputs && cd ..
|
cd cw_monero && flutter pub get && flutter packages pub run build_runner build --delete-conflicting-outputs && cd ..
|
||||||
cd cw_bitcoin && flutter pub get && flutter packages pub run build_runner build --delete-conflicting-outputs && cd ..
|
cd cw_bitcoin && flutter pub get && flutter packages pub run build_runner build --delete-conflicting-outputs && cd ..
|
||||||
cd cw_haven && flutter pub get && flutter packages pub run build_runner build --delete-conflicting-outputs && cd ..
|
cd cw_haven && flutter pub get && flutter packages pub run build_runner build --delete-conflicting-outputs && cd ..
|
||||||
|
cd cw_ethereum && flutter pub get && flutter packages pub run build_runner build --delete-conflicting-outputs && cd ..
|
||||||
flutter packages pub run build_runner build --delete-conflicting-outputs
|
flutter packages pub run build_runner build --delete-conflicting-outputs
|
||||||
|
|
||||||
- name: Add secrets
|
- name: Add secrets
|
||||||
run: |
|
run: |
|
||||||
cd /opt/android/cake_wallet
|
cd /opt/android/cake_wallet
|
||||||
touch lib/.secrets.g.dart
|
touch lib/.secrets.g.dart
|
||||||
|
touch cw_ethereum/lib/.secrets.g.dart
|
||||||
echo "const salt = '${{ secrets.SALT }}';" > lib/.secrets.g.dart
|
echo "const salt = '${{ secrets.SALT }}';" > lib/.secrets.g.dart
|
||||||
echo "const keychainSalt = '${{ secrets.KEY_CHAIN_SALT }}';" >> lib/.secrets.g.dart
|
echo "const keychainSalt = '${{ secrets.KEY_CHAIN_SALT }}';" >> lib/.secrets.g.dart
|
||||||
echo "const key = '${{ secrets.KEY }}';" >> lib/.secrets.g.dart
|
echo "const key = '${{ secrets.KEY }}';" >> lib/.secrets.g.dart
|
||||||
|
@ -117,6 +126,8 @@ jobs:
|
||||||
echo "const anonPayReferralCode = '${{ secrets.ANON_PAY_REFERRAL_CODE }}';" >> lib/.secrets.g.dart
|
echo "const anonPayReferralCode = '${{ secrets.ANON_PAY_REFERRAL_CODE }}';" >> lib/.secrets.g.dart
|
||||||
echo "const fiatApiKey = '${{ secrets.FIAT_API_KEY }}';" >> lib/.secrets.g.dart
|
echo "const fiatApiKey = '${{ secrets.FIAT_API_KEY }}';" >> lib/.secrets.g.dart
|
||||||
echo "const payfuraApiKey = '${{ secrets.PAYFURA_API_KEY }}';" >> lib/.secrets.g.dart
|
echo "const payfuraApiKey = '${{ secrets.PAYFURA_API_KEY }}';" >> lib/.secrets.g.dart
|
||||||
|
echo "const etherScanApiKey = '${{ secrets.ETHER_SCAN_API_KEY }}';" >> cw_ethereum/lib/.secrets.g.dart
|
||||||
|
echo "const chatwootWebsiteToken = '${{ secrets.CHATWOOT_WEBSITE_TOKEN }}';" >> lib/.secrets.g.dart
|
||||||
|
|
||||||
- name: Rename app
|
- name: Rename app
|
||||||
run: echo -e "id=com.cakewallet.test\nname=$GITHUB_HEAD_REF" > /opt/android/cake_wallet/android/app.properties
|
run: echo -e "id=com.cakewallet.test\nname=$GITHUB_HEAD_REF" > /opt/android/cake_wallet/android/app.properties
|
||||||
|
@ -152,7 +163,11 @@ jobs:
|
||||||
|
|
||||||
- name: Send Test APK
|
- name: Send Test APK
|
||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
run: |
|
uses: adrey/slack-file-upload-action@1.0.5
|
||||||
cd /opt/android/cake_wallet
|
with:
|
||||||
var=$(curl --upload-file build/app/outputs/apk/release/app-release.apk https://transfer.sh/$GITHUB_HEAD_REF.apk -H "Max-Days: 10")
|
token: ${{ secrets.SLACK_APP_TOKEN }}
|
||||||
curl ${{ secrets.SLACK_WEB_HOOK }} -H "Content-Type: application/json" -d '{"apk_link": "'"$var"'","ticket": "'"$GITHUB_HEAD_REF"'"}'
|
path: /opt/android/cake_wallet/build/app/outputs/apk/release/app-release.apk
|
||||||
|
channel: ${{ secrets.SLACK_APK_CHANNEL }}
|
||||||
|
title: '${{github.head_ref}}.apk'
|
||||||
|
filename: ${{github.head_ref}}.apk
|
||||||
|
initial_comment: ${{ github.event.head_commit.message }}
|
||||||
|
|
4
.gitignore
vendored
|
@ -8,6 +8,7 @@
|
||||||
.buildlog/
|
.buildlog/
|
||||||
.history
|
.history
|
||||||
.svn/
|
.svn/
|
||||||
|
.fvm/
|
||||||
|
|
||||||
# IntelliJ related
|
# IntelliJ related
|
||||||
*.iml
|
*.iml
|
||||||
|
@ -89,7 +90,9 @@ android/key.properties
|
||||||
**/tool/.secrets-prod.json
|
**/tool/.secrets-prod.json
|
||||||
**/tool/.secrets-test.json
|
**/tool/.secrets-test.json
|
||||||
**/tool/.secrets-config.json
|
**/tool/.secrets-config.json
|
||||||
|
**/tool/.ethereum-secrets-config.json
|
||||||
**/lib/.secrets.g.dart
|
**/lib/.secrets.g.dart
|
||||||
|
**/cw_ethereum/lib/.secrets.g.dart
|
||||||
|
|
||||||
vendor/
|
vendor/
|
||||||
|
|
||||||
|
@ -120,6 +123,7 @@ cw_haven/android/.cxx/
|
||||||
lib/bitcoin/bitcoin.dart
|
lib/bitcoin/bitcoin.dart
|
||||||
lib/monero/monero.dart
|
lib/monero/monero.dart
|
||||||
lib/haven/haven.dart
|
lib/haven/haven.dart
|
||||||
|
lib/ethereum/ethereum.dart
|
||||||
|
|
||||||
ios/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_180.png
|
ios/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_180.png
|
||||||
ios/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_120.png
|
ios/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_120.png
|
||||||
|
|
27
PRIVACY.md
|
@ -1,6 +1,6 @@
|
||||||
Privacy Policy
|
Privacy Policy
|
||||||
|
|
||||||
Last modified: July 21, 2022
|
Last modified: August 9, 2023
|
||||||
|
|
||||||
Introduction
|
Introduction
|
||||||
============
|
============
|
||||||
|
@ -13,7 +13,7 @@ Introduction
|
||||||
- On this App.
|
- On this App.
|
||||||
- In email, text, and other electronic messages between you and this App.
|
- In email, text, and other electronic messages between you and this App.
|
||||||
It does not apply to information collected by:
|
It does not apply to information collected by:
|
||||||
- Us offline or through any other means, including on any other App operated by Company or any third party (including our affiliates and subsidiaries)]; or
|
- Us offline or through any other means, including on any other App operated by Company or any third party (including our affiliates and subsidiaries); or
|
||||||
- Any third party (including our affiliates and subsidiaries), including through any application or content (including advertising) that may link to or be accessible from or on the App.
|
- Any third party (including our affiliates and subsidiaries), including through any application or content (including advertising) that may link to or be accessible from or on the App.
|
||||||
Please read this policy carefully to understand our policies and practices regarding your information and how we will treat it. If you do not agree with our policies and practices, you have the choice to not use the App. By accessing or using this App, you agree to this privacy policy. This policy may change from time to time. Your continued use of this App after we make changes is deemed to be acceptance of those changes, so please check the policy periodically for updates.
|
Please read this policy carefully to understand our policies and practices regarding your information and how we will treat it. If you do not agree with our policies and practices, you have the choice to not use the App. By accessing or using this App, you agree to this privacy policy. This policy may change from time to time. Your continued use of this App after we make changes is deemed to be acceptance of those changes, so please check the policy periodically for updates.
|
||||||
|
|
||||||
|
@ -25,7 +25,7 @@ Definitions
|
||||||
- "Node" means a server on a supported cryptocurrency network which transmits data to your App for processing and synchronization, and to which your Device transmits transactions which you would like to submit to the supported cryptocurrency networks. This includes full nodes, Electrum servers, and lightning network nodes.
|
- "Node" means a server on a supported cryptocurrency network which transmits data to your App for processing and synchronization, and to which your Device transmits transactions which you would like to submit to the supported cryptocurrency networks. This includes full nodes, Electrum servers, and lightning network nodes.
|
||||||
- "Cake Labs Nodes" refers to the set of cryptocurrency nodes operated and maintained by Cake Labs LLC.
|
- "Cake Labs Nodes" refers to the set of cryptocurrency nodes operated and maintained by Cake Labs LLC.
|
||||||
- "Service" refers to the App.
|
- "Service" refers to the App.
|
||||||
- "Third-party Service" refers to any service integrated into the App. This includes but is not limited to ChangeNOW, Wyre, MoonPay, and BlockBuy.
|
- "Third-party Service" refers to any service integrated into the App. This includes but is not limited to ChangeNOW, Onramper, and MoonPay.
|
||||||
- "Usage Data" refers to data collected automatically about your usage of an App.
|
- "Usage Data" refers to data collected automatically about your usage of an App.
|
||||||
- "You" means the individual, group, corporation, or any other entity accessing or using the Service.
|
- "You" means the individual, group, corporation, or any other entity accessing or using the Service.
|
||||||
|
|
||||||
|
@ -40,26 +40,29 @@ Information We Never Receive Nor Collect
|
||||||
Information We May Receive But Do Not Retain
|
Information We May Receive But Do Not Retain
|
||||||
--------------------------------------------
|
--------------------------------------------
|
||||||
|
|
||||||
We receive but do NOT store information from and about users of our App, including:
|
We may receive but do NOT store information from and about users of our App, including:
|
||||||
- The device IP address, the block height to which your wallet is synchronized, and any transactions or channels which you use our Node to submit to supported cryptocurrency networks.
|
- The device IP address, the block height to which your wallet is synchronized, and any transactions or channels which you use our Node to submit to supported cryptocurrency networks.
|
||||||
We receive this information:
|
We receive this information:
|
||||||
- Automatically as you use the App.
|
- Automatically as you use the App, unless you turn certain features off in your App privacy settings.
|
||||||
|
|
||||||
This data is provided by connecting to the Nodes and price API maintained by Cake Labs. You have the right to choose not to provide synchronization data to Cake Labs by choosing a different Node. We provide a list of Nodes in the app that include our own and third party Nodes, or you can use your own Node (which we recommend).
|
This data is provided by connecting to the Nodes and price API maintained by Cake Labs. You have the right to choose not to provide synchronization data to Cake Labs by choosing a different Node. We provide a list of Nodes in the app that include our own and third party Nodes, or you can use your own Node (which we recommend). You have the right to choose not to connect to our Fiat API service by disabling this Fiat API in App privacy settings.
|
||||||
|
|
||||||
Personal Data sent through the Cake Labs Nodes is limited to your device's IP address, the block height to which your wallet is synchronized, and any transactions or channels which you use our Node to submit to the supported cryptocurrency networks. Personal Data received by Cake Labs in this manner is not stored for any length of time, and thus Cake Labs is both unwilling to and incapable of sharing this data, or using it for any purpose beyond ensuring your appropriate connection to our Nodes.
|
Personal Data that may be sent through the Cake Labs Nodes is limited to your device's IP address, the block height to which your wallet is synchronized, and any transactions or channels which you use our Node to submit to the supported cryptocurrency networks. Personal Data received by Cake Labs in this manner is not stored for any length of time, and thus Cake Labs is incapable of sharing this data and will not use it for any purpose beyond ensuring your appropriate connection to our Nodes.
|
||||||
|
|
||||||
If you decide to use a Node offered by any third party, some of which we include in our Apps, said third party will receive this Personal Data instead of Cake Labs. We take no responsibility for the actions of any third-party Node offered within the Application. We recommend connecting to your own Node to limit third party sharing of your Personal Information.
|
If you decide to use a Node offered by any third party, some of which we include in our Apps, said third party will receive this Personal Data instead of Cake Labs. We take no responsibility for the actions of any third-party Node offered within the Application. We recommend connecting to your own Node to limit third party sharing of your Personal Information.
|
||||||
|
|
||||||
|
If you use our Fiat API service, you will share your IP address and the cryptocurrency and fiat currency exchange pair for which your wallet requests a spot price quote. You can disable this Fiat API in App privacy settings.
|
||||||
|
|
||||||
Information We May Collect About You and How We Collect It
|
Information We May Collect About You and How We Collect It
|
||||||
----------------------------------------------------------
|
----------------------------------------------------------
|
||||||
|
|
||||||
We collect several types of information from and about users of our App, including information:
|
We collect several types of information from and about users of our App, including information:
|
||||||
- By which you may be personally identified, such as name, e-mail address, or and a/any other identifier by which you may be contacted online or offline ("personal information" or "Personal Data”), ONLY when you provide it to us;
|
- By which you may be personally identified, such as name, e-mail address, or and a/any other identifier by which you may be contacted online or offline ("personal information" or "Personal Data”);
|
||||||
|
- Device data and error log data;
|
||||||
We collect this information:
|
We collect this information:
|
||||||
- Directly from you when you provide it to us.
|
- Directly from you ONLY when you provide it to us.
|
||||||
|
|
||||||
Personal information is received by Cake Labs ONLY in the event that you choose to provide it to us by voluntarily contacting Cake Labs regarding support, questions or suggestions.
|
Personal information is received by Cake Labs ONLY in the event that you choose to provide it to us by voluntarily contacting Cake Labs regarding support, questions or suggestions. You may optionally send us Error reports to help us improve the App. These Error reports contain error logs and basic device data. You can review and make modifications to these Error reports before sending them to us, or you may choose not to send them to us at all.
|
||||||
|
|
||||||
How We Use Your Information
|
How We Use Your Information
|
||||||
---------------------------
|
---------------------------
|
||||||
|
@ -112,7 +115,9 @@ Data Security
|
||||||
Links to Other Websites
|
Links to Other Websites
|
||||||
-----------------------
|
-----------------------
|
||||||
|
|
||||||
The App may contain links to other websites that are not operated by us. If you click on a third-party link, you will be directed to that third party's site. We strongly advise you to review the Privacy Policy of every site you visit. We have no control over and assume no responsibility for the content, privacy policies or practices of any third-party sites or services.
|
The App may contain links to other websites that are not operated by us. If you click on a Third-Party Service link, you will be directed to that third party's site. We strongly advise you to review the Privacy Policy of every site you visit. We have no control over and assume no responsibility for the content, privacy policies or practices of any third-party sites or services.
|
||||||
|
|
||||||
|
The App includes several optional Third-Party Services, which may not be available to all users. If you use Third-Party Services, you must agree to their respective Privacy Policies.
|
||||||
|
|
||||||
Changes to Our Privacy Policy
|
Changes to Our Privacy Policy
|
||||||
-----------------------------
|
-----------------------------
|
||||||
|
|
18
README.md
|
@ -1,4 +1,4 @@
|
||||||
# Cake Wallet for Android and iOS
|
# Cake Wallet for Mobile and Desktop
|
||||||
|
|
||||||
## Open Source Multi-Currency Wallet
|
## Open Source Multi-Currency Wallet
|
||||||
|
|
||||||
|
@ -7,6 +7,7 @@
|
||||||
* Website: https://cakewallet.com
|
* Website: https://cakewallet.com
|
||||||
* App Store (iOS / MacOS): https://cakewallet.com/ios
|
* App Store (iOS / MacOS): https://cakewallet.com/ios
|
||||||
* Google Play: https://cakewallet.com/gp
|
* Google Play: https://cakewallet.com/gp
|
||||||
|
* F-Droid: https://fdroid.cakelabs.com
|
||||||
* APK: https://github.com/cake-tech/cake_wallet/releases
|
* APK: https://github.com/cake-tech/cake_wallet/releases
|
||||||
* Linux: https://github.com/cake-tech/cake_wallet/releases
|
* Linux: https://github.com/cake-tech/cake_wallet/releases
|
||||||
|
|
||||||
|
@ -17,9 +18,8 @@
|
||||||
* Completely noncustodial. *Your keys, your coins.*
|
* Completely noncustodial. *Your keys, your coins.*
|
||||||
* Built-in exchange for dozens of pairs
|
* Built-in exchange for dozens of pairs
|
||||||
* Easily pay cryptocurrency invoices with fixed rate exchanges
|
* Easily pay cryptocurrency invoices with fixed rate exchanges
|
||||||
* Buy cryptocurrency (BTC/LTC/XMR) with credit/debit/bank
|
* Buy cryptocurrency (BTC/LTC/XMR/ETH) with credit/debit/bank
|
||||||
* Sell cryptocurrency by bank transfer
|
* Sell cryptocurrency by bank transfer
|
||||||
* Purchase gift cards at a discount using only an email with [Cake Pay](https://cakepay.com), available in-app
|
|
||||||
* Scan QR codes for easy cryptocurrency transfers
|
* Scan QR codes for easy cryptocurrency transfers
|
||||||
* Create several wallets
|
* Create several wallets
|
||||||
* Select your own custom nodes/servers
|
* Select your own custom nodes/servers
|
||||||
|
@ -32,6 +32,7 @@
|
||||||
* Convenient exchange and sending templates for recurring payments
|
* Convenient exchange and sending templates for recurring payments
|
||||||
* Create donation links and invoices in the receive screen
|
* Create donation links and invoices in the receive screen
|
||||||
* Robust privacy settings (eg: Tor-only connections)
|
* Robust privacy settings (eg: Tor-only connections)
|
||||||
|
* Robust security settings (eg: Cake 2FA)
|
||||||
|
|
||||||
### Monero Specific Features
|
### Monero Specific Features
|
||||||
|
|
||||||
|
@ -40,13 +41,19 @@
|
||||||
* Specify restore height for faster syncing
|
* Specify restore height for faster syncing
|
||||||
* Specify multiple recipients for batch sending
|
* Specify multiple recipients for batch sending
|
||||||
* Optionally set Monero nodes as trusted for faster syncing
|
* Optionally set Monero nodes as trusted for faster syncing
|
||||||
|
* Specify a proxy for Monero nodes, compatible with Tor and i2p
|
||||||
|
|
||||||
### Bitcoin Specific Features
|
### Bitcoin Specific Features
|
||||||
|
|
||||||
* Bitcoin coin control (specify specific outputs to spend)
|
* Bitcoin coin control (specify specific outputs to spend)
|
||||||
* Automatically generate new addresses
|
* Automatically generate new addresses
|
||||||
* Specify multiple recipients for batch sending
|
* Specify multiple recipients for batch sending
|
||||||
* Sell BTC for USD
|
|
||||||
|
### Ethereum Specific Features
|
||||||
|
|
||||||
|
* Store ETH and all ERc-20 tokens
|
||||||
|
* Add custom tokens by contract address
|
||||||
|
* Enable or disable Etherscan for transaction history
|
||||||
|
|
||||||
### Litecoin Specific Features
|
### Litecoin Specific Features
|
||||||
|
|
||||||
|
@ -69,6 +76,7 @@
|
||||||
* Website: https://monero.com
|
* Website: https://monero.com
|
||||||
* App Store (iOS): https://apps.apple.com/app/id1601990386
|
* App Store (iOS): https://apps.apple.com/app/id1601990386
|
||||||
* Google Play: https://play.google.com/store/apps/details?id=com.monero.app
|
* Google Play: https://play.google.com/store/apps/details?id=com.monero.app
|
||||||
|
* F-Droid: https://fdroid.cakelabs.com
|
||||||
* APK: https://github.com/cake-tech/cake_wallet/releases
|
* APK: https://github.com/cake-tech/cake_wallet/releases
|
||||||
|
|
||||||
# Support
|
# Support
|
||||||
|
@ -123,7 +131,7 @@ Edit the applicable `strings_XX.arb` file in `res/values/` and open a pull reque
|
||||||
|
|
||||||
2. Edit the strings in this file, replacing XXX below with the translation for each string.
|
2. Edit the strings in this file, replacing XXX below with the translation for each string.
|
||||||
|
|
||||||
`"welcome" : "Welcome to",` -> `"welcome" : "XXX",`
|
`"welcome": "Welcome to",` -> `"welcome": "XXX",`
|
||||||
|
|
||||||
3. For strings where there is a variable, denoted by a $ symbol and braces, such as ${status}, the string in braces should not be translated. For example, when editing line 106:
|
3. For strings where there is a variable, denoted by a $ symbol and braces, such as ${status}, the string in braces should not be translated. For example, when editing line 106:
|
||||||
|
|
||||||
|
|
|
@ -45,8 +45,8 @@ android {
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId appProperties['id']
|
applicationId appProperties['id']
|
||||||
minSdkVersion 21
|
minSdkVersion 24
|
||||||
targetSdkVersion 31
|
targetSdkVersion 33
|
||||||
versionCode flutterVersionCode.toInteger()
|
versionCode flutterVersionCode.toInteger()
|
||||||
versionName flutterVersionName
|
versionName flutterVersionName
|
||||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||||
|
|
|
@ -14,6 +14,8 @@
|
||||||
android:icon="@mipmap/ic_launcher"
|
android:icon="@mipmap/ic_launcher"
|
||||||
android:allowBackup="false"
|
android:allowBackup="false"
|
||||||
android:fullBackupContent="false"
|
android:fullBackupContent="false"
|
||||||
|
android:versionCode="__versionCode__"
|
||||||
|
android:versionName="__versionName__"
|
||||||
android:requestLegacyExternalStorage="true">
|
android:requestLegacyExternalStorage="true">
|
||||||
<activity
|
<activity
|
||||||
android:name=".MainActivity"
|
android:name=".MainActivity"
|
||||||
|
@ -22,7 +24,6 @@
|
||||||
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
|
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
|
||||||
android:hardwareAccelerated="true"
|
android:hardwareAccelerated="true"
|
||||||
android:windowSoftInputMode="adjustResize"
|
android:windowSoftInputMode="adjustResize"
|
||||||
android:screenOrientation="portrait"
|
|
||||||
android:exported="true">
|
android:exported="true">
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="io.flutter.embedding.android.SplashScreenDrawable"
|
android:name="io.flutter.embedding.android.SplashScreenDrawable"
|
||||||
|
|
|
@ -27,6 +27,6 @@ subprojects {
|
||||||
project.evaluationDependsOn(':app')
|
project.evaluationDependsOn(':app')
|
||||||
}
|
}
|
||||||
|
|
||||||
task clean(type: Delete) {
|
tasks.register("clean", Delete) {
|
||||||
delete rootProject.buildDir
|
delete rootProject.buildDir
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
org.gradle.jvmargs=-Xmx1536M
|
org.gradle.jvmargs=-Xmx1536M
|
||||||
android.enableR8=true
|
android.enableR8=true
|
||||||
android.useAndroidX=true
|
android.useAndroidX=true
|
||||||
android.enableJetifier=true
|
android.enableJetifier=true
|
10
assets/ethereum_server_list.yml
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
-
|
||||||
|
uri: ethereum.publicnode.com
|
||||||
|
-
|
||||||
|
uri: eth.llamarpc.com
|
||||||
|
-
|
||||||
|
uri: rpc.flashbots.net
|
||||||
|
-
|
||||||
|
uri: eth-mainnet.public.blastapi.io
|
||||||
|
-
|
||||||
|
uri: ethereum.publicnode.com
|
BIN
assets/images/aave_icon.png
Normal file
After Width: | Height: | Size: 13 KiB |
BIN
assets/images/arb_icon.png
Normal file
After Width: | Height: | Size: 9.7 KiB |
BIN
assets/images/bat_icon.png
Normal file
After Width: | Height: | Size: 4.2 KiB |
Before Width: | Height: | Size: 10 KiB |
BIN
assets/images/cake_icon.png
Normal file
After Width: | Height: | Size: 9.2 KiB |
Before Width: | Height: | Size: 582 B |
BIN
assets/images/comp_icon.png
Normal file
After Width: | Height: | Size: 5.8 KiB |
BIN
assets/images/cro_icon.png
Normal file
After Width: | Height: | Size: 4.2 KiB |
BIN
assets/images/dydx_icon.png
Normal file
After Width: | Height: | Size: 8.5 KiB |
BIN
assets/images/ens_icon.png
Normal file
After Width: | Height: | Size: 6.9 KiB |
Before Width: | Height: | Size: 1 KiB |
BIN
assets/images/frax_icon.png
Normal file
After Width: | Height: | Size: 5.4 KiB |
BIN
assets/images/ftm_icon.png
Normal file
After Width: | Height: | Size: 5.7 KiB |
Before Width: | Height: | Size: 576 B |
BIN
assets/images/grt_icon.png
Normal file
After Width: | Height: | Size: 5.7 KiB |
BIN
assets/images/gtc_icon.png
Normal file
After Width: | Height: | Size: 6.4 KiB |
BIN
assets/images/gusd_icon.png
Normal file
After Width: | Height: | Size: 6.3 KiB |
Before Width: | Height: | Size: 596 B |
BIN
assets/images/home_screen_settings_icon.png
Normal file
After Width: | Height: | Size: 394 B |
Before Width: | Height: | Size: 683 B |
Before Width: | Height: | Size: 1 KiB |
Before Width: | Height: | Size: 570 B |
BIN
assets/images/ldo_icon.png
Normal file
After Width: | Height: | Size: 8.8 KiB |
BIN
assets/images/live_support.png
Normal file
After Width: | Height: | Size: 1.7 KiB |
BIN
assets/images/more_links.png
Normal file
After Width: | Height: | Size: 1.8 KiB |
BIN
assets/images/nexo_icon.png
Normal file
After Width: | Height: | Size: 4.5 KiB |
BIN
assets/images/pepe_icon.png
Normal file
After Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 538 B |
Before Width: | Height: | Size: 773 B |
Before Width: | Height: | Size: 557 B |
Before Width: | Height: | Size: 680 B |
Before Width: | Height: | Size: 518 B |
BIN
assets/images/steth_icon.png
Normal file
After Width: | Height: | Size: 5 KiB |
BIN
assets/images/storj_icon.png
Normal file
After Width: | Height: | Size: 6.4 KiB |
BIN
assets/images/tusd_icon.png
Normal file
After Width: | Height: | Size: 5.3 KiB |
Before Width: | Height: | Size: 838 B |
Before Width: | Height: | Size: 6.8 KiB |
BIN
assets/images/wallet_guides.png
Normal file
After Width: | Height: | Size: 1.7 KiB |
BIN
assets/images/wbtc_icon.png
Normal file
After Width: | Height: | Size: 8.3 KiB |
BIN
assets/images/weth_icon.png
Normal file
After Width: | Height: | Size: 7 KiB |
Before Width: | Height: | Size: 6 KiB |
BIN
assets/images/zrx_icon.png
Normal file
After Width: | Height: | Size: 4.8 KiB |
|
@ -1,3 +1,2 @@
|
||||||
Enable iPad/Tablet separate layout from mobile UI
|
Bug fixes
|
||||||
SideShift update and fixes
|
Fiat Onramp improvements
|
||||||
Bug Fixes
|
|
|
@ -1,4 +1,2 @@
|
||||||
Enable iPad/Tablet separate layout from mobile UI
|
Bug fixes
|
||||||
SideShift update and fixes
|
Fiat Onramp improvements
|
||||||
Add MoonPay sell
|
|
||||||
Bug Fixes
|
|
10
configure_cake_wallet_android.sh
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
cd scripts/android
|
||||||
|
source ./app_env.sh cakewallet
|
||||||
|
./app_config.sh
|
||||||
|
cd ../.. && flutter pub get
|
||||||
|
cd cw_core && flutter pub get && flutter packages pub run build_runner build --delete-conflicting-outputs && cd ..
|
||||||
|
cd cw_monero && flutter pub get && flutter packages pub run build_runner build --delete-conflicting-outputs && cd ..
|
||||||
|
cd cw_bitcoin && flutter pub get && flutter packages pub run build_runner build --delete-conflicting-outputs && cd ..
|
||||||
|
cd cw_haven && flutter pub get && flutter packages pub run build_runner build --delete-conflicting-outputs && cd ..
|
||||||
|
cd cw_ethereum && flutter pub get && flutter packages pub run build_runner build --delete-conflicting-outputs && cd ..
|
||||||
|
flutter packages pub run build_runner build --delete-conflicting-outputs
|
|
@ -1,4 +1,4 @@
|
||||||
class BitcoinTransactionNoInputsException implements Exception {
|
class BitcoinTransactionNoInputsException implements Exception {
|
||||||
@override
|
@override
|
||||||
String toString() => 'Not enough inputs available';
|
String toString() => 'Not enough inputs available. Please select more under Coin Control';
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,9 +13,11 @@ import 'package:cw_core/wallet_type.dart';
|
||||||
import 'package:hive/hive.dart';
|
import 'package:hive/hive.dart';
|
||||||
import 'package:collection/collection.dart';
|
import 'package:collection/collection.dart';
|
||||||
|
|
||||||
class BitcoinWalletService extends WalletService<BitcoinNewWalletCredentials,
|
class BitcoinWalletService extends WalletService<
|
||||||
BitcoinRestoreWalletFromSeedCredentials, BitcoinRestoreWalletFromWIFCredentials> {
|
BitcoinNewWalletCredentials,
|
||||||
BitcoinWalletService(this.walletInfoSource, this.unspentCoinsInfoSource, this.isDirect);
|
BitcoinRestoreWalletFromSeedCredentials,
|
||||||
|
BitcoinRestoreWalletFromWIFCredentials> {
|
||||||
|
BitcoinWalletService(this.walletInfoSource, this.unspentCoinsInfoSource);
|
||||||
|
|
||||||
final Box<WalletInfo> walletInfoSource;
|
final Box<WalletInfo> walletInfoSource;
|
||||||
final Box<UnspentCoinsInfo> unspentCoinsInfoSource;
|
final Box<UnspentCoinsInfo> unspentCoinsInfoSource;
|
||||||
|
@ -43,8 +45,8 @@ class BitcoinWalletService extends WalletService<BitcoinNewWalletCredentials,
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<BitcoinWallet> openWallet(String name, String password) async {
|
Future<BitcoinWallet> openWallet(String name, String password) async {
|
||||||
final walletInfo = walletInfoSource.values
|
final walletInfo = walletInfoSource.values.firstWhereOrNull(
|
||||||
.firstWhereOrNull((info) => info.id == WalletBase.idFor(name, getType()))!;
|
(info) => info.id == WalletBase.idFor(name, getType()))!;
|
||||||
final wallet = await BitcoinWalletBase.open(
|
final wallet = await BitcoinWalletBase.open(
|
||||||
password: password,
|
password: password,
|
||||||
name: name,
|
name: name,
|
||||||
|
@ -57,23 +59,22 @@ class BitcoinWalletService extends WalletService<BitcoinNewWalletCredentials,
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> remove(String wallet) async {
|
Future<void> remove(String wallet) async {
|
||||||
File(await pathForWalletDir(name: wallet, type: getType())).delete(recursive: true);
|
File(await pathForWalletDir(name: wallet, type: getType()))
|
||||||
final walletInfo = walletInfoSource.values
|
.delete(recursive: true);
|
||||||
.firstWhereOrNull((info) => info.id == WalletBase.idFor(wallet, getType()))!;
|
final walletInfo = walletInfoSource.values.firstWhereOrNull(
|
||||||
|
(info) => info.id == WalletBase.idFor(wallet, getType()))!;
|
||||||
await walletInfoSource.delete(walletInfo.key);
|
await walletInfoSource.delete(walletInfo.key);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> rename(String currentName, String password, String newName) async {
|
Future<void> rename(String currentName, String password, String newName) async {
|
||||||
final currentWalletInfo = walletInfoSource.values
|
final currentWalletInfo = walletInfoSource.values.firstWhereOrNull(
|
||||||
.firstWhereOrNull((info) => info.id == WalletBase.idFor(currentName, getType()))!;
|
(info) => info.id == WalletBase.idFor(currentName, getType()))!;
|
||||||
final currentWallet = await BitcoinWalletBase.open(
|
final currentWallet = await BitcoinWalletBase.open(
|
||||||
password: password,
|
password: password,
|
||||||
name: currentName,
|
name: currentName,
|
||||||
walletInfo: currentWalletInfo,
|
walletInfo: currentWalletInfo,
|
||||||
unspentCoinsInfo: unspentCoinsInfoSource,
|
unspentCoinsInfo: unspentCoinsInfoSource);
|
||||||
encryptionFileUtils: encryptionFileUtilsFor(isDirect),
|
|
||||||
);
|
|
||||||
|
|
||||||
await currentWallet.renameWalletFiles(newName);
|
await currentWallet.renameWalletFiles(newName);
|
||||||
|
|
||||||
|
@ -85,11 +86,13 @@ class BitcoinWalletService extends WalletService<BitcoinNewWalletCredentials,
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<BitcoinWallet> restoreFromKeys(BitcoinRestoreWalletFromWIFCredentials credentials) async =>
|
Future<BitcoinWallet> restoreFromKeys(
|
||||||
|
BitcoinRestoreWalletFromWIFCredentials credentials) async =>
|
||||||
throw UnimplementedError();
|
throw UnimplementedError();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<BitcoinWallet> restoreFromSeed(BitcoinRestoreWalletFromSeedCredentials credentials) async {
|
Future<BitcoinWallet> restoreFromSeed(
|
||||||
|
BitcoinRestoreWalletFromSeedCredentials credentials) async {
|
||||||
if (!validateMnemonic(credentials.mnemonic)) {
|
if (!validateMnemonic(credentials.mnemonic)) {
|
||||||
throw BitcoinMnemonicIsIncorrectException();
|
throw BitcoinMnemonicIsIncorrectException();
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@ import 'package:cw_bitcoin/electrum_transaction_info.dart';
|
||||||
|
|
||||||
part 'electrum_transaction_history.g.dart';
|
part 'electrum_transaction_history.g.dart';
|
||||||
|
|
||||||
const _transactionsHistoryFileName = 'transactions.json';
|
const transactionsHistoryFileName = 'transactions.json';
|
||||||
|
|
||||||
class ElectrumTransactionHistory = ElectrumTransactionHistoryBase
|
class ElectrumTransactionHistory = ElectrumTransactionHistoryBase
|
||||||
with _$ElectrumTransactionHistory;
|
with _$ElectrumTransactionHistory;
|
||||||
|
@ -42,7 +42,7 @@ abstract class ElectrumTransactionHistoryBase
|
||||||
try {
|
try {
|
||||||
final dirPath =
|
final dirPath =
|
||||||
await pathForWalletDir(name: walletInfo.name, type: walletInfo.type);
|
await pathForWalletDir(name: walletInfo.name, type: walletInfo.type);
|
||||||
final path = '$dirPath/$_transactionsHistoryFileName';
|
final path = '$dirPath/$transactionsHistoryFileName';
|
||||||
final data =
|
final data =
|
||||||
json.encode({'height': _height, 'transactions': transactions});
|
json.encode({'height': _height, 'transactions': transactions});
|
||||||
await encryptionFileUtils.write(path: path, password: _password, data: data);
|
await encryptionFileUtils.write(path: path, password: _password, data: data);
|
||||||
|
@ -59,7 +59,7 @@ abstract class ElectrumTransactionHistoryBase
|
||||||
Future<Map<String, dynamic>> _read() async {
|
Future<Map<String, dynamic>> _read() async {
|
||||||
final dirPath =
|
final dirPath =
|
||||||
await pathForWalletDir(name: walletInfo.name, type: walletInfo.type);
|
await pathForWalletDir(name: walletInfo.name, type: walletInfo.type);
|
||||||
final path = '$dirPath/$_transactionsHistoryFileName';
|
final path = '$dirPath/$transactionsHistoryFileName';
|
||||||
final content = await encryptionFileUtils.read(path: path, password: _password);
|
final content = await encryptionFileUtils.read(path: path, password: _password);
|
||||||
return json.decode(content) as Map<String, dynamic>;
|
return json.decode(content) as Map<String, dynamic>;
|
||||||
}
|
}
|
||||||
|
@ -67,7 +67,7 @@ abstract class ElectrumTransactionHistoryBase
|
||||||
Future<void> _load() async {
|
Future<void> _load() async {
|
||||||
try {
|
try {
|
||||||
final content = await _read();
|
final content = await _read();
|
||||||
final txs = content['transactions'] as Map<String, dynamic> ?? {};
|
final txs = content['transactions'] as Map<String, dynamic>? ?? {};
|
||||||
|
|
||||||
txs.entries.forEach((entry) {
|
txs.entries.forEach((entry) {
|
||||||
final val = entry.value;
|
final val = entry.value;
|
||||||
|
@ -86,4 +86,5 @@ abstract class ElectrumTransactionHistoryBase
|
||||||
|
|
||||||
void _update(ElectrumTransactionInfo transaction) =>
|
void _update(ElectrumTransactionInfo transaction) =>
|
||||||
transactions[transaction.id] = transaction;
|
transactions[transaction.id] = transaction;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import 'package:flutter/foundation.dart';
|
|
||||||
import 'package:bitcoin_flutter/bitcoin_flutter.dart' as bitcoin;
|
import 'package:bitcoin_flutter/bitcoin_flutter.dart' as bitcoin;
|
||||||
import 'package:bitcoin_flutter/src/payments/index.dart' show PaymentData;
|
import 'package:bitcoin_flutter/src/payments/index.dart' show PaymentData;
|
||||||
import 'package:cw_bitcoin/address_from_output.dart';
|
import 'package:cw_bitcoin/address_from_output.dart';
|
||||||
|
@ -217,9 +216,9 @@ class ElectrumTransactionInfo extends TransactionInfo {
|
||||||
height: info.height,
|
height: info.height,
|
||||||
amount: info.amount,
|
amount: info.amount,
|
||||||
fee: info.fee,
|
fee: info.fee,
|
||||||
direction: direction ?? info.direction,
|
direction: direction,
|
||||||
date: date ?? info.date,
|
date: date,
|
||||||
isPending: isPending ?? info.isPending,
|
isPending: isPending,
|
||||||
confirmations: info.confirmations);
|
confirmations: info.confirmations);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
import 'dart:io';
|
||||||
import 'dart:math';
|
import 'dart:math';
|
||||||
import 'dart:typed_data';
|
import 'dart:typed_data';
|
||||||
import 'package:cw_bitcoin/encryption_file_utils.dart';
|
import 'package:cw_bitcoin/encryption_file_utils.dart';
|
||||||
|
@ -439,6 +440,29 @@ abstract class ElectrumWalletBase extends WalletBase<ElectrumBalance,
|
||||||
await transactionHistory.save();
|
await transactionHistory.save();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> renameWalletFiles(String newWalletName) async {
|
||||||
|
final currentWalletPath = await pathForWallet(name: walletInfo.name, type: type);
|
||||||
|
final currentWalletFile = File(currentWalletPath);
|
||||||
|
|
||||||
|
final currentDirPath =
|
||||||
|
await pathForWalletDir(name: walletInfo.name, type: type);
|
||||||
|
final currentTransactionsFile = File('$currentDirPath/$transactionsHistoryFileName');
|
||||||
|
|
||||||
|
// Copies current wallet files into new wallet name's dir and files
|
||||||
|
if (currentWalletFile.existsSync()) {
|
||||||
|
final newWalletPath = await pathForWallet(name: newWalletName, type: type);
|
||||||
|
await currentWalletFile.copy(newWalletPath);
|
||||||
|
}
|
||||||
|
if (currentTransactionsFile.existsSync()) {
|
||||||
|
final newDirPath = await pathForWalletDir(name: newWalletName, type: type);
|
||||||
|
await currentTransactionsFile.copy('$newDirPath/$transactionsHistoryFileName');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete old name's dir and files
|
||||||
|
await Directory(currentDirPath).delete(recursive: true);
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> changePassword(String password) async {
|
Future<void> changePassword(String password) async {
|
||||||
_password = password;
|
_password = password;
|
||||||
|
|
|
@ -13,8 +13,10 @@ import 'package:cw_core/wallet_info.dart';
|
||||||
import 'package:cw_core/wallet_base.dart';
|
import 'package:cw_core/wallet_base.dart';
|
||||||
import 'package:collection/collection.dart';
|
import 'package:collection/collection.dart';
|
||||||
|
|
||||||
class LitecoinWalletService extends WalletService<BitcoinNewWalletCredentials,
|
class LitecoinWalletService extends WalletService<
|
||||||
BitcoinRestoreWalletFromSeedCredentials, BitcoinRestoreWalletFromWIFCredentials> {
|
BitcoinNewWalletCredentials,
|
||||||
|
BitcoinRestoreWalletFromSeedCredentials,
|
||||||
|
BitcoinRestoreWalletFromWIFCredentials> {
|
||||||
LitecoinWalletService(this.walletInfoSource, this.unspentCoinsInfoSource, this.isDirect);
|
LitecoinWalletService(this.walletInfoSource, this.unspentCoinsInfoSource, this.isDirect);
|
||||||
|
|
||||||
final Box<WalletInfo> walletInfoSource;
|
final Box<WalletInfo> walletInfoSource;
|
||||||
|
@ -44,8 +46,8 @@ class LitecoinWalletService extends WalletService<BitcoinNewWalletCredentials,
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<LitecoinWallet> openWallet(String name, String password) async {
|
Future<LitecoinWallet> openWallet(String name, String password) async {
|
||||||
final walletInfo = walletInfoSource.values
|
final walletInfo = walletInfoSource.values.firstWhereOrNull(
|
||||||
.firstWhereOrNull((info) => info.id == WalletBase.idFor(name, getType()))!;
|
(info) => info.id == WalletBase.idFor(name, getType()))!;
|
||||||
final wallet = await LitecoinWalletBase.open(
|
final wallet = await LitecoinWalletBase.open(
|
||||||
password: password,
|
password: password,
|
||||||
name: name,
|
name: name,
|
||||||
|
@ -58,23 +60,23 @@ class LitecoinWalletService extends WalletService<BitcoinNewWalletCredentials,
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> remove(String wallet) async {
|
Future<void> remove(String wallet) async {
|
||||||
File(await pathForWalletDir(name: wallet, type: getType())).delete(recursive: true);
|
File(await pathForWalletDir(name: wallet, type: getType()))
|
||||||
final walletInfo = walletInfoSource.values
|
.delete(recursive: true);
|
||||||
.firstWhereOrNull((info) => info.id == WalletBase.idFor(wallet, getType()))!;
|
final walletInfo = walletInfoSource.values.firstWhereOrNull(
|
||||||
|
(info) => info.id == WalletBase.idFor(wallet, getType()))!;
|
||||||
await walletInfoSource.delete(walletInfo.key);
|
await walletInfoSource.delete(walletInfo.key);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> rename(String currentName, String password, String newName) async {
|
Future<void> rename(String currentName, String password, String newName) async {
|
||||||
final currentWalletInfo = walletInfoSource.values
|
final currentWalletInfo = walletInfoSource.values.firstWhereOrNull(
|
||||||
.firstWhereOrNull((info) => info.id == WalletBase.idFor(currentName, getType()))!;
|
(info) => info.id == WalletBase.idFor(currentName, getType()))!;
|
||||||
final currentWallet = await LitecoinWalletBase.open(
|
final currentWallet = await LitecoinWalletBase.open(
|
||||||
password: password,
|
password: password,
|
||||||
name: currentName,
|
name: currentName,
|
||||||
walletInfo: currentWalletInfo,
|
walletInfo: currentWalletInfo,
|
||||||
unspentCoinsInfo: unspentCoinsInfoSource,
|
unspentCoinsInfo: unspentCoinsInfoSource,
|
||||||
encryptionFileUtils: encryptionFileUtilsFor(isDirect),
|
encryptionFileUtils: encryptionFileUtilsFor(isDirect));
|
||||||
);
|
|
||||||
|
|
||||||
await currentWallet.renameWalletFiles(newName);
|
await currentWallet.renameWalletFiles(newName);
|
||||||
|
|
||||||
|
|
|
@ -37,19 +37,19 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: async
|
name: async
|
||||||
sha256: bfe67ef28df125b7dddcea62755991f807aa39a2492a23e1550161692950bbe0
|
sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.10.0"
|
version: "2.11.0"
|
||||||
bech32:
|
bech32:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
path: "."
|
path: "."
|
||||||
ref: "cake-0.2.1"
|
ref: "cake-0.2.2"
|
||||||
resolved-ref: cafd1c270641e95017d57d69f55cca9831d4db56
|
resolved-ref: "05755063b593aa6cca0a4820a318e0ce17de6192"
|
||||||
url: "https://github.com/cake-tech/bech32.git"
|
url: "https://github.com/cake-tech/bech32.git"
|
||||||
source: git
|
source: git
|
||||||
version: "0.2.1"
|
version: "0.2.2"
|
||||||
bip32:
|
bip32:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -70,8 +70,8 @@ packages:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
path: "."
|
path: "."
|
||||||
ref: cake-update-v2
|
ref: cake-update-v3
|
||||||
resolved-ref: "8f86453761c0c26e368392d0ff2c6f12f3b7397b"
|
resolved-ref: df9204144011ed9419eff7d9ef3143102a40252d
|
||||||
url: "https://github.com/cake-tech/bitcoin_flutter.git"
|
url: "https://github.com/cake-tech/bitcoin_flutter.git"
|
||||||
source: git
|
source: git
|
||||||
version: "2.0.2"
|
version: "2.0.2"
|
||||||
|
@ -168,10 +168,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: characters
|
name: characters
|
||||||
sha256: e6a326c8af69605aec75ed6c187d06b349707a27fbff8222ca9cc2cff167975c
|
sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.2.1"
|
version: "1.3.0"
|
||||||
checked_yaml:
|
checked_yaml:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -200,18 +200,18 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: collection
|
name: collection
|
||||||
sha256: cfc915e6923fe5ce6e153b0723c753045de46de1b4d63771530504004a45fae0
|
sha256: "4a07be6cb69c84d677a6c3096fcf960cc3285a8330b4603e0d463d15d9bd934c"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.17.0"
|
version: "1.17.1"
|
||||||
convert:
|
convert:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: convert
|
name: convert
|
||||||
sha256: "196284f26f69444b7f5c50692b55ec25da86d9e500451dc09333bf2e3ad69259"
|
sha256: "0f08b14755d163f6e2134cb58222dd25ea2a2ee8a195e53983d57c075324d592"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.0.2"
|
version: "3.1.1"
|
||||||
crypto:
|
crypto:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -361,10 +361,10 @@ packages:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: http
|
name: http
|
||||||
sha256: "6aa2946395183537c8b880962d935877325d6a09a2867c3970c05c0fed6ac482"
|
sha256: "759d1a329847dd0f39226c688d3e06a6b8679668e350e2891a6474f8b4bb8525"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.13.5"
|
version: "1.1.0"
|
||||||
http_multi_server:
|
http_multi_server:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -385,10 +385,10 @@ packages:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: intl
|
name: intl
|
||||||
sha256: "910f85bce16fb5c6f614e117efa303e85a1731bb0081edf3604a2ae6e9a3cc91"
|
sha256: "3bc132a9dbce73a7e4a21a17d06e1878839ffbf975568bc875c60537824b0c4d"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.17.0"
|
version: "0.18.1"
|
||||||
io:
|
io:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -401,10 +401,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: js
|
name: js
|
||||||
sha256: "5528c2f391ededb7775ec1daa69e65a2d61276f7552de2b5f7b8d34ee9fd4ab7"
|
sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.6.5"
|
version: "0.6.7"
|
||||||
json_annotation:
|
json_annotation:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -425,10 +425,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: matcher
|
name: matcher
|
||||||
sha256: "16db949ceee371e9b99d22f88fa3a73c4e59fd0afed0bd25fc336eb76c198b72"
|
sha256: "6501fbd55da300384b768785b83e5ce66991266cec21af89ab9ae7f5ce1c4cbb"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.12.13"
|
version: "0.12.15"
|
||||||
material_color_utilities:
|
material_color_utilities:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -441,10 +441,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: meta
|
name: meta
|
||||||
sha256: "6c268b42ed578a53088d834796959e4a1814b5e9e164f147f580a386e5decf42"
|
sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.8.0"
|
version: "1.9.1"
|
||||||
mime:
|
mime:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -481,10 +481,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: path
|
name: path
|
||||||
sha256: db9d4f58c908a4ba5953fcee2ae317c94889433e5024c27ce74a37f94267945b
|
sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.8.2"
|
version: "1.8.3"
|
||||||
path_provider:
|
path_provider:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
@ -686,10 +686,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: test_api
|
name: test_api
|
||||||
sha256: ad540f65f92caa91bf21dfc8ffb8c589d6e4dc0c2267818b4cc2792857706206
|
sha256: eb6ac1540b26de412b3403a163d919ba86f6a973fe6cc50ae3541b80092fdcfb
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.4.16"
|
version: "0.5.1"
|
||||||
timing:
|
timing:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -771,5 +771,5 @@ packages:
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.1.1"
|
version: "3.1.1"
|
||||||
sdks:
|
sdks:
|
||||||
dart: ">=2.19.0 <4.0.0"
|
dart: ">=3.0.0 <4.0.0"
|
||||||
flutter: ">=3.0.0"
|
flutter: ">=3.0.0"
|
||||||
|
|
|
@ -13,16 +13,16 @@ dependencies:
|
||||||
flutter:
|
flutter:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
path_provider: ^2.0.11
|
path_provider: ^2.0.11
|
||||||
http: ^0.13.4
|
http: ^1.1.0
|
||||||
mobx: ^2.0.7+4
|
mobx: ^2.0.7+4
|
||||||
flutter_mobx: ^2.0.6+1
|
flutter_mobx: ^2.0.6+1
|
||||||
intl: ^0.17.0
|
intl: ^0.18.0
|
||||||
cw_core:
|
cw_core:
|
||||||
path: ../cw_core
|
path: ../cw_core
|
||||||
bitcoin_flutter:
|
bitcoin_flutter:
|
||||||
git:
|
git:
|
||||||
url: https://github.com/cake-tech/bitcoin_flutter.git
|
url: https://github.com/cake-tech/bitcoin_flutter.git
|
||||||
ref: cake-update-v2
|
ref: cake-update-v3
|
||||||
rxdart: ^0.27.5
|
rxdart: ^0.27.5
|
||||||
unorm_dart: ^0.2.0
|
unorm_dart: ^0.2.0
|
||||||
cryptography: ^2.0.5
|
cryptography: ^2.0.5
|
||||||
|
@ -32,7 +32,7 @@ dependencies:
|
||||||
url: https://github.com/cake-tech/cake_backup.git
|
url: https://github.com/cake-tech/cake_backup.git
|
||||||
ref: main
|
ref: main
|
||||||
version: 1.0.0
|
version: 1.0.0
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
|
|
21
cw_core/lib/address_info.dart
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
import 'package:cw_core/hive_type_ids.dart';
|
||||||
|
import 'package:hive/hive.dart';
|
||||||
|
|
||||||
|
part 'address_info.g.dart';
|
||||||
|
|
||||||
|
@HiveType(typeId: ADDRESS_INFO_TYPE_ID)
|
||||||
|
class AddressInfo extends HiveObject {
|
||||||
|
AddressInfo({required this.address, this.accountIndex, required this.label});
|
||||||
|
|
||||||
|
static const typeId = ADDRESS_INFO_TYPE_ID;
|
||||||
|
static const boxName = 'AddressInfo';
|
||||||
|
|
||||||
|
@HiveField(0)
|
||||||
|
int? accountIndex;
|
||||||
|
|
||||||
|
@HiveField(1, defaultValue: '')
|
||||||
|
String address;
|
||||||
|
|
||||||
|
@HiveField(2, defaultValue: '')
|
||||||
|
String label;
|
||||||
|
}
|
4
cw_core/lib/cake_hive.dart
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
import 'package:hive/hive.dart';
|
||||||
|
import 'package:hive/src/hive_impl.dart';
|
||||||
|
|
||||||
|
final HiveInterface CakeHive = HiveImpl();
|
|
@ -11,6 +11,8 @@ CryptoCurrency currencyForWalletType(WalletType type) {
|
||||||
return CryptoCurrency.ltc;
|
return CryptoCurrency.ltc;
|
||||||
case WalletType.haven:
|
case WalletType.haven:
|
||||||
return CryptoCurrency.xhv;
|
return CryptoCurrency.xhv;
|
||||||
|
case WalletType.ethereum:
|
||||||
|
return CryptoCurrency.eth;
|
||||||
default:
|
default:
|
||||||
throw Exception('Unexpected wallet type: ${type.toString()} for CryptoCurrency currencyForWalletType');
|
throw Exception('Unexpected wallet type: ${type.toString()} for CryptoCurrency currencyForWalletType');
|
||||||
}
|
}
|
||||||
|
|
66
cw_core/lib/erc20_token.dart
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
import 'package:cw_core/crypto_currency.dart';
|
||||||
|
import 'package:cw_core/hive_type_ids.dart';
|
||||||
|
import 'package:hive/hive.dart';
|
||||||
|
|
||||||
|
part 'erc20_token.g.dart';
|
||||||
|
|
||||||
|
@HiveType(typeId: Erc20Token.typeId)
|
||||||
|
class Erc20Token extends CryptoCurrency with HiveObjectMixin {
|
||||||
|
@HiveField(0)
|
||||||
|
final String name;
|
||||||
|
@HiveField(1)
|
||||||
|
final String symbol;
|
||||||
|
@HiveField(2)
|
||||||
|
final String contractAddress;
|
||||||
|
@HiveField(3)
|
||||||
|
final int decimal;
|
||||||
|
@HiveField(4, defaultValue: true)
|
||||||
|
bool _enabled;
|
||||||
|
@HiveField(5)
|
||||||
|
final String? iconPath;
|
||||||
|
|
||||||
|
bool get enabled => _enabled;
|
||||||
|
|
||||||
|
set enabled(bool value) => _enabled = value;
|
||||||
|
|
||||||
|
Erc20Token({
|
||||||
|
required this.name,
|
||||||
|
required this.symbol,
|
||||||
|
required this.contractAddress,
|
||||||
|
required this.decimal,
|
||||||
|
bool enabled = true,
|
||||||
|
this.iconPath,
|
||||||
|
}) : _enabled = enabled,
|
||||||
|
super(
|
||||||
|
name: symbol.toLowerCase(),
|
||||||
|
title: symbol.toUpperCase(),
|
||||||
|
fullName: name,
|
||||||
|
tag: "ETH",
|
||||||
|
iconPath: iconPath,
|
||||||
|
);
|
||||||
|
|
||||||
|
Erc20Token.copyWith(Erc20Token other, String? icon)
|
||||||
|
: this.name = other.name,
|
||||||
|
this.symbol = other.symbol,
|
||||||
|
this.contractAddress = other.contractAddress,
|
||||||
|
this.decimal = other.decimal,
|
||||||
|
this._enabled = other.enabled,
|
||||||
|
this.iconPath = icon,
|
||||||
|
super(
|
||||||
|
name: other.name,
|
||||||
|
title: other.symbol.toUpperCase(),
|
||||||
|
fullName: other.name,
|
||||||
|
tag: "ETH",
|
||||||
|
iconPath: icon,
|
||||||
|
);
|
||||||
|
|
||||||
|
static const typeId = ERC20_TOKEN_TYPE_ID;
|
||||||
|
static const boxName = 'Erc20Tokens';
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(other) => (other is Erc20Token && other.contractAddress == contractAddress) ||
|
||||||
|
(other is CryptoCurrency && other.title == title);
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode => contractAddress.hashCode;
|
||||||
|
}
|
13
cw_core/lib/hive_type_ids.dart
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
const CONTACT_TYPE_ID = 0;
|
||||||
|
const NODE_TYPE_ID = 1;
|
||||||
|
const TRANSACTION_TYPE_ID = 2;
|
||||||
|
const TRADE_TYPE_ID = 3;
|
||||||
|
const WALLET_INFO_TYPE_ID = 4;
|
||||||
|
const WALLET_TYPE_TYPE_ID = 5;
|
||||||
|
const TEMPLATE_TYPE_ID = 6;
|
||||||
|
const EXCHANGE_TEMPLATE_TYPE_ID = 7;
|
||||||
|
const ORDER_TYPE_ID = 8;
|
||||||
|
const UNSPENT_COINS_INFO_TYPE_ID = 9;
|
||||||
|
const ANONPAY_INVOICE_INFO_TYPE_ID = 10;
|
||||||
|
const ADDRESS_INFO_TYPE_ID = 11;
|
||||||
|
const ERC20_TOKEN_TYPE_ID = 12;
|
|
@ -2,24 +2,31 @@ import 'package:cw_core/balance.dart';
|
||||||
import 'package:cw_core/monero_amount_format.dart';
|
import 'package:cw_core/monero_amount_format.dart';
|
||||||
|
|
||||||
class MoneroBalance extends Balance {
|
class MoneroBalance extends Balance {
|
||||||
MoneroBalance({required this.fullBalance, required this.unlockedBalance})
|
MoneroBalance({required this.fullBalance, required this.unlockedBalance, this.frozenBalance = 0})
|
||||||
: formattedFullBalance = moneroAmountToString(amount: fullBalance),
|
: formattedFullBalance = moneroAmountToString(amount: fullBalance),
|
||||||
formattedUnlockedBalance =
|
formattedUnlockedBalance = moneroAmountToString(amount: unlockedBalance),
|
||||||
moneroAmountToString(amount: unlockedBalance),
|
frozenFormatted = moneroAmountToString(amount: frozenBalance),
|
||||||
super(unlockedBalance, fullBalance);
|
super(unlockedBalance, fullBalance);
|
||||||
|
|
||||||
MoneroBalance.fromString(
|
MoneroBalance.fromString(
|
||||||
{required this.formattedFullBalance,
|
{required this.formattedFullBalance,
|
||||||
required this.formattedUnlockedBalance})
|
required this.formattedUnlockedBalance,
|
||||||
|
this.frozenFormatted = '0.0'})
|
||||||
: fullBalance = moneroParseAmount(amount: formattedFullBalance),
|
: fullBalance = moneroParseAmount(amount: formattedFullBalance),
|
||||||
unlockedBalance = moneroParseAmount(amount: formattedUnlockedBalance),
|
unlockedBalance = moneroParseAmount(amount: formattedUnlockedBalance),
|
||||||
|
frozenBalance = moneroParseAmount(amount: frozenFormatted),
|
||||||
super(moneroParseAmount(amount: formattedUnlockedBalance),
|
super(moneroParseAmount(amount: formattedUnlockedBalance),
|
||||||
moneroParseAmount(amount: formattedFullBalance));
|
moneroParseAmount(amount: formattedFullBalance));
|
||||||
|
|
||||||
final int fullBalance;
|
final int fullBalance;
|
||||||
final int unlockedBalance;
|
final int unlockedBalance;
|
||||||
|
final int frozenBalance;
|
||||||
final String formattedFullBalance;
|
final String formattedFullBalance;
|
||||||
final String formattedUnlockedBalance;
|
final String formattedUnlockedBalance;
|
||||||
|
final String frozenFormatted;
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get formattedFrozenBalance => frozenFormatted == '0.0' ? '' : frozenFormatted;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get formattedAvailableBalance => formattedUnlockedBalance;
|
String get formattedAvailableBalance => formattedUnlockedBalance;
|
||||||
|
|
|
@ -3,6 +3,7 @@ import 'package:cw_core/keyable.dart';
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
import 'package:http/http.dart' as http;
|
import 'package:http/http.dart' as http;
|
||||||
import 'package:hive/hive.dart';
|
import 'package:hive/hive.dart';
|
||||||
|
import 'package:cw_core/hive_type_ids.dart';
|
||||||
import 'package:cw_core/wallet_type.dart';
|
import 'package:cw_core/wallet_type.dart';
|
||||||
import 'package:http/io_client.dart' as ioc;
|
import 'package:http/io_client.dart' as ioc;
|
||||||
|
|
||||||
|
@ -18,6 +19,7 @@ class Node extends HiveObject with Keyable {
|
||||||
this.password,
|
this.password,
|
||||||
this.useSSL,
|
this.useSSL,
|
||||||
this.trusted = false,
|
this.trusted = false,
|
||||||
|
this.socksProxyAddress,
|
||||||
String? uri,
|
String? uri,
|
||||||
WalletType? type,}) {
|
WalletType? type,}) {
|
||||||
if (uri != null) {
|
if (uri != null) {
|
||||||
|
@ -33,9 +35,10 @@ class Node extends HiveObject with Keyable {
|
||||||
login = map['login'] as String?,
|
login = map['login'] as String?,
|
||||||
password = map['password'] as String?,
|
password = map['password'] as String?,
|
||||||
useSSL = map['useSSL'] as bool?,
|
useSSL = map['useSSL'] as bool?,
|
||||||
trusted = map['trusted'] as bool? ?? false;
|
trusted = map['trusted'] as bool? ?? false,
|
||||||
|
socksProxyAddress = map['socksProxyPort'] as String?;
|
||||||
|
|
||||||
static const typeId = 1;
|
static const typeId = NODE_TYPE_ID;
|
||||||
static const boxName = 'Nodes';
|
static const boxName = 'Nodes';
|
||||||
|
|
||||||
@HiveField(0, defaultValue: '')
|
@HiveField(0, defaultValue: '')
|
||||||
|
@ -56,8 +59,13 @@ class Node extends HiveObject with Keyable {
|
||||||
@HiveField(5, defaultValue: false)
|
@HiveField(5, defaultValue: false)
|
||||||
bool trusted;
|
bool trusted;
|
||||||
|
|
||||||
|
@HiveField(6)
|
||||||
|
String? socksProxyAddress;
|
||||||
|
|
||||||
bool get isSSL => useSSL ?? false;
|
bool get isSSL => useSSL ?? false;
|
||||||
|
|
||||||
|
bool get useSocksProxy => socksProxyAddress == null ? false : socksProxyAddress!.isNotEmpty;
|
||||||
|
|
||||||
Uri get uri {
|
Uri get uri {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case WalletType.monero:
|
case WalletType.monero:
|
||||||
|
@ -68,6 +76,8 @@ class Node extends HiveObject with Keyable {
|
||||||
return createUriFromElectrumAddress(uriRaw);
|
return createUriFromElectrumAddress(uriRaw);
|
||||||
case WalletType.haven:
|
case WalletType.haven:
|
||||||
return Uri.http(uriRaw, '');
|
return Uri.http(uriRaw, '');
|
||||||
|
case WalletType.ethereum:
|
||||||
|
return Uri.https(uriRaw, '');
|
||||||
default:
|
default:
|
||||||
throw Exception('Unexpected type ${type.toString()} for Node uri');
|
throw Exception('Unexpected type ${type.toString()} for Node uri');
|
||||||
}
|
}
|
||||||
|
@ -81,7 +91,8 @@ class Node extends HiveObject with Keyable {
|
||||||
other.password == password &&
|
other.password == password &&
|
||||||
other.typeRaw == typeRaw &&
|
other.typeRaw == typeRaw &&
|
||||||
other.useSSL == useSSL &&
|
other.useSSL == useSSL &&
|
||||||
other.trusted == trusted);
|
other.trusted == trusted &&
|
||||||
|
other.socksProxyAddress == socksProxyAddress);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
int get hashCode =>
|
int get hashCode =>
|
||||||
|
@ -90,7 +101,8 @@ class Node extends HiveObject with Keyable {
|
||||||
password.hashCode ^
|
password.hashCode ^
|
||||||
typeRaw.hashCode ^
|
typeRaw.hashCode ^
|
||||||
useSSL.hashCode ^
|
useSSL.hashCode ^
|
||||||
trusted.hashCode;
|
trusted.hashCode ^
|
||||||
|
socksProxyAddress.hashCode;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
dynamic get keyIndex {
|
dynamic get keyIndex {
|
||||||
|
@ -108,13 +120,15 @@ class Node extends HiveObject with Keyable {
|
||||||
try {
|
try {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case WalletType.monero:
|
case WalletType.monero:
|
||||||
return requestMoneroNode();
|
return useSocksProxy ? requestNodeWithProxy(socksProxyAddress ?? '') : requestMoneroNode();
|
||||||
case WalletType.bitcoin:
|
case WalletType.bitcoin:
|
||||||
return requestElectrumServer();
|
return requestElectrumServer();
|
||||||
case WalletType.litecoin:
|
case WalletType.litecoin:
|
||||||
return requestElectrumServer();
|
return requestElectrumServer();
|
||||||
case WalletType.haven:
|
case WalletType.haven:
|
||||||
return requestMoneroNode();
|
return requestMoneroNode();
|
||||||
|
case WalletType.ethereum:
|
||||||
|
return requestElectrumServer();
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -157,7 +171,23 @@ class Node extends HiveObject with Keyable {
|
||||||
} catch (_) {
|
} catch (_) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<bool> requestNodeWithProxy(String proxy) async {
|
||||||
|
|
||||||
|
if (proxy.isEmpty || !proxy.contains(':')) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
final proxyAddress = proxy.split(':')[0];
|
||||||
|
final proxyPort = int.parse(proxy.split(':')[1]);
|
||||||
|
try {
|
||||||
|
final socket = await Socket.connect(proxyAddress, proxyPort, timeout: Duration(seconds: 5));
|
||||||
|
socket.destroy();
|
||||||
|
return true;
|
||||||
|
} catch (_) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Future<bool> requestElectrumServer() async {
|
Future<bool> requestElectrumServer() async {
|
||||||
try {
|
try {
|
||||||
|
@ -168,4 +198,17 @@ class Node extends HiveObject with Keyable {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<bool> requestEthereumServer() async {
|
||||||
|
try {
|
||||||
|
final response = await http.get(
|
||||||
|
uri,
|
||||||
|
headers: {'Content-Type': 'application/json'},
|
||||||
|
);
|
||||||
|
|
||||||
|
return response.statusCode >= 200 && response.statusCode < 300;
|
||||||
|
} catch (_) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
|
|
||||||
const utils = const MethodChannel('com.cake_wallet/native_utils');
|
|
||||||
|
|
||||||
void setIsAppSecureNative(bool isAppSecure) {
|
void setIsAppSecureNative(bool isAppSecure) {
|
||||||
utils.invokeMethod<Uint8List>('setIsAppSecure', {'isAppSecure': isAppSecure});
|
try {
|
||||||
}
|
final utils = const MethodChannel('com.cake_wallet/native_utils');
|
||||||
|
|
||||||
|
utils.invokeMethod<Uint8List>('setIsAppSecure', {'isAppSecure': isAppSecure});
|
||||||
|
} catch (_) {}
|
||||||
|
}
|
||||||
|
|
|
@ -14,6 +14,8 @@ abstract class TransactionHistoryBase<TransactionType extends TransactionInfo> {
|
||||||
|
|
||||||
void addMany(Map<String, TransactionType> transactions);
|
void addMany(Map<String, TransactionType> transactions);
|
||||||
|
|
||||||
|
void clear() => transactions.clear();
|
||||||
|
|
||||||
// bool _isUpdating;
|
// bool _isUpdating;
|
||||||
|
|
||||||
// @action
|
// @action
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import 'package:cw_core/hive_type_ids.dart';
|
||||||
import 'package:hive/hive.dart';
|
import 'package:hive/hive.dart';
|
||||||
|
|
||||||
part 'unspent_coins_info.g.dart';
|
part 'unspent_coins_info.g.dart';
|
||||||
|
@ -12,9 +13,11 @@ class UnspentCoinsInfo extends HiveObject {
|
||||||
required this.noteRaw,
|
required this.noteRaw,
|
||||||
required this.address,
|
required this.address,
|
||||||
required this.vout,
|
required this.vout,
|
||||||
required this.value});
|
required this.value,
|
||||||
|
this.keyImage = null
|
||||||
|
});
|
||||||
|
|
||||||
static const typeId = 9;
|
static const typeId = UNSPENT_COINS_INFO_TYPE_ID;
|
||||||
static const boxName = 'Unspent';
|
static const boxName = 'Unspent';
|
||||||
static const boxKey = 'unspentBoxKey';
|
static const boxKey = 'unspentBoxKey';
|
||||||
|
|
||||||
|
@ -42,7 +45,10 @@ class UnspentCoinsInfo extends HiveObject {
|
||||||
@HiveField(7, defaultValue: 0)
|
@HiveField(7, defaultValue: 0)
|
||||||
int vout;
|
int vout;
|
||||||
|
|
||||||
|
@HiveField(8, defaultValue: null)
|
||||||
|
String? keyImage;
|
||||||
|
|
||||||
String get note => noteRaw ?? '';
|
String get note => noteRaw ?? '';
|
||||||
|
|
||||||
set note(String value) => noteRaw = value;
|
set note(String value) => noteRaw = value;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
|
import 'package:cw_core/address_info.dart';
|
||||||
import 'package:cw_core/wallet_info.dart';
|
import 'package:cw_core/wallet_info.dart';
|
||||||
|
|
||||||
abstract class WalletAddresses {
|
abstract class WalletAddresses {
|
||||||
WalletAddresses(this.walletInfo)
|
WalletAddresses(this.walletInfo)
|
||||||
: addressesMap = {};
|
: addressesMap = {},
|
||||||
|
addressInfos = {};
|
||||||
|
|
||||||
final WalletInfo walletInfo;
|
final WalletInfo walletInfo;
|
||||||
|
|
||||||
|
@ -12,6 +14,10 @@ abstract class WalletAddresses {
|
||||||
|
|
||||||
Map<String, String> addressesMap;
|
Map<String, String> addressesMap;
|
||||||
|
|
||||||
|
Map<int, List<AddressInfo>> addressInfos;
|
||||||
|
|
||||||
|
Set<String> usedAddresses = {};
|
||||||
|
|
||||||
Future<void> init();
|
Future<void> init();
|
||||||
|
|
||||||
Future<void> updateAddressesInBox();
|
Future<void> updateAddressesInBox();
|
||||||
|
@ -20,6 +26,8 @@ abstract class WalletAddresses {
|
||||||
try {
|
try {
|
||||||
walletInfo.address = address;
|
walletInfo.address = address;
|
||||||
walletInfo.addresses = addressesMap;
|
walletInfo.addresses = addressesMap;
|
||||||
|
walletInfo.addressInfos = addressInfos;
|
||||||
|
walletInfo.usedAddresses = usedAddresses.toList();
|
||||||
|
|
||||||
if (walletInfo.isInBox) {
|
if (walletInfo.isInBox) {
|
||||||
await walletInfo.save();
|
await walletInfo.save();
|
||||||
|
|
|
@ -42,7 +42,9 @@ abstract class WalletBase<
|
||||||
|
|
||||||
set syncStatus(SyncStatus status);
|
set syncStatus(SyncStatus status);
|
||||||
|
|
||||||
String get seed;
|
String? get seed;
|
||||||
|
|
||||||
|
String? get privateKey => null;
|
||||||
|
|
||||||
Object get keys;
|
Object get keys;
|
||||||
|
|
||||||
|
@ -50,6 +52,10 @@ abstract class WalletBase<
|
||||||
|
|
||||||
late HistoryType transactionHistory;
|
late HistoryType transactionHistory;
|
||||||
|
|
||||||
|
set isEnabledAutoGenerateSubaddress(bool value) {}
|
||||||
|
|
||||||
|
bool get isEnabledAutoGenerateSubaddress => false;
|
||||||
|
|
||||||
Future<void> connectToNode({required Node node});
|
Future<void> connectToNode({required Node node});
|
||||||
|
|
||||||
Future<void> startSync();
|
Future<void> startSync();
|
||||||
|
@ -77,4 +83,6 @@ abstract class WalletBase<
|
||||||
Future<void>? updateBalance();
|
Future<void>? updateBalance();
|
||||||
|
|
||||||
void setExceptionHandler(void Function(FlutterErrorDetails) onError) => null;
|
void setExceptionHandler(void Function(FlutterErrorDetails) onError) => null;
|
||||||
|
|
||||||
|
Future<void> renameWalletFiles(String newWalletName);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
import 'package:flutter/foundation.dart';
|
|
||||||
import 'package:hive/hive.dart';
|
|
||||||
import 'package:cw_core/wallet_type.dart';
|
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
import 'package:cw_core/address_info.dart';
|
||||||
|
import 'package:cw_core/hive_type_ids.dart';
|
||||||
|
import 'package:cw_core/wallet_type.dart';
|
||||||
|
import 'package:hive/hive.dart';
|
||||||
|
|
||||||
part 'wallet_info.g.dart';
|
part 'wallet_info.g.dart';
|
||||||
|
|
||||||
|
@ -30,7 +31,7 @@ class WalletInfo extends HiveObject {
|
||||||
yatEid, yatLastUsedAddressRaw, showIntroCakePayCard);
|
yatEid, yatLastUsedAddressRaw, showIntroCakePayCard);
|
||||||
}
|
}
|
||||||
|
|
||||||
static const typeId = 4;
|
static const typeId = WALLET_INFO_TYPE_ID;
|
||||||
static const boxName = 'WalletInfo';
|
static const boxName = 'WalletInfo';
|
||||||
|
|
||||||
@HiveField(0, defaultValue: '')
|
@HiveField(0, defaultValue: '')
|
||||||
|
@ -72,6 +73,12 @@ class WalletInfo extends HiveObject {
|
||||||
@HiveField(13)
|
@HiveField(13)
|
||||||
bool? showIntroCakePayCard;
|
bool? showIntroCakePayCard;
|
||||||
|
|
||||||
|
@HiveField(14)
|
||||||
|
Map<int, List<AddressInfo>>? addressInfos;
|
||||||
|
|
||||||
|
@HiveField(15)
|
||||||
|
List<String>? usedAddresses;
|
||||||
|
|
||||||
String get yatLastUsedAddress => yatLastUsedAddressRaw ?? '';
|
String get yatLastUsedAddress => yatLastUsedAddressRaw ?? '';
|
||||||
|
|
||||||
set yatLastUsedAddress(String address) {
|
set yatLastUsedAddress(String address) {
|
||||||
|
|
|
@ -17,4 +17,6 @@ abstract class WalletService<N extends WalletCredentials,
|
||||||
Future<bool> isWalletExit(String name);
|
Future<bool> isWalletExit(String name);
|
||||||
|
|
||||||
Future<void> remove(String wallet);
|
Future<void> remove(String wallet);
|
||||||
|
|
||||||
|
Future<void> rename(String currentName, String password, String newName);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import 'package:cw_core/crypto_currency.dart';
|
import 'package:cw_core/crypto_currency.dart';
|
||||||
|
import 'package:cw_core/hive_type_ids.dart';
|
||||||
import 'package:hive/hive.dart';
|
import 'package:hive/hive.dart';
|
||||||
|
|
||||||
part 'wallet_type.g.dart';
|
part 'wallet_type.g.dart';
|
||||||
|
@ -7,11 +8,11 @@ const walletTypes = [
|
||||||
WalletType.monero,
|
WalletType.monero,
|
||||||
WalletType.bitcoin,
|
WalletType.bitcoin,
|
||||||
WalletType.litecoin,
|
WalletType.litecoin,
|
||||||
WalletType.haven
|
WalletType.haven,
|
||||||
|
WalletType.ethereum,
|
||||||
];
|
];
|
||||||
const walletTypeTypeId = 5;
|
|
||||||
|
|
||||||
@HiveType(typeId: walletTypeTypeId)
|
@HiveType(typeId: WALLET_TYPE_TYPE_ID)
|
||||||
enum WalletType {
|
enum WalletType {
|
||||||
@HiveField(0)
|
@HiveField(0)
|
||||||
monero,
|
monero,
|
||||||
|
@ -26,7 +27,10 @@ enum WalletType {
|
||||||
litecoin,
|
litecoin,
|
||||||
|
|
||||||
@HiveField(4)
|
@HiveField(4)
|
||||||
haven
|
haven,
|
||||||
|
|
||||||
|
@HiveField(5)
|
||||||
|
ethereum,
|
||||||
}
|
}
|
||||||
|
|
||||||
int serializeToInt(WalletType type) {
|
int serializeToInt(WalletType type) {
|
||||||
|
@ -39,6 +43,8 @@ int serializeToInt(WalletType type) {
|
||||||
return 2;
|
return 2;
|
||||||
case WalletType.haven:
|
case WalletType.haven:
|
||||||
return 3;
|
return 3;
|
||||||
|
case WalletType.ethereum:
|
||||||
|
return 4;
|
||||||
default:
|
default:
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
@ -54,6 +60,8 @@ WalletType deserializeFromInt(int raw) {
|
||||||
return WalletType.litecoin;
|
return WalletType.litecoin;
|
||||||
case 3:
|
case 3:
|
||||||
return WalletType.haven;
|
return WalletType.haven;
|
||||||
|
case 4:
|
||||||
|
return WalletType.ethereum;
|
||||||
default:
|
default:
|
||||||
throw Exception('Unexpected token: $raw for WalletType deserializeFromInt');
|
throw Exception('Unexpected token: $raw for WalletType deserializeFromInt');
|
||||||
}
|
}
|
||||||
|
@ -69,6 +77,8 @@ String walletTypeToString(WalletType type) {
|
||||||
return 'Litecoin';
|
return 'Litecoin';
|
||||||
case WalletType.haven:
|
case WalletType.haven:
|
||||||
return 'Haven';
|
return 'Haven';
|
||||||
|
case WalletType.ethereum:
|
||||||
|
return 'Ethereum';
|
||||||
default:
|
default:
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
@ -77,13 +87,15 @@ String walletTypeToString(WalletType type) {
|
||||||
String walletTypeToDisplayName(WalletType type) {
|
String walletTypeToDisplayName(WalletType type) {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case WalletType.monero:
|
case WalletType.monero:
|
||||||
return 'Monero';
|
return 'Monero (XMR)';
|
||||||
case WalletType.bitcoin:
|
case WalletType.bitcoin:
|
||||||
return 'Bitcoin (Electrum)';
|
return 'Bitcoin (BTC)';
|
||||||
case WalletType.litecoin:
|
case WalletType.litecoin:
|
||||||
return 'Litecoin (Electrum)';
|
return 'Litecoin (LTC)';
|
||||||
case WalletType.haven:
|
case WalletType.haven:
|
||||||
return 'Haven';
|
return 'Haven (XHV)';
|
||||||
|
case WalletType.ethereum:
|
||||||
|
return 'Ethereum (ETH)';
|
||||||
default:
|
default:
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
@ -99,6 +111,8 @@ CryptoCurrency walletTypeToCryptoCurrency(WalletType type) {
|
||||||
return CryptoCurrency.ltc;
|
return CryptoCurrency.ltc;
|
||||||
case WalletType.haven:
|
case WalletType.haven:
|
||||||
return CryptoCurrency.xhv;
|
return CryptoCurrency.xhv;
|
||||||
|
case WalletType.ethereum:
|
||||||
|
return CryptoCurrency.eth;
|
||||||
default:
|
default:
|
||||||
throw Exception('Unexpected wallet type: ${type.toString()} for CryptoCurrency walletTypeToCryptoCurrency');
|
throw Exception('Unexpected wallet type: ${type.toString()} for CryptoCurrency walletTypeToCryptoCurrency');
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,191 +5,218 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: _fe_analyzer_shared
|
name: _fe_analyzer_shared
|
||||||
url: "https://pub.dartlang.org"
|
sha256: "4897882604d919befd350648c7f91926a9d5de99e67b455bf0917cc2362f4bb8"
|
||||||
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "47.0.0"
|
version: "47.0.0"
|
||||||
analyzer:
|
analyzer:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: analyzer
|
name: analyzer
|
||||||
url: "https://pub.dartlang.org"
|
sha256: "690e335554a8385bc9d787117d9eb52c0c03ee207a607e593de3c9d71b1cfe80"
|
||||||
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.7.0"
|
version: "4.7.0"
|
||||||
args:
|
args:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: args
|
name: args
|
||||||
url: "https://pub.dartlang.org"
|
sha256: "139d809800a412ebb26a3892da228b2d0ba36f0ef5d9a82166e5e52ec8d61611"
|
||||||
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.4.0"
|
version: "2.3.2"
|
||||||
asn1lib:
|
asn1lib:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: asn1lib
|
name: asn1lib
|
||||||
url: "https://pub.dartlang.org"
|
sha256: ab96a1cb3beeccf8145c52e449233fe68364c9641623acd3adad66f8184f1039
|
||||||
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.4.0"
|
version: "1.4.0"
|
||||||
async:
|
async:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: async
|
name: async
|
||||||
url: "https://pub.dartlang.org"
|
sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c"
|
||||||
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.9.0"
|
version: "2.11.0"
|
||||||
boolean_selector:
|
boolean_selector:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: boolean_selector
|
name: boolean_selector
|
||||||
url: "https://pub.dartlang.org"
|
sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66"
|
||||||
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.0"
|
version: "2.1.1"
|
||||||
build:
|
build:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: build
|
name: build
|
||||||
url: "https://pub.dartlang.org"
|
sha256: "3fbda25365741f8251b39f3917fb3c8e286a96fd068a5a242e11c2012d495777"
|
||||||
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.3.1"
|
version: "2.3.1"
|
||||||
build_config:
|
build_config:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: build_config
|
name: build_config
|
||||||
url: "https://pub.dartlang.org"
|
sha256: bf80fcfb46a29945b423bd9aad884590fb1dc69b330a4d4700cac476af1708d1
|
||||||
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.1.1"
|
version: "1.1.1"
|
||||||
build_daemon:
|
build_daemon:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: build_daemon
|
name: build_daemon
|
||||||
url: "https://pub.dartlang.org"
|
sha256: "6bc5544ea6ce4428266e7ea680e945c68806c4aae2da0eb5e9ccf38df8d6acbf"
|
||||||
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.1.1"
|
version: "3.1.0"
|
||||||
build_resolvers:
|
build_resolvers:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description:
|
description:
|
||||||
name: build_resolvers
|
name: build_resolvers
|
||||||
url: "https://pub.dartlang.org"
|
sha256: "687cf90a3951affac1bd5f9ecb5e3e90b60487f3d9cdc359bb310f8876bb02a6"
|
||||||
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.10"
|
version: "2.0.10"
|
||||||
build_runner:
|
build_runner:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description:
|
description:
|
||||||
name: build_runner
|
name: build_runner
|
||||||
url: "https://pub.dartlang.org"
|
sha256: b0a8a7b8a76c493e85f1b84bffa0588859a06197863dba8c9036b15581fd9727
|
||||||
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.3.3"
|
version: "2.3.3"
|
||||||
build_runner_core:
|
build_runner_core:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: build_runner_core
|
name: build_runner_core
|
||||||
url: "https://pub.dartlang.org"
|
sha256: "14febe0f5bac5ae474117a36099b4de6f1dbc52df6c5e55534b3da9591bf4292"
|
||||||
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "7.2.7"
|
version: "7.2.7"
|
||||||
built_collection:
|
built_collection:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: built_collection
|
name: built_collection
|
||||||
url: "https://pub.dartlang.org"
|
sha256: "376e3dd27b51ea877c28d525560790aee2e6fbb5f20e2f85d5081027d94e2100"
|
||||||
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "5.1.1"
|
version: "5.1.1"
|
||||||
built_value:
|
built_value:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: built_value
|
name: built_value
|
||||||
url: "https://pub.dartlang.org"
|
sha256: "169565c8ad06adb760c3645bf71f00bff161b00002cace266cad42c5d22a7725"
|
||||||
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "8.4.4"
|
version: "8.4.3"
|
||||||
characters:
|
characters:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: characters
|
name: characters
|
||||||
url: "https://pub.dartlang.org"
|
sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605"
|
||||||
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.2.1"
|
version: "1.3.0"
|
||||||
checked_yaml:
|
checked_yaml:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: checked_yaml
|
name: checked_yaml
|
||||||
url: "https://pub.dartlang.org"
|
sha256: "3d1505d91afa809d177efd4eed5bb0eb65805097a1463abdd2add076effae311"
|
||||||
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.2"
|
version: "2.0.2"
|
||||||
clock:
|
clock:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: clock
|
name: clock
|
||||||
url: "https://pub.dartlang.org"
|
sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf
|
||||||
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.1.1"
|
version: "1.1.1"
|
||||||
code_builder:
|
code_builder:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: code_builder
|
name: code_builder
|
||||||
url: "https://pub.dartlang.org"
|
sha256: "0d43dd1288fd145de1ecc9a3948ad4a6d5a82f0a14c4fdd0892260787d975cbe"
|
||||||
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.4.0"
|
version: "4.4.0"
|
||||||
collection:
|
collection:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: collection
|
name: collection
|
||||||
url: "https://pub.dartlang.org"
|
sha256: "4a07be6cb69c84d677a6c3096fcf960cc3285a8330b4603e0d463d15d9bd934c"
|
||||||
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.16.0"
|
version: "1.17.1"
|
||||||
convert:
|
convert:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: convert
|
name: convert
|
||||||
url: "https://pub.dartlang.org"
|
sha256: "0f08b14755d163f6e2134cb58222dd25ea2a2ee8a195e53983d57c075324d592"
|
||||||
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.1.1"
|
version: "3.1.1"
|
||||||
crypto:
|
crypto:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: crypto
|
name: crypto
|
||||||
url: "https://pub.dartlang.org"
|
sha256: aa274aa7774f8964e4f4f38cc994db7b6158dd36e9187aaceaddc994b35c6c67
|
||||||
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.0.2"
|
version: "3.0.2"
|
||||||
dart_style:
|
dart_style:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: dart_style
|
name: dart_style
|
||||||
url: "https://pub.dartlang.org"
|
sha256: "7a03456c3490394c8e7665890333e91ae8a49be43542b616e414449ac358acd4"
|
||||||
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.2.4"
|
version: "2.2.4"
|
||||||
encrypt:
|
encrypt:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: encrypt
|
name: encrypt
|
||||||
url: "https://pub.dartlang.org"
|
sha256: "4fd4e4fdc21b9d7d4141823e1e6515cd94e7b8d84749504c232999fba25d9bbb"
|
||||||
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "5.0.1"
|
version: "5.0.1"
|
||||||
fake_async:
|
fake_async:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: fake_async
|
name: fake_async
|
||||||
url: "https://pub.dartlang.org"
|
sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78"
|
||||||
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.3.1"
|
version: "1.3.1"
|
||||||
ffi:
|
ffi:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: ffi
|
name: ffi
|
||||||
url: "https://pub.dartlang.org"
|
sha256: a38574032c5f1dd06c4aee541789906c12ccaab8ba01446e800d9c5b79c4a978
|
||||||
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.1"
|
version: "2.0.1"
|
||||||
file:
|
file:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: file
|
name: file
|
||||||
url: "https://pub.dartlang.org"
|
sha256: "1b92bec4fc2a72f59a8e15af5f52cd441e4a7860b49499d69dfa817af20e925d"
|
||||||
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "6.1.4"
|
version: "6.1.4"
|
||||||
fixnum:
|
fixnum:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: fixnum
|
name: fixnum
|
||||||
url: "https://pub.dartlang.org"
|
sha256: "25517a4deb0c03aa0f32fd12db525856438902d9c16536311e76cdc57b31d7d1"
|
||||||
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.1"
|
version: "1.1.0"
|
||||||
flutter:
|
flutter:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description: flutter
|
description: flutter
|
||||||
|
@ -199,7 +226,8 @@ packages:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: flutter_mobx
|
name: flutter_mobx
|
||||||
url: "https://pub.dartlang.org"
|
sha256: "0da4add0016387a7bf309a0d0c41d36c6b3ae25ed7a176409267f166509e723e"
|
||||||
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.6+5"
|
version: "2.0.6+5"
|
||||||
flutter_test:
|
flutter_test:
|
||||||
|
@ -211,252 +239,288 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: frontend_server_client
|
name: frontend_server_client
|
||||||
url: "https://pub.dartlang.org"
|
sha256: "408e3ca148b31c20282ad6f37ebfa6f4bdc8fede5b74bc2f08d9d92b55db3612"
|
||||||
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.2.0"
|
version: "3.2.0"
|
||||||
glob:
|
glob:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: glob
|
name: glob
|
||||||
url: "https://pub.dartlang.org"
|
sha256: "4515b5b6ddb505ebdd242a5f2cc5d22d3d6a80013789debfbda7777f47ea308c"
|
||||||
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.1"
|
version: "2.1.1"
|
||||||
graphs:
|
graphs:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: graphs
|
name: graphs
|
||||||
url: "https://pub.dartlang.org"
|
sha256: f9e130f3259f52d26f0cfc0e964513796dafed572fa52e45d2f8d6ca14db39b2
|
||||||
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.2.0"
|
version: "2.2.0"
|
||||||
hive:
|
hive:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: hive
|
name: hive
|
||||||
url: "https://pub.dartlang.org"
|
sha256: "8dcf6db979d7933da8217edcec84e9df1bdb4e4edc7fc77dbd5aa74356d6d941"
|
||||||
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.2.3"
|
version: "2.2.3"
|
||||||
hive_generator:
|
hive_generator:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description:
|
description:
|
||||||
name: hive_generator
|
name: hive_generator
|
||||||
url: "https://pub.dartlang.org"
|
sha256: "81fd20125cb2ce8fd23623d7744ffbaf653aae93706c9bd3bf7019ea0ace3938"
|
||||||
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.1.3"
|
version: "1.1.3"
|
||||||
http:
|
http:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: http
|
name: http
|
||||||
url: "https://pub.dartlang.org"
|
sha256: "759d1a329847dd0f39226c688d3e06a6b8679668e350e2891a6474f8b4bb8525"
|
||||||
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.13.5"
|
version: "1.1.0"
|
||||||
http_multi_server:
|
http_multi_server:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: http_multi_server
|
name: http_multi_server
|
||||||
url: "https://pub.dartlang.org"
|
sha256: "97486f20f9c2f7be8f514851703d0119c3596d14ea63227af6f7a481ef2b2f8b"
|
||||||
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.2.1"
|
version: "3.2.1"
|
||||||
http_parser:
|
http_parser:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: http_parser
|
name: http_parser
|
||||||
url: "https://pub.dartlang.org"
|
sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b"
|
||||||
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.0.2"
|
version: "4.0.2"
|
||||||
intl:
|
intl:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: intl
|
name: intl
|
||||||
url: "https://pub.dartlang.org"
|
sha256: "3bc132a9dbce73a7e4a21a17d06e1878839ffbf975568bc875c60537824b0c4d"
|
||||||
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.17.0"
|
version: "0.18.1"
|
||||||
io:
|
io:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: io
|
name: io
|
||||||
url: "https://pub.dartlang.org"
|
sha256: "2ec25704aba361659e10e3e5f5d672068d332fc8ac516421d483a11e5cbd061e"
|
||||||
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.4"
|
version: "1.0.4"
|
||||||
js:
|
js:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: js
|
name: js
|
||||||
url: "https://pub.dartlang.org"
|
sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3
|
||||||
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.6.5"
|
version: "0.6.7"
|
||||||
json_annotation:
|
json_annotation:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: json_annotation
|
name: json_annotation
|
||||||
url: "https://pub.dartlang.org"
|
sha256: c33da08e136c3df0190bd5bbe51ae1df4a7d96e7954d1d7249fea2968a72d317
|
||||||
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.8.0"
|
version: "4.8.0"
|
||||||
logging:
|
logging:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: logging
|
name: logging
|
||||||
url: "https://pub.dartlang.org"
|
sha256: "04094f2eb032cbb06c6f6e8d3607edcfcb0455e2bb6cbc010cb01171dcb64e6d"
|
||||||
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.1.1"
|
version: "1.1.1"
|
||||||
matcher:
|
matcher:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: matcher
|
name: matcher
|
||||||
url: "https://pub.dartlang.org"
|
sha256: "6501fbd55da300384b768785b83e5ce66991266cec21af89ab9ae7f5ce1c4cbb"
|
||||||
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.12.12"
|
version: "0.12.15"
|
||||||
material_color_utilities:
|
material_color_utilities:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: material_color_utilities
|
name: material_color_utilities
|
||||||
url: "https://pub.dartlang.org"
|
sha256: d92141dc6fe1dad30722f9aa826c7fbc896d021d792f80678280601aff8cf724
|
||||||
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.1.5"
|
version: "0.2.0"
|
||||||
meta:
|
meta:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: meta
|
name: meta
|
||||||
url: "https://pub.dartlang.org"
|
sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3"
|
||||||
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.8.0"
|
version: "1.9.1"
|
||||||
mime:
|
mime:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: mime
|
name: mime
|
||||||
url: "https://pub.dartlang.org"
|
sha256: e4ff8e8564c03f255408decd16e7899da1733852a9110a58fe6d1b817684a63e
|
||||||
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.4"
|
version: "1.0.4"
|
||||||
mobx:
|
mobx:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: mobx
|
name: mobx
|
||||||
url: "https://pub.dartlang.org"
|
sha256: f1862bd92c6a903fab67338f27e2f731117c3cb9ea37cee1a487f9e4e0de314a
|
||||||
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.4"
|
version: "2.1.3+1"
|
||||||
mobx_codegen:
|
mobx_codegen:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description:
|
description:
|
||||||
name: mobx_codegen
|
name: mobx_codegen
|
||||||
url: "https://pub.dartlang.org"
|
sha256: "86122e410d8ea24dda0c69adb5c2a6ccadd5ce02ad46e144764e0d0184a06181"
|
||||||
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.1"
|
version: "2.1.1"
|
||||||
package_config:
|
package_config:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: package_config
|
name: package_config
|
||||||
url: "https://pub.dartlang.org"
|
sha256: "1c5b77ccc91e4823a5af61ee74e6b972db1ef98c2ff5a18d3161c982a55448bd"
|
||||||
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.0"
|
version: "2.1.0"
|
||||||
path:
|
path:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: path
|
name: path
|
||||||
url: "https://pub.dartlang.org"
|
sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917"
|
||||||
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.8.2"
|
version: "1.8.3"
|
||||||
path_provider:
|
path_provider:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: path_provider
|
name: path_provider
|
||||||
url: "https://pub.dartlang.org"
|
sha256: dcea5feb97d8abf90cab9e9030b497fb7c3cbf26b7a1fe9e3ef7dcb0a1ddec95
|
||||||
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.13"
|
version: "2.0.12"
|
||||||
path_provider_android:
|
path_provider_android:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: path_provider_android
|
name: path_provider_android
|
||||||
url: "https://pub.dartlang.org"
|
sha256: a776c088d671b27f6e3aa8881d64b87b3e80201c64e8869b811325de7a76c15e
|
||||||
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.24"
|
version: "2.0.22"
|
||||||
path_provider_foundation:
|
path_provider_foundation:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: path_provider_foundation
|
name: path_provider_foundation
|
||||||
url: "https://pub.dartlang.org"
|
sha256: "62a68e7e1c6c459f9289859e2fae58290c981ce21d1697faf54910fe1faa4c74"
|
||||||
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.3"
|
version: "2.1.1"
|
||||||
path_provider_linux:
|
path_provider_linux:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: path_provider_linux
|
name: path_provider_linux
|
||||||
url: "https://pub.dartlang.org"
|
sha256: ab0987bf95bc591da42dffb38c77398fc43309f0b9b894dcc5d6f40c4b26c379
|
||||||
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.10"
|
version: "2.1.7"
|
||||||
path_provider_platform_interface:
|
path_provider_platform_interface:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: path_provider_platform_interface
|
name: path_provider_platform_interface
|
||||||
url: "https://pub.dartlang.org"
|
sha256: f0abc8ebd7253741f05488b4813d936b4d07c6bae3e86148a09e342ee4b08e76
|
||||||
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.6"
|
version: "2.0.5"
|
||||||
path_provider_windows:
|
path_provider_windows:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: path_provider_windows
|
name: path_provider_windows
|
||||||
url: "https://pub.dartlang.org"
|
sha256: bcabbe399d4042b8ee687e17548d5d3f527255253b4a639f5f8d2094a9c2b45c
|
||||||
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.5"
|
version: "2.1.3"
|
||||||
platform:
|
platform:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: platform
|
name: platform
|
||||||
url: "https://pub.dartlang.org"
|
sha256: "4a451831508d7d6ca779f7ac6e212b4023dd5a7d08a27a63da33756410e32b76"
|
||||||
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.1.0"
|
version: "3.1.0"
|
||||||
plugin_platform_interface:
|
plugin_platform_interface:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: plugin_platform_interface
|
name: plugin_platform_interface
|
||||||
url: "https://pub.dartlang.org"
|
sha256: dbf0f707c78beedc9200146ad3cb0ab4d5da13c246336987be6940f026500d3a
|
||||||
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.4"
|
version: "2.1.3"
|
||||||
pointycastle:
|
pointycastle:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: pointycastle
|
name: pointycastle
|
||||||
url: "https://pub.dartlang.org"
|
sha256: db7306cf0249f838d1a24af52b5a5887c5bf7f31d8bb4e827d071dc0939ad346
|
||||||
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.6.2"
|
version: "3.6.2"
|
||||||
pool:
|
pool:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: pool
|
name: pool
|
||||||
url: "https://pub.dartlang.org"
|
sha256: "20fe868b6314b322ea036ba325e6fc0711a22948856475e2c2b6306e8ab39c2a"
|
||||||
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.5.1"
|
version: "1.5.1"
|
||||||
process:
|
process:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: process
|
name: process
|
||||||
url: "https://pub.dartlang.org"
|
sha256: "53fd8db9cec1d37b0574e12f07520d582019cb6c44abf5479a01505099a34a09"
|
||||||
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.2.4"
|
version: "4.2.4"
|
||||||
pub_semver:
|
pub_semver:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: pub_semver
|
name: pub_semver
|
||||||
url: "https://pub.dartlang.org"
|
sha256: "307de764d305289ff24ad257ad5c5793ce56d04947599ad68b3baa124105fc17"
|
||||||
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.3"
|
version: "2.1.3"
|
||||||
pubspec_parse:
|
pubspec_parse:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: pubspec_parse
|
name: pubspec_parse
|
||||||
url: "https://pub.dartlang.org"
|
sha256: "75f6614d6dde2dc68948dffbaa4fe5dae32cd700eb9fb763fe11dfb45a3c4d0a"
|
||||||
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.2.2"
|
version: "1.2.1"
|
||||||
shelf:
|
shelf:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: shelf
|
name: shelf
|
||||||
url: "https://pub.dartlang.org"
|
sha256: c24a96135a2ccd62c64b69315a14adc5c3419df63b4d7c05832a346fdb73682c
|
||||||
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.4.0"
|
version: "1.4.0"
|
||||||
shelf_web_socket:
|
shelf_web_socket:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: shelf_web_socket
|
name: shelf_web_socket
|
||||||
url: "https://pub.dartlang.org"
|
sha256: a988c0e8d8ffbdb8a28aa7ec8e449c260f3deb808781fe1284d22c5bba7156e8
|
||||||
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.3"
|
version: "1.0.3"
|
||||||
sky_engine:
|
sky_engine:
|
||||||
|
@ -468,121 +532,138 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: source_gen
|
name: source_gen
|
||||||
url: "https://pub.dartlang.org"
|
sha256: "2d79738b6bbf38a43920e2b8d189e9a3ce6cc201f4b8fc76be5e4fe377b1c38d"
|
||||||
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.2.6"
|
version: "1.2.6"
|
||||||
source_helper:
|
source_helper:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: source_helper
|
name: source_helper
|
||||||
url: "https://pub.dartlang.org"
|
sha256: "3b67aade1d52416149c633ba1bb36df44d97c6b51830c2198e934e3fca87ca1f"
|
||||||
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.3.3"
|
version: "1.3.3"
|
||||||
source_span:
|
source_span:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: source_span
|
name: source_span
|
||||||
url: "https://pub.dartlang.org"
|
sha256: dd904f795d4b4f3b870833847c461801f6750a9fa8e61ea5ac53f9422b31f250
|
||||||
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.9.0"
|
version: "1.9.1"
|
||||||
stack_trace:
|
stack_trace:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: stack_trace
|
name: stack_trace
|
||||||
url: "https://pub.dartlang.org"
|
sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5
|
||||||
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.10.0"
|
version: "1.11.0"
|
||||||
stream_channel:
|
stream_channel:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: stream_channel
|
name: stream_channel
|
||||||
url: "https://pub.dartlang.org"
|
sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8"
|
||||||
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.0"
|
version: "2.1.1"
|
||||||
stream_transform:
|
stream_transform:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: stream_transform
|
name: stream_transform
|
||||||
url: "https://pub.dartlang.org"
|
sha256: "14a00e794c7c11aa145a170587321aedce29769c08d7f58b1d141da75e3b1c6f"
|
||||||
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.0"
|
version: "2.1.0"
|
||||||
string_scanner:
|
string_scanner:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: string_scanner
|
name: string_scanner
|
||||||
url: "https://pub.dartlang.org"
|
sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde"
|
||||||
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.1.1"
|
version: "1.2.0"
|
||||||
term_glyph:
|
term_glyph:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: term_glyph
|
name: term_glyph
|
||||||
url: "https://pub.dartlang.org"
|
sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84
|
||||||
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.2.1"
|
version: "1.2.1"
|
||||||
test_api:
|
test_api:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: test_api
|
name: test_api
|
||||||
url: "https://pub.dartlang.org"
|
sha256: eb6ac1540b26de412b3403a163d919ba86f6a973fe6cc50ae3541b80092fdcfb
|
||||||
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.4.12"
|
version: "0.5.1"
|
||||||
timing:
|
timing:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: timing
|
name: timing
|
||||||
url: "https://pub.dartlang.org"
|
sha256: "70a3b636575d4163c477e6de42f247a23b315ae20e86442bebe32d3cabf61c32"
|
||||||
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.1"
|
version: "1.0.1"
|
||||||
typed_data:
|
typed_data:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: typed_data
|
name: typed_data
|
||||||
url: "https://pub.dartlang.org"
|
sha256: "26f87ade979c47a150c9eaab93ccd2bebe70a27dc0b4b29517f2904f04eb11a5"
|
||||||
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.3.1"
|
version: "1.3.1"
|
||||||
vector_math:
|
vector_math:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: vector_math
|
name: vector_math
|
||||||
url: "https://pub.dartlang.org"
|
sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803"
|
||||||
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.2"
|
version: "2.1.4"
|
||||||
watcher:
|
watcher:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: watcher
|
name: watcher
|
||||||
url: "https://pub.dartlang.org"
|
sha256: "6a7f46926b01ce81bfc339da6a7f20afbe7733eff9846f6d6a5466aa4c6667c0"
|
||||||
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.2"
|
version: "1.0.2"
|
||||||
web_socket_channel:
|
web_socket_channel:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: web_socket_channel
|
name: web_socket_channel
|
||||||
url: "https://pub.dartlang.org"
|
sha256: ca49c0bc209c687b887f30527fb6a9d80040b072cc2990f34b9bec3e7663101b
|
||||||
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.3.0"
|
version: "2.3.0"
|
||||||
win32:
|
win32:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: win32
|
name: win32
|
||||||
url: "https://pub.dartlang.org"
|
sha256: c9ebe7ee4ab0c2194e65d3a07d8c54c5d00bb001b76081c4a04cdb8448b59e46
|
||||||
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.1.3"
|
version: "3.1.3"
|
||||||
xdg_directories:
|
xdg_directories:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: xdg_directories
|
name: xdg_directories
|
||||||
url: "https://pub.dartlang.org"
|
sha256: bd512f03919aac5f1313eb8249f223bacf4927031bf60b02601f81f687689e86
|
||||||
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.0"
|
version: "0.2.0+3"
|
||||||
yaml:
|
yaml:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: yaml
|
name: yaml
|
||||||
url: "https://pub.dartlang.org"
|
sha256: "23812a9b125b48d4007117254bca50abb6c712352927eece9e155207b1db2370"
|
||||||
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.1.1"
|
version: "3.1.1"
|
||||||
sdks:
|
sdks:
|
||||||
dart: ">=2.19.0 <4.0.0"
|
dart: ">=3.0.0 <4.0.0"
|
||||||
flutter: ">=3.0.0"
|
flutter: ">=3.0.0"
|
||||||
|
|
|
@ -12,12 +12,12 @@ environment:
|
||||||
dependencies:
|
dependencies:
|
||||||
flutter:
|
flutter:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
http: ^0.13.4
|
http: ^1.1.0
|
||||||
file: ^6.1.4
|
file: ^6.1.4
|
||||||
path_provider: ^2.0.11
|
path_provider: ^2.0.11
|
||||||
mobx: ^2.0.7+4
|
mobx: ^2.0.7+4
|
||||||
flutter_mobx: ^2.0.6+1
|
flutter_mobx: ^2.0.6+1
|
||||||
intl: ^0.17.0
|
intl: ^0.18.0
|
||||||
encrypt: ^5.0.1
|
encrypt: ^5.0.1
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
|
|
30
cw_ethereum/.gitignore
vendored
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
# Miscellaneous
|
||||||
|
*.class
|
||||||
|
*.log
|
||||||
|
*.pyc
|
||||||
|
*.swp
|
||||||
|
.DS_Store
|
||||||
|
.atom/
|
||||||
|
.buildlog/
|
||||||
|
.history
|
||||||
|
.svn/
|
||||||
|
migrate_working_dir/
|
||||||
|
|
||||||
|
# IntelliJ related
|
||||||
|
*.iml
|
||||||
|
*.ipr
|
||||||
|
*.iws
|
||||||
|
.idea/
|
||||||
|
|
||||||
|
# The .vscode folder contains launch configuration and tasks you configure in
|
||||||
|
# VS Code which you may wish to be included in version control, so this line
|
||||||
|
# is commented out by default.
|
||||||
|
#.vscode/
|
||||||
|
|
||||||
|
# Flutter/Dart/Pub related
|
||||||
|
# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock.
|
||||||
|
/pubspec.lock
|
||||||
|
**/doc/api/
|
||||||
|
.dart_tool/
|
||||||
|
.packages
|
||||||
|
build/
|
10
cw_ethereum/.metadata
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
# This file tracks properties of this Flutter project.
|
||||||
|
# Used by Flutter tool to assess capabilities and perform upgrades etc.
|
||||||
|
#
|
||||||
|
# This file should be version controlled and should not be manually edited.
|
||||||
|
|
||||||
|
version:
|
||||||
|
revision: eb6d86ee27deecba4a83536aa20f366a6044895c
|
||||||
|
channel: stable
|
||||||
|
|
||||||
|
project_type: package
|
3
cw_ethereum/CHANGELOG.md
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
## 0.0.1
|
||||||
|
|
||||||
|
* TODO: Describe initial release.
|
1
cw_ethereum/LICENSE
Normal file
|
@ -0,0 +1 @@
|
||||||
|
TODO: Add your license here.
|
39
cw_ethereum/README.md
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
<!--
|
||||||
|
This README describes the package. If you publish this package to pub.dev,
|
||||||
|
this README's contents appear on the landing page for your package.
|
||||||
|
|
||||||
|
For information about how to write a good package README, see the guide for
|
||||||
|
[writing package pages](https://dart.dev/guides/libraries/writing-package-pages).
|
||||||
|
|
||||||
|
For general information about developing packages, see the Dart guide for
|
||||||
|
[creating packages](https://dart.dev/guides/libraries/create-library-packages)
|
||||||
|
and the Flutter guide for
|
||||||
|
[developing packages and plugins](https://flutter.dev/developing-packages).
|
||||||
|
-->
|
||||||
|
|
||||||
|
TODO: Put a short description of the package here that helps potential users
|
||||||
|
know whether this package might be useful for them.
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
TODO: List what your package can do. Maybe include images, gifs, or videos.
|
||||||
|
|
||||||
|
## Getting started
|
||||||
|
|
||||||
|
TODO: List prerequisites and provide or point to information on how to
|
||||||
|
start using the package.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
TODO: Include short and useful examples for package users. Add longer examples
|
||||||
|
to `/example` folder.
|
||||||
|
|
||||||
|
```dart
|
||||||
|
const like = 'sample';
|
||||||
|
```
|
||||||
|
|
||||||
|
## Additional information
|
||||||
|
|
||||||
|
TODO: Tell users more about the package: where to find more information, how to
|
||||||
|
contribute to the package, how to file issues, what response they can expect
|
||||||
|
from the package authors, and more.
|
4
cw_ethereum/analysis_options.yaml
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
include: package:flutter_lints/flutter.yaml
|
||||||
|
|
||||||
|
# Additional information about this file can be found at
|
||||||
|
# https://dart.dev/guides/language/analysis-options
|
7
cw_ethereum/lib/cw_ethereum.dart
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
library cw_ethereum;
|
||||||
|
|
||||||
|
/// A Calculator.
|
||||||
|
class Calculator {
|
||||||
|
/// Returns [value] plus 1.
|
||||||
|
int addOne(int value) => value + 1;
|
||||||
|
}
|
309
cw_ethereum/lib/default_erc20_tokens.dart
Normal file
|
@ -0,0 +1,309 @@
|
||||||
|
import 'package:cw_core/crypto_currency.dart';
|
||||||
|
import 'package:cw_core/erc20_token.dart';
|
||||||
|
|
||||||
|
class DefaultErc20Tokens {
|
||||||
|
final List<Erc20Token> _defaultTokens = [
|
||||||
|
Erc20Token(
|
||||||
|
name: "USD Coin",
|
||||||
|
symbol: "USDC",
|
||||||
|
contractAddress: "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48",
|
||||||
|
decimal: 6,
|
||||||
|
enabled: true,
|
||||||
|
),
|
||||||
|
Erc20Token(
|
||||||
|
name: "USDT Tether",
|
||||||
|
symbol: "USDT",
|
||||||
|
contractAddress: "0xdac17f958d2ee523a2206206994597c13d831ec7",
|
||||||
|
decimal: 6,
|
||||||
|
enabled: true,
|
||||||
|
),
|
||||||
|
Erc20Token(
|
||||||
|
name: "Dai",
|
||||||
|
symbol: "DAI",
|
||||||
|
contractAddress: "0x6B175474E89094C44Da98b954EedeAC495271d0F",
|
||||||
|
decimal: 18,
|
||||||
|
enabled: true,
|
||||||
|
),
|
||||||
|
Erc20Token(
|
||||||
|
name: "Wrapped Ether",
|
||||||
|
symbol: "WETH",
|
||||||
|
contractAddress: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
|
||||||
|
decimal: 18,
|
||||||
|
enabled: false,
|
||||||
|
),
|
||||||
|
Erc20Token(
|
||||||
|
name: "Pepe",
|
||||||
|
symbol: "PEPE",
|
||||||
|
contractAddress: "0x6982508145454ce325ddbe47a25d4ec3d2311933",
|
||||||
|
decimal: 18,
|
||||||
|
enabled: false,
|
||||||
|
),
|
||||||
|
Erc20Token(
|
||||||
|
name: "SHIBA INU",
|
||||||
|
symbol: "SHIB",
|
||||||
|
contractAddress: "0x95ad61b0a150d79219dcf64e1e6cc01f0b64c4ce",
|
||||||
|
decimal: 18,
|
||||||
|
enabled: false,
|
||||||
|
),
|
||||||
|
Erc20Token(
|
||||||
|
name: "ApeCoin",
|
||||||
|
symbol: "APE",
|
||||||
|
contractAddress: "0x4d224452801aced8b2f0aebe155379bb5d594381",
|
||||||
|
decimal: 18,
|
||||||
|
enabled: false,
|
||||||
|
),
|
||||||
|
Erc20Token(
|
||||||
|
name: "Matic Token",
|
||||||
|
symbol: "MATIC",
|
||||||
|
contractAddress: "0x7D1AfA7B718fb893dB30A3aBc0Cfc608AaCfeBB0",
|
||||||
|
decimal: 18,
|
||||||
|
enabled: false,
|
||||||
|
),
|
||||||
|
Erc20Token(
|
||||||
|
name: "Wrapped BTC",
|
||||||
|
symbol: "WBTC",
|
||||||
|
contractAddress: "0x2260fac5e5542a773aa44fbcfedf7c193bc2c599",
|
||||||
|
decimal: 8,
|
||||||
|
enabled: false,
|
||||||
|
),
|
||||||
|
Erc20Token(
|
||||||
|
name: "Gitcoin",
|
||||||
|
symbol: "GTC",
|
||||||
|
contractAddress: "0xde30da39c46104798bb5aa3fe8b9e0e1f348163f",
|
||||||
|
decimal: 18,
|
||||||
|
enabled: false,
|
||||||
|
),
|
||||||
|
Erc20Token(
|
||||||
|
name: "Compound",
|
||||||
|
symbol: "COMP",
|
||||||
|
contractAddress: "0xc00e94cb662c3520282e6f5717214004a7f26888",
|
||||||
|
decimal: 18,
|
||||||
|
enabled: false,
|
||||||
|
),
|
||||||
|
Erc20Token(
|
||||||
|
name: "Aave Token",
|
||||||
|
symbol: "AAVE",
|
||||||
|
contractAddress: "0x7fc66500c84a76ad7e9c93437bfc5ac33e2ddae9",
|
||||||
|
decimal: 18,
|
||||||
|
enabled: false,
|
||||||
|
),
|
||||||
|
Erc20Token(
|
||||||
|
name: "Uniswap",
|
||||||
|
symbol: "UNI",
|
||||||
|
contractAddress: "0x1f9840a85d5af5bf1d1762f925bdaddc4201f984",
|
||||||
|
decimal: 18,
|
||||||
|
enabled: false,
|
||||||
|
),
|
||||||
|
Erc20Token(
|
||||||
|
name: "Decentraland",
|
||||||
|
symbol: "MANA",
|
||||||
|
contractAddress: "0x0F5D2fB29fb7d3CFeE444a200298f468908cC942",
|
||||||
|
decimal: 18,
|
||||||
|
enabled: false,
|
||||||
|
),
|
||||||
|
Erc20Token(
|
||||||
|
name: "Storj",
|
||||||
|
symbol: "STORJ",
|
||||||
|
contractAddress: "0xb64ef51c888972c908cfacf59b47c1afbc0ab8ac",
|
||||||
|
decimal: 8,
|
||||||
|
enabled: false,
|
||||||
|
),
|
||||||
|
Erc20Token(
|
||||||
|
name: "Maker",
|
||||||
|
symbol: "MKR",
|
||||||
|
contractAddress: "0x9f8F72aA9304c8B593d555F12eF6589cC3A579A2",
|
||||||
|
decimal: 18,
|
||||||
|
enabled: false,
|
||||||
|
),
|
||||||
|
Erc20Token(
|
||||||
|
name: "Orchid",
|
||||||
|
symbol: "OXT",
|
||||||
|
contractAddress: "0x4575f41308EC1483f3d399aa9a2826d74Da13Deb",
|
||||||
|
decimal: 18,
|
||||||
|
enabled: false,
|
||||||
|
),
|
||||||
|
Erc20Token(
|
||||||
|
name: "Paxos Gold",
|
||||||
|
symbol: "PAXG",
|
||||||
|
contractAddress: "0x45804880De22913dAFE09f4980848ECE6EcbAf78",
|
||||||
|
decimal: 18,
|
||||||
|
enabled: false,
|
||||||
|
),
|
||||||
|
Erc20Token(
|
||||||
|
name: "Binance Coin",
|
||||||
|
symbol: "BNB",
|
||||||
|
contractAddress: "0xB8c77482e45F1F44dE1745F52C74426C631bDD52",
|
||||||
|
decimal: 18,
|
||||||
|
enabled: false,
|
||||||
|
),
|
||||||
|
Erc20Token(
|
||||||
|
name: "stETH",
|
||||||
|
symbol: "stETH",
|
||||||
|
contractAddress: "0xae7ab96520DE3A18E5e111B5EaAb095312D7fE84",
|
||||||
|
decimal: 18,
|
||||||
|
enabled: false,
|
||||||
|
),
|
||||||
|
Erc20Token(
|
||||||
|
name: "Lido DAO",
|
||||||
|
symbol: "LDO",
|
||||||
|
contractAddress: "0x5A98FcBEA516Cf06857215779Fd812CA3beF1B32",
|
||||||
|
decimal: 18,
|
||||||
|
enabled: false,
|
||||||
|
),
|
||||||
|
Erc20Token(
|
||||||
|
name: "Arbitrum",
|
||||||
|
symbol: "ARB",
|
||||||
|
contractAddress: "0xB50721BCf8d664c30412Cfbc6cf7a15145234ad1",
|
||||||
|
decimal: 18,
|
||||||
|
enabled: false,
|
||||||
|
),
|
||||||
|
Erc20Token(
|
||||||
|
name: "Graph Token",
|
||||||
|
symbol: "GRT",
|
||||||
|
contractAddress: "0xc944E90C64B2c07662A292be6244BDf05Cda44a7",
|
||||||
|
decimal: 18,
|
||||||
|
enabled: false,
|
||||||
|
),
|
||||||
|
Erc20Token(
|
||||||
|
name: "Frax",
|
||||||
|
symbol: "FRAX",
|
||||||
|
contractAddress: "0x853d955aCEf822Db058eb8505911ED77F175b99e",
|
||||||
|
decimal: 18,
|
||||||
|
enabled: false,
|
||||||
|
),
|
||||||
|
Erc20Token(
|
||||||
|
name: "Gemini dollar",
|
||||||
|
symbol: "GUSD",
|
||||||
|
contractAddress: "0x056Fd409E1d7A124BD7017459dFEa2F387b6d5Cd",
|
||||||
|
decimal: 2,
|
||||||
|
enabled: false,
|
||||||
|
),
|
||||||
|
Erc20Token(
|
||||||
|
name: "Compound Ether",
|
||||||
|
symbol: "cETH",
|
||||||
|
contractAddress: "0x4Ddc2D193948926D02f9B1fE9e1daa0718270ED5",
|
||||||
|
decimal: 8,
|
||||||
|
enabled: false,
|
||||||
|
),
|
||||||
|
Erc20Token(
|
||||||
|
name: "Binance USD",
|
||||||
|
symbol: "BUSD",
|
||||||
|
contractAddress: "0x4Fabb145d64652a948d72533023f6E7A623C7C53",
|
||||||
|
decimal: 18,
|
||||||
|
enabled: false,
|
||||||
|
),
|
||||||
|
Erc20Token(
|
||||||
|
name: "TrueUSD",
|
||||||
|
symbol: "TUSD",
|
||||||
|
contractAddress: "0x0000000000085d4780B73119b644AE5ecd22b376",
|
||||||
|
decimal: 18,
|
||||||
|
enabled: false,
|
||||||
|
),
|
||||||
|
Erc20Token(
|
||||||
|
name: "Cronos Coin",
|
||||||
|
symbol: "CRO",
|
||||||
|
contractAddress: "0xA0b73E1Ff0B80914AB6fe0444E65848C4C34450b",
|
||||||
|
decimal: 8,
|
||||||
|
enabled: false,
|
||||||
|
),
|
||||||
|
Erc20Token(
|
||||||
|
name: "Pax Dollar",
|
||||||
|
symbol: "USDP",
|
||||||
|
contractAddress: "0x8E870D67F660D95d5be530380D0eC0bd388289E1",
|
||||||
|
decimal: 18,
|
||||||
|
enabled: false,
|
||||||
|
),
|
||||||
|
Erc20Token(
|
||||||
|
name: "Fantom Token",
|
||||||
|
symbol: "FTM",
|
||||||
|
contractAddress: "0x4E15361FD6b4BB609Fa63C81A2be19d873717870",
|
||||||
|
decimal: 18,
|
||||||
|
enabled: false,
|
||||||
|
),
|
||||||
|
Erc20Token(
|
||||||
|
name: "BitTorrent",
|
||||||
|
symbol: "BTT",
|
||||||
|
contractAddress: "0xC669928185DbCE49d2230CC9B0979BE6DC797957",
|
||||||
|
decimal: 18,
|
||||||
|
enabled: false,
|
||||||
|
),
|
||||||
|
Erc20Token(
|
||||||
|
name: "Nexo",
|
||||||
|
symbol: "NEXO",
|
||||||
|
contractAddress: "0xB62132e35a6c13ee1EE0f84dC5d40bad8d815206",
|
||||||
|
decimal: 18,
|
||||||
|
enabled: false,
|
||||||
|
),
|
||||||
|
Erc20Token(
|
||||||
|
name: "dYdX",
|
||||||
|
symbol: "DYDX",
|
||||||
|
contractAddress: "0x92D6C1e31e14520e676a687F0a93788B716BEff5",
|
||||||
|
decimal: 18,
|
||||||
|
enabled: false,
|
||||||
|
),
|
||||||
|
Erc20Token(
|
||||||
|
name: "PancakeSwap Token",
|
||||||
|
symbol: "Cake",
|
||||||
|
contractAddress: "0x152649eA73beAb28c5b49B26eb48f7EAD6d4c898",
|
||||||
|
decimal: 18,
|
||||||
|
enabled: false,
|
||||||
|
),
|
||||||
|
Erc20Token(
|
||||||
|
name: "BAT",
|
||||||
|
symbol: "BAT",
|
||||||
|
contractAddress: "0x0D8775F648430679A709E98d2b0Cb6250d2887EF",
|
||||||
|
decimal: 18,
|
||||||
|
enabled: false,
|
||||||
|
),
|
||||||
|
Erc20Token(
|
||||||
|
name: "1INCH Token",
|
||||||
|
symbol: "1INCH",
|
||||||
|
contractAddress: "0x111111111117dC0aa78b770fA6A738034120C302",
|
||||||
|
decimal: 18,
|
||||||
|
enabled: false,
|
||||||
|
),
|
||||||
|
Erc20Token(
|
||||||
|
name: "Ethereum Name Service",
|
||||||
|
symbol: "ENS",
|
||||||
|
contractAddress: "0xC18360217D8F7Ab5e7c516566761Ea12Ce7F9D72",
|
||||||
|
decimal: 18,
|
||||||
|
enabled: false,
|
||||||
|
),
|
||||||
|
Erc20Token(
|
||||||
|
name: "ZRX",
|
||||||
|
symbol: "ZRX",
|
||||||
|
contractAddress: "0xE41d2489571d322189246DaFA5ebDe1F4699F498",
|
||||||
|
decimal: 18,
|
||||||
|
enabled: false,
|
||||||
|
),
|
||||||
|
Erc20Token(
|
||||||
|
name: "Verse",
|
||||||
|
symbol: "VERSE",
|
||||||
|
contractAddress: "0x249cA82617eC3DfB2589c4c17ab7EC9765350a18",
|
||||||
|
decimal: 18,
|
||||||
|
enabled: false,
|
||||||
|
),
|
||||||
|
Erc20Token(
|
||||||
|
name: "PayPal USD",
|
||||||
|
symbol: "PYUSD",
|
||||||
|
contractAddress: "0x6c3ea9036406852006290770bedfcaba0e23a0e8",
|
||||||
|
decimal: 6,
|
||||||
|
enabled: false,
|
||||||
|
),
|
||||||
|
];
|
||||||
|
|
||||||
|
List<Erc20Token> get initialErc20Tokens => _defaultTokens.map((token) {
|
||||||
|
String? iconPath;
|
||||||
|
try {
|
||||||
|
iconPath = CryptoCurrency.all
|
||||||
|
.firstWhere((element) => element.title.toUpperCase() == token.symbol.toUpperCase())
|
||||||
|
.iconPath;
|
||||||
|
} catch (_) {}
|
||||||
|
|
||||||
|
if (iconPath != null) {
|
||||||
|
return Erc20Token.copyWith(token, iconPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
return token;
|
||||||
|
}).toList();
|
||||||
|
}
|
47
cw_ethereum/lib/erc20_balance.dart
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
import 'dart:convert';
|
||||||
|
import 'dart:math';
|
||||||
|
|
||||||
|
import 'package:cw_core/balance.dart';
|
||||||
|
|
||||||
|
class ERC20Balance extends Balance {
|
||||||
|
ERC20Balance(this.balance, {this.exponent = 18})
|
||||||
|
: super(balance.toInt(),
|
||||||
|
balance.toInt());
|
||||||
|
|
||||||
|
final BigInt balance;
|
||||||
|
final int exponent;
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get formattedAdditionalBalance {
|
||||||
|
final String formattedBalance = (balance / BigInt.from(10).pow(exponent)).toString();
|
||||||
|
return formattedBalance.substring(0, min(12, formattedBalance.length));
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get formattedAvailableBalance {
|
||||||
|
final String formattedBalance = (balance / BigInt.from(10).pow(exponent)).toString();
|
||||||
|
return formattedBalance.substring(0, min(12, formattedBalance.length));
|
||||||
|
}
|
||||||
|
|
||||||
|
String toJSON() => json.encode({
|
||||||
|
'balanceInWei': balance.toString(),
|
||||||
|
'exponent': exponent,
|
||||||
|
});
|
||||||
|
|
||||||
|
static ERC20Balance? fromJSON(String? jsonSource) {
|
||||||
|
if (jsonSource == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
final decoded = json.decode(jsonSource) as Map;
|
||||||
|
|
||||||
|
try {
|
||||||
|
return ERC20Balance(
|
||||||
|
BigInt.parse(decoded['balanceInWei']),
|
||||||
|
exponent: decoded['exponent'],
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
return ERC20Balance(BigInt.zero);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
230
cw_ethereum/lib/ethereum_client.dart
Normal file
|
@ -0,0 +1,230 @@
|
||||||
|
import 'dart:async';
|
||||||
|
import 'dart:convert';
|
||||||
|
|
||||||
|
import 'package:cw_core/crypto_currency.dart';
|
||||||
|
import 'package:cw_ethereum/erc20_balance.dart';
|
||||||
|
import 'package:cw_core/erc20_token.dart';
|
||||||
|
import 'package:cw_ethereum/ethereum_transaction_model.dart';
|
||||||
|
import 'package:cw_ethereum/pending_ethereum_transaction.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
|
import 'package:http/http.dart';
|
||||||
|
import 'package:web3dart/web3dart.dart';
|
||||||
|
import 'package:erc20/erc20.dart';
|
||||||
|
import 'package:cw_core/node.dart';
|
||||||
|
import 'package:cw_ethereum/ethereum_transaction_priority.dart';
|
||||||
|
import 'package:cw_ethereum/.secrets.g.dart' as secrets;
|
||||||
|
|
||||||
|
class EthereumClient {
|
||||||
|
final _httpClient = Client();
|
||||||
|
Web3Client? _client;
|
||||||
|
|
||||||
|
bool connect(Node node) {
|
||||||
|
try {
|
||||||
|
_client = Web3Client(node.uri.toString(), _httpClient);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} catch (e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void setListeners(EthereumAddress userAddress, Function() onNewTransaction) async {
|
||||||
|
// _client?.pendingTransactions().listen((transactionHash) async {
|
||||||
|
// final transaction = await _client!.getTransactionByHash(transactionHash);
|
||||||
|
//
|
||||||
|
// if (transaction.from.hex == userAddress || transaction.to?.hex == userAddress) {
|
||||||
|
// onNewTransaction();
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<EtherAmount> getBalance(EthereumAddress address) async =>
|
||||||
|
await _client!.getBalance(address);
|
||||||
|
|
||||||
|
Future<int> getGasUnitPrice() async {
|
||||||
|
final gasPrice = await _client!.getGasPrice();
|
||||||
|
return gasPrice.getInWei.toInt();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<int> getEstimatedGas() async {
|
||||||
|
final estimatedGas = await _client!.estimateGas();
|
||||||
|
return estimatedGas.toInt();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<PendingEthereumTransaction> signTransaction({
|
||||||
|
required EthPrivateKey privateKey,
|
||||||
|
required String toAddress,
|
||||||
|
required String amount,
|
||||||
|
required int gas,
|
||||||
|
required EthereumTransactionPriority priority,
|
||||||
|
required CryptoCurrency currency,
|
||||||
|
required int exponent,
|
||||||
|
String? contractAddress,
|
||||||
|
}) async {
|
||||||
|
assert(currency == CryptoCurrency.eth || contractAddress != null);
|
||||||
|
|
||||||
|
bool _isEthereum = currency == CryptoCurrency.eth;
|
||||||
|
|
||||||
|
final price = await _client!.getGasPrice();
|
||||||
|
|
||||||
|
final Transaction transaction = Transaction(
|
||||||
|
from: privateKey.address,
|
||||||
|
to: EthereumAddress.fromHex(toAddress),
|
||||||
|
maxGas: gas,
|
||||||
|
gasPrice: price,
|
||||||
|
maxPriorityFeePerGas: EtherAmount.fromInt(EtherUnit.gwei, priority.tip),
|
||||||
|
value: _isEthereum ? EtherAmount.inWei(BigInt.parse(amount)) : EtherAmount.zero(),
|
||||||
|
);
|
||||||
|
|
||||||
|
final signedTransaction = await _client!.signTransaction(privateKey, transaction);
|
||||||
|
|
||||||
|
final Function _sendTransaction;
|
||||||
|
|
||||||
|
if (_isEthereum) {
|
||||||
|
_sendTransaction = () async => await sendTransaction(signedTransaction);
|
||||||
|
} else {
|
||||||
|
final erc20 = ERC20(
|
||||||
|
client: _client!,
|
||||||
|
address: EthereumAddress.fromHex(contractAddress!),
|
||||||
|
);
|
||||||
|
|
||||||
|
_sendTransaction = () async {
|
||||||
|
await erc20.transfer(
|
||||||
|
EthereumAddress.fromHex(toAddress),
|
||||||
|
BigInt.parse(amount),
|
||||||
|
credentials: privateKey,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return PendingEthereumTransaction(
|
||||||
|
signedTransaction: signedTransaction,
|
||||||
|
amount: amount,
|
||||||
|
fee: BigInt.from(gas) * price.getInWei,
|
||||||
|
sendTransaction: _sendTransaction,
|
||||||
|
exponent: exponent,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<String> sendTransaction(Uint8List signedTransaction) async =>
|
||||||
|
await _client!.sendRawTransaction(signedTransaction);
|
||||||
|
|
||||||
|
Future getTransactionDetails(String transactionHash) async {
|
||||||
|
// Wait for the transaction receipt to become available
|
||||||
|
TransactionReceipt? receipt;
|
||||||
|
while (receipt == null) {
|
||||||
|
receipt = await _client!.getTransactionReceipt(transactionHash);
|
||||||
|
await Future.delayed(Duration(seconds: 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print the receipt information
|
||||||
|
print('Transaction Hash: ${receipt.transactionHash}');
|
||||||
|
print('Block Hash: ${receipt.blockHash}');
|
||||||
|
print('Block Number: ${receipt.blockNumber}');
|
||||||
|
print('Gas Used: ${receipt.gasUsed}');
|
||||||
|
|
||||||
|
/*
|
||||||
|
Transaction Hash: [112, 244, 4, 238, 89, 199, 171, 191, 210, 236, 110, 42, 185, 202, 220, 21, 27, 132, 123, 221, 137, 90, 77, 13, 23, 43, 12, 230, 93, 63, 221, 116]
|
||||||
|
I/flutter ( 4474): Block Hash: [149, 44, 250, 119, 111, 104, 82, 98, 17, 89, 30, 190, 25, 44, 218, 118, 127, 189, 241, 35, 213, 106, 25, 95, 195, 37, 55, 131, 185, 180, 246, 200]
|
||||||
|
I/flutter ( 4474): Block Number: 17120242
|
||||||
|
I/flutter ( 4474): Gas Used: 21000
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Wait for the transaction receipt to become available
|
||||||
|
TransactionInformation? transactionInformation;
|
||||||
|
while (transactionInformation == null) {
|
||||||
|
print("********************************");
|
||||||
|
transactionInformation = await _client!.getTransactionByHash(transactionHash);
|
||||||
|
await Future.delayed(Duration(seconds: 1));
|
||||||
|
}
|
||||||
|
// Print the receipt information
|
||||||
|
print('Transaction Hash: ${transactionInformation.hash}');
|
||||||
|
print('Block Hash: ${transactionInformation.blockHash}');
|
||||||
|
print('Block Number: ${transactionInformation.blockNumber}');
|
||||||
|
print('Gas Used: ${transactionInformation.gas}');
|
||||||
|
|
||||||
|
/*
|
||||||
|
Transaction Hash: 0x70f404ee59c7abbfd2ec6e2ab9cadc151b847bdd895a4d0d172b0ce65d3fdd74
|
||||||
|
I/flutter ( 4474): Block Hash: 0x952cfa776f68526211591ebe192cda767fbdf123d56a195fc3253783b9b4f6c8
|
||||||
|
I/flutter ( 4474): Block Number: 17120242
|
||||||
|
I/flutter ( 4474): Gas Used: 53000
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<ERC20Balance> fetchERC20Balances(
|
||||||
|
EthereumAddress userAddress, String contractAddress) async {
|
||||||
|
final erc20 = ERC20(address: EthereumAddress.fromHex(contractAddress), client: _client!);
|
||||||
|
final balance = await erc20.balanceOf(userAddress);
|
||||||
|
|
||||||
|
int exponent = (await erc20.decimals()).toInt();
|
||||||
|
|
||||||
|
return ERC20Balance(balance, exponent: exponent);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<Erc20Token?> getErc20Token(String contractAddress) async {
|
||||||
|
try {
|
||||||
|
final erc20 = ERC20(address: EthereumAddress.fromHex(contractAddress), client: _client!);
|
||||||
|
final name = await erc20.name();
|
||||||
|
final symbol = await erc20.symbol();
|
||||||
|
final decimal = await erc20.decimals();
|
||||||
|
|
||||||
|
return Erc20Token(
|
||||||
|
name: name,
|
||||||
|
symbol: symbol,
|
||||||
|
contractAddress: contractAddress,
|
||||||
|
decimal: decimal.toInt(),
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void stop() {
|
||||||
|
_client?.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<List<EthereumTransactionModel>> fetchTransactions(String address,
|
||||||
|
{String? contractAddress}) async {
|
||||||
|
try {
|
||||||
|
final response = await _httpClient.get(Uri.https("api.etherscan.io", "/api", {
|
||||||
|
"module": "account",
|
||||||
|
"action": contractAddress != null ? "tokentx" : "txlist",
|
||||||
|
if (contractAddress != null) "contractaddress": contractAddress,
|
||||||
|
"address": address,
|
||||||
|
"apikey": secrets.etherScanApiKey,
|
||||||
|
}));
|
||||||
|
|
||||||
|
final _jsonResponse = json.decode(response.body) as Map<String, dynamic>;
|
||||||
|
|
||||||
|
if (response.statusCode >= 200 && response.statusCode < 300 && _jsonResponse['status'] != 0) {
|
||||||
|
return (_jsonResponse['result'] as List)
|
||||||
|
.map((e) => EthereumTransactionModel.fromJson(e as Map<String, dynamic>))
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
return [];
|
||||||
|
} catch (e) {
|
||||||
|
print(e);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Future<int> _getDecimalPlacesForContract(DeployedContract contract) async {
|
||||||
|
// final String abi = await rootBundle.loadString("assets/abi_json/erc20_abi.json");
|
||||||
|
// final contractAbi = ContractAbi.fromJson(abi, "ERC20");
|
||||||
|
//
|
||||||
|
// final contract = DeployedContract(
|
||||||
|
// contractAbi,
|
||||||
|
// EthereumAddress.fromHex(_erc20Currencies[erc20Currency]!),
|
||||||
|
// );
|
||||||
|
// final decimalsFunction = contract.function('decimals');
|
||||||
|
// final decimals = await _client!.call(
|
||||||
|
// contract: contract,
|
||||||
|
// function: decimalsFunction,
|
||||||
|
// params: [],
|
||||||
|
// );
|
||||||
|
//
|
||||||
|
// int exponent = int.parse(decimals.first.toString());
|
||||||
|
// return exponent;
|
||||||
|
// }
|
||||||
|
}
|
11
cw_ethereum/lib/ethereum_exceptions.dart
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
import 'package:cw_core/crypto_currency.dart';
|
||||||
|
|
||||||
|
class EthereumTransactionCreationException implements Exception {
|
||||||
|
final String exceptionMessage;
|
||||||
|
|
||||||
|
EthereumTransactionCreationException(CryptoCurrency currency) :
|
||||||
|
this.exceptionMessage = 'Wrong balance. Not enough ${currency.title} on your balance.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() => exceptionMessage;
|
||||||
|
}
|
25
cw_ethereum/lib/ethereum_formatter.dart
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
import 'package:intl/intl.dart';
|
||||||
|
|
||||||
|
const ethereumAmountLength = 12;
|
||||||
|
const ethereumAmountDivider = 1000000000000;
|
||||||
|
final ethereumAmountFormat = NumberFormat()
|
||||||
|
..maximumFractionDigits = ethereumAmountLength
|
||||||
|
..minimumFractionDigits = 1;
|
||||||
|
|
||||||
|
class EthereumFormatter {
|
||||||
|
static int parseEthereumAmount(String amount) {
|
||||||
|
try {
|
||||||
|
return (double.parse(amount) * ethereumAmountDivider).round();
|
||||||
|
} catch (_) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static double parseEthereumAmountToDouble(int amount) {
|
||||||
|
try {
|
||||||
|
return amount / ethereumAmountDivider;
|
||||||
|
} catch (_) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
2058
cw_ethereum/lib/ethereum_mnemonics.dart
Normal file
17
cw_ethereum/lib/ethereum_transaction_credentials.dart
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
import 'package:cw_core/crypto_currency.dart';
|
||||||
|
import 'package:cw_core/output_info.dart';
|
||||||
|
import 'package:cw_ethereum/ethereum_transaction_priority.dart';
|
||||||
|
|
||||||
|
class EthereumTransactionCredentials {
|
||||||
|
EthereumTransactionCredentials(
|
||||||
|
this.outputs, {
|
||||||
|
required this.priority,
|
||||||
|
required this.currency,
|
||||||
|
this.feeRate,
|
||||||
|
});
|
||||||
|
|
||||||
|
final List<OutputInfo> outputs;
|
||||||
|
final EthereumTransactionPriority? priority;
|
||||||
|
final int? feeRate;
|
||||||
|
final CryptoCurrency currency;
|
||||||
|
}
|
77
cw_ethereum/lib/ethereum_transaction_history.dart
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
import 'dart:convert';
|
||||||
|
import 'dart:core';
|
||||||
|
import 'package:cw_core/pathForWallet.dart';
|
||||||
|
import 'package:cw_core/wallet_info.dart';
|
||||||
|
import 'package:cw_ethereum/file.dart';
|
||||||
|
import 'package:mobx/mobx.dart';
|
||||||
|
import 'package:cw_core/transaction_history.dart';
|
||||||
|
import 'package:cw_ethereum/ethereum_transaction_info.dart';
|
||||||
|
|
||||||
|
part 'ethereum_transaction_history.g.dart';
|
||||||
|
|
||||||
|
const transactionsHistoryFileName = 'transactions.json';
|
||||||
|
|
||||||
|
class EthereumTransactionHistory = EthereumTransactionHistoryBase with _$EthereumTransactionHistory;
|
||||||
|
|
||||||
|
abstract class EthereumTransactionHistoryBase
|
||||||
|
extends TransactionHistoryBase<EthereumTransactionInfo> with Store {
|
||||||
|
EthereumTransactionHistoryBase({required this.walletInfo, required String password})
|
||||||
|
: _password = password {
|
||||||
|
transactions = ObservableMap<String, EthereumTransactionInfo>();
|
||||||
|
}
|
||||||
|
|
||||||
|
final WalletInfo walletInfo;
|
||||||
|
String _password;
|
||||||
|
|
||||||
|
Future<void> init() async => await _load();
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> save() async {
|
||||||
|
try {
|
||||||
|
final dirPath = await pathForWalletDir(name: walletInfo.name, type: walletInfo.type);
|
||||||
|
final path = '$dirPath/$transactionsHistoryFileName';
|
||||||
|
final data = json.encode({'transactions': transactions});
|
||||||
|
await writeData(path: path, password: _password, data: data);
|
||||||
|
} catch (e, s) {
|
||||||
|
print('Error while save ethereum transaction history: ${e.toString()}');
|
||||||
|
print(s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void addOne(EthereumTransactionInfo transaction) => transactions[transaction.id] = transaction;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void addMany(Map<String, EthereumTransactionInfo> transactions) =>
|
||||||
|
this.transactions.addAll(transactions);
|
||||||
|
|
||||||
|
Future<Map<String, dynamic>> _read() async {
|
||||||
|
final dirPath = await pathForWalletDir(name: walletInfo.name, type: walletInfo.type);
|
||||||
|
final path = '$dirPath/$transactionsHistoryFileName';
|
||||||
|
final content = await read(path: path, password: _password);
|
||||||
|
if (content.isEmpty) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
return json.decode(content) as Map<String, dynamic>;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _load() async {
|
||||||
|
try {
|
||||||
|
final content = await _read();
|
||||||
|
final txs = content['transactions'] as Map<String, dynamic>? ?? {};
|
||||||
|
|
||||||
|
txs.entries.forEach((entry) {
|
||||||
|
final val = entry.value;
|
||||||
|
|
||||||
|
if (val is Map<String, dynamic>) {
|
||||||
|
final tx = EthereumTransactionInfo.fromJson(val);
|
||||||
|
_update(tx);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
print(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _update(EthereumTransactionInfo transaction) => transactions[transaction.id] = transaction;
|
||||||
|
}
|
74
cw_ethereum/lib/ethereum_transaction_info.dart
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
import 'package:cw_core/format_amount.dart';
|
||||||
|
import 'package:cw_core/transaction_direction.dart';
|
||||||
|
import 'package:cw_core/transaction_info.dart';
|
||||||
|
|
||||||
|
class EthereumTransactionInfo extends TransactionInfo {
|
||||||
|
EthereumTransactionInfo({
|
||||||
|
required this.id,
|
||||||
|
required this.height,
|
||||||
|
required this.ethAmount,
|
||||||
|
required this.ethFee,
|
||||||
|
this.tokenSymbol = "ETH",
|
||||||
|
this.exponent = 18,
|
||||||
|
required this.direction,
|
||||||
|
required this.isPending,
|
||||||
|
required this.date,
|
||||||
|
required this.confirmations,
|
||||||
|
}) : this.amount = ethAmount.toInt(),
|
||||||
|
this.fee = ethFee.toInt();
|
||||||
|
|
||||||
|
final String id;
|
||||||
|
final int height;
|
||||||
|
final int amount;
|
||||||
|
final BigInt ethAmount;
|
||||||
|
final int exponent;
|
||||||
|
final TransactionDirection direction;
|
||||||
|
final DateTime date;
|
||||||
|
final bool isPending;
|
||||||
|
final int fee;
|
||||||
|
final BigInt ethFee;
|
||||||
|
final int confirmations;
|
||||||
|
final String tokenSymbol;
|
||||||
|
String? _fiatAmount;
|
||||||
|
|
||||||
|
@override
|
||||||
|
String amountFormatted() =>
|
||||||
|
'${formatAmount((ethAmount / BigInt.from(10).pow(exponent)).toString())} $tokenSymbol';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String fiatAmount() => _fiatAmount ?? '';
|
||||||
|
|
||||||
|
@override
|
||||||
|
void changeFiatAmount(String amount) => _fiatAmount = formatAmount(amount);
|
||||||
|
|
||||||
|
@override
|
||||||
|
String feeFormatted() => '${(ethFee / BigInt.from(10).pow(18)).toString()} ETH';
|
||||||
|
|
||||||
|
factory EthereumTransactionInfo.fromJson(Map<String, dynamic> data) {
|
||||||
|
return EthereumTransactionInfo(
|
||||||
|
id: data['id'] as String,
|
||||||
|
height: data['height'] as int,
|
||||||
|
ethAmount: BigInt.parse(data['amount']),
|
||||||
|
exponent: data['exponent'] as int,
|
||||||
|
ethFee: BigInt.parse(data['fee']),
|
||||||
|
direction: parseTransactionDirectionFromInt(data['direction'] as int),
|
||||||
|
date: DateTime.fromMillisecondsSinceEpoch(data['date'] as int),
|
||||||
|
isPending: data['isPending'] as bool,
|
||||||
|
confirmations: data['confirmations'] as int,
|
||||||
|
tokenSymbol: data['tokenSymbol'] as String,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() => {
|
||||||
|
'id': id,
|
||||||
|
'height': height,
|
||||||
|
'amount': ethAmount.toString(),
|
||||||
|
'exponent': exponent,
|
||||||
|
'fee': ethFee.toString(),
|
||||||
|
'direction': direction.index,
|
||||||
|
'date': date.millisecondsSinceEpoch,
|
||||||
|
'isPending': isPending,
|
||||||
|
'confirmations': confirmations,
|
||||||
|
'tokenSymbol': tokenSymbol,
|
||||||
|
};
|
||||||
|
}
|
47
cw_ethereum/lib/ethereum_transaction_model.dart
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
class EthereumTransactionModel {
|
||||||
|
final DateTime date;
|
||||||
|
final String hash;
|
||||||
|
final String from;
|
||||||
|
final String to;
|
||||||
|
final BigInt amount;
|
||||||
|
final int gasUsed;
|
||||||
|
final BigInt gasPrice;
|
||||||
|
final String contractAddress;
|
||||||
|
final int confirmations;
|
||||||
|
final int blockNumber;
|
||||||
|
final String? tokenSymbol;
|
||||||
|
final int? tokenDecimal;
|
||||||
|
final bool isError;
|
||||||
|
|
||||||
|
EthereumTransactionModel({
|
||||||
|
required this.date,
|
||||||
|
required this.hash,
|
||||||
|
required this.from,
|
||||||
|
required this.to,
|
||||||
|
required this.amount,
|
||||||
|
required this.gasUsed,
|
||||||
|
required this.gasPrice,
|
||||||
|
required this.contractAddress,
|
||||||
|
required this.confirmations,
|
||||||
|
required this.blockNumber,
|
||||||
|
required this.tokenSymbol,
|
||||||
|
required this.tokenDecimal,
|
||||||
|
required this.isError,
|
||||||
|
});
|
||||||
|
|
||||||
|
factory EthereumTransactionModel.fromJson(Map<String, dynamic> json) => EthereumTransactionModel(
|
||||||
|
date: DateTime.fromMillisecondsSinceEpoch(int.parse(json["timeStamp"]) * 1000),
|
||||||
|
hash: json["hash"],
|
||||||
|
from: json["from"],
|
||||||
|
to: json["to"],
|
||||||
|
amount: BigInt.parse(json["value"]),
|
||||||
|
gasUsed: int.parse(json["gasUsed"]),
|
||||||
|
gasPrice: BigInt.parse(json["gasPrice"]),
|
||||||
|
contractAddress: json["contractAddress"],
|
||||||
|
confirmations: int.parse(json["confirmations"]),
|
||||||
|
blockNumber: int.parse(json["blockNumber"]),
|
||||||
|
tokenSymbol: json["tokenSymbol"] ?? "ETH",
|
||||||
|
tokenDecimal: int.tryParse(json["tokenDecimal"] ?? ""),
|
||||||
|
isError: json["isError"] == "1",
|
||||||
|
);
|
||||||
|
}
|
52
cw_ethereum/lib/ethereum_transaction_priority.dart
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
import 'package:cw_core/transaction_priority.dart';
|
||||||
|
|
||||||
|
class EthereumTransactionPriority extends TransactionPriority {
|
||||||
|
final int tip;
|
||||||
|
|
||||||
|
const EthereumTransactionPriority({required String title, required int raw, required this.tip})
|
||||||
|
: super(title: title, raw: raw);
|
||||||
|
|
||||||
|
static const List<EthereumTransactionPriority> all = [fast, medium, slow];
|
||||||
|
static const EthereumTransactionPriority slow =
|
||||||
|
EthereumTransactionPriority(title: 'slow', raw: 0, tip: 1);
|
||||||
|
static const EthereumTransactionPriority medium =
|
||||||
|
EthereumTransactionPriority(title: 'Medium', raw: 1, tip: 2);
|
||||||
|
static const EthereumTransactionPriority fast =
|
||||||
|
EthereumTransactionPriority(title: 'Fast', raw: 2, tip: 4);
|
||||||
|
|
||||||
|
static EthereumTransactionPriority deserialize({required int raw}) {
|
||||||
|
switch (raw) {
|
||||||
|
case 0:
|
||||||
|
return slow;
|
||||||
|
case 1:
|
||||||
|
return medium;
|
||||||
|
case 2:
|
||||||
|
return fast;
|
||||||
|
default:
|
||||||
|
throw Exception('Unexpected token: $raw for EthereumTransactionPriority deserialize');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String get units => 'gas';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
var label = '';
|
||||||
|
|
||||||
|
switch (this) {
|
||||||
|
case EthereumTransactionPriority.slow:
|
||||||
|
label = 'Slow';
|
||||||
|
break;
|
||||||
|
case EthereumTransactionPriority.medium:
|
||||||
|
label = 'Medium';
|
||||||
|
break;
|
||||||
|
case EthereumTransactionPriority.fast:
|
||||||
|
label = 'Fast';
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return label;
|
||||||
|
}
|
||||||
|
}
|
505
cw_ethereum/lib/ethereum_wallet.dart
Normal file
|
@ -0,0 +1,505 @@
|
||||||
|
import 'dart:async';
|
||||||
|
import 'dart:convert';
|
||||||
|
import 'dart:io';
|
||||||
|
import 'dart:math';
|
||||||
|
|
||||||
|
import 'package:cw_core/crypto_currency.dart';
|
||||||
|
import 'package:cw_core/cake_hive.dart';
|
||||||
|
import 'package:cw_core/node.dart';
|
||||||
|
import 'package:cw_core/pathForWallet.dart';
|
||||||
|
import 'package:cw_core/pending_transaction.dart';
|
||||||
|
import 'package:cw_core/sync_status.dart';
|
||||||
|
import 'package:cw_core/transaction_direction.dart';
|
||||||
|
import 'package:cw_core/transaction_priority.dart';
|
||||||
|
import 'package:cw_core/wallet_addresses.dart';
|
||||||
|
import 'package:cw_core/wallet_base.dart';
|
||||||
|
import 'package:cw_core/wallet_info.dart';
|
||||||
|
import 'package:cw_ethereum/default_erc20_tokens.dart';
|
||||||
|
import 'package:cw_ethereum/erc20_balance.dart';
|
||||||
|
import 'package:cw_ethereum/ethereum_client.dart';
|
||||||
|
import 'package:cw_ethereum/ethereum_exceptions.dart';
|
||||||
|
import 'package:cw_ethereum/ethereum_formatter.dart';
|
||||||
|
import 'package:cw_ethereum/ethereum_transaction_credentials.dart';
|
||||||
|
import 'package:cw_ethereum/ethereum_transaction_history.dart';
|
||||||
|
import 'package:cw_ethereum/ethereum_transaction_info.dart';
|
||||||
|
import 'package:cw_ethereum/ethereum_transaction_model.dart';
|
||||||
|
import 'package:cw_ethereum/ethereum_transaction_priority.dart';
|
||||||
|
import 'package:cw_ethereum/ethereum_wallet_addresses.dart';
|
||||||
|
import 'package:cw_ethereum/file.dart';
|
||||||
|
import 'package:cw_core/erc20_token.dart';
|
||||||
|
import 'package:hive/hive.dart';
|
||||||
|
import 'package:hex/hex.dart';
|
||||||
|
import 'package:mobx/mobx.dart';
|
||||||
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
|
import 'package:web3dart/web3dart.dart';
|
||||||
|
import 'package:bip39/bip39.dart' as bip39;
|
||||||
|
import 'package:bip32/bip32.dart' as bip32;
|
||||||
|
|
||||||
|
part 'ethereum_wallet.g.dart';
|
||||||
|
|
||||||
|
class EthereumWallet = EthereumWalletBase with _$EthereumWallet;
|
||||||
|
|
||||||
|
abstract class EthereumWalletBase
|
||||||
|
extends WalletBase<ERC20Balance, EthereumTransactionHistory, EthereumTransactionInfo>
|
||||||
|
with Store {
|
||||||
|
EthereumWalletBase({
|
||||||
|
required WalletInfo walletInfo,
|
||||||
|
String? mnemonic,
|
||||||
|
String? privateKey,
|
||||||
|
required String password,
|
||||||
|
ERC20Balance? initialBalance,
|
||||||
|
}) : syncStatus = NotConnectedSyncStatus(),
|
||||||
|
_password = password,
|
||||||
|
_mnemonic = mnemonic,
|
||||||
|
_hexPrivateKey = privateKey,
|
||||||
|
_isTransactionUpdating = false,
|
||||||
|
_client = EthereumClient(),
|
||||||
|
walletAddresses = EthereumWalletAddresses(walletInfo),
|
||||||
|
balance = ObservableMap<CryptoCurrency, ERC20Balance>.of(
|
||||||
|
{CryptoCurrency.eth: initialBalance ?? ERC20Balance(BigInt.zero)}),
|
||||||
|
super(walletInfo) {
|
||||||
|
this.walletInfo = walletInfo;
|
||||||
|
transactionHistory = EthereumTransactionHistory(walletInfo: walletInfo, password: password);
|
||||||
|
|
||||||
|
if (!CakeHive.isAdapterRegistered(Erc20Token.typeId)) {
|
||||||
|
CakeHive.registerAdapter(Erc20TokenAdapter());
|
||||||
|
}
|
||||||
|
|
||||||
|
_sharedPrefs.complete(SharedPreferences.getInstance());
|
||||||
|
}
|
||||||
|
|
||||||
|
final String? _mnemonic;
|
||||||
|
final String? _hexPrivateKey;
|
||||||
|
final String _password;
|
||||||
|
|
||||||
|
late final Box<Erc20Token> erc20TokensBox;
|
||||||
|
|
||||||
|
late final EthPrivateKey _ethPrivateKey;
|
||||||
|
|
||||||
|
late EthereumClient _client;
|
||||||
|
|
||||||
|
int? _gasPrice;
|
||||||
|
int? _estimatedGas;
|
||||||
|
bool _isTransactionUpdating;
|
||||||
|
|
||||||
|
// TODO: remove after integrating our own node and having eth_newPendingTransactionFilter
|
||||||
|
Timer? _transactionsUpdateTimer;
|
||||||
|
|
||||||
|
@override
|
||||||
|
WalletAddresses walletAddresses;
|
||||||
|
|
||||||
|
@override
|
||||||
|
@observable
|
||||||
|
SyncStatus syncStatus;
|
||||||
|
|
||||||
|
@override
|
||||||
|
@observable
|
||||||
|
late ObservableMap<CryptoCurrency, ERC20Balance> balance;
|
||||||
|
|
||||||
|
Completer<SharedPreferences> _sharedPrefs = Completer();
|
||||||
|
|
||||||
|
Future<void> init() async {
|
||||||
|
erc20TokensBox = await CakeHive.openBox<Erc20Token>(Erc20Token.boxName);
|
||||||
|
await walletAddresses.init();
|
||||||
|
await transactionHistory.init();
|
||||||
|
_ethPrivateKey = await getPrivateKey(
|
||||||
|
mnemonic: _mnemonic,
|
||||||
|
privateKey: _hexPrivateKey,
|
||||||
|
password: _password,
|
||||||
|
);
|
||||||
|
walletAddresses.address = _ethPrivateKey.address.toString();
|
||||||
|
await save();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
int calculateEstimatedFee(TransactionPriority priority, int? amount) {
|
||||||
|
try {
|
||||||
|
if (priority is EthereumTransactionPriority) {
|
||||||
|
final priorityFee = EtherAmount.fromInt(EtherUnit.gwei, priority.tip).getInWei.toInt();
|
||||||
|
return (_gasPrice! + priorityFee) * (_estimatedGas ?? 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
} catch (e) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> changePassword(String password) {
|
||||||
|
throw UnimplementedError("changePassword");
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void close() {
|
||||||
|
_client.stop();
|
||||||
|
_transactionsUpdateTimer?.cancel();
|
||||||
|
}
|
||||||
|
|
||||||
|
@action
|
||||||
|
@override
|
||||||
|
Future<void> connectToNode({required Node node}) async {
|
||||||
|
try {
|
||||||
|
syncStatus = ConnectingSyncStatus();
|
||||||
|
|
||||||
|
final isConnected = _client.connect(node);
|
||||||
|
|
||||||
|
if (!isConnected) {
|
||||||
|
throw Exception("Ethereum Node connection failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
_client.setListeners(_ethPrivateKey.address, _onNewTransaction);
|
||||||
|
|
||||||
|
_setTransactionUpdateTimer();
|
||||||
|
|
||||||
|
syncStatus = ConnectedSyncStatus();
|
||||||
|
} catch (e) {
|
||||||
|
syncStatus = FailedSyncStatus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<PendingTransaction> createTransaction(Object credentials) async {
|
||||||
|
final _credentials = credentials as EthereumTransactionCredentials;
|
||||||
|
final outputs = _credentials.outputs;
|
||||||
|
final hasMultiDestination = outputs.length > 1;
|
||||||
|
|
||||||
|
final CryptoCurrency transactionCurrency =
|
||||||
|
balance.keys.firstWhere((element) => element.title == _credentials.currency.title);
|
||||||
|
|
||||||
|
final _erc20Balance = balance[transactionCurrency]!;
|
||||||
|
BigInt totalAmount = BigInt.zero;
|
||||||
|
int exponent = transactionCurrency is Erc20Token ? transactionCurrency.decimal : 18;
|
||||||
|
num amountToEthereumMultiplier = pow(10, exponent);
|
||||||
|
|
||||||
|
// so far this can not be made with Ethereum as Ethereum does not support multiple recipients
|
||||||
|
if (hasMultiDestination) {
|
||||||
|
if (outputs.any((item) => item.sendAll || (item.formattedCryptoAmount ?? 0) <= 0)) {
|
||||||
|
throw EthereumTransactionCreationException(transactionCurrency);
|
||||||
|
}
|
||||||
|
|
||||||
|
final totalOriginalAmount = EthereumFormatter.parseEthereumAmountToDouble(
|
||||||
|
outputs.fold(0, (acc, value) => acc + (value.formattedCryptoAmount ?? 0)));
|
||||||
|
totalAmount = BigInt.from(totalOriginalAmount * amountToEthereumMultiplier);
|
||||||
|
|
||||||
|
if (_erc20Balance.balance < totalAmount) {
|
||||||
|
throw EthereumTransactionCreationException(transactionCurrency);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
final output = outputs.first;
|
||||||
|
// since the fees are taken from Ethereum
|
||||||
|
// then no need to subtract the fees from the amount if send all
|
||||||
|
final BigInt allAmount;
|
||||||
|
if (transactionCurrency is Erc20Token) {
|
||||||
|
allAmount = _erc20Balance.balance;
|
||||||
|
} else {
|
||||||
|
allAmount = _erc20Balance.balance -
|
||||||
|
BigInt.from(calculateEstimatedFee(_credentials.priority!, null));
|
||||||
|
}
|
||||||
|
final totalOriginalAmount =
|
||||||
|
EthereumFormatter.parseEthereumAmountToDouble(output.formattedCryptoAmount ?? 0);
|
||||||
|
totalAmount = output.sendAll
|
||||||
|
? allAmount
|
||||||
|
: BigInt.from(totalOriginalAmount * amountToEthereumMultiplier);
|
||||||
|
|
||||||
|
if (_erc20Balance.balance < totalAmount) {
|
||||||
|
throw EthereumTransactionCreationException(transactionCurrency);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final pendingEthereumTransaction = await _client.signTransaction(
|
||||||
|
privateKey: _ethPrivateKey,
|
||||||
|
toAddress: _credentials.outputs.first.isParsedAddress
|
||||||
|
? _credentials.outputs.first.extractedAddress!
|
||||||
|
: _credentials.outputs.first.address,
|
||||||
|
amount: totalAmount.toString(),
|
||||||
|
gas: _estimatedGas!,
|
||||||
|
priority: _credentials.priority!,
|
||||||
|
currency: transactionCurrency,
|
||||||
|
exponent: exponent,
|
||||||
|
contractAddress:
|
||||||
|
transactionCurrency is Erc20Token ? transactionCurrency.contractAddress : null,
|
||||||
|
);
|
||||||
|
|
||||||
|
return pendingEthereumTransaction;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _updateTransactions() async {
|
||||||
|
try {
|
||||||
|
if (_isTransactionUpdating) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
bool isEtherscanEnabled = (await _sharedPrefs.future).getBool("use_etherscan") ?? true;
|
||||||
|
if (!isEtherscanEnabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_isTransactionUpdating = true;
|
||||||
|
final transactions = await fetchTransactions();
|
||||||
|
transactionHistory.addMany(transactions);
|
||||||
|
await transactionHistory.save();
|
||||||
|
_isTransactionUpdating = false;
|
||||||
|
} catch (_) {
|
||||||
|
_isTransactionUpdating = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<Map<String, EthereumTransactionInfo>> fetchTransactions() async {
|
||||||
|
final address = _ethPrivateKey.address.hex;
|
||||||
|
final transactions = await _client.fetchTransactions(address);
|
||||||
|
|
||||||
|
final List<Future<List<EthereumTransactionModel>>> erc20TokensTransactions = [];
|
||||||
|
|
||||||
|
for (var token in balance.keys) {
|
||||||
|
if (token is Erc20Token) {
|
||||||
|
erc20TokensTransactions.add(_client.fetchTransactions(
|
||||||
|
address,
|
||||||
|
contractAddress: token.contractAddress,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final tokensTransaction = await Future.wait(erc20TokensTransactions);
|
||||||
|
transactions.addAll(tokensTransaction.expand((element) => element));
|
||||||
|
|
||||||
|
final Map<String, EthereumTransactionInfo> result = {};
|
||||||
|
|
||||||
|
for (var transactionModel in transactions) {
|
||||||
|
if (transactionModel.isError) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
result[transactionModel.hash] = EthereumTransactionInfo(
|
||||||
|
id: transactionModel.hash,
|
||||||
|
height: transactionModel.blockNumber,
|
||||||
|
ethAmount: transactionModel.amount,
|
||||||
|
direction: transactionModel.from == address
|
||||||
|
? TransactionDirection.outgoing
|
||||||
|
: TransactionDirection.incoming,
|
||||||
|
isPending: false,
|
||||||
|
date: transactionModel.date,
|
||||||
|
confirmations: transactionModel.confirmations,
|
||||||
|
ethFee: BigInt.from(transactionModel.gasUsed) * transactionModel.gasPrice,
|
||||||
|
exponent: transactionModel.tokenDecimal ?? 18,
|
||||||
|
tokenSymbol: transactionModel.tokenSymbol ?? "ETH",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Object get keys => throw UnimplementedError("keys");
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> rescan({required int height}) {
|
||||||
|
throw UnimplementedError("rescan");
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> save() async {
|
||||||
|
await walletAddresses.updateAddressesInBox();
|
||||||
|
final path = await makePath();
|
||||||
|
await write(path: path, password: _password, data: toJSON());
|
||||||
|
await transactionHistory.save();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String? get seed => _mnemonic;
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get privateKey => HEX.encode(_ethPrivateKey.privateKey);
|
||||||
|
|
||||||
|
@action
|
||||||
|
@override
|
||||||
|
Future<void> startSync() async {
|
||||||
|
try {
|
||||||
|
syncStatus = AttemptingSyncStatus();
|
||||||
|
await _updateBalance();
|
||||||
|
await _updateTransactions();
|
||||||
|
_gasPrice = await _client.getGasUnitPrice();
|
||||||
|
_estimatedGas = await _client.getEstimatedGas();
|
||||||
|
|
||||||
|
Timer.periodic(
|
||||||
|
const Duration(minutes: 1), (timer) async => _gasPrice = await _client.getGasUnitPrice());
|
||||||
|
Timer.periodic(const Duration(seconds: 10),
|
||||||
|
(timer) async => _estimatedGas = await _client.getEstimatedGas());
|
||||||
|
|
||||||
|
syncStatus = SyncedSyncStatus();
|
||||||
|
} catch (e) {
|
||||||
|
syncStatus = FailedSyncStatus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<String> makePath() async => pathForWallet(name: walletInfo.name, type: walletInfo.type);
|
||||||
|
|
||||||
|
String toJSON() => json.encode({
|
||||||
|
'mnemonic': _mnemonic,
|
||||||
|
'private_key': privateKey,
|
||||||
|
'balance': balance[currency]!.toJSON(),
|
||||||
|
});
|
||||||
|
|
||||||
|
static Future<EthereumWallet> open({
|
||||||
|
required String name,
|
||||||
|
required String password,
|
||||||
|
required WalletInfo walletInfo,
|
||||||
|
}) async {
|
||||||
|
final path = await pathForWallet(name: name, type: walletInfo.type);
|
||||||
|
final jsonSource = await read(path: path, password: password);
|
||||||
|
final data = json.decode(jsonSource) as Map;
|
||||||
|
final mnemonic = data['mnemonic'] as String?;
|
||||||
|
final privateKey = data['private_key'] as String?;
|
||||||
|
final balance = ERC20Balance.fromJSON(data['balance'] as String) ?? ERC20Balance(BigInt.zero);
|
||||||
|
|
||||||
|
return EthereumWallet(
|
||||||
|
walletInfo: walletInfo,
|
||||||
|
password: password,
|
||||||
|
mnemonic: mnemonic,
|
||||||
|
privateKey: privateKey,
|
||||||
|
initialBalance: balance,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _updateBalance() async {
|
||||||
|
balance[currency] = await _fetchEthBalance();
|
||||||
|
|
||||||
|
await _fetchErc20Balances();
|
||||||
|
await save();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<ERC20Balance> _fetchEthBalance() async {
|
||||||
|
final balance = await _client.getBalance(_ethPrivateKey.address);
|
||||||
|
return ERC20Balance(balance.getInWei);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _fetchErc20Balances() async {
|
||||||
|
for (var token in erc20TokensBox.values) {
|
||||||
|
try {
|
||||||
|
if (token.enabled) {
|
||||||
|
balance[token] = await _client.fetchERC20Balances(
|
||||||
|
_ethPrivateKey.address,
|
||||||
|
token.contractAddress,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
balance.remove(token);
|
||||||
|
}
|
||||||
|
} catch (_) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<EthPrivateKey> getPrivateKey(
|
||||||
|
{String? mnemonic, String? privateKey, required String password}) async {
|
||||||
|
assert(mnemonic != null || privateKey != null);
|
||||||
|
|
||||||
|
if (privateKey != null) {
|
||||||
|
return EthPrivateKey.fromHex(privateKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
final seed = bip39.mnemonicToSeed(mnemonic!);
|
||||||
|
|
||||||
|
final root = bip32.BIP32.fromSeed(seed);
|
||||||
|
|
||||||
|
const _hdPathEthereum = "m/44'/60'/0'/0";
|
||||||
|
const index = 0;
|
||||||
|
final addressAtIndex = root.derivePath("$_hdPathEthereum/$index");
|
||||||
|
|
||||||
|
return EthPrivateKey.fromHex(HEX.encode(addressAtIndex.privateKey as List<int>));
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void>? updateBalance() async => await _updateBalance();
|
||||||
|
|
||||||
|
List<Erc20Token> get erc20Currencies => erc20TokensBox.values.toList();
|
||||||
|
|
||||||
|
Future<void> addErc20Token(Erc20Token token) async {
|
||||||
|
String? iconPath;
|
||||||
|
try {
|
||||||
|
iconPath = CryptoCurrency.all
|
||||||
|
.firstWhere((element) => element.title.toUpperCase() == token.symbol.toUpperCase())
|
||||||
|
.iconPath;
|
||||||
|
} catch (_) {}
|
||||||
|
|
||||||
|
final _token = Erc20Token(
|
||||||
|
name: token.name,
|
||||||
|
symbol: token.symbol,
|
||||||
|
contractAddress: token.contractAddress,
|
||||||
|
decimal: token.decimal,
|
||||||
|
enabled: token.enabled,
|
||||||
|
iconPath: iconPath,
|
||||||
|
);
|
||||||
|
|
||||||
|
await erc20TokensBox.put(_token.contractAddress, _token);
|
||||||
|
|
||||||
|
if (_token.enabled) {
|
||||||
|
balance[_token] = await _client.fetchERC20Balances(
|
||||||
|
_ethPrivateKey.address,
|
||||||
|
_token.contractAddress,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
balance.remove(_token);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> deleteErc20Token(Erc20Token token) async {
|
||||||
|
await token.delete();
|
||||||
|
|
||||||
|
balance.remove(token);
|
||||||
|
_updateBalance();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<Erc20Token?> getErc20Token(String contractAddress) async =>
|
||||||
|
await _client.getErc20Token(contractAddress);
|
||||||
|
|
||||||
|
void _onNewTransaction() {
|
||||||
|
_updateBalance();
|
||||||
|
_updateTransactions();
|
||||||
|
}
|
||||||
|
|
||||||
|
void addInitialTokens() {
|
||||||
|
final initialErc20Tokens = DefaultErc20Tokens().initialErc20Tokens;
|
||||||
|
|
||||||
|
initialErc20Tokens.forEach((token) => erc20TokensBox.put(token.contractAddress, token));
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> renameWalletFiles(String newWalletName) async {
|
||||||
|
final currentWalletPath = await pathForWallet(name: walletInfo.name, type: type);
|
||||||
|
final currentWalletFile = File(currentWalletPath);
|
||||||
|
|
||||||
|
final currentDirPath = await pathForWalletDir(name: walletInfo.name, type: type);
|
||||||
|
final currentTransactionsFile = File('$currentDirPath/$transactionsHistoryFileName');
|
||||||
|
|
||||||
|
// Copies current wallet files into new wallet name's dir and files
|
||||||
|
if (currentWalletFile.existsSync()) {
|
||||||
|
final newWalletPath = await pathForWallet(name: newWalletName, type: type);
|
||||||
|
await currentWalletFile.copy(newWalletPath);
|
||||||
|
}
|
||||||
|
if (currentTransactionsFile.existsSync()) {
|
||||||
|
final newDirPath = await pathForWalletDir(name: newWalletName, type: type);
|
||||||
|
await currentTransactionsFile.copy('$newDirPath/$transactionsHistoryFileName');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete old name's dir and files
|
||||||
|
await Directory(currentDirPath).delete(recursive: true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _setTransactionUpdateTimer() {
|
||||||
|
if (_transactionsUpdateTimer?.isActive ?? false) {
|
||||||
|
_transactionsUpdateTimer!.cancel();
|
||||||
|
}
|
||||||
|
|
||||||
|
_transactionsUpdateTimer = Timer.periodic(Duration(seconds: 10), (_) {
|
||||||
|
_updateTransactions();
|
||||||
|
_updateBalance();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void updateEtherscanUsageState(bool isEnabled) {
|
||||||
|
if (isEnabled) {
|
||||||
|
_updateTransactions();
|
||||||
|
_setTransactionUpdateTimer();
|
||||||
|
} else {
|
||||||
|
_transactionsUpdateTimer?.cancel();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
33
cw_ethereum/lib/ethereum_wallet_addresses.dart
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
import 'package:cw_core/wallet_addresses.dart';
|
||||||
|
import 'package:cw_core/wallet_info.dart';
|
||||||
|
import 'package:mobx/mobx.dart';
|
||||||
|
|
||||||
|
part 'ethereum_wallet_addresses.g.dart';
|
||||||
|
|
||||||
|
class EthereumWalletAddresses = EthereumWalletAddressesBase with _$EthereumWalletAddresses;
|
||||||
|
|
||||||
|
abstract class EthereumWalletAddressesBase extends WalletAddresses with Store {
|
||||||
|
EthereumWalletAddressesBase(WalletInfo walletInfo)
|
||||||
|
: address = '',
|
||||||
|
super(walletInfo);
|
||||||
|
|
||||||
|
@override
|
||||||
|
String address;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> init() async {
|
||||||
|
address = walletInfo.address;
|
||||||
|
await updateAddressesInBox();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> updateAddressesInBox() async {
|
||||||
|
try {
|
||||||
|
addressesMap.clear();
|
||||||
|
addressesMap[address] = '';
|
||||||
|
await saveAddressesInBox();
|
||||||
|
} catch (e) {
|
||||||
|
print(e.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|