diff --git a/android/app/build.gradle b/android/app/build.gradle index ee68ec0d..3251a6e9 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -71,8 +71,9 @@ android { release { signingConfig signingConfigs.release - minifyEnabled true - useProguard true + shrinkResources false + minifyEnabled false + useProguard false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } @@ -85,8 +86,9 @@ 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' implementation 'com.google.firebase:firebase-core:19.0.0' implementation 'com.google.firebase:firebase-messaging:19.0.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 26cc5618..3afdc111 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,14 +10,19 @@ 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 com.unstoppabledomains.resolution.DomainResolution; +import com.unstoppabledomains.resolution.Resolution; + import java.security.SecureRandom; public class MainActivity extends FlutterFragmentActivity { final String UTILS_CHANNEL = "com.cake_wallet/native_utils"; - + final int UNSTOPPABLE_DOMAIN_MIN_VERSION_SDK = 24; + @Override public void configureFlutterEngine(@NonNull FlutterEngine flutterEngine) { GeneratedPluginRegistrant.registerWith(flutterEngine); @@ -33,17 +38,44 @@ public class MainActivity extends FlutterFragmentActivity { 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()); + 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 >= UNSTOPPABLE_DOMAIN_MIN_VERSION_SDK) { + getUnstoppableDomainAddress(call, result); + } else { + handler.post(() -> result.success("")); + } + break; + default: + handler.post(() -> result.notImplemented()); } } catch (Exception e) { handler.post(() -> 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) { + System.out.println("Expected Address, but got " + e.getMessage()); + handler.post(() -> result.success("")); + } + }); + } +} \ No newline at end of file diff --git a/android/build.gradle b/android/build.gradle index 0d11c6b9..39e58da4 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' classpath 'com.google.gms:google-services:4.3.8' } } diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties index 296b146b..b7ca2e6d 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 3cfff7b1..3c8f789c 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' pod 'Firebase/Messaging', '6.33.0' end diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 50f9b20d..4be2c24b 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 @@ -150,6 +153,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): @@ -174,14 +181,17 @@ 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 - Firebase - FirebaseCore - FirebaseCoreDiagnostics @@ -198,6 +208,7 @@ SPEC REPOS: - SDWebImage - SwiftProtobuf - SwiftyGif + - UnstoppableDomainsResolution EXTERNAL SOURCES: barcode_scan: @@ -239,6 +250,7 @@ EXTERNAL SOURCES: SPEC CHECKSUMS: barcode_scan: a5c27959edfafaa0c771905bad0b29d6d39e4479 + BigInt: f668a80089607f521586bbe29513d708491ef2f7 connectivity: c4130b2985d4ef6fd26f9702e886bd5260681467 CryptoSwift: 093499be1a94b0cae36e6c26b70870668cb56060 cw_monero: 78f369253cc913efc23db9cf6be81a11eaf40fe1 @@ -246,6 +258,7 @@ SPEC CHECKSUMS: DKImagePickerController: b5eb7f7a388e4643264105d648d01f727110fc3d DKPhotoGallery: fdfad5125a9fdda9cc57df834d49df790dbb4179 esys_flutter_share: 403498dab005b36ce1f8d7aff377e81f0621b0b4 + EthereumAddress: 39fe8e11cf04e4e9902b55ae653dbc4e0aee5f30 file_picker: 3e6c3790de664ccf9b882732d9db5eaf6b8d4eb1 Firebase: 8db6f2d1b2c5e2984efba4949a145875a8f65fe5 firebase_core: 5d6a02f3d85acd5f8321c2d6d62877626a670659 @@ -273,9 +286,9 @@ SPEC CHECKSUMS: shared_preferences: af6bfa751691cdc24be3045c43ec037377ada40d SwiftProtobuf: 4ef85479c18ca85b5482b343df9c319c62bda699 SwiftyGif: e466e86c660d343357ab944a819a101c4127cb40 + UnstoppableDomainsResolution: 63abb84858d3e91eb838a5bfa6f7e3c0e0593f24 url_launcher: 6fef411d543ceb26efce54b05a0a40bfd74cbbef webview_flutter: 9f491a9b5a66f2573946a389b2677987b0ff8c0b -PODFILE CHECKSUM: f316539722a6f9dbb0d0f1065a27fa7ea38c6f88 -COCOAPODS: 1.9.3 +COCOAPODS: 1.10.1 diff --git a/ios/Runner/AppDelegate.swift b/ios/Runner/AppDelegate.swift index 2a3505ef..50b6e39d 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]? @@ -70,6 +75,27 @@ import Flutter } result(secRandom(count: count)) + case "getUnstoppableDomainAddress": + guard let args = call.arguments as? Dictionary, + 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) } diff --git a/lib/bitcoin/bitcoin_wallet.dart b/lib/bitcoin/bitcoin_wallet.dart index 022ed478..79444844 100644 --- a/lib/bitcoin/bitcoin_wallet.dart +++ b/lib/bitcoin/bitcoin_wallet.dart @@ -3,12 +3,12 @@ import 'package:hive/hive.dart'; 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'; @@ -30,8 +30,14 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store { unspentCoinsInfo: unspentCoinsInfo, networkType: bitcoin.bitcoin, initialAddresses: initialAddresses, - initialBalance: initialBalance, - accountIndex: accountIndex); + initialBalance: initialBalance) { + walletAddresses = BitcoinWalletAddresses( + walletInfo, + initialAddresses: initialAddresses, + accountIndex: accountIndex, + hd: hd, + networkType: networkType); + } static Future open({ @required String name, @@ -50,8 +56,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 00000000..ecded3cb --- /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 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 43c5cd17..78e3f696 100644 --- a/lib/bitcoin/electrum_wallet.dart +++ b/lib/bitcoin/electrum_wallet.dart @@ -2,6 +2,7 @@ import 'dart:async'; import 'dart:convert'; import 'package:cake_wallet/bitcoin/unspent_coins_info.dart'; import 'package:hive/hive.dart'; +import 'package:cake_wallet/bitcoin/electrum_wallet_addresses.dart'; import 'package:mobx/mobx.dart'; import 'package:rxdart/subjects.dart'; import 'package:flutter/foundation.dart'; @@ -45,18 +46,14 @@ abstract class ElectrumWalletBase extends WalletBase.of( - (initialAddresses ?? []).toSet()), syncStatus = NotConnectedSyncStatus(), _password = password, - _accountIndex = accountIndex, _feeRates = [], _isTransactionUpdating = false, super(walletInfo) { @@ -79,8 +76,7 @@ abstract class ElectrumWalletBase extends WalletBase unspentCoinsInfo; @override - @observable - String address; + ElectrumWalletAddresses walletAddresses; @override @observable @@ -90,9 +86,7 @@ abstract class ElectrumWalletBase extends WalletBase addresses; - - List get scriptHashes => addresses + List get scriptHashes => walletAddresses.addresses .map((addr) => scriptHash(addr.address, networkType: networkType)) .toList(); @@ -110,74 +104,15 @@ abstract class ElectrumWalletBase extends WalletBase unspentCoins; List _feeRates; - int _accountIndex; Map> _scripthashesUpdateSubject; bool _isTransactionUpdating; Future init() async { - await generateAddresses(); - address = addresses[_accountIndex].address; + await walletAddresses.init(); await transactionHistory.init(); - } - - @action - Future nextAddress() async { - _accountIndex += 1; - - if (_accountIndex >= addresses.length) { - _accountIndex = 0; - } - - address = addresses[_accountIndex].address; - await save(); } - Future generateAddresses() async { - if (addresses.length < 33) { - final addressesCount = 33 - addresses.length; - await generateNewAddresses(addressesCount, - startIndex: addresses.length, hd: hd); - } - } - - Future 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> generateNewAddresses(int count, - {int startIndex = 0, bitcoin.HDWallet hd, bool isHidden = false}) async { - final list = []; - - 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 updateAddress(String address) async { - for (final addr in addresses) { - if (addr.address == address) { - await save(); - break; - } - } - } - @action @override Future startSync() async { @@ -266,8 +201,7 @@ abstract class ElectrumWalletBase extends WalletBase 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() }); @@ -413,13 +347,12 @@ abstract class ElectrumWalletBase extends WalletBase ''; - Future makePath() async => pathForWallet(name: walletInfo.name, type: walletInfo.type); Future 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))))); @@ -491,7 +424,7 @@ abstract class ElectrumWalletBase extends WalletBase initialAddresses, + int accountIndex = 0, + @required bitcoin.HDWallet hd}) + : super(walletInfo) { + this.hd = hd; + this.accountIndex = accountIndex; + addresses = ObservableList.of( + (initialAddresses ?? []).toSet()); + } + + @override + @observable + String address; + + bitcoin.HDWallet hd; + + ObservableList addresses; + + int accountIndex; + + @override + Future init() async { + await generateAddresses(); + address = addresses[accountIndex].address; + await updateAddressesInBox(); + } + + @action + Future nextAddress() async { + accountIndex += 1; + + if (accountIndex >= addresses.length) { + accountIndex = 0; + } + + address = addresses[accountIndex].address; + + await updateAddressesInBox(); + } + + Future generateAddresses() async { + if (addresses.length < 33) { + final addressesCount = 33 - addresses.length; + await generateNewAddresses(addressesCount, + startIndex: addresses.length, hd: hd); + } + } + + Future 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> generateNewAddresses(int count, + {int startIndex = 0, bitcoin.HDWallet hd, bool isHidden = false}) async { + final list = []; + + 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 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 updateAddressesInBox() async { + try { + addressesMap.clear(); + addressesMap[address] = ''; + + await saveAddressesInBox(); + } catch (e) { + print(e.toString()); + } + } +} \ No newline at end of file diff --git a/lib/bitcoin/litecoin_wallet.dart b/lib/bitcoin/litecoin_wallet.dart index a627d8fc..ac7e2434 100644 --- a/lib/bitcoin/litecoin_wallet.dart +++ b/lib/bitcoin/litecoin_wallet.dart @@ -1,7 +1,6 @@ -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/bitcoin/litecoin_wallet_addresses.dart'; import 'package:cake_wallet/entities/transaction_priority.dart'; import 'package:flutter/foundation.dart'; import 'package:hive/hive.dart'; @@ -12,7 +11,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'; @@ -34,8 +32,15 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { unspentCoinsInfo: unspentCoinsInfo, 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 open({ @required String name, @@ -55,27 +60,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 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 00000000..19ade756 --- /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 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 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 b908d7bc..dc450ad7 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 ced91834..edcb0546 100644 --- a/lib/core/wallet_base.dart +++ b/lib/core/wallet_base.dart @@ -1,12 +1,12 @@ 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'; 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'; @@ -31,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; @@ -45,6 +45,8 @@ abstract class WalletBase< Object get keys; + WalletAddresses get walletAddresses; + HistoryType transactionHistory; Future connectToNode({@required Node node}); diff --git a/lib/di.dart b/lib/di.dart index 82ac29b6..09a0fd83 100644 --- a/lib/di.dart +++ b/lib/di.dart @@ -370,7 +370,8 @@ Future setup( getIt.registerFactoryParam( (AccountListItem account, _) => MoneroAccountEditOrCreateViewModel( - (getIt.get().wallet as MoneroWallet).accountList, + (getIt.get().wallet as MoneroWallet).walletAddresses.accountList, + wallet: getIt.get().wallet, accountListItem: account)); getIt.registerFactoryParam parseAddressFromDomain( + String domain, String ticker) async { + try { + final formattedName = OpenaliasRecord.formatDomainName(domain); + final domainParts = formattedName.split('.'); + final name = domainParts.last; + + if (domainParts.length <= 1 || domainParts.first.isEmpty || name.isEmpty) { + return ParsedAddress(address: domain); + } + + if (name.contains(topLevelDomain)) { + final address = + await fetchUnstoppableDomainAddress(domain, ticker); + + if (address?.isEmpty ?? true) { + return ParsedAddress(address: domain); + } + + return ParsedAddress( + address: address, + name: domain, + parseFrom: ParseFrom.unstoppableDomains); + } + + final record = await OpenaliasRecord.fetchAddressAndName(formattedName); + + if (record == null || record.address.contains(formattedName)) { + return ParsedAddress(address: domain); + } + + return ParsedAddress( + address: record.address, + name: record.name, + parseFrom: ParseFrom.openAlias); + } catch (e) { + print(e.toString()); + } + + return ParsedAddress(address: domain); +} \ 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 00000000..91fec675 --- /dev/null +++ b/lib/entities/parsed_address.dart @@ -0,0 +1,12 @@ +enum ParseFrom {unstoppableDomains, openAlias, notParsed} + +class ParsedAddress { + 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/entities/unstoppable_domain_address.dart b/lib/entities/unstoppable_domain_address.dart new file mode 100644 index 00000000..792f9a34 --- /dev/null +++ b/lib/entities/unstoppable_domain_address.dart @@ -0,0 +1,22 @@ +import 'package:flutter/services.dart'; + +const channel = MethodChannel('com.cake_wallet/native_utils'); + +Future fetchUnstoppableDomainAddress(String domain, String ticker) async { + var address = ''; + + try { + address = await channel.invokeMethod( + 'getUnstoppableDomainAddress', + { + '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/entities/wallet_addresses.dart b/lib/entities/wallet_addresses.dart new file mode 100644 index 00000000..cd533191 --- /dev/null +++ b/lib/entities/wallet_addresses.dart @@ -0,0 +1,36 @@ +import 'package:cake_wallet/entities/wallet_info.dart'; + +abstract class WalletAddresses { + WalletAddresses(this.walletInfo) { + addressesMap = {}; + } + + final WalletInfo walletInfo; + + String get address; + + set address(String address); + + Map addressesMap; + + Future init(); + + Future updateAddressesInBox(); + + Future saveAddressesInBox() async { + try { + if (walletInfo == null) { + return; + } + + walletInfo.address = address; + walletInfo.addresses = addressesMap; + + if (walletInfo.isInBox) { + await walletInfo.save(); + } + } catch (e) { + print(e.toString()); + } + } +} \ No newline at end of file diff --git a/lib/entities/wallet_info.dart b/lib/entities/wallet_info.dart index e29f6052..9dfb2011 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 addresses; + DateTime get date => DateTime.fromMillisecondsSinceEpoch(timestamp); } diff --git a/lib/monero/monero_wallet.dart b/lib/monero/monero_wallet.dart index e902f108..36f2e4bc 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_utils.dart'; import 'package:flutter/foundation.dart'; import 'package:mobx/mobx.dart'; import 'package:cw_monero/transaction_history.dart' @@ -15,10 +17,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'; @@ -35,9 +34,7 @@ class MoneroWallet = MoneroWalletBase with _$MoneroWallet; abstract class MoneroWalletBase extends WalletBase with Store { MoneroWalletBase({WalletInfo walletInfo}) - : accountList = MoneroAccountList(), - subaddressList = MoneroSubaddressList(), - super(walletInfo) { + : super(walletInfo) { transactionHistory = MoneroTransactionHistory(); balance = MoneroBalance( fullBalance: monero_wallet.getFullBalance(accountIndex: 0), @@ -47,32 +44,25 @@ abstract class MoneroWalletBase extends WalletBase 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 - @observable - SyncStatus syncStatus; + MoneroWalletAddresses walletAddresses; @override @observable - String address; + SyncStatus syncStatus; @override @observable @@ -88,10 +78,6 @@ abstract class MoneroWalletBase extends WalletBase 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; + monero_wallet.getUnlockedBalance(accountIndex: walletAddresses.account.id)); _setListeners(); await updateTransactions(); @@ -129,24 +111,6 @@ abstract class MoneroWalletBase extends WalletBase connectToNode({@required Node node}) async { try { @@ -189,7 +153,7 @@ abstract class MoneroWalletBase extends WalletBase save() async { + await walletAddresses.updateAddressesInBox(); + final now = DateTime.now().millisecondsSinceEpoch; if (now - _lastSaveTimestamp < Duration(seconds: 10).inMilliseconds) { return; } + await backupWalletFiles(name); _lastSaveTimestamp = now; await monero_wallet.store(); } @@ -265,7 +232,7 @@ abstract class MoneroWalletBase extends WalletBase - 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 _afterSyncSave() async { try { @@ -414,13 +381,13 @@ abstract class MoneroWalletBase extends WalletBase init() async { + accountList.update(); + account = accountList.accounts.first; + updateSubaddressList(accountIndex: account.id ?? 0); + await updateAddressesInBox(); + } + + @override + Future updateAddressesInBox() async { + try { + final _subaddressList = MoneroSubaddressList(); + + addressesMap.clear(); + + accountList.accounts.forEach((account) { + _subaddressList.update(accountIndex: account.id); + _subaddressList.subaddresses.forEach((subaddress) { + addressesMap[subaddress.address] = subaddress.label; + }); + }); + + 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_service.dart b/lib/monero/monero_wallet_service.dart index 7795b870..e3ec51a1 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 walletInfoSource; - - static Future _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(); @@ -117,10 +109,10 @@ 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); + 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 00000000..b0e50908 --- /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 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 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 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 removeCache(String name) async { + final path = await pathForWallet(name: name, type: WalletType.monero); + final cacheFile = File(path); + + if (cacheFile.existsSync()) { + cacheFile.deleteSync(); + } +} + +Future restoreOrResetWalletFiles(String name) async { + final backupsExists = await backupWalletFilesExists(name); + + if (backupsExists) { + await restoreWalletFiles(name); + } + + removeCache(name); +} \ No newline at end of file diff --git a/lib/reactions/on_current_wallet_change.dart b/lib/reactions/on_current_wallet_change.dart index 428f2703..5c702b0a 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/contact/contact_list_page.dart b/lib/src/screens/contact/contact_list_page.dart index bf2b593e..fd126981 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: [ 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/src/screens/exchange/exchange_page.dart b/lib/src/screens/exchange/exchange_page.dart index 3eb23da3..727630f5 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/exchange/changenow/changenow_exchange_provider.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'; @@ -9,8 +11,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 +23,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 +41,9 @@ class ExchangePage extends BasePage { final checkBoxKey = GlobalKey(); final _formKey = GlobalKey(); final _depositAmountFocus = FocusNode(); + final _depositAddressFocus = FocusNode(); final _receiveAmountFocus = FocusNode(); + final _receiveAddressFocus = FocusNode(); var _isReactionsSet = false; @override @@ -171,6 +172,7 @@ class ExchangePage extends BasePage { .calculateDepositAllAmount() : null, amountFocusNode: _depositAmountFocus, + addressFocusNode: _depositAddressFocus, key: depositKey, title: S.of(context).you_will_send, initialCurrency: @@ -179,7 +181,7 @@ class ExchangePage extends BasePage { initialAddress: exchangeViewModel.depositCurrency == exchangeViewModel.wallet.currency - ? exchangeViewModel.wallet.address + ? exchangeViewModel.wallet.walletAddresses.address : exchangeViewModel.depositAddress, initialIsAmountEditable: true, initialIsAddressEditable: @@ -223,6 +225,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.toLowerCase(); + exchangeViewModel.depositAddress = + await applyOpenaliasOrUnstoppableDomains( + context, domain, ticker); + }, ), ), ), @@ -232,6 +243,7 @@ class ExchangePage extends BasePage { child: Observer( builder: (_) => ExchangeCard( amountFocusNode: _receiveAmountFocus, + addressFocusNode: _receiveAddressFocus, key: receiveKey, title: S.of(context).you_will_get, initialCurrency: @@ -240,7 +252,7 @@ class ExchangePage extends BasePage { initialAddress: exchangeViewModel .receiveCurrency == exchangeViewModel.wallet.currency - ? exchangeViewModel.wallet.address + ? exchangeViewModel.wallet.walletAddresses.address : exchangeViewModel.receiveAddress, initialIsAmountEditable: exchangeViewModel .isReceiveAmountEditable, @@ -268,6 +280,15 @@ class ExchangePage extends BasePage { AddressValidator( type: exchangeViewModel .receiveCurrency), + onPushPasteButton: (context) async { + final domain = + exchangeViewModel.receiveAddress; + final ticker = exchangeViewModel + .receiveCurrency.title.toLowerCase(); + exchangeViewModel.receiveAddress = + await applyOpenaliasOrUnstoppableDomains( + context, domain, ticker); + }, )), ) ], @@ -371,7 +392,7 @@ class ExchangePage extends BasePage { from: template.depositCurrency, to: template.receiveCurrency, onTap: () { - applyTemplate( + applyTemplate(context, exchangeViewModel, template); }, onRemove: () { @@ -472,8 +493,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 +512,16 @@ class ExchangePage extends BasePage { exchangeViewModel.receiveAddress = template.receiveAddress; exchangeViewModel.isReceiveAmountEntered = false; exchangeViewModel.isFixedRateMode = false; + + var domain = template.depositAddress; + var ticker = template.depositCurrency.toLowerCase(); + exchangeViewModel.depositAddress = + await applyOpenaliasOrUnstoppableDomains(context, domain, ticker); + + domain = template.receiveAddress; + ticker = template.receiveCurrency.toLowerCase(); + exchangeViewModel.receiveAddress = + await applyOpenaliasOrUnstoppableDomains(context, domain, ticker); } void _setReactions( @@ -646,7 +677,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); } @@ -656,6 +688,26 @@ class ExchangePage extends BasePage { } }); + _depositAddressFocus.addListener(() async { + if (!_depositAddressFocus.hasFocus && + depositAddressController.text.isNotEmpty) { + final domain = depositAddressController.text; + final ticker = exchangeViewModel.depositCurrency.title.toLowerCase(); + exchangeViewModel.depositAddress = + await applyOpenaliasOrUnstoppableDomains(context, domain, ticker); + } + }); + + _receiveAddressFocus.addListener(() async { + if (!_receiveAddressFocus.hasFocus && + receiveAddressController.text.isNotEmpty) { + final domain = receiveAddressController.text; + final ticker = exchangeViewModel.receiveCurrency.title.toLowerCase(); + exchangeViewModel.receiveAddress = + await applyOpenaliasOrUnstoppableDomains(context, domain, ticker); + } + }); + _receiveAmountFocus.addListener(() { if (_receiveAmountFocus.hasFocus && !exchangeViewModel.isFixedRateMode) { showPopUp( @@ -707,7 +759,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,11 +772,35 @@ 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; } } + + Future 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(parsedAddress.name)); + break; + case ParseFrom.openAlias: + showAddressAlert( + context, + S.of(context).openalias_alert_title, + S.of(context).openalias_alert_content(parsedAddress.name)); + break; + case ParseFrom.notParsed: + break; + } + + return parsedAddress.address; + } } diff --git a/lib/src/screens/exchange/exchange_template_page.dart b/lib/src/screens/exchange/exchange_template_page.dart index 7df55f32..fb90f596 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 @@ -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), ), ), ), @@ -168,7 +167,7 @@ class ExchangeTemplatePage extends BasePage { initialAddress: exchangeViewModel.receiveCurrency == exchangeViewModel.wallet.currency - ? exchangeViewModel.wallet.address + ? exchangeViewModel.wallet.walletAddresses.address : exchangeViewModel.receiveAddress, initialIsAmountEditable: exchangeViewModel.provider is @@ -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), )), ) ], @@ -383,7 +382,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 +405,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 +418,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/src/screens/exchange/widgets/exchange_card.dart b/lib/src/screens/exchange/widgets/exchange_card.dart index 7af57e28..f534ccca 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 currencies; @@ -49,8 +51,10 @@ class ExchangeCard extends StatefulWidget { final FormFieldValidator currencyValueValidator; final FormFieldValidator 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 { ? 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 { .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 af6312ca..bc20352d 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/monero_transaction_priority.dart'; +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'; @@ -40,7 +42,7 @@ class SendPage extends BasePage { _addressFocusNode = FocusNode() { _addressFocusNode.addListener(() { if (!_addressFocusNode.hasFocus && _addressController.text.isNotEmpty) { - getOpenaliasRecord(_addressFocusNode.context); + applyOpenaliasOrUnstoppableDomains(_addressFocusNode.context); } }); } @@ -173,6 +175,9 @@ class SendPage extends BasePage { .headline .decorationColor), validator: sendViewModel.addressValidator, + onPushPasteButton: (context) { + applyOpenaliasOrUnstoppableDomains(context); + }, ), Observer( builder: (_) => Padding( @@ -542,7 +547,7 @@ class SendPage extends BasePage { template.address; _cryptoAmountController.text = template.amount; - getOpenaliasRecord(context); + applyOpenaliasOrUnstoppableDomains(context); }, onRemove: () { showPopUp( @@ -757,26 +762,6 @@ class SendPage extends BasePage { _effectsInstalled = true; } - Future getOpenaliasRecord(BuildContext context) async { - final record = - await sendViewModel.decodeOpenaliasRecord(_addressController.text); - - if (record != null) { - _addressController.text = record.address; - - await showPopUp( - 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 _setTransactionPriority(BuildContext context) async { final items = priorityForWalletType(sendViewModel.walletType); final selectedItem = items.indexOf(sendViewModel.transactionPriority); @@ -794,4 +779,28 @@ class SendPage extends BasePage { ), context: context); } + + void applyOpenaliasOrUnstoppableDomains(BuildContext context) async { + final domain = _addressController.text; + final ticker = sendViewModel.currency.title.toLowerCase(); + 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(parsedAddress.name)); + break; + case ParseFrom.openAlias: + showAddressAlert( + context, + S.of(context).openalias_alert_title, + S.of(context).openalias_alert_content(parsedAddress.name)); + break; + case ParseFrom.notParsed: + break; + } + } } diff --git a/lib/src/screens/send/send_template_page.dart b/lib/src/screens/send/send_template_page.dart index c71b6080..3880bd2f 100644 --- a/lib/src/screens/send/send_template_page.dart +++ b/lib/src/screens/send/send_template_page.dart @@ -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/parse_address_from_domain_alert.dart b/lib/src/screens/send/widgets/parse_address_from_domain_alert.dart new file mode 100644 index 00000000..76009161 --- /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( + 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/widgets/address_text_field.dart b/lib/src/widgets/address_text_field.dart index 8e2c069f..130912a2 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/contact_list/contact_list_view_model.dart b/lib/view_model/contact_list/contact_list_view_model.dart index 17083efe..1a8bbc02 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,22 @@ class ContactListViewModel = ContactListViewModelBase abstract class ContactListViewModelBase with Store { ContactListViewModelBase(this.contactSource, this.walletInfoSource) : contacts = ObservableList(), - 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.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))); + }); + } + }); + _subscription = contactSource.bindToListWithTransform( contacts, (Contact contact) => ContactRecord(contactSource, contact), initialFire: true); diff --git a/lib/view_model/dashboard/dashboard_view_model.dart b/lib/view_model/dashboard/dashboard_view_model.dart index e35316d1..0777d3ec 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 f3f00cd7..01246542 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_edit_or_create_view_model.dart b/lib/view_model/monero_account_list/monero_account_edit_or_create_view_model.dart index d32f8ead..a86fa277 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 save() async { try { @@ -38,6 +42,7 @@ abstract class MoneroAccountEditOrCreateViewModelBase with Store { await _moneroAccountList.addAccount(label: label); } + await _wallet.save(); state = ExecutedSuccessfullyState(); } catch (e) { state = FailureState(e.toString()); 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 c6d6aa68..ca0eb67e 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 get accounts => _moneroWallet.accountList.accounts - .map((acc) => AccountListItem( + List 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/send/send_view_model.dart b/lib/view_model/send/send_view_model.dart index 599ca813..b8459d4a 100644 --- a/lib/view_model/send/send_view_model.dart +++ b/lib/view_model/send/send_view_model.dart @@ -9,7 +9,6 @@ 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'; @@ -267,13 +266,6 @@ abstract class SendViewModelBase with Store { void setTransactionPriority(TransactionPriority priority) => _settingsStore.priority[_wallet.type] = priority; - Future decodeOpenaliasRecord(String name) async { - final record = await OpenaliasRecord.fetchAddressAndName( - OpenaliasRecord.formatDomainName(name)); - - return record.name != name ? record : null; - } - @action void _updateFiatAmount() { try { 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 b018c542..3676b3bf 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 _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 b1544322..a4d6eff6 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(); 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 58ee2408..402d3be8 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(); diff --git a/res/values/strings_de.arb b/res/values/strings_de.arb index 3c03078f..8ad27e3b 100644 --- a/res/values/strings_de.arb +++ b/res/values/strings_de.arb @@ -481,12 +481,15 @@ "buy_bitcoin" : "Bitcoin kaufen", "buy_with" : "Kaufen mit", "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", "unspent_coins_title" : "Nicht ausgegebene Münzen", "unspent_coins_details_title" : "Details zu nicht ausgegebenen Münzen", "freeze" : "Einfrieren", - "coin_control" : "Münzkontrolle (optional)" + "coin_control" : "Münzkontrolle (optional)", + + "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 c8acc77e..22ac371f 100644 --- a/res/values/strings_en.arb +++ b/res/values/strings_en.arb @@ -488,5 +488,8 @@ "unspent_coins_title" : "Unspent coins", "unspent_coins_details_title" : "Unspent coins details", "freeze" : "Freeze", - "coin_control" : "Coin control (optional)" + "coin_control" : "Coin control (optional)", + + "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 09aeb8e6..d372db5a 100644 --- a/res/values/strings_es.arb +++ b/res/values/strings_es.arb @@ -472,7 +472,7 @@ "submit_request" : "presentar una solicitud", "buy_alert_content" : "Actualmente solo apoyamos la compra de Bitcoin y Litecoin. Para comprar Bitcoin o Litecoin, cree o cambie a su billetera Bitcoin o Litecoin.", - + "outdated_electrum_wallet_description" : "Las nuevas carteras de Bitcoin creadas en Cake ahora tienen una semilla de 24 palabras. Es obligatorio que cree una nueva billetera de Bitcoin y transfiera todos sus fondos a la nueva billetera de 24 palabras, y deje de usar billeteras con una semilla de 12 palabras. Haga esto de inmediato para asegurar sus fondos.", "understand" : "Entiendo", @@ -488,5 +488,8 @@ "unspent_coins_title" : "Monedas no gastadas", "unspent_coins_details_title" : "Detalles de monedas no gastadas", "freeze" : "Congelar", - "coin_control" : "Control de monedas (opcional)" + "coin_control" : "Control de monedas (opcional)", + + "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 cee89d65..c290dc73 100644 --- a/res/values/strings_hi.arb +++ b/res/values/strings_hi.arb @@ -488,5 +488,8 @@ "unspent_coins_title" : "खर्च न किए गए सिक्के", "unspent_coins_details_title" : "अव्ययित सिक्कों का विवरण", "freeze" : "फ्रीज", - "coin_control" : "सिक्का नियंत्रण (वैकल्पिक)" + "coin_control" : "सिक्का नियंत्रण (वैकल्पिक)", + + "address_detected" : "पता लग गया", + "address_from_domain" : "आपको अजेय डोमेन ${domain} से पता मिला है" } \ No newline at end of file diff --git a/res/values/strings_hr.arb b/res/values/strings_hr.arb index c6fb2c68..dec79f2c 100644 --- a/res/values/strings_hr.arb +++ b/res/values/strings_hr.arb @@ -488,5 +488,8 @@ "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)" + "coin_control" : "Kontrola novca (nije obavezno)", + + "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 9fd63ed9..92f3be68 100644 --- a/res/values/strings_it.arb +++ b/res/values/strings_it.arb @@ -488,5 +488,8 @@ "unspent_coins_title" : "Monete non spese", "unspent_coins_details_title" : "Dettagli sulle monete non spese", "freeze" : "Congelare", - "coin_control" : "Controllo monete (opzionale)" + "coin_control" : "Controllo monete (opzionale)", + + "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 36d0ec07..d82f5de2 100644 --- a/res/values/strings_ja.arb +++ b/res/values/strings_ja.arb @@ -488,5 +488,8 @@ "unspent_coins_title" : "未使用のコイン", "unspent_coins_details_title" : "未使用のコインの詳細", "freeze" : "氷結", - "coin_control" : "コインコントロール(オプション)" + "coin_control" : "コインコントロール(オプション)", + + "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 82d8f6c2..0ff685c1 100644 --- a/res/values/strings_ko.arb +++ b/res/values/strings_ko.arb @@ -488,5 +488,8 @@ "unspent_coins_title" : "사용하지 않은 동전", "unspent_coins_details_title" : "사용하지 않은 동전 세부 정보", "freeze" : "얼다", - "coin_control" : "코인 제어 (옵션)" + "coin_control" : "코인 제어 (옵션)", + + "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 caae9789..2fd48e8b 100644 --- a/res/values/strings_nl.arb +++ b/res/values/strings_nl.arb @@ -488,5 +488,8 @@ "unspent_coins_title" : "Ongebruikte munten", "unspent_coins_details_title" : "Details van niet-uitgegeven munten", "freeze" : "Bevriezen", - "coin_control" : "Muntcontrole (optioneel)" + "coin_control" : "Muntcontrole (optioneel)", + + "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 47c4a706..9f8cf235 100644 --- a/res/values/strings_pl.arb +++ b/res/values/strings_pl.arb @@ -488,5 +488,8 @@ "unspent_coins_title" : "Niewydane monety", "unspent_coins_details_title" : "Szczegóły niewydanych monet", "freeze" : "Zamrażać", - "coin_control" : "Kontrola monet (opcjonalnie)" + "coin_control" : "Kontrola monet (opcjonalnie)", + + "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 a49211e2..59693aa8 100644 --- a/res/values/strings_pt.arb +++ b/res/values/strings_pt.arb @@ -488,5 +488,8 @@ "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)" + "coin_control" : "Controle de moedas (opcional)", + + "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 39508cd7..5fd09752 100644 --- a/res/values/strings_ru.arb +++ b/res/values/strings_ru.arb @@ -488,5 +488,8 @@ "unspent_coins_title" : "Неизрасходованные монеты", "unspent_coins_details_title" : "Сведения о неизрасходованных монетах", "freeze" : "Заморозить", - "coin_control" : "Контроль монет (необязательно)" + "coin_control" : "Контроль монет (необязательно)", + + "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 5a2a29c2..2bbbfd8e 100644 --- a/res/values/strings_uk.arb +++ b/res/values/strings_uk.arb @@ -488,5 +488,8 @@ "unspent_coins_title" : "Невитрачені монети", "unspent_coins_details_title" : "Відомості про невитрачені монети", "freeze" : "Заморозити", - "coin_control" : "Контроль монет (необов’язково)" + "coin_control" : "Контроль монет (необов’язково)", + + "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 1538c55b..1e4ebc39 100644 --- a/res/values/strings_zh.arb +++ b/res/values/strings_zh.arb @@ -488,5 +488,8 @@ "unspent_coins_title" : "未使用的硬幣", "unspent_coins_details_title" : "未使用代幣詳情", "freeze" : "凍結", - "coin_control" : "硬幣控制(可選)" + "coin_control" : "硬幣控制(可選)", + + "address_detected" : "檢測到地址", + "address_from_domain" : "您有以下地址 unstoppable domain ${domain}" } \ No newline at end of file