From ae9efb0ae8ae35e0c33bcfe7ba3473ddc9b4d0ae Mon Sep 17 00:00:00 2001 From: OleksandrSobol <dr.alexander.sobol@gmail.com> Date: Tue, 20 Apr 2021 20:49:53 +0300 Subject: [PATCH 01/28] CAKE-192 | implemented unstoppable domain address to the app --- android/app/build.gradle | 5 +- .../cakewallet/cake_wallet/MainActivity.java | 46 ++++++++++- android/build.gradle | 2 +- .../gradle/wrapper/gradle-wrapper.properties | 4 +- ios/Podfile | 3 +- ios/Podfile.lock | 18 ++++- ios/Runner/AppDelegate.swift | 40 +++++++++ lib/entities/unstoppable_domain_address.dart | 22 +++++ lib/src/screens/exchange/exchange_page.dart | 81 +++++++++++++++++-- .../exchange/exchange_template_page.dart | 11 ++- .../exchange/widgets/exchange_card.dart | 10 ++- lib/src/screens/send/send_page.dart | 34 ++++++-- lib/src/screens/send/send_template_page.dart | 4 +- .../unstoppable_domain_address_alert.dart | 17 ++++ lib/src/widgets/address_text_field.dart | 6 +- .../exchange/exchange_view_model.dart | 5 ++ lib/view_model/send/send_view_model.dart | 5 ++ res/values/strings_de.arb | 5 +- res/values/strings_en.arb | 5 +- res/values/strings_es.arb | 5 +- res/values/strings_hi.arb | 5 +- res/values/strings_ja.arb | 5 +- res/values/strings_ko.arb | 5 +- res/values/strings_nl.arb | 5 +- res/values/strings_pl.arb | 5 +- res/values/strings_pt.arb | 5 +- res/values/strings_ru.arb | 5 +- res/values/strings_uk.arb | 5 +- res/values/strings_zh.arb | 5 +- 29 files changed, 327 insertions(+), 46 deletions(-) create mode 100644 lib/entities/unstoppable_domain_address.dart create mode 100644 lib/src/screens/send/widgets/unstoppable_domain_address_alert.dart diff --git a/android/app/build.gradle b/android/app/build.gradle index 0c6a46dc4..cef66608c 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -81,6 +81,7 @@ flutter { dependencies { testImplementation 'junit:junit:4.12' - androidTestImplementation 'androidx.test:runner:1.1.1' - androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1' + androidTestImplementation 'androidx.test:runner:1.3.0' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0' + implementation 'com.unstoppabledomains:resolution:1.13.0' } diff --git a/android/app/src/main/java/com/cakewallet/cake_wallet/MainActivity.java b/android/app/src/main/java/com/cakewallet/cake_wallet/MainActivity.java index cf650375f..7efe1606c 100644 --- a/android/app/src/main/java/com/cakewallet/cake_wallet/MainActivity.java +++ b/android/app/src/main/java/com/cakewallet/cake_wallet/MainActivity.java @@ -5,11 +5,55 @@ import io.flutter.embedding.android.FlutterFragmentActivity; import io.flutter.embedding.engine.FlutterEngine; import io.flutter.plugins.GeneratedPluginRegistrant; +import com.unstoppabledomains.resolution.DomainResolution; +import com.unstoppabledomains.resolution.Resolution; + +import android.os.AsyncTask; +import android.os.Handler; +import android.os.Looper; + +import io.flutter.plugin.common.MethodCall; +import io.flutter.plugin.common.MethodChannel; public class MainActivity extends FlutterFragmentActivity { - + final String UNSTOPPABLE_DOMAIN_CHANNEL = "com.cakewallet.cake_wallet/unstoppable-domain"; + @Override public void configureFlutterEngine(@NonNull FlutterEngine flutterEngine) { GeneratedPluginRegistrant.registerWith(flutterEngine); + + MethodChannel unstoppableDomainChannel = + new MethodChannel(flutterEngine.getDartExecutor().getBinaryMessenger(), + UNSTOPPABLE_DOMAIN_CHANNEL); + + unstoppableDomainChannel.setMethodCallHandler(this::handle); + } + + private void handle(@NonNull MethodCall call, @NonNull MethodChannel.Result result) { + try { + if (call.method.equals("getUnstoppableDomainAddress")) { + getUnstoppableDomainAddress(call, result); + } else { + result.notImplemented(); + } + } catch (Exception e) { + result.error("UNCAUGHT_ERROR", e.getMessage(), null); + } + } + + private void getUnstoppableDomainAddress(@NonNull MethodCall call, @NonNull MethodChannel.Result result) { + DomainResolution resolution = new Resolution(); + Handler handler = new Handler(Looper.getMainLooper()); + String domain = call.argument("domain"); + String ticker = call.argument("ticker"); + + AsyncTask.execute(() -> { + try { + String address = resolution.getAddress(domain, ticker); + handler.post(() -> result.success(address)); + } catch (Exception e) { + handler.post(() -> result.error("INVALID DOMAIN", e.getMessage(), null)); + } + }); } } diff --git a/android/build.gradle b/android/build.gradle index 0baed694d..fab3c2e17 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -5,7 +5,7 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:3.5.4' + classpath 'com.android.tools.build:gradle:4.1.3' } } diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties index 296b146b7..b7ca2e6de 100644 --- a/android/gradle/wrapper/gradle-wrapper.properties +++ b/android/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Fri Jun 23 08:50:38 CEST 2017 +#Mon Apr 19 18:19:26 EEST 2021 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-bin.zip diff --git a/ios/Podfile b/ios/Podfile index e26de900c..8248dde48 100644 --- a/ios/Podfile +++ b/ios/Podfile @@ -1,5 +1,5 @@ # Uncomment this line to define a global platform for your project -platform :ios, '9.0' +platform :ios, '11.0' source 'https://github.com/CocoaPods/Specs.git' # CocoaPods analytics sends network stats synchronously affecting flutter build latency. @@ -36,6 +36,7 @@ target 'Runner' do # Cake Wallet (Legacy) pod 'CryptoSwift' + pod 'UnstoppableDomainsResolution', '~> 0.3.6' end post_install do |installer| diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 03302eab3..f627ed23f 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -3,6 +3,7 @@ PODS: - Flutter - MTBBarcodeScanner - SwiftProtobuf + - BigInt (5.2.0) - connectivity (0.0.1): - Flutter - Reachability @@ -59,6 +60,8 @@ PODS: - SwiftyGif - esys_flutter_share (0.0.1): - Flutter + - EthereumAddress (1.3.0): + - CryptoSwift (~> 1.0) - file_picker (0.0.1): - DKImagePickerController/PhotoGallery - Flutter @@ -84,6 +87,10 @@ PODS: - Flutter - SwiftProtobuf (1.12.0) - SwiftyGif (5.3.0) + - UnstoppableDomainsResolution (0.3.6): + - BigInt + - CryptoSwift (~> 1.0) + - EthereumAddress (~> 1.3) - url_launcher (0.0.1): - Flutter - webview_flutter (0.0.1): @@ -105,19 +112,23 @@ DEPENDENCIES: - permission_handler (from `.symlinks/plugins/permission_handler/ios`) - share (from `.symlinks/plugins/share/ios`) - shared_preferences (from `.symlinks/plugins/shared_preferences/ios`) + - UnstoppableDomainsResolution (~> 0.3.6) - url_launcher (from `.symlinks/plugins/url_launcher/ios`) - webview_flutter (from `.symlinks/plugins/webview_flutter/ios`) SPEC REPOS: https://github.com/CocoaPods/Specs.git: + - BigInt - CryptoSwift - DKImagePickerController - DKPhotoGallery + - EthereumAddress - MTBBarcodeScanner - Reachability - SDWebImage - SwiftProtobuf - SwiftyGif + - UnstoppableDomainsResolution EXTERNAL SOURCES: barcode_scan: @@ -155,6 +166,7 @@ EXTERNAL SOURCES: SPEC CHECKSUMS: barcode_scan: a5c27959edfafaa0c771905bad0b29d6d39e4479 + BigInt: f668a80089607f521586bbe29513d708491ef2f7 connectivity: c4130b2985d4ef6fd26f9702e886bd5260681467 CryptoSwift: 093499be1a94b0cae36e6c26b70870668cb56060 cw_monero: 2e1f79929880cc2293b5bc1b25e28152e4d84649 @@ -162,6 +174,7 @@ SPEC CHECKSUMS: DKImagePickerController: b5eb7f7a388e4643264105d648d01f727110fc3d DKPhotoGallery: fdfad5125a9fdda9cc57df834d49df790dbb4179 esys_flutter_share: 403498dab005b36ce1f8d7aff377e81f0621b0b4 + EthereumAddress: 39fe8e11cf04e4e9902b55ae653dbc4e0aee5f30 file_picker: 3e6c3790de664ccf9b882732d9db5eaf6b8d4eb1 Flutter: 434fef37c0980e73bb6479ef766c45957d4b510c flutter_secure_storage: 7953c38a04c3fdbb00571bcd87d8e3b5ceb9daec @@ -176,9 +189,10 @@ SPEC CHECKSUMS: shared_preferences: af6bfa751691cdc24be3045c43ec037377ada40d SwiftProtobuf: 4ef85479c18ca85b5482b343df9c319c62bda699 SwiftyGif: e466e86c660d343357ab944a819a101c4127cb40 + UnstoppableDomainsResolution: 63abb84858d3e91eb838a5bfa6f7e3c0e0593f24 url_launcher: 6fef411d543ceb26efce54b05a0a40bfd74cbbef webview_flutter: 9f491a9b5a66f2573946a389b2677987b0ff8c0b -PODFILE CHECKSUM: 5b5f101b119a1b6eb857c967d462832a9062dec4 +PODFILE CHECKSUM: 2172f10eb8ff8378f8d6e3e2c1366a0e6cdea018 -COCOAPODS: 1.9.3 +COCOAPODS: 1.10.1 diff --git a/ios/Runner/AppDelegate.swift b/ios/Runner/AppDelegate.swift index 01aa14195..8b6264a04 100644 --- a/ios/Runner/AppDelegate.swift +++ b/ios/Runner/AppDelegate.swift @@ -1,8 +1,13 @@ import UIKit import Flutter +import UnstoppableDomainsResolution @UIApplicationMain @objc class AppDelegate: FlutterAppDelegate { + lazy var resolution : Resolution? = { + return try? Resolution() + }() + override func application( _ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? @@ -10,6 +15,8 @@ import Flutter let controller : FlutterViewController = window?.rootViewController as! FlutterViewController let batteryChannel = FlutterMethodChannel(name: "com.cakewallet.cakewallet/legacy_wallet_migration", binaryMessenger: controller.binaryMessenger) + let unstoppableDomainChannel = FlutterMethodChannel(name: "com.cakewallet.cake_wallet/unstoppable-domain", binaryMessenger: controller.binaryMessenger) + batteryChannel.setMethodCallHandler({ (call: FlutterMethodCall, result: @escaping FlutterResult) -> Void in @@ -52,6 +59,39 @@ import Flutter } }) + unstoppableDomainChannel.setMethodCallHandler({ [weak self] + (call: FlutterMethodCall, result: @escaping FlutterResult) -> Void in + switch call.method { + case "getUnstoppableDomainAddress": + guard let args = call.arguments as? Dictionary<String, String>, + let domain = args["domain"], + let ticker = args["ticker"] else { + result(nil) + return + } + + guard let resolution = self?.resolution else { + result(nil) + return + } + + resolution.addr(domain: domain, ticker: ticker) { addrResult in + var address : String = "" + + switch addrResult { + case .success(let returnValue): + address = returnValue + case .failure(let error): + print("Expected Address, but got \(error)") + } + + result(address) + } + default: + result(FlutterMethodNotImplemented) + } + }) + GeneratedPluginRegistrant.register(with: self) return super.application(application, didFinishLaunchingWithOptions: launchOptions) } diff --git a/lib/entities/unstoppable_domain_address.dart b/lib/entities/unstoppable_domain_address.dart new file mode 100644 index 000000000..494782d54 --- /dev/null +++ b/lib/entities/unstoppable_domain_address.dart @@ -0,0 +1,22 @@ +import 'package:flutter/services.dart'; + +const channel = MethodChannel('com.cakewallet.cake_wallet/unstoppable-domain'); + +Future<String> fetchUnstoppableDomainAddress(String domain, String ticker) async { + String address; + + try { + address = await channel.invokeMethod( + 'getUnstoppableDomainAddress', + <String, String> { + 'domain' : domain, + 'ticker' : ticker + } + ); + } catch (e) { + print('Unstoppable domain error: ${e.toString()}'); + address = ''; + } + + return address; +} \ No newline at end of file diff --git a/lib/src/screens/exchange/exchange_page.dart b/lib/src/screens/exchange/exchange_page.dart index 82d7dd80e..853ff9876 100644 --- a/lib/src/screens/exchange/exchange_page.dart +++ b/lib/src/screens/exchange/exchange_page.dart @@ -1,7 +1,7 @@ import 'dart:ui'; import 'package:cake_wallet/entities/sync_status.dart'; import 'package:cake_wallet/entities/wallet_type.dart'; -import 'package:cake_wallet/exchange/changenow/changenow_exchange_provider.dart'; +import 'package:cake_wallet/src/screens/send/widgets/unstoppable_domain_address_alert.dart'; import 'package:cake_wallet/src/widgets/standard_checkbox.dart'; import 'package:dotted_border/dotted_border.dart'; import 'package:flutter/cupertino.dart'; @@ -9,8 +9,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_mobx/flutter_mobx.dart'; import 'package:keyboard_actions/keyboard_actions.dart'; import 'package:mobx/mobx.dart'; -import 'package:cake_wallet/exchange/exchange_provider.dart'; -import 'package:cake_wallet/core/execution_state.dart'; import 'package:cake_wallet/exchange/exchange_template.dart'; import 'package:cake_wallet/exchange/exchange_trade_state.dart'; import 'package:cake_wallet/exchange/limits_state.dart'; @@ -23,7 +21,6 @@ import 'package:cake_wallet/utils/show_pop_up.dart'; import 'package:cake_wallet/routes.dart'; import 'package:cake_wallet/generated/i18n.dart'; import 'package:cake_wallet/entities/crypto_currency.dart'; -import 'package:cake_wallet/exchange/xmrto/xmrto_exchange_provider.dart'; import 'package:cake_wallet/src/screens/exchange/widgets/exchange_card.dart'; import 'package:cake_wallet/src/widgets/primary_button.dart'; import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart'; @@ -42,7 +39,9 @@ class ExchangePage extends BasePage { final checkBoxKey = GlobalKey<StandardCheckboxState>(); final _formKey = GlobalKey<FormState>(); final _depositAmountFocus = FocusNode(); + final _depositAddressFocus = FocusNode(); final _receiveAmountFocus = FocusNode(); + final _receiveAddressFocus = FocusNode(); var _isReactionsSet = false; @override @@ -52,7 +51,7 @@ class ExchangePage extends BasePage { Color get titleColor => Colors.white; @override - bool get resizeToAvoidBottomPadding => false; + bool get resizeToAvoidBottomInset => false; @override bool get extendBodyBehindAppBar => true; @@ -171,6 +170,7 @@ class ExchangePage extends BasePage { .calculateDepositAllAmount() : null, amountFocusNode: _depositAmountFocus, + addressFocusNode: _depositAddressFocus, key: depositKey, title: S.of(context).you_will_send, initialCurrency: @@ -223,6 +223,15 @@ class ExchangePage extends BasePage { type: exchangeViewModel.wallet.type), addressTextFieldValidator: AddressValidator( type: exchangeViewModel.depositCurrency), + onPushPasteButton: (context) async { + final domain = + exchangeViewModel.depositAddress; + final ticker = + exchangeViewModel.depositCurrency.title; + exchangeViewModel.depositAddress = + await applyUnstoppableDomainAddress( + context, domain, ticker); + }, ), ), ), @@ -232,6 +241,7 @@ class ExchangePage extends BasePage { child: Observer( builder: (_) => ExchangeCard( amountFocusNode: _receiveAmountFocus, + addressFocusNode: _receiveAddressFocus, key: receiveKey, title: S.of(context).you_will_get, initialCurrency: @@ -268,6 +278,15 @@ class ExchangePage extends BasePage { AddressValidator( type: exchangeViewModel .receiveCurrency), + onPushPasteButton: (context) async { + final domain = + exchangeViewModel.receiveAddress; + final ticker = + exchangeViewModel.receiveCurrency.title; + exchangeViewModel.receiveAddress = + await applyUnstoppableDomainAddress( + context, domain, ticker); + }, )), ) ], @@ -371,7 +390,7 @@ class ExchangePage extends BasePage { from: template.depositCurrency, to: template.receiveCurrency, onTap: () { - applyTemplate( + applyTemplate(context, exchangeViewModel, template); }, onRemove: () { @@ -472,8 +491,8 @@ class ExchangePage extends BasePage { )); } - void applyTemplate( - ExchangeViewModel exchangeViewModel, ExchangeTemplate template) { + void applyTemplate(BuildContext context, + ExchangeViewModel exchangeViewModel, ExchangeTemplate template) async { exchangeViewModel.changeDepositCurrency( currency: CryptoCurrency.fromString(template.depositCurrency)); exchangeViewModel.changeReceiveCurrency( @@ -491,6 +510,16 @@ class ExchangePage extends BasePage { exchangeViewModel.receiveAddress = template.receiveAddress; exchangeViewModel.isReceiveAmountEntered = false; exchangeViewModel.isFixedRateMode = false; + + var domain = template.depositAddress; + var ticker = template.depositCurrency; + exchangeViewModel.depositAddress = + await applyUnstoppableDomainAddress(context, domain, ticker); + + domain = template.receiveAddress; + ticker = template.receiveCurrency; + exchangeViewModel.receiveAddress = + await applyUnstoppableDomainAddress(context, domain, ticker); } void _setReactions( @@ -656,6 +685,26 @@ class ExchangePage extends BasePage { } }); + _depositAddressFocus.addListener(() async { + if (!_depositAddressFocus.hasFocus && + depositAddressController.text.isNotEmpty) { + final domain = depositAddressController.text; + final ticker = exchangeViewModel.depositCurrency.title; + exchangeViewModel.depositAddress = + await applyUnstoppableDomainAddress(context, domain, ticker); + } + }); + + _receiveAddressFocus.addListener(() async { + if (!_receiveAddressFocus.hasFocus && + receiveAddressController.text.isNotEmpty) { + final domain = receiveAddressController.text; + final ticker = exchangeViewModel.receiveCurrency.title; + exchangeViewModel.receiveAddress = + await applyUnstoppableDomainAddress(context, domain, ticker); + } + }); + _receiveAmountFocus.addListener(() { if (_receiveAmountFocus.hasFocus && !exchangeViewModel.isFixedRateMode) { showPopUp<void>( @@ -726,4 +775,20 @@ class ExchangePage extends BasePage { key.currentState.addressController.text = null; } } + + Future<String> applyUnstoppableDomainAddress(BuildContext context, + String domain, String ticker) async { + try { + final address = + await exchangeViewModel.getUnstoppableDomainAddress(domain, ticker); + if ((address != null)&&address.isNotEmpty) { + unstoppableDomainAddressAlert(context, domain); + return address; + } + } catch (e) { + print(e.toString()); + } + + return domain; + } } diff --git a/lib/src/screens/exchange/exchange_template_page.dart b/lib/src/screens/exchange/exchange_template_page.dart index 5437703ce..1be8d1d6d 100644 --- a/lib/src/screens/exchange/exchange_template_page.dart +++ b/lib/src/screens/exchange/exchange_template_page.dart @@ -37,7 +37,7 @@ class ExchangeTemplatePage extends BasePage { Color get titleColor => Colors.white; @override - bool get resizeToAvoidBottomPadding => false; + bool get resizeToAvoidBottomInset => false; @override bool get extendBodyBehindAppBar => true; @@ -150,9 +150,8 @@ class ExchangeTemplatePage extends BasePage { .color, currencyValueValidator: AmountValidator( type: exchangeViewModel.wallet.type), - addressTextFieldValidator: AddressValidator( - type: - exchangeViewModel.depositCurrency), + //addressTextFieldValidator: AddressValidator( + // type: exchangeViewModel.depositCurrency), ), ), ), @@ -190,8 +189,8 @@ class ExchangeTemplatePage extends BasePage { .decorationColor, currencyValueValidator: AmountValidator( type: exchangeViewModel.wallet.type), - addressTextFieldValidator: AddressValidator( - type: exchangeViewModel.receiveCurrency), + //addressTextFieldValidator: AddressValidator( + // type: exchangeViewModel.receiveCurrency), )), ) ], diff --git a/lib/src/screens/exchange/widgets/exchange_card.dart b/lib/src/screens/exchange/widgets/exchange_card.dart index 7af57e28b..f534cccad 100644 --- a/lib/src/screens/exchange/widgets/exchange_card.dart +++ b/lib/src/screens/exchange/widgets/exchange_card.dart @@ -28,8 +28,10 @@ class ExchangeCard extends StatefulWidget { this.currencyValueValidator, this.addressTextFieldValidator, this.amountFocusNode, + this.addressFocusNode, this.hasAllAmount = false, - this.allAmount}) + this.allAmount, + this.onPushPasteButton}) : super(key: key); final List<CryptoCurrency> currencies; @@ -49,8 +51,10 @@ class ExchangeCard extends StatefulWidget { final FormFieldValidator<String> currencyValueValidator; final FormFieldValidator<String> addressTextFieldValidator; final FocusNode amountFocusNode; + final FocusNode addressFocusNode; final bool hasAllAmount; - Function allAmount; + final Function allAmount; + final Function(BuildContext context) onPushPasteButton; @override ExchangeCardState createState() => ExchangeCardState(); @@ -288,6 +292,7 @@ class ExchangeCardState extends State<ExchangeCard> { ? Padding( padding: EdgeInsets.only(top: 20), child: AddressTextField( + focusNode: widget.addressFocusNode, controller: addressController, placeholder: widget.hasRefundAddress ? S.of(context).refund_address @@ -311,6 +316,7 @@ class ExchangeCardState extends State<ExchangeCard> { .decorationColor), buttonColor: widget.addressButtonsColor, validator: widget.addressTextFieldValidator, + onPushPasteButton: widget.onPushPasteButton, ), ) : Padding( diff --git a/lib/src/screens/send/send_page.dart b/lib/src/screens/send/send_page.dart index 9fae0ec23..e40657b1d 100644 --- a/lib/src/screens/send/send_page.dart +++ b/lib/src/screens/send/send_page.dart @@ -1,6 +1,6 @@ import 'dart:ui'; -import 'package:cake_wallet/entities/monero_transaction_priority.dart'; import 'package:cake_wallet/entities/transaction_priority.dart'; +import 'package:cake_wallet/src/screens/send/widgets/unstoppable_domain_address_alert.dart'; import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart'; import 'package:cake_wallet/src/widgets/keyboard_done_button.dart'; import 'package:cake_wallet/src/widgets/picker.dart'; @@ -38,9 +38,10 @@ class SendPage extends BasePage { _cryptoAmountFocus = FocusNode(), _fiatAmountFocus = FocusNode(), _addressFocusNode = FocusNode() { - _addressFocusNode.addListener(() { + _addressFocusNode.addListener(() async { if (!_addressFocusNode.hasFocus && _addressController.text.isNotEmpty) { - getOpenaliasRecord(_addressFocusNode.context); + await getOpenaliasRecord(_addressFocusNode.context); + await applyUnstoppableDomainAddress(_addressFocusNode.context); } }); } @@ -67,7 +68,7 @@ class SendPage extends BasePage { Color get titleColor => Colors.white; @override - bool get resizeToAvoidBottomPadding => false; + bool get resizeToAvoidBottomInset => false; @override bool get extendBodyBehindAppBar => true; @@ -173,6 +174,10 @@ class SendPage extends BasePage { .headline .decorationColor), validator: sendViewModel.addressValidator, + onPushPasteButton: (context) async { + await getOpenaliasRecord(context); + await applyUnstoppableDomainAddress(context); + }, ), Observer( builder: (_) => Padding( @@ -513,12 +518,13 @@ class SendPage extends BasePage { to: template.name, amount: template.amount, from: template.cryptoCurrency, - onTap: () { + onTap: () async { _addressController.text = template.address; _cryptoAmountController.text = template.amount; - getOpenaliasRecord(context); + await getOpenaliasRecord(context); + await applyUnstoppableDomainAddress(context); }, onRemove: () { showPopUp<void>( @@ -770,4 +776,20 @@ class SendPage extends BasePage { ), context: context); } + + Future<void> applyUnstoppableDomainAddress(BuildContext context) async { + try { + final address = await sendViewModel + .getUnstoppableDomainAddress( + _addressController.text); + + if ((address != null)&&address.isNotEmpty) { + unstoppableDomainAddressAlert( + context, _addressController.text); + _addressController.text = address; + } + } catch (e) { + print(e.toString()); + } + } } diff --git a/lib/src/screens/send/send_template_page.dart b/lib/src/screens/send/send_template_page.dart index 6d3ac7132..3880bd2fb 100644 --- a/lib/src/screens/send/send_template_page.dart +++ b/lib/src/screens/send/send_template_page.dart @@ -34,7 +34,7 @@ class SendTemplatePage extends BasePage { Color get titleColor => Colors.white; @override - bool get resizeToAvoidBottomPadding => false; + bool get resizeToAvoidBottomInset => false; @override bool get extendBodyBehindAppBar => true; @@ -145,7 +145,7 @@ class SendTemplatePage extends BasePage { .primaryTextTheme .headline .decorationColor), - validator: sendViewModel.addressValidator, + //validator: sendViewModel.addressValidator, ), ), Observer(builder: (_) { diff --git a/lib/src/screens/send/widgets/unstoppable_domain_address_alert.dart b/lib/src/screens/send/widgets/unstoppable_domain_address_alert.dart new file mode 100644 index 000000000..011a36794 --- /dev/null +++ b/lib/src/screens/send/widgets/unstoppable_domain_address_alert.dart @@ -0,0 +1,17 @@ +import 'package:cake_wallet/src/widgets/alert_with_one_action.dart'; +import 'package:cake_wallet/utils/show_pop_up.dart'; +import 'package:flutter/material.dart'; +import 'package:cake_wallet/generated/i18n.dart'; + +void unstoppableDomainAddressAlert(BuildContext context, String domain) async { + await showPopUp<void>( + context: context, + builder: (BuildContext context) { + + return AlertWithOneAction( + alertTitle: S.of(context).address_detected, + alertContent: S.of(context).address_from_domain(domain), + buttonText: S.of(context).ok, + buttonAction: () => Navigator.of(context).pop()); + }); +} \ No newline at end of file diff --git a/lib/src/widgets/address_text_field.dart b/lib/src/widgets/address_text_field.dart index 8e2c069f1..130912a2f 100644 --- a/lib/src/widgets/address_text_field.dart +++ b/lib/src/widgets/address_text_field.dart @@ -24,7 +24,8 @@ class AddressTextField extends StatelessWidget { this.iconColor, this.textStyle, this.hintStyle, - this.validator}); + this.validator, + this.onPushPasteButton}); static const prefixIconWidth = 34.0; static const prefixIconHeight = 34.0; @@ -43,6 +44,7 @@ class AddressTextField extends StatelessWidget { final TextStyle textStyle; final TextStyle hintStyle; final FocusNode focusNode; + final Function(BuildContext context) onPushPasteButton; @override Widget build(BuildContext context) { @@ -225,5 +227,7 @@ class AddressTextField extends StatelessWidget { if (address?.isNotEmpty ?? false) { controller.text = address; } + + onPushPasteButton?.call(context); } } diff --git a/lib/view_model/exchange/exchange_view_model.dart b/lib/view_model/exchange/exchange_view_model.dart index fc860e011..2bcec3565 100644 --- a/lib/view_model/exchange/exchange_view_model.dart +++ b/lib/view_model/exchange/exchange_view_model.dart @@ -4,6 +4,7 @@ import 'package:cake_wallet/bitcoin/bitcoin_wallet.dart'; import 'package:cake_wallet/core/wallet_base.dart'; import 'package:cake_wallet/entities/crypto_currency.dart'; import 'package:cake_wallet/entities/sync_status.dart'; +import 'package:cake_wallet/entities/unstoppable_domain_address.dart'; import 'package:cake_wallet/entities/wallet_type.dart'; import 'package:cake_wallet/exchange/exchange_provider.dart'; import 'package:cake_wallet/exchange/limits.dart'; @@ -417,4 +418,8 @@ abstract class ExchangeViewModelBase with Store { }*/ isReceiveAmountEditable = false; } + + Future<String> getUnstoppableDomainAddress(String domain, String ticker) async { + return await fetchUnstoppableDomainAddress(domain, ticker.toLowerCase()); + } } diff --git a/lib/view_model/send/send_view_model.dart b/lib/view_model/send/send_view_model.dart index b92672ff8..f36261f73 100644 --- a/lib/view_model/send/send_view_model.dart +++ b/lib/view_model/send/send_view_model.dart @@ -4,6 +4,7 @@ import 'package:cake_wallet/entities/balance_display_mode.dart'; import 'package:cake_wallet/entities/calculate_fiat_amount_raw.dart'; import 'package:cake_wallet/entities/transaction_description.dart'; import 'package:cake_wallet/entities/transaction_priority.dart'; +import 'package:cake_wallet/entities/unstoppable_domain_address.dart'; import 'package:cake_wallet/monero/monero_amount_format.dart'; import 'package:cake_wallet/view_model/settings/settings_view_model.dart'; import 'package:hive/hive.dart'; @@ -267,6 +268,10 @@ abstract class SendViewModelBase with Store { return record.name != name ? record : null; } + Future<String> getUnstoppableDomainAddress(String domain) async { + return await fetchUnstoppableDomainAddress(domain, currency.title.toLowerCase()); + } + @action void _updateFiatAmount() { try { diff --git a/res/values/strings_de.arb b/res/values/strings_de.arb index 6d1d66b84..34cef6078 100644 --- a/res/values/strings_de.arb +++ b/res/values/strings_de.arb @@ -469,5 +469,8 @@ "unconfirmed" : "Unbestätigt", "displayable" : "Anzeigebar", - "submit_request" : "Einen Antrag stellen" + "submit_request" : "Einen Antrag stellen", + + "address_detected" : "Adresse erkannt", + "address_from_domain" : "Sie haben die Adresse von der unaufhaltsamen Domain ${domain} erhalten" } \ No newline at end of file diff --git a/res/values/strings_en.arb b/res/values/strings_en.arb index b0d98b7c9..a641fabf1 100644 --- a/res/values/strings_en.arb +++ b/res/values/strings_en.arb @@ -469,5 +469,8 @@ "unconfirmed" : "Unconfirmed", "displayable" : "Displayable", - "submit_request" : "submit a request" + "submit_request" : "submit a request", + + "address_detected" : "Address detected", + "address_from_domain" : "You got address from unstoppable domain ${domain}" } \ No newline at end of file diff --git a/res/values/strings_es.arb b/res/values/strings_es.arb index 190ad4304..95a34e230 100644 --- a/res/values/strings_es.arb +++ b/res/values/strings_es.arb @@ -469,5 +469,8 @@ "unconfirmed" : "Inconfirmado", "displayable" : "Visualizable", - "submit_request" : "presentar una solicitud" + "submit_request" : "presentar una solicitud", + + "address_detected" : "Dirección detectada", + "address_from_domain" : "Tienes la dirección de unstoppable domain ${domain}" } \ No newline at end of file diff --git a/res/values/strings_hi.arb b/res/values/strings_hi.arb index 6eac14e99..0db4e7216 100644 --- a/res/values/strings_hi.arb +++ b/res/values/strings_hi.arb @@ -469,5 +469,8 @@ "unconfirmed" : "अपुष्ट", "displayable" : "प्रदर्शन योग्य", - "submit_request" : "एक अनुरोध सबमिट करें" + "submit_request" : "एक अनुरोध सबमिट करें", + + "address_detected" : "पता लग गया", + "address_from_domain" : "आपको अजेय डोमेन ${domain} से पता मिला है" } \ No newline at end of file diff --git a/res/values/strings_ja.arb b/res/values/strings_ja.arb index 291d5afd3..89556602a 100644 --- a/res/values/strings_ja.arb +++ b/res/values/strings_ja.arb @@ -469,5 +469,8 @@ "unconfirmed" : "未確認", "displayable" : "表示可能", - "submit_request" : "リクエストを送信する" + "submit_request" : "リクエストを送信する", + + "address_detected" : "アドレスが検出されました", + "address_from_domain" : "あなたはからアドレスを得ました unstoppable domain ${domain}" } \ No newline at end of file diff --git a/res/values/strings_ko.arb b/res/values/strings_ko.arb index 781df8d8a..9433a4189 100644 --- a/res/values/strings_ko.arb +++ b/res/values/strings_ko.arb @@ -469,5 +469,8 @@ "unconfirmed" : "미확인", "displayable" : "표시 가능", - "submit_request" : "요청을 제출" + "submit_request" : "요청을 제출", + + "address_detected" : "주소 감지", + "address_from_domain" : "주소는 unstoppable domain ${domain}" } \ No newline at end of file diff --git a/res/values/strings_nl.arb b/res/values/strings_nl.arb index 9a6183efb..8b8bef453 100644 --- a/res/values/strings_nl.arb +++ b/res/values/strings_nl.arb @@ -469,5 +469,8 @@ "unconfirmed" : "Niet bevestigd", "displayable" : "Weer te geven", - "submit_request" : "een verzoek indienen" + "submit_request" : "een verzoek indienen", + + "address_detected" : "Adres gedetecteerd", + "address_from_domain" : "Je adres is van unstoppable domain ${domain}" } \ No newline at end of file diff --git a/res/values/strings_pl.arb b/res/values/strings_pl.arb index 6db66958e..b5291e227 100644 --- a/res/values/strings_pl.arb +++ b/res/values/strings_pl.arb @@ -469,5 +469,8 @@ "unconfirmed" : "Niepotwierdzony", "displayable" : "Wyświetlane", - "submit_request" : "złożyć wniosek" + "submit_request" : "złożyć wniosek", + + "address_detected" : "Wykryto adres", + "address_from_domain" : "Dostałeś adres od unstoppable domain ${domain}" } \ No newline at end of file diff --git a/res/values/strings_pt.arb b/res/values/strings_pt.arb index 631be8f42..8e955310f 100644 --- a/res/values/strings_pt.arb +++ b/res/values/strings_pt.arb @@ -469,5 +469,8 @@ "unconfirmed" : "Não confirmado", "displayable" : "Exibível", - "submit_request" : "enviar um pedido" + "submit_request" : "enviar um pedido", + + "address_detected" : "Endereço detectado", + "address_from_domain" : "Você obteve o endereço de unstoppable domain ${domain}" } \ No newline at end of file diff --git a/res/values/strings_ru.arb b/res/values/strings_ru.arb index 333a823d8..142ae0dd1 100644 --- a/res/values/strings_ru.arb +++ b/res/values/strings_ru.arb @@ -469,5 +469,8 @@ "unconfirmed" : "Неподтвержденный", "displayable" : "Отображаемый", - "submit_request" : "отправить запрос" + "submit_request" : "отправить запрос", + + "address_detected" : "Обнаружен адрес", + "address_from_domain" : "Вы получили адрес из unstoppable domain ${domain}" } \ No newline at end of file diff --git a/res/values/strings_uk.arb b/res/values/strings_uk.arb index cc10a2d89..6c659bcea 100644 --- a/res/values/strings_uk.arb +++ b/res/values/strings_uk.arb @@ -469,5 +469,8 @@ "unconfirmed" : "Непідтверджений", "displayable" : "Відображуваний", - "submit_request" : "надіслати запит" + "submit_request" : "надіслати запит", + + "address_detected" : "Виявлено адресу", + "address_from_domain" : "Ви отримали адресу від unstoppable domain ${domain}" } \ No newline at end of file diff --git a/res/values/strings_zh.arb b/res/values/strings_zh.arb index 639babcbc..f1560941e 100644 --- a/res/values/strings_zh.arb +++ b/res/values/strings_zh.arb @@ -469,5 +469,8 @@ "unconfirmed" : "未经证实", "displayable" : "可显示", - "submit_request" : "提交請求" + "submit_request" : "提交請求", + + "address_detected" : "檢測到地址", + "address_from_domain" : "您有以下地址 unstoppable domain ${domain}" } \ No newline at end of file From 2e22dc814a5177838169a07657b812557515a08b Mon Sep 17 00:00:00 2001 From: OleksandrSobol <dr.alexander.sobol@gmail.com> Date: Wed, 21 Apr 2021 20:05:38 +0300 Subject: [PATCH 02/28] CAKE-192 | configured build.gradle --- android/app/build.gradle | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/android/app/build.gradle b/android/app/build.gradle index cef66608c..e8d8aa315 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -67,7 +67,8 @@ android { release { signingConfig signingConfigs.release - minifyEnabled true + shrinkResources false + minifyEnabled false useProguard true proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' From 9f1f4575ddaaa9db3f6ab70d887f8e69daf7fd5e Mon Sep 17 00:00:00 2001 From: OleksandrSobol <dr.alexander.sobol@gmail.com> Date: Thu, 22 Apr 2021 19:16:47 +0300 Subject: [PATCH 03/28] CAKE-192 | configured build.gradle --- android/app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android/app/build.gradle b/android/app/build.gradle index e8d8aa315..8c89aee52 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -69,7 +69,7 @@ android { shrinkResources false minifyEnabled false - useProguard true + useProguard false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } From 4d6cc87529cb25f860d227992a962ea70c623cd7 Mon Sep 17 00:00:00 2001 From: OleksandrSobol <dr.alexander.sobol@gmail.com> Date: Mon, 24 May 2021 21:06:12 +0300 Subject: [PATCH 04/28] CAKE-192 | merged main branch into current; fixed call unstoppable domains api for different sdk versions; fixed italian and croatian locales --- .../cakewallet/cake_wallet/MainActivity.java | 68 +++++++--------- ios/Runner/AppDelegate.swift | 78 +++++++++---------- pubspec.lock | 6 +- res/values/strings_de.arb | 2 +- res/values/strings_en.arb | 2 +- res/values/strings_es.arb | 2 +- res/values/strings_hi.arb | 2 +- res/values/strings_hr.arb | 5 +- res/values/strings_it.arb | 5 +- res/values/strings_ja.arb | 2 +- res/values/strings_ko.arb | 2 +- res/values/strings_zh.arb | 1 + 12 files changed, 85 insertions(+), 90 deletions(-) diff --git a/android/app/src/main/java/com/cakewallet/cake_wallet/MainActivity.java b/android/app/src/main/java/com/cakewallet/cake_wallet/MainActivity.java index 074ce247f..f796e5417 100644 --- a/android/app/src/main/java/com/cakewallet/cake_wallet/MainActivity.java +++ b/android/app/src/main/java/com/cakewallet/cake_wallet/MainActivity.java @@ -10,29 +10,29 @@ import io.flutter.plugin.common.MethodCall; import io.flutter.plugin.common.MethodChannel; import android.os.AsyncTask; +import android.os.Build; import android.os.Handler; import android.os.Looper; -import java.security.SecureRandom; import com.unstoppabledomains.resolution.DomainResolution; import com.unstoppabledomains.resolution.Resolution; -import android.os.AsyncTask; -import android.os.Handler; -import android.os.Looper; - -import io.flutter.plugin.common.MethodCall; -import io.flutter.plugin.common.MethodChannel; +import java.security.SecureRandom; public class MainActivity extends FlutterFragmentActivity { final String UTILS_CHANNEL = "com.cake_wallet/native_utils"; - final String UNSTOPPABLE_DOMAIN_CHANNEL = "com.cakewallet.cake_wallet/unstoppable-domain"; @Override public void configureFlutterEngine(@NonNull FlutterEngine flutterEngine) { GeneratedPluginRegistrant.registerWith(flutterEngine); + MethodChannel utilsChannel = + new MethodChannel(flutterEngine.getDartExecutor().getBinaryMessenger(), + UTILS_CHANNEL); + + utilsChannel.setMethodCallHandler(this::handle); + MethodChannel unstoppableDomainChannel = new MethodChannel(flutterEngine.getDartExecutor().getBinaryMessenger(), UNSTOPPABLE_DOMAIN_CHANNEL); @@ -41,14 +41,30 @@ public class MainActivity extends FlutterFragmentActivity { } private void handle(@NonNull MethodCall call, @NonNull MethodChannel.Result result) { + Handler handler = new Handler(Looper.getMainLooper()); + try { - if (call.method.equals("getUnstoppableDomainAddress")) { - getUnstoppableDomainAddress(call, result); - } else { - result.notImplemented(); + switch (call.method) { + case "sec_random": + int count = call.argument("count"); + SecureRandom random = new SecureRandom(); + byte bytes[] = new byte[count]; + random.nextBytes(bytes); + handler.post(() -> result.success(bytes)); + break; + case "getUnstoppableDomainAddress": + int version = Build.VERSION.SDK_INT; + if (version >= 24) { + getUnstoppableDomainAddress(call, result); + } else { + handler.post(() -> result.success("")); + } + break; + default: + handler.post(() -> result.notImplemented()); } } catch (Exception e) { - result.error("UNCAUGHT_ERROR", e.getMessage(), null); + handler.post(() -> result.error("UNCAUGHT_ERROR", e.getMessage(), null)); } } @@ -66,29 +82,5 @@ public class MainActivity extends FlutterFragmentActivity { handler.post(() -> result.error("INVALID DOMAIN", e.getMessage(), null)); } }); - - MethodChannel utilsChannel = - new MethodChannel(flutterEngine.getDartExecutor().getBinaryMessenger(), - UTILS_CHANNEL); - - utilsChannel.setMethodCallHandler(this::handle); } - - private void handle(@NonNull MethodCall call, @NonNull MethodChannel.Result result) { - Handler handler = new Handler(Looper.getMainLooper()); - - try { - if (call.method.equals("sec_random")) { - int count = call.argument("count"); - SecureRandom random = new SecureRandom(); - byte bytes[] = new byte[count]; - random.nextBytes(bytes); - handler.post(() -> result.success(bytes)); - } else { - handler.post(() -> result.notImplemented()); - } - } catch (Exception e) { - handler.post(() -> result.error("UNCAUGHT_ERROR", e.getMessage(), null)); - } - } -} +} \ No newline at end of file diff --git a/ios/Runner/AppDelegate.swift b/ios/Runner/AppDelegate.swift index dbd935316..5a50892ec 100644 --- a/ios/Runner/AppDelegate.swift +++ b/ios/Runner/AppDelegate.swift @@ -5,9 +5,9 @@ import UnstoppableDomainsResolution @UIApplicationMain @objc class AppDelegate: FlutterAppDelegate { lazy var resolution : Resolution? = { - return try? Resolution() - }() - + return try? Resolution() + }() + override func application( _ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? @@ -17,11 +17,6 @@ import UnstoppableDomainsResolution name: "com.cakewallet.cakewallet/legacy_wallet_migration", binaryMessenger: controller.binaryMessenger) legacyMigrationChannel.setMethodCallHandler({ - let batteryChannel = FlutterMethodChannel(name: "com.cakewallet.cakewallet/legacy_wallet_migration", - binaryMessenger: controller.binaryMessenger) - let unstoppableDomainChannel = FlutterMethodChannel(name: "com.cakewallet.cake_wallet/unstoppable-domain", binaryMessenger: controller.binaryMessenger) - - batteryChannel.setMethodCallHandler({ (call: FlutterMethodCall, result: @escaping FlutterResult) -> Void in switch call.method { @@ -62,7 +57,7 @@ import UnstoppableDomainsResolution result(FlutterMethodNotImplemented) } }) - + let utilsChannel = FlutterMethodChannel( name: "com.cake_wallet/native_utils", binaryMessenger: controller.binaryMessenger) @@ -80,40 +75,41 @@ import UnstoppableDomainsResolution result(FlutterMethodNotImplemented) } }) - + + let unstoppableDomainChannel = FlutterMethodChannel(name: "com.cakewallet.cake_wallet/unstoppable-domain", binaryMessenger: controller.binaryMessenger) unstoppableDomainChannel.setMethodCallHandler({ [weak self] - (call: FlutterMethodCall, result: @escaping FlutterResult) -> Void in - switch call.method { - case "getUnstoppableDomainAddress": - guard let args = call.arguments as? Dictionary<String, String>, - let domain = args["domain"], - let ticker = args["ticker"] else { - result(nil) - return - } - - guard let resolution = self?.resolution else { - result(nil) - return - } - - resolution.addr(domain: domain, ticker: ticker) { addrResult in - var address : String = "" - - switch addrResult { - case .success(let returnValue): - address = returnValue - case .failure(let error): - print("Expected Address, but got \(error)") + (call: FlutterMethodCall, result: @escaping FlutterResult) -> Void in + switch call.method { + case "getUnstoppableDomainAddress": + guard let args = call.arguments as? Dictionary<String, String>, + let domain = args["domain"], + let ticker = args["ticker"] else { + result(nil) + return + } + + guard let resolution = self?.resolution else { + result(nil) + return + } + + resolution.addr(domain: domain, ticker: ticker) { addrResult in + var address : String = "" + + switch addrResult { + case .success(let returnValue): + address = returnValue + case .failure(let error): + print("Expected Address, but got \(error)") + } + + result(address) + } + default: + result(FlutterMethodNotImplemented) } - - result(address) - } - default: - result(FlutterMethodNotImplemented) - } - }) - + }) + GeneratedPluginRegistrant.register(with: self) return super.application(application, didFinishLaunchingWithOptions: launchOptions) } diff --git a/pubspec.lock b/pubspec.lock index 3ff596074..207f4b18a 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -70,7 +70,7 @@ packages: path: "." ref: cake resolved-ref: "02fef082f20af13de00b4e64efb93a2c1e5e1cf2" - url: "git@github.com:cake-tech/bech32.git" + url: "https://github.com/cake-tech/bech32.git" source: git version: "0.2.0" bip32: @@ -92,8 +92,8 @@ packages: description: path: "." ref: cake - resolved-ref: b3ab2926c665f0e68b74a4a5f31059f7fcd817b7 - url: "git@github.com:cake-tech/bitcoin_flutter.git" + resolved-ref: cbabfd87b6ce3cae6051a3e86ddb56e7a934e188 + url: "https://github.com/cake-tech/bitcoin_flutter.git" source: git version: "2.0.2" boolean_selector: diff --git a/res/values/strings_de.arb b/res/values/strings_de.arb index da4d4975b..fe64bc855 100644 --- a/res/values/strings_de.arb +++ b/res/values/strings_de.arb @@ -474,7 +474,7 @@ "buy_alert_content" : "Derzeit unterstützen wir nur den Kauf von Bitcoin. Um Bitcoin zu kaufen, erstellen Sie bitte Ihre Bitcoin-Brieftasche oder wechseln Sie zu dieser", "outdated_electrum_wallet_desceription" : "New Bitcoin wallets created in Cake now have a 24-word seed. It is mandatory that you create a new Bitcoin wallet and transfer all of your funds to the new 24-word wallet, and stop using wallets with a 12-word seed. Please do this immediately to secure your funds.", - "understand" : "I understand" + "understand" : "I understand", "address_detected" : "Adresse erkannt", "address_from_domain" : "Sie haben die Adresse von der unaufhaltsamen Domain ${domain} erhalten" diff --git a/res/values/strings_en.arb b/res/values/strings_en.arb index e43ec7318..e0e254762 100644 --- a/res/values/strings_en.arb +++ b/res/values/strings_en.arb @@ -474,7 +474,7 @@ "buy_alert_content" : "Currently we only support the purchase of Bitcoin. To buy Bitcoin, please create or switch to your Bitcoin wallet", "outdated_electrum_wallet_desceription" : "New Bitcoin wallets created in Cake now have a 24-word seed. It is mandatory that you create a new Bitcoin wallet and transfer all of your funds to the new 24-word wallet, and stop using wallets with a 12-word seed. Please do this immediately to secure your funds.", - "understand" : "I understand" + "understand" : "I understand", "address_detected" : "Address detected", "address_from_domain" : "You got address from unstoppable domain ${domain}" diff --git a/res/values/strings_es.arb b/res/values/strings_es.arb index 09dae1e32..a1efe9c9b 100644 --- a/res/values/strings_es.arb +++ b/res/values/strings_es.arb @@ -474,7 +474,7 @@ "buy_alert_content" : "Actualmente solo apoyamos la compra de Bitcoin. Para comprar Bitcoin, cree o cambie a su billetera Bitcoin", "outdated_electrum_wallet_desceription" : "New Bitcoin wallets created in Cake now have a 24-word seed. It is mandatory that you create a new Bitcoin wallet and transfer all of your funds to the new 24-word wallet, and stop using wallets with a 12-word seed. Please do this immediately to secure your funds.", - "understand" : "I understand" + "understand" : "I understand", "address_detected" : "Dirección detectada", "address_from_domain" : "Tienes la dirección de unstoppable domain ${domain}" diff --git a/res/values/strings_hi.arb b/res/values/strings_hi.arb index b9a873265..899a6d9a8 100644 --- a/res/values/strings_hi.arb +++ b/res/values/strings_hi.arb @@ -474,7 +474,7 @@ "buy_alert_content" : "वर्तमान में हम केवल बिटकॉइन की खरीद का समर्थन करते हैं। बिटकॉइन खरीदने के लिए, कृपया अपना बिटकॉइन वॉलेट बनाएं या स्विच करें", "outdated_electrum_wallet_desceription" : "New Bitcoin wallets created in Cake now have a 24-word seed. It is mandatory that you create a new Bitcoin wallet and transfer all of your funds to the new 24-word wallet, and stop using wallets with a 12-word seed. Please do this immediately to secure your funds.", - "understand" : "I understand" + "understand" : "I understand", "address_detected" : "पता लग गया", "address_from_domain" : "आपको अजेय डोमेन ${domain} से पता मिला है" diff --git a/res/values/strings_hr.arb b/res/values/strings_hr.arb index d449fca1b..857bb7905 100644 --- a/res/values/strings_hr.arb +++ b/res/values/strings_hr.arb @@ -474,5 +474,8 @@ "buy_alert_content" : "Currently we only support the purchase of Bitcoin. To buy Bitcoin, please create or switch to your Bitcoin wallet", "outdated_electrum_wallet_desceription" : "New Bitcoin wallets created in Cake now have a 24-word seed. It is mandatory that you create a new Bitcoin wallet and transfer all of your funds to the new 24-word wallet, and stop using wallets with a 12-word seed. Please do this immediately to secure your funds.", - "understand" : "I understand" + "understand" : "I understand", + + "address_detected" : "Adresa je otkrivena", + "address_from_domain" : "Dobili ste adresu od unstoppable domain ${domain}" } \ No newline at end of file diff --git a/res/values/strings_it.arb b/res/values/strings_it.arb index e8c4da26d..7d5fd783f 100644 --- a/res/values/strings_it.arb +++ b/res/values/strings_it.arb @@ -474,5 +474,8 @@ "buy_alert_content" : "Currently we only support the purchase of Bitcoin. To buy Bitcoin, please create or switch to your Bitcoin wallet", "outdated_electrum_wallet_desceription" : "New Bitcoin wallets created in Cake now have a 24-word seed. It is mandatory that you create a new Bitcoin wallet and transfer all of your funds to the new 24-word wallet, and stop using wallets with a 12-word seed. Please do this immediately to secure your funds.", - "understand" : "I understand" + "understand" : "I understand", + + "address_detected" : "Indirizzo rilevato", + "address_from_domain" : "Hai l'indirizzo da unstoppable domain ${domain}" } \ No newline at end of file diff --git a/res/values/strings_ja.arb b/res/values/strings_ja.arb index 5fb875578..94ddea3de 100644 --- a/res/values/strings_ja.arb +++ b/res/values/strings_ja.arb @@ -474,7 +474,7 @@ "buy_alert_content" : "現在、ビットコインの購入のみをサポートしています。 ビットコインを購入するには、ビットコインウォレットを作成するか切り替えてください", "outdated_electrum_wallet_desceription" : "New Bitcoin wallets created in Cake now have a 24-word seed. It is mandatory that you create a new Bitcoin wallet and transfer all of your funds to the new 24-word wallet, and stop using wallets with a 12-word seed. Please do this immediately to secure your funds.", - "understand" : "I understand" + "understand" : "I understand", "address_detected" : "アドレスが検出されました", "address_from_domain" : "あなたはからアドレスを得ました unstoppable domain ${domain}" diff --git a/res/values/strings_ko.arb b/res/values/strings_ko.arb index 0d64ea4fc..836e36d52 100644 --- a/res/values/strings_ko.arb +++ b/res/values/strings_ko.arb @@ -474,7 +474,7 @@ "buy_alert_content" : "현재 우리는 비트 코인 구매 만 지원합니다. 비트 코인을 구매하려면 비트 코인 지갑을 생성하거나 전환하십시오", "outdated_electrum_wallet_desceription" : "New Bitcoin wallets created in Cake now have a 24-word seed. It is mandatory that you create a new Bitcoin wallet and transfer all of your funds to the new 24-word wallet, and stop using wallets with a 12-word seed. Please do this immediately to secure your funds.", - "understand" : "I understand" + "understand" : "I understand", "address_detected" : "주소 감지", "address_from_domain" : "주소는 unstoppable domain ${domain}" diff --git a/res/values/strings_zh.arb b/res/values/strings_zh.arb index ce831a4b3..274108fc4 100644 --- a/res/values/strings_zh.arb +++ b/res/values/strings_zh.arb @@ -469,6 +469,7 @@ "displayable" : "可显示", "submit_request" : "提交请求", + "buy_alert_content" : "目前,我們僅支持購買比特幣。 要購買比特幣,請創建或切換到您的比特幣錢包", "outdated_electrum_wallet_desceription" : "New Bitcoin wallets created in Cake now have a 24-word seed. It is mandatory that you create a new Bitcoin wallet and transfer all of your funds to the new 24-word wallet, and stop using wallets with a 12-word seed. Please do this immediately to secure your funds.", From f324d6599d1e1c4fa634f0fc80ed17ac45365ce4 Mon Sep 17 00:00:00 2001 From: OleksandrSobol <dr.alexander.sobol@gmail.com> Date: Tue, 25 May 2021 16:53:35 +0300 Subject: [PATCH 05/28] CAKE-192 | added final int UNSTOPPABLE_DOMAIN_MIN_VERSION_SDK to MainActivity.java --- .../src/main/java/com/cakewallet/cake_wallet/MainActivity.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/android/app/src/main/java/com/cakewallet/cake_wallet/MainActivity.java b/android/app/src/main/java/com/cakewallet/cake_wallet/MainActivity.java index f796e5417..fa6930a05 100644 --- a/android/app/src/main/java/com/cakewallet/cake_wallet/MainActivity.java +++ b/android/app/src/main/java/com/cakewallet/cake_wallet/MainActivity.java @@ -22,6 +22,7 @@ import java.security.SecureRandom; public class MainActivity extends FlutterFragmentActivity { final String UTILS_CHANNEL = "com.cake_wallet/native_utils"; final String UNSTOPPABLE_DOMAIN_CHANNEL = "com.cakewallet.cake_wallet/unstoppable-domain"; + final int UNSTOPPABLE_DOMAIN_MIN_VERSION_SDK = 24; @Override public void configureFlutterEngine(@NonNull FlutterEngine flutterEngine) { @@ -54,7 +55,7 @@ public class MainActivity extends FlutterFragmentActivity { break; case "getUnstoppableDomainAddress": int version = Build.VERSION.SDK_INT; - if (version >= 24) { + if (version >= UNSTOPPABLE_DOMAIN_MIN_VERSION_SDK) { getUnstoppableDomainAddress(call, result); } else { handler.post(() -> result.success("")); From aca8e8a292d413782821ba4bb48038384955a9ba Mon Sep 17 00:00:00 2001 From: OleksandrSobol <dr.alexander.sobol@gmail.com> Date: Tue, 25 May 2021 20:32:36 +0300 Subject: [PATCH 06/28] CAKE-329 | added unspent coins list page to the app --- lib/di.dart | 8 ++ lib/router.dart | 5 ++ lib/routes.dart | 1 + lib/src/screens/send/send_page.dart | 41 ++++++---- .../unspent_coins_list_page.dart | 38 +++++++++ .../widgets/unspent_coins_list_item.dart | 77 +++++++++++++++++++ .../unspent_coins/unspent_coins_item.dart | 14 ++++ .../unspent_coins_list_view_model.dart | 47 +++++++++++ pubspec.lock | 6 +- 9 files changed, 220 insertions(+), 17 deletions(-) create mode 100644 lib/src/screens/unspent_coins/unspent_coins_list_page.dart create mode 100644 lib/src/screens/unspent_coins/widgets/unspent_coins_list_item.dart create mode 100644 lib/view_model/unspent_coins/unspent_coins_item.dart create mode 100644 lib/view_model/unspent_coins/unspent_coins_list_view_model.dart diff --git a/lib/di.dart b/lib/di.dart index cae3a068f..211bf5708 100644 --- a/lib/di.dart +++ b/lib/di.dart @@ -40,6 +40,7 @@ import 'package:cake_wallet/src/screens/setup_pin_code/setup_pin_code.dart'; import 'package:cake_wallet/src/screens/support/support_page.dart'; import 'package:cake_wallet/src/screens/trade_details/trade_details_page.dart'; import 'package:cake_wallet/src/screens/transaction_details/transaction_details_page.dart'; +import 'package:cake_wallet/src/screens/unspent_coins/unspent_coins_list_page.dart'; import 'package:cake_wallet/src/screens/wallet_keys/wallet_keys_page.dart'; import 'package:cake_wallet/src/screens/exchange/exchange_page.dart'; import 'package:cake_wallet/src/screens/exchange/exchange_template_page.dart'; @@ -76,6 +77,7 @@ import 'package:cake_wallet/view_model/setup_pin_code_view_model.dart'; import 'package:cake_wallet/view_model/support_view_model.dart'; import 'package:cake_wallet/view_model/transaction_details_view_model.dart'; import 'package:cake_wallet/view_model/trade_details_view_model.dart'; +import 'package:cake_wallet/view_model/unspent_coins/unspent_coins_list_view_model.dart'; import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_edit_or_create_view_model.dart'; import 'package:cake_wallet/view_model/auth_view_model.dart'; import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart'; @@ -561,5 +563,11 @@ Future setup( getIt.registerFactory(() => SupportPage(getIt.get<SupportViewModel>())); + getIt.registerFactory(() => UnspentCoinsListViewModel()); + + getIt.registerFactory(() => UnspentCoinsListPage( + unspentCoinsListViewModel: getIt.get<UnspentCoinsListViewModel>() + )); + _isSetupFinished = true; } diff --git a/lib/router.dart b/lib/router.dart index 19a6b760f..b8967645e 100644 --- a/lib/router.dart +++ b/lib/router.dart @@ -9,6 +9,7 @@ import 'package:cake_wallet/src/screens/restore/restore_from_backup_page.dart'; import 'package:cake_wallet/src/screens/restore/wallet_restore_page.dart'; import 'package:cake_wallet/src/screens/seed/pre_seed_page.dart'; import 'package:cake_wallet/src/screens/support/support_page.dart'; +import 'package:cake_wallet/src/screens/unspent_coins/unspent_coins_list_page.dart'; import 'package:cake_wallet/src/screens/wyre/wyre_page.dart'; import 'package:cake_wallet/store/settings_store.dart'; import 'package:cake_wallet/view_model/monero_account_list/account_list_item.dart'; @@ -355,6 +356,10 @@ Route<dynamic> createRoute(RouteSettings settings) { return CupertinoPageRoute<void>( builder: (_) => getIt.get<SupportPage>()); + case Routes.unspentCoinsList: + return MaterialPageRoute<void>( + builder: (_) => getIt.get<UnspentCoinsListPage>()); + default: return MaterialPageRoute<void>( builder: (_) => Scaffold( diff --git a/lib/routes.dart b/lib/routes.dart index 2bf8ee7b7..84a48d5ea 100644 --- a/lib/routes.dart +++ b/lib/routes.dart @@ -54,4 +54,5 @@ class Routes { static const support = '/support'; static const orderDetails = '/order_details'; static const wyre = '/wyre'; + static const unspentCoinsList = '/unspent_coins_list'; } \ No newline at end of file diff --git a/lib/src/screens/send/send_page.dart b/lib/src/screens/send/send_page.dart index 9fae0ec23..1fd74e130 100644 --- a/lib/src/screens/send/send_page.dart +++ b/lib/src/screens/send/send_page.dart @@ -558,22 +558,35 @@ class SendPage extends BasePage { ), bottomSectionPadding: EdgeInsets.only(left: 24, right: 24, bottom: 24), - bottomSection: Observer(builder: (_) { - return LoadingPrimaryButton( - onPressed: () async { - if (_formKey.currentState.validate()) { - await sendViewModel.createTransaction(); - } - }, - text: S.of(context).send, - color: Theme.of(context).accentTextTheme.body2.color, - textColor: Colors.white, - isLoading: sendViewModel.state is IsExecutingState || - sendViewModel.state is TransactionCommitting, - isDisabled: + bottomSection: Column( + children: [ + Observer(builder: (_) { + return LoadingPrimaryButton( + onPressed: () async { + if (_formKey.currentState.validate()) { + await sendViewModel.createTransaction(); + } + }, + text: S.of(context).send, + color: Theme.of(context).accentTextTheme.body2.color, + textColor: Colors.white, + isLoading: sendViewModel.state is IsExecutingState || + sendViewModel.state is TransactionCommitting, + isDisabled: false // FIXME !(syncStore.status is SyncedSyncStatus), ); - })), + }), + Padding( + padding: EdgeInsets.only(top: 12), + child: PrimaryButton( + onPressed: () => Navigator.of(context).pushNamed(Routes.unspentCoinsList), + text: 'Unspent coins', + color: Colors.green, + textColor: Colors.white, + ) + ) + ], + )), )); } diff --git a/lib/src/screens/unspent_coins/unspent_coins_list_page.dart b/lib/src/screens/unspent_coins/unspent_coins_list_page.dart new file mode 100644 index 000000000..44a6a34b8 --- /dev/null +++ b/lib/src/screens/unspent_coins/unspent_coins_list_page.dart @@ -0,0 +1,38 @@ +import 'package:cake_wallet/src/screens/unspent_coins/widgets/unspent_coins_list_item.dart'; +import 'package:cake_wallet/src/widgets/standard_list.dart'; +import 'package:cake_wallet/view_model/unspent_coins/unspent_coins_list_view_model.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:cake_wallet/src/screens/base_page.dart'; + +class UnspentCoinsListPage extends BasePage { + UnspentCoinsListPage({this.unspentCoinsListViewModel}); + + @override + String get title => 'Unspent coins'; + + final UnspentCoinsListViewModel unspentCoinsListViewModel; + + @override + Widget body(BuildContext context) { + return SectionStandardList( + sectionCount: 1, + itemCounter: (int _) => unspentCoinsListViewModel.items.length, + itemBuilder: (_, __, index) { + final item = unspentCoinsListViewModel.items[index]; + + return GestureDetector( + onTap: () {print('Item taped');}, + child: UnspentCoinsListItem( + address: item.address, + amount: item.amount, + isFrozen: item.isFrozen, + note: item.note, + isSending: item.isSending, + onCheckBoxTap: (value) {print('CheckBox taped');}, + ) + ); + }); + } + +} \ No newline at end of file diff --git a/lib/src/screens/unspent_coins/widgets/unspent_coins_list_item.dart b/lib/src/screens/unspent_coins/widgets/unspent_coins_list_item.dart new file mode 100644 index 000000000..eb8ba33c0 --- /dev/null +++ b/lib/src/screens/unspent_coins/widgets/unspent_coins_list_item.dart @@ -0,0 +1,77 @@ +import 'package:auto_size_text/auto_size_text.dart'; +import 'package:cake_wallet/src/widgets/standard_checkbox.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/cupertino.dart'; + +class UnspentCoinsListItem extends StatelessWidget { + UnspentCoinsListItem({ + @required this.address, + @required this.amount, + @required this.isFrozen, + @required this.note, + @required this.isSending, + @required this.onCheckBoxTap, +}); + + final String address; + final String amount; + final bool isFrozen; + final String note; + final bool isSending; + final Function(bool) onCheckBoxTap; + + @override + Widget build(BuildContext context) { + final textStyle = TextStyle( + fontSize: 16, + fontWeight: FontWeight.w500, + color: Theme.of(context) + .primaryTextTheme + .title + .color); + + return Container( + padding: EdgeInsets.fromLTRB(24, 16, 24, 16), + color: Theme.of(context).backgroundColor, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + address ?? 'Address', + style: textStyle, + ), + Padding( + padding: EdgeInsets.only(top: 12), + child: Text( + amount ?? 'Amount', + style: textStyle, + ) + ), + if (isFrozen ?? false) Padding( + padding: EdgeInsets.only(top: 12), + child: Text( + 'Freeze', + style: textStyle, + ) + ), + if (note?.isNotEmpty ?? false) Padding( + padding: EdgeInsets.only(top: 12), + child: AutoSizeText( + note, + style: textStyle, + maxLines: 1 + ) + ), + Padding( + padding: EdgeInsets.only(top: 12), + child: StandardCheckbox( + value: isSending, + caption: 'Sending', + onChanged: onCheckBoxTap + ) + ) + ], + ) + ); + } +} \ No newline at end of file diff --git a/lib/view_model/unspent_coins/unspent_coins_item.dart b/lib/view_model/unspent_coins/unspent_coins_item.dart new file mode 100644 index 000000000..94b7d55c6 --- /dev/null +++ b/lib/view_model/unspent_coins/unspent_coins_item.dart @@ -0,0 +1,14 @@ +class UnspentCoinsItem { + UnspentCoinsItem({ + this.address, + this.amount, + this.isFrozen, + this.note, + this.isSending = true}); + + final String address; + final String amount; + final bool isFrozen; + final String note; + final bool isSending; +} \ No newline at end of file diff --git a/lib/view_model/unspent_coins/unspent_coins_list_view_model.dart b/lib/view_model/unspent_coins/unspent_coins_list_view_model.dart new file mode 100644 index 000000000..4b765d55e --- /dev/null +++ b/lib/view_model/unspent_coins/unspent_coins_list_view_model.dart @@ -0,0 +1,47 @@ +import 'package:cake_wallet/view_model/unspent_coins/unspent_coins_item.dart'; +import 'package:mobx/mobx.dart'; + +part 'unspent_coins_list_view_model.g.dart'; + +const List<Map<String, dynamic>> unspentCoinsMap = [ + <String, dynamic>{ + "address" : "11111111111111121111132432432432432432432443124324324234324324324324332424", + "amount" : "222", + "isFrozen" : true, + "note" : "333cvgf23132132132132131321321314rwrtdggfdddewq ewqasfdxgdhgfgfszczcxgbhhhbcgbc"}, + <String, dynamic>{ + "address" : "444", + "amount" : "555", + "note" : "sfjskf"}, + <String, dynamic>{ + "address" : "777", + "amount" : "888", + "isFrozen" : false}, + <String, dynamic>{ + "address" : "11111111111111121111132432432432432432432443124324324234324324324324332424", + "amount" : "222", + "isFrozen" : true, + "note" : "333cvgf23132132132132131321321314rwrtdggfdddewq ewqasfdxgdhgfgfszczcxgbhhhbcgbc"}, + <String, dynamic>{ + "address" : "444", + "amount" : "555", + "note" : "sffsfsdsgs"}, + <String, dynamic>{ + "address" : "777", + "amount" : "888", + "isFrozen" : false}, + <String, dynamic>{}, +]; + +class UnspentCoinsListViewModel = UnspentCoinsListViewModelBase with _$UnspentCoinsListViewModel; + +abstract class UnspentCoinsListViewModelBase with Store { + @computed + List<UnspentCoinsItem> get items => unspentCoinsMap.map((elem) => + UnspentCoinsItem( + address: elem["address"] as String, + amount: elem["amount"] as String, + isFrozen: elem["isFrozen"] as bool, + note: elem["note"] as String + )).toList(); +} \ No newline at end of file diff --git a/pubspec.lock b/pubspec.lock index 3ff596074..207f4b18a 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -70,7 +70,7 @@ packages: path: "." ref: cake resolved-ref: "02fef082f20af13de00b4e64efb93a2c1e5e1cf2" - url: "git@github.com:cake-tech/bech32.git" + url: "https://github.com/cake-tech/bech32.git" source: git version: "0.2.0" bip32: @@ -92,8 +92,8 @@ packages: description: path: "." ref: cake - resolved-ref: b3ab2926c665f0e68b74a4a5f31059f7fcd817b7 - url: "git@github.com:cake-tech/bitcoin_flutter.git" + resolved-ref: cbabfd87b6ce3cae6051a3e86ddb56e7a934e188 + url: "https://github.com/cake-tech/bitcoin_flutter.git" source: git version: "2.0.2" boolean_selector: From b71e85b11259738b70ef5970cc605c8d9a3b7f17 Mon Sep 17 00:00:00 2001 From: OleksandrSobol <dr.alexander.sobol@gmail.com> Date: Wed, 26 May 2021 19:10:46 +0300 Subject: [PATCH 07/28] CAKE-329 | applied observable list to items in the unspent_coins_list_view_model.dart --- .../unspent_coins_list_page.dart | 24 +++++++++---------- .../unspent_coins_list_view_model.dart | 15 ++++++------ 2 files changed, 20 insertions(+), 19 deletions(-) diff --git a/lib/src/screens/unspent_coins/unspent_coins_list_page.dart b/lib/src/screens/unspent_coins/unspent_coins_list_page.dart index 44a6a34b8..cc175eb01 100644 --- a/lib/src/screens/unspent_coins/unspent_coins_list_page.dart +++ b/lib/src/screens/unspent_coins/unspent_coins_list_page.dart @@ -4,6 +4,7 @@ import 'package:cake_wallet/view_model/unspent_coins/unspent_coins_list_view_mod import 'package:flutter/material.dart'; import 'package:flutter/cupertino.dart'; import 'package:cake_wallet/src/screens/base_page.dart'; +import 'package:flutter_mobx/flutter_mobx.dart'; class UnspentCoinsListPage extends BasePage { UnspentCoinsListPage({this.unspentCoinsListViewModel}); @@ -15,24 +16,23 @@ class UnspentCoinsListPage extends BasePage { @override Widget body(BuildContext context) { - return SectionStandardList( + return Observer(builder: (_) => SectionStandardList( sectionCount: 1, itemCounter: (int _) => unspentCoinsListViewModel.items.length, itemBuilder: (_, __, index) { final item = unspentCoinsListViewModel.items[index]; return GestureDetector( - onTap: () {print('Item taped');}, - child: UnspentCoinsListItem( - address: item.address, - amount: item.amount, - isFrozen: item.isFrozen, - note: item.note, - isSending: item.isSending, - onCheckBoxTap: (value) {print('CheckBox taped');}, - ) - ); - }); + onTap: () {print('Item taped');}, + child: UnspentCoinsListItem( + address: item.address, + amount: item.amount, + isFrozen: item.isFrozen, + note: item.note, + isSending: item.isSending, + onCheckBoxTap: (value) {print('CheckBox taped');}, + )); + })); } } \ No newline at end of file diff --git a/lib/view_model/unspent_coins/unspent_coins_list_view_model.dart b/lib/view_model/unspent_coins/unspent_coins_list_view_model.dart index 4b765d55e..5f631e52a 100644 --- a/lib/view_model/unspent_coins/unspent_coins_list_view_model.dart +++ b/lib/view_model/unspent_coins/unspent_coins_list_view_model.dart @@ -37,11 +37,12 @@ class UnspentCoinsListViewModel = UnspentCoinsListViewModelBase with _$UnspentCo abstract class UnspentCoinsListViewModelBase with Store { @computed - List<UnspentCoinsItem> get items => unspentCoinsMap.map((elem) => - UnspentCoinsItem( - address: elem["address"] as String, - amount: elem["amount"] as String, - isFrozen: elem["isFrozen"] as bool, - note: elem["note"] as String - )).toList(); + ObservableList<UnspentCoinsItem> get items => + ObservableList.of(unspentCoinsMap.map((elem) => + UnspentCoinsItem( + address: elem["address"] as String, + amount: elem["amount"] as String, + isFrozen: elem["isFrozen"] as bool, + note: elem["note"] as String + ))); } \ No newline at end of file From e36c07b0da6377849f363c5fb94bd4f01bfd5af4 Mon Sep 17 00:00:00 2001 From: OleksandrSobol <dr.alexander.sobol@gmail.com> Date: Thu, 27 May 2021 20:57:51 +0300 Subject: [PATCH 08/28] CAKE-330 | added unspent coins details page to the app --- lib/di.dart | 12 ++++ lib/router.dart | 8 +++ lib/routes.dart | 1 + .../unspent_coins_details_page.dart | 52 ++++++++++++++++++ .../unspent_coins_list_page.dart | 5 +- .../widgets/unspent_coins_switch_row.dart | 45 +++++++++++++++ .../unspent_coins_details_view_model.dart | 55 +++++++++++++++++++ .../unspent_coins_switch_item.dart | 12 ++++ 8 files changed, 189 insertions(+), 1 deletion(-) create mode 100644 lib/src/screens/unspent_coins/unspent_coins_details_page.dart create mode 100644 lib/src/screens/unspent_coins/widgets/unspent_coins_switch_row.dart create mode 100644 lib/view_model/unspent_coins/unspent_coins_details_view_model.dart create mode 100644 lib/view_model/unspent_coins/unspent_coins_switch_item.dart diff --git a/lib/di.dart b/lib/di.dart index 211bf5708..70f5b4a51 100644 --- a/lib/di.dart +++ b/lib/di.dart @@ -40,6 +40,7 @@ import 'package:cake_wallet/src/screens/setup_pin_code/setup_pin_code.dart'; import 'package:cake_wallet/src/screens/support/support_page.dart'; import 'package:cake_wallet/src/screens/trade_details/trade_details_page.dart'; import 'package:cake_wallet/src/screens/transaction_details/transaction_details_page.dart'; +import 'package:cake_wallet/src/screens/unspent_coins/unspent_coins_details_page.dart'; import 'package:cake_wallet/src/screens/unspent_coins/unspent_coins_list_page.dart'; import 'package:cake_wallet/src/screens/wallet_keys/wallet_keys_page.dart'; import 'package:cake_wallet/src/screens/exchange/exchange_page.dart'; @@ -77,6 +78,8 @@ import 'package:cake_wallet/view_model/setup_pin_code_view_model.dart'; import 'package:cake_wallet/view_model/support_view_model.dart'; import 'package:cake_wallet/view_model/transaction_details_view_model.dart'; import 'package:cake_wallet/view_model/trade_details_view_model.dart'; +import 'package:cake_wallet/view_model/unspent_coins/unspent_coins_details_view_model.dart'; +import 'package:cake_wallet/view_model/unspent_coins/unspent_coins_item.dart'; import 'package:cake_wallet/view_model/unspent_coins/unspent_coins_list_view_model.dart'; import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_edit_or_create_view_model.dart'; import 'package:cake_wallet/view_model/auth_view_model.dart'; @@ -569,5 +572,14 @@ Future setup( unspentCoinsListViewModel: getIt.get<UnspentCoinsListViewModel>() )); + getIt.registerFactoryParam<UnspentCoinsDetailsViewModel, + UnspentCoinsItem, void>((item, _) => + UnspentCoinsDetailsViewModel(unspentCoinsItem: item)); + + getIt.registerFactoryParam<UnspentCoinsDetailsPage, + UnspentCoinsItem, void>((UnspentCoinsItem item, _) => + UnspentCoinsDetailsPage(unspentCoinsDetailsViewModel: + getIt.get<UnspentCoinsDetailsViewModel>(param1: item))); + _isSetupFinished = true; } diff --git a/lib/router.dart b/lib/router.dart index b8967645e..7ecb95026 100644 --- a/lib/router.dart +++ b/lib/router.dart @@ -9,10 +9,12 @@ import 'package:cake_wallet/src/screens/restore/restore_from_backup_page.dart'; import 'package:cake_wallet/src/screens/restore/wallet_restore_page.dart'; import 'package:cake_wallet/src/screens/seed/pre_seed_page.dart'; import 'package:cake_wallet/src/screens/support/support_page.dart'; +import 'package:cake_wallet/src/screens/unspent_coins/unspent_coins_details_page.dart'; import 'package:cake_wallet/src/screens/unspent_coins/unspent_coins_list_page.dart'; import 'package:cake_wallet/src/screens/wyre/wyre_page.dart'; import 'package:cake_wallet/store/settings_store.dart'; import 'package:cake_wallet/view_model/monero_account_list/account_list_item.dart'; +import 'package:cake_wallet/view_model/unspent_coins/unspent_coins_item.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:cake_wallet/routes.dart'; @@ -360,6 +362,12 @@ Route<dynamic> createRoute(RouteSettings settings) { return MaterialPageRoute<void>( builder: (_) => getIt.get<UnspentCoinsListPage>()); + case Routes.unspentCoinsDetails: + return MaterialPageRoute<void>( + builder: (_) => + getIt.get<UnspentCoinsDetailsPage>( + param1: settings.arguments as UnspentCoinsItem)); + default: return MaterialPageRoute<void>( builder: (_) => Scaffold( diff --git a/lib/routes.dart b/lib/routes.dart index 84a48d5ea..65f8253a8 100644 --- a/lib/routes.dart +++ b/lib/routes.dart @@ -55,4 +55,5 @@ class Routes { static const orderDetails = '/order_details'; static const wyre = '/wyre'; static const unspentCoinsList = '/unspent_coins_list'; + static const unspentCoinsDetails = '/unspent_coins_details'; } \ No newline at end of file diff --git a/lib/src/screens/unspent_coins/unspent_coins_details_page.dart b/lib/src/screens/unspent_coins/unspent_coins_details_page.dart new file mode 100644 index 000000000..9f73eaaa3 --- /dev/null +++ b/lib/src/screens/unspent_coins/unspent_coins_details_page.dart @@ -0,0 +1,52 @@ +import 'package:cake_wallet/src/screens/transaction_details/textfield_list_item.dart'; +import 'package:cake_wallet/src/screens/transaction_details/widgets/textfield_list_row.dart'; +import 'package:cake_wallet/src/screens/unspent_coins/widgets/unspent_coins_switch_row.dart'; +import 'package:cake_wallet/src/widgets/standard_list.dart'; +import 'package:cake_wallet/view_model/unspent_coins/unspent_coins_details_view_model.dart'; +import 'package:cake_wallet/view_model/unspent_coins/unspent_coins_switch_item.dart'; +import 'package:flutter/material.dart'; +import 'package:cake_wallet/src/widgets/standart_list_row.dart'; +import 'package:cake_wallet/src/screens/transaction_details/standart_list_item.dart'; +import 'package:cake_wallet/src/screens/base_page.dart'; +import 'package:flutter_mobx/flutter_mobx.dart'; + +class UnspentCoinsDetailsPage extends BasePage { + UnspentCoinsDetailsPage({this.unspentCoinsDetailsViewModel}); + + @override + String get title => 'Unspent coins details'; + + final UnspentCoinsDetailsViewModel unspentCoinsDetailsViewModel; + + @override + Widget body(BuildContext context) { + return SectionStandardList( + sectionCount: 1, + itemCounter: (int _) => unspentCoinsDetailsViewModel.items.length, + itemBuilder: (_, __, index) { + final item = unspentCoinsDetailsViewModel.items[index]; + + if (item is StandartListItem) { + return StandartListRow(title: '${item.title}:', value: item.value); + } + + if (item is TextFieldListItem) { + return TextFieldListRow( + title: item.title, + value: item.value, + onSubmitted: item.onSubmitted, + ); + } + + if (item is UnspentCoinsSwitchItem) { + return Observer(builder: (_) => UnspentCoinsSwitchRow( + title: item.title, + switchValue: item.switchValue(), + onSwitchValueChange: item.onSwitchValueChange + )); + } + + return null; + }); + } +} \ No newline at end of file diff --git a/lib/src/screens/unspent_coins/unspent_coins_list_page.dart b/lib/src/screens/unspent_coins/unspent_coins_list_page.dart index cc175eb01..8a7b7de6c 100644 --- a/lib/src/screens/unspent_coins/unspent_coins_list_page.dart +++ b/lib/src/screens/unspent_coins/unspent_coins_list_page.dart @@ -1,3 +1,4 @@ +import 'package:cake_wallet/routes.dart'; import 'package:cake_wallet/src/screens/unspent_coins/widgets/unspent_coins_list_item.dart'; import 'package:cake_wallet/src/widgets/standard_list.dart'; import 'package:cake_wallet/view_model/unspent_coins/unspent_coins_list_view_model.dart'; @@ -23,7 +24,9 @@ class UnspentCoinsListPage extends BasePage { final item = unspentCoinsListViewModel.items[index]; return GestureDetector( - onTap: () {print('Item taped');}, + onTap: () => + Navigator.of(context).pushNamed(Routes.unspentCoinsDetails, + arguments: item), child: UnspentCoinsListItem( address: item.address, amount: item.amount, diff --git a/lib/src/screens/unspent_coins/widgets/unspent_coins_switch_row.dart b/lib/src/screens/unspent_coins/widgets/unspent_coins_switch_row.dart new file mode 100644 index 000000000..c7c1e81b8 --- /dev/null +++ b/lib/src/screens/unspent_coins/widgets/unspent_coins_switch_row.dart @@ -0,0 +1,45 @@ +import 'package:cake_wallet/src/widgets/standart_switch.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; + +class UnspentCoinsSwitchRow extends StatelessWidget { + UnspentCoinsSwitchRow( + {this.title, + this.titleFontSize = 14, + this.switchValue, + this.onSwitchValueChange}); + + final String title; + final double titleFontSize; + final bool switchValue; + final void Function(bool value) onSwitchValueChange; + + @override + Widget build(BuildContext context) { + return Container( + width: double.infinity, + color: Theme.of(context).backgroundColor, + child: Padding( + padding: + const EdgeInsets.only(left: 24, top: 16, bottom: 16, right: 24), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: <Widget>[ + Text(title, + style: TextStyle( + fontSize: titleFontSize, + fontWeight: FontWeight.w500, + color: Theme.of(context) + .primaryTextTheme.overline.color), + textAlign: TextAlign.left), + Padding( + padding: EdgeInsets.only(top: 12), + child: StandartSwitch( + value: switchValue, + onTaped: () => onSwitchValueChange(!switchValue)) + ) + ]), + ), + ); + } +} \ No newline at end of file diff --git a/lib/view_model/unspent_coins/unspent_coins_details_view_model.dart b/lib/view_model/unspent_coins/unspent_coins_details_view_model.dart new file mode 100644 index 000000000..33db96eba --- /dev/null +++ b/lib/view_model/unspent_coins/unspent_coins_details_view_model.dart @@ -0,0 +1,55 @@ +import 'package:cake_wallet/src/screens/transaction_details/standart_list_item.dart'; +import 'package:cake_wallet/src/screens/transaction_details/textfield_list_item.dart'; +import 'package:cake_wallet/src/screens/transaction_details/transaction_details_list_item.dart'; +import 'package:cake_wallet/view_model/unspent_coins/unspent_coins_item.dart'; +import 'package:cake_wallet/generated/i18n.dart'; +import 'package:cake_wallet/view_model/unspent_coins/unspent_coins_switch_item.dart'; +import 'package:mobx/mobx.dart'; + +part 'unspent_coins_details_view_model.g.dart'; + +class UnspentCoinsDetailsViewModel = UnspentCoinsDetailsViewModelBase + with _$UnspentCoinsDetailsViewModel; + +abstract class UnspentCoinsDetailsViewModelBase with Store { + UnspentCoinsDetailsViewModelBase({this.unspentCoinsItem}) { + final amount = unspentCoinsItem.amount ?? ''; + final address = unspentCoinsItem.address ?? ''; + isFrozen = unspentCoinsItem.isFrozen ?? false; + note = unspentCoinsItem.note ?? ''; + + items = [ + StandartListItem( + title: 'Amount', + value: amount + ), + StandartListItem( + title: 'Address', + value: address + ), + TextFieldListItem( + title: S.current.note_tap_to_change, + value: note, + onSubmitted: (value) { + note = value; + }), + UnspentCoinsSwitchItem( + title: 'Freeze', + value: '', + switchValue: () => isFrozen, + onSwitchValueChange: (value) { + isFrozen = value; + } + ) + ]; + } + + @observable + bool isFrozen; + + @observable + String note; + + final UnspentCoinsItem unspentCoinsItem; + List<TransactionDetailsListItem> items; +} \ No newline at end of file diff --git a/lib/view_model/unspent_coins/unspent_coins_switch_item.dart b/lib/view_model/unspent_coins/unspent_coins_switch_item.dart new file mode 100644 index 000000000..925f8776e --- /dev/null +++ b/lib/view_model/unspent_coins/unspent_coins_switch_item.dart @@ -0,0 +1,12 @@ +import 'package:cake_wallet/src/screens/transaction_details/transaction_details_list_item.dart'; + +class UnspentCoinsSwitchItem extends TransactionDetailsListItem { + UnspentCoinsSwitchItem({ + String title, + String value, + this.switchValue, + this.onSwitchValueChange}) : super(title: title, value: value); + + final bool Function() switchValue; + final void Function(bool value) onSwitchValueChange; +} \ No newline at end of file From b1add664ad5b762618190c3ad8e9e5f4c24600bf Mon Sep 17 00:00:00 2001 From: OleksandrSobol <dr.alexander.sobol@gmail.com> Date: Mon, 31 May 2021 21:04:00 +0300 Subject: [PATCH 09/28] CAKE-329 | reworked unspent_coins_list_page.dart and unspent_coins_list_item.dart; fixed standard_checkbox.dart; applied unspent coins control only to btc wallet --- lib/palette.dart | 1 + lib/src/screens/send/send_page.dart | 71 +++++----- .../unspent_coins_list_page.dart | 70 +++++++--- .../widgets/unspent_coins_list_item.dart | 121 +++++++++++------- lib/src/widgets/standard_checkbox.dart | 22 ++-- lib/view_model/send/send_view_model.dart | 5 + .../unspent_coins_list_view_model.dart | 37 ++++-- 7 files changed, 209 insertions(+), 118 deletions(-) diff --git a/lib/palette.dart b/lib/palette.dart index 336f7b7f9..301669a24 100644 --- a/lib/palette.dart +++ b/lib/palette.dart @@ -44,6 +44,7 @@ class Palette { static const Color dullGray = Color.fromRGBO(98, 98, 98, 1.0); static const Color protectiveBlue = Color.fromRGBO(33, 148, 255, 1.0); static const Color darkBlue = Color.fromRGBO(109, 128, 178, 1.0); + static const Color paleCornflowerBlue = Color.fromRGBO(185, 196, 237, 1.0); } class PaletteDark { diff --git a/lib/src/screens/send/send_page.dart b/lib/src/screens/send/send_page.dart index 1fd74e130..22ba76836 100644 --- a/lib/src/screens/send/send_page.dart +++ b/lib/src/screens/send/send_page.dart @@ -425,7 +425,31 @@ class SendPage extends BasePage { ], ), ), - )) + )), + if (sendViewModel.isBitcoinWallet) Padding( + padding: EdgeInsets.only(top: 6), + child: GestureDetector( + onTap: () => Navigator.of(context) + .pushNamed(Routes.unspentCoinsList), + child: Row( + mainAxisAlignment: + MainAxisAlignment.spaceBetween, + children: [ + Text( + 'Coin control (optional)', + style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.w600, + color: Colors.white)), + Icon( + Icons.arrow_forward_ios, + size: 12, + color: Colors.white, + ) + ], + ) + ) + ) ], ), ) @@ -558,35 +582,22 @@ class SendPage extends BasePage { ), bottomSectionPadding: EdgeInsets.only(left: 24, right: 24, bottom: 24), - bottomSection: Column( - children: [ - Observer(builder: (_) { - return LoadingPrimaryButton( - onPressed: () async { - if (_formKey.currentState.validate()) { - await sendViewModel.createTransaction(); - } - }, - text: S.of(context).send, - color: Theme.of(context).accentTextTheme.body2.color, - textColor: Colors.white, - isLoading: sendViewModel.state is IsExecutingState || - sendViewModel.state is TransactionCommitting, - isDisabled: - false // FIXME !(syncStore.status is SyncedSyncStatus), - ); - }), - Padding( - padding: EdgeInsets.only(top: 12), - child: PrimaryButton( - onPressed: () => Navigator.of(context).pushNamed(Routes.unspentCoinsList), - text: 'Unspent coins', - color: Colors.green, - textColor: Colors.white, - ) - ) - ], - )), + bottomSection: Observer(builder: (_) { + return LoadingPrimaryButton( + onPressed: () async { + if (_formKey.currentState.validate()) { + await sendViewModel.createTransaction(); + } + }, + text: S.of(context).send, + color: Theme.of(context).accentTextTheme.body2.color, + textColor: Colors.white, + isLoading: sendViewModel.state is IsExecutingState || + sendViewModel.state is TransactionCommitting, + isDisabled: + false // FIXME !(syncStore.status is SyncedSyncStatus), + ); + })), )); } diff --git a/lib/src/screens/unspent_coins/unspent_coins_list_page.dart b/lib/src/screens/unspent_coins/unspent_coins_list_page.dart index cc175eb01..81dbaf104 100644 --- a/lib/src/screens/unspent_coins/unspent_coins_list_page.dart +++ b/lib/src/screens/unspent_coins/unspent_coins_list_page.dart @@ -1,10 +1,12 @@ import 'package:cake_wallet/src/screens/unspent_coins/widgets/unspent_coins_list_item.dart'; -import 'package:cake_wallet/src/widgets/standard_list.dart'; +import 'package:cake_wallet/src/widgets/alert_with_one_action.dart'; +import 'package:cake_wallet/utils/show_pop_up.dart'; import 'package:cake_wallet/view_model/unspent_coins/unspent_coins_list_view_model.dart'; import 'package:flutter/material.dart'; import 'package:flutter/cupertino.dart'; import 'package:cake_wallet/src/screens/base_page.dart'; import 'package:flutter_mobx/flutter_mobx.dart'; +import 'package:cake_wallet/generated/i18n.dart'; class UnspentCoinsListPage extends BasePage { UnspentCoinsListPage({this.unspentCoinsListViewModel}); @@ -12,27 +14,59 @@ class UnspentCoinsListPage extends BasePage { @override String get title => 'Unspent coins'; + @override + Widget trailing(BuildContext context) { + final questionImage = Image.asset('assets/images/question_mark.png', + color: Theme.of(context).primaryTextTheme.title.color); + + return SizedBox( + height: 20.0, + width: 20.0, + child: ButtonTheme( + minWidth: double.minPositive, + child: FlatButton( + highlightColor: Colors.transparent, + splashColor: Colors.transparent, + padding: EdgeInsets.all(0), + onPressed: () => showPopUp<void>( + context: context, + builder: (BuildContext context) { + return AlertWithOneAction( + alertTitle: '', + alertContent: 'Information about unspent coins', + buttonText: S.of(context).ok, + buttonAction: () => Navigator.of(context).pop()); + }), + child: questionImage), + ), + ); + } + final UnspentCoinsListViewModel unspentCoinsListViewModel; @override Widget body(BuildContext context) { - return Observer(builder: (_) => SectionStandardList( - sectionCount: 1, - itemCounter: (int _) => unspentCoinsListViewModel.items.length, - itemBuilder: (_, __, index) { - final item = unspentCoinsListViewModel.items[index]; + return Container( + padding: EdgeInsets.fromLTRB(24, 12, 24, 24), + child: Observer( + builder: (_) => ListView.separated( + itemCount: unspentCoinsListViewModel.items.length, + separatorBuilder: (_, __) => + SizedBox(height: 15), + itemBuilder: (_, int index) { + final item = unspentCoinsListViewModel.items[index]; - return GestureDetector( - onTap: () {print('Item taped');}, - child: UnspentCoinsListItem( - address: item.address, - amount: item.amount, - isFrozen: item.isFrozen, - note: item.note, - isSending: item.isSending, - onCheckBoxTap: (value) {print('CheckBox taped');}, - )); - })); + return GestureDetector( + onTap: () {print('Item taped');}, + child: UnspentCoinsListItem( + address: item.address, + amount: item.amount, + isSending: item.isSending, + onCheckBoxTap: (value) {}, + )); + } + ) + ) + ); } - } \ No newline at end of file diff --git a/lib/src/screens/unspent_coins/widgets/unspent_coins_list_item.dart b/lib/src/screens/unspent_coins/widgets/unspent_coins_list_item.dart index eb8ba33c0..790e19c77 100644 --- a/lib/src/screens/unspent_coins/widgets/unspent_coins_list_item.dart +++ b/lib/src/screens/unspent_coins/widgets/unspent_coins_list_item.dart @@ -1,74 +1,101 @@ import 'package:auto_size_text/auto_size_text.dart'; +import 'package:cake_wallet/palette.dart'; import 'package:cake_wallet/src/widgets/standard_checkbox.dart'; import 'package:flutter/material.dart'; import 'package:flutter/cupertino.dart'; -class UnspentCoinsListItem extends StatelessWidget { +class UnspentCoinsListItem extends StatefulWidget { UnspentCoinsListItem({ @required this.address, @required this.amount, - @required this.isFrozen, - @required this.note, @required this.isSending, @required this.onCheckBoxTap, -}); + }); final String address; final String amount; - final bool isFrozen; - final String note; final bool isSending; final Function(bool) onCheckBoxTap; + @override UnspentCoinsListItemState createState() => + UnspentCoinsListItemState( + address: address, + amount: amount, + isSending: isSending, + onCheckBoxTap: onCheckBoxTap + ); + +} + +class UnspentCoinsListItemState extends State<UnspentCoinsListItem> { + UnspentCoinsListItemState({ + @required this.address, + @required this.amount, + @required this.isSending, + @required this.onCheckBoxTap, + }) : checkBoxValue = isSending; + + static const amountColor = Palette.darkBlueCraiola; + static const addressColor = Palette.darkGray; + static const selectedItemColor = Palette.paleCornflowerBlue; + static const unselectedItemColor = Palette.moderateLavender; + + final String address; + final String amount; + final bool isSending; + final Function(bool) onCheckBoxTap; + + bool checkBoxValue; + @override Widget build(BuildContext context) { - final textStyle = TextStyle( - fontSize: 16, - fontWeight: FontWeight.w500, - color: Theme.of(context) - .primaryTextTheme - .title - .color); + final itemColor = checkBoxValue? selectedItemColor : unselectedItemColor; return Container( - padding: EdgeInsets.fromLTRB(24, 16, 24, 16), - color: Theme.of(context).backgroundColor, - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, + height: 62, + padding: EdgeInsets.all(12), + decoration: BoxDecoration( + borderRadius: BorderRadius.all(Radius.circular(12)), + color: itemColor), + child: Row( + mainAxisSize: MainAxisSize.max, + crossAxisAlignment: CrossAxisAlignment.center, children: [ - Text( - address ?? 'Address', - style: textStyle, - ), Padding( - padding: EdgeInsets.only(top: 12), - child: Text( - amount ?? 'Amount', - style: textStyle, - ) - ), - if (isFrozen ?? false) Padding( - padding: EdgeInsets.only(top: 12), - child: Text( - 'Freeze', - style: textStyle, - ) - ), - if (note?.isNotEmpty ?? false) Padding( - padding: EdgeInsets.only(top: 12), - child: AutoSizeText( - note, - style: textStyle, - maxLines: 1 - ) - ), - Padding( - padding: EdgeInsets.only(top: 12), + padding: EdgeInsets.only(right: 12), child: StandardCheckbox( - value: isSending, - caption: 'Sending', - onChanged: onCheckBoxTap + value: checkBoxValue, + onChanged: (value) { + onCheckBoxTap(value); + checkBoxValue = value; + setState(() {}); + } ) + ), + Expanded( + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + AutoSizeText( + amount ?? 'Amount', + style: TextStyle( + color: amountColor, + fontSize: 16, + fontWeight: FontWeight.w600 + ), + maxLines: 1, + ), + AutoSizeText( + address ?? 'Address', + style: TextStyle( + color: addressColor, + fontSize: 12, + ), + maxLines: 1, + ) + ] + ) ) ], ) diff --git a/lib/src/widgets/standard_checkbox.dart b/lib/src/widgets/standard_checkbox.dart index 64ae95acc..9deec340b 100644 --- a/lib/src/widgets/standard_checkbox.dart +++ b/lib/src/widgets/standard_checkbox.dart @@ -44,9 +44,6 @@ class StandardCheckboxState extends State<StandardCheckbox> { Container( height: 24.0, width: 24.0, - margin: EdgeInsets.only( - right: 10.0, - ), decoration: BoxDecoration( border: Border.all( color: Theme.of(context) @@ -65,14 +62,17 @@ class StandardCheckboxState extends State<StandardCheckbox> { ) : Offstage(), ), - Text( - caption, - style: TextStyle( - fontSize: 16.0, - color: Theme.of(context) - .primaryTextTheme - .title - .color), + if (caption.isNotEmpty) Padding( + padding: EdgeInsets.only(left: 10), + child: Text( + caption, + style: TextStyle( + fontSize: 16.0, + color: Theme.of(context) + .primaryTextTheme + .title + .color), + ) ) ], ), diff --git a/lib/view_model/send/send_view_model.dart b/lib/view_model/send/send_view_model.dart index 689032223..a9e6aaf93 100644 --- a/lib/view_model/send/send_view_model.dart +++ b/lib/view_model/send/send_view_model.dart @@ -55,6 +55,8 @@ abstract class SendViewModelBase with Store { _settingsStore.priority[_wallet.type] = priorities.first; } + isBitcoinWallet = _wallet is BitcoinWallet; + _setCryptoNumMaximumFractionDigits(); } @@ -182,6 +184,9 @@ abstract class SendViewModelBase with Store { @observable PendingTransaction pendingTransaction; + @observable + bool isBitcoinWallet; + @computed String get balance => _wallet.balance.formattedAvailableBalance ?? '0.0'; diff --git a/lib/view_model/unspent_coins/unspent_coins_list_view_model.dart b/lib/view_model/unspent_coins/unspent_coins_list_view_model.dart index 5f631e52a..25167f785 100644 --- a/lib/view_model/unspent_coins/unspent_coins_list_view_model.dart +++ b/lib/view_model/unspent_coins/unspent_coins_list_view_model.dart @@ -5,32 +5,45 @@ part 'unspent_coins_list_view_model.g.dart'; const List<Map<String, dynamic>> unspentCoinsMap = [ <String, dynamic>{ - "address" : "11111111111111121111132432432432432432432443124324324234324324324324332424", - "amount" : "222", + "address" : "bc1qm80mu5p3mf04a7cj7teymasf04dwpc3av2fwtr", + "amount" : "0.00358 BTC", "isFrozen" : true, "note" : "333cvgf23132132132132131321321314rwrtdggfdddewq ewqasfdxgdhgfgfszczcxgbhhhbcgbc"}, <String, dynamic>{ - "address" : "444", - "amount" : "555", + "address" : "bc1qm80mu5p3mf04a7cj7teymasf04dwpc3av2fwtr", + "amount" : "0.00567894 BTC", "note" : "sfjskf"}, <String, dynamic>{ - "address" : "777", - "amount" : "888", + "address" : "bc1qm80mu5p3mf04a7cj7teymasf04dwpc3av2fwtr", + "amount" : "0.00087 BTC", "isFrozen" : false}, <String, dynamic>{ - "address" : "11111111111111121111132432432432432432432443124324324234324324324324332424", - "amount" : "222", + "address" : "bc1qm80mu5p3mf04a7cj7teymasf04dwpc3av2fwtr", + "amount" : "0.00012 BTC", "isFrozen" : true, "note" : "333cvgf23132132132132131321321314rwrtdggfdddewq ewqasfdxgdhgfgfszczcxgbhhhbcgbc"}, <String, dynamic>{ - "address" : "444", - "amount" : "555", + "address" : "bc1qm80mu5p3mf04a7cj7teymasf04dwpc3av2fwtr", + "amount" : "0.00574 BTC", "note" : "sffsfsdsgs"}, <String, dynamic>{ - "address" : "777", - "amount" : "888", + "address" : "bc1qm80mu5p3mf04a7cj7teymasf04dwpc3av2fwtr", + "amount" : "0.000482 BTC", "isFrozen" : false}, <String, dynamic>{}, + <String, dynamic>{ + "address" : "bc1qm80mu5p3mf04a7cj7teymasf04dwpc3av2fwtr", + "amount" : "0.00012 BTC", + "isFrozen" : true, + "note" : "333cvgf23132132132132131321321314rwrtdggfdddewq ewqasfdxgdhgfgfszczcxgbhhhbcgbc"}, + <String, dynamic>{ + "address" : "bc1qm80mu5p3mf04a7cj7teymasf04dwpc3av2fwtr", + "amount" : "0.00574 BTC", + "note" : "sffsfsdsgs"}, + <String, dynamic>{ + "address" : "bc1qm80mu5p3mf04a7cj7teymasf04dwpc3av2fwtr", + "amount" : "0.000482 BTC", + "isFrozen" : false}, ]; class UnspentCoinsListViewModel = UnspentCoinsListViewModelBase with _$UnspentCoinsListViewModel; From 61375468a5ff43f964de4d77650faa92b7030143 Mon Sep 17 00:00:00 2001 From: OleksandrSobol <dr.alexander.sobol@gmail.com> Date: Wed, 2 Jun 2021 17:36:12 +0300 Subject: [PATCH 10/28] CAKE-329 | show unspent coins alert when unspent coins list page is started --- .../unspent_coins_list_page.dart | 92 +++++++++++++------ 1 file changed, 63 insertions(+), 29 deletions(-) diff --git a/lib/src/screens/unspent_coins/unspent_coins_list_page.dart b/lib/src/screens/unspent_coins/unspent_coins_list_page.dart index 81dbaf104..d5c5eff05 100644 --- a/lib/src/screens/unspent_coins/unspent_coins_list_page.dart +++ b/lib/src/screens/unspent_coins/unspent_coins_list_page.dart @@ -28,15 +28,7 @@ class UnspentCoinsListPage extends BasePage { highlightColor: Colors.transparent, splashColor: Colors.transparent, padding: EdgeInsets.all(0), - onPressed: () => showPopUp<void>( - context: context, - builder: (BuildContext context) { - return AlertWithOneAction( - alertTitle: '', - alertContent: 'Information about unspent coins', - buttonText: S.of(context).ok, - buttonAction: () => Navigator.of(context).pop()); - }), + onPressed: () => showUnspentCoinsAlert(context), child: questionImage), ), ); @@ -45,28 +37,70 @@ class UnspentCoinsListPage extends BasePage { final UnspentCoinsListViewModel unspentCoinsListViewModel; @override - Widget body(BuildContext context) { - return Container( - padding: EdgeInsets.fromLTRB(24, 12, 24, 24), - child: Observer( - builder: (_) => ListView.separated( - itemCount: unspentCoinsListViewModel.items.length, - separatorBuilder: (_, __) => - SizedBox(height: 15), - itemBuilder: (_, int index) { - final item = unspentCoinsListViewModel.items[index]; + Widget body(BuildContext context) => + UnspentCoinsListForm(unspentCoinsListViewModel); +} - return GestureDetector( - onTap: () {print('Item taped');}, - child: UnspentCoinsListItem( - address: item.address, - amount: item.amount, - isSending: item.isSending, - onCheckBoxTap: (value) {}, - )); - } +class UnspentCoinsListForm extends StatefulWidget { + UnspentCoinsListForm(this.unspentCoinsListViewModel); + + final UnspentCoinsListViewModel unspentCoinsListViewModel; + + @override + UnspentCoinsListFormState createState() => + UnspentCoinsListFormState(unspentCoinsListViewModel); +} + +class UnspentCoinsListFormState extends State<UnspentCoinsListForm> { + UnspentCoinsListFormState(this.unspentCoinsListViewModel); + + final UnspentCoinsListViewModel unspentCoinsListViewModel; + + @override + void initState() { + super.initState(); + WidgetsBinding.instance.addPostFrameCallback(afterLayout); + } + + void afterLayout(dynamic _) { + showUnspentCoinsAlert(context); + } + + @override + Widget build(BuildContext context) { + return Container( + padding: EdgeInsets.fromLTRB(24, 12, 24, 24), + child: Observer( + builder: (_) => ListView.separated( + itemCount: unspentCoinsListViewModel.items.length, + separatorBuilder: (_, __) => + SizedBox(height: 15), + itemBuilder: (_, int index) { + final item = unspentCoinsListViewModel.items[index]; + + return GestureDetector( + onTap: () {print('Item taped');}, + child: UnspentCoinsListItem( + address: item.address, + amount: item.amount, + isSending: item.isSending, + onCheckBoxTap: (value) {}, + )); + } + ) ) - ) ); } +} + +void showUnspentCoinsAlert(BuildContext context) { + showPopUp<void>( + context: context, + builder: (BuildContext context) { + return AlertWithOneAction( + alertTitle: '', + alertContent: 'Information about unspent coins', + buttonText: S.of(context).ok, + buttonAction: () => Navigator.of(context).pop()); + }); } \ No newline at end of file From 52b1abcbdb47be080b31b615685589a19191126a Mon Sep 17 00:00:00 2001 From: OleksandrSobol <dr.alexander.sobol@gmail.com> Date: Wed, 2 Jun 2021 19:54:25 +0300 Subject: [PATCH 11/28] CAKE-330 | merged CAKE-329 branch into current --- lib/src/screens/unspent_coins/unspent_coins_details_page.dart | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/src/screens/unspent_coins/unspent_coins_details_page.dart b/lib/src/screens/unspent_coins/unspent_coins_details_page.dart index 9f73eaaa3..f5e0f8991 100644 --- a/lib/src/screens/unspent_coins/unspent_coins_details_page.dart +++ b/lib/src/screens/unspent_coins/unspent_coins_details_page.dart @@ -27,7 +27,9 @@ class UnspentCoinsDetailsPage extends BasePage { final item = unspentCoinsDetailsViewModel.items[index]; if (item is StandartListItem) { - return StandartListRow(title: '${item.title}:', value: item.value); + return StandartListRow( + title: '${item.title}:', + value: item.value); } if (item is TextFieldListItem) { From 7ddbc6b8f420cea82b10291b7408ffdfaf617fdc Mon Sep 17 00:00:00 2001 From: OleksandrSobol <dr.alexander.sobol@gmail.com> Date: Tue, 15 Jun 2021 22:43:50 +0300 Subject: [PATCH 12/28] CAKE-192 | added verification of open alias and unstoppable domains in exchange_page.dart and send_page.dart --- lib/src/screens/exchange/exchange_page.dart | 23 +++++++++------ lib/src/screens/send/send_page.dart | 31 +++++++++++++++------ res/values/strings_de.arb | 2 +- res/values/strings_en.arb | 2 +- 4 files changed, 39 insertions(+), 19 deletions(-) diff --git a/lib/src/screens/exchange/exchange_page.dart b/lib/src/screens/exchange/exchange_page.dart index 853ff9876..7b57c251f 100644 --- a/lib/src/screens/exchange/exchange_page.dart +++ b/lib/src/screens/exchange/exchange_page.dart @@ -778,15 +778,22 @@ class ExchangePage extends BasePage { Future<String> applyUnstoppableDomainAddress(BuildContext context, String domain, String ticker) async { - try { - final address = - await exchangeViewModel.getUnstoppableDomainAddress(domain, ticker); - if ((address != null)&&address.isNotEmpty) { - unstoppableDomainAddressAlert(context, domain); - return address; + const topLevelDomain = 'crypto'; + + if (domain.contains('.')) { + final name = domain.split('.').last; + if (name.isNotEmpty && (name == topLevelDomain)) { + try { + final address = + await exchangeViewModel.getUnstoppableDomainAddress(domain, ticker); + if ((address != null)&&address.isNotEmpty) { + unstoppableDomainAddressAlert(context, domain); + return address; + } + } catch (e) { + print(e.toString()); + } } - } catch (e) { - print(e.toString()); } return domain; diff --git a/lib/src/screens/send/send_page.dart b/lib/src/screens/send/send_page.dart index e40657b1d..c3072b725 100644 --- a/lib/src/screens/send/send_page.dart +++ b/lib/src/screens/send/send_page.dart @@ -38,10 +38,9 @@ class SendPage extends BasePage { _cryptoAmountFocus = FocusNode(), _fiatAmountFocus = FocusNode(), _addressFocusNode = FocusNode() { - _addressFocusNode.addListener(() async { + _addressFocusNode.addListener(() { if (!_addressFocusNode.hasFocus && _addressController.text.isNotEmpty) { - await getOpenaliasRecord(_addressFocusNode.context); - await applyUnstoppableDomainAddress(_addressFocusNode.context); + applyOpenaliasOrUnstoppableDomains(_addressFocusNode.context); } }); } @@ -174,9 +173,8 @@ class SendPage extends BasePage { .headline .decorationColor), validator: sendViewModel.addressValidator, - onPushPasteButton: (context) async { - await getOpenaliasRecord(context); - await applyUnstoppableDomainAddress(context); + onPushPasteButton: (context) { + applyOpenaliasOrUnstoppableDomains(context); }, ), Observer( @@ -518,13 +516,12 @@ class SendPage extends BasePage { to: template.name, amount: template.amount, from: template.cryptoCurrency, - onTap: () async { + onTap: () { _addressController.text = template.address; _cryptoAmountController.text = template.amount; - await getOpenaliasRecord(context); - await applyUnstoppableDomainAddress(context); + applyOpenaliasOrUnstoppableDomains(context); }, onRemove: () { showPopUp<void>( @@ -792,4 +789,20 @@ class SendPage extends BasePage { print(e.toString()); } } + + void applyOpenaliasOrUnstoppableDomains(BuildContext context) async { + const topLevelDomain = 'crypto'; + final address = _addressController.text; + + if (address.contains('.')) { + final name = address.split('.').last; + if (name.isNotEmpty) { + if (name == topLevelDomain) { + await applyUnstoppableDomainAddress(context); + } else { + await getOpenaliasRecord(context); + } + } + } + } } diff --git a/res/values/strings_de.arb b/res/values/strings_de.arb index 5df364ff8..99c6f53b9 100644 --- a/res/values/strings_de.arb +++ b/res/values/strings_de.arb @@ -480,7 +480,7 @@ "moonpay_alert_text" : "Der Wert des Betrags muss größer oder gleich sein ${minAmount} ${fiatCurrency}", "outdated_electrum_wallet_receive_warning": "Wenn diese Brieftasche einen 12-Wort-Seed hat und in Cake erstellt wurde, zahlen Sie KEINE Bitcoins in diese Brieftasche ein. Alle auf diese Wallet übertragenen BTC können verloren gehen. Erstellen Sie eine neue 24-Wort-Wallet (tippen Sie auf das Menü oben rechts, wählen Sie Wallets, wählen Sie Create New Wallet und dann Bitcoin) und verschieben Sie Ihre BTC SOFORT dorthin. Neue (24 Wörter) BTC-Wallets von Cake sind sicher", - "do_not_show_me": "Zeig mir das nicht noch einmal" + "do_not_show_me": "Zeig mir das nicht noch einmal", "outdated_electrum_wallet_description" : "Neue Bitcoin-Geldbörsen, die in Cake erstellt wurden, haben jetzt einen Startwert von 24 Wörtern. Es ist obligatorisch, dass Sie eine neue Bitcoin-Brieftasche erstellen, Ihr gesamtes Geld in die neue 24-Wörter-Brieftasche überweisen und keine Brieftaschen mit einem 12-Wörter-Startwert mehr verwenden. Bitte tun Sie dies sofort, um Ihr Geld zu sichern.", "understand" : "Ich verstehe", diff --git a/res/values/strings_en.arb b/res/values/strings_en.arb index 3dd03a13c..922e00f6a 100644 --- a/res/values/strings_en.arb +++ b/res/values/strings_en.arb @@ -483,7 +483,7 @@ "moonpay_alert_text" : "Value of the amount must be more or equal to ${minAmount} ${fiatCurrency}", "outdated_electrum_wallet_receive_warning": "If this wallet has a 12-word seed and was created in Cake, DO NOT deposit Bitcoin into this wallet. Any BTC transferred to this wallet may be lost. Create a new 24-word wallet (tap the menu at the top right, select Wallets, choose Create New Wallet, then select Bitcoin) and IMMEDIATELY move your BTC there. New (24-word) BTC wallets from Cake are secure", - "do_not_show_me": "Do not show me this again" + "do_not_show_me": "Do not show me this again", "address_detected" : "Address detected", "address_from_domain" : "You got address from unstoppable domain ${domain}" From 3a5932f3dfc629209536004f7fae26d2d2546268 Mon Sep 17 00:00:00 2001 From: OleksandrSobol <dr.alexander.sobol@gmail.com> Date: Wed, 16 Jun 2021 11:44:20 +0300 Subject: [PATCH 13/28] CAKE-192 | renamed applyUnstoppableDomainAddress() to getUnstoppableDomainAddress() in the send_page.dart; fixed applyOpenaliasOrUnstoppableDomains() (send_page.dart) and applyUnstoppableDomainAddress() (exchange_page.dart) --- lib/src/screens/exchange/exchange_page.dart | 2 +- lib/src/screens/send/send_page.dart | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/src/screens/exchange/exchange_page.dart b/lib/src/screens/exchange/exchange_page.dart index 7b57c251f..aa81bd1d4 100644 --- a/lib/src/screens/exchange/exchange_page.dart +++ b/lib/src/screens/exchange/exchange_page.dart @@ -782,7 +782,7 @@ class ExchangePage extends BasePage { if (domain.contains('.')) { final name = domain.split('.').last; - if (name.isNotEmpty && (name == topLevelDomain)) { + if (name == topLevelDomain) { try { final address = await exchangeViewModel.getUnstoppableDomainAddress(domain, ticker); diff --git a/lib/src/screens/send/send_page.dart b/lib/src/screens/send/send_page.dart index c3072b725..5a755f26d 100644 --- a/lib/src/screens/send/send_page.dart +++ b/lib/src/screens/send/send_page.dart @@ -774,7 +774,7 @@ class SendPage extends BasePage { context: context); } - Future<void> applyUnstoppableDomainAddress(BuildContext context) async { + Future<void> getUnstoppableDomainAddress(BuildContext context) async { try { final address = await sendViewModel .getUnstoppableDomainAddress( @@ -798,7 +798,7 @@ class SendPage extends BasePage { final name = address.split('.').last; if (name.isNotEmpty) { if (name == topLevelDomain) { - await applyUnstoppableDomainAddress(context); + await getUnstoppableDomainAddress(context); } else { await getOpenaliasRecord(context); } From 6709b7779a9692cfafd8186439b7e9ca1dfdfde0 Mon Sep 17 00:00:00 2001 From: OleksandrSobol <dr.alexander.sobol@gmail.com> Date: Fri, 18 Jun 2021 15:36:49 +0300 Subject: [PATCH 14/28] CAKE-343 | applied subaddresses for contact book; added addresses field to wallet_info.dart; added updateAddressesInfo() to wallet_base.dart; applied updateAddressesInfo() to wallet services and view models --- lib/bitcoin/bitcoin_wallet_service.dart | 1 + lib/bitcoin/electrum_wallet.dart | 2 ++ lib/bitcoin/litecoin_wallet_service.dart | 1 + lib/core/wallet_base.dart | 9 +++++++ lib/di.dart | 1 + lib/entities/wallet_info.dart | 3 +++ lib/monero/monero_wallet.dart | 22 +++++++++++++++++ lib/monero/monero_wallet_service.dart | 1 + .../screens/contact/contact_list_page.dart | 24 ++++++++++--------- .../contact_list/contact_list_view_model.dart | 22 +++++++++++++---- ...ero_account_edit_or_create_view_model.dart | 10 ++++++-- ...let_address_edit_or_create_view_model.dart | 2 ++ lib/view_model/wallet_creation_vm.dart | 1 + 13 files changed, 81 insertions(+), 18 deletions(-) diff --git a/lib/bitcoin/bitcoin_wallet_service.dart b/lib/bitcoin/bitcoin_wallet_service.dart index aefe0fadf..2cd3310fd 100644 --- a/lib/bitcoin/bitcoin_wallet_service.dart +++ b/lib/bitcoin/bitcoin_wallet_service.dart @@ -44,6 +44,7 @@ class BitcoinWalletService extends WalletService< final wallet = await BitcoinWalletBase.open( password: password, name: name, walletInfo: walletInfo); await wallet.init(); + await wallet.updateAddressesInfo(); return wallet; } diff --git a/lib/bitcoin/electrum_wallet.dart b/lib/bitcoin/electrum_wallet.dart index bf3dfd542..bb0ee3c3e 100644 --- a/lib/bitcoin/electrum_wallet.dart +++ b/lib/bitcoin/electrum_wallet.dart @@ -125,6 +125,8 @@ abstract class ElectrumWalletBase extends WalletBase<ElectrumBalance, address = addresses[_accountIndex].address; + await updateAddressesInfo(); + await save(); } diff --git a/lib/bitcoin/litecoin_wallet_service.dart b/lib/bitcoin/litecoin_wallet_service.dart index 053fd785f..071baf4af 100644 --- a/lib/bitcoin/litecoin_wallet_service.dart +++ b/lib/bitcoin/litecoin_wallet_service.dart @@ -45,6 +45,7 @@ class LitecoinWalletService extends WalletService< final wallet = await LitecoinWalletBase.open( password: password, name: name, walletInfo: walletInfo); await wallet.init(); + await wallet.updateAddressesInfo(); return wallet; } diff --git a/lib/core/wallet_base.dart b/lib/core/wallet_base.dart index ced918342..9e7b89e8d 100644 --- a/lib/core/wallet_base.dart +++ b/lib/core/wallet_base.dart @@ -66,4 +66,13 @@ abstract class WalletBase< Future<void> rescan({int height}); void close(); + + Future<void> updateAddressesInfo() async { + try { + walletInfo.address = address; + await walletInfo.save(); + } catch (e) { + print(e.toString()); + } + } } diff --git a/lib/di.dart b/lib/di.dart index 37b6c5968..1f5350370 100644 --- a/lib/di.dart +++ b/lib/di.dart @@ -362,6 +362,7 @@ Future setup( AccountListItem, void>( (AccountListItem account, _) => MoneroAccountEditOrCreateViewModel( (getIt.get<AppStore>().wallet as MoneroWallet).accountList, + wallet: getIt.get<AppStore>().wallet, accountListItem: account)); getIt.registerFactoryParam<MoneroAccountEditOrCreatePage, AccountListItem, diff --git a/lib/entities/wallet_info.dart b/lib/entities/wallet_info.dart index e29f60528..9dfb2011b 100644 --- a/lib/entities/wallet_info.dart +++ b/lib/entities/wallet_info.dart @@ -53,5 +53,8 @@ class WalletInfo extends HiveObject { @HiveField(8) String address; + @HiveField(10) + Map<String, String> addresses; + DateTime get date => DateTime.fromMillisecondsSinceEpoch(timestamp); } diff --git a/lib/monero/monero_wallet.dart b/lib/monero/monero_wallet.dart index e902f1080..753dff53b 100644 --- a/lib/monero/monero_wallet.dart +++ b/lib/monero/monero_wallet.dart @@ -445,4 +445,26 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance, print(e.toString()); } } + + @override + Future<void> updateAddressesInfo() async { + final Map<String, String> _addresses = {}; + final _subaddressList = MoneroSubaddressList(); + + accountList.accounts.forEach((account) { + _subaddressList.update(accountIndex: account.id); + _subaddressList.subaddresses.forEach((subaddress) { + _addresses.addAll({subaddress.address:subaddress.label}); + }); + }); + + try { + walletInfo.addresses = _addresses; + walletInfo.address = address; + + await walletInfo.save(); + } catch (e) { + print(e.toString()); + } + } } diff --git a/lib/monero/monero_wallet_service.dart b/lib/monero/monero_wallet_service.dart index 7795b8700..17b02dcbb 100644 --- a/lib/monero/monero_wallet_service.dart +++ b/lib/monero/monero_wallet_service.dart @@ -126,6 +126,7 @@ class MoneroWalletService extends WalletService< } await wallet.init(); + await wallet.updateAddressesInfo(); return wallet; } catch (e) { diff --git a/lib/src/screens/contact/contact_list_page.dart b/lib/src/screens/contact/contact_list_page.dart index bf2b593ee..fd126981a 100644 --- a/lib/src/screens/contact/contact_list_page.dart +++ b/lib/src/screens/contact/contact_list_page.dart @@ -151,17 +151,19 @@ class ContactListPage extends BasePage { crossAxisAlignment: CrossAxisAlignment.center, children: <Widget>[ image ?? Offstage(), - Padding( - padding: image != null - ? EdgeInsets.only(left: 12) - : EdgeInsets.only(left: 0), - child: Text( - contact.name, - style: TextStyle( - fontSize: 14, - fontWeight: FontWeight.normal, - color: Theme.of(context).primaryTextTheme.title.color), - ), + Expanded( + child: Padding( + padding: image != null + ? EdgeInsets.only(left: 12) + : EdgeInsets.only(left: 0), + child: Text( + contact.name, + style: TextStyle( + fontSize: 14, + fontWeight: FontWeight.normal, + color: Theme.of(context).primaryTextTheme.title.color), + ), + ) ) ], ), diff --git a/lib/view_model/contact_list/contact_list_view_model.dart b/lib/view_model/contact_list/contact_list_view_model.dart index 17083efe2..fd6196c89 100644 --- a/lib/view_model/contact_list/contact_list_view_model.dart +++ b/lib/view_model/contact_list/contact_list_view_model.dart @@ -16,11 +16,23 @@ class ContactListViewModel = ContactListViewModelBase abstract class ContactListViewModelBase with Store { ContactListViewModelBase(this.contactSource, this.walletInfoSource) : contacts = ObservableList<ContactRecord>(), - walletContacts = walletInfoSource.values - .where((info) => info.address?.isNotEmpty ?? false) - .map((info) => WalletContact( - info.address, info.name, walletTypeToCryptoCurrency(info.type))) - .toList() { + walletContacts = [] { + walletInfoSource.values.forEach((info) { + if (info.address?.isNotEmpty ?? false) { + if (info.addresses != null) { + info.addresses.forEach((address, label) { + walletContacts.add(WalletContact( + address, + info.name + ' ($label)', + walletTypeToCryptoCurrency(info.type))); + }); + } else { + walletContacts.add(WalletContact(info.address, info.name, + walletTypeToCryptoCurrency(info.type))); + } + } + }); + _subscription = contactSource.bindToListWithTransform( contacts, (Contact contact) => ContactRecord(contactSource, contact), initialFire: true); diff --git a/lib/view_model/monero_account_list/monero_account_edit_or_create_view_model.dart b/lib/view_model/monero_account_list/monero_account_edit_or_create_view_model.dart index d32f8eadd..cf999e414 100644 --- a/lib/view_model/monero_account_list/monero_account_edit_or_create_view_model.dart +++ b/lib/view_model/monero_account_list/monero_account_edit_or_create_view_model.dart @@ -1,3 +1,5 @@ +import 'package:cake_wallet/core/wallet_base.dart'; +import 'package:flutter/foundation.dart'; import 'package:mobx/mobx.dart'; import 'package:cake_wallet/core/execution_state.dart'; import 'package:cake_wallet/monero/monero_account_list.dart'; @@ -10,11 +12,12 @@ class MoneroAccountEditOrCreateViewModel = MoneroAccountEditOrCreateViewModelBas abstract class MoneroAccountEditOrCreateViewModelBase with Store { MoneroAccountEditOrCreateViewModelBase(this._moneroAccountList, - {AccountListItem accountListItem}) + {@required WalletBase wallet, AccountListItem accountListItem}) : state = InitialExecutionState(), isEdit = accountListItem != null, label = accountListItem?.label??'', - _accountListItem = accountListItem; + _accountListItem = accountListItem, + _wallet = wallet; final bool isEdit; @@ -26,6 +29,7 @@ abstract class MoneroAccountEditOrCreateViewModelBase with Store { final MoneroAccountList _moneroAccountList; final AccountListItem _accountListItem; + final WalletBase _wallet; Future<void> save() async { try { @@ -38,6 +42,8 @@ abstract class MoneroAccountEditOrCreateViewModelBase with Store { await _moneroAccountList.addAccount(label: label); } + await _wallet.updateAddressesInfo(); + state = ExecutedSuccessfullyState(); } catch (e) { state = FailureState(e.toString()); diff --git a/lib/view_model/wallet_address_list/wallet_address_edit_or_create_view_model.dart b/lib/view_model/wallet_address_list/wallet_address_edit_or_create_view_model.dart index b018c542d..6b6873cd5 100644 --- a/lib/view_model/wallet_address_list/wallet_address_edit_or_create_view_model.dart +++ b/lib/view_model/wallet_address_list/wallet_address_edit_or_create_view_model.dart @@ -54,6 +54,8 @@ abstract class WalletAddressEditOrCreateViewModelBase with Store { await _createNew(); } + await _wallet.updateAddressesInfo(); + state = AddressSavedSuccessfully(); } catch (e) { state = AddressEditOrCreateStateFailure(error: e.toString()); diff --git a/lib/view_model/wallet_creation_vm.dart b/lib/view_model/wallet_creation_vm.dart index 58ee24087..54cad29c9 100644 --- a/lib/view_model/wallet_creation_vm.dart +++ b/lib/view_model/wallet_creation_vm.dart @@ -54,6 +54,7 @@ abstract class WalletCreationVMBase with Store { await _walletInfoSource.add(walletInfo); _appStore.changeCurrentWallet(wallet); _appStore.authenticationStore.allowed(); + await wallet.updateAddressesInfo(); state = ExecutedSuccessfullyState(); } catch (e) { state = FailureState(e.toString()); From bb219e4da27b9cbf5bcd6198be6e1e653b5fc9ac Mon Sep 17 00:00:00 2001 From: OleksandrSobol <dr.alexander.sobol@gmail.com> Date: Tue, 22 Jun 2021 11:29:30 +0300 Subject: [PATCH 15/28] CAKE-192 | created parse_address_from_domain.dart; applied parseAddressFromDomain() to send_page.dart and exchange_page.dart; deleted unstoppable_domain_address_alert.dart --- lib/src/screens/exchange/exchange_page.dart | 53 +++++------------- .../send/parse_address_from_domain.dart | 53 ++++++++++++++++++ lib/src/screens/send/send_page.dart | 55 ++----------------- .../unstoppable_domain_address_alert.dart | 17 ------ .../exchange/exchange_view_model.dart | 5 -- lib/view_model/send/send_view_model.dart | 15 ----- res/values/strings_zh.arb | 8 +-- 7 files changed, 77 insertions(+), 129 deletions(-) create mode 100644 lib/src/screens/send/parse_address_from_domain.dart delete mode 100644 lib/src/screens/send/widgets/unstoppable_domain_address_alert.dart diff --git a/lib/src/screens/exchange/exchange_page.dart b/lib/src/screens/exchange/exchange_page.dart index aa81bd1d4..fa69d5da2 100644 --- a/lib/src/screens/exchange/exchange_page.dart +++ b/lib/src/screens/exchange/exchange_page.dart @@ -1,7 +1,7 @@ import 'dart:ui'; import 'package:cake_wallet/entities/sync_status.dart'; import 'package:cake_wallet/entities/wallet_type.dart'; -import 'package:cake_wallet/src/screens/send/widgets/unstoppable_domain_address_alert.dart'; +import 'package:cake_wallet/src/screens/send/parse_address_from_domain.dart'; import 'package:cake_wallet/src/widgets/standard_checkbox.dart'; import 'package:dotted_border/dotted_border.dart'; import 'package:flutter/cupertino.dart'; @@ -226,10 +226,10 @@ class ExchangePage extends BasePage { onPushPasteButton: (context) async { final domain = exchangeViewModel.depositAddress; - final ticker = - exchangeViewModel.depositCurrency.title; + final ticker = exchangeViewModel + .depositCurrency.title.toLowerCase(); exchangeViewModel.depositAddress = - await applyUnstoppableDomainAddress( + await parseAddressFromDomain( context, domain, ticker); }, ), @@ -281,10 +281,10 @@ class ExchangePage extends BasePage { onPushPasteButton: (context) async { final domain = exchangeViewModel.receiveAddress; - final ticker = - exchangeViewModel.receiveCurrency.title; + final ticker = exchangeViewModel + .receiveCurrency.title.toLowerCase(); exchangeViewModel.receiveAddress = - await applyUnstoppableDomainAddress( + await parseAddressFromDomain( context, domain, ticker); }, )), @@ -512,14 +512,14 @@ class ExchangePage extends BasePage { exchangeViewModel.isFixedRateMode = false; var domain = template.depositAddress; - var ticker = template.depositCurrency; + var ticker = template.depositCurrency.toLowerCase(); exchangeViewModel.depositAddress = - await applyUnstoppableDomainAddress(context, domain, ticker); + await parseAddressFromDomain(context, domain, ticker); domain = template.receiveAddress; - ticker = template.receiveCurrency; + ticker = template.receiveCurrency.toLowerCase(); exchangeViewModel.receiveAddress = - await applyUnstoppableDomainAddress(context, domain, ticker); + await parseAddressFromDomain(context, domain, ticker); } void _setReactions( @@ -689,9 +689,9 @@ class ExchangePage extends BasePage { if (!_depositAddressFocus.hasFocus && depositAddressController.text.isNotEmpty) { final domain = depositAddressController.text; - final ticker = exchangeViewModel.depositCurrency.title; + final ticker = exchangeViewModel.depositCurrency.title.toLowerCase(); exchangeViewModel.depositAddress = - await applyUnstoppableDomainAddress(context, domain, ticker); + await parseAddressFromDomain(context, domain, ticker); } }); @@ -699,9 +699,9 @@ class ExchangePage extends BasePage { if (!_receiveAddressFocus.hasFocus && receiveAddressController.text.isNotEmpty) { final domain = receiveAddressController.text; - final ticker = exchangeViewModel.receiveCurrency.title; + final ticker = exchangeViewModel.receiveCurrency.title.toLowerCase(); exchangeViewModel.receiveAddress = - await applyUnstoppableDomainAddress(context, domain, ticker); + await parseAddressFromDomain(context, domain, ticker); } }); @@ -775,27 +775,4 @@ class ExchangePage extends BasePage { key.currentState.addressController.text = null; } } - - Future<String> applyUnstoppableDomainAddress(BuildContext context, - String domain, String ticker) async { - const topLevelDomain = 'crypto'; - - if (domain.contains('.')) { - final name = domain.split('.').last; - if (name == topLevelDomain) { - try { - final address = - await exchangeViewModel.getUnstoppableDomainAddress(domain, ticker); - if ((address != null)&&address.isNotEmpty) { - unstoppableDomainAddressAlert(context, domain); - return address; - } - } catch (e) { - print(e.toString()); - } - } - } - - return domain; - } } diff --git a/lib/src/screens/send/parse_address_from_domain.dart b/lib/src/screens/send/parse_address_from_domain.dart new file mode 100644 index 000000000..b114ef88f --- /dev/null +++ b/lib/src/screens/send/parse_address_from_domain.dart @@ -0,0 +1,53 @@ +import 'package:cake_wallet/entities/openalias_record.dart'; +import 'package:cake_wallet/entities/unstoppable_domain_address.dart'; +import 'package:cake_wallet/src/widgets/alert_with_one_action.dart'; +import 'package:cake_wallet/utils/show_pop_up.dart'; +import 'package:flutter/material.dart'; +import 'package:cake_wallet/generated/i18n.dart'; + +const topLevelDomain = 'crypto'; + +Future<String> parseAddressFromDomain( + BuildContext context, String domain, String ticker) async { + try { + final name = domain.split('.').last; + + if (name.contains(topLevelDomain)) { + final address = await fetchUnstoppableDomainAddress(domain, ticker); + if (address.isNotEmpty) { + showAddressAlert( + context, + S.of(context).address_detected, + S.of(context).address_from_domain(domain)); + return address; + } + } else if (name.isNotEmpty) { + final record = await OpenaliasRecord.fetchAddressAndName( + OpenaliasRecord.formatDomainName(domain)); + if (record.name != null && record.name != domain) { + showAddressAlert( + context, + S.of(context).openalias_alert_title, + S.of(context).openalias_alert_content(domain)); + return record.address; + } + } + } catch (e) { + print(e.toString()); + } + + return domain; +} + +void showAddressAlert(BuildContext context, String title, String content) async { + await showPopUp<void>( + context: context, + builder: (BuildContext context) { + + return AlertWithOneAction( + alertTitle: title, + alertContent: content, + buttonText: S.of(context).ok, + buttonAction: () => Navigator.of(context).pop()); + }); +} \ No newline at end of file diff --git a/lib/src/screens/send/send_page.dart b/lib/src/screens/send/send_page.dart index 5a755f26d..92b6c54d1 100644 --- a/lib/src/screens/send/send_page.dart +++ b/lib/src/screens/send/send_page.dart @@ -1,6 +1,6 @@ import 'dart:ui'; import 'package:cake_wallet/entities/transaction_priority.dart'; -import 'package:cake_wallet/src/screens/send/widgets/unstoppable_domain_address_alert.dart'; +import 'package:cake_wallet/src/screens/send/parse_address_from_domain.dart'; import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart'; import 'package:cake_wallet/src/widgets/keyboard_done_button.dart'; import 'package:cake_wallet/src/widgets/picker.dart'; @@ -736,26 +736,6 @@ class SendPage extends BasePage { _effectsInstalled = true; } - Future<void> getOpenaliasRecord(BuildContext context) async { - final record = - await sendViewModel.decodeOpenaliasRecord(_addressController.text); - - if (record != null) { - _addressController.text = record.address; - - await showPopUp<void>( - context: context, - builder: (BuildContext context) { - return AlertWithOneAction( - alertTitle: S.of(context).openalias_alert_title, - alertContent: - S.of(context).openalias_alert_content(record.name), - buttonText: S.of(context).ok, - buttonAction: () => Navigator.of(context).pop()); - }); - } - } - Future<void> _setTransactionPriority(BuildContext context) async { final items = priorityForWalletType(sendViewModel.walletType); final selectedItem = items.indexOf(sendViewModel.transactionPriority); @@ -774,35 +754,10 @@ class SendPage extends BasePage { context: context); } - Future<void> getUnstoppableDomainAddress(BuildContext context) async { - try { - final address = await sendViewModel - .getUnstoppableDomainAddress( - _addressController.text); - - if ((address != null)&&address.isNotEmpty) { - unstoppableDomainAddressAlert( - context, _addressController.text); - _addressController.text = address; - } - } catch (e) { - print(e.toString()); - } - } - void applyOpenaliasOrUnstoppableDomains(BuildContext context) async { - const topLevelDomain = 'crypto'; - final address = _addressController.text; - - if (address.contains('.')) { - final name = address.split('.').last; - if (name.isNotEmpty) { - if (name == topLevelDomain) { - await getUnstoppableDomainAddress(context); - } else { - await getOpenaliasRecord(context); - } - } - } + final domain = _addressController.text; + final ticker = sendViewModel.currency.title.toLowerCase(); + _addressController.text = + await parseAddressFromDomain(context, domain, ticker); } } diff --git a/lib/src/screens/send/widgets/unstoppable_domain_address_alert.dart b/lib/src/screens/send/widgets/unstoppable_domain_address_alert.dart deleted file mode 100644 index 011a36794..000000000 --- a/lib/src/screens/send/widgets/unstoppable_domain_address_alert.dart +++ /dev/null @@ -1,17 +0,0 @@ -import 'package:cake_wallet/src/widgets/alert_with_one_action.dart'; -import 'package:cake_wallet/utils/show_pop_up.dart'; -import 'package:flutter/material.dart'; -import 'package:cake_wallet/generated/i18n.dart'; - -void unstoppableDomainAddressAlert(BuildContext context, String domain) async { - await showPopUp<void>( - context: context, - builder: (BuildContext context) { - - return AlertWithOneAction( - alertTitle: S.of(context).address_detected, - alertContent: S.of(context).address_from_domain(domain), - buttonText: S.of(context).ok, - buttonAction: () => Navigator.of(context).pop()); - }); -} \ No newline at end of file diff --git a/lib/view_model/exchange/exchange_view_model.dart b/lib/view_model/exchange/exchange_view_model.dart index 7f0f601ab..f3f00cd77 100644 --- a/lib/view_model/exchange/exchange_view_model.dart +++ b/lib/view_model/exchange/exchange_view_model.dart @@ -4,7 +4,6 @@ import 'package:cake_wallet/bitcoin/bitcoin_wallet.dart'; import 'package:cake_wallet/core/wallet_base.dart'; import 'package:cake_wallet/entities/crypto_currency.dart'; import 'package:cake_wallet/entities/sync_status.dart'; -import 'package:cake_wallet/entities/unstoppable_domain_address.dart'; import 'package:cake_wallet/entities/wallet_type.dart'; import 'package:cake_wallet/exchange/exchange_provider.dart'; import 'package:cake_wallet/exchange/limits.dart'; @@ -423,8 +422,4 @@ abstract class ExchangeViewModelBase with Store { }*/ isReceiveAmountEditable = false; } - - Future<String> getUnstoppableDomainAddress(String domain, String ticker) async { - return await fetchUnstoppableDomainAddress(domain, ticker.toLowerCase()); - } } diff --git a/lib/view_model/send/send_view_model.dart b/lib/view_model/send/send_view_model.dart index 850ec0b3b..3fdcbc526 100644 --- a/lib/view_model/send/send_view_model.dart +++ b/lib/view_model/send/send_view_model.dart @@ -1,17 +1,14 @@ import 'package:cake_wallet/bitcoin/bitcoin_amount_format.dart'; import 'package:cake_wallet/bitcoin/bitcoin_transaction_priority.dart'; import 'package:cake_wallet/bitcoin/electrum_wallet.dart'; -import 'package:cake_wallet/entities/balance_display_mode.dart'; import 'package:cake_wallet/entities/calculate_fiat_amount_raw.dart'; import 'package:cake_wallet/entities/transaction_description.dart'; import 'package:cake_wallet/entities/transaction_priority.dart'; -import 'package:cake_wallet/entities/unstoppable_domain_address.dart'; import 'package:cake_wallet/monero/monero_amount_format.dart'; import 'package:cake_wallet/view_model/settings/settings_view_model.dart'; import 'package:hive/hive.dart'; import 'package:intl/intl.dart'; import 'package:mobx/mobx.dart'; -import 'package:cake_wallet/entities/openalias_record.dart'; import 'package:cake_wallet/entities/template.dart'; import 'package:cake_wallet/store/templates/send_template_store.dart'; import 'package:cake_wallet/core/template_validator.dart'; @@ -21,7 +18,6 @@ import 'package:cake_wallet/core/pending_transaction.dart'; import 'package:cake_wallet/core/validator.dart'; import 'package:cake_wallet/core/wallet_base.dart'; import 'package:cake_wallet/core/execution_state.dart'; -import 'package:cake_wallet/bitcoin/bitcoin_wallet.dart'; import 'package:cake_wallet/bitcoin/bitcoin_transaction_credentials.dart'; import 'package:cake_wallet/monero/monero_wallet.dart'; import 'package:cake_wallet/monero/monero_transaction_creation_credentials.dart'; @@ -265,17 +261,6 @@ abstract class SendViewModelBase with Store { void setTransactionPriority(TransactionPriority priority) => _settingsStore.priority[_wallet.type] = priority; - Future<OpenaliasRecord> decodeOpenaliasRecord(String name) async { - final record = await OpenaliasRecord.fetchAddressAndName( - OpenaliasRecord.formatDomainName(name)); - - return record.name != name ? record : null; - } - - Future<String> getUnstoppableDomainAddress(String domain) async { - return await fetchUnstoppableDomainAddress(domain, currency.title.toLowerCase()); - } - @action void _updateFiatAmount() { try { diff --git a/res/values/strings_zh.arb b/res/values/strings_zh.arb index b5a76f9a4..d7867ca4f 100644 --- a/res/values/strings_zh.arb +++ b/res/values/strings_zh.arb @@ -482,9 +482,9 @@ "buy_with" : "一起购买", "moonpay_alert_text" : "金额的价值必须大于或等于 ${minAmount} ${fiatCurrency}", - "address_detected" : "檢測到地址", - "address_from_domain" : "您有以下地址 unstoppable domain ${domain}", - "outdated_electrum_wallet_receive_warning": "如果这个钱包有一个 12 字的种子并且是在 Cake 中创建的,不要将比特币存入这个钱包。 任何转移到此钱包的 BTC 都可能丢失。 创建一个新的 24 字钱包(点击右上角的菜单,选择钱包,选择创建新钱包,然后选择比特币)并立即将您的 BTC 移到那里。 Cake 的新(24 字)BTC 钱包是安全的", - "do_not_show_me": "不再提示" + "do_not_show_me": "不再提示", + + "address_detected" : "檢測到地址", + "address_from_domain" : "您有以下地址 unstoppable domain ${domain}" } \ No newline at end of file From dbf4897ac8d6db21bc817254bf6af60c9342ef82 Mon Sep 17 00:00:00 2001 From: OleksandrSobol <dr.alexander.sobol@gmail.com> Date: Tue, 29 Jun 2021 13:52:22 +0300 Subject: [PATCH 16/28] CAKE-344 | added MoonPay contact to support page; applied icon color change according to theme --- assets/images/moonpay.png | Bin 0 -> 1392 bytes .../widgets/settings_link_provider_cell.dart | 4 +++- lib/src/screens/support/support_page.dart | 1 + lib/view_model/settings/link_list_item.dart | 5 ++++- lib/view_model/support_view_model.dart | 17 ++++++++++++++++- 5 files changed, 24 insertions(+), 3 deletions(-) create mode 100644 assets/images/moonpay.png diff --git a/assets/images/moonpay.png b/assets/images/moonpay.png new file mode 100644 index 0000000000000000000000000000000000000000..b02af6c00d107b4f68d30a5d5d6b7ee86c81f509 GIT binary patch literal 1392 zcmeAS@N?(olHy`uVBq!ia0vp^;y^6H!3HD+Ytl-A6id3JuOkD)#(wTUiL5}rLb6AY zF9SoB8UsT^3j@P1pisjL28L1t28LG&3=CE?fMyiT*%fF5l&DCJ@J#ddWzYh$IT%<O zg&3HDEJh$?V3cA2nFeGrcri-D*+GmNP&G^p4DFc=EKoI3KpF%*fEc6)LNhI3f~yc; zzzk<gfRxT~U3?NqaTa()7Bet#3xhBt!>l<#Nv5>Skcg59UmvUF{9L`nl>DSry^7od zkS+$B3M(KpH?<^Dp&~aYuh^=>Rtc=a3djZt>nkaMm6T-LDn<APC^+XAr7D=|8R!`( z*>Nc-DA*LGq*(>IxIwi8dA3R!B_#z``ugSN<$C4Ddih1^`i7R4mih)p`bI{&Koz>h zm3bwJ6}oxF$`C_f=D4I5Cl_TFlw{`TDS*sPOv*1Uu~kw6Sp)|Vca~(PA#BPkhI$L= zL4A;nzM-ChJ~nNs6`44+fn*@s!2W_*X9F_K%D*Tx73g4)v+N9Qz!sp0A)E(MACy|0 zpHm7_9-5a~VrK-^f+mcl3uL!dKxRd1PNYj_ZfagJ$R=Ym8-1)2ST#l<X*5CBh$Mlo z(K$aSzbLpMF*z0FEwEikLQvH<`k<IViWx|>gGGVyZpUS#4^KmOT)on|-+^iNgQtsQ zhzIZ0se8Sg9R*t7n_lc_T`-M}MS9~UCy_O}wG11#Y~Q$L(<javM%Ar1ezBA_b@g_* z<pg!sYbY9Cf3LGStvG)j=aN$g3e(ch&71r8q-j^%)kluf{*zKzc-LR<dTb#Xa`~+J zL%y{J&%@s+d@-KD5xea}@rUgP)K^TXR&IZt%r3;p+WaWO(zHRpIs3ueDGO4z{yC;7 z>hdb>%CV`(^O<k5|0oNbv?gk8z?qqAbpJcZE#1c^*0>|I!11l9{%%(Ngec}WY&JVI zx}u)l=5yTt)c2A1YqgS4w%rFz+wL=1^>IZ^61tbUCA3-PsI9=PGQ}56d*=$Il=<mZ zUsJg&`S@z*_BMW|^{%hX?rrz;?kRnFv#<9-SW7{k@NUj%&CI9|J@dEzluWGOx69$< zbH#NXJx}+Etv3Cbx`8KU=3|fbW}KOa0;~UjObV{NvgL>Pkt#V$_5brG_s_9#UlhK; z@7=+v6F0x#vUbs|zO`&N_MZ=OS(o4EI^piH>}bil%*h3ZCmlEW=90{6Izz3jM{(!T z59|kK$N6&1w^x4gSN5Z}!?)>6;&1ZpV9s1U(Sjv;Ry%+B`=xXK3xAG0acZM?Ilu9d z*9nXsZv<c54mxW6aNcpnq+1ax-{%HvM+v{olzr^;JW-S}*Yb(Rwf&p^>+6e1p86qv YGw80q^r|1g;*f#C)78&qol`;+0McLD?*IS* literal 0 HcmV?d00001 diff --git a/lib/src/screens/settings/widgets/settings_link_provider_cell.dart b/lib/src/screens/settings/widgets/settings_link_provider_cell.dart index 362d5ac46..0dbd5703f 100644 --- a/lib/src/screens/settings/widgets/settings_link_provider_cell.dart +++ b/lib/src/screens/settings/widgets/settings_link_provider_cell.dart @@ -7,6 +7,7 @@ class SettingsLinkProviderCell extends StandardListRow { SettingsLinkProviderCell( {@required String title, @required this.icon, + this.iconColor, @required this.link, @required this.linkTitle}) : super(title: title, isSelected: false, onTap: (BuildContext context) => _launchUrl(link) ); @@ -14,10 +15,11 @@ class SettingsLinkProviderCell extends StandardListRow { final String icon; final String link; final String linkTitle; + final Color iconColor; @override Widget buildLeading(BuildContext context) => - icon != null ? Image.asset(icon) : null; + icon != null ? Image.asset(icon, color: iconColor) : null; @override Widget buildTrailing(BuildContext context) => Text(linkTitle, diff --git a/lib/src/screens/support/support_page.dart b/lib/src/screens/support/support_page.dart index 2c5d7ef9a..9405214c6 100644 --- a/lib/src/screens/support/support_page.dart +++ b/lib/src/screens/support/support_page.dart @@ -34,6 +34,7 @@ class SupportPage extends BasePage { return SettingsLinkProviderCell( title: item.title, icon: item.icon, + iconColor: item.iconColor, link: item.link, linkTitle: item.linkTitle); } diff --git a/lib/view_model/settings/link_list_item.dart b/lib/view_model/settings/link_list_item.dart index bf282d2ca..cfcc601c1 100644 --- a/lib/view_model/settings/link_list_item.dart +++ b/lib/view_model/settings/link_list_item.dart @@ -1,15 +1,18 @@ import 'package:flutter/foundation.dart'; import 'package:cake_wallet/view_model/settings/settings_list_item.dart'; +import 'package:flutter/material.dart'; class LinkListItem extends SettingsListItem { LinkListItem( {@required String title, @required this.link, @required this.linkTitle, - this.icon}) + this.icon, + this.iconColor}) : super(title); final String icon; final String link; final String linkTitle; + final Color iconColor; } \ No newline at end of file diff --git a/lib/view_model/support_view_model.dart b/lib/view_model/support_view_model.dart index 1937b90ff..caa360339 100644 --- a/lib/view_model/support_view_model.dart +++ b/lib/view_model/support_view_model.dart @@ -6,6 +6,9 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:mobx/mobx.dart'; import 'package:url_launcher/url_launcher.dart'; +import 'package:cake_wallet/di.dart'; +import 'package:cake_wallet/store/settings_store.dart'; +import 'package:cake_wallet/themes/theme_base.dart'; part 'support_view_model.g.dart'; @@ -13,6 +16,11 @@ class SupportViewModel = SupportViewModelBase with _$SupportViewModel; abstract class SupportViewModelBase with Store { SupportViewModelBase() { + final currentTheme = getIt.get<SettingsStore>().currentTheme; + final iconColor = currentTheme.type == ThemeType.dark + ? Colors.white + : Colors.black; + items = [ RegularListItem( title: S.current.faq, @@ -31,6 +39,7 @@ abstract class SupportViewModelBase with Store { LinkListItem( title: 'GitHub', icon: 'assets/images/github.png', + iconColor: iconColor, linkTitle: S.current.apk_update, link: 'https://github.com/cake-tech/cake_wallet/releases'), LinkListItem( @@ -52,7 +61,13 @@ abstract class SupportViewModelBase with Store { title: 'Wyre', icon: 'assets/images/wyre.png', linkTitle: S.current.submit_request, - link: 'https://wyre-support.zendesk.com/hc/en-us/requests/new') + link: 'https://wyre-support.zendesk.com/hc/en-us/requests/new'), + LinkListItem( + title: 'MoonPay', + icon: 'assets/images/moonpay.png', + iconColor: iconColor, + linkTitle: S.current.submit_request, + link: 'https://support.moonpay.com/hc/en-gb/requests/new') ]; } static const url = 'https://cakewallet.com/guide/'; From 20e0c830cf6d6703fc00d2faeec23f4404a04321 Mon Sep 17 00:00:00 2001 From: OleksandrSobol <dr.alexander.sobol@gmail.com> Date: Mon, 5 Jul 2021 16:52:24 +0300 Subject: [PATCH 17/28] CAKE-334 | applied unspent coins control to the app; added unspent_coins_info.dart; reworked createTransaction(), calculateEstimatedFee() and updateUnspent() methods in the electrum_wallet.dart; fixed unspent_coins_list_view_model.dart, unspent_coins_details_view_model.dart, unspent_coins_list_item.dart, unspent_coins_list_page.dart and unspent_coins_details_page.dart; fixed bitcoin_transaction_wrong_balance_exception.dart; added properties to bitcoin_unspent.dart; applied localization to unspent coins pages --- ...n_transaction_wrong_balance_exception.dart | 8 +- lib/bitcoin/bitcoin_unspent.dart | 8 +- lib/bitcoin/bitcoin_wallet.dart | 6 + lib/bitcoin/bitcoin_wallet_service.dart | 13 +- lib/bitcoin/electrum_wallet.dart | 185 +++++++++++++----- lib/bitcoin/litecoin_wallet.dart | 6 + lib/bitcoin/litecoin_wallet_service.dart | 13 +- lib/bitcoin/unspent_coins_info.dart | 32 +++ lib/di.dart | 41 +++- lib/main.dart | 12 +- lib/router.dart | 4 +- lib/src/screens/send/send_page.dart | 4 +- .../unspent_coins_details_page.dart | 3 +- .../unspent_coins_list_page.dart | 31 +-- .../widgets/unspent_coins_list_item.dart | 138 ++++++------- lib/src/widgets/standart_switch.dart | 1 - lib/view_model/send/send_view_model.dart | 6 +- .../unspent_coins_details_view_model.dart | 22 ++- .../unspent_coins/unspent_coins_item.dart | 35 +++- .../unspent_coins_list_view_model.dart | 97 +++++---- res/values/strings_de.arb | 7 +- res/values/strings_en.arb | 7 +- res/values/strings_es.arb | 7 +- res/values/strings_hi.arb | 7 +- res/values/strings_hr.arb | 7 +- res/values/strings_it.arb | 7 +- res/values/strings_ja.arb | 7 +- res/values/strings_ko.arb | 7 +- res/values/strings_nl.arb | 7 +- res/values/strings_pl.arb | 7 +- res/values/strings_pt.arb | 7 +- res/values/strings_ru.arb | 7 +- res/values/strings_uk.arb | 7 +- res/values/strings_zh.arb | 7 +- 34 files changed, 525 insertions(+), 238 deletions(-) create mode 100644 lib/bitcoin/unspent_coins_info.dart diff --git a/lib/bitcoin/bitcoin_transaction_wrong_balance_exception.dart b/lib/bitcoin/bitcoin_transaction_wrong_balance_exception.dart index 9d1401818..b699ade29 100644 --- a/lib/bitcoin/bitcoin_transaction_wrong_balance_exception.dart +++ b/lib/bitcoin/bitcoin_transaction_wrong_balance_exception.dart @@ -1,4 +1,10 @@ +import 'package:cake_wallet/entities/crypto_currency.dart'; + class BitcoinTransactionWrongBalanceException implements Exception { + BitcoinTransactionWrongBalanceException(this.currency); + + final CryptoCurrency currency; + @override - String toString() => 'Wrong balance. Not enough BTC on your balance.'; + String toString() => 'Wrong balance. Not enough ${currency.title} on your balance.'; } \ No newline at end of file diff --git a/lib/bitcoin/bitcoin_unspent.dart b/lib/bitcoin/bitcoin_unspent.dart index 846eb8c7d..b95ed9bc3 100644 --- a/lib/bitcoin/bitcoin_unspent.dart +++ b/lib/bitcoin/bitcoin_unspent.dart @@ -1,7 +1,10 @@ import 'package:cake_wallet/bitcoin/bitcoin_address_record.dart'; class BitcoinUnspent { - BitcoinUnspent(this.address, this.hash, this.value, this.vout); + BitcoinUnspent(this.address, this.hash, this.value, this.vout) + : isSending = true, + isFrozen = false, + note = ''; factory BitcoinUnspent.fromJSON( BitcoinAddressRecord address, Map<String, dynamic> json) => @@ -15,4 +18,7 @@ class BitcoinUnspent { bool get isP2wpkh => address.address.startsWith('bc') || address.address.startsWith('ltc'); + bool isSending; + bool isFrozen; + String note; } diff --git a/lib/bitcoin/bitcoin_wallet.dart b/lib/bitcoin/bitcoin_wallet.dart index fd8402887..022ed478d 100644 --- a/lib/bitcoin/bitcoin_wallet.dart +++ b/lib/bitcoin/bitcoin_wallet.dart @@ -1,3 +1,5 @@ +import 'package:cake_wallet/bitcoin/unspent_coins_info.dart'; +import 'package:hive/hive.dart'; import 'package:mobx/mobx.dart'; import 'package:flutter/foundation.dart'; import 'package:bitcoin_flutter/bitcoin_flutter.dart' as bitcoin; @@ -17,6 +19,7 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store { {@required String mnemonic, @required String password, @required WalletInfo walletInfo, + @required Box<UnspentCoinsInfo> unspentCoinsInfo, List<BitcoinAddressRecord> initialAddresses, ElectrumBalance initialBalance, int accountIndex = 0}) @@ -24,6 +27,7 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store { mnemonic: mnemonic, password: password, walletInfo: walletInfo, + unspentCoinsInfo: unspentCoinsInfo, networkType: bitcoin.bitcoin, initialAddresses: initialAddresses, initialBalance: initialBalance, @@ -32,6 +36,7 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store { static Future<BitcoinWallet> open({ @required String name, @required WalletInfo walletInfo, + @required Box<UnspentCoinsInfo> unspentCoinsInfo, @required String password, }) async { final snp = ElectrumWallletSnapshot(name, walletInfo.type, password); @@ -40,6 +45,7 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store { mnemonic: snp.mnemonic, password: password, walletInfo: walletInfo, + unspentCoinsInfo: unspentCoinsInfo, initialAddresses: snp.addresses, initialBalance: snp.balance, accountIndex: snp.accountIndex); diff --git a/lib/bitcoin/bitcoin_wallet_service.dart b/lib/bitcoin/bitcoin_wallet_service.dart index aefe0fadf..8e5b31930 100644 --- a/lib/bitcoin/bitcoin_wallet_service.dart +++ b/lib/bitcoin/bitcoin_wallet_service.dart @@ -2,6 +2,7 @@ import 'dart:io'; import 'package:cake_wallet/bitcoin/bitcoin_mnemonic.dart'; import 'package:cake_wallet/bitcoin/bitcoin_mnemonic_is_incorrect_exception.dart'; import 'package:cake_wallet/bitcoin/bitcoin_wallet_creation_credentials.dart'; +import 'package:cake_wallet/bitcoin/unspent_coins_info.dart'; import 'package:cake_wallet/core/wallet_base.dart'; import 'package:cake_wallet/core/wallet_service.dart'; import 'package:cake_wallet/bitcoin/bitcoin_wallet.dart'; @@ -14,9 +15,10 @@ class BitcoinWalletService extends WalletService< BitcoinNewWalletCredentials, BitcoinRestoreWalletFromSeedCredentials, BitcoinRestoreWalletFromWIFCredentials> { - BitcoinWalletService(this.walletInfoSource); + BitcoinWalletService(this.walletInfoSource, this.unspentCoinsInfoSource); final Box<WalletInfo> walletInfoSource; + final Box<UnspentCoinsInfo> unspentCoinsInfoSource; @override WalletType getType() => WalletType.bitcoin; @@ -26,7 +28,8 @@ class BitcoinWalletService extends WalletService< final wallet = BitcoinWallet( mnemonic: await generateMnemonic(), password: credentials.password, - walletInfo: credentials.walletInfo); + walletInfo: credentials.walletInfo, + unspentCoinsInfo: unspentCoinsInfoSource); await wallet.save(); await wallet.init(); return wallet; @@ -42,7 +45,8 @@ class BitcoinWalletService extends WalletService< (info) => info.id == WalletBase.idFor(name, getType()), orElse: () => null); final wallet = await BitcoinWalletBase.open( - password: password, name: name, walletInfo: walletInfo); + password: password, name: name, walletInfo: walletInfo, + unspentCoinsInfo: unspentCoinsInfoSource); await wallet.init(); return wallet; } @@ -67,7 +71,8 @@ class BitcoinWalletService extends WalletService< final wallet = BitcoinWallet( password: credentials.password, mnemonic: credentials.mnemonic, - walletInfo: credentials.walletInfo); + walletInfo: credentials.walletInfo, + unspentCoinsInfo: unspentCoinsInfoSource); await wallet.save(); await wallet.init(); return wallet; diff --git a/lib/bitcoin/electrum_wallet.dart b/lib/bitcoin/electrum_wallet.dart index bf3dfd542..43c5cd179 100644 --- a/lib/bitcoin/electrum_wallet.dart +++ b/lib/bitcoin/electrum_wallet.dart @@ -1,5 +1,7 @@ import 'dart:async'; import 'dart:convert'; +import 'package:cake_wallet/bitcoin/unspent_coins_info.dart'; +import 'package:hive/hive.dart'; import 'package:mobx/mobx.dart'; import 'package:rxdart/subjects.dart'; import 'package:flutter/foundation.dart'; @@ -38,6 +40,7 @@ abstract class ElectrumWalletBase extends WalletBase<ElectrumBalance, ElectrumWalletBase( {@required String password, @required WalletInfo walletInfo, + @required Box<UnspentCoinsInfo> unspentCoinsInfo, @required List<BitcoinAddressRecord> initialAddresses, @required this.networkType, @required this.mnemonic, @@ -59,9 +62,10 @@ abstract class ElectrumWalletBase extends WalletBase<ElectrumBalance, super(walletInfo) { this.electrumClient = electrumClient ?? ElectrumClient(); this.walletInfo = walletInfo; + this.unspentCoinsInfo = unspentCoinsInfo; transactionHistory = ElectrumTransactionHistory(walletInfo: walletInfo, password: password); - _unspent = []; + unspentCoins = []; _scripthashesUpdateSubject = {}; } @@ -72,6 +76,7 @@ abstract class ElectrumWalletBase extends WalletBase<ElectrumBalance, final String mnemonic; ElectrumClient electrumClient; + Box<UnspentCoinsInfo> unspentCoinsInfo; @override @observable @@ -103,7 +108,7 @@ abstract class ElectrumWalletBase extends WalletBase<ElectrumBalance, wif: hd.wif, privateKey: hd.privKey, publicKey: hd.pubKey); final String _password; - List<BitcoinUnspent> _unspent; + List<BitcoinUnspent> unspentCoins; List<int> _feeRates; int _accountIndex; Map<String, BehaviorSubject<Object>> _scripthashesUpdateSubject; @@ -178,10 +183,10 @@ abstract class ElectrumWalletBase extends WalletBase<ElectrumBalance, Future<void> startSync() async { try { syncStatus = StartingSyncStatus(); - updateTransactions(); + await updateTransactions(); _subscribeForUpdates(); await _updateBalance(); - await _updateUnspent(); + await updateUnspent(); _feeRates = await electrumClient.feeRates(); Timer.periodic(const Duration(minutes: 1), @@ -218,33 +223,16 @@ abstract class ElectrumWalletBase extends WalletBase<ElectrumBalance, const minAmount = 546; final transactionCredentials = credentials as BitcoinTransactionCredentials; final inputs = <BitcoinUnspent>[]; - final allAmountFee = - calculateEstimatedFee(transactionCredentials.priority, null); - final allAmount = balance.confirmed - allAmountFee; - var fee = 0; - final credentialsAmount = transactionCredentials.amount != null - ? stringDoubleToBitcoinAmount(transactionCredentials.amount) - : 0; - final amount = transactionCredentials.amount == null || - allAmount - credentialsAmount < minAmount - ? allAmount - : credentialsAmount; - final txb = bitcoin.TransactionBuilder(network: networkType); - final changeAddress = address; - var leftAmount = amount; - var totalInputAmount = 0; + var allInputsAmount = 0; - if (_unspent.isEmpty) { - await _updateUnspent(); + if (unspentCoins.isEmpty) { + await updateUnspent(); } - for (final utx in _unspent) { - leftAmount = leftAmount - utx.value; - totalInputAmount += utx.value; - inputs.add(utx); - - if (leftAmount <= 0) { - break; + for (final utx in unspentCoins) { + if (utx.isSending) { + allInputsAmount += utx.value; + inputs.add(utx); } } @@ -252,18 +240,57 @@ abstract class ElectrumWalletBase extends WalletBase<ElectrumBalance, throw BitcoinTransactionNoInputsException(); } - final totalAmount = amount + fee; - fee = transactionCredentials.amount != null - ? feeAmountForPriority(transactionCredentials.priority, inputs.length, - amount == allAmount ? 1 : 2) - : allAmountFee; + final allAmountFee = + feeAmountForPriority(transactionCredentials.priority, inputs.length, 1); + final allAmount = allInputsAmount - allAmountFee; - if (totalAmount > balance.confirmed) { - throw BitcoinTransactionWrongBalanceException(); + final credentialsAmount = transactionCredentials.amount != null + ? stringDoubleToBitcoinAmount(transactionCredentials.amount) + : 0; + final amount = transactionCredentials.amount == null || + allAmount - credentialsAmount < minAmount + ? allAmount + : credentialsAmount; + final fee = transactionCredentials.amount == null || amount == allAmount + ? allAmountFee + : calculateEstimatedFee(transactionCredentials.priority, amount); + + if (fee == 0) { + throw BitcoinTransactionWrongBalanceException(currency); } - if (amount <= 0 || totalInputAmount < amount) { - throw BitcoinTransactionWrongBalanceException(); + final totalAmount = amount + fee; + + if (totalAmount > balance.confirmed || totalAmount > allInputsAmount) { + throw BitcoinTransactionWrongBalanceException(currency); + } + + final txb = bitcoin.TransactionBuilder(network: networkType); + final changeAddress = address; + + var leftAmount = totalAmount; + var totalInputAmount = 0; + + inputs.clear(); + + for (final utx in unspentCoins) { + if (utx.isSending) { + leftAmount = leftAmount - utx.value; + totalInputAmount += utx.value; + inputs.add(utx); + + if (leftAmount <= 0) { + break; + } + } + } + + if (inputs.isEmpty) { + throw BitcoinTransactionNoInputsException(); + } + + if (amount <= 0 || totalInputAmount < totalAmount) { + throw BitcoinTransactionWrongBalanceException(currency); } txb.setVersion(1); @@ -338,17 +365,26 @@ abstract class ElectrumWalletBase extends WalletBase<ElectrumBalance, if (amount != null) { int totalValue = 0; - for (final input in _unspent) { + for (final input in unspentCoins) { if (totalValue >= amount) { break; } - totalValue += input.value; - inputsCount += 1; + if (input.isSending) { + totalValue += input.value; + inputsCount += 1; + } } + + if (totalValue < amount) return 0; } else { - inputsCount = _unspent.length; + for (final input in unspentCoins) { + if (input.isSending) { + inputsCount += 1; + } + } } + // If send all, then we have no change value return feeAmountForPriority( priority, inputsCount, amount != null ? 2 : 1); @@ -382,12 +418,73 @@ abstract class ElectrumWalletBase extends WalletBase<ElectrumBalance, Future<String> makePath() async => pathForWallet(name: walletInfo.name, type: walletInfo.type); - Future<void> _updateUnspent() async { + Future<void> updateUnspent() async { final unspent = await Future.wait(addresses.map((address) => electrumClient .getListUnspentWithAddress(address.address, networkType) .then((unspent) => unspent .map((unspent) => BitcoinUnspent.fromJSON(address, unspent))))); - _unspent = unspent.expand((e) => e).toList(); + unspentCoins = unspent.expand((e) => e).toList(); + + if (unspentCoinsInfo.isEmpty) { + unspentCoins.forEach((coin) => _addCoinInfo(coin)); + return; + } + + if (unspentCoins.isNotEmpty) { + unspentCoins.forEach((coin) { + final coinInfoList = unspentCoinsInfo.values.where((element) => + element.walletId.contains(id) && element.hash.contains(coin.hash)); + + if (coinInfoList.isNotEmpty) { + final coinInfo = coinInfoList.first; + + coin.isFrozen = coinInfo.isFrozen; + coin.isSending = coinInfo.isSending; + coin.note = coinInfo.note; + } else { + _addCoinInfo(coin); + } + }); + } + + await _refreshUnspentCoinsInfo(); + } + + Future<void> _addCoinInfo(BitcoinUnspent coin) async { + final newInfo = UnspentCoinsInfo( + walletId: id, + hash: coin.hash, + isFrozen: coin.isFrozen, + isSending: coin.isSending, + note: coin.note + ); + + await unspentCoinsInfo.add(newInfo); + } + + Future<void> _refreshUnspentCoinsInfo() async { + try { + final List<dynamic> keys = <dynamic>[]; + final currentWalletUnspentCoins = unspentCoinsInfo.values + .where((element) => element.walletId.contains(id)); + + if (currentWalletUnspentCoins.isNotEmpty) { + currentWalletUnspentCoins.forEach((element) { + final existUnspentCoins = unspentCoins + ?.where((coin) => element.hash.contains(coin?.hash)); + + if (existUnspentCoins?.isEmpty ?? true) { + keys.add(element.key); + } + }); + } + + if (keys.isNotEmpty) { + await unspentCoinsInfo.deleteAll(keys); + } + } catch (e) { + print(e.toString()); + } } Future<ElectrumTransactionInfo> fetchTransactionInfo( @@ -438,7 +535,7 @@ abstract class ElectrumWalletBase extends WalletBase<ElectrumBalance, _scripthashesUpdateSubject[sh].listen((event) async { try { await _updateBalance(); - await _updateUnspent(); + await updateUnspent(); await updateTransactions(); } catch (e) { print(e.toString()); diff --git a/lib/bitcoin/litecoin_wallet.dart b/lib/bitcoin/litecoin_wallet.dart index 209d1904e..a627d8fcb 100644 --- a/lib/bitcoin/litecoin_wallet.dart +++ b/lib/bitcoin/litecoin_wallet.dart @@ -1,8 +1,10 @@ import 'package:bitcoin_flutter/bitcoin_flutter.dart' as bitcoin; import 'package:cake_wallet/bitcoin/bitcoin_mnemonic.dart'; import 'package:cake_wallet/bitcoin/bitcoin_transaction_priority.dart'; +import 'package:cake_wallet/bitcoin/unspent_coins_info.dart'; import 'package:cake_wallet/entities/transaction_priority.dart'; import 'package:flutter/foundation.dart'; +import 'package:hive/hive.dart'; import 'package:mobx/mobx.dart'; import 'package:cake_wallet/entities/wallet_info.dart'; import 'package:cake_wallet/bitcoin/electrum_wallet_snapshot.dart'; @@ -21,6 +23,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { {@required String mnemonic, @required String password, @required WalletInfo walletInfo, + @required Box<UnspentCoinsInfo> unspentCoinsInfo, List<BitcoinAddressRecord> initialAddresses, ElectrumBalance initialBalance, int accountIndex = 0}) @@ -28,6 +31,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { mnemonic: mnemonic, password: password, walletInfo: walletInfo, + unspentCoinsInfo: unspentCoinsInfo, networkType: litecoinNetwork, initialAddresses: initialAddresses, initialBalance: initialBalance, @@ -36,6 +40,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { static Future<LitecoinWallet> open({ @required String name, @required WalletInfo walletInfo, + @required Box<UnspentCoinsInfo> unspentCoinsInfo, @required String password, }) async { final snp = ElectrumWallletSnapshot(name, walletInfo.type, password); @@ -44,6 +49,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { mnemonic: snp.mnemonic, password: password, walletInfo: walletInfo, + unspentCoinsInfo: unspentCoinsInfo, initialAddresses: snp.addresses, initialBalance: snp.balance, accountIndex: snp.accountIndex); diff --git a/lib/bitcoin/litecoin_wallet_service.dart b/lib/bitcoin/litecoin_wallet_service.dart index 053fd785f..dc5081e12 100644 --- a/lib/bitcoin/litecoin_wallet_service.dart +++ b/lib/bitcoin/litecoin_wallet_service.dart @@ -1,4 +1,5 @@ import 'dart:io'; +import 'package:cake_wallet/bitcoin/unspent_coins_info.dart'; import 'package:hive/hive.dart'; import 'package:cake_wallet/bitcoin/bitcoin_mnemonic.dart'; import 'package:cake_wallet/bitcoin/bitcoin_mnemonic_is_incorrect_exception.dart'; @@ -14,9 +15,10 @@ class LitecoinWalletService extends WalletService< BitcoinNewWalletCredentials, BitcoinRestoreWalletFromSeedCredentials, BitcoinRestoreWalletFromWIFCredentials> { - LitecoinWalletService(this.walletInfoSource); + LitecoinWalletService(this.walletInfoSource, this.unspentCoinsInfoSource); final Box<WalletInfo> walletInfoSource; + final Box<UnspentCoinsInfo> unspentCoinsInfoSource; @override WalletType getType() => WalletType.litecoin; @@ -26,7 +28,8 @@ class LitecoinWalletService extends WalletService< final wallet = LitecoinWallet( mnemonic: await generateMnemonic(), password: credentials.password, - walletInfo: credentials.walletInfo); + walletInfo: credentials.walletInfo, + unspentCoinsInfo: unspentCoinsInfoSource); await wallet.save(); await wallet.init(); @@ -43,7 +46,8 @@ class LitecoinWalletService extends WalletService< (info) => info.id == WalletBase.idFor(name, getType()), orElse: () => null); final wallet = await LitecoinWalletBase.open( - password: password, name: name, walletInfo: walletInfo); + password: password, name: name, walletInfo: walletInfo, + unspentCoinsInfo: unspentCoinsInfoSource); await wallet.init(); return wallet; } @@ -68,7 +72,8 @@ class LitecoinWalletService extends WalletService< final wallet = LitecoinWallet( password: credentials.password, mnemonic: credentials.mnemonic, - walletInfo: credentials.walletInfo); + walletInfo: credentials.walletInfo, + unspentCoinsInfo: unspentCoinsInfoSource); await wallet.save(); await wallet.init(); return wallet; diff --git a/lib/bitcoin/unspent_coins_info.dart b/lib/bitcoin/unspent_coins_info.dart new file mode 100644 index 000000000..6ce647dce --- /dev/null +++ b/lib/bitcoin/unspent_coins_info.dart @@ -0,0 +1,32 @@ +import 'package:hive/hive.dart'; + +part 'unspent_coins_info.g.dart'; + +@HiveType(typeId: UnspentCoinsInfo.typeId) +class UnspentCoinsInfo extends HiveObject { + UnspentCoinsInfo({ + this.walletId, + this.hash, + this.isFrozen, + this.isSending, + this.note}); + + static const typeId = 9; + static const boxName = 'Unspent'; + static const boxKey = 'unspentBoxKey'; + + @HiveField(0) + String walletId; + + @HiveField(1) + String hash; + + @HiveField(2) + bool isFrozen; + + @HiveField(3) + bool isSending; + + @HiveField(4) + String note; +} \ No newline at end of file diff --git a/lib/di.dart b/lib/di.dart index d887d8832..82ac29b6d 100644 --- a/lib/di.dart +++ b/lib/di.dart @@ -1,5 +1,6 @@ import 'package:cake_wallet/bitcoin/bitcoin_wallet_service.dart'; import 'package:cake_wallet/bitcoin/litecoin_wallet_service.dart'; +import 'package:cake_wallet/bitcoin/unspent_coins_info.dart'; import 'package:cake_wallet/core/backup_service.dart'; import 'package:cake_wallet/core/wallet_service.dart'; import 'package:cake_wallet/entities/biometric_auth.dart'; @@ -130,6 +131,7 @@ Box<Template> _templates; Box<ExchangeTemplate> _exchangeTemplates; Box<TransactionDescription> _transactionDescriptionBox; Box<Order> _ordersSource; +Box<UnspentCoinsInfo> _unspentCoinsInfoSource; Future setup( {Box<WalletInfo> walletInfoSource, @@ -139,7 +141,8 @@ Future setup( Box<Template> templates, Box<ExchangeTemplate> exchangeTemplates, Box<TransactionDescription> transactionDescriptionBox, - Box<Order> ordersSource}) async { + Box<Order> ordersSource, + Box<UnspentCoinsInfo> unspentCoinsInfoSource}) async { _walletInfoSource = walletInfoSource; _nodeSource = nodeSource; _contactSource = contactSource; @@ -148,6 +151,7 @@ Future setup( _exchangeTemplates = exchangeTemplates; _transactionDescriptionBox = transactionDescriptionBox; _ordersSource = ordersSource; + _unspentCoinsInfoSource = unspentCoinsInfoSource; if (!_isSetupFinished) { getIt.registerSingletonAsync<SharedPreferences>( @@ -450,9 +454,11 @@ Future setup( getIt.registerFactory(() => MoneroWalletService(_walletInfoSource)); - getIt.registerFactory(() => BitcoinWalletService(_walletInfoSource)); + getIt.registerFactory(() => + BitcoinWalletService(_walletInfoSource, _unspentCoinsInfoSource)); - getIt.registerFactory(() => LitecoinWalletService(_walletInfoSource)); + getIt.registerFactory(() => + LitecoinWalletService(_walletInfoSource, _unspentCoinsInfoSource)); getIt.registerFactoryParam<WalletService, WalletType, void>( (WalletType param1, __) { @@ -588,20 +594,35 @@ Future setup( getIt.registerFactory(() => SupportPage(getIt.get<SupportViewModel>())); - getIt.registerFactory(() => UnspentCoinsListViewModel()); + getIt.registerFactory(() { + final wallet = getIt.get<AppStore>().wallet; + + return UnspentCoinsListViewModel( + wallet: wallet, + unspentCoinsInfo: _unspentCoinsInfoSource); + }); getIt.registerFactory(() => UnspentCoinsListPage( unspentCoinsListViewModel: getIt.get<UnspentCoinsListViewModel>() )); getIt.registerFactoryParam<UnspentCoinsDetailsViewModel, - UnspentCoinsItem, void>((item, _) => - UnspentCoinsDetailsViewModel(unspentCoinsItem: item)); + UnspentCoinsItem, UnspentCoinsListViewModel>((item, model) => + UnspentCoinsDetailsViewModel( + unspentCoinsItem: item, + unspentCoinsListViewModel: model)); - getIt.registerFactoryParam<UnspentCoinsDetailsPage, - UnspentCoinsItem, void>((UnspentCoinsItem item, _) => - UnspentCoinsDetailsPage(unspentCoinsDetailsViewModel: - getIt.get<UnspentCoinsDetailsViewModel>(param1: item))); + getIt.registerFactoryParam<UnspentCoinsDetailsPage, List, void>( + (List args, _) { + final item = args.first as UnspentCoinsItem; + final unspentCoinsListViewModel = args[1] as UnspentCoinsListViewModel; + + return UnspentCoinsDetailsPage( + unspentCoinsDetailsViewModel: + getIt.get<UnspentCoinsDetailsViewModel>( + param1: item, + param2: unspentCoinsListViewModel)); + }); _isSetupFinished = true; } diff --git a/lib/main.dart b/lib/main.dart index b24943f3d..7c4b84de2 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,3 +1,4 @@ +import 'package:cake_wallet/bitcoin/unspent_coins_info.dart'; import 'package:cake_wallet/entities/language_service.dart'; import 'package:cake_wallet/buy/order.dart'; import 'package:flutter/material.dart'; @@ -75,6 +76,10 @@ Future<void> main() async { Hive.registerAdapter(OrderAdapter()); } + if (!Hive.isAdapterRegistered(UnspentCoinsInfo.typeId)) { + Hive.registerAdapter(UnspentCoinsInfoAdapter()); + } + final secureStorage = FlutterSecureStorage(); final transactionDescriptionsBoxKey = await getEncryptionKey( secureStorage: secureStorage, forKey: TransactionDescription.boxKey); @@ -95,6 +100,8 @@ Future<void> main() async { final templates = await Hive.openBox<Template>(Template.boxName); final exchangeTemplates = await Hive.openBox<ExchangeTemplate>(ExchangeTemplate.boxName); + final unspentCoinsInfoSource = + await Hive.openBox<UnspentCoinsInfo>(UnspentCoinsInfo.boxName); await initialSetup( sharedPreferences: await SharedPreferences.getInstance(), nodes: nodes, @@ -102,6 +109,7 @@ Future<void> main() async { contactSource: contacts, tradesSource: trades, ordersSource: orders, + unspentCoinsInfoSource: unspentCoinsInfoSource, // fiatConvertationService: fiatConvertationService, templates: templates, exchangeTemplates: exchangeTemplates, @@ -134,6 +142,7 @@ Future<void> initialSetup( @required Box<Template> templates, @required Box<ExchangeTemplate> exchangeTemplates, @required Box<TransactionDescription> transactionDescriptions, + @required Box<UnspentCoinsInfo> unspentCoinsInfoSource, FlutterSecureStorage secureStorage, int initialMigrationVersion = 15}) async { LanguageService.loadLocaleList(); @@ -153,7 +162,8 @@ Future<void> initialSetup( templates: templates, exchangeTemplates: exchangeTemplates, transactionDescriptionBox: transactionDescriptions, - ordersSource: ordersSource); + ordersSource: ordersSource, + unspentCoinsInfoSource: unspentCoinsInfoSource); await bootstrap(navigatorKey); monero_wallet.onStartup(); } diff --git a/lib/router.dart b/lib/router.dart index b71efae0b..acf81043e 100644 --- a/lib/router.dart +++ b/lib/router.dart @@ -372,10 +372,12 @@ Route<dynamic> createRoute(RouteSettings settings) { builder: (_) => getIt.get<UnspentCoinsListPage>()); case Routes.unspentCoinsDetails: + final args = settings.arguments as List; + return MaterialPageRoute<void>( builder: (_) => getIt.get<UnspentCoinsDetailsPage>( - param1: settings.arguments as UnspentCoinsItem)); + param1: args)); default: return MaterialPageRoute<void>( diff --git a/lib/src/screens/send/send_page.dart b/lib/src/screens/send/send_page.dart index 885b177a5..af6312cad 100644 --- a/lib/src/screens/send/send_page.dart +++ b/lib/src/screens/send/send_page.dart @@ -426,7 +426,7 @@ class SendPage extends BasePage { ), ), )), - if (sendViewModel.isBitcoinWallet) Padding( + if (sendViewModel.isElectrumWallet) Padding( padding: EdgeInsets.only(top: 6), child: GestureDetector( onTap: () => Navigator.of(context) @@ -436,7 +436,7 @@ class SendPage extends BasePage { MainAxisAlignment.spaceBetween, children: [ Text( - 'Coin control (optional)', + S.of(context).coin_control, style: TextStyle( fontSize: 12, fontWeight: FontWeight.w600, diff --git a/lib/src/screens/unspent_coins/unspent_coins_details_page.dart b/lib/src/screens/unspent_coins/unspent_coins_details_page.dart index f5e0f8991..d765e3f19 100644 --- a/lib/src/screens/unspent_coins/unspent_coins_details_page.dart +++ b/lib/src/screens/unspent_coins/unspent_coins_details_page.dart @@ -9,12 +9,13 @@ import 'package:cake_wallet/src/widgets/standart_list_row.dart'; import 'package:cake_wallet/src/screens/transaction_details/standart_list_item.dart'; import 'package:cake_wallet/src/screens/base_page.dart'; import 'package:flutter_mobx/flutter_mobx.dart'; +import 'package:cake_wallet/generated/i18n.dart'; class UnspentCoinsDetailsPage extends BasePage { UnspentCoinsDetailsPage({this.unspentCoinsDetailsViewModel}); @override - String get title => 'Unspent coins details'; + String get title => S.current.unspent_coins_details_title; final UnspentCoinsDetailsViewModel unspentCoinsDetailsViewModel; diff --git a/lib/src/screens/unspent_coins/unspent_coins_list_page.dart b/lib/src/screens/unspent_coins/unspent_coins_list_page.dart index c6747e048..55b283b7c 100644 --- a/lib/src/screens/unspent_coins/unspent_coins_list_page.dart +++ b/lib/src/screens/unspent_coins/unspent_coins_list_page.dart @@ -13,7 +13,7 @@ class UnspentCoinsListPage extends BasePage { UnspentCoinsListPage({this.unspentCoinsListViewModel}); @override - String get title => 'Unspent coins'; + String get title => S.current.unspent_coins_title; @override Widget trailing(BuildContext context) { @@ -77,18 +77,25 @@ class UnspentCoinsListFormState extends State<UnspentCoinsListForm> { separatorBuilder: (_, __) => SizedBox(height: 15), itemBuilder: (_, int index) { - final item = unspentCoinsListViewModel.items[index]; + return Observer(builder: (_) { + final item = unspentCoinsListViewModel.items[index]; - return GestureDetector( - onTap: () => - Navigator.of(context).pushNamed(Routes.unspentCoinsDetails, - arguments: item), - child: UnspentCoinsListItem( - address: item.address, - amount: item.amount, - isSending: item.isSending, - onCheckBoxTap: (value) {}, - )); + return GestureDetector( + onTap: () => + Navigator.of(context) + .pushNamed(Routes.unspentCoinsDetails, + arguments: [item, unspentCoinsListViewModel]), + child: UnspentCoinsListItem( + address: item.address, + amount: item.amount, + isSending: item.isSending, + onCheckBoxTap: item.isFrozen + ? null + : () async { + item.isSending = !item.isSending; + await unspentCoinsListViewModel + .saveUnspentCoinInfo(item);})); + }); } ) ) diff --git a/lib/src/screens/unspent_coins/widgets/unspent_coins_list_item.dart b/lib/src/screens/unspent_coins/widgets/unspent_coins_list_item.dart index 790e19c77..44886dce2 100644 --- a/lib/src/screens/unspent_coins/widgets/unspent_coins_list_item.dart +++ b/lib/src/screens/unspent_coins/widgets/unspent_coins_list_item.dart @@ -1,10 +1,9 @@ import 'package:auto_size_text/auto_size_text.dart'; import 'package:cake_wallet/palette.dart'; -import 'package:cake_wallet/src/widgets/standard_checkbox.dart'; import 'package:flutter/material.dart'; import 'package:flutter/cupertino.dart'; -class UnspentCoinsListItem extends StatefulWidget { +class UnspentCoinsListItem extends StatelessWidget { UnspentCoinsListItem({ @required this.address, @required this.amount, @@ -12,29 +11,6 @@ class UnspentCoinsListItem extends StatefulWidget { @required this.onCheckBoxTap, }); - final String address; - final String amount; - final bool isSending; - final Function(bool) onCheckBoxTap; - - @override UnspentCoinsListItemState createState() => - UnspentCoinsListItemState( - address: address, - amount: amount, - isSending: isSending, - onCheckBoxTap: onCheckBoxTap - ); - -} - -class UnspentCoinsListItemState extends State<UnspentCoinsListItem> { - UnspentCoinsListItemState({ - @required this.address, - @required this.amount, - @required this.isSending, - @required this.onCheckBoxTap, - }) : checkBoxValue = isSending; - static const amountColor = Palette.darkBlueCraiola; static const addressColor = Palette.darkGray; static const selectedItemColor = Palette.paleCornflowerBlue; @@ -43,62 +19,76 @@ class UnspentCoinsListItemState extends State<UnspentCoinsListItem> { final String address; final String amount; final bool isSending; - final Function(bool) onCheckBoxTap; - - bool checkBoxValue; + final Function() onCheckBoxTap; @override Widget build(BuildContext context) { - final itemColor = checkBoxValue? selectedItemColor : unselectedItemColor; + final itemColor = isSending? selectedItemColor : unselectedItemColor; return Container( - height: 62, - padding: EdgeInsets.all(12), - decoration: BoxDecoration( - borderRadius: BorderRadius.all(Radius.circular(12)), - color: itemColor), - child: Row( - mainAxisSize: MainAxisSize.max, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Padding( - padding: EdgeInsets.only(right: 12), - child: StandardCheckbox( - value: checkBoxValue, - onChanged: (value) { - onCheckBoxTap(value); - checkBoxValue = value; - setState(() {}); - } - ) - ), - Expanded( - child: Column( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - AutoSizeText( - amount ?? 'Amount', - style: TextStyle( - color: amountColor, - fontSize: 16, - fontWeight: FontWeight.w600 - ), - maxLines: 1, - ), - AutoSizeText( - address ?? 'Address', - style: TextStyle( - color: addressColor, - fontSize: 12, - ), - maxLines: 1, + height: 62, + padding: EdgeInsets.all(12), + decoration: BoxDecoration( + borderRadius: BorderRadius.all(Radius.circular(12)), + color: itemColor), + child: Row( + mainAxisSize: MainAxisSize.max, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Padding( + padding: EdgeInsets.only(right: 12), + child: GestureDetector( + onTap: () => onCheckBoxTap?.call(), + child: Container( + height: 24.0, + width: 24.0, + decoration: BoxDecoration( + border: Border.all( + color: Theme.of(context) + .primaryTextTheme + .caption + .color, + width: 1.0), + borderRadius: BorderRadius.all( + Radius.circular(8.0)), + color: Theme.of(context).backgroundColor), + child: isSending + ? Icon( + Icons.check, + color: Colors.blue, + size: 20.0, + ) + : Offstage(), + ) + ) + ), + Expanded( + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + AutoSizeText( + amount, + style: TextStyle( + color: amountColor, + fontSize: 16, + fontWeight: FontWeight.w600 + ), + maxLines: 1, + ), + AutoSizeText( + address, + style: TextStyle( + color: addressColor, + fontSize: 12, + ), + maxLines: 1, + ) + ] ) - ] ) - ) - ], - ) + ], + ) ); } } \ No newline at end of file diff --git a/lib/src/widgets/standart_switch.dart b/lib/src/widgets/standart_switch.dart index 0fa56f6ad..8760279a1 100644 --- a/lib/src/widgets/standart_switch.dart +++ b/lib/src/widgets/standart_switch.dart @@ -1,4 +1,3 @@ -import 'package:cake_wallet/palette.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; diff --git a/lib/view_model/send/send_view_model.dart b/lib/view_model/send/send_view_model.dart index a9e6aaf93..599ca8132 100644 --- a/lib/view_model/send/send_view_model.dart +++ b/lib/view_model/send/send_view_model.dart @@ -1,7 +1,6 @@ import 'package:cake_wallet/bitcoin/bitcoin_amount_format.dart'; import 'package:cake_wallet/bitcoin/bitcoin_transaction_priority.dart'; import 'package:cake_wallet/bitcoin/electrum_wallet.dart'; -import 'package:cake_wallet/entities/balance_display_mode.dart'; import 'package:cake_wallet/entities/calculate_fiat_amount_raw.dart'; import 'package:cake_wallet/entities/transaction_description.dart'; import 'package:cake_wallet/entities/transaction_priority.dart'; @@ -20,7 +19,6 @@ import 'package:cake_wallet/core/pending_transaction.dart'; import 'package:cake_wallet/core/validator.dart'; import 'package:cake_wallet/core/wallet_base.dart'; import 'package:cake_wallet/core/execution_state.dart'; -import 'package:cake_wallet/bitcoin/bitcoin_wallet.dart'; import 'package:cake_wallet/bitcoin/bitcoin_transaction_credentials.dart'; import 'package:cake_wallet/monero/monero_wallet.dart'; import 'package:cake_wallet/monero/monero_transaction_creation_credentials.dart'; @@ -55,7 +53,7 @@ abstract class SendViewModelBase with Store { _settingsStore.priority[_wallet.type] = priorities.first; } - isBitcoinWallet = _wallet is BitcoinWallet; + isElectrumWallet = _wallet is ElectrumWallet; _setCryptoNumMaximumFractionDigits(); } @@ -185,7 +183,7 @@ abstract class SendViewModelBase with Store { PendingTransaction pendingTransaction; @observable - bool isBitcoinWallet; + bool isElectrumWallet; @computed String get balance => _wallet.balance.formattedAvailableBalance ?? '0.0'; diff --git a/lib/view_model/unspent_coins/unspent_coins_details_view_model.dart b/lib/view_model/unspent_coins/unspent_coins_details_view_model.dart index 33db96eba..c9a5c2463 100644 --- a/lib/view_model/unspent_coins/unspent_coins_details_view_model.dart +++ b/lib/view_model/unspent_coins/unspent_coins_details_view_model.dart @@ -3,6 +3,7 @@ import 'package:cake_wallet/src/screens/transaction_details/textfield_list_item. import 'package:cake_wallet/src/screens/transaction_details/transaction_details_list_item.dart'; import 'package:cake_wallet/view_model/unspent_coins/unspent_coins_item.dart'; import 'package:cake_wallet/generated/i18n.dart'; +import 'package:cake_wallet/view_model/unspent_coins/unspent_coins_list_view_model.dart'; import 'package:cake_wallet/view_model/unspent_coins/unspent_coins_switch_item.dart'; import 'package:mobx/mobx.dart'; @@ -12,7 +13,9 @@ class UnspentCoinsDetailsViewModel = UnspentCoinsDetailsViewModelBase with _$UnspentCoinsDetailsViewModel; abstract class UnspentCoinsDetailsViewModelBase with Store { - UnspentCoinsDetailsViewModelBase({this.unspentCoinsItem}) { + UnspentCoinsDetailsViewModelBase({ + this.unspentCoinsItem, this.unspentCoinsListViewModel}) { + final amount = unspentCoinsItem.amount ?? ''; final address = unspentCoinsItem.address ?? ''; isFrozen = unspentCoinsItem.isFrozen ?? false; @@ -20,25 +23,31 @@ abstract class UnspentCoinsDetailsViewModelBase with Store { items = [ StandartListItem( - title: 'Amount', + title: S.current.transaction_details_amount, value: amount ), StandartListItem( - title: 'Address', + title: S.current.widgets_address, value: address ), TextFieldListItem( title: S.current.note_tap_to_change, value: note, onSubmitted: (value) { - note = value; + unspentCoinsItem.note = value; + unspentCoinsListViewModel.saveUnspentCoinInfo(unspentCoinsItem); }), UnspentCoinsSwitchItem( - title: 'Freeze', + title: S.current.freeze, value: '', switchValue: () => isFrozen, - onSwitchValueChange: (value) { + onSwitchValueChange: (value) async { isFrozen = value; + unspentCoinsItem.isFrozen = value; + if (value) { + unspentCoinsItem.isSending = !value; + } + await unspentCoinsListViewModel.saveUnspentCoinInfo(unspentCoinsItem); } ) ]; @@ -51,5 +60,6 @@ abstract class UnspentCoinsDetailsViewModelBase with Store { String note; final UnspentCoinsItem unspentCoinsItem; + final UnspentCoinsListViewModel unspentCoinsListViewModel; List<TransactionDetailsListItem> items; } \ No newline at end of file diff --git a/lib/view_model/unspent_coins/unspent_coins_item.dart b/lib/view_model/unspent_coins/unspent_coins_item.dart index 94b7d55c6..c01ca62c0 100644 --- a/lib/view_model/unspent_coins/unspent_coins_item.dart +++ b/lib/view_model/unspent_coins/unspent_coins_item.dart @@ -1,14 +1,33 @@ -class UnspentCoinsItem { - UnspentCoinsItem({ +import 'package:mobx/mobx.dart'; + +part 'unspent_coins_item.g.dart'; + +class UnspentCoinsItem = UnspentCoinsItemBase with _$UnspentCoinsItem; + +abstract class UnspentCoinsItemBase with Store { + UnspentCoinsItemBase({ this.address, this.amount, + this.hash, this.isFrozen, this.note, - this.isSending = true}); + this.isSending}); - final String address; - final String amount; - final bool isFrozen; - final String note; - final bool isSending; + @observable + String address; + + @observable + String amount; + + @observable + String hash; + + @observable + bool isFrozen; + + @observable + String note; + + @observable + bool isSending; } \ No newline at end of file diff --git a/lib/view_model/unspent_coins/unspent_coins_list_view_model.dart b/lib/view_model/unspent_coins/unspent_coins_list_view_model.dart index 25167f785..2b892a829 100644 --- a/lib/view_model/unspent_coins/unspent_coins_list_view_model.dart +++ b/lib/view_model/unspent_coins/unspent_coins_list_view_model.dart @@ -1,61 +1,58 @@ +import 'package:cake_wallet/bitcoin/bitcoin_amount_format.dart'; +import 'package:cake_wallet/bitcoin/electrum_wallet.dart'; +import 'package:cake_wallet/bitcoin/unspent_coins_info.dart'; +import 'package:cake_wallet/core/wallet_base.dart'; import 'package:cake_wallet/view_model/unspent_coins/unspent_coins_item.dart'; +import 'package:flutter/foundation.dart'; +import 'package:hive/hive.dart'; import 'package:mobx/mobx.dart'; part 'unspent_coins_list_view_model.g.dart'; -const List<Map<String, dynamic>> unspentCoinsMap = [ - <String, dynamic>{ - "address" : "bc1qm80mu5p3mf04a7cj7teymasf04dwpc3av2fwtr", - "amount" : "0.00358 BTC", - "isFrozen" : true, - "note" : "333cvgf23132132132132131321321314rwrtdggfdddewq ewqasfdxgdhgfgfszczcxgbhhhbcgbc"}, - <String, dynamic>{ - "address" : "bc1qm80mu5p3mf04a7cj7teymasf04dwpc3av2fwtr", - "amount" : "0.00567894 BTC", - "note" : "sfjskf"}, - <String, dynamic>{ - "address" : "bc1qm80mu5p3mf04a7cj7teymasf04dwpc3av2fwtr", - "amount" : "0.00087 BTC", - "isFrozen" : false}, - <String, dynamic>{ - "address" : "bc1qm80mu5p3mf04a7cj7teymasf04dwpc3av2fwtr", - "amount" : "0.00012 BTC", - "isFrozen" : true, - "note" : "333cvgf23132132132132131321321314rwrtdggfdddewq ewqasfdxgdhgfgfszczcxgbhhhbcgbc"}, - <String, dynamic>{ - "address" : "bc1qm80mu5p3mf04a7cj7teymasf04dwpc3av2fwtr", - "amount" : "0.00574 BTC", - "note" : "sffsfsdsgs"}, - <String, dynamic>{ - "address" : "bc1qm80mu5p3mf04a7cj7teymasf04dwpc3av2fwtr", - "amount" : "0.000482 BTC", - "isFrozen" : false}, - <String, dynamic>{}, - <String, dynamic>{ - "address" : "bc1qm80mu5p3mf04a7cj7teymasf04dwpc3av2fwtr", - "amount" : "0.00012 BTC", - "isFrozen" : true, - "note" : "333cvgf23132132132132131321321314rwrtdggfdddewq ewqasfdxgdhgfgfszczcxgbhhhbcgbc"}, - <String, dynamic>{ - "address" : "bc1qm80mu5p3mf04a7cj7teymasf04dwpc3av2fwtr", - "amount" : "0.00574 BTC", - "note" : "sffsfsdsgs"}, - <String, dynamic>{ - "address" : "bc1qm80mu5p3mf04a7cj7teymasf04dwpc3av2fwtr", - "amount" : "0.000482 BTC", - "isFrozen" : false}, -]; - class UnspentCoinsListViewModel = UnspentCoinsListViewModelBase with _$UnspentCoinsListViewModel; abstract class UnspentCoinsListViewModelBase with Store { + UnspentCoinsListViewModelBase({ + @required WalletBase wallet, + @required Box<UnspentCoinsInfo> unspentCoinsInfo}) { + _unspentCoinsInfo = unspentCoinsInfo; + _wallet = wallet as ElectrumWallet; + _wallet.updateUnspent(); + } + + ElectrumWallet _wallet; + Box<UnspentCoinsInfo> _unspentCoinsInfo; + @computed ObservableList<UnspentCoinsItem> get items => - ObservableList.of(unspentCoinsMap.map((elem) => - UnspentCoinsItem( - address: elem["address"] as String, - amount: elem["amount"] as String, - isFrozen: elem["isFrozen"] as bool, - note: elem["note"] as String - ))); + ObservableList.of(_wallet.unspentCoins.map((elem) { + final amount = bitcoinAmountToString(amount: elem.value) + + ' ${_wallet.currency.title}'; + + return UnspentCoinsItem( + address: elem.address.address, + amount: amount, + hash: elem.hash, + isFrozen: elem.isFrozen, + note: elem.note, + isSending: elem.isSending + ); + })); + + Future<void> saveUnspentCoinInfo(UnspentCoinsItem item) async { + try { + final info = _unspentCoinsInfo.values + .firstWhere((element) => element.walletId.contains(_wallet.id) && + element.hash.contains(item.hash)); + + info.isFrozen = item.isFrozen; + info.isSending = item.isSending; + info.note = item.note; + + await info.save(); + await _wallet.updateUnspent(); + } catch (e) { + print(e.toString()); + } + } } \ No newline at end of file diff --git a/res/values/strings_de.arb b/res/values/strings_de.arb index e2d835f70..3c03078f7 100644 --- a/res/values/strings_de.arb +++ b/res/values/strings_de.arb @@ -483,5 +483,10 @@ "moonpay_alert_text" : "Der Wert des Betrags muss größer oder gleich ${minAmount} ${fiatCurrency} sein", "outdated_electrum_wallet_receive_warning": "Wenn diese Brieftasche einen 12-Wort-Seed hat und in Cake erstellt wurde, zahlen Sie KEINE Bitcoins in diese Brieftasche ein. Alle auf diese Wallet übertragenen BTC können verloren gehen. Erstellen Sie eine neue 24-Wort-Wallet (tippen Sie auf das Menü oben rechts, wählen Sie Wallets, wählen Sie Neue Wallet erstellen und dann Bitcoin) und verschieben Sie Ihre BTC SOFORT dorthin. Neue (24-Wort-)BTC-Wallets von Cake sind sicher", - "do_not_show_me": "Zeig mir das nicht noch einmal" + "do_not_show_me": "Zeig mir das nicht noch einmal", + + "unspent_coins_title" : "Nicht ausgegebene Münzen", + "unspent_coins_details_title" : "Details zu nicht ausgegebenen Münzen", + "freeze" : "Einfrieren", + "coin_control" : "Münzkontrolle (optional)" } diff --git a/res/values/strings_en.arb b/res/values/strings_en.arb index adb82ecb2..c8acc77e0 100644 --- a/res/values/strings_en.arb +++ b/res/values/strings_en.arb @@ -483,5 +483,10 @@ "moonpay_alert_text" : "Value of the amount must be more or equal to ${minAmount} ${fiatCurrency}", "outdated_electrum_wallet_receive_warning": "If this wallet has a 12-word seed and was created in Cake, DO NOT deposit Bitcoin into this wallet. Any BTC transferred to this wallet may be lost. Create a new 24-word wallet (tap the menu at the top right, select Wallets, choose Create New Wallet, then select Bitcoin) and IMMEDIATELY move your BTC there. New (24-word) BTC wallets from Cake are secure", - "do_not_show_me": "Do not show me this again" + "do_not_show_me": "Do not show me this again", + + "unspent_coins_title" : "Unspent coins", + "unspent_coins_details_title" : "Unspent coins details", + "freeze" : "Freeze", + "coin_control" : "Coin control (optional)" } \ No newline at end of file diff --git a/res/values/strings_es.arb b/res/values/strings_es.arb index e54a997a4..09aeb8e6f 100644 --- a/res/values/strings_es.arb +++ b/res/values/strings_es.arb @@ -483,5 +483,10 @@ "moonpay_alert_text" : "El valor de la cantidad debe ser mayor o igual a ${minAmount} ${fiatCurrency}", "outdated_electrum_wallet_receive_warning": "Si esta billetera tiene una semilla de 12 palabras y se creó en Cake, NO deposite Bitcoin en esta billetera. Cualquier BTC transferido a esta billetera se puede perder. Cree una nueva billetera de 24 palabras (toque el menú en la parte superior derecha, seleccione Monederos, elija Crear nueva billetera, luego seleccione Bitcoin) e INMEDIATAMENTE mueva su BTC allí. Las nuevas carteras BTC (24 palabras) de Cake son seguras", - "do_not_show_me": "no me muestres esto otra vez" + "do_not_show_me": "no me muestres esto otra vez", + + "unspent_coins_title" : "Monedas no gastadas", + "unspent_coins_details_title" : "Detalles de monedas no gastadas", + "freeze" : "Congelar", + "coin_control" : "Control de monedas (opcional)" } \ No newline at end of file diff --git a/res/values/strings_hi.arb b/res/values/strings_hi.arb index 7ef15cc8e..cee89d657 100644 --- a/res/values/strings_hi.arb +++ b/res/values/strings_hi.arb @@ -483,5 +483,10 @@ "moonpay_alert_text" : "राशि का मूल्य अधिक है या करने के लिए बराबर होना चाहिए ${minAmount} ${fiatCurrency}", "outdated_electrum_wallet_receive_warning": "अगर इस वॉलेट में 12 शब्दों का बीज है और इसे केक में बनाया गया है, तो इस वॉलेट में बिटकॉइन जमा न करें। इस वॉलेट में स्थानांतरित किया गया कोई भी बीटीसी खो सकता है। एक नया 24-शब्द वॉलेट बनाएं (ऊपर दाईं ओर स्थित मेनू पर टैप करें, वॉलेट चुनें, नया वॉलेट बनाएं चुनें, फिर बिटकॉइन चुनें) और तुरंत अपना बीटीसी वहां ले जाएं। केक से नए (24-शब्द) बीटीसी वॉलेट सुरक्षित हैं", - "do_not_show_me": "मुझे यह फिर न दिखाएं" + "do_not_show_me": "मुझे यह फिर न दिखाएं", + + "unspent_coins_title" : "खर्च न किए गए सिक्के", + "unspent_coins_details_title" : "अव्ययित सिक्कों का विवरण", + "freeze" : "फ्रीज", + "coin_control" : "सिक्का नियंत्रण (वैकल्पिक)" } \ No newline at end of file diff --git a/res/values/strings_hr.arb b/res/values/strings_hr.arb index 100311903..c6fb2c681 100644 --- a/res/values/strings_hr.arb +++ b/res/values/strings_hr.arb @@ -483,5 +483,10 @@ "moonpay_alert_text" : "Vrijednost iznosa mora biti veća ili jednaka ${minAmount} ${fiatCurrency}", "outdated_electrum_wallet_receive_warning": "Ako ovaj novčanik sadrži sjeme od 12 riječi i stvoren je u Torti, NEMOJTE polagati Bitcoin u ovaj novčanik. Bilo koji BTC prebačen u ovaj novčanik može se izgubiti. Stvorite novi novčanik od 24 riječi (taknite izbornik u gornjem desnom dijelu, odaberite Novčanici, odaberite Stvori novi novčanik, a zatim odaberite Bitcoin) i ODMAH premjestite svoj BTC tamo. Novi BTC novčanici (s 24 riječi) tvrtke Cake sigurni su", - "do_not_show_me": "Ne pokazuj mi ovo više" + "do_not_show_me": "Ne pokazuj mi ovo više", + + "unspent_coins_title" : "Nepotrošeni novčići", + "unspent_coins_details_title" : "Nepotrošeni detalji o novčićima", + "freeze" : "Zamrznuti", + "coin_control" : "Kontrola novca (nije obavezno)" } \ No newline at end of file diff --git a/res/values/strings_it.arb b/res/values/strings_it.arb index 2ce76646e..9fd63ed9d 100644 --- a/res/values/strings_it.arb +++ b/res/values/strings_it.arb @@ -483,5 +483,10 @@ "moonpay_alert_text" : "Il valore dell'importo deve essere maggiore o uguale a ${minAmount} ${fiatCurrency}", "outdated_electrum_wallet_receive_warning": "Se questo portafoglio ha un seme di 12 parole ed è stato creato in Cake, NON depositare Bitcoin in questo portafoglio. Qualsiasi BTC trasferito su questo portafoglio potrebbe andare perso. Crea un nuovo portafoglio di 24 parole (tocca il menu in alto a destra, seleziona Portafogli, scegli Crea nuovo portafoglio, quindi seleziona Bitcoin) e sposta IMMEDIATAMENTE lì il tuo BTC. I nuovi portafogli BTC (24 parole) di Cake sono sicuri", - "do_not_show_me": "Non mostrarmelo di nuovo" + "do_not_show_me": "Non mostrarmelo di nuovo", + + "unspent_coins_title" : "Monete non spese", + "unspent_coins_details_title" : "Dettagli sulle monete non spese", + "freeze" : "Congelare", + "coin_control" : "Controllo monete (opzionale)" } \ No newline at end of file diff --git a/res/values/strings_ja.arb b/res/values/strings_ja.arb index 0702398a7..36d0ec07b 100644 --- a/res/values/strings_ja.arb +++ b/res/values/strings_ja.arb @@ -483,5 +483,10 @@ "moonpay_alert_text" : "金額の値は以上でなければなりません ${minAmount} ${fiatCurrency}", "outdated_electrum_wallet_receive_warning": "このウォレットに 12 ワードのシードがあり、Cake で作成された場合、このウォレットにビットコインを入金しないでください。 このウォレットに転送された BTC は失われる可能性があります。 新しい 24 ワードのウォレットを作成し (右上のメニューをタップし、[ウォレット]、[新しいウォレットの作成]、[ビットコイン] の順に選択)、すぐに BTC をそこに移動します。 Cake の新しい (24 ワード) BTC ウォレットは安全です", - "do_not_show_me": "また僕にこれを見せないでください" + "do_not_show_me": "また僕にこれを見せないでください", + + "unspent_coins_title" : "未使用のコイン", + "unspent_coins_details_title" : "未使用のコインの詳細", + "freeze" : "氷結", + "coin_control" : "コインコントロール(オプション)" } \ No newline at end of file diff --git a/res/values/strings_ko.arb b/res/values/strings_ko.arb index 0659b7fbc..82d8f6c27 100644 --- a/res/values/strings_ko.arb +++ b/res/values/strings_ko.arb @@ -483,5 +483,10 @@ "moonpay_alert_text" : "금액은 다음보다 크거나 같아야합니다 ${minAmount} ${fiatCurrency}", "outdated_electrum_wallet_receive_warning": "이 지갑에 12 단어 시드가 있고 Cake에서 생성 된 경우이 지갑에 비트 코인을 입금하지 마십시오. 이 지갑으로 전송 된 모든 BTC는 손실 될 수 있습니다. 새로운 24 단어 지갑을 생성하고 (오른쪽 상단의 메뉴를 탭하고 지갑을 선택한 다음 새 지갑 생성을 선택한 다음 비트 코인을 선택하십시오) 즉시 BTC를 그곳으로 이동하십시오. Cake의 새로운 (24 단어) BTC 지갑은 안전합니다", - "do_not_show_me": "나를 다시 표시하지 않음" + "do_not_show_me": "나를 다시 표시하지 않음", + + "unspent_coins_title" : "사용하지 않은 동전", + "unspent_coins_details_title" : "사용하지 않은 동전 세부 정보", + "freeze" : "얼다", + "coin_control" : "코인 제어 (옵션)" } \ No newline at end of file diff --git a/res/values/strings_nl.arb b/res/values/strings_nl.arb index 2e6459d25..caae97894 100644 --- a/res/values/strings_nl.arb +++ b/res/values/strings_nl.arb @@ -483,5 +483,10 @@ "moonpay_alert_text" : "Waarde van het bedrag moet meer of gelijk zijn aan ${minAmount} ${fiatCurrency}", "outdated_electrum_wallet_receive_warning": "Als deze portemonnee een seed van 12 woorden heeft en is gemaakt in Cake, stort dan GEEN Bitcoin in deze portemonnee. Elke BTC die naar deze portemonnee is overgebracht, kan verloren gaan. Maak een nieuwe portemonnee van 24 woorden (tik op het menu rechtsboven, selecteer Portefeuilles, kies Nieuwe portemonnee maken en selecteer vervolgens Bitcoin) en verplaats je BTC ONMIDDELLIJK daar. Nieuwe (24-woorden) BTC-portefeuilles van Cake zijn veilig", - "do_not_show_me": "laat me dit niet opnieuw zien" + "do_not_show_me": "laat me dit niet opnieuw zien", + + "unspent_coins_title" : "Ongebruikte munten", + "unspent_coins_details_title" : "Details van niet-uitgegeven munten", + "freeze" : "Bevriezen", + "coin_control" : "Muntcontrole (optioneel)" } \ No newline at end of file diff --git a/res/values/strings_pl.arb b/res/values/strings_pl.arb index e28bad8dc..47c4a7062 100644 --- a/res/values/strings_pl.arb +++ b/res/values/strings_pl.arb @@ -483,5 +483,10 @@ "moonpay_alert_text" : "Wartość kwoty musi być większa lub równa ${minAmount} ${fiatCurrency}", "outdated_electrum_wallet_receive_warning": "Jeśli ten portfel ma 12-wyrazowy seed i został utworzony w Cake, NIE Wpłacaj Bitcoina do tego portfela. Wszelkie BTC przeniesione do tego portfela mogą zostać utracone. Utwórz nowy portfel z 24 słowami (dotknij menu w prawym górnym rogu, wybierz Portfele, wybierz Utwórz nowy portfel, a następnie Bitcoin) i NATYCHMIAST przenieś tam swoje BTC. Nowe (24 słowa) portfele BTC firmy Cake są bezpieczne", - "do_not_show_me": "Nie pokazuj mi tego ponownie" + "do_not_show_me": "Nie pokazuj mi tego ponownie", + + "unspent_coins_title" : "Niewydane monety", + "unspent_coins_details_title" : "Szczegóły niewydanych monet", + "freeze" : "Zamrażać", + "coin_control" : "Kontrola monet (opcjonalnie)" } \ No newline at end of file diff --git a/res/values/strings_pt.arb b/res/values/strings_pt.arb index 872a102e4..a49211e2a 100644 --- a/res/values/strings_pt.arb +++ b/res/values/strings_pt.arb @@ -483,5 +483,10 @@ "moonpay_alert_text" : "O valor do montante deve ser maior ou igual a ${minAmount} ${fiatCurrency}", "outdated_electrum_wallet_receive_warning": "Se esta carteira tiver uma semente de 12 palavras e foi criada no Cake, NÃO deposite Bitcoin nesta carteira. Qualquer BTC transferido para esta carteira pode ser perdido. Crie uma nova carteira de 24 palavras (toque no menu no canto superior direito, selecione Carteiras, escolha Criar Nova Carteira e selecione Bitcoin) e mova IMEDIATAMENTE seu BTC para lá. As novas carteiras BTC (24 palavras) da Cake são seguras", - "do_not_show_me": "não me mostre isso novamente" + "do_not_show_me": "não me mostre isso novamente", + + "unspent_coins_title" : "Moedas não gastas", + "unspent_coins_details_title" : "Detalhes de moedas não gastas", + "freeze" : "Congelar", + "coin_control" : "Controle de moedas (opcional)" } \ No newline at end of file diff --git a/res/values/strings_ru.arb b/res/values/strings_ru.arb index 1828c36c3..39508cd76 100644 --- a/res/values/strings_ru.arb +++ b/res/values/strings_ru.arb @@ -483,5 +483,10 @@ "moonpay_alert_text" : "Сумма должна быть больше или равна ${minAmount} ${fiatCurrency}", "outdated_electrum_wallet_receive_warning": "Если этот кошелек имеет мнемоническую фразу из 12 слов и был создан в Cake, НЕ переводите биткойны на этот кошелек. Любые BTC, переведенные на этот кошелек, могут быть потеряны. Создайте новый кошелек с мнемоническои фразы из 24 слов (коснитесь меню в правом верхнем углу, выберите «Кошельки», выберите «Создать новый кошелек», затем выберите «Bitcoin») и НЕМЕДЛЕННО переведите туда свои BTC. Новые (24 слова) кошельки BTC от Cake безопасны", - "do_not_show_me": "Не показывай мне это больше" + "do_not_show_me": "Не показывай мне это больше", + + "unspent_coins_title" : "Неизрасходованные монеты", + "unspent_coins_details_title" : "Сведения о неизрасходованных монетах", + "freeze" : "Заморозить", + "coin_control" : "Контроль монет (необязательно)" } \ No newline at end of file diff --git a/res/values/strings_uk.arb b/res/values/strings_uk.arb index 2becc69ab..5a2a29c2b 100644 --- a/res/values/strings_uk.arb +++ b/res/values/strings_uk.arb @@ -483,5 +483,10 @@ "moonpay_alert_text" : "Значення суми має бути більшим або дорівнювати ${minAmount} ${fiatCurrency}", "outdated_electrum_wallet_receive_warning": "Якщо цей гаманець має мнемонічну фразу з 12 слів і був створений у Cake, НЕ переводьте біткойни на цей гаманець. Будь-які BTC, переведений на цей гаманець, можуть бути втраченими. Створіть новий гаманець з мнемонічною фразою з 24 слів (торкніться меню у верхньому правому куті, виберіть Гаманці, виберіть Створити новий гаманець, потім виберіть Bitcoin) і НЕГАЙНО переведіть туди свії BTC. Нові (з мнемонічною фразою з 24 слів) гаманці BTC від Cake надійно захищені", - "do_not_show_me": "Не показуй мені це знову" + "do_not_show_me": "Не показуй мені це знову", + + "unspent_coins_title" : "Невитрачені монети", + "unspent_coins_details_title" : "Відомості про невитрачені монети", + "freeze" : "Заморозити", + "coin_control" : "Контроль монет (необов’язково)" } \ No newline at end of file diff --git a/res/values/strings_zh.arb b/res/values/strings_zh.arb index af19ca386..1538c55bb 100644 --- a/res/values/strings_zh.arb +++ b/res/values/strings_zh.arb @@ -483,5 +483,10 @@ "moonpay_alert_text" : "金额的价值必须大于或等于 ${minAmount} ${fiatCurrency}", "outdated_electrum_wallet_receive_warning": "如果这个钱包有一个 12 字的种子并且是在 Cake 中创建的,不要将比特币存入这个钱包。 任何转移到此钱包的 BTC 都可能丢失。 创建一个新的 24 字钱包(点击右上角的菜单,选择钱包,选择创建新钱包,然后选择比特币)并立即将您的 BTC 移到那里。 Cake 的新(24 字)BTC 钱包是安全的", - "do_not_show_me": "不再提示" + "do_not_show_me": "不再提示", + + "unspent_coins_title" : "未使用的硬幣", + "unspent_coins_details_title" : "未使用代幣詳情", + "freeze" : "凍結", + "coin_control" : "硬幣控制(可選)" } \ No newline at end of file From 1e3b53d2cf2dcaf3f2786d430d2497dcb25a2637 Mon Sep 17 00:00:00 2001 From: M <m@cakewallet.com> Date: Tue, 6 Jul 2021 13:51:54 +0300 Subject: [PATCH 18/28] Added backups for monero wallet files. --- lib/monero/monero_wallet.dart | 2 + lib/monero/monero_wallet_service.dart | 16 ++--- lib/monero/monero_wallet_utils.dart | 88 +++++++++++++++++++++++++++ 3 files changed, 94 insertions(+), 12 deletions(-) create mode 100644 lib/monero/monero_wallet_utils.dart diff --git a/lib/monero/monero_wallet.dart b/lib/monero/monero_wallet.dart index e902f1080..4105e5de2 100644 --- a/lib/monero/monero_wallet.dart +++ b/lib/monero/monero_wallet.dart @@ -3,6 +3,7 @@ import 'package:cake_wallet/entities/transaction_priority.dart'; import 'package:cake_wallet/monero/monero_amount_format.dart'; import 'package:cake_wallet/monero/monero_transaction_creation_exception.dart'; import 'package:cake_wallet/monero/monero_transaction_info.dart'; +import 'package:cake_wallet/monero/monero_wallet_utils.dart'; import 'package:flutter/foundation.dart'; import 'package:mobx/mobx.dart'; import 'package:cw_monero/transaction_history.dart' @@ -246,6 +247,7 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance, _lastSaveTimestamp = now; await monero_wallet.store(); + await backupWalletFiles(name); } Future<int> getNodeHeight() async => monero_wallet.getNodeHeight(); diff --git a/lib/monero/monero_wallet_service.dart b/lib/monero/monero_wallet_service.dart index 7795b8700..824daf41a 100644 --- a/lib/monero/monero_wallet_service.dart +++ b/lib/monero/monero_wallet_service.dart @@ -1,5 +1,6 @@ import 'dart:io'; import 'package:cake_wallet/core/wallet_base.dart'; +import 'package:cake_wallet/monero/monero_wallet_utils.dart'; import 'package:hive/hive.dart'; import 'package:cw_monero/wallet_manager.dart' as monero_wallet_manager; import 'package:cw_monero/wallet.dart' as monero_wallet; @@ -55,16 +56,7 @@ class MoneroWalletService extends WalletService< MoneroWalletService(this.walletInfoSource); final Box<WalletInfo> walletInfoSource; - - static Future<void> _removeCache(String name) async { - final path = await pathForWallet(name: name, type: WalletType.monero); - final cacheFile = File(path); - - if (cacheFile.existsSync()) { - cacheFile.deleteSync(); - } - } - + static bool walletFilesExist(String path) => !File(path).existsSync() && !File('$path.keys').existsSync(); @@ -120,7 +112,7 @@ class MoneroWalletService extends WalletService< final isValid = wallet.validate(); if (!isValid) { - await _removeCache(name); + await restoreOrResetWalletFiles(name); wallet.close(); return openWallet(name, password); } @@ -135,7 +127,7 @@ class MoneroWalletService extends WalletService< (e is WalletOpeningException && (e.message == 'std::bad_alloc' || e.message.contains('bad_alloc')))) { - await _removeCache(name); + await restoreOrResetWalletFiles(name); return openWallet(name, password); } diff --git a/lib/monero/monero_wallet_utils.dart b/lib/monero/monero_wallet_utils.dart new file mode 100644 index 000000000..b0e509085 --- /dev/null +++ b/lib/monero/monero_wallet_utils.dart @@ -0,0 +1,88 @@ +import 'dart:io'; +import 'package:cake_wallet/entities/pathForWallet.dart'; +import 'package:cake_wallet/entities/wallet_type.dart'; + +String backupFileName(String originalPath) { + final pathParts = originalPath.split('/'); + final newName = '#_${pathParts.last}'; + pathParts.removeLast(); + pathParts.add(newName); + return pathParts.join('/'); +} + +Future<void> backupWalletFiles(String name) async { + final path = await pathForWallet(name: name, type: WalletType.monero); + final cacheFile = File(path); + final keysFile = File('$path.keys'); + final addressListFile = File('$path.address.txt'); + final newCacheFilePath = backupFileName(cacheFile.path); + final newKeysFilePath = backupFileName(keysFile.path); + final newAddressListFilePath = backupFileName(addressListFile.path); + + if (cacheFile.existsSync()) { + await cacheFile.copy(newCacheFilePath); + } + + if (keysFile.existsSync()) { + await keysFile.copy(newKeysFilePath); + } + + if (addressListFile.existsSync()) { + await addressListFile.copy(newAddressListFilePath); + } +} + +Future<void> restoreWalletFiles(String name) async { + final walletDirPath = await pathForWalletDir(name: name, type: WalletType.monero); + final cacheFilePath = '$walletDirPath/$name'; + final keysFilePath = '$walletDirPath/$name.keys'; + final addressListFilePath = '$walletDirPath/$name.address.txt'; + final backupCacheFile = File(backupFileName(cacheFilePath)); + final backupKeysFile = File(backupFileName(keysFilePath)); + final backupAddressListFile = File(backupFileName(addressListFilePath)); + + if (backupCacheFile.existsSync()) { + await backupCacheFile.copy(cacheFilePath); + } + + if (backupKeysFile.existsSync()) { + await backupKeysFile.copy(keysFilePath); + } + + if (backupAddressListFile.existsSync()) { + await backupAddressListFile.copy(addressListFilePath); + } +} + +Future<bool> backupWalletFilesExists(String name) async { + final walletDirPath = await pathForWalletDir(name: name, type: WalletType.monero); + final cacheFilePath = '$walletDirPath/$name'; + final keysFilePath = '$walletDirPath/$name.keys'; + final addressListFilePath = '$walletDirPath/$name.address.txt'; + final backupCacheFile = File(backupFileName(cacheFilePath)); + final backupKeysFile = File(backupFileName(keysFilePath)); + final backupAddressListFile = File(backupFileName(addressListFilePath)); + + return backupCacheFile.existsSync() + && backupKeysFile.existsSync() + && backupAddressListFile.existsSync(); +} + +Future<void> removeCache(String name) async { + final path = await pathForWallet(name: name, type: WalletType.monero); + final cacheFile = File(path); + + if (cacheFile.existsSync()) { + cacheFile.deleteSync(); + } +} + +Future<void> restoreOrResetWalletFiles(String name) async { + final backupsExists = await backupWalletFilesExists(name); + + if (backupsExists) { + await restoreWalletFiles(name); + } + + removeCache(name); +} \ No newline at end of file From 5f02a7ddf5d776790925d6451e92cbf46a14c4bc Mon Sep 17 00:00:00 2001 From: OleksandrSobol <dr.alexander.sobol@gmail.com> Date: Tue, 6 Jul 2021 18:13:29 +0300 Subject: [PATCH 19/28] CAKE-344 | fixed MoonPay icon color on support and dashboard pages --- lib/buy/get_buy_provider_icon.dart | 11 ++++------- lib/src/screens/buy/widgets/buy_list_item.dart | 3 ++- lib/src/screens/dashboard/widgets/order_row.dart | 10 +++------- lib/src/screens/support/support_page.dart | 5 ++++- lib/themes/bright_theme.dart | 2 ++ lib/themes/dark_theme.dart | 2 ++ lib/themes/light_theme.dart | 2 ++ lib/view_model/settings/link_list_item.dart | 4 ++-- lib/view_model/support_view_model.dart | 12 ++---------- 9 files changed, 23 insertions(+), 28 deletions(-) diff --git a/lib/buy/get_buy_provider_icon.dart b/lib/buy/get_buy_provider_icon.dart index f284b6f04..672dfdd9d 100644 --- a/lib/buy/get_buy_provider_icon.dart +++ b/lib/buy/get_buy_provider_icon.dart @@ -2,23 +2,20 @@ import 'package:flutter/material.dart'; import 'package:cake_wallet/buy/buy_provider_description.dart'; Image getBuyProviderIcon(BuyProviderDescription providerDescription, - {bool isWhiteIconColor = false}) { + {Color iconColor = Colors.black}) { final _wyreIcon = Image.asset('assets/images/wyre-icon.png', width: 36, height: 36); - final _moonPayWhiteIcon = - Image.asset('assets/images/moonpay-icon.png', color: Colors.white, + final _moonPayIcon = + Image.asset('assets/images/moonpay-icon.png', color: iconColor, width: 36, height: 34); - final _moonPayBlackIcon = - Image.asset('assets/images/moonpay-icon.png', color: Colors.black, - width: 36, height: 34); if (providerDescription != null) { switch (providerDescription) { case BuyProviderDescription.wyre: return _wyreIcon; case BuyProviderDescription.moonPay: - return isWhiteIconColor ? _moonPayWhiteIcon : _moonPayBlackIcon; + return _moonPayIcon; default: return null; } diff --git a/lib/src/screens/buy/widgets/buy_list_item.dart b/lib/src/screens/buy/widgets/buy_list_item.dart index 97eb88ec6..26d173eb4 100644 --- a/lib/src/screens/buy/widgets/buy_list_item.dart +++ b/lib/src/screens/buy/widgets/buy_list_item.dart @@ -27,9 +27,10 @@ class BuyListItem extends StatelessWidget { @override Widget build(BuildContext context) { final isSelected = selectedProvider?.description == provider.description; + final iconColor = isSelected ? Colors.white : Colors.black; final providerIcon = getBuyProviderIcon(provider.description, - isWhiteIconColor: isSelected); + iconColor: iconColor); final backgroundColor = isSelected ? Palette.greyBlueCraiola diff --git a/lib/src/screens/dashboard/widgets/order_row.dart b/lib/src/screens/dashboard/widgets/order_row.dart index 9a7a61525..dea69cdd8 100644 --- a/lib/src/screens/dashboard/widgets/order_row.dart +++ b/lib/src/screens/dashboard/widgets/order_row.dart @@ -1,9 +1,6 @@ import 'package:cake_wallet/buy/buy_provider_description.dart'; import 'package:cake_wallet/buy/get_buy_provider_icon.dart'; -import 'package:cake_wallet/themes/theme_base.dart'; import 'package:flutter/material.dart'; -import 'package:cake_wallet/di.dart'; -import 'package:cake_wallet/store/settings_store.dart'; class OrderRow extends StatelessWidget { OrderRow({ @@ -22,11 +19,10 @@ class OrderRow extends StatelessWidget { @override Widget build(BuildContext context) { - final currentTheme = getIt.get<SettingsStore>().currentTheme; - final isWhiteIconColor = currentTheme.type != ThemeType.light; + final iconColor = + Theme.of(context).primaryTextTheme.display4.backgroundColor; - final providerIcon = getBuyProviderIcon(provider, - isWhiteIconColor: isWhiteIconColor); + final providerIcon = getBuyProviderIcon(provider, iconColor: iconColor); return InkWell( onTap: onTap, diff --git a/lib/src/screens/support/support_page.dart b/lib/src/screens/support/support_page.dart index 9405214c6..986317533 100644 --- a/lib/src/screens/support/support_page.dart +++ b/lib/src/screens/support/support_page.dart @@ -19,6 +19,9 @@ class SupportPage extends BasePage { @override Widget body(BuildContext context) { + final iconColor = + Theme.of(context).accentTextTheme.display4.backgroundColor; + return SectionStandardList( sectionCount: 1, itemCounter: (int _) => supportViewModel.items.length, @@ -34,7 +37,7 @@ class SupportPage extends BasePage { return SettingsLinkProviderCell( title: item.title, icon: item.icon, - iconColor: item.iconColor, + iconColor: item.hasIconColor ? iconColor : null, link: item.link, linkTitle: item.linkTitle); } diff --git a/lib/themes/bright_theme.dart b/lib/themes/bright_theme.dart index 39c33f8f1..73c33aba3 100644 --- a/lib/themes/bright_theme.dart +++ b/lib/themes/bright_theme.dart @@ -113,6 +113,7 @@ class BrightTheme extends ThemeBase { ), display4: TextStyle( color: Palette.darkBlueCraiola, // template title (send page) + backgroundColor: Colors.white, // icon color on order row (moonpay) decorationColor: Palette.niagara // receive amount text (exchange page) ), subtitle: TextStyle( @@ -179,6 +180,7 @@ class BrightTheme extends ThemeBase { ), display4: TextStyle( color: Palette.darkGray, // switch background (settings page) + backgroundColor: Colors.black, // icon color on support page (moonpay, github) decorationColor: Colors.white.withOpacity(0.4) // hint text (exchange page) ), body1: TextStyle( diff --git a/lib/themes/dark_theme.dart b/lib/themes/dark_theme.dart index 09fb5526f..428100eba 100644 --- a/lib/themes/dark_theme.dart +++ b/lib/themes/dark_theme.dart @@ -112,6 +112,7 @@ class DarkTheme extends ThemeBase { ), display4: TextStyle( color: PaletteDark.cyanBlue, // template title (send page) + backgroundColor: Colors.white, // icon color on order row (moonpay) decorationColor: PaletteDark.darkCyanBlue // receive amount text (exchange page) ), subtitle: TextStyle( @@ -178,6 +179,7 @@ class DarkTheme extends ThemeBase { ), display4: TextStyle( color: PaletteDark.deepVioletBlue, // switch background (settings page) + backgroundColor: Colors.white, // icon color on support page (moonpay, github) decorationColor: PaletteDark.lightBlueGrey // hint text (exchange page) ), body1: TextStyle( diff --git a/lib/themes/light_theme.dart b/lib/themes/light_theme.dart index 7d1486a91..fa12a90b6 100644 --- a/lib/themes/light_theme.dart +++ b/lib/themes/light_theme.dart @@ -113,6 +113,7 @@ class LightTheme extends ThemeBase { ), display4: TextStyle( color: Palette.darkBlueCraiola, // template title (send page) + backgroundColor: Colors.black, // icon color on order row (moonpay) decorationColor: Palette.niagara // receive amount text (exchange page) ), subtitle: TextStyle( @@ -178,6 +179,7 @@ class LightTheme extends ThemeBase { ), display4: TextStyle( color: Palette.darkGray, // switch background (settings page) + backgroundColor: Colors.black, // icon color on support page (moonpay, github) decorationColor: Colors.white.withOpacity(0.4) // hint text (exchange page) ), body1: TextStyle( diff --git a/lib/view_model/settings/link_list_item.dart b/lib/view_model/settings/link_list_item.dart index cfcc601c1..92a01f444 100644 --- a/lib/view_model/settings/link_list_item.dart +++ b/lib/view_model/settings/link_list_item.dart @@ -8,11 +8,11 @@ class LinkListItem extends SettingsListItem { @required this.link, @required this.linkTitle, this.icon, - this.iconColor}) + this.hasIconColor = false}) : super(title); final String icon; final String link; final String linkTitle; - final Color iconColor; + final bool hasIconColor; } \ No newline at end of file diff --git a/lib/view_model/support_view_model.dart b/lib/view_model/support_view_model.dart index caa360339..9b8dbe426 100644 --- a/lib/view_model/support_view_model.dart +++ b/lib/view_model/support_view_model.dart @@ -6,9 +6,6 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:mobx/mobx.dart'; import 'package:url_launcher/url_launcher.dart'; -import 'package:cake_wallet/di.dart'; -import 'package:cake_wallet/store/settings_store.dart'; -import 'package:cake_wallet/themes/theme_base.dart'; part 'support_view_model.g.dart'; @@ -16,11 +13,6 @@ class SupportViewModel = SupportViewModelBase with _$SupportViewModel; abstract class SupportViewModelBase with Store { SupportViewModelBase() { - final currentTheme = getIt.get<SettingsStore>().currentTheme; - final iconColor = currentTheme.type == ThemeType.dark - ? Colors.white - : Colors.black; - items = [ RegularListItem( title: S.current.faq, @@ -39,7 +31,7 @@ abstract class SupportViewModelBase with Store { LinkListItem( title: 'GitHub', icon: 'assets/images/github.png', - iconColor: iconColor, + hasIconColor: true, linkTitle: S.current.apk_update, link: 'https://github.com/cake-tech/cake_wallet/releases'), LinkListItem( @@ -65,7 +57,7 @@ abstract class SupportViewModelBase with Store { LinkListItem( title: 'MoonPay', icon: 'assets/images/moonpay.png', - iconColor: iconColor, + hasIconColor: true, linkTitle: S.current.submit_request, link: 'https://support.moonpay.com/hc/en-gb/requests/new') ]; From c72245d90463e460de6fa91a826ef54d4857b371 Mon Sep 17 00:00:00 2001 From: OleksandrSobol <dr.alexander.sobol@gmail.com> Date: Wed, 7 Jul 2021 12:29:33 +0300 Subject: [PATCH 20/28] CAKE-343 | fixed contact_list_view_model.dart and updateAddressInfo() in the monero_wallet.dart --- lib/monero/monero_wallet.dart | 2 +- lib/view_model/contact_list/contact_list_view_model.dart | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/monero/monero_wallet.dart b/lib/monero/monero_wallet.dart index 753dff53b..c9ea9e03e 100644 --- a/lib/monero/monero_wallet.dart +++ b/lib/monero/monero_wallet.dart @@ -454,7 +454,7 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance, accountList.accounts.forEach((account) { _subaddressList.update(accountIndex: account.id); _subaddressList.subaddresses.forEach((subaddress) { - _addresses.addAll({subaddress.address:subaddress.label}); + _addresses[subaddress.address] = subaddress.label; }); }); diff --git a/lib/view_model/contact_list/contact_list_view_model.dart b/lib/view_model/contact_list/contact_list_view_model.dart index fd6196c89..b5143cffa 100644 --- a/lib/view_model/contact_list/contact_list_view_model.dart +++ b/lib/view_model/contact_list/contact_list_view_model.dart @@ -20,7 +20,7 @@ abstract class ContactListViewModelBase with Store { walletInfoSource.values.forEach((info) { if (info.address?.isNotEmpty ?? false) { if (info.addresses != null) { - info.addresses.forEach((address, label) { + info.addresses?.forEach((address, label) { walletContacts.add(WalletContact( address, info.name + ' ($label)', From f995342491d1517a06390038a7acc63090fa5a57 Mon Sep 17 00:00:00 2001 From: OleksandrSobol <dr.alexander.sobol@gmail.com> Date: Wed, 7 Jul 2021 16:50:55 +0300 Subject: [PATCH 21/28] CAKE-192 | parse_address_from_domain.dart fixed and split on 2 files (current and parse_address_from_domain_alert.dart), then moved to the entities directory; used one channel for native calls; removed unneeded spaces; fixed handle of unstoppable domain error in the MainActivity.java --- .../cakewallet/cake_wallet/MainActivity.java | 10 +--- ios/Runner/AppDelegate.swift | 57 +++++++------------ lib/entities/parse_address_from_domain.dart | 52 +++++++++++++++++ lib/entities/unstoppable_domain_address.dart | 4 +- lib/src/screens/exchange/exchange_page.dart | 2 +- .../send/parse_address_from_domain.dart | 53 ----------------- lib/src/screens/send/send_page.dart | 2 +- .../parse_address_from_domain_alert.dart | 17 ++++++ 8 files changed, 97 insertions(+), 100 deletions(-) create mode 100644 lib/entities/parse_address_from_domain.dart delete mode 100644 lib/src/screens/send/parse_address_from_domain.dart create mode 100644 lib/src/screens/send/widgets/parse_address_from_domain_alert.dart diff --git a/android/app/src/main/java/com/cakewallet/cake_wallet/MainActivity.java b/android/app/src/main/java/com/cakewallet/cake_wallet/MainActivity.java index fa6930a05..3afdc111e 100644 --- a/android/app/src/main/java/com/cakewallet/cake_wallet/MainActivity.java +++ b/android/app/src/main/java/com/cakewallet/cake_wallet/MainActivity.java @@ -21,7 +21,6 @@ import java.security.SecureRandom; public class MainActivity extends FlutterFragmentActivity { final String UTILS_CHANNEL = "com.cake_wallet/native_utils"; - final String UNSTOPPABLE_DOMAIN_CHANNEL = "com.cakewallet.cake_wallet/unstoppable-domain"; final int UNSTOPPABLE_DOMAIN_MIN_VERSION_SDK = 24; @Override @@ -33,12 +32,6 @@ public class MainActivity extends FlutterFragmentActivity { UTILS_CHANNEL); utilsChannel.setMethodCallHandler(this::handle); - - MethodChannel unstoppableDomainChannel = - new MethodChannel(flutterEngine.getDartExecutor().getBinaryMessenger(), - UNSTOPPABLE_DOMAIN_CHANNEL); - - unstoppableDomainChannel.setMethodCallHandler(this::handle); } private void handle(@NonNull MethodCall call, @NonNull MethodChannel.Result result) { @@ -80,7 +73,8 @@ public class MainActivity extends FlutterFragmentActivity { String address = resolution.getAddress(domain, ticker); handler.post(() -> result.success(address)); } catch (Exception e) { - handler.post(() -> result.error("INVALID DOMAIN", e.getMessage(), null)); + System.out.println("Expected Address, but got " + e.getMessage()); + handler.post(() -> result.success("")); } }); } diff --git a/ios/Runner/AppDelegate.swift b/ios/Runner/AppDelegate.swift index 859a6e628..50b6e39dd 100644 --- a/ios/Runner/AppDelegate.swift +++ b/ios/Runner/AppDelegate.swift @@ -5,7 +5,7 @@ import UnstoppableDomainsResolution @UIApplicationMain @objc class AppDelegate: FlutterAppDelegate { lazy var resolution : Resolution? = { - return try? Resolution() + return try? Resolution() }() override func application( @@ -75,45 +75,32 @@ import UnstoppableDomainsResolution } result(secRandom(count: count)) + case "getUnstoppableDomainAddress": + guard let args = call.arguments as? Dictionary<String, String>, + let domain = args["domain"], + let ticker = args["ticker"], + let resolution = self?.resolution else { + result(nil) + return + } + + resolution.addr(domain: domain, ticker: ticker) { addrResult in + var address : String = "" + + switch addrResult { + case .success(let returnValue): + address = returnValue + case .failure(let error): + print("Expected Address, but got \(error)") + } + + result(address) + } default: result(FlutterMethodNotImplemented) } }) - let unstoppableDomainChannel = FlutterMethodChannel(name: "com.cakewallet.cake_wallet/unstoppable-domain", binaryMessenger: controller.binaryMessenger) - unstoppableDomainChannel.setMethodCallHandler({ [weak self] - (call: FlutterMethodCall, result: @escaping FlutterResult) -> Void in - switch call.method { - case "getUnstoppableDomainAddress": - guard let args = call.arguments as? Dictionary<String, String>, - let domain = args["domain"], - let ticker = args["ticker"] else { - result(nil) - return - } - - guard let resolution = self?.resolution else { - result(nil) - return - } - - resolution.addr(domain: domain, ticker: ticker) { addrResult in - var address : String = "" - - switch addrResult { - case .success(let returnValue): - address = returnValue - case .failure(let error): - print("Expected Address, but got \(error)") - } - - result(address) - } - default: - result(FlutterMethodNotImplemented) - } - }) - GeneratedPluginRegistrant.register(with: self) return super.application(application, didFinishLaunchingWithOptions: launchOptions) } diff --git a/lib/entities/parse_address_from_domain.dart b/lib/entities/parse_address_from_domain.dart new file mode 100644 index 000000000..b81120361 --- /dev/null +++ b/lib/entities/parse_address_from_domain.dart @@ -0,0 +1,52 @@ +import 'package:cake_wallet/entities/openalias_record.dart'; +import 'package:cake_wallet/entities/unstoppable_domain_address.dart'; +import 'package:cake_wallet/src/screens/send/widgets/parse_address_from_domain_alert.dart'; +import 'package:flutter/material.dart'; +import 'package:cake_wallet/generated/i18n.dart'; + +const topLevelDomain = 'crypto'; + +Future<String> parseAddressFromDomain( + BuildContext context, String domain, String ticker) async { + try { + final domainParts = domain.split('.'); + final name = domainParts.last; + + if (domainParts.length <= 1 || domainParts.first.isEmpty || name.isEmpty) { + return domain; + } + + if (name.contains(topLevelDomain)) { + final address = await fetchUnstoppableDomainAddress(domain, ticker); + + if (address?.isEmpty ?? true) { + return domain; + } + + showAddressAlert( + context, + S.of(context).address_detected, + S.of(context).address_from_domain(domain)); + + return address; + } + + final record = await OpenaliasRecord.fetchAddressAndName( + OpenaliasRecord.formatDomainName(domain)); + + if (record == null || record.address.contains(domain)) { + return domain; + } + + showAddressAlert( + context, + S.of(context).openalias_alert_title, + S.of(context).openalias_alert_content(domain)); + + return record.address; + } catch (e) { + print(e.toString()); + } + + return domain; +} \ No newline at end of file diff --git a/lib/entities/unstoppable_domain_address.dart b/lib/entities/unstoppable_domain_address.dart index 494782d54..792f9a348 100644 --- a/lib/entities/unstoppable_domain_address.dart +++ b/lib/entities/unstoppable_domain_address.dart @@ -1,9 +1,9 @@ import 'package:flutter/services.dart'; -const channel = MethodChannel('com.cakewallet.cake_wallet/unstoppable-domain'); +const channel = MethodChannel('com.cake_wallet/native_utils'); Future<String> fetchUnstoppableDomainAddress(String domain, String ticker) async { - String address; + var address = ''; try { address = await channel.invokeMethod( diff --git a/lib/src/screens/exchange/exchange_page.dart b/lib/src/screens/exchange/exchange_page.dart index fa69d5da2..2209fe01b 100644 --- a/lib/src/screens/exchange/exchange_page.dart +++ b/lib/src/screens/exchange/exchange_page.dart @@ -1,7 +1,7 @@ import 'dart:ui'; import 'package:cake_wallet/entities/sync_status.dart'; import 'package:cake_wallet/entities/wallet_type.dart'; -import 'package:cake_wallet/src/screens/send/parse_address_from_domain.dart'; +import 'package:cake_wallet/entities/parse_address_from_domain.dart'; import 'package:cake_wallet/src/widgets/standard_checkbox.dart'; import 'package:dotted_border/dotted_border.dart'; import 'package:flutter/cupertino.dart'; diff --git a/lib/src/screens/send/parse_address_from_domain.dart b/lib/src/screens/send/parse_address_from_domain.dart deleted file mode 100644 index b114ef88f..000000000 --- a/lib/src/screens/send/parse_address_from_domain.dart +++ /dev/null @@ -1,53 +0,0 @@ -import 'package:cake_wallet/entities/openalias_record.dart'; -import 'package:cake_wallet/entities/unstoppable_domain_address.dart'; -import 'package:cake_wallet/src/widgets/alert_with_one_action.dart'; -import 'package:cake_wallet/utils/show_pop_up.dart'; -import 'package:flutter/material.dart'; -import 'package:cake_wallet/generated/i18n.dart'; - -const topLevelDomain = 'crypto'; - -Future<String> parseAddressFromDomain( - BuildContext context, String domain, String ticker) async { - try { - final name = domain.split('.').last; - - if (name.contains(topLevelDomain)) { - final address = await fetchUnstoppableDomainAddress(domain, ticker); - if (address.isNotEmpty) { - showAddressAlert( - context, - S.of(context).address_detected, - S.of(context).address_from_domain(domain)); - return address; - } - } else if (name.isNotEmpty) { - final record = await OpenaliasRecord.fetchAddressAndName( - OpenaliasRecord.formatDomainName(domain)); - if (record.name != null && record.name != domain) { - showAddressAlert( - context, - S.of(context).openalias_alert_title, - S.of(context).openalias_alert_content(domain)); - return record.address; - } - } - } catch (e) { - print(e.toString()); - } - - return domain; -} - -void showAddressAlert(BuildContext context, String title, String content) async { - await showPopUp<void>( - context: context, - builder: (BuildContext context) { - - return AlertWithOneAction( - alertTitle: title, - alertContent: content, - buttonText: S.of(context).ok, - buttonAction: () => Navigator.of(context).pop()); - }); -} \ No newline at end of file diff --git a/lib/src/screens/send/send_page.dart b/lib/src/screens/send/send_page.dart index 92b6c54d1..3d7e011e1 100644 --- a/lib/src/screens/send/send_page.dart +++ b/lib/src/screens/send/send_page.dart @@ -1,6 +1,6 @@ import 'dart:ui'; import 'package:cake_wallet/entities/transaction_priority.dart'; -import 'package:cake_wallet/src/screens/send/parse_address_from_domain.dart'; +import 'package:cake_wallet/entities/parse_address_from_domain.dart'; import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart'; import 'package:cake_wallet/src/widgets/keyboard_done_button.dart'; import 'package:cake_wallet/src/widgets/picker.dart'; diff --git a/lib/src/screens/send/widgets/parse_address_from_domain_alert.dart b/lib/src/screens/send/widgets/parse_address_from_domain_alert.dart new file mode 100644 index 000000000..760091611 --- /dev/null +++ b/lib/src/screens/send/widgets/parse_address_from_domain_alert.dart @@ -0,0 +1,17 @@ +import 'package:cake_wallet/src/widgets/alert_with_one_action.dart'; +import 'package:cake_wallet/utils/show_pop_up.dart'; +import 'package:flutter/material.dart'; +import 'package:cake_wallet/generated/i18n.dart'; + +void showAddressAlert(BuildContext context, String title, String content) async { + await showPopUp<void>( + context: context, + builder: (BuildContext context) { + + return AlertWithOneAction( + alertTitle: title, + alertContent: content, + buttonText: S.of(context).ok, + buttonAction: () => Navigator.of(context).pop()); + }); +} \ No newline at end of file From bac491c44f33e6ba0b69fd74dc2fd7bb378b3c5e Mon Sep 17 00:00:00 2001 From: OleksandrSobol <dr.alexander.sobol@gmail.com> Date: Wed, 7 Jul 2021 17:31:16 +0300 Subject: [PATCH 22/28] CAKE-192 | created ParsedAddress class; applied this class to parse_address_from_domain.dart, send_page.dart and exchange_page.dart --- lib/entities/parse_address_from_domain.dart | 30 +++++----------- lib/entities/parsed_address.dart | 8 +++++ lib/src/screens/exchange/exchange_page.dart | 38 +++++++++++++++++---- lib/src/screens/send/send_page.dart | 23 +++++++++++-- 4 files changed, 70 insertions(+), 29 deletions(-) create mode 100644 lib/entities/parsed_address.dart diff --git a/lib/entities/parse_address_from_domain.dart b/lib/entities/parse_address_from_domain.dart index b81120361..0c2b864dd 100644 --- a/lib/entities/parse_address_from_domain.dart +++ b/lib/entities/parse_address_from_domain.dart @@ -1,52 +1,40 @@ import 'package:cake_wallet/entities/openalias_record.dart'; +import 'package:cake_wallet/entities/parsed_address.dart'; import 'package:cake_wallet/entities/unstoppable_domain_address.dart'; -import 'package:cake_wallet/src/screens/send/widgets/parse_address_from_domain_alert.dart'; -import 'package:flutter/material.dart'; -import 'package:cake_wallet/generated/i18n.dart'; const topLevelDomain = 'crypto'; -Future<String> parseAddressFromDomain( - BuildContext context, String domain, String ticker) async { +Future<ParsedAddress> parseAddressFromDomain( + String domain, String ticker) async { try { final domainParts = domain.split('.'); final name = domainParts.last; if (domainParts.length <= 1 || domainParts.first.isEmpty || name.isEmpty) { - return domain; + return ParsedAddress(domain, ParseFrom.notParsed); } if (name.contains(topLevelDomain)) { final address = await fetchUnstoppableDomainAddress(domain, ticker); if (address?.isEmpty ?? true) { - return domain; + return ParsedAddress(domain, ParseFrom.notParsed); } - showAddressAlert( - context, - S.of(context).address_detected, - S.of(context).address_from_domain(domain)); - - return address; + return ParsedAddress(address, ParseFrom.unstoppableDomains); } final record = await OpenaliasRecord.fetchAddressAndName( OpenaliasRecord.formatDomainName(domain)); if (record == null || record.address.contains(domain)) { - return domain; + return ParsedAddress(domain, ParseFrom.notParsed); } - showAddressAlert( - context, - S.of(context).openalias_alert_title, - S.of(context).openalias_alert_content(domain)); - - return record.address; + return ParsedAddress(record.address, ParseFrom.openAlias); } catch (e) { print(e.toString()); } - return domain; + return ParsedAddress(domain, ParseFrom.notParsed); } \ No newline at end of file diff --git a/lib/entities/parsed_address.dart b/lib/entities/parsed_address.dart new file mode 100644 index 000000000..f293d99c2 --- /dev/null +++ b/lib/entities/parsed_address.dart @@ -0,0 +1,8 @@ +enum ParseFrom {unstoppableDomains, openAlias, notParsed} + +class ParsedAddress { + ParsedAddress(this.address, this.parseFrom); + + final String address; + final ParseFrom parseFrom; +} \ No newline at end of file diff --git a/lib/src/screens/exchange/exchange_page.dart b/lib/src/screens/exchange/exchange_page.dart index 2209fe01b..693eade72 100644 --- a/lib/src/screens/exchange/exchange_page.dart +++ b/lib/src/screens/exchange/exchange_page.dart @@ -1,7 +1,9 @@ import 'dart:ui'; +import 'package:cake_wallet/entities/parsed_address.dart'; import 'package:cake_wallet/entities/sync_status.dart'; import 'package:cake_wallet/entities/wallet_type.dart'; import 'package:cake_wallet/entities/parse_address_from_domain.dart'; +import 'package:cake_wallet/src/screens/send/widgets/parse_address_from_domain_alert.dart'; import 'package:cake_wallet/src/widgets/standard_checkbox.dart'; import 'package:dotted_border/dotted_border.dart'; import 'package:flutter/cupertino.dart'; @@ -229,7 +231,7 @@ class ExchangePage extends BasePage { final ticker = exchangeViewModel .depositCurrency.title.toLowerCase(); exchangeViewModel.depositAddress = - await parseAddressFromDomain( + await applyOpenaliasOrUnstoppableDomains( context, domain, ticker); }, ), @@ -284,7 +286,7 @@ class ExchangePage extends BasePage { final ticker = exchangeViewModel .receiveCurrency.title.toLowerCase(); exchangeViewModel.receiveAddress = - await parseAddressFromDomain( + await applyOpenaliasOrUnstoppableDomains( context, domain, ticker); }, )), @@ -514,12 +516,12 @@ class ExchangePage extends BasePage { var domain = template.depositAddress; var ticker = template.depositCurrency.toLowerCase(); exchangeViewModel.depositAddress = - await parseAddressFromDomain(context, domain, ticker); + await applyOpenaliasOrUnstoppableDomains(context, domain, ticker); domain = template.receiveAddress; ticker = template.receiveCurrency.toLowerCase(); exchangeViewModel.receiveAddress = - await parseAddressFromDomain(context, domain, ticker); + await applyOpenaliasOrUnstoppableDomains(context, domain, ticker); } void _setReactions( @@ -691,7 +693,7 @@ class ExchangePage extends BasePage { final domain = depositAddressController.text; final ticker = exchangeViewModel.depositCurrency.title.toLowerCase(); exchangeViewModel.depositAddress = - await parseAddressFromDomain(context, domain, ticker); + await applyOpenaliasOrUnstoppableDomains(context, domain, ticker); } }); @@ -701,7 +703,7 @@ class ExchangePage extends BasePage { final domain = receiveAddressController.text; final ticker = exchangeViewModel.receiveCurrency.title.toLowerCase(); exchangeViewModel.receiveAddress = - await parseAddressFromDomain(context, domain, ticker); + await applyOpenaliasOrUnstoppableDomains(context, domain, ticker); } }); @@ -775,4 +777,28 @@ class ExchangePage extends BasePage { key.currentState.addressController.text = null; } } + + Future<String> applyOpenaliasOrUnstoppableDomains( + BuildContext context, String domain, String ticker) async { + final parsedAddress = await parseAddressFromDomain(domain, ticker); + + switch (parsedAddress.parseFrom) { + case ParseFrom.unstoppableDomains: + showAddressAlert( + context, + S.of(context).address_detected, + S.of(context).address_from_domain(domain)); + break; + case ParseFrom.openAlias: + showAddressAlert( + context, + S.of(context).openalias_alert_title, + S.of(context).openalias_alert_content(domain)); + break; + case ParseFrom.notParsed: + break; + } + + return parsedAddress.address; + } } diff --git a/lib/src/screens/send/send_page.dart b/lib/src/screens/send/send_page.dart index 3d7e011e1..2aa4ebb82 100644 --- a/lib/src/screens/send/send_page.dart +++ b/lib/src/screens/send/send_page.dart @@ -1,6 +1,8 @@ import 'dart:ui'; +import 'package:cake_wallet/entities/parsed_address.dart'; import 'package:cake_wallet/entities/transaction_priority.dart'; import 'package:cake_wallet/entities/parse_address_from_domain.dart'; +import 'package:cake_wallet/src/screens/send/widgets/parse_address_from_domain_alert.dart'; import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart'; import 'package:cake_wallet/src/widgets/keyboard_done_button.dart'; import 'package:cake_wallet/src/widgets/picker.dart'; @@ -757,7 +759,24 @@ class SendPage extends BasePage { void applyOpenaliasOrUnstoppableDomains(BuildContext context) async { final domain = _addressController.text; final ticker = sendViewModel.currency.title.toLowerCase(); - _addressController.text = - await parseAddressFromDomain(context, domain, ticker); + final parsedAddress = await parseAddressFromDomain(domain, ticker); + _addressController.text = parsedAddress.address; + + switch (parsedAddress.parseFrom) { + case ParseFrom.unstoppableDomains: + showAddressAlert( + context, + S.of(context).address_detected, + S.of(context).address_from_domain(domain)); + break; + case ParseFrom.openAlias: + showAddressAlert( + context, + S.of(context).openalias_alert_title, + S.of(context).openalias_alert_content(domain)); + break; + case ParseFrom.notParsed: + break; + } } } From e03f0ce4d3cf3e072a4ed4ef4e72655421cf065a Mon Sep 17 00:00:00 2001 From: OleksandrSobol <dr.alexander.sobol@gmail.com> Date: Thu, 8 Jul 2021 17:52:48 +0300 Subject: [PATCH 23/28] CAKE-343 | added wallet_addresses.dart, monero_wallet_addresses.dart, electrum_wallet_addresses.dart, wallet_addresses_credentials.dart, monero_wallet_addresses_credentials.dart and electrum_wallet_addresses_credentials.dart to the app; applied ElectrumWalletAddresses to electrum_wallet.dart and MoneroWalletAddresses to monero_wallet.dart; fixed contact_list_view_model.dart, other view models and services --- lib/bitcoin/bitcoin_wallet_service.dart | 1 - lib/bitcoin/electrum_wallet.dart | 16 +++++++- lib/bitcoin/electrum_wallet_addresses.dart | 32 ++++++++++++++++ ...electrum_wallet_addresses_credentials.dart | 7 ++++ lib/bitcoin/litecoin_wallet_service.dart | 1 - lib/core/wallet_base.dart | 10 ----- lib/entities/wallet_addresses.dart | 30 +++++++++++++++ .../wallet_addresses_credentials.dart | 1 + lib/monero/monero_wallet.dart | 37 +++++++++--------- lib/monero/monero_wallet_addresses.dart | 38 +++++++++++++++++++ .../monero_wallet_addresses_credentials.dart | 9 +++++ lib/monero/monero_wallet_service.dart | 1 - .../contact_list/contact_list_view_model.dart | 21 +++++----- ...ero_account_edit_or_create_view_model.dart | 3 +- ...let_address_edit_or_create_view_model.dart | 2 - lib/view_model/wallet_creation_vm.dart | 1 - 16 files changed, 159 insertions(+), 51 deletions(-) create mode 100644 lib/bitcoin/electrum_wallet_addresses.dart create mode 100644 lib/bitcoin/electrum_wallet_addresses_credentials.dart create mode 100644 lib/entities/wallet_addresses.dart create mode 100644 lib/entities/wallet_addresses_credentials.dart create mode 100644 lib/monero/monero_wallet_addresses.dart create mode 100644 lib/monero/monero_wallet_addresses_credentials.dart diff --git a/lib/bitcoin/bitcoin_wallet_service.dart b/lib/bitcoin/bitcoin_wallet_service.dart index 2cd3310fd..aefe0fadf 100644 --- a/lib/bitcoin/bitcoin_wallet_service.dart +++ b/lib/bitcoin/bitcoin_wallet_service.dart @@ -44,7 +44,6 @@ class BitcoinWalletService extends WalletService< final wallet = await BitcoinWalletBase.open( password: password, name: name, walletInfo: walletInfo); await wallet.init(); - await wallet.updateAddressesInfo(); return wallet; } diff --git a/lib/bitcoin/electrum_wallet.dart b/lib/bitcoin/electrum_wallet.dart index bb0ee3c3e..d0b1c9a6c 100644 --- a/lib/bitcoin/electrum_wallet.dart +++ b/lib/bitcoin/electrum_wallet.dart @@ -1,5 +1,7 @@ import 'dart:async'; import 'dart:convert'; +import 'package:cake_wallet/bitcoin/electrum_wallet_addresses.dart'; +import 'package:cake_wallet/bitcoin/electrum_wallet_addresses_credentials.dart'; import 'package:mobx/mobx.dart'; import 'package:rxdart/subjects.dart'; import 'package:flutter/foundation.dart'; @@ -63,6 +65,8 @@ abstract class ElectrumWalletBase extends WalletBase<ElectrumBalance, ElectrumTransactionHistory(walletInfo: walletInfo, password: password); _unspent = []; _scripthashesUpdateSubject = {}; + _walletAddressInBox = ElectrumWalletAddresses(walletInfo); + _walletAddressInBoxCredentials = ElectrumWalletAddressesCredentials(); } static int estimatedTransactionSize(int inputsCount, int outputsCounts) => @@ -93,6 +97,9 @@ abstract class ElectrumWalletBase extends WalletBase<ElectrumBalance, String get xpub => hd.base58; + ElectrumWalletAddresses _walletAddressInBox; + ElectrumWalletAddressesCredentials _walletAddressInBoxCredentials; + @override String get seed => mnemonic; @@ -113,6 +120,7 @@ abstract class ElectrumWalletBase extends WalletBase<ElectrumBalance, await generateAddresses(); address = addresses[_accountIndex].address; await transactionHistory.init(); + await _updateWalletAddressInBox(); } @action @@ -125,8 +133,6 @@ abstract class ElectrumWalletBase extends WalletBase<ElectrumBalance, address = addresses[_accountIndex].address; - await updateAddressesInfo(); - await save(); } @@ -364,6 +370,7 @@ abstract class ElectrumWalletBase extends WalletBase<ElectrumBalance, final path = await makePath(); await write(path: path, password: _password, data: toJSON()); await transactionHistory.save(); + await _updateWalletAddressInBox(); } bitcoin.ECPair keyPairFor({@required int index}) => @@ -466,4 +473,9 @@ abstract class ElectrumWalletBase extends WalletBase<ElectrumBalance, balance = await _fetchBalances(); await save(); } + + Future<void> _updateWalletAddressInBox() async { + _walletAddressInBoxCredentials.address = address; + await _walletAddressInBox.update(_walletAddressInBoxCredentials); + } } diff --git a/lib/bitcoin/electrum_wallet_addresses.dart b/lib/bitcoin/electrum_wallet_addresses.dart new file mode 100644 index 000000000..ac4895ae8 --- /dev/null +++ b/lib/bitcoin/electrum_wallet_addresses.dart @@ -0,0 +1,32 @@ +import 'package:cake_wallet/bitcoin/electrum_wallet_addresses_credentials.dart'; +import 'package:cake_wallet/entities/wallet_addresses.dart'; +import 'package:cake_wallet/entities/wallet_addresses_credentials.dart'; +import 'package:cake_wallet/entities/wallet_info.dart'; + +class ElectrumWalletAddresses extends WalletAddresses { + ElectrumWalletAddresses(WalletInfo walletInfo) : super(walletInfo); + + @override + Future<void> update(WalletAddressesCredentials credentials) async { + try { + if (credentials == null) { + return; + } + + final _credentials = credentials as ElectrumWalletAddressesCredentials; + final _address = _credentials.address; + final _label = ''; + + if (_address == null || _address.isEmpty) { + return; + } + + addresses.clear(); + addresses[_address] = _label; + + await save(); + } catch (e) { + print(e.toString()); + } + } +} \ No newline at end of file diff --git a/lib/bitcoin/electrum_wallet_addresses_credentials.dart b/lib/bitcoin/electrum_wallet_addresses_credentials.dart new file mode 100644 index 000000000..30a41f8ff --- /dev/null +++ b/lib/bitcoin/electrum_wallet_addresses_credentials.dart @@ -0,0 +1,7 @@ +import 'package:cake_wallet/entities/wallet_addresses_credentials.dart'; + +class ElectrumWalletAddressesCredentials extends WalletAddressesCredentials { + ElectrumWalletAddressesCredentials(); + + String address; +} \ No newline at end of file diff --git a/lib/bitcoin/litecoin_wallet_service.dart b/lib/bitcoin/litecoin_wallet_service.dart index 071baf4af..053fd785f 100644 --- a/lib/bitcoin/litecoin_wallet_service.dart +++ b/lib/bitcoin/litecoin_wallet_service.dart @@ -45,7 +45,6 @@ class LitecoinWalletService extends WalletService< final wallet = await LitecoinWalletBase.open( password: password, name: name, walletInfo: walletInfo); await wallet.init(); - await wallet.updateAddressesInfo(); return wallet; } diff --git a/lib/core/wallet_base.dart b/lib/core/wallet_base.dart index 9e7b89e8d..f6ed64142 100644 --- a/lib/core/wallet_base.dart +++ b/lib/core/wallet_base.dart @@ -6,7 +6,6 @@ import 'package:cake_wallet/entities/wallet_info.dart'; import 'package:cake_wallet/core/pending_transaction.dart'; import 'package:cake_wallet/core/transaction_history.dart'; import 'package:cake_wallet/entities/currency_for_wallet_type.dart'; -import 'package:cake_wallet/entities/monero_transaction_priority.dart'; import 'package:cake_wallet/entities/crypto_currency.dart'; import 'package:cake_wallet/entities/sync_status.dart'; import 'package:cake_wallet/entities/node.dart'; @@ -66,13 +65,4 @@ abstract class WalletBase< Future<void> rescan({int height}); void close(); - - Future<void> updateAddressesInfo() async { - try { - walletInfo.address = address; - await walletInfo.save(); - } catch (e) { - print(e.toString()); - } - } } diff --git a/lib/entities/wallet_addresses.dart b/lib/entities/wallet_addresses.dart new file mode 100644 index 000000000..81b4fdea7 --- /dev/null +++ b/lib/entities/wallet_addresses.dart @@ -0,0 +1,30 @@ +import 'package:cake_wallet/entities/wallet_addresses_credentials.dart'; +import 'package:cake_wallet/entities/wallet_info.dart'; + +abstract class WalletAddresses { + WalletAddresses(this.walletInfo) { + addresses = walletInfo?.addresses ?? {}; + } + + final WalletInfo walletInfo; + + Map<String, String> addresses; + + Future<void> update(WalletAddressesCredentials credentials); + + Future<void> save() async { + try { + if (walletInfo == null) { + return; + } + + walletInfo.addresses = addresses; + + if (walletInfo.isInBox) { + await walletInfo.save(); + } + } catch (e) { + print(e.toString()); + } + } +} \ No newline at end of file diff --git a/lib/entities/wallet_addresses_credentials.dart b/lib/entities/wallet_addresses_credentials.dart new file mode 100644 index 000000000..fe3d2c927 --- /dev/null +++ b/lib/entities/wallet_addresses_credentials.dart @@ -0,0 +1 @@ +abstract class WalletAddressesCredentials {} \ No newline at end of file diff --git a/lib/monero/monero_wallet.dart b/lib/monero/monero_wallet.dart index c9ea9e03e..77cd2f53f 100644 --- a/lib/monero/monero_wallet.dart +++ b/lib/monero/monero_wallet.dart @@ -3,6 +3,8 @@ import 'package:cake_wallet/entities/transaction_priority.dart'; import 'package:cake_wallet/monero/monero_amount_format.dart'; import 'package:cake_wallet/monero/monero_transaction_creation_exception.dart'; import 'package:cake_wallet/monero/monero_transaction_info.dart'; +import 'package:cake_wallet/monero/monero_wallet_addresses.dart'; +import 'package:cake_wallet/monero/monero_wallet_addresses_credentials.dart'; import 'package:flutter/foundation.dart'; import 'package:mobx/mobx.dart'; import 'package:cw_monero/transaction_history.dart' @@ -47,6 +49,8 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance, _isSavingAfterSync = false; _isSavingAfterNewTransaction = false; _isTransactionUpdating = false; + _walletAllAddresses = MoneroWalletAddresses(walletInfo); + _walletAddressesCredentials = MoneroWalletAddressesCredentials(); _onAccountChangeReaction = reaction((_) => account, (Account account) { balance = MoneroBalance( fullBalance: monero_wallet.getFullBalance(accountIndex: account.id), @@ -99,6 +103,8 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance, bool _isSavingAfterNewTransaction; bool _isTransactionUpdating; int _lastSaveTimestamp; + MoneroWalletAddresses _walletAllAddresses; + MoneroWalletAddressesCredentials _walletAddressesCredentials; Future<void> init() async { accountList.update(); @@ -110,6 +116,7 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance, unlockedBalance: monero_wallet.getUnlockedBalance(accountIndex: account.id)); address = subaddress.address; + await _updateWalletAllAddressesInBox(); _setListeners(); await updateTransactions(); @@ -238,6 +245,8 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance, @override Future<void> save() async { + await _updateWalletAllAddressesInBox(); + final now = DateTime.now().millisecondsSinceEpoch; if (now - _lastSaveTimestamp < Duration(seconds: 10).inMilliseconds) { @@ -446,25 +455,13 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance, } } - @override - Future<void> updateAddressesInfo() async { - final Map<String, String> _addresses = {}; - final _subaddressList = MoneroSubaddressList(); - - accountList.accounts.forEach((account) { - _subaddressList.update(accountIndex: account.id); - _subaddressList.subaddresses.forEach((subaddress) { - _addresses[subaddress.address] = subaddress.label; - }); - }); - - try { - walletInfo.addresses = _addresses; - walletInfo.address = address; - - await walletInfo.save(); - } catch (e) { - print(e.toString()); - } + Future<void> _updateWalletAllAddressesInBox() async { + _walletAddressesCredentials + ..accountList = accountList + ..subaddressList = subaddressList; + await _walletAllAddresses.update(_walletAddressesCredentials); + subaddressList.update(accountIndex: account.id ?? 0); + subaddress = subaddressList.subaddresses.first; + address = subaddress.address; } } diff --git a/lib/monero/monero_wallet_addresses.dart b/lib/monero/monero_wallet_addresses.dart new file mode 100644 index 000000000..55680e1c9 --- /dev/null +++ b/lib/monero/monero_wallet_addresses.dart @@ -0,0 +1,38 @@ +import 'package:cake_wallet/entities/wallet_addresses.dart'; +import 'package:cake_wallet/entities/wallet_addresses_credentials.dart'; +import 'package:cake_wallet/entities/wallet_info.dart'; +import 'monero_wallet_addresses_credentials.dart'; + +class MoneroWalletAddresses extends WalletAddresses { + MoneroWalletAddresses(WalletInfo walletInfo) : super(walletInfo); + + @override + Future<void> update(WalletAddressesCredentials credentials) async { + try { + if (credentials == null) { + return; + } + + final _credentials = credentials as MoneroWalletAddressesCredentials; + final _accountList = _credentials.accountList; + final _subaddressList = _credentials.subaddressList; + + if (_accountList == null || _subaddressList == null) { + return; + } + + addresses.clear(); + + _accountList.accounts.forEach((account) { + _subaddressList.update(accountIndex: account.id); + _subaddressList.subaddresses.forEach((subaddress) { + addresses[subaddress.address] = subaddress.label; + }); + }); + + await save(); + } catch (e) { + print(e.toString()); + } + } +} \ No newline at end of file diff --git a/lib/monero/monero_wallet_addresses_credentials.dart b/lib/monero/monero_wallet_addresses_credentials.dart new file mode 100644 index 000000000..0808cb127 --- /dev/null +++ b/lib/monero/monero_wallet_addresses_credentials.dart @@ -0,0 +1,9 @@ +import 'package:cake_wallet/entities/wallet_addresses_credentials.dart'; +import 'package:cake_wallet/monero/monero_account_list.dart'; +import 'package:cake_wallet/monero/monero_subaddress_list.dart'; + +class MoneroWalletAddressesCredentials extends WalletAddressesCredentials { + + MoneroAccountList accountList; + MoneroSubaddressList subaddressList; +} \ No newline at end of file diff --git a/lib/monero/monero_wallet_service.dart b/lib/monero/monero_wallet_service.dart index 17b02dcbb..7795b8700 100644 --- a/lib/monero/monero_wallet_service.dart +++ b/lib/monero/monero_wallet_service.dart @@ -126,7 +126,6 @@ class MoneroWalletService extends WalletService< } await wallet.init(); - await wallet.updateAddressesInfo(); return wallet; } catch (e) { diff --git a/lib/view_model/contact_list/contact_list_view_model.dart b/lib/view_model/contact_list/contact_list_view_model.dart index b5143cffa..1a8bbc021 100644 --- a/lib/view_model/contact_list/contact_list_view_model.dart +++ b/lib/view_model/contact_list/contact_list_view_model.dart @@ -18,18 +18,17 @@ abstract class ContactListViewModelBase with Store { : contacts = ObservableList<ContactRecord>(), walletContacts = [] { walletInfoSource.values.forEach((info) { - if (info.address?.isNotEmpty ?? false) { - if (info.addresses != null) { - info.addresses?.forEach((address, label) { - walletContacts.add(WalletContact( - address, - info.name + ' ($label)', - walletTypeToCryptoCurrency(info.type))); - }); - } else { - walletContacts.add(WalletContact(info.address, info.name, + if (info.addresses?.isNotEmpty ?? false) { + info.addresses?.forEach((address, label) { + final name = label.isNotEmpty + ? info.name + ' ($label)' + : info.name; + + walletContacts.add(WalletContact( + address, + name, walletTypeToCryptoCurrency(info.type))); - } + }); } }); diff --git a/lib/view_model/monero_account_list/monero_account_edit_or_create_view_model.dart b/lib/view_model/monero_account_list/monero_account_edit_or_create_view_model.dart index cf999e414..a86fa2773 100644 --- a/lib/view_model/monero_account_list/monero_account_edit_or_create_view_model.dart +++ b/lib/view_model/monero_account_list/monero_account_edit_or_create_view_model.dart @@ -42,8 +42,7 @@ abstract class MoneroAccountEditOrCreateViewModelBase with Store { await _moneroAccountList.addAccount(label: label); } - await _wallet.updateAddressesInfo(); - + await _wallet.save(); state = ExecutedSuccessfullyState(); } catch (e) { state = FailureState(e.toString()); diff --git a/lib/view_model/wallet_address_list/wallet_address_edit_or_create_view_model.dart b/lib/view_model/wallet_address_list/wallet_address_edit_or_create_view_model.dart index 6b6873cd5..b018c542d 100644 --- a/lib/view_model/wallet_address_list/wallet_address_edit_or_create_view_model.dart +++ b/lib/view_model/wallet_address_list/wallet_address_edit_or_create_view_model.dart @@ -54,8 +54,6 @@ abstract class WalletAddressEditOrCreateViewModelBase with Store { await _createNew(); } - await _wallet.updateAddressesInfo(); - state = AddressSavedSuccessfully(); } catch (e) { state = AddressEditOrCreateStateFailure(error: e.toString()); diff --git a/lib/view_model/wallet_creation_vm.dart b/lib/view_model/wallet_creation_vm.dart index 54cad29c9..58ee24087 100644 --- a/lib/view_model/wallet_creation_vm.dart +++ b/lib/view_model/wallet_creation_vm.dart @@ -54,7 +54,6 @@ abstract class WalletCreationVMBase with Store { await _walletInfoSource.add(walletInfo); _appStore.changeCurrentWallet(wallet); _appStore.authenticationStore.allowed(); - await wallet.updateAddressesInfo(); state = ExecutedSuccessfullyState(); } catch (e) { state = FailureState(e.toString()); From 0d367c4b455c2ea9972649aad731f725754471dc Mon Sep 17 00:00:00 2001 From: OleksandrSobol <dr.alexander.sobol@gmail.com> Date: Tue, 13 Jul 2021 08:46:34 +0300 Subject: [PATCH 24/28] CAKE-343 | added bitcoin_wallet_addresses.dart and litecoin_wallet_addresses.dart tj the app; moved addresses from wallets to wallet addresses classes; deleted wallet addresses credentials --- lib/bitcoin/bitcoin_wallet.dart | 16 +-- lib/bitcoin/bitcoin_wallet_addresses.dart | 33 +++++ lib/bitcoin/electrum_wallet.dart | 100 ++------------- lib/bitcoin/electrum_wallet_addresses.dart | 119 +++++++++++++++--- ...electrum_wallet_addresses_credentials.dart | 7 -- lib/bitcoin/litecoin_wallet.dart | 36 ++---- lib/bitcoin/litecoin_wallet_addresses.dart | 54 ++++++++ lib/buy/buy_provider.dart | 2 +- lib/core/wallet_base.dart | 7 +- lib/di.dart | 2 +- lib/entities/wallet_addresses.dart | 18 ++- .../wallet_addresses_credentials.dart | 1 - lib/monero/monero_wallet.dart | 90 +++---------- lib/monero/monero_wallet_addresses.dart | 87 ++++++++++--- .../monero_wallet_addresses_credentials.dart | 9 -- lib/monero/monero_wallet_service.dart | 2 +- lib/reactions/on_current_wallet_change.dart | 2 +- lib/src/screens/exchange/exchange_page.dart | 14 ++- .../exchange/exchange_template_page.dart | 14 ++- .../dashboard/dashboard_view_model.dart | 24 ++-- .../exchange/exchange_view_model.dart | 9 +- .../monero_account_list_view_model.dart | 9 +- ...let_address_edit_or_create_view_model.dart | 20 +-- .../wallet_address_list_view_model.dart | 19 +-- lib/view_model/wallet_creation_vm.dart | 2 +- 25 files changed, 384 insertions(+), 312 deletions(-) create mode 100644 lib/bitcoin/bitcoin_wallet_addresses.dart delete mode 100644 lib/bitcoin/electrum_wallet_addresses_credentials.dart create mode 100644 lib/bitcoin/litecoin_wallet_addresses.dart delete mode 100644 lib/entities/wallet_addresses_credentials.dart delete mode 100644 lib/monero/monero_wallet_addresses_credentials.dart diff --git a/lib/bitcoin/bitcoin_wallet.dart b/lib/bitcoin/bitcoin_wallet.dart index fd8402887..02af295fc 100644 --- a/lib/bitcoin/bitcoin_wallet.dart +++ b/lib/bitcoin/bitcoin_wallet.dart @@ -1,12 +1,12 @@ import 'package:mobx/mobx.dart'; import 'package:flutter/foundation.dart'; import 'package:bitcoin_flutter/bitcoin_flutter.dart' as bitcoin; -import 'package:cake_wallet/bitcoin/utils.dart'; import 'package:cake_wallet/bitcoin/electrum_wallet_snapshot.dart'; import 'package:cake_wallet/bitcoin/electrum_wallet.dart'; import 'package:cake_wallet/entities/wallet_info.dart'; import 'package:cake_wallet/bitcoin/bitcoin_address_record.dart'; import 'package:cake_wallet/bitcoin/electrum_balance.dart'; +import 'package:cake_wallet/bitcoin/bitcoin_wallet_addresses.dart'; part 'bitcoin_wallet.g.dart'; @@ -26,8 +26,14 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store { walletInfo: walletInfo, networkType: bitcoin.bitcoin, initialAddresses: initialAddresses, - initialBalance: initialBalance, - accountIndex: accountIndex); + initialBalance: initialBalance) { + walletAddresses = BitcoinWalletAddresses( + walletInfo, + initialAddresses: initialAddresses, + accountIndex: accountIndex, + hd: hd, + networkType: networkType); + } static Future<BitcoinWallet> open({ @required String name, @@ -44,8 +50,4 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store { initialBalance: snp.balance, accountIndex: snp.accountIndex); } - - @override - String getAddress({@required int index, @required bitcoin.HDWallet hd}) => - generateP2WPKHAddress(hd: hd, index: index, networkType: networkType); } diff --git a/lib/bitcoin/bitcoin_wallet_addresses.dart b/lib/bitcoin/bitcoin_wallet_addresses.dart new file mode 100644 index 000000000..ecded3cb8 --- /dev/null +++ b/lib/bitcoin/bitcoin_wallet_addresses.dart @@ -0,0 +1,33 @@ +import 'package:bitcoin_flutter/bitcoin_flutter.dart' as bitcoin; +import 'package:cake_wallet/bitcoin/utils.dart'; +import 'package:cake_wallet/bitcoin/bitcoin_address_record.dart'; +import 'package:cake_wallet/bitcoin/electrum_wallet_addresses.dart'; +import 'package:cake_wallet/entities/wallet_info.dart'; +import 'package:flutter/foundation.dart'; +import 'package:mobx/mobx.dart'; + +part 'bitcoin_wallet_addresses.g.dart'; + +class BitcoinWalletAddresses = BitcoinWalletAddressesBase + with _$BitcoinWalletAddresses; + +abstract class BitcoinWalletAddressesBase extends ElectrumWalletAddresses + with Store { + BitcoinWalletAddressesBase( + WalletInfo walletInfo, + {@required List<BitcoinAddressRecord> initialAddresses, + int accountIndex = 0, + @required bitcoin.HDWallet hd, + @required this.networkType}) + : super( + walletInfo, + initialAddresses: initialAddresses, + accountIndex: accountIndex, + hd: hd); + + bitcoin.NetworkType networkType; + + @override + String getAddress({@required int index, @required bitcoin.HDWallet hd}) => + generateP2WPKHAddress(hd: hd, index: index, networkType: networkType); +} \ No newline at end of file diff --git a/lib/bitcoin/electrum_wallet.dart b/lib/bitcoin/electrum_wallet.dart index d0b1c9a6c..38fc192de 100644 --- a/lib/bitcoin/electrum_wallet.dart +++ b/lib/bitcoin/electrum_wallet.dart @@ -1,7 +1,6 @@ import 'dart:async'; import 'dart:convert'; import 'package:cake_wallet/bitcoin/electrum_wallet_addresses.dart'; -import 'package:cake_wallet/bitcoin/electrum_wallet_addresses_credentials.dart'; import 'package:mobx/mobx.dart'; import 'package:rxdart/subjects.dart'; import 'package:flutter/foundation.dart'; @@ -44,18 +43,14 @@ abstract class ElectrumWalletBase extends WalletBase<ElectrumBalance, @required this.networkType, @required this.mnemonic, ElectrumClient electrumClient, - int accountIndex = 0, ElectrumBalance initialBalance}) : balance = initialBalance ?? const ElectrumBalance(confirmed: 0, unconfirmed: 0), hd = bitcoin.HDWallet.fromSeed(mnemonicToSeedBytes(mnemonic), network: networkType) .derivePath("m/0'/0"), - addresses = ObservableList<BitcoinAddressRecord>.of( - (initialAddresses ?? []).toSet()), syncStatus = NotConnectedSyncStatus(), _password = password, - _accountIndex = accountIndex, _feeRates = <int>[], _isTransactionUpdating = false, super(walletInfo) { @@ -65,8 +60,6 @@ abstract class ElectrumWalletBase extends WalletBase<ElectrumBalance, ElectrumTransactionHistory(walletInfo: walletInfo, password: password); _unspent = []; _scripthashesUpdateSubject = {}; - _walletAddressInBox = ElectrumWalletAddresses(walletInfo); - _walletAddressInBoxCredentials = ElectrumWalletAddressesCredentials(); } static int estimatedTransactionSize(int inputsCount, int outputsCounts) => @@ -78,8 +71,7 @@ abstract class ElectrumWalletBase extends WalletBase<ElectrumBalance, ElectrumClient electrumClient; @override - @observable - String address; + ElectrumWalletAddresses walletAddresses; @override @observable @@ -89,17 +81,12 @@ abstract class ElectrumWalletBase extends WalletBase<ElectrumBalance, @observable SyncStatus syncStatus; - ObservableList<BitcoinAddressRecord> addresses; - - List<String> get scriptHashes => addresses + List<String> get scriptHashes => walletAddresses.addresses .map((addr) => scriptHash(addr.address, networkType: networkType)) .toList(); String get xpub => hd.base58; - ElectrumWalletAddresses _walletAddressInBox; - ElectrumWalletAddressesCredentials _walletAddressInBoxCredentials; - @override String get seed => mnemonic; @@ -112,81 +99,21 @@ abstract class ElectrumWalletBase extends WalletBase<ElectrumBalance, final String _password; List<BitcoinUnspent> _unspent; List<int> _feeRates; - int _accountIndex; Map<String, BehaviorSubject<Object>> _scripthashesUpdateSubject; bool _isTransactionUpdating; Future<void> init() async { - await generateAddresses(); - address = addresses[_accountIndex].address; + await walletAddresses.init(); await transactionHistory.init(); - await _updateWalletAddressInBox(); - } - - @action - Future<void> nextAddress() async { - _accountIndex += 1; - - if (_accountIndex >= addresses.length) { - _accountIndex = 0; - } - - address = addresses[_accountIndex].address; - await save(); } - Future<void> generateAddresses() async { - if (addresses.length < 33) { - final addressesCount = 33 - addresses.length; - await generateNewAddresses(addressesCount, - startIndex: addresses.length, hd: hd); - } - } - - Future<BitcoinAddressRecord> generateNewAddress( - {bool isHidden = false, bitcoin.HDWallet hd}) async { - _accountIndex += 1; - final _hd = hd ?? this.hd; - final address = BitcoinAddressRecord( - getAddress(index: _accountIndex, hd: _hd), - index: _accountIndex, - isHidden: isHidden); - addresses.add(address); - await save(); - return address; - } - - Future<List<BitcoinAddressRecord>> generateNewAddresses(int count, - {int startIndex = 0, bitcoin.HDWallet hd, bool isHidden = false}) async { - final list = <BitcoinAddressRecord>[]; - - for (var i = startIndex; i < count + startIndex; i++) { - final address = BitcoinAddressRecord(getAddress(index: i, hd: hd), - index: i, isHidden: isHidden); - list.add(address); - } - - addresses.addAll(list); - await save(); - return list; - } - - Future<void> updateAddress(String address) async { - for (final addr in addresses) { - if (addr.address == address) { - await save(); - break; - } - } - } - @action @override Future<void> startSync() async { try { syncStatus = StartingSyncStatus(); - updateTransactions(); + await updateTransactions(); _subscribeForUpdates(); await _updateBalance(); await _updateUnspent(); @@ -238,7 +165,7 @@ abstract class ElectrumWalletBase extends WalletBase<ElectrumBalance, ? allAmount : credentialsAmount; final txb = bitcoin.TransactionBuilder(network: networkType); - final changeAddress = address; + final changeAddress = walletAddresses.address; var leftAmount = amount; var totalInputAmount = 0; @@ -321,8 +248,8 @@ abstract class ElectrumWalletBase extends WalletBase<ElectrumBalance, String toJSON() => json.encode({ 'mnemonic': mnemonic, - 'account_index': _accountIndex.toString(), - 'addresses': addresses.map((addr) => addr.toJSON()).toList(), + 'account_index': walletAddresses.accountIndex.toString(), + 'addresses': walletAddresses.addresses.map((addr) => addr.toJSON()).toList(), 'balance': balance?.toJSON() }); @@ -370,7 +297,6 @@ abstract class ElectrumWalletBase extends WalletBase<ElectrumBalance, final path = await makePath(); await write(path: path, password: _password, data: toJSON()); await transactionHistory.save(); - await _updateWalletAddressInBox(); } bitcoin.ECPair keyPairFor({@required int index}) => @@ -386,13 +312,12 @@ abstract class ElectrumWalletBase extends WalletBase<ElectrumBalance, } catch (_) {} } - String getAddress({@required int index, @required bitcoin.HDWallet hd}) => ''; - Future<String> makePath() async => pathForWallet(name: walletInfo.name, type: walletInfo.type); Future<void> _updateUnspent() async { - final unspent = await Future.wait(addresses.map((address) => electrumClient + final unspent = await Future.wait(walletAddresses + .addresses.map((address) => electrumClient .getListUnspentWithAddress(address.address, networkType) .then((unspent) => unspent .map((unspent) => BitcoinUnspent.fromJSON(address, unspent))))); @@ -403,7 +328,7 @@ abstract class ElectrumWalletBase extends WalletBase<ElectrumBalance, {@required String hash, @required int height}) async { final tx = await electrumClient.getTransactionExpanded(hash: hash); return ElectrumTransactionInfo.fromElectrumVerbose(tx, walletInfo.type, - height: height, addresses: addresses); + height: height, addresses: walletAddresses.addresses); } @override @@ -473,9 +398,4 @@ abstract class ElectrumWalletBase extends WalletBase<ElectrumBalance, balance = await _fetchBalances(); await save(); } - - Future<void> _updateWalletAddressInBox() async { - _walletAddressInBoxCredentials.address = address; - await _walletAddressInBox.update(_walletAddressInBoxCredentials); - } } diff --git a/lib/bitcoin/electrum_wallet_addresses.dart b/lib/bitcoin/electrum_wallet_addresses.dart index ac4895ae8..843ac4065 100644 --- a/lib/bitcoin/electrum_wallet_addresses.dart +++ b/lib/bitcoin/electrum_wallet_addresses.dart @@ -1,30 +1,109 @@ -import 'package:cake_wallet/bitcoin/electrum_wallet_addresses_credentials.dart'; +import 'package:bitcoin_flutter/bitcoin_flutter.dart' as bitcoin; +import 'package:cake_wallet/bitcoin/bitcoin_address_record.dart'; import 'package:cake_wallet/entities/wallet_addresses.dart'; -import 'package:cake_wallet/entities/wallet_addresses_credentials.dart'; import 'package:cake_wallet/entities/wallet_info.dart'; +import 'package:flutter/foundation.dart'; +import 'package:mobx/mobx.dart'; -class ElectrumWalletAddresses extends WalletAddresses { - ElectrumWalletAddresses(WalletInfo walletInfo) : super(walletInfo); +part 'electrum_wallet_addresses.g.dart'; + +class ElectrumWalletAddresses = ElectrumWalletAddressesBase + with _$ElectrumWalletAddresses; + +abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store { + ElectrumWalletAddressesBase(WalletInfo walletInfo, + {@required List<BitcoinAddressRecord> initialAddresses, + int accountIndex = 0, + @required bitcoin.HDWallet hd}) + : super(walletInfo) { + this.hd = hd; + this.accountIndex = accountIndex; + addresses = ObservableList<BitcoinAddressRecord>.of( + (initialAddresses ?? []).toSet()); + } @override - Future<void> update(WalletAddressesCredentials credentials) async { + @observable + String address; + + bitcoin.HDWallet hd; + + ObservableList<BitcoinAddressRecord> addresses; + + int accountIndex; + + @override + Future<void> init() async { + await generateAddresses(); + address = addresses[accountIndex].address; + await updateAddressesInBox(); + } + + @action + Future<void> nextAddress() async { + accountIndex += 1; + + if (accountIndex >= addresses.length) { + accountIndex = 0; + } + + address = addresses[accountIndex].address; + + await updateAddressesInBox(); + } + + Future<void> generateAddresses() async { + if (addresses.length < 33) { + final addressesCount = 33 - addresses.length; + await generateNewAddresses(addressesCount, + startIndex: addresses.length, hd: hd); + } + } + + Future<BitcoinAddressRecord> generateNewAddress( + {bool isHidden = false, bitcoin.HDWallet hd}) async { + accountIndex += 1; + final _hd = hd ?? this.hd; + final address = BitcoinAddressRecord( + getAddress(index: accountIndex, hd: _hd), + index: accountIndex, + isHidden: isHidden); + addresses.add(address); + return address; + } + + Future<List<BitcoinAddressRecord>> generateNewAddresses(int count, + {int startIndex = 0, bitcoin.HDWallet hd, bool isHidden = false}) async { + final list = <BitcoinAddressRecord>[]; + + for (var i = startIndex; i < count + startIndex; i++) { + final address = BitcoinAddressRecord(getAddress(index: i, hd: hd), + index: i, isHidden: isHidden); + list.add(address); + } + + addresses.addAll(list); + return list; + } + + /*Future<void> updateAddress(String address) async { + for (final addr in addresses) { + if (addr.address == address) { + await save(); + break; + } + } + }*/ + + String getAddress({@required int index, @required bitcoin.HDWallet hd}) => ''; + + @override + Future<void> updateAddressesInBox() async { try { - if (credentials == null) { - return; - } + addressesMap.clear(); + addressesMap[address] = ''; - final _credentials = credentials as ElectrumWalletAddressesCredentials; - final _address = _credentials.address; - final _label = ''; - - if (_address == null || _address.isEmpty) { - return; - } - - addresses.clear(); - addresses[_address] = _label; - - await save(); + await saveAddressesInBox(); } catch (e) { print(e.toString()); } diff --git a/lib/bitcoin/electrum_wallet_addresses_credentials.dart b/lib/bitcoin/electrum_wallet_addresses_credentials.dart deleted file mode 100644 index 30a41f8ff..000000000 --- a/lib/bitcoin/electrum_wallet_addresses_credentials.dart +++ /dev/null @@ -1,7 +0,0 @@ -import 'package:cake_wallet/entities/wallet_addresses_credentials.dart'; - -class ElectrumWalletAddressesCredentials extends WalletAddressesCredentials { - ElectrumWalletAddressesCredentials(); - - String address; -} \ No newline at end of file diff --git a/lib/bitcoin/litecoin_wallet.dart b/lib/bitcoin/litecoin_wallet.dart index 209d1904e..bfb7da6d6 100644 --- a/lib/bitcoin/litecoin_wallet.dart +++ b/lib/bitcoin/litecoin_wallet.dart @@ -1,6 +1,5 @@ -import 'package:bitcoin_flutter/bitcoin_flutter.dart' as bitcoin; -import 'package:cake_wallet/bitcoin/bitcoin_mnemonic.dart'; import 'package:cake_wallet/bitcoin/bitcoin_transaction_priority.dart'; +import 'package:cake_wallet/bitcoin/litecoin_wallet_addresses.dart'; import 'package:cake_wallet/entities/transaction_priority.dart'; import 'package:flutter/foundation.dart'; import 'package:mobx/mobx.dart'; @@ -10,7 +9,6 @@ import 'package:cake_wallet/bitcoin/electrum_wallet.dart'; import 'package:cake_wallet/bitcoin/bitcoin_address_record.dart'; import 'package:cake_wallet/bitcoin/electrum_balance.dart'; import 'package:cake_wallet/bitcoin/litecoin_network.dart'; -import 'package:cake_wallet/bitcoin/utils.dart'; part 'litecoin_wallet.g.dart'; @@ -30,8 +28,15 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { walletInfo: walletInfo, networkType: litecoinNetwork, initialAddresses: initialAddresses, - initialBalance: initialBalance, - accountIndex: accountIndex); + initialBalance: initialBalance) { + walletAddresses = LitecoinWalletAddresses( + walletInfo, + initialAddresses: initialAddresses, + accountIndex: accountIndex, + hd: hd, + networkType: networkType, + mnemonic: mnemonic); + } static Future<LitecoinWallet> open({ @required String name, @@ -49,27 +54,6 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { accountIndex: snp.accountIndex); } - @override - String getAddress({@required int index, @required bitcoin.HDWallet hd}) => - generateP2WPKHAddress(hd: hd, index: index, networkType: networkType); - - @override - Future<void> generateAddresses() async { - if (addresses.length < 33) { - final addressesCount = 22 - addresses.length; - await generateNewAddresses(addressesCount, - hd: hd, startIndex: addresses.length); - - final changeRoot = bitcoin.HDWallet.fromSeed( - mnemonicToSeedBytes(mnemonic), - network: networkType) - .derivePath("m/0'/1"); - - await generateNewAddresses(11, - startIndex: 0, hd: changeRoot, isHidden: true); - } - } - @override int feeRate(TransactionPriority priority) { if (priority is LitecoinTransactionPriority) { diff --git a/lib/bitcoin/litecoin_wallet_addresses.dart b/lib/bitcoin/litecoin_wallet_addresses.dart new file mode 100644 index 000000000..19ade7566 --- /dev/null +++ b/lib/bitcoin/litecoin_wallet_addresses.dart @@ -0,0 +1,54 @@ +import 'package:bitcoin_flutter/bitcoin_flutter.dart' as bitcoin; +import 'package:cake_wallet/bitcoin/bitcoin_mnemonic.dart'; +import 'package:cake_wallet/bitcoin/utils.dart'; +import 'package:cake_wallet/bitcoin/bitcoin_address_record.dart'; +import 'package:cake_wallet/bitcoin/electrum_wallet_addresses.dart'; +import 'package:cake_wallet/entities/wallet_info.dart'; +import 'package:flutter/foundation.dart'; +import 'package:mobx/mobx.dart'; + +part 'litecoin_wallet_addresses.g.dart'; + +class LitecoinWalletAddresses = LitecoinWalletAddressesBase + with _$LitecoinWalletAddresses; + +abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses + with Store { + LitecoinWalletAddressesBase( + WalletInfo walletInfo, + {@required List<BitcoinAddressRecord> initialAddresses, + int accountIndex = 0, + @required bitcoin.HDWallet hd, + @required this.networkType, + @required this.mnemonic}) + : super( + walletInfo, + initialAddresses: initialAddresses, + accountIndex: accountIndex, + hd: hd); + + bitcoin.NetworkType networkType; + + final String mnemonic; + + @override + String getAddress({@required int index, @required bitcoin.HDWallet hd}) => + generateP2WPKHAddress(hd: hd, index: index, networkType: networkType); + + @override + Future<void> generateAddresses() async { + if (addresses.length < 33) { + final addressesCount = 22 - addresses.length; + await generateNewAddresses(addressesCount, + hd: hd, startIndex: addresses.length); + + final changeRoot = bitcoin.HDWallet.fromSeed( + mnemonicToSeedBytes(mnemonic), + network: networkType) + .derivePath("m/0'/1"); + + await generateNewAddresses(11, + startIndex: 0, hd: changeRoot, isHidden: true); + } + } +} \ No newline at end of file diff --git a/lib/buy/buy_provider.dart b/lib/buy/buy_provider.dart index b908d7bc5..dc450ad7e 100644 --- a/lib/buy/buy_provider.dart +++ b/lib/buy/buy_provider.dart @@ -15,7 +15,7 @@ abstract class BuyProvider { String get trackUrl; WalletType get walletType => wallet.type; - String get walletAddress => wallet.address; + String get walletAddress => wallet.walletAddresses.address; String get walletId => wallet.id; @override diff --git a/lib/core/wallet_base.dart b/lib/core/wallet_base.dart index f6ed64142..edcb05466 100644 --- a/lib/core/wallet_base.dart +++ b/lib/core/wallet_base.dart @@ -1,6 +1,7 @@ import 'package:cake_wallet/entities/balance.dart'; import 'package:cake_wallet/entities/transaction_info.dart'; import 'package:cake_wallet/entities/transaction_priority.dart'; +import 'package:cake_wallet/entities/wallet_addresses.dart'; import 'package:flutter/foundation.dart'; import 'package:cake_wallet/entities/wallet_info.dart'; import 'package:cake_wallet/core/pending_transaction.dart'; @@ -30,9 +31,9 @@ abstract class WalletBase< String get name => walletInfo.name; - String get address; + //String get address; - set address(String address); + //set address(String address); BalanceType get balance; @@ -44,6 +45,8 @@ abstract class WalletBase< Object get keys; + WalletAddresses get walletAddresses; + HistoryType transactionHistory; Future<void> connectToNode({@required Node node}); diff --git a/lib/di.dart b/lib/di.dart index 1f5350370..3b709d707 100644 --- a/lib/di.dart +++ b/lib/di.dart @@ -361,7 +361,7 @@ Future setup( getIt.registerFactoryParam<MoneroAccountEditOrCreateViewModel, AccountListItem, void>( (AccountListItem account, _) => MoneroAccountEditOrCreateViewModel( - (getIt.get<AppStore>().wallet as MoneroWallet).accountList, + (getIt.get<AppStore>().wallet as MoneroWallet).walletAddresses.accountList, wallet: getIt.get<AppStore>().wallet, accountListItem: account)); diff --git a/lib/entities/wallet_addresses.dart b/lib/entities/wallet_addresses.dart index 81b4fdea7..cd5331911 100644 --- a/lib/entities/wallet_addresses.dart +++ b/lib/entities/wallet_addresses.dart @@ -1,24 +1,30 @@ -import 'package:cake_wallet/entities/wallet_addresses_credentials.dart'; import 'package:cake_wallet/entities/wallet_info.dart'; abstract class WalletAddresses { WalletAddresses(this.walletInfo) { - addresses = walletInfo?.addresses ?? {}; + addressesMap = {}; } final WalletInfo walletInfo; - Map<String, String> addresses; + String get address; - Future<void> update(WalletAddressesCredentials credentials); + set address(String address); - Future<void> save() async { + Map<String, String> addressesMap; + + Future<void> init(); + + Future<void> updateAddressesInBox(); + + Future<void> saveAddressesInBox() async { try { if (walletInfo == null) { return; } - walletInfo.addresses = addresses; + walletInfo.address = address; + walletInfo.addresses = addressesMap; if (walletInfo.isInBox) { await walletInfo.save(); diff --git a/lib/entities/wallet_addresses_credentials.dart b/lib/entities/wallet_addresses_credentials.dart deleted file mode 100644 index fe3d2c927..000000000 --- a/lib/entities/wallet_addresses_credentials.dart +++ /dev/null @@ -1 +0,0 @@ -abstract class WalletAddressesCredentials {} \ No newline at end of file diff --git a/lib/monero/monero_wallet.dart b/lib/monero/monero_wallet.dart index 77cd2f53f..411982a88 100644 --- a/lib/monero/monero_wallet.dart +++ b/lib/monero/monero_wallet.dart @@ -4,7 +4,6 @@ import 'package:cake_wallet/monero/monero_amount_format.dart'; import 'package:cake_wallet/monero/monero_transaction_creation_exception.dart'; import 'package:cake_wallet/monero/monero_transaction_info.dart'; import 'package:cake_wallet/monero/monero_wallet_addresses.dart'; -import 'package:cake_wallet/monero/monero_wallet_addresses_credentials.dart'; import 'package:flutter/foundation.dart'; import 'package:mobx/mobx.dart'; import 'package:cw_monero/transaction_history.dart' @@ -17,10 +16,7 @@ import 'package:cake_wallet/monero/pending_monero_transaction.dart'; import 'package:cake_wallet/monero/monero_wallet_keys.dart'; import 'package:cake_wallet/monero/monero_balance.dart'; import 'package:cake_wallet/monero/monero_transaction_history.dart'; -import 'package:cake_wallet/monero/monero_subaddress_list.dart'; -import 'package:cake_wallet/monero/monero_account_list.dart'; import 'package:cake_wallet/monero/account.dart'; -import 'package:cake_wallet/monero/subaddress.dart'; import 'package:cake_wallet/core/pending_transaction.dart'; import 'package:cake_wallet/core/wallet_base.dart'; import 'package:cake_wallet/entities/sync_status.dart'; @@ -37,9 +33,7 @@ class MoneroWallet = MoneroWalletBase with _$MoneroWallet; abstract class MoneroWalletBase extends WalletBase<MoneroBalance, MoneroTransactionHistory, MoneroTransactionInfo> with Store { MoneroWalletBase({WalletInfo walletInfo}) - : accountList = MoneroAccountList(), - subaddressList = MoneroSubaddressList(), - super(walletInfo) { + : super(walletInfo) { transactionHistory = MoneroTransactionHistory(); balance = MoneroBalance( fullBalance: monero_wallet.getFullBalance(accountIndex: 0), @@ -49,35 +43,26 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance, _isSavingAfterSync = false; _isSavingAfterNewTransaction = false; _isTransactionUpdating = false; - _walletAllAddresses = MoneroWalletAddresses(walletInfo); - _walletAddressesCredentials = MoneroWalletAddressesCredentials(); - _onAccountChangeReaction = reaction((_) => account, (Account account) { + walletAddresses = MoneroWalletAddresses(walletInfo); + _onAccountChangeReaction = reaction((_) => walletAddresses.account, + (Account account) { balance = MoneroBalance( fullBalance: monero_wallet.getFullBalance(accountIndex: account.id), unlockedBalance: monero_wallet.getUnlockedBalance(accountIndex: account.id)); - subaddressList.update(accountIndex: account.id); - subaddress = subaddressList.subaddresses.first; - address = subaddress.address; + walletAddresses.updateSubaddressList(accountIndex: account.id); }); } static const int _autoAfterSyncSaveInterval = 60000; - @observable - Account account; - - @observable - Subaddress subaddress; + @override + MoneroWalletAddresses walletAddresses; @override @observable SyncStatus syncStatus; - @override - @observable - String address; - @override @observable MoneroBalance balance; @@ -92,10 +77,6 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance, publicSpendKey: monero_wallet.getPublicSpendKey(), publicViewKey: monero_wallet.getPublicViewKey()); - final MoneroSubaddressList subaddressList; - - final MoneroAccountList accountList; - SyncListener _listener; ReactionDisposer _onAccountChangeReaction; int _lastAutosaveTimestamp; @@ -103,20 +84,13 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance, bool _isSavingAfterNewTransaction; bool _isTransactionUpdating; int _lastSaveTimestamp; - MoneroWalletAddresses _walletAllAddresses; - MoneroWalletAddressesCredentials _walletAddressesCredentials; Future<void> init() async { - accountList.update(); - account = accountList.accounts.first; - subaddressList.update(accountIndex: account.id ?? 0); - subaddress = subaddressList.getAll().first; + await walletAddresses.init(); balance = MoneroBalance( - fullBalance: monero_wallet.getFullBalance(accountIndex: account.id), + fullBalance: monero_wallet.getFullBalance(accountIndex: walletAddresses.account.id), unlockedBalance: - monero_wallet.getUnlockedBalance(accountIndex: account.id)); - address = subaddress.address; - await _updateWalletAllAddressesInBox(); + monero_wallet.getUnlockedBalance(accountIndex: walletAddresses.account.id)); _setListeners(); await updateTransactions(); @@ -136,24 +110,6 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance, _onAccountChangeReaction?.reaction?.dispose(); } - bool validate() { - accountList.update(); - final accountListLength = accountList.accounts?.length ?? 0; - - if (accountListLength <= 0) { - return false; - } - - subaddressList.update(accountIndex: accountList.accounts.first.id); - final subaddressListLength = subaddressList.subaddresses?.length ?? 0; - - if (subaddressListLength <= 0) { - return false; - } - - return true; - } - @override Future<void> connectToNode({@required Node node}) async { try { @@ -196,7 +152,7 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance, ? moneroParseAmount(amount: _credentials.amount) : null; final unlockedBalance = - monero_wallet.getUnlockedBalance(accountIndex: account.id); + monero_wallet.getUnlockedBalance(accountIndex: walletAddresses.account.id); if ((amount != null && unlockedBalance < amount) || (amount == null && unlockedBalance <= 0)) { @@ -216,7 +172,7 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance, paymentId: _credentials.paymentId, amount: _credentials.amount, priorityRaw: _credentials.priority.serialize(), - accountIndex: account.id); + accountIndex: walletAddresses.account.id); return PendingMoneroTransaction(pendingTransactionDescription); } @@ -245,7 +201,7 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance, @override Future<void> save() async { - await _updateWalletAllAddressesInBox(); + await walletAddresses.updateAddressesInBox(); final now = DateTime.now().millisecondsSinceEpoch; @@ -274,7 +230,7 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance, monero_wallet.rescanBlockchainAsync(); await startSync(); _askForUpdateBalance(); - accountList.update(); + walletAddresses.accountList.update(); await _askForUpdateTransactionHistory(); await save(); await walletInfo.save(); @@ -373,10 +329,10 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance, await updateTransactions(); int _getFullBalance() => - monero_wallet.getFullBalance(accountIndex: account.id); + monero_wallet.getFullBalance(accountIndex: walletAddresses.account.id); int _getUnlockedBalance() => - monero_wallet.getUnlockedBalance(accountIndex: account.id); + monero_wallet.getUnlockedBalance(accountIndex: walletAddresses.account.id); Future<void> _afterSyncSave() async { try { @@ -423,13 +379,13 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance, if (walletInfo.isRecovery) { await _askForUpdateTransactionHistory(); _askForUpdateBalance(); - accountList.update(); + walletAddresses.accountList.update(); } if (blocksLeft < 100) { await _askForUpdateTransactionHistory(); _askForUpdateBalance(); - accountList.update(); + walletAddresses.accountList.update(); syncStatus = SyncedSyncStatus(); await _afterSyncSave(); @@ -454,14 +410,4 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance, print(e.toString()); } } - - Future<void> _updateWalletAllAddressesInBox() async { - _walletAddressesCredentials - ..accountList = accountList - ..subaddressList = subaddressList; - await _walletAllAddresses.update(_walletAddressesCredentials); - subaddressList.update(accountIndex: account.id ?? 0); - subaddress = subaddressList.subaddresses.first; - address = subaddress.address; - } } diff --git a/lib/monero/monero_wallet_addresses.dart b/lib/monero/monero_wallet_addresses.dart index 55680e1c9..d14caa27b 100644 --- a/lib/monero/monero_wallet_addresses.dart +++ b/lib/monero/monero_wallet_addresses.dart @@ -1,38 +1,85 @@ import 'package:cake_wallet/entities/wallet_addresses.dart'; -import 'package:cake_wallet/entities/wallet_addresses_credentials.dart'; import 'package:cake_wallet/entities/wallet_info.dart'; -import 'monero_wallet_addresses_credentials.dart'; +import 'package:cake_wallet/monero/account.dart'; +import 'package:cake_wallet/monero/monero_account_list.dart'; +import 'package:cake_wallet/monero/monero_subaddress_list.dart'; +import 'package:cake_wallet/monero/subaddress.dart'; +import 'package:mobx/mobx.dart'; -class MoneroWalletAddresses extends WalletAddresses { - MoneroWalletAddresses(WalletInfo walletInfo) : super(walletInfo); +part 'monero_wallet_addresses.g.dart'; + +class MoneroWalletAddresses = MoneroWalletAddressesBase + with _$MoneroWalletAddresses; + +abstract class MoneroWalletAddressesBase extends WalletAddresses with Store { + MoneroWalletAddressesBase(WalletInfo walletInfo) : super(walletInfo) { + accountList = MoneroAccountList(); + subaddressList = MoneroSubaddressList(); + } @override - Future<void> update(WalletAddressesCredentials credentials) async { + @observable + String address; + + @observable + Account account; + + @observable + Subaddress subaddress; + + MoneroSubaddressList subaddressList; + + MoneroAccountList accountList; + + @override + Future<void> init() async { + accountList.update(); + account = accountList.accounts.first; + updateSubaddressList(accountIndex: account.id ?? 0); + await updateAddressesInBox(); + } + + @override + Future<void> updateAddressesInBox() async { try { - if (credentials == null) { - return; - } + final _subaddressList = MoneroSubaddressList(); - final _credentials = credentials as MoneroWalletAddressesCredentials; - final _accountList = _credentials.accountList; - final _subaddressList = _credentials.subaddressList; + addressesMap.clear(); - if (_accountList == null || _subaddressList == null) { - return; - } - - addresses.clear(); - - _accountList.accounts.forEach((account) { + accountList.accounts.forEach((account) { _subaddressList.update(accountIndex: account.id); _subaddressList.subaddresses.forEach((subaddress) { - addresses[subaddress.address] = subaddress.label; + addressesMap[subaddress.address] = subaddress.label; }); }); - await save(); + await saveAddressesInBox(); } catch (e) { print(e.toString()); } } + + bool validate() { + accountList.update(); + final accountListLength = accountList.accounts?.length ?? 0; + + if (accountListLength <= 0) { + return false; + } + + subaddressList.update(accountIndex: accountList.accounts.first.id); + final subaddressListLength = subaddressList.subaddresses?.length ?? 0; + + if (subaddressListLength <= 0) { + return false; + } + + return true; + } + + void updateSubaddressList({int accountIndex}) { + subaddressList.update(accountIndex: accountIndex); + subaddress = subaddressList.subaddresses.first; + address = subaddress.address; + } } \ No newline at end of file diff --git a/lib/monero/monero_wallet_addresses_credentials.dart b/lib/monero/monero_wallet_addresses_credentials.dart deleted file mode 100644 index 0808cb127..000000000 --- a/lib/monero/monero_wallet_addresses_credentials.dart +++ /dev/null @@ -1,9 +0,0 @@ -import 'package:cake_wallet/entities/wallet_addresses_credentials.dart'; -import 'package:cake_wallet/monero/monero_account_list.dart'; -import 'package:cake_wallet/monero/monero_subaddress_list.dart'; - -class MoneroWalletAddressesCredentials extends WalletAddressesCredentials { - - MoneroAccountList accountList; - MoneroSubaddressList subaddressList; -} \ No newline at end of file diff --git a/lib/monero/monero_wallet_service.dart b/lib/monero/monero_wallet_service.dart index 7795b8700..67db056d9 100644 --- a/lib/monero/monero_wallet_service.dart +++ b/lib/monero/monero_wallet_service.dart @@ -117,7 +117,7 @@ class MoneroWalletService extends WalletService< (info) => info.id == WalletBase.idFor(name, getType()), orElse: () => null); final wallet = MoneroWallet(walletInfo: walletInfo); - final isValid = wallet.validate(); + final isValid = wallet.walletAddresses.validate(); if (!isValid) { await _removeCache(name); diff --git a/lib/reactions/on_current_wallet_change.dart b/lib/reactions/on_current_wallet_change.dart index 428f2703e..5c702b0af 100644 --- a/lib/reactions/on_current_wallet_change.dart +++ b/lib/reactions/on_current_wallet_change.dart @@ -37,7 +37,7 @@ void startCurrentWalletChangeReaction(AppStore appStore, await wallet.connectToNode(node: node); if (wallet.walletInfo.address?.isEmpty ?? true) { - wallet.walletInfo.address = wallet.address; + wallet.walletInfo.address = wallet.walletAddresses.address; if (wallet.walletInfo.isInBox) { await wallet.walletInfo.save(); diff --git a/lib/src/screens/exchange/exchange_page.dart b/lib/src/screens/exchange/exchange_page.dart index 3eb23da3a..6b4e9e3cb 100644 --- a/lib/src/screens/exchange/exchange_page.dart +++ b/lib/src/screens/exchange/exchange_page.dart @@ -179,7 +179,7 @@ class ExchangePage extends BasePage { initialAddress: exchangeViewModel.depositCurrency == exchangeViewModel.wallet.currency - ? exchangeViewModel.wallet.address + ? exchangeViewModel.wallet.walletAddresses.address : exchangeViewModel.depositAddress, initialIsAmountEditable: true, initialIsAddressEditable: @@ -240,7 +240,7 @@ class ExchangePage extends BasePage { initialAddress: exchangeViewModel .receiveCurrency == exchangeViewModel.wallet.currency - ? exchangeViewModel.wallet.address + ? exchangeViewModel.wallet.walletAddresses.address : exchangeViewModel.receiveAddress, initialIsAmountEditable: exchangeViewModel .isReceiveAmountEditable, @@ -646,7 +646,8 @@ class ExchangePage extends BasePage { } }); - reaction((_) => exchangeViewModel.wallet.address, (String address) { + reaction((_) => exchangeViewModel.wallet.walletAddresses.address, + (String address) { if (exchangeViewModel.depositCurrency == CryptoCurrency.xmr) { depositKey.currentState.changeAddress(address: address); } @@ -707,7 +708,8 @@ class ExchangePage extends BasePage { isCurrentTypeWallet ? exchangeViewModel.wallet.name : null); key.currentState.changeAddress( - address: isCurrentTypeWallet ? exchangeViewModel.wallet.address : ''); + address: isCurrentTypeWallet + ? exchangeViewModel.wallet.walletAddresses.address : ''); key.currentState.changeAmount(amount: ''); } @@ -719,9 +721,9 @@ class ExchangePage extends BasePage { if (isCurrentTypeWallet) { key.currentState.changeWalletName(exchangeViewModel.wallet.name); key.currentState.addressController.text = - exchangeViewModel.wallet.address; + exchangeViewModel.wallet.walletAddresses.address; } else if (key.currentState.addressController.text == - exchangeViewModel.wallet.address) { + exchangeViewModel.wallet.walletAddresses.address) { key.currentState.changeWalletName(null); key.currentState.addressController.text = null; } diff --git a/lib/src/screens/exchange/exchange_template_page.dart b/lib/src/screens/exchange/exchange_template_page.dart index 7df55f320..c978a535f 100644 --- a/lib/src/screens/exchange/exchange_template_page.dart +++ b/lib/src/screens/exchange/exchange_template_page.dart @@ -129,7 +129,7 @@ class ExchangeTemplatePage extends BasePage { initialAddress: exchangeViewModel .depositCurrency == exchangeViewModel.wallet.currency - ? exchangeViewModel.wallet.address + ? exchangeViewModel.wallet.walletAddresses.address : exchangeViewModel.depositAddress, initialIsAmountEditable: true, initialIsAddressEditable: exchangeViewModel @@ -168,7 +168,7 @@ class ExchangeTemplatePage extends BasePage { initialAddress: exchangeViewModel.receiveCurrency == exchangeViewModel.wallet.currency - ? exchangeViewModel.wallet.address + ? exchangeViewModel.wallet.walletAddresses.address : exchangeViewModel.receiveAddress, initialIsAmountEditable: exchangeViewModel.provider is @@ -383,7 +383,8 @@ class ExchangeTemplatePage extends BasePage { } }); - reaction((_) => exchangeViewModel.wallet.address, (String address) { + reaction((_) => exchangeViewModel.wallet.walletAddresses.address, + (String address) { if (exchangeViewModel.depositCurrency == CryptoCurrency.xmr) { depositKey.currentState.changeAddress(address: address); } @@ -405,7 +406,8 @@ class ExchangeTemplatePage extends BasePage { isCurrentTypeWallet ? exchangeViewModel.wallet.name : null); key.currentState.changeAddress( - address: isCurrentTypeWallet ? exchangeViewModel.wallet.address : ''); + address: isCurrentTypeWallet + ? exchangeViewModel.wallet.walletAddresses.address : ''); key.currentState.changeAmount(amount: ''); } @@ -417,9 +419,9 @@ class ExchangeTemplatePage extends BasePage { if (isCurrentTypeWallet) { key.currentState.changeWalletName(exchangeViewModel.wallet.name); key.currentState.addressController.text = - exchangeViewModel.wallet.address; + exchangeViewModel.wallet.walletAddresses.address; } else if (key.currentState.addressController.text == - exchangeViewModel.wallet.address) { + exchangeViewModel.wallet.walletAddresses.address) { key.currentState.changeWalletName(null); key.currentState.addressController.text = null; } diff --git a/lib/view_model/dashboard/dashboard_view_model.dart b/lib/view_model/dashboard/dashboard_view_model.dart index e35316d11..0777d3ecd 100644 --- a/lib/view_model/dashboard/dashboard_view_model.dart +++ b/lib/view_model/dashboard/dashboard_view_model.dart @@ -92,17 +92,17 @@ abstract class DashboardViewModelBase with Store { final _wallet = wallet; if (_wallet is MoneroWallet) { - subname = _wallet.account?.label; + subname = _wallet.walletAddresses.account?.label; - _onMoneroAccountChangeReaction = reaction((_) => _wallet.account, - (Account account) => _onMoneroAccountChange(_wallet)); + _onMoneroAccountChangeReaction = reaction((_) => _wallet.walletAddresses + .account, (Account account) => _onMoneroAccountChange(_wallet)); _onMoneroBalanceChangeReaction = reaction((_) => _wallet.balance, (MoneroBalance balance) => _onMoneroTransactionsUpdate(_wallet)); final _accountTransactions = _wallet .transactionHistory.transactions.values - .where((tx) => tx.accountIndex == _wallet.account.id) + .where((tx) => tx.accountIndex == _wallet.walletAddresses.account.id) .toList(); transactions = ObservableList.of(_accountTransactions.map((transaction) => @@ -131,7 +131,7 @@ abstract class DashboardViewModelBase with Store { filter: (TransactionInfo tx) { final wallet = _wallet; if (tx is MoneroTransactionInfo && wallet is MoneroWallet) { - return tx.accountIndex == wallet.account.id; + return tx.accountIndex == wallet.walletAddresses.account.id; } return true; @@ -153,7 +153,7 @@ abstract class DashboardViewModelBase with Store { String subname; @computed - String get address => wallet.address; + String get address => wallet.walletAddresses.address; @computed SyncStatus get status => wallet.syncStatus; @@ -251,13 +251,13 @@ abstract class DashboardViewModelBase with Store { wallet.type == WalletType.bitcoin && wallet.seed.split(' ').length < 24; if (wallet is MoneroWallet) { - subname = wallet.account?.label; + subname = wallet.walletAddresses.account?.label; _onMoneroAccountChangeReaction?.reaction?.dispose(); _onMoneroBalanceChangeReaction?.reaction?.dispose(); - _onMoneroAccountChangeReaction = reaction((_) => wallet.account, - (Account account) => _onMoneroAccountChange(wallet)); + _onMoneroAccountChangeReaction = reaction((_) => wallet.walletAddresses + .account, (Account account) => _onMoneroAccountChange(wallet)); _onMoneroBalanceChangeReaction = reaction((_) => wallet.balance, (MoneroBalance balance) => _onMoneroTransactionsUpdate(wallet)); @@ -284,7 +284,7 @@ abstract class DashboardViewModelBase with Store { settingsStore: appStore.settingsStore), filter: (TransactionInfo tx) { if (tx is MoneroTransactionInfo && wallet is MoneroWallet) { - return tx.accountIndex == wallet.account.id; + return tx.accountIndex == wallet.walletAddresses.account.id; } return true; @@ -293,7 +293,7 @@ abstract class DashboardViewModelBase with Store { @action void _onMoneroAccountChange(MoneroWallet wallet) { - subname = wallet.account?.label; + subname = wallet.walletAddresses.account?.label; _onMoneroTransactionsUpdate(wallet); } @@ -302,7 +302,7 @@ abstract class DashboardViewModelBase with Store { transactions.clear(); final _accountTransactions = wallet.transactionHistory.transactions.values - .where((tx) => tx.accountIndex == wallet.account.id) + .where((tx) => tx.accountIndex == wallet.walletAddresses.account.id) .toList(); transactions.addAll(_accountTransactions.map((transaction) => diff --git a/lib/view_model/exchange/exchange_view_model.dart b/lib/view_model/exchange/exchange_view_model.dart index f3f00cd77..01246542b 100644 --- a/lib/view_model/exchange/exchange_view_model.dart +++ b/lib/view_model/exchange/exchange_view_model.dart @@ -41,7 +41,8 @@ abstract class ExchangeViewModelBase with Store { depositAmount = ''; receiveAmount = ''; receiveAddress = ''; - depositAddress = depositCurrency == wallet.currency ? wallet.address : ''; + depositAddress = depositCurrency == wallet.currency + ? wallet.walletAddresses.address : ''; limitsState = LimitsInitialState(); tradeState = ExchangeTradeStateInitial(); _cryptoNumberFormat = NumberFormat()..maximumFractionDigits = 12; @@ -308,8 +309,10 @@ abstract class ExchangeViewModelBase with Store { isReceiveAmountEntered = false; depositAmount = ''; receiveAmount = ''; - depositAddress = depositCurrency == wallet.currency ? wallet.address : ''; - receiveAddress = receiveCurrency == wallet.currency ? wallet.address : ''; + depositAddress = depositCurrency == wallet.currency + ? wallet.walletAddresses.address : ''; + receiveAddress = receiveCurrency == wallet.currency + ? wallet.walletAddresses.address : ''; isDepositAddressEnabled = !(depositCurrency == wallet.currency); isReceiveAddressEnabled = !(receiveCurrency == wallet.currency); isFixedRateMode = false; diff --git a/lib/view_model/monero_account_list/monero_account_list_view_model.dart b/lib/view_model/monero_account_list/monero_account_list_view_model.dart index c6d6aa686..ca0eb67ef 100644 --- a/lib/view_model/monero_account_list/monero_account_list_view_model.dart +++ b/lib/view_model/monero_account_list/monero_account_list_view_model.dart @@ -20,15 +20,16 @@ abstract class MoneroAccountListViewModelBase with Store { } @computed - List<AccountListItem> get accounts => _moneroWallet.accountList.accounts - .map((acc) => AccountListItem( + List<AccountListItem> get accounts => _moneroWallet.walletAddresses + .accountList.accounts.map((acc) => AccountListItem( label: acc.label, id: acc.id, - isSelected: acc.id == _moneroWallet.account.id)) + isSelected: acc.id == _moneroWallet.walletAddresses.account.id)) .toList(); final MoneroWallet _moneroWallet; void select(AccountListItem item) => - _moneroWallet.account = Account(id: item.id, label: item.label); + _moneroWallet.walletAddresses.account = + Account(id: item.id, label: item.label); } diff --git a/lib/view_model/wallet_address_list/wallet_address_edit_or_create_view_model.dart b/lib/view_model/wallet_address_list/wallet_address_edit_or_create_view_model.dart index b018c542d..3676b3bf6 100644 --- a/lib/view_model/wallet_address_list/wallet_address_edit_or_create_view_model.dart +++ b/lib/view_model/wallet_address_list/wallet_address_edit_or_create_view_model.dart @@ -64,12 +64,15 @@ abstract class WalletAddressEditOrCreateViewModelBase with Store { final wallet = _wallet; if (wallet is ElectrumWallet) { - await wallet.generateNewAddress(); + await wallet.walletAddresses.generateNewAddress(); + await wallet.save(); } if (wallet is MoneroWallet) { - await wallet.subaddressList - .addSubaddress(accountIndex: wallet.account.id, label: label); + await wallet.walletAddresses.subaddressList + .addSubaddress( + accountIndex: wallet.walletAddresses.account.id, + label: label); await wallet.save(); } } @@ -77,13 +80,14 @@ abstract class WalletAddressEditOrCreateViewModelBase with Store { Future<void> _update() async { final wallet = _wallet; - if (wallet is BitcoinWallet) { - await wallet.updateAddress(_item.address as String); - } + /*if (wallet is BitcoinWallet) { + await wallet.walletAddresses.updateAddress(_item.address as String); + await wallet.save(); + }*/ if (wallet is MoneroWallet) { - await wallet.subaddressList.setLabelSubaddress( - accountIndex: wallet.account.id, + await wallet.walletAddresses.subaddressList.setLabelSubaddress( + accountIndex: wallet.walletAddresses.account.id, addressIndex: _item.id as int, label: label); await wallet.save(); diff --git a/lib/view_model/wallet_address_list/wallet_address_list_view_model.dart b/lib/view_model/wallet_address_list/wallet_address_list_view_model.dart index b15443222..a4d6eff66 100644 --- a/lib/view_model/wallet_address_list/wallet_address_list_view_model.dart +++ b/lib/view_model/wallet_address_list/wallet_address_list_view_model.dart @@ -80,7 +80,7 @@ abstract class WalletAddressListViewModelBase with Store { @computed WalletAddressListItem get address => - WalletAddressListItem(address: _wallet.address); + WalletAddressListItem(address: _wallet.walletAddresses.address); @computed PaymentURI get uri { @@ -105,8 +105,10 @@ abstract class WalletAddressListViewModelBase with Store { final addressList = ObservableList<ListItem>(); if (wallet is MoneroWallet) { - final primaryAddress = wallet.subaddressList.subaddresses.first; - addressList.addAll(wallet.subaddressList.subaddresses.map((subaddress) { + final primaryAddress = + wallet.walletAddresses.subaddressList.subaddresses.first; + addressList.addAll(wallet.walletAddresses.subaddressList.subaddresses + .map((subaddress) { final isPrimary = subaddress == primaryAddress; return WalletAddressListItem( @@ -118,8 +120,8 @@ abstract class WalletAddressListViewModelBase with Store { } if (wallet is BitcoinWallet) { - final primaryAddress = wallet.addresses.first; - final bitcoinAddresses = wallet.addresses.map((addr) { + final primaryAddress = wallet.walletAddresses.addresses.first; + final bitcoinAddresses = wallet.walletAddresses.addresses.map((addr) { final isPrimary = addr == primaryAddress; return WalletAddressListItem( @@ -139,7 +141,7 @@ abstract class WalletAddressListViewModelBase with Store { final wallet = _wallet; if (wallet is MoneroWallet) { - return wallet.account.label; + return wallet.walletAddresses.account.label; } return null; @@ -160,7 +162,7 @@ abstract class WalletAddressListViewModelBase with Store { @action void setAddress(WalletAddressListItem address) => - _wallet.address = address.address; + _wallet.walletAddresses.address = address.address; void _init() { _baseItems = []; @@ -177,7 +179,8 @@ abstract class WalletAddressListViewModelBase with Store { final wallet = _wallet; if (wallet is ElectrumWallet) { - wallet.nextAddress(); + wallet.walletAddresses.nextAddress(); + wallet.save(); } } } diff --git a/lib/view_model/wallet_creation_vm.dart b/lib/view_model/wallet_creation_vm.dart index 58ee24087..402d3be8f 100644 --- a/lib/view_model/wallet_creation_vm.dart +++ b/lib/view_model/wallet_creation_vm.dart @@ -50,7 +50,7 @@ abstract class WalletCreationVMBase with Store { dirPath: dirPath); credentials.walletInfo = walletInfo; final wallet = await process(credentials); - walletInfo.address = wallet.address; + walletInfo.address = wallet.walletAddresses.address; await _walletInfoSource.add(walletInfo); _appStore.changeCurrentWallet(wallet); _appStore.authenticationStore.allowed(); From 008936435b287adbf06bba5114e988eead1707fb Mon Sep 17 00:00:00 2001 From: OleksandrSobol <dr.alexander.sobol@gmail.com> Date: Fri, 16 Jul 2021 13:28:46 +0300 Subject: [PATCH 25/28] CAKE-192 | added name property to parsed_address.dart; fixed parse_address_from_domain.dart --- lib/entities/parse_address_from_domain.dart | 29 +++++++++++++-------- lib/entities/parsed_address.dart | 6 ++++- lib/src/screens/exchange/exchange_page.dart | 4 +-- lib/src/screens/send/send_page.dart | 4 +-- 4 files changed, 27 insertions(+), 16 deletions(-) diff --git a/lib/entities/parse_address_from_domain.dart b/lib/entities/parse_address_from_domain.dart index 0c2b864dd..3dc12e6c4 100644 --- a/lib/entities/parse_address_from_domain.dart +++ b/lib/entities/parse_address_from_domain.dart @@ -7,34 +7,41 @@ const topLevelDomain = 'crypto'; Future<ParsedAddress> parseAddressFromDomain( String domain, String ticker) async { try { - final domainParts = domain.split('.'); + final formattedName = OpenaliasRecord.formatDomainName(domain); + final domainParts = formattedName.split('.'); final name = domainParts.last; if (domainParts.length <= 1 || domainParts.first.isEmpty || name.isEmpty) { - return ParsedAddress(domain, ParseFrom.notParsed); + return ParsedAddress(address: domain); } if (name.contains(topLevelDomain)) { - final address = await fetchUnstoppableDomainAddress(domain, ticker); + final address = + await fetchUnstoppableDomainAddress(formattedName, ticker); if (address?.isEmpty ?? true) { - return ParsedAddress(domain, ParseFrom.notParsed); + return ParsedAddress(address: domain); } - return ParsedAddress(address, ParseFrom.unstoppableDomains); + return ParsedAddress( + address: address, + name: formattedName, + parseFrom: ParseFrom.unstoppableDomains); } - final record = await OpenaliasRecord.fetchAddressAndName( - OpenaliasRecord.formatDomainName(domain)); + final record = await OpenaliasRecord.fetchAddressAndName(formattedName); - if (record == null || record.address.contains(domain)) { - return ParsedAddress(domain, ParseFrom.notParsed); + if (record == null || record.address.contains(formattedName)) { + return ParsedAddress(address: domain); } - return ParsedAddress(record.address, ParseFrom.openAlias); + return ParsedAddress( + address: record.address, + name: record.name, + parseFrom: ParseFrom.openAlias); } catch (e) { print(e.toString()); } - return ParsedAddress(domain, ParseFrom.notParsed); + return ParsedAddress(address: domain); } \ No newline at end of file diff --git a/lib/entities/parsed_address.dart b/lib/entities/parsed_address.dart index f293d99c2..91fec6750 100644 --- a/lib/entities/parsed_address.dart +++ b/lib/entities/parsed_address.dart @@ -1,8 +1,12 @@ enum ParseFrom {unstoppableDomains, openAlias, notParsed} class ParsedAddress { - ParsedAddress(this.address, this.parseFrom); + ParsedAddress({ + this.address = '', + this.name = '', + this.parseFrom = ParseFrom.notParsed}); final String address; + final String name; final ParseFrom parseFrom; } \ No newline at end of file diff --git a/lib/src/screens/exchange/exchange_page.dart b/lib/src/screens/exchange/exchange_page.dart index 693eade72..286134f16 100644 --- a/lib/src/screens/exchange/exchange_page.dart +++ b/lib/src/screens/exchange/exchange_page.dart @@ -787,13 +787,13 @@ class ExchangePage extends BasePage { showAddressAlert( context, S.of(context).address_detected, - S.of(context).address_from_domain(domain)); + S.of(context).address_from_domain(parsedAddress.name)); break; case ParseFrom.openAlias: showAddressAlert( context, S.of(context).openalias_alert_title, - S.of(context).openalias_alert_content(domain)); + S.of(context).openalias_alert_content(parsedAddress.name)); break; case ParseFrom.notParsed: break; diff --git a/lib/src/screens/send/send_page.dart b/lib/src/screens/send/send_page.dart index 2aa4ebb82..aae86483c 100644 --- a/lib/src/screens/send/send_page.dart +++ b/lib/src/screens/send/send_page.dart @@ -767,13 +767,13 @@ class SendPage extends BasePage { showAddressAlert( context, S.of(context).address_detected, - S.of(context).address_from_domain(domain)); + S.of(context).address_from_domain(parsedAddress.name)); break; case ParseFrom.openAlias: showAddressAlert( context, S.of(context).openalias_alert_title, - S.of(context).openalias_alert_content(domain)); + S.of(context).openalias_alert_content(parsedAddress.name)); break; case ParseFrom.notParsed: break; From 6855a6d68bf91a030fe10fafd16bd2eb7f56dcb4 Mon Sep 17 00:00:00 2001 From: OleksandrSobol <dr.alexander.sobol@gmail.com> Date: Fri, 16 Jul 2021 13:44:12 +0300 Subject: [PATCH 26/28] CAKE-192 | fixed parse_address_from_domain.dart --- lib/entities/parse_address_from_domain.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/entities/parse_address_from_domain.dart b/lib/entities/parse_address_from_domain.dart index 3dc12e6c4..45571e358 100644 --- a/lib/entities/parse_address_from_domain.dart +++ b/lib/entities/parse_address_from_domain.dart @@ -17,7 +17,7 @@ Future<ParsedAddress> parseAddressFromDomain( if (name.contains(topLevelDomain)) { final address = - await fetchUnstoppableDomainAddress(formattedName, ticker); + await fetchUnstoppableDomainAddress(domain, ticker); if (address?.isEmpty ?? true) { return ParsedAddress(address: domain); @@ -25,7 +25,7 @@ Future<ParsedAddress> parseAddressFromDomain( return ParsedAddress( address: address, - name: formattedName, + name: domain, parseFrom: ParseFrom.unstoppableDomains); } From 8759c2ce5652d24c2f3a10190cc359d21f895d42 Mon Sep 17 00:00:00 2001 From: M <m@cakewallet.com> Date: Wed, 21 Jul 2021 20:07:56 +0300 Subject: [PATCH 27/28] Changed backup wallet files call. --- lib/monero/monero_wallet.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/monero/monero_wallet.dart b/lib/monero/monero_wallet.dart index 4105e5de2..b51c94edf 100644 --- a/lib/monero/monero_wallet.dart +++ b/lib/monero/monero_wallet.dart @@ -245,9 +245,9 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance, return; } + await backupWalletFiles(name); _lastSaveTimestamp = now; await monero_wallet.store(); - await backupWalletFiles(name); } Future<int> getNodeHeight() async => monero_wallet.getNodeHeight(); From 95e47b7a6accd7a86cc2de58556b4998c780812a Mon Sep 17 00:00:00 2001 From: M <m@cakewallet.com> Date: Thu, 22 Jul 2021 13:34:42 +0300 Subject: [PATCH 28/28] Fixes for iOS project. --- ios/Runner.xcodeproj/project.pbxproj | 12 ++++++------ ios/Runner/AppDelegate.swift | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index 6f6193063..8fba27457 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -366,7 +366,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; - CURRENT_PROJECT_VERSION = 49; + CURRENT_PROJECT_VERSION = 51; DEVELOPMENT_TEAM = 32J6BB6VUS; ENABLE_BITCODE = NO; EXCLUDED_SOURCE_FILE_NAMES = ""; @@ -384,7 +384,7 @@ "$(inherited)", "$(PROJECT_DIR)/Flutter", ); - MARKETING_VERSION = 4.2.3; + MARKETING_VERSION = 4.2.4; PRODUCT_BUNDLE_IDENTIFIER = com.fotolockr.cakewallet; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; @@ -510,7 +510,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; - CURRENT_PROJECT_VERSION = 49; + CURRENT_PROJECT_VERSION = 51; DEVELOPMENT_TEAM = 32J6BB6VUS; ENABLE_BITCODE = NO; EXCLUDED_SOURCE_FILE_NAMES = ""; @@ -528,7 +528,7 @@ "$(inherited)", "$(PROJECT_DIR)/Flutter", ); - MARKETING_VERSION = 4.2.3; + MARKETING_VERSION = 4.2.4; PRODUCT_BUNDLE_IDENTIFIER = com.fotolockr.cakewallet; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; @@ -546,7 +546,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; - CURRENT_PROJECT_VERSION = 49; + CURRENT_PROJECT_VERSION = 51; DEVELOPMENT_TEAM = 32J6BB6VUS; ENABLE_BITCODE = NO; EXCLUDED_SOURCE_FILE_NAMES = ""; @@ -564,7 +564,7 @@ "$(inherited)", "$(PROJECT_DIR)/Flutter", ); - MARKETING_VERSION = 4.2.3; + MARKETING_VERSION = 4.2.4; PRODUCT_BUNDLE_IDENTIFIER = com.fotolockr.cakewallet; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; diff --git a/ios/Runner/AppDelegate.swift b/ios/Runner/AppDelegate.swift index 50b6e39dd..ebf578d09 100644 --- a/ios/Runner/AppDelegate.swift +++ b/ios/Runner/AppDelegate.swift @@ -65,7 +65,7 @@ import UnstoppableDomainsResolution let utilsChannel = FlutterMethodChannel( name: "com.cake_wallet/native_utils", binaryMessenger: controller.binaryMessenger) - utilsChannel.setMethodCallHandler({ (call: FlutterMethodCall, result: @escaping FlutterResult) -> Void in + utilsChannel.setMethodCallHandler({ [weak self] (call: FlutterMethodCall, result: @escaping FlutterResult) -> Void in switch call.method { case "sec_random": guard let args = call.arguments as? Dictionary<String, Any>,