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
This commit is contained in:
OmarHatem 2023-09-07 21:28:40 +03:00
commit 9fbb206a7c
523 changed files with 29361 additions and 19998 deletions

View file

@ -1,6 +1,7 @@
name: Cache Dependencies
on:
workflow_dispatch:
push:
branches: [ main ]
@ -18,7 +19,7 @@ jobs:
- name: Flutter action
uses: subosito/flutter-action@v1
with:
flutter-version: '3.3.x'
flutter-version: '3.10.x'
channel: stable
- name: Install package dependencies
@ -45,7 +46,7 @@ jobs:
/opt/android/cake_wallet/cw_monero/android/.cxx
/opt/android/cake_wallet/cw_monero/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' }}
name: Generate Externals

View file

@ -13,6 +13,13 @@ jobs:
KEY_PASS: test@cake_wallet
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/setup-java@v1
with:
@ -21,7 +28,7 @@ jobs:
- name: Flutter action
uses: subosito/flutter-action@v1
with:
flutter-version: '3.7.x'
flutter-version: '3.10.x'
channel: stable
- name: Install package dependencies
@ -48,7 +55,7 @@ jobs:
/opt/android/cake_wallet/cw_monero/android/.cxx
/opt/android/cake_wallet/cw_monero/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' }}
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_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
- name: Add secrets
run: |
cd /opt/android/cake_wallet
touch lib/.secrets.g.dart
touch cw_ethereum/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 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 fiatApiKey = '${{ secrets.FIAT_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
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
continue-on-error: true
run: |
cd /opt/android/cake_wallet
var=$(curl --upload-file build/app/outputs/apk/release/app-release.apk https://transfer.sh/$GITHUB_HEAD_REF.apk -H "Max-Days: 10")
curl ${{ secrets.SLACK_WEB_HOOK }} -H "Content-Type: application/json" -d '{"apk_link": "'"$var"'","ticket": "'"$GITHUB_HEAD_REF"'"}'
uses: adrey/slack-file-upload-action@1.0.5
with:
token: ${{ secrets.SLACK_APP_TOKEN }}
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
View file

@ -8,6 +8,7 @@
.buildlog/
.history
.svn/
.fvm/
# IntelliJ related
*.iml
@ -89,7 +90,9 @@ android/key.properties
**/tool/.secrets-prod.json
**/tool/.secrets-test.json
**/tool/.secrets-config.json
**/tool/.ethereum-secrets-config.json
**/lib/.secrets.g.dart
**/cw_ethereum/lib/.secrets.g.dart
vendor/
@ -120,6 +123,7 @@ cw_haven/android/.cxx/
lib/bitcoin/bitcoin.dart
lib/monero/monero.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_120.png

View file

@ -1,6 +1,6 @@
Privacy Policy
Last modified: July 21, 2022
Last modified: August 9, 2023
Introduction
============
@ -13,7 +13,7 @@ Introduction
- On this App.
- In email, text, and other electronic messages between you and this App.
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.
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.
- "Cake Labs Nodes" refers to the set of cryptocurrency nodes operated and maintained by Cake Labs LLC.
- "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.
- "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
--------------------------------------------
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.
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 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
----------------------------------------------------------
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:
- 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
---------------------------
@ -112,7 +115,9 @@ Data Security
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
-----------------------------

View file

@ -1,4 +1,4 @@
# Cake Wallet for Android and iOS
# Cake Wallet for Mobile and Desktop
## Open Source Multi-Currency Wallet
@ -7,6 +7,7 @@
* Website: https://cakewallet.com
* App Store (iOS / MacOS): https://cakewallet.com/ios
* Google Play: https://cakewallet.com/gp
* F-Droid: https://fdroid.cakelabs.com
* APK: 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.*
* Built-in exchange for dozens of pairs
* 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
* 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
* Create several wallets
* Select your own custom nodes/servers
@ -32,6 +32,7 @@
* Convenient exchange and sending templates for recurring payments
* Create donation links and invoices in the receive screen
* Robust privacy settings (eg: Tor-only connections)
* Robust security settings (eg: Cake 2FA)
### Monero Specific Features
@ -40,13 +41,19 @@
* Specify restore height for faster syncing
* Specify multiple recipients for batch sending
* Optionally set Monero nodes as trusted for faster syncing
* Specify a proxy for Monero nodes, compatible with Tor and i2p
### Bitcoin Specific Features
* Bitcoin coin control (specify specific outputs to spend)
* Automatically generate new addresses
* 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
@ -69,6 +76,7 @@
* Website: https://monero.com
* App Store (iOS): https://apps.apple.com/app/id1601990386
* 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
# Support

View file

@ -45,8 +45,8 @@ android {
defaultConfig {
applicationId appProperties['id']
minSdkVersion 21
targetSdkVersion 31
minSdkVersion 24
targetSdkVersion 33
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"

View file

@ -14,6 +14,8 @@
android:icon="@mipmap/ic_launcher"
android:allowBackup="false"
android:fullBackupContent="false"
android:versionCode="__versionCode__"
android:versionName="__versionName__"
android:requestLegacyExternalStorage="true">
<activity
android:name=".MainActivity"
@ -22,7 +24,6 @@
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize"
android:screenOrientation="portrait"
android:exported="true">
<meta-data
android:name="io.flutter.embedding.android.SplashScreenDrawable"

View file

@ -27,6 +27,6 @@ subprojects {
project.evaluationDependsOn(':app')
}
task clean(type: Delete) {
tasks.register("clean", Delete) {
delete rootProject.buildDir
}

View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

BIN
assets/images/arb_icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.7 KiB

BIN
assets/images/bat_icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

BIN
assets/images/cake_icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 582 B

BIN
assets/images/comp_icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

BIN
assets/images/cro_icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

BIN
assets/images/dydx_icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

BIN
assets/images/ens_icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1 KiB

BIN
assets/images/frax_icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

BIN
assets/images/ftm_icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 576 B

BIN
assets/images/grt_icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

BIN
assets/images/gtc_icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 KiB

BIN
assets/images/gusd_icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 596 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 394 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 683 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 570 B

BIN
assets/images/ldo_icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

BIN
assets/images/nexo_icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

BIN
assets/images/pepe_icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 538 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 773 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 557 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 680 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 518 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 KiB

BIN
assets/images/tusd_icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 838 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

BIN
assets/images/wbtc_icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.3 KiB

BIN
assets/images/weth_icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6 KiB

BIN
assets/images/zrx_icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

View file

@ -1,3 +1,2 @@
Enable iPad/Tablet separate layout from mobile UI
SideShift update and fixes
Bug Fixes
Bug fixes
Fiat Onramp improvements

View file

@ -1,4 +1,2 @@
Enable iPad/Tablet separate layout from mobile UI
SideShift update and fixes
Add MoonPay sell
Bug Fixes
Bug fixes
Fiat Onramp improvements

View 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

View file

@ -1,4 +1,4 @@
class BitcoinTransactionNoInputsException implements Exception {
@override
String toString() => 'Not enough inputs available';
String toString() => 'Not enough inputs available. Please select more under Coin Control';
}

View file

@ -13,9 +13,11 @@ import 'package:cw_core/wallet_type.dart';
import 'package:hive/hive.dart';
import 'package:collection/collection.dart';
class BitcoinWalletService extends WalletService<BitcoinNewWalletCredentials,
BitcoinRestoreWalletFromSeedCredentials, BitcoinRestoreWalletFromWIFCredentials> {
BitcoinWalletService(this.walletInfoSource, this.unspentCoinsInfoSource, this.isDirect);
class BitcoinWalletService extends WalletService<
BitcoinNewWalletCredentials,
BitcoinRestoreWalletFromSeedCredentials,
BitcoinRestoreWalletFromWIFCredentials> {
BitcoinWalletService(this.walletInfoSource, this.unspentCoinsInfoSource);
final Box<WalletInfo> walletInfoSource;
final Box<UnspentCoinsInfo> unspentCoinsInfoSource;
@ -43,8 +45,8 @@ class BitcoinWalletService extends WalletService<BitcoinNewWalletCredentials,
@override
Future<BitcoinWallet> openWallet(String name, String password) async {
final walletInfo = walletInfoSource.values
.firstWhereOrNull((info) => info.id == WalletBase.idFor(name, getType()))!;
final walletInfo = walletInfoSource.values.firstWhereOrNull(
(info) => info.id == WalletBase.idFor(name, getType()))!;
final wallet = await BitcoinWalletBase.open(
password: password,
name: name,
@ -57,23 +59,22 @@ class BitcoinWalletService extends WalletService<BitcoinNewWalletCredentials,
@override
Future<void> remove(String wallet) async {
File(await pathForWalletDir(name: wallet, type: getType())).delete(recursive: true);
final walletInfo = walletInfoSource.values
.firstWhereOrNull((info) => info.id == WalletBase.idFor(wallet, getType()))!;
File(await pathForWalletDir(name: wallet, type: getType()))
.delete(recursive: true);
final walletInfo = walletInfoSource.values.firstWhereOrNull(
(info) => info.id == WalletBase.idFor(wallet, getType()))!;
await walletInfoSource.delete(walletInfo.key);
}
@override
Future<void> rename(String currentName, String password, String newName) async {
final currentWalletInfo = walletInfoSource.values
.firstWhereOrNull((info) => info.id == WalletBase.idFor(currentName, getType()))!;
final currentWalletInfo = walletInfoSource.values.firstWhereOrNull(
(info) => info.id == WalletBase.idFor(currentName, getType()))!;
final currentWallet = await BitcoinWalletBase.open(
password: password,
name: currentName,
walletInfo: currentWalletInfo,
unspentCoinsInfo: unspentCoinsInfoSource,
encryptionFileUtils: encryptionFileUtilsFor(isDirect),
);
unspentCoinsInfo: unspentCoinsInfoSource);
await currentWallet.renameWalletFiles(newName);
@ -85,11 +86,13 @@ class BitcoinWalletService extends WalletService<BitcoinNewWalletCredentials,
}
@override
Future<BitcoinWallet> restoreFromKeys(BitcoinRestoreWalletFromWIFCredentials credentials) async =>
Future<BitcoinWallet> restoreFromKeys(
BitcoinRestoreWalletFromWIFCredentials credentials) async =>
throw UnimplementedError();
@override
Future<BitcoinWallet> restoreFromSeed(BitcoinRestoreWalletFromSeedCredentials credentials) async {
Future<BitcoinWallet> restoreFromSeed(
BitcoinRestoreWalletFromSeedCredentials credentials) async {
if (!validateMnemonic(credentials.mnemonic)) {
throw BitcoinMnemonicIsIncorrectException();
}

View file

@ -8,7 +8,7 @@ import 'package:cw_bitcoin/electrum_transaction_info.dart';
part 'electrum_transaction_history.g.dart';
const _transactionsHistoryFileName = 'transactions.json';
const transactionsHistoryFileName = 'transactions.json';
class ElectrumTransactionHistory = ElectrumTransactionHistoryBase
with _$ElectrumTransactionHistory;
@ -42,7 +42,7 @@ abstract class ElectrumTransactionHistoryBase
try {
final dirPath =
await pathForWalletDir(name: walletInfo.name, type: walletInfo.type);
final path = '$dirPath/$_transactionsHistoryFileName';
final path = '$dirPath/$transactionsHistoryFileName';
final data =
json.encode({'height': _height, 'transactions': transactions});
await encryptionFileUtils.write(path: path, password: _password, data: data);
@ -59,7 +59,7 @@ abstract class ElectrumTransactionHistoryBase
Future<Map<String, dynamic>> _read() async {
final dirPath =
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);
return json.decode(content) as Map<String, dynamic>;
}
@ -67,7 +67,7 @@ abstract class ElectrumTransactionHistoryBase
Future<void> _load() async {
try {
final content = await _read();
final txs = content['transactions'] as Map<String, dynamic> ?? {};
final txs = content['transactions'] as Map<String, dynamic>? ?? {};
txs.entries.forEach((entry) {
final val = entry.value;
@ -86,4 +86,5 @@ abstract class ElectrumTransactionHistoryBase
void _update(ElectrumTransactionInfo transaction) =>
transactions[transaction.id] = transaction;
}

View file

@ -1,4 +1,3 @@
import 'package:flutter/foundation.dart';
import 'package:bitcoin_flutter/bitcoin_flutter.dart' as bitcoin;
import 'package:bitcoin_flutter/src/payments/index.dart' show PaymentData;
import 'package:cw_bitcoin/address_from_output.dart';
@ -217,9 +216,9 @@ class ElectrumTransactionInfo extends TransactionInfo {
height: info.height,
amount: info.amount,
fee: info.fee,
direction: direction ?? info.direction,
date: date ?? info.date,
isPending: isPending ?? info.isPending,
direction: direction,
date: date,
isPending: isPending,
confirmations: info.confirmations);
}

View file

@ -1,5 +1,6 @@
import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'dart:math';
import 'dart:typed_data';
import 'package:cw_bitcoin/encryption_file_utils.dart';
@ -439,6 +440,29 @@ abstract class ElectrumWalletBase extends WalletBase<ElectrumBalance,
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
Future<void> changePassword(String password) async {
_password = password;

View file

@ -13,8 +13,10 @@ import 'package:cw_core/wallet_info.dart';
import 'package:cw_core/wallet_base.dart';
import 'package:collection/collection.dart';
class LitecoinWalletService extends WalletService<BitcoinNewWalletCredentials,
BitcoinRestoreWalletFromSeedCredentials, BitcoinRestoreWalletFromWIFCredentials> {
class LitecoinWalletService extends WalletService<
BitcoinNewWalletCredentials,
BitcoinRestoreWalletFromSeedCredentials,
BitcoinRestoreWalletFromWIFCredentials> {
LitecoinWalletService(this.walletInfoSource, this.unspentCoinsInfoSource, this.isDirect);
final Box<WalletInfo> walletInfoSource;
@ -44,8 +46,8 @@ class LitecoinWalletService extends WalletService<BitcoinNewWalletCredentials,
@override
Future<LitecoinWallet> openWallet(String name, String password) async {
final walletInfo = walletInfoSource.values
.firstWhereOrNull((info) => info.id == WalletBase.idFor(name, getType()))!;
final walletInfo = walletInfoSource.values.firstWhereOrNull(
(info) => info.id == WalletBase.idFor(name, getType()))!;
final wallet = await LitecoinWalletBase.open(
password: password,
name: name,
@ -58,23 +60,23 @@ class LitecoinWalletService extends WalletService<BitcoinNewWalletCredentials,
@override
Future<void> remove(String wallet) async {
File(await pathForWalletDir(name: wallet, type: getType())).delete(recursive: true);
final walletInfo = walletInfoSource.values
.firstWhereOrNull((info) => info.id == WalletBase.idFor(wallet, getType()))!;
File(await pathForWalletDir(name: wallet, type: getType()))
.delete(recursive: true);
final walletInfo = walletInfoSource.values.firstWhereOrNull(
(info) => info.id == WalletBase.idFor(wallet, getType()))!;
await walletInfoSource.delete(walletInfo.key);
}
@override
Future<void> rename(String currentName, String password, String newName) async {
final currentWalletInfo = walletInfoSource.values
.firstWhereOrNull((info) => info.id == WalletBase.idFor(currentName, getType()))!;
final currentWalletInfo = walletInfoSource.values.firstWhereOrNull(
(info) => info.id == WalletBase.idFor(currentName, getType()))!;
final currentWallet = await LitecoinWalletBase.open(
password: password,
name: currentName,
walletInfo: currentWalletInfo,
unspentCoinsInfo: unspentCoinsInfoSource,
encryptionFileUtils: encryptionFileUtilsFor(isDirect),
);
encryptionFileUtils: encryptionFileUtilsFor(isDirect));
await currentWallet.renameWalletFiles(newName);

View file

@ -37,19 +37,19 @@ packages:
dependency: transitive
description:
name: async
sha256: bfe67ef28df125b7dddcea62755991f807aa39a2492a23e1550161692950bbe0
sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c"
url: "https://pub.dev"
source: hosted
version: "2.10.0"
version: "2.11.0"
bech32:
dependency: transitive
description:
path: "."
ref: "cake-0.2.1"
resolved-ref: cafd1c270641e95017d57d69f55cca9831d4db56
ref: "cake-0.2.2"
resolved-ref: "05755063b593aa6cca0a4820a318e0ce17de6192"
url: "https://github.com/cake-tech/bech32.git"
source: git
version: "0.2.1"
version: "0.2.2"
bip32:
dependency: transitive
description:
@ -70,8 +70,8 @@ packages:
dependency: "direct main"
description:
path: "."
ref: cake-update-v2
resolved-ref: "8f86453761c0c26e368392d0ff2c6f12f3b7397b"
ref: cake-update-v3
resolved-ref: df9204144011ed9419eff7d9ef3143102a40252d
url: "https://github.com/cake-tech/bitcoin_flutter.git"
source: git
version: "2.0.2"
@ -168,10 +168,10 @@ packages:
dependency: transitive
description:
name: characters
sha256: e6a326c8af69605aec75ed6c187d06b349707a27fbff8222ca9cc2cff167975c
sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605"
url: "https://pub.dev"
source: hosted
version: "1.2.1"
version: "1.3.0"
checked_yaml:
dependency: transitive
description:
@ -200,18 +200,18 @@ packages:
dependency: transitive
description:
name: collection
sha256: cfc915e6923fe5ce6e153b0723c753045de46de1b4d63771530504004a45fae0
sha256: "4a07be6cb69c84d677a6c3096fcf960cc3285a8330b4603e0d463d15d9bd934c"
url: "https://pub.dev"
source: hosted
version: "1.17.0"
version: "1.17.1"
convert:
dependency: transitive
description:
name: convert
sha256: "196284f26f69444b7f5c50692b55ec25da86d9e500451dc09333bf2e3ad69259"
sha256: "0f08b14755d163f6e2134cb58222dd25ea2a2ee8a195e53983d57c075324d592"
url: "https://pub.dev"
source: hosted
version: "3.0.2"
version: "3.1.1"
crypto:
dependency: transitive
description:
@ -361,10 +361,10 @@ packages:
dependency: "direct main"
description:
name: http
sha256: "6aa2946395183537c8b880962d935877325d6a09a2867c3970c05c0fed6ac482"
sha256: "759d1a329847dd0f39226c688d3e06a6b8679668e350e2891a6474f8b4bb8525"
url: "https://pub.dev"
source: hosted
version: "0.13.5"
version: "1.1.0"
http_multi_server:
dependency: transitive
description:
@ -385,10 +385,10 @@ packages:
dependency: "direct main"
description:
name: intl
sha256: "910f85bce16fb5c6f614e117efa303e85a1731bb0081edf3604a2ae6e9a3cc91"
sha256: "3bc132a9dbce73a7e4a21a17d06e1878839ffbf975568bc875c60537824b0c4d"
url: "https://pub.dev"
source: hosted
version: "0.17.0"
version: "0.18.1"
io:
dependency: transitive
description:
@ -401,10 +401,10 @@ packages:
dependency: transitive
description:
name: js
sha256: "5528c2f391ededb7775ec1daa69e65a2d61276f7552de2b5f7b8d34ee9fd4ab7"
sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3
url: "https://pub.dev"
source: hosted
version: "0.6.5"
version: "0.6.7"
json_annotation:
dependency: transitive
description:
@ -425,10 +425,10 @@ packages:
dependency: transitive
description:
name: matcher
sha256: "16db949ceee371e9b99d22f88fa3a73c4e59fd0afed0bd25fc336eb76c198b72"
sha256: "6501fbd55da300384b768785b83e5ce66991266cec21af89ab9ae7f5ce1c4cbb"
url: "https://pub.dev"
source: hosted
version: "0.12.13"
version: "0.12.15"
material_color_utilities:
dependency: transitive
description:
@ -441,10 +441,10 @@ packages:
dependency: transitive
description:
name: meta
sha256: "6c268b42ed578a53088d834796959e4a1814b5e9e164f147f580a386e5decf42"
sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3"
url: "https://pub.dev"
source: hosted
version: "1.8.0"
version: "1.9.1"
mime:
dependency: transitive
description:
@ -481,10 +481,10 @@ packages:
dependency: transitive
description:
name: path
sha256: db9d4f58c908a4ba5953fcee2ae317c94889433e5024c27ce74a37f94267945b
sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917"
url: "https://pub.dev"
source: hosted
version: "1.8.2"
version: "1.8.3"
path_provider:
dependency: "direct main"
description:
@ -686,10 +686,10 @@ packages:
dependency: transitive
description:
name: test_api
sha256: ad540f65f92caa91bf21dfc8ffb8c589d6e4dc0c2267818b4cc2792857706206
sha256: eb6ac1540b26de412b3403a163d919ba86f6a973fe6cc50ae3541b80092fdcfb
url: "https://pub.dev"
source: hosted
version: "0.4.16"
version: "0.5.1"
timing:
dependency: transitive
description:
@ -771,5 +771,5 @@ packages:
source: hosted
version: "3.1.1"
sdks:
dart: ">=2.19.0 <4.0.0"
dart: ">=3.0.0 <4.0.0"
flutter: ">=3.0.0"

View file

@ -13,16 +13,16 @@ dependencies:
flutter:
sdk: flutter
path_provider: ^2.0.11
http: ^0.13.4
http: ^1.1.0
mobx: ^2.0.7+4
flutter_mobx: ^2.0.6+1
intl: ^0.17.0
intl: ^0.18.0
cw_core:
path: ../cw_core
bitcoin_flutter:
git:
url: https://github.com/cake-tech/bitcoin_flutter.git
ref: cake-update-v2
ref: cake-update-v3
rxdart: ^0.27.5
unorm_dart: ^0.2.0
cryptography: ^2.0.5

View 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;
}

View file

@ -0,0 +1,4 @@
import 'package:hive/hive.dart';
import 'package:hive/src/hive_impl.dart';
final HiveInterface CakeHive = HiveImpl();

View file

@ -11,6 +11,8 @@ CryptoCurrency currencyForWalletType(WalletType type) {
return CryptoCurrency.ltc;
case WalletType.haven:
return CryptoCurrency.xhv;
case WalletType.ethereum:
return CryptoCurrency.eth;
default:
throw Exception('Unexpected wallet type: ${type.toString()} for CryptoCurrency currencyForWalletType');
}

View 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;
}

View 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;

View file

@ -2,24 +2,31 @@ import 'package:cw_core/balance.dart';
import 'package:cw_core/monero_amount_format.dart';
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),
formattedUnlockedBalance =
moneroAmountToString(amount: unlockedBalance),
formattedUnlockedBalance = moneroAmountToString(amount: unlockedBalance),
frozenFormatted = moneroAmountToString(amount: frozenBalance),
super(unlockedBalance, fullBalance);
MoneroBalance.fromString(
{required this.formattedFullBalance,
required this.formattedUnlockedBalance})
required this.formattedUnlockedBalance,
this.frozenFormatted = '0.0'})
: fullBalance = moneroParseAmount(amount: formattedFullBalance),
unlockedBalance = moneroParseAmount(amount: formattedUnlockedBalance),
frozenBalance = moneroParseAmount(amount: frozenFormatted),
super(moneroParseAmount(amount: formattedUnlockedBalance),
moneroParseAmount(amount: formattedFullBalance));
final int fullBalance;
final int unlockedBalance;
final int frozenBalance;
final String formattedFullBalance;
final String formattedUnlockedBalance;
final String frozenFormatted;
@override
String get formattedFrozenBalance => frozenFormatted == '0.0' ? '' : frozenFormatted;
@override
String get formattedAvailableBalance => formattedUnlockedBalance;

View file

@ -3,6 +3,7 @@ import 'package:cw_core/keyable.dart';
import 'dart:convert';
import 'package:http/http.dart' as http;
import 'package:hive/hive.dart';
import 'package:cw_core/hive_type_ids.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:http/io_client.dart' as ioc;
@ -18,6 +19,7 @@ class Node extends HiveObject with Keyable {
this.password,
this.useSSL,
this.trusted = false,
this.socksProxyAddress,
String? uri,
WalletType? type,}) {
if (uri != null) {
@ -33,9 +35,10 @@ class Node extends HiveObject with Keyable {
login = map['login'] as String?,
password = map['password'] as String?,
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';
@HiveField(0, defaultValue: '')
@ -56,8 +59,13 @@ class Node extends HiveObject with Keyable {
@HiveField(5, defaultValue: false)
bool trusted;
@HiveField(6)
String? socksProxyAddress;
bool get isSSL => useSSL ?? false;
bool get useSocksProxy => socksProxyAddress == null ? false : socksProxyAddress!.isNotEmpty;
Uri get uri {
switch (type) {
case WalletType.monero:
@ -68,6 +76,8 @@ class Node extends HiveObject with Keyable {
return createUriFromElectrumAddress(uriRaw);
case WalletType.haven:
return Uri.http(uriRaw, '');
case WalletType.ethereum:
return Uri.https(uriRaw, '');
default:
throw Exception('Unexpected type ${type.toString()} for Node uri');
}
@ -81,7 +91,8 @@ class Node extends HiveObject with Keyable {
other.password == password &&
other.typeRaw == typeRaw &&
other.useSSL == useSSL &&
other.trusted == trusted);
other.trusted == trusted &&
other.socksProxyAddress == socksProxyAddress);
@override
int get hashCode =>
@ -90,7 +101,8 @@ class Node extends HiveObject with Keyable {
password.hashCode ^
typeRaw.hashCode ^
useSSL.hashCode ^
trusted.hashCode;
trusted.hashCode ^
socksProxyAddress.hashCode;
@override
dynamic get keyIndex {
@ -108,13 +120,15 @@ class Node extends HiveObject with Keyable {
try {
switch (type) {
case WalletType.monero:
return requestMoneroNode();
return useSocksProxy ? requestNodeWithProxy(socksProxyAddress ?? '') : requestMoneroNode();
case WalletType.bitcoin:
return requestElectrumServer();
case WalletType.litecoin:
return requestElectrumServer();
case WalletType.haven:
return requestMoneroNode();
case WalletType.ethereum:
return requestElectrumServer();
default:
return false;
}
@ -159,6 +173,22 @@ class Node extends HiveObject with Keyable {
}
}
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 {
try {
await SecureSocket.connect(uri.host, uri.port,
@ -168,4 +198,17 @@ class Node extends HiveObject with Keyable {
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;
}
}
}

View file

@ -1,7 +1,9 @@
import 'package:flutter/services.dart';
const utils = const MethodChannel('com.cake_wallet/native_utils');
void setIsAppSecureNative(bool isAppSecure) {
try {
final utils = const MethodChannel('com.cake_wallet/native_utils');
utils.invokeMethod<Uint8List>('setIsAppSecure', {'isAppSecure': isAppSecure});
} catch (_) {}
}

View file

@ -14,6 +14,8 @@ abstract class TransactionHistoryBase<TransactionType extends TransactionInfo> {
void addMany(Map<String, TransactionType> transactions);
void clear() => transactions.clear();
// bool _isUpdating;
// @action

View file

@ -1,3 +1,4 @@
import 'package:cw_core/hive_type_ids.dart';
import 'package:hive/hive.dart';
part 'unspent_coins_info.g.dart';
@ -12,9 +13,11 @@ class UnspentCoinsInfo extends HiveObject {
required this.noteRaw,
required this.address,
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 boxKey = 'unspentBoxKey';
@ -42,6 +45,9 @@ class UnspentCoinsInfo extends HiveObject {
@HiveField(7, defaultValue: 0)
int vout;
@HiveField(8, defaultValue: null)
String? keyImage;
String get note => noteRaw ?? '';
set note(String value) => noteRaw = value;

View file

@ -1,8 +1,10 @@
import 'package:cw_core/address_info.dart';
import 'package:cw_core/wallet_info.dart';
abstract class WalletAddresses {
WalletAddresses(this.walletInfo)
: addressesMap = {};
: addressesMap = {},
addressInfos = {};
final WalletInfo walletInfo;
@ -12,6 +14,10 @@ abstract class WalletAddresses {
Map<String, String> addressesMap;
Map<int, List<AddressInfo>> addressInfos;
Set<String> usedAddresses = {};
Future<void> init();
Future<void> updateAddressesInBox();
@ -20,6 +26,8 @@ abstract class WalletAddresses {
try {
walletInfo.address = address;
walletInfo.addresses = addressesMap;
walletInfo.addressInfos = addressInfos;
walletInfo.usedAddresses = usedAddresses.toList();
if (walletInfo.isInBox) {
await walletInfo.save();

View file

@ -42,7 +42,9 @@ abstract class WalletBase<
set syncStatus(SyncStatus status);
String get seed;
String? get seed;
String? get privateKey => null;
Object get keys;
@ -50,6 +52,10 @@ abstract class WalletBase<
late HistoryType transactionHistory;
set isEnabledAutoGenerateSubaddress(bool value) {}
bool get isEnabledAutoGenerateSubaddress => false;
Future<void> connectToNode({required Node node});
Future<void> startSync();
@ -77,4 +83,6 @@ abstract class WalletBase<
Future<void>? updateBalance();
void setExceptionHandler(void Function(FlutterErrorDetails) onError) => null;
Future<void> renameWalletFiles(String newWalletName);
}

View file

@ -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 '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';
@ -30,7 +31,7 @@ class WalletInfo extends HiveObject {
yatEid, yatLastUsedAddressRaw, showIntroCakePayCard);
}
static const typeId = 4;
static const typeId = WALLET_INFO_TYPE_ID;
static const boxName = 'WalletInfo';
@HiveField(0, defaultValue: '')
@ -72,6 +73,12 @@ class WalletInfo extends HiveObject {
@HiveField(13)
bool? showIntroCakePayCard;
@HiveField(14)
Map<int, List<AddressInfo>>? addressInfos;
@HiveField(15)
List<String>? usedAddresses;
String get yatLastUsedAddress => yatLastUsedAddressRaw ?? '';
set yatLastUsedAddress(String address) {

View file

@ -17,4 +17,6 @@ abstract class WalletService<N extends WalletCredentials,
Future<bool> isWalletExit(String name);
Future<void> remove(String wallet);
Future<void> rename(String currentName, String password, String newName);
}

View file

@ -1,4 +1,5 @@
import 'package:cw_core/crypto_currency.dart';
import 'package:cw_core/hive_type_ids.dart';
import 'package:hive/hive.dart';
part 'wallet_type.g.dart';
@ -7,11 +8,11 @@ const walletTypes = [
WalletType.monero,
WalletType.bitcoin,
WalletType.litecoin,
WalletType.haven
WalletType.haven,
WalletType.ethereum,
];
const walletTypeTypeId = 5;
@HiveType(typeId: walletTypeTypeId)
@HiveType(typeId: WALLET_TYPE_TYPE_ID)
enum WalletType {
@HiveField(0)
monero,
@ -26,7 +27,10 @@ enum WalletType {
litecoin,
@HiveField(4)
haven
haven,
@HiveField(5)
ethereum,
}
int serializeToInt(WalletType type) {
@ -39,6 +43,8 @@ int serializeToInt(WalletType type) {
return 2;
case WalletType.haven:
return 3;
case WalletType.ethereum:
return 4;
default:
return -1;
}
@ -54,6 +60,8 @@ WalletType deserializeFromInt(int raw) {
return WalletType.litecoin;
case 3:
return WalletType.haven;
case 4:
return WalletType.ethereum;
default:
throw Exception('Unexpected token: $raw for WalletType deserializeFromInt');
}
@ -69,6 +77,8 @@ String walletTypeToString(WalletType type) {
return 'Litecoin';
case WalletType.haven:
return 'Haven';
case WalletType.ethereum:
return 'Ethereum';
default:
return '';
}
@ -77,13 +87,15 @@ String walletTypeToString(WalletType type) {
String walletTypeToDisplayName(WalletType type) {
switch (type) {
case WalletType.monero:
return 'Monero';
return 'Monero (XMR)';
case WalletType.bitcoin:
return 'Bitcoin (Electrum)';
return 'Bitcoin (BTC)';
case WalletType.litecoin:
return 'Litecoin (Electrum)';
return 'Litecoin (LTC)';
case WalletType.haven:
return 'Haven';
return 'Haven (XHV)';
case WalletType.ethereum:
return 'Ethereum (ETH)';
default:
return '';
}
@ -99,6 +111,8 @@ CryptoCurrency walletTypeToCryptoCurrency(WalletType type) {
return CryptoCurrency.ltc;
case WalletType.haven:
return CryptoCurrency.xhv;
case WalletType.ethereum:
return CryptoCurrency.eth;
default:
throw Exception('Unexpected wallet type: ${type.toString()} for CryptoCurrency walletTypeToCryptoCurrency');
}

View file

@ -5,191 +5,218 @@ packages:
dependency: transitive
description:
name: _fe_analyzer_shared
url: "https://pub.dartlang.org"
sha256: "4897882604d919befd350648c7f91926a9d5de99e67b455bf0917cc2362f4bb8"
url: "https://pub.dev"
source: hosted
version: "47.0.0"
analyzer:
dependency: transitive
description:
name: analyzer
url: "https://pub.dartlang.org"
sha256: "690e335554a8385bc9d787117d9eb52c0c03ee207a607e593de3c9d71b1cfe80"
url: "https://pub.dev"
source: hosted
version: "4.7.0"
args:
dependency: transitive
description:
name: args
url: "https://pub.dartlang.org"
sha256: "139d809800a412ebb26a3892da228b2d0ba36f0ef5d9a82166e5e52ec8d61611"
url: "https://pub.dev"
source: hosted
version: "2.4.0"
version: "2.3.2"
asn1lib:
dependency: transitive
description:
name: asn1lib
url: "https://pub.dartlang.org"
sha256: ab96a1cb3beeccf8145c52e449233fe68364c9641623acd3adad66f8184f1039
url: "https://pub.dev"
source: hosted
version: "1.4.0"
async:
dependency: transitive
description:
name: async
url: "https://pub.dartlang.org"
sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c"
url: "https://pub.dev"
source: hosted
version: "2.9.0"
version: "2.11.0"
boolean_selector:
dependency: transitive
description:
name: boolean_selector
url: "https://pub.dartlang.org"
sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66"
url: "https://pub.dev"
source: hosted
version: "2.1.0"
version: "2.1.1"
build:
dependency: transitive
description:
name: build
url: "https://pub.dartlang.org"
sha256: "3fbda25365741f8251b39f3917fb3c8e286a96fd068a5a242e11c2012d495777"
url: "https://pub.dev"
source: hosted
version: "2.3.1"
build_config:
dependency: transitive
description:
name: build_config
url: "https://pub.dartlang.org"
sha256: bf80fcfb46a29945b423bd9aad884590fb1dc69b330a4d4700cac476af1708d1
url: "https://pub.dev"
source: hosted
version: "1.1.1"
build_daemon:
dependency: transitive
description:
name: build_daemon
url: "https://pub.dartlang.org"
sha256: "6bc5544ea6ce4428266e7ea680e945c68806c4aae2da0eb5e9ccf38df8d6acbf"
url: "https://pub.dev"
source: hosted
version: "3.1.1"
version: "3.1.0"
build_resolvers:
dependency: "direct dev"
description:
name: build_resolvers
url: "https://pub.dartlang.org"
sha256: "687cf90a3951affac1bd5f9ecb5e3e90b60487f3d9cdc359bb310f8876bb02a6"
url: "https://pub.dev"
source: hosted
version: "2.0.10"
build_runner:
dependency: "direct dev"
description:
name: build_runner
url: "https://pub.dartlang.org"
sha256: b0a8a7b8a76c493e85f1b84bffa0588859a06197863dba8c9036b15581fd9727
url: "https://pub.dev"
source: hosted
version: "2.3.3"
build_runner_core:
dependency: transitive
description:
name: build_runner_core
url: "https://pub.dartlang.org"
sha256: "14febe0f5bac5ae474117a36099b4de6f1dbc52df6c5e55534b3da9591bf4292"
url: "https://pub.dev"
source: hosted
version: "7.2.7"
built_collection:
dependency: transitive
description:
name: built_collection
url: "https://pub.dartlang.org"
sha256: "376e3dd27b51ea877c28d525560790aee2e6fbb5f20e2f85d5081027d94e2100"
url: "https://pub.dev"
source: hosted
version: "5.1.1"
built_value:
dependency: transitive
description:
name: built_value
url: "https://pub.dartlang.org"
sha256: "169565c8ad06adb760c3645bf71f00bff161b00002cace266cad42c5d22a7725"
url: "https://pub.dev"
source: hosted
version: "8.4.4"
version: "8.4.3"
characters:
dependency: transitive
description:
name: characters
url: "https://pub.dartlang.org"
sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605"
url: "https://pub.dev"
source: hosted
version: "1.2.1"
version: "1.3.0"
checked_yaml:
dependency: transitive
description:
name: checked_yaml
url: "https://pub.dartlang.org"
sha256: "3d1505d91afa809d177efd4eed5bb0eb65805097a1463abdd2add076effae311"
url: "https://pub.dev"
source: hosted
version: "2.0.2"
clock:
dependency: transitive
description:
name: clock
url: "https://pub.dartlang.org"
sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf
url: "https://pub.dev"
source: hosted
version: "1.1.1"
code_builder:
dependency: transitive
description:
name: code_builder
url: "https://pub.dartlang.org"
sha256: "0d43dd1288fd145de1ecc9a3948ad4a6d5a82f0a14c4fdd0892260787d975cbe"
url: "https://pub.dev"
source: hosted
version: "4.4.0"
collection:
dependency: transitive
description:
name: collection
url: "https://pub.dartlang.org"
sha256: "4a07be6cb69c84d677a6c3096fcf960cc3285a8330b4603e0d463d15d9bd934c"
url: "https://pub.dev"
source: hosted
version: "1.16.0"
version: "1.17.1"
convert:
dependency: transitive
description:
name: convert
url: "https://pub.dartlang.org"
sha256: "0f08b14755d163f6e2134cb58222dd25ea2a2ee8a195e53983d57c075324d592"
url: "https://pub.dev"
source: hosted
version: "3.1.1"
crypto:
dependency: transitive
description:
name: crypto
url: "https://pub.dartlang.org"
sha256: aa274aa7774f8964e4f4f38cc994db7b6158dd36e9187aaceaddc994b35c6c67
url: "https://pub.dev"
source: hosted
version: "3.0.2"
dart_style:
dependency: transitive
description:
name: dart_style
url: "https://pub.dartlang.org"
sha256: "7a03456c3490394c8e7665890333e91ae8a49be43542b616e414449ac358acd4"
url: "https://pub.dev"
source: hosted
version: "2.2.4"
encrypt:
dependency: "direct main"
description:
name: encrypt
url: "https://pub.dartlang.org"
sha256: "4fd4e4fdc21b9d7d4141823e1e6515cd94e7b8d84749504c232999fba25d9bbb"
url: "https://pub.dev"
source: hosted
version: "5.0.1"
fake_async:
dependency: transitive
description:
name: fake_async
url: "https://pub.dartlang.org"
sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78"
url: "https://pub.dev"
source: hosted
version: "1.3.1"
ffi:
dependency: transitive
description:
name: ffi
url: "https://pub.dartlang.org"
sha256: a38574032c5f1dd06c4aee541789906c12ccaab8ba01446e800d9c5b79c4a978
url: "https://pub.dev"
source: hosted
version: "2.0.1"
file:
dependency: "direct main"
description:
name: file
url: "https://pub.dartlang.org"
sha256: "1b92bec4fc2a72f59a8e15af5f52cd441e4a7860b49499d69dfa817af20e925d"
url: "https://pub.dev"
source: hosted
version: "6.1.4"
fixnum:
dependency: transitive
description:
name: fixnum
url: "https://pub.dartlang.org"
sha256: "25517a4deb0c03aa0f32fd12db525856438902d9c16536311e76cdc57b31d7d1"
url: "https://pub.dev"
source: hosted
version: "1.0.1"
version: "1.1.0"
flutter:
dependency: "direct main"
description: flutter
@ -199,7 +226,8 @@ packages:
dependency: "direct main"
description:
name: flutter_mobx
url: "https://pub.dartlang.org"
sha256: "0da4add0016387a7bf309a0d0c41d36c6b3ae25ed7a176409267f166509e723e"
url: "https://pub.dev"
source: hosted
version: "2.0.6+5"
flutter_test:
@ -211,252 +239,288 @@ packages:
dependency: transitive
description:
name: frontend_server_client
url: "https://pub.dartlang.org"
sha256: "408e3ca148b31c20282ad6f37ebfa6f4bdc8fede5b74bc2f08d9d92b55db3612"
url: "https://pub.dev"
source: hosted
version: "3.2.0"
glob:
dependency: transitive
description:
name: glob
url: "https://pub.dartlang.org"
sha256: "4515b5b6ddb505ebdd242a5f2cc5d22d3d6a80013789debfbda7777f47ea308c"
url: "https://pub.dev"
source: hosted
version: "2.1.1"
graphs:
dependency: transitive
description:
name: graphs
url: "https://pub.dartlang.org"
sha256: f9e130f3259f52d26f0cfc0e964513796dafed572fa52e45d2f8d6ca14db39b2
url: "https://pub.dev"
source: hosted
version: "2.2.0"
hive:
dependency: transitive
description:
name: hive
url: "https://pub.dartlang.org"
sha256: "8dcf6db979d7933da8217edcec84e9df1bdb4e4edc7fc77dbd5aa74356d6d941"
url: "https://pub.dev"
source: hosted
version: "2.2.3"
hive_generator:
dependency: "direct dev"
description:
name: hive_generator
url: "https://pub.dartlang.org"
sha256: "81fd20125cb2ce8fd23623d7744ffbaf653aae93706c9bd3bf7019ea0ace3938"
url: "https://pub.dev"
source: hosted
version: "1.1.3"
http:
dependency: "direct main"
description:
name: http
url: "https://pub.dartlang.org"
sha256: "759d1a329847dd0f39226c688d3e06a6b8679668e350e2891a6474f8b4bb8525"
url: "https://pub.dev"
source: hosted
version: "0.13.5"
version: "1.1.0"
http_multi_server:
dependency: transitive
description:
name: http_multi_server
url: "https://pub.dartlang.org"
sha256: "97486f20f9c2f7be8f514851703d0119c3596d14ea63227af6f7a481ef2b2f8b"
url: "https://pub.dev"
source: hosted
version: "3.2.1"
http_parser:
dependency: transitive
description:
name: http_parser
url: "https://pub.dartlang.org"
sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b"
url: "https://pub.dev"
source: hosted
version: "4.0.2"
intl:
dependency: "direct main"
description:
name: intl
url: "https://pub.dartlang.org"
sha256: "3bc132a9dbce73a7e4a21a17d06e1878839ffbf975568bc875c60537824b0c4d"
url: "https://pub.dev"
source: hosted
version: "0.17.0"
version: "0.18.1"
io:
dependency: transitive
description:
name: io
url: "https://pub.dartlang.org"
sha256: "2ec25704aba361659e10e3e5f5d672068d332fc8ac516421d483a11e5cbd061e"
url: "https://pub.dev"
source: hosted
version: "1.0.4"
js:
dependency: transitive
description:
name: js
url: "https://pub.dartlang.org"
sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3
url: "https://pub.dev"
source: hosted
version: "0.6.5"
version: "0.6.7"
json_annotation:
dependency: transitive
description:
name: json_annotation
url: "https://pub.dartlang.org"
sha256: c33da08e136c3df0190bd5bbe51ae1df4a7d96e7954d1d7249fea2968a72d317
url: "https://pub.dev"
source: hosted
version: "4.8.0"
logging:
dependency: transitive
description:
name: logging
url: "https://pub.dartlang.org"
sha256: "04094f2eb032cbb06c6f6e8d3607edcfcb0455e2bb6cbc010cb01171dcb64e6d"
url: "https://pub.dev"
source: hosted
version: "1.1.1"
matcher:
dependency: transitive
description:
name: matcher
url: "https://pub.dartlang.org"
sha256: "6501fbd55da300384b768785b83e5ce66991266cec21af89ab9ae7f5ce1c4cbb"
url: "https://pub.dev"
source: hosted
version: "0.12.12"
version: "0.12.15"
material_color_utilities:
dependency: transitive
description:
name: material_color_utilities
url: "https://pub.dartlang.org"
sha256: d92141dc6fe1dad30722f9aa826c7fbc896d021d792f80678280601aff8cf724
url: "https://pub.dev"
source: hosted
version: "0.1.5"
version: "0.2.0"
meta:
dependency: transitive
description:
name: meta
url: "https://pub.dartlang.org"
sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3"
url: "https://pub.dev"
source: hosted
version: "1.8.0"
version: "1.9.1"
mime:
dependency: transitive
description:
name: mime
url: "https://pub.dartlang.org"
sha256: e4ff8e8564c03f255408decd16e7899da1733852a9110a58fe6d1b817684a63e
url: "https://pub.dev"
source: hosted
version: "1.0.4"
mobx:
dependency: "direct main"
description:
name: mobx
url: "https://pub.dartlang.org"
sha256: f1862bd92c6a903fab67338f27e2f731117c3cb9ea37cee1a487f9e4e0de314a
url: "https://pub.dev"
source: hosted
version: "2.1.4"
version: "2.1.3+1"
mobx_codegen:
dependency: "direct dev"
description:
name: mobx_codegen
url: "https://pub.dartlang.org"
sha256: "86122e410d8ea24dda0c69adb5c2a6ccadd5ce02ad46e144764e0d0184a06181"
url: "https://pub.dev"
source: hosted
version: "2.1.1"
package_config:
dependency: transitive
description:
name: package_config
url: "https://pub.dartlang.org"
sha256: "1c5b77ccc91e4823a5af61ee74e6b972db1ef98c2ff5a18d3161c982a55448bd"
url: "https://pub.dev"
source: hosted
version: "2.1.0"
path:
dependency: transitive
description:
name: path
url: "https://pub.dartlang.org"
sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917"
url: "https://pub.dev"
source: hosted
version: "1.8.2"
version: "1.8.3"
path_provider:
dependency: "direct main"
description:
name: path_provider
url: "https://pub.dartlang.org"
sha256: dcea5feb97d8abf90cab9e9030b497fb7c3cbf26b7a1fe9e3ef7dcb0a1ddec95
url: "https://pub.dev"
source: hosted
version: "2.0.13"
version: "2.0.12"
path_provider_android:
dependency: transitive
description:
name: path_provider_android
url: "https://pub.dartlang.org"
sha256: a776c088d671b27f6e3aa8881d64b87b3e80201c64e8869b811325de7a76c15e
url: "https://pub.dev"
source: hosted
version: "2.0.24"
version: "2.0.22"
path_provider_foundation:
dependency: transitive
description:
name: path_provider_foundation
url: "https://pub.dartlang.org"
sha256: "62a68e7e1c6c459f9289859e2fae58290c981ce21d1697faf54910fe1faa4c74"
url: "https://pub.dev"
source: hosted
version: "2.1.3"
version: "2.1.1"
path_provider_linux:
dependency: transitive
description:
name: path_provider_linux
url: "https://pub.dartlang.org"
sha256: ab0987bf95bc591da42dffb38c77398fc43309f0b9b894dcc5d6f40c4b26c379
url: "https://pub.dev"
source: hosted
version: "2.1.10"
version: "2.1.7"
path_provider_platform_interface:
dependency: transitive
description:
name: path_provider_platform_interface
url: "https://pub.dartlang.org"
sha256: f0abc8ebd7253741f05488b4813d936b4d07c6bae3e86148a09e342ee4b08e76
url: "https://pub.dev"
source: hosted
version: "2.0.6"
version: "2.0.5"
path_provider_windows:
dependency: transitive
description:
name: path_provider_windows
url: "https://pub.dartlang.org"
sha256: bcabbe399d4042b8ee687e17548d5d3f527255253b4a639f5f8d2094a9c2b45c
url: "https://pub.dev"
source: hosted
version: "2.1.5"
version: "2.1.3"
platform:
dependency: transitive
description:
name: platform
url: "https://pub.dartlang.org"
sha256: "4a451831508d7d6ca779f7ac6e212b4023dd5a7d08a27a63da33756410e32b76"
url: "https://pub.dev"
source: hosted
version: "3.1.0"
plugin_platform_interface:
dependency: transitive
description:
name: plugin_platform_interface
url: "https://pub.dartlang.org"
sha256: dbf0f707c78beedc9200146ad3cb0ab4d5da13c246336987be6940f026500d3a
url: "https://pub.dev"
source: hosted
version: "2.1.4"
version: "2.1.3"
pointycastle:
dependency: transitive
description:
name: pointycastle
url: "https://pub.dartlang.org"
sha256: db7306cf0249f838d1a24af52b5a5887c5bf7f31d8bb4e827d071dc0939ad346
url: "https://pub.dev"
source: hosted
version: "3.6.2"
pool:
dependency: transitive
description:
name: pool
url: "https://pub.dartlang.org"
sha256: "20fe868b6314b322ea036ba325e6fc0711a22948856475e2c2b6306e8ab39c2a"
url: "https://pub.dev"
source: hosted
version: "1.5.1"
process:
dependency: transitive
description:
name: process
url: "https://pub.dartlang.org"
sha256: "53fd8db9cec1d37b0574e12f07520d582019cb6c44abf5479a01505099a34a09"
url: "https://pub.dev"
source: hosted
version: "4.2.4"
pub_semver:
dependency: transitive
description:
name: pub_semver
url: "https://pub.dartlang.org"
sha256: "307de764d305289ff24ad257ad5c5793ce56d04947599ad68b3baa124105fc17"
url: "https://pub.dev"
source: hosted
version: "2.1.3"
pubspec_parse:
dependency: transitive
description:
name: pubspec_parse
url: "https://pub.dartlang.org"
sha256: "75f6614d6dde2dc68948dffbaa4fe5dae32cd700eb9fb763fe11dfb45a3c4d0a"
url: "https://pub.dev"
source: hosted
version: "1.2.2"
version: "1.2.1"
shelf:
dependency: transitive
description:
name: shelf
url: "https://pub.dartlang.org"
sha256: c24a96135a2ccd62c64b69315a14adc5c3419df63b4d7c05832a346fdb73682c
url: "https://pub.dev"
source: hosted
version: "1.4.0"
shelf_web_socket:
dependency: transitive
description:
name: shelf_web_socket
url: "https://pub.dartlang.org"
sha256: a988c0e8d8ffbdb8a28aa7ec8e449c260f3deb808781fe1284d22c5bba7156e8
url: "https://pub.dev"
source: hosted
version: "1.0.3"
sky_engine:
@ -468,121 +532,138 @@ packages:
dependency: transitive
description:
name: source_gen
url: "https://pub.dartlang.org"
sha256: "2d79738b6bbf38a43920e2b8d189e9a3ce6cc201f4b8fc76be5e4fe377b1c38d"
url: "https://pub.dev"
source: hosted
version: "1.2.6"
source_helper:
dependency: transitive
description:
name: source_helper
url: "https://pub.dartlang.org"
sha256: "3b67aade1d52416149c633ba1bb36df44d97c6b51830c2198e934e3fca87ca1f"
url: "https://pub.dev"
source: hosted
version: "1.3.3"
source_span:
dependency: transitive
description:
name: source_span
url: "https://pub.dartlang.org"
sha256: dd904f795d4b4f3b870833847c461801f6750a9fa8e61ea5ac53f9422b31f250
url: "https://pub.dev"
source: hosted
version: "1.9.0"
version: "1.9.1"
stack_trace:
dependency: transitive
description:
name: stack_trace
url: "https://pub.dartlang.org"
sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5
url: "https://pub.dev"
source: hosted
version: "1.10.0"
version: "1.11.0"
stream_channel:
dependency: transitive
description:
name: stream_channel
url: "https://pub.dartlang.org"
sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8"
url: "https://pub.dev"
source: hosted
version: "2.1.0"
version: "2.1.1"
stream_transform:
dependency: transitive
description:
name: stream_transform
url: "https://pub.dartlang.org"
sha256: "14a00e794c7c11aa145a170587321aedce29769c08d7f58b1d141da75e3b1c6f"
url: "https://pub.dev"
source: hosted
version: "2.1.0"
string_scanner:
dependency: transitive
description:
name: string_scanner
url: "https://pub.dartlang.org"
sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde"
url: "https://pub.dev"
source: hosted
version: "1.1.1"
version: "1.2.0"
term_glyph:
dependency: transitive
description:
name: term_glyph
url: "https://pub.dartlang.org"
sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84
url: "https://pub.dev"
source: hosted
version: "1.2.1"
test_api:
dependency: transitive
description:
name: test_api
url: "https://pub.dartlang.org"
sha256: eb6ac1540b26de412b3403a163d919ba86f6a973fe6cc50ae3541b80092fdcfb
url: "https://pub.dev"
source: hosted
version: "0.4.12"
version: "0.5.1"
timing:
dependency: transitive
description:
name: timing
url: "https://pub.dartlang.org"
sha256: "70a3b636575d4163c477e6de42f247a23b315ae20e86442bebe32d3cabf61c32"
url: "https://pub.dev"
source: hosted
version: "1.0.1"
typed_data:
dependency: transitive
description:
name: typed_data
url: "https://pub.dartlang.org"
sha256: "26f87ade979c47a150c9eaab93ccd2bebe70a27dc0b4b29517f2904f04eb11a5"
url: "https://pub.dev"
source: hosted
version: "1.3.1"
vector_math:
dependency: transitive
description:
name: vector_math
url: "https://pub.dartlang.org"
sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803"
url: "https://pub.dev"
source: hosted
version: "2.1.2"
version: "2.1.4"
watcher:
dependency: transitive
description:
name: watcher
url: "https://pub.dartlang.org"
sha256: "6a7f46926b01ce81bfc339da6a7f20afbe7733eff9846f6d6a5466aa4c6667c0"
url: "https://pub.dev"
source: hosted
version: "1.0.2"
web_socket_channel:
dependency: transitive
description:
name: web_socket_channel
url: "https://pub.dartlang.org"
sha256: ca49c0bc209c687b887f30527fb6a9d80040b072cc2990f34b9bec3e7663101b
url: "https://pub.dev"
source: hosted
version: "2.3.0"
win32:
dependency: transitive
description:
name: win32
url: "https://pub.dartlang.org"
sha256: c9ebe7ee4ab0c2194e65d3a07d8c54c5d00bb001b76081c4a04cdb8448b59e46
url: "https://pub.dev"
source: hosted
version: "3.1.3"
xdg_directories:
dependency: transitive
description:
name: xdg_directories
url: "https://pub.dartlang.org"
sha256: bd512f03919aac5f1313eb8249f223bacf4927031bf60b02601f81f687689e86
url: "https://pub.dev"
source: hosted
version: "1.0.0"
version: "0.2.0+3"
yaml:
dependency: transitive
description:
name: yaml
url: "https://pub.dartlang.org"
sha256: "23812a9b125b48d4007117254bca50abb6c712352927eece9e155207b1db2370"
url: "https://pub.dev"
source: hosted
version: "3.1.1"
sdks:
dart: ">=2.19.0 <4.0.0"
dart: ">=3.0.0 <4.0.0"
flutter: ">=3.0.0"

View file

@ -12,12 +12,12 @@ environment:
dependencies:
flutter:
sdk: flutter
http: ^0.13.4
http: ^1.1.0
file: ^6.1.4
path_provider: ^2.0.11
mobx: ^2.0.7+4
flutter_mobx: ^2.0.6+1
intl: ^0.17.0
intl: ^0.18.0
encrypt: ^5.0.1
dev_dependencies:

30
cw_ethereum/.gitignore vendored Normal file
View 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
View 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
View file

@ -0,0 +1,3 @@
## 0.0.1
* TODO: Describe initial release.

1
cw_ethereum/LICENSE Normal file
View file

@ -0,0 +1 @@
TODO: Add your license here.

39
cw_ethereum/README.md Normal file
View 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.

View 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

View file

@ -0,0 +1,7 @@
library cw_ethereum;
/// A Calculator.
class Calculator {
/// Returns [value] plus 1.
int addOne(int value) => value + 1;
}

View 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();
}

View 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);
}
}
}

View 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;
// }
}

View 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;
}

View 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;
}
}
}

File diff suppressed because it is too large Load diff

View 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;
}

View 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;
}

View 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,
};
}

View 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",
);
}

View 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;
}
}

View 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();
}
}
}

View 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());
}
}
}

View file

@ -0,0 +1,29 @@
import 'package:cw_core/wallet_credentials.dart';
import 'package:cw_core/wallet_info.dart';
class EthereumNewWalletCredentials extends WalletCredentials {
EthereumNewWalletCredentials({required String name, WalletInfo? walletInfo})
: super(name: name, walletInfo: walletInfo);
}
class EthereumRestoreWalletFromSeedCredentials extends WalletCredentials {
EthereumRestoreWalletFromSeedCredentials(
{required String name,
required String password,
required this.mnemonic,
WalletInfo? walletInfo})
: super(name: name, password: password, walletInfo: walletInfo);
final String mnemonic;
}
class EthereumRestoreWalletFromPrivateKey extends WalletCredentials {
EthereumRestoreWalletFromPrivateKey(
{required String name,
required String password,
required this.privateKey,
WalletInfo? walletInfo})
: super(name: name, password: password, walletInfo: walletInfo);
final String privateKey;
}

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