Merge branch 'release-4.2.4' into CAKE-334-unspent-coins-control-application-logic

wownero
M 3 years ago
commit 3dd1bdabcc

@ -71,8 +71,9 @@ android {
release { release {
signingConfig signingConfigs.release signingConfig signingConfigs.release
minifyEnabled true shrinkResources false
useProguard true minifyEnabled false
useProguard false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
} }
@ -85,8 +86,9 @@ flutter {
dependencies { dependencies {
testImplementation 'junit:junit:4.12' testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test:runner:1.1.1' androidTestImplementation 'androidx.test:runner:1.3.0'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1' 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-core:19.0.0'
implementation 'com.google.firebase:firebase-messaging:19.0.0' implementation 'com.google.firebase:firebase-messaging:19.0.0'
} }

@ -10,14 +10,19 @@ import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel; import io.flutter.plugin.common.MethodChannel;
import android.os.AsyncTask; import android.os.AsyncTask;
import android.os.Build;
import android.os.Handler; import android.os.Handler;
import android.os.Looper; import android.os.Looper;
import com.unstoppabledomains.resolution.DomainResolution;
import com.unstoppabledomains.resolution.Resolution;
import java.security.SecureRandom; import java.security.SecureRandom;
public class MainActivity extends FlutterFragmentActivity { public class MainActivity extends FlutterFragmentActivity {
final String UTILS_CHANNEL = "com.cake_wallet/native_utils"; final String UTILS_CHANNEL = "com.cake_wallet/native_utils";
final int UNSTOPPABLE_DOMAIN_MIN_VERSION_SDK = 24;
@Override @Override
public void configureFlutterEngine(@NonNull FlutterEngine flutterEngine) { public void configureFlutterEngine(@NonNull FlutterEngine flutterEngine) {
GeneratedPluginRegistrant.registerWith(flutterEngine); GeneratedPluginRegistrant.registerWith(flutterEngine);
@ -33,17 +38,44 @@ public class MainActivity extends FlutterFragmentActivity {
Handler handler = new Handler(Looper.getMainLooper()); Handler handler = new Handler(Looper.getMainLooper());
try { try {
if (call.method.equals("sec_random")) { switch (call.method) {
int count = call.argument("count"); case "sec_random":
SecureRandom random = new SecureRandom(); int count = call.argument("count");
byte bytes[] = new byte[count]; SecureRandom random = new SecureRandom();
random.nextBytes(bytes); byte bytes[] = new byte[count];
handler.post(() -> result.success(bytes)); random.nextBytes(bytes);
} else { handler.post(() -> result.success(bytes));
handler.post(() -> result.notImplemented()); 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) { } catch (Exception e) {
handler.post(() -> result.error("UNCAUGHT_ERROR", e.getMessage(), null)); 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(""));
}
});
}
}

@ -5,7 +5,7 @@ buildscript {
} }
dependencies { 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' classpath 'com.google.gms:google-services:4.3.8'
} }
} }

@ -1,6 +1,6 @@
#Fri Jun 23 08:50:38 CEST 2017 #Mon Apr 19 18:19:26 EEST 2021
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists 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

@ -1,5 +1,5 @@
# Uncomment this line to define a global platform for your project # 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' source 'https://github.com/CocoaPods/Specs.git'
# CocoaPods analytics sends network stats synchronously affecting flutter build latency. # CocoaPods analytics sends network stats synchronously affecting flutter build latency.
@ -36,6 +36,7 @@ target 'Runner' do
# Cake Wallet (Legacy) # Cake Wallet (Legacy)
pod 'CryptoSwift' pod 'CryptoSwift'
pod 'UnstoppableDomainsResolution', '~> 0.3.6'
pod 'Firebase/Messaging', '6.33.0' pod 'Firebase/Messaging', '6.33.0'
end end

@ -3,6 +3,7 @@ PODS:
- Flutter - Flutter
- MTBBarcodeScanner - MTBBarcodeScanner
- SwiftProtobuf - SwiftProtobuf
- BigInt (5.2.0)
- connectivity (0.0.1): - connectivity (0.0.1):
- Flutter - Flutter
- Reachability - Reachability
@ -59,6 +60,8 @@ PODS:
- SwiftyGif - SwiftyGif
- esys_flutter_share (0.0.1): - esys_flutter_share (0.0.1):
- Flutter - Flutter
- EthereumAddress (1.3.0):
- CryptoSwift (~> 1.0)
- file_picker (0.0.1): - file_picker (0.0.1):
- DKImagePickerController/PhotoGallery - DKImagePickerController/PhotoGallery
- Flutter - Flutter
@ -150,6 +153,10 @@ PODS:
- Flutter - Flutter
- SwiftProtobuf (1.12.0) - SwiftProtobuf (1.12.0)
- SwiftyGif (5.3.0) - SwiftyGif (5.3.0)
- UnstoppableDomainsResolution (0.3.6):
- BigInt
- CryptoSwift (~> 1.0)
- EthereumAddress (~> 1.3)
- url_launcher (0.0.1): - url_launcher (0.0.1):
- Flutter - Flutter
- webview_flutter (0.0.1): - webview_flutter (0.0.1):
@ -174,14 +181,17 @@ DEPENDENCIES:
- permission_handler (from `.symlinks/plugins/permission_handler/ios`) - permission_handler (from `.symlinks/plugins/permission_handler/ios`)
- share (from `.symlinks/plugins/share/ios`) - share (from `.symlinks/plugins/share/ios`)
- shared_preferences (from `.symlinks/plugins/shared_preferences/ios`) - shared_preferences (from `.symlinks/plugins/shared_preferences/ios`)
- UnstoppableDomainsResolution (~> 0.3.6)
- url_launcher (from `.symlinks/plugins/url_launcher/ios`) - url_launcher (from `.symlinks/plugins/url_launcher/ios`)
- webview_flutter (from `.symlinks/plugins/webview_flutter/ios`) - webview_flutter (from `.symlinks/plugins/webview_flutter/ios`)
SPEC REPOS: SPEC REPOS:
https://github.com/CocoaPods/Specs.git: https://github.com/CocoaPods/Specs.git:
- BigInt
- CryptoSwift - CryptoSwift
- DKImagePickerController - DKImagePickerController
- DKPhotoGallery - DKPhotoGallery
- EthereumAddress
- Firebase - Firebase
- FirebaseCore - FirebaseCore
- FirebaseCoreDiagnostics - FirebaseCoreDiagnostics
@ -198,6 +208,7 @@ SPEC REPOS:
- SDWebImage - SDWebImage
- SwiftProtobuf - SwiftProtobuf
- SwiftyGif - SwiftyGif
- UnstoppableDomainsResolution
EXTERNAL SOURCES: EXTERNAL SOURCES:
barcode_scan: barcode_scan:
@ -239,6 +250,7 @@ EXTERNAL SOURCES:
SPEC CHECKSUMS: SPEC CHECKSUMS:
barcode_scan: a5c27959edfafaa0c771905bad0b29d6d39e4479 barcode_scan: a5c27959edfafaa0c771905bad0b29d6d39e4479
BigInt: f668a80089607f521586bbe29513d708491ef2f7
connectivity: c4130b2985d4ef6fd26f9702e886bd5260681467 connectivity: c4130b2985d4ef6fd26f9702e886bd5260681467
CryptoSwift: 093499be1a94b0cae36e6c26b70870668cb56060 CryptoSwift: 093499be1a94b0cae36e6c26b70870668cb56060
cw_monero: 78f369253cc913efc23db9cf6be81a11eaf40fe1 cw_monero: 78f369253cc913efc23db9cf6be81a11eaf40fe1
@ -246,6 +258,7 @@ SPEC CHECKSUMS:
DKImagePickerController: b5eb7f7a388e4643264105d648d01f727110fc3d DKImagePickerController: b5eb7f7a388e4643264105d648d01f727110fc3d
DKPhotoGallery: fdfad5125a9fdda9cc57df834d49df790dbb4179 DKPhotoGallery: fdfad5125a9fdda9cc57df834d49df790dbb4179
esys_flutter_share: 403498dab005b36ce1f8d7aff377e81f0621b0b4 esys_flutter_share: 403498dab005b36ce1f8d7aff377e81f0621b0b4
EthereumAddress: 39fe8e11cf04e4e9902b55ae653dbc4e0aee5f30
file_picker: 3e6c3790de664ccf9b882732d9db5eaf6b8d4eb1 file_picker: 3e6c3790de664ccf9b882732d9db5eaf6b8d4eb1
Firebase: 8db6f2d1b2c5e2984efba4949a145875a8f65fe5 Firebase: 8db6f2d1b2c5e2984efba4949a145875a8f65fe5
firebase_core: 5d6a02f3d85acd5f8321c2d6d62877626a670659 firebase_core: 5d6a02f3d85acd5f8321c2d6d62877626a670659
@ -273,9 +286,9 @@ SPEC CHECKSUMS:
shared_preferences: af6bfa751691cdc24be3045c43ec037377ada40d shared_preferences: af6bfa751691cdc24be3045c43ec037377ada40d
SwiftProtobuf: 4ef85479c18ca85b5482b343df9c319c62bda699 SwiftProtobuf: 4ef85479c18ca85b5482b343df9c319c62bda699
SwiftyGif: e466e86c660d343357ab944a819a101c4127cb40 SwiftyGif: e466e86c660d343357ab944a819a101c4127cb40
UnstoppableDomainsResolution: 63abb84858d3e91eb838a5bfa6f7e3c0e0593f24
url_launcher: 6fef411d543ceb26efce54b05a0a40bfd74cbbef url_launcher: 6fef411d543ceb26efce54b05a0a40bfd74cbbef
webview_flutter: 9f491a9b5a66f2573946a389b2677987b0ff8c0b webview_flutter: 9f491a9b5a66f2573946a389b2677987b0ff8c0b
PODFILE CHECKSUM: f316539722a6f9dbb0d0f1065a27fa7ea38c6f88
COCOAPODS: 1.9.3 COCOAPODS: 1.10.1

@ -1,8 +1,13 @@
import UIKit import UIKit
import Flutter import Flutter
import UnstoppableDomainsResolution
@UIApplicationMain @UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate { @objc class AppDelegate: FlutterAppDelegate {
lazy var resolution : Resolution? = {
return try? Resolution()
}()
override func application( override func application(
_ application: UIApplication, _ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
@ -70,6 +75,27 @@ import Flutter
} }
result(secRandom(count: count)) 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: default:
result(FlutterMethodNotImplemented) result(FlutterMethodNotImplemented)
} }

@ -3,12 +3,12 @@ import 'package:hive/hive.dart';
import 'package:mobx/mobx.dart'; import 'package:mobx/mobx.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:bitcoin_flutter/bitcoin_flutter.dart' as bitcoin; import 'package:bitcoin_flutter/bitcoin_flutter.dart' as bitcoin;
import 'package:cake_wallet/bitcoin/utils.dart';
import 'package:cake_wallet/bitcoin/electrum_wallet_snapshot.dart'; import 'package:cake_wallet/bitcoin/electrum_wallet_snapshot.dart';
import 'package:cake_wallet/bitcoin/electrum_wallet.dart'; import 'package:cake_wallet/bitcoin/electrum_wallet.dart';
import 'package:cake_wallet/entities/wallet_info.dart'; import 'package:cake_wallet/entities/wallet_info.dart';
import 'package:cake_wallet/bitcoin/bitcoin_address_record.dart'; import 'package:cake_wallet/bitcoin/bitcoin_address_record.dart';
import 'package:cake_wallet/bitcoin/electrum_balance.dart'; import 'package:cake_wallet/bitcoin/electrum_balance.dart';
import 'package:cake_wallet/bitcoin/bitcoin_wallet_addresses.dart';
part 'bitcoin_wallet.g.dart'; part 'bitcoin_wallet.g.dart';
@ -30,8 +30,14 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
unspentCoinsInfo: unspentCoinsInfo, unspentCoinsInfo: unspentCoinsInfo,
networkType: bitcoin.bitcoin, networkType: bitcoin.bitcoin,
initialAddresses: initialAddresses, initialAddresses: initialAddresses,
initialBalance: initialBalance, initialBalance: initialBalance) {
accountIndex: accountIndex); walletAddresses = BitcoinWalletAddresses(
walletInfo,
initialAddresses: initialAddresses,
accountIndex: accountIndex,
hd: hd,
networkType: networkType);
}
static Future<BitcoinWallet> open({ static Future<BitcoinWallet> open({
@required String name, @required String name,
@ -50,8 +56,4 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
initialBalance: snp.balance, initialBalance: snp.balance,
accountIndex: snp.accountIndex); accountIndex: snp.accountIndex);
} }
@override
String getAddress({@required int index, @required bitcoin.HDWallet hd}) =>
generateP2WPKHAddress(hd: hd, index: index, networkType: networkType);
} }

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

@ -2,6 +2,7 @@ import 'dart:async';
import 'dart:convert'; import 'dart:convert';
import 'package:cake_wallet/bitcoin/unspent_coins_info.dart'; import 'package:cake_wallet/bitcoin/unspent_coins_info.dart';
import 'package:hive/hive.dart'; import 'package:hive/hive.dart';
import 'package:cake_wallet/bitcoin/electrum_wallet_addresses.dart';
import 'package:mobx/mobx.dart'; import 'package:mobx/mobx.dart';
import 'package:rxdart/subjects.dart'; import 'package:rxdart/subjects.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
@ -45,18 +46,14 @@ abstract class ElectrumWalletBase extends WalletBase<ElectrumBalance,
@required this.networkType, @required this.networkType,
@required this.mnemonic, @required this.mnemonic,
ElectrumClient electrumClient, ElectrumClient electrumClient,
int accountIndex = 0,
ElectrumBalance initialBalance}) ElectrumBalance initialBalance})
: balance = initialBalance ?? : balance = initialBalance ??
const ElectrumBalance(confirmed: 0, unconfirmed: 0), const ElectrumBalance(confirmed: 0, unconfirmed: 0),
hd = bitcoin.HDWallet.fromSeed(mnemonicToSeedBytes(mnemonic), hd = bitcoin.HDWallet.fromSeed(mnemonicToSeedBytes(mnemonic),
network: networkType) network: networkType)
.derivePath("m/0'/0"), .derivePath("m/0'/0"),
addresses = ObservableList<BitcoinAddressRecord>.of(
(initialAddresses ?? []).toSet()),
syncStatus = NotConnectedSyncStatus(), syncStatus = NotConnectedSyncStatus(),
_password = password, _password = password,
_accountIndex = accountIndex,
_feeRates = <int>[], _feeRates = <int>[],
_isTransactionUpdating = false, _isTransactionUpdating = false,
super(walletInfo) { super(walletInfo) {
@ -79,8 +76,7 @@ abstract class ElectrumWalletBase extends WalletBase<ElectrumBalance,
Box<UnspentCoinsInfo> unspentCoinsInfo; Box<UnspentCoinsInfo> unspentCoinsInfo;
@override @override
@observable ElectrumWalletAddresses walletAddresses;
String address;
@override @override
@observable @observable
@ -90,9 +86,7 @@ abstract class ElectrumWalletBase extends WalletBase<ElectrumBalance,
@observable @observable
SyncStatus syncStatus; SyncStatus syncStatus;
ObservableList<BitcoinAddressRecord> addresses; List<String> get scriptHashes => walletAddresses.addresses
List<String> get scriptHashes => addresses
.map((addr) => scriptHash(addr.address, networkType: networkType)) .map((addr) => scriptHash(addr.address, networkType: networkType))
.toList(); .toList();
@ -110,74 +104,15 @@ abstract class ElectrumWalletBase extends WalletBase<ElectrumBalance,
final String _password; final String _password;
List<BitcoinUnspent> unspentCoins; List<BitcoinUnspent> unspentCoins;
List<int> _feeRates; List<int> _feeRates;
int _accountIndex;
Map<String, BehaviorSubject<Object>> _scripthashesUpdateSubject; Map<String, BehaviorSubject<Object>> _scripthashesUpdateSubject;
bool _isTransactionUpdating; bool _isTransactionUpdating;
Future<void> init() async { Future<void> init() async {
await generateAddresses(); await walletAddresses.init();
address = addresses[_accountIndex].address;
await transactionHistory.init(); await transactionHistory.init();
}
@action
Future<void> nextAddress() async {
_accountIndex += 1;
if (_accountIndex >= addresses.length) {
_accountIndex = 0;
}
address = addresses[_accountIndex].address;
await save(); 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 @action
@override @override
Future<void> startSync() async { Future<void> startSync() async {
@ -266,8 +201,7 @@ abstract class ElectrumWalletBase extends WalletBase<ElectrumBalance,
} }
final txb = bitcoin.TransactionBuilder(network: networkType); final txb = bitcoin.TransactionBuilder(network: networkType);
final changeAddress = address; final changeAddress = walletAddresses.address;
var leftAmount = totalAmount; var leftAmount = totalAmount;
var totalInputAmount = 0; var totalInputAmount = 0;
@ -340,8 +274,8 @@ abstract class ElectrumWalletBase extends WalletBase<ElectrumBalance,
String toJSON() => json.encode({ String toJSON() => json.encode({
'mnemonic': mnemonic, 'mnemonic': mnemonic,
'account_index': _accountIndex.toString(), 'account_index': walletAddresses.accountIndex.toString(),
'addresses': addresses.map((addr) => addr.toJSON()).toList(), 'addresses': walletAddresses.addresses.map((addr) => addr.toJSON()).toList(),
'balance': balance?.toJSON() 'balance': balance?.toJSON()
}); });
@ -413,13 +347,12 @@ abstract class ElectrumWalletBase extends WalletBase<ElectrumBalance,
} catch (_) {} } catch (_) {}
} }
String getAddress({@required int index, @required bitcoin.HDWallet hd}) => '';
Future<String> makePath() async => Future<String> makePath() async =>
pathForWallet(name: walletInfo.name, type: walletInfo.type); pathForWallet(name: walletInfo.name, type: walletInfo.type);
Future<void> updateUnspent() async { 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) .getListUnspentWithAddress(address.address, networkType)
.then((unspent) => unspent .then((unspent) => unspent
.map((unspent) => BitcoinUnspent.fromJSON(address, unspent))))); .map((unspent) => BitcoinUnspent.fromJSON(address, unspent)))));
@ -491,7 +424,7 @@ abstract class ElectrumWalletBase extends WalletBase<ElectrumBalance,
{@required String hash, @required int height}) async { {@required String hash, @required int height}) async {
final tx = await electrumClient.getTransactionExpanded(hash: hash); final tx = await electrumClient.getTransactionExpanded(hash: hash);
return ElectrumTransactionInfo.fromElectrumVerbose(tx, walletInfo.type, return ElectrumTransactionInfo.fromElectrumVerbose(tx, walletInfo.type,
height: height, addresses: addresses); height: height, addresses: walletAddresses.addresses);
} }
@override @override

@ -0,0 +1,111 @@
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_info.dart';
import 'package:flutter/foundation.dart';
import 'package:mobx/mobx.dart';
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
@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 {
addressesMap.clear();
addressesMap[address] = '';
await saveAddressesInBox();
} catch (e) {
print(e.toString());
}
}
}

@ -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/bitcoin_transaction_priority.dart';
import 'package:cake_wallet/bitcoin/unspent_coins_info.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:cake_wallet/entities/transaction_priority.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:hive/hive.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/bitcoin_address_record.dart';
import 'package:cake_wallet/bitcoin/electrum_balance.dart'; import 'package:cake_wallet/bitcoin/electrum_balance.dart';
import 'package:cake_wallet/bitcoin/litecoin_network.dart'; import 'package:cake_wallet/bitcoin/litecoin_network.dart';
import 'package:cake_wallet/bitcoin/utils.dart';
part 'litecoin_wallet.g.dart'; part 'litecoin_wallet.g.dart';
@ -34,8 +32,15 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
unspentCoinsInfo: unspentCoinsInfo, unspentCoinsInfo: unspentCoinsInfo,
networkType: litecoinNetwork, networkType: litecoinNetwork,
initialAddresses: initialAddresses, initialAddresses: initialAddresses,
initialBalance: initialBalance, initialBalance: initialBalance) {
accountIndex: accountIndex); walletAddresses = LitecoinWalletAddresses(
walletInfo,
initialAddresses: initialAddresses,
accountIndex: accountIndex,
hd: hd,
networkType: networkType,
mnemonic: mnemonic);
}
static Future<LitecoinWallet> open({ static Future<LitecoinWallet> open({
@required String name, @required String name,
@ -55,27 +60,6 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
accountIndex: snp.accountIndex); 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 @override
int feeRate(TransactionPriority priority) { int feeRate(TransactionPriority priority) {
if (priority is LitecoinTransactionPriority) { if (priority is LitecoinTransactionPriority) {

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

@ -15,7 +15,7 @@ abstract class BuyProvider {
String get trackUrl; String get trackUrl;
WalletType get walletType => wallet.type; WalletType get walletType => wallet.type;
String get walletAddress => wallet.address; String get walletAddress => wallet.walletAddresses.address;
String get walletId => wallet.id; String get walletId => wallet.id;
@override @override

@ -1,12 +1,12 @@
import 'package:cake_wallet/entities/balance.dart'; import 'package:cake_wallet/entities/balance.dart';
import 'package:cake_wallet/entities/transaction_info.dart'; import 'package:cake_wallet/entities/transaction_info.dart';
import 'package:cake_wallet/entities/transaction_priority.dart'; import 'package:cake_wallet/entities/transaction_priority.dart';
import 'package:cake_wallet/entities/wallet_addresses.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:cake_wallet/entities/wallet_info.dart'; import 'package:cake_wallet/entities/wallet_info.dart';
import 'package:cake_wallet/core/pending_transaction.dart'; import 'package:cake_wallet/core/pending_transaction.dart';
import 'package:cake_wallet/core/transaction_history.dart'; import 'package:cake_wallet/core/transaction_history.dart';
import 'package:cake_wallet/entities/currency_for_wallet_type.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/crypto_currency.dart';
import 'package:cake_wallet/entities/sync_status.dart'; import 'package:cake_wallet/entities/sync_status.dart';
import 'package:cake_wallet/entities/node.dart'; import 'package:cake_wallet/entities/node.dart';
@ -31,9 +31,9 @@ abstract class WalletBase<
String get name => walletInfo.name; String get name => walletInfo.name;
String get address; //String get address;
set address(String address); //set address(String address);
BalanceType get balance; BalanceType get balance;
@ -45,6 +45,8 @@ abstract class WalletBase<
Object get keys; Object get keys;
WalletAddresses get walletAddresses;
HistoryType transactionHistory; HistoryType transactionHistory;
Future<void> connectToNode({@required Node node}); Future<void> connectToNode({@required Node node});

@ -370,7 +370,8 @@ Future setup(
getIt.registerFactoryParam<MoneroAccountEditOrCreateViewModel, getIt.registerFactoryParam<MoneroAccountEditOrCreateViewModel,
AccountListItem, void>( AccountListItem, void>(
(AccountListItem account, _) => MoneroAccountEditOrCreateViewModel( (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)); accountListItem: account));
getIt.registerFactoryParam<MoneroAccountEditOrCreatePage, AccountListItem, getIt.registerFactoryParam<MoneroAccountEditOrCreatePage, AccountListItem,

@ -0,0 +1,47 @@
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';
const topLevelDomain = 'crypto';
Future<ParsedAddress> 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);
}

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

@ -0,0 +1,22 @@
import 'package:flutter/services.dart';
const channel = MethodChannel('com.cake_wallet/native_utils');
Future<String> fetchUnstoppableDomainAddress(String domain, String ticker) async {
var address = '';
try {
address = await channel.invokeMethod(
'getUnstoppableDomainAddress',
<String, String> {
'domain' : domain,
'ticker' : ticker
}
);
} catch (e) {
print('Unstoppable domain error: ${e.toString()}');
address = '';
}
return address;
}

@ -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<String, String> addressesMap;
Future<void> init();
Future<void> updateAddressesInBox();
Future<void> saveAddressesInBox() async {
try {
if (walletInfo == null) {
return;
}
walletInfo.address = address;
walletInfo.addresses = addressesMap;
if (walletInfo.isInBox) {
await walletInfo.save();
}
} catch (e) {
print(e.toString());
}
}
}

@ -53,5 +53,8 @@ class WalletInfo extends HiveObject {
@HiveField(8) @HiveField(8)
String address; String address;
@HiveField(10)
Map<String, String> addresses;
DateTime get date => DateTime.fromMillisecondsSinceEpoch(timestamp); DateTime get date => DateTime.fromMillisecondsSinceEpoch(timestamp);
} }

@ -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_amount_format.dart';
import 'package:cake_wallet/monero/monero_transaction_creation_exception.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_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:flutter/foundation.dart';
import 'package:mobx/mobx.dart'; import 'package:mobx/mobx.dart';
import 'package:cw_monero/transaction_history.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_wallet_keys.dart';
import 'package:cake_wallet/monero/monero_balance.dart'; import 'package:cake_wallet/monero/monero_balance.dart';
import 'package:cake_wallet/monero/monero_transaction_history.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/account.dart';
import 'package:cake_wallet/monero/subaddress.dart';
import 'package:cake_wallet/core/pending_transaction.dart'; import 'package:cake_wallet/core/pending_transaction.dart';
import 'package:cake_wallet/core/wallet_base.dart'; import 'package:cake_wallet/core/wallet_base.dart';
import 'package:cake_wallet/entities/sync_status.dart'; import 'package:cake_wallet/entities/sync_status.dart';
@ -35,9 +34,7 @@ class MoneroWallet = MoneroWalletBase with _$MoneroWallet;
abstract class MoneroWalletBase extends WalletBase<MoneroBalance, abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
MoneroTransactionHistory, MoneroTransactionInfo> with Store { MoneroTransactionHistory, MoneroTransactionInfo> with Store {
MoneroWalletBase({WalletInfo walletInfo}) MoneroWalletBase({WalletInfo walletInfo})
: accountList = MoneroAccountList(), : super(walletInfo) {
subaddressList = MoneroSubaddressList(),
super(walletInfo) {
transactionHistory = MoneroTransactionHistory(); transactionHistory = MoneroTransactionHistory();
balance = MoneroBalance( balance = MoneroBalance(
fullBalance: monero_wallet.getFullBalance(accountIndex: 0), fullBalance: monero_wallet.getFullBalance(accountIndex: 0),
@ -47,32 +44,25 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
_isSavingAfterSync = false; _isSavingAfterSync = false;
_isSavingAfterNewTransaction = false; _isSavingAfterNewTransaction = false;
_isTransactionUpdating = false; _isTransactionUpdating = false;
_onAccountChangeReaction = reaction((_) => account, (Account account) { walletAddresses = MoneroWalletAddresses(walletInfo);
_onAccountChangeReaction = reaction((_) => walletAddresses.account,
(Account account) {
balance = MoneroBalance( balance = MoneroBalance(
fullBalance: monero_wallet.getFullBalance(accountIndex: account.id), fullBalance: monero_wallet.getFullBalance(accountIndex: account.id),
unlockedBalance: unlockedBalance:
monero_wallet.getUnlockedBalance(accountIndex: account.id)); monero_wallet.getUnlockedBalance(accountIndex: account.id));
subaddressList.update(accountIndex: account.id); walletAddresses.updateSubaddressList(accountIndex: account.id);
subaddress = subaddressList.subaddresses.first;
address = subaddress.address;
}); });
} }
static const int _autoAfterSyncSaveInterval = 60000; static const int _autoAfterSyncSaveInterval = 60000;
@observable
Account account;
@observable
Subaddress subaddress;
@override @override
@observable MoneroWalletAddresses walletAddresses;
SyncStatus syncStatus;
@override @override
@observable @observable
String address; SyncStatus syncStatus;
@override @override
@observable @observable
@ -88,10 +78,6 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
publicSpendKey: monero_wallet.getPublicSpendKey(), publicSpendKey: monero_wallet.getPublicSpendKey(),
publicViewKey: monero_wallet.getPublicViewKey()); publicViewKey: monero_wallet.getPublicViewKey());
final MoneroSubaddressList subaddressList;
final MoneroAccountList accountList;
SyncListener _listener; SyncListener _listener;
ReactionDisposer _onAccountChangeReaction; ReactionDisposer _onAccountChangeReaction;
int _lastAutosaveTimestamp; int _lastAutosaveTimestamp;
@ -101,15 +87,11 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
int _lastSaveTimestamp; int _lastSaveTimestamp;
Future<void> init() async { Future<void> init() async {
accountList.update(); await walletAddresses.init();
account = accountList.accounts.first;
subaddressList.update(accountIndex: account.id ?? 0);
subaddress = subaddressList.getAll().first;
balance = MoneroBalance( balance = MoneroBalance(
fullBalance: monero_wallet.getFullBalance(accountIndex: account.id), fullBalance: monero_wallet.getFullBalance(accountIndex: walletAddresses.account.id),
unlockedBalance: unlockedBalance:
monero_wallet.getUnlockedBalance(accountIndex: account.id)); monero_wallet.getUnlockedBalance(accountIndex: walletAddresses.account.id));
address = subaddress.address;
_setListeners(); _setListeners();
await updateTransactions(); await updateTransactions();
@ -129,24 +111,6 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
_onAccountChangeReaction?.reaction?.dispose(); _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 @override
Future<void> connectToNode({@required Node node}) async { Future<void> connectToNode({@required Node node}) async {
try { try {
@ -189,7 +153,7 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
? moneroParseAmount(amount: _credentials.amount) ? moneroParseAmount(amount: _credentials.amount)
: null; : null;
final unlockedBalance = final unlockedBalance =
monero_wallet.getUnlockedBalance(accountIndex: account.id); monero_wallet.getUnlockedBalance(accountIndex: walletAddresses.account.id);
if ((amount != null && unlockedBalance < amount) || if ((amount != null && unlockedBalance < amount) ||
(amount == null && unlockedBalance <= 0)) { (amount == null && unlockedBalance <= 0)) {
@ -209,7 +173,7 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
paymentId: _credentials.paymentId, paymentId: _credentials.paymentId,
amount: _credentials.amount, amount: _credentials.amount,
priorityRaw: _credentials.priority.serialize(), priorityRaw: _credentials.priority.serialize(),
accountIndex: account.id); accountIndex: walletAddresses.account.id);
return PendingMoneroTransaction(pendingTransactionDescription); return PendingMoneroTransaction(pendingTransactionDescription);
} }
@ -238,12 +202,15 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
@override @override
Future<void> save() async { Future<void> save() async {
await walletAddresses.updateAddressesInBox();
final now = DateTime.now().millisecondsSinceEpoch; final now = DateTime.now().millisecondsSinceEpoch;
if (now - _lastSaveTimestamp < Duration(seconds: 10).inMilliseconds) { if (now - _lastSaveTimestamp < Duration(seconds: 10).inMilliseconds) {
return; return;
} }
await backupWalletFiles(name);
_lastSaveTimestamp = now; _lastSaveTimestamp = now;
await monero_wallet.store(); await monero_wallet.store();
} }
@ -265,7 +232,7 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
monero_wallet.rescanBlockchainAsync(); monero_wallet.rescanBlockchainAsync();
await startSync(); await startSync();
_askForUpdateBalance(); _askForUpdateBalance();
accountList.update(); walletAddresses.accountList.update();
await _askForUpdateTransactionHistory(); await _askForUpdateTransactionHistory();
await save(); await save();
await walletInfo.save(); await walletInfo.save();
@ -364,10 +331,10 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
await updateTransactions(); await updateTransactions();
int _getFullBalance() => int _getFullBalance() =>
monero_wallet.getFullBalance(accountIndex: account.id); monero_wallet.getFullBalance(accountIndex: walletAddresses.account.id);
int _getUnlockedBalance() => int _getUnlockedBalance() =>
monero_wallet.getUnlockedBalance(accountIndex: account.id); monero_wallet.getUnlockedBalance(accountIndex: walletAddresses.account.id);
Future<void> _afterSyncSave() async { Future<void> _afterSyncSave() async {
try { try {
@ -414,13 +381,13 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
if (walletInfo.isRecovery) { if (walletInfo.isRecovery) {
await _askForUpdateTransactionHistory(); await _askForUpdateTransactionHistory();
_askForUpdateBalance(); _askForUpdateBalance();
accountList.update(); walletAddresses.accountList.update();
} }
if (blocksLeft < 100) { if (blocksLeft < 100) {
await _askForUpdateTransactionHistory(); await _askForUpdateTransactionHistory();
_askForUpdateBalance(); _askForUpdateBalance();
accountList.update(); walletAddresses.accountList.update();
syncStatus = SyncedSyncStatus(); syncStatus = SyncedSyncStatus();
await _afterSyncSave(); await _afterSyncSave();

@ -0,0 +1,85 @@
import 'package:cake_wallet/entities/wallet_addresses.dart';
import 'package:cake_wallet/entities/wallet_info.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';
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
@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 {
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;
}
}

@ -1,5 +1,6 @@
import 'dart:io'; import 'dart:io';
import 'package:cake_wallet/core/wallet_base.dart'; import 'package:cake_wallet/core/wallet_base.dart';
import 'package:cake_wallet/monero/monero_wallet_utils.dart';
import 'package:hive/hive.dart'; import 'package:hive/hive.dart';
import 'package:cw_monero/wallet_manager.dart' as monero_wallet_manager; import 'package:cw_monero/wallet_manager.dart' as monero_wallet_manager;
import 'package:cw_monero/wallet.dart' as monero_wallet; import 'package:cw_monero/wallet.dart' as monero_wallet;
@ -55,16 +56,7 @@ class MoneroWalletService extends WalletService<
MoneroWalletService(this.walletInfoSource); MoneroWalletService(this.walletInfoSource);
final Box<WalletInfo> 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) => static bool walletFilesExist(String path) =>
!File(path).existsSync() && !File('$path.keys').existsSync(); !File(path).existsSync() && !File('$path.keys').existsSync();
@ -117,10 +109,10 @@ class MoneroWalletService extends WalletService<
(info) => info.id == WalletBase.idFor(name, getType()), (info) => info.id == WalletBase.idFor(name, getType()),
orElse: () => null); orElse: () => null);
final wallet = MoneroWallet(walletInfo: walletInfo); final wallet = MoneroWallet(walletInfo: walletInfo);
final isValid = wallet.validate(); final isValid = wallet.walletAddresses.validate();
if (!isValid) { if (!isValid) {
await _removeCache(name); await restoreOrResetWalletFiles(name);
wallet.close(); wallet.close();
return openWallet(name, password); return openWallet(name, password);
} }
@ -135,7 +127,7 @@ class MoneroWalletService extends WalletService<
(e is WalletOpeningException && (e is WalletOpeningException &&
(e.message == 'std::bad_alloc' || (e.message == 'std::bad_alloc' ||
e.message.contains('bad_alloc')))) { e.message.contains('bad_alloc')))) {
await _removeCache(name); await restoreOrResetWalletFiles(name);
return openWallet(name, password); return openWallet(name, password);
} }

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

@ -37,7 +37,7 @@ void startCurrentWalletChangeReaction(AppStore appStore,
await wallet.connectToNode(node: node); await wallet.connectToNode(node: node);
if (wallet.walletInfo.address?.isEmpty ?? true) { if (wallet.walletInfo.address?.isEmpty ?? true) {
wallet.walletInfo.address = wallet.address; wallet.walletInfo.address = wallet.walletAddresses.address;
if (wallet.walletInfo.isInBox) { if (wallet.walletInfo.isInBox) {
await wallet.walletInfo.save(); await wallet.walletInfo.save();

@ -151,17 +151,19 @@ class ContactListPage extends BasePage {
crossAxisAlignment: CrossAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[ children: <Widget>[
image ?? Offstage(), image ?? Offstage(),
Padding( Expanded(
padding: image != null child: Padding(
? EdgeInsets.only(left: 12) padding: image != null
: EdgeInsets.only(left: 0), ? EdgeInsets.only(left: 12)
child: Text( : EdgeInsets.only(left: 0),
contact.name, child: Text(
style: TextStyle( contact.name,
fontSize: 14, style: TextStyle(
fontWeight: FontWeight.normal, fontSize: 14,
color: Theme.of(context).primaryTextTheme.title.color), fontWeight: FontWeight.normal,
), color: Theme.of(context).primaryTextTheme.title.color),
),
)
) )
], ],
), ),

@ -1,7 +1,9 @@
import 'dart:ui'; import 'dart:ui';
import 'package:cake_wallet/entities/parsed_address.dart';
import 'package:cake_wallet/entities/sync_status.dart'; import 'package:cake_wallet/entities/sync_status.dart';
import 'package:cake_wallet/entities/wallet_type.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:cake_wallet/src/widgets/standard_checkbox.dart';
import 'package:dotted_border/dotted_border.dart'; import 'package:dotted_border/dotted_border.dart';
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
@ -9,8 +11,6 @@ import 'package:flutter/material.dart';
import 'package:flutter_mobx/flutter_mobx.dart'; import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:keyboard_actions/keyboard_actions.dart'; import 'package:keyboard_actions/keyboard_actions.dart';
import 'package:mobx/mobx.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_template.dart';
import 'package:cake_wallet/exchange/exchange_trade_state.dart'; import 'package:cake_wallet/exchange/exchange_trade_state.dart';
import 'package:cake_wallet/exchange/limits_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/routes.dart';
import 'package:cake_wallet/generated/i18n.dart'; import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/entities/crypto_currency.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/screens/exchange/widgets/exchange_card.dart';
import 'package:cake_wallet/src/widgets/primary_button.dart'; import 'package:cake_wallet/src/widgets/primary_button.dart';
import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart'; import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart';
@ -42,7 +41,9 @@ class ExchangePage extends BasePage {
final checkBoxKey = GlobalKey<StandardCheckboxState>(); final checkBoxKey = GlobalKey<StandardCheckboxState>();
final _formKey = GlobalKey<FormState>(); final _formKey = GlobalKey<FormState>();
final _depositAmountFocus = FocusNode(); final _depositAmountFocus = FocusNode();
final _depositAddressFocus = FocusNode();
final _receiveAmountFocus = FocusNode(); final _receiveAmountFocus = FocusNode();
final _receiveAddressFocus = FocusNode();
var _isReactionsSet = false; var _isReactionsSet = false;
@override @override
@ -171,6 +172,7 @@ class ExchangePage extends BasePage {
.calculateDepositAllAmount() .calculateDepositAllAmount()
: null, : null,
amountFocusNode: _depositAmountFocus, amountFocusNode: _depositAmountFocus,
addressFocusNode: _depositAddressFocus,
key: depositKey, key: depositKey,
title: S.of(context).you_will_send, title: S.of(context).you_will_send,
initialCurrency: initialCurrency:
@ -179,7 +181,7 @@ class ExchangePage extends BasePage {
initialAddress: initialAddress:
exchangeViewModel.depositCurrency == exchangeViewModel.depositCurrency ==
exchangeViewModel.wallet.currency exchangeViewModel.wallet.currency
? exchangeViewModel.wallet.address ? exchangeViewModel.wallet.walletAddresses.address
: exchangeViewModel.depositAddress, : exchangeViewModel.depositAddress,
initialIsAmountEditable: true, initialIsAmountEditable: true,
initialIsAddressEditable: initialIsAddressEditable:
@ -223,6 +225,15 @@ class ExchangePage extends BasePage {
type: exchangeViewModel.wallet.type), type: exchangeViewModel.wallet.type),
addressTextFieldValidator: AddressValidator( addressTextFieldValidator: AddressValidator(
type: exchangeViewModel.depositCurrency), 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( child: Observer(
builder: (_) => ExchangeCard( builder: (_) => ExchangeCard(
amountFocusNode: _receiveAmountFocus, amountFocusNode: _receiveAmountFocus,
addressFocusNode: _receiveAddressFocus,
key: receiveKey, key: receiveKey,
title: S.of(context).you_will_get, title: S.of(context).you_will_get,
initialCurrency: initialCurrency:
@ -240,7 +252,7 @@ class ExchangePage extends BasePage {
initialAddress: exchangeViewModel initialAddress: exchangeViewModel
.receiveCurrency == .receiveCurrency ==
exchangeViewModel.wallet.currency exchangeViewModel.wallet.currency
? exchangeViewModel.wallet.address ? exchangeViewModel.wallet.walletAddresses.address
: exchangeViewModel.receiveAddress, : exchangeViewModel.receiveAddress,
initialIsAmountEditable: exchangeViewModel initialIsAmountEditable: exchangeViewModel
.isReceiveAmountEditable, .isReceiveAmountEditable,
@ -268,6 +280,15 @@ class ExchangePage extends BasePage {
AddressValidator( AddressValidator(
type: exchangeViewModel type: exchangeViewModel
.receiveCurrency), .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, from: template.depositCurrency,
to: template.receiveCurrency, to: template.receiveCurrency,
onTap: () { onTap: () {
applyTemplate( applyTemplate(context,
exchangeViewModel, template); exchangeViewModel, template);
}, },
onRemove: () { onRemove: () {
@ -472,8 +493,8 @@ class ExchangePage extends BasePage {
)); ));
} }
void applyTemplate( void applyTemplate(BuildContext context,
ExchangeViewModel exchangeViewModel, ExchangeTemplate template) { ExchangeViewModel exchangeViewModel, ExchangeTemplate template) async {
exchangeViewModel.changeDepositCurrency( exchangeViewModel.changeDepositCurrency(
currency: CryptoCurrency.fromString(template.depositCurrency)); currency: CryptoCurrency.fromString(template.depositCurrency));
exchangeViewModel.changeReceiveCurrency( exchangeViewModel.changeReceiveCurrency(
@ -491,6 +512,16 @@ class ExchangePage extends BasePage {
exchangeViewModel.receiveAddress = template.receiveAddress; exchangeViewModel.receiveAddress = template.receiveAddress;
exchangeViewModel.isReceiveAmountEntered = false; exchangeViewModel.isReceiveAmountEntered = false;
exchangeViewModel.isFixedRateMode = 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( 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) { if (exchangeViewModel.depositCurrency == CryptoCurrency.xmr) {
depositKey.currentState.changeAddress(address: address); 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(() { _receiveAmountFocus.addListener(() {
if (_receiveAmountFocus.hasFocus && !exchangeViewModel.isFixedRateMode) { if (_receiveAmountFocus.hasFocus && !exchangeViewModel.isFixedRateMode) {
showPopUp<void>( showPopUp<void>(
@ -707,7 +759,8 @@ class ExchangePage extends BasePage {
isCurrentTypeWallet ? exchangeViewModel.wallet.name : null); isCurrentTypeWallet ? exchangeViewModel.wallet.name : null);
key.currentState.changeAddress( key.currentState.changeAddress(
address: isCurrentTypeWallet ? exchangeViewModel.wallet.address : ''); address: isCurrentTypeWallet
? exchangeViewModel.wallet.walletAddresses.address : '');
key.currentState.changeAmount(amount: ''); key.currentState.changeAmount(amount: '');
} }
@ -719,11 +772,35 @@ class ExchangePage extends BasePage {
if (isCurrentTypeWallet) { if (isCurrentTypeWallet) {
key.currentState.changeWalletName(exchangeViewModel.wallet.name); key.currentState.changeWalletName(exchangeViewModel.wallet.name);
key.currentState.addressController.text = key.currentState.addressController.text =
exchangeViewModel.wallet.address; exchangeViewModel.wallet.walletAddresses.address;
} else if (key.currentState.addressController.text == } else if (key.currentState.addressController.text ==
exchangeViewModel.wallet.address) { exchangeViewModel.wallet.walletAddresses.address) {
key.currentState.changeWalletName(null); key.currentState.changeWalletName(null);
key.currentState.addressController.text = null; 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(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;
}
} }

@ -129,7 +129,7 @@ class ExchangeTemplatePage extends BasePage {
initialAddress: exchangeViewModel initialAddress: exchangeViewModel
.depositCurrency == .depositCurrency ==
exchangeViewModel.wallet.currency exchangeViewModel.wallet.currency
? exchangeViewModel.wallet.address ? exchangeViewModel.wallet.walletAddresses.address
: exchangeViewModel.depositAddress, : exchangeViewModel.depositAddress,
initialIsAmountEditable: true, initialIsAmountEditable: true,
initialIsAddressEditable: exchangeViewModel initialIsAddressEditable: exchangeViewModel
@ -150,9 +150,8 @@ class ExchangeTemplatePage extends BasePage {
.color, .color,
currencyValueValidator: AmountValidator( currencyValueValidator: AmountValidator(
type: exchangeViewModel.wallet.type), type: exchangeViewModel.wallet.type),
addressTextFieldValidator: AddressValidator( //addressTextFieldValidator: AddressValidator(
type: // type: exchangeViewModel.depositCurrency),
exchangeViewModel.depositCurrency),
), ),
), ),
), ),
@ -168,7 +167,7 @@ class ExchangeTemplatePage extends BasePage {
initialAddress: initialAddress:
exchangeViewModel.receiveCurrency == exchangeViewModel.receiveCurrency ==
exchangeViewModel.wallet.currency exchangeViewModel.wallet.currency
? exchangeViewModel.wallet.address ? exchangeViewModel.wallet.walletAddresses.address
: exchangeViewModel.receiveAddress, : exchangeViewModel.receiveAddress,
initialIsAmountEditable: initialIsAmountEditable:
exchangeViewModel.provider is exchangeViewModel.provider is
@ -190,8 +189,8 @@ class ExchangeTemplatePage extends BasePage {
.decorationColor, .decorationColor,
currencyValueValidator: AmountValidator( currencyValueValidator: AmountValidator(
type: exchangeViewModel.wallet.type), type: exchangeViewModel.wallet.type),
addressTextFieldValidator: AddressValidator( //addressTextFieldValidator: AddressValidator(
type: exchangeViewModel.receiveCurrency), // 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) { if (exchangeViewModel.depositCurrency == CryptoCurrency.xmr) {
depositKey.currentState.changeAddress(address: address); depositKey.currentState.changeAddress(address: address);
} }
@ -405,7 +405,8 @@ class ExchangeTemplatePage extends BasePage {
isCurrentTypeWallet ? exchangeViewModel.wallet.name : null); isCurrentTypeWallet ? exchangeViewModel.wallet.name : null);
key.currentState.changeAddress( key.currentState.changeAddress(
address: isCurrentTypeWallet ? exchangeViewModel.wallet.address : ''); address: isCurrentTypeWallet
? exchangeViewModel.wallet.walletAddresses.address : '');
key.currentState.changeAmount(amount: ''); key.currentState.changeAmount(amount: '');
} }
@ -417,9 +418,9 @@ class ExchangeTemplatePage extends BasePage {
if (isCurrentTypeWallet) { if (isCurrentTypeWallet) {
key.currentState.changeWalletName(exchangeViewModel.wallet.name); key.currentState.changeWalletName(exchangeViewModel.wallet.name);
key.currentState.addressController.text = key.currentState.addressController.text =
exchangeViewModel.wallet.address; exchangeViewModel.wallet.walletAddresses.address;
} else if (key.currentState.addressController.text == } else if (key.currentState.addressController.text ==
exchangeViewModel.wallet.address) { exchangeViewModel.wallet.walletAddresses.address) {
key.currentState.changeWalletName(null); key.currentState.changeWalletName(null);
key.currentState.addressController.text = null; key.currentState.addressController.text = null;
} }

@ -28,8 +28,10 @@ class ExchangeCard extends StatefulWidget {
this.currencyValueValidator, this.currencyValueValidator,
this.addressTextFieldValidator, this.addressTextFieldValidator,
this.amountFocusNode, this.amountFocusNode,
this.addressFocusNode,
this.hasAllAmount = false, this.hasAllAmount = false,
this.allAmount}) this.allAmount,
this.onPushPasteButton})
: super(key: key); : super(key: key);
final List<CryptoCurrency> currencies; final List<CryptoCurrency> currencies;
@ -49,8 +51,10 @@ class ExchangeCard extends StatefulWidget {
final FormFieldValidator<String> currencyValueValidator; final FormFieldValidator<String> currencyValueValidator;
final FormFieldValidator<String> addressTextFieldValidator; final FormFieldValidator<String> addressTextFieldValidator;
final FocusNode amountFocusNode; final FocusNode amountFocusNode;
final FocusNode addressFocusNode;
final bool hasAllAmount; final bool hasAllAmount;
Function allAmount; final Function allAmount;
final Function(BuildContext context) onPushPasteButton;
@override @override
ExchangeCardState createState() => ExchangeCardState(); ExchangeCardState createState() => ExchangeCardState();
@ -288,6 +292,7 @@ class ExchangeCardState extends State<ExchangeCard> {
? Padding( ? Padding(
padding: EdgeInsets.only(top: 20), padding: EdgeInsets.only(top: 20),
child: AddressTextField( child: AddressTextField(
focusNode: widget.addressFocusNode,
controller: addressController, controller: addressController,
placeholder: widget.hasRefundAddress placeholder: widget.hasRefundAddress
? S.of(context).refund_address ? S.of(context).refund_address
@ -311,6 +316,7 @@ class ExchangeCardState extends State<ExchangeCard> {
.decorationColor), .decorationColor),
buttonColor: widget.addressButtonsColor, buttonColor: widget.addressButtonsColor,
validator: widget.addressTextFieldValidator, validator: widget.addressTextFieldValidator,
onPushPasteButton: widget.onPushPasteButton,
), ),
) )
: Padding( : Padding(

@ -1,6 +1,8 @@
import 'dart:ui'; 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/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/alert_with_two_actions.dart';
import 'package:cake_wallet/src/widgets/keyboard_done_button.dart'; import 'package:cake_wallet/src/widgets/keyboard_done_button.dart';
import 'package:cake_wallet/src/widgets/picker.dart'; import 'package:cake_wallet/src/widgets/picker.dart';
@ -40,7 +42,7 @@ class SendPage extends BasePage {
_addressFocusNode = FocusNode() { _addressFocusNode = FocusNode() {
_addressFocusNode.addListener(() { _addressFocusNode.addListener(() {
if (!_addressFocusNode.hasFocus && _addressController.text.isNotEmpty) { if (!_addressFocusNode.hasFocus && _addressController.text.isNotEmpty) {
getOpenaliasRecord(_addressFocusNode.context); applyOpenaliasOrUnstoppableDomains(_addressFocusNode.context);
} }
}); });
} }
@ -173,6 +175,9 @@ class SendPage extends BasePage {
.headline .headline
.decorationColor), .decorationColor),
validator: sendViewModel.addressValidator, validator: sendViewModel.addressValidator,
onPushPasteButton: (context) {
applyOpenaliasOrUnstoppableDomains(context);
},
), ),
Observer( Observer(
builder: (_) => Padding( builder: (_) => Padding(
@ -542,7 +547,7 @@ class SendPage extends BasePage {
template.address; template.address;
_cryptoAmountController.text = _cryptoAmountController.text =
template.amount; template.amount;
getOpenaliasRecord(context); applyOpenaliasOrUnstoppableDomains(context);
}, },
onRemove: () { onRemove: () {
showPopUp<void>( showPopUp<void>(
@ -757,26 +762,6 @@ class SendPage extends BasePage {
_effectsInstalled = true; _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 { Future<void> _setTransactionPriority(BuildContext context) async {
final items = priorityForWalletType(sendViewModel.walletType); final items = priorityForWalletType(sendViewModel.walletType);
final selectedItem = items.indexOf(sendViewModel.transactionPriority); final selectedItem = items.indexOf(sendViewModel.transactionPriority);
@ -794,4 +779,28 @@ class SendPage extends BasePage {
), ),
context: context); 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;
}
}
} }

@ -145,7 +145,7 @@ class SendTemplatePage extends BasePage {
.primaryTextTheme .primaryTextTheme
.headline .headline
.decorationColor), .decorationColor),
validator: sendViewModel.addressValidator, //validator: sendViewModel.addressValidator,
), ),
), ),
Observer(builder: (_) { Observer(builder: (_) {

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

@ -24,7 +24,8 @@ class AddressTextField extends StatelessWidget {
this.iconColor, this.iconColor,
this.textStyle, this.textStyle,
this.hintStyle, this.hintStyle,
this.validator}); this.validator,
this.onPushPasteButton});
static const prefixIconWidth = 34.0; static const prefixIconWidth = 34.0;
static const prefixIconHeight = 34.0; static const prefixIconHeight = 34.0;
@ -43,6 +44,7 @@ class AddressTextField extends StatelessWidget {
final TextStyle textStyle; final TextStyle textStyle;
final TextStyle hintStyle; final TextStyle hintStyle;
final FocusNode focusNode; final FocusNode focusNode;
final Function(BuildContext context) onPushPasteButton;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -225,5 +227,7 @@ class AddressTextField extends StatelessWidget {
if (address?.isNotEmpty ?? false) { if (address?.isNotEmpty ?? false) {
controller.text = address; controller.text = address;
} }
onPushPasteButton?.call(context);
} }
} }

@ -16,11 +16,22 @@ class ContactListViewModel = ContactListViewModelBase
abstract class ContactListViewModelBase with Store { abstract class ContactListViewModelBase with Store {
ContactListViewModelBase(this.contactSource, this.walletInfoSource) ContactListViewModelBase(this.contactSource, this.walletInfoSource)
: contacts = ObservableList<ContactRecord>(), : contacts = ObservableList<ContactRecord>(),
walletContacts = walletInfoSource.values walletContacts = [] {
.where((info) => info.address?.isNotEmpty ?? false) walletInfoSource.values.forEach((info) {
.map((info) => WalletContact( if (info.addresses?.isNotEmpty ?? false) {
info.address, info.name, walletTypeToCryptoCurrency(info.type))) info.addresses?.forEach((address, label) {
.toList() { final name = label.isNotEmpty
? info.name + ' ($label)'
: info.name;
walletContacts.add(WalletContact(
address,
name,
walletTypeToCryptoCurrency(info.type)));
});
}
});
_subscription = contactSource.bindToListWithTransform( _subscription = contactSource.bindToListWithTransform(
contacts, (Contact contact) => ContactRecord(contactSource, contact), contacts, (Contact contact) => ContactRecord(contactSource, contact),
initialFire: true); initialFire: true);

@ -92,17 +92,17 @@ abstract class DashboardViewModelBase with Store {
final _wallet = wallet; final _wallet = wallet;
if (_wallet is MoneroWallet) { if (_wallet is MoneroWallet) {
subname = _wallet.account?.label; subname = _wallet.walletAddresses.account?.label;
_onMoneroAccountChangeReaction = reaction((_) => _wallet.account, _onMoneroAccountChangeReaction = reaction((_) => _wallet.walletAddresses
(Account account) => _onMoneroAccountChange(_wallet)); .account, (Account account) => _onMoneroAccountChange(_wallet));
_onMoneroBalanceChangeReaction = reaction((_) => _wallet.balance, _onMoneroBalanceChangeReaction = reaction((_) => _wallet.balance,
(MoneroBalance balance) => _onMoneroTransactionsUpdate(_wallet)); (MoneroBalance balance) => _onMoneroTransactionsUpdate(_wallet));
final _accountTransactions = _wallet final _accountTransactions = _wallet
.transactionHistory.transactions.values .transactionHistory.transactions.values
.where((tx) => tx.accountIndex == _wallet.account.id) .where((tx) => tx.accountIndex == _wallet.walletAddresses.account.id)
.toList(); .toList();
transactions = ObservableList.of(_accountTransactions.map((transaction) => transactions = ObservableList.of(_accountTransactions.map((transaction) =>
@ -131,7 +131,7 @@ abstract class DashboardViewModelBase with Store {
filter: (TransactionInfo tx) { filter: (TransactionInfo tx) {
final wallet = _wallet; final wallet = _wallet;
if (tx is MoneroTransactionInfo && wallet is MoneroWallet) { if (tx is MoneroTransactionInfo && wallet is MoneroWallet) {
return tx.accountIndex == wallet.account.id; return tx.accountIndex == wallet.walletAddresses.account.id;
} }
return true; return true;
@ -153,7 +153,7 @@ abstract class DashboardViewModelBase with Store {
String subname; String subname;
@computed @computed
String get address => wallet.address; String get address => wallet.walletAddresses.address;
@computed @computed
SyncStatus get status => wallet.syncStatus; SyncStatus get status => wallet.syncStatus;
@ -251,13 +251,13 @@ abstract class DashboardViewModelBase with Store {
wallet.type == WalletType.bitcoin && wallet.seed.split(' ').length < 24; wallet.type == WalletType.bitcoin && wallet.seed.split(' ').length < 24;
if (wallet is MoneroWallet) { if (wallet is MoneroWallet) {
subname = wallet.account?.label; subname = wallet.walletAddresses.account?.label;
_onMoneroAccountChangeReaction?.reaction?.dispose(); _onMoneroAccountChangeReaction?.reaction?.dispose();
_onMoneroBalanceChangeReaction?.reaction?.dispose(); _onMoneroBalanceChangeReaction?.reaction?.dispose();
_onMoneroAccountChangeReaction = reaction((_) => wallet.account, _onMoneroAccountChangeReaction = reaction((_) => wallet.walletAddresses
(Account account) => _onMoneroAccountChange(wallet)); .account, (Account account) => _onMoneroAccountChange(wallet));
_onMoneroBalanceChangeReaction = reaction((_) => wallet.balance, _onMoneroBalanceChangeReaction = reaction((_) => wallet.balance,
(MoneroBalance balance) => _onMoneroTransactionsUpdate(wallet)); (MoneroBalance balance) => _onMoneroTransactionsUpdate(wallet));
@ -284,7 +284,7 @@ abstract class DashboardViewModelBase with Store {
settingsStore: appStore.settingsStore), settingsStore: appStore.settingsStore),
filter: (TransactionInfo tx) { filter: (TransactionInfo tx) {
if (tx is MoneroTransactionInfo && wallet is MoneroWallet) { if (tx is MoneroTransactionInfo && wallet is MoneroWallet) {
return tx.accountIndex == wallet.account.id; return tx.accountIndex == wallet.walletAddresses.account.id;
} }
return true; return true;
@ -293,7 +293,7 @@ abstract class DashboardViewModelBase with Store {
@action @action
void _onMoneroAccountChange(MoneroWallet wallet) { void _onMoneroAccountChange(MoneroWallet wallet) {
subname = wallet.account?.label; subname = wallet.walletAddresses.account?.label;
_onMoneroTransactionsUpdate(wallet); _onMoneroTransactionsUpdate(wallet);
} }
@ -302,7 +302,7 @@ abstract class DashboardViewModelBase with Store {
transactions.clear(); transactions.clear();
final _accountTransactions = wallet.transactionHistory.transactions.values final _accountTransactions = wallet.transactionHistory.transactions.values
.where((tx) => tx.accountIndex == wallet.account.id) .where((tx) => tx.accountIndex == wallet.walletAddresses.account.id)
.toList(); .toList();
transactions.addAll(_accountTransactions.map((transaction) => transactions.addAll(_accountTransactions.map((transaction) =>

@ -41,7 +41,8 @@ abstract class ExchangeViewModelBase with Store {
depositAmount = ''; depositAmount = '';
receiveAmount = ''; receiveAmount = '';
receiveAddress = ''; receiveAddress = '';
depositAddress = depositCurrency == wallet.currency ? wallet.address : ''; depositAddress = depositCurrency == wallet.currency
? wallet.walletAddresses.address : '';
limitsState = LimitsInitialState(); limitsState = LimitsInitialState();
tradeState = ExchangeTradeStateInitial(); tradeState = ExchangeTradeStateInitial();
_cryptoNumberFormat = NumberFormat()..maximumFractionDigits = 12; _cryptoNumberFormat = NumberFormat()..maximumFractionDigits = 12;
@ -308,8 +309,10 @@ abstract class ExchangeViewModelBase with Store {
isReceiveAmountEntered = false; isReceiveAmountEntered = false;
depositAmount = ''; depositAmount = '';
receiveAmount = ''; receiveAmount = '';
depositAddress = depositCurrency == wallet.currency ? wallet.address : ''; depositAddress = depositCurrency == wallet.currency
receiveAddress = receiveCurrency == wallet.currency ? wallet.address : ''; ? wallet.walletAddresses.address : '';
receiveAddress = receiveCurrency == wallet.currency
? wallet.walletAddresses.address : '';
isDepositAddressEnabled = !(depositCurrency == wallet.currency); isDepositAddressEnabled = !(depositCurrency == wallet.currency);
isReceiveAddressEnabled = !(receiveCurrency == wallet.currency); isReceiveAddressEnabled = !(receiveCurrency == wallet.currency);
isFixedRateMode = false; isFixedRateMode = false;

@ -1,3 +1,5 @@
import 'package:cake_wallet/core/wallet_base.dart';
import 'package:flutter/foundation.dart';
import 'package:mobx/mobx.dart'; import 'package:mobx/mobx.dart';
import 'package:cake_wallet/core/execution_state.dart'; import 'package:cake_wallet/core/execution_state.dart';
import 'package:cake_wallet/monero/monero_account_list.dart'; import 'package:cake_wallet/monero/monero_account_list.dart';
@ -10,11 +12,12 @@ class MoneroAccountEditOrCreateViewModel = MoneroAccountEditOrCreateViewModelBas
abstract class MoneroAccountEditOrCreateViewModelBase with Store { abstract class MoneroAccountEditOrCreateViewModelBase with Store {
MoneroAccountEditOrCreateViewModelBase(this._moneroAccountList, MoneroAccountEditOrCreateViewModelBase(this._moneroAccountList,
{AccountListItem accountListItem}) {@required WalletBase wallet, AccountListItem accountListItem})
: state = InitialExecutionState(), : state = InitialExecutionState(),
isEdit = accountListItem != null, isEdit = accountListItem != null,
label = accountListItem?.label??'', label = accountListItem?.label??'',
_accountListItem = accountListItem; _accountListItem = accountListItem,
_wallet = wallet;
final bool isEdit; final bool isEdit;
@ -26,6 +29,7 @@ abstract class MoneroAccountEditOrCreateViewModelBase with Store {
final MoneroAccountList _moneroAccountList; final MoneroAccountList _moneroAccountList;
final AccountListItem _accountListItem; final AccountListItem _accountListItem;
final WalletBase _wallet;
Future<void> save() async { Future<void> save() async {
try { try {
@ -38,6 +42,7 @@ abstract class MoneroAccountEditOrCreateViewModelBase with Store {
await _moneroAccountList.addAccount(label: label); await _moneroAccountList.addAccount(label: label);
} }
await _wallet.save();
state = ExecutedSuccessfullyState(); state = ExecutedSuccessfullyState();
} catch (e) { } catch (e) {
state = FailureState(e.toString()); state = FailureState(e.toString());

@ -20,15 +20,16 @@ abstract class MoneroAccountListViewModelBase with Store {
} }
@computed @computed
List<AccountListItem> get accounts => _moneroWallet.accountList.accounts List<AccountListItem> get accounts => _moneroWallet.walletAddresses
.map((acc) => AccountListItem( .accountList.accounts.map((acc) => AccountListItem(
label: acc.label, label: acc.label,
id: acc.id, id: acc.id,
isSelected: acc.id == _moneroWallet.account.id)) isSelected: acc.id == _moneroWallet.walletAddresses.account.id))
.toList(); .toList();
final MoneroWallet _moneroWallet; final MoneroWallet _moneroWallet;
void select(AccountListItem item) => void select(AccountListItem item) =>
_moneroWallet.account = Account(id: item.id, label: item.label); _moneroWallet.walletAddresses.account =
Account(id: item.id, label: item.label);
} }

@ -9,7 +9,6 @@ import 'package:cake_wallet/view_model/settings/settings_view_model.dart';
import 'package:hive/hive.dart'; import 'package:hive/hive.dart';
import 'package:intl/intl.dart'; import 'package:intl/intl.dart';
import 'package:mobx/mobx.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/entities/template.dart';
import 'package:cake_wallet/store/templates/send_template_store.dart'; import 'package:cake_wallet/store/templates/send_template_store.dart';
import 'package:cake_wallet/core/template_validator.dart'; import 'package:cake_wallet/core/template_validator.dart';
@ -267,13 +266,6 @@ abstract class SendViewModelBase with Store {
void setTransactionPriority(TransactionPriority priority) => void setTransactionPriority(TransactionPriority priority) =>
_settingsStore.priority[_wallet.type] = 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;
}
@action @action
void _updateFiatAmount() { void _updateFiatAmount() {
try { try {

@ -64,12 +64,15 @@ abstract class WalletAddressEditOrCreateViewModelBase with Store {
final wallet = _wallet; final wallet = _wallet;
if (wallet is ElectrumWallet) { if (wallet is ElectrumWallet) {
await wallet.generateNewAddress(); await wallet.walletAddresses.generateNewAddress();
await wallet.save();
} }
if (wallet is MoneroWallet) { if (wallet is MoneroWallet) {
await wallet.subaddressList await wallet.walletAddresses.subaddressList
.addSubaddress(accountIndex: wallet.account.id, label: label); .addSubaddress(
accountIndex: wallet.walletAddresses.account.id,
label: label);
await wallet.save(); await wallet.save();
} }
} }
@ -77,13 +80,14 @@ abstract class WalletAddressEditOrCreateViewModelBase with Store {
Future<void> _update() async { Future<void> _update() async {
final wallet = _wallet; final wallet = _wallet;
if (wallet is BitcoinWallet) { /*if (wallet is BitcoinWallet) {
await wallet.updateAddress(_item.address as String); await wallet.walletAddresses.updateAddress(_item.address as String);
} await wallet.save();
}*/
if (wallet is MoneroWallet) { if (wallet is MoneroWallet) {
await wallet.subaddressList.setLabelSubaddress( await wallet.walletAddresses.subaddressList.setLabelSubaddress(
accountIndex: wallet.account.id, accountIndex: wallet.walletAddresses.account.id,
addressIndex: _item.id as int, addressIndex: _item.id as int,
label: label); label: label);
await wallet.save(); await wallet.save();

@ -80,7 +80,7 @@ abstract class WalletAddressListViewModelBase with Store {
@computed @computed
WalletAddressListItem get address => WalletAddressListItem get address =>
WalletAddressListItem(address: _wallet.address); WalletAddressListItem(address: _wallet.walletAddresses.address);
@computed @computed
PaymentURI get uri { PaymentURI get uri {
@ -105,8 +105,10 @@ abstract class WalletAddressListViewModelBase with Store {
final addressList = ObservableList<ListItem>(); final addressList = ObservableList<ListItem>();
if (wallet is MoneroWallet) { if (wallet is MoneroWallet) {
final primaryAddress = wallet.subaddressList.subaddresses.first; final primaryAddress =
addressList.addAll(wallet.subaddressList.subaddresses.map((subaddress) { wallet.walletAddresses.subaddressList.subaddresses.first;
addressList.addAll(wallet.walletAddresses.subaddressList.subaddresses
.map((subaddress) {
final isPrimary = subaddress == primaryAddress; final isPrimary = subaddress == primaryAddress;
return WalletAddressListItem( return WalletAddressListItem(
@ -118,8 +120,8 @@ abstract class WalletAddressListViewModelBase with Store {
} }
if (wallet is BitcoinWallet) { if (wallet is BitcoinWallet) {
final primaryAddress = wallet.addresses.first; final primaryAddress = wallet.walletAddresses.addresses.first;
final bitcoinAddresses = wallet.addresses.map((addr) { final bitcoinAddresses = wallet.walletAddresses.addresses.map((addr) {
final isPrimary = addr == primaryAddress; final isPrimary = addr == primaryAddress;
return WalletAddressListItem( return WalletAddressListItem(
@ -139,7 +141,7 @@ abstract class WalletAddressListViewModelBase with Store {
final wallet = _wallet; final wallet = _wallet;
if (wallet is MoneroWallet) { if (wallet is MoneroWallet) {
return wallet.account.label; return wallet.walletAddresses.account.label;
} }
return null; return null;
@ -160,7 +162,7 @@ abstract class WalletAddressListViewModelBase with Store {
@action @action
void setAddress(WalletAddressListItem address) => void setAddress(WalletAddressListItem address) =>
_wallet.address = address.address; _wallet.walletAddresses.address = address.address;
void _init() { void _init() {
_baseItems = []; _baseItems = [];
@ -177,7 +179,8 @@ abstract class WalletAddressListViewModelBase with Store {
final wallet = _wallet; final wallet = _wallet;
if (wallet is ElectrumWallet) { if (wallet is ElectrumWallet) {
wallet.nextAddress(); wallet.walletAddresses.nextAddress();
wallet.save();
} }
} }
} }

@ -50,7 +50,7 @@ abstract class WalletCreationVMBase with Store {
dirPath: dirPath); dirPath: dirPath);
credentials.walletInfo = walletInfo; credentials.walletInfo = walletInfo;
final wallet = await process(credentials); final wallet = await process(credentials);
walletInfo.address = wallet.address; walletInfo.address = wallet.walletAddresses.address;
await _walletInfoSource.add(walletInfo); await _walletInfoSource.add(walletInfo);
_appStore.changeCurrentWallet(wallet); _appStore.changeCurrentWallet(wallet);
_appStore.authenticationStore.allowed(); _appStore.authenticationStore.allowed();

@ -481,12 +481,15 @@
"buy_bitcoin" : "Bitcoin kaufen", "buy_bitcoin" : "Bitcoin kaufen",
"buy_with" : "Kaufen mit", "buy_with" : "Kaufen mit",
"moonpay_alert_text" : "Der Wert des Betrags muss größer oder gleich ${minAmount} ${fiatCurrency} sein", "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", "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_title" : "Nicht ausgegebene Münzen",
"unspent_coins_details_title" : "Details zu nicht ausgegebenen Münzen", "unspent_coins_details_title" : "Details zu nicht ausgegebenen Münzen",
"freeze" : "Einfrieren", "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"
} }

@ -488,5 +488,8 @@
"unspent_coins_title" : "Unspent coins", "unspent_coins_title" : "Unspent coins",
"unspent_coins_details_title" : "Unspent coins details", "unspent_coins_details_title" : "Unspent coins details",
"freeze" : "Freeze", "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}"
} }

@ -472,7 +472,7 @@
"submit_request" : "presentar una solicitud", "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.", "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.", "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", "understand" : "Entiendo",
@ -488,5 +488,8 @@
"unspent_coins_title" : "Monedas no gastadas", "unspent_coins_title" : "Monedas no gastadas",
"unspent_coins_details_title" : "Detalles de monedas no gastadas", "unspent_coins_details_title" : "Detalles de monedas no gastadas",
"freeze" : "Congelar", "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}"
} }

@ -488,5 +488,8 @@
"unspent_coins_title" : "खर्च न किए गए सिक्के", "unspent_coins_title" : "खर्च न किए गए सिक्के",
"unspent_coins_details_title" : "अव्ययित सिक्कों का विवरण", "unspent_coins_details_title" : "अव्ययित सिक्कों का विवरण",
"freeze" : "फ्रीज", "freeze" : "फ्रीज",
"coin_control" : "सिक्का नियंत्रण (वैकल्पिक)" "coin_control" : "सिक्का नियंत्रण (वैकल्पिक)",
"address_detected" : "पता लग गया",
"address_from_domain" : "आपको अजेय डोमेन ${domain} से पता मिला है"
} }

@ -488,5 +488,8 @@
"unspent_coins_title" : "Nepotrošeni novčići", "unspent_coins_title" : "Nepotrošeni novčići",
"unspent_coins_details_title" : "Nepotrošeni detalji o novčićima", "unspent_coins_details_title" : "Nepotrošeni detalji o novčićima",
"freeze" : "Zamrznuti", "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}"
} }

@ -488,5 +488,8 @@
"unspent_coins_title" : "Monete non spese", "unspent_coins_title" : "Monete non spese",
"unspent_coins_details_title" : "Dettagli sulle monete non spese", "unspent_coins_details_title" : "Dettagli sulle monete non spese",
"freeze" : "Congelare", "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}"
} }

@ -488,5 +488,8 @@
"unspent_coins_title" : "未使用のコイン", "unspent_coins_title" : "未使用のコイン",
"unspent_coins_details_title" : "未使用のコインの詳細", "unspent_coins_details_title" : "未使用のコインの詳細",
"freeze" : "氷結", "freeze" : "氷結",
"coin_control" : "コインコントロール(オプション)" "coin_control" : "コインコントロール(オプション)",
"address_detected" : "アドレスが検出されました",
"address_from_domain" : "あなたはからアドレスを得ました unstoppable domain ${domain}"
} }

@ -488,5 +488,8 @@
"unspent_coins_title" : "사용하지 않은 동전", "unspent_coins_title" : "사용하지 않은 동전",
"unspent_coins_details_title" : "사용하지 않은 동전 세부 정보", "unspent_coins_details_title" : "사용하지 않은 동전 세부 정보",
"freeze" : "얼다", "freeze" : "얼다",
"coin_control" : "코인 제어 (옵션)" "coin_control" : "코인 제어 (옵션)",
"address_detected" : "주소 감지",
"address_from_domain" : "주소는 unstoppable domain ${domain}"
} }

@ -488,5 +488,8 @@
"unspent_coins_title" : "Ongebruikte munten", "unspent_coins_title" : "Ongebruikte munten",
"unspent_coins_details_title" : "Details van niet-uitgegeven munten", "unspent_coins_details_title" : "Details van niet-uitgegeven munten",
"freeze" : "Bevriezen", "freeze" : "Bevriezen",
"coin_control" : "Muntcontrole (optioneel)" "coin_control" : "Muntcontrole (optioneel)",
"address_detected" : "Adres gedetecteerd",
"address_from_domain" : "Je adres is van unstoppable domain ${domain}"
} }

@ -488,5 +488,8 @@
"unspent_coins_title" : "Niewydane monety", "unspent_coins_title" : "Niewydane monety",
"unspent_coins_details_title" : "Szczegóły niewydanych monet", "unspent_coins_details_title" : "Szczegóły niewydanych monet",
"freeze" : "Zamrażać", "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}"
} }

@ -488,5 +488,8 @@
"unspent_coins_title" : "Moedas não gastas", "unspent_coins_title" : "Moedas não gastas",
"unspent_coins_details_title" : "Detalhes de moedas não gastas", "unspent_coins_details_title" : "Detalhes de moedas não gastas",
"freeze" : "Congelar", "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}"
} }

@ -488,5 +488,8 @@
"unspent_coins_title" : "Неизрасходованные монеты", "unspent_coins_title" : "Неизрасходованные монеты",
"unspent_coins_details_title" : "Сведения о неизрасходованных монетах", "unspent_coins_details_title" : "Сведения о неизрасходованных монетах",
"freeze" : "Заморозить", "freeze" : "Заморозить",
"coin_control" : "Контроль монет (необязательно)" "coin_control" : "Контроль монет (необязательно)",
"address_detected" : "Обнаружен адрес",
"address_from_domain" : "Вы получили адрес из unstoppable domain ${domain}"
} }

@ -488,5 +488,8 @@
"unspent_coins_title" : "Невитрачені монети", "unspent_coins_title" : "Невитрачені монети",
"unspent_coins_details_title" : "Відомості про невитрачені монети", "unspent_coins_details_title" : "Відомості про невитрачені монети",
"freeze" : "Заморозити", "freeze" : "Заморозити",
"coin_control" : "Контроль монет (необов’язково)" "coin_control" : "Контроль монет (необов’язково)",
"address_detected" : "Виявлено адресу",
"address_from_domain" : "Ви отримали адресу від unstoppable domain ${domain}"
} }

@ -488,5 +488,8 @@
"unspent_coins_title" : "未使用的硬幣", "unspent_coins_title" : "未使用的硬幣",
"unspent_coins_details_title" : "未使用代幣詳情", "unspent_coins_details_title" : "未使用代幣詳情",
"freeze" : "凍結", "freeze" : "凍結",
"coin_control" : "硬幣控制(可選)" "coin_control" : "硬幣控制(可選)",
"address_detected" : "檢測到地址",
"address_from_domain" : "您有以下地址 unstoppable domain ${domain}"
} }
Loading…
Cancel
Save